You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by se...@apache.org on 2015/09/17 20:19:33 UTC
[01/51] [abbrv] flink git commit: [FLINK-2415] [optimizer] Create and
attach proper JobGraph describing JSON plans to JobGraph for batch jobs.
Repository: flink
Updated Branches:
refs/heads/master 77989d3cb -> 482d4373f
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
index 05daaab..8584e63 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
@@ -19,16 +19,16 @@ limitations under the License.
-->
<div ng-if="node" class="panel panel-default panel-multi">
<div class="panel-heading clearfix">
- <div class="panel-title">{{ node.pact }}</div>
+ <div class="panel-title">{{ node.operator }}</div>
<div class="panel-info first">ID: {{ node.id }}</div>
- <div ng-if="node.contents" class="panel-info">
+ <div ng-if="node.description" class="panel-info">
<div class="label-group">
<bs-label status="{{status}}" ng-repeat="(index, status) in stateList">{{node.vertex.groupvertex[status]}}</bs-label>
</div>
</div>
</div>
<div class="panel-heading clearfix">
- <div ng-if="node.contents" class="panel-info first last"><span>{{ node.contents }}</span></div>
+ <div ng-if="node.description" class="panel-info first last"><span>{{ node.description }}</span></div>
</div>
<div class="panel-body">
<table class="table table-hover table-clickable">
@@ -86,22 +86,18 @@ limitations under the License.
<table class="table table-properties">
<thead>
<tr>
- <th colspan="2">Pact Properties</th>
+ <th colspan="2">Properties</th>
</tr>
</thead>
<tbody>
<tr>
<td>Operator</td>
- <td table-property="table-property" value="node.driver_strategy"></td>
+ <td table-property="table-property" value="node.operator_strategy"></td>
</tr>
<tr>
<td>Parallelism</td>
<td table-property="table-property" value="node.parallelism"></td>
</tr>
- <tr>
- <td>Subtasks-per-instance</td>
- <td table-property="table-property" value="node.subtasks_per_instance"></td>
- </tr>
</tbody>
</table>
</div>
@@ -110,22 +106,18 @@ limitations under the License.
<table class="table table-properties">
<thead>
<tr>
- <th colspan="2">Pact Properties</th>
+ <th colspan="2">Properties</th>
</tr>
</thead>
<tbody>
<tr>
<td>Operator</td>
- <td table-property="table-property" value="node.driver_strategy"></td>
+ <td table-property="table-property" value="node.operator_strategy"></td>
</tr>
<tr>
<td>Parallelism</td>
<td table-property="table-property" value="node.parallelism"></td>
</tr>
- <tr>
- <td>Subtasks-per-instance</td>
- <td table-property="table-property" value="node.subtasks_per_instance"></td>
- </tr>
</tbody>
</table>
<table ng-if="node.estimates" class="table table-properties">
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobEdge.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobEdge.java b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobEdge.java
index 5faea83..6e5c6e5 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobEdge.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobEdge.java
@@ -21,7 +21,7 @@ package org.apache.flink.runtime.jobgraph;
/**
* This class represent edges (communication channels) in a job graph.
* The edges always go from an intermediate result partition to a job vertex.
- * An edge is parameterized with its {@link DistributionPattern}.
+ * An edge is parametrized with its {@link DistributionPattern}.
*/
public class JobEdge implements java.io.Serializable {
@@ -39,7 +39,18 @@ public class JobEdge implements java.io.Serializable {
/** The id of the source intermediate data set */
private IntermediateDataSetID sourceId;
+
+ /** Optional name for the data shipping strategy (forward, partition hash, rebalance, ...),
+ * to be displayed in the JSON plan */
+ private String shipStrategyName;
+
+ /** Optional name for the pre-processing operation (sort, combining sort, ...),
+ * to be displayed in the JSON plan */
+ private String preProcessingOperationName;
+ /** Optional description of the caching inside an operator, to be displayed in the JSON plan */
+ private String operatorLevelCachingDescription;
+
/**
* Constructs a new job edge, that connects an intermediate result to a consumer task.
*
@@ -115,7 +126,7 @@ public class JobEdge implements java.io.Serializable {
public boolean isIdReference() {
return this.source == null;
}
-
+
// --------------------------------------------------------------------------------------------
public void connecDataSet(IntermediateDataSet dataSet) {
@@ -133,9 +144,66 @@ public class JobEdge implements java.io.Serializable {
}
// --------------------------------------------------------------------------------------------
+
+ /**
+ * Gets the name of the ship strategy for the represented input, like "forward", "partition hash",
+ * "rebalance", "broadcast", ...
+ *
+ * @return The name of the ship strategy for the represented input, or null, if none was set.
+ */
+ public String getShipStrategyName() {
+ return shipStrategyName;
+ }
+
+ /**
+ * Sets the name of the ship strategy for the represented input.
+ *
+ * @param shipStrategyName The name of the ship strategy.
+ */
+ public void setShipStrategyName(String shipStrategyName) {
+ this.shipStrategyName = shipStrategyName;
+ }
+
+ /**
+ * Gets the name of the pro-processing operation for this input.
+ *
+ * @return The name of the pro-processing operation, or null, if none was set.
+ */
+ public String getPreProcessingOperationName() {
+ return preProcessingOperationName;
+ }
+
+ /**
+ * Sets the name of the pre-processing operation for this input.
+ *
+ * @param preProcessingOperationName The name of the pre-processing operation.
+ */
+ public void setPreProcessingOperationName(String preProcessingOperationName) {
+ this.preProcessingOperationName = preProcessingOperationName;
+ }
+
+ /**
+ * Gets the operator-level caching description for this input.
+ *
+ * @return The description of operator-level caching, or null, is none was set.
+ */
+ public String getOperatorLevelCachingDescription() {
+ return operatorLevelCachingDescription;
+ }
+
+ /**
+ * Sets the operator-level caching description for this input.
+ *
+ * @param operatorLevelCachingDescription The description of operator-level caching.
+ */
+ public void setOperatorLevelCachingDescription(String operatorLevelCachingDescription) {
+ this.operatorLevelCachingDescription = operatorLevelCachingDescription;
+ }
+
+ // --------------------------------------------------------------------------------------------
@Override
public String toString() {
- return String.format("%s --> %s []", sourceId, target, distributionPattern.name());
+ return String.format("%s --> %s [%s]", sourceId, target, distributionPattern.name());
}
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobGraph.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobGraph.java b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobGraph.java
index d29d685..7677a1b 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobGraph.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobGraph.java
@@ -88,9 +88,6 @@ public class JobGraph implements Serializable {
/** The settings for asynchronous snapshotting */
private JobSnapshottingSettings snapshotSettings;
-
-
- private String jsonPlan;
// --------------------------------------------------------------------------------------------
@@ -225,14 +222,6 @@ public class JobGraph implements Serializable {
return scheduleMode;
}
- public String getJsonPlan() {
- return jsonPlan;
- }
-
- public void setJsonPlan(String jsonPlan) {
- this.jsonPlan = jsonPlan;
- }
-
/**
* Adds a new task vertex to the job graph if it is not already included.
*
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobVertex.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobVertex.java b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobVertex.java
index 4bf9fc4..f897f6c 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobVertex.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/JobVertex.java
@@ -65,7 +65,7 @@ public class JobVertex implements java.io.Serializable {
/** Optionally, a source of input splits */
private InputSplitSource<?> inputSplitSource;
- /** The name of the vertex */
+ /** The name of the vertex. This will be shown in runtime logs and will be in the runtime environment */
private String name;
/** Optionally, a sharing group that allows subtasks from different job vertices to run concurrently in one slot */
@@ -73,6 +73,20 @@ public class JobVertex implements java.io.Serializable {
/** The group inside which the vertex subtasks share slots */
private CoLocationGroup coLocationGroup;
+
+ /** Optional, the name of the operator, such as 'Flat Map' or 'Join', to be included in the JSON plan */
+ private String operatorName;
+
+ /** Optional, the description of the operator, like 'Hash Join', or 'Sorted Group Reduce',
+ * to be included in the JSON plan */
+ private String operatorDescription;
+
+ /** Optional, pretty name of the operator, to be displayed in the JSON plan */
+ private String operatorPrettyName;
+
+ /** Optional, the JSON for the optimizer properties of the operator result,
+ * to be included in the JSON plan */
+ private String resultOptimizerProperties;
// --------------------------------------------------------------------------------------------
@@ -314,7 +328,7 @@ public class JobVertex implements java.io.Serializable {
public void updateCoLocationGroup(CoLocationGroup group) {
this.coLocationGroup = group;
}
-
+
// --------------------------------------------------------------------------------------------
public IntermediateDataSet createAndAddResultDataSet(ResultPartitionType partitionType) {
@@ -330,17 +344,18 @@ public class JobVertex implements java.io.Serializable {
return result;
}
- public void connectDataSetAsInput(IntermediateDataSet dataSet, DistributionPattern distPattern) {
+ public JobEdge connectDataSetAsInput(IntermediateDataSet dataSet, DistributionPattern distPattern) {
JobEdge edge = new JobEdge(dataSet, this, distPattern);
this.inputs.add(edge);
dataSet.addConsumer(edge);
+ return edge;
}
- public void connectNewDataSetAsInput(JobVertex input, DistributionPattern distPattern) {
- connectNewDataSetAsInput(input, distPattern, ResultPartitionType.PIPELINED);
+ public JobEdge connectNewDataSetAsInput(JobVertex input, DistributionPattern distPattern) {
+ return connectNewDataSetAsInput(input, distPattern, ResultPartitionType.PIPELINED);
}
- public void connectNewDataSetAsInput(
+ public JobEdge connectNewDataSetAsInput(
JobVertex input,
DistributionPattern distPattern,
ResultPartitionType partitionType) {
@@ -349,6 +364,7 @@ public class JobVertex implements java.io.Serializable {
JobEdge edge = new JobEdge(dataSet, this, distPattern);
this.inputs.add(edge);
dataSet.addConsumer(edge);
+ return edge;
}
public void connectIdInput(IntermediateDataSetID dataSetId, DistributionPattern distPattern) {
@@ -395,9 +411,43 @@ public class JobVertex implements java.io.Serializable {
* @throws Exception The method may throw exceptions which cause the job to fail immediately.
*/
public void finalizeOnMaster(ClassLoader loader) throws Exception {}
-
+
// --------------------------------------------------------------------------------------------
+ public String getOperatorName() {
+ return operatorName;
+ }
+
+ public void setOperatorName(String operatorName) {
+ this.operatorName = operatorName;
+ }
+
+ public String getOperatorDescription() {
+ return operatorDescription;
+ }
+
+ public void setOperatorDescription(String operatorDescription) {
+ this.operatorDescription = operatorDescription;
+ }
+
+ public void setOperatorPrettyName(String operatorPrettyName) {
+ this.operatorPrettyName = operatorPrettyName;
+ }
+
+ public String getOperatorPrettyName() {
+ return operatorPrettyName;
+ }
+
+ public String getResultOptimizerProperties() {
+ return resultOptimizerProperties;
+ }
+
+ public void setResultOptimizerProperties(String resultOptimizerProperties) {
+ this.resultOptimizerProperties = resultOptimizerProperties;
+ }
+
+ // --------------------------------------------------------------------------------------------
+
@Override
public String toString() {
return this.name + " (" + this.invokableClassName + ')';
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonPlanGenerator.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonPlanGenerator.java b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonPlanGenerator.java
new file mode 100644
index 0000000..0844dab
--- /dev/null
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonPlanGenerator.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.jobgraph.jsonplan;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.flink.runtime.jobgraph.JobEdge;
+import org.apache.flink.runtime.jobgraph.JobGraph;
+import org.apache.flink.runtime.jobgraph.JobVertex;
+
+import java.io.StringWriter;
+import java.util.List;
+
+
+public class JsonPlanGenerator {
+
+ private static final String NOT_SET = "(not set)";
+ private static final String EMPTY = "{}";
+
+ public static String generatePlan(JobGraph jg) {
+ try {
+ final StringWriter writer = new StringWriter(1024);
+
+ final JsonFactory factory = new JsonFactory();
+ final JsonGenerator gen = factory.createGenerator(writer);
+ gen.useDefaultPrettyPrinter();
+
+ // start of everything
+ gen.writeStartObject();
+ gen.writeStringField("jid", jg.getJobID().toString());
+ gen.writeStringField("name", jg.getName());
+ gen.writeArrayFieldStart("nodes");
+
+ // info per vertex
+ for (JobVertex vertex : jg.getVertices()) {
+
+ String operator = vertex.getOperatorName() != null ?
+ vertex.getOperatorName() : NOT_SET;
+
+ String operatorDescr = vertex.getOperatorDescription() != null ?
+ vertex.getOperatorDescription() : NOT_SET;
+
+ String optimizerProps = vertex.getResultOptimizerProperties() != null ?
+ vertex.getResultOptimizerProperties() : EMPTY;
+
+ String description = vertex.getOperatorPrettyName() != null ?
+ vertex.getOperatorPrettyName() : vertex.getName();
+
+ // make sure the encoding is HTML pretty
+ description = StringEscapeUtils.escapeHtml4(description);
+ description = description.replace("\n", "<br/>");
+ description = description.replace("\\", "\");
+
+ operatorDescr = StringEscapeUtils.escapeHtml4(operatorDescr);
+ operatorDescr = operatorDescr.replace("\n", "<br/>");
+
+ gen.writeStartObject();
+
+ // write the core properties
+ gen.writeStringField("id", vertex.getID().toString());
+ gen.writeNumberField("parallelism", vertex.getParallelism());
+ gen.writeStringField("operator", operator);
+ gen.writeStringField("operator_strategy", operatorDescr);
+ gen.writeStringField("description", description);
+
+ if (!vertex.isInputVertex()) {
+ // write the input edge properties
+ gen.writeArrayFieldStart("inputs");
+
+ List<JobEdge> inputs = vertex.getInputs();
+ for (int inputNum = 0; inputNum < inputs.size(); inputNum++) {
+ JobEdge edge = inputs.get(inputNum);
+ if (edge.getSource() == null) {
+ continue;
+ }
+
+ JobVertex predecessor = edge.getSource().getProducer();
+
+ String shipStrategy = edge.getShipStrategyName();
+ String preProcessingOperation = edge.getPreProcessingOperationName();
+ String operatorLevelCaching = edge.getOperatorLevelCachingDescription();
+
+ gen.writeStartObject();
+ gen.writeNumberField("num", inputNum);
+ gen.writeStringField("id", predecessor.getID().toString());
+
+ if (shipStrategy != null) {
+ gen.writeStringField("ship_strategy", shipStrategy);
+ }
+ if (preProcessingOperation != null) {
+ gen.writeStringField("local_strategy", preProcessingOperation);
+ }
+ if (operatorLevelCaching != null) {
+ gen.writeStringField("caching", operatorLevelCaching);
+ }
+
+ gen.writeStringField("exchange", edge.getSource().getResultType().name().toLowerCase());
+
+ gen.writeEndObject();
+ }
+
+ gen.writeEndArray();
+ }
+
+ // write the optimizer properties
+ gen.writeFieldName("optimizer_properties");
+ gen.writeRawValue(optimizerProps);
+
+ gen.writeEndObject();
+ }
+
+ // end of everything
+ gen.writeEndArray();
+ gen.writeEndObject();
+
+ gen.close();
+
+ return writer.toString();
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Failed to generate plan", e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime/src/main/java/org/apache/flink/runtime/operators/DriverStrategy.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/operators/DriverStrategy.java b/flink-runtime/src/main/java/org/apache/flink/runtime/operators/DriverStrategy.java
index 74f737e..0aceae2 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/operators/DriverStrategy.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/operators/DriverStrategy.java
@@ -104,10 +104,14 @@ public enum DriverStrategy {
// the first input is inner loop, the second input is outer loop and stream-processed
NESTEDLOOP_STREAMED_OUTER_SECOND(CrossDriver.class, null, FULL_DAM, PIPELINED, 0),
- // union utility op. unions happen implicitly on the network layer (in the readers) when bundeling streams
+ // union utility op. unions happen implicitly on the network layer (in the readers) when bundling streams
UNION(null, null, PIPELINED, PIPELINED, 0),
// explicit binary union between a streamed and a cached input
- UNION_WITH_CACHED(UnionWithTempOperator.class, null, FULL_DAM, PIPELINED, 0);
+ UNION_WITH_CACHED(UnionWithTempOperator.class, null, FULL_DAM, PIPELINED, 0),
+
+ // some enumeration constants to mark sources and sinks
+ SOURCE(null, null, PIPELINED, 0),
+ SINK(null, null, PIPELINED, 0);
// --------------------------------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
index 27fc6e3..7046937 100644
--- a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
+++ b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
@@ -36,6 +36,7 @@ import org.apache.flink.runtime.accumulators.AccumulatorSnapshot
import org.apache.flink.runtime.blob.BlobServer
import org.apache.flink.runtime.client._
import org.apache.flink.runtime.executiongraph.{ExecutionGraph, ExecutionJobVertex}
+import org.apache.flink.runtime.jobgraph.jsonplan.JsonPlanGenerator
import org.apache.flink.runtime.jobmanager.web.WebInfoServer
import org.apache.flink.runtime.leaderelection.{LeaderContender, LeaderElectionService}
import org.apache.flink.runtime.leaderretrieval.LeaderRetrievalService
@@ -647,7 +648,15 @@ class JobManager(
executionGraph.setDelayBeforeRetrying(delayBetweenRetries)
executionGraph.setScheduleMode(jobGraph.getScheduleMode())
executionGraph.setQueuedSchedulingAllowed(jobGraph.getAllowQueuedScheduling())
- executionGraph.setJsonPlan(jobGraph.getJsonPlan())
+
+ try {
+ executionGraph.setJsonPlan(JsonPlanGenerator.generatePlan(jobGraph))
+ }
+ catch {
+ case t: Throwable =>
+ log.warn("Cannot create JSON plan for job", t)
+ executionGraph.setJsonPlan("{}")
+ }
// initialize the vertices that have a master initialization hook
// file output formats create directories here, input formats create splits
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime/src/test/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonGeneratorTest.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonGeneratorTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonGeneratorTest.java
new file mode 100644
index 0000000..1ed46f2
--- /dev/null
+++ b/flink-runtime/src/test/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonGeneratorTest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.jobgraph.jsonplan;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.TextNode;
+import org.apache.flink.runtime.io.network.partition.ResultPartitionType;
+import org.apache.flink.runtime.jobgraph.DistributionPattern;
+import org.apache.flink.runtime.jobgraph.JobGraph;
+import org.apache.flink.runtime.jobgraph.JobVertex;
+import org.apache.flink.runtime.jobgraph.JobVertexID;
+import org.apache.flink.runtime.operators.testutils.DummyInvokable;
+import org.junit.Test;
+
+import java.util.Iterator;
+
+import static org.junit.Assert.*;
+
+public class JsonGeneratorTest {
+
+ @Test
+ public void testGeneratorWithoutAnyAttachements() {
+ try {
+ JobVertex source1 = new JobVertex("source 1");
+
+ JobVertex source2 = new JobVertex("source 2");
+ source2.setInvokableClass(DummyInvokable.class);
+
+ JobVertex source3 = new JobVertex("source 3");
+
+ JobVertex intermediate1 = new JobVertex("intermediate 1");
+ JobVertex intermediate2 = new JobVertex("intermediate 2");
+
+ JobVertex join1 = new JobVertex("join 1");
+ JobVertex join2 = new JobVertex("join 2");
+
+ JobVertex sink1 = new JobVertex("sink 1");
+ JobVertex sink2 = new JobVertex("sink 2");
+
+ intermediate1.connectNewDataSetAsInput(source1, DistributionPattern.POINTWISE, ResultPartitionType.PIPELINED);
+ intermediate2.connectNewDataSetAsInput(source2, DistributionPattern.ALL_TO_ALL, ResultPartitionType.PIPELINED);
+
+ join1.connectNewDataSetAsInput(intermediate1, DistributionPattern.POINTWISE, ResultPartitionType.BLOCKING);
+ join1.connectNewDataSetAsInput(intermediate2, DistributionPattern.ALL_TO_ALL, ResultPartitionType.BLOCKING);
+
+ join2.connectNewDataSetAsInput(join1, DistributionPattern.POINTWISE, ResultPartitionType.PIPELINED);
+ join2.connectNewDataSetAsInput(source3, DistributionPattern.POINTWISE, ResultPartitionType.BLOCKING);
+
+ sink1.connectNewDataSetAsInput(join2, DistributionPattern.POINTWISE);
+ sink2.connectNewDataSetAsInput(join1, DistributionPattern.ALL_TO_ALL);
+
+ JobGraph jg = new JobGraph("my job", source1, source2, source3,
+ intermediate1, intermediate2, join1, join2, sink1, sink2);
+
+ String plan = JsonPlanGenerator.generatePlan(jg);
+ assertNotNull(plan);
+
+ System.out.println(plan);
+
+ // validate the produced JSON
+ ObjectMapper m = new ObjectMapper();
+ JsonNode rootNode = m.readTree(plan);
+
+ // core fields
+ assertEquals(new TextNode(jg.getJobID().toString()), rootNode.get("jid"));
+ assertEquals(new TextNode(jg.getName()), rootNode.get("name"));
+
+ assertTrue(rootNode.path("nodes").isArray());
+
+ for (Iterator<JsonNode> iter = rootNode.path("nodes").elements(); iter.hasNext(); ){
+ JsonNode next = iter.next();
+
+ JsonNode idNode = next.get("id");
+ assertNotNull(idNode);
+ assertTrue(idNode.isTextual());
+ checkVertexExists(idNode.asText(), jg);
+
+ String description = next.get("description").asText();
+ assertTrue(
+ description.startsWith("source") ||
+ description.startsWith("sink") ||
+ description.startsWith("intermediate") ||
+ description.startsWith("join"));
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ private void checkVertexExists(String vertexId, JobGraph graph) {
+ // validate that the vertex has a valid
+ JobVertexID id = JobVertexID.fromHexString(vertexId);
+ for (JobVertex vertex : graph.getVertices()) {
+ if (vertex.getID().equals(id)) {
+ return;
+ }
+ }
+ fail("could not find vertex with id " + vertexId + " in JobGraph");
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-tests/src/test/java/org/apache/flink/test/optimizer/jsonplan/JsonJobGraphGenerationTest.java
----------------------------------------------------------------------
diff --git a/flink-tests/src/test/java/org/apache/flink/test/optimizer/jsonplan/JsonJobGraphGenerationTest.java b/flink-tests/src/test/java/org/apache/flink/test/optimizer/jsonplan/JsonJobGraphGenerationTest.java
new file mode 100644
index 0000000..c552700
--- /dev/null
+++ b/flink-tests/src/test/java/org/apache/flink/test/optimizer/jsonplan/JsonJobGraphGenerationTest.java
@@ -0,0 +1,352 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.test.optimizer.jsonplan;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+import org.apache.flink.api.common.JobExecutionResult;
+import org.apache.flink.api.common.Plan;
+import org.apache.flink.api.java.ExecutionEnvironment;
+import org.apache.flink.api.java.ExecutionEnvironmentFactory;
+import org.apache.flink.configuration.ConfigConstants;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.examples.java.clustering.KMeans;
+import org.apache.flink.examples.java.graph.ConnectedComponents;
+import org.apache.flink.examples.java.relational.WebLogAnalysis;
+import org.apache.flink.examples.java.wordcount.WordCount;
+import org.apache.flink.optimizer.Optimizer;
+import org.apache.flink.optimizer.plan.OptimizedPlan;
+import org.apache.flink.optimizer.plantranslate.JobGraphGenerator;
+
+import org.apache.flink.runtime.jobgraph.JobGraph;
+import org.apache.flink.runtime.jobgraph.jsonplan.JsonPlanGenerator;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+public class JsonJobGraphGenerationTest {
+
+ private PrintStream out;
+ private PrintStream err;
+
+// @Before
+// public void redirectStreams() {
+// this.out = System.out;
+// this.err = System.err;
+//
+// OutputStream discards = new OutputStream() {
+// @Override
+// public void write(int b) {}
+// };
+//
+// System.setOut(new PrintStream(discards));
+// System.setErr(new PrintStream(discards));
+// }
+
+ @After
+ public void restoreStreams() {
+ if (out != null) {
+ System.setOut(out);
+ }
+ if (err != null) {
+ System.setOut(err);
+ }
+ }
+
+
+ @Test
+ public void testWordCountPlan() {
+ try {
+ // without arguments
+ try {
+ final int parallelism = 1; // some ops have DOP 1 forced
+ JsonValidator validator = new GenericValidator(parallelism, 3);
+ TestingExecutionEnvironment.setAsNext(validator, parallelism);
+
+ WordCount.main(new String[0]);
+ }
+ catch (AbortError ignored) {}
+
+ // with arguments
+ try {
+ final int parallelism = 17;
+ JsonValidator validator = new GenericValidator(parallelism, 3);
+ TestingExecutionEnvironment.setAsNext(validator, parallelism);
+
+ String tmpDir = ConfigConstants.DEFAULT_TASK_MANAGER_TMP_PATH;
+ WordCount.main(new String[] {tmpDir, tmpDir});
+ }
+ catch (AbortError ignored) {}
+ }
+ catch (Exception e) {
+ restoreStreams();
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testWebLogAnalysis() {
+ try {
+ // without arguments
+ try {
+ final int parallelism = 1; // some ops have DOP 1 forced
+ JsonValidator validator = new GenericValidator(parallelism, 6);
+ TestingExecutionEnvironment.setAsNext(validator, parallelism);
+
+ WebLogAnalysis.main(new String[0]);
+ }
+ catch (AbortError ignored) {}
+
+ // with arguments
+ try {
+ final int parallelism = 17;
+ JsonValidator validator = new GenericValidator(parallelism, 6);
+ TestingExecutionEnvironment.setAsNext(validator, parallelism);
+
+ String tmpDir = ConfigConstants.DEFAULT_TASK_MANAGER_TMP_PATH;
+ WebLogAnalysis.main(new String[] {tmpDir, tmpDir, tmpDir, tmpDir});
+ }
+ catch (AbortError ignored) {}
+ }
+ catch (Exception e) {
+ restoreStreams();
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testKMeans() {
+ try {
+ // without arguments
+ try {
+ final int parallelism = 1; // some ops have DOP 1 forced
+ JsonValidator validator = new GenericValidator(parallelism, 9);
+ TestingExecutionEnvironment.setAsNext(validator, parallelism);
+
+ KMeans.main(new String[0]);
+ }
+ catch (AbortError ignored) {}
+
+ // with arguments
+ try {
+ final int parallelism = 42;
+ JsonValidator validator = new GenericValidator(parallelism, 9);
+ TestingExecutionEnvironment.setAsNext(validator, parallelism);
+
+ String tmpDir = ConfigConstants.DEFAULT_TASK_MANAGER_TMP_PATH;
+ KMeans.main(new String[] {tmpDir, tmpDir, tmpDir, "100"});
+ }
+ catch (AbortError ignored) {}
+
+ }
+ catch (Exception e) {
+ restoreStreams();
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testConnectedComponents() {
+ try {
+ // without arguments
+ try {
+ final int parallelism = 1; // some ops have DOP 1 forced
+ JsonValidator validator = new GenericValidator(parallelism, 10);
+ TestingExecutionEnvironment.setAsNext(validator, parallelism);
+
+ ConnectedComponents.main();
+ }
+ catch (AbortError ignored) {}
+
+ // with arguments
+ try {
+ final int parallelism = 23;
+ JsonValidator validator = new GenericValidator(parallelism, 9);
+ TestingExecutionEnvironment.setAsNext(validator, parallelism);
+
+ String tmpDir = ConfigConstants.DEFAULT_TASK_MANAGER_TMP_PATH;
+ ConnectedComponents.main(tmpDir, tmpDir, tmpDir, "100");
+ }
+ catch (AbortError ignored) {}
+
+ }
+ catch (Exception e) {
+ restoreStreams();
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ private static interface JsonValidator {
+
+ void validtateJson(String json) throws Exception;
+ }
+
+ private static class GenericValidator implements JsonValidator {
+
+ private final int expectedParallelism;
+ private final int numNodes;
+
+ GenericValidator(int expectedParallelism, int numNodes) {
+ this.expectedParallelism = expectedParallelism;
+ this.numNodes = numNodes;
+ }
+
+ @Override
+ public void validtateJson(String json) throws Exception {
+ System.out.println(json);
+
+ final Map<String, JsonNode> idToNode = new HashMap<>();
+
+ // validate the produced JSON
+ ObjectMapper m = new ObjectMapper();
+ JsonNode rootNode = m.readTree(json);
+
+ JsonNode idField = rootNode.get("jid");
+ JsonNode nameField = rootNode.get("name");
+ JsonNode arrayField = rootNode.get("nodes");
+
+ assertNotNull(idField);
+ assertNotNull(nameField);
+ assertNotNull(arrayField);
+ assertTrue(idField.isTextual());
+ assertTrue(nameField.isTextual());
+ assertTrue(arrayField.isArray());
+
+ ArrayNode array = (ArrayNode) arrayField;
+ for (Iterator<JsonNode> iter = array.elements(); iter.hasNext(); ) {
+ JsonNode vertex = iter.next();
+
+ JsonNode vertexIdField = vertex.get("id");
+ JsonNode parallelismField = vertex.get("parallelism");
+ JsonNode contentsFields = vertex.get("description");
+ JsonNode operatorField = vertex.get("operator");
+
+ assertNotNull(vertexIdField);
+ assertTrue(vertexIdField.isTextual());
+ assertNotNull(parallelismField);
+ assertTrue(parallelismField.isNumber());
+ assertNotNull(contentsFields);
+ assertTrue(contentsFields.isTextual());
+ assertNotNull(operatorField);
+ assertTrue(operatorField.isTextual());
+
+ if (contentsFields.asText().startsWith("Sync")) {
+ assertEquals(1, parallelismField.asInt());
+ }
+ else {
+ assertEquals(expectedParallelism, parallelismField.asInt());
+ }
+
+ idToNode.put(vertexIdField.asText(), vertex);
+ }
+
+ assertEquals(numNodes, idToNode.size());
+
+ // check that all inputs are contained
+ for (JsonNode node : idToNode.values()) {
+ JsonNode inputsField = node.get("inputs");
+ if (inputsField != null) {
+ for (Iterator<JsonNode> inputsIter = inputsField.elements(); inputsIter.hasNext(); ) {
+ JsonNode inputNode = inputsIter.next();
+ JsonNode inputIdField = inputNode.get("id");
+
+ assertNotNull(inputIdField);
+ assertTrue(inputIdField.isTextual());
+
+ String inputIdString = inputIdField.asText();
+ assertTrue(idToNode.containsKey(inputIdString));
+ }
+ }
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ private static class AbortError extends Error {
+ private static final long serialVersionUID = 152179957828703919L;
+ }
+
+ // ------------------------------------------------------------------------
+
+ private static class TestingExecutionEnvironment extends ExecutionEnvironment {
+
+ private final JsonValidator validator;
+
+ private TestingExecutionEnvironment(JsonValidator validator) {
+ this.validator = validator;
+ }
+
+ @Override
+ public JobExecutionResult execute(String jobName) throws Exception {
+ Plan plan = createProgramPlan(jobName);
+
+ Optimizer pc = new Optimizer(new Configuration());
+ OptimizedPlan op = pc.compile(plan);
+
+ JobGraphGenerator jgg = new JobGraphGenerator();
+ JobGraph jobGraph = jgg.compileJobGraph(op);
+
+ String jsonPlan = JsonPlanGenerator.generatePlan(jobGraph);
+
+ // first check that the JSON is valid
+ JsonParser parser = new JsonFactory().createJsonParser(jsonPlan);
+ while (parser.nextToken() != null);
+
+ validator.validtateJson(jsonPlan);
+
+ throw new AbortError();
+ }
+
+ @Override
+ public String getExecutionPlan() throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ public static void setAsNext(final JsonValidator validator, final int defaultParallelism) {
+ initializeContextEnvironment(new ExecutionEnvironmentFactory() {
+ @Override
+ public ExecutionEnvironment createExecutionEnvironment() {
+ ExecutionEnvironment env = new TestingExecutionEnvironment(validator);
+ env.setParallelism(defaultParallelism);
+ return env;
+ }
+ });
+ }
+ }
+}
[28/51] [abbrv] flink git commit: [FLINK-2554] [web dashboard] Add
request hander that lists exceptions encountered during execution
Posted by se...@apache.org.
[FLINK-2554] [web dashboard] Add request hander that lists exceptions encountered during execution
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/1984a3fb
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/1984a3fb
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/1984a3fb
Branch: refs/heads/master
Commit: 1984a3fbc22498b6e88b3c3144e59c2af098f1e2
Parents: b29a5d4
Author: Stephan Ewen <se...@apache.org>
Authored: Thu Aug 20 17:06:16 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:51 2015 +0200
----------------------------------------------------------------------
.../flink/runtime/webmonitor/JsonFactory.java | 40 ++++++-
.../runtime/webmonitor/WebRuntimeMonitor.java | 4 +-
.../handlers/JobExceptionsHandler.java | 107 +++++++++++++++++++
.../runtime/webmonitor/WebMonitorUtils.java | 2 +
4 files changed, 149 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/1984a3fb/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
index 40d095f..66d7b0c 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
@@ -25,6 +25,8 @@ import org.apache.flink.runtime.messages.webmonitor.JobDetails;
import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
+import org.apache.flink.runtime.webmonitor.handlers.JobExceptionsHandler;
+import org.apache.flink.util.ExceptionUtils;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
@@ -180,9 +182,6 @@ public class JsonFactory {
gen.writeEndObject();
}
-
-
-
public static String generateJobsOverviewJSON(JobsWithIDsOverview overview) {
try {
List<JobID> runningIDs = overview.getJobsRunningOrPending();
@@ -222,6 +221,41 @@ public class JsonFactory {
}
}
+ public static String generateExceptionsJson(Throwable rootCause,
+ List<JobExceptionsHandler.ExceptionWithContext> allExceptions,
+ boolean truncated) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+ gen.writeStringField("root-exception", ExceptionUtils.stringifyException(rootCause));
+
+ if (allExceptions != null && !allExceptions.isEmpty()) {
+ gen.writeArrayFieldStart("all-exceptions");
+ for (JobExceptionsHandler.ExceptionWithContext ewc : allExceptions) {
+ gen.writeStartObject();
+ gen.writeStringField("exception", ExceptionUtils.stringifyException(ewc.getException()));
+ gen.writeStringField("task", ewc.getTaskName());
+ gen.writeStringField("location", ewc.getLocation());
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+
+ gen.writeBooleanField("truncated", truncated);
+ }
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+ catch (Exception e) {
+ // this should not happen
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
// --------------------------------------------------------------------------------------------
/** Don't instantiate */
http://git-wip-us.apache.org/repos/asf/flink/blob/1984a3fb/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
index 0799328..51df17c 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
@@ -37,6 +37,7 @@ import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.webmonitor.files.StaticFileServerHandler;
import org.apache.flink.runtime.webmonitor.handlers.ExecutionPlanHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobConfigHandler;
+import org.apache.flink.runtime.webmonitor.handlers.JobExceptionsHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobSummaryHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobVerticesOverviewHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobsOverviewHandler;
@@ -138,11 +139,12 @@ public class WebRuntimeMonitor implements WebMonitor {
.GET("/jobs/:jobid/vertices", handler(new JobVerticesOverviewHandler(currentGraphs)))
.GET("/jobs/:jobid/plan", handler(new ExecutionPlanHandler(currentGraphs)))
.GET("/jobs/:jobid/config", handler(new JobConfigHandler(currentGraphs)))
+ .GET("/jobs/:jobid/exceptions", handler(new JobExceptionsHandler(currentGraphs)))
// .GET("/running/:jobid/:jobvertex", handler(new ExecutionPlanHandler(currentGraphs)))
// the handler for the legacy requests
- .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
+ .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
// this handler serves all the static contents
.GET("/:*", new StaticFileServerHandler(webRootDir));
http://git-wip-us.apache.org/repos/asf/flink/blob/1984a3fb/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobExceptionsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobExceptionsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobExceptionsHandler.java
new file mode 100644
index 0000000..0f8cba7
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobExceptionsHandler.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import org.apache.flink.runtime.executiongraph.ExecutionGraph;
+import org.apache.flink.runtime.executiongraph.ExecutionVertex;
+import org.apache.flink.runtime.instance.InstanceConnectionInfo;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+import org.apache.flink.runtime.webmonitor.JsonFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Request handler that returns the configuration of a job.
+ */
+public class JobExceptionsHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+
+ private static final int MAX_NUMBER_EXCEPTION_TO_REPORT = 20;
+
+ public JobExceptionsHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
+ // most important is the root failure cause
+ Throwable rootException = graph.getFailureCause();
+
+ // we additionally collect all exceptions (up to a limit) that occurred in the individual tasks
+ List<ExceptionWithContext> localExceptions = new ArrayList<>();
+ boolean truncated = false;
+
+ for (ExecutionVertex task : graph.getAllExecutionVertices()) {
+ Throwable t = task.getFailureCause();
+ if (t != null) {
+ if (localExceptions.size() >= MAX_NUMBER_EXCEPTION_TO_REPORT) {
+ truncated = true;
+ break;
+ }
+
+ InstanceConnectionInfo location = task.getCurrentAssignedResourceLocation();
+ String locationString = location != null ?
+ location.getFQDNHostname() + ':' + location.dataPort() :
+ "(unassigned)";
+
+ localExceptions.add(new ExceptionWithContext(t, task.getTaskName(), locationString));
+ }
+ }
+
+ // if only one exception occurred in a task, and that is the root exception,
+ // there is no need to display it twice
+ if (localExceptions.size() == 1 && localExceptions.get(0).getException() == rootException) {
+ localExceptions = null;
+ }
+
+ return JsonFactory.generateExceptionsJson(rootException, localExceptions, truncated);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class that encapsulated an exception, together with the name of the throwing task, and
+ * the instance on which the exception occurred.
+ */
+ public static class ExceptionWithContext {
+
+ private final Throwable exception;
+ private final String taskName;
+ private final String location;
+
+ public ExceptionWithContext(Throwable exception, String taskName, String location) {
+ this.exception = exception;
+ this.taskName = taskName;
+ this.location = location;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public String getTaskName() {
+ return taskName;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/1984a3fb/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java b/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
index aa784e6..2e50ca4 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
@@ -53,6 +53,8 @@ public class WebMonitorUtils {
}
}
+ lastChanged = Math.max(lastChanged, finished);
+
return new JobDetails(job.getJobID(), job.getJobName(),
started, finished, status, lastChanged,
countsPerStatus, numTotalTasks);
[39/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Add
auto-refresh to dashboard
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/616b7d56/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index f7394f8..6324e9a 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -216,7 +216,8 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
return JobsService.unRegisterObserver($scope.jobObserver);
});
return $scope.jobObserver();
-}]).controller('SingleJobController', ["$scope", "$state", "$stateParams", "JobsService", "$rootScope", function($scope, $state, $stateParams, JobsService, $rootScope) {
+}]).controller('SingleJobController', ["$scope", "$state", "$stateParams", "JobsService", "$rootScope", "flinkConfig", "$interval", function($scope, $state, $stateParams, JobsService, $rootScope, flinkConfig, $interval) {
+ var refresher;
console.log('SingleJobController');
$scope.jobid = $stateParams.jobid;
$rootScope.job = null;
@@ -225,9 +226,16 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
$rootScope.job = data;
return $rootScope.plan = data.plan;
});
+ refresher = $interval(function() {
+ return JobsService.loadJob($stateParams.jobid).then(function(data) {
+ $rootScope.job = data;
+ return $scope.$broadcast('reload');
+ });
+ }, flinkConfig["refresh-interval"]);
return $scope.$on('$destroy', function() {
$rootScope.job = null;
- return $rootScope.plan = null;
+ $rootScope.plan = null;
+ return $interval.cancel(refresher);
});
}]).controller('JobPlanController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
console.log('JobPlanController');
@@ -237,15 +245,7 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
if (nodeid !== $scope.nodeid) {
$scope.nodeid = nodeid;
$scope.vertex = null;
- if ($state.is('single-job.plan.overview')) {
- return JobsService.getSubtasks(nodeid).then(function(data) {
- return $scope.vertex = data;
- });
- } else if ($state.is('single-job.plan.accumulators')) {
- return JobsService.getAccumulators(nodeid).then(function(data) {
- return $scope.vertex = data;
- });
- }
+ return $scope.$broadcast('reload');
} else {
$scope.nodeid = null;
return $scope.vertex = null;
@@ -253,22 +253,45 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
};
}]).controller('JobPlanOverviewController', ["$scope", "JobsService", function($scope, JobsService) {
console.log('JobPlanOverviewController');
- if ($scope.nodeid && !$scope.vertex.st) {
- return JobsService.getSubtasks($scope.nodeid).then(function(data) {
+ if ($scope.nodeid && (!$scope.vertex || !$scope.vertex.st)) {
+ JobsService.getSubtasks($scope.nodeid).then(function(data) {
return $scope.vertex = data;
});
}
+ return $scope.$on('reload', function(event) {
+ console.log('JobPlanOverviewController');
+ if ($scope.nodeid) {
+ return JobsService.getSubtasks($scope.nodeid).then(function(data) {
+ return $scope.vertex = data;
+ });
+ }
+ });
}]).controller('JobPlanAccumulatorsController', ["$scope", "JobsService", function($scope, JobsService) {
console.log('JobPlanAccumulatorsController');
- if ($scope.nodeid && !$scope.vertex.accumulators) {
- return JobsService.getAccumulators($scope.nodeid).then(function(data) {
+ if ($scope.nodeid && (!$scope.vertex || !$scope.vertex.accumulators)) {
+ JobsService.getAccumulators($scope.nodeid).then(function(data) {
return $scope.vertex = data;
});
}
+ return $scope.$on('reload', function(event) {
+ console.log('JobPlanAccumulatorsController');
+ if ($scope.nodeid) {
+ return JobsService.getAccumulators($scope.nodeid).then(function(data) {
+ return $scope.vertex = data;
+ });
+ }
+ });
}]).controller('JobTimelineVertexController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
- return JobsService.getVertex($stateParams.vertexId).then(function(data) {
+ console.log('JobTimelineVertexController');
+ JobsService.getVertex($stateParams.vertexId).then(function(data) {
return $scope.vertex = data;
});
+ return $scope.$on('reload', function(event) {
+ console.log('JobTimelineVertexController');
+ return JobsService.getVertex($stateParams.vertexId).then(function(data) {
+ return $scope.vertex = data;
+ });
+ });
}]).controller('JobExceptionsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
return JobsService.loadExceptions().then(function(data) {
return $scope.exceptions = data;
@@ -301,34 +324,41 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
angular.element(svgEl).attr('width', containerW);
analyzeTime = function(data) {
var chart, svg, testData;
+ d3.select(svgEl).selectAll("*").remove();
testData = [];
angular.forEach(data.subtasks, function(subtask, i) {
+ var times;
+ console.log(data.subtasks);
+ times = [
+ {
+ label: "Scheduled",
+ color: "#666",
+ borderColor: "#555",
+ starting_time: subtask.timestamps["SCHEDULED"],
+ ending_time: subtask.timestamps["DEPLOYING"],
+ type: 'regular'
+ }, {
+ label: "Deploying",
+ color: "#aaa",
+ borderColor: "#555",
+ starting_time: subtask.timestamps["DEPLOYING"],
+ ending_time: subtask.timestamps["RUNNING"],
+ type: 'regular'
+ }
+ ];
+ if (subtask.timestamps["FINISHED"] > 0) {
+ times.push({
+ label: "Running",
+ color: "#ddd",
+ borderColor: "#555",
+ starting_time: subtask.timestamps["RUNNING"],
+ ending_time: subtask.timestamps["FINISHED"],
+ type: 'regular'
+ });
+ }
return testData.push({
label: subtask.host + " (" + subtask.subtask + ")",
- times: [
- {
- label: "Scheduled",
- color: "#666",
- borderColor: "#555",
- starting_time: subtask.timestamps["SCHEDULED"],
- ending_time: subtask.timestamps["DEPLOYING"],
- type: 'regular'
- }, {
- label: "Deploying",
- color: "#aaa",
- borderColor: "#555",
- starting_time: subtask.timestamps["DEPLOYING"],
- ending_time: subtask.timestamps["RUNNING"],
- type: 'regular'
- }, {
- label: "Running",
- color: "#ddd",
- borderColor: "#555",
- starting_time: subtask.timestamps["RUNNING"],
- ending_time: subtask.timestamps["FINISHED"],
- type: 'regular'
- }
- ]
+ times: times
});
});
chart = d3.timeline().stack().tickFormat({
@@ -363,6 +393,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
};
analyzeTime = function(data) {
var chart, svg, testData;
+ d3.select(svgEl).selectAll("*").remove();
testData = [];
testData.push({
times: [
@@ -771,9 +802,9 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
}
};
this.setEndTimes = function(list) {
- return angular.forEach(list, function(job, jobKey) {
- if (!(job['end-time'] > -1)) {
- return job['end-time'] = job['start-time'] + job['duration'];
+ return angular.forEach(list, function(item, jobKey) {
+ if (!(item['end-time'] > -1)) {
+ return item['end-time'] = item['start-time'] + item['duration'];
}
});
};
@@ -821,15 +852,6 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
})(this));
return deferreds.job.promise;
};
- this.loadPlan = function(jobid) {
- currentPlan = null;
- deferreds.plan = $q.defer();
- $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/plan").success(function(data) {
- currentPlan = data;
- return deferreds.plan.resolve(data);
- });
- return deferreds.plan.promise;
- };
this.getNode = function(nodeid) {
var deferred, seekNode;
seekNode = function(nodeid, data) {
@@ -849,7 +871,7 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
return null;
};
deferred = $q.defer();
- $q.all([deferreds.job.promise]).then((function(_this) {
+ deferreds.job.promise.then((function(_this) {
return function(data) {
var foundNode;
foundNode = seekNode(nodeid, currentJob.plan.nodes);
@@ -873,12 +895,14 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.getVertex = function(vertexid) {
var deferred;
deferred = $q.defer();
- $q.all([deferreds.job.promise]).then((function(_this) {
+ deferreds.job.promise.then((function(_this) {
return function(data) {
var vertex;
vertex = _this.seekVertex(vertexid);
return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasktimes").success(function(data) {
vertex.subtasks = data.subtasks;
+ _this.setEndTimes(vertex.subtasks);
+ console.log(vertex.subtasks);
return deferred.resolve(vertex);
});
};
@@ -888,7 +912,7 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.getSubtasks = function(vertexid) {
var deferred;
deferred = $q.defer();
- $q.all([deferreds.job.promise]).then((function(_this) {
+ deferreds.job.promise.then((function(_this) {
return function(data) {
var vertex;
vertex = _this.seekVertex(vertexid);
@@ -903,7 +927,7 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.getAccumulators = function(vertexid) {
var deferred;
deferred = $q.defer();
- $q.all([deferreds.job.promise]).then((function(_this) {
+ deferreds.job.promise.then((function(_this) {
return function(data) {
var vertex;
vertex = _this.seekVertex(vertexid);
@@ -918,7 +942,7 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.loadExceptions = function() {
var deferred;
deferred = $q.defer();
- $q.all([deferreds.job.promise]).then((function(_this) {
+ deferreds.job.promise.then((function(_this) {
return function(data) {
return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/exceptions").success(function(exceptions) {
currentJob.exceptions = exceptions;
@@ -970,4 +994,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsImNvbW1vbi9zZXJ2aWNlcy5jb2ZmZWUiLCJjb21tb24vc2VydmljZXMuanMiLCJtb2R1bGVzL2pvYnMvam9icy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuanMiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuZGlyLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLnN2Yy5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LmN0cmwuanMiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkEsUUFBUSxPQUFPLFlBQVksQ0FBQyxhQUFhLGtCQUl4QyxtQkFBSSxTQUFDLFlBQUQ7RUFDSCxXQUFXLGlCQUFpQjtFQ3JCNUIsT0RzQkEsV0FBVyxjQUFjLFdBQUE7SUFDdkIsV0FBVyxpQkFBaUIsQ0FBQyxXQUFXO0lDckJ4QyxPRHNCQSxXQUFXLGVBQWU7O0lBSTdCLE1BQU0sZUFBZTtFQUN
wQixXQUFXO0VBRVgsb0JBQW9CO0dBS3JCLCtEQUFJLFNBQUMsYUFBYSxhQUFhLGFBQWEsV0FBeEM7RUM1QkgsT0Q2QkEsWUFBWSxhQUFhLEtBQUssU0FBQyxRQUFEO0lBQzVCLFFBQVEsT0FBTyxhQUFhO0lBRTVCLFlBQVk7SUM3QlosT0QrQkEsVUFBVSxXQUFBO01DOUJSLE9EK0JBLFlBQVk7T0FDWixZQUFZOztJQUtqQixpQ0FBTyxTQUFDLHVCQUFEO0VDakNOLE9Ea0NBLHNCQUFzQjtJQUl2QixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsVUFBVTtJQUNWLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLDRCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxnQkFDRTtRQUFBLGFBQWE7UUFDYixZQUFZOzs7S0FFakIsTUFBTSxnQ0FDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsZ0JBQ0U7UUFBQSxhQUFhO1FB
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
RG9CQSxPQUFPLFdBQVc7O0VBRXBCLFVBQVUsVUFBVSxXQUFBO0lDbkJsQixPRG9CQSxnQkFBZ0IsZUFBZSxLQUFLLFNBQUMsTUFBRDtNQ25CbEMsT0RvQkEsT0FBTyxXQUFXOztLQUNwQixZQUFZO0VDbEJkLE9Eb0JBLE9BQU8sSUFBSSxZQUFZLFdBQUE7SUNuQnJCLE9Eb0JBLFVBQVUsT0FBTzs7O0FDakJyQjtBQ0xBLFFBQVEsT0FBTyxZQUVkLFFBQVEsa0RBQW1CLFNBQUMsT0FBTyxhQUFhLElBQXJCO0VBQzFCLElBQUE7RUFBQSxXQUFXO0VBRVgsS0FBQyxlQUFlLFdBQUE7SUFDZCxJQUFBO0lBQUEsV0FBVyxHQUFHO0lBRWQsTUFBTSxJQUFJLFlBQVksWUFBWSxhQUNqQyxRQUFRLFNBQUMsTUFBTSxRQUFRLFNBQVMsUUFBeEI7TUFDUCxXQUFXO01DcEJYLE9EcUJBLFNBQVMsUUFBUTs7SUNuQm5CLE9EcUJBLFNBQVM7O0VDbkJYLE9EcUJBOztBQ25CRiIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlL
CBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLnJ1biAoJHJvb3RTY29wZSkgLT5cbiAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9IGZhbHNlXG4gICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSAtPlxuICAgICRyb290U2NvcGUuc2lkZWJhcl
Zpc2libGUgPSAhJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZVxuICAgICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLnZhbHVlICdmbGlua0NvbmZpZycsIHtcbiAgam9iU2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDo4MDgxJ1xuIyAgam9iU2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDozMDAwL25ldy1zZXJ2ZXInXG4gIFwicmVmcmVzaC1pbnRlcnZhbFwiOiAxMDAwMFxufVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKEpvYnNTZXJ2aWNlLCBNYWluU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkgLT5cbiAgTWFpblNlcnZpY2UubG9hZENvbmZpZygpLnRoZW4gKGNvbmZpZykgLT5cbiAgICBhbmd1bGFyLmV4dGVuZCBmbGlua0NvbmZpZywgY29uZmlnXG5cbiAgICBKb2JzU2VydmljZS5saXN0Sm9icygpXG5cbiAgICAkaW50ZXJ2YWwgLT5cbiAgICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKClcbiAgICAsIGZsaW5rQ29uZmlnW1wicmVmcmVzaC1pbnRlcnZhbFwiXVxuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbmZpZyAoJHVpVmlld1Njcm9sbFByb3ZpZGVyKSAtPlxuICAkdWlWaWV3U2Nyb2xsUHJvdmlkZXIudXNlQW5jaG9yU2Nyb2xsKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0
tLS0tLS0tLVxuXG4uY29uZmlnICgkc3RhdGVQcm92aWRlciwgJHVybFJvdXRlclByb3ZpZGVyKSAtPlxuICAkc3RhdGVQcm92aWRlci5zdGF0ZSBcIm92ZXJ2aWV3XCIsXG4gICAgdXJsOiBcIi9vdmVydmlld1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9vdmVydmlldy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udHJvbGxlcidcblxuICAuc3RhdGUgXCJydW5uaW5nLWpvYnNcIixcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL3J1bm5pbmctam9icy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgXG4gIC5zdGF0ZSBcImNvbXBsZXRlZC1qb2JzXCIsXG4gICAgdXJsOiBcIi9jb21wbGV0ZWQtam9ic1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYlwiLFxuICAgIHVybDogXCIvam9icy97am9iaWR9XCJcbiAgICBhYnN0cmFjdDogdHJ1ZVxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFy
dGlhbHMvam9icy9qb2IuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdTaW5nbGVKb2JDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhblwiLFxuICAgIHVybDogXCJcIlxuICAgIGFic3RyYWN0OiB0cnVlXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm92ZXJ2aWV3XCIsXG4gICAgdXJsOiBcIlwiXG4gICAgdmlld3M6XG4gICAgICAnbm9kZS1kZXRhaWxzJzpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLWxpc3Qub3ZlcnZpZXcuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuT3ZlcnZpZXdDb250cm9sbGVyJyBcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4uYWNjdW11bGF0b3JzXCIsXG4gICAgdXJsOiBcIi9hY2N1bXVsYXRvcnNcIlxuICAgIHZpZXdzOlxuICAgICAgJ25vZGUtZGV0YWlscyc6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS1saXN0LmFjY3VtdWxhdG9ycy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5BY2N1bXVsYXRvcnNDb250cm9sbGVyJyBcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lXCIsX
G4gICAgdXJsOiBcIi90aW1lbGluZVwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLFxuICAgIHVybDogXCIve3ZlcnRleElkfVwiXG4gICAgdmlld3M6XG4gICAgICB2ZXJ0ZXg6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLnZlcnRleC5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIixcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2Iuc3RhdGlzdGljcy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIixcbiAgICB1cmw6IFwiL2V4Y2VwdGlvbnNcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYkV4Y2VwdGlvbnNDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucHJvcGVydGllc1wiLFxuICAgIHVybDogXCIvcHJvcGVydGllc1wiXG4gICAgdm
lld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wcm9wZXJ0aWVzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUHJvcGVydGllc0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5jb25maWdcIixcbiAgICB1cmw6IFwiL2NvbmZpZ1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5jb25maWcuaHRtbFwiXG5cbiAgJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZSBcIi9vdmVydmlld1wiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pLnJ1bihmdW5jdGlvbigkcm9vdFNjb3BlKSB7XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZTtcbiAgcmV0dXJuICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSBmdW5jdGlvbigpIHtcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGU7XG4gICAgcmV0dXJuICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnO1xuICB9O1xufSkudmFsdWUoJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnLFxuICBcInJlZnJlc2gtaW50ZXJ2YWxcIjogMTAwMDBcbn0pLnJ1bihmdW5jdGlvbihKb2JzU2VydmljZSwgTWFpblNlcnZ
pY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIHtcbiAgcmV0dXJuIE1haW5TZXJ2aWNlLmxvYWRDb25maWcoKS50aGVuKGZ1bmN0aW9uKGNvbmZpZykge1xuICAgIGFuZ3VsYXIuZXh0ZW5kKGZsaW5rQ29uZmlnLCBjb25maWcpO1xuICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCk7XG4gICAgcmV0dXJuICRpbnRlcnZhbChmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBKb2JzU2VydmljZS5saXN0Sm9icygpO1xuICAgIH0sIGZsaW5rQ29uZmlnW1wicmVmcmVzaC1pbnRlcnZhbFwiXSk7XG4gIH0pO1xufSkuY29uZmlnKGZ1bmN0aW9uKCR1aVZpZXdTY3JvbGxQcm92aWRlcikge1xuICByZXR1cm4gJHVpVmlld1Njcm9sbFByb3ZpZGVyLnVzZUFuY2hvclNjcm9sbCgpO1xufSkuY29uZmlnKGZ1bmN0aW9uKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIHtcbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUoXCJvdmVydmlld1wiLCB7XG4gICAgdXJsOiBcIi9vdmVydmlld1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwicnVubmluZy1qb2JzXCIsIHtcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRl
VXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwiY29tcGxldGVkLWpvYnNcIiwge1xuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iXCIsIHtcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwiLFxuICAgIGFic3RyYWN0OiB0cnVlLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhblwiLCB7XG4gICAgdXJsOiBcIlwiLFxuICAgIGFic3RyYWN0OiB0cnVlLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG4gICAgICB9X
G4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhbi5vdmVydmlld1wiLCB7XG4gICAgdXJsOiBcIlwiLFxuICAgIHZpZXdzOiB7XG4gICAgICAnbm9kZS1kZXRhaWxzJzoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUtbGlzdC5vdmVydmlldy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuT3ZlcnZpZXdDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnBsYW4uYWNjdW11bGF0b3JzXCIsIHtcbiAgICB1cmw6IFwiL2FjY3VtdWxhdG9yc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICAnbm9kZS1kZXRhaWxzJzoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUtbGlzdC5hY2N1bXVsYXRvcnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkFjY3VtdWxhdG9yc0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IudGltZWxpbmVcIiwge1xuICAgIHVybDogXCIvdGltZWxpbmVcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIiwge1xuICAgIHVybDogXCIve3ZlcnRleElkfV
wiLFxuICAgIHZpZXdzOiB7XG4gICAgICB2ZXJ0ZXg6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUudmVydGV4Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5zdGF0aXN0aWNzXCIsIHtcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5zdGF0aXN0aWNzLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIiwge1xuICAgIHVybDogXCIvZXhjZXB0aW9uc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmV4Y2VwdGlvbnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iRXhjZXB0aW9uc0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucHJvcGVydGllc1wiLCB7XG4gICAgdXJsOiBcIi9wcm9wZXJ0aWVzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucHJvcGVydGllcy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI
6ICdKb2JQcm9wZXJ0aWVzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5jb25maWdcIiwge1xuICAgIHVybDogXCIvY29uZmlnXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuY29uZmlnLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIHJldHVybiAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlKFwiL292ZXJ2aWV3XCIpO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJ
Q0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2JzTGFiZWwnLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHRyYW5zY2x1ZGU6IHRydWVcbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6IFwiQFwiXG5cbiAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknPjxuZy10cmFuc2NsdWRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCJcbiAgXG4gIGxpbms6IChzY29wZSwgZWxlbWVudCwgYXR0cnMpIC0+XG4gICAgc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IC0+XG4gICAgICAnbGFiZ
WwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnaW5kaWNhdG9yUHJpbWFyeScsIChKb2JzU2VydmljZSkgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6ICdAJ1xuXG4gIHRlbXBsYXRlOiBcIjxpIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJyAvPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2ZhIGZhLWNpcmNsZSBpbmRpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3RhYmxlUHJvcGVydHknLCAtPlxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOlxuICAgIHZhbHVlOiAnPSdcblxuICB0ZW1wbGF0ZTogXCI8dGQgdGl0bGU9XFxcInt7dmFsdWUgfHwgJ05vbmUnfX1cXFwiPnt7dmFsdWUgfHwgJ05vbmUnfX08L3RkPlwiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ2JzTGFiZWwnLCBmdW5jdGlvbihKb2JzU2VydmljZS
kge1xuICByZXR1cm4ge1xuICAgIHRyYW5zY2x1ZGU6IHRydWUsXG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6IFwiQFwiXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8c3BhbiB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKSc+PG5nLXRyYW5zY2x1ZGU+PC9uZy10cmFuc2NsdWRlPjwvc3Bhbj5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdpbmRpY2F0b3JQcmltYXJ5JywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICBnZXRMYWJlbENsYXNzOiBcIiZcIixcbiAgICAgIHN0YXR1czogJ0AnXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8aSB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXN
zID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnZmEgZmEtY2lyY2xlIGluZGljYXRvciBpbmRpY2F0b3ItJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0YWJsZVByb3BlcnR5JywgZnVuY3Rpb24oKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgdmFsdWU6ICc9J1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkg
b2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmZpbHRlciBcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCAoYW5ndWxhck1vbWVudENvbmZpZykgLT5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSAtPlxuICAgIHJldHVybiBcIlwiICBpZiB0eXBlb2YgdmFsdWUgaXMgXCJ1bmRlZmluZWRcIiBvciB2YWx1ZSBpcyBudWxsXG5cbiAgICBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7IHRyaW06IGZhbHNlIH0pXG5cbiAgYW1EdXJhdGlvbkZvcm1hd
EV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyXG5cbi5maWx0ZXIgXCJodW1hbml6ZVRleHRcIiwgLT5cbiAgKHRleHQpIC0+XG4gICAgIyBUT0RPOiBleHRlbmQuLi4gYSBsb3RcbiAgICBpZiB0ZXh0IHRoZW4gdGV4dC5yZXBsYWNlKC8mZ3Q7L2csIFwiPlwiKS5yZXBsYWNlKC88YnJcXC8+L2csXCJcIikgZWxzZSAnJ1xuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZmlsdGVyKFwiYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkXCIsIGZ1bmN0aW9uKGFuZ3VsYXJNb21lbnRDb25maWcpIHtcbiAgdmFyIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlcjtcbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gZnVuY3Rpb24odmFsdWUsIGZvcm1hdCwgZHVyYXRpb25Gb3JtYXQpIHtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcInVuZGVmaW5lZFwiIHx8IHZhbHVlID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gXCJcIjtcbiAgICB9XG4gICAgcmV0dXJuIG1vbWVudC5kdXJhdGlvbih2YWx1ZSwgZm9ybWF0KS5mb3JtYXQoZHVyYXRpb25Gb3JtYXQsIHtcbiAgICAgIHRyaW06IGZhbHNlXG4gICAgfSk7XG4gIH07XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlci4kc3RhdGVmdWwgPSBhbmd1bGFyTW9tZW50Q29uZmlnLnN0YXRlZnVsRmlsdGVycztcbiAgcm
V0dXJuIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlcjtcbn0pLmZpbHRlcihcImh1bWFuaXplVGV4dFwiLCBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uKHRleHQpIHtcbiAgICBpZiAodGV4dCkge1xuICAgICAgcmV0dXJuIHRleHQucmVwbGFjZSgvJmd0Oy9nLCBcIj5cIikucmVwbGFjZSgvPGJyXFwvPi9nLCBcIlwiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuICcnO1xuICAgIH1cbiAgfTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQ
gYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5zZXJ2aWNlICdNYWluU2VydmljZScsICgkaHR0cCwgZmxpbmtDb25maWcsICRxKSAtPlxuICBAbG9hZENvbmZpZyA9IC0+XG4gICAgZGVmZXJyZWQgPSAkcS5kZWZlcigpXG5cbiAgICAkaHR0cC5nZXQgZmxpbmtDb25maWcuam9iU2VydmVyICsgXCIvY29uZmlnXCJcbiAgICAuc3VjY2VzcyAoZGF0YSwgc3RhdHVzLCBoZWFkZXJzLCBjb25maWcpIC0+XG4gICAgICBkZWZlcnJlZC5yZXNvbHZlKGRhdGEpXG5cbiAgICBkZWZlcnJlZC5wcm9taXNlXG5cblxuICBAXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5zZXJ2aWNlKCdNYWluU2VydmljZScsIGZ1bmN0aW9uKCRodHRwLCBmbGlua0NvbmZpZywgJHEpIHtcbiAgdGhpcy5sb2FkQ29uZmlnID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGRlZmVycmVkO1xu
ICAgIGRlZmVycmVkID0gJHEuZGVmZXIoKTtcbiAgICAkaHR0cC5nZXQoZmxpbmtDb25maWcuam9iU2VydmVyICsgXCIvY29uZmlnXCIpLnN1Y2Nlc3MoZnVuY3Rpb24oZGF0YSwgc3RhdHVzLCBoZWFkZXJzLCBjb25maWcpIHtcbiAgICAgIHJldHVybiBkZWZlcnJlZC5yZXNvbHZlKGRhdGEpO1xuICAgIH0pO1xuICAgIHJldHVybiBkZWZlcnJlZC5wcm9taXNlO1xuICB9O1xuICByZXR1cm4gdGhpcztcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljY
WJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5jb250cm9sbGVyICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInLCAoJHNjb3
BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdmaW5pc2hlZCcpXG5cbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuXG4gICRzY29wZS5qb2JPYnNlcnZlcigpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ1NpbmdsZUpvYkNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRyb290U2NvcGUpIC0+XG4gIGNvbnNvbGUubG9nICdTaW5nbGVKb2JDb250cm9sbGVyJ1xuXG4gICRzY29wZS5qb2JpZCA9ICRzdGF0ZVBhcmFtcy5qb2JpZFxuICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcbiAgJHJvb3RTY29wZS5wbGFuID0gbnVsbFxuXG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuIChkYXRhKSAtPlxuICAgICRyb290U2NvcGUuam9iID0gZGF0YVxuICAgICRyb290U2NvcGUucGxhbiA9IGRhdGEucGxhblxuXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcbiAgICAkcm9vdFNjb3BlLnB
sYW4gPSBudWxsXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Db250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iUGxhbkNvbnRyb2xsZXInXG5cbiAgJHNjb3BlLm5vZGVpZCA9IG51bGxcbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpXG5cbiAgJHNjb3BlLmNoYW5nZU5vZGUgPSAobm9kZWlkKSAtPlxuICAgIGlmIG5vZGVpZCAhPSAkc2NvcGUubm9kZWlkXG4gICAgICAkc2NvcGUubm9kZWlkID0gbm9kZWlkXG4gICAgICAkc2NvcGUudmVydGV4ID0gbnVsbFxuXG4gICAgICBpZiAkc3RhdGUuaXMoJ3NpbmdsZS1qb2IucGxhbi5vdmVydmlldycpXG4gICAgICAgIEpvYnNTZXJ2aWNlLmdldFN1YnRhc2tzKG5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAgICAgICAkc2NvcGUudmVydGV4ID0gZGF0YVxuXG4gICAgICBlbHNlIGlmICRzdGF0ZS5pcygnc2luZ2xlLWpvYi5wbGFuLmFjY3VtdWxhdG9ycycpXG4gICAgICAgIEpvYnNTZXJ2aWNlLmdldEFjY3VtdWxhdG9ycyhub2RlaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgICAgICAgJHNjb3BlLnZlcnRleCA9IGRhdGFcblxuICAgIGVsc2VcbiAgICAgICRzY29wZS5ub2RlaWQgPSBudWxsXG4gICAgICAkc2NvcGUudmVydGV4ID0gbnVsbFxuXG4jIC0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQbGFuT3ZlcnZpZXdDb250cm9sbGVyJywgKCRzY29wZSwgSm9ic1NlcnZpY2UpIC0+XG4gIGNvbnNvbGUubG9nICdKb2JQbGFuT3ZlcnZpZXdDb250cm9sbGVyJ1xuXG4gIGlmICRzY29wZS5ub2RlaWQgYW5kICEkc2NvcGUudmVydGV4LnN0XG4gICAgSm9ic1NlcnZpY2UuZ2V0U3VidGFza3MoJHNjb3BlLm5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAgICRzY29wZS52ZXJ0ZXggPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5BY2N1bXVsYXRvcnNDb250cm9sbGVyJywgKCRzY29wZSwgSm9ic1NlcnZpY2UpIC0+XG4gIGNvbnNvbGUubG9nICdKb2JQbGFuQWNjdW11bGF0b3JzQ29udHJvbGxlcidcblxuICBpZiAkc2NvcGUubm9kZWlkIGFuZCAhJHNjb3BlLnZlcnRleC5hY2N1bXVsYXRvcnNcbiAgICBKb2JzU2VydmljZS5nZXRBY2N1bXVsYXRvcnMoJHNjb3BlLm5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAgICRzY29wZS52ZXJ0ZXggPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhc
mFtcy52ZXJ0ZXhJZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUudmVydGV4ID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JFeGNlcHRpb25zQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgSm9ic1NlcnZpY2UubG9hZEV4Y2VwdGlvbnMoKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS5leGNlcHRpb25zID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQcm9wZXJ0aWVzQ29udHJvbGxlcicsICgkc2NvcGUsIEpvYnNTZXJ2aWNlKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iUHJvcGVydGllc0NvbnRyb2xsZXInXG5cbiAgJHNjb3BlLmNoYW5nZU5vZGUgPSAobm9kZWlkKSAtPlxuICAgIGlmIG5vZGVpZCAhPSAkc2NvcGUubm9kZWlkXG4gICAgICAkc2NvcGUubm9kZWlkID0gbm9kZWlkXG5cbiAgICAgIEpvYnNTZXJ2aWNlLmdldE5vZGUobm9kZWlkKS50aGVuIChkYXRhKSAtPlxuICAgICAgICAkc2NvcGUubm9kZSA9IGRhdGFcblxuICAgIGVsc2VcbiAgICAgICRzY29wZS5ub2RlaWQgPSBudWxsXG4gICAgICAkc2NvcGUubm9kZSA9IG51bGxcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmNvbnRyb2xsZXIoJ1J1bm5pbmdKb2JzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJH
N0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ3J1bm5pbmcnKTtcbiAgfTtcbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgfSk7XG4gIHJldHVybiAkc2NvcGUuam9iT2JzZXJ2ZXIoKTtcbn0pLmNvbnRyb2xsZXIoJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygnZmluaXNoZWQnKTtcbiAgfTtcbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgfSk7XG4gIHJldHVybiAkc2NvcGUuam9iT2JzZXJ2ZXIoKTtcbn0pLmNvbnRyb2xsZXIoJ1NpbmdsZUp
vYkNvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHJvb3RTY29wZSkge1xuICBjb25zb2xlLmxvZygnU2luZ2xlSm9iQ29udHJvbGxlcicpO1xuICAkc2NvcGUuam9iaWQgPSAkc3RhdGVQYXJhbXMuam9iaWQ7XG4gICRyb290U2NvcGUuam9iID0gbnVsbDtcbiAgJHJvb3RTY29wZS5wbGFuID0gbnVsbDtcbiAgSm9ic1NlcnZpY2UubG9hZEpvYigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICRyb290U2NvcGUuam9iID0gZGF0YTtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5wbGFuID0gZGF0YS5wbGFuO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgJHJvb3RTY29wZS5qb2IgPSBudWxsO1xuICAgIHJldHVybiAkcm9vdFNjb3BlLnBsYW4gPSBudWxsO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Db250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgY29uc29sZS5sb2coJ0pvYlBsYW5Db250cm9sbGVyJyk7XG4gICRzY29wZS5ub2RlaWQgPSBudWxsO1xuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KCk7XG4gIHJldHVybiAkc2NvcGUuY2hhbmdlTm9kZSA9IGZ1bmN0aW9uKG5vZGVpZCkge1xuICAgIGlmIChub2RlaWQgIT09ICRzY29wZS5ub2Rl
aWQpIHtcbiAgICAgICRzY29wZS5ub2RlaWQgPSBub2RlaWQ7XG4gICAgICAkc2NvcGUudmVydGV4ID0gbnVsbDtcbiAgICAgIGlmICgkc3RhdGUuaXMoJ3NpbmdsZS1qb2IucGxhbi5vdmVydmlldycpKSB7XG4gICAgICAgIHJldHVybiBKb2JzU2VydmljZS5nZXRTdWJ0YXNrcyhub2RlaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICAgIHJldHVybiAkc2NvcGUudmVydGV4ID0gZGF0YTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYgKCRzdGF0ZS5pcygnc2luZ2xlLWpvYi5wbGFuLmFjY3VtdWxhdG9ycycpKSB7XG4gICAgICAgIHJldHVybiBKb2JzU2VydmljZS5nZXRBY2N1bXVsYXRvcnMobm9kZWlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgICByZXR1cm4gJHNjb3BlLnZlcnRleCA9IGRhdGE7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAkc2NvcGUubm9kZWlkID0gbnVsbDtcbiAgICAgIHJldHVybiAkc2NvcGUudmVydGV4ID0gbnVsbDtcbiAgICB9XG4gIH07XG59KS5jb250cm9sbGVyKCdKb2JQbGFuT3ZlcnZpZXdDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCBKb2JzU2VydmljZSkge1xuICBjb25zb2xlLmxvZygnSm9iUGxhbk92ZXJ2aWV3Q29udHJvbGxlcicpO1xuICBpZiAoJHNjb3BlLm5vZGVpZCAmJiAhJHNjb3BlLnZlcnRleC5zdCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS5nZXRTdWJ0YXNrcygkc2NvcGUubm9kZWlkK
S50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgIHJldHVybiAkc2NvcGUudmVydGV4ID0gZGF0YTtcbiAgICB9KTtcbiAgfVxufSkuY29udHJvbGxlcignSm9iUGxhbkFjY3VtdWxhdG9yc0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsIEpvYnNTZXJ2aWNlKSB7XG4gIGNvbnNvbGUubG9nKCdKb2JQbGFuQWNjdW11bGF0b3JzQ29udHJvbGxlcicpO1xuICBpZiAoJHNjb3BlLm5vZGVpZCAmJiAhJHNjb3BlLnZlcnRleC5hY2N1bXVsYXRvcnMpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0QWNjdW11bGF0b3JzKCRzY29wZS5ub2RlaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICAgcmV0dXJuICRzY29wZS52ZXJ0ZXggPSBkYXRhO1xuICAgIH0pO1xuICB9XG59KS5jb250cm9sbGVyKCdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy52ZXJ0ZXhJZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS52ZXJ0ZXggPSBkYXRhO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYkV4Y2VwdGlvbnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmxvYWRFeGNlcHRpb25zKCkudGhlbihmdW5jdG
lvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5leGNlcHRpb25zID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQcm9wZXJ0aWVzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgSm9ic1NlcnZpY2UpIHtcbiAgY29uc29sZS5sb2coJ0pvYlByb3BlcnRpZXNDb250cm9sbGVyJyk7XG4gIHJldHVybiAkc2NvcGUuY2hhbmdlTm9kZSA9IGZ1bmN0aW9uKG5vZGVpZCkge1xuICAgIGlmIChub2RlaWQgIT09ICRzY29wZS5ub2RlaWQpIHtcbiAgICAgICRzY29wZS5ub2RlaWQgPSBub2RlaWQ7XG4gICAgICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0Tm9kZShub2RlaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICByZXR1cm4gJHNjb3BlLm5vZGUgPSBkYXRhO1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICRzY29wZS5ub2RlaWQgPSBudWxsO1xuICAgICAgcmV0dXJuICRzY29wZS5ub2RlID0gbnVsbDtcbiAgICB9XG4gIH07XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVx
uIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAndmVydGV4JywgKCRzdGF0ZSkgLT5cbiAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUgc2Vjb25kYXJ5JyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48
L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGRhdGE6IFwiPVwiXG5cbiAgbGluazogKHNjb3BlLCBlbGVtLCBhdHRycykgLT5cbiAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEuc3VidGFza3MsIChzdWJ0YXNrLCBpKSAtPlxuICAgICAgICB0ZXN0RGF0YS5wdXNoIHtcbiAgICAgICAgICBsYWJlbDogXCIje3N1YnRhc2suaG9zdH0gKCN7c3VidGFzay5zdWJ0YXNrfSlcIlxuICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiM2NjZcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiU0NIRURVTEVEXCJdXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJERVBMT1lJTkdcIl1cbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIkRlcGxveWluZ1wiXG4gI
CAgICAgICAgICAgIGNvbG9yOiBcIiNhYWFcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiREVQTE9ZSU5HXCJdXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU5HXCJdXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJSdW5uaW5nXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2RkZFwiXG4gICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU5HXCJdXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJGSU5JU0hFRFwiXVxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBdXG4gICAgICAgIH1cblxuICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKClcbiAgICAgIC50aWNrRm9ybWF0KHtcbiAgICAgICAgZm9ybWF0OiBkMy50aW1lLmZvcm1hdChcIiVMXCIpXG4gICAgICAgICMgdGlja0ludGVydmFsOiAxXG4gICAgICAgIHRpY2tTaXplOiAxXG4gICAgICB9KVxuICAgICAgLnByZWZpeChcInNpbmdsZVwiKVxuICAgICAgLmxhYmVsRm9ybW
F0KChsYWJlbCkgLT5cbiAgICAgICAgbGFiZWxcbiAgICAgIClcbiAgICAgIC5tYXJnaW4oeyBsZWZ0OiAxMDAsIHJpZ2h0OiAwLCB0b3A6IDAsIGJvdHRvbTogMCB9KVxuICAgICAgLml0ZW1IZWlnaHQoMzApXG4gICAgICAucmVsYXRpdmVUaW1lKClcblxuICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKVxuICAgICAgLmRhdHVtKHRlc3REYXRhKVxuICAgICAgLmNhbGwoY2hhcnQpXG5cbiAgICBhbmFseXplVGltZShzY29wZS5kYXRhKVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0aW1lbGluZScsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGpvYjogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpXG5cbiAgICB0cmFuc2xhdGVMYWJlbCA9IChsYWJlbCkgLT5cbiAgICAgIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgdGVzdERhdGEucHV
zaCBcbiAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIlxuICAgICAgICAgIGNvbG9yOiBcIiNjY2NjY2NcIlxuICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEudGltZXN0YW1wc1tcIkNSRUFURURcIl1cbiAgICAgICAgICBlbmRpbmdfdGltZTogZGF0YS50aW1lc3RhbXBzW1wiQ1JFQVRFRFwiXSArIDFcbiAgICAgICAgICB0eXBlOiAnc2NoZWR1bGVkJ1xuICAgICAgICBdXG5cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEudmVydGljZXMsICh2ZXJ0ZXgpIC0+XG4gICAgICAgIGlmIHZlcnRleFsnc3RhcnQtdGltZSddID4gLTFcbiAgICAgICAgICB0ZXN0RGF0YS5wdXNoIFxuICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAgbGFiZWw6IHRyYW5zbGF0ZUxhYmVsKHZlcnRleC5uYW1lKVxuICAgICAgICAgICAgICBjb2xvcjogXCIjZDlmMWY3XCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzYyY2RlYVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZlcnRleFsnc3RhcnQtdGltZSddXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2ZXJ0ZXhbJ2VuZC10aW1lJ11cbiAgICAgICAgICAgICAgbGluazogdmVydGV4LmlkXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgXVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5j
bGljaygoZCwgaSwgZGF0dW0pIC0+XG4gICAgICAgIGlmIGQubGlua1xuICAgICAgICAgICRzdGF0ZS5nbyBcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHsgam9iaWQ6IGRhdGEuamlkLCB2ZXJ0ZXhJZDogZC5saW5rIH1cblxuICAgICAgKVxuICAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICAgICAgIyB0aWNrVGltZTogZDMudGltZS5zZWNvbmRcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDAuNVxuICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJtYWluXCIpXG4gICAgICAubWFyZ2luKHsgbGVmdDogMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgzMClcbiAgICAgIC5zaG93Qm9yZGVyTGluZSgpXG4gICAgICAuc2hvd0hvdXJUaW1lbGluZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgc2NvcGUuJHdhdGNoIGF0dHJzLmpvYiwgKGRhdGEpIC0+XG4gICAgICBhbmFseXplVGltZShkYXRhKSBpZiBkYXRhXG5cbiAgICByZXR1cm5cblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2pvYlBsYW4nLCAoJHRpbWVvdXQpIC0+XG4gIHRlbXBsYXRlOiBcIlxuICAgIDxzdmcgY2xhc3M9J2dyY
XBoJyB3aWR0aD0nNTAwJyBoZWlnaHQ9JzQwMCc+PGcgLz48L3N2Zz5cbiAgICA8c3ZnIGNsYXNzPSd0bXAnIHdpZHRoPScxJyBoZWlnaHQ9JzEnPjxnIC8+PC9zdmc+XG4gICAgPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+XG4gICAgICA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20taW4nIG5nLWNsaWNrPSd6b29tSW4oKSc+PGkgY2xhc3M9J2ZhIGZhLXBsdXMnIC8+PC9hPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLW91dCcgbmctY2xpY2s9J3pvb21PdXQoKSc+PGkgY2xhc3M9J2ZhIGZhLW1pbnVzJyAvPjwvYT5cbiAgICA8L2Rpdj5cIlxuXG4gIHNjb3BlOlxuICAgIHBsYW46ICc9J1xuICAgIHNldE5vZGU6ICcmJ1xuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgbWFpblpvb20gPSBkMy5iZWhhdmlvci56b29tKClcbiAgICBzdWJncmFwaHMgPSBbXVxuICAgIGpvYmlkID0gYXR0cnMuam9iaWRcblxuICAgIG1haW5TdmdFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzBdXG4gICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXVxuICAgIG1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdXG5cbiAgICBkM21haW5TdmcgPSBkMy5zZWxlY3QobWFpblN2Z0VsZW1lbnQpXG4gICAgZDNtYWluU3ZnRyA9IGQzLnNlbGVjdChtYWluRylcbiAgICBkM3RtcFN2ZyA9IGQzLnNlbGVjdChtYWluVG1wRWxlbWVudClcbl
xuICAgICMgYW5ndWxhci5lbGVtZW50KG1haW5HKS5lbXB0eSgpXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KGVsZW0uY2hpbGRyZW4oKVswXSkud2lkdGgoY29udGFpbmVyVylcblxuICAgIHNjb3BlLnpvb21JbiA9IC0+XG4gICAgICBpZiBtYWluWm9vbS5zY2FsZSgpIDwgMi45OVxuICAgICAgICBcbiAgICAgICAgIyBDYWxjdWxhdGUgYW5kIHN0b3JlIG5ldyB2YWx1ZXMgaW4gem9vbSBvYmplY3RcbiAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKClcbiAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgbWFpblpvb20uc2NhbGUgbWFpblpvb20uc2NhbGUoKSArIDAuMVxuICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUgWyB2MSwgdjIgXVxuICAgICAgICBcbiAgICAgICAgIyBUcmFuc2Zvcm0gc3ZnXG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIlxuXG4gICAgc2NvcGUuem9vbU91dCA9IC0+XG4gICAgICBpZiBtYWluWm9vbS5zY2FsZSgpID4gMC4zMVxuICA
gICAgICBcbiAgICAgICAgIyBDYWxjdWxhdGUgYW5kIHN0b3JlIG5ldyB2YWx1ZXMgaW4gbWFpblpvb20gb2JqZWN0XG4gICAgICAgIG1haW5ab29tLnNjYWxlIG1haW5ab29tLnNjYWxlKCkgLSAwLjFcbiAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKClcbiAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgbWFpblpvb20udHJhbnNsYXRlIFsgdjEsIHYyIF1cbiAgICAgICAgXG4gICAgICAgICMgVHJhbnNmb3JtIHN2Z1xuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCJcblxuICAgICNjcmVhdGUgYSBsYWJlbCBvZiBhbiBlZGdlXG4gICAgY3JlYXRlTGFiZWxFZGdlID0gKGVsKSAtPlxuICAgICAgbGFiZWxWYWx1ZSA9IFwiXCJcbiAgICAgIGlmIGVsLnNoaXBfc3RyYXRlZ3k/IG9yIGVsLmxvY2FsX3N0cmF0ZWd5P1xuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGRpdiBjbGFzcz0nZWRnZS1sYWJlbCc+XCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBlbC5zaGlwX3N0cmF0ZWd5ICBpZiBlbC5zaGlwX3N0cmF0ZWd5
P1xuICAgICAgICBsYWJlbFZhbHVlICs9IFwiIChcIiArIGVsLnRlbXBfbW9kZSArIFwiKVwiICB1bmxlc3MgZWwudGVtcF9tb2RlIGlzIGB1bmRlZmluZWRgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCIsPGJyPlwiICsgZWwubG9jYWxfc3RyYXRlZ3kgIHVubGVzcyBlbC5sb2NhbF9zdHJhdGVneSBpcyBgdW5kZWZpbmVkYFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPC9kaXY+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuXG4gICAgIyB0cnVlLCBpZiB0aGUgbm9kZSBpcyBhIHNwZWNpYWwgbm9kZSBmcm9tIGFuIGl0ZXJhdGlvblxuICAgIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUgPSAoaW5mbykgLT5cbiAgICAgIChpbmZvIGlzIFwicGFydGlhbFNvbHV0aW9uXCIgb3IgaW5mbyBpcyBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiBvciBpbmZvIGlzIFwid29ya3NldFwiIG9yIGluZm8gaXMgXCJuZXh0V29ya3NldFwiIG9yIGluZm8gaXMgXCJzb2x1dGlvblNldFwiIG9yIGluZm8gaXMgXCJzb2x1dGlvbkRlbHRhXCIpXG5cbiAgICBnZXROb2RlVHlwZSA9IChlbCwgaW5mbykgLT5cbiAgICAgIGlmIGluZm8gaXMgXCJtaXJyb3JcIlxuICAgICAgICAnbm9kZS1taXJyb3InXG5cbiAgICAgIGVsc2UgaWYgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKVxuICAgICAgICAnbm9kZS1pdGVyYXRpb24nXG5cbiAgICAgIGVsc2VcbiAgICAgICAgICAnbm9kZS1ub3JtYWwnXG4gICAgICBcbiAgICAjIGNyZWF0ZXMgdGhlIGxhY
mVsIG9mIGEgbm9kZSwgaW4gaW5mbyBpcyBzdG9yZWQsIHdoZXRoZXIgaXQgaXMgYSBzcGVjaWFsIG5vZGUgKGxpa2UgYSBtaXJyb3IgaW4gYW4gaXRlcmF0aW9uKVxuICAgIGNyZWF0ZUxhYmVsTm9kZSA9IChlbCwgaW5mbywgbWF4VywgbWF4SCkgLT5cbiAgICAgICMgbGFiZWxWYWx1ZSA9IFwiPGEgaHJlZj0nIy9qb2JzL1wiICsgam9iaWQgKyBcIi92ZXJ0ZXgvXCIgKyBlbC5pZCArIFwiJyBjbGFzcz0nbm9kZS1sYWJlbCBcIiArIGdldE5vZGVUeXBlKGVsLCBpbmZvKSArIFwiJz5cIlxuICAgICAgbGFiZWxWYWx1ZSA9IFwiPGRpdiBocmVmPScjL2pvYnMvXCIgKyBqb2JpZCArIFwiL3ZlcnRleC9cIiArIGVsLmlkICsgXCInIGNsYXNzPSdub2RlLWxhYmVsIFwiICsgZ2V0Tm9kZVR5cGUoZWwsIGluZm8pICsgXCInPlwiXG5cbiAgICAgICMgTm9kZW5hbWVcbiAgICAgIGlmIGluZm8gaXMgXCJtaXJyb3JcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPk1pcnJvciBvZiBcIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiXG4gICAgICBlbHNlXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+XCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIlxuICAgICAgaWYgZWwuZGVzY3JpcHRpb24gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiXCJcbiAgICAgIGVsc2VcbiAgICAgICAgc3RlcE5hbWUgPSBlbC5kZXNjcmlwdGlvblxuICAgIC
AgICBcbiAgICAgICAgIyBjbGVhbiBzdGVwTmFtZVxuICAgICAgICBzdGVwTmFtZSA9IHNob3J0ZW5TdHJpbmcoc3RlcE5hbWUpXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDQgY2xhc3M9J3N0ZXAtbmFtZSc+XCIgKyBzdGVwTmFtZSArIFwiPC9oND5cIlxuICAgICAgXG4gICAgICAjIElmIHRoaXMgbm9kZSBpcyBhbiBcIml0ZXJhdGlvblwiIHdlIG5lZWQgYSBkaWZmZXJlbnQgcGFuZWwtYm9keVxuICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvbj9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24oZWwuaWQsIG1heFcsIG1heEgpXG4gICAgICBlbHNlXG4gICAgICAgIFxuICAgICAgICAjIE90aGVyd2lzZSBhZGQgaW5mb3MgICAgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+XCIgKyBpbmZvICsgXCIgTm9kZTwvaDU+XCIgIGlmIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbylcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5QYXJhbGxlbGlzbTogXCIgKyBlbC5wYXJhbGxlbGlzbSArIFwiPC9oNT5cIiAgdW5sZXNzIGVsLnBhcmFsbGVsaXNtIGlzIFwiXCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5PcGVyYXRpb246IFwiICsgc2hvcnRlblN0cmluZyhlbC5vcGVyYXRvcl9zdHJhdGVneSkgKyBcIjwvaDU+XCIgIHVubGVzcyBlbC5vcGVyYXRvciBpcyBgdW5kZWZpbmVkYFxuICAgICAgXG4gICAgICAjIGxhYmVsVmFsdWUgKz0gXCI8L2E+XCJcbiA
gICAgIGxhYmVsVmFsdWUgKz0gXCI8L2Rpdj5cIlxuICAgICAgbGFiZWxWYWx1ZVxuXG4gICAgIyBFeHRlbmRzIHRoZSBsYWJlbCBvZiBhIG5vZGUgd2l0aCBhbiBhZGRpdGlvbmFsIHN2ZyBFbGVtZW50IHRvIHByZXNlbnQgdGhlIGl0ZXJhdGlvbi5cbiAgICBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24gPSAoaWQsIG1heFcsIG1heEgpIC0+XG4gICAgICBzdmdJRCA9IFwic3ZnLVwiICsgaWRcblxuICAgICAgbGFiZWxWYWx1ZSA9IFwiPHN2ZyBjbGFzcz0nXCIgKyBzdmdJRCArIFwiJyB3aWR0aD1cIiArIG1heFcgKyBcIiBoZWlnaHQ9XCIgKyBtYXhIICsgXCI+PGcgLz48L3N2Zz5cIlxuICAgICAgbGFiZWxWYWx1ZVxuXG4gICAgIyBTcGxpdCBhIHN0cmluZyBpbnRvIG11bHRpcGxlIGxpbmVzIHNvIHRoYXQgZWFjaCBsaW5lIGhhcyBsZXNzIHRoYW4gMzAgbGV0dGVycy5cbiAgICBzaG9ydGVuU3RyaW5nID0gKHMpIC0+XG4gICAgICAjIG1ha2Ugc3VyZSB0aGF0IG5hbWUgZG9lcyBub3QgY29udGFpbiBhIDwgKGJlY2F1c2Ugb2YgaHRtbClcbiAgICAgIGlmIHMuY2hhckF0KDApIGlzIFwiPFwiXG4gICAgICAgIHMgPSBzLnJlcGxhY2UoXCI8XCIsIFwiJmx0O1wiKVxuICAgICAgICBzID0gcy5yZXBsYWNlKFwiPlwiLCBcIiZndDtcIilcbiAgICAgIHNiciA9IFwiXCJcbiAgICAgIHdoaWxlIHMubGVuZ3RoID4gMzBcbiAgICAgICAgc2JyID0gc2JyICsgcy5zdWJzdHJpbmcoMCwgMzApICsgXCI8YnI+XCJcbiAg
ICAgICAgcyA9IHMuc3Vic3RyaW5nKDMwLCBzLmxlbmd0aClcbiAgICAgIHNiciA9IHNiciArIHNcbiAgICAgIHNiclxuXG4gICAgY3JlYXRlTm9kZSA9IChnLCBkYXRhLCBlbCwgaXNQYXJlbnQgPSBmYWxzZSwgbWF4VywgbWF4SCkgLT5cbiAgICAgICMgY3JlYXRlIG5vZGUsIHNlbmQgYWRkaXRpb25hbCBpbmZvcm1hdGlvbnMgYWJvdXQgdGhlIG5vZGUgaWYgaXQgaXMgYSBzcGVjaWFsIG9uZVxuICAgICAgaWYgZWwuaWQgaXMgZGF0YS5wYXJ0aWFsX3NvbHV0aW9uXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEubmV4dF9wYXJ0aWFsX3NvbHV0aW9uXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS53b3Jrc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlb
C5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIndvcmtzZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJ3b3Jrc2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5uZXh0X3dvcmtzZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFdvcmtzZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0V29ya3NldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEuc29sdXRpb25fc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uU2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25TZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLnNvbHV0aW9uX2RlbHRhXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uRGVsdGFcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1
xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIpXG5cbiAgICAgIGVsc2VcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwiXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwiXCIpXG5cbiAgICBjcmVhdGVFZGdlID0gKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKSAtPlxuICAgICAgdW5sZXNzIGV4aXN0aW5nTm9kZXMuaW5kZXhPZihwcmVkLmlkKSBpcyAtMVxuICAgICAgICBnLnNldEVkZ2UgcHJlZC5pZCwgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShwcmVkKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgYXJyb3doZWFkOiAnbm9ybWFsJ1xuXG4gICAgICBlbHNlXG4gICAgICAgIG1pc3NpbmdOb2RlID0gc2VhcmNoRm9yTm9kZShkYXRhLCBwcmVkLmlkKVxuICAgICAgICB1bmxlc3MgIW1pc3NpbmdOb2RlIG9yIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCBpcyB0cnVlXG4gICAgICAgICAgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkID0gdHJ1ZVxuICAgICAgICAgIGcuc2V0Tm9kZSBtaXNzaW5nTm9kZS5pZCxcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUobWlzc2luZ05vZGUsIFwibWlycm9yXCIpXG4gICAgICAgICAgICB
sYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKG1pc3NpbmdOb2RlLCAnbWlycm9yJylcblxuICAgICAgICAgIGcuc2V0RWRnZSBtaXNzaW5nTm9kZS5pZCwgZWwuaWQsXG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKG1pc3NpbmdOb2RlKVxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcblxuICAgIGxvYWRKc29uVG9EYWdyZSA9IChnLCBkYXRhKSAtPlxuICAgICAgZXhpc3RpbmdOb2RlcyA9IFtdXG5cbiAgICAgIGlmIGRhdGEubm9kZXM/XG4gICAgICAgICMgVGhpcyBpcyB0aGUgbm9ybWFsIGpzb24gZGF0YVxuICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLm5vZGVzXG5cbiAgICAgIGVsc2VcbiAgICAgICAgIyBUaGlzIGlzIGFuIGl0ZXJhdGlvbiwgd2Ugbm93IHN0b3JlIHNwZWNpYWwgaXRlcmF0aW9uIG5vZGVzIGlmIHBvc3NpYmxlXG4gICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEuc3RlcF9mdW5jdGlvblxuICAgICAgICBpc1BhcmVudCA9IHRydWVcblxuICAgICAgZm9yIGVsIGluIHRvSXRlcmF0ZVxuICAgICAgICBtYXhXID0gMFxuICAgICAgICBtYXhIID0gMFxuXG4gICAgICAgIGlmIGVsLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgICBzZyA9IG5ldyBkYWdyZUQzLmdyYXBobGliLkdyYXBoKHsgbXVsdGlncmFwaDogdHJ1ZSwgY29tcG91bmQ6IHRydWUgfSkuc2V0R3JhcGgoe1xuICAgICAgICAgICAgbm9kZXNlcDogMjBcbiAgICAgICAgICAgIGVk
Z2VzZXA6IDBcbiAgICAgICAgICAgIHJhbmtzZXA6IDIwXG4gICAgICAgICAgICByYW5rZGlyOiBcIkxSXCJcbiAgICAgICAgICAgIG1hcmdpbng6IDEwXG4gICAgICAgICAgICBtYXJnaW55OiAxMFxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgIHN1YmdyYXBoc1tlbC5pZF0gPSBzZ1xuXG4gICAgICAgICAgbG9hZEpzb25Ub0RhZ3JlKHNnLCBlbClcblxuICAgICAgICAgIHIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKVxuICAgICAgICAgIGQzdG1wU3ZnLnNlbGVjdCgnZycpLmNhbGwociwgc2cpXG4gICAgICAgICAgbWF4VyA9IHNnLmdyYXBoKCkud2lkdGhcbiAgICAgICAgICBtYXhIID0gc2cuZ3JhcGgoKS5oZWlnaHRcblxuICAgICAgICAgIGFuZ3VsYXIuZWxlbWVudChtYWluVG1wRWxlbWVudCkuZW1wdHkoKVxuXG4gICAgICAgIGNyZWF0ZU5vZGUoZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXhXLCBtYXhIKVxuXG4gICAgICAgIGV4aXN0aW5nTm9kZXMucHVzaCBlbC5pZFxuICAgICAgICBcbiAgICAgICAgIyBjcmVhdGUgZWRnZXMgZnJvbSBpbnB1dHMgdG8gY3VycmVudCBub2RlXG4gICAgICAgIGlmIGVsLmlucHV0cz9cbiAgICAgICAgICBmb3IgcHJlZCBpbiBlbC5pbnB1dHNcbiAgICAgICAgICAgIGNyZWF0ZUVkZ2UoZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpXG5cbiAgICAgIGdcblxuICAgICMgc2VhcmNoZXMgaW4gdGhlIGdsb2JhbCBKU09ORGF0YSBmb3IgdGhlIG5vZGUgd2l0aCB0a
GUgZ2l2ZW4gaWRcbiAgICBzZWFyY2hGb3JOb2RlID0gKGRhdGEsIG5vZGVJRCkgLT5cbiAgICAgIGZvciBpIG9mIGRhdGEubm9kZXNcbiAgICAgICAgZWwgPSBkYXRhLm5vZGVzW2ldXG4gICAgICAgIHJldHVybiBlbCAgaWYgZWwuaWQgaXMgbm9kZUlEXG4gICAgICAgIFxuICAgICAgICAjIGxvb2sgZm9yIG5vZGVzIHRoYXQgYXJlIGluIGl0ZXJhdGlvbnNcbiAgICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvbj9cbiAgICAgICAgICBmb3IgaiBvZiBlbC5zdGVwX2Z1bmN0aW9uXG4gICAgICAgICAgICByZXR1cm4gZWwuc3RlcF9mdW5jdGlvbltqXSAgaWYgZWwuc3RlcF9mdW5jdGlvbltqXS5pZCBpcyBub2RlSURcblxuICAgIGRyYXdHcmFwaCA9IChkYXRhKSAtPlxuICAgICAgZyA9IG5ldyBkYWdyZUQzLmdyYXBobGliLkdyYXBoKHsgbXVsdGlncmFwaDogdHJ1ZSwgY29tcG91bmQ6IHRydWUgfSkuc2V0R3JhcGgoe1xuICAgICAgICBub2Rlc2VwOiA3MFxuICAgICAgICBlZGdlc2VwOiAwXG4gICAgICAgIHJhbmtzZXA6IDUwXG4gICAgICAgIHJhbmtkaXI6IFwiTFJcIlxuICAgICAgICBtYXJnaW54OiA0MFxuICAgICAgICBtYXJnaW55OiA0MFxuICAgICAgICB9KVxuXG4gICAgICBsb2FkSnNvblRvRGFncmUoZywgZGF0YSlcblxuICAgICAgcmVuZGVyZXIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKVxuICAgICAgZDNtYWluU3ZnRy5jYWxsKHJlbmRlcmVyLCBnKVxuXG4gICAgICBmb3IgaSwgc2cgb2Ygc3ViZ3JhcGhzXG
4gICAgICAgIGQzbWFpblN2Zy5zZWxlY3QoJ3N2Zy5zdmctJyArIGkgKyAnIGcnKS5jYWxsKHJlbmRlcmVyLCBzZylcblxuICAgICAgbmV3U2NhbGUgPSAwLjVcblxuICAgICAgeENlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkud2lkdGgoKSAtIGcuZ3JhcGgoKS53aWR0aCAqIG5ld1NjYWxlKSAvIDIpXG4gICAgICB5Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS5oZWlnaHQoKSAtIGcuZ3JhcGgoKS5oZWlnaHQgKiBuZXdTY2FsZSkgLyAyKVxuXG4gICAgICBtYWluWm9vbS5zY2FsZShuZXdTY2FsZSkudHJhbnNsYXRlKFt4Q2VudGVyT2Zmc2V0LCB5Q2VudGVyT2Zmc2V0XSlcblxuICAgICAgZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgeENlbnRlck9mZnNldCArIFwiLCBcIiArIHlDZW50ZXJPZmZzZXQgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpXG5cbiAgICAgIG1haW5ab29tLm9uKFwiem9vbVwiLCAtPlxuICAgICAgICBldiA9IGQzLmV2ZW50XG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIGV2LnRyYW5zbGF0ZSArIFwiKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIpXCJcbiAgICAgIClcbiAgICAgIG1haW5ab29tKGQzbWFpblN2ZylcblxuICAgICAgZDNtYWluU3ZnRy5
zZWxlY3RBbGwoJy5ub2RlJykub24gJ2NsaWNrJywgKGQpIC0+XG4gICAgICAgIHNjb3BlLnNldE5vZGUoeyBub2RlaWQ6IGQgfSlcblxuICAgIHNjb3BlLiR3YXRjaCBhdHRycy5wbGFuLCAobmV3UGxhbikgLT5cbiAgICAgIGRyYXdHcmFwaChuZXdQbGFuKSBpZiBuZXdQbGFuXG5cbiAgICByZXR1cm5cbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmRpcmVjdGl2ZSgndmVydGV4JywgZnVuY3Rpb24oJHN0YXRlKSB7XG4gIHJldHVybiB7XG4gICAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUgc2Vjb25kYXJ5JyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIixcbiAgICBzY29wZToge1xuICAgICAgZGF0YTogXCI9XCJcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGFuYWx5emVUaW1lLCBjb250YWluZXJXLCBzdmdFbDtcbiAgICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKTtcbiAgICAgIGFuYWx5emVUaW1lID0gZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICB2YXIgY2hhcnQsIHN2ZywgdGVzdERhdGE7XG4gICAgICAgIHRlc3REYXRhID0gW107XG4gICAgICAgIGFuZ3VsYXIuZm9yRWFjaChkYXRhLnN1YnRhc2tzLCBmdW5jdGlvbihzdWJ0YXNrLCBpKSB7XG4g
ICAgICAgICAgcmV0dXJuIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgICAgbGFiZWw6IHN1YnRhc2suaG9zdCArIFwiIChcIiArIHN1YnRhc2suc3VidGFzayArIFwiKVwiLFxuICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiM2NjZcIixcbiAgICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiU0NIRURVTEVEXCJdLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJERVBMT1lJTkdcIl0sXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH0sIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogXCJEZXBsb3lpbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjYWFhXCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkRFUExPWUlOR1wiXSxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiUlVOTklOR1wiXSxcbiAgICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgfSwge1xuICAgICAgICAgICAgI
CAgIGxhYmVsOiBcIlJ1bm5pbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlJVTk5JTkdcIl0sXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkZJTklTSEVEXCJdLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICBdXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS50aWNrRm9ybWF0KHtcbiAgICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIiksXG4gICAgICAgICAgdGlja1NpemU6IDFcbiAgICAgICAgfSkucHJlZml4KFwic2luZ2xlXCIpLmxhYmVsRm9ybWF0KGZ1bmN0aW9uKGxhYmVsKSB7XG4gICAgICAgICAgcmV0dXJuIGxhYmVsO1xuICAgICAgICB9KS5tYXJnaW4oe1xuICAgICAgICAgIGxlZnQ6IDEwMCxcbiAgICAgICAgICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMzApLnJlbGF0aXZlVGltZSgpO1xuICAgICAgICByZXR1cm4gc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCk7XG4gICAgICB9O1
xuICAgICAgYW5hbHl6ZVRpbWUoc2NvcGUuZGF0YSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0aW1lbGluZScsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIixcbiAgICBzY29wZToge1xuICAgICAgam9iOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5lclcsIHN2Z0VsLCB0cmFuc2xhdGVMYWJlbDtcbiAgICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKTtcbiAgICAgIHRyYW5zbGF0ZUxhYmVsID0gZnVuY3Rpb24obGFiZWwpIHtcbiAgICAgICAgcmV0dXJuIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKTtcbiAgICAgIH07XG4gICAgICBhbmFseXplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGNoYXJ0LCBzdmcsIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIixcbiAgICAgICAgICAgICA
gY29sb3I6IFwiI2NjY2NjY1wiLFxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEudGltZXN0YW1wc1tcIkNSRUFURURcIl0sXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBkYXRhLnRpbWVzdGFtcHNbXCJDUkVBVEVEXCJdICsgMSxcbiAgICAgICAgICAgICAgdHlwZTogJ3NjaGVkdWxlZCdcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBdXG4gICAgICAgIH0pO1xuICAgICAgICBhbmd1bGFyLmZvckVhY2goZGF0YS52ZXJ0aWNlcywgZnVuY3Rpb24odmVydGV4KSB7XG4gICAgICAgICAgaWYgKHZlcnRleFsnc3RhcnQtdGltZSddID4gLTEpIHtcbiAgICAgICAgICAgIHJldHVybiB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBsYWJlbDogdHJhbnNsYXRlTGFiZWwodmVydGV4Lm5hbWUpLFxuICAgICAgICAgICAgICAgICAgY29sb3I6IFwiI2Q5ZjFmN1wiLFxuICAgICAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzYyY2RlYVwiLFxuICAgICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdmVydGV4WydzdGFydC10aW1lJ10sXG4gICAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdmVydGV4WydlbmQtdGltZSddLFxuICAgICAgICAgICAgICAgICAgbGluazogdmVydGV4LmlkLFxuICAgICAgICAgICAgICAgICAgdHlwZTogJ3Jl
Z3VsYXInXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBdXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5jbGljayhmdW5jdGlvbihkLCBpLCBkYXR1bSkge1xuICAgICAgICAg
<TRUNCATED>
[51/51] [abbrv] flink git commit: [FLINK-2688] [monitoring api]
Integrate monitoring request handler with HA leader handling
Posted by se...@apache.org.
[FLINK-2688] [monitoring api] Integrate monitoring request handler with HA leader handling
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/506ce61f
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/506ce61f
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/506ce61f
Branch: refs/heads/master
Commit: 506ce61f66b862037bc57c7c25ae838bd762f2ac
Parents: 8db523c
Author: Stephan Ewen <se...@apache.org>
Authored: Wed Sep 16 22:04:19 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:53 2015 +0200
----------------------------------------------------------------------
.../optimizer/plantranslate/JsonMapper.java | 8 +-
.../webmonitor/ExecutionGraphHolder.java | 56 +++++++-----
.../runtime/webmonitor/WebRuntimeMonitor.java | 43 ++++++++--
.../handlers/ClusterOverviewHandler.java | 56 ++++++------
.../handlers/CurrentJobIdsHandler.java | 90 +++++++++++---------
.../handlers/CurrentJobsOverviewHandler.java | 82 ++++++++++--------
.../runtime/messages/GenericMessageTester.java | 2 +-
7 files changed, 207 insertions(+), 130 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/506ce61f/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JsonMapper.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JsonMapper.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JsonMapper.java
index 4d81058..d5ddf4d 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JsonMapper.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JsonMapper.java
@@ -107,8 +107,14 @@ public class JsonMapper {
case NESTEDLOOP_STREAMED_OUTER_SECOND:
return "Nested Loops (Streamed Outer: " + secondInputName + ")";
- case MERGE:
+ case INNER_MERGE:
return "Merge";
+ case FULL_OUTER_MERGE:
+ return "Full Outer Merge";
+ case LEFT_OUTER_MERGE:
+ return "Left Outer Merge";
+ case RIGHT_OUTER_MERGE:
+ return "Right Outer Merge";
case CO_GROUP:
return "Co-Group";
http://git-wip-us.apache.org/repos/asf/flink/blob/506ce61f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java
index 74278a1..09ede4c 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java
@@ -23,6 +23,8 @@ import org.apache.flink.runtime.executiongraph.ExecutionGraph;
import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.messages.JobManagerMessages;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.FiniteDuration;
@@ -30,7 +32,7 @@ import scala.concurrent.duration.FiniteDuration;
import java.util.WeakHashMap;
/**
- * Gateway to obtaining an {@link ExecutionGraph} from a source, like JobManager or Archiver.
+ * Gateway to obtaining an {@link ExecutionGraph} from a source, like JobManager or Archive.
* <p>
* The holder will cache the ExecutionGraph behind a weak reference, which will be cleared
* at some point once no one else is pointing to the ExecutionGraph.
@@ -39,26 +41,33 @@ import java.util.WeakHashMap;
*/
public class ExecutionGraphHolder {
- private final ActorGateway source;
+ private static final Logger LOG = LoggerFactory.getLogger(ExecutionGraphHolder.class);
+
+ /** Retrieves the current leading JobManager and its corresponding archive */
+ private final JobManagerArchiveRetriever retriever;
private final FiniteDuration timeout;
private final WeakHashMap<JobID, ExecutionGraph> cache = new WeakHashMap<JobID, ExecutionGraph>();
-
- public ExecutionGraphHolder(ActorGateway source) {
- this(source, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
+ public ExecutionGraphHolder(JobManagerArchiveRetriever retriever) {
+ this(retriever, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
}
- public ExecutionGraphHolder(ActorGateway source, FiniteDuration timeout) {
- if (source == null || timeout == null) {
+ public ExecutionGraphHolder(JobManagerArchiveRetriever retriever, FiniteDuration timeout) {
+ if (retriever == null || timeout == null) {
throw new NullPointerException();
}
- this.source = source;
+ this.retriever = retriever;
this.timeout = timeout;
}
-
+ /**
+ * Retrieves the execution graph with {@link JobID} jid or null if it cannot be found.
+ *
+ * @param jid jobID of the execution graph to be retrieved
+ * @return the retrieved execution graph or null if it is not retrievable
+ */
public ExecutionGraph getExecutionGraph(JobID jid) {
ExecutionGraph cached = cache.get(jid);
if (cached != null) {
@@ -66,18 +75,27 @@ public class ExecutionGraphHolder {
}
try {
- Future<Object> future = source.ask(new JobManagerMessages.RequestJob(jid), timeout);
- Object result = Await.result(future, timeout);
- if (result instanceof JobManagerMessages.JobNotFound) {
- return null;
- }
- else if (result instanceof JobManagerMessages.JobFound) {
- ExecutionGraph eg = ((JobManagerMessages.JobFound) result).executionGraph();
- cache.put(jid, eg);
- return eg;
+ ActorGateway jobManager = retriever.getJobManagerGateway();
+
+ if (jobManager != null) {
+ Future<Object> future = jobManager.ask(new JobManagerMessages.RequestJob(jid), timeout);
+ Object result = Await.result(future, timeout);
+
+ if (result instanceof JobManagerMessages.JobNotFound) {
+ return null;
+ }
+ else if (result instanceof JobManagerMessages.JobFound) {
+ ExecutionGraph eg = ((JobManagerMessages.JobFound) result).executionGraph();
+ cache.put(jid, eg);
+ return eg;
+ }
+ else {
+ throw new RuntimeException("Unknown response from JobManager / Archive: " + result);
+ }
}
else {
- throw new RuntimeException("Unknown response from JobManager / Archive: " + result);
+ LOG.warn("No connection to the leading JobManager.");
+ return null;
}
}
catch (Exception e) {
http://git-wip-us.apache.org/repos/asf/flink/blob/506ce61f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
index 4633dcf..a34823e 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
@@ -18,6 +18,8 @@
package org.apache.flink.runtime.webmonitor;
+import akka.actor.ActorSystem;
+
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
@@ -33,7 +35,8 @@ import io.netty.handler.stream.ChunkedWriteHandler;
import org.apache.flink.configuration.ConfigConstants;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.IllegalConfigurationException;
-import org.apache.flink.runtime.instance.ActorGateway;
+import org.apache.flink.runtime.akka.AkkaUtils;
+import org.apache.flink.runtime.leaderretrieval.LeaderRetrievalService;
import org.apache.flink.runtime.webmonitor.files.StaticFileServerHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobAccumulatorsHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobPlanHandler;
@@ -63,6 +66,8 @@ import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
* The root component of the web runtime monitor. This class starts the web server and creates
* all request handlers for the REST API.
@@ -84,7 +89,13 @@ public class WebRuntimeMonitor implements WebMonitor {
// ------------------------------------------------------------------------
+ /** Guarding concurrent modifications to the server channel pipeline during startup and shutdown */
private final Object startupShutdownLock = new Object();
+
+ private final LeaderRetrievalService leaderRetrievalService;
+
+ /** LeaderRetrievalListener which stores the currently leading JobManager and its archive */
+ private final JobManagerArchiveRetriever retriever;
private final Router router;
@@ -95,7 +106,12 @@ public class WebRuntimeMonitor implements WebMonitor {
private Channel serverChannel;
- public WebRuntimeMonitor(Configuration config, ActorGateway jobManager, ActorGateway archive) throws IOException {
+ public WebRuntimeMonitor(
+ Configuration config,
+ LeaderRetrievalService leaderRetrievalService,
+ ActorSystem actorSystem) throws IOException
+ {
+ this.leaderRetrievalService = checkNotNull(leaderRetrievalService);
final WebMonitorConfig cfg = new WebMonitorConfig(config);
@@ -127,22 +143,27 @@ public class WebRuntimeMonitor implements WebMonitor {
if (this.configuredPort < 0) {
throw new IllegalArgumentException("Web frontend port is invalid: " + this.configuredPort);
}
+
+ FiniteDuration timeout = AkkaUtils.getTimeout(config);
+ FiniteDuration lookupTimeout = AkkaUtils.getTimeout(config);
+
+ retriever = new JobManagerArchiveRetriever(this, actorSystem, lookupTimeout, timeout);
- ExecutionGraphHolder currentGraphs = new ExecutionGraphHolder(jobManager);
-
+ ExecutionGraphHolder currentGraphs = new ExecutionGraphHolder(retriever);
+
router = new Router()
// config how to interact with this web server
.GET("/config", handler(new DashboardConfigHandler(cfg.getRefreshInterval())))
// the overview - how many task managers, slots, free slots, ...
- .GET("/overview", handler(new ClusterOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
+ .GET("/overview", handler(new ClusterOverviewHandler(retriever, DEFAULT_REQUEST_TIMEOUT)))
// overview over jobs
- .GET("/joboverview", handler(new CurrentJobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, true)))
- .GET("/joboverview/running", handler(new CurrentJobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, false)))
- .GET("/joboverview/completed", handler(new CurrentJobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, false, true)))
+ .GET("/joboverview", handler(new CurrentJobsOverviewHandler(retriever, DEFAULT_REQUEST_TIMEOUT, true, true)))
+ .GET("/joboverview/running", handler(new CurrentJobsOverviewHandler(retriever, DEFAULT_REQUEST_TIMEOUT, true, false)))
+ .GET("/joboverview/completed", handler(new CurrentJobsOverviewHandler(retriever, DEFAULT_REQUEST_TIMEOUT, false, true)))
- .GET("/jobs", handler(new CurrentJobIdsHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
+ .GET("/jobs", handler(new CurrentJobIdsHandler(retriever, DEFAULT_REQUEST_TIMEOUT)))
.GET("/jobs/:jobid", handler(new JobDetailsHandler(currentGraphs)))
.GET("/jobs/:jobid/vertices", handler(new JobDetailsHandler(currentGraphs)))
@@ -203,12 +224,16 @@ public class WebRuntimeMonitor implements WebMonitor {
int port = bindAddress.getPort();
LOG.info("Web frontend listening at " + address + ':' + port);
+
+ leaderRetrievalService.start(retriever);
}
}
@Override
public void stop() throws Exception {
synchronized (startupShutdownLock) {
+ leaderRetrievalService.stop();
+
if (this.serverChannel != null) {
this.serverChannel.close().awaitUninterruptibly();
this.serverChannel = null;
http://git-wip-us.apache.org/repos/asf/flink/blob/506ce61f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ClusterOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ClusterOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ClusterOverviewHandler.java
index 2d8b217..dde368b 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ClusterOverviewHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ClusterOverviewHandler.java
@@ -23,6 +23,7 @@ import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.messages.webmonitor.RequestStatusOverview;
import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
+import org.apache.flink.runtime.webmonitor.JobManagerArchiveRetriever;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.FiniteDuration;
@@ -35,44 +36,51 @@ import java.util.Map;
* TaskManagers are currently connected, and how many jobs are running.
*/
public class ClusterOverviewHandler implements RequestHandler, RequestHandler.JsonResponse {
-
- private final ActorGateway jobManager;
-
+
+ private final JobManagerArchiveRetriever retriever;
+
private final FiniteDuration timeout;
-
- public ClusterOverviewHandler(ActorGateway jobManager, FiniteDuration timeout) {
- if (jobManager == null || timeout == null) {
+
+ public ClusterOverviewHandler(JobManagerArchiveRetriever retriever, FiniteDuration timeout) {
+ if (retriever == null || timeout == null) {
throw new NullPointerException();
}
- this.jobManager = jobManager;
+ this.retriever = retriever;
this.timeout = timeout;
}
-
+
@Override
public String handleRequest(Map<String, String> params) throws Exception {
+ // we need no parameters, get all requests
try {
- Future<Object> future = jobManager.ask(RequestStatusOverview.getInstance(), timeout);
- StatusOverview overview = (StatusOverview) Await.result(future, timeout);
+ ActorGateway jobManager = retriever.getJobManagerGateway();
+
+ if (jobManager != null) {
+ Future<Object> future = jobManager.ask(RequestStatusOverview.getInstance(), timeout);
+ StatusOverview overview = (StatusOverview) Await.result(future, timeout);
- StringWriter writer = new StringWriter();
- JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
- gen.writeStartObject();
- gen.writeNumberField("taskmanagers", overview.getNumTaskManagersConnected());
- gen.writeNumberField("slots-total", overview.getNumSlotsTotal());
- gen.writeNumberField("slots-available", overview.getNumSlotsAvailable());
- gen.writeNumberField("jobs-running", overview.getNumJobsRunningOrPending());
- gen.writeNumberField("jobs-finished", overview.getNumJobsFinished());
- gen.writeNumberField("jobs-cancelled", overview.getNumJobsCancelled());
- gen.writeNumberField("jobs-failed", overview.getNumJobsFailed());
- gen.writeEndObject();
+ gen.writeStartObject();
+ gen.writeNumberField("taskmanagers", overview.getNumTaskManagersConnected());
+ gen.writeNumberField("slots-total", overview.getNumSlotsTotal());
+ gen.writeNumberField("slots-available", overview.getNumSlotsAvailable());
+ gen.writeNumberField("jobs-running", overview.getNumJobsRunningOrPending());
+ gen.writeNumberField("jobs-finished", overview.getNumJobsFinished());
+ gen.writeNumberField("jobs-cancelled", overview.getNumJobsCancelled());
+ gen.writeNumberField("jobs-failed", overview.getNumJobsFailed());
+ gen.writeEndObject();
- gen.close();
- return writer.toString();
+ gen.close();
+ return writer.toString();
+ } else {
+ throw new Exception("No connection to the leading JobManager.");
+ }
}
catch (Exception e) {
- throw new Exception("Failed to fetch the status overview: " + e.getMessage(), e);
+ throw new RuntimeException("Failed to fetch list of all running jobs: " + e.getMessage(), e);
}
}
}
http://git-wip-us.apache.org/repos/asf/flink/blob/506ce61f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobIdsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobIdsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobIdsHandler.java
index e7e96e2..049bd54 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobIdsHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobIdsHandler.java
@@ -24,6 +24,7 @@ import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
import org.apache.flink.runtime.messages.webmonitor.RequestJobsWithIDsOverview;
+import org.apache.flink.runtime.webmonitor.JobManagerArchiveRetriever;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.FiniteDuration;
@@ -37,17 +38,17 @@ import java.util.Map;
* given the JobManager or Archive Actor Reference.
*/
public class CurrentJobIdsHandler implements RequestHandler, RequestHandler.JsonResponse {
-
- private final ActorGateway target;
-
+
+ private final JobManagerArchiveRetriever retriever;
+
private final FiniteDuration timeout;
-
-
- public CurrentJobIdsHandler(ActorGateway target, FiniteDuration timeout) {
- if (target == null || timeout == null) {
+
+
+ public CurrentJobIdsHandler(JobManagerArchiveRetriever retriever, FiniteDuration timeout) {
+ if (retriever == null || timeout == null) {
throw new NullPointerException();
}
- this.target = target;
+ this.retriever = retriever;
this.timeout = timeout;
}
@@ -55,42 +56,49 @@ public class CurrentJobIdsHandler implements RequestHandler, RequestHandler.Json
public String handleRequest(Map<String, String> params) throws Exception {
// we need no parameters, get all requests
try {
- Future<Object> future = target.ask(RequestJobsWithIDsOverview.getInstance(), timeout);
- JobsWithIDsOverview overview = (JobsWithIDsOverview) Await.result(future, timeout);
-
- StringWriter writer = new StringWriter();
- JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
-
- gen.writeStartObject();
+ ActorGateway jobManager = retriever.getJobManagerGateway();
- gen.writeArrayFieldStart("jobs-running");
- for (JobID jid : overview.getJobsRunningOrPending()) {
- gen.writeString(jid.toString());
- }
- gen.writeEndArray();
-
- gen.writeArrayFieldStart("jobs-finished");
- for (JobID jid : overview.getJobsFinished()) {
- gen.writeString(jid.toString());
- }
- gen.writeEndArray();
-
- gen.writeArrayFieldStart("jobs-cancelled");
- for (JobID jid : overview.getJobsCancelled()) {
- gen.writeString(jid.toString());
+ if (jobManager != null) {
+ Future<Object> future = jobManager.ask(RequestJobsWithIDsOverview.getInstance(), timeout);
+ JobsWithIDsOverview overview = (JobsWithIDsOverview) Await.result(future, timeout);
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+
+ gen.writeArrayFieldStart("jobs-running");
+ for (JobID jid : overview.getJobsRunningOrPending()) {
+ gen.writeString(jid.toString());
+ }
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("jobs-finished");
+ for (JobID jid : overview.getJobsFinished()) {
+ gen.writeString(jid.toString());
+ }
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("jobs-cancelled");
+ for (JobID jid : overview.getJobsCancelled()) {
+ gen.writeString(jid.toString());
+ }
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("jobs-failed");
+ for (JobID jid : overview.getJobsFailed()) {
+ gen.writeString(jid.toString());
+ }
+ gen.writeEndArray();
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
}
- gen.writeEndArray();
-
- gen.writeArrayFieldStart("jobs-failed");
- for (JobID jid : overview.getJobsFailed()) {
- gen.writeString(jid.toString());
+ else {
+ throw new Exception("No connection to the leading JobManager.");
}
- gen.writeEndArray();
-
- gen.writeEndObject();
-
- gen.close();
- return writer.toString();
}
catch (Exception e) {
throw new RuntimeException("Failed to fetch list of all running jobs: " + e.getMessage(), e);
http://git-wip-us.apache.org/repos/asf/flink/blob/506ce61f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
index 6444e4b..d9bfcb7 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
@@ -25,6 +25,7 @@ import org.apache.flink.runtime.messages.webmonitor.JobDetails;
import org.apache.flink.runtime.messages.webmonitor.MultipleJobsDetails;
import org.apache.flink.runtime.messages.webmonitor.RequestJobDetails;
+import org.apache.flink.runtime.webmonitor.JobManagerArchiveRetriever;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.FiniteDuration;
@@ -36,8 +37,8 @@ import java.util.Map;
* Request handler that returns a summary of the job status.
*/
public class CurrentJobsOverviewHandler implements RequestHandler, RequestHandler.JsonResponse {
-
- private final ActorGateway jobManager;
+
+ private final JobManagerArchiveRetriever retriever;
private final FiniteDuration timeout;
@@ -45,9 +46,12 @@ public class CurrentJobsOverviewHandler implements RequestHandler, RequestHandle
private final boolean includeFinishedJobs;
- public CurrentJobsOverviewHandler(ActorGateway jobManager, FiniteDuration timeout,
+ public CurrentJobsOverviewHandler(JobManagerArchiveRetriever retriever, FiniteDuration timeout,
boolean includeRunningJobs, boolean includeFinishedJobs) {
- this.jobManager = jobManager;
+ if (retriever == null || timeout == null) {
+ throw new NullPointerException();
+ }
+ this.retriever = retriever;
this.timeout = timeout;
this.includeRunningJobs = includeRunningJobs;
this.includeFinishedJobs = includeFinishedJobs;
@@ -56,42 +60,50 @@ public class CurrentJobsOverviewHandler implements RequestHandler, RequestHandle
@Override
public String handleRequest(Map<String, String> params) throws Exception {
try {
- Future<Object> future = jobManager.ask(
- new RequestJobDetails(includeRunningJobs, includeFinishedJobs), timeout);
-
- MultipleJobsDetails result = (MultipleJobsDetails) Await.result(future, timeout);
-
- final long now = System.currentTimeMillis();
-
- StringWriter writer = new StringWriter();
- JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
- gen.writeStartObject();
-
-
- if (includeRunningJobs && includeFinishedJobs) {
- gen.writeArrayFieldStart("running");
- for (JobDetails detail : result.getRunningJobs()) {
- generateSingleJobDetails(detail, gen, now);
+ ActorGateway jobManager = retriever.getJobManagerGateway();
+
+ if (jobManager != null) {
+
+ Future<Object> future = jobManager.ask(
+ new RequestJobDetails(includeRunningJobs, includeFinishedJobs), timeout);
+
+ MultipleJobsDetails result = (MultipleJobsDetails) Await.result(future, timeout);
+
+ final long now = System.currentTimeMillis();
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+ gen.writeStartObject();
+
+
+ if (includeRunningJobs && includeFinishedJobs) {
+ gen.writeArrayFieldStart("running");
+ for (JobDetails detail : result.getRunningJobs()) {
+ generateSingleJobDetails(detail, gen, now);
+ }
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("finished");
+ for (JobDetails detail : result.getFinishedJobs()) {
+ generateSingleJobDetails(detail, gen, now);
+ }
+ gen.writeEndArray();
}
- gen.writeEndArray();
-
- gen.writeArrayFieldStart("finished");
- for (JobDetails detail : result.getFinishedJobs()) {
- generateSingleJobDetails(detail, gen, now);
+ else {
+ gen.writeArrayFieldStart("jobs");
+ for (JobDetails detail : includeRunningJobs ? result.getRunningJobs() : result.getFinishedJobs()) {
+ generateSingleJobDetails(detail, gen, now);
+ }
+ gen.writeEndArray();
}
- gen.writeEndArray();
+
+ gen.writeEndObject();
+ gen.close();
+ return writer.toString();
}
else {
- gen.writeArrayFieldStart("jobs");
- for (JobDetails detail : includeRunningJobs ? result.getRunningJobs() : result.getFinishedJobs()) {
- generateSingleJobDetails(detail, gen, now);
- }
- gen.writeEndArray();
+ throw new Exception("No connection to the leading JobManager.");
}
-
- gen.writeEndObject();
- gen.close();
- return writer.toString();
}
catch (Exception e) {
throw new Exception("Failed to fetch the status overview: " + e.getMessage(), e);
http://git-wip-us.apache.org/repos/asf/flink/blob/506ce61f/flink-runtime/src/test/java/org/apache/flink/runtime/messages/GenericMessageTester.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/messages/GenericMessageTester.java b/flink-runtime/src/test/java/org/apache/flink/runtime/messages/GenericMessageTester.java
index dcf4839..76c1bd0 100644
--- a/flink-runtime/src/test/java/org/apache/flink/runtime/messages/GenericMessageTester.java
+++ b/flink-runtime/src/test/java/org/apache/flink/runtime/messages/GenericMessageTester.java
@@ -19,8 +19,8 @@
package org.apache.flink.runtime.messages;
import org.apache.flink.api.common.JobID;
+import org.apache.flink.core.testutils.CommonTestUtils;
import org.apache.flink.runtime.jobgraph.JobStatus;
-import org.apache.flink.runtime.testutils.CommonTestUtils;
import java.io.Serializable;
import java.lang.reflect.Constructor;
[17/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Added
tooltips to timeline
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Added tooltips to timeline
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/4825c8c5
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/4825c8c5
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/4825c8c5
Branch: refs/heads/master
Commit: 4825c8c5802cc5b7f08aa1e703c6cf349b2a8d68
Parents: f9d177b
Author: Piotr Godek <pi...@gmail.com>
Authored: Wed Aug 19 23:19:34 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:50 2015 +0200
----------------------------------------------------------------------
.../app/scripts/modules/jobs/jobs.dir.coffee | 10 -
.../web-dashboard/app/styles/timeline.styl | 3 +
flink-runtime-web/web-dashboard/bower.json | 3 +-
flink-runtime-web/web-dashboard/gulpfile.js | 4 +-
.../web-dashboard/vendor-local/d3-timeline.js | 30 +-
.../web-dashboard/web/css/index.css | 4 +
.../web-dashboard/web/css/vendor.css | 620 +-
flink-runtime-web/web-dashboard/web/js/index.js | 16 +-
.../web-dashboard/web/js/vendor.js | 6618 +++++++++++++++++-
9 files changed, 7146 insertions(+), 162 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/4825c8c5/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
index 2045a12..da83e83 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
@@ -83,11 +83,6 @@ angular.module('flinkApp')
.datum(testData)
.call(chart)
- svgG = svg.select("g")
-
- bbox = svgG[0][0].getBBox()
- svg.attr('height', bbox.height + 30)
-
analyzeTime(scope.data)
return
@@ -159,11 +154,6 @@ angular.module('flinkApp')
.datum(testData)
.call(chart)
- svgG = svg.select("g")
-
- bbox = svgG[0][0].getBBox()
- svg.attr('height', bbox.height + 30)
-
scope.$watch attrs.job, (data) ->
analyzeTime(data) if data
http://git-wip-us.apache.org/repos/asf/flink/blob/4825c8c5/flink-runtime-web/web-dashboard/app/styles/timeline.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/timeline.styl b/flink-runtime-web/web-dashboard/app/styles/timeline.styl
index 86cefed..8952707 100644
--- a/flink-runtime-web/web-dashboard/app/styles/timeline.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/timeline.styl
@@ -33,3 +33,6 @@
.timeline-insidelabel, .timeline-series
cursor: auto
+.qtip-timeline-bar
+ font-size: 14px
+ line-height: 1.4
http://git-wip-us.apache.org/repos/asf/flink/blob/4825c8c5/flink-runtime-web/web-dashboard/bower.json
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/bower.json b/flink-runtime-web/web-dashboard/bower.json
index d08d1e2..38a9101 100644
--- a/flink-runtime-web/web-dashboard/bower.json
+++ b/flink-runtime-web/web-dashboard/bower.json
@@ -21,7 +21,8 @@
"dagre-d3": "~0.4.8",
"font-awesome": "~4.3.0",
"angular-moment": "~0.9.2",
- "moment-duration-format": "~1.3.0"
+ "moment-duration-format": "~1.3.0",
+ "qtip2": "~2.2.1"
},
"overrides": {
"dagre-d3": {
http://git-wip-us.apache.org/repos/asf/flink/blob/4825c8c5/flink-runtime-web/web-dashboard/gulpfile.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/gulpfile.js b/flink-runtime-web/web-dashboard/gulpfile.js
index 2564c88..737519e 100644
--- a/flink-runtime-web/web-dashboard/gulpfile.js
+++ b/flink-runtime-web/web-dashboard/gulpfile.js
@@ -44,7 +44,7 @@ var environment = 'development';
var paths = {
src: './app/',
dest: './web/',
- vendor: './vendor/',
+ vendor: './bower_components/',
vendorLocal: './vendor-local/',
assets: './assets/',
tmp: './tmp/'
@@ -76,7 +76,7 @@ gulp.task('bootstrap', function () {
});
gulp.task('vendor-styles', [ 'bootstrap' ], function() {
- stream = gulp.src(mainBowerFiles().concat([paths.tmp + 'css/bootstrap_custom.css']))
+ stream = gulp.src(mainBowerFiles().concat([paths.tmp + 'css/bootstrap_custom.css']).concat([paths.vendor + 'qtip2/jquery.qtip.css']))
.pipe(filter(['*.css', '!bootstrap.css']))
.pipe(sourcemaps.init())
.pipe(concat("vendor.css"))
http://git-wip-us.apache.org/repos/asf/flink/blob/4825c8c5/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js b/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js
index c18f404..f12d478 100644
--- a/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js
+++ b/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js
@@ -159,8 +159,6 @@
}
if (timeAxisTick) {
- console.log('here');
-
g.append("g")
.attr("class", "axis axis-tick")
.attr("transform", "translate(" + 0 +","+
@@ -215,7 +213,7 @@
.attr("width", getTextWidth)
.attr("height", itemHeight);
- nel
+ var bar = nel
.append(function(d, i) {
return document.createElementNS(d3.ns.prefix.svg, "display" in d? d.display:display);
})
@@ -273,11 +271,6 @@
.attr("width", getTextWidth)
.attr("height", itemHeight)
.attr("clip-path", "url(#" + prefix + "-timeline-textclip-" + i + "-" + index + ")")
-
- // .attr("width", function (d, i) {
- // // console.log((d.ending_time - d.starting_time) * scaleFactor);
- // return (d.ending_time - d.starting_time) * scaleFactor;
- // })
.text(function(d) {
return d.label;
})
@@ -286,6 +279,22 @@
});
;
+ g.selectAll("svg .bar-container").each(function(d, i) {
+ $(this).qtip({
+ content: {
+ text: d.label
+ },
+ position: {
+ my: 'bottom left',
+ at: 'top left'
+ },
+ style: {
+ classes: 'qtip-light qtip-timeline-bar'
+ }
+ });
+ });
+
+
if (rowSeperatorsColor) {
var lineYAxis = ( itemHeight + itemMargin / 2 + margin.top + (itemHeight + itemMargin) * yAxisMapping[index]);
gParent.append("svg:line")
@@ -357,6 +366,8 @@
});
var move = function() {
+ $('.qtip.qtip-timeline-bar').qtip('hide');
+
g.selectAll(".bar-type-scheduled .timeline-series")
.attr("x", getXPos);
@@ -414,6 +425,9 @@
var gSize = g[0][0].getBoundingClientRect();
setHeight();
+ bbox = g[0][0].getBBox();
+ gParent.attr('height', bbox.height + 30);
+
function getBorderStart(d, i) {
return xScale(d.starting_time);
}
http://git-wip-us.apache.org/repos/asf/flink/blob/4825c8c5/flink-runtime-web/web-dashboard/web/css/index.css
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/css/index.css b/flink-runtime-web/web-dashboard/web/css/index.css
index 7ab866a..854a619 100644
--- a/flink-runtime-web/web-dashboard/web/css/index.css
+++ b/flink-runtime-web/web-dashboard/web/css/index.css
@@ -408,6 +408,10 @@ svg.graph .node-label {
.timeline-canvas.secondary .timeline-series {
cursor: auto;
}
+.qtip-timeline-bar {
+ font-size: 14px;
+ line-height: 1.4;
+}
@media (min-width: 768px) {
#sidebar {
left: 0;
[30/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Adjust
view for details of a job
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index d8ea8ad..f7394f8 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -53,43 +53,27 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
}
}).state("single-job.plan", {
url: "",
+ abstract: true,
views: {
details: {
templateUrl: "partials/jobs/job.plan.html",
controller: 'JobPlanController'
}
}
- }).state("single-job.plan.node", {
- url: "/vertex/{nodeid}",
- abstract: true,
- views: {
- 'node-tabs': {
- templateUrl: "partials/jobs/job.plan.node-tabs.html",
- controller: 'JobPlanNodeTabsController'
- }
- }
- }).state("single-job.plan.node.generic", {
+ }).state("single-job.plan.overview", {
url: "",
views: {
'node-details': {
- controller: 'JobPlanNodeListController',
- templateUrl: "partials/jobs/job.plan.node-list.html"
+ templateUrl: "partials/jobs/job.plan.node-list.overview.html",
+ controller: 'JobPlanOverviewController'
}
}
- }).state("single-job.plan.node.accumulators", {
+ }).state("single-job.plan.accumulators", {
url: "/accumulators",
views: {
'node-details': {
- controller: 'JobPlanNodeListAccumulatorsController',
- templateUrl: "partials/jobs/job.plan.node-list.html"
- }
- }
- }).state("single-job.plan.node.properties", {
- url: "/properties",
- views: {
- 'node-details': {
- controller: 'JobPlanNodePropertiesController',
- templateUrl: "partials/jobs/job.plan.node.properties.html"
+ templateUrl: "partials/jobs/job.plan.node-list.accumulators.html",
+ controller: 'JobPlanAccumulatorsController'
}
}
}).state("single-job.timeline", {
@@ -122,6 +106,14 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
controller: 'JobExceptionsController'
}
}
+ }).state("single-job.properties", {
+ url: "/properties",
+ views: {
+ details: {
+ templateUrl: "partials/jobs/job.properties.html",
+ controller: 'JobPropertiesController'
+ }
+ }
}).state("single-job.config", {
url: "/config",
views: {
@@ -184,10 +176,10 @@ angular.module('flinkApp').filter("amDurationFormatExtended", ["angularMomentCon
};
amDurationFormatExtendedFilter.$stateful = angularMomentConfig.statefulFilters;
return amDurationFormatExtendedFilter;
-}]).filter("humanizeTaskName", function() {
+}]).filter("humanizeText", function() {
return function(text) {
if (text) {
- return text.replace(">", ">").replace("<br/>", "");
+ return text.replace(/>/g, ">").replace(/<br\/>/g, "");
} else {
return '';
}
@@ -195,8 +187,6 @@ angular.module('flinkApp').filter("amDurationFormatExtended", ["angularMomentCon
});
angular.module('flinkApp').service('MainService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) {
- var jobObservers;
- jobObservers = [];
this.loadConfig = function() {
var deferred;
deferred = $q.defer();
@@ -227,6 +217,7 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
});
return $scope.jobObserver();
}]).controller('SingleJobController', ["$scope", "$state", "$stateParams", "JobsService", "$rootScope", function($scope, $state, $stateParams, JobsService, $rootScope) {
+ console.log('SingleJobController');
$scope.jobid = $stateParams.jobid;
$rootScope.job = null;
$rootScope.plan = null;
@@ -240,44 +231,40 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
});
}]).controller('JobPlanController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
console.log('JobPlanController');
- return $scope.stateList = JobsService.stateList();
-}]).controller('JobPlanNodeController', ["$scope", "$state", "$stateParams", "JobsService", "$anchorScroll", function($scope, $state, $stateParams, JobsService, $anchorScroll) {
- console.log('JobPlanNodeController');
- $scope.nodeid = $stateParams.nodeid;
- $scope.stateList = JobsService.stateList();
- return JobsService.getNode($scope.nodeid).then(function(data) {
- $scope.node = data;
- return $scope.job.currentNode = data;
- });
-}]).controller('JobPlanNodeTabsController', ["$scope", "$stateParams", "JobsService", function($scope, $stateParams, JobsService) {
- console.log('JobPlanNodeTabsController');
- $scope.nodeid = $stateParams.nodeid;
+ $scope.nodeid = null;
$scope.stateList = JobsService.stateList();
- return JobsService.getNode($scope.nodeid).then(function(data) {
- $scope.node = data;
- return $scope.job.currentNode = data;
- });
-}]).controller('JobPlanNodeListController', ["$scope", "$stateParams", "JobsService", "$state", function($scope, $stateParams, JobsService, $state) {
- console.log('JobPlanNodeListController');
- $scope.gotoAccumulators = function(nodeid) {
- return $state.go('^.accumulators', {
- nodeid: nodeid
- });
+ return $scope.changeNode = function(nodeid) {
+ if (nodeid !== $scope.nodeid) {
+ $scope.nodeid = nodeid;
+ $scope.vertex = null;
+ if ($state.is('single-job.plan.overview')) {
+ return JobsService.getSubtasks(nodeid).then(function(data) {
+ return $scope.vertex = data;
+ });
+ } else if ($state.is('single-job.plan.accumulators')) {
+ return JobsService.getAccumulators(nodeid).then(function(data) {
+ return $scope.vertex = data;
+ });
+ }
+ } else {
+ $scope.nodeid = null;
+ return $scope.vertex = null;
+ }
};
- return JobsService.getSubtasks($stateParams.nodeid).then(function(data) {
- return $scope.vertex = data;
- });
-}]).controller('JobPlanNodeListAccumulatorsController', ["$scope", "$stateParams", "JobsService", "$state", function($scope, $stateParams, JobsService, $state) {
- console.log('JobPlanNodeListAccumulatorsController');
- return $scope.gotoGeneric = function(nodeid) {
- return $state.go('^.generic', {
- nodeid: nodeid
+}]).controller('JobPlanOverviewController', ["$scope", "JobsService", function($scope, JobsService) {
+ console.log('JobPlanOverviewController');
+ if ($scope.nodeid && !$scope.vertex.st) {
+ return JobsService.getSubtasks($scope.nodeid).then(function(data) {
+ return $scope.vertex = data;
});
- };
-}]).controller('JobPlanNodePropertiesController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
- return console.log('JobPlanNodePropertiesController');
-}]).controller('JobPlanNodeAccumulatorsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
- return console.log('JobPlanNodeAccumulatorsController');
+ }
+}]).controller('JobPlanAccumulatorsController', ["$scope", "JobsService", function($scope, JobsService) {
+ console.log('JobPlanAccumulatorsController');
+ if ($scope.nodeid && !$scope.vertex.accumulators) {
+ return JobsService.getAccumulators($scope.nodeid).then(function(data) {
+ return $scope.vertex = data;
+ });
+ }
}]).controller('JobTimelineVertexController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
return JobsService.getVertex($stateParams.vertexId).then(function(data) {
return $scope.vertex = data;
@@ -286,6 +273,19 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
return JobsService.loadExceptions().then(function(data) {
return $scope.exceptions = data;
});
+}]).controller('JobPropertiesController', ["$scope", "JobsService", function($scope, JobsService) {
+ console.log('JobPropertiesController');
+ return $scope.changeNode = function(nodeid) {
+ if (nodeid !== $scope.nodeid) {
+ $scope.nodeid = nodeid;
+ return JobsService.getNode(nodeid).then(function(data) {
+ return $scope.node = data;
+ });
+ } else {
+ $scope.nodeid = null;
+ return $scope.node = null;
+ }
+ };
}]);
angular.module('flinkApp').directive('vertex', ["$state", function($state) {
@@ -422,7 +422,8 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
return {
template: "<svg class='graph' width='500' height='400'><g /></svg> <svg class='tmp' width='1' height='1'><g /></svg> <div class='btn-group zoom-buttons'> <a class='btn btn-default zoom-in' ng-click='zoomIn()'><i class='fa fa-plus' /></a> <a class='btn btn-default zoom-out' ng-click='zoomOut()'><i class='fa fa-minus' /></a> </div>",
scope: {
- plan: '='
+ plan: '=',
+ setNode: '&'
},
link: function(scope, elem, attrs) {
var containerW, createEdge, createLabelEdge, createLabelNode, createNode, d3mainSvg, d3mainSvgG, d3tmpSvg, drawGraph, extendLabelNodeForIteration, getNodeType, isSpecialIterationNode, jobid, loadJsonToDagre, mainG, mainSvgElement, mainTmpElement, mainZoom, searchForNode, shortenString, subgraphs;
@@ -491,7 +492,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
};
createLabelNode = function(el, info, maxW, maxH) {
var labelValue, stepName;
- labelValue = "<a href='#/jobs/" + jobid + "/vertex/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>";
+ labelValue = "<div href='#/jobs/" + jobid + "/vertex/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>";
if (info === "mirror") {
labelValue += "<h3 class='node-name'>Mirror of " + el.operator + "</h3>";
} else {
@@ -517,7 +518,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
labelValue += "<h5>Operation: " + shortenString(el.operator_strategy) + "</h5>";
}
}
- labelValue += "</a>";
+ labelValue += "</div>";
return labelValue;
};
extendLabelNodeForIteration = function(id, maxW, maxH) {
@@ -703,7 +704,12 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
ev = d3.event;
return d3mainSvgG.attr("transform", "translate(" + ev.translate + ") scale(" + ev.scale + ")");
});
- return mainZoom(d3mainSvg);
+ mainZoom(d3mainSvg);
+ return d3mainSvgG.selectAll('.node').on('click', function(d) {
+ return scope.setNode({
+ nodeid: d
+ });
+ });
};
scope.$watch(attrs.plan, function(newPlan) {
if (newPlan) {
@@ -894,6 +900,21 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
})(this));
return deferred.promise;
};
+ this.getAccumulators = function(vertexid) {
+ var deferred;
+ deferred = $q.defer();
+ $q.all([deferreds.job.promise]).then((function(_this) {
+ return function(data) {
+ var vertex;
+ vertex = _this.seekVertex(vertexid);
+ return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/accumulators").success(function(data) {
+ vertex.accumulators = data['user-accumulators'];
+ return deferred.resolve(vertex);
+ });
+ };
+ })(this));
+ return deferred.promise;
+ };
this.loadExceptions = function() {
var deferred;
deferred = $q.defer();
@@ -949,4 +970,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsImNvbW1vbi9zZXJ2aWNlcy5jb2ZmZWUiLCJjb21tb24vc2VydmljZXMuanMiLCJtb2R1bGVzL2pvYnMvam9icy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuanMiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuZGlyLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLnN2Yy5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LmN0cmwuanMiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkEsUUFBUSxPQUFPLFlBQVksQ0FBQyxhQUFhLGtCQUl4QyxtQkFBSSxTQUFDLFlBQUQ7RUFDSCxXQUFXLGlCQUFpQjtFQ3JCNUIsT0RzQkEsV0FBVyxjQUFjLFdBQUE7SUFDdkIsV0FBVyxpQkFBaUIsQ0FBQyxXQUFXO0lDckJ4QyxPRHNCQSxXQUFXLGVBQWU7O0lBSTdCLE1BQU0sZUFBZTtFQUN
wQixXQUFXO0VBRVgsb0JBQW9CO0dBS3JCLCtEQUFJLFNBQUMsYUFBYSxhQUFhLGFBQWEsV0FBeEM7RUM1QkgsT0Q2QkEsWUFBWSxhQUFhLEtBQUssU0FBQyxRQUFEO0lBQzVCLFFBQVEsT0FBTyxhQUFhO0lBRTVCLFlBQVk7SUM3QlosT0QrQkEsVUFBVSxXQUFBO01DOUJSLE9EK0JBLFlBQVk7T0FDWixZQUFZOztJQUtqQixpQ0FBTyxTQUFDLHVCQUFEO0VDakNOLE9Ea0NBLHNCQUFzQjtJQUl2QixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sd0JBQ0w7SUFBQSxLQUFLO0lBQ0wsVUFBVTtJQUNWLE9BQ0U7TUFBQSxhQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLGdDQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxnQkFDRTtRQUFBLFlBQVk7UUFD
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
UUFBUSxTQUFTLFFBQXhCO01BQ1AsV0FBVztNQ3BCWCxPRHFCQSxTQUFTLFFBQVE7O0lDbkJuQixPRHFCQSxTQUFTOztFQ25CWCxPRHFCQTs7QUNuQkYiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTL
FxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJywgWyd1aS5yb3V0ZXInLCAnYW5ndWxhck1vbWVudCddKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKCRyb290U2NvcGUpIC0+XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZVxuICAkcm9vdFNjb3BlLnNob3dTaWRlYmFyID0gLT5cbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGVcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJDbGFzcyA9ICdmb3JjZS1zaG93J1xuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi52YWx1ZSAnZmxpbmtDb25maWcnLCB7XG4gIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MSdcbiMgIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9uZXctc2VydmVyJ1xuICBcInJlZnJlc2gtaW50ZXJ2YWxcIjogMTAwMDBcbn1cblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4ucnVuIChKb2JzU2VydmljZSwgTW
FpblNlcnZpY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIC0+XG4gIE1haW5TZXJ2aWNlLmxvYWRDb25maWcoKS50aGVuIChjb25maWcpIC0+XG4gICAgYW5ndWxhci5leHRlbmQgZmxpbmtDb25maWcsIGNvbmZpZ1xuXG4gICAgSm9ic1NlcnZpY2UubGlzdEpvYnMoKVxuXG4gICAgJGludGVydmFsIC0+XG4gICAgICBKb2JzU2VydmljZS5saXN0Sm9icygpXG4gICAgLCBmbGlua0NvbmZpZ1tcInJlZnJlc2gtaW50ZXJ2YWxcIl1cblxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb25maWcgKCR1aVZpZXdTY3JvbGxQcm92aWRlcikgLT5cbiAgJHVpVmlld1Njcm9sbFByb3ZpZGVyLnVzZUFuY2hvclNjcm9sbCgpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbmZpZyAoJHN0YXRlUHJvdmlkZXIsICR1cmxSb3V0ZXJQcm92aWRlcikgLT5cbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUgXCJvdmVydmlld1wiLFxuICAgIHVybDogXCIvb3ZlcnZpZXdcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvb3ZlcnZpZXcuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdPdmVydmlld0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwicnVubmluZy1qb2JzXCIsXG4gICAgdXJsOiBcIi9ydW5uaW5nLWpvYnNcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw
6IFwicGFydGlhbHMvam9icy9ydW5uaW5nLWpvYnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInXG4gIFxuICAuc3RhdGUgXCJjb21wbGV0ZWQtam9ic1wiLFxuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9jb21wbGV0ZWQtam9icy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2JcIixcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwiXG4gICAgYWJzdHJhY3Q6IHRydWVcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnU2luZ2xlSm9iQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW5cIixcbiAgICB1cmw6IFwiXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4ubm9kZVwiLFxuICAgIHVybDogXCIvdmVydGV4L3tub2RlaWR9XCJcbiAgICBhYnN0cmFjdDogdHJ1ZVxuICAgIHZpZXdzOlxuICAg
ICAgJ25vZGUtdGFicyc6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS10YWJzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbk5vZGVUYWJzQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4ubm9kZS5nZW5lcmljXCIsXG4gICAgdXJsOiBcIlwiXG4gICAgdmlld3M6XG4gICAgICAnbm9kZS1kZXRhaWxzJzpcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Ob2RlTGlzdENvbnRyb2xsZXInIFxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUtbGlzdC5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4ubm9kZS5hY2N1bXVsYXRvcnNcIixcbiAgICB1cmw6IFwiL2FjY3VtdWxhdG9yc1wiXG4gICAgdmlld3M6XG4gICAgICAnbm9kZS1kZXRhaWxzJzpcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Ob2RlTGlzdEFjY3VtdWxhdG9yc0NvbnRyb2xsZXInIFxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUtbGlzdC5odG1sXCJcbiAgICAgICAgIyBjb250cm9sbGVyOiAnSm9iUGxhbk5vZGVQcm9wZXJ0aWVzQ29udHJvbGxlcidcbiAgICAgICAgIyB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUucHJvcGVydGllcy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4ubm9kZS5wcm9wZ
XJ0aWVzXCIsXG4gICAgdXJsOiBcIi9wcm9wZXJ0aWVzXCJcbiAgICB2aWV3czpcbiAgICAgICdub2RlLWRldGFpbHMnOlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbk5vZGVQcm9wZXJ0aWVzQ29udHJvbGxlcidcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLnByb3BlcnRpZXMuaHRtbFwiXG5cblxuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IudGltZWxpbmVcIixcbiAgICB1cmw6IFwiL3RpbWVsaW5lXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLmh0bWxcIlxuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsXG4gICAgdXJsOiBcIi97dmVydGV4SWR9XCJcbiAgICB2aWV3czpcbiAgICAgIHZlcnRleDpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUudmVydGV4Lmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2Iuc3RhdGlzdGljc1wiLFxuICAgIHVybDogXCIvc3RhdGlzdGljc1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5zdGF0aXN0aWNzLmh0bWxcIlxuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IuZXhjZXB0aW9uc1
wiLFxuICAgIHVybDogXCIvZXhjZXB0aW9uc1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5leGNlcHRpb25zLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iRXhjZXB0aW9uc0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5jb25maWdcIixcbiAgICB1cmw6IFwiL2NvbmZpZ1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5jb25maWcuaHRtbFwiXG5cbiAgJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZSBcIi9vdmVydmlld1wiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pLnJ1bihmdW5jdGlvbigkcm9vdFNjb3BlKSB7XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZTtcbiAgcmV0dXJuICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSBmdW5jdGlvbigpIHtcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGU7XG4gICAgcmV0dXJuICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnO1xuICB9O1xufSkudmFsdWUoJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnLFxuICBcInJlZnJlc2gtaW50ZXJ2YWxcIjogMTAwMDBcbn0
pLnJ1bihmdW5jdGlvbihKb2JzU2VydmljZSwgTWFpblNlcnZpY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIHtcbiAgcmV0dXJuIE1haW5TZXJ2aWNlLmxvYWRDb25maWcoKS50aGVuKGZ1bmN0aW9uKGNvbmZpZykge1xuICAgIGFuZ3VsYXIuZXh0ZW5kKGZsaW5rQ29uZmlnLCBjb25maWcpO1xuICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCk7XG4gICAgcmV0dXJuICRpbnRlcnZhbChmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBKb2JzU2VydmljZS5saXN0Sm9icygpO1xuICAgIH0sIGZsaW5rQ29uZmlnW1wicmVmcmVzaC1pbnRlcnZhbFwiXSk7XG4gIH0pO1xufSkuY29uZmlnKGZ1bmN0aW9uKCR1aVZpZXdTY3JvbGxQcm92aWRlcikge1xuICByZXR1cm4gJHVpVmlld1Njcm9sbFByb3ZpZGVyLnVzZUFuY2hvclNjcm9sbCgpO1xufSkuY29uZmlnKGZ1bmN0aW9uKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIHtcbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUoXCJvdmVydmlld1wiLCB7XG4gICAgdXJsOiBcIi9vdmVydmlld1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwicnVubmluZy1qb2JzXCIsIHtcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiLFxuICAgIHZpZXdz
OiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwiY29tcGxldGVkLWpvYnNcIiwge1xuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iXCIsIHtcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwiLFxuICAgIGFic3RyYWN0OiB0cnVlLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhblwiLCB7XG4gICAgdXJsOiBcIlwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb
2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhbi5ub2RlXCIsIHtcbiAgICB1cmw6IFwiL3ZlcnRleC97bm9kZWlkfVwiLFxuICAgIGFic3RyYWN0OiB0cnVlLFxuICAgIHZpZXdzOiB7XG4gICAgICAnbm9kZS10YWJzJzoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUtdGFicy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZVRhYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnBsYW4ubm9kZS5nZW5lcmljXCIsIHtcbiAgICB1cmw6IFwiXCIsXG4gICAgdmlld3M6IHtcbiAgICAgICdub2RlLWRldGFpbHMnOiB7XG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZUxpc3RDb250cm9sbGVyJyxcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLWxpc3QuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhbi5ub2RlLmFjY3VtdWxhdG9yc1wiLCB7XG4gICAgdXJsOiBcIi9hY2N1bXVsYXRvcnNcIixcbiAgICB2aWV3czoge1xuICAgICAgJ25vZGUtZGV0YWlscyc6IHtcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Ob2RlTGlzdEFjY3VtdWxhdG9yc0NvbnRyb2xsZXInLFxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZG
UtbGlzdC5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuLm5vZGUucHJvcGVydGllc1wiLCB7XG4gICAgdXJsOiBcIi9wcm9wZXJ0aWVzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgICdub2RlLWRldGFpbHMnOiB7XG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZVByb3BlcnRpZXNDb250cm9sbGVyJyxcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLnByb3BlcnRpZXMuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IudGltZWxpbmVcIiwge1xuICAgIHVybDogXCIvdGltZWxpbmVcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIiwge1xuICAgIHVybDogXCIve3ZlcnRleElkfVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICB2ZXJ0ZXg6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUudmVydGV4Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5zdGF0aXN0aWNzXCIsIHtcbiAgICB1cmw6IFw
iL3N0YXRpc3RpY3NcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5zdGF0aXN0aWNzLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIiwge1xuICAgIHVybDogXCIvZXhjZXB0aW9uc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmV4Y2VwdGlvbnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iRXhjZXB0aW9uc0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IuY29uZmlnXCIsIHtcbiAgICB1cmw6IFwiL2NvbmZpZ1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmNvbmZpZy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuICByZXR1cm4gJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZShcIi9vdmVydmlld1wiKTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRk
aXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICdic0xhYmVsJ
ywgKEpvYnNTZXJ2aWNlKSAtPlxuICB0cmFuc2NsdWRlOiB0cnVlXG4gIHJlcGxhY2U6IHRydWVcbiAgc2NvcGU6IFxuICAgIGdldExhYmVsQ2xhc3M6IFwiJlwiXG4gICAgc3RhdHVzOiBcIkBcIlxuXG4gIHRlbXBsYXRlOiBcIjxzcGFuIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJz48bmctdHJhbnNjbHVkZT48L25nLXRyYW5zY2x1ZGU+PC9zcGFuPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2xhYmVsIGxhYmVsLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2luZGljYXRvclByaW1hcnknLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHJlcGxhY2U6IHRydWVcbiAgc2NvcGU6IFxuICAgIGdldExhYmVsQ2xhc3M6IFwiJlwiXG4gICAgc3RhdHVzOiAnQCdcblxuICB0ZW1wbGF0ZTogXCI8aSB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIlxuICBcbiAgbGluazogKHNjb3BlLCBlbGVtZW50LCBhdHRycykgLT5cbiAgICBzY29wZS5nZXRMYWJlbENsYXNzID0gLT5cbiAgICAgICdmYSBmYS1jaXJjbGUgaW5kaWNhdG9yIGluZGljYXRvci0nICsgSm9ic1NlcnZpY2UudHJhbnNsYXRlTGFiZW
xTdGF0ZShhdHRycy5zdGF0dXMpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0YWJsZVByb3BlcnR5JywgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTpcbiAgICB2YWx1ZTogJz0nXG5cbiAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZGlyZWN0aXZlKCdic0xhYmVsJywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICB0cmFuc2NsdWRlOiB0cnVlLFxuICAgIHJlcGxhY2U6IHRydWUsXG4gICAgc2NvcGU6IHtcbiAgICAgIGdldExhYmVsQ2xhc3M6IFwiJlwiLFxuICAgICAgc3RhdHVzOiBcIkBcIlxuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknPjxuZy10cmFuc2NsdWRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCIsXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSB7XG4gICAgICByZXR1cm4gc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJ2xhYmVsIGxhYmVsLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cyk7XG4gICAgICB9O1xuICAgIH1cbiAgfTtcbn0pLmR
pcmVjdGl2ZSgnaW5kaWNhdG9yUHJpbWFyeScsIGZ1bmN0aW9uKEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6ICdAJ1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPGkgdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknIC8+XCIsXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSB7XG4gICAgICByZXR1cm4gc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJ2ZhIGZhLWNpcmNsZSBpbmRpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cyk7XG4gICAgICB9O1xuICAgIH1cbiAgfTtcbn0pLmRpcmVjdGl2ZSgndGFibGVQcm9wZXJ0eScsIGZ1bmN0aW9uKCkge1xuICByZXR1cm4ge1xuICAgIHJlcGxhY2U6IHRydWUsXG4gICAgc2NvcGU6IHtcbiAgICAgIHZhbHVlOiAnPSdcbiAgICB9LFxuICAgIHRlbXBsYXRlOiBcIjx0ZCB0aXRsZT1cXFwie3t2YWx1ZSB8fCAnTm9uZSd9fVxcXCI+e3t2YWx1ZSB8fCAnTm9uZSd9fTwvdGQ+XCJcbiAgfTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGlj
ZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpX
G5cbi5maWx0ZXIgXCJhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRcIiwgKGFuZ3VsYXJNb21lbnRDb25maWcpIC0+XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlciA9ICh2YWx1ZSwgZm9ybWF0LCBkdXJhdGlvbkZvcm1hdCkgLT5cbiAgICByZXR1cm4gXCJcIiAgaWYgdHlwZW9mIHZhbHVlIGlzIFwidW5kZWZpbmVkXCIgb3IgdmFsdWUgaXMgbnVsbFxuXG4gICAgbW9tZW50LmR1cmF0aW9uKHZhbHVlLCBmb3JtYXQpLmZvcm1hdChkdXJhdGlvbkZvcm1hdCwgeyB0cmltOiBmYWxzZSB9KVxuXG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlci4kc3RhdGVmdWwgPSBhbmd1bGFyTW9tZW50Q29uZmlnLnN0YXRlZnVsRmlsdGVyc1xuXG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlclxuXG4uZmlsdGVyIFwiaHVtYW5pemVUYXNrTmFtZVwiLCAtPlxuICAodGV4dCkgLT5cbiAgICAjIFRPRE86IGV4dGVuZC4uLiBhIGxvdFxuICAgIGlmIHRleHQgdGhlbiB0ZXh0LnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKS5yZXBsYWNlKFwiPGJyLz5cIixcIlwiKSBlbHNlICcnXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5maWx0ZXIoXCJhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRcIiwgZnVuY3Rpb24oYW5ndWxhck1vbWVudENvbmZpZykge1xuICB2YXIgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyO1xuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIgPSBmdW5jdGlvbih2YW
x1ZSwgZm9ybWF0LCBkdXJhdGlvbkZvcm1hdCkge1xuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwidW5kZWZpbmVkXCIgfHwgdmFsdWUgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiBcIlwiO1xuICAgIH1cbiAgICByZXR1cm4gbW9tZW50LmR1cmF0aW9uKHZhbHVlLCBmb3JtYXQpLmZvcm1hdChkdXJhdGlvbkZvcm1hdCwge1xuICAgICAgdHJpbTogZmFsc2VcbiAgICB9KTtcbiAgfTtcbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzO1xuICByZXR1cm4gYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyO1xufSkuZmlsdGVyKFwiaHVtYW5pemVUYXNrTmFtZVwiLCBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uKHRleHQpIHtcbiAgICBpZiAodGV4dCkge1xuICAgICAgcmV0dXJuIHRleHQucmVwbGFjZShcIiZndDtcIiwgXCI+XCIpLnJlcGxhY2UoXCI8YnIvPlwiLCBcIlwiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuICcnO1xuICAgIH1cbiAgfTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyB
yZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5zZXJ2aWNlICdNYWluU2VydmljZScsICgkaHR0cCwgZmxpbmtDb25maWcsICRxKSAtPlxuICBqb2JPYnNlcnZlcnMgPSBbXVxuXG4gIEBsb2FkQ29uZmlnID0gLT5c
biAgICBkZWZlcnJlZCA9ICRxLmRlZmVyKClcblxuICAgICRodHRwLmdldCBmbGlua0NvbmZpZy5qb2JTZXJ2ZXIgKyBcIi9jb25maWdcIlxuICAgIC5zdWNjZXNzIChkYXRhLCBzdGF0dXMsIGhlYWRlcnMsIGNvbmZpZykgLT5cbiAgICAgIGRlZmVycmVkLnJlc29sdmUoZGF0YSlcblxuICAgIGRlZmVycmVkLnByb21pc2VcblxuXG4gIEBcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLnNlcnZpY2UoJ01haW5TZXJ2aWNlJywgZnVuY3Rpb24oJGh0dHAsIGZsaW5rQ29uZmlnLCAkcSkge1xuICB2YXIgam9iT2JzZXJ2ZXJzO1xuICBqb2JPYnNlcnZlcnMgPSBbXTtcbiAgdGhpcy5sb2FkQ29uZmlnID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGRlZmVycmVkO1xuICAgIGRlZmVycmVkID0gJHEuZGVmZXIoKTtcbiAgICAkaHR0cC5nZXQoZmxpbmtDb25maWcuam9iU2VydmVyICsgXCIvY29uZmlnXCIpLnN1Y2Nlc3MoZnVuY3Rpb24oZGF0YSwgc3RhdHVzLCBoZWFkZXJzLCBjb25maWcpIHtcbiAgICAgIHJldHVybiBkZWZlcnJlZC5yZXNvbHZlKGRhdGEpO1xuICAgIH0pO1xuICAgIHJldHVybiBkZWZlcnJlZC5wcm9taXNlO1xuICB9O1xuICByZXR1cm4gdGhpcztcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBka
XN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5jb250cm9sbGVyICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCAoJH
Njb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdmaW5pc2hlZCcpXG5cbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuXG4gICRzY29wZS5qb2JPYnNlcnZlcigpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ1NpbmdsZUpvYkNvbnRyb2xsZXInLCAoJHN
jb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRyb290U2NvcGUpIC0+XG4gICRzY29wZS5qb2JpZCA9ICRzdGF0ZVBhcmFtcy5qb2JpZFxuICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcbiAgJHJvb3RTY29wZS5wbGFuID0gbnVsbFxuXG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuIChkYXRhKSAtPlxuICAgICRyb290U2NvcGUuam9iID0gZGF0YVxuICAgICRyb290U2NvcGUucGxhbiA9IGRhdGEucGxhblxuXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcbiAgICAkcm9vdFNjb3BlLnBsYW4gPSBudWxsXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Db250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iUGxhbkNvbnRyb2xsZXInXG5cbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJGFuY2hvclNjcm9sbCkgLT5cbiAgY29uc29sZS5sb2cgJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcidcblxu
ICAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZFxuXG4gICRzY29wZS5zdGF0ZUxpc3QgPSBKb2JzU2VydmljZS5zdGF0ZUxpc3QoKVxuXG4gIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUubm9kZSA9IGRhdGFcbiAgICAkc2NvcGUuam9iLmN1cnJlbnROb2RlID0gZGF0YVxuXG4gICAgIyBhbmNob3IgPSBcInZlcnRleC1yb3ctXCIgKyBkYXRhLmlkXG4gICAgIyBjb25zb2xlLmxvZyBhbmNob3JcbiAgICAjICRhbmNob3JTY3JvbGwoYW5jaG9yKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQbGFuTm9kZVRhYnNDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgY29uc29sZS5sb2cgJ0pvYlBsYW5Ob2RlVGFic0NvbnRyb2xsZXInXG5cbiAgJHNjb3BlLm5vZGVpZCA9ICRzdGF0ZVBhcmFtcy5ub2RlaWRcbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpXG5cbiAgSm9ic1NlcnZpY2UuZ2V0Tm9kZSgkc2NvcGUubm9kZWlkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS5ub2RlID0gZGF0YVxuICAgICRzY29wZS5qb2IuY3VycmVudE5vZGUgPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Ob2RlTGlzdENvb
nRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlLCAkc3RhdGUpIC0+XG4gIGNvbnNvbGUubG9nICdKb2JQbGFuTm9kZUxpc3RDb250cm9sbGVyJ1xuXG4gICRzY29wZS5nb3RvQWNjdW11bGF0b3JzID0gKG5vZGVpZCkgLT5cbiAgICAkc3RhdGUuZ28oJ14uYWNjdW11bGF0b3JzJywgeyBub2RlaWQ6IG5vZGVpZCB9KVxuXG4gIEpvYnNTZXJ2aWNlLmdldFN1YnRhc2tzKCRzdGF0ZVBhcmFtcy5ub2RlaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLnZlcnRleCA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbk5vZGVMaXN0QWNjdW11bGF0b3JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRzdGF0ZSkgLT5cbiAgY29uc29sZS5sb2cgJ0pvYlBsYW5Ob2RlTGlzdEFjY3VtdWxhdG9yc0NvbnRyb2xsZXInXG5cbiAgJHNjb3BlLmdvdG9HZW5lcmljID0gKG5vZGVpZCkgLT5cbiAgICAkc3RhdGUuZ28oJ14uZ2VuZXJpYycsIHsgbm9kZWlkOiBub2RlaWQgfSlcblxuICAjIEpvYnNTZXJ2aWNlLmdldFN1YnRhc2tzKCRzdGF0ZVBhcmFtcy5ub2RlaWQpLnRoZW4gKGRhdGEpIC0+XG4gICMgICAkc2NvcGUudmVydGV4ID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQbGFuTm9kZVByb3Blcn
RpZXNDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iUGxhbk5vZGVQcm9wZXJ0aWVzQ29udHJvbGxlcidcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbk5vZGVBY2N1bXVsYXRvcnNDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iUGxhbk5vZGVBY2N1bXVsYXRvcnNDb250cm9sbGVyJ1xuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmdldFZlcnRleCgkc3RhdGVQYXJhbXMudmVydGV4SWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLnZlcnRleCA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iRXhjZXB0aW9uc0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmxvYWRFeGNlcHRpb25zKCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUuZXhjZXB0aW9ucyA9IGRhdGFcblxuIiw
iYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuY29udHJvbGxlcignUnVubmluZ0pvYnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygncnVubmluZycpO1xuICB9O1xuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS5qb2JPYnNlcnZlcigpO1xufSkuY29udHJvbGxlcignQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdmaW5pc2hlZCcpO1xuICB9O1xuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigk
c2NvcGUuam9iT2JzZXJ2ZXIpO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS5qb2JPYnNlcnZlcigpO1xufSkuY29udHJvbGxlcignU2luZ2xlSm9iQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlLCAkcm9vdFNjb3BlKSB7XG4gICRzY29wZS5qb2JpZCA9ICRzdGF0ZVBhcmFtcy5qb2JpZDtcbiAgJHJvb3RTY29wZS5qb2IgPSBudWxsO1xuICAkcm9vdFNjb3BlLnBsYW4gPSBudWxsO1xuICBKb2JzU2VydmljZS5sb2FkSm9iKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgJHJvb3RTY29wZS5qb2IgPSBkYXRhO1xuICAgIHJldHVybiAkcm9vdFNjb3BlLnBsYW4gPSBkYXRhLnBsYW47XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICAkcm9vdFNjb3BlLmpvYiA9IG51bGw7XG4gICAgcmV0dXJuICRyb290U2NvcGUucGxhbiA9IG51bGw7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUGxhbkNvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICBjb25zb2xlLmxvZygnSm9iUGxhbkNvbnRyb2xsZXInKTtcbiAgcmV0dXJuICRzY29wZS5zdGF0ZUxpc3QgPSBKb2JzU2VydmljZS5zdGF0ZUxpc3QoKTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZ
SwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlLCAkYW5jaG9yU2Nyb2xsKSB7XG4gIGNvbnNvbGUubG9nKCdKb2JQbGFuTm9kZUNvbnRyb2xsZXInKTtcbiAgJHNjb3BlLm5vZGVpZCA9ICRzdGF0ZVBhcmFtcy5ub2RlaWQ7XG4gICRzY29wZS5zdGF0ZUxpc3QgPSBKb2JzU2VydmljZS5zdGF0ZUxpc3QoKTtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgJHNjb3BlLm5vZGUgPSBkYXRhO1xuICAgIHJldHVybiAkc2NvcGUuam9iLmN1cnJlbnROb2RlID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuTm9kZVRhYnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIGNvbnNvbGUubG9nKCdKb2JQbGFuTm9kZVRhYnNDb250cm9sbGVyJyk7XG4gICRzY29wZS5ub2RlaWQgPSAkc3RhdGVQYXJhbXMubm9kZWlkO1xuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KCk7XG4gIHJldHVybiBKb2JzU2VydmljZS5nZXROb2RlKCRzY29wZS5ub2RlaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICRzY29wZS5ub2RlID0gZGF0YTtcbiAgICByZXR1cm4gJHNjb3BlLmpvYi5jdXJyZW50Tm9kZSA9IGRhdGE7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUGxhbk5vZGVMaXN0Q29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZS
wgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHN0YXRlKSB7XG4gIGNvbnNvbGUubG9nKCdKb2JQbGFuTm9kZUxpc3RDb250cm9sbGVyJyk7XG4gICRzY29wZS5nb3RvQWNjdW11bGF0b3JzID0gZnVuY3Rpb24obm9kZWlkKSB7XG4gICAgcmV0dXJuICRzdGF0ZS5nbygnXi5hY2N1bXVsYXRvcnMnLCB7XG4gICAgICBub2RlaWQ6IG5vZGVpZFxuICAgIH0pO1xuICB9O1xuICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0U3VidGFza3MoJHN0YXRlUGFyYW1zLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS52ZXJ0ZXggPSBkYXRhO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Ob2RlTGlzdEFjY3VtdWxhdG9yc0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRzdGF0ZSkge1xuICBjb25zb2xlLmxvZygnSm9iUGxhbk5vZGVMaXN0QWNjdW11bGF0b3JzQ29udHJvbGxlcicpO1xuICByZXR1cm4gJHNjb3BlLmdvdG9HZW5lcmljID0gZnVuY3Rpb24obm9kZWlkKSB7XG4gICAgcmV0dXJuICRzdGF0ZS5nbygnXi5nZW5lcmljJywge1xuICAgICAgbm9kZWlkOiBub2RlaWRcbiAgICB9KTtcbiAgfTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Ob2RlUHJvcGVydGllc0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gY29uc29sZS5sb2coJ0p
vYlBsYW5Ob2RlUHJvcGVydGllc0NvbnRyb2xsZXInKTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Ob2RlQWNjdW11bGF0b3JzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiBjb25zb2xlLmxvZygnSm9iUGxhbk5vZGVBY2N1bXVsYXRvcnNDb250cm9sbGVyJyk7XG59KS5jb250cm9sbGVyKCdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy52ZXJ0ZXhJZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS52ZXJ0ZXggPSBkYXRhO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYkV4Y2VwdGlvbnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmxvYWRFeGNlcHRpb25zKCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5leGNlcHRpb25zID0gZGF0YTtcbiAgfSk7XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9U
SUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tL
S0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAndmVydGV4JywgKCRzdGF0ZSkgLT5cbiAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUgc2Vjb25kYXJ5JyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGRhdGE6IFwiPVwiXG5cbiAgbGluazogKHNjb3BlLCBlbGVtLCBhdHRycykgLT5cbiAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEuc3VidGFza3MsIChzdWJ0YXNrLCBpKSAtPlxuICAgICAgICB0ZXN0RGF0YS5wdXNoIHtcbiAgICAgICAgICBsYWJlbDogXCIje3N1YnRhc2suaG9zdH0gKCN7c3VidGFzay5zdWJ0YXNrfSlcIlxuICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiM2NjZcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiU0NIRURVTEVEXCJdXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YX
NrLnRpbWVzdGFtcHNbXCJERVBMT1lJTkdcIl1cbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIkRlcGxveWluZ1wiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiNhYWFcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiREVQTE9ZSU5HXCJdXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU5HXCJdXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJSdW5uaW5nXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2RkZFwiXG4gICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU5HXCJdXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJGSU5JU0hFRFwiXVxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBdXG4gICAgICAgIH1cblxuICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKClcbiAgICAgIC50aWNrRm9ybWF0KHtcbiAgICAgICAgZm9ybWF
0OiBkMy50aW1lLmZvcm1hdChcIiVMXCIpXG4gICAgICAgICMgdGlja0ludGVydmFsOiAxXG4gICAgICAgIHRpY2tTaXplOiAxXG4gICAgICB9KVxuICAgICAgLnByZWZpeChcInNpbmdsZVwiKVxuICAgICAgLmxhYmVsRm9ybWF0KChsYWJlbCkgLT5cbiAgICAgICAgbGFiZWxcbiAgICAgIClcbiAgICAgIC5tYXJnaW4oeyBsZWZ0OiAxMDAsIHJpZ2h0OiAwLCB0b3A6IDAsIGJvdHRvbTogMCB9KVxuICAgICAgLml0ZW1IZWlnaHQoMzApXG4gICAgICAucmVsYXRpdmVUaW1lKClcblxuICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKVxuICAgICAgLmRhdHVtKHRlc3REYXRhKVxuICAgICAgLmNhbGwoY2hhcnQpXG5cbiAgICBhbmFseXplVGltZShzY29wZS5kYXRhKVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0aW1lbGluZScsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGpvYjogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpXG5cbiAgICB0cmFuc2xhdGVM
YWJlbCA9IChsYWJlbCkgLT5cbiAgICAgIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgdGVzdERhdGEucHVzaCBcbiAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIlxuICAgICAgICAgIGNvbG9yOiBcIiNjY2NjY2NcIlxuICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEudGltZXN0YW1wc1tcIkNSRUFURURcIl1cbiAgICAgICAgICBlbmRpbmdfdGltZTogZGF0YS50aW1lc3RhbXBzW1wiQ1JFQVRFRFwiXSArIDFcbiAgICAgICAgICB0eXBlOiAnc2NoZWR1bGVkJ1xuICAgICAgICBdXG5cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEudmVydGljZXMsICh2ZXJ0ZXgpIC0+XG4gICAgICAgIGlmIHZlcnRleFsnc3RhcnQtdGltZSddID4gLTFcbiAgICAgICAgICB0ZXN0RGF0YS5wdXNoIFxuICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAgbGFiZWw6IHRyYW5zbGF0ZUxhYmVsKHZlcnRleC5uYW1lKVxuICAgICAgICAgICAgICBjb2xvcjogXCIjZDlmMWY3XCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzYyY2RlYVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZlcnRleFsnc3RhcnQtdGltZSddXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2ZXJ0ZXhbJ
2VuZC10aW1lJ11cbiAgICAgICAgICAgICAgbGluazogdmVydGV4LmlkXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgXVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5jbGljaygoZCwgaSwgZGF0dW0pIC0+XG4gICAgICAgIGlmIGQubGlua1xuICAgICAgICAgICRzdGF0ZS5nbyBcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHsgam9iaWQ6IGRhdGEuamlkLCB2ZXJ0ZXhJZDogZC5saW5rIH1cblxuICAgICAgKVxuICAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICAgICAgIyB0aWNrVGltZTogZDMudGltZS5zZWNvbmRcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDAuNVxuICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJtYWluXCIpXG4gICAgICAubWFyZ2luKHsgbGVmdDogMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgzMClcbiAgICAgIC5zaG93Qm9yZGVyTGluZSgpXG4gICAgICAuc2hvd0hvdXJUaW1lbGluZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgc2NvcGUuJHdhdGNoIGF0dHJzLmpvYiwgKGRhdGEpIC0+XG4gICAgICBhbmFseXplVGltZShkYXRhKSBpZiBkYXRhXG5cbiAgICByZXR1cm
5cblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2pvYlBsYW4nLCAoJHRpbWVvdXQpIC0+XG4gIHRlbXBsYXRlOiBcIlxuICAgIDxzdmcgY2xhc3M9J2dyYXBoJyB3aWR0aD0nNTAwJyBoZWlnaHQ9JzQwMCc+PGcgLz48L3N2Zz5cbiAgICA8c3ZnIGNsYXNzPSd0bXAnIHdpZHRoPScxJyBoZWlnaHQ9JzEnPjxnIC8+PC9zdmc+XG4gICAgPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+XG4gICAgICA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20taW4nIG5nLWNsaWNrPSd6b29tSW4oKSc+PGkgY2xhc3M9J2ZhIGZhLXBsdXMnIC8+PC9hPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLW91dCcgbmctY2xpY2s9J3pvb21PdXQoKSc+PGkgY2xhc3M9J2ZhIGZhLW1pbnVzJyAvPjwvYT5cbiAgICA8L2Rpdj5cIlxuXG4gIHNjb3BlOlxuICAgIHBsYW46ICc9J1xuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgbWFpblpvb20gPSBkMy5iZWhhdmlvci56b29tKClcbiAgICBzdWJncmFwaHMgPSBbXVxuICAgIGpvYmlkID0gYXR0cnMuam9iaWRcblxuICAgIG1haW5TdmdFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzBdXG4gICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXVxuICAgIG1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdXG5cbiAgICBkM21haW5
TdmcgPSBkMy5zZWxlY3QobWFpblN2Z0VsZW1lbnQpXG4gICAgZDNtYWluU3ZnRyA9IGQzLnNlbGVjdChtYWluRylcbiAgICBkM3RtcFN2ZyA9IGQzLnNlbGVjdChtYWluVG1wRWxlbWVudClcblxuICAgICMgYW5ndWxhci5lbGVtZW50KG1haW5HKS5lbXB0eSgpXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KGVsZW0uY2hpbGRyZW4oKVswXSkud2lkdGgoY29udGFpbmVyVylcblxuICAgIHNjb3BlLnpvb21JbiA9IC0+XG4gICAgICBpZiBtYWluWm9vbS5zY2FsZSgpIDwgMi45OVxuICAgICAgICBcbiAgICAgICAgIyBDYWxjdWxhdGUgYW5kIHN0b3JlIG5ldyB2YWx1ZXMgaW4gem9vbSBvYmplY3RcbiAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKClcbiAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgbWFpblpvb20uc2NhbGUgbWFpblpvb20uc2NhbGUoKSArIDAuMVxuICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUgWyB2MSwgdjIgXVxuICAgICAgICBcbiAgICAgICAgIyBUcmFuc2Zvcm0gc3ZnXG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIg
KyB2MiArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIlxuXG4gICAgc2NvcGUuem9vbU91dCA9IC0+XG4gICAgICBpZiBtYWluWm9vbS5zY2FsZSgpID4gMC4zMVxuICAgICAgICBcbiAgICAgICAgIyBDYWxjdWxhdGUgYW5kIHN0b3JlIG5ldyB2YWx1ZXMgaW4gbWFpblpvb20gb2JqZWN0XG4gICAgICAgIG1haW5ab29tLnNjYWxlIG1haW5ab29tLnNjYWxlKCkgLSAwLjFcbiAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKClcbiAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgbWFpblpvb20udHJhbnNsYXRlIFsgdjEsIHYyIF1cbiAgICAgICAgXG4gICAgICAgICMgVHJhbnNmb3JtIHN2Z1xuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCJcblxuICAgICNjcmVhdGUgYSBsYWJlbCBvZiBhbiBlZGdlXG4gICAgY3JlYXRlTGFiZWxFZGdlID0gKGVsKSAtPlxuICAgICAgbGFiZWxWYWx1ZSA9IFwiXCJcbiAgICAgIGlmIGVsLnNoaXBfc3RyYXRlZ3k/IG9yIGVsLmxvY2FsX3N0cmF0ZWd5P1xuI
CAgICAgICBsYWJlbFZhbHVlICs9IFwiPGRpdiBjbGFzcz0nZWRnZS1sYWJlbCc+XCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBlbC5zaGlwX3N0cmF0ZWd5ICBpZiBlbC5zaGlwX3N0cmF0ZWd5P1xuICAgICAgICBsYWJlbFZhbHVlICs9IFwiIChcIiArIGVsLnRlbXBfbW9kZSArIFwiKVwiICB1bmxlc3MgZWwudGVtcF9tb2RlIGlzIGB1bmRlZmluZWRgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCIsPGJyPlwiICsgZWwubG9jYWxfc3RyYXRlZ3kgIHVubGVzcyBlbC5sb2NhbF9zdHJhdGVneSBpcyBgdW5kZWZpbmVkYFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPC9kaXY+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuXG4gICAgIyB0cnVlLCBpZiB0aGUgbm9kZSBpcyBhIHNwZWNpYWwgbm9kZSBmcm9tIGFuIGl0ZXJhdGlvblxuICAgIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUgPSAoaW5mbykgLT5cbiAgICAgIChpbmZvIGlzIFwicGFydGlhbFNvbHV0aW9uXCIgb3IgaW5mbyBpcyBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiBvciBpbmZvIGlzIFwid29ya3NldFwiIG9yIGluZm8gaXMgXCJuZXh0V29ya3NldFwiIG9yIGluZm8gaXMgXCJzb2x1dGlvblNldFwiIG9yIGluZm8gaXMgXCJzb2x1dGlvbkRlbHRhXCIpXG5cbiAgICBnZXROb2RlVHlwZSA9IChlbCwgaW5mbykgLT5cbiAgICAgIGlmIGluZm8gaXMgXCJtaXJyb3JcIlxuICAgICAgICAnbm9kZS1taXJyb3InXG5cbiAgICAgIGVsc2UgaWYgaXNTcGVjaWFsSXRlcm
F0aW9uTm9kZShpbmZvKVxuICAgICAgICAnbm9kZS1pdGVyYXRpb24nXG5cbiAgICAgIGVsc2VcbiAgICAgICAgICAnbm9kZS1ub3JtYWwnXG4gICAgICBcbiAgICAjIGNyZWF0ZXMgdGhlIGxhYmVsIG9mIGEgbm9kZSwgaW4gaW5mbyBpcyBzdG9yZWQsIHdoZXRoZXIgaXQgaXMgYSBzcGVjaWFsIG5vZGUgKGxpa2UgYSBtaXJyb3IgaW4gYW4gaXRlcmF0aW9uKVxuICAgIGNyZWF0ZUxhYmVsTm9kZSA9IChlbCwgaW5mbywgbWF4VywgbWF4SCkgLT5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIjxhIGhyZWY9JyMvam9icy9cIiArIGpvYmlkICsgXCIvdmVydGV4L1wiICsgZWwuaWQgKyBcIicgY2xhc3M9J25vZGUtbGFiZWwgXCIgKyBnZXROb2RlVHlwZShlbCwgaW5mbykgKyBcIic+XCJcblxuICAgICAgIyBOb2RlbmFtZVxuICAgICAgaWYgaW5mbyBpcyBcIm1pcnJvclwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+TWlycm9yIG9mIFwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCJcbiAgICAgIGVsc2VcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5cIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiXG4gICAgICBpZiBlbC5kZXNjcmlwdGlvbiBpcyBcIlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCJcIlxuICAgICAgZWxzZVxuICAgICAgICBzdGVwTmFtZSA9IGVsLmRlc2NyaXB0aW9uXG4gICAgICAgIFxuICAgICAgICAjIGNsZWFuIHN0ZXB
OYW1lXG4gICAgICAgIHN0ZXBOYW1lID0gc2hvcnRlblN0cmluZyhzdGVwTmFtZSlcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNCBjbGFzcz0nc3RlcC1uYW1lJz5cIiArIHN0ZXBOYW1lICsgXCI8L2g0PlwiXG4gICAgICBcbiAgICAgICMgSWYgdGhpcyBub2RlIGlzIGFuIFwiaXRlcmF0aW9uXCIgd2UgbmVlZCBhIGRpZmZlcmVudCBwYW5lbC1ib2R5XG4gICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICBsYWJlbFZhbHVlICs9IGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbihlbC5pZCwgbWF4VywgbWF4SClcbiAgICAgIGVsc2VcbiAgICAgICAgXG4gICAgICAgICMgT3RoZXJ3aXNlIGFkZCBpbmZvcyAgICBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5cIiArIGluZm8gKyBcIiBOb2RlPC9oNT5cIiAgaWYgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlBhcmFsbGVsaXNtOiBcIiArIGVsLnBhcmFsbGVsaXNtICsgXCI8L2g1PlwiICB1bmxlc3MgZWwucGFyYWxsZWxpc20gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1Pk9wZXJhdGlvbjogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLm9wZXJhdG9yX3N0cmF0ZWd5KSArIFwiPC9oNT5cIiAgdW5sZXNzIGVsLm9wZXJhdG9yIGlzIGB1bmRlZmluZWRgXG4gICAgICBcbiAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2E+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuICAgICMgRXh0
ZW5kcyB0aGUgbGFiZWwgb2YgYSBub2RlIHdpdGggYW4gYWRkaXRpb25hbCBzdmcgRWxlbWVudCB0byBwcmVzZW50IHRoZSBpdGVyYXRpb24uXG4gICAgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uID0gKGlkLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgc3ZnSUQgPSBcInN2Zy1cIiArIGlkXG5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIjxzdmcgY2xhc3M9J1wiICsgc3ZnSUQgKyBcIicgd2lkdGg9XCIgKyBtYXhXICsgXCIgaGVpZ2h0PVwiICsgbWF4SCArIFwiPjxnIC8+PC9zdmc+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuICAgICMgU3BsaXQgYSBzdHJpbmcgaW50byBtdWx0aXBsZSBsaW5lcyBzbyB0aGF0IGVhY2ggbGluZSBoYXMgbGVzcyB0aGFuIDMwIGxldHRlcnMuXG4gICAgc2hvcnRlblN0cmluZyA9IChzKSAtPlxuICAgICAgIyBtYWtlIHN1cmUgdGhhdCBuYW1lIGRvZXMgbm90IGNvbnRhaW4gYSA8IChiZWNhdXNlIG9mIGh0bWwpXG4gICAgICBpZiBzLmNoYXJBdCgwKSBpcyBcIjxcIlxuICAgICAgICBzID0gcy5yZXBsYWNlKFwiPFwiLCBcIiZsdDtcIilcbiAgICAgICAgcyA9IHMucmVwbGFjZShcIj5cIiwgXCImZ3Q7XCIpXG4gICAgICBzYnIgPSBcIlwiXG4gICAgICB3aGlsZSBzLmxlbmd0aCA+IDMwXG4gICAgICAgIHNiciA9IHNiciArIHMuc3Vic3RyaW5nKDAsIDMwKSArIFwiPGJyPlwiXG4gICAgICAgIHMgPSBzLnN1YnN0cmluZygzMCwgcy5sZW5ndGgpXG4gICAgICBzYnIgPSBzYnIgKyBzXG4gI
CAgICBzYnJcblxuICAgIGNyZWF0ZU5vZGUgPSAoZywgZGF0YSwgZWwsIGlzUGFyZW50ID0gZmFsc2UsIG1heFcsIG1heEgpIC0+XG4gICAgICAjIGNyZWF0ZSBub2RlLCBzZW5kIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25zIGFib3V0IHRoZSBub2RlIGlmIGl0IGlzIGEgc3BlY2lhbCBvbmVcbiAgICAgIGlmIGVsLmlkIGlzIGRhdGEucGFydGlhbF9zb2x1dGlvblxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLm5leHRfcGFydGlhbF9zb2x1dGlvblxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEud29ya3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJ3b3Jrc2V0XCIsIG1heF
csIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwid29ya3NldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEubmV4dF93b3Jrc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRXb3Jrc2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFdvcmtzZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLnNvbHV0aW9uX3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJzb2x1dGlvblNldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uU2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5zb2x1dGlvbl9kZWx0YVxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25EZWx0YVwiKVxuXG4gICA
gICBlbHNlXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIlwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIlwiKVxuXG4gICAgY3JlYXRlRWRnZSA9IChnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZCkgLT5cbiAgICAgIHVubGVzcyBleGlzdGluZ05vZGVzLmluZGV4T2YocHJlZC5pZCkgaXMgLTFcbiAgICAgICAgZy5zZXRFZGdlIHByZWQuaWQsIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UocHJlZClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGFycm93aGVhZDogJ25vcm1hbCdcblxuICAgICAgZWxzZVxuICAgICAgICBtaXNzaW5nTm9kZSA9IHNlYXJjaEZvck5vZGUoZGF0YSwgcHJlZC5pZClcbiAgICAgICAgdW5sZXNzICFtaXNzaW5nTm9kZSBvciBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgaXMgdHJ1ZVxuICAgICAgICAgIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9IHRydWVcbiAgICAgICAgICBnLnNldE5vZGUgbWlzc2luZ05vZGUuaWQsXG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKG1pc3NpbmdOb2RlLCBcIm1pcnJvclwiKVxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShtaXNzaW5nTm9k
ZSwgJ21pcnJvcicpXG5cbiAgICAgICAgICBnLnNldEVkZ2UgbWlzc2luZ05vZGUuaWQsIGVsLmlkLFxuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShtaXNzaW5nTm9kZSlcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG5cbiAgICBsb2FkSnNvblRvRGFncmUgPSAoZywgZGF0YSkgLT5cbiAgICAgIGV4aXN0aW5nTm9kZXMgPSBbXVxuXG4gICAgICBpZiBkYXRhLm5vZGVzP1xuICAgICAgICAjIFRoaXMgaXMgdGhlIG5vcm1hbCBqc29uIGRhdGFcbiAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5ub2Rlc1xuXG4gICAgICBlbHNlXG4gICAgICAgICMgVGhpcyBpcyBhbiBpdGVyYXRpb24sIHdlIG5vdyBzdG9yZSBzcGVjaWFsIGl0ZXJhdGlvbiBub2RlcyBpZiBwb3NzaWJsZVxuICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgaXNQYXJlbnQgPSB0cnVlXG5cbiAgICAgIGZvciBlbCBpbiB0b0l0ZXJhdGVcbiAgICAgICAgbWF4VyA9IDBcbiAgICAgICAgbWF4SCA9IDBcblxuICAgICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uXG4gICAgICAgICAgc2cgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7IG11bHRpZ3JhcGg6IHRydWUsIGNvbXBvdW5kOiB0cnVlIH0pLnNldEdyYXBoKHtcbiAgICAgICAgICAgIG5vZGVzZXA6IDIwXG4gICAgICAgICAgICBlZGdlc2VwOiAwXG4gICAgICAgICAgICByYW5rc2VwOiAyMFxuICAgICAgICAgICAgcmFua2RpcjogXCJMU
lwiXG4gICAgICAgICAgICBtYXJnaW54OiAxMFxuICAgICAgICAgICAgbWFyZ2lueTogMTBcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICBzdWJncmFwaHNbZWwuaWRdID0gc2dcblxuICAgICAgICAgIGxvYWRKc29uVG9EYWdyZShzZywgZWwpXG5cbiAgICAgICAgICByID0gbmV3IGRhZ3JlRDMucmVuZGVyKClcbiAgICAgICAgICBkM3RtcFN2Zy5zZWxlY3QoJ2cnKS5jYWxsKHIsIHNnKVxuICAgICAgICAgIG1heFcgPSBzZy5ncmFwaCgpLndpZHRoXG4gICAgICAgICAgbWF4SCA9IHNnLmdyYXBoKCkuaGVpZ2h0XG5cbiAgICAgICAgICBhbmd1bGFyLmVsZW1lbnQobWFpblRtcEVsZW1lbnQpLmVtcHR5KClcblxuICAgICAgICBjcmVhdGVOb2RlKGcsIGRhdGEsIGVsLCBpc1BhcmVudCwgbWF4VywgbWF4SClcblxuICAgICAgICBleGlzdGluZ05vZGVzLnB1c2ggZWwuaWRcbiAgICAgICAgXG4gICAgICAgICMgY3JlYXRlIGVkZ2VzIGZyb20gaW5wdXRzIHRvIGN1cnJlbnQgbm9kZVxuICAgICAgICBpZiBlbC5pbnB1dHM/XG4gICAgICAgICAgZm9yIHByZWQgaW4gZWwuaW5wdXRzXG4gICAgICAgICAgICBjcmVhdGVFZGdlKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKVxuXG4gICAgICBnXG5cbiAgICAjIHNlYXJjaGVzIGluIHRoZSBnbG9iYWwgSlNPTkRhdGEgZm9yIHRoZSBub2RlIHdpdGggdGhlIGdpdmVuIGlkXG4gICAgc2VhcmNoRm9yTm9kZSA9IChkYXRhLCBub2RlSUQpIC0+XG4gICAgICBmb3
IgaSBvZiBkYXRhLm5vZGVzXG4gICAgICAgIGVsID0gZGF0YS5ub2Rlc1tpXVxuICAgICAgICByZXR1cm4gZWwgIGlmIGVsLmlkIGlzIG5vZGVJRFxuICAgICAgICBcbiAgICAgICAgIyBsb29rIGZvciBub2RlcyB0aGF0IGFyZSBpbiBpdGVyYXRpb25zXG4gICAgICAgIGlmIGVsLnN0ZXBfZnVuY3Rpb24/XG4gICAgICAgICAgZm9yIGogb2YgZWwuc3RlcF9mdW5jdGlvblxuICAgICAgICAgICAgcmV0dXJuIGVsLnN0ZXBfZnVuY3Rpb25bal0gIGlmIGVsLnN0ZXBfZnVuY3Rpb25bal0uaWQgaXMgbm9kZUlEXG5cbiAgICBkcmF3R3JhcGggPSAoZGF0YSkgLT5cbiAgICAgIGcgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7IG11bHRpZ3JhcGg6IHRydWUsIGNvbXBvdW5kOiB0cnVlIH0pLnNldEdyYXBoKHtcbiAgICAgICAgbm9kZXNlcDogNzBcbiAgICAgICAgZWRnZXNlcDogMFxuICAgICAgICByYW5rc2VwOiA1MFxuICAgICAgICByYW5rZGlyOiBcIkxSXCJcbiAgICAgICAgbWFyZ2lueDogNDBcbiAgICAgICAgbWFyZ2lueTogNDBcbiAgICAgICAgfSlcblxuICAgICAgbG9hZEpzb25Ub0RhZ3JlKGcsIGRhdGEpXG5cbiAgICAgIHJlbmRlcmVyID0gbmV3IGRhZ3JlRDMucmVuZGVyKClcbiAgICAgIGQzbWFpblN2Z0cuY2FsbChyZW5kZXJlciwgZylcblxuICAgICAgZm9yIGksIHNnIG9mIHN1YmdyYXBoc1xuICAgICAgICBkM21haW5Tdmcuc2VsZWN0KCdzdmcuc3ZnLScgKyBpICsgJyBnJykuY2FsbChyZW5kZXJ
lciwgc2cpXG5cbiAgICAgIG5ld1NjYWxlID0gMC41XG5cbiAgICAgIHhDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLndpZHRoKCkgLSBnLmdyYXBoKCkud2lkdGggKiBuZXdTY2FsZSkgLyAyKVxuICAgICAgeUNlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkuaGVpZ2h0KCkgLSBnLmdyYXBoKCkuaGVpZ2h0ICogbmV3U2NhbGUpIC8gMilcblxuICAgICAgbWFpblpvb20uc2NhbGUobmV3U2NhbGUpLnRyYW5zbGF0ZShbeENlbnRlck9mZnNldCwgeUNlbnRlck9mZnNldF0pXG5cbiAgICAgIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHhDZW50ZXJPZmZzZXQgKyBcIiwgXCIgKyB5Q2VudGVyT2Zmc2V0ICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKVxuXG4gICAgICBtYWluWm9vbS5vbihcInpvb21cIiwgLT5cbiAgICAgICAgZXYgPSBkMy5ldmVudFxuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyBldi50cmFuc2xhdGUgKyBcIikgc2NhbGUoXCIgKyBldi5zY2FsZSArIFwiKVwiXG4gICAgICApXG4gICAgICBtYWluWm9vbShkM21haW5TdmcpXG5cbiAgICBzY29wZS4kd2F0Y2ggYXR0cnMucGxhbiwgKG5ld1BsYW4pIC0+XG4gICAgICBkcmF3R3JhcGgobmV3UGxhbikgaWYgbmV3UGxh
blxuXG4gICAgcmV0dXJuXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ3ZlcnRleCcsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCIsXG4gICAgc2NvcGU6IHtcbiAgICAgIGRhdGE6IFwiPVwiXG4gICAgfSxcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbSwgYXR0cnMpIHtcbiAgICAgIHZhciBhbmFseXplVGltZSwgY29udGFpbmVyVywgc3ZnRWw7XG4gICAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXTtcbiAgICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKCk7XG4gICAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVyk7XG4gICAgICBhbmFseXplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGNoYXJ0LCBzdmcsIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICBhbmd1bGFyLmZvckVhY2goZGF0YS5zdWJ0YXNrcywgZnVuY3Rpb24oc3VidGFzaywgaSkge1xuICAgICAgICAgIHJldHVybiB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICAgIGxhYmVsOiBzdWJ0YXNrLmhvc3QgKyBcIiAoXCIgKyBzdWJ0YXNrLnN1YnRhc2sgKyBcIilcIixcbiAgICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgI
CBsYWJlbDogXCJTY2hlZHVsZWRcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjNjY2XCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlNDSEVEVUxFRFwiXSxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiREVQTE9ZSU5HXCJdLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiRGVwbG95aW5nXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiI2FhYVwiLFxuICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJERVBMT1lJTkdcIl0sXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlJVTk5JTkdcIl0sXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH0sIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogXCJSdW5uaW5nXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiI2RkZFwiLFxuICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU
5HXCJdLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJGSU5JU0hFRFwiXSxcbiAgICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgXVxuICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKCkudGlja0Zvcm1hdCh7XG4gICAgICAgICAgZm9ybWF0OiBkMy50aW1lLmZvcm1hdChcIiVMXCIpLFxuICAgICAgICAgIHRpY2tTaXplOiAxXG4gICAgICAgIH0pLnByZWZpeChcInNpbmdsZVwiKS5sYWJlbEZvcm1hdChmdW5jdGlvbihsYWJlbCkge1xuICAgICAgICAgIHJldHVybiBsYWJlbDtcbiAgICAgICAgfSkubWFyZ2luKHtcbiAgICAgICAgICBsZWZ0OiAxMDAsXG4gICAgICAgICAgcmlnaHQ6IDAsXG4gICAgICAgICAgdG9wOiAwLFxuICAgICAgICAgIGJvdHRvbTogMFxuICAgICAgICB9KS5pdGVtSGVpZ2h0KDMwKS5yZWxhdGl2ZVRpbWUoKTtcbiAgICAgICAgcmV0dXJuIHN2ZyA9IGQzLnNlbGVjdChzdmdFbCkuZGF0dW0odGVzdERhdGEpLmNhbGwoY2hhcnQpO1xuICAgICAgfTtcbiAgICAgIGFuYWx5emVUaW1lKHNjb3BlLmRhdGEpO1xuICAgIH1cbiAgfTtcbn0pLmRpcmVjdGl2ZSgndGltZWxpbmUnLCBmdW5jdGlvbigkc3RhdGUpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZScgd2lkdGg9JzAnIGhlaWd
odD0nMCc+PC9zdmc+XCIsXG4gICAgc2NvcGU6IHtcbiAgICAgIGpvYjogXCI9XCJcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGFuYWx5emVUaW1lLCBjb250YWluZXJXLCBzdmdFbCwgdHJhbnNsYXRlTGFiZWw7XG4gICAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXTtcbiAgICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKCk7XG4gICAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVyk7XG4gICAgICB0cmFuc2xhdGVMYWJlbCA9IGZ1bmN0aW9uKGxhYmVsKSB7XG4gICAgICAgIHJldHVybiBsYWJlbC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIik7XG4gICAgICB9O1xuICAgICAgYW5hbHl6ZVRpbWUgPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIHZhciBjaGFydCwgc3ZnLCB0ZXN0RGF0YTtcbiAgICAgICAgdGVzdERhdGEgPSBbXTtcbiAgICAgICAgdGVzdERhdGEucHVzaCh7XG4gICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCIsXG4gICAgI
<TRUNCATED>
[27/51] [abbrv] flink git commit: [FLINK-2415] [monitoring api] Add
vertex details request handler, unify IDs between vertices and plan
Posted by se...@apache.org.
[FLINK-2415] [monitoring api] Add vertex details request handler, unify IDs between vertices and plan
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/b26ce5a3
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/b26ce5a3
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/b26ce5a3
Branch: refs/heads/master
Commit: b26ce5a35c5b9e1d0451fa129b80f3fb97960f2e
Parents: 1984a3f
Author: Stephan Ewen <se...@apache.org>
Authored: Thu Aug 20 23:27:50 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:51 2015 +0200
----------------------------------------------------------------------
.../api/common/accumulators/LongCounter.java | 35 ++-
.../flink/runtime/webmonitor/JsonFactory.java | 263 -------------------
.../runtime/webmonitor/WebRuntimeMonitor.java | 46 ++--
.../AbstractExecutionGraphRequestHandler.java | 4 +-
.../handlers/ClusterOverviewHandler.java | 78 ++++++
.../handlers/CurrentJobIdsHandler.java | 99 +++++++
.../handlers/CurrentJobsOverviewHandler.java | 128 +++++++++
.../handlers/DashboardConfigHandler.java | 64 +++++
.../handlers/ExecutionPlanHandler.java | 40 ---
.../webmonitor/handlers/JobConfigHandler.java | 53 ++--
.../webmonitor/handlers/JobDetailsHandler.java | 154 +++++++++++
.../handlers/JobExceptionsHandler.java | 73 ++---
.../webmonitor/handlers/JobPlanHandler.java | 40 +++
.../webmonitor/handlers/JobSummaryHandler.java | 43 ---
.../handlers/JobVertexDetailsHandler.java | 42 ---
.../handlers/JobVerticesOverviewHandler.java | 99 -------
.../handlers/JobsOverviewHandler.java | 74 ------
.../webmonitor/handlers/JsonFactory.java | 31 +++
.../runtime/webmonitor/handlers/Parameters.java | 40 ---
.../handlers/RequestConfigHandler.java | 47 ----
.../handlers/RequestJobIdsHandler.java | 68 -----
.../handlers/RequestOverviewHandler.java | 62 -----
.../handlers/SubtasksTimesHandler.java | 98 +++++++
.../executiongraph/ExecutionJobVertex.java | 50 +++-
.../runtime/webmonitor/WebMonitorUtils.java | 22 ++
25 files changed, 867 insertions(+), 886 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-core/src/main/java/org/apache/flink/api/common/accumulators/LongCounter.java
----------------------------------------------------------------------
diff --git a/flink-core/src/main/java/org/apache/flink/api/common/accumulators/LongCounter.java b/flink-core/src/main/java/org/apache/flink/api/common/accumulators/LongCounter.java
index 93b06d6..fa7f1ac 100644
--- a/flink-core/src/main/java/org/apache/flink/api/common/accumulators/LongCounter.java
+++ b/flink-core/src/main/java/org/apache/flink/api/common/accumulators/LongCounter.java
@@ -25,8 +25,19 @@ public class LongCounter implements SimpleAccumulator<Long> {
private static final long serialVersionUID = 1L;
- private long localValue = 0;
+ private long localValue;
+
+ public LongCounter() {}
+
+ public LongCounter(long value) {
+ this.localValue = value;
+ }
+
+ // ------------------------------------------------------------------------
+ // Accumulator
+ // ------------------------------------------------------------------------
+
/**
* Consider using {@link #add(long)} instead for primitive long values
*/
@@ -35,10 +46,6 @@ public class LongCounter implements SimpleAccumulator<Long> {
this.localValue += value;
}
- public void add(long value){
- this.localValue += value;
- }
-
@Override
public Long getLocalValue() {
return this.localValue;
@@ -53,7 +60,7 @@ public class LongCounter implements SimpleAccumulator<Long> {
public void resetLocal() {
this.localValue = 0;
}
-
+
@Override
public LongCounter clone() {
LongCounter result = new LongCounter();
@@ -61,6 +68,22 @@ public class LongCounter implements SimpleAccumulator<Long> {
return result;
}
+ // ------------------------------------------------------------------------
+ // Primitive Specializations
+ // ------------------------------------------------------------------------
+
+ public void add(long value){
+ this.localValue += value;
+ }
+
+ public long getLocalValuePrimitive() {
+ return this.localValue;
+ }
+
+ // ------------------------------------------------------------------------
+ // Utilities
+ // ------------------------------------------------------------------------
+
@Override
public String toString() {
return "LongCounter " + this.localValue;
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
deleted file mode 100644
index 66d7b0c..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import org.apache.flink.api.common.JobID;
-import org.apache.flink.runtime.execution.ExecutionState;
-import org.apache.flink.runtime.messages.webmonitor.JobDetails;
-import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
-import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
-
-import org.apache.flink.runtime.webmonitor.handlers.JobExceptionsHandler;
-import org.apache.flink.util.ExceptionUtils;
-import org.codehaus.jettison.json.JSONException;
-import org.codehaus.jettison.json.JSONObject;
-
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class implements the utility methods that convert the responses into JSON strings.
- */
-public class JsonFactory {
-
- private static final com.fasterxml.jackson.core.JsonFactory jacksonFactory =
- new com.fasterxml.jackson.core.JsonFactory();
-
-
- public static String generateConfigJSON(long refreshInterval, long timeZoneOffset, String timeZoneName) {
- try {
- StringWriter writer = new StringWriter();
- JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
-
- gen.writeStartObject();
- gen.writeNumberField("refresh-interval", refreshInterval);
- gen.writeNumberField("timezone-offset", timeZoneOffset);
- gen.writeStringField("timezone-name", timeZoneName);
- gen.writeEndObject();
-
- gen.close();
- return writer.toString();
- }
- catch (Exception e) {
- // this should not happen
- throw new RuntimeException(e.getMessage(), e);
- }
- }
-
- public static String generateOverviewJSON(StatusOverview overview) {
- try {
- StringWriter writer = new StringWriter();
- JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
-
- gen.writeStartObject();
- gen.writeNumberField("taskmanagers", overview.getNumTaskManagersConnected());
- gen.writeNumberField("slots-total", overview.getNumSlotsTotal());
- gen.writeNumberField("slots-available", overview.getNumSlotsAvailable());
- gen.writeNumberField("jobs-running", overview.getNumJobsRunningOrPending());
- gen.writeNumberField("jobs-finished", overview.getNumJobsFinished());
- gen.writeNumberField("jobs-cancelled", overview.getNumJobsCancelled());
- gen.writeNumberField("jobs-failed", overview.getNumJobsFailed());
- gen.writeEndObject();
-
- gen.close();
- return writer.toString();
- }
- catch (Exception e) {
- // this should not happen
- throw new RuntimeException(e.getMessage(), e);
- }
- }
-
- public static String generateJobDetailsJSON(JobDetails job) {
- try {
- StringWriter writer = new StringWriter();
- JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
-
- generateSingleJobDetails(job, gen);
-
- gen.close();
- return writer.toString();
- }
- catch (Exception e) {
- // this should not happen
- throw new RuntimeException(e.getMessage(), e);
- }
- }
-
- public static String generateMultipleJobsDetailsJSON(JobDetails[] jobs) {
- try {
- StringWriter writer = new StringWriter();
- JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
-
- gen.writeStartObject();
- gen.writeArrayFieldStart("jobs");
- for (JobDetails detail : jobs) {
- generateSingleJobDetails(detail, gen);
- }
- gen.writeEndArray();
- gen.writeEndObject();
-
- gen.close();
- return writer.toString();
- }
- catch (Exception e) {
- // this should not happen
- throw new RuntimeException(e.getMessage(), e);
- }
- }
-
- public static String generateRunningAndFinishedJobDetailsJSON(JobDetails[] runningJobs, JobDetails[] finishedJobs) {
- try {
- StringWriter writer = new StringWriter();
- JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
-
- gen.writeStartObject();
-
- gen.writeArrayFieldStart("running");
- for (JobDetails detail : runningJobs) {
- generateSingleJobDetails(detail, gen);
- }
- gen.writeEndArray();
-
- gen.writeArrayFieldStart("finished");
- for (JobDetails detail : finishedJobs) {
- generateSingleJobDetails(detail, gen);
- }
- gen.writeEndArray();
-
- gen.writeEndObject();
-
- gen.close();
- return writer.toString();
- }
- catch (Exception e) {
- // this should not happen
- throw new RuntimeException(e.getMessage(), e);
- }
- }
-
- private static void generateSingleJobDetails(JobDetails details, JsonGenerator gen) throws Exception {
- gen.writeStartObject();
-
- gen.writeStringField("jid", details.getJobId().toString());
- gen.writeStringField("name", details.getJobName());
- gen.writeStringField("state", details.getStatus().name());
- gen.writeNumberField("start-time", details.getStartTime());
- gen.writeNumberField("end-time", details.getEndTime());
- gen.writeNumberField("last-modification", details.getLastUpdateTime());
-
- gen.writeObjectFieldStart("tasks");
- gen.writeNumberField("total", details.getNumTasks());
-
- final int[] perState = details.getNumVerticesPerExecutionState();
- gen.writeNumberField("pending", perState[ExecutionState.CREATED.ordinal()] +
- perState[ExecutionState.SCHEDULED.ordinal()] +
- perState[ExecutionState.DEPLOYING.ordinal()]);
- gen.writeNumberField("running", perState[ExecutionState.RUNNING.ordinal()]);
- gen.writeNumberField("finished", perState[ExecutionState.FINISHED.ordinal()]);
- gen.writeNumberField("canceling", perState[ExecutionState.CANCELING.ordinal()]);
- gen.writeNumberField("canceled", perState[ExecutionState.CANCELED.ordinal()]);
- gen.writeNumberField("failed", perState[ExecutionState.FAILED.ordinal()]);
- gen.writeEndObject();
-
- gen.writeEndObject();
- }
-
- public static String generateJobsOverviewJSON(JobsWithIDsOverview overview) {
- try {
- List<JobID> runningIDs = overview.getJobsRunningOrPending();
- List<String> runningStrings = new ArrayList<String>(runningIDs.size());
- for (JobID jid : runningIDs) {
- runningStrings.add(jid.toString());
- }
-
- List<JobID> finishedIDs = overview.getJobsFinished();
- List<String> finishedStrings = new ArrayList<String>(finishedIDs.size());
- for (JobID jid : finishedIDs) {
- finishedStrings.add(jid.toString());
- }
-
- List<JobID> canceledIDs = overview.getJobsCancelled();
- List<String> canceledStrings = new ArrayList<String>(canceledIDs.size());
- for (JobID jid : canceledIDs) {
- canceledStrings.add(jid.toString());
- }
-
- List<JobID> failedIDs = overview.getJobsFailed();
- List<String> failedStrings = new ArrayList<String>(failedIDs.size());
- for (JobID jid : failedIDs) {
- failedStrings.add(jid.toString());
- }
-
- JSONObject response = new JSONObject();
- response.put("jobs-running", runningStrings);
- response.put("jobs-finished", finishedStrings);
- response.put("jobs-cancelled", canceledStrings);
- response.put("jobs-failed", failedStrings);
- return response.toString(2);
- }
- catch (JSONException e) {
- // this should not happen
- throw new RuntimeException(e);
- }
- }
-
- public static String generateExceptionsJson(Throwable rootCause,
- List<JobExceptionsHandler.ExceptionWithContext> allExceptions,
- boolean truncated) {
- try {
- StringWriter writer = new StringWriter();
- JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
-
- gen.writeStartObject();
- gen.writeStringField("root-exception", ExceptionUtils.stringifyException(rootCause));
-
- if (allExceptions != null && !allExceptions.isEmpty()) {
- gen.writeArrayFieldStart("all-exceptions");
- for (JobExceptionsHandler.ExceptionWithContext ewc : allExceptions) {
- gen.writeStartObject();
- gen.writeStringField("exception", ExceptionUtils.stringifyException(ewc.getException()));
- gen.writeStringField("task", ewc.getTaskName());
- gen.writeStringField("location", ewc.getLocation());
- gen.writeEndObject();
- }
- gen.writeEndArray();
-
- gen.writeBooleanField("truncated", truncated);
- }
-
- gen.writeEndObject();
-
- gen.close();
- return writer.toString();
- }
- catch (Exception e) {
- // this should not happen
- throw new RuntimeException(e.getMessage(), e);
- }
- }
-
- // --------------------------------------------------------------------------------------------
-
- /** Don't instantiate */
- private JsonFactory() {}
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
index 51df17c..1c1b723 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
@@ -35,16 +35,16 @@ import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.IllegalConfigurationException;
import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.webmonitor.files.StaticFileServerHandler;
-import org.apache.flink.runtime.webmonitor.handlers.ExecutionPlanHandler;
+import org.apache.flink.runtime.webmonitor.handlers.JobPlanHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobConfigHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobExceptionsHandler;
-import org.apache.flink.runtime.webmonitor.handlers.JobSummaryHandler;
-import org.apache.flink.runtime.webmonitor.handlers.JobVerticesOverviewHandler;
-import org.apache.flink.runtime.webmonitor.handlers.JobsOverviewHandler;
-import org.apache.flink.runtime.webmonitor.handlers.RequestConfigHandler;
+import org.apache.flink.runtime.webmonitor.handlers.JobDetailsHandler;
+import org.apache.flink.runtime.webmonitor.handlers.CurrentJobsOverviewHandler;
+import org.apache.flink.runtime.webmonitor.handlers.DashboardConfigHandler;
import org.apache.flink.runtime.webmonitor.handlers.RequestHandler;
-import org.apache.flink.runtime.webmonitor.handlers.RequestJobIdsHandler;
-import org.apache.flink.runtime.webmonitor.handlers.RequestOverviewHandler;
+import org.apache.flink.runtime.webmonitor.handlers.CurrentJobIdsHandler;
+import org.apache.flink.runtime.webmonitor.handlers.ClusterOverviewHandler;
+import org.apache.flink.runtime.webmonitor.handlers.SubtasksTimesHandler;
import org.apache.flink.runtime.webmonitor.legacy.JobManagerInfoHandler;
import org.slf4j.Logger;
@@ -124,28 +124,30 @@ public class WebRuntimeMonitor implements WebMonitor {
router = new Router()
// config how to interact with this web server
- .GET("/config", handler(new RequestConfigHandler(cfg.getRefreshInterval())))
-
+ .GET("/config", handler(new DashboardConfigHandler(cfg.getRefreshInterval())))
+
// the overview - how many task managers, slots, free slots, ...
- .GET("/overview", handler(new RequestOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
+ .GET("/overview", handler(new ClusterOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
// overview over jobs
- .GET("/joboverview", handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, true)))
- .GET("/joboverview/running", handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, false)))
- .GET("/joboverview/completed", handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, false, true)))
-
- .GET("/jobs", handler(new RequestJobIdsHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
- .GET("/jobs/:jobid", handler(new JobSummaryHandler(currentGraphs)))
- .GET("/jobs/:jobid/vertices", handler(new JobVerticesOverviewHandler(currentGraphs)))
- .GET("/jobs/:jobid/plan", handler(new ExecutionPlanHandler(currentGraphs)))
+ .GET("/joboverview", handler(new CurrentJobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, true)))
+ .GET("/joboverview/running", handler(new CurrentJobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, false)))
+ .GET("/joboverview/completed", handler(new CurrentJobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, false, true)))
+
+ .GET("/jobs", handler(new CurrentJobIdsHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
+
+ .GET("/jobs/:jobid", handler(new JobDetailsHandler(currentGraphs)))
+ .GET("/jobs/:jobid/vertices", handler(new JobDetailsHandler(currentGraphs)))
+
+ .GET("/jobs/:jobid/vertices/:vertexid/subtasktimes", handler(new SubtasksTimesHandler(currentGraphs)))
+
+ .GET("/jobs/:jobid/plan", handler(new JobPlanHandler(currentGraphs)))
.GET("/jobs/:jobid/config", handler(new JobConfigHandler(currentGraphs)))
.GET("/jobs/:jobid/exceptions", handler(new JobExceptionsHandler(currentGraphs)))
-// .GET("/running/:jobid/:jobvertex", handler(new ExecutionPlanHandler(currentGraphs)))
-
// the handler for the legacy requests
- .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
-
+ .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
+
// this handler serves all the static contents
.GET("/:*", new StaticFileServerHandler(webRootDir));
}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java
index 44ca2d4..01f48e4 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java
@@ -27,7 +27,7 @@ import java.util.Map;
/**
* Base class for request handlers whose response depends on an ExecutionGraph
- * that can be retrieved via {@link Parameters#JOB_ID} parameter.
+ * that can be retrieved via "jobid" parameter.
*/
public abstract class AbstractExecutionGraphRequestHandler implements RequestHandler {
@@ -41,7 +41,7 @@ public abstract class AbstractExecutionGraphRequestHandler implements RequestHan
@Override
public String handleRequest(Map<String, String> params) throws Exception {
- String jidString = params.get(Parameters.JOB_ID);
+ String jidString = params.get("jobid");
if (jidString == null) {
throw new RuntimeException("JobId parameter missing");
}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ClusterOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ClusterOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ClusterOverviewHandler.java
new file mode 100644
index 0000000..2d8b217
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ClusterOverviewHandler.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.flink.runtime.instance.ActorGateway;
+import org.apache.flink.runtime.messages.webmonitor.RequestStatusOverview;
+import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
+
+import scala.concurrent.Await;
+import scala.concurrent.Future;
+import scala.concurrent.duration.FiniteDuration;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Responder that returns the status of the Flink cluster, such as how many
+ * TaskManagers are currently connected, and how many jobs are running.
+ */
+public class ClusterOverviewHandler implements RequestHandler, RequestHandler.JsonResponse {
+
+ private final ActorGateway jobManager;
+
+ private final FiniteDuration timeout;
+
+
+ public ClusterOverviewHandler(ActorGateway jobManager, FiniteDuration timeout) {
+ if (jobManager == null || timeout == null) {
+ throw new NullPointerException();
+ }
+ this.jobManager = jobManager;
+ this.timeout = timeout;
+ }
+
+ @Override
+ public String handleRequest(Map<String, String> params) throws Exception {
+ try {
+ Future<Object> future = jobManager.ask(RequestStatusOverview.getInstance(), timeout);
+ StatusOverview overview = (StatusOverview) Await.result(future, timeout);
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+ gen.writeNumberField("taskmanagers", overview.getNumTaskManagersConnected());
+ gen.writeNumberField("slots-total", overview.getNumSlotsTotal());
+ gen.writeNumberField("slots-available", overview.getNumSlotsAvailable());
+ gen.writeNumberField("jobs-running", overview.getNumJobsRunningOrPending());
+ gen.writeNumberField("jobs-finished", overview.getNumJobsFinished());
+ gen.writeNumberField("jobs-cancelled", overview.getNumJobsCancelled());
+ gen.writeNumberField("jobs-failed", overview.getNumJobsFailed());
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+ catch (Exception e) {
+ throw new Exception("Failed to fetch the status overview: " + e.getMessage(), e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobIdsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobIdsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobIdsHandler.java
new file mode 100644
index 0000000..e7e96e2
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobIdsHandler.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.flink.api.common.JobID;
+import org.apache.flink.runtime.instance.ActorGateway;
+import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
+import org.apache.flink.runtime.messages.webmonitor.RequestJobsWithIDsOverview;
+
+import scala.concurrent.Await;
+import scala.concurrent.Future;
+import scala.concurrent.duration.FiniteDuration;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Responder that returns with a list of all JobIDs of jobs found at the target actor.
+ * May serve the IDs of current jobs, or past jobs, depending on whether this handler is
+ * given the JobManager or Archive Actor Reference.
+ */
+public class CurrentJobIdsHandler implements RequestHandler, RequestHandler.JsonResponse {
+
+ private final ActorGateway target;
+
+ private final FiniteDuration timeout;
+
+
+ public CurrentJobIdsHandler(ActorGateway target, FiniteDuration timeout) {
+ if (target == null || timeout == null) {
+ throw new NullPointerException();
+ }
+ this.target = target;
+ this.timeout = timeout;
+ }
+
+ @Override
+ public String handleRequest(Map<String, String> params) throws Exception {
+ // we need no parameters, get all requests
+ try {
+ Future<Object> future = target.ask(RequestJobsWithIDsOverview.getInstance(), timeout);
+ JobsWithIDsOverview overview = (JobsWithIDsOverview) Await.result(future, timeout);
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+
+ gen.writeArrayFieldStart("jobs-running");
+ for (JobID jid : overview.getJobsRunningOrPending()) {
+ gen.writeString(jid.toString());
+ }
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("jobs-finished");
+ for (JobID jid : overview.getJobsFinished()) {
+ gen.writeString(jid.toString());
+ }
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("jobs-cancelled");
+ for (JobID jid : overview.getJobsCancelled()) {
+ gen.writeString(jid.toString());
+ }
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("jobs-failed");
+ for (JobID jid : overview.getJobsFailed()) {
+ gen.writeString(jid.toString());
+ }
+ gen.writeEndArray();
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Failed to fetch list of all running jobs: " + e.getMessage(), e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
new file mode 100644
index 0000000..d792ff4
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.flink.runtime.execution.ExecutionState;
+import org.apache.flink.runtime.instance.ActorGateway;
+import org.apache.flink.runtime.messages.webmonitor.JobDetails;
+import org.apache.flink.runtime.messages.webmonitor.MultipleJobsDetails;
+import org.apache.flink.runtime.messages.webmonitor.RequestJobDetails;
+
+import scala.concurrent.Await;
+import scala.concurrent.Future;
+import scala.concurrent.duration.FiniteDuration;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Request handler that returns a summary of the job status.
+ */
+public class CurrentJobsOverviewHandler implements RequestHandler, RequestHandler.JsonResponse {
+
+ private final ActorGateway jobManager;
+
+ private final FiniteDuration timeout;
+
+ private final boolean includeRunningJobs;
+ private final boolean includeFinishedJobs;
+
+
+ public CurrentJobsOverviewHandler(ActorGateway jobManager, FiniteDuration timeout,
+ boolean includeRunningJobs, boolean includeFinishedJobs) {
+ this.jobManager = jobManager;
+ this.timeout = timeout;
+ this.includeRunningJobs = includeRunningJobs;
+ this.includeFinishedJobs = includeFinishedJobs;
+ }
+
+ @Override
+ public String handleRequest(Map<String, String> params) throws Exception {
+ try {
+ Future<Object> future = jobManager.ask(
+ new RequestJobDetails(includeRunningJobs, includeFinishedJobs), timeout);
+
+ MultipleJobsDetails result = (MultipleJobsDetails) Await.result(future, timeout);
+
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+ gen.writeStartObject();
+
+
+ if (includeRunningJobs && includeFinishedJobs) {
+ gen.writeArrayFieldStart("running");
+ for (JobDetails detail : result.getRunningJobs()) {
+ generateSingleJobDetails(detail, gen);
+ }
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("finished");
+ for (JobDetails detail : result.getFinishedJobs()) {
+ generateSingleJobDetails(detail, gen);
+ }
+ gen.writeEndArray();
+ }
+ else {
+ gen.writeArrayFieldStart("jobs");
+ for (JobDetails detail : includeRunningJobs ? result.getRunningJobs() : result.getFinishedJobs()) {
+ generateSingleJobDetails(detail, gen);
+ }
+ gen.writeEndArray();
+ }
+
+ gen.writeEndObject();
+ gen.close();
+ return writer.toString();
+ }
+ catch (Exception e) {
+ throw new Exception("Failed to fetch the status overview: " + e.getMessage(), e);
+ }
+ }
+
+ private static void generateSingleJobDetails(JobDetails details, JsonGenerator gen) throws Exception {
+ gen.writeStartObject();
+
+ gen.writeStringField("jid", details.getJobId().toString());
+ gen.writeStringField("name", details.getJobName());
+ gen.writeStringField("state", details.getStatus().name());
+
+ gen.writeNumberField("start-time", details.getStartTime());
+ gen.writeNumberField("end-time", details.getEndTime());
+ gen.writeNumberField("duration", details.getEndTime() <= 0 ? -1L : details.getEndTime() - details.getStartTime());
+ gen.writeNumberField("last-modification", details.getLastUpdateTime());
+
+ gen.writeObjectFieldStart("tasks");
+ gen.writeNumberField("total", details.getNumTasks());
+
+ final int[] perState = details.getNumVerticesPerExecutionState();
+ gen.writeNumberField("pending", perState[ExecutionState.CREATED.ordinal()] +
+ perState[ExecutionState.SCHEDULED.ordinal()] +
+ perState[ExecutionState.DEPLOYING.ordinal()]);
+ gen.writeNumberField("running", perState[ExecutionState.RUNNING.ordinal()]);
+ gen.writeNumberField("finished", perState[ExecutionState.FINISHED.ordinal()]);
+ gen.writeNumberField("canceling", perState[ExecutionState.CANCELING.ordinal()]);
+ gen.writeNumberField("canceled", perState[ExecutionState.CANCELED.ordinal()]);
+ gen.writeNumberField("failed", perState[ExecutionState.FAILED.ordinal()]);
+ gen.writeEndObject();
+
+ gen.writeEndObject();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/DashboardConfigHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/DashboardConfigHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/DashboardConfigHandler.java
new file mode 100644
index 0000000..c21c35d
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/DashboardConfigHandler.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import java.io.StringWriter;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * Responder that returns the parameters that define how the asynchronous requests
+ * against this web server should behave. It defines for example the refresh interval,
+ * and time zone of the server timestamps.
+ */
+public class DashboardConfigHandler implements RequestHandler, RequestHandler.JsonResponse {
+
+ private final String configString;
+
+ public DashboardConfigHandler(long refreshInterval) {
+ TimeZone timeZome = TimeZone.getDefault();
+ String timeZoneName = timeZome.getDisplayName();
+ long timeZoneOffset= timeZome.getRawOffset();
+
+ try {
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+ gen.writeNumberField("refresh-interval", refreshInterval);
+ gen.writeNumberField("timezone-offset", timeZoneOffset);
+ gen.writeStringField("timezone-name", timeZoneName);
+ gen.writeEndObject();
+
+ gen.close();
+ this.configString = writer.toString();
+ }
+ catch (Exception e) {
+ // should never happen
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public String handleRequest(Map<String, String> params) {
+ return this.configString;
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ExecutionPlanHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ExecutionPlanHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ExecutionPlanHandler.java
deleted file mode 100644
index fd0a731..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/ExecutionPlanHandler.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-import org.apache.flink.runtime.executiongraph.ExecutionGraph;
-import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
-
-import java.util.Map;
-
-/**
- * Request handler that returns the JSON program plan of a job graph.
- */
-public class ExecutionPlanHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
-
-
- public ExecutionPlanHandler(ExecutionGraphHolder executionGraphHolder) {
- super(executionGraphHolder);
- }
-
- @Override
- public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
- return graph.getJsonPlan();
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobConfigHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobConfigHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobConfigHandler.java
index 50cfd96..a6c16ff 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobConfigHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobConfigHandler.java
@@ -18,22 +18,19 @@
package org.apache.flink.runtime.webmonitor.handlers;
+import java.io.StringWriter;
import java.util.Map;
+import com.fasterxml.jackson.core.JsonGenerator;
import org.apache.flink.api.common.ExecutionConfig;
import org.apache.flink.runtime.executiongraph.ExecutionGraph;
import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
-import org.codehaus.jettison.json.JSONObject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
- * Request handler that returns the configuration of a job.
+ * Request handler that returns the execution config of a job.
*/
public class JobConfigHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
- private static final Logger LOG = LoggerFactory.getLogger(JobConfigHandler.class);
-
public JobConfigHandler(ExecutionGraphHolder executionGraphHolder) {
super(executionGraphHolder);
}
@@ -41,41 +38,41 @@ public class JobConfigHandler extends AbstractExecutionGraphRequestHandler imple
@Override
public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
- JSONObject obj = new JSONObject();
-
- obj.put("jid", graph.getJobID().toString());
- obj.put("name", graph.getJobName());
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
- JSONObject execConfig = new JSONObject();
+ gen.writeStartObject();
+ gen.writeStringField("jid", graph.getJobID().toString());
+ gen.writeStringField("name", graph.getJobName());
ExecutionConfig ec = graph.getExecutionConfig();
if (ec != null) {
- execConfig.put("execution-mode", ec.getExecutionMode());
- execConfig.put("max-execution-retries", ec.getNumberOfExecutionRetries());
- execConfig.put("job-parallelism", ec.getParallelism());
- execConfig.put("object-reuse-mode", ec.isObjectReuseEnabled());
+ gen.writeObjectFieldStart("execution-config");
+
+ gen.writeStringField("execution-mode", ec.getExecutionMode().name());
+ gen.writeNumberField("max-execution-retries", ec.getNumberOfExecutionRetries());
+ gen.writeNumberField("job-parallelism", ec.getParallelism());
+ gen.writeBooleanField("object-reuse-mode", ec.isObjectReuseEnabled());
ExecutionConfig.GlobalJobParameters uc = ec.getGlobalJobParameters();
if (uc != null) {
Map<String, String> ucVals = uc.toMap();
if (ucVals != null) {
- JSONObject userConfig = new JSONObject();
+ gen.writeObjectFieldStart("user-config");
+
for (Map.Entry<String, String> ucVal : ucVals.entrySet()) {
- userConfig.put(ucVal.getKey(), ucVal.getValue());
+ gen.writeStringField(ucVal.getKey(), ucVal.getValue());
}
- execConfig.put("user-config", userConfig);
- } else {
- LOG.debug("GlobalJobParameters.toMap() did not return anything");
+
+ gen.writeEndObject();
}
- } else {
- LOG.debug("No GlobalJobParameters were set in the execution config");
}
- } else {
- LOG.warn("Unable to retrieve execution config from execution graph");
+
+ gen.writeEndObject();
}
-
- obj.put("execution-config", execConfig);
-
- return obj.toString();
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
}
}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobDetailsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobDetailsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobDetailsHandler.java
new file mode 100644
index 0000000..feca11b
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobDetailsHandler.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import org.apache.flink.api.common.accumulators.Accumulator;
+import org.apache.flink.api.common.accumulators.LongCounter;
+import org.apache.flink.runtime.accumulators.AccumulatorRegistry;
+import org.apache.flink.runtime.execution.ExecutionState;
+import org.apache.flink.runtime.executiongraph.ExecutionGraph;
+import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
+import org.apache.flink.runtime.executiongraph.ExecutionVertex;
+import org.apache.flink.runtime.jobgraph.JobStatus;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Request handler that returns the JSON program plan of a job graph.
+ */
+public class JobDetailsHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+
+ public JobDetailsHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
+ final StringWriter writer = new StringWriter();
+ final JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ final long now = System.currentTimeMillis();
+
+ gen.writeStartObject();
+
+ // basic info
+ gen.writeStringField("jid", graph.getJobID().toString());
+ gen.writeStringField("name", graph.getJobName());
+ gen.writeStringField("state", graph.getState().name());
+
+ // times and duration
+ final long jobStartTime = graph.getStatusTimestamp(JobStatus.CREATED);
+ final long jobEndTime = graph.getState().isTerminalState() ?
+ graph.getStatusTimestamp(graph.getState()) : -1L;
+ gen.writeNumberField("start-time", jobStartTime);
+ gen.writeNumberField("end-time", jobEndTime);
+ gen.writeNumberField("duration", (jobEndTime > 0 ? jobEndTime : now) - jobStartTime);
+
+ // timestamps
+ gen.writeObjectFieldStart("timestamps");
+ for (JobStatus status : JobStatus.values()) {
+ gen.writeNumberField(status.name(), graph.getStatusTimestamp(status));
+ }
+ gen.writeEndObject();
+
+ final int[] tasksPerStatusTotal = new int[ExecutionState.values().length];
+
+ // job vertices
+ gen.writeArrayFieldStart("vertices");
+
+ for (ExecutionJobVertex ejv : graph.getVerticesTopologically()) {
+ int[] tasksPerState = new int[ExecutionState.values().length];
+ long startTime = Long.MAX_VALUE;
+ long endTime = 0;
+ boolean allFinished = true;
+
+ for (ExecutionVertex vertex : ejv.getTaskVertices()) {
+ final ExecutionState state = vertex.getExecutionState();
+ tasksPerState[state.ordinal()]++;
+ tasksPerStatusTotal[state.ordinal()]++;
+
+ // take the earliest start time
+ long started = vertex.getStateTimestamp(ExecutionState.DEPLOYING);
+ if (started > 0) {
+ startTime = Math.min(startTime, started);
+ }
+
+ allFinished &= state.isTerminal();
+ endTime = Math.max(endTime, vertex.getStateTimestamp(state));
+ }
+
+ long duration;
+ if (startTime < Long.MAX_VALUE) {
+ if (allFinished) {
+ duration = endTime - startTime;
+ }
+ else {
+ endTime = -1L;
+ duration = now - startTime;
+ }
+ }
+ else {
+ startTime = -1L;
+ endTime = -1L;
+ duration = -1L;
+ }
+
+ Map<AccumulatorRegistry.Metric, Accumulator<?, ?>> metrics = ejv.getAggregatedMetricAccumulators();
+
+ LongCounter readBytes = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_BYTES_IN);
+ LongCounter writeBytes = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_BYTES_OUT);
+ LongCounter readRecords = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_RECORDS_IN);
+ LongCounter writeRecords = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_RECORDS_OUT);
+
+ gen.writeStartObject();
+ gen.writeStringField("id", ejv.getJobVertexId().toString());
+ gen.writeStringField("name", ejv.getJobVertex().getName());
+ gen.writeNumberField("parallelism", ejv.getParallelism());
+
+ gen.writeNumberField("start-time", startTime);
+ gen.writeNumberField("end-time", endTime);
+ gen.writeNumberField("duration", duration);
+
+ gen.writeObjectFieldStart("tasks");
+ for (ExecutionState state : ExecutionState.values()) {
+ gen.writeNumberField(state.name(), tasksPerState[state.ordinal()]);
+ }
+ gen.writeEndObject();
+
+ gen.writeObjectFieldStart("metrics");
+ gen.writeNumberField("read-bytes", readBytes != null ? readBytes.getLocalValuePrimitive() : -1L);
+ gen.writeNumberField("write-bytes", writeBytes != null ? writeBytes.getLocalValuePrimitive() : -1L);
+ gen.writeNumberField("read-records", readRecords != null ? readRecords.getLocalValuePrimitive() : -1L);
+ gen.writeNumberField("write-records",writeRecords != null ? writeRecords.getLocalValuePrimitive() : -1L);
+ gen.writeEndObject();
+
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobExceptionsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobExceptionsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobExceptionsHandler.java
index 0f8cba7..8e01465 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobExceptionsHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobExceptionsHandler.java
@@ -18,14 +18,14 @@
package org.apache.flink.runtime.webmonitor.handlers;
+import com.fasterxml.jackson.core.JsonGenerator;
import org.apache.flink.runtime.executiongraph.ExecutionGraph;
import org.apache.flink.runtime.executiongraph.ExecutionVertex;
import org.apache.flink.runtime.instance.InstanceConnectionInfo;
import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
-import org.apache.flink.runtime.webmonitor.JsonFactory;
+import org.apache.flink.util.ExceptionUtils;
-import java.util.ArrayList;
-import java.util.List;
+import java.io.StringWriter;
import java.util.Map;
/**
@@ -41,67 +41,48 @@ public class JobExceptionsHandler extends AbstractExecutionGraphRequestHandler i
@Override
public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+
// most important is the root failure cause
Throwable rootException = graph.getFailureCause();
+ if (rootException != null) {
+ gen.writeStringField("root-exception", ExceptionUtils.stringifyException(rootException));
+ }
// we additionally collect all exceptions (up to a limit) that occurred in the individual tasks
- List<ExceptionWithContext> localExceptions = new ArrayList<>();
+ gen.writeArrayFieldStart("all-exceptions");
+
+ int numExceptionsSoFar = 0;
boolean truncated = false;
for (ExecutionVertex task : graph.getAllExecutionVertices()) {
Throwable t = task.getFailureCause();
if (t != null) {
- if (localExceptions.size() >= MAX_NUMBER_EXCEPTION_TO_REPORT) {
+ if (numExceptionsSoFar >= MAX_NUMBER_EXCEPTION_TO_REPORT) {
truncated = true;
break;
}
InstanceConnectionInfo location = task.getCurrentAssignedResourceLocation();
String locationString = location != null ?
- location.getFQDNHostname() + ':' + location.dataPort() :
- "(unassigned)";
-
- localExceptions.add(new ExceptionWithContext(t, task.getTaskName(), locationString));
- }
- }
-
- // if only one exception occurred in a task, and that is the root exception,
- // there is no need to display it twice
- if (localExceptions.size() == 1 && localExceptions.get(0).getException() == rootException) {
- localExceptions = null;
- }
-
- return JsonFactory.generateExceptionsJson(rootException, localExceptions, truncated);
- }
-
- // ------------------------------------------------------------------------
+ location.getFQDNHostname() + ':' + location.dataPort() : "(unassigned)";
- /**
- * Class that encapsulated an exception, together with the name of the throwing task, and
- * the instance on which the exception occurred.
- */
- public static class ExceptionWithContext {
-
- private final Throwable exception;
- private final String taskName;
- private final String location;
-
- public ExceptionWithContext(Throwable exception, String taskName, String location) {
- this.exception = exception;
- this.taskName = taskName;
- this.location = location;
- }
-
- public Throwable getException() {
- return exception;
+ gen.writeStartObject();
+ gen.writeStringField("exception", ExceptionUtils.stringifyException(t));
+ gen.writeStringField("task", task.getSimpleName());
+ gen.writeStringField("location", locationString);
+ gen.writeEndObject();
+ }
}
+ gen.writeEndArray();
- public String getTaskName() {
- return taskName;
- }
+ gen.writeBooleanField("truncated", truncated);
+ gen.writeEndObject();
- public String getLocation() {
- return location;
- }
+ gen.close();
+ return writer.toString();
}
}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobPlanHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobPlanHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobPlanHandler.java
new file mode 100644
index 0000000..99838b3
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobPlanHandler.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import org.apache.flink.runtime.executiongraph.ExecutionGraph;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.util.Map;
+
+/**
+ * Request handler that returns the JSON program plan of a job graph.
+ */
+public class JobPlanHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+
+
+ public JobPlanHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
+ return graph.getJsonPlan();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobSummaryHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobSummaryHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobSummaryHandler.java
deleted file mode 100644
index a682fd0..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobSummaryHandler.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-import org.apache.flink.runtime.executiongraph.ExecutionGraph;
-import org.apache.flink.runtime.messages.webmonitor.JobDetails;
-import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
-import org.apache.flink.runtime.webmonitor.JsonFactory;
-import org.apache.flink.runtime.webmonitor.WebMonitorUtils;
-
-import java.util.Map;
-
-/**
- * Request handler that returns a summary of the job status.
- */
-public class JobSummaryHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
-
- public JobSummaryHandler(ExecutionGraphHolder executionGraphHolder) {
- super(executionGraphHolder);
- }
-
- @Override
- public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
- JobDetails details = WebMonitorUtils.createDetailsForJob(graph);
- return JsonFactory.generateJobDetailsJSON(details);
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
deleted file mode 100644
index 17c31a5..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-import org.apache.flink.runtime.executiongraph.ExecutionGraph;
-import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
-
-import java.util.Map;
-
-/**
- * Request handler that returns the JSON program plan of a job graph.
- */
-public class JobVertexDetailsHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
-
-
- public JobVertexDetailsHandler(ExecutionGraphHolder executionGraphHolder) {
- super(executionGraphHolder);
- }
-
- @Override
- public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
- String vertexId = params.get(Parameters.JOB_VERTEX_ID);
-
- return "vertex: " + vertexId;
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVerticesOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVerticesOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVerticesOverviewHandler.java
deleted file mode 100644
index 6a0da66..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVerticesOverviewHandler.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-import org.apache.flink.runtime.execution.ExecutionState;
-import org.apache.flink.runtime.executiongraph.Execution;
-import org.apache.flink.runtime.executiongraph.ExecutionGraph;
-import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
-import org.apache.flink.runtime.executiongraph.ExecutionVertex;
-import org.apache.flink.runtime.jobgraph.JobVertex;
-import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
-import org.codehaus.jettison.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Request handler that returns the JSON program plan of a job graph.
- */
-public class JobVerticesOverviewHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
-
-
- public JobVerticesOverviewHandler(ExecutionGraphHolder executionGraphHolder) {
- super(executionGraphHolder);
- }
-
- @Override
- public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
-
- JSONObject obj = new JSONObject();
-
- obj.put("jid", graph.getJobID().toString());
- obj.put("name", graph.getJobName());
-
- List<JSONObject> vertexJSONs = new ArrayList<JSONObject>();
-
- for (ExecutionJobVertex vertex : graph.getVerticesTopologically()) {
- JSONObject vertexJSON = new JSONObject();
- vertexJSONs.add(vertexJSON);
-
- // identifying parameters
- JobVertex jobVertex = vertex.getJobVertex();
- vertexJSON.put("id", jobVertex.getID().toString());
- vertexJSON.put("name", jobVertex.getName());
-
- // time
- vertexJSON.put("start-time", System.currentTimeMillis() - 10000);
- vertexJSON.put("end-time", System.currentTimeMillis() - 6453);
-
- // read / write
- vertexJSON.put("bytes-read-local", 14355376592L);
- vertexJSON.put("bytes-read-remote", 607623465782L);
- vertexJSON.put("bytes-written", 5372934L);
- vertexJSON.put("records-read", 4659765L);
- vertexJSON.put("records-written", 4659765L);
-
- vertexJSON.put("parallelism", vertex.getParallelism());
-
- JSONObject states = new JSONObject();
- {
- // count the occurrence of each state
- int[] statesCount = new int[ExecutionState.values().length];
- for (ExecutionVertex ev: vertex.getTaskVertices()) {
- Execution ee = ev.getCurrentExecutionAttempt();
- if (ee != null) {
- statesCount[ee.getState().ordinal()]++;
- }
- }
-
- int i = 0;
- for (ExecutionState state : ExecutionState.values()) {
- states.put(state.name(), statesCount[i++]);
- }
- }
- vertexJSON.put("states", states);
- }
-
- obj.put("vertices", vertexJSONs);
-
- return obj.toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobsOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobsOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobsOverviewHandler.java
deleted file mode 100644
index 304c521..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobsOverviewHandler.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-import org.apache.flink.runtime.instance.ActorGateway;
-import org.apache.flink.runtime.messages.webmonitor.MultipleJobsDetails;
-import org.apache.flink.runtime.messages.webmonitor.RequestJobDetails;
-import org.apache.flink.runtime.webmonitor.JsonFactory;
-
-import scala.concurrent.Await;
-import scala.concurrent.Future;
-import scala.concurrent.duration.FiniteDuration;
-
-import java.util.Map;
-
-/**
- * Request handler that returns a summary of the job status.
- */
-public class JobsOverviewHandler implements RequestHandler, RequestHandler.JsonResponse {
-
- private final ActorGateway jobManager;
-
- private final FiniteDuration timeout;
-
- private final boolean includeRunningJobs;
- private final boolean includeFinishedJobs;
-
-
- public JobsOverviewHandler(ActorGateway jobManager, FiniteDuration timeout,
- boolean includeRunningJobs, boolean includeFinishedJobs) {
- this.jobManager = jobManager;
- this.timeout = timeout;
- this.includeRunningJobs = includeRunningJobs;
- this.includeFinishedJobs = includeFinishedJobs;
- }
-
- @Override
- public String handleRequest(Map<String, String> params) throws Exception {
- try {
- Future<Object> future = jobManager.ask(
- new RequestJobDetails(includeRunningJobs, includeFinishedJobs), timeout);
-
- MultipleJobsDetails result = (MultipleJobsDetails) Await.result(future, timeout);
-
- if (includeRunningJobs && includeFinishedJobs) {
- return JsonFactory.generateRunningAndFinishedJobDetailsJSON(
- result.getRunningJobs(), result.getFinishedJobs());
- }
- else {
- return JsonFactory.generateMultipleJobsDetailsJSON(
- includeRunningJobs ? result.getRunningJobs() : result.getFinishedJobs());
- }
- }
- catch (Exception e) {
- throw new Exception("Failed to fetch the status overview: " + e.getMessage(), e);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JsonFactory.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JsonFactory.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JsonFactory.java
new file mode 100644
index 0000000..cb83361
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JsonFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+
+public class JsonFactory {
+
+ public static final com.fasterxml.jackson.core.JsonFactory jacksonFactory =
+ new com.fasterxml.jackson.core.JsonFactory();
+
+ // --------------------------------------------------------------------------------------------
+
+ /** Don't instantiate */
+ private JsonFactory() {}
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/Parameters.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/Parameters.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/Parameters.java
deleted file mode 100644
index 11dffb8..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/Parameters.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-/**
- * Parameter name constants.
- */
-public class Parameters {
-
- /**
- * The path parameter name for job ids.
- */
- public static final String JOB_ID = "jobid";
-
- /**
- * The path parameter name for job vertex id ids.
- */
- public static final String JOB_VERTEX_ID = "jobvertex";
-
- // --------------------------------------------------------------------------------------------
-
- /** Do not instantiate */
- private Parameters() {}
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestConfigHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestConfigHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestConfigHandler.java
deleted file mode 100644
index af9280e..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestConfigHandler.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-import org.apache.flink.runtime.webmonitor.JsonFactory;
-
-import java.util.Map;
-import java.util.TimeZone;
-
-/**
- * Responder that returns the parameters that define how the asynchronous requests
- * against this web server should behave. It defines for example the refresh interval,
- * and time zone of the server timestamps.
- */
-public class RequestConfigHandler implements RequestHandler, RequestHandler.JsonResponse {
-
- private final String configString;
-
- public RequestConfigHandler(long refreshInterval) {
- TimeZone timeZome = TimeZone.getDefault();
- String timeZoneName = timeZome.getDisplayName();
- long timeZoneOffset= timeZome.getRawOffset();
-
- this.configString = JsonFactory.generateConfigJSON(refreshInterval, timeZoneOffset, timeZoneName);
- }
-
- @Override
- public String handleRequest(Map<String, String> params) {
- return this.configString;
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestJobIdsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestJobIdsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestJobIdsHandler.java
deleted file mode 100644
index c3efce0..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestJobIdsHandler.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-import org.apache.flink.runtime.instance.ActorGateway;
-import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
-import org.apache.flink.runtime.messages.webmonitor.RequestJobsWithIDsOverview;
-import org.apache.flink.runtime.webmonitor.JsonFactory;
-import org.apache.flink.runtime.webmonitor.WebRuntimeMonitor;
-
-import scala.concurrent.Await;
-import scala.concurrent.Future;
-import scala.concurrent.duration.FiniteDuration;
-
-import java.util.Map;
-
-/**
- * Responder that returns with a list of all JobIDs of jobs found at the target actor.
- * May serve the IDs of current jobs, or past jobs, depending on whether this handler is
- * given the JobManager or Archive Actor Reference.
- */
-public class RequestJobIdsHandler implements RequestHandler, RequestHandler.JsonResponse {
-
- private final ActorGateway target;
-
- private final FiniteDuration timeout;
-
- public RequestJobIdsHandler(ActorGateway target) {
- this(target, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
- }
-
- public RequestJobIdsHandler(ActorGateway target, FiniteDuration timeout) {
- if (target == null || timeout == null) {
- throw new NullPointerException();
- }
- this.target = target;
- this.timeout = timeout;
- }
-
- @Override
- public String handleRequest(Map<String, String> params) throws Exception {
- // we need no parameters, get all requests
- try {
- Future<Object> future = target.ask(RequestJobsWithIDsOverview.getInstance(), timeout);
- JobsWithIDsOverview result = (JobsWithIDsOverview) Await.result(future, timeout);
- return JsonFactory.generateJobsOverviewJSON(result);
- }
- catch (Exception e) {
- throw new RuntimeException("Failed to fetch list of all running jobs: " + e.getMessage(), e);
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
deleted file mode 100644
index 377e86a..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-import org.apache.flink.runtime.instance.ActorGateway;
-import org.apache.flink.runtime.messages.webmonitor.RequestStatusOverview;
-import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
-import org.apache.flink.runtime.webmonitor.JsonFactory;
-
-import scala.concurrent.Await;
-import scala.concurrent.Future;
-import scala.concurrent.duration.FiniteDuration;
-
-import java.util.Map;
-
-/**
- * Responder that returns the status of the Flink cluster, such as how many
- * TaskManagers are currently connected, and how many jobs are running.
- */
-public class RequestOverviewHandler implements RequestHandler, RequestHandler.JsonResponse {
-
- private final ActorGateway jobManager;
-
- private final FiniteDuration timeout;
-
-
- public RequestOverviewHandler(ActorGateway jobManager, FiniteDuration timeout) {
- if (jobManager == null || timeout == null) {
- throw new NullPointerException();
- }
- this.jobManager = jobManager;
- this.timeout = timeout;
- }
-
- @Override
- public String handleRequest(Map<String, String> params) throws Exception {
- try {
- Future<Object> future = jobManager.ask(RequestStatusOverview.getInstance(), timeout);
- StatusOverview result = (StatusOverview) Await.result(future, timeout);
- return JsonFactory.generateOverviewJSON(result);
- }
- catch (Exception e) {
- throw new Exception("Failed to fetch the status overview: " + e.getMessage(), e);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
new file mode 100644
index 0000000..4d268ef
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.flink.runtime.execution.ExecutionState;
+import org.apache.flink.runtime.executiongraph.ExecutionGraph;
+import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
+import org.apache.flink.runtime.executiongraph.ExecutionVertex;
+import org.apache.flink.runtime.instance.InstanceConnectionInfo;
+import org.apache.flink.runtime.jobgraph.JobVertexID;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Request handler that returns the JSON program plan of a job graph.
+ */
+public class SubtasksTimesHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+
+
+ public SubtasksTimesHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
+ String vidString = params.get("vertexid");
+ if (vidString == null) {
+ throw new IllegalArgumentException("vertexId parameter missing");
+ }
+
+ JobVertexID vid;
+ try {
+ vid = JobVertexID.fromHexString(vidString);
+ }
+ catch (Exception e) {
+ throw new IllegalArgumentException("Invalid JobVertexID string '" + vidString + "': " + e.getMessage());
+ }
+
+ ExecutionJobVertex jobVertex = graph.getJobVertex(vid);
+ if (jobVertex == null) {
+ throw new IllegalArgumentException("No vertex with ID '" + vidString + "' exists.");
+ }
+
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+
+ gen.writeStringField("id", jobVertex.getJobVertexId().toString());
+ gen.writeStringField("name", jobVertex.getJobVertex().getName());
+
+ gen.writeArrayFieldStart("subtasks");
+
+ int num = 0;
+ for (ExecutionVertex vertex : jobVertex.getTaskVertices()) {
+ gen.writeStartObject();
+ gen.writeNumberField("subtask", num);
+
+ InstanceConnectionInfo location = vertex.getCurrentAssignedResourceLocation();
+ String locationString = location == null ? "(unassigned)" : location.getHostname();
+ gen.writeStringField("host", locationString);
+
+ gen.writeObjectFieldStart("timestamps");
+ long[] timestamps = vertex.getCurrentExecutionAttempt().getStateTimestamps();
+ for (ExecutionState state : ExecutionState.values()) {
+ gen.writeNumberField(state.name(), timestamps[state.ordinal()]);
+ }
+ gen.writeEndObject();
+ gen.writeEndObject();
+ }
+
+ gen.writeEndArray();
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+}
[21/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Auto-update overview page
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
index b11513b..07e09c2 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
@@ -38,7 +38,7 @@ limitations under the License.
<tr ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
- <td>{{job['end-time'] - job['start-time']}} ms</td>
+ <td>{{job.duration}} ms</td>
<td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
index 5a91719..7e9d196 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
@@ -28,12 +28,10 @@ limitations under the License.
<bs-label status="{{status}}" ng-repeat="(status, value) in job.tasks">{{value}}</bs-label>
</div>
</div>
- <div class="navbar-info first last hidden-xs hidden-sm">
- {{ job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- -
- {{ job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- </div>
- <div class="navbar-info last first">{{job['end-time'] - job['start-time']}} ms</div>
+ <div class="navbar-info first last hidden-xs hidden-sm">{{ job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}<span ng-if="job['end-time'] > -1">
+ -
+ {{ job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></div>
+ <div ng-if="job.duration > -1" class="navbar-info last first">{{job.duration}} ms</div>
</nav>
<nav ng-if="job" class="navbar navbar-default navbar-fixed-top navbar-main-additional">
<ul class="nav nav-tabs">
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
index aca3959..dcc19f7 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
@@ -23,7 +23,7 @@ limitations under the License.
<div class="panel-info first">ID: {{ node.id }}</div>
<div ng-if="node.description" class="panel-info">
<div class="label-group">
- <bs-label status="{{status}}" ng-repeat="(index, status) in stateList">{{node.vertex.groupvertex[status]}}</bs-label>
+ <bs-label status="{{status}}" ng-repeat="(index, status) in stateList">{{node.vertex.tasks[status]}}</bs-label>
</div>
</div>
</div>
@@ -40,7 +40,7 @@ limitations under the License.
</thead>
<tbody>
<tr ng-repeat="vertex in node.vertex.groupvertex.groupmembers">
- <td>{{ vertex.vertexname | humanizeTaskName }}</td>
+ <td>{{ vertex.name | humanizeTaskName }}</td>
<td>
<bs-label status="{{vertex.vertexstatus}}">{{vertex.vertexstatus}}</bs-label>
</td>
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
index 9a18a39..11ccaa3 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
@@ -26,8 +26,6 @@ limitations under the License.
<thead>
<tr>
<th>Start Time</th>
- <th>End Time</th>
- <th>Duration</th>
<th>Job Name</th>
<th>Job ID</th>
<th>Tasks</th>
@@ -37,8 +35,6 @@ limitations under the License.
<tbody>
<tr ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
- <td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
- <td>{{job['end-time'] - job['start-time']}} ms</td>
<td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/web/partials/overview.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/overview.html b/flink-runtime-web/web-dashboard/web/partials/overview.html
index de39ad9..31812b7 100644
--- a/flink-runtime-web/web-dashboard/web/partials/overview.html
+++ b/flink-runtime-web/web-dashboard/web/partials/overview.html
@@ -83,8 +83,6 @@ limitations under the License.
<thead>
<tr>
<th>Start Time</th>
- <th>End Time</th>
- <th>Duration</th>
<th>Job Name</th>
<th>Job ID</th>
<th>Tasks</th>
@@ -94,8 +92,6 @@ limitations under the License.
<tbody>
<tr ng-repeat="job in runningJobs" ui-sref="single-job.plan({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
- <td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
- <td>{{job.duration}}</td>
<td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
@@ -130,7 +126,7 @@ limitations under the License.
<tr ng-repeat="job in finishedJobs" ui-sref="single-job.plan({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
- <td>{{job['end-time'] - job['start-time']}} ms</td>
+ <td>{{job.duration}} ms</td>
<td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
[02/51] [abbrv] flink git commit: [FLINK-2415] [optimizer] Create and
attach proper JobGraph describing JSON plans to JobGraph for batch jobs.
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime-web/web-dashboard/web/js/vendor.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/vendor.js b/flink-runtime-web/web-dashboard/web/js/vendor.js
index 323d214..c02c4f5 100644
--- a/flink-runtime-web/web-dashboard/web/js/vendor.js
+++ b/flink-runtime-web/web-dashboard/web/js/vendor.js
@@ -49528,7 +49528,7 @@ angular.module('ui.router.state')
}();
/**
* @license
- * lodash 3.10.0 (Custom Build) <https://lodash.com/>
+ * lodash 3.10.1 (Custom Build) <https://lodash.com/>
* Build: `lodash modern -o ./lodash.js`
* Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
@@ -49541,7 +49541,7 @@ angular.module('ui.router.state')
var undefined;
/** Used as the semantic version number. */
- var VERSION = '3.10.0';
+ var VERSION = '3.10.1';
/** Used to compose bitmasks for wrapper metadata. */
var BIND_FLAG = 1,
@@ -50123,7 +50123,7 @@ angular.module('ui.router.state')
* @private
* @param {Array} array The array to inspect.
* @param {Function} [iteratee] The function invoked per iteration.
- * @returns {Array} Returns the new duplicate-value-free array.
+ * @returns {Array} Returns the new duplicate free array.
*/
function sortedUniq(array, iteratee) {
var seen,
@@ -50602,7 +50602,7 @@ angular.module('ui.router.state')
takeCount = nativeMin(length, this.__takeCount__);
if (!isArr || arrLength < LARGE_ARRAY_SIZE || (arrLength == length && takeCount == length)) {
- return baseWrapperValue((isRight && isArr) ? array.reverse() : array, this.__actions__);
+ return baseWrapperValue(array, this.__actions__);
}
var result = [];
@@ -51319,7 +51319,7 @@ angular.module('ui.router.state')
}
var index = -1,
indexOf = getIndexOf(),
- isCommon = indexOf == baseIndexOf,
+ isCommon = indexOf === baseIndexOf,
cache = (isCommon && values.length >= LARGE_ARRAY_SIZE) ? createCache(values) : null,
valuesLength = values.length;
@@ -52186,13 +52186,13 @@ angular.module('ui.router.state')
* @private
* @param {Array} array The array to inspect.
* @param {Function} [iteratee] The function invoked per iteration.
- * @returns {Array} Returns the new duplicate-value-free array.
+ * @returns {Array} Returns the new duplicate free array.
*/
function baseUniq(array, iteratee) {
var index = -1,
indexOf = getIndexOf(),
length = array.length,
- isCommon = indexOf == baseIndexOf,
+ isCommon = indexOf === baseIndexOf,
isLarge = isCommon && length >= LARGE_ARRAY_SIZE,
seen = isLarge ? createCache() : null,
result = [];
@@ -53401,7 +53401,7 @@ angular.module('ui.router.state')
* @returns {string} Returns the function name.
*/
function getFuncName(func) {
- var result = func.name,
+ var result = (func.name + ''),
array = realNames[result],
length = array ? array.length : 0;
@@ -53668,11 +53668,12 @@ angular.module('ui.router.state')
* @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
*/
function isLaziable(func) {
- var funcName = getFuncName(func);
- if (!(funcName in LazyWrapper.prototype)) {
+ var funcName = getFuncName(func),
+ other = lodash[funcName];
+
+ if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
return false;
}
- var other = lodash[funcName];
if (func === other) {
return true;
}
@@ -54407,7 +54408,7 @@ angular.module('ui.router.state')
/**
* Flattens a nested array. If `isDeep` is `true` the array is recursively
- * flattened, otherwise it is only flattened a single level.
+ * flattened, otherwise it's only flattened a single level.
*
* @static
* @memberOf _
@@ -54454,7 +54455,7 @@ angular.module('ui.router.state')
/**
* Gets the index at which the first occurrence of `value` is found in `array`
* using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
- * for equality comparisons. If `fromIndex` is negative, it is used as the offset
+ * for equality comparisons. If `fromIndex` is negative, it's used as the offset
* from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
* performs a faster binary search.
*
@@ -54533,7 +54534,7 @@ angular.module('ui.router.state')
othIndex = othLength,
caches = Array(length),
indexOf = getIndexOf(),
- isCommon = indexOf == baseIndexOf,
+ isCommon = indexOf === baseIndexOf,
result = [];
while (othIndex--) {
@@ -54818,7 +54819,7 @@ angular.module('ui.router.state')
/**
* Uses a binary search to determine the lowest index at which `value` should
* be inserted into `array` in order to maintain its sort order. If an iteratee
- * function is provided it is invoked for `value` and each element of `array`
+ * function is provided it's invoked for `value` and each element of `array`
* to compute their sort ranking. The iteratee is bound to `thisArg` and
* invoked with one argument; (value).
*
@@ -55092,7 +55093,7 @@ angular.module('ui.router.state')
* [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
* for equality comparisons, in which only the first occurence of each element
* is kept. Providing `true` for `isSorted` performs a faster search algorithm
- * for sorted arrays. If an iteratee function is provided it is invoked for
+ * for sorted arrays. If an iteratee function is provided it's invoked for
* each element in the array to generate the criterion by which uniqueness
* is computed. The `iteratee` is bound to `thisArg` and invoked with three
* arguments: (value, index, array).
@@ -55150,7 +55151,7 @@ angular.module('ui.router.state')
if (!(iteratee == null && callback === baseCallback)) {
iteratee = callback(iteratee, thisArg, 3);
}
- return (isSorted && getIndexOf() == baseIndexOf)
+ return (isSorted && getIndexOf() === baseIndexOf)
? sortedUniq(array, iteratee)
: baseUniq(array, iteratee);
}
@@ -55605,7 +55606,7 @@ angular.module('ui.router.state')
var value = this.__wrapped__;
var interceptor = function(value) {
- return (wrapped && wrapped.__dir__ < 0) ? value : value.reverse();
+ return value.reverse();
};
if (value instanceof LazyWrapper) {
var wrapped = value;
@@ -56047,9 +56048,9 @@ angular.module('ui.router.state')
});
/**
- * Checks if `value` is in `collection` using
+ * Checks if `target` is in `collection` using
* [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
- * for equality comparisons. If `fromIndex` is negative, it is used as the offset
+ * for equality comparisons. If `fromIndex` is negative, it's used as the offset
* from the end of `collection`.
*
* @static
@@ -56144,7 +56145,7 @@ angular.module('ui.router.state')
/**
* Invokes the method at `path` of each element in `collection`, returning
* an array of the results of each invoked method. Any additional arguments
- * are provided to each invoked method. If `methodName` is a function it is
+ * are provided to each invoked method. If `methodName` is a function it's
* invoked for, and `this` bound to, each element in `collection`.
*
* @static
@@ -56796,7 +56797,7 @@ angular.module('ui.router.state')
/**
* The opposite of `_.before`; this method creates a function that invokes
- * `func` once it is called `n` or more times.
+ * `func` once it's called `n` or more times.
*
* @static
* @memberOf _
@@ -56861,7 +56862,7 @@ angular.module('ui.router.state')
/**
* Creates a function that invokes `func`, with the `this` binding and arguments
- * of the created function, while it is called less than `n` times. Subsequent
+ * of the created function, while it's called less than `n` times. Subsequent
* calls to the created function return the result of the last `func` invocation.
*
* @static
@@ -57141,7 +57142,7 @@ angular.module('ui.router.state')
* @param {boolean} [options.leading=false] Specify invoking on the leading
* edge of the timeout.
* @param {number} [options.maxWait] The maximum time `func` is allowed to be
- * delayed before it is invoked.
+ * delayed before it's invoked.
* @param {boolean} [options.trailing=true] Specify invoking on the trailing
* edge of the timeout.
* @returns {Function} Returns the new debounced function.
@@ -57289,7 +57290,7 @@ angular.module('ui.router.state')
/**
* Defers invoking the `func` until the current call stack has cleared. Any
- * additional arguments are provided to `func` when it is invoked.
+ * additional arguments are provided to `func` when it's invoked.
*
* @static
* @memberOf _
@@ -57310,7 +57311,7 @@ angular.module('ui.router.state')
/**
* Invokes `func` after `wait` milliseconds. Any additional arguments are
- * provided to `func` when it is invoked.
+ * provided to `func` when it's invoked.
*
* @static
* @memberOf _
@@ -57643,7 +57644,7 @@ angular.module('ui.router.state')
* Creates a function that invokes `func` with the `this` binding of the
* created function and arguments from `start` and beyond provided as an array.
*
- * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
+ * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/Web/JavaScript/Reference/Functions/rest_parameters).
*
* @static
* @memberOf _
@@ -57694,7 +57695,7 @@ angular.module('ui.router.state')
* Creates a function that invokes `func` with the `this` binding of the created
* function and an array of arguments much like [`Function#apply`](https://es5.github.io/#x15.3.4.3).
*
- * **Note:** This method is based on the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator).
+ * **Note:** This method is based on the [spread operator](https://developer.mozilla.org/Web/JavaScript/Reference/Operators/Spread_operator).
*
* @static
* @memberOf _
@@ -57815,10 +57816,10 @@ angular.module('ui.router.state')
/**
* Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
- * otherwise they are assigned by reference. If `customizer` is provided it is
+ * otherwise they are assigned by reference. If `customizer` is provided it's
* invoked to produce the cloned values. If `customizer` returns `undefined`
* cloning is handled by the method instead. The `customizer` is bound to
- * `thisArg` and invoked with two argument; (value [, index|key, object]).
+ * `thisArg` and invoked with up to three argument; (value [, index|key, object]).
*
* **Note:** This method is loosely based on the
* [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
@@ -57874,15 +57875,15 @@ angular.module('ui.router.state')
isDeep = false;
}
return typeof customizer == 'function'
- ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1))
+ ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 3))
: baseClone(value, isDeep);
}
/**
- * Creates a deep clone of `value`. If `customizer` is provided it is invoked
+ * Creates a deep clone of `value`. If `customizer` is provided it's invoked
* to produce the cloned values. If `customizer` returns `undefined` cloning
* is handled by the method instead. The `customizer` is bound to `thisArg`
- * and invoked with two argument; (value [, index|key, object]).
+ * and invoked with up to three argument; (value [, index|key, object]).
*
* **Note:** This method is loosely based on the
* [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
@@ -57925,7 +57926,7 @@ angular.module('ui.router.state')
*/
function cloneDeep(value, customizer, thisArg) {
return typeof customizer == 'function'
- ? baseClone(value, true, bindCallback(customizer, thisArg, 1))
+ ? baseClone(value, true, bindCallback(customizer, thisArg, 3))
: baseClone(value, true);
}
@@ -58079,7 +58080,7 @@ angular.module('ui.router.state')
}
/**
- * Checks if `value` is empty. A value is considered empty unless it is an
+ * Checks if `value` is empty. A value is considered empty unless it's an
* `arguments` object, array, string, or jQuery-like collection with a length
* greater than `0` or an object with own enumerable properties.
*
@@ -58118,10 +58119,10 @@ angular.module('ui.router.state')
/**
* Performs a deep comparison between two values to determine if they are
- * equivalent. If `customizer` is provided it is invoked to compare values.
+ * equivalent. If `customizer` is provided it's invoked to compare values.
* If `customizer` returns `undefined` comparisons are handled by the method
- * instead. The `customizer` is bound to `thisArg` and invoked with three
- * arguments: (value, other [, index|key]).
+ * instead. The `customizer` is bound to `thisArg` and invoked with up to
+ * three arguments: (value, other [, index|key]).
*
* **Note:** This method supports comparing arrays, booleans, `Date` objects,
* numbers, `Object` objects, regexes, and strings. Objects are compared by
@@ -58237,7 +58238,7 @@ angular.module('ui.router.state')
function isFunction(value) {
// The use of `Object#toString` avoids issues with the `typeof` operator
// in older versions of Chrome and Safari which return 'function' for regexes
- // and Safari 8 equivalents which return 'object' for typed array constructors.
+ // and Safari 8 which returns 'object' for typed array constructors.
return isObject(value) && objToString.call(value) == funcTag;
}
@@ -58271,7 +58272,7 @@ angular.module('ui.router.state')
/**
* Performs a deep comparison between `object` and `source` to determine if
* `object` contains equivalent property values. If `customizer` is provided
- * it is invoked to compare values. If `customizer` returns `undefined`
+ * it's invoked to compare values. If `customizer` returns `undefined`
* comparisons are handled by the method instead. The `customizer` is bound
* to `thisArg` and invoked with three arguments: (value, other, index|key).
*
@@ -58653,7 +58654,7 @@ angular.module('ui.router.state')
* Recursively merges own enumerable properties of the source object(s), that
* don't resolve to `undefined` into the destination object. Subsequent sources
* overwrite property assignments of previous sources. If `customizer` is
- * provided it is invoked to produce the merged values of the destination and
+ * provided it's invoked to produce the merged values of the destination and
* source properties. If `customizer` returns `undefined` merging is handled
* by the method instead. The `customizer` is bound to `thisArg` and invoked
* with five arguments: (objectValue, sourceValue, key, object, source).
@@ -58702,7 +58703,7 @@ angular.module('ui.router.state')
/**
* Assigns own enumerable properties of source object(s) to the destination
* object. Subsequent sources overwrite property assignments of previous sources.
- * If `customizer` is provided it is invoked to produce the assigned values.
+ * If `customizer` is provided it's invoked to produce the assigned values.
* The `customizer` is bound to `thisArg` and invoked with five arguments:
* (objectValue, sourceValue, key, object, source).
*
@@ -59075,7 +59076,7 @@ angular.module('ui.router.state')
* // => 'default'
*/
function get(object, path, defaultValue) {
- var result = object == null ? undefined : baseGet(object, toPath(path), path + '');
+ var result = object == null ? undefined : baseGet(object, toPath(path), (path + ''));
return result === undefined ? defaultValue : result;
}
@@ -59390,7 +59391,7 @@ angular.module('ui.router.state')
/**
* Creates an object composed of the picked `object` properties. Property
* names may be specified as individual arguments or as arrays of property
- * names. If `predicate` is provided it is invoked for each property of `object`
+ * names. If `predicate` is provided it's invoked for each property of `object`
* picking the properties `predicate` returns truthy for. The predicate is
* bound to `thisArg` and invoked with three arguments: (value, key, object).
*
@@ -59424,7 +59425,7 @@ angular.module('ui.router.state')
/**
* This method is like `_.get` except that if the resolved value is a function
- * it is invoked with the `this` binding of its parent object and its result
+ * it's invoked with the `this` binding of its parent object and its result
* is returned.
*
* @static
@@ -59465,7 +59466,7 @@ angular.module('ui.router.state')
/**
* Sets the property value of `path` on `object`. If a portion of `path`
- * does not exist it is created.
+ * does not exist it's created.
*
* @static
* @memberOf _
@@ -59623,7 +59624,7 @@ angular.module('ui.router.state')
/**
* Checks if `n` is between `start` and up to but not including, `end`. If
- * `end` is not specified it is set to `start` with `start` then set to `0`.
+ * `end` is not specified it's set to `start` with `start` then set to `0`.
*
* @static
* @memberOf _
@@ -60587,7 +60588,7 @@ angular.module('ui.router.state')
/**
* Attempts to invoke `func`, returning either the result or the caught error
- * object. Any additional arguments are provided to `func` when it is invoked.
+ * object. Any additional arguments are provided to `func` when it's invoked.
*
* @static
* @memberOf _
@@ -60985,13 +60986,13 @@ angular.module('ui.router.state')
*/
function propertyOf(object) {
return function(path) {
- return baseGet(object, toPath(path), path + '');
+ return baseGet(object, toPath(path), (path + ''));
};
}
/**
* Creates an array of numbers (positive and/or negative) progressing from
- * `start` up to, but not including, `end`. If `end` is not specified it is
+ * `start` up to, but not including, `end`. If `end` is not specified it's
* set to `start` with `start` then set to `0`. If `end` is less than `start`
* a zero-length range is created unless a negative `step` is specified.
*
@@ -61184,7 +61185,7 @@ angular.module('ui.router.state')
/**
* Gets the maximum value of `collection`. If `collection` is empty or falsey
- * `-Infinity` is returned. If an iteratee function is provided it is invoked
+ * `-Infinity` is returned. If an iteratee function is provided it's invoked
* for each value in `collection` to generate the criterion by which the value
* is ranked. The `iteratee` is bound to `thisArg` and invoked with three
* arguments: (value, index, collection).
@@ -61233,7 +61234,7 @@ angular.module('ui.router.state')
/**
* Gets the minimum value of `collection`. If `collection` is empty or falsey
- * `Infinity` is returned. If an iteratee function is provided it is invoked
+ * `Infinity` is returned. If an iteratee function is provided it's invoked
* for each value in `collection` to generate the criterion by which the value
* is ranked. The `iteratee` is bound to `thisArg` and invoked with three
* arguments: (value, index, collection).
@@ -61810,7 +61811,7 @@ angular.module('ui.router.state')
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
var lodashFunc = lodash[methodName];
if (lodashFunc) {
- var key = lodashFunc.name,
+ var key = (lodashFunc.name + ''),
names = realNames[key] || (realNames[key] = []);
names.push({ 'name': methodName, 'func': lodashFunc });
@@ -69851,7 +69852,7 @@ function debugOrdering(g) {
var graphlib;
-if (require) {
+if (typeof require === "function") {
try {
graphlib = require("graphlib");
} catch (e) {}
@@ -70382,7 +70383,7 @@ function canonicalize(attrs) {
var lodash;
-if (require) {
+if (typeof require === "function") {
try {
lodash = require("lodash");
} catch (e) {}
@@ -72434,7 +72435,7 @@ function notime(name, fn) {
}
},{"./graphlib":7,"./lodash":10}],30:[function(require,module,exports){
-module.exports = "0.7.3";
+module.exports = "0.7.4";
},{}]},{},[1])(1)
});
@@ -73661,16 +73662,18 @@ module.exports = "0.4.10";
itemHeight = 20,
itemMargin = 5,
showTimeAxis = true,
+ showTodayLine = false,
timeAxisTick = false,
timeAxisTickFormat = {stroke: "stroke-dasharray", spacing: "4 10"},
+ showTodayFormat = {marginTop: 25, marginBottom: 0, width: 1, color: colorCycle},
showBorderLine = false,
- showHourTimeline = false,
- showBorderFormat = {marginTop: 25, marginBottom: 0, width: 1, color: colorCycle},
- prefix = 'timeline'
+ showBorderFormat = {marginTop: 25, marginBottom: 0, width: 1, color: colorCycle}
;
function timeline (gParent) {
+ var g = gParent.append("g");
var gParentSize = gParent[0][0].getBoundingClientRect();
+
var gParentItem = d3.select(gParent[0][0]);
var yAxisMapping = {},
@@ -73680,18 +73683,6 @@ module.exports = "0.4.10";
setWidth();
- var gClip = gParent.append("svg:clipPath")
- .attr("id", prefix + "-gclip")
- .append("svg:rect")
- .attr("clipPathUnits","objectBoundingBox")
- .attr("x", margin.left)
- .attr("y", margin.top)
- .attr('width', width - margin.left - margin.right)
- .attr("height", 1000);
-
- var g = gParent.append("g")
- .attr("clip-path", "url(#" + prefix + "-gclip" + ")")
-
// check if the user wants relative time
// if so, substract the first timestamp from each subsequent timestamps
if(timeIsRelative){
@@ -73743,7 +73734,7 @@ module.exports = "0.4.10";
}
}
- // var scaleFactor = (1/(ending - beginning)) * (width - margin.left - margin.right);
+ var scaleFactor = (1/(ending - beginning)) * (width - margin.left - margin.right);
// draw the axis
var xScale = d3.time.scale()
@@ -73757,36 +73748,16 @@ module.exports = "0.4.10";
.ticks(tickFormat.numTicks || tickFormat.tickTime, tickFormat.tickInterval)
.tickSize(tickFormat.tickSize);
- if (showHourTimeline) {
- var xAxis2 = d3.svg.axis()
- .scale(xScale)
- .orient(orient)
- .tickFormat(d3.time.format("%X"))
- .ticks(tickFormat.numTicks || tickFormat.tickTime, tickFormat.tickInterval)
- .tickSize(0);
- }
-
if (showTimeAxis) {
- var axisOffsetY = margin.top + (itemHeight + itemMargin) * maxStack;
-
g.append("g")
.attr("class", "axis")
- .attr("transform", "translate(" + 0 +","+axisOffsetY+")")
+ .attr("transform", "translate(" + 0 +","+(margin.top + (itemHeight + itemMargin) * maxStack)+")")
.call(xAxis);
-
- if (showHourTimeline) {
- g.append("g")
- .attr("class", "axis-hour")
- .attr("transform", "translate(" + 0 +","+(axisOffsetY + 20)+")")
- .call(xAxis2);
- }
}
if (timeAxisTick) {
- console.log('here');
-
g.append("g")
- .attr("class", "axis axis-tick")
+ .attr("class", "axis")
.attr("transform", "translate(" + 0 +","+
(margin.top + (itemHeight + itemMargin) * maxStack)+")")
.attr(timeAxisTickFormat.stroke, timeAxisTickFormat.spacing)
@@ -73824,34 +73795,24 @@ module.exports = "0.4.10";
;
}
- var nel = g.selectAll("svg").data(data).enter().append("g")
- .attr("class", "bar-container")
- .attr("width", getBarWidth);
-
- nel
- .append("svg:clipPath")
- .attr("id", prefix + "-timeline-textclip-" + i + "-" + index)
- .attr("class", "timeline-clip")
- .append("svg:rect")
- .attr("clipPathUnits","objectBoundingBox")
- .attr("x", getXPos)
- .attr("y", getStackPosition)
- .attr("width", getTextWidth)
- .attr("height", itemHeight);
-
- nel
+ g.selectAll("svg").data(data).enter()
.append(function(d, i) {
- return document.createElementNS(d3.ns.prefix.svg, "display" in d? d.display:display);
+ return document.createElementNS(d3.ns.prefix.svg, "display" in d? d.display:display);
})
.attr("x", getXPos)
.attr("y", getStackPosition)
- .attr("width", getBarWidth)
+ .attr("width", function (d, i) {
+ return (d.ending_time - d.starting_time) * scaleFactor;
+ })
.attr("cy", function(d, i) {
return getStackPosition(d, i) + itemHeight/2;
})
.attr("cx", getXPos)
.attr("r", itemHeight / 2)
.attr("height", itemHeight)
+ // .style("fill", function(d, i) {
+ // return "#ffffff";
+ // })
.style("fill", function(d, i){
var dColorPropName;
if (d.color) return d.color;
@@ -73888,20 +73849,17 @@ module.exports = "0.4.10";
return d.id ? d.id : "timelineItem_"+index+"_"+i;
})
+ ;
- nel
+ g.selectAll("svg").data(data).enter()
.append("text")
.attr("class", "timeline-insidelabel")
.attr("x", getXTextPos)
.attr("y", getStackTextPosition)
- .attr("width", getTextWidth)
- .attr("height", itemHeight)
- .attr("clip-path", "url(#" + prefix + "-timeline-textclip-" + i + "-" + index + ")")
-
- // .attr("width", function (d, i) {
- // // console.log((d.ending_time - d.starting_time) * scaleFactor);
- // return (d.ending_time - d.starting_time) * scaleFactor;
- // })
+ .attr("width", function (d, i) {
+ // console.log((d.ending_time - d.starting_time) * scaleFactor);
+ return (d.ending_time - d.starting_time) * scaleFactor;
+ })
.text(function(d) {
return d.label;
})
@@ -73923,28 +73881,6 @@ module.exports = "0.4.10";
;
}
- if (showBorderLine) {
- g.selectAll("svg").data(data).enter().append("svg:line")
- .attr("class", "line-" + 'start')
- .attr("x1", getBorderStart)
- // .attr("y1", showBorderFormat.marginTop)
- .attr("y1", getStackPosition)
- .attr("x2", getBorderStart)
- .attr("y2", margin.top + (itemHeight + itemMargin) * maxStack)
- .style("stroke", showBorderFormat.color)
- .style("stroke-width", showBorderFormat.width);
-
- g.selectAll("svg").data(data).enter().append("svg:line")
- .attr("class", "line-" + 'end')
- .attr("x1", getBorderEnd)
- // .attr("y1", showBorderFormat.marginTop)
- .attr("y1", getStackPosition)
- .attr("x2", getBorderEnd)
- .attr("y2", margin.top + (itemHeight + itemMargin) * maxStack)
- .style("stroke", showBorderFormat.color)
- .style("stroke-width", showBorderFormat.width);
- }
-
// add the label
if (hasLabel) {
gParent.append("text")
@@ -73973,67 +73909,27 @@ module.exports = "0.4.10";
}
function getStackTextPosition(d, i) {
if (stacked) {
- return margin.top + (itemHeight + itemMargin) * yAxisMapping[index] + itemHeight * 0.70;
+ return margin.top + (itemHeight + itemMargin) * yAxisMapping[index] + itemHeight * 0.75;
}
- return margin.top + itemHeight * 0.70;
+ return margin.top + itemHeight * 0.75;
}
});
});
- // if (width > gParentSize.width) {
+ if (width > gParentSize.width) {
var move = function() {
-
- // var x = Math.min(0, Math.max(gParentSize.width - width, d3.event.translate[0]));
- // zoom.translate([x, 0]);
- // g.attr("transform", "translate(" + x + ",0)");
-
- // console.log('zoom', d3.event.translate, d3.event.scale);
-
- g.selectAll(".timeline-series")
- .attr("x", getXPos)
- .attr("width", getBarWidth);
-
- g.selectAll(".timeline-insidelabel")
- .attr("x", getXTextPos)
- .attr("width", getTextWidth);
-
- g.selectAll(".timeline-clip")
- .attr("x", getXPos)
- .attr("width", getTextWidth);
-
- g.selectAll(".timeline-clip").select('rect')
- .attr("x", getXPos)
- .attr("width", getTextWidth);
-
- g.selectAll("g.axis")
- .call(xAxis);
-
- if (showHourTimeline) {
- g.selectAll("g.axis-hour")
- .call(xAxis2);
- }
-
- if (showBorderLine) {
- g.selectAll("line.line-start")
- .attr("x1", getBorderStart)
- .attr("x2", getBorderStart);
-
- g.selectAll("line.line-end")
- .attr("x1", getBorderEnd)
- .attr("x2", getBorderEnd);
- }
-
- // scroll(x*scaleFactor, xScale);
+ var x = Math.min(0, Math.max(gParentSize.width - width, d3.event.translate[0]));
+ zoom.translate([x, 0]);
+ g.attr("transform", "translate(" + x + ",0)");
+ scroll(x*scaleFactor, xScale);
};
var zoom = d3.behavior.zoom().x(xScale).on("zoom", move);
- // gParent
- // .attr("class", "scrollable")
- // .call(zoom);
-
- gParent.call(zoom);
- // }
+ gParent
+ .attr("class", "scrollable")
+ .call(zoom);
+ }
if (rotateTicks) {
g.selectAll(".tick text")
@@ -74047,32 +73943,29 @@ module.exports = "0.4.10";
var gSize = g[0][0].getBoundingClientRect();
setHeight();
- function getBorderStart(d, i) {
- return xScale(d.starting_time);
+ if (showBorderLine) {
+ g.each(function (d, i) {
+ d.forEach(function (datum) {
+ var times = datum.times;
+ times.forEach(function (time) {
+ appendLine(xScale(time.starting_time), showBorderFormat);
+ appendLine(xScale(time.ending_time), showBorderFormat);
+ });
+ });
+ });
}
- function getBorderEnd(d, i) {
- return xScale(d.ending_time);
+ if (showTodayLine) {
+ var todayLine = xScale(new Date());
+ appendLine(todayLine, showTodayFormat);
}
function getXPos(d, i) {
- // return margin.left + (d.starting_time - beginning) * scaleFactor;
- return xScale(d.starting_time);
+ return margin.left + (d.starting_time - beginning) * scaleFactor;
}
function getXTextPos(d, i) {
- // return margin.left + (d.starting_time - beginning) * scaleFactor + 5;
- return xScale(d.starting_time) + 5;
- }
-
- function getBarWidth(d, i) {
- // return (d.ending_time - d.starting_time) * scaleFactor;
- return xScale(d.ending_time) - xScale(d.starting_time);
- }
-
- function getTextWidth(d, i) {
- var w = xScale(d.ending_time) - xScale(d.starting_time);
- return w > 5 ? w - 5 : w;
+ return margin.left + (d.starting_time - beginning) * scaleFactor + 5;
}
function setHeight() {
@@ -74113,6 +74006,17 @@ module.exports = "0.4.10";
}
// if both are set, do nothing
}
+
+ function appendLine(lineScale, lineFormat) {
+ gParent.append("svg:line")
+ .attr("x1", lineScale)
+ .attr("y1", lineFormat.marginTop)
+ .attr("x2", lineScale)
+ .attr("y2", height - lineFormat.marginBottom)
+ .style("stroke", lineFormat.color)//"rgb(6,120,155)")
+ .style("stroke-width", lineFormat.width);
+ }
+
}
// SETTINGS
@@ -74171,12 +74075,6 @@ module.exports = "0.4.10";
return timeline;
};
- timeline.prefix = function (p) {
- if (!arguments.length) return prefix;
- prefix = p;
- return timeline;
- };
-
timeline.hover = function (hoverFunc) {
if (!arguments.length) return hover;
hover = hoverFunc;
@@ -74245,17 +74143,23 @@ module.exports = "0.4.10";
return timeline;
};
- timeline.showHourTimeline = function () {
- showHourTimeline = !showHourTimeline;
- return timeline;
- };
-
timeline.showBorderFormat = function(borderFormat) {
if (!arguments.length) return showBorderFormat;
showBorderFormat = borderFormat;
return timeline;
};
+ timeline.showToday = function () {
+ showTodayLine = !showTodayLine;
+ return timeline;
+ };
+
+ timeline.showTodayFormat = function(todayFormat) {
+ if (!arguments.length) return showTodayFormat;
+ showTodayFormat = todayFormat;
+ return timeline;
+ };
+
timeline.colorProperty = function(colorProp) {
if (!arguments.length) return colorPropertyName;
colorPropertyName = colorProp;
@@ -74294,4 +74198,4 @@ module.exports = "0.4.10";
};
})();
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImpxdWVyeS5qcyIsImFuZ3VsYXIuanMiLCJhbmd1bGFyLXVpLXJvdXRlci5qcyIsImQzLmpzIiwibG9kYXNoLmpzIiwibW9tZW50LmpzIiwibW9tZW50LWR1cmF0aW9uLWZvcm1hdC5qcyIsImJvb3RzdHJhcC5qcyIsImdyYXBobGliLmNvcmUuanMiLCJhbmd1bGFyLW1vbWVudC5qcyIsImRhZ3JlLmNvcmUuanMiLCJkYWdyZS1kMy5jb3JlLmpzIiwiZDMtdGltZWxpbmUuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB
Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB
Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB
Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
<TRUNCATED>
[15/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Added
tooltips to timeline
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/4825c8c5/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index d2e4bd1..49c1d4c 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -208,7 +208,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
containerW = elem.width();
angular.element(svgEl).attr('width', containerW);
analyzeTime = function(data) {
- var bbox, chart, svg, svgG, testData;
+ var chart, svg, testData;
testData = [];
angular.forEach(data.groupvertex.groupmembers, function(vertex, i) {
var vTime;
@@ -249,10 +249,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
top: 0,
bottom: 0
}).itemHeight(24).relativeTime();
- svg = d3.select(svgEl).datum(testData).call(chart);
- svgG = svg.select("g");
- bbox = svgG[0][0].getBBox();
- return svg.attr('height', bbox.height + 30);
+ return svg = d3.select(svgEl).datum(testData).call(chart);
};
analyzeTime(scope.data);
}
@@ -272,7 +269,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
return label.replace(">", ">");
};
analyzeTime = function(data) {
- var bbox, chart, svg, svgG, testData;
+ var chart, svg, testData;
testData = [];
testData.push({
times: [
@@ -317,10 +314,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
top: 0,
bottom: 0
}).itemHeight(24).showBorderLine().showHourTimeline();
- svg = d3.select(svgEl).datum(testData).call(chart);
- svgG = svg.select("g");
- bbox = svgG[0][0].getBBox();
- return svg.attr('height', bbox.height + 30);
+ return svg = d3.select(svgEl).datum(testData).call(chart);
};
scope.$watch(attrs.job, function(data) {
if (data) {
@@ -804,4 +798,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuY3RybC5qcyIsIm1vZHVsZXMvam9icy9qb2JzLmRpci5jb2ZmZWUiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuanMiLCJtb2R1bGVzL2pvYnMvam9icy5zdmMuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmpzIiwibW9kdWxlcy9vdmVydmlldy9vdmVydmlldy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuc3ZjLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQWtCQSxRQUFRLE9BQU8sWUFBWSxDQUFDLGFBQWEsa0JBSXhDLG1CQUFJLFNBQUMsWUFBRDtFQUNILFdBQVcsaUJBQWlCO0VDckI1QixPRHNCQSxXQUFXLGNBQWMsV0FBQTtJQUN2QixXQUFXLGlCQUFpQixDQUFDLFdBQVc7SUNyQnhDLE9Ec0JBLFdBQVcsZUFBZTs7SUFJN0IsU0FBUyxlQUFlO0VBQ3ZCLFdBQVc7RUFDWCxXQUFXO0VBR1gsaUJBQWlCO0dBS2xCLGdEQUFJLFNBQUM
sYUFBYSxhQUFhLFdBQTNCO0VDN0JILE9EOEJBLFlBQVk7SUFTYixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sd0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sdUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhOzs7S0FFbEIsTUFBTSw4QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsUUFDRTtRQUFBLGFBQWE7UUFDYixZQUFZOzs7S0FFakIsTUFBTSx5QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsU0FDRTtRQUFBLGFBQWE7OztLQUVsQixNQUFNLHlCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTs7O0tBRWxCLE1BQU0s
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
T0FBTyxVQUFSO0lBQ1gsSUFBQTtJQUFBLFdBQVcsR0FBRztJQUVkLE1BQU0sSUFBSSxZQUFZLFlBQVksbUNBQW1DLFFBQVEsa0JBQWtCLFVBQzlGLFFBQVEsU0FBQyxNQUFEO01DckJQLE9Ec0JBLFNBQVMsUUFBUTs7SUNwQm5CLE9Ec0JBLFNBQVM7O0VDcEJYLE9Ec0JBOztBQ3BCRjtBQ3hJQSxRQUFRLE9BQU8sWUFFZCxXQUFXLG1FQUFzQixTQUFDLFFBQVEsaUJBQWlCLGFBQTFCO0VBQ2hDLE9BQU8sY0FBYyxXQUFBO0lBQ25CLE9BQU8sY0FBYyxZQUFZLFFBQVE7SUNuQnpDLE9Eb0JBLE9BQU8sZUFBZSxZQUFZLFFBQVE7O0VBRTVDLFlBQVksaUJBQWlCLE9BQU87RUFDcEMsT0FBTyxJQUFJLFlBQVksV0FBQTtJQ25CckIsT0RvQkEsWUFBWSxtQkFBbUIsT0FBTzs7RUNsQnhDLE9Eb0JBLE9BQU87O0FDbEJUO0FDT0EsUUFBUSxPQUFPLFlBRWQsUUFBUSxvREFBbUIsU0FBQyxPQUFPLGFBQWEsTUFBckI7RUFDMUIsSUFBQTtFQUFBLGVBQWU7RUFFZixLQUFDLG1CQUFtQixXQUFBO0lBQ2xCLE1BQU0sSUFBSSxZQUFZLFlBQVksbUJBQ2pDLFFBQVEsU0FBQyxNQUFNLFFBQVEsU0FBUyxRQUF4QjtNQ3BCUCxPRHFCQSxLQUFLO09BRU4sTUFBTSxTQUFDLE1BQU0sUUFBUSxTQUFTLFFBQXhCO0lDckJQLE9Ed0JBOztFQ3RCRixPRHdCQTs7QUN0QkYiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvb
mVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcbl
xuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJywgWyd1aS5yb3V0ZXInLCAnYW5ndWxhck1vbWVudCddKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKCRyb290U2NvcGUpIC0+XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZVxuICAkcm9vdFNjb3BlLnNob3dTaWRlYmFyID0gLT5cbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGVcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJDbGFzcyA9ICdmb3JjZS1zaG93J1xuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb25zdGFudCAnZmxpbmtDb25maWcnLCB7XG4gIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MSdcbiAgbmV3U2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDo4MDgxJ1xuIyAgam9iU2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDozMDAwL25ldy1zZXJ2ZXInXG4jICBuZXdTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjMwMDAvbmV3LXNlcnZlcidcbiAgcmVmcmVzaEludGVydmFsOiAxMDAwMFxufVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKEpvYnNTZXJ2aWNlLCBmbGlua0NvbmZpZywgJGludGVydmFsKSAtPlxuICBKb2JzU2VydmljZS5saXN0Sm9icygpXG5cbiAgIyAkaW50ZXJ2YWwgLT5cbiAgIyAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCl
cbiAgIyAsIGZsaW5rQ29uZmlnLnJlZnJlc2hJbnRlcnZhbFxuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbmZpZyAoJHN0YXRlUHJvdmlkZXIsICR1cmxSb3V0ZXJQcm92aWRlcikgLT5cbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUgXCJvdmVydmlld1wiLFxuICAgIHVybDogXCIvb3ZlcnZpZXdcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvb3ZlcnZpZXcuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdPdmVydmlld0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwicnVubmluZy1qb2JzXCIsXG4gICAgdXJsOiBcIi9ydW5uaW5nLWpvYnNcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9ydW5uaW5nLWpvYnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInXG4gIFxuICAuc3RhdGUgXCJjb21wbGV0ZWQtam9ic1wiLFxuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9jb21wbGV0ZWQtam9icy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2JcIixcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwi
XG4gICAgYWJzdHJhY3Q6IHRydWVcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnU2luZ2xlSm9iQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW5cIixcbiAgICB1cmw6IFwiXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4ubm9kZVwiLFxuICAgIHVybDogXCIvdmVydGV4L3tub2RlaWR9XCJcbiAgICB2aWV3czpcbiAgICAgIG5vZGU6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lXCIsXG4gICAgdXJsOiBcIi90aW1lbGluZVwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLFxuICAgIHVybDogXCIve3ZlcnRleElkfVwiXG4gICAgdmlld3M6XG4gICAgICB2ZXJ0Z
Xg6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLnZlcnRleC5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIixcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2Iuc3RhdGlzdGljcy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIixcbiAgICB1cmw6IFwiL2V4Y2VwdGlvbnNcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLmNvbmZpZ1wiLFxuICAgIHVybDogXCIvY29uZmlnXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmNvbmZpZy5odG1sXCJcblxuICAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlIFwiL292ZXJ2aWV3XCJcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcsIFsndWkucm91dGVyJywgJ2FuZ3VsYXJNb21lbnQnXSkucnVuKGZ1bmN0aW9uKCRyb290U2NvcGUpIHtcbiAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9IGZhbHNlO1xuICByZX
R1cm4gJHJvb3RTY29wZS5zaG93U2lkZWJhciA9IGZ1bmN0aW9uKCkge1xuICAgICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSAhJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZTtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5zaWRlYmFyQ2xhc3MgPSAnZm9yY2Utc2hvdyc7XG4gIH07XG59KS5jb25zdGFudCgnZmxpbmtDb25maWcnLCB7XG4gIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MScsXG4gIG5ld1NlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MScsXG4gIHJlZnJlc2hJbnRlcnZhbDogMTAwMDBcbn0pLnJ1bihmdW5jdGlvbihKb2JzU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UubGlzdEpvYnMoKTtcbn0pLmNvbmZpZyhmdW5jdGlvbigkc3RhdGVQcm92aWRlciwgJHVybFJvdXRlclByb3ZpZGVyKSB7XG4gICRzdGF0ZVByb3ZpZGVyLnN0YXRlKFwib3ZlcnZpZXdcIiwge1xuICAgIHVybDogXCIvb3ZlcnZpZXdcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9vdmVydmlldy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdPdmVydmlld0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInJ1bm5pbmctam9ic1wiLCB7XG4gICAgdXJsOiBcIi9ydW5uaW5nLWpvYnNcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1
wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL3J1bm5pbmctam9icy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcImNvbXBsZXRlZC1qb2JzXCIsIHtcbiAgICB1cmw6IFwiL2NvbXBsZXRlZC1qb2JzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9jb21wbGV0ZWQtam9icy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYlwiLCB7XG4gICAgdXJsOiBcIi9qb2JzL3tqb2JpZH1cIixcbiAgICBhYnN0cmFjdDogdHJ1ZSxcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdTaW5nbGVKb2JDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnBsYW5cIiwge1xuICAgIHVybDogXCJcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Db250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3Rh
dGUoXCJzaW5nbGUtam9iLnBsYW4ubm9kZVwiLCB7XG4gICAgdXJsOiBcIi92ZXJ0ZXgve25vZGVpZH1cIixcbiAgICB2aWV3czoge1xuICAgICAgbm9kZToge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbk5vZGVDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnRpbWVsaW5lXCIsIHtcbiAgICB1cmw6IFwiL3RpbWVsaW5lXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHtcbiAgICB1cmw6IFwiL3t2ZXJ0ZXhJZH1cIixcbiAgICB2aWV3czoge1xuICAgICAgdmVydGV4OiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLnZlcnRleC5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2Iuc3RhdGlzdGljc1wiLCB7XG4gICAgdXJsOiBcIi9zdGF0aXN0aWNzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFyd
GlhbHMvam9icy9qb2Iuc3RhdGlzdGljcy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5leGNlcHRpb25zXCIsIHtcbiAgICB1cmw6IFwiL2V4Y2VwdGlvbnNcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5leGNlcHRpb25zLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLmNvbmZpZ1wiLCB7XG4gICAgdXJsOiBcIi9jb25maWdcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5jb25maWcuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KTtcbiAgcmV0dXJuICR1cmxSb3V0ZXJQcm92aWRlci5vdGhlcndpc2UoXCIvb3ZlcnZpZXdcIik7XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW
9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnYnNMYWJlbCcsIChKb2JzU2VydmljZSkgLT5cbiAgdHJhbnNjbHVkZTogdHJ1ZVxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOiBcbiAgICBnZXRMYWJlbENsYXNzOiBcIiZcIlxuICAgIHN0YXR1czogXCJAXCJcblxuICB0ZW1wbGF
0ZTogXCI8c3BhbiB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKSc+PG5nLXRyYW5zY2x1ZGU+PC9uZy10cmFuc2NsdWRlPjwvc3Bhbj5cIlxuICBcbiAgbGluazogKHNjb3BlLCBlbGVtZW50LCBhdHRycykgLT5cbiAgICBzY29wZS5nZXRMYWJlbENsYXNzID0gLT5cbiAgICAgICdsYWJlbCBsYWJlbC0nICsgSm9ic1NlcnZpY2UudHJhbnNsYXRlTGFiZWxTdGF0ZShhdHRycy5zdGF0dXMpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICdpbmRpY2F0b3JQcmltYXJ5JywgKEpvYnNTZXJ2aWNlKSAtPlxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOiBcbiAgICBnZXRMYWJlbENsYXNzOiBcIiZcIlxuICAgIHN0YXR1czogJ0AnXG5cbiAgdGVtcGxhdGU6IFwiPGkgdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknIC8+XCJcbiAgXG4gIGxpbms6IChzY29wZSwgZWxlbWVudCwgYXR0cnMpIC0+XG4gICAgc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IC0+XG4gICAgICAnZmEgZmEtY2lyY2xlIGluZGljYXRvciBpbmRpY2F0b3ItJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAndGFibGVQcm9wZXJ0eScsIC0+XG4gIHJlcGxhY2U6IHRydWVc
biAgc2NvcGU6XG4gICAgdmFsdWU6ICc9J1xuXG4gIHRlbXBsYXRlOiBcIjx0ZCB0aXRsZT1cXFwie3t2YWx1ZSB8fCAnTm9uZSd9fVxcXCI+e3t2YWx1ZSB8fCAnTm9uZSd9fTwvdGQ+XCJcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmRpcmVjdGl2ZSgnYnNMYWJlbCcsIGZ1bmN0aW9uKEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiB7XG4gICAgdHJhbnNjbHVkZTogdHJ1ZSxcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICBnZXRMYWJlbENsYXNzOiBcIiZcIixcbiAgICAgIHN0YXR1czogXCJAXCJcbiAgICB9LFxuICAgIHRlbXBsYXRlOiBcIjxzcGFuIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJz48bmctdHJhbnNjbHVkZT48L25nLXRyYW5zY2x1ZGU+PC9zcGFuPlwiLFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtZW50LCBhdHRycykge1xuICAgICAgcmV0dXJuIHNjb3BlLmdldExhYmVsQ2xhc3MgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuICdsYWJlbCBsYWJlbC0nICsgSm9ic1NlcnZpY2UudHJhbnNsYXRlTGFiZWxTdGF0ZShhdHRycy5zdGF0dXMpO1xuICAgICAgfTtcbiAgICB9XG4gIH07XG59KS5kaXJlY3RpdmUoJ2luZGljYXRvclByaW1hcnknLCBmdW5jdGlvbihKb2JzU2VydmljZSkge1xuICByZXR1cm4ge1xuICAgIHJlcGxhY2U6IHRydWUsXG4gICAgc2NvcGU6IHtcbiAgICAgIGdldExhYmVsQ2xhc3M6IFwiJ
lwiLFxuICAgICAgc3RhdHVzOiAnQCdcbiAgICB9LFxuICAgIHRlbXBsYXRlOiBcIjxpIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJyAvPlwiLFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtZW50LCBhdHRycykge1xuICAgICAgcmV0dXJuIHNjb3BlLmdldExhYmVsQ2xhc3MgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuICdmYSBmYS1jaXJjbGUgaW5kaWNhdG9yIGluZGljYXRvci0nICsgSm9ic1NlcnZpY2UudHJhbnNsYXRlTGFiZWxTdGF0ZShhdHRycy5zdGF0dXMpO1xuICAgICAgfTtcbiAgICB9XG4gIH07XG59KS5kaXJlY3RpdmUoJ3RhYmxlUHJvcGVydHknLCBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHtcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICB2YWx1ZTogJz0nXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8dGQgdGl0bGU9XFxcInt7dmFsdWUgfHwgJ05vbmUnfX1cXFwiPnt7dmFsdWUgfHwgJ05vbmUnfX08L3RkPlwiXG4gIH07XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcn
NoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4uZmlsdGVyIFwiYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkXCIsIChhbmd1bGFyTW9tZW50Q29uZmlnKSAtPlxuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIgPSAodmFsdWUsIGZvcm1hdCwgZHVyYXR
pb25Gb3JtYXQpIC0+XG4gICAgcmV0dXJuIFwiXCIgIGlmIHR5cGVvZiB2YWx1ZSBpcyBcInVuZGVmaW5lZFwiIG9yIHZhbHVlIGlzIG51bGxcblxuICAgIG1vbWVudC5kdXJhdGlvbih2YWx1ZSwgZm9ybWF0KS5mb3JtYXQoZHVyYXRpb25Gb3JtYXQsIHsgdHJpbTogZmFsc2UgfSlcblxuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIuJHN0YXRlZnVsID0gYW5ndWxhck1vbWVudENvbmZpZy5zdGF0ZWZ1bEZpbHRlcnNcblxuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXJcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmZpbHRlcihcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCBmdW5jdGlvbihhbmd1bGFyTW9tZW50Q29uZmlnKSB7XG4gIHZhciBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXI7XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlciA9IGZ1bmN0aW9uKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJ1bmRlZmluZWRcIiB8fCB2YWx1ZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxuICAgIHJldHVybiBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7XG4gICAgICB0cmltOiBmYWxzZVxuICAgIH0pO1xuICB9O1xuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIuJHN0YXRlZnVsID0gYW5ndWxhck1vbWVudENv
bmZpZy5zdGF0ZWZ1bEZpbHRlcnM7XG4gIHJldHVybiBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXI7XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBla
XRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4uY29udHJvbGxlciAnUnVubmluZ0pvYnNDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSAtPlxuICAgICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygncnVubmluZycpXG5cbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuXG4gICRzY29wZS5qb2JPYnNlcnZlcigpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSAtPlxuICAgICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygnZmluaXNoZWQnKVxuXG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk
9ic2VydmVyKVxuICAkc2NvcGUuJG9uICckZGVzdHJveScsIC0+XG4gICAgSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcblxuICAkc2NvcGUuam9iT2JzZXJ2ZXIoKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdTaW5nbGVKb2JDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlLCAkcm9vdFNjb3BlKSAtPlxuICAkc2NvcGUuam9iaWQgPSAkc3RhdGVQYXJhbXMuam9iaWRcbiAgJHJvb3RTY29wZS5qb2IgPSBudWxsXG5cbiAgSm9ic1NlcnZpY2UubG9hZEpvYigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHJvb3RTY29wZS5qb2IgPSBkYXRhXG5cbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgICRyb290U2NvcGUuam9iID0gbnVsbFxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQbGFuQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgSm9ic1NlcnZpY2UubG9hZFBsYW4oJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS5wbGFuID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQbGFuTm9
kZUNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5ub2RlaWQgPSAkc3RhdGVQYXJhbXMubm9kZWlkXG4gICRzY29wZS5zdGF0ZUxpc3QgPSBKb2JzU2VydmljZS5zdGF0ZUxpc3QoKVxuXG4gIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUubm9kZSA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBKb2JzU2VydmljZS5nZXRWZXJ0ZXgoJHN0YXRlUGFyYW1zLmpvYmlkLCAkc3RhdGVQYXJhbXMudmVydGV4SWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLnZlcnRleCA9IGRhdGFcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmNvbnRyb2xsZXIoJ1J1bm5pbmdKb2JzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ3J1bm5pbmcnKTtcbiAgfTtcbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xu
ICAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgfSk7XG4gIHJldHVybiAkc2NvcGUuam9iT2JzZXJ2ZXIoKTtcbn0pLmNvbnRyb2xsZXIoJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygnZmluaXNoZWQnKTtcbiAgfTtcbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgfSk7XG4gIHJldHVybiAkc2NvcGUuam9iT2JzZXJ2ZXIoKTtcbn0pLmNvbnRyb2xsZXIoJ1NpbmdsZUpvYkNvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHJvb3RTY29wZSkge1xuICAkc2NvcGUuam9iaWQgPSAkc3RhdGVQYXJhbXMuam9iaWQ7XG4gICRyb290U2NvcGUuam9iID0gbnVsbDtcbiAgSm9ic1NlcnZpY2UubG9hZEpvYigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZ
W4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkcm9vdFNjb3BlLmpvYiA9IGRhdGE7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5qb2IgPSBudWxsO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Db250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmxvYWRQbGFuKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5wbGFuID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuTm9kZUNvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZDtcbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpO1xuICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0Tm9kZSgkc2NvcGUubm9kZWlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlLm5vZGUgPSBkYXRhO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybi
BKb2JzU2VydmljZS5nZXRWZXJ0ZXgoJHN0YXRlUGFyYW1zLmpvYmlkLCAkc3RhdGVQYXJhbXMudmVydGV4SWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkc2NvcGUudmVydGV4ID0gZGF0YTtcbiAgfSk7XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkF
TIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAndmVydGV4JywgKCRzdGF0ZSkgLT5cbiAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUgc2Vjb25kYXJ5JyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGRhdGE6IFwiPVwiXG5cbiAgbGluazogKHNjb3BlLCBlbGVtLCBhdHRycykgLT5cbiAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEuZ3JvdXB2ZXJ0ZXguZ3JvdXBtZW1iZXJzLCAodmVydGV4LCBpKSAtPlxuICAgICAgICB2VGltZSA9IGRhdGEudmVydGljZXRpbWVzW3ZlcnRleC52ZXJ0ZXhp
ZF1cblxuICAgICAgICB0ZXN0RGF0YS5wdXNoIHtcbiAgICAgICAgICBsYWJlbDogXCIje3ZlcnRleC52ZXJ0ZXhpbnN0YW5jZW5hbWV9ICgje2l9KVwiXG4gICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiIzY2NlwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiU0NIRURVTEVEXCJdICogMTAwXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkRFUExPWUlOR1wiXSAqIDEwMFxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6IFwiRGVwbG95aW5nXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2FhYVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiREVQTE9ZSU5HXCJdICogMTAwXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIlJVTk5JTkdcIl0gKiAxMDBcbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlJ1bm5pbmdcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJSVU5OSU5HXCJdICogMTAwXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lO
iB2VGltZVtcIkZJTklTSEVEXCJdICogMTAwXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgIF1cbiAgICAgICAgfVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKVxuICAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDFcbiAgICAgICAgdGlja1NpemU6IDFcbiAgICAgIH0pXG4gICAgICAucHJlZml4KFwic2luZ2xlXCIpXG4gICAgICAubGFiZWxGb3JtYXQoKGxhYmVsKSAtPlxuICAgICAgICBsYWJlbFxuICAgICAgKVxuICAgICAgLm1hcmdpbih7IGxlZnQ6IDEwMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgyNClcbiAgICAgIC5yZWxhdGl2ZVRpbWUoKVxuXG4gICAgICBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpXG4gICAgICAuZGF0dW0odGVzdERhdGEpXG4gICAgICAuY2FsbChjaGFydClcblxuICAgICAgc3ZnRyA9IHN2Zy5zZWxlY3QoXCJnXCIpXG5cbiAgICAgIGJib3ggPSBzdmdHWzBdWzBdLmdldEJCb3goKVxuICAgICAgc3ZnLmF0dHIoJ2hlaWdodCcsIGJib3guaGVpZ2h0ICsgMzApXG5cbiAgICBhbmFseXplVGltZShzY29wZS5kYXRhKVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0aW
1lbGluZScsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGpvYjogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpXG5cbiAgICB0cmFuc2xhdGVMYWJlbCA9IChsYWJlbCkgLT5cbiAgICAgIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgdGVzdERhdGEucHVzaCBcbiAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIlxuICAgICAgICAgIGNvbG9yOiBcIiNjY2NjY2NcIlxuICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEub2xkVltcIlNDSEVEVUxFRFwiXVxuICAgICAgICAgIGVuZGluZ190aW1lOiBkYXRhLm9sZFZbXCJTQ0hFRFVMRURcIl0gKyAzMFxuICAgICAgICAgICMgbGluazogdmVydGV4Lmdyb3VwdmVydGV4aWRcbiAgICAgICAgICB0eXBlOiAnc2NoZWR1bGVkJ1xuICAgICAgICBdXG5cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEub2xkVi5ncm91cHZlcnRpY2VzLCAodmVydGV4KSAtPlx
uICAgICAgICB2VGltZSA9IGRhdGEub2xkVi5ncm91cHZlcnRpY2V0aW1lc1t2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhpZF1cblxuICAgICAgICAjIGNvbnNvbGUubG9nIHZUaW1lLCB2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhpZFxuXG4gICAgICAgIHRlc3REYXRhLnB1c2ggXG4gICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgIGxhYmVsOiB0cmFuc2xhdGVMYWJlbCh2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhuYW1lKVxuICAgICAgICAgICAgY29sb3I6IFwiIzYyY2RlYVwiXG4gICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIlNUQVJURURcIl1cbiAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkVOREVEXCJdXG4gICAgICAgICAgICBsaW5rOiB2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhpZFxuICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgXVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5jbGljaygoZCwgaSwgZGF0dW0pIC0+XG4gICAgICAgIGlmIGQubGlua1xuICAgICAgICAgICRzdGF0ZS5nbyBcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHsgam9iaWQ6IGRhdGEuamlkLCB2ZXJ0ZXhJZDogZC5saW5rIH1cblxuICAgICAgKVxuICAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICAgICAgIyB0aWNrVGltZTogZDMudGltZS5zZWNvbmRcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDAuNVxuICAgICAgICB0
aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJtYWluXCIpXG4gICAgICAubWFyZ2luKHsgbGVmdDogMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgyNClcbiAgICAgIC5zaG93Qm9yZGVyTGluZSgpXG4gICAgICAuc2hvd0hvdXJUaW1lbGluZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgICBzdmdHID0gc3ZnLnNlbGVjdChcImdcIilcblxuICAgICAgYmJveCA9IHN2Z0dbMF1bMF0uZ2V0QkJveCgpXG4gICAgICBzdmcuYXR0cignaGVpZ2h0JywgYmJveC5oZWlnaHQgKyAzMClcblxuICAgIHNjb3BlLiR3YXRjaCBhdHRycy5qb2IsIChkYXRhKSAtPlxuICAgICAgYW5hbHl6ZVRpbWUoZGF0YSkgaWYgZGF0YVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICdqb2JQbGFuJywgKCR0aW1lb3V0KSAtPlxuICB0ZW1wbGF0ZTogXCJcbiAgICA8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+XG4gICAgPHN2ZyBjbGFzcz0ndG1wJyB3aWR0aD0nMScgaGVpZ2h0PScxJz48ZyAvPjwvc3ZnPlxuICAgIDxkaXYgY2xhc3M9J2J0bi1ncm91cCB6b29tLWJ1dHRvbnMnPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZ
GVmYXVsdCB6b29tLWluJyBuZy1jbGljaz0nem9vbUluKCknPjxpIGNsYXNzPSdmYSBmYS1wbHVzJyAvPjwvYT5cbiAgICAgIDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+XG4gICAgPC9kaXY+XCJcblxuICBzY29wZTpcbiAgICBwbGFuOiAnPSdcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIG1haW5ab29tID0gZDMuYmVoYXZpb3Iuem9vbSgpXG4gICAgc3ViZ3JhcGhzID0gW11cbiAgICBqb2JpZCA9IGF0dHJzLmpvYmlkXG5cbiAgICBtYWluU3ZnRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuICAgIG1haW5HID0gZWxlbS5jaGlsZHJlbigpLmNoaWxkcmVuKClbMF1cbiAgICBtYWluVG1wRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVsxXVxuXG4gICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1haW5TdmdFbGVtZW50KVxuICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpXG4gICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpXG5cbiAgICAjIGFuZ3VsYXIuZWxlbWVudChtYWluRykuZW1wdHkoKVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpXG5cbiAgICBzY29wZS56b29tSW4gPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKS
A8IDIuOTlcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIHpvb20gb2JqZWN0XG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW5ab29tLnNjYWxlIG1haW5ab29tLnNjYWxlKCkgKyAwLjFcbiAgICAgICAgbWFpblpvb20udHJhbnNsYXRlIFsgdjEsIHYyIF1cbiAgICAgICAgXG4gICAgICAgICMgVHJhbnNmb3JtIHN2Z1xuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCJcblxuICAgIHNjb3BlLnpvb21PdXQgPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKSA+IDAuMzFcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIG1haW5ab29tIG9iamVjdFxuICAgICAgICBtYWluWm9vbS5zY2FsZSBtYWluWm9vbS5zY2FsZSgpIC0gMC4xXG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzB
dICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZSBbIHYxLCB2MiBdXG4gICAgICAgIFxuICAgICAgICAjIFRyYW5zZm9ybSBzdmdcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiXG5cbiAgICAjY3JlYXRlIGEgbGFiZWwgb2YgYW4gZWRnZVxuICAgIGNyZWF0ZUxhYmVsRWRnZSA9IChlbCkgLT5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIlwiXG4gICAgICBpZiBlbC5zaGlwX3N0cmF0ZWd5PyBvciBlbC5sb2NhbF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxkaXYgY2xhc3M9J2VkZ2UtbGFiZWwnPlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneSAgaWYgZWwuc2hpcF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiAoXCIgKyBlbC50ZW1wX21vZGUgKyBcIilcIiAgdW5sZXNzIGVsLnRlbXBfbW9kZSBpcyBgdW5kZWZpbmVkYFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiLDxicj5cIiArIGVsLmxvY2FsX3N0cmF0ZWd5ICB1bmxlc3MgZWwubG9jYWxfc3RyYXRlZ3kgaXMgYHVuZGVmaW5l
ZGBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiXG4gICAgICBsYWJlbFZhbHVlXG5cblxuICAgICMgdHJ1ZSwgaWYgdGhlIG5vZGUgaXMgYSBzcGVjaWFsIG5vZGUgZnJvbSBhbiBpdGVyYXRpb25cbiAgICBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlID0gKGluZm8pIC0+XG4gICAgICAoaW5mbyBpcyBcInBhcnRpYWxTb2x1dGlvblwiIG9yIGluZm8gaXMgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIgb3IgaW5mbyBpcyBcIndvcmtzZXRcIiBvciBpbmZvIGlzIFwibmV4dFdvcmtzZXRcIiBvciBpbmZvIGlzIFwic29sdXRpb25TZXRcIiBvciBpbmZvIGlzIFwic29sdXRpb25EZWx0YVwiKVxuXG4gICAgZ2V0Tm9kZVR5cGUgPSAoZWwsIGluZm8pIC0+XG4gICAgICBpZiBpbmZvIGlzIFwibWlycm9yXCJcbiAgICAgICAgJ25vZGUtbWlycm9yJ1xuXG4gICAgICBlbHNlIGlmIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbylcbiAgICAgICAgJ25vZGUtaXRlcmF0aW9uJ1xuXG4gICAgICBlbHNlXG4gICAgICAgICAgJ25vZGUtbm9ybWFsJ1xuICAgICAgXG4gICAgIyBjcmVhdGVzIHRoZSBsYWJlbCBvZiBhIG5vZGUsIGluIGluZm8gaXMgc3RvcmVkLCB3aGV0aGVyIGl0IGlzIGEgc3BlY2lhbCBub2RlIChsaWtlIGEgbWlycm9yIGluIGFuIGl0ZXJhdGlvbilcbiAgICBjcmVhdGVMYWJlbE5vZGUgPSAoZWwsIGluZm8sIG1heFcsIG1heEgpIC0+XG4gICAgICBsYWJlbFZhbHVlID0gXCI8YSBocmVmPScjL2pvYnMvX
CIgKyBqb2JpZCArIFwiL3ZlcnRleC9cIiArIGVsLmlkICsgXCInIGNsYXNzPSdub2RlLWxhYmVsIFwiICsgZ2V0Tm9kZVR5cGUoZWwsIGluZm8pICsgXCInPlwiXG5cbiAgICAgICMgTm9kZW5hbWVcbiAgICAgIGlmIGluZm8gaXMgXCJtaXJyb3JcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPk1pcnJvciBvZiBcIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiXG4gICAgICBlbHNlXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+XCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIlxuICAgICAgaWYgZWwuZGVzY3JpcHRpb24gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiXCJcbiAgICAgIGVsc2VcbiAgICAgICAgc3RlcE5hbWUgPSBlbC5kZXNjcmlwdGlvblxuICAgICAgICBcbiAgICAgICAgIyBjbGVhbiBzdGVwTmFtZVxuICAgICAgICBzdGVwTmFtZSA9IHNob3J0ZW5TdHJpbmcoc3RlcE5hbWUpXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDQgY2xhc3M9J3N0ZXAtbmFtZSc+XCIgKyBzdGVwTmFtZSArIFwiPC9oND5cIlxuICAgICAgXG4gICAgICAjIElmIHRoaXMgbm9kZSBpcyBhbiBcIml0ZXJhdGlvblwiIHdlIG5lZWQgYSBkaWZmZXJlbnQgcGFuZWwtYm9keVxuICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvbj9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24oZWwuaWQsIG1heF
csIG1heEgpXG4gICAgICBlbHNlXG4gICAgICAgIFxuICAgICAgICAjIE90aGVyd2lzZSBhZGQgaW5mb3MgICAgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+XCIgKyBpbmZvICsgXCIgTm9kZTwvaDU+XCIgIGlmIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbylcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5QYXJhbGxlbGlzbTogXCIgKyBlbC5wYXJhbGxlbGlzbSArIFwiPC9oNT5cIiAgdW5sZXNzIGVsLnBhcmFsbGVsaXNtIGlzIFwiXCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5PcGVyYXRpb246IFwiICsgc2hvcnRlblN0cmluZyhlbC5vcGVyYXRvcl9zdHJhdGVneSkgKyBcIjwvaDU+XCIgIHVubGVzcyBlbC5vcGVyYXRvciBpcyBgdW5kZWZpbmVkYFxuICAgICAgXG4gICAgICBsYWJlbFZhbHVlICs9IFwiPC9hPlwiXG4gICAgICBsYWJlbFZhbHVlXG5cbiAgICAjIEV4dGVuZHMgdGhlIGxhYmVsIG9mIGEgbm9kZSB3aXRoIGFuIGFkZGl0aW9uYWwgc3ZnIEVsZW1lbnQgdG8gcHJlc2VudCB0aGUgaXRlcmF0aW9uLlxuICAgIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiA9IChpZCwgbWF4VywgbWF4SCkgLT5cbiAgICAgIHN2Z0lEID0gXCJzdmctXCIgKyBpZFxuXG4gICAgICBsYWJlbFZhbHVlID0gXCI8c3ZnIGNsYXNzPSdcIiArIHN2Z0lEICsgXCInIHdpZHRoPVwiICsgbWF4VyArIFwiIGhlaWdodD1cIiArIG1heEggKyBcIj48ZyAvPjwvc3ZnPlwiXG4gICAgICBsYWJlbFZhbHV
lXG5cbiAgICAjIFNwbGl0IGEgc3RyaW5nIGludG8gbXVsdGlwbGUgbGluZXMgc28gdGhhdCBlYWNoIGxpbmUgaGFzIGxlc3MgdGhhbiAzMCBsZXR0ZXJzLlxuICAgIHNob3J0ZW5TdHJpbmcgPSAocykgLT5cbiAgICAgICMgbWFrZSBzdXJlIHRoYXQgbmFtZSBkb2VzIG5vdCBjb250YWluIGEgPCAoYmVjYXVzZSBvZiBodG1sKVxuICAgICAgaWYgcy5jaGFyQXQoMCkgaXMgXCI8XCJcbiAgICAgICAgcyA9IHMucmVwbGFjZShcIjxcIiwgXCImbHQ7XCIpXG4gICAgICAgIHMgPSBzLnJlcGxhY2UoXCI+XCIsIFwiJmd0O1wiKVxuICAgICAgc2JyID0gXCJcIlxuICAgICAgd2hpbGUgcy5sZW5ndGggPiAzMFxuICAgICAgICBzYnIgPSBzYnIgKyBzLnN1YnN0cmluZygwLCAzMCkgKyBcIjxicj5cIlxuICAgICAgICBzID0gcy5zdWJzdHJpbmcoMzAsIHMubGVuZ3RoKVxuICAgICAgc2JyID0gc2JyICsgc1xuICAgICAgc2JyXG5cbiAgICBjcmVhdGVOb2RlID0gKGcsIGRhdGEsIGVsLCBpc1BhcmVudCA9IGZhbHNlLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgIyBjcmVhdGUgbm9kZSwgc2VuZCBhZGRpdGlvbmFsIGluZm9ybWF0aW9ucyBhYm91dCB0aGUgbm9kZSBpZiBpdCBpcyBhIHNwZWNpYWwgb25lXG4gICAgICBpZiBlbC5pZCBpcyBkYXRhLnBhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1h
eFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5uZXh0X3BhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLndvcmtzZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwid29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIndvcmtzZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLm5leHRfd29ya3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0V29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRXb
3Jrc2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5zb2x1dGlvbl9zZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25TZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvblNldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEuc29sdXRpb25fZGVsdGFcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25EZWx0YVwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uRGVsdGFcIilcblxuICAgICAgZWxzZVxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJcIilcblxuICAgIGNyZWF0ZUVkZ2UgPSAoZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpIC0+XG4gICAgICB1bmxlc3MgZXhpc3RpbmdOb2Rlcy5pbmRleE9mKHByZWQuaWQpIGlzIC0xXG4gICAgICAgIGcuc2V0RWRnZSBwcmVkLm
lkLCBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKHByZWQpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBhcnJvd2hlYWQ6ICdub3JtYWwnXG5cbiAgICAgIGVsc2VcbiAgICAgICAgbWlzc2luZ05vZGUgPSBzZWFyY2hGb3JOb2RlKGRhdGEsIHByZWQuaWQpXG4gICAgICAgIHVubGVzcyAhbWlzc2luZ05vZGUgb3IgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkIGlzIHRydWVcbiAgICAgICAgICBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgPSB0cnVlXG4gICAgICAgICAgZy5zZXROb2RlIG1pc3NpbmdOb2RlLmlkLFxuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShtaXNzaW5nTm9kZSwgXCJtaXJyb3JcIilcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUobWlzc2luZ05vZGUsICdtaXJyb3InKVxuXG4gICAgICAgICAgZy5zZXRFZGdlIG1pc3NpbmdOb2RlLmlkLCBlbC5pZCxcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UobWlzc2luZ05vZGUpXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuXG4gICAgbG9hZEpzb25Ub0RhZ3JlID0gKGcsIGRhdGEpIC0+XG4gICAgICBleGlzdGluZ05vZGVzID0gW11cblxuICAgICAgaWYgZGF0YS5ub2Rlcz9cbiAgICAgICAgIyBUaGlzIGlzIHRoZSBub3JtYWwganNvbiBkYXRhXG4gICAgICAgIHRvSXRlcmF0ZSA9IGRhdGE
ubm9kZXNcblxuICAgICAgZWxzZVxuICAgICAgICAjIFRoaXMgaXMgYW4gaXRlcmF0aW9uLCB3ZSBub3cgc3RvcmUgc3BlY2lhbCBpdGVyYXRpb24gbm9kZXMgaWYgcG9zc2libGVcbiAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5zdGVwX2Z1bmN0aW9uXG4gICAgICAgIGlzUGFyZW50ID0gdHJ1ZVxuXG4gICAgICBmb3IgZWwgaW4gdG9JdGVyYXRlXG4gICAgICAgIG1heFcgPSAwXG4gICAgICAgIG1heEggPSAwXG5cbiAgICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvblxuICAgICAgICAgIHNnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoeyBtdWx0aWdyYXBoOiB0cnVlLCBjb21wb3VuZDogdHJ1ZSB9KS5zZXRHcmFwaCh7XG4gICAgICAgICAgICBub2Rlc2VwOiAyMFxuICAgICAgICAgICAgZWRnZXNlcDogMFxuICAgICAgICAgICAgcmFua3NlcDogMjBcbiAgICAgICAgICAgIHJhbmtkaXI6IFwiTFJcIlxuICAgICAgICAgICAgbWFyZ2lueDogMTBcbiAgICAgICAgICAgIG1hcmdpbnk6IDEwXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgICAgc3ViZ3JhcGhzW2VsLmlkXSA9IHNnXG5cbiAgICAgICAgICBsb2FkSnNvblRvRGFncmUoc2csIGVsKVxuXG4gICAgICAgICAgciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpXG4gICAgICAgICAgZDN0bXBTdmcuc2VsZWN0KCdnJykuY2FsbChyLCBzZylcbiAgICAgICAgICBtYXhXID0gc2cuZ3JhcGgoKS53aWR0aFxuICAgICAgICAgIG1heEggPSBzZy5ncmFwaCgpLmhlaWdo
dFxuXG4gICAgICAgICAgYW5ndWxhci5lbGVtZW50KG1haW5UbXBFbGVtZW50KS5lbXB0eSgpXG5cbiAgICAgICAgY3JlYXRlTm9kZShnLCBkYXRhLCBlbCwgaXNQYXJlbnQsIG1heFcsIG1heEgpXG5cbiAgICAgICAgZXhpc3RpbmdOb2Rlcy5wdXNoIGVsLmlkXG4gICAgICAgIFxuICAgICAgICAjIGNyZWF0ZSBlZGdlcyBmcm9tIGlucHV0cyB0byBjdXJyZW50IG5vZGVcbiAgICAgICAgaWYgZWwuaW5wdXRzP1xuICAgICAgICAgIGZvciBwcmVkIGluIGVsLmlucHV0c1xuICAgICAgICAgICAgY3JlYXRlRWRnZShnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZClcblxuICAgICAgZ1xuXG4gICAgIyBzZWFyY2hlcyBpbiB0aGUgZ2xvYmFsIEpTT05EYXRhIGZvciB0aGUgbm9kZSB3aXRoIHRoZSBnaXZlbiBpZFxuICAgIHNlYXJjaEZvck5vZGUgPSAoZGF0YSwgbm9kZUlEKSAtPlxuICAgICAgZm9yIGkgb2YgZGF0YS5ub2Rlc1xuICAgICAgICBlbCA9IGRhdGEubm9kZXNbaV1cbiAgICAgICAgcmV0dXJuIGVsICBpZiBlbC5pZCBpcyBub2RlSURcbiAgICAgICAgXG4gICAgICAgICMgbG9vayBmb3Igbm9kZXMgdGhhdCBhcmUgaW4gaXRlcmF0aW9uc1xuICAgICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICAgIGZvciBqIG9mIGVsLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgICAgIHJldHVybiBlbC5zdGVwX2Z1bmN0aW9uW2pdICBpZiBlbC5zdGVwX2Z1bmN0aW9uW2pdLmlkIGlzIG5vZGVJRFxuXG4gI
CAgZHJhd0dyYXBoID0gKGRhdGEpIC0+XG4gICAgICBnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoeyBtdWx0aWdyYXBoOiB0cnVlLCBjb21wb3VuZDogdHJ1ZSB9KS5zZXRHcmFwaCh7XG4gICAgICAgIG5vZGVzZXA6IDcwXG4gICAgICAgIGVkZ2VzZXA6IDBcbiAgICAgICAgcmFua3NlcDogNTBcbiAgICAgICAgcmFua2RpcjogXCJMUlwiXG4gICAgICAgIG1hcmdpbng6IDQwXG4gICAgICAgIG1hcmdpbnk6IDQwXG4gICAgICAgIH0pXG5cbiAgICAgIGxvYWRKc29uVG9EYWdyZShnLCBkYXRhKVxuXG4gICAgICByZW5kZXJlciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpXG4gICAgICBkM21haW5TdmdHLmNhbGwocmVuZGVyZXIsIGcpXG5cbiAgICAgIGZvciBpLCBzZyBvZiBzdWJncmFwaHNcbiAgICAgICAgZDNtYWluU3ZnLnNlbGVjdCgnc3ZnLnN2Zy0nICsgaSArICcgZycpLmNhbGwocmVuZGVyZXIsIHNnKVxuXG4gICAgICBuZXdTY2FsZSA9IDAuNVxuXG4gICAgICB4Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS53aWR0aCgpIC0gZy5ncmFwaCgpLndpZHRoICogbmV3U2NhbGUpIC8gMilcbiAgICAgIHlDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLmhlaWdodCgpIC0gZy5ncmFwaCgpLmhlaWdodCAqIG5ld1NjYWxlKSAvIDIpXG5cbiAgICAgIG1haW5ab29tLnNjYWxlKG5ld1NjYWxlKS50cm
Fuc2xhdGUoW3hDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXRdKVxuXG4gICAgICBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB4Q2VudGVyT2Zmc2V0ICsgXCIsIFwiICsgeUNlbnRlck9mZnNldCArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIilcblxuICAgICAgbWFpblpvb20ub24oXCJ6b29tXCIsIC0+XG4gICAgICAgIGV2ID0gZDMuZXZlbnRcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgZXYudHJhbnNsYXRlICsgXCIpIHNjYWxlKFwiICsgZXYuc2NhbGUgKyBcIilcIlxuICAgICAgKVxuICAgICAgbWFpblpvb20oZDNtYWluU3ZnKVxuXG4gICAgc2NvcGUuJHdhdGNoIGF0dHJzLnBsYW4sIChuZXdQbGFuKSAtPlxuICAgICAgZHJhd0dyYXBoKG5ld1BsYW4pIGlmIG5ld1BsYW5cblxuICAgIHJldHVyblxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZGlyZWN0aXZlKCd2ZXJ0ZXgnLCBmdW5jdGlvbigkc3RhdGUpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZSBzZWNvbmRhcnknIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBkYXRhOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5
lclcsIHN2Z0VsO1xuICAgICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpO1xuICAgICAgYW5hbHl6ZVRpbWUgPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIHZhciBiYm94LCBjaGFydCwgc3ZnLCBzdmdHLCB0ZXN0RGF0YTtcbiAgICAgICAgdGVzdERhdGEgPSBbXTtcbiAgICAgICAgYW5ndWxhci5mb3JFYWNoKGRhdGEuZ3JvdXB2ZXJ0ZXguZ3JvdXBtZW1iZXJzLCBmdW5jdGlvbih2ZXJ0ZXgsIGkpIHtcbiAgICAgICAgICB2YXIgdlRpbWU7XG4gICAgICAgICAgdlRpbWUgPSBkYXRhLnZlcnRpY2V0aW1lc1t2ZXJ0ZXgudmVydGV4aWRdO1xuICAgICAgICAgIHJldHVybiB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICAgIGxhYmVsOiB2ZXJ0ZXgudmVydGV4aW5zdGFuY2VuYW1lICsgXCIgKFwiICsgaSArIFwiKVwiLFxuICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiM2NjZcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIlNDSEVEVUxFRFwiXSAqIDEwMCxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJERVBMT1lJTkdcIl0gKiAxMDAsXG4gICAgICAgICAgICAg
ICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH0sIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogXCJEZXBsb3lpbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjYWFhXCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJERVBMT1lJTkdcIl0gKiAxMDAsXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZUaW1lW1wiUlVOTklOR1wiXSAqIDEwMCxcbiAgICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgfSwge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlJ1bm5pbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJSVU5OSU5HXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkZJTklTSEVEXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICBdXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS50aWNrRm9ybWF0KHtcbiAgICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIiksXG4gICAgICAgICAgdGlja1NpemU6IDFcbiAgICAgICAgfSkucHJlZml4KFwic2luZ2xlXCIpLmxhYmVsRm9ybWF0KGZ1bmN0aW9uKGxhYmVsKSB7XG4gICAgICAgI
CAgcmV0dXJuIGxhYmVsO1xuICAgICAgICB9KS5tYXJnaW4oe1xuICAgICAgICAgIGxlZnQ6IDEwMCxcbiAgICAgICAgICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMjQpLnJlbGF0aXZlVGltZSgpO1xuICAgICAgICBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpLmRhdHVtKHRlc3REYXRhKS5jYWxsKGNoYXJ0KTtcbiAgICAgICAgc3ZnRyA9IHN2Zy5zZWxlY3QoXCJnXCIpO1xuICAgICAgICBiYm94ID0gc3ZnR1swXVswXS5nZXRCQm94KCk7XG4gICAgICAgIHJldHVybiBzdmcuYXR0cignaGVpZ2h0JywgYmJveC5oZWlnaHQgKyAzMCk7XG4gICAgICB9O1xuICAgICAgYW5hbHl6ZVRpbWUoc2NvcGUuZGF0YSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0aW1lbGluZScsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIixcbiAgICBzY29wZToge1xuICAgICAgam9iOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5lclcsIHN2Z0VsLCB0cmFuc2xhdGVMYWJlbDtcbiAgICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgIC
AgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKTtcbiAgICAgIHRyYW5zbGF0ZUxhYmVsID0gZnVuY3Rpb24obGFiZWwpIHtcbiAgICAgICAgcmV0dXJuIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKTtcbiAgICAgIH07XG4gICAgICBhbmFseXplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGJib3gsIGNoYXJ0LCBzdmcsIHN2Z0csIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIixcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2NjY2NjY1wiLFxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBkYXRhLm9sZFZbXCJTQ0hFRFVMRURcIl0sXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBkYXRhLm9sZFZbXCJTQ0hFRFVMRURcIl0gKyAzMCxcbiAgICAgICAgICAgICAgdHlwZTogJ3NjaGVkdWxlZCdcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBdXG4gICAgICAgIH0pO1xuICAgICAgICBhbmd1bGFyLmZvckVhY2goZGF0YS5vbGRWLmdyb3VwdmVydGljZXMsIGZ1bmN0aW9uKHZlcnRleCkge1xuICAgICAgICAgIHZhciB2VGltZTtcbiAgICAgICAgICB2VGltZSA9IGRhdGEub2xkVi5ncm91cHZlcnRpY2V0aW1lc1t2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhpZF07XG4gICA
gICAgICAgcmV0dXJuIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGxhYmVsOiB0cmFuc2xhdGVMYWJlbCh2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhuYW1lKSxcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjNjJjZGVhXCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJTVEFSVEVEXCJdLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkVOREVEXCJdLFxuICAgICAgICAgICAgICAgIGxpbms6IHZlcnRleC5ncm91cHZlcnRleGlkLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICBdXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5jbGljayhmdW5jdGlvbihkLCBpLCBkYXR1bSkge1xuICAgICAgICAgIGlmIChkLmxpbmspIHtcbiAgICAgICAgICAgIHJldHVybiAkc3RhdGUuZ28oXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7XG4gICAgICAgICAgICAgIGpvYmlkOiBkYXRhLmppZCxcbiAgICAgICAgICAgICAgdmVydGV4SWQ6IGQubGlua1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KS50aWNrRm9ybWF0KHtcbiAgICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIiksXG4gICAgICAgICAgdGlja1NpemU6
IDFcbiAgICAgICAgfSkucHJlZml4KFwibWFpblwiKS5tYXJnaW4oe1xuICAgICAgICAgIGxlZnQ6IDAsXG4gICAgICAgICAgcmlnaHQ6IDAsXG4gICAgICAgICAgdG9wOiAwLFxuICAgICAgICAgIGJvdHRvbTogMFxuICAgICAgICB9KS5pdGVtSGVpZ2h0KDI0KS5zaG93Qm9yZGVyTGluZSgpLnNob3dIb3VyVGltZWxpbmUoKTtcbiAgICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCk7XG4gICAgICAgIHN2Z0cgPSBzdmcuc2VsZWN0KFwiZ1wiKTtcbiAgICAgICAgYmJveCA9IHN2Z0dbMF1bMF0uZ2V0QkJveCgpO1xuICAgICAgICByZXR1cm4gc3ZnLmF0dHIoJ2hlaWdodCcsIGJib3guaGVpZ2h0ICsgMzApO1xuICAgICAgfTtcbiAgICAgIHNjb3BlLiR3YXRjaChhdHRycy5qb2IsIGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICByZXR1cm4gYW5hbHl6ZVRpbWUoZGF0YSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgfTtcbn0pLmRpcmVjdGl2ZSgnam9iUGxhbicsIGZ1bmN0aW9uKCR0aW1lb3V0KSB7XG4gIHJldHVybiB7XG4gICAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0nZ3JhcGgnIHdpZHRoPSc1MDAnIGhlaWdodD0nNDAwJz48ZyAvPjwvc3ZnPiA8c3ZnIGNsYXNzPSd0bXAnIHdpZHRoPScxJyBoZWlnaHQ9JzEnPjxnIC8+PC9zdmc+IDxkaXYgY2xhc3M9J2J0bi1ncm91cCB6b29tLWJ1dHRvbnMnPiA8YSBjb
GFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20taW4nIG5nLWNsaWNrPSd6b29tSW4oKSc+PGkgY2xhc3M9J2ZhIGZhLXBsdXMnIC8+PC9hPiA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20tb3V0JyBuZy1jbGljaz0nem9vbU91dCgpJz48aSBjbGFzcz0nZmEgZmEtbWludXMnIC8+PC9hPiA8L2Rpdj5cIixcbiAgICBzY29wZToge1xuICAgICAgcGxhbjogJz0nXG4gICAgfSxcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbSwgYXR0cnMpIHtcbiAgICAgIHZhciBjb250YWluZXJXLCBjcmVhdGVFZGdlLCBjcmVhdGVMYWJlbEVkZ2UsIGNyZWF0ZUxhYmVsTm9kZSwgY3JlYXRlTm9kZSwgZDNtYWluU3ZnLCBkM21haW5TdmdHLCBkM3RtcFN2ZywgZHJhd0dyYXBoLCBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24sIGdldE5vZGVUeXBlLCBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlLCBqb2JpZCwgbG9hZEpzb25Ub0RhZ3JlLCBtYWluRywgbWFpblN2Z0VsZW1lbnQsIG1haW5UbXBFbGVtZW50LCBtYWluWm9vbSwgc2VhcmNoRm9yTm9kZSwgc2hvcnRlblN0cmluZywgc3ViZ3JhcGhzO1xuICAgICAgbWFpblpvb20gPSBkMy5iZWhhdmlvci56b29tKCk7XG4gICAgICBzdWJncmFwaHMgPSBbXTtcbiAgICAgIGpvYmlkID0gYXR0cnMuam9iaWQ7XG4gICAgICBtYWluU3ZnRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVswXTtcbiAgICAgIG1haW5HID0gZWxlbS5jaGlsZHJlbigpLmNoaWxkcmVuKClbMF07XG4gICAgIC
BtYWluVG1wRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVsxXTtcbiAgICAgIGQzbWFpblN2ZyA9IGQzLnNlbGVjdChtYWluU3ZnRWxlbWVudCk7XG4gICAgICBkM21haW5TdmdHID0gZDMuc2VsZWN0KG1haW5HKTtcbiAgICAgIGQzdG1wU3ZnID0gZDMuc2VsZWN0KG1haW5UbXBFbGVtZW50KTtcbiAgICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKCk7XG4gICAgICBhbmd1bGFyLmVsZW1lbnQoZWxlbS5jaGlsZHJlbigpWzBdKS53aWR0aChjb250YWluZXJXKTtcbiAgICAgIHNjb3BlLnpvb21JbiA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgdHJhbnNsYXRlLCB2MSwgdjI7XG4gICAgICAgIGlmIChtYWluWm9vbS5zY2FsZSgpIDwgMi45OSkge1xuICAgICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpO1xuICAgICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpO1xuICAgICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpO1xuICAgICAgICAgIG1haW5ab29tLnNjYWxlKG1haW5ab29tLnNjYWxlKCkgKyAwLjEpO1xuICAgICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZShbdjEsIHYyXSk7XG4gICAgICAgICAgcmV0dXJuIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFw
iKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIik7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBzY29wZS56b29tT3V0ID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB0cmFuc2xhdGUsIHYxLCB2MjtcbiAgICAgICAgaWYgKG1haW5ab29tLnNjYWxlKCkgPiAwLjMxKSB7XG4gICAgICAgICAgbWFpblpvb20uc2NhbGUobWFpblpvb20uc2NhbGUoKSAtIDAuMSk7XG4gICAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKCk7XG4gICAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgbWFpblpvb20udHJhbnNsYXRlKFt2MSwgdjJdKTtcbiAgICAgICAgICByZXR1cm4gZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNyZWF0ZUxhYmVsRWRnZSA9IGZ1bmN0aW9uKGVsKSB7XG4gICAgICAgIHZhciBsYWJlbFZhbHVlO1xuICAgICAgICBsYWJlbFZhbHVlID0gXCJcIjtcbiAgICAgICAgaWYgKChlbC5zaGlwX3N0cmF0ZWd5ICE9IG51bGwpIHx8IChl
bC5sb2NhbF9zdHJhdGVneSAhPSBudWxsKSkge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8ZGl2IGNsYXNzPSdlZGdlLWxhYmVsJz5cIjtcbiAgICAgICAgICBpZiAoZWwuc2hpcF9zdHJhdGVneSAhPSBudWxsKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IGVsLnNoaXBfc3RyYXRlZ3k7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC50ZW1wX21vZGUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiAoXCIgKyBlbC50ZW1wX21vZGUgKyBcIilcIjtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLmxvY2FsX3N0cmF0ZWd5ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCIsPGJyPlwiICsgZWwubG9jYWxfc3RyYXRlZ3k7XG4gICAgICAgICAgfVxuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2Rpdj5cIjtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbGFiZWxWYWx1ZTtcbiAgICAgIH07XG4gICAgICBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlID0gZnVuY3Rpb24oaW5mbykge1xuICAgICAgICByZXR1cm4gaW5mbyA9PT0gXCJwYXJ0aWFsU29sdXRpb25cIiB8fCBpbmZvID09PSBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiB8fCBpbmZvID09PSBcIndvcmtzZXRcIiB8fCBpbmZvID09PSBcIm5leHRXb3Jrc2V0XCIgfHwgaW5mbyA9PT0gXCJzb2x1dGlvblNldFwiIHx8IGluZm8gPT09IFwic29sdXRpb25EZWx0YVwiO
1xuICAgICAgfTtcbiAgICAgIGdldE5vZGVUeXBlID0gZnVuY3Rpb24oZWwsIGluZm8pIHtcbiAgICAgICAgaWYgKGluZm8gPT09IFwibWlycm9yXCIpIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtbWlycm9yJztcbiAgICAgICAgfSBlbHNlIGlmIChpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pKSB7XG4gICAgICAgICAgcmV0dXJuICdub2RlLWl0ZXJhdGlvbic7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuICdub2RlLW5vcm1hbCc7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjcmVhdGVMYWJlbE5vZGUgPSBmdW5jdGlvbihlbCwgaW5mbywgbWF4VywgbWF4SCkge1xuICAgICAgICB2YXIgbGFiZWxWYWx1ZSwgc3RlcE5hbWU7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIjxhIGhyZWY9JyMvam9icy9cIiArIGpvYmlkICsgXCIvdmVydGV4L1wiICsgZWwuaWQgKyBcIicgY2xhc3M9J25vZGUtbGFiZWwgXCIgKyBnZXROb2RlVHlwZShlbCwgaW5mbykgKyBcIic+XCI7XG4gICAgICAgIGlmIChpbmZvID09PSBcIm1pcnJvclwiKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5NaXJyb3Igb2YgXCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPlwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCI7XG4gICAgICAgIH1cbiAgIC
AgICAgaWYgKGVsLmRlc2NyaXB0aW9uID09PSBcIlwiKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHN0ZXBOYW1lID0gZWwuZGVzY3JpcHRpb247XG4gICAgICAgICAgc3RlcE5hbWUgPSBzaG9ydGVuU3RyaW5nKHN0ZXBOYW1lKTtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg0IGNsYXNzPSdzdGVwLW5hbWUnPlwiICsgc3RlcE5hbWUgKyBcIjwvaDQ+XCI7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVsLnN0ZXBfZnVuY3Rpb24gIT0gbnVsbCkge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uKGVsLmlkLCBtYXhXLCBtYXhIKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpZiAoaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKSkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5cIiArIGluZm8gKyBcIiBOb2RlPC9oNT5cIjtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLnBhcmFsbGVsaXNtICE9PSBcIlwiKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlBhcmFsbGVsaXNtOiBcIiArIGVsLnBhcmFsbGVsaXNtICsgXCI8L2g1PlwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwub3BlcmF0b3IgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5PcGVyYXRpb246IFwiICsgc2hvcnRlblN0cml
uZyhlbC5vcGVyYXRvcl9zdHJhdGVneSkgKyBcIjwvaDU+XCI7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2E+XCI7XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiA9IGZ1bmN0aW9uKGlkLCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIHZhciBsYWJlbFZhbHVlLCBzdmdJRDtcbiAgICAgICAgc3ZnSUQgPSBcInN2Zy1cIiArIGlkO1xuICAgICAgICBsYWJlbFZhbHVlID0gXCI8c3ZnIGNsYXNzPSdcIiArIHN2Z0lEICsgXCInIHdpZHRoPVwiICsgbWF4VyArIFwiIGhlaWdodD1cIiArIG1heEggKyBcIj48ZyAvPjwvc3ZnPlwiO1xuICAgICAgICByZXR1cm4gbGFiZWxWYWx1ZTtcbiAgICAgIH07XG4gICAgICBzaG9ydGVuU3RyaW5nID0gZnVuY3Rpb24ocykge1xuICAgICAgICB2YXIgc2JyO1xuICAgICAgICBpZiAocy5jaGFyQXQoMCkgPT09IFwiPFwiKSB7XG4gICAgICAgICAgcyA9IHMucmVwbGFjZShcIjxcIiwgXCImbHQ7XCIpO1xuICAgICAgICAgIHMgPSBzLnJlcGxhY2UoXCI+XCIsIFwiJmd0O1wiKTtcbiAgICAgICAgfVxuICAgICAgICBzYnIgPSBcIlwiO1xuICAgICAgICB3aGlsZSAocy5sZW5ndGggPiAzMCkge1xuICAgICAgICAgIHNiciA9IHNiciArIHMuc3Vic3RyaW5nKDAsIDMwKSArIFwiPGJyPlwiO1xuICAgICAgICAgIHMgPSBzLnN1YnN0cmluZygzMCwgcy5sZW5ndGgp
O1xuICAgICAgICB9XG4gICAgICAgIHNiciA9IHNiciArIHM7XG4gICAgICAgIHJldHVybiBzYnI7XG4gICAgICB9O1xuICAgICAgY3JlYXRlTm9kZSA9IGZ1bmN0aW9uKGcsIGRhdGEsIGVsLCBpc1BhcmVudCwgbWF4VywgbWF4SCkge1xuICAgICAgICBpZiAoaXNQYXJlbnQgPT0gbnVsbCkge1xuICAgICAgICAgIGlzUGFyZW50ID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVsLmlkID09PSBkYXRhLnBhcnRpYWxfc29sdXRpb24pIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5uZXh0X3BhcnRpYWxfc29sdXRpb24pIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiKVxuI
CAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLndvcmtzZXQpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIndvcmtzZXRcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwid29ya3NldFwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLm5leHRfd29ya3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFdvcmtzZXRcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFdvcmtzZXRcIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5zb2x1dGlvbl9zZXQpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uU2V0XCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeX
BlKGVsLCBcInNvbHV0aW9uU2V0XCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEuc29sdXRpb25fZGVsdGEpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uRGVsdGFcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25EZWx0YVwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwiXCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcIlwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlRWRnZSA9IGZ1bmN0aW9uKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKSB7XG4gICAgICAgIHZhciBtaXNzaW5nTm9kZTtcbiAgICAgICAgaWYgKGV4aXN0aW5nTm9kZXMuaW5kZXhPZihwcmVkLmlkKSAhPT0gLTEpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXRFZGdlKHByZWQuaWQsIGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3J
lYXRlTGFiZWxFZGdlKHByZWQpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBhcnJvd2hlYWQ6ICdub3JtYWwnXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbWlzc2luZ05vZGUgPSBzZWFyY2hGb3JOb2RlKGRhdGEsIHByZWQuaWQpO1xuICAgICAgICAgIGlmICghKCFtaXNzaW5nTm9kZSB8fCBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgPT09IHRydWUpKSB7XG4gICAgICAgICAgICBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgPSB0cnVlO1xuICAgICAgICAgICAgZy5zZXROb2RlKG1pc3NpbmdOb2RlLmlkLCB7XG4gICAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUobWlzc2luZ05vZGUsIFwibWlycm9yXCIpLFxuICAgICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShtaXNzaW5nTm9kZSwgJ21pcnJvcicpXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHJldHVybiBnLnNldEVkZ2UobWlzc2luZ05vZGUuaWQsIGVsLmlkLCB7XG4gICAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UobWlzc2luZ05vZGUpLFxuICAgICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgbG9hZEpzb25Ub0RhZ3JlID0gZnVuY3Rpb24oZywgZGF0YSkge1xuICAg
ICAgICB2YXIgZWwsIGV4aXN0aW5nTm9kZXMsIGlzUGFyZW50LCBrLCBsLCBsZW4sIGxlbjEsIG1heEgsIG1heFcsIHByZWQsIHIsIHJlZiwgc2csIHRvSXRlcmF0ZTtcbiAgICAgICAgZXhpc3RpbmdOb2RlcyA9IFtdO1xuICAgICAgICBpZiAoZGF0YS5ub2RlcyAhPSBudWxsKSB7XG4gICAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5ub2RlcztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLnN0ZXBfZnVuY3Rpb247XG4gICAgICAgICAgaXNQYXJlbnQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIGZvciAoayA9IDAsIGxlbiA9IHRvSXRlcmF0ZS5sZW5ndGg7IGsgPCBsZW47IGsrKykge1xuICAgICAgICAgIGVsID0gdG9JdGVyYXRlW2tdO1xuICAgICAgICAgIG1heFcgPSAwO1xuICAgICAgICAgIG1heEggPSAwO1xuICAgICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uKSB7XG4gICAgICAgICAgICBzZyA9IG5ldyBkYWdyZUQzLmdyYXBobGliLkdyYXBoKHtcbiAgICAgICAgICAgICAgbXVsdGlncmFwaDogdHJ1ZSxcbiAgICAgICAgICAgICAgY29tcG91bmQ6IHRydWVcbiAgICAgICAgICAgIH0pLnNldEdyYXBoKHtcbiAgICAgICAgICAgICAgbm9kZXNlcDogMjAsXG4gICAgICAgICAgICAgIGVkZ2VzZXA6IDAsXG4gICAgICAgICAgICAgIHJhbmtzZXA6IDIwLFxuICAgICAgICAgICAgICByYW5rZGlyOiBcIkxSXCIsXG4gICAgICAgICAgICAgIG1hcmdpbng6IDEwLFxuICAgI
CAgICAgICAgICBtYXJnaW55OiAxMFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBzdWJncmFwaHNbZWwuaWRdID0gc2c7XG4gICAgICAgICAgICBsb2FkSnNvblRvRGFncmUoc2csIGVsKTtcbiAgICAgICAgICAgIHIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKTtcbiAgICAgICAgICAgIGQzdG1wU3ZnLnNlbGVjdCgnZycpLmNhbGwociwgc2cpO1xuICAgICAgICAgICAgbWF4VyA9IHNnLmdyYXBoKCkud2lkdGg7XG4gICAgICAgICAgICBtYXhIID0gc2cuZ3JhcGgoKS5oZWlnaHQ7XG4gICAgICAgICAgICBhbmd1bGFyLmVsZW1lbnQobWFpblRtcEVsZW1lbnQpLmVtcHR5KCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNyZWF0ZU5vZGUoZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXhXLCBtYXhIKTtcbiAgICAgICAgICBleGlzdGluZ05vZGVzLnB1c2goZWwuaWQpO1xuICAgICAgICAgIGlmIChlbC5pbnB1dHMgIT0gbnVsbCkge1xuICAgICAgICAgICAgcmVmID0gZWwuaW5wdXRzO1xuICAgICAgICAgICAgZm9yIChsID0gMCwgbGVuMSA9IHJlZi5sZW5ndGg7IGwgPCBsZW4xOyBsKyspIHtcbiAgICAgICAgICAgICAgcHJlZCA9IHJlZltsXTtcbiAgICAgICAgICAgICAgY3JlYXRlRWRnZShnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBnO1xuICAgICAgfTtcbiAgICAgIHNlYXJjaEZvck5vZGUgPS
BmdW5jdGlvbihkYXRhLCBub2RlSUQpIHtcbiAgICAgICAgdmFyIGVsLCBpLCBqO1xuICAgICAgICBmb3IgKGkgaW4gZGF0YS5ub2Rlcykge1xuICAgICAgICAgIGVsID0gZGF0YS5ub2Rlc1tpXTtcbiAgICAgICAgICBpZiAoZWwuaWQgPT09IG5vZGVJRCkge1xuICAgICAgICAgICAgcmV0dXJuIGVsO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwuc3RlcF9mdW5jdGlvbiAhPSBudWxsKSB7XG4gICAgICAgICAgICBmb3IgKGogaW4gZWwuc3RlcF9mdW5jdGlvbikge1xuICAgICAgICAgICAgICBpZiAoZWwuc3RlcF9mdW5jdGlvbltqXS5pZCA9PT0gbm9kZUlEKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGVsLnN0ZXBfZnVuY3Rpb25bal07XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBkcmF3R3JhcGggPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIHZhciBnLCBpLCBuZXdTY2FsZSwgcmVuZGVyZXIsIHNnLCB4Q2VudGVyT2Zmc2V0LCB5Q2VudGVyT2Zmc2V0O1xuICAgICAgICBnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoe1xuICAgICAgICAgIG11bHRpZ3JhcGg6IHRydWUsXG4gICAgICAgICAgY29tcG91bmQ6IHRydWVcbiAgICAgICAgfSkuc2V0R3JhcGgoe1xuICAgICAgICAgIG5vZGVzZXA6IDcwLFxuICAgICAgICAgIGVkZ2VzZXA6IDAsXG4gICAgICAgICAgcmFua3NlcDogNTAsXG4gICAgICAgICA
gcmFua2RpcjogXCJMUlwiLFxuICAgICAgICAgIG1hcmdpbng6IDQwLFxuICAgICAgICAgIG1hcmdpbnk6IDQwXG4gICAgICAgIH0pO1xuICAgICAgICBsb2FkSnNvblRvRGFncmUoZywgZGF0YSk7XG4gICAgICAgIHJlbmRlcmVyID0gbmV3IGRhZ3JlRDMucmVuZGVyKCk7XG4gICAgICAgIGQzbWFpblN2Z0cuY2FsbChyZW5kZXJlciwgZyk7XG4gICAgICAgIGZvciAoaSBpbiBzdWJncmFwaHMpIHtcbiAgICAgICAgICBzZyA9IHN1YmdyYXBoc1tpXTtcbiAgICAgICAgICBkM21haW5Tdmcuc2VsZWN0KCdzdmcuc3ZnLScgKyBpICsgJyBnJykuY2FsbChyZW5kZXJlciwgc2cpO1xuICAgICAgICB9XG4gICAgICAgIG5ld1NjYWxlID0gMC41O1xuICAgICAgICB4Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS53aWR0aCgpIC0gZy5ncmFwaCgpLndpZHRoICogbmV3U2NhbGUpIC8gMik7XG4gICAgICAgIHlDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLmhlaWdodCgpIC0gZy5ncmFwaCgpLmhlaWdodCAqIG5ld1NjYWxlKSAvIDIpO1xuICAgICAgICBtYWluWm9vbS5zY2FsZShuZXdTY2FsZSkudHJhbnNsYXRlKFt4Q2VudGVyT2Zmc2V0LCB5Q2VudGVyT2Zmc2V0XSk7XG4gICAgICAgIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHhDZW50ZXJPZmZzZXQgKyBcIiwgXCIg
KyB5Q2VudGVyT2Zmc2V0ICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKTtcbiAgICAgICAgbWFpblpvb20ub24oXCJ6b29tXCIsIGZ1bmN0aW9uKCkge1xuICAgICAgICAgIHZhciBldjtcbiAgICAgICAgICBldiA9IGQzLmV2ZW50O1xuICAgICAgICAgIHJldHVybiBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyBldi50cmFuc2xhdGUgKyBcIikgc2NhbGUoXCIgKyBldi5zY2FsZSArIFwiKVwiKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBtYWluWm9vbShkM21haW5TdmcpO1xuICAgICAgfTtcbiAgICAgIHNjb3BlLiR3YXRjaChhdHRycy5wbGFuLCBmdW5jdGlvbihuZXdQbGFuKSB7XG4gICAgICAgIGlmIChuZXdQbGFuKSB7XG4gICAgICAgICAgcmV0dXJuIGRyYXdHcmFwaChuZXdQbGFuKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZ
SBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLnNlcnZpY2UgJ0pvYnNTZXJ2aWNlJywgKCRodHRwLCBmbGlua0NvbmZpZywgJGxvZywgYW1Nb21lbnQsICRxLCAkdGltZW91dCkgLT5cbiAgY3VycmVudEpvYiA9IG51bGxcbiAgY3VycmVudFBsYW4gPSBudWxsXG4gIGRlZmVycmVkcyA9IHt9XG4gIGpvYnMgPSB7XG4gICAgcnVubmluZzogW11cbiAgIC
BmaW5pc2hlZDogW11cbiAgICBjYW5jZWxsZWQ6IFtdXG4gICAgZmFpbGVkOiBbXVxuICB9XG5cbiAgam9iT2JzZXJ2ZXJzID0gW11cblxuICBub3RpZnlPYnNlcnZlcnMgPSAtPlxuICAgIGFuZ3VsYXIuZm9yRWFjaCBqb2JPYnNlcnZlcnMsIChjYWxsYmFjaykgLT5cbiAgICAgIGNhbGxiYWNrKClcblxuICBAcmVnaXN0ZXJPYnNlcnZlciA9IChjYWxsYmFjaykgLT5cbiAgICBqb2JPYnNlcnZlcnMucHVzaChjYWxsYmFjaylcblxuICBAdW5SZWdpc3Rlck9ic2VydmVyID0gKGNhbGxiYWNrKSAtPlxuICAgIGluZGV4ID0gam9iT2JzZXJ2ZXJzLmluZGV4T2YoY2FsbGJhY2spXG4gICAgam9iT2JzZXJ2ZXJzLnNwbGljZShpbmRleCwgMSlcblxuICBAc3RhdGVMaXN0ID0gLT5cbiAgICBbIFxuICAgICAgIyAnQ1JFQVRFRCdcbiAgICAgICdTQ0hFRFVMRUQnXG4gICAgICAnREVQTE9ZSU5HJ1xuICAgICAgJ1JVTk5JTkcnXG4gICAgICAnRklOSVNIRUQnXG4gICAgICAnRkFJTEVEJ1xuICAgICAgJ0NBTkNFTElORydcbiAgICAgICdDQU5DRUxFRCdcbiAgICBdXG5cbiAgQHRyYW5zbGF0ZUxhYmVsU3RhdGUgPSAoc3RhdGUpIC0+XG4gICAgc3dpdGNoIHN0YXRlLnRvTG93ZXJDYXNlKClcbiAgICAgIHdoZW4gJ2ZpbmlzaGVkJyB0aGVuICdzdWNjZXNzJ1xuICAgICAgd2hlbiAnZmFpbGVkJyB0aGVuICdkYW5nZXInXG4gICAgICB3aGVuICdzY2hlZHVsZWQnIHRoZW4gJ2RlZmF1bHQnXG4gICAgICB3aGVuICdkZXBsb3lpbmc
nIHRoZW4gJ2luZm8nXG4gICAgICB3aGVuICdydW5uaW5nJyB0aGVuICdwcmltYXJ5J1xuICAgICAgd2hlbiAnY2FuY2VsaW5nJyB0aGVuICd3YXJuaW5nJ1xuICAgICAgd2hlbiAncGVuZGluZycgdGhlbiAnaW5mbydcbiAgICAgIHdoZW4gJ3RvdGFsJyB0aGVuICdibGFjaydcbiAgICAgIGVsc2UgJ2RlZmF1bHQnXG5cbiAgQGxpc3RKb2JzID0gLT5cbiAgICBkZWZlcnJlZCA9ICRxLmRlZmVyKClcblxuICAgICRodHRwLmdldCBmbGlua0NvbmZpZy5uZXdTZXJ2ZXIgKyBcIi9qb2JzXCJcbiAgICAuc3VjY2VzcyAoZGF0YSwgc3RhdHVzLCBoZWFkZXJzLCBjb25maWcpIC0+XG5cbiAgICAgIGFuZ3VsYXIuZm9yRWFjaCBkYXRhLCAobGlzdCwgbGlzdEtleSkgLT5cblxuICAgICAgICBzd2l0Y2ggbGlzdEtleVxuICAgICAgICAgIHdoZW4gJ2pvYnMtcnVubmluZycgdGhlbiBqb2JzLnJ1bm5pbmcgPSBsaXN0XG4gICAgICAgICAgd2hlbiAnam9icy1maW5pc2hlZCcgdGhlbiBqb2JzLmZpbmlzaGVkID0gbGlzdFxuICAgICAgICAgIHdoZW4gJ2pvYnMtY2FuY2VsbGVkJyB0aGVuIGpvYnMuY2FuY2VsbGVkID0gbGlzdFxuICAgICAgICAgIHdoZW4gJ2pvYnMtZmFpbGVkJyB0aGVuIGpvYnMuZmFpbGVkID0gbGlzdFxuXG4gICAgICAgIGFuZ3VsYXIuZm9yRWFjaCBsaXN0LCAoam9iaWQsIGluZGV4KSAtPlxuICAgICAgICAgICRodHRwLmdldCBmbGlua0NvbmZpZy5uZXdTZXJ2ZXIgKyBcIi9qb2JzL1wiICsgam9iaWRcbiAg
ICAgICAgICAuc3VjY2VzcyAoZGV0YWlscykgLT5cbiAgICAgICAgICAgIGxpc3RbaW5kZXhdID0gZGV0YWlsc1xuXG4gICAgICBkZWZlcnJlZC5yZXNvbHZlKGpvYnMpXG4gICAgICBub3RpZnlPYnNlcnZlcnMoKVxuXG4gICAgZGVmZXJyZWQucHJvbWlzZVxuXG4gIEBnZXRKb2JzID0gKHR5cGUpIC0+XG4gICAgam9ic1t0eXBlXVxuXG4gIEBnZXRBbGxKb2JzID0gLT5cbiAgICBqb2JzXG5cbiAgQGxvYWRKb2IgPSAoam9iaWQpIC0+XG4gICAgY3VycmVudEpvYiA9IG51bGxcbiAgICBkZWZlcnJlZHMuam9iID0gJHEuZGVmZXIoKVxuXG4gICAgJGh0dHAuZ2V0IGZsaW5rQ29uZmlnLm5ld1NlcnZlciArIFwiL2pvYnMvXCIgKyBqb2JpZFxuICAgIC5zdWNjZXNzIChkYXRhLCBzdGF0dXMsIGhlYWRlcnMsIGNvbmZpZykgLT5cbiAgICAgIGRhdGEudGltZSA9IERhdGUubm93KClcblxuICAgICAgJGh0dHAuZ2V0IGZsaW5rQ29uZmlnLm5ld1NlcnZlciArIFwiL2pvYnMvXCIgKyBqb2JpZCArIFwiL3ZlcnRpY2VzXCJcbiAgICAgIC5zdWNjZXNzICh2ZXJ0aWNlcykgLT5cbiAgICAgICAgZGF0YSA9IGFuZ3VsYXIuZXh0ZW5kKGRhdGEsIHZlcnRpY2VzKVxuXG4gICAgICAgICRodHRwLmdldCBmbGlua0NvbmZpZy5qb2JTZXJ2ZXIgKyBcIi9qb2JzSW5mbz9nZXQ9am9iJmpvYj1cIiArIGpvYmlkXG4gICAgICAgIC5zdWNjZXNzIChvbGRWZXJ0aWNlcykgLT5cbiAgICAgICAgICBkYXRhLm9sZFYgPSBvbGRWZXJ0aWNlc1swX
VxuXG4gICAgICAgICAgJGh0dHAuZ2V0IGZsaW5rQ29uZmlnLmpvYlNlcnZlciArIFwiL2pvYnMvXCIgKyBqb2JpZCArIFwiL2NvbmZpZ1wiXG4gICAgICAgICAgLnN1Y2Nlc3MgKGpvYkNvbmZpZykgLT5cbiAgICAgICAgICAgIGRhdGEgPSBhbmd1bGFyLmV4dGVuZChkYXRhLCBqb2JDb25maWcpXG5cbiAgICAgICAgICAgIGN1cnJlbnRKb2IgPSBkYXRhXG4gICAgICAgICAgICBkZWZlcnJlZHMuam9iLnJlc29sdmUoZGF0YSlcblxuICAgIGRlZmVycmVkcy5qb2IucHJvbWlzZVxuXG4gIEBsb2FkUGxhbiA9IChqb2JpZCkgLT5cbiAgIC
<TRUNCATED>
[25/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Extend
exceptions view
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Extend exceptions view
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/749c0df6
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/749c0df6
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/749c0df6
Branch: refs/heads/master
Commit: 749c0df6ad15be7bda8d1f34ca25f30b478e0a74
Parents: 99a351e
Author: Piotr Godek <pi...@gmail.com>
Authored: Thu Aug 27 21:31:13 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:51 2015 +0200
----------------------------------------------------------------------
.../web-dashboard/app/partials/jobs/job.exceptions.jade | 9 +++++++++
.../web-dashboard/web/partials/jobs/job.exceptions.html | 8 ++++++++
2 files changed, 17 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/749c0df6/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
index d2a0d58..0ea4c7f 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
@@ -18,6 +18,15 @@
.panel.panel-default.panel-multi(ng-repeat="exception in exceptions['all-exceptions']" )
.panel-heading.clearfix
.panel-title
+ | Root exception
+
+ .panel-body
+ pre.exception
+ {{ exceptions['root-exception'] }}
+
+.panel.panel-default.panel-multi(ng-repeat="exception in exceptions['all-exceptions']" )
+ .panel-heading.clearfix
+ .panel-title
| {{ exception.task }}
.panel-heading.clearfix
http://git-wip-us.apache.org/repos/asf/flink/blob/749c0df6/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
index 7c88ca2..fc43714 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
@@ -19,6 +19,14 @@ limitations under the License.
-->
<div ng-repeat="exception in exceptions['all-exceptions']" class="panel panel-default panel-multi">
<div class="panel-heading clearfix">
+ <div class="panel-title">Root exception</div>
+ </div>
+ <div class="panel-body">
+ <pre class="exception">{{ exceptions['root-exception'] }}</pre>
+ </div>
+</div>
+<div ng-repeat="exception in exceptions['all-exceptions']" class="panel panel-default panel-multi">
+ <div class="panel-heading clearfix">
<div class="panel-title">{{ exception.task }}</div>
</div>
<div class="panel-heading clearfix">
[38/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Add
subtasks to vertex display
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Add subtasks to vertex display
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/93a4d31d
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/93a4d31d
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/93a4d31d
Branch: refs/heads/master
Commit: 93a4d31d9e5d0ffa1ef2b578d1eee8088fa7c0e1
Parents: b3f7917
Author: Piotr Godek <pi...@gmail.com>
Authored: Fri Sep 11 22:37:13 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:52 2015 +0200
----------------------------------------------------------------------
.../jobs/job.plan.node-list.accumulators.jade | 4 +-
.../jobs/job.plan.node-list.overview.jade | 2 +-
.../jobs/job.plan.node.accumulators.jade | 53 +-
.../partials/jobs/job.plan.node.subtasks.jade | 4 +-
.../app/partials/jobs/job.timeline.jade | 2 +-
.../app/partials/jobs/job.timeline.vertex.jade | 3 -
.../app/scripts/modules/jobs/jobs.ctrl.coffee | 44 +-
.../app/scripts/modules/jobs/jobs.dir.coffee | 55 +-
.../app/scripts/modules/jobs/jobs.svc.coffee | 35 +-
.../web-dashboard/app/styles/animate.styl | 1907 ------------------
.../web-dashboard/app/styles/index.styl | 1 -
.../web-dashboard/app/styles/job.styl | 5 +
flink-runtime-web/web-dashboard/bower.json | 2 +-
.../web-dashboard/web/css/index.css | 5 +
flink-runtime-web/web-dashboard/web/js/index.js | 168 +-
.../jobs/job.plan.node-list.accumulators.html | 4 +-
.../jobs/job.plan.node-list.overview.html | 2 +-
.../jobs/job.plan.node.accumulators.html | 46 +-
.../partials/jobs/job.plan.node.subtasks.html | 4 +-
.../web/partials/jobs/job.timeline.html | 2 +-
20 files changed, 291 insertions(+), 2057 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.accumulators.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.accumulators.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.accumulators.jade
index b3f1f54..ac210af 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.accumulators.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.accumulators.jade
@@ -21,8 +21,8 @@ table.table.table-body-hover.table-clickable.table-activable
th Name
th Status
- tbody(ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="changeNode(v.id)")
- tr
+ tbody(ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="v.id == nodeid || changeNode(v.id)")
+ tr(ng-if="v.type == 'regular'")
td {{ v.name | humanizeText }}
td
bs-label(status="{{v.status}}") {{v.status}}
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.overview.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.overview.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.overview.jade
index fbdc012..5145bf9 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.overview.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.overview.jade
@@ -30,7 +30,7 @@ table.table.table-body-hover.table-clickable.table-activable
th Status
tbody(ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="changeNode(v.id)")
- tr
+ tr(ng-if="v.type == 'regular'")
td
span(ng-if="v['start-time'] > -1") {{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
td
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
index 4cb93fa..8f0313b 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
@@ -18,13 +18,14 @@
//- center(ng-if="!vertex")
//- i.fa.fa-circle-o-notch.fa-spin.fa-3x
-div(ng-if="vertex")
+//- div
- div(ng-if="vertex.accumulators.length == 0")
- p
- i No accumulators
-
- table.table.table-hover.table-clickable.table-activable.table-inner(ng-if="vertex.accumulators.length > 0")
+div(ng-if="accumulators.length == 0")
+ p
+ i No accumulators
+
+div(ng-if="accumulators && accumulators.length > 0")
+ table.table.table-hover.table-clickable.table-activable.table-inner
thead
tr
th Name
@@ -32,7 +33,39 @@ div(ng-if="vertex")
th Value
tbody
- tr(ng-repeat="accumulator in vertex.accumulators")
- td {{ accumulator.name }}
- td {{ accumulator.type }}
- td {{ accumulator.value }}
+ tr(ng-repeat="accumulator in accumulators")
+ td(width="30%") {{ accumulator.name }}
+ td(width="30%") {{ accumulator.type }}
+ td(width="30%") {{ accumulator.value }}
+
+ div(ng-if="!nodeUnfolded")
+ a.btn.btn-default(ng-click="toggleFold()")
+ | Show subtasks
+ |
+ i.fa.fa-chevron-down
+
+ a.btn.btn-default.pull-right(ng-click="deactivateNode(); $event.stopPropagation()" title="Fold")
+ i.fa.fa-chevron-up
+
+ div(ng-if="nodeUnfolded && subtaskAccumulators && subtaskAccumulators.length > 0")
+ a.btn.btn-default(ng-click="toggleFold()")
+ | Hide subtasks
+ |
+ i.fa.fa-chevron-up
+
+ table.table.table-hover.table-clickable.table-activable.table-inner
+ thead
+ tr
+ th Name
+ th Type
+ th Value
+
+ tbody(ng-if="subtask['user-accumulators'] && subtask['user-accumulators'].length > 0" ng-repeat="subtask in subtaskAccumulators")
+ tr
+ td(colwidth="3")
+ .small-label ({{ subtask.subtask }}) {{ subtask.host }}, attempt: {{ subtask.attempt + 1 }}
+
+ tr(ng-repeat="accumulator in subtask['user-accumulators']")
+ td(width="30%") {{ accumulator.name }}
+ td(width="30%") {{ accumulator.type }}
+ td(width="30%") {{ accumulator.value }}
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
index 3169e1a..9cfed06 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
@@ -19,7 +19,7 @@
//- center(ng-if="!vertex")
//- i.fa.fa-circle-o-notch.fa-spin.fa-3x
-table.table.table-hover.table-clickable.table-activable.table-inner(ng-if="vertex")
+table.table.table-hover.table-clickable.table-activable.table-inner(ng-if="subtasks")
thead
tr
th Start Time
@@ -34,7 +34,7 @@ table.table.table-hover.table-clickable.table-activable.table-inner(ng-if="verte
th Status
tbody
- tr(ng-repeat="subtask in vertex.st")
+ tr(ng-repeat="subtask in subtasks")
td
span(ng-if="subtask['start-time'] > -1") {{ subtask['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
td
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.jade
index d7b4a31..fdf0561 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.jade
@@ -16,6 +16,6 @@
limitations under the License.
.canvas-wrapper
- div.timeline-canvas(timeline job="job")
+ div.timeline-canvas(timeline vertices="vertices" jobid="jobid")
div(ui-view="vertex")
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
index 7ceb328..68b772f 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
@@ -20,9 +20,6 @@
.panel-title
| {{ vertex.groupvertex.groupvertexname | humanizeText }}
- //- .panel-info.first
- //- | Vertex ID: {{ vertex.groupvertex.groupvertexid }}
-
.panel-body
.canvas-wrapper
div.timeline-canvas(vertex data="vertex")
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
index a5f9594..037a7e8 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
@@ -46,25 +46,27 @@ angular.module('flinkApp')
console.log 'SingleJobController'
$scope.jobid = $stateParams.jobid
- $rootScope.job = null
- $rootScope.plan = null
+ $scope.job = null
+ $scope.plan = null
+ $scope.vertices = null
JobsService.loadJob($stateParams.jobid).then (data) ->
- $rootScope.job = data
- $rootScope.plan = data.plan
+ $scope.job = data
+ $scope.plan = data.plan
+ $scope.vertices = data.vertices
refresher = $interval ->
JobsService.loadJob($stateParams.jobid).then (data) ->
- $rootScope.job = data
- # $rootScope.plan = data.plan
+ $scope.job = data
$scope.$broadcast 'reload'
, flinkConfig["refresh-interval"]
$scope.$on '$destroy', ->
- $rootScope.job = null
- $rootScope.plan = null
+ $scope.job = null
+ $scope.plan = null
+ $scope.vertices = null
$interval.cancel(refresher)
@@ -75,18 +77,34 @@ angular.module('flinkApp')
console.log 'JobPlanController'
$scope.nodeid = null
+ $scope.nodeUnfolded = false
$scope.stateList = JobsService.stateList()
$scope.changeNode = (nodeid) ->
if nodeid != $scope.nodeid
$scope.nodeid = nodeid
$scope.vertex = null
+ $scope.subtasks = null
+ $scope.accumulators = null
$scope.$broadcast 'reload'
else
$scope.nodeid = null
+ $scope.nodeUnfolded = false
$scope.vertex = null
+ $scope.subtasks = null
+ $scope.accumulators = null
+
+ $scope.deactivateNode = ->
+ $scope.nodeid = null
+ $scope.nodeUnfolded = false
+ $scope.vertex = null
+ $scope.subtasks = null
+ $scope.accumulators = null
+
+ $scope.toggleFold = ->
+ $scope.nodeUnfolded = !$scope.nodeUnfolded
# --------------------------------------
@@ -95,13 +113,13 @@ angular.module('flinkApp')
if $scope.nodeid and (!$scope.vertex or !$scope.vertex.st)
JobsService.getSubtasks($scope.nodeid).then (data) ->
- $scope.vertex = data
+ $scope.subtasks = data
$scope.$on 'reload', (event) ->
console.log 'JobPlanOverviewController'
if $scope.nodeid
JobsService.getSubtasks($scope.nodeid).then (data) ->
- $scope.vertex = data
+ $scope.subtasks = data
# --------------------------------------
@@ -110,13 +128,15 @@ angular.module('flinkApp')
if $scope.nodeid and (!$scope.vertex or !$scope.vertex.accumulators)
JobsService.getAccumulators($scope.nodeid).then (data) ->
- $scope.vertex = data
+ $scope.accumulators = data.main
+ $scope.subtaskAccumulators = data.subtasks
$scope.$on 'reload', (event) ->
console.log 'JobPlanAccumulatorsController'
if $scope.nodeid
JobsService.getAccumulators($scope.nodeid).then (data) ->
- $scope.vertex = data
+ $scope.accumulators = data.main
+ $scope.subtaskAccumulators = data.subtasks
# --------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
index 60d6224..11f9012 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
@@ -38,8 +38,6 @@ angular.module('flinkApp')
testData = []
angular.forEach data.subtasks, (subtask, i) ->
- console.log data.subtasks
-
times = [
{
label: "Scheduled"
@@ -70,7 +68,7 @@ angular.module('flinkApp')
}
testData.push {
- label: "#{subtask.host} (#{subtask.subtask})"
+ label: "(#{subtask.subtask}) #{subtask.host}"
times: times
}
@@ -102,7 +100,8 @@ angular.module('flinkApp')
template: "<svg class='timeline' width='0' height='0'></svg>"
scope:
- job: "="
+ vertices: "="
+ jobid: "="
link: (scope, elem, attrs) ->
svgEl = elem.children()[0]
@@ -118,32 +117,33 @@ angular.module('flinkApp')
testData = []
- testData.push
- times: [
- label: "Scheduled"
- color: "#cccccc"
- borderColor: "#555"
- starting_time: data.timestamps["CREATED"]
- ending_time: data.timestamps["CREATED"] + 1
- type: 'scheduled'
- ]
-
- angular.forEach data.vertices, (vertex) ->
+ angular.forEach data, (vertex) ->
if vertex['start-time'] > -1
- testData.push
- times: [
- label: translateLabel(vertex.name)
- color: "#d9f1f7"
- borderColor: "#62cdea"
- starting_time: vertex['start-time']
- ending_time: vertex['end-time']
- link: vertex.id
- type: 'regular'
- ]
+ if vertex.type is 'scheduled'
+ testData.push
+ times: [
+ label: translateLabel(vertex.name)
+ color: "#cccccc"
+ borderColor: "#555555"
+ starting_time: vertex['start-time']
+ ending_time: vertex['end-time']
+ type: vertex.type
+ ]
+ else
+ testData.push
+ times: [
+ label: translateLabel(vertex.name)
+ color: "#d9f1f7"
+ borderColor: "#62cdea"
+ starting_time: vertex['start-time']
+ ending_time: vertex['end-time']
+ link: vertex.id
+ type: vertex.type
+ ]
chart = d3.timeline().stack().click((d, i, datum) ->
if d.link
- $state.go "single-job.timeline.vertex", { jobid: data.jid, vertexId: d.link }
+ $state.go "single-job.timeline.vertex", { jobid: scope.jobid, vertexId: d.link }
)
.tickFormat({
@@ -162,7 +162,7 @@ angular.module('flinkApp')
.datum(testData)
.call(chart)
- scope.$watch attrs.job, (data) ->
+ scope.$watch attrs.vertices, (data) ->
analyzeTime(data) if data
return
@@ -183,6 +183,7 @@ angular.module('flinkApp')
setNode: '&'
link: (scope, elem, attrs) ->
+ g = null
mainZoom = d3.behavior.zoom()
subgraphs = []
jobid = attrs.jobid
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index 2ef6a34..685f90f 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -21,6 +21,7 @@ angular.module('flinkApp')
.service 'JobsService', ($http, flinkConfig, $log, amMoment, $q, $timeout) ->
currentJob = null
currentPlan = null
+
deferreds = {}
jobs = {
running: []
@@ -71,6 +72,17 @@ angular.module('flinkApp')
unless item['end-time'] > -1
item['end-time'] = item['start-time'] + item['duration']
+ @processVertices = (data) ->
+ angular.forEach data.vertices, (vertex, i) ->
+ vertex.type = 'regular'
+
+ data.vertices.unshift({
+ name: 'Scheduled'
+ 'start-time': data.timestamps['CREATED']
+ 'end-time': data.timestamps['CREATED'] + 1
+ type: 'scheduled'
+ })
+
@listJobs = ->
deferred = $q.defer()
@@ -101,6 +113,7 @@ angular.module('flinkApp')
$http.get flinkConfig.jobServer + "/jobs/" + jobid
.success (data, status, headers, config) =>
@setEndTimes(data.vertices)
+ @processVertices(data)
$http.get flinkConfig.jobServer + "/jobs/" + jobid + "/config"
.success (jobConfig) ->
@@ -108,7 +121,7 @@ angular.module('flinkApp')
currentJob = data
- deferreds.job.resolve(data)
+ deferreds.job.resolve(currentJob)
deferreds.job.promise
@@ -121,7 +134,6 @@ angular.module('flinkApp')
null
-
deferred = $q.defer()
deferreds.job.promise.then (data) =>
@@ -149,8 +161,6 @@ angular.module('flinkApp')
.success (data) =>
# TODO: change to subtasktimes
vertex.subtasks = data.subtasks
- @setEndTimes(vertex.subtasks)
- console.log vertex.subtasks
deferred.resolve(vertex)
@@ -160,13 +170,13 @@ angular.module('flinkApp')
deferred = $q.defer()
deferreds.job.promise.then (data) =>
- vertex = @seekVertex(vertexid)
+ # vertex = @seekVertex(vertexid)
$http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid
.success (data) ->
- vertex.st = data.subtasks
+ subtasks = data.subtasks
- deferred.resolve(vertex)
+ deferred.resolve(subtasks)
deferred.promise
@@ -174,16 +184,19 @@ angular.module('flinkApp')
deferred = $q.defer()
deferreds.job.promise.then (data) =>
- vertex = @seekVertex(vertexid)
+ # vertex = @seekVertex(vertexid)
$http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/accumulators"
.success (data) ->
- vertex.accumulators = data['user-accumulators']
+ accumulators = data['user-accumulators']
- deferred.resolve(vertex)
+ $http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasks/accumulators"
+ .success (data) ->
+ subtaskAccumulators = data.subtasks
- deferred.promise
+ deferred.resolve({ main: accumulators, subtasks: subtaskAccumulators })
+ deferred.promise
@loadExceptions = ->
deferred = $q.defer()
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/styles/animate.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/animate.styl b/flink-runtime-web/web-dashboard/app/styles/animate.styl
deleted file mode 100644
index 14171a2..0000000
--- a/flink-runtime-web/web-dashboard/app/styles/animate.styl
+++ /dev/null
@@ -1,1907 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Animate.css - http://daneden.me/animate
-// Licensed under the MIT license - http://opensource.org/licenses/MIT
-
-// Copyright (c) 2015 Daniel Eden
-
-22
- &.2%
- -webkit-transform skewX(-12.5deg) skewY(-12.5deg)
- transform skewX(-12.5deg) skewY(-12.5deg)
- -webkit-transform skewX(-12.5deg) skewY(-12.5deg)
- transform skewX(-12.5deg) skewY(-12.5deg)
-
-33
- &.3%
- -webkit-transform skewX(6.25deg) skewY(6.25deg)
- transform skewX(6.25deg) skewY(6.25deg)
- -webkit-transform skewX(6.25deg) skewY(6.25deg)
- transform skewX(6.25deg) skewY(6.25deg)
-
-44
- &.4%
- -webkit-transform skewX(-3.125deg) skewY(-3.125deg)
- transform skewX(-3.125deg) skewY(-3.125deg)
- -webkit-transform skewX(-3.125deg) skewY(-3.125deg)
- transform skewX(-3.125deg) skewY(-3.125deg)
-
-55
- &.5%
- -webkit-transform skewX(1.5625deg) skewY(1.5625deg)
- transform skewX(1.5625deg) skewY(1.5625deg)
- -webkit-transform skewX(1.5625deg) skewY(1.5625deg)
- transform skewX(1.5625deg) skewY(1.5625deg)
-
-66
- &.6%
- -webkit-transform skewX(-0.78125deg) skewY(-0.78125deg)
- transform skewX(-0.78125deg) skewY(-0.78125deg)
- -webkit-transform skewX(-0.78125deg) skewY(-0.78125deg)
- transform skewX(-0.78125deg) skewY(-0.78125deg)
-
-77
- &.7%
- -webkit-transform skewX(0.390625deg) skewY(0.390625deg)
- transform skewX(0.390625deg) skewY(0.390625deg)
- -webkit-transform skewX(0.390625deg) skewY(0.390625deg)
- transform skewX(0.390625deg) skewY(0.390625deg)
-
-88
- &.8%
- -webkit-transform skewX(-0.1953125deg) skewY(-0.1953125deg)
- transform skewX(-0.1953125deg) skewY(-0.1953125deg)
- -webkit-transform skewX(-0.1953125deg) skewY(-0.1953125deg)
- transform skewX(-0.1953125deg) skewY(-0.1953125deg)
-
-@charset
- "UTF-8";
- .animated
- -webkit-animation-duration 1s
- animation-duration 1s
- -webkit-animation-fill-mode both
- animation-fill-mode both
-
-.animated
- &.infinite
- -webkit-animation-iteration-count infinite
- animation-iteration-count infinite
- &.hinge
- -webkit-animation-duration 2s
- animation-duration 2s
-
-.animated.bounceIn,
-.animated.bounceOut
- -webkit-animation-duration .75s
- animation-duration .75s
-
-.animated.flipOutX,
-.animated.flipOutY
- -webkit-animation-duration .75s
- animation-duration .75s
-
-@-webkit-keyframes
- bounce
- 0%, 20%, 53%, 80%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- -webkit-transform translate3d(0,0,0)
- transform translate3d(0,0,0)
- flash
- 0%, 50%, 100% {
- opacity 1
- pulse
- 0% {
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- rubberBand
- 0% {
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- shake
- 0%, 100% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- swing
- 20% {
- -webkit-transform rotate3d(0, 0, 1, 15deg)
- transform rotate3d(0, 0, 1, 15deg)
- tada
- 0% {
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- wobble
- 0% {
- -webkit-transform none
- transform none
- jello
- 0%, 11.1%, 100% {
- -webkit-transform none
- transform none
- bounceIn
- 0%, 20%, 40%, 60%, 80%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceInDown
- 0%, 60%, 75%, 90%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceInLeft
- 0%, 60%, 75%, 90%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceInRight
- 0%, 60%, 75%, 90%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceInUp
- 0%, 60%, 75%, 90%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceOut
- 20% {
- -webkit-transform scale3d(.9, .9, .9)
- transform scale3d(.9, .9, .9)
- bounceOutDown
- 20% {
- -webkit-transform translate3d(0, 10px, 0)
- transform translate3d(0, 10px, 0)
- bounceOutLeft
- 20% {
- opacity 1
- -webkit-transform translate3d(20px, 0, 0)
- transform translate3d(20px, 0, 0)
- bounceOutRight
- 20% {
- opacity 1
- -webkit-transform translate3d(-20px, 0, 0)
- transform translate3d(-20px, 0, 0)
- bounceOutUp
- 20% {
- -webkit-transform translate3d(0, -10px, 0)
- transform translate3d(0, -10px, 0)
- fadeIn
- 0% {
- opacity 0
- fadeInDown
- 0% {
- opacity 0
- -webkit-transform translate3d(0, -100%, 0)
- transform translate3d(0, -100%, 0)
- fadeInDownBig
- 0% {
- opacity 0
- -webkit-transform translate3d(0, -2000px, 0)
- transform translate3d(0, -2000px, 0)
- fadeInLeft
- 0% {
- opacity 0
- -webkit-transform translate3d(-100%, 0, 0)
- transform translate3d(-100%, 0, 0)
- fadeInLeftBig
- 0% {
- opacity 0
- -webkit-transform translate3d(-2000px, 0, 0)
- transform translate3d(-2000px, 0, 0)
- fadeInRight
- 0% {
- opacity 0
- -webkit-transform translate3d(100%, 0, 0)
- transform translate3d(100%, 0, 0)
- fadeInRightBig
- 0% {
- opacity 0
- -webkit-transform translate3d(2000px, 0, 0)
- transform translate3d(2000px, 0, 0)
- fadeInUp
- 0% {
- opacity 0
- -webkit-transform translate3d(0, 100%, 0)
- transform translate3d(0, 100%, 0)
- fadeInUpBig
- 0% {
- opacity 0
- -webkit-transform translate3d(0, 2000px, 0)
- transform translate3d(0, 2000px, 0)
- fadeOut
- 0% {
- opacity 1
- fadeOutDown
- 0% {
- opacity 1
- fadeOutDownBig
- 0% {
- opacity 1
- fadeOutLeft
- 0% {
- opacity 1
- fadeOutLeftBig
- 0% {
- opacity 1
- fadeOutRight
- 0% {
- opacity 1
- fadeOutRightBig
- 0% {
- opacity 1
- fadeOutUp
- 0% {
- opacity 1
- fadeOutUpBig
- 0% {
- opacity 1
- flip
- 0% {
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, -360deg)
- transform perspective(400px) rotate3d(0, 1, 0, -360deg)
- -webkit-animation-timing-function ease-out
- animation-timing-function ease-out
- flipInX
- 0% {
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, 90deg)
- transform perspective(400px) rotate3d(1, 0, 0, 90deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- opacity 0
- flipInY
- 0% {
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, 90deg)
- transform perspective(400px) rotate3d(0, 1, 0, 90deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- opacity 0
- flipOutX
- 0% {
- -webkit-transform perspective(400px)
- transform perspective(400px)
- flipOutY
- 0% {
- -webkit-transform perspective(400px)
- transform perspective(400px)
- lightSpeedIn
- 0% {
- -webkit-transform translate3d(100%, 0, 0) skewX(-30deg)
- transform translate3d(100%, 0, 0) skewX(-30deg)
- opacity 0
- lightSpeedOut
- 0% {
- opacity 1
- rotateIn
- 0% {
- -webkit-transform-origin center
- transform-origin center
- -webkit-transform rotate3d(0, 0, 1, -200deg)
- transform rotate3d(0, 0, 1, -200deg)
- opacity 0
- rotateInDownLeft
- 0% {
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform rotate3d(0, 0, 1, -45deg)
- transform rotate3d(0, 0, 1, -45deg)
- opacity 0
- rotateInDownRight
- 0% {
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform rotate3d(0, 0, 1, 45deg)
- transform rotate3d(0, 0, 1, 45deg)
- opacity 0
- rotateInUpLeft
- 0% {
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform rotate3d(0, 0, 1, 45deg)
- transform rotate3d(0, 0, 1, 45deg)
- opacity 0
- rotateInUpRight
- 0% {
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform rotate3d(0, 0, 1, -90deg)
- transform rotate3d(0, 0, 1, -90deg)
- opacity 0
- rotateOut
- 0% {
- -webkit-transform-origin center
- transform-origin center
- opacity 1
- rotateOutDownLeft
- 0% {
- -webkit-transform-origin left bottom
- transform-origin left bottom
- opacity 1
- rotateOutDownRight
- 0% {
- -webkit-transform-origin right bottom
- transform-origin right bottom
- opacity 1
- rotateOutUpLeft
- 0% {
- -webkit-transform-origin left bottom
- transform-origin left bottom
- opacity 1
- rotateOutUpRight
- 0% {
- -webkit-transform-origin right bottom
- transform-origin right bottom
- opacity 1
- hinge
- 0% {
- -webkit-transform-origin top left
- transform-origin top left
- -webkit-animation-timing-function ease-in-out
- animation-timing-function ease-in-out
- rollIn
- 0% {
- opacity 0
- -webkit-transform translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg)
- transform translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg)
- rollOut
- 0% {
- opacity 1
- zoomIn
- 0% {
- opacity 0
- -webkit-transform scale3d(.3, .3, .3)
- transform scale3d(.3, .3, .3)
- zoomInDown
- 0% {
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(0, -1000px, 0)
- transform scale3d(.1, .1, .1) translate3d(0, -1000px, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomInLeft
- 0% {
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(-1000px, 0, 0)
- transform scale3d(.1, .1, .1) translate3d(-1000px, 0, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomInRight
- 0% {
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(1000px, 0, 0)
- transform scale3d(.1, .1, .1) translate3d(1000px, 0, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomInUp
- 0% {
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(0, 1000px, 0)
- transform scale3d(.1, .1, .1) translate3d(0, 1000px, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomOut
- 0% {
- opacity 1
- zoomOutDown
- 40% {
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
- transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomOutLeft
- 40% {
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(42px, 0, 0)
- transform scale3d(.475, .475, .475) translate3d(42px, 0, 0)
- zoomOutRight
- 40% {
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(-42px, 0, 0)
- transform scale3d(.475, .475, .475) translate3d(-42px, 0, 0)
- zoomOutUp
- 40% {
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
- transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- slideInDown
- 0% {
- -webkit-transform translate3d(0, -100%, 0)
- transform translate3d(0, -100%, 0)
- visibility visible
- slideInLeft
- 0% {
- -webkit-transform translate3d(-100%, 0, 0)
- transform translate3d(-100%, 0, 0)
- visibility visible
- slideInRight
- 0% {
- -webkit-transform translate3d(100%, 0, 0)
- transform translate3d(100%, 0, 0)
- visibility visible
- slideInUp
- 0% {
- -webkit-transform translate3d(0, 100%, 0)
- transform translate3d(0, 100%, 0)
- visibility visible
- slideOutDown
- 0% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- slideOutLeft
- 0% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- slideOutRight
- 0% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- slideOutUp
- 0% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
-
-40%, 43%
- -webkit-animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
- animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
- -webkit-transform translate3d(0, -30px, 0)
- transform translate3d(0, -30px, 0)
- -webkit-animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
- animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
- -webkit-transform translate3d(0, -30px, 0)
- transform translate3d(0, -30px, 0)
-
-70%
- -webkit-animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
- animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
- -webkit-transform translate3d(0, -15px, 0)
- transform translate3d(0, -15px, 0)
- -webkit-animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
- animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
- -webkit-transform translate3d(0, -15px, 0)
- transform translate3d(0, -15px, 0)
-
-90%
- -webkit-transform translate3d(0,-4px,0)
- transform translate3d(0,-4px,0)
- -webkit-transform translate3d(0,-4px,0)
- transform translate3d(0,-4px,0)
- -webkit-transform translate3d(0, 5px, 0)
- transform translate3d(0, 5px, 0)
- -webkit-transform translate3d(0, 5px, 0)
- transform translate3d(0, 5px, 0)
- -webkit-transform translate3d(5px, 0, 0)
- transform translate3d(5px, 0, 0)
- -webkit-transform translate3d(5px, 0, 0)
- transform translate3d(5px, 0, 0)
- -webkit-transform translate3d(-5px, 0, 0)
- transform translate3d(-5px, 0, 0)
- -webkit-transform translate3d(-5px, 0, 0)
- transform translate3d(-5px, 0, 0)
- -webkit-transform translate3d(0, -5px, 0)
- transform translate3d(0, -5px, 0)
- -webkit-transform translate3d(0, -5px, 0)
- transform translate3d(0, -5px, 0)
-
-}
- @keyframes
- bounce
- 0%, 20%, 53%, 80%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- -webkit-transform translate3d(0,0,0)
- transform translate3d(0,0,0)
- flash
- 0%, 50%, 100% {
- opacity 1
- pulse
- 0% {
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- rubberBand
- 0% {
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- shake
- 0%, 100% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- swing
- 20% {
- -webkit-transform rotate3d(0, 0, 1, 15deg)
- transform rotate3d(0, 0, 1, 15deg)
- tada
- 0% {
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- wobble
- 0% {
- -webkit-transform none
- transform none
- jello
- 0%, 11.1%, 100% {
- -webkit-transform none
- transform none
- bounceIn
- 0%, 20%, 40%, 60%, 80%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceInDown
- 0%, 60%, 75%, 90%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceInLeft
- 0%, 60%, 75%, 90%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceInRight
- 0%, 60%, 75%, 90%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceInUp
- 0%, 60%, 75%, 90%, 100% {
- -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
- bounceOut
- 20% {
- -webkit-transform scale3d(.9, .9, .9)
- transform scale3d(.9, .9, .9)
- bounceOutDown
- 20% {
- -webkit-transform translate3d(0, 10px, 0)
- transform translate3d(0, 10px, 0)
- bounceOutLeft
- 20% {
- opacity 1
- -webkit-transform translate3d(20px, 0, 0)
- transform translate3d(20px, 0, 0)
- bounceOutRight
- 20% {
- opacity 1
- -webkit-transform translate3d(-20px, 0, 0)
- transform translate3d(-20px, 0, 0)
- bounceOutUp
- 20% {
- -webkit-transform translate3d(0, -10px, 0)
- transform translate3d(0, -10px, 0)
- fadeIn
- 0% {
- opacity 0
- fadeInDown
- 0% {
- opacity 0
- -webkit-transform translate3d(0, -100%, 0)
- transform translate3d(0, -100%, 0)
- fadeInDownBig
- 0% {
- opacity 0
- -webkit-transform translate3d(0, -2000px, 0)
- transform translate3d(0, -2000px, 0)
- fadeInLeft
- 0% {
- opacity 0
- -webkit-transform translate3d(-100%, 0, 0)
- transform translate3d(-100%, 0, 0)
- fadeInLeftBig
- 0% {
- opacity 0
- -webkit-transform translate3d(-2000px, 0, 0)
- transform translate3d(-2000px, 0, 0)
- fadeInRight
- 0% {
- opacity 0
- -webkit-transform translate3d(100%, 0, 0)
- transform translate3d(100%, 0, 0)
- fadeInRightBig
- 0% {
- opacity 0
- -webkit-transform translate3d(2000px, 0, 0)
- transform translate3d(2000px, 0, 0)
- fadeInUp
- 0% {
- opacity 0
- -webkit-transform translate3d(0, 100%, 0)
- transform translate3d(0, 100%, 0)
- fadeInUpBig
- 0% {
- opacity 0
- -webkit-transform translate3d(0, 2000px, 0)
- transform translate3d(0, 2000px, 0)
- fadeOut
- 0% {
- opacity 1
- fadeOutDown
- 0% {
- opacity 1
- fadeOutDownBig
- 0% {
- opacity 1
- fadeOutLeft
- 0% {
- opacity 1
- fadeOutLeftBig
- 0% {
- opacity 1
- fadeOutRight
- 0% {
- opacity 1
- fadeOutRightBig
- 0% {
- opacity 1
- fadeOutUp
- 0% {
- opacity 1
- fadeOutUpBig
- 0% {
- opacity 1
- flip
- 0% {
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, -360deg)
- transform perspective(400px) rotate3d(0, 1, 0, -360deg)
- -webkit-animation-timing-function ease-out
- animation-timing-function ease-out
- flipInX
- 0% {
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, 90deg)
- transform perspective(400px) rotate3d(1, 0, 0, 90deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- opacity 0
- flipInY
- 0% {
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, 90deg)
- transform perspective(400px) rotate3d(0, 1, 0, 90deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- opacity 0
- flipOutX
- 0% {
- -webkit-transform perspective(400px)
- transform perspective(400px)
- flipOutY
- 0% {
- -webkit-transform perspective(400px)
- transform perspective(400px)
- lightSpeedIn
- 0% {
- -webkit-transform translate3d(100%, 0, 0) skewX(-30deg)
- transform translate3d(100%, 0, 0) skewX(-30deg)
- opacity 0
- lightSpeedOut
- 0% {
- opacity 1
- rotateIn
- 0% {
- -webkit-transform-origin center
- transform-origin center
- -webkit-transform rotate3d(0, 0, 1, -200deg)
- transform rotate3d(0, 0, 1, -200deg)
- opacity 0
- rotateInDownLeft
- 0% {
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform rotate3d(0, 0, 1, -45deg)
- transform rotate3d(0, 0, 1, -45deg)
- opacity 0
- rotateInDownRight
- 0% {
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform rotate3d(0, 0, 1, 45deg)
- transform rotate3d(0, 0, 1, 45deg)
- opacity 0
- rotateInUpLeft
- 0% {
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform rotate3d(0, 0, 1, 45deg)
- transform rotate3d(0, 0, 1, 45deg)
- opacity 0
- rotateInUpRight
- 0% {
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform rotate3d(0, 0, 1, -90deg)
- transform rotate3d(0, 0, 1, -90deg)
- opacity 0
- rotateOut
- 0% {
- -webkit-transform-origin center
- transform-origin center
- opacity 1
- rotateOutDownLeft
- 0% {
- -webkit-transform-origin left bottom
- transform-origin left bottom
- opacity 1
- rotateOutDownRight
- 0% {
- -webkit-transform-origin right bottom
- transform-origin right bottom
- opacity 1
- rotateOutUpLeft
- 0% {
- -webkit-transform-origin left bottom
- transform-origin left bottom
- opacity 1
- rotateOutUpRight
- 0% {
- -webkit-transform-origin right bottom
- transform-origin right bottom
- opacity 1
- hinge
- 0% {
- -webkit-transform-origin top left
- transform-origin top left
- -webkit-animation-timing-function ease-in-out
- animation-timing-function ease-in-out
- rollIn
- 0% {
- opacity 0
- -webkit-transform translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg)
- transform translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg)
- rollOut
- 0% {
- opacity 1
- zoomIn
- 0% {
- opacity 0
- -webkit-transform scale3d(.3, .3, .3)
- transform scale3d(.3, .3, .3)
- zoomInDown
- 0% {
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(0, -1000px, 0)
- transform scale3d(.1, .1, .1) translate3d(0, -1000px, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomInLeft
- 0% {
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(-1000px, 0, 0)
- transform scale3d(.1, .1, .1) translate3d(-1000px, 0, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomInRight
- 0% {
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(1000px, 0, 0)
- transform scale3d(.1, .1, .1) translate3d(1000px, 0, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomInUp
- 0% {
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(0, 1000px, 0)
- transform scale3d(.1, .1, .1) translate3d(0, 1000px, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomOut
- 0% {
- opacity 1
- zoomOutDown
- 40% {
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
- transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- zoomOutLeft
- 40% {
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(42px, 0, 0)
- transform scale3d(.475, .475, .475) translate3d(42px, 0, 0)
- zoomOutRight
- 40% {
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(-42px, 0, 0)
- transform scale3d(.475, .475, .475) translate3d(-42px, 0, 0)
- zoomOutUp
- 40% {
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
- transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
- -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
- slideInDown
- 0% {
- -webkit-transform translate3d(0, -100%, 0)
- transform translate3d(0, -100%, 0)
- visibility visible
- slideInLeft
- 0% {
- -webkit-transform translate3d(-100%, 0, 0)
- transform translate3d(-100%, 0, 0)
- visibility visible
- slideInRight
- 0% {
- -webkit-transform translate3d(100%, 0, 0)
- transform translate3d(100%, 0, 0)
- visibility visible
- slideInUp
- 0% {
- -webkit-transform translate3d(0, 100%, 0)
- transform translate3d(0, 100%, 0)
- visibility visible
- slideOutDown
- 0% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- slideOutLeft
- 0% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- slideOutRight
- 0% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- slideOutUp
- 0% {
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- .bounce
- -webkit-animation-name bounce
- animation-name bounce
- -webkit-transform-origin center bottom
- transform-origin center bottom
- .flash
- -webkit-animation-name flash
- animation-name flash
- .pulse
- -webkit-animation-name pulse
- animation-name pulse
- .rubberBand
- -webkit-animation-name rubberBand
- animation-name rubberBand
- .shake
- -webkit-animation-name shake
- animation-name shake
- .swing
- -webkit-transform-origin top center
- transform-origin top center
- -webkit-animation-name swing
- animation-name swing
- .tada
- -webkit-animation-name tada
- animation-name tada
- .wobble
- -webkit-animation-name wobble
- animation-name wobble
- .jello
- -webkit-animation-name jello
- animation-name jello
- -webkit-transform-origin center
- transform-origin center
- .bounceIn
- -webkit-animation-name bounceIn
- animation-name bounceIn
- .bounceInDown
- -webkit-animation-name bounceInDown
- animation-name bounceInDown
- .bounceInLeft
- -webkit-animation-name bounceInLeft
- animation-name bounceInLeft
- .bounceInRight
- -webkit-animation-name bounceInRight
- animation-name bounceInRight
- .bounceInUp
- -webkit-animation-name bounceInUp
- animation-name bounceInUp
- .bounceOut
- -webkit-animation-name bounceOut
- animation-name bounceOut
- .bounceOutDown
- -webkit-animation-name bounceOutDown
- animation-name bounceOutDown
- .bounceOutLeft
- -webkit-animation-name bounceOutLeft
- animation-name bounceOutLeft
- .bounceOutRight
- -webkit-animation-name bounceOutRight
- animation-name bounceOutRight
- .bounceOutUp
- -webkit-animation-name bounceOutUp
- animation-name bounceOutUp
- .fadeIn
- -webkit-animation-name fadeIn
- animation-name fadeIn
- .fadeInDown
- -webkit-animation-name fadeInDown
- animation-name fadeInDown
- .fadeInDownBig
- -webkit-animation-name fadeInDownBig
- animation-name fadeInDownBig
- .fadeInLeft
- -webkit-animation-name fadeInLeft
- animation-name fadeInLeft
- .fadeInLeftBig
- -webkit-animation-name fadeInLeftBig
- animation-name fadeInLeftBig
- .fadeInRight
- -webkit-animation-name fadeInRight
- animation-name fadeInRight
- .fadeInRightBig
- -webkit-animation-name fadeInRightBig
- animation-name fadeInRightBig
- .fadeInUp
- -webkit-animation-name fadeInUp
- animation-name fadeInUp
- .fadeInUpBig
- -webkit-animation-name fadeInUpBig
- animation-name fadeInUpBig
- .fadeOut
- -webkit-animation-name fadeOut
- animation-name fadeOut
- .fadeOutDown
- -webkit-animation-name fadeOutDown
- animation-name fadeOutDown
- .fadeOutDownBig
- -webkit-animation-name fadeOutDownBig
- animation-name fadeOutDownBig
- .fadeOutLeft
- -webkit-animation-name fadeOutLeft
- animation-name fadeOutLeft
- .fadeOutLeftBig
- -webkit-animation-name fadeOutLeftBig
- animation-name fadeOutLeftBig
- .fadeOutRight
- -webkit-animation-name fadeOutRight
- animation-name fadeOutRight
- .fadeOutRightBig
- -webkit-animation-name fadeOutRightBig
- animation-name fadeOutRightBig
- .fadeOutUp
- -webkit-animation-name fadeOutUp
- animation-name fadeOutUp
- .fadeOutUpBig
- -webkit-animation-name fadeOutUpBig
- animation-name fadeOutUpBig
- .animated
- &.flip
- -webkit-backface-visibility visible
- backface-visibility visible
- -webkit-animation-name flip
- animation-name flip
- .flipInX
- -webkit-backface-visibility visible !important
- backface-visibility visible !important
- -webkit-animation-name flipInX
- animation-name flipInX
- .flipInY
- -webkit-backface-visibility visible !important
- backface-visibility visible !important
- -webkit-animation-name flipInY
- animation-name flipInY
- .flipOutX
- -webkit-animation-name flipOutX
- animation-name flipOutX
- -webkit-backface-visibility visible !important
- backface-visibility visible !important
- .flipOutY
- -webkit-backface-visibility visible !important
- backface-visibility visible !important
- -webkit-animation-name flipOutY
- animation-name flipOutY
- .lightSpeedIn
- -webkit-animation-name lightSpeedIn
- animation-name lightSpeedIn
- -webkit-animation-timing-function ease-out
- animation-timing-function ease-out
- .lightSpeedOut
- -webkit-animation-name lightSpeedOut
- animation-name lightSpeedOut
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- .rotateIn
- -webkit-animation-name rotateIn
- animation-name rotateIn
- .rotateInDownLeft
- -webkit-animation-name rotateInDownLeft
- animation-name rotateInDownLeft
- .rotateInDownRight
- -webkit-animation-name rotateInDownRight
- animation-name rotateInDownRight
- .rotateInUpLeft
- -webkit-animation-name rotateInUpLeft
- animation-name rotateInUpLeft
- .rotateInUpRight
- -webkit-animation-name rotateInUpRight
- animation-name rotateInUpRight
- .rotateOut
- -webkit-animation-name rotateOut
- animation-name rotateOut
- .rotateOutDownLeft
- -webkit-animation-name rotateOutDownLeft
- animation-name rotateOutDownLeft
- .rotateOutDownRight
- -webkit-animation-name rotateOutDownRight
- animation-name rotateOutDownRight
- .rotateOutUpLeft
- -webkit-animation-name rotateOutUpLeft
- animation-name rotateOutUpLeft
- .rotateOutUpRight
- -webkit-animation-name rotateOutUpRight
- animation-name rotateOutUpRight
- .hinge
- -webkit-animation-name hinge
- animation-name hinge
- .rollIn
- -webkit-animation-name rollIn
- animation-name rollIn
- .rollOut
- -webkit-animation-name rollOut
- animation-name rollOut
- .zoomIn
- -webkit-animation-name zoomIn
- animation-name zoomIn
- .zoomInDown
- -webkit-animation-name zoomInDown
- animation-name zoomInDown
- .zoomInLeft
- -webkit-animation-name zoomInLeft
- animation-name zoomInLeft
- .zoomInRight
- -webkit-animation-name zoomInRight
- animation-name zoomInRight
- .zoomInUp
- -webkit-animation-name zoomInUp
- animation-name zoomInUp
- .zoomOut
- -webkit-animation-name zoomOut
- animation-name zoomOut
- .zoomOutDown
- -webkit-animation-name zoomOutDown
- animation-name zoomOutDown
- .zoomOutLeft
- -webkit-animation-name zoomOutLeft
- animation-name zoomOutLeft
- .zoomOutRight
- -webkit-animation-name zoomOutRight
- animation-name zoomOutRight
- .zoomOutUp
- -webkit-animation-name zoomOutUp
- animation-name zoomOutUp
- .slideInDown
- -webkit-animation-name slideInDown
- animation-name slideInDown
- .slideInLeft
- -webkit-animation-name slideInLeft
- animation-name slideInLeft
- .slideInRight
- -webkit-animation-name slideInRight
- animation-name slideInRight
- .slideInUp
- -webkit-animation-name slideInUp
- animation-name slideInUp
- .slideOutDown
- -webkit-animation-name slideOutDown
- animation-name slideOutDown
- .slideOutLeft
- -webkit-animation-name slideOutLeft
- animation-name slideOutLeft
- .slideOutRight
- -webkit-animation-name slideOutRight
- animation-name slideOutRight
- .slideOutUp
- -webkit-animation-name slideOutUp
- animation-name slideOutUp
-
-25%, 75%
- opacity 0
- opacity 0
-
-50%
- -webkit-transform scale3d(1.05, 1.05, 1.05)
- transform scale3d(1.05, 1.05, 1.05)
- -webkit-transform scale3d(1.05, 1.05, 1.05)
- transform scale3d(1.05, 1.05, 1.05)
- -webkit-transform scale3d(1.15, 0.85, 1)
- transform scale3d(1.15, 0.85, 1)
- -webkit-transform scale3d(1.15, 0.85, 1)
- transform scale3d(1.15, 0.85, 1)
- -webkit-transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)
- transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- -webkit-transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)
- transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- opacity 1
- opacity 1
- opacity 0
- -webkit-transform scale3d(.3, .3, .3)
- transform scale3d(.3, .3, .3)
- opacity 0
- -webkit-transform scale3d(.3, .3, .3)
- transform scale3d(.3, .3, .3)
-
-100%
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- -webkit-transform rotate3d(0, 0, 1, 0deg)
- transform rotate3d(0, 0, 1, 0deg)
- -webkit-transform rotate3d(0, 0, 1, 0deg)
- transform rotate3d(0, 0, 1, 0deg)
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- -webkit-transform none
- transform none
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- opacity 1
- -webkit-transform scale3d(1, 1, 1)
- transform scale3d(1, 1, 1)
- -webkit-transform none
- transform none
- -webkit-transform none
- transform none
- -webkit-transform none
- transform none
- -webkit-transform none
- transform none
- -webkit-transform none
- transform none
- -webkit-transform none
- transform none
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- opacity 0
- -webkit-transform scale3d(.3, .3, .3)
- transform scale3d(.3, .3, .3)
- opacity 0
- -webkit-transform scale3d(.3, .3, .3)
- transform scale3d(.3, .3, .3)
- opacity 0
- -webkit-transform translate3d(0, 2000px, 0)
- transform translate3d(0, 2000px, 0)
- opacity 0
- -webkit-transform translate3d(0, 2000px, 0)
- transform translate3d(0, 2000px, 0)
- opacity 0
- -webkit-transform translate3d(-2000px, 0, 0)
- transform translate3d(-2000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(-2000px, 0, 0)
- transform translate3d(-2000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(2000px, 0, 0)
- transform translate3d(2000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(2000px, 0, 0)
- transform translate3d(2000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(0, -2000px, 0)
- transform translate3d(0, -2000px, 0)
- opacity 0
- -webkit-transform translate3d(0, -2000px, 0)
- transform translate3d(0, -2000px, 0)
- opacity 1
- opacity 1
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 0
- opacity 0
- opacity 0
- -webkit-transform translate3d(0, 100%, 0)
- transform translate3d(0, 100%, 0)
- opacity 0
- -webkit-transform translate3d(0, 100%, 0)
- transform translate3d(0, 100%, 0)
- opacity 0
- -webkit-transform translate3d(0, 2000px, 0)
- transform translate3d(0, 2000px, 0)
- opacity 0
- -webkit-transform translate3d(0, 2000px, 0)
- transform translate3d(0, 2000px, 0)
- opacity 0
- -webkit-transform translate3d(-100%, 0, 0)
- transform translate3d(-100%, 0, 0)
- opacity 0
- -webkit-transform translate3d(-100%, 0, 0)
- transform translate3d(-100%, 0, 0)
- opacity 0
- -webkit-transform translate3d(-2000px, 0, 0)
- transform translate3d(-2000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(-2000px, 0, 0)
- transform translate3d(-2000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(100%, 0, 0)
- transform translate3d(100%, 0, 0)
- opacity 0
- -webkit-transform translate3d(100%, 0, 0)
- transform translate3d(100%, 0, 0)
- opacity 0
- -webkit-transform translate3d(2000px, 0, 0)
- transform translate3d(2000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(2000px, 0, 0)
- transform translate3d(2000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(0, -100%, 0)
- transform translate3d(0, -100%, 0)
- opacity 0
- -webkit-transform translate3d(0, -100%, 0)
- transform translate3d(0, -100%, 0)
- opacity 0
- -webkit-transform translate3d(0, -2000px, 0)
- transform translate3d(0, -2000px, 0)
- opacity 0
- -webkit-transform translate3d(0, -2000px, 0)
- transform translate3d(0, -2000px, 0)
- -webkit-transform perspective(400px)
- transform perspective(400px)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- -webkit-transform perspective(400px)
- transform perspective(400px)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- -webkit-transform perspective(400px)
- transform perspective(400px)
- -webkit-transform perspective(400px)
- transform perspective(400px)
- -webkit-transform perspective(400px)
- transform perspective(400px)
- -webkit-transform perspective(400px)
- transform perspective(400px)
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, 90deg)
- transform perspective(400px) rotate3d(1, 0, 0, 90deg)
- opacity 0
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, 90deg)
- transform perspective(400px) rotate3d(1, 0, 0, 90deg)
- opacity 0
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, 90deg)
- transform perspective(400px) rotate3d(0, 1, 0, 90deg)
- opacity 0
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, 90deg)
- transform perspective(400px) rotate3d(0, 1, 0, 90deg)
- opacity 0
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform translate3d(100%, 0, 0) skewX(30deg)
- transform translate3d(100%, 0, 0) skewX(30deg)
- opacity 0
- -webkit-transform translate3d(100%, 0, 0) skewX(30deg)
- transform translate3d(100%, 0, 0) skewX(30deg)
- opacity 0
- -webkit-transform-origin center
- transform-origin center
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin center
- transform-origin center
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform-origin center
- transform-origin center
- -webkit-transform rotate3d(0, 0, 1, 200deg)
- transform rotate3d(0, 0, 1, 200deg)
- opacity 0
- -webkit-transform-origin center
- transform-origin center
- -webkit-transform rotate3d(0, 0, 1, 200deg)
- transform rotate3d(0, 0, 1, 200deg)
- opacity 0
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform rotate3d(0, 0, 1, 45deg)
- transform rotate3d(0, 0, 1, 45deg)
- opacity 0
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform rotate3d(0, 0, 1, 45deg)
- transform rotate3d(0, 0, 1, 45deg)
- opacity 0
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform rotate3d(0, 0, 1, -45deg)
- transform rotate3d(0, 0, 1, -45deg)
- opacity 0
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform rotate3d(0, 0, 1, -45deg)
- transform rotate3d(0, 0, 1, -45deg)
- opacity 0
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform rotate3d(0, 0, 1, -45deg)
- transform rotate3d(0, 0, 1, -45deg)
- opacity 0
- -webkit-transform-origin left bottom
- transform-origin left bottom
- -webkit-transform rotate3d(0, 0, 1, -45deg)
- transform rotate3d(0, 0, 1, -45deg)
- opacity 0
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform rotate3d(0, 0, 1, 90deg)
- transform rotate3d(0, 0, 1, 90deg)
- opacity 0
- -webkit-transform-origin right bottom
- transform-origin right bottom
- -webkit-transform rotate3d(0, 0, 1, 90deg)
- transform rotate3d(0, 0, 1, 90deg)
- opacity 0
- -webkit-transform translate3d(0, 700px, 0)
- transform translate3d(0, 700px, 0)
- opacity 0
- -webkit-transform translate3d(0, 700px, 0)
- transform translate3d(0, 700px, 0)
- opacity 0
- opacity 1
- -webkit-transform none
- transform none
- opacity 1
- -webkit-transform none
- transform none
- opacity 0
- -webkit-transform translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg)
- transform translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg)
- opacity 0
- -webkit-transform translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg)
- transform translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg)
- opacity 0
- opacity 0
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(0, 2000px, 0)
- transform scale3d(.1, .1, .1) translate3d(0, 2000px, 0)
- -webkit-transform-origin center bottom
- transform-origin center bottom
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(0, 2000px, 0)
- transform scale3d(.1, .1, .1) translate3d(0, 2000px, 0)
- -webkit-transform-origin center bottom
- transform-origin center bottom
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 0
- -webkit-transform scale(.1) translate3d(-2000px, 0, 0)
- transform scale(.1) translate3d(-2000px, 0, 0)
- -webkit-transform-origin left center
- transform-origin left center
- opacity 0
- -webkit-transform scale(.1) translate3d(-2000px, 0, 0)
- transform scale(.1) translate3d(-2000px, 0, 0)
- -webkit-transform-origin left center
- transform-origin left center
- opacity 0
- -webkit-transform scale(.1) translate3d(2000px, 0, 0)
- transform scale(.1) translate3d(2000px, 0, 0)
- -webkit-transform-origin right center
- transform-origin right center
- opacity 0
- -webkit-transform scale(.1) translate3d(2000px, 0, 0)
- transform scale(.1) translate3d(2000px, 0, 0)
- -webkit-transform-origin right center
- transform-origin right center
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(0, -2000px, 0)
- transform scale3d(.1, .1, .1) translate3d(0, -2000px, 0)
- -webkit-transform-origin center bottom
- transform-origin center bottom
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 0
- -webkit-transform scale3d(.1, .1, .1) translate3d(0, -2000px, 0)
- transform scale3d(.1, .1, .1) translate3d(0, -2000px, 0)
- -webkit-transform-origin center bottom
- transform-origin center bottom
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- -webkit-transform translate3d(0, 0, 0)
- transform translate3d(0, 0, 0)
- visibility hidden
- -webkit-transform translate3d(0, 100%, 0)
- transform translate3d(0, 100%, 0)
- visibility hidden
- -webkit-transform translate3d(0, 100%, 0)
- transform translate3d(0, 100%, 0)
- visibility hidden
- -webkit-transform translate3d(-100%, 0, 0)
- transform translate3d(-100%, 0, 0)
- visibility hidden
- -webkit-transform translate3d(-100%, 0, 0)
- transform translate3d(-100%, 0, 0)
- visibility hidden
- -webkit-transform translate3d(100%, 0, 0)
- transform translate3d(100%, 0, 0)
- visibility hidden
- -webkit-transform translate3d(100%, 0, 0)
- transform translate3d(100%, 0, 0)
- visibility hidden
- -webkit-transform translate3d(0, -100%, 0)
- transform translate3d(0, -100%, 0)
- visibility hidden
- -webkit-transform translate3d(0, -100%, 0)
- transform translate3d(0, -100%, 0)
-
-30%
- -webkit-transform scale3d(1.25, 0.75, 1)
- transform scale3d(1.25, 0.75, 1)
- -webkit-transform scale3d(1.25, 0.75, 1)
- transform scale3d(1.25, 0.75, 1)
- -webkit-transform translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)
- transform translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)
- -webkit-transform translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)
- transform translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, -20deg)
- transform perspective(400px) rotate3d(1, 0, 0, -20deg)
- opacity 1
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, -20deg)
- transform perspective(400px) rotate3d(1, 0, 0, -20deg)
- opacity 1
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, -15deg)
- transform perspective(400px) rotate3d(0, 1, 0, -15deg)
- opacity 1
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, -15deg)
- transform perspective(400px) rotate3d(0, 1, 0, -15deg)
- opacity 1
-
-40%
- -webkit-transform scale3d(0.75, 1.25, 1)
- transform scale3d(0.75, 1.25, 1)
- -webkit-transform scale3d(0.75, 1.25, 1)
- transform scale3d(0.75, 1.25, 1)
- -webkit-transform rotate3d(0, 0, 1, -10deg)
- transform rotate3d(0, 0, 1, -10deg)
- -webkit-transform rotate3d(0, 0, 1, -10deg)
- transform rotate3d(0, 0, 1, -10deg)
- -webkit-transform scale3d(.9, .9, .9)
- transform scale3d(.9, .9, .9)
- -webkit-transform scale3d(.9, .9, .9)
- transform scale3d(.9, .9, .9)
- -webkit-transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)
- transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)
- -webkit-animation-timing-function ease-out
- animation-timing-function ease-out
- -webkit-transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)
- transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)
- -webkit-animation-timing-function ease-out
- animation-timing-function ease-out
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, -20deg)
- transform perspective(400px) rotate3d(1, 0, 0, -20deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, -20deg)
- transform perspective(400px) rotate3d(1, 0, 0, -20deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, -20deg)
- transform perspective(400px) rotate3d(0, 1, 0, -20deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, -20deg)
- transform perspective(400px) rotate3d(0, 1, 0, -20deg)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
-
-65%
- -webkit-transform scale3d(.95, 1.05, 1)
- transform scale3d(.95, 1.05, 1)
- -webkit-transform scale3d(.95, 1.05, 1)
- transform scale3d(.95, 1.05, 1)
-
-75%
- -webkit-transform scale3d(1.05, .95, 1)
- transform scale3d(1.05, .95, 1)
- -webkit-transform scale3d(1.05, .95, 1)
- transform scale3d(1.05, .95, 1)
- -webkit-transform translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)
- transform translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)
- -webkit-transform translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)
- transform translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)
- -webkit-transform translate3d(0, -10px, 0)
- transform translate3d(0, -10px, 0)
- -webkit-transform translate3d(0, -10px, 0)
- transform translate3d(0, -10px, 0)
- -webkit-transform translate3d(-10px, 0, 0)
- transform translate3d(-10px, 0, 0)
- -webkit-transform translate3d(-10px, 0, 0)
- transform translate3d(-10px, 0, 0)
- -webkit-transform translate3d(10px, 0, 0)
- transform translate3d(10px, 0, 0)
- -webkit-transform translate3d(10px, 0, 0)
- transform translate3d(10px, 0, 0)
- -webkit-transform translate3d(0, 10px, 0)
- transform translate3d(0, 10px, 0)
- -webkit-transform translate3d(0, 10px, 0)
- transform translate3d(0, 10px, 0)
-
-10%, 30%, 50%, 70%, 90%
- -webkit-transform translate3d(-10px, 0, 0)
- transform translate3d(-10px, 0, 0)
- -webkit-transform translate3d(-10px, 0, 0)
- transform translate3d(-10px, 0, 0)
-
-20%, 40%, 60%, 80%
- -webkit-transform translate3d(10px, 0, 0)
- transform translate3d(10px, 0, 0)
- -webkit-transform translate3d(10px, 0, 0)
- transform translate3d(10px, 0, 0)
-
-60%
- -webkit-transform rotate3d(0, 0, 1, 5deg)
- transform rotate3d(0, 0, 1, 5deg)
- -webkit-transform rotate3d(0, 0, 1, 5deg)
- transform rotate3d(0, 0, 1, 5deg)
- -webkit-transform translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)
- transform translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)
- -webkit-transform translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)
- transform translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)
- opacity 1
- -webkit-transform scale3d(1.03, 1.03, 1.03)
- transform scale3d(1.03, 1.03, 1.03)
- opacity 1
- -webkit-transform scale3d(1.03, 1.03, 1.03)
- transform scale3d(1.03, 1.03, 1.03)
- opacity 1
- -webkit-transform translate3d(0, 25px, 0)
- transform translate3d(0, 25px, 0)
- opacity 1
- -webkit-transform translate3d(0, 25px, 0)
- transform translate3d(0, 25px, 0)
- opacity 1
- -webkit-transform translate3d(25px, 0, 0)
- transform translate3d(25px, 0, 0)
- opacity 1
- -webkit-transform translate3d(25px, 0, 0)
- transform translate3d(25px, 0, 0)
- opacity 1
- -webkit-transform translate3d(-25px, 0, 0)
- transform translate3d(-25px, 0, 0)
- opacity 1
- -webkit-transform translate3d(-25px, 0, 0)
- transform translate3d(-25px, 0, 0)
- opacity 1
- -webkit-transform translate3d(0, -20px, 0)
- transform translate3d(0, -20px, 0)
- opacity 1
- -webkit-transform translate3d(0, -20px, 0)
- transform translate3d(0, -20px, 0)
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, 10deg)
- transform perspective(400px) rotate3d(1, 0, 0, 10deg)
- opacity 1
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, 10deg)
- transform perspective(400px) rotate3d(1, 0, 0, 10deg)
- opacity 1
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, 10deg)
- transform perspective(400px) rotate3d(0, 1, 0, 10deg)
- opacity 1
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, 10deg)
- transform perspective(400px) rotate3d(0, 1, 0, 10deg)
- opacity 1
- -webkit-transform skewX(20deg)
- transform skewX(20deg)
- opacity 1
- -webkit-transform skewX(20deg)
- transform skewX(20deg)
- opacity 1
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
- transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
- transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(10px, 0, 0)
- transform scale3d(.475, .475, .475) translate3d(10px, 0, 0)
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(10px, 0, 0)
- transform scale3d(.475, .475, .475) translate3d(10px, 0, 0)
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(-10px, 0, 0)
- transform scale3d(.475, .475, .475) translate3d(-10px, 0, 0)
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(-10px, 0, 0)
- transform scale3d(.475, .475, .475) translate3d(-10px, 0, 0)
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
- transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- opacity 1
- -webkit-transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
- transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
- -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
- animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
-
-80%
- -webkit-transform rotate3d(0, 0, 1, -5deg)
- transform rotate3d(0, 0, 1, -5deg)
- -webkit-transform rotate3d(0, 0, 1, -5deg)
- transform rotate3d(0, 0, 1, -5deg)
- -webkit-transform scale3d(.97, .97, .97)
- transform scale3d(.97, .97, .97)
- -webkit-transform scale3d(.97, .97, .97)
- transform scale3d(.97, .97, .97)
- -webkit-transform perspective(400px) scale3d(.95, .95, .95)
- transform perspective(400px) scale3d(.95, .95, .95)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- -webkit-transform perspective(400px) scale3d(.95, .95, .95)
- transform perspective(400px) scale3d(.95, .95, .95)
- -webkit-animation-timing-function ease-in
- animation-timing-function ease-in
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, -5deg)
- transform perspective(400px) rotate3d(1, 0, 0, -5deg)
- -webkit-transform perspective(400px) rotate3d(1, 0, 0, -5deg)
- transform perspective(400px) rotate3d(1, 0, 0, -5deg)
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, -5deg)
- transform perspective(400px) rotate3d(0, 1, 0, -5deg)
- -webkit-transform perspective(400px) rotate3d(0, 1, 0, -5deg)
- transform perspective(400px) rotate3d(0, 1, 0, -5deg)
- -webkit-transform skewX(-5deg)
- transform skewX(-5deg)
- opacity 1
- -webkit-transform skewX(-5deg)
- transform skewX(-5deg)
- opacity 1
-
-10%, 20%
- -webkit-transform scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)
- transform scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)
- -webkit-transform scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)
- transform scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)
-
-30%, 50%, 70%, 90%
- -webkit-transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)
- transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)
- -webkit-transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)
- transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)
-
-40%, 60%, 80%
- -webkit-transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)
- transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)
- -webkit-transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)
- transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)
-
-15%
- -webkit-transform translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg)
- transform translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg)
- -webkit-transform translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg)
- transform translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg)
-
-45%
- -webkit-transform translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)
- transform translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)
- -webkit-transform translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)
- transform translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)
-
-0%
- opacity 0
- -webkit-transform scale3d(.3, .3, .3)
- transform scale3d(.3, .3, .3)
- opacity 0
- -webkit-transform scale3d(.3, .3, .3)
- transform scale3d(.3, .3, .3)
- opacity 0
- -webkit-transform translate3d(0, -3000px, 0)
- transform translate3d(0, -3000px, 0)
- opacity 0
- -webkit-transform translate3d(0, -3000px, 0)
- transform translate3d(0, -3000px, 0)
- opacity 0
- -webkit-transform translate3d(-3000px, 0, 0)
- transform translate3d(-3000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(-3000px, 0, 0)
- transform translate3d(-3000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(3000px, 0, 0)
- transform translate3d(3000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(3000px, 0, 0)
- transform translate3d(3000px, 0, 0)
- opacity 0
- -webkit-transform translate3d(0, 3000px, 0)
- transform translate3d(0, 3000px, 0)
- opacity 0
- -webkit-transform translate3d(0, 3000px, 0)
- transform translate3d(0, 3000px, 0)
-
-20%
- -webkit-transform scale3d(1.1, 1.1, 1.1)
- transform scale3d(1.1, 1.1, 1.1)
- -webkit-transform scale3d(1.1, 1.1, 1.1)
- transform scale3d(1.1, 1.1, 1.1)
-
-50%, 55%
- opacity 1
- -webkit-transform scale3d(1.1, 1.1, 1.1)
- transform scale3d(1.1, 1.1, 1.1)
- opacity 1
- -webkit-transform scale3d(1.1, 1.1, 1.1)
- transform scale3d(1.1, 1.1, 1.1)
-
-40%, 45%
- opacity 1
- -webkit-transform translate3d(0, -20px, 0)
- transform translate3d(0, -20px, 0)
- opacity 1
- -webkit-transform translate3d(0, -20px, 0)
- transform translate3d(0, -20px, 0)
- opacity 1
- -webkit-transform translate3d(0, 20px, 0)
- transform translate3d(0, 20px, 0)
- opacity 1
- -webkit-transform translate3d(0, 20px, 0)
- transform translate3d(0, 20px, 0)
-
-20%, 60%
- -webkit-transform rotate3d(0, 0, 1, 80deg)
- transform rotate3d(0, 0, 1, 80deg)
- -webkit-transform-origin top left
- transform-origin top left
- -webkit-animation-timing-function ease-in-out
- animation-timing-function ease-in-out
- -webkit-transform rotate3d(0, 0, 1, 80deg)
- transform rotate3d(0, 0, 1, 80deg)
- -webkit-transform-origin top left
- transform-origin top left
- -webkit-animation-timing-function ease-in-out
- animation-timing-function ease-in-out
-
-40%, 80%
- -webkit-transform rotate3d(0, 0, 1, 60deg)
- transform rotate3d(0, 0, 1, 60deg)
- -webkit-transform-origin top left
- transform-origin top left
- -webkit-animation-timing-function ease-in-out
- animation-timing-function ease-in-out
- opacity 1
- -webkit-transform rotate3d(0, 0, 1, 60deg)
- transform rotate3d(0, 0, 1, 60deg)
- -webkit-transform-origin top left
- transform-origin top left
- -webkit-animation-timing-function ease-in-out
- animation-timing-function ease-in-out
- opacity 1
-
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/styles/index.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/index.styl b/flink-runtime-web/web-dashboard/app/styles/index.styl
index 6299185..e033264 100644
--- a/flink-runtime-web/web-dashboard/app/styles/index.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/index.styl
@@ -17,7 +17,6 @@
*/
@import 'nib'
-@import './animate'
sidebar-width = 250px
sidebar-width-small = 160px
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/app/styles/job.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/job.styl b/flink-runtime-web/web-dashboard/app/styles/job.styl
index f499260..0432071 100644
--- a/flink-runtime-web/web-dashboard/app/styles/job.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/job.styl
@@ -62,6 +62,11 @@
td
position: relative
+
+ .small-label
+ text-transform: uppercase
+ font-size: 13px
+ color: #999999
.label-group
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/bower.json
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/bower.json b/flink-runtime-web/web-dashboard/bower.json
index 38a9101..0e9b557 100644
--- a/flink-runtime-web/web-dashboard/bower.json
+++ b/flink-runtime-web/web-dashboard/bower.json
@@ -18,7 +18,7 @@
"angular": "~1.3.15",
"angular-ui-router": "~0.2.13",
"d3": "~3.5.5",
- "dagre-d3": "~0.4.8",
+ "dagre-d3": "~0.4.10",
"font-awesome": "~4.3.0",
"angular-moment": "~0.9.2",
"moment-duration-format": "~1.3.0",
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/web/css/index.css
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/css/index.css b/flink-runtime-web/web-dashboard/web/css/index.css
index 09f8291..0ffea9e 100644
--- a/flink-runtime-web/web-dashboard/web/css/index.css
+++ b/flink-runtime-web/web-dashboard/web/css/index.css
@@ -430,6 +430,11 @@ pre.exception {
.table.table-activable td {
position: relative;
}
+.table .small-label {
+ text-transform: uppercase;
+ font-size: 13px;
+ color: #999;
+}
.label-group .label {
display: inline-block;
width: 2em;
[23/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Auto-update overview page
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Auto-update overview page
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/907f773c
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/907f773c
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/907f773c
Branch: refs/heads/master
Commit: 907f773c5da8a67abae0bfaa56850816509bbb42
Parents: b26ce5a
Author: Piotr Godek <pi...@gmail.com>
Authored: Fri Aug 21 14:58:38 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:51 2015 +0200
----------------------------------------------------------------------
flink-runtime-web/README.md | 2 +-
.../app/partials/jobs/completed-jobs.jade | 2 +-
.../web-dashboard/app/partials/jobs/job.jade | 9 +-
.../app/partials/jobs/job.plan.node.jade | 4 +-
.../app/partials/jobs/running-jobs.jade | 8 +-
.../web-dashboard/app/partials/overview.jade | 10 +-
.../app/scripts/common/services.coffee | 2 +-
.../web-dashboard/app/scripts/index.coffee | 2 -
.../app/scripts/modules/jobs/jobs.ctrl.coffee | 2 +-
.../app/scripts/modules/jobs/jobs.dir.coffee | 37 +++----
.../app/scripts/modules/jobs/jobs.svc.coffee | 69 +++++++------
.../modules/overview/overview.ctrl.coffee | 10 +-
flink-runtime-web/web-dashboard/web/js/index.js | 103 +++++++++++--------
.../web/partials/jobs/completed-jobs.html | 2 +-
.../web-dashboard/web/partials/jobs/job.html | 10 +-
.../web/partials/jobs/job.plan.node.html | 4 +-
.../web/partials/jobs/running-jobs.html | 4 -
.../web-dashboard/web/partials/overview.html | 6 +-
18 files changed, 150 insertions(+), 136 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/README.md
----------------------------------------------------------------------
diff --git a/flink-runtime-web/README.md b/flink-runtime-web/README.md
index ad12943..9664681 100644
--- a/flink-runtime-web/README.md
+++ b/flink-runtime-web/README.md
@@ -113,7 +113,7 @@ mvn -DskipTests clean package
To simplify continuous development, one can use a *standalone proxy server*, together with automatic
re-compilation:
-1. Edit the file `app/scripts/index.coffee`. Comment/uncomment the lines that define the `webServer`, `jobServer`, and `newServer` URLs.
+1. Edit the file `app/scripts/index.coffee`. Comment/uncomment the lines that define the `webServer`, and `jobServer` URLs.
2. Re-compile the files via `gulp`. By calling `gulp watch`, the build-tool autocompiles future changes.
3. Start the proxy server via `node server.js`
4. Access the dashboard at [`http://localhost:3000`](http://localhost:3000)
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
index 1233a21..23303b2 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
@@ -38,7 +38,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tr(ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- td {{job['end-time'] - job['start-time']}} ms
+ td {{job.duration}} ms
td {{job.name}}
td {{job.jid}}
td.label-group
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
index 7fb3234..07e7402 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
@@ -32,11 +32,12 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main(ng-if="job")
.navbar-info.first.last.hidden-xs.hidden-sm
| {{ job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- | -
- | {{ job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ span(ng-if="job['end-time'] > -1")
+ | -
+ | {{ job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- .navbar-info.last.first
- | {{job['end-time'] - job['start-time']}} ms
+ .navbar-info.last.first(ng-if="job.duration > -1")
+ | {{job.duration}} ms
nav.navbar.navbar-default.navbar-fixed-top.navbar-main-additional(ng-if="job")
ul.nav.nav-tabs
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
index 9ca30f1..a554b89 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
@@ -25,7 +25,7 @@
.panel-info(ng-if="node.description")
.label-group
- bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{node.vertex.groupvertex[status]}}
+ bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{node.vertex.tasks[status]}}
.panel-heading.clearfix
.panel-info.first.last(ng-if="node.description")
@@ -40,7 +40,7 @@
tbody
tr(ng-repeat="vertex in node.vertex.groupvertex.groupmembers")
- td {{ vertex.vertexname | humanizeTaskName }}
+ td {{ vertex.name | humanizeTaskName }}
td
bs-label(status="{{vertex.vertexstatus}}") {{vertex.vertexstatus}}
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
index d76d9af..73ded56 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
@@ -27,8 +27,8 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
thead
tr
th Start Time
- th End Time
- th Duration
+ //- th End Time
+ //- th Duration
th Job Name
th Job ID
th Tasks
@@ -37,8 +37,8 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tbody
tr(ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- td {{job['end-time'] - job['start-time']}} ms
+ //- td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ //- td {{job.duration}} ms
td {{job.name}}
td {{job.jid}}
td.label-group
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/partials/overview.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/overview.jade b/flink-runtime-web/web-dashboard/app/partials/overview.jade
index aa92ac0..87f8f57 100644
--- a/flink-runtime-web/web-dashboard/app/partials/overview.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/overview.jade
@@ -87,8 +87,8 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
thead
tr
th Start Time
- th End Time
- th Duration
+ //- th End Time
+ //- th Duration
th Job Name
th Job ID
th Tasks
@@ -97,8 +97,8 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tbody
tr(ng-repeat="job in runningJobs" ui-sref="single-job.plan({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- td {{job.duration}}
+ //- td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ //- td {{job.duration}} ms
td {{job.name}}
td {{job.jid}}
td.label-group
@@ -126,7 +126,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tr(ng-repeat="job in finishedJobs" ui-sref="single-job.plan({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- td {{job['end-time'] - job['start-time']}} ms
+ td {{job.duration}} ms
td {{job.name}}
td {{job.jid}}
td.label-group
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee b/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
index 2e45a44..d3fc584 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
@@ -24,7 +24,7 @@ angular.module('flinkApp')
@loadConfig = ->
deferred = $q.defer()
- $http.get flinkConfig.newServer + "/config"
+ $http.get flinkConfig.jobServer + "/config"
.success (data, status, headers, config) ->
deferred.resolve(data)
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/scripts/index.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/index.coffee b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
index 6c52e12..3d01ef5 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
@@ -30,9 +30,7 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
.value 'flinkConfig', {
jobServer: 'http://localhost:8081'
- newServer: 'http://localhost:8081'
# jobServer: 'http://localhost:3000/new-server'
-# newServer: 'http://localhost:3000/new-server'
"refresh-interval": 10000
}
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
index ebd632b..1fc8eae 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
@@ -70,5 +70,5 @@ angular.module('flinkApp')
# --------------------------------------
.controller 'JobTimelineVertexController', ($scope, $state, $stateParams, JobsService) ->
- JobsService.getVertex($stateParams.jobid, $stateParams.vertexId).then (data) ->
+ JobsService.getVertex($stateParams.vertexId).then (data) ->
$scope.vertex = data
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
index 15a3bb6..bf10799 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
@@ -35,34 +35,32 @@ angular.module('flinkApp')
analyzeTime = (data) ->
testData = []
- angular.forEach data.groupvertex.groupmembers, (vertex, i) ->
- vTime = data.verticetimes[vertex.vertexid]
-
+ angular.forEach data.subtasks, (subtask, i) ->
testData.push {
- label: "#{vertex.vertexinstancename} (#{i})"
+ label: "#{subtask.host} (#{subtask.subtask})"
times: [
{
label: "Scheduled"
color: "#666"
borderColor: "#555"
- starting_time: vTime["SCHEDULED"] * 100
- ending_time: vTime["DEPLOYING"] * 100
+ starting_time: subtask.timestamps["SCHEDULED"]
+ ending_time: subtask.timestamps["DEPLOYING"]
type: 'regular'
}
{
label: "Deploying"
color: "#aaa"
borderColor: "#555"
- starting_time: vTime["DEPLOYING"] * 100
- ending_time: vTime["RUNNING"] * 100
+ starting_time: subtask.timestamps["DEPLOYING"]
+ ending_time: subtask.timestamps["RUNNING"]
type: 'regular'
}
{
label: "Running"
color: "#ddd"
borderColor: "#555"
- starting_time: vTime["RUNNING"] * 100
- ending_time: vTime["FINISHED"] * 100
+ starting_time: subtask.timestamps["RUNNING"]
+ ending_time: subtask.timestamps["FINISHED"]
type: 'regular'
}
]
@@ -115,26 +113,21 @@ angular.module('flinkApp')
label: "Scheduled"
color: "#cccccc"
borderColor: "#555"
- starting_time: data.oldV["SCHEDULED"]
- ending_time: data.oldV["SCHEDULED"] + 1
- # link: vertex.groupvertexid
+ starting_time: data.timestamps["CREATED"]
+ ending_time: data.timestamps["CREATED"] + 1
type: 'scheduled'
]
- angular.forEach data.oldV.groupvertices, (vertex) ->
- vTime = data.oldV.groupverticetimes[vertex.groupvertexid]
-
- # console.log vTime, vertex.groupvertexid
-
+ angular.forEach data.vertices, (vertex) ->
testData.push
times: [
- label: translateLabel(vertex.groupvertexname)
+ label: translateLabel(vertex.name)
color: "#d9f1f7"
borderColor: "#62cdea"
- starting_time: vTime["STARTED"]
- ending_time: vTime["ENDED"]
- link: vertex.groupvertexid
+ starting_time: vertex['start-time']
+ ending_time: vertex['end-time']
+ link: vertex.id
type: 'regular'
]
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index 72bdb6c..cc5c29b 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -69,7 +69,7 @@ angular.module('flinkApp')
@listJobs = ->
deferred = $q.defer()
- $http.get flinkConfig.newServer + "/joboverview"
+ $http.get flinkConfig.jobServer + "/joboverview"
.success (data, status, headers, config) ->
angular.forEach data, (list, listKey) ->
@@ -80,11 +80,6 @@ angular.module('flinkApp')
when 'cancelled' then jobs.cancelled = list
when 'failed' then jobs.failed = list
- # angular.forEach list, (jobid, index) ->
- # $http.get flinkConfig.newServer + "/jobs/" + jobid
- # .success (details) ->
- # list[index] = details
-
deferred.resolve(jobs)
notifyObservers()
@@ -100,23 +95,28 @@ angular.module('flinkApp')
currentJob = null
deferreds.job = $q.defer()
- $http.get flinkConfig.newServer + "/jobs/" + jobid
+ $http.get flinkConfig.jobServer + "/jobs/" + jobid
.success (data, status, headers, config) ->
- data.time = Date.now()
+ # data.time = Date.now()
- $http.get flinkConfig.newServer + "/jobs/" + jobid + "/vertices"
+ $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/vertices"
.success (vertices) ->
data = angular.extend(data, vertices)
- $http.get flinkConfig.jobServer + "/jobsInfo?get=job&job=" + jobid
- .success (oldVertices) ->
- data.oldV = oldVertices[0]
+ # $http.get flinkConfig.jobServer + "/jobsInfo?get=job&job=" + jobid
+ # .success (oldVertices) ->
+ # data.oldV = oldVertices[0]
+
+ $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/config"
+ .success (jobConfig) ->
+ data = angular.extend(data, jobConfig)
- $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/config"
- .success (jobConfig) ->
- data = angular.extend(data, jobConfig)
+ $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/exceptions"
+ .success (exceptions) ->
+ data.exceptions = exceptions
currentJob = data
+
deferreds.job.resolve(data)
deferreds.job.promise
@@ -125,7 +125,7 @@ angular.module('flinkApp')
currentPlan = null
deferreds.plan = $q.defer()
- $http.get flinkConfig.newServer + "/jobs/" + jobid + "/plan"
+ $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/plan"
.success (data) ->
currentPlan = data
@@ -142,33 +142,38 @@ angular.module('flinkApp')
null
- deferred = $q.defer()
- # if currentPlan
- # deferred.resolve(seekNode(nodeid, currentPlan.nodes))
- # else
- # # deferreds.plan.promise.then (data) ->
- # $q.all([deferreds.plan.promise, deferreds.job.promise]).then (data) ->
- # console.log 'resolving getNode'
- # deferred.resolve(seekNode(nodeid, currentPlan.nodes))
+ deferred = $q.defer()
$q.all([deferreds.plan.promise, deferreds.job.promise]).then (data) =>
foundNode = seekNode(nodeid, currentPlan.nodes)
- # @getVertex(currentJob.jid, currentJob.oldV.groupvertices[0].groupvertexid).then (vertex) ->
- @getVertex(currentJob.jid, nodeid).then (vertex) ->
- foundNode.vertex = vertex
- deferred.resolve(foundNode)
+ # $http.get flinkConfig.jobServer + "/jobsInfo?get=groupvertex&job=" + currentJob.jid + "&groupvertex=" + nodeid
+
+ foundNode.vertex = @seekVertex(nodeid)
+
+ deferred.resolve(foundNode)
deferred.promise
+ @seekVertex = (nodeid) ->
+ for vertex in currentJob.vertices
+ return vertex if vertex.id is nodeid
+
+ return null
+
- @getVertex = (jobId, vertexId) ->
+ @getVertex = (vertexid) ->
deferred = $q.defer()
- $http.get flinkConfig.jobServer + "/jobsInfo?get=groupvertex&job=" + jobId + "&groupvertex=" + vertexId
- .success (data) ->
- deferred.resolve(data)
+ $q.all([deferreds.job.promise]).then (data) =>
+ vertex = @seekVertex(vertexid)
+
+ $http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasktimes"
+ .success (data) ->
+ vertex.subtasks = data.subtasks
+
+ deferred.resolve(vertex)
deferred.promise
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee
index 828f276..dc8ab2c 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee
@@ -18,7 +18,7 @@
angular.module('flinkApp')
-.controller 'OverviewController', ($scope, OverviewService, JobsService) ->
+.controller 'OverviewController', ($scope, OverviewService, JobsService, $interval, flinkConfig) ->
$scope.jobObserver = ->
$scope.runningJobs = JobsService.getJobs('running')
$scope.finishedJobs = JobsService.getJobs('finished')
@@ -31,3 +31,11 @@ angular.module('flinkApp')
OverviewService.loadOverview().then (data) ->
$scope.overview = data
+
+ refresh = $interval ->
+ OverviewService.loadOverview().then (data) ->
+ $scope.overview = data
+ , flinkConfig["refresh-interval"]
+
+ $scope.$on '$destroy', ->
+ $interval.cancel(refresh)
[32/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Adjust
view for details of a job
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Adjust view for details of a job
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/c36b3d7a
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/c36b3d7a
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/c36b3d7a
Branch: refs/heads/master
Commit: c36b3d7ab76c5712614b259200e2f01a47a06f01
Parents: 6a3b095
Author: Piotr Godek <pi...@gmail.com>
Authored: Wed Sep 9 19:36:11 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:52 2015 +0200
----------------------------------------------------------------------
.../app/partials/jobs/completed-jobs.jade | 2 +-
.../app/partials/jobs/job.exceptions.jade | 2 +-
.../web-dashboard/app/partials/jobs/job.jade | 5 +-
.../app/partials/jobs/job.plan.jade | 13 +-
.../jobs/job.plan.node-list.accumulators.jade | 32 +
.../app/partials/jobs/job.plan.node-list.jade | 72 -
.../jobs/job.plan.node-list.overview.jade | 54 +
.../app/partials/jobs/job.plan.node-tabs.jade | 34 -
.../jobs/job.plan.node.accumulators.jade | 22 +-
.../app/partials/jobs/job.plan.node.jade | 47 -
.../partials/jobs/job.plan.node.properties.jade | 109 -
.../partials/jobs/job.plan.node.subtasks.jade | 5 +-
.../app/partials/jobs/job.properties.jade | 118 ++
.../app/partials/jobs/job.timeline.vertex.jade | 2 +-
.../app/partials/jobs/running-jobs.jade | 2 +-
.../web-dashboard/app/partials/overview.jade | 4 +-
.../app/scripts/common/filters.coffee | 4 +-
.../app/scripts/common/services.coffee | 2 -
.../web-dashboard/app/scripts/index.coffee | 39 +-
.../app/scripts/modules/jobs/jobs.ctrl.coffee | 90 +-
.../app/scripts/modules/jobs/jobs.dir.coffee | 10 +-
.../app/scripts/modules/jobs/jobs.svc.coffee | 26 +-
.../web-dashboard/app/styles/animate.styl | 1889 ++++++++++++++++++
.../web-dashboard/app/styles/graph.styl | 1 +
.../web-dashboard/app/styles/index.styl | 52 +-
.../web-dashboard/app/styles/job.styl | 10 +-
.../web-dashboard/web/css/index.css | 62 +-
flink-runtime-web/web-dashboard/web/js/index.js | 155 +-
.../web/partials/jobs/completed-jobs.html | 2 +-
.../web/partials/jobs/job.exceptions.html | 2 +-
.../web-dashboard/web/partials/jobs/job.html | 3 +-
.../web/partials/jobs/job.plan.html | 12 +-
.../jobs/job.plan.node-list.accumulators.html | 40 +
.../web/partials/jobs/job.plan.node-list.html | 69 -
.../jobs/job.plan.node-list.overview.html | 60 +
.../web/partials/jobs/job.plan.node-tabs.html | 13 -
.../jobs/job.plan.node.accumulators.html | 22 +-
.../web/partials/jobs/job.plan.node.html | 44 -
.../partials/jobs/job.plan.node.properties.html | 130 --
.../partials/jobs/job.plan.node.subtasks.html | 2 +-
.../web/partials/jobs/job.properties.html | 140 ++
.../web/partials/jobs/job.timeline.vertex.html | 2 +-
.../web/partials/jobs/running-jobs.html | 2 +-
.../web-dashboard/web/partials/overview.html | 4 +-
44 files changed, 2695 insertions(+), 715 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
index 23303b2..02c9fd2 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
@@ -35,7 +35,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
th Status
tbody
- tr(ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })")
+ tr(ng-repeat="job in jobs" ui-sref="single-job.plan.overview({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job.duration}} ms
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
index 09f5b36..1aa10b0 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
@@ -15,7 +15,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-.panel.panel-default.panel-multi
+.panel.panel-default.panel-multi(ng-if="exceptions['root-exception']")
.panel-heading.clearfix
.panel-title
| Root exception
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
index b264c72..9da922a 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
@@ -42,7 +42,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main(ng-if="job")
nav.navbar.navbar-default.navbar-fixed-top.navbar-main-additional(ng-if="job")
ul.nav.nav-tabs
li(ui-sref-active='active')
- a(ui-sref=".plan") Plan
+ a(ui-sref=".plan.overview") Plan
li(ui-sref-active='active')
a(ui-sref=".statistics") Job Accumulators / Statistics
@@ -55,6 +55,9 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main-additional(ng-if="job")
a(ui-sref=".exceptions") Exceptions
li(ui-sref-active='active')
+ a(ui-sref=".properties") Properties
+
+ li(ui-sref-active='active')
a(ui-sref=".config") Configuration
#content-inner.has-navbar-main-additional
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade
index 582d7e6..e2afdf3 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade
@@ -16,6 +16,15 @@
limitations under the License.
.canvas-wrapper
- div.main-canvas(job-plan, plan="plan", jobid="{{jobid}}")
+ div.main-canvas(job-plan, plan="plan", jobid="{{jobid}}", set-node="changeNode(nodeid)")
-div(ui-view="node-tabs")
+.panel.panel-default.panel-multi(ng-if="plan")
+ nav.navbar.navbar-default.navbar-secondary-additional
+ ul.nav.nav-tabs
+ li(ui-sref-active='active')
+ a(ui-sref=".overview({nodeid: nodeid})") Overview
+
+ li(ui-sref-active='active')
+ a(ui-sref=".accumulators({nodeid: nodeid})") Accumulators
+
+ .panel-body.clean(ui-view="node-details")
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.accumulators.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.accumulators.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.accumulators.jade
new file mode 100644
index 0000000..b3f1f54
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.accumulators.jade
@@ -0,0 +1,32 @@
+//
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+table.table.table-body-hover.table-clickable.table-activable
+ thead
+ tr
+ th Name
+ th Status
+
+ tbody(ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="changeNode(v.id)")
+ tr
+ td {{ v.name | humanizeText }}
+ td
+ bs-label(status="{{v.status}}") {{v.status}}
+
+ tr(ng-if="nodeid && v.id == nodeid")
+ td(colspan="10")
+ div(ng-include=" 'partials/jobs/job.plan.node.accumulators.html' ")
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
deleted file mode 100644
index b8a8a4c..0000000
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
+++ /dev/null
@@ -1,72 +0,0 @@
-//
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-table.table.table-body-hover.table-clickable.table-activable
- thead
- tr
- th.tab-column
- th Start Time
- th End Time
- th Duration
- th Name
- th Bytes read
- th Records read
- th Bytes written
- th Records written
- th Tasks
- th Status
-
- //- tbody(ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" id="vertex-row-{{v.id}}" ui-sref="{nodeid: v.id}")
- tbody(ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" ui-sref="{nodeid: v.id}")
- tr
- td.tab-column
- ul.nav.nav-tabs.tabs-vertical(ng-if="job.currentNode && v.id == job.currentNode.id")
- li(ng-class="{ active: ('single-job.plan.node.generic' | isState) }")
- a(ng-click="gotoGeneric(v.id)" title='Basic')
- i.fa.fa-tasks
-
- li(ng-class="{ active: ('single-job.plan.node.accumulators' | isState) }")
- a(ng-click="gotoAccumulators(v.id)" title='Accumulators')
- i.fa.fa-calculator
-
- td
- span(ng-if="v['start-time'] > -1") {{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- td
- span(ng-if="v['end-time'] > -1") {{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- td
- span(ng-if="v.duration > -1") {{ v.duration }} ms
-
- td {{ v.name | humanizeTaskName }}
- td {{ v.metrics['read-bytes'] }}
- td {{ v.metrics['read-records'] }}
- td {{ v.metrics['write-bytes'] }}
- td {{ v.metrics['write-records'] }}
- td
- .label-group
- bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{v.tasks[status]}}
-
- td
- bs-label(status="{{v.status}}") {{v.status}}
- tr(ng-if="job.currentNode && v.id == job.currentNode.id")
- td.tab-column
- td(colspan="10")
- //- a(ng-click="gotoGeneric(nodeid)") Gen!
- //- |
- //- a(ng-click="gotoAccumulators(nodeid)") Acc!
-
- div(ng-if="'single-job.plan.node.generic' | isState" ng-include=" 'partials/jobs/job.plan.node.subtasks.html' ")
- div(ng-if="'single-job.plan.node.accumulators' | isState" ng-include=" 'partials/jobs/job.plan.node.accumulators.html' ")
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.overview.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.overview.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.overview.jade
new file mode 100644
index 0000000..fbdc012
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.overview.jade
@@ -0,0 +1,54 @@
+//
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+table.table.table-body-hover.table-clickable.table-activable
+ thead
+ tr
+ th Start Time
+ th End Time
+ th Duration
+ th Name
+ th Bytes read
+ th Records read
+ th Bytes written
+ th Records written
+ th Tasks
+ th Status
+
+ tbody(ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="changeNode(v.id)")
+ tr
+ td
+ span(ng-if="v['start-time'] > -1") {{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ td
+ span(ng-if="v['end-time'] > -1") {{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ td
+ span(ng-if="v.duration > -1") {{ v.duration }} ms
+
+ td.td-long {{ v.name | humanizeText }}
+ td {{ v.metrics['read-bytes'] }}
+ td {{ v.metrics['read-records'] }}
+ td {{ v.metrics['write-bytes'] }}
+ td {{ v.metrics['write-records'] }}
+ td
+ .label-group
+ bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{v.tasks[status]}}
+
+ td
+ bs-label(status="{{v.status}}") {{v.status}}
+ tr(ng-if="nodeid && v.id == nodeid")
+ td(colspan="10")
+ div(ng-include=" 'partials/jobs/job.plan.node.subtasks.html' ")
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
deleted file mode 100644
index cc7961c..0000000
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
+++ /dev/null
@@ -1,34 +0,0 @@
-//
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-.panel.panel-default.panel-multi(ng-if="node")
- nav.navbar.navbar-default.navbar-secondary-additional(ng-if="job.currentNode")
- ul.nav.nav-tabs
- li(ui-sref-active='active')
- a(ui-sref=".generic({nodeid: nodeid})") Subtasks
-
- //- li(ui-sref-active='active')
- //- a(ui-sref=".accumulators({nodeid: nodeid})") Accumulators
-
- li(ui-sref-active='active')
- a(ui-sref=".properties({nodeid: nodeid})") Properties
-
- .panel-heading.clearfix(ng-if="'single-job.plan.node.properties' | isState")
- .panel-title
- | {{ node.description | humanizeTaskName }}
-
- .panel-body.clean(ui-view="node-details")
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
index 67d0921..4cb93fa 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
@@ -15,4 +15,24 @@
See the License for the specific language governing permissions and
limitations under the License.
-h3 Accumulators
+//- center(ng-if="!vertex")
+//- i.fa.fa-circle-o-notch.fa-spin.fa-3x
+
+div(ng-if="vertex")
+
+ div(ng-if="vertex.accumulators.length == 0")
+ p
+ i No accumulators
+
+ table.table.table-hover.table-clickable.table-activable.table-inner(ng-if="vertex.accumulators.length > 0")
+ thead
+ tr
+ th Name
+ th Type
+ th Value
+
+ tbody
+ tr(ng-repeat="accumulator in vertex.accumulators")
+ td {{ accumulator.name }}
+ td {{ accumulator.type }}
+ td {{ accumulator.value }}
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
deleted file mode 100644
index 3c5ed0f..0000000
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
+++ /dev/null
@@ -1,47 +0,0 @@
-//
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-.panel.panel-default.panel-multi(ng-if="node")
- .panel-heading.clearfix
- .panel-title
- | {{ node.operator }}
-
- .panel-info.first
- | ID: {{ node.id }}
-
- .panel-info(ng-if="node.description")
- .label-group
- bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{node.vertex.tasks[status]}}
-
- .panel-heading.clearfix
- .panel-info.thin.last(ng-if="node.description")
- span {{ vertex.name | humanizeTaskName }}
- //- span {{ node.description | humanizeTaskName }}
-
- nav.navbar.navbar-default.navbar-secondary-additional
- ul.nav.nav-tabs
- li(ui-sref-active='active')
- a(ui-sref=".subtasks") Subtasks
-
- li(ui-sref-active='active')
- a(ui-sref=".properties") Properties
-
- li(ui-sref-active='active')
- a(ui-sref=".accumulators") Accumulators
-
- .panel-body.clean
- div(ui-view="nodeTab")
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
deleted file mode 100644
index 0f5c9b7..0000000
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
+++ /dev/null
@@ -1,109 +0,0 @@
-//
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-.row
- .col-sm-6.col-md-4
- table.table.table-properties(ng-if="node.optimizer_properties.global_properties")
- thead
- tr
- th(colspan="2")
- | Global Data Properties
-
- tbody
- tr(ng-repeat="property in node.optimizer_properties.global_properties")
- td {{property.name}}
- td(table-property value="property.value")
-
- table.table.table-properties(ng-if="node.optimizer_properties.local_properties")
- thead
- tr
- th(colspan="2")
- | Local Data Properties
-
- tbody
- tr(ng-repeat="property in node.optimizer_properties.local_properties")
- td {{property.name}}
- td(table-property value="property.value")
-
- .visible-xs.visible-sm
- table.table.table-properties
- thead
- tr
- th(colspan="2")
- | Properties
-
- tbody
- tr
- td Operator
- td(table-property value="node.operator_strategy")
-
- tr
- td Parallelism
- td(table-property value="node.parallelism")
-
-
- .hidden-sm.col-md-4
- table.table.table-properties
- thead
- tr
- th(colspan="2")
- | Properties
-
- tbody
- tr
- td Operator
- td(table-property value="node.operator_strategy")
-
- tr
- td Parallelism
- td(table-property value="node.parallelism")
-
-
- table.table.table-properties(ng-if="node.optimizer_properties.estimates")
- thead
- tr
- th(colspan="2")
- | Size Estimates
-
- tbody
- tr(ng-repeat="property in node.optimizer_properties.estimates")
- td {{property.name}}
- td(table-property value="property.value")
-
- .col-sm-6.col-md-4
- .visible-xs.visible-sm
- table.table.table-properties(ng-if="node.optimizer_properties.estimates")
- thead
- tr
- th(colspan="2")
- | Size Estimates
-
- tbody
- tr(ng-repeat="property in node.optimizer_properties.estimates")
- td {{property.name}}
- td(table-property value="property.value")
-
- table.table.table-properties(ng-if="node.optimizer_properties.costs")
- thead
- tr
- th(colspan="2")
- | Cost Estimates
-
- tbody
- tr(ng-repeat="property in node.optimizer_properties.costs")
- td {{property.name}}
- td(table-property value="property.value")
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
index 6072dd0..3169e1a 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
@@ -16,7 +16,10 @@
limitations under the License.
-table.table.table-hover.table-clickable.table-activable.table-inner
+//- center(ng-if="!vertex")
+//- i.fa.fa-circle-o-notch.fa-spin.fa-3x
+
+table.table.table-hover.table-clickable.table-activable.table-inner(ng-if="vertex")
thead
tr
th Start Time
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.properties.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.properties.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.properties.jade
new file mode 100644
index 0000000..76cbc78
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.properties.jade
@@ -0,0 +1,118 @@
+//
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+.canvas-wrapper
+ div.main-canvas(job-plan, plan="plan", jobid="{{jobid}}", set-node="changeNode(nodeid)")
+
+.panel.panel-default(ng-if="node")
+ .panel-heading.clearfix
+ .panel-title
+ | {{ node.description | humanizeText }}
+
+ .panel-body.clean
+ .row
+ .col-sm-6.col-md-4
+ table.table.table-properties(ng-if="node.optimizer_properties.global_properties")
+ thead
+ tr
+ th(colspan="2")
+ | Global Data Properties
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.global_properties")
+ td {{property.name}}
+ td(table-property value="property.value")
+
+ table.table.table-properties(ng-if="node.optimizer_properties.local_properties")
+ thead
+ tr
+ th(colspan="2")
+ | Local Data Properties
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.local_properties")
+ td {{property.name}}
+ td(table-property value="property.value")
+
+ .visible-xs.visible-sm
+ table.table.table-properties
+ thead
+ tr
+ th(colspan="2")
+ | Properties
+
+ tbody
+ tr
+ td Operator
+ td(table-property value="node.operator_strategy")
+
+ tr
+ td Parallelism
+ td(table-property value="node.parallelism")
+
+
+ .hidden-sm.col-md-4
+ table.table.table-properties
+ thead
+ tr
+ th(colspan="2")
+ | Properties
+
+ tbody
+ tr
+ td Operator
+ td(table-property value="node.operator_strategy")
+
+ tr
+ td Parallelism
+ td(table-property value="node.parallelism")
+
+
+ table.table.table-properties(ng-if="node.optimizer_properties.estimates")
+ thead
+ tr
+ th(colspan="2")
+ | Size Estimates
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.estimates")
+ td {{property.name}}
+ td(table-property value="property.value")
+
+ .col-sm-6.col-md-4
+ .visible-xs.visible-sm
+ table.table.table-properties(ng-if="node.optimizer_properties.estimates")
+ thead
+ tr
+ th(colspan="2")
+ | Size Estimates
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.estimates")
+ td {{property.name}}
+ td(table-property value="property.value")
+
+ table.table.table-properties(ng-if="node.optimizer_properties.costs")
+ thead
+ tr
+ th(colspan="2")
+ | Cost Estimates
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.costs")
+ td {{property.name}}
+ td(table-property value="property.value")
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
index 5c912f3..7ceb328 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
@@ -18,7 +18,7 @@
.panel.panel-default.panel-multi(ng-if="vertex")
.panel-heading.clearfix
.panel-title
- | {{ vertex.groupvertex.groupvertexname | humanizeTaskName }}
+ | {{ vertex.groupvertex.groupvertexname | humanizeText }}
//- .panel-info.first
//- | Vertex ID: {{ vertex.groupvertex.groupvertexid }}
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
index e0c500b..9c02c02 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
@@ -35,7 +35,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
th Status
tbody
- tr(ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })")
+ tr(ng-repeat="job in jobs" ui-sref="single-job.plan.overview({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job.duration}} ms
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/partials/overview.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/overview.jade b/flink-runtime-web/web-dashboard/app/partials/overview.jade
index 846d833..cad0792 100644
--- a/flink-runtime-web/web-dashboard/app/partials/overview.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/overview.jade
@@ -95,7 +95,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
th Status
tbody
- tr(ng-repeat="job in runningJobs" ui-sref="single-job.plan({ jobid: job.jid })")
+ tr(ng-repeat="job in runningJobs" ui-sref="single-job.plan.overview({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job.duration}} ms
@@ -123,7 +123,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
th Status
tbody
- tr(ng-repeat="job in finishedJobs" ui-sref="single-job.plan({ jobid: job.jid })")
+ tr(ng-repeat="job in finishedJobs" ui-sref="single-job.plan.overview({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job.duration}} ms
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee b/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee
index 83b51c1..c0db7d0 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee
@@ -28,7 +28,7 @@ angular.module('flinkApp')
amDurationFormatExtendedFilter
-.filter "humanizeTaskName", ->
+.filter "humanizeText", ->
(text) ->
# TODO: extend... a lot
- if text then text.replace(">", ">").replace("<br/>","") else ''
+ if text then text.replace(/>/g, ">").replace(/<br\/>/g,"") else ''
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee b/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
index d3fc584..a54c502 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
@@ -19,8 +19,6 @@
angular.module('flinkApp')
.service 'MainService', ($http, flinkConfig, $q) ->
- jobObservers = []
-
@loadConfig = ->
deferred = $q.defer()
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/index.coffee b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
index 3bd1dac..b636c77 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
@@ -86,43 +86,25 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
.state "single-job.plan",
url: ""
+ abstract: true
views:
details:
templateUrl: "partials/jobs/job.plan.html"
controller: 'JobPlanController'
- .state "single-job.plan.node",
- url: "/vertex/{nodeid}"
- abstract: true
- views:
- 'node-tabs':
- templateUrl: "partials/jobs/job.plan.node-tabs.html"
- controller: 'JobPlanNodeTabsController'
-
- .state "single-job.plan.node.generic",
+ .state "single-job.plan.overview",
url: ""
views:
'node-details':
- controller: 'JobPlanNodeListController'
- templateUrl: "partials/jobs/job.plan.node-list.html"
+ templateUrl: "partials/jobs/job.plan.node-list.overview.html"
+ controller: 'JobPlanOverviewController'
- .state "single-job.plan.node.accumulators",
+ .state "single-job.plan.accumulators",
url: "/accumulators"
views:
'node-details':
- controller: 'JobPlanNodeListAccumulatorsController'
- templateUrl: "partials/jobs/job.plan.node-list.html"
- # controller: 'JobPlanNodePropertiesController'
- # templateUrl: "partials/jobs/job.plan.node.properties.html"
-
- .state "single-job.plan.node.properties",
- url: "/properties"
- views:
- 'node-details':
- controller: 'JobPlanNodePropertiesController'
- templateUrl: "partials/jobs/job.plan.node.properties.html"
-
-
+ templateUrl: "partials/jobs/job.plan.node-list.accumulators.html"
+ controller: 'JobPlanAccumulatorsController'
.state "single-job.timeline",
url: "/timeline"
@@ -150,6 +132,13 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
templateUrl: "partials/jobs/job.exceptions.html"
controller: 'JobExceptionsController'
+ .state "single-job.properties",
+ url: "/properties"
+ views:
+ details:
+ templateUrl: "partials/jobs/job.properties.html"
+ controller: 'JobPropertiesController'
+
.state "single-job.config",
url: "/config"
views:
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
index eee69f0..1e367c9 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
@@ -43,6 +43,8 @@ angular.module('flinkApp')
# --------------------------------------
.controller 'SingleJobController', ($scope, $state, $stateParams, JobsService, $rootScope) ->
+ console.log 'SingleJobController'
+
$scope.jobid = $stateParams.jobid
$rootScope.job = null
$rootScope.plan = null
@@ -60,68 +62,43 @@ angular.module('flinkApp')
.controller 'JobPlanController', ($scope, $state, $stateParams, JobsService) ->
console.log 'JobPlanController'
+ $scope.nodeid = null
$scope.stateList = JobsService.stateList()
-# --------------------------------------
-
-.controller 'JobPlanNodeController', ($scope, $state, $stateParams, JobsService, $anchorScroll) ->
- console.log 'JobPlanNodeController'
+ $scope.changeNode = (nodeid) ->
+ if nodeid != $scope.nodeid
+ $scope.nodeid = nodeid
+ $scope.vertex = null
- $scope.nodeid = $stateParams.nodeid
+ if $state.is('single-job.plan.overview')
+ JobsService.getSubtasks(nodeid).then (data) ->
+ $scope.vertex = data
- $scope.stateList = JobsService.stateList()
+ else if $state.is('single-job.plan.accumulators')
+ JobsService.getAccumulators(nodeid).then (data) ->
+ $scope.vertex = data
- JobsService.getNode($scope.nodeid).then (data) ->
- $scope.node = data
- $scope.job.currentNode = data
-
- # anchor = "vertex-row-" + data.id
- # console.log anchor
- # $anchorScroll(anchor)
+ else
+ $scope.nodeid = null
+ $scope.vertex = null
# --------------------------------------
-.controller 'JobPlanNodeTabsController', ($scope, $stateParams, JobsService) ->
- console.log 'JobPlanNodeTabsController'
-
- $scope.nodeid = $stateParams.nodeid
- $scope.stateList = JobsService.stateList()
+.controller 'JobPlanOverviewController', ($scope, JobsService) ->
+ console.log 'JobPlanOverviewController'
- JobsService.getNode($scope.nodeid).then (data) ->
- $scope.node = data
- $scope.job.currentNode = data
+ if $scope.nodeid and !$scope.vertex.st
+ JobsService.getSubtasks($scope.nodeid).then (data) ->
+ $scope.vertex = data
# --------------------------------------
-.controller 'JobPlanNodeListController', ($scope, $stateParams, JobsService, $state) ->
- console.log 'JobPlanNodeListController'
+.controller 'JobPlanAccumulatorsController', ($scope, JobsService) ->
+ console.log 'JobPlanAccumulatorsController'
- $scope.gotoAccumulators = (nodeid) ->
- $state.go('^.accumulators', { nodeid: nodeid })
-
- JobsService.getSubtasks($stateParams.nodeid).then (data) ->
- $scope.vertex = data
-
-# --------------------------------------
-
-.controller 'JobPlanNodeListAccumulatorsController', ($scope, $stateParams, JobsService, $state) ->
- console.log 'JobPlanNodeListAccumulatorsController'
-
- $scope.gotoGeneric = (nodeid) ->
- $state.go('^.generic', { nodeid: nodeid })
-
- # JobsService.getSubtasks($stateParams.nodeid).then (data) ->
- # $scope.vertex = data
-
-# --------------------------------------
-
-.controller 'JobPlanNodePropertiesController', ($scope, $state, $stateParams, JobsService) ->
- console.log 'JobPlanNodePropertiesController'
-
-# --------------------------------------
-
-.controller 'JobPlanNodeAccumulatorsController', ($scope, $state, $stateParams, JobsService) ->
- console.log 'JobPlanNodeAccumulatorsController'
+ if $scope.nodeid and !$scope.vertex.accumulators
+ JobsService.getAccumulators($scope.nodeid).then (data) ->
+ $scope.vertex = data
# --------------------------------------
@@ -135,3 +112,18 @@ angular.module('flinkApp')
JobsService.loadExceptions().then (data) ->
$scope.exceptions = data
+# --------------------------------------
+
+.controller 'JobPropertiesController', ($scope, JobsService) ->
+ console.log 'JobPropertiesController'
+
+ $scope.changeNode = (nodeid) ->
+ if nodeid != $scope.nodeid
+ $scope.nodeid = nodeid
+
+ JobsService.getNode(nodeid).then (data) ->
+ $scope.node = data
+
+ else
+ $scope.nodeid = null
+ $scope.node = null
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
index b0f9bae..74f616a 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
@@ -171,6 +171,7 @@ angular.module('flinkApp')
scope:
plan: '='
+ setNode: '&'
link: (scope, elem, attrs) ->
mainZoom = d3.behavior.zoom()
@@ -244,7 +245,8 @@ angular.module('flinkApp')
# creates the label of a node, in info is stored, whether it is a special node (like a mirror in an iteration)
createLabelNode = (el, info, maxW, maxH) ->
- labelValue = "<a href='#/jobs/" + jobid + "/vertex/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>"
+ # labelValue = "<a href='#/jobs/" + jobid + "/vertex/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>"
+ labelValue = "<div href='#/jobs/" + jobid + "/vertex/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>"
# Nodename
if info is "mirror"
@@ -270,7 +272,8 @@ angular.module('flinkApp')
labelValue += "<h5>Parallelism: " + el.parallelism + "</h5>" unless el.parallelism is ""
labelValue += "<h5>Operation: " + shortenString(el.operator_strategy) + "</h5>" unless el.operator is `undefined`
- labelValue += "</a>"
+ # labelValue += "</a>"
+ labelValue += "</div>"
labelValue
# Extends the label of a node with an additional svg Element to present the iteration.
@@ -449,6 +452,9 @@ angular.module('flinkApp')
)
mainZoom(d3mainSvg)
+ d3mainSvgG.selectAll('.node').on 'click', (d) ->
+ scope.setNode({ nodeid: d })
+
scope.$watch attrs.plan, (newPlan) ->
drawGraph(newPlan) if newPlan
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index f840c14..3142e4f 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -100,17 +100,7 @@ angular.module('flinkApp')
$http.get flinkConfig.jobServer + "/jobs/" + jobid
.success (data, status, headers, config) =>
- # console.log data.vertices
@setEndTimes(data.vertices)
- # console.log data.vertices
-
- # $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/vertices"
- # .success (vertices) ->
- # data = angular.extend(data, vertices)
-
- # $http.get flinkConfig.jobServer + "/jobsInfo?get=job&job=" + jobid
- # .success (oldVertices) ->
- # data.oldV = oldVertices[0]
$http.get flinkConfig.jobServer + "/jobs/" + jobid + "/config"
.success (jobConfig) ->
@@ -146,9 +136,7 @@ angular.module('flinkApp')
deferred = $q.defer()
- # $q.all([deferreds.plan.promise, deferreds.job.promise]).then (data) =>
$q.all([deferreds.job.promise]).then (data) =>
- # foundNode = seekNode(nodeid, currentPlan.nodes)
foundNode = seekNode(nodeid, currentJob.plan.nodes)
foundNode.vertex = @seekVertex(nodeid)
@@ -192,6 +180,20 @@ angular.module('flinkApp')
deferred.promise
+ @getAccumulators = (vertexid) ->
+ deferred = $q.defer()
+
+ $q.all([deferreds.job.promise]).then (data) =>
+ vertex = @seekVertex(vertexid)
+
+ $http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/accumulators"
+ .success (data) ->
+ vertex.accumulators = data['user-accumulators']
+
+ deferred.resolve(vertex)
+
+ deferred.promise
+
@loadExceptions = ->
deferred = $q.defer()
[45/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Timeline for running tasks
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
new file mode 100644
index 0000000..f2e5e7e
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
@@ -0,0 +1,20 @@
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<h1>Accumulators</h1>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
index dcc19f7..f97b077 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
@@ -28,135 +28,17 @@ limitations under the License.
</div>
</div>
<div class="panel-heading clearfix">
- <div ng-if="node.description" class="panel-info first last"><span>{{ node.description | humanizeTaskName }}</span></div>
- </div>
- <div class="panel-body">
- <table class="table table-hover table-clickable">
- <thead>
- <tr>
- <th>Name</th>
- <th>Status</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="vertex in node.vertex.groupvertex.groupmembers">
- <td>{{ vertex.name | humanizeTaskName }}</td>
- <td>
- <bs-label status="{{vertex.vertexstatus}}">{{vertex.vertexstatus}}</bs-label>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
-</div>
-<div ng-if="node" class="row">
- <div class="col-sm-6 col-md-4">
- <table ng-if="node.optimizer_properties.global_properties" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Global Data Properties</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.global_properties">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
- <table ng-if="node.optimizer_properties.local_properties" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Local Data Properties</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.local_properties">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
- <div class="visible-xs visible-sm">
- <table class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Properties</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>Operator</td>
- <td table-property="table-property" value="node.operator_strategy"></td>
- </tr>
- <tr>
- <td>Parallelism</td>
- <td table-property="table-property" value="node.parallelism"></td>
- </tr>
- </tbody>
- </table>
+ <div ng-if="node.description" class="panel-info thin last"><span>{{ vertex.name | humanizeTaskName }}</span>
</div>
</div>
- <div class="hidden-sm col-md-4">
- <table class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Properties</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>Operator</td>
- <td table-property="table-property" value="node.operator_strategy"></td>
- </tr>
- <tr>
- <td>Parallelism</td>
- <td table-property="table-property" value="node.parallelism"></td>
- </tr>
- </tbody>
- </table>
- <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Size Estimates</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.estimates">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
- </div>
- <div class="col-sm-6 col-md-4">
- <div class="visible-xs visible-sm">
- <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Size Estimates</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.estimates">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
- </div>
- <table ng-if="node.optimizer_properties.costs" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Cost Estimates</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.costs">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
+ <nav class="navbar navbar-default navbar-secondary-additional">
+ <ul class="nav nav-tabs">
+ <li ui-sref-active="active"><a ui-sref=".subtasks">Subtasks</a></li>
+ <li ui-sref-active="active"><a ui-sref=".properties">Properties</a></li>
+ <li ui-sref-active="active"><a ui-sref=".accumulators">Accumulators</a></li>
+ </ul>
+ </nav>
+ <div class="panel-body clean">
+ <div ui-view="nodeTab"></div>
</div>
</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
new file mode 100644
index 0000000..7592c65
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
@@ -0,0 +1,131 @@
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+-->
+<div ng-if="node" class="row">
+ <div class="col-sm-6 col-md-4">
+ <table ng-if="node.optimizer_properties.global_properties" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Global Data Properties</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.global_properties">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ <table ng-if="node.optimizer_properties.local_properties" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Local Data Properties</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.local_properties">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="visible-xs visible-sm">
+ <table class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Properties</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Operator</td>
+ <td table-property="table-property" value="node.operator_strategy"></td>
+ </tr>
+ <tr>
+ <td>Parallelism</td>
+ <td table-property="table-property" value="node.parallelism"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <div class="hidden-sm col-md-4">
+ <table class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Properties</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Operator</td>
+ <td table-property="table-property" value="node.operator_strategy"></td>
+ </tr>
+ <tr>
+ <td>Parallelism</td>
+ <td table-property="table-property" value="node.parallelism"></td>
+ </tr>
+ </tbody>
+ </table>
+ <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Size Estimates</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.estimates">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div class="col-sm-6 col-md-4">
+ <div class="visible-xs visible-sm">
+ <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Size Estimates</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.estimates">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <table ng-if="node.optimizer_properties.costs" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Cost Estimates</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.costs">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
new file mode 100644
index 0000000..34fedd0
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
@@ -0,0 +1,53 @@
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<table class="table table-hover table-clickable">
+ <thead>
+ <tr>
+ <th>Start Time</th>
+ <th>End Time</th>
+ <th>Duration</th>
+ <th>Bytes read</th>
+ <th>Records read</th>
+ <th>Bytes written</th>
+ <th>Records written</th>
+ <th>Tasks</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>{{ vertex['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</td>
+ <td>{{ vertex['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</td>
+ <td>{{ vertex.duration }} ms</td>
+ <td>{{ vertex.metrics['read-bytes'] }}</td>
+ <td>{{ vertex.metrics['read-records'] }}</td>
+ <td>{{ vertex.metrics['write-bytes'] }}</td>
+ <td>{{ vertex.metrics['write-records'] }}</td>
+ <td>
+ <div class="label-group">
+ <bs-label status="{{status}}" ng-repeat="(index, status) in stateList">{{vertex.tasks[status]}}</bs-label>
+ </div>
+ </td>
+ <td>
+ <bs-label status="{{vertex.status}}">{{vertex.status}}</bs-label>
+ </td>
+ </tr>
+ </tbody>
+</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
index 11ccaa3..edd524f 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
@@ -26,6 +26,8 @@ limitations under the License.
<thead>
<tr>
<th>Start Time</th>
+ <th>End Time</th>
+ <th>Duration</th>
<th>Job Name</th>
<th>Job ID</th>
<th>Tasks</th>
@@ -35,6 +37,8 @@ limitations under the License.
<tbody>
<tr ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job.duration}} ms</td>
<td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/web/partials/overview.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/overview.html b/flink-runtime-web/web-dashboard/web/partials/overview.html
index 31812b7..c88b779 100644
--- a/flink-runtime-web/web-dashboard/web/partials/overview.html
+++ b/flink-runtime-web/web-dashboard/web/partials/overview.html
@@ -83,6 +83,8 @@ limitations under the License.
<thead>
<tr>
<th>Start Time</th>
+ <th>End Time</th>
+ <th>Duration</th>
<th>Job Name</th>
<th>Job ID</th>
<th>Tasks</th>
@@ -92,6 +94,8 @@ limitations under the License.
<tbody>
<tr ng-repeat="job in runningJobs" ui-sref="single-job.plan({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job.duration}} ms</td>
<td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
[47/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Timeline for running tasks
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Timeline for running tasks
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/6d873e60
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/6d873e60
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/6d873e60
Branch: refs/heads/master
Commit: 6d873e60cf63a24157a4b0f5026c34833da46401
Parents: 7138cd5
Author: Piotr Godek <pi...@gmail.com>
Authored: Thu Aug 27 23:25:13 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:52 2015 +0200
----------------------------------------------------------------------
.../jobs/job.plan.node.accumulators.jade | 18 +++
.../app/partials/jobs/job.plan.node.jade | 116 ++--------------
.../partials/jobs/job.plan.node.properties.jade | 110 +++++++++++++++
.../partials/jobs/job.plan.node.subtasks.jade | 47 +++++++
.../app/partials/jobs/running-jobs.jade | 8 +-
.../web-dashboard/app/partials/overview.jade | 8 +-
.../web-dashboard/app/scripts/index.coffee | 19 +++
.../app/scripts/modules/jobs/jobs.ctrl.coffee | 3 +
.../app/scripts/modules/jobs/jobs.dir.coffee | 21 +--
.../app/scripts/modules/jobs/jobs.svc.coffee | 41 +++---
.../web-dashboard/app/styles/index.styl | 17 ++-
.../web-dashboard/web/css/index.css | 41 ++++--
flink-runtime-web/web-dashboard/web/js/index.js | 108 ++++++++++-----
.../jobs/job.plan.node.accumulators.html | 20 +++
.../web/partials/jobs/job.plan.node.html | 138 ++-----------------
.../partials/jobs/job.plan.node.properties.html | 131 ++++++++++++++++++
.../partials/jobs/job.plan.node.subtasks.html | 53 +++++++
.../web/partials/jobs/running-jobs.html | 4 +
.../web-dashboard/web/partials/overview.html | 4 +
19 files changed, 593 insertions(+), 314 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
new file mode 100644
index 0000000..a50b134
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
@@ -0,0 +1,18 @@
+//
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+h1 Accumulators
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
index a554b89..f72f412 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
@@ -28,113 +28,23 @@
bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{node.vertex.tasks[status]}}
.panel-heading.clearfix
- .panel-info.first.last(ng-if="node.description")
- span {{ node.description | humanizeTaskName }}
+ .panel-info.thin.last(ng-if="node.description")
+ span {{ vertex.name | humanizeTaskName }}
+ //- span {{ node.description | humanizeTaskName }}
- .panel-body
- table.table.table-hover.table-clickable
- thead
- tr
- th Name
- th Status
+ nav.navbar.navbar-default.navbar-secondary-additional
+ ul.nav.nav-tabs
+ li(ui-sref-active='active')
+ a(ui-sref=".subtasks") Subtasks
- tbody
- tr(ng-repeat="vertex in node.vertex.groupvertex.groupmembers")
- td {{ vertex.name | humanizeTaskName }}
- td
- bs-label(status="{{vertex.vertexstatus}}") {{vertex.vertexstatus}}
+ li(ui-sref-active='active')
+ a(ui-sref=".properties") Properties
-//-----------------------------------------------------------------
+ li(ui-sref-active='active')
+ a(ui-sref=".accumulators") Accumulators
-.row(ng-if="node")
- .col-sm-6.col-md-4
- table.table.table-properties(ng-if="node.optimizer_properties.global_properties")
- thead
- tr
- th(colspan="2")
- | Global Data Properties
+ .panel-body.clean
+ div(ui-view="nodeTab")
- tbody
- tr(ng-repeat="property in node.optimizer_properties.global_properties")
- td {{property.name}}
- td(table-property value="property.value")
- table.table.table-properties(ng-if="node.optimizer_properties.local_properties")
- thead
- tr
- th(colspan="2")
- | Local Data Properties
- tbody
- tr(ng-repeat="property in node.optimizer_properties.local_properties")
- td {{property.name}}
- td(table-property value="property.value")
-
- .visible-xs.visible-sm
- table.table.table-properties
- thead
- tr
- th(colspan="2")
- | Properties
-
- tbody
- tr
- td Operator
- td(table-property value="node.operator_strategy")
-
- tr
- td Parallelism
- td(table-property value="node.parallelism")
-
-
- .hidden-sm.col-md-4
- table.table.table-properties
- thead
- tr
- th(colspan="2")
- | Properties
-
- tbody
- tr
- td Operator
- td(table-property value="node.operator_strategy")
-
- tr
- td Parallelism
- td(table-property value="node.parallelism")
-
-
- table.table.table-properties(ng-if="node.optimizer_properties.estimates")
- thead
- tr
- th(colspan="2")
- | Size Estimates
-
- tbody
- tr(ng-repeat="property in node.optimizer_properties.estimates")
- td {{property.name}}
- td(table-property value="property.value")
-
- .col-sm-6.col-md-4
- .visible-xs.visible-sm
- table.table.table-properties(ng-if="node.optimizer_properties.estimates")
- thead
- tr
- th(colspan="2")
- | Size Estimates
-
- tbody
- tr(ng-repeat="property in node.optimizer_properties.estimates")
- td {{property.name}}
- td(table-property value="property.value")
-
- table.table.table-properties(ng-if="node.optimizer_properties.costs")
- thead
- tr
- th(colspan="2")
- | Cost Estimates
-
- tbody
- tr(ng-repeat="property in node.optimizer_properties.costs")
- td {{property.name}}
- td(table-property value="property.value")
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
new file mode 100644
index 0000000..19d5c0d
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
@@ -0,0 +1,110 @@
+//
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+.row(ng-if="node")
+ .col-sm-6.col-md-4
+ table.table.table-properties(ng-if="node.optimizer_properties.global_properties")
+ thead
+ tr
+ th(colspan="2")
+ | Global Data Properties
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.global_properties")
+ td {{property.name}}
+ td(table-property value="property.value")
+
+ table.table.table-properties(ng-if="node.optimizer_properties.local_properties")
+ thead
+ tr
+ th(colspan="2")
+ | Local Data Properties
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.local_properties")
+ td {{property.name}}
+ td(table-property value="property.value")
+
+ .visible-xs.visible-sm
+ table.table.table-properties
+ thead
+ tr
+ th(colspan="2")
+ | Properties
+
+ tbody
+ tr
+ td Operator
+ td(table-property value="node.operator_strategy")
+
+ tr
+ td Parallelism
+ td(table-property value="node.parallelism")
+
+
+ .hidden-sm.col-md-4
+ table.table.table-properties
+ thead
+ tr
+ th(colspan="2")
+ | Properties
+
+ tbody
+ tr
+ td Operator
+ td(table-property value="node.operator_strategy")
+
+ tr
+ td Parallelism
+ td(table-property value="node.parallelism")
+
+
+ table.table.table-properties(ng-if="node.optimizer_properties.estimates")
+ thead
+ tr
+ th(colspan="2")
+ | Size Estimates
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.estimates")
+ td {{property.name}}
+ td(table-property value="property.value")
+
+ .col-sm-6.col-md-4
+ .visible-xs.visible-sm
+ table.table.table-properties(ng-if="node.optimizer_properties.estimates")
+ thead
+ tr
+ th(colspan="2")
+ | Size Estimates
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.estimates")
+ td {{property.name}}
+ td(table-property value="property.value")
+
+ table.table.table-properties(ng-if="node.optimizer_properties.costs")
+ thead
+ tr
+ th(colspan="2")
+ | Cost Estimates
+
+ tbody
+ tr(ng-repeat="property in node.optimizer_properties.costs")
+ td {{property.name}}
+ td(table-property value="property.value")
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
new file mode 100644
index 0000000..d20b889
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
@@ -0,0 +1,47 @@
+//
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+table.table.table-hover.table-clickable
+ thead
+ tr
+ th Start Time
+ th End Time
+ th Duration
+ //- th Name
+ th Bytes read
+ th Records read
+ th Bytes written
+ th Records written
+ th Tasks
+ th Status
+
+ tbody
+ tr
+ td {{ vertex['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ td {{ vertex['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ td {{ vertex.duration }} ms
+ //- td {{ vertex.name | humanizeTaskName }}
+ td {{ vertex.metrics['read-bytes'] }}
+ td {{ vertex.metrics['read-records'] }}
+ td {{ vertex.metrics['write-bytes'] }}
+ td {{ vertex.metrics['write-records'] }}
+ td
+ .label-group
+ bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{vertex.tasks[status]}}
+
+ td
+ bs-label(status="{{vertex.status}}") {{vertex.status}}
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
index 73ded56..e0c500b 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
@@ -27,8 +27,8 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
thead
tr
th Start Time
- //- th End Time
- //- th Duration
+ th End Time
+ th Duration
th Job Name
th Job ID
th Tasks
@@ -37,8 +37,8 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tbody
tr(ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- //- td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- //- td {{job.duration}} ms
+ td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ td {{job.duration}} ms
td {{job.name}}
td {{job.jid}}
td.label-group
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/partials/overview.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/overview.jade b/flink-runtime-web/web-dashboard/app/partials/overview.jade
index 87f8f57..846d833 100644
--- a/flink-runtime-web/web-dashboard/app/partials/overview.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/overview.jade
@@ -87,8 +87,8 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
thead
tr
th Start Time
- //- th End Time
- //- th Duration
+ th End Time
+ th Duration
th Job Name
th Job ID
th Tasks
@@ -97,8 +97,8 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tbody
tr(ng-repeat="job in runningJobs" ui-sref="single-job.plan({ jobid: job.jid })")
td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- //- td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
- //- td {{job.duration}} ms
+ td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ td {{job.duration}} ms
td {{job.name}}
td {{job.jid}}
td.label-group
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/scripts/index.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/index.coffee b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
index d7db7e0..63b5bf2 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
@@ -88,11 +88,30 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
.state "single-job.plan.node",
url: "/vertex/{nodeid}"
+ abstract: true
views:
node:
templateUrl: "partials/jobs/job.plan.node.html"
controller: 'JobPlanNodeController'
+ .state "single-job.plan.node.subtasks",
+ url: ""
+ views:
+ nodeTab:
+ templateUrl: "partials/jobs/job.plan.node.subtasks.html"
+
+ .state "single-job.plan.node.properties",
+ url: "/properties"
+ views:
+ nodeTab:
+ templateUrl: "partials/jobs/job.plan.node.properties.html"
+
+ .state "single-job.plan.node.accumulators",
+ url: "/properties"
+ views:
+ nodeTab:
+ templateUrl: "partials/jobs/job.plan.node.accumulators.html"
+
.state "single-job.timeline",
url: "/timeline"
views:
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
index d6ffdb0..d74f781 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
@@ -67,6 +67,9 @@ angular.module('flinkApp')
JobsService.getNode($scope.nodeid).then (data) ->
$scope.node = data
+ JobsService.getVertex($scope.nodeid).then (data) ->
+ $scope.vertex = data
+
# --------------------------------------
.controller 'JobTimelineVertexController', ($scope, $state, $stateParams, JobsService) ->
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
index bf10799..b0f9bae 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
@@ -120,16 +120,17 @@ angular.module('flinkApp')
angular.forEach data.vertices, (vertex) ->
- testData.push
- times: [
- label: translateLabel(vertex.name)
- color: "#d9f1f7"
- borderColor: "#62cdea"
- starting_time: vertex['start-time']
- ending_time: vertex['end-time']
- link: vertex.id
- type: 'regular'
- ]
+ if vertex['start-time'] > -1
+ testData.push
+ times: [
+ label: translateLabel(vertex.name)
+ color: "#d9f1f7"
+ borderColor: "#62cdea"
+ starting_time: vertex['start-time']
+ ending_time: vertex['end-time']
+ link: vertex.id
+ type: 'regular'
+ ]
chart = d3.timeline().stack().click((d, i, datum) ->
if d.link
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index 6624b4f..2290261 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -66,19 +66,22 @@ angular.module('flinkApp')
when 'total' then 'black'
else 'default'
+ @setEndTimes = (list) ->
+ angular.forEach list, (job, jobKey) ->
+ unless job['end-time'] > -1
+ job['end-time'] = job['start-time'] + job['duration']
+
@listJobs = ->
deferred = $q.defer()
$http.get flinkConfig.jobServer + "/joboverview"
- .success (data, status, headers, config) ->
-
- angular.forEach data, (list, listKey) ->
-
+ .success (data, status, headers, config) =>
+ angular.forEach data, (list, listKey) =>
switch listKey
- when 'running' then jobs.running = list
- when 'finished' then jobs.finished = list
- when 'cancelled' then jobs.cancelled = list
- when 'failed' then jobs.failed = list
+ when 'running' then jobs.running = @setEndTimes(list)
+ when 'finished' then jobs.finished = @setEndTimes(list)
+ when 'cancelled' then jobs.cancelled = @setEndTimes(list)
+ when 'failed' then jobs.failed = @setEndTimes(list)
deferred.resolve(jobs)
notifyObservers()
@@ -96,24 +99,26 @@ angular.module('flinkApp')
deferreds.job = $q.defer()
$http.get flinkConfig.jobServer + "/jobs/" + jobid
- .success (data, status, headers, config) ->
- # data.time = Date.now()
+ .success (data, status, headers, config) =>
+ # console.log data.vertices
+ @setEndTimes(data.vertices)
+ # console.log data.vertices
- $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/vertices"
- .success (vertices) ->
- data = angular.extend(data, vertices)
+ # $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/vertices"
+ # .success (vertices) ->
+ # data = angular.extend(data, vertices)
# $http.get flinkConfig.jobServer + "/jobsInfo?get=job&job=" + jobid
# .success (oldVertices) ->
# data.oldV = oldVertices[0]
- $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/config"
- .success (jobConfig) ->
- data = angular.extend(data, jobConfig)
+ $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/config"
+ .success (jobConfig) ->
+ data = angular.extend(data, jobConfig)
- currentJob = data
+ currentJob = data
- deferreds.job.resolve(data)
+ deferreds.job.resolve(data)
deferreds.job.promise
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/app/styles/index.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/index.styl b/flink-runtime-web/web-dashboard/app/styles/index.styl
index 608dc3a..295bc47 100644
--- a/flink-runtime-web/web-dashboard/app/styles/index.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/index.styl
@@ -73,6 +73,13 @@ sidebar-width = 250px
.nav-tabs
margin: 0 -20px
padding: 0 20px
+
+ .navbar-secondary-additional
+ border: none
+ padding: 0 20px
+ margin-bottom: 0
+ .nav-tabs
+ margin: 0 -20px
&.sidebar-visible
margin-left: sidebar-width
@@ -129,7 +136,7 @@ sidebar-width = 250px
.navbar-secondary
overflow: auto
-.navbar-main, .navbar-secondary, .navbar-main-additional, .panel.panel-multi
+.navbar-main, .navbar-secondary, .navbar-main-additional, .panel.panel-multi, .navbar-secondary-additional
.navbar-title, .panel-title
float: left
font-size: 18px
@@ -173,8 +180,12 @@ sidebar-width = 250px
background-color: #fdfdfd
color: #999
font-size: 13px
+
+ &.clean
+ color: inherit
+ font-size: inherit
-.navbar-main-additional
+.navbar-main-additional, .navbar-secondary-additional
min-height: 40px
background-color: #fdfdfd
@@ -276,6 +287,6 @@ pre.exception
.navbar-main, .navbar-main-additional
left: sidebar-width
- .navbar-main, .navbar-secondary, .navbar-main-additional
+ .navbar-main, .navbar-secondary, .navbar-main-additional, .navbar-secondary-additional
.navbar-title
padding: 12px 20px 13px 20px
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/web/css/index.css
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/css/index.css b/flink-runtime-web/web-dashboard/web/css/index.css
index 438ff1b..8d4f45c 100644
--- a/flink-runtime-web/web-dashboard/web/css/index.css
+++ b/flink-runtime-web/web-dashboard/web/css/index.css
@@ -84,6 +84,14 @@
margin: 0 -20px;
padding: 0 20px;
}
+#content .navbar-secondary-additional {
+ border: none;
+ padding: 0 20px;
+ margin-bottom: 0;
+}
+#content .navbar-secondary-additional .nav-tabs {
+ margin: 0 -20px;
+}
#content.sidebar-visible {
margin-left: 250px;
}
@@ -143,10 +151,12 @@
.navbar-secondary .navbar-title,
.navbar-main-additional .navbar-title,
.panel.panel-multi .navbar-title,
+.navbar-secondary-additional .navbar-title,
.navbar-main .panel-title,
.navbar-secondary .panel-title,
.navbar-main-additional .panel-title,
-.panel.panel-multi .panel-title {
+.panel.panel-multi .panel-title,
+.navbar-secondary-additional .panel-title {
float: left;
font-size: 18px;
padding: 12px 20px 13px 10px;
@@ -157,10 +167,12 @@
.navbar-secondary .navbar-info,
.navbar-main-additional .navbar-info,
.panel.panel-multi .navbar-info,
+.navbar-secondary-additional .navbar-info,
.navbar-main .panel-info,
.navbar-secondary .panel-info,
.navbar-main-additional .panel-info,
-.panel.panel-multi .panel-info {
+.panel.panel-multi .panel-info,
+.navbar-secondary-additional .panel-info {
float: left;
font-size: 14px;
padding: 15px 15px 15px 15px;
@@ -173,10 +185,12 @@
.navbar-secondary .navbar-info .overflow,
.navbar-main-additional .navbar-info .overflow,
.panel.panel-multi .navbar-info .overflow,
+.navbar-secondary-additional .navbar-info .overflow,
.navbar-main .panel-info .overflow,
.navbar-secondary .panel-info .overflow,
.navbar-main-additional .panel-info .overflow,
-.panel.panel-multi .panel-info .overflow {
+.panel.panel-multi .panel-info .overflow,
+.navbar-secondary-additional .panel-info .overflow {
position: absolute;
display: block;
-o-text-overflow: ellipsis;
@@ -190,20 +204,24 @@
.navbar-secondary .navbar-info.first,
.navbar-main-additional .navbar-info.first,
.panel.panel-multi .navbar-info.first,
+.navbar-secondary-additional .navbar-info.first,
.navbar-main .panel-info.first,
.navbar-secondary .panel-info.first,
.navbar-main-additional .panel-info.first,
-.panel.panel-multi .panel-info.first {
+.panel.panel-multi .panel-info.first,
+.navbar-secondary-additional .panel-info.first {
border-left: 1px solid #e7e7e7;
}
.navbar-main .navbar-info.last,
.navbar-secondary .navbar-info.last,
.navbar-main-additional .navbar-info.last,
.panel.panel-multi .navbar-info.last,
+.navbar-secondary-additional .navbar-info.last,
.navbar-main .panel-info.last,
.navbar-secondary .panel-info.last,
.navbar-main-additional .panel-info.last,
-.panel.panel-multi .panel-info.last {
+.panel.panel-multi .panel-info.last,
+.navbar-secondary-additional .panel-info.last {
border-right: none;
}
.panel.panel-multi .panel-heading {
@@ -218,11 +236,17 @@
color: #999;
font-size: 13px;
}
-.navbar-main-additional {
+.panel.panel-multi .panel-body.clean {
+ color: inherit;
+ font-size: inherit;
+}
+.navbar-main-additional,
+.navbar-secondary-additional {
min-height: 40px;
background-color: #fdfdfd;
}
-.navbar-main-additional .navbar-info {
+.navbar-main-additional .navbar-info,
+.navbar-secondary-additional .navbar-info {
font-size: 13px;
padding: 10px 15px 10px 15px;
}
@@ -437,7 +461,8 @@ svg.graph .node-label {
}
.navbar-main .navbar-title,
.navbar-secondary .navbar-title,
- .navbar-main-additional .navbar-title {
+ .navbar-main-additional .navbar-title,
+ .navbar-secondary-additional .navbar-title {
padding: 12px 20px 13px 20px;
}
}
[34/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] New
node organization
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index 3808bcb..0aa7882 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -15,6 +15,8 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
return JobsService.listJobs();
}, flinkConfig["refresh-interval"]);
});
+}]).config(["$uiViewScrollProvider", function($uiViewScrollProvider) {
+ return $uiViewScrollProvider.useAnchorScroll();
}]).config(["$stateProvider", "$urlRouterProvider", function($stateProvider, $urlRouterProvider) {
$stateProvider.state("overview", {
url: "/overview",
@@ -61,32 +63,27 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
url: "/vertex/{nodeid}",
abstract: true,
views: {
- node: {
- templateUrl: "partials/jobs/job.plan.node.html",
- controller: 'JobPlanNodeController'
+ 'node-tabs': {
+ templateUrl: "partials/jobs/job.plan.node-tabs.html",
+ controller: 'JobPlanNodeTabsController'
}
}
- }).state("single-job.plan.node.subtasks", {
+ }).state("single-job.plan.node.generic", {
url: "",
views: {
- nodeTab: {
- templateUrl: "partials/jobs/job.plan.node.subtasks.html"
+ 'node-details': {
+ controller: 'JobPlanNodeListController',
+ templateUrl: "partials/jobs/job.plan.node-list.html"
}
}
}).state("single-job.plan.node.properties", {
url: "/properties",
views: {
- nodeTab: {
+ 'node-details': {
+ controller: 'JobPlanNodePropertiesController',
templateUrl: "partials/jobs/job.plan.node.properties.html"
}
}
- }).state("single-job.plan.node.accumulators", {
- url: "/properties",
- views: {
- nodeTab: {
- templateUrl: "partials/jobs/job.plan.node.accumulators.html"
- }
- }
}).state("single-job.timeline", {
url: "/timeline",
views: {
@@ -224,25 +221,40 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
}]).controller('SingleJobController', ["$scope", "$state", "$stateParams", "JobsService", "$rootScope", function($scope, $state, $stateParams, JobsService, $rootScope) {
$scope.jobid = $stateParams.jobid;
$rootScope.job = null;
+ $rootScope.plan = null;
JobsService.loadJob($stateParams.jobid).then(function(data) {
- return $rootScope.job = data;
+ $rootScope.job = data;
+ return $rootScope.plan = data.plan;
});
return $scope.$on('$destroy', function() {
- return $rootScope.job = null;
+ $rootScope.job = null;
+ return $rootScope.plan = null;
});
}]).controller('JobPlanController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
- return JobsService.loadPlan($stateParams.jobid).then(function(data) {
- return $scope.plan = data;
- });
-}]).controller('JobPlanNodeController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
+ console.log('JobPlanController');
+ return $scope.stateList = JobsService.stateList();
+}]).controller('JobPlanNodeController', ["$scope", "$state", "$stateParams", "JobsService", "$anchorScroll", function($scope, $state, $stateParams, JobsService, $anchorScroll) {
+ console.log('JobPlanNodeController');
$scope.nodeid = $stateParams.nodeid;
$scope.stateList = JobsService.stateList();
- JobsService.getNode($scope.nodeid).then(function(data) {
- return $scope.node = data;
+ return JobsService.getNode($scope.nodeid).then(function(data) {
+ $scope.node = data;
+ return $scope.job.currentNode = data;
});
- return JobsService.getVertex($scope.nodeid).then(function(data) {
- return $scope.vertex = data;
+}]).controller('JobPlanNodeTabsController', ["$scope", "$stateParams", "JobsService", function($scope, $stateParams, JobsService) {
+ console.log('JobPlanNodeTabsController');
+ $scope.nodeid = $stateParams.nodeid;
+ return JobsService.getNode($scope.nodeid).then(function(data) {
+ $scope.node = data;
+ return $scope.job.currentNode = data;
});
+}]).controller('JobPlanNodeListController', ["$scope", "$stateParams", "JobsService", function($scope, $stateParams, JobsService) {
+ console.log('JobPlanNodeListController');
+ return $scope.stateList = JobsService.stateList();
+}]).controller('JobPlanNodePropertiesController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
+ return console.log('JobPlanNodePropertiesController');
+}]).controller('JobPlanNodeAccumulatorsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
+ return console.log('JobPlanNodeAccumulatorsController');
}]).controller('JobTimelineVertexController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
return JobsService.getVertex($stateParams.vertexId).then(function(data) {
return $scope.vertex = data;
@@ -808,10 +820,10 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
return null;
};
deferred = $q.defer();
- $q.all([deferreds.plan.promise, deferreds.job.promise]).then((function(_this) {
+ $q.all([deferreds.job.promise]).then((function(_this) {
return function(data) {
var foundNode;
- foundNode = seekNode(nodeid, currentPlan.nodes);
+ foundNode = seekNode(nodeid, currentJob.plan.nodes);
foundNode.vertex = _this.seekVertex(nodeid);
return deferred.resolve(foundNode);
};
@@ -899,4 +911,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsImNvbW1vbi9zZXJ2aWNlcy5jb2ZmZWUiLCJjb21tb24vc2VydmljZXMuanMiLCJtb2R1bGVzL2pvYnMvam9icy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuanMiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuZGlyLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLnN2Yy5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LmN0cmwuanMiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkEsUUFBUSxPQUFPLFlBQVksQ0FBQyxhQUFhLGtCQUl4QyxtQkFBSSxTQUFDLFlBQUQ7RUFDSCxXQUFXLGlCQUFpQjtFQ3JCNUIsT0RzQkEsV0FBVyxjQUFjLFdBQUE7SUFDdkIsV0FBVyxpQkFBaUIsQ0FBQyxXQUFXO0lDckJ4QyxPRHNCQSxXQUFXLGVBQWU7O0lBSTdCLE1BQU0sZUFBZTtFQUN
wQixXQUFXO0VBRVgsb0JBQW9CO0dBS3JCLCtEQUFJLFNBQUMsYUFBYSxhQUFhLGFBQWEsV0FBeEM7RUM1QkgsT0Q2QkEsWUFBWSxhQUFhLEtBQUssU0FBQyxRQUFEO0lBQzVCLFFBQVEsT0FBTyxhQUFhO0lBRTVCLFlBQVk7SUM3QlosT0QrQkEsVUFBVSxXQUFBO01DOUJSLE9EK0JBLFlBQVk7T0FDWixZQUFZOztJQUtqQixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sd0JBQ0w7SUFBQSxLQUFLO0lBQ0wsVUFBVTtJQUNWLE9BQ0U7TUFBQSxNQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLGlDQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTs7O0tBRWxCLE1BQU0sbUNBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNB
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
lBQVksUUFBUTs7RUFFNUMsWUFBWSxpQkFBaUIsT0FBTztFQUNwQyxPQUFPLElBQUksWUFBWSxXQUFBO0lDbEJyQixPRG1CQSxZQUFZLG1CQUFtQixPQUFPOztFQUV4QyxPQUFPO0VBRVAsZ0JBQWdCLGVBQWUsS0FBSyxTQUFDLE1BQUQ7SUNuQmxDLE9Eb0JBLE9BQU8sV0FBVzs7RUFFcEIsVUFBVSxVQUFVLFdBQUE7SUNuQmxCLE9Eb0JBLGdCQUFnQixlQUFlLEtBQUssU0FBQyxNQUFEO01DbkJsQyxPRG9CQSxPQUFPLFdBQVc7O0tBQ3BCLFlBQVk7RUNsQmQsT0RvQkEsT0FBTyxJQUFJLFlBQVksV0FBQTtJQ25CckIsT0RvQkEsVUFBVSxPQUFPOzs7QUNqQnJCO0FDTEEsUUFBUSxPQUFPLFlBRWQsUUFBUSxrREFBbUIsU0FBQyxPQUFPLGFBQWEsSUFBckI7RUFDMUIsSUFBQTtFQUFBLFdBQVc7RUFFWCxLQUFDLGVBQWUsV0FBQTtJQUNkLElBQUE7SUFBQSxXQUFXLEdBQUc7SUFFZCxNQUFNLElBQUksWUFBWSxZQUFZLGFBQ2pDLFFBQVEsU0FBQyxNQUFNLFFBQVEsU0FBUyxRQUF4QjtNQUNQLFdBQVc7TUNwQlgsT0RxQkEsU0FBUyxRQUFROztJQ25CbkIsT0RxQkEsU0FBUzs7RUNuQlgsT0RxQkE7O0FDbkJGIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXR
lZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcsIFsndWkucm91dGVyJywgJ2FuZ3VsYXJNb21lbnQnXSlcblxuIyAtLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4ucnVuICgkcm9vdFNjb3BlKSAtPlxuICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gZmFsc2VcbiAgJHJvb3RTY29wZS5zaG93U2lkZWJhciA9IC0+XG4gICAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9ICEkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlXG4gICAgJHJvb3RTY29wZS5zaWRlYmFyQ2xhc3MgPSAnZm9yY2Utc2hvdydcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4udmFsdWUgJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnXG4jICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjMwMDAvbmV3LXNlcnZlcidcbiAgXCJyZWZyZXNoLWludGVydmFsXCI6IDEwMDAwXG59XG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLnJ1biAoSm9ic1NlcnZpY2UsIE1haW5TZXJ2aWNlLCBmbGlua0NvbmZpZywgJGludGVydmFsKSAtPlxuICBNYWluU2VydmljZS5sb2FkQ29uZmlnKCkudGhlbiAoY29uZmlnKSAtPlxuICAgIGFuZ3VsYXIuZXh0ZW5kIGZsaW5rQ29uZmlnLCBjb25maWdcblxuICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKClcblxuICAgICRpbnRlcnZhbCAtPlxuICAgICAgSm9ic1NlcnZpY2UubGlzdEpvYnMoKVxuICAgICwgZmxpbmtDb25maWdbXCJyZWZyZXNoLWludGVydmFsXCJdXG5cblxuIyAtLS0tLS0tLS0tLS0tL
S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29uZmlnICgkc3RhdGVQcm92aWRlciwgJHVybFJvdXRlclByb3ZpZGVyKSAtPlxuICAkc3RhdGVQcm92aWRlci5zdGF0ZSBcIm92ZXJ2aWV3XCIsXG4gICAgdXJsOiBcIi9vdmVydmlld1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9vdmVydmlldy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udHJvbGxlcidcblxuICAuc3RhdGUgXCJydW5uaW5nLWpvYnNcIixcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL3J1bm5pbmctam9icy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgXG4gIC5zdGF0ZSBcImNvbXBsZXRlZC1qb2JzXCIsXG4gICAgdXJsOiBcIi9jb21wbGV0ZWQtam9ic1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYlwiLFxuICAgIHVybDogXCIvam9icy97am9iaWR9XCJcbiAgICBhYnN0cmFjdDogdHJ1ZVxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdG
VtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdTaW5nbGVKb2JDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhblwiLFxuICAgIHVybDogXCJcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Db250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhbi5ub2RlXCIsXG4gICAgdXJsOiBcIi92ZXJ0ZXgve25vZGVpZH1cIlxuICAgIGFic3RyYWN0OiB0cnVlXG4gICAgdmlld3M6XG4gICAgICBub2RlOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZUNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm5vZGUuc3VidGFza3NcIixcbiAgICB1cmw6IFwiXCJcbiAgICB2aWV3czpcbiAgICAgIG5vZGVUYWI6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5zdWJ0YXNrcy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4ubm9kZS5wcm9wZXJ0aWVzXCIsXG4gICAgdXJsOiBcIi9wcm9wZXJ0aWVzXCJcbiAgICB2aWV3czpcbiAgICAgIG5vZGVUYWI6XG4gICAgICAgIHRlbXBsYXR
lVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5wcm9wZXJ0aWVzLmh0bWxcIlxuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhbi5ub2RlLmFjY3VtdWxhdG9yc1wiLFxuICAgIHVybDogXCIvcHJvcGVydGllc1wiXG4gICAgdmlld3M6XG4gICAgICBub2RlVGFiOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUuYWNjdW11bGF0b3JzLmh0bWxcIlxuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IudGltZWxpbmVcIixcbiAgICB1cmw6IFwiL3RpbWVsaW5lXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLmh0bWxcIlxuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsXG4gICAgdXJsOiBcIi97dmVydGV4SWR9XCJcbiAgICB2aWV3czpcbiAgICAgIHZlcnRleDpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUudmVydGV4Lmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2Iuc3RhdGlzdGljc1wiLFxuICAgIHVybDogXCIvc3RhdGlzdGljc1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5zdGF0aXN0aWNzLmh0bWxcIlxuXG4g
IC5zdGF0ZSBcInNpbmdsZS1qb2IuZXhjZXB0aW9uc1wiLFxuICAgIHVybDogXCIvZXhjZXB0aW9uc1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5leGNlcHRpb25zLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iRXhjZXB0aW9uc0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5jb25maWdcIixcbiAgICB1cmw6IFwiL2NvbmZpZ1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5jb25maWcuaHRtbFwiXG5cbiAgJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZSBcIi9vdmVydmlld1wiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pLnJ1bihmdW5jdGlvbigkcm9vdFNjb3BlKSB7XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZTtcbiAgcmV0dXJuICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSBmdW5jdGlvbigpIHtcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGU7XG4gICAgcmV0dXJuICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnO1xuICB9O1xufSkudmFsdWUoJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnLFxuI
CBcInJlZnJlc2gtaW50ZXJ2YWxcIjogMTAwMDBcbn0pLnJ1bihmdW5jdGlvbihKb2JzU2VydmljZSwgTWFpblNlcnZpY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIHtcbiAgcmV0dXJuIE1haW5TZXJ2aWNlLmxvYWRDb25maWcoKS50aGVuKGZ1bmN0aW9uKGNvbmZpZykge1xuICAgIGFuZ3VsYXIuZXh0ZW5kKGZsaW5rQ29uZmlnLCBjb25maWcpO1xuICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCk7XG4gICAgcmV0dXJuICRpbnRlcnZhbChmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBKb2JzU2VydmljZS5saXN0Sm9icygpO1xuICAgIH0sIGZsaW5rQ29uZmlnW1wicmVmcmVzaC1pbnRlcnZhbFwiXSk7XG4gIH0pO1xufSkuY29uZmlnKGZ1bmN0aW9uKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIHtcbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUoXCJvdmVydmlld1wiLCB7XG4gICAgdXJsOiBcIi9vdmVydmlld1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwicnVubmluZy1qb2JzXCIsIHtcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy
1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwiY29tcGxldGVkLWpvYnNcIiwge1xuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iXCIsIHtcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwiLFxuICAgIGFic3RyYWN0OiB0cnVlLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhblwiLCB7XG4gICAgdXJsOiBcIlwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhbi5ub2RlXCIsIHtcbiA
gICB1cmw6IFwiL3ZlcnRleC97bm9kZWlkfVwiLFxuICAgIGFic3RyYWN0OiB0cnVlLFxuICAgIHZpZXdzOiB7XG4gICAgICBub2RlOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZUNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhbi5ub2RlLnN1YnRhc2tzXCIsIHtcbiAgICB1cmw6IFwiXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG5vZGVUYWI6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLnN1YnRhc2tzLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnBsYW4ubm9kZS5wcm9wZXJ0aWVzXCIsIHtcbiAgICB1cmw6IFwiL3Byb3BlcnRpZXNcIixcbiAgICB2aWV3czoge1xuICAgICAgbm9kZVRhYjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUucHJvcGVydGllcy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuLm5vZGUuYWNjdW11bGF0b3JzXCIsIHtcbiAgICB1cmw6IFwiL3Byb3BlcnRpZXNcIixcbiAgICB2aWV3czoge1xuICAgICAgbm9kZVRhYjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUuYWNjdW11
bGF0b3JzLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnRpbWVsaW5lXCIsIHtcbiAgICB1cmw6IFwiL3RpbWVsaW5lXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHtcbiAgICB1cmw6IFwiL3t2ZXJ0ZXhJZH1cIixcbiAgICB2aWV3czoge1xuICAgICAgdmVydGV4OiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLnZlcnRleC5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2Iuc3RhdGlzdGljc1wiLCB7XG4gICAgdXJsOiBcIi9zdGF0aXN0aWNzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2Iuc3RhdGlzdGljcy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5leGNlcHRpb25zXCIsIHtcbiAgICB1cmw6IFwiL2V4Y2VwdGlvbnNcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb
2JzL2pvYi5leGNlcHRpb25zLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYkV4Y2VwdGlvbnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLmNvbmZpZ1wiLCB7XG4gICAgdXJsOiBcIi9jb25maWdcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5jb25maWcuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KTtcbiAgcmV0dXJuICR1cmxSb3V0ZXJQcm92aWRlci5vdGhlcndpc2UoXCIvb3ZlcnZpZXdcIik7XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIG
F0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnYnNMYWJlbCcsIChKb2JzU2VydmljZSkgLT5cbiAgdHJhbnNjbHVkZTogdHJ1ZVxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOiBcbiAgICBnZXRMYWJlbENsYXNzOiBcIiZcIlxuICAgIHN0YXR1czogXCJAXCJcblxuICB0ZW1wbGF0ZTogXCI8c3BhbiB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKSc+PG5nLXRyYW5zY2x1ZGU+PC9uZy10cmFuc2NsdWRlPjwvc3Bhbj5cIlxuICBcbiAgbGluazogKHNjb3BlLCBlbGVtZW50LCBhdHRycyk
gLT5cbiAgICBzY29wZS5nZXRMYWJlbENsYXNzID0gLT5cbiAgICAgICdsYWJlbCBsYWJlbC0nICsgSm9ic1NlcnZpY2UudHJhbnNsYXRlTGFiZWxTdGF0ZShhdHRycy5zdGF0dXMpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICdpbmRpY2F0b3JQcmltYXJ5JywgKEpvYnNTZXJ2aWNlKSAtPlxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOiBcbiAgICBnZXRMYWJlbENsYXNzOiBcIiZcIlxuICAgIHN0YXR1czogJ0AnXG5cbiAgdGVtcGxhdGU6IFwiPGkgdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknIC8+XCJcbiAgXG4gIGxpbms6IChzY29wZSwgZWxlbWVudCwgYXR0cnMpIC0+XG4gICAgc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IC0+XG4gICAgICAnZmEgZmEtY2lyY2xlIGluZGljYXRvciBpbmRpY2F0b3ItJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAndGFibGVQcm9wZXJ0eScsIC0+XG4gIHJlcGxhY2U6IHRydWVcbiAgc2NvcGU6XG4gICAgdmFsdWU6ICc9J1xuXG4gIHRlbXBsYXRlOiBcIjx0ZCB0aXRsZT1cXFwie3t2YWx1ZSB8fCAnTm9uZSd9fVxcXCI+e3t2YWx1ZSB8fCAnTm9uZSd9fTwvdGQ+XCJcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0Fw
cCcpLmRpcmVjdGl2ZSgnYnNMYWJlbCcsIGZ1bmN0aW9uKEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiB7XG4gICAgdHJhbnNjbHVkZTogdHJ1ZSxcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICBnZXRMYWJlbENsYXNzOiBcIiZcIixcbiAgICAgIHN0YXR1czogXCJAXCJcbiAgICB9LFxuICAgIHRlbXBsYXRlOiBcIjxzcGFuIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJz48bmctdHJhbnNjbHVkZT48L25nLXRyYW5zY2x1ZGU+PC9zcGFuPlwiLFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtZW50LCBhdHRycykge1xuICAgICAgcmV0dXJuIHNjb3BlLmdldExhYmVsQ2xhc3MgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuICdsYWJlbCBsYWJlbC0nICsgSm9ic1NlcnZpY2UudHJhbnNsYXRlTGFiZWxTdGF0ZShhdHRycy5zdGF0dXMpO1xuICAgICAgfTtcbiAgICB9XG4gIH07XG59KS5kaXJlY3RpdmUoJ2luZGljYXRvclByaW1hcnknLCBmdW5jdGlvbihKb2JzU2VydmljZSkge1xuICByZXR1cm4ge1xuICAgIHJlcGxhY2U6IHRydWUsXG4gICAgc2NvcGU6IHtcbiAgICAgIGdldExhYmVsQ2xhc3M6IFwiJlwiLFxuICAgICAgc3RhdHVzOiAnQCdcbiAgICB9LFxuICAgIHRlbXBsYXRlOiBcIjxpIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJyAvPlwiLFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtZ
W50LCBhdHRycykge1xuICAgICAgcmV0dXJuIHNjb3BlLmdldExhYmVsQ2xhc3MgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuICdmYSBmYS1jaXJjbGUgaW5kaWNhdG9yIGluZGljYXRvci0nICsgSm9ic1NlcnZpY2UudHJhbnNsYXRlTGFiZWxTdGF0ZShhdHRycy5zdGF0dXMpO1xuICAgICAgfTtcbiAgICB9XG4gIH07XG59KS5kaXJlY3RpdmUoJ3RhYmxlUHJvcGVydHknLCBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHtcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICB2YWx1ZTogJz0nXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8dGQgdGl0bGU9XFxcInt7dmFsdWUgfHwgJ05vbmUnfX1cXFwiPnt7dmFsdWUgfHwgJ05vbmUnfX08L3RkPlwiXG4gIH07XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcH
QgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4uZmlsdGVyIFwiYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkXCIsIChhbmd1bGFyTW9tZW50Q29uZmlnKSAtPlxuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIgPSAodmFsdWUsIGZvcm1hdCwgZHVyYXRpb25Gb3JtYXQpIC0+XG4gICAgcmV0dXJuIFwiXCIgIGlmIHR5cGVvZiB2YWx1ZSBpcyBcInVuZGVmaW5lZFwiIG9yIHZhbHVlIGlzIG51bGxcblxuICAgIG1vbWVudC5kdXJhdGlvbih2YWx1ZSwgZm9ybWF0KS5mb3JtYXQoZHVyYXRpb25
Gb3JtYXQsIHsgdHJpbTogZmFsc2UgfSlcblxuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIuJHN0YXRlZnVsID0gYW5ndWxhck1vbWVudENvbmZpZy5zdGF0ZWZ1bEZpbHRlcnNcblxuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXJcblxuLmZpbHRlciBcImh1bWFuaXplVGFza05hbWVcIiwgLT5cbiAgKHRleHQpIC0+XG4gICAgIyBUT0RPOiBleHRlbmQuLi4gYSBsb3RcbiAgICBpZiB0ZXh0IHRoZW4gdGV4dC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIikucmVwbGFjZShcIjxici8+XCIsXCJcIikgZWxzZSAnJ1xuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZmlsdGVyKFwiYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkXCIsIGZ1bmN0aW9uKGFuZ3VsYXJNb21lbnRDb25maWcpIHtcbiAgdmFyIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlcjtcbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gZnVuY3Rpb24odmFsdWUsIGZvcm1hdCwgZHVyYXRpb25Gb3JtYXQpIHtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcInVuZGVmaW5lZFwiIHx8IHZhbHVlID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gXCJcIjtcbiAgICB9XG4gICAgcmV0dXJuIG1vbWVudC5kdXJhdGlvbih2YWx1ZSwgZm9ybWF0KS5mb3JtYXQoZHVyYXRpb25Gb3JtYXQsIHtcbiAgICAgIHRyaW06IGZhbHNlXG4gICAgfSk7XG4gIH07XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlci4kc3Rh
dGVmdWwgPSBhbmd1bGFyTW9tZW50Q29uZmlnLnN0YXRlZnVsRmlsdGVycztcbiAgcmV0dXJuIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlcjtcbn0pLmZpbHRlcihcImh1bWFuaXplVGFza05hbWVcIiwgZnVuY3Rpb24oKSB7XG4gIHJldHVybiBmdW5jdGlvbih0ZXh0KSB7XG4gICAgaWYgKHRleHQpIHtcbiAgICAgIHJldHVybiB0ZXh0LnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKS5yZXBsYWNlKFwiPGJyLz5cIiwgXCJcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiAnJztcbiAgICB9XG4gIH07XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3L
mFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4uc2VydmljZSAnTWFpblNlcnZpY2UnLCAoJGh0dHAsIGZsaW5rQ29uZmlnLCAkcSkgLT5cbiAgam9iT2JzZXJ2ZXJzID0gW11cblxuICBAbG9hZENvbmZpZyA9IC0+XG4gICAgZGVmZXJyZWQgPSAkcS5kZWZlcigpXG5cbiAgICAkaHR0cC5nZXQgZmxpbmtDb25maWcuam9iU2VydmVyICsgXCIvY29uZmlnXCJcbiAgICAuc3VjY2VzcyAoZGF0YSwgc3RhdHVzLCBoZWFkZXJzLCBjb25maWcpIC0+XG4gICAgICBkZWZlcnJlZC5yZXNvbHZlKGRhdGEpXG5cbiAgICBkZWZlcnJlZC5wcm9taXNlXG5cblxuICBAXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5zZXJ2aWNlKCdNYWluU2VydmljZScsIGZ1bmN0aW9uKCRodH
RwLCBmbGlua0NvbmZpZywgJHEpIHtcbiAgdmFyIGpvYk9ic2VydmVycztcbiAgam9iT2JzZXJ2ZXJzID0gW107XG4gIHRoaXMubG9hZENvbmZpZyA9IGZ1bmN0aW9uKCkge1xuICAgIHZhciBkZWZlcnJlZDtcbiAgICBkZWZlcnJlZCA9ICRxLmRlZmVyKCk7XG4gICAgJGh0dHAuZ2V0KGZsaW5rQ29uZmlnLmpvYlNlcnZlciArIFwiL2NvbmZpZ1wiKS5zdWNjZXNzKGZ1bmN0aW9uKGRhdGEsIHN0YXR1cywgaGVhZGVycywgY29uZmlnKSB7XG4gICAgICByZXR1cm4gZGVmZXJyZWQucmVzb2x2ZShkYXRhKTtcbiAgICB9KTtcbiAgICByZXR1cm4gZGVmZXJyZWQucHJvbWlzZTtcbiAgfTtcbiAgcmV0dXJuIHRoaXM7XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1
heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4uY29udHJvbGxlciAnUnVubmluZ0pvYnNDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSAtPlxuICAgICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygncnVubmluZycpXG5cbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVy
KVxuXG4gICRzY29wZS5qb2JPYnNlcnZlcigpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSAtPlxuICAgICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygnZmluaXNoZWQnKVxuXG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuICAkc2NvcGUuJG9uICckZGVzdHJveScsIC0+XG4gICAgSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcblxuICAkc2NvcGUuam9iT2JzZXJ2ZXIoKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdTaW5nbGVKb2JDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlLCAkcm9vdFNjb3BlKSAtPlxuICAkc2NvcGUuam9iaWQgPSAkc3RhdGVQYXJhbXMuam9iaWRcbiAgJHJvb3RTY29wZS5qb2IgPSBudWxsXG5cbiAgSm9ic1NlcnZpY2UubG9hZEpvYigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHJvb3RTY29wZS5qb2IgPSBkYXRhXG5cbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgICRyb290U2NvcGUuam9iID0gb
nVsbFxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQbGFuQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgSm9ic1NlcnZpY2UubG9hZFBsYW4oJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS5wbGFuID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQbGFuTm9kZUNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5ub2RlaWQgPSAkc3RhdGVQYXJhbXMubm9kZWlkXG4gICRzY29wZS5zdGF0ZUxpc3QgPSBKb2JzU2VydmljZS5zdGF0ZUxpc3QoKVxuXG4gIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUubm9kZSA9IGRhdGFcblxuICBKb2JzU2VydmljZS5nZXRWZXJ0ZXgoJHNjb3BlLm5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUudmVydGV4ID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmdldF
ZlcnRleCgkc3RhdGVQYXJhbXMudmVydGV4SWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLnZlcnRleCA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iRXhjZXB0aW9uc0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmxvYWRFeGNlcHRpb25zKCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUuZXhjZXB0aW9ucyA9IGRhdGFcblxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuY29udHJvbGxlcignUnVubmluZ0pvYnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygncnVubmluZycpO1xuICB9O1xuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS5qb2JPYnNlcnZlcigpO1xufSkuY29udHJvbGxlcignQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInLCBmdW5jdGl
vbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdmaW5pc2hlZCcpO1xuICB9O1xuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS5qb2JPYnNlcnZlcigpO1xufSkuY29udHJvbGxlcignU2luZ2xlSm9iQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlLCAkcm9vdFNjb3BlKSB7XG4gICRzY29wZS5qb2JpZCA9ICRzdGF0ZVBhcmFtcy5qb2JpZDtcbiAgJHJvb3RTY29wZS5qb2IgPSBudWxsO1xuICBKb2JzU2VydmljZS5sb2FkSm9iKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRyb290U2NvcGUuam9iID0gZGF0YTtcbiAgfSk7XG4gIHJldHVybiAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkcm9vdFNjb3BlLmpvYiA9IG51bGw7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUGxhbkNvbnRyb2xsZXInLCBmdW5j
dGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UubG9hZFBsYW4oJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlLnBsYW4gPSBkYXRhO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5ub2RlaWQgPSAkc3RhdGVQYXJhbXMubm9kZWlkO1xuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KCk7XG4gIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5ub2RlID0gZGF0YTtcbiAgfSk7XG4gIHJldHVybiBKb2JzU2VydmljZS5nZXRWZXJ0ZXgoJHNjb3BlLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS52ZXJ0ZXggPSBkYXRhO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiBKb2JzU2VydmljZS5nZXRWZXJ0ZXgoJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlL
nZlcnRleCA9IGRhdGE7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iRXhjZXB0aW9uc0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UubG9hZEV4Y2VwdGlvbnMoKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlLmV4Y2VwdGlvbnMgPSBkYXRhO1xuICB9KTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZS
BsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd2ZXJ0ZXgnLCAoJHN0YXRlKSAtPlxuICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZSBzZWNvbmRhcnknIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwiXG5cbiAgc2NvcGU6XG4gICAgZGF0YTogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpXG5cbiAgICBhbmFseXplVGltZSA9IChkYXRhKSAtPlxuICAgICAgdGVzdERhdGEgPSBbXVxuXG4gICAgICBhbmd1bGFyLmZvckVhY2g
gZGF0YS5zdWJ0YXNrcywgKHN1YnRhc2ssIGkpIC0+XG4gICAgICAgIHRlc3REYXRhLnB1c2gge1xuICAgICAgICAgIGxhYmVsOiBcIiN7c3VidGFzay5ob3N0fSAoI3tzdWJ0YXNrLnN1YnRhc2t9KVwiXG4gICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiIzY2NlwiXG4gICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJTQ0hFRFVMRURcIl1cbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkRFUExPWUlOR1wiXVxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6IFwiRGVwbG95aW5nXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2FhYVwiXG4gICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJERVBMT1lJTkdcIl1cbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlJVTk5JTkdcIl1cbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxh
YmVsOiBcIlJ1bm5pbmdcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlJVTk5JTkdcIl1cbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkZJTklTSEVEXCJdXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgIF1cbiAgICAgICAgfVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKVxuICAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDFcbiAgICAgICAgdGlja1NpemU6IDFcbiAgICAgIH0pXG4gICAgICAucHJlZml4KFwic2luZ2xlXCIpXG4gICAgICAubGFiZWxGb3JtYXQoKGxhYmVsKSAtPlxuICAgICAgICBsYWJlbFxuICAgICAgKVxuICAgICAgLm1hcmdpbih7IGxlZnQ6IDEwMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgzMClcbiAgICAgIC5yZWxhdGl2ZVRpbWUoKVxuXG4gICAgICBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpXG4gICAgICAuZGF0dW0odGVzdERhdGEpXG4gICAgICAuY2FsbChjaGFydClcblxuICAgIGFuYWx5emVUaW1lKHNjb3BlLmRhdGEpXG5cbiAgICByZXR1cm5cblxuI
yAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3RpbWVsaW5lJywgKCRzdGF0ZSkgLT5cbiAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUnIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwiXG5cbiAgc2NvcGU6XG4gICAgam9iOiBcIj1cIlxuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF1cblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVylcblxuICAgIHRyYW5zbGF0ZUxhYmVsID0gKGxhYmVsKSAtPlxuICAgICAgbGFiZWwucmVwbGFjZShcIiZndDtcIiwgXCI+XCIpXG5cbiAgICBhbmFseXplVGltZSA9IChkYXRhKSAtPlxuICAgICAgdGVzdERhdGEgPSBbXVxuXG4gICAgICB0ZXN0RGF0YS5wdXNoIFxuICAgICAgICB0aW1lczogW1xuICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiXG4gICAgICAgICAgY29sb3I6IFwiI2NjY2NjY1wiXG4gICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiXG4gICAgICAgICAgc3RhcnRpbmdfdGltZTogZGF0YS50aW1lc3RhbXBzW1wiQ1JFQVRFRFwiXVxuICAgICAgICAgIGVuZGluZ190aW1lOiBkYXRhLnRpbWVzdGFtcHNbXCJDUkVBVEVEXCJdICsgMVxuICAgICAgICAgIHR5cGU6ICdzY2hlZHVsZWQnXG4gICAgICAgIF
1cblxuXG4gICAgICBhbmd1bGFyLmZvckVhY2ggZGF0YS52ZXJ0aWNlcywgKHZlcnRleCkgLT5cbiAgICAgICAgaWYgdmVydGV4WydzdGFydC10aW1lJ10gPiAtMVxuICAgICAgICAgIHRlc3REYXRhLnB1c2ggXG4gICAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgICBsYWJlbDogdHJhbnNsYXRlTGFiZWwodmVydGV4Lm5hbWUpXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiNkOWYxZjdcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNjJjZGVhXCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdmVydGV4WydzdGFydC10aW1lJ11cbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZlcnRleFsnZW5kLXRpbWUnXVxuICAgICAgICAgICAgICBsaW5rOiB2ZXJ0ZXguaWRcbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICBdXG5cbiAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLmNsaWNrKChkLCBpLCBkYXR1bSkgLT5cbiAgICAgICAgaWYgZC5saW5rXG4gICAgICAgICAgJHN0YXRlLmdvIFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIiwgeyBqb2JpZDogZGF0YS5qaWQsIHZlcnRleElkOiBkLmxpbmsgfVxuXG4gICAgICApXG4gICAgICAudGlja0Zvcm1hdCh7XG4gICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKVxuICAgICAgICAjIHRpY2tUaW1lOiBkMy50aW1lLnNlY29uZFxuICAgICAgICAjIHRpY2tJbnRlcnZhbDogMC4
1XG4gICAgICAgIHRpY2tTaXplOiAxXG4gICAgICB9KVxuICAgICAgLnByZWZpeChcIm1haW5cIilcbiAgICAgIC5tYXJnaW4oeyBsZWZ0OiAwLCByaWdodDogMCwgdG9wOiAwLCBib3R0b206IDAgfSlcbiAgICAgIC5pdGVtSGVpZ2h0KDMwKVxuICAgICAgLnNob3dCb3JkZXJMaW5lKClcbiAgICAgIC5zaG93SG91clRpbWVsaW5lKClcblxuICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKVxuICAgICAgLmRhdHVtKHRlc3REYXRhKVxuICAgICAgLmNhbGwoY2hhcnQpXG5cbiAgICBzY29wZS4kd2F0Y2ggYXR0cnMuam9iLCAoZGF0YSkgLT5cbiAgICAgIGFuYWx5emVUaW1lKGRhdGEpIGlmIGRhdGFcblxuICAgIHJldHVyblxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnam9iUGxhbicsICgkdGltZW91dCkgLT5cbiAgdGVtcGxhdGU6IFwiXG4gICAgPHN2ZyBjbGFzcz0nZ3JhcGgnIHdpZHRoPSc1MDAnIGhlaWdodD0nNDAwJz48ZyAvPjwvc3ZnPlxuICAgIDxzdmcgY2xhc3M9J3RtcCcgd2lkdGg9JzEnIGhlaWdodD0nMSc+PGcgLz48L3N2Zz5cbiAgICA8ZGl2IGNsYXNzPSdidG4tZ3JvdXAgem9vbS1idXR0b25zJz5cbiAgICAgIDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1pbicgbmctY2xpY2s9J3pvb21JbigpJz48aSBjbGFzcz0nZmEgZmEtcGx1cycgLz48L2E+XG4gICAgICA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20t
b3V0JyBuZy1jbGljaz0nem9vbU91dCgpJz48aSBjbGFzcz0nZmEgZmEtbWludXMnIC8+PC9hPlxuICAgIDwvZGl2PlwiXG5cbiAgc2NvcGU6XG4gICAgcGxhbjogJz0nXG5cbiAgbGluazogKHNjb3BlLCBlbGVtLCBhdHRycykgLT5cbiAgICBtYWluWm9vbSA9IGQzLmJlaGF2aW9yLnpvb20oKVxuICAgIHN1YmdyYXBocyA9IFtdXG4gICAgam9iaWQgPSBhdHRycy5qb2JpZFxuXG4gICAgbWFpblN2Z0VsZW1lbnQgPSBlbGVtLmNoaWxkcmVuKClbMF1cbiAgICBtYWluRyA9IGVsZW0uY2hpbGRyZW4oKS5jaGlsZHJlbigpWzBdXG4gICAgbWFpblRtcEVsZW1lbnQgPSBlbGVtLmNoaWxkcmVuKClbMV1cblxuICAgIGQzbWFpblN2ZyA9IGQzLnNlbGVjdChtYWluU3ZnRWxlbWVudClcbiAgICBkM21haW5TdmdHID0gZDMuc2VsZWN0KG1haW5HKVxuICAgIGQzdG1wU3ZnID0gZDMuc2VsZWN0KG1haW5UbXBFbGVtZW50KVxuXG4gICAgIyBhbmd1bGFyLmVsZW1lbnQobWFpbkcpLmVtcHR5KClcblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoZWxlbS5jaGlsZHJlbigpWzBdKS53aWR0aChjb250YWluZXJXKVxuXG4gICAgc2NvcGUuem9vbUluID0gLT5cbiAgICAgIGlmIG1haW5ab29tLnNjYWxlKCkgPCAyLjk5XG4gICAgICAgIFxuICAgICAgICAjIENhbGN1bGF0ZSBhbmQgc3RvcmUgbmV3IHZhbHVlcyBpbiB6b29tIG9iamVjdFxuICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vb
S50cmFuc2xhdGUoKVxuICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICBtYWluWm9vbS5zY2FsZSBtYWluWm9vbS5zY2FsZSgpICsgMC4xXG4gICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZSBbIHYxLCB2MiBdXG4gICAgICAgIFxuICAgICAgICAjIFRyYW5zZm9ybSBzdmdcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiXG5cbiAgICBzY29wZS56b29tT3V0ID0gLT5cbiAgICAgIGlmIG1haW5ab29tLnNjYWxlKCkgPiAwLjMxXG4gICAgICAgIFxuICAgICAgICAjIENhbGN1bGF0ZSBhbmQgc3RvcmUgbmV3IHZhbHVlcyBpbiBtYWluWm9vbSBvYmplY3RcbiAgICAgICAgbWFpblpvb20uc2NhbGUgbWFpblpvb20uc2NhbGUoKSAtIDAuMVxuICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKVxuICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC
8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUgWyB2MSwgdjIgXVxuICAgICAgICBcbiAgICAgICAgIyBUcmFuc2Zvcm0gc3ZnXG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIlxuXG4gICAgI2NyZWF0ZSBhIGxhYmVsIG9mIGFuIGVkZ2VcbiAgICBjcmVhdGVMYWJlbEVkZ2UgPSAoZWwpIC0+XG4gICAgICBsYWJlbFZhbHVlID0gXCJcIlxuICAgICAgaWYgZWwuc2hpcF9zdHJhdGVneT8gb3IgZWwubG9jYWxfc3RyYXRlZ3k/XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8ZGl2IGNsYXNzPSdlZGdlLWxhYmVsJz5cIlxuICAgICAgICBsYWJlbFZhbHVlICs9IGVsLnNoaXBfc3RyYXRlZ3kgIGlmIGVsLnNoaXBfc3RyYXRlZ3k/XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCIgKFwiICsgZWwudGVtcF9tb2RlICsgXCIpXCIgIHVubGVzcyBlbC50ZW1wX21vZGUgaXMgYHVuZGVmaW5lZGBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiw8YnI+XCIgKyBlbC5sb2NhbF9zdHJhdGVneSAgdW5sZXNzIGVsLmxvY2FsX3N0cmF0ZWd5IGlzIGB1bmRlZmluZWRgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2Rpdj5cIlxuICAgICAgbGFiZWxWYWx1ZVxuXG5cbiAgICAjIHRydWUsIGlmIHRoZSBub2RlIGlzIGEgc3BlY2lhbCBub2RlIGZ
yb20gYW4gaXRlcmF0aW9uXG4gICAgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZSA9IChpbmZvKSAtPlxuICAgICAgKGluZm8gaXMgXCJwYXJ0aWFsU29sdXRpb25cIiBvciBpbmZvIGlzIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiIG9yIGluZm8gaXMgXCJ3b3Jrc2V0XCIgb3IgaW5mbyBpcyBcIm5leHRXb3Jrc2V0XCIgb3IgaW5mbyBpcyBcInNvbHV0aW9uU2V0XCIgb3IgaW5mbyBpcyBcInNvbHV0aW9uRGVsdGFcIilcblxuICAgIGdldE5vZGVUeXBlID0gKGVsLCBpbmZvKSAtPlxuICAgICAgaWYgaW5mbyBpcyBcIm1pcnJvclwiXG4gICAgICAgICdub2RlLW1pcnJvcidcblxuICAgICAgZWxzZSBpZiBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pXG4gICAgICAgICdub2RlLWl0ZXJhdGlvbidcblxuICAgICAgZWxzZVxuICAgICAgICAgICdub2RlLW5vcm1hbCdcbiAgICAgIFxuICAgICMgY3JlYXRlcyB0aGUgbGFiZWwgb2YgYSBub2RlLCBpbiBpbmZvIGlzIHN0b3JlZCwgd2hldGhlciBpdCBpcyBhIHNwZWNpYWwgbm9kZSAobGlrZSBhIG1pcnJvciBpbiBhbiBpdGVyYXRpb24pXG4gICAgY3JlYXRlTGFiZWxOb2RlID0gKGVsLCBpbmZvLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgbGFiZWxWYWx1ZSA9IFwiPGEgaHJlZj0nIy9qb2JzL1wiICsgam9iaWQgKyBcIi92ZXJ0ZXgvXCIgKyBlbC5pZCArIFwiJyBjbGFzcz0nbm9kZS1sYWJlbCBcIiArIGdldE5vZGVUeXBlKGVsLCBpbmZvKSArIFwiJz5cIlxuXG4gICAgICAj
IE5vZGVuYW1lXG4gICAgICBpZiBpbmZvIGlzIFwibWlycm9yXCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5NaXJyb3Igb2YgXCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIlxuICAgICAgZWxzZVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPlwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCJcbiAgICAgIGlmIGVsLmRlc2NyaXB0aW9uIGlzIFwiXCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIlwiXG4gICAgICBlbHNlXG4gICAgICAgIHN0ZXBOYW1lID0gZWwuZGVzY3JpcHRpb25cbiAgICAgICAgXG4gICAgICAgICMgY2xlYW4gc3RlcE5hbWVcbiAgICAgICAgc3RlcE5hbWUgPSBzaG9ydGVuU3RyaW5nKHN0ZXBOYW1lKVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg0IGNsYXNzPSdzdGVwLW5hbWUnPlwiICsgc3RlcE5hbWUgKyBcIjwvaDQ+XCJcbiAgICAgIFxuICAgICAgIyBJZiB0aGlzIG5vZGUgaXMgYW4gXCJpdGVyYXRpb25cIiB3ZSBuZWVkIGEgZGlmZmVyZW50IHBhbmVsLWJvZHlcbiAgICAgIGlmIGVsLnN0ZXBfZnVuY3Rpb24/XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uKGVsLmlkLCBtYXhXLCBtYXhIKVxuICAgICAgZWxzZVxuICAgICAgICBcbiAgICAgICAgIyBPdGhlcndpc2UgYWRkIGluZm9zICAgIFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlwiICsgaW5mb
yArIFwiIE5vZGU8L2g1PlwiICBpZiBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+UGFyYWxsZWxpc206IFwiICsgZWwucGFyYWxsZWxpc20gKyBcIjwvaDU+XCIgIHVubGVzcyBlbC5wYXJhbGxlbGlzbSBpcyBcIlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+T3BlcmF0aW9uOiBcIiArIHNob3J0ZW5TdHJpbmcoZWwub3BlcmF0b3Jfc3RyYXRlZ3kpICsgXCI8L2g1PlwiICB1bmxlc3MgZWwub3BlcmF0b3IgaXMgYHVuZGVmaW5lZGBcbiAgICAgIFxuICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvYT5cIlxuICAgICAgbGFiZWxWYWx1ZVxuXG4gICAgIyBFeHRlbmRzIHRoZSBsYWJlbCBvZiBhIG5vZGUgd2l0aCBhbiBhZGRpdGlvbmFsIHN2ZyBFbGVtZW50IHRvIHByZXNlbnQgdGhlIGl0ZXJhdGlvbi5cbiAgICBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24gPSAoaWQsIG1heFcsIG1heEgpIC0+XG4gICAgICBzdmdJRCA9IFwic3ZnLVwiICsgaWRcblxuICAgICAgbGFiZWxWYWx1ZSA9IFwiPHN2ZyBjbGFzcz0nXCIgKyBzdmdJRCArIFwiJyB3aWR0aD1cIiArIG1heFcgKyBcIiBoZWlnaHQ9XCIgKyBtYXhIICsgXCI+PGcgLz48L3N2Zz5cIlxuICAgICAgbGFiZWxWYWx1ZVxuXG4gICAgIyBTcGxpdCBhIHN0cmluZyBpbnRvIG11bHRpcGxlIGxpbmVzIHNvIHRoYXQgZWFjaCBsaW5lIGhhcyBsZXNzIHRoYW4gMzAgbGV0dGVycy5cbiAgICBzaG9ydGVuU3
RyaW5nID0gKHMpIC0+XG4gICAgICAjIG1ha2Ugc3VyZSB0aGF0IG5hbWUgZG9lcyBub3QgY29udGFpbiBhIDwgKGJlY2F1c2Ugb2YgaHRtbClcbiAgICAgIGlmIHMuY2hhckF0KDApIGlzIFwiPFwiXG4gICAgICAgIHMgPSBzLnJlcGxhY2UoXCI8XCIsIFwiJmx0O1wiKVxuICAgICAgICBzID0gcy5yZXBsYWNlKFwiPlwiLCBcIiZndDtcIilcbiAgICAgIHNiciA9IFwiXCJcbiAgICAgIHdoaWxlIHMubGVuZ3RoID4gMzBcbiAgICAgICAgc2JyID0gc2JyICsgcy5zdWJzdHJpbmcoMCwgMzApICsgXCI8YnI+XCJcbiAgICAgICAgcyA9IHMuc3Vic3RyaW5nKDMwLCBzLmxlbmd0aClcbiAgICAgIHNiciA9IHNiciArIHNcbiAgICAgIHNiclxuXG4gICAgY3JlYXRlTm9kZSA9IChnLCBkYXRhLCBlbCwgaXNQYXJlbnQgPSBmYWxzZSwgbWF4VywgbWF4SCkgLT5cbiAgICAgICMgY3JlYXRlIG5vZGUsIHNlbmQgYWRkaXRpb25hbCBpbmZvcm1hdGlvbnMgYWJvdXQgdGhlIG5vZGUgaWYgaXQgaXMgYSBzcGVjaWFsIG9uZVxuICAgICAgaWYgZWwuaWQgaXMgZGF0YS5wYXJ0aWFsX3NvbHV0aW9uXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiKVxuXG4gICAgICB
lbHNlIGlmIGVsLmlkIGlzIGRhdGEubmV4dF9wYXJ0aWFsX3NvbHV0aW9uXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS53b3Jrc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIndvcmtzZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJ3b3Jrc2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5uZXh0X3dvcmtzZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFdvcmtzZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0V29ya3NldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEuc29sdXRpb25fc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRl
TGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uU2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25TZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLnNvbHV0aW9uX2RlbHRhXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uRGVsdGFcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIpXG5cbiAgICAgIGVsc2VcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwiXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwiXCIpXG5cbiAgICBjcmVhdGVFZGdlID0gKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKSAtPlxuICAgICAgdW5sZXNzIGV4aXN0aW5nTm9kZXMuaW5kZXhPZihwcmVkLmlkKSBpcyAtMVxuICAgICAgICBnLnNldEVkZ2UgcHJlZC5pZCwgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShwcmVkKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgYXJyb3doZWFkOiAnb
m9ybWFsJ1xuXG4gICAgICBlbHNlXG4gICAgICAgIG1pc3NpbmdOb2RlID0gc2VhcmNoRm9yTm9kZShkYXRhLCBwcmVkLmlkKVxuICAgICAgICB1bmxlc3MgIW1pc3NpbmdOb2RlIG9yIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCBpcyB0cnVlXG4gICAgICAgICAgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkID0gdHJ1ZVxuICAgICAgICAgIGcuc2V0Tm9kZSBtaXNzaW5nTm9kZS5pZCxcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUobWlzc2luZ05vZGUsIFwibWlycm9yXCIpXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKG1pc3NpbmdOb2RlLCAnbWlycm9yJylcblxuICAgICAgICAgIGcuc2V0RWRnZSBtaXNzaW5nTm9kZS5pZCwgZWwuaWQsXG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKG1pc3NpbmdOb2RlKVxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcblxuICAgIGxvYWRKc29uVG9EYWdyZSA9IChnLCBkYXRhKSAtPlxuICAgICAgZXhpc3RpbmdOb2RlcyA9IFtdXG5cbiAgICAgIGlmIGRhdGEubm9kZXM/XG4gICAgICAgICMgVGhpcyBpcyB0aGUgbm9ybWFsIGpzb24gZGF0YVxuICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLm5vZGVzXG5cbiAgICAgIGVsc2VcbiAgICAgICAgIyBUaGlzIGlzIGFuIGl0ZXJhdGlvbiwgd2Ugbm93IHN0b3JlIHNwZWNpYWwgaXRlcmF0aW9uIG5vZGVzIGlmIHBvc3NpYmxlXG
4gICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEuc3RlcF9mdW5jdGlvblxuICAgICAgICBpc1BhcmVudCA9IHRydWVcblxuICAgICAgZm9yIGVsIGluIHRvSXRlcmF0ZVxuICAgICAgICBtYXhXID0gMFxuICAgICAgICBtYXhIID0gMFxuXG4gICAgICAgIGlmIGVsLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgICBzZyA9IG5ldyBkYWdyZUQzLmdyYXBobGliLkdyYXBoKHsgbXVsdGlncmFwaDogdHJ1ZSwgY29tcG91bmQ6IHRydWUgfSkuc2V0R3JhcGgoe1xuICAgICAgICAgICAgbm9kZXNlcDogMjBcbiAgICAgICAgICAgIGVkZ2VzZXA6IDBcbiAgICAgICAgICAgIHJhbmtzZXA6IDIwXG4gICAgICAgICAgICByYW5rZGlyOiBcIkxSXCJcbiAgICAgICAgICAgIG1hcmdpbng6IDEwXG4gICAgICAgICAgICBtYXJnaW55OiAxMFxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgIHN1YmdyYXBoc1tlbC5pZF0gPSBzZ1xuXG4gICAgICAgICAgbG9hZEpzb25Ub0RhZ3JlKHNnLCBlbClcblxuICAgICAgICAgIHIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKVxuICAgICAgICAgIGQzdG1wU3ZnLnNlbGVjdCgnZycpLmNhbGwociwgc2cpXG4gICAgICAgICAgbWF4VyA9IHNnLmdyYXBoKCkud2lkdGhcbiAgICAgICAgICBtYXhIID0gc2cuZ3JhcGgoKS5oZWlnaHRcblxuICAgICAgICAgIGFuZ3VsYXIuZWxlbWVudChtYWluVG1wRWxlbWVudCkuZW1wdHkoKVxuXG4gICAgICAgIGNyZWF0ZU5vZGUoZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXh
XLCBtYXhIKVxuXG4gICAgICAgIGV4aXN0aW5nTm9kZXMucHVzaCBlbC5pZFxuICAgICAgICBcbiAgICAgICAgIyBjcmVhdGUgZWRnZXMgZnJvbSBpbnB1dHMgdG8gY3VycmVudCBub2RlXG4gICAgICAgIGlmIGVsLmlucHV0cz9cbiAgICAgICAgICBmb3IgcHJlZCBpbiBlbC5pbnB1dHNcbiAgICAgICAgICAgIGNyZWF0ZUVkZ2UoZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpXG5cbiAgICAgIGdcblxuICAgICMgc2VhcmNoZXMgaW4gdGhlIGdsb2JhbCBKU09ORGF0YSBmb3IgdGhlIG5vZGUgd2l0aCB0aGUgZ2l2ZW4gaWRcbiAgICBzZWFyY2hGb3JOb2RlID0gKGRhdGEsIG5vZGVJRCkgLT5cbiAgICAgIGZvciBpIG9mIGRhdGEubm9kZXNcbiAgICAgICAgZWwgPSBkYXRhLm5vZGVzW2ldXG4gICAgICAgIHJldHVybiBlbCAgaWYgZWwuaWQgaXMgbm9kZUlEXG4gICAgICAgIFxuICAgICAgICAjIGxvb2sgZm9yIG5vZGVzIHRoYXQgYXJlIGluIGl0ZXJhdGlvbnNcbiAgICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvbj9cbiAgICAgICAgICBmb3IgaiBvZiBlbC5zdGVwX2Z1bmN0aW9uXG4gICAgICAgICAgICByZXR1cm4gZWwuc3RlcF9mdW5jdGlvbltqXSAgaWYgZWwuc3RlcF9mdW5jdGlvbltqXS5pZCBpcyBub2RlSURcblxuICAgIGRyYXdHcmFwaCA9IChkYXRhKSAtPlxuICAgICAgZyA9IG5ldyBkYWdyZUQzLmdyYXBobGliLkdyYXBoKHsgbXVsdGlncmFwaDogdHJ1ZSwgY29tcG91bmQ6IHRydWUgfSkuc2V0
R3JhcGgoe1xuICAgICAgICBub2Rlc2VwOiA3MFxuICAgICAgICBlZGdlc2VwOiAwXG4gICAgICAgIHJhbmtzZXA6IDUwXG4gICAgICAgIHJhbmtkaXI6IFwiTFJcIlxuICAgICAgICBtYXJnaW54OiA0MFxuICAgICAgICBtYXJnaW55OiA0MFxuICAgICAgICB9KVxuXG4gICAgICBsb2FkSnNvblRvRGFncmUoZywgZGF0YSlcblxuICAgICAgcmVuZGVyZXIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKVxuICAgICAgZDNtYWluU3ZnRy5jYWxsKHJlbmRlcmVyLCBnKVxuXG4gICAgICBmb3IgaSwgc2cgb2Ygc3ViZ3JhcGhzXG4gICAgICAgIGQzbWFpblN2Zy5zZWxlY3QoJ3N2Zy5zdmctJyArIGkgKyAnIGcnKS5jYWxsKHJlbmRlcmVyLCBzZylcblxuICAgICAgbmV3U2NhbGUgPSAwLjVcblxuICAgICAgeENlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkud2lkdGgoKSAtIGcuZ3JhcGgoKS53aWR0aCAqIG5ld1NjYWxlKSAvIDIpXG4gICAgICB5Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS5oZWlnaHQoKSAtIGcuZ3JhcGgoKS5oZWlnaHQgKiBuZXdTY2FsZSkgLyAyKVxuXG4gICAgICBtYWluWm9vbS5zY2FsZShuZXdTY2FsZSkudHJhbnNsYXRlKFt4Q2VudGVyT2Zmc2V0LCB5Q2VudGVyT2Zmc2V0XSlcblxuICAgICAgZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgeENlbnRlc
k9mZnNldCArIFwiLCBcIiArIHlDZW50ZXJPZmZzZXQgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpXG5cbiAgICAgIG1haW5ab29tLm9uKFwiem9vbVwiLCAtPlxuICAgICAgICBldiA9IGQzLmV2ZW50XG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIGV2LnRyYW5zbGF0ZSArIFwiKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIpXCJcbiAgICAgIClcbiAgICAgIG1haW5ab29tKGQzbWFpblN2ZylcblxuICAgIHNjb3BlLiR3YXRjaCBhdHRycy5wbGFuLCAobmV3UGxhbikgLT5cbiAgICAgIGRyYXdHcmFwaChuZXdQbGFuKSBpZiBuZXdQbGFuXG5cbiAgICByZXR1cm5cbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmRpcmVjdGl2ZSgndmVydGV4JywgZnVuY3Rpb24oJHN0YXRlKSB7XG4gIHJldHVybiB7XG4gICAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUgc2Vjb25kYXJ5JyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIixcbiAgICBzY29wZToge1xuICAgICAgZGF0YTogXCI9XCJcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGFuYWx5emVUaW1lLCBjb250YWluZXJXLCBzdmdFbDtcbiAgICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudC
hzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKTtcbiAgICAgIGFuYWx5emVUaW1lID0gZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICB2YXIgY2hhcnQsIHN2ZywgdGVzdERhdGE7XG4gICAgICAgIHRlc3REYXRhID0gW107XG4gICAgICAgIGFuZ3VsYXIuZm9yRWFjaChkYXRhLnN1YnRhc2tzLCBmdW5jdGlvbihzdWJ0YXNrLCBpKSB7XG4gICAgICAgICAgcmV0dXJuIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgICAgbGFiZWw6IHN1YnRhc2suaG9zdCArIFwiIChcIiArIHN1YnRhc2suc3VidGFzayArIFwiKVwiLFxuICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiM2NjZcIixcbiAgICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiU0NIRURVTEVEXCJdLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJERVBMT1lJTkdcIl0sXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH0sIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogXCJEZXBsb3lpbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjYWFhXCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiLFx
uICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkRFUExPWUlOR1wiXSxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiUlVOTklOR1wiXSxcbiAgICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgfSwge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlJ1bm5pbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlJVTk5JTkdcIl0sXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkZJTklTSEVEXCJdLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICBdXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS50aWNrRm9ybWF0KHtcbiAgICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIiksXG4gICAgICAgICAgdGlja1NpemU6IDFcbiAgICAgICAgfSkucHJlZml4KFwic2luZ2xlXCIpLmxhYmVsRm9ybWF0KGZ1bmN0aW9uKGxhYmVsKSB7XG4gICAgICAgICAgcmV0dXJuIGxhYmVsO1xuICAgICAgICB9KS5tYXJnaW4oe1xuICAgICAg
ICAgIGxlZnQ6IDEwMCxcbiAgICAgICAgICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMzApLnJlbGF0aXZlVGltZSgpO1xuICAgICAgICByZXR1cm4gc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCk7XG4gICAgICB9O1xuICAgICAgYW5hbHl6ZVRpbWUoc2NvcGUuZGF0YSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0aW1lbGluZScsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIixcbiAgICBzY29wZToge1xuICAgICAgam9iOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5lclcsIHN2Z0VsLCB0cmFuc2xhdGVMYWJlbDtcbiAgICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKTtcbiAgICAgIHRyYW5zbGF0ZUxhYmVsID0gZnVuY3Rpb24obGFiZWwpIHtcbiAgICAgICAgcmV0dXJuIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKTtcbiAgICAgIH07XG4gICAgICBhbmFse
XplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGNoYXJ0LCBzdmcsIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIixcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2NjY2NjY1wiLFxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEudGltZXN0YW1wc1tcIkNSRUFURURcIl0sXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBkYXRhLnRpbWVzdGFtcHNbXCJDUkVBVEVEXCJdICsgMSxcbiAgICAgICAgICAgICAgdHlwZTogJ3NjaGVkdWxlZCdcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBdXG4gICAgICAgIH0pO1xuICAgICAgICBhbmd1bGFyLmZvckVhY2goZGF0YS52ZXJ0aWNlcywgZnVuY3Rpb24odmVydGV4KSB7XG4gICAgICAgICAgaWYgKHZlcnRleFsnc3RhcnQtdGltZSddID4gLTEpIHtcbiAgICAgICAgICAgIHJldHVybiB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBsYWJlbDogdHJhbnNsYXRlTGFiZWwodmVydGV4Lm5hbWUpLFxuICAgICAgICAgICAgICAgICAgY29sb3I6IFwiI2Q5ZjFmN1wiLFxuICAgICAgICAgICAgICAgICAgYm
9yZGVyQ29sb3I6IFwiIzYyY2RlYVwiLFxuICAgICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdmVydGV4WydzdGFydC10aW1lJ10sXG4gICAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdmVydGV4WydlbmQtdGltZSddLFxuICAgICAgICAgICAgICAgICAgbGluazogdmVydGV4LmlkLFxuICAgICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBdXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5jbGljayhmdW5jdGlvbihkLCBpLCBkYXR1bSkge1xuICAgICAgICAgIGlmIChkLmxpbmspIHtcbiAgICAgICAgICAgIHJldHVybiAkc3RhdGUuZ28oXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7XG4gICAgICAgICAgICAgIGpvYmlkOiBkYXRhLmppZCxcbiAgICAgICAgICAgICAgdmVydGV4SWQ6IGQubGlua1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KS50aWNrRm9ybWF0KHtcbiAgICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIiksXG4gICAgICAgICAgdGlja1NpemU6IDFcbiAgICAgICAgfSkucHJlZml4KFwibWFpblwiKS5tYXJnaW4oe1xuICAgICAgICAgIGxlZnQ6IDAsXG4gICAgICAgICAgcmlnaHQ6IDAsXG4gICAgICAgICAgdG9wOiAwLFxuICAgICAgICAgIGJvdHRvbTogMFxuICA
gICAgICB9KS5pdGVtSGVpZ2h0KDMwKS5zaG93Qm9yZGVyTGluZSgpLnNob3dIb3VyVGltZWxpbmUoKTtcbiAgICAgICAgcmV0dXJuIHN2ZyA9IGQzLnNlbGVjdChzdmdFbCkuZGF0dW0odGVzdERhdGEpLmNhbGwoY2hhcnQpO1xuICAgICAgfTtcbiAgICAgIHNjb3BlLiR3YXRjaChhdHRycy5qb2IsIGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICByZXR1cm4gYW5hbHl6ZVRpbWUoZGF0YSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgfTtcbn0pLmRpcmVjdGl2ZSgnam9iUGxhbicsIGZ1bmN0aW9uKCR0aW1lb3V0KSB7XG4gIHJldHVybiB7XG4gICAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0nZ3JhcGgnIHdpZHRoPSc1MDAnIGhlaWdodD0nNDAwJz48ZyAvPjwvc3ZnPiA8c3ZnIGNsYXNzPSd0bXAnIHdpZHRoPScxJyBoZWlnaHQ9JzEnPjxnIC8+PC9zdmc+IDxkaXYgY2xhc3M9J2J0bi1ncm91cCB6b29tLWJ1dHRvbnMnPiA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20taW4nIG5nLWNsaWNrPSd6b29tSW4oKSc+PGkgY2xhc3M9J2ZhIGZhLXBsdXMnIC8+PC9hPiA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20tb3V0JyBuZy1jbGljaz0nem9vbU91dCgpJz48aSBjbGFzcz0nZmEgZmEtbWludXMnIC8+PC9hPiA8L2Rpdj5cIixcbiAgICBzY29wZToge1xuICAgICAgcGxhbjogJz0nXG4gICAgfSxcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxl
bSwgYXR0cnMpIHtcbiAgICAgIHZhciBjb250YWluZXJXLCBjcmVhdGVFZGdlLCBjcmVhdGVMYWJlbEVkZ2UsIGNyZWF0ZUxhYmVsTm9kZSwgY3JlYXRlTm9kZSwgZDNtYWluU3ZnLCBkM21haW5TdmdHLCBkM3RtcFN2ZywgZHJhd0dyYXBoLCBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24sIGdldE5vZGVUeXBlLCBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlLCBqb2JpZCwgbG9hZEpzb25Ub0RhZ3JlLCBtYWluRywgbWFpblN2Z0VsZW1lbnQsIG1haW5UbXBFbGVtZW50LCBtYWluWm9vbSwgc2VhcmNoRm9yTm9kZSwgc2hvcnRlblN0cmluZywgc3ViZ3JhcGhzO1xuICAgICAgbWFpblpvb20gPSBkMy5iZWhhdmlvci56b29tKCk7XG4gICAgICBzdWJncmFwaHMgPSBbXTtcbiAgICAgIGpvYmlkID0gYXR0cnMuam9iaWQ7XG4gICAgICBtYWluU3ZnRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVswXTtcbiAgICAgIG1haW5HID0gZWxlbS5jaGlsZHJlbigpLmNoaWxkcmVuKClbMF07XG4gICAgICBtYWluVG1wRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVsxXTtcbiAgICAgIGQzbWFpblN2ZyA9IGQzLnNlbGVjdChtYWluU3ZnRWxlbWVudCk7XG4gICAgICBkM21haW5TdmdHID0gZDMuc2VsZWN0KG1haW5HKTtcbiAgICAgIGQzdG1wU3ZnID0gZDMuc2VsZWN0KG1haW5UbXBFbGVtZW50KTtcbiAgICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKCk7XG4gICAgICBhbmd1bGFyLmVsZW1lbnQoZWxlbS5jaGlsZHJlbigpWzBdKS53a
WR0aChjb250YWluZXJXKTtcbiAgICAgIHNjb3BlLnpvb21JbiA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgdHJhbnNsYXRlLCB2MSwgdjI7XG4gICAgICAgIGlmIChtYWluWm9vbS5zY2FsZSgpIDwgMi45OSkge1xuICAgICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpO1xuICAgICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpO1xuICAgICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpO1xuICAgICAgICAgIG1haW5ab29tLnNjYWxlKG1haW5ab29tLnNjYWxlKCkgKyAwLjEpO1xuICAgICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZShbdjEsIHYyXSk7XG4gICAgICAgICAgcmV0dXJuIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIik7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBzY29wZS56b29tT3V0ID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB0cmFuc2xhdGUsIHYxLCB2MjtcbiAgICAgICAgaWYgKG1haW5ab29tLnNjYWxlKCkgPiAwLjMxKSB7XG4gICAgICAgICAgbWFpblpvb20uc2NhbGUobWFpblpvb20uc2NhbGUoKSAtIDAuMSk7XG4gICAgICAgICAgdHJhbnNsYXRlID0gbW
Fpblpvb20udHJhbnNsYXRlKCk7XG4gICAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgbWFpblpvb20udHJhbnNsYXRlKFt2MSwgdjJdKTtcbiAgICAgICAgICByZXR1cm4gZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNyZWF0ZUxhYmVsRWRnZSA9IGZ1bmN0aW9uKGVsKSB7XG4gICAgICAgIHZhciBsYWJlbFZhbHVlO1xuICAgICAgICBsYWJlbFZhbHVlID0gXCJcIjtcbiAgICAgICAgaWYgKChlbC5zaGlwX3N0cmF0ZWd5ICE9IG51bGwpIHx8IChlbC5sb2NhbF9zdHJhdGVneSAhPSBudWxsKSkge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8ZGl2IGNsYXNzPSdlZGdlLWxhYmVsJz5cIjtcbiAgICAgICAgICBpZiAoZWwuc2hpcF9zdHJhdGVneSAhPSBudWxsKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IGVsLnNoaXBfc3RyYXRlZ3k7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC50ZW1wX21vZGUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGF
iZWxWYWx1ZSArPSBcIiAoXCIgKyBlbC50ZW1wX21vZGUgKyBcIilcIjtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLmxvY2FsX3N0cmF0ZWd5ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCIsPGJyPlwiICsgZWwubG9jYWxfc3RyYXRlZ3k7XG4gICAgICAgICAgfVxuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2Rpdj5cIjtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbGFiZWxWYWx1ZTtcbiAgICAgIH07XG4gICAgICBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlID0gZnVuY3Rpb24oaW5mbykge1xuICAgICAgICByZXR1cm4gaW5mbyA9PT0gXCJwYXJ0aWFsU29sdXRpb25cIiB8fCBpbmZvID09PSBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiB8fCBpbmZvID09PSBcIndvcmtzZXRcIiB8fCBpbmZvID09PSBcIm5leHRXb3Jrc2V0XCIgfHwgaW5mbyA9PT0gXCJzb2x1dGlvblNldFwiIHx8IGluZm8gPT09IFwic29sdXRpb25EZWx0YVwiO1xuICAgICAgfTtcbiAgICAgIGdldE5vZGVUeXBlID0gZnVuY3Rpb24oZWwsIGluZm8pIHtcbiAgICAgICAgaWYgKGluZm8gPT09IFwibWlycm9yXCIpIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtbWlycm9yJztcbiAgICAgICAgfSBlbHNlIGlmIChpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pKSB7XG4gICAgICAgICAgcmV0dXJuICdub2RlLWl0ZXJhdGlvbic7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJu
ICdub2RlLW5vcm1hbCc7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjcmVhdGVMYWJlbE5vZGUgPSBmdW5jdGlvbihlbCwgaW5mbywgbWF4VywgbWF4SCkge1xuICAgICAgICB2YXIgbGFiZWxWYWx1ZSwgc3RlcE5hbWU7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIjxhIGhyZWY9JyMvam9icy9cIiArIGpvYmlkICsgXCIvdmVydGV4L1wiICsgZWwuaWQgKyBcIicgY2xhc3M9J25vZGUtbGFiZWwgXCIgKyBnZXROb2RlVHlwZShlbCwgaW5mbykgKyBcIic+XCI7XG4gICAgICAgIGlmIChpbmZvID09PSBcIm1pcnJvclwiKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5NaXJyb3Igb2YgXCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPlwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCI7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVsLmRlc2NyaXB0aW9uID09PSBcIlwiKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHN0ZXBOYW1lID0gZWwuZGVzY3JpcHRpb247XG4gICAgICAgICAgc3RlcE5hbWUgPSBzaG9ydGVuU3RyaW5nKHN0ZXBOYW1lKTtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg0IGNsYXNzPSdzdGVwLW5hbWUnPlwiICsgc3RlcE5hbWUgKyBcIjwvaDQ+XCI7X
G4gICAgICAgIH1cbiAgICAgICAgaWYgKGVsLnN0ZXBfZnVuY3Rpb24gIT0gbnVsbCkge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uKGVsLmlkLCBtYXhXLCBtYXhIKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpZiAoaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKSkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5cIiArIGluZm8gKyBcIiBOb2RlPC9oNT5cIjtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLnBhcmFsbGVsaXNtICE9PSBcIlwiKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlBhcmFsbGVsaXNtOiBcIiArIGVsLnBhcmFsbGVsaXNtICsgXCI8L2g1PlwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwub3BlcmF0b3IgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5PcGVyYXRpb246IFwiICsgc2hvcnRlblN0cmluZyhlbC5vcGVyYXRvcl9zdHJhdGVneSkgKyBcIjwvaDU+XCI7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2E+XCI7XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiA9IGZ1bmN0aW9uKGlkLCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIHZhciBsYWJlbFZhbHVlLCBzdmdJRDtcbiAgICAgICAgc3ZnSUQgPS
BcInN2Zy1cIiArIGlkO1xuICAgICAgICBsYWJlbFZhbHVlID0gXCI8c3ZnIGNsYXNzPSdcIiArIHN2Z0lEICsgXCInIHdpZHRoPVwiICsgbWF4VyArIFwiIGhlaWdodD1cIiArIG1heEggKyBcIj48ZyAvPjwvc3ZnPlwiO1xuICAgICAgICByZXR1cm4gbGFiZWxWYWx1ZTtcbiAgICAgIH07XG4gICAgICBzaG9ydGVuU3RyaW5nID0gZnVuY3Rpb24ocykge1xuICAgICAgICB2YXIgc2JyO1xuICAgICAgICBpZiAocy5jaGFyQXQoMCkgPT09IFwiPFwiKSB7XG4gICAgICAgICAgcyA9IHMucmVwbGFjZShcIjxcIiwgXCImbHQ7XCIpO1xuICAgICAgICAgIHMgPSBzLnJlcGxhY2UoXCI+XCIsIFwiJmd0O1wiKTtcbiAgICAgICAgfVxuICAgICAgICBzYnIgPSBcIlwiO1xuICAgICAgICB3aGlsZSAocy5sZW5ndGggPiAzMCkge1xuICAgICAgICAgIHNiciA9IHNiciArIHMuc3Vic3RyaW5nKDAsIDMwKSArIFwiPGJyPlwiO1xuICAgICAgICAgIHMgPSBzLnN1YnN0cmluZygzMCwgcy5sZW5ndGgpO1xuICAgICAgICB9XG4gICAgICAgIHNiciA9IHNiciArIHM7XG4gICAgICAgIHJldHVybiBzYnI7XG4gICAgICB9O1xuICAgICAgY3JlYXRlTm9kZSA9IGZ1bmN0aW9uKGcsIGRhdGEsIGVsLCBpc1BhcmVudCwgbWF4VywgbWF4SCkge1xuICAgICAgICBpZiAoaXNQYXJlbnQgPT0gbnVsbCkge1xuICAgICAgICAgIGlzUGFyZW50ID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVsLmlkID09PSBkYXRhLnBhcnRpYWxfc29
sdXRpb24pIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5uZXh0X3BhcnRpYWxfc29sdXRpb24pIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLndvcmtzZXQpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIndvcmtzZXRcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwid29ya3NldFwi
KVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLm5leHRfd29ya3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFdvcmtzZXRcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFdvcmtzZXRcIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5zb2x1dGlvbl9zZXQpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uU2V0XCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uU2V0XCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEuc29sdXRpb25fZGVsdGEpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uRGVsdGFcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgI
FwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25EZWx0YVwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwiXCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcIlwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlRWRnZSA9IGZ1bmN0aW9uKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKSB7XG4gICAgICAgIHZhciBtaXNzaW5nTm9kZTtcbiAgICAgICAgaWYgKGV4aXN0aW5nTm9kZXMuaW5kZXhPZihwcmVkLmlkKSAhPT0gLTEpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXRFZGdlKHByZWQuaWQsIGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXR
<TRUNCATED>
[12/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Changed overview and timeline
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Changed overview and timeline
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/b29a5d44
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/b29a5d44
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/b29a5d44
Branch: refs/heads/master
Commit: b29a5d446b1b3b2cea5f0bd3230f854f3eecdd3c
Parents: 628e5f7
Author: Piotr Godek <pi...@gmail.com>
Authored: Thu Aug 20 17:06:57 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:50 2015 +0200
----------------------------------------------------------------------
.../runtime/webmonitor/WebRuntimeMonitor.java | 6 +-
.../app/partials/jobs/completed-jobs.jade | 8 +-
.../web-dashboard/app/partials/jobs/job.jade | 8 +-
.../app/partials/jobs/job.plan.node.jade | 4 +-
.../app/partials/jobs/job.timeline.vertex.jade | 2 +-
.../app/partials/jobs/running-jobs.jade | 8 +-
.../web-dashboard/app/partials/overview.jade | 32 +++---
.../app/scripts/common/filters.coffee | 5 +
.../app/scripts/common/services.coffee | 34 +++++++
.../web-dashboard/app/scripts/index.coffee | 17 ++--
.../app/scripts/modules/jobs/jobs.dir.coffee | 13 ++-
.../app/scripts/modules/jobs/jobs.svc.coffee | 20 ++--
.../modules/overview/overview.ctrl.coffee | 3 +
.../modules/overview/overview.svc.coffee | 18 ++--
.../web-dashboard/vendor-local/d3-timeline.js | 69 ++++++++-----
flink-runtime-web/web-dashboard/web/js/index.js | 101 ++++++++++++-------
.../web-dashboard/web/js/vendor.js | 71 ++++++++-----
.../web/partials/jobs/completed-jobs.html | 8 +-
.../web-dashboard/web/partials/jobs/job.html | 8 +-
.../web/partials/jobs/job.plan.node.html | 4 +-
.../web/partials/jobs/job.timeline.vertex.html | 2 +-
.../web/partials/jobs/running-jobs.html | 8 +-
.../web-dashboard/web/partials/overview.html | 32 +++---
23 files changed, 299 insertions(+), 182 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
index 85b2171..0799328 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
@@ -128,14 +128,12 @@ public class WebRuntimeMonitor implements WebMonitor {
// the overview - how many task managers, slots, free slots, ...
.GET("/overview", handler(new RequestOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
- // list of job ids for all jobs in each status
- .GET("/jobids", handler(new RequestJobIdsHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
-
// overview over jobs
.GET("/joboverview", handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, true)))
- .GET("/joboverview/running",handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, false)))
+ .GET("/joboverview/running", handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, false)))
.GET("/joboverview/completed", handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, false, true)))
+ .GET("/jobs", handler(new RequestJobIdsHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
.GET("/jobs/:jobid", handler(new JobSummaryHandler(currentGraphs)))
.GET("/jobs/:jobid/vertices", handler(new JobVerticesOverviewHandler(currentGraphs)))
.GET("/jobs/:jobid/plan", handler(new ExecutionPlanHandler(currentGraphs)))
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
index a629730..1233a21 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/completed-jobs.jade
@@ -36,12 +36,12 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tbody
tr(ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })")
- td {{job['start-time']}}
- td {{job['end-time']}}
- td {{job.duration}}
+ td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ td {{job['end-time'] - job['start-time']}} ms
td {{job.name}}
td {{job.jid}}
td.label-group
- bs-label(status="{{status}}" ng-repeat="(status, value) in job.operators") {{value}}
+ bs-label(status="{{status}}" ng-repeat="(status, value) in job.tasks") {{value}}
td
bs-label(status="{{job.state}}") {{job.state}}
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
index 2591aff..7fb3234 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
@@ -28,15 +28,15 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main(ng-if="job")
.navbar-info.first.last
.label-group
- bs-label(status="{{status}}" ng-repeat="(status, value) in job.operators") {{value}}
+ bs-label(status="{{status}}" ng-repeat="(status, value) in job.tasks") {{value}}
.navbar-info.first.last.hidden-xs.hidden-sm
- | {{ job['start-time'] }}
+ | {{ job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
| -
- | {{ job['end-time'] }}
+ | {{ job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
.navbar-info.last.first
- | {{job.duration}}
+ | {{job['end-time'] - job['start-time']}} ms
nav.navbar.navbar-default.navbar-fixed-top.navbar-main-additional(ng-if="job")
ul.nav.nav-tabs
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
index 0ddd457..9ca30f1 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
@@ -29,7 +29,7 @@
.panel-heading.clearfix
.panel-info.first.last(ng-if="node.description")
- span {{ node.description }}
+ span {{ node.description | humanizeTaskName }}
.panel-body
table.table.table-hover.table-clickable
@@ -40,7 +40,7 @@
tbody
tr(ng-repeat="vertex in node.vertex.groupvertex.groupmembers")
- td {{vertex.vertexname}}
+ td {{ vertex.vertexname | humanizeTaskName }}
td
bs-label(status="{{vertex.vertexstatus}}") {{vertex.vertexstatus}}
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
index e90bf56..5c912f3 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.timeline.vertex.jade
@@ -18,7 +18,7 @@
.panel.panel-default.panel-multi(ng-if="vertex")
.panel-heading.clearfix
.panel-title
- | {{ vertex.groupvertex.groupvertexname }}
+ | {{ vertex.groupvertex.groupvertexname | humanizeTaskName }}
//- .panel-info.first
//- | Vertex ID: {{ vertex.groupvertex.groupvertexid }}
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
index e606508..d76d9af 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/running-jobs.jade
@@ -36,12 +36,12 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tbody
tr(ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })")
- td {{job['start-time']}}
- td {{job['end-time']}}
- td {{job.duration}}
+ td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ td {{job['end-time'] - job['start-time']}} ms
td {{job.name}}
td {{job.jid}}
td.label-group
- bs-label(status="{{status}}" ng-repeat="(status, value) in job.operators") {{value}}
+ bs-label(status="{{status}}" ng-repeat="(status, value) in job.tasks") {{value}}
td
bs-label(status="{{job.state}}") {{job.state}}
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/partials/overview.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/overview.jade b/flink-runtime-web/web-dashboard/app/partials/overview.jade
index e8b1f65..aa92ac0 100644
--- a/flink-runtime-web/web-dashboard/app/partials/overview.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/overview.jade
@@ -31,7 +31,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
.col-xs-3
i.fa.fa-tasks.fa-3x
.col-xs-9.text-right
- .huge 3
+ .huge {{overview.taskmanagers}}
div Task Managers
.panel-heading
@@ -39,7 +39,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
.col-xs-3
i.fa.fa-folder.fa-3x
.col-xs-9.text-right
- .huge 5
+ .huge {{overview["slots-total"]}}
div Task Slots
.panel-heading
@@ -47,7 +47,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
.col-xs-3
i.fa.fa-folder-o.fa-3x
.col-xs-9.text-right
- .huge 2
+ .huge {{overview["slots-available"]}}
div Available Task Slots
.col-md-6
@@ -56,16 +56,16 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
| Total Jobs
.list-group
.list-group-item
- .badge.badge-primary 3
+ .badge.badge-primary {{overview["jobs-running"]}}
| Running
.list-group-item
- .badge.badge-success 6
+ .badge.badge-success {{overview["jobs-finished"]}}
| Finished
.list-group-item
- .badge.badge-info 0
+ .badge.badge-info {{overview["jobs-cancelled"]}}
| Canceled
.list-group-item
- .badge.badge-danger 2
+ .badge.badge-danger {{overview["jobs-failed"]}}
| Failed
//- .row
@@ -96,13 +96,13 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tbody
tr(ng-repeat="job in runningJobs" ui-sref="single-job.plan({ jobid: job.jid })")
- td {{job['start-time']}}
- td {{job['end-time']}}
+ td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
td {{job.duration}}
- td {{job.jobname}}
+ td {{job.name}}
td {{job.jid}}
td.label-group
- bs-label(status="{{status}}" ng-repeat="(status, value) in job.operators") {{value}}
+ bs-label(status="{{status}}" ng-repeat="(status, value) in job.tasks") {{value}}
td
bs-label(status="{{job.state}}") {{job.state}}
@@ -124,12 +124,12 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main
tbody
tr(ng-repeat="job in finishedJobs" ui-sref="single-job.plan({ jobid: job.jid })")
- td {{job['start-time']}}
- td {{job['end-time']}}
- td {{job.duration}}
- td {{job.jobname}}
+ td {{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ td {{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}
+ td {{job['end-time'] - job['start-time']}} ms
+ td {{job.name}}
td {{job.jid}}
td.label-group
- bs-label(status="{{status}}" ng-repeat="(status, value) in job.operators") {{value}}
+ bs-label(status="{{status}}" ng-repeat="(status, value) in job.tasks") {{value}}
td
bs-label(status="{{job.state}}") {{job.state}}
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee b/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee
index 8dcd734..83b51c1 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/common/filters.coffee
@@ -27,3 +27,8 @@ angular.module('flinkApp')
amDurationFormatExtendedFilter.$stateful = angularMomentConfig.statefulFilters
amDurationFormatExtendedFilter
+
+.filter "humanizeTaskName", ->
+ (text) ->
+ # TODO: extend... a lot
+ if text then text.replace(">", ">").replace("<br/>","") else ''
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee b/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
new file mode 100644
index 0000000..2e45a44
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/scripts/common/services.coffee
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+angular.module('flinkApp')
+
+.service 'MainService', ($http, flinkConfig, $q) ->
+ jobObservers = []
+
+ @loadConfig = ->
+ deferred = $q.defer()
+
+ $http.get flinkConfig.newServer + "/config"
+ .success (data, status, headers, config) ->
+ deferred.resolve(data)
+
+ deferred.promise
+
+
+ @
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/scripts/index.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/index.coffee b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
index 3cea143..6c52e12 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
@@ -28,22 +28,25 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
# --------------------------------------
-.constant 'flinkConfig', {
+.value 'flinkConfig', {
jobServer: 'http://localhost:8081'
newServer: 'http://localhost:8081'
# jobServer: 'http://localhost:3000/new-server'
# newServer: 'http://localhost:3000/new-server'
- refreshInterval: 10000
+ "refresh-interval": 10000
}
# --------------------------------------
-.run (JobsService, flinkConfig, $interval) ->
- JobsService.listJobs()
+.run (JobsService, MainService, flinkConfig, $interval) ->
+ MainService.loadConfig().then (config) ->
+ angular.extend flinkConfig, config
- # $interval ->
- # JobsService.listJobs()
- # , flinkConfig.refreshInterval
+ JobsService.listJobs()
+
+ $interval ->
+ JobsService.listJobs()
+ , flinkConfig["refresh-interval"]
# --------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
index da83e83..15a3bb6 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
@@ -44,6 +44,7 @@ angular.module('flinkApp')
{
label: "Scheduled"
color: "#666"
+ borderColor: "#555"
starting_time: vTime["SCHEDULED"] * 100
ending_time: vTime["DEPLOYING"] * 100
type: 'regular'
@@ -51,6 +52,7 @@ angular.module('flinkApp')
{
label: "Deploying"
color: "#aaa"
+ borderColor: "#555"
starting_time: vTime["DEPLOYING"] * 100
ending_time: vTime["RUNNING"] * 100
type: 'regular'
@@ -58,6 +60,7 @@ angular.module('flinkApp')
{
label: "Running"
color: "#ddd"
+ borderColor: "#555"
starting_time: vTime["RUNNING"] * 100
ending_time: vTime["FINISHED"] * 100
type: 'regular'
@@ -76,7 +79,7 @@ angular.module('flinkApp')
label
)
.margin({ left: 100, right: 0, top: 0, bottom: 0 })
- .itemHeight(24)
+ .itemHeight(30)
.relativeTime()
svg = d3.select(svgEl)
@@ -111,8 +114,9 @@ angular.module('flinkApp')
times: [
label: "Scheduled"
color: "#cccccc"
+ borderColor: "#555"
starting_time: data.oldV["SCHEDULED"]
- ending_time: data.oldV["SCHEDULED"] + 30
+ ending_time: data.oldV["SCHEDULED"] + 1
# link: vertex.groupvertexid
type: 'scheduled'
]
@@ -126,7 +130,8 @@ angular.module('flinkApp')
testData.push
times: [
label: translateLabel(vertex.groupvertexname)
- color: "#62cdea"
+ color: "#d9f1f7"
+ borderColor: "#62cdea"
starting_time: vTime["STARTED"]
ending_time: vTime["ENDED"]
link: vertex.groupvertexid
@@ -146,7 +151,7 @@ angular.module('flinkApp')
})
.prefix("main")
.margin({ left: 0, right: 0, top: 0, bottom: 0 })
- .itemHeight(24)
+ .itemHeight(30)
.showBorderLine()
.showHourTimeline()
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index 6640d60..72bdb6c 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -69,21 +69,21 @@ angular.module('flinkApp')
@listJobs = ->
deferred = $q.defer()
- $http.get flinkConfig.newServer + "/jobs"
+ $http.get flinkConfig.newServer + "/joboverview"
.success (data, status, headers, config) ->
angular.forEach data, (list, listKey) ->
switch listKey
- when 'jobs-running' then jobs.running = list
- when 'jobs-finished' then jobs.finished = list
- when 'jobs-cancelled' then jobs.cancelled = list
- when 'jobs-failed' then jobs.failed = list
-
- angular.forEach list, (jobid, index) ->
- $http.get flinkConfig.newServer + "/jobs/" + jobid
- .success (details) ->
- list[index] = details
+ when 'running' then jobs.running = list
+ when 'finished' then jobs.finished = list
+ when 'cancelled' then jobs.cancelled = list
+ when 'failed' then jobs.failed = list
+
+ # angular.forEach list, (jobid, index) ->
+ # $http.get flinkConfig.newServer + "/jobs/" + jobid
+ # .success (details) ->
+ # list[index] = details
deferred.resolve(jobs)
notifyObservers()
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee
index 1a010e9..828f276 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.ctrl.coffee
@@ -28,3 +28,6 @@ angular.module('flinkApp')
JobsService.unRegisterObserver($scope.jobObserver)
$scope.jobObserver()
+
+ OverviewService.loadOverview().then (data) ->
+ $scope.overview = data
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.svc.coffee
index 21e60b1..8ce9d61 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/overview/overview.svc.coffee
@@ -18,17 +18,17 @@
angular.module('flinkApp')
-.service 'OverviewService', ($http, flinkConfig, $log) ->
- serverStatus = {}
+.service 'OverviewService', ($http, flinkConfig, $q) ->
+ overview = {}
- @loadServerStatus = ->
- $http.get(flinkConfig.jobServer + "/monitor/status")
- .success (data, status, headers, config) ->
- $log data
+ @loadOverview = ->
+ deferred = $q.defer()
- .error (data, status, headers, config) ->
- return
+ $http.get(flinkConfig.jobServer + "/overview")
+ .success (data, status, headers, config) ->
+ overview = data
+ deferred.resolve(data)
- serverStatus
+ deferred.promise
@
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js b/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js
index f12d478..aadca55 100644
--- a/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js
+++ b/flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js
@@ -202,16 +202,18 @@
.attr("class", function(d, i) { return "bar-container bar-type-" + d.type; } )
.attr("width", getBarWidth);
- nel
- .append("svg:clipPath")
- .attr("id", prefix + "-timeline-textclip-" + i + "-" + index)
- .attr("class", "timeline-clip")
- .append("svg:rect")
- .attr("clipPathUnits","objectBoundingBox")
- .attr("x", getXPos)
- .attr("y", getStackPosition)
- .attr("width", getTextWidth)
- .attr("height", itemHeight);
+ if (data[0].type != "scheduled") {
+ nel
+ .append("svg:clipPath")
+ .attr("id", prefix + "-timeline-textclip-" + i + "-" + index)
+ .attr("class", "timeline-clip")
+ .append("svg:rect")
+ .attr("clipPathUnits","objectBoundingBox")
+ .attr("x", getXPos)
+ .attr("y", getStackPosition)
+ .attr("width", getTextWidth)
+ .attr("height", itemHeight);
+ }
var bar = nel
.append(function(d, i) {
@@ -219,6 +221,8 @@
})
.attr("x", getXPos)
.attr("y", getStackPosition)
+ .attr("rx", 5)
+ .attr("ry", 5)
.attr("width", getBarWidth)
.attr("cy", function(d, i) {
return getStackPosition(d, i) + itemHeight/2;
@@ -226,6 +230,8 @@
.attr("cx", getXPos)
.attr("r", itemHeight / 2)
.attr("height", itemHeight)
+ .style("stroke", function(d, i){ return d.borderColor; })
+ .style("stroke-width", 1)
.style("fill", function(d, i){
var dColorPropName;
if (d.color) return d.color;
@@ -263,12 +269,12 @@
return d.id ? d.id : "timelineItem_"+index+"_"+i;
})
- nel
+ var barText = nel
.append("text")
.attr("class", "timeline-insidelabel")
.attr("x", getXTextPos)
.attr("y", getStackTextPosition)
- .attr("width", getTextWidth)
+ // .attr("width", getTextWidth)
.attr("height", itemHeight)
.attr("clip-path", "url(#" + prefix + "-timeline-textclip-" + i + "-" + index + ")")
.text(function(d) {
@@ -279,6 +285,10 @@
});
;
+ if (data[0].type == "scheduled") {
+ bar.attr('width', barText.node().getComputedTextLength() + 10);
+ }
+
g.selectAll("svg .bar-container").each(function(d, i) {
$(this).qtip({
content: {
@@ -309,16 +319,19 @@
}
if (showBorderLine) {
- g.selectAll("svg").data(data).enter().append("svg:line")
- .attr("class", "line-" + 'start')
- .attr("x1", getBorderStart)
- .attr("y1", getStackPosition)
- .attr("x2", getBorderStart)
- .attr("y2", margin.top + (itemHeight + itemMargin) * maxStack)
- .style("stroke", function(d, i) { return d.color; })
- .style("stroke-width", showBorderFormat.width);
-
- if (data[0].type != "scheduled") {
+ if (data[0].type == "scheduled") {
+ g.selectAll("svg").data(data).enter().append("svg:line")
+ .attr("class", "line-" + 'start')
+ .attr("x1", getBorderStart)
+ .attr("y1", getStackBorderPosition)
+ .attr("x2", getBorderStart)
+ .attr("y2", margin.top + (itemHeight + itemMargin) * maxStack)
+ .style("stroke", function(d, i) { return d.color; })
+ .style("stroke-width", showBorderFormat.width);
+ }
+
+ // if (data[0].type != "scheduled") {
+ if (false) {
g.selectAll("svg").data(data).enter().append("svg:line")
.attr("class", "line-" + 'end')
.attr("x1", getBorderEnd)
@@ -358,9 +371,15 @@
}
function getStackTextPosition(d, i) {
if (stacked) {
- return margin.top + (itemHeight + itemMargin) * yAxisMapping[index] + itemHeight * 0.70;
+ return margin.top + (itemHeight + itemMargin) * yAxisMapping[index] + itemHeight * 0.65;
+ }
+ return margin.top + itemHeight * 0.65;
+ }
+ function getStackBorderPosition(d, i) {
+ if (stacked) {
+ return margin.top + (itemHeight + itemMargin) * yAxisMapping[index] + itemHeight - 3;
}
- return margin.top + itemHeight * 0.70;
+ return margin.top + itemHeight - 3;
}
});
});
@@ -426,7 +445,7 @@
setHeight();
bbox = g[0][0].getBBox();
- gParent.attr('height', bbox.height + 30);
+ gParent.attr('height', bbox.height + 40);
function getBorderStart(d, i) {
return xScale(d.starting_time);
[31/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Adjust
view for details of a job
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/styles/animate.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/animate.styl b/flink-runtime-web/web-dashboard/app/styles/animate.styl
new file mode 100644
index 0000000..14fd938
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/styles/animate.styl
@@ -0,0 +1,1889 @@
+// Animate.css - http://daneden.me/animate
+// Licensed under the MIT license - http://opensource.org/licenses/MIT
+
+// Copyright (c) 2015 Daniel Eden
+
+22
+ &.2%
+ -webkit-transform skewX(-12.5deg) skewY(-12.5deg)
+ transform skewX(-12.5deg) skewY(-12.5deg)
+ -webkit-transform skewX(-12.5deg) skewY(-12.5deg)
+ transform skewX(-12.5deg) skewY(-12.5deg)
+
+33
+ &.3%
+ -webkit-transform skewX(6.25deg) skewY(6.25deg)
+ transform skewX(6.25deg) skewY(6.25deg)
+ -webkit-transform skewX(6.25deg) skewY(6.25deg)
+ transform skewX(6.25deg) skewY(6.25deg)
+
+44
+ &.4%
+ -webkit-transform skewX(-3.125deg) skewY(-3.125deg)
+ transform skewX(-3.125deg) skewY(-3.125deg)
+ -webkit-transform skewX(-3.125deg) skewY(-3.125deg)
+ transform skewX(-3.125deg) skewY(-3.125deg)
+
+55
+ &.5%
+ -webkit-transform skewX(1.5625deg) skewY(1.5625deg)
+ transform skewX(1.5625deg) skewY(1.5625deg)
+ -webkit-transform skewX(1.5625deg) skewY(1.5625deg)
+ transform skewX(1.5625deg) skewY(1.5625deg)
+
+66
+ &.6%
+ -webkit-transform skewX(-0.78125deg) skewY(-0.78125deg)
+ transform skewX(-0.78125deg) skewY(-0.78125deg)
+ -webkit-transform skewX(-0.78125deg) skewY(-0.78125deg)
+ transform skewX(-0.78125deg) skewY(-0.78125deg)
+
+77
+ &.7%
+ -webkit-transform skewX(0.390625deg) skewY(0.390625deg)
+ transform skewX(0.390625deg) skewY(0.390625deg)
+ -webkit-transform skewX(0.390625deg) skewY(0.390625deg)
+ transform skewX(0.390625deg) skewY(0.390625deg)
+
+88
+ &.8%
+ -webkit-transform skewX(-0.1953125deg) skewY(-0.1953125deg)
+ transform skewX(-0.1953125deg) skewY(-0.1953125deg)
+ -webkit-transform skewX(-0.1953125deg) skewY(-0.1953125deg)
+ transform skewX(-0.1953125deg) skewY(-0.1953125deg)
+
+@charset
+ "UTF-8";
+ .animated
+ -webkit-animation-duration 1s
+ animation-duration 1s
+ -webkit-animation-fill-mode both
+ animation-fill-mode both
+
+.animated
+ &.infinite
+ -webkit-animation-iteration-count infinite
+ animation-iteration-count infinite
+ &.hinge
+ -webkit-animation-duration 2s
+ animation-duration 2s
+
+.animated.bounceIn,
+.animated.bounceOut
+ -webkit-animation-duration .75s
+ animation-duration .75s
+
+.animated.flipOutX,
+.animated.flipOutY
+ -webkit-animation-duration .75s
+ animation-duration .75s
+
+@-webkit-keyframes
+ bounce
+ 0%, 20%, 53%, 80%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ -webkit-transform translate3d(0,0,0)
+ transform translate3d(0,0,0)
+ flash
+ 0%, 50%, 100% {
+ opacity 1
+ pulse
+ 0% {
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ rubberBand
+ 0% {
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ shake
+ 0%, 100% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ swing
+ 20% {
+ -webkit-transform rotate3d(0, 0, 1, 15deg)
+ transform rotate3d(0, 0, 1, 15deg)
+ tada
+ 0% {
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ wobble
+ 0% {
+ -webkit-transform none
+ transform none
+ jello
+ 0%, 11.1%, 100% {
+ -webkit-transform none
+ transform none
+ bounceIn
+ 0%, 20%, 40%, 60%, 80%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceInDown
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceInLeft
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceInRight
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceInUp
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceOut
+ 20% {
+ -webkit-transform scale3d(.9, .9, .9)
+ transform scale3d(.9, .9, .9)
+ bounceOutDown
+ 20% {
+ -webkit-transform translate3d(0, 10px, 0)
+ transform translate3d(0, 10px, 0)
+ bounceOutLeft
+ 20% {
+ opacity 1
+ -webkit-transform translate3d(20px, 0, 0)
+ transform translate3d(20px, 0, 0)
+ bounceOutRight
+ 20% {
+ opacity 1
+ -webkit-transform translate3d(-20px, 0, 0)
+ transform translate3d(-20px, 0, 0)
+ bounceOutUp
+ 20% {
+ -webkit-transform translate3d(0, -10px, 0)
+ transform translate3d(0, -10px, 0)
+ fadeIn
+ 0% {
+ opacity 0
+ fadeInDown
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(0, -100%, 0)
+ transform translate3d(0, -100%, 0)
+ fadeInDownBig
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(0, -2000px, 0)
+ transform translate3d(0, -2000px, 0)
+ fadeInLeft
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(-100%, 0, 0)
+ transform translate3d(-100%, 0, 0)
+ fadeInLeftBig
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(-2000px, 0, 0)
+ transform translate3d(-2000px, 0, 0)
+ fadeInRight
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(100%, 0, 0)
+ transform translate3d(100%, 0, 0)
+ fadeInRightBig
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(2000px, 0, 0)
+ transform translate3d(2000px, 0, 0)
+ fadeInUp
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(0, 100%, 0)
+ transform translate3d(0, 100%, 0)
+ fadeInUpBig
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(0, 2000px, 0)
+ transform translate3d(0, 2000px, 0)
+ fadeOut
+ 0% {
+ opacity 1
+ fadeOutDown
+ 0% {
+ opacity 1
+ fadeOutDownBig
+ 0% {
+ opacity 1
+ fadeOutLeft
+ 0% {
+ opacity 1
+ fadeOutLeftBig
+ 0% {
+ opacity 1
+ fadeOutRight
+ 0% {
+ opacity 1
+ fadeOutRightBig
+ 0% {
+ opacity 1
+ fadeOutUp
+ 0% {
+ opacity 1
+ fadeOutUpBig
+ 0% {
+ opacity 1
+ flip
+ 0% {
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, -360deg)
+ transform perspective(400px) rotate3d(0, 1, 0, -360deg)
+ -webkit-animation-timing-function ease-out
+ animation-timing-function ease-out
+ flipInX
+ 0% {
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, 90deg)
+ transform perspective(400px) rotate3d(1, 0, 0, 90deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ opacity 0
+ flipInY
+ 0% {
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, 90deg)
+ transform perspective(400px) rotate3d(0, 1, 0, 90deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ opacity 0
+ flipOutX
+ 0% {
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ flipOutY
+ 0% {
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ lightSpeedIn
+ 0% {
+ -webkit-transform translate3d(100%, 0, 0) skewX(-30deg)
+ transform translate3d(100%, 0, 0) skewX(-30deg)
+ opacity 0
+ lightSpeedOut
+ 0% {
+ opacity 1
+ rotateIn
+ 0% {
+ -webkit-transform-origin center
+ transform-origin center
+ -webkit-transform rotate3d(0, 0, 1, -200deg)
+ transform rotate3d(0, 0, 1, -200deg)
+ opacity 0
+ rotateInDownLeft
+ 0% {
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform rotate3d(0, 0, 1, -45deg)
+ transform rotate3d(0, 0, 1, -45deg)
+ opacity 0
+ rotateInDownRight
+ 0% {
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform rotate3d(0, 0, 1, 45deg)
+ transform rotate3d(0, 0, 1, 45deg)
+ opacity 0
+ rotateInUpLeft
+ 0% {
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform rotate3d(0, 0, 1, 45deg)
+ transform rotate3d(0, 0, 1, 45deg)
+ opacity 0
+ rotateInUpRight
+ 0% {
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform rotate3d(0, 0, 1, -90deg)
+ transform rotate3d(0, 0, 1, -90deg)
+ opacity 0
+ rotateOut
+ 0% {
+ -webkit-transform-origin center
+ transform-origin center
+ opacity 1
+ rotateOutDownLeft
+ 0% {
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ opacity 1
+ rotateOutDownRight
+ 0% {
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ opacity 1
+ rotateOutUpLeft
+ 0% {
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ opacity 1
+ rotateOutUpRight
+ 0% {
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ opacity 1
+ hinge
+ 0% {
+ -webkit-transform-origin top left
+ transform-origin top left
+ -webkit-animation-timing-function ease-in-out
+ animation-timing-function ease-in-out
+ rollIn
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg)
+ transform translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg)
+ rollOut
+ 0% {
+ opacity 1
+ zoomIn
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.3, .3, .3)
+ transform scale3d(.3, .3, .3)
+ zoomInDown
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(0, -1000px, 0)
+ transform scale3d(.1, .1, .1) translate3d(0, -1000px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomInLeft
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(-1000px, 0, 0)
+ transform scale3d(.1, .1, .1) translate3d(-1000px, 0, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomInRight
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(1000px, 0, 0)
+ transform scale3d(.1, .1, .1) translate3d(1000px, 0, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomInUp
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(0, 1000px, 0)
+ transform scale3d(.1, .1, .1) translate3d(0, 1000px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomOut
+ 0% {
+ opacity 1
+ zoomOutDown
+ 40% {
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
+ transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomOutLeft
+ 40% {
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(42px, 0, 0)
+ transform scale3d(.475, .475, .475) translate3d(42px, 0, 0)
+ zoomOutRight
+ 40% {
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(-42px, 0, 0)
+ transform scale3d(.475, .475, .475) translate3d(-42px, 0, 0)
+ zoomOutUp
+ 40% {
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
+ transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ slideInDown
+ 0% {
+ -webkit-transform translate3d(0, -100%, 0)
+ transform translate3d(0, -100%, 0)
+ visibility visible
+ slideInLeft
+ 0% {
+ -webkit-transform translate3d(-100%, 0, 0)
+ transform translate3d(-100%, 0, 0)
+ visibility visible
+ slideInRight
+ 0% {
+ -webkit-transform translate3d(100%, 0, 0)
+ transform translate3d(100%, 0, 0)
+ visibility visible
+ slideInUp
+ 0% {
+ -webkit-transform translate3d(0, 100%, 0)
+ transform translate3d(0, 100%, 0)
+ visibility visible
+ slideOutDown
+ 0% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ slideOutLeft
+ 0% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ slideOutRight
+ 0% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ slideOutUp
+ 0% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+
+40%, 43%
+ -webkit-animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
+ animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
+ -webkit-transform translate3d(0, -30px, 0)
+ transform translate3d(0, -30px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
+ animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
+ -webkit-transform translate3d(0, -30px, 0)
+ transform translate3d(0, -30px, 0)
+
+70%
+ -webkit-animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
+ animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
+ -webkit-transform translate3d(0, -15px, 0)
+ transform translate3d(0, -15px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
+ animation-timing-function cubic-bezier(0.755, 0.050, 0.855, 0.060)
+ -webkit-transform translate3d(0, -15px, 0)
+ transform translate3d(0, -15px, 0)
+
+90%
+ -webkit-transform translate3d(0,-4px,0)
+ transform translate3d(0,-4px,0)
+ -webkit-transform translate3d(0,-4px,0)
+ transform translate3d(0,-4px,0)
+ -webkit-transform translate3d(0, 5px, 0)
+ transform translate3d(0, 5px, 0)
+ -webkit-transform translate3d(0, 5px, 0)
+ transform translate3d(0, 5px, 0)
+ -webkit-transform translate3d(5px, 0, 0)
+ transform translate3d(5px, 0, 0)
+ -webkit-transform translate3d(5px, 0, 0)
+ transform translate3d(5px, 0, 0)
+ -webkit-transform translate3d(-5px, 0, 0)
+ transform translate3d(-5px, 0, 0)
+ -webkit-transform translate3d(-5px, 0, 0)
+ transform translate3d(-5px, 0, 0)
+ -webkit-transform translate3d(0, -5px, 0)
+ transform translate3d(0, -5px, 0)
+ -webkit-transform translate3d(0, -5px, 0)
+ transform translate3d(0, -5px, 0)
+
+}
+ @keyframes
+ bounce
+ 0%, 20%, 53%, 80%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ -webkit-transform translate3d(0,0,0)
+ transform translate3d(0,0,0)
+ flash
+ 0%, 50%, 100% {
+ opacity 1
+ pulse
+ 0% {
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ rubberBand
+ 0% {
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ shake
+ 0%, 100% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ swing
+ 20% {
+ -webkit-transform rotate3d(0, 0, 1, 15deg)
+ transform rotate3d(0, 0, 1, 15deg)
+ tada
+ 0% {
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ wobble
+ 0% {
+ -webkit-transform none
+ transform none
+ jello
+ 0%, 11.1%, 100% {
+ -webkit-transform none
+ transform none
+ bounceIn
+ 0%, 20%, 40%, 60%, 80%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceInDown
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceInLeft
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceInRight
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceInUp
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ animation-timing-function cubic-bezier(0.215, 0.610, 0.355, 1.000)
+ bounceOut
+ 20% {
+ -webkit-transform scale3d(.9, .9, .9)
+ transform scale3d(.9, .9, .9)
+ bounceOutDown
+ 20% {
+ -webkit-transform translate3d(0, 10px, 0)
+ transform translate3d(0, 10px, 0)
+ bounceOutLeft
+ 20% {
+ opacity 1
+ -webkit-transform translate3d(20px, 0, 0)
+ transform translate3d(20px, 0, 0)
+ bounceOutRight
+ 20% {
+ opacity 1
+ -webkit-transform translate3d(-20px, 0, 0)
+ transform translate3d(-20px, 0, 0)
+ bounceOutUp
+ 20% {
+ -webkit-transform translate3d(0, -10px, 0)
+ transform translate3d(0, -10px, 0)
+ fadeIn
+ 0% {
+ opacity 0
+ fadeInDown
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(0, -100%, 0)
+ transform translate3d(0, -100%, 0)
+ fadeInDownBig
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(0, -2000px, 0)
+ transform translate3d(0, -2000px, 0)
+ fadeInLeft
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(-100%, 0, 0)
+ transform translate3d(-100%, 0, 0)
+ fadeInLeftBig
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(-2000px, 0, 0)
+ transform translate3d(-2000px, 0, 0)
+ fadeInRight
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(100%, 0, 0)
+ transform translate3d(100%, 0, 0)
+ fadeInRightBig
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(2000px, 0, 0)
+ transform translate3d(2000px, 0, 0)
+ fadeInUp
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(0, 100%, 0)
+ transform translate3d(0, 100%, 0)
+ fadeInUpBig
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(0, 2000px, 0)
+ transform translate3d(0, 2000px, 0)
+ fadeOut
+ 0% {
+ opacity 1
+ fadeOutDown
+ 0% {
+ opacity 1
+ fadeOutDownBig
+ 0% {
+ opacity 1
+ fadeOutLeft
+ 0% {
+ opacity 1
+ fadeOutLeftBig
+ 0% {
+ opacity 1
+ fadeOutRight
+ 0% {
+ opacity 1
+ fadeOutRightBig
+ 0% {
+ opacity 1
+ fadeOutUp
+ 0% {
+ opacity 1
+ fadeOutUpBig
+ 0% {
+ opacity 1
+ flip
+ 0% {
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, -360deg)
+ transform perspective(400px) rotate3d(0, 1, 0, -360deg)
+ -webkit-animation-timing-function ease-out
+ animation-timing-function ease-out
+ flipInX
+ 0% {
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, 90deg)
+ transform perspective(400px) rotate3d(1, 0, 0, 90deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ opacity 0
+ flipInY
+ 0% {
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, 90deg)
+ transform perspective(400px) rotate3d(0, 1, 0, 90deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ opacity 0
+ flipOutX
+ 0% {
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ flipOutY
+ 0% {
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ lightSpeedIn
+ 0% {
+ -webkit-transform translate3d(100%, 0, 0) skewX(-30deg)
+ transform translate3d(100%, 0, 0) skewX(-30deg)
+ opacity 0
+ lightSpeedOut
+ 0% {
+ opacity 1
+ rotateIn
+ 0% {
+ -webkit-transform-origin center
+ transform-origin center
+ -webkit-transform rotate3d(0, 0, 1, -200deg)
+ transform rotate3d(0, 0, 1, -200deg)
+ opacity 0
+ rotateInDownLeft
+ 0% {
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform rotate3d(0, 0, 1, -45deg)
+ transform rotate3d(0, 0, 1, -45deg)
+ opacity 0
+ rotateInDownRight
+ 0% {
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform rotate3d(0, 0, 1, 45deg)
+ transform rotate3d(0, 0, 1, 45deg)
+ opacity 0
+ rotateInUpLeft
+ 0% {
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform rotate3d(0, 0, 1, 45deg)
+ transform rotate3d(0, 0, 1, 45deg)
+ opacity 0
+ rotateInUpRight
+ 0% {
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform rotate3d(0, 0, 1, -90deg)
+ transform rotate3d(0, 0, 1, -90deg)
+ opacity 0
+ rotateOut
+ 0% {
+ -webkit-transform-origin center
+ transform-origin center
+ opacity 1
+ rotateOutDownLeft
+ 0% {
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ opacity 1
+ rotateOutDownRight
+ 0% {
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ opacity 1
+ rotateOutUpLeft
+ 0% {
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ opacity 1
+ rotateOutUpRight
+ 0% {
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ opacity 1
+ hinge
+ 0% {
+ -webkit-transform-origin top left
+ transform-origin top left
+ -webkit-animation-timing-function ease-in-out
+ animation-timing-function ease-in-out
+ rollIn
+ 0% {
+ opacity 0
+ -webkit-transform translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg)
+ transform translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg)
+ rollOut
+ 0% {
+ opacity 1
+ zoomIn
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.3, .3, .3)
+ transform scale3d(.3, .3, .3)
+ zoomInDown
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(0, -1000px, 0)
+ transform scale3d(.1, .1, .1) translate3d(0, -1000px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomInLeft
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(-1000px, 0, 0)
+ transform scale3d(.1, .1, .1) translate3d(-1000px, 0, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomInRight
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(1000px, 0, 0)
+ transform scale3d(.1, .1, .1) translate3d(1000px, 0, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomInUp
+ 0% {
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(0, 1000px, 0)
+ transform scale3d(.1, .1, .1) translate3d(0, 1000px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomOut
+ 0% {
+ opacity 1
+ zoomOutDown
+ 40% {
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
+ transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ zoomOutLeft
+ 40% {
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(42px, 0, 0)
+ transform scale3d(.475, .475, .475) translate3d(42px, 0, 0)
+ zoomOutRight
+ 40% {
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(-42px, 0, 0)
+ transform scale3d(.475, .475, .475) translate3d(-42px, 0, 0)
+ zoomOutUp
+ 40% {
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
+ transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ animation-timing-function cubic-bezier(0.550, 0.055, 0.675, 0.190)
+ slideInDown
+ 0% {
+ -webkit-transform translate3d(0, -100%, 0)
+ transform translate3d(0, -100%, 0)
+ visibility visible
+ slideInLeft
+ 0% {
+ -webkit-transform translate3d(-100%, 0, 0)
+ transform translate3d(-100%, 0, 0)
+ visibility visible
+ slideInRight
+ 0% {
+ -webkit-transform translate3d(100%, 0, 0)
+ transform translate3d(100%, 0, 0)
+ visibility visible
+ slideInUp
+ 0% {
+ -webkit-transform translate3d(0, 100%, 0)
+ transform translate3d(0, 100%, 0)
+ visibility visible
+ slideOutDown
+ 0% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ slideOutLeft
+ 0% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ slideOutRight
+ 0% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ slideOutUp
+ 0% {
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ .bounce
+ -webkit-animation-name bounce
+ animation-name bounce
+ -webkit-transform-origin center bottom
+ transform-origin center bottom
+ .flash
+ -webkit-animation-name flash
+ animation-name flash
+ .pulse
+ -webkit-animation-name pulse
+ animation-name pulse
+ .rubberBand
+ -webkit-animation-name rubberBand
+ animation-name rubberBand
+ .shake
+ -webkit-animation-name shake
+ animation-name shake
+ .swing
+ -webkit-transform-origin top center
+ transform-origin top center
+ -webkit-animation-name swing
+ animation-name swing
+ .tada
+ -webkit-animation-name tada
+ animation-name tada
+ .wobble
+ -webkit-animation-name wobble
+ animation-name wobble
+ .jello
+ -webkit-animation-name jello
+ animation-name jello
+ -webkit-transform-origin center
+ transform-origin center
+ .bounceIn
+ -webkit-animation-name bounceIn
+ animation-name bounceIn
+ .bounceInDown
+ -webkit-animation-name bounceInDown
+ animation-name bounceInDown
+ .bounceInLeft
+ -webkit-animation-name bounceInLeft
+ animation-name bounceInLeft
+ .bounceInRight
+ -webkit-animation-name bounceInRight
+ animation-name bounceInRight
+ .bounceInUp
+ -webkit-animation-name bounceInUp
+ animation-name bounceInUp
+ .bounceOut
+ -webkit-animation-name bounceOut
+ animation-name bounceOut
+ .bounceOutDown
+ -webkit-animation-name bounceOutDown
+ animation-name bounceOutDown
+ .bounceOutLeft
+ -webkit-animation-name bounceOutLeft
+ animation-name bounceOutLeft
+ .bounceOutRight
+ -webkit-animation-name bounceOutRight
+ animation-name bounceOutRight
+ .bounceOutUp
+ -webkit-animation-name bounceOutUp
+ animation-name bounceOutUp
+ .fadeIn
+ -webkit-animation-name fadeIn
+ animation-name fadeIn
+ .fadeInDown
+ -webkit-animation-name fadeInDown
+ animation-name fadeInDown
+ .fadeInDownBig
+ -webkit-animation-name fadeInDownBig
+ animation-name fadeInDownBig
+ .fadeInLeft
+ -webkit-animation-name fadeInLeft
+ animation-name fadeInLeft
+ .fadeInLeftBig
+ -webkit-animation-name fadeInLeftBig
+ animation-name fadeInLeftBig
+ .fadeInRight
+ -webkit-animation-name fadeInRight
+ animation-name fadeInRight
+ .fadeInRightBig
+ -webkit-animation-name fadeInRightBig
+ animation-name fadeInRightBig
+ .fadeInUp
+ -webkit-animation-name fadeInUp
+ animation-name fadeInUp
+ .fadeInUpBig
+ -webkit-animation-name fadeInUpBig
+ animation-name fadeInUpBig
+ .fadeOut
+ -webkit-animation-name fadeOut
+ animation-name fadeOut
+ .fadeOutDown
+ -webkit-animation-name fadeOutDown
+ animation-name fadeOutDown
+ .fadeOutDownBig
+ -webkit-animation-name fadeOutDownBig
+ animation-name fadeOutDownBig
+ .fadeOutLeft
+ -webkit-animation-name fadeOutLeft
+ animation-name fadeOutLeft
+ .fadeOutLeftBig
+ -webkit-animation-name fadeOutLeftBig
+ animation-name fadeOutLeftBig
+ .fadeOutRight
+ -webkit-animation-name fadeOutRight
+ animation-name fadeOutRight
+ .fadeOutRightBig
+ -webkit-animation-name fadeOutRightBig
+ animation-name fadeOutRightBig
+ .fadeOutUp
+ -webkit-animation-name fadeOutUp
+ animation-name fadeOutUp
+ .fadeOutUpBig
+ -webkit-animation-name fadeOutUpBig
+ animation-name fadeOutUpBig
+ .animated
+ &.flip
+ -webkit-backface-visibility visible
+ backface-visibility visible
+ -webkit-animation-name flip
+ animation-name flip
+ .flipInX
+ -webkit-backface-visibility visible !important
+ backface-visibility visible !important
+ -webkit-animation-name flipInX
+ animation-name flipInX
+ .flipInY
+ -webkit-backface-visibility visible !important
+ backface-visibility visible !important
+ -webkit-animation-name flipInY
+ animation-name flipInY
+ .flipOutX
+ -webkit-animation-name flipOutX
+ animation-name flipOutX
+ -webkit-backface-visibility visible !important
+ backface-visibility visible !important
+ .flipOutY
+ -webkit-backface-visibility visible !important
+ backface-visibility visible !important
+ -webkit-animation-name flipOutY
+ animation-name flipOutY
+ .lightSpeedIn
+ -webkit-animation-name lightSpeedIn
+ animation-name lightSpeedIn
+ -webkit-animation-timing-function ease-out
+ animation-timing-function ease-out
+ .lightSpeedOut
+ -webkit-animation-name lightSpeedOut
+ animation-name lightSpeedOut
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ .rotateIn
+ -webkit-animation-name rotateIn
+ animation-name rotateIn
+ .rotateInDownLeft
+ -webkit-animation-name rotateInDownLeft
+ animation-name rotateInDownLeft
+ .rotateInDownRight
+ -webkit-animation-name rotateInDownRight
+ animation-name rotateInDownRight
+ .rotateInUpLeft
+ -webkit-animation-name rotateInUpLeft
+ animation-name rotateInUpLeft
+ .rotateInUpRight
+ -webkit-animation-name rotateInUpRight
+ animation-name rotateInUpRight
+ .rotateOut
+ -webkit-animation-name rotateOut
+ animation-name rotateOut
+ .rotateOutDownLeft
+ -webkit-animation-name rotateOutDownLeft
+ animation-name rotateOutDownLeft
+ .rotateOutDownRight
+ -webkit-animation-name rotateOutDownRight
+ animation-name rotateOutDownRight
+ .rotateOutUpLeft
+ -webkit-animation-name rotateOutUpLeft
+ animation-name rotateOutUpLeft
+ .rotateOutUpRight
+ -webkit-animation-name rotateOutUpRight
+ animation-name rotateOutUpRight
+ .hinge
+ -webkit-animation-name hinge
+ animation-name hinge
+ .rollIn
+ -webkit-animation-name rollIn
+ animation-name rollIn
+ .rollOut
+ -webkit-animation-name rollOut
+ animation-name rollOut
+ .zoomIn
+ -webkit-animation-name zoomIn
+ animation-name zoomIn
+ .zoomInDown
+ -webkit-animation-name zoomInDown
+ animation-name zoomInDown
+ .zoomInLeft
+ -webkit-animation-name zoomInLeft
+ animation-name zoomInLeft
+ .zoomInRight
+ -webkit-animation-name zoomInRight
+ animation-name zoomInRight
+ .zoomInUp
+ -webkit-animation-name zoomInUp
+ animation-name zoomInUp
+ .zoomOut
+ -webkit-animation-name zoomOut
+ animation-name zoomOut
+ .zoomOutDown
+ -webkit-animation-name zoomOutDown
+ animation-name zoomOutDown
+ .zoomOutLeft
+ -webkit-animation-name zoomOutLeft
+ animation-name zoomOutLeft
+ .zoomOutRight
+ -webkit-animation-name zoomOutRight
+ animation-name zoomOutRight
+ .zoomOutUp
+ -webkit-animation-name zoomOutUp
+ animation-name zoomOutUp
+ .slideInDown
+ -webkit-animation-name slideInDown
+ animation-name slideInDown
+ .slideInLeft
+ -webkit-animation-name slideInLeft
+ animation-name slideInLeft
+ .slideInRight
+ -webkit-animation-name slideInRight
+ animation-name slideInRight
+ .slideInUp
+ -webkit-animation-name slideInUp
+ animation-name slideInUp
+ .slideOutDown
+ -webkit-animation-name slideOutDown
+ animation-name slideOutDown
+ .slideOutLeft
+ -webkit-animation-name slideOutLeft
+ animation-name slideOutLeft
+ .slideOutRight
+ -webkit-animation-name slideOutRight
+ animation-name slideOutRight
+ .slideOutUp
+ -webkit-animation-name slideOutUp
+ animation-name slideOutUp
+
+25%, 75%
+ opacity 0
+ opacity 0
+
+50%
+ -webkit-transform scale3d(1.05, 1.05, 1.05)
+ transform scale3d(1.05, 1.05, 1.05)
+ -webkit-transform scale3d(1.05, 1.05, 1.05)
+ transform scale3d(1.05, 1.05, 1.05)
+ -webkit-transform scale3d(1.15, 0.85, 1)
+ transform scale3d(1.15, 0.85, 1)
+ -webkit-transform scale3d(1.15, 0.85, 1)
+ transform scale3d(1.15, 0.85, 1)
+ -webkit-transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)
+ transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ -webkit-transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)
+ transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ opacity 1
+ opacity 1
+ opacity 0
+ -webkit-transform scale3d(.3, .3, .3)
+ transform scale3d(.3, .3, .3)
+ opacity 0
+ -webkit-transform scale3d(.3, .3, .3)
+ transform scale3d(.3, .3, .3)
+
+100%
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ -webkit-transform rotate3d(0, 0, 1, 0deg)
+ transform rotate3d(0, 0, 1, 0deg)
+ -webkit-transform rotate3d(0, 0, 1, 0deg)
+ transform rotate3d(0, 0, 1, 0deg)
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ -webkit-transform none
+ transform none
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ opacity 1
+ -webkit-transform scale3d(1, 1, 1)
+ transform scale3d(1, 1, 1)
+ -webkit-transform none
+ transform none
+ -webkit-transform none
+ transform none
+ -webkit-transform none
+ transform none
+ -webkit-transform none
+ transform none
+ -webkit-transform none
+ transform none
+ -webkit-transform none
+ transform none
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ opacity 0
+ -webkit-transform scale3d(.3, .3, .3)
+ transform scale3d(.3, .3, .3)
+ opacity 0
+ -webkit-transform scale3d(.3, .3, .3)
+ transform scale3d(.3, .3, .3)
+ opacity 0
+ -webkit-transform translate3d(0, 2000px, 0)
+ transform translate3d(0, 2000px, 0)
+ opacity 0
+ -webkit-transform translate3d(0, 2000px, 0)
+ transform translate3d(0, 2000px, 0)
+ opacity 0
+ -webkit-transform translate3d(-2000px, 0, 0)
+ transform translate3d(-2000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(-2000px, 0, 0)
+ transform translate3d(-2000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(2000px, 0, 0)
+ transform translate3d(2000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(2000px, 0, 0)
+ transform translate3d(2000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(0, -2000px, 0)
+ transform translate3d(0, -2000px, 0)
+ opacity 0
+ -webkit-transform translate3d(0, -2000px, 0)
+ transform translate3d(0, -2000px, 0)
+ opacity 1
+ opacity 1
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 0
+ opacity 0
+ opacity 0
+ -webkit-transform translate3d(0, 100%, 0)
+ transform translate3d(0, 100%, 0)
+ opacity 0
+ -webkit-transform translate3d(0, 100%, 0)
+ transform translate3d(0, 100%, 0)
+ opacity 0
+ -webkit-transform translate3d(0, 2000px, 0)
+ transform translate3d(0, 2000px, 0)
+ opacity 0
+ -webkit-transform translate3d(0, 2000px, 0)
+ transform translate3d(0, 2000px, 0)
+ opacity 0
+ -webkit-transform translate3d(-100%, 0, 0)
+ transform translate3d(-100%, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(-100%, 0, 0)
+ transform translate3d(-100%, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(-2000px, 0, 0)
+ transform translate3d(-2000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(-2000px, 0, 0)
+ transform translate3d(-2000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(100%, 0, 0)
+ transform translate3d(100%, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(100%, 0, 0)
+ transform translate3d(100%, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(2000px, 0, 0)
+ transform translate3d(2000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(2000px, 0, 0)
+ transform translate3d(2000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(0, -100%, 0)
+ transform translate3d(0, -100%, 0)
+ opacity 0
+ -webkit-transform translate3d(0, -100%, 0)
+ transform translate3d(0, -100%, 0)
+ opacity 0
+ -webkit-transform translate3d(0, -2000px, 0)
+ transform translate3d(0, -2000px, 0)
+ opacity 0
+ -webkit-transform translate3d(0, -2000px, 0)
+ transform translate3d(0, -2000px, 0)
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ -webkit-transform perspective(400px)
+ transform perspective(400px)
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, 90deg)
+ transform perspective(400px) rotate3d(1, 0, 0, 90deg)
+ opacity 0
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, 90deg)
+ transform perspective(400px) rotate3d(1, 0, 0, 90deg)
+ opacity 0
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, 90deg)
+ transform perspective(400px) rotate3d(0, 1, 0, 90deg)
+ opacity 0
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, 90deg)
+ transform perspective(400px) rotate3d(0, 1, 0, 90deg)
+ opacity 0
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform translate3d(100%, 0, 0) skewX(30deg)
+ transform translate3d(100%, 0, 0) skewX(30deg)
+ opacity 0
+ -webkit-transform translate3d(100%, 0, 0) skewX(30deg)
+ transform translate3d(100%, 0, 0) skewX(30deg)
+ opacity 0
+ -webkit-transform-origin center
+ transform-origin center
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin center
+ transform-origin center
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform-origin center
+ transform-origin center
+ -webkit-transform rotate3d(0, 0, 1, 200deg)
+ transform rotate3d(0, 0, 1, 200deg)
+ opacity 0
+ -webkit-transform-origin center
+ transform-origin center
+ -webkit-transform rotate3d(0, 0, 1, 200deg)
+ transform rotate3d(0, 0, 1, 200deg)
+ opacity 0
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform rotate3d(0, 0, 1, 45deg)
+ transform rotate3d(0, 0, 1, 45deg)
+ opacity 0
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform rotate3d(0, 0, 1, 45deg)
+ transform rotate3d(0, 0, 1, 45deg)
+ opacity 0
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform rotate3d(0, 0, 1, -45deg)
+ transform rotate3d(0, 0, 1, -45deg)
+ opacity 0
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform rotate3d(0, 0, 1, -45deg)
+ transform rotate3d(0, 0, 1, -45deg)
+ opacity 0
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform rotate3d(0, 0, 1, -45deg)
+ transform rotate3d(0, 0, 1, -45deg)
+ opacity 0
+ -webkit-transform-origin left bottom
+ transform-origin left bottom
+ -webkit-transform rotate3d(0, 0, 1, -45deg)
+ transform rotate3d(0, 0, 1, -45deg)
+ opacity 0
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform rotate3d(0, 0, 1, 90deg)
+ transform rotate3d(0, 0, 1, 90deg)
+ opacity 0
+ -webkit-transform-origin right bottom
+ transform-origin right bottom
+ -webkit-transform rotate3d(0, 0, 1, 90deg)
+ transform rotate3d(0, 0, 1, 90deg)
+ opacity 0
+ -webkit-transform translate3d(0, 700px, 0)
+ transform translate3d(0, 700px, 0)
+ opacity 0
+ -webkit-transform translate3d(0, 700px, 0)
+ transform translate3d(0, 700px, 0)
+ opacity 0
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 1
+ -webkit-transform none
+ transform none
+ opacity 0
+ -webkit-transform translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg)
+ transform translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg)
+ opacity 0
+ -webkit-transform translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg)
+ transform translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg)
+ opacity 0
+ opacity 0
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(0, 2000px, 0)
+ transform scale3d(.1, .1, .1) translate3d(0, 2000px, 0)
+ -webkit-transform-origin center bottom
+ transform-origin center bottom
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(0, 2000px, 0)
+ transform scale3d(.1, .1, .1) translate3d(0, 2000px, 0)
+ -webkit-transform-origin center bottom
+ transform-origin center bottom
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 0
+ -webkit-transform scale(.1) translate3d(-2000px, 0, 0)
+ transform scale(.1) translate3d(-2000px, 0, 0)
+ -webkit-transform-origin left center
+ transform-origin left center
+ opacity 0
+ -webkit-transform scale(.1) translate3d(-2000px, 0, 0)
+ transform scale(.1) translate3d(-2000px, 0, 0)
+ -webkit-transform-origin left center
+ transform-origin left center
+ opacity 0
+ -webkit-transform scale(.1) translate3d(2000px, 0, 0)
+ transform scale(.1) translate3d(2000px, 0, 0)
+ -webkit-transform-origin right center
+ transform-origin right center
+ opacity 0
+ -webkit-transform scale(.1) translate3d(2000px, 0, 0)
+ transform scale(.1) translate3d(2000px, 0, 0)
+ -webkit-transform-origin right center
+ transform-origin right center
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(0, -2000px, 0)
+ transform scale3d(.1, .1, .1) translate3d(0, -2000px, 0)
+ -webkit-transform-origin center bottom
+ transform-origin center bottom
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 0
+ -webkit-transform scale3d(.1, .1, .1) translate3d(0, -2000px, 0)
+ transform scale3d(.1, .1, .1) translate3d(0, -2000px, 0)
+ -webkit-transform-origin center bottom
+ transform-origin center bottom
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ -webkit-transform translate3d(0, 0, 0)
+ transform translate3d(0, 0, 0)
+ visibility hidden
+ -webkit-transform translate3d(0, 100%, 0)
+ transform translate3d(0, 100%, 0)
+ visibility hidden
+ -webkit-transform translate3d(0, 100%, 0)
+ transform translate3d(0, 100%, 0)
+ visibility hidden
+ -webkit-transform translate3d(-100%, 0, 0)
+ transform translate3d(-100%, 0, 0)
+ visibility hidden
+ -webkit-transform translate3d(-100%, 0, 0)
+ transform translate3d(-100%, 0, 0)
+ visibility hidden
+ -webkit-transform translate3d(100%, 0, 0)
+ transform translate3d(100%, 0, 0)
+ visibility hidden
+ -webkit-transform translate3d(100%, 0, 0)
+ transform translate3d(100%, 0, 0)
+ visibility hidden
+ -webkit-transform translate3d(0, -100%, 0)
+ transform translate3d(0, -100%, 0)
+ visibility hidden
+ -webkit-transform translate3d(0, -100%, 0)
+ transform translate3d(0, -100%, 0)
+
+30%
+ -webkit-transform scale3d(1.25, 0.75, 1)
+ transform scale3d(1.25, 0.75, 1)
+ -webkit-transform scale3d(1.25, 0.75, 1)
+ transform scale3d(1.25, 0.75, 1)
+ -webkit-transform translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)
+ transform translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)
+ -webkit-transform translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)
+ transform translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, -20deg)
+ transform perspective(400px) rotate3d(1, 0, 0, -20deg)
+ opacity 1
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, -20deg)
+ transform perspective(400px) rotate3d(1, 0, 0, -20deg)
+ opacity 1
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, -15deg)
+ transform perspective(400px) rotate3d(0, 1, 0, -15deg)
+ opacity 1
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, -15deg)
+ transform perspective(400px) rotate3d(0, 1, 0, -15deg)
+ opacity 1
+
+40%
+ -webkit-transform scale3d(0.75, 1.25, 1)
+ transform scale3d(0.75, 1.25, 1)
+ -webkit-transform scale3d(0.75, 1.25, 1)
+ transform scale3d(0.75, 1.25, 1)
+ -webkit-transform rotate3d(0, 0, 1, -10deg)
+ transform rotate3d(0, 0, 1, -10deg)
+ -webkit-transform rotate3d(0, 0, 1, -10deg)
+ transform rotate3d(0, 0, 1, -10deg)
+ -webkit-transform scale3d(.9, .9, .9)
+ transform scale3d(.9, .9, .9)
+ -webkit-transform scale3d(.9, .9, .9)
+ transform scale3d(.9, .9, .9)
+ -webkit-transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)
+ transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)
+ -webkit-animation-timing-function ease-out
+ animation-timing-function ease-out
+ -webkit-transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)
+ transform perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)
+ -webkit-animation-timing-function ease-out
+ animation-timing-function ease-out
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, -20deg)
+ transform perspective(400px) rotate3d(1, 0, 0, -20deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, -20deg)
+ transform perspective(400px) rotate3d(1, 0, 0, -20deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, -20deg)
+ transform perspective(400px) rotate3d(0, 1, 0, -20deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, -20deg)
+ transform perspective(400px) rotate3d(0, 1, 0, -20deg)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+
+65%
+ -webkit-transform scale3d(.95, 1.05, 1)
+ transform scale3d(.95, 1.05, 1)
+ -webkit-transform scale3d(.95, 1.05, 1)
+ transform scale3d(.95, 1.05, 1)
+
+75%
+ -webkit-transform scale3d(1.05, .95, 1)
+ transform scale3d(1.05, .95, 1)
+ -webkit-transform scale3d(1.05, .95, 1)
+ transform scale3d(1.05, .95, 1)
+ -webkit-transform translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)
+ transform translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)
+ -webkit-transform translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)
+ transform translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)
+ -webkit-transform translate3d(0, -10px, 0)
+ transform translate3d(0, -10px, 0)
+ -webkit-transform translate3d(0, -10px, 0)
+ transform translate3d(0, -10px, 0)
+ -webkit-transform translate3d(-10px, 0, 0)
+ transform translate3d(-10px, 0, 0)
+ -webkit-transform translate3d(-10px, 0, 0)
+ transform translate3d(-10px, 0, 0)
+ -webkit-transform translate3d(10px, 0, 0)
+ transform translate3d(10px, 0, 0)
+ -webkit-transform translate3d(10px, 0, 0)
+ transform translate3d(10px, 0, 0)
+ -webkit-transform translate3d(0, 10px, 0)
+ transform translate3d(0, 10px, 0)
+ -webkit-transform translate3d(0, 10px, 0)
+ transform translate3d(0, 10px, 0)
+
+10%, 30%, 50%, 70%, 90%
+ -webkit-transform translate3d(-10px, 0, 0)
+ transform translate3d(-10px, 0, 0)
+ -webkit-transform translate3d(-10px, 0, 0)
+ transform translate3d(-10px, 0, 0)
+
+20%, 40%, 60%, 80%
+ -webkit-transform translate3d(10px, 0, 0)
+ transform translate3d(10px, 0, 0)
+ -webkit-transform translate3d(10px, 0, 0)
+ transform translate3d(10px, 0, 0)
+
+60%
+ -webkit-transform rotate3d(0, 0, 1, 5deg)
+ transform rotate3d(0, 0, 1, 5deg)
+ -webkit-transform rotate3d(0, 0, 1, 5deg)
+ transform rotate3d(0, 0, 1, 5deg)
+ -webkit-transform translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)
+ transform translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)
+ -webkit-transform translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)
+ transform translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)
+ opacity 1
+ -webkit-transform scale3d(1.03, 1.03, 1.03)
+ transform scale3d(1.03, 1.03, 1.03)
+ opacity 1
+ -webkit-transform scale3d(1.03, 1.03, 1.03)
+ transform scale3d(1.03, 1.03, 1.03)
+ opacity 1
+ -webkit-transform translate3d(0, 25px, 0)
+ transform translate3d(0, 25px, 0)
+ opacity 1
+ -webkit-transform translate3d(0, 25px, 0)
+ transform translate3d(0, 25px, 0)
+ opacity 1
+ -webkit-transform translate3d(25px, 0, 0)
+ transform translate3d(25px, 0, 0)
+ opacity 1
+ -webkit-transform translate3d(25px, 0, 0)
+ transform translate3d(25px, 0, 0)
+ opacity 1
+ -webkit-transform translate3d(-25px, 0, 0)
+ transform translate3d(-25px, 0, 0)
+ opacity 1
+ -webkit-transform translate3d(-25px, 0, 0)
+ transform translate3d(-25px, 0, 0)
+ opacity 1
+ -webkit-transform translate3d(0, -20px, 0)
+ transform translate3d(0, -20px, 0)
+ opacity 1
+ -webkit-transform translate3d(0, -20px, 0)
+ transform translate3d(0, -20px, 0)
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, 10deg)
+ transform perspective(400px) rotate3d(1, 0, 0, 10deg)
+ opacity 1
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, 10deg)
+ transform perspective(400px) rotate3d(1, 0, 0, 10deg)
+ opacity 1
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, 10deg)
+ transform perspective(400px) rotate3d(0, 1, 0, 10deg)
+ opacity 1
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, 10deg)
+ transform perspective(400px) rotate3d(0, 1, 0, 10deg)
+ opacity 1
+ -webkit-transform skewX(20deg)
+ transform skewX(20deg)
+ opacity 1
+ -webkit-transform skewX(20deg)
+ transform skewX(20deg)
+ opacity 1
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
+ transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
+ transform scale3d(.475, .475, .475) translate3d(0, 60px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(10px, 0, 0)
+ transform scale3d(.475, .475, .475) translate3d(10px, 0, 0)
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(10px, 0, 0)
+ transform scale3d(.475, .475, .475) translate3d(10px, 0, 0)
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(-10px, 0, 0)
+ transform scale3d(.475, .475, .475) translate3d(-10px, 0, 0)
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(-10px, 0, 0)
+ transform scale3d(.475, .475, .475) translate3d(-10px, 0, 0)
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
+ transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ opacity 1
+ -webkit-transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
+ transform scale3d(.475, .475, .475) translate3d(0, -60px, 0)
+ -webkit-animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+ animation-timing-function cubic-bezier(0.175, 0.885, 0.320, 1)
+
+80%
+ -webkit-transform rotate3d(0, 0, 1, -5deg)
+ transform rotate3d(0, 0, 1, -5deg)
+ -webkit-transform rotate3d(0, 0, 1, -5deg)
+ transform rotate3d(0, 0, 1, -5deg)
+ -webkit-transform scale3d(.97, .97, .97)
+ transform scale3d(.97, .97, .97)
+ -webkit-transform scale3d(.97, .97, .97)
+ transform scale3d(.97, .97, .97)
+ -webkit-transform perspective(400px) scale3d(.95, .95, .95)
+ transform perspective(400px) scale3d(.95, .95, .95)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ -webkit-transform perspective(400px) scale3d(.95, .95, .95)
+ transform perspective(400px) scale3d(.95, .95, .95)
+ -webkit-animation-timing-function ease-in
+ animation-timing-function ease-in
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, -5deg)
+ transform perspective(400px) rotate3d(1, 0, 0, -5deg)
+ -webkit-transform perspective(400px) rotate3d(1, 0, 0, -5deg)
+ transform perspective(400px) rotate3d(1, 0, 0, -5deg)
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, -5deg)
+ transform perspective(400px) rotate3d(0, 1, 0, -5deg)
+ -webkit-transform perspective(400px) rotate3d(0, 1, 0, -5deg)
+ transform perspective(400px) rotate3d(0, 1, 0, -5deg)
+ -webkit-transform skewX(-5deg)
+ transform skewX(-5deg)
+ opacity 1
+ -webkit-transform skewX(-5deg)
+ transform skewX(-5deg)
+ opacity 1
+
+10%, 20%
+ -webkit-transform scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)
+ transform scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)
+ -webkit-transform scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)
+ transform scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)
+
+30%, 50%, 70%, 90%
+ -webkit-transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)
+ transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)
+ -webkit-transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)
+ transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)
+
+40%, 60%, 80%
+ -webkit-transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)
+ transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)
+ -webkit-transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)
+ transform scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)
+
+15%
+ -webkit-transform translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg)
+ transform translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg)
+ -webkit-transform translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg)
+ transform translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg)
+
+45%
+ -webkit-transform translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)
+ transform translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)
+ -webkit-transform translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)
+ transform translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)
+
+0%
+ opacity 0
+ -webkit-transform scale3d(.3, .3, .3)
+ transform scale3d(.3, .3, .3)
+ opacity 0
+ -webkit-transform scale3d(.3, .3, .3)
+ transform scale3d(.3, .3, .3)
+ opacity 0
+ -webkit-transform translate3d(0, -3000px, 0)
+ transform translate3d(0, -3000px, 0)
+ opacity 0
+ -webkit-transform translate3d(0, -3000px, 0)
+ transform translate3d(0, -3000px, 0)
+ opacity 0
+ -webkit-transform translate3d(-3000px, 0, 0)
+ transform translate3d(-3000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(-3000px, 0, 0)
+ transform translate3d(-3000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(3000px, 0, 0)
+ transform translate3d(3000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(3000px, 0, 0)
+ transform translate3d(3000px, 0, 0)
+ opacity 0
+ -webkit-transform translate3d(0, 3000px, 0)
+ transform translate3d(0, 3000px, 0)
+ opacity 0
+ -webkit-transform translate3d(0, 3000px, 0)
+ transform translate3d(0, 3000px, 0)
+
+20%
+ -webkit-transform scale3d(1.1, 1.1, 1.1)
+ transform scale3d(1.1, 1.1, 1.1)
+ -webkit-transform scale3d(1.1, 1.1, 1.1)
+ transform scale3d(1.1, 1.1, 1.1)
+
+50%, 55%
+ opacity 1
+ -webkit-transform scale3d(1.1, 1.1, 1.1)
+ transform scale3d(1.1, 1.1, 1.1)
+ opacity 1
+ -webkit-transform scale3d(1.1, 1.1, 1.1)
+ transform scale3d(1.1, 1.1, 1.1)
+
+40%, 45%
+ opacity 1
+ -webkit-transform translate3d(0, -20px, 0)
+ transform translate3d(0, -20px, 0)
+ opacity 1
+ -webkit-transform translate3d(0, -20px, 0)
+ transform translate3d(0, -20px, 0)
+ opacity 1
+ -webkit-transform translate3d(0, 20px, 0)
+ transform translate3d(0, 20px, 0)
+ opacity 1
+ -webkit-transform translate3d(0, 20px, 0)
+ transform translate3d(0, 20px, 0)
+
+20%, 60%
+ -webkit-transform rotate3d(0, 0, 1, 80deg)
+ transform rotate3d(0, 0, 1, 80deg)
+ -webkit-transform-origin top left
+ transform-origin top left
+ -webkit-animation-timing-function ease-in-out
+ animation-timing-function ease-in-out
+ -webkit-transform rotate3d(0, 0, 1, 80deg)
+ transform rotate3d(0, 0, 1, 80deg)
+ -webkit-transform-origin top left
+ transform-origin top left
+ -webkit-animation-timing-function ease-in-out
+ animation-timing-function ease-in-out
+
+40%, 80%
+ -webkit-transform rotate3d(0, 0, 1, 60deg)
+ transform rotate3d(0, 0, 1, 60deg)
+ -webkit-transform-origin top left
+ transform-origin top left
+ -webkit-animation-timing-function ease-in-out
+ animation-timing-function ease-in-out
+ opacity 1
+ -webkit-transform rotate3d(0, 0, 1, 60deg)
+ transform rotate3d(0, 0, 1, 60deg)
+ -webkit-transform-origin top left
+ transform-origin top left
+ -webkit-animation-timing-function ease-in-out
+ animation-timing-function ease-in-out
+ opacity 1
+
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/styles/graph.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/graph.styl b/flink-runtime-web/web-dashboard/app/styles/graph.styl
index 3753f69..6de87c2 100644
--- a/flink-runtime-web/web-dashboard/app/styles/graph.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/graph.styl
@@ -29,6 +29,7 @@ svg.graph
font-size 14px
.node
+ cursor: pointer
> rect
stroke #999
stroke-width 5px
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/styles/index.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/index.styl b/flink-runtime-web/web-dashboard/app/styles/index.styl
index 3c146a5..6299185 100644
--- a/flink-runtime-web/web-dashboard/app/styles/index.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/index.styl
@@ -17,8 +17,10 @@
*/
@import 'nib'
+@import './animate'
sidebar-width = 250px
+sidebar-width-small = 160px
#sidebar
overflow: hidden
@@ -39,6 +41,14 @@ sidebar-width = 250px
.logo
width: auto
height: 22px
+ img
+ display: inline-block
+
+ .navbar-static-top
+ overflow: hidden
+ height: 51px
+ .navbar-header
+ width: 100%
.navbar-brand.navbar-brand-text
font-size: 14px
@@ -118,6 +128,14 @@ sidebar-width = 250px
th
font-weight: normal
color: #999999
+
+ td.td-long
+ width: 20%
+ white-space: pre-wrap
+ white-space: -moz-pre-wrap
+ white-space: -pre-wrap
+ white-space: -o-pre-wrap
+ word-wrap: break-word
&.table-clickable
tr
@@ -301,11 +319,37 @@ pre.exception
border-bottom: none
border-left: 2px solid #000000
+.navbar-main, .navbar-secondary, .navbar-main-additional, .navbar-secondary-additional
+ .navbar-title
+ padding: 12px 20px 13px 20px
+
@import './job'
@import './graph'
@import './timeline'
-@media (min-width: 768px)
+@media (min-width: 1024px) and (max-width: 1279px)
+ #sidebar
+ left: 0
+ width: sidebar-width-small
+
+ .navbar-static-top
+ .navbar-brand-text
+ display: none
+
+ #content
+ margin-left: sidebar-width-small
+
+ #fold-button
+ display: none
+
+ .navbar-main, .navbar-main-additional
+ left: sidebar-width-small
+
+ .table
+ td.td-long
+ width: 20%
+
+@media (min-width: 1280px)
#sidebar
left: 0
@@ -318,6 +362,6 @@ pre.exception
.navbar-main, .navbar-main-additional
left: sidebar-width
- .navbar-main, .navbar-secondary, .navbar-main-additional, .navbar-secondary-additional
- .navbar-title
- padding: 12px 20px 13px 20px
+ .table
+ td.td-long
+ width: 30%
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/app/styles/job.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/job.styl b/flink-runtime-web/web-dashboard/app/styles/job.styl
index ef12806..f499260 100644
--- a/flink-runtime-web/web-dashboard/app/styles/job.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/job.styl
@@ -43,18 +43,16 @@
&.table-body-hover
> tbody
border-top: none
+ border-left: 2px solid transparent
+ &.active
+ border-left: 2px solid #000000
&:hover, &.active
td:not(.tab-column)
background-color: #f0f0f0
td.tab-column li.active
- background-color: #f0f0f0
+ background-color: #f0f0f0
&.table-activable
- // tbody
- // border-left: 2px solid transparent
- // &.active
- // border-left: 2px solid #000000
-
th.tab-column, td.tab-column
border-top: none
width: 47px
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/css/index.css
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/css/index.css b/flink-runtime-web/web-dashboard/web/css/index.css
index 2077b14..09f8291 100644
--- a/flink-runtime-web/web-dashboard/web/css/index.css
+++ b/flink-runtime-web/web-dashboard/web/css/index.css
@@ -39,6 +39,16 @@
width: auto;
height: 22px;
}
+#sidebar .logo img {
+ display: inline-block;
+}
+#sidebar .navbar-static-top {
+ overflow: hidden;
+ height: 51px;
+}
+#sidebar .navbar-static-top .navbar-header {
+ width: 100%;
+}
#sidebar .navbar-brand.navbar-brand-text {
font-size: 14px;
font-weight: bold;
@@ -132,6 +142,14 @@
font-weight: normal;
color: #999;
}
+.table td.td-long {
+ width: 20%;
+ white-space: pre-wrap;
+ white-space: -moz-pre-wrap;
+ white-space: -pre-wrap;
+ white-space: -o-pre-wrap;
+ word-wrap: break-word;
+}
.table.table-clickable tr {
cursor: pointer;
}
@@ -355,6 +373,12 @@ pre.exception {
border-bottom: none;
border-left: 2px solid #000;
}
+.navbar-main .navbar-title,
+.navbar-secondary .navbar-title,
+.navbar-main-additional .navbar-title,
+.navbar-secondary-additional .navbar-title {
+ padding: 12px 20px 13px 20px;
+}
.canvas-wrapper {
border: 1px solid #ddd;
position: relative;
@@ -382,6 +406,10 @@ pre.exception {
}
.table.table-body-hover > tbody {
border-top: none;
+ border-left: 2px solid transparent;
+}
+.table.table-body-hover > tbody.active {
+ border-left: 2px solid #000;
}
.table.table-body-hover > tbody:hover td:not(.tab-column),
.table.table-body-hover > tbody.active td:not(.tab-column) {
@@ -425,6 +453,9 @@ svg.graph text {
font-weight: 300;
font-size: 14px;
}
+svg.graph .node {
+ cursor: pointer;
+}
svg.graph .node > rect {
stroke: #999;
stroke-width: 5px;
@@ -498,7 +529,29 @@ svg.graph .node-label {
font-size: 14px;
line-height: 1.4;
}
-@media (min-width: 768px) {
+@media (min-width: 1024px) and (max-width: 1279px) {
+ #sidebar {
+ left: 0;
+ width: 160px;
+ }
+ #sidebar .navbar-static-top .navbar-brand-text {
+ display: none;
+ }
+ #content {
+ margin-left: 160px;
+ }
+ #content #fold-button {
+ display: none;
+ }
+ #content .navbar-main,
+ #content .navbar-main-additional {
+ left: 160px;
+ }
+ .table td.td-long {
+ width: 20%;
+ }
+}
+@media (min-width: 1280px) {
#sidebar {
left: 0;
}
@@ -512,10 +565,7 @@ svg.graph .node-label {
#content .navbar-main-additional {
left: 250px;
}
- .navbar-main .navbar-title,
- .navbar-secondary .navbar-title,
- .navbar-main-additional .navbar-title,
- .navbar-secondary-additional .navbar-title {
- padding: 12px 20px 13px 20px;
+ .table td.td-long {
+ width: 30%;
}
}
[20/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Add
view for exceptions
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Add view for exceptions
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/ba31f83a
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/ba31f83a
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/ba31f83a
Branch: refs/heads/master
Commit: ba31f83a13d7baa9c922f4a379fd5f4e3e62756c
Parents: 907f773
Author: Piotr Godek <pi...@gmail.com>
Authored: Fri Aug 21 17:42:13 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:51 2015 +0200
----------------------------------------------------------------------
.../app/partials/jobs/job.exceptions.jade | 64 ++++----------------
.../web-dashboard/app/partials/jobs/job.jade | 2 +-
.../web-dashboard/app/scripts/index.coffee | 1 +
.../app/scripts/modules/jobs/jobs.ctrl.coffee | 7 +++
.../app/scripts/modules/jobs/jobs.svc.coffee | 22 ++++---
.../web-dashboard/app/styles/index.styl | 10 +++
.../web-dashboard/web/css/index.css | 9 +++
flink-runtime-web/web-dashboard/web/js/index.js | 29 ++++++---
.../web/partials/jobs/job.exceptions.html | 62 ++++---------------
.../web-dashboard/web/partials/jobs/job.html | 2 +-
10 files changed, 89 insertions(+), 119 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
index ff79fe5..d2a0d58 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
@@ -15,55 +15,15 @@
See the License for the specific language governing permissions and
limitations under the License.
-pre
- | org.apache.flink.client.program.ProgramInvocationException: The
- | program execution failed: java.lang.Exception: The data preparation
- | for task 'Reduce (SUM(2))' , caused an error: Error obtaining the
- | sorted input: Thread 'SortMerger spilling thread' terminated due to an
- | exception: The user-defined combiner failed in its 'open()' method.
- | at org.apache.flink.runtime.operators.RegularPactTask.run(RegularPactTask.java:472)
- | at org.apache.flink.runtime.operators.RegularPactTask.invoke(RegularPactTask.java:360)
- | at org.apache.flink.runtime.execution.RuntimeEnvironment.run(RuntimeEnvironment.java:204)
- | at java.lang.Thread.run(Thread.java:745)
- | Caused by: java.lang.RuntimeException: Error obtaining the sorted
- | input: Thread 'SortMerger spilling thread' terminated due to an
- | exception: The user-defined combiner failed in its 'open()' method.
- | at org.apache.flink.runtime.operators.sort.UnilateralSortMerger.getIterator(UnilateralSortMerger.java:607)
- | at org.apache.flink.runtime.operators.RegularPactTask.getInput(RegularPactTask.java:1133)
- | at org.apache.flink.runtime.operators.GroupReduceDriver.prepare(GroupReduceDriver.java:94)
- | at org.apache.flink.runtime.operators.RegularPactTask.run(RegularPactTask.java:466)
- | ... 3 more
- | Caused by: java.io.IOException: Thread 'SortMerger spilling thread'
- | terminated due to an exception: The user-defined combiner failed in
- | its 'open()' method.
- | at org.apache.flink.runtime.operators.sort.UnilateralSortMerger$ThreadBase.run(UnilateralSortMerger.java:785)
- | Caused by: java.io.IOException: The user-defined combiner failed in
- | its 'open()' method.
- | at org.apache.flink.runtime.operators.sort.CombiningUnilateralSortMerger$CombiningSpillingThread.go(CombiningUnilateralSortMerger.java:264)
- | at org.apache.flink.runtime.operators.sort.UnilateralSortMerger$ThreadBase.run(UnilateralSortMerger.java:781)
- | Caused by: java.lang.IllegalStateException: The runtime context has
- | not been initialized.
- | at org.apache.flink.api.common.functions.AbstractRichFunction.getRuntimeContext(AbstractRichFunction.java:49)
- | at org.apache.flink.api.scala.operators.ScalaAggregateOperator$AggregatingUdf.open(ScalaAggregateOperator.java:261)
- | at org.apache.flink.api.common.functions.util.FunctionUtils.openFunction(FunctionUtils.java:33)
- | at org.apache.flink.runtime.operators.sort.CombiningUnilateralSortMerger$CombiningSpillingThread.go(CombiningUnilateralSortMerger.java:261)
- | ... 1 more
- |
- | at org.apache.flink.client.program.Client.run(Client.java:345)
- | at org.apache.flink.client.program.Client.run(Client.java:304)
- | at org.apache.flink.client.program.Client.run(Client.java:298)
- | at org.apache.flink.client.program.ContextEnvironment.execute(ContextEnvironment.java:55)
- | at org.apache.flink.api.scala.ExecutionEnvironment.execute(ExecutionEnvironment.scala:530)
- | at com.gmp.MyJob$.main(MyJob.scala:33)
- | at com.gmp.MyJob.main(MyJob.scala)
- | at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- | at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
- | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- | at java.lang.reflect.Method.invoke(Method.java:483)
- | at org.apache.flink.client.program.PackagedProgram.callMainMethod(PackagedProgram.java:437)
- | at org.apache.flink.client.program.PackagedProgram.invokeInteractiveModeForExecution(PackagedProgram.java:353)
- | at org.apache.flink.client.program.Client.run(Client.java:250)
- | at org.apache.flink.client.CliFrontend.executeProgram(CliFrontend.java:374)
- | at org.apache.flink.client.CliFrontend.run(CliFrontend.java:347)
- | at org.apache.flink.client.CliFrontend.parseParameters(CliFrontend.java:1088)
- | at org.apache.flink.client.CliFrontend.main(CliFrontend.java:1115)
+.panel.panel-default.panel-multi(ng-repeat="exception in exceptions['all-exceptions']" )
+ .panel-heading.clearfix
+ .panel-title
+ | {{ exception.task }}
+
+ .panel-heading.clearfix
+ .panel-info.thin.last
+ span {{ exception.location }}
+
+ .panel-body
+ pre.exception
+ {{ exception.exception }}
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
index 07e7402..083e921 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
@@ -47,7 +47,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main-additional(ng-if="job")
li(ui-sref-active='active')
a(ui-sref=".statistics") Job Accumulators / Statistics
- li(ui-sref-active='active')
+ li(ui-sref-active='active' ng-if="job['end-time'] > -1")
a(ui-sref=".timeline") Timeline
li(ui-sref-active='active')
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/index.coffee b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
index 3d01ef5..d7db7e0 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
@@ -117,6 +117,7 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
views:
details:
templateUrl: "partials/jobs/job.exceptions.html"
+ controller: 'JobExceptionsController'
.state "single-job.config",
url: "/config"
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
index 1fc8eae..d6ffdb0 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
@@ -72,3 +72,10 @@ angular.module('flinkApp')
.controller 'JobTimelineVertexController', ($scope, $state, $stateParams, JobsService) ->
JobsService.getVertex($stateParams.vertexId).then (data) ->
$scope.vertex = data
+
+# --------------------------------------
+
+.controller 'JobExceptionsController', ($scope, $state, $stateParams, JobsService) ->
+ JobsService.loadExceptions().then (data) ->
+ $scope.exceptions = data
+
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index cc5c29b..6624b4f 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -111,13 +111,9 @@ angular.module('flinkApp')
.success (jobConfig) ->
data = angular.extend(data, jobConfig)
- $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/exceptions"
- .success (exceptions) ->
- data.exceptions = exceptions
+ currentJob = data
- currentJob = data
-
- deferreds.job.resolve(data)
+ deferreds.job.resolve(data)
deferreds.job.promise
@@ -162,7 +158,6 @@ angular.module('flinkApp')
return null
-
@getVertex = (vertexid) ->
deferred = $q.defer()
@@ -177,4 +172,17 @@ angular.module('flinkApp')
deferred.promise
+ @loadExceptions = ->
+ deferred = $q.defer()
+
+ $q.all([deferreds.job.promise]).then (data) =>
+
+ $http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/exceptions"
+ .success (exceptions) ->
+ currentJob.exceptions = exceptions
+
+ deferred.resolve(exceptions)
+
+ deferred.promise
+
@
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/app/styles/index.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/index.styl b/flink-runtime-web/web-dashboard/app/styles/index.styl
index d23d8c5..608dc3a 100644
--- a/flink-runtime-web/web-dashboard/app/styles/index.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/index.styl
@@ -163,6 +163,10 @@ sidebar-width = 250px
.panel.panel-multi
.panel-heading
padding: 0
+
+ .panel-info
+ &.thin
+ padding: 8px 10px
.panel-body
padding: 10px
@@ -249,6 +253,12 @@ sidebar-width = 250px
color: #d9534f
+pre.exception
+ border: none
+ background-color: transparent
+ padding: 0
+ margin: 0
+
@import './job'
@import './graph'
@import './timeline'
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/web/css/index.css
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/css/index.css b/flink-runtime-web/web-dashboard/web/css/index.css
index 854a619..438ff1b 100644
--- a/flink-runtime-web/web-dashboard/web/css/index.css
+++ b/flink-runtime-web/web-dashboard/web/css/index.css
@@ -209,6 +209,9 @@
.panel.panel-multi .panel-heading {
padding: 0;
}
+.panel.panel-multi .panel-heading .panel-info.thin {
+ padding: 8px 10px;
+}
.panel.panel-multi .panel-body {
padding: 10px;
background-color: #fdfdfd;
@@ -291,6 +294,12 @@
.indicator.indicator-danger {
color: #d9534f;
}
+pre.exception {
+ border: none;
+ background-color: transparent;
+ padding: 0;
+ margin: 0;
+}
.canvas-wrapper {
border: 1px solid #ddd;
position: relative;
[06/51] [abbrv] flink git commit: [FLINK-2547] [web dashboard]
Updated web dashboard after request/response changes
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/562573df/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
index 8584e63..69bf8d9 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
@@ -39,11 +39,6 @@ limitations under the License.
</tr>
</thead>
<tbody>
- <tr colspan="2">
- <td>
- <center><i>WARNING! This is a sample</i></center>
- </td>
- </tr>
<tr ng-repeat="vertex in node.vertex.groupvertex.groupmembers">
<td>{{vertex.vertexname}}</td>
<td>
@@ -56,27 +51,27 @@ limitations under the License.
</div>
<div ng-if="node" class="row">
<div class="col-sm-6 col-md-4">
- <table ng-if="node.global_properties" class="table table-properties">
+ <table ng-if="node.optimizer_properties.global_properties" class="table table-properties">
<thead>
<tr>
<th colspan="2">Global Data Properties</th>
</tr>
</thead>
<tbody>
- <tr ng-repeat="property in node.global_properties">
+ <tr ng-repeat="property in node.optimizer_properties.global_properties">
<td>{{property.name}}</td>
<td table-property="table-property" value="property.value"></td>
</tr>
</tbody>
</table>
- <table ng-if="node.local_properties" class="table table-properties">
+ <table ng-if="node.optimizer_properties.local_properties" class="table table-properties">
<thead>
<tr>
<th colspan="2">Local Data Properties</th>
</tr>
</thead>
<tbody>
- <tr ng-repeat="property in node.local_properties">
+ <tr ng-repeat="property in node.optimizer_properties.local_properties">
<td>{{property.name}}</td>
<td table-property="table-property" value="property.value"></td>
</tr>
@@ -120,14 +115,14 @@ limitations under the License.
</tr>
</tbody>
</table>
- <table ng-if="node.estimates" class="table table-properties">
+ <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
<thead>
<tr>
<th colspan="2">Size Estimates</th>
</tr>
</thead>
<tbody>
- <tr ng-repeat="property in node.estimates">
+ <tr ng-repeat="property in node.optimizer_properties.estimates">
<td>{{property.name}}</td>
<td table-property="table-property" value="property.value"></td>
</tr>
@@ -136,28 +131,28 @@ limitations under the License.
</div>
<div class="col-sm-6 col-md-4">
<div class="visible-xs visible-sm">
- <table ng-if="node.estimates" class="table table-properties">
+ <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
<thead>
<tr>
<th colspan="2">Size Estimates</th>
</tr>
</thead>
<tbody>
- <tr ng-repeat="property in node.estimates">
+ <tr ng-repeat="property in node.optimizer_properties.estimates">
<td>{{property.name}}</td>
<td table-property="table-property" value="property.value"></td>
</tr>
</tbody>
</table>
</div>
- <table ng-if="node.costs" class="table table-properties">
+ <table ng-if="node.optimizer_properties.costs" class="table table-properties">
<thead>
<tr>
<th colspan="2">Cost Estimates</th>
</tr>
</thead>
<tbody>
- <tr ng-repeat="property in node.costs">
+ <tr ng-repeat="property in node.optimizer_properties.costs">
<td>{{property.name}}</td>
<td table-property="table-property" value="property.value"></td>
</tr>
[26/51] [abbrv] flink git commit: [FLINK-2415] [monitoring api] Add
vertex details request handler, unify IDs between vertices and plan
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java
index 999ca1b..2d6af6f 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java
@@ -18,12 +18,15 @@
package org.apache.flink.runtime.executiongraph;
+import org.apache.flink.api.common.accumulators.Accumulator;
+import org.apache.flink.api.common.accumulators.LongCounter;
import org.apache.flink.api.common.io.StrictlyLocalAssignment;
import org.apache.flink.core.io.InputSplit;
import org.apache.flink.core.io.InputSplitAssigner;
import org.apache.flink.core.io.InputSplitSource;
import org.apache.flink.core.io.LocatableInputSplit;
import org.apache.flink.runtime.JobException;
+import org.apache.flink.runtime.accumulators.AccumulatorRegistry;
import org.apache.flink.runtime.execution.ExecutionState;
import org.apache.flink.runtime.instance.Instance;
import org.apache.flink.runtime.jobgraph.JobVertex;
@@ -490,6 +493,45 @@ public class ExecutionJobVertex implements Serializable {
}
}
+ // --------------------------------------------------------------------------------------------
+ // Accumulators / Metrics
+ // --------------------------------------------------------------------------------------------
+
+ public Map<AccumulatorRegistry.Metric, Accumulator<?, ?>> getAggregatedMetricAccumulators() {
+ // some specialized code to speed things up
+ long bytesRead = 0;
+ long bytesWritten = 0;
+ long recordsRead = 0;
+ long recordsWritten = 0;
+
+ for (ExecutionVertex v : getTaskVertices()) {
+ Map<AccumulatorRegistry.Metric, Accumulator<?, ?>> metrics = v.getCurrentExecutionAttempt().getFlinkAccumulators();
+
+ if (metrics != null) {
+ LongCounter br = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_BYTES_IN);
+ LongCounter bw = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_BYTES_OUT);
+ LongCounter rr = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_RECORDS_IN);
+ LongCounter rw = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_RECORDS_OUT);
+
+ bytesRead += br != null ? br.getLocalValuePrimitive() : 0;
+ bytesWritten += bw != null ? bw.getLocalValuePrimitive() : 0;
+ recordsRead += rr != null ? rr.getLocalValuePrimitive() : 0;
+ recordsWritten += rw != null ? rw.getLocalValuePrimitive() : 0;
+ }
+ }
+
+ HashMap<AccumulatorRegistry.Metric, Accumulator<?, ?>> agg = new HashMap<>();
+ agg.put(AccumulatorRegistry.Metric.NUM_BYTES_IN, new LongCounter(bytesRead));
+ agg.put(AccumulatorRegistry.Metric.NUM_BYTES_OUT, new LongCounter(bytesWritten));
+ agg.put(AccumulatorRegistry.Metric.NUM_RECORDS_IN, new LongCounter(recordsRead));
+ agg.put(AccumulatorRegistry.Metric.NUM_RECORDS_OUT, new LongCounter(recordsWritten));
+ return agg;
+ }
+
+ // --------------------------------------------------------------------------------------------
+ // Static / pre-assigned input splits
+ // --------------------------------------------------------------------------------------------
+
private List<LocatableInputSplit>[] computeLocalInputSplitsPerTask(InputSplit[] splits) throws JobException {
final int numSubTasks = getParallelism();
@@ -593,10 +635,10 @@ public class ExecutionJobVertex implements Serializable {
return subTaskSplitAssignment;
}
- //---------------------------------------------------------------------------------------------
- // Predetermined InputSplitAssigner
- //---------------------------------------------------------------------------------------------
+ /**
+ * An InputSplitAssigner that assigns to pre-determined hosts.
+ */
public static class PredeterminedInputSplitAssigner implements InputSplitAssigner {
private List<LocatableInputSplit>[] inputSplitsPerSubtask;
@@ -604,7 +646,7 @@ public class ExecutionJobVertex implements Serializable {
@SuppressWarnings("unchecked")
public PredeterminedInputSplitAssigner(List<LocatableInputSplit>[] inputSplitsPerSubtask) {
// copy input split assignment
- this.inputSplitsPerSubtask = (List<LocatableInputSplit>[]) new List[inputSplitsPerSubtask.length];
+ this.inputSplitsPerSubtask = (List<LocatableInputSplit>[]) new List<?>[inputSplitsPerSubtask.length];
for (int i = 0; i < inputSplitsPerSubtask.length; i++) {
List<LocatableInputSplit> next = inputSplitsPerSubtask[i];
http://git-wip-us.apache.org/repos/asf/flink/blob/b26ce5a3/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java b/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
index 2e50ca4..41ad2dc 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
@@ -59,4 +59,26 @@ public class WebMonitorUtils {
started, finished, status, lastChanged,
countsPerStatus, numTotalTasks);
}
+
+ public static void aggregateExecutionStateTimestamps(long[] timestamps, long[] other) {
+ timestamps[CREATED_POS] = Math.min(timestamps[CREATED_POS], other[CREATED_POS]);
+ timestamps[SCHEDULED_POS] = Math.min(timestamps[SCHEDULED_POS], other[SCHEDULED_POS]);
+ timestamps[DEPLOYING_POS] = Math.min(timestamps[DEPLOYING_POS], other[DEPLOYING_POS]);
+ timestamps[RUNNING_POS] = Math.min(timestamps[RUNNING_POS], other[RUNNING_POS]);
+ timestamps[FINISHED_POS] = Math.max(timestamps[FINISHED_POS], other[FINISHED_POS]);
+ timestamps[CANCELING_POS] = Math.min(timestamps[CANCELING_POS], other[CANCELING_POS]);
+ timestamps[CANCELED_POS] = Math.max(timestamps[CANCELED_POS], other[CANCELED_POS]);
+ timestamps[FAILED_POS] = Math.min(timestamps[FAILED_POS], other[FAILED_POS]);
+ }
+
+ // ------------------------------------------------------------------------
+
+ private static final int CREATED_POS = ExecutionState.CREATED.ordinal();
+ private static final int SCHEDULED_POS = ExecutionState.SCHEDULED.ordinal();
+ private static final int DEPLOYING_POS = ExecutionState.DEPLOYING.ordinal();
+ private static final int RUNNING_POS = ExecutionState.RUNNING.ordinal();
+ private static final int FINISHED_POS = ExecutionState.FINISHED.ordinal();
+ private static final int CANCELING_POS = ExecutionState.CANCELING.ordinal();
+ private static final int CANCELED_POS = ExecutionState.CANCELED.ordinal();
+ private static final int FAILED_POS = ExecutionState.FAILED.ordinal();
}
[33/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] New
node organization
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html
index f243143..34ad7a9 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html
@@ -20,4 +20,4 @@ limitations under the License.
<div class="canvas-wrapper">
<div job-plan="job-plan" plan="plan" jobid="{{jobid}}" class="main-canvas"></div>
</div>
-<div ui-view="node"></div>
\ No newline at end of file
+<div ui-view="node-tabs"></div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
new file mode 100644
index 0000000..94a9930
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
@@ -0,0 +1,57 @@
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<table class="table table-hover table-clickable table-activable">
+ <thead>
+ <tr>
+ <th>Start Time</th>
+ <th>End Time</th>
+ <th>Duration</th>
+ <th>Name</th>
+ <th>Bytes read</th>
+ <th>Records read</th>
+ <th>Bytes written</th>
+ <th>Records written</th>
+ <th>Tasks</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" id="vertex-row-{{v.id}}" ui-sref="{nodeid: v.id}">
+ <td>{{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ <div ui-view="node"></div>
+ </td>
+ <td>{{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</td>
+ <td>{{ v.duration }} ms</td>
+ <td>{{ v.name | humanizeTaskName }}</td>
+ <td>{{ v.metrics['read-bytes'] }}</td>
+ <td>{{ v.metrics['read-records'] }}</td>
+ <td>{{ v.metrics['write-bytes'] }}</td>
+ <td>{{ v.metrics['write-records'] }}</td>
+ <td>
+ <div class="label-group">
+ <bs-label status="{{status}}" ng-repeat="(index, status) in stateList">{{v.tasks[status]}}</bs-label>
+ </div>
+ </td>
+ <td>
+ <bs-label status="{{v.status}}">{{v.status}}</bs-label>
+ </td>
+ </tr>
+ </tbody>
+</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-nav.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-nav.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-nav.html
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
new file mode 100644
index 0000000..3ca9c90
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
@@ -0,0 +1,10 @@
+
+<div ng-if="node" class="panel panel-default panel-multi">
+ <nav ng-if="job.currentNode" class="navbar navbar-default navbar-secondary-additional">
+ <ul class="nav nav-tabs">
+ <li ui-sref-active="active"><a ui-sref=".generic({nodeid: nodeid})">Subtasks</a></li>
+ <li ui-sref-active="active"><a ui-sref=".properties({nodeid: nodeid})">Properties</a></li>
+ </ul>
+ </nav>
+ <div ui-view="node-details" class="panel-body clean"></div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
index f2e5e7e..16ca93a 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
@@ -17,4 +17,4 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<h1>Accumulators</h1>
\ No newline at end of file
+<h3>Accumulators</h3>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
index 7592c65..1a1ed14 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
@@ -16,7 +16,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
-->
<div ng-if="node" class="row">
<div class="col-sm-6 col-md-4">
[42/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Show
plan (and optimizer properties) as a dedicated view
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index 0aa7882..d8ea8ad 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -76,6 +76,14 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
templateUrl: "partials/jobs/job.plan.node-list.html"
}
}
+ }).state("single-job.plan.node.accumulators", {
+ url: "/accumulators",
+ views: {
+ 'node-details': {
+ controller: 'JobPlanNodeListAccumulatorsController',
+ templateUrl: "partials/jobs/job.plan.node-list.html"
+ }
+ }
}).state("single-job.plan.node.properties", {
url: "/properties",
views: {
@@ -244,13 +252,28 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
}]).controller('JobPlanNodeTabsController', ["$scope", "$stateParams", "JobsService", function($scope, $stateParams, JobsService) {
console.log('JobPlanNodeTabsController');
$scope.nodeid = $stateParams.nodeid;
+ $scope.stateList = JobsService.stateList();
return JobsService.getNode($scope.nodeid).then(function(data) {
$scope.node = data;
return $scope.job.currentNode = data;
});
-}]).controller('JobPlanNodeListController', ["$scope", "$stateParams", "JobsService", function($scope, $stateParams, JobsService) {
+}]).controller('JobPlanNodeListController', ["$scope", "$stateParams", "JobsService", "$state", function($scope, $stateParams, JobsService, $state) {
console.log('JobPlanNodeListController');
- return $scope.stateList = JobsService.stateList();
+ $scope.gotoAccumulators = function(nodeid) {
+ return $state.go('^.accumulators', {
+ nodeid: nodeid
+ });
+ };
+ return JobsService.getSubtasks($stateParams.nodeid).then(function(data) {
+ return $scope.vertex = data;
+ });
+}]).controller('JobPlanNodeListAccumulatorsController', ["$scope", "$stateParams", "JobsService", "$state", function($scope, $stateParams, JobsService, $state) {
+ console.log('JobPlanNodeListAccumulatorsController');
+ return $scope.gotoGeneric = function(nodeid) {
+ return $state.go('^.generic', {
+ nodeid: nodeid
+ });
+ };
}]).controller('JobPlanNodePropertiesController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
return console.log('JobPlanNodePropertiesController');
}]).controller('JobPlanNodeAccumulatorsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
@@ -856,6 +879,21 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
})(this));
return deferred.promise;
};
+ this.getSubtasks = function(vertexid) {
+ var deferred;
+ deferred = $q.defer();
+ $q.all([deferreds.job.promise]).then((function(_this) {
+ return function(data) {
+ var vertex;
+ vertex = _this.seekVertex(vertexid);
+ return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid).success(function(data) {
+ vertex.st = data.subtasks;
+ return deferred.resolve(vertex);
+ });
+ };
+ })(this));
+ return deferred.promise;
+ };
this.loadExceptions = function() {
var deferred;
deferred = $q.defer();
@@ -911,4 +949,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsImNvbW1vbi9zZXJ2aWNlcy5jb2ZmZWUiLCJjb21tb24vc2VydmljZXMuanMiLCJtb2R1bGVzL2pvYnMvam9icy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuanMiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuZGlyLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLnN2Yy5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LmN0cmwuanMiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkEsUUFBUSxPQUFPLFlBQVksQ0FBQyxhQUFhLGtCQUl4QyxtQkFBSSxTQUFDLFlBQUQ7RUFDSCxXQUFXLGlCQUFpQjtFQ3JCNUIsT0RzQkEsV0FBVyxjQUFjLFdBQUE7SUFDdkIsV0FBVyxpQkFBaUIsQ0FBQyxXQUFXO0lDckJ4QyxPRHNCQSxXQUFXLGVBQWU7O0lBSTdCLE1BQU0sZUFBZTtFQUN
wQixXQUFXO0VBRVgsb0JBQW9CO0dBS3JCLCtEQUFJLFNBQUMsYUFBYSxhQUFhLGFBQWEsV0FBeEM7RUM1QkgsT0Q2QkEsWUFBWSxhQUFhLEtBQUssU0FBQyxRQUFEO0lBQzVCLFFBQVEsT0FBTyxhQUFhO0lBRTVCLFlBQVk7SUM3QlosT0QrQkEsVUFBVSxXQUFBO01DOUJSLE9EK0JBLFlBQVk7T0FDWixZQUFZOztJQUtqQixpQ0FBTyxTQUFDLHVCQUFEO0VDakNOLE9Ea0NBLHNCQUFzQjtJQUl2QixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sd0JBQ0w7SUFBQSxLQUFLO0lBQ0wsVUFBVTtJQUNWLE9BQ0U7TUFBQSxhQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLGdDQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxnQkFDRTtRQUFBLFlBQVk7UUFD
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
FXLEdBQUc7SUFFZCxHQUFHLElBQUksQ0FBQyxVQUFVLElBQUksVUFBVSxLQUFLLENBQUEsU0FBQSxPQUFBO01DYm5DLE9EYW1DLFNBQUMsTUFBRDtRQ1pqQyxPRGNGLE1BQU0sSUFBSSxZQUFZLFlBQVksV0FBVyxXQUFXLE1BQU0sZUFDN0QsUUFBUSxTQUFDLFlBQUQ7VUFDUCxXQUFXLGFBQWE7VUNkdEIsT0RnQkYsU0FBUyxRQUFROzs7T0FOZ0I7SUNOckMsT0RjQSxTQUFTOztFQ1pYLE9EY0E7O0FDWkY7QUNsS0EsUUFBUSxPQUFPLFlBRWQsV0FBVywrRkFBc0IsU0FBQyxRQUFRLGlCQUFpQixhQUFhLFdBQVcsYUFBbEQ7RUFDaEMsSUFBQTtFQUFBLE9BQU8sY0FBYyxXQUFBO0lBQ25CLE9BQU8sY0FBYyxZQUFZLFFBQVE7SUNsQnpDLE9EbUJBLE9BQU8sZUFBZSxZQUFZLFFBQVE7O0VBRTVDLFlBQVksaUJBQWlCLE9BQU87RUFDcEMsT0FBTyxJQUFJLFlBQVksV0FBQTtJQ2xCckIsT0RtQkEsWUFBWSxtQkFBbUIsT0FBTzs7RUFFeEMsT0FBTztFQUVQLGdCQUFnQixlQUFlLEtBQUssU0FBQyxNQUFEO0lDbkJsQyxPRG9CQSxPQUFPLFdBQVc7O0VBRXBCLFVBQVUsVUFBVSxXQUFBO0lDbkJsQixPRG9CQSxnQkFBZ0IsZUFBZSxLQUFLLFNBQUMsTUFBRDtNQ25CbEMsT0RvQkEsT0FBTyxXQUFXOztLQUNwQixZQUFZO0VDbEJkLE9Eb0JBLE9BQU8sSUFBSSxZQUFZLFdBQUE7SUNuQnJCLE9Eb0JBLFVBQVUsT0FBTzs7O0FDakJyQjtBQ0xBLFFBQVEsT0FBTyxZQUVkLFFBQVEsa0RBQW1CLFNBQUMsT0FBTyxhQUFhLElBQXJCO0V
BQzFCLElBQUE7RUFBQSxXQUFXO0VBRVgsS0FBQyxlQUFlLFdBQUE7SUFDZCxJQUFBO0lBQUEsV0FBVyxHQUFHO0lBRWQsTUFBTSxJQUFJLFlBQVksWUFBWSxhQUNqQyxRQUFRLFNBQUMsTUFBTSxRQUFRLFNBQVMsUUFBeEI7TUFDUCxXQUFXO01DcEJYLE9EcUJBLFNBQVMsUUFBUTs7SUNuQm5CLE9EcUJBLFNBQVM7O0VDbkJYLE9EcUJBOztBQ25CRiIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2Fi
bGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLnJ1biAoJHJvb3RTY29wZSkgLT5cbiAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9IGZhbHNlXG4gICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSAtPlxuICAgICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSAhJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZVxuICAgICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLnZhbHVlICdmbGlua0NvbmZpZycsIHtcbiAgam9iU2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDo4MDgxJ1xuIyAgam9iU2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDozMDAwL25ld
y1zZXJ2ZXInXG4gIFwicmVmcmVzaC1pbnRlcnZhbFwiOiAxMDAwMFxufVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKEpvYnNTZXJ2aWNlLCBNYWluU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkgLT5cbiAgTWFpblNlcnZpY2UubG9hZENvbmZpZygpLnRoZW4gKGNvbmZpZykgLT5cbiAgICBhbmd1bGFyLmV4dGVuZCBmbGlua0NvbmZpZywgY29uZmlnXG5cbiAgICBKb2JzU2VydmljZS5saXN0Sm9icygpXG5cbiAgICAkaW50ZXJ2YWwgLT5cbiAgICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKClcbiAgICAsIGZsaW5rQ29uZmlnW1wicmVmcmVzaC1pbnRlcnZhbFwiXVxuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbmZpZyAoJHVpVmlld1Njcm9sbFByb3ZpZGVyKSAtPlxuICAkdWlWaWV3U2Nyb2xsUHJvdmlkZXIudXNlQW5jaG9yU2Nyb2xsKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29uZmlnICgkc3RhdGVQcm92aWRlciwgJHVybFJvdXRlclByb3ZpZGVyKSAtPlxuICAkc3RhdGVQcm92aWRlci5zdGF0ZSBcIm92ZXJ2aWV3XCIsXG4gICAgdXJsOiBcIi9vdmVydmlld1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9vdmVydmlldy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udH
JvbGxlcidcblxuICAuc3RhdGUgXCJydW5uaW5nLWpvYnNcIixcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL3J1bm5pbmctam9icy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgXG4gIC5zdGF0ZSBcImNvbXBsZXRlZC1qb2JzXCIsXG4gICAgdXJsOiBcIi9jb21wbGV0ZWQtam9ic1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYlwiLFxuICAgIHVybDogXCIvam9icy97am9iaWR9XCJcbiAgICBhYnN0cmFjdDogdHJ1ZVxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdTaW5nbGVKb2JDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhblwiLFxuICAgIHVybDogXCJcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Db250cm9
sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhbi5ub2RlXCIsXG4gICAgdXJsOiBcIi92ZXJ0ZXgve25vZGVpZH1cIlxuICAgIGFic3RyYWN0OiB0cnVlXG4gICAgdmlld3M6XG4gICAgICAnbm9kZS10YWJzJzpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLXRhYnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZVRhYnNDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhbi5ub2RlLmdlbmVyaWNcIixcbiAgICB1cmw6IFwiXCJcbiAgICB2aWV3czpcbiAgICAgICdub2RlLWRldGFpbHMnOlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbk5vZGVMaXN0Q29udHJvbGxlcicgXG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS1saXN0Lmh0bWxcIlxuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhbi5ub2RlLnByb3BlcnRpZXNcIixcbiAgICB1cmw6IFwiL3Byb3BlcnRpZXNcIlxuICAgIHZpZXdzOlxuICAgICAgJ25vZGUtZGV0YWlscyc6XG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZVByb3BlcnRpZXNDb250cm9sbGVyJ1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUucHJvcGVydGllcy5odG1sXCJcblxuICAjIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhbi5ub2RlLmFjY3VtdWxhdG9yc1wiLFxuICAjICAgdXJs
OiBcIi9hY2N1bXVsYXRvcnNcIlxuICAjICAgdmlld3M6XG4gICMgICAgICdub2RlLWNvbnRlbnRzJzpcbiAgIyAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbk5vZGVBY2N1bXVsYXRvcnNDb250cm9sbGVyJ1xuICAjICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5hY2N1bXVsYXRvcnMuaHRtbFwiXG5cbiAgIyAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4ubm9kZVwiLFxuICAjICAgdXJsOiBcIi92ZXJ0ZXgve25vZGVpZH1cIlxuICAjICAgYWJzdHJhY3Q6IHRydWVcbiAgIyAgIHZpZXdzOlxuICAjICAgICBub2RlOlxuICAjICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5odG1sXCJcbiAgIyAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbk5vZGVDb250cm9sbGVyJ1xuXG4gICMgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm5vZGUuc3VidGFza3NcIixcbiAgIyAgIHVybDogXCJcIlxuICAjICAgdmlld3M6XG4gICMgICAgIG5vZGVUYWI6XG4gICMgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLnN1YnRhc2tzLmh0bWxcIlxuXG4gICMgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm5vZGUucHJvcGVydGllc1wiLFxuICAjICAgdXJsOiBcIi9wcm9wZXJ0aWVzXCJcbiAgIyAgIHZpZXdzOlxuICAjICAgICBub2RlVGFiOlxuICAjICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iL
nBsYW4ubm9kZS5wcm9wZXJ0aWVzLmh0bWxcIlxuXG4gICMgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm5vZGUuYWNjdW11bGF0b3JzXCIsXG4gICMgICB1cmw6IFwiL3Byb3BlcnRpZXNcIlxuICAjICAgdmlld3M6XG4gICMgICAgIG5vZGVUYWI6XG4gICMgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLmFjY3VtdWxhdG9ycy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lXCIsXG4gICAgdXJsOiBcIi90aW1lbGluZVwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLFxuICAgIHVybDogXCIve3ZlcnRleElkfVwiXG4gICAgdmlld3M6XG4gICAgICB2ZXJ0ZXg6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLnZlcnRleC5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIixcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2Iuc3RhdGlzdGljcy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbG
Utam9iLmV4Y2VwdGlvbnNcIixcbiAgICB1cmw6IFwiL2V4Y2VwdGlvbnNcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYkV4Y2VwdGlvbnNDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IuY29uZmlnXCIsXG4gICAgdXJsOiBcIi9jb25maWdcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuY29uZmlnLmh0bWxcIlxuXG4gICR1cmxSb3V0ZXJQcm92aWRlci5vdGhlcndpc2UgXCIvb3ZlcnZpZXdcIlxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJywgWyd1aS5yb3V0ZXInLCAnYW5ndWxhck1vbWVudCddKS5ydW4oZnVuY3Rpb24oJHJvb3RTY29wZSkge1xuICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gZmFsc2U7XG4gIHJldHVybiAkcm9vdFNjb3BlLnNob3dTaWRlYmFyID0gZnVuY3Rpb24oKSB7XG4gICAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9ICEkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlO1xuICAgIHJldHVybiAkcm9vdFNjb3BlLnNpZGViYXJDbGFzcyA9ICdmb3JjZS1zaG93JztcbiAgfTtcbn0pLnZhbHVlKCdmbGlua0NvbmZpZycsIHtcbiAgam9iU2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDo4MDgxJyxcbiAgXCJyZWZyZXNoLWludGV
ydmFsXCI6IDEwMDAwXG59KS5ydW4oZnVuY3Rpb24oSm9ic1NlcnZpY2UsIE1haW5TZXJ2aWNlLCBmbGlua0NvbmZpZywgJGludGVydmFsKSB7XG4gIHJldHVybiBNYWluU2VydmljZS5sb2FkQ29uZmlnKCkudGhlbihmdW5jdGlvbihjb25maWcpIHtcbiAgICBhbmd1bGFyLmV4dGVuZChmbGlua0NvbmZpZywgY29uZmlnKTtcbiAgICBKb2JzU2VydmljZS5saXN0Sm9icygpO1xuICAgIHJldHVybiAkaW50ZXJ2YWwoZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gSm9ic1NlcnZpY2UubGlzdEpvYnMoKTtcbiAgICB9LCBmbGlua0NvbmZpZ1tcInJlZnJlc2gtaW50ZXJ2YWxcIl0pO1xuICB9KTtcbn0pLmNvbmZpZyhmdW5jdGlvbigkdWlWaWV3U2Nyb2xsUHJvdmlkZXIpIHtcbiAgcmV0dXJuICR1aVZpZXdTY3JvbGxQcm92aWRlci51c2VBbmNob3JTY3JvbGwoKTtcbn0pLmNvbmZpZyhmdW5jdGlvbigkc3RhdGVQcm92aWRlciwgJHVybFJvdXRlclByb3ZpZGVyKSB7XG4gICRzdGF0ZVByb3ZpZGVyLnN0YXRlKFwib3ZlcnZpZXdcIiwge1xuICAgIHVybDogXCIvb3ZlcnZpZXdcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9vdmVydmlldy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdPdmVydmlld0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInJ1bm5pbmctam9ic1wiLCB7XG4gICAgdXJsOiBcIi9ydW5uaW5nLWpv
YnNcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL3J1bm5pbmctam9icy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcImNvbXBsZXRlZC1qb2JzXCIsIHtcbiAgICB1cmw6IFwiL2NvbXBsZXRlZC1qb2JzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9jb21wbGV0ZWQtam9icy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYlwiLCB7XG4gICAgdXJsOiBcIi9qb2JzL3tqb2JpZH1cIixcbiAgICBhYnN0cmFjdDogdHJ1ZSxcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdTaW5nbGVKb2JDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnBsYW5cIiwge1xuICAgIHVybDogXCJcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlc
jogJ0pvYlBsYW5Db250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnBsYW4ubm9kZVwiLCB7XG4gICAgdXJsOiBcIi92ZXJ0ZXgve25vZGVpZH1cIixcbiAgICBhYnN0cmFjdDogdHJ1ZSxcbiAgICB2aWV3czoge1xuICAgICAgJ25vZGUtdGFicyc6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLXRhYnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbk5vZGVUYWJzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuLm5vZGUuZ2VuZXJpY1wiLCB7XG4gICAgdXJsOiBcIlwiLFxuICAgIHZpZXdzOiB7XG4gICAgICAnbm9kZS1kZXRhaWxzJzoge1xuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbk5vZGVMaXN0Q29udHJvbGxlcicsXG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS1saXN0Lmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnBsYW4ubm9kZS5wcm9wZXJ0aWVzXCIsIHtcbiAgICB1cmw6IFwiL3Byb3BlcnRpZXNcIixcbiAgICB2aWV3czoge1xuICAgICAgJ25vZGUtZGV0YWlscyc6IHtcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Ob2RlUHJvcGVydGllc0NvbnRyb2xsZXInLFxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbG
FuLm5vZGUucHJvcGVydGllcy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi50aW1lbGluZVwiLCB7XG4gICAgdXJsOiBcIi90aW1lbGluZVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7XG4gICAgdXJsOiBcIi97dmVydGV4SWR9XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIHZlcnRleDoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS52ZXJ0ZXguaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIiwge1xuICAgIHVybDogXCIvc3RhdGlzdGljc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnN0YXRpc3RpY3MuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IuZXhjZXB0aW9uc1wiLCB7XG4gICAgdXJsOiBcIi9leGNlcHRpb25zXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFw
icGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JFeGNlcHRpb25zQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5jb25maWdcIiwge1xuICAgIHVybDogXCIvY29uZmlnXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuY29uZmlnLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIHJldHVybiAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlKFwiL292ZXJ2aWV3XCIpO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0
aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2JzTGFiZWwnLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHRyYW5zY2x1ZGU6IHRydWVcbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6IFwiQFwiXG5cbiAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknPjxuZy10cmFuc2NsdWRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCJcbiAgXG4gIGxpbms6IChzY29wZSwgZWxlb
WVudCwgYXR0cnMpIC0+XG4gICAgc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IC0+XG4gICAgICAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnaW5kaWNhdG9yUHJpbWFyeScsIChKb2JzU2VydmljZSkgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6ICdAJ1xuXG4gIHRlbXBsYXRlOiBcIjxpIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJyAvPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2ZhIGZhLWNpcmNsZSBpbmRpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3RhYmxlUHJvcGVydHknLCAtPlxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOlxuICAgIHZhbHVlOiAnPSdcblxuICB0ZW1wbGF0ZTogXCI8dGQgdGl0bGU9XFxcInt7dmFsdWUgfHwgJ05vbmUnfX1cXFwiPnt7dmFsdWUgfHwgJ05vbmUnfX08L3RkPlwiXG4iLCJhbmd1bGFyLm1vZH
VsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ2JzTGFiZWwnLCBmdW5jdGlvbihKb2JzU2VydmljZSkge1xuICByZXR1cm4ge1xuICAgIHRyYW5zY2x1ZGU6IHRydWUsXG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6IFwiQFwiXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8c3BhbiB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKSc+PG5nLXRyYW5zY2x1ZGU+PC9uZy10cmFuc2NsdWRlPjwvc3Bhbj5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdpbmRpY2F0b3JQcmltYXJ5JywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICBnZXRMYWJlbENsYXNzOiBcIiZcIixcbiAgICAgIHN0YXR1czogJ0AnXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8aSB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIixcbiAgICBsaW5rOiBmdW5jdGlvbih
zY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnZmEgZmEtY2lyY2xlIGluZGljYXRvciBpbmRpY2F0b3ItJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0YWJsZVByb3BlcnR5JywgZnVuY3Rpb24oKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgdmFsdWU6ICc9J1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlz
IGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmZpbHRlciBcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCAoYW5ndWxhck1vbWVudENvbmZpZykgLT5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSAtPlxuICAgIHJldHVybiBcIlwiICBpZiB0eXBlb2YgdmFsdWUgaXMgXCJ1bmRlZmluZWRcIiBvciB2YWx1ZSBpcyBudWxsXG5cbiAgICBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9yb
WF0KGR1cmF0aW9uRm9ybWF0LCB7IHRyaW06IGZhbHNlIH0pXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyXG5cbi5maWx0ZXIgXCJodW1hbml6ZVRhc2tOYW1lXCIsIC0+XG4gICh0ZXh0KSAtPlxuICAgICMgVE9ETzogZXh0ZW5kLi4uIGEgbG90XG4gICAgaWYgdGV4dCB0aGVuIHRleHQucmVwbGFjZShcIiZndDtcIiwgXCI+XCIpLnJlcGxhY2UoXCI8YnIvPlwiLFwiXCIpIGVsc2UgJydcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmZpbHRlcihcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCBmdW5jdGlvbihhbmd1bGFyTW9tZW50Q29uZmlnKSB7XG4gIHZhciBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXI7XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlciA9IGZ1bmN0aW9uKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJ1bmRlZmluZWRcIiB8fCB2YWx1ZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxuICAgIHJldHVybiBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7XG4gICAgICB0cmltOiBmYWxzZVxuICAgIH0pO1xuICB9O1xuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZW
RGaWx0ZXIuJHN0YXRlZnVsID0gYW5ndWxhck1vbWVudENvbmZpZy5zdGF0ZWZ1bEZpbHRlcnM7XG4gIHJldHVybiBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXI7XG59KS5maWx0ZXIoXCJodW1hbml6ZVRhc2tOYW1lXCIsIGZ1bmN0aW9uKCkge1xuICByZXR1cm4gZnVuY3Rpb24odGV4dCkge1xuICAgIGlmICh0ZXh0KSB7XG4gICAgICByZXR1cm4gdGV4dC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIikucmVwbGFjZShcIjxici8+XCIsIFwiXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gJyc7XG4gICAgfVxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICA
gaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLnNlcnZpY2UgJ01haW5TZXJ2aWNlJywgKCRodHRwLCBmbGlua0NvbmZpZywgJHEpIC0+XG4gIGpvYk9ic2VydmVycyA9IFtdXG5cbiAgQGxvYWRDb25maWcgPSAtPlxuICAgIGRlZmVycmVkID0gJHEuZGVmZXIoKVxuXG4gICAgJGh0dHAuZ2V0IGZsaW5rQ29uZmlnLmpvYlNlcnZlciArIFwiL2NvbmZpZ1wiXG4gICAgLnN1Y2Nlc3MgKGRhdGEsIHN0YXR1cywgaGVhZGVycywgY29uZmlnKSAtPlxuICAgICAgZGVmZXJyZWQucmVzb2x2ZShkYXRhKVxuXG4gICAgZGVmZXJyZWQucHJvbWlzZVxuXG5cbiAgQFxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuc2VydmljZSgnTWFpblNlcnZpY2UnLCBm
dW5jdGlvbigkaHR0cCwgZmxpbmtDb25maWcsICRxKSB7XG4gIHZhciBqb2JPYnNlcnZlcnM7XG4gIGpvYk9ic2VydmVycyA9IFtdO1xuICB0aGlzLmxvYWRDb25maWcgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgZGVmZXJyZWQ7XG4gICAgZGVmZXJyZWQgPSAkcS5kZWZlcigpO1xuICAgICRodHRwLmdldChmbGlua0NvbmZpZy5qb2JTZXJ2ZXIgKyBcIi9jb25maWdcIikuc3VjY2VzcyhmdW5jdGlvbihkYXRhLCBzdGF0dXMsIGhlYWRlcnMsIGNvbmZpZykge1xuICAgICAgcmV0dXJuIGRlZmVycmVkLnJlc29sdmUoZGF0YSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIGRlZmVycmVkLnByb21pc2U7XG4gIH07XG4gIHJldHVybiB0aGlzO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZ
W5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmNvbnRyb2xsZXIgJ1J1bm5pbmdKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ3J1bm5pbmcnKVxuXG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuICAkc2NvcGUuJG9uICckZGVzdHJveScsIC0+XG4gICAgSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS
5qb2JPYnNlcnZlcilcblxuICAkc2NvcGUuam9iT2JzZXJ2ZXIoKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnU2luZ2xlSm9iQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHJvb3RTY29wZSkgLT5cbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkXG4gICRyb290U2NvcGUuam9iID0gbnVsbFxuICAkcm9vdFNjb3BlLnBsYW4gPSBudWxsXG5cbiAgSm9ic1NlcnZpY2UubG9hZEpvYigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHJvb3RTY29wZS5qb2IgPSBkYXRhXG4gICAgJHJvb3RTY29wZS5
wbGFuID0gZGF0YS5wbGFuXG5cbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgICRyb290U2NvcGUuam9iID0gbnVsbFxuICAgICRyb290U2NvcGUucGxhbiA9IG51bGxcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbkNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIGNvbnNvbGUubG9nICdKb2JQbGFuQ29udHJvbGxlcidcblxuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbk5vZGVDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlLCAkYW5jaG9yU2Nyb2xsKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iUGxhbk5vZGVDb250cm9sbGVyJ1xuXG4gICRzY29wZS5ub2RlaWQgPSAkc3RhdGVQYXJhbXMubm9kZWlkXG5cbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpXG5cbiAgSm9ic1NlcnZpY2UuZ2V0Tm9kZSgkc2NvcGUubm9kZWlkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS5ub2RlID0gZGF0YVxuICAgICRzY29wZS5qb2IuY3VycmVudE5vZGUgPSBkYXRhXG5cbiAgICAjIGFuY2hvciA9IFwidmVydGV4LXJvdy1cIiArIGRhdGEuaWRcbiAg
ICAjIGNvbnNvbGUubG9nIGFuY2hvclxuICAgICMgJGFuY2hvclNjcm9sbChhbmNob3IpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Ob2RlVGFic0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iUGxhbk5vZGVUYWJzQ29udHJvbGxlcidcblxuICAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZFxuXG4gIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUubm9kZSA9IGRhdGFcbiAgICAkc2NvcGUuam9iLmN1cnJlbnROb2RlID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQbGFuTm9kZUxpc3RDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgY29uc29sZS5sb2cgJ0pvYlBsYW5Ob2RlTGlzdENvbnRyb2xsZXInXG5cbiAgIyAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZFxuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbk5vZGVQcm9wZXJ0aWVzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJ
HN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgY29uc29sZS5sb2cgJ0pvYlBsYW5Ob2RlUHJvcGVydGllc0NvbnRyb2xsZXInXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Ob2RlQWNjdW11bGF0b3JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgY29uc29sZS5sb2cgJ0pvYlBsYW5Ob2RlQWNjdW11bGF0b3JzQ29udHJvbGxlcidcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBKb2JzU2VydmljZS5nZXRWZXJ0ZXgoJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS52ZXJ0ZXggPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYkV4Y2VwdGlvbnNDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBKb2JzU2VydmljZS5sb2FkRXhjZXB0aW9ucygpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLmV4Y2VwdGlvbnMgPSBkYXRhXG5cbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmNvbnRyb2
xsZXIoJ1J1bm5pbmdKb2JzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ3J1bm5pbmcnKTtcbiAgfTtcbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgfSk7XG4gIHJldHVybiAkc2NvcGUuam9iT2JzZXJ2ZXIoKTtcbn0pLmNvbnRyb2xsZXIoJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygnZmluaXNoZWQnKTtcbiAgfTtcbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgfSk7XG4gIHJldHV
ybiAkc2NvcGUuam9iT2JzZXJ2ZXIoKTtcbn0pLmNvbnRyb2xsZXIoJ1NpbmdsZUpvYkNvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHJvb3RTY29wZSkge1xuICAkc2NvcGUuam9iaWQgPSAkc3RhdGVQYXJhbXMuam9iaWQ7XG4gICRyb290U2NvcGUuam9iID0gbnVsbDtcbiAgJHJvb3RTY29wZS5wbGFuID0gbnVsbDtcbiAgSm9ic1NlcnZpY2UubG9hZEpvYigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICRyb290U2NvcGUuam9iID0gZGF0YTtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5wbGFuID0gZGF0YS5wbGFuO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgJHJvb3RTY29wZS5qb2IgPSBudWxsO1xuICAgIHJldHVybiAkcm9vdFNjb3BlLnBsYW4gPSBudWxsO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Db250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgY29uc29sZS5sb2coJ0pvYlBsYW5Db250cm9sbGVyJyk7XG4gIHJldHVybiAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KCk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuTm9kZUNvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2Vydmlj
ZSwgJGFuY2hvclNjcm9sbCkge1xuICBjb25zb2xlLmxvZygnSm9iUGxhbk5vZGVDb250cm9sbGVyJyk7XG4gICRzY29wZS5ub2RlaWQgPSAkc3RhdGVQYXJhbXMubm9kZWlkO1xuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KCk7XG4gIHJldHVybiBKb2JzU2VydmljZS5nZXROb2RlKCRzY29wZS5ub2RlaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICRzY29wZS5ub2RlID0gZGF0YTtcbiAgICByZXR1cm4gJHNjb3BlLmpvYi5jdXJyZW50Tm9kZSA9IGRhdGE7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUGxhbk5vZGVUYWJzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICBjb25zb2xlLmxvZygnSm9iUGxhbk5vZGVUYWJzQ29udHJvbGxlcicpO1xuICAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZDtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgJHNjb3BlLm5vZGUgPSBkYXRhO1xuICAgIHJldHVybiAkc2NvcGUuam9iLmN1cnJlbnROb2RlID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuTm9kZUxpc3RDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIGNvbnNvbGUubG9nKCdKb2JQbGFuTm9kZUxpc3RDb250cm9sbGVyJyk7XG4gIHJld
HVybiAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KCk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuTm9kZVByb3BlcnRpZXNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIGNvbnNvbGUubG9nKCdKb2JQbGFuTm9kZVByb3BlcnRpZXNDb250cm9sbGVyJyk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuTm9kZUFjY3VtdWxhdG9yc0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gY29uc29sZS5sb2coJ0pvYlBsYW5Ob2RlQWNjdW11bGF0b3JzQ29udHJvbGxlcicpO1xufSkuY29udHJvbGxlcignSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldFZlcnRleCgkc3RhdGVQYXJhbXMudmVydGV4SWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkc2NvcGUudmVydGV4ID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JFeGNlcHRpb25zQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiBKb2JzU2VydmljZS5sb2FkRXhjZXB0aW9ucygpLnRoZW4oZnVuY3Rpb24oZGF0YS
kge1xuICAgIHJldHVybiAkc2NvcGUuZXhjZXB0aW9ucyA9IGRhdGE7XG4gIH0pO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3I
gaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3ZlcnRleCcsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCJcblxuICBzY29wZTpcbiAgICBkYXRhOiBcIj1cIlxuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF1cblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVylcblxuICAgIGFuYWx5emVUaW1lID0gKGRhdGEpIC0+XG4gICAgICB0ZXN0RGF0YSA9IFtdXG5cbiAgICAgIGFuZ3VsYXIuZm9yRWFjaCBkYXRhLnN1YnRhc2tzLCAoc3VidGFzaywgaSkgLT5cbiAgICAgICAgdGVzdERhdGEucHVzaCB7XG4gICAgICAgICAgbGFiZWw6IFwiI3tzdWJ0YXNrLmhvc3R9ICgje3N1YnRhc2suc3VidGFza30pXCJcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJT
Y2hlZHVsZWRcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjNjY2XCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlNDSEVEVUxFRFwiXVxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiREVQTE9ZSU5HXCJdXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJEZXBsb3lpbmdcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjYWFhXCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkRFUExPWUlOR1wiXVxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiUlVOTklOR1wiXVxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6IFwiUnVubmluZ1wiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiNkZGRcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiUlVOTklOR1wiXVxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc
3VidGFzay50aW1lc3RhbXBzW1wiRklOSVNIRURcIl1cbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICB9XG4gICAgICAgICAgXVxuICAgICAgICB9XG5cbiAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpXG4gICAgICAudGlja0Zvcm1hdCh7XG4gICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKVxuICAgICAgICAjIHRpY2tJbnRlcnZhbDogMVxuICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJzaW5nbGVcIilcbiAgICAgIC5sYWJlbEZvcm1hdCgobGFiZWwpIC0+XG4gICAgICAgIGxhYmVsXG4gICAgICApXG4gICAgICAubWFyZ2luKHsgbGVmdDogMTAwLCByaWdodDogMCwgdG9wOiAwLCBib3R0b206IDAgfSlcbiAgICAgIC5pdGVtSGVpZ2h0KDMwKVxuICAgICAgLnJlbGF0aXZlVGltZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgYW5hbHl6ZVRpbWUoc2NvcGUuZGF0YSlcblxuICAgIHJldHVyblxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAndGltZWxpbmUnLCAoJHN0YXRlKSAtPlxuICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCJcblxuICBzY29wZTpcbiAgICBqb2I6IF
wiPVwiXG5cbiAgbGluazogKHNjb3BlLCBlbGVtLCBhdHRycykgLT5cbiAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKVxuXG4gICAgdHJhbnNsYXRlTGFiZWwgPSAobGFiZWwpIC0+XG4gICAgICBsYWJlbC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIilcblxuICAgIGFuYWx5emVUaW1lID0gKGRhdGEpIC0+XG4gICAgICB0ZXN0RGF0YSA9IFtdXG5cbiAgICAgIHRlc3REYXRhLnB1c2ggXG4gICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCJcbiAgICAgICAgICBjb2xvcjogXCIjY2NjY2NjXCJcbiAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICBzdGFydGluZ190aW1lOiBkYXRhLnRpbWVzdGFtcHNbXCJDUkVBVEVEXCJdXG4gICAgICAgICAgZW5kaW5nX3RpbWU6IGRhdGEudGltZXN0YW1wc1tcIkNSRUFURURcIl0gKyAxXG4gICAgICAgICAgdHlwZTogJ3NjaGVkdWxlZCdcbiAgICAgICAgXVxuXG5cbiAgICAgIGFuZ3VsYXIuZm9yRWFjaCBkYXRhLnZlcnRpY2VzLCAodmVydGV4KSAtPlxuICAgICAgICBpZiB2ZXJ0ZXhbJ3N0YXJ0LXRpbWUnXSA+IC0xXG4gICAgICAgICAgdGVzdERhdGEucHVzaCBcbiAgICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICAgIGxhYmVsOiB0cmFuc2xhdGVMYWJ
lbCh2ZXJ0ZXgubmFtZSlcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2Q5ZjFmN1wiXG4gICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM2MmNkZWFcIlxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2ZXJ0ZXhbJ3N0YXJ0LXRpbWUnXVxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdmVydGV4WydlbmQtdGltZSddXG4gICAgICAgICAgICAgIGxpbms6IHZlcnRleC5pZFxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIF1cblxuICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKCkuY2xpY2soKGQsIGksIGRhdHVtKSAtPlxuICAgICAgICBpZiBkLmxpbmtcbiAgICAgICAgICAkc3RhdGUuZ28gXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7IGpvYmlkOiBkYXRhLmppZCwgdmVydGV4SWQ6IGQubGluayB9XG5cbiAgICAgIClcbiAgICAgIC50aWNrRm9ybWF0KHtcbiAgICAgICAgZm9ybWF0OiBkMy50aW1lLmZvcm1hdChcIiVMXCIpXG4gICAgICAgICMgdGlja1RpbWU6IGQzLnRpbWUuc2Vjb25kXG4gICAgICAgICMgdGlja0ludGVydmFsOiAwLjVcbiAgICAgICAgdGlja1NpemU6IDFcbiAgICAgIH0pXG4gICAgICAucHJlZml4KFwibWFpblwiKVxuICAgICAgLm1hcmdpbih7IGxlZnQ6IDAsIHJpZ2h0OiAwLCB0b3A6IDAsIGJvdHRvbTogMCB9KVxuICAgICAgLml0ZW1IZWlnaHQoMzApXG4gICAgICAuc2hvd0JvcmRlckxpbmUoKVxuICAgICAgLnNob3dI
b3VyVGltZWxpbmUoKVxuXG4gICAgICBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpXG4gICAgICAuZGF0dW0odGVzdERhdGEpXG4gICAgICAuY2FsbChjaGFydClcblxuICAgIHNjb3BlLiR3YXRjaCBhdHRycy5qb2IsIChkYXRhKSAtPlxuICAgICAgYW5hbHl6ZVRpbWUoZGF0YSkgaWYgZGF0YVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICdqb2JQbGFuJywgKCR0aW1lb3V0KSAtPlxuICB0ZW1wbGF0ZTogXCJcbiAgICA8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+XG4gICAgPHN2ZyBjbGFzcz0ndG1wJyB3aWR0aD0nMScgaGVpZ2h0PScxJz48ZyAvPjwvc3ZnPlxuICAgIDxkaXYgY2xhc3M9J2J0bi1ncm91cCB6b29tLWJ1dHRvbnMnPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLWluJyBuZy1jbGljaz0nem9vbUluKCknPjxpIGNsYXNzPSdmYSBmYS1wbHVzJyAvPjwvYT5cbiAgICAgIDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+XG4gICAgPC9kaXY+XCJcblxuICBzY29wZTpcbiAgICBwbGFuOiAnPSdcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIG1haW5ab29tID0gZDMuYmVoYXZpb3Iuem9vbSgpXG4gICAgc3ViZ
3JhcGhzID0gW11cbiAgICBqb2JpZCA9IGF0dHJzLmpvYmlkXG5cbiAgICBtYWluU3ZnRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuICAgIG1haW5HID0gZWxlbS5jaGlsZHJlbigpLmNoaWxkcmVuKClbMF1cbiAgICBtYWluVG1wRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVsxXVxuXG4gICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1haW5TdmdFbGVtZW50KVxuICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpXG4gICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpXG5cbiAgICAjIGFuZ3VsYXIuZWxlbWVudChtYWluRykuZW1wdHkoKVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpXG5cbiAgICBzY29wZS56b29tSW4gPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKSA8IDIuOTlcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIHpvb20gb2JqZWN0XG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW
5ab29tLnNjYWxlIG1haW5ab29tLnNjYWxlKCkgKyAwLjFcbiAgICAgICAgbWFpblpvb20udHJhbnNsYXRlIFsgdjEsIHYyIF1cbiAgICAgICAgXG4gICAgICAgICMgVHJhbnNmb3JtIHN2Z1xuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCJcblxuICAgIHNjb3BlLnpvb21PdXQgPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKSA+IDAuMzFcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIG1haW5ab29tIG9iamVjdFxuICAgICAgICBtYWluWm9vbS5zY2FsZSBtYWluWm9vbS5zY2FsZSgpIC0gMC4xXG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZSBbIHYxLCB2MiBdXG4gICAgICAgIFxuICAgICAgICAjIFRyYW5zZm9ybSBzdmdcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHN
jYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiXG5cbiAgICAjY3JlYXRlIGEgbGFiZWwgb2YgYW4gZWRnZVxuICAgIGNyZWF0ZUxhYmVsRWRnZSA9IChlbCkgLT5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIlwiXG4gICAgICBpZiBlbC5zaGlwX3N0cmF0ZWd5PyBvciBlbC5sb2NhbF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxkaXYgY2xhc3M9J2VkZ2UtbGFiZWwnPlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneSAgaWYgZWwuc2hpcF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiAoXCIgKyBlbC50ZW1wX21vZGUgKyBcIilcIiAgdW5sZXNzIGVsLnRlbXBfbW9kZSBpcyBgdW5kZWZpbmVkYFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiLDxicj5cIiArIGVsLmxvY2FsX3N0cmF0ZWd5ICB1bmxlc3MgZWwubG9jYWxfc3RyYXRlZ3kgaXMgYHVuZGVmaW5lZGBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiXG4gICAgICBsYWJlbFZhbHVlXG5cblxuICAgICMgdHJ1ZSwgaWYgdGhlIG5vZGUgaXMgYSBzcGVjaWFsIG5vZGUgZnJvbSBhbiBpdGVyYXRpb25cbiAgICBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlID0gKGluZm8pIC0+XG4gICAgICAoaW5mbyBpcyBcInBhcnRpYWxTb2x1dGlvblwiIG9yIGluZm8gaXMgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIgb3IgaW5mbyBpcyBcIndvcmtzZXRcIiBvciBpbmZvIGlzIFwibmV4dFdvcmtzZXRc
IiBvciBpbmZvIGlzIFwic29sdXRpb25TZXRcIiBvciBpbmZvIGlzIFwic29sdXRpb25EZWx0YVwiKVxuXG4gICAgZ2V0Tm9kZVR5cGUgPSAoZWwsIGluZm8pIC0+XG4gICAgICBpZiBpbmZvIGlzIFwibWlycm9yXCJcbiAgICAgICAgJ25vZGUtbWlycm9yJ1xuXG4gICAgICBlbHNlIGlmIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbylcbiAgICAgICAgJ25vZGUtaXRlcmF0aW9uJ1xuXG4gICAgICBlbHNlXG4gICAgICAgICAgJ25vZGUtbm9ybWFsJ1xuICAgICAgXG4gICAgIyBjcmVhdGVzIHRoZSBsYWJlbCBvZiBhIG5vZGUsIGluIGluZm8gaXMgc3RvcmVkLCB3aGV0aGVyIGl0IGlzIGEgc3BlY2lhbCBub2RlIChsaWtlIGEgbWlycm9yIGluIGFuIGl0ZXJhdGlvbilcbiAgICBjcmVhdGVMYWJlbE5vZGUgPSAoZWwsIGluZm8sIG1heFcsIG1heEgpIC0+XG4gICAgICBsYWJlbFZhbHVlID0gXCI8YSBocmVmPScjL2pvYnMvXCIgKyBqb2JpZCArIFwiL3ZlcnRleC9cIiArIGVsLmlkICsgXCInIGNsYXNzPSdub2RlLWxhYmVsIFwiICsgZ2V0Tm9kZVR5cGUoZWwsIGluZm8pICsgXCInPlwiXG5cbiAgICAgICMgTm9kZW5hbWVcbiAgICAgIGlmIGluZm8gaXMgXCJtaXJyb3JcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPk1pcnJvciBvZiBcIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiXG4gICAgICBlbHNlXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZ
GUtbmFtZSc+XCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIlxuICAgICAgaWYgZWwuZGVzY3JpcHRpb24gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiXCJcbiAgICAgIGVsc2VcbiAgICAgICAgc3RlcE5hbWUgPSBlbC5kZXNjcmlwdGlvblxuICAgICAgICBcbiAgICAgICAgIyBjbGVhbiBzdGVwTmFtZVxuICAgICAgICBzdGVwTmFtZSA9IHNob3J0ZW5TdHJpbmcoc3RlcE5hbWUpXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDQgY2xhc3M9J3N0ZXAtbmFtZSc+XCIgKyBzdGVwTmFtZSArIFwiPC9oND5cIlxuICAgICAgXG4gICAgICAjIElmIHRoaXMgbm9kZSBpcyBhbiBcIml0ZXJhdGlvblwiIHdlIG5lZWQgYSBkaWZmZXJlbnQgcGFuZWwtYm9keVxuICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvbj9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24oZWwuaWQsIG1heFcsIG1heEgpXG4gICAgICBlbHNlXG4gICAgICAgIFxuICAgICAgICAjIE90aGVyd2lzZSBhZGQgaW5mb3MgICAgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+XCIgKyBpbmZvICsgXCIgTm9kZTwvaDU+XCIgIGlmIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbylcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5QYXJhbGxlbGlzbTogXCIgKyBlbC5wYXJhbGxlbGlzbSArIFwiPC9oNT5cIiAgdW5sZXNzIGVsLnBhcmFsbGVsaXNtIGlzIFwiXCJcbiAgICAgICAgbGFiZWxWYWx1ZS
ArPSBcIjxoNT5PcGVyYXRpb246IFwiICsgc2hvcnRlblN0cmluZyhlbC5vcGVyYXRvcl9zdHJhdGVneSkgKyBcIjwvaDU+XCIgIHVubGVzcyBlbC5vcGVyYXRvciBpcyBgdW5kZWZpbmVkYFxuICAgICAgXG4gICAgICBsYWJlbFZhbHVlICs9IFwiPC9hPlwiXG4gICAgICBsYWJlbFZhbHVlXG5cbiAgICAjIEV4dGVuZHMgdGhlIGxhYmVsIG9mIGEgbm9kZSB3aXRoIGFuIGFkZGl0aW9uYWwgc3ZnIEVsZW1lbnQgdG8gcHJlc2VudCB0aGUgaXRlcmF0aW9uLlxuICAgIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiA9IChpZCwgbWF4VywgbWF4SCkgLT5cbiAgICAgIHN2Z0lEID0gXCJzdmctXCIgKyBpZFxuXG4gICAgICBsYWJlbFZhbHVlID0gXCI8c3ZnIGNsYXNzPSdcIiArIHN2Z0lEICsgXCInIHdpZHRoPVwiICsgbWF4VyArIFwiIGhlaWdodD1cIiArIG1heEggKyBcIj48ZyAvPjwvc3ZnPlwiXG4gICAgICBsYWJlbFZhbHVlXG5cbiAgICAjIFNwbGl0IGEgc3RyaW5nIGludG8gbXVsdGlwbGUgbGluZXMgc28gdGhhdCBlYWNoIGxpbmUgaGFzIGxlc3MgdGhhbiAzMCBsZXR0ZXJzLlxuICAgIHNob3J0ZW5TdHJpbmcgPSAocykgLT5cbiAgICAgICMgbWFrZSBzdXJlIHRoYXQgbmFtZSBkb2VzIG5vdCBjb250YWluIGEgPCAoYmVjYXVzZSBvZiBodG1sKVxuICAgICAgaWYgcy5jaGFyQXQoMCkgaXMgXCI8XCJcbiAgICAgICAgcyA9IHMucmVwbGFjZShcIjxcIiwgXCImbHQ7XCIpXG4gICAgICAgIHMgPSBzLnJlcGxhY2U
oXCI+XCIsIFwiJmd0O1wiKVxuICAgICAgc2JyID0gXCJcIlxuICAgICAgd2hpbGUgcy5sZW5ndGggPiAzMFxuICAgICAgICBzYnIgPSBzYnIgKyBzLnN1YnN0cmluZygwLCAzMCkgKyBcIjxicj5cIlxuICAgICAgICBzID0gcy5zdWJzdHJpbmcoMzAsIHMubGVuZ3RoKVxuICAgICAgc2JyID0gc2JyICsgc1xuICAgICAgc2JyXG5cbiAgICBjcmVhdGVOb2RlID0gKGcsIGRhdGEsIGVsLCBpc1BhcmVudCA9IGZhbHNlLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgIyBjcmVhdGUgbm9kZSwgc2VuZCBhZGRpdGlvbmFsIGluZm9ybWF0aW9ucyBhYm91dCB0aGUgbm9kZSBpZiBpdCBpcyBhIHNwZWNpYWwgb25lXG4gICAgICBpZiBlbC5pZCBpcyBkYXRhLnBhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5uZXh0X3BhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4g
ICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLndvcmtzZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwid29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIndvcmtzZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLm5leHRfd29ya3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0V29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRXb3Jrc2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5zb2x1dGlvbl9zZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25TZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvblNldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEuc29sdXRpb25fZGVsdGFcbiAgICAgI
CAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25EZWx0YVwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uRGVsdGFcIilcblxuICAgICAgZWxzZVxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJcIilcblxuICAgIGNyZWF0ZUVkZ2UgPSAoZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpIC0+XG4gICAgICB1bmxlc3MgZXhpc3RpbmdOb2Rlcy5pbmRleE9mKHByZWQuaWQpIGlzIC0xXG4gICAgICAgIGcuc2V0RWRnZSBwcmVkLmlkLCBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKHByZWQpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBhcnJvd2hlYWQ6ICdub3JtYWwnXG5cbiAgICAgIGVsc2VcbiAgICAgICAgbWlzc2luZ05vZGUgPSBzZWFyY2hGb3JOb2RlKGRhdGEsIHByZWQuaWQpXG4gICAgICAgIHVubGVzcyAhbWlzc2luZ05vZGUgb3IgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkIGlzIHRydWVcbiAgICAgICAgICBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgPS
B0cnVlXG4gICAgICAgICAgZy5zZXROb2RlIG1pc3NpbmdOb2RlLmlkLFxuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShtaXNzaW5nTm9kZSwgXCJtaXJyb3JcIilcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUobWlzc2luZ05vZGUsICdtaXJyb3InKVxuXG4gICAgICAgICAgZy5zZXRFZGdlIG1pc3NpbmdOb2RlLmlkLCBlbC5pZCxcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UobWlzc2luZ05vZGUpXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuXG4gICAgbG9hZEpzb25Ub0RhZ3JlID0gKGcsIGRhdGEpIC0+XG4gICAgICBleGlzdGluZ05vZGVzID0gW11cblxuICAgICAgaWYgZGF0YS5ub2Rlcz9cbiAgICAgICAgIyBUaGlzIGlzIHRoZSBub3JtYWwganNvbiBkYXRhXG4gICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEubm9kZXNcblxuICAgICAgZWxzZVxuICAgICAgICAjIFRoaXMgaXMgYW4gaXRlcmF0aW9uLCB3ZSBub3cgc3RvcmUgc3BlY2lhbCBpdGVyYXRpb24gbm9kZXMgaWYgcG9zc2libGVcbiAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5zdGVwX2Z1bmN0aW9uXG4gICAgICAgIGlzUGFyZW50ID0gdHJ1ZVxuXG4gICAgICBmb3IgZWwgaW4gdG9JdGVyYXRlXG4gICAgICAgIG1heFcgPSAwXG4gICAgICAgIG1heEggPSAwXG5cbiAgICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvblxuICAgICAgICAgIHNnID0
gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoeyBtdWx0aWdyYXBoOiB0cnVlLCBjb21wb3VuZDogdHJ1ZSB9KS5zZXRHcmFwaCh7XG4gICAgICAgICAgICBub2Rlc2VwOiAyMFxuICAgICAgICAgICAgZWRnZXNlcDogMFxuICAgICAgICAgICAgcmFua3NlcDogMjBcbiAgICAgICAgICAgIHJhbmtkaXI6IFwiTFJcIlxuICAgICAgICAgICAgbWFyZ2lueDogMTBcbiAgICAgICAgICAgIG1hcmdpbnk6IDEwXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgICAgc3ViZ3JhcGhzW2VsLmlkXSA9IHNnXG5cbiAgICAgICAgICBsb2FkSnNvblRvRGFncmUoc2csIGVsKVxuXG4gICAgICAgICAgciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpXG4gICAgICAgICAgZDN0bXBTdmcuc2VsZWN0KCdnJykuY2FsbChyLCBzZylcbiAgICAgICAgICBtYXhXID0gc2cuZ3JhcGgoKS53aWR0aFxuICAgICAgICAgIG1heEggPSBzZy5ncmFwaCgpLmhlaWdodFxuXG4gICAgICAgICAgYW5ndWxhci5lbGVtZW50KG1haW5UbXBFbGVtZW50KS5lbXB0eSgpXG5cbiAgICAgICAgY3JlYXRlTm9kZShnLCBkYXRhLCBlbCwgaXNQYXJlbnQsIG1heFcsIG1heEgpXG5cbiAgICAgICAgZXhpc3RpbmdOb2Rlcy5wdXNoIGVsLmlkXG4gICAgICAgIFxuICAgICAgICAjIGNyZWF0ZSBlZGdlcyBmcm9tIGlucHV0cyB0byBjdXJyZW50IG5vZGVcbiAgICAgICAgaWYgZWwuaW5wdXRzP1xuICAgICAgICAgIGZvciBwcmVkIGluIGVsLmlucHV0c1xuICAgICAgICAgICAg
Y3JlYXRlRWRnZShnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZClcblxuICAgICAgZ1xuXG4gICAgIyBzZWFyY2hlcyBpbiB0aGUgZ2xvYmFsIEpTT05EYXRhIGZvciB0aGUgbm9kZSB3aXRoIHRoZSBnaXZlbiBpZFxuICAgIHNlYXJjaEZvck5vZGUgPSAoZGF0YSwgbm9kZUlEKSAtPlxuICAgICAgZm9yIGkgb2YgZGF0YS5ub2Rlc1xuICAgICAgICBlbCA9IGRhdGEubm9kZXNbaV1cbiAgICAgICAgcmV0dXJuIGVsICBpZiBlbC5pZCBpcyBub2RlSURcbiAgICAgICAgXG4gICAgICAgICMgbG9vayBmb3Igbm9kZXMgdGhhdCBhcmUgaW4gaXRlcmF0aW9uc1xuICAgICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICAgIGZvciBqIG9mIGVsLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgICAgIHJldHVybiBlbC5zdGVwX2Z1bmN0aW9uW2pdICBpZiBlbC5zdGVwX2Z1bmN0aW9uW2pdLmlkIGlzIG5vZGVJRFxuXG4gICAgZHJhd0dyYXBoID0gKGRhdGEpIC0+XG4gICAgICBnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoeyBtdWx0aWdyYXBoOiB0cnVlLCBjb21wb3VuZDogdHJ1ZSB9KS5zZXRHcmFwaCh7XG4gICAgICAgIG5vZGVzZXA6IDcwXG4gICAgICAgIGVkZ2VzZXA6IDBcbiAgICAgICAgcmFua3NlcDogNTBcbiAgICAgICAgcmFua2RpcjogXCJMUlwiXG4gICAgICAgIG1hcmdpbng6IDQwXG4gICAgICAgIG1hcmdpbnk6IDQwXG4gICAgICAgIH0pXG5cbiAgICAgIGxvYWRKc29uVG9EYWdyZShnL
CBkYXRhKVxuXG4gICAgICByZW5kZXJlciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpXG4gICAgICBkM21haW5TdmdHLmNhbGwocmVuZGVyZXIsIGcpXG5cbiAgICAgIGZvciBpLCBzZyBvZiBzdWJncmFwaHNcbiAgICAgICAgZDNtYWluU3ZnLnNlbGVjdCgnc3ZnLnN2Zy0nICsgaSArICcgZycpLmNhbGwocmVuZGVyZXIsIHNnKVxuXG4gICAgICBuZXdTY2FsZSA9IDAuNVxuXG4gICAgICB4Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS53aWR0aCgpIC0gZy5ncmFwaCgpLndpZHRoICogbmV3U2NhbGUpIC8gMilcbiAgICAgIHlDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLmhlaWdodCgpIC0gZy5ncmFwaCgpLmhlaWdodCAqIG5ld1NjYWxlKSAvIDIpXG5cbiAgICAgIG1haW5ab29tLnNjYWxlKG5ld1NjYWxlKS50cmFuc2xhdGUoW3hDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXRdKVxuXG4gICAgICBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB4Q2VudGVyT2Zmc2V0ICsgXCIsIFwiICsgeUNlbnRlck9mZnNldCArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIilcblxuICAgICAgbWFpblpvb20ub24oXCJ6b29tXCIsIC0+XG4gICAgICAgIGV2ID0gZDMuZXZlbnRcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidH
JhbnNsYXRlKFwiICsgZXYudHJhbnNsYXRlICsgXCIpIHNjYWxlKFwiICsgZXYuc2NhbGUgKyBcIilcIlxuICAgICAgKVxuICAgICAgbWFpblpvb20oZDNtYWluU3ZnKVxuXG4gICAgc2NvcGUuJHdhdGNoIGF0dHJzLnBsYW4sIChuZXdQbGFuKSAtPlxuICAgICAgZHJhd0dyYXBoKG5ld1BsYW4pIGlmIG5ld1BsYW5cblxuICAgIHJldHVyblxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZGlyZWN0aXZlKCd2ZXJ0ZXgnLCBmdW5jdGlvbigkc3RhdGUpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZSBzZWNvbmRhcnknIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBkYXRhOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5lclcsIHN2Z0VsO1xuICAgICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpO1xuICAgICAgYW5hbHl6ZVRpbWUgPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIHZhciBjaGFydCwgc3ZnLCB0ZXN0RGF0YTtcbiAgICAgICAgdGVzdERhdGEgPSBbXTtcbiAgICAgICAgYW5ndWxhci5mb3JFYWNoKGRhdGEuc3VidGFza3MsIGZ1bmN
0aW9uKHN1YnRhc2ssIGkpIHtcbiAgICAgICAgICByZXR1cm4gdGVzdERhdGEucHVzaCh7XG4gICAgICAgICAgICBsYWJlbDogc3VidGFzay5ob3N0ICsgXCIgKFwiICsgc3VidGFzay5zdWJ0YXNrICsgXCIpXCIsXG4gICAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiIzY2NlwiLFxuICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJTQ0hFRFVMRURcIl0sXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkRFUExPWUlOR1wiXSxcbiAgICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgfSwge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIkRlcGxveWluZ1wiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiNhYWFcIixcbiAgICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiREVQTE9ZSU5HXCJdLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU5HXCJdLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAg
ICB9LCB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiUnVubmluZ1wiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiNkZGRcIixcbiAgICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiUlVOTklOR1wiXSxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiRklOSVNIRURcIl0sXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIF1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLnRpY2tGb3JtYXQoe1xuICAgICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKSxcbiAgICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgICB9KS5wcmVmaXgoXCJzaW5nbGVcIikubGFiZWxGb3JtYXQoZnVuY3Rpb24obGFiZWwpIHtcbiAgICAgICAgICByZXR1cm4gbGFiZWw7XG4gICAgICAgIH0pLm1hcmdpbih7XG4gICAgICAgICAgbGVmdDogMTAwLFxuICAgICAgICAgIHJpZ2h0OiAwLFxuICAgICAgICAgIHRvcDogMCxcbiAgICAgICAgICBib3R0b206IDBcbiAgICAgICAgfSkuaXRlbUhlaWdodCgzMCkucmVsYXRpdmVUaW1lKCk7XG4gICAgICAgIHJldHVybiBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpLmRhdHVtKHRlc3REYXRhKS5jY
WxsKGNoYXJ0KTtcbiAgICAgIH07XG4gICAgICBhbmFseXplVGltZShzY29wZS5kYXRhKTtcbiAgICB9XG4gIH07XG59KS5kaXJlY3RpdmUoJ3RpbWVsaW5lJywgZnVuY3Rpb24oJHN0YXRlKSB7XG4gIHJldHVybiB7XG4gICAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUnIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBqb2I6IFwiPVwiXG4gICAgfSxcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbSwgYXR0cnMpIHtcbiAgICAgIHZhciBhbmFseXplVGltZSwgY29udGFpbmVyVywgc3ZnRWwsIHRyYW5zbGF0ZUxhYmVsO1xuICAgICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpO1xuICAgICAgdHJhbnNsYXRlTGFiZWwgPSBmdW5jdGlvbihsYWJlbCkge1xuICAgICAgICByZXR1cm4gbGFiZWwucmVwbGFjZShcIiZndDtcIiwgXCI+XCIpO1xuICAgICAgfTtcbiAgICAgIGFuYWx5emVUaW1lID0gZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICB2YXIgY2hhcnQsIHN2ZywgdGVzdERhdGE7XG4gICAgICAgIHRlc3REYXRhID0gW107XG4gICAgICAgIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdW
xlZFwiLFxuICAgICAgICAgICAgICBjb2xvcjogXCIjY2NjY2NjXCIsXG4gICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIixcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogZGF0YS50aW1lc3RhbXBzW1wiQ1JFQVRFRFwiXSxcbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IGRhdGEudGltZXN0YW1wc1tcIkNSRUFURURcIl0gKyAxLFxuICAgICAgICAgICAgICB0eXBlOiAnc2NoZWR1bGVkJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgIF1cbiAgICAgICAgfSk7XG4gICAgICAgIGFuZ3VsYXIuZm9yRWFjaChkYXRhLnZlcnRpY2VzLCBmdW5jdGlvbih2ZXJ0ZXgpIHtcbiAgICAgICAgICBpZiAodmVydGV4WydzdGFydC10aW1lJ10gPiAtMSkge1xuICAgICAgICAgICAgcmV0dXJuIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIGxhYmVsOiB0cmFuc2xhdGVMYWJlbCh2ZXJ0ZXgubmFtZSksXG4gICAgICAgICAgICAgICAgICBjb2xvcjogXCIjZDlmMWY3XCIsXG4gICAgICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNjJjZGVhXCIsXG4gICAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2ZXJ0ZXhbJ3N0YXJ0LXRpbWUnXSxcbiAgICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2ZXJ0ZXhbJ2VuZC10aW1lJ10sXG4gICAgICAgICAgICAgICAgICBsaW5rOiB2ZXJ0ZXguaWQsXG4gICAgICA
gICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIF1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLmNsaWNrKGZ1bmN0aW9uKGQsIGksIGRhdHVtKSB7XG4gICAgICAgICAgaWYgKGQubGluaykge1xuICAgICAgICAgICAgcmV0dXJuICRzdGF0ZS5nbyhcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHtcbiAgICAgICAgICAgICAgam9iaWQ6IGRhdGEuamlkLFxuICAgICAgICAgICAgICB2ZXJ0ZXhJZDogZC5saW5rXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pLnRpY2tGb3JtYXQoe1xuICAgICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKSxcbiAgICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgICB9KS5wcmVmaXgoXCJtYWluXCIpLm1hcmdpbih7XG4gICAgICAgICAgbGVmdDogMCxcbiAgICAgICAgICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMzApLnNob3dCb3JkZXJMaW5lKCkuc2hvd0hvdXJUaW1lbGluZSgpO1xuICAgICAgICByZXR1cm4gc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCk7XG4gICAgICB9O1xuICAgICAgc2NvcGUuJHdhdGNoKGF0dHJzLmpvYiwgZnVuY3Rp
b24oZGF0YSkge1xuICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgIHJldHVybiBhbmFseXplVGltZShkYXRhKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdqb2JQbGFuJywgZnVuY3Rpb24oJHRpbWVvdXQpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+IDxzdmcgY2xhc3M9J3RtcCcgd2lkdGg9JzEnIGhlaWdodD0nMSc+PGcgLz48L3N2Zz4gPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1pbicgbmctY2xpY2s9J3pvb21JbigpJz48aSBjbGFzcz0nZmEgZmEtcGx1cycgLz48L2E+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+IDwvZGl2PlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBwbGFuOiAnPSdcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGNvbnRhaW5lclcsIGNyZWF0ZUVkZ2UsIGNyZWF0ZUxhYmVsRWRnZSwgY3JlYXRlTGFiZWxOb2RlLCBjcmVhdGVOb2RlLCBkM21haW5TdmcsIGQzbWFpblN2Z0csIGQzdG1wU3ZnLCBkcmF3R3JhcGgsIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiwgZ2V0Tm9kZVR5c
GUsIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUsIGpvYmlkLCBsb2FkSnNvblRvRGFncmUsIG1haW5HLCBtYWluU3ZnRWxlbWVudCwgbWFpblRtcEVsZW1lbnQsIG1haW5ab29tLCBzZWFyY2hGb3JOb2RlLCBzaG9ydGVuU3RyaW5nLCBzdWJncmFwaHM7XG4gICAgICBtYWluWm9vbSA9IGQzLmJlaGF2aW9yLnpvb20oKTtcbiAgICAgIHN1YmdyYXBocyA9IFtdO1xuICAgICAgam9iaWQgPSBhdHRycy5qb2JpZDtcbiAgICAgIG1haW5TdmdFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXTtcbiAgICAgIG1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdO1xuICAgICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1haW5TdmdFbGVtZW50KTtcbiAgICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpO1xuICAgICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpO1xuICAgICAgc2NvcGUuem9vbUluID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB0cmFuc2xhdGUsIHYxLCB2MjtcbiAgICAgICAgaWYgKG1haW5ab29tLnNjYWxlKCkgPCAyLjk5KSB7XG4gICAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKCk7XG4gICAgIC
AgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgbWFpblpvb20uc2NhbGUobWFpblpvb20uc2NhbGUoKSArIDAuMSk7XG4gICAgICAgICAgbWFpblpvb20udHJhbnNsYXRlKFt2MSwgdjJdKTtcbiAgICAgICAgICByZXR1cm4gZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIHNjb3BlLnpvb21PdXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHRyYW5zbGF0ZSwgdjEsIHYyO1xuICAgICAgICBpZiAobWFpblpvb20uc2NhbGUoKSA+IDAuMzEpIHtcbiAgICAgICAgICBtYWluWm9vbS5zY2FsZShtYWluWm9vbS5zY2FsZSgpIC0gMC4xKTtcbiAgICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKTtcbiAgICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTt
cbiAgICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUoW3YxLCB2Ml0pO1xuICAgICAgICAgIHJldHVybiBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlTGFiZWxFZGdlID0gZnVuY3Rpb24oZWwpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWU7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIlwiO1xuICAgICAgICBpZiAoKGVsLnNoaXBfc3RyYXRlZ3kgIT0gbnVsbCkgfHwgKGVsLmxvY2FsX3N0cmF0ZWd5ICE9IG51bGwpKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxkaXYgY2xhc3M9J2VkZ2UtbGFiZWwnPlwiO1xuICAgICAgICAgIGlmIChlbC5zaGlwX3N0cmF0ZWd5ICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLnRlbXBfbW9kZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiIChcIiArIGVsLnRlbXBfbW9kZSArIFwiKVwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwubG9jYWxfc3RyYXRlZ3kgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiw8YnI+XCIgKyBlbC5sb2NhbF9zdHJhdGVneTtcbiAgICAgICAgICB9
XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUgPSBmdW5jdGlvbihpbmZvKSB7XG4gICAgICAgIHJldHVybiBpbmZvID09PSBcInBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwibmV4dFBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwid29ya3NldFwiIHx8IGluZm8gPT09IFwibmV4dFdvcmtzZXRcIiB8fCBpbmZvID09PSBcInNvbHV0aW9uU2V0XCIgfHwgaW5mbyA9PT0gXCJzb2x1dGlvbkRlbHRhXCI7XG4gICAgICB9O1xuICAgICAgZ2V0Tm9kZVR5cGUgPSBmdW5jdGlvbihlbCwgaW5mbykge1xuICAgICAgICBpZiAoaW5mbyA9PT0gXCJtaXJyb3JcIikge1xuICAgICAgICAgIHJldHVybiAnbm9kZS1taXJyb3InO1xuICAgICAgICB9IGVsc2UgaWYgKGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbykpIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtaXRlcmF0aW9uJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtbm9ybWFsJztcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNyZWF0ZUxhYmVsTm9kZSA9IGZ1bmN0aW9uKGVsLCBpbmZvLCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIHZhciBsYWJlbFZhbHVlLCBzdGVwTmFtZTtcbiAgICAgICAgbGFiZWxWYWx1ZSA9IFwiPGEgaHJlZj0nIy9qb2JzL1wiICsga
m9iaWQgKyBcIi92ZXJ0ZXgvXCIgKyBlbC5pZCArIFwiJyBjbGFzcz0nbm9kZS1sYWJlbCBcIiArIGdldE5vZGVUeXBlKGVsLCBpbmZvKSArIFwiJz5cIjtcbiAgICAgICAgaWYgKGluZm8gPT09IFwibWlycm9yXCIpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPk1pcnJvciBvZiBcIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+XCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuZGVzY3JpcHRpb24gPT09IFwiXCIpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiXCI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc3RlcE5hbWUgPSBlbC5kZXNjcmlwdGlvbjtcbiAgICAgICAgICBzdGVwTmFtZSA9IHNob3J0ZW5TdHJpbmcoc3RlcE5hbWUpO1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDQgY2xhc3M9J3N0ZXAtbmFtZSc+XCIgKyBzdGVwTmFtZSArIFwiPC9oND5cIjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuc3RlcF9mdW5jdGlvbiAhPSBudWxsKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24oZWwuaWQsIG1heFcsIG1heEgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmIChpc1NwZWNpYWxJdGVyYXRpb2
5Ob2RlKGluZm8pKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlwiICsgaW5mbyArIFwiIE5vZGU8L2g1PlwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwucGFyYWxsZWxpc20gIT09IFwiXCIpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+UGFyYWxsZWxpc206IFwiICsgZWwucGFyYWxsZWxpc20gKyBcIjwvaDU+XCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5vcGVyYXRvciAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1Pk9wZXJhdGlvbjogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLm9wZXJhdG9yX3N0cmF0ZWd5KSArIFwiPC9oNT5cIjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvYT5cIjtcbiAgICAgICAgcmV0dXJuIGxhYmVsVmFsdWU7XG4gICAgICB9O1xuICAgICAgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uID0gZnVuY3Rpb24oaWQsIG1heFcsIG1heEgpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWUsIHN2Z0lEO1xuICAgICAgICBzdmdJRCA9IFwic3ZnLVwiICsgaWQ7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIjxzdmcgY2xhc3M9J1wiICsgc3ZnSUQgKyBcIicgd2lkdGg9XCIgKyBtYXhXICsgXCIgaGVpZ2h0PVwiICsgbWF4SCArIFwiPjxnIC8+PC9zdmc+XCI7XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIHNob3J
0ZW5TdHJpbmcgPSBmdW5jdGlvbihzKSB7XG4gICAgICAgIHZhciBzYnI7XG4gICAgICAgIGlmIChzLmNoYXJBdCgwKSA9PT0gXCI8XCIpIHtcbiAgICAgICAgICBzID0gcy5yZXBsYWNlKFwiPFwiLCBcIiZsdDtcIik7XG4gICAgICAgICAgcyA9IHMucmVwbGFjZShcIj5cIiwgXCImZ3Q7XCIpO1xuICAgICAgICB9XG4gICAgICAgIHNiciA9IFwiXCI7XG4gICAgICAgIHdoaWxlIChzLmxlbmd0aCA+IDMwKSB7XG4gICAgICAgICAgc2JyID0gc2JyICsgcy5zdWJzdHJpbmcoMCwgMzApICsgXCI8YnI+XCI7XG4gICAgICAgICAgcyA9IHMuc3Vic3RyaW5nKDMwLCBzLmxlbmd0aCk7XG4gICAgICAgIH1cbiAgICAgICAgc2JyID0gc2JyICsgcztcbiAgICAgICAgcmV0dXJuIHNicjtcbiAgICAgIH07XG4gICAgICBjcmVhdGVOb2RlID0gZnVuY3Rpb24oZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIGlmIChpc1BhcmVudCA9PSBudWxsKSB7XG4gICAgICAgICAgaXNQYXJlbnQgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuaWQgPT09IGRhdGEucGFydGlhbF9zb2x1dGlvbikge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6
IGdldE5vZGVUeXBlKGVsLCBcInBhcnRpYWxTb2x1d
<TRUNCATED>
[04/51] [abbrv] flink git commit: [FLINK-2415] [optimizer] Create and
attach proper JobGraph describing JSON plans to JobGraph for batch jobs.
Posted by se...@apache.org.
[FLINK-2415] [optimizer] Create and attach proper JobGraph describing JSON plans to JobGraph for batch jobs.
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/ff28981f
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/ff28981f
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/ff28981f
Branch: refs/heads/master
Commit: ff28981f97cc434fb00ff7c931004888a84ad6c6
Parents: b6f52df
Author: Stephan Ewen <se...@apache.org>
Authored: Tue Aug 18 11:04:48 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:49 2015 +0200
----------------------------------------------------------------------
.../operators/base/BulkIterationBase.java | 2 +-
.../operators/base/DeltaIterationBase.java | 6 +-
.../flink/optimizer/dag/BinaryUnionNode.java | 2 +-
.../flink/optimizer/dag/BulkIterationNode.java | 4 +-
.../optimizer/dag/BulkPartialSolutionNode.java | 2 +-
.../apache/flink/optimizer/dag/CoGroupNode.java | 2 +-
.../flink/optimizer/dag/CoGroupRawNode.java | 2 +-
.../flink/optimizer/dag/CollectorMapNode.java | 2 +-
.../apache/flink/optimizer/dag/CrossNode.java | 2 +-
.../flink/optimizer/dag/DagConnection.java | 4 +-
.../flink/optimizer/dag/DataSinkNode.java | 2 +-
.../flink/optimizer/dag/DataSourceNode.java | 2 +-
.../apache/flink/optimizer/dag/FilterNode.java | 2 +-
.../apache/flink/optimizer/dag/FlatMapNode.java | 2 +-
.../flink/optimizer/dag/GroupCombineNode.java | 2 +-
.../flink/optimizer/dag/GroupReduceNode.java | 17 +-
.../apache/flink/optimizer/dag/JoinNode.java | 2 +-
.../org/apache/flink/optimizer/dag/MapNode.java | 2 +-
.../flink/optimizer/dag/MapPartitionNode.java | 2 +-
.../apache/flink/optimizer/dag/MatchNode.java | 2 +-
.../flink/optimizer/dag/OptimizerNode.java | 4 +-
.../flink/optimizer/dag/PartitionNode.java | 2 +-
.../apache/flink/optimizer/dag/ReduceNode.java | 2 +-
.../apache/flink/optimizer/dag/SinkJoiner.java | 2 +-
.../flink/optimizer/dag/SolutionSetNode.java | 2 +-
.../flink/optimizer/dag/SortPartitionNode.java | 2 +-
.../flink/optimizer/dag/UnaryOperatorNode.java | 2 +-
.../optimizer/dag/WorksetIterationNode.java | 6 +-
.../apache/flink/optimizer/dag/WorksetNode.java | 2 +-
.../apache/flink/optimizer/plan/PlanNode.java | 2 +-
.../flink/optimizer/plan/SinkPlanNode.java | 5 +-
.../flink/optimizer/plan/SourcePlanNode.java | 1 -
.../plandump/PlanJSONDumpGenerator.java | 16 +-
.../plantranslate/JobGraphGenerator.java | 227 +++++++++---
.../optimizer/plantranslate/JsonMapper.java | 299 ++++++++++++++++
.../app/partials/jobs/job.plan.node.jade | 23 +-
.../app/scripts/modules/jobs/jobs.dir.coffee | 21 +-
flink-runtime-web/web-dashboard/web/js/index.js | 74 ++--
.../web-dashboard/web/js/vendor.js | 352 +++++++------------
.../web/partials/jobs/job.plan.node.html | 22 +-
.../apache/flink/runtime/jobgraph/JobEdge.java | 74 +++-
.../apache/flink/runtime/jobgraph/JobGraph.java | 11 -
.../flink/runtime/jobgraph/JobVertex.java | 64 +++-
.../jobgraph/jsonplan/JsonPlanGenerator.java | 142 ++++++++
.../flink/runtime/operators/DriverStrategy.java | 8 +-
.../flink/runtime/jobmanager/JobManager.scala | 11 +-
.../jobgraph/jsonplan/JsonGeneratorTest.java | 119 +++++++
.../jsonplan/JsonJobGraphGenerationTest.java | 352 +++++++++++++++++++
48 files changed, 1489 insertions(+), 421 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-core/src/main/java/org/apache/flink/api/common/operators/base/BulkIterationBase.java
----------------------------------------------------------------------
diff --git a/flink-core/src/main/java/org/apache/flink/api/common/operators/base/BulkIterationBase.java b/flink-core/src/main/java/org/apache/flink/api/common/operators/base/BulkIterationBase.java
index 6304197..4fd4de6 100644
--- a/flink-core/src/main/java/org/apache/flink/api/common/operators/base/BulkIterationBase.java
+++ b/flink-core/src/main/java/org/apache/flink/api/common/operators/base/BulkIterationBase.java
@@ -50,7 +50,7 @@ import org.apache.flink.util.Visitor;
*/
public class BulkIterationBase<T> extends SingleInputOperator<T, T, AbstractRichFunction> implements IterationOperator {
- private static String DEFAULT_NAME = "<Unnamed Bulk Iteration>";
+ private static final String DEFAULT_NAME = "<Unnamed Bulk Iteration>";
public static final String TERMINATION_CRITERION_AGGREGATOR_NAME = "terminationCriterion.aggregator";
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-core/src/main/java/org/apache/flink/api/common/operators/base/DeltaIterationBase.java
----------------------------------------------------------------------
diff --git a/flink-core/src/main/java/org/apache/flink/api/common/operators/base/DeltaIterationBase.java b/flink-core/src/main/java/org/apache/flink/api/common/operators/base/DeltaIterationBase.java
index 2986534..9a7674c 100644
--- a/flink-core/src/main/java/org/apache/flink/api/common/operators/base/DeltaIterationBase.java
+++ b/flink-core/src/main/java/org/apache/flink/api/common/operators/base/DeltaIterationBase.java
@@ -84,7 +84,7 @@ public class DeltaIterationBase<ST, WT> extends DualInputOperator<ST, WT, ST, Ab
}
public DeltaIterationBase(BinaryOperatorInformation<ST, WT, ST> operatorInfo, int[] keyPositions) {
- this(operatorInfo, keyPositions, "<Unnamed Workset-Iteration>");
+ this(operatorInfo, keyPositions, "<Unnamed Delta Iteration>");
}
public DeltaIterationBase(BinaryOperatorInformation<ST, WT, ST> operatorInfo, int keyPosition, String name) {
@@ -283,7 +283,7 @@ public class DeltaIterationBase<ST, WT> extends DualInputOperator<ST, WT, ST, Ab
private final DeltaIterationBase<?, WT> containingIteration;
public WorksetPlaceHolder(DeltaIterationBase<?, WT> container, OperatorInformation<WT> operatorInfo) {
- super(operatorInfo, "Workset Place Holder");
+ super(operatorInfo, "Workset");
this.containingIteration = container;
}
@@ -312,7 +312,7 @@ public class DeltaIterationBase<ST, WT> extends DualInputOperator<ST, WT, ST, Ab
protected final DeltaIterationBase<ST, ?> containingIteration;
public SolutionSetPlaceHolder(DeltaIterationBase<ST, ?> container, OperatorInformation<ST> operatorInfo) {
- super(operatorInfo, "Solution Set Place Holder");
+ super(operatorInfo, "Solution Set");
this.containingIteration = container;
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BinaryUnionNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BinaryUnionNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BinaryUnionNode.java
index 1600a50..fdd76a8 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BinaryUnionNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BinaryUnionNode.java
@@ -54,7 +54,7 @@ public class BinaryUnionNode extends TwoInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Union";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkIterationNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkIterationNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkIterationNode.java
index 5dd868e..3d95c22 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkIterationNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkIterationNode.java
@@ -184,7 +184,7 @@ public class BulkIterationNode extends SingleInputNode implements IterationNode
// --------------------------------------------------------------------------------------------
@Override
- public String getName() {
+ public String getOperatorName() {
return "Bulk Iteration";
}
@@ -352,7 +352,7 @@ public class BulkIterationNode extends SingleInputNode implements IterationNode
// 5) Create a candidate for the Iteration Node for every remaining plan of the step function.
if (terminationCriterion == null) {
for (PlanNode candidate : candidates) {
- BulkIterationPlanNode node = new BulkIterationPlanNode(this, "BulkIteration ("+this.getOperator().getName()+")", in, pspn, candidate);
+ BulkIterationPlanNode node = new BulkIterationPlanNode(this, this.getOperator().getName(), in, pspn, candidate);
GlobalProperties gProps = candidate.getGlobalProperties().clone();
LocalProperties lProps = candidate.getLocalProperties().clone();
node.initProperties(gProps, lProps);
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkPartialSolutionNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkPartialSolutionNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkPartialSolutionNode.java
index 25a7eef..0a02dd1 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkPartialSolutionNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/BulkPartialSolutionNode.java
@@ -84,7 +84,7 @@ public class BulkPartialSolutionNode extends AbstractPartialSolutionNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Bulk Partial Solution";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupNode.java
index 20bad0d..58bce4b 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupNode.java
@@ -59,7 +59,7 @@ public class CoGroupNode extends TwoInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "CoGroup";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupRawNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupRawNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupRawNode.java
index 971d244..dcc3102 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupRawNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CoGroupRawNode.java
@@ -48,7 +48,7 @@ public class CoGroupRawNode extends TwoInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "CoGroup";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CollectorMapNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CollectorMapNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CollectorMapNode.java
index 93be1e4..9c1bcd3 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CollectorMapNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CollectorMapNode.java
@@ -42,7 +42,7 @@ public class CollectorMapNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Map";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CrossNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CrossNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CrossNode.java
index 8de67e8..622bfac 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CrossNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/CrossNode.java
@@ -104,7 +104,7 @@ public class CrossNode extends TwoInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Cross";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DagConnection.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DagConnection.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DagConnection.java
index 4e65976..1f98a11 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DagConnection.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DagConnection.java
@@ -267,7 +267,7 @@ public class DagConnection implements EstimateProvider, DumpableConnection<Optim
buf.append("null");
} else {
buf.append(this.source.getOperator().getName());
- buf.append('(').append(this.source.getName()).append(')');
+ buf.append('(').append(this.source.getOperatorName()).append(')');
}
buf.append(" -> ");
@@ -282,7 +282,7 @@ public class DagConnection implements EstimateProvider, DumpableConnection<Optim
buf.append("null");
} else {
buf.append(this.target.getOperator().getName());
- buf.append('(').append(this.target.getName()).append(')');
+ buf.append('(').append(this.target.getOperatorName()).append(')');
}
return buf.toString();
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSinkNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSinkNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSinkNode.java
index 6ca1149..b35613f 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSinkNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSinkNode.java
@@ -92,7 +92,7 @@ public class DataSinkNode extends OptimizerNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Data Sink";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSourceNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSourceNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSourceNode.java
index 6010f6a..867698d 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSourceNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/DataSourceNode.java
@@ -110,7 +110,7 @@ public class DataSourceNode extends OptimizerNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Data Source";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FilterNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FilterNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FilterNode.java
index 118ddc8..cb4e7bd 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FilterNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FilterNode.java
@@ -46,7 +46,7 @@ public class FilterNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Filter";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FlatMapNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FlatMapNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FlatMapNode.java
index f713d56..7dd84b2 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FlatMapNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/FlatMapNode.java
@@ -45,7 +45,7 @@ public class FlatMapNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "FlatMap";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupCombineNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupCombineNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupCombineNode.java
index 766d6af..7a1dd34 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupCombineNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupCombineNode.java
@@ -83,7 +83,7 @@ public class GroupCombineNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "GroupCombine";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupReduceNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupReduceNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupReduceNode.java
index 51da36b..bd118ec 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupReduceNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/GroupReduceNode.java
@@ -24,7 +24,6 @@ import java.util.List;
import org.apache.flink.api.common.functions.Partitioner;
import org.apache.flink.api.common.operators.Ordering;
import org.apache.flink.api.common.operators.SemanticProperties;
-import org.apache.flink.api.common.operators.SingleInputOperator;
import org.apache.flink.api.common.operators.SingleInputSemanticProperties;
import org.apache.flink.api.common.operators.base.GroupReduceOperatorBase;
import org.apache.flink.optimizer.CompilerException;
@@ -45,6 +44,8 @@ public class GroupReduceNode extends SingleInputNode {
private final List<OperatorDescriptorSingle> possibleProperties;
+ private final String operatorName;
+
private GroupReduceNode combinerUtilityNode;
/**
@@ -54,6 +55,7 @@ public class GroupReduceNode extends SingleInputNode {
*/
public GroupReduceNode(GroupReduceOperatorBase<?, ?, ?> operator) {
super(operator);
+ this.operatorName = "GroupReduce";
if (this.keys == null) {
// case of a key-less reducer. force a parallelism of 1
@@ -63,8 +65,9 @@ public class GroupReduceNode extends SingleInputNode {
this.possibleProperties = initPossibleProperties(operator.getCustomPartitioner());
}
- public GroupReduceNode(GroupReduceNode reducerToCopyForCombiner) {
+ private GroupReduceNode(GroupReduceNode reducerToCopyForCombiner) {
super(reducerToCopyForCombiner);
+ this.operatorName = "GroupCombine";
this.possibleProperties = Collections.emptyList();
}
@@ -95,7 +98,7 @@ public class GroupReduceNode extends SingleInputNode {
// check if we can work with a grouping (simple reducer), or if we need ordering because of a group order
Ordering groupOrder = null;
if (getOperator() instanceof GroupReduceOperatorBase) {
- groupOrder = ((GroupReduceOperatorBase<?, ?, ?>) getOperator()).getGroupOrder();
+ groupOrder = getOperator().getGroupOrder();
if (groupOrder != null && groupOrder.getNumberOfFields() == 0) {
groupOrder = null;
}
@@ -131,8 +134,8 @@ public class GroupReduceNode extends SingleInputNode {
}
@Override
- public String getName() {
- return "GroupReduce";
+ public String getOperatorName() {
+ return this.operatorName;
}
@Override
@@ -142,10 +145,8 @@ public class GroupReduceNode extends SingleInputNode {
@Override
protected SemanticProperties getSemanticPropertiesForLocalPropertyFiltering() {
-
// Local properties for GroupReduce may only be preserved on key fields.
- SingleInputSemanticProperties origProps =
- ((SingleInputOperator<?,?,?>) getOperator()).getSemanticProperties();
+ SingleInputSemanticProperties origProps = getOperator().getSemanticProperties();
SingleInputSemanticProperties filteredProps = new SingleInputSemanticProperties();
FieldSet readSet = origProps.getReadFields(0);
if(readSet != null) {
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/JoinNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/JoinNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/JoinNode.java
index cbd58ca..02c9b5b 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/JoinNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/JoinNode.java
@@ -67,7 +67,7 @@ public class JoinNode extends TwoInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Join";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapNode.java
index 35def59..afbf4a8 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapNode.java
@@ -45,7 +45,7 @@ public class MapNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Map";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapPartitionNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapPartitionNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapPartitionNode.java
index 6914c15..575dd63 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapPartitionNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MapPartitionNode.java
@@ -49,7 +49,7 @@ public class MapPartitionNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "MapPartition";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MatchNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MatchNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MatchNode.java
index de3cd22..ee8ab05 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MatchNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/MatchNode.java
@@ -63,7 +63,7 @@ public class MatchNode extends TwoInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Join";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/OptimizerNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/OptimizerNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/OptimizerNode.java
index 9688bb8..490c304 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/OptimizerNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/OptimizerNode.java
@@ -152,7 +152,7 @@ public abstract class OptimizerNode implements Visitable<OptimizerNode>, Estimat
*
* @return The node name.
*/
- public abstract String getName();
+ public abstract String getOperatorName();
/**
* This function connects the predecessors to this operator.
@@ -1119,7 +1119,7 @@ public abstract class OptimizerNode implements Visitable<OptimizerNode>, Estimat
public String toString() {
StringBuilder bld = new StringBuilder();
- bld.append(getName());
+ bld.append(getOperatorName());
bld.append(" (").append(getOperator().getName()).append(") ");
int i = 1;
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/PartitionNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/PartitionNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/PartitionNode.java
index 5c811b0..33383cb 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/PartitionNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/PartitionNode.java
@@ -59,7 +59,7 @@ public class PartitionNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Partition";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/ReduceNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/ReduceNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/ReduceNode.java
index 52bfb6a..ed010bb 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/ReduceNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/ReduceNode.java
@@ -67,7 +67,7 @@ public class ReduceNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Reduce";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SinkJoiner.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SinkJoiner.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SinkJoiner.java
index 06606f0..979d254 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SinkJoiner.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SinkJoiner.java
@@ -51,7 +51,7 @@ public class SinkJoiner extends TwoInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Internal Utility Node";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SolutionSetNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SolutionSetNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SolutionSetNode.java
index 1292cf5..9b53999 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SolutionSetNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SolutionSetNode.java
@@ -79,7 +79,7 @@ public class SolutionSetNode extends AbstractPartialSolutionNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Solution Set";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SortPartitionNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SortPartitionNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SortPartitionNode.java
index 83bc39a..8037533 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SortPartitionNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/SortPartitionNode.java
@@ -56,7 +56,7 @@ public class SortPartitionNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Sort-Partition";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/UnaryOperatorNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/UnaryOperatorNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/UnaryOperatorNode.java
index 45ecdac..0c48033 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/UnaryOperatorNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/UnaryOperatorNode.java
@@ -53,7 +53,7 @@ public class UnaryOperatorNode extends SingleInputNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return this.name;
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetIterationNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetIterationNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetIterationNode.java
index 99c868c..15b9a50 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetIterationNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetIterationNode.java
@@ -222,7 +222,7 @@ public class WorksetIterationNode extends TwoInputNode implements IterationNode
// --------------------------------------------------------------------------------------------
@Override
- public String getName() {
+ public String getOperatorName() {
return "Workset Iteration";
}
@@ -454,7 +454,7 @@ public class WorksetIterationNode extends TwoInputNode implements IterationNode
}
WorksetIterationPlanNode wsNode = new WorksetIterationPlanNode(this,
- "WorksetIteration ("+this.getOperator().getName()+")", solutionSetIn,
+ this.getOperator().getName(), solutionSetIn,
worksetIn, sspn, wspn, worksetCandidate, solutionSetCandidate);
wsNode.setImmediateSolutionSetUpdate(immediateDeltaUpdate);
wsNode.initProperties(gp, lp);
@@ -572,7 +572,7 @@ public class WorksetIterationNode extends TwoInputNode implements IterationNode
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Internal Utility Node";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetNode.java
index 3b05aba..ae636c5 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/dag/WorksetNode.java
@@ -84,7 +84,7 @@ public class WorksetNode extends AbstractPartialSolutionNode {
}
@Override
- public String getName() {
+ public String getOperatorName() {
return "Workset";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/PlanNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/PlanNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/PlanNode.java
index 6f634fb..9505a57 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/PlanNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/PlanNode.java
@@ -513,7 +513,7 @@ public abstract class PlanNode implements Visitable<PlanNode>, DumpableNode<Plan
@Override
public String toString() {
- return this.template.getName() + " \"" + getProgramOperator().getName() + "\" : " + this.driverStrategy +
+ return this.template.getOperatorName() + " \"" + getProgramOperator().getName() + "\" : " + this.driverStrategy +
" [[ " + this.globalProps + " ]] [[ " + this.localProps + " ]]";
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SinkPlanNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SinkPlanNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SinkPlanNode.java
index 656e67f..c28a5c3 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SinkPlanNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SinkPlanNode.java
@@ -16,7 +16,6 @@
* limitations under the License.
*/
-
package org.apache.flink.optimizer.plan;
import org.apache.flink.optimizer.dag.DataSinkNode;
@@ -25,8 +24,8 @@ import org.apache.flink.runtime.operators.DriverStrategy;
/**
* Plan candidate node for data flow sinks.
*/
-public class SinkPlanNode extends SingleInputPlanNode
-{
+public class SinkPlanNode extends SingleInputPlanNode {
+
/**
* Constructs a new sink candidate node that uses <i>NONE</i> as its local strategy. Note that
* local sorting and range partitioning are handled by the incoming channel already.
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SourcePlanNode.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SourcePlanNode.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SourcePlanNode.java
index 11b7cc9..937fe71 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SourcePlanNode.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plan/SourcePlanNode.java
@@ -16,7 +16,6 @@
* limitations under the License.
*/
-
package org.apache.flink.optimizer.plan;
import static org.apache.flink.optimizer.plan.PlanNode.SourceAndDamReport.FOUND_SOURCE;
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/plandump/PlanJSONDumpGenerator.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plandump/PlanJSONDumpGenerator.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plandump/PlanJSONDumpGenerator.java
index dc99fd7..e248b0b 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plandump/PlanJSONDumpGenerator.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plandump/PlanJSONDumpGenerator.java
@@ -39,7 +39,6 @@ import org.apache.flink.optimizer.dag.BulkIterationNode;
import org.apache.flink.optimizer.dag.DataSinkNode;
import org.apache.flink.optimizer.dag.DataSourceNode;
import org.apache.flink.optimizer.dag.OptimizerNode;
-import org.apache.flink.optimizer.dag.DagConnection;
import org.apache.flink.optimizer.dag.TempMode;
import org.apache.flink.optimizer.dag.WorksetIterationNode;
import org.apache.flink.optimizer.dataproperties.GlobalProperties;
@@ -56,9 +55,7 @@ import org.apache.flink.runtime.operators.DriverStrategy;
import org.apache.flink.runtime.operators.shipping.ShipStrategyType;
import org.apache.flink.util.StringUtils;
-/**
- *
- */
+
public class PlanJSONDumpGenerator {
private Map<DumpableNode<?>, Integer> nodeIds; // resolves pact nodes to ids
@@ -260,7 +257,7 @@ public class PlanJSONDumpGenerator {
}
- String name = n.getName();
+ String name = n.getOperatorName();
if (name.equals("Reduce") && (node instanceof SingleInputPlanNode) &&
((SingleInputPlanNode) node).getDriverStrategy() == DriverStrategy.SORTED_GROUP_COMBINE) {
name = "Combine";
@@ -309,8 +306,9 @@ public class PlanJSONDumpGenerator {
}
// output shipping strategy and channel type
final Channel channel = (inConn instanceof Channel) ? (Channel) inConn : null;
- final ShipStrategyType shipType = channel != null ? channel.getShipStrategy() :
- ((DagConnection) inConn).getShipStrategy();
+ final ShipStrategyType shipType = channel != null ?
+ channel.getShipStrategy() :
+ inConn.getShipStrategy();
String shipStrategy = null;
if (shipType != null) {
@@ -623,11 +621,11 @@ public class PlanJSONDumpGenerator {
writer.print("\" }");
}
- public static final String formatNumber(double number) {
+ public static String formatNumber(double number) {
return formatNumber(number, "");
}
- public static final String formatNumber(double number, String suffix) {
+ public static String formatNumber(double number, String suffix) {
if (number <= 0.0) {
return String.valueOf(number);
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JobGraphGenerator.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JobGraphGenerator.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JobGraphGenerator.java
index 943ec2e..0cbcea8 100644
--- a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JobGraphGenerator.java
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JobGraphGenerator.java
@@ -18,6 +18,8 @@
package org.apache.flink.optimizer.plantranslate;
+import com.fasterxml.jackson.core.JsonFactory;
+
import org.apache.flink.api.common.ExecutionConfig;
import org.apache.flink.api.common.aggregators.AggregatorRegistry;
import org.apache.flink.api.common.aggregators.AggregatorWithName;
@@ -48,7 +50,7 @@ import org.apache.flink.optimizer.plan.WorksetPlanNode;
import org.apache.flink.configuration.ConfigConstants;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.GlobalConfiguration;
-import org.apache.flink.optimizer.plandump.PlanJSONDumpGenerator;
+import org.apache.flink.optimizer.util.Utils;
import org.apache.flink.runtime.io.network.DataExchangeMode;
import org.apache.flink.runtime.io.network.partition.ResultPartitionType;
import org.apache.flink.runtime.iterative.convergence.WorksetEmptyConvergenceCriterion;
@@ -56,6 +58,7 @@ import org.apache.flink.runtime.iterative.task.IterationHeadPactTask;
import org.apache.flink.runtime.iterative.task.IterationIntermediatePactTask;
import org.apache.flink.runtime.iterative.task.IterationSynchronizationSinkTask;
import org.apache.flink.runtime.iterative.task.IterationTailPactTask;
+import org.apache.flink.runtime.jobgraph.JobEdge;
import org.apache.flink.runtime.jobgraph.JobVertex;
import org.apache.flink.runtime.jobgraph.DistributionPattern;
import org.apache.flink.runtime.jobgraph.InputFormatVertex;
@@ -78,6 +81,7 @@ import org.apache.flink.runtime.operators.shipping.ShipStrategyType;
import org.apache.flink.runtime.operators.util.LocalStrategy;
import org.apache.flink.runtime.operators.util.TaskConfig;
import org.apache.flink.util.InstantiationUtil;
+import org.apache.flink.util.StringUtils;
import org.apache.flink.util.Visitor;
import java.io.IOException;
@@ -107,7 +111,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
private static final boolean mergeIterationAuxTasks = GlobalConfiguration.getBoolean(MERGE_ITERATION_AUX_TASKS_KEY, false);
- private static final TaskInChain ALREADY_VISITED_PLACEHOLDER = new TaskInChain(null, null, null);
+ private static final TaskInChain ALREADY_VISITED_PLACEHOLDER = new TaskInChain(null, null, null, null);
// ------------------------------------------------------------------------
@@ -193,21 +197,27 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
t.addChainedTask(tic.getChainedTask(), tic.getTaskConfig(), tic.getTaskName());
}
+ // ----- attach the additional info to the job vertices, for display in the runtime monitor
+
+ attachOperatorNamesAndDescriptions();
+
+ // ----------- finalize the job graph -----------
+
// create the job graph object
JobGraph graph = new JobGraph(program.getJobName());
graph.setNumberOfExecutionRetries(program.getOriginalPactPlan().getNumberOfExecutionRetries());
graph.setAllowQueuedScheduling(false);
-
+
// add vertices to the graph
for (JobVertex vertex : this.vertices.values()) {
graph.addVertex(vertex);
}
-
+
for (JobVertex vertex : this.auxVertices) {
graph.addVertex(vertex);
vertex.setSlotSharingGroup(sharingGroup);
}
-
+
// add registered cache file into job configuration
for (Entry<String, DistributedCacheEntry> e : program.getOriginalPactPlan().getCachedFiles()) {
DistributedCache.writeFileInfoToConfig(e.getKey(), e.getValue(), graph.getJobConfiguration());
@@ -222,9 +232,6 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
throw new RuntimeException("Config object could not be written to Job Configuration: " + e);
}
- String jsonPlan = new PlanJSONDumpGenerator().getOptimizerPlanAsJSON(program);
- graph.setJsonPlan(jsonPlan);
-
// release all references again
this.vertices = null;
this.chainedTasks = null;
@@ -507,10 +514,10 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
// update name of container task
String containerTaskName = container.getName();
- if(containerTaskName.startsWith("CHAIN ")) {
- container.setName(containerTaskName+" -> "+chainedTask.getTaskName());
+ if (containerTaskName.startsWith("CHAIN ")) {
+ container.setName(containerTaskName + " -> " + chainedTask.getTaskName());
} else {
- container.setName("CHAIN "+containerTaskName+" -> "+chainedTask.getTaskName());
+ container.setName("CHAIN " + containerTaskName + " -> " + chainedTask.getTaskName());
}
this.chainedTasksInSequence.add(chainedTask);
@@ -581,7 +588,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
}
} catch (Exception e) {
throw new CompilerException(
- "An error occurred while translating the optimized plan to a nephele JobGraph: " + e.getMessage(), e);
+ "An error occurred while translating the optimized plan to a JobGraph: " + e.getMessage(), e);
}
}
@@ -612,7 +619,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
// check if the iteration's input is a union
if (iterationNode.getInput().getSource() instanceof NAryUnionPlanNode) {
- allInChannels = ((NAryUnionPlanNode) iterationNode.getInput().getSource()).getInputs().iterator();
+ allInChannels = (iterationNode.getInput().getSource()).getInputs().iterator();
} else {
allInChannels = Collections.singletonList(iterationNode.getInput()).iterator();
}
@@ -631,7 +638,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
// check if the iteration's input is a union
if (iterationNode.getInput2().getSource() instanceof NAryUnionPlanNode) {
- allInChannels = ((NAryUnionPlanNode) iterationNode.getInput2().getSource()).getInputs().iterator();
+ allInChannels = (iterationNode.getInput2().getSource()).getInputs().iterator();
} else {
allInChannels = Collections.singletonList(iterationNode.getInput2()).iterator();
}
@@ -760,7 +767,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
final DriverStrategy ds = node.getDriverStrategy();
// check, whether chaining is possible
- boolean chaining = false;
+ boolean chaining;
{
Channel inConn = node.getInput();
PlanNode pred = inConn.getSource();
@@ -804,7 +811,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
if (chaining) {
vertex = null;
config = new TaskConfig(new Configuration());
- this.chainedTasks.put(node, new TaskInChain(ds.getPushChainDriverClass(), config, taskName));
+ this.chainedTasks.put(node, new TaskInChain(node, ds.getPushChainDriverClass(), config, taskName));
} else {
// create task vertex
vertex = new JobVertex(taskName);
@@ -820,7 +827,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
// set the driver strategy
config.setDriverStrategy(ds);
- for(int i=0;i<ds.getNumRequiredComparators();i++) {
+ for (int i = 0; i < ds.getNumRequiredComparators(); i++) {
config.setDriverComparator(node.getComparator(i), i);
}
// assign memory, file-handles, etc.
@@ -922,11 +929,11 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
final TaskConfig headConfig;
if (merge) {
final PlanNode successor = pspn.getOutgoingChannels().get(0).getTarget();
- headVertex = (JobVertex) this.vertices.get(successor);
+ headVertex = this.vertices.get(successor);
if (headVertex == null) {
throw new CompilerException(
- "Bug: Trying to merge solution set with its sucessor, but successor has not been created.");
+ "Bug: Trying to merge solution set with its successor, but successor has not been created.");
}
// reset the vertex type to iteration head
@@ -990,7 +997,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
final TaskConfig headConfig;
if (merge) {
final PlanNode successor = wspn.getOutgoingChannels().get(0).getTarget();
- headVertex = (JobVertex) this.vertices.get(successor);
+ headVertex = this.vertices.get(successor);
if (headVertex == null) {
throw new CompilerException(
@@ -1050,13 +1057,6 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
* channel is then the channel into the union node, the local strategy channel the one from the union to the
* actual target operator.
*
- * @param channel
- * @param inputNumber
- * @param sourceVertex
- * @param sourceConfig
- * @param targetVertex
- * @param targetConfig
- * @param isBroadcast
* @throws CompilerException
*/
private DistributionPattern connectJobVertices(Channel channel, int inputNumber,
@@ -1109,7 +1109,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
}
- targetVertex.connectNewDataSetAsInput(sourceVertex, distributionPattern, resultType);
+ JobEdge edge = targetVertex.connectNewDataSetAsInput(sourceVertex, distributionPattern, resultType);
// -------------- configure the source task's ship strategy strategies in task config --------------
final int outputIndex = sourceConfig.getNumOutputs();
@@ -1146,6 +1146,35 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
} else {
targetConfig.addInputToGroup(inputNumber);
}
+
+ // ---------------- attach the additional infos to the job edge -------------------
+
+ String shipStrategy = JsonMapper.getShipStrategyString(channel.getShipStrategy());
+ if (channel.getShipStrategyKeys() != null && channel.getShipStrategyKeys().size() > 0) {
+ shipStrategy += " on " + (channel.getShipStrategySortOrder() == null ?
+ channel.getShipStrategyKeys().toString() :
+ Utils.createOrdering(channel.getShipStrategyKeys(), channel.getShipStrategySortOrder()).toString());
+ }
+
+ String localStrategy;
+ if (channel.getLocalStrategy() == null || channel.getLocalStrategy() == LocalStrategy.NONE) {
+ localStrategy = null;
+ }
+ else {
+ localStrategy = JsonMapper.getLocalStrategyString(channel.getLocalStrategy());
+ if (localStrategy != null && channel.getLocalStrategyKeys() != null && channel.getLocalStrategyKeys().size() > 0) {
+ localStrategy += " on " + (channel.getLocalStrategySortOrder() == null ?
+ channel.getLocalStrategyKeys().toString() :
+ Utils.createOrdering(channel.getLocalStrategyKeys(), channel.getLocalStrategySortOrder()).toString());
+ }
+ }
+
+ String caching = channel.getTempMode() == TempMode.NONE ? null : channel.getTempMode().toString();
+
+ edge.setShipStrategyName(shipStrategy);
+ edge.setPreProcessingOperationName(localStrategy);
+ edge.setOperatorLevelCachingDescription(caching);
+
return distributionPattern;
}
@@ -1191,7 +1220,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
if (needsMemory) {
// sanity check
- if (tm == null || tm == TempMode.NONE || channel.getRelativeTempMemory() <= 0) {
+ if (tm == TempMode.NONE || channel.getRelativeTempMemory() <= 0) {
throw new CompilerException("Bug in compiler: Inconsistent description of input materialization.");
}
config.setRelativeInputMaterializationMemory(inputNum, channel.getRelativeTempMemory());
@@ -1247,14 +1276,14 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
final PlanNode rootOfStepFunction = bulkNode.getRootOfStepFunction();
final TaskConfig tailConfig;
- JobVertex rootOfStepFunctionVertex = (JobVertex) this.vertices.get(rootOfStepFunction);
+ JobVertex rootOfStepFunctionVertex = this.vertices.get(rootOfStepFunction);
if (rootOfStepFunctionVertex == null) {
// last op is chained
final TaskInChain taskInChain = this.chainedTasks.get(rootOfStepFunction);
if (taskInChain == null) {
throw new CompilerException("Bug: Tail of step function not found as vertex or chained task.");
}
- rootOfStepFunctionVertex = (JobVertex) taskInChain.getContainingVertex();
+ rootOfStepFunctionVertex = taskInChain.getContainingVertex();
// the fake channel is statically typed to pact record. no data is sent over this channel anyways.
tailConfig = taskInChain.getTaskConfig();
@@ -1277,7 +1306,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
final TaskConfig tailConfigOfTerminationCriterion;
// If we have a termination criterion and it is not an intermediate node
if(rootOfTerminationCriterion != null && rootOfTerminationCriterion.getOutgoingChannels().isEmpty()) {
- JobVertex rootOfTerminationCriterionVertex = (JobVertex) this.vertices.get(rootOfTerminationCriterion);
+ JobVertex rootOfTerminationCriterionVertex = this.vertices.get(rootOfTerminationCriterion);
if (rootOfTerminationCriterionVertex == null) {
@@ -1286,7 +1315,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
if (taskInChain == null) {
throw new CompilerException("Bug: Tail of termination criterion not found as vertex or chained task.");
}
- rootOfTerminationCriterionVertex = (JobVertex) taskInChain.getContainingVertex();
+ rootOfTerminationCriterionVertex = taskInChain.getContainingVertex();
// the fake channel is statically typed to pact record. no data is sent over this channel anyways.
tailConfigOfTerminationCriterion = taskInChain.getTaskConfig();
@@ -1396,14 +1425,14 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
{
// get the vertex for the workset update
final TaskConfig worksetTailConfig;
- JobVertex nextWorksetVertex = (JobVertex) this.vertices.get(nextWorksetNode);
+ JobVertex nextWorksetVertex = this.vertices.get(nextWorksetNode);
if (nextWorksetVertex == null) {
// nextWorksetVertex is chained
TaskInChain taskInChain = this.chainedTasks.get(nextWorksetNode);
if (taskInChain == null) {
throw new CompilerException("Bug: Next workset node not found as vertex or chained task.");
}
- nextWorksetVertex = (JobVertex) taskInChain.getContainingVertex();
+ nextWorksetVertex = taskInChain.getContainingVertex();
worksetTailConfig = taskInChain.getTaskConfig();
} else {
worksetTailConfig = new TaskConfig(nextWorksetVertex.getConfiguration());
@@ -1421,14 +1450,14 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
}
{
final TaskConfig solutionDeltaConfig;
- JobVertex solutionDeltaVertex = (JobVertex) this.vertices.get(solutionDeltaNode);
+ JobVertex solutionDeltaVertex = this.vertices.get(solutionDeltaNode);
if (solutionDeltaVertex == null) {
// last op is chained
TaskInChain taskInChain = this.chainedTasks.get(solutionDeltaNode);
if (taskInChain == null) {
throw new CompilerException("Bug: Solution Set Delta not found as vertex or chained task.");
}
- solutionDeltaVertex = (JobVertex) taskInChain.getContainingVertex();
+ solutionDeltaVertex = taskInChain.getContainingVertex();
solutionDeltaConfig = taskInChain.getTaskConfig();
} else {
solutionDeltaConfig = new TaskConfig(solutionDeltaVertex.getConfiguration());
@@ -1481,7 +1510,7 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
syncConfig.setConvergenceCriterion(WorksetEmptyConvergenceCriterion.AGGREGATOR_NAME, new WorksetEmptyConvergenceCriterion());
}
- private static String getDescriptionForUserCode(UserCodeWrapper<?> wrapper) {
+ private String getDescriptionForUserCode(UserCodeWrapper<?> wrapper) {
try {
if (wrapper.hasObject()) {
try {
@@ -1499,6 +1528,113 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
return null;
}
}
+
+ private void attachOperatorNamesAndDescriptions() {
+ JsonFactory jsonFactory = new JsonFactory();
+
+ // we go back to front
+
+ // start with the in chains
+ for (int i = chainedTasksInSequence.size() - 1; i >= 0; i--) {
+ TaskInChain next = chainedTasksInSequence.get(i);
+ PlanNode planNode = next.getPlanNode();
+
+ JobVertex vertex = next.getContainingVertex();
+
+ // operator
+ String opName = planNode.getOptimizerNode().getOperatorName();
+ if (vertex.getOperatorName() == null) {
+ vertex.setOperatorName(opName);
+ }
+ else {
+ vertex.setOperatorName(opName + " -> " + vertex.getOperatorName());
+ }
+
+ // operator description
+ String opDescription = JsonMapper.getOperatorStrategyString(planNode.getDriverStrategy());
+ if (vertex.getOperatorDescription() == null) {
+ vertex.setOperatorDescription(opDescription);
+ }
+ else {
+ vertex.setOperatorDescription(opDescription + "\n -> " + vertex.getOperatorDescription());
+ }
+
+ // pretty name
+ String prettyName = StringUtils.showControlCharacters(planNode.getNodeName());
+ if (vertex.getOperatorPrettyName() == null) {
+ vertex.setOperatorPrettyName(prettyName);
+ }
+ else {
+ vertex.setOperatorPrettyName(prettyName + "\n -> " + vertex.getOperatorPrettyName());
+ }
+
+ // optimizer output properties
+ if (vertex.getResultOptimizerProperties() == null) {
+ // since we go backwards, this must be the last in its chain
+ String outputProps =
+ JsonMapper.getOptimizerPropertiesJson(jsonFactory, planNode);
+ vertex.setResultOptimizerProperties(outputProps);
+ }
+ }
+
+ // finish back-to-front traversal by going over the head vertices
+ for (Map.Entry<PlanNode, JobVertex> entry : vertices.entrySet()) {
+ PlanNode node = entry.getKey();
+ JobVertex vertex = entry.getValue();
+
+ // get the predecessors
+
+ String input1name = null;
+ String input2name = null;
+ int num = 0;
+ for (Channel c : node.getInputs()) {
+ if (num == 0) {
+ input1name = c.getSource().getNodeName();
+ }
+ else if (num == 1) {
+ input2name = c.getSource().getNodeName();
+ }
+ num++;
+ }
+
+ // operator
+ String opName = node.getOptimizerNode().getOperatorName();
+ if (vertex.getOperatorName() == null) {
+ vertex.setOperatorName(opName);
+ }
+ else {
+ vertex.setOperatorName(opName + " -> " + vertex.getOperatorName());
+ }
+
+ // operator description
+ String opStrategy = JsonMapper.getOperatorStrategyString(
+ node.getDriverStrategy(),
+ input1name != null ? input1name : "(unnamed)",
+ input2name != null ? input2name : "(unnamed)");
+
+ if (vertex.getOperatorDescription() == null) {
+ vertex.setOperatorDescription(opStrategy);
+ }
+ else {
+ vertex.setOperatorDescription(opStrategy + "\n -> " + vertex.getOperatorDescription());
+ }
+
+ // pretty name
+ String prettyName = StringUtils.showControlCharacters(node.getNodeName());
+ if (vertex.getOperatorPrettyName() == null) {
+ vertex.setOperatorPrettyName(prettyName);
+ }
+ else {
+ vertex.setOperatorPrettyName(prettyName + "\n -> " + vertex.getOperatorPrettyName());
+ }
+
+ // if there is not yet an output from a chained task, we set this output
+ if (vertex.getResultOptimizerProperties() == null) {
+ vertex.setResultOptimizerProperties(
+ JsonMapper.getOptimizerPropertiesJson(jsonFactory, node));
+ }
+ }
+ }
// -------------------------------------------------------------------------------------
// Descriptors for tasks / configurations that are chained or merged with other tasks
@@ -1516,15 +1652,24 @@ public class JobGraphGenerator implements Visitor<PlanNode> {
private final String taskName;
+ private final PlanNode planNode;
+
private JobVertex containingVertex;
- TaskInChain(Class<? extends ChainedDriver<?, ?>> chainedTask, TaskConfig taskConfig,
- String taskName) {
+ TaskInChain(PlanNode planNode, Class<? extends ChainedDriver<?, ?>> chainedTask,
+ TaskConfig taskConfig, String taskName) {
+
+ this.planNode = planNode;
this.chainedTask = chainedTask;
this.taskConfig = taskConfig;
this.taskName = taskName;
}
-
+
+
+ public PlanNode getPlanNode() {
+ return planNode;
+ }
+
public Class<? extends ChainedDriver<?, ?>> getChainedTask() {
return this.chainedTask;
}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JsonMapper.java
----------------------------------------------------------------------
diff --git a/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JsonMapper.java b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JsonMapper.java
new file mode 100644
index 0000000..4d81058
--- /dev/null
+++ b/flink-optimizer/src/main/java/org/apache/flink/optimizer/plantranslate/JsonMapper.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.optimizer.plantranslate;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.flink.api.common.operators.CompilerHints;
+import org.apache.flink.optimizer.dag.OptimizerNode;
+import org.apache.flink.optimizer.dataproperties.GlobalProperties;
+import org.apache.flink.optimizer.dataproperties.LocalProperties;
+import org.apache.flink.optimizer.plan.PlanNode;
+import org.apache.flink.runtime.operators.DriverStrategy;
+import org.apache.flink.runtime.operators.shipping.ShipStrategyType;
+import org.apache.flink.runtime.operators.util.LocalStrategy;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import static org.apache.flink.optimizer.plandump.PlanJSONDumpGenerator.formatNumber;
+
+public class JsonMapper {
+
+ public static String getOperatorStrategyString(DriverStrategy strategy) {
+ return getOperatorStrategyString(strategy, "input 1", "input 2");
+ }
+
+ public static String getOperatorStrategyString(DriverStrategy strategy, String firstInputName, String secondInputName) {
+ if (strategy == null) {
+ return "(null)";
+ }
+ switch (strategy) {
+ case SOURCE:
+ return "Data Source";
+ case SINK:
+ return "Data Sink";
+
+ case NONE:
+ return "(none)";
+
+ case BINARY_NO_OP:
+ case UNARY_NO_OP:
+ return "No-Op";
+
+ case COLLECTOR_MAP:
+ case MAP:
+ return "Map";
+
+ case FLAT_MAP:
+ return "FlatMap";
+
+ case MAP_PARTITION:
+ return "Map Partition";
+
+ case ALL_REDUCE:
+ return "Reduce All";
+
+ case ALL_GROUP_REDUCE:
+ case ALL_GROUP_REDUCE_COMBINE:
+ return "Group Reduce All";
+
+ case SORTED_REDUCE:
+ return "Sorted Reduce";
+
+ case SORTED_PARTIAL_REDUCE:
+ return "Sorted Combine/Reduce";
+
+ case SORTED_GROUP_REDUCE:
+ return "Sorted Group Reduce";
+
+ case SORTED_GROUP_COMBINE:
+ return "Sorted Combine";
+
+ case HYBRIDHASH_BUILD_FIRST:
+ return "Hybrid Hash (build: " + firstInputName + ")";
+
+ case HYBRIDHASH_BUILD_SECOND:
+ return "Hybrid Hash (build: " + secondInputName + ")";
+
+ case HYBRIDHASH_BUILD_FIRST_CACHED:
+ return "Hybrid Hash (CACHED) (build: " + firstInputName + ")";
+
+ case HYBRIDHASH_BUILD_SECOND_CACHED:
+ return "Hybrid Hash (CACHED) (build: " + secondInputName + ")";
+
+ case NESTEDLOOP_BLOCKED_OUTER_FIRST:
+ return "Nested Loops (Blocked Outer: " + firstInputName + ")";
+ case NESTEDLOOP_BLOCKED_OUTER_SECOND:
+ return "Nested Loops (Blocked Outer: " + secondInputName + ")";
+ case NESTEDLOOP_STREAMED_OUTER_FIRST:
+ return "Nested Loops (Streamed Outer: " + firstInputName + ")";
+ case NESTEDLOOP_STREAMED_OUTER_SECOND:
+ return "Nested Loops (Streamed Outer: " + secondInputName + ")";
+
+ case MERGE:
+ return "Merge";
+
+ case CO_GROUP:
+ return "Co-Group";
+
+ default:
+ return strategy.name();
+ }
+ }
+
+ public static String getShipStrategyString(ShipStrategyType shipType) {
+ if (shipType == null) {
+ return "(null)";
+ }
+ switch (shipType) {
+ case NONE:
+ return "(none)";
+ case FORWARD:
+ return "Forward";
+ case BROADCAST:
+ return "Broadcast";
+ case PARTITION_HASH:
+ return "Hash Partition";
+ case PARTITION_RANGE:
+ return "Range Partition";
+ case PARTITION_RANDOM:
+ return "Redistribute";
+ case PARTITION_FORCED_REBALANCE:
+ return "Rebalance";
+ case PARTITION_CUSTOM:
+ return "Custom Partition";
+ default:
+ return shipType.name();
+ }
+ }
+
+ public static String getLocalStrategyString(LocalStrategy localStrategy) {
+ if (localStrategy == null) {
+ return "(null)";
+ }
+ switch (localStrategy) {
+ case NONE:
+ return "(none)";
+ case SORT:
+ return "Sort";
+ case COMBININGSORT:
+ return "Sort (combining)";
+ default:
+ return localStrategy.name();
+ }
+ }
+
+ public static String getOptimizerPropertiesJson(JsonFactory jsonFactory, PlanNode node) {
+ try {
+ final StringWriter writer = new StringWriter(256);
+ final JsonGenerator gen = jsonFactory.createGenerator(writer);
+
+ final OptimizerNode optNode = node.getOptimizerNode();
+
+ gen.writeStartObject();
+
+ // global properties
+ if (node.getGlobalProperties() != null) {
+ GlobalProperties gp = node.getGlobalProperties();
+ gen.writeArrayFieldStart("global_properties");
+
+ addProperty(gen, "Partitioning", gp.getPartitioning().name());
+ if (gp.getPartitioningFields() != null) {
+ addProperty(gen, "Partitioned on", gp.getPartitioningFields().toString());
+ }
+ if (gp.getPartitioningOrdering() != null) {
+ addProperty(gen, "Partitioning Order", gp.getPartitioningOrdering().toString());
+ }
+ else {
+ addProperty(gen, "Partitioning Order", "(none)");
+ }
+ if (optNode.getUniqueFields() == null || optNode.getUniqueFields().size() == 0) {
+ addProperty(gen, "Uniqueness", "not unique");
+ }
+ else {
+ addProperty(gen, "Uniqueness", optNode.getUniqueFields().toString());
+ }
+
+ gen.writeEndArray();
+ }
+
+ // local properties
+ if (node.getLocalProperties() != null) {
+ LocalProperties lp = node.getLocalProperties();
+ gen.writeArrayFieldStart("local_properties");
+
+ if (lp.getOrdering() != null) {
+ addProperty(gen, "Order", lp.getOrdering().toString());
+ }
+ else {
+ addProperty(gen, "Order", "(none)");
+ }
+ if (lp.getGroupedFields() != null && lp.getGroupedFields().size() > 0) {
+ addProperty(gen, "Grouped on", lp.getGroupedFields().toString());
+ } else {
+ addProperty(gen, "Grouping", "not grouped");
+ }
+ if (optNode.getUniqueFields() == null || optNode.getUniqueFields().size() == 0) {
+ addProperty(gen, "Uniqueness", "not unique");
+ }
+ else {
+ addProperty(gen, "Uniqueness", optNode.getUniqueFields().toString());
+ }
+
+ gen.writeEndArray();
+ }
+
+ // output size estimates
+ {
+ gen.writeArrayFieldStart("estimates");
+
+ addProperty(gen, "Est. Output Size", optNode.getEstimatedOutputSize() == -1 ? "(unknown)"
+ : formatNumber(optNode.getEstimatedOutputSize(), "B"));
+
+ addProperty(gen, "Est. Cardinality", optNode.getEstimatedNumRecords() == -1 ? "(unknown)"
+ : formatNumber(optNode.getEstimatedNumRecords()));
+ gen.writeEndArray();
+ }
+
+ // output node cost
+ if (node.getNodeCosts() != null) {
+ gen.writeArrayFieldStart("costs");
+
+ addProperty(gen, "Network", node.getNodeCosts().getNetworkCost() == -1 ?
+ "(unknown)" : formatNumber(node.getNodeCosts().getNetworkCost(), "B"));
+ addProperty(gen, "Disk I/O", node.getNodeCosts().getDiskCost() == -1 ?
+ "(unknown)" : formatNumber(node.getNodeCosts().getDiskCost(), "B"));
+ addProperty(gen, "CPU", node.getNodeCosts().getCpuCost() == -1 ?
+ "(unknown)" : formatNumber(node.getNodeCosts().getCpuCost(), ""));
+
+ addProperty(gen, "Cumulative Network", node.getCumulativeCosts().getNetworkCost() == -1 ?
+ "(unknown)" : formatNumber(node.getCumulativeCosts().getNetworkCost(), "B"));
+ addProperty(gen, "Cumulative Disk I/O", node.getCumulativeCosts().getDiskCost() == -1 ?
+ "(unknown)" : formatNumber(node.getCumulativeCosts().getDiskCost(), "B"));
+ addProperty(gen, "Cumulative CPU", node.getCumulativeCosts().getCpuCost() == -1 ?
+ "(unknown)" : formatNumber(node.getCumulativeCosts().getCpuCost(), ""));
+
+ gen.writeEndArray();
+ }
+
+
+ // compiler hints
+ if (optNode.getOperator().getCompilerHints() != null) {
+ CompilerHints hints = optNode.getOperator().getCompilerHints();
+ CompilerHints defaults = new CompilerHints();
+
+ String size = hints.getOutputSize() == defaults.getOutputSize() ?
+ "(none)" : String.valueOf(hints.getOutputSize());
+ String card = hints.getOutputCardinality() == defaults.getOutputCardinality() ?
+ "(none)" : String.valueOf(hints.getOutputCardinality());
+ String width = hints.getAvgOutputRecordSize() == defaults.getAvgOutputRecordSize() ?
+ "(none)" : String.valueOf(hints.getAvgOutputRecordSize());
+ String filter = hints.getFilterFactor() == defaults.getFilterFactor() ?
+ "(none)" : String.valueOf(hints.getFilterFactor());
+
+ gen.writeArrayFieldStart("compiler_hints");
+
+ addProperty(gen, "Output Size (bytes)", size);
+ addProperty(gen, "Output Cardinality", card);
+ addProperty(gen, "Avg. Output Record Size (bytes)", width);
+ addProperty(gen, "Filter Factor", filter);
+
+ gen.writeEndArray();
+ }
+
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+ catch (Exception e) {
+ return "{}";
+ }
+ }
+
+
+ private static void addProperty(JsonGenerator gen, String name, String value) throws IOException {
+ gen.writeStartObject();
+ gen.writeStringField("name", name);
+ gen.writeStringField("value", value);
+ gen.writeEndObject();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
index 839f91f..82e61db 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
@@ -18,18 +18,18 @@
.panel.panel-default.panel-multi(ng-if="node")
.panel-heading.clearfix
.panel-title
- | {{ node.pact }}
+ | {{ node.operator }}
.panel-info.first
| ID: {{ node.id }}
- .panel-info(ng-if="node.contents")
+ .panel-info(ng-if="node.description")
.label-group
bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{node.vertex.groupvertex[status]}}
.panel-heading.clearfix
- .panel-info.first.last(ng-if="node.contents")
- span {{ node.contents }}
+ .panel-info.first.last(ng-if="node.description")
+ span {{ node.description }}
.panel-body
table.table.table-hover.table-clickable
@@ -79,41 +79,34 @@
thead
tr
th(colspan="2")
- | Pact Properties
+ | Properties
tbody
tr
td Operator
- td(table-property value="node.driver_strategy")
+ td(table-property value="node.operator_strategy")
tr
td Parallelism
td(table-property value="node.parallelism")
- tr
- td Subtasks-per-instance
- td(table-property value="node.subtasks_per_instance")
-
.hidden-sm.col-md-4
table.table.table-properties
thead
tr
th(colspan="2")
- | Pact Properties
+ | Properties
tbody
tr
td Operator
- td(table-property value="node.driver_strategy")
+ td(table-property value="node.operator_strategy")
tr
td Parallelism
td(table-property value="node.parallelism")
- tr
- td Subtasks-per-instance
- td(table-property value="node.subtasks_per_instance")
table.table.table-properties(ng-if="node.estimates")
thead
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
index bb4d925..ee309ad 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
@@ -235,11 +235,6 @@ angular.module('flinkApp')
'node-iteration'
else
- if el.pact is "Data Source"
- 'node-source'
- else if el.pact is "Data Sink"
- 'node-sink'
- else
'node-normal'
# creates the label of a node, in info is stored, whether it is a special node (like a mirror in an iteration)
@@ -248,13 +243,13 @@ angular.module('flinkApp')
# Nodename
if info is "mirror"
- labelValue += "<h3 class='node-name'>Mirror of " + el.pact + "</h3>"
+ labelValue += "<h3 class='node-name'>Mirror of " + el.operator + "</h3>"
else
- labelValue += "<h3 class='node-name'>" + el.pact + "</h3>"
- if el.contents is ""
+ labelValue += "<h3 class='node-name'>" + el.operator + "</h3>"
+ if el.description is ""
labelValue += ""
else
- stepName = el.contents
+ stepName = el.description
# clean stepName
stepName = shortenString(stepName)
@@ -268,7 +263,7 @@ angular.module('flinkApp')
# Otherwise add infos
labelValue += "<h5>" + info + " Node</h5>" if isSpecialIterationNode(info)
labelValue += "<h5>Parallelism: " + el.parallelism + "</h5>" unless el.parallelism is ""
- labelValue += "<h5>Driver Strategy: " + shortenString(el.driver_strategy) + "</h5" unless el.driver_strategy is `undefined`
+ labelValue += "<h5>Operation: " + shortenString(el.operator_strategy) + "</h5>" unless el.operator is `undefined`
labelValue += "</a>"
labelValue
@@ -398,9 +393,9 @@ angular.module('flinkApp')
existingNodes.push el.id
- # create edges from predecessors to current node
- if el.predecessors?
- for pred in el.predecessors
+ # create edges from inputs to current node
+ if el.inputs?
+ for pred in el.inputs
createEdge(g, data, el, existingNodes, pred)
g
[41/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Show
plan (and optimizer properties) as a dedicated view
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
index fc43714..bcaef0d 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
@@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<div ng-repeat="exception in exceptions['all-exceptions']" class="panel panel-default panel-multi">
+<div class="panel panel-default panel-multi">
<div class="panel-heading clearfix">
<div class="panel-title">Root exception</div>
</div>
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
index 94a9930..27e965a 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
@@ -17,9 +17,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<table class="table table-hover table-clickable table-activable">
+<table class="table table-body-hover table-clickable table-activable">
<thead>
<tr>
+ <th class="tab-column"></th>
<th>Start Time</th>
<th>End Time</th>
<th>Duration</th>
@@ -32,13 +33,17 @@ limitations under the License.
<th>Status</th>
</tr>
</thead>
- <tbody>
- <tr ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" id="vertex-row-{{v.id}}" ui-sref="{nodeid: v.id}">
- <td>{{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- <div ui-view="node"></div>
+ <tbody ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" ui-sref="{nodeid: v.id}">
+ <tr>
+ <td class="tab-column">
+ <ul ng-if="job.currentNode && v.id == job.currentNode.id" class="nav nav-tabs tabs-vertical">
+ <li ng-class="{ active: ('single-job.plan.node.generic' | isState) }"><a ng-click="gotoGeneric(v.id)" title="Basic"><i class="fa fa-tasks"></i></a></li>
+ <li ng-class="{ active: ('single-job.plan.node.accumulators' | isState) }"><a ng-click="gotoAccumulators(v.id)" title="Accumulators"><i class="fa fa-calculator"></i></a></li>
+ </ul>
</td>
- <td>{{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</td>
- <td>{{ v.duration }} ms</td>
+ <td><span ng-if="v['start-time'] > -1">{{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
+ <td><span ng-if="v['end-time'] > -1">{{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
+ <td><span ng-if="v.duration > -1">{{ v.duration }} ms</span></td>
<td>{{ v.name | humanizeTaskName }}</td>
<td>{{ v.metrics['read-bytes'] }}</td>
<td>{{ v.metrics['read-records'] }}</td>
@@ -53,5 +58,12 @@ limitations under the License.
<bs-label status="{{v.status}}">{{v.status}}</bs-label>
</td>
</tr>
+ <tr ng-if="job.currentNode && v.id == job.currentNode.id">
+ <td class="tab-column"></td>
+ <td colspan="10">
+ <div ng-if="'single-job.plan.node.generic' | isState" ng-include=" 'partials/jobs/job.plan.node.subtasks.html' "></div>
+ <div ng-if="'single-job.plan.node.accumulators' | isState" ng-include=" 'partials/jobs/job.plan.node.accumulators.html' "></div>
+ </td>
+ </tr>
</tbody>
</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-nav.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-nav.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-nav.html
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
index 3ca9c90..15a8638 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
@@ -6,5 +6,8 @@
<li ui-sref-active="active"><a ui-sref=".properties({nodeid: nodeid})">Properties</a></li>
</ul>
</nav>
+ <div ng-if="'single-job.plan.node.properties' | isState" class="panel-heading clearfix">
+ <div class="panel-title">{{ node.description | humanizeTaskName }}</div>
+ </div>
<div ui-view="node-details" class="panel-body clean"></div>
</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
index 1a1ed14..dce9fe5 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
@@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<div ng-if="node" class="row">
+<div class="row">
<div class="col-sm-6 col-md-4">
<table ng-if="node.optimizer_properties.global_properties" class="table table-properties">
<thead>
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
index 34fedd0..594e080 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
@@ -16,8 +16,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+
-->
-<table class="table table-hover table-clickable">
+<table class="table table-hover table-clickable table-activable table-inner">
<thead>
<tr>
<th>Start Time</th>
@@ -27,26 +28,24 @@ limitations under the License.
<th>Records read</th>
<th>Bytes written</th>
<th>Records written</th>
- <th>Tasks</th>
+ <th>Attempt</th>
+ <th>Host</th>
<th>Status</th>
</tr>
</thead>
<tbody>
- <tr>
- <td>{{ vertex['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</td>
- <td>{{ vertex['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</td>
- <td>{{ vertex.duration }} ms</td>
- <td>{{ vertex.metrics['read-bytes'] }}</td>
- <td>{{ vertex.metrics['read-records'] }}</td>
- <td>{{ vertex.metrics['write-bytes'] }}</td>
- <td>{{ vertex.metrics['write-records'] }}</td>
- <td>
- <div class="label-group">
- <bs-label status="{{status}}" ng-repeat="(index, status) in stateList">{{vertex.tasks[status]}}</bs-label>
- </div>
- </td>
+ <tr ng-repeat="subtask in vertex.st">
+ <td><span ng-if="subtask['start-time'] > -1">{{ subtask['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
+ <td><span ng-if="subtask['end-time'] > -1">{{ subtask['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
+ <td><span ng-if="subtask.duration > -1">{{ subtask.duration }} ms</span></td>
+ <td><span ng-if="subtask.metrics['read-bytes'] > -1">{{ subtask.metrics['read-bytes'] }}</span></td>
+ <td><span ng-if="subtask.metrics['read-records'] > -1">{{ subtask.metrics['read-records'] }}</span></td>
+ <td><span ng-if="subtask.metrics['write-bytes'] > -1">{{ subtask.metrics['write-bytes'] }}</span></td>
+ <td><span ng-if="subtask.metrics['write-records'] > -1">{{ subtask.metrics['write-records'] }}</span></td>
+ <td>{{ subtask.attempt + 1 }}</td>
+ <td>{{ subtask.host }}</td>
<td>
- <bs-label status="{{vertex.status}}">{{vertex.status}}</bs-label>
+ <bs-label status="{{subtask.status}}">{{subtask.status}}</bs-label>
</td>
</tr>
</tbody>
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 3f47993..6220ac8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -741,11 +741,9 @@ under the License.
<exclude>flink-runtime-web/web-dashboard/vendor-local/d3-timeline.js</exclude>
<exclude>flink-runtime-web/web-dashboard/assets/fonts/FontAwesome.otf</exclude>
<exclude>flink-runtime-web/web-dashboard/assets/fonts/fontawesome*</exclude>
- <exclude>flink-runtime-web/web-dashboard/web/css/vendor.css</exclude>
- <exclude>flink-runtime-web/web-dashboard/web/fonts/FontAwesome.otf</exclude>
- <exclude>flink-runtime-web/web-dashboard/web/fonts/fontawesome*</exclude>
- <exclude>flink-runtime-web/web-dashboard/web/js/index.js</exclude>
- <exclude>flink-runtime-web/web-dashboard/web/js/vendor.js</exclude>
+
+ <!-- generated contents -->
+ <exclude>flink-runtime-web/web-dashboard/web/**</exclude>
<!-- downloaded and generated web libraries. -->
<exclude>flink-runtime-web/web-dashboard/node_modules/**</exclude>
[10/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Changed overview and timeline
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/web/js/vendor.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/vendor.js b/flink-runtime-web/web-dashboard/web/js/vendor.js
index 3f15020..949ef06 100644
--- a/flink-runtime-web/web-dashboard/web/js/vendor.js
+++ b/flink-runtime-web/web-dashboard/web/js/vendor.js
@@ -80172,16 +80172,18 @@ module.exports = "0.4.10";
.attr("class", function(d, i) { return "bar-container bar-type-" + d.type; } )
.attr("width", getBarWidth);
- nel
- .append("svg:clipPath")
- .attr("id", prefix + "-timeline-textclip-" + i + "-" + index)
- .attr("class", "timeline-clip")
- .append("svg:rect")
- .attr("clipPathUnits","objectBoundingBox")
- .attr("x", getXPos)
- .attr("y", getStackPosition)
- .attr("width", getTextWidth)
- .attr("height", itemHeight);
+ if (data[0].type != "scheduled") {
+ nel
+ .append("svg:clipPath")
+ .attr("id", prefix + "-timeline-textclip-" + i + "-" + index)
+ .attr("class", "timeline-clip")
+ .append("svg:rect")
+ .attr("clipPathUnits","objectBoundingBox")
+ .attr("x", getXPos)
+ .attr("y", getStackPosition)
+ .attr("width", getTextWidth)
+ .attr("height", itemHeight);
+ }
var bar = nel
.append(function(d, i) {
@@ -80189,6 +80191,8 @@ module.exports = "0.4.10";
})
.attr("x", getXPos)
.attr("y", getStackPosition)
+ .attr("rx", 5)
+ .attr("ry", 5)
.attr("width", getBarWidth)
.attr("cy", function(d, i) {
return getStackPosition(d, i) + itemHeight/2;
@@ -80196,6 +80200,8 @@ module.exports = "0.4.10";
.attr("cx", getXPos)
.attr("r", itemHeight / 2)
.attr("height", itemHeight)
+ .style("stroke", function(d, i){ return d.borderColor; })
+ .style("stroke-width", 1)
.style("fill", function(d, i){
var dColorPropName;
if (d.color) return d.color;
@@ -80233,12 +80239,12 @@ module.exports = "0.4.10";
return d.id ? d.id : "timelineItem_"+index+"_"+i;
})
- nel
+ var barText = nel
.append("text")
.attr("class", "timeline-insidelabel")
.attr("x", getXTextPos)
.attr("y", getStackTextPosition)
- .attr("width", getTextWidth)
+ // .attr("width", getTextWidth)
.attr("height", itemHeight)
.attr("clip-path", "url(#" + prefix + "-timeline-textclip-" + i + "-" + index + ")")
.text(function(d) {
@@ -80249,6 +80255,10 @@ module.exports = "0.4.10";
});
;
+ if (data[0].type == "scheduled") {
+ bar.attr('width', barText.node().getComputedTextLength() + 10);
+ }
+
g.selectAll("svg .bar-container").each(function(d, i) {
$(this).qtip({
content: {
@@ -80279,16 +80289,19 @@ module.exports = "0.4.10";
}
if (showBorderLine) {
- g.selectAll("svg").data(data).enter().append("svg:line")
- .attr("class", "line-" + 'start')
- .attr("x1", getBorderStart)
- .attr("y1", getStackPosition)
- .attr("x2", getBorderStart)
- .attr("y2", margin.top + (itemHeight + itemMargin) * maxStack)
- .style("stroke", function(d, i) { return d.color; })
- .style("stroke-width", showBorderFormat.width);
-
- if (data[0].type != "scheduled") {
+ if (data[0].type == "scheduled") {
+ g.selectAll("svg").data(data).enter().append("svg:line")
+ .attr("class", "line-" + 'start')
+ .attr("x1", getBorderStart)
+ .attr("y1", getStackBorderPosition)
+ .attr("x2", getBorderStart)
+ .attr("y2", margin.top + (itemHeight + itemMargin) * maxStack)
+ .style("stroke", function(d, i) { return d.color; })
+ .style("stroke-width", showBorderFormat.width);
+ }
+
+ // if (data[0].type != "scheduled") {
+ if (false) {
g.selectAll("svg").data(data).enter().append("svg:line")
.attr("class", "line-" + 'end')
.attr("x1", getBorderEnd)
@@ -80328,9 +80341,15 @@ module.exports = "0.4.10";
}
function getStackTextPosition(d, i) {
if (stacked) {
- return margin.top + (itemHeight + itemMargin) * yAxisMapping[index] + itemHeight * 0.70;
+ return margin.top + (itemHeight + itemMargin) * yAxisMapping[index] + itemHeight * 0.65;
+ }
+ return margin.top + itemHeight * 0.65;
+ }
+ function getStackBorderPosition(d, i) {
+ if (stacked) {
+ return margin.top + (itemHeight + itemMargin) * yAxisMapping[index] + itemHeight - 3;
}
- return margin.top + itemHeight * 0.70;
+ return margin.top + itemHeight - 3;
}
});
});
@@ -80396,7 +80415,7 @@ module.exports = "0.4.10";
setHeight();
bbox = g[0][0].getBBox();
- gParent.attr('height', bbox.height + 30);
+ gParent.attr('height', bbox.height + 40);
function getBorderStart(d, i) {
return xScale(d.starting_time);
@@ -80645,4 +80664,4 @@ module.exports = "0.4.10";
};
})();
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImpxdWVyeS5qcyIsImFuZ3VsYXIuanMiLCJhbmd1bGFyLXVpLXJvdXRlci5qcyIsImQzLmpzIiwibG9kYXNoLmpzIiwibW9tZW50LmpzIiwibW9tZW50LWR1cmF0aW9uLWZvcm1hdC5qcyIsIkV2ZW50RW1pdHRlci5qcyIsImV2ZW50aWUuanMiLCJib290c3RyYXAuanMiLCJncmFwaGxpYi5jb3JlLmpzIiwiYW5ndWxhci1tb21lbnQuanMiLCJpbWFnZXNsb2FkZWQuanMiLCJkYWdyZS5jb3JlLmpzIiwianF1ZXJ5LnF0aXAuanMiLCJkYWdyZS1kMy5jb3JlLmpzIiwiZDMtdGltZWxpbmUuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB
Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB
Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB
Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUMxL1JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB
Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
<TRUNCATED>
[03/51] [abbrv] flink git commit: [FLINK-2415] [optimizer] Create and
attach proper JobGraph describing JSON plans to JobGraph for batch jobs.
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/ff28981f/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index 7ec0a36..4d7b406 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -9,7 +9,10 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
newServer: 'http://localhost:8081',
refreshInterval: 10000
}).run(["JobsService", "flinkConfig", "$interval", function(JobsService, flinkConfig, $interval) {
- return JobsService.listJobs();
+ JobsService.listJobs();
+ return $interval(function() {
+ return JobsService.listJobs();
+ }, flinkConfig.refreshInterval);
}]).config(["$stateProvider", "$urlRouterProvider", function($stateProvider, $urlRouterProvider) {
$stateProvider.state("overview", {
url: "/overview",
@@ -203,7 +206,8 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
data: "="
},
link: function(scope, elem, attrs) {
- var analyzeTime, containerW, svgEl;
+ var analyzeTime, containerW, svgEl, zoom;
+ zoom = d3.behavior.zoom();
svgEl = elem.children()[0];
containerW = elem.width();
angular.element(svgEl).attr('width', containerW - 16);
@@ -236,18 +240,25 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
});
});
chart = d3.timeline().stack().tickFormat({
- format: d3.time.format("%L"),
+ format: d3.time.format("%S"),
+ tickInterval: 1,
tickSize: 1
- }).prefix("single").labelFormat(function(label) {
+ }).labelFormat(function(label) {
return label;
}).margin({
left: 100,
right: 0,
top: 0,
bottom: 0
- }).itemHeight(24).relativeTime();
- svg = d3.select(svgEl).datum(testData).call(chart);
+ });
+ svg = d3.select(svgEl).datum(testData).call(chart).call(zoom);
svgG = svg.select("g");
+ zoom.on("zoom", function() {
+ var ev;
+ ev = d3.event;
+ svgG.selectAll('rect').attr("transform", "translate(" + ev.translate[0] + ",0) scale(" + ev.scale + ",1)");
+ return svgG.selectAll('text').attr("transform", "translate(" + ev.translate[0] + ",0) scale(" + ev.scale + ",1)");
+ });
bbox = svgG[0][0].getBBox();
return svg.attr('height', bbox.height + 30);
};
@@ -261,13 +272,11 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
job: "="
},
link: function(scope, elem, attrs) {
- var analyzeTime, containerW, svgEl, translateLabel;
+ var analyzeTime, containerW, svgEl, zoom;
+ zoom = d3.behavior.zoom();
svgEl = elem.children()[0];
containerW = elem.width();
angular.element(svgEl).attr('width', containerW - 16);
- translateLabel = function(label) {
- return label.replace(">", ">");
- };
analyzeTime = function(data) {
var bbox, chart, svg, svgG, testData;
testData = [];
@@ -277,8 +286,8 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
return testData.push({
times: [
{
- label: translateLabel(vertex.groupvertexname),
- color: "#62cdea",
+ label: vertex.groupvertexname,
+ color: "#3fb6d8",
starting_time: vTime["STARTED"],
ending_time: vTime["ENDED"],
link: vertex.groupvertexid
@@ -292,16 +301,23 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
vertexId: d.link
});
}).tickFormat({
- format: d3.time.format("%L"),
+ format: d3.time.format("%S"),
+ tickInterval: 1,
tickSize: 1
- }).prefix("main").margin({
+ }).margin({
left: 0,
right: 0,
top: 0,
bottom: 0
- }).itemHeight(24).showBorderLine().showHourTimeline();
- svg = d3.select(svgEl).datum(testData).call(chart);
+ });
+ svg = d3.select(svgEl).datum(testData).call(chart).call(zoom);
svgG = svg.select("g");
+ zoom.on("zoom", function() {
+ var ev;
+ ev = d3.event;
+ svgG.selectAll('rect').attr("transform", "translate(" + ev.translate[0] + ",0) scale(" + ev.scale + ",1)");
+ return svgG.selectAll('text').attr("transform", "translate(" + ev.translate[0] + ",0) scale(" + ev.scale + ",1)");
+ });
bbox = svgG[0][0].getBBox();
return svg.attr('height', bbox.height + 30);
};
@@ -380,27 +396,21 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
} else if (isSpecialIterationNode(info)) {
return 'node-iteration';
} else {
- if (el.pact === "Data Source") {
- return 'node-source';
- } else if (el.pact === "Data Sink") {
- return 'node-sink';
- } else {
- return 'node-normal';
- }
+ return 'node-normal';
}
};
createLabelNode = function(el, info, maxW, maxH) {
var labelValue, stepName;
labelValue = "<a href='#/jobs/" + jobid + "/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>";
if (info === "mirror") {
- labelValue += "<h3 class='node-name'>Mirror of " + el.pact + "</h3>";
+ labelValue += "<h3 class='node-name'>Mirror of " + el.operator + "</h3>";
} else {
- labelValue += "<h3 class='node-name'>" + el.pact + "</h3>";
+ labelValue += "<h3 class='node-name'>" + el.operator + "</h3>";
}
- if (el.contents === "") {
+ if (el.description === "") {
labelValue += "";
} else {
- stepName = el.contents;
+ stepName = el.description;
stepName = shortenString(stepName);
labelValue += "<h4 class='step-name'>" + stepName + "</h4>";
}
@@ -413,8 +423,8 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
if (el.parallelism !== "") {
labelValue += "<h5>Parallelism: " + el.parallelism + "</h5>";
}
- if (el.driver_strategy !== undefined) {
- labelValue += "<h5>Driver Strategy: " + shortenString(el.driver_strategy) + "</h5";
+ if (el.operator !== undefined) {
+ labelValue += "<h5>Operation: " + shortenString(el.operator_strategy) + "</h5>";
}
}
labelValue += "</a>";
@@ -547,8 +557,8 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
}
createNode(g, data, el, isParent, maxW, maxH);
existingNodes.push(el.id);
- if (el.predecessors != null) {
- ref = el.predecessors;
+ if (el.inputs != null) {
+ ref = el.inputs;
for (l = 0, len1 = ref.length; l < len1; l++) {
pred = ref[l];
createEdge(g, data, el, existingNodes, pred);
@@ -794,4 +804,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuY3RybC5qcyIsIm1vZHVsZXMvam9icy9qb2JzLmRpci5jb2ZmZWUiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuanMiLCJtb2R1bGVzL2pvYnMvam9icy5zdmMuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmpzIiwibW9kdWxlcy9vdmVydmlldy9vdmVydmlldy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuc3ZjLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQWtCQSxRQUFRLE9BQU8sWUFBWSxDQUFDLGFBQWEsa0JBSXhDLG1CQUFJLFNBQUMsWUFBRDtFQUNILFdBQVcsaUJBQWlCO0VDckI1QixPRHNCQSxXQUFXLGNBQWMsV0FBQTtJQUN2QixXQUFXLGlCQUFpQixDQUFDLFdBQVc7SUNyQnhDLE9Ec0JBLFdBQVcsZUFBZTs7SUFJN0IsU0FBUyxlQUFlO0VBQ3ZCLFdBQVc7RUFDWCxXQUFXO0VBR1gsaUJBQWlCO0dBS2xCLGdEQUFJLFNBQUM
sYUFBYSxhQUFhLFdBQTNCO0VDN0JILE9EOEJBLFlBQVk7SUFTYixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sd0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sdUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhOzs7S0FFbEIsTUFBTSw4QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsUUFDRTtRQUFBLGFBQWE7UUFDYixZQUFZOzs7S0FFakIsTUFBTSx5QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsU0FDRTtRQUFBLGFBQWE7OztLQUVsQixNQUFNLHlCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTs7O0tBRWxCLE1BQU0s
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
LElBQUksWUFBWSxZQUFZLG1DQUFtQyxRQUFRLGtCQUFrQixVQUM5RixRQUFRLFNBQUMsTUFBRDtNQ3RCUCxPRHVCQSxTQUFTLFFBQVE7O0lDckJuQixPRHVCQSxTQUFTOztFQ3JCWCxPRHVCQTs7QUNyQkY7QUN6SUEsUUFBUSxPQUFPLFlBRWQsV0FBVyxtRUFBc0IsU0FBQyxRQUFRLGlCQUFpQixhQUExQjtFQUNoQyxPQUFPLGNBQWMsV0FBQTtJQUNuQixPQUFPLGNBQWMsWUFBWSxRQUFRO0lDbkJ6QyxPRG9CQSxPQUFPLGVBQWUsWUFBWSxRQUFROztFQUU1QyxZQUFZLGlCQUFpQixPQUFPO0VBQ3BDLE9BQU8sSUFBSSxZQUFZLFdBQUE7SUNuQnJCLE9Eb0JBLFlBQVksbUJBQW1CLE9BQU87O0VDbEJ4QyxPRG9CQSxPQUFPOztBQ2xCVDtBQ09BLFFBQVEsT0FBTyxZQUVkLFFBQVEsb0RBQW1CLFNBQUMsT0FBTyxhQUFhLE1BQXJCO0VBQzFCLElBQUE7RUFBQSxlQUFlO0VBRWYsS0FBQyxtQkFBbUIsV0FBQTtJQUNsQixNQUFNLElBQUksWUFBWSxZQUFZLG1CQUNqQyxRQUFRLFNBQUMsTUFBTSxRQUFRLFNBQVMsUUFBeEI7TUNwQlAsT0RxQkEsS0FBSztPQUVOLE1BQU0sU0FBQyxNQUFNLFFBQVEsU0FBUyxRQUF4QjtJQ3JCUCxPRHdCQTs7RUN0QkYsT0R3QkE7O0FDdEJGIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzL
iAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcsIFsndWkucm91dGVyJywgJ2
FuZ3VsYXJNb21lbnQnXSlcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4ucnVuICgkcm9vdFNjb3BlKSAtPlxuICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gZmFsc2VcbiAgJHJvb3RTY29wZS5zaG93U2lkZWJhciA9IC0+XG4gICAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9ICEkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlXG4gICAgJHJvb3RTY29wZS5zaWRlYmFyQ2xhc3MgPSAnZm9yY2Utc2hvdydcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29uc3RhbnQgJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnXG4gIG5ld1NlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MSdcbiMgIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9uZXctc2VydmVyJ1xuIyAgbmV3U2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDozMDAwL25ldy1zZXJ2ZXInXG4gIHJlZnJlc2hJbnRlcnZhbDogMTAwMDBcbn1cblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4ucnVuIChKb2JzU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkgLT5cbiAgSm9ic1NlcnZpY2UubGlzdEpvYnMoKVxuXG4gICMgJGludGVydmFsIC0+XG4gICMgICBKb2JzU2VydmljZS5saXN0Sm9icygpXG4gICMgLCBmbGlua0NvbmZpZy5yZWZyZXNoSW50ZXJ2YWxcblxuXG4jIC0
tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb25maWcgKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIC0+XG4gICRzdGF0ZVByb3ZpZGVyLnN0YXRlIFwib3ZlcnZpZXdcIixcbiAgICB1cmw6IFwiL292ZXJ2aWV3XCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnT3ZlcnZpZXdDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInJ1bm5pbmctam9ic1wiLFxuICAgIHVybDogXCIvcnVubmluZy1qb2JzXCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy1qb2JzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnUnVubmluZ0pvYnNDb250cm9sbGVyJ1xuICBcbiAgLnN0YXRlIFwiY29tcGxldGVkLWpvYnNcIixcbiAgICB1cmw6IFwiL2NvbXBsZXRlZC1qb2JzXCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvY29tcGxldGVkLWpvYnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iXCIsXG4gICAgdXJsOiBcIi9qb2JzL3tqb2JpZH1cIlxuICAgIGFic3RyYWN0OiB0cnVlXG4gICAgdmlld3M6XG4gICAgICBtYWlu
OlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuXCIsXG4gICAgdXJsOiBcIlwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm5vZGVcIixcbiAgICB1cmw6IFwiL3tub2RlaWQ6aW50fVwiXG4gICAgdmlld3M6XG4gICAgICBub2RlOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZUNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi50aW1lbGluZVwiLFxuICAgIHVybDogXCIvdGltZWxpbmVcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIixcbiAgICB1cmw6IFwiL3t2ZXJ0ZXhJZH1cIlxuICAgIHZpZXdzOlxuICAgICAgdmVydGV4OlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50a
W1lbGluZS52ZXJ0ZXguaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5zdGF0aXN0aWNzXCIsXG4gICAgdXJsOiBcIi9zdGF0aXN0aWNzXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnN0YXRpc3RpY3MuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5leGNlcHRpb25zXCIsXG4gICAgdXJsOiBcIi9leGNlcHRpb25zXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmV4Y2VwdGlvbnMuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5jb25maWdcIixcbiAgICB1cmw6IFwiL2NvbmZpZ1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5jb25maWcuaHRtbFwiXG5cbiAgJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZSBcIi9vdmVydmlld1wiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pLnJ1bihmdW5jdGlvbigkcm9vdFNjb3BlKSB7XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZTtcbiAgcmV0dXJuICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSBmdW5jdGlvbigpIHtcbiAgIC
Akcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGU7XG4gICAgcmV0dXJuICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnO1xuICB9O1xufSkuY29uc3RhbnQoJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnLFxuICBuZXdTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnLFxuICByZWZyZXNoSW50ZXJ2YWw6IDEwMDAwXG59KS5ydW4oZnVuY3Rpb24oSm9ic1NlcnZpY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIHtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCk7XG59KS5jb25maWcoZnVuY3Rpb24oJHN0YXRlUHJvdmlkZXIsICR1cmxSb3V0ZXJQcm92aWRlcikge1xuICAkc3RhdGVQcm92aWRlci5zdGF0ZShcIm92ZXJ2aWV3XCIsIHtcbiAgICB1cmw6IFwiL292ZXJ2aWV3XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvb3ZlcnZpZXcuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnT3ZlcnZpZXdDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJydW5uaW5nLWpvYnNcIiwge1xuICAgIHVybDogXCIvcnVubmluZy1qb2JzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9ydW5uaW5nLWpvYnMuaHRtbFwiLFx
uICAgICAgICBjb250cm9sbGVyOiAnUnVubmluZ0pvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJjb21wbGV0ZWQtam9ic1wiLCB7XG4gICAgdXJsOiBcIi9jb21wbGV0ZWQtam9ic1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvY29tcGxldGVkLWpvYnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2JcIiwge1xuICAgIHVybDogXCIvam9icy97am9iaWR9XCIsXG4gICAgYWJzdHJhY3Q6IHRydWUsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnU2luZ2xlSm9iQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuXCIsIHtcbiAgICB1cmw6IFwiXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuLm5vZGVcIiwge1xuICAgIHVybDogXCIve25v
ZGVpZDppbnR9XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG5vZGU6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi50aW1lbGluZVwiLCB7XG4gICAgdXJsOiBcIi90aW1lbGluZVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7XG4gICAgdXJsOiBcIi97dmVydGV4SWR9XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIHZlcnRleDoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS52ZXJ0ZXguaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIiwge1xuICAgIHVybDogXCIvc3RhdGlzdGljc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnN0YXRpc3RpY3MuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuI
CB9KS5zdGF0ZShcInNpbmdsZS1qb2IuZXhjZXB0aW9uc1wiLCB7XG4gICAgdXJsOiBcIi9leGNlcHRpb25zXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5jb25maWdcIiwge1xuICAgIHVybDogXCIvY29uZmlnXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuY29uZmlnLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIHJldHVybiAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlKFwiL292ZXJ2aWV3XCIpO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIG
ZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2JzTGFiZWwnLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHRyYW5zY2x1ZGU6IHRydWVcbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6IFwiQFwiXG5cbiAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbEN
sYXNzKCknPjxuZy10cmFuc2NsdWRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCJcbiAgXG4gIGxpbms6IChzY29wZSwgZWxlbWVudCwgYXR0cnMpIC0+XG4gICAgc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IC0+XG4gICAgICAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnaW5kaWNhdG9yUHJpbWFyeScsIChKb2JzU2VydmljZSkgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6ICdAJ1xuXG4gIHRlbXBsYXRlOiBcIjxpIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJyAvPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2ZhIGZhLWNpcmNsZSBpbmRpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3RhYmxlUHJvcGVydHknLCAtPlxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOlxuICAgIHZhbHVlOiAnPSdcblxuICB0ZW1wbGF0ZTogXCI8dGQgdGl0
bGU9XFxcInt7dmFsdWUgfHwgJ05vbmUnfX1cXFwiPnt7dmFsdWUgfHwgJ05vbmUnfX08L3RkPlwiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ2JzTGFiZWwnLCBmdW5jdGlvbihKb2JzU2VydmljZSkge1xuICByZXR1cm4ge1xuICAgIHRyYW5zY2x1ZGU6IHRydWUsXG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6IFwiQFwiXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8c3BhbiB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKSc+PG5nLXRyYW5zY2x1ZGU+PC9uZy10cmFuc2NsdWRlPjwvc3Bhbj5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdpbmRpY2F0b3JQcmltYXJ5JywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICBnZXRMYWJlbENsYXNzOiBcIiZcIixcbiAgICAgIHN0YXR1czogJ0AnXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8a
SB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnZmEgZmEtY2lyY2xlIGluZGljYXRvciBpbmRpY2F0b3ItJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0YWJsZVByb3BlcnR5JywgZnVuY3Rpb24oKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgdmFsdWU6ICc9J1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIH
RoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmZpbHRlciBcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCAoYW5ndWxhck1vbWVudENvbmZpZykgLT5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSAtPlxuICAgIHJldHVybiBcIlwiICBpZiB0eXBlb2YgdmFsdWUgaXM
gXCJ1bmRlZmluZWRcIiBvciB2YWx1ZSBpcyBudWxsXG5cbiAgICBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7IHRyaW06IGZhbHNlIH0pXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5maWx0ZXIoXCJhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRcIiwgZnVuY3Rpb24oYW5ndWxhck1vbWVudENvbmZpZykge1xuICB2YXIgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyO1xuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIgPSBmdW5jdGlvbih2YWx1ZSwgZm9ybWF0LCBkdXJhdGlvbkZvcm1hdCkge1xuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwidW5kZWZpbmVkXCIgfHwgdmFsdWUgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiBcIlwiO1xuICAgIH1cbiAgICByZXR1cm4gbW9tZW50LmR1cmF0aW9uKHZhbHVlLCBmb3JtYXQpLmZvcm1hdChkdXJhdGlvbkZvcm1hdCwge1xuICAgICAgdHJpbTogZmFsc2VcbiAgICB9KTtcbiAgfTtcbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzO1xuICByZXR1cm4gYW1EdXJhdGlvbkZvcm1hdEV4
dGVuZGVkRmlsdGVyO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0a
GUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmNvbnRyb2xsZXIgJ1J1bm5pbmdKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ3J1bm5pbmcnKVxuXG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuICAkc2NvcGUuJG9uICckZGVzdHJveScsIC0+XG4gICAgSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcblxuICAkc2NvcGUuam9iT2JzZXJ2ZXIoKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZX
J2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnU2luZ2xlSm9iQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHJvb3RTY29wZSkgLT5cbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkXG4gICRyb290U2NvcGUuam9iID0gbnVsbFxuXG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuIChkYXRhKSAtPlxuICAgICRyb290U2NvcGUuam9iID0gZGF0YVxuXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbkNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmxvYWRQbGFuKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUucGxhbiA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbk5vZGVDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnN
TZXJ2aWNlKSAtPlxuICAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZFxuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KClcblxuICBKb2JzU2VydmljZS5nZXROb2RlKCRzY29wZS5ub2RlaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLm5vZGUgPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy5qb2JpZCwgJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS52ZXJ0ZXggPSBkYXRhXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5jb250cm9sbGVyKCdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJyk7XG4gIH07XG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4g
Sm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJyk7XG4gIH07XG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdTaW5nbGVKb2JDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRyb290U2NvcGUpIHtcbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkO1xuICAkcm9vdFNjb3BlLmpvYiA9IG51bGw7XG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5qb2IgPSBkY
XRhO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRyb290U2NvcGUuam9iID0gbnVsbDtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiBKb2JzU2VydmljZS5sb2FkUGxhbigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkc2NvcGUucGxhbiA9IGRhdGE7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUGxhbk5vZGVDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLm5vZGVpZCA9ICRzdGF0ZVBhcmFtcy5ub2RlaWQ7XG4gICRzY29wZS5zdGF0ZUxpc3QgPSBKb2JzU2VydmljZS5zdGF0ZUxpc3QoKTtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5ub2RlID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy5qb2JpZCwgJHN0YXRlUG
FyYW1zLnZlcnRleElkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlLnZlcnRleCA9IGRhdGE7XG4gIH0pO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyB
PRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3ZlcnRleCcsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCJcblxuICBzY29wZTpcbiAgICBkYXRhOiBcIj1cIlxuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF1cblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVyAtIDE2KVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEuZ3JvdXB2ZXJ0ZXguZ3JvdXBtZW1iZXJzLCAodmVydGV4LCBpKSAtPlxuICAgICAgICB2VGltZSA9IGRhdGEudmVydGljZXRpbWVzW3ZlcnRleC52ZXJ0ZXhpZF1cblxuICAgICAgICB0ZXN0RGF0YS5wdXNoIHtcbiAgICAgICAgICBsYWJl
bDogXCIje3ZlcnRleC52ZXJ0ZXhpbnN0YW5jZW5hbWV9ICgje2l9KVwiXG4gICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiIzY2NlwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiU0NIRURVTEVEXCJdICogMTAwXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkRFUExPWUlOR1wiXSAqIDEwMFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJEZXBsb3lpbmdcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjYWFhXCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJERVBMT1lJTkdcIl0gKiAxMDBcbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZUaW1lW1wiUlVOTklOR1wiXSAqIDEwMFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJSdW5uaW5nXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2RkZFwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiUlVOTklOR1wiXSAqIDEwMFxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJGSU5JU0hFRFwiXSAqIDEwMFxuICAgICAgICAgICAgfVxuICAgICAgICAgIF1cbiAgICAgICAgfVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKVxuI
CAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDFcbiAgICAgICAgdGlja1NpemU6IDFcbiAgICAgIH0pXG4gICAgICAucHJlZml4KFwic2luZ2xlXCIpXG4gICAgICAubGFiZWxGb3JtYXQoKGxhYmVsKSAtPlxuICAgICAgICBsYWJlbFxuICAgICAgKVxuICAgICAgLm1hcmdpbih7IGxlZnQ6IDEwMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgyNClcbiAgICAgIC5yZWxhdGl2ZVRpbWUoKVxuXG4gICAgICBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpXG4gICAgICAuZGF0dW0odGVzdERhdGEpXG4gICAgICAuY2FsbChjaGFydClcblxuICAgICAgc3ZnRyA9IHN2Zy5zZWxlY3QoXCJnXCIpXG5cbiAgICAgIGJib3ggPSBzdmdHWzBdWzBdLmdldEJCb3goKVxuICAgICAgc3ZnLmF0dHIoJ2hlaWdodCcsIGJib3guaGVpZ2h0ICsgMzApXG5cbiAgICBhbmFseXplVGltZShzY29wZS5kYXRhKVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0aW1lbGluZScsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGpvYjogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW
0sIGF0dHJzKSAtPlxuICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcgLSAxNilcblxuICAgIHRyYW5zbGF0ZUxhYmVsID0gKGxhYmVsKSAtPlxuICAgICAgbGFiZWwucmVwbGFjZShcIiZndDtcIiwgXCI+XCIpXG5cbiAgICBhbmFseXplVGltZSA9IChkYXRhKSAtPlxuICAgICAgdGVzdERhdGEgPSBbXVxuXG4gICAgICBhbmd1bGFyLmZvckVhY2ggZGF0YS5vbGRWLmdyb3VwdmVydGljZXMsICh2ZXJ0ZXgpIC0+XG4gICAgICAgIHZUaW1lID0gZGF0YS5vbGRWLmdyb3VwdmVydGljZXRpbWVzW3ZlcnRleC5ncm91cHZlcnRleGlkXVxuXG4gICAgICAgICMgY29uc29sZS5sb2cgdlRpbWUsIHZlcnRleC5ncm91cHZlcnRleGlkXG5cbiAgICAgICAgdGVzdERhdGEucHVzaCBcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgbGFiZWw6IHRyYW5zbGF0ZUxhYmVsKHZlcnRleC5ncm91cHZlcnRleG5hbWUpXG4gICAgICAgICAgICBjb2xvcjogXCIjNjJjZGVhXCJcbiAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiU1RBUlRFRFwiXVxuICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZUaW1lW1wiRU5ERURcIl1cbiAgICAgICAgICAgIGxpbms6IHZlcnRleC5ncm91cHZlcnRleGlkXG4gICAgICAgICAgXVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWV
saW5lKCkuc3RhY2soKS5jbGljaygoZCwgaSwgZGF0dW0pIC0+XG4gICAgICAgICRzdGF0ZS5nbyBcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHsgam9iaWQ6IGRhdGEuamlkLCB2ZXJ0ZXhJZDogZC5saW5rIH1cblxuICAgICAgKVxuICAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICAgICAgIyB0aWNrVGltZTogZDMudGltZS5zZWNvbmRcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDAuNVxuICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJtYWluXCIpXG4gICAgICAubWFyZ2luKHsgbGVmdDogMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgyNClcbiAgICAgIC5zaG93Qm9yZGVyTGluZSgpXG4gICAgICAuc2hvd0hvdXJUaW1lbGluZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgICBzdmdHID0gc3ZnLnNlbGVjdChcImdcIilcblxuICAgICAgYmJveCA9IHN2Z0dbMF1bMF0uZ2V0QkJveCgpXG4gICAgICBzdmcuYXR0cignaGVpZ2h0JywgYmJveC5oZWlnaHQgKyAzMClcblxuICAgIHNjb3BlLiR3YXRjaCBhdHRycy5qb2IsIChkYXRhKSAtPlxuICAgICAgYW5hbHl6ZVRpbWUoZGF0YSkgaWYgZGF0YVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICdqb2JQbGFuJywgKCR0aW1lb3V0KSAtPlxuICB0ZW1wbGF0ZTogXCJcbiAgICA8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+XG4gICAgPHN2ZyBjbGFzcz0ndG1wJyB3aWR0aD0nMScgaGVpZ2h0PScxJz48ZyAvPjwvc3ZnPlxuICAgIDxkaXYgY2xhc3M9J2J0bi1ncm91cCB6b29tLWJ1dHRvbnMnPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLWluJyBuZy1jbGljaz0nem9vbUluKCknPjxpIGNsYXNzPSdmYSBmYS1wbHVzJyAvPjwvYT5cbiAgICAgIDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+XG4gICAgPC9kaXY+XCJcblxuICBzY29wZTpcbiAgICBwbGFuOiAnPSdcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIG1haW5ab29tID0gZDMuYmVoYXZpb3Iuem9vbSgpXG4gICAgc3ViZ3JhcGhzID0gW11cbiAgICBqb2JpZCA9IGF0dHJzLmpvYmlkXG5cbiAgICBtYWluU3ZnRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuICAgIG1haW5HID0gZWxlbS5jaGlsZHJlbigpLmNoaWxkcmVuKClbMF1cbiAgICBtYWluVG1wRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVsxXVxuXG4gICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1ha
W5TdmdFbGVtZW50KVxuICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpXG4gICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpXG5cbiAgICAjIGFuZ3VsYXIuZWxlbWVudChtYWluRykuZW1wdHkoKVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpXG5cbiAgICBzY29wZS56b29tSW4gPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKSA8IDIuOTlcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIHpvb20gb2JqZWN0XG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW5ab29tLnNjYWxlIG1haW5ab29tLnNjYWxlKCkgKyAwLjFcbiAgICAgICAgbWFpblpvb20udHJhbnNsYXRlIFsgdjEsIHYyIF1cbiAgICAgICAgXG4gICAgICAgICMgVHJhbnNmb3JtIHN2Z1xuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXC
IgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCJcblxuICAgIHNjb3BlLnpvb21PdXQgPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKSA+IDAuMzFcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIG1haW5ab29tIG9iamVjdFxuICAgICAgICBtYWluWm9vbS5zY2FsZSBtYWluWm9vbS5zY2FsZSgpIC0gMC4xXG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZSBbIHYxLCB2MiBdXG4gICAgICAgIFxuICAgICAgICAjIFRyYW5zZm9ybSBzdmdcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiXG5cbiAgICAjY3JlYXRlIGEgbGFiZWwgb2YgYW4gZWRnZVxuICAgIGNyZWF0ZUxhYmVsRWRnZSA9IChlbCkgLT5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIlwiXG4gICAgICBpZiBlbC5zaGlwX3N0cmF0ZWd5PyBvciBlbC5sb2NhbF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSA
rPSBcIjxkaXYgY2xhc3M9J2VkZ2UtbGFiZWwnPlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneSAgaWYgZWwuc2hpcF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiAoXCIgKyBlbC50ZW1wX21vZGUgKyBcIilcIiAgdW5sZXNzIGVsLnRlbXBfbW9kZSBpcyBgdW5kZWZpbmVkYFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiLDxicj5cIiArIGVsLmxvY2FsX3N0cmF0ZWd5ICB1bmxlc3MgZWwubG9jYWxfc3RyYXRlZ3kgaXMgYHVuZGVmaW5lZGBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiXG4gICAgICBsYWJlbFZhbHVlXG5cblxuICAgICMgdHJ1ZSwgaWYgdGhlIG5vZGUgaXMgYSBzcGVjaWFsIG5vZGUgZnJvbSBhbiBpdGVyYXRpb25cbiAgICBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlID0gKGluZm8pIC0+XG4gICAgICAoaW5mbyBpcyBcInBhcnRpYWxTb2x1dGlvblwiIG9yIGluZm8gaXMgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIgb3IgaW5mbyBpcyBcIndvcmtzZXRcIiBvciBpbmZvIGlzIFwibmV4dFdvcmtzZXRcIiBvciBpbmZvIGlzIFwic29sdXRpb25TZXRcIiBvciBpbmZvIGlzIFwic29sdXRpb25EZWx0YVwiKVxuXG4gICAgZ2V0Tm9kZVR5cGUgPSAoZWwsIGluZm8pIC0+XG4gICAgICBpZiBpbmZvIGlzIFwibWlycm9yXCJcbiAgICAgICAgJ25vZGUtbWlycm9yJ1xuXG4gICAgICBlbHNlIGlmIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbylcbiAg
ICAgICAgJ25vZGUtaXRlcmF0aW9uJ1xuXG4gICAgICBlbHNlXG4gICAgICAgIGlmIGVsLnBhY3QgaXMgXCJEYXRhIFNvdXJjZVwiXG4gICAgICAgICAgJ25vZGUtc291cmNlJ1xuICAgICAgICBlbHNlIGlmIGVsLnBhY3QgaXMgXCJEYXRhIFNpbmtcIlxuICAgICAgICAgICdub2RlLXNpbmsnXG4gICAgICAgIGVsc2VcbiAgICAgICAgICAnbm9kZS1ub3JtYWwnXG4gICAgICBcbiAgICAjIGNyZWF0ZXMgdGhlIGxhYmVsIG9mIGEgbm9kZSwgaW4gaW5mbyBpcyBzdG9yZWQsIHdoZXRoZXIgaXQgaXMgYSBzcGVjaWFsIG5vZGUgKGxpa2UgYSBtaXJyb3IgaW4gYW4gaXRlcmF0aW9uKVxuICAgIGNyZWF0ZUxhYmVsTm9kZSA9IChlbCwgaW5mbywgbWF4VywgbWF4SCkgLT5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIjxhIGhyZWY9JyMvam9icy9cIiArIGpvYmlkICsgXCIvXCIgKyBlbC5pZCArIFwiJyBjbGFzcz0nbm9kZS1sYWJlbCBcIiArIGdldE5vZGVUeXBlKGVsLCBpbmZvKSArIFwiJz5cIlxuXG4gICAgICAjIE5vZGVuYW1lXG4gICAgICBpZiBpbmZvIGlzIFwibWlycm9yXCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5NaXJyb3Igb2YgXCIgKyBlbC5wYWN0ICsgXCI8L2gzPlwiXG4gICAgICBlbHNlXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+XCIgKyBlbC5wYWN0ICsgXCI8L2gzPlwiXG4gICAgICBpZiBlbC5jb250ZW50cyBpcyBcIlwiX
G4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCJcIlxuICAgICAgZWxzZVxuICAgICAgICBzdGVwTmFtZSA9IGVsLmNvbnRlbnRzXG4gICAgICAgIFxuICAgICAgICAjIGNsZWFuIHN0ZXBOYW1lXG4gICAgICAgIHN0ZXBOYW1lID0gc2hvcnRlblN0cmluZyhzdGVwTmFtZSlcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNCBjbGFzcz0nc3RlcC1uYW1lJz5cIiArIHN0ZXBOYW1lICsgXCI8L2g0PlwiXG4gICAgICBcbiAgICAgICMgSWYgdGhpcyBub2RlIGlzIGFuIFwiaXRlcmF0aW9uXCIgd2UgbmVlZCBhIGRpZmZlcmVudCBwYW5lbC1ib2R5XG4gICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICBsYWJlbFZhbHVlICs9IGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbihlbC5pZCwgbWF4VywgbWF4SClcbiAgICAgIGVsc2VcbiAgICAgICAgXG4gICAgICAgICMgT3RoZXJ3aXNlIGFkZCBpbmZvcyAgICBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5cIiArIGluZm8gKyBcIiBOb2RlPC9oNT5cIiAgaWYgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlBhcmFsbGVsaXNtOiBcIiArIGVsLnBhcmFsbGVsaXNtICsgXCI8L2g1PlwiICB1bmxlc3MgZWwucGFyYWxsZWxpc20gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PkRyaXZlciBTdHJhdGVneTogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLmRyaXZlcl9zdHJhdGVneSkgKyBcIjwvaD
VcIiAgdW5sZXNzIGVsLmRyaXZlcl9zdHJhdGVneSBpcyBgdW5kZWZpbmVkYFxuICAgICAgXG4gICAgICBsYWJlbFZhbHVlICs9IFwiPC9hPlwiXG4gICAgICBsYWJlbFZhbHVlXG5cbiAgICAjIEV4dGVuZHMgdGhlIGxhYmVsIG9mIGEgbm9kZSB3aXRoIGFuIGFkZGl0aW9uYWwgc3ZnIEVsZW1lbnQgdG8gcHJlc2VudCB0aGUgaXRlcmF0aW9uLlxuICAgIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiA9IChpZCwgbWF4VywgbWF4SCkgLT5cbiAgICAgIHN2Z0lEID0gXCJzdmctXCIgKyBpZFxuXG4gICAgICBsYWJlbFZhbHVlID0gXCI8c3ZnIGNsYXNzPSdcIiArIHN2Z0lEICsgXCInIHdpZHRoPVwiICsgbWF4VyArIFwiIGhlaWdodD1cIiArIG1heEggKyBcIj48ZyAvPjwvc3ZnPlwiXG4gICAgICBsYWJlbFZhbHVlXG5cbiAgICAjIFNwbGl0IGEgc3RyaW5nIGludG8gbXVsdGlwbGUgbGluZXMgc28gdGhhdCBlYWNoIGxpbmUgaGFzIGxlc3MgdGhhbiAzMCBsZXR0ZXJzLlxuICAgIHNob3J0ZW5TdHJpbmcgPSAocykgLT5cbiAgICAgICMgbWFrZSBzdXJlIHRoYXQgbmFtZSBkb2VzIG5vdCBjb250YWluIGEgPCAoYmVjYXVzZSBvZiBodG1sKVxuICAgICAgaWYgcy5jaGFyQXQoMCkgaXMgXCI8XCJcbiAgICAgICAgcyA9IHMucmVwbGFjZShcIjxcIiwgXCImbHQ7XCIpXG4gICAgICAgIHMgPSBzLnJlcGxhY2UoXCI+XCIsIFwiJmd0O1wiKVxuICAgICAgc2JyID0gXCJcIlxuICAgICAgd2hpbGUgcy5sZW5ndGggPiAzMFx
uICAgICAgICBzYnIgPSBzYnIgKyBzLnN1YnN0cmluZygwLCAzMCkgKyBcIjxicj5cIlxuICAgICAgICBzID0gcy5zdWJzdHJpbmcoMzAsIHMubGVuZ3RoKVxuICAgICAgc2JyID0gc2JyICsgc1xuICAgICAgc2JyXG5cbiAgICBjcmVhdGVOb2RlID0gKGcsIGRhdGEsIGVsLCBpc1BhcmVudCA9IGZhbHNlLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgIyBjcmVhdGUgbm9kZSwgc2VuZCBhZGRpdGlvbmFsIGluZm9ybWF0aW9ucyBhYm91dCB0aGUgbm9kZSBpZiBpdCBpcyBhIHNwZWNpYWwgb25lXG4gICAgICBpZiBlbC5pZCBpcyBkYXRhLnBhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5uZXh0X3BhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIilcblxuICAg
ICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLndvcmtzZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwid29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIndvcmtzZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLm5leHRfd29ya3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0V29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRXb3Jrc2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5zb2x1dGlvbl9zZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25TZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvblNldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEuc29sdXRpb25fZGVsdGFcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sd
XRpb25EZWx0YVwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uRGVsdGFcIilcblxuICAgICAgZWxzZVxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJcIilcblxuICAgIGNyZWF0ZUVkZ2UgPSAoZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpIC0+XG4gICAgICB1bmxlc3MgZXhpc3RpbmdOb2Rlcy5pbmRleE9mKHByZWQuaWQpIGlzIC0xXG4gICAgICAgIGcuc2V0RWRnZSBwcmVkLmlkLCBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKHByZWQpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBhcnJvd2hlYWQ6ICdub3JtYWwnXG5cbiAgICAgIGVsc2VcbiAgICAgICAgbWlzc2luZ05vZGUgPSBzZWFyY2hGb3JOb2RlKGRhdGEsIHByZWQuaWQpXG4gICAgICAgIHVubGVzcyAhbWlzc2luZ05vZGUgb3IgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkIGlzIHRydWVcbiAgICAgICAgICBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgPSB0cnVlXG4gICAgICAgICAgZy5zZXROb2RlIG1pc3NpbmdOb2RlLmlkLFxuICAgICAgICAgICAgbGFiZWw6IG
NyZWF0ZUxhYmVsTm9kZShtaXNzaW5nTm9kZSwgXCJtaXJyb3JcIilcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUobWlzc2luZ05vZGUsICdtaXJyb3InKVxuXG4gICAgICAgICAgZy5zZXRFZGdlIG1pc3NpbmdOb2RlLmlkLCBlbC5pZCxcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UobWlzc2luZ05vZGUpXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuXG4gICAgbG9hZEpzb25Ub0RhZ3JlID0gKGcsIGRhdGEpIC0+XG4gICAgICBleGlzdGluZ05vZGVzID0gW11cblxuICAgICAgaWYgZGF0YS5ub2Rlcz9cbiAgICAgICAgIyBUaGlzIGlzIHRoZSBub3JtYWwganNvbiBkYXRhXG4gICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEubm9kZXNcblxuICAgICAgZWxzZVxuICAgICAgICAjIFRoaXMgaXMgYW4gaXRlcmF0aW9uLCB3ZSBub3cgc3RvcmUgc3BlY2lhbCBpdGVyYXRpb24gbm9kZXMgaWYgcG9zc2libGVcbiAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5zdGVwX2Z1bmN0aW9uXG4gICAgICAgIGlzUGFyZW50ID0gdHJ1ZVxuXG4gICAgICBmb3IgZWwgaW4gdG9JdGVyYXRlXG4gICAgICAgIG1heFcgPSAwXG4gICAgICAgIG1heEggPSAwXG5cbiAgICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvblxuICAgICAgICAgIHNnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoeyBtdWx0aWdyYXBoOiB0cnVlLCBjb21wb3VuZDogdHJ1ZSB
9KS5zZXRHcmFwaCh7XG4gICAgICAgICAgICBub2Rlc2VwOiAyMFxuICAgICAgICAgICAgZWRnZXNlcDogMFxuICAgICAgICAgICAgcmFua3NlcDogMjBcbiAgICAgICAgICAgIHJhbmtkaXI6IFwiTFJcIlxuICAgICAgICAgICAgbWFyZ2lueDogMTBcbiAgICAgICAgICAgIG1hcmdpbnk6IDEwXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgICAgc3ViZ3JhcGhzW2VsLmlkXSA9IHNnXG5cbiAgICAgICAgICBsb2FkSnNvblRvRGFncmUoc2csIGVsKVxuXG4gICAgICAgICAgciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpXG4gICAgICAgICAgZDN0bXBTdmcuc2VsZWN0KCdnJykuY2FsbChyLCBzZylcbiAgICAgICAgICBtYXhXID0gc2cuZ3JhcGgoKS53aWR0aFxuICAgICAgICAgIG1heEggPSBzZy5ncmFwaCgpLmhlaWdodFxuXG4gICAgICAgICAgYW5ndWxhci5lbGVtZW50KG1haW5UbXBFbGVtZW50KS5lbXB0eSgpXG5cbiAgICAgICAgY3JlYXRlTm9kZShnLCBkYXRhLCBlbCwgaXNQYXJlbnQsIG1heFcsIG1heEgpXG5cbiAgICAgICAgZXhpc3RpbmdOb2Rlcy5wdXNoIGVsLmlkXG4gICAgICAgIFxuICAgICAgICAjIGNyZWF0ZSBlZGdlcyBmcm9tIHByZWRlY2Vzc29ycyB0byBjdXJyZW50IG5vZGVcbiAgICAgICAgaWYgZWwucHJlZGVjZXNzb3JzP1xuICAgICAgICAgIGZvciBwcmVkIGluIGVsLnByZWRlY2Vzc29yc1xuICAgICAgICAgICAgY3JlYXRlRWRnZShnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZClc
blxuICAgICAgZ1xuXG4gICAgIyBzZWFyY2hlcyBpbiB0aGUgZ2xvYmFsIEpTT05EYXRhIGZvciB0aGUgbm9kZSB3aXRoIHRoZSBnaXZlbiBpZFxuICAgIHNlYXJjaEZvck5vZGUgPSAoZGF0YSwgbm9kZUlEKSAtPlxuICAgICAgZm9yIGkgb2YgZGF0YS5ub2Rlc1xuICAgICAgICBlbCA9IGRhdGEubm9kZXNbaV1cbiAgICAgICAgcmV0dXJuIGVsICBpZiBlbC5pZCBpcyBub2RlSURcbiAgICAgICAgXG4gICAgICAgICMgbG9vayBmb3Igbm9kZXMgdGhhdCBhcmUgaW4gaXRlcmF0aW9uc1xuICAgICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICAgIGZvciBqIG9mIGVsLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgICAgIHJldHVybiBlbC5zdGVwX2Z1bmN0aW9uW2pdICBpZiBlbC5zdGVwX2Z1bmN0aW9uW2pdLmlkIGlzIG5vZGVJRFxuXG4gICAgZHJhd0dyYXBoID0gKGRhdGEpIC0+XG4gICAgICBnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoeyBtdWx0aWdyYXBoOiB0cnVlLCBjb21wb3VuZDogdHJ1ZSB9KS5zZXRHcmFwaCh7XG4gICAgICAgIG5vZGVzZXA6IDcwXG4gICAgICAgIGVkZ2VzZXA6IDBcbiAgICAgICAgcmFua3NlcDogNTBcbiAgICAgICAgcmFua2RpcjogXCJMUlwiXG4gICAgICAgIG1hcmdpbng6IDQwXG4gICAgICAgIG1hcmdpbnk6IDQwXG4gICAgICAgIH0pXG5cbiAgICAgIGxvYWRKc29uVG9EYWdyZShnLCBkYXRhKVxuXG4gICAgICByZW5kZXJlciA9IG5ldyBkYWdyZUQzLnJlbmRlc
igpXG4gICAgICBkM21haW5TdmdHLmNhbGwocmVuZGVyZXIsIGcpXG5cbiAgICAgIGZvciBpLCBzZyBvZiBzdWJncmFwaHNcbiAgICAgICAgZDNtYWluU3ZnLnNlbGVjdCgnc3ZnLnN2Zy0nICsgaSArICcgZycpLmNhbGwocmVuZGVyZXIsIHNnKVxuXG4gICAgICBuZXdTY2FsZSA9IDAuNVxuXG4gICAgICB4Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS53aWR0aCgpIC0gZy5ncmFwaCgpLndpZHRoICogbmV3U2NhbGUpIC8gMilcbiAgICAgIHlDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLmhlaWdodCgpIC0gZy5ncmFwaCgpLmhlaWdodCAqIG5ld1NjYWxlKSAvIDIpXG5cbiAgICAgIG1haW5ab29tLnNjYWxlKG5ld1NjYWxlKS50cmFuc2xhdGUoW3hDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXRdKVxuXG4gICAgICBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB4Q2VudGVyT2Zmc2V0ICsgXCIsIFwiICsgeUNlbnRlck9mZnNldCArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIilcblxuICAgICAgbWFpblpvb20ub24oXCJ6b29tXCIsIC0+XG4gICAgICAgIGV2ID0gZDMuZXZlbnRcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgZXYudHJhbnNsYXRlICsgXCIpIHNjYWxlKFwiICsgZX
Yuc2NhbGUgKyBcIilcIlxuICAgICAgKVxuICAgICAgbWFpblpvb20oZDNtYWluU3ZnKVxuXG4gICAgc2NvcGUuJHdhdGNoIGF0dHJzLnBsYW4sIChuZXdQbGFuKSAtPlxuICAgICAgZHJhd0dyYXBoKG5ld1BsYW4pIGlmIG5ld1BsYW5cblxuICAgIHJldHVyblxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZGlyZWN0aXZlKCd2ZXJ0ZXgnLCBmdW5jdGlvbigkc3RhdGUpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZSBzZWNvbmRhcnknIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBkYXRhOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5lclcsIHN2Z0VsO1xuICAgICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcgLSAxNik7XG4gICAgICBhbmFseXplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGJib3gsIGNoYXJ0LCBzdmcsIHN2Z0csIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICBhbmd1bGFyLmZvckVhY2goZGF0YS5ncm91cHZlcnRleC5ncm91cG1lbWJlcnMsIGZ1bmN0aW9uKHZlcnRleCw
gaSkge1xuICAgICAgICAgIHZhciB2VGltZTtcbiAgICAgICAgICB2VGltZSA9IGRhdGEudmVydGljZXRpbWVzW3ZlcnRleC52ZXJ0ZXhpZF07XG4gICAgICAgICAgcmV0dXJuIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgICAgbGFiZWw6IHZlcnRleC52ZXJ0ZXhpbnN0YW5jZW5hbWUgKyBcIiAoXCIgKyBpICsgXCIpXCIsXG4gICAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiIzY2NlwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiU0NIRURVTEVEXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkRFUExPWUlOR1wiXSAqIDEwMFxuICAgICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiRGVwbG95aW5nXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiI2FhYVwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiREVQTE9ZSU5HXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIlJVTk5JTkdcIl0gKiAxMDBcbiAgICAgICAgICAgICAgfSwge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlJ1bm5pbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRp
bWVbXCJSVU5OSU5HXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkZJTklTSEVEXCJdICogMTAwXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIF1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLnRpY2tGb3JtYXQoe1xuICAgICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKSxcbiAgICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgICB9KS5wcmVmaXgoXCJzaW5nbGVcIikubGFiZWxGb3JtYXQoZnVuY3Rpb24obGFiZWwpIHtcbiAgICAgICAgICByZXR1cm4gbGFiZWw7XG4gICAgICAgIH0pLm1hcmdpbih7XG4gICAgICAgICAgbGVmdDogMTAwLFxuICAgICAgICAgIHJpZ2h0OiAwLFxuICAgICAgICAgIHRvcDogMCxcbiAgICAgICAgICBib3R0b206IDBcbiAgICAgICAgfSkuaXRlbUhlaWdodCgyNCkucmVsYXRpdmVUaW1lKCk7XG4gICAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbCkuZGF0dW0odGVzdERhdGEpLmNhbGwoY2hhcnQpO1xuICAgICAgICBzdmdHID0gc3ZnLnNlbGVjdChcImdcIik7XG4gICAgICAgIGJib3ggPSBzdmdHWzBdWzBdLmdldEJCb3goKTtcbiAgICAgICAgcmV0dXJuIHN2Zy5hdHRyKCdoZWlnaHQnLCBiYm94LmhlaWdodCArIDMwKTtcbiAgICAgIH07XG4gICAgICBhbmFseXplVGltZShzY29wZS5kYXRhKTtcbiAgICB9XG4gIH07XG59KS5kaXJlY3RpdmUoJ
3RpbWVsaW5lJywgZnVuY3Rpb24oJHN0YXRlKSB7XG4gIHJldHVybiB7XG4gICAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUnIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBqb2I6IFwiPVwiXG4gICAgfSxcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbSwgYXR0cnMpIHtcbiAgICAgIHZhciBhbmFseXplVGltZSwgY29udGFpbmVyVywgc3ZnRWwsIHRyYW5zbGF0ZUxhYmVsO1xuICAgICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcgLSAxNik7XG4gICAgICB0cmFuc2xhdGVMYWJlbCA9IGZ1bmN0aW9uKGxhYmVsKSB7XG4gICAgICAgIHJldHVybiBsYWJlbC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIik7XG4gICAgICB9O1xuICAgICAgYW5hbHl6ZVRpbWUgPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIHZhciBiYm94LCBjaGFydCwgc3ZnLCBzdmdHLCB0ZXN0RGF0YTtcbiAgICAgICAgdGVzdERhdGEgPSBbXTtcbiAgICAgICAgYW5ndWxhci5mb3JFYWNoKGRhdGEub2xkVi5ncm91cHZlcnRpY2VzLCBmdW5jdGlvbih2ZXJ0ZXgpIHtcbiAgICAgICAgICB2YXIgdlRpbWU7XG4gICAgICAgICAgdlRpbWUgPSBkYXRhLm9sZFYuZ3JvdXB2ZXJ0aWNldGltZXNbdmVydGV4Lmdyb3VwdmVydG
V4aWRdO1xuICAgICAgICAgIHJldHVybiB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogdHJhbnNsYXRlTGFiZWwodmVydGV4Lmdyb3VwdmVydGV4bmFtZSksXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiIzYyY2RlYVwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiU1RBUlRFRFwiXSxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJFTkRFRFwiXSxcbiAgICAgICAgICAgICAgICBsaW5rOiB2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhpZFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICBdXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5jbGljayhmdW5jdGlvbihkLCBpLCBkYXR1bSkge1xuICAgICAgICAgIHJldHVybiAkc3RhdGUuZ28oXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7XG4gICAgICAgICAgICBqb2JpZDogZGF0YS5qaWQsXG4gICAgICAgICAgICB2ZXJ0ZXhJZDogZC5saW5rXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pLnRpY2tGb3JtYXQoe1xuICAgICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKSxcbiAgICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgICB9KS5wcmVmaXgoXCJtYWluXCIpLm1hcmdpbih7XG4gICAgICAgICAgbGVmdDogMCxcbiAgICAgICA
gICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMjQpLnNob3dCb3JkZXJMaW5lKCkuc2hvd0hvdXJUaW1lbGluZSgpO1xuICAgICAgICBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpLmRhdHVtKHRlc3REYXRhKS5jYWxsKGNoYXJ0KTtcbiAgICAgICAgc3ZnRyA9IHN2Zy5zZWxlY3QoXCJnXCIpO1xuICAgICAgICBiYm94ID0gc3ZnR1swXVswXS5nZXRCQm94KCk7XG4gICAgICAgIHJldHVybiBzdmcuYXR0cignaGVpZ2h0JywgYmJveC5oZWlnaHQgKyAzMCk7XG4gICAgICB9O1xuICAgICAgc2NvcGUuJHdhdGNoKGF0dHJzLmpvYiwgZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgIHJldHVybiBhbmFseXplVGltZShkYXRhKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdqb2JQbGFuJywgZnVuY3Rpb24oJHRpbWVvdXQpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+IDxzdmcgY2xhc3M9J3RtcCcgd2lkdGg9JzEnIGhlaWdodD0nMSc+PGcgLz48L3N2Zz4gPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1pbicgbmctY2xpY2s9J3pvb21JbigpJz48aSBjbGFzcz0nZmEgZmEtcGx1
cycgLz48L2E+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+IDwvZGl2PlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBwbGFuOiAnPSdcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGNvbnRhaW5lclcsIGNyZWF0ZUVkZ2UsIGNyZWF0ZUxhYmVsRWRnZSwgY3JlYXRlTGFiZWxOb2RlLCBjcmVhdGVOb2RlLCBkM21haW5TdmcsIGQzbWFpblN2Z0csIGQzdG1wU3ZnLCBkcmF3R3JhcGgsIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiwgZ2V0Tm9kZVR5cGUsIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUsIGpvYmlkLCBsb2FkSnNvblRvRGFncmUsIG1haW5HLCBtYWluU3ZnRWxlbWVudCwgbWFpblRtcEVsZW1lbnQsIG1haW5ab29tLCBzZWFyY2hGb3JOb2RlLCBzaG9ydGVuU3RyaW5nLCBzdWJncmFwaHM7XG4gICAgICBtYWluWm9vbSA9IGQzLmJlaGF2aW9yLnpvb20oKTtcbiAgICAgIHN1YmdyYXBocyA9IFtdO1xuICAgICAgam9iaWQgPSBhdHRycy5qb2JpZDtcbiAgICAgIG1haW5TdmdFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXTtcbiAgICAgIG1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdO1xuICAgICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1ha
W5TdmdFbGVtZW50KTtcbiAgICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpO1xuICAgICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpO1xuICAgICAgc2NvcGUuem9vbUluID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB0cmFuc2xhdGUsIHYxLCB2MjtcbiAgICAgICAgaWYgKG1haW5ab29tLnNjYWxlKCkgPCAyLjk5KSB7XG4gICAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKCk7XG4gICAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgbWFpblpvb20uc2NhbGUobWFpblpvb20uc2NhbGUoKSArIDAuMSk7XG4gICAgICAgICAgbWFpblpvb20udHJhbnNsYXRlKFt2MSwgdjJdKTtcbiAgICAgICAgICByZXR1cm4gZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIH
Njb3BlLnpvb21PdXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHRyYW5zbGF0ZSwgdjEsIHYyO1xuICAgICAgICBpZiAobWFpblpvb20uc2NhbGUoKSA+IDAuMzEpIHtcbiAgICAgICAgICBtYWluWm9vbS5zY2FsZShtYWluWm9vbS5zY2FsZSgpIC0gMC4xKTtcbiAgICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKTtcbiAgICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUoW3YxLCB2Ml0pO1xuICAgICAgICAgIHJldHVybiBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlTGFiZWxFZGdlID0gZnVuY3Rpb24oZWwpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWU7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIlwiO1xuICAgICAgICBpZiAoKGVsLnNoaXBfc3RyYXRlZ3kgIT0gbnVsbCkgfHwgKGVsLmxvY2FsX3N0cmF0ZWd5ICE9IG51bGwpKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxkaXYgY2xhc3M9J2V
kZ2UtbGFiZWwnPlwiO1xuICAgICAgICAgIGlmIChlbC5zaGlwX3N0cmF0ZWd5ICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLnRlbXBfbW9kZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiIChcIiArIGVsLnRlbXBfbW9kZSArIFwiKVwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwubG9jYWxfc3RyYXRlZ3kgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiw8YnI+XCIgKyBlbC5sb2NhbF9zdHJhdGVneTtcbiAgICAgICAgICB9XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUgPSBmdW5jdGlvbihpbmZvKSB7XG4gICAgICAgIHJldHVybiBpbmZvID09PSBcInBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwibmV4dFBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwid29ya3NldFwiIHx8IGluZm8gPT09IFwibmV4dFdvcmtzZXRcIiB8fCBpbmZvID09PSBcInNvbHV0aW9uU2V0XCIgfHwgaW5mbyA9PT0gXCJzb2x1dGlvbkRlbHRhXCI7XG4gICAgICB9O1xuICAgICAgZ2V0Tm9kZVR5cGUgPSBmdW5jdGlvbihlbCwgaW5mbykge1xuICAgICAgICBpZiAoaW5m
byA9PT0gXCJtaXJyb3JcIikge1xuICAgICAgICAgIHJldHVybiAnbm9kZS1taXJyb3InO1xuICAgICAgICB9IGVsc2UgaWYgKGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbykpIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtaXRlcmF0aW9uJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpZiAoZWwucGFjdCA9PT0gXCJEYXRhIFNvdXJjZVwiKSB7XG4gICAgICAgICAgICByZXR1cm4gJ25vZGUtc291cmNlJztcbiAgICAgICAgICB9IGVsc2UgaWYgKGVsLnBhY3QgPT09IFwiRGF0YSBTaW5rXCIpIHtcbiAgICAgICAgICAgIHJldHVybiAnbm9kZS1zaW5rJztcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuICdub2RlLW5vcm1hbCc7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlTGFiZWxOb2RlID0gZnVuY3Rpb24oZWwsIGluZm8sIG1heFcsIG1heEgpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWUsIHN0ZXBOYW1lO1xuICAgICAgICBsYWJlbFZhbHVlID0gXCI8YSBocmVmPScjL2pvYnMvXCIgKyBqb2JpZCArIFwiL1wiICsgZWwuaWQgKyBcIicgY2xhc3M9J25vZGUtbGFiZWwgXCIgKyBnZXROb2RlVHlwZShlbCwgaW5mbykgKyBcIic+XCI7XG4gICAgICAgIGlmIChpbmZvID09PSBcIm1pcnJvclwiKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5NaXJyb3Igb2YgXCIgKyBlbC5wYWN0ICsgXCI8L
2gzPlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+XCIgKyBlbC5wYWN0ICsgXCI8L2gzPlwiO1xuICAgICAgICB9XG4gICAgICAgIGlmIChlbC5jb250ZW50cyA9PT0gXCJcIikge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCJcIjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzdGVwTmFtZSA9IGVsLmNvbnRlbnRzO1xuICAgICAgICAgIHN0ZXBOYW1lID0gc2hvcnRlblN0cmluZyhzdGVwTmFtZSk7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNCBjbGFzcz0nc3RlcC1uYW1lJz5cIiArIHN0ZXBOYW1lICsgXCI8L2g0PlwiO1xuICAgICAgICB9XG4gICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uICE9IG51bGwpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbihlbC5pZCwgbWF4VywgbWF4SCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWYgKGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbykpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+XCIgKyBpbmZvICsgXCIgTm9kZTwvaDU+XCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5wYXJhbGxlbGlzbSAhPT0gXCJcIikge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5QYXJhbGxlbGlzbTogXCIgKyBlbC5wYXJhbGxlbGlzbSArIFwiPC9oNT5cIjtcbiAgICAgIC
AgICB9XG4gICAgICAgICAgaWYgKGVsLmRyaXZlcl9zdHJhdGVneSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PkRyaXZlciBTdHJhdGVneTogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLmRyaXZlcl9zdHJhdGVneSkgKyBcIjwvaDVcIjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvYT5cIjtcbiAgICAgICAgcmV0dXJuIGxhYmVsVmFsdWU7XG4gICAgICB9O1xuICAgICAgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uID0gZnVuY3Rpb24oaWQsIG1heFcsIG1heEgpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWUsIHN2Z0lEO1xuICAgICAgICBzdmdJRCA9IFwic3ZnLVwiICsgaWQ7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIjxzdmcgY2xhc3M9J1wiICsgc3ZnSUQgKyBcIicgd2lkdGg9XCIgKyBtYXhXICsgXCIgaGVpZ2h0PVwiICsgbWF4SCArIFwiPjxnIC8+PC9zdmc+XCI7XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIHNob3J0ZW5TdHJpbmcgPSBmdW5jdGlvbihzKSB7XG4gICAgICAgIHZhciBzYnI7XG4gICAgICAgIGlmIChzLmNoYXJBdCgwKSA9PT0gXCI8XCIpIHtcbiAgICAgICAgICBzID0gcy5yZXBsYWNlKFwiPFwiLCBcIiZsdDtcIik7XG4gICAgICAgICAgcyA9IHMucmVwbGFjZShcIj5cIiwgXCImZ3Q7XCIpO1xuICAgICAgICB9XG4gICAgICAgIHNiciA9IFwiXCI7XG4gICAgICA
gIHdoaWxlIChzLmxlbmd0aCA+IDMwKSB7XG4gICAgICAgICAgc2JyID0gc2JyICsgcy5zdWJzdHJpbmcoMCwgMzApICsgXCI8YnI+XCI7XG4gICAgICAgICAgcyA9IHMuc3Vic3RyaW5nKDMwLCBzLmxlbmd0aCk7XG4gICAgICAgIH1cbiAgICAgICAgc2JyID0gc2JyICsgcztcbiAgICAgICAgcmV0dXJuIHNicjtcbiAgICAgIH07XG4gICAgICBjcmVhdGVOb2RlID0gZnVuY3Rpb24oZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIGlmIChpc1BhcmVudCA9PSBudWxsKSB7XG4gICAgICAgICAgaXNQYXJlbnQgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuaWQgPT09IGRhdGEucGFydGlhbF9zb2x1dGlvbikge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLm5leHRfcGFydGlhbF9zb2x1dGlvbikge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1
dGlvblwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEud29ya3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwid29ya3NldFwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJ3b3Jrc2V0XCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEubmV4dF93b3Jrc2V0KSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0V29ya3NldFwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0V29ya3NldFwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLnNvbHV0aW9uX3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhY
mVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25TZXRcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25TZXRcIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5zb2x1dGlvbl9kZWx0YSkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25EZWx0YVwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwiXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjcmVhdGVFZGdlID0gZnVuY3Rpb24oZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpIHtcbiAgICAgICAgdmFyIG1pc3NpbmdOb2RlO1xuIC
AgICAgICBpZiAoZXhpc3RpbmdOb2Rlcy5pbmRleE9mKHByZWQuaWQpICE9PSAtMSkge1xuICAgICAgICAgIHJldHVybiBnLnNldEVkZ2UocHJlZC5pZCwgZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UocHJlZCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIGFycm93aGVhZDogJ25vcm1hbCdcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBtaXNzaW5nTm9kZSA9IHNlYXJjaEZvck5vZGUoZGF0YSwgcHJlZC5pZCk7XG4gICAgICAgICAgaWYgKCEoIW1pc3NpbmdOb2RlIHx8IG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9PT0gdHJ1ZSkpIHtcbiAgICAgICAgICAgIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9IHRydWU7XG4gICAgICAgICAgICBnLnNldE5vZGUobWlzc2luZ05vZGUuaWQsIHtcbiAgICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShtaXNzaW5nTm9kZSwgXCJtaXJyb3JcIiksXG4gICAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKG1pc3NpbmdOb2RlLCAnbWlycm9yJylcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGcuc2V0RWRnZShtaXNzaW5nTm9kZS5pZCwgZWwuaWQsIHtcbiAgICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShtaXNzaW5nTm9kZSksXG4gICAgICAgICAgICA
gIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBsb2FkSnNvblRvRGFncmUgPSBmdW5jdGlvbihnLCBkYXRhKSB7XG4gICAgICAgIHZhciBlbCwgZXhpc3RpbmdOb2RlcywgaXNQYXJlbnQsIGssIGwsIGxlbiwgbGVuMSwgbWF4SCwgbWF4VywgcHJlZCwgciwgcmVmLCBzZywgdG9JdGVyYXRlO1xuICAgICAgICBleGlzdGluZ05vZGVzID0gW107XG4gICAgICAgIGlmIChkYXRhLm5vZGVzICE9IG51bGwpIHtcbiAgICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLm5vZGVzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEuc3RlcF9mdW5jdGlvbjtcbiAgICAgICAgICBpc1BhcmVudCA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChrID0gMCwgbGVuID0gdG9JdGVyYXRlLmxlbmd0aDsgayA8IGxlbjsgaysrKSB7XG4gICAgICAgICAgZWwgPSB0b0l0ZXJhdGVba107XG4gICAgICAgICAgbWF4VyA9IDA7XG4gICAgICAgICAgbWF4SCA9IDA7XG4gICAgICAgICAgaWYgKGVsLnN0ZXBfZnVuY3Rpb24pIHtcbiAgICAgICAgICAgIHNnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoe1xuICAgICAgICAgICAgICBtdWx0aWdyYXBoOiB0cnVlLFxuICAgICAgICAgICAgICBjb21wb3VuZDogdHJ1ZVxuICAgICAgICAgICAgfSkuc2V0R3JhcGgoe1xuICAgICAgICAgICAgICBub2Rlc2VwOiAyMCxc
biAgICAgICAgICAgICAgZWRnZXNlcDogMCxcbiAgICAgICAgICAgICAgcmFua3NlcDogMjAsXG4gICAgICAgICAgICAgIHJhbmtkaXI6IFwiTFJcIixcbiAgICAgICAgICAgICAgbWFyZ2lueDogMTAsXG4gICAgICAgICAgICAgIG1hcmdpbnk6IDEwXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHN1YmdyYXBoc1tlbC5pZF0gPSBzZztcbiAgICAgICAgICAgIGxvYWRKc29uVG9EYWdyZShzZywgZWwpO1xuICAgICAgICAgICAgciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpO1xuICAgICAgICAgICAgZDN0bXBTdmcuc2VsZWN0KCdnJykuY2FsbChyLCBzZyk7XG4gICAgICAgICAgICBtYXhXID0gc2cuZ3JhcGgoKS53aWR0aDtcbiAgICAgICAgICAgIG1heEggPSBzZy5ncmFwaCgpLmhlaWdodDtcbiAgICAgICAgICAgIGFuZ3VsYXIuZWxlbWVudChtYWluVG1wRWxlbWVudCkuZW1wdHkoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY3JlYXRlTm9kZShnLCBkYXRhLCBlbCwgaXNQYXJlbnQsIG1heFcsIG1heEgpO1xuICAgICAgICAgIGV4aXN0aW5nTm9kZXMucHVzaChlbC5pZCk7XG4gICAgICAgICAgaWYgKGVsLnByZWRlY2Vzc29ycyAhPSBudWxsKSB7XG4gICAgICAgICAgICByZWYgPSBlbC5wcmVkZWNlc3NvcnM7XG4gICAgICAgICAgICBmb3IgKGwgPSAwLCBsZW4xID0gcmVmLmxlbmd0aDsgbCA8IGxlbjE7IGwrKykge1xuICAgICAgICAgICAgICBwcmVkID0gcmVmW2xdO1xuICAgICAgICAgICAgICBjcmVhd
GVFZGdlKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGc7XG4gICAgICB9O1xuICAgICAgc2VhcmNoRm9yTm9kZSA9IGZ1bmN0aW9uKGRhdGEsIG5vZGVJRCkge1xuICAgICAgICB2YXIgZWwsIGksIGo7XG4gICAgICAgIGZvciAoaSBpbiBkYXRhLm5vZGVzKSB7XG4gICAgICAgICAgZWwgPSBkYXRhLm5vZGVzW2ldO1xuICAgICAgICAgIGlmIChlbC5pZCA9PT0gbm9kZUlEKSB7XG4gICAgICAgICAgICByZXR1cm4gZWw7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uICE9IG51bGwpIHtcbiAgICAgICAgICAgIGZvciAoaiBpbiBlbC5zdGVwX2Z1bmN0aW9uKSB7XG4gICAgICAgICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uW2pdLmlkID09PSBub2RlSUQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZWwuc3RlcF9mdW5jdGlvbltqXTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGRyYXdHcmFwaCA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGcsIGksIG5ld1NjYWxlLCByZW5kZXJlciwgc2csIHhDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXQ7XG4gICAgICAgIGcgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7XG4gICAgICAgICAgbXVsdGlncmFwaDogdHJ1ZS
xcbiAgICAgICAgICBjb21wb3VuZDogdHJ1ZVxuICAgICAgICB9KS5zZXRHcmFwaCh7XG4gICAgICAgICAgbm9kZXNlcDogNzAsXG4gICAgICAgICAgZWRnZXNlcDogMCxcbiAgICAgICAgICByYW5rc2VwOiA1MCxcbiAgICAgICAgICByYW5rZGlyOiBcIkxSXCIsXG4gICAgICAgICAgbWFyZ2lueDogNDAsXG4gICAgICAgICAgbWFyZ2lueTogNDBcbiAgICAgICAgfSk7XG4gICAgICAgIGxvYWRKc29uVG9EYWdyZShnLCBkYXRhKTtcbiAgICAgICAgcmVuZGVyZXIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKTtcbiAgICAgICAgZDNtYWluU3ZnRy5jYWxsKHJlbmRlcmVyLCBnKTtcbiAgICAgICAgZm9yIChpIGluIHN1YmdyYXBocykge1xuICAgICAgICAgIHNnID0gc3ViZ3JhcGhzW2ldO1xuICAgICAgICAgIGQzbWFpblN2Zy5zZWxlY3QoJ3N2Zy5zdmctJyArIGkgKyAnIGcnKS5jYWxsKHJlbmRlcmVyLCBzZyk7XG4gICAgICAgIH1cbiAgICAgICAgbmV3U2NhbGUgPSAwLjU7XG4gICAgICAgIHhDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLndpZHRoKCkgLSBnLmdyYXBoKCkud2lkdGggKiBuZXdTY2FsZSkgLyAyKTtcbiAgICAgICAgeUNlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkuaGVpZ2h0KCkgLSBnLmdyYXBoKCkuaGVpZ2h0ICogbmV3U2NhbGUpIC8gMik7XG4gICAgICAgIG1haW5ab29tLnNjYWxlKG5
ld1NjYWxlKS50cmFuc2xhdGUoW3hDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXRdKTtcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgeENlbnRlck9mZnNldCArIFwiLCBcIiArIHlDZW50ZXJPZmZzZXQgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICBtYWluWm9vbS5vbihcInpvb21cIiwgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgdmFyIGV2O1xuICAgICAgICAgIGV2ID0gZDMuZXZlbnQ7XG4gICAgICAgICAgcmV0dXJuIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIGV2LnRyYW5zbGF0ZSArIFwiKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIpXCIpO1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIG1haW5ab29tKGQzbWFpblN2Zyk7XG4gICAgICB9O1xuICAgICAgc2NvcGUuJHdhdGNoKGF0dHJzLnBsYW4sIGZ1bmN0aW9uKG5ld1BsYW4pIHtcbiAgICAgICAgaWYgKG5ld1BsYW4pIHtcbiAgICAgICAgICByZXR1cm4gZHJhd0dyYXBoKG5ld1BsYW4pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH07XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlz
dHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5
<TRUNCATED>
[35/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] New
node organization
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] New node organization
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/46d82177
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/46d82177
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/46d82177
Branch: refs/heads/master
Commit: 46d82177fd511de044c06b4fb876e0d1bf2eaa3c
Parents: 6d873e6
Author: Piotr Godek <pi...@gmail.com>
Authored: Tue Sep 1 00:01:34 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:52 2015 +0200
----------------------------------------------------------------------
.../app/partials/jobs/job.exceptions.jade | 4 +-
.../app/partials/jobs/job.plan.jade | 2 +-
.../app/partials/jobs/job.plan.node-list.jade | 51 +++++++++++++++
.../app/partials/jobs/job.plan.node-tabs.jade | 10 +++
.../jobs/job.plan.node.accumulators.jade | 2 +-
.../app/partials/jobs/job.plan.node.jade | 3 -
.../partials/jobs/job.plan.node.properties.jade | 1 -
.../web-dashboard/app/scripts/index.coffee | 58 +++++++++++++----
.../app/scripts/modules/jobs/jobs.ctrl.coffee | 48 ++++++++++++--
.../app/scripts/modules/jobs/jobs.svc.coffee | 8 +--
.../web-dashboard/app/styles/graph.styl | 7 +--
.../web-dashboard/app/styles/job.styl | 6 ++
.../web-dashboard/web/css/index.css | 8 ++-
flink-runtime-web/web-dashboard/web/js/index.js | 66 ++++++++++++--------
.../web/partials/jobs/job.plan.html | 2 +-
.../web/partials/jobs/job.plan.node-list.html | 57 +++++++++++++++++
.../web/partials/jobs/job.plan.node-nav.html | 0
.../web/partials/jobs/job.plan.node-tabs.html | 10 +++
.../jobs/job.plan.node.accumulators.html | 2 +-
.../partials/jobs/job.plan.node.properties.html | 1 -
20 files changed, 281 insertions(+), 65 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
index 0ea4c7f..66606e6 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
@@ -22,7 +22,7 @@
.panel-body
pre.exception
- {{ exceptions['root-exception'] }}
+ | {{ exceptions['root-exception'] }}
.panel.panel-default.panel-multi(ng-repeat="exception in exceptions['all-exceptions']" )
.panel-heading.clearfix
@@ -35,4 +35,4 @@
.panel-body
pre.exception
- {{ exception.exception }}
+ | {{ exception.exception }}
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade
index ea0c1b2..582d7e6 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.jade
@@ -18,4 +18,4 @@
.canvas-wrapper
div.main-canvas(job-plan, plan="plan", jobid="{{jobid}}")
-div(ui-view="node")
+div(ui-view="node-tabs")
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
new file mode 100644
index 0000000..5621336
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
@@ -0,0 +1,51 @@
+//
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+table.table.table-hover.table-clickable.table-activable
+ thead
+ tr
+ th Start Time
+ th End Time
+ th Duration
+ th Name
+ th Bytes read
+ th Records read
+ th Bytes written
+ th Records written
+ th Tasks
+ th Status
+
+ tbody
+ //- tr(ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" id="vertex-row-{{v.id}}" ng-click="changeNode(v.id)")
+ tr(ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" id="vertex-row-{{v.id}}" ui-sref="{nodeid: v.id}")
+ td
+ | {{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ div(ui-view="node")
+
+ td {{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ td {{ v.duration }} ms
+ td {{ v.name | humanizeTaskName }}
+ td {{ v.metrics['read-bytes'] }}
+ td {{ v.metrics['read-records'] }}
+ td {{ v.metrics['write-bytes'] }}
+ td {{ v.metrics['write-records'] }}
+ td
+ .label-group
+ bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{v.tasks[status]}}
+
+ td
+ bs-label(status="{{v.status}}") {{v.status}}
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
new file mode 100644
index 0000000..57b7d24
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
@@ -0,0 +1,10 @@
+.panel.panel-default.panel-multi(ng-if="node")
+ nav.navbar.navbar-default.navbar-secondary-additional(ng-if="job.currentNode")
+ ul.nav.nav-tabs
+ li(ui-sref-active='active')
+ a(ui-sref=".generic({nodeid: nodeid})") Subtasks
+
+ li(ui-sref-active='active')
+ a(ui-sref=".properties({nodeid: nodeid})") Properties
+
+ div.panel-body.clean(ui-view="node-details")
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
index a50b134..67d0921 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.accumulators.jade
@@ -15,4 +15,4 @@
See the License for the specific language governing permissions and
limitations under the License.
-h1 Accumulators
+h3 Accumulators
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
index f72f412..3c5ed0f 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
@@ -45,6 +45,3 @@
.panel-body.clean
div(ui-view="nodeTab")
-
-
-
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
index 19d5c0d..6427ab3 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
@@ -15,7 +15,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-
.row(ng-if="node")
.col-sm-6.col-md-4
table.table.table-properties(ng-if="node.optimizer_properties.global_properties")
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/scripts/index.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/index.coffee b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
index 63b5bf2..806a2a1 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
@@ -49,6 +49,11 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
# --------------------------------------
+.config ($uiViewScrollProvider) ->
+ $uiViewScrollProvider.useAnchorScroll()
+
+# --------------------------------------
+
.config ($stateProvider, $urlRouterProvider) ->
$stateProvider.state "overview",
url: "/overview"
@@ -90,27 +95,56 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
url: "/vertex/{nodeid}"
abstract: true
views:
- node:
- templateUrl: "partials/jobs/job.plan.node.html"
- controller: 'JobPlanNodeController'
+ 'node-tabs':
+ templateUrl: "partials/jobs/job.plan.node-tabs.html"
+ controller: 'JobPlanNodeTabsController'
- .state "single-job.plan.node.subtasks",
+ .state "single-job.plan.node.generic",
url: ""
views:
- nodeTab:
- templateUrl: "partials/jobs/job.plan.node.subtasks.html"
+ 'node-details':
+ controller: 'JobPlanNodeListController'
+ templateUrl: "partials/jobs/job.plan.node-list.html"
.state "single-job.plan.node.properties",
url: "/properties"
views:
- nodeTab:
+ 'node-details':
+ controller: 'JobPlanNodePropertiesController'
templateUrl: "partials/jobs/job.plan.node.properties.html"
- .state "single-job.plan.node.accumulators",
- url: "/properties"
- views:
- nodeTab:
- templateUrl: "partials/jobs/job.plan.node.accumulators.html"
+ # .state "single-job.plan.node.accumulators",
+ # url: "/accumulators"
+ # views:
+ # 'node-contents':
+ # controller: 'JobPlanNodeAccumulatorsController'
+ # templateUrl: "partials/jobs/job.plan.node.accumulators.html"
+
+ # .state "single-job.plan.node",
+ # url: "/vertex/{nodeid}"
+ # abstract: true
+ # views:
+ # node:
+ # templateUrl: "partials/jobs/job.plan.node.html"
+ # controller: 'JobPlanNodeController'
+
+ # .state "single-job.plan.node.subtasks",
+ # url: ""
+ # views:
+ # nodeTab:
+ # templateUrl: "partials/jobs/job.plan.node.subtasks.html"
+
+ # .state "single-job.plan.node.properties",
+ # url: "/properties"
+ # views:
+ # nodeTab:
+ # templateUrl: "partials/jobs/job.plan.node.properties.html"
+
+ # .state "single-job.plan.node.accumulators",
+ # url: "/properties"
+ # views:
+ # nodeTab:
+ # templateUrl: "partials/jobs/job.plan.node.accumulators.html"
.state "single-job.timeline",
url: "/timeline"
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
index d74f781..127cec1 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
@@ -45,30 +45,68 @@ angular.module('flinkApp')
.controller 'SingleJobController', ($scope, $state, $stateParams, JobsService, $rootScope) ->
$scope.jobid = $stateParams.jobid
$rootScope.job = null
+ $rootScope.plan = null
JobsService.loadJob($stateParams.jobid).then (data) ->
$rootScope.job = data
+ $rootScope.plan = data.plan
$scope.$on '$destroy', ->
$rootScope.job = null
+ $rootScope.plan = null
# --------------------------------------
.controller 'JobPlanController', ($scope, $state, $stateParams, JobsService) ->
- JobsService.loadPlan($stateParams.jobid).then (data) ->
- $scope.plan = data
+ console.log 'JobPlanController'
+
+ $scope.stateList = JobsService.stateList()
# --------------------------------------
-.controller 'JobPlanNodeController', ($scope, $state, $stateParams, JobsService) ->
+.controller 'JobPlanNodeController', ($scope, $state, $stateParams, JobsService, $anchorScroll) ->
+ console.log 'JobPlanNodeController'
+
$scope.nodeid = $stateParams.nodeid
+
$scope.stateList = JobsService.stateList()
JobsService.getNode($scope.nodeid).then (data) ->
$scope.node = data
+ $scope.job.currentNode = data
- JobsService.getVertex($scope.nodeid).then (data) ->
- $scope.vertex = data
+ # anchor = "vertex-row-" + data.id
+ # console.log anchor
+ # $anchorScroll(anchor)
+
+# --------------------------------------
+
+.controller 'JobPlanNodeTabsController', ($scope, $stateParams, JobsService) ->
+ console.log 'JobPlanNodeTabsController'
+
+ $scope.nodeid = $stateParams.nodeid
+
+ JobsService.getNode($scope.nodeid).then (data) ->
+ $scope.node = data
+ $scope.job.currentNode = data
+
+# --------------------------------------
+
+.controller 'JobPlanNodeListController', ($scope, $stateParams, JobsService) ->
+ console.log 'JobPlanNodeListController'
+
+ # $scope.nodeid = $stateParams.nodeid
+ $scope.stateList = JobsService.stateList()
+
+# --------------------------------------
+
+.controller 'JobPlanNodePropertiesController', ($scope, $state, $stateParams, JobsService) ->
+ console.log 'JobPlanNodePropertiesController'
+
+# --------------------------------------
+
+.controller 'JobPlanNodeAccumulatorsController', ($scope, $state, $stateParams, JobsService) ->
+ console.log 'JobPlanNodeAccumulatorsController'
# --------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index 2290261..f1a2e95 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -146,10 +146,10 @@ angular.module('flinkApp')
deferred = $q.defer()
- $q.all([deferreds.plan.promise, deferreds.job.promise]).then (data) =>
- foundNode = seekNode(nodeid, currentPlan.nodes)
-
- # $http.get flinkConfig.jobServer + "/jobsInfo?get=groupvertex&job=" + currentJob.jid + "&groupvertex=" + nodeid
+ # $q.all([deferreds.plan.promise, deferreds.job.promise]).then (data) =>
+ $q.all([deferreds.job.promise]).then (data) =>
+ # foundNode = seekNode(nodeid, currentPlan.nodes)
+ foundNode = seekNode(nodeid, currentJob.plan.nodes)
foundNode.vertex = @seekVertex(nodeid)
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/styles/graph.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/graph.styl b/flink-runtime-web/web-dashboard/app/styles/graph.styl
index 54e290c..3753f69 100644
--- a/flink-runtime-web/web-dashboard/app/styles/graph.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/graph.styl
@@ -34,6 +34,7 @@ svg.graph
stroke-width 5px
fill #fff
margin: 0
+ padding: 0
&[active]
> rect
@@ -79,14 +80,10 @@ svg.graph
margin: 0
.edge-label
- padding: 5px
+ // padding: 5px
font-size: 14px
- // border: 2px solid
.node-label
display: block
- // border-width: 4px
- // border-style: solid
- // padding: 10px
margin: 0
text-decoration: none
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/app/styles/job.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/job.styl b/flink-runtime-web/web-dashboard/app/styles/job.styl
index 4c16ee9..4259824 100644
--- a/flink-runtime-web/web-dashboard/app/styles/job.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/job.styl
@@ -39,6 +39,12 @@
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
+
+ &.table-activable
+ tr
+ border-left: 2px solid transparent
+ &.active
+ border-left: 2px solid #000000
.label-group
http://git-wip-us.apache.org/repos/asf/flink/blob/46d82177/flink-runtime-web/web-dashboard/web/css/index.css
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/css/index.css b/flink-runtime-web/web-dashboard/web/css/index.css
index 8d4f45c..eca32cd 100644
--- a/flink-runtime-web/web-dashboard/web/css/index.css
+++ b/flink-runtime-web/web-dashboard/web/css/index.css
@@ -349,6 +349,12 @@ pre.exception {
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
}
+.table.table-activable tr {
+ border-left: 2px solid transparent;
+}
+.table.table-activable tr.active {
+ border-left: 2px solid #000;
+}
.label-group .label {
display: inline-block;
width: 2em;
@@ -377,6 +383,7 @@ svg.graph .node > rect {
stroke-width: 5px;
fill: #fff;
margin: 0;
+ padding: 0;
}
svg.graph .node[active] > rect {
fill: #eee;
@@ -415,7 +422,6 @@ svg.graph .label {
margin: 0;
}
svg.graph .edge-label {
- padding: 5px;
font-size: 14px;
}
svg.graph .node-label {
[49/51] [abbrv] flink git commit: Revert "[FLINK-2605] [runtime]
Unclosed RandomAccessFile may leak resource in StaticFileServerHandler"
Posted by se...@apache.org.
Revert "[FLINK-2605] [runtime] Unclosed RandomAccessFile may leak resource in StaticFileServerHandler"
The change breaks the functionality by returning incorrect HTTP responses (length / contents mismatch).
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/482d4373
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/482d4373
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/482d4373
Branch: refs/heads/master
Commit: 482d4373facbf2e24bc76eb04b2a5550434c91b8
Parents: 506ce61
Author: Stephan Ewen <se...@apache.org>
Authored: Wed Sep 16 22:38:40 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:53 2015 +0200
----------------------------------------------------------------------
.../files/StaticFileServerHandler.java | 37 +++++++++++---------
1 file changed, 20 insertions(+), 17 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/482d4373/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/StaticFileServerHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/StaticFileServerHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/StaticFileServerHandler.java
index b6642a2..e368ea9 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/StaticFileServerHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/StaticFileServerHandler.java
@@ -165,26 +165,29 @@ public class StaticFileServerHandler extends SimpleChannelInboundHandler<Routed>
logger.debug("Responding with file '" + file.getAbsolutePath() + '\'');
}
- try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
- long fileLength = raf.length();
-
- HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
- setContentTypeHeader(response, file);
- setDateAndCacheHeaders(response, file);
- if (HttpHeaders.isKeepAlive(request)) {
- response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
- }
- HttpHeaders.setContentLength(response, fileLength);
-
- // write the initial line and the header.
- ctx.write(response);
-
- // write the content.
- ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
- } catch (FileNotFoundException e) {
+ final RandomAccessFile raf;
+ try {
+ raf = new RandomAccessFile(file, "r");
+ }
+ catch (FileNotFoundException e) {
sendError(ctx, NOT_FOUND);
return;
}
+ long fileLength = raf.length();
+
+ HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
+ setContentTypeHeader(response, file);
+ setDateAndCacheHeaders(response, file);
+ if (HttpHeaders.isKeepAlive(request)) {
+ response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
+ }
+ HttpHeaders.setContentLength(response, fileLength);
+
+ // write the initial line and the header.
+ ctx.write(response);
+
+ // write the content.
+ ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
// close the connection, if no keep-alive is needed
[14/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Added
tooltips to timeline
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/4825c8c5/flink-runtime-web/web-dashboard/web/js/vendor.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/vendor.js b/flink-runtime-web/web-dashboard/web/js/vendor.js
index 874099b..3f15020 100644
--- a/flink-runtime-web/web-dashboard/web/js/vendor.js
+++ b/flink-runtime-web/web-dashboard/web/js/vendor.js
@@ -65406,6 +65406,562 @@ angular.module('ui.router.state')
})(this);
/*!
+ * EventEmitter v4.2.11 - git.io/ee
+ * Unlicense - http://unlicense.org/
+ * Oliver Caldwell - http://oli.me.uk/
+ * @preserve
+ */
+
+;(function () {
+ 'use strict';
+
+ /**
+ * Class for managing events.
+ * Can be extended to provide event functionality in other classes.
+ *
+ * @class EventEmitter Manages event registering and emitting.
+ */
+ function EventEmitter() {}
+
+ // Shortcuts to improve speed and size
+ var proto = EventEmitter.prototype;
+ var exports = this;
+ var originalGlobalValue = exports.EventEmitter;
+
+ /**
+ * Finds the index of the listener for the event in its storage array.
+ *
+ * @param {Function[]} listeners Array of listeners to search through.
+ * @param {Function} listener Method to look for.
+ * @return {Number} Index of the specified listener, -1 if not found
+ * @api private
+ */
+ function indexOfListener(listeners, listener) {
+ var i = listeners.length;
+ while (i--) {
+ if (listeners[i].listener === listener) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Alias a method while keeping the context correct, to allow for overwriting of target method.
+ *
+ * @param {String} name The name of the target method.
+ * @return {Function} The aliased method
+ * @api private
+ */
+ function alias(name) {
+ return function aliasClosure() {
+ return this[name].apply(this, arguments);
+ };
+ }
+
+ /**
+ * Returns the listener array for the specified event.
+ * Will initialise the event object and listener arrays if required.
+ * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
+ * Each property in the object response is an array of listener functions.
+ *
+ * @param {String|RegExp} evt Name of the event to return the listeners from.
+ * @return {Function[]|Object} All listener functions for the event.
+ */
+ proto.getListeners = function getListeners(evt) {
+ var events = this._getEvents();
+ var response;
+ var key;
+
+ // Return a concatenated array of all matching events if
+ // the selector is a regular expression.
+ if (evt instanceof RegExp) {
+ response = {};
+ for (key in events) {
+ if (events.hasOwnProperty(key) && evt.test(key)) {
+ response[key] = events[key];
+ }
+ }
+ }
+ else {
+ response = events[evt] || (events[evt] = []);
+ }
+
+ return response;
+ };
+
+ /**
+ * Takes a list of listener objects and flattens it into a list of listener functions.
+ *
+ * @param {Object[]} listeners Raw listener objects.
+ * @return {Function[]} Just the listener functions.
+ */
+ proto.flattenListeners = function flattenListeners(listeners) {
+ var flatListeners = [];
+ var i;
+
+ for (i = 0; i < listeners.length; i += 1) {
+ flatListeners.push(listeners[i].listener);
+ }
+
+ return flatListeners;
+ };
+
+ /**
+ * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
+ *
+ * @param {String|RegExp} evt Name of the event to return the listeners from.
+ * @return {Object} All listener functions for an event in an object.
+ */
+ proto.getListenersAsObject = function getListenersAsObject(evt) {
+ var listeners = this.getListeners(evt);
+ var response;
+
+ if (listeners instanceof Array) {
+ response = {};
+ response[evt] = listeners;
+ }
+
+ return response || listeners;
+ };
+
+ /**
+ * Adds a listener function to the specified event.
+ * The listener will not be added if it is a duplicate.
+ * If the listener returns true then it will be removed after it is called.
+ * If you pass a regular expression as the event name then the listener will be added to all events that match it.
+ *
+ * @param {String|RegExp} evt Name of the event to attach the listener to.
+ * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.addListener = function addListener(evt, listener) {
+ var listeners = this.getListenersAsObject(evt);
+ var listenerIsWrapped = typeof listener === 'object';
+ var key;
+
+ for (key in listeners) {
+ if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
+ listeners[key].push(listenerIsWrapped ? listener : {
+ listener: listener,
+ once: false
+ });
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Alias of addListener
+ */
+ proto.on = alias('addListener');
+
+ /**
+ * Semi-alias of addListener. It will add a listener that will be
+ * automatically removed after its first execution.
+ *
+ * @param {String|RegExp} evt Name of the event to attach the listener to.
+ * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.addOnceListener = function addOnceListener(evt, listener) {
+ return this.addListener(evt, {
+ listener: listener,
+ once: true
+ });
+ };
+
+ /**
+ * Alias of addOnceListener.
+ */
+ proto.once = alias('addOnceListener');
+
+ /**
+ * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
+ * You need to tell it what event names should be matched by a regex.
+ *
+ * @param {String} evt Name of the event to create.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.defineEvent = function defineEvent(evt) {
+ this.getListeners(evt);
+ return this;
+ };
+
+ /**
+ * Uses defineEvent to define multiple events.
+ *
+ * @param {String[]} evts An array of event names to define.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.defineEvents = function defineEvents(evts) {
+ for (var i = 0; i < evts.length; i += 1) {
+ this.defineEvent(evts[i]);
+ }
+ return this;
+ };
+
+ /**
+ * Removes a listener function from the specified event.
+ * When passed a regular expression as the event name, it will remove the listener from all events that match it.
+ *
+ * @param {String|RegExp} evt Name of the event to remove the listener from.
+ * @param {Function} listener Method to remove from the event.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.removeListener = function removeListener(evt, listener) {
+ var listeners = this.getListenersAsObject(evt);
+ var index;
+ var key;
+
+ for (key in listeners) {
+ if (listeners.hasOwnProperty(key)) {
+ index = indexOfListener(listeners[key], listener);
+
+ if (index !== -1) {
+ listeners[key].splice(index, 1);
+ }
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Alias of removeListener
+ */
+ proto.off = alias('removeListener');
+
+ /**
+ * Adds listeners in bulk using the manipulateListeners method.
+ * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
+ * You can also pass it a regular expression to add the array of listeners to all events that match it.
+ * Yeah, this function does quite a bit. That's probably a bad thing.
+ *
+ * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
+ * @param {Function[]} [listeners] An optional array of listener functions to add.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.addListeners = function addListeners(evt, listeners) {
+ // Pass through to manipulateListeners
+ return this.manipulateListeners(false, evt, listeners);
+ };
+
+ /**
+ * Removes listeners in bulk using the manipulateListeners method.
+ * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
+ * You can also pass it an event name and an array of listeners to be removed.
+ * You can also pass it a regular expression to remove the listeners from all events that match it.
+ *
+ * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
+ * @param {Function[]} [listeners] An optional array of listener functions to remove.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.removeListeners = function removeListeners(evt, listeners) {
+ // Pass through to manipulateListeners
+ return this.manipulateListeners(true, evt, listeners);
+ };
+
+ /**
+ * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
+ * The first argument will determine if the listeners are removed (true) or added (false).
+ * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
+ * You can also pass it an event name and an array of listeners to be added/removed.
+ * You can also pass it a regular expression to manipulate the listeners of all events that match it.
+ *
+ * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
+ * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
+ * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
+ var i;
+ var value;
+ var single = remove ? this.removeListener : this.addListener;
+ var multiple = remove ? this.removeListeners : this.addListeners;
+
+ // If evt is an object then pass each of its properties to this method
+ if (typeof evt === 'object' && !(evt instanceof RegExp)) {
+ for (i in evt) {
+ if (evt.hasOwnProperty(i) && (value = evt[i])) {
+ // Pass the single listener straight through to the singular method
+ if (typeof value === 'function') {
+ single.call(this, i, value);
+ }
+ else {
+ // Otherwise pass back to the multiple function
+ multiple.call(this, i, value);
+ }
+ }
+ }
+ }
+ else {
+ // So evt must be a string
+ // And listeners must be an array of listeners
+ // Loop over it and pass each one to the multiple method
+ i = listeners.length;
+ while (i--) {
+ single.call(this, evt, listeners[i]);
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Removes all listeners from a specified event.
+ * If you do not specify an event then all listeners will be removed.
+ * That means every event will be emptied.
+ * You can also pass a regex to remove all events that match it.
+ *
+ * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.removeEvent = function removeEvent(evt) {
+ var type = typeof evt;
+ var events = this._getEvents();
+ var key;
+
+ // Remove different things depending on the state of evt
+ if (type === 'string') {
+ // Remove all listeners for the specified event
+ delete events[evt];
+ }
+ else if (evt instanceof RegExp) {
+ // Remove all events matching the regex.
+ for (key in events) {
+ if (events.hasOwnProperty(key) && evt.test(key)) {
+ delete events[key];
+ }
+ }
+ }
+ else {
+ // Remove all listeners in all events
+ delete this._events;
+ }
+
+ return this;
+ };
+
+ /**
+ * Alias of removeEvent.
+ *
+ * Added to mirror the node API.
+ */
+ proto.removeAllListeners = alias('removeEvent');
+
+ /**
+ * Emits an event of your choice.
+ * When emitted, every listener attached to that event will be executed.
+ * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
+ * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
+ * So they will not arrive within the array on the other side, they will be separate.
+ * You can also pass a regular expression to emit to all events that match it.
+ *
+ * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
+ * @param {Array} [args] Optional array of arguments to be passed to each listener.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.emitEvent = function emitEvent(evt, args) {
+ var listeners = this.getListenersAsObject(evt);
+ var listener;
+ var i;
+ var key;
+ var response;
+
+ for (key in listeners) {
+ if (listeners.hasOwnProperty(key)) {
+ i = listeners[key].length;
+
+ while (i--) {
+ // If the listener returns true then it shall be removed from the event
+ // The function is executed either with a basic call or an apply if there is an args array
+ listener = listeners[key][i];
+
+ if (listener.once === true) {
+ this.removeListener(evt, listener.listener);
+ }
+
+ response = listener.listener.apply(this, args || []);
+
+ if (response === this._getOnceReturnValue()) {
+ this.removeListener(evt, listener.listener);
+ }
+ }
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Alias of emitEvent
+ */
+ proto.trigger = alias('emitEvent');
+
+ /**
+ * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
+ * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
+ *
+ * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
+ * @param {...*} Optional additional arguments to be passed to each listener.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.emit = function emit(evt) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return this.emitEvent(evt, args);
+ };
+
+ /**
+ * Sets the current value to check against when executing listeners. If a
+ * listeners return value matches the one set here then it will be removed
+ * after execution. This value defaults to true.
+ *
+ * @param {*} value The new value to check for when executing listeners.
+ * @return {Object} Current instance of EventEmitter for chaining.
+ */
+ proto.setOnceReturnValue = function setOnceReturnValue(value) {
+ this._onceReturnValue = value;
+ return this;
+ };
+
+ /**
+ * Fetches the current value to check against when executing listeners. If
+ * the listeners return value matches this one then it should be removed
+ * automatically. It will return true by default.
+ *
+ * @return {*|Boolean} The current value to check for or the default, true.
+ * @api private
+ */
+ proto._getOnceReturnValue = function _getOnceReturnValue() {
+ if (this.hasOwnProperty('_onceReturnValue')) {
+ return this._onceReturnValue;
+ }
+ else {
+ return true;
+ }
+ };
+
+ /**
+ * Fetches the events object and creates one if required.
+ *
+ * @return {Object} The events storage object.
+ * @api private
+ */
+ proto._getEvents = function _getEvents() {
+ return this._events || (this._events = {});
+ };
+
+ /**
+ * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
+ *
+ * @return {Function} Non conflicting EventEmitter class.
+ */
+ EventEmitter.noConflict = function noConflict() {
+ exports.EventEmitter = originalGlobalValue;
+ return EventEmitter;
+ };
+
+ // Expose the class either via AMD, CommonJS or the global object
+ if (typeof define === 'function' && define.amd) {
+ define(function () {
+ return EventEmitter;
+ });
+ }
+ else if (typeof module === 'object' && module.exports){
+ module.exports = EventEmitter;
+ }
+ else {
+ exports.EventEmitter = EventEmitter;
+ }
+}.call(this));
+
+/*!
+ * eventie v1.0.6
+ * event binding helper
+ * eventie.bind( elem, 'click', myFn )
+ * eventie.unbind( elem, 'click', myFn )
+ * MIT license
+ */
+
+/*jshint browser: true, undef: true, unused: true */
+/*global define: false, module: false */
+
+( function( window ) {
+
+'use strict';
+
+var docElem = document.documentElement;
+
+var bind = function() {};
+
+function getIEEvent( obj ) {
+ var event = window.event;
+ // add event.target
+ event.target = event.target || event.srcElement || obj;
+ return event;
+}
+
+if ( docElem.addEventListener ) {
+ bind = function( obj, type, fn ) {
+ obj.addEventListener( type, fn, false );
+ };
+} else if ( docElem.attachEvent ) {
+ bind = function( obj, type, fn ) {
+ obj[ type + fn ] = fn.handleEvent ?
+ function() {
+ var event = getIEEvent( obj );
+ fn.handleEvent.call( fn, event );
+ } :
+ function() {
+ var event = getIEEvent( obj );
+ fn.call( obj, event );
+ };
+ obj.attachEvent( "on" + type, obj[ type + fn ] );
+ };
+}
+
+var unbind = function() {};
+
+if ( docElem.removeEventListener ) {
+ unbind = function( obj, type, fn ) {
+ obj.removeEventListener( type, fn, false );
+ };
+} else if ( docElem.detachEvent ) {
+ unbind = function( obj, type, fn ) {
+ obj.detachEvent( "on" + type, obj[ type + fn ] );
+ try {
+ delete obj[ type + fn ];
+ } catch ( err ) {
+ // can't delete window object properties
+ obj[ type + fn ] = undefined;
+ }
+ };
+}
+
+var eventie = {
+ bind: bind,
+ unbind: unbind
+};
+
+// ----- module definition ----- //
+
+if ( typeof define === 'function' && define.amd ) {
+ // AMD
+ define( eventie );
+} else if ( typeof exports === 'object' ) {
+ // CommonJS
+ module.exports = eventie;
+} else {
+ // browser global
+ window.eventie = eventie;
+}
+
+})( window );
+
+/*!
* Bootstrap v3.3.5 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under the MIT license
@@ -69534,37 +70090,373 @@ module.exports = '1.0.7';
}
})();
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.dagre=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-/*
-Copyright (c) 2012-2014 Chris Pettitt
+/*!
+ * imagesLoaded v3.1.8
+ * JavaScript is all like "You images are done yet or what?"
+ * MIT License
+ */
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+( function( window, factory ) { 'use strict';
+ // universal module definition
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+ /*global define: false, module: false, require: false */
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
+ if ( typeof define === 'function' && define.amd ) {
+ // AMD
+ define( [
+ 'eventEmitter/EventEmitter',
+ 'eventie/eventie'
+ ], function( EventEmitter, eventie ) {
+ return factory( window, EventEmitter, eventie );
+ });
+ } else if ( typeof exports === 'object' ) {
+ // CommonJS
+ module.exports = factory(
+ window,
+ require('wolfy87-eventemitter'),
+ require('eventie')
+ );
+ } else {
+ // browser global
+ window.imagesLoaded = factory(
+ window,
+ window.EventEmitter,
+ window.eventie
+ );
+ }
-module.exports = {
- graphlib: require("./lib/graphlib"),
+})( window,
- layout: require("./lib/layout"),
- debug: require("./lib/debug"),
- util: {
- time: require("./lib/util").time,
- notime: require("./lib/util").notime
+// -------------------------- factory -------------------------- //
+
+function factory( window, EventEmitter, eventie ) {
+
+'use strict';
+
+var $ = window.jQuery;
+var console = window.console;
+var hasConsole = typeof console !== 'undefined';
+
+// -------------------------- helpers -------------------------- //
+
+// extend objects
+function extend( a, b ) {
+ for ( var prop in b ) {
+ a[ prop ] = b[ prop ];
+ }
+ return a;
+}
+
+var objToString = Object.prototype.toString;
+function isArray( obj ) {
+ return objToString.call( obj ) === '[object Array]';
+}
+
+// turn element or nodeList into an array
+function makeArray( obj ) {
+ var ary = [];
+ if ( isArray( obj ) ) {
+ // use object if already an array
+ ary = obj;
+ } else if ( typeof obj.length === 'number' ) {
+ // convert nodeList to array
+ for ( var i=0, len = obj.length; i < len; i++ ) {
+ ary.push( obj[i] );
+ }
+ } else {
+ // array of single index
+ ary.push( obj );
+ }
+ return ary;
+}
+
+ // -------------------------- imagesLoaded -------------------------- //
+
+ /**
+ * @param {Array, Element, NodeList, String} elem
+ * @param {Object or Function} options - if function, use as callback
+ * @param {Function} onAlways - callback function
+ */
+ function ImagesLoaded( elem, options, onAlways ) {
+ // coerce ImagesLoaded() without new, to be new ImagesLoaded()
+ if ( !( this instanceof ImagesLoaded ) ) {
+ return new ImagesLoaded( elem, options );
+ }
+ // use elem as selector string
+ if ( typeof elem === 'string' ) {
+ elem = document.querySelectorAll( elem );
+ }
+
+ this.elements = makeArray( elem );
+ this.options = extend( {}, this.options );
+
+ if ( typeof options === 'function' ) {
+ onAlways = options;
+ } else {
+ extend( this.options, options );
+ }
+
+ if ( onAlways ) {
+ this.on( 'always', onAlways );
+ }
+
+ this.getImages();
+
+ if ( $ ) {
+ // add jQuery Deferred object
+ this.jqDeferred = new $.Deferred();
+ }
+
+ // HACK check async to allow time to bind listeners
+ var _this = this;
+ setTimeout( function() {
+ _this.check();
+ });
+ }
+
+ ImagesLoaded.prototype = new EventEmitter();
+
+ ImagesLoaded.prototype.options = {};
+
+ ImagesLoaded.prototype.getImages = function() {
+ this.images = [];
+
+ // filter & find items if we have an item selector
+ for ( var i=0, len = this.elements.length; i < len; i++ ) {
+ var elem = this.elements[i];
+ // filter siblings
+ if ( elem.nodeName === 'IMG' ) {
+ this.addImage( elem );
+ }
+ // find children
+ // no non-element nodes, #143
+ var nodeType = elem.nodeType;
+ if ( !nodeType || !( nodeType === 1 || nodeType === 9 || nodeType === 11 ) ) {
+ continue;
+ }
+ var childElems = elem.querySelectorAll('img');
+ // concat childElems to filterFound array
+ for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
+ var img = childElems[j];
+ this.addImage( img );
+ }
+ }
+ };
+
+ /**
+ * @param {Image} img
+ */
+ ImagesLoaded.prototype.addImage = function( img ) {
+ var loadingImage = new LoadingImage( img );
+ this.images.push( loadingImage );
+ };
+
+ ImagesLoaded.prototype.check = function() {
+ var _this = this;
+ var checkedCount = 0;
+ var length = this.images.length;
+ this.hasAnyBroken = false;
+ // complete if no images
+ if ( !length ) {
+ this.complete();
+ return;
+ }
+
+ function onConfirm( image, message ) {
+ if ( _this.options.debug && hasConsole ) {
+ console.log( 'confirm', image, message );
+ }
+
+ _this.progress( image );
+ checkedCount++;
+ if ( checkedCount === length ) {
+ _this.complete();
+ }
+ return true; // bind once
+ }
+
+ for ( var i=0; i < length; i++ ) {
+ var loadingImage = this.images[i];
+ loadingImage.on( 'confirm', onConfirm );
+ loadingImage.check();
+ }
+ };
+
+ ImagesLoaded.prototype.progress = function( image ) {
+ this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
+ // HACK - Chrome triggers event before object properties have changed. #83
+ var _this = this;
+ setTimeout( function() {
+ _this.emit( 'progress', _this, image );
+ if ( _this.jqDeferred && _this.jqDeferred.notify ) {
+ _this.jqDeferred.notify( _this, image );
+ }
+ });
+ };
+
+ ImagesLoaded.prototype.complete = function() {
+ var eventName = this.hasAnyBroken ? 'fail' : 'done';
+ this.isComplete = true;
+ var _this = this;
+ // HACK - another setTimeout so that confirm happens after progress
+ setTimeout( function() {
+ _this.emit( eventName, _this );
+ _this.emit( 'always', _this );
+ if ( _this.jqDeferred ) {
+ var jqMethod = _this.hasAnyBroken ? 'reject' : 'resolve';
+ _this.jqDeferred[ jqMethod ]( _this );
+ }
+ });
+ };
+
+ // -------------------------- jquery -------------------------- //
+
+ if ( $ ) {
+ $.fn.imagesLoaded = function( options, callback ) {
+ var instance = new ImagesLoaded( this, options, callback );
+ return instance.jqDeferred.promise( $(this) );
+ };
+ }
+
+
+ // -------------------------- -------------------------- //
+
+ function LoadingImage( img ) {
+ this.img = img;
+ }
+
+ LoadingImage.prototype = new EventEmitter();
+
+ LoadingImage.prototype.check = function() {
+ // first check cached any previous images that have same src
+ var resource = cache[ this.img.src ] || new Resource( this.img.src );
+ if ( resource.isConfirmed ) {
+ this.confirm( resource.isLoaded, 'cached was confirmed' );
+ return;
+ }
+
+ // If complete is true and browser supports natural sizes,
+ // try to check for image status manually.
+ if ( this.img.complete && this.img.naturalWidth !== undefined ) {
+ // report based on naturalWidth
+ this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
+ return;
+ }
+
+ // If none of the checks above matched, simulate loading on detached element.
+ var _this = this;
+ resource.on( 'confirm', function( resrc, message ) {
+ _this.confirm( resrc.isLoaded, message );
+ return true;
+ });
+
+ resource.check();
+ };
+
+ LoadingImage.prototype.confirm = function( isLoaded, message ) {
+ this.isLoaded = isLoaded;
+ this.emit( 'confirm', this, message );
+ };
+
+ // -------------------------- Resource -------------------------- //
+
+ // Resource checks each src, only once
+ // separate class from LoadingImage to prevent memory leaks. See #115
+
+ var cache = {};
+
+ function Resource( src ) {
+ this.src = src;
+ // add to cache
+ cache[ src ] = this;
+ }
+
+ Resource.prototype = new EventEmitter();
+
+ Resource.prototype.check = function() {
+ // only trigger checking once
+ if ( this.isChecked ) {
+ return;
+ }
+ // simulate loading on detached element
+ var proxyImage = new Image();
+ eventie.bind( proxyImage, 'load', this );
+ eventie.bind( proxyImage, 'error', this );
+ proxyImage.src = this.src;
+ // set flag
+ this.isChecked = true;
+ };
+
+ // ----- events ----- //
+
+ // trigger specified handler for event type
+ Resource.prototype.handleEvent = function( event ) {
+ var method = 'on' + event.type;
+ if ( this[ method ] ) {
+ this[ method ]( event );
+ }
+ };
+
+ Resource.prototype.onload = function( event ) {
+ this.confirm( true, 'onload' );
+ this.unbindProxyEvents( event );
+ };
+
+ Resource.prototype.onerror = function( event ) {
+ this.confirm( false, 'onerror' );
+ this.unbindProxyEvents( event );
+ };
+
+ // ----- confirm ----- //
+
+ Resource.prototype.confirm = function( isLoaded, message ) {
+ this.isConfirmed = true;
+ this.isLoaded = isLoaded;
+ this.emit( 'confirm', this, message );
+ };
+
+ Resource.prototype.unbindProxyEvents = function( event ) {
+ eventie.unbind( event.target, 'load', this );
+ eventie.unbind( event.target, 'error', this );
+ };
+
+ // ----- ----- //
+
+ return ImagesLoaded;
+
+});
+
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.dagre=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+/*
+Copyright (c) 2012-2014 Chris Pettitt
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+module.exports = {
+ graphlib: require("./lib/graphlib"),
+
+ layout: require("./lib/layout"),
+ debug: require("./lib/debug"),
+ util: {
+ time: require("./lib/util").time,
+ notime: require("./lib/util").notime
},
version: require("./lib/version")
};
@@ -72438,116 +73330,5570 @@ module.exports = "0.7.3";
},{}]},{},[1])(1)
});
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.dagreD3 = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-/**
- * @license
- * Copyright (c) 2012-2013 Chris Pettitt
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
+/*
+ * qTip2 - Pretty powerful tooltips - v2.2.1
+ * http://qtip2.com
*
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
+ * Copyright (c) 2014
+ * Released under the MIT licenses
+ * http://jquery.org/license
*
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+ * Date: Sun Sep 7 2014 12:09 GMT+0100+0100
+ * Plugins: tips modal viewport svg imagemap ie6
+ * Styles: core basic css3
*/
-module.exports = {
- graphlib: require("./lib/graphlib"),
- dagre: require("./lib/dagre"),
- intersect: require("./lib/intersect"),
- render: require("./lib/render"),
- util: require("./lib/util"),
- version: require("./lib/version")
-};
+/*global window: false, jQuery: false, console: false, define: false */
-},{"./lib/dagre":8,"./lib/graphlib":9,"./lib/intersect":10,"./lib/render":25,"./lib/util":27,"./lib/version":28}],2:[function(require,module,exports){
-var util = require("./util");
+/* Cache window, document, undefined */
+(function( window, document, undefined ) {
-module.exports = {
- "default": normal,
- "normal": normal,
- "vee": vee,
- "undirected": undirected
+// Uses AMD or browser globals to create a jQuery plugin.
+(function( factory ) {
+ "use strict";
+ if(typeof define === 'function' && define.amd) {
+ define(['jquery'], factory);
+ }
+ else if(jQuery && !jQuery.fn.qtip) {
+ factory(jQuery);
+ }
+}
+(function($) {
+ "use strict"; // Enable ECMAScript "strict" operation for this function. See more: http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
+;// Munge the primitives - Paul Irish tip
+var TRUE = true,
+FALSE = false,
+NULL = null,
+
+// Common variables
+X = 'x', Y = 'y',
+WIDTH = 'width',
+HEIGHT = 'height',
+
+// Positioning sides
+TOP = 'top',
+LEFT = 'left',
+BOTTOM = 'bottom',
+RIGHT = 'right',
+CENTER = 'center',
+
+// Position adjustment types
+FLIP = 'flip',
+FLIPINVERT = 'flipinvert',
+SHIFT = 'shift',
+
+// Shortcut vars
+QTIP, PROTOTYPE, CORNER, CHECKS,
+PLUGINS = {},
+NAMESPACE = 'qtip',
+ATTR_HAS = 'data-hasqtip',
+ATTR_ID = 'data-qtip-id',
+WIDGET = ['ui-widget', 'ui-tooltip'],
+SELECTOR = '.'+NAMESPACE,
+INACTIVE_EVENTS = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' '),
+
+CLASS_FIXED = NAMESPACE+'-fixed',
+CLASS_DEFAULT = NAMESPACE + '-default',
+CLASS_FOCUS = NAMESPACE + '-focus',
+CLASS_HOVER = NAMESPACE + '-hover',
+CLASS_DISABLED = NAMESPACE+'-disabled',
+
+replaceSuffix = '_replacedByqTip',
+oldtitle = 'oldtitle',
+trackingBound,
+
+// Browser detection
+BROWSER = {
+ /*
+ * IE version detection
+ *
+ * Adapted from: http://ajaxian.com/archives/attack-of-the-ie-conditional-comment
+ * Credit to James Padolsey for the original implemntation!
+ */
+ ie: (function(){
+ for (
+ var v = 4, i = document.createElement("div");
+ (i.innerHTML = "<!--[if gt IE " + v + "]><i></i><![endif]-->") && i.getElementsByTagName("i")[0];
+ v+=1
+ ) {}
+ return v > 4 ? v : NaN;
+ }()),
+
+ /*
+ * iOS version detection
+ */
+ iOS: parseFloat(
+ ('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1])
+ .replace('undefined', '3_2').replace('_', '.').replace('_', '')
+ ) || FALSE
};
+;function QTip(target, options, id, attr) {
+ // Elements and ID
+ this.id = id;
+ this.target = target;
+ this.tooltip = NULL;
+ this.elements = { target: target };
+
+ // Internal constructs
+ this._id = NAMESPACE + '-' + id;
+ this.timers = { img: {} };
+ this.options = options;
+ this.plugins = {};
+
+ // Cache object
+ this.cache = {
+ event: {},
+ target: $(),
+ disabled: FALSE,
+ attr: attr,
+ onTooltip: FALSE,
+ lastClass: ''
+ };
+
+ // Set the initial flags
+ this.rendered = this.destroyed = this.disabled = this.waiting =
+ this.hiddenDuringWait = this.positioning = this.triggering = FALSE;
+}
+PROTOTYPE = QTip.prototype;
+
+PROTOTYPE._when = function(deferreds) {
+ return $.when.apply($, deferreds);
+};
+
+PROTOTYPE.render = function(show) {
+ if(this.rendered || this.destroyed) { return this; } // If tooltip has already been rendered, exit
+
+ var self = this,
+ options = this.options,
+ cache = this.cache,
+ elements = this.elements,
+ text = options.content.text,
+ title = options.content.title,
+ button = options.content.button,
+ posOptions = options.position,
+ namespace = '.'+this._id+' ',
+ deferreds = [],
+ tooltip;
+
+ // Add ARIA attributes to target
+ $.attr(this.target[0], 'aria-describedby', this._id);
+
+ // Create public position object that tracks current position corners
+ cache.posClass = this._createPosClass(
+ (this.position = { my: posOptions.my, at: posOptions.at }).my
+ );
+
+ // Create tooltip element
+ this.tooltip = elements.tooltip = tooltip = $('<div/>', {
+ 'id': this._id,
+ 'class': [ NAMESPACE, CLASS_DEFAULT, options.style.classes, cache.posClass ].join(' '),
+ 'width': options.style.width || '',
+ 'height': options.style.height || '',
+ 'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse,
+
+ /* ARIA specific attributes */
+ 'role': 'alert',
+ 'aria-live': 'polite',
+ 'aria-atomic': FALSE,
+ 'aria-describedby': this._id + '-content',
+ 'aria-hidden': TRUE
+ })
+ .toggleClass(CLASS_DISABLED, this.disabled)
+ .attr(ATTR_ID, this.id)
+ .data(NAMESPACE, this)
+ .appendTo(posOptions.container)
+ .append(
+ // Create content element
+ elements.content = $('<div />', {
+ 'class': NAMESPACE + '-content',
+ 'id': this._id + '-content',
+ 'aria-atomic': TRUE
+ })
+ );
+
+ // Set rendered flag and prevent redundant reposition calls for now
+ this.rendered = -1;
+ this.positioning = TRUE;
+
+ // Create title...
+ if(title) {
+ this._createTitle();
+
+ // Update title only if its not a callback (called in toggle if so)
+ if(!$.isFunction(title)) {
+ deferreds.push( this._updateTitle(title, FALSE) );
+ }
+ }
+
+ // Create button
+ if(button) { this._createButton(); }
+
+ // Set proper rendered flag and update content if not a callback function (called in toggle)
+ if(!$.isFunction(text)) {
+ deferreds.push( this._updateContent(text, FALSE) );
+ }
+ this.rendered = TRUE;
+
+ // Setup widget classes
+ this._setWidget();
+
+ // Initialize 'render' plugins
+ $.each(PLUGINS, function(name) {
+ var instance;
+ if(this.initialize === 'render' && (instance = this(self))) {
+ self.plugins[name] = instance;
+ }
+ });
+
+ // Unassign initial events and assign proper events
+ this._unassignEvents();
+ this._assignEvents();
+
+ // When deferreds have completed
+ this._when(deferreds).then(function() {
+ // tooltiprender event
+ self._trigger('render');
+
+ // Reset flags
+ self.positioning = FALSE;
+
+ // Show tooltip if not hidden during wait period
+ if(!self.hiddenDuringWait && (options.show.ready || show)) {
+ self.toggle(TRUE, cache.event, FALSE);
+ }
+ self.hiddenDuringWait = FALSE;
+ });
+
+ // Expose API
+ QTIP.api[this.id] = this;
+
+ return this;
+};
+
+PROTOTYPE.destroy = function(immediate) {
+ // Set flag the signify destroy is taking place to plugins
+ // and ensure it only gets destroyed once!
+ if(this.destroyed) { return this.target; }
+
+ function process() {
+ if(this.destroyed) { return; }
+ this.destroyed = TRUE;
+
+ var target = this.target,
+ title = target.attr(oldtitle),
+ timer;
+
+ // Destroy tooltip if rendered
+ if(this.rendered) {
+ this.tooltip.stop(1,0).find('*').remove().end().remove();
+ }
+
+ // Destroy all plugins
+ $.each(this.plugins, function(name) {
+ this.destroy && this.destroy();
+ });
+
+ // Clear timers
+ for(timer in this.timers) {
+ clearTimeout(this.timers[timer]);
+ }
+
+ // Remove api object and ARIA attributes
+ target.removeData(NAMESPACE)
+ .removeAttr(ATTR_ID)
+ .removeAttr(ATTR_HAS)
+ .removeAttr('aria-describedby');
+
+ // Reset old title attribute if removed
+ if(this.options.suppress && title) {
+ target.attr('title', title).removeAttr(oldtitle);
+ }
+
+ // Remove qTip events associated with this API
+ this._unassignEvents();
+
+ // Remove ID from used id objects, and delete object references
+ // for better garbage collection and leak protection
+ this.options = this.elements = this.cache = this.timers =
+ this.plugins = this.mouse = NULL;
+
+ // Delete epoxsed API object
+ delete QTIP.api[this.id];
+ }
+
+ // If an immediate destory is needed
+ if((immediate !== TRUE || this.triggering === 'hide') && this.rendered) {
+ this.tooltip.one('tooltiphidden', $.proxy(process, this));
+ !this.triggering && this.hide();
+ }
+
+ // If we're not in the process of hiding... process
+ else { process.call(this); }
+
+ return this.target;
+};
+;function invalidOpt(a) {
+ return a === NULL || $.type(a) !== 'object';
+}
-function normal(parent, id, edge, type) {
- var marker = parent.append("marker")
- .attr("id", id)
- .attr("viewBox", "0 0 10 10")
- .attr("refX", 9)
- .attr("refY", 5)
- .attr("markerUnits", "strokeWidth")
- .attr("markerWidth", 8)
- .attr("markerHeight", 6)
- .attr("orient", "auto");
-
- var path = marker.append("path")
- .attr("d", "M 0 0 L 10 5 L 0 10 z")
- .style("stroke-width", 1)
- .style("stroke-dasharray", "1,0");
- util.applyStyle(path, edge[type + "Style"]);
+function invalidContent(c) {
+ return !( $.isFunction(c) || (c && c.attr) || c.length || ($.type(c) === 'object' && (c.jquery || c.then) ));
}
-function vee(parent, id, edge, type) {
- var marker = parent.append("marker")
- .attr("id", id)
- .attr("viewBox", "0 0 10 10")
- .attr("refX", 9)
- .attr("refY", 5)
- .attr("markerUnits", "strokeWidth")
- .attr("markerWidth", 8)
- .attr("markerHeight", 6)
- .attr("orient", "auto");
+// Option object sanitizer
+function sanitizeOptions(opts) {
+ var content, text, ajax, once;
- var path = marker.append("path")
- .attr("d", "M 0 0 L 10 5 L 0 10 L 4 5 z")
- .style("stroke-width", 1)
- .style("stroke-dasharray", "1,0");
- util.applyStyle(path, edge[type + "Style"]);
-}
+ if(invalidOpt(opts)) { return FALSE; }
-function undirected(parent, id, edge, type) {
- var marker = parent.append("marker")
- .attr("id", id)
- .attr("viewBox", "0 0 10 10")
- .attr("refX", 9)
- .attr("refY", 5)
- .attr("markerUnits", "strokeWidth")
- .attr("markerWidth", 8)
- .attr("markerHeight", 6)
- .attr("orient", "auto");
+ if(invalidOpt(opts.metadata)) {
+ opts.metadata = { type: opts.metadata };
+ }
- var path = marker.append("path")
- .attr("d", "M 0 5 L 10 5")
- .style("stroke-width", 1)
- .style("stroke-dasharray", "1,0");
- util.applyStyle(path, edge[type + "Style"]);
-}
+ if('content' in opts) {
+ content = opts.content;
-},{"./util":27}],3:[function(require,module,exports){
-var util = require("./util"),
- addLabel = require("./label/add-label");
+ if(invalidOpt(content) || content.jquery || content.done) {
+ content = opts.content = {
+ text: (text = invalidContent(content) ? FALSE : content)
+ };
+ }
+ else { text = content.text; }
+
+ // DEPRECATED - Old content.ajax plugin functionality
+ // Converts it into the proper Deferred syntax
+ if('ajax' in content) {
+ ajax = content.ajax;
+ once = ajax && ajax.once !== FALSE;
+ delete content.ajax;
+
+ content.text = function(event, api) {
+ var loading = text || $(this).attr(api.options.content.attr) || 'Loading...',
+
+ deferred = $.ajax(
+ $.extend({}, ajax, { context: api })
+ )
+ .then(ajax.success, NULL, ajax.error)
+ .then(function(content) {
+ if(content && once) { api.set('content.text', content); }
+ return content;
+ },
+ function(xhr, status, error) {
+ if(api.destroyed || xhr.status === 0) { return; }
+ api.set('content.text', status + ': ' + error);
+ });
-module.exports = createClusters;
+ return !once ? (api.set('content.text', loading), deferred) : loading;
+ };
+ }
-function createClusters(selection, g) {
- var clusters = g.nodes().filter(function(v) { return util.isSubgraph(g, v); }),
- svgClusters = selection.selectAll("g.cluster")
- .data(clusters, function(v) { return v; });
+ if('title' in content) {
+ if($.isPlainObject(content.title)) {
+ content.button = content.title.button;
+ content.title = content.title.text;
+ }
- svgClusters.selectAll("*").remove();
- svgClusters.enter()
- .append("g")
+ if(invalidContent(content.title || FALSE)) {
+ content.title = FALSE;
+ }
+ }
+ }
+
+ if('position' in opts && invalidOpt(opts.position)) {
+ opts.position = { my: opts.position, at: opts.position };
+ }
+
+ if('show' in opts && invalidOpt(opts.show)) {
+ opts.show = opts.show.jquery ? { target: opts.show } :
+ opts.show === TRUE ? { ready: TRUE } : { event: opts.show };
+ }
+
+ if('hide' in opts && invalidOpt(opts.hide)) {
+ opts.hide = opts.hide.jquery ? { target: opts.hide } : { event: opts.hide };
+ }
+
+ if('style' in opts && invalidOpt(opts.style)) {
+ opts.style = { classes: opts.style };
+ }
+
+ // Sanitize plugin options
+ $.each(PLUGINS, function() {
+ this.sanitize && this.sanitize(opts);
+ });
+
+ return opts;
+}
+
+// Setup builtin .set() option checks
+CHECKS = PROTOTYPE.checks = {
+ builtin: {
+ // Core checks
+ '^id$': function(obj, o, v, prev) {
+ var id = v === TRUE ? QTIP.nextid : v,
+ new_id = NAMESPACE + '-' + id;
+
+ if(id !== FALSE && id.length > 0 && !$('#'+new_id).length) {
+ this._id = new_id;
+
+ if(this.rendered) {
+ this.tooltip[0].id = this._id;
+ this.elements.content[0].id = this._id + '-content';
+ this.elements.title[0].id = this._id + '-title';
+ }
+ }
+ else { obj[o] = prev; }
+ },
+ '^prerender': function(obj, o, v) {
+ v && !this.rendered && this.render(this.options.show.ready);
+ },
+
+ // Content checks
+ '^content.text$': function(obj, o, v) {
+ this._updateContent(v);
+ },
+ '^content.attr$': function(obj, o, v, prev) {
+ if(this.options.content.text === this.target.attr(prev)) {
+ this._updateContent( this.target.attr(v) );
+ }
+ },
+ '^content.title$': function(obj, o, v) {
+ // Remove title if content is null
+ if(!v) { return this._removeTitle(); }
+
+ // If title isn't already created, create it now and update
+ v && !this.elements.title && this._createTitle();
+ this._updateTitle(v);
+ },
+ '^content.button$': function(obj, o, v) {
+ this._updateButton(v);
+ },
+ '^content.title.(text|button)$': function(obj, o, v) {
+ this.set('content.'+o, v); // Backwards title.text/button compat
+ },
+
+ // Position checks
+ '^position.(my|at)$': function(obj, o, v){
+ 'string' === typeof v && (this.position[o] = obj[o] = new CORNER(v, o === 'at'));
+ },
+ '^position.container$': function(obj, o, v){
+ this.rendered && this.tooltip.appendTo(v);
+ },
+
+ // Show checks
+ '^show.ready$': function(obj, o, v) {
+ v && (!this.rendered && this.render(TRUE) || this.toggle(TRUE));
+ },
+
+ // Style checks
+ '^style.classes$': function(obj, o, v, p) {
+ this.rendered && this.tooltip.removeClass(p).addClass(v);
+ },
+ '^style.(width|height)': function(obj, o, v) {
+ this.rendered && this.tooltip.css(o, v);
+ },
+ '^style.widget|content.title': function() {
+ this.rendered && this._setWidget();
+ },
+ '^style.def': function(obj, o, v) {
+ this.rendered && this.tooltip.toggleClass(CLASS_DEFAULT, !!v);
+ },
+
+ // Events check
+ '^events.(render|show|move|hide|focus|blur)$': function(obj, o, v) {
+ this.rendered && this.tooltip[($.isFunction(v) ? '' : 'un') + 'bind']('tooltip'+o, v);
+ },
+
+ // Properties which require event reassignment
+ '^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)': function() {
+ if(!this.rendered) { return; }
+
+ // Set tracking flag
+ var posOptions = this.options.position;
+ this.tooltip.attr('tracking', posOptions.target === 'mouse' && posOptions.adjust.mouse);
+
+ // Reassign events
+ this._unassignEvents();
+ this._assignEvents();
+ }
+ }
+};
+
+// Dot notation converter
+function convertNotation(options, notation) {
+ var i = 0, obj, option = options,
+
+ // Split notation into array
+ levels = notation.split('.');
+
+ // Loop through
+ while( option = option[ levels[i++] ] ) {
+ if(i < levels.length) { obj = option; }
+ }
+
+ return [obj || options, levels.pop()];
+}
+
+PROTOTYPE.get = function(notation) {
+ if(this.destroyed) { return this; }
+
+ var o = convertNotation(this.options, notation.toLowerCase()),
+ result = o[0][ o[1] ];
+
+ return result.precedance ? result.string() : result;
+};
+
+function setCallback(notation, args) {
+ var category, rule, match;
+
+ for(category in this.checks) {
+ for(rule in this.checks[category]) {
+ if(match = (new RegExp(rule, 'i')).exec(notation)) {
+ args.push(match);
+
+ if(category === 'builtin' || this.plugins[category]) {
+ this.checks[category][rule].apply(
+ this.plugins[category] || this, args
+ );
+ }
+ }
+ }
+ }
+}
+
+var rmove = /^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i,
+ rrender = /^prerender|show\.ready/i;
+
+PROTOTYPE.set = function(option, value) {
+ if(this.destroyed) { return this; }
+
+ var rendered = this.rendered,
+ reposition = FALSE,
+ options = this.options,
+ checks = this.checks,
+ name;
+
+ // Convert singular option/value pair into object form
+ if('string' === typeof option) {
+ name = option; option = {}; option[name] = value;
+ }
+ else { option = $.extend({}, option); }
+
+ // Set all of the defined options to their new values
+ $.each(option, function(notation, value) {
+ if(rendered && rrender.test(notation)) {
+ delete option[notation]; return;
+ }
+
+ // Set new obj value
+ var obj = convertNotation(options, notation.toLowerCase()), previous;
+ previous = obj[0][ obj[1] ];
+ obj[0][ obj[1] ] = value && value.nodeType ? $(value) : value;
+
+ // Also check if we need to reposition
+ reposition = rmove.test(notation) || reposition;
+
+ // Set the new params for the callback
+ option[notation] = [obj[0], obj[1], value, previous];
+ });
+
+ // Re-sanitize options
+ sanitizeOptions(options);
+
+ /*
+ * Execute any valid callbacks for the set options
+ * Also set positioning flag so we don't get loads of redundant repositioning calls.
+ */
+ this.positioning = TRUE;
+ $.each(option, $.proxy(setCallback, this));
+ this.positioning = FALSE;
+
+ // Update position if needed
+ if(this.rendered && this.tooltip[0].offsetWidth > 0 && reposition) {
+ this.reposition( options.position.target === 'mouse' ? NULL : this.cache.event );
+ }
+
+ return this;
+};
+;PROTOTYPE._update = function(content, element, reposition) {
+ var self = this,
+ cache = this.cache;
+
+ // Make sure tooltip is rendered and content is defined. If not return
+ if(!this.rendered || !content) { return FALSE; }
+
+ // Use function to parse content
+ if($.isFunction(content)) {
+ content = content.call(this.elements.target, cache.event, this) || '';
+ }
+
+ // Handle deferred content
+ if($.isFunction(content.then)) {
+ cache.waiting = TRUE;
+ return content.then(function(c) {
+ cache.waiting = FALSE;
+ return self._update(c, element);
+ }, NULL, function(e) {
+ return self._update(e, element);
+ });
+ }
+
+ // If content is null... return false
+ if(content === FALSE || (!content && content !== '')) { return FALSE; }
+
+ // Append new content if its a DOM array and show it if hidden
+ if(content.jquery && content.length > 0) {
+ element.empty().append(
+ content.css({ display: 'block', visibility: 'visible' })
+ );
+ }
+
+ // Content is a regular string, insert the new content
+ else { element.html(content); }
+
+ // Wait for content to be loaded, and reposition
+ return this._waitForContent(element).then(function(images) {
+ if(self.rendered && self.tooltip[0].offsetWidth > 0) {
+ self.reposition(cache.event, !images.length);
+ }
+ });
+};
+
+PROTOTYPE._waitForContent = function(element) {
+ var cache = this.cache;
+
+ // Set flag
+ cache.waiting = TRUE;
+
+ // If imagesLoaded is included, ensure images have loaded and return promise
+ return ( $.fn.imagesLoaded ? element.imagesLoaded() : $.Deferred().resolve([]) )
+ .done(function() { cache.waiting = FALSE; })
+ .promise();
+};
+
+PROTOTYPE._updateContent = function(content, reposition) {
+ this._update(content, this.elements.content, reposition);
+};
+
+PROTOTYPE._updateTitle = function(content, reposition) {
+ if(this._update(content, this.elements.title, reposition) === FALSE) {
+ this._removeTitle(FALSE);
+ }
+};
+
+PROTOTYPE._createTitle = function()
+{
+ var elements = this.elements,
+ id = this._id+'-title';
+
+ // Destroy previous title element, if present
+ if(elements.titlebar) { this._removeTitle(); }
+
+ // Create title bar and title elements
+ elements.titlebar = $('<div />', {
+ 'class': NAMESPACE + '-titlebar ' + (this.options.style.widget ? createWidgetClass('header') : '')
+ })
+ .append(
+ elements.title = $('<div />', {
+ 'id': id,
+ 'class': NAMESPACE + '-title',
+ 'aria-atomic': TRUE
+ })
+ )
+ .insertBefore(elements.content)
+
+ // Button-specific events
+ .delegate('.qtip-close', 'mousedown keydown mouseup keyup mouseout', function(event) {
+ $(this).toggleClass('ui-state-active ui-state-focus', event.type.substr(-4) === 'down');
+ })
+ .delegate('.qtip-close', 'mouseover mouseout', function(event){
+ $(this).toggleClass('ui-state-hover', event.type === 'mouseover');
+ });
+
+ // Create button if enabled
+ if(this.options.content.button) { this._createButton(); }
+};
+
+PROTOTYPE._removeTitle = function(reposition)
+{
+ var elements = this.elements;
+
+ if(elements.title) {
+ elements.titlebar.remove();
+ elements.titlebar = elements.title = elements.button = NULL;
+
+ // Reposition if enabled
+ if(reposition !== FALSE) { this.reposition(); }
+ }
+};
+;PROTOTYPE._createPosClass = function(my) {
+ return NAMESPACE + '-pos-' + (my || this.options.position.my).abbrev();
+};
+
+PROTOTYPE.reposition = function(event, effect) {
+ if(!this.rendered || this.positioning || this.destroyed) { return this; }
+
+ // Set positioning flag
+ this.positioning = TRUE;
+
+ var cache = this.cache,
+ tooltip = this.tooltip,
+ posOptions = this.options.position,
+ target = posOptions.target,
+ my = posOptions.my,
+ at = posOptions.at,
+ viewport = posOptions.viewport,
+ container = posOptions.container,
+ adjust = posOptions.adjust,
+ method = adjust.method.split(' '),
+ tooltipWidth = tooltip.outerWidth(FALSE),
+ tooltipHeight = tooltip.outerHeight(FALSE),
+ targetWidth = 0,
+ targetHeight = 0,
+ type = tooltip.css('position'),
+ position = { left: 0, top: 0 },
+ visible = tooltip[0].offsetWidth > 0,
+ isScroll = event && event.type === 'scroll',
+ win = $(window),
+ doc = container[0].ownerDocument,
+ mouse = this.mouse,
+ pluginCalculations, offset, adjusted, newClass;
+
+ // Check if absolute position was passed
+ if($.isArray(target) && target.length === 2) {
+ // Force left top and set position
+ at = { x: LEFT, y: TOP };
+ position = { left: target[0], top: target[1] };
+ }
+
+ // Check if mouse was the target
+ else if(target === 'mouse') {
+ // Force left top to allow flipping
+ at = { x: LEFT, y: TOP };
+
+ // Use the mouse origin that caused the show event, if distance hiding is enabled
+ if((!adjust.mouse || this.options.hide.distance) && cache.origin && cache.origin.pageX) {
+ event = cache.origin;
+ }
+
+ // Use cached event for resize/scroll events
+ else if(!event || (event && (event.type === 'resize' || event.type === 'scroll'))) {
+ event = cache.event;
+ }
+
+ // Otherwise, use the cached mouse coordinates if available
+ else if(mouse && mouse.pageX) {
+ event = mouse;
+ }
+
+ // Calculate body and container offset and take them into account below
+ if(type !== 'static') { position = container.offset(); }
+ if(doc.body.offsetWidth !== (window.innerWidth || doc.documentElement.clientWidth)) {
+ offset = $(document.body).offset();
+ }
+
+ // Use event coordinates for position
+ position = {
+ left: event.pageX - position.left + (offset && offset.left || 0),
+ top: event.pageY - position.top + (offset && offset.top || 0)
+ };
+
+ // Scroll events are a pain, some browsers
+ if(adjust.mouse && isScroll && mouse) {
+ position.left -= (mouse.scrollX || 0) - win.scrollLeft();
+ position.top -= (mouse.scrollY || 0) - win.scrollTop();
+ }
+ }
+
+ // Target wasn't mouse or absolute...
+ else {
+ // Check if event targetting is being used
+ if(target === 'event') {
+ if(event && event.target && event.type !== 'scroll' && event.type !== 'resize') {
+ cache.target = $(event.target);
+ }
+ else if(!event.target) {
+ cache.target = this.elements.target;
+ }
+ }
+ else if(target !== 'event'){
+ cache.target = $(target.jquery ? target : this.elements.target);
+ }
+ target = cache.target;
+
+ // Parse the target into a jQuery object and make sure there's an element present
+ target = $(target).eq(0);
+ if(target.length === 0) { return this; }
+
+ // Check if window or document is the target
+ else if(target[0] === document || target[0] === window) {
+ targetWidth = BROWSER.iOS ? window.innerWidth : target.width();
+ targetHeight = BROWSER.iOS ? window.innerHeight : target.height();
+
+ if(target[0] === window) {
+ position = {
+ top: (viewport || target).scrollTop(),
+ left: (viewport || target).scrollLeft()
+ };
+ }
+ }
+
+ // Check if the target is an <AREA> element
+ else if(PLUGINS.imagemap && target.is('area')) {
+ pluginCalculations = PLUGINS.imagemap(this, target, at, PLUGINS.viewport ? method : FALSE);
+ }
+
+ // Check if the target is an SVG element
+ else if(PLUGINS.svg && target && target[0].ownerSVGElement) {
+ pluginCalculations = PLUGINS.svg(this, target, at, PLUGINS.viewport ? method : FALSE);
+ }
+
+ // Otherwise use regular jQuery methods
+ else {
+ targetWidth = target.outerWidth(FALSE);
+ targetHeight = target.outerHeight(FALSE);
+ position = target.offset();
+ }
+
+ // Parse returned plugin values into proper variables
+ if(pluginCalculations) {
+ targetWidth = pluginCalculations.width;
+ targetHeight = pluginCalculations.height;
+ offset = pluginCalculations.offset;
+ position = pluginCalculations.position;
+ }
+
+ // Adjust position to take into account offset parents
+ position = this.reposition.offset(target, position, container);
+
+ // Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2-4.0 & v4.3-4.3.2)
+ if((BROWSER.iOS > 3.1 && BROWSER.iOS < 4.1) ||
+ (BROWSER.iOS >= 4.3 && BROWSER.iOS < 4.33) ||
+ (!BROWSER.iOS && type === 'fixed')
+ ){
+ position.left -= win.scrollLeft();
+ position.top -= win.scrollTop();
+ }
+
+ // Adjust position relative to target
+ if(!pluginCalculations || (pluginCalculations && pluginCalculations.adjustable !== FALSE)) {
+ position.left += at.x === RIGHT ? targetWidth : at.x === CENTER ? targetWidth / 2 : 0;
+ position.top += at.y === BOTTOM ? targetHeight : at.y === CENTER ? targetHeight / 2 : 0;
+ }
+ }
+
+ // Adjust position relative to tooltip
+ position.left += adjust.x + (my.x === RIGHT ? -tooltipWidth : my.x === CENTER ? -tooltipWidth / 2 : 0);
+ position.top += adjust.y + (my.y === BOTTOM ? -tooltipHeight : my.y === CENTER ? -tooltipHeight / 2 : 0);
+
+ // Use viewport adjustment plugin if enabled
+ if(PLUGINS.viewport) {
+ adjusted = position.adjusted = PLUGINS.viewport(
+ this, position, posOptions, targetWidth, targetHeight, tooltipWidth, tooltipHeight
+ );
+
+ // Apply offsets supplied by positioning plugin (if used)
+ if(offset && adjusted.left) { position.left += offset.left; }
+ if(offset && adjusted.top) { position.top += offset.top; }
+
+ // Apply any new 'my' position
+ if(adjusted.my) { this.position.my = adjusted.my; }
+ }
+
+ // Viewport adjustment is disabled, set values to zero
+ else { position.adjusted = { left: 0, top: 0 }; }
+
+ // Set tooltip position class if it's changed
+ if(cache.posClass !== (newClass = this._createPosClass(this.position.my))) {
+ tooltip.removeClass(cache.posClass).addClass( (cache.posClass = newClass) );
+ }
+
+ // tooltipmove event
+ if(!this._trigger('move', [position, viewport.elem || viewport], event)) { return this; }
+ delete position.adjusted;
+
+ // If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly
+ if(effect === FALSE || !visible || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) {
+ tooltip.css(position);
+ }
+
+ // Use custom function if provided
+ else if($.isFunction(posOptions.effect)) {
+ posOptions.effect.call(tooltip, this, $.extend({}, position));
+ tooltip.queue(function(next) {
+ // Reset attributes to avoid cross-browser rendering bugs
+ $(this).css({ opacity: '', height: '' });
+ if(BROWSER.ie) { this.style.removeAttribute('filter'); }
+
+ next();
+ });
+ }
+
+ // Set positioning flag
+ this.positioning = FALSE;
+
+ return this;
+};
+
+// Custom (more correct for qTip!) offset calculator
+PROTOTYPE.reposition.offset = function(elem, pos, container) {
+ if(!container[0]) { return pos; }
+
+ var ownerDocument = $(elem[0].ownerDocument),
+ quirks = !!BROWSER.ie && document.compatMode !== 'CSS1Compat',
+ parent = container[0],
+ scrolled, position, parentOffset, overflow;
+
+ function scroll(e, i) {
+ pos.left += i * e.scrollLeft();
+ pos.top += i * e.scrollTop();
+ }
+
+ // Compensate for non-static containers offset
+ do {
+ if((position = $.css(parent, 'position')) !== 'static') {
+ if(position === 'fixed') {
+ parentOffset = parent.getBoundingClientRect();
+ scroll(ownerDocument, -1);
+ }
+ else {
+ parentOffset = $(parent).position();
+ parentOffset.left += (parseFloat($.css(parent, 'borderLeftWidth')) || 0);
+ parentOffset.top += (parseFloat($.css(parent, 'borderTopWidth')) || 0);
+ }
+
+ pos.left -= parentOffset.left + (parseFloat($.css(parent, 'marginLeft')) || 0);
+ pos.top -= parentOffset.top + (parseFloat($.css(parent, 'marginTop')) || 0);
+
+ // If this is the first parent element with an overflow of "scroll" or "auto", store it
+ if(!scrolled && (overflow = $.css(parent, 'overflow')) !== 'hidden' && overflow !== 'visible') { scrolled = $(parent); }
+ }
+ }
+ while((parent = parent.offsetParent));
+
+ // Compensate for containers scroll if it also has an offsetParent (or in IE quirks mode)
+ if(scrolled && (scrolled[0] !== ownerDocument[0] || quirks)) {
+ scroll(scrolled, 1);
+ }
+
+ return pos;
+};
+
+// Corner class
+var C = (CORNER = PROTOTYPE.reposition.Corner = function(corner, forceY) {
+ corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, CENTER).toLowerCase();
+ this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase();
+ this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase();
+ this.forceY = !!forceY;
+
+ var f = corner.charAt(0);
+ this.precedance = (f === 't' || f === 'b' ? Y : X);
+}).prototype;
+
+C.invert = function(z, center) {
+ this[z] = this[z] === LEFT ? RIGHT : this[z] === RIGHT ? LEFT : center || this[z];
+};
+
+C.string = function(join) {
+ var x = this.x, y = this.y;
+
+ var result = x !== y ?
+ (x === 'center' || y !== 'center' && (this.precedance === Y || this.forceY) ?
+ [y,x] : [x,y]
+ ) :
+ [x];
+
+ return join !== false ? result.join(' ') : result;
+};
+
+C.abbrev = function() {
+ var result = this.string(false);
+ return result[0].charAt(0) + (result[1] && result[1].charAt(0) || '');
+};
+
+C.clone = function() {
+ return new CORNER( this.string(), this.forceY );
+};
+
+;
+PROTOTYPE.toggle = function(state, event) {
+ var cache = this.cache,
+ options = this.options,
+ tooltip = this.tooltip;
+
+ // Try to prevent flickering when tooltip overlaps show element
+ if(event) {
+ if((/over|enter/).test(event.type) && cache.event && (/out|leave/).test(cache.event.type) &&
+ options.show.target.add(event.target).length === options.show.target.length &&
+ tooltip.has(event.relatedTarget).length) {
+ return this;
+ }
+
+ // Cache event
+ cache.event = $.event.fix(event);
+ }
+
+ // If we're currently waiting and we've just hidden... stop it
+ this.waiting && !state && (this.hiddenDuringWait = TRUE);
+
+ // Render the tooltip if showing and it isn't already
+ if(!this.rendered) { return state ? this.render(1) : this; }
+ else if(this.destroyed || this.disabled) { return this; }
+
+ var type = state ? 'show' : 'hide',
+ opts = this.options[type],
+ otherOpts = this.options[ !state ? 'show' : 'hide' ],
+ posOptions = this.options.position,
+ contentOptions = this.options.content,
+ width = this.tooltip.css('width'),
+ visible = this.tooltip.is(':visible'),
+ animate = state || opts.target.length === 1,
+ sameTarget = !event || opts.target.length < 2 || cache.target[0] === event.target,
+ identicalState, allow, showEvent, delay, after;
+
+ // Detect state if valid one isn't provided
+ if((typeof state).search('boolean|number')) { state = !visible; }
+
+ // Check if the tooltip is in an identical state to the new would-be state
+ identicalState = !tooltip.is(':animated') && visible === state && sameTarget;
+
+ // Fire tooltip(show/hide) event and check if destroyed
+ allow = !identicalState ? !!this._trigger(type, [90]) : NULL;
+
+ // Check to make sure the tooltip wasn't destroyed in the callback
+ if(this.destroyed) { return this; }
+
+ // If the user didn't stop the method prematurely and we're showing the tooltip, focus it
+ if(allow !== FALSE && state) { this.focus(event); }
+
+ // If the state hasn't changed or the user stopped it, return early
+ if(!allow || identicalState) { return this; }
+
+ // Set ARIA hidden attribute
+ $.attr(tooltip[0], 'aria-hidden', !!!state);
+
+ // Execute state specific properties
+ if(state) {
+ // Store show origin coordinates
+ this.mouse && (cache.origin = $.event.fix(this.mouse));
+
+ // Update tooltip content & title if it's a dynamic function
+ if($.isFunction(contentOptions.text)) { this._updateContent(contentOptions.text, FALSE); }
+ if($.isFunction(contentOptions.title)) { this._updateTitle(contentOptions.title, FALSE); }
+
+ // Cache mousemove events for positioning purposes (if not already tracking)
+ if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) {
+ $(document).bind('mousemove.'+NAMESPACE, this._storeMouse);
+ trackingBound = TRUE;
+ }
+
+ // Update the tooltip position (set width first to prevent viewport/max-width issues)
+ if(!width) { tooltip.css('width', tooltip.outerWidth(FALSE)); }
+ this.reposition(event, arguments[2]);
+ if(!width) { tooltip.css('width', ''); }
+
+ // Hide other tooltips if tooltip is solo
+ if(!!opts.solo) {
+ (typeof opts.solo === 'string' ? $(opts.solo) : $(SELECTOR, opts.solo))
+ .not(tooltip).not(opts.target).qtip('hide', $.Event('tooltipsolo'));
+ }
+ }
+ else {
+ // Clear show timer if we're hiding
+ clearTimeout(this.timers.show);
+
+ // Remove cached origin on hide
+ delete cache.origin;
+
+ // Remove mouse tracking event if not needed (all tracking qTips are hidden)
+ if(trackingBound && !$(SELECTOR+'[tracking="true"]:visible', opts.solo).not(tooltip).length) {
+ $(document).unbind('mousemove.'+NAMESPACE);
+ trackingBound = FALSE;
+ }
+
+ // Blur the tooltip
+ this.blur(event);
+ }
+
+ // Define post-animation, state specific properties
+ after = $.proxy(function() {
+ if(state) {
+ // Prevent antialias from disappearing in IE by removing filter
+ if(BROWSER.ie) { tooltip[0].style.removeAttribute('filter'); }
+
+ // Remove overflow setting to prevent tip bugs
+ tooltip.css('overflow', '');
+
+ // Autofocus elements if enabled
+ if('string' === typeof opts.autofocus) {
+ $(this.options.show.autofocus, tooltip).focus();
+ }
+
+ // If set, hide tooltip when inactive for delay period
+ this.options.show.target.trigger('qtip-'+this.id+'-inactive');
+ }
+ else {
+ // Reset CSS states
+ tooltip.css({
+ display: '',
+ visibility: '',
+ opacity: '',
+ left: '',
+ top: ''
+ });
+ }
+
+ // tooltipvisible/tooltiphidden events
+ this._trigger(state ? 'visible' : 'hidden');
+ }, this);
+
+ // If no effect type is supplied, use a simple toggle
+ if(opts.effect === FALSE || animate === FALSE) {
+ tooltip[ type ]();
+ after();
+ }
+
+ // Use custom function if provided
+ else if($.isFunction(opts.effect)) {
+ tooltip.stop(1, 1);
+ opts.effect.call(tooltip, this);
+ tooltip.queue('fx', function(n) {
+ after(); n();
+ });
+ }
+
+ // Use basic fade function by default
+ else { tooltip.fadeTo(90, state ? 1 : 0, after); }
+
+ // If inactive hide method is set, active it
+ if(state) { opts.target.trigger('qtip-'+this.id+'-inactive'); }
+
+ return this;
+};
+
+PROTOTYPE.show = function(event) { return this.toggle(TRUE, event); };
+
+PROTOTYPE.hide = function(event) { return this.toggle(FALSE, event); };
+;PROTOTYPE.focus = function(event) {
+ if(!this.rendered || this.destroyed) { return this; }
+
+ var qtips = $(SELECTOR),
+ tooltip = this.tooltip,
+ curIndex = parseInt(tooltip[0].style.zIndex, 10),
+ newIndex = QTIP.zindex + qtips.length,
+ focusedElem;
+
+ // Only update the z-index if it has changed and tooltip is not already focused
+ if(!tooltip.hasClass(CLASS_FOCUS)) {
+ // tooltipfocus event
+ if(this._trigger('focus', [newIndex], event)) {
+ // Only update z-index's if they've changed
+ if(curIndex !== newIndex) {
+ // Reduce our z-index's and keep them properly ordered
+ qtips.each(function() {
+ if(this.style.zIndex > curIndex) {
+ this.style.zIndex = this.style.zIndex - 1;
+ }
+ });
+
+ // Fire blur event for focused tooltip
+ qtips.filter('.' + CLASS_FOCUS).qtip('blur', event);
+ }
+
+ // Set the new z-index
+ tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex;
+ }
+ }
+
+ return this;
+};
+
+PROTOTYPE.blur = function(event) {
+ if(!this.rendered || this.destroyed) { return this; }
+
+ // Set focused status to FALSE
+ this.tooltip.removeClass(CLASS_FOCUS);
+
+ // tooltipblur event
+ this._trigger('blur', [ this.tooltip.css('zIndex') ], event);
+
+ return this;
+};
+;PROTOTYPE.disable = function(state) {
+ if(this.destroyed) { return this; }
+
+ // If 'toggle' is passed, toggle the current state
+ if(state === 'toggle') {
+ state = !(this.rendered ? this.tooltip.hasClass(CLASS_DISABLED) : this.disabled);
+ }
+
+ // Disable if no state passed
+ else if('boolean' !== typeof state) {
+ state = TRUE;
+ }
+
+ if(this.rendered) {
+ this.tooltip.toggleClass(CLASS_DISABLED, state)
+ .attr('aria-disabled', state);
+ }
+
+ this.disabled = !!state;
+
+ return this;
+};
+
+PROTOTYPE.enable = function() { return this.disable(FALSE); };
+;PROTOTYPE._createButton = function()
+{
+ var self = this,
+ elements = this.elements,
+ tooltip = elements.tooltip,
+ button = this.options.content.button,
+ isString = typeof button === 'string',
+ close = isString ? button : 'Close tooltip';
+
+ if(elements.button) { elements.button.remove(); }
+
+ // Use custom button if one was supplied by user, else use default
+ if(button.jquery) {
+ elements.button = button;
+ }
+ else {
+ elements.button = $('<a />', {
+ 'class': 'qtip-close ' + (this.options.style.widget ? '' : NAMESPACE+'-icon'),
+ 'title': close,
+ 'aria-label': close
+ })
+ .prepend(
+ $('<span />', {
+ 'class': 'ui-icon ui-icon-close',
+ 'html': '×'
+ })
+ );
+ }
+
+ // Create button and setup attributes
+ elements.button.appendTo(elements.titlebar || tooltip)
+ .attr('role', 'button')
+ .click(function(event) {
+ if(!tooltip.hasClass(CLASS_DISABLED)) { self.hide(event); }
+ return FALSE;
+ });
+};
+
+PROTOTYPE._updateButton = function(button)
+{
+ // Make sure tooltip is rendered and if not, return
+ if(!this.rendered) { return FALSE; }
+
+ var elem = this.elements.button;
+ if(button) { this._createButton(); }
+ else { elem.remove(); }
+};
+;// Widget class creator
+function createWidgetClass(cls) {
+ return WIDGET.concat('').join(cls ? '-'+cls+' ' : ' ');
+}
+
+// Widget class setter method
+PROTOTYPE._setWidget = function()
+{
+ var on = this.options.style.widget,
+ elements = this.elements,
+ tooltip = elements.tooltip,
+ disabled = tooltip.hasClass(CLASS_DISABLED);
+
+ tooltip.removeClass(CLASS_DISABLED);
+ CLASS_DISABLED = on ? 'ui-state-disabled' : 'qtip-disabled';
+ tooltip.toggleClass(CLASS_DISABLED, disabled);
+
+ tooltip.toggleClass('ui-helper-reset '+createWidgetClass(), on).toggleClass(CLASS_DEFAULT, this.options.style.def && !on);
+
+ if(elements.content) {
+ elements.content.toggleClass( createWidgetClass('content'), on);
+ }
+ if(elements.titlebar) {
+ elements.titlebar.toggleClass( createWidgetClass('header'), on);
+ }
+ if(elements.button) {
+ elements.button.toggleClass(NAMESPACE+'-icon', !on);
+ }
+};
+;function delay(callback, duration) {
+ // If tooltip has displayed, start hide timer
+ if(duration > 0) {
+ return setTimeout(
+ $.proxy(callback, this), duration
+ );
+ }
+ else{ callback.call(this); }
+}
+
+function showMethod(event) {
+ if(this.tooltip.hasClass(CLASS_DISABLED)) { return; }
+
+ // Clear hide timers
+ clearTimeout(this.timers.show);
+ clearTimeout(this.timers.hide);
+
+ // Start show timer
+ this.timers.show = delay.call(this,
+ function() { this.toggle(TRUE, event); },
+ this.options.show.delay
+ );
+}
+
+function hideMethod(event) {
+ if(this.tooltip.hasClass(CLASS_DISABLED) || this.destroyed) { return; }
+
+ // Check if new target was actually the tooltip element
+ var relatedTarget = $(event.relatedTarget),
+ ontoTooltip = relatedTarget.closest(SELECTOR)[0] === this.tooltip[0],
+ ontoTarget = relatedTarget[0] === this.options.show.target[0];
+
+ // Clear timers and stop animation queue
+ clearTimeout(this.timers.show);
+ clearTimeout(this.timers.hide);
+
+ // Prevent hiding if tooltip is fixed and event target is the tooltip.
+ // Or if mouse positioning is enabled and cursor momentarily overlaps
+ if(this !== relatedTarget[0] &&
+ (this.options.position.target === 'mouse' && ontoTooltip) ||
+ (this.options.hide.fixed && (
+ (/mouse(out|leave|move)/).test(event.type) && (ontoTooltip || ontoTarget))
+ ))
+ {
+ try {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ } catch(e) {}
+
+ return;
+ }
+
+ // If tooltip has displayed, start hide timer
+ this.timers.hide = delay.call(this,
+ function() { this.toggle(FALSE, event); },
+ this.options.hide.delay,
+ this
+ );
+}
+
+function inactiveMethod(event) {
+ if(this.tooltip.hasClass(CLASS_DISABLED) || !this.options.hide.inactive) { return; }
+
+ // Clear timer
+ clearTimeout(this.timers.inactive);
+
+ this.timers.inactive = delay.call(this,
+ function(){ this.hide(event); },
+ this.options.hide.inactive
+ );
+}
+
+function repositionMethod(event) {
+ if(this.rendered && this.tooltip[0].offsetWidth > 0) { this.reposition(event); }
+}
+
+// Store mouse coordinates
+PROTOTYPE._storeMouse = function(event) {
+ (this.mouse = $.event.fix(event)).type = 'mousemove';
+ return this;
+};
+
+// Bind events
+PROTOTYPE._bind = function(targets, events, method, suffix, context) {
+ if(!targets || !method || !events.length) { return; }
+ var ns = '.' + this._id + (suffix ? '-'+suffix : '');
+ $(targets).bind(
+ (events.split ? events : events.join(ns + ' ')) + ns,
+ $.proxy(method, context || this)
+ );
+ return this;
+};
+PROTOTYPE._unbind = function(targets, suffix) {
+ targets && $(targets).unbind('.' + this._id + (suffix ? '-'+suffix : ''));
+ return this;
+};
+
+// Global delegation helper
+function delegate(selector, events, method) {
+ $(document.body).delegate(selector,
+ (events.split ? events : events.join('.'+NAMESPACE + ' ')) + '.'+NAMESPACE,
+ function() {
+ var api = QTIP.api[ $.attr(this, ATTR_ID) ];
+ api && !api.disabled && method.apply(api, arguments);
+ }
+ );
+}
+// Event trigger
+PROTOTYPE._trigger = function(type, args, event) {
+ var callback = $.Event('tooltip'+type);
+ callback.originalEvent = (event && $.extend({}, event)) || this.cache.event || NULL;
+
+ this.triggering = type;
+ this.tooltip.trigger(callback, [this].concat(args || []));
+ this.triggering = FALSE;
+
+ return !callback.isDefaultPrevented();
+};
+
+PROTOTYPE._bindEvents = function(showEvents, hideEvents, showTargets, hideTargets, showMethod, hideMethod) {
+ // Get tasrgets that lye within both
+ var similarTargets = showTargets.filter( hideTargets ).add( hideTargets.filter(showTargets) ),
+ toggleEvents = [];
+
+ // If hide and show targets are the same...
+ if(similarTargets.length) {
+
+ // Filter identical show/hide events
+ $.each(hideEvents, function(i, type) {
+ var showIndex = $.inArray(type, showEvents);
+
+ // Both events are identical, remove from both hide and show events
+ // and append to toggleEvents
+ showIndex > -1 && toggleEvents.push( showEvents.splice( showIndex, 1 )[0] );
+ });
+
+ // Toggle events are special case of identical show/hide events, which happen in sequence
+ if(toggleEvents.length) {
+ // Bind toggle events to the similar targets
+ this._bind(similarTargets, toggleEvents, function(event) {
+ var state = this.rendered ? this.tooltip[0].offsetWidth > 0 : false;
+ (state ? hideMethod : showMethod).call(this, event);
+ });
+
+ // Remove the similar targets from the regular show/hide bindings
+ showTargets = showTargets.not(similarTargets);
+ hideTargets = hideTargets.not(similarTargets);
+ }
+ }
+
+ // Apply show/hide/toggle events
+ this._bind(showTargets, showEvents, showMethod);
+ this._bind(hideTargets, hideEvents, hideMethod);
+};
+
+PROTOTYPE._assignInitialEvents = function(event) {
+ var options = this.options,
+ showTarget = options.show.target,
+ hideTarget = options.hide.target,
+ showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [],
+ hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : [];
+
+ // Catch remove/removeqtip events on target element to destroy redundant tooltips
+ this._bind(this.elements.target, ['remove', 'removeqtip'], function(event) {
+ this.destroy(true);
+ }, 'destroy');
+
+ /*
+ * Make sure hoverIntent functions properly by using mouseleave as a hide event if
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
+ */
+ if(/mouse(over|enter)/i.test(options.show.event) && !/mouse(out|leave)/i.test(options.hide.event)) {
+ hideEvents.push('mouseleave');
+ }
+
+ /*
+ * Also make sure initial mouse targetting works correctly by caching mousemove coords
+ * on show targets before the tooltip has rendered. Also set onTarget when triggered to
+ * keep mouse tracking working.
+ */
+ this._bind(showTarget, 'mousemove', function(event) {
+ this._storeMouse(event);
+ this.cache.onTarget = TRUE;
+ });
+
+ // Define hoverIntent function
+ function hoverIntent(event) {
+ // Only continue if tooltip isn't disabled
+ if(this.disabled || this.destroyed) { return FALSE; }
+
+ // Cache the event data
+ this.cache.event = event && $.event.fix(event);
+ this.cache.target = event && $(event.target);
+
+ // Start the event sequence
+ clearTimeout(this.timers.show);
+ this.timers.show = delay.call(this,
+ function() { this.render(typeof event === 'object' || options.show.ready); },
+ options.prerender ? 0 : options.show.delay
+ );
+ }
+
+ // Filter and bind events
+ this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, hoverIntent, function() {
+ if(!this.timers) { return FALSE; }
+ clearTimeout(this.timers.show);
+ });
+
+ // Prerendering is enabled, create tooltip now
+ if(options.show.ready || options.prerender) { hoverIntent.call(this, event); }
+};
+
+// Event assignment method
+PROTOTYPE._assignEvents = function() {
+ var self = this,
+ options = this.options,
+ posOptions = options.position,
+
+ tooltip = this.tooltip,
+ showTarget = options.show.target,
+ hideTarget = options.hide.target,
+ containerTarget = posOptions.container,
+ viewportTarget = posOptions.viewport,
+ documentTarget = $(document),
+ bodyTarget = $(document.body),
+ windowTarget = $(window),
+
+ showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [],
+ hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : [];
+
+
+ // Assign passed event callbacks
+ $.each(options.events, function(name, callback) {
+ self._bind(tooltip, name === 'toggle' ? ['tooltipshow','tooltiphide'] : ['tooltip'+name], callback, null, tooltip);
+ });
+
+ // Hide tooltips when leaving current window/frame (but not select/option elements)
+ if(/mouse(out|leave)/i.test(options.hide.event) && options.hide.leave === 'window') {
+ this._bind(documentTarget, ['mouseout', 'blur'], function(event) {
+ if(!/select|option/.test(event.target.nodeName) && !event.relatedTarget) {
+ this.hide(event);
+ }
+ });
+ }
+
+ // Enable hide.fixed by adding appropriate class
+ if(options.hide.fixed) {
+ hideTarget = hideTarget.add( tooltip.addClass(CLASS_FIXED) );
+ }
+
+ /*
+ * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
+ */
+ else if(/mouse(over|enter)/i.test(options.show.event)) {
+ this._bind(hideTarget, 'mouseleave', function() {
+ clearTimeout(this.timers.show);
+ });
+ }
+
+ // Hide tooltip on document mousedown if unfocus events are enabled
+ if(('' + options.hide.event).indexOf('unfocus') > -1) {
+ this._bind(containerTarget.closest('html'), ['mousedown', 'touchstart'], function(event) {
+ var elem = $(event.target),
+ enabled = this.rendered && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0,
+ isAncestor = elem.parents(SELECTOR).filter(this.tooltip[0]).length > 0;
+
+ if(elem[0] !== this.target[0] && elem[0] !== this.tooltip[0] && !isAncestor &&
+ !this.target.has(elem[0]).length && enabled
+ ) {
+ this.hide(event);
+ }
+ });
+ }
+
+ // Check if the tooltip hides when inactive
+ if('number' === typeof options.hide.inactive) {
+ // Bind inactive method to show target(s) as a custom event
+ this._bind(showTarget, 'qtip-'+this.id+'-inactive', inactiveMethod, 'inactive');
+
+ // Define events which reset the 'inactive' event handler
+ this._bind(hideTarget.add(tooltip), QTIP.inactiveEvents, inactiveMethod);
+ }
+
+ // Filter and bind events
+ this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, showMethod, hideMethod);
+
+ // Mouse movement bindings
+ this._bind(showTarget.add(tooltip), 'mousemove', function(event) {
+ // Check if the tooltip hides when mouse is moved a certain distance
+ if('number' === typeof options.hide.distance) {
+ var origin = this.cache.origin || {},
+ limit = this.options.hide.distance,
+ abs = Math.abs;
+
+ // Check if the movement has gone beyond the limit, and hide it if so
+ if(abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) {
+ this.hide(event);
+ }
+ }
+
+ // Cache mousemove coords on show targets
+ this._storeMouse(event);
+ });
+
+ // Mouse positioning events
+ if(posOptions.target === 'mouse') {
+ // If mouse adjustment is on...
+ if(posOptions.adjust.mouse) {
+ // Apply a mouseleave event so we don't get problems with overlapping
+ if(options.hide.event) {
+ // Track if we're on the target or not
+ this._bind(showTarget, ['mouseenter', 'mouseleave'], function(event) {
+ if(!this.cache) {return FALSE; }
+ this.cache.onTarget = event.type === 'mouseenter';
+ });
+ }
+
+ // Update tooltip position on mousemove
+ this._bind(documentTarget, 'mousemove', function(event) {
+ // Update the tooltip position only if the tooltip is visible and adjustment is enabled
+ if(this.rendered && this.cache.onTarget && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0) {
+ this.reposition(event);
+ }
+ });
+ }
+ }
+
+ // Adjust positions of the tooltip on window resize if enabled
+ if(posOptions.adjust.resize || viewportTarget.length) {
+ this._bind( $.event.special.resize ? viewportTarget : windowTarget, 'resize', repositionMethod );
+ }
+
+ // Adjust tooltip position on scroll of the window or viewport element if present
+ if(posOptions.adjust.scroll) {
+ this._bind( windowTarget.add(posOptions.container), 'scroll', reposit
<TRUNCATED>
[40/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Add
auto-refresh to dashboard
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Add auto-refresh to dashboard
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/616b7d56
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/616b7d56
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/616b7d56
Branch: refs/heads/master
Commit: 616b7d56f76d32276081b54f1eb10680f9fa0ffd
Parents: c36b3d7
Author: Piotr Godek <pi...@gmail.com>
Authored: Thu Sep 10 00:18:12 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:52 2015 +0200
----------------------------------------------------------------------
.../app/scripts/modules/jobs/jobs.ctrl.coffee | 45 ++++--
.../app/scripts/modules/jobs/jobs.dir.coffee | 66 +++++----
.../app/scripts/modules/jobs/jobs.svc.coffee | 32 ++---
.../web-dashboard/app/styles/animate.styl | 18 +++
flink-runtime-web/web-dashboard/web/js/index.js | 140 +++++++++++--------
5 files changed, 185 insertions(+), 116 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/616b7d56/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
index 1e367c9..a5f9594 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
@@ -42,7 +42,7 @@ angular.module('flinkApp')
# --------------------------------------
-.controller 'SingleJobController', ($scope, $state, $stateParams, JobsService, $rootScope) ->
+.controller 'SingleJobController', ($scope, $state, $stateParams, JobsService, $rootScope, flinkConfig, $interval) ->
console.log 'SingleJobController'
$scope.jobid = $stateParams.jobid
@@ -53,10 +53,22 @@ angular.module('flinkApp')
$rootScope.job = data
$rootScope.plan = data.plan
+ refresher = $interval ->
+ JobsService.loadJob($stateParams.jobid).then (data) ->
+ $rootScope.job = data
+ # $rootScope.plan = data.plan
+
+ $scope.$broadcast 'reload'
+
+ , flinkConfig["refresh-interval"]
+
$scope.$on '$destroy', ->
$rootScope.job = null
$rootScope.plan = null
+ $interval.cancel(refresher)
+
+
# --------------------------------------
.controller 'JobPlanController', ($scope, $state, $stateParams, JobsService) ->
@@ -70,13 +82,7 @@ angular.module('flinkApp')
$scope.nodeid = nodeid
$scope.vertex = null
- if $state.is('single-job.plan.overview')
- JobsService.getSubtasks(nodeid).then (data) ->
- $scope.vertex = data
-
- else if $state.is('single-job.plan.accumulators')
- JobsService.getAccumulators(nodeid).then (data) ->
- $scope.vertex = data
+ $scope.$broadcast 'reload'
else
$scope.nodeid = null
@@ -87,25 +93,44 @@ angular.module('flinkApp')
.controller 'JobPlanOverviewController', ($scope, JobsService) ->
console.log 'JobPlanOverviewController'
- if $scope.nodeid and !$scope.vertex.st
+ if $scope.nodeid and (!$scope.vertex or !$scope.vertex.st)
JobsService.getSubtasks($scope.nodeid).then (data) ->
$scope.vertex = data
+ $scope.$on 'reload', (event) ->
+ console.log 'JobPlanOverviewController'
+ if $scope.nodeid
+ JobsService.getSubtasks($scope.nodeid).then (data) ->
+ $scope.vertex = data
+
# --------------------------------------
.controller 'JobPlanAccumulatorsController', ($scope, JobsService) ->
console.log 'JobPlanAccumulatorsController'
- if $scope.nodeid and !$scope.vertex.accumulators
+ if $scope.nodeid and (!$scope.vertex or !$scope.vertex.accumulators)
JobsService.getAccumulators($scope.nodeid).then (data) ->
$scope.vertex = data
+ $scope.$on 'reload', (event) ->
+ console.log 'JobPlanAccumulatorsController'
+ if $scope.nodeid
+ JobsService.getAccumulators($scope.nodeid).then (data) ->
+ $scope.vertex = data
+
# --------------------------------------
.controller 'JobTimelineVertexController', ($scope, $state, $stateParams, JobsService) ->
+ console.log 'JobTimelineVertexController'
+
JobsService.getVertex($stateParams.vertexId).then (data) ->
$scope.vertex = data
+ $scope.$on 'reload', (event) ->
+ console.log 'JobTimelineVertexController'
+ JobsService.getVertex($stateParams.vertexId).then (data) ->
+ $scope.vertex = data
+
# --------------------------------------
.controller 'JobExceptionsController', ($scope, $state, $stateParams, JobsService) ->
http://git-wip-us.apache.org/repos/asf/flink/blob/616b7d56/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
index 74f616a..60d6224 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
@@ -33,37 +33,45 @@ angular.module('flinkApp')
angular.element(svgEl).attr('width', containerW)
analyzeTime = (data) ->
+ d3.select(svgEl).selectAll("*").remove()
+
testData = []
angular.forEach data.subtasks, (subtask, i) ->
+ console.log data.subtasks
+
+ times = [
+ {
+ label: "Scheduled"
+ color: "#666"
+ borderColor: "#555"
+ starting_time: subtask.timestamps["SCHEDULED"]
+ ending_time: subtask.timestamps["DEPLOYING"]
+ type: 'regular'
+ }
+ {
+ label: "Deploying"
+ color: "#aaa"
+ borderColor: "#555"
+ starting_time: subtask.timestamps["DEPLOYING"]
+ ending_time: subtask.timestamps["RUNNING"]
+ type: 'regular'
+ }
+ ]
+
+ if subtask.timestamps["FINISHED"] > 0
+ times.push {
+ label: "Running"
+ color: "#ddd"
+ borderColor: "#555"
+ starting_time: subtask.timestamps["RUNNING"]
+ ending_time: subtask.timestamps["FINISHED"]
+ type: 'regular'
+ }
+
testData.push {
label: "#{subtask.host} (#{subtask.subtask})"
- times: [
- {
- label: "Scheduled"
- color: "#666"
- borderColor: "#555"
- starting_time: subtask.timestamps["SCHEDULED"]
- ending_time: subtask.timestamps["DEPLOYING"]
- type: 'regular'
- }
- {
- label: "Deploying"
- color: "#aaa"
- borderColor: "#555"
- starting_time: subtask.timestamps["DEPLOYING"]
- ending_time: subtask.timestamps["RUNNING"]
- type: 'regular'
- }
- {
- label: "Running"
- color: "#ddd"
- borderColor: "#555"
- starting_time: subtask.timestamps["RUNNING"]
- ending_time: subtask.timestamps["FINISHED"]
- type: 'regular'
- }
- ]
+ times: times
}
chart = d3.timeline().stack()
@@ -106,6 +114,8 @@ angular.module('flinkApp')
label.replace(">", ">")
analyzeTime = (data) ->
+ d3.select(svgEl).selectAll("*").remove()
+
testData = []
testData.push
@@ -118,7 +128,6 @@ angular.module('flinkApp')
type: 'scheduled'
]
-
angular.forEach data.vertices, (vertex) ->
if vertex['start-time'] > -1
testData.push
@@ -187,6 +196,7 @@ angular.module('flinkApp')
d3tmpSvg = d3.select(mainTmpElement)
# angular.element(mainG).empty()
+ # d3mainSvgG.selectAll("*").remove()
containerW = elem.width()
angular.element(elem.children()[0]).width(containerW)
@@ -420,6 +430,8 @@ angular.module('flinkApp')
return el.step_function[j] if el.step_function[j].id is nodeID
drawGraph = (data) ->
+ # console.log data
+
g = new dagreD3.graphlib.Graph({ multigraph: true, compound: true }).setGraph({
nodesep: 70
edgesep: 0
http://git-wip-us.apache.org/repos/asf/flink/blob/616b7d56/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index 3142e4f..2ef6a34 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -67,9 +67,9 @@ angular.module('flinkApp')
else 'default'
@setEndTimes = (list) ->
- angular.forEach list, (job, jobKey) ->
- unless job['end-time'] > -1
- job['end-time'] = job['start-time'] + job['duration']
+ angular.forEach list, (item, jobKey) ->
+ unless item['end-time'] > -1
+ item['end-time'] = item['start-time'] + item['duration']
@listJobs = ->
deferred = $q.defer()
@@ -112,18 +112,6 @@ angular.module('flinkApp')
deferreds.job.promise
- @loadPlan = (jobid) ->
- currentPlan = null
- deferreds.plan = $q.defer()
-
- $http.get flinkConfig.jobServer + "/jobs/" + jobid + "/plan"
- .success (data) ->
- currentPlan = data
-
- deferreds.plan.resolve(data)
-
- deferreds.plan.promise
-
@getNode = (nodeid) ->
seekNode = (nodeid, data) ->
for node in data
@@ -136,7 +124,7 @@ angular.module('flinkApp')
deferred = $q.defer()
- $q.all([deferreds.job.promise]).then (data) =>
+ deferreds.job.promise.then (data) =>
foundNode = seekNode(nodeid, currentJob.plan.nodes)
foundNode.vertex = @seekVertex(nodeid)
@@ -154,13 +142,15 @@ angular.module('flinkApp')
@getVertex = (vertexid) ->
deferred = $q.defer()
- $q.all([deferreds.job.promise]).then (data) =>
+ deferreds.job.promise.then (data) =>
vertex = @seekVertex(vertexid)
$http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasktimes"
- .success (data) ->
+ .success (data) =>
# TODO: change to subtasktimes
vertex.subtasks = data.subtasks
+ @setEndTimes(vertex.subtasks)
+ console.log vertex.subtasks
deferred.resolve(vertex)
@@ -169,7 +159,7 @@ angular.module('flinkApp')
@getSubtasks = (vertexid) ->
deferred = $q.defer()
- $q.all([deferreds.job.promise]).then (data) =>
+ deferreds.job.promise.then (data) =>
vertex = @seekVertex(vertexid)
$http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid
@@ -183,7 +173,7 @@ angular.module('flinkApp')
@getAccumulators = (vertexid) ->
deferred = $q.defer()
- $q.all([deferreds.job.promise]).then (data) =>
+ deferreds.job.promise.then (data) =>
vertex = @seekVertex(vertexid)
$http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/accumulators"
@@ -198,7 +188,7 @@ angular.module('flinkApp')
@loadExceptions = ->
deferred = $q.defer()
- $q.all([deferreds.job.promise]).then (data) =>
+ deferreds.job.promise.then (data) =>
$http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/exceptions"
.success (exceptions) ->
http://git-wip-us.apache.org/repos/asf/flink/blob/616b7d56/flink-runtime-web/web-dashboard/app/styles/animate.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/animate.styl b/flink-runtime-web/web-dashboard/app/styles/animate.styl
index 14fd938..14171a2 100644
--- a/flink-runtime-web/web-dashboard/app/styles/animate.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/animate.styl
@@ -1,3 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
// Animate.css - http://daneden.me/animate
// Licensed under the MIT license - http://opensource.org/licenses/MIT
[05/51] [abbrv] flink git commit: [FLINK-2547] [web dashboard] Add
handlers for cluster status and web dashboard configuration
Posted by se...@apache.org.
[FLINK-2547] [web dashboard] Add handlers for cluster status and web dashboard configuration
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/01b0fc8f
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/01b0fc8f
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/01b0fc8f
Branch: refs/heads/master
Commit: 01b0fc8fc44d2454ef351928e73f5d49f9e41247
Parents: 562573d
Author: Stephan Ewen <se...@apache.org>
Authored: Wed Aug 19 16:32:30 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:49 2015 +0200
----------------------------------------------------------------------
.../webmonitor/ExecutionGraphHolder.java | 61 ++++------
.../flink/runtime/webmonitor/JsonFactory.java | 97 ++++++---------
.../runtime/webmonitor/WebMonitorConfig.java | 79 ++++++++++++
.../runtime/webmonitor/WebRuntimeMonitor.java | 122 ++++++++-----------
.../handlers/RequestConfigHandler.java | 10 +-
.../handlers/RequestJobIdsHandler.java | 37 +++---
.../handlers/RequestOverviewHandler.java | 43 +++----
.../legacy/JobManagerInfoHandler.java | 50 +++-----
.../RequestStatusWithJobIDsOverview.java | 68 -----------
.../webmonitor/StatusWithJobIDsOverview.java | 82 -------------
.../flink/runtime/jobmanager/JobManager.scala | 21 +---
11 files changed, 250 insertions(+), 420 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java
index 4efb7ad..74278a1 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/ExecutionGraphHolder.java
@@ -23,8 +23,6 @@ import org.apache.flink.runtime.executiongraph.ExecutionGraph;
import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.messages.JobManagerMessages;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.FiniteDuration;
@@ -32,7 +30,7 @@ import scala.concurrent.duration.FiniteDuration;
import java.util.WeakHashMap;
/**
- * Gateway to obtaining an {@link ExecutionGraph} from a source, like JobManager or Archive.
+ * Gateway to obtaining an {@link ExecutionGraph} from a source, like JobManager or Archiver.
* <p>
* The holder will cache the ExecutionGraph behind a weak reference, which will be cleared
* at some point once no one else is pointing to the ExecutionGraph.
@@ -41,62 +39,49 @@ import java.util.WeakHashMap;
*/
public class ExecutionGraphHolder {
- private static final Logger LOG = LoggerFactory.getLogger(ExecutionGraphHolder.class);
+ private final ActorGateway source;
- /** Retrieves the current leading JobManager and its corresponding archive */
- private final JobManagerArchiveRetriever retriever;
-
private final FiniteDuration timeout;
-
+
private final WeakHashMap<JobID, ExecutionGraph> cache = new WeakHashMap<JobID, ExecutionGraph>();
- public ExecutionGraphHolder(JobManagerArchiveRetriever retriever) {
- this(retriever, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
+
+ public ExecutionGraphHolder(ActorGateway source) {
+ this(source, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
}
- public ExecutionGraphHolder(JobManagerArchiveRetriever retriever, FiniteDuration timeout) {
- if (retriever == null || timeout == null) {
+ public ExecutionGraphHolder(ActorGateway source, FiniteDuration timeout) {
+ if (source == null || timeout == null) {
throw new NullPointerException();
}
- this.retriever = retriever;
+ this.source = source;
this.timeout = timeout;
}
- /**
- * Retrieves the execution graph with {@link JobID} jid or null if it cannot be found.
- *
- * @param jid jobID of the execution graph to be retrieved
- * @return the retrieved execution graph or null if it is not retrievable
- */
+
public ExecutionGraph getExecutionGraph(JobID jid) {
ExecutionGraph cached = cache.get(jid);
if (cached != null) {
return cached;
}
-
- try {
- ActorGateway jobManager = retriever.getJobManagerGateway();
- if (jobManager != null) {
-
- Future<Object> future = jobManager.ask(new JobManagerMessages.RequestJob(jid), timeout);
- Object result = Await.result(future, timeout);
- if (result instanceof JobManagerMessages.JobNotFound) {
- return null;
- } else if (result instanceof JobManagerMessages.JobFound) {
- ExecutionGraph eg = ((JobManagerMessages.JobFound) result).executionGraph();
- cache.put(jid, eg);
- return eg;
- } else {
- throw new RuntimeException("Unknown response from JobManager / Archive: " + result);
- }
- } else {
- LOG.warn("No connection to the leading JobManager.");
+ try {
+ Future<Object> future = source.ask(new JobManagerMessages.RequestJob(jid), timeout);
+ Object result = Await.result(future, timeout);
+ if (result instanceof JobManagerMessages.JobNotFound) {
return null;
}
+ else if (result instanceof JobManagerMessages.JobFound) {
+ ExecutionGraph eg = ((JobManagerMessages.JobFound) result).executionGraph();
+ cache.put(jid, eg);
+ return eg;
+ }
+ else {
+ throw new RuntimeException("Unknown response from JobManager / Archive: " + result);
+ }
}
catch (Exception e) {
throw new RuntimeException("Error requesting execution graph", e);
}
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
index 67c0b79..66449e0 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
@@ -18,95 +18,70 @@
package org.apache.flink.runtime.webmonitor;
+import com.fasterxml.jackson.core.JsonGenerator;
import org.apache.flink.api.common.JobID;
import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
-import org.apache.flink.runtime.messages.webmonitor.StatusWithJobIDsOverview;
+
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
-
/**
* This class implements the utility methods that convert the responses into JSON strings.
*/
public class JsonFactory {
- public static String generateConfigJSON(long refreshInterval) {
+ private static final com.fasterxml.jackson.core.JsonFactory jacksonFactory =
+ new com.fasterxml.jackson.core.JsonFactory();
+
+ public static String generateConfigJSON(long refreshInterval, long timeZoneOffset, String timeZoneName) {
try {
- JSONObject response = new JSONObject();
- response.put("refresh-interval", refreshInterval);
- return response.toString(2);
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+ gen.writeNumberField("refresh-interval", refreshInterval);
+ gen.writeNumberField("timezone-offset", timeZoneOffset);
+ gen.writeStringField("timezone-name", timeZoneName);
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
}
- catch (JSONException e) {
+ catch (Exception e) {
// this should not happen
- throw new RuntimeException(e);
+ throw new RuntimeException(e.getMessage(), e);
}
}
public static String generateOverviewJSON(StatusOverview overview) {
try {
- JSONObject response = new JSONObject();
- response.put("taskmanagers", overview.getNumTaskManagersConnected());
- response.put("slots-total", overview.getNumSlotsTotal());
- response.put("slots-available", overview.getNumSlotsAvailable());
- response.put("jobs-running", overview.getNumJobsRunningOrPending());
- response.put("jobs-finished", overview.getNumJobsFinished());
- response.put("jobs-cancelled", overview.getNumJobsCancelled());
- response.put("jobs-failed", overview.getNumJobsFailed());
- return response.toString(2);
- }
- catch (JSONException e) {
- // this should not happen
- throw new RuntimeException(e);
- }
- }
-
- public static String generateOverviewWithJobIDsJSON(StatusWithJobIDsOverview overview) {
- try {
- List<JobID> runningIDs = overview.getJobsRunningOrPending();
- List<String> runningStrings = new ArrayList<String>(runningIDs.size());
- for (JobID jid : runningIDs) {
- runningStrings.add(jid.toString());
- }
-
- List<JobID> finishedIDs = overview.getJobsFinished();
- List<String> finishedStrings = new ArrayList<String>(finishedIDs.size());
- for (JobID jid : finishedIDs) {
- finishedStrings.add(jid.toString());
- }
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
- List<JobID> canceledIDs = overview.getJobsCancelled();
- List<String> canceledStrings = new ArrayList<String>(canceledIDs.size());
- for (JobID jid : canceledIDs) {
- canceledStrings.add(jid.toString());
- }
+ gen.writeStartObject();
+ gen.writeNumberField("taskmanagers", overview.getNumTaskManagersConnected());
+ gen.writeNumberField("slots-total", overview.getNumSlotsTotal());
+ gen.writeNumberField("slots-available", overview.getNumSlotsAvailable());
+ gen.writeNumberField("jobs-running", overview.getNumJobsRunningOrPending());
+ gen.writeNumberField("jobs-finished", overview.getNumJobsFinished());
+ gen.writeNumberField("jobs-cancelled", overview.getNumJobsCancelled());
+ gen.writeNumberField("jobs-failed", overview.getNumJobsFailed());
+ gen.writeEndObject();
- List<JobID> failedIDs = overview.getJobsFailed();
- List<String> failedStrings = new ArrayList<String>(failedIDs.size());
- for (JobID jid : failedIDs) {
- failedStrings.add(jid.toString());
- }
-
- JSONObject response = new JSONObject();
- response.put("taskmanagers", overview.getNumTaskManagersConnected());
- response.put("slots-total", overview.getNumSlotsTotal());
- response.put("slots-available", overview.getNumSlotsAvailable());
-
-
- response.put("jobs-running", runningStrings);
- response.put("jobs-finished", finishedStrings);
- response.put("jobs-cancelled", canceledStrings);
- response.put("jobs-failed", failedStrings);
- return response.toString(2);
+ gen.close();
+ return writer.toString();
}
- catch (JSONException e) {
+ catch (Exception e) {
// this should not happen
- throw new RuntimeException(e);
+ throw new RuntimeException(e.getMessage(), e);
}
}
+
public static String generateJobsOverviewJSON(JobsWithIDsOverview overview) {
try {
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorConfig.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorConfig.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorConfig.java
new file mode 100644
index 0000000..c8e64c9
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorConfig.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor;
+
+
+import org.apache.flink.configuration.ConfigConstants;
+import org.apache.flink.configuration.Configuration;
+
+public class WebMonitorConfig {
+
+ // ------------------------------------------------------------------------
+ // Config Keys
+ // ------------------------------------------------------------------------
+
+ /** The port for the runtime monitor web-frontend server. */
+ public static final String JOB_MANAGER_WEB_PORT_KEY = ConfigConstants.JOB_MANAGER_WEB_PORT_KEY;
+
+ /** The directory where the web server's static contents is stored */
+ public static final String JOB_MANAGER_WEB_DOC_ROOT_KEY = ConfigConstants.JOB_MANAGER_WEB_DOC_ROOT_KEY;
+
+ /** The initial refresh interval for the web dashboard */
+ public static final String JOB_MANAGER_WEB_REFRESH_INTERVAL_KEY = "jobmanager.web.refresh-interval";
+
+
+ // ------------------------------------------------------------------------
+ // Default values
+ // ------------------------------------------------------------------------
+
+ /** Default port for the web dashboard (= 8081) */
+ public static final int DEFAULT_JOB_MANAGER_WEB_FRONTEND_PORT = ConfigConstants.DEFAULT_JOB_MANAGER_WEB_FRONTEND_PORT;
+
+ /** Default refresh interval for the web dashboard (= 3000 msecs) */
+ public static final long DEFAULT_JOB_MANAGER_WEB_REFRESH_INTERVAL = 3000;
+
+
+ // ------------------------------------------------------------------------
+ // Config
+ // ------------------------------------------------------------------------
+
+ /** The configuration queried by this config object */
+ private final Configuration config;
+
+
+ public WebMonitorConfig(Configuration config) {
+ if (config == null) {
+ throw new NullPointerException();
+ }
+ this.config = config;
+ }
+
+
+ public int getWebFrontendPort() {
+ return config.getInteger(JOB_MANAGER_WEB_PORT_KEY, DEFAULT_JOB_MANAGER_WEB_FRONTEND_PORT);
+ }
+
+ public String getWebRoot() {
+ return config.getString(JOB_MANAGER_WEB_DOC_ROOT_KEY, null);
+ }
+
+ public long getRefreshInterval() {
+ return config.getLong(JOB_MANAGER_WEB_REFRESH_INTERVAL_KEY, DEFAULT_JOB_MANAGER_WEB_REFRESH_INTERVAL);
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
index 4c38cae..5c7dc6b 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
@@ -18,8 +18,6 @@
package org.apache.flink.runtime.webmonitor;
-import akka.actor.ActorSystem;
-import com.google.common.base.Preconditions;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
@@ -35,8 +33,7 @@ import io.netty.handler.stream.ChunkedWriteHandler;
import org.apache.flink.configuration.ConfigConstants;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.IllegalConfigurationException;
-import org.apache.flink.runtime.akka.AkkaUtils;
-import org.apache.flink.runtime.leaderretrieval.LeaderRetrievalService;
+import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.webmonitor.files.StaticFileServerHandler;
import org.apache.flink.runtime.webmonitor.handlers.ExecutionPlanHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobConfigHandler;
@@ -60,7 +57,7 @@ import java.util.concurrent.TimeUnit;
/**
* The root component of the web runtime monitor.
- *
+ *
* <p>The web runtime monitor is based in Netty HTTP. It uses the Netty-Router library to route
* HTTP requests of different paths to different response handlers. In addition, it serves the static
* files of the web frontend, such as HTML, CSS, or JS files.</p>
@@ -68,48 +65,37 @@ import java.util.concurrent.TimeUnit;
public class WebRuntimeMonitor implements WebMonitor {
public static final FiniteDuration DEFAULT_REQUEST_TIMEOUT = new FiniteDuration(10, TimeUnit.SECONDS);
-
+
public static final long DEFAULT_REFRESH_INTERVAL = 5000;
/** Logger for web frontend startup / shutdown messages */
private static final Logger LOG = LoggerFactory.getLogger(WebRuntimeMonitor.class);
-
+
/** Teh default path under which the static contents is stored */
private static final String STATIC_CONTENTS_PATH = "resources/web-runtime-monitor";
-
+
// ------------------------------------------------------------------------
-
- private final Object startupShutdownLock = new Object();
- private final LeaderRetrievalService leaderRetrievalService;
+ private final Object startupShutdownLock = new Object();
- /** LeaderRetrievalListener which stores the currently leading JobManager and its archive */
- private final JobManagerArchiveRetriever retriever;
+ private final Router router;
private final int configuredPort;
- private final Router router;
-
private ServerBootstrap bootstrap;
-
+
private Channel serverChannel;
- // ------------------------------------------------------------------------
- public WebRuntimeMonitor(
- Configuration config,
- LeaderRetrievalService leaderRetrievalService,
- ActorSystem actorSystem)
- throws IOException {
- Preconditions.checkNotNull(config);
- this.leaderRetrievalService = Preconditions.checkNotNull(leaderRetrievalService);
+ public WebRuntimeMonitor(Configuration config, ActorGateway jobManager, ActorGateway archive) throws IOException {
+
+ final WebMonitorConfig cfg = new WebMonitorConfig(config);
// figure out where our static contents is
- final String configuredWebRoot = config.getString(ConfigConstants.JOB_MANAGER_WEB_DOC_ROOT_KEY, null);
final String flinkRoot = config.getString(ConfigConstants.FLINK_BASE_DIR_PATH_KEY, null);
+ final String configuredWebRoot = cfg.getWebRoot();
final File webRootDir;
-
if (configuredWebRoot != null) {
webRootDir = new File(configuredWebRoot);
}
@@ -117,52 +103,48 @@ public class WebRuntimeMonitor implements WebMonitor {
webRootDir = new File(flinkRoot, STATIC_CONTENTS_PATH);
}
else {
- throw new IllegalConfigurationException("The given configuration provides neither the web-document root ("
- + ConfigConstants.JOB_MANAGER_WEB_DOC_ROOT_KEY + "), not the Flink installation root ("
+ throw new IllegalConfigurationException("The given configuration provides neither the web-document root ("
+ + WebMonitorConfig.JOB_MANAGER_WEB_DOC_ROOT_KEY + "), not the Flink installation root ("
+ ConfigConstants.FLINK_BASE_DIR_PATH_KEY + ").");
}
-
+
// validate that the doc root is a valid directory
if (!(webRootDir.exists() && webRootDir.isDirectory() && webRootDir.canRead())) {
- throw new IllegalConfigurationException("The path to the static contents (" +
+ throw new IllegalConfigurationException("The path to the static contents (" +
webRootDir.getAbsolutePath() + ") is not a readable directory.");
}
-
+
// port configuration
- this.configuredPort = config.getInteger(ConfigConstants.JOB_MANAGER_WEB_PORT_KEY,
- ConfigConstants.DEFAULT_JOB_MANAGER_WEB_FRONTEND_PORT);
+ this.configuredPort = cfg.getWebFrontendPort();
if (this.configuredPort < 0) {
throw new IllegalArgumentException("Web frontend port is invalid: " + this.configuredPort);
}
- FiniteDuration timeout = AkkaUtils.getTimeout(config);
- FiniteDuration lookupTimeout = AkkaUtils.getTimeout(config);
-
- retriever = new JobManagerArchiveRetriever(this, actorSystem, lookupTimeout, timeout);
-
- ExecutionGraphHolder currentGraphs = new ExecutionGraphHolder(retriever);
+ ExecutionGraphHolder currentGraphs = new ExecutionGraphHolder(jobManager);
router = new Router()
- // config how to interact with this web server
- .GET("/config", handler(new RequestConfigHandler(DEFAULT_REFRESH_INTERVAL)))
+ // config how to interact with this web server
+ .GET("/config", handler(new RequestConfigHandler(cfg.getRefreshInterval())))
- // the overview - how many task managers, slots, free slots, ...
- .GET("/overview", handler(new RequestOverviewHandler(retriever)))
+ // the overview - how many task managers, slots, free slots, ...
+ .GET("/overview", handler(new RequestOverviewHandler(jobManager)))
- // currently running jobs
- .GET("/jobs", handler(new RequestJobIdsHandler(retriever)))
- .GET("/jobs/:jobid", handler(new JobSummaryHandler(currentGraphs)))
- .GET("/jobs/:jobid/vertices", handler(new JobVerticesOverviewHandler(currentGraphs)))
- .GET("/jobs/:jobid/plan", handler(new ExecutionPlanHandler(currentGraphs)))
- .GET("/jobs/:jobid/config", handler(new JobConfigHandler(currentGraphs)))
+ // currently running jobs
+ .GET("/jobs", handler(new RequestJobIdsHandler(jobManager)))
+ .GET("/jobs/:jobid", handler(new JobSummaryHandler(currentGraphs)))
+ .GET("/jobs/:jobid/vertices", handler(new JobVerticesOverviewHandler(currentGraphs)))
+ .GET("/jobs/:jobid/plan", handler(new ExecutionPlanHandler(currentGraphs)))
+ .GET("/jobs/:jobid/config", handler(new JobConfigHandler(currentGraphs)))
// .GET("/running/:jobid/:jobvertex", handler(new ExecutionPlanHandler(currentGraphs)))
- // the handler for the legacy requests
- .GET("/jobsInfo", new JobManagerInfoHandler(retriever, DEFAULT_REQUEST_TIMEOUT))
+ // the handler for the legacy requests
+ .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
+
+ // this handler serves all the static contents
+ .GET("/:*", new StaticFileServerHandler(webRootDir));
+
- // this handler serves all the static contents
- .GET("/:*", new StaticFileServerHandler(webRootDir));
}
@Override
@@ -172,20 +154,20 @@ public class WebRuntimeMonitor implements WebMonitor {
throw new IllegalStateException("The server has already been started");
}
- final Handler handler = new Handler(router);
-
ChannelInitializer<SocketChannel> initializer = new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
+ Handler handler = new Handler(router);
+
ch.pipeline()
- .addLast(new HttpServerCodec())
- .addLast(new HttpObjectAggregator(65536))
- .addLast(new ChunkedWriteHandler())
- .addLast(handler.name(), handler);
+ .addLast(new HttpServerCodec())
+ .addLast(new HttpObjectAggregator(65536))
+ .addLast(new ChunkedWriteHandler())
+ .addLast(handler.name(), handler);
}
};
-
+
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
@@ -194,25 +176,21 @@ public class WebRuntimeMonitor implements WebMonitor {
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(initializer);
-
+
Channel ch = this.bootstrap.bind(configuredPort).sync().channel();
this.serverChannel = ch;
InetSocketAddress bindAddress = (InetSocketAddress) ch.localAddress();
String address = bindAddress.getAddress().getHostAddress();
int port = bindAddress.getPort();
-
- LOG.info("Web frontend listening at " + address + ':' + port);
- leaderRetrievalService.start(retriever);
+ LOG.info("Web frontend listening at " + address + ':' + port);
}
}
-
+
@Override
public void stop() throws Exception {
synchronized (startupShutdownLock) {
- leaderRetrievalService.stop();
-
if (this.serverChannel != null) {
this.serverChannel.close().awaitUninterruptibly();
this.serverChannel = null;
@@ -225,7 +203,7 @@ public class WebRuntimeMonitor implements WebMonitor {
}
}
}
-
+
@Override
public int getServerPort() {
Channel server = this.serverChannel;
@@ -237,15 +215,15 @@ public class WebRuntimeMonitor implements WebMonitor {
LOG.error("Cannot access local server port", e);
}
}
-
+
return -1;
}
-
+
// ------------------------------------------------------------------------
// Utilities
// ------------------------------------------------------------------------
-
+
private static RuntimeMonitorHandler handler(RequestHandler handler) {
return new RuntimeMonitorHandler(handler);
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestConfigHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestConfigHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestConfigHandler.java
index edf70c1..af9280e 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestConfigHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestConfigHandler.java
@@ -21,17 +21,23 @@ package org.apache.flink.runtime.webmonitor.handlers;
import org.apache.flink.runtime.webmonitor.JsonFactory;
import java.util.Map;
+import java.util.TimeZone;
/**
* Responder that returns the parameters that define how the asynchronous requests
- * against this web server should behave. It defines for example the refresh interval.
+ * against this web server should behave. It defines for example the refresh interval,
+ * and time zone of the server timestamps.
*/
public class RequestConfigHandler implements RequestHandler, RequestHandler.JsonResponse {
private final String configString;
public RequestConfigHandler(long refreshInterval) {
- this.configString = JsonFactory.generateConfigJSON(refreshInterval);
+ TimeZone timeZome = TimeZone.getDefault();
+ String timeZoneName = timeZome.getDisplayName();
+ long timeZoneOffset= timeZome.getRawOffset();
+
+ this.configString = JsonFactory.generateConfigJSON(refreshInterval, timeZoneOffset, timeZoneName);
}
@Override
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestJobIdsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestJobIdsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestJobIdsHandler.java
index 8a177f4..c3efce0 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestJobIdsHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestJobIdsHandler.java
@@ -21,7 +21,6 @@ package org.apache.flink.runtime.webmonitor.handlers;
import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
import org.apache.flink.runtime.messages.webmonitor.RequestJobsWithIDsOverview;
-import org.apache.flink.runtime.webmonitor.JobManagerArchiveRetriever;
import org.apache.flink.runtime.webmonitor.JsonFactory;
import org.apache.flink.runtime.webmonitor.WebRuntimeMonitor;
@@ -37,39 +36,33 @@ import java.util.Map;
* given the JobManager or Archive Actor Reference.
*/
public class RequestJobIdsHandler implements RequestHandler, RequestHandler.JsonResponse {
-
- private final JobManagerArchiveRetriever retriever;
-
+
+ private final ActorGateway target;
+
private final FiniteDuration timeout;
-
- public RequestJobIdsHandler(JobManagerArchiveRetriever retriever) {
- this(retriever, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
+
+ public RequestJobIdsHandler(ActorGateway target) {
+ this(target, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
}
-
- public RequestJobIdsHandler(JobManagerArchiveRetriever retriever, FiniteDuration timeout) {
- if (retriever == null || timeout == null) {
+
+ public RequestJobIdsHandler(ActorGateway target, FiniteDuration timeout) {
+ if (target == null || timeout == null) {
throw new NullPointerException();
}
- this.retriever = retriever;
+ this.target = target;
this.timeout = timeout;
}
-
+
@Override
public String handleRequest(Map<String, String> params) throws Exception {
// we need no parameters, get all requests
try {
- ActorGateway jobManager = retriever.getJobManagerGateway();
-
- if (jobManager != null) {
- Future<Object> future = jobManager.ask(RequestJobsWithIDsOverview.getInstance(), timeout);
- JobsWithIDsOverview result = (JobsWithIDsOverview) Await.result(future, timeout);
- return JsonFactory.generateJobsOverviewJSON(result);
- } else {
- throw new Exception("No connection to the leading JobManager.");
- }
+ Future<Object> future = target.ask(RequestJobsWithIDsOverview.getInstance(), timeout);
+ JobsWithIDsOverview result = (JobsWithIDsOverview) Await.result(future, timeout);
+ return JsonFactory.generateJobsOverviewJSON(result);
}
catch (Exception e) {
throw new RuntimeException("Failed to fetch list of all running jobs: " + e.getMessage(), e);
}
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
index ce30122..7268945 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
@@ -19,9 +19,8 @@
package org.apache.flink.runtime.webmonitor.handlers;
import org.apache.flink.runtime.instance.ActorGateway;
-import org.apache.flink.runtime.messages.webmonitor.RequestStatusWithJobIDsOverview;
-import org.apache.flink.runtime.messages.webmonitor.StatusWithJobIDsOverview;
-import org.apache.flink.runtime.webmonitor.JobManagerArchiveRetriever;
+import org.apache.flink.runtime.messages.webmonitor.RequestStatusOverview;
+import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
import org.apache.flink.runtime.webmonitor.JsonFactory;
import org.apache.flink.runtime.webmonitor.WebRuntimeMonitor;
@@ -36,39 +35,33 @@ import java.util.Map;
* TaskManagers are currently connected, and how many jobs are running.
*/
public class RequestOverviewHandler implements RequestHandler, RequestHandler.JsonResponse {
-
- private final JobManagerArchiveRetriever retriever;
-
+
+ private final ActorGateway jobManager;
+
private final FiniteDuration timeout;
-
-
- public RequestOverviewHandler(JobManagerArchiveRetriever retriever) {
- this(retriever, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
+
+
+ public RequestOverviewHandler(ActorGateway jobManager) {
+ this(jobManager, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
}
-
- public RequestOverviewHandler(JobManagerArchiveRetriever retriever, FiniteDuration timeout) {
- if (retriever == null || timeout == null) {
+
+ public RequestOverviewHandler(ActorGateway jobManager, FiniteDuration timeout) {
+ if (jobManager == null || timeout == null) {
throw new NullPointerException();
}
- this.retriever = retriever;
+ this.jobManager = jobManager;
this.timeout = timeout;
}
-
+
@Override
public String handleRequest(Map<String, String> params) throws Exception {
try {
- ActorGateway jobManager = retriever.getJobManagerGateway();
-
- if (jobManager != null) {
- Future<Object> future = jobManager.ask(RequestStatusWithJobIDsOverview.getInstance(), timeout);
- StatusWithJobIDsOverview result = (StatusWithJobIDsOverview) Await.result(future, timeout);
- return JsonFactory.generateOverviewWithJobIDsJSON(result);
- } else {
- throw new Exception("No connection to the leading job manager.");
- }
+ Future<Object> future = jobManager.ask(RequestStatusOverview.getInstance(), timeout);
+ StatusOverview result = (StatusOverview) Await.result(future, timeout);
+ return JsonFactory.generateOverviewJSON(result);
}
catch (Exception e) {
throw new Exception("Failed to fetch the status overview: " + e.getMessage(), e);
}
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JobManagerInfoHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JobManagerInfoHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JobManagerInfoHandler.java
index 3f1842b..c65bc0f 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JobManagerInfoHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JobManagerInfoHandler.java
@@ -48,7 +48,6 @@ import org.apache.flink.runtime.messages.accumulators.AccumulatorResultsErroneou
import org.apache.flink.runtime.messages.accumulators.AccumulatorResultsNotFound;
import org.apache.flink.runtime.messages.accumulators.RequestAccumulatorResultsStringified;
import org.apache.flink.runtime.util.EnvironmentInformation;
-import org.apache.flink.runtime.webmonitor.JobManagerArchiveRetriever;
import org.apache.flink.util.ExceptionUtils;
import org.apache.flink.util.StringUtils;
@@ -76,15 +75,14 @@ public class JobManagerInfoHandler extends SimpleChannelInboundHandler<Routed> {
private static final Charset ENCODING = Charset.forName("UTF-8");
/** Underlying JobManager */
- private final JobManagerArchiveRetriever retriever;
+ private final ActorGateway jobmanager;
+ private final ActorGateway archive;
private final FiniteDuration timeout;
- private ActorGateway jobmanager;
- private ActorGateway archive;
-
- public JobManagerInfoHandler(JobManagerArchiveRetriever retriever, FiniteDuration timeout) {
- this.retriever = retriever;
+ public JobManagerInfoHandler(ActorGateway jobmanager, ActorGateway archive, FiniteDuration timeout) {
+ this.jobmanager = jobmanager;
+ this.archive = archive;
this.timeout = timeout;
}
@@ -92,18 +90,6 @@ public class JobManagerInfoHandler extends SimpleChannelInboundHandler<Routed> {
protected void channelRead0(ChannelHandlerContext ctx, Routed routed) throws Exception {
DefaultFullHttpResponse response;
try {
- jobmanager = retriever.getJobManagerGateway();
-
- if (jobmanager == null) {
- throw new Exception("No connection to leading JobManager.");
- }
-
- archive = retriever.getArchiveGateway();
-
- if (archive == null) {
- throw new Exception("No connection to leading JobManager.");
- }
-
String result = handleRequest(routed);
byte[] bytes = result.getBytes(ENCODING);
@@ -121,15 +107,13 @@ public class JobManagerInfoHandler extends SimpleChannelInboundHandler<Routed> {
response.headers().set(HttpHeaders.Names.CONTENT_ENCODING, "utf-8");
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
-
+
KeepAliveWrite.flush(ctx, routed.request(), response);
}
-
-
- @SuppressWarnings("unchecked")
- private String handleRequest(Routed routed) throws Exception {
+ @SuppressWarnings("unchecked")
+ private String handleRequest(Routed routed) throws Exception {
if ("archive".equals(routed.queryParam("get"))) {
Future<Object> response = archive.ask(ArchiveMessages.getRequestArchivedJobs(), timeout);
@@ -302,7 +286,7 @@ public class JobManagerInfoHandler extends SimpleChannelInboundHandler<Routed> {
}
}
bld.append("]");
-
+
return bld.toString();
}
@@ -555,7 +539,7 @@ public class JobManagerInfoHandler extends SimpleChannelInboundHandler<Routed> {
bld.append("}");
bld.append("}");
bld.append("]");
-
+
return bld.toString();
}
@@ -584,7 +568,7 @@ public class JobManagerInfoHandler extends SimpleChannelInboundHandler<Routed> {
//Serialize job to json
final StringBuilder bld = new StringBuilder();
-
+
bld.append("{");
bld.append("\"jobid\": \"").append(jobId).append("\",");
bld.append("\"timestamp\": \"").append(System.currentTimeMillis()).append("\",");
@@ -667,15 +651,15 @@ public class JobManagerInfoHandler extends SimpleChannelInboundHandler<Routed> {
bld.append("}");
}
}
-
+
return bld.toString();
}
}
-
+
private String writeJsonForArchivedJobGroupvertex(ExecutionGraph graph, JobVertexID vertexId) {
ExecutionJobVertex jobVertex = graph.getJobVertex(vertexId);
StringBuilder bld = new StringBuilder();
-
+
bld.append("{\"groupvertex\": ").append(JsonFactory.toJson(jobVertex)).append(",");
bld.append("\"verticetimes\": {");
@@ -710,9 +694,9 @@ public class JobManagerInfoHandler extends SimpleChannelInboundHandler<Routed> {
return bld.toString();
}
-
+
private String writeJsonForVersion() {
- return "{\"version\": \"" + EnvironmentInformation.getVersion() + "\",\"revision\": \"" +
+ return "{\"version\": \"" + EnvironmentInformation.getVersion() + "\",\"revision\": \"" +
EnvironmentInformation.getRevisionInformation().commitId + "\"}";
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/RequestStatusWithJobIDsOverview.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/RequestStatusWithJobIDsOverview.java b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/RequestStatusWithJobIDsOverview.java
deleted file mode 100644
index d26fe3d..0000000
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/RequestStatusWithJobIDsOverview.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.messages.webmonitor;
-
-/**
- * This message requests an overview of the status, such as how many TaskManagers
- * are currently connected, how many slots are available, how many are free, ...
- * The response to this message is a {@link org.apache.flink.runtime.messages.webmonitor.StatusOverview} message.
- */
-public class RequestStatusWithJobIDsOverview implements InfoMessage {
-
- private static final long serialVersionUID = 3052933564788843275L;
-
- // ------------------------------------------------------------------------
-
- private static final RequestStatusWithJobIDsOverview INSTANCE = new RequestStatusWithJobIDsOverview();
-
- public static RequestStatusWithJobIDsOverview getInstance() {
- return INSTANCE;
- }
-
- // ------------------------------------------------------------------------
-
- @Override
- public int hashCode() {
- return RequestStatusWithJobIDsOverview.class.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj != null && obj.getClass() == RequestStatusWithJobIDsOverview.class;
- }
-
- @Override
- public String toString() {
- return RequestStatusWithJobIDsOverview.class.getSimpleName();
- }
-
- // ------------------------------------------------------------------------
-
- /**
- * No external instantiation
- */
- private RequestStatusWithJobIDsOverview() {}
-
- /**
- * Preserve the singleton property by returning the singleton instance
- */
- private Object readResolve() {
- return INSTANCE;
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/StatusWithJobIDsOverview.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/StatusWithJobIDsOverview.java b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/StatusWithJobIDsOverview.java
deleted file mode 100644
index 72fb01b..0000000
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/StatusWithJobIDsOverview.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.messages.webmonitor;
-
-import org.apache.flink.api.common.JobID;
-
-import java.util.List;
-
-/**
- * Response to the {@link org.apache.flink.runtime.messages.webmonitor.RequestStatusWithJobIDsOverview}
- * message, carrying a description of the Flink cluster status.
- */
-public class StatusWithJobIDsOverview extends JobsWithIDsOverview {
-
- private static final long serialVersionUID = -729861859715105265L;
-
- private final int numTaskManagersConnected;
- private final int numSlotsTotal;
- private final int numSlotsAvailable;
-
- public StatusWithJobIDsOverview(int numTaskManagersConnected, int numSlotsTotal, int numSlotsAvailable,
- List<JobID> jobsRunningOrPending, List<JobID> jobsFinished,
- List<JobID> jobsCancelled, List<JobID> jobsFailed) {
-
- super(jobsRunningOrPending, jobsFinished, jobsCancelled, jobsFailed);
-
- this.numTaskManagersConnected = numTaskManagersConnected;
- this.numSlotsTotal = numSlotsTotal;
- this.numSlotsAvailable = numSlotsAvailable;
- }
-
- public StatusWithJobIDsOverview(int numTaskManagersConnected, int numSlotsTotal, int numSlotsAvailable,
- JobsWithIDsOverview jobs1, JobsWithIDsOverview jobs2) {
- super(jobs1, jobs2);
- this.numTaskManagersConnected = numTaskManagersConnected;
- this.numSlotsTotal = numSlotsTotal;
- this.numSlotsAvailable = numSlotsAvailable;
- }
-
- public int getNumTaskManagersConnected() {
- return numTaskManagersConnected;
- }
-
- public int getNumSlotsTotal() {
- return numSlotsTotal;
- }
-
- public int getNumSlotsAvailable() {
- return numSlotsAvailable;
- }
-
- // ------------------------------------------------------------------------
-
- @Override
- public String toString() {
- return "StatusOverview {" +
- "numTaskManagersConnected=" + numTaskManagersConnected +
- ", numSlotsTotal=" + numSlotsTotal +
- ", numSlotsAvailable=" + numSlotsAvailable +
- ", numJobsRunningOrPending=" + getJobsRunningOrPending() +
- ", numJobsFinished=" + getJobsFinished() +
- ", numJobsCancelled=" + getJobsCancelled() +
- ", numJobsFailed=" + getJobsFailed() +
- '}';
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/01b0fc8f/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
index 7046937..64ed129 100644
--- a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
+++ b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
@@ -871,6 +871,9 @@ class JobManager(
/**
* Dedicated handler for monitor info request messages.
+ *
+ * Note that this handler does not fail. Errors while responding to info messages are logged,
+ * but will not cause the actor to crash.
*
* @param actorMessage The info request message.
*/
@@ -918,23 +921,7 @@ class JobManager(
ourJobs, archiveOverview)
}(context.dispatcher)
- case _ : RequestStatusWithJobIDsOverview =>
-
- val ourJobs = createJobStatusWithIDsOverview()
-
- val numTMs = instanceManager.getNumberOfRegisteredTaskManagers()
- val numSlotsTotal = instanceManager.getTotalNumberOfSlots()
- val numSlotsAvailable = instanceManager.getNumberOfAvailableSlots()
-
- // add to that the jobs from the archive
- val future = (archive ? RequestJobsWithIDsOverview.getInstance())(timeout)
- future.onSuccess {
- case archiveOverview: JobsWithIDsOverview =>
- theSender ! new StatusWithJobIDsOverview(numTMs, numSlotsTotal, numSlotsAvailable,
- ourJobs, archiveOverview)
- }(context.dispatcher)
-
- case _ => throw new Exception("Unrecognized info message " + actorMessage)
+ case _ => log.error("Unrecognized info message " + actorMessage)
}
}
catch {
[24/51] [abbrv] flink git commit: [FLINK-2687] [monitoring API]
Extend vertex requests with subtask data and accumulators
Posted by se...@apache.org.
[FLINK-2687] [monitoring API] Extend vertex requests with subtask data and accumulators
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/99a351e4
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/99a351e4
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/99a351e4
Branch: refs/heads/master
Commit: 99a351e4ef3048d8beac9f287d67c6f3ce3680b0
Parents: ba31f83
Author: Stephan Ewen <se...@apache.org>
Authored: Fri Aug 21 18:19:24 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:51 2015 +0200
----------------------------------------------------------------------
.../common/accumulators/AccumulatorHelper.java | 11 +-
.../runtime/webmonitor/WebRuntimeMonitor.java | 15 ++-
.../handlers/CurrentJobsOverviewHandler.java | 11 +-
.../handlers/DashboardConfigHandler.java | 8 ++
.../handlers/JobAccumulatorsHandler.java | 65 +++++++++
.../webmonitor/handlers/JobDetailsHandler.java | 34 ++++-
.../handlers/JobVertexAccumulatorsHandler.java | 82 ++++++++++++
.../handlers/JobVertexDetailsHandler.java | 134 +++++++++++++++++++
.../handlers/SubtasksTimesHandler.java | 3 +-
.../StringifiedAccumulatorResult.java | 26 ++++
.../runtime/executiongraph/ExecutionGraph.java | 21 +--
.../executiongraph/ExecutionJobVertex.java | 67 ++++++----
.../jobgraph/jsonplan/JsonPlanGenerator.java | 1 -
.../runtime/jobmanager/MemoryArchivist.scala | 6 +-
.../jobgraph/jsonplan/JsonGeneratorTest.java | 2 -
.../messages/WebMonitorMessagesTest.java | 5 +-
.../jsonplan/JsonJobGraphGenerationTest.java | 28 ++--
17 files changed, 431 insertions(+), 88 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-core/src/main/java/org/apache/flink/api/common/accumulators/AccumulatorHelper.java
----------------------------------------------------------------------
diff --git a/flink-core/src/main/java/org/apache/flink/api/common/accumulators/AccumulatorHelper.java b/flink-core/src/main/java/org/apache/flink/api/common/accumulators/AccumulatorHelper.java
index bb48bdd..72670bf 100644
--- a/flink-core/src/main/java/org/apache/flink/api/common/accumulators/AccumulatorHelper.java
+++ b/flink-core/src/main/java/org/apache/flink/api/common/accumulators/AccumulatorHelper.java
@@ -58,7 +58,7 @@ public class AccumulatorHelper {
/**
* Workaround method for type safety
*/
- private static final <V, R extends Serializable> void mergeSingle(Accumulator<?, ?> target,
+ private static <V, R extends Serializable> void mergeSingle(Accumulator<?, ?> target,
Accumulator<?, ?> toMerge) {
@SuppressWarnings("unchecked")
Accumulator<V, R> typedTarget = (Accumulator<V, R>) target;
@@ -104,7 +104,7 @@ public class AccumulatorHelper {
public static Map<String, Object> toResultMap(Map<String, Accumulator<?, ?>> accumulators) {
Map<String, Object> resultMap = new HashMap<String, Object>();
for (Map.Entry<String, Accumulator<?, ?>> entry : accumulators.entrySet()) {
- resultMap.put(entry.getKey(), (Object) entry.getValue().getLocalValue());
+ resultMap.put(entry.getKey(), entry.getValue().getLocalValue());
}
return resultMap;
}
@@ -112,8 +112,8 @@ public class AccumulatorHelper {
public static String getResultsFormated(Map<String, Object> map) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, Object> entry : map.entrySet()) {
- builder.append("- " + entry.getKey() + " (" + entry.getValue().getClass().getName()
- + ")" + ": " + entry.getValue().toString() + "\n");
+ builder.append("- ").append(entry.getKey()).append(" (").append(entry.getValue().getClass().getName());
+ builder.append(")").append(": ").append(entry.getValue().toString()).append("\n");
}
return builder.toString();
}
@@ -127,8 +127,7 @@ public class AccumulatorHelper {
}
}
- public static Map<String, Accumulator<?, ?>> copy(final Map<String, Accumulator<?,
- ?>> accumulators) {
+ public static Map<String, Accumulator<?, ?>> copy(Map<String, Accumulator<?, ?>> accumulators) {
Map<String, Accumulator<?, ?>> result = new HashMap<String, Accumulator<?, ?>>();
for(Map.Entry<String, Accumulator<?, ?>> entry: accumulators.entrySet()){
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
index 1c1b723..8aa25dd 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
@@ -35,12 +35,15 @@ import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.IllegalConfigurationException;
import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.webmonitor.files.StaticFileServerHandler;
+import org.apache.flink.runtime.webmonitor.handlers.JobAccumulatorsHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobPlanHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobConfigHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobExceptionsHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobDetailsHandler;
import org.apache.flink.runtime.webmonitor.handlers.CurrentJobsOverviewHandler;
import org.apache.flink.runtime.webmonitor.handlers.DashboardConfigHandler;
+import org.apache.flink.runtime.webmonitor.handlers.JobVertexAccumulatorsHandler;
+import org.apache.flink.runtime.webmonitor.handlers.JobVertexDetailsHandler;
import org.apache.flink.runtime.webmonitor.handlers.RequestHandler;
import org.apache.flink.runtime.webmonitor.handlers.CurrentJobIdsHandler;
import org.apache.flink.runtime.webmonitor.handlers.ClusterOverviewHandler;
@@ -139,16 +142,22 @@ public class WebRuntimeMonitor implements WebMonitor {
.GET("/jobs/:jobid", handler(new JobDetailsHandler(currentGraphs)))
.GET("/jobs/:jobid/vertices", handler(new JobDetailsHandler(currentGraphs)))
+ .GET("/jobs/:jobid/vertices/:vertexid", handler(new JobVertexDetailsHandler(currentGraphs)))
.GET("/jobs/:jobid/vertices/:vertexid/subtasktimes", handler(new SubtasksTimesHandler(currentGraphs)))
+ .GET("/jobs/:jobid/vertices/:vertexid/accumulators", handler(new JobVertexAccumulatorsHandler(currentGraphs)))
+
+// .GET("/jobs/:jobid/vertices/:vertexid/subtasks/:subtaskid/:attempt", handler(null))
+// .GET("/jobs/:jobid/vertices/:vertexid/subtasks/:subtaskid/:attempt/accumulators", handler(null))
.GET("/jobs/:jobid/plan", handler(new JobPlanHandler(currentGraphs)))
.GET("/jobs/:jobid/config", handler(new JobConfigHandler(currentGraphs)))
.GET("/jobs/:jobid/exceptions", handler(new JobExceptionsHandler(currentGraphs)))
+ .GET("/jobs/:jobid/accumulators", handler(new JobAccumulatorsHandler(currentGraphs)))
- // the handler for the legacy requests
- .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
+ // the handler for the legacy requests
+ .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
- // this handler serves all the static contents
+ // this handler serves all the static contents
.GET("/:*", new StaticFileServerHandler(webRootDir));
}
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
index d792ff4..6444e4b 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/CurrentJobsOverviewHandler.java
@@ -61,6 +61,7 @@ public class CurrentJobsOverviewHandler implements RequestHandler, RequestHandle
MultipleJobsDetails result = (MultipleJobsDetails) Await.result(future, timeout);
+ final long now = System.currentTimeMillis();
StringWriter writer = new StringWriter();
JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
@@ -70,20 +71,20 @@ public class CurrentJobsOverviewHandler implements RequestHandler, RequestHandle
if (includeRunningJobs && includeFinishedJobs) {
gen.writeArrayFieldStart("running");
for (JobDetails detail : result.getRunningJobs()) {
- generateSingleJobDetails(detail, gen);
+ generateSingleJobDetails(detail, gen, now);
}
gen.writeEndArray();
gen.writeArrayFieldStart("finished");
for (JobDetails detail : result.getFinishedJobs()) {
- generateSingleJobDetails(detail, gen);
+ generateSingleJobDetails(detail, gen, now);
}
gen.writeEndArray();
}
else {
gen.writeArrayFieldStart("jobs");
for (JobDetails detail : includeRunningJobs ? result.getRunningJobs() : result.getFinishedJobs()) {
- generateSingleJobDetails(detail, gen);
+ generateSingleJobDetails(detail, gen, now);
}
gen.writeEndArray();
}
@@ -97,7 +98,7 @@ public class CurrentJobsOverviewHandler implements RequestHandler, RequestHandle
}
}
- private static void generateSingleJobDetails(JobDetails details, JsonGenerator gen) throws Exception {
+ private static void generateSingleJobDetails(JobDetails details, JsonGenerator gen, long now) throws Exception {
gen.writeStartObject();
gen.writeStringField("jid", details.getJobId().toString());
@@ -106,7 +107,7 @@ public class CurrentJobsOverviewHandler implements RequestHandler, RequestHandle
gen.writeNumberField("start-time", details.getStartTime());
gen.writeNumberField("end-time", details.getEndTime());
- gen.writeNumberField("duration", details.getEndTime() <= 0 ? -1L : details.getEndTime() - details.getStartTime());
+ gen.writeNumberField("duration", (details.getEndTime() <= 0 ? now : details.getEndTime()) - details.getStartTime());
gen.writeNumberField("last-modification", details.getLastUpdateTime());
gen.writeObjectFieldStart("tasks");
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/DashboardConfigHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/DashboardConfigHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/DashboardConfigHandler.java
index c21c35d..ad72f0a 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/DashboardConfigHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/DashboardConfigHandler.java
@@ -19,6 +19,7 @@
package org.apache.flink.runtime.webmonitor.handlers;
import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.flink.runtime.util.EnvironmentInformation;
import java.io.StringWriter;
import java.util.Map;
@@ -46,6 +47,13 @@ public class DashboardConfigHandler implements RequestHandler, RequestHandler.Js
gen.writeNumberField("refresh-interval", refreshInterval);
gen.writeNumberField("timezone-offset", timeZoneOffset);
gen.writeStringField("timezone-name", timeZoneName);
+ gen.writeStringField("flink-version", EnvironmentInformation.getVersion());
+
+ EnvironmentInformation.RevisionInformation revision = EnvironmentInformation.getRevisionInformation();
+ if (revision != null) {
+ gen.writeStringField("flink-revision", revision.commitId + " @ " + revision.commitDate);
+ }
+
gen.writeEndObject();
gen.close();
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobAccumulatorsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobAccumulatorsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobAccumulatorsHandler.java
new file mode 100644
index 0000000..2732497
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobAccumulatorsHandler.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.flink.runtime.accumulators.StringifiedAccumulatorResult;
+import org.apache.flink.runtime.executiongraph.ExecutionGraph;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Request handler that returns the aggregated user accumulators of a job.
+ */
+public class JobAccumulatorsHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+
+ public JobAccumulatorsHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
+ StringifiedAccumulatorResult[] allAccumulators = graph.getAccumulatorResultsStringified();
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+
+ gen.writeArrayFieldStart("job-accumulators");
+ // empty for now
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("user-task-accumulators");
+ for (StringifiedAccumulatorResult acc : allAccumulators) {
+ gen.writeStartObject();
+ gen.writeStringField("name", acc.getName());
+ gen.writeStringField("type", acc.getType());
+ gen.writeStringField("value", acc.getValue());
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobDetailsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobDetailsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobDetailsHandler.java
index feca11b..25b0f19 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobDetailsHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobDetailsHandler.java
@@ -34,7 +34,14 @@ import java.io.StringWriter;
import java.util.Map;
/**
- * Request handler that returns the JSON program plan of a job graph.
+ * Request handler that returns details about a job, including:
+ * <ul>
+ * <li>Dataflow plan</li>
+ * <li>id, name, and current status</li>
+ * <li>start time, end time, duration</li>
+ * <li>number of job vertices in each state (pending, running, finished, failed)</li>
+ * <li>info about job vertices, including runtime, status, I/O bytes and records, subtasks in each status</li>
+ * </ul>
*/
public class JobDetailsHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
@@ -63,6 +70,7 @@ public class JobDetailsHandler extends AbstractExecutionGraphRequestHandler impl
gen.writeNumberField("start-time", jobStartTime);
gen.writeNumberField("end-time", jobEndTime);
gen.writeNumberField("duration", (jobEndTime > 0 ? jobEndTime : now) - jobStartTime);
+ gen.writeNumberField("now", now);
// timestamps
gen.writeObjectFieldStart("timestamps");
@@ -71,9 +79,8 @@ public class JobDetailsHandler extends AbstractExecutionGraphRequestHandler impl
}
gen.writeEndObject();
- final int[] tasksPerStatusTotal = new int[ExecutionState.values().length];
-
// job vertices
+ int[] jobVerticesPerState = new int[ExecutionState.values().length];
gen.writeArrayFieldStart("vertices");
for (ExecutionJobVertex ejv : graph.getVerticesTopologically()) {
@@ -85,7 +92,6 @@ public class JobDetailsHandler extends AbstractExecutionGraphRequestHandler impl
for (ExecutionVertex vertex : ejv.getTaskVertices()) {
final ExecutionState state = vertex.getExecutionState();
tasksPerState[state.ordinal()]++;
- tasksPerStatusTotal[state.ordinal()]++;
// take the earliest start time
long started = vertex.getStateTimestamp(ExecutionState.DEPLOYING);
@@ -112,7 +118,11 @@ public class JobDetailsHandler extends AbstractExecutionGraphRequestHandler impl
endTime = -1L;
duration = -1L;
}
-
+
+ ExecutionState jobVertexState =
+ ExecutionJobVertex.getAggregateJobVertexState(tasksPerState, ejv.getParallelism());
+ jobVerticesPerState[jobVertexState.ordinal()]++;
+
Map<AccumulatorRegistry.Metric, Accumulator<?, ?>> metrics = ejv.getAggregatedMetricAccumulators();
LongCounter readBytes = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_BYTES_IN);
@@ -124,7 +134,8 @@ public class JobDetailsHandler extends AbstractExecutionGraphRequestHandler impl
gen.writeStringField("id", ejv.getJobVertexId().toString());
gen.writeStringField("name", ejv.getJobVertex().getName());
gen.writeNumberField("parallelism", ejv.getParallelism());
-
+ gen.writeStringField("status", jobVertexState.name());
+
gen.writeNumberField("start-time", startTime);
gen.writeNumberField("end-time", endTime);
gen.writeNumberField("duration", duration);
@@ -141,11 +152,20 @@ public class JobDetailsHandler extends AbstractExecutionGraphRequestHandler impl
gen.writeNumberField("read-records", readRecords != null ? readRecords.getLocalValuePrimitive() : -1L);
gen.writeNumberField("write-records",writeRecords != null ? writeRecords.getLocalValuePrimitive() : -1L);
gen.writeEndObject();
-
+
gen.writeEndObject();
}
gen.writeEndArray();
+ gen.writeObjectFieldStart("status-counts");
+ for (ExecutionState state : ExecutionState.values()) {
+ gen.writeNumberField(state.name(), jobVerticesPerState[state.ordinal()]);
+ }
+ gen.writeEndObject();
+
+ gen.writeFieldName("plan");
+ gen.writeRawValue(graph.getJsonPlan());
+
gen.writeEndObject();
gen.close();
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexAccumulatorsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexAccumulatorsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexAccumulatorsHandler.java
new file mode 100644
index 0000000..8554a31
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexAccumulatorsHandler.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import org.apache.flink.runtime.accumulators.StringifiedAccumulatorResult;
+import org.apache.flink.runtime.executiongraph.ExecutionGraph;
+import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
+import org.apache.flink.runtime.jobgraph.JobVertexID;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+
+public class JobVertexAccumulatorsHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+
+ public JobVertexAccumulatorsHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
+ String vidString = params.get("vertexid");
+ if (vidString == null) {
+ throw new IllegalArgumentException("vertexId parameter missing");
+ }
+
+ JobVertexID vid;
+ try {
+ vid = JobVertexID.fromHexString(vidString);
+ }
+ catch (Exception e) {
+ throw new IllegalArgumentException("Invalid JobVertexID string '" + vidString + "': " + e.getMessage());
+ }
+
+ ExecutionJobVertex jobVertex = graph.getJobVertex(vid);
+ if (jobVertex == null) {
+ throw new IllegalArgumentException("No vertex with ID '" + vidString + "' exists.");
+ }
+
+ StringifiedAccumulatorResult[] accs = jobVertex.getAggregatedUserAccumulatorsStringified();
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+ gen.writeStringField("id", jobVertex.getJobVertexId().toString());
+
+ gen.writeArrayFieldStart("user-accumulators");
+ for (StringifiedAccumulatorResult acc : accs) {
+ gen.writeStartObject();
+ gen.writeStringField("name", acc.getName());
+ gen.writeStringField("type", acc.getType());
+ gen.writeStringField("value", acc.getValue());
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
new file mode 100644
index 0000000..7815f49
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.flink.api.common.accumulators.Accumulator;
+import org.apache.flink.api.common.accumulators.LongCounter;
+import org.apache.flink.runtime.accumulators.AccumulatorRegistry;
+import org.apache.flink.runtime.execution.ExecutionState;
+import org.apache.flink.runtime.executiongraph.ExecutionGraph;
+import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
+import org.apache.flink.runtime.executiongraph.ExecutionVertex;
+import org.apache.flink.runtime.instance.InstanceConnectionInfo;
+import org.apache.flink.runtime.jobgraph.JobVertexID;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+
+public class JobVertexDetailsHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+
+ public JobVertexDetailsHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
+ String vidString = params.get("vertexid");
+ if (vidString == null) {
+ throw new IllegalArgumentException("vertexId parameter missing");
+ }
+
+ JobVertexID vid;
+ try {
+ vid = JobVertexID.fromHexString(vidString);
+ }
+ catch (Exception e) {
+ throw new IllegalArgumentException("Invalid JobVertexID string '" + vidString + "': " + e.getMessage());
+ }
+
+ ExecutionJobVertex jobVertex = graph.getJobVertex(vid);
+ if (jobVertex == null) {
+ throw new IllegalArgumentException("No vertex with ID '" + vidString + "' exists.");
+ }
+
+ final long now = System.currentTimeMillis();
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+
+ gen.writeStringField("id", jobVertex.getJobVertexId().toString());
+ gen.writeStringField("name", jobVertex.getJobVertex().getName());
+ gen.writeNumberField("parallelism", jobVertex.getParallelism());
+ gen.writeNumberField("now", now);
+
+ gen.writeArrayFieldStart("subtasks");
+ int num = 0;
+ for (ExecutionVertex vertex : jobVertex.getTaskVertices()) {
+ final ExecutionState status = vertex.getExecutionState();
+
+ InstanceConnectionInfo location = vertex.getCurrentAssignedResourceLocation();
+ String locationString = location == null ? "(unassigned)" : location.getHostname();
+
+ long startTime = vertex.getStateTimestamp(ExecutionState.DEPLOYING);
+ if (startTime == 0) {
+ startTime = -1;
+ }
+ long endTime = status.isTerminal() ? vertex.getStateTimestamp(status) : -1;
+ long duration = startTime > 0 ? ((endTime > 0 ? endTime : now) - startTime) : -1;
+
+ Map<AccumulatorRegistry.Metric, Accumulator<?, ?>> metrics = vertex.getCurrentExecutionAttempt().getFlinkAccumulators();
+ LongCounter readBytes;
+ LongCounter writeBytes;
+ LongCounter readRecords;
+ LongCounter writeRecords;
+
+ if (metrics != null) {
+ readBytes = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_BYTES_IN);
+ writeBytes = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_BYTES_OUT);
+ readRecords = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_RECORDS_IN);
+ writeRecords = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_RECORDS_OUT);
+ }
+ else {
+ readBytes = null;
+ writeBytes = null;
+ readRecords = null;
+ writeRecords = null;
+ }
+
+ gen.writeStartObject();
+ gen.writeNumberField("subtask", num);
+ gen.writeStringField("status", status.name());
+ gen.writeNumberField("attempt", vertex.getCurrentExecutionAttempt().getAttemptNumber());
+ gen.writeStringField("host", locationString);
+ gen.writeNumberField("start-time", startTime);
+ gen.writeNumberField("end-time", endTime);
+ gen.writeNumberField("duration", duration);
+
+ gen.writeObjectFieldStart("metrics");
+ gen.writeNumberField("read-bytes", readBytes != null ? readBytes.getLocalValuePrimitive() : -1L);
+ gen.writeNumberField("write-bytes", writeBytes != null ? writeBytes.getLocalValuePrimitive() : -1L);
+ gen.writeNumberField("read-records", readRecords != null ? readRecords.getLocalValuePrimitive() : -1L);
+ gen.writeNumberField("write-records",writeRecords != null ? writeRecords.getLocalValuePrimitive() : -1L);
+ gen.writeEndObject();
+
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
index 4d268ef..8cd7b44 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
@@ -68,7 +68,8 @@ public class SubtasksTimesHandler extends AbstractExecutionGraphRequestHandler i
gen.writeStringField("id", jobVertex.getJobVertexId().toString());
gen.writeStringField("name", jobVertex.getJobVertex().getName());
-
+ gen.writeNumberField("now", System.currentTimeMillis());
+
gen.writeArrayFieldStart("subtasks");
int num = 0;
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java b/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java
index 3b25fe0..ea01acd 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java
@@ -18,6 +18,10 @@
package org.apache.flink.runtime.accumulators;
+import org.apache.flink.api.common.accumulators.Accumulator;
+
+import java.util.Map;
+
/**
* Container class that transports the result of an accumulator as set of strings.
*/
@@ -46,4 +50,26 @@ public class StringifiedAccumulatorResult implements java.io.Serializable{
public String getValue() {
return value;
}
+
+ // ------------------------------------------------------------------------
+ // Utilities
+ // ------------------------------------------------------------------------
+
+ public static StringifiedAccumulatorResult[] stringifyAccumulatorResults(Map<String, Accumulator<?, ?>> accs) {
+ StringifiedAccumulatorResult[] results = new StringifiedAccumulatorResult[accs.size()];
+
+ int i = 0;
+ for (Map.Entry<String, Accumulator<?, ?>> entry : accs.entrySet()) {
+ StringifiedAccumulatorResult result;
+ Accumulator<?, ?> value = entry.getValue();
+ if (value != null) {
+ result = new StringifiedAccumulatorResult(entry.getKey(), value.getClass().getSimpleName(), value.toString());
+ } else {
+ result = new StringifiedAccumulatorResult(entry.getKey(), "null", "null");
+ }
+
+ results[i++] = result;
+ }
+ return results;
+ }
}
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java
index 47175a9..70a49fd 100755
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java
@@ -584,27 +584,8 @@ public class ExecutionGraph implements Serializable {
* @return an Array containing the StringifiedAccumulatorResult objects
*/
public StringifiedAccumulatorResult[] getAccumulatorResultsStringified() {
-
Map<String, Accumulator<?, ?>> accumulatorMap = aggregateUserAccumulators();
-
- int num = accumulatorMap.size();
- StringifiedAccumulatorResult[] resultStrings = new StringifiedAccumulatorResult[num];
-
- int i = 0;
- for (Map.Entry<String, Accumulator<?, ?>> entry : accumulatorMap.entrySet()) {
-
- StringifiedAccumulatorResult result;
- Accumulator<?, ?> value = entry.getValue();
- if (value != null) {
- result = new StringifiedAccumulatorResult(entry.getKey(), value.getClass().getSimpleName(), value.toString());
- } else {
- result = new StringifiedAccumulatorResult(entry.getKey(), "null", "null");
- }
-
- resultStrings[i++] = result;
- }
-
- return resultStrings;
+ return StringifiedAccumulatorResult.stringifyAccumulatorResults(accumulatorMap);
}
// --------------------------------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java
index 2d6af6f..53d94d7 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionJobVertex.java
@@ -19,6 +19,7 @@
package org.apache.flink.runtime.executiongraph;
import org.apache.flink.api.common.accumulators.Accumulator;
+import org.apache.flink.api.common.accumulators.AccumulatorHelper;
import org.apache.flink.api.common.accumulators.LongCounter;
import org.apache.flink.api.common.io.StrictlyLocalAssignment;
import org.apache.flink.core.io.InputSplit;
@@ -27,6 +28,7 @@ import org.apache.flink.core.io.InputSplitSource;
import org.apache.flink.core.io.LocatableInputSplit;
import org.apache.flink.runtime.JobException;
import org.apache.flink.runtime.accumulators.AccumulatorRegistry;
+import org.apache.flink.runtime.accumulators.StringifiedAccumulatorResult;
import org.apache.flink.runtime.execution.ExecutionState;
import org.apache.flink.runtime.instance.Instance;
import org.apache.flink.runtime.jobgraph.JobVertex;
@@ -218,33 +220,12 @@ public class ExecutionJobVertex implements Serializable {
}
public ExecutionState getAggregateState() {
-
int[] num = new int[ExecutionState.values().length];
-
for (ExecutionVertex vertex : this.taskVertices) {
num[vertex.getExecutionState().ordinal()]++;
}
-
- if (num[ExecutionState.FAILED.ordinal()] > 0) {
- return ExecutionState.FAILED;
- }
- if (num[ExecutionState.CANCELING.ordinal()] > 0) {
- return ExecutionState.CANCELING;
- }
- else if (num[ExecutionState.CANCELED.ordinal()] > 0) {
- return ExecutionState.CANCELED;
- }
- else if (num[ExecutionState.RUNNING.ordinal()] > 0) {
- return ExecutionState.RUNNING;
- }
- else if (num[ExecutionState.FINISHED.ordinal()] > 0) {
- return num[ExecutionState.FINISHED.ordinal()] == parallelism ?
- ExecutionState.FINISHED : ExecutionState.RUNNING;
- }
- else {
- // all else collapses under created
- return ExecutionState.CREATED;
- }
+
+ return getAggregateJobVertexState(num, parallelism);
}
//---------------------------------------------------------------------------------------------
@@ -528,6 +509,19 @@ public class ExecutionJobVertex implements Serializable {
return agg;
}
+ public StringifiedAccumulatorResult[] getAggregatedUserAccumulatorsStringified() {
+ Map<String, Accumulator<?, ?>> userAccumulators = new HashMap<String, Accumulator<?, ?>>();
+
+ for (ExecutionVertex vertex : taskVertices) {
+ Map<String, Accumulator<?, ?>> next = vertex.getCurrentExecutionAttempt().getUserAccumulators();
+ if (next != null) {
+ AccumulatorHelper.mergeInto(userAccumulators, next);
+ }
+ }
+
+ return StringifiedAccumulatorResult.stringifyAccumulatorResults(userAccumulators);
+ }
+
// --------------------------------------------------------------------------------------------
// Static / pre-assigned input splits
// --------------------------------------------------------------------------------------------
@@ -665,4 +659,31 @@ public class ExecutionJobVertex implements Serializable {
}
}
}
+
+ public static ExecutionState getAggregateJobVertexState(int[] verticesPerState, int parallelism) {
+ if (verticesPerState == null || verticesPerState.length != ExecutionState.values().length) {
+ throw new IllegalArgumentException("Must provide an array as large as there are execution states.");
+ }
+
+ if (verticesPerState[ExecutionState.FAILED.ordinal()] > 0) {
+ return ExecutionState.FAILED;
+ }
+ if (verticesPerState[ExecutionState.CANCELING.ordinal()] > 0) {
+ return ExecutionState.CANCELING;
+ }
+ else if (verticesPerState[ExecutionState.CANCELED.ordinal()] > 0) {
+ return ExecutionState.CANCELED;
+ }
+ else if (verticesPerState[ExecutionState.RUNNING.ordinal()] > 0) {
+ return ExecutionState.RUNNING;
+ }
+ else if (verticesPerState[ExecutionState.FINISHED.ordinal()] > 0) {
+ return verticesPerState[ExecutionState.FINISHED.ordinal()] == parallelism ?
+ ExecutionState.FINISHED : ExecutionState.RUNNING;
+ }
+ else {
+ // all else collapses under created
+ return ExecutionState.CREATED;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonPlanGenerator.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonPlanGenerator.java b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonPlanGenerator.java
index 0844dab..bd5357e 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonPlanGenerator.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonPlanGenerator.java
@@ -41,7 +41,6 @@ public class JsonPlanGenerator {
final JsonFactory factory = new JsonFactory();
final JsonGenerator gen = factory.createGenerator(writer);
- gen.useDefaultPrettyPrinter();
// start of everything
gen.writeStartObject();
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala
index 55a7ccb..d3fc8b1 100644
--- a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala
+++ b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala
@@ -157,7 +157,7 @@ class MemoryArchivist(private val max_entries: Int)
message match {
case _ : RequestJobsOverview =>
try {
- sender ! createJobsOverview()
+ sender ! decorateMessage(createJobsOverview())
}
catch {
case t: Throwable => log.error("Exception while creating the jobs overview", t)
@@ -165,7 +165,7 @@ class MemoryArchivist(private val max_entries: Int)
case _ : RequestJobsWithIDsOverview =>
try {
- sender ! createJobsWithIDsOverview()
+ sender ! decorateMessage(createJobsWithIDsOverview())
}
catch {
case t: Throwable => log.error("Exception while creating the jobs overview", t)
@@ -176,7 +176,7 @@ class MemoryArchivist(private val max_entries: Int)
v => WebMonitorUtils.createDetailsForJob(v)
}.toArray[JobDetails]
- theSender ! new MultipleJobsDetails(null, details)
+ theSender ! decorateMessage(new MultipleJobsDetails(null, details))
}
}
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime/src/test/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonGeneratorTest.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonGeneratorTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonGeneratorTest.java
index 1ed46f2..46fb694 100644
--- a/flink-runtime/src/test/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonGeneratorTest.java
+++ b/flink-runtime/src/test/java/org/apache/flink/runtime/jobgraph/jsonplan/JsonGeneratorTest.java
@@ -71,8 +71,6 @@ public class JsonGeneratorTest {
String plan = JsonPlanGenerator.generatePlan(jg);
assertNotNull(plan);
-
- System.out.println(plan);
// validate the produced JSON
ObjectMapper m = new ObjectMapper();
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-runtime/src/test/java/org/apache/flink/runtime/messages/WebMonitorMessagesTest.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/messages/WebMonitorMessagesTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/messages/WebMonitorMessagesTest.java
index b70853e..8d281f7 100644
--- a/flink-runtime/src/test/java/org/apache/flink/runtime/messages/WebMonitorMessagesTest.java
+++ b/flink-runtime/src/test/java/org/apache/flink/runtime/messages/WebMonitorMessagesTest.java
@@ -25,6 +25,7 @@ import org.apache.flink.runtime.messages.webmonitor.JobDetails;
import org.apache.flink.runtime.messages.webmonitor.JobsOverview;
import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
import org.apache.flink.runtime.messages.webmonitor.MultipleJobsDetails;
+import org.apache.flink.runtime.messages.webmonitor.RequestJobDetails;
import org.apache.flink.runtime.messages.webmonitor.RequestJobsOverview;
import org.apache.flink.runtime.messages.webmonitor.RequestJobsWithIDsOverview;
import org.apache.flink.runtime.messages.webmonitor.RequestStatusOverview;
@@ -47,9 +48,9 @@ public class WebMonitorMessagesTest {
GenericMessageTester.testMessageInstance(RequestJobsOverview.getInstance());
GenericMessageTester.testMessageInstance(RequestJobsWithIDsOverview.getInstance());
GenericMessageTester.testMessageInstance(RequestStatusOverview.getInstance());
+ GenericMessageTester.testMessageInstance(RequestJobsOverview.getInstance());
- GenericMessageTester.testMessageInstance(GenericMessageTester.instantiateGeneric(RequestJobsOverview.class, rnd));
-
+ GenericMessageTester.testMessageInstance(GenericMessageTester.instantiateGeneric(RequestJobDetails.class, rnd));
GenericMessageTester.testMessageInstance(GenericMessageTester.instantiateGeneric(StatusOverview.class, rnd));
GenericMessageTester.testMessageInstance(GenericMessageTester.instantiateGeneric(JobsOverview.class, rnd));
http://git-wip-us.apache.org/repos/asf/flink/blob/99a351e4/flink-tests/src/test/java/org/apache/flink/test/optimizer/jsonplan/JsonJobGraphGenerationTest.java
----------------------------------------------------------------------
diff --git a/flink-tests/src/test/java/org/apache/flink/test/optimizer/jsonplan/JsonJobGraphGenerationTest.java b/flink-tests/src/test/java/org/apache/flink/test/optimizer/jsonplan/JsonJobGraphGenerationTest.java
index c552700..121bc88 100644
--- a/flink-tests/src/test/java/org/apache/flink/test/optimizer/jsonplan/JsonJobGraphGenerationTest.java
+++ b/flink-tests/src/test/java/org/apache/flink/test/optimizer/jsonplan/JsonJobGraphGenerationTest.java
@@ -58,19 +58,19 @@ public class JsonJobGraphGenerationTest {
private PrintStream out;
private PrintStream err;
-// @Before
-// public void redirectStreams() {
-// this.out = System.out;
-// this.err = System.err;
-//
-// OutputStream discards = new OutputStream() {
-// @Override
-// public void write(int b) {}
-// };
-//
-// System.setOut(new PrintStream(discards));
-// System.setErr(new PrintStream(discards));
-// }
+ @Before
+ public void redirectStreams() {
+ this.out = System.out;
+ this.err = System.err;
+
+ OutputStream discards = new OutputStream() {
+ @Override
+ public void write(int b) {}
+ };
+
+ System.setOut(new PrintStream(discards));
+ System.setErr(new PrintStream(discards));
+ }
@After
public void restoreStreams() {
@@ -228,8 +228,6 @@ public class JsonJobGraphGenerationTest {
@Override
public void validtateJson(String json) throws Exception {
- System.out.println(json);
-
final Map<String, JsonNode> idToNode = new HashMap<>();
// validate the produced JSON
[22/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Auto-update overview page
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/907f773c/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index 130ca05..8605702 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -6,7 +6,6 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
};
}]).value('flinkConfig', {
jobServer: 'http://localhost:8081',
- newServer: 'http://localhost:8081',
"refresh-interval": 10000
}).run(["JobsService", "MainService", "flinkConfig", "$interval", function(JobsService, MainService, flinkConfig, $interval) {
return MainService.loadConfig().then(function(config) {
@@ -173,7 +172,7 @@ angular.module('flinkApp').service('MainService', ["$http", "flinkConfig", "$q",
this.loadConfig = function() {
var deferred;
deferred = $q.defer();
- $http.get(flinkConfig.newServer + "/config").success(function(data, status, headers, config) {
+ $http.get(flinkConfig.jobServer + "/config").success(function(data, status, headers, config) {
return deferred.resolve(data);
});
return deferred.promise;
@@ -219,7 +218,7 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
return $scope.node = data;
});
}]).controller('JobTimelineVertexController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
- return JobsService.getVertex($stateParams.jobid, $stateParams.vertexId).then(function(data) {
+ return JobsService.getVertex($stateParams.vertexId).then(function(data) {
return $scope.vertex = data;
});
}]);
@@ -238,32 +237,30 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
analyzeTime = function(data) {
var chart, svg, testData;
testData = [];
- angular.forEach(data.groupvertex.groupmembers, function(vertex, i) {
- var vTime;
- vTime = data.verticetimes[vertex.vertexid];
+ angular.forEach(data.subtasks, function(subtask, i) {
return testData.push({
- label: vertex.vertexinstancename + " (" + i + ")",
+ label: subtask.host + " (" + subtask.subtask + ")",
times: [
{
label: "Scheduled",
color: "#666",
borderColor: "#555",
- starting_time: vTime["SCHEDULED"] * 100,
- ending_time: vTime["DEPLOYING"] * 100,
+ starting_time: subtask.timestamps["SCHEDULED"],
+ ending_time: subtask.timestamps["DEPLOYING"],
type: 'regular'
}, {
label: "Deploying",
color: "#aaa",
borderColor: "#555",
- starting_time: vTime["DEPLOYING"] * 100,
- ending_time: vTime["RUNNING"] * 100,
+ starting_time: subtask.timestamps["DEPLOYING"],
+ ending_time: subtask.timestamps["RUNNING"],
type: 'regular'
}, {
label: "Running",
color: "#ddd",
borderColor: "#555",
- starting_time: vTime["RUNNING"] * 100,
- ending_time: vTime["FINISHED"] * 100,
+ starting_time: subtask.timestamps["RUNNING"],
+ ending_time: subtask.timestamps["FINISHED"],
type: 'regular'
}
]
@@ -308,24 +305,22 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
label: "Scheduled",
color: "#cccccc",
borderColor: "#555",
- starting_time: data.oldV["SCHEDULED"],
- ending_time: data.oldV["SCHEDULED"] + 1,
+ starting_time: data.timestamps["CREATED"],
+ ending_time: data.timestamps["CREATED"] + 1,
type: 'scheduled'
}
]
});
- angular.forEach(data.oldV.groupvertices, function(vertex) {
- var vTime;
- vTime = data.oldV.groupverticetimes[vertex.groupvertexid];
+ angular.forEach(data.vertices, function(vertex) {
return testData.push({
times: [
{
- label: translateLabel(vertex.groupvertexname),
+ label: translateLabel(vertex.name),
color: "#d9f1f7",
borderColor: "#62cdea",
- starting_time: vTime["STARTED"],
- ending_time: vTime["ENDED"],
- link: vertex.groupvertexid,
+ starting_time: vertex['start-time'],
+ ending_time: vertex['end-time'],
+ link: vertex.id,
type: 'regular'
}
]
@@ -705,7 +700,7 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.listJobs = function() {
var deferred;
deferred = $q.defer();
- $http.get(flinkConfig.newServer + "/joboverview").success(function(data, status, headers, config) {
+ $http.get(flinkConfig.jobServer + "/joboverview").success(function(data, status, headers, config) {
angular.forEach(data, function(list, listKey) {
switch (listKey) {
case 'running':
@@ -732,14 +727,13 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.loadJob = function(jobid) {
currentJob = null;
deferreds.job = $q.defer();
- $http.get(flinkConfig.newServer + "/jobs/" + jobid).success(function(data, status, headers, config) {
- data.time = Date.now();
- return $http.get(flinkConfig.newServer + "/jobs/" + jobid + "/vertices").success(function(vertices) {
+ $http.get(flinkConfig.jobServer + "/jobs/" + jobid).success(function(data, status, headers, config) {
+ return $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/vertices").success(function(vertices) {
data = angular.extend(data, vertices);
- return $http.get(flinkConfig.jobServer + "/jobsInfo?get=job&job=" + jobid).success(function(oldVertices) {
- data.oldV = oldVertices[0];
- return $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/config").success(function(jobConfig) {
- data = angular.extend(data, jobConfig);
+ return $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/config").success(function(jobConfig) {
+ data = angular.extend(data, jobConfig);
+ return $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/exceptions").success(function(exceptions) {
+ data.exceptions = exceptions;
currentJob = data;
return deferreds.job.resolve(data);
});
@@ -751,7 +745,7 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.loadPlan = function(jobid) {
currentPlan = null;
deferreds.plan = $q.defer();
- $http.get(flinkConfig.newServer + "/jobs/" + jobid + "/plan").success(function(data) {
+ $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/plan").success(function(data) {
currentPlan = data;
return deferreds.plan.resolve(data);
});
@@ -780,26 +774,43 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
return function(data) {
var foundNode;
foundNode = seekNode(nodeid, currentPlan.nodes);
- return _this.getVertex(currentJob.jid, nodeid).then(function(vertex) {
- foundNode.vertex = vertex;
- return deferred.resolve(foundNode);
- });
+ foundNode.vertex = _this.seekVertex(nodeid);
+ return deferred.resolve(foundNode);
};
})(this));
return deferred.promise;
};
- this.getVertex = function(jobId, vertexId) {
+ this.seekVertex = function(nodeid) {
+ var i, len, ref, vertex;
+ ref = currentJob.vertices;
+ for (i = 0, len = ref.length; i < len; i++) {
+ vertex = ref[i];
+ if (vertex.id === nodeid) {
+ return vertex;
+ }
+ }
+ return null;
+ };
+ this.getVertex = function(vertexid) {
var deferred;
deferred = $q.defer();
- $http.get(flinkConfig.jobServer + "/jobsInfo?get=groupvertex&job=" + jobId + "&groupvertex=" + vertexId).success(function(data) {
- return deferred.resolve(data);
- });
+ $q.all([deferreds.job.promise]).then((function(_this) {
+ return function(data) {
+ var vertex;
+ vertex = _this.seekVertex(vertexid);
+ return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasktimes").success(function(data) {
+ vertex.subtasks = data.subtasks;
+ return deferred.resolve(vertex);
+ });
+ };
+ })(this));
return deferred.promise;
};
return this;
}]);
-angular.module('flinkApp').controller('OverviewController', ["$scope", "OverviewService", "JobsService", function($scope, OverviewService, JobsService) {
+angular.module('flinkApp').controller('OverviewController', ["$scope", "OverviewService", "JobsService", "$interval", "flinkConfig", function($scope, OverviewService, JobsService, $interval, flinkConfig) {
+ var refresh;
$scope.jobObserver = function() {
$scope.runningJobs = JobsService.getJobs('running');
return $scope.finishedJobs = JobsService.getJobs('finished');
@@ -809,9 +820,17 @@ angular.module('flinkApp').controller('OverviewController', ["$scope", "Overview
return JobsService.unRegisterObserver($scope.jobObserver);
});
$scope.jobObserver();
- return OverviewService.loadOverview().then(function(data) {
+ OverviewService.loadOverview().then(function(data) {
return $scope.overview = data;
});
+ refresh = $interval(function() {
+ return OverviewService.loadOverview().then(function(data) {
+ return $scope.overview = data;
+ });
+ }, flinkConfig["refresh-interval"]);
+ return $scope.$on('$destroy', function() {
+ return $interval.cancel(refresh);
+ });
}]);
angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) {
@@ -829,4 +848,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsImNvbW1vbi9zZXJ2aWNlcy5jb2ZmZWUiLCJjb21tb24vc2VydmljZXMuanMiLCJtb2R1bGVzL2pvYnMvam9icy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuanMiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuZGlyLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLnN2Yy5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LmN0cmwuanMiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkEsUUFBUSxPQUFPLFlBQVksQ0FBQyxhQUFhLGtCQUl4QyxtQkFBSSxTQUFDLFlBQUQ7RUFDSCxXQUFXLGlCQUFpQjtFQ3JCNUIsT0RzQkEsV0FBVyxjQUFjLFdBQUE7SUFDdkIsV0FBVyxpQkFBaUIsQ0FBQyxXQUFXO0lDckJ4QyxPRHNCQSxXQUFXLGVBQWU7O0lBSTdCLE1BQU0sZUFBZTtFQUN
wQixXQUFXO0VBQ1gsV0FBVztFQUdYLG9CQUFvQjtHQUtyQiwrREFBSSxTQUFDLGFBQWEsYUFBYSxhQUFhLFdBQXhDO0VDN0JILE9EOEJBLFlBQVksYUFBYSxLQUFLLFNBQUMsUUFBRDtJQUM1QixRQUFRLE9BQU8sYUFBYTtJQUU1QixZQUFZO0lDOUJaLE9EZ0NBLFVBQVUsV0FBQTtNQy9CUixPRGdDQSxZQUFZO09BQ1osWUFBWTs7SUFLakIsZ0RBQU8sU0FBQyxnQkFBZ0Isb0JBQWpCO0VBQ04sZUFBZSxNQUFNLFlBQ25CO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxNQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLGdCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxNQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLGtCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxNQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLGNBQ0w7SUFBQSxLQUFLO0lBQ0wsVUFBVTtJQUNWLE9BQ0U7TUFBQSxNQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLG1CQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLHdCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxNQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLHVCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTs7O0tBRWxCLE1BQU0sOEJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFFB
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
QUFPOzs7TUNuQlQsT0RxQkE7O0lBRUYsV0FBVyxHQUFHO0lBVWQsR0FBRyxJQUFJLENBQUMsVUFBVSxLQUFLLFNBQVMsVUFBVSxJQUFJLFVBQVUsS0FBSyxDQUFBLFNBQUEsT0FBQTtNQzdCM0QsT0Q2QjJELFNBQUMsTUFBRDtRQUMzRCxJQUFBO1FBQUEsWUFBWSxTQUFTLFFBQVEsWUFBWTtRQzNCdkMsT0Q4QkYsTUFBQyxVQUFVLFdBQVcsS0FBSyxRQUFRLEtBQUssU0FBQyxRQUFEO1VBQ3RDLFVBQVUsU0FBUztVQzdCakIsT0Q4QkYsU0FBUyxRQUFROzs7T0FOd0M7SUNwQjdELE9ENEJBLFNBQVM7O0VBR1gsS0FBQyxZQUFZLFNBQUMsT0FBTyxVQUFSO0lBQ1gsSUFBQTtJQUFBLFdBQVcsR0FBRztJQUVkLE1BQU0sSUFBSSxZQUFZLFlBQVksbUNBQW1DLFFBQVEsa0JBQWtCLFVBQzlGLFFBQVEsU0FBQyxNQUFEO01DN0JQLE9EOEJBLFNBQVMsUUFBUTs7SUM1Qm5CLE9EOEJBLFNBQVM7O0VDNUJYLE9EOEJBOztBQzVCRjtBQ2hJQSxRQUFRLE9BQU8sWUFFZCxXQUFXLG1FQUFzQixTQUFDLFFBQVEsaUJBQWlCLGFBQTFCO0VBQ2hDLE9BQU8sY0FBYyxXQUFBO0lBQ25CLE9BQU8sY0FBYyxZQUFZLFFBQVE7SUNuQnpDLE9Eb0JBLE9BQU8sZUFBZSxZQUFZLFFBQVE7O0VBRTVDLFlBQVksaUJBQWlCLE9BQU87RUFDcEMsT0FBTyxJQUFJLFlBQVksV0FBQTtJQ25CckIsT0RvQkEsWUFBWSxtQkFBbUIsT0FBTzs7RUFFeEMsT0FBTztFQ25CUCxPRHFCQSxnQkFBZ0IsZUFBZSxLQUFLLFNBQUMsTUFBRDtJQ3BCbEMsT0RxQkEsT0FBT
yxXQUFXOzs7QUNsQnRCO0FDSUEsUUFBUSxPQUFPLFlBRWQsUUFBUSxrREFBbUIsU0FBQyxPQUFPLGFBQWEsSUFBckI7RUFDMUIsSUFBQTtFQUFBLFdBQVc7RUFFWCxLQUFDLGVBQWUsV0FBQTtJQUNkLElBQUE7SUFBQSxXQUFXLEdBQUc7SUFFZCxNQUFNLElBQUksWUFBWSxZQUFZLGFBQ2pDLFFBQVEsU0FBQyxNQUFNLFFBQVEsU0FBUyxRQUF4QjtNQUNQLFdBQVc7TUNwQlgsT0RxQkEsU0FBUyxRQUFROztJQ25CbkIsT0RxQkEsU0FBUzs7RUNuQlgsT0RxQkE7O0FDbkJGIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly
93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcsIFsndWkucm91dGVyJywgJ2FuZ3VsYXJNb21lbnQnXSlcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4ucnVuICgkcm9vdFNjb3BlKSAtPlxuICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gZmFsc2VcbiAgJHJvb3RTY29wZS5zaG93U2lkZWJhciA9IC0+XG4gICAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9ICEkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlXG4gICAgJHJvb3RTY29wZS5zaWRlYmFyQ2xhc3MgPSAnZm9yY2Utc2hvdydcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4udmFsdWUgJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ
2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnXG4gIG5ld1NlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MSdcbiMgIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9uZXctc2VydmVyJ1xuIyAgbmV3U2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDozMDAwL25ldy1zZXJ2ZXInXG4gIFwicmVmcmVzaC1pbnRlcnZhbFwiOiAxMDAwMFxufVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKEpvYnNTZXJ2aWNlLCBNYWluU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkgLT5cbiAgTWFpblNlcnZpY2UubG9hZENvbmZpZygpLnRoZW4gKGNvbmZpZykgLT5cbiAgICBhbmd1bGFyLmV4dGVuZCBmbGlua0NvbmZpZywgY29uZmlnXG5cbiAgICBKb2JzU2VydmljZS5saXN0Sm9icygpXG5cbiAgICAkaW50ZXJ2YWwgLT5cbiAgICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKClcbiAgICAsIGZsaW5rQ29uZmlnW1wicmVmcmVzaC1pbnRlcnZhbFwiXVxuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbmZpZyAoJHN0YXRlUHJvdmlkZXIsICR1cmxSb3V0ZXJQcm92aWRlcikgLT5cbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUgXCJvdmVydmlld1wiLFxuICAgIHVybDogXCIvb3ZlcnZpZXdcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvb3ZlcnZpZXcuaHRtbFwiXG4g
ICAgICAgIGNvbnRyb2xsZXI6ICdPdmVydmlld0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwicnVubmluZy1qb2JzXCIsXG4gICAgdXJsOiBcIi9ydW5uaW5nLWpvYnNcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9ydW5uaW5nLWpvYnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInXG4gIFxuICAuc3RhdGUgXCJjb21wbGV0ZWQtam9ic1wiLFxuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9jb21wbGV0ZWQtam9icy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2JcIixcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwiXG4gICAgYWJzdHJhY3Q6IHRydWVcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnU2luZ2xlSm9iQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW5cIixcbiAgICB1cmw6IFwiXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiXG4gI
CAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4ubm9kZVwiLFxuICAgIHVybDogXCIvdmVydGV4L3tub2RlaWR9XCJcbiAgICB2aWV3czpcbiAgICAgIG5vZGU6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lXCIsXG4gICAgdXJsOiBcIi90aW1lbGluZVwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLFxuICAgIHVybDogXCIve3ZlcnRleElkfVwiXG4gICAgdmlld3M6XG4gICAgICB2ZXJ0ZXg6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLnZlcnRleC5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIixcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2Iuc3RhdGlzdGljcy5odG1sXC
JcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIixcbiAgICB1cmw6IFwiL2V4Y2VwdGlvbnNcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLmNvbmZpZ1wiLFxuICAgIHVybDogXCIvY29uZmlnXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmNvbmZpZy5odG1sXCJcblxuICAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlIFwiL292ZXJ2aWV3XCJcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcsIFsndWkucm91dGVyJywgJ2FuZ3VsYXJNb21lbnQnXSkucnVuKGZ1bmN0aW9uKCRyb290U2NvcGUpIHtcbiAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9IGZhbHNlO1xuICByZXR1cm4gJHJvb3RTY29wZS5zaG93U2lkZWJhciA9IGZ1bmN0aW9uKCkge1xuICAgICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSAhJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZTtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5zaWRlYmFyQ2xhc3MgPSAnZm9yY2Utc2hvdyc7XG4gIH07XG59KS52YWx1ZSgnZmxpbmtDb25maWcnLCB7XG4gIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MScsXG4gIG5ld1NlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MScsXG4gIFw
icmVmcmVzaC1pbnRlcnZhbFwiOiAxMDAwMFxufSkucnVuKGZ1bmN0aW9uKEpvYnNTZXJ2aWNlLCBNYWluU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkge1xuICByZXR1cm4gTWFpblNlcnZpY2UubG9hZENvbmZpZygpLnRoZW4oZnVuY3Rpb24oY29uZmlnKSB7XG4gICAgYW5ndWxhci5leHRlbmQoZmxpbmtDb25maWcsIGNvbmZpZyk7XG4gICAgSm9ic1NlcnZpY2UubGlzdEpvYnMoKTtcbiAgICByZXR1cm4gJGludGVydmFsKGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCk7XG4gICAgfSwgZmxpbmtDb25maWdbXCJyZWZyZXNoLWludGVydmFsXCJdKTtcbiAgfSk7XG59KS5jb25maWcoZnVuY3Rpb24oJHN0YXRlUHJvdmlkZXIsICR1cmxSb3V0ZXJQcm92aWRlcikge1xuICAkc3RhdGVQcm92aWRlci5zdGF0ZShcIm92ZXJ2aWV3XCIsIHtcbiAgICB1cmw6IFwiL292ZXJ2aWV3XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvb3ZlcnZpZXcuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnT3ZlcnZpZXdDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJydW5uaW5nLWpvYnNcIiwge1xuICAgIHVybDogXCIvcnVubmluZy1qb2JzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9ydW5uaW5nLWpv
YnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnUnVubmluZ0pvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJjb21wbGV0ZWQtam9ic1wiLCB7XG4gICAgdXJsOiBcIi9jb21wbGV0ZWQtam9ic1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvY29tcGxldGVkLWpvYnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2JcIiwge1xuICAgIHVybDogXCIvam9icy97am9iaWR9XCIsXG4gICAgYWJzdHJhY3Q6IHRydWUsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnU2luZ2xlSm9iQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuXCIsIHtcbiAgICB1cmw6IFwiXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuLm5vZGVcIiwge1xuICAgI
HVybDogXCIvdmVydGV4L3tub2RlaWR9XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG5vZGU6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi50aW1lbGluZVwiLCB7XG4gICAgdXJsOiBcIi90aW1lbGluZVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7XG4gICAgdXJsOiBcIi97dmVydGV4SWR9XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIHZlcnRleDoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS52ZXJ0ZXguaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIiwge1xuICAgIHVybDogXCIvc3RhdGlzdGljc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnN0YXRpc3RpY3MuaHRtbFwiXG4gIC
AgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IuZXhjZXB0aW9uc1wiLCB7XG4gICAgdXJsOiBcIi9leGNlcHRpb25zXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5jb25maWdcIiwge1xuICAgIHVybDogXCIvY29uZmlnXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuY29uZmlnLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIHJldHVybiAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlKFwiL292ZXJ2aWV3XCIpO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXk
gbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2JzTGFiZWwnLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHRyYW5zY2x1ZGU6IHRydWVcbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6IFwiQFwiXG5cbiAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNs
YXNzPSdnZXRMYWJlbENsYXNzKCknPjxuZy10cmFuc2NsdWRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCJcbiAgXG4gIGxpbms6IChzY29wZSwgZWxlbWVudCwgYXR0cnMpIC0+XG4gICAgc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IC0+XG4gICAgICAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnaW5kaWNhdG9yUHJpbWFyeScsIChKb2JzU2VydmljZSkgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6ICdAJ1xuXG4gIHRlbXBsYXRlOiBcIjxpIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJyAvPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2ZhIGZhLWNpcmNsZSBpbmRpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3RhYmxlUHJvcGVydHknLCAtPlxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOlxuICAgIHZhbHVlOiAnPSdcblxuICB0ZW1wb
GF0ZTogXCI8dGQgdGl0bGU9XFxcInt7dmFsdWUgfHwgJ05vbmUnfX1cXFwiPnt7dmFsdWUgfHwgJ05vbmUnfX08L3RkPlwiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ2JzTGFiZWwnLCBmdW5jdGlvbihKb2JzU2VydmljZSkge1xuICByZXR1cm4ge1xuICAgIHRyYW5zY2x1ZGU6IHRydWUsXG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6IFwiQFwiXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8c3BhbiB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKSc+PG5nLXRyYW5zY2x1ZGU+PC9uZy10cmFuc2NsdWRlPjwvc3Bhbj5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdpbmRpY2F0b3JQcmltYXJ5JywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICBnZXRMYWJlbENsYXNzOiBcIiZcIixcbiAgICAgIHN0YXR1czogJ0AnXG4gICAgfSxcbiAgIC
B0ZW1wbGF0ZTogXCI8aSB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnZmEgZmEtY2lyY2xlIGluZGljYXRvciBpbmRpY2F0b3ItJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0YWJsZVByb3BlcnR5JywgZnVuY3Rpb24oKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgdmFsdWU6ICc9J1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiM
gdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmZpbHRlciBcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCAoYW5ndWxhck1vbWVudENvbmZpZykgLT5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSAtPlxuICAgIHJldHVybiBcIlwiICBpZiB0
eXBlb2YgdmFsdWUgaXMgXCJ1bmRlZmluZWRcIiBvciB2YWx1ZSBpcyBudWxsXG5cbiAgICBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7IHRyaW06IGZhbHNlIH0pXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyXG5cbi5maWx0ZXIgXCJodW1hbml6ZVRhc2tOYW1lXCIsIC0+XG4gICh0ZXh0KSAtPlxuICAgICMgVE9ETzogZXh0ZW5kLi4uIGEgbG90XG4gICAgaWYgdGV4dCB0aGVuIHRleHQucmVwbGFjZShcIiZndDtcIiwgXCI+XCIpLnJlcGxhY2UoXCI8YnIvPlwiLFwiXCIpIGVsc2UgJydcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmZpbHRlcihcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCBmdW5jdGlvbihhbmd1bGFyTW9tZW50Q29uZmlnKSB7XG4gIHZhciBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXI7XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlciA9IGZ1bmN0aW9uKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJ1bmRlZmluZWRcIiB8fCB2YWx1ZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxuICAgIHJldHVybiBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hd
CkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7XG4gICAgICB0cmltOiBmYWxzZVxuICAgIH0pO1xuICB9O1xuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIuJHN0YXRlZnVsID0gYW5ndWxhck1vbWVudENvbmZpZy5zdGF0ZWZ1bEZpbHRlcnM7XG4gIHJldHVybiBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXI7XG59KS5maWx0ZXIoXCJodW1hbml6ZVRhc2tOYW1lXCIsIGZ1bmN0aW9uKCkge1xuICByZXR1cm4gZnVuY3Rpb24odGV4dCkge1xuICAgIGlmICh0ZXh0KSB7XG4gICAgICByZXR1cm4gdGV4dC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIikucmVwbGFjZShcIjxici8+XCIsIFwiXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gJyc7XG4gICAgfVxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZX
B0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLnNlcnZpY2UgJ01haW5TZXJ2aWNlJywgKCRodHRwLCBmbGlua0NvbmZpZywgJHEpIC0+XG4gIGpvYk9ic2VydmVycyA9IFtdXG5cbiAgQGxvYWRDb25maWcgPSAtPlxuICAgIGRlZmVycmVkID0gJHEuZGVmZXIoKVxuXG4gICAgJGh0dHAuZ2V0IGZsaW5rQ29uZmlnLm5ld1NlcnZlciArIFwiL2NvbmZpZ1wiXG4gICAgLnN1Y2Nlc3MgKGRhdGEsIHN0YXR1cywgaGVhZGVycywgY29uZmlnKSAtPlxuICAgICAgZGVmZXJyZWQucmVzb2x2ZShkYXRhKVx
uXG4gICAgZGVmZXJyZWQucHJvbWlzZVxuXG5cbiAgQFxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuc2VydmljZSgnTWFpblNlcnZpY2UnLCBmdW5jdGlvbigkaHR0cCwgZmxpbmtDb25maWcsICRxKSB7XG4gIHZhciBqb2JPYnNlcnZlcnM7XG4gIGpvYk9ic2VydmVycyA9IFtdO1xuICB0aGlzLmxvYWRDb25maWcgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgZGVmZXJyZWQ7XG4gICAgZGVmZXJyZWQgPSAkcS5kZWZlcigpO1xuICAgICRodHRwLmdldChmbGlua0NvbmZpZy5uZXdTZXJ2ZXIgKyBcIi9jb25maWdcIikuc3VjY2VzcyhmdW5jdGlvbihkYXRhLCBzdGF0dXMsIGhlYWRlcnMsIGNvbmZpZykge1xuICAgICAgcmV0dXJuIGRlZmVycmVkLnJlc29sdmUoZGF0YSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIGRlZmVycmVkLnByb21pc2U7XG4gIH07XG4gIHJldHVybiB0aGlzO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAy
LjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmNvbnRyb2xsZXIgJ1J1bm5pbmdKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ3J1bm5pbmcnKVxuXG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb
3BlLmpvYk9ic2VydmVyKVxuICAkc2NvcGUuJG9uICckZGVzdHJveScsIC0+XG4gICAgSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcblxuICAkc2NvcGUuam9iT2JzZXJ2ZXIoKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnU2luZ2xlSm9iQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHJvb3RTY29wZSkgLT5cbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkXG4gICRyb290U2NvcGUuam9iID0gbnVsbFxuXG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYm
lkKS50aGVuIChkYXRhKSAtPlxuICAgICRyb290U2NvcGUuam9iID0gZGF0YVxuXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbkNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmxvYWRQbGFuKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUucGxhbiA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbk5vZGVDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZFxuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KClcblxuICBKb2JzU2VydmljZS5nZXROb2RlKCRzY29wZS5ub2RlaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLm5vZGUgPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiA
gSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy5qb2JpZCwgJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS52ZXJ0ZXggPSBkYXRhXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5jb250cm9sbGVyKCdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJyk7XG4gIH07XG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJyk7XG4gIH07XG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2Jz
ZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdTaW5nbGVKb2JDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRyb290U2NvcGUpIHtcbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkO1xuICAkcm9vdFNjb3BlLmpvYiA9IG51bGw7XG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5qb2IgPSBkYXRhO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRyb290U2NvcGUuam9iID0gbnVsbDtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiBKb2JzU2VydmljZS5sb2FkUGxhbigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkc2NvcGUucGxhbiA9IGRhdGE7XG4gIH0pO1xufSkuY29udHJvbGxlc
ignSm9iUGxhbk5vZGVDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLm5vZGVpZCA9ICRzdGF0ZVBhcmFtcy5ub2RlaWQ7XG4gICRzY29wZS5zdGF0ZUxpc3QgPSBKb2JzU2VydmljZS5zdGF0ZUxpc3QoKTtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5ub2RlID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy5qb2JpZCwgJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlLnZlcnRleCA9IGRhdGE7XG4gIH0pO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRi
BsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3ZlcnRleCcsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2l
kdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCJcblxuICBzY29wZTpcbiAgICBkYXRhOiBcIj1cIlxuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF1cblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVylcblxuICAgIGFuYWx5emVUaW1lID0gKGRhdGEpIC0+XG4gICAgICB0ZXN0RGF0YSA9IFtdXG5cbiAgICAgIGFuZ3VsYXIuZm9yRWFjaCBkYXRhLmdyb3VwdmVydGV4Lmdyb3VwbWVtYmVycywgKHZlcnRleCwgaSkgLT5cbiAgICAgICAgdlRpbWUgPSBkYXRhLnZlcnRpY2V0aW1lc1t2ZXJ0ZXgudmVydGV4aWRdXG5cbiAgICAgICAgdGVzdERhdGEucHVzaCB7XG4gICAgICAgICAgbGFiZWw6IFwiI3t2ZXJ0ZXgudmVydGV4aW5zdGFuY2VuYW1lfSAoI3tpfSlcIlxuICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiM2NjZcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJTQ0hFRFVMRURcIl0gKiAxMDBcbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZUaW1lW1wiREVQTE9ZSU5HXCJdICogMTAwXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1
bGFyJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJEZXBsb3lpbmdcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjYWFhXCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiREVQTE9ZSU5HXCJdICogMTAwXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIlJVTk5JTkdcIl0gKiAxMDBcbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlJ1bm5pbmdcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiUlVOTklOR1wiXSAqIDEwMFxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJGSU5JU0hFRFwiXSAqIDEwMFxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBdXG4gICAgICAgIH1cblxuICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKClcbiAgICAgIC50aWNrRm9ybWF0KHtcbiAgICAgICAgZm9ybWF0OiBkMy50aW1lLmZvcm1hdChcIiVMXCIpXG4gICAgICAgICMgdGlja0ludGVydmFsOiAxXG4gICAgICAgIHRpY2tTaXplOiAxXG4gICAgICB9K
VxuICAgICAgLnByZWZpeChcInNpbmdsZVwiKVxuICAgICAgLmxhYmVsRm9ybWF0KChsYWJlbCkgLT5cbiAgICAgICAgbGFiZWxcbiAgICAgIClcbiAgICAgIC5tYXJnaW4oeyBsZWZ0OiAxMDAsIHJpZ2h0OiAwLCB0b3A6IDAsIGJvdHRvbTogMCB9KVxuICAgICAgLml0ZW1IZWlnaHQoMzApXG4gICAgICAucmVsYXRpdmVUaW1lKClcblxuICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKVxuICAgICAgLmRhdHVtKHRlc3REYXRhKVxuICAgICAgLmNhbGwoY2hhcnQpXG5cbiAgICBhbmFseXplVGltZShzY29wZS5kYXRhKVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0aW1lbGluZScsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGpvYjogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpXG5cbiAgICB0cmFuc2xhdGVMYWJlbCA9IChsYWJlbCkgLT5cbiAgICAgIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YS
kgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgdGVzdERhdGEucHVzaCBcbiAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIlxuICAgICAgICAgIGNvbG9yOiBcIiNjY2NjY2NcIlxuICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEub2xkVltcIlNDSEVEVUxFRFwiXVxuICAgICAgICAgIGVuZGluZ190aW1lOiBkYXRhLm9sZFZbXCJTQ0hFRFVMRURcIl0gKyAxXG4gICAgICAgICAgIyBsaW5rOiB2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhpZFxuICAgICAgICAgIHR5cGU6ICdzY2hlZHVsZWQnXG4gICAgICAgIF1cblxuXG4gICAgICBhbmd1bGFyLmZvckVhY2ggZGF0YS5vbGRWLmdyb3VwdmVydGljZXMsICh2ZXJ0ZXgpIC0+XG4gICAgICAgIHZUaW1lID0gZGF0YS5vbGRWLmdyb3VwdmVydGljZXRpbWVzW3ZlcnRleC5ncm91cHZlcnRleGlkXVxuXG4gICAgICAgICMgY29uc29sZS5sb2cgdlRpbWUsIHZlcnRleC5ncm91cHZlcnRleGlkXG5cbiAgICAgICAgdGVzdERhdGEucHVzaCBcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgbGFiZWw6IHRyYW5zbGF0ZUxhYmVsKHZlcnRleC5ncm91cHZlcnRleG5hbWUpXG4gICAgICAgICAgICBjb2xvcjogXCIjZDlmMWY3XCJcbiAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM2MmNkZWFcIlxuICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJTVEFSVEV
EXCJdXG4gICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJFTkRFRFwiXVxuICAgICAgICAgICAgbGluazogdmVydGV4Lmdyb3VwdmVydGV4aWRcbiAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgIF1cblxuICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKCkuY2xpY2soKGQsIGksIGRhdHVtKSAtPlxuICAgICAgICBpZiBkLmxpbmtcbiAgICAgICAgICAkc3RhdGUuZ28gXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7IGpvYmlkOiBkYXRhLmppZCwgdmVydGV4SWQ6IGQubGluayB9XG5cbiAgICAgIClcbiAgICAgIC50aWNrRm9ybWF0KHtcbiAgICAgICAgZm9ybWF0OiBkMy50aW1lLmZvcm1hdChcIiVMXCIpXG4gICAgICAgICMgdGlja1RpbWU6IGQzLnRpbWUuc2Vjb25kXG4gICAgICAgICMgdGlja0ludGVydmFsOiAwLjVcbiAgICAgICAgdGlja1NpemU6IDFcbiAgICAgIH0pXG4gICAgICAucHJlZml4KFwibWFpblwiKVxuICAgICAgLm1hcmdpbih7IGxlZnQ6IDAsIHJpZ2h0OiAwLCB0b3A6IDAsIGJvdHRvbTogMCB9KVxuICAgICAgLml0ZW1IZWlnaHQoMzApXG4gICAgICAuc2hvd0JvcmRlckxpbmUoKVxuICAgICAgLnNob3dIb3VyVGltZWxpbmUoKVxuXG4gICAgICBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpXG4gICAgICAuZGF0dW0odGVzdERhdGEpXG4gICAgICAuY2FsbChjaGFydClcblxuICAgIHNjb3BlLiR3YXRjaCBhdHRycy5qb2IsIChkYXRhKSAtPlxuICAg
ICAgYW5hbHl6ZVRpbWUoZGF0YSkgaWYgZGF0YVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICdqb2JQbGFuJywgKCR0aW1lb3V0KSAtPlxuICB0ZW1wbGF0ZTogXCJcbiAgICA8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+XG4gICAgPHN2ZyBjbGFzcz0ndG1wJyB3aWR0aD0nMScgaGVpZ2h0PScxJz48ZyAvPjwvc3ZnPlxuICAgIDxkaXYgY2xhc3M9J2J0bi1ncm91cCB6b29tLWJ1dHRvbnMnPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLWluJyBuZy1jbGljaz0nem9vbUluKCknPjxpIGNsYXNzPSdmYSBmYS1wbHVzJyAvPjwvYT5cbiAgICAgIDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+XG4gICAgPC9kaXY+XCJcblxuICBzY29wZTpcbiAgICBwbGFuOiAnPSdcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIG1haW5ab29tID0gZDMuYmVoYXZpb3Iuem9vbSgpXG4gICAgc3ViZ3JhcGhzID0gW11cbiAgICBqb2JpZCA9IGF0dHJzLmpvYmlkXG5cbiAgICBtYWluU3ZnRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuICAgIG1haW5HID0gZWxlbS5jaGlsZHJlbigpLmNoaWxkcmVuKClbMF1cbiAgICBtYWluVG1wR
WxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVsxXVxuXG4gICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1haW5TdmdFbGVtZW50KVxuICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpXG4gICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpXG5cbiAgICAjIGFuZ3VsYXIuZWxlbWVudChtYWluRykuZW1wdHkoKVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpXG5cbiAgICBzY29wZS56b29tSW4gPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKSA8IDIuOTlcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIHpvb20gb2JqZWN0XG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW5ab29tLnNjYWxlIG1haW5ab29tLnNjYWxlKCkgKyAwLjFcbiAgICAgICAgbWFpblpvb20udHJhbnNsYXRlIFsgdjEsIHYyIF1cbiAgICAgICAgXG4gICAgICAgICMgVHJhbnNmb3JtIHN2Z1xuICAgICAgICBkM21haW5TdmdHLmF0dHIgXC
J0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCJcblxuICAgIHNjb3BlLnpvb21PdXQgPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKSA+IDAuMzFcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIG1haW5ab29tIG9iamVjdFxuICAgICAgICBtYWluWm9vbS5zY2FsZSBtYWluWm9vbS5zY2FsZSgpIC0gMC4xXG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZSBbIHYxLCB2MiBdXG4gICAgICAgIFxuICAgICAgICAjIFRyYW5zZm9ybSBzdmdcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiXG5cbiAgICAjY3JlYXRlIGEgbGFiZWwgb2YgYW4gZWRnZVxuICAgIGNyZWF0ZUxhYmVsRWRnZSA9IChlbCkgLT5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIlwiXG4gICAgICBpZiB
lbC5zaGlwX3N0cmF0ZWd5PyBvciBlbC5sb2NhbF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxkaXYgY2xhc3M9J2VkZ2UtbGFiZWwnPlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneSAgaWYgZWwuc2hpcF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiAoXCIgKyBlbC50ZW1wX21vZGUgKyBcIilcIiAgdW5sZXNzIGVsLnRlbXBfbW9kZSBpcyBgdW5kZWZpbmVkYFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiLDxicj5cIiArIGVsLmxvY2FsX3N0cmF0ZWd5ICB1bmxlc3MgZWwubG9jYWxfc3RyYXRlZ3kgaXMgYHVuZGVmaW5lZGBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiXG4gICAgICBsYWJlbFZhbHVlXG5cblxuICAgICMgdHJ1ZSwgaWYgdGhlIG5vZGUgaXMgYSBzcGVjaWFsIG5vZGUgZnJvbSBhbiBpdGVyYXRpb25cbiAgICBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlID0gKGluZm8pIC0+XG4gICAgICAoaW5mbyBpcyBcInBhcnRpYWxTb2x1dGlvblwiIG9yIGluZm8gaXMgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIgb3IgaW5mbyBpcyBcIndvcmtzZXRcIiBvciBpbmZvIGlzIFwibmV4dFdvcmtzZXRcIiBvciBpbmZvIGlzIFwic29sdXRpb25TZXRcIiBvciBpbmZvIGlzIFwic29sdXRpb25EZWx0YVwiKVxuXG4gICAgZ2V0Tm9kZVR5cGUgPSAoZWwsIGluZm8pIC0+XG4gICAgICBpZiBpbmZvIGlzIFwibWlycm9yXCJcbiAgICAgICAgJ25v
ZGUtbWlycm9yJ1xuXG4gICAgICBlbHNlIGlmIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbylcbiAgICAgICAgJ25vZGUtaXRlcmF0aW9uJ1xuXG4gICAgICBlbHNlXG4gICAgICAgICAgJ25vZGUtbm9ybWFsJ1xuICAgICAgXG4gICAgIyBjcmVhdGVzIHRoZSBsYWJlbCBvZiBhIG5vZGUsIGluIGluZm8gaXMgc3RvcmVkLCB3aGV0aGVyIGl0IGlzIGEgc3BlY2lhbCBub2RlIChsaWtlIGEgbWlycm9yIGluIGFuIGl0ZXJhdGlvbilcbiAgICBjcmVhdGVMYWJlbE5vZGUgPSAoZWwsIGluZm8sIG1heFcsIG1heEgpIC0+XG4gICAgICBsYWJlbFZhbHVlID0gXCI8YSBocmVmPScjL2pvYnMvXCIgKyBqb2JpZCArIFwiL3ZlcnRleC9cIiArIGVsLmlkICsgXCInIGNsYXNzPSdub2RlLWxhYmVsIFwiICsgZ2V0Tm9kZVR5cGUoZWwsIGluZm8pICsgXCInPlwiXG5cbiAgICAgICMgTm9kZW5hbWVcbiAgICAgIGlmIGluZm8gaXMgXCJtaXJyb3JcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPk1pcnJvciBvZiBcIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiXG4gICAgICBlbHNlXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+XCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIlxuICAgICAgaWYgZWwuZGVzY3JpcHRpb24gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiXCJcbiAgICAgIGVsc2VcbiAgICAgICAgc3RlcE5hbWUgPSBlbC5kZ
XNjcmlwdGlvblxuICAgICAgICBcbiAgICAgICAgIyBjbGVhbiBzdGVwTmFtZVxuICAgICAgICBzdGVwTmFtZSA9IHNob3J0ZW5TdHJpbmcoc3RlcE5hbWUpXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDQgY2xhc3M9J3N0ZXAtbmFtZSc+XCIgKyBzdGVwTmFtZSArIFwiPC9oND5cIlxuICAgICAgXG4gICAgICAjIElmIHRoaXMgbm9kZSBpcyBhbiBcIml0ZXJhdGlvblwiIHdlIG5lZWQgYSBkaWZmZXJlbnQgcGFuZWwtYm9keVxuICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvbj9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24oZWwuaWQsIG1heFcsIG1heEgpXG4gICAgICBlbHNlXG4gICAgICAgIFxuICAgICAgICAjIE90aGVyd2lzZSBhZGQgaW5mb3MgICAgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+XCIgKyBpbmZvICsgXCIgTm9kZTwvaDU+XCIgIGlmIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbylcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5QYXJhbGxlbGlzbTogXCIgKyBlbC5wYXJhbGxlbGlzbSArIFwiPC9oNT5cIiAgdW5sZXNzIGVsLnBhcmFsbGVsaXNtIGlzIFwiXCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5PcGVyYXRpb246IFwiICsgc2hvcnRlblN0cmluZyhlbC5vcGVyYXRvcl9zdHJhdGVneSkgKyBcIjwvaDU+XCIgIHVubGVzcyBlbC5vcGVyYXRvciBpcyBgdW5kZWZpbmVkYFxuICAgICAgXG4gICAgICBsYWJlbFZhbHVlIC
s9IFwiPC9hPlwiXG4gICAgICBsYWJlbFZhbHVlXG5cbiAgICAjIEV4dGVuZHMgdGhlIGxhYmVsIG9mIGEgbm9kZSB3aXRoIGFuIGFkZGl0aW9uYWwgc3ZnIEVsZW1lbnQgdG8gcHJlc2VudCB0aGUgaXRlcmF0aW9uLlxuICAgIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiA9IChpZCwgbWF4VywgbWF4SCkgLT5cbiAgICAgIHN2Z0lEID0gXCJzdmctXCIgKyBpZFxuXG4gICAgICBsYWJlbFZhbHVlID0gXCI8c3ZnIGNsYXNzPSdcIiArIHN2Z0lEICsgXCInIHdpZHRoPVwiICsgbWF4VyArIFwiIGhlaWdodD1cIiArIG1heEggKyBcIj48ZyAvPjwvc3ZnPlwiXG4gICAgICBsYWJlbFZhbHVlXG5cbiAgICAjIFNwbGl0IGEgc3RyaW5nIGludG8gbXVsdGlwbGUgbGluZXMgc28gdGhhdCBlYWNoIGxpbmUgaGFzIGxlc3MgdGhhbiAzMCBsZXR0ZXJzLlxuICAgIHNob3J0ZW5TdHJpbmcgPSAocykgLT5cbiAgICAgICMgbWFrZSBzdXJlIHRoYXQgbmFtZSBkb2VzIG5vdCBjb250YWluIGEgPCAoYmVjYXVzZSBvZiBodG1sKVxuICAgICAgaWYgcy5jaGFyQXQoMCkgaXMgXCI8XCJcbiAgICAgICAgcyA9IHMucmVwbGFjZShcIjxcIiwgXCImbHQ7XCIpXG4gICAgICAgIHMgPSBzLnJlcGxhY2UoXCI+XCIsIFwiJmd0O1wiKVxuICAgICAgc2JyID0gXCJcIlxuICAgICAgd2hpbGUgcy5sZW5ndGggPiAzMFxuICAgICAgICBzYnIgPSBzYnIgKyBzLnN1YnN0cmluZygwLCAzMCkgKyBcIjxicj5cIlxuICAgICAgICBzID0gcy5zdWJzdHJ
pbmcoMzAsIHMubGVuZ3RoKVxuICAgICAgc2JyID0gc2JyICsgc1xuICAgICAgc2JyXG5cbiAgICBjcmVhdGVOb2RlID0gKGcsIGRhdGEsIGVsLCBpc1BhcmVudCA9IGZhbHNlLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgIyBjcmVhdGUgbm9kZSwgc2VuZCBhZGRpdGlvbmFsIGluZm9ybWF0aW9ucyBhYm91dCB0aGUgbm9kZSBpZiBpdCBpcyBhIHNwZWNpYWwgb25lXG4gICAgICBpZiBlbC5pZCBpcyBkYXRhLnBhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5uZXh0X3BhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLndvcmtzZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxh
YmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwid29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIndvcmtzZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLm5leHRfd29ya3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0V29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRXb3Jrc2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5zb2x1dGlvbl9zZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25TZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvblNldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEuc29sdXRpb25fZGVsdGFcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25EZWx0YVwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6I
GdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uRGVsdGFcIilcblxuICAgICAgZWxzZVxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJcIilcblxuICAgIGNyZWF0ZUVkZ2UgPSAoZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpIC0+XG4gICAgICB1bmxlc3MgZXhpc3RpbmdOb2Rlcy5pbmRleE9mKHByZWQuaWQpIGlzIC0xXG4gICAgICAgIGcuc2V0RWRnZSBwcmVkLmlkLCBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKHByZWQpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBhcnJvd2hlYWQ6ICdub3JtYWwnXG5cbiAgICAgIGVsc2VcbiAgICAgICAgbWlzc2luZ05vZGUgPSBzZWFyY2hGb3JOb2RlKGRhdGEsIHByZWQuaWQpXG4gICAgICAgIHVubGVzcyAhbWlzc2luZ05vZGUgb3IgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkIGlzIHRydWVcbiAgICAgICAgICBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgPSB0cnVlXG4gICAgICAgICAgZy5zZXROb2RlIG1pc3NpbmdOb2RlLmlkLFxuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShtaXNzaW5nTm9kZSwgXCJtaXJyb3JcIilcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG
4gICAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUobWlzc2luZ05vZGUsICdtaXJyb3InKVxuXG4gICAgICAgICAgZy5zZXRFZGdlIG1pc3NpbmdOb2RlLmlkLCBlbC5pZCxcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UobWlzc2luZ05vZGUpXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuXG4gICAgbG9hZEpzb25Ub0RhZ3JlID0gKGcsIGRhdGEpIC0+XG4gICAgICBleGlzdGluZ05vZGVzID0gW11cblxuICAgICAgaWYgZGF0YS5ub2Rlcz9cbiAgICAgICAgIyBUaGlzIGlzIHRoZSBub3JtYWwganNvbiBkYXRhXG4gICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEubm9kZXNcblxuICAgICAgZWxzZVxuICAgICAgICAjIFRoaXMgaXMgYW4gaXRlcmF0aW9uLCB3ZSBub3cgc3RvcmUgc3BlY2lhbCBpdGVyYXRpb24gbm9kZXMgaWYgcG9zc2libGVcbiAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5zdGVwX2Z1bmN0aW9uXG4gICAgICAgIGlzUGFyZW50ID0gdHJ1ZVxuXG4gICAgICBmb3IgZWwgaW4gdG9JdGVyYXRlXG4gICAgICAgIG1heFcgPSAwXG4gICAgICAgIG1heEggPSAwXG5cbiAgICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvblxuICAgICAgICAgIHNnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoeyBtdWx0aWdyYXBoOiB0cnVlLCBjb21wb3VuZDogdHJ1ZSB9KS5zZXRHcmFwaCh7XG4gICAgICAgICAgICBub2Rlc2VwOiAyMFxuICAgICAgICAgICAgZWRnZXNlcDogMFxuICAgICAgICA
gICAgcmFua3NlcDogMjBcbiAgICAgICAgICAgIHJhbmtkaXI6IFwiTFJcIlxuICAgICAgICAgICAgbWFyZ2lueDogMTBcbiAgICAgICAgICAgIG1hcmdpbnk6IDEwXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgICAgc3ViZ3JhcGhzW2VsLmlkXSA9IHNnXG5cbiAgICAgICAgICBsb2FkSnNvblRvRGFncmUoc2csIGVsKVxuXG4gICAgICAgICAgciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpXG4gICAgICAgICAgZDN0bXBTdmcuc2VsZWN0KCdnJykuY2FsbChyLCBzZylcbiAgICAgICAgICBtYXhXID0gc2cuZ3JhcGgoKS53aWR0aFxuICAgICAgICAgIG1heEggPSBzZy5ncmFwaCgpLmhlaWdodFxuXG4gICAgICAgICAgYW5ndWxhci5lbGVtZW50KG1haW5UbXBFbGVtZW50KS5lbXB0eSgpXG5cbiAgICAgICAgY3JlYXRlTm9kZShnLCBkYXRhLCBlbCwgaXNQYXJlbnQsIG1heFcsIG1heEgpXG5cbiAgICAgICAgZXhpc3RpbmdOb2Rlcy5wdXNoIGVsLmlkXG4gICAgICAgIFxuICAgICAgICAjIGNyZWF0ZSBlZGdlcyBmcm9tIGlucHV0cyB0byBjdXJyZW50IG5vZGVcbiAgICAgICAgaWYgZWwuaW5wdXRzP1xuICAgICAgICAgIGZvciBwcmVkIGluIGVsLmlucHV0c1xuICAgICAgICAgICAgY3JlYXRlRWRnZShnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZClcblxuICAgICAgZ1xuXG4gICAgIyBzZWFyY2hlcyBpbiB0aGUgZ2xvYmFsIEpTT05EYXRhIGZvciB0aGUgbm9kZSB3aXRoIHRoZSBnaXZlbiBpZFxuICAgIHNl
YXJjaEZvck5vZGUgPSAoZGF0YSwgbm9kZUlEKSAtPlxuICAgICAgZm9yIGkgb2YgZGF0YS5ub2Rlc1xuICAgICAgICBlbCA9IGRhdGEubm9kZXNbaV1cbiAgICAgICAgcmV0dXJuIGVsICBpZiBlbC5pZCBpcyBub2RlSURcbiAgICAgICAgXG4gICAgICAgICMgbG9vayBmb3Igbm9kZXMgdGhhdCBhcmUgaW4gaXRlcmF0aW9uc1xuICAgICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICAgIGZvciBqIG9mIGVsLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgICAgIHJldHVybiBlbC5zdGVwX2Z1bmN0aW9uW2pdICBpZiBlbC5zdGVwX2Z1bmN0aW9uW2pdLmlkIGlzIG5vZGVJRFxuXG4gICAgZHJhd0dyYXBoID0gKGRhdGEpIC0+XG4gICAgICBnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoeyBtdWx0aWdyYXBoOiB0cnVlLCBjb21wb3VuZDogdHJ1ZSB9KS5zZXRHcmFwaCh7XG4gICAgICAgIG5vZGVzZXA6IDcwXG4gICAgICAgIGVkZ2VzZXA6IDBcbiAgICAgICAgcmFua3NlcDogNTBcbiAgICAgICAgcmFua2RpcjogXCJMUlwiXG4gICAgICAgIG1hcmdpbng6IDQwXG4gICAgICAgIG1hcmdpbnk6IDQwXG4gICAgICAgIH0pXG5cbiAgICAgIGxvYWRKc29uVG9EYWdyZShnLCBkYXRhKVxuXG4gICAgICByZW5kZXJlciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpXG4gICAgICBkM21haW5TdmdHLmNhbGwocmVuZGVyZXIsIGcpXG5cbiAgICAgIGZvciBpLCBzZyBvZiBzdWJncmFwaHNcbiAgICAgICAgZDNtYWluU3ZnL
nNlbGVjdCgnc3ZnLnN2Zy0nICsgaSArICcgZycpLmNhbGwocmVuZGVyZXIsIHNnKVxuXG4gICAgICBuZXdTY2FsZSA9IDAuNVxuXG4gICAgICB4Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS53aWR0aCgpIC0gZy5ncmFwaCgpLndpZHRoICogbmV3U2NhbGUpIC8gMilcbiAgICAgIHlDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLmhlaWdodCgpIC0gZy5ncmFwaCgpLmhlaWdodCAqIG5ld1NjYWxlKSAvIDIpXG5cbiAgICAgIG1haW5ab29tLnNjYWxlKG5ld1NjYWxlKS50cmFuc2xhdGUoW3hDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXRdKVxuXG4gICAgICBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB4Q2VudGVyT2Zmc2V0ICsgXCIsIFwiICsgeUNlbnRlck9mZnNldCArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIilcblxuICAgICAgbWFpblpvb20ub24oXCJ6b29tXCIsIC0+XG4gICAgICAgIGV2ID0gZDMuZXZlbnRcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgZXYudHJhbnNsYXRlICsgXCIpIHNjYWxlKFwiICsgZXYuc2NhbGUgKyBcIilcIlxuICAgICAgKVxuICAgICAgbWFpblpvb20oZDNtYWluU3ZnKVxuXG4gICAgc2NvcGUuJHdhdGNoIGF0dHJzLnBsYW4sIChuZXdQbG
FuKSAtPlxuICAgICAgZHJhd0dyYXBoKG5ld1BsYW4pIGlmIG5ld1BsYW5cblxuICAgIHJldHVyblxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZGlyZWN0aXZlKCd2ZXJ0ZXgnLCBmdW5jdGlvbigkc3RhdGUpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZSBzZWNvbmRhcnknIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBkYXRhOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5lclcsIHN2Z0VsO1xuICAgICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpO1xuICAgICAgYW5hbHl6ZVRpbWUgPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIHZhciBjaGFydCwgc3ZnLCB0ZXN0RGF0YTtcbiAgICAgICAgdGVzdERhdGEgPSBbXTtcbiAgICAgICAgYW5ndWxhci5mb3JFYWNoKGRhdGEuZ3JvdXB2ZXJ0ZXguZ3JvdXBtZW1iZXJzLCBmdW5jdGlvbih2ZXJ0ZXgsIGkpIHtcbiAgICAgICAgICB2YXIgdlRpbWU7XG4gICAgICAgICAgdlRpbWUgPSBkYXRhLnZlcnRpY2V0aW1lc1t2ZXJ0ZXgudmVydGV4aWRdO1xuICAgICAgICAgIHJldHVybiB0ZXN0RGF
0YS5wdXNoKHtcbiAgICAgICAgICAgIGxhYmVsOiB2ZXJ0ZXgudmVydGV4aW5zdGFuY2VuYW1lICsgXCIgKFwiICsgaSArIFwiKVwiLFxuICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiM2NjZcIixcbiAgICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJTQ0hFRFVMRURcIl0gKiAxMDAsXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZUaW1lW1wiREVQTE9ZSU5HXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiRGVwbG95aW5nXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiI2FhYVwiLFxuICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIkRFUExPWUlOR1wiXSAqIDEwMCxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJSVU5OSU5HXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiUnVubmluZ1wiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiNk
ZGRcIixcbiAgICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJSVU5OSU5HXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkZJTklTSEVEXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICBdXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS50aWNrRm9ybWF0KHtcbiAgICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIiksXG4gICAgICAgICAgdGlja1NpemU6IDFcbiAgICAgICAgfSkucHJlZml4KFwic2luZ2xlXCIpLmxhYmVsRm9ybWF0KGZ1bmN0aW9uKGxhYmVsKSB7XG4gICAgICAgICAgcmV0dXJuIGxhYmVsO1xuICAgICAgICB9KS5tYXJnaW4oe1xuICAgICAgICAgIGxlZnQ6IDEwMCxcbiAgICAgICAgICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMzApLnJlbGF0aXZlVGltZSgpO1xuICAgICAgICByZXR1cm4gc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCk7XG4gICAgICB9O1xuICAgICAgYW5hbHl6ZVRpbWUoc2NvcGUuZGF0YSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0aW1lb
GluZScsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIixcbiAgICBzY29wZToge1xuICAgICAgam9iOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5lclcsIHN2Z0VsLCB0cmFuc2xhdGVMYWJlbDtcbiAgICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKTtcbiAgICAgIHRyYW5zbGF0ZUxhYmVsID0gZnVuY3Rpb24obGFiZWwpIHtcbiAgICAgICAgcmV0dXJuIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKTtcbiAgICAgIH07XG4gICAgICBhbmFseXplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGNoYXJ0LCBzdmcsIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIixcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2NjY2NjY1wiLFxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgIC
AgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEub2xkVltcIlNDSEVEVUxFRFwiXSxcbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IGRhdGEub2xkVltcIlNDSEVEVUxFRFwiXSArIDEsXG4gICAgICAgICAgICAgIHR5cGU6ICdzY2hlZHVsZWQnXG4gICAgICAgICAgICB9XG4gICAgICAgICAgXVxuICAgICAgICB9KTtcbiAgICAgICAgYW5ndWxhci5mb3JFYWNoKGRhdGEub2xkVi5ncm91cHZlcnRpY2VzLCBmdW5jdGlvbih2ZXJ0ZXgpIHtcbiAgICAgICAgICB2YXIgdlRpbWU7XG4gICAgICAgICAgdlRpbWUgPSBkYXRhLm9sZFYuZ3JvdXB2ZXJ0aWNldGltZXNbdmVydGV4Lmdyb3VwdmVydGV4aWRdO1xuICAgICAgICAgIHJldHVybiB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogdHJhbnNsYXRlTGFiZWwodmVydGV4Lmdyb3VwdmVydGV4bmFtZSksXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiI2Q5ZjFmN1wiLFxuICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM2MmNkZWFcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIlNUQVJURURcIl0sXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZUaW1lW1wiRU5ERURcIl0sXG4gICAgICAgICAgICAgICAgbGluazogdmVydGV4Lmdyb3VwdmVydGV4aWQsXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICA
gICAgIH1cbiAgICAgICAgICAgIF1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLmNsaWNrKGZ1bmN0aW9uKGQsIGksIGRhdHVtKSB7XG4gICAgICAgICAgaWYgKGQubGluaykge1xuICAgICAgICAgICAgcmV0dXJuICRzdGF0ZS5nbyhcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHtcbiAgICAgICAgICAgICAgam9iaWQ6IGRhdGEuamlkLFxuICAgICAgICAgICAgICB2ZXJ0ZXhJZDogZC5saW5rXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pLnRpY2tGb3JtYXQoe1xuICAgICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKSxcbiAgICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgICB9KS5wcmVmaXgoXCJtYWluXCIpLm1hcmdpbih7XG4gICAgICAgICAgbGVmdDogMCxcbiAgICAgICAgICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMzApLnNob3dCb3JkZXJMaW5lKCkuc2hvd0hvdXJUaW1lbGluZSgpO1xuICAgICAgICByZXR1cm4gc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCk7XG4gICAgICB9O1xuICAgICAgc2NvcGUuJHdhdGNoKGF0dHJzLmpvYiwgZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgIHJldHVybiBhbmFseXpl
VGltZShkYXRhKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdqb2JQbGFuJywgZnVuY3Rpb24oJHRpbWVvdXQpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+IDxzdmcgY2xhc3M9J3RtcCcgd2lkdGg9JzEnIGhlaWdodD0nMSc+PGcgLz48L3N2Zz4gPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1pbicgbmctY2xpY2s9J3pvb21JbigpJz48aSBjbGFzcz0nZmEgZmEtcGx1cycgLz48L2E+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+IDwvZGl2PlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBwbGFuOiAnPSdcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGNvbnRhaW5lclcsIGNyZWF0ZUVkZ2UsIGNyZWF0ZUxhYmVsRWRnZSwgY3JlYXRlTGFiZWxOb2RlLCBjcmVhdGVOb2RlLCBkM21haW5TdmcsIGQzbWFpblN2Z0csIGQzdG1wU3ZnLCBkcmF3R3JhcGgsIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiwgZ2V0Tm9kZVR5cGUsIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUsIGpvYmlkLCBsb2FkSnNvblRvRGFncmUsIG1haW5HL
CBtYWluU3ZnRWxlbWVudCwgbWFpblRtcEVsZW1lbnQsIG1haW5ab29tLCBzZWFyY2hGb3JOb2RlLCBzaG9ydGVuU3RyaW5nLCBzdWJncmFwaHM7XG4gICAgICBtYWluWm9vbSA9IGQzLmJlaGF2aW9yLnpvb20oKTtcbiAgICAgIHN1YmdyYXBocyA9IFtdO1xuICAgICAgam9iaWQgPSBhdHRycy5qb2JpZDtcbiAgICAgIG1haW5TdmdFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXTtcbiAgICAgIG1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdO1xuICAgICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1haW5TdmdFbGVtZW50KTtcbiAgICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpO1xuICAgICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpO1xuICAgICAgc2NvcGUuem9vbUluID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB0cmFuc2xhdGUsIHYxLCB2MjtcbiAgICAgICAgaWYgKG1haW5ab29tLnNjYWxlKCkgPCAyLjk5KSB7XG4gICAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKCk7XG4gICAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm
9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgbWFpblpvb20uc2NhbGUobWFpblpvb20uc2NhbGUoKSArIDAuMSk7XG4gICAgICAgICAgbWFpblpvb20udHJhbnNsYXRlKFt2MSwgdjJdKTtcbiAgICAgICAgICByZXR1cm4gZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIHNjb3BlLnpvb21PdXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHRyYW5zbGF0ZSwgdjEsIHYyO1xuICAgICAgICBpZiAobWFpblpvb20uc2NhbGUoKSA+IDAuMzEpIHtcbiAgICAgICAgICBtYWluWm9vbS5zY2FsZShtYWluWm9vbS5zY2FsZSgpIC0gMC4xKTtcbiAgICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKTtcbiAgICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUoW3YxLCB2Ml0pO1xuICAgICAgICAgIHJldHV
ybiBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlTGFiZWxFZGdlID0gZnVuY3Rpb24oZWwpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWU7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIlwiO1xuICAgICAgICBpZiAoKGVsLnNoaXBfc3RyYXRlZ3kgIT0gbnVsbCkgfHwgKGVsLmxvY2FsX3N0cmF0ZWd5ICE9IG51bGwpKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxkaXYgY2xhc3M9J2VkZ2UtbGFiZWwnPlwiO1xuICAgICAgICAgIGlmIChlbC5zaGlwX3N0cmF0ZWd5ICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLnRlbXBfbW9kZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiIChcIiArIGVsLnRlbXBfbW9kZSArIFwiKVwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwubG9jYWxfc3RyYXRlZ3kgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiw8YnI+XCIgKyBlbC5sb2NhbF9zdHJhdGVneTtcbiAgICAgICAgICB9XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiO1xuICAgICAgICB9XG4gICAgICAg
IHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUgPSBmdW5jdGlvbihpbmZvKSB7XG4gICAgICAgIHJldHVybiBpbmZvID09PSBcInBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwibmV4dFBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwid29ya3NldFwiIHx8IGluZm8gPT09IFwibmV4dFdvcmtzZXRcIiB8fCBpbmZvID09PSBcInNvbHV0aW9uU2V0XCIgfHwgaW5mbyA9PT0gXCJzb2x1dGlvbkRlbHRhXCI7XG4gICAgICB9O1xuICAgICAgZ2V0Tm9kZVR5cGUgPSBmdW5jdGlvbihlbCwgaW5mbykge1xuICAgICAgICBpZiAoaW5mbyA9PT0gXCJtaXJyb3JcIikge1xuICAgICAgICAgIHJldHVybiAnbm9kZS1taXJyb3InO1xuICAgICAgICB9IGVsc2UgaWYgKGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbykpIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtaXRlcmF0aW9uJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtbm9ybWFsJztcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNyZWF0ZUxhYmVsTm9kZSA9IGZ1bmN0aW9uKGVsLCBpbmZvLCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIHZhciBsYWJlbFZhbHVlLCBzdGVwTmFtZTtcbiAgICAgICAgbGFiZWxWYWx1ZSA9IFwiPGEgaHJlZj0nIy9qb2JzL1wiICsgam9iaWQgKyBcIi92ZXJ0ZXgvXCIgKyBlbC5pZCArIFwiJyBjbGFzcz0nbm9kZS1sYWJlbCBcIiArI
GdldE5vZGVUeXBlKGVsLCBpbmZvKSArIFwiJz5cIjtcbiAgICAgICAgaWYgKGluZm8gPT09IFwibWlycm9yXCIpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPk1pcnJvciBvZiBcIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+XCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuZGVzY3JpcHRpb24gPT09IFwiXCIpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiXCI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc3RlcE5hbWUgPSBlbC5kZXNjcmlwdGlvbjtcbiAgICAgICAgICBzdGVwTmFtZSA9IHNob3J0ZW5TdHJpbmcoc3RlcE5hbWUpO1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDQgY2xhc3M9J3N0ZXAtbmFtZSc+XCIgKyBzdGVwTmFtZSArIFwiPC9oND5cIjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuc3RlcF9mdW5jdGlvbiAhPSBudWxsKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24oZWwuaWQsIG1heFcsIG1heEgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmIChpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlwiICsgaW5mby
ArIFwiIE5vZGU8L2g1PlwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwucGFyYWxsZWxpc20gIT09IFwiXCIpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+UGFyYWxsZWxpc206IFwiICsgZWwucGFyYWxsZWxpc20gKyBcIjwvaDU+XCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5vcGVyYXRvciAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1Pk9wZXJhdGlvbjogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLm9wZXJhdG9yX3N0cmF0ZWd5KSArIFwiPC9oNT5cIjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvYT5cIjtcbiAgICAgICAgcmV0dXJuIGxhYmVsVmFsdWU7XG4gICAgICB9O1xuICAgICAgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uID0gZnVuY3Rpb24oaWQsIG1heFcsIG1heEgpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWUsIHN2Z0lEO1xuICAgICAgICBzdmdJRCA9IFwic3ZnLVwiICsgaWQ7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIjxzdmcgY2xhc3M9J1wiICsgc3ZnSUQgKyBcIicgd2lkdGg9XCIgKyBtYXhXICsgXCIgaGVpZ2h0PVwiICsgbWF4SCArIFwiPjxnIC8+PC9zdmc+XCI7XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIHNob3J0ZW5TdHJpbmcgPSBmdW5jdGlvbihzKSB7XG4gICAgICAgIHZhciBzYnI7XG4gICAgICAgIGlmICh
zLmNoYXJBdCgwKSA9PT0gXCI8XCIpIHtcbiAgICAgICAgICBzID0gcy5yZXBsYWNlKFwiPFwiLCBcIiZsdDtcIik7XG4gICAgICAgICAgcyA9IHMucmVwbGFjZShcIj5cIiwgXCImZ3Q7XCIpO1xuICAgICAgICB9XG4gICAgICAgIHNiciA9IFwiXCI7XG4gICAgICAgIHdoaWxlIChzLmxlbmd0aCA+IDMwKSB7XG4gICAgICAgICAgc2JyID0gc2JyICsgcy5zdWJzdHJpbmcoMCwgMzApICsgXCI8YnI+XCI7XG4gICAgICAgICAgcyA9IHMuc3Vic3RyaW5nKDMwLCBzLmxlbmd0aCk7XG4gICAgICAgIH1cbiAgICAgICAgc2JyID0gc2JyICsgcztcbiAgICAgICAgcmV0dXJuIHNicjtcbiAgICAgIH07XG4gICAgICBjcmVhdGVOb2RlID0gZnVuY3Rpb24oZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIGlmIChpc1BhcmVudCA9PSBudWxsKSB7XG4gICAgICAgICAgaXNQYXJlbnQgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuaWQgPT09IGRhdGEucGFydGlhbF9zb2x1dGlvbikge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiKVxuICAgICAgICAgIH0pO1xuICAg
ICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLm5leHRfcGFydGlhbF9zb2x1dGlvbikge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEud29ya3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwid29ya3NldFwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJ3b3Jrc2V0XCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEubmV4dF93b3Jrc2V0KSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0V29ya3NldFwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZ
XROb2RlVHlwZShlbCwgXCJuZXh0V29ya3NldFwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLnNvbHV0aW9uX3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25TZXRcIiwgbWF4Vyw
<TRUNCATED>
[19/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Add
view for exceptions
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index 8605702..2f5646e 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -91,7 +91,8 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
url: "/exceptions",
views: {
details: {
- templateUrl: "partials/jobs/job.exceptions.html"
+ templateUrl: "partials/jobs/job.exceptions.html",
+ controller: 'JobExceptionsController'
}
}
}).state("single-job.config", {
@@ -221,6 +222,10 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
return JobsService.getVertex($stateParams.vertexId).then(function(data) {
return $scope.vertex = data;
});
+}]).controller('JobExceptionsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
+ return JobsService.loadExceptions().then(function(data) {
+ return $scope.exceptions = data;
+ });
}]);
angular.module('flinkApp').directive('vertex', ["$state", function($state) {
@@ -732,11 +737,8 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
data = angular.extend(data, vertices);
return $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/config").success(function(jobConfig) {
data = angular.extend(data, jobConfig);
- return $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/exceptions").success(function(exceptions) {
- data.exceptions = exceptions;
- currentJob = data;
- return deferreds.job.resolve(data);
- });
+ currentJob = data;
+ return deferreds.job.resolve(data);
});
});
});
@@ -806,6 +808,19 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
})(this));
return deferred.promise;
};
+ this.loadExceptions = function() {
+ var deferred;
+ deferred = $q.defer();
+ $q.all([deferreds.job.promise]).then((function(_this) {
+ return function(data) {
+ return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/exceptions").success(function(exceptions) {
+ currentJob.exceptions = exceptions;
+ return deferred.resolve(exceptions);
+ });
+ };
+ })(this));
+ return deferred.promise;
+ };
return this;
}]);
@@ -848,4 +863,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsImNvbW1vbi9zZXJ2aWNlcy5jb2ZmZWUiLCJjb21tb24vc2VydmljZXMuanMiLCJtb2R1bGVzL2pvYnMvam9icy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuanMiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuZGlyLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLnN2Yy5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LmN0cmwuanMiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkEsUUFBUSxPQUFPLFlBQVksQ0FBQyxhQUFhLGtCQUl4QyxtQkFBSSxTQUFDLFlBQUQ7RUFDSCxXQUFXLGlCQUFpQjtFQ3JCNUIsT0RzQkEsV0FBVyxjQUFjLFdBQUE7SUFDdkIsV0FBVyxpQkFBaUIsQ0FBQyxXQUFXO0lDckJ4QyxPRHNCQSxXQUFXLGVBQWU7O0lBSTdCLE1BQU0sZUFBZTtFQUN
wQixXQUFXO0VBRVgsb0JBQW9CO0dBS3JCLCtEQUFJLFNBQUMsYUFBYSxhQUFhLGFBQWEsV0FBeEM7RUM1QkgsT0Q2QkEsWUFBWSxhQUFhLEtBQUssU0FBQyxRQUFEO0lBQzVCLFFBQVEsT0FBTyxhQUFhO0lBRTVCLFlBQVk7SUM3QlosT0QrQkEsVUFBVSxXQUFBO01DOUJSLE9EK0JBLFlBQVk7T0FDWixZQUFZOztJQUtqQixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sd0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sdUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhOzs7S0FFbEIsTUFBTSw4QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsUUFDRTtRQUFBLGFB
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
LElBQUE7UUFBQSxZQUFZLFNBQVMsUUFBUSxZQUFZO1FBSXpDLFVBQVUsU0FBUyxNQUFDLFdBQVc7UUN4QjdCLE9EMEJGLFNBQVMsUUFBUTs7T0FQMEM7SUNoQjdELE9EeUJBLFNBQVM7O0VBRVgsS0FBQyxhQUFhLFNBQUMsUUFBRDtJQUNaLElBQUEsR0FBQSxLQUFBLEtBQUE7SUFBQSxNQUFBLFdBQUE7SUFBQSxLQUFBLElBQUEsR0FBQSxNQUFBLElBQUEsUUFBQSxJQUFBLEtBQUEsS0FBQTtNQ3RCRSxTQUFTLElBQUk7TUR1QmIsSUFBaUIsT0FBTyxPQUFNLFFBQTlCO1FBQUEsT0FBTzs7O0lBRVQsT0FBTzs7RUFHVCxLQUFDLFlBQVksU0FBQyxVQUFEO0lBQ1gsSUFBQTtJQUFBLFdBQVcsR0FBRztJQUVkLEdBQUcsSUFBSSxDQUFDLFVBQVUsSUFBSSxVQUFVLEtBQUssQ0FBQSxTQUFBLE9BQUE7TUNwQm5DLE9Eb0JtQyxTQUFDLE1BQUQ7UUFDbkMsSUFBQTtRQUFBLFNBQVMsTUFBQyxXQUFXO1FDbEJuQixPRG9CRixNQUFNLElBQUksWUFBWSxZQUFZLFdBQVcsV0FBVyxNQUFNLGVBQWUsV0FBVyxpQkFDdkYsUUFBUSxTQUFDLE1BQUQ7VUFDUCxPQUFPLFdBQVcsS0FBSztVQ3BCckIsT0RzQkYsU0FBUyxRQUFROzs7T0FQZ0I7SUNYckMsT0RvQkEsU0FBUzs7RUNsQlgsT0RvQkE7O0FDbEJGO0FDL0lBLFFBQVEsT0FBTyxZQUVkLFdBQVcsK0ZBQXNCLFNBQUMsUUFBUSxpQkFBaUIsYUFBYSxXQUFXLGFBQWxEO0VBQ2hDLElBQUE7RUFBQSxPQUFPLGNBQWMsV0FBQTtJQUNuQixPQUFPLGNBQWMsWUFBWSxRQUFRO0lDbEJ6QyxPR
G1CQSxPQUFPLGVBQWUsWUFBWSxRQUFROztFQUU1QyxZQUFZLGlCQUFpQixPQUFPO0VBQ3BDLE9BQU8sSUFBSSxZQUFZLFdBQUE7SUNsQnJCLE9EbUJBLFlBQVksbUJBQW1CLE9BQU87O0VBRXhDLE9BQU87RUFFUCxnQkFBZ0IsZUFBZSxLQUFLLFNBQUMsTUFBRDtJQ25CbEMsT0RvQkEsT0FBTyxXQUFXOztFQUVwQixVQUFVLFVBQVUsV0FBQTtJQ25CbEIsT0RvQkEsZ0JBQWdCLGVBQWUsS0FBSyxTQUFDLE1BQUQ7TUNuQmxDLE9Eb0JBLE9BQU8sV0FBVzs7S0FDcEIsWUFBWTtFQ2xCZCxPRG9CQSxPQUFPLElBQUksWUFBWSxXQUFBO0lDbkJyQixPRG9CQSxVQUFVLE9BQU87OztBQ2pCckI7QUNMQSxRQUFRLE9BQU8sWUFFZCxRQUFRLGtEQUFtQixTQUFDLE9BQU8sYUFBYSxJQUFyQjtFQUMxQixJQUFBO0VBQUEsV0FBVztFQUVYLEtBQUMsZUFBZSxXQUFBO0lBQ2QsSUFBQTtJQUFBLFdBQVcsR0FBRztJQUVkLE1BQU0sSUFBSSxZQUFZLFlBQVksYUFDakMsUUFBUSxTQUFDLE1BQU0sUUFBUSxTQUFTLFFBQXhCO01BQ1AsV0FBVztNQ3BCWCxPRHFCQSxTQUFTLFFBQVE7O0lDbkJuQixPRHFCQSxTQUFTOztFQ25CWCxPRHFCQTs7QUNuQkYiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaW
xlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJywgWyd1aS5yb3V0ZXInLCAnYW5ndWxhck1vbWVudCddKVxuXG4
jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKCRyb290U2NvcGUpIC0+XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZVxuICAkcm9vdFNjb3BlLnNob3dTaWRlYmFyID0gLT5cbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGVcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJDbGFzcyA9ICdmb3JjZS1zaG93J1xuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi52YWx1ZSAnZmxpbmtDb25maWcnLCB7XG4gIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MSdcbiMgIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9uZXctc2VydmVyJ1xuICBcInJlZnJlc2gtaW50ZXJ2YWxcIjogMTAwMDBcbn1cblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4ucnVuIChKb2JzU2VydmljZSwgTWFpblNlcnZpY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIC0+XG4gIE1haW5TZXJ2aWNlLmxvYWRDb25maWcoKS50aGVuIChjb25maWcpIC0+XG4gICAgYW5ndWxhci5leHRlbmQgZmxpbmtDb25maWcsIGNvbmZpZ1xuXG4gICAgSm9ic1NlcnZpY2UubGlzdEpvYnMoKVxuXG4gICAgJGludGVydmFsIC0+XG4gICAgICBKb2JzU2VydmljZS5saXN0Sm9icygpXG4gICAgLCBmbGlua0NvbmZpZ1tcInJlZnJlc2gtaW50ZXJ2YWxcIl1cblxuXG4j
IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb25maWcgKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIC0+XG4gICRzdGF0ZVByb3ZpZGVyLnN0YXRlIFwib3ZlcnZpZXdcIixcbiAgICB1cmw6IFwiL292ZXJ2aWV3XCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnT3ZlcnZpZXdDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInJ1bm5pbmctam9ic1wiLFxuICAgIHVybDogXCIvcnVubmluZy1qb2JzXCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy1qb2JzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnUnVubmluZ0pvYnNDb250cm9sbGVyJ1xuICBcbiAgLnN0YXRlIFwiY29tcGxldGVkLWpvYnNcIixcbiAgICB1cmw6IFwiL2NvbXBsZXRlZC1qb2JzXCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvY29tcGxldGVkLWpvYnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iXCIsXG4gICAgdXJsOiBcIi9qb2JzL3tqb2JpZH1cIlxuICAgIGFic3RyYWN0OiB0cnVlXG4gICAgdmlld3M6XG4gICAgICBtY
WluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuXCIsXG4gICAgdXJsOiBcIlwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm5vZGVcIixcbiAgICB1cmw6IFwiL3ZlcnRleC97bm9kZWlkfVwiXG4gICAgdmlld3M6XG4gICAgICBub2RlOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZUNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi50aW1lbGluZVwiLFxuICAgIHVybDogXCIvdGltZWxpbmVcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIixcbiAgICB1cmw6IFwiL3t2ZXJ0ZXhJZH1cIlxuICAgIHZpZXdzOlxuICAgICAgdmVydGV4OlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2
pvYi50aW1lbGluZS52ZXJ0ZXguaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5zdGF0aXN0aWNzXCIsXG4gICAgdXJsOiBcIi9zdGF0aXN0aWNzXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnN0YXRpc3RpY3MuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5leGNlcHRpb25zXCIsXG4gICAgdXJsOiBcIi9leGNlcHRpb25zXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmV4Y2VwdGlvbnMuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5jb25maWdcIixcbiAgICB1cmw6IFwiL2NvbmZpZ1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5jb25maWcuaHRtbFwiXG5cbiAgJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZSBcIi9vdmVydmlld1wiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pLnJ1bihmdW5jdGlvbigkcm9vdFNjb3BlKSB7XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZTtcbiAgcmV0dXJuICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSBmdW5jdGlvbigpIHt
cbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGU7XG4gICAgcmV0dXJuICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnO1xuICB9O1xufSkudmFsdWUoJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnLFxuICBcInJlZnJlc2gtaW50ZXJ2YWxcIjogMTAwMDBcbn0pLnJ1bihmdW5jdGlvbihKb2JzU2VydmljZSwgTWFpblNlcnZpY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIHtcbiAgcmV0dXJuIE1haW5TZXJ2aWNlLmxvYWRDb25maWcoKS50aGVuKGZ1bmN0aW9uKGNvbmZpZykge1xuICAgIGFuZ3VsYXIuZXh0ZW5kKGZsaW5rQ29uZmlnLCBjb25maWcpO1xuICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCk7XG4gICAgcmV0dXJuICRpbnRlcnZhbChmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBKb2JzU2VydmljZS5saXN0Sm9icygpO1xuICAgIH0sIGZsaW5rQ29uZmlnW1wicmVmcmVzaC1pbnRlcnZhbFwiXSk7XG4gIH0pO1xufSkuY29uZmlnKGZ1bmN0aW9uKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIHtcbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUoXCJvdmVydmlld1wiLCB7XG4gICAgdXJsOiBcIi9vdmVydmlld1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIixcbiAgICAg
ICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwicnVubmluZy1qb2JzXCIsIHtcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwiY29tcGxldGVkLWpvYnNcIiwge1xuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iXCIsIHtcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwiLFxuICAgIGFic3RyYWN0OiB0cnVlLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhblwiLCB7XG4gICAgdXJsOiBcIlwiL
FxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhbi5ub2RlXCIsIHtcbiAgICB1cmw6IFwiL3ZlcnRleC97bm9kZWlkfVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBub2RlOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZUNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IudGltZWxpbmVcIiwge1xuICAgIHVybDogXCIvdGltZWxpbmVcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIiwge1xuICAgIHVybDogXCIve3ZlcnRleElkfVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICB2ZXJ0ZXg6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUudmVydGV4Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q2
9udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5zdGF0aXN0aWNzXCIsIHtcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5zdGF0aXN0aWNzLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIiwge1xuICAgIHVybDogXCIvZXhjZXB0aW9uc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmV4Y2VwdGlvbnMuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IuY29uZmlnXCIsIHtcbiAgICB1cmw6IFwiL2NvbmZpZ1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmNvbmZpZy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuICByZXR1cm4gJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZShcIi9vdmVydmlld1wiKTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN
0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLVxuXG4uZGlyZWN0aXZlICdic0xhYmVsJywgKEpvYnNTZXJ2aWNlKSAtPlxuICB0cmFuc2NsdWRlOiB0cnVlXG4gIHJlcGxhY2U6IHRydWVcbiAgc2NvcGU6IFxuICAgIGdldExhYmVsQ2xhc3M6IFwiJlwiXG4gICAgc3RhdHVzOiBcIkBcIlxuXG4gIHRlbXBsYXRlOiBcIjxzcGFuIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJz48bmctdHJhbnNjbHVkZT48L25nLXRyYW5zY2x1ZGU+PC9zcGFuPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2xhYmVsIGxhYmVsLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2luZGljYXRvclByaW1hcnknLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHJlcGxhY2U6IHRydWVcbiAgc2NvcGU6IFxuICAgIGdldExhYmVsQ2xhc3M6IFwiJlwiXG4gICAgc3RhdHVzOiAnQCdcblxuICB0ZW1wbGF0ZTogXCI8aSB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIlxuICBcbiAgbGluazogKHNjb3BlLCBlbGVtZW50LCBhdHRycykgLT5cbiAgICBzY29wZS5nZXRMYWJlbENsYXNzID0gLT5cbiAgICAgICdmYSBmYS1jaXJjbGUgaW5kaWNhdG9yIGluZGljYXRvc
i0nICsgSm9ic1NlcnZpY2UudHJhbnNsYXRlTGFiZWxTdGF0ZShhdHRycy5zdGF0dXMpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0YWJsZVByb3BlcnR5JywgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTpcbiAgICB2YWx1ZTogJz0nXG5cbiAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZGlyZWN0aXZlKCdic0xhYmVsJywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICB0cmFuc2NsdWRlOiB0cnVlLFxuICAgIHJlcGxhY2U6IHRydWUsXG4gICAgc2NvcGU6IHtcbiAgICAgIGdldExhYmVsQ2xhc3M6IFwiJlwiLFxuICAgICAgc3RhdHVzOiBcIkBcIlxuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknPjxuZy10cmFuc2NsdWRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCIsXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSB7XG4gICAgICByZXR1cm4gc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJ2xhYmVsIGxhYmVsLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cy
k7XG4gICAgICB9O1xuICAgIH1cbiAgfTtcbn0pLmRpcmVjdGl2ZSgnaW5kaWNhdG9yUHJpbWFyeScsIGZ1bmN0aW9uKEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6ICdAJ1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPGkgdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknIC8+XCIsXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSB7XG4gICAgICByZXR1cm4gc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJ2ZhIGZhLWNpcmNsZSBpbmRpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cyk7XG4gICAgICB9O1xuICAgIH1cbiAgfTtcbn0pLmRpcmVjdGl2ZSgndGFibGVQcm9wZXJ0eScsIGZ1bmN0aW9uKCkge1xuICByZXR1cm4ge1xuICAgIHJlcGxhY2U6IHRydWUsXG4gICAgc2NvcGU6IHtcbiAgICAgIHZhbHVlOiAnPSdcbiAgICB9LFxuICAgIHRlbXBsYXRlOiBcIjx0ZCB0aXRsZT1cXFwie3t2YWx1ZSB8fCAnTm9uZSd9fVxcXCI+e3t2YWx1ZSB8fCAnTm9uZSd9fTwvdGQ+XCJcbiAgfTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXI
gb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4j
XG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5maWx0ZXIgXCJhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRcIiwgKGFuZ3VsYXJNb21lbnRDb25maWcpIC0+XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlciA9ICh2YWx1ZSwgZm9ybWF0LCBkdXJhdGlvbkZvcm1hdCkgLT5cbiAgICByZXR1cm4gXCJcIiAgaWYgdHlwZW9mIHZhbHVlIGlzIFwidW5kZWZpbmVkXCIgb3IgdmFsdWUgaXMgbnVsbFxuXG4gICAgbW9tZW50LmR1cmF0aW9uKHZhbHVlLCBmb3JtYXQpLmZvcm1hdChkdXJhdGlvbkZvcm1hdCwgeyB0cmltOiBmYWxzZSB9KVxuXG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlci4kc3RhdGVmdWwgPSBhbmd1bGFyTW9tZW50Q29uZmlnLnN0YXRlZnVsRmlsdGVyc1xuXG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlclxuXG4uZmlsdGVyIFwiaHVtYW5pemVUYXNrTmFtZVwiLCAtPlxuICAodGV4dCkgLT5cbiAgICAjIFRPRE86IGV4dGVuZC4uLiBhIGxvdFxuICAgIGlmIHRleHQgdGhlbiB0ZXh0LnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKS5yZXBsYWNlKFwiPGJyLz5cIixcIlwiKSBlbHNlICcnXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5maWx0ZXIoXCJhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRcIiwgZnVuY3Rpb24oYW5ndWxhck1vbWVudENvbmZpZykge1xuICB2YXIgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyO1xuICBhbUR1cmF0aW9uRm9yb
WF0RXh0ZW5kZWRGaWx0ZXIgPSBmdW5jdGlvbih2YWx1ZSwgZm9ybWF0LCBkdXJhdGlvbkZvcm1hdCkge1xuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwidW5kZWZpbmVkXCIgfHwgdmFsdWUgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiBcIlwiO1xuICAgIH1cbiAgICByZXR1cm4gbW9tZW50LmR1cmF0aW9uKHZhbHVlLCBmb3JtYXQpLmZvcm1hdChkdXJhdGlvbkZvcm1hdCwge1xuICAgICAgdHJpbTogZmFsc2VcbiAgICB9KTtcbiAgfTtcbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzO1xuICByZXR1cm4gYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyO1xufSkuZmlsdGVyKFwiaHVtYW5pemVUYXNrTmFtZVwiLCBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uKHRleHQpIHtcbiAgICBpZiAodGV4dCkge1xuICAgICAgcmV0dXJuIHRleHQucmVwbGFjZShcIiZndDtcIiwgXCI+XCIpLnJlcGxhY2UoXCI8YnIvPlwiLCBcIlwiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuICcnO1xuICAgIH1cbiAgfTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yay
Bmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5zZXJ2aWNlICdNYWluU2VydmljZScsICgkaHR0cCwgZmxpbmtDb25maWcsICRxKSAtPlxuICBqb2JPYnNlcnZ
lcnMgPSBbXVxuXG4gIEBsb2FkQ29uZmlnID0gLT5cbiAgICBkZWZlcnJlZCA9ICRxLmRlZmVyKClcblxuICAgICRodHRwLmdldCBmbGlua0NvbmZpZy5qb2JTZXJ2ZXIgKyBcIi9jb25maWdcIlxuICAgIC5zdWNjZXNzIChkYXRhLCBzdGF0dXMsIGhlYWRlcnMsIGNvbmZpZykgLT5cbiAgICAgIGRlZmVycmVkLnJlc29sdmUoZGF0YSlcblxuICAgIGRlZmVycmVkLnByb21pc2VcblxuXG4gIEBcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLnNlcnZpY2UoJ01haW5TZXJ2aWNlJywgZnVuY3Rpb24oJGh0dHAsIGZsaW5rQ29uZmlnLCAkcSkge1xuICB2YXIgam9iT2JzZXJ2ZXJzO1xuICBqb2JPYnNlcnZlcnMgPSBbXTtcbiAgdGhpcy5sb2FkQ29uZmlnID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGRlZmVycmVkO1xuICAgIGRlZmVycmVkID0gJHEuZGVmZXIoKTtcbiAgICAkaHR0cC5nZXQoZmxpbmtDb25maWcuam9iU2VydmVyICsgXCIvY29uZmlnXCIpLnN1Y2Nlc3MoZnVuY3Rpb24oZGF0YSwgc3RhdHVzLCBoZWFkZXJzLCBjb25maWcpIHtcbiAgICAgIHJldHVybiBkZWZlcnJlZC5yZXNvbHZlKGRhdGEpO1xuICAgIH0pO1xuICAgIHJldHVybiBkZWZlcnJlZC5wcm9taXNlO1xuICB9O1xuICByZXR1cm4gdGhpcztcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1l
bnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5jb250cm9sb
GVyICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdmaW5pc2hlZCcpXG5cbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuXG4gICRzY29wZS5qb2JPYnNlcnZlcigpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2
xsZXIgJ1NpbmdsZUpvYkNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRyb290U2NvcGUpIC0+XG4gICRzY29wZS5qb2JpZCA9ICRzdGF0ZVBhcmFtcy5qb2JpZFxuICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcblxuICBKb2JzU2VydmljZS5sb2FkSm9iKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkcm9vdFNjb3BlLmpvYiA9IGRhdGFcblxuICAkc2NvcGUuJG9uICckZGVzdHJveScsIC0+XG4gICAgJHJvb3RTY29wZS5qb2IgPSBudWxsXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Db250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBKb2JzU2VydmljZS5sb2FkUGxhbigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLnBsYW4gPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLm5vZGVpZCA9ICRzdGF0ZVBhcmFtcy5ub2RlaWRcbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpXG5cbiAgSm9ic1NlcnZpY2UuZ2V0Tm9kZSgkc2N
vcGUubm9kZWlkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS5ub2RlID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmdldFZlcnRleCgkc3RhdGVQYXJhbXMudmVydGV4SWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLnZlcnRleCA9IGRhdGFcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmNvbnRyb2xsZXIoJ1J1bm5pbmdKb2JzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ3J1bm5pbmcnKTtcbiAgfTtcbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgfSk7XG4gIHJldHVybiAkc2NvcGUuam9iT2JzZXJ2ZXIoKTtcbn0pLmNvbnRyb2xsZXIoJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNj
b3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygnZmluaXNoZWQnKTtcbiAgfTtcbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgfSk7XG4gIHJldHVybiAkc2NvcGUuam9iT2JzZXJ2ZXIoKTtcbn0pLmNvbnRyb2xsZXIoJ1NpbmdsZUpvYkNvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHJvb3RTY29wZSkge1xuICAkc2NvcGUuam9iaWQgPSAkc3RhdGVQYXJhbXMuam9iaWQ7XG4gICRyb290U2NvcGUuam9iID0gbnVsbDtcbiAgSm9ic1NlcnZpY2UubG9hZEpvYigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkcm9vdFNjb3BlLmpvYiA9IGRhdGE7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5qb2IgPSBudWxsO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Db250cm9sbGVyJywgZnVuY3Rpb24oJ
HNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmxvYWRQbGFuKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5wbGFuID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuTm9kZUNvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZDtcbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpO1xuICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0Tm9kZSgkc2NvcGUubm9kZWlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlLm5vZGUgPSBkYXRhO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiBKb2JzU2VydmljZS5nZXRWZXJ0ZXgoJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlLnZlcnRleCA9IGRhdGE7XG4gIH0pO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb2
50cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGU
oJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3ZlcnRleCcsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCJcblxuICBzY29wZTpcbiAgICBkYXRhOiBcIj1cIlxuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF1cblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVylcblxuICAgIGFuYWx5emVUaW1lID0gKGRhdGEpIC0+XG4gICAgICB0ZXN0RGF0YSA9IFtdXG5cbiAgICAgIGFuZ3VsYXIuZm9yRWFjaCBkYXRhLnN1YnRhc2tzLCAoc3VidGFzaywgaSkgLT5cbiAgICAgICAgdGVzdERhdGEucHVzaCB7XG4gICAgICAgICAgbGFiZWw6IFwiI3tzdWJ0YXNrLmhvc3R9ICgje3N1YnRhc2suc3VidGFza30pXCJcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjNjY2XCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlNDSEVE
VUxFRFwiXVxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiREVQTE9ZSU5HXCJdXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJEZXBsb3lpbmdcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjYWFhXCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkRFUExPWUlOR1wiXVxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiUlVOTklOR1wiXVxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6IFwiUnVubmluZ1wiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiNkZGRcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiUlVOTklOR1wiXVxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiRklOSVNIRURcIl1cbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICB9XG4gICAgICAgICAgXVxuICAgICAgICB9XG5cbiAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zd
GFjaygpXG4gICAgICAudGlja0Zvcm1hdCh7XG4gICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKVxuICAgICAgICAjIHRpY2tJbnRlcnZhbDogMVxuICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJzaW5nbGVcIilcbiAgICAgIC5sYWJlbEZvcm1hdCgobGFiZWwpIC0+XG4gICAgICAgIGxhYmVsXG4gICAgICApXG4gICAgICAubWFyZ2luKHsgbGVmdDogMTAwLCByaWdodDogMCwgdG9wOiAwLCBib3R0b206IDAgfSlcbiAgICAgIC5pdGVtSGVpZ2h0KDMwKVxuICAgICAgLnJlbGF0aXZlVGltZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgYW5hbHl6ZVRpbWUoc2NvcGUuZGF0YSlcblxuICAgIHJldHVyblxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAndGltZWxpbmUnLCAoJHN0YXRlKSAtPlxuICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCJcblxuICBzY29wZTpcbiAgICBqb2I6IFwiPVwiXG5cbiAgbGluazogKHNjb3BlLCBlbGVtLCBhdHRycykgLT5cbiAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYX
R0cignd2lkdGgnLCBjb250YWluZXJXKVxuXG4gICAgdHJhbnNsYXRlTGFiZWwgPSAobGFiZWwpIC0+XG4gICAgICBsYWJlbC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIilcblxuICAgIGFuYWx5emVUaW1lID0gKGRhdGEpIC0+XG4gICAgICB0ZXN0RGF0YSA9IFtdXG5cbiAgICAgIHRlc3REYXRhLnB1c2ggXG4gICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCJcbiAgICAgICAgICBjb2xvcjogXCIjY2NjY2NjXCJcbiAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICBzdGFydGluZ190aW1lOiBkYXRhLnRpbWVzdGFtcHNbXCJDUkVBVEVEXCJdXG4gICAgICAgICAgZW5kaW5nX3RpbWU6IGRhdGEudGltZXN0YW1wc1tcIkNSRUFURURcIl0gKyAxXG4gICAgICAgICAgdHlwZTogJ3NjaGVkdWxlZCdcbiAgICAgICAgXVxuXG5cbiAgICAgIGFuZ3VsYXIuZm9yRWFjaCBkYXRhLnZlcnRpY2VzLCAodmVydGV4KSAtPlxuICAgICAgICB0ZXN0RGF0YS5wdXNoIFxuICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICBsYWJlbDogdHJhbnNsYXRlTGFiZWwodmVydGV4Lm5hbWUpXG4gICAgICAgICAgICBjb2xvcjogXCIjZDlmMWY3XCJcbiAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM2MmNkZWFcIlxuICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdmVydGV4WydzdGFydC10aW1lJ11cbiAgICAgICAgICAgIGVuZGluZ190aW1lOiB2ZXJ0ZXhbJ2VuZC10aW1lJ11
cbiAgICAgICAgICAgIGxpbms6IHZlcnRleC5pZFxuICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgXVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5jbGljaygoZCwgaSwgZGF0dW0pIC0+XG4gICAgICAgIGlmIGQubGlua1xuICAgICAgICAgICRzdGF0ZS5nbyBcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHsgam9iaWQ6IGRhdGEuamlkLCB2ZXJ0ZXhJZDogZC5saW5rIH1cblxuICAgICAgKVxuICAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICAgICAgIyB0aWNrVGltZTogZDMudGltZS5zZWNvbmRcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDAuNVxuICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJtYWluXCIpXG4gICAgICAubWFyZ2luKHsgbGVmdDogMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgzMClcbiAgICAgIC5zaG93Qm9yZGVyTGluZSgpXG4gICAgICAuc2hvd0hvdXJUaW1lbGluZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgc2NvcGUuJHdhdGNoIGF0dHJzLmpvYiwgKGRhdGEpIC0+XG4gICAgICBhbmFseXplVGltZShkYXRhKSBpZiBkYXRhXG5cbiAgICByZXR1cm5cblxuIyAtLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2pvYlBsYW4nLCAoJHRpbWVvdXQpIC0+XG4gIHRlbXBsYXRlOiBcIlxuICAgIDxzdmcgY2xhc3M9J2dyYXBoJyB3aWR0aD0nNTAwJyBoZWlnaHQ9JzQwMCc+PGcgLz48L3N2Zz5cbiAgICA8c3ZnIGNsYXNzPSd0bXAnIHdpZHRoPScxJyBoZWlnaHQ9JzEnPjxnIC8+PC9zdmc+XG4gICAgPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+XG4gICAgICA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20taW4nIG5nLWNsaWNrPSd6b29tSW4oKSc+PGkgY2xhc3M9J2ZhIGZhLXBsdXMnIC8+PC9hPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLW91dCcgbmctY2xpY2s9J3pvb21PdXQoKSc+PGkgY2xhc3M9J2ZhIGZhLW1pbnVzJyAvPjwvYT5cbiAgICA8L2Rpdj5cIlxuXG4gIHNjb3BlOlxuICAgIHBsYW46ICc9J1xuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgbWFpblpvb20gPSBkMy5iZWhhdmlvci56b29tKClcbiAgICBzdWJncmFwaHMgPSBbXVxuICAgIGpvYmlkID0gYXR0cnMuam9iaWRcblxuICAgIG1haW5TdmdFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzBdXG4gICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXVxuICAgIG1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdXG5cbiAgICBkM21haW5TdmcgPSBkMy5zZWxlY3Qob
WFpblN2Z0VsZW1lbnQpXG4gICAgZDNtYWluU3ZnRyA9IGQzLnNlbGVjdChtYWluRylcbiAgICBkM3RtcFN2ZyA9IGQzLnNlbGVjdChtYWluVG1wRWxlbWVudClcblxuICAgICMgYW5ndWxhci5lbGVtZW50KG1haW5HKS5lbXB0eSgpXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KGVsZW0uY2hpbGRyZW4oKVswXSkud2lkdGgoY29udGFpbmVyVylcblxuICAgIHNjb3BlLnpvb21JbiA9IC0+XG4gICAgICBpZiBtYWluWm9vbS5zY2FsZSgpIDwgMi45OVxuICAgICAgICBcbiAgICAgICAgIyBDYWxjdWxhdGUgYW5kIHN0b3JlIG5ldyB2YWx1ZXMgaW4gem9vbSBvYmplY3RcbiAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKClcbiAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgbWFpblpvb20uc2NhbGUgbWFpblpvb20uc2NhbGUoKSArIDAuMVxuICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUgWyB2MSwgdjIgXVxuICAgICAgICBcbiAgICAgICAgIyBUcmFuc2Zvcm0gc3ZnXG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFwiKSBzY2FsZS
hcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIlxuXG4gICAgc2NvcGUuem9vbU91dCA9IC0+XG4gICAgICBpZiBtYWluWm9vbS5zY2FsZSgpID4gMC4zMVxuICAgICAgICBcbiAgICAgICAgIyBDYWxjdWxhdGUgYW5kIHN0b3JlIG5ldyB2YWx1ZXMgaW4gbWFpblpvb20gb2JqZWN0XG4gICAgICAgIG1haW5ab29tLnNjYWxlIG1haW5ab29tLnNjYWxlKCkgLSAwLjFcbiAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKClcbiAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgbWFpblpvb20udHJhbnNsYXRlIFsgdjEsIHYyIF1cbiAgICAgICAgXG4gICAgICAgICMgVHJhbnNmb3JtIHN2Z1xuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCJcblxuICAgICNjcmVhdGUgYSBsYWJlbCBvZiBhbiBlZGdlXG4gICAgY3JlYXRlTGFiZWxFZGdlID0gKGVsKSAtPlxuICAgICAgbGFiZWxWYWx1ZSA9IFwiXCJcbiAgICAgIGlmIGVsLnNoaXBfc3RyYXRlZ3k/IG9yIGVsLmxvY2FsX3N0cmF0ZWd5P1xuICAgICAgICBsYWJlbFZhbHV
lICs9IFwiPGRpdiBjbGFzcz0nZWRnZS1sYWJlbCc+XCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBlbC5zaGlwX3N0cmF0ZWd5ICBpZiBlbC5zaGlwX3N0cmF0ZWd5P1xuICAgICAgICBsYWJlbFZhbHVlICs9IFwiIChcIiArIGVsLnRlbXBfbW9kZSArIFwiKVwiICB1bmxlc3MgZWwudGVtcF9tb2RlIGlzIGB1bmRlZmluZWRgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCIsPGJyPlwiICsgZWwubG9jYWxfc3RyYXRlZ3kgIHVubGVzcyBlbC5sb2NhbF9zdHJhdGVneSBpcyBgdW5kZWZpbmVkYFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPC9kaXY+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuXG4gICAgIyB0cnVlLCBpZiB0aGUgbm9kZSBpcyBhIHNwZWNpYWwgbm9kZSBmcm9tIGFuIGl0ZXJhdGlvblxuICAgIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUgPSAoaW5mbykgLT5cbiAgICAgIChpbmZvIGlzIFwicGFydGlhbFNvbHV0aW9uXCIgb3IgaW5mbyBpcyBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiBvciBpbmZvIGlzIFwid29ya3NldFwiIG9yIGluZm8gaXMgXCJuZXh0V29ya3NldFwiIG9yIGluZm8gaXMgXCJzb2x1dGlvblNldFwiIG9yIGluZm8gaXMgXCJzb2x1dGlvbkRlbHRhXCIpXG5cbiAgICBnZXROb2RlVHlwZSA9IChlbCwgaW5mbykgLT5cbiAgICAgIGlmIGluZm8gaXMgXCJtaXJyb3JcIlxuICAgICAgICAnbm9kZS1taXJyb3InXG5cbiAgICAgIGVsc2UgaWYgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKVxu
ICAgICAgICAnbm9kZS1pdGVyYXRpb24nXG5cbiAgICAgIGVsc2VcbiAgICAgICAgICAnbm9kZS1ub3JtYWwnXG4gICAgICBcbiAgICAjIGNyZWF0ZXMgdGhlIGxhYmVsIG9mIGEgbm9kZSwgaW4gaW5mbyBpcyBzdG9yZWQsIHdoZXRoZXIgaXQgaXMgYSBzcGVjaWFsIG5vZGUgKGxpa2UgYSBtaXJyb3IgaW4gYW4gaXRlcmF0aW9uKVxuICAgIGNyZWF0ZUxhYmVsTm9kZSA9IChlbCwgaW5mbywgbWF4VywgbWF4SCkgLT5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIjxhIGhyZWY9JyMvam9icy9cIiArIGpvYmlkICsgXCIvdmVydGV4L1wiICsgZWwuaWQgKyBcIicgY2xhc3M9J25vZGUtbGFiZWwgXCIgKyBnZXROb2RlVHlwZShlbCwgaW5mbykgKyBcIic+XCJcblxuICAgICAgIyBOb2RlbmFtZVxuICAgICAgaWYgaW5mbyBpcyBcIm1pcnJvclwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+TWlycm9yIG9mIFwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCJcbiAgICAgIGVsc2VcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5cIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiXG4gICAgICBpZiBlbC5kZXNjcmlwdGlvbiBpcyBcIlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCJcIlxuICAgICAgZWxzZVxuICAgICAgICBzdGVwTmFtZSA9IGVsLmRlc2NyaXB0aW9uXG4gICAgICAgIFxuICAgICAgICAjIGNsZWFuIHN0ZXBOYW1lXG4gICAgICAgIHN0Z
XBOYW1lID0gc2hvcnRlblN0cmluZyhzdGVwTmFtZSlcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNCBjbGFzcz0nc3RlcC1uYW1lJz5cIiArIHN0ZXBOYW1lICsgXCI8L2g0PlwiXG4gICAgICBcbiAgICAgICMgSWYgdGhpcyBub2RlIGlzIGFuIFwiaXRlcmF0aW9uXCIgd2UgbmVlZCBhIGRpZmZlcmVudCBwYW5lbC1ib2R5XG4gICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICBsYWJlbFZhbHVlICs9IGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbihlbC5pZCwgbWF4VywgbWF4SClcbiAgICAgIGVsc2VcbiAgICAgICAgXG4gICAgICAgICMgT3RoZXJ3aXNlIGFkZCBpbmZvcyAgICBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5cIiArIGluZm8gKyBcIiBOb2RlPC9oNT5cIiAgaWYgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlBhcmFsbGVsaXNtOiBcIiArIGVsLnBhcmFsbGVsaXNtICsgXCI8L2g1PlwiICB1bmxlc3MgZWwucGFyYWxsZWxpc20gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1Pk9wZXJhdGlvbjogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLm9wZXJhdG9yX3N0cmF0ZWd5KSArIFwiPC9oNT5cIiAgdW5sZXNzIGVsLm9wZXJhdG9yIGlzIGB1bmRlZmluZWRgXG4gICAgICBcbiAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2E+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuICAgICMgRXh0ZW5kcyB0aGUgbGFiZWwgb2
YgYSBub2RlIHdpdGggYW4gYWRkaXRpb25hbCBzdmcgRWxlbWVudCB0byBwcmVzZW50IHRoZSBpdGVyYXRpb24uXG4gICAgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uID0gKGlkLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgc3ZnSUQgPSBcInN2Zy1cIiArIGlkXG5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIjxzdmcgY2xhc3M9J1wiICsgc3ZnSUQgKyBcIicgd2lkdGg9XCIgKyBtYXhXICsgXCIgaGVpZ2h0PVwiICsgbWF4SCArIFwiPjxnIC8+PC9zdmc+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuICAgICMgU3BsaXQgYSBzdHJpbmcgaW50byBtdWx0aXBsZSBsaW5lcyBzbyB0aGF0IGVhY2ggbGluZSBoYXMgbGVzcyB0aGFuIDMwIGxldHRlcnMuXG4gICAgc2hvcnRlblN0cmluZyA9IChzKSAtPlxuICAgICAgIyBtYWtlIHN1cmUgdGhhdCBuYW1lIGRvZXMgbm90IGNvbnRhaW4gYSA8IChiZWNhdXNlIG9mIGh0bWwpXG4gICAgICBpZiBzLmNoYXJBdCgwKSBpcyBcIjxcIlxuICAgICAgICBzID0gcy5yZXBsYWNlKFwiPFwiLCBcIiZsdDtcIilcbiAgICAgICAgcyA9IHMucmVwbGFjZShcIj5cIiwgXCImZ3Q7XCIpXG4gICAgICBzYnIgPSBcIlwiXG4gICAgICB3aGlsZSBzLmxlbmd0aCA+IDMwXG4gICAgICAgIHNiciA9IHNiciArIHMuc3Vic3RyaW5nKDAsIDMwKSArIFwiPGJyPlwiXG4gICAgICAgIHMgPSBzLnN1YnN0cmluZygzMCwgcy5sZW5ndGgpXG4gICAgICBzYnIgPSBzYnIgKyBzXG4gICAgICBzYnJcblxuICAgIGN
yZWF0ZU5vZGUgPSAoZywgZGF0YSwgZWwsIGlzUGFyZW50ID0gZmFsc2UsIG1heFcsIG1heEgpIC0+XG4gICAgICAjIGNyZWF0ZSBub2RlLCBzZW5kIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25zIGFib3V0IHRoZSBub2RlIGlmIGl0IGlzIGEgc3BlY2lhbCBvbmVcbiAgICAgIGlmIGVsLmlkIGlzIGRhdGEucGFydGlhbF9zb2x1dGlvblxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLm5leHRfcGFydGlhbF9zb2x1dGlvblxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEud29ya3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJ3b3Jrc2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAg
ICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwid29ya3NldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEubmV4dF93b3Jrc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRXb3Jrc2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFdvcmtzZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLnNvbHV0aW9uX3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJzb2x1dGlvblNldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uU2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5zb2x1dGlvbl9kZWx0YVxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25EZWx0YVwiKVxuXG4gICAgICBlbHNlXG4gICAgICAgI
Gcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIlwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIlwiKVxuXG4gICAgY3JlYXRlRWRnZSA9IChnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZCkgLT5cbiAgICAgIHVubGVzcyBleGlzdGluZ05vZGVzLmluZGV4T2YocHJlZC5pZCkgaXMgLTFcbiAgICAgICAgZy5zZXRFZGdlIHByZWQuaWQsIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UocHJlZClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGFycm93aGVhZDogJ25vcm1hbCdcblxuICAgICAgZWxzZVxuICAgICAgICBtaXNzaW5nTm9kZSA9IHNlYXJjaEZvck5vZGUoZGF0YSwgcHJlZC5pZClcbiAgICAgICAgdW5sZXNzICFtaXNzaW5nTm9kZSBvciBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgaXMgdHJ1ZVxuICAgICAgICAgIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9IHRydWVcbiAgICAgICAgICBnLnNldE5vZGUgbWlzc2luZ05vZGUuaWQsXG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKG1pc3NpbmdOb2RlLCBcIm1pcnJvclwiKVxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShtaXNzaW5nTm9kZSwgJ21pcnJvcicpXG5cbi
AgICAgICAgICBnLnNldEVkZ2UgbWlzc2luZ05vZGUuaWQsIGVsLmlkLFxuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShtaXNzaW5nTm9kZSlcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG5cbiAgICBsb2FkSnNvblRvRGFncmUgPSAoZywgZGF0YSkgLT5cbiAgICAgIGV4aXN0aW5nTm9kZXMgPSBbXVxuXG4gICAgICBpZiBkYXRhLm5vZGVzP1xuICAgICAgICAjIFRoaXMgaXMgdGhlIG5vcm1hbCBqc29uIGRhdGFcbiAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5ub2Rlc1xuXG4gICAgICBlbHNlXG4gICAgICAgICMgVGhpcyBpcyBhbiBpdGVyYXRpb24sIHdlIG5vdyBzdG9yZSBzcGVjaWFsIGl0ZXJhdGlvbiBub2RlcyBpZiBwb3NzaWJsZVxuICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgaXNQYXJlbnQgPSB0cnVlXG5cbiAgICAgIGZvciBlbCBpbiB0b0l0ZXJhdGVcbiAgICAgICAgbWF4VyA9IDBcbiAgICAgICAgbWF4SCA9IDBcblxuICAgICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uXG4gICAgICAgICAgc2cgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7IG11bHRpZ3JhcGg6IHRydWUsIGNvbXBvdW5kOiB0cnVlIH0pLnNldEdyYXBoKHtcbiAgICAgICAgICAgIG5vZGVzZXA6IDIwXG4gICAgICAgICAgICBlZGdlc2VwOiAwXG4gICAgICAgICAgICByYW5rc2VwOiAyMFxuICAgICAgICAgICAgcmFua2RpcjogXCJMUlwiXG4gICAgICAgICAgICB
tYXJnaW54OiAxMFxuICAgICAgICAgICAgbWFyZ2lueTogMTBcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICBzdWJncmFwaHNbZWwuaWRdID0gc2dcblxuICAgICAgICAgIGxvYWRKc29uVG9EYWdyZShzZywgZWwpXG5cbiAgICAgICAgICByID0gbmV3IGRhZ3JlRDMucmVuZGVyKClcbiAgICAgICAgICBkM3RtcFN2Zy5zZWxlY3QoJ2cnKS5jYWxsKHIsIHNnKVxuICAgICAgICAgIG1heFcgPSBzZy5ncmFwaCgpLndpZHRoXG4gICAgICAgICAgbWF4SCA9IHNnLmdyYXBoKCkuaGVpZ2h0XG5cbiAgICAgICAgICBhbmd1bGFyLmVsZW1lbnQobWFpblRtcEVsZW1lbnQpLmVtcHR5KClcblxuICAgICAgICBjcmVhdGVOb2RlKGcsIGRhdGEsIGVsLCBpc1BhcmVudCwgbWF4VywgbWF4SClcblxuICAgICAgICBleGlzdGluZ05vZGVzLnB1c2ggZWwuaWRcbiAgICAgICAgXG4gICAgICAgICMgY3JlYXRlIGVkZ2VzIGZyb20gaW5wdXRzIHRvIGN1cnJlbnQgbm9kZVxuICAgICAgICBpZiBlbC5pbnB1dHM/XG4gICAgICAgICAgZm9yIHByZWQgaW4gZWwuaW5wdXRzXG4gICAgICAgICAgICBjcmVhdGVFZGdlKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKVxuXG4gICAgICBnXG5cbiAgICAjIHNlYXJjaGVzIGluIHRoZSBnbG9iYWwgSlNPTkRhdGEgZm9yIHRoZSBub2RlIHdpdGggdGhlIGdpdmVuIGlkXG4gICAgc2VhcmNoRm9yTm9kZSA9IChkYXRhLCBub2RlSUQpIC0+XG4gICAgICBmb3IgaSBvZiBkYXRhLm5vZGVz
XG4gICAgICAgIGVsID0gZGF0YS5ub2Rlc1tpXVxuICAgICAgICByZXR1cm4gZWwgIGlmIGVsLmlkIGlzIG5vZGVJRFxuICAgICAgICBcbiAgICAgICAgIyBsb29rIGZvciBub2RlcyB0aGF0IGFyZSBpbiBpdGVyYXRpb25zXG4gICAgICAgIGlmIGVsLnN0ZXBfZnVuY3Rpb24/XG4gICAgICAgICAgZm9yIGogb2YgZWwuc3RlcF9mdW5jdGlvblxuICAgICAgICAgICAgcmV0dXJuIGVsLnN0ZXBfZnVuY3Rpb25bal0gIGlmIGVsLnN0ZXBfZnVuY3Rpb25bal0uaWQgaXMgbm9kZUlEXG5cbiAgICBkcmF3R3JhcGggPSAoZGF0YSkgLT5cbiAgICAgIGcgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7IG11bHRpZ3JhcGg6IHRydWUsIGNvbXBvdW5kOiB0cnVlIH0pLnNldEdyYXBoKHtcbiAgICAgICAgbm9kZXNlcDogNzBcbiAgICAgICAgZWRnZXNlcDogMFxuICAgICAgICByYW5rc2VwOiA1MFxuICAgICAgICByYW5rZGlyOiBcIkxSXCJcbiAgICAgICAgbWFyZ2lueDogNDBcbiAgICAgICAgbWFyZ2lueTogNDBcbiAgICAgICAgfSlcblxuICAgICAgbG9hZEpzb25Ub0RhZ3JlKGcsIGRhdGEpXG5cbiAgICAgIHJlbmRlcmVyID0gbmV3IGRhZ3JlRDMucmVuZGVyKClcbiAgICAgIGQzbWFpblN2Z0cuY2FsbChyZW5kZXJlciwgZylcblxuICAgICAgZm9yIGksIHNnIG9mIHN1YmdyYXBoc1xuICAgICAgICBkM21haW5Tdmcuc2VsZWN0KCdzdmcuc3ZnLScgKyBpICsgJyBnJykuY2FsbChyZW5kZXJlciwgc2cpXG5cbiAgICAgI
G5ld1NjYWxlID0gMC41XG5cbiAgICAgIHhDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLndpZHRoKCkgLSBnLmdyYXBoKCkud2lkdGggKiBuZXdTY2FsZSkgLyAyKVxuICAgICAgeUNlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkuaGVpZ2h0KCkgLSBnLmdyYXBoKCkuaGVpZ2h0ICogbmV3U2NhbGUpIC8gMilcblxuICAgICAgbWFpblpvb20uc2NhbGUobmV3U2NhbGUpLnRyYW5zbGF0ZShbeENlbnRlck9mZnNldCwgeUNlbnRlck9mZnNldF0pXG5cbiAgICAgIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHhDZW50ZXJPZmZzZXQgKyBcIiwgXCIgKyB5Q2VudGVyT2Zmc2V0ICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKVxuXG4gICAgICBtYWluWm9vbS5vbihcInpvb21cIiwgLT5cbiAgICAgICAgZXYgPSBkMy5ldmVudFxuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyBldi50cmFuc2xhdGUgKyBcIikgc2NhbGUoXCIgKyBldi5zY2FsZSArIFwiKVwiXG4gICAgICApXG4gICAgICBtYWluWm9vbShkM21haW5TdmcpXG5cbiAgICBzY29wZS4kd2F0Y2ggYXR0cnMucGxhbiwgKG5ld1BsYW4pIC0+XG4gICAgICBkcmF3R3JhcGgobmV3UGxhbikgaWYgbmV3UGxhblxuXG4gICAgcmV0dXJuXG
4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ3ZlcnRleCcsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCIsXG4gICAgc2NvcGU6IHtcbiAgICAgIGRhdGE6IFwiPVwiXG4gICAgfSxcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbSwgYXR0cnMpIHtcbiAgICAgIHZhciBhbmFseXplVGltZSwgY29udGFpbmVyVywgc3ZnRWw7XG4gICAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXTtcbiAgICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKCk7XG4gICAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVyk7XG4gICAgICBhbmFseXplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGNoYXJ0LCBzdmcsIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICBhbmd1bGFyLmZvckVhY2goZGF0YS5zdWJ0YXNrcywgZnVuY3Rpb24oc3VidGFzaywgaSkge1xuICAgICAgICAgIHJldHVybiB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICAgIGxhYmVsOiBzdWJ0YXNrLmhvc3QgKyBcIiAoXCIgKyBzdWJ0YXNrLnN1YnRhc2sgKyBcIilcIixcbiAgICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogXCJTY2hlZHV
sZWRcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjNjY2XCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlNDSEVEVUxFRFwiXSxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiREVQTE9ZSU5HXCJdLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiRGVwbG95aW5nXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiI2FhYVwiLFxuICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJERVBMT1lJTkdcIl0sXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlJVTk5JTkdcIl0sXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH0sIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogXCJSdW5uaW5nXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiI2RkZFwiLFxuICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU5HXCJdLFxuICAgICAgICAg
ICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJGSU5JU0hFRFwiXSxcbiAgICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgXVxuICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKCkudGlja0Zvcm1hdCh7XG4gICAgICAgICAgZm9ybWF0OiBkMy50aW1lLmZvcm1hdChcIiVMXCIpLFxuICAgICAgICAgIHRpY2tTaXplOiAxXG4gICAgICAgIH0pLnByZWZpeChcInNpbmdsZVwiKS5sYWJlbEZvcm1hdChmdW5jdGlvbihsYWJlbCkge1xuICAgICAgICAgIHJldHVybiBsYWJlbDtcbiAgICAgICAgfSkubWFyZ2luKHtcbiAgICAgICAgICBsZWZ0OiAxMDAsXG4gICAgICAgICAgcmlnaHQ6IDAsXG4gICAgICAgICAgdG9wOiAwLFxuICAgICAgICAgIGJvdHRvbTogMFxuICAgICAgICB9KS5pdGVtSGVpZ2h0KDMwKS5yZWxhdGl2ZVRpbWUoKTtcbiAgICAgICAgcmV0dXJuIHN2ZyA9IGQzLnNlbGVjdChzdmdFbCkuZGF0dW0odGVzdERhdGEpLmNhbGwoY2hhcnQpO1xuICAgICAgfTtcbiAgICAgIGFuYWx5emVUaW1lKHNjb3BlLmRhdGEpO1xuICAgIH1cbiAgfTtcbn0pLmRpcmVjdGl2ZSgndGltZWxpbmUnLCBmdW5jdGlvbigkc3RhdGUpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCIsX
G4gICAgc2NvcGU6IHtcbiAgICAgIGpvYjogXCI9XCJcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGFuYWx5emVUaW1lLCBjb250YWluZXJXLCBzdmdFbCwgdHJhbnNsYXRlTGFiZWw7XG4gICAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXTtcbiAgICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKCk7XG4gICAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVyk7XG4gICAgICB0cmFuc2xhdGVMYWJlbCA9IGZ1bmN0aW9uKGxhYmVsKSB7XG4gICAgICAgIHJldHVybiBsYWJlbC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIik7XG4gICAgICB9O1xuICAgICAgYW5hbHl6ZVRpbWUgPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIHZhciBjaGFydCwgc3ZnLCB0ZXN0RGF0YTtcbiAgICAgICAgdGVzdERhdGEgPSBbXTtcbiAgICAgICAgdGVzdERhdGEucHVzaCh7XG4gICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCIsXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiNjY2NjY2NcIixcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiLFxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBkYXRhLnRpbWVzdGFtcHNbXCJDUkVBVEVEXCJdLFxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogZGF0YS50aW1lc3RhbXBzW1wiQ1JFQVRFRF
wiXSArIDEsXG4gICAgICAgICAgICAgIHR5cGU6ICdzY2hlZHVsZWQnXG4gICAgICAgICAgICB9XG4gICAgICAgICAgXVxuICAgICAgICB9KTtcbiAgICAgICAgYW5ndWxhci5mb3JFYWNoKGRhdGEudmVydGljZXMsIGZ1bmN0aW9uKHZlcnRleCkge1xuICAgICAgICAgIHJldHVybiB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogdHJhbnNsYXRlTGFiZWwodmVydGV4Lm5hbWUpLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiNkOWYxZjdcIixcbiAgICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNjJjZGVhXCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdmVydGV4WydzdGFydC10aW1lJ10sXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZlcnRleFsnZW5kLXRpbWUnXSxcbiAgICAgICAgICAgICAgICBsaW5rOiB2ZXJ0ZXguaWQsXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIF1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLmNsaWNrKGZ1bmN0aW9uKGQsIGksIGRhdHVtKSB7XG4gICAgICAgICAgaWYgKGQubGluaykge1xuICAgICAgICAgICAgcmV0dXJuICRzdGF0ZS5nbyhcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHtcbiAgICAgICAgICA
gICAgam9iaWQ6IGRhdGEuamlkLFxuICAgICAgICAgICAgICB2ZXJ0ZXhJZDogZC5saW5rXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pLnRpY2tGb3JtYXQoe1xuICAgICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKSxcbiAgICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgICB9KS5wcmVmaXgoXCJtYWluXCIpLm1hcmdpbih7XG4gICAgICAgICAgbGVmdDogMCxcbiAgICAgICAgICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMzApLnNob3dCb3JkZXJMaW5lKCkuc2hvd0hvdXJUaW1lbGluZSgpO1xuICAgICAgICByZXR1cm4gc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCk7XG4gICAgICB9O1xuICAgICAgc2NvcGUuJHdhdGNoKGF0dHJzLmpvYiwgZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgIHJldHVybiBhbmFseXplVGltZShkYXRhKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdqb2JQbGFuJywgZnVuY3Rpb24oJHRpbWVvdXQpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+IDxzdmcgY2xhc3M9J3RtcCcgd2lkdGg9JzEnIGhlaWdodD0nMSc+PGcg
Lz48L3N2Zz4gPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1pbicgbmctY2xpY2s9J3pvb21JbigpJz48aSBjbGFzcz0nZmEgZmEtcGx1cycgLz48L2E+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+IDwvZGl2PlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBwbGFuOiAnPSdcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGNvbnRhaW5lclcsIGNyZWF0ZUVkZ2UsIGNyZWF0ZUxhYmVsRWRnZSwgY3JlYXRlTGFiZWxOb2RlLCBjcmVhdGVOb2RlLCBkM21haW5TdmcsIGQzbWFpblN2Z0csIGQzdG1wU3ZnLCBkcmF3R3JhcGgsIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiwgZ2V0Tm9kZVR5cGUsIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUsIGpvYmlkLCBsb2FkSnNvblRvRGFncmUsIG1haW5HLCBtYWluU3ZnRWxlbWVudCwgbWFpblRtcEVsZW1lbnQsIG1haW5ab29tLCBzZWFyY2hGb3JOb2RlLCBzaG9ydGVuU3RyaW5nLCBzdWJncmFwaHM7XG4gICAgICBtYWluWm9vbSA9IGQzLmJlaGF2aW9yLnpvb20oKTtcbiAgICAgIHN1YmdyYXBocyA9IFtdO1xuICAgICAgam9iaWQgPSBhdHRycy5qb2JpZDtcbiAgICAgIG1haW5TdmdFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuI
CAgICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXTtcbiAgICAgIG1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdO1xuICAgICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1haW5TdmdFbGVtZW50KTtcbiAgICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpO1xuICAgICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpO1xuICAgICAgc2NvcGUuem9vbUluID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB0cmFuc2xhdGUsIHYxLCB2MjtcbiAgICAgICAgaWYgKG1haW5ab29tLnNjYWxlKCkgPCAyLjk5KSB7XG4gICAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKCk7XG4gICAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgbWFpblpvb20uc2NhbGUobWFpblpvb20uc2NhbGUoKSArIDAuMSk7XG4gICAgICAgICAgbWFpblpvb20udHJhbnNsYXRlKFt2MSwgdjJdKTtcbiAgICAgICAgICByZXR1cm4gZDNtYWluU3ZnRy5hdHRyKF
widHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIHNjb3BlLnpvb21PdXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHRyYW5zbGF0ZSwgdjEsIHYyO1xuICAgICAgICBpZiAobWFpblpvb20uc2NhbGUoKSA+IDAuMzEpIHtcbiAgICAgICAgICBtYWluWm9vbS5zY2FsZShtYWluWm9vbS5zY2FsZSgpIC0gMC4xKTtcbiAgICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKTtcbiAgICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUoW3YxLCB2Ml0pO1xuICAgICAgICAgIHJldHVybiBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlTGFiZWxFZGdlID0gZnVuY3Rpb24oZWwpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWU7XG4gICAgICAgIGxhYmVsVmFsdWUgPSB
cIlwiO1xuICAgICAgICBpZiAoKGVsLnNoaXBfc3RyYXRlZ3kgIT0gbnVsbCkgfHwgKGVsLmxvY2FsX3N0cmF0ZWd5ICE9IG51bGwpKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxkaXYgY2xhc3M9J2VkZ2UtbGFiZWwnPlwiO1xuICAgICAgICAgIGlmIChlbC5zaGlwX3N0cmF0ZWd5ICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLnRlbXBfbW9kZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiIChcIiArIGVsLnRlbXBfbW9kZSArIFwiKVwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwubG9jYWxfc3RyYXRlZ3kgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiw8YnI+XCIgKyBlbC5sb2NhbF9zdHJhdGVneTtcbiAgICAgICAgICB9XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUgPSBmdW5jdGlvbihpbmZvKSB7XG4gICAgICAgIHJldHVybiBpbmZvID09PSBcInBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwibmV4dFBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwid29ya3NldFwiIHx8IGluZm8gPT09IFwibmV4dFdvcmtzZXRcIiB8fCBpbmZv
ID09PSBcInNvbHV0aW9uU2V0XCIgfHwgaW5mbyA9PT0gXCJzb2x1dGlvbkRlbHRhXCI7XG4gICAgICB9O1xuICAgICAgZ2V0Tm9kZVR5cGUgPSBmdW5jdGlvbihlbCwgaW5mbykge1xuICAgICAgICBpZiAoaW5mbyA9PT0gXCJtaXJyb3JcIikge1xuICAgICAgICAgIHJldHVybiAnbm9kZS1taXJyb3InO1xuICAgICAgICB9IGVsc2UgaWYgKGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbykpIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtaXRlcmF0aW9uJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtbm9ybWFsJztcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNyZWF0ZUxhYmVsTm9kZSA9IGZ1bmN0aW9uKGVsLCBpbmZvLCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIHZhciBsYWJlbFZhbHVlLCBzdGVwTmFtZTtcbiAgICAgICAgbGFiZWxWYWx1ZSA9IFwiPGEgaHJlZj0nIy9qb2JzL1wiICsgam9iaWQgKyBcIi92ZXJ0ZXgvXCIgKyBlbC5pZCArIFwiJyBjbGFzcz0nbm9kZS1sYWJlbCBcIiArIGdldE5vZGVUeXBlKGVsLCBpbmZvKSArIFwiJz5cIjtcbiAgICAgICAgaWYgKGluZm8gPT09IFwibWlycm9yXCIpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPk1pcnJvciBvZiBcIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtb
mFtZSc+XCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuZGVzY3JpcHRpb24gPT09IFwiXCIpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiXCI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc3RlcE5hbWUgPSBlbC5kZXNjcmlwdGlvbjtcbiAgICAgICAgICBzdGVwTmFtZSA9IHNob3J0ZW5TdHJpbmcoc3RlcE5hbWUpO1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDQgY2xhc3M9J3N0ZXAtbmFtZSc+XCIgKyBzdGVwTmFtZSArIFwiPC9oND5cIjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuc3RlcF9mdW5jdGlvbiAhPSBudWxsKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24oZWwuaWQsIG1heFcsIG1heEgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmIChpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlwiICsgaW5mbyArIFwiIE5vZGU8L2g1PlwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwucGFyYWxsZWxpc20gIT09IFwiXCIpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+UGFyYWxsZWxpc206IFwiICsgZWwucGFyYWxsZWxpc20gKyBcIjwvaDU+XCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5vcGVyYXRvciAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIC
AgICBsYWJlbFZhbHVlICs9IFwiPGg1Pk9wZXJhdGlvbjogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLm9wZXJhdG9yX3N0cmF0ZWd5KSArIFwiPC9oNT5cIjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvYT5cIjtcbiAgICAgICAgcmV0dXJuIGxhYmVsVmFsdWU7XG4gICAgICB9O1xuICAgICAgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uID0gZnVuY3Rpb24oaWQsIG1heFcsIG1heEgpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWUsIHN2Z0lEO1xuICAgICAgICBzdmdJRCA9IFwic3ZnLVwiICsgaWQ7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIjxzdmcgY2xhc3M9J1wiICsgc3ZnSUQgKyBcIicgd2lkdGg9XCIgKyBtYXhXICsgXCIgaGVpZ2h0PVwiICsgbWF4SCArIFwiPjxnIC8+PC9zdmc+XCI7XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIHNob3J0ZW5TdHJpbmcgPSBmdW5jdGlvbihzKSB7XG4gICAgICAgIHZhciBzYnI7XG4gICAgICAgIGlmIChzLmNoYXJBdCgwKSA9PT0gXCI8XCIpIHtcbiAgICAgICAgICBzID0gcy5yZXBsYWNlKFwiPFwiLCBcIiZsdDtcIik7XG4gICAgICAgICAgcyA9IHMucmVwbGFjZShcIj5cIiwgXCImZ3Q7XCIpO1xuICAgICAgICB9XG4gICAgICAgIHNiciA9IFwiXCI7XG4gICAgICAgIHdoaWxlIChzLmxlbmd0aCA+IDMwKSB7XG4gICAgICAgICAgc2JyID0gc2JyICsgcy5zdWJzdHJpbmcoMCwgMzApICs
gXCI8YnI+XCI7XG4gICAgICAgICAgcyA9IHMuc3Vic3RyaW5nKDMwLCBzLmxlbmd0aCk7XG4gICAgICAgIH1cbiAgICAgICAgc2JyID0gc2JyICsgcztcbiAgICAgICAgcmV0dXJuIHNicjtcbiAgICAgIH07XG4gICAgICBjcmVhdGVOb2RlID0gZnVuY3Rpb24oZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIGlmIChpc1BhcmVudCA9PSBudWxsKSB7XG4gICAgICAgICAgaXNQYXJlbnQgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuaWQgPT09IGRhdGEucGFydGlhbF9zb2x1dGlvbikge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLm5leHRfcGFydGlhbF9zb2x1dGlvbikge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJj
bGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEud29ya3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwid29ya3NldFwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJ3b3Jrc2V0XCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEubmV4dF93b3Jrc2V0KSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0V29ya3NldFwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0V29ya3NldFwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLnNvbHV0aW9uX3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25TZXRcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsY
WJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25TZXRcIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5zb2x1dGlvbl9kZWx0YSkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25EZWx0YVwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwiXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjcmVhdGVFZGdlID0gZnVuY3Rpb24oZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpIHtcbiAgICAgICAgdmFyIG1pc3NpbmdOb2RlO1xuICAgICAgICBpZiAoZXhpc3RpbmdOb2Rlcy5pbmRleE9mKHByZWQuaWQpICE9PSAtMSkge1xuICAgICAgICAgIHJldHVybi
BnLnNldEVkZ2UocHJlZC5pZCwgZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UocHJlZCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIGFycm93aGVhZDogJ25vcm1hbCdcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBtaXNzaW5nTm9kZSA9IHNlYXJjaEZvck5vZGUoZGF0YSwgcHJlZC5pZCk7XG4gICAgICAgICAgaWYgKCEoIW1pc3NpbmdOb2RlIHx8IG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9PT0gdHJ1ZSkpIHtcbiAgICAgICAgICAgIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9IHRydWU7XG4gICAgICAgICAgICBnLnNldE5vZGUobWlzc2luZ05vZGUuaWQsIHtcbiAgICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShtaXNzaW5nTm9kZSwgXCJtaXJyb3JcIiksXG4gICAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKG1pc3NpbmdOb2RlLCAnbWlycm9yJylcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGcuc2V0RWRnZShtaXNzaW5nTm9kZS5pZCwgZWwuaWQsIHtcbiAgICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShtaXNzaW5nTm9kZSksXG4gICAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0
7XG4gICAgICBsb2FkSnNvblRvRGFncmUgPSBmdW5jdGlvbihnLCBkYXRhKSB7XG4gICAgICAgIHZhciBlbCwgZXhpc3RpbmdOb2RlcywgaXNQYXJlbnQsIGssIGwsIGxlbiwgbGVuMSwgbWF4SCwgbWF4VywgcHJlZCwgciwgcmVmLCBzZywgdG9JdGVyYXRlO1xuICAgICAgICBleGlzdGluZ05vZGVzID0gW107XG4gICAgICAgIGlmIChkYXRhLm5vZGVzICE9IG51bGwpIHtcbiAgICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLm5vZGVzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEuc3RlcF9mdW5jdGlvbjtcbiAgICAgICAgICBpc1BhcmVudCA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChrID0gMCwgbGVuID0gdG9JdGVyYXRlLmxlbmd0aDsgayA8IGxlbjsgaysrKSB7XG4gICAgICAgICAgZWwgPSB0b0l0ZXJhdGVba107XG4gICAgICAgICAgbWF4VyA9IDA7XG4gICAgICAgICAgbWF4SCA9IDA7XG4gICAgICAgICAgaWYgKGVsLnN0ZXBfZnVuY3Rpb24pIHtcbiAgICAgICAgICAgIHNnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoe1xuICAgICAgICAgICAgICBtdWx0aWdyYXBoOiB0cnVlLFxuICAgICAgICAgICAgICBjb21wb3VuZDogdHJ1ZVxuICAgICAgICAgICAgfSkuc2V0R3JhcGgoe1xuICAgICAgICAgICAgICBub2Rlc2VwOiAyMCxcbiAgICAgICAgICAgICAgZWRnZXNlcDogMCxcbiAgICAgICAgICAgICAgcmFua3NlcDogMjAsXG4gICAgICAgICAgICAg
IHJhbmtkaXI6IFwiTFJcIixcbiAgICAgICAgICAgICAgbWFyZ2lueDogMTAsXG4gICAgICAgICAgICAgIG1hcmdpbnk6IDEwXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHN1YmdyYXBoc1tlbC5pZF0gPSBzZztcbiAgICAgICAgICAgIGxvYWRKc29uVG9EYWdyZShzZywgZWwpO1xuICAgICAgICAgICAgciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpO1xuICAgICAgICAgICAgZDN0bXBTdmcuc2VsZWN0KCdnJykuY2FsbChyLCBzZyk7XG4gICAgICAgICAgICBtYXhXID0gc2cuZ3JhcGgoKS53aWR0aDtcbiAgICAgICAgICAgIG1heEggPSBzZy5ncmFwaCgpLmhlaWdodDtcbiAgICAgICAgICAgIGFuZ3VsYXIuZWxlbWVudChtYWluVG1wRWxlbWVudCkuZW1wdHkoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY3JlYXRlTm9kZShnLCBkYXRhLCBlbCwgaXNQYXJlbnQsIG1heFcsIG1heEgpO1xuICAgICAgICAgIGV4aXN0aW5nTm9kZXMucHVzaChlbC5pZCk7XG4gICAgICAgICAgaWYgKGVsLmlucHV0cyAhPSBudWxsKSB7XG4gICAgICAgICAgICByZWYgPSBlbC5pbnB1dHM7XG4gICAgICAgICAgICBmb3IgKGwgPSAwLCBsZW4xID0gcmVmLmxlbmd0aDsgbCA8IGxlbjE7IGwrKykge1xuICAgICAgICAgICAgICBwcmVkID0gcmVmW2xdO1xuICAgICAgICAgICAgICBjcmVhdGVFZGdlKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cb
iAgICAgICAgcmV0dXJuIGc7XG4gICAgICB9O1xuICAgICAgc2VhcmNoRm9yTm9kZSA9IGZ1bmN0aW9uKGRhdGEsIG5vZGVJRCkge1xuICAgICAgICB2YXIgZWwsIGksIGo7XG4gICAgICAgIGZvciAoaSBpbiBkYXRhLm5vZGVzKSB7XG4gICAgICAgICAgZWwgPSBkYXRhLm5vZGVzW2ldO1xuICAgICAgICAgIGlmIChlbC5pZCA9PT0gbm9kZUlEKSB7XG4gICAgICAgICAgICByZXR1cm4gZWw7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uICE9IG51bGwpIHtcbiAgICAgICAgICAgIGZvciAoaiBpbiBlbC5zdGVwX2Z1bmN0aW9uKSB7XG4gICAgICAgICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uW2pdLmlkID09PSBub2RlSUQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZWwuc3RlcF9mdW5jdGlvbltqXTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGRyYXdHcmFwaCA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGcsIGksIG5ld1NjYWxlLCByZW5kZXJlciwgc2csIHhDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXQ7XG4gICAgICAgIGcgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7XG4gICAgICAgICAgbXVsdGlncmFwaDogdHJ1ZSxcbiAgICAgICAgICBjb21wb3VuZDogdHJ1ZVxuICAgICAgICB9KS5zZXRHcmFwaCh7XG4gICAgICAgICAgbm9kZXNlcDogNzAsXG4gICAgIC
AgICAgZWRnZXNlcDogMCxcbiAgICAgICAgICByYW5rc2VwOiA1MCxcbiAgICAgICAgICByYW5rZGlyOiBcIkxSXCIsXG4gICAgICAgICAgbWFyZ2lueDogNDAsXG4gICAgICAgICAgbWFyZ2lueTogNDBcbiAgICAgICAgfSk7XG4gICAgICAgIGxvYWRKc29uVG9EYWdyZShnLCBkYXRhKTtcbiAgICAgICAgcmVuZGVyZXIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKTtcbiAgICAgICAgZDNtYWluU3ZnRy5jYWxsKHJlbmRlcmVyLCBnKTtcbiAgICAgICAgZm9yIChpIGluIHN1YmdyYXBocykge1xuICAgICAgICAgIHNnID0gc3ViZ3JhcGhzW2ldO1xuICAgICAgICAgIGQzbWFpblN2Zy5zZWxlY3QoJ3N2Zy5zdmctJyArIGkgKyAnIGcnKS5jYWxsKHJlbmRlcmVyLCBzZyk7XG4gICAgICAgIH1cbiAgICAgICAgbmV3U2NhbGUgPSAwLjU7XG4gICAgICAgIHhDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLndpZHRoKCkgLSBnLmdyYXBoKCkud2lkdGggKiBuZXdTY2FsZSkgLyAyKTtcbiAgICAgICAgeUNlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkuaGVpZ2h0KCkgLSBnLmdyYXBoKCkuaGVpZ2h0ICogbmV3U2NhbGUpIC8gMik7XG4gICAgICAgIG1haW5ab29tLnNjYWxlKG5ld1NjYWxlKS50cmFuc2xhdGUoW3hDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXRdKTtcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyKFwidHJ
hbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgeENlbnRlck9mZnNldCArIFwiLCBcIiArIHlDZW50ZXJPZmZzZXQgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICBtYWluWm9vbS5vbihcInpvb21cIiwgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgdmFyIGV2O1xuICAgICAgICAgIGV2ID0gZDMuZXZlbnQ7XG4gICAgICAgICAgcmV0dXJuIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIGV2LnRyYW5zbGF0ZSArIFwiKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIpXCIpO1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIG1haW5ab29tKGQzbWFpblN2Zyk7XG4gICAgICB9O1xuICAgICAgc2NvcGUuJHdhdGNoKGF0dHJzLnBsYW4sIGZ1bmN0aW9uKG5ld1BsYW4pIHtcbiAgICAgICAgaWYgKG5ld1BsYW4pIHtcbiAgICAgICAgICByZXR1cm4gZHJhd0dyYXBoKG5ld1BsYW4pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH07XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNo
aXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4uc2VydmljZSAnSm9ic1NlcnZpY2UnLCAoJGh0dHAsIGZsaW5rQ29uZmlnLCAkbG9nLCBhbU1vbWVudCwgJHEsICR0aW1lb3V0KSA
<TRUNCATED>
[08/51] [abbrv] flink git commit: [FLINK-2547] [web dashboard]
Updated web dashboard after request/response changes
Posted by se...@apache.org.
[FLINK-2547] [web dashboard] Updated web dashboard after request/response changes
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/562573df
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/562573df
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/562573df
Branch: refs/heads/master
Commit: 562573df790f4c07f456e2999f884d865a4c4f7c
Parents: ff28981
Author: Piotr Godek <pi...@gmail.com>
Authored: Wed Aug 19 14:46:41 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:49 2015 +0200
----------------------------------------------------------------------
.../app/partials/jobs/job.plan.node.jade | 24 ++++-----
.../web-dashboard/app/scripts/index.coffee | 2 +-
.../app/scripts/modules/jobs/jobs.dir.coffee | 2 +-
.../app/scripts/modules/jobs/jobs.svc.coffee | 6 +--
flink-runtime-web/web-dashboard/web/js/index.js | 57 +++++++-------------
.../web/partials/jobs/job.plan.node.html | 25 ++++-----
6 files changed, 44 insertions(+), 72 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/562573df/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
index 82e61db..0ddd457 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.jade
@@ -39,10 +39,6 @@
th Status
tbody
- tr(colspan="2")
- td
- center
- i WARNING! This is a sample
tr(ng-repeat="vertex in node.vertex.groupvertex.groupmembers")
td {{vertex.vertexname}}
td
@@ -52,25 +48,25 @@
.row(ng-if="node")
.col-sm-6.col-md-4
- table.table.table-properties(ng-if="node.global_properties")
+ table.table.table-properties(ng-if="node.optimizer_properties.global_properties")
thead
tr
th(colspan="2")
| Global Data Properties
tbody
- tr(ng-repeat="property in node.global_properties")
+ tr(ng-repeat="property in node.optimizer_properties.global_properties")
td {{property.name}}
td(table-property value="property.value")
- table.table.table-properties(ng-if="node.local_properties")
+ table.table.table-properties(ng-if="node.optimizer_properties.local_properties")
thead
tr
th(colspan="2")
| Local Data Properties
tbody
- tr(ng-repeat="property in node.local_properties")
+ tr(ng-repeat="property in node.optimizer_properties.local_properties")
td {{property.name}}
td(table-property value="property.value")
@@ -108,37 +104,37 @@
td(table-property value="node.parallelism")
- table.table.table-properties(ng-if="node.estimates")
+ table.table.table-properties(ng-if="node.optimizer_properties.estimates")
thead
tr
th(colspan="2")
| Size Estimates
tbody
- tr(ng-repeat="property in node.estimates")
+ tr(ng-repeat="property in node.optimizer_properties.estimates")
td {{property.name}}
td(table-property value="property.value")
.col-sm-6.col-md-4
.visible-xs.visible-sm
- table.table.table-properties(ng-if="node.estimates")
+ table.table.table-properties(ng-if="node.optimizer_properties.estimates")
thead
tr
th(colspan="2")
| Size Estimates
tbody
- tr(ng-repeat="property in node.estimates")
+ tr(ng-repeat="property in node.optimizer_properties.estimates")
td {{property.name}}
td(table-property value="property.value")
- table.table.table-properties(ng-if="node.costs")
+ table.table.table-properties(ng-if="node.optimizer_properties.costs")
thead
tr
th(colspan="2")
| Cost Estimates
tbody
- tr(ng-repeat="property in node.costs")
+ tr(ng-repeat="property in node.optimizer_properties.costs")
td {{property.name}}
td(table-property value="property.value")
http://git-wip-us.apache.org/repos/asf/flink/blob/562573df/flink-runtime-web/web-dashboard/app/scripts/index.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/index.coffee b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
index 00d828e..3cea143 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
@@ -86,7 +86,7 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
controller: 'JobPlanController'
.state "single-job.plan.node",
- url: "/{nodeid:int}"
+ url: "/vertex/{nodeid}"
views:
node:
templateUrl: "partials/jobs/job.plan.node.html"
http://git-wip-us.apache.org/repos/asf/flink/blob/562573df/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
index ee309ad..f67ec32 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.dir.coffee
@@ -239,7 +239,7 @@ angular.module('flinkApp')
# creates the label of a node, in info is stored, whether it is a special node (like a mirror in an iteration)
createLabelNode = (el, info, maxW, maxH) ->
- labelValue = "<a href='#/jobs/" + jobid + "/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>"
+ labelValue = "<a href='#/jobs/" + jobid + "/vertex/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>"
# Nodename
if info is "mirror"
http://git-wip-us.apache.org/repos/asf/flink/blob/562573df/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index 3c5c385..6640d60 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -135,8 +135,6 @@ angular.module('flinkApp')
@getNode = (nodeid) ->
seekNode = (nodeid, data) ->
- nodeid = parseInt(nodeid)
-
for node in data
return node if node.id is nodeid
sub = seekNode(nodeid, node.step_function) if node.step_function
@@ -157,8 +155,8 @@ angular.module('flinkApp')
$q.all([deferreds.plan.promise, deferreds.job.promise]).then (data) =>
foundNode = seekNode(nodeid, currentPlan.nodes)
- # TODO link to real vertex. for now there is no way to get the right one, so we are showing the first one - just for testing
- @getVertex(currentJob.jid, currentJob.oldV.groupvertices[0].groupvertexid).then (vertex) ->
+ # @getVertex(currentJob.jid, currentJob.oldV.groupvertices[0].groupvertexid).then (vertex) ->
+ @getVertex(currentJob.jid, nodeid).then (vertex) ->
foundNode.vertex = vertex
deferred.resolve(foundNode)
[48/51] [abbrv] flink git commit: [FLINK-2687] [monitoring api] Add
handlers for subtask details and accumulators
Posted by se...@apache.org.
[FLINK-2687] [monitoring api] Add handlers for subtask details and accumulators
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/b3f79172
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/b3f79172
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/b3f79172
Branch: refs/heads/master
Commit: b3f791727c0713251a4b02a766eb177eb9327113
Parents: 616b7d5
Author: Stephan Ewen <se...@apache.org>
Authored: Thu Sep 10 15:47:28 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:52 2015 +0200
----------------------------------------------------------------------
flink-runtime-web/pom.xml | 18 -
.../runtime/webmonitor/WebRuntimeMonitor.java | 26 +-
.../AbstractExecutionGraphRequestHandler.java | 4 +-
.../AbstractJobVertexRequestHandler.java | 62 ++
.../AbstractSubtaskAttemptRequestHandler.java | 68 ++
.../handlers/AbstractSubtaskRequestHandler.java | 62 ++
.../handlers/JobVertexAccumulatorsHandler.java | 24 +-
.../handlers/JobVertexDetailsHandler.java | 32 +-
.../webmonitor/handlers/JsonFactory.java | 7 +-
.../SubtaskCurrentAttemptDetailsHandler.java | 39 ++
...taskExecutionAttemptAccumulatorsHandler.java | 68 ++
.../SubtaskExecutionAttemptDetailsHandler.java | 101 +++
.../SubtasksAllAccumulatorsHandler.java | 83 +++
.../handlers/SubtasksTimesHandler.java | 47 +-
.../legacy/JobManagerInfoHandler.java | 702 -------------------
.../runtime/webmonitor/legacy/JsonFactory.java | 112 ---
.../runtime/webmonitor/runner/TestRunner.java | 199 ------
.../StringifiedAccumulatorResult.java | 31 +-
.../flink/runtime/executiongraph/Execution.java | 14 +
.../runtime/executiongraph/ExecutionVertex.java | 9 +
20 files changed, 579 insertions(+), 1129 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/pom.xml
----------------------------------------------------------------------
diff --git a/flink-runtime-web/pom.xml b/flink-runtime-web/pom.xml
index 0a05111..804e6da 100644
--- a/flink-runtime-web/pom.xml
+++ b/flink-runtime-web/pom.xml
@@ -90,24 +90,6 @@ under the License.
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
-
- <!-- ===================================================
- Testing
- =================================================== -->
-
- <dependency>
- <groupId>org.apache.flink</groupId>
- <artifactId>flink-test-utils</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>org.apache.flink</groupId>
- <artifactId>flink-java-examples</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
</dependencies>
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
index 8aa25dd..4633dcf 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
@@ -47,8 +47,11 @@ import org.apache.flink.runtime.webmonitor.handlers.JobVertexDetailsHandler;
import org.apache.flink.runtime.webmonitor.handlers.RequestHandler;
import org.apache.flink.runtime.webmonitor.handlers.CurrentJobIdsHandler;
import org.apache.flink.runtime.webmonitor.handlers.ClusterOverviewHandler;
+import org.apache.flink.runtime.webmonitor.handlers.SubtaskCurrentAttemptDetailsHandler;
+import org.apache.flink.runtime.webmonitor.handlers.SubtaskExecutionAttemptAccumulatorsHandler;
+import org.apache.flink.runtime.webmonitor.handlers.SubtaskExecutionAttemptDetailsHandler;
+import org.apache.flink.runtime.webmonitor.handlers.SubtasksAllAccumulatorsHandler;
import org.apache.flink.runtime.webmonitor.handlers.SubtasksTimesHandler;
-import org.apache.flink.runtime.webmonitor.legacy.JobManagerInfoHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,14 +64,16 @@ import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
/**
- * The root component of the web runtime monitor.
- *
- * <p>The web runtime monitor is based in Netty HTTP. It uses the Netty-Router library to route
+ * The root component of the web runtime monitor. This class starts the web server and creates
+ * all request handlers for the REST API.
+ * <p>
+ * The web runtime monitor is based in Netty HTTP. It uses the Netty-Router library to route
* HTTP requests of different paths to different response handlers. In addition, it serves the static
- * files of the web frontend, such as HTML, CSS, or JS files.</p>
+ * files of the web frontend, such as HTML, CSS, or JS files.
*/
public class WebRuntimeMonitor implements WebMonitor {
+ /** By default, all requests to the JobManager have a timeout of 10 seconds */
public static final FiniteDuration DEFAULT_REQUEST_TIMEOUT = new FiniteDuration(10, TimeUnit.SECONDS);
/** Logger for web frontend startup / shutdown messages */
@@ -146,18 +151,17 @@ public class WebRuntimeMonitor implements WebMonitor {
.GET("/jobs/:jobid/vertices/:vertexid/subtasktimes", handler(new SubtasksTimesHandler(currentGraphs)))
.GET("/jobs/:jobid/vertices/:vertexid/accumulators", handler(new JobVertexAccumulatorsHandler(currentGraphs)))
-// .GET("/jobs/:jobid/vertices/:vertexid/subtasks/:subtaskid/:attempt", handler(null))
-// .GET("/jobs/:jobid/vertices/:vertexid/subtasks/:subtaskid/:attempt/accumulators", handler(null))
+ .GET("/jobs/:jobid/vertices/:vertexid/subtasks/accumulators", handler(new SubtasksAllAccumulatorsHandler(currentGraphs)))
+ .GET("/jobs/:jobid/vertices/:vertexid/subtasks/:subtasknum", handler(new SubtaskCurrentAttemptDetailsHandler(currentGraphs)))
+ .GET("/jobs/:jobid/vertices/:vertexid/subtasks/:subtasknum/attempts/:attempt", handler(new SubtaskExecutionAttemptDetailsHandler(currentGraphs)))
+ .GET("/jobs/:jobid/vertices/:vertexid/subtasks/:subtasknum/attempts/:attempt/accumulators", handler(new SubtaskExecutionAttemptAccumulatorsHandler(currentGraphs)))
.GET("/jobs/:jobid/plan", handler(new JobPlanHandler(currentGraphs)))
.GET("/jobs/:jobid/config", handler(new JobConfigHandler(currentGraphs)))
.GET("/jobs/:jobid/exceptions", handler(new JobExceptionsHandler(currentGraphs)))
.GET("/jobs/:jobid/accumulators", handler(new JobAccumulatorsHandler(currentGraphs)))
- // the handler for the legacy requests
- .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
-
- // this handler serves all the static contents
+ // this handler serves all the static contents
.GET("/:*", new StaticFileServerHandler(webRootDir));
}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java
index 01f48e4..d9b4e59 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractExecutionGraphRequestHandler.java
@@ -40,7 +40,7 @@ public abstract class AbstractExecutionGraphRequestHandler implements RequestHan
@Override
- public String handleRequest(Map<String, String> params) throws Exception {
+ public final String handleRequest(Map<String, String> params) throws Exception {
String jidString = params.get("jobid");
if (jidString == null) {
throw new RuntimeException("JobId parameter missing");
@@ -56,7 +56,7 @@ public abstract class AbstractExecutionGraphRequestHandler implements RequestHan
ExecutionGraph eg = executionGraphHolder.getExecutionGraph(jid);
if (eg == null) {
- throw new NotFoundException("Could not find execution graph for job " + jid);
+ throw new NotFoundException("Could not find job with id " + jid);
}
return handleRequest(eg, params);
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractJobVertexRequestHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractJobVertexRequestHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractJobVertexRequestHandler.java
new file mode 100644
index 0000000..5b12907
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractJobVertexRequestHandler.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import org.apache.flink.runtime.executiongraph.ExecutionGraph;
+import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
+import org.apache.flink.runtime.jobgraph.JobVertexID;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.util.Map;
+
+/**
+ * Base class for request handlers whose response depends on a specific job vertex (defined
+ * via the "vertexid" parameter) in a specific job, defined via (defined voa the "jobid" parameter).
+ */
+public abstract class AbstractJobVertexRequestHandler extends AbstractExecutionGraphRequestHandler {
+
+ public AbstractJobVertexRequestHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public final String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
+ final String vidString = params.get("vertexid");
+ if (vidString == null) {
+ throw new IllegalArgumentException("vertexId parameter missing");
+ }
+
+ final JobVertexID vid;
+ try {
+ vid = JobVertexID.fromHexString(vidString);
+ }
+ catch (Exception e) {
+ throw new IllegalArgumentException("Invalid JobVertexID string '" + vidString + "': " + e.getMessage());
+ }
+
+ final ExecutionJobVertex jobVertex = graph.getJobVertex(vid);
+ if (jobVertex == null) {
+ throw new IllegalArgumentException("No vertex with ID '" + vidString + "' exists.");
+ }
+
+ return handleRequest(jobVertex, params);
+ }
+
+ public abstract String handleRequest(ExecutionJobVertex jobVertex, Map<String, String> params) throws Exception;
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractSubtaskAttemptRequestHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractSubtaskAttemptRequestHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractSubtaskAttemptRequestHandler.java
new file mode 100644
index 0000000..672df16
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractSubtaskAttemptRequestHandler.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import org.apache.flink.runtime.executiongraph.Execution;
+import org.apache.flink.runtime.executiongraph.ExecutionVertex;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.util.Map;
+
+/**
+ * Base class for request handlers whose response depends on a specific subtask execution attempt
+ * (defined via the "attempt" parameter) of a specific subtask (defined via the
+ * "subtasknum" parameter) in a specific job vertex (defined via the "vertexid" parameter) in a
+ * specific job, defined via (defined voa the "jobid" parameter).
+ */
+public abstract class AbstractSubtaskAttemptRequestHandler extends AbstractSubtaskRequestHandler {
+
+ public AbstractSubtaskAttemptRequestHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionVertex vertex, Map<String, String> params) throws Exception {
+ final String attemptNumberString = params.get("attempt");
+ if (attemptNumberString == null) {
+ throw new RuntimeException("Attempt number parameter missing");
+ }
+
+ final int attempt;
+ try {
+ attempt = Integer.parseInt(attemptNumberString);
+ }
+ catch (NumberFormatException e) {
+ throw new RuntimeException("Invalid attempt number parameter");
+ }
+
+ final Execution currentAttempt = vertex.getCurrentExecutionAttempt();
+ if (attempt == currentAttempt.getAttemptNumber()) {
+ return handleRequest(currentAttempt, params);
+ }
+ else if (attempt >= 0 && attempt < currentAttempt.getAttemptNumber()) {
+ Execution exec = vertex.getPriorExecutionAttempt(attempt);
+ return handleRequest(exec, params);
+ }
+ else {
+ throw new RuntimeException("Attempt does not exist: " + attempt);
+ }
+ }
+
+ public abstract String handleRequest(Execution execAttempt, Map<String, String> params) throws Exception;
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractSubtaskRequestHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractSubtaskRequestHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractSubtaskRequestHandler.java
new file mode 100644
index 0000000..90866c6
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/AbstractSubtaskRequestHandler.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
+import org.apache.flink.runtime.executiongraph.ExecutionVertex;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.util.Map;
+
+/**
+ * Base class for request handlers whose response depends on a specific subtask (defined via the
+ * "subtasknum" parameter) in a specific job vertex (defined via the "vertexid" parameter) in a
+ * specific job, defined via (defined voa the "jobid" parameter).
+ */
+public abstract class AbstractSubtaskRequestHandler extends AbstractJobVertexRequestHandler {
+
+ public AbstractSubtaskRequestHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public final String handleRequest(ExecutionJobVertex jobVertex, Map<String, String> params) throws Exception {
+ final String subtaskNumberString = params.get("subtasknum");
+ if (subtaskNumberString == null) {
+ throw new RuntimeException("Subtask number parameter missing");
+ }
+
+ final int subtask;
+ try {
+ subtask = Integer.parseInt(subtaskNumberString);
+ }
+ catch (NumberFormatException e) {
+ throw new RuntimeException("Invalid subtask number parameter");
+ }
+
+ if (subtask < 0 || subtask >= jobVertex.getParallelism()) {
+ throw new RuntimeException("subtask does not exist: " + subtask);
+ }
+
+ final ExecutionVertex vertex = jobVertex.getTaskVertices()[subtask];
+ return handleRequest(vertex, params);
+ }
+
+ public abstract String handleRequest(ExecutionVertex vertex, Map<String, String> params) throws Exception;
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexAccumulatorsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexAccumulatorsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexAccumulatorsHandler.java
index 8554a31..ed2c541 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexAccumulatorsHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexAccumulatorsHandler.java
@@ -21,41 +21,21 @@ package org.apache.flink.runtime.webmonitor.handlers;
import com.fasterxml.jackson.core.JsonGenerator;
import org.apache.flink.runtime.accumulators.StringifiedAccumulatorResult;
-import org.apache.flink.runtime.executiongraph.ExecutionGraph;
import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
-import org.apache.flink.runtime.jobgraph.JobVertexID;
import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
import java.io.StringWriter;
import java.util.Map;
-public class JobVertexAccumulatorsHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+public class JobVertexAccumulatorsHandler extends AbstractJobVertexRequestHandler implements RequestHandler.JsonResponse {
public JobVertexAccumulatorsHandler(ExecutionGraphHolder executionGraphHolder) {
super(executionGraphHolder);
}
@Override
- public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
- String vidString = params.get("vertexid");
- if (vidString == null) {
- throw new IllegalArgumentException("vertexId parameter missing");
- }
-
- JobVertexID vid;
- try {
- vid = JobVertexID.fromHexString(vidString);
- }
- catch (Exception e) {
- throw new IllegalArgumentException("Invalid JobVertexID string '" + vidString + "': " + e.getMessage());
- }
-
- ExecutionJobVertex jobVertex = graph.getJobVertex(vid);
- if (jobVertex == null) {
- throw new IllegalArgumentException("No vertex with ID '" + vidString + "' exists.");
- }
-
+ public String handleRequest(ExecutionJobVertex jobVertex, Map<String, String> params) throws Exception {
StringifiedAccumulatorResult[] accs = jobVertex.getAggregatedUserAccumulatorsStringified();
StringWriter writer = new StringWriter();
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
index 7815f49..21e2868 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobVertexDetailsHandler.java
@@ -19,47 +19,31 @@
package org.apache.flink.runtime.webmonitor.handlers;
import com.fasterxml.jackson.core.JsonGenerator;
+
import org.apache.flink.api.common.accumulators.Accumulator;
import org.apache.flink.api.common.accumulators.LongCounter;
import org.apache.flink.runtime.accumulators.AccumulatorRegistry;
import org.apache.flink.runtime.execution.ExecutionState;
-import org.apache.flink.runtime.executiongraph.ExecutionGraph;
import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
import org.apache.flink.runtime.executiongraph.ExecutionVertex;
import org.apache.flink.runtime.instance.InstanceConnectionInfo;
-import org.apache.flink.runtime.jobgraph.JobVertexID;
import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
import java.io.StringWriter;
import java.util.Map;
-
-public class JobVertexDetailsHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+/**
+ * A request handler that provides the details of a job vertex, including id, name, parallelism,
+ * and the runtime and metrics of all its subtasks.
+ */
+public class JobVertexDetailsHandler extends AbstractJobVertexRequestHandler implements RequestHandler.JsonResponse {
public JobVertexDetailsHandler(ExecutionGraphHolder executionGraphHolder) {
super(executionGraphHolder);
}
@Override
- public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
- String vidString = params.get("vertexid");
- if (vidString == null) {
- throw new IllegalArgumentException("vertexId parameter missing");
- }
-
- JobVertexID vid;
- try {
- vid = JobVertexID.fromHexString(vidString);
- }
- catch (Exception e) {
- throw new IllegalArgumentException("Invalid JobVertexID string '" + vidString + "': " + e.getMessage());
- }
-
- ExecutionJobVertex jobVertex = graph.getJobVertex(vid);
- if (jobVertex == null) {
- throw new IllegalArgumentException("No vertex with ID '" + vidString + "' exists.");
- }
-
+ public String handleRequest(ExecutionJobVertex jobVertex, Map<String, String> params) throws Exception {
final long now = System.currentTimeMillis();
StringWriter writer = new StringWriter();
@@ -123,6 +107,8 @@ public class JobVertexDetailsHandler extends AbstractExecutionGraphRequestHandle
gen.writeEndObject();
gen.writeEndObject();
+
+ num++;
}
gen.writeEndArray();
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JsonFactory.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JsonFactory.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JsonFactory.java
index cb83361..e886532 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JsonFactory.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JsonFactory.java
@@ -18,9 +18,14 @@
package org.apache.flink.runtime.webmonitor.handlers;
-
+/**
+ * A holder for the singleton Jackson JSON factory. Since the Jackson's JSON factory object
+ * is a heavyweight object that is encouraged to be shared, we use a singleton instance across
+ * all request handlers.
+ */
public class JsonFactory {
+ /** The singleton Jackson JSON factory. */
public static final com.fasterxml.jackson.core.JsonFactory jacksonFactory =
new com.fasterxml.jackson.core.JsonFactory();
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskCurrentAttemptDetailsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskCurrentAttemptDetailsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskCurrentAttemptDetailsHandler.java
new file mode 100644
index 0000000..d301bd1
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskCurrentAttemptDetailsHandler.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import org.apache.flink.runtime.executiongraph.ExecutionVertex;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.util.Map;
+
+/**
+ * Request handler providing details about a single task execution attempt.
+ */
+public class SubtaskCurrentAttemptDetailsHandler extends SubtaskExecutionAttemptDetailsHandler {
+
+ public SubtaskCurrentAttemptDetailsHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionVertex vertex, Map<String, String> params) throws Exception {
+ return handleRequest(vertex.getCurrentExecutionAttempt(), params);
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskExecutionAttemptAccumulatorsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskExecutionAttemptAccumulatorsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskExecutionAttemptAccumulatorsHandler.java
new file mode 100644
index 0000000..f177b47
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskExecutionAttemptAccumulatorsHandler.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import org.apache.flink.runtime.accumulators.StringifiedAccumulatorResult;
+import org.apache.flink.runtime.executiongraph.Execution;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Base class for request handlers whose response depends on a specific job vertex (defined
+ * via the "vertexid" parameter) in a specific job, defined via (defined voa the "jobid" parameter).
+ */
+public class SubtaskExecutionAttemptAccumulatorsHandler extends AbstractSubtaskAttemptRequestHandler implements RequestHandler.JsonResponse {
+
+ public SubtaskExecutionAttemptAccumulatorsHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(Execution execAttempt, Map<String, String> params) throws Exception {
+ final StringifiedAccumulatorResult[] accs = execAttempt.getUserAccumulatorsStringified();
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+
+ gen.writeNumberField("subtask", execAttempt.getVertex().getSubTaskIndex());
+ gen.writeNumberField("attempt", execAttempt.getAttemptNumber());
+ gen.writeStringField("id", execAttempt.getAttemptId().toShortString());
+
+ gen.writeArrayFieldStart("user-accumulators");
+ for (StringifiedAccumulatorResult acc : accs) {
+ gen.writeStartObject();
+ gen.writeStringField("name", acc.getName());
+ gen.writeStringField("type", acc.getType());
+ gen.writeStringField("value", acc.getValue());
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskExecutionAttemptDetailsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskExecutionAttemptDetailsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskExecutionAttemptDetailsHandler.java
new file mode 100644
index 0000000..41aa929
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtaskExecutionAttemptDetailsHandler.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import org.apache.flink.api.common.accumulators.Accumulator;
+import org.apache.flink.api.common.accumulators.LongCounter;
+import org.apache.flink.runtime.accumulators.AccumulatorRegistry;
+import org.apache.flink.runtime.execution.ExecutionState;
+import org.apache.flink.runtime.executiongraph.Execution;
+import org.apache.flink.runtime.instance.InstanceConnectionInfo;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Request handler providing details about a single task execution attempt.
+ */
+public class SubtaskExecutionAttemptDetailsHandler extends AbstractSubtaskAttemptRequestHandler implements RequestHandler.JsonResponse {
+
+ public SubtaskExecutionAttemptDetailsHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(Execution execAttempt, Map<String, String> params) throws Exception {
+ final ExecutionState status = execAttempt.getState();
+ final long now = System.currentTimeMillis();
+
+ InstanceConnectionInfo location = execAttempt.getAssignedResourceLocation();
+ String locationString = location == null ? "(unassigned)" : location.getHostname();
+
+ long startTime = execAttempt.getStateTimestamp(ExecutionState.DEPLOYING);
+ if (startTime == 0) {
+ startTime = -1;
+ }
+ long endTime = status.isTerminal() ? execAttempt.getStateTimestamp(status) : -1;
+ long duration = startTime > 0 ? ((endTime > 0 ? endTime : now) - startTime) : -1;
+
+ Map<AccumulatorRegistry.Metric, Accumulator<?, ?>> metrics = execAttempt.getFlinkAccumulators();
+ LongCounter readBytes;
+ LongCounter writeBytes;
+ LongCounter readRecords;
+ LongCounter writeRecords;
+
+ if (metrics != null) {
+ readBytes = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_BYTES_IN);
+ writeBytes = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_BYTES_OUT);
+ readRecords = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_RECORDS_IN);
+ writeRecords = (LongCounter) metrics.get(AccumulatorRegistry.Metric.NUM_RECORDS_OUT);
+ }
+ else {
+ readBytes = null;
+ writeBytes = null;
+ readRecords = null;
+ writeRecords = null;
+ }
+
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+ gen.writeNumberField("subtask", execAttempt.getVertex().getSubTaskIndex());
+ gen.writeStringField("status", status.name());
+ gen.writeNumberField("attempt", execAttempt.getAttemptNumber());
+ gen.writeStringField("host", locationString);
+ gen.writeNumberField("start-time", startTime);
+ gen.writeNumberField("end-time", endTime);
+ gen.writeNumberField("duration", duration);
+
+ gen.writeObjectFieldStart("metrics");
+ gen.writeNumberField("read-bytes", readBytes != null ? readBytes.getLocalValuePrimitive() : -1L);
+ gen.writeNumberField("write-bytes", writeBytes != null ? writeBytes.getLocalValuePrimitive() : -1L);
+ gen.writeNumberField("read-records", readRecords != null ? readRecords.getLocalValuePrimitive() : -1L);
+ gen.writeNumberField("write-records",writeRecords != null ? writeRecords.getLocalValuePrimitive() : -1L);
+ gen.writeEndObject();
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksAllAccumulatorsHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksAllAccumulatorsHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksAllAccumulatorsHandler.java
new file mode 100644
index 0000000..4f97292
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksAllAccumulatorsHandler.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import org.apache.flink.runtime.accumulators.StringifiedAccumulatorResult;
+import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
+import org.apache.flink.runtime.executiongraph.ExecutionVertex;
+import org.apache.flink.runtime.instance.InstanceConnectionInfo;
+import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+/**
+ * Request handler that returns the accumulators for all subtasks of job vertex.
+ */
+public class SubtasksAllAccumulatorsHandler extends AbstractJobVertexRequestHandler implements RequestHandler.JsonResponse {
+
+ public SubtasksAllAccumulatorsHandler(ExecutionGraphHolder executionGraphHolder) {
+ super(executionGraphHolder);
+ }
+
+ @Override
+ public String handleRequest(ExecutionJobVertex jobVertex, Map<String, String> params) throws Exception {
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+ gen.writeStringField("id", jobVertex.getJobVertexId().toString());
+ gen.writeNumberField("parallelism", jobVertex.getParallelism());
+
+ gen.writeArrayFieldStart("subtasks");
+
+ int num = 0;
+ for (ExecutionVertex vertex : jobVertex.getTaskVertices()) {
+
+ InstanceConnectionInfo location = vertex.getCurrentAssignedResourceLocation();
+ String locationString = location == null ? "(unassigned)" : location.getHostname();
+
+ gen.writeStartObject();
+
+ gen.writeNumberField("subtask", num++);
+ gen.writeNumberField("attempt", vertex.getCurrentExecutionAttempt().getAttemptNumber());
+ gen.writeStringField("host", locationString);
+
+ StringifiedAccumulatorResult[] accs = vertex.getCurrentExecutionAttempt().getUserAccumulatorsStringified();
+ gen.writeArrayFieldStart("user-accumulators");
+ for (StringifiedAccumulatorResult acc : accs) {
+ gen.writeStartObject();
+ gen.writeStringField("name", acc.getName());
+ gen.writeStringField("type", acc.getType());
+ gen.writeStringField("value", acc.getValue());
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+
+ gen.writeEndObject();
+ }
+ gen.writeEndArray();
+
+ gen.writeEndObject();
+ gen.close();
+ return writer.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
index 8cd7b44..b6fa136 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/SubtasksTimesHandler.java
@@ -19,21 +19,21 @@
package org.apache.flink.runtime.webmonitor.handlers;
import com.fasterxml.jackson.core.JsonGenerator;
+
import org.apache.flink.runtime.execution.ExecutionState;
-import org.apache.flink.runtime.executiongraph.ExecutionGraph;
import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
import org.apache.flink.runtime.executiongraph.ExecutionVertex;
import org.apache.flink.runtime.instance.InstanceConnectionInfo;
-import org.apache.flink.runtime.jobgraph.JobVertexID;
import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
import java.io.StringWriter;
import java.util.Map;
/**
- * Request handler that returns the JSON program plan of a job graph.
+ * Request handler that returns the state transition timestamps for all subtasks, plus their
+ * location and duration.
*/
-public class SubtasksTimesHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
+public class SubtasksTimesHandler extends AbstractJobVertexRequestHandler implements RequestHandler.JsonResponse {
public SubtasksTimesHandler(ExecutionGraphHolder executionGraphHolder) {
@@ -41,25 +41,8 @@ public class SubtasksTimesHandler extends AbstractExecutionGraphRequestHandler i
}
@Override
- public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
- String vidString = params.get("vertexid");
- if (vidString == null) {
- throw new IllegalArgumentException("vertexId parameter missing");
- }
-
- JobVertexID vid;
- try {
- vid = JobVertexID.fromHexString(vidString);
- }
- catch (Exception e) {
- throw new IllegalArgumentException("Invalid JobVertexID string '" + vidString + "': " + e.getMessage());
- }
-
- ExecutionJobVertex jobVertex = graph.getJobVertex(vid);
- if (jobVertex == null) {
- throw new IllegalArgumentException("No vertex with ID '" + vidString + "' exists.");
- }
-
+ public String handleRequest(ExecutionJobVertex jobVertex, Map<String, String> params) throws Exception {
+ final long now = System.currentTimeMillis();
StringWriter writer = new StringWriter();
JsonGenerator gen = JsonFactory.jacksonFactory.createJsonGenerator(writer);
@@ -68,25 +51,37 @@ public class SubtasksTimesHandler extends AbstractExecutionGraphRequestHandler i
gen.writeStringField("id", jobVertex.getJobVertexId().toString());
gen.writeStringField("name", jobVertex.getJobVertex().getName());
- gen.writeNumberField("now", System.currentTimeMillis());
+ gen.writeNumberField("now", now);
gen.writeArrayFieldStart("subtasks");
int num = 0;
for (ExecutionVertex vertex : jobVertex.getTaskVertices()) {
+
+ long[] timestamps = vertex.getCurrentExecutionAttempt().getStateTimestamps();
+ ExecutionState status = vertex.getExecutionState();
+
+ long scheduledTime = timestamps[ExecutionState.SCHEDULED.ordinal()];
+
+ long start = scheduledTime > 0 ? scheduledTime : -1;
+ long end = status.isTerminal() ? timestamps[status.ordinal()] : now;
+ long duration = start >= 0 ? end - start : -1L;
+
gen.writeStartObject();
- gen.writeNumberField("subtask", num);
+ gen.writeNumberField("subtask", num++);
InstanceConnectionInfo location = vertex.getCurrentAssignedResourceLocation();
String locationString = location == null ? "(unassigned)" : location.getHostname();
gen.writeStringField("host", locationString);
+ gen.writeNumberField("duration", duration);
+
gen.writeObjectFieldStart("timestamps");
- long[] timestamps = vertex.getCurrentExecutionAttempt().getStateTimestamps();
for (ExecutionState state : ExecutionState.values()) {
gen.writeNumberField(state.name(), timestamps[state.ordinal()]);
}
gen.writeEndObject();
+
gen.writeEndObject();
}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JobManagerInfoHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JobManagerInfoHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JobManagerInfoHandler.java
deleted file mode 100644
index c65bc0f..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JobManagerInfoHandler.java
+++ /dev/null
@@ -1,702 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.legacy;
-
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.SimpleChannelInboundHandler;
-import io.netty.handler.codec.http.DefaultFullHttpResponse;
-import io.netty.handler.codec.http.HttpHeaders;
-import io.netty.handler.codec.http.HttpResponseStatus;
-import io.netty.handler.codec.http.HttpVersion;
-import io.netty.handler.codec.http.router.KeepAliveWrite;
-import io.netty.handler.codec.http.router.Routed;
-
-import org.apache.flink.api.common.ExecutionConfig;
-import org.apache.flink.api.common.JobID;
-import org.apache.flink.runtime.accumulators.StringifiedAccumulatorResult;
-import org.apache.flink.runtime.execution.ExecutionState;
-import org.apache.flink.runtime.executiongraph.Execution;
-import org.apache.flink.runtime.executiongraph.ExecutionGraph;
-import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
-import org.apache.flink.runtime.executiongraph.ExecutionVertex;
-import org.apache.flink.runtime.instance.ActorGateway;
-import org.apache.flink.runtime.instance.InstanceConnectionInfo;
-import org.apache.flink.runtime.jobgraph.JobStatus;
-import org.apache.flink.runtime.jobgraph.JobVertexID;
-import org.apache.flink.runtime.messages.ArchiveMessages;
-import org.apache.flink.runtime.messages.JobManagerMessages;
-import org.apache.flink.runtime.messages.accumulators.AccumulatorResultStringsFound;
-import org.apache.flink.runtime.messages.accumulators.AccumulatorResultsErroneous;
-import org.apache.flink.runtime.messages.accumulators.AccumulatorResultsNotFound;
-import org.apache.flink.runtime.messages.accumulators.RequestAccumulatorResultsStringified;
-import org.apache.flink.runtime.util.EnvironmentInformation;
-import org.apache.flink.util.ExceptionUtils;
-
-import org.apache.flink.util.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import scala.Tuple3;
-import scala.concurrent.Await;
-import scala.concurrent.Future;
-import scala.concurrent.duration.FiniteDuration;
-
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-@ChannelHandler.Sharable
-public class JobManagerInfoHandler extends SimpleChannelInboundHandler<Routed> {
-
- private static final Logger LOG = LoggerFactory.getLogger(JobManagerInfoHandler.class);
-
- private static final Charset ENCODING = Charset.forName("UTF-8");
-
- /** Underlying JobManager */
- private final ActorGateway jobmanager;
- private final ActorGateway archive;
- private final FiniteDuration timeout;
-
-
- public JobManagerInfoHandler(ActorGateway jobmanager, ActorGateway archive, FiniteDuration timeout) {
- this.jobmanager = jobmanager;
- this.archive = archive;
- this.timeout = timeout;
- }
-
- @Override
- protected void channelRead0(ChannelHandlerContext ctx, Routed routed) throws Exception {
- DefaultFullHttpResponse response;
- try {
- String result = handleRequest(routed);
- byte[] bytes = result.getBytes(ENCODING);
-
- response = new DefaultFullHttpResponse(
- HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(bytes));
-
- response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "application/json");
- }
- catch (Exception e) {
- byte[] bytes = ExceptionUtils.stringifyException(e).getBytes(ENCODING);
- response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
- HttpResponseStatus.INTERNAL_SERVER_ERROR, Unpooled.wrappedBuffer(bytes));
- response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain");
- }
-
- response.headers().set(HttpHeaders.Names.CONTENT_ENCODING, "utf-8");
- response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
-
- KeepAliveWrite.flush(ctx, routed.request(), response);
- }
-
-
- @SuppressWarnings("unchecked")
- private String handleRequest(Routed routed) throws Exception {
- if ("archive".equals(routed.queryParam("get"))) {
- Future<Object> response = archive.ask(ArchiveMessages.getRequestArchivedJobs(), timeout);
-
- Object result = Await.result(response, timeout);
-
- if(!(result instanceof ArchiveMessages.ArchivedJobs)) {
- throw new RuntimeException("RequestArchiveJobs requires a response of type " +
- "ArchivedJobs. Instead the response is of type " + result.getClass() +".");
- }
- else {
- final List<ExecutionGraph> archivedJobs = new ArrayList<ExecutionGraph>(
- ((ArchiveMessages.ArchivedJobs) result).asJavaCollection());
-
- return writeJsonForArchive(archivedJobs);
- }
- }
- else if ("jobcounts".equals(routed.queryParam("get"))) {
- Future<Object> response = archive.ask(ArchiveMessages.getRequestJobCounts(), timeout);
-
- Object result = Await.result(response, timeout);
-
- if (!(result instanceof Tuple3)) {
- throw new RuntimeException("RequestJobCounts requires a response of type " +
- "Tuple3. Instead the response is of type " + result.getClass() +
- ".");
- }
- else {
- return writeJsonForJobCounts((Tuple3<Integer, Integer, Integer>) result);
- }
- }
- else if ("job".equals(routed.queryParam("get"))) {
- String jobId = routed.queryParam("job");
-
- Future<Object> response = archive.ask(new JobManagerMessages.RequestJob(JobID.fromHexString(jobId)),
- timeout);
-
- Object result = Await.result(response, timeout);
-
- if (!(result instanceof JobManagerMessages.JobResponse)){
- throw new RuntimeException("RequestJob requires a response of type JobResponse. " +
- "Instead the response is of type " + result.getClass());
- }
- else {
- final JobManagerMessages.JobResponse jobResponse = (JobManagerMessages.JobResponse) result;
-
- if (jobResponse instanceof JobManagerMessages.JobFound){
- ExecutionGraph archivedJob = ((JobManagerMessages.JobFound)result).executionGraph();
- return writeJsonForArchivedJob(archivedJob);
- }
- else {
- throw new Exception("DoGet:job: Could not find job for job ID " + jobId);
- }
- }
- }
- else if ("groupvertex".equals(routed.queryParam("get"))) {
- String jobId = routed.queryParam("job");
- String groupVertexId = routed.queryParam("groupvertex");
-
- // No group vertex specified
- if (groupVertexId.equals("null")) {
- throw new Exception("Found null groupVertexId");
- }
-
- Future<Object> response = archive.ask(new JobManagerMessages.RequestJob(JobID.fromHexString(jobId)),
- timeout);
-
- Object result = Await.result(response, timeout);
-
- if (!(result instanceof JobManagerMessages.JobResponse)){
- throw new RuntimeException("RequestJob requires a response of type JobResponse. " +
- "Instead the response is of type " + result.getClass());
- }
- else {
- final JobManagerMessages.JobResponse jobResponse = (JobManagerMessages.JobResponse) result;
-
- if (jobResponse instanceof JobManagerMessages.JobFound) {
- ExecutionGraph archivedJob = ((JobManagerMessages.JobFound)jobResponse).executionGraph();
-
- return writeJsonForArchivedJobGroupvertex(archivedJob, JobVertexID.fromHexString(groupVertexId));
- }
- else {
- throw new Exception("DoGet:groupvertex: Could not find job for job ID " + jobId);
- }
- }
- }
- else if ("taskmanagers".equals(routed.queryParam("get"))) {
- Future<Object> response = jobmanager.ask(
- JobManagerMessages.getRequestNumberRegisteredTaskManager(),
- timeout);
-
- Object result = Await.result(response, timeout);
-
- if (!(result instanceof Integer)) {
- throw new RuntimeException("RequestNumberRegisteredTaskManager requires a " +
- "response of type Integer. Instead the response is of type " +
- result.getClass() + ".");
- }
- else {
- final int numberOfTaskManagers = (Integer)result;
-
- final Future<Object> responseRegisteredSlots = jobmanager.ask(
- JobManagerMessages.getRequestTotalNumberOfSlots(),
- timeout);
-
- final Object resultRegisteredSlots = Await.result(responseRegisteredSlots,
- timeout);
-
- if (!(resultRegisteredSlots instanceof Integer)) {
- throw new RuntimeException("RequestTotalNumberOfSlots requires a response of " +
- "type Integer. Instaed the response of type " +
- resultRegisteredSlots.getClass() + ".");
- }
- else {
- final int numberOfRegisteredSlots = (Integer) resultRegisteredSlots;
-
- return "{\"taskmanagers\": " + numberOfTaskManagers + ", " +
- "\"slots\": " + numberOfRegisteredSlots + "}";
- }
- }
- }
- else if ("cancel".equals(routed.queryParam("get"))) {
- String jobId = routed.queryParam("job");
-
- Future<Object> response = jobmanager.ask(new JobManagerMessages.CancelJob(JobID.fromHexString(jobId)),
- timeout);
-
- Await.ready(response, timeout);
- return "{}";
- }
- else if ("updates".equals(routed.queryParam("get"))) {
- String jobId = routed.queryParam("job");
- return writeJsonUpdatesForJob(JobID.fromHexString(jobId));
- }
- else if ("version".equals(routed.queryParam("get"))) {
- return writeJsonForVersion();
- }
- else{
- Future<Object> response = jobmanager.ask(JobManagerMessages.getRequestRunningJobs(),
- timeout);
-
- Object result = Await.result(response, timeout);
-
- if(!(result instanceof JobManagerMessages.RunningJobs)){
- throw new RuntimeException("RequestRunningJobs requires a response of type " +
- "RunningJobs. Instead the response of type " + result.getClass() + ".");
- }
- else {
- final Iterable<ExecutionGraph> runningJobs =
- ((JobManagerMessages.RunningJobs) result).asJavaIterable();
-
- return writeJsonForJobs(runningJobs);
- }
- }
- }
-
- private String writeJsonForJobs(Iterable<ExecutionGraph> graphs) {
- StringBuilder bld = new StringBuilder();
- bld.append("[");
-
- Iterator<ExecutionGraph> it = graphs.iterator();
- // Loop Jobs
- while(it.hasNext()){
- ExecutionGraph graph = it.next();
-
- writeJsonForJob(bld, graph);
-
- //Write seperator between json objects
- if(it.hasNext()) {
- bld.append(",");
- }
- }
- bld.append("]");
-
- return bld.toString();
- }
-
- private void writeJsonForJob(StringBuilder bld, ExecutionGraph graph) {
- //Serialize job to json
- bld.append("{");
- bld.append("\"jobid\": \"").append(graph.getJobID()).append("\",");
- bld.append("\"jobname\": \"").append(graph.getJobName()).append("\",");
- bld.append("\"status\": \"").append(graph.getState()).append("\",");
- bld.append("\"time\": ").append(graph.getStatusTimestamp(graph.getState())).append(",");
-
- // Serialize ManagementGraph to json
- bld.append("\"groupvertices\": [");
- boolean first = true;
-
- for (ExecutionJobVertex groupVertex : graph.getVerticesTopologically()) {
- //Write seperator between json objects
- if (first) {
- first = false;
- } else {
- bld.append(",");
- }
- bld.append(JsonFactory.toJson(groupVertex));
- }
- bld.append("]");
- bld.append("}");
- }
-
- private String writeJsonForArchive(List<ExecutionGraph> graphs) {
- StringBuilder bld = new StringBuilder();
- bld.append("[");
-
- // sort jobs by time
- Collections.sort(graphs, new Comparator<ExecutionGraph>() {
- @Override
- public int compare(ExecutionGraph o1, ExecutionGraph o2) {
- if (o1.getStatusTimestamp(o1.getState()) < o2.getStatusTimestamp(o2.getState())) {
- return 1;
- } else {
- return -1;
- }
- }
-
- });
-
- // Loop Jobs
- for (int i = 0; i < graphs.size(); i++) {
- ExecutionGraph graph = graphs.get(i);
-
- //Serialize job to json
- bld.append("{");
- bld.append("\"jobid\": \"").append(graph.getJobID()).append("\",");
- bld.append("\"jobname\": \"").append(graph.getJobName()).append("\",");
- bld.append("\"status\": \"").append(graph.getState()).append("\",");
- bld.append("\"time\": ").append(graph.getStatusTimestamp(graph.getState()));
-
- bld.append("}");
-
- //Write seperator between json objects
- if(i != graphs.size() - 1) {
- bld.append(",");
- }
- }
- bld.append("]");
- return bld.toString();
- }
-
- private String writeJsonForJobCounts(Tuple3<Integer, Integer, Integer> jobCounts) {
- return "{\"finished\": " + jobCounts._1() + ",\"canceled\": " + jobCounts._2() + ",\"failed\": "
- + jobCounts._3() + "}";
- }
-
-
- private String writeJsonForArchivedJob(ExecutionGraph graph) {
- StringBuilder bld = new StringBuilder();
-
- bld.append("[");
- bld.append("{");
- bld.append("\"jobid\": \"").append(graph.getJobID()).append("\",");
- bld.append("\"jobname\": \"").append(graph.getJobName()).append("\",");
- bld.append("\"status\": \"").append(graph.getState()).append("\",");
- bld.append("\"SCHEDULED\": ").append(graph.getStatusTimestamp(JobStatus.CREATED)).append(",");
- bld.append("\"RUNNING\": ").append(graph.getStatusTimestamp(JobStatus.RUNNING)).append(",");
- bld.append("\"FINISHED\": ").append(graph.getStatusTimestamp(JobStatus.FINISHED)).append(",");
- bld.append("\"FAILED\": ").append(graph.getStatusTimestamp(JobStatus.FAILED)).append(",");
- bld.append("\"CANCELED\": ").append(graph.getStatusTimestamp(JobStatus.CANCELED)).append(",");
-
- if (graph.getState() == JobStatus.FAILED) {
- bld.append("\"failednodes\": [");
- boolean first = true;
- for (ExecutionVertex vertex : graph.getAllExecutionVertices()) {
- if (vertex.getExecutionState() == ExecutionState.FAILED) {
- InstanceConnectionInfo location = vertex.getCurrentAssignedResourceLocation();
- Throwable failureCause = vertex.getFailureCause();
- if (location != null || failureCause != null) {
- if (first) {
- first = false;
- } else {
- bld.append(",");
- }
- bld.append("{");
- bld.append("\"node\": \"").append(location == null ? "(none)" : location.getFQDNHostname()).append("\",");
- bld.append("\"message\": \"").append(failureCause == null ? "" : StringUtils.escapeHtml(ExceptionUtils.stringifyException(failureCause))).append("\"");
- bld.append("}");
- }
- }
- }
- bld.append("],");
- }
-
- // Serialize ManagementGraph to json
- bld.append("\"groupvertices\": [");
- boolean first = true;
- for (ExecutionJobVertex groupVertex : graph.getVerticesTopologically()) {
- //Write seperator between json objects
- if (first) {
- first = false;
- } else {
- bld.append(",");
- }
-
- bld.append(JsonFactory.toJson(groupVertex));
-
- }
- bld.append("],");
-
- // write user config
- ExecutionConfig ec = graph.getExecutionConfig();
- if(ec != null) {
- bld.append("\"executionConfig\": {");
- bld.append("\"Execution Mode\": \"").append(ec.getExecutionMode()).append("\",");
- bld.append("\"Number of execution retries\": \"").append(ec.getNumberOfExecutionRetries()).append("\",");
- bld.append("\"Job parallelism\": \"").append(ec.getParallelism()).append("\",");
- bld.append("\"Object reuse mode\": \"").append(ec.isObjectReuseEnabled()).append("\"");
- ExecutionConfig.GlobalJobParameters uc = ec.getGlobalJobParameters();
- if(uc != null) {
- Map<String, String> ucVals = uc.toMap();
- if (ucVals != null) {
- String ucString = "{";
- int i = 0;
- for (Map.Entry<String, String> ucVal : ucVals.entrySet()) {
- ucString += "\"" + ucVal.getKey() + "\":\"" + ucVal.getValue() + "\"";
- if (++i < ucVals.size()) {
- ucString += ",\n";
- }
- }
- bld.append(", \"userConfig\": ").append(ucString).append("}");
- }
- else {
- LOG.debug("GlobalJobParameters.toMap() did not return anything");
- }
- }
- else {
- LOG.debug("No GlobalJobParameters were set in the execution config");
- }
- bld.append("},");
- }
- else {
- LOG.warn("Unable to retrieve execution config from execution graph");
- }
-
- // write accumulators
- final Future<Object> response = jobmanager.ask(
- new RequestAccumulatorResultsStringified(graph.getJobID()),
- timeout);
-
- Object result;
- try {
- result = Await.result(response, timeout);
- }
- catch (Exception ex) {
- throw new RuntimeException("Could not retrieve the accumulator results from the job manager.", ex);
- }
-
- if (result instanceof AccumulatorResultStringsFound) {
- StringifiedAccumulatorResult[] accumulators = ((AccumulatorResultStringsFound) result).result();
-
- bld.append("\n\"accumulators\": [");
- int i = 0;
- for (StringifiedAccumulatorResult accumulator : accumulators) {
- bld.append("{ \"name\": \"").append(accumulator.getName()).append(" (").append(accumulator.getType()).append(")\",").append(" \"value\": \"").append(accumulator.getValue()).append("\"}\n");
- if (++i < accumulators.length) {
- bld.append(",");
- }
- }
- bld.append("],\n");
- }
- else if (result instanceof AccumulatorResultsNotFound) {
- bld.append("\n\"accumulators\": [],");
- }
- else if (result instanceof AccumulatorResultsErroneous) {
- LOG.error("Could not obtain accumulators for job " + graph.getJobID(),
- ((AccumulatorResultsErroneous) result).cause());
- }
- else {
- throw new RuntimeException("RequestAccumulatorResults requires a response of type " +
- "AccumulatorResultStringsFound. Instead the response is of type " +
- result.getClass() + ".");
- }
-
- bld.append("\"groupverticetimes\": {");
- first = true;
-
- for (ExecutionJobVertex groupVertex : graph.getVerticesTopologically()) {
- if (first) {
- first = false;
- } else {
- bld.append(",");
- }
-
- // Calculate start and end time for groupvertex
- long started = Long.MAX_VALUE;
- long ended = 0;
-
- // Take earliest running state and latest endstate of groupmembers
- for (ExecutionVertex vertex : groupVertex.getTaskVertices()) {
-
- long running = vertex.getStateTimestamp(ExecutionState.RUNNING);
- if (running != 0 && running < started) {
- started = running;
- }
-
- long finished = vertex.getStateTimestamp(ExecutionState.FINISHED);
- long canceled = vertex.getStateTimestamp(ExecutionState.CANCELED);
- long failed = vertex.getStateTimestamp(ExecutionState.FAILED);
-
- if (finished != 0 && finished > ended) {
- ended = finished;
- }
-
- if (canceled != 0 && canceled > ended) {
- ended = canceled;
- }
-
- if (failed != 0 && failed > ended) {
- ended = failed;
- }
-
- }
-
- bld.append("\"").append(groupVertex.getJobVertexId()).append("\": {");
- bld.append("\"groupvertexid\": \"").append(groupVertex.getJobVertexId()).append("\",");
- bld.append("\"groupvertexname\": \"").append(groupVertex).append("\",");
- bld.append("\"STARTED\": ").append(started).append(",");
- bld.append("\"ENDED\": ").append(ended);
- bld.append("}");
-
- }
-
- bld.append("}");
- bld.append("}");
- bld.append("]");
-
- return bld.toString();
- }
-
-
- private String writeJsonUpdatesForJob(JobID jobId) {
- final Future<Object> responseArchivedJobs = jobmanager.ask(
- JobManagerMessages.getRequestRunningJobs(),
- timeout);
-
- Object resultArchivedJobs;
- try{
- resultArchivedJobs = Await.result(responseArchivedJobs, timeout);
- }
- catch (Exception ex) {
- throw new RuntimeException("Could not retrieve archived jobs from the job manager.", ex);
- }
-
- if(!(resultArchivedJobs instanceof JobManagerMessages.RunningJobs)){
- throw new RuntimeException("RequestArchivedJobs requires a response of type " +
- "RunningJobs. Instead the response is of type " +
- resultArchivedJobs.getClass() + ".");
- }
- else {
- final Iterable<ExecutionGraph> graphs = ((JobManagerMessages.RunningJobs)resultArchivedJobs).
- asJavaIterable();
-
- //Serialize job to json
- final StringBuilder bld = new StringBuilder();
-
- bld.append("{");
- bld.append("\"jobid\": \"").append(jobId).append("\",");
- bld.append("\"timestamp\": \"").append(System.currentTimeMillis()).append("\",");
- bld.append("\"recentjobs\": [");
-
- boolean first = true;
-
- for (ExecutionGraph g : graphs){
- if (first) {
- first = false;
- } else {
- bld.append(",");
- }
-
- bld.append("\"").append(g.getJobID()).append("\"");
- }
- bld.append("],");
-
- final Future<Object> responseJob = jobmanager.ask(
- new JobManagerMessages.RequestJob(jobId),
- timeout);
-
- Object resultJob;
- try{
- resultJob = Await.result(responseJob, timeout);
- }
- catch (Exception ex){
- throw new RuntimeException("Could not retrieve the job with jobID " + jobId +
- "from the job manager.", ex);
- }
-
- if (!(resultJob instanceof JobManagerMessages.JobResponse)) {
- throw new RuntimeException("RequestJob requires a response of type JobResponse. " +
- "Instead the response is of type " + resultJob.getClass() + ".");
- }
- else {
- final JobManagerMessages.JobResponse response = (JobManagerMessages.JobResponse) resultJob;
-
- if (response instanceof JobManagerMessages.JobFound){
- ExecutionGraph graph = ((JobManagerMessages.JobFound)response).executionGraph();
-
- bld.append("\"vertexevents\": [");
-
- first = true;
- for (ExecutionVertex ev : graph.getAllExecutionVertices()) {
- if (first) {
- first = false;
- } else {
- bld.append(",");
- }
-
- bld.append("{");
- bld.append("\"vertexid\": \"").append(ev.getCurrentExecutionAttempt().getAttemptId()).append("\",");
- bld.append("\"newstate\": \"").append(ev.getExecutionState()).append("\",");
- bld.append("\"timestamp\": \"").append(ev.getStateTimestamp(ev.getExecutionState())).append("\"");
- bld.append("}");
- }
-
- bld.append("],");
-
- bld.append("\"jobevents\": [");
-
- bld.append("{");
- bld.append("\"newstate\": \"").append(graph.getState()).append("\",");
- bld.append("\"timestamp\": \"").append(graph.getStatusTimestamp(graph.getState())).append("\"");
- bld.append("}");
-
- bld.append("]");
-
- bld.append("}");
- }
- else {
- bld.append("\"vertexevents\": [],");
- bld.append("\"jobevents\": [");
- bld.append("{");
- bld.append("\"newstate\": \"").append(JobStatus.FINISHED.toString()).append("\",");
- bld.append("\"timestamp\": \"").append(System.currentTimeMillis()).append("\"");
- bld.append("}");
- bld.append("]");
- bld.append("}");
- }
- }
-
- return bld.toString();
- }
- }
-
- private String writeJsonForArchivedJobGroupvertex(ExecutionGraph graph, JobVertexID vertexId) {
- ExecutionJobVertex jobVertex = graph.getJobVertex(vertexId);
- StringBuilder bld = new StringBuilder();
-
- bld.append("{\"groupvertex\": ").append(JsonFactory.toJson(jobVertex)).append(",");
-
- bld.append("\"verticetimes\": {");
- boolean first = true;
- for (ExecutionJobVertex groupVertex : graph.getAllVertices().values()) {
-
- for (ExecutionVertex vertex : groupVertex.getTaskVertices()) {
-
- Execution exec = vertex.getCurrentExecutionAttempt();
-
- if(first) {
- first = false;
- } else {
- bld.append(","); }
-
- bld.append("\"").append(exec.getAttemptId()).append("\": {");
- bld.append("\"vertexid\": \"").append(exec.getAttemptId()).append("\",");
- bld.append("\"vertexname\": \"").append(vertex).append("\",");
- bld.append("\"CREATED\": ").append(vertex.getStateTimestamp(ExecutionState.CREATED)).append(",");
- bld.append("\"SCHEDULED\": ").append(vertex.getStateTimestamp(ExecutionState.SCHEDULED)).append(",");
- bld.append("\"DEPLOYING\": ").append(vertex.getStateTimestamp(ExecutionState.DEPLOYING)).append(",");
- bld.append("\"RUNNING\": ").append(vertex.getStateTimestamp(ExecutionState.RUNNING)).append(",");
- bld.append("\"FINISHED\": ").append(vertex.getStateTimestamp(ExecutionState.FINISHED)).append(",");
- bld.append("\"CANCELING\": ").append(vertex.getStateTimestamp(ExecutionState.CANCELING)).append(",");
- bld.append("\"CANCELED\": ").append(vertex.getStateTimestamp(ExecutionState.CANCELED)).append(",");
- bld.append("\"FAILED\": ").append(vertex.getStateTimestamp(ExecutionState.FAILED)).append("");
- bld.append("}");
- }
-
- }
- bld.append("}}");
- return bld.toString();
- }
-
-
- private String writeJsonForVersion() {
- return "{\"version\": \"" + EnvironmentInformation.getVersion() + "\",\"revision\": \"" +
- EnvironmentInformation.getRevisionInformation().commitId + "\"}";
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JsonFactory.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JsonFactory.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JsonFactory.java
deleted file mode 100644
index fe18d3f..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/legacy/JsonFactory.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.legacy;
-
-import org.apache.flink.runtime.execution.ExecutionState;
-import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
-import org.apache.flink.runtime.executiongraph.ExecutionVertex;
-import org.apache.flink.runtime.executiongraph.IntermediateResult;
-import org.apache.flink.runtime.instance.InstanceConnectionInfo;
-import org.apache.flink.util.StringUtils;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class JsonFactory {
-
- public static String toJson(ExecutionVertex vertex) {
- StringBuilder json = new StringBuilder("");
- json.append("{");
- json.append("\"vertexid\": \"").append(vertex.getCurrentExecutionAttempt().getAttemptId()).append("\",");
- json.append("\"vertexname\": \"").append(StringUtils.escapeHtml(vertex.getSimpleName())).append("\",");
- json.append("\"vertexstatus\": \"").append(vertex.getExecutionState()).append("\",");
-
- InstanceConnectionInfo location = vertex.getCurrentAssignedResourceLocation();
- String instanceName = location == null ? "(null)" : location.getFQDNHostname();
-
- json.append("\"vertexinstancename\": \"").append(instanceName).append("\"");
- json.append("}");
- return json.toString();
- }
-
- public static String toJson(ExecutionJobVertex jobVertex) {
- StringBuilder json = new StringBuilder("");
-
- json.append("{");
- json.append("\"groupvertexid\": \"").append(jobVertex.getJobVertexId()).append("\",");
- json.append("\"groupvertexname\": \"").append(StringUtils.escapeHtml(jobVertex.getJobVertex().getName())).append("\",");
- json.append("\"numberofgroupmembers\": ").append(jobVertex.getParallelism()).append(",");
- json.append("\"groupmembers\": [");
-
- // Count state status of group members
- Map<ExecutionState, Integer> stateCounts = new HashMap<ExecutionState, Integer>();
-
- // initialize with 0
- for (ExecutionState state : ExecutionState.values()) {
- stateCounts.put(state, 0);
- }
-
- ExecutionVertex[] vertices = jobVertex.getTaskVertices();
-
- for (int j = 0; j < vertices.length; j++) {
- ExecutionVertex vertex = vertices[j];
-
- json.append(toJson(vertex));
-
- // print delimiter
- if (j != vertices.length - 1) {
- json.append(",");
- }
-
- // Increment state status count
- int count = stateCounts.get(vertex.getExecutionState()) + 1;
- stateCounts.put(vertex.getExecutionState(), count);
- }
-
- json.append("],");
- json.append("\"backwardEdges\": [");
-
- List<IntermediateResult> inputs = jobVertex.getInputs();
-
- for (int inputNumber = 0; inputNumber < inputs.size(); inputNumber++) {
- ExecutionJobVertex input = inputs.get(inputNumber).getProducer();
-
- json.append("{");
- json.append("\"groupvertexid\": \"").append(input.getJobVertexId()).append("\",");
- json.append("\"groupvertexname\": \"").append(StringUtils.escapeHtml(jobVertex.getJobVertex().getName())).append("\"");
- json.append("}");
-
- // print delimiter
- if(inputNumber != inputs.size() - 1) {
- json.append(",");
- }
- }
- json.append("]");
-
- // list number of members for each status
- for (Map.Entry<ExecutionState, Integer> stateCount : stateCounts.entrySet()) {
- json.append(",\"").append(stateCount.getKey()).append("\": ").append(stateCount.getValue());
- }
-
- json.append("}");
-
- return json.toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime-web/src/test/java/org/apache/flink/runtime/webmonitor/runner/TestRunner.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/test/java/org/apache/flink/runtime/webmonitor/runner/TestRunner.java b/flink-runtime-web/src/test/java/org/apache/flink/runtime/webmonitor/runner/TestRunner.java
deleted file mode 100644
index a2978a1..0000000
--- a/flink-runtime-web/src/test/java/org/apache/flink/runtime/webmonitor/runner/TestRunner.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.runner;
-
-import org.apache.flink.api.common.functions.CoGroupFunction;
-import org.apache.flink.api.common.functions.FilterFunction;
-import org.apache.flink.api.common.functions.FlatMapFunction;
-import org.apache.flink.api.java.DataSet;
-import org.apache.flink.api.java.ExecutionEnvironment;
-import org.apache.flink.api.java.functions.FunctionAnnotation;
-import org.apache.flink.api.java.tuple.Tuple1;
-import org.apache.flink.api.java.tuple.Tuple2;
-import org.apache.flink.api.java.tuple.Tuple3;
-import org.apache.flink.configuration.ConfigConstants;
-import org.apache.flink.configuration.Configuration;
-import org.apache.flink.examples.java.relational.util.WebLogData;
-import org.apache.flink.runtime.minicluster.LocalFlinkMiniCluster;
-import org.apache.flink.test.testdata.WordCountData;
-import org.apache.flink.util.Collector;
-
-/**
- * Simple runner that brings up a local cluster with the web server and executes two
- * jobs to expose their data in the archive
- */
-@SuppressWarnings("serial")
-public class TestRunner {
-
- public static void main(String[] args) throws Exception {
-
- // start the cluster with the runtime monitor
- Configuration configuration = new Configuration();
- configuration.setBoolean(ConfigConstants.LOCAL_START_WEBSERVER, true);
- configuration.setBoolean(ConfigConstants.JOB_MANAGER_NEW_WEB_FRONTEND_KEY, true);
- configuration.setString(ConfigConstants.JOB_MANAGER_WEB_DOC_ROOT_KEY,
- "flink-dist/target/flink-0.10-SNAPSHOT-bin/flink-0.10-SNAPSHOT/resources/web-runtime-monitor");
-
- LocalFlinkMiniCluster cluster = new LocalFlinkMiniCluster(configuration, false);
- cluster.start();
-
- final int port = cluster.getLeaderRPCPort();
- runWordCount(port);
- runWebLogAnalysisExample(port);
- runWordCount(port);
-
- // block the thread
- Object o = new Object();
- synchronized (o) {
- o.wait();
- }
-
- cluster.shutdown();
- }
-
- private static void runWordCount(int port) throws Exception {
- final ExecutionEnvironment env = ExecutionEnvironment.createRemoteEnvironment("localhost", port);
-
- DataSet<String> text = env.fromElements(WordCountData.TEXT.split("\n"));
-
- DataSet<Tuple2<String, Integer>> counts =
- // split up the lines in pairs (2-tuples) containing: (word,1)
- text.flatMap(new Tokenizer())
- // group by the tuple field "0" and sum up tuple field "1"
- .groupBy(0)
- .sum(1);
-
- counts.print();
- }
-
- private static void runWebLogAnalysisExample(int port) throws Exception {
- final ExecutionEnvironment env = ExecutionEnvironment.createRemoteEnvironment("localhost", port);
-
- // get input data
- DataSet<Tuple2<String, String>> documents = WebLogData.getDocumentDataSet(env);
- DataSet<Tuple3<Integer, String, Integer>> ranks = WebLogData.getRankDataSet(env);
- DataSet<Tuple2<String, String>> visits = WebLogData.getVisitDataSet(env);
-
- // Retain documents with keywords
- DataSet<Tuple1<String>> filterDocs = documents
- .filter(new FilterDocByKeyWords())
- .project(0);
-
- // Filter ranks by minimum rank
- DataSet<Tuple3<Integer, String, Integer>> filterRanks = ranks
- .filter(new FilterByRank());
-
- // Filter visits by visit date
- DataSet<Tuple1<String>> filterVisits = visits
- .filter(new FilterVisitsByDate())
- .project(0);
-
- // Join the filtered documents and ranks, i.e., get all URLs with min rank and keywords
- DataSet<Tuple3<Integer, String, Integer>> joinDocsRanks =
- filterDocs.join(filterRanks)
- .where(0).equalTo(1)
- .projectSecond(0,1,2);
-
- // Anti-join urls with visits, i.e., retain all URLs which have NOT been visited in a certain time
- DataSet<Tuple3<Integer, String, Integer>> result =
- joinDocsRanks.coGroup(filterVisits)
- .where(1).equalTo(0)
- .with(new AntiJoinVisits());
-
- result.print();
- }
-
- // *************************************************************************
- // USER FUNCTIONS
- // *************************************************************************
-
- public static final class Tokenizer implements FlatMapFunction<String, Tuple2<String, Integer>> {
-
- @Override
- public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
- // normalize and split the line
- String[] tokens = value.toLowerCase().split("\\W+");
-
- // emit the pairs
- for (String token : tokens) {
- if (token.length() > 0) {
- out.collect(new Tuple2<String, Integer>(token, 1));
- }
- }
- }
- }
-
- public static class FilterDocByKeyWords implements FilterFunction<Tuple2<String, String>> {
-
- private static final String[] KEYWORDS = { " editors ", " oscillations " };
-
- @Override
- public boolean filter(Tuple2<String, String> value) throws Exception {
- // FILTER
- // Only collect the document if all keywords are contained
- String docText = value.f1;
- for (String kw : KEYWORDS) {
- if (!docText.contains(kw)) {
- return false;
- }
- }
- return true;
- }
- }
-
- public static class FilterByRank implements FilterFunction<Tuple3<Integer, String, Integer>> {
-
- private static final int RANKFILTER = 40;
-
- @Override
- public boolean filter(Tuple3<Integer, String, Integer> value) throws Exception {
- return (value.f0 > RANKFILTER);
- }
- }
-
-
- public static class FilterVisitsByDate implements FilterFunction<Tuple2<String, String>> {
-
- private static final int YEARFILTER = 2007;
-
- @Override
- public boolean filter(Tuple2<String, String> value) throws Exception {
- // Parse date string with the format YYYY-MM-DD and extract the year
- String dateString = value.f1;
- int year = Integer.parseInt(dateString.substring(0,4));
- return (year == YEARFILTER);
- }
- }
-
-
- @FunctionAnnotation.ForwardedFieldsFirst("*")
- public static class AntiJoinVisits implements CoGroupFunction<Tuple3<Integer, String, Integer>, Tuple1<String>, Tuple3<Integer, String, Integer>> {
-
- @Override
- public void coGroup(Iterable<Tuple3<Integer, String, Integer>> ranks, Iterable<Tuple1<String>> visits, Collector<Tuple3<Integer, String, Integer>> out) {
- // Check if there is a entry in the visits relation
- if (!visits.iterator().hasNext()) {
- for (Tuple3<Integer, String, Integer> next : ranks) {
- // Emit all rank pairs
- out.collect(next);
- }
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java b/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java
index ea01acd..a0d1eda 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/accumulators/StringifiedAccumulatorResult.java
@@ -56,20 +56,25 @@ public class StringifiedAccumulatorResult implements java.io.Serializable{
// ------------------------------------------------------------------------
public static StringifiedAccumulatorResult[] stringifyAccumulatorResults(Map<String, Accumulator<?, ?>> accs) {
- StringifiedAccumulatorResult[] results = new StringifiedAccumulatorResult[accs.size()];
-
- int i = 0;
- for (Map.Entry<String, Accumulator<?, ?>> entry : accs.entrySet()) {
- StringifiedAccumulatorResult result;
- Accumulator<?, ?> value = entry.getValue();
- if (value != null) {
- result = new StringifiedAccumulatorResult(entry.getKey(), value.getClass().getSimpleName(), value.toString());
- } else {
- result = new StringifiedAccumulatorResult(entry.getKey(), "null", "null");
+ if (accs == null || accs.isEmpty()) {
+ return new StringifiedAccumulatorResult[0];
+ }
+ else {
+ StringifiedAccumulatorResult[] results = new StringifiedAccumulatorResult[accs.size()];
+
+ int i = 0;
+ for (Map.Entry<String, Accumulator<?, ?>> entry : accs.entrySet()) {
+ StringifiedAccumulatorResult result;
+ Accumulator<?, ?> value = entry.getValue();
+ if (value != null) {
+ result = new StringifiedAccumulatorResult(entry.getKey(), value.getClass().getSimpleName(), value.toString());
+ } else {
+ result = new StringifiedAccumulatorResult(entry.getKey(), "null", "null");
+ }
+
+ results[i++] = result;
}
-
- results[i++] = result;
+ return results;
}
- return results;
}
}
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/Execution.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/Execution.java b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/Execution.java
index 4cee2f3..faabfb3 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/Execution.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/Execution.java
@@ -23,6 +23,7 @@ import akka.dispatch.OnFailure;
import org.apache.flink.api.common.accumulators.Accumulator;
import org.apache.flink.runtime.JobException;
import org.apache.flink.runtime.accumulators.AccumulatorRegistry;
+import org.apache.flink.runtime.accumulators.StringifiedAccumulatorResult;
import org.apache.flink.runtime.deployment.InputChannelDeploymentDescriptor;
import org.apache.flink.runtime.deployment.PartialInputChannelDeploymentDescriptor;
import org.apache.flink.runtime.deployment.ResultPartitionLocation;
@@ -959,6 +960,10 @@ public class Execution implements Serializable {
return vertex.getSimpleName() + " - execution #" + attemptNumber;
}
+ // ------------------------------------------------------------------------
+ // Accumulators
+ // ------------------------------------------------------------------------
+
/**
* Update accumulators (discarded when the Execution has already been terminated).
* @param flinkAccumulators the flink internal accumulators
@@ -973,14 +978,23 @@ public class Execution implements Serializable {
}
}
}
+
public Map<String, Accumulator<?, ?>> getUserAccumulators() {
return userAccumulators;
}
+ public StringifiedAccumulatorResult[] getUserAccumulatorsStringified() {
+ return StringifiedAccumulatorResult.stringifyAccumulatorResults(userAccumulators);
+ }
+
public Map<AccumulatorRegistry.Metric, Accumulator<?, ?>> getFlinkAccumulators() {
return flinkAccumulators;
}
+ // ------------------------------------------------------------------------
+ // Standard utilities
+ // ------------------------------------------------------------------------
+
@Override
public String toString() {
return String.format("Attempt #%d (%s) @ %s - [%s]", attemptNumber, vertex.getSimpleName(),
http://git-wip-us.apache.org/repos/asf/flink/blob/b3f79172/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionVertex.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionVertex.java b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionVertex.java
index 78e9804..0d039cc 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionVertex.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionVertex.java
@@ -229,6 +229,15 @@ public class ExecutionVertex implements Serializable {
return currentExecution.getAssignedResourceLocation();
}
+ public Execution getPriorExecutionAttempt(int attemptNumber) {
+ if (attemptNumber >= 0 && attemptNumber < priorExecutions.size()) {
+ return priorExecutions.get(attemptNumber);
+ }
+ else {
+ throw new IllegalArgumentException("attempt does not exist");
+ }
+ }
+
public ExecutionGraph getExecutionGraph() {
return this.jobVertex.getGraph();
}
[09/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Changed overview and timeline
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
index 97bff2c..b11513b 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
@@ -36,13 +36,13 @@ limitations under the License.
</thead>
<tbody>
<tr ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })">
- <td>{{job['start-time']}}</td>
- <td>{{job['end-time']}}</td>
- <td>{{job.duration}}</td>
+ <td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job['end-time'] - job['start-time']}} ms</td>
<td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
- <bs-label status="{{status}}" ng-repeat="(status, value) in job.operators">{{value}}</bs-label>
+ <bs-label status="{{status}}" ng-repeat="(status, value) in job.tasks">{{value}}</bs-label>
</td>
<td>
<bs-label status="{{job.state}}">{{job.state}}</bs-label>
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
index 1995e6d..5a91719 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
@@ -25,15 +25,15 @@ limitations under the License.
<div class="navbar-info first last hidden-xs hidden-sm">{{ job.jid }}</div>
<div class="navbar-info first last">
<div class="label-group">
- <bs-label status="{{status}}" ng-repeat="(status, value) in job.operators">{{value}}</bs-label>
+ <bs-label status="{{status}}" ng-repeat="(status, value) in job.tasks">{{value}}</bs-label>
</div>
</div>
<div class="navbar-info first last hidden-xs hidden-sm">
- {{ job['start-time'] }}
+ {{ job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
-
- {{ job['end-time'] }}
+ {{ job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
</div>
- <div class="navbar-info last first">{{job.duration}}</div>
+ <div class="navbar-info last first">{{job['end-time'] - job['start-time']}} ms</div>
</nav>
<nav ng-if="job" class="navbar navbar-default navbar-fixed-top navbar-main-additional">
<ul class="nav nav-tabs">
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
index 69bf8d9..aca3959 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
@@ -28,7 +28,7 @@ limitations under the License.
</div>
</div>
<div class="panel-heading clearfix">
- <div ng-if="node.description" class="panel-info first last"><span>{{ node.description }}</span></div>
+ <div ng-if="node.description" class="panel-info first last"><span>{{ node.description | humanizeTaskName }}</span></div>
</div>
<div class="panel-body">
<table class="table table-hover table-clickable">
@@ -40,7 +40,7 @@ limitations under the License.
</thead>
<tbody>
<tr ng-repeat="vertex in node.vertex.groupvertex.groupmembers">
- <td>{{vertex.vertexname}}</td>
+ <td>{{ vertex.vertexname | humanizeTaskName }}</td>
<td>
<bs-label status="{{vertex.vertexstatus}}">{{vertex.vertexstatus}}</bs-label>
</td>
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html
index 4aade1c..36f38ab 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html
@@ -19,7 +19,7 @@ limitations under the License.
-->
<div ng-if="vertex" class="panel panel-default panel-multi">
<div class="panel-heading clearfix">
- <div class="panel-title">{{ vertex.groupvertex.groupvertexname }}</div>
+ <div class="panel-title">{{ vertex.groupvertex.groupvertexname | humanizeTaskName }}</div>
</div>
<div class="panel-body">
<div class="canvas-wrapper">
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
index 2fd44e4..9a18a39 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
@@ -36,13 +36,13 @@ limitations under the License.
</thead>
<tbody>
<tr ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })">
- <td>{{job['start-time']}}</td>
- <td>{{job['end-time']}}</td>
- <td>{{job.duration}}</td>
+ <td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job['end-time'] - job['start-time']}} ms</td>
<td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
- <bs-label status="{{status}}" ng-repeat="(status, value) in job.operators">{{value}}</bs-label>
+ <bs-label status="{{status}}" ng-repeat="(status, value) in job.tasks">{{value}}</bs-label>
</td>
<td>
<bs-label status="{{job.state}}">{{job.state}}</bs-label>
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/web/partials/overview.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/overview.html b/flink-runtime-web/web-dashboard/web/partials/overview.html
index db0a01d..de39ad9 100644
--- a/flink-runtime-web/web-dashboard/web/partials/overview.html
+++ b/flink-runtime-web/web-dashboard/web/partials/overview.html
@@ -29,7 +29,7 @@ limitations under the License.
<div class="row">
<div class="col-xs-3"><i class="fa fa-tasks fa-3x"></i></div>
<div class="col-xs-9 text-right">
- <div class="huge">3</div>
+ <div class="huge">{{overview.taskmanagers}}</div>
<div>Task Managers</div>
</div>
</div>
@@ -38,7 +38,7 @@ limitations under the License.
<div class="row">
<div class="col-xs-3"><i class="fa fa-folder fa-3x"></i></div>
<div class="col-xs-9 text-right">
- <div class="huge">5</div>
+ <div class="huge">{{overview["slots-total"]}}</div>
<div>Task Slots</div>
</div>
</div>
@@ -47,7 +47,7 @@ limitations under the License.
<div class="row">
<div class="col-xs-3"><i class="fa fa-folder-o fa-3x"></i></div>
<div class="col-xs-9 text-right">
- <div class="huge">2</div>
+ <div class="huge">{{overview["slots-available"]}}</div>
<div>Available Task Slots</div>
</div>
</div>
@@ -59,16 +59,16 @@ limitations under the License.
<div class="panel-heading">Total Jobs</div>
<div class="list-group">
<div class="list-group-item">
- <div class="badge badge-primary">3</div>Running
+ <div class="badge badge-primary">{{overview["jobs-running"]}}</div>Running
</div>
<div class="list-group-item">
- <div class="badge badge-success">6</div>Finished
+ <div class="badge badge-success">{{overview["jobs-finished"]}}</div>Finished
</div>
<div class="list-group-item">
- <div class="badge badge-info">0</div>Canceled
+ <div class="badge badge-info">{{overview["jobs-cancelled"]}}</div>Canceled
</div>
<div class="list-group-item">
- <div class="badge badge-danger">2</div>Failed
+ <div class="badge badge-danger">{{overview["jobs-failed"]}}</div>Failed
</div>
</div>
</div>
@@ -93,13 +93,13 @@ limitations under the License.
</thead>
<tbody>
<tr ng-repeat="job in runningJobs" ui-sref="single-job.plan({ jobid: job.jid })">
- <td>{{job['start-time']}}</td>
- <td>{{job['end-time']}}</td>
+ <td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job.duration}}</td>
- <td>{{job.jobname}}</td>
+ <td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
- <bs-label status="{{status}}" ng-repeat="(status, value) in job.operators">{{value}}</bs-label>
+ <bs-label status="{{status}}" ng-repeat="(status, value) in job.tasks">{{value}}</bs-label>
</td>
<td>
<bs-label status="{{job.state}}">{{job.state}}</bs-label>
@@ -128,13 +128,13 @@ limitations under the License.
</thead>
<tbody>
<tr ng-repeat="job in finishedJobs" ui-sref="single-job.plan({ jobid: job.jid })">
- <td>{{job['start-time']}}</td>
- <td>{{job['end-time']}}</td>
- <td>{{job.duration}}</td>
- <td>{{job.jobname}}</td>
+ <td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
+ <td>{{job['end-time'] - job['start-time']}} ms</td>
+ <td>{{job.name}}</td>
<td>{{job.jid}}</td>
<td class="label-group">
- <bs-label status="{{status}}" ng-repeat="(status, value) in job.operators">{{value}}</bs-label>
+ <bs-label status="{{status}}" ng-repeat="(status, value) in job.tasks">{{value}}</bs-label>
</td>
<td>
<bs-label status="{{job.state}}">{{job.state}}</bs-label>
[13/51] [abbrv] flink git commit: [FLINK-2415] [web dashboard]
Provide more data in the job overview responses
Posted by se...@apache.org.
[FLINK-2415] [web dashboard] Provide more data in the job overview responses
Rather than only listing job ids, this now includes all information necessary to describe the job.
This reduces the number of requests for the job overview page to a single request.
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/628e5f71
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/628e5f71
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/628e5f71
Branch: refs/heads/master
Commit: 628e5f716d0050674da5251f254b21b6012a1519
Parents: 4825c8c
Author: Stephan Ewen <se...@apache.org>
Authored: Thu Aug 20 14:48:13 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:50 2015 +0200
----------------------------------------------------------------------
.../flink/runtime/webmonitor/JsonFactory.java | 136 +++++++---
.../runtime/webmonitor/WebRuntimeMonitor.java | 111 ++++----
.../runtime/webmonitor/files/MimeTypes.java | 6 +-
.../webmonitor/handlers/JobSummaryHandler.java | 68 +----
.../handlers/JobsOverviewHandler.java | 74 ++++++
.../handlers/RequestOverviewHandler.java | 17 +-
.../webmonitor/handlers/TextResponder.java | 38 ---
.../runtime/executiongraph/ExecutionGraph.java | 8 +
.../runtime/messages/webmonitor/JobDetails.java | 152 +++++++++++
.../webmonitor/JobsWithIDsOverview.java | 38 ++-
.../webmonitor/MultipleJobsDetails.java | 81 ++++++
.../messages/webmonitor/RequestJobDetails.java | 80 ++++++
.../runtime/webmonitor/WebMonitorUtils.java | 60 +++++
.../flink/runtime/jobmanager/JobManager.scala | 22 +-
.../runtime/jobmanager/MemoryArchivist.scala | 56 ++--
.../runtime/messages/GenericMessageTester.java | 264 +++++++++++++++++++
.../messages/WebMonitorMessagesTest.java | 146 ++++++++++
.../testingUtils/TestingMemoryArchivist.scala | 2 +-
18 files changed, 1127 insertions(+), 232 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
index 66449e0..40d095f 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/JsonFactory.java
@@ -20,6 +20,8 @@ package org.apache.flink.runtime.webmonitor;
import com.fasterxml.jackson.core.JsonGenerator;
import org.apache.flink.api.common.JobID;
+import org.apache.flink.runtime.execution.ExecutionState;
+import org.apache.flink.runtime.messages.webmonitor.JobDetails;
import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
@@ -37,7 +39,8 @@ public class JsonFactory {
private static final com.fasterxml.jackson.core.JsonFactory jacksonFactory =
new com.fasterxml.jackson.core.JsonFactory();
-
+
+
public static String generateConfigJSON(long refreshInterval, long timeZoneOffset, String timeZoneName) {
try {
StringWriter writer = new StringWriter();
@@ -82,6 +85,103 @@ public class JsonFactory {
}
}
+ public static String generateJobDetailsJSON(JobDetails job) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
+
+ generateSingleJobDetails(job, gen);
+
+ gen.close();
+ return writer.toString();
+ }
+ catch (Exception e) {
+ // this should not happen
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ public static String generateMultipleJobsDetailsJSON(JobDetails[] jobs) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+ gen.writeArrayFieldStart("jobs");
+ for (JobDetails detail : jobs) {
+ generateSingleJobDetails(detail, gen);
+ }
+ gen.writeEndArray();
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+ catch (Exception e) {
+ // this should not happen
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ public static String generateRunningAndFinishedJobDetailsJSON(JobDetails[] runningJobs, JobDetails[] finishedJobs) {
+ try {
+ StringWriter writer = new StringWriter();
+ JsonGenerator gen = jacksonFactory.createJsonGenerator(writer);
+
+ gen.writeStartObject();
+
+ gen.writeArrayFieldStart("running");
+ for (JobDetails detail : runningJobs) {
+ generateSingleJobDetails(detail, gen);
+ }
+ gen.writeEndArray();
+
+ gen.writeArrayFieldStart("finished");
+ for (JobDetails detail : finishedJobs) {
+ generateSingleJobDetails(detail, gen);
+ }
+ gen.writeEndArray();
+
+ gen.writeEndObject();
+
+ gen.close();
+ return writer.toString();
+ }
+ catch (Exception e) {
+ // this should not happen
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ private static void generateSingleJobDetails(JobDetails details, JsonGenerator gen) throws Exception {
+ gen.writeStartObject();
+
+ gen.writeStringField("jid", details.getJobId().toString());
+ gen.writeStringField("name", details.getJobName());
+ gen.writeStringField("state", details.getStatus().name());
+ gen.writeNumberField("start-time", details.getStartTime());
+ gen.writeNumberField("end-time", details.getEndTime());
+ gen.writeNumberField("last-modification", details.getLastUpdateTime());
+
+ gen.writeObjectFieldStart("tasks");
+ gen.writeNumberField("total", details.getNumTasks());
+
+ final int[] perState = details.getNumVerticesPerExecutionState();
+ gen.writeNumberField("pending", perState[ExecutionState.CREATED.ordinal()] +
+ perState[ExecutionState.SCHEDULED.ordinal()] +
+ perState[ExecutionState.DEPLOYING.ordinal()]);
+ gen.writeNumberField("running", perState[ExecutionState.RUNNING.ordinal()]);
+ gen.writeNumberField("finished", perState[ExecutionState.FINISHED.ordinal()]);
+ gen.writeNumberField("canceling", perState[ExecutionState.CANCELING.ordinal()]);
+ gen.writeNumberField("canceled", perState[ExecutionState.CANCELED.ordinal()]);
+ gen.writeNumberField("failed", perState[ExecutionState.FAILED.ordinal()]);
+ gen.writeEndObject();
+
+ gen.writeEndObject();
+ }
+
+
+
public static String generateJobsOverviewJSON(JobsWithIDsOverview overview) {
try {
@@ -122,40 +222,6 @@ public class JsonFactory {
}
}
- public static String createJobSummaryJSON(JobID jid, String jobName, String state,
- String start, String end, String duration,
- int numOperators, int numOperatorsPending,
- int numOperatorsRunning, int numOperatorsFinished,
- int numOperatorsCanceling, int numOperatorsCanceled,
- int numOperatorsFailed) {
- try {
- JSONObject json = new JSONObject();
-
- json.put("jid", jid.toString());
- json.put("name", jobName);
- json.put("state", state);
- json.put("start-time", start);
- json.put("end-time", end);
- json.put("duration", duration);
-
- JSONObject operators = new JSONObject();
- operators.put("total", numOperators);
- operators.put("pending", numOperatorsPending);
- operators.put("running", numOperatorsRunning);
- operators.put("finished", numOperatorsFinished);
- operators.put("canceling", numOperatorsCanceling);
- operators.put("canceled", numOperatorsCanceled);
- operators.put("failed", numOperatorsFailed);
- json.put("operators", operators);
-
- return json.toString(2);
- }
- catch (JSONException e) {
- // this should not happen
- throw new RuntimeException(e);
- }
- }
-
// --------------------------------------------------------------------------------------------
/** Don't instantiate */
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
index 5c7dc6b..85b2171 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/WebRuntimeMonitor.java
@@ -39,6 +39,7 @@ import org.apache.flink.runtime.webmonitor.handlers.ExecutionPlanHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobConfigHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobSummaryHandler;
import org.apache.flink.runtime.webmonitor.handlers.JobVerticesOverviewHandler;
+import org.apache.flink.runtime.webmonitor.handlers.JobsOverviewHandler;
import org.apache.flink.runtime.webmonitor.handlers.RequestConfigHandler;
import org.apache.flink.runtime.webmonitor.handlers.RequestHandler;
import org.apache.flink.runtime.webmonitor.handlers.RequestJobIdsHandler;
@@ -57,7 +58,7 @@ import java.util.concurrent.TimeUnit;
/**
* The root component of the web runtime monitor.
- *
+ *
* <p>The web runtime monitor is based in Netty HTTP. It uses the Netty-Router library to route
* HTTP requests of different paths to different response handlers. In addition, it serves the static
* files of the web frontend, such as HTML, CSS, or JS files.</p>
@@ -65,36 +66,34 @@ import java.util.concurrent.TimeUnit;
public class WebRuntimeMonitor implements WebMonitor {
public static final FiniteDuration DEFAULT_REQUEST_TIMEOUT = new FiniteDuration(10, TimeUnit.SECONDS);
-
- public static final long DEFAULT_REFRESH_INTERVAL = 5000;
-
+
/** Logger for web frontend startup / shutdown messages */
private static final Logger LOG = LoggerFactory.getLogger(WebRuntimeMonitor.class);
-
+
/** Teh default path under which the static contents is stored */
private static final String STATIC_CONTENTS_PATH = "resources/web-runtime-monitor";
-
+
// ------------------------------------------------------------------------
-
+
private final Object startupShutdownLock = new Object();
-
+
private final Router router;
private final int configuredPort;
private ServerBootstrap bootstrap;
-
+
private Channel serverChannel;
-
+
public WebRuntimeMonitor(Configuration config, ActorGateway jobManager, ActorGateway archive) throws IOException {
-
+
final WebMonitorConfig cfg = new WebMonitorConfig(config);
-
+
// figure out where our static contents is
final String flinkRoot = config.getString(ConfigConstants.FLINK_BASE_DIR_PATH_KEY, null);
final String configuredWebRoot = cfg.getWebRoot();
-
+
final File webRootDir;
if (configuredWebRoot != null) {
webRootDir = new File(configuredWebRoot);
@@ -103,48 +102,52 @@ public class WebRuntimeMonitor implements WebMonitor {
webRootDir = new File(flinkRoot, STATIC_CONTENTS_PATH);
}
else {
- throw new IllegalConfigurationException("The given configuration provides neither the web-document root ("
+ throw new IllegalConfigurationException("The given configuration provides neither the web-document root ("
+ WebMonitorConfig.JOB_MANAGER_WEB_DOC_ROOT_KEY + "), not the Flink installation root ("
+ ConfigConstants.FLINK_BASE_DIR_PATH_KEY + ").");
}
-
+
// validate that the doc root is a valid directory
if (!(webRootDir.exists() && webRootDir.isDirectory() && webRootDir.canRead())) {
- throw new IllegalConfigurationException("The path to the static contents (" +
+ throw new IllegalConfigurationException("The path to the static contents (" +
webRootDir.getAbsolutePath() + ") is not a readable directory.");
}
-
+
// port configuration
this.configuredPort = cfg.getWebFrontendPort();
if (this.configuredPort < 0) {
throw new IllegalArgumentException("Web frontend port is invalid: " + this.configuredPort);
}
-
+
ExecutionGraphHolder currentGraphs = new ExecutionGraphHolder(jobManager);
-
+
router = new Router()
- // config how to interact with this web server
- .GET("/config", handler(new RequestConfigHandler(cfg.getRefreshInterval())))
+ // config how to interact with this web server
+ .GET("/config", handler(new RequestConfigHandler(cfg.getRefreshInterval())))
+
+ // the overview - how many task managers, slots, free slots, ...
+ .GET("/overview", handler(new RequestOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
- // the overview - how many task managers, slots, free slots, ...
- .GET("/overview", handler(new RequestOverviewHandler(jobManager)))
+ // list of job ids for all jobs in each status
+ .GET("/jobids", handler(new RequestJobIdsHandler(jobManager, DEFAULT_REQUEST_TIMEOUT)))
- // currently running jobs
- .GET("/jobs", handler(new RequestJobIdsHandler(jobManager)))
- .GET("/jobs/:jobid", handler(new JobSummaryHandler(currentGraphs)))
- .GET("/jobs/:jobid/vertices", handler(new JobVerticesOverviewHandler(currentGraphs)))
- .GET("/jobs/:jobid/plan", handler(new ExecutionPlanHandler(currentGraphs)))
- .GET("/jobs/:jobid/config", handler(new JobConfigHandler(currentGraphs)))
+ // overview over jobs
+ .GET("/joboverview", handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, true)))
+ .GET("/joboverview/running",handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, true, false)))
+ .GET("/joboverview/completed", handler(new JobsOverviewHandler(jobManager, DEFAULT_REQUEST_TIMEOUT, false, true)))
-// .GET("/running/:jobid/:jobvertex", handler(new ExecutionPlanHandler(currentGraphs)))
-
- // the handler for the legacy requests
- .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
-
- // this handler serves all the static contents
- .GET("/:*", new StaticFileServerHandler(webRootDir));
+ .GET("/jobs/:jobid", handler(new JobSummaryHandler(currentGraphs)))
+ .GET("/jobs/:jobid/vertices", handler(new JobVerticesOverviewHandler(currentGraphs)))
+ .GET("/jobs/:jobid/plan", handler(new ExecutionPlanHandler(currentGraphs)))
+ .GET("/jobs/:jobid/config", handler(new JobConfigHandler(currentGraphs)))
+// .GET("/running/:jobid/:jobvertex", handler(new ExecutionPlanHandler(currentGraphs)))
+ // the handler for the legacy requests
+ .GET("/jobsInfo", new JobManagerInfoHandler(jobManager, archive, DEFAULT_REQUEST_TIMEOUT))
+
+ // this handler serves all the static contents
+ .GET("/:*", new StaticFileServerHandler(webRootDir));
}
@Override
@@ -153,41 +156,41 @@ public class WebRuntimeMonitor implements WebMonitor {
if (this.bootstrap != null) {
throw new IllegalStateException("The server has already been started");
}
-
+
ChannelInitializer<SocketChannel> initializer = new ChannelInitializer<SocketChannel>() {
-
+
@Override
protected void initChannel(SocketChannel ch) {
Handler handler = new Handler(router);
-
+
ch.pipeline()
- .addLast(new HttpServerCodec())
- .addLast(new HttpObjectAggregator(65536))
- .addLast(new ChunkedWriteHandler())
- .addLast(handler.name(), handler);
+ .addLast(new HttpServerCodec())
+ .addLast(new HttpObjectAggregator(65536))
+ .addLast(new ChunkedWriteHandler())
+ .addLast(handler.name(), handler);
}
};
-
+
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
-
+
this.bootstrap = new ServerBootstrap();
this.bootstrap
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(initializer);
-
+
Channel ch = this.bootstrap.bind(configuredPort).sync().channel();
this.serverChannel = ch;
-
+
InetSocketAddress bindAddress = (InetSocketAddress) ch.localAddress();
String address = bindAddress.getAddress().getHostAddress();
int port = bindAddress.getPort();
-
+
LOG.info("Web frontend listening at " + address + ':' + port);
}
}
-
+
@Override
public void stop() throws Exception {
synchronized (startupShutdownLock) {
@@ -203,7 +206,7 @@ public class WebRuntimeMonitor implements WebMonitor {
}
}
}
-
+
@Override
public int getServerPort() {
Channel server = this.serverChannel;
@@ -215,15 +218,15 @@ public class WebRuntimeMonitor implements WebMonitor {
LOG.error("Cannot access local server port", e);
}
}
-
+
return -1;
}
-
+
// ------------------------------------------------------------------------
// Utilities
// ------------------------------------------------------------------------
-
+
private static RuntimeMonitorHandler handler(RequestHandler handler) {
return new RuntimeMonitorHandler(handler);
}
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/MimeTypes.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/MimeTypes.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/MimeTypes.java
index 9c3b45d..4efd029 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/MimeTypes.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/files/MimeTypes.java
@@ -22,10 +22,10 @@ import java.util.HashMap;
/**
* Simple utility class that resolves file extensions to MIME types.
- *
- * <p>There are various solutions built into Java that depend on extra resource and configuration
+ * <p>
+ * There are various solutions built into Java that depend on extra resource and configuration
* files. They are designed to be composable and extensible, but also unfortunately tricky to control.
- * This is meant to be a simple solution that may eventually be subsumed by a better one.</p>
+ * This is meant to be a simple solution that may eventually be subsumed by a better one.
*/
public class MimeTypes {
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobSummaryHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobSummaryHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobSummaryHandler.java
index 2dfe4de..a682fd0 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobSummaryHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobSummaryHandler.java
@@ -18,24 +18,18 @@
package org.apache.flink.runtime.webmonitor.handlers;
-import org.apache.flink.api.common.JobID;
-import org.apache.flink.runtime.execution.ExecutionState;
import org.apache.flink.runtime.executiongraph.ExecutionGraph;
-import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
-import org.apache.flink.runtime.jobgraph.JobStatus;
+import org.apache.flink.runtime.messages.webmonitor.JobDetails;
import org.apache.flink.runtime.webmonitor.ExecutionGraphHolder;
import org.apache.flink.runtime.webmonitor.JsonFactory;
+import org.apache.flink.runtime.webmonitor.WebMonitorUtils;
-import java.text.SimpleDateFormat;
-import java.util.Date;
import java.util.Map;
/**
* Request handler that returns a summary of the job status.
*/
public class JobSummaryHandler extends AbstractExecutionGraphRequestHandler implements RequestHandler.JsonResponse {
-
- private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public JobSummaryHandler(ExecutionGraphHolder executionGraphHolder) {
super(executionGraphHolder);
@@ -43,61 +37,7 @@ public class JobSummaryHandler extends AbstractExecutionGraphRequestHandler impl
@Override
public String handleRequest(ExecutionGraph graph, Map<String, String> params) throws Exception {
-
- JobID jid = graph.getJobID();
- String name = graph.getJobName();
-
- long startTime = graph.getStatusTimestamp(JobStatus.CREATED);
- long endTime = graph.getState().isTerminalState() ?
- graph.getStatusTimestamp(graph.getState()) : -1;
-
- long duration = endTime == -1 ? System.currentTimeMillis() - startTime :
- endTime - startTime;
-
- String startTimeString;
- String endTimeTimeString;
- String durationString = duration + " msecs";
-
- synchronized (dateFormatter) {
- startTimeString = dateFormatter.format(new Date(startTime));
- endTimeTimeString = endTime == -1 ? "(pending)" : dateFormatter.format(new Date(endTime));
- }
-
- String status = graph.getState().name();
-
- int pending = 0;
- int running = 0;
- int finished = 0;
- int canceling = 0;
- int canceled = 0;
- int failed = 0;
-
- for (ExecutionJobVertex vertex : graph.getVerticesTopologically()) {
- ExecutionState aggState = vertex.getAggregateState();
- switch (aggState) {
- case FINISHED:
- finished++;
- break;
- case FAILED:
- failed++;
- break;
- case CANCELED:
- canceled++;
- break;
- case RUNNING:
- running++;
- break;
- case CANCELING:
- canceling++;
- break;
- default:
- pending++;
- }
- }
-
- int total = pending + running + finished + canceling + canceled + failed;
-
- return JsonFactory.createJobSummaryJSON(jid, name, status, startTimeString, endTimeTimeString, durationString,
- total, pending, running, finished, canceling, canceled, failed);
+ JobDetails details = WebMonitorUtils.createDetailsForJob(graph);
+ return JsonFactory.generateJobDetailsJSON(details);
}
}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobsOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobsOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobsOverviewHandler.java
new file mode 100644
index 0000000..304c521
--- /dev/null
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/JobsOverviewHandler.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor.handlers;
+
+import org.apache.flink.runtime.instance.ActorGateway;
+import org.apache.flink.runtime.messages.webmonitor.MultipleJobsDetails;
+import org.apache.flink.runtime.messages.webmonitor.RequestJobDetails;
+import org.apache.flink.runtime.webmonitor.JsonFactory;
+
+import scala.concurrent.Await;
+import scala.concurrent.Future;
+import scala.concurrent.duration.FiniteDuration;
+
+import java.util.Map;
+
+/**
+ * Request handler that returns a summary of the job status.
+ */
+public class JobsOverviewHandler implements RequestHandler, RequestHandler.JsonResponse {
+
+ private final ActorGateway jobManager;
+
+ private final FiniteDuration timeout;
+
+ private final boolean includeRunningJobs;
+ private final boolean includeFinishedJobs;
+
+
+ public JobsOverviewHandler(ActorGateway jobManager, FiniteDuration timeout,
+ boolean includeRunningJobs, boolean includeFinishedJobs) {
+ this.jobManager = jobManager;
+ this.timeout = timeout;
+ this.includeRunningJobs = includeRunningJobs;
+ this.includeFinishedJobs = includeFinishedJobs;
+ }
+
+ @Override
+ public String handleRequest(Map<String, String> params) throws Exception {
+ try {
+ Future<Object> future = jobManager.ask(
+ new RequestJobDetails(includeRunningJobs, includeFinishedJobs), timeout);
+
+ MultipleJobsDetails result = (MultipleJobsDetails) Await.result(future, timeout);
+
+ if (includeRunningJobs && includeFinishedJobs) {
+ return JsonFactory.generateRunningAndFinishedJobDetailsJSON(
+ result.getRunningJobs(), result.getFinishedJobs());
+ }
+ else {
+ return JsonFactory.generateMultipleJobsDetailsJSON(
+ includeRunningJobs ? result.getRunningJobs() : result.getFinishedJobs());
+ }
+ }
+ catch (Exception e) {
+ throw new Exception("Failed to fetch the status overview: " + e.getMessage(), e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
index 7268945..377e86a 100644
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
+++ b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/RequestOverviewHandler.java
@@ -22,7 +22,6 @@ import org.apache.flink.runtime.instance.ActorGateway;
import org.apache.flink.runtime.messages.webmonitor.RequestStatusOverview;
import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
import org.apache.flink.runtime.webmonitor.JsonFactory;
-import org.apache.flink.runtime.webmonitor.WebRuntimeMonitor;
import scala.concurrent.Await;
import scala.concurrent.Future;
@@ -35,16 +34,12 @@ import java.util.Map;
* TaskManagers are currently connected, and how many jobs are running.
*/
public class RequestOverviewHandler implements RequestHandler, RequestHandler.JsonResponse {
-
+
private final ActorGateway jobManager;
-
+
private final FiniteDuration timeout;
-
-
- public RequestOverviewHandler(ActorGateway jobManager) {
- this(jobManager, WebRuntimeMonitor.DEFAULT_REQUEST_TIMEOUT);
- }
-
+
+
public RequestOverviewHandler(ActorGateway jobManager, FiniteDuration timeout) {
if (jobManager == null || timeout == null) {
throw new NullPointerException();
@@ -52,7 +47,7 @@ public class RequestOverviewHandler implements RequestHandler, RequestHandler.J
this.jobManager = jobManager;
this.timeout = timeout;
}
-
+
@Override
public String handleRequest(Map<String, String> params) throws Exception {
try {
@@ -64,4 +59,4 @@ public class RequestOverviewHandler implements RequestHandler, RequestHandler.J
throw new Exception("Failed to fetch the status overview: " + e.getMessage(), e);
}
}
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/TextResponder.java
----------------------------------------------------------------------
diff --git a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/TextResponder.java b/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/TextResponder.java
deleted file mode 100644
index 5cc7273..0000000
--- a/flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/handlers/TextResponder.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.flink.runtime.webmonitor.handlers;
-
-import java.util.Map;
-
-/**
- * Simple placeholder responder with a JSON object holding one key/value pair
- */
-public class TextResponder implements RequestHandler, RequestHandler.TextResponse {
-
- private final String message;
-
- public TextResponder(String message) {
- this.message = message == null ? "" : message;
- }
-
- @Override
- public String handleRequest(Map<String, String> params) throws Exception {
- return message;
- }
-}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java
index 1d8a37c..47175a9 100755
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/ExecutionGraph.java
@@ -280,6 +280,14 @@ public class ExecutionGraph implements Serializable {
// Configuration of Data-flow wide execution settings
// --------------------------------------------------------------------------------------------
+ /**
+ * Gets the number of job vertices currently held by this execution graph.
+ * @return The current number of job vertices.
+ */
+ public int getNumberOfExecutionJobVertices() {
+ return this.verticesInCreationOrder.size();
+ }
+
public void setNumberOfRetriesLeft(int numberOfRetriesLeft) {
if (numberOfRetriesLeft < -1) {
throw new IllegalArgumentException();
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/JobDetails.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/JobDetails.java b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/JobDetails.java
new file mode 100644
index 0000000..c5f0c15
--- /dev/null
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/JobDetails.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.messages.webmonitor;
+
+import org.apache.flink.api.common.JobID;
+import org.apache.flink.runtime.jobgraph.JobStatus;
+
+import java.util.Arrays;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An actor message with a detailed overview of the current status of a job.
+ */
+public class JobDetails implements java.io.Serializable {
+
+ private static final long serialVersionUID = -3391462110304948766L;
+
+ private final JobID jobId;
+
+ private final String jobName;
+
+ private final long startTime;
+
+ private final long endTime;
+
+ private final JobStatus status;
+
+ private final long lastUpdateTime;
+
+ private final int[] numVerticesPerExecutionState;
+
+ private final int numTasks;
+
+
+ public JobDetails(JobID jobId, String jobName,
+ long startTime, long endTime,
+ JobStatus status,
+ long lastUpdateTime,
+ int[] numVerticesPerExecutionState, int numTasks) {
+
+ this.jobId = checkNotNull(jobId);
+ this.jobName = checkNotNull(jobName);
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.status = checkNotNull(status);
+ this.lastUpdateTime = lastUpdateTime;
+ this.numVerticesPerExecutionState = checkNotNull(numVerticesPerExecutionState);
+ this.numTasks = numTasks;
+ }
+
+ // ------------------------------------------------------------------------
+
+ public JobID getJobId() {
+ return jobId;
+ }
+
+ public String getJobName() {
+ return jobName;
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public long getEndTime() {
+ return endTime;
+ }
+
+ public JobStatus getStatus() {
+ return status;
+ }
+
+ public long getLastUpdateTime() {
+ return lastUpdateTime;
+ }
+
+ public int getNumTasks() {
+ return numTasks;
+ }
+
+ public int[] getNumVerticesPerExecutionState() {
+ return numVerticesPerExecutionState;
+ }
+
+ // ------------------------------------------------------------------------
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ else if (o != null && o.getClass() == JobDetails.class) {
+ JobDetails that = (JobDetails) o;
+
+ return this.endTime == that.endTime &&
+ this.lastUpdateTime == that.lastUpdateTime &&
+ this.numTasks == that.numTasks &&
+ this.startTime == that.startTime &&
+ this.status == that.status &&
+ this.jobId.equals(that.jobId) &&
+ this.jobName.equals(that.jobName) &&
+ Arrays.equals(this.numVerticesPerExecutionState, that.numVerticesPerExecutionState);
+ }
+ else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int result = jobId.hashCode();
+ result = 31 * result + jobName.hashCode();
+ result = 31 * result + (int) (startTime ^ (startTime >>> 32));
+ result = 31 * result + (int) (endTime ^ (endTime >>> 32));
+ result = 31 * result + status.hashCode();
+ result = 31 * result + (int) (lastUpdateTime ^ (lastUpdateTime >>> 32));
+ result = 31 * result + Arrays.hashCode(numVerticesPerExecutionState);
+ result = 31 * result + numTasks;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "JobDetails {" +
+ "jobId=" + jobId +
+ ", jobName='" + jobName + '\'' +
+ ", startTime=" + startTime +
+ ", endTime=" + endTime +
+ ", status=" + status +
+ ", lastUpdateTime=" + lastUpdateTime +
+ ", numVerticesPerExecutionState=" + Arrays.toString(numVerticesPerExecutionState) +
+ ", numTasks=" + numTasks +
+ '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/JobsWithIDsOverview.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/JobsWithIDsOverview.java b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/JobsWithIDsOverview.java
index a261c2a..de24c15 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/JobsWithIDsOverview.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/JobsWithIDsOverview.java
@@ -23,6 +23,8 @@ import org.apache.flink.api.common.JobID;
import java.util.ArrayList;
import java.util.List;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
* An overview of how many jobs are in which status.
*/
@@ -38,10 +40,10 @@ public class JobsWithIDsOverview implements InfoMessage {
public JobsWithIDsOverview(List<JobID> jobsRunningOrPending, List<JobID> jobsFinished,
List<JobID> jobsCancelled, List<JobID> jobsFailed) {
- this.jobsRunningOrPending = jobsRunningOrPending;
- this.jobsFinished = jobsFinished;
- this.jobsCancelled = jobsCancelled;
- this.jobsFailed = jobsFailed;
+ this.jobsRunningOrPending = checkNotNull(jobsRunningOrPending);
+ this.jobsFinished = checkNotNull(jobsFinished);
+ this.jobsCancelled = checkNotNull(jobsCancelled);
+ this.jobsFailed = checkNotNull(jobsFailed);
}
public JobsWithIDsOverview(JobsWithIDsOverview first, JobsWithIDsOverview second) {
@@ -69,6 +71,32 @@ public class JobsWithIDsOverview implements InfoMessage {
// ------------------------------------------------------------------------
+
+ @Override
+ public int hashCode() {
+ return jobsRunningOrPending.hashCode() ^
+ jobsFinished.hashCode() ^
+ jobsCancelled.hashCode() ^
+ jobsFailed.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ else if (obj instanceof JobsWithIDsOverview) {
+ JobsWithIDsOverview that = (JobsWithIDsOverview) obj;
+ return this.jobsRunningOrPending.equals(that.jobsRunningOrPending) &&
+ this.jobsFinished.equals(that.jobsFinished) &&
+ this.jobsCancelled.equals(that.jobsCancelled) &&
+ this.jobsFailed.equals(that.jobsFailed);
+ }
+ else {
+ return false;
+ }
+ }
+
@Override
public String toString() {
return "JobsOverview {" +
@@ -82,6 +110,8 @@ public class JobsWithIDsOverview implements InfoMessage {
// ------------------------------------------------------------------------
private static ArrayList<JobID> combine(List<JobID> first, List<JobID> second) {
+ checkNotNull(first);
+ checkNotNull(second);
ArrayList<JobID> result = new ArrayList<JobID>(first.size() + second.size());
result.addAll(first);
result.addAll(second);
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/MultipleJobsDetails.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/MultipleJobsDetails.java b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/MultipleJobsDetails.java
new file mode 100644
index 0000000..47de58a
--- /dev/null
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/MultipleJobsDetails.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.messages.webmonitor;
+
+import java.util.Arrays;
+
+/**
+ * An actor messages describing details of various jobs. This message is sent for example
+ * in response to the {@link org.apache.flink.runtime.messages.webmonitor.RequestJobDetails}
+ * message.
+ */
+public class MultipleJobsDetails implements java.io.Serializable {
+
+ private static final long serialVersionUID = -1526236139616019127L;
+
+ private static final JobDetails[] EMPTY = new JobDetails[0];
+
+ private final JobDetails[] runningJobs;
+ private final JobDetails[] finishedJobs;
+
+ public MultipleJobsDetails(JobDetails[] running, JobDetails[] finished) {
+ this.runningJobs = running == null ? EMPTY : running;
+ this.finishedJobs = finished == null ? EMPTY : finished;
+ }
+
+ // ------------------------------------------------------------------------
+
+ public JobDetails[] getRunningJobs() {
+ return runningJobs;
+ }
+
+ public JobDetails[] getFinishedJobs() {
+ return finishedJobs;
+ }
+
+ // ------------------------------------------------------------------------
+
+ @Override
+ public int hashCode() {
+ return Arrays.deepHashCode(runningJobs) + Arrays.deepHashCode(finishedJobs);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ else if (obj instanceof MultipleJobsDetails) {
+ MultipleJobsDetails that = (MultipleJobsDetails) obj;
+ return Arrays.deepEquals(this.runningJobs, that.runningJobs) &&
+ Arrays.deepEquals(this.finishedJobs, that.finishedJobs);
+ }
+ else {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "MultipleJobsDetails {" +
+ "running=" + Arrays.toString(runningJobs) +
+ ", finished=" + Arrays.toString(finishedJobs) +
+ '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/RequestJobDetails.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/RequestJobDetails.java b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/RequestJobDetails.java
new file mode 100644
index 0000000..91025bf
--- /dev/null
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/messages/webmonitor/RequestJobDetails.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.messages.webmonitor;
+
+/**
+ * This message requests an overview of the jobs on the JobManager,
+ * including running jobs and/or finished jobs.
+ * <p>
+ * The response to this message is a
+ * {@link org.apache.flink.runtime.messages.webmonitor.MultipleJobsDetails} message.
+ */
+public class RequestJobDetails implements InfoMessage {
+
+ private static final long serialVersionUID = 5208137000412166747L;
+
+ private final boolean includeRunning;
+ private final boolean includeFinished;
+
+ public RequestJobDetails(boolean includeRunning, boolean includeFinished) {
+ this.includeRunning = includeRunning;
+ this.includeFinished = includeFinished;
+ }
+
+ // ------------------------------------------------------------------------
+
+ public boolean shouldIncludeFinished() {
+ return includeFinished;
+ }
+
+ public boolean shouldIncludeRunning() {
+ return includeRunning;
+ }
+
+ // ------------------------------------------------------------------------
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ else if (o instanceof RequestJobDetails) {
+ RequestJobDetails that = (RequestJobDetails) o;
+
+ return this.includeFinished == that.includeFinished &&
+ this.includeRunning == that.includeRunning;
+ }
+ else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return (includeRunning ? 31 : 0) + (includeFinished ? 1 : 0);
+ }
+
+ @Override
+ public String toString() {
+ return "RequestJobDetails{" +
+ "includeRunning=" + includeRunning +
+ ", includeFinished=" + includeFinished +
+ '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java b/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
new file mode 100644
index 0000000..aa784e6
--- /dev/null
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/WebMonitorUtils.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.webmonitor;
+
+import org.apache.flink.runtime.execution.ExecutionState;
+import org.apache.flink.runtime.executiongraph.ExecutionGraph;
+import org.apache.flink.runtime.executiongraph.ExecutionJobVertex;
+import org.apache.flink.runtime.executiongraph.ExecutionVertex;
+import org.apache.flink.runtime.jobgraph.JobStatus;
+import org.apache.flink.runtime.messages.webmonitor.JobDetails;
+
+/**
+ * Utilities for the web runtime monitor. This class contains for example methods to build
+ * messages with aggregate information about the state of an execution graph, to be send
+ * to the web server.
+ */
+public class WebMonitorUtils {
+
+ public static JobDetails createDetailsForJob(ExecutionGraph job) {
+ JobStatus status = job.getState();
+
+ long started = job.getStatusTimestamp(JobStatus.CREATED);
+ long finished = status.isTerminalState() ? job.getStatusTimestamp(status) : -1L;
+
+ int[] countsPerStatus = new int[ExecutionState.values().length];
+ long lastChanged = 0;
+ int numTotalTasks = 0;
+
+ for (ExecutionJobVertex ejv : job.getVerticesTopologically()) {
+ ExecutionVertex[] vertices = ejv.getTaskVertices();
+ numTotalTasks += vertices.length;
+
+ for (ExecutionVertex vertex : vertices) {
+ ExecutionState state = vertex.getExecutionState();
+ countsPerStatus[state.ordinal()]++;
+ lastChanged = Math.max(lastChanged, vertex.getStateTimestamp(state));
+ }
+ }
+
+ return new JobDetails(job.getJobID(), job.getJobName(),
+ started, finished, status, lastChanged,
+ countsPerStatus, numTotalTasks);
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
index 64ed129..d93b2ed 100644
--- a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
+++ b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/JobManager.scala
@@ -56,7 +56,7 @@ import org.apache.flink.runtime.security.SecurityUtils
import org.apache.flink.runtime.security.SecurityUtils.FlinkSecuredRunner
import org.apache.flink.runtime.taskmanager.TaskManager
import org.apache.flink.runtime.util._
-import org.apache.flink.runtime.webmonitor.WebMonitor
+import org.apache.flink.runtime.webmonitor.{WebMonitorUtils, WebMonitor}
import org.apache.flink.runtime.{FlinkActor, StreamingMode, LeaderSessionMessageFilter}
import org.apache.flink.runtime.LogMessages
import org.apache.flink.runtime.akka.{ListeningBehaviour, AkkaUtils}
@@ -921,6 +921,26 @@ class JobManager(
ourJobs, archiveOverview)
}(context.dispatcher)
+ case msg : RequestJobDetails =>
+
+ val ourDetails: Array[JobDetails] = if (msg.shouldIncludeRunning()) {
+ currentJobs.values.map {
+ v => WebMonitorUtils.createDetailsForJob(v._1)
+ }.toArray[JobDetails]
+ } else {
+ null
+ }
+
+ if (msg.shouldIncludeFinished()) {
+ val future = (archive ? msg)(timeout)
+ future.onSuccess {
+ case archiveDetails: MultipleJobsDetails =>
+ theSender ! new MultipleJobsDetails(ourDetails, archiveDetails.getFinishedJobs())
+ }(context.dispatcher)
+ } else {
+ theSender ! new MultipleJobsDetails(ourDetails, null)
+ }
+
case _ => log.error("Unrecognized info message " + actorMessage)
}
}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala
----------------------------------------------------------------------
diff --git a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala
index 702e34b..55a7ccb 100644
--- a/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala
+++ b/flink-runtime/src/main/scala/org/apache/flink/runtime/jobmanager/MemoryArchivist.scala
@@ -20,10 +20,12 @@ package org.apache.flink.runtime.jobmanager
import java.util
+import akka.actor.ActorRef
import grizzled.slf4j.Logger
import org.apache.flink.api.common.JobID
import org.apache.flink.runtime.jobgraph.JobStatus
import org.apache.flink.runtime.messages.accumulators._
+import org.apache.flink.runtime.webmonitor.WebMonitorUtils
import org.apache.flink.runtime.{FlinkActor, LogMessages}
import org.apache.flink.runtime.messages.webmonitor._
import org.apache.flink.runtime.executiongraph.ExecutionGraph
@@ -64,12 +66,12 @@ class MemoryArchivist(private val max_entries: Int)
* Map of execution graphs belonging to recently started jobs with the time stamp of the last
* received job event. The insert order is preserved through a LinkedHashMap.
*/
- val graphs = mutable.LinkedHashMap[JobID, ExecutionGraph]()
+ protected val graphs = mutable.LinkedHashMap[JobID, ExecutionGraph]()
/* Counters for finished, canceled, and failed jobs */
- var finishedCnt: Int = 0
- var canceledCnt: Int = 0
- var failedCnt: Int = 0
+ private[this] var finishedCnt: Int = 0
+ private[this] var canceledCnt: Int = 0
+ private[this] var failedCnt: Int = 0
override def preStart(): Unit = {
log.info(s"Started memory archivist ${self.path}")
@@ -93,6 +95,8 @@ class MemoryArchivist(private val max_entries: Int)
trimHistory()
+ case msg : InfoMessage => handleWebServerInfoMessage(msg, sender())
+
case RequestArchivedJob(jobID: JobID) =>
val graph = graphs.get(jobID)
sender ! decorateMessage(ArchivedJob(graph))
@@ -115,23 +119,6 @@ class MemoryArchivist(private val max_entries: Int)
case RequestJobCounts =>
sender ! decorateMessage((finishedCnt, canceledCnt, failedCnt))
- case _ : RequestJobsOverview =>
- try {
- sender ! createJobsOverview()
- }
- catch {
- case t: Throwable => log.error("Exception while creating the jobs overview", t)
- }
-
- case _ : RequestJobsWithIDsOverview =>
- try {
- sender ! createJobsWithIDsOverview()
- }
- catch {
- case t: Throwable => log.error("Exception while creating the jobs overview", t)
- }
-
-
case RequestAccumulatorResults(jobID) =>
try {
graphs.get(jobID) match {
@@ -165,6 +152,33 @@ class MemoryArchivist(private val max_entries: Int)
throw new RuntimeException("Received unknown message " + message)
}
+
+ private def handleWebServerInfoMessage(message: InfoMessage, theSender: ActorRef): Unit = {
+ message match {
+ case _ : RequestJobsOverview =>
+ try {
+ sender ! createJobsOverview()
+ }
+ catch {
+ case t: Throwable => log.error("Exception while creating the jobs overview", t)
+ }
+
+ case _ : RequestJobsWithIDsOverview =>
+ try {
+ sender ! createJobsWithIDsOverview()
+ }
+ catch {
+ case t: Throwable => log.error("Exception while creating the jobs overview", t)
+ }
+
+ case _ : RequestJobDetails =>
+ val details = graphs.values.map {
+ v => WebMonitorUtils.createDetailsForJob(v)
+ }.toArray[JobDetails]
+
+ theSender ! new MultipleJobsDetails(null, details)
+ }
+ }
// --------------------------------------------------------------------------
// Request Responses
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/test/java/org/apache/flink/runtime/messages/GenericMessageTester.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/messages/GenericMessageTester.java b/flink-runtime/src/test/java/org/apache/flink/runtime/messages/GenericMessageTester.java
new file mode 100644
index 0000000..dcf4839
--- /dev/null
+++ b/flink-runtime/src/test/java/org/apache/flink/runtime/messages/GenericMessageTester.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.messages;
+
+import org.apache.flink.api.common.JobID;
+import org.apache.flink.runtime.jobgraph.JobStatus;
+import org.apache.flink.runtime.testutils.CommonTestUtils;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.ParameterizedType;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+public class GenericMessageTester {
+
+ public static void testMessageInstance(Serializable instance) throws Exception {
+ Serializable copy = CommonTestUtils.createCopySerializable(instance);
+
+ // test equals, hash code, toString
+ assertTrue(instance.equals(copy));
+ assertTrue(copy.equals(instance));
+ assertTrue(instance.hashCode() == copy.hashCode());
+ assertTrue(instance.toString().equals(copy.toString()));
+ }
+
+ public static void testMessageInstances(Serializable instance1, Serializable instance2) throws Exception {
+ // test equals, hash code, toString
+ assertTrue(instance1.equals(instance2));
+ assertTrue(instance2.equals(instance1));
+ assertTrue(instance1.hashCode() == instance2.hashCode());
+ assertTrue(instance1.toString().equals(instance2.toString()));
+
+ // test serializability
+ Serializable copy = CommonTestUtils.createCopySerializable(instance1);
+ assertTrue(instance1.equals(copy));
+ assertTrue(copy.equals(instance1));
+ assertTrue(instance1.hashCode() == copy.hashCode());
+ }
+
+ // ------------------------------------------------------------------------
+ // Random Generators
+ // ------------------------------------------------------------------------
+
+ @SuppressWarnings("unchecked")
+ public static <T> T instantiateGeneric(Class<T> messageClass, Random rnd, Instantiator<?>... extraInstantiators) {
+ try {
+ // build the map of extra instantiators
+ Map<Class<?>, Instantiator<?>> extraInsts = new HashMap<>();
+ for (Instantiator<?> inst : extraInstantiators) {
+ Class<?> type = (Class<?>) ((ParameterizedType) inst.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
+ assertNotNull("Cannot get type for extra instantiator", type);
+ extraInsts.put(type, inst);
+ }
+
+ Constructor<?>[] constructors = messageClass.getConstructors();
+
+ Class<?> missingType = null;
+
+ outer:
+ for (Constructor<?> constructor : constructors) {
+
+ Class<?>[] paramTypes = constructor.getParameterTypes();
+ Object[] params = new Object[paramTypes.length];
+
+ for (int i = 0; i < paramTypes.length; i++) {
+ Instantiator<?> inst = extraInsts.get(paramTypes[i]);
+ if (inst == null) {
+ inst = INSTANTIATORS.get(paramTypes[i]);
+ }
+
+ if (inst == null) {
+ missingType = paramTypes[i];
+ continue outer;
+ }
+
+ params[i] = inst.instantiate(rnd);
+ }
+
+ return (T) constructor.newInstance(params);
+ }
+
+ //noinspection ConstantConditions
+ fail("No instantiator available for type " + missingType.getCanonicalName());
+ throw new RuntimeException();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail("Could not perform reflective tests: " + e.getMessage());
+ throw new RuntimeException();
+ }
+ }
+
+ public static String randomString(Random rnd) {
+ int len = rnd.nextInt(64 + 1);
+ char[] chars = new char[len];
+ for (int i = 0; i < len; i++) {
+ chars[i] = (char) rnd.nextInt();
+ }
+ return new String(chars);
+ }
+
+ public static JobID randomJobId(Random rnd) {
+ return new JobID(rnd.nextLong(), rnd.nextLong());
+ }
+
+ public static JobStatus randomJobStatus(Random rnd) {
+ return JobStatus.values()[rnd.nextInt(JobStatus.values().length)];
+ }
+
+ // ------------------------------------------------------------------------
+ // Map of Instantiators
+ // ------------------------------------------------------------------------
+
+ private static final Map<Class<?>, Instantiator<?>> INSTANTIATORS = new HashMap<>();
+
+ static {
+ INSTANTIATORS.put(boolean.class, new BooleanInstantiator());
+ INSTANTIATORS.put(Boolean.class, new BooleanInstantiator());
+
+ INSTANTIATORS.put(char.class, new CharInstantiator());
+ INSTANTIATORS.put(Character.class, new CharInstantiator());
+
+ INSTANTIATORS.put(byte.class, new ByteInstantiator());
+ INSTANTIATORS.put(Byte.class, new ByteInstantiator());
+
+ INSTANTIATORS.put(short.class, new ShortInstantiator());
+ INSTANTIATORS.put(Short.class, new ShortInstantiator());
+
+ INSTANTIATORS.put(int.class, new IntInstantiator());
+ INSTANTIATORS.put(Integer.class, new IntInstantiator());
+
+ INSTANTIATORS.put(long.class, new LongInstantiator());
+ INSTANTIATORS.put(Long.class, new LongInstantiator());
+
+ INSTANTIATORS.put(float.class, new FloatInstantiator());
+ INSTANTIATORS.put(Float.class, new FloatInstantiator());
+
+ INSTANTIATORS.put(double.class, new DoubleInstantiator());
+ INSTANTIATORS.put(Double.class, new DoubleInstantiator());
+
+ INSTANTIATORS.put(String.class, new StringInstantiator());
+
+ INSTANTIATORS.put(JobID.class, new JobIdInstantiator());
+ INSTANTIATORS.put(JobStatus.class, new JobStatusInstantiator());
+ }
+
+ // ------------------------------------------------------------------------
+ // Instantiators
+ // ------------------------------------------------------------------------
+
+ public static interface Instantiator<T> {
+
+ T instantiate(Random rnd);
+ }
+
+ public static class ByteInstantiator implements Instantiator<Byte> {
+
+ @Override
+ public Byte instantiate(Random rnd) {
+ int i = rnd.nextInt(100);
+ return (byte) i;
+ }
+ }
+
+ public static class ShortInstantiator implements Instantiator<Short> {
+
+ @Override
+ public Short instantiate(Random rnd) {
+ return (short) rnd.nextInt(30000);
+ }
+ }
+
+ public static class IntInstantiator implements Instantiator<Integer> {
+
+ @Override
+ public Integer instantiate(Random rnd) {
+ return rnd.nextInt(Integer.MAX_VALUE);
+ }
+ }
+
+ public static class LongInstantiator implements Instantiator<Long> {
+
+ @Override
+ public Long instantiate(Random rnd) {
+ return (long) rnd.nextInt(Integer.MAX_VALUE);
+ }
+ }
+
+ public static class FloatInstantiator implements Instantiator<Float> {
+
+ @Override
+ public Float instantiate(Random rnd) {
+ return rnd.nextFloat();
+ }
+ }
+
+ public static class DoubleInstantiator implements Instantiator<Double> {
+
+ @Override
+ public Double instantiate(Random rnd) {
+ return rnd.nextDouble();
+ }
+ }
+
+ public static class BooleanInstantiator implements Instantiator<Boolean> {
+
+ @Override
+ public Boolean instantiate(Random rnd) {
+ return rnd.nextBoolean();
+ }
+ }
+
+ public static class CharInstantiator implements Instantiator<Character> {
+
+ @Override
+ public Character instantiate(Random rnd) {
+ return (char) rnd.nextInt(30000);
+ }
+ }
+
+ public static class StringInstantiator implements Instantiator<String> {
+
+ @Override
+ public String instantiate(Random rnd) {
+ return randomString(rnd);
+ }
+ }
+
+ public static class JobIdInstantiator implements Instantiator<JobID> {
+
+ @Override
+ public JobID instantiate(Random rnd) {
+ return randomJobId(rnd);
+ }
+ }
+
+ public static class JobStatusInstantiator implements Instantiator<JobStatus> {
+
+ @Override
+ public JobStatus instantiate(Random rnd) {
+ return randomJobStatus(rnd);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/test/java/org/apache/flink/runtime/messages/WebMonitorMessagesTest.java
----------------------------------------------------------------------
diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/messages/WebMonitorMessagesTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/messages/WebMonitorMessagesTest.java
new file mode 100644
index 0000000..b70853e
--- /dev/null
+++ b/flink-runtime/src/test/java/org/apache/flink/runtime/messages/WebMonitorMessagesTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.runtime.messages;
+
+import org.apache.flink.api.common.JobID;
+import org.apache.flink.runtime.execution.ExecutionState;
+import org.apache.flink.runtime.jobgraph.JobStatus;
+import org.apache.flink.runtime.messages.webmonitor.JobDetails;
+import org.apache.flink.runtime.messages.webmonitor.JobsOverview;
+import org.apache.flink.runtime.messages.webmonitor.JobsWithIDsOverview;
+import org.apache.flink.runtime.messages.webmonitor.MultipleJobsDetails;
+import org.apache.flink.runtime.messages.webmonitor.RequestJobsOverview;
+import org.apache.flink.runtime.messages.webmonitor.RequestJobsWithIDsOverview;
+import org.apache.flink.runtime.messages.webmonitor.RequestStatusOverview;
+import org.apache.flink.runtime.messages.webmonitor.StatusOverview;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+public class WebMonitorMessagesTest {
+
+ @Test
+ public void testStatusMessages() {
+ try {
+ final Random rnd = new Random();
+
+ GenericMessageTester.testMessageInstance(RequestJobsOverview.getInstance());
+ GenericMessageTester.testMessageInstance(RequestJobsWithIDsOverview.getInstance());
+ GenericMessageTester.testMessageInstance(RequestStatusOverview.getInstance());
+
+ GenericMessageTester.testMessageInstance(GenericMessageTester.instantiateGeneric(RequestJobsOverview.class, rnd));
+
+ GenericMessageTester.testMessageInstance(GenericMessageTester.instantiateGeneric(StatusOverview.class, rnd));
+ GenericMessageTester.testMessageInstance(GenericMessageTester.instantiateGeneric(JobsOverview.class, rnd));
+
+ GenericMessageTester.testMessageInstance(new JobsWithIDsOverview(
+ randomIds(rnd), randomIds(rnd), randomIds(rnd), randomIds(rnd)));
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testJobDetailsMessage() {
+ try {
+ final Random rnd = new Random();
+
+ int[] numVerticesPerState = new int[ExecutionState.values().length];
+ int numTotal = 0;
+
+ for (int i = 0; i < numVerticesPerState.length; i++) {
+ int count = rnd.nextInt(55);
+ numVerticesPerState[i] = count;
+ numTotal += count;
+ }
+
+ long time = rnd.nextLong();
+ long endTime = rnd.nextBoolean() ? -1L : time + rnd.nextInt();
+ long lastModified = endTime == -1 ? time + rnd.nextInt() : endTime;
+
+ String name = GenericMessageTester.randomString(rnd);
+ JobID jid = GenericMessageTester.randomJobId(rnd);
+ JobStatus status = GenericMessageTester.randomJobStatus(rnd);
+
+ JobDetails msg1 = new JobDetails(jid, name, time, endTime, status, lastModified, numVerticesPerState, numTotal);
+ JobDetails msg2 = new JobDetails(jid, name, time, endTime, status, lastModified, numVerticesPerState, numTotal);
+
+ GenericMessageTester.testMessageInstances(msg1, msg2);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testMultipleJobDetails() {
+ try {
+ final Random rnd = new Random();
+ GenericMessageTester.testMessageInstance(
+ new MultipleJobsDetails(randomJobDetails(rnd), randomJobDetails(rnd)));
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ private static List<JobID> randomIds(Random rnd) {
+ final int num = rnd.nextInt(20);
+ ArrayList<JobID> ids = new ArrayList<>(num);
+
+ for (int i = 0; i < num; i++) {
+ ids.add(new JobID(rnd.nextLong(), rnd.nextLong()));
+ }
+
+ return ids;
+ }
+
+ private JobDetails[] randomJobDetails(Random rnd) {
+ final JobDetails[] details = new JobDetails[rnd.nextInt(10)];
+ for (int k = 0; k < details.length; k++) {
+ int[] numVerticesPerState = new int[ExecutionState.values().length];
+ int numTotal = 0;
+
+ for (int i = 0; i < numVerticesPerState.length; i++) {
+ int count = rnd.nextInt(55);
+ numVerticesPerState[i] = count;
+ numTotal += count;
+ }
+
+ long time = rnd.nextLong();
+ long endTime = rnd.nextBoolean() ? -1L : time + rnd.nextInt();
+ long lastModified = endTime == -1 ? time + rnd.nextInt() : endTime;
+
+ String name = new GenericMessageTester.StringInstantiator().instantiate(rnd);
+ JobID jid = new JobID();
+ JobStatus status = JobStatus.values()[rnd.nextInt(JobStatus.values().length)];
+
+ details[k] = new JobDetails(jid, name, time, endTime, status, lastModified, numVerticesPerState, numTotal);
+ }
+ return details;
+ }
+}
http://git-wip-us.apache.org/repos/asf/flink/blob/628e5f71/flink-runtime/src/test/scala/org/apache/flink/runtime/testingUtils/TestingMemoryArchivist.scala
----------------------------------------------------------------------
diff --git a/flink-runtime/src/test/scala/org/apache/flink/runtime/testingUtils/TestingMemoryArchivist.scala b/flink-runtime/src/test/scala/org/apache/flink/runtime/testingUtils/TestingMemoryArchivist.scala
index 2ccddfa..167afbf 100644
--- a/flink-runtime/src/test/scala/org/apache/flink/runtime/testingUtils/TestingMemoryArchivist.scala
+++ b/flink-runtime/src/test/scala/org/apache/flink/runtime/testingUtils/TestingMemoryArchivist.scala
@@ -18,7 +18,7 @@
package org.apache.flink.runtime.testingUtils
-import org.apache.flink.runtime.{FlinkActor}
+import org.apache.flink.runtime.FlinkActor
import org.apache.flink.runtime.jobmanager.MemoryArchivist
import org.apache.flink.runtime.testingUtils.TestingJobManagerMessages.{ExecutionGraphNotFound, ExecutionGraphFound, RequestExecutionGraph}
[44/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Added
status counts for a job
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Added status counts for a job
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/7138cd55
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/7138cd55
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/7138cd55
Branch: refs/heads/master
Commit: 7138cd55f6e4ad124245bbc3b5f6dc508be54ea2
Parents: 749c0df
Author: Piotr Godek <pi...@gmail.com>
Authored: Thu Aug 27 21:56:21 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:52 2015 +0200
----------------------------------------------------------------------
flink-runtime-web/web-dashboard/app/partials/jobs/job.jade | 5 +++--
flink-runtime-web/web-dashboard/web/partials/jobs/job.html | 4 ++--
2 files changed, 5 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/7138cd55/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
index 083e921..b264c72 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.jade
@@ -28,7 +28,7 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main(ng-if="job")
.navbar-info.first.last
.label-group
- bs-label(status="{{status}}" ng-repeat="(status, value) in job.tasks") {{value}}
+ bs-label(status="{{status}}" ng-repeat="(status, value) in job['status-counts']") {{value}}
.navbar-info.first.last.hidden-xs.hidden-sm
| {{ job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
@@ -47,7 +47,8 @@ nav.navbar.navbar-default.navbar-fixed-top.navbar-main-additional(ng-if="job")
li(ui-sref-active='active')
a(ui-sref=".statistics") Job Accumulators / Statistics
- li(ui-sref-active='active' ng-if="job['end-time'] > -1")
+ //- li(ui-sref-active='active' ng-if="job['end-time'] > -1")
+ li(ui-sref-active='active')
a(ui-sref=".timeline") Timeline
li(ui-sref-active='active')
http://git-wip-us.apache.org/repos/asf/flink/blob/7138cd55/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
index 3f09b53..ed10feb 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
@@ -25,7 +25,7 @@ limitations under the License.
<div class="navbar-info first last hidden-xs hidden-sm">{{ job.jid }}</div>
<div class="navbar-info first last">
<div class="label-group">
- <bs-label status="{{status}}" ng-repeat="(status, value) in job.tasks">{{value}}</bs-label>
+ <bs-label status="{{status}}" ng-repeat="(status, value) in job['status-counts']">{{value}}</bs-label>
</div>
</div>
<div class="navbar-info first last hidden-xs hidden-sm">{{ job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}<span ng-if="job['end-time'] > -1">
@@ -37,7 +37,7 @@ limitations under the License.
<ul class="nav nav-tabs">
<li ui-sref-active="active"><a ui-sref=".plan">Plan</a></li>
<li ui-sref-active="active"><a ui-sref=".statistics">Job Accumulators / Statistics</a></li>
- <li ui-sref-active="active" ng-if="job['end-time'] > -1"><a ui-sref=".timeline">Timeline</a></li>
+ <li ui-sref-active="active"><a ui-sref=".timeline">Timeline</a></li>
<li ui-sref-active="active"><a ui-sref=".exceptions">Exceptions</a></li>
<li ui-sref-active="active"><a ui-sref=".config">Configuration</a></li>
</ul>
[46/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Timeline for running tasks
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/6d873e60/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index 2f5646e..3808bcb 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -59,12 +59,34 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
}
}).state("single-job.plan.node", {
url: "/vertex/{nodeid}",
+ abstract: true,
views: {
node: {
templateUrl: "partials/jobs/job.plan.node.html",
controller: 'JobPlanNodeController'
}
}
+ }).state("single-job.plan.node.subtasks", {
+ url: "",
+ views: {
+ nodeTab: {
+ templateUrl: "partials/jobs/job.plan.node.subtasks.html"
+ }
+ }
+ }).state("single-job.plan.node.properties", {
+ url: "/properties",
+ views: {
+ nodeTab: {
+ templateUrl: "partials/jobs/job.plan.node.properties.html"
+ }
+ }
+ }).state("single-job.plan.node.accumulators", {
+ url: "/properties",
+ views: {
+ nodeTab: {
+ templateUrl: "partials/jobs/job.plan.node.accumulators.html"
+ }
+ }
}).state("single-job.timeline", {
url: "/timeline",
views: {
@@ -215,9 +237,12 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
}]).controller('JobPlanNodeController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
$scope.nodeid = $stateParams.nodeid;
$scope.stateList = JobsService.stateList();
- return JobsService.getNode($scope.nodeid).then(function(data) {
+ JobsService.getNode($scope.nodeid).then(function(data) {
return $scope.node = data;
});
+ return JobsService.getVertex($scope.nodeid).then(function(data) {
+ return $scope.vertex = data;
+ });
}]).controller('JobTimelineVertexController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
return JobsService.getVertex($stateParams.vertexId).then(function(data) {
return $scope.vertex = data;
@@ -317,19 +342,21 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
]
});
angular.forEach(data.vertices, function(vertex) {
- return testData.push({
- times: [
- {
- label: translateLabel(vertex.name),
- color: "#d9f1f7",
- borderColor: "#62cdea",
- starting_time: vertex['start-time'],
- ending_time: vertex['end-time'],
- link: vertex.id,
- type: 'regular'
- }
- ]
- });
+ if (vertex['start-time'] > -1) {
+ return testData.push({
+ times: [
+ {
+ label: translateLabel(vertex.name),
+ color: "#d9f1f7",
+ borderColor: "#62cdea",
+ starting_time: vertex['start-time'],
+ ending_time: vertex['end-time'],
+ link: vertex.id,
+ type: 'regular'
+ }
+ ]
+ });
+ }
});
chart = d3.timeline().stack().click(function(d, i, datum) {
if (d.link) {
@@ -702,25 +729,34 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
return 'default';
}
};
+ this.setEndTimes = function(list) {
+ return angular.forEach(list, function(job, jobKey) {
+ if (!(job['end-time'] > -1)) {
+ return job['end-time'] = job['start-time'] + job['duration'];
+ }
+ });
+ };
this.listJobs = function() {
var deferred;
deferred = $q.defer();
- $http.get(flinkConfig.jobServer + "/joboverview").success(function(data, status, headers, config) {
- angular.forEach(data, function(list, listKey) {
- switch (listKey) {
- case 'running':
- return jobs.running = list;
- case 'finished':
- return jobs.finished = list;
- case 'cancelled':
- return jobs.cancelled = list;
- case 'failed':
- return jobs.failed = list;
- }
- });
- deferred.resolve(jobs);
- return notifyObservers();
- });
+ $http.get(flinkConfig.jobServer + "/joboverview").success((function(_this) {
+ return function(data, status, headers, config) {
+ angular.forEach(data, function(list, listKey) {
+ switch (listKey) {
+ case 'running':
+ return jobs.running = _this.setEndTimes(list);
+ case 'finished':
+ return jobs.finished = _this.setEndTimes(list);
+ case 'cancelled':
+ return jobs.cancelled = _this.setEndTimes(list);
+ case 'failed':
+ return jobs.failed = _this.setEndTimes(list);
+ }
+ });
+ deferred.resolve(jobs);
+ return notifyObservers();
+ };
+ })(this));
return deferred.promise;
};
this.getJobs = function(type) {
@@ -732,16 +768,16 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.loadJob = function(jobid) {
currentJob = null;
deferreds.job = $q.defer();
- $http.get(flinkConfig.jobServer + "/jobs/" + jobid).success(function(data, status, headers, config) {
- return $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/vertices").success(function(vertices) {
- data = angular.extend(data, vertices);
+ $http.get(flinkConfig.jobServer + "/jobs/" + jobid).success((function(_this) {
+ return function(data, status, headers, config) {
+ _this.setEndTimes(data.vertices);
return $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/config").success(function(jobConfig) {
data = angular.extend(data, jobConfig);
currentJob = data;
return deferreds.job.resolve(data);
});
- });
- });
+ };
+ })(this));
return deferreds.job.promise;
};
this.loadPlan = function(jobid) {
@@ -863,4 +899,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsImNvbW1vbi9zZXJ2aWNlcy5jb2ZmZWUiLCJjb21tb24vc2VydmljZXMuanMiLCJtb2R1bGVzL2pvYnMvam9icy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuanMiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuZGlyLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLnN2Yy5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LmN0cmwuanMiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkEsUUFBUSxPQUFPLFlBQVksQ0FBQyxhQUFhLGtCQUl4QyxtQkFBSSxTQUFDLFlBQUQ7RUFDSCxXQUFXLGlCQUFpQjtFQ3JCNUIsT0RzQkEsV0FBVyxjQUFjLFdBQUE7SUFDdkIsV0FBVyxpQkFBaUIsQ0FBQyxXQUFXO0lDckJ4QyxPRHNCQSxXQUFXLGVBQWU7O0lBSTdCLE1BQU0sZUFBZTtFQUN
wQixXQUFXO0VBRVgsb0JBQW9CO0dBS3JCLCtEQUFJLFNBQUMsYUFBYSxhQUFhLGFBQWEsV0FBeEM7RUM1QkgsT0Q2QkEsWUFBWSxhQUFhLEtBQUssU0FBQyxRQUFEO0lBQzVCLFFBQVEsT0FBTyxhQUFhO0lBRTVCLFlBQVk7SUM3QlosT0QrQkEsVUFBVSxXQUFBO01DOUJSLE9EK0JBLFlBQVk7T0FDWixZQUFZOztJQUtqQixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sd0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sdUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhOzs7S0FFbEIsTUFBTSw4QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsUUFDRTtRQUFBLGFB
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
tJQUVkLE1BQU0sSUFBSSxZQUFZLFlBQVksYUFDakMsUUFBUSxTQUFDLE1BQU0sUUFBUSxTQUFTLFFBQXhCO01BQ1AsV0FBVztNQ3BCWCxPRHFCQSxTQUFTLFFBQVE7O0lDbkJuQixPRHFCQSxTQUFTOztFQ25CWCxPRHFCQTs7QUNuQkYiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGh
lIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJywgWyd1aS5yb3V0ZXInLCAnYW5ndWxhck1vbWVudCddKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKCRyb290U2NvcGUpIC0+XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZVxuICAkcm9vdFNjb3BlLnNob3dTaWRlYmFyID0gLT5cbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGVcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJDbGFzcyA9ICdmb3JjZS1zaG93J1xuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi52YWx1ZSAnZmxpbmtDb25maWcnLCB7XG4gIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MSdcbiMgIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9uZXctc2VydmVyJ1xuICBcInJlZnJlc2gtaW50ZXJ2YWxcIjogMTAwMDBcbn1cblxuIyAtLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4ucnVuIChKb2JzU2VydmljZSwgTWFpblNlcnZpY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIC0+XG4gIE1haW5TZXJ2aWNlLmxvYWRDb25maWcoKS50aGVuIChjb25maWcpIC0+XG4gICAgYW5ndWxhci5leHRlbmQgZmxpbmtDb25maWcsIGNvbmZpZ1xuXG4gICAgSm9ic1NlcnZpY2UubGlzdEpvYnMoKVxuXG4gICAgJGludGVydmFsIC0+XG4gICAgICBKb2JzU2VydmljZS5saXN0Sm9icygpXG4gICAgLCBmbGlua0NvbmZpZ1tcInJlZnJlc2gtaW50ZXJ2YWxcIl1cblxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb25maWcgKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIC0+XG4gICRzdGF0ZVByb3ZpZGVyLnN0YXRlIFwib3ZlcnZpZXdcIixcbiAgICB1cmw6IFwiL292ZXJ2aWV3XCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnT3ZlcnZpZXdDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInJ1bm5pbmctam9ic1wiLFxuICAgIHVybDogXCIvcnVubmluZy1qb2JzXCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy1qb2JzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnUnVubmluZ0pvYnNDb250cm9sb
GVyJ1xuICBcbiAgLnN0YXRlIFwiY29tcGxldGVkLWpvYnNcIixcbiAgICB1cmw6IFwiL2NvbXBsZXRlZC1qb2JzXCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvY29tcGxldGVkLWpvYnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iXCIsXG4gICAgdXJsOiBcIi9qb2JzL3tqb2JpZH1cIlxuICAgIGFic3RyYWN0OiB0cnVlXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuXCIsXG4gICAgdXJsOiBcIlwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm5vZGVcIixcbiAgICB1cmw6IFwiL3ZlcnRleC97bm9kZWlkfVwiXG4gICAgdmlld3M6XG4gICAgICBub2RlOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm
9kZUNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi50aW1lbGluZVwiLFxuICAgIHVybDogXCIvdGltZWxpbmVcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIixcbiAgICB1cmw6IFwiL3t2ZXJ0ZXhJZH1cIlxuICAgIHZpZXdzOlxuICAgICAgdmVydGV4OlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS52ZXJ0ZXguaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5zdGF0aXN0aWNzXCIsXG4gICAgdXJsOiBcIi9zdGF0aXN0aWNzXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnN0YXRpc3RpY3MuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5leGNlcHRpb25zXCIsXG4gICAgdXJsOiBcIi9leGNlcHRpb25zXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmV4Y2VwdGlvbnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JFeGNlcHRpb25zQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGU
tam9iLmNvbmZpZ1wiLFxuICAgIHVybDogXCIvY29uZmlnXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmNvbmZpZy5odG1sXCJcblxuICAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlIFwiL292ZXJ2aWV3XCJcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcsIFsndWkucm91dGVyJywgJ2FuZ3VsYXJNb21lbnQnXSkucnVuKGZ1bmN0aW9uKCRyb290U2NvcGUpIHtcbiAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9IGZhbHNlO1xuICByZXR1cm4gJHJvb3RTY29wZS5zaG93U2lkZWJhciA9IGZ1bmN0aW9uKCkge1xuICAgICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSAhJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZTtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5zaWRlYmFyQ2xhc3MgPSAnZm9yY2Utc2hvdyc7XG4gIH07XG59KS52YWx1ZSgnZmxpbmtDb25maWcnLCB7XG4gIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MScsXG4gIFwicmVmcmVzaC1pbnRlcnZhbFwiOiAxMDAwMFxufSkucnVuKGZ1bmN0aW9uKEpvYnNTZXJ2aWNlLCBNYWluU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkge1xuICByZXR1cm4gTWFpblNlcnZpY2UubG9hZENvbmZpZygpLnRoZW4oZnVuY3Rpb24oY29uZmlnKSB7XG4gICAgYW5ndWxhci5leHRlbmQoZmxpbmtDb25maWcsIGNvbmZpZyk7XG4gICAgSm9ic1NlcnZpY2Uu
bGlzdEpvYnMoKTtcbiAgICByZXR1cm4gJGludGVydmFsKGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCk7XG4gICAgfSwgZmxpbmtDb25maWdbXCJyZWZyZXNoLWludGVydmFsXCJdKTtcbiAgfSk7XG59KS5jb25maWcoZnVuY3Rpb24oJHN0YXRlUHJvdmlkZXIsICR1cmxSb3V0ZXJQcm92aWRlcikge1xuICAkc3RhdGVQcm92aWRlci5zdGF0ZShcIm92ZXJ2aWV3XCIsIHtcbiAgICB1cmw6IFwiL292ZXJ2aWV3XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvb3ZlcnZpZXcuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnT3ZlcnZpZXdDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJydW5uaW5nLWpvYnNcIiwge1xuICAgIHVybDogXCIvcnVubmluZy1qb2JzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9ydW5uaW5nLWpvYnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnUnVubmluZ0pvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJjb21wbGV0ZWQtam9ic1wiLCB7XG4gICAgdXJsOiBcIi9jb21wbGV0ZWQtam9ic1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvY29tcGxldGVkL
WpvYnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2JcIiwge1xuICAgIHVybDogXCIvam9icy97am9iaWR9XCIsXG4gICAgYWJzdHJhY3Q6IHRydWUsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnU2luZ2xlSm9iQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuXCIsIHtcbiAgICB1cmw6IFwiXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuLm5vZGVcIiwge1xuICAgIHVybDogXCIvdmVydGV4L3tub2RlaWR9XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG5vZGU6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi50aW1lbGluZVwiLC
B7XG4gICAgdXJsOiBcIi90aW1lbGluZVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7XG4gICAgdXJsOiBcIi97dmVydGV4SWR9XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIHZlcnRleDoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS52ZXJ0ZXguaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIiwge1xuICAgIHVybDogXCIvc3RhdGlzdGljc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnN0YXRpc3RpY3MuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IuZXhjZXB0aW9uc1wiLCB7XG4gICAgdXJsOiBcIi9leGNlcHRpb25zXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JFeGNlcHRpb25zQ29udHJ
vbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5jb25maWdcIiwge1xuICAgIHVybDogXCIvY29uZmlnXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuY29uZmlnLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIHJldHVybiAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlKFwiL292ZXJ2aWV3XCIpO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVz
cyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2JzTGFiZWwnLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHRyYW5zY2x1ZGU6IHRydWVcbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6IFwiQFwiXG5cbiAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknPjxuZy10cmFuc2NsdWRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCJcbiAgXG4gIGxpbms6IChzY29wZSwgZWxlbWVudCwgYXR0cnMpIC0+XG4gICAgc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IC0+XG4gICAgICAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2a
WNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnaW5kaWNhdG9yUHJpbWFyeScsIChKb2JzU2VydmljZSkgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6ICdAJ1xuXG4gIHRlbXBsYXRlOiBcIjxpIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJyAvPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2ZhIGZhLWNpcmNsZSBpbmRpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3RhYmxlUHJvcGVydHknLCAtPlxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOlxuICAgIHZhbHVlOiAnPSdcblxuICB0ZW1wbGF0ZTogXCI8dGQgdGl0bGU9XFxcInt7dmFsdWUgfHwgJ05vbmUnfX1cXFwiPnt7dmFsdWUgfHwgJ05vbmUnfX08L3RkPlwiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ2JzTGFiZWwnLCBmdW5jdGlvbihKb2JzU2VydmljZSkge1xuICByZXR1cm4ge1xuICAgIH
RyYW5zY2x1ZGU6IHRydWUsXG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6IFwiQFwiXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8c3BhbiB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKSc+PG5nLXRyYW5zY2x1ZGU+PC9uZy10cmFuc2NsdWRlPjwvc3Bhbj5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdpbmRpY2F0b3JQcmltYXJ5JywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICBnZXRMYWJlbENsYXNzOiBcIiZcIixcbiAgICAgIHN0YXR1czogJ0AnXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8aSB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICA
gICAgIHJldHVybiAnZmEgZmEtY2lyY2xlIGluZGljYXRvciBpbmRpY2F0b3ItJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0YWJsZVByb3BlcnR5JywgZnVuY3Rpb24oKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgdmFsdWU6ICc9J1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUg
TGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmZpbHRlciBcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCAoYW5ndWxhck1vbWVudENvbmZpZykgLT5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSAtPlxuICAgIHJldHVybiBcIlwiICBpZiB0eXBlb2YgdmFsdWUgaXMgXCJ1bmRlZmluZWRcIiBvciB2YWx1ZSBpcyBudWxsXG5cbiAgICBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7IHRyaW06IGZhbHNlIH0pXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0Z
WZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyXG5cbi5maWx0ZXIgXCJodW1hbml6ZVRhc2tOYW1lXCIsIC0+XG4gICh0ZXh0KSAtPlxuICAgICMgVE9ETzogZXh0ZW5kLi4uIGEgbG90XG4gICAgaWYgdGV4dCB0aGVuIHRleHQucmVwbGFjZShcIiZndDtcIiwgXCI+XCIpLnJlcGxhY2UoXCI8YnIvPlwiLFwiXCIpIGVsc2UgJydcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmZpbHRlcihcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCBmdW5jdGlvbihhbmd1bGFyTW9tZW50Q29uZmlnKSB7XG4gIHZhciBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXI7XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlciA9IGZ1bmN0aW9uKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJ1bmRlZmluZWRcIiB8fCB2YWx1ZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxuICAgIHJldHVybiBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7XG4gICAgICB0cmltOiBmYWxzZVxuICAgIH0pO1xuICB9O1xuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIuJHN0YXRlZnVsID0gYW5ndWxhck1vbWVudENvbmZpZy5zdGF0ZWZ1bEZpbHRlcnM7XG4gIHJldHVybiBhbUR1cmF0aW9uRm
9ybWF0RXh0ZW5kZWRGaWx0ZXI7XG59KS5maWx0ZXIoXCJodW1hbml6ZVRhc2tOYW1lXCIsIGZ1bmN0aW9uKCkge1xuICByZXR1cm4gZnVuY3Rpb24odGV4dCkge1xuICAgIGlmICh0ZXh0KSB7XG4gICAgICByZXR1cm4gdGV4dC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIikucmVwbGFjZShcIjxici8+XCIsIFwiXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gJyc7XG4gICAgfVxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmx
lIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLnNlcnZpY2UgJ01haW5TZXJ2aWNlJywgKCRodHRwLCBmbGlua0NvbmZpZywgJHEpIC0+XG4gIGpvYk9ic2VydmVycyA9IFtdXG5cbiAgQGxvYWRDb25maWcgPSAtPlxuICAgIGRlZmVycmVkID0gJHEuZGVmZXIoKVxuXG4gICAgJGh0dHAuZ2V0IGZsaW5rQ29uZmlnLmpvYlNlcnZlciArIFwiL2NvbmZpZ1wiXG4gICAgLnN1Y2Nlc3MgKGRhdGEsIHN0YXR1cywgaGVhZGVycywgY29uZmlnKSAtPlxuICAgICAgZGVmZXJyZWQucmVzb2x2ZShkYXRhKVxuXG4gICAgZGVmZXJyZWQucHJvbWlzZVxuXG5cbiAgQFxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuc2VydmljZSgnTWFpblNlcnZpY2UnLCBmdW5jdGlvbigkaHR0cCwgZmxpbmtDb25maWcsICRxKSB7XG4gIHZhciBqb2JPYnNlcnZlcnM7XG4gIGpvYk9ic2VydmVycyA9IFtdO1xu
ICB0aGlzLmxvYWRDb25maWcgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgZGVmZXJyZWQ7XG4gICAgZGVmZXJyZWQgPSAkcS5kZWZlcigpO1xuICAgICRodHRwLmdldChmbGlua0NvbmZpZy5qb2JTZXJ2ZXIgKyBcIi9jb25maWdcIikuc3VjY2VzcyhmdW5jdGlvbihkYXRhLCBzdGF0dXMsIGhlYWRlcnMsIGNvbmZpZykge1xuICAgICAgcmV0dXJuIGRlZmVycmVkLnJlc29sdmUoZGF0YSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIGRlZmVycmVkLnByb21pc2U7XG4gIH07XG4gIHJldHVybiB0aGlzO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL
2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmNvbnRyb2xsZXIgJ1J1bm5pbmdKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ3J1bm5pbmcnKVxuXG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuICAkc2NvcGUuJG9uICckZGVzdHJveScsIC0+XG4gICAgSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcblxuICAkc2NvcGUuam9iT2JzZXJ2ZXIoKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS
0tLS0tXG5cbi5jb250cm9sbGVyICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnU2luZ2xlSm9iQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHJvb3RTY29wZSkgLT5cbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkXG4gICRyb290U2NvcGUuam9iID0gbnVsbFxuXG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuIChkYXRhKSAtPlxuICAgICRyb290U2NvcGUuam9iID0gZGF0YVxuXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9
iUGxhbkNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmxvYWRQbGFuKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUucGxhbiA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbk5vZGVDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZFxuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KClcblxuICBKb2JzU2VydmljZS5nZXROb2RlKCRzY29wZS5ub2RlaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLm5vZGUgPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy52ZXJ0ZXhJZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUudmVydGV4ID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JFeGNlcHRpb25zQ29udHJv
bGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgSm9ic1NlcnZpY2UubG9hZEV4Y2VwdGlvbnMoKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS5leGNlcHRpb25zID0gZGF0YVxuXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5jb250cm9sbGVyKCdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJyk7XG4gIH07XG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJyk7XG4gIH07X
G4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdTaW5nbGVKb2JDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRyb290U2NvcGUpIHtcbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkO1xuICAkcm9vdFNjb3BlLmpvYiA9IG51bGw7XG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5qb2IgPSBkYXRhO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRyb290U2NvcGUuam9iID0gbnVsbDtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiBKb2JzU2VydmljZS5sb2FkUGxhbigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkc2NvcGUucGxhbiA9IG
RhdGE7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUGxhbk5vZGVDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLm5vZGVpZCA9ICRzdGF0ZVBhcmFtcy5ub2RlaWQ7XG4gICRzY29wZS5zdGF0ZUxpc3QgPSBKb2JzU2VydmljZS5zdGF0ZUxpc3QoKTtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5ub2RlID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy52ZXJ0ZXhJZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS52ZXJ0ZXggPSBkYXRhO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYkV4Y2VwdGlvbnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmxvYWRFeGNlcHRpb25zKCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5leGNlcHRpb25zID0gZGF0YTtcbiAgfSk7XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGh
lIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMg
YW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAndmVydGV4JywgKCRzdGF0ZSkgLT5cbiAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUgc2Vjb25kYXJ5JyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGRhdGE6IFwiPVwiXG5cbiAgbGluazogKHNjb3BlLCBlbGVtLCBhdHRycykgLT5cbiAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEuc3VidGFza3MsIChzdWJ0YXNrLCBpKSAtPlxuICAgICAgICB0ZXN0RGF0YS5wdXNoIHtcbiAgICAgICAgICBsYWJlbDogXCIje3N1YnRhc2suaG9zdH0gKCN7c3VidGFzay5zdWJ0YXNrfSlcIlxuICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiM2NjZcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1X
CJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiU0NIRURVTEVEXCJdXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJERVBMT1lJTkdcIl1cbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIkRlcGxveWluZ1wiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiNhYWFcIlxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiREVQTE9ZSU5HXCJdXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU5HXCJdXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJSdW5uaW5nXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2RkZFwiXG4gICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU5HXCJdXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJGSU5JU0hFRFwiXVxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIH
1cbiAgICAgICAgICBdXG4gICAgICAgIH1cblxuICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKClcbiAgICAgIC50aWNrRm9ybWF0KHtcbiAgICAgICAgZm9ybWF0OiBkMy50aW1lLmZvcm1hdChcIiVMXCIpXG4gICAgICAgICMgdGlja0ludGVydmFsOiAxXG4gICAgICAgIHRpY2tTaXplOiAxXG4gICAgICB9KVxuICAgICAgLnByZWZpeChcInNpbmdsZVwiKVxuICAgICAgLmxhYmVsRm9ybWF0KChsYWJlbCkgLT5cbiAgICAgICAgbGFiZWxcbiAgICAgIClcbiAgICAgIC5tYXJnaW4oeyBsZWZ0OiAxMDAsIHJpZ2h0OiAwLCB0b3A6IDAsIGJvdHRvbTogMCB9KVxuICAgICAgLml0ZW1IZWlnaHQoMzApXG4gICAgICAucmVsYXRpdmVUaW1lKClcblxuICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKVxuICAgICAgLmRhdHVtKHRlc3REYXRhKVxuICAgICAgLmNhbGwoY2hhcnQpXG5cbiAgICBhbmFseXplVGltZShzY29wZS5kYXRhKVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0aW1lbGluZScsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGpvYjogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdXG5cbiA
gICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpXG5cbiAgICB0cmFuc2xhdGVMYWJlbCA9IChsYWJlbCkgLT5cbiAgICAgIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgdGVzdERhdGEucHVzaCBcbiAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIlxuICAgICAgICAgIGNvbG9yOiBcIiNjY2NjY2NcIlxuICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEudGltZXN0YW1wc1tcIkNSRUFURURcIl1cbiAgICAgICAgICBlbmRpbmdfdGltZTogZGF0YS50aW1lc3RhbXBzW1wiQ1JFQVRFRFwiXSArIDFcbiAgICAgICAgICB0eXBlOiAnc2NoZWR1bGVkJ1xuICAgICAgICBdXG5cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEudmVydGljZXMsICh2ZXJ0ZXgpIC0+XG4gICAgICAgIHRlc3REYXRhLnB1c2ggXG4gICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgIGxhYmVsOiB0cmFuc2xhdGVMYWJlbCh2ZXJ0ZXgubmFtZSlcbiAgICAgICAgICAgIGNvbG9yOiBcIiNkOWYxZjdcIlxuICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzYyY2RlYVwiXG4gICAgICAgICAgICBzdGFydGluZ190aW1lOiB2ZXJ0ZXhb
J3N0YXJ0LXRpbWUnXVxuICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZlcnRleFsnZW5kLXRpbWUnXVxuICAgICAgICAgICAgbGluazogdmVydGV4LmlkXG4gICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICBdXG5cbiAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLmNsaWNrKChkLCBpLCBkYXR1bSkgLT5cbiAgICAgICAgaWYgZC5saW5rXG4gICAgICAgICAgJHN0YXRlLmdvIFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIiwgeyBqb2JpZDogZGF0YS5qaWQsIHZlcnRleElkOiBkLmxpbmsgfVxuXG4gICAgICApXG4gICAgICAudGlja0Zvcm1hdCh7XG4gICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKVxuICAgICAgICAjIHRpY2tUaW1lOiBkMy50aW1lLnNlY29uZFxuICAgICAgICAjIHRpY2tJbnRlcnZhbDogMC41XG4gICAgICAgIHRpY2tTaXplOiAxXG4gICAgICB9KVxuICAgICAgLnByZWZpeChcIm1haW5cIilcbiAgICAgIC5tYXJnaW4oeyBsZWZ0OiAwLCByaWdodDogMCwgdG9wOiAwLCBib3R0b206IDAgfSlcbiAgICAgIC5pdGVtSGVpZ2h0KDMwKVxuICAgICAgLnNob3dCb3JkZXJMaW5lKClcbiAgICAgIC5zaG93SG91clRpbWVsaW5lKClcblxuICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKVxuICAgICAgLmRhdHVtKHRlc3REYXRhKVxuICAgICAgLmNhbGwoY2hhcnQpXG5cbiAgICBzY29wZS4kd2F0Y2ggYXR0cnMuam9iLCAoZGF0YSkgLT5cbiAgI
CAgIGFuYWx5emVUaW1lKGRhdGEpIGlmIGRhdGFcblxuICAgIHJldHVyblxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnam9iUGxhbicsICgkdGltZW91dCkgLT5cbiAgdGVtcGxhdGU6IFwiXG4gICAgPHN2ZyBjbGFzcz0nZ3JhcGgnIHdpZHRoPSc1MDAnIGhlaWdodD0nNDAwJz48ZyAvPjwvc3ZnPlxuICAgIDxzdmcgY2xhc3M9J3RtcCcgd2lkdGg9JzEnIGhlaWdodD0nMSc+PGcgLz48L3N2Zz5cbiAgICA8ZGl2IGNsYXNzPSdidG4tZ3JvdXAgem9vbS1idXR0b25zJz5cbiAgICAgIDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1pbicgbmctY2xpY2s9J3pvb21JbigpJz48aSBjbGFzcz0nZmEgZmEtcGx1cycgLz48L2E+XG4gICAgICA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20tb3V0JyBuZy1jbGljaz0nem9vbU91dCgpJz48aSBjbGFzcz0nZmEgZmEtbWludXMnIC8+PC9hPlxuICAgIDwvZGl2PlwiXG5cbiAgc2NvcGU6XG4gICAgcGxhbjogJz0nXG5cbiAgbGluazogKHNjb3BlLCBlbGVtLCBhdHRycykgLT5cbiAgICBtYWluWm9vbSA9IGQzLmJlaGF2aW9yLnpvb20oKVxuICAgIHN1YmdyYXBocyA9IFtdXG4gICAgam9iaWQgPSBhdHRycy5qb2JpZFxuXG4gICAgbWFpblN2Z0VsZW1lbnQgPSBlbGVtLmNoaWxkcmVuKClbMF1cbiAgICBtYWluRyA9IGVsZW0uY2hpbGRyZW4oKS5jaGlsZHJlbigpWzBdXG4gICAgbWFpblRtcE
VsZW1lbnQgPSBlbGVtLmNoaWxkcmVuKClbMV1cblxuICAgIGQzbWFpblN2ZyA9IGQzLnNlbGVjdChtYWluU3ZnRWxlbWVudClcbiAgICBkM21haW5TdmdHID0gZDMuc2VsZWN0KG1haW5HKVxuICAgIGQzdG1wU3ZnID0gZDMuc2VsZWN0KG1haW5UbXBFbGVtZW50KVxuXG4gICAgIyBhbmd1bGFyLmVsZW1lbnQobWFpbkcpLmVtcHR5KClcblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoZWxlbS5jaGlsZHJlbigpWzBdKS53aWR0aChjb250YWluZXJXKVxuXG4gICAgc2NvcGUuem9vbUluID0gLT5cbiAgICAgIGlmIG1haW5ab29tLnNjYWxlKCkgPCAyLjk5XG4gICAgICAgIFxuICAgICAgICAjIENhbGN1bGF0ZSBhbmQgc3RvcmUgbmV3IHZhbHVlcyBpbiB6b29tIG9iamVjdFxuICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKVxuICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICBtYWluWm9vbS5zY2FsZSBtYWluWm9vbS5zY2FsZSgpICsgMC4xXG4gICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZSBbIHYxLCB2MiBdXG4gICAgICAgIFxuICAgICAgICAjIFRyYW5zZm9ybSBzdmdcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFw
idHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiXG5cbiAgICBzY29wZS56b29tT3V0ID0gLT5cbiAgICAgIGlmIG1haW5ab29tLnNjYWxlKCkgPiAwLjMxXG4gICAgICAgIFxuICAgICAgICAjIENhbGN1bGF0ZSBhbmQgc3RvcmUgbmV3IHZhbHVlcyBpbiBtYWluWm9vbSBvYmplY3RcbiAgICAgICAgbWFpblpvb20uc2NhbGUgbWFpblpvb20uc2NhbGUoKSAtIDAuMVxuICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKVxuICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUgWyB2MSwgdjIgXVxuICAgICAgICBcbiAgICAgICAgIyBUcmFuc2Zvcm0gc3ZnXG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIlxuXG4gICAgI2NyZWF0ZSBhIGxhYmVsIG9mIGFuIGVkZ2VcbiAgICBjcmVhdGVMYWJlbEVkZ2UgPSAoZWwpIC0+XG4gICAgICBsYWJlbFZhbHVlID0gXCJcIlxuICAgICAgaWYg
ZWwuc2hpcF9zdHJhdGVneT8gb3IgZWwubG9jYWxfc3RyYXRlZ3k/XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8ZGl2IGNsYXNzPSdlZGdlLWxhYmVsJz5cIlxuICAgICAgICBsYWJlbFZhbHVlICs9IGVsLnNoaXBfc3RyYXRlZ3kgIGlmIGVsLnNoaXBfc3RyYXRlZ3k/XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCIgKFwiICsgZWwudGVtcF9tb2RlICsgXCIpXCIgIHVubGVzcyBlbC50ZW1wX21vZGUgaXMgYHVuZGVmaW5lZGBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiw8YnI+XCIgKyBlbC5sb2NhbF9zdHJhdGVneSAgdW5sZXNzIGVsLmxvY2FsX3N0cmF0ZWd5IGlzIGB1bmRlZmluZWRgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2Rpdj5cIlxuICAgICAgbGFiZWxWYWx1ZVxuXG5cbiAgICAjIHRydWUsIGlmIHRoZSBub2RlIGlzIGEgc3BlY2lhbCBub2RlIGZyb20gYW4gaXRlcmF0aW9uXG4gICAgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZSA9IChpbmZvKSAtPlxuICAgICAgKGluZm8gaXMgXCJwYXJ0aWFsU29sdXRpb25cIiBvciBpbmZvIGlzIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiIG9yIGluZm8gaXMgXCJ3b3Jrc2V0XCIgb3IgaW5mbyBpcyBcIm5leHRXb3Jrc2V0XCIgb3IgaW5mbyBpcyBcInNvbHV0aW9uU2V0XCIgb3IgaW5mbyBpcyBcInNvbHV0aW9uRGVsdGFcIilcblxuICAgIGdldE5vZGVUeXBlID0gKGVsLCBpbmZvKSAtPlxuICAgICAgaWYgaW5mbyBpcyBcIm1pcnJvclwiXG4gICAgICAgICdub
2RlLW1pcnJvcidcblxuICAgICAgZWxzZSBpZiBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pXG4gICAgICAgICdub2RlLWl0ZXJhdGlvbidcblxuICAgICAgZWxzZVxuICAgICAgICAgICdub2RlLW5vcm1hbCdcbiAgICAgIFxuICAgICMgY3JlYXRlcyB0aGUgbGFiZWwgb2YgYSBub2RlLCBpbiBpbmZvIGlzIHN0b3JlZCwgd2hldGhlciBpdCBpcyBhIHNwZWNpYWwgbm9kZSAobGlrZSBhIG1pcnJvciBpbiBhbiBpdGVyYXRpb24pXG4gICAgY3JlYXRlTGFiZWxOb2RlID0gKGVsLCBpbmZvLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgbGFiZWxWYWx1ZSA9IFwiPGEgaHJlZj0nIy9qb2JzL1wiICsgam9iaWQgKyBcIi92ZXJ0ZXgvXCIgKyBlbC5pZCArIFwiJyBjbGFzcz0nbm9kZS1sYWJlbCBcIiArIGdldE5vZGVUeXBlKGVsLCBpbmZvKSArIFwiJz5cIlxuXG4gICAgICAjIE5vZGVuYW1lXG4gICAgICBpZiBpbmZvIGlzIFwibWlycm9yXCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5NaXJyb3Igb2YgXCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIlxuICAgICAgZWxzZVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPlwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCJcbiAgICAgIGlmIGVsLmRlc2NyaXB0aW9uIGlzIFwiXCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIlwiXG4gICAgICBlbHNlXG4gICAgICAgIHN0ZXBOYW1lID0gZWwuZG
VzY3JpcHRpb25cbiAgICAgICAgXG4gICAgICAgICMgY2xlYW4gc3RlcE5hbWVcbiAgICAgICAgc3RlcE5hbWUgPSBzaG9ydGVuU3RyaW5nKHN0ZXBOYW1lKVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg0IGNsYXNzPSdzdGVwLW5hbWUnPlwiICsgc3RlcE5hbWUgKyBcIjwvaDQ+XCJcbiAgICAgIFxuICAgICAgIyBJZiB0aGlzIG5vZGUgaXMgYW4gXCJpdGVyYXRpb25cIiB3ZSBuZWVkIGEgZGlmZmVyZW50IHBhbmVsLWJvZHlcbiAgICAgIGlmIGVsLnN0ZXBfZnVuY3Rpb24/XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uKGVsLmlkLCBtYXhXLCBtYXhIKVxuICAgICAgZWxzZVxuICAgICAgICBcbiAgICAgICAgIyBPdGhlcndpc2UgYWRkIGluZm9zICAgIFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlwiICsgaW5mbyArIFwiIE5vZGU8L2g1PlwiICBpZiBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+UGFyYWxsZWxpc206IFwiICsgZWwucGFyYWxsZWxpc20gKyBcIjwvaDU+XCIgIHVubGVzcyBlbC5wYXJhbGxlbGlzbSBpcyBcIlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+T3BlcmF0aW9uOiBcIiArIHNob3J0ZW5TdHJpbmcoZWwub3BlcmF0b3Jfc3RyYXRlZ3kpICsgXCI8L2g1PlwiICB1bmxlc3MgZWwub3BlcmF0b3IgaXMgYHVuZGVmaW5lZGBcbiAgICAgIFxuICAgICAgbGFiZWxWYWx1ZSA
rPSBcIjwvYT5cIlxuICAgICAgbGFiZWxWYWx1ZVxuXG4gICAgIyBFeHRlbmRzIHRoZSBsYWJlbCBvZiBhIG5vZGUgd2l0aCBhbiBhZGRpdGlvbmFsIHN2ZyBFbGVtZW50IHRvIHByZXNlbnQgdGhlIGl0ZXJhdGlvbi5cbiAgICBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24gPSAoaWQsIG1heFcsIG1heEgpIC0+XG4gICAgICBzdmdJRCA9IFwic3ZnLVwiICsgaWRcblxuICAgICAgbGFiZWxWYWx1ZSA9IFwiPHN2ZyBjbGFzcz0nXCIgKyBzdmdJRCArIFwiJyB3aWR0aD1cIiArIG1heFcgKyBcIiBoZWlnaHQ9XCIgKyBtYXhIICsgXCI+PGcgLz48L3N2Zz5cIlxuICAgICAgbGFiZWxWYWx1ZVxuXG4gICAgIyBTcGxpdCBhIHN0cmluZyBpbnRvIG11bHRpcGxlIGxpbmVzIHNvIHRoYXQgZWFjaCBsaW5lIGhhcyBsZXNzIHRoYW4gMzAgbGV0dGVycy5cbiAgICBzaG9ydGVuU3RyaW5nID0gKHMpIC0+XG4gICAgICAjIG1ha2Ugc3VyZSB0aGF0IG5hbWUgZG9lcyBub3QgY29udGFpbiBhIDwgKGJlY2F1c2Ugb2YgaHRtbClcbiAgICAgIGlmIHMuY2hhckF0KDApIGlzIFwiPFwiXG4gICAgICAgIHMgPSBzLnJlcGxhY2UoXCI8XCIsIFwiJmx0O1wiKVxuICAgICAgICBzID0gcy5yZXBsYWNlKFwiPlwiLCBcIiZndDtcIilcbiAgICAgIHNiciA9IFwiXCJcbiAgICAgIHdoaWxlIHMubGVuZ3RoID4gMzBcbiAgICAgICAgc2JyID0gc2JyICsgcy5zdWJzdHJpbmcoMCwgMzApICsgXCI8YnI+XCJcbiAgICAgICAgcyA9IHMuc3Vic3Ry
aW5nKDMwLCBzLmxlbmd0aClcbiAgICAgIHNiciA9IHNiciArIHNcbiAgICAgIHNiclxuXG4gICAgY3JlYXRlTm9kZSA9IChnLCBkYXRhLCBlbCwgaXNQYXJlbnQgPSBmYWxzZSwgbWF4VywgbWF4SCkgLT5cbiAgICAgICMgY3JlYXRlIG5vZGUsIHNlbmQgYWRkaXRpb25hbCBpbmZvcm1hdGlvbnMgYWJvdXQgdGhlIG5vZGUgaWYgaXQgaXMgYSBzcGVjaWFsIG9uZVxuICAgICAgaWYgZWwuaWQgaXMgZGF0YS5wYXJ0aWFsX3NvbHV0aW9uXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEubmV4dF9wYXJ0aWFsX3NvbHV0aW9uXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS53b3Jrc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsY
WJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIndvcmtzZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJ3b3Jrc2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5uZXh0X3dvcmtzZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFdvcmtzZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0V29ya3NldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEuc29sdXRpb25fc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uU2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25TZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLnNvbHV0aW9uX2RlbHRhXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uRGVsdGFcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOi
BnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIpXG5cbiAgICAgIGVsc2VcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwiXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwiXCIpXG5cbiAgICBjcmVhdGVFZGdlID0gKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKSAtPlxuICAgICAgdW5sZXNzIGV4aXN0aW5nTm9kZXMuaW5kZXhPZihwcmVkLmlkKSBpcyAtMVxuICAgICAgICBnLnNldEVkZ2UgcHJlZC5pZCwgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShwcmVkKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgYXJyb3doZWFkOiAnbm9ybWFsJ1xuXG4gICAgICBlbHNlXG4gICAgICAgIG1pc3NpbmdOb2RlID0gc2VhcmNoRm9yTm9kZShkYXRhLCBwcmVkLmlkKVxuICAgICAgICB1bmxlc3MgIW1pc3NpbmdOb2RlIG9yIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCBpcyB0cnVlXG4gICAgICAgICAgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkID0gdHJ1ZVxuICAgICAgICAgIGcuc2V0Tm9kZSBtaXNzaW5nTm9kZS5pZCxcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUobWlzc2luZ05vZGUsIFwibWlycm9yXCIpXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1x
uICAgICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKG1pc3NpbmdOb2RlLCAnbWlycm9yJylcblxuICAgICAgICAgIGcuc2V0RWRnZSBtaXNzaW5nTm9kZS5pZCwgZWwuaWQsXG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKG1pc3NpbmdOb2RlKVxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcblxuICAgIGxvYWRKc29uVG9EYWdyZSA9IChnLCBkYXRhKSAtPlxuICAgICAgZXhpc3RpbmdOb2RlcyA9IFtdXG5cbiAgICAgIGlmIGRhdGEubm9kZXM/XG4gICAgICAgICMgVGhpcyBpcyB0aGUgbm9ybWFsIGpzb24gZGF0YVxuICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLm5vZGVzXG5cbiAgICAgIGVsc2VcbiAgICAgICAgIyBUaGlzIGlzIGFuIGl0ZXJhdGlvbiwgd2Ugbm93IHN0b3JlIHNwZWNpYWwgaXRlcmF0aW9uIG5vZGVzIGlmIHBvc3NpYmxlXG4gICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEuc3RlcF9mdW5jdGlvblxuICAgICAgICBpc1BhcmVudCA9IHRydWVcblxuICAgICAgZm9yIGVsIGluIHRvSXRlcmF0ZVxuICAgICAgICBtYXhXID0gMFxuICAgICAgICBtYXhIID0gMFxuXG4gICAgICAgIGlmIGVsLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgICBzZyA9IG5ldyBkYWdyZUQzLmdyYXBobGliLkdyYXBoKHsgbXVsdGlncmFwaDogdHJ1ZSwgY29tcG91bmQ6IHRydWUgfSkuc2V0R3JhcGgoe1xuICAgICAgICAgICAgbm9kZXNlcDogMjBcbiAgICAgICAgICAgIGVkZ2VzZXA6IDBcbiAgICAgICAg
ICAgIHJhbmtzZXA6IDIwXG4gICAgICAgICAgICByYW5rZGlyOiBcIkxSXCJcbiAgICAgICAgICAgIG1hcmdpbng6IDEwXG4gICAgICAgICAgICBtYXJnaW55OiAxMFxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgIHN1YmdyYXBoc1tlbC5pZF0gPSBzZ1xuXG4gICAgICAgICAgbG9hZEpzb25Ub0RhZ3JlKHNnLCBlbClcblxuICAgICAgICAgIHIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKVxuICAgICAgICAgIGQzdG1wU3ZnLnNlbGVjdCgnZycpLmNhbGwociwgc2cpXG4gICAgICAgICAgbWF4VyA9IHNnLmdyYXBoKCkud2lkdGhcbiAgICAgICAgICBtYXhIID0gc2cuZ3JhcGgoKS5oZWlnaHRcblxuICAgICAgICAgIGFuZ3VsYXIuZWxlbWVudChtYWluVG1wRWxlbWVudCkuZW1wdHkoKVxuXG4gICAgICAgIGNyZWF0ZU5vZGUoZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXhXLCBtYXhIKVxuXG4gICAgICAgIGV4aXN0aW5nTm9kZXMucHVzaCBlbC5pZFxuICAgICAgICBcbiAgICAgICAgIyBjcmVhdGUgZWRnZXMgZnJvbSBpbnB1dHMgdG8gY3VycmVudCBub2RlXG4gICAgICAgIGlmIGVsLmlucHV0cz9cbiAgICAgICAgICBmb3IgcHJlZCBpbiBlbC5pbnB1dHNcbiAgICAgICAgICAgIGNyZWF0ZUVkZ2UoZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpXG5cbiAgICAgIGdcblxuICAgICMgc2VhcmNoZXMgaW4gdGhlIGdsb2JhbCBKU09ORGF0YSBmb3IgdGhlIG5vZGUgd2l0aCB0aGUgZ2l2ZW4gaWRcbiAgICBzZ
WFyY2hGb3JOb2RlID0gKGRhdGEsIG5vZGVJRCkgLT5cbiAgICAgIGZvciBpIG9mIGRhdGEubm9kZXNcbiAgICAgICAgZWwgPSBkYXRhLm5vZGVzW2ldXG4gICAgICAgIHJldHVybiBlbCAgaWYgZWwuaWQgaXMgbm9kZUlEXG4gICAgICAgIFxuICAgICAgICAjIGxvb2sgZm9yIG5vZGVzIHRoYXQgYXJlIGluIGl0ZXJhdGlvbnNcbiAgICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvbj9cbiAgICAgICAgICBmb3IgaiBvZiBlbC5zdGVwX2Z1bmN0aW9uXG4gICAgICAgICAgICByZXR1cm4gZWwuc3RlcF9mdW5jdGlvbltqXSAgaWYgZWwuc3RlcF9mdW5jdGlvbltqXS5pZCBpcyBub2RlSURcblxuICAgIGRyYXdHcmFwaCA9IChkYXRhKSAtPlxuICAgICAgZyA9IG5ldyBkYWdyZUQzLmdyYXBobGliLkdyYXBoKHsgbXVsdGlncmFwaDogdHJ1ZSwgY29tcG91bmQ6IHRydWUgfSkuc2V0R3JhcGgoe1xuICAgICAgICBub2Rlc2VwOiA3MFxuICAgICAgICBlZGdlc2VwOiAwXG4gICAgICAgIHJhbmtzZXA6IDUwXG4gICAgICAgIHJhbmtkaXI6IFwiTFJcIlxuICAgICAgICBtYXJnaW54OiA0MFxuICAgICAgICBtYXJnaW55OiA0MFxuICAgICAgICB9KVxuXG4gICAgICBsb2FkSnNvblRvRGFncmUoZywgZGF0YSlcblxuICAgICAgcmVuZGVyZXIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKVxuICAgICAgZDNtYWluU3ZnRy5jYWxsKHJlbmRlcmVyLCBnKVxuXG4gICAgICBmb3IgaSwgc2cgb2Ygc3ViZ3JhcGhzXG4gICAgICAgIGQzbWFpblN2Zy
5zZWxlY3QoJ3N2Zy5zdmctJyArIGkgKyAnIGcnKS5jYWxsKHJlbmRlcmVyLCBzZylcblxuICAgICAgbmV3U2NhbGUgPSAwLjVcblxuICAgICAgeENlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkud2lkdGgoKSAtIGcuZ3JhcGgoKS53aWR0aCAqIG5ld1NjYWxlKSAvIDIpXG4gICAgICB5Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS5oZWlnaHQoKSAtIGcuZ3JhcGgoKS5oZWlnaHQgKiBuZXdTY2FsZSkgLyAyKVxuXG4gICAgICBtYWluWm9vbS5zY2FsZShuZXdTY2FsZSkudHJhbnNsYXRlKFt4Q2VudGVyT2Zmc2V0LCB5Q2VudGVyT2Zmc2V0XSlcblxuICAgICAgZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgeENlbnRlck9mZnNldCArIFwiLCBcIiArIHlDZW50ZXJPZmZzZXQgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpXG5cbiAgICAgIG1haW5ab29tLm9uKFwiem9vbVwiLCAtPlxuICAgICAgICBldiA9IGQzLmV2ZW50XG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIGV2LnRyYW5zbGF0ZSArIFwiKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIpXCJcbiAgICAgIClcbiAgICAgIG1haW5ab29tKGQzbWFpblN2ZylcblxuICAgIHNjb3BlLiR3YXRjaCBhdHRycy5wbGFuLCAobmV3UGx
hbikgLT5cbiAgICAgIGRyYXdHcmFwaChuZXdQbGFuKSBpZiBuZXdQbGFuXG5cbiAgICByZXR1cm5cbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpLmRpcmVjdGl2ZSgndmVydGV4JywgZnVuY3Rpb24oJHN0YXRlKSB7XG4gIHJldHVybiB7XG4gICAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUgc2Vjb25kYXJ5JyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIixcbiAgICBzY29wZToge1xuICAgICAgZGF0YTogXCI9XCJcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGFuYWx5emVUaW1lLCBjb250YWluZXJXLCBzdmdFbDtcbiAgICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKTtcbiAgICAgIGFuYWx5emVUaW1lID0gZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICB2YXIgY2hhcnQsIHN2ZywgdGVzdERhdGE7XG4gICAgICAgIHRlc3REYXRhID0gW107XG4gICAgICAgIGFuZ3VsYXIuZm9yRWFjaChkYXRhLnN1YnRhc2tzLCBmdW5jdGlvbihzdWJ0YXNrLCBpKSB7XG4gICAgICAgICAgcmV0dXJuIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgICAgbGFiZWw6IHN1YnRhc2suaG9zdCArIFwiIChcIiArIHN1YnRhc2suc3VidGFzayArIFwiKVwiLFxuICAgICAgICAgICAg
dGltZXM6IFtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiM2NjZcIixcbiAgICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiU0NIRURVTEVEXCJdLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJERVBMT1lJTkdcIl0sXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH0sIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogXCJEZXBsb3lpbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjYWFhXCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkRFUExPWUlOR1wiXSxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiUlVOTklOR1wiXSxcbiAgICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgfSwge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlJ1bm5pbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiLFxuICAgICAgICAgICAgICAgI
HN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlJVTk5JTkdcIl0sXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkZJTklTSEVEXCJdLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICBdXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS50aWNrRm9ybWF0KHtcbiAgICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIiksXG4gICAgICAgICAgdGlja1NpemU6IDFcbiAgICAgICAgfSkucHJlZml4KFwic2luZ2xlXCIpLmxhYmVsRm9ybWF0KGZ1bmN0aW9uKGxhYmVsKSB7XG4gICAgICAgICAgcmV0dXJuIGxhYmVsO1xuICAgICAgICB9KS5tYXJnaW4oe1xuICAgICAgICAgIGxlZnQ6IDEwMCxcbiAgICAgICAgICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMzApLnJlbGF0aXZlVGltZSgpO1xuICAgICAgICByZXR1cm4gc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCk7XG4gICAgICB9O1xuICAgICAgYW5hbHl6ZVRpbWUoc2NvcGUuZGF0YSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0aW1lbGluZScsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYX
RlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIixcbiAgICBzY29wZToge1xuICAgICAgam9iOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5lclcsIHN2Z0VsLCB0cmFuc2xhdGVMYWJlbDtcbiAgICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKTtcbiAgICAgIHRyYW5zbGF0ZUxhYmVsID0gZnVuY3Rpb24obGFiZWwpIHtcbiAgICAgICAgcmV0dXJuIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKTtcbiAgICAgIH07XG4gICAgICBhbmFseXplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGNoYXJ0LCBzdmcsIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIixcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2NjY2NjY1wiLFxuICAgICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCIsXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEudGltZXN0YW1wc1tcIkNSRUFURUR
cIl0sXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiBkYXRhLnRpbWVzdGFtcHNbXCJDUkVBVEVEXCJdICsgMSxcbiAgICAgICAgICAgICAgdHlwZTogJ3NjaGVkdWxlZCdcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBdXG4gICAgICAgIH0pO1xuICAgICAgICBhbmd1bGFyLmZvckVhY2goZGF0YS52ZXJ0aWNlcywgZnVuY3Rpb24odmVydGV4KSB7XG4gICAgICAgICAgcmV0dXJuIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGxhYmVsOiB0cmFuc2xhdGVMYWJlbCh2ZXJ0ZXgubmFtZSksXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiI2Q5ZjFmN1wiLFxuICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM2MmNkZWFcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2ZXJ0ZXhbJ3N0YXJ0LXRpbWUnXSxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdmVydGV4WydlbmQtdGltZSddLFxuICAgICAgICAgICAgICAgIGxpbms6IHZlcnRleC5pZCxcbiAgICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgXVxuICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKCkuY2xpY2soZnVuY3Rpb24oZCwgaSwgZGF0dW0pIHtcbiAgICAgICAgICBpZiAoZC5saW5rKSB7XG4gICAgICAgICAgICByZXR1
cm4gJHN0YXRlLmdvKFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIiwge1xuICAgICAgICAgICAgICBqb2JpZDogZGF0YS5qaWQsXG4gICAgICAgICAgICAgIHZlcnRleElkOiBkLmxpbmtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSkudGlja0Zvcm1hdCh7XG4gICAgICAgICAgZm9ybWF0OiBkMy50aW1lLmZvcm1hdChcIiVMXCIpLFxuICAgICAgICAgIHRpY2tTaXplOiAxXG4gICAgICAgIH0pLnByZWZpeChcIm1haW5cIikubWFyZ2luKHtcbiAgICAgICAgICBsZWZ0OiAwLFxuICAgICAgICAgIHJpZ2h0OiAwLFxuICAgICAgICAgIHRvcDogMCxcbiAgICAgICAgICBib3R0b206IDBcbiAgICAgICAgfSkuaXRlbUhlaWdodCgzMCkuc2hvd0JvcmRlckxpbmUoKS5zaG93SG91clRpbWVsaW5lKCk7XG4gICAgICAgIHJldHVybiBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpLmRhdHVtKHRlc3REYXRhKS5jYWxsKGNoYXJ0KTtcbiAgICAgIH07XG4gICAgICBzY29wZS4kd2F0Y2goYXR0cnMuam9iLCBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgcmV0dXJuIGFuYWx5emVUaW1lKGRhdGEpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH07XG59KS5kaXJlY3RpdmUoJ2pvYlBsYW4nLCBmdW5jdGlvbigkdGltZW91dCkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J2dyYXBoJyB3aWR0aD0nNTAwJyBoZWlnaHQ9J
zQwMCc+PGcgLz48L3N2Zz4gPHN2ZyBjbGFzcz0ndG1wJyB3aWR0aD0nMScgaGVpZ2h0PScxJz48ZyAvPjwvc3ZnPiA8ZGl2IGNsYXNzPSdidG4tZ3JvdXAgem9vbS1idXR0b25zJz4gPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLWluJyBuZy1jbGljaz0nem9vbUluKCknPjxpIGNsYXNzPSdmYSBmYS1wbHVzJyAvPjwvYT4gPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLW91dCcgbmctY2xpY2s9J3pvb21PdXQoKSc+PGkgY2xhc3M9J2ZhIGZhLW1pbnVzJyAvPjwvYT4gPC9kaXY+XCIsXG4gICAgc2NvcGU6IHtcbiAgICAgIHBsYW46ICc9J1xuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgY29udGFpbmVyVywgY3JlYXRlRWRnZSwgY3JlYXRlTGFiZWxFZGdlLCBjcmVhdGVMYWJlbE5vZGUsIGNyZWF0ZU5vZGUsIGQzbWFpblN2ZywgZDNtYWluU3ZnRywgZDN0bXBTdmcsIGRyYXdHcmFwaCwgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uLCBnZXROb2RlVHlwZSwgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZSwgam9iaWQsIGxvYWRKc29uVG9EYWdyZSwgbWFpbkcsIG1haW5TdmdFbGVtZW50LCBtYWluVG1wRWxlbWVudCwgbWFpblpvb20sIHNlYXJjaEZvck5vZGUsIHNob3J0ZW5TdHJpbmcsIHN1YmdyYXBocztcbiAgICAgIG1haW5ab29tID0gZDMuYmVoYXZpb3Iuem9vbSgpO1xuICAgICAgc3ViZ3JhcGhzID0gW107XG4gICAgICBqb2JpZCA9IG
F0dHJzLmpvYmlkO1xuICAgICAgbWFpblN2Z0VsZW1lbnQgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBtYWluRyA9IGVsZW0uY2hpbGRyZW4oKS5jaGlsZHJlbigpWzBdO1xuICAgICAgbWFpblRtcEVsZW1lbnQgPSBlbGVtLmNoaWxkcmVuKClbMV07XG4gICAgICBkM21haW5TdmcgPSBkMy5zZWxlY3QobWFpblN2Z0VsZW1lbnQpO1xuICAgICAgZDNtYWluU3ZnRyA9IGQzLnNlbGVjdChtYWluRyk7XG4gICAgICBkM3RtcFN2ZyA9IGQzLnNlbGVjdChtYWluVG1wRWxlbWVudCk7XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW50KGVsZW0uY2hpbGRyZW4oKVswXSkud2lkdGgoY29udGFpbmVyVyk7XG4gICAgICBzY29wZS56b29tSW4gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHRyYW5zbGF0ZSwgdjEsIHYyO1xuICAgICAgICBpZiAobWFpblpvb20uc2NhbGUoKSA8IDIuOTkpIHtcbiAgICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKTtcbiAgICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICBtYWluWm9vbS5zY2FsZShtYWluWm9vbS5zY2FsZSgpICsgMC4xKTtcbiAgICAgICAgICBtYWluWm9
vbS50cmFuc2xhdGUoW3YxLCB2Ml0pO1xuICAgICAgICAgIHJldHVybiBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgc2NvcGUuem9vbU91dCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgdHJhbnNsYXRlLCB2MSwgdjI7XG4gICAgICAgIGlmIChtYWluWm9vbS5zY2FsZSgpID4gMC4zMSkge1xuICAgICAgICAgIG1haW5ab29tLnNjYWxlKG1haW5ab29tLnNjYWxlKCkgLSAwLjEpO1xuICAgICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpO1xuICAgICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpO1xuICAgICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpO1xuICAgICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZShbdjEsIHYyXSk7XG4gICAgICAgICAgcmV0dXJuIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIik7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjcmVhdGVMYWJlbEVkZ2UgPSBmdW5j
dGlvbihlbCkge1xuICAgICAgICB2YXIgbGFiZWxWYWx1ZTtcbiAgICAgICAgbGFiZWxWYWx1ZSA9IFwiXCI7XG4gICAgICAgIGlmICgoZWwuc2hpcF9zdHJhdGVneSAhPSBudWxsKSB8fCAoZWwubG9jYWxfc3RyYXRlZ3kgIT0gbnVsbCkpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGRpdiBjbGFzcz0nZWRnZS1sYWJlbCc+XCI7XG4gICAgICAgICAgaWYgKGVsLnNoaXBfc3RyYXRlZ3kgIT0gbnVsbCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBlbC5zaGlwX3N0cmF0ZWd5O1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwudGVtcF9tb2RlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCIgKFwiICsgZWwudGVtcF9tb2RlICsgXCIpXCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5sb2NhbF9zdHJhdGVneSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiLDxicj5cIiArIGVsLmxvY2FsX3N0cmF0ZWd5O1xuICAgICAgICAgIH1cbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPC9kaXY+XCI7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGxhYmVsVmFsdWU7XG4gICAgICB9O1xuICAgICAgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZSA9IGZ1bmN0aW9uKGluZm8pIHtcbiAgICAgICAgcmV0dXJuIGluZm8gPT09IFwicGFydGlhbFNvbHV0aW9uXCIgfHwgaW5mbyA9PT0gXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIgf
HwgaW5mbyA9PT0gXCJ3b3Jrc2V0XCIgfHwgaW5mbyA9PT0gXCJuZXh0V29ya3NldFwiIHx8IGluZm8gPT09IFwic29sdXRpb25TZXRcIiB8fCBpbmZvID09PSBcInNvbHV0aW9uRGVsdGFcIjtcbiAgICAgIH07XG4gICAgICBnZXROb2RlVHlwZSA9IGZ1bmN0aW9uKGVsLCBpbmZvKSB7XG4gICAgICAgIGlmIChpbmZvID09PSBcIm1pcnJvclwiKSB7XG4gICAgICAgICAgcmV0dXJuICdub2RlLW1pcnJvcic7XG4gICAgICAgIH0gZWxzZSBpZiAoaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKSkge1xuICAgICAgICAgIHJldHVybiAnbm9kZS1pdGVyYXRpb24nO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiAnbm9kZS1ub3JtYWwnO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlTGFiZWxOb2RlID0gZnVuY3Rpb24oZWwsIGluZm8sIG1heFcsIG1heEgpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWUsIHN0ZXBOYW1lO1xuICAgICAgICBsYWJlbFZhbHVlID0gXCI8YSBocmVmPScjL2pvYnMvXCIgKyBqb2JpZCArIFwiL3ZlcnRleC9cIiArIGVsLmlkICsgXCInIGNsYXNzPSdub2RlLWxhYmVsIFwiICsgZ2V0Tm9kZVR5cGUoZWwsIGluZm8pICsgXCInPlwiO1xuICAgICAgICBpZiAoaW5mbyA9PT0gXCJtaXJyb3JcIikge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+TWlycm9yIG9mIFwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCI7XG4gIC
AgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5cIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiO1xuICAgICAgICB9XG4gICAgICAgIGlmIChlbC5kZXNjcmlwdGlvbiA9PT0gXCJcIikge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCJcIjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzdGVwTmFtZSA9IGVsLmRlc2NyaXB0aW9uO1xuICAgICAgICAgIHN0ZXBOYW1lID0gc2hvcnRlblN0cmluZyhzdGVwTmFtZSk7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNCBjbGFzcz0nc3RlcC1uYW1lJz5cIiArIHN0ZXBOYW1lICsgXCI8L2g0PlwiO1xuICAgICAgICB9XG4gICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uICE9IG51bGwpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbihlbC5pZCwgbWF4VywgbWF4SCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWYgKGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbykpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+XCIgKyBpbmZvICsgXCIgTm9kZTwvaDU+XCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5wYXJhbGxlbGlzbSAhPT0gXCJcIikge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5QYXJhbGxlbGlzbTogXCIgKyBlbC5wYXJhbGxlbGlzbSArIFwiPC9oNT5cIjtcbiAgICAgICA
gICB9XG4gICAgICAgICAgaWYgKGVsLm9wZXJhdG9yICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+T3BlcmF0aW9uOiBcIiArIHNob3J0ZW5TdHJpbmcoZWwub3BlcmF0b3Jfc3RyYXRlZ3kpICsgXCI8L2g1PlwiO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPC9hPlwiO1xuICAgICAgICByZXR1cm4gbGFiZWxWYWx1ZTtcbiAgICAgIH07XG4gICAgICBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24gPSBmdW5jdGlvbihpZCwgbWF4VywgbWF4SCkge1xuICAgICAgICB2YXIgbGFiZWxWYWx1ZSwgc3ZnSUQ7XG4gICAgICAgIHN2Z0lEID0gXCJzdmctXCIgKyBpZDtcbiAgICAgICAgbGFiZWxWYWx1ZSA9IFwiPHN2ZyBjbGFzcz0nXCIgKyBzdmdJRCArIFwiJyB3aWR0aD1cIiArIG1heFcgKyBcIiBoZWlnaHQ9XCIgKyBtYXhIICsgXCI+PGcgLz48L3N2Zz5cIjtcbiAgICAgICAgcmV0dXJuIGxhYmVsVmFsdWU7XG4gICAgICB9O1xuICAgICAgc2hvcnRlblN0cmluZyA9IGZ1bmN0aW9uKHMpIHtcbiAgICAgICAgdmFyIHNicjtcbiAgICAgICAgaWYgKHMuY2hhckF0KDApID09PSBcIjxcIikge1xuICAgICAgICAgIHMgPSBzLnJlcGxhY2UoXCI8XCIsIFwiJmx0O1wiKTtcbiAgICAgICAgICBzID0gcy5yZXBsYWNlKFwiPlwiLCBcIiZndDtcIik7XG4gICAgICAgIH1cbiAgICAgICAgc2JyID0gXCJcIjtcbiAgICAgICAgd2hpbGUgKHMu
bGVuZ3RoID4gMzApIHtcbiAgICAgICAgICBzYnIgPSBzYnIgKyBzLnN1YnN0cmluZygwLCAzMCkgKyBcIjxicj5cIjtcbiAgICAgICAgICBzID0gcy5zdWJzdHJpbmcoMzAsIHMubGVuZ3RoKTtcbiAgICAgICAgfVxuICAgICAgICBzYnIgPSBzYnIgKyBzO1xuICAgICAgICByZXR1cm4gc2JyO1xuICAgICAgfTtcbiAgICAgIGNyZWF0ZU5vZGUgPSBmdW5jdGlvbihnLCBkYXRhLCBlbCwgaXNQYXJlbnQsIG1heFcsIG1heEgpIHtcbiAgICAgICAgaWYgKGlzUGFyZW50ID09IG51bGwpIHtcbiAgICAgICAgICBpc1BhcmVudCA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIGlmIChlbC5pZCA9PT0gZGF0YS5wYXJ0aWFsX3NvbHV0aW9uKSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEubmV4dF9wYXJ0aWFsX3NvbHV0aW9uKSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIsIG1he
FcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS53b3Jrc2V0KSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJ3b3Jrc2V0XCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcIndvcmtzZXRcIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5uZXh0X3dvcmtzZXQpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRXb3Jrc2V0XCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRXb3Jrc2V0XCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEuc29sdXRpb25fc2V0KSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZU
xhYmVsTm9kZShlbCwgXCJzb2x1dGlvblNldFwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvblNldFwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLnNvbHV0aW9uX2RlbHRhKSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uRGVsdGFcIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIlwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJcIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNyZWF0ZUVkZ2UgPSBmdW5jdGlvbihnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZCkge1xuICAgICAgICB2YXIgbWlzc2luZ05vZGU7XG4gICAgICAgIGlmICh
leGlzdGluZ05vZGVzLmluZGV4T2YocHJlZC5pZCkgIT09IC0xKSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0RWRnZShwcmVkLmlkLCBlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShwcmVkKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgYXJyb3doZWFkOiAnbm9ybWFsJ1xuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG1pc3NpbmdOb2RlID0gc2VhcmNoRm9yTm9kZShkYXRhLCBwcmVkLmlkKTtcbiAgICAgICAgICBpZiAoISghbWlzc2luZ05vZGUgfHwgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkID09PSB0cnVlKSkge1xuICAgICAgICAgICAgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkID0gdHJ1ZTtcbiAgICAgICAgICAgIGcuc2V0Tm9kZShtaXNzaW5nTm9kZS5pZCwge1xuICAgICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKG1pc3NpbmdOb2RlLCBcIm1pcnJvclwiKSxcbiAgICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUobWlzc2luZ05vZGUsICdtaXJyb3InKVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXR1cm4gZy5zZXRFZGdlKG1pc3NpbmdOb2RlLmlkLCBlbC5pZCwge1xuICAgICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKG1pc3NpbmdOb2RlKSxcbiAgICAgICAgICAgICAgbGFiZWxUeXBl
OiAnaHRtbCdcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGxvYWRKc29uVG9EYWdyZSA9IGZ1bmN0aW9uKGcsIGRhdGEpIHtcbiAgICAgICAgdmFyIGVsLCBleGlzdGluZ05vZGVzLCBpc1BhcmVudCwgaywgbCwgbGVuLCBsZW4xLCBtYXhILCBtYXhXLCBwcmVkLCByLCByZWYsIHNnLCB0b0l0ZXJhdGU7XG4gICAgICAgIGV4aXN0aW5nTm9kZXMgPSBbXTtcbiAgICAgICAgaWYgKGRhdGEubm9kZXMgIT0gbnVsbCkge1xuICAgICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEubm9kZXM7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5zdGVwX2Z1bmN0aW9uO1xuICAgICAgICAgIGlzUGFyZW50ID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICBmb3IgKGsgPSAwLCBsZW4gPSB0b0l0ZXJhdGUubGVuZ3RoOyBrIDwgbGVuOyBrKyspIHtcbiAgICAgICAgICBlbCA9IHRvSXRlcmF0ZVtrXTtcbiAgICAgICAgICBtYXhXID0gMDtcbiAgICAgICAgICBtYXhIID0gMDtcbiAgICAgICAgICBpZiAoZWwuc3RlcF9mdW5jdGlvbikge1xuICAgICAgICAgICAgc2cgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7XG4gICAgICAgICAgICAgIG11bHRpZ3JhcGg6IHRydWUsXG4gICAgICAgICAgICAgIGNvbXBvdW5kOiB0cnVlXG4gICAgICAgICAgICB9KS5zZXRHcmFwaCh7XG4gICAgICAgICAgICAgIG5vZGVzZXA6IDIwLFxuICAgICAgICAgI
CAgICBlZGdlc2VwOiAwLFxuICAgICAgICAgICAgICByYW5rc2VwOiAyMCxcbiAgICAgICAgICAgICAgcmFua2RpcjogXCJMUlwiLFxuICAgICAgICAgICAgICBtYXJnaW54OiAxMCxcbiAgICAgICAgICAgICAgbWFyZ2lueTogMTBcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgc3ViZ3JhcGhzW2VsLmlkXSA9IHNnO1xuICAgICAgICAgICAgbG9hZEpzb25Ub0RhZ3JlKHNnLCBlbCk7XG4gICAg
<TRUNCATED>
[36/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Add
subtasks to vertex display
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.accumulators.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.accumulators.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.accumulators.html
index e3a20e3..8de3921 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.accumulators.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.accumulators.html
@@ -24,8 +24,8 @@ limitations under the License.
<th>Status</th>
</tr>
</thead>
- <tbody ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="changeNode(v.id)">
- <tr>
+ <tbody ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="v.id == nodeid || changeNode(v.id)">
+ <tr ng-if="v.type == 'regular'">
<td>{{ v.name | humanizeText }}</td>
<td>
<bs-label status="{{v.status}}">{{v.status}}</bs-label>
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.overview.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.overview.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.overview.html
index 01440f8..1706d3e 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.overview.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.overview.html
@@ -33,7 +33,7 @@ limitations under the License.
</tr>
</thead>
<tbody ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="changeNode(v.id)">
- <tr>
+ <tr ng-if="v.type == 'regular'">
<td><span ng-if="v['start-time'] > -1">{{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
<td><span ng-if="v['end-time'] > -1">{{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
<td><span ng-if="v.duration > -1">{{ v.duration }} ms</span></td>
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
index 6a79a7b..e7dcf2c 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
@@ -17,11 +17,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<div ng-if="vertex">
- <div ng-if="vertex.accumulators.length == 0">
- <p><i>No accumulators</i></p>
- </div>
- <table ng-if="vertex.accumulators.length > 0" class="table table-hover table-clickable table-activable table-inner">
+<div ng-if="accumulators.length == 0">
+ <p><i>No accumulators</i></p>
+</div>
+<div ng-if="accumulators && accumulators.length > 0">
+ <table class="table table-hover table-clickable table-activable table-inner">
<thead>
<tr>
<th>Name</th>
@@ -30,11 +30,39 @@ limitations under the License.
</tr>
</thead>
<tbody>
- <tr ng-repeat="accumulator in vertex.accumulators">
- <td>{{ accumulator.name }}</td>
- <td>{{ accumulator.type }}</td>
- <td>{{ accumulator.value }}</td>
+ <tr ng-repeat="accumulator in accumulators">
+ <td width="30%">{{ accumulator.name }}</td>
+ <td width="30%">{{ accumulator.type }}</td>
+ <td width="30%">{{ accumulator.value }}</td>
</tr>
</tbody>
</table>
+ <div ng-if="!nodeUnfolded"><a ng-click="toggleFold()" class="btn btn-default">
+ Show subtasks
+ <i class="fa fa-chevron-down"></i></a><a ng-click="deactivateNode(); $event.stopPropagation()" title="Fold" class="btn btn-default pull-right"><i class="fa fa-chevron-up"></i></a></div>
+ <div ng-if="nodeUnfolded && subtaskAccumulators && subtaskAccumulators.length > 0"><a ng-click="toggleFold()" class="btn btn-default">
+ Hide subtasks
+ <i class="fa fa-chevron-up"></i></a>
+ <table class="table table-hover table-clickable table-activable table-inner">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Type</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody ng-if="subtask['user-accumulators'] && subtask['user-accumulators'].length > 0" ng-repeat="subtask in subtaskAccumulators">
+ <tr>
+ <td colwidth="3">
+ <div class="small-label">({{ subtask.subtask }}) {{ subtask.host }}, attempt: {{ subtask.attempt + 1 }}</div>
+ </td>
+ </tr>
+ <tr ng-repeat="accumulator in subtask['user-accumulators']">
+ <td width="30%">{{ accumulator.name }}</td>
+ <td width="30%">{{ accumulator.type }}</td>
+ <td width="30%">{{ accumulator.value }}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
index 451c3a5..40b16bc 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
@@ -18,7 +18,7 @@ limitations under the License.
-->
-<table ng-if="vertex" class="table table-hover table-clickable table-activable table-inner">
+<table ng-if="subtasks" class="table table-hover table-clickable table-activable table-inner">
<thead>
<tr>
<th>Start Time</th>
@@ -34,7 +34,7 @@ limitations under the License.
</tr>
</thead>
<tbody>
- <tr ng-repeat="subtask in vertex.st">
+ <tr ng-repeat="subtask in subtasks">
<td><span ng-if="subtask['start-time'] > -1">{{ subtask['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
<td><span ng-if="subtask['end-time'] > -1">{{ subtask['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
<td><span ng-if="subtask.duration > -1">{{ subtask.duration }} ms</span></td>
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.html
index 4d15201..2f22576 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.html
@@ -18,6 +18,6 @@ limitations under the License.
-->
<div class="canvas-wrapper">
- <div timeline="timeline" job="job" class="timeline-canvas"></div>
+ <div timeline="timeline" vertices="vertices" jobid="jobid" class="timeline-canvas"></div>
</div>
<div ui-view="vertex"></div>
\ No newline at end of file
[37/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Add
subtasks to vertex display
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/93a4d31d/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index 6324e9a..bfd20db 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -220,49 +220,68 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
var refresher;
console.log('SingleJobController');
$scope.jobid = $stateParams.jobid;
- $rootScope.job = null;
- $rootScope.plan = null;
+ $scope.job = null;
+ $scope.plan = null;
+ $scope.vertices = null;
JobsService.loadJob($stateParams.jobid).then(function(data) {
- $rootScope.job = data;
- return $rootScope.plan = data.plan;
+ $scope.job = data;
+ $scope.plan = data.plan;
+ return $scope.vertices = data.vertices;
});
refresher = $interval(function() {
return JobsService.loadJob($stateParams.jobid).then(function(data) {
- $rootScope.job = data;
+ $scope.job = data;
return $scope.$broadcast('reload');
});
}, flinkConfig["refresh-interval"]);
return $scope.$on('$destroy', function() {
- $rootScope.job = null;
- $rootScope.plan = null;
+ $scope.job = null;
+ $scope.plan = null;
+ $scope.vertices = null;
return $interval.cancel(refresher);
});
}]).controller('JobPlanController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
console.log('JobPlanController');
$scope.nodeid = null;
+ $scope.nodeUnfolded = false;
$scope.stateList = JobsService.stateList();
- return $scope.changeNode = function(nodeid) {
+ $scope.changeNode = function(nodeid) {
if (nodeid !== $scope.nodeid) {
$scope.nodeid = nodeid;
$scope.vertex = null;
+ $scope.subtasks = null;
+ $scope.accumulators = null;
return $scope.$broadcast('reload');
} else {
$scope.nodeid = null;
- return $scope.vertex = null;
+ $scope.nodeUnfolded = false;
+ $scope.vertex = null;
+ $scope.subtasks = null;
+ return $scope.accumulators = null;
}
};
+ $scope.deactivateNode = function() {
+ $scope.nodeid = null;
+ $scope.nodeUnfolded = false;
+ $scope.vertex = null;
+ $scope.subtasks = null;
+ return $scope.accumulators = null;
+ };
+ return $scope.toggleFold = function() {
+ return $scope.nodeUnfolded = !$scope.nodeUnfolded;
+ };
}]).controller('JobPlanOverviewController', ["$scope", "JobsService", function($scope, JobsService) {
console.log('JobPlanOverviewController');
if ($scope.nodeid && (!$scope.vertex || !$scope.vertex.st)) {
JobsService.getSubtasks($scope.nodeid).then(function(data) {
- return $scope.vertex = data;
+ return $scope.subtasks = data;
});
}
return $scope.$on('reload', function(event) {
console.log('JobPlanOverviewController');
if ($scope.nodeid) {
return JobsService.getSubtasks($scope.nodeid).then(function(data) {
- return $scope.vertex = data;
+ return $scope.subtasks = data;
});
}
});
@@ -270,14 +289,16 @@ angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$stat
console.log('JobPlanAccumulatorsController');
if ($scope.nodeid && (!$scope.vertex || !$scope.vertex.accumulators)) {
JobsService.getAccumulators($scope.nodeid).then(function(data) {
- return $scope.vertex = data;
+ $scope.accumulators = data.main;
+ return $scope.subtaskAccumulators = data.subtasks;
});
}
return $scope.$on('reload', function(event) {
console.log('JobPlanAccumulatorsController');
if ($scope.nodeid) {
return JobsService.getAccumulators($scope.nodeid).then(function(data) {
- return $scope.vertex = data;
+ $scope.accumulators = data.main;
+ return $scope.subtaskAccumulators = data.subtasks;
});
}
});
@@ -328,7 +349,6 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
testData = [];
angular.forEach(data.subtasks, function(subtask, i) {
var times;
- console.log(data.subtasks);
times = [
{
label: "Scheduled",
@@ -357,7 +377,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
});
}
return testData.push({
- label: subtask.host + " (" + subtask.subtask + ")",
+ label: "(" + subtask.subtask + ") " + subtask.host,
times: times
});
});
@@ -381,7 +401,8 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
return {
template: "<svg class='timeline' width='0' height='0'></svg>",
scope: {
- job: "="
+ vertices: "=",
+ jobid: "="
},
link: function(scope, elem, attrs) {
var analyzeTime, containerW, svgEl, translateLabel;
@@ -395,39 +416,42 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
var chart, svg, testData;
d3.select(svgEl).selectAll("*").remove();
testData = [];
- testData.push({
- times: [
- {
- label: "Scheduled",
- color: "#cccccc",
- borderColor: "#555",
- starting_time: data.timestamps["CREATED"],
- ending_time: data.timestamps["CREATED"] + 1,
- type: 'scheduled'
- }
- ]
- });
- angular.forEach(data.vertices, function(vertex) {
+ angular.forEach(data, function(vertex) {
if (vertex['start-time'] > -1) {
- return testData.push({
- times: [
- {
- label: translateLabel(vertex.name),
- color: "#d9f1f7",
- borderColor: "#62cdea",
- starting_time: vertex['start-time'],
- ending_time: vertex['end-time'],
- link: vertex.id,
- type: 'regular'
- }
- ]
- });
+ if (vertex.type === 'scheduled') {
+ return testData.push({
+ times: [
+ {
+ label: translateLabel(vertex.name),
+ color: "#cccccc",
+ borderColor: "#555555",
+ starting_time: vertex['start-time'],
+ ending_time: vertex['end-time'],
+ type: vertex.type
+ }
+ ]
+ });
+ } else {
+ return testData.push({
+ times: [
+ {
+ label: translateLabel(vertex.name),
+ color: "#d9f1f7",
+ borderColor: "#62cdea",
+ starting_time: vertex['start-time'],
+ ending_time: vertex['end-time'],
+ link: vertex.id,
+ type: vertex.type
+ }
+ ]
+ });
+ }
}
});
chart = d3.timeline().stack().click(function(d, i, datum) {
if (d.link) {
return $state.go("single-job.timeline.vertex", {
- jobid: data.jid,
+ jobid: scope.jobid,
vertexId: d.link
});
}
@@ -442,7 +466,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
}).itemHeight(30).showBorderLine().showHourTimeline();
return svg = d3.select(svgEl).datum(testData).call(chart);
};
- scope.$watch(attrs.job, function(data) {
+ scope.$watch(attrs.vertices, function(data) {
if (data) {
return analyzeTime(data);
}
@@ -457,7 +481,8 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
setNode: '&'
},
link: function(scope, elem, attrs) {
- var containerW, createEdge, createLabelEdge, createLabelNode, createNode, d3mainSvg, d3mainSvgG, d3tmpSvg, drawGraph, extendLabelNodeForIteration, getNodeType, isSpecialIterationNode, jobid, loadJsonToDagre, mainG, mainSvgElement, mainTmpElement, mainZoom, searchForNode, shortenString, subgraphs;
+ var containerW, createEdge, createLabelEdge, createLabelNode, createNode, d3mainSvg, d3mainSvgG, d3tmpSvg, drawGraph, extendLabelNodeForIteration, g, getNodeType, isSpecialIterationNode, jobid, loadJsonToDagre, mainG, mainSvgElement, mainTmpElement, mainZoom, searchForNode, shortenString, subgraphs;
+ g = null;
mainZoom = d3.behavior.zoom();
subgraphs = [];
jobid = attrs.jobid;
@@ -706,7 +731,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
}
};
drawGraph = function(data) {
- var g, i, newScale, renderer, sg, xCenterOffset, yCenterOffset;
+ var i, newScale, renderer, sg, xCenterOffset, yCenterOffset;
g = new dagreD3.graphlib.Graph({
multigraph: true,
compound: true
@@ -808,6 +833,17 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
}
});
};
+ this.processVertices = function(data) {
+ angular.forEach(data.vertices, function(vertex, i) {
+ return vertex.type = 'regular';
+ });
+ return data.vertices.unshift({
+ name: 'Scheduled',
+ 'start-time': data.timestamps['CREATED'],
+ 'end-time': data.timestamps['CREATED'] + 1,
+ type: 'scheduled'
+ });
+ };
this.listJobs = function() {
var deferred;
deferred = $q.defer();
@@ -843,10 +879,11 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
$http.get(flinkConfig.jobServer + "/jobs/" + jobid).success((function(_this) {
return function(data, status, headers, config) {
_this.setEndTimes(data.vertices);
+ _this.processVertices(data);
return $http.get(flinkConfig.jobServer + "/jobs/" + jobid + "/config").success(function(jobConfig) {
data = angular.extend(data, jobConfig);
currentJob = data;
- return deferreds.job.resolve(data);
+ return deferreds.job.resolve(currentJob);
});
};
})(this));
@@ -855,9 +892,9 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.getNode = function(nodeid) {
var deferred, seekNode;
seekNode = function(nodeid, data) {
- var i, len, node, sub;
- for (i = 0, len = data.length; i < len; i++) {
- node = data[i];
+ var j, len, node, sub;
+ for (j = 0, len = data.length; j < len; j++) {
+ node = data[j];
if (node.id === nodeid) {
return node;
}
@@ -882,10 +919,10 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
return deferred.promise;
};
this.seekVertex = function(nodeid) {
- var i, len, ref, vertex;
+ var j, len, ref, vertex;
ref = currentJob.vertices;
- for (i = 0, len = ref.length; i < len; i++) {
- vertex = ref[i];
+ for (j = 0, len = ref.length; j < len; j++) {
+ vertex = ref[j];
if (vertex.id === nodeid) {
return vertex;
}
@@ -901,8 +938,6 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
vertex = _this.seekVertex(vertexid);
return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasktimes").success(function(data) {
vertex.subtasks = data.subtasks;
- _this.setEndTimes(vertex.subtasks);
- console.log(vertex.subtasks);
return deferred.resolve(vertex);
});
};
@@ -914,11 +949,10 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
deferred = $q.defer();
deferreds.job.promise.then((function(_this) {
return function(data) {
- var vertex;
- vertex = _this.seekVertex(vertexid);
return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid).success(function(data) {
- vertex.st = data.subtasks;
- return deferred.resolve(vertex);
+ var subtasks;
+ subtasks = data.subtasks;
+ return deferred.resolve(subtasks);
});
};
})(this));
@@ -929,11 +963,17 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
deferred = $q.defer();
deferreds.job.promise.then((function(_this) {
return function(data) {
- var vertex;
- vertex = _this.seekVertex(vertexid);
return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/accumulators").success(function(data) {
- vertex.accumulators = data['user-accumulators'];
- return deferred.resolve(vertex);
+ var accumulators;
+ accumulators = data['user-accumulators'];
+ return $http.get(flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasks/accumulators").success(function(data) {
+ var subtaskAccumulators;
+ subtaskAccumulators = data.subtasks;
+ return deferred.resolve({
+ main: accumulators,
+ subtasks: subtaskAccumulators
+ });
+ });
});
};
})(this));
@@ -994,4 +1034,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsImNvbW1vbi9zZXJ2aWNlcy5jb2ZmZWUiLCJjb21tb24vc2VydmljZXMuanMiLCJtb2R1bGVzL2pvYnMvam9icy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuanMiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuZGlyLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLnN2Yy5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LmN0cmwuanMiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkEsUUFBUSxPQUFPLFlBQVksQ0FBQyxhQUFhLGtCQUl4QyxtQkFBSSxTQUFDLFlBQUQ7RUFDSCxXQUFXLGlCQUFpQjtFQ3JCNUIsT0RzQkEsV0FBVyxjQUFjLFdBQUE7SUFDdkIsV0FBVyxpQkFBaUIsQ0FBQyxXQUFXO0lDckJ4QyxPRHNCQSxXQUFXLGVBQWU7O0lBSTdCLE1BQU0sZUFBZTtFQUN
wQixXQUFXO0VBRVgsb0JBQW9CO0dBS3JCLCtEQUFJLFNBQUMsYUFBYSxhQUFhLGFBQWEsV0FBeEM7RUM1QkgsT0Q2QkEsWUFBWSxhQUFhLEtBQUssU0FBQyxRQUFEO0lBQzVCLFFBQVEsT0FBTyxhQUFhO0lBRTVCLFlBQVk7SUM3QlosT0QrQkEsVUFBVSxXQUFBO01DOUJSLE9EK0JBLFlBQVk7T0FDWixZQUFZOztJQUtqQixpQ0FBTyxTQUFDLHVCQUFEO0VDakNOLE9Ea0NBLHNCQUFzQjtJQUl2QixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsVUFBVTtJQUNWLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLDRCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxnQkFDRTtRQUFBLGFBQWE7UUFDYixZQUFZOzs7S0FFakIsTUFBTSxnQ0FDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsZ0JBQ0U7UUFBQSxhQUFhO1FB
Q2IsWUFBWTs7O0tBRWpCLE1BQU0sdUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhOzs7S0FFbEIsTUFBTSw4QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsUUFDRTtRQUFBLGFBQWE7UUFDYixZQUFZOzs7S0FFakIsTUFBTSx5QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsU0FDRTtRQUFBLGFBQWE7OztLQUVsQixNQUFNLHlCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLHlCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTtRQUNiLFlBQVk7OztLQUVqQixNQUFNLHFCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTs7OztFQ3JCbkIsT0R1QkEsbUJBQW1CLFVBQVU7O0FDckIvQjtBQzVHQSxRQUFRLE9BQU8sWUFJZCxVQUFVLDJCQUFXLFNBQUMsYUFBRDtFQ3JCcEIsT0RzQkE7SUFBQSxZQUFZO0lBQ1osU0FBUztJQUNULE9BQ0U7TUFBQSxlQUFlO01BQ2YsUUFBUTs7SUFFVixVQUFVO0lBRVYsTUFBTSxTQUFDLE9BQU8sU0FBUyxPQUFqQjtNQ3JCRixPRHNCRixNQUFNLGdCQUFnQixXQUFBO1FDckJsQixPRHNCRixpQkFBaUIsWUFBWSxvQkFBb0IsTUFBTTs7OztJQUk1RCxVQUFVLG9DQUFvQixTQUFDLGFBQUQ7RUNyQjdCLE9Ec0JBO0lBQUEsU0FBUztJQUNULE9BQ0U7TUFBQSxlQUFlO01BQ2YsUUFBUTs7SUFFVixVQUFVO0lBRVYsTUFBTSxTQ
UFDLE9BQU8sU0FBUyxPQUFqQjtNQ3JCRixPRHNCRixNQUFNLGdCQUFnQixXQUFBO1FDckJsQixPRHNCRixzQ0FBc0MsWUFBWSxvQkFBb0IsTUFBTTs7OztJQUlqRixVQUFVLGlCQUFpQixXQUFBO0VDckIxQixPRHNCQTtJQUFBLFNBQVM7SUFDVCxPQUNFO01BQUEsT0FBTzs7SUFFVCxVQUFVOzs7QUNsQlo7QUNwQkEsUUFBUSxPQUFPLFlBRWQsT0FBTyxvREFBNEIsU0FBQyxxQkFBRDtFQUNsQyxJQUFBO0VBQUEsaUNBQWlDLFNBQUMsT0FBTyxRQUFRLGdCQUFoQjtJQUMvQixJQUFjLE9BQU8sVUFBUyxlQUFlLFVBQVMsTUFBdEQ7TUFBQSxPQUFPOztJQ2hCUCxPRGtCQSxPQUFPLFNBQVMsT0FBTyxRQUFRLE9BQU8sZ0JBQWdCO01BQUUsTUFBTTs7O0VBRWhFLCtCQUErQixZQUFZLG9CQUFvQjtFQ2YvRCxPRGlCQTtJQUVELE9BQU8sZ0JBQWdCLFdBQUE7RUNqQnRCLE9Ea0JBLFNBQUMsTUFBRDtJQUVFLElBQUcsTUFBSDtNQ2xCRSxPRGtCVyxLQUFLLFFBQVEsU0FBUyxLQUFLLFFBQVEsV0FBVTtXQUExRDtNQ2hCRSxPRGdCaUU7Ozs7QUNadkU7QUNIQSxRQUFRLE9BQU8sWUFFZCxRQUFRLDhDQUFlLFNBQUMsT0FBTyxhQUFhLElBQXJCO0VBQ3RCLEtBQUMsYUFBYSxXQUFBO0lBQ1osSUFBQTtJQUFBLFdBQVcsR0FBRztJQUVkLE1BQU0sSUFBSSxZQUFZLFlBQVksV0FDakMsUUFBUSxTQUFDLE1BQU0sUUFBUSxTQUFTLFFBQXhCO01DcEJQLE9EcUJBLFNBQVMsUUFBUTs7SUNuQm5CLE9EcUJBLFNBQVM7O0VDbkJYLE9Ec0JBOz
tBQ3BCRjtBQ09BLFFBQVEsT0FBTyxZQUVkLFdBQVcsNkVBQXlCLFNBQUMsUUFBUSxRQUFRLGNBQWMsYUFBL0I7RUFDbkMsT0FBTyxjQUFjLFdBQUE7SUNuQm5CLE9Eb0JBLE9BQU8sT0FBTyxZQUFZLFFBQVE7O0VBRXBDLFlBQVksaUJBQWlCLE9BQU87RUFDcEMsT0FBTyxJQUFJLFlBQVksV0FBQTtJQ25CckIsT0RvQkEsWUFBWSxtQkFBbUIsT0FBTzs7RUNsQnhDLE9Eb0JBLE9BQU87SUFJUixXQUFXLCtFQUEyQixTQUFDLFFBQVEsUUFBUSxjQUFjLGFBQS9CO0VBQ3JDLE9BQU8sY0FBYyxXQUFBO0lDdEJuQixPRHVCQSxPQUFPLE9BQU8sWUFBWSxRQUFROztFQUVwQyxZQUFZLGlCQUFpQixPQUFPO0VBQ3BDLE9BQU8sSUFBSSxZQUFZLFdBQUE7SUN0QnJCLE9EdUJBLFlBQVksbUJBQW1CLE9BQU87O0VDckJ4QyxPRHVCQSxPQUFPO0lBSVIsV0FBVyxxSEFBdUIsU0FBQyxRQUFRLFFBQVEsY0FBYyxhQUFhLFlBQVksYUFBYSxXQUFyRTtFQUNqQyxJQUFBO0VBQUEsUUFBUSxJQUFJO0VBRVosT0FBTyxRQUFRLGFBQWE7RUFDNUIsV0FBVyxNQUFNO0VBQ2pCLFdBQVcsT0FBTztFQUVsQixZQUFZLFFBQVEsYUFBYSxPQUFPLEtBQUssU0FBQyxNQUFEO0lBQzNDLFdBQVcsTUFBTTtJQzFCakIsT0QyQkEsV0FBVyxPQUFPLEtBQUs7O0VBRXpCLFlBQVksVUFBVSxXQUFBO0lDMUJwQixPRDJCQSxZQUFZLFFBQVEsYUFBYSxPQUFPLEtBQUssU0FBQyxNQUFEO01BQzNDLFdBQVcsTUFBTTtNQzFCakIsT0Q2QkEsT0FBTyxXQUFXOztLQUV
wQixZQUFZO0VDNUJkLE9EOEJBLE9BQU8sSUFBSSxZQUFZLFdBQUE7SUFDckIsV0FBVyxNQUFNO0lBQ2pCLFdBQVcsT0FBTztJQzdCbEIsT0QrQkEsVUFBVSxPQUFPOztJQUtwQixXQUFXLHlFQUFxQixTQUFDLFFBQVEsUUFBUSxjQUFjLGFBQS9CO0VBQy9CLFFBQVEsSUFBSTtFQUVaLE9BQU8sU0FBUztFQUNoQixPQUFPLFlBQVksWUFBWTtFQ2xDL0IsT0RvQ0EsT0FBTyxhQUFhLFNBQUMsUUFBRDtJQUNsQixJQUFHLFdBQVUsT0FBTyxRQUFwQjtNQUNFLE9BQU8sU0FBUztNQUNoQixPQUFPLFNBQVM7TUNuQ2hCLE9EcUNBLE9BQU8sV0FBVztXQUpwQjtNQU9FLE9BQU8sU0FBUztNQ3JDaEIsT0RzQ0EsT0FBTyxTQUFTOzs7SUFJckIsV0FBVyx1REFBNkIsU0FBQyxRQUFRLGFBQVQ7RUFDdkMsUUFBUSxJQUFJO0VBRVosSUFBRyxPQUFPLFdBQVksQ0FBQyxPQUFPLFVBQVUsQ0FBQyxPQUFPLE9BQU8sS0FBdkQ7SUFDRSxZQUFZLFlBQVksT0FBTyxRQUFRLEtBQUssU0FBQyxNQUFEO01DdkMxQyxPRHdDQSxPQUFPLFNBQVM7OztFQ3JDcEIsT0R1Q0EsT0FBTyxJQUFJLFVBQVUsU0FBQyxPQUFEO0lBQ25CLFFBQVEsSUFBSTtJQUNaLElBQUcsT0FBTyxRQUFWO01DdENFLE9EdUNBLFlBQVksWUFBWSxPQUFPLFFBQVEsS0FBSyxTQUFDLE1BQUQ7UUN0QzFDLE9EdUNBLE9BQU8sU0FBUzs7OztJQUl2QixXQUFXLDJEQUFpQyxTQUFDLFFBQVEsYUFBVDtFQUMzQyxRQUFRLElBQUk7RUFFWixJQUFHLE9BQU8sV0FBWSxDQUFDLE9BQU8s
VUFBVSxDQUFDLE9BQU8sT0FBTyxlQUF2RDtJQUNFLFlBQVksZ0JBQWdCLE9BQU8sUUFBUSxLQUFLLFNBQUMsTUFBRDtNQ3ZDOUMsT0R3Q0EsT0FBTyxTQUFTOzs7RUNyQ3BCLE9EdUNBLE9BQU8sSUFBSSxVQUFVLFNBQUMsT0FBRDtJQUNuQixRQUFRLElBQUk7SUFDWixJQUFHLE9BQU8sUUFBVjtNQ3RDRSxPRHVDQSxZQUFZLGdCQUFnQixPQUFPLFFBQVEsS0FBSyxTQUFDLE1BQUQ7UUN0QzlDLE9EdUNBLE9BQU8sU0FBUzs7OztJQUl2QixXQUFXLG1GQUErQixTQUFDLFFBQVEsUUFBUSxjQUFjLGFBQS9CO0VBQ3pDLFFBQVEsSUFBSTtFQUVaLFlBQVksVUFBVSxhQUFhLFVBQVUsS0FBSyxTQUFDLE1BQUQ7SUN2Q2hELE9Ed0NBLE9BQU8sU0FBUzs7RUN0Q2xCLE9Ed0NBLE9BQU8sSUFBSSxVQUFVLFNBQUMsT0FBRDtJQUNuQixRQUFRLElBQUk7SUN2Q1osT0R3Q0EsWUFBWSxVQUFVLGFBQWEsVUFBVSxLQUFLLFNBQUMsTUFBRDtNQ3ZDaEQsT0R3Q0EsT0FBTyxTQUFTOzs7SUFJckIsV0FBVywrRUFBMkIsU0FBQyxRQUFRLFFBQVEsY0FBYyxhQUEvQjtFQ3hDckMsT0R5Q0EsWUFBWSxpQkFBaUIsS0FBSyxTQUFDLE1BQUQ7SUN4Q2hDLE9EeUNBLE9BQU8sYUFBYTs7SUFJdkIsV0FBVyxxREFBMkIsU0FBQyxRQUFRLGFBQVQ7RUFDckMsUUFBUSxJQUFJO0VDMUNaLE9ENENBLE9BQU8sYUFBYSxTQUFDLFFBQUQ7SUFDbEIsSUFBRyxXQUFVLE9BQU8sUUFBcEI7TUFDRSxPQUFPLFNBQVM7TUMzQ2hCLE9ENkNBLFlBQVksUUFBUSxRQ
UFRLEtBQUssU0FBQyxNQUFEO1FDNUMvQixPRDZDQSxPQUFPLE9BQU87O1dBSmxCO01BT0UsT0FBTyxTQUFTO01DNUNoQixPRDZDQSxPQUFPLE9BQU87Ozs7QUN6Q3BCO0FDOUZBLFFBQVEsT0FBTyxZQUlkLFVBQVUscUJBQVUsU0FBQyxRQUFEO0VDckJuQixPRHNCQTtJQUFBLFVBQVU7SUFFVixPQUNFO01BQUEsTUFBTTs7SUFFUixNQUFNLFNBQUMsT0FBTyxNQUFNLE9BQWQ7TUFDSixJQUFBLGFBQUEsWUFBQTtNQUFBLFFBQVEsS0FBSyxXQUFXO01BRXhCLGFBQWEsS0FBSztNQUNsQixRQUFRLFFBQVEsT0FBTyxLQUFLLFNBQVM7TUFFckMsY0FBYyxTQUFDLE1BQUQ7UUFDWixJQUFBLE9BQUEsS0FBQTtRQUFBLEdBQUcsT0FBTyxPQUFPLFVBQVUsS0FBSztRQUVoQyxXQUFXO1FBRVgsUUFBUSxRQUFRLEtBQUssVUFBVSxTQUFDLFNBQVMsR0FBVjtVQUM3QixJQUFBO1VBQUEsUUFBUSxJQUFJLEtBQUs7VUFFakIsUUFBUTtZQUNOO2NBQ0UsT0FBTztjQUNQLE9BQU87Y0FDUCxhQUFhO2NBQ2IsZUFBZSxRQUFRLFdBQVc7Y0FDbEMsYUFBYSxRQUFRLFdBQVc7Y0FDaEMsTUFBTTtlQUVSO2NBQ0UsT0FBTztjQUNQLE9BQU87Y0FDUCxhQUFhO2NBQ2IsZUFBZSxRQUFRLFdBQVc7Y0FDbEMsYUFBYSxRQUFRLFdBQVc7Y0FDaEMsTUFBTTs7O1VBSVYsSUFBRyxRQUFRLFdBQVcsY0FBYyxHQUFwQztZQUNFLE1BQU0sS0FBSztjQUNULE9BQU87Y0FDUCxPQUFPO2NBQ1AsYUFBYTtjQUNiLGVBQWUsUUFBUSxXQUFXO2NBQ2xDLGFBQWEsUU
FBUSxXQUFXO2NBQ2hDLE1BQU07OztVQ3ZCUixPRDBCRixTQUFTLEtBQUs7WUFDWixPQUFVLFFBQVEsT0FBSyxPQUFJLFFBQVEsVUFBUTtZQUMzQyxPQUFPOzs7UUFHWCxRQUFRLEdBQUcsV0FBVyxRQUNyQixXQUFXO1VBQ1YsUUFBUSxHQUFHLEtBQUssT0FBTztVQUV2QixVQUFVO1dBRVgsT0FBTyxVQUNQLFlBQVksU0FBQyxPQUFEO1VDN0JULE9EOEJGO1dBRUQsT0FBTztVQUFFLE1BQU07VUFBSyxPQUFPO1VBQUcsS0FBSztVQUFHLFFBQVE7V0FDOUMsV0FBVyxJQUNYO1FDM0JDLE9ENkJGLE1BQU0sR0FBRyxPQUFPLE9BQ2YsTUFBTSxVQUNOLEtBQUs7O01BRVIsWUFBWSxNQUFNOzs7SUFNckIsVUFBVSx1QkFBWSxTQUFDLFFBQUQ7RUNqQ3JCLE9Ea0NBO0lBQUEsVUFBVTtJQUVWLE9BQ0U7TUFBQSxLQUFLOztJQUVQLE1BQU0sU0FBQyxPQUFPLE1BQU0sT0FBZDtNQUNKLElBQUEsYUFBQSxZQUFBLE9BQUE7TUFBQSxRQUFRLEtBQUssV0FBVztNQUV4QixhQUFhLEtBQUs7TUFDbEIsUUFBUSxRQUFRLE9BQU8sS0FBSyxTQUFTO01BRXJDLGlCQUFpQixTQUFDLE9BQUQ7UUNsQ2IsT0RtQ0YsTUFBTSxRQUFRLFFBQVE7O01BRXhCLGNBQWMsU0FBQyxNQUFEO1FBQ1osSUFBQSxPQUFBLEtBQUE7UUFBQSxHQUFHLE9BQU8sT0FBTyxVQUFVLEtBQUs7UUFFaEMsV0FBVztRQUVYLFNBQVMsS0FDUDtVQUFBLE9BQU87WUFDTDtjQUFBLE9BQU87Y0FDUCxPQUFPO2NBQ1AsYUFBYTtjQUNiLGVBQWUsS0FBSyxXQUFXO2NBQy9CLGFBQWE
sS0FBSyxXQUFXLGFBQWE7Y0FDMUMsTUFBTTs7OztRQUdWLFFBQVEsUUFBUSxLQUFLLFVBQVUsU0FBQyxRQUFEO1VBQzdCLElBQUcsT0FBTyxnQkFBZ0IsQ0FBQyxHQUEzQjtZQ2pDSSxPRGtDRixTQUFTLEtBQ1A7Y0FBQSxPQUFPO2dCQUNMO2tCQUFBLE9BQU8sZUFBZSxPQUFPO2tCQUM3QixPQUFPO2tCQUNQLGFBQWE7a0JBQ2IsZUFBZSxPQUFPO2tCQUN0QixhQUFhLE9BQU87a0JBQ3BCLE1BQU0sT0FBTztrQkFDYixNQUFNOzs7Ozs7UUFHZCxRQUFRLEdBQUcsV0FBVyxRQUFRLE1BQU0sU0FBQyxHQUFHLEdBQUcsT0FBUDtVQUNsQyxJQUFHLEVBQUUsTUFBTDtZQzdCSSxPRDhCRixPQUFPLEdBQUcsOEJBQThCO2NBQUUsT0FBTyxLQUFLO2NBQUssVUFBVSxFQUFFOzs7V0FHMUUsV0FBVztVQUNWLFFBQVEsR0FBRyxLQUFLLE9BQU87VUFHdkIsVUFBVTtXQUVYLE9BQU8sUUFDUCxPQUFPO1VBQUUsTUFBTTtVQUFHLE9BQU87VUFBRyxLQUFLO1VBQUcsUUFBUTtXQUM1QyxXQUFXLElBQ1gsaUJBQ0E7UUM3QkMsT0QrQkYsTUFBTSxHQUFHLE9BQU8sT0FDZixNQUFNLFVBQ04sS0FBSzs7TUFFUixNQUFNLE9BQU8sTUFBTSxLQUFLLFNBQUMsTUFBRDtRQUN0QixJQUFxQixNQUFyQjtVQ2hDSSxPRGdDSixZQUFZOzs7OztJQU1qQixVQUFVLHdCQUFXLFNBQUMsVUFBRDtFQ2hDcEIsT0RpQ0E7SUFBQSxVQUFVO0lBUVYsT0FDRTtNQUFBLE1BQU07TUFDTixTQUFTOztJQUVYLE1BQU0sU0FBQyxPQUFPLE1BQU0sT0FBZDtNQUNKLElB
QUEsWUFBQSxZQUFBLGlCQUFBLGlCQUFBLFlBQUEsV0FBQSxZQUFBLFVBQUEsV0FBQSw2QkFBQSxhQUFBLHdCQUFBLE9BQUEsaUJBQUEsT0FBQSxnQkFBQSxnQkFBQSxVQUFBLGVBQUEsZUFBQTtNQUFBLFdBQVcsR0FBRyxTQUFTO01BQ3ZCLFlBQVk7TUFDWixRQUFRLE1BQU07TUFFZCxpQkFBaUIsS0FBSyxXQUFXO01BQ2pDLFFBQVEsS0FBSyxXQUFXLFdBQVc7TUFDbkMsaUJBQWlCLEtBQUssV0FBVztNQUVqQyxZQUFZLEdBQUcsT0FBTztNQUN0QixhQUFhLEdBQUcsT0FBTztNQUN2QixXQUFXLEdBQUcsT0FBTztNQUtyQixhQUFhLEtBQUs7TUFDbEIsUUFBUSxRQUFRLEtBQUssV0FBVyxJQUFJLE1BQU07TUFFMUMsTUFBTSxTQUFTLFdBQUE7UUFDYixJQUFBLFdBQUEsSUFBQTtRQUFBLElBQUcsU0FBUyxVQUFVLE1BQXRCO1VBR0UsWUFBWSxTQUFTO1VBQ3JCLEtBQUssVUFBVSxNQUFNLFNBQVMsVUFBVSxPQUFPLFNBQVM7VUFDeEQsS0FBSyxVQUFVLE1BQU0sU0FBUyxVQUFVLE9BQU8sU0FBUztVQUN4RCxTQUFTLE1BQU0sU0FBUyxVQUFVO1VBQ2xDLFNBQVMsVUFBVSxDQUFFLElBQUk7VUM3Q3ZCLE9EZ0RGLFdBQVcsS0FBSyxhQUFhLGVBQWUsS0FBSyxNQUFNLEtBQUssYUFBYSxTQUFTLFVBQVU7OztNQUVoRyxNQUFNLFVBQVUsV0FBQTtRQUNkLElBQUEsV0FBQSxJQUFBO1FBQUEsSUFBRyxTQUFTLFVBQVUsTUFBdEI7VUFHRSxTQUFTLE1BQU0sU0FBUyxVQUFVO1VBQ2xDLFlBQVksU0FBUztVQUNyQixLQUFLLFVBQVUsT
UFBTSxTQUFTLFVBQVUsT0FBTyxTQUFTO1VBQ3hELEtBQUssVUFBVSxNQUFNLFNBQVMsVUFBVSxPQUFPLFNBQVM7VUFDeEQsU0FBUyxVQUFVLENBQUUsSUFBSTtVQy9DdkIsT0RrREYsV0FBVyxLQUFLLGFBQWEsZUFBZSxLQUFLLE1BQU0sS0FBSyxhQUFhLFNBQVMsVUFBVTs7O01BR2hHLGtCQUFrQixTQUFDLElBQUQ7UUFDaEIsSUFBQTtRQUFBLGFBQWE7UUFDYixJQUFHLENBQUEsR0FBQSxpQkFBQSxVQUFxQixHQUFBLGtCQUFBLE9BQXhCO1VBQ0UsY0FBYztVQUNkLElBQW1DLEdBQUEsaUJBQUEsTUFBbkM7WUFBQSxjQUFjLEdBQUc7O1VBQ2pCLElBQWdELEdBQUcsY0FBYSxXQUFoRTtZQUFBLGNBQWMsT0FBTyxHQUFHLFlBQVk7O1VBQ3BDLElBQWtELEdBQUcsbUJBQWtCLFdBQXZFO1lBQUEsY0FBYyxVQUFVLEdBQUc7O1VBQzNCLGNBQWM7O1FDekNkLE9EMENGOztNQUlGLHlCQUF5QixTQUFDLE1BQUQ7UUMzQ3JCLE9ENENELFNBQVEscUJBQXFCLFNBQVEseUJBQXlCLFNBQVEsYUFBYSxTQUFRLGlCQUFpQixTQUFRLGlCQUFpQixTQUFROztNQUVoSixjQUFjLFNBQUMsSUFBSSxNQUFMO1FBQ1osSUFBRyxTQUFRLFVBQVg7VUMzQ0ksT0Q0Q0Y7ZUFFRyxJQUFHLHVCQUF1QixPQUExQjtVQzVDRCxPRDZDRjtlQURHO1VDMUNELE9EOENBOzs7TUFHTixrQkFBa0IsU0FBQyxJQUFJLE1BQU0sTUFBTSxNQUFqQjtRQUVoQixJQUFBLFlBQUE7UUFBQSxhQUFhLHVCQUF1QixRQUFRLGFBQWEsR0FBRyxLQUFLLHlCQUF5QixZQUFZLE
lBQUksUUFBUTtRQUdsSCxJQUFHLFNBQVEsVUFBWDtVQUNFLGNBQWMscUNBQXFDLEdBQUcsV0FBVztlQURuRTtVQUdFLGNBQWMsMkJBQTJCLEdBQUcsV0FBVzs7UUFDekQsSUFBRyxHQUFHLGdCQUFlLElBQXJCO1VBQ0UsY0FBYztlQURoQjtVQUdFLFdBQVcsR0FBRztVQUdkLFdBQVcsY0FBYztVQUN6QixjQUFjLDJCQUEyQixXQUFXOztRQUd0RCxJQUFHLEdBQUEsaUJBQUEsTUFBSDtVQUNFLGNBQWMsNEJBQTRCLEdBQUcsSUFBSSxNQUFNO2VBRHpEO1VBS0UsSUFBK0MsdUJBQXVCLE9BQXRFO1lBQUEsY0FBYyxTQUFTLE9BQU87O1VBQzlCLElBQXFFLEdBQUcsZ0JBQWUsSUFBdkY7WUFBQSxjQUFjLHNCQUFzQixHQUFHLGNBQWM7O1VBQ3JELElBQXdGLEdBQUcsYUFBWSxXQUF2RztZQUFBLGNBQWMsb0JBQW9CLGNBQWMsR0FBRyxxQkFBcUI7OztRQUcxRSxjQUFjO1FDOUNaLE9EK0NGOztNQUdGLDhCQUE4QixTQUFDLElBQUksTUFBTSxNQUFYO1FBQzVCLElBQUEsWUFBQTtRQUFBLFFBQVEsU0FBUztRQUVqQixhQUFhLGlCQUFpQixRQUFRLGFBQWEsT0FBTyxhQUFhLE9BQU87UUMvQzVFLE9EZ0RGOztNQUdGLGdCQUFnQixTQUFDLEdBQUQ7UUFFZCxJQUFBO1FBQUEsSUFBRyxFQUFFLE9BQU8sT0FBTSxLQUFsQjtVQUNFLElBQUksRUFBRSxRQUFRLEtBQUs7VUFDbkIsSUFBSSxFQUFFLFFBQVEsS0FBSzs7UUFDckIsTUFBTTtRQUNOLE9BQU0sRUFBRSxTQUFTLElBQWpCO1VBQ0UsTUFBTSxNQUFNLEVBQUUsVUFBVSxHQUFHLE1
BQU07VUFDakMsSUFBSSxFQUFFLFVBQVUsSUFBSSxFQUFFOztRQUN4QixNQUFNLE1BQU07UUM5Q1YsT0QrQ0Y7O01BRUYsYUFBYSxTQUFDLEdBQUcsTUFBTSxJQUFJLFVBQWtCLE1BQU0sTUFBdEM7UUM5Q1QsSUFBSSxZQUFZLE1BQU07VUQ4Q0MsV0FBVzs7UUFFcEMsSUFBRyxHQUFHLE9BQU0sS0FBSyxrQkFBakI7VUM1Q0ksT0Q2Q0YsRUFBRSxRQUFRLEdBQUcsSUFDWDtZQUFBLE9BQU8sZ0JBQWdCLElBQUksbUJBQW1CLE1BQU07WUFDcEQsV0FBVztZQUNYLFNBQU8sWUFBWSxJQUFJOztlQUV0QixJQUFHLEdBQUcsT0FBTSxLQUFLLHVCQUFqQjtVQzVDRCxPRDZDRixFQUFFLFFBQVEsR0FBRyxJQUNYO1lBQUEsT0FBTyxnQkFBZ0IsSUFBSSx1QkFBdUIsTUFBTTtZQUN4RCxXQUFXO1lBQ1gsU0FBTyxZQUFZLElBQUk7O2VBRXRCLElBQUcsR0FBRyxPQUFNLEtBQUssU0FBakI7VUM1Q0QsT0Q2Q0YsRUFBRSxRQUFRLEdBQUcsSUFDWDtZQUFBLE9BQU8sZ0JBQWdCLElBQUksV0FBVyxNQUFNO1lBQzVDLFdBQVc7WUFDWCxTQUFPLFlBQVksSUFBSTs7ZUFFdEIsSUFBRyxHQUFHLE9BQU0sS0FBSyxjQUFqQjtVQzVDRCxPRDZDRixFQUFFLFFBQVEsR0FBRyxJQUNYO1lBQUEsT0FBTyxnQkFBZ0IsSUFBSSxlQUFlLE1BQU07WUFDaEQsV0FBVztZQUNYLFNBQU8sWUFBWSxJQUFJOztlQUV0QixJQUFHLEdBQUcsT0FBTSxLQUFLLGNBQWpCO1VDNUNELE9ENkNGLEVBQUUsUUFBUSxHQUFHLElBQ1g7WUFBQSxPQUFPLGdCQUFnQixJQUFJ
LGVBQWUsTUFBTTtZQUNoRCxXQUFXO1lBQ1gsU0FBTyxZQUFZLElBQUk7O2VBRXRCLElBQUcsR0FBRyxPQUFNLEtBQUssZ0JBQWpCO1VDNUNELE9ENkNGLEVBQUUsUUFBUSxHQUFHLElBQ1g7WUFBQSxPQUFPLGdCQUFnQixJQUFJLGlCQUFpQixNQUFNO1lBQ2xELFdBQVc7WUFDWCxTQUFPLFlBQVksSUFBSTs7ZUFKdEI7VUN0Q0QsT0Q2Q0YsRUFBRSxRQUFRLEdBQUcsSUFDWDtZQUFBLE9BQU8sZ0JBQWdCLElBQUksSUFBSSxNQUFNO1lBQ3JDLFdBQVc7WUFDWCxTQUFPLFlBQVksSUFBSTs7OztNQUU3QixhQUFhLFNBQUMsR0FBRyxNQUFNLElBQUksZUFBZSxNQUE3QjtRQUNYLElBQUE7UUFBQSxJQUFPLGNBQWMsUUFBUSxLQUFLLFFBQU8sQ0FBQyxHQUExQztVQ3pDSSxPRDBDRixFQUFFLFFBQVEsS0FBSyxJQUFJLEdBQUcsSUFDcEI7WUFBQSxPQUFPLGdCQUFnQjtZQUN2QixXQUFXO1lBQ1gsV0FBVzs7ZUFKZjtVQU9FLGNBQWMsY0FBYyxNQUFNLEtBQUs7VUFDdkMsSUFBQSxFQUFPLENBQUMsZUFBZSxZQUFZLGlCQUFnQixPQUFuRDtZQUNFLFlBQVksZUFBZTtZQUMzQixFQUFFLFFBQVEsWUFBWSxJQUNwQjtjQUFBLE9BQU8sZ0JBQWdCLGFBQWE7Y0FDcEMsV0FBVztjQUNYLFNBQU8sWUFBWSxhQUFhOztZQ3hDaEMsT0QwQ0YsRUFBRSxRQUFRLFlBQVksSUFBSSxHQUFHLElBQzNCO2NBQUEsT0FBTyxnQkFBZ0I7Y0FDdkIsV0FBVzs7Ozs7TUFFbkIsa0JBQWtCLFNBQUMsR0FBRyxNQUFKO1FBQ2hCLElBQUEsSUFBQSxlQUFBL
FVBQUEsR0FBQSxHQUFBLEtBQUEsTUFBQSxNQUFBLE1BQUEsTUFBQSxHQUFBLEtBQUEsSUFBQTtRQUFBLGdCQUFnQjtRQUVoQixJQUFHLEtBQUEsU0FBQSxNQUFIO1VBRUUsWUFBWSxLQUFLO2VBRm5CO1VBTUUsWUFBWSxLQUFLO1VBQ2pCLFdBQVc7O1FBRWIsS0FBQSxJQUFBLEdBQUEsTUFBQSxVQUFBLFFBQUEsSUFBQSxLQUFBLEtBQUE7VUN6Q0ksS0FBSyxVQUFVO1VEMENqQixPQUFPO1VBQ1AsT0FBTztVQUVQLElBQUcsR0FBRyxlQUFOO1lBQ0UsS0FBUyxJQUFBLFFBQVEsU0FBUyxNQUFNO2NBQUUsWUFBWTtjQUFNLFVBQVU7ZUFBUSxTQUFTO2NBQzdFLFNBQVM7Y0FDVCxTQUFTO2NBQ1QsU0FBUztjQUNULFNBQVM7Y0FDVCxTQUFTO2NBQ1QsU0FBUzs7WUFHWCxVQUFVLEdBQUcsTUFBTTtZQUVuQixnQkFBZ0IsSUFBSTtZQUVwQixJQUFRLElBQUEsUUFBUTtZQUNoQixTQUFTLE9BQU8sS0FBSyxLQUFLLEdBQUc7WUFDN0IsT0FBTyxHQUFHLFFBQVE7WUFDbEIsT0FBTyxHQUFHLFFBQVE7WUFFbEIsUUFBUSxRQUFRLGdCQUFnQjs7VUFFbEMsV0FBVyxHQUFHLE1BQU0sSUFBSSxVQUFVLE1BQU07VUFFeEMsY0FBYyxLQUFLLEdBQUc7VUFHdEIsSUFBRyxHQUFBLFVBQUEsTUFBSDtZQUNFLE1BQUEsR0FBQTtZQUFBLEtBQUEsSUFBQSxHQUFBLE9BQUEsSUFBQSxRQUFBLElBQUEsTUFBQSxLQUFBO2NDNUNJLE9BQU8sSUFBSTtjRDZDYixXQUFXLEdBQUcsTUFBTSxJQUFJLGVBQWU7Ozs7UUN4QzNDLE9EMENGOztNQUdGLGdCQU
FnQixTQUFDLE1BQU0sUUFBUDtRQUNkLElBQUEsSUFBQSxHQUFBO1FBQUEsS0FBQSxLQUFBLEtBQUEsT0FBQTtVQUNFLEtBQUssS0FBSyxNQUFNO1VBQ2hCLElBQWMsR0FBRyxPQUFNLFFBQXZCO1lBQUEsT0FBTzs7VUFHUCxJQUFHLEdBQUEsaUJBQUEsTUFBSDtZQUNFLEtBQUEsS0FBQSxHQUFBLGVBQUE7Y0FDRSxJQUErQixHQUFHLGNBQWMsR0FBRyxPQUFNLFFBQXpEO2dCQUFBLE9BQU8sR0FBRyxjQUFjOzs7Ozs7TUFFaEMsWUFBWSxTQUFDLE1BQUQ7UUFHVixJQUFBLEdBQUEsR0FBQSxVQUFBLFVBQUEsSUFBQSxlQUFBO1FBQUEsSUFBUSxJQUFBLFFBQVEsU0FBUyxNQUFNO1VBQUUsWUFBWTtVQUFNLFVBQVU7V0FBUSxTQUFTO1VBQzVFLFNBQVM7VUFDVCxTQUFTO1VBQ1QsU0FBUztVQUNULFNBQVM7VUFDVCxTQUFTO1VBQ1QsU0FBUzs7UUFHWCxnQkFBZ0IsR0FBRztRQUVuQixXQUFlLElBQUEsUUFBUTtRQUN2QixXQUFXLEtBQUssVUFBVTtRQUUxQixLQUFBLEtBQUEsV0FBQTtVQ3JDSSxLQUFLLFVBQVU7VURzQ2pCLFVBQVUsT0FBTyxhQUFhLElBQUksTUFBTSxLQUFLLFVBQVU7O1FBRXpELFdBQVc7UUFFWCxnQkFBZ0IsS0FBSyxNQUFNLENBQUMsUUFBUSxRQUFRLGdCQUFnQixVQUFVLEVBQUUsUUFBUSxRQUFRLFlBQVk7UUFDcEcsZ0JBQWdCLEtBQUssTUFBTSxDQUFDLFFBQVEsUUFBUSxnQkFBZ0IsV0FBVyxFQUFFLFFBQVEsU0FBUyxZQUFZO1FBRXRHLFNBQVMsTUFBTSxVQUFVLFVBQVUsQ0FBQyxlQUFlO1FBRW5
ELFdBQVcsS0FBSyxhQUFhLGVBQWUsZ0JBQWdCLE9BQU8sZ0JBQWdCLGFBQWEsU0FBUyxVQUFVO1FBRW5ILFNBQVMsR0FBRyxRQUFRLFdBQUE7VUFDbEIsSUFBQTtVQUFBLEtBQUssR0FBRztVQ3ZDTixPRHdDRixXQUFXLEtBQUssYUFBYSxlQUFlLEdBQUcsWUFBWSxhQUFhLEdBQUcsUUFBUTs7UUFFckYsU0FBUztRQ3ZDUCxPRHlDRixXQUFXLFVBQVUsU0FBUyxHQUFHLFNBQVMsU0FBQyxHQUFEO1VDeEN0QyxPRHlDRixNQUFNLFFBQVE7WUFBRSxRQUFROzs7O01BRTVCLE1BQU0sT0FBTyxNQUFNLE1BQU0sU0FBQyxTQUFEO1FBQ3ZCLElBQXNCLFNBQXRCO1VDckNJLE9EcUNKLFVBQVU7Ozs7OztBQy9CaEI7QUNyYUEsUUFBUSxPQUFPLFlBRWQsUUFBUSw4RUFBZSxTQUFDLE9BQU8sYUFBYSxNQUFNLFVBQVUsSUFBSSxVQUF6QztFQUN0QixJQUFBLFlBQUEsYUFBQSxXQUFBLGNBQUEsTUFBQTtFQUFBLGFBQWE7RUFDYixjQUFjO0VBQ2QsWUFBWTtFQUNaLE9BQU87SUFDTCxTQUFTO0lBQ1QsVUFBVTtJQUNWLFdBQVc7SUFDWCxRQUFROztFQUdWLGVBQWU7RUFFZixrQkFBa0IsV0FBQTtJQ3BCaEIsT0RxQkEsUUFBUSxRQUFRLGNBQWMsU0FBQyxVQUFEO01DcEI1QixPRHFCQTs7O0VBRUosS0FBQyxtQkFBbUIsU0FBQyxVQUFEO0lDbkJsQixPRG9CQSxhQUFhLEtBQUs7O0VBRXBCLEtBQUMscUJBQXFCLFNBQUMsVUFBRDtJQUNwQixJQUFBO0lBQUEsUUFBUSxhQUFhLFFBQVE7SUNsQjdCLE9EbUJBLGFBQWEsT0FBTyxPQUFPOztF
QUU3QixLQUFDLFlBQVksV0FBQTtJQ2xCWCxPRG1CQSxDQUVFLGFBQ0EsYUFDQSxXQUNBLFlBQ0EsVUFDQSxhQUNBOztFQUdKLEtBQUMsc0JBQXNCLFNBQUMsT0FBRDtJQUNyQixRQUFPLE1BQU07TUFBYixLQUNPO1FDM0JILE9EMkJtQjtNQUR2QixLQUVPO1FDMUJILE9EMEJpQjtNQUZyQixLQUdPO1FDekJILE9EeUJvQjtNQUh4QixLQUlPO1FDeEJILE9Ed0JvQjtNQUp4QixLQUtPO1FDdkJILE9EdUJrQjtNQUx0QixLQU1PO1FDdEJILE9Ec0JvQjtNQU54QixLQU9PO1FDckJILE9EcUJrQjtNQVB0QixLQVFPO1FDcEJILE9Eb0JnQjtNQVJwQjtRQ1ZJLE9EbUJHOzs7RUFFVCxLQUFDLGNBQWMsU0FBQyxNQUFEO0lDakJiLE9Ea0JBLFFBQVEsUUFBUSxNQUFNLFNBQUMsTUFBTSxRQUFQO01BQ3BCLElBQUEsRUFBTyxLQUFLLGNBQWMsQ0FBQyxJQUEzQjtRQ2pCRSxPRGtCQSxLQUFLLGNBQWMsS0FBSyxnQkFBZ0IsS0FBSzs7OztFQUVuRCxLQUFDLFdBQVcsV0FBQTtJQUNWLElBQUE7SUFBQSxXQUFXLEdBQUc7SUFFZCxNQUFNLElBQUksWUFBWSxZQUFZLGdCQUNqQyxRQUFRLENBQUEsU0FBQSxPQUFBO01DaEJQLE9EZ0JPLFNBQUMsTUFBTSxRQUFRLFNBQVMsUUFBeEI7UUFDUCxRQUFRLFFBQVEsTUFBTSxTQUFDLE1BQU0sU0FBUDtVQUNwQixRQUFPO1lBQVAsS0FDTztjQ2ZELE9EZWdCLEtBQUssVUFBVSxNQUFDLFlBQVk7WUFEbEQsS0FFTztjQ2RELE9EY2lCLEtBQUssV0FBVyxNQUFDLFlBQVk7WUFGcEQsS0FHTztjQ2JEL
E9EYWtCLEtBQUssWUFBWSxNQUFDLFlBQVk7WUFIdEQsS0FJTztjQ1pELE9EWWUsS0FBSyxTQUFTLE1BQUMsWUFBWTs7O1FBRWxELFNBQVMsUUFBUTtRQ1ZmLE9EV0Y7O09BVE87SUNDVCxPRFVBLFNBQVM7O0VBRVgsS0FBQyxVQUFVLFNBQUMsTUFBRDtJQ1RULE9EVUEsS0FBSzs7RUFFUCxLQUFDLGFBQWEsV0FBQTtJQ1RaLE9EVUE7O0VBRUYsS0FBQyxVQUFVLFNBQUMsT0FBRDtJQUNULGFBQWE7SUFDYixVQUFVLE1BQU0sR0FBRztJQUVuQixNQUFNLElBQUksWUFBWSxZQUFZLFdBQVcsT0FDNUMsUUFBUSxDQUFBLFNBQUEsT0FBQTtNQ1hQLE9EV08sU0FBQyxNQUFNLFFBQVEsU0FBUyxRQUF4QjtRQUNQLE1BQUMsWUFBWSxLQUFLO1FDVmhCLE9EWUYsTUFBTSxJQUFJLFlBQVksWUFBWSxXQUFXLFFBQVEsV0FDcEQsUUFBUSxTQUFDLFdBQUQ7VUFDUCxPQUFPLFFBQVEsT0FBTyxNQUFNO1VBRTVCLGFBQWE7VUNiWCxPRGVGLFVBQVUsSUFBSSxRQUFROzs7T0FUakI7SUNGVCxPRGFBLFVBQVUsSUFBSTs7RUFFaEIsS0FBQyxVQUFVLFNBQUMsUUFBRDtJQUNULElBQUEsVUFBQTtJQUFBLFdBQVcsU0FBQyxRQUFRLE1BQVQ7TUFDVCxJQUFBLEdBQUEsS0FBQSxNQUFBO01BQUEsS0FBQSxJQUFBLEdBQUEsTUFBQSxLQUFBLFFBQUEsSUFBQSxLQUFBLEtBQUE7UUNWRSxPQUFPLEtBQUs7UURXWixJQUFlLEtBQUssT0FBTSxRQUExQjtVQUFBLE9BQU87O1FBQ1AsSUFBOEMsS0FBSyxlQUFuRDtVQUFBLE1BQU0sU0FBUyxRQUFRLEtBQU
s7O1FBQzVCLElBQWMsS0FBZDtVQUFBLE9BQU87OztNQ0ZULE9ESUE7O0lBR0YsV0FBVyxHQUFHO0lBRWQsVUFBVSxJQUFJLFFBQVEsS0FBSyxDQUFBLFNBQUEsT0FBQTtNQ0x6QixPREt5QixTQUFDLE1BQUQ7UUFDekIsSUFBQTtRQUFBLFlBQVksU0FBUyxRQUFRLFdBQVcsS0FBSztRQUU3QyxVQUFVLFNBQVMsTUFBQyxXQUFXO1FDSjdCLE9ETUYsU0FBUyxRQUFROztPQUxRO0lDRTNCLE9ES0EsU0FBUzs7RUFFWCxLQUFDLGFBQWEsU0FBQyxRQUFEO0lBQ1osSUFBQSxHQUFBLEtBQUEsS0FBQTtJQUFBLE1BQUEsV0FBQTtJQUFBLEtBQUEsSUFBQSxHQUFBLE1BQUEsSUFBQSxRQUFBLElBQUEsS0FBQSxLQUFBO01DRkUsU0FBUyxJQUFJO01ER2IsSUFBaUIsT0FBTyxPQUFNLFFBQTlCO1FBQUEsT0FBTzs7O0lBRVQsT0FBTzs7RUFFVCxLQUFDLFlBQVksU0FBQyxVQUFEO0lBQ1gsSUFBQTtJQUFBLFdBQVcsR0FBRztJQUVkLFVBQVUsSUFBSSxRQUFRLEtBQUssQ0FBQSxTQUFBLE9BQUE7TUNDekIsT0REeUIsU0FBQyxNQUFEO1FBQ3pCLElBQUE7UUFBQSxTQUFTLE1BQUMsV0FBVztRQ0duQixPRERGLE1BQU0sSUFBSSxZQUFZLFlBQVksV0FBVyxXQUFXLE1BQU0sZUFBZSxXQUFXLGlCQUN2RixRQUFRLFNBQUMsTUFBRDtVQUVQLE9BQU8sV0FBVyxLQUFLO1VBQ3ZCLE1BQUMsWUFBWSxPQUFPO1VBQ3BCLFFBQVEsSUFBSSxPQUFPO1VDQWpCLE9ERUYsU0FBUyxRQUFROzs7T0FWTTtJQ1kzQixPREFBLFNBQVM7O0VBRVgsS0FBQyx
jQUFjLFNBQUMsVUFBRDtJQUNiLElBQUE7SUFBQSxXQUFXLEdBQUc7SUFFZCxVQUFVLElBQUksUUFBUSxLQUFLLENBQUEsU0FBQSxPQUFBO01DQ3pCLE9ERHlCLFNBQUMsTUFBRDtRQUN6QixJQUFBO1FBQUEsU0FBUyxNQUFDLFdBQVc7UUNHbkIsT0RERixNQUFNLElBQUksWUFBWSxZQUFZLFdBQVcsV0FBVyxNQUFNLGVBQWUsVUFDNUUsUUFBUSxTQUFDLE1BQUQ7VUFDUCxPQUFPLEtBQUssS0FBSztVQ0NmLE9EQ0YsU0FBUyxRQUFROzs7T0FQTTtJQ1UzQixPRERBLFNBQVM7O0VBRVgsS0FBQyxrQkFBa0IsU0FBQyxVQUFEO0lBQ2pCLElBQUE7SUFBQSxXQUFXLEdBQUc7SUFFZCxVQUFVLElBQUksUUFBUSxLQUFLLENBQUEsU0FBQSxPQUFBO01DRXpCLE9ERnlCLFNBQUMsTUFBRDtRQUN6QixJQUFBO1FBQUEsU0FBUyxNQUFDLFdBQVc7UUNJbkIsT0RGRixNQUFNLElBQUksWUFBWSxZQUFZLFdBQVcsV0FBVyxNQUFNLGVBQWUsV0FBVyxpQkFDdkYsUUFBUSxTQUFDLE1BQUQ7VUFDUCxPQUFPLGVBQWUsS0FBSztVQ0V6QixPREFGLFNBQVMsUUFBUTs7O09BUE07SUNXM0IsT0RGQSxTQUFTOztFQUdYLEtBQUMsaUJBQWlCLFdBQUE7SUFDaEIsSUFBQTtJQUFBLFdBQVcsR0FBRztJQUVkLFVBQVUsSUFBSSxRQUFRLEtBQUssQ0FBQSxTQUFBLE9BQUE7TUNFekIsT0RGeUIsU0FBQyxNQUFEO1FDR3ZCLE9EREYsTUFBTSxJQUFJLFlBQVksWUFBWSxXQUFXLFdBQVcsTUFBTSxlQUM3RCxRQUFRLFNBQUMsWUFBRDtVQUNQLFdBQVcsYUFB
YTtVQ0N0QixPRENGLFNBQVMsUUFBUTs7O09BTk07SUNTM0IsT0REQSxTQUFTOztFQ0dYLE9EREE7O0FDR0Y7QUN6TEEsUUFBUSxPQUFPLFlBRWQsV0FBVywrRkFBc0IsU0FBQyxRQUFRLGlCQUFpQixhQUFhLFdBQVcsYUFBbEQ7RUFDaEMsSUFBQTtFQUFBLE9BQU8sY0FBYyxXQUFBO0lBQ25CLE9BQU8sY0FBYyxZQUFZLFFBQVE7SUNsQnpDLE9EbUJBLE9BQU8sZUFBZSxZQUFZLFFBQVE7O0VBRTVDLFlBQVksaUJBQWlCLE9BQU87RUFDcEMsT0FBTyxJQUFJLFlBQVksV0FBQTtJQ2xCckIsT0RtQkEsWUFBWSxtQkFBbUIsT0FBTzs7RUFFeEMsT0FBTztFQUVQLGdCQUFnQixlQUFlLEtBQUssU0FBQyxNQUFEO0lDbkJsQyxPRG9CQSxPQUFPLFdBQVc7O0VBRXBCLFVBQVUsVUFBVSxXQUFBO0lDbkJsQixPRG9CQSxnQkFBZ0IsZUFBZSxLQUFLLFNBQUMsTUFBRDtNQ25CbEMsT0RvQkEsT0FBTyxXQUFXOztLQUNwQixZQUFZO0VDbEJkLE9Eb0JBLE9BQU8sSUFBSSxZQUFZLFdBQUE7SUNuQnJCLE9Eb0JBLFVBQVUsT0FBTzs7O0FDakJyQjtBQ0xBLFFBQVEsT0FBTyxZQUVkLFFBQVEsa0RBQW1CLFNBQUMsT0FBTyxhQUFhLElBQXJCO0VBQzFCLElBQUE7RUFBQSxXQUFXO0VBRVgsS0FBQyxlQUFlLFdBQUE7SUFDZCxJQUFBO0lBQUEsV0FBVyxHQUFHO0lBRWQsTUFBTSxJQUFJLFlBQVksWUFBWSxhQUNqQyxRQUFRLFNBQUMsTUFBTSxRQUFRLFNBQVMsUUFBeEI7TUFDUCxXQUFXO01DcEJYLE9EcUJBLFNBQVMsUUFBUTs7SUNuQm5CL
E9EcUJBLFNBQVM7O0VDbkJYLE9EcUJBOztBQ25CRiIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcbiMgZGlzdHJpYnV0ZWQgd2l0aCB0aGlzIHdvcmsgZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25cbiMgcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuIyB0byB5b3UgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlXG4jIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuIyB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4jXG4jICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiNcbiMgVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuIyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4jIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZX
IgZXhwcmVzcyBvciBpbXBsaWVkLlxuIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4jIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuI1xuXG5hbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLnJ1biAoJHJvb3RTY29wZSkgLT5cbiAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9IGZhbHNlXG4gICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSAtPlxuICAgICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSAhJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZVxuICAgICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLnZhbHVlICdmbGlua0NvbmZpZycsIHtcbiAgam9iU2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDo4MDgxJ1xuIyAgam9iU2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDozMDAwL25ldy1zZXJ2ZXInXG4gIFwicmVmcmVzaC1pbnRlcnZhbFwiOiAxMDAwMFxufVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKEpvYnNTZXJ2aWNlLCBNYWluU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkgLT5cbiAgTWFpblNlcnZpY2UubG9
hZENvbmZpZygpLnRoZW4gKGNvbmZpZykgLT5cbiAgICBhbmd1bGFyLmV4dGVuZCBmbGlua0NvbmZpZywgY29uZmlnXG5cbiAgICBKb2JzU2VydmljZS5saXN0Sm9icygpXG5cbiAgICAkaW50ZXJ2YWwgLT5cbiAgICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKClcbiAgICAsIGZsaW5rQ29uZmlnW1wicmVmcmVzaC1pbnRlcnZhbFwiXVxuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbmZpZyAoJHVpVmlld1Njcm9sbFByb3ZpZGVyKSAtPlxuICAkdWlWaWV3U2Nyb2xsUHJvdmlkZXIudXNlQW5jaG9yU2Nyb2xsKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29uZmlnICgkc3RhdGVQcm92aWRlciwgJHVybFJvdXRlclByb3ZpZGVyKSAtPlxuICAkc3RhdGVQcm92aWRlci5zdGF0ZSBcIm92ZXJ2aWV3XCIsXG4gICAgdXJsOiBcIi9vdmVydmlld1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9vdmVydmlldy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udHJvbGxlcidcblxuICAuc3RhdGUgXCJydW5uaW5nLWpvYnNcIixcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL3J1bm5pbmctam9icy5odG1sXCJcbiAgICAgICAgY29udHJvbGxl
cjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgXG4gIC5zdGF0ZSBcImNvbXBsZXRlZC1qb2JzXCIsXG4gICAgdXJsOiBcIi9jb21wbGV0ZWQtam9ic1wiXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYlwiLFxuICAgIHVybDogXCIvam9icy97am9iaWR9XCJcbiAgICBhYnN0cmFjdDogdHJ1ZVxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdTaW5nbGVKb2JDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucGxhblwiLFxuICAgIHVybDogXCJcIlxuICAgIGFic3RyYWN0OiB0cnVlXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm92ZXJ2aWV3XCIsXG4gICAgdXJsOiBcIlwiXG4gICAgdmlld3M6XG4gICAgICAnbm9kZS1kZXRhaWxzJzpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub
2RlLWxpc3Qub3ZlcnZpZXcuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuT3ZlcnZpZXdDb250cm9sbGVyJyBcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4uYWNjdW11bGF0b3JzXCIsXG4gICAgdXJsOiBcIi9hY2N1bXVsYXRvcnNcIlxuICAgIHZpZXdzOlxuICAgICAgJ25vZGUtZGV0YWlscyc6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS1saXN0LmFjY3VtdWxhdG9ycy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlBsYW5BY2N1bXVsYXRvcnNDb250cm9sbGVyJyBcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lXCIsXG4gICAgdXJsOiBcIi90aW1lbGluZVwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLFxuICAgIHVybDogXCIve3ZlcnRleElkfVwiXG4gICAgdmlld3M6XG4gICAgICB2ZXJ0ZXg6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLnZlcnRleC5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIixcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIlxuICAgIHZpZX
dzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2Iuc3RhdGlzdGljcy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIixcbiAgICB1cmw6IFwiL2V4Y2VwdGlvbnNcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYkV4Y2VwdGlvbnNDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2IucHJvcGVydGllc1wiLFxuICAgIHVybDogXCIvcHJvcGVydGllc1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wcm9wZXJ0aWVzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUHJvcGVydGllc0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5jb25maWdcIixcbiAgICB1cmw6IFwiL2NvbmZpZ1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5jb25maWcuaHRtbFwiXG5cbiAgJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZSBcIi9vdmVydmlld1wiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pLnJ1bihmdW5jdGlvbigkcm9
vdFNjb3BlKSB7XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZTtcbiAgcmV0dXJuICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSBmdW5jdGlvbigpIHtcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGU7XG4gICAgcmV0dXJuICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnO1xuICB9O1xufSkudmFsdWUoJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnLFxuICBcInJlZnJlc2gtaW50ZXJ2YWxcIjogMTAwMDBcbn0pLnJ1bihmdW5jdGlvbihKb2JzU2VydmljZSwgTWFpblNlcnZpY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIHtcbiAgcmV0dXJuIE1haW5TZXJ2aWNlLmxvYWRDb25maWcoKS50aGVuKGZ1bmN0aW9uKGNvbmZpZykge1xuICAgIGFuZ3VsYXIuZXh0ZW5kKGZsaW5rQ29uZmlnLCBjb25maWcpO1xuICAgIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCk7XG4gICAgcmV0dXJuICRpbnRlcnZhbChmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBKb2JzU2VydmljZS5saXN0Sm9icygpO1xuICAgIH0sIGZsaW5rQ29uZmlnW1wicmVmcmVzaC1pbnRlcnZhbFwiXSk7XG4gIH0pO1xufSkuY29uZmlnKGZ1bmN0aW9uKCR1aVZpZXdTY3JvbGxQcm92aWRlcikge1xuICByZXR1cm4gJHVpVmlld1Njcm9sbFByb3ZpZGVyLnVzZUFuY2hvclNjcm9sbCgpO1xufSkuY29uZmlnKGZ1bmN0
aW9uKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIHtcbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUoXCJvdmVydmlld1wiLCB7XG4gICAgdXJsOiBcIi9vdmVydmlld1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwicnVubmluZy1qb2JzXCIsIHtcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwiY29tcGxldGVkLWpvYnNcIiwge1xuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iXCIsIHtcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwiLFxuICAgIGFic3RyYWN0OiB0c
nVlLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhblwiLCB7XG4gICAgdXJsOiBcIlwiLFxuICAgIGFic3RyYWN0OiB0cnVlLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhbi5vdmVydmlld1wiLCB7XG4gICAgdXJsOiBcIlwiLFxuICAgIHZpZXdzOiB7XG4gICAgICAnbm9kZS1kZXRhaWxzJzoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUtbGlzdC5vdmVydmlldy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuT3ZlcnZpZXdDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnBsYW4uYWNjdW11bGF0b3JzXCIsIHtcbiAgICB1cmw6IFwiL2FjY3VtdWxhdG9yc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICAnbm9kZS1kZXRhaWxzJzoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2
JzL2pvYi5wbGFuLm5vZGUtbGlzdC5hY2N1bXVsYXRvcnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkFjY3VtdWxhdG9yc0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IudGltZWxpbmVcIiwge1xuICAgIHVybDogXCIvdGltZWxpbmVcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIiwge1xuICAgIHVybDogXCIve3ZlcnRleElkfVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICB2ZXJ0ZXg6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUudmVydGV4Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5zdGF0aXN0aWNzXCIsIHtcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5zdGF0aXN0aWNzLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIiwge1xuICAgIHVybDogXCI
vZXhjZXB0aW9uc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmV4Y2VwdGlvbnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iRXhjZXB0aW9uc0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucHJvcGVydGllc1wiLCB7XG4gICAgdXJsOiBcIi9wcm9wZXJ0aWVzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucHJvcGVydGllcy5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQcm9wZXJ0aWVzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5jb25maWdcIiwge1xuICAgIHVybDogXCIvY29uZmlnXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuY29uZmlnLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIHJldHVybiAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlKFwiL292ZXJ2aWV3XCIpO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5P
VElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tL
S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2JzTGFiZWwnLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHRyYW5zY2x1ZGU6IHRydWVcbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6IFwiQFwiXG5cbiAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknPjxuZy10cmFuc2NsdWRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCJcbiAgXG4gIGxpbms6IChzY29wZSwgZWxlbWVudCwgYXR0cnMpIC0+XG4gICAgc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IC0+XG4gICAgICAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnaW5kaWNhdG9yUHJpbWFyeScsIChKb2JzU2VydmljZSkgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6ICdAJ1xuXG4gIHRlbXBsYXRlOiBcIjxpIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJyAvPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2ZhIGZhLWNpcmNsZSBpbm
RpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3RhYmxlUHJvcGVydHknLCAtPlxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOlxuICAgIHZhbHVlOiAnPSdcblxuICB0ZW1wbGF0ZTogXCI8dGQgdGl0bGU9XFxcInt7dmFsdWUgfHwgJ05vbmUnfX1cXFwiPnt7dmFsdWUgfHwgJ05vbmUnfX08L3RkPlwiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ2JzTGFiZWwnLCBmdW5jdGlvbihKb2JzU2VydmljZSkge1xuICByZXR1cm4ge1xuICAgIHRyYW5zY2x1ZGU6IHRydWUsXG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6IFwiQFwiXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8c3BhbiB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKSc+PG5nLXRyYW5zY2x1ZGU+PC9uZy10cmFuc2NsdWRlPjwvc3Bhbj5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3R
hdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdpbmRpY2F0b3JQcmltYXJ5JywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICBnZXRMYWJlbENsYXNzOiBcIiZcIixcbiAgICAgIHN0YXR1czogJ0AnXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8aSB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnZmEgZmEtY2lyY2xlIGluZGljYXRvciBpbmRpY2F0b3ItJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0YWJsZVByb3BlcnR5JywgZnVuY3Rpb24oKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgdmFsdWU6ICc9J1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRh
dGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlc
iB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmZpbHRlciBcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCAoYW5ndWxhck1vbWVudENvbmZpZykgLT5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSAtPlxuICAgIHJldHVybiBcIlwiICBpZiB0eXBlb2YgdmFsdWUgaXMgXCJ1bmRlZmluZWRcIiBvciB2YWx1ZSBpcyBudWxsXG5cbiAgICBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7IHRyaW06IGZhbHNlIH0pXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyXG5cbi5maWx0ZXIgXCJodW1hbml6ZVRleHRcIiwgLT5cbiAgKHRleHQpIC0+XG4gICAgIyBUT0RPOiBleHRlbmQuLi4gYSBsb3RcbiAgICBpZiB0ZXh0IHRoZW4gdGV4dC5yZXBsYWNlKC8mZ3Q7L2csIFwiPlwiKS5yZXBsYWNlKC88YnJcXC8+L2csXCJcIikgZWxzZSAnJ1xuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZmlsdGVyKFwiYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkXCIsIGZ1bmN0aW9uKGFuZ3VsYXJNb21lbnRDb25maWcpIHtcbiAgdmFyIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlcjtcbiAgYW
1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gZnVuY3Rpb24odmFsdWUsIGZvcm1hdCwgZHVyYXRpb25Gb3JtYXQpIHtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcInVuZGVmaW5lZFwiIHx8IHZhbHVlID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gXCJcIjtcbiAgICB9XG4gICAgcmV0dXJuIG1vbWVudC5kdXJhdGlvbih2YWx1ZSwgZm9ybWF0KS5mb3JtYXQoZHVyYXRpb25Gb3JtYXQsIHtcbiAgICAgIHRyaW06IGZhbHNlXG4gICAgfSk7XG4gIH07XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlci4kc3RhdGVmdWwgPSBhbmd1bGFyTW9tZW50Q29uZmlnLnN0YXRlZnVsRmlsdGVycztcbiAgcmV0dXJuIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlcjtcbn0pLmZpbHRlcihcImh1bWFuaXplVGV4dFwiLCBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uKHRleHQpIHtcbiAgICBpZiAodGV4dCkge1xuICAgICAgcmV0dXJuIHRleHQucmVwbGFjZSgvJmd0Oy9nLCBcIj5cIikucmVwbGFjZSgvPGJyXFwvPi9nLCBcIlwiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuICcnO1xuICAgIH1cbiAgfTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHR
oaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5zZXJ2aWNlICdNYWluU2VydmljZScsICgkaHR0cCwgZmxpbmtDb25maWcsICRxKSAtPlxuICBA
bG9hZENvbmZpZyA9IC0+XG4gICAgZGVmZXJyZWQgPSAkcS5kZWZlcigpXG5cbiAgICAkaHR0cC5nZXQgZmxpbmtDb25maWcuam9iU2VydmVyICsgXCIvY29uZmlnXCJcbiAgICAuc3VjY2VzcyAoZGF0YSwgc3RhdHVzLCBoZWFkZXJzLCBjb25maWcpIC0+XG4gICAgICBkZWZlcnJlZC5yZXNvbHZlKGRhdGEpXG5cbiAgICBkZWZlcnJlZC5wcm9taXNlXG5cblxuICBAXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5zZXJ2aWNlKCdNYWluU2VydmljZScsIGZ1bmN0aW9uKCRodHRwLCBmbGlua0NvbmZpZywgJHEpIHtcbiAgdGhpcy5sb2FkQ29uZmlnID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGRlZmVycmVkO1xuICAgIGRlZmVycmVkID0gJHEuZGVmZXIoKTtcbiAgICAkaHR0cC5nZXQoZmxpbmtDb25maWcuam9iU2VydmVyICsgXCIvY29uZmlnXCIpLnN1Y2Nlc3MoZnVuY3Rpb24oZGF0YSwgc3RhdHVzLCBoZWFkZXJzLCBjb25maWcpIHtcbiAgICAgIHJldHVybiBkZWZlcnJlZC5yZXNvbHZlKGRhdGEpO1xuICAgIH0pO1xuICAgIHJldHVybiBkZWZlcnJlZC5wcm9taXNlO1xuICB9O1xuICByZXR1cm4gdGhpcztcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb
3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5jb250cm9sbGVyICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcy
wgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdmaW5pc2hlZCcpXG5cbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuXG4gICRzY29wZS5qb2JPYnNlcnZlcigpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ1NpbmdsZUpvYkNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcyw
gSm9ic1NlcnZpY2UsICRyb290U2NvcGUsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIC0+XG4gIGNvbnNvbGUubG9nICdTaW5nbGVKb2JDb250cm9sbGVyJ1xuXG4gICRzY29wZS5qb2JpZCA9ICRzdGF0ZVBhcmFtcy5qb2JpZFxuICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcbiAgJHJvb3RTY29wZS5wbGFuID0gbnVsbFxuXG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuIChkYXRhKSAtPlxuICAgICRyb290U2NvcGUuam9iID0gZGF0YVxuICAgICRyb290U2NvcGUucGxhbiA9IGRhdGEucGxhblxuXG4gIHJlZnJlc2hlciA9ICRpbnRlcnZhbCAtPlxuICAgIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuIChkYXRhKSAtPlxuICAgICAgJHJvb3RTY29wZS5qb2IgPSBkYXRhXG4gICAgICAjICRyb290U2NvcGUucGxhbiA9IGRhdGEucGxhblxuXG4gICAgICAkc2NvcGUuJGJyb2FkY2FzdCAncmVsb2FkJ1xuXG4gICwgZmxpbmtDb25maWdbXCJyZWZyZXNoLWludGVydmFsXCJdXG5cbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgICRyb290U2NvcGUuam9iID0gbnVsbFxuICAgICRyb290U2NvcGUucGxhbiA9IG51bGxcblxuICAgICRpbnRlcnZhbC5jYW5jZWwocmVmcmVzaGVyKVxuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Db250cm9sbGVyJywgKCRzY29wZSwgJHN0
YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iUGxhbkNvbnRyb2xsZXInXG5cbiAgJHNjb3BlLm5vZGVpZCA9IG51bGxcbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpXG5cbiAgJHNjb3BlLmNoYW5nZU5vZGUgPSAobm9kZWlkKSAtPlxuICAgIGlmIG5vZGVpZCAhPSAkc2NvcGUubm9kZWlkXG4gICAgICAkc2NvcGUubm9kZWlkID0gbm9kZWlkXG4gICAgICAkc2NvcGUudmVydGV4ID0gbnVsbFxuXG4gICAgICAkc2NvcGUuJGJyb2FkY2FzdCAncmVsb2FkJ1xuXG4gICAgZWxzZVxuICAgICAgJHNjb3BlLm5vZGVpZCA9IG51bGxcbiAgICAgICRzY29wZS52ZXJ0ZXggPSBudWxsXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5PdmVydmlld0NvbnRyb2xsZXInLCAoJHNjb3BlLCBKb2JzU2VydmljZSkgLT5cbiAgY29uc29sZS5sb2cgJ0pvYlBsYW5PdmVydmlld0NvbnRyb2xsZXInXG5cbiAgaWYgJHNjb3BlLm5vZGVpZCBhbmQgKCEkc2NvcGUudmVydGV4IG9yICEkc2NvcGUudmVydGV4LnN0KVxuICAgIEpvYnNTZXJ2aWNlLmdldFN1YnRhc2tzKCRzY29wZS5ub2RlaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgICAkc2NvcGUudmVydGV4ID0gZGF0YVxuXG4gICRzY29wZS4kb24gJ3JlbG9hZCcsIChldmVudCkgLT5cbiAgICBjb25zb2xlLmxvZyAnSm9iUGxhbk92ZXJ2a
WV3Q29udHJvbGxlcidcbiAgICBpZiAkc2NvcGUubm9kZWlkXG4gICAgICBKb2JzU2VydmljZS5nZXRTdWJ0YXNrcygkc2NvcGUubm9kZWlkKS50aGVuIChkYXRhKSAtPlxuICAgICAgICAkc2NvcGUudmVydGV4ID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JQbGFuQWNjdW11bGF0b3JzQ29udHJvbGxlcicsICgkc2NvcGUsIEpvYnNTZXJ2aWNlKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iUGxhbkFjY3VtdWxhdG9yc0NvbnRyb2xsZXInXG5cbiAgaWYgJHNjb3BlLm5vZGVpZCBhbmQgKCEkc2NvcGUudmVydGV4IG9yICEkc2NvcGUudmVydGV4LmFjY3VtdWxhdG9ycylcbiAgICBKb2JzU2VydmljZS5nZXRBY2N1bXVsYXRvcnMoJHNjb3BlLm5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAgICRzY29wZS52ZXJ0ZXggPSBkYXRhXG5cbiAgJHNjb3BlLiRvbiAncmVsb2FkJywgKGV2ZW50KSAtPlxuICAgIGNvbnNvbGUubG9nICdKb2JQbGFuQWNjdW11bGF0b3JzQ29udHJvbGxlcidcbiAgICBpZiAkc2NvcGUubm9kZWlkXG4gICAgICBKb2JzU2VydmljZS5nZXRBY2N1bXVsYXRvcnMoJHNjb3BlLm5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAgICAgJHNjb3BlLnZlcnRleCA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJy
wgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBjb25zb2xlLmxvZyAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJ1xuXG4gIEpvYnNTZXJ2aWNlLmdldFZlcnRleCgkc3RhdGVQYXJhbXMudmVydGV4SWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLnZlcnRleCA9IGRhdGFcblxuICAkc2NvcGUuJG9uICdyZWxvYWQnLCAoZXZlbnQpIC0+XG4gICAgY29uc29sZS5sb2cgJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcbiAgICBKb2JzU2VydmljZS5nZXRWZXJ0ZXgoJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuIChkYXRhKSAtPlxuICAgICAgJHNjb3BlLnZlcnRleCA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iRXhjZXB0aW9uc0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmxvYWRFeGNlcHRpb25zKCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUuZXhjZXB0aW9ucyA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUHJvcGVydGllc0NvbnRyb2xsZXInLCAoJHNjb3BlLCBKb2JzU2VydmljZSkgLT5cbiAgY29uc29sZS5sb2cgJ0pvYlByb3BlcnRpZXNDb250cm9sbGVyJ1xuXG4gICRzY29wZS5jaGFuZ2VOb2RlID0
gKG5vZGVpZCkgLT5cbiAgICBpZiBub2RlaWQgIT0gJHNjb3BlLm5vZGVpZFxuICAgICAgJHNjb3BlLm5vZGVpZCA9IG5vZGVpZFxuXG4gICAgICBKb2JzU2VydmljZS5nZXROb2RlKG5vZGVpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAgICAgJHNjb3BlLm5vZGUgPSBkYXRhXG5cbiAgICBlbHNlXG4gICAgICAkc2NvcGUubm9kZWlkID0gbnVsbFxuICAgICAgJHNjb3BlLm5vZGUgPSBudWxsXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5jb250cm9sbGVyKCdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJyk7XG4gIH07XG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5qb2JP
YnNlcnZlciA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJyk7XG4gIH07XG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdTaW5nbGVKb2JDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRyb290U2NvcGUsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIHtcbiAgdmFyIHJlZnJlc2hlcjtcbiAgY29uc29sZS5sb2coJ1NpbmdsZUpvYkNvbnRyb2xsZXInKTtcbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkO1xuICAkcm9vdFNjb3BlLmpvYiA9IG51bGw7XG4gICRyb290U2NvcGUucGxhbiA9IG51bGw7XG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAkcm9vdFNjb3BlLmpvYiA9IGRhdGE7XG4gICAgcmV0dXJuICRyb290U2NvcGUucGxhbiA9IGRhdGEucGxhbjtcbiAgfSk7XG4gIHJlZnJlc2hlciA9ICRpbnRlcnZhbChmdW5jdGlvbigpIHtcbiAgICByZXR1c
m4gSm9ic1NlcnZpY2UubG9hZEpvYigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICAgJHJvb3RTY29wZS5qb2IgPSBkYXRhO1xuICAgICAgcmV0dXJuICRzY29wZS4kYnJvYWRjYXN0KCdyZWxvYWQnKTtcbiAgICB9KTtcbiAgfSwgZmxpbmtDb25maWdbXCJyZWZyZXNoLWludGVydmFsXCJdKTtcbiAgcmV0dXJuICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgJHJvb3RTY29wZS5qb2IgPSBudWxsO1xuICAgICRyb290U2NvcGUucGxhbiA9IG51bGw7XG4gICAgcmV0dXJuICRpbnRlcnZhbC5jYW5jZWwocmVmcmVzaGVyKTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIGNvbnNvbGUubG9nKCdKb2JQbGFuQ29udHJvbGxlcicpO1xuICAkc2NvcGUubm9kZWlkID0gbnVsbDtcbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpO1xuICByZXR1cm4gJHNjb3BlLmNoYW5nZU5vZGUgPSBmdW5jdGlvbihub2RlaWQpIHtcbiAgICBpZiAobm9kZWlkICE9PSAkc2NvcGUubm9kZWlkKSB7XG4gICAgICAkc2NvcGUubm9kZWlkID0gbm9kZWlkO1xuICAgICAgJHNjb3BlLnZlcnRleCA9IG51bGw7XG4gICAgICByZXR1cm4gJHNjb3BlLiRicm9hZGNhc3QoJ3JlbG9hZCcpO1xuICAgIH0gZWxzZSB7XG4gICAgIC
Akc2NvcGUubm9kZWlkID0gbnVsbDtcbiAgICAgIHJldHVybiAkc2NvcGUudmVydGV4ID0gbnVsbDtcbiAgICB9XG4gIH07XG59KS5jb250cm9sbGVyKCdKb2JQbGFuT3ZlcnZpZXdDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCBKb2JzU2VydmljZSkge1xuICBjb25zb2xlLmxvZygnSm9iUGxhbk92ZXJ2aWV3Q29udHJvbGxlcicpO1xuICBpZiAoJHNjb3BlLm5vZGVpZCAmJiAoISRzY29wZS52ZXJ0ZXggfHwgISRzY29wZS52ZXJ0ZXguc3QpKSB7XG4gICAgSm9ic1NlcnZpY2UuZ2V0U3VidGFza3MoJHNjb3BlLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgICByZXR1cm4gJHNjb3BlLnZlcnRleCA9IGRhdGE7XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuICRzY29wZS4kb24oJ3JlbG9hZCcsIGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgY29uc29sZS5sb2coJ0pvYlBsYW5PdmVydmlld0NvbnRyb2xsZXInKTtcbiAgICBpZiAoJHNjb3BlLm5vZGVpZCkge1xuICAgICAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldFN1YnRhc2tzKCRzY29wZS5ub2RlaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICByZXR1cm4gJHNjb3BlLnZlcnRleCA9IGRhdGE7XG4gICAgICB9KTtcbiAgICB9XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUGxhbkFjY3VtdWxhdG9yc0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsIEpvYnNTZXJ2aWNlKSB7XG4gIGNvbnNvbGUubG9nKCdKb2JQbGFuQWNjdW11bGF0b3J
zQ29udHJvbGxlcicpO1xuICBpZiAoJHNjb3BlLm5vZGVpZCAmJiAoISRzY29wZS52ZXJ0ZXggfHwgISRzY29wZS52ZXJ0ZXguYWNjdW11bGF0b3JzKSkge1xuICAgIEpvYnNTZXJ2aWNlLmdldEFjY3VtdWxhdG9ycygkc2NvcGUubm9kZWlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgIHJldHVybiAkc2NvcGUudmVydGV4ID0gZGF0YTtcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gJHNjb3BlLiRvbigncmVsb2FkJywgZnVuY3Rpb24oZXZlbnQpIHtcbiAgICBjb25zb2xlLmxvZygnSm9iUGxhbkFjY3VtdWxhdG9yc0NvbnRyb2xsZXInKTtcbiAgICBpZiAoJHNjb3BlLm5vZGVpZCkge1xuICAgICAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldEFjY3VtdWxhdG9ycygkc2NvcGUubm9kZWlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgcmV0dXJuICRzY29wZS52ZXJ0ZXggPSBkYXRhO1xuICAgICAgfSk7XG4gICAgfVxuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIGNvbnNvbGUubG9nKCdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInKTtcbiAgSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy52ZXJ0ZXhJZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS52ZXJ0ZXggPSBkYXRhO1xuICB9KTtcbiAgcmV0dXJuICRz
Y29wZS4kb24oJ3JlbG9hZCcsIGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgY29uc29sZS5sb2coJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicpO1xuICAgIHJldHVybiBKb2JzU2VydmljZS5nZXRWZXJ0ZXgoJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgIHJldHVybiAkc2NvcGUudmVydGV4ID0gZGF0YTtcbiAgICB9KTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JFeGNlcHRpb25zQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiBKb2JzU2VydmljZS5sb2FkRXhjZXB0aW9ucygpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkc2NvcGUuZXhjZXB0aW9ucyA9IGRhdGE7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUHJvcGVydGllc0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsIEpvYnNTZXJ2aWNlKSB7XG4gIGNvbnNvbGUubG9nKCdKb2JQcm9wZXJ0aWVzQ29udHJvbGxlcicpO1xuICByZXR1cm4gJHNjb3BlLmNoYW5nZU5vZGUgPSBmdW5jdGlvbihub2RlaWQpIHtcbiAgICBpZiAobm9kZWlkICE9PSAkc2NvcGUubm9kZWlkKSB7XG4gICAgICAkc2NvcGUubm9kZWlkID0gbm9kZWlkO1xuICAgICAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldE5vZGUobm9kZWlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgcmV0dXJuICRzY29wZS5ub2RlID0gZ
GF0YTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAkc2NvcGUubm9kZWlkID0gbnVsbDtcbiAgICAgIHJldHVybiAkc2NvcGUubm9kZSA9IG51bGw7XG4gICAgfVxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1
VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3ZlcnRleCcsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCJcblxuICBzY29wZTpcbiAgICBkYXRhOiBcIj1cIlxuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF1cblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVylcblxuICAgIGFuYWx5emVUaW1lID0gKGRhdGEpIC0+XG4gICAgICBkMy5zZWxlY3Qoc3ZnRWwpLnNlbGVjdEFsbChcIipcIikucmVtb3ZlKClcblxuICAgICAgdGVzdERhdGEgPSBbXVxuXG4gICAgICBhbmd1bGFyLmZvckVhY2ggZGF0YS5zdWJ0YXNrcywgKHN1YnRhc2ssIGkpIC0+XG4gICAgICAgIGNvbnNvbGUubG9nIGRhdGEuc3VidGFza3N
cblxuICAgICAgICB0aW1lcyA9IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIlxuICAgICAgICAgICAgY29sb3I6IFwiIzY2NlwiXG4gICAgICAgICAgICBib3JkZXJDb2xvcjogXCIjNTU1XCJcbiAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIlNDSEVEVUxFRFwiXVxuICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHN1YnRhc2sudGltZXN0YW1wc1tcIkRFUExPWUlOR1wiXVxuICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgfVxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiBcIkRlcGxveWluZ1wiXG4gICAgICAgICAgICBjb2xvcjogXCIjYWFhXCJcbiAgICAgICAgICAgIGJvcmRlckNvbG9yOiBcIiM1NTVcIlxuICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiREVQTE9ZSU5HXCJdXG4gICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiUlVOTklOR1wiXVxuICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgfVxuICAgICAgICBdXG5cbiAgICAgICAgaWYgc3VidGFzay50aW1lc3RhbXBzW1wiRklOSVNIRURcIl0gPiAwXG4gICAgICAgICAgdGltZXMucHVzaCB7XG4gICAgICAgICAgICBsYWJlbDogXCJSdW5uaW5nXCJcbiAgICAgICAgICAgIGNvbG9yOiBcIiNkZGRcIlxuICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1
NVwiXG4gICAgICAgICAgICBzdGFydGluZ190aW1lOiBzdWJ0YXNrLnRpbWVzdGFtcHNbXCJSVU5OSU5HXCJdXG4gICAgICAgICAgICBlbmRpbmdfdGltZTogc3VidGFzay50aW1lc3RhbXBzW1wiRklOSVNIRURcIl1cbiAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgIH1cblxuICAgICAgICB0ZXN0RGF0YS5wdXNoIHtcbiAgICAgICAgICBsYWJlbDogXCIje3N1YnRhc2suaG9zdH0gKCN7c3VidGFzay5zdWJ0YXNrfSlcIlxuICAgICAgICAgIHRpbWVzOiB0aW1lc1xuICAgICAgICB9XG5cbiAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpXG4gICAgICAudGlja0Zvcm1hdCh7XG4gICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKVxuICAgICAgICAjIHRpY2tJbnRlcnZhbDogMVxuICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJzaW5nbGVcIilcbiAgICAgIC5sYWJlbEZvcm1hdCgobGFiZWwpIC0+XG4gICAgICAgIGxhYmVsXG4gICAgICApXG4gICAgICAubWFyZ2luKHsgbGVmdDogMTAwLCByaWdodDogMCwgdG9wOiAwLCBib3R0b206IDAgfSlcbiAgICAgIC5pdGVtSGVpZ2h0KDMwKVxuICAgICAgLnJlbGF0aXZlVGltZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgYW5hbHl6ZVRpbWUoc2NvcGUuZGF0YSlcblxuICAgIHJld
HVyblxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAndGltZWxpbmUnLCAoJHN0YXRlKSAtPlxuICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCJcblxuICBzY29wZTpcbiAgICBqb2I6IFwiPVwiXG5cbiAgbGluazogKHNjb3BlLCBlbGVtLCBhdHRycykgLT5cbiAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXKVxuXG4gICAgdHJhbnNsYXRlTGFiZWwgPSAobGFiZWwpIC0+XG4gICAgICBsYWJlbC5yZXBsYWNlKFwiJmd0O1wiLCBcIj5cIilcblxuICAgIGFuYWx5emVUaW1lID0gKGRhdGEpIC0+XG4gICAgICBkMy5zZWxlY3Qoc3ZnRWwpLnNlbGVjdEFsbChcIipcIikucmVtb3ZlKClcblxuICAgICAgdGVzdERhdGEgPSBbXVxuXG4gICAgICB0ZXN0RGF0YS5wdXNoIFxuICAgICAgICB0aW1lczogW1xuICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiXG4gICAgICAgICAgY29sb3I6IFwiI2NjY2NjY1wiXG4gICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzU1NVwiXG4gICAgICAgICAgc3RhcnRpbmdfdGltZTogZGF0YS50aW1lc3RhbXBzW1wiQ1JFQVRFRFwiXVxuICAgICAgICAgIGVuZGluZ190aW1lOiBkYXRhLnRpbWVzdG
FtcHNbXCJDUkVBVEVEXCJdICsgMVxuICAgICAgICAgIHR5cGU6ICdzY2hlZHVsZWQnXG4gICAgICAgIF1cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEudmVydGljZXMsICh2ZXJ0ZXgpIC0+XG4gICAgICAgIGlmIHZlcnRleFsnc3RhcnQtdGltZSddID4gLTFcbiAgICAgICAgICB0ZXN0RGF0YS5wdXNoIFxuICAgICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgICAgbGFiZWw6IHRyYW5zbGF0ZUxhYmVsKHZlcnRleC5uYW1lKVxuICAgICAgICAgICAgICBjb2xvcjogXCIjZDlmMWY3XCJcbiAgICAgICAgICAgICAgYm9yZGVyQ29sb3I6IFwiIzYyY2RlYVwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZlcnRleFsnc3RhcnQtdGltZSddXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2ZXJ0ZXhbJ2VuZC10aW1lJ11cbiAgICAgICAgICAgICAgbGluazogdmVydGV4LmlkXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgXVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5jbGljaygoZCwgaSwgZGF0dW0pIC0+XG4gICAgICAgIGlmIGQubGlua1xuICAgICAgICAgICRzdGF0ZS5nbyBcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHsgam9iaWQ6IGRhdGEuamlkLCB2ZXJ0ZXhJZDogZC5saW5rIH1cblxuICAgICAgKVxuICAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICA
gICAgIyB0aWNrVGltZTogZDMudGltZS5zZWNvbmRcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDAuNVxuICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJtYWluXCIpXG4gICAgICAubWFyZ2luKHsgbGVmdDogMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgzMClcbiAgICAgIC5zaG93Qm9yZGVyTGluZSgpXG4gICAgICAuc2hvd0hvdXJUaW1lbGluZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgc2NvcGUuJHdhdGNoIGF0dHJzLmpvYiwgKGRhdGEpIC0+XG4gICAgICBhbmFseXplVGltZShkYXRhKSBpZiBkYXRhXG5cbiAgICByZXR1cm5cblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2pvYlBsYW4nLCAoJHRpbWVvdXQpIC0+XG4gIHRlbXBsYXRlOiBcIlxuICAgIDxzdmcgY2xhc3M9J2dyYXBoJyB3aWR0aD0nNTAwJyBoZWlnaHQ9JzQwMCc+PGcgLz48L3N2Zz5cbiAgICA8c3ZnIGNsYXNzPSd0bXAnIHdpZHRoPScxJyBoZWlnaHQ9JzEnPjxnIC8+PC9zdmc+XG4gICAgPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+XG4gICAgICA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20taW4nIG5nLWNsaWNrPSd6b29tSW4oKSc+PGkgY2xhc3M9
J2ZhIGZhLXBsdXMnIC8+PC9hPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLW91dCcgbmctY2xpY2s9J3pvb21PdXQoKSc+PGkgY2xhc3M9J2ZhIGZhLW1pbnVzJyAvPjwvYT5cbiAgICA8L2Rpdj5cIlxuXG4gIHNjb3BlOlxuICAgIHBsYW46ICc9J1xuICAgIHNldE5vZGU6ICcmJ1xuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgbWFpblpvb20gPSBkMy5iZWhhdmlvci56b29tKClcbiAgICBzdWJncmFwaHMgPSBbXVxuICAgIGpvYmlkID0gYXR0cnMuam9iaWRcblxuICAgIG1haW5TdmdFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzBdXG4gICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXVxuICAgIG1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdXG5cbiAgICBkM21haW5TdmcgPSBkMy5zZWxlY3QobWFpblN2Z0VsZW1lbnQpXG4gICAgZDNtYWluU3ZnRyA9IGQzLnNlbGVjdChtYWluRylcbiAgICBkM3RtcFN2ZyA9IGQzLnNlbGVjdChtYWluVG1wRWxlbWVudClcblxuICAgICMgYW5ndWxhci5lbGVtZW50KG1haW5HKS5lbXB0eSgpXG4gICAgIyBkM21haW5TdmdHLnNlbGVjdEFsbChcIipcIikucmVtb3ZlKClcblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoZWxlbS5jaGlsZHJlbigpWzBdKS53aWR0aChjb250YWluZXJXKVxuXG4gICAgc2NvcGUuem9vbUluID0gLT5cbiAgICAgIGlmI
G1haW5ab29tLnNjYWxlKCkgPCAyLjk5XG4gICAgICAgIFxuICAgICAgICAjIENhbGN1bGF0ZSBhbmQgc3RvcmUgbmV3IHZhbHVlcyBpbiB6b29tIG9iamVjdFxuICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKVxuICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICBtYWluWm9vbS5zY2FsZSBtYWluWm9vbS5zY2FsZSgpICsgMC4xXG4gICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZSBbIHYxLCB2MiBdXG4gICAgICAgIFxuICAgICAgICAjIFRyYW5zZm9ybSBzdmdcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiXG5cbiAgICBzY29wZS56b29tT3V0ID0gLT5cbiAgICAgIGlmIG1haW5ab29tLnNjYWxlKCkgPiAwLjMxXG4gICAgICAgIFxuICAgICAgICAjIENhbGN1bGF0ZSBhbmQgc3RvcmUgbmV3IHZhbHVlcyBpbiBtYWluWm9vbSBvYmplY3RcbiAgICAgICAgbWFpblpvb20uc2NhbGUgbWFpblpvb20uc2NhbGUoKSAtIDAuMVxuICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKVxuICAgICAgIC
B2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKVxuICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUgWyB2MSwgdjIgXVxuICAgICAgICBcbiAgICAgICAgIyBUcmFuc2Zvcm0gc3ZnXG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIlxuXG4gICAgI2NyZWF0ZSBhIGxhYmVsIG9mIGFuIGVkZ2VcbiAgICBjcmVhdGVMYWJlbEVkZ2UgPSAoZWwpIC0+XG4gICAgICBsYWJlbFZhbHVlID0gXCJcIlxuICAgICAgaWYgZWwuc2hpcF9zdHJhdGVneT8gb3IgZWwubG9jYWxfc3RyYXRlZ3k/XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8ZGl2IGNsYXNzPSdlZGdlLWxhYmVsJz5cIlxuICAgICAgICBsYWJlbFZhbHVlICs9IGVsLnNoaXBfc3RyYXRlZ3kgIGlmIGVsLnNoaXBfc3RyYXRlZ3k/XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCIgKFwiICsgZWwudGVtcF9tb2RlICsgXCIpXCIgIHVubGVzcyBlbC50ZW1wX21vZGUgaXMgYHVuZGVmaW5lZGBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiw8YnI+XCIgKyBlbC5sb2NhbF9zdHJhdGVneSAgdW5sZXNzIGVsLmxvY2FsX3N0cmF
0ZWd5IGlzIGB1bmRlZmluZWRgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2Rpdj5cIlxuICAgICAgbGFiZWxWYWx1ZVxuXG5cbiAgICAjIHRydWUsIGlmIHRoZSBub2RlIGlzIGEgc3BlY2lhbCBub2RlIGZyb20gYW4gaXRlcmF0aW9uXG4gICAgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZSA9IChpbmZvKSAtPlxuICAgICAgKGluZm8gaXMgXCJwYXJ0aWFsU29sdXRpb25cIiBvciBpbmZvIGlzIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiIG9yIGluZm8gaXMgXCJ3b3Jrc2V0XCIgb3IgaW5mbyBpcyBcIm5leHRXb3Jrc2V0XCIgb3IgaW5mbyBpcyBcInNvbHV0aW9uU2V0XCIgb3IgaW5mbyBpcyBcInNvbHV0aW9uRGVsdGFcIilcblxuICAgIGdldE5vZGVUeXBlID0gKGVsLCBpbmZvKSAtPlxuICAgICAgaWYgaW5mbyBpcyBcIm1pcnJvclwiXG4gICAgICAgICdub2RlLW1pcnJvcidcblxuICAgICAgZWxzZSBpZiBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pXG4gICAgICAgICdub2RlLWl0ZXJhdGlvbidcblxuICAgICAgZWxzZVxuICAgICAgICAgICdub2RlLW5vcm1hbCdcbiAgICAgIFxuICAgICMgY3JlYXRlcyB0aGUgbGFiZWwgb2YgYSBub2RlLCBpbiBpbmZvIGlzIHN0b3JlZCwgd2hldGhlciBpdCBpcyBhIHNwZWNpYWwgbm9kZSAobGlrZSBhIG1pcnJvciBpbiBhbiBpdGVyYXRpb24pXG4gICAgY3JlYXRlTGFiZWxOb2RlID0gKGVsLCBpbmZvLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgIyBsYWJlbFZhbHVlID0g
XCI8YSBocmVmPScjL2pvYnMvXCIgKyBqb2JpZCArIFwiL3ZlcnRleC9cIiArIGVsLmlkICsgXCInIGNsYXNzPSdub2RlLWxhYmVsIFwiICsgZ2V0Tm9kZVR5cGUoZWwsIGluZm8pICsgXCInPlwiXG4gICAgICBsYWJlbFZhbHVlID0gXCI8ZGl2IGhyZWY9JyMvam9icy9cIiArIGpvYmlkICsgXCIvdmVydGV4L1wiICsgZWwuaWQgKyBcIicgY2xhc3M9J25vZGUtbGFiZWwgXCIgKyBnZXROb2RlVHlwZShlbCwgaW5mbykgKyBcIic+XCJcblxuICAgICAgIyBOb2RlbmFtZVxuICAgICAgaWYgaW5mbyBpcyBcIm1pcnJvclwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+TWlycm9yIG9mIFwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCJcbiAgICAgIGVsc2VcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5cIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiXG4gICAgICBpZiBlbC5kZXNjcmlwdGlvbiBpcyBcIlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCJcIlxuICAgICAgZWxzZVxuICAgICAgICBzdGVwTmFtZSA9IGVsLmRlc2NyaXB0aW9uXG4gICAgICAgIFxuICAgICAgICAjIGNsZWFuIHN0ZXBOYW1lXG4gICAgICAgIHN0ZXBOYW1lID0gc2hvcnRlblN0cmluZyhzdGVwTmFtZSlcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNCBjbGFzcz0nc3RlcC1uYW1lJz5cIiArIHN0ZXBOYW1lICsgXCI8L2g0PlwiXG4gICAgICBcbiAgICAgICMgS
WYgdGhpcyBub2RlIGlzIGFuIFwiaXRlcmF0aW9uXCIgd2UgbmVlZCBhIGRpZmZlcmVudCBwYW5lbC1ib2R5XG4gICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICBsYWJlbFZhbHVlICs9IGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbihlbC5pZCwgbWF4VywgbWF4SClcbiAgICAgIGVsc2VcbiAgICAgICAgXG4gICAgICAgICMgT3RoZXJ3aXNlIGFkZCBpbmZvcyAgICBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5cIiArIGluZm8gKyBcIiBOb2RlPC9oNT5cIiAgaWYgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlBhcmFsbGVsaXNtOiBcIiArIGVsLnBhcmFsbGVsaXNtICsgXCI8L2g1PlwiICB1bmxlc3MgZWwucGFyYWxsZWxpc20gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1Pk9wZXJhdGlvbjogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLm9wZXJhdG9yX3N0cmF0ZWd5KSArIFwiPC9oNT5cIiAgdW5sZXNzIGVsLm9wZXJhdG9yIGlzIGB1bmRlZmluZWRgXG4gICAgICBcbiAgICAgICMgbGFiZWxWYWx1ZSArPSBcIjwvYT5cIlxuICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiXG4gICAgICBsYWJlbFZhbHVlXG5cbiAgICAjIEV4dGVuZHMgdGhlIGxhYmVsIG9mIGEgbm9kZSB3aXRoIGFuIGFkZGl0aW9uYWwgc3ZnIEVsZW1lbnQgdG8gcHJlc2VudCB0aGUgaXRlcmF0aW9uLlxuICAgIGV4dGVuZExhYmVsTm9kZUZvck
l0ZXJhdGlvbiA9IChpZCwgbWF4VywgbWF4SCkgLT5cbiAgICAgIHN2Z0lEID0gXCJzdmctXCIgKyBpZFxuXG4gICAgICBsYWJlbFZhbHVlID0gXCI8c3ZnIGNsYXNzPSdcIiArIHN2Z0lEICsgXCInIHdpZHRoPVwiICsgbWF4VyArIFwiIGhlaWdodD1cIiArIG1heEggKyBcIj48ZyAvPjwvc3ZnPlwiXG4gICAgICBsYWJlbFZhbHVlXG5cbiAgICAjIFNwbGl0IGEgc3RyaW5nIGludG8gbXVsdGlwbGUgbGluZXMgc28gdGhhdCBlYWNoIGxpbmUgaGFzIGxlc3MgdGhhbiAzMCBsZXR0ZXJzLlxuICAgIHNob3J0ZW5TdHJpbmcgPSAocykgLT5cbiAgICAgICMgbWFrZSBzdXJlIHRoYXQgbmFtZSBkb2VzIG5vdCBjb250YWluIGEgPCAoYmVjYXVzZSBvZiBodG1sKVxuICAgICAgaWYgcy5jaGFyQXQoMCkgaXMgXCI8XCJcbiAgICAgICAgcyA9IHMucmVwbGFjZShcIjxcIiwgXCImbHQ7XCIpXG4gICAgICAgIHMgPSBzLnJlcGxhY2UoXCI+XCIsIFwiJmd0O1wiKVxuICAgICAgc2JyID0gXCJcIlxuICAgICAgd2hpbGUgcy5sZW5ndGggPiAzMFxuICAgICAgICBzYnIgPSBzYnIgKyBzLnN1YnN0cmluZygwLCAzMCkgKyBcIjxicj5cIlxuICAgICAgICBzID0gcy5zdWJzdHJpbmcoMzAsIHMubGVuZ3RoKVxuICAgICAgc2JyID0gc2JyICsgc1xuICAgICAgc2JyXG5cbiAgICBjcmVhdGVOb2RlID0gKGcsIGRhdGEsIGVsLCBpc1BhcmVudCA9IGZhbHNlLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgIyBjcmVhdGUgbm9kZSwgc2VuZCBhZGR
pdGlvbmFsIGluZm9ybWF0aW9ucyBhYm91dCB0aGUgbm9kZSBpZiBpdCBpcyBhIHNwZWNpYWwgb25lXG4gICAgICBpZiBlbC5pZCBpcyBkYXRhLnBhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5uZXh0X3BhcnRpYWxfc29sdXRpb25cbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLndvcmtzZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwid29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIndvcmtzZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5p
ZCBpcyBkYXRhLm5leHRfd29ya3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0V29ya3NldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIm5leHRXb3Jrc2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5zb2x1dGlvbl9zZXRcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25TZXRcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvblNldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEuc29sdXRpb25fZGVsdGFcbiAgICAgICAgZy5zZXROb2RlIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25EZWx0YVwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uRGVsdGFcIilcblxuICAgICAgZWxzZVxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJcIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlb
FR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJcIilcblxuICAgIGNyZWF0ZUVkZ2UgPSAoZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpIC0+XG4gICAgICB1bmxlc3MgZXhpc3RpbmdOb2Rlcy5pbmRleE9mKHByZWQuaWQpIGlzIC0xXG4gICAgICAgIGcuc2V0RWRnZSBwcmVkLmlkLCBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKHByZWQpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBhcnJvd2hlYWQ6ICdub3JtYWwnXG5cbiAgICAgIGVsc2VcbiAgICAgICAgbWlzc2luZ05vZGUgPSBzZWFyY2hGb3JOb2RlKGRhdGEsIHByZWQuaWQpXG4gICAgICAgIHVubGVzcyAhbWlzc2luZ05vZGUgb3IgbWlzc2luZ05vZGUuYWxyZWFkeUFkZGVkIGlzIHRydWVcbiAgICAgICAgICBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgPSB0cnVlXG4gICAgICAgICAgZy5zZXROb2RlIG1pc3NpbmdOb2RlLmlkLFxuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShtaXNzaW5nTm9kZSwgXCJtaXJyb3JcIilcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUobWlzc2luZ05vZGUsICdtaXJyb3InKVxuXG4gICAgICAgICAgZy5zZXRFZGdlIG1pc3NpbmdOb2RlLmlkLCBlbC5pZCxcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UobWlzc2luZ05vZG
UpXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuXG4gICAgbG9hZEpzb25Ub0RhZ3JlID0gKGcsIGRhdGEpIC0+XG4gICAgICBleGlzdGluZ05vZGVzID0gW11cblxuICAgICAgaWYgZGF0YS5ub2Rlcz9cbiAgICAgICAgIyBUaGlzIGlzIHRoZSBub3JtYWwganNvbiBkYXRhXG4gICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEubm9kZXNcblxuICAgICAgZWxzZVxuICAgICAgICAjIFRoaXMgaXMgYW4gaXRlcmF0aW9uLCB3ZSBub3cgc3RvcmUgc3BlY2lhbCBpdGVyYXRpb24gbm9kZXMgaWYgcG9zc2libGVcbiAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5zdGVwX2Z1bmN0aW9uXG4gICAgICAgIGlzUGFyZW50ID0gdHJ1ZVxuXG4gICAgICBmb3IgZWwgaW4gdG9JdGVyYXRlXG4gICAgICAgIG1heFcgPSAwXG4gICAgICAgIG1heEggPSAwXG5cbiAgICAgICAgaWYgZWwuc3RlcF9mdW5jdGlvblxuICAgICAgICAgIHNnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoeyBtdWx0aWdyYXBoOiB0cnVlLCBjb21wb3VuZDogdHJ1ZSB9KS5zZXRHcmFwaCh7XG4gICAgICAgICAgICBub2Rlc2VwOiAyMFxuICAgICAgICAgICAgZWRnZXNlcDogMFxuICAgICAgICAgICAgcmFua3NlcDogMjBcbiAgICAgICAgICAgIHJhbmtkaXI6IFwiTFJcIlxuICAgICAgICAgICAgbWFyZ2lueDogMTBcbiAgICAgICAgICAgIG1hcmdpbnk6IDEwXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgICAgc3ViZ3JhcGhzW2VsLmlkXSA9IHNnXG5
cbiAgICAgICAgICBsb2FkSnNvblRvRGFncmUoc2csIGVsKVxuXG4gICAgICAgICAgciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpXG4gICAgICAgICAgZDN0bXBTdmcuc2VsZWN0KCdnJykuY2FsbChyLCBzZylcbiAgICAgICAgICBtYXhXID0gc2cuZ3JhcGgoKS53aWR0aFxuICAgICAgICAgIG1heEggPSBzZy5ncmFwaCgpLmhlaWdodFxuXG4gICAgICAgICAgYW5ndWxhci5lbGVtZW50KG1haW5UbXBFbGVtZW50KS5lbXB0eSgpXG5cbiAgICAgICAgY3JlYXRlTm9kZShnLCBkYXRhLCBlbCwgaXNQYXJlbnQsIG1heFcsIG1heEgpXG5cbiAgICAgICAgZXhpc3RpbmdOb2Rlcy5wdXNoIGVsLmlkXG4gICAgICAgIFxuICAgICAgICAjIGNyZWF0ZSBlZGdlcyBmcm9tIGlucHV0cyB0byBjdXJyZW50IG5vZGVcbiAgICAgICAgaWYgZWwuaW5wdXRzP1xuICAgICAgICAgIGZvciBwcmVkIGluIGVsLmlucHV0c1xuICAgICAgICAgICAgY3JlYXRlRWRnZShnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZClcblxuICAgICAgZ1xuXG4gICAgIyBzZWFyY2hlcyBpbiB0aGUgZ2xvYmFsIEpTT05EYXRhIGZvciB0aGUgbm9kZSB3aXRoIHRoZSBnaXZlbiBpZFxuICAgIHNlYXJjaEZvck5vZGUgPSAoZGF0YSwgbm9kZUlEKSAtPlxuICAgICAgZm9yIGkgb2YgZGF0YS5ub2Rlc1xuICAgICAgICBlbCA9IGRhdGEubm9kZXNbaV1cbiAgICAgICAgcmV0dXJuIGVsICBpZiBlbC5pZCBpcyBub2RlSURcbiAgICAgICAgXG4gICAgICAgICMg
bG9vayBmb3Igbm9kZXMgdGhhdCBhcmUgaW4gaXRlcmF0aW9uc1xuICAgICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICAgIGZvciBqIG9mIGVsLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgICAgIHJldHVybiBlbC5zdGVwX2Z1bmN0aW9uW2pdICBpZiBlbC5zdGVwX2Z1bmN0aW9uW2pdLmlkIGlzIG5vZGVJRFxuXG4gICAgZHJhd0dyYXBoID0gKGRhdGEpIC0+XG4gICAgICAjIGNvbnNvbGUubG9nIGRhdGFcblxuICAgICAgZyA9IG5ldyBkYWdyZUQzLmdyYXBobGliLkdyYXBoKHsgbXVsdGlncmFwaDogdHJ1ZSwgY29tcG91bmQ6IHRydWUgfSkuc2V0R3JhcGgoe1xuICAgICAgICBub2Rlc2VwOiA3MFxuICAgICAgICBlZGdlc2VwOiAwXG4gICAgICAgIHJhbmtzZXA6IDUwXG4gICAgICAgIHJhbmtkaXI6IFwiTFJcIlxuICAgICAgICBtYXJnaW54OiA0MFxuICAgICAgICBtYXJnaW55OiA0MFxuICAgICAgICB9KVxuXG4gICAgICBsb2FkSnNvblRvRGFncmUoZywgZGF0YSlcblxuICAgICAgcmVuZGVyZXIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKVxuICAgICAgZDNtYWluU3ZnRy5jYWxsKHJlbmRlcmVyLCBnKVxuXG4gICAgICBmb3IgaSwgc2cgb2Ygc3ViZ3JhcGhzXG4gICAgICAgIGQzbWFpblN2Zy5zZWxlY3QoJ3N2Zy5zdmctJyArIGkgKyAnIGcnKS5jYWxsKHJlbmRlcmVyLCBzZylcblxuICAgICAgbmV3U2NhbGUgPSAwLjVcblxuICAgICAgeENlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZ
WxlbWVudChtYWluU3ZnRWxlbWVudCkud2lkdGgoKSAtIGcuZ3JhcGgoKS53aWR0aCAqIG5ld1NjYWxlKSAvIDIpXG4gICAgICB5Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS5oZWlnaHQoKSAtIGcuZ3JhcGgoKS5oZWlnaHQgKiBuZXdTY2FsZSkgLyAyKVxuXG4gICAgICBtYWluWm9vbS5zY2FsZShuZXdTY2FsZSkudHJhbnNsYXRlKFt4Q2VudGVyT2Zmc2V0LCB5Q2VudGVyT2Zmc2V0XSlcblxuICAgICAgZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgeENlbnRlck9mZnNldCArIFwiLCBcIiArIHlDZW50ZXJPZmZzZXQgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpXG5cbiAgICAgIG1haW5ab29tLm9uKFwiem9vbVwiLCAtPlxuICAgICAgICBldiA9IGQzLmV2ZW50XG4gICAgICAgIGQzbWFpblN2Z0cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIGV2LnRyYW5zbGF0ZSArIFwiKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIpXCJcbiAgICAgIClcbiAgICAgIG1haW5ab29tKGQzbWFpblN2ZylcblxuICAgICAgZDNtYWluU3ZnRy5zZWxlY3RBbGwoJy5ub2RlJykub24gJ2NsaWNrJywgKGQpIC0+XG4gICAgICAgIHNjb3BlLnNldE5vZGUoeyBub2RlaWQ6IGQgfSlcblxuICAgIHNjb3BlLiR3YXRjaCBhdHRycy5wbGFuLCAo
<TRUNCATED>
[43/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Show
plan (and optimizer properties) as a dedicated view
Posted by se...@apache.org.
[FLINK-2357] [web dashboard] Show plan (and optimizer properties) as a dedicated view
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/6a3b095e
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/6a3b095e
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/6a3b095e
Branch: refs/heads/master
Commit: 6a3b095ee686062af0933e94ded2b43f82014607
Parents: 46d8217
Author: Piotr Godek <pi...@gmail.com>
Authored: Tue Sep 1 16:18:35 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:52 2015 +0200
----------------------------------------------------------------------
.../app/partials/jobs/job.exceptions.jade | 2 +-
.../app/partials/jobs/job.plan.node-list.jade | 37 ++++++++++---
.../app/partials/jobs/job.plan.node-tabs.jade | 26 ++++++++-
.../partials/jobs/job.plan.node.properties.jade | 2 +-
.../partials/jobs/job.plan.node.subtasks.jade | 37 +++++++------
.../web-dashboard/app/scripts/index.coffee | 42 ++++-----------
.../app/scripts/modules/jobs/jobs.ctrl.coffee | 21 ++++++--
.../app/scripts/modules/jobs/jobs.svc.coffee | 16 ++++++
.../web-dashboard/app/styles/index.styl | 33 +++++++++++-
.../web-dashboard/app/styles/job.styl | 27 ++++++++--
.../web-dashboard/web/css/index.css | 55 ++++++++++++++++++--
flink-runtime-web/web-dashboard/web/js/index.js | 44 ++++++++++++++--
.../web/partials/jobs/job.exceptions.html | 2 +-
.../web/partials/jobs/job.plan.node-list.html | 26 ++++++---
.../web/partials/jobs/job.plan.node-nav.html | 0
.../web/partials/jobs/job.plan.node-tabs.html | 3 ++
.../partials/jobs/job.plan.node.properties.html | 2 +-
.../partials/jobs/job.plan.node.subtasks.html | 31 ++++++-----
pom.xml | 8 ++-
19 files changed, 311 insertions(+), 103 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
index 66606e6..09f5b36 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.exceptions.jade
@@ -15,7 +15,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-.panel.panel-default.panel-multi(ng-repeat="exception in exceptions['all-exceptions']" )
+.panel.panel-default.panel-multi
.panel-heading.clearfix
.panel-title
| Root exception
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
index 5621336..b8a8a4c 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-list.jade
@@ -15,9 +15,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-table.table.table-hover.table-clickable.table-activable
+table.table.table-body-hover.table-clickable.table-activable
thead
tr
+ th.tab-column
th Start Time
th End Time
th Duration
@@ -29,15 +30,26 @@ table.table.table-hover.table-clickable.table-activable
th Tasks
th Status
- tbody
- //- tr(ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" id="vertex-row-{{v.id}}" ng-click="changeNode(v.id)")
- tr(ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" id="vertex-row-{{v.id}}" ui-sref="{nodeid: v.id}")
+ //- tbody(ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" id="vertex-row-{{v.id}}" ui-sref="{nodeid: v.id}")
+ tbody(ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" ui-sref="{nodeid: v.id}")
+ tr
+ td.tab-column
+ ul.nav.nav-tabs.tabs-vertical(ng-if="job.currentNode && v.id == job.currentNode.id")
+ li(ng-class="{ active: ('single-job.plan.node.generic' | isState) }")
+ a(ng-click="gotoGeneric(v.id)" title='Basic')
+ i.fa.fa-tasks
+
+ li(ng-class="{ active: ('single-job.plan.node.accumulators' | isState) }")
+ a(ng-click="gotoAccumulators(v.id)" title='Accumulators')
+ i.fa.fa-calculator
+
td
- | {{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- div(ui-view="node")
+ span(ng-if="v['start-time'] > -1") {{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ td
+ span(ng-if="v['end-time'] > -1") {{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ td
+ span(ng-if="v.duration > -1") {{ v.duration }} ms
- td {{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- td {{ v.duration }} ms
td {{ v.name | humanizeTaskName }}
td {{ v.metrics['read-bytes'] }}
td {{ v.metrics['read-records'] }}
@@ -49,3 +61,12 @@ table.table.table-hover.table-clickable.table-activable
td
bs-label(status="{{v.status}}") {{v.status}}
+ tr(ng-if="job.currentNode && v.id == job.currentNode.id")
+ td.tab-column
+ td(colspan="10")
+ //- a(ng-click="gotoGeneric(nodeid)") Gen!
+ //- |
+ //- a(ng-click="gotoAccumulators(nodeid)") Acc!
+
+ div(ng-if="'single-job.plan.node.generic' | isState" ng-include=" 'partials/jobs/job.plan.node.subtasks.html' ")
+ div(ng-if="'single-job.plan.node.accumulators' | isState" ng-include=" 'partials/jobs/job.plan.node.accumulators.html' ")
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
index 57b7d24..cc7961c 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node-tabs.jade
@@ -1,10 +1,34 @@
+//
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
.panel.panel-default.panel-multi(ng-if="node")
nav.navbar.navbar-default.navbar-secondary-additional(ng-if="job.currentNode")
ul.nav.nav-tabs
li(ui-sref-active='active')
a(ui-sref=".generic({nodeid: nodeid})") Subtasks
+ //- li(ui-sref-active='active')
+ //- a(ui-sref=".accumulators({nodeid: nodeid})") Accumulators
+
li(ui-sref-active='active')
a(ui-sref=".properties({nodeid: nodeid})") Properties
- div.panel-body.clean(ui-view="node-details")
+ .panel-heading.clearfix(ng-if="'single-job.plan.node.properties' | isState")
+ .panel-title
+ | {{ node.description | humanizeTaskName }}
+
+ .panel-body.clean(ui-view="node-details")
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
index 6427ab3..0f5c9b7 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.properties.jade
@@ -15,7 +15,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-.row(ng-if="node")
+.row
.col-sm-6.col-md-4
table.table.table-properties(ng-if="node.optimizer_properties.global_properties")
thead
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
index d20b889..6072dd0 100644
--- a/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
+++ b/flink-runtime-web/web-dashboard/app/partials/jobs/job.plan.node.subtasks.jade
@@ -15,33 +15,40 @@
See the License for the specific language governing permissions and
limitations under the License.
-table.table.table-hover.table-clickable
+
+table.table.table-hover.table-clickable.table-activable.table-inner
thead
tr
th Start Time
th End Time
th Duration
- //- th Name
th Bytes read
th Records read
th Bytes written
th Records written
- th Tasks
+ th Attempt
+ th Host
th Status
tbody
- tr
- td {{ vertex['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- td {{ vertex['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
- td {{ vertex.duration }} ms
- //- td {{ vertex.name | humanizeTaskName }}
- td {{ vertex.metrics['read-bytes'] }}
- td {{ vertex.metrics['read-records'] }}
- td {{ vertex.metrics['write-bytes'] }}
- td {{ vertex.metrics['write-records'] }}
+ tr(ng-repeat="subtask in vertex.st")
+ td
+ span(ng-if="subtask['start-time'] > -1") {{ subtask['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ td
+ span(ng-if="subtask['end-time'] > -1") {{ subtask['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}
+ td
+ span(ng-if="subtask.duration > -1") {{ subtask.duration }} ms
+
+ td
+ span(ng-if="subtask.metrics['read-bytes'] > -1") {{ subtask.metrics['read-bytes'] }}
+ td
+ span(ng-if="subtask.metrics['read-records'] > -1") {{ subtask.metrics['read-records'] }}
+ td
+ span(ng-if="subtask.metrics['write-bytes'] > -1") {{ subtask.metrics['write-bytes'] }}
td
- .label-group
- bs-label(status="{{status}}" ng-repeat="(index, status) in stateList") {{vertex.tasks[status]}}
+ span(ng-if="subtask.metrics['write-records'] > -1") {{ subtask.metrics['write-records'] }}
+ td {{ subtask.attempt + 1 }}
+ td {{ subtask.host }}
td
- bs-label(status="{{vertex.status}}") {{vertex.status}}
+ bs-label(status="{{subtask.status}}") {{subtask.status}}
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/scripts/index.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/index.coffee b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
index 806a2a1..3bd1dac 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/index.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/index.coffee
@@ -106,6 +106,15 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
controller: 'JobPlanNodeListController'
templateUrl: "partials/jobs/job.plan.node-list.html"
+ .state "single-job.plan.node.accumulators",
+ url: "/accumulators"
+ views:
+ 'node-details':
+ controller: 'JobPlanNodeListAccumulatorsController'
+ templateUrl: "partials/jobs/job.plan.node-list.html"
+ # controller: 'JobPlanNodePropertiesController'
+ # templateUrl: "partials/jobs/job.plan.node.properties.html"
+
.state "single-job.plan.node.properties",
url: "/properties"
views:
@@ -113,38 +122,7 @@ angular.module('flinkApp', ['ui.router', 'angularMoment'])
controller: 'JobPlanNodePropertiesController'
templateUrl: "partials/jobs/job.plan.node.properties.html"
- # .state "single-job.plan.node.accumulators",
- # url: "/accumulators"
- # views:
- # 'node-contents':
- # controller: 'JobPlanNodeAccumulatorsController'
- # templateUrl: "partials/jobs/job.plan.node.accumulators.html"
-
- # .state "single-job.plan.node",
- # url: "/vertex/{nodeid}"
- # abstract: true
- # views:
- # node:
- # templateUrl: "partials/jobs/job.plan.node.html"
- # controller: 'JobPlanNodeController'
-
- # .state "single-job.plan.node.subtasks",
- # url: ""
- # views:
- # nodeTab:
- # templateUrl: "partials/jobs/job.plan.node.subtasks.html"
-
- # .state "single-job.plan.node.properties",
- # url: "/properties"
- # views:
- # nodeTab:
- # templateUrl: "partials/jobs/job.plan.node.properties.html"
-
- # .state "single-job.plan.node.accumulators",
- # url: "/properties"
- # views:
- # nodeTab:
- # templateUrl: "partials/jobs/job.plan.node.accumulators.html"
+
.state "single-job.timeline",
url: "/timeline"
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
index 127cec1..eee69f0 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.ctrl.coffee
@@ -85,6 +85,7 @@ angular.module('flinkApp')
console.log 'JobPlanNodeTabsController'
$scope.nodeid = $stateParams.nodeid
+ $scope.stateList = JobsService.stateList()
JobsService.getNode($scope.nodeid).then (data) ->
$scope.node = data
@@ -92,11 +93,25 @@ angular.module('flinkApp')
# --------------------------------------
-.controller 'JobPlanNodeListController', ($scope, $stateParams, JobsService) ->
+.controller 'JobPlanNodeListController', ($scope, $stateParams, JobsService, $state) ->
console.log 'JobPlanNodeListController'
- # $scope.nodeid = $stateParams.nodeid
- $scope.stateList = JobsService.stateList()
+ $scope.gotoAccumulators = (nodeid) ->
+ $state.go('^.accumulators', { nodeid: nodeid })
+
+ JobsService.getSubtasks($stateParams.nodeid).then (data) ->
+ $scope.vertex = data
+
+# --------------------------------------
+
+.controller 'JobPlanNodeListAccumulatorsController', ($scope, $stateParams, JobsService, $state) ->
+ console.log 'JobPlanNodeListAccumulatorsController'
+
+ $scope.gotoGeneric = (nodeid) ->
+ $state.go('^.generic', { nodeid: nodeid })
+
+ # JobsService.getSubtasks($stateParams.nodeid).then (data) ->
+ # $scope.vertex = data
# --------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
index f1a2e95..f840c14 100644
--- a/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
+++ b/flink-runtime-web/web-dashboard/app/scripts/modules/jobs/jobs.svc.coffee
@@ -171,12 +171,28 @@ angular.module('flinkApp')
$http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasktimes"
.success (data) ->
+ # TODO: change to subtasktimes
vertex.subtasks = data.subtasks
deferred.resolve(vertex)
deferred.promise
+ @getSubtasks = (vertexid) ->
+ deferred = $q.defer()
+
+ $q.all([deferreds.job.promise]).then (data) =>
+ vertex = @seekVertex(vertexid)
+
+ $http.get flinkConfig.jobServer + "/jobs/" + currentJob.jid + "/vertices/" + vertexid
+ .success (data) ->
+ vertex.st = data.subtasks
+
+ deferred.resolve(vertex)
+
+ deferred.promise
+
+
@loadExceptions = ->
deferred = $q.defer()
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/styles/index.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/index.styl b/flink-runtime-web/web-dashboard/app/styles/index.styl
index 295bc47..3c146a5 100644
--- a/flink-runtime-web/web-dashboard/app/styles/index.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/index.styl
@@ -122,6 +122,9 @@ sidebar-width = 250px
&.table-clickable
tr
cursor: pointer
+
+ &.table-inner
+ background-color: transparent
.panel
&.panel-dashboard
@@ -263,13 +266,41 @@ sidebar-width = 250px
&.indicator-danger
color: #d9534f
-
pre.exception
border: none
background-color: transparent
padding: 0
margin: 0
+.nav-tabs
+ &.tabs-vertical
+ // float: right
+ position: absolute
+ left: 0
+ top: 0
+ border-bottom: none
+ // border-right: 1px solid #dddddd
+ z-index: 100
+ li
+ float: none
+ margin-bottom: 0
+ margin-right: -1px
+
+ > a
+ margin-right: 0
+ border-radius(0)
+ border-bottom: none
+ border-left: 2px solid transparent
+
+ > a:hover, > a:focus
+ border-bottom: none
+ border-left: 2px solid #000000
+
+ &.active
+ > a
+ border-bottom: none
+ border-left: 2px solid #000000
+
@import './job'
@import './graph'
@import './timeline'
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/app/styles/job.styl
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/app/styles/job.styl b/flink-runtime-web/web-dashboard/app/styles/job.styl
index 4259824..ef12806 100644
--- a/flink-runtime-web/web-dashboard/app/styles/job.styl
+++ b/flink-runtime-web/web-dashboard/app/styles/job.styl
@@ -39,12 +39,31 @@
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
+
+ &.table-body-hover
+ > tbody
+ border-top: none
+ &:hover, &.active
+ td:not(.tab-column)
+ background-color: #f0f0f0
+ td.tab-column li.active
+ background-color: #f0f0f0
&.table-activable
- tr
- border-left: 2px solid transparent
- &.active
- border-left: 2px solid #000000
+ // tbody
+ // border-left: 2px solid transparent
+ // &.active
+ // border-left: 2px solid #000000
+
+ th.tab-column, td.tab-column
+ border-top: none
+ width: 47px
+
+ td.tab-column
+ border-right: 1px solid #dddddd
+
+ td
+ position: relative
.label-group
http://git-wip-us.apache.org/repos/asf/flink/blob/6a3b095e/flink-runtime-web/web-dashboard/web/css/index.css
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/css/index.css b/flink-runtime-web/web-dashboard/web/css/index.css
index eca32cd..2077b14 100644
--- a/flink-runtime-web/web-dashboard/web/css/index.css
+++ b/flink-runtime-web/web-dashboard/web/css/index.css
@@ -135,6 +135,9 @@
.table.table-clickable tr {
cursor: pointer;
}
+.table.table-inner {
+ background-color: transparent;
+}
.panel.panel-dashboard .huge {
font-size: 28px;
}
@@ -324,6 +327,34 @@ pre.exception {
padding: 0;
margin: 0;
}
+.nav-tabs.tabs-vertical {
+ position: absolute;
+ left: 0;
+ top: 0;
+ border-bottom: none;
+ z-index: 100;
+}
+.nav-tabs.tabs-vertical li {
+ float: none;
+ margin-bottom: 0;
+ margin-right: -1px;
+}
+.nav-tabs.tabs-vertical li > a {
+ margin-right: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+ border-bottom: none;
+ border-left: 2px solid transparent;
+}
+.nav-tabs.tabs-vertical li > a:hover,
+.nav-tabs.tabs-vertical li > a:focus {
+ border-bottom: none;
+ border-left: 2px solid #000;
+}
+.nav-tabs.tabs-vertical li.active > a {
+ border-bottom: none;
+ border-left: 2px solid #000;
+}
.canvas-wrapper {
border: 1px solid #ddd;
position: relative;
@@ -349,11 +380,27 @@ pre.exception {
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
}
-.table.table-activable tr {
- border-left: 2px solid transparent;
+.table.table-body-hover > tbody {
+ border-top: none;
}
-.table.table-activable tr.active {
- border-left: 2px solid #000;
+.table.table-body-hover > tbody:hover td:not(.tab-column),
+.table.table-body-hover > tbody.active td:not(.tab-column) {
+ background-color: #f0f0f0;
+}
+.table.table-body-hover > tbody:hover td.tab-column li.active,
+.table.table-body-hover > tbody.active td.tab-column li.active {
+ background-color: #f0f0f0;
+}
+.table.table-activable th.tab-column,
+.table.table-activable td.tab-column {
+ border-top: none;
+ width: 47px;
+}
+.table.table-activable td.tab-column {
+ border-right: 1px solid #ddd;
+}
+.table.table-activable td {
+ position: relative;
}
.label-group .label {
display: inline-block;
[07/51] [abbrv] flink git commit: [FLINK-2547] [web dashboard]
Updated web dashboard after request/response changes
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/562573df/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index 4d7b406..b7dd5e5 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -9,10 +9,7 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
newServer: 'http://localhost:8081',
refreshInterval: 10000
}).run(["JobsService", "flinkConfig", "$interval", function(JobsService, flinkConfig, $interval) {
- JobsService.listJobs();
- return $interval(function() {
- return JobsService.listJobs();
- }, flinkConfig.refreshInterval);
+ return JobsService.listJobs();
}]).config(["$stateProvider", "$urlRouterProvider", function($stateProvider, $urlRouterProvider) {
$stateProvider.state("overview", {
url: "/overview",
@@ -56,7 +53,7 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
}
}
}).state("single-job.plan.node", {
- url: "/{nodeid:int}",
+ url: "/vertex/{nodeid}",
views: {
node: {
templateUrl: "partials/jobs/job.plan.node.html",
@@ -206,8 +203,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
data: "="
},
link: function(scope, elem, attrs) {
- var analyzeTime, containerW, svgEl, zoom;
- zoom = d3.behavior.zoom();
+ var analyzeTime, containerW, svgEl;
svgEl = elem.children()[0];
containerW = elem.width();
angular.element(svgEl).attr('width', containerW - 16);
@@ -240,25 +236,18 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
});
});
chart = d3.timeline().stack().tickFormat({
- format: d3.time.format("%S"),
- tickInterval: 1,
+ format: d3.time.format("%L"),
tickSize: 1
- }).labelFormat(function(label) {
+ }).prefix("single").labelFormat(function(label) {
return label;
}).margin({
left: 100,
right: 0,
top: 0,
bottom: 0
- });
- svg = d3.select(svgEl).datum(testData).call(chart).call(zoom);
+ }).itemHeight(24).relativeTime();
+ svg = d3.select(svgEl).datum(testData).call(chart);
svgG = svg.select("g");
- zoom.on("zoom", function() {
- var ev;
- ev = d3.event;
- svgG.selectAll('rect').attr("transform", "translate(" + ev.translate[0] + ",0) scale(" + ev.scale + ",1)");
- return svgG.selectAll('text').attr("transform", "translate(" + ev.translate[0] + ",0) scale(" + ev.scale + ",1)");
- });
bbox = svgG[0][0].getBBox();
return svg.attr('height', bbox.height + 30);
};
@@ -272,11 +261,13 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
job: "="
},
link: function(scope, elem, attrs) {
- var analyzeTime, containerW, svgEl, zoom;
- zoom = d3.behavior.zoom();
+ var analyzeTime, containerW, svgEl, translateLabel;
svgEl = elem.children()[0];
containerW = elem.width();
angular.element(svgEl).attr('width', containerW - 16);
+ translateLabel = function(label) {
+ return label.replace(">", ">");
+ };
analyzeTime = function(data) {
var bbox, chart, svg, svgG, testData;
testData = [];
@@ -286,8 +277,8 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
return testData.push({
times: [
{
- label: vertex.groupvertexname,
- color: "#3fb6d8",
+ label: translateLabel(vertex.groupvertexname),
+ color: "#62cdea",
starting_time: vTime["STARTED"],
ending_time: vTime["ENDED"],
link: vertex.groupvertexid
@@ -301,23 +292,16 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
vertexId: d.link
});
}).tickFormat({
- format: d3.time.format("%S"),
- tickInterval: 1,
+ format: d3.time.format("%L"),
tickSize: 1
- }).margin({
+ }).prefix("main").margin({
left: 0,
right: 0,
top: 0,
bottom: 0
- });
- svg = d3.select(svgEl).datum(testData).call(chart).call(zoom);
+ }).itemHeight(24).showBorderLine().showHourTimeline();
+ svg = d3.select(svgEl).datum(testData).call(chart);
svgG = svg.select("g");
- zoom.on("zoom", function() {
- var ev;
- ev = d3.event;
- svgG.selectAll('rect').attr("transform", "translate(" + ev.translate[0] + ",0) scale(" + ev.scale + ",1)");
- return svgG.selectAll('text').attr("transform", "translate(" + ev.translate[0] + ",0) scale(" + ev.scale + ",1)");
- });
bbox = svgG[0][0].getBBox();
return svg.attr('height', bbox.height + 30);
};
@@ -401,7 +385,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
};
createLabelNode = function(el, info, maxW, maxH) {
var labelValue, stepName;
- labelValue = "<a href='#/jobs/" + jobid + "/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>";
+ labelValue = "<a href='#/jobs/" + jobid + "/vertex/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>";
if (info === "mirror") {
labelValue += "<h3 class='node-name'>Mirror of " + el.operator + "</h3>";
} else {
@@ -741,7 +725,6 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
var deferred, seekNode;
seekNode = function(nodeid, data) {
var i, len, node, sub;
- nodeid = parseInt(nodeid);
for (i = 0, len = data.length; i < len; i++) {
node = data[i];
if (node.id === nodeid) {
@@ -761,7 +744,7 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
return function(data) {
var foundNode;
foundNode = seekNode(nodeid, currentPlan.nodes);
- return _this.getVertex(currentJob.jid, currentJob.oldV.groupvertices[0].groupvertexid).then(function(vertex) {
+ return _this.getVertex(currentJob.jid, nodeid).then(function(vertex) {
foundNode.vertex = vertex;
return deferred.resolve(foundNode);
});
@@ -804,4 +787,4 @@ angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuY3RybC5qcyIsIm1vZHVsZXMvam9icy9qb2JzLmRpci5jb2ZmZWUiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuanMiLCJtb2R1bGVzL2pvYnMvam9icy5zdmMuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmpzIiwibW9kdWxlcy9vdmVydmlldy9vdmVydmlldy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuc3ZjLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQWtCQSxRQUFRLE9BQU8sWUFBWSxDQUFDLGFBQWEsa0JBSXhDLG1CQUFJLFNBQUMsWUFBRDtFQUNILFdBQVcsaUJBQWlCO0VDckI1QixPRHNCQSxXQUFXLGNBQWMsV0FBQTtJQUN2QixXQUFXLGlCQUFpQixDQUFDLFdBQVc7SUNyQnhDLE9Ec0JBLFdBQVcsZUFBZTs7SUFJN0IsU0FBUyxlQUFlO0VBQ3ZCLFdBQVc7RUFDWCxXQUFXO0VBR1gsaUJBQWlCO0dBS2xCLGdEQUFJLFNBQUM
sYUFBYSxhQUFhLFdBQTNCO0VBQ0gsWUFBWTtFQzdCWixPRCtCQSxVQUFVLFdBQUE7SUM5QlIsT0QrQkEsWUFBWTtLQUNaLFlBQVk7SUFLZixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sd0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sdUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhOzs7S0FFbEIsTUFBTSw4QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsUUFDRTtRQUFBLGFBQWE7UUFDYixZQUFZOzs7S0FFakIsTUFBTSx5QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsU0FDRTtRQUFBLGFBQWE7OztLQUVsQixNQUFNLHlCQUNMO0lBQUEsS0FBSztJ
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
QyxVQUFVLEtBQUssU0FBUyxVQUFVLElBQUksVUFBVSxLQUFLLENBQUEsU0FBQSxPQUFBO01DdEIzRCxPRHNCMkQsU0FBQyxNQUFEO1FBQzNELElBQUE7UUFBQSxZQUFZLFNBQVMsUUFBUSxZQUFZO1FDcEJ2QyxPRHVCRixNQUFDLFVBQVUsV0FBVyxLQUFLLFdBQVcsS0FBSyxjQUFjLEdBQUcsZUFBZSxLQUFLLFNBQUMsUUFBRDtVQUM5RSxVQUFVLFNBQVM7VUN0QmpCLE9EdUJGLFNBQVMsUUFBUTs7O09BTndDO0lDYjdELE9EcUJBLFNBQVM7O0VBR1gsS0FBQyxZQUFZLFNBQUMsT0FBTyxVQUFSO0lBQ1gsSUFBQTtJQUFBLFdBQVcsR0FBRztJQUVkLE1BQU0sSUFBSSxZQUFZLFlBQVksbUNBQW1DLFFBQVEsa0JBQWtCLFVBQzlGLFFBQVEsU0FBQyxNQUFEO01DdEJQLE9EdUJBLFNBQVMsUUFBUTs7SUNyQm5CLE9EdUJBLFNBQVM7O0VDckJYLE9EdUJBOztBQ3JCRjtBQ3pJQSxRQUFRLE9BQU8sWUFFZCxXQUFXLG1FQUFzQixTQUFDLFFBQVEsaUJBQWlCLGFBQTFCO0VBQ2hDLE9BQU8sY0FBYyxXQUFBO0lBQ25CLE9BQU8sY0FBYyxZQUFZLFFBQVE7SUNuQnpDLE9Eb0JBLE9BQU8sZUFBZSxZQUFZLFFBQVE7O0VBRTVDLFlBQVksaUJBQWlCLE9BQU87RUFDcEMsT0FBTyxJQUFJLFlBQVksV0FBQTtJQ25CckIsT0RvQkEsWUFBWSxtQkFBbUIsT0FBTzs7RUNsQnhDLE9Eb0JBLE9BQU87O0FDbEJUO0FDT0EsUUFBUSxPQUFPLFlBRWQsUUFBUSxvREFBbUIsU0FBQyxPQUFPLGFBQWEsTUFBckI7RUFDMUIsSUFBQTtFQUFBLGVBQWU7R
UFFZixLQUFDLG1CQUFtQixXQUFBO0lBQ2xCLE1BQU0sSUFBSSxZQUFZLFlBQVksbUJBQ2pDLFFBQVEsU0FBQyxNQUFNLFFBQVEsU0FBUyxRQUF4QjtNQ3BCUCxPRHFCQSxLQUFLO09BRU4sTUFBTSxTQUFDLE1BQU0sUUFBUSxTQUFTLFFBQXhCO0lDckJQLE9Ed0JBOztFQ3RCRixPRHdCQTs7QUN0QkYiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdG
luZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJywgWyd1aS5yb3V0ZXInLCAnYW5ndWxhck1vbWVudCddKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKCRyb290U2NvcGUpIC0+XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZVxuICAkcm9vdFNjb3BlLnNob3dTaWRlYmFyID0gLT5cbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGVcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJDbGFzcyA9ICdmb3JjZS1zaG93J1xuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb25zdGFudCAnZmxpbmtDb25maWcnLCB7XG4gIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MSdcbiAgbmV3U2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDo4MDgxJ1xuIyAgam9iU2VydmVyOiAnaHR0cDovL2xvY2F
saG9zdDozMDAwL25ldy1zZXJ2ZXInXG4jICBuZXdTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjMwMDAvbmV3LXNlcnZlcidcbiAgcmVmcmVzaEludGVydmFsOiAxMDAwMFxufVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5ydW4gKEpvYnNTZXJ2aWNlLCBmbGlua0NvbmZpZywgJGludGVydmFsKSAtPlxuICBKb2JzU2VydmljZS5saXN0Sm9icygpXG5cbiAgJGludGVydmFsIC0+XG4gICAgSm9ic1NlcnZpY2UubGlzdEpvYnMoKVxuICAsIGZsaW5rQ29uZmlnLnJlZnJlc2hJbnRlcnZhbFxuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbmZpZyAoJHN0YXRlUHJvdmlkZXIsICR1cmxSb3V0ZXJQcm92aWRlcikgLT5cbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUgXCJvdmVydmlld1wiLFxuICAgIHVybDogXCIvb3ZlcnZpZXdcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvb3ZlcnZpZXcuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdPdmVydmlld0NvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwicnVubmluZy1qb2JzXCIsXG4gICAgdXJsOiBcIi9ydW5uaW5nLWpvYnNcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9ydW5uaW5nLWpvYnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdSdW5uaW5nSm9i
c0NvbnRyb2xsZXInXG4gIFxuICAuc3RhdGUgXCJjb21wbGV0ZWQtam9ic1wiLFxuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIlxuICAgIHZpZXdzOlxuICAgICAgbWFpbjpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9jb21wbGV0ZWQtam9icy5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInNpbmdsZS1qb2JcIixcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwiXG4gICAgYWJzdHJhY3Q6IHRydWVcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnU2luZ2xlSm9iQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW5cIixcbiAgICB1cmw6IFwiXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnBsYW4ubm9kZVwiLFxuICAgIHVybDogXCIve25vZGVpZDppbnR9XCJcbiAgICB2aWV3czpcbiAgICAgIG5vZGU6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvY
lBsYW5Ob2RlQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lXCIsXG4gICAgdXJsOiBcIi90aW1lbGluZVwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLFxuICAgIHVybDogXCIve3ZlcnRleElkfVwiXG4gICAgdmlld3M6XG4gICAgICB2ZXJ0ZXg6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLnZlcnRleC5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIixcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2Iuc3RhdGlzdGljcy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIixcbiAgICB1cmw6IFwiL2V4Y2VwdGlvbnNcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCJcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iLmNvbmZpZ1wiLFxuICAgIHVybDogXCIvY29uZmlnXCJcbiAgIC
B2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmNvbmZpZy5odG1sXCJcblxuICAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlIFwiL292ZXJ2aWV3XCJcbiIsImFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcsIFsndWkucm91dGVyJywgJ2FuZ3VsYXJNb21lbnQnXSkucnVuKGZ1bmN0aW9uKCRyb290U2NvcGUpIHtcbiAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9IGZhbHNlO1xuICByZXR1cm4gJHJvb3RTY29wZS5zaG93U2lkZWJhciA9IGZ1bmN0aW9uKCkge1xuICAgICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSAhJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZTtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5zaWRlYmFyQ2xhc3MgPSAnZm9yY2Utc2hvdyc7XG4gIH07XG59KS5jb25zdGFudCgnZmxpbmtDb25maWcnLCB7XG4gIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MScsXG4gIG5ld1NlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MScsXG4gIHJlZnJlc2hJbnRlcnZhbDogMTAwMDBcbn0pLnJ1bihmdW5jdGlvbihKb2JzU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkge1xuICBKb2JzU2VydmljZS5saXN0Sm9icygpO1xuICByZXR1cm4gJGludGVydmFsKGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBKb2JzU2VydmljZS5saXN0Sm9icygpO1xuICB9LCBmbGlua0NvbmZpZy5yZWZyZXNoSW50ZXJ2YWwpO1xufSk
uY29uZmlnKGZ1bmN0aW9uKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIHtcbiAgJHN0YXRlUHJvdmlkZXIuc3RhdGUoXCJvdmVydmlld1wiLCB7XG4gICAgdXJsOiBcIi9vdmVydmlld1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ092ZXJ2aWV3Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwicnVubmluZy1qb2JzXCIsIHtcbiAgICB1cmw6IFwiL3J1bm5pbmctam9ic1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1J1bm5pbmdKb2JzQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwiY29tcGxldGVkLWpvYnNcIiwge1xuICAgIHVybDogXCIvY29tcGxldGVkLWpvYnNcIixcbiAgICB2aWV3czoge1xuICAgICAgbWFpbjoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2NvbXBsZXRlZC1qb2JzLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0NvbXBsZXRlZEpvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iXCIsIHtcbiAgICB1cmw6IFwiL2pvYnMve2pvYmlkfVwiLFxuICAg
IGFic3RyYWN0OiB0cnVlLFxuICAgIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhblwiLCB7XG4gICAgdXJsOiBcIlwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4uaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IucGxhbi5ub2RlXCIsIHtcbiAgICB1cmw6IFwiL3tub2RlaWQ6aW50fVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBub2RlOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnBsYW4ubm9kZS5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZUNvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IudGltZWxpbmVcIiwge1xuICAgIHVybDogXCIvdGltZWxpbmVcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0Y
XRlKFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIiwge1xuICAgIHVybDogXCIve3ZlcnRleElkfVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICB2ZXJ0ZXg6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUudmVydGV4Lmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5zdGF0aXN0aWNzXCIsIHtcbiAgICB1cmw6IFwiL3N0YXRpc3RpY3NcIixcbiAgICB2aWV3czoge1xuICAgICAgZGV0YWlsczoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5zdGF0aXN0aWNzLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLmV4Y2VwdGlvbnNcIiwge1xuICAgIHVybDogXCIvZXhjZXB0aW9uc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmV4Y2VwdGlvbnMuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IuY29uZmlnXCIsIHtcbiAgICB1cmw6IFwiL2NvbmZpZ1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmNvbmZpZy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH
0pO1xuICByZXR1cm4gJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZShcIi9vdmVydmlld1wiKTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciB
leHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICdic0xhYmVsJywgKEpvYnNTZXJ2aWNlKSAtPlxuICB0cmFuc2NsdWRlOiB0cnVlXG4gIHJlcGxhY2U6IHRydWVcbiAgc2NvcGU6IFxuICAgIGdldExhYmVsQ2xhc3M6IFwiJlwiXG4gICAgc3RhdHVzOiBcIkBcIlxuXG4gIHRlbXBsYXRlOiBcIjxzcGFuIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJz48bmctdHJhbnNjbHVkZT48L25nLXRyYW5zY2x1ZGU+PC9zcGFuPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2xhYmVsIGxhYmVsLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2luZGljYXRvclByaW1hcnknLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHJlcGxhY2U6IHRydWVcbiAgc2NvcGU6IFxuICAgIGdldExhYmVsQ2xhc3M6IFwi
JlwiXG4gICAgc3RhdHVzOiAnQCdcblxuICB0ZW1wbGF0ZTogXCI8aSB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIlxuICBcbiAgbGluazogKHNjb3BlLCBlbGVtZW50LCBhdHRycykgLT5cbiAgICBzY29wZS5nZXRMYWJlbENsYXNzID0gLT5cbiAgICAgICdmYSBmYS1jaXJjbGUgaW5kaWNhdG9yIGluZGljYXRvci0nICsgSm9ic1NlcnZpY2UudHJhbnNsYXRlTGFiZWxTdGF0ZShhdHRycy5zdGF0dXMpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0YWJsZVByb3BlcnR5JywgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTpcbiAgICB2YWx1ZTogJz0nXG5cbiAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZGlyZWN0aXZlKCdic0xhYmVsJywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICB0cmFuc2NsdWRlOiB0cnVlLFxuICAgIHJlcGxhY2U6IHRydWUsXG4gICAgc2NvcGU6IHtcbiAgICAgIGdldExhYmVsQ2xhc3M6IFwiJlwiLFxuICAgICAgc3RhdHVzOiBcIkBcIlxuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknPjxuZy10cmFuc2Nsd
WRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCIsXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSB7XG4gICAgICByZXR1cm4gc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJ2xhYmVsIGxhYmVsLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cyk7XG4gICAgICB9O1xuICAgIH1cbiAgfTtcbn0pLmRpcmVjdGl2ZSgnaW5kaWNhdG9yUHJpbWFyeScsIGZ1bmN0aW9uKEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6ICdAJ1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPGkgdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknIC8+XCIsXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSB7XG4gICAgICByZXR1cm4gc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gJ2ZhIGZhLWNpcmNsZSBpbmRpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cyk7XG4gICAgICB9O1xuICAgIH1cbiAgfTtcbn0pLmRpcmVjdGl2ZSgndGFibGVQcm9wZXJ0eScsIGZ1bmN0aW9uKCkge1xuICByZXR1cm4ge1xuICAgIHJlcGxhY2U6IH
RydWUsXG4gICAgc2NvcGU6IHtcbiAgICAgIHZhbHVlOiAnPSdcbiAgICB9LFxuICAgIHRlbXBsYXRlOiBcIjx0ZCB0aXRsZT1cXFwie3t2YWx1ZSB8fCAnTm9uZSd9fVxcXCI+e3t2YWx1ZSB8fCAnTm9uZSd9fTwvdGQ+XCJcbiAgfTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGF
uIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5maWx0ZXIgXCJhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRcIiwgKGFuZ3VsYXJNb21lbnRDb25maWcpIC0+XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlciA9ICh2YWx1ZSwgZm9ybWF0LCBkdXJhdGlvbkZvcm1hdCkgLT5cbiAgICByZXR1cm4gXCJcIiAgaWYgdHlwZW9mIHZhbHVlIGlzIFwidW5kZWZpbmVkXCIgb3IgdmFsdWUgaXMgbnVsbFxuXG4gICAgbW9tZW50LmR1cmF0aW9uKHZhbHVlLCBmb3JtYXQpLmZvcm1hdChkdXJhdGlvbkZvcm1hdCwgeyB0cmltOiBmYWxzZSB9KVxuXG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlci4kc3RhdGVmdWwgPSBhbmd1bGFyTW9tZW50Q29uZmlnLnN0YXRlZnVsRmlsdGVyc1xuXG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlclxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuZmlsdGVyKFwiYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkXCIsIGZ1bmN0aW9uKGFuZ3VsYXJNb21lbnRDb25maWcpIHtcbiAgdmFyIGFtRHVy
YXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlcjtcbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gZnVuY3Rpb24odmFsdWUsIGZvcm1hdCwgZHVyYXRpb25Gb3JtYXQpIHtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcInVuZGVmaW5lZFwiIHx8IHZhbHVlID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gXCJcIjtcbiAgICB9XG4gICAgcmV0dXJuIG1vbWVudC5kdXJhdGlvbih2YWx1ZSwgZm9ybWF0KS5mb3JtYXQoZHVyYXRpb25Gb3JtYXQsIHtcbiAgICAgIHRyaW06IGZhbHNlXG4gICAgfSk7XG4gIH07XG4gIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlci4kc3RhdGVmdWwgPSBhbmd1bGFyTW9tZW50Q29uZmlnLnN0YXRlZnVsRmlsdGVycztcbiAgcmV0dXJuIGFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZEZpbHRlcjtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b
3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbi5jb250cm9sbGVyICdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLi
RvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IC0+XG4gICAgJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdmaW5pc2hlZCcpXG5cbiAgSm9ic1NlcnZpY2UucmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICBKb2JzU2VydmljZS51blJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuXG4gICRzY29wZS5qb2JPYnNlcnZlcigpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ1NpbmdsZUpvYkNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRyb290U2NvcGUpIC0+XG4gICRzY29wZS5qb2JpZCA9ICRzdGF0ZVBhcmFtcy5qb2JpZFxuICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcblxuICBKb2JzU2VydmljZS5sb2FkSm9iKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkcm9
vdFNjb3BlLmpvYiA9IGRhdGFcblxuICAkc2NvcGUuJG9uICckZGVzdHJveScsIC0+XG4gICAgJHJvb3RTY29wZS5qb2IgPSBudWxsXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Db250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICBKb2JzU2VydmljZS5sb2FkUGxhbigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLnBsYW4gPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLm5vZGVpZCA9ICRzdGF0ZVBhcmFtcy5ub2RlaWRcbiAgJHNjb3BlLnN0YXRlTGlzdCA9IEpvYnNTZXJ2aWNlLnN0YXRlTGlzdCgpXG5cbiAgSm9ic1NlcnZpY2UuZ2V0Tm9kZSgkc2NvcGUubm9kZWlkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS5ub2RlID0gZGF0YVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmdldFZlcnRleCgkc3Rh
dGVQYXJhbXMuam9iaWQsICRzdGF0ZVBhcmFtcy52ZXJ0ZXhJZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUudmVydGV4ID0gZGF0YVxuIiwiYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJykuY29udHJvbGxlcignUnVubmluZ0pvYnNDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRzY29wZS5qb2JzID0gSm9ic1NlcnZpY2UuZ2V0Sm9icygncnVubmluZycpO1xuICB9O1xuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS5qb2JPYnNlcnZlcigpO1xufSkuY29udHJvbGxlcignQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdmaW5pc2hlZCcpO1xuICB9O1xuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7X
G4gICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS5qb2JPYnNlcnZlcigpO1xufSkuY29udHJvbGxlcignU2luZ2xlSm9iQ29udHJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlLCAkcm9vdFNjb3BlKSB7XG4gICRzY29wZS5qb2JpZCA9ICRzdGF0ZVBhcmFtcy5qb2JpZDtcbiAgJHJvb3RTY29wZS5qb2IgPSBudWxsO1xuICBKb2JzU2VydmljZS5sb2FkSm9iKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRyb290U2NvcGUuam9iID0gZGF0YTtcbiAgfSk7XG4gIHJldHVybiAkc2NvcGUuJG9uKCckZGVzdHJveScsIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkcm9vdFNjb3BlLmpvYiA9IG51bGw7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUGxhbkNvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UubG9hZFBsYW4oJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlLnBsYW4gPSBkYXRhO1xuICB9KTtcbn0pLmNvbnRyb2xsZXIoJ0pvYlBsYW5Ob2RlQ29udHJvbGxlcicsIG
Z1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5ub2RlaWQgPSAkc3RhdGVQYXJhbXMubm9kZWlkO1xuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KCk7XG4gIHJldHVybiBKb2JzU2VydmljZS5nZXROb2RlKCRzY29wZS5ub2RlaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkc2NvcGUubm9kZSA9IGRhdGE7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldFZlcnRleCgkc3RhdGVQYXJhbXMuam9iaWQsICRzdGF0ZVBhcmFtcy52ZXJ0ZXhJZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS52ZXJ0ZXggPSBkYXRhO1xuICB9KTtcbn0pO1xuIiwiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgdGhpcyBmaWxlXG4jIHRvIHl
vdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcpXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd2ZXJ0ZXgnLCAoJHN0YXRlKSAtPlxuICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSd0aW1lbGluZSBzZWNvbmRhcnknIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwi
XG5cbiAgc2NvcGU6XG4gICAgZGF0YTogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIHpvb20gPSBkMy5iZWhhdmlvci56b29tKClcbiAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXIC0gMTYpXG5cbiAgICBhbmFseXplVGltZSA9IChkYXRhKSAtPlxuICAgICAgdGVzdERhdGEgPSBbXVxuXG4gICAgICBhbmd1bGFyLmZvckVhY2ggZGF0YS5ncm91cHZlcnRleC5ncm91cG1lbWJlcnMsICh2ZXJ0ZXgsIGkpIC0+XG4gICAgICAgIHZUaW1lID0gZGF0YS52ZXJ0aWNldGltZXNbdmVydGV4LnZlcnRleGlkXVxuXG4gICAgICAgIHRlc3REYXRhLnB1c2gge1xuICAgICAgICAgIGxhYmVsOiBcIiN7dmVydGV4LnZlcnRleGluc3RhbmNlbmFtZX0gKCN7aX0pXCJcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjNjY2XCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJTQ0hFRFVMRURcIl0gKiAxMDBcbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZUaW1lW1wiREVQTE9ZSU5HXCJdICogMTAwXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcI
kRlcGxveWluZ1wiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiNhYWFcIlxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIkRFUExPWUlOR1wiXSAqIDEwMFxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJSVU5OSU5HXCJdICogMTAwXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlJ1bm5pbmdcIlxuICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJSVU5OSU5HXCJdICogMTAwXG4gICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkZJTklTSEVEXCJdICogMTAwXG4gICAgICAgICAgICB9XG4gICAgICAgICAgXVxuICAgICAgICB9XG5cbiAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJVNcIiksXG4gICAgICAgICMgdGlja1RpbWU6IGQzLnRpbWUubWlsbGlzZWNvbmRzLFxuICAgICAgICB0aWNrSW50ZXJ2YWw6IDEsXG4gICAgICAgIHRpY2tTaXplOiAxXG4gICAgICB9KS5sYWJlbEZvcm1hdCgobGFiZWwpIC0+XG4gICAgICAgIGxhYmVsXG4gICAgICApLm1hcmdpbih7IGxlZnQ6IDEwMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgIC
AgIC5jYWxsKGNoYXJ0KVxuICAgICAgLmNhbGwoem9vbSlcblxuICAgICAgc3ZnRyA9IHN2Zy5zZWxlY3QoXCJnXCIpXG5cbiAgICAgIHpvb20ub24oXCJ6b29tXCIsIC0+XG4gICAgICAgIGV2ID0gZDMuZXZlbnRcblxuICAgICAgICBzdmdHLnNlbGVjdEFsbCgncmVjdCcpLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyBldi50cmFuc2xhdGVbMF0gKyBcIiwwKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIsMSlcIilcbiAgICAgICAgc3ZnRy5zZWxlY3RBbGwoJ3RleHQnKS5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgZXYudHJhbnNsYXRlWzBdICsgXCIsMCkgc2NhbGUoXCIgKyBldi5zY2FsZSArIFwiLDEpXCIpXG4gICAgICApXG5cbiAgICAgIGJib3ggPSBzdmdHWzBdWzBdLmdldEJCb3goKVxuICAgICAgc3ZnLmF0dHIoJ2hlaWdodCcsIGJib3guaGVpZ2h0ICsgMzApXG5cbiAgICBhbmFseXplVGltZShzY29wZS5kYXRhKVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0aW1lbGluZScsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGpvYjogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIHpvb20gPSBkMy5iZWhhdml
vci56b29tKClcbiAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCBjb250YWluZXJXIC0gMTYpXG5cbiAgICBhbmFseXplVGltZSA9IChkYXRhKSAtPlxuICAgICAgdGVzdERhdGEgPSBbXVxuXG4gICAgICBhbmd1bGFyLmZvckVhY2ggZGF0YS5vbGRWLmdyb3VwdmVydGljZXMsICh2ZXJ0ZXgpIC0+XG4gICAgICAgIHZUaW1lID0gZGF0YS5vbGRWLmdyb3VwdmVydGljZXRpbWVzW3ZlcnRleC5ncm91cHZlcnRleGlkXVxuXG4gICAgICAgICMgY29uc29sZS5sb2cgdlRpbWUsIHZlcnRleC5ncm91cHZlcnRleGlkXG5cbiAgICAgICAgdGVzdERhdGEucHVzaCBcbiAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgbGFiZWw6IHZlcnRleC5ncm91cHZlcnRleG5hbWVcbiAgICAgICAgICAgIGNvbG9yOiBcIiMzZmI2ZDhcIlxuICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJTVEFSVEVEXCJdXG4gICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJFTkRFRFwiXVxuICAgICAgICAgICAgbGluazogdmVydGV4Lmdyb3VwdmVydGV4aWRcbiAgICAgICAgICBdXG5cbiAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLmNsaWNrKChkLCBpLCBkYXR1bSkgLT5cbiAgICAgICAgJHN0YXRlLmdvIFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIiwgeyBq
b2JpZDogZGF0YS5qaWQsIHZlcnRleElkOiBkLmxpbmsgfVxuXG4gICAgICApLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJVNcIilcbiAgICAgICAgIyB0aWNrVGltZTogZDMudGltZS5taWxsaXNlY29uZHNcbiAgICAgICAgdGlja0ludGVydmFsOiAxXG4gICAgICAgIHRpY2tTaXplOiAxXG4gICAgICB9KS5tYXJnaW4oeyBsZWZ0OiAwLCByaWdodDogMCwgdG9wOiAwLCBib3R0b206IDAgfSlcblxuICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKVxuICAgICAgLmRhdHVtKHRlc3REYXRhKVxuICAgICAgLmNhbGwoY2hhcnQpXG4gICAgICAuY2FsbCh6b29tKVxuXG4gICAgICBzdmdHID0gc3ZnLnNlbGVjdChcImdcIilcblxuICAgICAgem9vbS5vbihcInpvb21cIiwgLT5cbiAgICAgICAgZXYgPSBkMy5ldmVudFxuXG4gICAgICAgIHN2Z0cuc2VsZWN0QWxsKCdyZWN0JykuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIGV2LnRyYW5zbGF0ZVswXSArIFwiLDApIHNjYWxlKFwiICsgZXYuc2NhbGUgKyBcIiwxKVwiKVxuICAgICAgICBzdmdHLnNlbGVjdEFsbCgndGV4dCcpLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyBldi50cmFuc2xhdGVbMF0gKyBcIiwwKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIsMSlcIilcbiAgICAgIClcblxuICAgICAgYmJveCA9IHN2Z0dbMF1bMF0uZ2V0QkJveCgpXG4gICAgICBzdmcuYXR0cignaGVpZ2h0J
ywgYmJveC5oZWlnaHQgKyAzMClcblxuICAgIHNjb3BlLiR3YXRjaCBhdHRycy5qb2IsIChkYXRhKSAtPlxuICAgICAgYW5hbHl6ZVRpbWUoZGF0YSkgaWYgZGF0YVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICdqb2JQbGFuJywgKCR0aW1lb3V0KSAtPlxuICB0ZW1wbGF0ZTogXCJcbiAgICA8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+XG4gICAgPHN2ZyBjbGFzcz0ndG1wJyB3aWR0aD0nMScgaGVpZ2h0PScxJz48ZyAvPjwvc3ZnPlxuICAgIDxkaXYgY2xhc3M9J2J0bi1ncm91cCB6b29tLWJ1dHRvbnMnPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLWluJyBuZy1jbGljaz0nem9vbUluKCknPjxpIGNsYXNzPSdmYSBmYS1wbHVzJyAvPjwvYT5cbiAgICAgIDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+XG4gICAgPC9kaXY+XCJcblxuICBzY29wZTpcbiAgICBwbGFuOiAnPSdcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIG1haW5ab29tID0gZDMuYmVoYXZpb3Iuem9vbSgpXG4gICAgc3ViZ3JhcGhzID0gW11cbiAgICBqb2JpZCA9IGF0dHJzLmpvYmlkXG5cbiAgICBtYWluU3ZnRWxlbWVudCA9IGVsZW0uY2hpbG
RyZW4oKVswXVxuICAgIG1haW5HID0gZWxlbS5jaGlsZHJlbigpLmNoaWxkcmVuKClbMF1cbiAgICBtYWluVG1wRWxlbWVudCA9IGVsZW0uY2hpbGRyZW4oKVsxXVxuXG4gICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1haW5TdmdFbGVtZW50KVxuICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpXG4gICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpXG5cbiAgICAjIGFuZ3VsYXIuZWxlbWVudChtYWluRykuZW1wdHkoKVxuXG4gICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKVxuICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpXG5cbiAgICBzY29wZS56b29tSW4gPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKSA8IDIuOTlcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIHpvb20gb2JqZWN0XG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgKyAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW5ab29tLnNjYWxlIG1haW5ab29tLnNjYWxlKCkgKyAwLjFcbiAgICAgICAgbWFpblpvb20udHJhbnNsYXRlIFsgdjEsIHY
yIF1cbiAgICAgICAgXG4gICAgICAgICMgVHJhbnNmb3JtIHN2Z1xuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCJcblxuICAgIHNjb3BlLnpvb21PdXQgPSAtPlxuICAgICAgaWYgbWFpblpvb20uc2NhbGUoKSA+IDAuMzFcbiAgICAgICAgXG4gICAgICAgICMgQ2FsY3VsYXRlIGFuZCBzdG9yZSBuZXcgdmFsdWVzIGluIG1haW5ab29tIG9iamVjdFxuICAgICAgICBtYWluWm9vbS5zY2FsZSBtYWluWm9vbS5zY2FsZSgpIC0gMC4xXG4gICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpXG4gICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpXG4gICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZSBbIHYxLCB2MiBdXG4gICAgICAgIFxuICAgICAgICAjIFRyYW5zZm9ybSBzdmdcbiAgICAgICAgZDNtYWluU3ZnRy5hdHRyIFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiXG5cbiAgICAjY3JlYXRlIGEgbGFiZWwgb2YgYW4gZWRnZVxu
ICAgIGNyZWF0ZUxhYmVsRWRnZSA9IChlbCkgLT5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIlwiXG4gICAgICBpZiBlbC5zaGlwX3N0cmF0ZWd5PyBvciBlbC5sb2NhbF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxkaXYgY2xhc3M9J2VkZ2UtbGFiZWwnPlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneSAgaWYgZWwuc2hpcF9zdHJhdGVneT9cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiAoXCIgKyBlbC50ZW1wX21vZGUgKyBcIilcIiAgdW5sZXNzIGVsLnRlbXBfbW9kZSBpcyBgdW5kZWZpbmVkYFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiLDxicj5cIiArIGVsLmxvY2FsX3N0cmF0ZWd5ICB1bmxlc3MgZWwubG9jYWxfc3RyYXRlZ3kgaXMgYHVuZGVmaW5lZGBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiXG4gICAgICBsYWJlbFZhbHVlXG5cblxuICAgICMgdHJ1ZSwgaWYgdGhlIG5vZGUgaXMgYSBzcGVjaWFsIG5vZGUgZnJvbSBhbiBpdGVyYXRpb25cbiAgICBpc1NwZWNpYWxJdGVyYXRpb25Ob2RlID0gKGluZm8pIC0+XG4gICAgICAoaW5mbyBpcyBcInBhcnRpYWxTb2x1dGlvblwiIG9yIGluZm8gaXMgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIgb3IgaW5mbyBpcyBcIndvcmtzZXRcIiBvciBpbmZvIGlzIFwibmV4dFdvcmtzZXRcIiBvciBpbmZvIGlzIFwic29sdXRpb25TZXRcIiBvciBpbmZvIGlzIFwic29sdXRpb25EZWx0YVwiKVxuXG4gICAgZ2V0T
m9kZVR5cGUgPSAoZWwsIGluZm8pIC0+XG4gICAgICBpZiBpbmZvIGlzIFwibWlycm9yXCJcbiAgICAgICAgJ25vZGUtbWlycm9yJ1xuXG4gICAgICBlbHNlIGlmIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbylcbiAgICAgICAgJ25vZGUtaXRlcmF0aW9uJ1xuXG4gICAgICBlbHNlXG4gICAgICAgICAgJ25vZGUtbm9ybWFsJ1xuICAgICAgXG4gICAgIyBjcmVhdGVzIHRoZSBsYWJlbCBvZiBhIG5vZGUsIGluIGluZm8gaXMgc3RvcmVkLCB3aGV0aGVyIGl0IGlzIGEgc3BlY2lhbCBub2RlIChsaWtlIGEgbWlycm9yIGluIGFuIGl0ZXJhdGlvbilcbiAgICBjcmVhdGVMYWJlbE5vZGUgPSAoZWwsIGluZm8sIG1heFcsIG1heEgpIC0+XG4gICAgICBsYWJlbFZhbHVlID0gXCI8YSBocmVmPScjL2pvYnMvXCIgKyBqb2JpZCArIFwiL1wiICsgZWwuaWQgKyBcIicgY2xhc3M9J25vZGUtbGFiZWwgXCIgKyBnZXROb2RlVHlwZShlbCwgaW5mbykgKyBcIic+XCJcblxuICAgICAgIyBOb2RlbmFtZVxuICAgICAgaWYgaW5mbyBpcyBcIm1pcnJvclwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+TWlycm9yIG9mIFwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCJcbiAgICAgIGVsc2VcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5cIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiXG4gICAgICBpZiBlbC5kZXNjcmlwdGlvbiBpcyBcIlwiXG4gICAgIC
AgIGxhYmVsVmFsdWUgKz0gXCJcIlxuICAgICAgZWxzZVxuICAgICAgICBzdGVwTmFtZSA9IGVsLmRlc2NyaXB0aW9uXG4gICAgICAgIFxuICAgICAgICAjIGNsZWFuIHN0ZXBOYW1lXG4gICAgICAgIHN0ZXBOYW1lID0gc2hvcnRlblN0cmluZyhzdGVwTmFtZSlcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNCBjbGFzcz0nc3RlcC1uYW1lJz5cIiArIHN0ZXBOYW1lICsgXCI8L2g0PlwiXG4gICAgICBcbiAgICAgICMgSWYgdGhpcyBub2RlIGlzIGFuIFwiaXRlcmF0aW9uXCIgd2UgbmVlZCBhIGRpZmZlcmVudCBwYW5lbC1ib2R5XG4gICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICBsYWJlbFZhbHVlICs9IGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbihlbC5pZCwgbWF4VywgbWF4SClcbiAgICAgIGVsc2VcbiAgICAgICAgXG4gICAgICAgICMgT3RoZXJ3aXNlIGFkZCBpbmZvcyAgICBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5cIiArIGluZm8gKyBcIiBOb2RlPC9oNT5cIiAgaWYgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlBhcmFsbGVsaXNtOiBcIiArIGVsLnBhcmFsbGVsaXNtICsgXCI8L2g1PlwiICB1bmxlc3MgZWwucGFyYWxsZWxpc20gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1Pk9wZXJhdGlvbjogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLm9wZXJhdG9yX3N0cmF0ZWd5KSArIFwiPC9oNT5cIiAgdW5
sZXNzIGVsLm9wZXJhdG9yIGlzIGB1bmRlZmluZWRgXG4gICAgICBcbiAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2E+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuICAgICMgRXh0ZW5kcyB0aGUgbGFiZWwgb2YgYSBub2RlIHdpdGggYW4gYWRkaXRpb25hbCBzdmcgRWxlbWVudCB0byBwcmVzZW50IHRoZSBpdGVyYXRpb24uXG4gICAgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uID0gKGlkLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgc3ZnSUQgPSBcInN2Zy1cIiArIGlkXG5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIjxzdmcgY2xhc3M9J1wiICsgc3ZnSUQgKyBcIicgd2lkdGg9XCIgKyBtYXhXICsgXCIgaGVpZ2h0PVwiICsgbWF4SCArIFwiPjxnIC8+PC9zdmc+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuICAgICMgU3BsaXQgYSBzdHJpbmcgaW50byBtdWx0aXBsZSBsaW5lcyBzbyB0aGF0IGVhY2ggbGluZSBoYXMgbGVzcyB0aGFuIDMwIGxldHRlcnMuXG4gICAgc2hvcnRlblN0cmluZyA9IChzKSAtPlxuICAgICAgIyBtYWtlIHN1cmUgdGhhdCBuYW1lIGRvZXMgbm90IGNvbnRhaW4gYSA8IChiZWNhdXNlIG9mIGh0bWwpXG4gICAgICBpZiBzLmNoYXJBdCgwKSBpcyBcIjxcIlxuICAgICAgICBzID0gcy5yZXBsYWNlKFwiPFwiLCBcIiZsdDtcIilcbiAgICAgICAgcyA9IHMucmVwbGFjZShcIj5cIiwgXCImZ3Q7XCIpXG4gICAgICBzYnIgPSBcIlwiXG4gICAgICB3aGlsZSBzLmxlbmd0aCA+IDMwXG4gICAgICAgIHNiciA9
IHNiciArIHMuc3Vic3RyaW5nKDAsIDMwKSArIFwiPGJyPlwiXG4gICAgICAgIHMgPSBzLnN1YnN0cmluZygzMCwgcy5sZW5ndGgpXG4gICAgICBzYnIgPSBzYnIgKyBzXG4gICAgICBzYnJcblxuICAgIGNyZWF0ZU5vZGUgPSAoZywgZGF0YSwgZWwsIGlzUGFyZW50ID0gZmFsc2UsIG1heFcsIG1heEgpIC0+XG4gICAgICAjIGNyZWF0ZSBub2RlLCBzZW5kIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25zIGFib3V0IHRoZSBub2RlIGlmIGl0IGlzIGEgc3BlY2lhbCBvbmVcbiAgICAgIGlmIGVsLmlkIGlzIGRhdGEucGFydGlhbF9zb2x1dGlvblxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLm5leHRfcGFydGlhbF9zb2x1dGlvblxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiKVxuXG4gICAgICBlbHNlIGlmIGVsL
mlkIGlzIGRhdGEud29ya3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJ3b3Jrc2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwid29ya3NldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEubmV4dF93b3Jrc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRXb3Jrc2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFdvcmtzZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLnNvbHV0aW9uX3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJzb2x1dGlvblNldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uU2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5zb2x1dGlvbl9kZWx0YVxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIsIG
1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25EZWx0YVwiKVxuXG4gICAgICBlbHNlXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIlwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIlwiKVxuXG4gICAgY3JlYXRlRWRnZSA9IChnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZCkgLT5cbiAgICAgIHVubGVzcyBleGlzdGluZ05vZGVzLmluZGV4T2YocHJlZC5pZCkgaXMgLTFcbiAgICAgICAgZy5zZXRFZGdlIHByZWQuaWQsIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UocHJlZClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGFycm93aGVhZDogJ25vcm1hbCdcblxuICAgICAgZWxzZVxuICAgICAgICBtaXNzaW5nTm9kZSA9IHNlYXJjaEZvck5vZGUoZGF0YSwgcHJlZC5pZClcbiAgICAgICAgdW5sZXNzICFtaXNzaW5nTm9kZSBvciBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgaXMgdHJ1ZVxuICAgICAgICAgIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9IHRydWVcbiAgICAgICAgICBnLnNldE5vZGUgbWlzc2luZ05vZGUuaWQsXG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2R
lKG1pc3NpbmdOb2RlLCBcIm1pcnJvclwiKVxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShtaXNzaW5nTm9kZSwgJ21pcnJvcicpXG5cbiAgICAgICAgICBnLnNldEVkZ2UgbWlzc2luZ05vZGUuaWQsIGVsLmlkLFxuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShtaXNzaW5nTm9kZSlcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG5cbiAgICBsb2FkSnNvblRvRGFncmUgPSAoZywgZGF0YSkgLT5cbiAgICAgIGV4aXN0aW5nTm9kZXMgPSBbXVxuXG4gICAgICBpZiBkYXRhLm5vZGVzP1xuICAgICAgICAjIFRoaXMgaXMgdGhlIG5vcm1hbCBqc29uIGRhdGFcbiAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5ub2Rlc1xuXG4gICAgICBlbHNlXG4gICAgICAgICMgVGhpcyBpcyBhbiBpdGVyYXRpb24sIHdlIG5vdyBzdG9yZSBzcGVjaWFsIGl0ZXJhdGlvbiBub2RlcyBpZiBwb3NzaWJsZVxuICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgaXNQYXJlbnQgPSB0cnVlXG5cbiAgICAgIGZvciBlbCBpbiB0b0l0ZXJhdGVcbiAgICAgICAgbWF4VyA9IDBcbiAgICAgICAgbWF4SCA9IDBcblxuICAgICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uXG4gICAgICAgICAgc2cgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7IG11bHRpZ3JhcGg6IHRydWUsIGNvbXBvdW5kOiB0cnVlIH0pLnNldEdyYXBoKHtc
biAgICAgICAgICAgIG5vZGVzZXA6IDIwXG4gICAgICAgICAgICBlZGdlc2VwOiAwXG4gICAgICAgICAgICByYW5rc2VwOiAyMFxuICAgICAgICAgICAgcmFua2RpcjogXCJMUlwiXG4gICAgICAgICAgICBtYXJnaW54OiAxMFxuICAgICAgICAgICAgbWFyZ2lueTogMTBcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICBzdWJncmFwaHNbZWwuaWRdID0gc2dcblxuICAgICAgICAgIGxvYWRKc29uVG9EYWdyZShzZywgZWwpXG5cbiAgICAgICAgICByID0gbmV3IGRhZ3JlRDMucmVuZGVyKClcbiAgICAgICAgICBkM3RtcFN2Zy5zZWxlY3QoJ2cnKS5jYWxsKHIsIHNnKVxuICAgICAgICAgIG1heFcgPSBzZy5ncmFwaCgpLndpZHRoXG4gICAgICAgICAgbWF4SCA9IHNnLmdyYXBoKCkuaGVpZ2h0XG5cbiAgICAgICAgICBhbmd1bGFyLmVsZW1lbnQobWFpblRtcEVsZW1lbnQpLmVtcHR5KClcblxuICAgICAgICBjcmVhdGVOb2RlKGcsIGRhdGEsIGVsLCBpc1BhcmVudCwgbWF4VywgbWF4SClcblxuICAgICAgICBleGlzdGluZ05vZGVzLnB1c2ggZWwuaWRcbiAgICAgICAgXG4gICAgICAgICMgY3JlYXRlIGVkZ2VzIGZyb20gaW5wdXRzIHRvIGN1cnJlbnQgbm9kZVxuICAgICAgICBpZiBlbC5pbnB1dHM/XG4gICAgICAgICAgZm9yIHByZWQgaW4gZWwuaW5wdXRzXG4gICAgICAgICAgICBjcmVhdGVFZGdlKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKVxuXG4gICAgICBnXG5cbiAgICAjIHNlYXJjaGVzIGluI
HRoZSBnbG9iYWwgSlNPTkRhdGEgZm9yIHRoZSBub2RlIHdpdGggdGhlIGdpdmVuIGlkXG4gICAgc2VhcmNoRm9yTm9kZSA9IChkYXRhLCBub2RlSUQpIC0+XG4gICAgICBmb3IgaSBvZiBkYXRhLm5vZGVzXG4gICAgICAgIGVsID0gZGF0YS5ub2Rlc1tpXVxuICAgICAgICByZXR1cm4gZWwgIGlmIGVsLmlkIGlzIG5vZGVJRFxuICAgICAgICBcbiAgICAgICAgIyBsb29rIGZvciBub2RlcyB0aGF0IGFyZSBpbiBpdGVyYXRpb25zXG4gICAgICAgIGlmIGVsLnN0ZXBfZnVuY3Rpb24/XG4gICAgICAgICAgZm9yIGogb2YgZWwuc3RlcF9mdW5jdGlvblxuICAgICAgICAgICAgcmV0dXJuIGVsLnN0ZXBfZnVuY3Rpb25bal0gIGlmIGVsLnN0ZXBfZnVuY3Rpb25bal0uaWQgaXMgbm9kZUlEXG5cbiAgICBkcmF3R3JhcGggPSAoZGF0YSkgLT5cbiAgICAgIGcgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7IG11bHRpZ3JhcGg6IHRydWUsIGNvbXBvdW5kOiB0cnVlIH0pLnNldEdyYXBoKHtcbiAgICAgICAgbm9kZXNlcDogNzBcbiAgICAgICAgZWRnZXNlcDogMFxuICAgICAgICByYW5rc2VwOiA1MFxuICAgICAgICByYW5rZGlyOiBcIkxSXCJcbiAgICAgICAgbWFyZ2lueDogNDBcbiAgICAgICAgbWFyZ2lueTogNDBcbiAgICAgICAgfSlcblxuICAgICAgbG9hZEpzb25Ub0RhZ3JlKGcsIGRhdGEpXG5cbiAgICAgIHJlbmRlcmVyID0gbmV3IGRhZ3JlRDMucmVuZGVyKClcbiAgICAgIGQzbWFpblN2Z0cuY2FsbChyZW5kZX
JlciwgZylcblxuICAgICAgZm9yIGksIHNnIG9mIHN1YmdyYXBoc1xuICAgICAgICBkM21haW5Tdmcuc2VsZWN0KCdzdmcuc3ZnLScgKyBpICsgJyBnJykuY2FsbChyZW5kZXJlciwgc2cpXG5cbiAgICAgIG5ld1NjYWxlID0gMC41XG5cbiAgICAgIHhDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLndpZHRoKCkgLSBnLmdyYXBoKCkud2lkdGggKiBuZXdTY2FsZSkgLyAyKVxuICAgICAgeUNlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkuaGVpZ2h0KCkgLSBnLmdyYXBoKCkuaGVpZ2h0ICogbmV3U2NhbGUpIC8gMilcblxuICAgICAgbWFpblpvb20uc2NhbGUobmV3U2NhbGUpLnRyYW5zbGF0ZShbeENlbnRlck9mZnNldCwgeUNlbnRlck9mZnNldF0pXG5cbiAgICAgIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHhDZW50ZXJPZmZzZXQgKyBcIiwgXCIgKyB5Q2VudGVyT2Zmc2V0ICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKVxuXG4gICAgICBtYWluWm9vbS5vbihcInpvb21cIiwgLT5cbiAgICAgICAgZXYgPSBkMy5ldmVudFxuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyBldi50cmFuc2xhdGUgKyBcIikgc2NhbGUoXCIgKyBldi5zY2FsZSArIFwiKVwiXG4gICAgICApXG4gICAgICB
tYWluWm9vbShkM21haW5TdmcpXG5cbiAgICBzY29wZS4kd2F0Y2ggYXR0cnMucGxhbiwgKG5ld1BsYW4pIC0+XG4gICAgICBkcmF3R3JhcGgobmV3UGxhbikgaWYgbmV3UGxhblxuXG4gICAgcmV0dXJuXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ3ZlcnRleCcsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCIsXG4gICAgc2NvcGU6IHtcbiAgICAgIGRhdGE6IFwiPVwiXG4gICAgfSxcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbSwgYXR0cnMpIHtcbiAgICAgIHZhciBhbmFseXplVGltZSwgY29udGFpbmVyVywgc3ZnRWwsIHpvb207XG4gICAgICB6b29tID0gZDMuYmVoYXZpb3Iuem9vbSgpO1xuICAgICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcgLSAxNik7XG4gICAgICBhbmFseXplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGJib3gsIGNoYXJ0LCBzdmcsIHN2Z0csIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICBhbmd1bGFyLmZvckVhY2goZGF0YS5ncm91cHZlcnRleC5ncm91cG1lbWJlcnMsIGZ1bmN0aW9u
KHZlcnRleCwgaSkge1xuICAgICAgICAgIHZhciB2VGltZTtcbiAgICAgICAgICB2VGltZSA9IGRhdGEudmVydGljZXRpbWVzW3ZlcnRleC52ZXJ0ZXhpZF07XG4gICAgICAgICAgcmV0dXJuIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgICAgbGFiZWw6IHZlcnRleC52ZXJ0ZXhpbnN0YW5jZW5hbWUgKyBcIiAoXCIgKyBpICsgXCIpXCIsXG4gICAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiIzY2NlwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiU0NIRURVTEVEXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkRFUExPWUlOR1wiXSAqIDEwMFxuICAgICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiRGVwbG95aW5nXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiI2FhYVwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiREVQTE9ZSU5HXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIlJVTk5JTkdcIl0gKiAxMDBcbiAgICAgICAgICAgICAgfSwge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlJ1bm5pbmdcIixcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjZGRkXCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfd
GltZTogdlRpbWVbXCJSVU5OSU5HXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkZJTklTSEVEXCJdICogMTAwXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIF1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLnRpY2tGb3JtYXQoe1xuICAgICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlU1wiKSxcbiAgICAgICAgICB0aWNrSW50ZXJ2YWw6IDEsXG4gICAgICAgICAgdGlja1NpemU6IDFcbiAgICAgICAgfSkubGFiZWxGb3JtYXQoZnVuY3Rpb24obGFiZWwpIHtcbiAgICAgICAgICByZXR1cm4gbGFiZWw7XG4gICAgICAgIH0pLm1hcmdpbih7XG4gICAgICAgICAgbGVmdDogMTAwLFxuICAgICAgICAgIHJpZ2h0OiAwLFxuICAgICAgICAgIHRvcDogMCxcbiAgICAgICAgICBib3R0b206IDBcbiAgICAgICAgfSk7XG4gICAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbCkuZGF0dW0odGVzdERhdGEpLmNhbGwoY2hhcnQpLmNhbGwoem9vbSk7XG4gICAgICAgIHN2Z0cgPSBzdmcuc2VsZWN0KFwiZ1wiKTtcbiAgICAgICAgem9vbS5vbihcInpvb21cIiwgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgdmFyIGV2O1xuICAgICAgICAgIGV2ID0gZDMuZXZlbnQ7XG4gICAgICAgICAgc3ZnRy5zZWxlY3RBbGwoJ3JlY3QnKS5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgZXYudHJhbn
NsYXRlWzBdICsgXCIsMCkgc2NhbGUoXCIgKyBldi5zY2FsZSArIFwiLDEpXCIpO1xuICAgICAgICAgIHJldHVybiBzdmdHLnNlbGVjdEFsbCgndGV4dCcpLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyBldi50cmFuc2xhdGVbMF0gKyBcIiwwKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIsMSlcIik7XG4gICAgICAgIH0pO1xuICAgICAgICBiYm94ID0gc3ZnR1swXVswXS5nZXRCQm94KCk7XG4gICAgICAgIHJldHVybiBzdmcuYXR0cignaGVpZ2h0JywgYmJveC5oZWlnaHQgKyAzMCk7XG4gICAgICB9O1xuICAgICAgYW5hbHl6ZVRpbWUoc2NvcGUuZGF0YSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0aW1lbGluZScsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIixcbiAgICBzY29wZToge1xuICAgICAgam9iOiBcIj1cIlxuICAgIH0sXG4gICAgbGluazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgYW5hbHl6ZVRpbWUsIGNvbnRhaW5lclcsIHN2Z0VsLCB6b29tO1xuICAgICAgem9vbSA9IGQzLmJlaGF2aW9yLnpvb20oKTtcbiAgICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChzdmdFbCkuYXR0cignd2lkdGgnLCB
jb250YWluZXJXIC0gMTYpO1xuICAgICAgYW5hbHl6ZVRpbWUgPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIHZhciBiYm94LCBjaGFydCwgc3ZnLCBzdmdHLCB0ZXN0RGF0YTtcbiAgICAgICAgdGVzdERhdGEgPSBbXTtcbiAgICAgICAgYW5ndWxhci5mb3JFYWNoKGRhdGEub2xkVi5ncm91cHZlcnRpY2VzLCBmdW5jdGlvbih2ZXJ0ZXgpIHtcbiAgICAgICAgICB2YXIgdlRpbWU7XG4gICAgICAgICAgdlRpbWUgPSBkYXRhLm9sZFYuZ3JvdXB2ZXJ0aWNldGltZXNbdmVydGV4Lmdyb3VwdmVydGV4aWRdO1xuICAgICAgICAgIHJldHVybiB0ZXN0RGF0YS5wdXNoKHtcbiAgICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBsYWJlbDogdmVydGV4Lmdyb3VwdmVydGV4bmFtZSxcbiAgICAgICAgICAgICAgICBjb2xvcjogXCIjM2ZiNmQ4XCIsXG4gICAgICAgICAgICAgICAgc3RhcnRpbmdfdGltZTogdlRpbWVbXCJTVEFSVEVEXCJdLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkVOREVEXCJdLFxuICAgICAgICAgICAgICAgIGxpbms6IHZlcnRleC5ncm91cHZlcnRleGlkXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIF1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLmNsaWNrKGZ1bmN0aW9uKGQsIGksIGRhdHVtKSB7XG4gICAgICAgICAgcmV0dXJuICRzdGF0ZS5nbyhc
InNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHtcbiAgICAgICAgICAgIGpvYmlkOiBkYXRhLmppZCxcbiAgICAgICAgICAgIHZlcnRleElkOiBkLmxpbmtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSkudGlja0Zvcm1hdCh7XG4gICAgICAgICAgZm9ybWF0OiBkMy50aW1lLmZvcm1hdChcIiVTXCIpLFxuICAgICAgICAgIHRpY2tJbnRlcnZhbDogMSxcbiAgICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgICB9KS5tYXJnaW4oe1xuICAgICAgICAgIGxlZnQ6IDAsXG4gICAgICAgICAgcmlnaHQ6IDAsXG4gICAgICAgICAgdG9wOiAwLFxuICAgICAgICAgIGJvdHRvbTogMFxuICAgICAgICB9KTtcbiAgICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCkuY2FsbCh6b29tKTtcbiAgICAgICAgc3ZnRyA9IHN2Zy5zZWxlY3QoXCJnXCIpO1xuICAgICAgICB6b29tLm9uKFwiem9vbVwiLCBmdW5jdGlvbigpIHtcbiAgICAgICAgICB2YXIgZXY7XG4gICAgICAgICAgZXYgPSBkMy5ldmVudDtcbiAgICAgICAgICBzdmdHLnNlbGVjdEFsbCgncmVjdCcpLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyBldi50cmFuc2xhdGVbMF0gKyBcIiwwKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIsMSlcIik7XG4gICAgICAgICAgcmV0dXJuIHN2Z0cuc2VsZWN0QWxsKCd0ZXh0JykuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIGV2LnRyYW5zb
GF0ZVswXSArIFwiLDApIHNjYWxlKFwiICsgZXYuc2NhbGUgKyBcIiwxKVwiKTtcbiAgICAgICAgfSk7XG4gICAgICAgIGJib3ggPSBzdmdHWzBdWzBdLmdldEJCb3goKTtcbiAgICAgICAgcmV0dXJuIHN2Zy5hdHRyKCdoZWlnaHQnLCBiYm94LmhlaWdodCArIDMwKTtcbiAgICAgIH07XG4gICAgICBzY29wZS4kd2F0Y2goYXR0cnMuam9iLCBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgcmV0dXJuIGFuYWx5emVUaW1lKGRhdGEpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH07XG59KS5kaXJlY3RpdmUoJ2pvYlBsYW4nLCBmdW5jdGlvbigkdGltZW91dCkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J2dyYXBoJyB3aWR0aD0nNTAwJyBoZWlnaHQ9JzQwMCc+PGcgLz48L3N2Zz4gPHN2ZyBjbGFzcz0ndG1wJyB3aWR0aD0nMScgaGVpZ2h0PScxJz48ZyAvPjwvc3ZnPiA8ZGl2IGNsYXNzPSdidG4tZ3JvdXAgem9vbS1idXR0b25zJz4gPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLWluJyBuZy1jbGljaz0nem9vbUluKCknPjxpIGNsYXNzPSdmYSBmYS1wbHVzJyAvPjwvYT4gPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLW91dCcgbmctY2xpY2s9J3pvb21PdXQoKSc+PGkgY2xhc3M9J2ZhIGZhLW1pbnVzJyAvPjwvYT4gPC9kaXY+XCIsXG4gICAgc2NvcGU6IHtcbiAgICAgIHBsYW46ICc9J1xuICAgIH0sXG4gICAgbG
luazogZnVuY3Rpb24oc2NvcGUsIGVsZW0sIGF0dHJzKSB7XG4gICAgICB2YXIgY29udGFpbmVyVywgY3JlYXRlRWRnZSwgY3JlYXRlTGFiZWxFZGdlLCBjcmVhdGVMYWJlbE5vZGUsIGNyZWF0ZU5vZGUsIGQzbWFpblN2ZywgZDNtYWluU3ZnRywgZDN0bXBTdmcsIGRyYXdHcmFwaCwgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uLCBnZXROb2RlVHlwZSwgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZSwgam9iaWQsIGxvYWRKc29uVG9EYWdyZSwgbWFpbkcsIG1haW5TdmdFbGVtZW50LCBtYWluVG1wRWxlbWVudCwgbWFpblpvb20sIHNlYXJjaEZvck5vZGUsIHNob3J0ZW5TdHJpbmcsIHN1YmdyYXBocztcbiAgICAgIG1haW5ab29tID0gZDMuYmVoYXZpb3Iuem9vbSgpO1xuICAgICAgc3ViZ3JhcGhzID0gW107XG4gICAgICBqb2JpZCA9IGF0dHJzLmpvYmlkO1xuICAgICAgbWFpblN2Z0VsZW1lbnQgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBtYWluRyA9IGVsZW0uY2hpbGRyZW4oKS5jaGlsZHJlbigpWzBdO1xuICAgICAgbWFpblRtcEVsZW1lbnQgPSBlbGVtLmNoaWxkcmVuKClbMV07XG4gICAgICBkM21haW5TdmcgPSBkMy5zZWxlY3QobWFpblN2Z0VsZW1lbnQpO1xuICAgICAgZDNtYWluU3ZnRyA9IGQzLnNlbGVjdChtYWluRyk7XG4gICAgICBkM3RtcFN2ZyA9IGQzLnNlbGVjdChtYWluVG1wRWxlbWVudCk7XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW5
0KGVsZW0uY2hpbGRyZW4oKVswXSkud2lkdGgoY29udGFpbmVyVyk7XG4gICAgICBzY29wZS56b29tSW4gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHRyYW5zbGF0ZSwgdjEsIHYyO1xuICAgICAgICBpZiAobWFpblpvb20uc2NhbGUoKSA8IDIuOTkpIHtcbiAgICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKTtcbiAgICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpICsgMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICBtYWluWm9vbS5zY2FsZShtYWluWm9vbS5zY2FsZSgpICsgMC4xKTtcbiAgICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUoW3YxLCB2Ml0pO1xuICAgICAgICAgIHJldHVybiBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgc2NvcGUuem9vbU91dCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgdHJhbnNsYXRlLCB2MSwgdjI7XG4gICAgICAgIGlmIChtYWluWm9vbS5zY2FsZSgpID4gMC4zMSkge1xuICAgICAgICAgIG1haW5ab29tLnNjYWxlKG1haW5ab29tLnNjYWxlKCkgLSAwLjEpO1xu
ICAgICAgICAgIHRyYW5zbGF0ZSA9IG1haW5ab29tLnRyYW5zbGF0ZSgpO1xuICAgICAgICAgIHYxID0gdHJhbnNsYXRlWzBdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpO1xuICAgICAgICAgIHYyID0gdHJhbnNsYXRlWzFdICogKG1haW5ab29tLnNjYWxlKCkgLSAwLjEgLyAobWFpblpvb20uc2NhbGUoKSkpO1xuICAgICAgICAgIG1haW5ab29tLnRyYW5zbGF0ZShbdjEsIHYyXSk7XG4gICAgICAgICAgcmV0dXJuIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIik7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjcmVhdGVMYWJlbEVkZ2UgPSBmdW5jdGlvbihlbCkge1xuICAgICAgICB2YXIgbGFiZWxWYWx1ZTtcbiAgICAgICAgbGFiZWxWYWx1ZSA9IFwiXCI7XG4gICAgICAgIGlmICgoZWwuc2hpcF9zdHJhdGVneSAhPSBudWxsKSB8fCAoZWwubG9jYWxfc3RyYXRlZ3kgIT0gbnVsbCkpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGRpdiBjbGFzcz0nZWRnZS1sYWJlbCc+XCI7XG4gICAgICAgICAgaWYgKGVsLnNoaXBfc3RyYXRlZ3kgIT0gbnVsbCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBlbC5zaGlwX3N0cmF0ZWd5O1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwudGVtcF9tb2RlICE9PSB1bmRlZ
mluZWQpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCIgKFwiICsgZWwudGVtcF9tb2RlICsgXCIpXCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5sb2NhbF9zdHJhdGVneSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiLDxicj5cIiArIGVsLmxvY2FsX3N0cmF0ZWd5O1xuICAgICAgICAgIH1cbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPC9kaXY+XCI7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGxhYmVsVmFsdWU7XG4gICAgICB9O1xuICAgICAgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZSA9IGZ1bmN0aW9uKGluZm8pIHtcbiAgICAgICAgcmV0dXJuIGluZm8gPT09IFwicGFydGlhbFNvbHV0aW9uXCIgfHwgaW5mbyA9PT0gXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIgfHwgaW5mbyA9PT0gXCJ3b3Jrc2V0XCIgfHwgaW5mbyA9PT0gXCJuZXh0V29ya3NldFwiIHx8IGluZm8gPT09IFwic29sdXRpb25TZXRcIiB8fCBpbmZvID09PSBcInNvbHV0aW9uRGVsdGFcIjtcbiAgICAgIH07XG4gICAgICBnZXROb2RlVHlwZSA9IGZ1bmN0aW9uKGVsLCBpbmZvKSB7XG4gICAgICAgIGlmIChpbmZvID09PSBcIm1pcnJvclwiKSB7XG4gICAgICAgICAgcmV0dXJuICdub2RlLW1pcnJvcic7XG4gICAgICAgIH0gZWxzZSBpZiAoaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKSkge1xuICAgICAgICAgIHJldHVybiAnbm9kZS1pdGVyYXRpb24nO1xuICAgICAgICB9IG
Vsc2Uge1xuICAgICAgICAgIHJldHVybiAnbm9kZS1ub3JtYWwnO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlTGFiZWxOb2RlID0gZnVuY3Rpb24oZWwsIGluZm8sIG1heFcsIG1heEgpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWUsIHN0ZXBOYW1lO1xuICAgICAgICBsYWJlbFZhbHVlID0gXCI8YSBocmVmPScjL2pvYnMvXCIgKyBqb2JpZCArIFwiL1wiICsgZWwuaWQgKyBcIicgY2xhc3M9J25vZGUtbGFiZWwgXCIgKyBnZXROb2RlVHlwZShlbCwgaW5mbykgKyBcIic+XCI7XG4gICAgICAgIGlmIChpbmZvID09PSBcIm1pcnJvclwiKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5NaXJyb3Igb2YgXCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPlwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCI7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVsLmRlc2NyaXB0aW9uID09PSBcIlwiKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHN0ZXBOYW1lID0gZWwuZGVzY3JpcHRpb247XG4gICAgICAgICAgc3RlcE5hbWUgPSBzaG9ydGVuU3RyaW5nKHN0ZXBOYW1lKTtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg0IGNsYXNzPSdzdGVwLW5hbWUnPlwiICsgc3RlcE5
hbWUgKyBcIjwvaDQ+XCI7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVsLnN0ZXBfZnVuY3Rpb24gIT0gbnVsbCkge1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uKGVsLmlkLCBtYXhXLCBtYXhIKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpZiAoaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKSkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5cIiArIGluZm8gKyBcIiBOb2RlPC9oNT5cIjtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLnBhcmFsbGVsaXNtICE9PSBcIlwiKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlBhcmFsbGVsaXNtOiBcIiArIGVsLnBhcmFsbGVsaXNtICsgXCI8L2g1PlwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwub3BlcmF0b3IgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5PcGVyYXRpb246IFwiICsgc2hvcnRlblN0cmluZyhlbC5vcGVyYXRvcl9zdHJhdGVneSkgKyBcIjwvaDU+XCI7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8L2E+XCI7XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiA9IGZ1bmN0aW9uKGlkLCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIHZhciBsYWJlbFZhbHVlLCBzdmdJRDtc
biAgICAgICAgc3ZnSUQgPSBcInN2Zy1cIiArIGlkO1xuICAgICAgICBsYWJlbFZhbHVlID0gXCI8c3ZnIGNsYXNzPSdcIiArIHN2Z0lEICsgXCInIHdpZHRoPVwiICsgbWF4VyArIFwiIGhlaWdodD1cIiArIG1heEggKyBcIj48ZyAvPjwvc3ZnPlwiO1xuICAgICAgICByZXR1cm4gbGFiZWxWYWx1ZTtcbiAgICAgIH07XG4gICAgICBzaG9ydGVuU3RyaW5nID0gZnVuY3Rpb24ocykge1xuICAgICAgICB2YXIgc2JyO1xuICAgICAgICBpZiAocy5jaGFyQXQoMCkgPT09IFwiPFwiKSB7XG4gICAgICAgICAgcyA9IHMucmVwbGFjZShcIjxcIiwgXCImbHQ7XCIpO1xuICAgICAgICAgIHMgPSBzLnJlcGxhY2UoXCI+XCIsIFwiJmd0O1wiKTtcbiAgICAgICAgfVxuICAgICAgICBzYnIgPSBcIlwiO1xuICAgICAgICB3aGlsZSAocy5sZW5ndGggPiAzMCkge1xuICAgICAgICAgIHNiciA9IHNiciArIHMuc3Vic3RyaW5nKDAsIDMwKSArIFwiPGJyPlwiO1xuICAgICAgICAgIHMgPSBzLnN1YnN0cmluZygzMCwgcy5sZW5ndGgpO1xuICAgICAgICB9XG4gICAgICAgIHNiciA9IHNiciArIHM7XG4gICAgICAgIHJldHVybiBzYnI7XG4gICAgICB9O1xuICAgICAgY3JlYXRlTm9kZSA9IGZ1bmN0aW9uKGcsIGRhdGEsIGVsLCBpc1BhcmVudCwgbWF4VywgbWF4SCkge1xuICAgICAgICBpZiAoaXNQYXJlbnQgPT0gbnVsbCkge1xuICAgICAgICAgIGlzUGFyZW50ID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVsLmlkID09P
SBkYXRhLnBhcnRpYWxfc29sdXRpb24pIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5uZXh0X3BhcnRpYWxfc29sdXRpb24pIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLndvcmtzZXQpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIndvcmtzZXRcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cG
UoZWwsIFwid29ya3NldFwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLm5leHRfd29ya3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFdvcmtzZXRcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFdvcmtzZXRcIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5zb2x1dGlvbl9zZXQpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uU2V0XCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uU2V0XCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEuc29sdXRpb25fZGVsdGEpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXROb2RlKGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcInNvbHV0aW9uRGVsdGFcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1
sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25EZWx0YVwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwiXCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcIlwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlRWRnZSA9IGZ1bmN0aW9uKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKSB7XG4gICAgICAgIHZhciBtaXNzaW5nTm9kZTtcbiAgICAgICAgaWYgKGV4aXN0aW5nTm9kZXMuaW5kZXhPZihwcmVkLmlkKSAhPT0gLTEpIHtcbiAgICAgICAgICByZXR1cm4gZy5zZXRFZGdlKHByZWQuaWQsIGVsLmlkLCB7XG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxFZGdlKHByZWQpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBhcnJvd2hlYWQ6ICdub3JtYWwnXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbWlzc2luZ05vZGUgPSBzZWFyY2hGb3JOb2RlKGRhdGEsIHByZWQuaWQpO1xuICAgICAgICAgIGlmICghKCFtaXNzaW5nTm9kZSB8fCBtaXNzaW5nTm9kZS5hbHJlYWR5QWRk
ZWQgPT09IHRydWUpKSB7XG4gICAgICAgICAgICBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgPSB0cnVlO1xuICAgICAgICAgICAgZy5zZXROb2RlKG1pc3NpbmdOb2RlLmlkLCB7XG4gICAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUobWlzc2luZ05vZGUsIFwibWlycm9yXCIpLFxuICAgICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShtaXNzaW5nTm9kZSwgJ21pcnJvcicpXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHJldHVybiBnLnNldEVkZ2UobWlzc2luZ05vZGUuaWQsIGVsLmlkLCB7XG4gICAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UobWlzc2luZ05vZGUpLFxuICAgICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgbG9hZEpzb25Ub0RhZ3JlID0gZnVuY3Rpb24oZywgZGF0YSkge1xuICAgICAgICB2YXIgZWwsIGV4aXN0aW5nTm9kZXMsIGlzUGFyZW50LCBrLCBsLCBsZW4sIGxlbjEsIG1heEgsIG1heFcsIHByZWQsIHIsIHJlZiwgc2csIHRvSXRlcmF0ZTtcbiAgICAgICAgZXhpc3RpbmdOb2RlcyA9IFtdO1xuICAgICAgICBpZiAoZGF0YS5ub2RlcyAhPSBudWxsKSB7XG4gICAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5ub2RlcztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0b0l0Z
XJhdGUgPSBkYXRhLnN0ZXBfZnVuY3Rpb247XG4gICAgICAgICAgaXNQYXJlbnQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIGZvciAoayA9IDAsIGxlbiA9IHRvSXRlcmF0ZS5sZW5ndGg7IGsgPCBsZW47IGsrKykge1xuICAgICAgICAgIGVsID0gdG9JdGVyYXRlW2tdO1xuICAgICAgICAgIG1heFcgPSAwO1xuICAgICAgICAgIG1heEggPSAwO1xuICAgICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uKSB7XG4gICAgICAgICAgICBzZyA9IG5ldyBkYWdyZUQzLmdyYXBobGliLkdyYXBoKHtcbiAgICAgICAgICAgICAgbXVsdGlncmFwaDogdHJ1ZSxcbiAgICAgICAgICAgICAgY29tcG91bmQ6IHRydWVcbiAgICAgICAgICAgIH0pLnNldEdyYXBoKHtcbiAgICAgICAgICAgICAgbm9kZXNlcDogMjAsXG4gICAgICAgICAgICAgIGVkZ2VzZXA6IDAsXG4gICAgICAgICAgICAgIHJhbmtzZXA6IDIwLFxuICAgICAgICAgICAgICByYW5rZGlyOiBcIkxSXCIsXG4gICAgICAgICAgICAgIG1hcmdpbng6IDEwLFxuICAgICAgICAgICAgICBtYXJnaW55OiAxMFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBzdWJncmFwaHNbZWwuaWRdID0gc2c7XG4gICAgICAgICAgICBsb2FkSnNvblRvRGFncmUoc2csIGVsKTtcbiAgICAgICAgICAgIHIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKTtcbiAgICAgICAgICAgIGQzdG1wU3ZnLnNlbGVjdCgnZycpLmNhbGwociwgc2cpO1xuICAgICAgICAgICAgbWF4VyA9IHNnLmdyYXBoKC
kud2lkdGg7XG4gICAgICAgICAgICBtYXhIID0gc2cuZ3JhcGgoKS5oZWlnaHQ7XG4gICAgICAgICAgICBhbmd1bGFyLmVsZW1lbnQobWFpblRtcEVsZW1lbnQpLmVtcHR5KCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNyZWF0ZU5vZGUoZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXhXLCBtYXhIKTtcbiAgICAgICAgICBleGlzdGluZ05vZGVzLnB1c2goZWwuaWQpO1xuICAgICAgICAgIGlmIChlbC5pbnB1dHMgIT0gbnVsbCkge1xuICAgICAgICAgICAgcmVmID0gZWwuaW5wdXRzO1xuICAgICAgICAgICAgZm9yIChsID0gMCwgbGVuMSA9IHJlZi5sZW5ndGg7IGwgPCBsZW4xOyBsKyspIHtcbiAgICAgICAgICAgICAgcHJlZCA9IHJlZltsXTtcbiAgICAgICAgICAgICAgY3JlYXRlRWRnZShnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBnO1xuICAgICAgfTtcbiAgICAgIHNlYXJjaEZvck5vZGUgPSBmdW5jdGlvbihkYXRhLCBub2RlSUQpIHtcbiAgICAgICAgdmFyIGVsLCBpLCBqO1xuICAgICAgICBmb3IgKGkgaW4gZGF0YS5ub2Rlcykge1xuICAgICAgICAgIGVsID0gZGF0YS5ub2Rlc1tpXTtcbiAgICAgICAgICBpZiAoZWwuaWQgPT09IG5vZGVJRCkge1xuICAgICAgICAgICAgcmV0dXJuIGVsO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwuc3RlcF9mdW5jdGlvbiAhPSBudWxsKSB7XG4
gICAgICAgICAgICBmb3IgKGogaW4gZWwuc3RlcF9mdW5jdGlvbikge1xuICAgICAgICAgICAgICBpZiAoZWwuc3RlcF9mdW5jdGlvbltqXS5pZCA9PT0gbm9kZUlEKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGVsLnN0ZXBfZnVuY3Rpb25bal07XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBkcmF3R3JhcGggPSBmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgIHZhciBnLCBpLCBuZXdTY2FsZSwgcmVuZGVyZXIsIHNnLCB4Q2VudGVyT2Zmc2V0LCB5Q2VudGVyT2Zmc2V0O1xuICAgICAgICBnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoe1xuICAgICAgICAgIG11bHRpZ3JhcGg6IHRydWUsXG4gICAgICAgICAgY29tcG91bmQ6IHRydWVcbiAgICAgICAgfSkuc2V0R3JhcGgoe1xuICAgICAgICAgIG5vZGVzZXA6IDcwLFxuICAgICAgICAgIGVkZ2VzZXA6IDAsXG4gICAgICAgICAgcmFua3NlcDogNTAsXG4gICAgICAgICAgcmFua2RpcjogXCJMUlwiLFxuICAgICAgICAgIG1hcmdpbng6IDQwLFxuICAgICAgICAgIG1hcmdpbnk6IDQwXG4gICAgICAgIH0pO1xuICAgICAgICBsb2FkSnNvblRvRGFncmUoZywgZGF0YSk7XG4gICAgICAgIHJlbmRlcmVyID0gbmV3IGRhZ3JlRDMucmVuZGVyKCk7XG4gICAgICAgIGQzbWFpblN2Z0cuY2FsbChyZW5kZXJlciwgZyk7XG4gICAgICAgIGZvciAoaSBpbiBzdWJncmFwaHMpIHtcbiAg
ICAgICAgICBzZyA9IHN1YmdyYXBoc1tpXTtcbiAgICAgICAgICBkM21haW5Tdmcuc2VsZWN0KCdzdmcuc3ZnLScgKyBpICsgJyBnJykuY2FsbChyZW5kZXJlciwgc2cpO1xuICAgICAgICB9XG4gICAgICAgIG5ld1NjYWxlID0gMC41O1xuICAgICAgICB4Q2VudGVyT2Zmc2V0ID0gTWF0aC5mbG9vcigoYW5ndWxhci5lbGVtZW50KG1haW5TdmdFbGVtZW50KS53aWR0aCgpIC0gZy5ncmFwaCgpLndpZHRoICogbmV3U2NhbGUpIC8gMik7XG4gICAgICAgIHlDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLmhlaWdodCgpIC0gZy5ncmFwaCgpLmhlaWdodCAqIG5ld1NjYWxlKSAvIDIpO1xuICAgICAgICBtYWluWm9vbS5zY2FsZShuZXdTY2FsZSkudHJhbnNsYXRlKFt4Q2VudGVyT2Zmc2V0LCB5Q2VudGVyT2Zmc2V0XSk7XG4gICAgICAgIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHhDZW50ZXJPZmZzZXQgKyBcIiwgXCIgKyB5Q2VudGVyT2Zmc2V0ICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKTtcbiAgICAgICAgbWFpblpvb20ub24oXCJ6b29tXCIsIGZ1bmN0aW9uKCkge1xuICAgICAgICAgIHZhciBldjtcbiAgICAgICAgICBldiA9IGQzLmV2ZW50O1xuICAgICAgICAgIHJldHVybiBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0
<TRUNCATED>
[29/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Adjust
view for details of a job
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
index 07e09c2..b76278d 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/completed-jobs.html
@@ -35,7 +35,7 @@ limitations under the License.
</tr>
</thead>
<tbody>
- <tr ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })">
+ <tr ng-repeat="job in jobs" ui-sref="single-job.plan.overview({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job.duration}} ms</td>
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
index bcaef0d..a5f6676 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
@@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<div class="panel panel-default panel-multi">
+<div ng-if="exceptions['root-exception']" class="panel panel-default panel-multi">
<div class="panel-heading clearfix">
<div class="panel-title">Root exception</div>
</div>
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
index ed10feb..9d3e171 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
@@ -35,10 +35,11 @@ limitations under the License.
</nav>
<nav ng-if="job" class="navbar navbar-default navbar-fixed-top navbar-main-additional">
<ul class="nav nav-tabs">
- <li ui-sref-active="active"><a ui-sref=".plan">Plan</a></li>
+ <li ui-sref-active="active"><a ui-sref=".plan.overview">Plan</a></li>
<li ui-sref-active="active"><a ui-sref=".statistics">Job Accumulators / Statistics</a></li>
<li ui-sref-active="active"><a ui-sref=".timeline">Timeline</a></li>
<li ui-sref-active="active"><a ui-sref=".exceptions">Exceptions</a></li>
+ <li ui-sref-active="active"><a ui-sref=".properties">Properties</a></li>
<li ui-sref-active="active"><a ui-sref=".config">Configuration</a></li>
</ul>
</nav>
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html
index 34ad7a9..f2c4143 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.html
@@ -18,6 +18,14 @@ limitations under the License.
-->
<div class="canvas-wrapper">
- <div job-plan="job-plan" plan="plan" jobid="{{jobid}}" class="main-canvas"></div>
+ <div job-plan="job-plan" plan="plan" jobid="{{jobid}}" set-node="changeNode(nodeid)" class="main-canvas"></div>
</div>
-<div ui-view="node-tabs"></div>
\ No newline at end of file
+<div ng-if="plan" class="panel panel-default panel-multi">
+ <nav class="navbar navbar-default navbar-secondary-additional">
+ <ul class="nav nav-tabs">
+ <li ui-sref-active="active"><a ui-sref=".overview({nodeid: nodeid})">Overview</a></li>
+ <li ui-sref-active="active"><a ui-sref=".accumulators({nodeid: nodeid})">Accumulators</a></li>
+ </ul>
+ </nav>
+ <div ui-view="node-details" class="panel-body clean"></div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.accumulators.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.accumulators.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.accumulators.html
new file mode 100644
index 0000000..e3a20e3
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.accumulators.html
@@ -0,0 +1,40 @@
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<table class="table table-body-hover table-clickable table-activable">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ <tbody ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="changeNode(v.id)">
+ <tr>
+ <td>{{ v.name | humanizeText }}</td>
+ <td>
+ <bs-label status="{{v.status}}">{{v.status}}</bs-label>
+ </td>
+ </tr>
+ <tr ng-if="nodeid && v.id == nodeid">
+ <td colspan="10">
+ <div ng-include=" 'partials/jobs/job.plan.node.accumulators.html' "></div>
+ </td>
+ </tr>
+ </tbody>
+</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
deleted file mode 100644
index 27e965a..0000000
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.html
+++ /dev/null
@@ -1,69 +0,0 @@
-
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements. See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership. The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
--->
-<table class="table table-body-hover table-clickable table-activable">
- <thead>
- <tr>
- <th class="tab-column"></th>
- <th>Start Time</th>
- <th>End Time</th>
- <th>Duration</th>
- <th>Name</th>
- <th>Bytes read</th>
- <th>Records read</th>
- <th>Bytes written</th>
- <th>Records written</th>
- <th>Tasks</th>
- <th>Status</th>
- </tr>
- </thead>
- <tbody ng-repeat="v in job.vertices" ng-class="{ active: job.currentNode && v.id == job.currentNode.id }" ui-sref="{nodeid: v.id}">
- <tr>
- <td class="tab-column">
- <ul ng-if="job.currentNode && v.id == job.currentNode.id" class="nav nav-tabs tabs-vertical">
- <li ng-class="{ active: ('single-job.plan.node.generic' | isState) }"><a ng-click="gotoGeneric(v.id)" title="Basic"><i class="fa fa-tasks"></i></a></li>
- <li ng-class="{ active: ('single-job.plan.node.accumulators' | isState) }"><a ng-click="gotoAccumulators(v.id)" title="Accumulators"><i class="fa fa-calculator"></i></a></li>
- </ul>
- </td>
- <td><span ng-if="v['start-time'] > -1">{{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
- <td><span ng-if="v['end-time'] > -1">{{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
- <td><span ng-if="v.duration > -1">{{ v.duration }} ms</span></td>
- <td>{{ v.name | humanizeTaskName }}</td>
- <td>{{ v.metrics['read-bytes'] }}</td>
- <td>{{ v.metrics['read-records'] }}</td>
- <td>{{ v.metrics['write-bytes'] }}</td>
- <td>{{ v.metrics['write-records'] }}</td>
- <td>
- <div class="label-group">
- <bs-label status="{{status}}" ng-repeat="(index, status) in stateList">{{v.tasks[status]}}</bs-label>
- </div>
- </td>
- <td>
- <bs-label status="{{v.status}}">{{v.status}}</bs-label>
- </td>
- </tr>
- <tr ng-if="job.currentNode && v.id == job.currentNode.id">
- <td class="tab-column"></td>
- <td colspan="10">
- <div ng-if="'single-job.plan.node.generic' | isState" ng-include=" 'partials/jobs/job.plan.node.subtasks.html' "></div>
- <div ng-if="'single-job.plan.node.accumulators' | isState" ng-include=" 'partials/jobs/job.plan.node.accumulators.html' "></div>
- </td>
- </tr>
- </tbody>
-</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.overview.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.overview.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.overview.html
new file mode 100644
index 0000000..01440f8
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-list.overview.html
@@ -0,0 +1,60 @@
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<table class="table table-body-hover table-clickable table-activable">
+ <thead>
+ <tr>
+ <th>Start Time</th>
+ <th>End Time</th>
+ <th>Duration</th>
+ <th>Name</th>
+ <th>Bytes read</th>
+ <th>Records read</th>
+ <th>Bytes written</th>
+ <th>Records written</th>
+ <th>Tasks</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ <tbody ng-repeat="v in job.vertices" ng-class="{ active: v.id == nodeid }" ng-click="changeNode(v.id)">
+ <tr>
+ <td><span ng-if="v['start-time'] > -1">{{ v['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
+ <td><span ng-if="v['end-time'] > -1">{{ v['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss' }}</span></td>
+ <td><span ng-if="v.duration > -1">{{ v.duration }} ms</span></td>
+ <td class="td-long">{{ v.name | humanizeText }}</td>
+ <td>{{ v.metrics['read-bytes'] }}</td>
+ <td>{{ v.metrics['read-records'] }}</td>
+ <td>{{ v.metrics['write-bytes'] }}</td>
+ <td>{{ v.metrics['write-records'] }}</td>
+ <td>
+ <div class="label-group">
+ <bs-label status="{{status}}" ng-repeat="(index, status) in stateList">{{v.tasks[status]}}</bs-label>
+ </div>
+ </td>
+ <td>
+ <bs-label status="{{v.status}}">{{v.status}}</bs-label>
+ </td>
+ </tr>
+ <tr ng-if="nodeid && v.id == nodeid">
+ <td colspan="10">
+ <div ng-include=" 'partials/jobs/job.plan.node.subtasks.html' "></div>
+ </td>
+ </tr>
+ </tbody>
+</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
deleted file mode 100644
index 15a8638..0000000
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node-tabs.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-<div ng-if="node" class="panel panel-default panel-multi">
- <nav ng-if="job.currentNode" class="navbar navbar-default navbar-secondary-additional">
- <ul class="nav nav-tabs">
- <li ui-sref-active="active"><a ui-sref=".generic({nodeid: nodeid})">Subtasks</a></li>
- <li ui-sref-active="active"><a ui-sref=".properties({nodeid: nodeid})">Properties</a></li>
- </ul>
- </nav>
- <div ng-if="'single-job.plan.node.properties' | isState" class="panel-heading clearfix">
- <div class="panel-title">{{ node.description | humanizeTaskName }}</div>
- </div>
- <div ui-view="node-details" class="panel-body clean"></div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
index 16ca93a..6a79a7b 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.accumulators.html
@@ -17,4 +17,24 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<h3>Accumulators</h3>
\ No newline at end of file
+<div ng-if="vertex">
+ <div ng-if="vertex.accumulators.length == 0">
+ <p><i>No accumulators</i></p>
+ </div>
+ <table ng-if="vertex.accumulators.length > 0" class="table table-hover table-clickable table-activable table-inner">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Type</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="accumulator in vertex.accumulators">
+ <td>{{ accumulator.name }}</td>
+ <td>{{ accumulator.type }}</td>
+ <td>{{ accumulator.value }}</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
deleted file mode 100644
index f97b077..0000000
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.html
+++ /dev/null
@@ -1,44 +0,0 @@
-
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements. See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership. The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
--->
-<div ng-if="node" class="panel panel-default panel-multi">
- <div class="panel-heading clearfix">
- <div class="panel-title">{{ node.operator }}</div>
- <div class="panel-info first">ID: {{ node.id }}</div>
- <div ng-if="node.description" class="panel-info">
- <div class="label-group">
- <bs-label status="{{status}}" ng-repeat="(index, status) in stateList">{{node.vertex.tasks[status]}}</bs-label>
- </div>
- </div>
- </div>
- <div class="panel-heading clearfix">
- <div ng-if="node.description" class="panel-info thin last"><span>{{ vertex.name | humanizeTaskName }}</span>
- </div>
- </div>
- <nav class="navbar navbar-default navbar-secondary-additional">
- <ul class="nav nav-tabs">
- <li ui-sref-active="active"><a ui-sref=".subtasks">Subtasks</a></li>
- <li ui-sref-active="active"><a ui-sref=".properties">Properties</a></li>
- <li ui-sref-active="active"><a ui-sref=".accumulators">Accumulators</a></li>
- </ul>
- </nav>
- <div class="panel-body clean">
- <div ui-view="nodeTab"></div>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
deleted file mode 100644
index dce9fe5..0000000
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.properties.html
+++ /dev/null
@@ -1,130 +0,0 @@
-
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements. See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership. The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
--->
-<div class="row">
- <div class="col-sm-6 col-md-4">
- <table ng-if="node.optimizer_properties.global_properties" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Global Data Properties</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.global_properties">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
- <table ng-if="node.optimizer_properties.local_properties" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Local Data Properties</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.local_properties">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
- <div class="visible-xs visible-sm">
- <table class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Properties</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>Operator</td>
- <td table-property="table-property" value="node.operator_strategy"></td>
- </tr>
- <tr>
- <td>Parallelism</td>
- <td table-property="table-property" value="node.parallelism"></td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- <div class="hidden-sm col-md-4">
- <table class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Properties</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>Operator</td>
- <td table-property="table-property" value="node.operator_strategy"></td>
- </tr>
- <tr>
- <td>Parallelism</td>
- <td table-property="table-property" value="node.parallelism"></td>
- </tr>
- </tbody>
- </table>
- <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Size Estimates</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.estimates">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
- </div>
- <div class="col-sm-6 col-md-4">
- <div class="visible-xs visible-sm">
- <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Size Estimates</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.estimates">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
- </div>
- <table ng-if="node.optimizer_properties.costs" class="table table-properties">
- <thead>
- <tr>
- <th colspan="2">Cost Estimates</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="property in node.optimizer_properties.costs">
- <td>{{property.name}}</td>
- <td table-property="table-property" value="property.value"></td>
- </tr>
- </tbody>
- </table>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
index 594e080..451c3a5 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.plan.node.subtasks.html
@@ -18,7 +18,7 @@ limitations under the License.
-->
-<table class="table table-hover table-clickable table-activable table-inner">
+<table ng-if="vertex" class="table table-hover table-clickable table-activable table-inner">
<thead>
<tr>
<th>Start Time</th>
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.properties.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.properties.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.properties.html
new file mode 100644
index 0000000..907afd3
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.properties.html
@@ -0,0 +1,140 @@
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<div class="canvas-wrapper">
+ <div job-plan="job-plan" plan="plan" jobid="{{jobid}}" set-node="changeNode(nodeid)" class="main-canvas"></div>
+</div>
+<div ng-if="node" class="panel panel-default">
+ <div class="panel-heading clearfix">
+ <div class="panel-title">{{ node.description | humanizeText }}</div>
+ </div>
+ <div class="panel-body clean">
+ <div class="row">
+ <div class="col-sm-6 col-md-4">
+ <table ng-if="node.optimizer_properties.global_properties" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Global Data Properties</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.global_properties">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ <table ng-if="node.optimizer_properties.local_properties" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Local Data Properties</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.local_properties">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="visible-xs visible-sm">
+ <table class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Properties</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Operator</td>
+ <td table-property="table-property" value="node.operator_strategy"></td>
+ </tr>
+ <tr>
+ <td>Parallelism</td>
+ <td table-property="table-property" value="node.parallelism"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <div class="hidden-sm col-md-4">
+ <table class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Properties</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Operator</td>
+ <td table-property="table-property" value="node.operator_strategy"></td>
+ </tr>
+ <tr>
+ <td>Parallelism</td>
+ <td table-property="table-property" value="node.parallelism"></td>
+ </tr>
+ </tbody>
+ </table>
+ <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Size Estimates</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.estimates">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div class="col-sm-6 col-md-4">
+ <div class="visible-xs visible-sm">
+ <table ng-if="node.optimizer_properties.estimates" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Size Estimates</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.estimates">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <table ng-if="node.optimizer_properties.costs" class="table table-properties">
+ <thead>
+ <tr>
+ <th colspan="2">Cost Estimates</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="property in node.optimizer_properties.costs">
+ <td>{{property.name}}</td>
+ <td table-property="table-property" value="property.value"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html
index 36f38ab..1a4bd06 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.timeline.vertex.html
@@ -19,7 +19,7 @@ limitations under the License.
-->
<div ng-if="vertex" class="panel panel-default panel-multi">
<div class="panel-heading clearfix">
- <div class="panel-title">{{ vertex.groupvertex.groupvertexname | humanizeTaskName }}</div>
+ <div class="panel-title">{{ vertex.groupvertex.groupvertexname | humanizeText }}</div>
</div>
<div class="panel-body">
<div class="canvas-wrapper">
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html b/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
index edd524f..e175d07 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/running-jobs.html
@@ -35,7 +35,7 @@ limitations under the License.
</tr>
</thead>
<tbody>
- <tr ng-repeat="job in jobs" ui-sref="single-job.plan({ jobid: job.jid })">
+ <tr ng-repeat="job in jobs" ui-sref="single-job.plan.overview({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job.duration}} ms</td>
http://git-wip-us.apache.org/repos/asf/flink/blob/c36b3d7a/flink-runtime-web/web-dashboard/web/partials/overview.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/overview.html b/flink-runtime-web/web-dashboard/web/partials/overview.html
index c88b779..ec3c580 100644
--- a/flink-runtime-web/web-dashboard/web/partials/overview.html
+++ b/flink-runtime-web/web-dashboard/web/partials/overview.html
@@ -92,7 +92,7 @@ limitations under the License.
</tr>
</thead>
<tbody>
- <tr ng-repeat="job in runningJobs" ui-sref="single-job.plan({ jobid: job.jid })">
+ <tr ng-repeat="job in runningJobs" ui-sref="single-job.plan.overview({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job.duration}} ms</td>
@@ -127,7 +127,7 @@ limitations under the License.
</tr>
</thead>
<tbody>
- <tr ng-repeat="job in finishedJobs" ui-sref="single-job.plan({ jobid: job.jid })">
+ <tr ng-repeat="job in finishedJobs" ui-sref="single-job.plan.overview({ jobid: job.jid })">
<td>{{job['start-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job['end-time'] | amDateFormat:'YYYY-MM-DD, H:mm:ss'}}</td>
<td>{{job.duration}} ms</td>
[16/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Added
tooltips to timeline
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/4825c8c5/flink-runtime-web/web-dashboard/web/css/vendor.css
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/css/vendor.css b/flink-runtime-web/web-dashboard/web/css/vendor.css
index 336d931..cdb196c 100644
--- a/flink-runtime-web/web-dashboard/web/css/vendor.css
+++ b/flink-runtime-web/web-dashboard/web/css/vendor.css
@@ -8562,4 +8562,622 @@ button.close {
}
}
-/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZvbnQtYXdlc29tZS5jc3MiLCJib290c3RyYXBfY3VzdG9tLmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDendEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB
Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB
Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoidmVuZG9yLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIi8qIVxuICogIEZvbnQgQXdlc29tZSA0LjMuMCBieSBAZGF2ZWdhbmR5IC0gaHR0cDovL2ZvbnRhd2Vzb21lLmlvIC0gQGZvbnRhd2Vzb21lXG4gKiAgTGljZW5zZSAtIGh0dHA6Ly9mb250YXdlc29tZS5pby9saWNlbnNlIChGb250OiBTSUwgT0ZMIDEuMSwgQ1NTOiBNSVQgTGljZW5zZSlcbiAqL1xuLyogRk9OVCBQQVRIXG4gKiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuQGZvbnQtZmFjZSB7XG4gIGZvbnQtZmFtaWx5OiAnRm9udEF3ZXNvbWUnO1xuICBzcmM6IHVybCgnLi4vZm9udHMvZm9udGF3ZXNvbWUtd2ViZm9udC5lb3Q/dj00LjMuMCcpO1xuICBzcmM6IHVybCgnLi4vZm9udHMvZm9udGF3ZXNvbWUtd2ViZm9udC5lb3Q/I2llZml4JnY9NC4zLjAn
KSBmb3JtYXQoJ2VtYmVkZGVkLW9wZW50eXBlJyksIHVybCgnLi4vZm9udHMvZm9udGF3ZXNvbWUtd2ViZm9udC53b2ZmMj92PTQuMy4wJykgZm9ybWF0KCd3b2ZmMicpLCB1cmwoJy4uL2ZvbnRzL2ZvbnRhd2Vzb21lLXdlYmZvbnQud29mZj92PTQuMy4wJykgZm9ybWF0KCd3b2ZmJyksIHVybCgnLi4vZm9udHMvZm9udGF3ZXNvbWUtd2ViZm9udC50dGY/dj00LjMuMCcpIGZvcm1hdCgndHJ1ZXR5cGUnKSwgdXJsKCcuLi9mb250cy9mb250YXdlc29tZS13ZWJmb250LnN2Zz92PTQuMy4wI2ZvbnRhd2Vzb21lcmVndWxhcicpIGZvcm1hdCgnc3ZnJyk7XG4gIGZvbnQtd2VpZ2h0OiBub3JtYWw7XG4gIGZvbnQtc3R5bGU6IG5vcm1hbDtcbn1cbi5mYSB7XG4gIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgZm9udDogbm9ybWFsIG5vcm1hbCBub3JtYWwgMTRweC8xIEZvbnRBd2Vzb21lO1xuICBmb250LXNpemU6IGluaGVyaXQ7XG4gIHRleHQtcmVuZGVyaW5nOiBhdXRvO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhbnRpYWxpYXNlZDtcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgdHJhbnNmb3JtOiB0cmFuc2xhdGUoMCwgMCk7XG59XG4vKiBtYWtlcyB0aGUgZm9udCAzMyUgbGFyZ2VyIHJlbGF0aXZlIHRvIHRoZSBpY29uIGNvbnRhaW5lciAqL1xuLmZhLWxnIHtcbiAgZm9udC1zaXplOiAxLjMzMzMzMzMzZW07XG4gIGxpbmUtaGVpZ2h0OiAwLjc1ZW07XG4gIHZlcnRpY2FsLWFsa
WduOiAtMTUlO1xufVxuLmZhLTJ4IHtcbiAgZm9udC1zaXplOiAyZW07XG59XG4uZmEtM3gge1xuICBmb250LXNpemU6IDNlbTtcbn1cbi5mYS00eCB7XG4gIGZvbnQtc2l6ZTogNGVtO1xufVxuLmZhLTV4IHtcbiAgZm9udC1zaXplOiA1ZW07XG59XG4uZmEtZncge1xuICB3aWR0aDogMS4yODU3MTQyOWVtO1xuICB0ZXh0LWFsaWduOiBjZW50ZXI7XG59XG4uZmEtdWwge1xuICBwYWRkaW5nLWxlZnQ6IDA7XG4gIG1hcmdpbi1sZWZ0OiAyLjE0Mjg1NzE0ZW07XG4gIGxpc3Qtc3R5bGUtdHlwZTogbm9uZTtcbn1cbi5mYS11bCA+IGxpIHtcbiAgcG9zaXRpb246IHJlbGF0aXZlO1xufVxuLmZhLWxpIHtcbiAgcG9zaXRpb246IGFic29sdXRlO1xuICBsZWZ0OiAtMi4xNDI4NTcxNGVtO1xuICB3aWR0aDogMi4xNDI4NTcxNGVtO1xuICB0b3A6IDAuMTQyODU3MTRlbTtcbiAgdGV4dC1hbGlnbjogY2VudGVyO1xufVxuLmZhLWxpLmZhLWxnIHtcbiAgbGVmdDogLTEuODU3MTQyODZlbTtcbn1cbi5mYS1ib3JkZXIge1xuICBwYWRkaW5nOiAuMmVtIC4yNWVtIC4xNWVtO1xuICBib3JkZXI6IHNvbGlkIDAuMDhlbSAjZWVlZWVlO1xuICBib3JkZXItcmFkaXVzOiAuMWVtO1xufVxuLnB1bGwtcmlnaHQge1xuICBmbG9hdDogcmlnaHQ7XG59XG4ucHVsbC1sZWZ0IHtcbiAgZmxvYXQ6IGxlZnQ7XG59XG4uZmEucHVsbC1sZWZ0IHtcbiAgbWFyZ2luLXJpZ2h0OiAuM2VtO1xufVxuLmZhLnB1bGwtcmlnaHQge1xuICBtYXJnaW4tbG
VmdDogLjNlbTtcbn1cbi5mYS1zcGluIHtcbiAgLXdlYmtpdC1hbmltYXRpb246IGZhLXNwaW4gMnMgaW5maW5pdGUgbGluZWFyO1xuICBhbmltYXRpb246IGZhLXNwaW4gMnMgaW5maW5pdGUgbGluZWFyO1xufVxuLmZhLXB1bHNlIHtcbiAgLXdlYmtpdC1hbmltYXRpb246IGZhLXNwaW4gMXMgaW5maW5pdGUgc3RlcHMoOCk7XG4gIGFuaW1hdGlvbjogZmEtc3BpbiAxcyBpbmZpbml0ZSBzdGVwcyg4KTtcbn1cbkAtd2Via2l0LWtleWZyYW1lcyBmYS1zcGluIHtcbiAgMCUge1xuICAgIC13ZWJraXQtdHJhbnNmb3JtOiByb3RhdGUoMGRlZyk7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMGRlZyk7XG4gIH1cbiAgMTAwJSB7XG4gICAgLXdlYmtpdC10cmFuc2Zvcm06IHJvdGF0ZSgzNTlkZWcpO1xuICAgIHRyYW5zZm9ybTogcm90YXRlKDM1OWRlZyk7XG4gIH1cbn1cbkBrZXlmcmFtZXMgZmEtc3BpbiB7XG4gIDAlIHtcbiAgICAtd2Via2l0LXRyYW5zZm9ybTogcm90YXRlKDBkZWcpO1xuICAgIHRyYW5zZm9ybTogcm90YXRlKDBkZWcpO1xuICB9XG4gIDEwMCUge1xuICAgIC13ZWJraXQtdHJhbnNmb3JtOiByb3RhdGUoMzU5ZGVnKTtcbiAgICB0cmFuc2Zvcm06IHJvdGF0ZSgzNTlkZWcpO1xuICB9XG59XG4uZmEtcm90YXRlLTkwIHtcbiAgZmlsdGVyOiBwcm9naWQ6RFhJbWFnZVRyYW5zZm9ybS5NaWNyb3NvZnQuQmFzaWNJbWFnZShyb3RhdGlvbj0xKTtcbiAgLXdlYmtpdC10cmFuc2Zvcm06IHJvdGF0ZSg5MGRlZyk
7XG4gIC1tcy10cmFuc2Zvcm06IHJvdGF0ZSg5MGRlZyk7XG4gIHRyYW5zZm9ybTogcm90YXRlKDkwZGVnKTtcbn1cbi5mYS1yb3RhdGUtMTgwIHtcbiAgZmlsdGVyOiBwcm9naWQ6RFhJbWFnZVRyYW5zZm9ybS5NaWNyb3NvZnQuQmFzaWNJbWFnZShyb3RhdGlvbj0yKTtcbiAgLXdlYmtpdC10cmFuc2Zvcm06IHJvdGF0ZSgxODBkZWcpO1xuICAtbXMtdHJhbnNmb3JtOiByb3RhdGUoMTgwZGVnKTtcbiAgdHJhbnNmb3JtOiByb3RhdGUoMTgwZGVnKTtcbn1cbi5mYS1yb3RhdGUtMjcwIHtcbiAgZmlsdGVyOiBwcm9naWQ6RFhJbWFnZVRyYW5zZm9ybS5NaWNyb3NvZnQuQmFzaWNJbWFnZShyb3RhdGlvbj0zKTtcbiAgLXdlYmtpdC10cmFuc2Zvcm06IHJvdGF0ZSgyNzBkZWcpO1xuICAtbXMtdHJhbnNmb3JtOiByb3RhdGUoMjcwZGVnKTtcbiAgdHJhbnNmb3JtOiByb3RhdGUoMjcwZGVnKTtcbn1cbi5mYS1mbGlwLWhvcml6b250YWwge1xuICBmaWx0ZXI6IHByb2dpZDpEWEltYWdlVHJhbnNmb3JtLk1pY3Jvc29mdC5CYXNpY0ltYWdlKHJvdGF0aW9uPTAsIG1pcnJvcj0xKTtcbiAgLXdlYmtpdC10cmFuc2Zvcm06IHNjYWxlKC0xLCAxKTtcbiAgLW1zLXRyYW5zZm9ybTogc2NhbGUoLTEsIDEpO1xuICB0cmFuc2Zvcm06IHNjYWxlKC0xLCAxKTtcbn1cbi5mYS1mbGlwLXZlcnRpY2FsIHtcbiAgZmlsdGVyOiBwcm9naWQ6RFhJbWFnZVRyYW5zZm9ybS5NaWNyb3NvZnQuQmFzaWNJbWFnZShyb3RhdGlvbj0yLCBtaXJyb3I9
MSk7XG4gIC13ZWJraXQtdHJhbnNmb3JtOiBzY2FsZSgxLCAtMSk7XG4gIC1tcy10cmFuc2Zvcm06IHNjYWxlKDEsIC0xKTtcbiAgdHJhbnNmb3JtOiBzY2FsZSgxLCAtMSk7XG59XG46cm9vdCAuZmEtcm90YXRlLTkwLFxuOnJvb3QgLmZhLXJvdGF0ZS0xODAsXG46cm9vdCAuZmEtcm90YXRlLTI3MCxcbjpyb290IC5mYS1mbGlwLWhvcml6b250YWwsXG46cm9vdCAuZmEtZmxpcC12ZXJ0aWNhbCB7XG4gIGZpbHRlcjogbm9uZTtcbn1cbi5mYS1zdGFjayB7XG4gIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICB3aWR0aDogMmVtO1xuICBoZWlnaHQ6IDJlbTtcbiAgbGluZS1oZWlnaHQ6IDJlbTtcbiAgdmVydGljYWwtYWxpZ246IG1pZGRsZTtcbn1cbi5mYS1zdGFjay0xeCxcbi5mYS1zdGFjay0yeCB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgbGVmdDogMDtcbiAgd2lkdGg6IDEwMCU7XG4gIHRleHQtYWxpZ246IGNlbnRlcjtcbn1cbi5mYS1zdGFjay0xeCB7XG4gIGxpbmUtaGVpZ2h0OiBpbmhlcml0O1xufVxuLmZhLXN0YWNrLTJ4IHtcbiAgZm9udC1zaXplOiAyZW07XG59XG4uZmEtaW52ZXJzZSB7XG4gIGNvbG9yOiAjZmZmZmZmO1xufVxuLyogRm9udCBBd2Vzb21lIHVzZXMgdGhlIFVuaWNvZGUgUHJpdmF0ZSBVc2UgQXJlYSAoUFVBKSB0byBlbnN1cmUgc2NyZWVuXG4gICByZWFkZXJzIGRvIG5vdCByZWFkIG9mZiByYW5kb20gY2hhcmFjdGVycyB0aGF0IHJlcHJlc
2VudCBpY29ucyAqL1xuLmZhLWdsYXNzOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDAwXCI7XG59XG4uZmEtbXVzaWM6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMDFcIjtcbn1cbi5mYS1zZWFyY2g6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMDJcIjtcbn1cbi5mYS1lbnZlbG9wZS1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDAzXCI7XG59XG4uZmEtaGVhcnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMDRcIjtcbn1cbi5mYS1zdGFyOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDA1XCI7XG59XG4uZmEtc3Rhci1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDA2XCI7XG59XG4uZmEtdXNlcjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAwN1wiO1xufVxuLmZhLWZpbG06YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMDhcIjtcbn1cbi5mYS10aC1sYXJnZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAwOVwiO1xufVxuLmZhLXRoOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDBhXCI7XG59XG4uZmEtdGgtbGlzdDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAwYlwiO1xufVxuLmZhLWNoZWNrOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDBjXCI7XG59XG4uZmEtcmVtb3ZlOmJlZm9yZSxcbi5mYS1jbG9zZTpiZWZvcmUsXG4uZmEtdGltZXM6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMGRcIjtcbn1cbi5mYS1zZWFyY2gtcGx1czpiZWZvcmUge1xuICBjb2
50ZW50OiBcIlxcZjAwZVwiO1xufVxuLmZhLXNlYXJjaC1taW51czpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAxMFwiO1xufVxuLmZhLXBvd2VyLW9mZjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAxMVwiO1xufVxuLmZhLXNpZ25hbDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAxMlwiO1xufVxuLmZhLWdlYXI6YmVmb3JlLFxuLmZhLWNvZzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAxM1wiO1xufVxuLmZhLXRyYXNoLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMTRcIjtcbn1cbi5mYS1ob21lOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDE1XCI7XG59XG4uZmEtZmlsZS1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDE2XCI7XG59XG4uZmEtY2xvY2stbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAxN1wiO1xufVxuLmZhLXJvYWQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMThcIjtcbn1cbi5mYS1kb3dubG9hZDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAxOVwiO1xufVxuLmZhLWFycm93LWNpcmNsZS1vLWRvd246YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMWFcIjtcbn1cbi5mYS1hcnJvdy1jaXJjbGUtby11cDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAxYlwiO1xufVxuLmZhLWluYm94OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDFjXCI7XG59XG4uZmEtcGxheS1jaXJjbGUtbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAxZFwiO1xufVx
uLmZhLXJvdGF0ZS1yaWdodDpiZWZvcmUsXG4uZmEtcmVwZWF0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDFlXCI7XG59XG4uZmEtcmVmcmVzaDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAyMVwiO1xufVxuLmZhLWxpc3QtYWx0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDIyXCI7XG59XG4uZmEtbG9jazpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAyM1wiO1xufVxuLmZhLWZsYWc6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMjRcIjtcbn1cbi5mYS1oZWFkcGhvbmVzOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDI1XCI7XG59XG4uZmEtdm9sdW1lLW9mZjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAyNlwiO1xufVxuLmZhLXZvbHVtZS1kb3duOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDI3XCI7XG59XG4uZmEtdm9sdW1lLXVwOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDI4XCI7XG59XG4uZmEtcXJjb2RlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDI5XCI7XG59XG4uZmEtYmFyY29kZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAyYVwiO1xufVxuLmZhLXRhZzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAyYlwiO1xufVxuLmZhLXRhZ3M6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMmNcIjtcbn1cbi5mYS1ib29rOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDJkXCI7XG59XG4uZmEtYm9va21hcms6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYw
MmVcIjtcbn1cbi5mYS1wcmludDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAyZlwiO1xufVxuLmZhLWNhbWVyYTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAzMFwiO1xufVxuLmZhLWZvbnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMzFcIjtcbn1cbi5mYS1ib2xkOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDMyXCI7XG59XG4uZmEtaXRhbGljOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDMzXCI7XG59XG4uZmEtdGV4dC1oZWlnaHQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwMzRcIjtcbn1cbi5mYS10ZXh0LXdpZHRoOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDM1XCI7XG59XG4uZmEtYWxpZ24tbGVmdDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAzNlwiO1xufVxuLmZhLWFsaWduLWNlbnRlcjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAzN1wiO1xufVxuLmZhLWFsaWduLXJpZ2h0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDM4XCI7XG59XG4uZmEtYWxpZ24tanVzdGlmeTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAzOVwiO1xufVxuLmZhLWxpc3Q6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwM2FcIjtcbn1cbi5mYS1kZWRlbnQ6YmVmb3JlLFxuLmZhLW91dGRlbnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwM2JcIjtcbn1cbi5mYS1pbmRlbnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwM2NcIjtcbn1cbi5mYS12aWRlby1jYW1lcmE6YmVmb
3JlIHtcbiAgY29udGVudDogXCJcXGYwM2RcIjtcbn1cbi5mYS1waG90bzpiZWZvcmUsXG4uZmEtaW1hZ2U6YmVmb3JlLFxuLmZhLXBpY3R1cmUtbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjAzZVwiO1xufVxuLmZhLXBlbmNpbDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA0MFwiO1xufVxuLmZhLW1hcC1tYXJrZXI6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNDFcIjtcbn1cbi5mYS1hZGp1c3Q6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNDJcIjtcbn1cbi5mYS10aW50OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDQzXCI7XG59XG4uZmEtZWRpdDpiZWZvcmUsXG4uZmEtcGVuY2lsLXNxdWFyZS1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDQ0XCI7XG59XG4uZmEtc2hhcmUtc3F1YXJlLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNDVcIjtcbn1cbi5mYS1jaGVjay1zcXVhcmUtbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA0NlwiO1xufVxuLmZhLWFycm93czpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA0N1wiO1xufVxuLmZhLXN0ZXAtYmFja3dhcmQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNDhcIjtcbn1cbi5mYS1mYXN0LWJhY2t3YXJkOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDQ5XCI7XG59XG4uZmEtYmFja3dhcmQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNGFcIjtcbn1cbi5mYS1wbGF5OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMD
RiXCI7XG59XG4uZmEtcGF1c2U6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNGNcIjtcbn1cbi5mYS1zdG9wOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDRkXCI7XG59XG4uZmEtZm9yd2FyZDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA0ZVwiO1xufVxuLmZhLWZhc3QtZm9yd2FyZDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA1MFwiO1xufVxuLmZhLXN0ZXAtZm9yd2FyZDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA1MVwiO1xufVxuLmZhLWVqZWN0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDUyXCI7XG59XG4uZmEtY2hldnJvbi1sZWZ0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDUzXCI7XG59XG4uZmEtY2hldnJvbi1yaWdodDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA1NFwiO1xufVxuLmZhLXBsdXMtY2lyY2xlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDU1XCI7XG59XG4uZmEtbWludXMtY2lyY2xlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDU2XCI7XG59XG4uZmEtdGltZXMtY2lyY2xlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDU3XCI7XG59XG4uZmEtY2hlY2stY2lyY2xlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDU4XCI7XG59XG4uZmEtcXVlc3Rpb24tY2lyY2xlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDU5XCI7XG59XG4uZmEtaW5mby1jaXJjbGU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNWFcIjtcbn1cbi5mYS1jcm9
zc2hhaXJzOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDViXCI7XG59XG4uZmEtdGltZXMtY2lyY2xlLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNWNcIjtcbn1cbi5mYS1jaGVjay1jaXJjbGUtbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA1ZFwiO1xufVxuLmZhLWJhbjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA1ZVwiO1xufVxuLmZhLWFycm93LWxlZnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNjBcIjtcbn1cbi5mYS1hcnJvdy1yaWdodDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA2MVwiO1xufVxuLmZhLWFycm93LXVwOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDYyXCI7XG59XG4uZmEtYXJyb3ctZG93bjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA2M1wiO1xufVxuLmZhLW1haWwtZm9yd2FyZDpiZWZvcmUsXG4uZmEtc2hhcmU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNjRcIjtcbn1cbi5mYS1leHBhbmQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNjVcIjtcbn1cbi5mYS1jb21wcmVzczpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA2NlwiO1xufVxuLmZhLXBsdXM6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNjdcIjtcbn1cbi5mYS1taW51czpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA2OFwiO1xufVxuLmZhLWFzdGVyaXNrOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDY5XCI7XG59XG4uZmEtZXhjbGFtYXRpb24tY2lyY2xlOmJl
Zm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDZhXCI7XG59XG4uZmEtZ2lmdDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA2YlwiO1xufVxuLmZhLWxlYWY6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNmNcIjtcbn1cbi5mYS1maXJlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDZkXCI7XG59XG4uZmEtZXllOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDZlXCI7XG59XG4uZmEtZXllLXNsYXNoOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDcwXCI7XG59XG4uZmEtd2FybmluZzpiZWZvcmUsXG4uZmEtZXhjbGFtYXRpb24tdHJpYW5nbGU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNzFcIjtcbn1cbi5mYS1wbGFuZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA3MlwiO1xufVxuLmZhLWNhbGVuZGFyOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDczXCI7XG59XG4uZmEtcmFuZG9tOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDc0XCI7XG59XG4uZmEtY29tbWVudDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA3NVwiO1xufVxuLmZhLW1hZ25ldDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA3NlwiO1xufVxuLmZhLWNoZXZyb24tdXA6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNzdcIjtcbn1cbi5mYS1jaGV2cm9uLWRvd246YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwNzhcIjtcbn1cbi5mYS1yZXR3ZWV0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDc5XCI7XG59X
G4uZmEtc2hvcHBpbmctY2FydDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA3YVwiO1xufVxuLmZhLWZvbGRlcjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA3YlwiO1xufVxuLmZhLWZvbGRlci1vcGVuOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDdjXCI7XG59XG4uZmEtYXJyb3dzLXY6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwN2RcIjtcbn1cbi5mYS1hcnJvd3MtaDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA3ZVwiO1xufVxuLmZhLWJhci1jaGFydC1vOmJlZm9yZSxcbi5mYS1iYXItY2hhcnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwODBcIjtcbn1cbi5mYS10d2l0dGVyLXNxdWFyZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA4MVwiO1xufVxuLmZhLWZhY2Vib29rLXNxdWFyZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA4MlwiO1xufVxuLmZhLWNhbWVyYS1yZXRybzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA4M1wiO1xufVxuLmZhLWtleTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA4NFwiO1xufVxuLmZhLWdlYXJzOmJlZm9yZSxcbi5mYS1jb2dzOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDg1XCI7XG59XG4uZmEtY29tbWVudHM6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwODZcIjtcbn1cbi5mYS10aHVtYnMtby11cDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA4N1wiO1xufVxuLmZhLXRodW1icy1vLWRvd246YmVmb3JlIHtcbiAgY29udG
VudDogXCJcXGYwODhcIjtcbn1cbi5mYS1zdGFyLWhhbGY6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwODlcIjtcbn1cbi5mYS1oZWFydC1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDhhXCI7XG59XG4uZmEtc2lnbi1vdXQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwOGJcIjtcbn1cbi5mYS1saW5rZWRpbi1zcXVhcmU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwOGNcIjtcbn1cbi5mYS10aHVtYi10YWNrOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDhkXCI7XG59XG4uZmEtZXh0ZXJuYWwtbGluazpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA4ZVwiO1xufVxuLmZhLXNpZ24taW46YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwOTBcIjtcbn1cbi5mYS10cm9waHk6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwOTFcIjtcbn1cbi5mYS1naXRodWItc3F1YXJlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDkyXCI7XG59XG4uZmEtdXBsb2FkOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDkzXCI7XG59XG4uZmEtbGVtb24tbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA5NFwiO1xufVxuLmZhLXBob25lOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDk1XCI7XG59XG4uZmEtc3F1YXJlLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwOTZcIjtcbn1cbi5mYS1ib29rbWFyay1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDk3XCI7XG59XG4uZmEtcGhvbmUtc3F1YXJ
lOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDk4XCI7XG59XG4uZmEtdHdpdHRlcjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjA5OVwiO1xufVxuLmZhLWZhY2Vib29rLWY6YmVmb3JlLFxuLmZhLWZhY2Vib29rOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDlhXCI7XG59XG4uZmEtZ2l0aHViOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDliXCI7XG59XG4uZmEtdW5sb2NrOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMDljXCI7XG59XG4uZmEtY3JlZGl0LWNhcmQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwOWRcIjtcbn1cbi5mYS1yc3M6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwOWVcIjtcbn1cbi5mYS1oZGQtbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBhMFwiO1xufVxuLmZhLWJ1bGxob3JuOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGExXCI7XG59XG4uZmEtYmVsbDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBmM1wiO1xufVxuLmZhLWNlcnRpZmljYXRlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGEzXCI7XG59XG4uZmEtaGFuZC1vLXJpZ2h0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGE0XCI7XG59XG4uZmEtaGFuZC1vLWxlZnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwYTVcIjtcbn1cbi5mYS1oYW5kLW8tdXA6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwYTZcIjtcbn1cbi5mYS1oYW5kLW8tZG93bjpiZWZvcmUge1xuICBjb250ZW50OiBc
IlxcZjBhN1wiO1xufVxuLmZhLWFycm93LWNpcmNsZS1sZWZ0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGE4XCI7XG59XG4uZmEtYXJyb3ctY2lyY2xlLXJpZ2h0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGE5XCI7XG59XG4uZmEtYXJyb3ctY2lyY2xlLXVwOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGFhXCI7XG59XG4uZmEtYXJyb3ctY2lyY2xlLWRvd246YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwYWJcIjtcbn1cbi5mYS1nbG9iZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBhY1wiO1xufVxuLmZhLXdyZW5jaDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBhZFwiO1xufVxuLmZhLXRhc2tzOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGFlXCI7XG59XG4uZmEtZmlsdGVyOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGIwXCI7XG59XG4uZmEtYnJpZWZjYXNlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGIxXCI7XG59XG4uZmEtYXJyb3dzLWFsdDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBiMlwiO1xufVxuLmZhLWdyb3VwOmJlZm9yZSxcbi5mYS11c2VyczpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBjMFwiO1xufVxuLmZhLWNoYWluOmJlZm9yZSxcbi5mYS1saW5rOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGMxXCI7XG59XG4uZmEtY2xvdWQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwYzJcIjtcbn1cbi5mYS1mbGFzazpiZWZvcmUge1xuICBjb250Z
W50OiBcIlxcZjBjM1wiO1xufVxuLmZhLWN1dDpiZWZvcmUsXG4uZmEtc2Npc3NvcnM6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwYzRcIjtcbn1cbi5mYS1jb3B5OmJlZm9yZSxcbi5mYS1maWxlcy1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGM1XCI7XG59XG4uZmEtcGFwZXJjbGlwOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGM2XCI7XG59XG4uZmEtc2F2ZTpiZWZvcmUsXG4uZmEtZmxvcHB5LW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwYzdcIjtcbn1cbi5mYS1zcXVhcmU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwYzhcIjtcbn1cbi5mYS1uYXZpY29uOmJlZm9yZSxcbi5mYS1yZW9yZGVyOmJlZm9yZSxcbi5mYS1iYXJzOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGM5XCI7XG59XG4uZmEtbGlzdC11bDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBjYVwiO1xufVxuLmZhLWxpc3Qtb2w6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwY2JcIjtcbn1cbi5mYS1zdHJpa2V0aHJvdWdoOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGNjXCI7XG59XG4uZmEtdW5kZXJsaW5lOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGNkXCI7XG59XG4uZmEtdGFibGU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwY2VcIjtcbn1cbi5mYS1tYWdpYzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBkMFwiO1xufVxuLmZhLXRydWNrOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGQxXC
I7XG59XG4uZmEtcGludGVyZXN0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGQyXCI7XG59XG4uZmEtcGludGVyZXN0LXNxdWFyZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBkM1wiO1xufVxuLmZhLWdvb2dsZS1wbHVzLXNxdWFyZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBkNFwiO1xufVxuLmZhLWdvb2dsZS1wbHVzOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGQ1XCI7XG59XG4uZmEtbW9uZXk6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZDZcIjtcbn1cbi5mYS1jYXJldC1kb3duOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGQ3XCI7XG59XG4uZmEtY2FyZXQtdXA6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZDhcIjtcbn1cbi5mYS1jYXJldC1sZWZ0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGQ5XCI7XG59XG4uZmEtY2FyZXQtcmlnaHQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZGFcIjtcbn1cbi5mYS1jb2x1bW5zOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGRiXCI7XG59XG4uZmEtdW5zb3J0ZWQ6YmVmb3JlLFxuLmZhLXNvcnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZGNcIjtcbn1cbi5mYS1zb3J0LWRvd246YmVmb3JlLFxuLmZhLXNvcnQtZGVzYzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBkZFwiO1xufVxuLmZhLXNvcnQtdXA6YmVmb3JlLFxuLmZhLXNvcnQtYXNjOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGRlXCI7XG59XG4uZmE
tZW52ZWxvcGU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZTBcIjtcbn1cbi5mYS1saW5rZWRpbjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBlMVwiO1xufVxuLmZhLXJvdGF0ZS1sZWZ0OmJlZm9yZSxcbi5mYS11bmRvOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGUyXCI7XG59XG4uZmEtbGVnYWw6YmVmb3JlLFxuLmZhLWdhdmVsOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGUzXCI7XG59XG4uZmEtZGFzaGJvYXJkOmJlZm9yZSxcbi5mYS10YWNob21ldGVyOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGU0XCI7XG59XG4uZmEtY29tbWVudC1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGU1XCI7XG59XG4uZmEtY29tbWVudHMtbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBlNlwiO1xufVxuLmZhLWZsYXNoOmJlZm9yZSxcbi5mYS1ib2x0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGU3XCI7XG59XG4uZmEtc2l0ZW1hcDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBlOFwiO1xufVxuLmZhLXVtYnJlbGxhOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGU5XCI7XG59XG4uZmEtcGFzdGU6YmVmb3JlLFxuLmZhLWNsaXBib2FyZDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBlYVwiO1xufVxuLmZhLWxpZ2h0YnVsYi1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGViXCI7XG59XG4uZmEtZXhjaGFuZ2U6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZWNcIjtcbn1c
bi5mYS1jbG91ZC1kb3dubG9hZDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBlZFwiO1xufVxuLmZhLWNsb3VkLXVwbG9hZDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBlZVwiO1xufVxuLmZhLXVzZXItbWQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZjBcIjtcbn1cbi5mYS1zdGV0aG9zY29wZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBmMVwiO1xufVxuLmZhLXN1aXRjYXNlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGYyXCI7XG59XG4uZmEtYmVsbC1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGEyXCI7XG59XG4uZmEtY29mZmVlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGY0XCI7XG59XG4uZmEtY3V0bGVyeTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBmNVwiO1xufVxuLmZhLWZpbGUtdGV4dC1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGY2XCI7XG59XG4uZmEtYnVpbGRpbmctbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBmN1wiO1xufVxuLmZhLWhvc3BpdGFsLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZjhcIjtcbn1cbi5mYS1hbWJ1bGFuY2U6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZjlcIjtcbn1cbi5mYS1tZWRraXQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYwZmFcIjtcbn1cbi5mYS1maWdodGVyLWpldDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBmYlwiO1xufVxuLmZhLWJlZXI6YmVmb3JlIHtcbiAgY29udGVudDogX
CJcXGYwZmNcIjtcbn1cbi5mYS1oLXNxdWFyZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjBmZFwiO1xufVxuLmZhLXBsdXMtc3F1YXJlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMGZlXCI7XG59XG4uZmEtYW5nbGUtZG91YmxlLWxlZnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMDBcIjtcbn1cbi5mYS1hbmdsZS1kb3VibGUtcmlnaHQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMDFcIjtcbn1cbi5mYS1hbmdsZS1kb3VibGUtdXA6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMDJcIjtcbn1cbi5mYS1hbmdsZS1kb3VibGUtZG93bjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjEwM1wiO1xufVxuLmZhLWFuZ2xlLWxlZnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMDRcIjtcbn1cbi5mYS1hbmdsZS1yaWdodDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjEwNVwiO1xufVxuLmZhLWFuZ2xlLXVwOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTA2XCI7XG59XG4uZmEtYW5nbGUtZG93bjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjEwN1wiO1xufVxuLmZhLWRlc2t0b3A6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMDhcIjtcbn1cbi5mYS1sYXB0b3A6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMDlcIjtcbn1cbi5mYS10YWJsZXQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMGFcIjtcbn1cbi5mYS1tb2JpbGUtcGhvbmU6YmVmb3JlLFxuLmZhLW1vYmlsZTpiZWZvcm
Uge1xuICBjb250ZW50OiBcIlxcZjEwYlwiO1xufVxuLmZhLWNpcmNsZS1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTBjXCI7XG59XG4uZmEtcXVvdGUtbGVmdDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjEwZFwiO1xufVxuLmZhLXF1b3RlLXJpZ2h0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTBlXCI7XG59XG4uZmEtc3Bpbm5lcjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjExMFwiO1xufVxuLmZhLWNpcmNsZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjExMVwiO1xufVxuLmZhLW1haWwtcmVwbHk6YmVmb3JlLFxuLmZhLXJlcGx5OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTEyXCI7XG59XG4uZmEtZ2l0aHViLWFsdDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjExM1wiO1xufVxuLmZhLWZvbGRlci1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTE0XCI7XG59XG4uZmEtZm9sZGVyLW9wZW4tbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjExNVwiO1xufVxuLmZhLXNtaWxlLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMThcIjtcbn1cbi5mYS1mcm93bi1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTE5XCI7XG59XG4uZmEtbWVoLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMWFcIjtcbn1cbi5mYS1nYW1lcGFkOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTFiXCI7XG59XG4uZmEta2V5Ym9hcmQtbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE
xY1wiO1xufVxuLmZhLWZsYWctbzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjExZFwiO1xufVxuLmZhLWZsYWctY2hlY2tlcmVkOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTFlXCI7XG59XG4uZmEtdGVybWluYWw6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMjBcIjtcbn1cbi5mYS1jb2RlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTIxXCI7XG59XG4uZmEtbWFpbC1yZXBseS1hbGw6YmVmb3JlLFxuLmZhLXJlcGx5LWFsbDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjEyMlwiO1xufVxuLmZhLXN0YXItaGFsZi1lbXB0eTpiZWZvcmUsXG4uZmEtc3Rhci1oYWxmLWZ1bGw6YmVmb3JlLFxuLmZhLXN0YXItaGFsZi1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTIzXCI7XG59XG4uZmEtbG9jYXRpb24tYXJyb3c6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMjRcIjtcbn1cbi5mYS1jcm9wOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTI1XCI7XG59XG4uZmEtY29kZS1mb3JrOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTI2XCI7XG59XG4uZmEtdW5saW5rOmJlZm9yZSxcbi5mYS1jaGFpbi1icm9rZW46YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMjdcIjtcbn1cbi5mYS1xdWVzdGlvbjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjEyOFwiO1xufVxuLmZhLWluZm86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMjlcIjtcbn1cbi5mYS1leGNsYW1hdGlvbjpiZWZvcmUge1xu
ICBjb250ZW50OiBcIlxcZjEyYVwiO1xufVxuLmZhLXN1cGVyc2NyaXB0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTJiXCI7XG59XG4uZmEtc3Vic2NyaXB0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTJjXCI7XG59XG4uZmEtZXJhc2VyOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTJkXCI7XG59XG4uZmEtcHV6emxlLXBpZWNlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTJlXCI7XG59XG4uZmEtbWljcm9waG9uZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjEzMFwiO1xufVxuLmZhLW1pY3JvcGhvbmUtc2xhc2g6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMzFcIjtcbn1cbi5mYS1zaGllbGQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMzJcIjtcbn1cbi5mYS1jYWxlbmRhci1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTMzXCI7XG59XG4uZmEtZmlyZS1leHRpbmd1aXNoZXI6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMzRcIjtcbn1cbi5mYS1yb2NrZXQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMzVcIjtcbn1cbi5mYS1tYXhjZG46YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMzZcIjtcbn1cbi5mYS1jaGV2cm9uLWNpcmNsZS1sZWZ0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTM3XCI7XG59XG4uZmEtY2hldnJvbi1jaXJjbGUtcmlnaHQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxMzhcIjtcbn1cbi5mYS1jaGV2cm9uLWNpcmNsZS11cDpiZWZvc
mUge1xuICBjb250ZW50OiBcIlxcZjEzOVwiO1xufVxuLmZhLWNoZXZyb24tY2lyY2xlLWRvd246YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxM2FcIjtcbn1cbi5mYS1odG1sNTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjEzYlwiO1xufVxuLmZhLWNzczM6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxM2NcIjtcbn1cbi5mYS1hbmNob3I6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxM2RcIjtcbn1cbi5mYS11bmxvY2stYWx0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTNlXCI7XG59XG4uZmEtYnVsbHNleWU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNDBcIjtcbn1cbi5mYS1lbGxpcHNpcy1oOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTQxXCI7XG59XG4uZmEtZWxsaXBzaXMtdjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE0MlwiO1xufVxuLmZhLXJzcy1zcXVhcmU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNDNcIjtcbn1cbi5mYS1wbGF5LWNpcmNsZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE0NFwiO1xufVxuLmZhLXRpY2tldDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE0NVwiO1xufVxuLmZhLW1pbnVzLXNxdWFyZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE0NlwiO1xufVxuLmZhLW1pbnVzLXNxdWFyZS1vOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTQ3XCI7XG59XG4uZmEtbGV2ZWwtdXA6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNDhcIj
tcbn1cbi5mYS1sZXZlbC1kb3duOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTQ5XCI7XG59XG4uZmEtY2hlY2stc3F1YXJlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTRhXCI7XG59XG4uZmEtcGVuY2lsLXNxdWFyZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE0YlwiO1xufVxuLmZhLWV4dGVybmFsLWxpbmstc3F1YXJlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTRjXCI7XG59XG4uZmEtc2hhcmUtc3F1YXJlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTRkXCI7XG59XG4uZmEtY29tcGFzczpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE0ZVwiO1xufVxuLmZhLXRvZ2dsZS1kb3duOmJlZm9yZSxcbi5mYS1jYXJldC1zcXVhcmUtby1kb3duOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTUwXCI7XG59XG4uZmEtdG9nZ2xlLXVwOmJlZm9yZSxcbi5mYS1jYXJldC1zcXVhcmUtby11cDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE1MVwiO1xufVxuLmZhLXRvZ2dsZS1yaWdodDpiZWZvcmUsXG4uZmEtY2FyZXQtc3F1YXJlLW8tcmlnaHQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNTJcIjtcbn1cbi5mYS1ldXJvOmJlZm9yZSxcbi5mYS1ldXI6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNTNcIjtcbn1cbi5mYS1nYnA6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNTRcIjtcbn1cbi5mYS1kb2xsYXI6YmVmb3JlLFxuLmZhLXVzZDpiZWZvcmUge1xuICBjb250ZW50OiBcIlx
cZjE1NVwiO1xufVxuLmZhLXJ1cGVlOmJlZm9yZSxcbi5mYS1pbnI6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNTZcIjtcbn1cbi5mYS1jbnk6YmVmb3JlLFxuLmZhLXJtYjpiZWZvcmUsXG4uZmEteWVuOmJlZm9yZSxcbi5mYS1qcHk6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNTdcIjtcbn1cbi5mYS1ydWJsZTpiZWZvcmUsXG4uZmEtcm91YmxlOmJlZm9yZSxcbi5mYS1ydWI6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNThcIjtcbn1cbi5mYS13b246YmVmb3JlLFxuLmZhLWtydzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE1OVwiO1xufVxuLmZhLWJpdGNvaW46YmVmb3JlLFxuLmZhLWJ0YzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE1YVwiO1xufVxuLmZhLWZpbGU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNWJcIjtcbn1cbi5mYS1maWxlLXRleHQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNWNcIjtcbn1cbi5mYS1zb3J0LWFscGhhLWFzYzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE1ZFwiO1xufVxuLmZhLXNvcnQtYWxwaGEtZGVzYzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE1ZVwiO1xufVxuLmZhLXNvcnQtYW1vdW50LWFzYzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE2MFwiO1xufVxuLmZhLXNvcnQtYW1vdW50LWRlc2M6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNjFcIjtcbn1cbi5mYS1zb3J0LW51bWVyaWMtYXNjOmJlZm9yZSB7XG4gIGNvbnRl
bnQ6IFwiXFxmMTYyXCI7XG59XG4uZmEtc29ydC1udW1lcmljLWRlc2M6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNjNcIjtcbn1cbi5mYS10aHVtYnMtdXA6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNjRcIjtcbn1cbi5mYS10aHVtYnMtZG93bjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE2NVwiO1xufVxuLmZhLXlvdXR1YmUtc3F1YXJlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTY2XCI7XG59XG4uZmEteW91dHViZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE2N1wiO1xufVxuLmZhLXhpbmc6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNjhcIjtcbn1cbi5mYS14aW5nLXNxdWFyZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE2OVwiO1xufVxuLmZhLXlvdXR1YmUtcGxheTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE2YVwiO1xufVxuLmZhLWRyb3Bib3g6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNmJcIjtcbn1cbi5mYS1zdGFjay1vdmVyZmxvdzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE2Y1wiO1xufVxuLmZhLWluc3RhZ3JhbTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE2ZFwiO1xufVxuLmZhLWZsaWNrcjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE2ZVwiO1xufVxuLmZhLWFkbjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE3MFwiO1xufVxuLmZhLWJpdGJ1Y2tldDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE3MVwiO1xufVxuLmZhLWJpd
GJ1Y2tldC1zcXVhcmU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNzJcIjtcbn1cbi5mYS10dW1ibHI6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNzNcIjtcbn1cbi5mYS10dW1ibHItc3F1YXJlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTc0XCI7XG59XG4uZmEtbG9uZy1hcnJvdy1kb3duOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTc1XCI7XG59XG4uZmEtbG9uZy1hcnJvdy11cDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE3NlwiO1xufVxuLmZhLWxvbmctYXJyb3ctbGVmdDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE3N1wiO1xufVxuLmZhLWxvbmctYXJyb3ctcmlnaHQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxNzhcIjtcbn1cbi5mYS1hcHBsZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE3OVwiO1xufVxuLmZhLXdpbmRvd3M6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxN2FcIjtcbn1cbi5mYS1hbmRyb2lkOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTdiXCI7XG59XG4uZmEtbGludXg6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxN2NcIjtcbn1cbi5mYS1kcmliYmJsZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE3ZFwiO1xufVxuLmZhLXNreXBlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTdlXCI7XG59XG4uZmEtZm91cnNxdWFyZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE4MFwiO1xufVxuLmZhLXRyZWxsbzpiZWZvcmUge1xuICBjb2
50ZW50OiBcIlxcZjE4MVwiO1xufVxuLmZhLWZlbWFsZTpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE4MlwiO1xufVxuLmZhLW1hbGU6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxODNcIjtcbn1cbi5mYS1naXR0aXA6YmVmb3JlLFxuLmZhLWdyYXRpcGF5OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTg0XCI7XG59XG4uZmEtc3VuLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxODVcIjtcbn1cbi5mYS1tb29uLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxODZcIjtcbn1cbi5mYS1hcmNoaXZlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTg3XCI7XG59XG4uZmEtYnVnOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTg4XCI7XG59XG4uZmEtdms6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxODlcIjtcbn1cbi5mYS13ZWlibzpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE4YVwiO1xufVxuLmZhLXJlbnJlbjpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE4YlwiO1xufVxuLmZhLXBhZ2VsaW5lczpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcZjE4Y1wiO1xufVxuLmZhLXN0YWNrLWV4Y2hhbmdlOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMThkXCI7XG59XG4uZmEtYXJyb3ctY2lyY2xlLW8tcmlnaHQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxOGVcIjtcbn1cbi5mYS1hcnJvdy1jaXJjbGUtby1sZWZ0OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTkwXCI7XG59XG4uZmE
tdG9nZ2xlLWxlZnQ6YmVmb3JlLFxuLmZhLWNhcmV0LXNxdWFyZS1vLWxlZnQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxOTFcIjtcbn1cbi5mYS1kb3QtY2lyY2xlLW86YmVmb3JlIHtcbiAgY29udGVudDogXCJcXGYxOTJcIjtcbn1cbi5mYS13aGVlbGNoYWlyOmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFxmMTkzXCI7XG59XG4uZmEtdmltZW
<TRUNCATED>
[18/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard] Add
view for exceptions
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
index e7f5fc2..7c88ca2 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.exceptions.html
@@ -17,54 +17,14 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<pre>org.apache.flink.client.program.ProgramInvocationException: The
-program execution failed: java.lang.Exception: The data preparation
-for task 'Reduce (SUM(2))' , caused an error: Error obtaining the
-sorted input: Thread 'SortMerger spilling thread' terminated due to an
-exception: The user-defined combiner failed in its 'open()' method.
- at org.apache.flink.runtime.operators.RegularPactTask.run(RegularPactTask.java:472)
- at org.apache.flink.runtime.operators.RegularPactTask.invoke(RegularPactTask.java:360)
- at org.apache.flink.runtime.execution.RuntimeEnvironment.run(RuntimeEnvironment.java:204)
- at java.lang.Thread.run(Thread.java:745)
-Caused by: java.lang.RuntimeException: Error obtaining the sorted
-input: Thread 'SortMerger spilling thread' terminated due to an
-exception: The user-defined combiner failed in its 'open()' method.
- at org.apache.flink.runtime.operators.sort.UnilateralSortMerger.getIterator(UnilateralSortMerger.java:607)
- at org.apache.flink.runtime.operators.RegularPactTask.getInput(RegularPactTask.java:1133)
- at org.apache.flink.runtime.operators.GroupReduceDriver.prepare(GroupReduceDriver.java:94)
- at org.apache.flink.runtime.operators.RegularPactTask.run(RegularPactTask.java:466)
- ... 3 more
-Caused by: java.io.IOException: Thread 'SortMerger spilling thread'
-terminated due to an exception: The user-defined combiner failed in
-its 'open()' method.
- at org.apache.flink.runtime.operators.sort.UnilateralSortMerger$ThreadBase.run(UnilateralSortMerger.java:785)
-Caused by: java.io.IOException: The user-defined combiner failed in
-its 'open()' method.
- at org.apache.flink.runtime.operators.sort.CombiningUnilateralSortMerger$CombiningSpillingThread.go(CombiningUnilateralSortMerger.java:264)
- at org.apache.flink.runtime.operators.sort.UnilateralSortMerger$ThreadBase.run(UnilateralSortMerger.java:781)
-Caused by: java.lang.IllegalStateException: The runtime context has
-not been initialized.
- at org.apache.flink.api.common.functions.AbstractRichFunction.getRuntimeContext(AbstractRichFunction.java:49)
- at org.apache.flink.api.scala.operators.ScalaAggregateOperator$AggregatingUdf.open(ScalaAggregateOperator.java:261)
- at org.apache.flink.api.common.functions.util.FunctionUtils.openFunction(FunctionUtils.java:33)
- at org.apache.flink.runtime.operators.sort.CombiningUnilateralSortMerger$CombiningSpillingThread.go(CombiningUnilateralSortMerger.java:261)
- ... 1 more
-
- at org.apache.flink.client.program.Client.run(Client.java:345)
- at org.apache.flink.client.program.Client.run(Client.java:304)
- at org.apache.flink.client.program.Client.run(Client.java:298)
- at org.apache.flink.client.program.ContextEnvironment.execute(ContextEnvironment.java:55)
- at org.apache.flink.api.scala.ExecutionEnvironment.execute(ExecutionEnvironment.scala:530)
- at com.gmp.MyJob$.main(MyJob.scala:33)
- at com.gmp.MyJob.main(MyJob.scala)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:483)
- at org.apache.flink.client.program.PackagedProgram.callMainMethod(PackagedProgram.java:437)
- at org.apache.flink.client.program.PackagedProgram.invokeInteractiveModeForExecution(PackagedProgram.java:353)
- at org.apache.flink.client.program.Client.run(Client.java:250)
- at org.apache.flink.client.CliFrontend.executeProgram(CliFrontend.java:374)
- at org.apache.flink.client.CliFrontend.run(CliFrontend.java:347)
- at org.apache.flink.client.CliFrontend.parseParameters(CliFrontend.java:1088)
- at org.apache.flink.client.CliFrontend.main(CliFrontend.java:1115)</pre>
\ No newline at end of file
+<div ng-repeat="exception in exceptions['all-exceptions']" class="panel panel-default panel-multi">
+ <div class="panel-heading clearfix">
+ <div class="panel-title">{{ exception.task }}</div>
+ </div>
+ <div class="panel-heading clearfix">
+ <div class="panel-info thin last"><span>{{ exception.location }}</span></div>
+ </div>
+ <div class="panel-body">
+ <pre class="exception">{{ exception.exception }}</pre>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flink/blob/ba31f83a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
index 7e9d196..3f09b53 100644
--- a/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
+++ b/flink-runtime-web/web-dashboard/web/partials/jobs/job.html
@@ -37,7 +37,7 @@ limitations under the License.
<ul class="nav nav-tabs">
<li ui-sref-active="active"><a ui-sref=".plan">Plan</a></li>
<li ui-sref-active="active"><a ui-sref=".statistics">Job Accumulators / Statistics</a></li>
- <li ui-sref-active="active"><a ui-sref=".timeline">Timeline</a></li>
+ <li ui-sref-active="active" ng-if="job['end-time'] > -1"><a ui-sref=".timeline">Timeline</a></li>
<li ui-sref-active="active"><a ui-sref=".exceptions">Exceptions</a></li>
<li ui-sref-active="active"><a ui-sref=".config">Configuration</a></li>
</ul>
[11/51] [abbrv] flink git commit: [FLINK-2357] [web dashboard]
Changed overview and timeline
Posted by se...@apache.org.
http://git-wip-us.apache.org/repos/asf/flink/blob/b29a5d44/flink-runtime-web/web-dashboard/web/js/index.js
----------------------------------------------------------------------
diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js
index 49c1d4c..130ca05 100644
--- a/flink-runtime-web/web-dashboard/web/js/index.js
+++ b/flink-runtime-web/web-dashboard/web/js/index.js
@@ -4,12 +4,18 @@ angular.module('flinkApp', ['ui.router', 'angularMoment']).run(["$rootScope", fu
$rootScope.sidebarVisible = !$rootScope.sidebarVisible;
return $rootScope.sidebarClass = 'force-show';
};
-}]).constant('flinkConfig', {
+}]).value('flinkConfig', {
jobServer: 'http://localhost:8081',
newServer: 'http://localhost:8081',
- refreshInterval: 10000
-}).run(["JobsService", "flinkConfig", "$interval", function(JobsService, flinkConfig, $interval) {
- return JobsService.listJobs();
+ "refresh-interval": 10000
+}).run(["JobsService", "MainService", "flinkConfig", "$interval", function(JobsService, MainService, flinkConfig, $interval) {
+ return MainService.loadConfig().then(function(config) {
+ angular.extend(flinkConfig, config);
+ JobsService.listJobs();
+ return $interval(function() {
+ return JobsService.listJobs();
+ }, flinkConfig["refresh-interval"]);
+ });
}]).config(["$stateProvider", "$urlRouterProvider", function($stateProvider, $urlRouterProvider) {
$stateProvider.state("overview", {
url: "/overview",
@@ -151,6 +157,28 @@ angular.module('flinkApp').filter("amDurationFormatExtended", ["angularMomentCon
};
amDurationFormatExtendedFilter.$stateful = angularMomentConfig.statefulFilters;
return amDurationFormatExtendedFilter;
+}]).filter("humanizeTaskName", function() {
+ return function(text) {
+ if (text) {
+ return text.replace(">", ">").replace("<br/>", "");
+ } else {
+ return '';
+ }
+ };
+});
+
+angular.module('flinkApp').service('MainService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) {
+ var jobObservers;
+ jobObservers = [];
+ this.loadConfig = function() {
+ var deferred;
+ deferred = $q.defer();
+ $http.get(flinkConfig.newServer + "/config").success(function(data, status, headers, config) {
+ return deferred.resolve(data);
+ });
+ return deferred.promise;
+ };
+ return this;
}]);
angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) {
@@ -219,18 +247,21 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
{
label: "Scheduled",
color: "#666",
+ borderColor: "#555",
starting_time: vTime["SCHEDULED"] * 100,
ending_time: vTime["DEPLOYING"] * 100,
type: 'regular'
}, {
label: "Deploying",
color: "#aaa",
+ borderColor: "#555",
starting_time: vTime["DEPLOYING"] * 100,
ending_time: vTime["RUNNING"] * 100,
type: 'regular'
}, {
label: "Running",
color: "#ddd",
+ borderColor: "#555",
starting_time: vTime["RUNNING"] * 100,
ending_time: vTime["FINISHED"] * 100,
type: 'regular'
@@ -248,7 +279,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
right: 0,
top: 0,
bottom: 0
- }).itemHeight(24).relativeTime();
+ }).itemHeight(30).relativeTime();
return svg = d3.select(svgEl).datum(testData).call(chart);
};
analyzeTime(scope.data);
@@ -276,8 +307,9 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
{
label: "Scheduled",
color: "#cccccc",
+ borderColor: "#555",
starting_time: data.oldV["SCHEDULED"],
- ending_time: data.oldV["SCHEDULED"] + 30,
+ ending_time: data.oldV["SCHEDULED"] + 1,
type: 'scheduled'
}
]
@@ -289,7 +321,8 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
times: [
{
label: translateLabel(vertex.groupvertexname),
- color: "#62cdea",
+ color: "#d9f1f7",
+ borderColor: "#62cdea",
starting_time: vTime["STARTED"],
ending_time: vTime["ENDED"],
link: vertex.groupvertexid,
@@ -313,7 +346,7 @@ angular.module('flinkApp').directive('vertex', ["$state", function($state) {
right: 0,
top: 0,
bottom: 0
- }).itemHeight(24).showBorderLine().showHourTimeline();
+ }).itemHeight(30).showBorderLine().showHourTimeline();
return svg = d3.select(svgEl).datum(testData).call(chart);
};
scope.$watch(attrs.job, function(data) {
@@ -672,26 +705,18 @@ angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log
this.listJobs = function() {
var deferred;
deferred = $q.defer();
- $http.get(flinkConfig.newServer + "/jobs").success(function(data, status, headers, config) {
+ $http.get(flinkConfig.newServer + "/joboverview").success(function(data, status, headers, config) {
angular.forEach(data, function(list, listKey) {
switch (listKey) {
- case 'jobs-running':
- jobs.running = list;
- break;
- case 'jobs-finished':
- jobs.finished = list;
- break;
- case 'jobs-cancelled':
- jobs.cancelled = list;
- break;
- case 'jobs-failed':
- jobs.failed = list;
+ case 'running':
+ return jobs.running = list;
+ case 'finished':
+ return jobs.finished = list;
+ case 'cancelled':
+ return jobs.cancelled = list;
+ case 'failed':
+ return jobs.failed = list;
}
- return angular.forEach(list, function(jobid, index) {
- return $http.get(flinkConfig.newServer + "/jobs/" + jobid).success(function(details) {
- return list[index] = details;
- });
- });
});
deferred.resolve(jobs);
return notifyObservers();
@@ -783,19 +808,25 @@ angular.module('flinkApp').controller('OverviewController', ["$scope", "Overview
$scope.$on('$destroy', function() {
return JobsService.unRegisterObserver($scope.jobObserver);
});
- return $scope.jobObserver();
+ $scope.jobObserver();
+ return OverviewService.loadOverview().then(function(data) {
+ return $scope.overview = data;
+ });
}]);
-angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "$log", function($http, flinkConfig, $log) {
- var serverStatus;
- serverStatus = {};
- this.loadServerStatus = function() {
- $http.get(flinkConfig.jobServer + "/monitor/status").success(function(data, status, headers, config) {
- return $log(data);
- }).error(function(data, status, headers, config) {});
- return serverStatus;
+angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) {
+ var overview;
+ overview = {};
+ this.loadOverview = function() {
+ var deferred;
+ deferred = $q.defer();
+ $http.get(flinkConfig.jobServer + "/overview").success(function(data, status, headers, config) {
+ overview = data;
+ return deferred.resolve(data);
+ });
+ return deferred.promise;
};
return this;
}]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsIm1vZHVsZXMvam9icy9qb2JzLmN0cmwuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuY3RybC5qcyIsIm1vZHVsZXMvam9icy9qb2JzLmRpci5jb2ZmZWUiLCJtb2R1bGVzL2pvYnMvam9icy5kaXIuanMiLCJtb2R1bGVzL2pvYnMvam9icy5zdmMuY29mZmVlIiwibW9kdWxlcy9qb2JzL2pvYnMuc3ZjLmpzIiwibW9kdWxlcy9vdmVydmlldy9vdmVydmlldy5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuc3ZjLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQWtCQSxRQUFRLE9BQU8sWUFBWSxDQUFDLGFBQWEsa0JBSXhDLG1CQUFJLFNBQUMsWUFBRDtFQUNILFdBQVcsaUJBQWlCO0VDckI1QixPRHNCQSxXQUFXLGNBQWMsV0FBQTtJQUN2QixXQUFXLGlCQUFpQixDQUFDLFdBQVc7SUNyQnhDLE9Ec0JBLFdBQVcsZUFBZTs7SUFJN0IsU0FBUyxlQUFlO0VBQ3ZCLFdBQVc7RUFDWCxXQUFXO0VBR1gsaUJBQWlCO0dBS2xCLGdEQUFJLFNBQUM
sYUFBYSxhQUFhLFdBQTNCO0VDN0JILE9EOEJBLFlBQVk7SUFTYixnREFBTyxTQUFDLGdCQUFnQixvQkFBakI7RUFDTixlQUFlLE1BQU0sWUFDbkI7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sZ0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sa0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sY0FDTDtJQUFBLEtBQUs7SUFDTCxVQUFVO0lBQ1YsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sbUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sd0JBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLE1BQ0U7UUFBQSxhQUFhO1FBQ2IsWUFBWTs7O0tBRWpCLE1BQU0sdUJBQ0w7SUFBQSxLQUFLO0lBQ0wsT0FDRTtNQUFBLFNBQ0U7UUFBQSxhQUFhOzs7S0FFbEIsTUFBTSw4QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsUUFDRTtRQUFBLGFBQWE7UUFDYixZQUFZOzs7S0FFakIsTUFBTSx5QkFDTDtJQUFBLEtBQUs7SUFDTCxPQUNFO01BQUEsU0FDRTtRQUFBLGFBQWE7OztLQUVsQixNQUFNLHlCQUNMO0lBQUEsS0FBSztJQUNMLE9BQ0U7TUFBQSxTQUNFO1FBQUEsYUFBYTs7O0tBRWxCLE1BQU0s
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
LFlBRWQsV0FBVyxtRUFBc0IsU0FBQyxRQUFRLGlCQUFpQixhQUExQjtFQUNoQyxPQUFPLGNBQWMsV0FBQTtJQUNuQixPQUFPLGNBQWMsWUFBWSxRQUFRO0lDbkJ6QyxPRG9CQSxPQUFPLGVBQWUsWUFBWSxRQUFROztFQUU1QyxZQUFZLGlCQUFpQixPQUFPO0VBQ3BDLE9BQU8sSUFBSSxZQUFZLFdBQUE7SUNuQnJCLE9Eb0JBLFlBQVksbUJBQW1CLE9BQU87O0VDbEJ4QyxPRG9CQSxPQUFPOztBQ2xCVDtBQ09BLFFBQVEsT0FBTyxZQUVkLFFBQVEsb0RBQW1CLFNBQUMsT0FBTyxhQUFhLE1BQXJCO0VBQzFCLElBQUE7RUFBQSxlQUFlO0VBRWYsS0FBQyxtQkFBbUIsV0FBQTtJQUNsQixNQUFNLElBQUksWUFBWSxZQUFZLG1CQUNqQyxRQUFRLFNBQUMsTUFBTSxRQUFRLFNBQVMsUUFBeEI7TUNwQlAsT0RxQkEsS0FBSztPQUVOLE1BQU0sU0FBQyxNQUFNLFFBQVEsU0FBUyxRQUF4QjtJQ3JCUCxPRHdCQTs7RUN0QkYsT0R3QkE7O0FDdEJGIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiI1xuIyBMaWNlbnNlZCB0byB0aGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24gKEFTRikgdW5kZXIgb25lXG4jIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiAgU2VlIHRoZSBOT1RJQ0UgZmlsZVxuIyBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuIyByZWdhcmRpbmcgY29weXJpZ2h0IG93bmVyc2hpcC4gIFRoZSBBU0YgbGljZW5zZXMgd
GhpcyBmaWxlXG4jIHRvIHlvdSB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGVcbiMgXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4jIHdpdGggdGhlIExpY2Vuc2UuICBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiNcbiMgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuI1xuIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4jIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiMgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4jIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4jXG5cbmFuZ3VsYXIubW9kdWxlKCdmbGlua0FwcCcsIFsndWkucm91dGVyJywgJ2FuZ3VsYXJNb21lbnQnXSlcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4ucnVuICgkcm9vdFNjb3BlKSAtPlxuICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gZmFsc2VcbiAgJHJvb3RTY2
9wZS5zaG93U2lkZWJhciA9IC0+XG4gICAgJHJvb3RTY29wZS5zaWRlYmFyVmlzaWJsZSA9ICEkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlXG4gICAgJHJvb3RTY29wZS5zaWRlYmFyQ2xhc3MgPSAnZm9yY2Utc2hvdydcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29uc3RhbnQgJ2ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnXG4gIG5ld1NlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MSdcbiMgIGpvYlNlcnZlcjogJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9uZXctc2VydmVyJ1xuIyAgbmV3U2VydmVyOiAnaHR0cDovL2xvY2FsaG9zdDozMDAwL25ldy1zZXJ2ZXInXG4gIHJlZnJlc2hJbnRlcnZhbDogMTAwMDBcbn1cblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4ucnVuIChKb2JzU2VydmljZSwgZmxpbmtDb25maWcsICRpbnRlcnZhbCkgLT5cbiAgSm9ic1NlcnZpY2UubGlzdEpvYnMoKVxuXG4gICMgJGludGVydmFsIC0+XG4gICMgICBKb2JzU2VydmljZS5saXN0Sm9icygpXG4gICMgLCBmbGlua0NvbmZpZy5yZWZyZXNoSW50ZXJ2YWxcblxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb25maWcgKCRzdGF0ZVByb3ZpZGVyLCAkdXJsUm91dGVyUHJvdmlkZXIpIC0+XG4gICRzdGF0ZVByb3ZpZGVyLnN0YXRlIFwib3ZlcnZpZXdcIixcbiAgICB
1cmw6IFwiL292ZXJ2aWV3XCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL292ZXJ2aWV3Lmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnT3ZlcnZpZXdDb250cm9sbGVyJ1xuXG4gIC5zdGF0ZSBcInJ1bm5pbmctam9ic1wiLFxuICAgIHVybDogXCIvcnVubmluZy1qb2JzXCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvcnVubmluZy1qb2JzLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnUnVubmluZ0pvYnNDb250cm9sbGVyJ1xuICBcbiAgLnN0YXRlIFwiY29tcGxldGVkLWpvYnNcIixcbiAgICB1cmw6IFwiL2NvbXBsZXRlZC1qb2JzXCJcbiAgICB2aWV3czpcbiAgICAgIG1haW46XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvY29tcGxldGVkLWpvYnMuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcidcblxuICAuc3RhdGUgXCJzaW5nbGUtam9iXCIsXG4gICAgdXJsOiBcIi9qb2JzL3tqb2JpZH1cIlxuICAgIGFic3RyYWN0OiB0cnVlXG4gICAgdmlld3M6XG4gICAgICBtYWluOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5odG1sXCJcbiAgICAgICAgY29udHJvbGxlcjogJ1NpbmdsZUpvYkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuXCIsXG4gICAg
dXJsOiBcIlwiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLmh0bWxcIlxuICAgICAgICBjb250cm9sbGVyOiAnSm9iUGxhbkNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5wbGFuLm5vZGVcIixcbiAgICB1cmw6IFwiL3ZlcnRleC97bm9kZWlkfVwiXG4gICAgdmlld3M6XG4gICAgICBub2RlOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5wbGFuLm5vZGUuaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuTm9kZUNvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi50aW1lbGluZVwiLFxuICAgIHVybDogXCIvdGltZWxpbmVcIlxuICAgIHZpZXdzOlxuICAgICAgZGV0YWlsczpcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IudGltZWxpbmUuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi50aW1lbGluZS52ZXJ0ZXhcIixcbiAgICB1cmw6IFwiL3t2ZXJ0ZXhJZH1cIlxuICAgIHZpZXdzOlxuICAgICAgdmVydGV4OlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS52ZXJ0ZXguaHRtbFwiXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5zdGF0aXN0aWNzXCIsXG4gICAgdXJsOiBcIi9zdGF0a
XN0aWNzXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnN0YXRpc3RpY3MuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5leGNlcHRpb25zXCIsXG4gICAgdXJsOiBcIi9leGNlcHRpb25zXCJcbiAgICB2aWV3czpcbiAgICAgIGRldGFpbHM6XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLmV4Y2VwdGlvbnMuaHRtbFwiXG5cbiAgLnN0YXRlIFwic2luZ2xlLWpvYi5jb25maWdcIixcbiAgICB1cmw6IFwiL2NvbmZpZ1wiXG4gICAgdmlld3M6XG4gICAgICBkZXRhaWxzOlxuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi5jb25maWcuaHRtbFwiXG5cbiAgJHVybFJvdXRlclByb3ZpZGVyLm90aGVyd2lzZSBcIi9vdmVydmlld1wiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnLCBbJ3VpLnJvdXRlcicsICdhbmd1bGFyTW9tZW50J10pLnJ1bihmdW5jdGlvbigkcm9vdFNjb3BlKSB7XG4gICRyb290U2NvcGUuc2lkZWJhclZpc2libGUgPSBmYWxzZTtcbiAgcmV0dXJuICRyb290U2NvcGUuc2hvd1NpZGViYXIgPSBmdW5jdGlvbigpIHtcbiAgICAkcm9vdFNjb3BlLnNpZGViYXJWaXNpYmxlID0gISRyb290U2NvcGUuc2lkZWJhclZpc2libGU7XG4gICAgcmV0dXJuICRyb290U2NvcGUuc2lkZWJhckNsYXNzID0gJ2ZvcmNlLXNob3cnO1xuICB9O1xufSkuY29uc3RhbnQoJ2
ZsaW5rQ29uZmlnJywge1xuICBqb2JTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnLFxuICBuZXdTZXJ2ZXI6ICdodHRwOi8vbG9jYWxob3N0OjgwODEnLFxuICByZWZyZXNoSW50ZXJ2YWw6IDEwMDAwXG59KS5ydW4oZnVuY3Rpb24oSm9ic1NlcnZpY2UsIGZsaW5rQ29uZmlnLCAkaW50ZXJ2YWwpIHtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmxpc3RKb2JzKCk7XG59KS5jb25maWcoZnVuY3Rpb24oJHN0YXRlUHJvdmlkZXIsICR1cmxSb3V0ZXJQcm92aWRlcikge1xuICAkc3RhdGVQcm92aWRlci5zdGF0ZShcIm92ZXJ2aWV3XCIsIHtcbiAgICB1cmw6IFwiL292ZXJ2aWV3XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvb3ZlcnZpZXcuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnT3ZlcnZpZXdDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJydW5uaW5nLWpvYnNcIiwge1xuICAgIHVybDogXCIvcnVubmluZy1qb2JzXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9ydW5uaW5nLWpvYnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnUnVubmluZ0pvYnNDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJjb21wbGV0ZWQtam9ic1wiLCB7XG4gICAgdXJsOiBcIi9jb21wbGV0ZWQtam9ic1wiLFxuICA
gIHZpZXdzOiB7XG4gICAgICBtYWluOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvY29tcGxldGVkLWpvYnMuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnQ29tcGxldGVkSm9ic0NvbnRyb2xsZXInXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2JcIiwge1xuICAgIHVybDogXCIvam9icy97am9iaWR9XCIsXG4gICAgYWJzdHJhY3Q6IHRydWUsXG4gICAgdmlld3M6IHtcbiAgICAgIG1haW46IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnU2luZ2xlSm9iQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuXCIsIHtcbiAgICB1cmw6IFwiXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5odG1sXCIsXG4gICAgICAgIGNvbnRyb2xsZXI6ICdKb2JQbGFuQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5wbGFuLm5vZGVcIiwge1xuICAgIHVybDogXCIvdmVydGV4L3tub2RlaWR9XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIG5vZGU6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IucGxhbi5ub2RlLmh0bWxcIixcbiAgICAgICAgY29udHJvbGxlcjogJ0pv
YlBsYW5Ob2RlQ29udHJvbGxlcidcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi50aW1lbGluZVwiLCB7XG4gICAgdXJsOiBcIi90aW1lbGluZVwiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnRpbWVsaW5lLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnRpbWVsaW5lLnZlcnRleFwiLCB7XG4gICAgdXJsOiBcIi97dmVydGV4SWR9XCIsXG4gICAgdmlld3M6IHtcbiAgICAgIHZlcnRleDoge1xuICAgICAgICB0ZW1wbGF0ZVVybDogXCJwYXJ0aWFscy9qb2JzL2pvYi50aW1lbGluZS52ZXJ0ZXguaHRtbFwiLFxuICAgICAgICBjb250cm9sbGVyOiAnSm9iVGltZWxpbmVWZXJ0ZXhDb250cm9sbGVyJ1xuICAgICAgfVxuICAgIH1cbiAgfSkuc3RhdGUoXCJzaW5nbGUtam9iLnN0YXRpc3RpY3NcIiwge1xuICAgIHVybDogXCIvc3RhdGlzdGljc1wiLFxuICAgIHZpZXdzOiB7XG4gICAgICBkZXRhaWxzOiB7XG4gICAgICAgIHRlbXBsYXRlVXJsOiBcInBhcnRpYWxzL2pvYnMvam9iLnN0YXRpc3RpY3MuaHRtbFwiXG4gICAgICB9XG4gICAgfVxuICB9KS5zdGF0ZShcInNpbmdsZS1qb2IuZXhjZXB0aW9uc1wiLCB7XG4gICAgdXJsOiBcIi9leGNlcHRpb25zXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFyd
GlhbHMvam9icy9qb2IuZXhjZXB0aW9ucy5odG1sXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pLnN0YXRlKFwic2luZ2xlLWpvYi5jb25maWdcIiwge1xuICAgIHVybDogXCIvY29uZmlnXCIsXG4gICAgdmlld3M6IHtcbiAgICAgIGRldGFpbHM6IHtcbiAgICAgICAgdGVtcGxhdGVVcmw6IFwicGFydGlhbHMvam9icy9qb2IuY29uZmlnLmh0bWxcIlxuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIHJldHVybiAkdXJsUm91dGVyUHJvdmlkZXIub3RoZXJ3aXNlKFwiL292ZXJ2aWV3XCIpO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2
VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2JzTGFiZWwnLCAoSm9ic1NlcnZpY2UpIC0+XG4gIHRyYW5zY2x1ZGU6IHRydWVcbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6IFwiQFwiXG5cbiAgdGVtcGxhdGU6IFwiPHNwYW4gdGl0bGU9J3t7c3RhdHVzfX0nIG5nLWNsYXNzPSdnZXRMYWJlbENsYXNzKCknPjxuZy10cmFuc2NsdWRlPjwvbmctdHJhbnNjbHVkZT48L3NwYW4+XCJcbiAgXG4gIGxpbms6IChzY29wZSwgZWxlbWVudCwgYXR0cnMpIC0+XG4gICAgc2NvcGUuZ2V0TGFiZWxDbGFzcyA9IC0+XG4gICAgICA
nbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmRpcmVjdGl2ZSAnaW5kaWNhdG9yUHJpbWFyeScsIChKb2JzU2VydmljZSkgLT5cbiAgcmVwbGFjZTogdHJ1ZVxuICBzY29wZTogXG4gICAgZ2V0TGFiZWxDbGFzczogXCImXCJcbiAgICBzdGF0dXM6ICdAJ1xuXG4gIHRlbXBsYXRlOiBcIjxpIHRpdGxlPSd7e3N0YXR1c319JyBuZy1jbGFzcz0nZ2V0TGFiZWxDbGFzcygpJyAvPlwiXG4gIFxuICBsaW5rOiAoc2NvcGUsIGVsZW1lbnQsIGF0dHJzKSAtPlxuICAgIHNjb3BlLmdldExhYmVsQ2xhc3MgPSAtPlxuICAgICAgJ2ZhIGZhLWNpcmNsZSBpbmRpY2F0b3IgaW5kaWNhdG9yLScgKyBKb2JzU2VydmljZS50cmFuc2xhdGVMYWJlbFN0YXRlKGF0dHJzLnN0YXR1cylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3RhYmxlUHJvcGVydHknLCAtPlxuICByZXBsYWNlOiB0cnVlXG4gIHNjb3BlOlxuICAgIHZhbHVlOiAnPSdcblxuICB0ZW1wbGF0ZTogXCI8dGQgdGl0bGU9XFxcInt7dmFsdWUgfHwgJ05vbmUnfX1cXFwiPnt7dmFsdWUgfHwgJ05vbmUnfX08L3RkPlwiXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ2JzTGFiZWwnLCBmdW5jdGlvbihKb2JzU2Vy
dmljZSkge1xuICByZXR1cm4ge1xuICAgIHRyYW5zY2x1ZGU6IHRydWUsXG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgZ2V0TGFiZWxDbGFzczogXCImXCIsXG4gICAgICBzdGF0dXM6IFwiQFwiXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8c3BhbiB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKSc+PG5nLXRyYW5zY2x1ZGU+PC9uZy10cmFuc2NsdWRlPjwvc3Bhbj5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlbENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnbGFiZWwgbGFiZWwtJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdpbmRpY2F0b3JQcmltYXJ5JywgZnVuY3Rpb24oSm9ic1NlcnZpY2UpIHtcbiAgcmV0dXJuIHtcbiAgICByZXBsYWNlOiB0cnVlLFxuICAgIHNjb3BlOiB7XG4gICAgICBnZXRMYWJlbENsYXNzOiBcIiZcIixcbiAgICAgIHN0YXR1czogJ0AnXG4gICAgfSxcbiAgICB0ZW1wbGF0ZTogXCI8aSB0aXRsZT0ne3tzdGF0dXN9fScgbmctY2xhc3M9J2dldExhYmVsQ2xhc3MoKScgLz5cIixcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbWVudCwgYXR0cnMpIHtcbiAgICAgIHJldHVybiBzY29wZS5nZXRMYWJlb
ENsYXNzID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiAnZmEgZmEtY2lyY2xlIGluZGljYXRvciBpbmRpY2F0b3ItJyArIEpvYnNTZXJ2aWNlLnRyYW5zbGF0ZUxhYmVsU3RhdGUoYXR0cnMuc3RhdHVzKTtcbiAgICAgIH07XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCd0YWJsZVByb3BlcnR5JywgZnVuY3Rpb24oKSB7XG4gIHJldHVybiB7XG4gICAgcmVwbGFjZTogdHJ1ZSxcbiAgICBzY29wZToge1xuICAgICAgdmFsdWU6ICc9J1xuICAgIH0sXG4gICAgdGVtcGxhdGU6IFwiPHRkIHRpdGxlPVxcXCJ7e3ZhbHVlIHx8ICdOb25lJ319XFxcIj57e3ZhbHVlIHx8ICdOb25lJ319PC90ZD5cIlxuICB9O1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdS
BtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmZpbHRlciBcImFtRHVyYXRpb25Gb3JtYXRFeHRlbmRlZFwiLCAoYW5ndWxhck1vbWVudENvbmZpZykgLT5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyID0gKHZhbHVlLCBmb3JtYXQsIGR1cmF0aW9uRm9ybWF0KSAtPlxuICAgIHJldHVybiBcIlwiICBpZiB0eXBlb2YgdmFsdWUgaXMgXCJ1bmRlZmluZWRcIiBvciB2YWx1ZSBpcyBudWxsXG5cbiAgICBtb21lbnQuZHVyYXRpb24odmFsdWUsIGZvcm1hdCkuZm9ybWF0KGR1cmF0aW9uRm9ybWF0LCB7IHRyaW06IGZhbHNlIH0pXG5cbiAgYW1EdXJhdGlvbkZ
vcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzXG5cbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5maWx0ZXIoXCJhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRcIiwgZnVuY3Rpb24oYW5ndWxhck1vbWVudENvbmZpZykge1xuICB2YXIgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyO1xuICBhbUR1cmF0aW9uRm9ybWF0RXh0ZW5kZWRGaWx0ZXIgPSBmdW5jdGlvbih2YWx1ZSwgZm9ybWF0LCBkdXJhdGlvbkZvcm1hdCkge1xuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwidW5kZWZpbmVkXCIgfHwgdmFsdWUgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiBcIlwiO1xuICAgIH1cbiAgICByZXR1cm4gbW9tZW50LmR1cmF0aW9uKHZhbHVlLCBmb3JtYXQpLmZvcm1hdChkdXJhdGlvbkZvcm1hdCwge1xuICAgICAgdHJpbTogZmFsc2VcbiAgICB9KTtcbiAgfTtcbiAgYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyLiRzdGF0ZWZ1bCA9IGFuZ3VsYXJNb21lbnRDb25maWcuc3RhdGVmdWxGaWx0ZXJzO1xuICByZXR1cm4gYW1EdXJhdGlvbkZvcm1hdEV4dGVuZGVkRmlsdGVyO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVu
dHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuLmNvbnRyb2xsZ
XIgJ1J1bm5pbmdKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ3J1bm5pbmcnKVxuXG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKVxuICAkc2NvcGUuJG9uICckZGVzdHJveScsIC0+XG4gICAgSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcblxuICAkc2NvcGUuam9iT2JzZXJ2ZXIoKVxuXG4jIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5jb250cm9sbGVyICdDb21wbGV0ZWRKb2JzQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgJHNjb3BlLmpvYk9ic2VydmVyID0gLT5cbiAgICAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJylcblxuICBKb2JzU2VydmljZS5yZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcilcbiAgJHNjb3BlLiRvbiAnJGRlc3Ryb3knLCAtPlxuICAgIEpvYnNTZXJ2aWNlLnVuUmVnaXN0ZXJPYnNlcnZlcigkc2NvcGUuam9iT2JzZXJ2ZXIpXG5cbiAgJHNjb3BlLmpvYk9ic2VydmVyKClcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbG
xlciAnU2luZ2xlSm9iQ29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSwgJHJvb3RTY29wZSkgLT5cbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkXG4gICRyb290U2NvcGUuam9iID0gbnVsbFxuXG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuIChkYXRhKSAtPlxuICAgICRyb290U2NvcGUuam9iID0gZGF0YVxuXG4gICRzY29wZS4kb24gJyRkZXN0cm95JywgLT5cbiAgICAkcm9vdFNjb3BlLmpvYiA9IG51bGxcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbkNvbnRyb2xsZXInLCAoJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIC0+XG4gIEpvYnNTZXJ2aWNlLmxvYWRQbGFuKCRzdGF0ZVBhcmFtcy5qb2JpZCkudGhlbiAoZGF0YSkgLT5cbiAgICAkc2NvcGUucGxhbiA9IGRhdGFcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uY29udHJvbGxlciAnSm9iUGxhbk5vZGVDb250cm9sbGVyJywgKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSAtPlxuICAkc2NvcGUubm9kZWlkID0gJHN0YXRlUGFyYW1zLm5vZGVpZFxuICAkc2NvcGUuc3RhdGVMaXN0ID0gSm9ic1NlcnZpY2Uuc3RhdGVMaXN0KClcblxuICBKb2JzU2VydmljZS5nZXROb2RlKCRzY29
wZS5ub2RlaWQpLnRoZW4gKGRhdGEpIC0+XG4gICAgJHNjb3BlLm5vZGUgPSBkYXRhXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLmNvbnRyb2xsZXIgJ0pvYlRpbWVsaW5lVmVydGV4Q29udHJvbGxlcicsICgkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkgLT5cbiAgSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy5qb2JpZCwgJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuIChkYXRhKSAtPlxuICAgICRzY29wZS52ZXJ0ZXggPSBkYXRhXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5jb250cm9sbGVyKCdSdW5uaW5nSm9ic0NvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICAkc2NvcGUuam9iT2JzZXJ2ZXIgPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJHNjb3BlLmpvYnMgPSBKb2JzU2VydmljZS5nZXRKb2JzKCdydW5uaW5nJyk7XG4gIH07XG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdDb21wbGV0ZWRKb2JzQ29udHJv
bGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gICRzY29wZS5qb2JPYnNlcnZlciA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAkc2NvcGUuam9icyA9IEpvYnNTZXJ2aWNlLmdldEpvYnMoJ2ZpbmlzaGVkJyk7XG4gIH07XG4gIEpvYnNTZXJ2aWNlLnJlZ2lzdGVyT2JzZXJ2ZXIoJHNjb3BlLmpvYk9ic2VydmVyKTtcbiAgJHNjb3BlLiRvbignJGRlc3Ryb3knLCBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gSm9ic1NlcnZpY2UudW5SZWdpc3Rlck9ic2VydmVyKCRzY29wZS5qb2JPYnNlcnZlcik7XG4gIH0pO1xuICByZXR1cm4gJHNjb3BlLmpvYk9ic2VydmVyKCk7XG59KS5jb250cm9sbGVyKCdTaW5nbGVKb2JDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UsICRyb290U2NvcGUpIHtcbiAgJHNjb3BlLmpvYmlkID0gJHN0YXRlUGFyYW1zLmpvYmlkO1xuICAkcm9vdFNjb3BlLmpvYiA9IG51bGw7XG4gIEpvYnNTZXJ2aWNlLmxvYWRKb2IoJHN0YXRlUGFyYW1zLmpvYmlkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHJvb3RTY29wZS5qb2IgPSBkYXRhO1xuICB9KTtcbiAgcmV0dXJuICRzY29wZS4kb24oJyRkZXN0cm95JywgZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuICRyb290U2NvcGUuam9iID0gbnVsbDtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JQbGFuQ29ud
HJvbGxlcicsIGZ1bmN0aW9uKCRzY29wZSwgJHN0YXRlLCAkc3RhdGVQYXJhbXMsIEpvYnNTZXJ2aWNlKSB7XG4gIHJldHVybiBKb2JzU2VydmljZS5sb2FkUGxhbigkc3RhdGVQYXJhbXMuam9iaWQpLnRoZW4oZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiAkc2NvcGUucGxhbiA9IGRhdGE7XG4gIH0pO1xufSkuY29udHJvbGxlcignSm9iUGxhbk5vZGVDb250cm9sbGVyJywgZnVuY3Rpb24oJHNjb3BlLCAkc3RhdGUsICRzdGF0ZVBhcmFtcywgSm9ic1NlcnZpY2UpIHtcbiAgJHNjb3BlLm5vZGVpZCA9ICRzdGF0ZVBhcmFtcy5ub2RlaWQ7XG4gICRzY29wZS5zdGF0ZUxpc3QgPSBKb2JzU2VydmljZS5zdGF0ZUxpc3QoKTtcbiAgcmV0dXJuIEpvYnNTZXJ2aWNlLmdldE5vZGUoJHNjb3BlLm5vZGVpZCkudGhlbihmdW5jdGlvbihkYXRhKSB7XG4gICAgcmV0dXJuICRzY29wZS5ub2RlID0gZGF0YTtcbiAgfSk7XG59KS5jb250cm9sbGVyKCdKb2JUaW1lbGluZVZlcnRleENvbnRyb2xsZXInLCBmdW5jdGlvbigkc2NvcGUsICRzdGF0ZSwgJHN0YXRlUGFyYW1zLCBKb2JzU2VydmljZSkge1xuICByZXR1cm4gSm9ic1NlcnZpY2UuZ2V0VmVydGV4KCRzdGF0ZVBhcmFtcy5qb2JpZCwgJHN0YXRlUGFyYW1zLnZlcnRleElkKS50aGVuKGZ1bmN0aW9uKGRhdGEpIHtcbiAgICByZXR1cm4gJHNjb3BlLnZlcnRleCA9IGRhdGE7XG4gIH0pO1xufSk7XG4iLCIjXG4jIExpY2Vuc2VkIHRvIHRoZSBBcGFjaGUgU29mdHdhcmUgRm
91bmRhdGlvbiAoQVNGKSB1bmRlciBvbmVcbiMgb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4jIGRpc3RyaWJ1dGVkIHdpdGggdGhpcyB3b3JrIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uXG4jIHJlZ2FyZGluZyBjb3B5cmlnaHQgb3duZXJzaGlwLiAgVGhlIEFTRiBsaWNlbnNlcyB0aGlzIGZpbGVcbiMgdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuIyBcIkxpY2Vuc2VcIik7IHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiMgd2l0aCB0aGUgTGljZW5zZS4gIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuI1xuIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4jXG4jIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiMgU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuIyBsaW1pdGF0aW9ucyB
1bmRlciB0aGUgTGljZW5zZS5cbiNcblxuYW5ndWxhci5tb2R1bGUoJ2ZsaW5rQXBwJylcblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ3ZlcnRleCcsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCJcblxuICBzY29wZTpcbiAgICBkYXRhOiBcIj1cIlxuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF1cblxuICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKClcbiAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVylcblxuICAgIGFuYWx5emVUaW1lID0gKGRhdGEpIC0+XG4gICAgICB0ZXN0RGF0YSA9IFtdXG5cbiAgICAgIGFuZ3VsYXIuZm9yRWFjaCBkYXRhLmdyb3VwdmVydGV4Lmdyb3VwbWVtYmVycywgKHZlcnRleCwgaSkgLT5cbiAgICAgICAgdlRpbWUgPSBkYXRhLnZlcnRpY2V0aW1lc1t2ZXJ0ZXgudmVydGV4aWRdXG5cbiAgICAgICAgdGVzdERhdGEucHVzaCB7XG4gICAgICAgICAgbGFiZWw6IFwiI3t2ZXJ0ZXgudmVydGV4aW5zdGFuY2VuYW1lfSAoI3tpfSlcIlxuICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiXG4gICAgICAgICAgICAgIGNv
bG9yOiBcIiM2NjZcIlxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIlNDSEVEVUxFRFwiXSAqIDEwMFxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJERVBMT1lJTkdcIl0gKiAxMDBcbiAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIkRlcGxveWluZ1wiXG4gICAgICAgICAgICAgIGNvbG9yOiBcIiNhYWFcIlxuICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIkRFUExPWUlOR1wiXSAqIDEwMFxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJSVU5OSU5HXCJdICogMTAwXG4gICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsYWJlbDogXCJSdW5uaW5nXCJcbiAgICAgICAgICAgICAgY29sb3I6IFwiI2RkZFwiXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiUlVOTklOR1wiXSAqIDEwMFxuICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJGSU5JU0hFRFwiXSAqIDEwMFxuICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBdXG4gICAgICAgIH1cblxuICAgICAgY2hhcnQgPSBkMy50aW1lbGluZSgpLnN0YWNrKClcbiAgICAgIC50aWNrRm9ybWF0KHtcbiAgICAgICAgZm9ybWF0OiBkM
y50aW1lLmZvcm1hdChcIiVMXCIpXG4gICAgICAgICMgdGlja0ludGVydmFsOiAxXG4gICAgICAgIHRpY2tTaXplOiAxXG4gICAgICB9KVxuICAgICAgLnByZWZpeChcInNpbmdsZVwiKVxuICAgICAgLmxhYmVsRm9ybWF0KChsYWJlbCkgLT5cbiAgICAgICAgbGFiZWxcbiAgICAgIClcbiAgICAgIC5tYXJnaW4oeyBsZWZ0OiAxMDAsIHJpZ2h0OiAwLCB0b3A6IDAsIGJvdHRvbTogMCB9KVxuICAgICAgLml0ZW1IZWlnaHQoMjQpXG4gICAgICAucmVsYXRpdmVUaW1lKClcblxuICAgICAgc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKVxuICAgICAgLmRhdHVtKHRlc3REYXRhKVxuICAgICAgLmNhbGwoY2hhcnQpXG5cbiAgICBhbmFseXplVGltZShzY29wZS5kYXRhKVxuXG4gICAgcmV0dXJuXG5cbiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4uZGlyZWN0aXZlICd0aW1lbGluZScsICgkc3RhdGUpIC0+XG4gIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lJyB3aWR0aD0nMCcgaGVpZ2h0PScwJz48L3N2Zz5cIlxuXG4gIHNjb3BlOlxuICAgIGpvYjogXCI9XCJcblxuICBsaW5rOiAoc2NvcGUsIGVsZW0sIGF0dHJzKSAtPlxuICAgIHN2Z0VsID0gZWxlbS5jaGlsZHJlbigpWzBdXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpXG5cbiAgICB0cmFuc2xhdGVMYWJlbC
A9IChsYWJlbCkgLT5cbiAgICAgIGxhYmVsLnJlcGxhY2UoXCImZ3Q7XCIsIFwiPlwiKVxuXG4gICAgYW5hbHl6ZVRpbWUgPSAoZGF0YSkgLT5cbiAgICAgIHRlc3REYXRhID0gW11cblxuICAgICAgdGVzdERhdGEucHVzaCBcbiAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICBsYWJlbDogXCJTY2hlZHVsZWRcIlxuICAgICAgICAgIGNvbG9yOiBcIiNjY2NjY2NcIlxuICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEub2xkVltcIlNDSEVEVUxFRFwiXVxuICAgICAgICAgIGVuZGluZ190aW1lOiBkYXRhLm9sZFZbXCJTQ0hFRFVMRURcIl0gKyAzMFxuICAgICAgICAgICMgbGluazogdmVydGV4Lmdyb3VwdmVydGV4aWRcbiAgICAgICAgICB0eXBlOiAnc2NoZWR1bGVkJ1xuICAgICAgICBdXG5cblxuICAgICAgYW5ndWxhci5mb3JFYWNoIGRhdGEub2xkVi5ncm91cHZlcnRpY2VzLCAodmVydGV4KSAtPlxuICAgICAgICB2VGltZSA9IGRhdGEub2xkVi5ncm91cHZlcnRpY2V0aW1lc1t2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhpZF1cblxuICAgICAgICAjIGNvbnNvbGUubG9nIHZUaW1lLCB2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhpZFxuXG4gICAgICAgIHRlc3REYXRhLnB1c2ggXG4gICAgICAgICAgdGltZXM6IFtcbiAgICAgICAgICAgIGxhYmVsOiB0cmFuc2xhdGVMYWJlbCh2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhuYW1lKVxuICAgICAgICAgICAgY29sb3I6IFwiIzYyY2RlYVwiXG4gICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVt
cIlNUQVJURURcIl1cbiAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkVOREVEXCJdXG4gICAgICAgICAgICBsaW5rOiB2ZXJ0ZXguZ3JvdXB2ZXJ0ZXhpZFxuICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgXVxuXG4gICAgICBjaGFydCA9IGQzLnRpbWVsaW5lKCkuc3RhY2soKS5jbGljaygoZCwgaSwgZGF0dW0pIC0+XG4gICAgICAgIGlmIGQubGlua1xuICAgICAgICAgICRzdGF0ZS5nbyBcInNpbmdsZS1qb2IudGltZWxpbmUudmVydGV4XCIsIHsgam9iaWQ6IGRhdGEuamlkLCB2ZXJ0ZXhJZDogZC5saW5rIH1cblxuICAgICAgKVxuICAgICAgLnRpY2tGb3JtYXQoe1xuICAgICAgICBmb3JtYXQ6IGQzLnRpbWUuZm9ybWF0KFwiJUxcIilcbiAgICAgICAgIyB0aWNrVGltZTogZDMudGltZS5zZWNvbmRcbiAgICAgICAgIyB0aWNrSW50ZXJ2YWw6IDAuNVxuICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgfSlcbiAgICAgIC5wcmVmaXgoXCJtYWluXCIpXG4gICAgICAubWFyZ2luKHsgbGVmdDogMCwgcmlnaHQ6IDAsIHRvcDogMCwgYm90dG9tOiAwIH0pXG4gICAgICAuaXRlbUhlaWdodCgyNClcbiAgICAgIC5zaG93Qm9yZGVyTGluZSgpXG4gICAgICAuc2hvd0hvdXJUaW1lbGluZSgpXG5cbiAgICAgIHN2ZyA9IGQzLnNlbGVjdChzdmdFbClcbiAgICAgIC5kYXR1bSh0ZXN0RGF0YSlcbiAgICAgIC5jYWxsKGNoYXJ0KVxuXG4gICAgc2NvcGUuJHdhdGNoIGF0dHJzLmpvYiwgKGRhdGEp
IC0+XG4gICAgICBhbmFseXplVGltZShkYXRhKSBpZiBkYXRhXG5cbiAgICByZXR1cm5cblxuIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi5kaXJlY3RpdmUgJ2pvYlBsYW4nLCAoJHRpbWVvdXQpIC0+XG4gIHRlbXBsYXRlOiBcIlxuICAgIDxzdmcgY2xhc3M9J2dyYXBoJyB3aWR0aD0nNTAwJyBoZWlnaHQ9JzQwMCc+PGcgLz48L3N2Zz5cbiAgICA8c3ZnIGNsYXNzPSd0bXAnIHdpZHRoPScxJyBoZWlnaHQ9JzEnPjxnIC8+PC9zdmc+XG4gICAgPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+XG4gICAgICA8YSBjbGFzcz0nYnRuIGJ0bi1kZWZhdWx0IHpvb20taW4nIG5nLWNsaWNrPSd6b29tSW4oKSc+PGkgY2xhc3M9J2ZhIGZhLXBsdXMnIC8+PC9hPlxuICAgICAgPGEgY2xhc3M9J2J0biBidG4tZGVmYXVsdCB6b29tLW91dCcgbmctY2xpY2s9J3pvb21PdXQoKSc+PGkgY2xhc3M9J2ZhIGZhLW1pbnVzJyAvPjwvYT5cbiAgICA8L2Rpdj5cIlxuXG4gIHNjb3BlOlxuICAgIHBsYW46ICc9J1xuXG4gIGxpbms6IChzY29wZSwgZWxlbSwgYXR0cnMpIC0+XG4gICAgbWFpblpvb20gPSBkMy5iZWhhdmlvci56b29tKClcbiAgICBzdWJncmFwaHMgPSBbXVxuICAgIGpvYmlkID0gYXR0cnMuam9iaWRcblxuICAgIG1haW5TdmdFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzBdXG4gICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXVxuICAgI
G1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdXG5cbiAgICBkM21haW5TdmcgPSBkMy5zZWxlY3QobWFpblN2Z0VsZW1lbnQpXG4gICAgZDNtYWluU3ZnRyA9IGQzLnNlbGVjdChtYWluRylcbiAgICBkM3RtcFN2ZyA9IGQzLnNlbGVjdChtYWluVG1wRWxlbWVudClcblxuICAgICMgYW5ndWxhci5lbGVtZW50KG1haW5HKS5lbXB0eSgpXG5cbiAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpXG4gICAgYW5ndWxhci5lbGVtZW50KGVsZW0uY2hpbGRyZW4oKVswXSkud2lkdGgoY29udGFpbmVyVylcblxuICAgIHNjb3BlLnpvb21JbiA9IC0+XG4gICAgICBpZiBtYWluWm9vbS5zY2FsZSgpIDwgMi45OVxuICAgICAgICBcbiAgICAgICAgIyBDYWxjdWxhdGUgYW5kIHN0b3JlIG5ldyB2YWx1ZXMgaW4gem9vbSBvYmplY3RcbiAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKClcbiAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgbWFpblpvb20uc2NhbGUgbWFpblpvb20uc2NhbGUoKSArIDAuMVxuICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUgWyB2MSwgdjIgXVxuICAgICAgICBcbiAgICAgICAgIyBUcmFuc2Zvcm0gc3ZnXG4gICAgICAgIGQzbWFpblN2Z0
cuYXR0ciBcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHYxICsgXCIsXCIgKyB2MiArIFwiKSBzY2FsZShcIiArIG1haW5ab29tLnNjYWxlKCkgKyBcIilcIlxuXG4gICAgc2NvcGUuem9vbU91dCA9IC0+XG4gICAgICBpZiBtYWluWm9vbS5zY2FsZSgpID4gMC4zMVxuICAgICAgICBcbiAgICAgICAgIyBDYWxjdWxhdGUgYW5kIHN0b3JlIG5ldyB2YWx1ZXMgaW4gbWFpblpvb20gb2JqZWN0XG4gICAgICAgIG1haW5ab29tLnNjYWxlIG1haW5ab29tLnNjYWxlKCkgLSAwLjFcbiAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKClcbiAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSAtIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSlcbiAgICAgICAgbWFpblpvb20udHJhbnNsYXRlIFsgdjEsIHYyIF1cbiAgICAgICAgXG4gICAgICAgICMgVHJhbnNmb3JtIHN2Z1xuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCJcblxuICAgICNjcmVhdGUgYSBsYWJlbCBvZiBhbiBlZGdlXG4gICAgY3JlYXRlTGFiZWxFZGdlID0gKGVsKSAtPlxuICAgICAgbGFiZWxWYWx1ZSA9IFwiXCJcbiA
gICAgIGlmIGVsLnNoaXBfc3RyYXRlZ3k/IG9yIGVsLmxvY2FsX3N0cmF0ZWd5P1xuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGRpdiBjbGFzcz0nZWRnZS1sYWJlbCc+XCJcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBlbC5zaGlwX3N0cmF0ZWd5ICBpZiBlbC5zaGlwX3N0cmF0ZWd5P1xuICAgICAgICBsYWJlbFZhbHVlICs9IFwiIChcIiArIGVsLnRlbXBfbW9kZSArIFwiKVwiICB1bmxlc3MgZWwudGVtcF9tb2RlIGlzIGB1bmRlZmluZWRgXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCIsPGJyPlwiICsgZWwubG9jYWxfc3RyYXRlZ3kgIHVubGVzcyBlbC5sb2NhbF9zdHJhdGVneSBpcyBgdW5kZWZpbmVkYFxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPC9kaXY+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuXG4gICAgIyB0cnVlLCBpZiB0aGUgbm9kZSBpcyBhIHNwZWNpYWwgbm9kZSBmcm9tIGFuIGl0ZXJhdGlvblxuICAgIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUgPSAoaW5mbykgLT5cbiAgICAgIChpbmZvIGlzIFwicGFydGlhbFNvbHV0aW9uXCIgb3IgaW5mbyBpcyBcIm5leHRQYXJ0aWFsU29sdXRpb25cIiBvciBpbmZvIGlzIFwid29ya3NldFwiIG9yIGluZm8gaXMgXCJuZXh0V29ya3NldFwiIG9yIGluZm8gaXMgXCJzb2x1dGlvblNldFwiIG9yIGluZm8gaXMgXCJzb2x1dGlvbkRlbHRhXCIpXG5cbiAgICBnZXROb2RlVHlwZSA9IChlbCwgaW5mbykgLT5cbiAgICAgIGlmIGluZm8gaXMgXCJtaXJyb3JcIlxuICAg
ICAgICAnbm9kZS1taXJyb3InXG5cbiAgICAgIGVsc2UgaWYgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKVxuICAgICAgICAnbm9kZS1pdGVyYXRpb24nXG5cbiAgICAgIGVsc2VcbiAgICAgICAgICAnbm9kZS1ub3JtYWwnXG4gICAgICBcbiAgICAjIGNyZWF0ZXMgdGhlIGxhYmVsIG9mIGEgbm9kZSwgaW4gaW5mbyBpcyBzdG9yZWQsIHdoZXRoZXIgaXQgaXMgYSBzcGVjaWFsIG5vZGUgKGxpa2UgYSBtaXJyb3IgaW4gYW4gaXRlcmF0aW9uKVxuICAgIGNyZWF0ZUxhYmVsTm9kZSA9IChlbCwgaW5mbywgbWF4VywgbWF4SCkgLT5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIjxhIGhyZWY9JyMvam9icy9cIiArIGpvYmlkICsgXCIvdmVydGV4L1wiICsgZWwuaWQgKyBcIicgY2xhc3M9J25vZGUtbGFiZWwgXCIgKyBnZXROb2RlVHlwZShlbCwgaW5mbykgKyBcIic+XCJcblxuICAgICAgIyBOb2RlbmFtZVxuICAgICAgaWYgaW5mbyBpcyBcIm1pcnJvclwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+TWlycm9yIG9mIFwiICsgZWwub3BlcmF0b3IgKyBcIjwvaDM+XCJcbiAgICAgIGVsc2VcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoMyBjbGFzcz0nbm9kZS1uYW1lJz5cIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiXG4gICAgICBpZiBlbC5kZXNjcmlwdGlvbiBpcyBcIlwiXG4gICAgICAgIGxhYmVsVmFsdWUgKz0gXCJcIlxuICAgICAgZWxzZVxuICAgICAgICBzdGVwTmFtZ
SA9IGVsLmRlc2NyaXB0aW9uXG4gICAgICAgIFxuICAgICAgICAjIGNsZWFuIHN0ZXBOYW1lXG4gICAgICAgIHN0ZXBOYW1lID0gc2hvcnRlblN0cmluZyhzdGVwTmFtZSlcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNCBjbGFzcz0nc3RlcC1uYW1lJz5cIiArIHN0ZXBOYW1lICsgXCI8L2g0PlwiXG4gICAgICBcbiAgICAgICMgSWYgdGhpcyBub2RlIGlzIGFuIFwiaXRlcmF0aW9uXCIgd2UgbmVlZCBhIGRpZmZlcmVudCBwYW5lbC1ib2R5XG4gICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uP1xuICAgICAgICBsYWJlbFZhbHVlICs9IGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbihlbC5pZCwgbWF4VywgbWF4SClcbiAgICAgIGVsc2VcbiAgICAgICAgXG4gICAgICAgICMgT3RoZXJ3aXNlIGFkZCBpbmZvcyAgICBcbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxoNT5cIiArIGluZm8gKyBcIiBOb2RlPC9oNT5cIiAgaWYgaXNTcGVjaWFsSXRlcmF0aW9uTm9kZShpbmZvKVxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlBhcmFsbGVsaXNtOiBcIiArIGVsLnBhcmFsbGVsaXNtICsgXCI8L2g1PlwiICB1bmxlc3MgZWwucGFyYWxsZWxpc20gaXMgXCJcIlxuICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1Pk9wZXJhdGlvbjogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLm9wZXJhdG9yX3N0cmF0ZWd5KSArIFwiPC9oNT5cIiAgdW5sZXNzIGVsLm9wZXJhdG9yIGlzIGB1bmRlZmluZWRgXG4gICAgICBcbiAgICAgIGxhYm
VsVmFsdWUgKz0gXCI8L2E+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuICAgICMgRXh0ZW5kcyB0aGUgbGFiZWwgb2YgYSBub2RlIHdpdGggYW4gYWRkaXRpb25hbCBzdmcgRWxlbWVudCB0byBwcmVzZW50IHRoZSBpdGVyYXRpb24uXG4gICAgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uID0gKGlkLCBtYXhXLCBtYXhIKSAtPlxuICAgICAgc3ZnSUQgPSBcInN2Zy1cIiArIGlkXG5cbiAgICAgIGxhYmVsVmFsdWUgPSBcIjxzdmcgY2xhc3M9J1wiICsgc3ZnSUQgKyBcIicgd2lkdGg9XCIgKyBtYXhXICsgXCIgaGVpZ2h0PVwiICsgbWF4SCArIFwiPjxnIC8+PC9zdmc+XCJcbiAgICAgIGxhYmVsVmFsdWVcblxuICAgICMgU3BsaXQgYSBzdHJpbmcgaW50byBtdWx0aXBsZSBsaW5lcyBzbyB0aGF0IGVhY2ggbGluZSBoYXMgbGVzcyB0aGFuIDMwIGxldHRlcnMuXG4gICAgc2hvcnRlblN0cmluZyA9IChzKSAtPlxuICAgICAgIyBtYWtlIHN1cmUgdGhhdCBuYW1lIGRvZXMgbm90IGNvbnRhaW4gYSA8IChiZWNhdXNlIG9mIGh0bWwpXG4gICAgICBpZiBzLmNoYXJBdCgwKSBpcyBcIjxcIlxuICAgICAgICBzID0gcy5yZXBsYWNlKFwiPFwiLCBcIiZsdDtcIilcbiAgICAgICAgcyA9IHMucmVwbGFjZShcIj5cIiwgXCImZ3Q7XCIpXG4gICAgICBzYnIgPSBcIlwiXG4gICAgICB3aGlsZSBzLmxlbmd0aCA+IDMwXG4gICAgICAgIHNiciA9IHNiciArIHMuc3Vic3RyaW5nKDAsIDMwKSArIFwiPGJyPlwiXG4gICAgICAgIHMgPSB
zLnN1YnN0cmluZygzMCwgcy5sZW5ndGgpXG4gICAgICBzYnIgPSBzYnIgKyBzXG4gICAgICBzYnJcblxuICAgIGNyZWF0ZU5vZGUgPSAoZywgZGF0YSwgZWwsIGlzUGFyZW50ID0gZmFsc2UsIG1heFcsIG1heEgpIC0+XG4gICAgICAjIGNyZWF0ZSBub2RlLCBzZW5kIGFkZGl0aW9uYWwgaW5mb3JtYXRpb25zIGFib3V0IHRoZSBub2RlIGlmIGl0IGlzIGEgc3BlY2lhbCBvbmVcbiAgICAgIGlmIGVsLmlkIGlzIGRhdGEucGFydGlhbF9zb2x1dGlvblxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIiwgbWF4VywgbWF4SClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShlbCwgXCJwYXJ0aWFsU29sdXRpb25cIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLm5leHRfcGFydGlhbF9zb2x1dGlvblxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEud29ya3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAg
ICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJ3b3Jrc2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwid29ya3NldFwiKVxuXG4gICAgICBlbHNlIGlmIGVsLmlkIGlzIGRhdGEubmV4dF93b3Jrc2V0XG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIm5leHRXb3Jrc2V0XCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgICBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwibmV4dFdvcmtzZXRcIilcblxuICAgICAgZWxzZSBpZiBlbC5pZCBpcyBkYXRhLnNvbHV0aW9uX3NldFxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJzb2x1dGlvblNldFwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcInNvbHV0aW9uU2V0XCIpXG5cbiAgICAgIGVsc2UgaWYgZWwuaWQgaXMgZGF0YS5zb2x1dGlvbl9kZWx0YVxuICAgICAgICBnLnNldE5vZGUgZWwuaWQsXG4gICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIsIG1heFcsIG1heEgpXG4gICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCdcbiAgICAgICAgI
CBjbGFzczogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25EZWx0YVwiKVxuXG4gICAgICBlbHNlXG4gICAgICAgIGcuc2V0Tm9kZSBlbC5pZCxcbiAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKGVsLCBcIlwiLCBtYXhXLCBtYXhIKVxuICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgY2xhc3M6IGdldE5vZGVUeXBlKGVsLCBcIlwiKVxuXG4gICAgY3JlYXRlRWRnZSA9IChnLCBkYXRhLCBlbCwgZXhpc3RpbmdOb2RlcywgcHJlZCkgLT5cbiAgICAgIHVubGVzcyBleGlzdGluZ05vZGVzLmluZGV4T2YocHJlZC5pZCkgaXMgLTFcbiAgICAgICAgZy5zZXRFZGdlIHByZWQuaWQsIGVsLmlkLFxuICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UocHJlZClcbiAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJ1xuICAgICAgICAgIGFycm93aGVhZDogJ25vcm1hbCdcblxuICAgICAgZWxzZVxuICAgICAgICBtaXNzaW5nTm9kZSA9IHNlYXJjaEZvck5vZGUoZGF0YSwgcHJlZC5pZClcbiAgICAgICAgdW5sZXNzICFtaXNzaW5nTm9kZSBvciBtaXNzaW5nTm9kZS5hbHJlYWR5QWRkZWQgaXMgdHJ1ZVxuICAgICAgICAgIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9IHRydWVcbiAgICAgICAgICBnLnNldE5vZGUgbWlzc2luZ05vZGUuaWQsXG4gICAgICAgICAgICBsYWJlbDogY3JlYXRlTGFiZWxOb2RlKG1pc3NpbmdOb2RlLCBcIm1pcnJvclwiKVxuICAgICAgICAgICAgbGFiZWxUeXBlOi
AnaHRtbCdcbiAgICAgICAgICAgIGNsYXNzOiBnZXROb2RlVHlwZShtaXNzaW5nTm9kZSwgJ21pcnJvcicpXG5cbiAgICAgICAgICBnLnNldEVkZ2UgbWlzc2luZ05vZGUuaWQsIGVsLmlkLFxuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShtaXNzaW5nTm9kZSlcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG5cbiAgICBsb2FkSnNvblRvRGFncmUgPSAoZywgZGF0YSkgLT5cbiAgICAgIGV4aXN0aW5nTm9kZXMgPSBbXVxuXG4gICAgICBpZiBkYXRhLm5vZGVzP1xuICAgICAgICAjIFRoaXMgaXMgdGhlIG5vcm1hbCBqc29uIGRhdGFcbiAgICAgICAgdG9JdGVyYXRlID0gZGF0YS5ub2Rlc1xuXG4gICAgICBlbHNlXG4gICAgICAgICMgVGhpcyBpcyBhbiBpdGVyYXRpb24sIHdlIG5vdyBzdG9yZSBzcGVjaWFsIGl0ZXJhdGlvbiBub2RlcyBpZiBwb3NzaWJsZVxuICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLnN0ZXBfZnVuY3Rpb25cbiAgICAgICAgaXNQYXJlbnQgPSB0cnVlXG5cbiAgICAgIGZvciBlbCBpbiB0b0l0ZXJhdGVcbiAgICAgICAgbWF4VyA9IDBcbiAgICAgICAgbWF4SCA9IDBcblxuICAgICAgICBpZiBlbC5zdGVwX2Z1bmN0aW9uXG4gICAgICAgICAgc2cgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7IG11bHRpZ3JhcGg6IHRydWUsIGNvbXBvdW5kOiB0cnVlIH0pLnNldEdyYXBoKHtcbiAgICAgICAgICAgIG5vZGVzZXA6IDIwXG4gICAgICAgICAgICBlZGdlc2VwOiAwXG4
gICAgICAgICAgICByYW5rc2VwOiAyMFxuICAgICAgICAgICAgcmFua2RpcjogXCJMUlwiXG4gICAgICAgICAgICBtYXJnaW54OiAxMFxuICAgICAgICAgICAgbWFyZ2lueTogMTBcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICBzdWJncmFwaHNbZWwuaWRdID0gc2dcblxuICAgICAgICAgIGxvYWRKc29uVG9EYWdyZShzZywgZWwpXG5cbiAgICAgICAgICByID0gbmV3IGRhZ3JlRDMucmVuZGVyKClcbiAgICAgICAgICBkM3RtcFN2Zy5zZWxlY3QoJ2cnKS5jYWxsKHIsIHNnKVxuICAgICAgICAgIG1heFcgPSBzZy5ncmFwaCgpLndpZHRoXG4gICAgICAgICAgbWF4SCA9IHNnLmdyYXBoKCkuaGVpZ2h0XG5cbiAgICAgICAgICBhbmd1bGFyLmVsZW1lbnQobWFpblRtcEVsZW1lbnQpLmVtcHR5KClcblxuICAgICAgICBjcmVhdGVOb2RlKGcsIGRhdGEsIGVsLCBpc1BhcmVudCwgbWF4VywgbWF4SClcblxuICAgICAgICBleGlzdGluZ05vZGVzLnB1c2ggZWwuaWRcbiAgICAgICAgXG4gICAgICAgICMgY3JlYXRlIGVkZ2VzIGZyb20gaW5wdXRzIHRvIGN1cnJlbnQgbm9kZVxuICAgICAgICBpZiBlbC5pbnB1dHM/XG4gICAgICAgICAgZm9yIHByZWQgaW4gZWwuaW5wdXRzXG4gICAgICAgICAgICBjcmVhdGVFZGdlKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKVxuXG4gICAgICBnXG5cbiAgICAjIHNlYXJjaGVzIGluIHRoZSBnbG9iYWwgSlNPTkRhdGEgZm9yIHRoZSBub2RlIHdpdGggdGhlIGdpdmVuIGlk
XG4gICAgc2VhcmNoRm9yTm9kZSA9IChkYXRhLCBub2RlSUQpIC0+XG4gICAgICBmb3IgaSBvZiBkYXRhLm5vZGVzXG4gICAgICAgIGVsID0gZGF0YS5ub2Rlc1tpXVxuICAgICAgICByZXR1cm4gZWwgIGlmIGVsLmlkIGlzIG5vZGVJRFxuICAgICAgICBcbiAgICAgICAgIyBsb29rIGZvciBub2RlcyB0aGF0IGFyZSBpbiBpdGVyYXRpb25zXG4gICAgICAgIGlmIGVsLnN0ZXBfZnVuY3Rpb24/XG4gICAgICAgICAgZm9yIGogb2YgZWwuc3RlcF9mdW5jdGlvblxuICAgICAgICAgICAgcmV0dXJuIGVsLnN0ZXBfZnVuY3Rpb25bal0gIGlmIGVsLnN0ZXBfZnVuY3Rpb25bal0uaWQgaXMgbm9kZUlEXG5cbiAgICBkcmF3R3JhcGggPSAoZGF0YSkgLT5cbiAgICAgIGcgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7IG11bHRpZ3JhcGg6IHRydWUsIGNvbXBvdW5kOiB0cnVlIH0pLnNldEdyYXBoKHtcbiAgICAgICAgbm9kZXNlcDogNzBcbiAgICAgICAgZWRnZXNlcDogMFxuICAgICAgICByYW5rc2VwOiA1MFxuICAgICAgICByYW5rZGlyOiBcIkxSXCJcbiAgICAgICAgbWFyZ2lueDogNDBcbiAgICAgICAgbWFyZ2lueTogNDBcbiAgICAgICAgfSlcblxuICAgICAgbG9hZEpzb25Ub0RhZ3JlKGcsIGRhdGEpXG5cbiAgICAgIHJlbmRlcmVyID0gbmV3IGRhZ3JlRDMucmVuZGVyKClcbiAgICAgIGQzbWFpblN2Z0cuY2FsbChyZW5kZXJlciwgZylcblxuICAgICAgZm9yIGksIHNnIG9mIHN1YmdyYXBoc1xuICAgICAgICBkM
21haW5Tdmcuc2VsZWN0KCdzdmcuc3ZnLScgKyBpICsgJyBnJykuY2FsbChyZW5kZXJlciwgc2cpXG5cbiAgICAgIG5ld1NjYWxlID0gMC41XG5cbiAgICAgIHhDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLndpZHRoKCkgLSBnLmdyYXBoKCkud2lkdGggKiBuZXdTY2FsZSkgLyAyKVxuICAgICAgeUNlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkuaGVpZ2h0KCkgLSBnLmdyYXBoKCkuaGVpZ2h0ICogbmV3U2NhbGUpIC8gMilcblxuICAgICAgbWFpblpvb20uc2NhbGUobmV3U2NhbGUpLnRyYW5zbGF0ZShbeENlbnRlck9mZnNldCwgeUNlbnRlck9mZnNldF0pXG5cbiAgICAgIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIHhDZW50ZXJPZmZzZXQgKyBcIiwgXCIgKyB5Q2VudGVyT2Zmc2V0ICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKVxuXG4gICAgICBtYWluWm9vbS5vbihcInpvb21cIiwgLT5cbiAgICAgICAgZXYgPSBkMy5ldmVudFxuICAgICAgICBkM21haW5TdmdHLmF0dHIgXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyBldi50cmFuc2xhdGUgKyBcIikgc2NhbGUoXCIgKyBldi5zY2FsZSArIFwiKVwiXG4gICAgICApXG4gICAgICBtYWluWm9vbShkM21haW5TdmcpXG5cbiAgICBzY29wZS4kd2F0Y2ggYXR0cnMucGxhbi
wgKG5ld1BsYW4pIC0+XG4gICAgICBkcmF3R3JhcGgobmV3UGxhbikgaWYgbmV3UGxhblxuXG4gICAgcmV0dXJuXG4iLCJhbmd1bGFyLm1vZHVsZSgnZmxpbmtBcHAnKS5kaXJlY3RpdmUoJ3ZlcnRleCcsIGZ1bmN0aW9uKCRzdGF0ZSkge1xuICByZXR1cm4ge1xuICAgIHRlbXBsYXRlOiBcIjxzdmcgY2xhc3M9J3RpbWVsaW5lIHNlY29uZGFyeScgd2lkdGg9JzAnIGhlaWdodD0nMCc+PC9zdmc+XCIsXG4gICAgc2NvcGU6IHtcbiAgICAgIGRhdGE6IFwiPVwiXG4gICAgfSxcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbSwgYXR0cnMpIHtcbiAgICAgIHZhciBhbmFseXplVGltZSwgY29udGFpbmVyVywgc3ZnRWw7XG4gICAgICBzdmdFbCA9IGVsZW0uY2hpbGRyZW4oKVswXTtcbiAgICAgIGNvbnRhaW5lclcgPSBlbGVtLndpZHRoKCk7XG4gICAgICBhbmd1bGFyLmVsZW1lbnQoc3ZnRWwpLmF0dHIoJ3dpZHRoJywgY29udGFpbmVyVyk7XG4gICAgICBhbmFseXplVGltZSA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGNoYXJ0LCBzdmcsIHRlc3REYXRhO1xuICAgICAgICB0ZXN0RGF0YSA9IFtdO1xuICAgICAgICBhbmd1bGFyLmZvckVhY2goZGF0YS5ncm91cHZlcnRleC5ncm91cG1lbWJlcnMsIGZ1bmN0aW9uKHZlcnRleCwgaSkge1xuICAgICAgICAgIHZhciB2VGltZTtcbiAgICAgICAgICB2VGltZSA9IGRhdGEudmVydGljZXRpbWVzW3ZlcnRleC52ZXJ0ZXhpZF07XG4gICAgICAgICAgcmV0dXJ
uIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgICAgbGFiZWw6IHZlcnRleC52ZXJ0ZXhpbnN0YW5jZW5hbWUgKyBcIiAoXCIgKyBpICsgXCIpXCIsXG4gICAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiU2NoZWR1bGVkXCIsXG4gICAgICAgICAgICAgICAgY29sb3I6IFwiIzY2NlwiLFxuICAgICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IHZUaW1lW1wiU0NIRURVTEVEXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIGVuZGluZ190aW1lOiB2VGltZVtcIkRFUExPWUlOR1wiXSAqIDEwMCxcbiAgICAgICAgICAgICAgICB0eXBlOiAncmVndWxhcidcbiAgICAgICAgICAgICAgfSwge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIkRlcGxveWluZ1wiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiNhYWFcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIkRFUExPWUlOR1wiXSAqIDEwMCxcbiAgICAgICAgICAgICAgICBlbmRpbmdfdGltZTogdlRpbWVbXCJSVU5OSU5HXCJdICogMTAwLFxuICAgICAgICAgICAgICAgIHR5cGU6ICdyZWd1bGFyJ1xuICAgICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IFwiUnVubmluZ1wiLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiNkZGRcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIlJVTk5JTkdcIl0gKiAxMDAsXG4gICAgICAg
ICAgICAgICAgZW5kaW5nX3RpbWU6IHZUaW1lW1wiRklOSVNIRURcIl0gKiAxMDAsXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIF1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLnRpY2tGb3JtYXQoe1xuICAgICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKSxcbiAgICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgICB9KS5wcmVmaXgoXCJzaW5nbGVcIikubGFiZWxGb3JtYXQoZnVuY3Rpb24obGFiZWwpIHtcbiAgICAgICAgICByZXR1cm4gbGFiZWw7XG4gICAgICAgIH0pLm1hcmdpbih7XG4gICAgICAgICAgbGVmdDogMTAwLFxuICAgICAgICAgIHJpZ2h0OiAwLFxuICAgICAgICAgIHRvcDogMCxcbiAgICAgICAgICBib3R0b206IDBcbiAgICAgICAgfSkuaXRlbUhlaWdodCgyNCkucmVsYXRpdmVUaW1lKCk7XG4gICAgICAgIHJldHVybiBzdmcgPSBkMy5zZWxlY3Qoc3ZnRWwpLmRhdHVtKHRlc3REYXRhKS5jYWxsKGNoYXJ0KTtcbiAgICAgIH07XG4gICAgICBhbmFseXplVGltZShzY29wZS5kYXRhKTtcbiAgICB9XG4gIH07XG59KS5kaXJlY3RpdmUoJ3RpbWVsaW5lJywgZnVuY3Rpb24oJHN0YXRlKSB7XG4gIHJldHVybiB7XG4gICAgdGVtcGxhdGU6IFwiPHN2ZyBjbGFzcz0ndGltZWxpbmUnIHdpZHRoPScwJyBoZWlnaHQ9JzAnPjwvc3ZnPlwiLFxuICAgI
HNjb3BlOiB7XG4gICAgICBqb2I6IFwiPVwiXG4gICAgfSxcbiAgICBsaW5rOiBmdW5jdGlvbihzY29wZSwgZWxlbSwgYXR0cnMpIHtcbiAgICAgIHZhciBhbmFseXplVGltZSwgY29udGFpbmVyVywgc3ZnRWwsIHRyYW5zbGF0ZUxhYmVsO1xuICAgICAgc3ZnRWwgPSBlbGVtLmNoaWxkcmVuKClbMF07XG4gICAgICBjb250YWluZXJXID0gZWxlbS53aWR0aCgpO1xuICAgICAgYW5ndWxhci5lbGVtZW50KHN2Z0VsKS5hdHRyKCd3aWR0aCcsIGNvbnRhaW5lclcpO1xuICAgICAgdHJhbnNsYXRlTGFiZWwgPSBmdW5jdGlvbihsYWJlbCkge1xuICAgICAgICByZXR1cm4gbGFiZWwucmVwbGFjZShcIiZndDtcIiwgXCI+XCIpO1xuICAgICAgfTtcbiAgICAgIGFuYWx5emVUaW1lID0gZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICB2YXIgY2hhcnQsIHN2ZywgdGVzdERhdGE7XG4gICAgICAgIHRlc3REYXRhID0gW107XG4gICAgICAgIHRlc3REYXRhLnB1c2goe1xuICAgICAgICAgIHRpbWVzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGxhYmVsOiBcIlNjaGVkdWxlZFwiLFxuICAgICAgICAgICAgICBjb2xvcjogXCIjY2NjY2NjXCIsXG4gICAgICAgICAgICAgIHN0YXJ0aW5nX3RpbWU6IGRhdGEub2xkVltcIlNDSEVEVUxFRFwiXSxcbiAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IGRhdGEub2xkVltcIlNDSEVEVUxFRFwiXSArIDMwLFxuICAgICAgICAgICAgICB0eXBlOiAnc2NoZWR1bGVkJ1xuICAgICAgIC
AgICAgfVxuICAgICAgICAgIF1cbiAgICAgICAgfSk7XG4gICAgICAgIGFuZ3VsYXIuZm9yRWFjaChkYXRhLm9sZFYuZ3JvdXB2ZXJ0aWNlcywgZnVuY3Rpb24odmVydGV4KSB7XG4gICAgICAgICAgdmFyIHZUaW1lO1xuICAgICAgICAgIHZUaW1lID0gZGF0YS5vbGRWLmdyb3VwdmVydGljZXRpbWVzW3ZlcnRleC5ncm91cHZlcnRleGlkXTtcbiAgICAgICAgICByZXR1cm4gdGVzdERhdGEucHVzaCh7XG4gICAgICAgICAgICB0aW1lczogW1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IHRyYW5zbGF0ZUxhYmVsKHZlcnRleC5ncm91cHZlcnRleG5hbWUpLFxuICAgICAgICAgICAgICAgIGNvbG9yOiBcIiM2MmNkZWFcIixcbiAgICAgICAgICAgICAgICBzdGFydGluZ190aW1lOiB2VGltZVtcIlNUQVJURURcIl0sXG4gICAgICAgICAgICAgICAgZW5kaW5nX3RpbWU6IHZUaW1lW1wiRU5ERURcIl0sXG4gICAgICAgICAgICAgICAgbGluazogdmVydGV4Lmdyb3VwdmVydGV4aWQsXG4gICAgICAgICAgICAgICAgdHlwZTogJ3JlZ3VsYXInXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIF1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNoYXJ0ID0gZDMudGltZWxpbmUoKS5zdGFjaygpLmNsaWNrKGZ1bmN0aW9uKGQsIGksIGRhdHVtKSB7XG4gICAgICAgICAgaWYgKGQubGluaykge1xuICAgICAgICAgICAgcmV0dXJuICRzdGF0ZS5nbyhcInNpbmdsZS1qb2IudGltZWx
pbmUudmVydGV4XCIsIHtcbiAgICAgICAgICAgICAgam9iaWQ6IGRhdGEuamlkLFxuICAgICAgICAgICAgICB2ZXJ0ZXhJZDogZC5saW5rXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pLnRpY2tGb3JtYXQoe1xuICAgICAgICAgIGZvcm1hdDogZDMudGltZS5mb3JtYXQoXCIlTFwiKSxcbiAgICAgICAgICB0aWNrU2l6ZTogMVxuICAgICAgICB9KS5wcmVmaXgoXCJtYWluXCIpLm1hcmdpbih7XG4gICAgICAgICAgbGVmdDogMCxcbiAgICAgICAgICByaWdodDogMCxcbiAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0pLml0ZW1IZWlnaHQoMjQpLnNob3dCb3JkZXJMaW5lKCkuc2hvd0hvdXJUaW1lbGluZSgpO1xuICAgICAgICByZXR1cm4gc3ZnID0gZDMuc2VsZWN0KHN2Z0VsKS5kYXR1bSh0ZXN0RGF0YSkuY2FsbChjaGFydCk7XG4gICAgICB9O1xuICAgICAgc2NvcGUuJHdhdGNoKGF0dHJzLmpvYiwgZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgIHJldHVybiBhbmFseXplVGltZShkYXRhKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9O1xufSkuZGlyZWN0aXZlKCdqb2JQbGFuJywgZnVuY3Rpb24oJHRpbWVvdXQpIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wbGF0ZTogXCI8c3ZnIGNsYXNzPSdncmFwaCcgd2lkdGg9JzUwMCcgaGVpZ2h0PSc0MDAnPjxnIC8+PC9zdmc+IDxzdmcgY2xhc3M9J3Rt
cCcgd2lkdGg9JzEnIGhlaWdodD0nMSc+PGcgLz48L3N2Zz4gPGRpdiBjbGFzcz0nYnRuLWdyb3VwIHpvb20tYnV0dG9ucyc+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1pbicgbmctY2xpY2s9J3pvb21JbigpJz48aSBjbGFzcz0nZmEgZmEtcGx1cycgLz48L2E+IDxhIGNsYXNzPSdidG4gYnRuLWRlZmF1bHQgem9vbS1vdXQnIG5nLWNsaWNrPSd6b29tT3V0KCknPjxpIGNsYXNzPSdmYSBmYS1taW51cycgLz48L2E+IDwvZGl2PlwiLFxuICAgIHNjb3BlOiB7XG4gICAgICBwbGFuOiAnPSdcbiAgICB9LFxuICAgIGxpbms6IGZ1bmN0aW9uKHNjb3BlLCBlbGVtLCBhdHRycykge1xuICAgICAgdmFyIGNvbnRhaW5lclcsIGNyZWF0ZUVkZ2UsIGNyZWF0ZUxhYmVsRWRnZSwgY3JlYXRlTGFiZWxOb2RlLCBjcmVhdGVOb2RlLCBkM21haW5TdmcsIGQzbWFpblN2Z0csIGQzdG1wU3ZnLCBkcmF3R3JhcGgsIGV4dGVuZExhYmVsTm9kZUZvckl0ZXJhdGlvbiwgZ2V0Tm9kZVR5cGUsIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUsIGpvYmlkLCBsb2FkSnNvblRvRGFncmUsIG1haW5HLCBtYWluU3ZnRWxlbWVudCwgbWFpblRtcEVsZW1lbnQsIG1haW5ab29tLCBzZWFyY2hGb3JOb2RlLCBzaG9ydGVuU3RyaW5nLCBzdWJncmFwaHM7XG4gICAgICBtYWluWm9vbSA9IGQzLmJlaGF2aW9yLnpvb20oKTtcbiAgICAgIHN1YmdyYXBocyA9IFtdO1xuICAgICAgam9iaWQgPSBhdHRycy5qb2JpZDtcbiAgICAgIG1haW5TdmdFbGVtZ
W50ID0gZWxlbS5jaGlsZHJlbigpWzBdO1xuICAgICAgbWFpbkcgPSBlbGVtLmNoaWxkcmVuKCkuY2hpbGRyZW4oKVswXTtcbiAgICAgIG1haW5UbXBFbGVtZW50ID0gZWxlbS5jaGlsZHJlbigpWzFdO1xuICAgICAgZDNtYWluU3ZnID0gZDMuc2VsZWN0KG1haW5TdmdFbGVtZW50KTtcbiAgICAgIGQzbWFpblN2Z0cgPSBkMy5zZWxlY3QobWFpbkcpO1xuICAgICAgZDN0bXBTdmcgPSBkMy5zZWxlY3QobWFpblRtcEVsZW1lbnQpO1xuICAgICAgY29udGFpbmVyVyA9IGVsZW0ud2lkdGgoKTtcbiAgICAgIGFuZ3VsYXIuZWxlbWVudChlbGVtLmNoaWxkcmVuKClbMF0pLndpZHRoKGNvbnRhaW5lclcpO1xuICAgICAgc2NvcGUuem9vbUluID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB0cmFuc2xhdGUsIHYxLCB2MjtcbiAgICAgICAgaWYgKG1haW5ab29tLnNjYWxlKCkgPCAyLjk5KSB7XG4gICAgICAgICAgdHJhbnNsYXRlID0gbWFpblpvb20udHJhbnNsYXRlKCk7XG4gICAgICAgICAgdjEgPSB0cmFuc2xhdGVbMF0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgdjIgPSB0cmFuc2xhdGVbMV0gKiAobWFpblpvb20uc2NhbGUoKSArIDAuMSAvIChtYWluWm9vbS5zY2FsZSgpKSk7XG4gICAgICAgICAgbWFpblpvb20uc2NhbGUobWFpblpvb20uc2NhbGUoKSArIDAuMSk7XG4gICAgICAgICAgbWFpblpvb20udHJhbnNsYXRlKFt2MSwgdjJdKTtcbiAgICAgIC
AgICByZXR1cm4gZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgdjEgKyBcIixcIiArIHYyICsgXCIpIHNjYWxlKFwiICsgbWFpblpvb20uc2NhbGUoKSArIFwiKVwiKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIHNjb3BlLnpvb21PdXQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHRyYW5zbGF0ZSwgdjEsIHYyO1xuICAgICAgICBpZiAobWFpblpvb20uc2NhbGUoKSA+IDAuMzEpIHtcbiAgICAgICAgICBtYWluWm9vbS5zY2FsZShtYWluWm9vbS5zY2FsZSgpIC0gMC4xKTtcbiAgICAgICAgICB0cmFuc2xhdGUgPSBtYWluWm9vbS50cmFuc2xhdGUoKTtcbiAgICAgICAgICB2MSA9IHRyYW5zbGF0ZVswXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICB2MiA9IHRyYW5zbGF0ZVsxXSAqIChtYWluWm9vbS5zY2FsZSgpIC0gMC4xIC8gKG1haW5ab29tLnNjYWxlKCkpKTtcbiAgICAgICAgICBtYWluWm9vbS50cmFuc2xhdGUoW3YxLCB2Ml0pO1xuICAgICAgICAgIHJldHVybiBkM21haW5TdmdHLmF0dHIoXCJ0cmFuc2Zvcm1cIiwgXCJ0cmFuc2xhdGUoXCIgKyB2MSArIFwiLFwiICsgdjIgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgY3JlYXRlTGFiZWxFZGdlID0gZnVuY3Rpb24oZWwpIHtcbiAgICAgICAgdmFyIGxhYmVsVmF
sdWU7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIlwiO1xuICAgICAgICBpZiAoKGVsLnNoaXBfc3RyYXRlZ3kgIT0gbnVsbCkgfHwgKGVsLmxvY2FsX3N0cmF0ZWd5ICE9IG51bGwpKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjxkaXYgY2xhc3M9J2VkZ2UtbGFiZWwnPlwiO1xuICAgICAgICAgIGlmIChlbC5zaGlwX3N0cmF0ZWd5ICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gZWwuc2hpcF9zdHJhdGVneTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGVsLnRlbXBfbW9kZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiIChcIiArIGVsLnRlbXBfbW9kZSArIFwiKVwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwubG9jYWxfc3RyYXRlZ3kgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIiw8YnI+XCIgKyBlbC5sb2NhbF9zdHJhdGVneTtcbiAgICAgICAgICB9XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvZGl2PlwiO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUgPSBmdW5jdGlvbihpbmZvKSB7XG4gICAgICAgIHJldHVybiBpbmZvID09PSBcInBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwibmV4dFBhcnRpYWxTb2x1dGlvblwiIHx8IGluZm8gPT09IFwid29ya3NldFwiIHx8IGluZm8g
PT09IFwibmV4dFdvcmtzZXRcIiB8fCBpbmZvID09PSBcInNvbHV0aW9uU2V0XCIgfHwgaW5mbyA9PT0gXCJzb2x1dGlvbkRlbHRhXCI7XG4gICAgICB9O1xuICAgICAgZ2V0Tm9kZVR5cGUgPSBmdW5jdGlvbihlbCwgaW5mbykge1xuICAgICAgICBpZiAoaW5mbyA9PT0gXCJtaXJyb3JcIikge1xuICAgICAgICAgIHJldHVybiAnbm9kZS1taXJyb3InO1xuICAgICAgICB9IGVsc2UgaWYgKGlzU3BlY2lhbEl0ZXJhdGlvbk5vZGUoaW5mbykpIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtaXRlcmF0aW9uJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gJ25vZGUtbm9ybWFsJztcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNyZWF0ZUxhYmVsTm9kZSA9IGZ1bmN0aW9uKGVsLCBpbmZvLCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIHZhciBsYWJlbFZhbHVlLCBzdGVwTmFtZTtcbiAgICAgICAgbGFiZWxWYWx1ZSA9IFwiPGEgaHJlZj0nIy9qb2JzL1wiICsgam9iaWQgKyBcIi92ZXJ0ZXgvXCIgKyBlbC5pZCArIFwiJyBjbGFzcz0nbm9kZS1sYWJlbCBcIiArIGdldE5vZGVUeXBlKGVsLCBpbmZvKSArIFwiJz5cIjtcbiAgICAgICAgaWYgKGluZm8gPT09IFwibWlycm9yXCIpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGgzIGNsYXNzPSdub2RlLW5hbWUnPk1pcnJvciBvZiBcIiArIGVsLm9wZXJhdG9yICsgXCI8L2gzPlwiO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxhYmVsV
mFsdWUgKz0gXCI8aDMgY2xhc3M9J25vZGUtbmFtZSc+XCIgKyBlbC5vcGVyYXRvciArIFwiPC9oMz5cIjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuZGVzY3JpcHRpb24gPT09IFwiXCIpIHtcbiAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiXCI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc3RlcE5hbWUgPSBlbC5kZXNjcmlwdGlvbjtcbiAgICAgICAgICBzdGVwTmFtZSA9IHNob3J0ZW5TdHJpbmcoc3RlcE5hbWUpO1xuICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDQgY2xhc3M9J3N0ZXAtbmFtZSc+XCIgKyBzdGVwTmFtZSArIFwiPC9oND5cIjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuc3RlcF9mdW5jdGlvbiAhPSBudWxsKSB7XG4gICAgICAgICAgbGFiZWxWYWx1ZSArPSBleHRlbmRMYWJlbE5vZGVGb3JJdGVyYXRpb24oZWwuaWQsIG1heFcsIG1heEgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmIChpc1NwZWNpYWxJdGVyYXRpb25Ob2RlKGluZm8pKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1PlwiICsgaW5mbyArIFwiIE5vZGU8L2g1PlwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZWwucGFyYWxsZWxpc20gIT09IFwiXCIpIHtcbiAgICAgICAgICAgIGxhYmVsVmFsdWUgKz0gXCI8aDU+UGFyYWxsZWxpc206IFwiICsgZWwucGFyYWxsZWxpc20gKyBcIjwvaDU+XCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5vcGVyYXRvci
AhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBsYWJlbFZhbHVlICs9IFwiPGg1Pk9wZXJhdGlvbjogXCIgKyBzaG9ydGVuU3RyaW5nKGVsLm9wZXJhdG9yX3N0cmF0ZWd5KSArIFwiPC9oNT5cIjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGFiZWxWYWx1ZSArPSBcIjwvYT5cIjtcbiAgICAgICAgcmV0dXJuIGxhYmVsVmFsdWU7XG4gICAgICB9O1xuICAgICAgZXh0ZW5kTGFiZWxOb2RlRm9ySXRlcmF0aW9uID0gZnVuY3Rpb24oaWQsIG1heFcsIG1heEgpIHtcbiAgICAgICAgdmFyIGxhYmVsVmFsdWUsIHN2Z0lEO1xuICAgICAgICBzdmdJRCA9IFwic3ZnLVwiICsgaWQ7XG4gICAgICAgIGxhYmVsVmFsdWUgPSBcIjxzdmcgY2xhc3M9J1wiICsgc3ZnSUQgKyBcIicgd2lkdGg9XCIgKyBtYXhXICsgXCIgaGVpZ2h0PVwiICsgbWF4SCArIFwiPjxnIC8+PC9zdmc+XCI7XG4gICAgICAgIHJldHVybiBsYWJlbFZhbHVlO1xuICAgICAgfTtcbiAgICAgIHNob3J0ZW5TdHJpbmcgPSBmdW5jdGlvbihzKSB7XG4gICAgICAgIHZhciBzYnI7XG4gICAgICAgIGlmIChzLmNoYXJBdCgwKSA9PT0gXCI8XCIpIHtcbiAgICAgICAgICBzID0gcy5yZXBsYWNlKFwiPFwiLCBcIiZsdDtcIik7XG4gICAgICAgICAgcyA9IHMucmVwbGFjZShcIj5cIiwgXCImZ3Q7XCIpO1xuICAgICAgICB9XG4gICAgICAgIHNiciA9IFwiXCI7XG4gICAgICAgIHdoaWxlIChzLmxlbmd0aCA+IDMwKSB7XG4gICAgICAgICAgc2JyID0
gc2JyICsgcy5zdWJzdHJpbmcoMCwgMzApICsgXCI8YnI+XCI7XG4gICAgICAgICAgcyA9IHMuc3Vic3RyaW5nKDMwLCBzLmxlbmd0aCk7XG4gICAgICAgIH1cbiAgICAgICAgc2JyID0gc2JyICsgcztcbiAgICAgICAgcmV0dXJuIHNicjtcbiAgICAgIH07XG4gICAgICBjcmVhdGVOb2RlID0gZnVuY3Rpb24oZywgZGF0YSwgZWwsIGlzUGFyZW50LCBtYXhXLCBtYXhIKSB7XG4gICAgICAgIGlmIChpc1BhcmVudCA9PSBudWxsKSB7XG4gICAgICAgICAgaXNQYXJlbnQgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZWwuaWQgPT09IGRhdGEucGFydGlhbF9zb2x1dGlvbikge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwicGFydGlhbFNvbHV0aW9uXCIsIG1heFcsIG1heEgpLFxuICAgICAgICAgICAgbGFiZWxUeXBlOiAnaHRtbCcsXG4gICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKGVsLCBcInBhcnRpYWxTb2x1dGlvblwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLm5leHRfcGFydGlhbF9zb2x1dGlvbikge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwibmV4dFBhcnRpYWxTb2x1dGlvblwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlw
ZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0UGFydGlhbFNvbHV0aW9uXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEud29ya3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwid29ya3NldFwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJ3b3Jrc2V0XCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZWwuaWQgPT09IGRhdGEubmV4dF93b3Jrc2V0KSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJuZXh0V29ya3NldFwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJuZXh0V29ya3NldFwiKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGVsLmlkID09PSBkYXRhLnNvbHV0aW9uX3NldCkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25TZXRcIiwgb
WF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwic29sdXRpb25TZXRcIilcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChlbC5pZCA9PT0gZGF0YS5zb2x1dGlvbl9kZWx0YSkge1xuICAgICAgICAgIHJldHVybiBnLnNldE5vZGUoZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbE5vZGUoZWwsIFwic29sdXRpb25EZWx0YVwiLCBtYXhXLCBtYXhIKSxcbiAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgXCJjbGFzc1wiOiBnZXROb2RlVHlwZShlbCwgXCJzb2x1dGlvbkRlbHRhXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIGcuc2V0Tm9kZShlbC5pZCwge1xuICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShlbCwgXCJcIiwgbWF4VywgbWF4SCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIFwiY2xhc3NcIjogZ2V0Tm9kZVR5cGUoZWwsIFwiXCIpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjcmVhdGVFZGdlID0gZnVuY3Rpb24oZywgZGF0YSwgZWwsIGV4aXN0aW5nTm9kZXMsIHByZWQpIHtcbiAgICAgICAgdmFyIG1pc3NpbmdOb2RlO1xuICAgICAgICBpZiAoZXhpc3RpbmdOb2Rlcy5pbmRleE9mKHByZWQuaWQpIC
E9PSAtMSkge1xuICAgICAgICAgIHJldHVybiBnLnNldEVkZ2UocHJlZC5pZCwgZWwuaWQsIHtcbiAgICAgICAgICAgIGxhYmVsOiBjcmVhdGVMYWJlbEVkZ2UocHJlZCksXG4gICAgICAgICAgICBsYWJlbFR5cGU6ICdodG1sJyxcbiAgICAgICAgICAgIGFycm93aGVhZDogJ25vcm1hbCdcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBtaXNzaW5nTm9kZSA9IHNlYXJjaEZvck5vZGUoZGF0YSwgcHJlZC5pZCk7XG4gICAgICAgICAgaWYgKCEoIW1pc3NpbmdOb2RlIHx8IG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9PT0gdHJ1ZSkpIHtcbiAgICAgICAgICAgIG1pc3NpbmdOb2RlLmFscmVhZHlBZGRlZCA9IHRydWU7XG4gICAgICAgICAgICBnLnNldE5vZGUobWlzc2luZ05vZGUuaWQsIHtcbiAgICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsTm9kZShtaXNzaW5nTm9kZSwgXCJtaXJyb3JcIiksXG4gICAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnLFxuICAgICAgICAgICAgICBcImNsYXNzXCI6IGdldE5vZGVUeXBlKG1pc3NpbmdOb2RlLCAnbWlycm9yJylcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGcuc2V0RWRnZShtaXNzaW5nTm9kZS5pZCwgZWwuaWQsIHtcbiAgICAgICAgICAgICAgbGFiZWw6IGNyZWF0ZUxhYmVsRWRnZShtaXNzaW5nTm9kZSksXG4gICAgICAgICAgICAgIGxhYmVsVHlwZTogJ2h0bWwnXG4gICAgICAgICAgICB9KTtcbiAgICA
gICAgICB9XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBsb2FkSnNvblRvRGFncmUgPSBmdW5jdGlvbihnLCBkYXRhKSB7XG4gICAgICAgIHZhciBlbCwgZXhpc3RpbmdOb2RlcywgaXNQYXJlbnQsIGssIGwsIGxlbiwgbGVuMSwgbWF4SCwgbWF4VywgcHJlZCwgciwgcmVmLCBzZywgdG9JdGVyYXRlO1xuICAgICAgICBleGlzdGluZ05vZGVzID0gW107XG4gICAgICAgIGlmIChkYXRhLm5vZGVzICE9IG51bGwpIHtcbiAgICAgICAgICB0b0l0ZXJhdGUgPSBkYXRhLm5vZGVzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRvSXRlcmF0ZSA9IGRhdGEuc3RlcF9mdW5jdGlvbjtcbiAgICAgICAgICBpc1BhcmVudCA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChrID0gMCwgbGVuID0gdG9JdGVyYXRlLmxlbmd0aDsgayA8IGxlbjsgaysrKSB7XG4gICAgICAgICAgZWwgPSB0b0l0ZXJhdGVba107XG4gICAgICAgICAgbWF4VyA9IDA7XG4gICAgICAgICAgbWF4SCA9IDA7XG4gICAgICAgICAgaWYgKGVsLnN0ZXBfZnVuY3Rpb24pIHtcbiAgICAgICAgICAgIHNnID0gbmV3IGRhZ3JlRDMuZ3JhcGhsaWIuR3JhcGgoe1xuICAgICAgICAgICAgICBtdWx0aWdyYXBoOiB0cnVlLFxuICAgICAgICAgICAgICBjb21wb3VuZDogdHJ1ZVxuICAgICAgICAgICAgfSkuc2V0R3JhcGgoe1xuICAgICAgICAgICAgICBub2Rlc2VwOiAyMCxcbiAgICAgICAgICAgICAgZWRnZXNlcDogMCxcbiAgICAgICAgICAgICAg
cmFua3NlcDogMjAsXG4gICAgICAgICAgICAgIHJhbmtkaXI6IFwiTFJcIixcbiAgICAgICAgICAgICAgbWFyZ2lueDogMTAsXG4gICAgICAgICAgICAgIG1hcmdpbnk6IDEwXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHN1YmdyYXBoc1tlbC5pZF0gPSBzZztcbiAgICAgICAgICAgIGxvYWRKc29uVG9EYWdyZShzZywgZWwpO1xuICAgICAgICAgICAgciA9IG5ldyBkYWdyZUQzLnJlbmRlcigpO1xuICAgICAgICAgICAgZDN0bXBTdmcuc2VsZWN0KCdnJykuY2FsbChyLCBzZyk7XG4gICAgICAgICAgICBtYXhXID0gc2cuZ3JhcGgoKS53aWR0aDtcbiAgICAgICAgICAgIG1heEggPSBzZy5ncmFwaCgpLmhlaWdodDtcbiAgICAgICAgICAgIGFuZ3VsYXIuZWxlbWVudChtYWluVG1wRWxlbWVudCkuZW1wdHkoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY3JlYXRlTm9kZShnLCBkYXRhLCBlbCwgaXNQYXJlbnQsIG1heFcsIG1heEgpO1xuICAgICAgICAgIGV4aXN0aW5nTm9kZXMucHVzaChlbC5pZCk7XG4gICAgICAgICAgaWYgKGVsLmlucHV0cyAhPSBudWxsKSB7XG4gICAgICAgICAgICByZWYgPSBlbC5pbnB1dHM7XG4gICAgICAgICAgICBmb3IgKGwgPSAwLCBsZW4xID0gcmVmLmxlbmd0aDsgbCA8IGxlbjE7IGwrKykge1xuICAgICAgICAgICAgICBwcmVkID0gcmVmW2xdO1xuICAgICAgICAgICAgICBjcmVhdGVFZGdlKGcsIGRhdGEsIGVsLCBleGlzdGluZ05vZGVzLCBwcmVkKTtcbiAgICAgICAgICAgI
H1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGc7XG4gICAgICB9O1xuICAgICAgc2VhcmNoRm9yTm9kZSA9IGZ1bmN0aW9uKGRhdGEsIG5vZGVJRCkge1xuICAgICAgICB2YXIgZWwsIGksIGo7XG4gICAgICAgIGZvciAoaSBpbiBkYXRhLm5vZGVzKSB7XG4gICAgICAgICAgZWwgPSBkYXRhLm5vZGVzW2ldO1xuICAgICAgICAgIGlmIChlbC5pZCA9PT0gbm9kZUlEKSB7XG4gICAgICAgICAgICByZXR1cm4gZWw7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uICE9IG51bGwpIHtcbiAgICAgICAgICAgIGZvciAoaiBpbiBlbC5zdGVwX2Z1bmN0aW9uKSB7XG4gICAgICAgICAgICAgIGlmIChlbC5zdGVwX2Z1bmN0aW9uW2pdLmlkID09PSBub2RlSUQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZWwuc3RlcF9mdW5jdGlvbltqXTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGRyYXdHcmFwaCA9IGZ1bmN0aW9uKGRhdGEpIHtcbiAgICAgICAgdmFyIGcsIGksIG5ld1NjYWxlLCByZW5kZXJlciwgc2csIHhDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXQ7XG4gICAgICAgIGcgPSBuZXcgZGFncmVEMy5ncmFwaGxpYi5HcmFwaCh7XG4gICAgICAgICAgbXVsdGlncmFwaDogdHJ1ZSxcbiAgICAgICAgICBjb21wb3VuZDogdHJ1ZVxuICAgICAgICB9KS5zZXRHcmFwaCh7XG4gIC
AgICAgICAgbm9kZXNlcDogNzAsXG4gICAgICAgICAgZWRnZXNlcDogMCxcbiAgICAgICAgICByYW5rc2VwOiA1MCxcbiAgICAgICAgICByYW5rZGlyOiBcIkxSXCIsXG4gICAgICAgICAgbWFyZ2lueDogNDAsXG4gICAgICAgICAgbWFyZ2lueTogNDBcbiAgICAgICAgfSk7XG4gICAgICAgIGxvYWRKc29uVG9EYWdyZShnLCBkYXRhKTtcbiAgICAgICAgcmVuZGVyZXIgPSBuZXcgZGFncmVEMy5yZW5kZXIoKTtcbiAgICAgICAgZDNtYWluU3ZnRy5jYWxsKHJlbmRlcmVyLCBnKTtcbiAgICAgICAgZm9yIChpIGluIHN1YmdyYXBocykge1xuICAgICAgICAgIHNnID0gc3ViZ3JhcGhzW2ldO1xuICAgICAgICAgIGQzbWFpblN2Zy5zZWxlY3QoJ3N2Zy5zdmctJyArIGkgKyAnIGcnKS5jYWxsKHJlbmRlcmVyLCBzZyk7XG4gICAgICAgIH1cbiAgICAgICAgbmV3U2NhbGUgPSAwLjU7XG4gICAgICAgIHhDZW50ZXJPZmZzZXQgPSBNYXRoLmZsb29yKChhbmd1bGFyLmVsZW1lbnQobWFpblN2Z0VsZW1lbnQpLndpZHRoKCkgLSBnLmdyYXBoKCkud2lkdGggKiBuZXdTY2FsZSkgLyAyKTtcbiAgICAgICAgeUNlbnRlck9mZnNldCA9IE1hdGguZmxvb3IoKGFuZ3VsYXIuZWxlbWVudChtYWluU3ZnRWxlbWVudCkuaGVpZ2h0KCkgLSBnLmdyYXBoKCkuaGVpZ2h0ICogbmV3U2NhbGUpIC8gMik7XG4gICAgICAgIG1haW5ab29tLnNjYWxlKG5ld1NjYWxlKS50cmFuc2xhdGUoW3hDZW50ZXJPZmZzZXQsIHlDZW50ZXJPZmZzZXRdKTtcbiA
gICAgICAgZDNtYWluU3ZnRy5hdHRyKFwidHJhbnNmb3JtXCIsIFwidHJhbnNsYXRlKFwiICsgeENlbnRlck9mZnNldCArIFwiLCBcIiArIHlDZW50ZXJPZmZzZXQgKyBcIikgc2NhbGUoXCIgKyBtYWluWm9vbS5zY2FsZSgpICsgXCIpXCIpO1xuICAgICAgICBtYWluWm9vbS5vbihcInpvb21cIiwgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgdmFyIGV2O1xuICAgICAgICAgIGV2ID0gZDMuZXZlbnQ7XG4gICAgICAgICAgcmV0dXJuIGQzbWFpblN2Z0cuYXR0cihcInRyYW5zZm9ybVwiLCBcInRyYW5zbGF0ZShcIiArIGV2LnRyYW5zbGF0ZSArIFwiKSBzY2FsZShcIiArIGV2LnNjYWxlICsgXCIpXCIpO1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIG1haW5ab29tKGQzbWFpblN2Zyk7XG4gICAgICB9O1xuICAgICAgc2NvcGUuJHdhdGNoKGF0dHJzLnBsYW4sIGZ1bmN0aW9uKG5ld1BsYW4pIHtcbiAgICAgICAgaWYgKG5ld1BsYW4pIHtcbiAgICAgICAgICByZXR1cm4gZHJhd0dyYXBoKG5ld1BsYW4pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH07XG59KTtcbiIsIiNcbiMgTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuIyBvciBtb3JlIGNvbnRyaWJ1dG9yIGxpY2Vuc2UgYWdyZWVtZW50cy4gIFNlZSB0aGUgTk9USUNFIGZpbGVcb
<TRUNCATED>
[50/51] [abbrv] flink git commit: [FLINK-2688] [monitoring api] Add
docs for monitoring REST API.
Posted by se...@apache.org.
[FLINK-2688] [monitoring api] Add docs for monitoring REST API.
Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/8db523cf
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/8db523cf
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/8db523cf
Branch: refs/heads/master
Commit: 8db523cf5e9ca2706a30b5cdfa0b677e9b6f134b
Parents: 93a4d31
Author: Stephan Ewen <se...@apache.org>
Authored: Wed Sep 16 19:23:07 2015 +0200
Committer: Stephan Ewen <se...@apache.org>
Committed: Thu Sep 17 14:21:53 2015 +0200
----------------------------------------------------------------------
docs/_includes/navbar.html | 1 +
docs/internals/monitoring_rest_api.md | 545 +++++++++++++++++++++++++++++
2 files changed, 546 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/flink/blob/8db523cf/docs/_includes/navbar.html
----------------------------------------------------------------------
diff --git a/docs/_includes/navbar.html b/docs/_includes/navbar.html
index 84cc6bc..3a8e326 100644
--- a/docs/_includes/navbar.html
+++ b/docs/_includes/navbar.html
@@ -111,6 +111,7 @@ under the License.
<li><a href="{{ internals }}/general_arch.html">Architecture & Process Model</a></li>
<li><a href="{{ internals }}/stream_checkpointing.html">Fault Tolerance for Data Streaming</a></li>
<li><a href="{{ internals }}/types_serialization.html">Type Extraction & Serialization</a></li>
+ <li><a href="{{ internals }}/monitoring_rest_api.html">Monitoring REST API</a></li>
<li><a href="{{ internals }}/job_scheduling.html">Jobs & Scheduling</a></li>
<li><a href="{{ internals }}/add_operator.html">How-To: Add an Operator</a></li>
</ul>
http://git-wip-us.apache.org/repos/asf/flink/blob/8db523cf/docs/internals/monitoring_rest_api.md
----------------------------------------------------------------------
diff --git a/docs/internals/monitoring_rest_api.md b/docs/internals/monitoring_rest_api.md
new file mode 100644
index 0000000..7946331
--- /dev/null
+++ b/docs/internals/monitoring_rest_api.md
@@ -0,0 +1,545 @@
+---
+title: "Monitoring REST API"
+---
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+Flink has a monitoring API that can be used to query status and statistics of running jobs, as well as recent completed jobs.
+This monitoring API is used by Flink's own dashboard, but is designed to be used also by custom monitoring tools.
+
+The monitoring API is a REST-ful API that accepts HTTP GET requests and responds with JSON data.
+
+* This will be replaced by the TOC
+{:toc}
+
+
+
+## Overview
+
+The monitoring API is backed by a web server that runs as part of the *JobManager*. By default, this server listens at post `8081`, which can be configured in `flink-conf.yaml` via `jobmanager.web.port`. Note that the monitoring API web server and the web dashboard web server are currently the same and thus run together at the same port. They respond to different HTTP URLs, though.
+
+In the case of multiple JobManagers (for high availability), each JobManager will run its own instance of the monitoring API, which offers information about completed and running job while that JobManager was elected the cluster leader.
+
+**NOTE:** Currently, the monitoring API is started together with the new web dashboard. To enable it, one need to add the following entry to the `flink-conf.yaml` in order to activate the new dashboard instread of the old dashboard: `jobmanager.new-web-frontend: true`
+
+
+## Developing
+
+The REST API backend is in the `flink-runtime-web` project. The core class is `org.apache.flink.runtime.webmonitor.WebRuntimeMonitor`, which sets up the server and the request routing.
+
+We use *Netty* and the *Netty Router* library to handle REST requests and translate URLs. This choice was made because this combination has lightweight dependencies, and the performance of Netty HTTP is very good.
+
+To add new requests, one needs to add a new *request handler* class. A good example to look at is the `org.apache.flink.runtime.webmonitor.handlers.JobExceptionsHandler`. After creating the handler, the handler needs to be registered with the request router in `org.apache.flink.runtime.webmonitor.WebRuntimeMonitor`.
+
+
+## Available Requests
+
+Below is a list of available requests, with a sample JSON response. All requests are of the sample form `http://hostname:8081/jobs`, below we list only the *path* part of the URLs.
+
+Values in angle brackets are variables, for example `http://hostname:8081/jobs/<jobid>/exceptions` will have to requested for example as `http://hostname:8081/jobs/7684be6004e4e955c2a558a9bc463f65/exceptions`.
+
+ - `/config`
+ - `/overview`
+ - `/jobs`
+ - `/joboverview/running`
+ - `/joboverview/completed`
+ - `/jobs/<jobid>`
+ - `/jobs/<jobid>/vertices`
+ - `/jobs/<jobid>/config`
+ - `/jobs/<jobid>/exceptions`
+ - `/jobs/<jobid>/accumulators`
+ - `/jobs/<jobid>/vertices/<vertexid>/subtasktimes`
+ - `/jobs/<jobid>/vertices/<vertexid>/accumulators`
+ - `/jobs/<jobid>/vertices/<vertexid>/subtasks/accumulators`
+ - `/jobs/<jobid>/vertices/<vertexid>/subtasks/<subtasknum>`
+ - `/jobs/<jobid>/vertices/<vertexid>/subtasks/<subtasknum>/attempts/<attempt>`
+ - `/jobs/<jobid>/vertices/<vertexid>/subtasks/<subtasknum>/attempts/<attempt>/accumulators`
+ - `/jobs/<jobid>/plan`
+
+
+### General
+
+**`/config`**
+
+Some information about the monitoring API and the server setup.
+
+Sample Result:
+
+~~~
+{
+ "refresh-interval": 3000,
+ "timezone-offset": 3600000,
+ "timezone-name": "Central European Time",
+ "flink-version": "0.10-SNAPSHOT",
+ "flink-revision": "8124545 @ 16.09.2015 @ 15:38:42 CEST"
+}
+~~~
+
+**`/overview`**
+
+Simple summary of the Flink cluster status.
+
+Sample Result:
+
+~~~
+{
+ "taskmanagers": 17,
+ "slots-total": 68,
+ "slots-available": 68,
+ "jobs-running": 0,
+ "jobs-finished": 3,
+ "jobs-cancelled": 1,
+ "jobs-failed": 0
+}
+~~~
+
+### Overview of Jobs
+
+**`/jobs`**
+
+IDs of the jobs, grouped by status *running*, *finished*, *failed*, *canceled*.
+
+Sample Result:
+
+~~~
+{
+ "jobs-running": [],
+ "jobs-finished": ["7684be6004e4e955c2a558a9bc463f65","49306f94d0920216b636e8dd503a6409"],
+ "jobs-cancelled":[],
+ "jobs-failed":[]
+}
+~~~
+
+**`/joboverview`**
+
+Jobs, groupes by status, each with a small summary of its status.
+
+Sample Result:
+
+~~~
+{
+ "running":[],
+ "finished":[
+ {
+ "jid": "7684be6004e4e955c2a558a9bc463f65",
+ "name": "Flink Java Job at Wed Sep 16 18:08:21 CEST 2015",
+ "state": "FINISHED",
+ "start-time": 1442419702857,
+ "end-time": 1442419975312,
+ "duration":272455,
+ "last-modification": 1442419975312,
+ "tasks": {
+ "total": 6,
+ "pending": 0,
+ "running": 0,
+ "finished": 6,
+ "canceling": 0,
+ "canceled": 0,
+ "failed": 0
+ }
+ },
+ {
+ "jid": "49306f94d0920216b636e8dd503a6409",
+ "name": "Flink Java Job at Wed Sep 16 18:16:39 CEST 2015",
+ ...
+ }]
+}
+~~~
+
+**`/joboverview/running`**
+
+Jobs, groupes by status, each with a small summary of its status. The same as `/joboverview`, but containing only currently running jobs.
+
+**`/joboverview/completed`**
+
+Jobs, groupes by status, each with a small summary of its status. The same as `/joboverview`, but containing only completed (finished, canceled, or failed) jobs.
+
+
+### Details of a Running or Completed Job
+
+**`/jobs/<jobid>`**
+
+Summary of one job, listing dataflow plan, status, timestamps of state transitions, aggregate information for each vertex (operator).
+
+Sample Result:
+
+~~~
+{
+ "jid": "ab78dcdbb1db025539e30217ec54ee16",
+ "name": "WordCount Example",
+ "state":"FINISHED",
+ "start-time":1442421277536,
+ "end-time":1442421299791,
+ "duration":22255,
+ "now":1442421991768,
+ "timestamps": {
+ "CREATED": 1442421277536, "RUNNING": 1442421277609, "FAILING": 0, "FAILED": 0, "CANCELLING": 0, "CANCELED": 0, "FINISHED": 1442421299791, "RESTARTING": 0
+ },
+ "vertices": [ {
+ "id": "19b5b24062c48a06e4eac65422ac3317",
+ "name": "CHAIN DataSource (at getTextDataSet(WordCount.java:142) ...",
+ "parallelism": 2,
+ "status": "FINISHED",
+ "start-time": 1442421277609,
+ "end-time": 1442421299469,
+ "duration": 21860,
+ "tasks": {
+ "CREATED": 0, "SCHEDULED": 0, "DEPLOYING": 0, "RUNNING": 0, "FINISHED": 2, "CANCELING": 0, "CANCELED": 0, "FAILED": 0
+ },
+ "metrics": {
+ "read-bytes": 0, "write-bytes": 37098, "read-records": 0, "write-records": 3312
+ }
+ }, {
+ "id": "f00c89b349b5c998cfd9fe2a06e50fd0",
+ "name":"Reduce (SUM(1), at main(WordCount.java:67)",
+ "parallelism": 2,
+ ....
+ }, {
+ "id": "0a36cbc29102d7bc993d0a9bf23afa12",
+ "name": "DataSink (CsvOutputFormat (path: /tmp/abzs, delimiter: ))",
+ ...
+ } ],
+ "status-counts": {
+ "CREATED": 0, "SCHEDULED": 0, "DEPLOYING": 0, "RUNNING": 0, "FINISHED": 3, "CANCELING": 0, "CANCELED": 0, "FAILED": 0
+ },
+ "plan": {
+ // see plan details below
+ }
+}
+~~~
+
+**`/jobs/<jobid>/vertices`**
+
+Currently the same as `/jobs/<jobid>`
+
+
+**`/jobs/<jobid>/config`**
+
+The user-defined execution config used by the job.
+
+Sample Result:
+
+~~~
+{
+ "jid": "ab78dcdbb1db025539e30217ec54ee16",
+ "name": "WordCount Example",
+ "execution-config": {
+ "execution-mode": "PIPELINED",
+ "max-execution-retries": -1,
+ "job-parallelism": -1,
+ "object-reuse-mode": false
+ }
+}
+~~~
+
+**`/jobs/<jobid>/exceptions`**
+
+The non-recoverable exceptions that have been observed by the job.
+The `truncated` flag defines whether more exceptions occurred, but are not listed, because the response would otherwise get too big.
+
+Sample Result:
+
+~~~
+{
+ "root-exception": "java.io.IOException: File already exists:/tmp/abzs/2\n\tat org.apache.flink.core.fs.local.LocalFileSystem. ...",
+ "all-exceptions": [ {
+ "exception": "java.io.IOException: File already exists:/tmp/abzs/1\n\tat org.apache.flink...",
+ "task": "DataSink (CsvOutputFormat (path: /tmp/abzs, delimiter: )) (1/2)",
+ "location": "localhost:49220"
+ }, {
+ "exception": "java.io.IOException: File already exists:/tmp/abzs/2\n\tat org.apache.flink...",
+ "task": "DataSink (CsvOutputFormat (path: /tmp/abzs, delimiter: )) (2/2)",
+ "location": "localhost:49220"
+ } ],
+ "truncated":false
+}
+~~~
+
+**`/jobs/<jobid>/accumulators`**
+
+The aggregated user accumulators plus job accumulators.
+
+Sample Result:
+
+~~~
+{
+ "job-accumulators":[],
+ "user-task-accumulators": [ {
+ "name": "avglen",
+ "type": "DoubleCounter",
+ "value": "DoubleCounter 61.5162972"
+ },
+ {
+ "name": "genwords",
+ "type": "LongCounter",
+ "value": "LongCounter 37500000"
+ } ]
+}
+~~~
+
+**`/jobs/<jobid>/vertices/<vertexid>`**
+
+Information about one specific vertex, with a summary for each of its subtasks.
+
+Sample Result:
+
+~~~
+{
+ "id": "dceafe2df1f57a1206fcb907cb38ad97",
+ "name": "CHAIN DataSource -> Map -> FlatMap -> Combine(SUM(1))",
+ "parallelism": 2,
+ "now": 1442424002154,
+ "subtasks": [ {
+ "subtask":0,
+ "status": "FINISHED",
+ "attempt": 0,
+ "host": "localhost",
+ "start-time": 1442421093762,
+ "end-time": 1442421386680,
+ "duration": 292918,
+ "metrics": {
+ "read-bytes": 0, "write-bytes": 12684375, "read-records": 0, "write-records": 1153125
+ }
+ }, {
+ "subtask": 1,
+ "status": "FINISHED",
+ "attempt": 0,
+ "host": "localhost",
+ "start-time": 1442421093774,
+ "end-time": 1442421386267,
+ "duration": 292493,
+ "metrics": {
+ "read-bytes": 0, "write-bytes": 12684375, "read-records": 0, "write-records": 1153125
+ }
+ } ]
+}
+~~~
+
+**`/jobs/<jobid>/vertices/<vertexid>/subtasktimes`**
+
+This request returns the timestamps for the state transitions of all subtasks of a given vertex.
+These can be used, for example, to create time-line comparisons between subtasks.
+
+Sample Result:
+
+~~~
+{
+ "id": "dceafe2df1f57a1206fcb907cb38ad97",
+ "name": "CHAIN DataSource -> Map -> Combine(SUM(1))",
+ "now":1442423745088,
+ "subtasks": [ {
+ "subtask": 0,
+ "host": "localhost",
+ "duration": 292924,
+ "timestamps": {
+ "CREATED": 1442421093741, "SCHEDULED": 1442421093756, "DEPLOYING": 1442421093762, "RUNNING": 1442421094026, "FINISHED": 1442421386680, "CANCELING": 0, "CANCELED": 0, "FAILED": 0
+ }
+ }, {
+ "subtask": 1,
+ "host": "localhost",
+ "duration": 292494,
+ "timestamps": {
+ "CREATED": 1442421093741, "SCHEDULED": 1442421093773, "DEPLOYING": 1442421093774, "RUNNING": 1442421094013, "FINISHED": 1442421386267, "CANCELING": 0, "CANCELED": 0, "FAILED": 0
+ }
+ } ]
+}
+~~~
+
+**`/jobs/<jobid>/vertices/<vertexid>/accumulators`**
+
+The aggregated user-defined accumulators, for a specific vertex.
+
+Sample Result:
+
+~~~
+{
+ "id": "dceafe2df1f57a1206fcb907cb38ad97",
+ "user-accumulators": [ {
+ "name": "avglen", "type": "DoubleCounter", "value": "DoubleCounter 123.03259440000001"
+ }, {
+ "name": "genwords", "type": "LongCounter", "value": "LongCounter 75000000"
+ } ]
+}
+~~~
+
+**`/jobs/<jobid>/vertices/<vertexid>/subtasks/accumulators`**
+
+Gets all user-defined accumulators for all subtasks of a given vertex. These are the individual accumulators that are returned in aggregated form by the
+request `/jobs/<jobid>/vertices/<vertexid>/accumulators`.
+
+Sample Result:
+
+~~~
+{
+ "id": "dceafe2df1f57a1206fcb907cb38ad97",
+ "parallelism": 2,
+ "subtasks": [ {
+ "subtask": 0,
+ "attempt": 0,
+ "host": "localhost",
+ "user-accumulators": [ {
+ "name": "genwords", "type": "LongCounter", "value": "LongCounter 62500000"
+ }, {
+ "name": "genletters", "type": "LongCounter", "value": "LongCounter 1281589525"
+ } ]
+ }, {
+ "subtask": 1,
+ "attempt": 0,
+ "host": "localhost",
+ "user-accumulators": [ {
+ "name": "genwords", "type": "LongCounter", "value": "LongCounter 12500000"
+ }, {
+ "name": "genletters", "type": "LongCounter", "value": "LongCounter 256317905"
+ } ]
+ } ]
+}
+~~~
+
+**`/jobs/<jobid>/vertices/<vertexid>/subtasks/<subtasknum>`**
+
+Summary of the current or latest execution attempt of a specific subtask. See below for a sample.
+
+
+**`/jobs/<jobid>/vertices/<vertexid>/subtasks/<subtasknum>/attempts/<attempt>`**
+
+Summary of a specific execution attempt of a specific subtask. Multiple execution attempts happen in case of failure/recovery.
+
+Sample Result:
+
+~~~
+{
+ "subtask": 0,
+ "status": "FINISHED",
+ "attempt": 0,
+ "host": "localhost",
+ "start-time": 1442421093762,
+ "end-time": 1442421386680,
+ "duration": 292918,
+ "metrics": {
+ "read-bytes": 0, "write-bytes": 12684375, "read-records": 0, "write-records": 1153125
+ }
+}
+~~~
+
+**`/jobs/<jobid>/vertices/<vertexid>/subtasks/<subtasknum>/attempts/<attempt>/accumulators`**
+
+The accumulators collected for one specific subtask during one specific execution attempt (multiple attempts happen in case of failure/recovery).
+
+Sample Result:
+
+~~~
+{
+ "subtask": 0,
+ "attempt": 0,
+ "id": "b22f94d91bf41ddb",
+ "user-accumulators": [ {
+ "name": "genwords", "type":"LongCounter", "value":"LongCounter 62500000"
+ }, {
+ "name": "genletters", "type": "LongCounter", "value": "LongCounter 1281589525"
+ }, {
+ "name": "avglen", "type": "DoubleCounter", "value": "DoubleCounter 102.527162"
+ } ]
+}
+~~~
+
+**`/jobs/<jobid>/plan`**
+
+The dataflow plan of a job. The plan is also included in the job summary (`/jobs/<jobid>`).
+
+Sample Result:
+
+~~~
+{
+ "jid":"ab78dcdbb1db025539e30217ec54ee16",
+ "name":"WordCount Example",
+ "nodes": [ {
+ "id": "f00c89b349b5c998cfd9fe2a06e50fd0",
+ "parallelism": 2,
+ "operator": "GroupReduce",
+ "operator_strategy": "Sorted Group Reduce",
+ "description": "Reduce (SUM(1), at main(WordCount.java:67)",
+ "inputs": [ {
+ "num": 0,
+ "id":"19b5b24062c48a06e4eac65422ac3317",
+ "ship_strategy": "Hash Partition on [0]",
+ "local_strategy":"Sort (combining) on [0:ASC]",
+ "exchange":"pipelined"
+ } ],
+ "optimizer_properties": {
+ "global_properties": [
+ { "name":"Partitioning", "value":"HASH_PARTITIONED" },
+ { "name":"Partitioned on", "value":"[0]" },
+ { "name":"Partitioning Order", "value":"(none)" },
+ { "name":"Uniqueness", "value":"not unique" }
+ ],
+ "local_properties": [
+ { "name":"Order", "value":"[0:ASC]" },
+ { "name":"Grouped on", "value":"[0]" },
+ { "name":"Uniqueness", "value":"not unique" }
+ ],
+ "estimates": [
+ { "name":"Est. Output Size", "value":"(unknown)" },
+ { "name":"Est. Cardinality", "value":"(unknown)" }
+ ],
+ "costs": [
+ { "name":"Network", "value":"(unknown)" },
+ { "name":"Disk I/O", "value":"(unknown)" },
+ { "name":"CPU", "value":"(unknown)" },
+ { "name":"Cumulative Network", "value":"(unknown)" },
+ { "name":"Cumulative Disk I/O", "value":"(unknown)" },
+ { "name":"Cumulative CPU","value":"(unknown)" }
+ ],
+ "compiler_hints": [
+ { "name":"Output Size (bytes)", "value":"(none)" },
+ { "name":"Output Cardinality", "value":"(none)" },
+ { "name":"Avg. Output Record Size (bytes)", "value":"(none)" },
+ { "name":"Filter Factor", "value":"(none)" }
+ ]
+ }
+ },
+ {
+ "id": "19b5b24062c48a06e4eac65422ac3317",
+ "parallelism": 2,
+ "operator": "Data Source -> FlatMap -> GroupCombine",
+ "operator_strategy":" (none) -> FlatMap -> Sorted Combine",
+ "description":"DataSource (at getTextDataSet(WordCount.java:142) (org.apache.flink.api.java.io.TextInputFormat)) -> FlatMap (FlatMap at main(WordCount.java:67)) -> Combine(SUM(1), at main(WordCount.java:67)",
+ "optimizer_properties": {
+ ...
+ }
+ },
+ {
+ "id": "0a36cbc29102d7bc993d0a9bf23afa12",
+ "parallelism": 2,
+ "operator": "Data Sink",
+ "operator_strategy": "(none)",
+ "description": "DataSink (CsvOutputFormat (path: /tmp/abzs, delimiter: ))",
+ "inputs":[ {
+ "num": 0,
+ "id": "f00c89b349b5c998cfd9fe2a06e50fd0",
+ "ship_strategy": "Forward",
+ "exchange": "pipelined"
+ } ],
+ "optimizer_properties": {
+ ...
+ }
+ } ]
+}
+~~~
+
+