/**
|
* @license
|
* Copyright 2018 Google LLC. All Rights Reserved.
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
* =============================================================================
|
*/
|
import { dispose, io, Tensor, util } from '@tensorflow/tfjs-core';
|
import { OperationMapper } from '../operations/operation_mapper';
|
import { GraphExecutor } from './graph_executor';
|
import { ResourceManager } from './resource_manager';
|
// tslint:disable-next-line: no-imports-from-dist
|
import { decodeWeightsStream } from '@tensorflow/tfjs-core/dist/io/io_utils';
|
export const TFHUB_SEARCH_PARAM = '?tfjs-format=file';
|
export const DEFAULT_MODEL_NAME = 'model.json';
|
/**
|
* A `tf.GraphModel` is a directed, acyclic graph built from a
|
* SavedModel GraphDef and allows inference execution.
|
*
|
* A `tf.GraphModel` can only be created by loading from a model converted from
|
* a [TensorFlow SavedModel](https://www.tensorflow.org/guide/saved_model) using
|
* the command line converter tool and loaded via `tf.loadGraphModel`.
|
*
|
* @doc {heading: 'Models', subheading: 'Classes'}
|
*/
|
export class GraphModel {
|
// Returns the version information for the tensorflow model GraphDef.
|
get modelVersion() {
|
return this.version;
|
}
|
get inputNodes() {
|
return this.executor.inputNodes;
|
}
|
get outputNodes() {
|
return this.executor.outputNodes;
|
}
|
get inputs() {
|
return this.executor.inputs;
|
}
|
get outputs() {
|
return this.executor.outputs;
|
}
|
get weights() {
|
return this.executor.weightMap;
|
}
|
get metadata() {
|
return this.artifacts.userDefinedMetadata;
|
}
|
get modelSignature() {
|
return this.signature;
|
}
|
get modelStructuredOutputKeys() {
|
return this.structuredOutputKeys;
|
}
|
/**
|
* @param modelUrl url for the model, or an `io.IOHandler`.
|
* @param weightManifestUrl url for the weight file generated by
|
* scripts/convert.py script.
|
* @param requestOption options for Request, which allows to send credentials
|
* and custom headers.
|
* @param onProgress Optional, progress callback function, fired periodically
|
* before the load is completed.
|
*/
|
constructor(modelUrl, loadOptions = {}, tfio = io) {
|
this.modelUrl = modelUrl;
|
this.loadOptions = loadOptions;
|
this.version = 'n/a';
|
this.io = tfio;
|
if (loadOptions == null) {
|
this.loadOptions = {};
|
}
|
this.resourceManager = new ResourceManager();
|
}
|
findIOHandler() {
|
const path = this.modelUrl;
|
if (path.load != null) {
|
// Path is an IO Handler.
|
this.handler = path;
|
}
|
else if (this.loadOptions.requestInit != null) {
|
this.handler = this.io.browserHTTPRequest(path, this.loadOptions);
|
}
|
else {
|
const handlers = this.io.getLoadHandlers(path, this.loadOptions);
|
if (handlers.length === 0) {
|
// For backward compatibility: if no load handler can be found,
|
// assume it is a relative http path.
|
handlers.push(this.io.browserHTTPRequest(path, this.loadOptions));
|
}
|
else if (handlers.length > 1) {
|
throw new Error(`Found more than one (${handlers.length}) load handlers for ` +
|
`URL '${[path]}'`);
|
}
|
this.handler = handlers[0];
|
}
|
}
|
/**
|
* Loads the model and weight files, construct the in memory weight map and
|
* compile the inference graph.
|
*/
|
load() {
|
this.findIOHandler();
|
if (this.handler.load == null) {
|
throw new Error('Cannot proceed with model loading because the IOHandler provided ' +
|
'does not have the `load` method implemented.');
|
}
|
const loadResult = this.handler.load();
|
if (util.isPromise(loadResult)) {
|
return loadResult.then(artifacts => {
|
if (artifacts.getWeightStream == null) {
|
return this.loadSync(artifacts);
|
}
|
return this.loadStreaming(artifacts);
|
});
|
}
|
return this.loadSync(loadResult);
|
}
|
/**
|
* Synchronously construct the in memory weight map and
|
* compile the inference graph.
|
*
|
* @doc {heading: 'Models', subheading: 'Classes', ignoreCI: true}
|
*/
|
loadSync(artifacts) {
|
const weightMap = this.io.decodeWeights(artifacts.weightData, artifacts.weightSpecs);
|
return this.loadWithWeightMap(artifacts, weightMap);
|
}
|
async loadStreaming(artifacts) {
|
if (artifacts.getWeightStream == null) {
|
throw new Error('Model artifacts missing streamWeights function');
|
}
|
const weightMap = await decodeWeightsStream(artifacts.getWeightStream(), artifacts.weightSpecs);
|
return this.loadWithWeightMap(artifacts, weightMap);
|
}
|
loadWithWeightMap(artifacts, weightMap) {
|
this.artifacts = artifacts;
|
const graph = this.artifacts.modelTopology;
|
let signature = this.artifacts.signature;
|
if (this.artifacts.userDefinedMetadata != null) {
|
const metadata = this.artifacts.userDefinedMetadata;
|
if (metadata.signature != null) {
|
signature = metadata.signature;
|
}
|
if (metadata.structuredOutputKeys != null) {
|
this.structuredOutputKeys = metadata.structuredOutputKeys;
|
}
|
}
|
this.signature = signature;
|
this.version = `${graph.versions.producer}.${graph.versions.minConsumer}`;
|
this.executor = new GraphExecutor(OperationMapper.Instance.transformGraph(graph, this.signature));
|
this.executor.weightMap = this.convertTensorMapToTensorsMap(weightMap);
|
// Attach a model-level resourceManager to each executor to share resources,
|
// such as `HashTable`.
|
this.executor.resourceManager = this.resourceManager;
|
if (artifacts.modelInitializer != null &&
|
artifacts.modelInitializer.node != null) {
|
const initializer = OperationMapper.Instance.transformGraph(artifacts.modelInitializer);
|
this.initializer = new GraphExecutor(initializer);
|
this.initializer.weightMap = this.executor.weightMap;
|
// Attach a model-level resourceManager to the initializer, the
|
// hashTables created from when executing the initializer will be stored
|
// in the resourceManager.
|
this.initializer.resourceManager = this.resourceManager;
|
this.initializerSignature = artifacts.initializerSignature;
|
}
|
return true;
|
}
|
/**
|
* Save the configuration and/or weights of the GraphModel.
|
*
|
* An `IOHandler` is an object that has a `save` method of the proper
|
* signature defined. The `save` method manages the storing or
|
* transmission of serialized data ("artifacts") that represent the
|
* model's topology and weights onto or via a specific medium, such as
|
* file downloads, local storage, IndexedDB in the web browser and HTTP
|
* requests to a server. TensorFlow.js provides `IOHandler`
|
* implementations for a number of frequently used saving mediums, such as
|
* `tf.io.browserDownloads` and `tf.io.browserLocalStorage`. See `tf.io`
|
* for more details.
|
*
|
* This method also allows you to refer to certain types of `IOHandler`s
|
* as URL-like string shortcuts, such as 'localstorage://' and
|
* 'indexeddb://'.
|
*
|
* Example 1: Save `model`'s topology and weights to browser [local
|
* storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage);
|
* then load it back.
|
*
|
* ```js
|
* const modelUrl =
|
* 'https://storage.googleapis.com/tfjs-models/savedmodel/mobilenet_v2_1.0_224/model.json';
|
* const model = await tf.loadGraphModel(modelUrl);
|
* const zeros = tf.zeros([1, 224, 224, 3]);
|
* model.predict(zeros).print();
|
*
|
* const saveResults = await model.save('localstorage://my-model-1');
|
*
|
* const loadedModel = await tf.loadGraphModel('localstorage://my-model-1');
|
* console.log('Prediction from loaded model:');
|
* model.predict(zeros).print();
|
* ```
|
*
|
* @param handlerOrURL An instance of `IOHandler` or a URL-like,
|
* scheme-based string shortcut for `IOHandler`.
|
* @param config Options for saving the model.
|
* @returns A `Promise` of `SaveResult`, which summarizes the result of
|
* the saving, such as byte sizes of the saved artifacts for the model's
|
* topology and weight values.
|
*
|
* @doc {heading: 'Models', subheading: 'Classes', ignoreCI: true}
|
*/
|
async save(handlerOrURL, config) {
|
if (typeof handlerOrURL === 'string') {
|
const handlers = this.io.getSaveHandlers(handlerOrURL);
|
if (handlers.length === 0) {
|
throw new Error(`Cannot find any save handlers for URL '${handlerOrURL}'`);
|
}
|
else if (handlers.length > 1) {
|
throw new Error(`Found more than one (${handlers.length}) save handlers for ` +
|
`URL '${handlerOrURL}'`);
|
}
|
handlerOrURL = handlers[0];
|
}
|
if (handlerOrURL.save == null) {
|
throw new Error('GraphModel.save() cannot proceed because the IOHandler ' +
|
'provided does not have the `save` attribute defined.');
|
}
|
return handlerOrURL.save(this.artifacts);
|
}
|
addStructuredOutputNames(outputTensors) {
|
if (this.structuredOutputKeys) {
|
const outputTensorsArray = outputTensors instanceof Tensor ? [outputTensors] : outputTensors;
|
const outputTensorMap = {};
|
outputTensorsArray.forEach((outputTensor, i) => outputTensorMap[this.structuredOutputKeys[i]] =
|
outputTensor);
|
return outputTensorMap;
|
}
|
return outputTensors;
|
}
|
/**
|
* Execute the inference for the input tensors.
|
*
|
* @param input The input tensors, when there is single input for the model,
|
* inputs param should be a `tf.Tensor`. For models with mutliple inputs,
|
* inputs params should be in either `tf.Tensor`[] if the input order is
|
* fixed, or otherwise NamedTensorMap format.
|
*
|
* For model with multiple inputs, we recommend you use NamedTensorMap as the
|
* input type, if you use `tf.Tensor`[], the order of the array needs to
|
* follow the
|
* order of inputNodes array. @see {@link GraphModel.inputNodes}
|
*
|
* You can also feed any intermediate nodes using the NamedTensorMap as the
|
* input type. For example, given the graph
|
* InputNode => Intermediate => OutputNode,
|
* you can execute the subgraph Intermediate => OutputNode by calling
|
* model.execute('IntermediateNode' : tf.tensor(...));
|
*
|
* This is useful for models that uses tf.dynamic_rnn, where the intermediate
|
* state needs to be fed manually.
|
*
|
* For batch inference execution, the tensors for each input need to be
|
* concatenated together. For example with mobilenet, the required input shape
|
* is [1, 244, 244, 3], which represents the [batch, height, width, channel].
|
* If we are provide a batched data of 100 images, the input tensor should be
|
* in the shape of [100, 244, 244, 3].
|
*
|
* @param config Prediction configuration for specifying the batch size.
|
* Currently the batch size option is ignored for graph model.
|
*
|
* @returns Inference result tensors. If the model is converted and it
|
* originally had structured_outputs in tensorflow, then a NamedTensorMap
|
* will be returned matching the structured_outputs. If no structured_outputs
|
* are present, the output will be single `tf.Tensor` if the model has single
|
* output node, otherwise Tensor[].
|
*
|
* @doc {heading: 'Models', subheading: 'Classes'}
|
*/
|
predict(inputs, config) {
|
const outputTensors = this.execute(inputs, this.outputNodes);
|
return this.addStructuredOutputNames(outputTensors);
|
}
|
/**
|
* Execute the inference for the input tensors in async fashion, use this
|
* method when your model contains control flow ops.
|
*
|
* @param input The input tensors, when there is single input for the model,
|
* inputs param should be a `tf.Tensor`. For models with mutliple inputs,
|
* inputs params should be in either `tf.Tensor`[] if the input order is
|
* fixed, or otherwise NamedTensorMap format.
|
*
|
* For model with multiple inputs, we recommend you use NamedTensorMap as the
|
* input type, if you use `tf.Tensor`[], the order of the array needs to
|
* follow the
|
* order of inputNodes array. @see {@link GraphModel.inputNodes}
|
*
|
* You can also feed any intermediate nodes using the NamedTensorMap as the
|
* input type. For example, given the graph
|
* InputNode => Intermediate => OutputNode,
|
* you can execute the subgraph Intermediate => OutputNode by calling
|
* model.execute('IntermediateNode' : tf.tensor(...));
|
*
|
* This is useful for models that uses tf.dynamic_rnn, where the intermediate
|
* state needs to be fed manually.
|
*
|
* For batch inference execution, the tensors for each input need to be
|
* concatenated together. For example with mobilenet, the required input shape
|
* is [1, 244, 244, 3], which represents the [batch, height, width, channel].
|
* If we are provide a batched data of 100 images, the input tensor should be
|
* in the shape of [100, 244, 244, 3].
|
*
|
* @param config Prediction configuration for specifying the batch size.
|
* Currently the batch size option is ignored for graph model.
|
*
|
* @returns A Promise of inference result tensors. If the model is converted
|
* and it originally had structured_outputs in tensorflow, then a
|
* NamedTensorMap will be returned matching the structured_outputs. If no
|
* structured_outputs are present, the output will be single `tf.Tensor` if
|
* the model has single output node, otherwise Tensor[].
|
*
|
* @doc {heading: 'Models', subheading: 'Classes'}
|
*/
|
async predictAsync(inputs, config) {
|
const outputTensors = await this.executeAsync(inputs, this.outputNodes);
|
return this.addStructuredOutputNames(outputTensors);
|
}
|
normalizeInputs(inputs) {
|
var _a;
|
if (!(inputs instanceof Tensor) && !Array.isArray(inputs)) {
|
// The input is already a NamedTensorMap.
|
const signatureInputs = (_a = this.signature) === null || _a === void 0 ? void 0 : _a.inputs;
|
if (signatureInputs != null) {
|
for (const input in signatureInputs) {
|
const tensor = signatureInputs[input];
|
if (tensor.resourceId != null) {
|
inputs[input] = this.resourceIdToCapturedInput[tensor.resourceId];
|
}
|
}
|
}
|
return inputs;
|
}
|
inputs = Array.isArray(inputs) ? inputs : [inputs];
|
const numCapturedInputs = Object.keys(this.resourceIdToCapturedInput).length;
|
if (inputs.length + numCapturedInputs !== this.inputNodes.length) {
|
throw new Error(`Input tensor count mismatch, the graph model has ${this.inputNodes.length -
|
numCapturedInputs} non-resource placeholders, while there are ${inputs.length} input tensors provided.`);
|
}
|
let inputIndex = 0;
|
return this.inputNodes.reduce((map, inputName) => {
|
var _a, _b, _c;
|
const resourceId = (_c = (_b = (_a = this.signature) === null || _a === void 0 ? void 0 : _a.inputs) === null || _b === void 0 ? void 0 : _b[inputName]) === null || _c === void 0 ? void 0 : _c.resourceId;
|
if (resourceId != null) {
|
map[inputName] = this.resourceIdToCapturedInput[resourceId];
|
}
|
else {
|
map[inputName] = inputs[inputIndex++];
|
}
|
return map;
|
}, {});
|
}
|
normalizeOutputs(outputs) {
|
outputs = outputs || this.outputNodes;
|
return !Array.isArray(outputs) ? [outputs] : outputs;
|
}
|
executeInitializerGraph() {
|
if (this.initializer == null) {
|
return [];
|
}
|
if (this.initializerSignature == null) {
|
return this.initializer.execute({}, []);
|
}
|
else {
|
return this.initializer.execute({}, Object.keys(this.initializerSignature.outputs));
|
}
|
}
|
async executeInitializerGraphAsync() {
|
if (this.initializer == null) {
|
return [];
|
}
|
if (this.initializerSignature == null) {
|
return this.initializer.executeAsync({}, []);
|
}
|
else {
|
return this.initializer.executeAsync({}, Object.keys(this.initializerSignature.outputs));
|
}
|
}
|
setResourceIdToCapturedInput(outputs) {
|
this.resourceIdToCapturedInput = {};
|
if (this.initializerSignature) {
|
const signatureOutputs = this.initializerSignature.outputs;
|
const outputNames = Object.keys(signatureOutputs);
|
for (let i = 0; i < outputNames.length; i++) {
|
const outputName = outputNames[i];
|
const tensorInfo = signatureOutputs[outputName];
|
this.resourceIdToCapturedInput[tensorInfo.resourceId] = outputs[i];
|
}
|
}
|
}
|
/**
|
* Executes inference for the model for given input tensors.
|
* @param inputs tensor, tensor array or tensor map of the inputs for the
|
* model, keyed by the input node names.
|
* @param outputs output node name from the TensorFlow model, if no
|
* outputs are specified, the default outputs of the model would be used.
|
* You can inspect intermediate nodes of the model by adding them to the
|
* outputs array.
|
*
|
* @returns A single tensor if provided with a single output or no outputs
|
* are provided and there is only one default output, otherwise return a
|
* tensor array. The order of the tensor array is the same as the outputs
|
* if provided, otherwise the order of outputNodes attribute of the model.
|
*
|
* @doc {heading: 'Models', subheading: 'Classes'}
|
*/
|
execute(inputs, outputs) {
|
if (this.resourceIdToCapturedInput == null) {
|
this.setResourceIdToCapturedInput(this.executeInitializerGraph());
|
}
|
inputs = this.normalizeInputs(inputs);
|
outputs = this.normalizeOutputs(outputs);
|
const result = this.executor.execute(inputs, outputs);
|
return result.length > 1 ? result : result[0];
|
}
|
/**
|
* Executes inference for the model for given input tensors in async
|
* fashion, use this method when your model contains control flow ops.
|
* @param inputs tensor, tensor array or tensor map of the inputs for the
|
* model, keyed by the input node names.
|
* @param outputs output node name from the TensorFlow model, if no outputs
|
* are specified, the default outputs of the model would be used. You can
|
* inspect intermediate nodes of the model by adding them to the outputs
|
* array.
|
*
|
* @returns A Promise of single tensor if provided with a single output or
|
* no outputs are provided and there is only one default output, otherwise
|
* return a tensor map.
|
*
|
* @doc {heading: 'Models', subheading: 'Classes'}
|
*/
|
async executeAsync(inputs, outputs) {
|
if (this.resourceIdToCapturedInput == null) {
|
this.setResourceIdToCapturedInput(await this.executeInitializerGraphAsync());
|
}
|
inputs = this.normalizeInputs(inputs);
|
outputs = this.normalizeOutputs(outputs);
|
const result = await this.executor.executeAsync(inputs, outputs);
|
return result.length > 1 ? result : result[0];
|
}
|
/**
|
* Get intermediate tensors for model debugging mode (flag
|
* KEEP_INTERMEDIATE_TENSORS is true).
|
*
|
* @doc {heading: 'Models', subheading: 'Classes'}
|
*/
|
getIntermediateTensors() {
|
return this.executor.getIntermediateTensors();
|
}
|
/**
|
* Dispose intermediate tensors for model debugging mode (flag
|
* KEEP_INTERMEDIATE_TENSORS is true).
|
*
|
* @doc {heading: 'Models', subheading: 'Classes'}
|
*/
|
disposeIntermediateTensors() {
|
this.executor.disposeIntermediateTensors();
|
}
|
convertTensorMapToTensorsMap(map) {
|
return Object.keys(map).reduce((newMap, key) => {
|
newMap[key] = [map[key]];
|
return newMap;
|
}, {});
|
}
|
/**
|
* Releases the memory used by the weight tensors and resourceManager.
|
*
|
* @doc {heading: 'Models', subheading: 'Classes'}
|
*/
|
dispose() {
|
this.executor.dispose();
|
if (this.initializer) {
|
this.initializer.dispose();
|
if (this.resourceIdToCapturedInput) {
|
dispose(this.resourceIdToCapturedInput);
|
}
|
}
|
this.resourceManager.dispose();
|
}
|
}
|
/**
|
* Load a graph model given a URL to the model definition.
|
*
|
* Example of loading MobileNetV2 from a URL and making a prediction with a
|
* zeros input:
|
*
|
* ```js
|
* const modelUrl =
|
* 'https://storage.googleapis.com/tfjs-models/savedmodel/mobilenet_v2_1.0_224/model.json';
|
* const model = await tf.loadGraphModel(modelUrl);
|
* const zeros = tf.zeros([1, 224, 224, 3]);
|
* model.predict(zeros).print();
|
* ```
|
*
|
* Example of loading MobileNetV2 from a TF Hub URL and making a prediction
|
* with a zeros input:
|
*
|
* ```js
|
* const modelUrl =
|
* 'https://tfhub.dev/google/imagenet/mobilenet_v2_140_224/classification/2';
|
* const model = await tf.loadGraphModel(modelUrl, {fromTFHub: true});
|
* const zeros = tf.zeros([1, 224, 224, 3]);
|
* model.predict(zeros).print();
|
* ```
|
* @param modelUrl The url or an `io.IOHandler` that loads the model.
|
* @param options Options for the HTTP request, which allows to send
|
* credentials
|
* and custom headers.
|
*
|
* @doc {heading: 'Models', subheading: 'Loading'}
|
*/
|
export async function loadGraphModel(modelUrl, options = {}, tfio = io) {
|
if (modelUrl == null) {
|
throw new Error('modelUrl in loadGraphModel() cannot be null. Please provide a url ' +
|
'or an IOHandler that loads the model');
|
}
|
if (options == null) {
|
options = {};
|
}
|
if (options.fromTFHub && typeof modelUrl === 'string') {
|
modelUrl = getTFHubUrl(modelUrl);
|
}
|
const model = new GraphModel(modelUrl, options, tfio);
|
await model.load();
|
return model;
|
}
|
/**
|
* Load a graph model given a synchronous IO handler with a 'load' method.
|
*
|
* @param modelSource The `io.IOHandlerSync` that loads the model, or the
|
* `io.ModelArtifacts` that encode the model, or a tuple of
|
* `[io.ModelJSON, ArrayBuffer]` of which the first element encodes the
|
* model and the second contains the weights.
|
*
|
* @doc {heading: 'Models', subheading: 'Loading'}
|
*/
|
export function loadGraphModelSync(modelSource) {
|
if (modelSource == null) {
|
throw new Error('modelUrl in loadGraphModelSync() cannot be null. Please provide ' +
|
'model artifacts or an IOHandler that loads the model');
|
}
|
let ioHandler;
|
if (modelSource instanceof Array) {
|
const [modelJSON, weights] = modelSource;
|
if (!modelJSON) {
|
throw new Error('modelJSON must be the first element of the array');
|
}
|
if (!weights || !(weights instanceof ArrayBuffer)) {
|
throw new Error('An ArrayBuffer of weights must be the second element of' +
|
' the array');
|
}
|
if (!('modelTopology' in modelJSON)) {
|
throw new Error('Model JSON is missing \'modelTopology\'');
|
}
|
if (!('weightsManifest' in modelJSON)) {
|
throw new Error('Model JSON is missing \'weightsManifest\'');
|
}
|
const weightSpecs = io.getWeightSpecs(modelJSON.weightsManifest);
|
const modelArtifacts = io.getModelArtifactsForJSONSync(modelJSON, weightSpecs, weights);
|
ioHandler = io.fromMemorySync(modelArtifacts);
|
}
|
else if ('load' in modelSource) {
|
// Then modelSource is already an IOHandlerSync.
|
ioHandler = modelSource;
|
}
|
else if ('modelTopology' in modelSource && 'weightSpecs' in modelSource &&
|
'weightData' in modelSource) {
|
// modelSource is of type ModelArtifacts.
|
ioHandler = io.fromMemorySync(modelSource);
|
}
|
else {
|
throw new Error('Unknown model format');
|
}
|
const model = new GraphModel(ioHandler);
|
model.load();
|
return model;
|
}
|
function getTFHubUrl(modelUrl) {
|
if (!modelUrl.endsWith('/')) {
|
modelUrl = (modelUrl) + '/';
|
}
|
return `${modelUrl}${DEFAULT_MODEL_NAME}${TFHUB_SEARCH_PARAM}`;
|
}
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JhcGhfbW9kZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi90ZmpzLWNvbnZlcnRlci9zcmMvZXhlY3V0b3IvZ3JhcGhfbW9kZWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxFQUFDLE9BQU8sRUFBa0IsRUFBRSxFQUFzQyxNQUFNLEVBQUUsSUFBSSxFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFJcEgsT0FBTyxFQUFDLGVBQWUsRUFBQyxNQUFNLGdDQUFnQyxDQUFDO0FBRS9ELE9BQU8sRUFBQyxhQUFhLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQztBQUMvQyxPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0sb0JBQW9CLENBQUM7QUFDbkQsaURBQWlEO0FBQ2pELE9BQU8sRUFBQyxtQkFBbUIsRUFBQyxNQUFNLHdDQUF3QyxDQUFDO0FBRTNFLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLG1CQUFtQixDQUFDO0FBQ3RELE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLFlBQVksQ0FBQztBQUkvQzs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQWNyQixxRUFBcUU7SUFDckUsSUFBSSxZQUFZO1FBQ2QsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDO0lBQ3RCLENBQUM7SUFFRCxJQUFJLFVBQVU7UUFDWixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDO0lBQ2xDLENBQUM7SUFFRCxJQUFJLFdBQVc7UUFDYixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDO0lBQ25DLENBQUM7SUFFRCxJQUFJLE1BQU07UUFDUixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDO0lBQzlCLENBQUM7SUFFRCxJQUFJLE9BQU87UUFDVCxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDO0lBQy9CLENBQUM7SUFF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QUFJLGFBQWEsSUFBSSxXQUFXO1FBQzlELFlBQVksSUFBSSxXQUFXLEVBQUU7UUFDL0IseUNBQXlDO1FBQ3pDLFNBQVMsR0FBRyxFQUFFLENBQUMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0tBQzVDO1NBQU07UUFDTCxNQUFNLElBQUksS0FBSyxDQUFDLHNCQUFzQixDQUFDLENBQUM7S0FDekM7SUFFRCxNQUFNLEtBQUssR0FBRyxJQUFJLFVBQVUsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUN4QyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDYixPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRCxTQUFTLFdBQVcsQ0FBQyxRQUFnQjtJQUNuQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRTtRQUMzQixRQUFRLEdBQUcsQ0FBQyxRQUFRLENBQUMsR0FBRyxHQUFHLENBQUM7S0FDN0I7SUFDRCxPQUFPLEdBQUcsUUFBUSxHQUFHLGtCQUFrQixHQUFHLGtCQUFrQixFQUFFLENBQUM7QUFDakUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCAyMDE4IEdvb2dsZSBMTEMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKi9cblxuaW1wb3J0IHtkaXNwb3NlLCBJbmZlcmVuY2VNb2RlbCwgaW8sIE1vZGVsUHJlZGljdENvbmZpZywgTmFtZWRUZW5zb3JNYXAsIFRlbnNvciwgdXRpbH0gZnJvbSAnQHRlbnNvcmZsb3cvdGZqcy1jb3JlJztcblxuaW1wb3J0ICogYXMgdGVuc29yZmxvdyBmcm9tICcuLi9kYXRhL2NvbXBpbGVkX2FwaSc7XG5pbXBvcnQge05hbWVkVGVuc29yc01hcCwgVGVuc29ySW5mb30gZnJvbSAnLi4vZGF0YS90eXBlcyc7XG5pbXBvcnQge09wZXJhdGlvbk1hcHBlcn0gZnJvbSAnLi4vb3BlcmF0aW9ucy9vcGVyYXRpb25fbWFwcGVyJztcblxuaW1wb3J0IHtHcmFwaEV4ZWN1dG9yfSBmcm9tICcuL2dyYXBoX2V4ZWN1dG9yJztcbmltcG9ydCB7UmVzb3VyY2VNYW5hZ2VyfSBmcm9tICcuL3Jlc291cmNlX21hbmFnZXInO1xuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOiBuby1pbXBvcnRzLWZyb20tZGlzdFxuaW1wb3J0IHtkZWNvZGVXZWlnaHRzU3RyZWFtfSBmcm9tICdAdGVuc29yZmxvdy90ZmpzLWNvcmUvZGlzdC9pby9pb191dGlscyc7XG5cbmV4cG9ydCBjb25zdCBURkhVQl9TRUFSQ0hfUEFSQU0gPSAnP3RmanMtZm9ybWF0PWZpbGUnO1xuZXhwb3J0IGNvbnN0IERFRkFVTFRfTU9ERUxfTkFNRSA9ICdtb2RlbC5qc29uJztcbnR5cGUgVXJsID0gc3RyaW5nfGlvLklPSGFuZGxlcnxpby5JT0hhbmRsZXJTeW5jO1xudHlwZSBVcmxJT0hhbmRsZXI8VCBleHRlbmRzIFVybD4gPSBUIGV4dGVuZHMgc3RyaW5nID8gaW8uSU9IYW5kbGVyIDogVDtcblxuLyoqXG4gKiBBIGB0Zi5HcmFwaE1vZGVsYCBpcyBhIGRpcmVjdGVkLCBhY3ljbGljIGdyYXBoIGJ1aWx0IGZyb20gYVxuICogU2F2ZWRNb2RlbCBHcmFwaERlZiBhbmQgYWxsb3dzIGluZmVyZW5jZSBleGVjdXRpb24uXG4gKlxuICogQSBgdGYuR3JhcGhNb2RlbGAgY2FuIG9ubHkgYmUgY3JlYXRlZCBieSBsb2FkaW5nIGZyb20gYSBtb2RlbCBjb252ZXJ0ZWQgZnJvbVxuICogYSBbVGVuc29yRmxvdyBTYXZlZE1vZGVsXShodHRwczovL3d3dy50ZW5zb3JmbG93Lm9yZy9ndWlkZS9zYXZlZF9tb2RlbCkgdXNpbmdcbiAqIHRoZSBjb21tYW5kIGxpbmUgY29udmVydGVyIHRvb2wgYW5kIGxvYWRlZCB2aWEgYHRmLmxvYWRHcmFwaE1vZGVsYC5cbiAqXG4gKiBAZG9jIHtoZWFkaW5nOiAnTW9kZWxzJywgc3ViaGVhZGluZzogJ0NsYXNzZXMnfVxuICovXG5leHBvcnQgY2xhc3MgR3JhcGhNb2RlbDxNb2RlbFVSTCBleHRlbmRzIFVybCA9IHN0cmluZyB8IGlvLklPSGFuZGxlcj4gaW1wbGVtZW50c1xuICAgIEluZmVyZW5jZU1vZGVsIHtcbiAgcHJpdmF0ZSBleGVjdXRvcjogR3JhcGhFeGVjdXRvcjtcbiAgcHJpdmF0ZSB2ZXJzaW9uID0gJ24vYSc7XG4gIHByaXZhdGUgaGFuZGxlcjogVXJsSU9IYW5kbGVyPE1vZGVsVVJMPjtcbiAgcHJpdmF0ZSBhcnRpZmFjdHM6IGlvLk1vZGVsQXJ0aWZhY3RzO1xuICBwcml2YXRlIGluaXRpYWxpemVyOiBHcmFwaEV4ZWN1dG9yO1xuICBwcml2YXRlIHJlc291cmNlSWRUb0NhcHR1cmVkSW5wdXQ6IHtba2V5OiBudW1iZXJdOiBUZW5zb3J9O1xuICBwcml2YXRlIHJlc291cmNlTWFuYWdlcjogUmVzb3VyY2VNYW5hZ2VyO1xuICBwcml2YXRlIHNpZ25hdHVyZTogdGVuc29yZmxvdy5JU2lnbmF0dXJlRGVmO1xuICBwcml2YXRlIGluaXRpYWxpemVyU2lnbmF0dXJlOiB0ZW5zb3JmbG93LklTaWduYXR1cmVEZWY7XG4gIHByaXZhdGUgc3RydWN0dXJlZE91dHB1dEtleXM6IHN0cmluZ1tdO1xuICBwcml2YXRlIHJlYWRvbmx5IGlvOiB0eXBlb2YgaW87XG5cbiAgLy8gUmV0dXJucyB0aGUgdmVyc2lvbiBpbmZvcm1hdGlvbiBmb3IgdGhlIHRlbnNvcmZsb3cgbW9kZWwgR3JhcGhEZWYuXG4gIGdldCBtb2RlbFZlcnNpb24oKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy52ZXJzaW9uO1xuICB9XG5cbiAgZ2V0IGlucHV0Tm9kZXMoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiB0aGlzLmV4ZWN1dG9yLmlucHV0Tm9kZXM7XG4gIH1cblxuICBnZXQgb3V0cHV0Tm9kZXMoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiB0aGlzLmV4ZWN1dG9yLm91dHB1dE5vZGVzO1xuICB9XG5cbiAgZ2V0IGlucHV0cygpOiBUZW5zb3JJbmZvW10ge1xuICAgIHJldHVybiB0aGlzLmV4ZWN1dG9yLmlucHV0cztcbiAgfVxuXG4gIGdldCBvdXRwdXRzKCk6IFRlbnNvckluZm9bXSB7XG4gICAgcmV0dXJuIHRoaXMuZXhlY3V0b3Iub3V0cHV0cztcbiAgfVxuXG4gIGdldCB3ZWlnaHRzKCk6IE5hbWVkVGVuc29yc01hcCB7XG4gICAgcmV0dXJuIHRoaXMuZXhlY3V0b3Iud2VpZ2h0TWFwO1xuICB9XG5cbiAgZ2V0IG1ldGFkYXRhKCk6IHt9IHtcbiAgICByZXR1cm4gdGhpcy5hcnRpZmFjdHMudXNlckRlZmluZWRNZXRhZGF0YTtcbiAgfVxuXG4gIGdldCBtb2RlbFNpZ25hdHVyZSgpOiB7fSB7XG4gICAgcmV0dXJuIHRoaXMuc2lnbmF0dXJlO1xuICB9XG5cbiAgZ2V0IG1vZGVsU3RydWN0dXJlZE91dHB1dEtleXMoKToge30ge1xuICAgIHJldHVybiB0aGlzLnN0cnVjdHVyZWRPdXRwdXRLZXlzO1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSBtb2RlbFVybCB1cmwgZm9yIHRoZSBtb2RlbCwgb3IgYW4gYGlvLklPSGFuZGxlcmAuXG4gICAqIEBwYXJhbSB3ZWlnaHRNYW5pZmVzdFVybCB1cmwgZm9yIHRoZSB3ZWlnaHQgZmlsZSBnZW5lcmF0ZWQgYnlcbiAgICogc2NyaXB0cy9jb252ZXJ0LnB5IHNjcmlwdC5cbiAgICogQHBhcmFtIHJlcXVlc3RPcHRpb24gb3B0aW9ucyBmb3IgUmVxdWVzdCwgd2hpY2ggYWxsb3dzIHRvIHNlbmQgY3JlZGVudGlhbHNcbiAgICogYW5kIGN1c3RvbSBoZWFkZXJzLlxuICAgKiBAcGFyYW0gb25Qcm9ncmVzcyBPcHRpb25hbCwgcHJvZ3Jlc3MgY2FsbGJhY2sgZnVuY3Rpb24sIGZpcmVkIHBlcmlvZGljYWxseVxuICAgKiBiZWZvcmUgdGhlIGxvYWQgaXMgY29tcGxldGVkLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIG1vZGVsVXJsOiBNb2RlbFVSTCwgcHJpdmF0ZSBsb2FkT3B0aW9uczogaW8uTG9hZE9wdGlvbnMgPSB7fSxcbiAgICAgIHRmaW8gPSBpbykge1xuICAgIHRoaXMuaW8gPSB0ZmlvO1xuICAgIGlmIChsb2FkT3B0aW9ucyA9PSBudWxsKSB7XG4gICAgICB0aGlzLmxvYWRPcHRpb25zID0ge307XG4gICAgfVxuICAgIHRoaXMucmVzb3VyY2VNYW5hZ2VyID0gbmV3IFJlc291cmNlTWFuYWdlcigpO1xuICB9XG5cbiAgcHJpdmF0ZSBmaW5kSU9IYW5kbGVyKCkge1xuICAgIHR5cGUgSU9IYW5kbGVyID0gVXJsSU9IYW5kbGVyPE1vZGVsVVJMPjtcbiAgICBjb25zdCBwYXRoID0gdGhpcy5tb2RlbFVybDtcbiAgICBpZiAoKHBhdGggYXMgaW8uSU9IYW5kbGVyKS5sb2FkICE9IG51bGwpIHtcbiAgICAgIC8vIFBhdGggaXMgYW4gSU8gSGFuZGxlci5cbiAgICAgIHRoaXMuaGFuZGxlciA9IHBhdGggYXMgSU9IYW5kbGVyO1xuICAgIH0gZWxzZSBpZiAodGhpcy5sb2FkT3B0aW9ucy5yZXF1ZXN0SW5pdCAhPSBudWxsKSB7XG4gICAgICB0aGlzLmhhbmRsZXIgPSB0aGlzLmlvLmJyb3dzZXJIVFRQUmVxdWVzdChcbiAgICAgICAgICAgICAgICAgICAgICAgICBwYXRoIGFzIHN0cmluZywgdGhpcy5sb2FkT3B0aW9ucykgYXMgSU9IYW5kbGVyO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBoYW5kbGVycyA9XG4gICAgICAgICAgdGhpcy5pby5nZXRMb2FkSGFuZGxlcnMocGF0aCBhcyBzdHJpbmcsIHRoaXMubG9hZE9wdGlvbnMpO1xuICAgICAgaWYgKGhhbmRsZXJzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAvLyBGb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eTogaWYgbm8gbG9hZCBoYW5kbGVyIGNhbiBiZSBmb3VuZCxcbiAgICAgICAgLy8gYXNzdW1lIGl0IGlzIGEgcmVsYXRpdmUgaHR0cCBwYXRoLlxuICAgICAgICBoYW5kbGVycy5wdXNoKFxuICAgICAgICAgICAgdGhpcy5pby5icm93c2VySFRUUFJlcXVlc3QocGF0aCBhcyBzdHJpbmcsIHRoaXMubG9hZE9wdGlvbnMpKTtcbiAgICAgIH0gZWxzZSBpZiAoaGFuZGxlcnMubGVuZ3RoID4gMSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRm91bmQgbW9yZSB0aGFuIG9uZSAoJHtoYW5kbGVycy5sZW5ndGh9KSBsb2FkIGhhbmRsZXJzIGZvciBgICtcbiAgICAgICAgICAgIGBVUkwgJyR7W3BhdGhdfSdgKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuaGFuZGxlciA9IGhhbmRsZXJzWzBdIGFzIElPSGFuZGxlcjtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTG9hZHMgdGhlIG1vZGVsIGFuZCB3ZWlnaHQgZmlsZXMsIGNvbnN0cnVjdCB0aGUgaW4gbWVtb3J5IHdlaWdodCBtYXAgYW5kXG4gICAqIGNvbXBpbGUgdGhlIGluZmVyZW5jZSBncmFwaC5cbiAgICovXG4gIGxvYWQoKTogVXJsSU9IYW5kbGVyPE1vZGVsVVJMPiBleHRlbmRzIGlvLklPSGFuZGxlclN5bmM/IGJvb2xlYW46XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICB0eXBlIElPSGFuZGxlciA9IFVybElPSGFuZGxlcjxNb2RlbFVSTD47XG4gICAgdGhpcy5maW5kSU9IYW5kbGVyKCk7XG4gICAgaWYgKHRoaXMuaGFuZGxlci5sb2FkID09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnQ2Fubm90IHByb2NlZWQgd2l0aCBtb2RlbCBsb2FkaW5nIGJlY2F1c2UgdGhlIElPSGFuZGxlciBwcm92aWRlZCAnICtcbiAgICAgICAgICAnZG9lcyBub3QgaGF2ZSB0aGUgYGxvYWRgIG1ldGhvZCBpbXBsZW1lbnRlZC4nKTtcbiAgICB9XG5cbiAgICB0eXBlIFJlc3VsdCA9XG4gICAgICAgIElPSGFuZGxlciBleHRlbmRzIGlvLklPSGFuZGxlclN5bmMgPyBib29sZWFuIDogUHJvbWlzZTxib29sZWFuPjtcblxuICAgIGNvbnN0IGxvYWRSZXN1bHQgPSB0aGlzLmhhbmRsZXIubG9hZCgpIGFzIFJldHVyblR5cGU8SU9IYW5kbGVyWydsb2FkJ10+O1xuICAgIGlmICh1dGlsLmlzUHJvbWlzZShsb2FkUmVzdWx0KSkge1xuICAgICAgcmV0dXJuIGxvYWRSZXN1bHQudGhlbihhcnRpZmFjdHMgPT4ge1xuICAgICAgICBpZiAoYXJ0aWZhY3RzLmdldFdlaWdodFN0cmVhbSA9PSBudWxsKSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMubG9hZFN5bmMoYXJ0aWZhY3RzKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5sb2FkU3RyZWFtaW5nKGFydGlmYWN0cyk7XG4gICAgICB9KSBhcyBSZXN1bHQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMubG9hZFN5bmMobG9hZFJlc3VsdCkgYXMgUmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIFN5bmNocm9ub3VzbHkgY29uc3RydWN0IHRoZSBpbiBtZW1vcnkgd2VpZ2h0IG1hcCBhbmRcbiAgICogY29tcGlsZSB0aGUgaW5mZXJlbmNlIGdyYXBoLlxuICAgKlxuICAgKiBAZG9jIHtoZWFkaW5nOiAnTW9kZWxzJywgc3ViaGVhZGluZzogJ0NsYXNzZXMnLCBpZ25vcmVDSTogdHJ1ZX1cbiAgICovXG4gIGxvYWRTeW5jKGFydGlmYWN0czogaW8uTW9kZWxBcnRpZmFjdHMpIHtcbiAgICBjb25zdCB3ZWlnaHRNYXAgPSB0aGlzLmlvLmRlY29kZVdlaWdodHMoXG4gICAgICAgIGFydGlmYWN0cy53ZWlnaHREYXRhLCBhcnRpZmFjdHMud2VpZ2h0U3BlY3MpO1xuXG4gICAgcmV0dXJuIHRoaXMubG9hZFdpdGhXZWlnaHRNYXAoYXJ0aWZhY3RzLCB3ZWlnaHRNYXApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBsb2FkU3RyZWFtaW5nKGFydGlmYWN0czogaW8uTW9kZWxBcnRpZmFjdHMpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBpZiAoYXJ0aWZhY3RzLmdldFdlaWdodFN0cmVhbSA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vZGVsIGFydGlmYWN0cyBtaXNzaW5nIHN0cmVhbVdlaWdodHMgZnVuY3Rpb24nKTtcbiAgICB9XG5cbiAgICBjb25zdCB3ZWlnaHRNYXAgPSBhd2FpdCBkZWNvZGVXZWlnaHRzU3RyZWFtKFxuICAgICAgYXJ0aWZhY3RzLmdldFdlaWdodFN0cmVhbSgpLCBhcnRpZmFjdHMud2VpZ2h0U3BlY3MpO1xuXG4gICAgcmV0dXJuIHRoaXMubG9hZFdpdGhXZWlnaHRNYXAoYXJ0aWZhY3RzLCB3ZWlnaHRNYXApO1xuICB9XG5cbiAgcHJpdmF0ZSBsb2FkV2l0aFdlaWdodE1hcChhcnRpZmFjdHM6IGlvLk1vZGVsQXJ0aWZhY3RzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodE1hcDogTmFtZWRUZW5zb3JNYXApIHtcbiAgICB0aGlzLmFydGlmYWN0cyA9IGFydGlmYWN0cztcbiAgICBjb25zdCBncmFwaCA9IHRoaXMuYXJ0aWZhY3RzLm1vZGVsVG9wb2xvZ3kgYXMgdGVuc29yZmxvdy5JR3JhcGhEZWY7XG5cbiAgICBsZXQgc2lnbmF0dXJlID0gdGhpcy5hcnRpZmFjdHMuc2lnbmF0dXJlO1xuICAgIGlmICh0aGlzLmFydGlmYWN0cy51c2VyRGVmaW5lZE1ldGFkYXRhICE9IG51bGwpIHtcbiAgICAgIGNvbnN0IG1ldGFkYXRhID0gdGhpcy5hcnRpZmFjdHMudXNlckRlZmluZWRNZXRhZGF0YTtcbiAgICAgIGlmIChtZXRhZGF0YS5zaWduYXR1cmUgIT0gbnVsbCkge1xuICAgICAgICBzaWduYXR1cmUgPSBtZXRhZGF0YS5zaWduYXR1cmU7XG4gICAgICB9XG5cbiAgICAgIGlmIChtZXRhZGF0YS5zdHJ1Y3R1cmVkT3V0cHV0S2V5cyAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMuc3RydWN0dXJlZE91dHB1dEtleXMgPSBtZXRhZGF0YS5zdHJ1Y3R1cmVkT3V0cHV0S2V5cyBhcyBzdHJpbmdbXTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5zaWduYXR1cmUgPSBzaWduYXR1cmU7XG5cbiAgICB0aGlzLnZlcnNpb24gPSBgJHtncmFwaC52ZXJzaW9ucy5wcm9kdWNlcn0uJHtncmFwaC52ZXJzaW9ucy5taW5Db25zdW1lcn1gO1xuICAgIHRoaXMuZXhlY3V0b3IgPSBuZXcgR3JhcGhFeGVjdXRvcihcbiAgICAgICAgT3BlcmF0aW9uTWFwcGVyLkluc3RhbmNlLnRyYW5zZm9ybUdyYXBoKGdyYXBoLCB0aGlzLnNpZ25hdHVyZSkpO1xuICAgIHRoaXMuZXhlY3V0b3Iud2VpZ2h0TWFwID0gdGhpcy5jb252ZXJ0VGVuc29yTWFwVG9UZW5zb3JzTWFwKHdlaWdodE1hcCk7XG4gICAgLy8gQXR0YWNoIGEgbW9kZWwtbGV2ZWwgcmVzb3VyY2VNYW5hZ2VyIHRvIGVhY2ggZXhlY3V0b3IgdG8gc2hhcmUgcmVzb3VyY2VzLFxuICAgIC8vIHN1Y2ggYXMgYEhhc2hUYWJsZWAuXG4gICAgdGhpcy5leGVjdXRvci5yZXNvdXJjZU1hbmFnZXIgPSB0aGlzLnJlc291cmNlTWFuYWdlcjtcblxuICAgIGlmIChhcnRpZmFjdHMubW9kZWxJbml0aWFsaXplciAhPSBudWxsICYmXG4gICAgICAgIChhcnRpZmFjdHMubW9kZWxJbml0aWFsaXplciBhcyB0ZW5zb3JmbG93LklHcmFwaERlZikubm9kZSAhPSBudWxsKSB7XG4gICAgICBjb25zdCBpbml0aWFsaXplciA9XG4gICAgICAgICAgT3BlcmF0aW9uTWFwcGVyLkluc3RhbmNlLnRyYW5zZm9ybUdyYXBoKGFydGlmYWN0cy5tb2RlbEluaXRpYWxpemVyKTtcbiAgICAgIHRoaXMuaW5pdGlhbGl6ZXIgPSBuZXcgR3JhcGhFeGVjdXRvcihpbml0aWFsaXplcik7XG4gICAgICB0aGlzLmluaXRpYWxpemVyLndlaWdodE1hcCA9IHRoaXMuZXhlY3V0b3Iud2VpZ2h0TWFwO1xuICAgICAgLy8gQXR0YWNoIGEgbW9kZWwtbGV2ZWwgcmVzb3VyY2VNYW5hZ2VyIHRvIHRoZSBpbml0aWFsaXplciwgdGhlXG4gICAgICAvLyBoYXNoVGFibGVzIGNyZWF0ZWQgZnJvbSB3aGVuIGV4ZWN1dGluZyB0aGUgaW5pdGlhbGl6ZXIgd2lsbCBiZSBzdG9yZWRcbiAgICAgIC8vIGluIHRoZSByZXNvdXJjZU1hbmFnZXIuXG4gICAgICB0aGlzLmluaXRpYWxpemVyLnJlc291cmNlTWFuYWdlciA9IHRoaXMucmVzb3VyY2VNYW5hZ2VyO1xuICAgICAgdGhpcy5pbml0aWFsaXplclNpZ25hdHVyZSA9IGFydGlmYWN0cy5pbml0aWFsaXplclNpZ25hdHVyZTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYXZlIHRoZSBjb25maWd1cmF0aW9uIGFuZC9vciB3ZWlnaHRzIG9mIHRoZSBHcmFwaE1vZGVsLlxuICAgKlxuICAgKiBBbiBgSU9IYW5kbGVyYCBpcyBhbiBvYmplY3QgdGhhdCBoYXMgYSBgc2F2ZWAgbWV0aG9kIG9mIHRoZSBwcm9wZXJcbiAgICogc2lnbmF0dXJlIGRlZmluZWQuIFRoZSBgc2F2ZWAgbWV0aG9kIG1hbmFnZXMgdGhlIHN0b3Jpbmcgb3JcbiAgICogdHJhbnNtaXNzaW9uIG9mIHNlcmlhbGl6ZWQgZGF0YSAoXCJhcnRpZmFjdHNcIikgdGhhdCByZXByZXNlbnQgdGhlXG4gICAqIG1vZGVsJ3MgdG9wb2xvZ3kgYW5kIHdlaWdodHMgb250byBvciB2aWEgYSBzcGVjaWZpYyBtZWRpdW0sIHN1Y2ggYXNcbiAgICogZmlsZSBkb3dubG9hZHMsIGxvY2FsIHN0b3JhZ2UsIEluZGV4ZWREQiBpbiB0aGUgd2ViIGJyb3dzZXIgYW5kIEhUVFBcbiAgICogcmVxdWVzdHMgdG8gYSBzZXJ2ZXIuIFRlbnNvckZsb3cuanMgcHJvdmlkZXMgYElPSGFuZGxlcmBcbiAgICogaW1wbGVtZW50YXRpb25zIGZvciBhIG51bWJlciBvZiBmcmVxdWVudGx5IHVzZWQgc2F2aW5nIG1lZGl1bXMsIHN1Y2ggYXNcbiAgICogYHRmLmlvLmJyb3dzZXJEb3dubG9hZHNgIGFuZCBgdGYuaW8uYnJvd3NlckxvY2FsU3RvcmFnZWAuIFNlZSBgdGYuaW9gXG4gICAqIGZvciBtb3JlIGRldGFpbHMuXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGFsc28gYWxsb3dzIHlvdSB0byByZWZlciB0byBjZXJ0YWluIHR5cGVzIG9mIGBJT0hhbmRsZXJgc1xuICAgKiBhcyBVUkwtbGlrZSBzdHJpbmcgc2hvcnRjdXRzLCBzdWNoIGFzICdsb2NhbHN0b3JhZ2U6Ly8nIGFuZFxuICAgKiAnaW5kZXhlZGRiOi8vJy5cbiAgICpcbiAgICogRXhhbXBsZSAxOiBTYXZlIGBtb2RlbGAncyB0b3BvbG9neSBhbmQgd2VpZ2h0cyB0byBicm93c2VyIFtsb2NhbFxuICAgKiBzdG9yYWdlXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9BUEkvV2luZG93L2xvY2FsU3RvcmFnZSk7XG4gICAqIHRoZW4gbG9hZCBpdCBiYWNrLlxuICAgKlxuICAgKiBgYGBqc1xuICAgKiBjb25zdCBtb2RlbFVybCA9XG4gICAqICAgICdodHRwczovL3N0b3JhZ2UuZ29vZ2xlYXBpcy5jb20vdGZqcy1tb2RlbHMvc2F2ZWRtb2RlbC9tb2JpbGVuZXRfdjJfMS4wXzIyNC9tb2RlbC5qc29uJztcbiAgICogY29uc3QgbW9kZWwgPSBhd2FpdCB0Zi5sb2FkR3JhcGhNb2RlbChtb2RlbFVybCk7XG4gICAqIGNvbnN0IHplcm9zID0gdGYuemVyb3MoWzEsIDIyNCwgMjI0LCAzXSk7XG4gICAqIG1vZGVsLnByZWRpY3QoemVyb3MpLnByaW50KCk7XG4gICAqXG4gICAqIGNvbnN0IHNhdmVSZXN1bHRzID0gYXdhaXQgbW9kZWwuc2F2ZSgnbG9jYWxzdG9yYWdlOi8vbXktbW9kZWwtMScpO1xuICAgKlxuICAgKiBjb25zdCBsb2FkZWRNb2RlbCA9IGF3YWl0IHRmLmxvYWRHcmFwaE1vZGVsKCdsb2NhbHN0b3JhZ2U6Ly9teS1tb2RlbC0xJyk7XG4gICAqIGNvbnNvbGUubG9nKCdQcmVkaWN0aW9uIGZyb20gbG9hZGVkIG1vZGVsOicpO1xuICAgKiBtb2RlbC5wcmVkaWN0KHplcm9zKS5wcmludCgpO1xuICAgKiBgYGBcbiAgICpcbiAgICogQHBhcmFtIGhhbmRsZXJPclVSTCBBbiBpbnN0YW5jZSBvZiBgSU9IYW5kbGVyYCBvciBhIFVSTC1saWtlLFxuICAgKiBzY2hlbWUtYmFzZWQgc3RyaW5nIHNob3J0Y3V0IGZvciBgSU9IYW5kbGVyYC5cbiAgICogQHBhcmFtIGNvbmZpZyBPcHRpb25zIGZvciBzYXZpbmcgdGhlIG1vZGVsLlxuICAgKiBAcmV0dXJucyBBIGBQcm9taXNlYCBvZiBgU2F2ZVJlc3VsdGAsIHdoaWNoIHN1bW1hcml6ZXMgdGhlIHJlc3VsdCBvZlxuICAgKiB0aGUgc2F2aW5nLCBzdWNoIGFzIGJ5dGUgc2l6ZXMgb2YgdGhlIHNhdmVkIGFydGlmYWN0cyBmb3IgdGhlIG1vZGVsJ3NcbiAgICogICB0b3BvbG9neSBhbmQgd2VpZ2h0IHZhbHVlcy5cbiAgICpcbiAgICogQGRvYyB7aGVhZGluZzogJ01vZGVscycsIHN1YmhlYWRpbmc6ICdDbGFzc2VzJywgaWdub3JlQ0k6IHRydWV9XG4gICAqL1xuICBhc3luYyBzYXZlKGhhbmRsZXJPclVSTDogaW8uSU9IYW5kbGVyfHN0cmluZywgY29uZmlnPzogaW8uU2F2ZUNvbmZpZyk6XG4gICAgICBQcm9taXNlPGlvLlNhdmVSZXN1bHQ+IHtcbiAgICBpZiAodHlwZW9mIGhhbmRsZXJPclVSTCA9PT0gJ3N0cmluZycpIHtcbiAgICAgIGNvbnN0IGhhbmRsZXJzID0gdGhpcy5pby5nZXRTYXZlSGFuZGxlcnMoaGFuZGxlck9yVVJMKTtcbiAgICAgIGlmIChoYW5kbGVycy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYENhbm5vdCBmaW5kIGFueSBzYXZlIGhhbmRsZXJzIGZvciBVUkwgJyR7aGFuZGxlck9yVVJMfSdgKTtcbiAgICAgIH0gZWxzZSBpZiAoaGFuZGxlcnMubGVuZ3RoID4gMSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRm91bmQgbW9yZSB0aGFuIG9uZSAoJHtoYW5kbGVycy5sZW5ndGh9KSBzYXZlIGhhbmRsZXJzIGZvciBgICtcbiAgICAgICAgICAgIGBVUkwgJyR7aGFuZGxlck9yVVJMfSdgKTtcbiAgICAgIH1cbiAgICAgIGhhbmRsZXJPclVSTCA9IGhhbmRsZXJzWzBdO1xuICAgIH1cbiAgICBpZiAoaGFuZGxlck9yVVJMLnNhdmUgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdHcmFwaE1vZGVsLnNhdmUoKSBjYW5ub3QgcHJvY2VlZCBiZWNhdXNlIHRoZSBJT0hhbmRsZXIgJyArXG4gICAgICAgICAgJ3Byb3ZpZGVkIGRvZXMgbm90IGhhdmUgdGhlIGBzYXZlYCBhdHRyaWJ1dGUgZGVmaW5lZC4nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gaGFuZGxlck9yVVJMLnNhdmUodGhpcy5hcnRpZmFjdHMpO1xuICB9XG5cbiAgcHJpdmF0ZSBhZGRTdHJ1Y3R1cmVkT3V0cHV0TmFtZXMob3V0cHV0VGVuc29yczogVGVuc29yfFRlbnNvcltdKSB7XG4gICAgaWYgKHRoaXMuc3RydWN0dXJlZE91dHB1dEtleXMpIHtcbiAgICAgIGNvbnN0IG91dHB1dFRlbnNvcnNBcnJheSA9XG4gICAgICAgICAgb3V0cHV0VGVuc29ycyBpbnN0YW5jZW9mIFRlbnNvciA/IFtvdXRwdXRUZW5zb3JzXSA6IG91dHB1dFRlbnNvcnM7XG4gICAgICBjb25zdCBvdXRwdXRUZW5zb3JNYXA6IE5hbWVkVGVuc29yTWFwID0ge307XG5cbiAgICAgIG91dHB1dFRlbnNvcnNBcnJheS5mb3JFYWNoKFxuICAgICAgICAgIChvdXRwdXRUZW5zb3IsIGkpID0+IG91dHB1dFRlbnNvck1hcFt0aGlzLnN0cnVjdHVyZWRPdXRwdXRLZXlzW2ldXSA9XG4gICAgICAgICAgICAgIG91dHB1dFRlbnNvcik7XG5cbiAgICAgIHJldHVybiBvdXRwdXRUZW5zb3JNYXA7XG4gICAgfVxuICAgIHJldHVybiBvdXRwdXRUZW5zb3JzO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4ZWN1dGUgdGhlIGluZmVyZW5jZSBmb3IgdGhlIGlucHV0IHRlbnNvcnMuXG4gICAqXG4gICAqIEBwYXJhbSBpbnB1dCBUaGUgaW5wdXQgdGVuc29ycywgd2hlbiB0aGVyZSBpcyBzaW5nbGUgaW5wdXQgZm9yIHRoZSBtb2RlbCxcbiAgICogaW5wdXRzIHBhcmFtIHNob3VsZCBiZSBhIGB0Zi5UZW5zb3JgLiBGb3IgbW9kZWxzIHdpdGggbXV0bGlwbGUgaW5wdXRzLFxuICAgKiBpbnB1dHMgcGFyYW1zIHNob3VsZCBiZSBpbiBlaXRoZXIgYHRmLlRlbnNvcmBbXSBpZiB0aGUgaW5wdXQgb3JkZXIgaXNcbiAgICogZml4ZWQsIG9yIG90aGVyd2lzZSBOYW1lZFRlbnNvck1hcCBmb3JtYXQuXG4gICAqXG4gICAqIEZvciBtb2RlbCB3aXRoIG11bHRpcGxlIGlucHV0cywgd2UgcmVjb21tZW5kIHlvdSB1c2UgTmFtZWRUZW5zb3JNYXAgYXMgdGhlXG4gICAqIGlucHV0IHR5cGUsIGlmIHlvdSB1c2UgYHRmLlRlbnNvcmBbXSwgdGhlIG9yZGVyIG9mIHRoZSBhcnJheSBuZWVkcyB0b1xuICAgKiBmb2xsb3cgdGhlXG4gICAqIG9yZGVyIG9mIGlucHV0Tm9kZXMgYXJyYXkuIEBzZWUge0BsaW5rIEdyYXBoTW9kZWwuaW5wdXROb2Rlc31cbiAgICpcbiAgICogWW91IGNhbiBhbHNvIGZlZWQgYW55IGludGVybWVkaWF0ZSBub2RlcyB1c2luZyB0aGUgTmFtZWRUZW5zb3JNYXAgYXMgdGhlXG4gICAqIGlucHV0IHR5cGUuIEZvciBleGFtcGxlLCBnaXZlbiB0aGUgZ3JhcGhcbiAgICogICAgSW5wdXROb2RlID0+IEludGVybWVkaWF0ZSA9PiBPdXRwdXROb2RlLFxuICAgKiB5b3UgY2FuIGV4ZWN1dGUgdGhlIHN1YmdyYXBoIEludGVybWVkaWF0ZSA9PiBPdXRwdXROb2RlIGJ5IGNhbGxpbmdcbiAgICogICAgbW9kZWwuZXhlY3V0ZSgnSW50ZXJtZWRpYXRlTm9kZScgOiB0Zi50ZW5zb3IoLi4uKSk7XG4gICAqXG4gICAqIFRoaXMgaXMgdXNlZnVsIGZvciBtb2RlbHMgdGhhdCB1c2VzIHRmLmR5bmFtaWNfcm5uLCB3aGVyZSB0aGUgaW50ZXJtZWRpYXRlXG4gICAqIHN0YXRlIG5lZWRzIHRvIGJlIGZlZCBtYW51YWxseS5cbiAgICpcbiAgICogRm9yIGJhdGNoIGluZmVyZW5jZSBleGVjdXRpb24sIHRoZSB0ZW5zb3JzIGZvciBlYWNoIGlucHV0IG5lZWQgdG8gYmVcbiAgICogY29uY2F0ZW5hdGVkIHRvZ2V0aGVyLiBGb3IgZXhhbXBsZSB3aXRoIG1vYmlsZW5ldCwgdGhlIHJlcXVpcmVkIGlucHV0IHNoYXBlXG4gICAqIGlzIFsxLCAyNDQsIDI0NCwgM10sIHdoaWNoIHJlcHJlc2VudHMgdGhlIFtiYXRjaCwgaGVpZ2h0LCB3aWR0aCwgY2hhbm5lbF0uXG4gICAqIElmIHdlIGFyZSBwcm92aWRlIGEgYmF0Y2hlZCBkYXRhIG9mIDEwMCBpbWFnZXMsIHRoZSBpbnB1dCB0ZW5zb3Igc2hvdWxkIGJlXG4gICAqIGluIHRoZSBzaGFwZSBvZiBbMTAwLCAyNDQsIDI0NCwgM10uXG4gICAqXG4gICAqIEBwYXJhbSBjb25maWcgUHJlZGljdGlvbiBjb25maWd1cmF0aW9uIGZvciBzcGVjaWZ5aW5nIHRoZSBiYXRjaCBzaXplLlxuICAgKiBDdXJyZW50bHkgdGhlIGJhdGNoIHNpemUgb3B0aW9uIGlzIGlnbm9yZWQgZm9yIGdyYXBoIG1vZGVsLlxuICAgKlxuICAgKiBAcmV0dXJucyBJbmZlcmVuY2UgcmVzdWx0IHRlbnNvcnMuIElmIHRoZSBtb2RlbCBpcyBjb252ZXJ0ZWQgYW5kIGl0XG4gICAqIG9yaWdpbmFsbHkgaGFkIHN0cnVjdHVyZWRfb3V0cHV0cyBpbiB0ZW5zb3JmbG93LCB0aGVuIGEgTmFtZWRUZW5zb3JNYXBcbiAgICogd2lsbCBiZSByZXR1cm5lZCBtYXRjaGluZyB0aGUgc3RydWN0dXJlZF9vdXRwdXRzLiBJZiBubyBzdHJ1Y3R1cmVkX291dHB1dHNcbiAgICogYXJlIHByZXNlbnQsIHRoZSBvdXRwdXQgd2lsbCBiZSBzaW5nbGUgYHRmLlRlbnNvcmAgaWYgdGhlIG1vZGVsIGhhcyBzaW5nbGVcbiAgICogb3V0cHV0IG5vZGUsIG90aGVyd2lzZSBUZW5zb3JbXS5cbiAgICpcbiAgICogQGRvYyB7aGVhZGluZzogJ01vZGVscycsIHN1YmhlYWRpbmc6ICdDbGFzc2VzJ31cbiAgICovXG4gIHByZWRpY3QoaW5wdXRzOiBUZW5zb3J8VGVuc29yW118TmFtZWRUZW5zb3JNYXAsIGNvbmZpZz86IE1vZGVsUHJlZGljdENvbmZpZyk6XG4gICAgICBUZW5zb3J8VGVuc29yW118TmFtZWRUZW5zb3JNYXAge1xuICAgIGNvbnN0IG91dHB1dFRlbnNvcnMgPSB0aGlzLmV4ZWN1dGUoaW5wdXRzLCB0aGlzLm91dHB1dE5vZGVzKTtcbiAgICByZXR1cm4gdGhpcy5hZGRTdHJ1Y3R1cmVkT3V0cHV0TmFtZXMob3V0cHV0VGVuc29ycyk7XG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZSB0aGUgaW5mZXJlbmNlIGZvciB0aGUgaW5wdXQgdGVuc29ycyBpbiBhc3luYyBmYXNoaW9uLCB1c2UgdGhpc1xuICAgKiBtZXRob2Qgd2hlbiB5b3VyIG1vZGVsIGNvbnRhaW5zIGNvbnRyb2wgZmxvdyBvcHMuXG4gICAqXG4gICAqIEBwYXJhbSBpbnB1dCBUaGUgaW5wdXQgdGVuc29ycywgd2hlbiB0aGVyZSBpcyBzaW5nbGUgaW5wdXQgZm9yIHRoZSBtb2RlbCxcbiAgICogaW5wdXRzIHBhcmFtIHNob3VsZCBiZSBhIGB0Zi5UZW5zb3JgLiBGb3IgbW9kZWxzIHdpdGggbXV0bGlwbGUgaW5wdXRzLFxuICAgKiBpbnB1dHMgcGFyYW1zIHNob3VsZCBiZSBpbiBlaXRoZXIgYHRmLlRlbnNvcmBbXSBpZiB0aGUgaW5wdXQgb3JkZXIgaXNcbiAgICogZml4ZWQsIG9yIG90aGVyd2lzZSBOYW1lZFRlbnNvck1hcCBmb3JtYXQuXG4gICAqXG4gICAqIEZvciBtb2RlbCB3aXRoIG11bHRpcGxlIGlucHV0cywgd2UgcmVjb21tZW5kIHlvdSB1c2UgTmFtZWRUZW5zb3JNYXAgYXMgdGhlXG4gICAqIGlucHV0IHR5cGUsIGlmIHlvdSB1c2UgYHRmLlRlbnNvcmBbXSwgdGhlIG9yZGVyIG9mIHRoZSBhcnJheSBuZWVkcyB0b1xuICAgKiBmb2xsb3cgdGhlXG4gICAqIG9yZGVyIG9mIGlucHV0Tm9kZXMgYXJyYXkuIEBzZWUge0BsaW5rIEdyYXBoTW9kZWwuaW5wdXROb2Rlc31cbiAgICpcbiAgICogWW91IGNhbiBhbHNvIGZlZWQgYW55IGludGVybWVkaWF0ZSBub2RlcyB1c2luZyB0aGUgTmFtZWRUZW5zb3JNYXAgYXMgdGhlXG4gICAqIGlucHV0IHR5cGUuIEZvciBleGFtcGxlLCBnaXZlbiB0aGUgZ3JhcGhcbiAgICogICAgSW5wdXROb2RlID0+IEludGVybWVkaWF0ZSA9PiBPdXRwdXROb2RlLFxuICAgKiB5b3UgY2FuIGV4ZWN1dGUgdGhlIHN1YmdyYXBoIEludGVybWVkaWF0ZSA9PiBPdXRwdXROb2RlIGJ5IGNhbGxpbmdcbiAgICogICAgbW9kZWwuZXhlY3V0ZSgnSW50ZXJtZWRpYXRlTm9kZScgOiB0Zi50ZW5zb3IoLi4uKSk7XG4gICAqXG4gICAqIFRoaXMgaXMgdXNlZnVsIGZvciBtb2RlbHMgdGhhdCB1c2VzIHRmLmR5bmFtaWNfcm5uLCB3aGVyZSB0aGUgaW50ZXJtZWRpYXRlXG4gICAqIHN0YXRlIG5lZWRzIHRvIGJlIGZlZCBtYW51YWxseS5cbiAgICpcbiAgICogRm9yIGJhdGNoIGluZmVyZW5jZSBleGVjdXRpb24sIHRoZSB0ZW5zb3JzIGZvciBlYWNoIGlucHV0IG5lZWQgdG8gYmVcbiAgICogY29uY2F0ZW5hdGVkIHRvZ2V0aGVyLiBGb3IgZXhhbXBsZSB3aXRoIG1vYmlsZW5ldCwgdGhlIHJlcXVpcmVkIGlucHV0IHNoYXBlXG4gICAqIGlzIFsxLCAyNDQsIDI0NCwgM10sIHdoaWNoIHJlcHJlc2VudHMgdGhlIFtiYXRjaCwgaGVpZ2h0LCB3aWR0aCwgY2hhbm5lbF0uXG4gICAqIElmIHdlIGFyZSBwcm92aWRlIGEgYmF0Y2hlZCBkYXRhIG9mIDEwMCBpbWFnZXMsIHRoZSBpbnB1dCB0ZW5zb3Igc2hvdWxkIGJlXG4gICAqIGluIHRoZSBzaGFwZSBvZiBbMTAwLCAyNDQsIDI0NCwgM10uXG4gICAqXG4gICAqIEBwYXJhbSBjb25maWcgUHJlZGljdGlvbiBjb25maWd1cmF0aW9uIGZvciBzcGVjaWZ5aW5nIHRoZSBiYXRjaCBzaXplLlxuICAgKiBDdXJyZW50bHkgdGhlIGJhdGNoIHNpemUgb3B0aW9uIGlzIGlnbm9yZWQgZm9yIGdyYXBoIG1vZGVsLlxuICAgKlxuICAgKiBAcmV0dXJucyBBIFByb21pc2Ugb2YgaW5mZXJlbmNlIHJlc3VsdCB0ZW5zb3JzLiBJZiB0aGUgbW9kZWwgaXMgY29udmVydGVkXG4gICAqIGFuZCBpdCBvcmlnaW5hbGx5IGhhZCBzdHJ1Y3R1cmVkX291dHB1dHMgaW4gdGVuc29yZmxvdywgdGhlbiBhXG4gICAqIE5hbWVkVGVuc29yTWFwIHdpbGwgYmUgcmV0dXJuZWQgbWF0Y2hpbmcgdGhlIHN0cnVjdHVyZWRfb3V0cHV0cy4gSWYgbm9cbiAgICogc3RydWN0dXJlZF9vdXRwdXRzIGFyZSBwcmVzZW50LCB0aGUgb3V0cHV0IHdpbGwgYmUgc2luZ2xlIGB0Zi5UZW5zb3JgIGlmXG4gICAqIHRoZSBtb2RlbCBoYXMgc2luZ2xlIG91dHB1dCBub2RlLCBvdGhlcndpc2UgVGVuc29yW10uXG4gICAqXG4gICAqIEBkb2Mge2hlYWRpbmc6ICdNb2RlbHMnLCBzdWJoZWFkaW5nOiAnQ2xhc3Nlcyd9XG4gICAqL1xuICBhc3luYyBwcmVkaWN0QXN5bmMoXG4gICAgICBpbnB1dHM6IFRlbnNvcnxUZW5zb3JbXXxOYW1lZFRlbnNvck1hcCxcbiAgICAgIGNvbmZpZz86IE1vZGVsUHJlZGljdENvbmZpZyk6IFByb21pc2U8VGVuc29yfFRlbnNvcltdfE5hbWVkVGVuc29yTWFwPiB7XG4gICAgY29uc3Qgb3V0cHV0VGVuc29ycyA9IGF3YWl0IHRoaXMuZXhlY3V0ZUFzeW5jKGlucHV0cywgdGhpcy5vdXRwdXROb2Rlcyk7XG4gICAgcmV0dXJuIHRoaXMuYWRkU3RydWN0dXJlZE91dHB1dE5hbWVzKG91dHB1dFRlbnNvcnMpO1xuICB9XG5cbiAgcHJpdmF0ZSBub3JtYWxpemVJbnB1dHMoaW5wdXRzOiBUZW5zb3J8VGVuc29yW118XG4gICAgICAgICAgICAgICAgICAgICAgICAgIE5hbWVkVGVuc29yTWFwKTogTmFtZWRUZW5zb3JNYXAge1xuICAgIGlmICghKGlucHV0cyBpbnN0YW5jZW9mIFRlbnNvcikgJiYgIUFycmF5LmlzQXJyYXkoaW5wdXRzKSkge1xuICAgICAgLy8gVGhlIGlucHV0IGlzIGFscmVhZHkgYSBOYW1lZFRlbnNvck1hcC5cbiAgICAgIGNvbnN0IHNpZ25hdHVyZUlucHV0cyA9IHRoaXMuc2lnbmF0dXJlPy5pbnB1dHM7XG4gICAgICBpZiAoc2lnbmF0dXJlSW5wdXRzICE9IG51bGwpIHtcbiAgICAgICAgZm9yIChjb25zdCBpbnB1dCBpbiBzaWduYXR1cmVJbnB1dHMpIHtcbiAgICAgICAgICBjb25zdCB0ZW5zb3IgPSBzaWduYXR1cmVJbnB1dHNbaW5wdXRdO1xuICAgICAgICAgIGlmICh0ZW5zb3IucmVzb3VyY2VJZCAhPSBudWxsKSB7XG4gICAgICAgICAgICBpbnB1dHNbaW5wdXRdID0gdGhpcy5yZXNvdXJjZUlkVG9DYXB0dXJlZElucHV0W3RlbnNvci5yZXNvdXJjZUlkXTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBpbnB1dHM7XG4gICAgfVxuICAgIGlucHV0cyA9IEFycmF5LmlzQXJyYXkoaW5wdXRzKSA/IGlucHV0cyA6IFtpbnB1dHNdO1xuXG4gICAgY29uc3QgbnVtQ2FwdHVyZWRJbnB1dHMgPVxuICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnJlc291cmNlSWRUb0NhcHR1cmVkSW5wdXQpLmxlbmd0aDtcbiAgICBpZiAoaW5wdXRzLmxlbmd0aCArIG51bUNhcHR1cmVkSW5wdXRzICE9PSB0aGlzLmlucHV0Tm9kZXMubGVuZ3RoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYElucHV0IHRlbnNvciBjb3VudCBtaXNtYXRjaCwgdGhlIGdyYXBoIG1vZGVsIGhhcyAke1xuICAgICAgICAgIHRoaXMuaW5wdXROb2Rlcy5sZW5ndGggLVxuICAgICAgICAgIG51bUNhcHR1cmVkSW5wdXRzfSBub24tcmVzb3VyY2UgcGxhY2Vob2xkZXJzLCB3aGlsZSB0aGVyZSBhcmUgJHtcbiAgICAgICAgICBpbnB1dHMubGVuZ3RofSBpbnB1dCB0ZW5zb3JzIHByb3ZpZGVkLmApO1xuICAgIH1cblxuICAgIGxldCBpbnB1dEluZGV4ID0gMDtcbiAgICByZXR1cm4gdGhpcy5pbnB1dE5vZGVzLnJlZHVjZSgobWFwLCBpbnB1dE5hbWUpID0+IHtcbiAgICAgIGNvbnN0IHJlc291cmNlSWQgPSB0aGlzLnNpZ25hdHVyZT8uaW5wdXRzPy5baW5wdXROYW1lXT8ucmVzb3VyY2VJZDtcbiAgICAgIGlmIChyZXNvdXJjZUlkICE9IG51bGwpIHtcbiAgICAgICAgbWFwW2lucHV0TmFtZV0gPSB0aGlzLnJlc291cmNlSWRUb0NhcHR1cmVkSW5wdXRbcmVzb3VyY2VJZF07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBtYXBbaW5wdXROYW1lXSA9IChpbnB1dHMgYXMgVGVuc29yW10pW2lucHV0SW5kZXgrK107XG4gICAgICB9XG4gICAgICByZXR1cm4gbWFwO1xuICAgIH0sIHt9IGFzIE5hbWVkVGVuc29yTWFwKTtcbiAgfVxuXG4gIHByaXZhdGUgbm9ybWFsaXplT3V0cHV0cyhvdXRwdXRzOiBzdHJpbmd8c3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gICAgb3V0cHV0cyA9IG91dHB1dHMgfHwgdGhpcy5vdXRwdXROb2RlcztcbiAgICByZXR1cm4gIUFycmF5LmlzQXJyYXkob3V0cHV0cykgPyBbb3V0cHV0c10gOiBvdXRwdXRzO1xuICB9XG5cbiAgcHJpdmF0ZSBleGVjdXRlSW5pdGlhbGl6ZXJHcmFwaCgpIHtcbiAgICBpZiAodGhpcy5pbml0aWFsaXplciA9PSBudWxsKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICAgIGlmICh0aGlzLmluaXRpYWxpemVyU2lnbmF0dXJlID09IG51bGwpIHtcbiAgICAgIHJldHVybiB0aGlzLmluaXRpYWxpemVyLmV4ZWN1dGUoe30sIFtdKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHRoaXMuaW5pdGlhbGl6ZXIuZXhlY3V0ZShcbiAgICAgICAgICB7fSwgT2JqZWN0LmtleXModGhpcy5pbml0aWFsaXplclNpZ25hdHVyZS5vdXRwdXRzKSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBleGVjdXRlSW5pdGlhbGl6ZXJHcmFwaEFzeW5jKCkge1xuICAgIGlmICh0aGlzLmluaXRpYWxpemVyID09IG51bGwpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaW5pdGlhbGl6ZXJTaWduYXR1cmUgPT0gbnVsbCkge1xuICAgICAgcmV0dXJuIHRoaXMuaW5pdGlhbGl6ZXIuZXhlY3V0ZUFzeW5jKHt9LCBbXSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB0aGlzLmluaXRpYWxpemVyLmV4ZWN1dGVBc3luYyhcbiAgICAgICAgICB7fSwgT2JqZWN0LmtleXModGhpcy5pbml0aWFsaXplclNpZ25hdHVyZS5vdXRwdXRzKSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzZXRSZXNvdXJjZUlkVG9DYXB0dXJlZElucHV0KG91dHB1dHM6IFRlbnNvcltdKSB7XG4gICAgdGhpcy5yZXNvdXJjZUlkVG9DYXB0dXJlZElucHV0ID0ge307XG5cbiAgICBpZiAodGhpcy5pbml0aWFsaXplclNpZ25hdHVyZSkge1xuICAgICAgY29uc3Qgc2lnbmF0dXJlT3V0cHV0cyA9IHRoaXMuaW5pdGlhbGl6ZXJTaWduYXR1cmUub3V0cHV0cztcbiAgICAgIGNvbnN0IG91dHB1dE5hbWVzID0gT2JqZWN0LmtleXMoc2lnbmF0dXJlT3V0cHV0cyk7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG91dHB1dE5hbWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IG91dHB1dE5hbWUgPSBvdXRwdXROYW1lc1tpXTtcbiAgICAgICAgY29uc3QgdGVuc29ySW5mbyA9IHNpZ25hdHVyZU91dHB1dHNbb3V0cHV0TmFtZV07XG4gICAgICAgIHRoaXMucmVzb3VyY2VJZFRvQ2FwdHVyZWRJbnB1dFt0ZW5zb3JJbmZvLnJlc291cmNlSWRdID0gb3V0cHV0c1tpXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZXMgaW5mZXJlbmNlIGZvciB0aGUgbW9kZWwgZm9yIGdpdmVuIGlucHV0IHRlbnNvcnMuXG4gICAqIEBwYXJhbSBpbnB1dHMgdGVuc29yLCB0ZW5zb3IgYXJyYXkgb3IgdGVuc29yIG1hcCBvZiB0aGUgaW5wdXRzIGZvciB0aGVcbiAgICogbW9kZWwsIGtleWVkIGJ5IHRoZSBpbnB1dCBub2RlIG5hbWVzLlxuICAgKiBAcGFyYW0gb3V0cHV0cyBvdXRwdXQgbm9kZSBuYW1lIGZyb20gdGhlIFRlbnNvckZsb3cgbW9kZWwsIGlmIG5vXG4gICAqIG91dHB1dHMgYXJlIHNwZWNpZmllZCwgdGhlIGRlZmF1bHQgb3V0cHV0cyBvZiB0aGUgbW9kZWwgd291bGQgYmUgdXNlZC5cbiAgICogWW91IGNhbiBpbnNwZWN0IGludGVybWVkaWF0ZSBub2RlcyBvZiB0aGUgbW9kZWwgYnkgYWRkaW5nIHRoZW0gdG8gdGhlXG4gICAqIG91dHB1dHMgYXJyYXkuXG4gICAqXG4gICAqIEByZXR1cm5zIEEgc2luZ2xlIHRlbnNvciBpZiBwcm92aWRlZCB3aXRoIGEgc2luZ2xlIG91dHB1dCBvciBubyBvdXRwdXRzXG4gICAqIGFyZSBwcm92aWRlZCBhbmQgdGhlcmUgaXMgb25seSBvbmUgZGVmYXVsdCBvdXRwdXQsIG90aGVyd2lzZSByZXR1cm4gYVxuICAgKiB0ZW5zb3IgYXJyYXkuIFRoZSBvcmRlciBvZiB0aGUgdGVuc29yIGFycmF5IGlzIHRoZSBzYW1lIGFzIHRoZSBvdXRwdXRzXG4gICAqIGlmIHByb3ZpZGVkLCBvdGhlcndpc2UgdGhlIG9yZGVyIG9mIG91dHB1dE5vZGVzIGF0dHJpYnV0ZSBvZiB0aGUgbW9kZWwuXG4gICAqXG4gICAqIEBkb2Mge2hlYWRpbmc6ICdNb2RlbHMnLCBzdWJoZWFkaW5nOiAnQ2xhc3Nlcyd9XG4gICAqL1xuICBleGVjdXRlKGlucHV0czogVGVuc29yfFRlbnNvcltdfE5hbWVkVGVuc29yTWFwLCBvdXRwdXRzPzogc3RyaW5nfHN0cmluZ1tdKTpcbiAgICAgIFRlbnNvcnxUZW5zb3JbXSB7XG4gICAgaWYgKHRoaXMucmVzb3VyY2VJZFRvQ2FwdHVyZWRJbnB1dCA9PSBudWxsKSB7XG4gICAgICB0aGlzLnNldFJlc291cmNlSWRUb0NhcHR1cmVkSW5wdXQodGhpcy5leGVjdXRlSW5pdGlhbGl6ZXJHcmFwaCgpKTtcbiAgICB9XG4gICAgaW5wdXRzID0gdGhpcy5ub3JtYWxpemVJbnB1dHMoaW5wdXRzKTtcbiAgICBvdXRwdXRzID0gdGhpcy5ub3JtYWxpemVPdXRwdXRzKG91dHB1dHMpO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuZXhlY3V0b3IuZXhlY3V0ZShpbnB1dHMsIG91dHB1dHMpO1xuICAgIHJldHVybiByZXN1bHQubGVuZ3RoID4gMSA/IHJlc3VsdCA6IHJlc3VsdFswXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFeGVjdXRlcyBpbmZlcmVuY2UgZm9yIHRoZSBtb2RlbCBmb3IgZ2l2ZW4gaW5wdXQgdGVuc29ycyBpbiBhc3luY1xuICAgKiBmYXNoaW9uLCB1c2UgdGhpcyBtZXRob2Qgd2hlbiB5b3VyIG1vZGVsIGNvbnRhaW5zIGNvbnRyb2wgZmxvdyBvcHMuXG4gICAqIEBwYXJhbSBpbnB1dHMgdGVuc29yLCB0ZW5zb3IgYXJyYXkgb3IgdGVuc29yIG1hcCBvZiB0aGUgaW5wdXRzIGZvciB0aGVcbiAgICogbW9kZWwsIGtleWVkIGJ5IHRoZSBpbnB1dCBub2RlIG5hbWVzLlxuICAgKiBAcGFyYW0gb3V0cHV0cyBvdXRwdXQgbm9kZSBuYW1lIGZyb20gdGhlIFRlbnNvckZsb3cgbW9kZWwsIGlmIG5vIG91dHB1dHNcbiAgICogYXJlIHNwZWNpZmllZCwgdGhlIGRlZmF1bHQgb3V0cHV0cyBvZiB0aGUgbW9kZWwgd291bGQgYmUgdXNlZC4gWW91IGNhblxuICAgKiBpbnNwZWN0IGludGVybWVkaWF0ZSBub2RlcyBvZiB0aGUgbW9kZWwgYnkgYWRkaW5nIHRoZW0gdG8gdGhlIG91dHB1dHNcbiAgICogYXJyYXkuXG4gICAqXG4gICAqIEByZXR1cm5zIEEgUHJvbWlzZSBvZiBzaW5nbGUgdGVuc29yIGlmIHByb3ZpZGVkIHdpdGggYSBzaW5nbGUgb3V0cHV0IG9yXG4gICAqIG5vIG91dHB1dHMgYXJlIHByb3ZpZGVkIGFuZCB0aGVyZSBpcyBvbmx5IG9uZSBkZWZhdWx0IG91dHB1dCwgb3RoZXJ3aXNlXG4gICAqIHJldHVybiBhIHRlbnNvciBtYXAuXG4gICAqXG4gICAqIEBkb2Mge2hlYWRpbmc6ICdNb2RlbHMnLCBzdWJoZWFkaW5nOiAnQ2xhc3Nlcyd9XG4gICAqL1xuICBhc3luYyBleGVjdXRlQXN5bmMoXG4gICAgICBpbnB1dHM6IFRlbnNvcnxUZW5zb3JbXXxOYW1lZFRlbnNvck1hcCxcbiAgICAgIG91dHB1dHM/OiBzdHJpbmd8c3RyaW5nW10pOiBQcm9taXNlPFRlbnNvcnxUZW5zb3JbXT4ge1xuICAgIGlmICh0aGlzLnJlc291cmNlSWRUb0NhcHR1cmVkSW5wdXQgPT0gbnVsbCkge1xuICAgICAgdGhpcy5zZXRSZXNvdXJjZUlkVG9DYXB0dXJlZElucHV0KFxuICAgICAgICAgIGF3YWl0IHRoaXMuZXhlY3V0ZUluaXRpYWxpemVyR3JhcGhBc3luYygpKTtcbiAgICB9XG4gICAgaW5wdXRzID0gdGhpcy5ub3JtYWxpemVJbnB1dHMoaW5wdXRzKTtcbiAgICBvdXRwdXRzID0gdGhpcy5ub3JtYWxpemVPdXRwdXRzKG91dHB1dHMpO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuZXhlY3V0b3IuZXhlY3V0ZUFzeW5jKGlucHV0cywgb3V0cHV0cyk7XG4gICAgcmV0dXJuIHJlc3VsdC5sZW5ndGggPiAxID8gcmVzdWx0IDogcmVzdWx0WzBdO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBpbnRlcm1lZGlhdGUgdGVuc29ycyBmb3IgbW9kZWwgZGVidWdnaW5nIG1vZGUgKGZsYWdcbiAgICogS0VFUF9JTlRFUk1FRElBVEVfVEVOU09SUyBpcyB0cnVlKS5cbiAgICpcbiAgICogQGRvYyB7aGVhZGluZzogJ01vZGVscycsIHN1YmhlYWRpbmc6ICdDbGFzc2VzJ31cbiAgICovXG4gIGdldEludGVybWVkaWF0ZVRlbnNvcnMoKTogTmFtZWRUZW5zb3JzTWFwIHtcbiAgICByZXR1cm4gdGhpcy5leGVjdXRvci5nZXRJbnRlcm1lZGlhdGVUZW5zb3JzKCk7XG4gIH1cblxuICAvKipcbiAgICogRGlzcG9zZSBpbnRlcm1lZGlhdGUgdGVuc29ycyBmb3IgbW9kZWwgZGVidWdnaW5nIG1vZGUgKGZsYWdcbiAgICogS0VFUF9JTlRFUk1FRElBVEVfVEVOU09SUyBpcyB0cnVlKS5cbiAgICpcbiAgICogQGRvYyB7aGVhZGluZzogJ01vZGVscycsIHN1YmhlYWRpbmc6ICdDbGFzc2VzJ31cbiAgICovXG4gIGRpc3Bvc2VJbnRlcm1lZGlhdGVUZW5zb3JzKCkge1xuICAgIHRoaXMuZXhlY3V0b3IuZGlzcG9zZUludGVybWVkaWF0ZVRlbnNvcnMoKTtcbiAgfVxuXG4gIHByaXZhdGUgY29udmVydFRlbnNvck1hcFRvVGVuc29yc01hcChtYXA6IE5hbWVkVGVuc29yTWFwKTogTmFtZWRUZW5zb3JzTWFwIHtcbiAgICByZXR1cm4gT2JqZWN0LmtleXMobWFwKS5yZWR1Y2UoKG5ld01hcDogTmFtZWRUZW5zb3JzTWFwLCBrZXkpID0+IHtcbiAgICAgIG5ld01hcFtrZXldID0gW21hcFtrZXldXTtcbiAgICAgIHJldHVybiBuZXdNYXA7XG4gICAgfSwge30pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbGVhc2VzIHRoZSBtZW1vcnkgdXNlZCBieSB0aGUgd2VpZ2h0IHRlbnNvcnMgYW5kIHJlc291cmNlTWFuYWdlci5cbiAgICpcbiAgICogQGRvYyB7aGVhZGluZzogJ01vZGVscycsIHN1YmhlYWRpbmc6ICdDbGFzc2VzJ31cbiAgICovXG4gIGRpc3Bvc2UoKSB7XG4gICAgdGhpcy5leGVjdXRvci5kaXNwb3NlKCk7XG5cbiAgICBpZiAodGhpcy5pbml0aWFsaXplcikge1xuICAgICAgdGhpcy5pbml0aWFsaXplci5kaXNwb3NlKCk7XG4gICAgICBpZiAodGhpcy5yZXNvdXJjZUlkVG9DYXB0dXJlZElucHV0KSB7XG4gICAgICAgIGRpc3Bvc2UodGhpcy5yZXNvdXJjZUlkVG9DYXB0dXJlZElucHV0KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLnJlc291cmNlTWFuYWdlci5kaXNwb3NlKCk7XG4gIH1cbn1cblxuLyoqXG4gKiBMb2FkIGEgZ3JhcGggbW9kZWwgZ2l2ZW4gYSBVUkwgdG8gdGhlIG1vZGVsIGRlZmluaXRpb24uXG4gKlxuICogRXhhbXBsZSBvZiBsb2FkaW5nIE1vYmlsZU5ldFYyIGZyb20gYSBVUkwgYW5kIG1ha2luZyBhIHByZWRpY3Rpb24gd2l0aCBhXG4gKiB6ZXJvcyBpbnB1dDpcbiAqXG4gKiBgYGBqc1xuICogY29uc3QgbW9kZWxVcmwgPVxuICogICAgJ2h0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS90ZmpzLW1vZGVscy9zYXZlZG1vZGVsL21vYmlsZW5ldF92Ml8xLjBfMjI0L21vZGVsLmpzb24nO1xuICogY29uc3QgbW9kZWwgPSBhd2FpdCB0Zi5sb2FkR3JhcGhNb2RlbChtb2RlbFVybCk7XG4gKiBjb25zdCB6ZXJvcyA9IHRmLnplcm9zKFsxLCAyMjQsIDIyNCwgM10pO1xuICogbW9kZWwucHJlZGljdCh6ZXJvcykucHJpbnQoKTtcbiAqIGBgYFxuICpcbiAqIEV4YW1wbGUgb2YgbG9hZGluZyBNb2JpbGVOZXRWMiBmcm9tIGEgVEYgSHViIFVSTCBhbmQgbWFraW5nIGEgcHJlZGljdGlvblxuICogd2l0aCBhIHplcm9zIGlucHV0OlxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCBtb2RlbFVybCA9XG4gKiAgICAnaHR0cHM6Ly90Zmh1Yi5kZXYvZ29vZ2xlL2ltYWdlbmV0L21vYmlsZW5ldF92Ml8xNDBfMjI0L2NsYXNzaWZpY2F0aW9uLzInO1xuICogY29uc3QgbW9kZWwgPSBhd2FpdCB0Zi5sb2FkR3JhcGhNb2RlbChtb2RlbFVybCwge2Zyb21URkh1YjogdHJ1ZX0pO1xuICogY29uc3QgemVyb3MgPSB0Zi56ZXJvcyhbMSwgMjI0LCAyMjQsIDNdKTtcbiAqIG1vZGVsLnByZWRpY3QoemVyb3MpLnByaW50KCk7XG4gKiBgYGBcbiAqIEBwYXJhbSBtb2RlbFVybCBUaGUgdXJsIG9yIGFuIGBpby5JT0hhbmRsZXJgIHRoYXQgbG9hZHMgdGhlIG1vZGVsLlxuICogQHBhcmFtIG9wdGlvbnMgT3B0aW9ucyBmb3IgdGhlIEhUVFAgcmVxdWVzdCwgd2hpY2ggYWxsb3dzIHRvIHNlbmRcbiAqICAgICBjcmVkZW50aWFsc1xuICogICAgYW5kIGN1c3RvbSBoZWFkZXJzLlxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdNb2RlbHMnLCBzdWJoZWFkaW5nOiAnTG9hZGluZyd9XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkR3JhcGhNb2RlbChcbiAgICBtb2RlbFVybDogc3RyaW5nfGlvLklPSGFuZGxlciwgb3B0aW9uczogaW8uTG9hZE9wdGlvbnMgPSB7fSxcbiAgICB0ZmlvID0gaW8pOiBQcm9taXNlPEdyYXBoTW9kZWw+IHtcbiAgaWYgKG1vZGVsVXJsID09IG51bGwpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdtb2RlbFVybCBpbiBsb2FkR3JhcGhNb2RlbCgpIGNhbm5vdCBiZSBudWxsLiBQbGVhc2UgcHJvdmlkZSBhIHVybCAnICtcbiAgICAgICAgJ29yIGFuIElPSGFuZGxlciB0aGF0IGxvYWRzIHRoZSBtb2RlbCcpO1xuICB9XG4gIGlmIChvcHRpb25zID09IG51bGwpIHtcbiAgICBvcHRpb25zID0ge307XG4gIH1cblxuICBpZiAob3B0aW9ucy5mcm9tVEZIdWIgJiYgdHlwZW9mIG1vZGVsVXJsID09PSAnc3RyaW5nJykge1xuICAgIG1vZGVsVXJsID0gZ2V0VEZIdWJVcmwobW9kZWxVcmwpO1xuICB9XG4gIGNvbnN0IG1vZGVsID0gbmV3IEdyYXBoTW9kZWwobW9kZWxVcmwsIG9wdGlvbnMsIHRmaW8pO1xuICBhd2FpdCBtb2RlbC5sb2FkKCk7XG4gIHJldHVybiBtb2RlbDtcbn1cblxuLyoqXG4gKiBMb2FkIGEgZ3JhcGggbW9kZWwgZ2l2ZW4gYSBzeW5jaHJvbm91cyBJTyBoYW5kbGVyIHdpdGggYSAnbG9hZCcgbWV0aG9kLlxuICpcbiAqIEBwYXJhbSBtb2RlbFNvdXJjZSBUaGUgYGlvLklPSGFuZGxlclN5bmNgIHRoYXQgbG9hZHMgdGhlIG1vZGVsLCBvciB0aGVcbiAqICAgICBgaW8uTW9kZWxBcnRpZmFjdHNgIHRoYXQgZW5jb2RlIHRoZSBtb2RlbCwgb3IgYSB0dXBsZSBvZlxuICogICAgIGBbaW8uTW9kZWxKU09OLCBBcnJheUJ1ZmZlcl1gIG9mIHdoaWNoIHRoZSBmaXJzdCBlbGVtZW50IGVuY29kZXMgdGhlXG4gKiAgICAgIG1vZGVsIGFuZCB0aGUgc2Vjb25kIGNvbnRhaW5zIHRoZSB3ZWlnaHRzLlxuICpcbiAqIEBkb2Mge2hlYWRpbmc6ICdNb2RlbHMnLCBzdWJoZWFkaW5nOiAnTG9hZGluZyd9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkR3JhcGhNb2RlbFN5bmMoXG4gICAgbW9kZWxTb3VyY2U6IGlvLklPSGFuZGxlclN5bmN8XG4gICAgaW8uTW9kZWxBcnRpZmFjdHN8W2lvLk1vZGVsSlNPTiwgLyogV2VpZ2h0cyAqLyBBcnJheUJ1ZmZlcl0pOlxuICAgIEdyYXBoTW9kZWw8aW8uSU9IYW5kbGVyU3luYz4ge1xuICBpZiAobW9kZWxTb3VyY2UgPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ21vZGVsVXJsIGluIGxvYWRHcmFwaE1vZGVsU3luYygpIGNhbm5vdCBiZSBudWxsLiBQbGVhc2UgcHJvdmlkZSAnICtcbiAgICAgICAgJ21vZGVsIGFydGlmYWN0cyBvciBhbiBJT0hhbmRsZXIgdGhhdCBsb2FkcyB0aGUgbW9kZWwnKTtcbiAgfVxuXG4gIGxldCBpb0hhbmRsZXI6IGlvLklPSGFuZGxlclN5bmM7XG4gIGlmIChtb2RlbFNvdXJjZSBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgY29uc3QgW21vZGVsSlNPTiwgd2VpZ2h0c10gPSBtb2RlbFNvdXJjZTtcbiAgICBpZiAoIW1vZGVsSlNPTikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdtb2RlbEpTT04gbXVzdCBiZSB0aGUgZmlyc3QgZWxlbWVudCBvZiB0aGUgYXJyYXknKTtcbiAgICB9XG4gICAgaWYgKCF3ZWlnaHRzIHx8ICEod2VpZ2h0cyBpbnN0YW5jZW9mIEFycmF5QnVmZmVyKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdBbiBBcnJheUJ1ZmZlciBvZiB3ZWlnaHRzIG11c3QgYmUgdGhlIHNlY29uZCBlbGVtZW50IG9mJyArXG4gICAgICAgICAgJyB0aGUgYXJyYXknKTtcbiAgICB9XG4gICAgaWYgKCEoJ21vZGVsVG9wb2xvZ3knIGluIG1vZGVsSlNPTikpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignTW9kZWwgSlNPTiBpcyBtaXNzaW5nIFxcJ21vZGVsVG9wb2xvZ3lcXCcnKTtcbiAgICB9XG4gICAgaWYgKCEoJ3dlaWdodHNNYW5pZmVzdCcgaW4gbW9kZWxKU09OKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2RlbCBKU09OIGlzIG1pc3NpbmcgXFwnd2VpZ2h0c01hbmlmZXN0XFwnJyk7XG4gICAgfVxuXG4gICAgY29uc3Qgd2VpZ2h0U3BlY3MgPSBpby5nZXRXZWlnaHRTcGVjcyhtb2RlbEpTT04ud2VpZ2h0c01hbmlmZXN0KTtcbiAgICBjb25zdCBtb2RlbEFydGlmYWN0cyA9XG4gICAgICAgIGlvLmdldE1vZGVsQXJ0aWZhY3RzRm9ySlNPTlN5bmMobW9kZWxKU09OLCB3ZWlnaHRTcGVjcywgd2VpZ2h0cyk7XG4gICAgaW9IYW5kbGVyID0gaW8uZnJvbU1lbW9yeVN5bmMobW9kZWxBcnRpZmFjdHMpO1xuICB9IGVsc2UgaWYgKCdsb2FkJyBpbiBtb2RlbFNvdXJjZSkge1xuICAgIC8vIFRoZW4gbW9kZWxTb3VyY2UgaXMgYWxyZWFkeSBhbiBJT0hhbmRsZXJTeW5jLlxuICAgIGlvSGFuZGxlciA9IG1vZGVsU291cmNlO1xuICB9IGVsc2UgaWYgKFxuICAgICAgJ21vZGVsVG9wb2xvZ3knIGluIG1vZGVsU291cmNlICYmICd3ZWlnaHRTcGVjcycgaW4gbW9kZWxTb3VyY2UgJiZcbiAgICAgICd3ZWlnaHREYXRhJyBpbiBtb2RlbFNvdXJjZSkge1xuICAgIC8vIG1vZGVsU291cmNlIGlzIG9mIHR5cGUgTW9kZWxBcnRpZmFjdHMuXG4gICAgaW9IYW5kbGVyID0gaW8uZnJvbU1lbW9yeVN5bmMobW9kZWxTb3VyY2UpO1xuICB9IGVsc2Uge1xuICAgIHRocm93IG5ldyBFcnJvcignVW5rbm93biBtb2RlbCBmb3JtYXQnKTtcbiAgfVxuXG4gIGNvbnN0IG1vZGVsID0gbmV3IEdyYXBoTW9kZWwoaW9IYW5kbGVyKTtcbiAgbW9kZWwubG9hZCgpO1xuICByZXR1cm4gbW9kZWw7XG59XG5cbmZ1bmN0aW9uIGdldFRGSHViVXJsKG1vZGVsVXJsOiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoIW1vZGVsVXJsLmVuZHNXaXRoKCcvJykpIHtcbiAgICBtb2RlbFVybCA9IChtb2RlbFVybCkgKyAnLyc7XG4gIH1cbiAgcmV0dXJuIGAke21vZGVsVXJsfSR7REVGQVVMVF9NT0RFTF9OQU1FfSR7VEZIVUJfU0VBUkNIX1BBUkFNfWA7XG59XG4iXX0=
|