UNPKG

foop

Version:

interfaces that describe their intentions.

342 lines (319 loc) 27.6 kB
var SHORTHANDS_KEY = require('./deps/meta/SHORTHANDS_KEY') var Chainable = require('./Chainable') var ObjectKeys = require('./deps/util/keys') var ArrayFrom = require('./deps/util/from') var dopemerge = require('./deps/dopemerge') var reduce = require('./deps/reduce') var reduceEntries = require('./deps/reduce/entries') var isFunction = require('./deps/is/function') var isUndefined = require('./deps/is/undefined') var getMeta = require('./deps/meta') var hasOwnPropertyFlipped = require('./deps/flipped/hasOwnPropertyFlipped') var composer = require('./compose/composer') var newMap = require('./deps/construct/map') var hasMerge = hasOwnPropertyFlipped('merge') /** * this is to avoid circular requires * because MergeChain & MethodChain extend this * yet .method & .merge use those chains * ...also, it serves as a non-references creator for extending new instances * of Chainable, where it splits into (Map | Set) -> composed prototype decorators * * * @file * @since 4.0.0-alpha.1 * @inheritdoc * @class ChainedMapBase * @member ChainedMapBase * @category Chainable * @extends {Chainable} * @type {Chainable} * * @types ChainedMapBase * @tests ChainedMap * * @prop {Meta} meta meta fn * @prop {Map} store main store * * {@link https://tc39.github.io/ecma262/#sec-map-objects emca-map} * {@link https://ponyfoo.com/articles/es6-maps-in-depth pony-map} * {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map mozilla-map} * @see {@link pony-map} * @see {@link mozilla-map} * @see {@link emca-map} * * @see ChainedMap * @see Chainable * @see MergeChain * @see MethodChain * @see ChainedMap * */ var ComposeChainedMapBase = function (Target) { var ChainedMapBase = (function (Target) { function ChainedMapBase(parent) { Target.call(this, parent) this.store = new Map() this.meta = getMeta(this) } if ( Target ) ChainedMapBase.__proto__ = Target; ChainedMapBase.prototype = Object.create( Target && Target.prototype ); ChainedMapBase.prototype.constructor = ChainedMapBase; return ChainedMapBase; }(Target)); // const constructRef = Target.prototype.construct // ChainedMapBase.prototype.construct = function() { // this.store = new Map() // this.meta = getMeta(this) // if (constructRef) constructRef.call(this) // } /** * @desc tap a value with a function * @modifies this.store.get(name) * Invokes interceptor with the obj, and then returns obj. * The primary purpose of this method is to "tap into" a method chain, in * order to perform operations on intermediate results within the chain. * * @memberOf ChainedMapBase * @version 0.7.0 * @since 4.0.0-alpha.1 <- moved from transform & shorthands * * @param {string | any} name key to `.get` * @param {Function} fn function to tap with * @return {Chain} @chainable * * {@link https://github.com/jashkenas/underscore/blob/master/underscore.js#L1161 underscore-tap} * {@link https://github.com/ramda/ramda/blob/master/src/internal/_xtap.js ramda-xtap} * {@link https://github.com/ramda/ramda/blob/master/src/tap.js ramda-tap} * {@link https://github.com/sindresorhus/awesome-tap awesome-tap} * {@link https://github.com/midknight41/map-factory map-factory} * {@link https://github.com/webpack/tapable tapable} * @see {@link underscore-tap} * @see {@link tapable} * @see {@link ramda-tap} * * @see ChainedMapBase.set * @see ChainedMapBase.get * * @example * * chain * .set('moose', {eh: true}) * .tap('moose', moose => {moose.eh = false; return moose}) * .get('moose') * * //=> {eh: false} * * @example * * const entries = new Chain() * .set('str', 'emptyish') * .tap('str', str => str + '+') * .set('arr', [1]) * .tap('arr', arr => arr.concat([2])) * .entries() * * //=> {str: 'emptyish+', arr: [1, 2]} * */ ChainedMapBase.prototype.tap = function(name, fn) { // get value, tap it, set it return this.set(name, fn(this.get(name), dopemerge)) } /** * @version 5.0.0 <- moved into ChainedMapBase & ChainedSet for less monomorphic usage * @since 5.0.0-beta.7 * @return {Array<number | string | *>} keys * * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/keys mozilla-set-keys} * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys mozilla-map-keys} * @see {@link mozilla-map-keys} * @see {@link mozilla-set-keys} * * @example * * Chain.set('eh', 1).keys() * //=> ['eh'] * */ ChainedMapBase.prototype.keys = function() { return ArrayFrom(this.store.keys()) } /** * @desc checks each property of the object * calls the chains accordingly * * @memberOf ChainedMapBase * @since 0.5.0 * * @param {Object} obj object with functions to hydrate from * @return {Chainable} @chainable * * @TODO could also add parsing stringified * * @example * * const from = new Chain().from({eh: true}) * const eh = new Chain().set('eh', true) * eq(from, eh) * //=> true * */ ChainedMapBase.prototype.from = function(obj) { var this$1 = this; var keys = ObjectKeys(obj) for (var k = 0; k < keys.length; k++) { var key = keys[k] var value = obj[key] var fn = this$1[key] if (hasMerge(fn)) { fn.merge(value) } else if (isFunction(fn)) { fn.call(this$1, value) } else { this$1.set(key, value) } } return this } /** * @desc shorthand methods, from strings to functions that call .set * @since 0.4.0 * @memberOf ChainedMapBase * * @param {Array<string>} methods decorates/extends an object with new shorthand functions to get/set * @return {ChainedMapBase} @chainable * * @example * * const chain1 = new Chain() * chain1.extend(['eh']) * * const chain2 = new Chain() * chain2.eh = val => this.set('eh', val) * * eq(chain2.eh, chain1.eh) * //=> true * */ ChainedMapBase.prototype.extend = function(methods) { var this$1 = this; methods.forEach(function (method) { this$1.meta(SHORTHANDS_KEY, method) this$1[method] = function (value) { return this$1.set(method, value); } }) return this } /** * @desc spreads the entries from ChainedMapBase.store (Map) * return store.entries, plus all chain properties if they exist * * @memberOf ChainedMapBase * @version 4.0.0 <- improved reducing * @since 0.4.0 * * @param {boolean} [chains=false] if true, returns all properties that are chains * @return {Object} reduced object containing all properties from the store, and when `chains` is true, all instance properties, and recursive chains * * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries mozilla-map-entries} * @see {@link mozilla-map-entries} * @see deps/reduce/entries * * @example * * map.set('a', 'alpha').set('b', 'beta').entries() * //=> {a: 'alpha', b: 'beta'} * */ ChainedMapBase.prototype.entries = function(chains) { var reduced = reduce(this.store) if (isUndefined(chains)) { return reduced } var reducer = reduceEntries(reduced) reducer(this) reducer(reduced) return reduced } /** * @desc get value for key path in the Map store * ❗ `debug` is a special key and is *not* included into .store * it goes onto .meta * * @memberOf ChainedMapBase * @version 4.0.0 <- moved debug here * @since 0.4.0 * * @param {Primitive} key Primitive data key used as map property to reference the value * @return {any} value in .store at key * * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get mozilla-map-get} * @see {@link mozilla-map-get} * * @example * * const chain = new Chain() * chain.set('eh', true) * chain.get('eh') * //=> true * * chain.get('nope') * //=> undefined * */ ChainedMapBase.prototype.get = function(key) { // @TODO move this back out... if (key === 'debug') { return this.meta.debug } return this.store.get(key) } /** * @desc sets the value using the key on store * adds or updates an element with a specified key and value * * @memberOf ChainedMapBase * @since 0.4.0 * * @param {Primitive} key Primitive to reference the value * @param {any} value any data to store * @return {ChainedMapBase} @chainable * * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set mozilla-map-set} * @see {@link mozilla-map-set} * @see ChainedMapBase.store * * @example * * const chain = new Chain() * chain.set('eh', true) * chain.get('eh') * //=> true * */ ChainedMapBase.prototype.set = function(key, value) { this.store.set(key, value) return this } return ChainedMapBase } /** * @desc ChainedMapBase composer * @alias ComposeMap * @type {Composer} * @method compose * @memberOf ChainedMapBase * * @param {Class | Object | Composable} [Target=Chainable] class to extend * @return {Class} ChainedMapBase * * @example * * const heh = class {} * const composed = ChainedMapBase.compose(heh) * const hehchain = new Composed() * hehchain instanceof heh * //=> true * */ var composed = composer(ComposeChainedMapBase, Chainable) module.exports = composed //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2hhaW5lZE1hcEJhc2UuanMiLCJzb3VyY2VzIjpbIkNoYWluZWRNYXBCYXNlLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnN0IFNIT1JUSEFORFNfS0VZID0gcmVxdWlyZSgnLi9kZXBzL21ldGEvU0hPUlRIQU5EU19LRVknKVxuY29uc3QgQ2hhaW5hYmxlID0gcmVxdWlyZSgnLi9DaGFpbmFibGUnKVxuY29uc3QgT2JqZWN0S2V5cyA9IHJlcXVpcmUoJy4vZGVwcy91dGlsL2tleXMnKVxuY29uc3QgQXJyYXlGcm9tID0gcmVxdWlyZSgnLi9kZXBzL3V0aWwvZnJvbScpXG5jb25zdCBkb3BlbWVyZ2UgPSByZXF1aXJlKCcuL2RlcHMvZG9wZW1lcmdlJylcbmNvbnN0IHJlZHVjZSA9IHJlcXVpcmUoJy4vZGVwcy9yZWR1Y2UnKVxuY29uc3QgcmVkdWNlRW50cmllcyA9IHJlcXVpcmUoJy4vZGVwcy9yZWR1Y2UvZW50cmllcycpXG5jb25zdCBpc0Z1bmN0aW9uID0gcmVxdWlyZSgnLi9kZXBzL2lzL2Z1bmN0aW9uJylcbmNvbnN0IGlzVW5kZWZpbmVkID0gcmVxdWlyZSgnLi9kZXBzL2lzL3VuZGVmaW5lZCcpXG5jb25zdCBnZXRNZXRhID0gcmVxdWlyZSgnLi9kZXBzL21ldGEnKVxuY29uc3QgaGFzT3duUHJvcGVydHlGbGlwcGVkID0gcmVxdWlyZSgnLi9kZXBzL2ZsaXBwZWQvaGFzT3duUHJvcGVydHlGbGlwcGVkJylcbmNvbnN0IGNvbXBvc2VyID0gcmVxdWlyZSgnLi9jb21wb3NlL2NvbXBvc2VyJylcbmNvbnN0IG5ld01hcCA9IHJlcXVpcmUoJy4vZGVwcy9jb25zdHJ1Y3QvbWFwJylcblxuY29uc3QgaGFzTWVyZ2UgPSBoYXNPd25Qcm9wZXJ0eUZsaXBwZWQoJ21lcmdlJylcblxuLyoqXG4gKiB0aGlzIGlzIHRvIGF2b2lkIGNpcmN1bGFyIHJlcXVpcmVzXG4gKiBiZWNhdXNlIE1lcmdlQ2hhaW4gJiBNZXRob2RDaGFpbiBleHRlbmQgdGhpc1xuICogeWV0IC5tZXRob2QgJiAubWVyZ2UgdXNlIHRob3NlIGNoYWluc1xuICogLi4uYWxzbywgaXQgc2VydmVzIGFzIGEgbm9uLXJlZmVyZW5jZXMgY3JlYXRvciBmb3IgZXh0ZW5kaW5nIG5ldyBpbnN0YW5jZXNcbiAqICAgIG9mIENoYWluYWJsZSwgd2hlcmUgaXQgc3BsaXRzIGludG8gKE1hcCB8IFNldCkgLT4gY29tcG9zZWQgcHJvdG90eXBlIGRlY29yYXRvcnNcbiAqXG4gKlxuICogQGZpbGVcbiAqIEBzaW5jZSA0LjAuMC1hbHBoYS4xXG4gKiBAaW5oZXJpdGRvY1xuICogQGNsYXNzIENoYWluZWRNYXBCYXNlXG4gKiBAbWVtYmVyIENoYWluZWRNYXBCYXNlXG4gKiBAY2F0ZWdvcnkgQ2hhaW5hYmxlXG4gKiBAZXh0ZW5kcyB7Q2hhaW5hYmxlfVxuICogQHR5cGUge0NoYWluYWJsZX1cbiAqXG4gKiBAdHlwZXMgQ2hhaW5lZE1hcEJhc2VcbiAqIEB0ZXN0cyBDaGFpbmVkTWFwXG4gKlxuICogQHByb3Age01ldGF9IG1ldGEgbWV0YSBmblxuICogQHByb3Age01hcH0gc3RvcmUgbWFpbiBzdG9yZVxuICpcbiAqIHtAbGluayBodHRwczovL3RjMzkuZ2l0aHViLmlvL2VjbWEyNjIvI3NlYy1tYXAtb2JqZWN0cyBlbWNhLW1hcH1cbiAqIHtAbGluayBodHRwczovL3Bvbnlmb28uY29tL2FydGljbGVzL2VzNi1tYXBzLWluLWRlcHRoIHBvbnktbWFwfVxuICoge0BsaW5rIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL01hcCBtb3ppbGxhLW1hcH1cbiAqIEBzZWUge0BsaW5rIHBvbnktbWFwfVxuICogQHNlZSB7QGxpbmsgbW96aWxsYS1tYXB9XG4gKiBAc2VlIHtAbGluayBlbWNhLW1hcH1cbiAqXG4gKiBAc2VlIENoYWluZWRNYXBcbiAqIEBzZWUgQ2hhaW5hYmxlXG4gKiBAc2VlIE1lcmdlQ2hhaW5cbiAqIEBzZWUgTWV0aG9kQ2hhaW5cbiAqIEBzZWUgQ2hhaW5lZE1hcFxuICpcbiAqL1xuY29uc3QgQ29tcG9zZUNoYWluZWRNYXBCYXNlID0gVGFyZ2V0ID0+IHtcbiAgY2xhc3MgQ2hhaW5lZE1hcEJhc2UgZXh0ZW5kcyBUYXJnZXQge1xuICAgIC8qKlxuICAgICAqIEBwYXJhbSB7Q2hhaW5lZE1hcEJhc2UgfCBDaGFpbmFibGUgfCBQYXJlbnRUeXBlIHwgYW55fSBwYXJlbnQgUGFyZW50VHlwZVxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqXG4gICAgICogQGV4YW1wbGVcbiAgICAgKlxuICAgICAqICAgIGNsYXNzIEVoIGV4dGVuZHMgQ2hhaW5lZE1hcEJhc2Uge31cbiAgICAgKiAgICBPYmplY3Qua2V5cyhlaClcbiAgICAgKiAgICAvLz0+IFsnc3RvcmUnLCAnbWV0YSddXG4gICAgICpcbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihwYXJlbnQpIHtcbiAgICAgIHN1cGVyKHBhcmVudClcbiAgICAgIHRoaXMuc3RvcmUgPSBuZXcgTWFwKClcbiAgICAgIHRoaXMubWV0YSA9IGdldE1ldGEodGhpcylcbiAgICB9XG4gIH1cblxuICAvLyBjb25zdCBjb25zdHJ1Y3RSZWYgPSBUYXJnZXQucHJvdG90eXBlLmNvbnN0cnVjdFxuICAvLyBDaGFpbmVkTWFwQmFzZS5wcm90b3R5cGUuY29uc3RydWN0ID0gZnVuY3Rpb24oKSB7XG4gIC8vICAgdGhpcy5zdG9yZSA9IG5ldyBNYXAoKVxuICAvLyAgIHRoaXMubWV0YSA9IGdldE1ldGEodGhpcylcbiAgLy8gICBpZiAoY29uc3RydWN0UmVmKSBjb25zdHJ1Y3RSZWYuY2FsbCh0aGlzKVxuICAvLyB9XG5cbiAgLyoqXG4gICAqIEBkZXNjIHRhcCBhIHZhbHVlIHdpdGggYSBmdW5jdGlvblxuICAgKiAgICAgICBAbW9kaWZpZXMgdGhpcy5zdG9yZS5nZXQobmFtZSlcbiAgICogICAgICAgSW52b2tlcyBpbnRlcmNlcHRvciB3aXRoIHRoZSBvYmosIGFuZCB0aGVuIHJldHVybnMgb2JqLlxuICAgKiAgICAgICBUaGUgcHJpbWFyeSBwdXJwb3NlIG9mIHRoaXMgbWV0aG9kIGlzIHRvIFwidGFwIGludG9cIiBhIG1ldGhvZCBjaGFpbiwgaW5cbiAgICogICAgICAgb3JkZXIgdG8gcGVyZm9ybSBvcGVyYXRpb25zIG9uIGludGVybWVkaWF0ZSByZXN1bHRzIHdpdGhpbiB0aGUgY2hhaW4uXG4gICAqXG4gICAqIEBtZW1iZXJPZiBDaGFpbmVkTWFwQmFzZVxuICAgKiBAdmVyc2lvbiAwLjcuMFxuICAgKiBAc2luY2UgNC4wLjAtYWxwaGEuMSA8LSBtb3ZlZCBmcm9tIHRyYW5zZm9ybSAmIHNob3J0aGFuZHNcbiAgICpcbiAgICogQHBhcmFtICB7c3RyaW5nIHwgYW55fSBuYW1lIGtleSB0byBgLmdldGBcbiAgICogQHBhcmFtICB7RnVuY3Rpb259IGZuIGZ1bmN0aW9uIHRvIHRhcCB3aXRoXG4gICAqIEByZXR1cm4ge0NoYWlufSBAY2hhaW5hYmxlXG4gICAqXG4gICAqIHtAbGluayBodHRwczovL2dpdGh1Yi5jb20vamFzaGtlbmFzL3VuZGVyc2NvcmUvYmxvYi9tYXN0ZXIvdW5kZXJzY29yZS5qcyNMMTE2MSB1bmRlcnNjb3JlLXRhcH1cbiAgICoge0BsaW5rIGh0dHBzOi8vZ2l0aHViLmNvbS9yYW1kYS9yYW1kYS9ibG9iL21hc3Rlci9zcmMvaW50ZXJuYWwvX3h0YXAuanMgcmFtZGEteHRhcH1cbiAgICoge0BsaW5rIGh0dHBzOi8vZ2l0aHViLmNvbS9yYW1kYS9yYW1kYS9ibG9iL21hc3Rlci9zcmMvdGFwLmpzIHJhbWRhLXRhcH1cbiAgICoge0BsaW5rIGh0dHBzOi8vZ2l0aHViLmNvbS9zaW5kcmVzb3JodXMvYXdlc29tZS10YXAgYXdlc29tZS10YXB9XG4gICAqIHtAbGluayBodHRwczovL2dpdGh1Yi5jb20vbWlka25pZ2h0NDEvbWFwLWZhY3RvcnkgbWFwLWZhY3Rvcnl9XG4gICAqIHtAbGluayBodHRwczovL2dpdGh1Yi5jb20vd2VicGFjay90YXBhYmxlIHRhcGFibGV9XG4gICAqIEBzZWUge0BsaW5rIHVuZGVyc2NvcmUtdGFwfVxuICAgKiBAc2VlIHtAbGluayB0YXBhYmxlfVxuICAgKiBAc2VlIHtAbGluayByYW1kYS10YXB9XG4gICAqXG4gICAqIEBzZWUgQ2hhaW5lZE1hcEJhc2Uuc2V0XG4gICAqIEBzZWUgQ2hhaW5lZE1hcEJhc2UuZ2V0XG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqXG4gICAqICAgIGNoYWluXG4gICAqICAgICAgLnNldCgnbW9vc2UnLCB7ZWg6IHRydWV9KVxuICAgKiAgICAgIC50YXAoJ21vb3NlJywgbW9vc2UgPT4ge21vb3NlLmVoID0gZmFsc2U7IHJldHVybiBtb29zZX0pXG4gICAqICAgICAgLmdldCgnbW9vc2UnKVxuICAgKlxuICAgKiAgICAvLz0+IHtlaDogZmFsc2V9XG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqXG4gICAqICAgY29uc3QgZW50cmllcyA9IG5ldyBDaGFpbigpXG4gICAqICAgICAuc2V0KCdzdHInLCAnZW1wdHlpc2gnKVxuICAgKiAgICAgLnRhcCgnc3RyJywgc3RyID0+IHN0ciArICcrJylcbiAgICogICAgIC5zZXQoJ2FycicsIFsxXSlcbiAgICogICAgIC50YXAoJ2FycicsIGFyciA9PiBhcnIuY29uY2F0KFsyXSkpXG4gICAqICAgICAuZW50cmllcygpXG4gICAqXG4gICAqICAgLy89PiB7c3RyOiAnZW1wdHlpc2grJywgYXJyOiBbMSwgMl19XG4gICAqXG4gICAqL1xuICBDaGFpbmVkTWFwQmFzZS5wcm90b3R5cGUudGFwID0gZnVuY3Rpb24obmFtZSwgZm4pIHtcbiAgICAvLyBnZXQgdmFsdWUsIHRhcCBpdCwgc2V0IGl0XG4gICAgcmV0dXJuIHRoaXMuc2V0KG5hbWUsIGZuKHRoaXMuZ2V0KG5hbWUpLCBkb3BlbWVyZ2UpKVxuICB9XG5cbiAgLyoqXG4gICAqIEB2ZXJzaW9uIDUuMC4wIDwtIG1vdmVkIGludG8gQ2hhaW5lZE1hcEJhc2UgJiBDaGFpbmVkU2V0IGZvciBsZXNzIG1vbm9tb3JwaGljIHVzYWdlXG4gICAqIEBzaW5jZSA1LjAuMC1iZXRhLjdcbiAgICogQHJldHVybiB7QXJyYXk8bnVtYmVyIHwgc3RyaW5nIHwgKj59IGtleXNcbiAgICpcbiAgICoge0BsaW5rIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL1NldC9rZXlzIG1vemlsbGEtc2V0LWtleXN9XG4gICAqIHtAbGluayBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9NYXAva2V5cyBtb3ppbGxhLW1hcC1rZXlzfVxuICAgKiBAc2VlIHtAbGluayBtb3ppbGxhLW1hcC1rZXlzfVxuICAgKiBAc2VlIHtAbGluayBtb3ppbGxhLXNldC1rZXlzfVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKlxuICAgKiAgICBDaGFpbi5zZXQoJ2VoJywgMSkua2V5cygpXG4gICAqICAgIC8vPT4gWydlaCddXG4gICAqXG4gICAqL1xuICBDaGFpbmVkTWFwQmFzZS5wcm90b3R5cGUua2V5cyA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBBcnJheUZyb20odGhpcy5zdG9yZS5rZXlzKCkpXG4gIH1cblxuICAvKipcbiAgICogQGRlc2MgY2hlY2tzIGVhY2ggcHJvcGVydHkgb2YgdGhlIG9iamVjdFxuICAgKiAgICAgICBjYWxscyB0aGUgY2hhaW5zIGFjY29yZGluZ2x5XG4gICAqXG4gICAqIEBtZW1iZXJPZiBDaGFpbmVkTWFwQmFzZVxuICAgKiBAc2luY2UgMC41LjBcbiAgICpcbiAgICogQHBhcmFtIHtPYmplY3R9IG9iaiBvYmplY3Qgd2l0aCBmdW5jdGlvbnMgdG8gaHlkcmF0ZSBmcm9tXG4gICAqIEByZXR1cm4ge0NoYWluYWJsZX0gQGNoYWluYWJsZVxuICAgKlxuICAgKiBAVE9ETyBjb3VsZCBhbHNvIGFkZCBwYXJzaW5nIHN0cmluZ2lmaWVkXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqXG4gICAqICAgICBjb25zdCBmcm9tID0gbmV3IENoYWluKCkuZnJvbSh7ZWg6IHRydWV9KVxuICAgKiAgICAgY29uc3QgZWggPSBuZXcgQ2hhaW4oKS5zZXQoJ2VoJywgdHJ1ZSlcbiAgICogICAgIGVxKGZyb20sIGVoKVxuICAgKiAgICAgLy89PiB0cnVlXG4gICAqXG4gICAqL1xuICBDaGFpbmVkTWFwQmFzZS5wcm90b3R5cGUuZnJvbSA9IGZ1bmN0aW9uKG9iaikge1xuICAgIGNvbnN0IGtleXMgPSBPYmplY3RLZXlzKG9iailcblxuICAgIGZvciAobGV0IGsgPSAwOyBrIDwga2V5cy5sZW5ndGg7IGsrKykge1xuICAgICAgY29uc3Qga2V5ID0ga2V5c1trXVxuICAgICAgY29uc3QgdmFsdWUgPSBvYmpba2V5XVxuICAgICAgY29uc3QgZm4gPSB0aGlzW2tleV1cblxuICAgICAgaWYgKGhhc01lcmdlKGZuKSkge1xuICAgICAgICBmbi5tZXJnZSh2YWx1ZSlcbiAgICAgIH1cbiAgICAgIGVsc2UgaWYgKGlzRnVuY3Rpb24oZm4pKSB7XG4gICAgICAgIGZuLmNhbGwodGhpcywgdmFsdWUpXG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgdGhpcy5zZXQoa2V5LCB2YWx1ZSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdGhpc1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjIHNob3J0aGFuZCBtZXRob2RzLCBmcm9tIHN0cmluZ3MgdG8gZnVuY3Rpb25zIHRoYXQgY2FsbCAuc2V0XG4gICAqIEBzaW5jZSAwLjQuMFxuICAgKiBAbWVtYmVyT2YgQ2hhaW5lZE1hcEJhc2VcbiAgICpcbiAgICogQHBhcmFtICB7QXJyYXk8c3RyaW5nPn0gbWV0aG9kcyBkZWNvcmF0ZXMvZXh0ZW5kcyBhbiBvYmplY3Qgd2l0aCBuZXcgc2hvcnRoYW5kIGZ1bmN0aW9ucyB0byBnZXQvc2V0XG4gICAqIEByZXR1cm4ge0NoYWluZWRNYXBCYXNlfSBAY2hhaW5hYmxlXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqXG4gICAqICAgIGNvbnN0IGNoYWluMSA9IG5ldyBDaGFpbigpXG4gICAqICAgIGNoYWluMS5leHRlbmQoWydlaCddKVxuICAgKlxuICAgKiAgICBjb25zdCBjaGFpbjIgPSBuZXcgQ2hhaW4oKVxuICAgKiAgICBjaGFpbjIuZWggPSB2YWwgPT4gdGhpcy5zZXQoJ2VoJywgdmFsKVxuICAgKlxuICAgKiAgICBlcShjaGFpbjIuZWgsIGNoYWluMS5laClcbiAgICogICAgLy89PiB0cnVlXG4gICAqXG4gICAqL1xuICBDaGFpbmVkTWFwQmFzZS5wcm90b3R5cGUuZXh0ZW5kID0gZnVuY3Rpb24obWV0aG9kcykge1xuICAgIG1ldGhvZHMuZm9yRWFjaChtZXRob2QgPT4ge1xuICAgICAgdGhpcy5tZXRhKFNIT1JUSEFORFNfS0VZLCBtZXRob2QpXG4gICAgICB0aGlzW21ldGhvZF0gPSB2YWx1ZSA9PiB0aGlzLnNldChtZXRob2QsIHZhbHVlKVxuICAgIH0pXG4gICAgcmV0dXJuIHRoaXNcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzYyBzcHJlYWRzIHRoZSBlbnRyaWVzIGZyb20gQ2hhaW5lZE1hcEJhc2Uuc3RvcmUgKE1hcClcbiAgICogICAgICAgcmV0dXJuIHN0b3JlLmVudHJpZXMsIHBsdXMgYWxsIGNoYWluIHByb3BlcnRpZXMgaWYgdGhleSBleGlzdFxuICAgKlxuICAgKiBAbWVtYmVyT2YgQ2hhaW5lZE1hcEJhc2VcbiAgICogQHZlcnNpb24gNC4wLjAgPC0gaW1wcm92ZWQgcmVkdWNpbmdcbiAgICogQHNpbmNlIDAuNC4wXG4gICAqXG4gICAqIEBwYXJhbSAge2Jvb2xlYW59IFtjaGFpbnM9ZmFsc2VdIGlmIHRydWUsIHJldHVybnMgYWxsIHByb3BlcnRpZXMgdGhhdCBhcmUgY2hhaW5zXG4gICAqIEByZXR1cm4ge09iamVjdH0gcmVkdWNlZCBvYmplY3QgY29udGFpbmluZyBhbGwgcHJvcGVydGllcyBmcm9tIHRoZSBzdG9yZSwgYW5kIHdoZW4gYGNoYWluc2AgaXMgdHJ1ZSwgYWxsIGluc3RhbmNlIHByb3BlcnRpZXMsIGFuZCByZWN1cnNpdmUgY2hhaW5zXG4gICAqXG4gICAqIHtAbGluayBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9NYXAvZW50cmllcyBtb3ppbGxhLW1hcC1lbnRyaWVzfVxuICAgKiBAc2VlIHtAbGluayBtb3ppbGxhLW1hcC1lbnRyaWVzfVxuICAgKiBAc2VlIGRlcHMvcmVkdWNlL2VudHJpZXNcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICpcbiAgICogICAgbWFwLnNldCgnYScsICdhbHBoYScpLnNldCgnYicsICdiZXRhJykuZW50cmllcygpXG4gICAqICAgIC8vPT4ge2E6ICdhbHBoYScsIGI6ICdiZXRhJ31cbiAgICpcbiAgICovXG4gIENoYWluZWRNYXBCYXNlLnByb3RvdHlwZS5lbnRyaWVzID0gZnVuY3Rpb24oY2hhaW5zKSB7XG4gICAgY29uc3QgcmVkdWNlZCA9IHJlZHVjZSh0aGlzLnN0b3JlKVxuICAgIGlmIChpc1VuZGVmaW5lZChjaGFpbnMpKSByZXR1cm4gcmVkdWNlZFxuXG4gICAgY29uc3QgcmVkdWNlciA9IHJlZHVjZUVudHJpZXMocmVkdWNlZClcbiAgICByZWR1Y2VyKHRoaXMpXG4gICAgcmVkdWNlcihyZWR1Y2VkKVxuICAgIHJldHVybiByZWR1Y2VkXG4gIH1cblxuICAvKipcbiAgICogQGRlc2MgZ2V0IHZhbHVlIGZvciBrZXkgcGF0aCBpbiB0aGUgTWFwIHN0b3JlXG4gICAqICAgICAgIOKdlyBgZGVidWdgIGlzIGEgc3BlY2lhbCBrZXkgYW5kIGlzICpub3QqIGluY2x1ZGVkIGludG8gLnN0b3JlXG4gICAqICAgICAgICAgIGl0IGdvZXMgb250byAubWV0YVxuICAgKlxuICAgKiBAbWVtYmVyT2YgQ2hhaW5lZE1hcEJhc2VcbiAgICogQHZlcnNpb24gNC4wLjAgPC0gbW92ZWQgZGVidWcgaGVyZVxuICAgKiBAc2luY2UgMC40LjBcbiAgICpcbiAgICogQHBhcmFtICB7UHJpbWl0aXZlfSBrZXkgUHJpbWl0aXZlIGRhdGEga2V5IHVzZWQgYXMgbWFwIHByb3BlcnR5IHRvIHJlZmVyZW5jZSB0aGUgdmFsdWVcbiAgICogQHJldHVybiB7YW55fSB2YWx1ZSBpbiAuc3RvcmUgYXQga2V5XG4gICAqXG4gICAqIHtAbGluayBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9NYXAvZ2V0IG1vemlsbGEtbWFwLWdldH1cbiAgICogQHNlZSB7QGxpbmsgbW96aWxsYS1tYXAtZ2V0fVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKlxuICAgKiAgICBjb25zdCBjaGFpbiA9IG5ldyBDaGFpbigpXG4gICAqICAgIGNoYWluLnNldCgnZWgnLCB0cnVlKVxuICAgKiAgICBjaGFpbi5nZXQoJ2VoJylcbiAgICogICAgLy89PiB0cnVlXG4gICAqXG4gICAqICAgIGNoYWluLmdldCgnbm9wZScpXG4gICAqICAgIC8vPT4gdW5kZWZpbmVkXG4gICAqXG4gICAqL1xuICBDaGFpbmVkTWFwQmFzZS5wcm90b3R5cGUuZ2V0ID0gZnVuY3Rpb24oa2V5KSB7XG4gICAgLy8gQFRPRE8gbW92ZSB0aGlzIGJhY2sgb3V0Li4uXG4gICAgaWYgKGtleSA9PT0gJ2RlYnVnJykgcmV0dXJuIHRoaXMubWV0YS5kZWJ1Z1xuICAgIHJldHVybiB0aGlzLnN0b3JlLmdldChrZXkpXG4gIH1cblxuICAvKipcbiAgICogQGRlc2Mgc2V0cyB0aGUgdmFsdWUgdXNpbmcgdGhlIGtleSBvbiBzdG9yZVxuICAgKiAgICAgICBhZGRzIG9yIHVwZGF0ZXMgYW4gZWxlbWVudCB3aXRoIGEgc3BlY2lmaWVkIGtleSBhbmQgdmFsdWVcbiAgICpcbiAgICogQG1lbWJlck9mIENoYWluZWRNYXBCYXNlXG4gICAqIEBzaW5jZSAwLjQuMFxuICAgKlxuICAgKiBAcGFyYW0ge1ByaW1pdGl2ZX0ga2V5IFByaW1pdGl2ZSB0byByZWZlcmVuY2UgdGhlIHZhbHVlXG4gICAqIEBwYXJhbSB7YW55fSB2YWx1ZSBhbnkgZGF0YSB0byBzdG9yZVxuICAgKiBAcmV0dXJuIHtDaGFpbmVkTWFwQmFzZX0gQGNoYWluYWJsZVxuICAgKlxuICAgKiB7QGxpbmsgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWFwL3NldCBtb3ppbGxhLW1hcC1zZXR9XG4gICAqIEBzZWUge0BsaW5rIG1vemlsbGEtbWFwLXNldH1cbiAgICogQHNlZSBDaGFpbmVkTWFwQmFzZS5zdG9yZVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKlxuICAgKiAgICBjb25zdCBjaGFpbiA9IG5ldyBDaGFpbigpXG4gICAqICAgIGNoYWluLnNldCgnZWgnLCB0cnVlKVxuICAgKiAgICBjaGFpbi5nZXQoJ2VoJylcbiAgICogICAgLy89PiB0cnVlXG4gICAqXG4gICAqL1xuICBDaGFpbmVkTWFwQmFzZS5wcm90b3R5cGUuc2V0ID0gZnVuY3Rpb24oa2V5LCB2YWx1ZSkge1xuICAgIHRoaXMuc3RvcmUuc2V0KGtleSwgdmFsdWUpXG4gICAgcmV0dXJuIHRoaXNcbiAgfVxuXG4gIHJldHVybiBDaGFpbmVkTWFwQmFzZVxufVxuXG4vKipcbiAqIEBkZXNjIENoYWluZWRNYXBCYXNlIGNvbXBvc2VyXG4gKiBAYWxpYXMgQ29tcG9zZU1hcFxuICogQHR5cGUge0NvbXBvc2VyfVxuICogQG1ldGhvZCBjb21wb3NlXG4gKiBAbWVtYmVyT2YgQ2hhaW5lZE1hcEJhc2VcbiAqXG4gKiBAcGFyYW0ge0NsYXNzIHwgT2JqZWN0IHwgQ29tcG9zYWJsZX0gW1RhcmdldD1DaGFpbmFibGVdIGNsYXNzIHRvIGV4dGVuZFxuICogQHJldHVybiB7Q2xhc3N9IENoYWluZWRNYXBCYXNlXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiAgICBjb25zdCBoZWggPSBjbGFzcyB7fVxuICogICAgY29uc3QgY29tcG9zZWQgPSBDaGFpbmVkTWFwQmFzZS5jb21wb3NlKGhlaClcbiAqICAgIGNvbnN0IGhlaGNoYWluID0gbmV3IENvbXBvc2VkKClcbiAqICAgIGhlaGNoYWluIGluc3RhbmNlb2YgaGVoXG4gKiAgICAvLz0+IHRydWVcbiAqXG4gKi9cblxuY29uc3QgY29tcG9zZWQgPSBjb21wb3NlcihDb21wb3NlQ2hhaW5lZE1hcEJhc2UsIENoYWluYWJsZSlcblxubW9kdWxlLmV4cG9ydHMgPSBjb21wb3NlZFxuIl0sIm5hbWVzIjpbImNvbnN0Iiwic3VwZXIiLCJsZXQiLCJ0aGlzIl0sIm1hcHBpbmdzIjoiQUFBQUEsR0FBSyxDQUFDLGNBQWMsR0FBRyxPQUFPLENBQUMsNEJBQTRCLENBQUM7QUFDNURBLEdBQUssQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLGFBQWEsQ0FBQztBQUN4Q0EsR0FBSyxDQUFDLFVBQVUsR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUM7QUFDOUNBLEdBQUssQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLGtCQUFrQixDQUFDO0FBQzdDQSxHQUFLLENBQUMsU0FBUyxHQUFHLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQztBQUM3Q0EsR0FBSyxDQUFDLE1BQU0sR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDO0FBQ3ZDQSxHQUFLLENBQUMsYUFBYSxHQUFHLE9BQU8sQ0FBQyx1QkFBdUIsQ0FBQztBQUN0REEsR0FBSyxDQUFDLFVBQVUsR0FBRyxPQUFPLENBQUMsb0JBQW9CLENBQUM7QUFDaERBLEdBQUssQ0FBQyxXQUFXLEdBQUcsT0FBTyxDQUFDLHFCQUFxQixDQUFDO0FBQ2xEQSxHQUFLLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQyxhQUFhLENBQUM7QUFDdENBLEdBQUssQ0FBQyxxQkFBcUIsR0FBRyxPQUFPLENBQUMsc0NBQXNDLENBQUM7QUFDN0VBLEdBQUssQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDLG9CQUFvQixDQUFDO0FBQzlDQSxHQUFLLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxzQkFBc0IsQ0FBQzs7QUFFOUNBLEdBQUssQ0FBQyxRQUFRLEdBQUcscUJBQXFCLENBQUMsT0FBTyxDQUFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF1Qy9DQSxHQUFLLENBQUMscUJBQXFCLEdBQUcsVUFBQSxNQUFNLENBQUEsQ0FBQyxBQUFHO0VBQ3RDLElBQU0sY0FBYyxHQUFlO0lBQUMsQUFZbEMsdUJBQVcsQ0FBQyxNQUFNLEVBQUU7TUFDbEJDLE1BQUssS0FBQSxDQUFDLE1BQUEsTUFBTSxDQUFDO01BQ2IsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLEdBQUcsRUFBRTtNQUN0QixJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUM7S0FDMUI7Ozs7MERBQUEsQUFDRjs7O0lBakI0QixNQWlCNUIsR0FBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQTBERCxjQUFjLENBQUMsU0FBUyxDQUFDLEdBQUcsR0FBRyxTQUFTLElBQUksRUFBRSxFQUFFLEVBQUU7O0lBRWhELE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUM7R0FDckQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQWtCRCxjQUFjLENBQUMsU0FBUyxDQUFDLElBQUksR0FBRyxXQUFXO0lBQ3pDLE9BQU8sU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7R0FDcEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUFzQkQsY0FBYyxDQUFDLFNBQVMsQ0FBQyxJQUFJLEdBQUcsU0FBUyxHQUFHLEVBQUUsQ0FBQzs7QUFBQTtJQUM3Q0QsR0FBSyxDQUFDLElBQUksR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDOztJQUU1QixLQUFLRSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRTtNQUNwQ0YsR0FBSyxDQUFDLEdBQUcsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDO01BQ25CQSxHQUFLLENBQUMsS0FBSyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUM7TUFDdEJBLEdBQUssQ0FBQyxFQUFFLEdBQUdHLE1BQUksQ0FBQyxHQUFHLENBQUM7O01BRXBCLElBQUksUUFBUSxDQUFDLEVBQUUsQ0FBQyxFQUFFO1FBQ2hCLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDO09BQ2hCO1dBQ0ksSUFBSSxVQUFVLENBQUMsRUFBRSxDQUFDLEVBQUU7UUFDdkIsRUFBRSxDQUFDLElBQUksQ0FBQ0EsTUFBSSxFQUFFLEtBQUssQ0FBQztPQUNyQjtXQUNJO1FBQ0hBLE1BQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQztPQUNyQjtLQUNGOztJQUVELE9BQU8sSUFBSTtHQUNaOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0VBc0JELGNBQWMsQ0FBQyxTQUFTLENBQUMsTUFBTSxHQUFHLFNBQVMsT0FBTyxFQUFFLENBQUM7O0FBQUE7SUFDbkQsT0FBTyxDQUFDLE9BQU8sQ0FBQyxVQUFBLE1BQU0sQ0FBQSxDQUFDLEFBQUc7TUFDeEJBLE1BQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLE1BQU0sQ0FBQztNQUNqQ0EsTUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLFVBQUEsS0FBSyxDQUFBLENBQUMsQUFBRyxTQUFBQSxNQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsR0FBQTtLQUNoRCxDQUFDO0lBQ0YsT0FBTyxJQUFJO0dBQ1o7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0VBdUJELGNBQWMsQ0FBQyxTQUFTLENBQUMsT0FBTyxHQUFHLFNBQVMsTUFBTSxFQUFFO0lBQ2xESCxHQUFLLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDO0lBQ2xDLElBQUksV0FBVyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEVBQUEsT0FBTyxPQUFPLEVBQUE7O0lBRXZDQSxHQUFLLENBQUMsT0FBTyxHQUFHLGFBQWEsQ0FBQyxPQUFPLENBQUM7SUFDdEMsT0FBTyxDQUFDLElBQUksQ0FBQztJQUNiLE9BQU8sQ0FBQyxPQUFPLENBQUM7SUFDaEIsT0FBTyxPQUFPO0dBQ2Y7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUE0QkQsY0FBYyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEdBQUcsU0FBUyxHQUFHLEVBQUU7O0lBRTNDLElBQUksR0FBRyxLQUFLLE9BQU8sRUFBRSxFQUFBLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUE7SUFDM0MsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUM7R0FDM0I7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUF5QkQsY0FBYyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEdBQUcsU0FBUyxHQUFHLEVBQUUsS0FBSyxFQUFFO0lBQ2xELElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUM7SUFDMUIsT0FBTyxJQUFJO0dBQ1o7O0VBRUQsT0FBTyxjQUFjO0NBQ3RCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBc0JEQSxHQUFLLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQyxxQkFBcUIsRUFBRSxTQUFTLENBQUM7O0FBRTNELE1BQU0sQ0FBQyxPQUFPLEdBQUcsUUFBUTsifQ==