UNPKG

foop

Version:

interfaces that describe their intentions.

896 lines (796 loc) 60.7 kB
// 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