foop
Version:
interfaces that describe their intentions.
896 lines (796 loc) • 60.7 kB
JavaScript
// conditionals
/* eslint complexity: "OFF" */
// inlined rollup
/* eslint import/max-dependencies: "OFF" */
// one file
/* eslint max-lines: "OFF" */
// debug conditionals
/* eslint max-depth: "OFF" */
var isEmpty = require('./is/empty')
var isTrue = require('./is/true')
var isIteratable = require('./is/iteratable')
var isUndefined = require('./is/undefined')
var isArray = require('./is/array')
var isMap = require('./is/map')
var isSet = require('./is/set')
var isObj = require('./is/obj')
var isPrimitive = require('./is/primitive')
var isNull = require('./is/null')
var ObjectKeys = require('./util/keys')
var reduce = require('./reduce')
var toarr = require('./to-arr')
var dotSet = require('./dot/set')
var emptyTarget = require('./dopemerge/emptyTarget')
var copy = require('./traversers/copy')
var eq = require('./traversers/_eq')
var addPoolingTo = require('./cache/pooler')
// const props = require('./util/props')
// const ENV_DEBUG = require('./env/debug')
// const ENV_DEBUG = true
// const TRUTH = true
var ENV_DEBUG = false
/**
* {@link https://github.com/wmira/object-traverse/blob/master/index.js object-traverse}
* {@link https://www.npmjs.com/browse/keyword/traverse traverse-js}
* {@link https://www.npmjs.com/package/tree-walk tree-walk}
* {@link https://www.npmjs.com/package/1tree 1tree}
* {@link https://www.npmjs.com/package/pathway pathway}
* {@link https://www.npmjs.com/package/@mojule/tree tree}
* {@link http://web.archive.org/web/20160930054101/http://substack.net/tree_traversal tree-traversal-article}
* {@link https://medium.com/@alexanderv/tries-javascript-simple-implementation-e2a4e54e4330 js-trie-medium}
* --------------------
*
* if needed, clone
*
* first things to check are number/string/boolean/null/undefined
*
* then check non-iteratables
* symbol, promise,
*
* then check conversions
* - map, set
*
* then check empties
* - obj
* - fn
*
* -------
*
* numbers f-or first/last
* and as a sort of hash like
* 1 + 2 + 4 = ISLEAF & ISROOT ?
*
* Array
*
* Object Function Date Error Map Set
*
* String
* Number NaN Infinity
* Boolean
*
*
* null undefined
*
* Promise Symbol
*
* ----
*
* @emits before
* @emits pre
* @emits post
* @emits after
*/
/**
* @desc Traverse class, pooled
* @modifies this.node
* @modifies this.parent
* @modifies this.root
* @since 5.0.0
*
* @member Traverse
* @class
* @constructor
* @alias IterAteOr
* @extends pooler
*
* @param {Traversable} iteratee value to iterate, clone, copy, check for eq
* @param {Object | undefined} [config] wip config for things such as events or configs
*
* @see {@link tree-traversal-article}
* @see traverse
* @TODO make this a trie OR a linked-list
*
* @tests traverse
* @types traverse
*
* @example
*
* new Traverse([1])
* new Traverse([], {})
*
*/
function Traverse(iteratee, config) {
// always cleared when done anyway
if (isUndefined(this.parents)) { this.parents = new Set() }
this.node = iteratee
this.parent = iteratee
this.root = iteratee
this.reset()
// to pass in the events (as commented below) without messing up scope?
// if (config.on) this.on = config.on
return this
}
/**
* @desc reset the properties when finished pooling or instantiating
* @since 5.0.0
* @method
*
* @memberOf Traverse
* @modifies Traverse.path
* @modifies Traverse.key
* @modifies Traverse.isAlive
* @modifies Traverse.isCircular
* @modifies Traverse.isLeaf
* @modifies Traverse.isRoot
* @modifies Traverse.depth
* @return {void}
*
* @example
* traverse([]).reset()
*/
Traverse.prototype.reset = function() {
this.path = []
this.key = undefined
this.isAlive = true
this.isCircular = false
this.isLeaf = false
this.isRoot = true
// iterates +1 so start at 0
this.depth = -1
}
/**
* @desc find parent,
* is there a parent
* above the current depth
* with the same value,
* making it circular?
*
* @memberOf Traverse
* @since 5.0.0
* @private
* @method
*
* @param {number} depth current depth, to find parent >=
* @param {parent} value parent value to find
* @return {boolean} hasParent
*
* @example
*
* var obj = {eh: ['eh']}
* traverse(obj).addParent(0, obj)
*
*/
Traverse.prototype.hasParent = function(depth, value) {
// or array
return isObj(value) ? this.parents.has(value) : false
}
/**
* @desc add parent, to prevent circular iterations
* @memberOf Traverse
* @since 5.0.0
* @private
* @method
*
* @param {number} depth current depth, to add parent to >=
* @param {parent} value parent value to add
* @return {void}
*
* @example
*
* var obj = {eh: ['eh']}
* traverse(obj).addParent(0, obj)
*
*/
Traverse.prototype.addParent = function(depth, value) {
// && this.hasParent(value) === false
if (isObj(value)) { this.parents.add(value) }
}
/**
* @desc remove all parents, reset the map
*
* @memberOf Traverse
* @since 5.0.0
* @private
* @method
*
* @return {void}
*
* @example
*
* var obj = {eh: ['eh']}
* traverse(obj).forEach((key, value, t) => {
* t.parents
* //=> Set([obj])
* t.clear()
* t.parents
* //=> Set[]
* })
*
*/
Traverse.prototype.clear = function() {
// if (!isUndefined(this.parents))
this.parents.clear()
}
/**
* @memberOf Traverse
* @since 5.0.0
* @private
* @method
*
* @param {number} depth current depth, to find parents >=
* @param {parent} value parent value to remove
* @return {void}
*
* @example
*
* var obj = {eh: ['eh']}
* traverse(obj).removeParent(0, obj)
*
*/
Traverse.prototype.removeParent = function(depth, value) {
this.parents.delete(value)
}
/**
* @desc this is the main usage of Traverse
* @memberOf Traverse
* @since 3.0.0
* @version 5.0.0
* @method
*
* @param {Function} cb callback for each iteration
* @return {*} mapped result or original value, depends how it is used
*
* @example
*
* traverse([1, 2, 3]).forEach((key, value) => console.log({[key]: value}))
* //=> {'0': 1}
* //=> {'1': 2}
* //=> {'2': 3}
*
*/
Traverse.prototype.forEach = function iterateForEach(cb) {
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
console.log('\n forEach \n')
}
var result = this.iterate(cb)
// TODO: HERE, WHEN THIS IS ADDED, CAN BREAK SOME TESTS? SCOPED PARENTS MAP?
Traverse.release(this)
return result
}
/**
* @desc stop the iteration
* @modifies this.isAlive = false
* @memberOf Traverse
* @method
*
* @return {void}
*
* @example
*
* traverse({eh: true, arr: []}).forEach((key, val, t) => {
* if (isArray(val)) this.stop()
* })
*
*/
Traverse.prototype.stop = function stop() {
this.isAlive = false
// this.release()
}
/**
* @TODO skip 1 branch
* @version 5.0.0
* @since 3.0.0
* @memberOf Traverse
* @method
*
* @return {void}
*
* @example
*
* traverse([1, 2, 3, [4]]).forEach((key, val, t) => {
* if (isArray(val)) t.skip()
* })
*
*/
Traverse.prototype.skip = function skip() {
this.skipBranch = true
}
/* prettier-ignore */
/**
* @desc checks whether a node is iteratable
* @modifies Traverse.isIteratable
* @modifies Traverse.isLeaf
* @modifies Traverse.isCircular
*
* @memberOf Traverse
* @protected
* @method
*
* @param {*} node value to check
* @return {void}
*
* @TODO move into the wrapper? if perf allows?
*
* @example
*
* .checkIteratable({eh: true})
* //=> this.isLeaf = false
* //=> this.isCircular = false
* //=> this.isIteratable = true
*
* .checkIteratable({} || [])
* //=> this.isLeaf = true
* //=> this.isCircular = false
* //=> this.isIteratable = false
*
* var circular = {}
* circular.circular = circular
* .checkIteratable(circular)
* //=> this.isLeaf = false
* //=> this.isCircular = true
* //=> this.isIteratable = true
*
*/
Traverse.prototype.checkIteratable = function check(node) {
this.isIteratable = isIteratable(node)
// just put these as an array?
if (isTrue(this.isIteratable)) {
// native = leaf if not root
this.isLeaf = false
var path = this.path.join('.')
if (this.hasParent(path, node)) {
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
console.log('circular___________', {node: node, path: this.path})
}
this.isCircular = true
}
else {
this.addParent(path, node)
this.isCircular = false
}
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
// console.log('IS_CIRCULAR_JSON', isCircular(node), this.isCircular, node)
}
}
else {
this.isLeaf = true
this.isCircular = false
}
}
/* prettier-ignore */
/**
* Remove the current element from the output.
* If the node is in an Array it will be spliced off.
* Otherwise it will be deleted from its parent.
*
* @memberOf Traverse
* @version 5.0.0
* @since 2.0.0
* @method
*
* @param {undefined | Object} [arg] optional obj to use, defaults to this.node
* @return {void}
*
* @example
*
* traverse([0]).forEach((key, val, it) => it.remove())
* //=> []
*
* traverse({eh: true}).forEach((key, val, it) => it.remove())
* //=> {}
*
* traverse({eh: true, str: 'stringy'}).forEach((key, val, it) => {
* if (!isString(val)) it.remove()
* })
* //=> {str: 'stringy'}
*
*/
Traverse.prototype.remove = function removes(arg) {
// ignore undefined & non-object/arrays
if (isUndefined(this.key)) { return }
var obj = arg || this.node
if (!isObj(obj)) { return }
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
console.log('remove')
console.log({parent: this.parent, key: this.key, obj: obj})
}
this.removeParent(obj)
this.skip()
delete obj[this.key]
delete this.parent[this.key]
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
console.log('traverse:remove:', this.key, {obj: obj, iteratee: this.node})
}
}
/**
* @desc update the value for the current key
* @version 5.0.0
* @since 2.0.0
* @memberOf Traverse
*
* @param {*} value this.node[this.key] = value
* @return {void}
*
* @example
*
* traverse({eh: true})
* .forEach((key, val, traverser) => {
* if (this.isRoot) return
* traverser.update(false)
* })
* //=> {eh: false}
*
*/
Traverse.prototype.update = function update(value) {
dotSet(this.root, this.path, value)
}
/**
* @desc mark the iteration as done, clear the map
* @NOTE this recycles the instance in the pooler to re-use allocated objects
* @memberOf Traverse
* @private
* @since 5.0.0
*
* @return {void}
*
* @see Traverse.iterate
*
* @example
*
* traverse([]).destructor()
*
*/
Traverse.prototype.destructor = function destructor() {
this.node = undefined
this.parent = undefined
this.reset()
this.clear()
}
/* prettier-ignore */
/**
* @TODO handler for Set & Map so they can be skipped or traversed, for example when cloning...
* @TODO add hook to add custom checking if isIteratable
* @TODO deal with .isRoot if needed
* @TODO examples with clone and stop
*
* @memberOf Traverse
* @protected
* @sig on(key: null | Primitive, val: any, instance: Traverse): any
*
* @param {Function} on callback fn for each iteration
* @return {*} this.node
*
* @example
*
* iterate([])
* //=> []
* //=> on(null, [])
*
* @example
*
* iterate([1])
* //=> [1]
* //=> on(null, [1])
* //=> on('1', 1)
*
* @example
*
* //primitive - same for any number, string, symbol, null, undefined
* iterate(Symbol('eh'))
* //=> Symbol('eh')
* //=> on(Symbol('eh'))
*
* @example
*
* var deeper = {eh: 'canada', arr: [{moose: true}, 0]}
* iterate(deeper)
* //=> deeper // returns
* //=> on(null, deeper, this) // root
*
* //=> on('eh', 'canada', this) // 1st branch
*
* //=> on('arr', [{moose: true}, 0], this)
* //=> on('arr.0', [{moose: true}], this)
* //=> on('arr.0.moose', true, this)
* //=> on('arr.1', [0], this)
*
*
*/
Traverse.prototype.iterate = function iterate(on) {
var this$1 = this;
/* istanbul ignore next : dev */
if (ENV_DEBUG) {
// require('fliplog')
// .bold(this.path.join('.'))
// .data(parents.keys())
// .echo()
console.log('\n...iterate...\n')
}
if (this.isAlive === false) {
/* istanbul ignore next : dev */
if (ENV_DEBUG) {
console.log('DEAD')
}
return Traverse.release(this)
}
var node = this.node
// convert to iteratable
if (isMap(node)) {
node = reduce(node)
}
else if (isSet(node)) {
node = toarr(node)
}
// @TODO: maybe only right before sub-loop
this.addParent(this.depth, node)
var nodeIsArray = isArray(node)
var nodeIsObj = nodeIsArray || isObj(node)
// ---
// @event
if (!isUndefined(this.onBefore)) {
// eslint-disable-next-line no-useless-call
this.onBefore(this)
}
/* istanbul ignore next : dev */
if (ENV_DEBUG) {
// const str = require('pretty-format')({nodeIsObj, nodeIsArray, node})
// require('fliplog').verbose(1).data({nodeIsObj, nodeIsArray, node}).echo()
// console.log(node, parents)
// console.log(str)
console.log({nodeIsObj: nodeIsObj, nodeIsArray: nodeIsArray, node: node})
}
/**
* call as root, helpful when we
* - iterate something with no keys
* - iterate a non-iteratable (symbol, error, native, promise, etc)
*/
if (isTrue(this.isRoot)) {
on.call(this, null, node, this)
this.isRoot = false
}
var isObjOrArray = nodeIsArray || nodeIsObj
// if (isObjOrArray) {
// // @event
// if (!isUndefined(this.onBefore)) {
// // eslint-disable-next-line no-useless-call
// this.onBefore(this)
// }
// }
// --------------------
// IF OBJWITHOUTKEYS, IF ARRAY WITHOUT LENGTH...
if (isObjOrArray && isEmpty(node)) {
on.call(this, this.key, node, this)
this.node = node
}
// --------------------
else if (isObjOrArray) {
this.depth = this.path.length
// @TODO SAFETY WITH `props(node)` <- fixes Error
var keys = nodeIsArray ? node : ObjectKeys(node)
/* istanbul ignore next : dev */
if (ENV_DEBUG) {
console.log({keys: keys})
// require('fliplog').verbose(1).data(this).echo()
}
// @event
// if (!isUndefined(this.onBefore)) this.onBefore()
// @NOTE: safety here
// this.checkIteratable(node)
// const last = keys[keys.length - 1]
// @loop
for (var key = 0; key < keys.length; key++) {
// if (ENV_DEBUG)
// console.log('iterating:', {key})
// --- safety ---
if (this$1.isAlive === false) {
/* istanbul ignore next : dev */
if (ENV_DEBUG) {
console.log('DEAD')
}
return Traverse.release(this$1)
}
// @NOTE: look above add prev add parent
// addParent(this.depth, node)
// ----- setup our data ----
// to make it deletable
if (node !== this$1.node) { this$1.parent = node }
this$1.key = nodeIsArray ? key : keys[key]
// this.isLast = key === last
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
console.log('alive', this$1.key)
}
// @event
if (!isUndefined(this$1.onPre)) {
// eslint-disable-next-line no-useless-call
this$1.onPre(this$1)
}
var value = node[this$1.key]
this$1.checkIteratable(value)
// addParent(value)
var pathBeforeNesting = this$1.path.slice(0)
// @NOTE: can go forward-backwards if this is after the nested iterating
this$1.path.push(this$1.key)
this$1.depth = this$1.path.length
// ----- continue events, loop deeper when needed ----
// @NOTE since we check isAlive at the beginning of each loop
// could use .skip alongisde .stop
// @TODO @IMPORTANT @HACK @FIXME right here it should also handle the .stop
on.call(this$1, this$1.key, value, this$1)
if (isTrue(this$1.skipBranch)) {
this$1.skipBranch = false
continue
}
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
// require('fliplog').data(parents).echo()
// require('fliplog').data(this).echo()
}
// handle data
if (isTrue(this$1.isCircular)) {
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
console.log('(((circular)))', this$1.key)
}
// on.call(this, this.key, value, this)
// this.path.pop()
this$1.path = pathBeforeNesting
// this.isCircular = false
// break
continue
// return
}
// &&
if (isTrue(this$1.isIteratable)) {
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
console.log('(((iteratable)))', this$1.key)
}
this$1.node = value
this$1.iterate(on)
this$1.path = pathBeforeNesting
}
/* istanbul ignore next: dev */
if (ENV_DEBUG) {
if (this$1.isIteratable === false) {
console.log('not iteratable', this$1.key)
}
console.log('----------------- post ----------', node)
}
// @event
if (!isUndefined(this$1.onPost)) {
// eslint-disable-next-line no-useless-call
this$1.onPost(this$1)
}
// cleanup, backup 1 level
this$1.path.pop()
this$1.removeParent(node)
}
// this.path.pop()
this.depth = this.path.length
}
else {
// this.isLast = false
on.call(this, this.depth, node, this)
}
// @NOTE: careful
// removeParent(node)
// @NOTE: just for .after ?
this.node = node
// @event
if (!isUndefined(this.onAfter)) {
// eslint-disable-next-line no-useless-call
this.onAfter(this)
}
this.path.pop()
return this.node
}
// is smaller, but probably slower
// function onEvent(property) {
// return function(fn) {
// this[property] = function
// }
// }
// when it's some sort of itertable object, loop it further
// @TODO: need to handle these better without totally messing with bad scope
Traverse.prototype.pre = function(fn) {
this.onPre = fn
}
Traverse.prototype.post = function(fn) {
this.onPost = fn
}
Traverse.prototype.before = function(fn) {
this.onBefore = fn
}
Traverse.prototype.after = function(fn) {
this.onAfter = fn
}
// -----------------------
/**
* @TODO merge with dopemerge?
* @TODO needs tests converted back for this (observe tests do cover somewhat)
*
* @param {*} arg defaults to this.node
* @return {*} cloned
*
* @example
*
* var obj = {}
* var cloned = traverse().clone(obj)
* obj.eh = true
* eq(obj, cloned)
* //=> false
*
*/
Traverse.prototype.clone = clone
/**
* @todo ugh, how to clone better with *recursive* objects?
* @param {any} src wip
* @return {any} wip
*/
Traverse.prototype.copy = copy
/**
* @desc clone any value
* @version 5.0.0
* @since 4.0.0
* @memberOf Traverse
* @extends copy
* @extends Traverse
*
* @param {*} arg argument to clone
* @return {*} cloned value
*
* {@link http://underscorejs.org/#clone underscore-clone}
* @see {@link underscore-clone}
* @see dopemerge
*
* @example
*
* var obj = {eh: true}
* clone(obj) === obj //=> false
*
* var obj = {eh: true}
* var obj2 = clone(obj)
* obj.eh = false
* console.log(obj2.eh) //=> true
*
*/
function clone(arg) {
var obj = isUndefined(arg) ? this.node : arg
if (isPrimitive(obj)) { return obj }
var cloned = emptyTarget(obj)
var current = cloned
traverse(obj).forEach(function (key, value, traverser) {
// t.isRoot
if (isNull(key)) { return }
var copied = copy(value)
if (traverser.isCircular && isArray(value)) { copied = value.slice(0) }
dotSet(current, traverser.path, copied)
})
return cloned
}
// @TODO could just have traverse = Traverse.getPooled ?
addPoolingTo(Traverse)
function traverse(value) {
return Traverse.getPooled(value)
}
traverse.eq = eq(traverse)
traverse.clone = clone
traverse.copy = copy
module.exports = traverse
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhdmVyc2UuanMiLCJzb3VyY2VzIjpbInRyYXZlcnNlLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIGNvbmRpdGlvbmFsc1xuLyogZXNsaW50IGNvbXBsZXhpdHk6IFwiT0ZGXCIgKi9cblxuLy8gaW5saW5lZCByb2xsdXBcbi8qIGVzbGludCBpbXBvcnQvbWF4LWRlcGVuZGVuY2llczogXCJPRkZcIiAqL1xuXG4vLyBvbmUgZmlsZVxuLyogZXNsaW50IG1heC1saW5lczogXCJPRkZcIiAqL1xuXG4vLyBkZWJ1ZyBjb25kaXRpb25hbHNcbi8qIGVzbGludCBtYXgtZGVwdGg6IFwiT0ZGXCIgKi9cblxuY29uc3QgaXNFbXB0eSA9IHJlcXVpcmUoJy4vaXMvZW1wdHknKVxuY29uc3QgaXNUcnVlID0gcmVxdWlyZSgnLi9pcy90cnVlJylcbmNvbnN0IGlzSXRlcmF0YWJsZSA9IHJlcXVpcmUoJy4vaXMvaXRlcmF0YWJsZScpXG5jb25zdCBpc1VuZGVmaW5lZCA9IHJlcXVpcmUoJy4vaXMvdW5kZWZpbmVkJylcbmNvbnN0IGlzQXJyYXkgPSByZXF1aXJlKCcuL2lzL2FycmF5JylcbmNvbnN0IGlzTWFwID0gcmVxdWlyZSgnLi9pcy9tYXAnKVxuY29uc3QgaXNTZXQgPSByZXF1aXJlKCcuL2lzL3NldCcpXG5jb25zdCBpc09iaiA9IHJlcXVpcmUoJy4vaXMvb2JqJylcbmNvbnN0IGlzUHJpbWl0aXZlID0gcmVxdWlyZSgnLi9pcy9wcmltaXRpdmUnKVxuY29uc3QgaXNOdWxsID0gcmVxdWlyZSgnLi9pcy9udWxsJylcbmNvbnN0IE9iamVjdEtleXMgPSByZXF1aXJlKCcuL3V0aWwva2V5cycpXG5jb25zdCByZWR1Y2UgPSByZXF1aXJlKCcuL3JlZHVjZScpXG5jb25zdCB0b2FyciA9IHJlcXVpcmUoJy4vdG8tYXJyJylcbmNvbnN0IGRvdFNldCA9IHJlcXVpcmUoJy4vZG90L3NldCcpXG5jb25zdCBlbXB0eVRhcmdldCA9IHJlcXVpcmUoJy4vZG9wZW1lcmdlL2VtcHR5VGFyZ2V0JylcbmNvbnN0IGNvcHkgPSByZXF1aXJlKCcuL3RyYXZlcnNlcnMvY29weScpXG5jb25zdCBlcSA9IHJlcXVpcmUoJy4vdHJhdmVyc2Vycy9fZXEnKVxuY29uc3QgYWRkUG9vbGluZ1RvID0gcmVxdWlyZSgnLi9jYWNoZS9wb29sZXInKVxuLy8gY29uc3QgcHJvcHMgPSByZXF1aXJlKCcuL3V0aWwvcHJvcHMnKVxuXG4vLyBjb25zdCBFTlZfREVCVUcgPSByZXF1aXJlKCcuL2Vudi9kZWJ1ZycpXG4vLyBjb25zdCBFTlZfREVCVUcgPSB0cnVlXG4vLyBjb25zdCBUUlVUSCA9IHRydWVcbmNvbnN0IEVOVl9ERUJVRyA9IGZhbHNlXG5cbi8qKlxuICoge0BsaW5rIGh0dHBzOi8vZ2l0aHViLmNvbS93bWlyYS9vYmplY3QtdHJhdmVyc2UvYmxvYi9tYXN0ZXIvaW5kZXguanMgb2JqZWN0LXRyYXZlcnNlfVxuICoge0BsaW5rIGh0dHBzOi8vd3d3Lm5wbWpzLmNvbS9icm93c2Uva2V5d29yZC90cmF2ZXJzZSB0cmF2ZXJzZS1qc31cbiAqIHtAbGluayBodHRwczovL3d3dy5ucG1qcy5jb20vcGFja2FnZS90cmVlLXdhbGsgdHJlZS13YWxrfVxuICoge0BsaW5rIGh0dHBzOi8vd3d3Lm5wbWpzLmNvbS9wYWNrYWdlLzF0cmVlIDF0cmVlfVxuICoge0BsaW5rIGh0dHBzOi8vd3d3Lm5wbWpzLmNvbS9wYWNrYWdlL3BhdGh3YXkgcGF0aHdheX1cbiAqIHtAbGluayBodHRwczovL3d3dy5ucG1qcy5jb20vcGFja2FnZS9AbW9qdWxlL3RyZWUgdHJlZX1cbiAqIHtAbGluayBodHRwOi8vd2ViLmFyY2hpdmUub3JnL3dlYi8yMDE2MDkzMDA1NDEwMS9odHRwOi8vc3Vic3RhY2submV0L3RyZWVfdHJhdmVyc2FsIHRyZWUtdHJhdmVyc2FsLWFydGljbGV9XG4gKiB7QGxpbmsgaHR0cHM6Ly9tZWRpdW0uY29tL0BhbGV4YW5kZXJ2L3RyaWVzLWphdmFzY3JpcHQtc2ltcGxlLWltcGxlbWVudGF0aW9uLWUyYTRlNTRlNDMzMCBqcy10cmllLW1lZGl1bX1cbiAqIC0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKlxuICogaWYgbmVlZGVkLCBjbG9uZVxuICpcbiAqIGZpcnN0IHRoaW5ncyB0byBjaGVjayBhcmUgbnVtYmVyL3N0cmluZy9ib29sZWFuL251bGwvdW5kZWZpbmVkXG4gKlxuICogdGhlbiBjaGVjayBub24taXRlcmF0YWJsZXNcbiAqIHN5bWJvbCwgcHJvbWlzZSxcbiAqXG4gKiB0aGVuIGNoZWNrIGNvbnZlcnNpb25zXG4gKiAtIG1hcCwgc2V0XG4gKlxuICogdGhlbiBjaGVjayBlbXB0aWVzXG4gKiAtIG9ialxuICogLSBmblxuICpcbiAqIC0tLS0tLS1cbiAqXG4gKiBudW1iZXJzIGYtb3IgZmlyc3QvbGFzdFxuICogYW5kIGFzIGEgc29ydCBvZiBoYXNoIGxpa2VcbiAqIDEgKyAyICsgNCA9IElTTEVBRiAmIElTUk9PVCA/XG4gKlxuICogQXJyYXlcbiAqXG4gKiBPYmplY3QgRnVuY3Rpb24gRGF0ZSBFcnJvciBNYXAgU2V0XG4gKlxuICogU3RyaW5nXG4gKiBOdW1iZXIgTmFOIEluZmluaXR5XG4gKiBCb29sZWFuXG4gKlxuICpcbiAqIG51bGwgdW5kZWZpbmVkXG4gKlxuICogUHJvbWlzZSBTeW1ib2xcbiAqXG4gKiAtLS0tXG4gKlxuICogQGVtaXRzIGJlZm9yZVxuICogQGVtaXRzIHByZVxuICogQGVtaXRzIHBvc3RcbiAqIEBlbWl0cyBhZnRlclxuICovXG5cbi8qKlxuICogQGRlc2MgVHJhdmVyc2UgY2xhc3MsIHBvb2xlZFxuICogQG1vZGlmaWVzIHRoaXMubm9kZVxuICogQG1vZGlmaWVzIHRoaXMucGFyZW50XG4gKiBAbW9kaWZpZXMgdGhpcy5yb290XG4gKiBAc2luY2UgNS4wLjBcbiAqXG4gKiBAbWVtYmVyIFRyYXZlcnNlXG4gKiBAY2xhc3NcbiAqIEBjb25zdHJ1Y3RvclxuICogQGFsaWFzIEl0ZXJBdGVPclxuICogQGV4dGVuZHMgcG9vbGVyXG4gKlxuICogQHBhcmFtIHtUcmF2ZXJzYWJsZX0gaXRlcmF0ZWUgdmFsdWUgdG8gaXRlcmF0ZSwgY2xvbmUsIGNvcHksIGNoZWNrIGZvciBlcVxuICogQHBhcmFtIHtPYmplY3QgfCB1bmRlZmluZWR9IFtjb25maWddIHdpcCBjb25maWcgZm9yIHRoaW5ncyBzdWNoIGFzIGV2ZW50cyBvciBjb25maWdzXG4gKlxuICogQHNlZSB7QGxpbmsgdHJlZS10cmF2ZXJzYWwtYXJ0aWNsZX1cbiAqIEBzZWUgdHJhdmVyc2VcbiAqIEBUT0RPIG1ha2UgdGhpcyBhIHRyaWUgT1IgYSBsaW5rZWQtbGlzdFxuICpcbiAqIEB0ZXN0cyB0cmF2ZXJzZVxuICogQHR5cGVzIHRyYXZlcnNlXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiAgICBuZXcgVHJhdmVyc2UoWzFdKVxuICogICAgbmV3IFRyYXZlcnNlKFtdLCB7fSlcbiAqXG4gKi9cbmZ1bmN0aW9uIFRyYXZlcnNlKGl0ZXJhdGVlLCBjb25maWcpIHtcbiAgLy8gYWx3YXlzIGNsZWFyZWQgd2hlbiBkb25lIGFueXdheVxuICBpZiAoaXNVbmRlZmluZWQodGhpcy5wYXJlbnRzKSkgdGhpcy5wYXJlbnRzID0gbmV3IFNldCgpXG5cbiAgdGhpcy5ub2RlID0gaXRlcmF0ZWVcbiAgdGhpcy5wYXJlbnQgPSBpdGVyYXRlZVxuICB0aGlzLnJvb3QgPSBpdGVyYXRlZVxuICB0aGlzLnJlc2V0KClcblxuICAvLyB0byBwYXNzIGluIHRoZSBldmVudHMgKGFzIGNvbW1lbnRlZCBiZWxvdykgd2l0aG91dCBtZXNzaW5nIHVwIHNjb3BlP1xuICAvLyBpZiAoY29uZmlnLm9uKSB0aGlzLm9uID0gY29uZmlnLm9uXG4gIHJldHVybiB0aGlzXG59XG5cbi8qKlxuICogQGRlc2MgcmVzZXQgdGhlIHByb3BlcnRpZXMgd2hlbiBmaW5pc2hlZCBwb29saW5nIG9yIGluc3RhbnRpYXRpbmdcbiAqIEBzaW5jZSA1LjAuMFxuICogQG1ldGhvZFxuICpcbiAqIEBtZW1iZXJPZiBUcmF2ZXJzZVxuICogQG1vZGlmaWVzIFRyYXZlcnNlLnBhdGhcbiAqIEBtb2RpZmllcyBUcmF2ZXJzZS5rZXlcbiAqIEBtb2RpZmllcyBUcmF2ZXJzZS5pc0FsaXZlXG4gKiBAbW9kaWZpZXMgVHJhdmVyc2UuaXNDaXJjdWxhclxuICogQG1vZGlmaWVzIFRyYXZlcnNlLmlzTGVhZlxuICogQG1vZGlmaWVzIFRyYXZlcnNlLmlzUm9vdFxuICogQG1vZGlmaWVzIFRyYXZlcnNlLmRlcHRoXG4gKiBAcmV0dXJuIHt2b2lkfVxuICpcbiAqIEBleGFtcGxlXG4gKiAgICB0cmF2ZXJzZShbXSkucmVzZXQoKVxuICovXG5UcmF2ZXJzZS5wcm90b3R5cGUucmVzZXQgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5wYXRoID0gW11cbiAgdGhpcy5rZXkgPSB1bmRlZmluZWRcbiAgdGhpcy5pc0FsaXZlID0gdHJ1ZVxuICB0aGlzLmlzQ2lyY3VsYXIgPSBmYWxzZVxuICB0aGlzLmlzTGVhZiA9IGZhbHNlXG4gIHRoaXMuaXNSb290ID0gdHJ1ZVxuXG4gIC8vIGl0ZXJhdGVzICsxIHNvIHN0YXJ0IGF0IDBcbiAgdGhpcy5kZXB0aCA9IC0xXG59XG5cbi8qKlxuICogQGRlc2MgZmluZCBwYXJlbnQsXG4gKiAgICAgICBpcyB0aGVyZSBhIHBhcmVudFxuICogICAgICAgYWJvdmUgdGhlIGN1cnJlbnQgZGVwdGhcbiAqICAgICAgIHdpdGggdGhlIHNhbWUgdmFsdWUsXG4gKiAgICAgICBtYWtpbmcgaXQgY2lyY3VsYXI/XG4gKlxuICogQG1lbWJlck9mIFRyYXZlcnNlXG4gKiBAc2luY2UgNS4wLjBcbiAqIEBwcml2YXRlXG4gKiBAbWV0aG9kXG4gKlxuICogQHBhcmFtICB7bnVtYmVyfSBkZXB0aCBjdXJyZW50IGRlcHRoLCB0byBmaW5kIHBhcmVudCA+PVxuICogQHBhcmFtICB7cGFyZW50fSB2YWx1ZSBwYXJlbnQgdmFsdWUgdG8gZmluZFxuICogQHJldHVybiB7Ym9vbGVhbn0gaGFzUGFyZW50XG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiAgICB2YXIgb2JqID0ge2VoOiBbJ2VoJ119XG4gKiAgICB0cmF2ZXJzZShvYmopLmFkZFBhcmVudCgwLCBvYmopXG4gKlxuICovXG5UcmF2ZXJzZS5wcm90b3R5cGUuaGFzUGFyZW50ID0gZnVuY3Rpb24oZGVwdGgsIHZhbHVlKSB7XG4gIC8vIG9yIGFycmF5XG4gIHJldHVybiBpc09iaih2YWx1ZSkgPyB0aGlzLnBhcmVudHMuaGFzKHZhbHVlKSA6IGZhbHNlXG59XG5cbi8qKlxuICogQGRlc2MgYWRkIHBhcmVudCwgdG8gcHJldmVudCBjaXJjdWxhciBpdGVyYXRpb25zXG4gKiBAbWVtYmVyT2YgVHJhdmVyc2VcbiAqIEBzaW5jZSA1LjAuMFxuICogQHByaXZhdGVcbiAqIEBtZXRob2RcbiAqXG4gKiBAcGFyYW0gIHtudW1iZXJ9IGRlcHRoIGN1cnJlbnQgZGVwdGgsIHRvIGFkZCBwYXJlbnQgdG8gPj1cbiAqIEBwYXJhbSAge3BhcmVudH0gdmFsdWUgcGFyZW50IHZhbHVlIHRvIGFkZFxuICogQHJldHVybiB7dm9pZH1cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqICAgIHZhciBvYmogPSB7ZWg6IFsnZWgnXX1cbiAqICAgIHRyYXZlcnNlKG9iaikuYWRkUGFyZW50KDAsIG9iailcbiAqXG4gKi9cblRyYXZlcnNlLnByb3RvdHlwZS5hZGRQYXJlbnQgPSBmdW5jdGlvbihkZXB0aCwgdmFsdWUpIHtcbiAgLy8gJiYgdGhpcy5oYXNQYXJlbnQodmFsdWUpID09PSBmYWxzZVxuICBpZiAoaXNPYmoodmFsdWUpKSB0aGlzLnBhcmVudHMuYWRkKHZhbHVlKVxufVxuXG4vKipcbiAqIEBkZXNjIHJlbW92ZSBhbGwgcGFyZW50cywgcmVzZXQgdGhlIG1hcFxuICpcbiAqIEBtZW1iZXJPZiBUcmF2ZXJzZVxuICogQHNpbmNlIDUuMC4wXG4gKiBAcHJpdmF0ZVxuICogQG1ldGhvZFxuICpcbiAqIEByZXR1cm4ge3ZvaWR9XG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiAgICB2YXIgb2JqID0ge2VoOiBbJ2VoJ119XG4gKiAgICB0cmF2ZXJzZShvYmopLmZvckVhY2goKGtleSwgdmFsdWUsIHQpID0+IHtcbiAqICAgICAgIHQucGFyZW50c1xuICogICAgICAgLy89PiBTZXQoW29ial0pXG4gKiAgICAgICB0LmNsZWFyKClcbiAqICAgICAgIHQucGFyZW50c1xuICogICAgICAgLy89PiBTZXRbXVxuICogICAgfSlcbiAqXG4gKi9cblRyYXZlcnNlLnByb3RvdHlwZS5jbGVhciA9IGZ1bmN0aW9uKCkge1xuICAvLyBpZiAoIWlzVW5kZWZpbmVkKHRoaXMucGFyZW50cykpXG4gIHRoaXMucGFyZW50cy5jbGVhcigpXG59XG5cbi8qKlxuICogQG1lbWJlck9mIFRyYXZlcnNlXG4gKiBAc2luY2UgNS4wLjBcbiAqIEBwcml2YXRlXG4gKiBAbWV0aG9kXG4gKlxuICogQHBhcmFtICB7bnVtYmVyfSBkZXB0aCBjdXJyZW50IGRlcHRoLCB0byBmaW5kIHBhcmVudHMgPj1cbiAqIEBwYXJhbSAge3BhcmVudH0gdmFsdWUgcGFyZW50IHZhbHVlIHRvIHJlbW92ZVxuICogQHJldHVybiB7dm9pZH1cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqICAgIHZhciBvYmogPSB7ZWg6IFsnZWgnXX1cbiAqICAgIHRyYXZlcnNlKG9iaikucmVtb3ZlUGFyZW50KDAsIG9iailcbiAqXG4gKi9cblRyYXZlcnNlLnByb3RvdHlwZS5yZW1vdmVQYXJlbnQgPSBmdW5jdGlvbihkZXB0aCwgdmFsdWUpIHtcbiAgdGhpcy5wYXJlbnRzLmRlbGV0ZSh2YWx1ZSlcbn1cblxuLyoqXG4gKiBAZGVzYyB0aGlzIGlzIHRoZSBtYWluIHVzYWdlIG9mIFRyYXZlcnNlXG4gKiBAbWVtYmVyT2YgVHJhdmVyc2VcbiAqIEBzaW5jZSAzLjAuMFxuICogQHZlcnNpb24gNS4wLjBcbiAqIEBtZXRob2RcbiAqXG4gKiBAcGFyYW0gIHtGdW5jdGlvbn0gY2IgY2FsbGJhY2sgZm9yIGVhY2ggaXRlcmF0aW9uXG4gKiBAcmV0dXJuIHsqfSBtYXBwZWQgcmVzdWx0IG9yIG9yaWdpbmFsIHZhbHVlLCBkZXBlbmRzIGhvdyBpdCBpcyB1c2VkXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiAgICB0cmF2ZXJzZShbMSwgMiwgM10pLmZvckVhY2goKGtleSwgdmFsdWUpID0+IGNvbnNvbGUubG9nKHtba2V5XTogdmFsdWV9KSlcbiAqICAgIC8vPT4geycwJzogMX1cbiAqICAgIC8vPT4geycxJzogMn1cbiAqICAgIC8vPT4geycyJzogM31cbiAqXG4gKi9cblRyYXZlcnNlLnByb3RvdHlwZS5mb3JFYWNoID0gZnVuY3Rpb24gaXRlcmF0ZUZvckVhY2goY2IpIHtcbiAgLyogaXN0YW5idWwgaWdub3JlIG5leHQ6IGRldiAqL1xuICBpZiAoRU5WX0RFQlVHKSB7XG4gICAgY29uc29sZS5sb2coJ1xcbiBmb3JFYWNoIFxcbicpXG4gIH1cblxuICBjb25zdCByZXN1bHQgPSB0aGlzLml0ZXJhdGUoY2IpXG5cbiAgLy8gVE9ETzogSEVSRSwgV0hFTiBUSElTIElTIEFEREVELCBDQU4gQlJFQUsgU09NRSBURVNUUz8gU0NPUEVEIFBBUkVOVFMgTUFQP1xuICBUcmF2ZXJzZS5yZWxlYXNlKHRoaXMpXG5cbiAgcmV0dXJuIHJlc3VsdFxufVxuXG4vKipcbiAqIEBkZXNjIHN0b3AgdGhlIGl0ZXJhdGlvblxuICogQG1vZGlmaWVzIHRoaXMuaXNBbGl2ZSA9IGZhbHNlXG4gKiBAbWVtYmVyT2YgVHJhdmVyc2VcbiAqIEBtZXRob2RcbiAqXG4gKiBAcmV0dXJuIHt2b2lkfVxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogICB0cmF2ZXJzZSh7ZWg6IHRydWUsIGFycjogW119KS5mb3JFYWNoKChrZXksIHZhbCwgdCkgPT4ge1xuICogICAgICBpZiAoaXNBcnJheSh2YWwpKSB0aGlzLnN0b3AoKVxuICogICB9KVxuICpcbiAqL1xuVHJhdmVyc2UucHJvdG90eXBlLnN0b3AgPSBmdW5jdGlvbiBzdG9wKCkge1xuICB0aGlzLmlzQWxpdmUgPSBmYWxzZVxuICAvLyB0aGlzLnJlbGVhc2UoKVxufVxuXG4vKipcbiAqIEBUT0RPIHNraXAgMSBicmFuY2hcbiAqIEB2ZXJzaW9uIDUuMC4wXG4gKiBAc2luY2UgMy4wLjBcbiAqIEBtZW1iZXJPZiBUcmF2ZXJzZVxuICogQG1ldGhvZFxuICpcbiAqIEByZXR1cm4ge3ZvaWR9XG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiAgICB0cmF2ZXJzZShbMSwgMiwgMywgWzRdXSkuZm9yRWFjaCgoa2V5LCB2YWwsIHQpID0+IHtcbiAqICAgICAgaWYgKGlzQXJyYXkodmFsKSkgdC5za2lwKClcbiAqICAgIH0pXG4gKlxuICovXG5UcmF2ZXJzZS5wcm90b3R5cGUuc2tpcCA9IGZ1bmN0aW9uIHNraXAoKSB7XG4gIHRoaXMuc2tpcEJyYW5jaCA9IHRydWVcbn1cblxuLyogcHJldHRpZXItaWdub3JlICovXG4vKipcbiAqIEBkZXNjIGNoZWNrcyB3aGV0aGVyIGEgbm9kZSBpcyBpdGVyYXRhYmxlXG4gKiAgICAgICBAbW9kaWZpZXMgVHJhdmVyc2UuaXNJdGVyYXRhYmxlXG4gKiAgICAgICBAbW9kaWZpZXMgVHJhdmVyc2UuaXNMZWFmXG4gKiAgICAgICBAbW9kaWZpZXMgVHJhdmVyc2UuaXNDaXJjdWxhclxuICpcbiAqIEBtZW1iZXJPZiBUcmF2ZXJzZVxuICogQHByb3RlY3RlZFxuICogQG1ldGhvZFxuICpcbiAqIEBwYXJhbSAgeyp9IG5vZGUgdmFsdWUgdG8gY2hlY2tcbiAqIEByZXR1cm4ge3ZvaWR9XG4gKlxuICogQFRPRE8gbW92ZSBpbnRvIHRoZSB3cmFwcGVyPyBpZiBwZXJmIGFsbG93cz9cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqICAgIC5jaGVja0l0ZXJhdGFibGUoe2VoOiB0cnVlfSlcbiAqICAgIC8vPT4gdGhpcy5pc0xlYWYgPSBmYWxzZVxuICogICAgLy89PiB0aGlzLmlzQ2lyY3VsYXIgPSBmYWxzZVxuICogICAgLy89PiB0aGlzLmlzSXRlcmF0YWJsZSA9IHRydWVcbiAqXG4gKiAgICAuY2hlY2tJdGVyYXRhYmxlKHt9IHx8IFtdKVxuICogICAgLy89PiB0aGlzLmlzTGVhZiA9IHRydWVcbiAqICAgIC8vPT4gdGhpcy5pc0NpcmN1bGFyID0gZmFsc2VcbiAqICAgIC8vPT4gdGhpcy5pc0l0ZXJhdGFibGUgPSBmYWxzZVxuICpcbiAqICAgIHZhciBjaXJjdWxhciA9IHt9XG4gKiAgICBjaXJjdWxhci5jaXJjdWxhciA9IGNpcmN1bGFyXG4gKiAgICAuY2hlY2tJdGVyYXRhYmxlKGNpcmN1bGFyKVxuICogICAgLy89PiB0aGlzLmlzTGVhZiA9IGZhbHNlXG4gKiAgICAvLz0+IHRoaXMuaXNDaXJjdWxhciA9IHRydWVcbiAqICAgIC8vPT4gdGhpcy5pc0l0ZXJhdGFibGUgPSB0cnVlXG4gKlxuICovXG5UcmF2ZXJzZS5wcm90b3R5cGUuY2hlY2tJdGVyYXRhYmxlID0gZnVuY3Rpb24gY2hlY2sobm9kZSkge1xuICB0aGlzLmlzSXRlcmF0YWJsZSA9IGlzSXRlcmF0YWJsZShub2RlKVxuICAvLyBqdXN0IHB1dCB0aGVzZSBhcyBhbiBhcnJheT9cbiAgaWYgKGlzVHJ1ZSh0aGlzLmlzSXRlcmF0YWJsZSkpIHtcbiAgICAvLyBuYXRpdmUgPSBsZWFmIGlmIG5vdCByb290XG4gICAgdGhpcy5pc0xlYWYgPSBmYWxzZVxuICAgIGNvbnN0IHBhdGggPSB0aGlzLnBhdGguam9pbignLicpXG5cbiAgICBpZiAodGhpcy5oYXNQYXJlbnQocGF0aCwgbm9kZSkpIHtcbiAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0OiBkZXYgKi9cbiAgICAgIGlmIChFTlZfREVCVUcpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ2NpcmN1bGFyX19fX19fX19fX18nLCB7bm9kZSwgcGF0aDogdGhpcy5wYXRofSlcbiAgICAgIH1cbiAgICAgIHRoaXMuaXNDaXJjdWxhciA9IHRydWVcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICB0aGlzLmFkZFBhcmVudChwYXRoLCBub2RlKVxuICAgICAgdGhpcy5pc0NpcmN1bGFyID0gZmFsc2VcbiAgICB9XG5cbiAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dDogZGV2ICovXG4gICAgaWYgKEVOVl9ERUJVRykge1xuICAgICAgLy8gY29uc29sZS5sb2coJ0lTX0NJUkNVTEFSX0pTT04nLCBpc0NpcmN1bGFyKG5vZGUpLCB0aGlzLmlzQ2lyY3VsYXIsIG5vZGUpXG4gICAgfVxuICB9XG4gIGVsc2Uge1xuICAgIHRoaXMuaXNMZWFmID0gdHJ1ZVxuICAgIHRoaXMuaXNDaXJjdWxhciA9IGZhbHNlXG4gIH1cbn1cblxuLyogcHJldHRpZXItaWdub3JlICovXG4vKipcbiAqIFJlbW92ZSB0aGUgY3VycmVudCBlbGVtZW50IGZyb20gdGhlIG91dHB1dC5cbiAqIElmIHRoZSBub2RlIGlzIGluIGFuIEFycmF5IGl0IHdpbGwgYmUgc3BsaWNlZCBvZmYuXG4gKiBPdGhlcndpc2UgaXQgd2lsbCBiZSBkZWxldGVkIGZyb20gaXRzIHBhcmVudC5cbiAqXG4gKiBAbWVtYmVyT2YgVHJhdmVyc2VcbiAqIEB2ZXJzaW9uIDUuMC4wXG4gKiBAc2luY2UgMi4wLjBcbiAqIEBtZXRob2RcbiAqXG4gKiBAcGFyYW0ge3VuZGVmaW5lZCB8IE9iamVjdH0gW2FyZ10gb3B0aW9uYWwgb2JqIHRvIHVzZSwgZGVmYXVsdHMgdG8gdGhpcy5ub2RlXG4gKiBAcmV0dXJuIHt2b2lkfVxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogICAgdHJhdmVyc2UoWzBdKS5mb3JFYWNoKChrZXksIHZhbCwgaXQpID0+IGl0LnJlbW92ZSgpKVxuICogICAgLy89PiBbXVxuICpcbiAqICAgIHRyYXZlcnNlKHtlaDogdHJ1ZX0pLmZvckVhY2goKGtleSwgdmFsLCBpdCkgPT4gaXQucmVtb3ZlKCkpXG4gKiAgICAvLz0+IHt9XG4gKlxuICogICAgdHJhdmVyc2Uoe2VoOiB0cnVlLCBzdHI6ICdzdHJpbmd5J30pLmZvckVhY2goKGtleSwgdmFsLCBpdCkgPT4ge1xuICogICAgICBpZiAoIWlzU3RyaW5nKHZhbCkpIGl0LnJlbW92ZSgpXG4gKiAgICB9KVxuICogICAgLy89PiB7c3RyOiAnc3RyaW5neSd9XG4gKlxuICovXG5UcmF2ZXJzZS5wcm90b3R5cGUucmVtb3ZlID0gZnVuY3Rpb24gcmVtb3ZlcyhhcmcpIHtcbiAgLy8gaWdub3JlIHVuZGVmaW5lZCAmIG5vbi1vYmplY3QvYXJyYXlzXG4gIGlmIChpc1VuZGVmaW5lZCh0aGlzLmtleSkpIHJldHVyblxuICBsZXQgb2JqID0gYXJnIHx8IHRoaXMubm9kZVxuICBpZiAoIWlzT2JqKG9iaikpIHJldHVyblxuXG4gIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0OiBkZXYgKi9cbiAgaWYgKEVOVl9ERUJVRykge1xuICAgIGNvbnNvbGUubG9nKCdyZW1vdmUnKVxuICAgIGNvbnNvbGUubG9nKHtwYXJlbnQ6IHRoaXMucGFyZW50LCBrZXk6IHRoaXMua2V5LCBvYmp9KVxuICB9XG5cbiAgdGhpcy5yZW1vdmVQYXJlbnQob2JqKVxuICB0aGlzLnNraXAoKVxuXG4gIGRlbGV0ZSBvYmpbdGhpcy5rZXldXG4gIGRlbGV0ZSB0aGlzLnBhcmVudFt0aGlzLmtleV1cblxuICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dDogZGV2ICovXG4gIGlmIChFTlZfREVCVUcpIHtcbiAgICBjb25zb2xlLmxvZygndHJhdmVyc2U6cmVtb3ZlOicsIHRoaXMua2V5LCB7b2JqLCBpdGVyYXRlZTogdGhpcy5ub2RlfSlcbiAgfVxufVxuXG4vKipcbiAqIEBkZXNjIHVwZGF0ZSB0aGUgdmFsdWUgZm9yIHRoZSBjdXJyZW50IGtleVxuICogQHZlcnNpb24gNS4wLjBcbiAqIEBzaW5jZSAyLjAuMFxuICogQG1lbWJlck9mIFRyYXZlcnNlXG4gKlxuICogQHBhcmFtICB7Kn0gdmFsdWUgdGhpcy5ub2RlW3RoaXMua2V5XSA9IHZhbHVlXG4gKiBAcmV0dXJuIHt2b2lkfVxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogICAgdHJhdmVyc2Uoe2VoOiB0cnVlfSlcbiAqICAgIC5mb3JFYWNoKChrZXksIHZhbCwgdHJhdmVyc2VyKSA9PiB7XG4gKiAgICAgICBpZiAodGhpcy5pc1Jvb3QpIHJldHVyblxuICogICAgICAgdHJhdmVyc2VyLnVwZGF0ZShmYWxzZSlcbiAqICAgIH0pXG4gKiAgICAvLz0+IHtlaDogZmFsc2V9XG4gKlxuICovXG5UcmF2ZXJzZS5wcm90b3R5cGUudXBkYXRlID0gZnVuY3Rpb24gdXBkYXRlKHZhbHVlKSB7XG4gIGRvdFNldCh0aGlzLnJvb3QsIHRoaXMucGF0aCwgdmFsdWUpXG59XG5cbi8qKlxuICogQGRlc2MgbWFyayB0aGUgaXRlcmF0aW9uIGFzIGRvbmUsIGNsZWFyIHRoZSBtYXBcbiAqIEBOT1RFIHRoaXMgcmVjeWNsZXMgdGhlIGluc3RhbmNlIGluIHRoZSBwb29sZXIgdG8gcmUtdXNlIGFsbG9jYXRlZCBvYmplY3RzXG4gKiBAbWVtYmVyT2YgVHJhdmVyc2VcbiAqIEBwcml2YXRlXG4gKiBAc2luY2UgNS4wLjBcbiAqXG4gKiBAcmV0dXJuIHt2b2lkfVxuICpcbiAqIEBzZWUgVHJhdmVyc2UuaXRlcmF0ZVxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogIHRyYXZlcnNlKFtdKS5kZXN0cnVjdG9yKClcbiAqXG4gKi9cblRyYXZlcnNlLnByb3RvdHlwZS5kZXN0cnVjdG9yID0gZnVuY3Rpb24gZGVzdHJ1Y3RvcigpIHtcbiAgdGhpcy5ub2RlID0gdW5kZWZpbmVkXG4gIHRoaXMucGFyZW50ID0gdW5kZWZpbmVkXG4gIHRoaXMucmVzZXQoKVxuXG4gIHRoaXMuY2xlYXIoKVxufVxuXG4vKiBwcmV0dGllci1pZ25vcmUgKi9cbi8qKlxuICogQFRPRE8gaGFuZGxlciBmb3IgU2V0ICYgTWFwIHNvIHRoZXkgY2FuIGJlIHNraXBwZWQgb3IgdHJhdmVyc2VkLCBmb3IgZXhhbXBsZSB3aGVuIGNsb25pbmcuLi5cbiAqIEBUT0RPIGFkZCBob29rIHRvIGFkZCBjdXN0b20gY2hlY2tpbmcgaWYgaXNJdGVyYXRhYmxlXG4gKiBAVE9ETyBkZWFsIHdpdGggLmlzUm9vdCBpZiBuZWVkZWRcbiAqIEBUT0RPIGV4YW1wbGVzIHdpdGggY2xvbmUgYW5kIHN0b3BcbiAqXG4gKiBAbWVtYmVyT2YgVHJhdmVyc2VcbiAqIEBwcm90ZWN0ZWRcbiAqIEBzaWcgb24oa2V5OiBudWxsIHwgUHJpbWl0aXZlLCB2YWw6IGFueSwgaW5zdGFuY2U6IFRyYXZlcnNlKTogYW55XG4gKlxuICogQHBhcmFtICB7RnVuY3Rpb259IG9uIGNhbGxiYWNrIGZuIGZvciBlYWNoIGl0ZXJhdGlvblxuICogQHJldHVybiB7Kn0gdGhpcy5ub2RlXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiAgICBpdGVyYXRlKFtdKVxuICogICAgLy89PiBbXVxuICogICAgLy89PiBvbihudWxsLCBbXSlcbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqICAgIGl0ZXJhdGUoWzFdKVxuICogICAgLy89PiBbMV1cbiAqICAgIC8vPT4gb24obnVsbCwgWzFdKVxuICogICAgLy89PiBvbignMScsIDEpXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiAgICAvL3ByaW1pdGl2ZSAtIHNhbWUgZm9yIGFueSBudW1iZXIsIHN0cmluZywgc3ltYm9sLCBudWxsLCB1bmRlZmluZWRcbiAqICAgIGl0ZXJhdGUoU3ltYm9sKCdlaCcpKVxuICogICAgLy89PiBTeW1ib2woJ2VoJylcbiAqICAgIC8vPT4gb24oU3ltYm9sKCdlaCcpKVxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogICAgdmFyIGRlZXBlciA9IHtlaDogJ2NhbmFkYScsIGFycjogW3ttb29zZTogdHJ1ZX0sIDBdfVxuICogICAgaXRlcmF0ZShkZWVwZXIpXG4gKiAgICAvLz0+IGRlZXBlciAvLyByZXR1cm5zXG4gKiAgICAvLz0+IG9uKG51bGwsIGRlZXBlciwgdGhpcykgLy8gcm9vdFxuICpcbiAqICAgIC8vPT4gb24oJ2VoJywgJ2NhbmFkYScsIHRoaXMpIC8vIDFzdCBicmFuY2hcbiAqXG4gKiAgICAvLz0+IG9uKCdhcnInLCBbe21vb3NlOiB0cnVlfSwgMF0sIHRoaXMpXG4gKiAgICAvLz0+IG9uKCdhcnIuMCcsIFt7bW9vc2U6IHRydWV9XSwgdGhpcylcbiAqICAgIC8vPT4gb24oJ2Fyci4wLm1vb3NlJywgdHJ1ZSwgdGhpcylcbiAqICAgIC8vPT4gb24oJ2Fyci4xJywgWzBdLCB0aGlzKVxuICpcbiAqXG4gKi9cblRyYXZlcnNlLnByb3RvdHlwZS5pdGVyYXRlID0gZnVuY3Rpb24gaXRlcmF0ZShvbikge1xuICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCA6IGRldiAqL1xuICBpZiAoRU5WX0RFQlVHKSB7XG4gICAgLy8gcmVxdWlyZSgnZmxpcGxvZycpXG4gICAgLy8gLmJvbGQodGhpcy5wYXRoLmpvaW4oJy4nKSlcbiAgICAvLyAuZGF0YShwYXJlbnRzLmtleXMoKSlcbiAgICAvLyAuZWNobygpXG4gICAgY29uc29sZS5sb2coJ1xcbi4uLml0ZXJhdGUuLi5cXG4nKVxuICB9XG5cbiAgaWYgKHRoaXMuaXNBbGl2ZSA9PT0gZmFsc2UpIHtcbiAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCA6IGRldiAqL1xuICAgIGlmIChFTlZfREVCVUcpIHtcbiAgICAgIGNvbnNvbGUubG9nKCdERUFEJylcbiAgICB9XG5cbiAgICByZXR1cm4gVHJhdmVyc2UucmVsZWFzZSh0aGlzKVxuICB9XG5cbiAgbGV0IG5vZGUgPSB0aGlzLm5vZGVcblxuICAvLyBjb252ZXJ0IHRvIGl0ZXJhdGFibGVcbiAgaWYgKGlzTWFwKG5vZGUpKSB7XG4gICAgbm9kZSA9IHJlZHVjZShub2RlKVxuICB9XG4gIGVsc2UgaWYgKGlzU2V0KG5vZGUpKSB7XG4gICAgbm9kZSA9IHRvYXJyKG5vZGUpXG4gIH1cblxuICAvLyBAVE9ETzogbWF5YmUgb25seSByaWdodCBiZWZvcmUgc3ViLWxvb3BcbiAgdGhpcy5hZGRQYXJlbnQodGhpcy5kZXB0aCwgbm9kZSlcblxuICBjb25zdCBub2RlSXNBcnJheSA9IGlzQXJyYXkobm9kZSlcbiAgY29uc3Qgbm9kZUlzT2JqID0gbm9kZUlzQXJyYXkgfHwgaXNPYmoobm9kZSlcblxuICAvLyAtLS1cblxuICAvLyBAZXZlbnRcbiAgaWYgKCFpc1VuZGVmaW5lZCh0aGlzLm9uQmVmb3JlKSkge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11c2VsZXNzLWNhbGxcbiAgICB0aGlzLm9uQmVmb3JlKHRoaXMpXG4gIH1cblxuICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCA6IGRldiAqL1xuICBpZiAoRU5WX0RFQlVHKSB7XG4gICAgLy8gY29uc3Qgc3RyID0gcmVxdWlyZSgncHJldHR5LWZvcm1hdCcpKHtub2RlSXNPYmosIG5vZGVJc0FycmF5LCBub2RlfSlcbiAgICAvLyByZXF1aXJlKCdmbGlwbG9nJykudmVyYm9zZSgxKS5kYXRhKHtub2RlSXNPYmosIG5vZGVJc0FycmF5LCBub2RlfSkuZWNobygpXG4gICAgLy8gY29uc29sZS5sb2cobm9kZSwgcGFyZW50cylcbiAgICAvLyBjb25zb2xlLmxvZyhzdHIpXG4gICAgY29uc29sZS5sb2coe25vZGVJc09iaiwgbm9kZUlzQXJyYXksIG5vZGV9KVxuICB9XG5cbiAgLyoqXG4gICAqIGNhbGwgYXMgcm9vdCwgaGVscGZ1bCB3aGVuIHdlXG4gICAqIC0gaXRlcmF0ZSBzb21ldGhpbmcgd2l0aCBubyBrZXlzXG4gICAqIC0gaXRlcmF0ZSBhIG5vbi1pdGVyYXRhYmxlIChzeW1ib2wsIGVycm9yLCBuYXRpdmUsIHByb21pc2UsIGV0YylcbiAgICovXG4gIGlmIChpc1RydWUodGhpcy5pc1Jvb3QpKSB7XG4gICAgb24uY2FsbCh0aGlzLCBudWxsLCBub2RlLCB0aGlzKVxuICAgIHRoaXMuaXNSb290ID0gZmFsc2VcbiAgfVxuXG4gIGNvbnN0IGlzT2JqT3JBcnJheSA9IG5vZGVJc0FycmF5IHx8IG5vZGVJc09ialxuXG4gIC8vIGlmIChpc09iak9yQXJyYXkpIHtcbiAgLy8gICAvLyBAZXZlbnRcbiAgLy8gICBpZiAoIWlzVW5kZWZpbmVkKHRoaXMub25CZWZvcmUpKSB7XG4gIC8vICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdXNlbGVzcy1jYWxsXG4gIC8vICAgICB0aGlzLm9uQmVmb3JlKHRoaXMpXG4gIC8vICAgfVxuICAvLyB9XG5cbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgLy8gSUYgT0JKV0lUSE9VVEtFWVMsIElGIEFSUkFZIFdJVEhPVVQgTEVOR1RILi4uXG4gIGlmIChpc09iak9yQXJyYXkgJiYgaXNFbXB0eShub2RlKSkge1xuICAgIG9uLmNhbGwodGhpcywgdGhpcy5rZXksIG5vZGUsIHRoaXMpXG4gICAgdGhpcy5ub2RlID0gbm9kZVxuICB9XG5cbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuICBlbHNlIGlmIChpc09iak9yQXJyYXkpIHtcbiAgICB0aGlzLmRlcHRoID0gdGhpcy5wYXRoLmxlbmd0aFxuXG4gICAgLy8gQFRPRE8gU0FGRVRZIFdJVEggYHByb3BzKG5vZGUpYCA8LSBmaXhlcyBFcnJvclxuICAgIGxldCBrZXlzID0gbm9kZUlzQXJyYXkgPyBub2RlIDogT2JqZWN0S2V5cyhub2RlKVxuXG4gICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgOiBkZXYgKi9cbiAgICBpZiAoRU5WX0RFQlVHKSB7XG4gICAgICBjb25zb2xlLmxvZyh7a2V5c30pXG4gICAgICAvLyByZXF1aXJlKCdmbGlwbG9nJykudmVyYm9zZSgxKS5kYXRhKHRoaXMpLmVjaG8oKVxuICAgIH1cblxuICAgIC8vIEBldmVudFxuICAgIC8vIGlmICghaXNVbmRlZmluZWQodGhpcy5vbkJlZm9yZSkpIHRoaXMub25CZWZvcmUoKVxuXG4gICAgLy8gQE5PVEU6IHNhZmV0eSBoZXJlXG4gICAgLy8gdGhpcy5jaGVja0l0ZXJhdGFibGUobm9kZSlcblxuICAgIC8vIGNvbnN0IGxhc3QgPSBrZXlzW2tleXMubGVuZ3RoIC0gMV1cblxuICAgIC8vIEBsb29wXG4gICAgZm9yIChsZXQga2V5ID0gMDsga2V5IDwga2V5cy5sZW5ndGg7IGtleSsrKSB7XG4gICAgICAvLyBpZiAoRU5WX0RFQlVHKVxuICAgICAgLy8gY29uc29sZS5sb2coJ2l0ZXJhdGluZzonLCB7a2V5fSlcblxuICAgICAgLy8gLS0tIHNhZmV0eSAtLS1cbiAgICAgIGlmICh0aGlzLmlzQWxpdmUgPT09IGZhbHNlKSB7XG4gICAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0IDogZGV2ICovXG4gICAgICAgIGlmIChFTlZfREVCVUcpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZygnREVBRCcpXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gVHJhdmVyc2UucmVsZWFzZSh0aGlzKVxuICAgICAgfVxuXG4gICAgICAvLyBATk9URTogbG9vayBhYm92ZSBhZGQgcHJldiBhZGQgcGFyZW50XG4gICAgICAvLyBhZGRQYXJlbnQodGhpcy5kZXB0aCwgbm9kZSlcblxuXG4gICAgICAvLyAtLS0tLSBzZXR1cCBvdXIgZGF0YSAtLS0tXG5cbiAgICAgIC8vIHRvIG1ha2UgaXQgZGVsZXRhYmxlXG4gICAgICBpZiAobm9kZSAhPT0gdGhpcy5ub2RlKSB0aGlzLnBhcmVudCA9IG5vZGVcblxuICAgICAgdGhpcy5rZXkgPSBub2RlSXNBcnJheSA/IGtleSA6IGtleXNba2V5XVxuICAgICAgLy8gdGhpcy5pc0xhc3QgPSBrZXkgPT09IGxhc3RcblxuICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQ6IGRldiAqL1xuICAgICAgaWYgKEVOVl9ERUJVRykge1xuICAgICAgICBjb25zb2xlLmxvZygnYWxpdmUnLCB0aGlzLmtleSlcbiAgICAgIH1cblxuICAgICAgLy8gQGV2ZW50XG4gICAgICBpZiAoIWlzVW5kZWZpbmVkKHRoaXMub25QcmUpKSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11c2VsZXNzLWNhbGxcbiAgICAgICAgdGhpcy5vblByZSh0aGlzKVxuICAgICAgfVxuXG5cbiAgICAgIGNvbnN0IHZhbHVlID0gbm9kZVt0aGlzLmtleV1cblxuICAgICAgdGhpcy5jaGVja0l0ZXJhdGFibGUodmFsdWUpXG4gICAgICAvLyBhZGRQYXJlbnQodmFsdWUpXG4gICAgICBjb25zdCBwYXRoQmVmb3JlTmVzdGluZyA9IHRoaXMucGF0aC5zbGljZSgwKVxuXG4gICAgICAvLyBATk9URTogY2FuIGdvIGZvcndhcmQtYmFja3dhcmRzIGlmIHRoaXMgaXMgYWZ0ZXIgdGhlIG5lc3RlZCBpdGVyYXRpbmdcbiAgICAgIHRoaXMucGF0aC5wdXNoKHRoaXMua2V5KVxuICAgICAgdGhpcy5kZXB0aCA9IHRoaXMucGF0aC5sZW5ndGhcblxuICAgICAgLy8gLS0tLS0gY29udGludWUgZXZlbnRzLCBsb29wIGRlZXBlciB3aGVuIG5lZWRlZCAtLS0tXG5cbiAgICAgIC8vIEBOT1RFIHNpbmNlIHdlIGNoZWNrIGlzQWxpdmUgYXQgdGhlIGJlZ2lubmluZyBvZiBlYWNoIGxvb3BcbiAgICAgIC8vIGNvdWxkIHVzZSAuc2tpcCBhbG9uZ2lzZGUgLnN0b3BcbiAgICAgIC8vIEBUT0RPIEBJTVBPUlRBTlQgQEhBQ0sgQEZJWE1FIHJpZ2h0IGhlcmUgaXQgc2hvdWxkIGFsc28gaGFuZGxlIHRoZSAuc3RvcFxuICAgICAgb24uY2FsbCh0aGlzLCB0aGlzLmtleSwgdmFsdWUsIHRoaXMpXG4gICAgICBpZiAoaXNUcnVlKHRoaXMuc2tpcEJyYW5jaCkpIHtcbiAgICAgICAgdGhpcy5za2lwQnJhbmNoID0gZmFsc2VcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cblxuICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQ6IGRldiAqL1xuICAgICAgaWYgKEVOVl9ERUJVRykge1xuICAgICAgICAvLyByZXF1aXJlKCdmbGlwbG9nJykuZGF0YShwYXJlbnRzKS5lY2hvKClcbiAgICAgICAgLy8gcmVxdWlyZSgnZmxpcGxvZycpLmRhdGEodGhpcykuZWNobygpXG4gICAgICB9XG5cbiAgICAgIC8vIGhhbmRsZSBkYXRhXG4gICAgICBpZiAoaXNUcnVlKHRoaXMuaXNDaXJjdWxhcikpIHtcbiAgICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQ6IGRldiAqL1xuICAgICAgICBpZiAoRU5WX0RFQlVHKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coJygoKGNpcmN1bGFyKSkpJywgdGhpcy5rZXkpXG4gICAgICAgIH1cblxuICAgICAgICAvLyBvbi5jYWxsKHRoaXMsIHRoaXMua2V5LCB2YWx1ZSwgdGhpcylcbiAgICAgICAgLy8gdGhpcy5wYXRoLnBvcCgpXG4gICAgICAgIHRoaXMucGF0aCA9IHBhdGhCZWZvcmVOZXN0aW5nXG5cbiAgICAgICAgLy8gdGhpcy5pc0NpcmN1bGFyID0gZmFsc2VcbiAgICAgICAgLy8gYnJlYWtcbiAgICAgICAgY29udGludWVcbiAgICAgICAgLy8gcmV0dXJuXG4gICAgICB9XG5cblxuICAgICAgLy8gJiZcbiAgICAgIGlmIChpc1RydWUodGhpcy5pc0l0ZXJhdGFibGUpKSB7XG4gICAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0OiBkZXYgKi9cbiAgICAgICAgaWYgKEVOVl9ERUJVRykge1xuICAgICAgICAgIGNvbnNvbGUubG9nKCcoKChpdGVyYXRhYmxlKSkpJywgdGhpcy5rZXkpXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLm5vZGUgPSB2YWx1ZVxuICAgICAgICB0aGlzLml0ZXJhdGUob24pXG4gICAgICAgIHRoaXMucGF0aCA9IHBhdGhCZWZvcmVOZXN0aW5nXG4gICAgICB9XG5cbiAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0OiBkZXYgKi9cbiAgICAgIGlmIChFTlZfREVCVUcpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNJdGVyYXRhYmxlID09PSBmYWxzZSkge1xuICAgICAgICAgIGNvbnNvbGUubG9nKCdub3QgaXRlcmF0YWJsZScsIHRoaXMua2V5KVxuICAgICAgICB9XG5cbiAgICAgICAgY29uc29sZS5sb2coJy0tLS0tLS0tLS0tLS0tLS0tIHBvc3QgLS0tLS0tLS0tLScsIG5vZGUpXG4gICAgICB9XG5cbiAgICAgIC8vIEBldmVudFxuICAgICAgaWYgKCFpc1VuZGVmaW5lZCh0aGlzLm9uUG9zdCkpIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVzZWxlc3MtY2FsbFxuICAgICAgICB0aGlzLm9uUG9zdCh0aGlzKVxuICAgICAgfVxuXG4gICAgICAvLyBjbGVhbnVwLCBiYWNrdXAgMSBsZXZlbFxuICAgICAgdGhpcy5wYXRoLnBvcCgpXG5cbiAgICAgIHRoaXMucmVtb3ZlUGFyZW50KG5vZGUpXG4gICAgfVxuXG4gICAgLy8gdGhpcy5wYXRoLnBvcCgpXG4gICAgdGhpcy5kZXB0aCA9IHRoaXMucGF0aC5sZW5ndGhcbiAgfVxuICBlbHNlIHtcbiAgICAvLyB0aGlzLmlzTGFzdCA9IGZhbHNlXG4gICAgb24uY2FsbCh0aGlzLCB0aGlzLmRlcHRoLCBub2RlLCB0aGlzKVxuICB9XG5cbiAgLy8gQE5PVEU6IGNhcmVmdWxcbiAgLy8gcmVtb3ZlUGFyZW50KG5vZGUpXG5cbiAgLy8gQE5PVEU6IGp1c3QgZm9yIC5hZnRlciA/XG4gIHRoaXMubm9kZSA9IG5vZGVcblxuICAvLyBAZXZlbnRcbiAgaWYgKCFpc1VuZGVmaW5lZCh0aGlzLm9uQWZ0ZXIpKSB7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVzZWxlc3MtY2FsbFxuICAgIHRoaXMub25BZnRlcih0aGlzKVxuICB9XG5cbiAgdGhpcy5wYXRoLnBvcCgpXG5cbiAgcmV0dXJuIHRoaXMubm9kZVxufVxuXG4vLyBpcyBzbWFsbGVyLCBidXQgcHJvYmFibHkgc2xvd2VyXG4vLyBmdW5jdGlvbiBvbkV2ZW50KHByb3BlcnR5KSB7XG4vLyAgIHJldHVybiBmdW5jdGlvbihmbikge1xuLy8gICAgIHRoaXNbcHJvcGVydHldID0gZnVuY3Rpb25cbi8vICAgfVxuLy8gfVxuXG4vLyB3aGVuIGl0J3Mgc29tZSBzb3J0IG9mIGl0ZXJ0YWJsZSBvYmplY3QsIGxvb3AgaXQgZnVydGhlclxuLy8gQFRPRE86IG5lZWQgdG8gaGFuZGxlIHRoZXNlIGJldHRlciB3aXRob3V0IHRvdGFsbHkgbWVzc2luZyB3aXRoIGJhZCBzY29wZVxuVHJhdmVyc2UucHJvdG90eXBlLnByZSA9IGZ1bmN0aW9uKGZuKSB7XG4gIHRoaXMub25QcmUgPSBmblxufVxuVHJhdmVyc2UucHJvdG90eXBlLnBvc3QgPSBmdW5jdGlvbihmbikge1xuICB0aGlzLm9uUG9zdCA9IGZuXG59XG5UcmF2ZXJzZS5wcm90b3R5cGUuYmVmb3JlID0gZnVuY3Rpb24oZm4pIHtcbiAgdGhpcy5vbkJlZm9yZSA9IGZuXG59XG5UcmF2ZXJzZS5wcm90b3R5cGUuYWZ0ZXIgPSBmdW5jdGlvbihmbikge1xuICB0aGlzLm9uQWZ0ZXIgPSBmblxufVxuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4vKipcbiAqIEBUT0RPIG1lcmdlIHdpdGggZG9wZW1lcmdlP1xuICogQFRPRE8gbmVlZHMgdGVzdHMgY29udmVydGVkIGJhY2sgZm9yIHRoaXMgKG9ic2VydmUgdGVzdHMgZG8gY292ZXIgc29tZXdoYXQpXG4gKlxuICogQHBhcmFtICB7Kn0gYXJnIGRlZmF1bHRzIHRvIHRoaXMubm9kZVxuICogQHJldHVybiB7Kn0gY2xvbmVkXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiAgIHZhciBvYmogPSB7fVxuICogICB2YXIgY2xvbmVkID0gdHJhdmVyc2UoKS5jbG9uZShvYmopXG4gKiAgIG9iai5laCA9IHRydWVcbiAqICAgZXEob2JqLCBjbG9uZWQpXG4gKiAgIC8vPT4gZmFsc2VcbiAqXG4gKi9cblRyYXZlcnNlLnByb3RvdHlwZS5jbG9uZSA9IGNsb25lXG5cbi8qKlxuICogQHRvZG8gdWdoLCBob3cgdG8gY2xvbmUgYmV0dGVyIHdpdGggKnJlY3Vyc2l2ZSogb2JqZWN0cz9cbiAqIEBwYXJhbSAge2FueX0gc3JjIHdpcFxuICogQHJldHVybiB7YW55fSB3aXBcbiAqL1xuVHJhdmVyc2UucHJvdG90eXBlLmNvcHkgPSBjb3B5XG5cbi8qKlxuICogQGRlc2MgY2xvbmUgYW55IHZhbHVlXG4gKiBAdmVyc2lvbiA1LjAuMFxuICogQHNpbmNlIDQuMC4wXG4gKiBAbWVtYmVyT2YgVHJhdmVyc2VcbiAqIEBleHRlbmRzIGNvcHlcbiAqIEBleHRlbmRzIFRyYXZlcnNlXG4gKlxuICogQHBhcmFtICB7Kn0gYXJnIGFyZ3VtZW50IHRvIGNsb25lXG4gKiBAcmV0dXJuIHsqfSBjbG9uZWQgdmFsdWVcbiAqXG4gKiB7QGxpbmsgaHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvI2Nsb25lIHVuZGVyc2NvcmUtY2xvbmV9XG4gKiBAc2VlIHtAbGluayB1bmRlcnNjb3JlLWNsb25lfVxuICogQHNlZSBkb3BlbWVyZ2VcbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqICAgIHZhciBvYmogPSB7ZWg6IHRydWV9XG4gKiAgICBjbG9uZShvYmopID09PSBvYmogLy89PiBmYWxzZVxuICpcbiAqICAgIHZhciBvYmogPSB7ZWg6IHRydWV9XG4gKiAgICB2YXIgb2JqMiA9IGNsb25lKG9iailcbiAqICAgIG9iai5laCA9IGZhbHNlXG4gKiAgICBjb25zb2xlLmxvZyhvYmoyLmVoKSAvLz0+IHRydWVcbiAqXG4gKi9cbmZ1bmN0aW9uIGNsb25lKGFyZykge1xuICBjb25zdCBvYmogPSBpc1VuZGVmaW5lZChhcmcpID8gdGhpcy5ub2RlIDogYXJnXG4gIGlmIChpc1ByaW1pdGl2ZShvYmopKSByZXR1cm4gb2JqXG4gIGxldCBjbG9uZWQgPSBlbXB0eVRhcmdldChvYmopXG4gIGxldCBjdXJyZW50ID0gY2xvbmVkXG5cbiAgdHJhdmVyc2Uob2JqKS5mb3JFYWNoKChrZXksIHZhbHVlLCB0cmF2ZXJzZXIpID0+IHtcbiAgICAvLyB0LmlzUm9vdFxuICAgIGlmIChpc051bGwoa2V5KSkgcmV0dXJuXG5cbiAgICBsZXQgY29waWVkID0gY29weSh2YWx1ZSlcbiAgICBpZiAodHJhdmVyc2VyLmlzQ2lyY3VsYXIgJiYgaXNBcnJheSh2YWx1ZSkpIGNvcGllZCA9IHZhbHVlLnNsaWNlKDApXG4gICAgZG90U2V0KGN1cnJlbnQsIHRyYXZlcnNlci5wYXRoLCBjb3BpZWQpXG4gIH0pXG5cbiAgcmV0dXJuIGNsb25lZFxufVxuXG4vLyBAVE9ETyBjb3VsZCBqdXN0IGhhdmUgdHJhdmVyc2UgPSBUcmF2ZXJzZS5nZXRQb29sZWQgP1xuYWRkUG9vbGluZ1RvKFRyYXZlcnNlKVxuZnVuY3Rpb24gdHJhdmVyc2UodmFsdWUpIHtcbiAgcmV0dXJuIFRyYXZlcnNlLmdldFBvb2xlZCh2YWx1ZSlcbn1cblxudHJhdmVyc2UuZXEgPSBlcSh0cmF2ZXJzZSlcbnRyYXZlcnNlLmNsb25lID0gY2xvbmVcbnRyYXZlcnNlLmNvcHkgPSBjb3B5XG5tb2R1bGUuZXhwb3J0cyA9IHRyYXZlcnNlXG4iXSwibmFtZXMiOlsiY29uc3QiLCJsZXQiLCJ0aGlzIl0sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7O0FBWUFBLEdBQUssQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQztBQUNyQ0EsR0FBSyxDQUFDLE1BQU0sR0FBRyxPQUFPLENBQUMsV0FBVyxDQUFDO0FBQ25DQSxHQUFLLENBQUMsWUFBWSxHQUFHLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQztBQUMvQ0EsR0FBSyxDQUFDLFdBQVcsR0FBRyxPQUFPLENBQUMsZ0JBQWdCLENBQUM7QUFDN0NBLEdBQUssQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQztBQUNyQ0EsR0FBSyxDQUFDLEtBQUssR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDO0FBQ2pDQSxHQUFLLENBQUMsS0FBSyxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUM7QUFDakNBLEdBQUssQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQztBQUNqQ0EsR0FBSyxDQUFDLFdBQVcsR0FBRyxPQUFPLENBQUMsZ0JBQWdCLENBQUM7QUFDN0NBLEdBQUssQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLFdBQVcsQ0FBQztBQUNuQ0EsR0FBSyxDQUFDLFVBQVUsR0FBRyxPQUFPLENBQUMsYUFBYSxDQUFDO0FBQ3pDQSxHQUFLLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUM7QUFDbENBLEdBQUssQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQztBQUNqQ0EsR0FBSyxDQUFDLE1BQU0sR0FBRyxPQUFPLENBQUMsV0FBVyxDQUFDO0FBQ25DQSxHQUFLLENBQUMsV0FBVyxHQUFHLE9BQU8sQ0FBQyx5QkFBeUIsQ0FBQztBQUN0REEsR0FBSyxDQUFDLElBQUksR0FBRyxPQUFPLENBQUMsbUJBQW1CLENBQUM7QUFDekNBLEdBQUssQ0FBQyxFQUFFLEdBQUcsT0FBTyxDQUFDLGtCQUFrQixDQUFDO0FBQ3RDQSxHQUFLLENBQUMsWUFBWSxHQUFHLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQzs7Ozs7O0FBTTlDQSxHQUFLLENBQUMsU0FBUyxHQUFHLEtBQUs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBbUZ2QixTQUFTLFFBQVEsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFOztFQUVsQyxJQUFJLFdBQVcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsRUFBQSxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksR0FBRyxFQUFFLEVBQUE7O0VBRXZELElBQUksQ0FBQyxJQUFJLEdBQUcsUUFBUTtFQUNwQixJQUFJLENBQUMsTUFBTSxHQUFHLFFBQVE7RUFDdEIsSUFBSSxDQUFDLElBQUksR0FBRyxRQUFRO0VBQ3BCLElBQUksQ0FBQyxLQUFLLEVBQUU7Ozs7RUFJWixPQUFPLElBQUk7Q0FDWjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFvQkQsUUFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEdBQUcsV0FBVztFQUNwQyxJQUFJLENBQUMsSUFBSSxHQUFHLEVBQUU7RUFDZCxJQUFJLENBQUMsR0FBRyxHQUFHLFNBQVM7RUFDcEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJO0VBQ25CLElBQUksQ0FBQyxVQUFVLEdBQUcsS0FBSzt