975 lines
100 KiB
JavaScript
975 lines
100 KiB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
/* eslint-env browser */
|
|
|
|
/** @namespace cssInjector */
|
|
|
|
/**
|
|
* @summary Insert base stylesheet into DOM
|
|
*
|
|
* @desc Creates a new `<style>...</style>` element from the named text string(s) and inserts it but only if it does not already exist in the specified container as per `referenceElement`.
|
|
*
|
|
* > Caveat: If stylesheet is for use in a shadow DOM, you must specify a local `referenceElement`.
|
|
*
|
|
* @returns A reference to the newly created `<style>...</style>` element.
|
|
*
|
|
* @param {string|string[]} cssRules
|
|
* @param {string} [ID]
|
|
* @param {undefined|null|Element|string} [referenceElement] - Container for insertion. Overloads:
|
|
* * `undefined` type (or omitted): injects stylesheet at top of `<head>...</head>` element
|
|
* * `null` value: injects stylesheet at bottom of `<head>...</head>` element
|
|
* * `Element` type: injects stylesheet immediately before given element, wherever it is found.
|
|
* * `string` type: injects stylesheet immediately before given first element found that matches the given css selector.
|
|
*
|
|
* @memberOf cssInjector
|
|
*/
|
|
function cssInjector(cssRules, ID, referenceElement) {
|
|
if (typeof referenceElement === 'string') {
|
|
referenceElement = document.querySelector(referenceElement);
|
|
if (!referenceElement) {
|
|
throw 'Cannot find reference element for CSS injection.';
|
|
}
|
|
} else if (referenceElement && !(referenceElement instanceof Element)) {
|
|
throw 'Given value not a reference element.';
|
|
}
|
|
|
|
var container = referenceElement && referenceElement.parentNode || document.head || document.getElementsByTagName('head')[0];
|
|
|
|
if (ID) {
|
|
ID = cssInjector.idPrefix + ID;
|
|
|
|
if (container.querySelector('#' + ID)) {
|
|
return; // stylesheet already in DOM
|
|
}
|
|
}
|
|
|
|
var style = document.createElement('style');
|
|
style.type = 'text/css';
|
|
if (ID) {
|
|
style.id = ID;
|
|
}
|
|
if (cssRules instanceof Array) {
|
|
cssRules = cssRules.join('\n');
|
|
}
|
|
cssRules = '\n' + cssRules + '\n';
|
|
if (style.styleSheet) {
|
|
style.styleSheet.cssText = cssRules;
|
|
} else {
|
|
style.appendChild(document.createTextNode(cssRules));
|
|
}
|
|
|
|
if (referenceElement === undefined) {
|
|
referenceElement = container.firstChild;
|
|
}
|
|
|
|
container.insertBefore(style, referenceElement);
|
|
|
|
return style;
|
|
}
|
|
|
|
/**
|
|
* @summary Optional prefix for `<style>` tag IDs.
|
|
* @desc Defaults to `'injected-stylesheet-'`.
|
|
* @type {string}
|
|
* @memberOf cssInjector
|
|
*/
|
|
cssInjector.idPrefix = 'injected-stylesheet-';
|
|
|
|
// Interface
|
|
module.exports = cssInjector;
|
|
|
|
},{}],2:[function(require,module,exports){
|
|
/* eslint-env browser */
|
|
|
|
'use strict';
|
|
|
|
if (!window.FinBar) {
|
|
window.FinBar = require('./');
|
|
}
|
|
|
|
},{"./":3}],3:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
/* eslint-env node, browser */
|
|
|
|
var cssInjector = require('css-injector');
|
|
|
|
// Following is the sole style requirement for bar and thumb elements.
|
|
// Maintained in code so not dependent being in stylesheet.
|
|
var BAR_STYLE = 'position: absolute;';
|
|
var THUMB_STYLE = 'position: absolute;';
|
|
|
|
/**
|
|
* @constructor FinBar
|
|
* @summary Create a scrollbar object.
|
|
* @desc Creating a scrollbar is a three-step process:
|
|
*
|
|
* 1. Instantiate the scrollbar object by calling this constructor function. Upon instantiation, the DOM element for the scrollbar (with a single child element for the scrollbar "thumb") is created but is not insert it into the DOM.
|
|
* 2. After instantiation, it is the caller's responsibility to insert the scrollbar, {@link FinBar#bar|this.bar}, into the DOM.
|
|
* 3. After insertion, the caller must call {@link FinBar#resize|resize()} at least once to size and position the scrollbar and its thumb. After that, `resize()` should also be called repeatedly on resize events (as the content element is being resized).
|
|
*
|
|
* Suggested configurations:
|
|
* * _**Unbound**_<br/>
|
|
* The scrollbar serves merely as a simple range (slider) control. Omit both `options.onchange` and `options.content`.
|
|
* * _**Bound to virtual content element**_<br/>
|
|
* Virtual content is projected into the element using a custom event handler supplied by the programmer in `options.onchange`. A typical use case would be to handle scrolling of the virtual content. Other use cases include data transformations, graphics transformations, _etc._
|
|
* * _**Bound to real content**_<br/>
|
|
* Set `options.content` to the "real" content element but omit `options.onchange`. This will cause the scrollbar to use the built-in event handler (`this.scrollRealContent`) which implements smooth scrolling of the content element within the container.
|
|
*
|
|
* @param {finbarOptions} [options={}] - Options object. See the type definition for member details.
|
|
*/
|
|
function FinBar(options) {
|
|
|
|
// make bound versions of all the mouse event handler
|
|
var bound = this._bound = {};
|
|
Object.keys(handlersToBeBound).forEach(function (key) {
|
|
bound[key] = handlersToBeBound[key].bind(this);
|
|
}, this);
|
|
|
|
/**
|
|
* @name thumb
|
|
* @summary The generated scrollbar thumb element.
|
|
* @desc The thumb element's parent element is always the {@link FinBar#bar|bar} element.
|
|
*
|
|
* This property is typically referenced internally only. The size and position of the thumb element is maintained by `_calcThumb()`.
|
|
* @type {Element}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
var thumb = this.thumb = document.createElement('div');
|
|
thumb.classList.add('thumb');
|
|
thumb.setAttribute('style', THUMB_STYLE);
|
|
thumb.onclick = bound.shortStop;
|
|
thumb.onmouseover = bound.onmouseover;
|
|
thumb.onmouseout = this._bound.onmouseout;
|
|
|
|
/**
|
|
* @name bar
|
|
* @summary The generated scrollbar element.
|
|
* @desc The caller inserts this element into the DOM (typically into the content container) and then calls its {@link FinBar#resize|resize()} method.
|
|
*
|
|
* Thus the node tree is typically:
|
|
* * A **content container** element, which contains:
|
|
* * The content element(s)
|
|
* * This **scrollbar element**, which in turn contains:
|
|
* * The **thumb element**
|
|
*
|
|
* @type {Element}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
var bar = this.bar = document.createElement('div');
|
|
bar.classList.add('finbar-vertical');
|
|
bar.setAttribute('style', BAR_STYLE);
|
|
bar.onmousedown = this._bound.onmousedown;
|
|
if (this.paging) { bar.onclick = bound.onclick; }
|
|
bar.appendChild(thumb);
|
|
|
|
options = options || {};
|
|
|
|
// presets
|
|
this.orientation = 'vertical';
|
|
this._min = this._index = 0;
|
|
this._max = 100;
|
|
|
|
/**
|
|
* Wheel metric normalization, applied equally to all three axes.
|
|
*
|
|
* This value is overridden with a platform- and browser-specific wheel factor when available in {@link FinBar.normals}.
|
|
*
|
|
* To suppress, delete `FinBar.normals` before instantiation or override this instance variable (with `1.0`) after instantiation.
|
|
* @type {number}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
this.normal = getNormal() || 1.0;
|
|
|
|
// options
|
|
Object.keys(options).forEach(function (key) {
|
|
var option = options[key];
|
|
if (option !== undefined) {
|
|
switch (key) {
|
|
|
|
case 'index':
|
|
this._index = option;
|
|
break;
|
|
|
|
case 'range':
|
|
validRange(option);
|
|
this._min = option.min;
|
|
this._max = option.max;
|
|
this.contentSize = option.max - option.min + 1;
|
|
break;
|
|
|
|
default:
|
|
if (
|
|
key.charAt(0) !== '_' &&
|
|
typeof FinBar.prototype[key] !== 'function'
|
|
) {
|
|
// override prototype defaults for standard ;
|
|
// extend with additional properties (for use in onchange event handlers)
|
|
this[key] = option;
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
}, this);
|
|
|
|
cssInjector(cssFinBars, 'finbar-base', options.cssStylesheetReferenceElement);
|
|
}
|
|
|
|
FinBar.prototype = {
|
|
|
|
/**
|
|
* @summary The scrollbar orientation.
|
|
* @desc Set by the constructor to either `'vertical'` or `'horizontal'`. See the similarly named property in the {@link finbarOptions} object.
|
|
*
|
|
* Useful values are `'vertical'` (the default) or `'horizontal'`.
|
|
*
|
|
* Setting this property resets `this.oh` and `this.deltaProp` and changes the class names so as to reposition the scrollbar as per the CSS rules for the new orientation.
|
|
* @default 'vertical'
|
|
* @type {string}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
set orientation(orientation) {
|
|
if (orientation === this._orientation) {
|
|
return;
|
|
}
|
|
|
|
this._orientation = orientation;
|
|
|
|
/**
|
|
* @readonly
|
|
* @name oh
|
|
* @summary <u>O</u>rientation <u>h</u>ash for this scrollbar.
|
|
* @desc Set by the `orientation` setter to either the vertical or the horizontal orientation hash. The property should always be synchronized with `orientation`; do not update directly!
|
|
*
|
|
* This object is used internally to access scrollbars' DOM element properties in a generalized way without needing to constantly query the scrollbar orientation. For example, instead of explicitly coding `this.bar.top` for a vertical scrollbar and `this.bar.left` for a horizontal scrollbar, simply code `this.bar[this.oh.leading]` instead. See the {@link orientationHashType} definition for details.
|
|
*
|
|
* This object is useful externally for coding generalized {@link finbarOnChange} event handler functions that serve both horizontal and vertical scrollbars.
|
|
* @type {orientationHashType}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
this.oh = orientationHashes[this._orientation];
|
|
|
|
if (!this.oh) {
|
|
error('Invalid value for `options._orientation.');
|
|
}
|
|
|
|
/**
|
|
* @name deltaProp
|
|
* @summary The name of the `WheelEvent` property this scrollbar should listen to.
|
|
* @desc Set by the constructor. See the similarly named property in the {@link finbarOptions} object.
|
|
*
|
|
* Useful values are `'deltaX'`, `'deltaY'`, or `'deltaZ'`. A value of `null` means to ignore mouse wheel events entirely.
|
|
*
|
|
* The mouse wheel is one-dimensional and only emits events with `deltaY` data. This property is provided so that you can override the default of `'deltaX'` with a value of `'deltaY'` on your horizontal scrollbar primarily to accommodate certain "panoramic" interface designs where the mouse wheel should control horizontal rather than vertical scrolling. Just give `{ deltaProp: 'deltaY' }` in your horizontal scrollbar instantiation.
|
|
*
|
|
* Caveat: Note that a 2-finger drag on an Apple trackpad emits events with _both_ `deltaX ` and `deltaY` data so you might want to delay making the above adjustment until you can determine that you are getting Y data only with no X data at all (which is a sure bet you on a mouse wheel rather than a trackpad).
|
|
|
|
* @type {object|null}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
this.deltaProp = this.oh.delta;
|
|
|
|
this.bar.className = this.bar.className.replace(/(vertical|horizontal)/g, orientation);
|
|
|
|
if (this.bar.style.cssText !== BAR_STYLE || this.thumb.style.cssText !== THUMB_STYLE) {
|
|
this.bar.setAttribute('style', BAR_STYLE);
|
|
this.thumb.setAttribute('style', THUMB_STYLE);
|
|
this.resize();
|
|
}
|
|
},
|
|
get orientation() {
|
|
return this._orientation;
|
|
},
|
|
|
|
/**
|
|
* @summary Callback for scroll events.
|
|
* @desc Set by the constructor via the similarly named property in the {@link finbarOptions} object. After instantiation, `this.onchange` may be updated directly.
|
|
*
|
|
* This event handler is called whenever the value of the scrollbar is changed through user interaction. The typical use case is when the content is scrolled. It is called with the `FinBar` object as its context and the current value of the scrollbar (its index, rounded) as the only parameter.
|
|
*
|
|
* Set this property to `null` to stop emitting such events.
|
|
* @type {function(number)|null}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
onchange: null,
|
|
|
|
/**
|
|
* @summary Add a CSS class name to the bar element's class list.
|
|
* @desc Set by the constructor. See the similarly named property in the {@link finbarOptions} object.
|
|
*
|
|
* The bar element's class list will always include `finbar-vertical` (or `finbar-horizontal` based on the current orientation). Whenever this property is set to some value, first the old prefix+orientation is removed from the bar element's class list; then the new prefix+orientation is added to the bar element's class list. This property causes _an additional_ class name to be added to the bar element's class list. Therefore, this property will only add at most one additional class name to the list.
|
|
*
|
|
* To remove _classname-orientation_ from the bar element's class list, set this property to a falsy value, such as `null`.
|
|
*
|
|
* > NOTE: You only need to specify an additional class name when you need to have mulltiple different styles of scrollbars on the same page. If this is not a requirement, then you don't need to make a new class; you would just create some additional rules using the same selectors in the built-in stylesheet (../css/finbars.css):
|
|
* *`div.finbar-vertical` (or `div.finbar-horizontal`) for the scrollbar
|
|
* *`div.finbar-vertical > div` (or `div.finbar-horizontal > div`) for the "thumb."
|
|
*
|
|
* Of course, your rules should come after the built-ins.
|
|
* @type {string}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
set classPrefix(prefix) {
|
|
if (this._classPrefix) {
|
|
this.bar.classList.remove(this._classPrefix + this.orientation);
|
|
}
|
|
|
|
this._classPrefix = prefix;
|
|
|
|
if (prefix) {
|
|
this.bar.classList.add(prefix + '-' + this.orientation);
|
|
}
|
|
},
|
|
get classPrefix() {
|
|
return this._classPrefix;
|
|
},
|
|
|
|
/**
|
|
* @name increment
|
|
* @summary Number of scrollbar index units representing a pageful. Used exclusively for paging up and down and for setting thumb size relative to content size.
|
|
* @desc Set by the constructor. See the similarly named property in the {@link finbarOptions} object.
|
|
*
|
|
* Can also be given as a parameter to the {@link FinBar#resize|resize} method, which is pertinent because content area size changes affect the definition of a "pageful." However, you only need to do this if this value is being used. It not used when:
|
|
* * you define `paging.up` and `paging.down`
|
|
* * your scrollbar is using `scrollRealContent`
|
|
* @type {number}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
increment: 1,
|
|
|
|
/**
|
|
* Default value of multiplier for `WheelEvent#deltaX` (horizontal scrolling delta).
|
|
* @default
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
deltaXFactor: 1,
|
|
|
|
/**
|
|
* Default value of multiplier for `WheelEvent#deltaY` (vertical scrolling delta).
|
|
* @default
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
deltaYFactor: 1,
|
|
|
|
/**
|
|
* Default value of multiplier for `WheelEvent#deltaZ` (delpth scrolling delta).
|
|
* @default
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
deltaZFactor: 1,
|
|
|
|
/**
|
|
* @name barStyles
|
|
* @summary Scrollbar styles to be applied by {@link FinBar#resize|resize()}.
|
|
* @desc Set by the constructor. See the similarly named property in the {@link finbarOptions} object.
|
|
*
|
|
* This is a value to be assigned to {@link FinBar#styles|styles} on each call to {@link FinBar#resize|resize()}. That is, a hash of values to be copied to the scrollbar element's style object on resize; or `null` for none.
|
|
*
|
|
* @see {@link FinBar#style|style}
|
|
* @type {finbarStyles|null}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
barStyles: null,
|
|
|
|
/**
|
|
* @name style
|
|
* @summary Additional scrollbar styles.
|
|
* @desc See type definition for more details. These styles are applied directly to the scrollbar's `bar` element.
|
|
*
|
|
* Values are adjusted as follows before being applied to the element:
|
|
* 1. Included "pseudo-property" names from the scrollbar's orientation hash, {@link FinBar#oh|oh}, are translated to actual property names before being applied.
|
|
* 2. When there are margins, percentages are translated to absolute pixel values because CSS ignores margins in its percentage calculations.
|
|
* 3. If you give a value without a unit (a raw number), "px" unit is appended.
|
|
*
|
|
* General notes:
|
|
* 1. It is always preferable to specify styles via a stylesheet. Only set this property when you need to specifically override (a) stylesheet value(s).
|
|
* 2. Can be set directly or via calls to the {@link FinBar#resize|resize} method.
|
|
* 3. Should only be set after the scrollbar has been inserted into the DOM.
|
|
* 4. Before applying these new values to the element, _all_ in-line style values are reset (by removing the element's `style` attribute), exposing inherited values (from stylesheets).
|
|
* 5. Empty object has no effect.
|
|
* 6. Falsey value in place of object has no effect.
|
|
*
|
|
* > CAVEAT: Do not attempt to treat the object you assign to this property as if it were `this.bar.style`. Specifically, changing this object after assigning it will have no effect on the scrollbar. You must assign it again if you want it to have an effect.
|
|
*
|
|
* @see {@link FinBar#barStyles|barStyles}
|
|
* @type {finbarStyles}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
set style(styles) {
|
|
var keys = Object.keys(styles = extend({}, styles, this._auxStyles));
|
|
|
|
if (keys.length) {
|
|
var bar = this.bar,
|
|
barRect = bar.getBoundingClientRect(),
|
|
container = this.container || bar.parentElement,
|
|
containerRect = container.getBoundingClientRect(),
|
|
oh = this.oh;
|
|
|
|
// Before applying new styles, revert all styles to values inherited from stylesheets
|
|
bar.setAttribute('style', BAR_STYLE);
|
|
|
|
keys.forEach(function (key) {
|
|
var val = styles[key];
|
|
|
|
if (key in oh) {
|
|
key = oh[key];
|
|
}
|
|
|
|
if (!isNaN(Number(val))) {
|
|
val = (val || 0) + 'px';
|
|
} else if (/%$/.test(val)) {
|
|
// When bar size given as percentage of container, if bar has margins, restate size in pixels less margins.
|
|
// (If left as percentage, CSS's calculation will not exclude margins.)
|
|
var oriented = axis[key],
|
|
margins = barRect[oriented.marginLeading] + barRect[oriented.marginTrailing];
|
|
if (margins) {
|
|
val = parseInt(val, 10) / 100 * containerRect[oriented.size] - margins + 'px';
|
|
}
|
|
}
|
|
|
|
bar.style[key] = val;
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @readonly
|
|
* @name paging
|
|
* @summary Enable page up/dn clicks.
|
|
* @desc Set by the constructor. See the similarly named property in the {@link finbarOptions} object.
|
|
*
|
|
* If truthy, listen for clicks in page-up and page-down regions of scrollbar.
|
|
*
|
|
* If an object, call `.paging.up()` on page-up clicks and `.paging.down()` will be called on page-down clicks.
|
|
*
|
|
* Changing the truthiness of this value after instantiation currently has no effect.
|
|
* @type {boolean|object}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
paging: true,
|
|
|
|
/**
|
|
* @name range
|
|
* @summary Setter for the minimum and maximum scroll values.
|
|
* @desc Set by the constructor. These values are the limits for {@link FooBar#index|index}.
|
|
*
|
|
* The setter accepts an object with exactly two numeric properties: `.min` which must be less than `.max`. The values are extracted and the object is discarded.
|
|
*
|
|
* The getter returns a new object with `.min` and '.max`.
|
|
*
|
|
* @type {rangeType}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
set range(range) {
|
|
validRange(range);
|
|
this._min = range.min;
|
|
this._max = range.max;
|
|
this.contentSize = range.max - range.min + 1;
|
|
this.index = this.index; // re-clamp
|
|
},
|
|
get range() {
|
|
return {
|
|
min: this._min,
|
|
max: this._max
|
|
};
|
|
},
|
|
|
|
/**
|
|
* @summary Index value of the scrollbar.
|
|
* @desc This is the position of the scroll thumb.
|
|
*
|
|
* Setting this value clamps it to {@link FinBar#min|min}..{@link FinBar#max|max}, scroll the content, and moves thumb.
|
|
*
|
|
* Getting this value returns the current index. The returned value will be in the range `min`..`max`. It is intentionally not rounded.
|
|
*
|
|
* Use this value as an alternative to (or in addition to) using the {@link FinBar#onchange|onchange} callback function.
|
|
*
|
|
* @see {@link FinBar#_setScroll|_setScroll}
|
|
* @type {number}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
set index(idx) {
|
|
idx = Math.min(this._max, Math.max(this._min, idx)); // clamp it
|
|
this._setScroll(idx);
|
|
// this._setThumbSize();
|
|
},
|
|
get index() {
|
|
return this._index;
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @summary Move the thumb.
|
|
* @desc Also displays the index value in the test panel and invokes the callback.
|
|
* @param idx - The new scroll index, a value in the range `min`..`max`.
|
|
* @param [scaled=f(idx)] - The new thumb position in pixels and scaled relative to the containing {@link FinBar#bar|bar} element, i.e., a proportional number in the range `0`..`thumbMax`. When omitted, a function of `idx` is used.
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
_setScroll: function (idx, scaled) {
|
|
this._index = idx;
|
|
|
|
// Display the index value in the test panel
|
|
if (this.testPanelItem && this.testPanelItem.index instanceof Element) {
|
|
this.testPanelItem.index.innerHTML = Math.round(idx);
|
|
}
|
|
|
|
// Call the callback
|
|
if (this.onchange) {
|
|
this.onchange.call(this, Math.round(idx));
|
|
}
|
|
|
|
// Move the thumb
|
|
if (scaled === undefined) {
|
|
scaled = (idx - this._min) / (this._max - this._min) * this._thumbMax;
|
|
}
|
|
this.thumb.style[this.oh.leading] = scaled + 'px';
|
|
},
|
|
|
|
scrollRealContent: function (idx) {
|
|
var containerRect = this.content.parentElement.getBoundingClientRect(),
|
|
sizeProp = this.oh.size,
|
|
maxScroll = Math.max(0, this.content[sizeProp] - containerRect[sizeProp]),
|
|
//scroll = Math.min(idx, maxScroll);
|
|
scroll = (idx - this._min) / (this._max - this._min) * maxScroll;
|
|
//console.log('scroll: ' + scroll);
|
|
this.content.style[this.oh.leading] = -scroll + 'px';
|
|
},
|
|
|
|
/**
|
|
* @summary Recalculate thumb position.
|
|
*
|
|
* @desc This method recalculates the thumb size and position. Call it once after inserting your scrollbar into the DOM, and repeatedly while resizing the scrollbar (which typically happens when the scrollbar's parent is resized by user.
|
|
*
|
|
* > This function shifts args if first arg omitted.
|
|
*
|
|
* @param {number} [increment=this.increment] - Resets {@link FooBar#increment|increment} (see).
|
|
*
|
|
* @param {finbarStyles} [barStyles=this.barStyles] - (See type definition for details.) Scrollbar styles to be applied to the bar element.
|
|
*
|
|
* Only specify a `barStyles` object when you need to override stylesheet values. If provided, becomes the new default (`this.barStyles`), for use as a default on subsequent calls.
|
|
*
|
|
* It is generally the case that the scrollbar's new position is sufficiently described by the current styles. Therefore, it is unusual to need to provide a `barStyles` object on every call to `resize`.
|
|
*
|
|
* @returns {FinBar} Self for chaining.
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
resize: function (increment, barStyles) {
|
|
var bar = this.bar;
|
|
|
|
if (!bar.parentNode) {
|
|
return; // not in DOM yet so nothing to do
|
|
}
|
|
|
|
var container = this.container || bar.parentElement,
|
|
containerRect = container.getBoundingClientRect();
|
|
|
|
// shift args if if 1st arg omitted
|
|
if (typeof increment === 'object') {
|
|
barStyles = increment;
|
|
increment = undefined;
|
|
}
|
|
|
|
this.style = this.barStyles = barStyles || this.barStyles;
|
|
|
|
// Bound to real content: Content was given but no onchange handler.
|
|
// Set up .onchange, .containerSize, and .increment.
|
|
// Note this only makes sense if your index unit is pixels.
|
|
if (this.content) {
|
|
if (!this.onchange) {
|
|
this.onchange = this.scrollRealContent;
|
|
this.contentSize = this.content[this.oh.size];
|
|
this._min = 0;
|
|
this._max = this.contentSize - 1;
|
|
}
|
|
}
|
|
if (this.onchange === this.scrollRealContent) {
|
|
this.containerSize = containerRect[this.oh.size];
|
|
this.increment = this.containerSize / (this.contentSize - this.containerSize) * (this._max - this._min);
|
|
} else {
|
|
this.containerSize = 1;
|
|
this.increment = increment || this.increment;
|
|
}
|
|
|
|
var index = this.index;
|
|
this.testPanelItem = this.testPanelItem || this._addTestPanelItem();
|
|
this._setThumbSize();
|
|
this.index = index;
|
|
|
|
if (this.deltaProp !== null) {
|
|
container.addEventListener('wheel', this._bound.onwheel);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* @summary Shorten trailing end of scrollbar by thickness of some other scrollbar.
|
|
* @desc In the "classical" scenario where vertical scroll bar is on the right and horizontal scrollbar is on the bottom, you want to shorten the "trailing end" (bottom and right ends, respectively) of at least one of them so they don't overlay.
|
|
*
|
|
* This convenience function is an programmatic alternative to hardcoding the correct style with the correct value in your stylesheet; or setting the correct style with the correct value in the {@link FinBar#barStyles|barStyles} object.
|
|
*
|
|
* @see {@link FinBar#foreshortenBy|foreshortenBy}.
|
|
*
|
|
* @param {FinBar|null} otherFinBar - Other scrollbar to avoid by shortening this one; `null` removes the trailing space
|
|
* @returns {FinBar} For chaining
|
|
*/
|
|
shortenBy: function (otherFinBar) { return this.shortenEndBy('trailing', otherFinBar); },
|
|
|
|
/**
|
|
* @summary Shorten leading end of scrollbar by thickness of some other scrollbar.
|
|
* @desc Supports non-classical scrollbar scenarios where vertical scroll bar may be on left and horizontal scrollbar may be on top, in which case you want to shorten the "leading end" rather than the trailing end.
|
|
* @see {@link FinBar#shortenBy|shortenBy}.
|
|
* @param {FinBar|null} otherFinBar - Other scrollbar to avoid by shortening this one; `null` removes the trailing space
|
|
* @returns {FinBar} For chaining
|
|
*/
|
|
foreshortenBy: function (otherFinBar) { return this.shortenEndBy('leading', otherFinBar); },
|
|
|
|
/**
|
|
* @summary Generalized shortening function.
|
|
* @see {@link FinBar#shortenBy|shortenBy}.
|
|
* @see {@link FinBar#foreshortenBy|foreshortenBy}.
|
|
* @param {string} whichEnd - a CSS style property name or an orientation hash name that translates to a CSS style property name.
|
|
* @param {FinBar|null} otherFinBar - Other scrollbar to avoid by shortening this one; `null` removes the trailing space
|
|
* @returns {FinBar} For chaining
|
|
*/
|
|
shortenEndBy: function (whichEnd, otherFinBar) {
|
|
if (!otherFinBar) {
|
|
delete this._auxStyles;
|
|
} else if (otherFinBar instanceof FinBar && otherFinBar.orientation !== this.orientation) {
|
|
var otherStyle = window.getComputedStyle(otherFinBar.bar),
|
|
ooh = orientationHashes[otherFinBar.orientation];
|
|
this._auxStyles = {};
|
|
this._auxStyles[whichEnd] = otherStyle[ooh.thickness];
|
|
}
|
|
return this; // for chaining
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @summary Sets the proportional thumb size and hides thumb when 100%.
|
|
* @desc The thumb size has an absolute minimum of 20 (pixels).
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
_setThumbSize: function () {
|
|
var oh = this.oh,
|
|
thumbComp = window.getComputedStyle(this.thumb),
|
|
thumbMarginLeading = parseInt(thumbComp[oh.marginLeading]),
|
|
thumbMarginTrailing = parseInt(thumbComp[oh.marginTrailing]),
|
|
thumbMargins = thumbMarginLeading + thumbMarginTrailing,
|
|
barSize = this.bar.getBoundingClientRect()[oh.size],
|
|
thumbSize = Math.max(20, barSize * this.containerSize / this.contentSize);
|
|
|
|
if (this.containerSize < this.contentSize) {
|
|
this.bar.style.visibility = 'visible';
|
|
this.thumb.style[oh.size] = thumbSize + 'px';
|
|
} else {
|
|
this.bar.style.visibility = 'hidden';
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @name _thumbMax
|
|
* @summary Maximum offset of thumb's leading edge.
|
|
* @desc This is the pixel offset within the scrollbar of the thumb when it is at its maximum position at the extreme end of its range.
|
|
*
|
|
* This value takes into account the newly calculated size of the thumb element (including its margins) and the inner size of the scrollbar (the thumb's containing element, including _its_ margins).
|
|
*
|
|
* NOTE: Scrollbar padding is not taken into account and assumed to be 0 in the current implementation and is assumed to be `0`; use thumb margins in place of scrollbar padding.
|
|
* @type {number}
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
this._thumbMax = barSize - thumbSize - thumbMargins;
|
|
|
|
this._thumbMarginLeading = thumbMarginLeading; // used in mousedown
|
|
},
|
|
|
|
/**
|
|
* @summary Remove the scrollbar.
|
|
* @desc Unhooks all the event handlers and then removes the element from the DOM. Always call this method prior to disposing of the scrollbar object.
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
remove: function () {
|
|
this.bar.onmousedown = null;
|
|
this._removeEvt('mousemove');
|
|
this._removeEvt('mouseup');
|
|
|
|
(this.container || this.bar.parentElement)._removeEvt('wheel');
|
|
|
|
this.bar.onclick =
|
|
this.thumb.onclick =
|
|
this.thumb.onmouseover =
|
|
this.thumb.transitionend =
|
|
this.thumb.onmouseout = null;
|
|
|
|
this.bar.remove();
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @function _addTestPanelItem
|
|
* @summary Append a test panel element.
|
|
* @desc If there is a test panel in the DOM (typically an `<ol>...</ol>` element) with class names of both `this.classPrefix` and `'test-panel'` (or, barring that, any element with class name `'test-panel'`), an `<li>...</li>` element will be created and appended to it. This new element will contain a span for each class name given.
|
|
*
|
|
* You should define a CSS selector `.listening` for these spans. This class will be added to the spans to alter their appearance when a listener is added with that class name (prefixed with 'on').
|
|
*
|
|
* (This is an internal function that is called once by the constructor on every instantiation.)
|
|
* @returns {Element|undefined} The appended `<li>...</li>` element or `undefined` if there is no test panel.
|
|
* @memberOf FinBar.prototype
|
|
*/
|
|
_addTestPanelItem: function () {
|
|
var testPanelItem,
|
|
testPanelElement = document.querySelector('.' + this._classPrefix + '.test-panel') || document.querySelector('.test-panel');
|
|
|
|
if (testPanelElement) {
|
|
var testPanelItemPartNames = [ 'mousedown', 'mousemove', 'mouseup', 'index' ],
|
|
item = document.createElement('li');
|
|
|
|
testPanelItemPartNames.forEach(function (partName) {
|
|
item.innerHTML += '<span class="' + partName + '">' + partName.replace('mouse', '') + '</span>';
|
|
});
|
|
|
|
testPanelElement.appendChild(item);
|
|
|
|
testPanelItem = {};
|
|
testPanelItemPartNames.forEach(function (partName) {
|
|
testPanelItem[partName] = item.getElementsByClassName(partName)[0];
|
|
});
|
|
}
|
|
|
|
return testPanelItem;
|
|
},
|
|
|
|
_addEvt: function (evtName) {
|
|
var spy = this.testPanelItem && this.testPanelItem[evtName];
|
|
if (spy) { spy.classList.add('listening'); }
|
|
window.addEventListener(evtName, this._bound['on' + evtName]);
|
|
},
|
|
|
|
_removeEvt: function (evtName) {
|
|
var spy = this.testPanelItem && this.testPanelItem[evtName];
|
|
if (spy) { spy.classList.remove('listening'); }
|
|
window.removeEventListener(evtName, this._bound['on' + evtName]);
|
|
}
|
|
};
|
|
|
|
function extend(obj) {
|
|
for (var i = 1; i < arguments.length; ++i) {
|
|
var objn = arguments[i];
|
|
if (objn) {
|
|
for (var key in objn) {
|
|
obj[key] = objn[key];
|
|
}
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
function validRange(range) {
|
|
var keys = Object.keys(range),
|
|
valid = keys.length === 2 &&
|
|
typeof range.min === 'number' &&
|
|
typeof range.max === 'number' &&
|
|
range.min <= range.max;
|
|
|
|
if (!valid) {
|
|
error('Invalid .range object.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @name handlersToBeBound
|
|
* @type {object}
|
|
* @desc The functions defined in this object are all DOM event handlers that are bound by the FinBar constructor to each new instance. In other words, the `this` value of these handlers, once bound, refer to the FinBar object and not to the event emitter. "Do not consume raw."
|
|
*/
|
|
var handlersToBeBound = {
|
|
shortStop: function (evt) {
|
|
evt.stopPropagation();
|
|
},
|
|
|
|
onwheel: function (evt) {
|
|
this.index += evt[this.deltaProp] * this[this.deltaProp + 'Factor'] * this.normal;
|
|
evt.stopPropagation();
|
|
evt.preventDefault();
|
|
},
|
|
|
|
onclick: function (evt) {
|
|
var thumbBox = this.thumb.getBoundingClientRect(),
|
|
goingUp = evt[this.oh.coordinate] < thumbBox[this.oh.leading];
|
|
|
|
if (typeof this.paging === 'object') {
|
|
this.index = this.paging[goingUp ? 'up' : 'down'](Math.round(this.index));
|
|
} else {
|
|
this.index += goingUp ? -this.increment : this.increment;
|
|
}
|
|
|
|
// make the thumb glow momentarily
|
|
this.thumb.classList.add('hover');
|
|
var self = this;
|
|
this.thumb.addEventListener('transitionend', function waitForIt() {
|
|
this.removeEventListener('transitionend', waitForIt);
|
|
self._bound.onmouseup(evt);
|
|
});
|
|
|
|
evt.stopPropagation();
|
|
},
|
|
|
|
onmouseover: function () {
|
|
this.thumb.classList.add('hover');
|
|
},
|
|
|
|
onmouseout: function () {
|
|
if (!this.dragging) {
|
|
this.thumb.classList.remove('hover');
|
|
}
|
|
},
|
|
|
|
onmousedown: function (evt) {
|
|
var thumbBox = this.thumb.getBoundingClientRect();
|
|
this.pinOffset = evt[this.oh.axis] - thumbBox[this.oh.leading] + this.bar.getBoundingClientRect()[this.oh.leading] + this._thumbMarginLeading;
|
|
document.documentElement.style.cursor = 'default';
|
|
|
|
this.dragging = true;
|
|
|
|
this._addEvt('mousemove');
|
|
this._addEvt('mouseup');
|
|
|
|
evt.stopPropagation();
|
|
evt.preventDefault();
|
|
},
|
|
|
|
onmousemove: function (evt) {
|
|
if (!(evt.buttons & 1)) {
|
|
// mouse button may have been released without `onmouseup` triggering (see
|
|
window.dispatchEvent(new MouseEvent('mouseup', evt));
|
|
return;
|
|
}
|
|
|
|
var scaled = Math.min(this._thumbMax, Math.max(0, evt[this.oh.axis] - this.pinOffset));
|
|
var idx = scaled / this._thumbMax * (this._max - this._min) + this._min;
|
|
|
|
this._setScroll(idx, scaled);
|
|
|
|
evt.stopPropagation();
|
|
evt.preventDefault();
|
|
},
|
|
|
|
onmouseup: function (evt) {
|
|
this._removeEvt('mousemove');
|
|
this._removeEvt('mouseup');
|
|
|
|
this.dragging = false;
|
|
|
|
document.documentElement.style.cursor = 'auto';
|
|
|
|
var thumbBox = this.thumb.getBoundingClientRect();
|
|
if (
|
|
thumbBox.left <= evt.clientX && evt.clientX <= thumbBox.right &&
|
|
thumbBox.top <= evt.clientY && evt.clientY <= thumbBox.bottom
|
|
) {
|
|
this._bound.onmouseover(evt);
|
|
} else {
|
|
this._bound.onmouseout(evt);
|
|
}
|
|
|
|
evt.stopPropagation();
|
|
evt.preventDefault();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Table of wheel normals to webkit.
|
|
*
|
|
* This object is a dictionary of platform dictionaries, keyed by:
|
|
* * `mac` — macOS
|
|
* * `win` — Window
|
|
*
|
|
* Each platform dictionary is keyed by:
|
|
* * `webkit` — Chrome, Opera, Safari
|
|
* * `moz` — Firefox
|
|
* * `ms` — IE 11 _(Windows only)_
|
|
* * `edge` — Edge _(Windows only)_
|
|
*
|
|
* @todo add `linux` platform
|
|
* @type {object}
|
|
*/
|
|
FinBar.normals = {
|
|
mac: {
|
|
webkit: 1.0,
|
|
moz: 35
|
|
},
|
|
win: {
|
|
webkit: 2.6,
|
|
moz: 85,
|
|
ms: 2.9,
|
|
edge: 2
|
|
}
|
|
};
|
|
|
|
function getNormal() {
|
|
var nav = window.navigator, ua = nav.userAgent;
|
|
var platform = nav.platform.substr(0, 3).toLowerCase();
|
|
var browser = /Edge/.test(ua) ? 'edge' :
|
|
/Opera|OPR|Chrome|Safari/.test(ua) ? 'webkit' :
|
|
/Firefox/.test(ua) ? 'moz' :
|
|
document.documentMode ? 'ms' : // internet explorer
|
|
undefined;
|
|
var platformDictionary = FinBar.normals[platform] || {};
|
|
return platformDictionary[browser];
|
|
}
|
|
|
|
var orientationHashes = {
|
|
vertical: {
|
|
coordinate: 'clientY',
|
|
axis: 'pageY',
|
|
size: 'height',
|
|
outside: 'right',
|
|
inside: 'left',
|
|
leading: 'top',
|
|
trailing: 'bottom',
|
|
marginLeading: 'marginTop',
|
|
marginTrailing: 'marginBottom',
|
|
thickness: 'width',
|
|
delta: 'deltaY'
|
|
},
|
|
horizontal: {
|
|
coordinate: 'clientX',
|
|
axis: 'pageX',
|
|
size: 'width',
|
|
outside: 'bottom',
|
|
inside: 'top',
|
|
leading: 'left',
|
|
trailing: 'right',
|
|
marginLeading: 'marginLeft',
|
|
marginTrailing: 'marginRight',
|
|
thickness: 'height',
|
|
delta: 'deltaX'
|
|
}
|
|
};
|
|
|
|
var axis = {
|
|
top: 'vertical',
|
|
bottom: 'vertical',
|
|
height: 'vertical',
|
|
left: 'horizontal',
|
|
right: 'horizontal',
|
|
width: 'horizontal'
|
|
};
|
|
|
|
var cssFinBars; // definition inserted by gulpfile between following comments
|
|
/* inject:css */
|
|
cssFinBars = 'div.finbar-horizontal,div.finbar-vertical{margin:3px}div.finbar-horizontal>.thumb,div.finbar-vertical>.thumb{background-color:#d3d3d3;-webkit-box-shadow:0 0 1px #000;-moz-box-shadow:0 0 1px #000;box-shadow:0 0 1px #000;border-radius:4px;margin:2px;opacity:.4;transition:opacity .5s}div.finbar-horizontal>.thumb.hover,div.finbar-vertical>.thumb.hover{opacity:1;transition:opacity .5s}div.finbar-vertical{top:0;bottom:0;right:0;width:11px}div.finbar-vertical>.thumb{top:0;right:0;width:7px}div.finbar-horizontal{left:0;right:0;bottom:0;height:11px}div.finbar-horizontal>.thumb{left:0;bottom:0;height:7px}';
|
|
/* endinject */
|
|
|
|
function error(msg) {
|
|
throw 'finbars: ' + msg;
|
|
}
|
|
|
|
// Interface
|
|
module.exports = FinBar;
|
|
|
|
},{"css-injector":1}]},{},[2])
|
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9qb25laXQvcmVwb3MvZmluYmFycy9ub2RlX21vZHVsZXMvYnJvd3Nlci1wYWNrL19wcmVsdWRlLmpzIiwiL1VzZXJzL2pvbmVpdC9yZXBvcy9maW5iYXJzL25vZGVfbW9kdWxlcy9jc3MtaW5qZWN0b3IvaW5kZXguanMiLCIvVXNlcnMvam9uZWl0L3JlcG9zL2ZpbmJhcnMvc3JjL2Zha2VfYWRkMTg5YWUuanMiLCIvVXNlcnMvam9uZWl0L3JlcG9zL2ZpbmJhcnMvc3JjL2luZGV4LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNQQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3Rocm93IG5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIil9dmFyIGY9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGYuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sZixmLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIid1c2Ugc3RyaWN0JztcblxuLyogZXNsaW50LWVudiBicm93c2VyICovXG5cbi8qKiBAbmFtZXNwYWNlIGNzc0luamVjdG9yICovXG5cbi8qKlxuICogQHN1bW1hcnkgSW5zZXJ0IGJhc2Ugc3R5bGVzaGVldCBpbnRvIERPTVxuICpcbiAqIEBkZXNjIENyZWF0ZXMgYSBuZXcgYDxzdHlsZT4uLi48L3N0eWxlPmAgZWxlbWVudCBmcm9tIHRoZSBuYW1lZCB0ZXh0IHN0cmluZyhzKSBhbmQgaW5zZXJ0cyBpdCBidXQgb25seSBpZiBpdCBkb2VzIG5vdCBhbHJlYWR5IGV4aXN0IGluIHRoZSBzcGVjaWZpZWQgY29udGFpbmVyIGFzIHBlciBgcmVmZXJlbmNlRWxlbWVudGAuXG4gKlxuICogPiBDYXZlYXQ6IElmIHN0eWxlc2hlZXQgaXMgZm9yIHVzZSBpbiBhIHNoYWRvdyBET00sIHlvdSBtdXN0IHNwZWNpZnkgYSBsb2NhbCBgcmVmZXJlbmNlRWxlbWVudGAuXG4gKlxuICogQHJldHVybnMgQSByZWZlcmVuY2UgdG8gdGhlIG5ld2x5IGNyZWF0ZWQgYDxzdHlsZT4uLi48L3N0eWxlPmAgZWxlbWVudC5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ3xzdHJpbmdbXX0gY3NzUnVsZXNcbiAqIEBwYXJhbSB7c3RyaW5nfSBbSURdXG4gKiBAcGFyYW0ge3VuZGVmaW5lZHxudWxsfEVsZW1lbnR8c3RyaW5nfSBbcmVmZXJlbmNlRWxlbWVudF0gLSBDb250YWluZXIgZm9yIGluc2VydGlvbi4gT3ZlcmxvYWRzOlxuICogKiBgdW5kZWZpbmVkYCB0eXBlIChvciBvbWl0dGVkKTogaW5qZWN0cyBzdHlsZXNoZWV0IGF0IHRvcCBvZiBgPGhlYWQ+Li4uPC9oZWFkPmAgZWxlbWVudFxuICogKiBgbnVsbGAgdmFsdWU6IGluamVjdHMgc3R5bGVzaGVldCBhdCBib3R0b20gb2YgYDxoZWFkPi4uLjwvaGVhZD5gIGVsZW1lbnRcbiAqICogYEVsZW1lbnRgIHR5cGU6IGluamVjdHMgc3R5bGVzaGVldCBpbW1lZGlhdGVseSBiZWZvcmUgZ2l2ZW4gZWxlbWVudCwgd2hlcmV2ZXIgaXQgaXMgZm91bmQuXG4gKiAqIGBzdHJpbmdgIHR5cGU6IGluamVjdHMgc3R5bGVzaGVldCBpbW1lZGlhdGVseSBiZWZvcmUgZ2l2ZW4gZmlyc3QgZWxlbWVudCBmb3VuZCB0aGF0IG1hdGNoZXMgdGhlIGdpdmVuIGNzcyBzZWxlY3Rvci5cbiAqXG4gKiBAbWVtYmVyT2YgY3NzSW5qZWN0b3JcbiAqL1xuZnVuY3Rpb24gY3NzSW5qZWN0b3IoY3NzUnVsZXMsIElELCByZWZlcmVuY2VFbGVtZW50KSB7XG4gICAgaWYgKHR5cGVvZiByZWZlcmVuY2VFbGVtZW50ID09PSAnc3RyaW5nJykge1xuICAgICAgICByZWZlcmVuY2VFbGVtZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihyZWZlcmVuY2VFbGVtZW50KTtcbiAgICAgICAgaWYgKCFyZWZlcmVuY2VFbGVtZW50KSB7XG4gICAgICAgICAgICB0aHJvdyAnQ2Fubm90IGZpbmQgcmVmZXJlbmNlIGVsZW1lbnQgZm9yIENTUyBpbmplY3Rpb24uJztcbiAgICAgICAgfVxuICAgIH0gZWxzZSBpZiAocmVmZXJlbmNlRWxlbWVudCAmJiAhKHJlZmVyZW5jZUVsZW1lbnQgaW5zdGFuY2VvZiBFbGVtZW50KSkge1xuICAgICAgICB0aHJvdyAnR2l2ZW4gdmFsdWUgbm90IGEgcmVmZXJlbmNlIGVsZW1lbnQuJztcbiAgICB9XG5cbiAgICB2YXIgY29udGFpbmVyID0gcmVmZXJlbmNlRWxlbWVudCAmJiByZWZlcmVuY2VFbGVtZW50LnBhcmVudE5vZGUgfHwgZG9jdW1lbnQuaGVhZCB8fCBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaGVhZCcpWzBdO1xuXG4gICAgaWYgKElEKSB7XG4gICAgICAgIElEID0gY3NzSW5qZWN0b3IuaWRQcmVmaXggKyBJRDtcblxuICAgICAgICBpZiAoY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJyMnICsgSUQpKSB7XG4gICAgICAgICAgICByZXR1cm47IC8vIHN0eWxlc2hlZXQgYWxyZWFkeSBpbiBET01cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHZhciBzdHlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N0eWxlJyk7XG4gICAgc3R5bGUudHlwZSA9ICd0ZXh0L2Nzcyc7XG4gICAgaWYgKElEKSB7XG4gICAgICAgIHN0eWxlLmlkID0gSUQ7XG4gICAgfVxuICAgIGlmIChjc3NSdWxlcyBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICAgIGNzc1J1bGVzID0gY3NzUnVsZXMuam9pbignXFxuJyk7XG4gICAgfVxuICAgIGNzc1J1bGVzID0gJ1xcbicgKyBjc3NSdWxlcyArICdcXG4nO1xuICAgIGlmIChzdHlsZS5zdHlsZVNoZWV0KSB7XG4gICAgICAgIHN0eWxlLnN0eWxlU2hlZXQuY3NzVGV4dCA9IGNzc1J1bGVzO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHN0eWxlLmFwcGVuZENoaWxkKGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKGNzc1J1bGVzKSk7XG4gICAgfVxuXG4gICAgaWYgKHJlZmVyZW5jZUVsZW1lbnQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZWZlcmVuY2VFbGVtZW50ID0gY29udGFpbmVyLmZpcnN0Q2hpbGQ7XG4gICAgfVxuXG4gICAgY29udGFpbmVyLmluc2VydEJlZm9yZShzdHlsZSwgcmVmZXJlbmNlRWxlbWVudCk7XG5cbiAgICByZXR1cm4gc3R5bGU7XG59XG5cbi8qKlxuICogQHN1bW1hcnkgT3B0aW9uYWwgcHJlZml4IGZvciBgPHN0eWxlPmAgdGFnIElEcy5cbiAqIEBkZXNjIERlZmF1bHRzIHRvIGAnaW5qZWN0ZWQtc3R5bGVzaGVldC0nYC5cbiAqIEB0eXBlIHtzdHJpbmd9XG4gKiBAbWVtYmVyT2YgY3NzSW5qZWN0b3JcbiAqL1xuY3NzSW5qZWN0b3IuaWRQcmVmaXggPSAnaW5qZWN0ZWQtc3R5bGVzaGVldC0nO1xuXG4vLyBJbnRlcmZhY2Vcbm1vZHVsZS5leHBvcnRzID0gY3NzSW5qZWN0b3I7XG4iLCIvKiBlc2xpbnQtZW52IGJyb3dzZXIgKi9cblxuJ3VzZSBzdHJpY3QnO1xuXG5pZiAoIXdpbmRvdy5GaW5CYXIpIHtcbiAgICB3aW5kb3cuRmluQmFyID0gcmVxdWlyZSgnLi8nKTtcbn1cbiIsIid1c2Ugc3RyaWN0JztcblxuLyogZXNsaW50LWVudiBub2RlLCBicm93c2VyICovXG5cbnZhciBjc3NJbmplY3RvciA9IHJlcXVpcmUoJ2Nzcy1pbmplY3RvcicpO1xuXG4vLyBGb2xsb3dpbmcgaXMgdGhlIHNvbGUgc3R5bGUgcmVxdWlyZW1lbnQgZm9yIGJhciBhbmQgdGh1bWIgZWxlbWVudHMuXG4vLyBNYWludGFpbmVkIGluIGNvZGUgc28gbm90IGRlcGVuZGVudCBiZWluZyBpbiBzdHlsZXNoZWV0LlxudmFyIEJBUl9TVFlMRSA9ICdwb3NpdGlvbjogYWJzb2x1dGU7JztcbnZhciBUSFVNQl9TVFlMRSA9ICdwb3NpdGlvbjogYWJzb2x1dGU7JztcblxuLyoqXG4gKiBAY29uc3RydWN0b3IgRmluQmFyXG4gKiBAc3VtbWFyeSBDcmVhdGUgYSBzY3JvbGxiYXIgb2JqZWN0LlxuICogQGRlc2MgQ3JlYXRpbmcgYSBzY3JvbGxiYXIgaXMgYSB0aHJlZS1zdGVwIHByb2Nlc3M6XG4gKlxuICogMS4gSW5zdGFudGlhdGUgdGhlIHNjcm9sbGJhciBvYmplY3QgYnkgY2FsbGluZyB0aGlzIGNvbnN0cnVjdG9yIGZ1bmN0aW9uLiBVcG9uIGluc3RhbnRpYXRpb24sIHRoZSBET00gZWxlbWVudCBmb3IgdGhlIHNjcm9sbGJhciAod2l0aCBhIHNpbmdsZSBjaGlsZCBlbGVtZW50IGZvciB0aGUgc2Nyb2xsYmFyIFwidGh1bWJcIikgaXMgY3JlYXRlZCBidXQgaXMgbm90IGluc2VydCBpdCBpbnRvIHRoZSBET00uXG4gKiAyLiBBZnRlciBpbnN0YW50aWF0aW9uLCBpdCBpcyB0aGUgY2FsbGVyJ3MgcmVzcG9uc2liaWxpdHkgdG8gaW5zZXJ0IHRoZSBzY3JvbGxiYXIsIHtAbGluayBGaW5CYXIjYmFyfHRoaXMuYmFyfSwgaW50byB0aGUgRE9NLlxuICogMy4gQWZ0ZXIgaW5zZXJ0aW9uLCB0aGUgY2FsbGVyIG11c3QgY2FsbCB7QGxpbmsgRmluQmFyI3Jlc2l6ZXxyZXNpemUoKX0gYXQgbGVhc3Qgb25jZSB0byBzaXplIGFuZCBwb3NpdGlvbiB0aGUgc2Nyb2xsYmFyIGFuZCBpdHMgdGh1bWIuIEFmdGVyIHRoYXQsIGByZXNpemUoKWAgc2hvdWxkIGFsc28gYmUgY2FsbGVkIHJlcGVhdGVkbHkgb24gcmVzaXplIGV2ZW50cyAoYXMgdGhlIGNvbnRlbnQgZWxlbWVudCBpcyBiZWluZyByZXNpemVkKS5cbiAqXG4gKiBTdWdnZXN0ZWQgY29uZmlndXJhdGlvbnM6XG4gKiAqIF8qKlVuYm91bmQqKl88YnIvPlxuICogVGhlIHNjcm9sbGJhciBzZXJ2ZXMgbWVyZWx5IGFzIGEgc2ltcGxlIHJhbmdlIChzbGlkZXIpIGNvbnRyb2wuIE9taXQgYm90aCBgb3B0aW9ucy5vbmNoYW5nZWAgYW5kIGBvcHRpb25zLmNvbnRlbnRgLlxuICogKiBfKipCb3VuZCB0byB2aXJ0dWFsIGNvbnRlbnQgZWxlbWVudCoqXzxici8+XG4gKiBWaXJ0dWFsIGNvbnRlbnQgaXMgcHJvamVjdGVkIGludG8gdGhlIGVsZW1lbnQgdXNpbmcgYSBjdXN0b20gZXZlbnQgaGFuZGxlciBzdXBwbGllZCBieSB0aGUgcHJvZ3JhbW1lciBpbiBgb3B0aW9ucy5vbmNoYW5nZWAuIEEgdHlwaWNhbCB1c2UgY2FzZSB3b3VsZCBiZSB0byBoYW5kbGUgc2Nyb2xsaW5nIG9mIHRoZSB2aXJ0dWFsIGNvbnRlbnQuIE90aGVyIHVzZSBjYXNlcyBpbmNsdWRlIGRhdGEgdHJhbnNmb3JtYXRpb25zLCBncmFwaGljcyB0cmFuc2Zvcm1hdGlvbnMsIF9ldGMuX1xuICogKiBfKipCb3VuZCB0byByZWFsIGNvbnRlbnQqKl88YnIvPlxuICogU2V0IGBvcHRpb25zLmNvbnRlbnRgIHRvIHRoZSBcInJlYWxcIiBjb250ZW50IGVsZW1lbnQgYnV0IG9taXQgYG9wdGlvbnMub25jaGFuZ2VgLiBUaGlzIHdpbGwgY2F1c2UgdGhlIHNjcm9sbGJhciB0byB1c2UgdGhlIGJ1aWx0LWluIGV2ZW50IGhhbmRsZXIgKGB0aGlzLnNjcm9sbFJlYWxDb250ZW50YCkgd2hpY2ggaW1wbGVtZW50cyBzbW9vdGggc2Nyb2xsaW5nIG9mIHRoZSBjb250ZW50IGVsZW1lbnQgd2l0aGluIHRoZSBjb250YWluZXIuXG4gKlxuICogQHBhcmFtIHtmaW5iYXJPcHRpb25zfSBbb3B0aW9ucz17fV0gLSBPcHRpb25zIG9iamVjdC4gU2VlIHRoZSB0eXBlIGRlZmluaXRpb24gZm9yIG1lbWJlciBkZXRhaWxzLlxuICovXG5mdW5jdGlvbiBGaW5CYXIob3B0aW9ucykge1xuXG4gICAgLy8gbWFrZSBib3VuZCB2ZXJzaW9ucyBvZiBhbGwgdGhlIG1vdXNlIGV2ZW50IGhhbmRsZXJcbiAgICB2YXIgYm91bmQgPSB0aGlzLl9ib3VuZCA9IHt9O1xuICAgIE9iamVjdC5rZXlzKGhhbmRsZXJzVG9CZUJvdW5kKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgYm91bmRba2V5XSA9IGhhbmRsZXJzVG9CZUJvdW5kW2tleV0uYmluZCh0aGlzKTtcbiAgICB9LCB0aGlzKTtcblxuICAgIC8qKlxuICAgICAqIEBuYW1lIHRodW1iXG4gICAgICogQHN1bW1hcnkgVGhlIGdlbmVyYXRlZCBzY3JvbGxiYXIgdGh1bWIgZWxlbWVudC5cbiAgICAgKiBAZGVzYyBUaGUgdGh1bWIgZWxlbWVudCdzIHBhcmVudCBlbGVtZW50IGlzIGFsd2F5cyB0aGUge0BsaW5rIEZpbkJhciNiYXJ8YmFyfSBlbGVtZW50LlxuICAgICAqXG4gICAgICogVGhpcyBwcm9wZXJ0eSBpcyB0eXBpY2FsbHkgcmVmZXJlbmNlZCBpbnRlcm5hbGx5IG9ubHkuIFRoZSBzaXplIGFuZCBwb3NpdGlvbiBvZiB0aGUgdGh1bWIgZWxlbWVudCBpcyBtYWludGFpbmVkIGJ5IGBfY2FsY1RodW1iKClgLlxuICAgICAqIEB0eXBlIHtFbGVtZW50fVxuICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICovXG4gICAgdmFyIHRodW1iID0gdGhpcy50aHVtYiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgIHRodW1iLmNsYXNzTGlzdC5hZGQoJ3RodW1iJyk7XG4gICAgdGh1bWIuc2V0QXR0cmlidXRlKCdzdHlsZScsIFRIVU1CX1NUWUxFKTtcbiAgICB0aHVtYi5vbmNsaWNrID0gYm91bmQuc2hvcnRTdG9wO1xuICAgIHRodW1iLm9ubW91c2VvdmVyID0gYm91bmQub25tb3VzZW92ZXI7XG4gICAgdGh1bWIub25tb3VzZW91dCA9IHRoaXMuX2JvdW5kLm9ubW91c2VvdXQ7XG5cbiAgICAvKipcbiAgICAgKiBAbmFtZSBiYXJcbiAgICAgKiBAc3VtbWFyeSBUaGUgZ2VuZXJhdGVkIHNjcm9sbGJhciBlbGVtZW50LlxuICAgICAqIEBkZXNjIFRoZSBjYWxsZXIgaW5zZXJ0cyB0aGlzIGVsZW1lbnQgaW50byB0aGUgRE9NICh0eXBpY2FsbHkgaW50byB0aGUgY29udGVudCBjb250YWluZXIpIGFuZCB0aGVuIGNhbGxzIGl0cyB7QGxpbmsgRmluQmFyI3Jlc2l6ZXxyZXNpemUoKX0gbWV0aG9kLlxuICAgICAqXG4gICAgICogVGh1cyB0aGUgbm9kZSB0cmVlIGlzIHR5cGljYWxseTpcbiAgICAgKiAqIEEgKipjb250ZW50IGNvbnRhaW5lcioqIGVsZW1lbnQsIHdoaWNoIGNvbnRhaW5zOlxuICAgICAqICAgKiBUaGUgY29udGVudCBlbGVtZW50KHMpXG4gICAgICogICAqIFRoaXMgKipzY3JvbGxiYXIgZWxlbWVudCoqLCB3aGljaCBpbiB0dXJuIGNvbnRhaW5zOlxuICAgICAqICAgICAqIFRoZSAqKnRodW1iIGVsZW1lbnQqKlxuICAgICAqXG4gICAgICogQHR5cGUge0VsZW1lbnR9XG4gICAgICogQG1lbWJlck9mIEZpbkJhci5wcm90b3R5cGVcbiAgICAgKi9cbiAgICB2YXIgYmFyID0gdGhpcy5iYXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgYmFyLmNsYXNzTGlzdC5hZGQoJ2ZpbmJhci12ZXJ0aWNhbCcpO1xuICAgIGJhci5zZXRBdHRyaWJ1dGUoJ3N0eWxlJywgQkFSX1NUWUxFKTtcbiAgICBiYXIub25tb3VzZWRvd24gPSB0aGlzLl9ib3VuZC5vbm1vdXNlZG93bjtcbiAgICBpZiAodGhpcy5wYWdpbmcpIHsgYmFyLm9uY2xpY2sgPSBib3VuZC5vbmNsaWNrOyB9XG4gICAgYmFyLmFwcGVuZENoaWxkKHRodW1iKTtcblxuICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gICAgLy8gcHJlc2V0c1xuICAgIHRoaXMub3JpZW50YXRpb24gPSAndmVydGljYWwnO1xuICAgIHRoaXMuX21pbiA9IHRoaXMuX2luZGV4ID0gMDtcbiAgICB0aGlzLl9tYXggPSAxMDA7XG5cbiAgICAvKipcbiAgICAgKiBXaGVlbCBtZXRyaWMgbm9ybWFsaXphdGlvbiwgYXBwbGllZCBlcXVhbGx5IHRvIGFsbCB0aHJlZSBheGVzLlxuICAgICAqXG4gICAgICogVGhpcyB2YWx1ZSBpcyBvdmVycmlkZGVuIHdpdGggYSBwbGF0Zm9ybS0gYW5kIGJyb3dzZXItc3BlY2lmaWMgd2hlZWwgZmFjdG9yIHdoZW4gYXZhaWxhYmxlIGluIHtAbGluayBGaW5CYXIubm9ybWFsc30uXG4gICAgICpcbiAgICAgKiBUbyBzdXBwcmVzcywgZGVsZXRlIGBGaW5CYXIubm9ybWFsc2AgYmVmb3JlIGluc3RhbnRpYXRpb24gb3Igb3ZlcnJpZGUgdGhpcyBpbnN0YW5jZSB2YXJpYWJsZSAod2l0aCBgMS4wYCkgYWZ0ZXIgaW5zdGFudGlhdGlvbi5cbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICovXG4gICAgdGhpcy5ub3JtYWwgPSBnZXROb3JtYWwoKSB8fCAxLjA7XG5cbiAgICAvLyBvcHRpb25zXG4gICAgT2JqZWN0LmtleXMob3B0aW9ucykuZm9yRWFjaChmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgIHZhciBvcHRpb24gPSBvcHRpb25zW2tleV07XG4gICAgICAgIGlmIChvcHRpb24gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgc3dpdGNoIChrZXkpIHtcblxuICAgICAgICAgICAgICAgIGNhc2UgJ2luZGV4JzpcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5faW5kZXggPSBvcHRpb247XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgICAgICAgY2FzZSAncmFuZ2UnOlxuICAgICAgICAgICAgICAgICAgICB2YWxpZFJhbmdlKG9wdGlvbik7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX21pbiA9IG9wdGlvbi5taW47XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX21heCA9IG9wdGlvbi5tYXg7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29udGVudFNpemUgPSBvcHRpb24ubWF4IC0gb3B0aW9uLm1pbiArIDE7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgICAgICAga2V5LmNoYXJBdCgwKSAhPT0gJ18nICYmXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlb2YgRmluQmFyLnByb3RvdHlwZVtrZXldICE9PSAnZnVuY3Rpb24nXG4gICAgICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gb3ZlcnJpZGUgcHJvdG90eXBlIGRlZmF1bHRzIGZvciBzdGFuZGFyZCA7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBleHRlbmQgd2l0aCBhZGRpdGlvbmFsIHByb3BlcnRpZXMgKGZvciB1c2UgaW4gb25jaGFuZ2UgZXZlbnQgaGFuZGxlcnMpXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzW2tleV0gPSBvcHRpb247XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0sIHRoaXMpO1xuXG4gICAgY3NzSW5qZWN0b3IoY3NzRmluQmFycywgJ2ZpbmJhci1iYXNlJywgb3B0aW9ucy5jc3NTdHlsZXNoZWV0UmVmZXJlbmNlRWxlbWVudCk7XG59XG5cbkZpbkJhci5wcm90b3R5cGUgPSB7XG5cbiAgICAvKipcbiAgICAgKiBAc3VtbWFyeSBUaGUgc2Nyb2xsYmFyIG9yaWVudGF0aW9uLlxuICAgICAqIEBkZXNjIFNldCBieSB0aGUgY29uc3RydWN0b3IgdG8gZWl0aGVyIGAndmVydGljYWwnYCBvciBgJ2hvcml6b250YWwnYC4gU2VlIHRoZSBzaW1pbGFybHkgbmFtZWQgcHJvcGVydHkgaW4gdGhlIHtAbGluayBmaW5iYXJPcHRpb25zfSBvYmplY3QuXG4gICAgICpcbiAgICAgKiBVc2VmdWwgdmFsdWVzIGFyZSBgJ3ZlcnRpY2FsJ2AgKHRoZSBkZWZhdWx0KSBvciBgJ2hvcml6b250YWwnYC5cbiAgICAgKlxuICAgICAqIFNldHRpbmcgdGhpcyBwcm9wZXJ0eSByZXNldHMgYHRoaXMub2hgIGFuZCBgdGhpcy5kZWx0YVByb3BgIGFuZCBjaGFuZ2VzIHRoZSBjbGFzcyBuYW1lcyBzbyBhcyB0byByZXBvc2l0aW9uIHRoZSBzY3JvbGxiYXIgYXMgcGVyIHRoZSBDU1MgcnVsZXMgZm9yIHRoZSBuZXcgb3JpZW50YXRpb24uXG4gICAgICogQGRlZmF1bHQgJ3ZlcnRpY2FsJ1xuICAgICAqIEB0eXBlIHtzdHJpbmd9XG4gICAgICogQG1lbWJlck9mIEZpbkJhci5wcm90b3R5cGVcbiAgICAgKi9cbiAgICBzZXQgb3JpZW50YXRpb24ob3JpZW50YXRpb24pIHtcbiAgICAgICAgaWYgKG9yaWVudGF0aW9uID09PSB0aGlzLl9vcmllbnRhdGlvbikge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fb3JpZW50YXRpb24gPSBvcmllbnRhdGlvbjtcblxuICAgICAgICAvKipcbiAgICAgICAgICogQHJlYWRvbmx5XG4gICAgICAgICAqIEBuYW1lIG9oXG4gICAgICAgICAqIEBzdW1tYXJ5IDx1Pk88L3U+cmllbnRhdGlvbiA8dT5oPC91PmFzaCBmb3IgdGhpcyBzY3JvbGxiYXIuXG4gICAgICAgICAqIEBkZXNjIFNldCBieSB0aGUgYG9yaWVudGF0aW9uYCBzZXR0ZXIgdG8gZWl0aGVyIHRoZSB2ZXJ0aWNhbCBvciB0aGUgaG9yaXpvbnRhbCBvcmllbnRhdGlvbiBoYXNoLiBUaGUgcHJvcGVydHkgc2hvdWxkIGFsd2F5cyBiZSBzeW5jaHJvbml6ZWQgd2l0aCBgb3JpZW50YXRpb25gOyBkbyBub3QgdXBkYXRlIGRpcmVjdGx5IVxuICAgICAgICAgKlxuICAgICAgICAgKiBUaGlzIG9iamVjdCBpcyB1c2VkIGludGVybmFsbHkgdG8gYWNjZXNzIHNjcm9sbGJhcnMnIERPTSBlbGVtZW50IHByb3BlcnRpZXMgaW4gYSBnZW5lcmFsaXplZCB3YXkgd2l0aG91dCBuZWVkaW5nIHRvIGNvbnN0YW50bHkgcXVlcnkgdGhlIHNjcm9sbGJhciBvcmllbnRhdGlvbi4gRm9yIGV4YW1wbGUsIGluc3RlYWQgb2YgZXhwbGljaXRseSBjb2RpbmcgYHRoaXMuYmFyLnRvcGAgZm9yIGEgdmVydGljYWwgc2Nyb2xsYmFyIGFuZCBgdGhpcy5iYXIubGVmdGAgZm9yIGEgaG9yaXpvbnRhbCBzY3JvbGxiYXIsIHNpbXBseSBjb2RlIGB0aGlzLmJhclt0aGlzLm9oLmxlYWRpbmddYCBpbnN0ZWFkLiBTZWUgdGhlIHtAbGluayBvcmllbnRhdGlvbkhhc2hUeXBlfSBkZWZpbml0aW9uIGZvciBkZXRhaWxzLlxuICAgICAgICAgKlxuICAgICAgICAgKiBUaGlzIG9iamVjdCBpcyB1c2VmdWwgZXh0ZXJuYWxseSBmb3IgY29kaW5nIGdlbmVyYWxpemVkIHtAbGluayBmaW5iYXJPbkNoYW5nZX0gZXZlbnQgaGFuZGxlciBmdW5jdGlvbnMgdGhhdCBzZXJ2ZSBib3RoIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIHNjcm9sbGJhcnMuXG4gICAgICAgICAqIEB0eXBlIHtvcmllbnRhdGlvbkhhc2hUeXBlfVxuICAgICAgICAgKiBAbWVtYmVyT2YgRmluQmFyLnByb3RvdHlwZVxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5vaCA9IG9yaWVudGF0aW9uSGFzaGVzW3RoaXMuX29yaWVudGF0aW9uXTtcblxuICAgICAgICBpZiAoIXRoaXMub2gpIHtcbiAgICAgICAgICAgIGVycm9yKCdJbnZhbGlkIHZhbHVlIGZvciBgb3B0aW9ucy5fb3JpZW50YXRpb24uJyk7XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogQG5hbWUgZGVsdGFQcm9wXG4gICAgICAgICAqIEBzdW1tYXJ5IFRoZSBuYW1lIG9mIHRoZSBgV2hlZWxFdmVudGAgcHJvcGVydHkgdGhpcyBzY3JvbGxiYXIgc2hvdWxkIGxpc3RlbiB0by5cbiAgICAgICAgICogQGRlc2MgU2V0IGJ5IHRoZSBjb25zdHJ1Y3Rvci4gU2VlIHRoZSBzaW1pbGFybHkgbmFtZWQgcHJvcGVydHkgaW4gdGhlIHtAbGluayBmaW5iYXJPcHRpb25zfSBvYmplY3QuXG4gICAgICAgICAqXG4gICAgICAgICAqIFVzZWZ1bCB2YWx1ZXMgYXJlIGAnZGVsdGFYJ2AsIGAnZGVsdGFZJ2AsIG9yIGAnZGVsdGFaJ2AuIEEgdmFsdWUgb2YgYG51bGxgIG1lYW5zIHRvIGlnbm9yZSBtb3VzZSB3aGVlbCBldmVudHMgZW50aXJlbHkuXG4gICAgICAgICAqXG4gICAgICAgICAqIFRoZSBtb3VzZSB3aGVlbCBpcyBvbmUtZGltZW5zaW9uYWwgYW5kIG9ubHkgZW1pdHMgZXZlbnRzIHdpdGggYGRlbHRhWWAgZGF0YS4gVGhpcyBwcm9wZXJ0eSBpcyBwcm92aWRlZCBzbyB0aGF0IHlvdSBjYW4gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgb2YgYCdkZWx0YVgnYCB3aXRoIGEgdmFsdWUgb2YgYCdkZWx0YVknYCBvbiB5b3VyIGhvcml6b250YWwgc2Nyb2xsYmFyIHByaW1hcmlseSB0byBhY2NvbW1vZGF0ZSBjZXJ0YWluIFwicGFub3JhbWljXCIgaW50ZXJmYWNlIGRlc2lnbnMgd2hlcmUgdGhlIG1vdXNlIHdoZWVsIHNob3VsZCBjb250cm9sIGhvcml6b250YWwgcmF0aGVyIHRoYW4gdmVydGljYWwgc2Nyb2xsaW5nLiBKdXN0IGdpdmUgYHsgZGVsdGFQcm9wOiAnZGVsdGFZJyB9YCBpbiB5b3VyIGhvcml6b250YWwgc2Nyb2xsYmFyIGluc3RhbnRpYXRpb24uXG4gICAgICAgICAqXG4gICAgICAgICAqIENhdmVhdDogTm90ZSB0aGF0IGEgMi1maW5nZXIgZHJhZyBvbiBhbiBBcHBsZSB0cmFja3BhZCBlbWl0cyBldmVudHMgd2l0aCBfYm90aF8gYGRlbHRhWCBgIGFuZCBgZGVsdGFZYCBkYXRhIHNvIHlvdSBtaWdodCB3YW50IHRvIGRlbGF5IG1ha2luZyB0aGUgYWJvdmUgYWRqdXN0bWVudCB1bnRpbCB5b3UgY2FuIGRldGVybWluZSB0aGF0IHlvdSBhcmUgZ2V0dGluZyBZIGRhdGEgb25seSB3aXRoIG5vIFggZGF0YSBhdCBhbGwgKHdoaWNoIGlzIGEgc3VyZSBiZXQgeW91IG9uIGEgbW91c2Ugd2hlZWwgcmF0aGVyIHRoYW4gYSB0cmFja3BhZCkuXG5cbiAgICAgICAgICogQHR5cGUge29iamVjdHxudWxsfVxuICAgICAgICAgKiBAbWVtYmVyT2YgRmluQmFyLnByb3RvdHlwZVxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5kZWx0YVByb3AgPSB0aGlzLm9oLmRlbHRhO1xuXG4gICAgICAgIHRoaXMuYmFyLmNsYXNzTmFtZSA9IHRoaXMuYmFyLmNsYXNzTmFtZS5yZXBsYWNlKC8odmVydGljYWx8aG9yaXpvbnRhbCkvZywgb3JpZW50YXRpb24pO1xuXG4gICAgICAgIGlmICh0aGlzLmJhci5zdHlsZS5jc3NUZXh0ICE9PSBCQVJfU1RZTEUgfHwgdGhpcy50aHVtYi5zdHlsZS5jc3NUZXh0ICE9PSBUSFVNQl9TVFlMRSkge1xuICAgICAgICAgICAgdGhpcy5iYXIuc2V0QXR0cmlidXRlKCdzdHlsZScsIEJBUl9TVFlMRSk7XG4gICAgICAgICAgICB0aGlzLnRodW1iLnNldEF0dHJpYnV0ZSgnc3R5bGUnLCBUSFVNQl9TVFlMRSk7XG4gICAgICAgICAgICB0aGlzLnJlc2l6ZSgpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBnZXQgb3JpZW50YXRpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9vcmllbnRhdGlvbjtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQHN1bW1hcnkgQ2FsbGJhY2sgZm9yIHNjcm9sbCBldmVudHMuXG4gICAgICogQGRlc2MgU2V0IGJ5IHRoZSBjb25zdHJ1Y3RvciB2aWEgdGhlIHNpbWlsYXJseSBuYW1lZCBwcm9wZXJ0eSBpbiB0aGUge0BsaW5rIGZpbmJhck9wdGlvbnN9IG9iamVjdC4gQWZ0ZXIgaW5zdGFudGlhdGlvbiwgYHRoaXMub25jaGFuZ2VgIG1heSBiZSB1cGRhdGVkIGRpcmVjdGx5LlxuICAgICAqXG4gICAgICogVGhpcyBldmVudCBoYW5kbGVyIGlzIGNhbGxlZCB3aGVuZXZlciB0aGUgdmFsdWUgb2YgdGhlIHNjcm9sbGJhciBpcyBjaGFuZ2VkIHRocm91Z2ggdXNlciBpbnRlcmFjdGlvbi4gVGhlIHR5cGljYWwgdXNlIGNhc2UgaXMgd2hlbiB0aGUgY29udGVudCBpcyBzY3JvbGxlZC4gSXQgaXMgY2FsbGVkIHdpdGggdGhlIGBGaW5CYXJgIG9iamVjdCBhcyBpdHMgY29udGV4dCBhbmQgdGhlIGN1cnJlbnQgdmFsdWUgb2YgdGhlIHNjcm9sbGJhciAoaXRzIGluZGV4LCByb3VuZGVkKSBhcyB0aGUgb25seSBwYXJhbWV0ZXIuXG4gICAgICpcbiAgICAgKiBTZXQgdGhpcyBwcm9wZXJ0eSB0byBgbnVsbGAgdG8gc3RvcCBlbWl0dGluZyBzdWNoIGV2ZW50cy5cbiAgICAgKiBAdHlwZSB7ZnVuY3Rpb24obnVtYmVyKXxudWxsfVxuICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICovXG4gICAgb25jaGFuZ2U6IG51bGwsXG5cbiAgICAvKipcbiAgICAgKiBAc3VtbWFyeSBBZGQgYSBDU1MgY2xhc3MgbmFtZSB0byB0aGUgYmFyIGVsZW1lbnQncyBjbGFzcyBsaXN0LlxuICAgICAqIEBkZXNjIFNldCBieSB0aGUgY29uc3RydWN0b3IuIFNlZSB0aGUgc2ltaWxhcmx5IG5hbWVkIHByb3BlcnR5IGluIHRoZSB7QGxpbmsgZmluYmFyT3B0aW9uc30gb2JqZWN0LlxuICAgICAqXG4gICAgICogVGhlIGJhciBlbGVtZW50J3MgY2xhc3MgbGlzdCB3aWxsIGFsd2F5cyBpbmNsdWRlIGBmaW5iYXItdmVydGljYWxgIChvciBgZmluYmFyLWhvcml6b250YWxgIGJhc2VkIG9uIHRoZSBjdXJyZW50IG9yaWVudGF0aW9uKS4gV2hlbmV2ZXIgdGhpcyBwcm9wZXJ0eSBpcyBzZXQgdG8gc29tZSB2YWx1ZSwgZmlyc3QgdGhlIG9sZCBwcmVmaXgrb3JpZW50YXRpb24gaXMgcmVtb3ZlZCBmcm9tIHRoZSBiYXIgZWxlbWVudCdzIGNsYXNzIGxpc3Q7IHRoZW4gdGhlIG5ldyBwcmVmaXgrb3JpZW50YXRpb24gaXMgYWRkZWQgdG8gdGhlIGJhciBlbGVtZW50J3MgY2xhc3MgbGlzdC4gVGhpcyBwcm9wZXJ0eSBjYXVzZXMgX2FuIGFkZGl0aW9uYWxfIGNsYXNzIG5hbWUgdG8gYmUgYWRkZWQgdG8gdGhlIGJhciBlbGVtZW50J3MgY2xhc3MgbGlzdC4gVGhlcmVmb3JlLCB0aGlzIHByb3BlcnR5IHdpbGwgb25seSBhZGQgYXQgbW9zdCBvbmUgYWRkaXRpb25hbCBjbGFzcyBuYW1lIHRvIHRoZSBsaXN0LlxuICAgICAqXG4gICAgICogVG8gcmVtb3ZlIF9jbGFzc25hbWUtb3JpZW50YXRpb25fIGZyb20gdGhlIGJhciBlbGVtZW50J3MgY2xhc3MgbGlzdCwgc2V0IHRoaXMgcHJvcGVydHkgdG8gYSBmYWxzeSB2YWx1ZSwgc3VjaCBhcyBgbnVsbGAuXG4gICAgICpcbiAgICAgKiA+IE5PVEU6IFlvdSBvbmx5IG5lZWQgdG8gc3BlY2lmeSBhbiBhZGRpdGlvbmFsIGNsYXNzIG5hbWUgd2hlbiB5b3UgbmVlZCB0byBoYXZlIG11bGx0aXBsZSBkaWZmZXJlbnQgc3R5bGVzIG9mIHNjcm9sbGJhcnMgb24gdGhlIHNhbWUgcGFnZS4gSWYgdGhpcyBpcyBub3QgYSByZXF1aXJlbWVudCwgdGhlbiB5b3UgZG9uJ3QgbmVlZCB0byBtYWtlIGEgbmV3IGNsYXNzOyB5b3Ugd291bGQganVzdCBjcmVhdGUgc29tZSBhZGRpdGlvbmFsIHJ1bGVzIHVzaW5nIHRoZSBzYW1lIHNlbGVjdG9ycyBpbiB0aGUgYnVpbHQtaW4gc3R5bGVzaGVldCAoLi4vY3NzL2ZpbmJhcnMuY3NzKTpcbiAgICAgKiAqYGRpdi5maW5iYXItdmVydGljYWxgIChvciBgZGl2LmZpbmJhci1ob3Jpem9udGFsYCkgZm9yIHRoZSBzY3JvbGxiYXJcbiAgICAgKiAqYGRpdi5maW5iYXItdmVydGljYWwgPiBkaXZgIChvciBgZGl2LmZpbmJhci1ob3Jpem9udGFsID4gZGl2YCkgZm9yIHRoZSBcInRodW1iLlwiXG4gICAgICpcbiAgICAgKiBPZiBjb3Vyc2UsIHlvdXIgcnVsZXMgc2hvdWxkIGNvbWUgYWZ0ZXIgdGhlIGJ1aWx0LWlucy5cbiAgICAgKiBAdHlwZSB7c3RyaW5nfVxuICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICovXG4gICAgc2V0IGNsYXNzUHJlZml4KHByZWZpeCkge1xuICAgICAgICBpZiAodGhpcy5fY2xhc3NQcmVmaXgpIHtcbiAgICAgICAgICAgIHRoaXMuYmFyLmNsYXNzTGlzdC5yZW1vdmUodGhpcy5fY2xhc3NQcmVmaXggKyB0aGlzLm9yaWVudGF0aW9uKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX2NsYXNzUHJlZml4ID0gcHJlZml4O1xuXG4gICAgICAgIGlmIChwcmVmaXgpIHtcbiAgICAgICAgICAgIHRoaXMuYmFyLmNsYXNzTGlzdC5hZGQocHJlZml4ICsgJy0nICsgdGhpcy5vcmllbnRhdGlvbik7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGdldCBjbGFzc1ByZWZpeCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NsYXNzUHJlZml4O1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBAbmFtZSBpbmNyZW1lbnRcbiAgICAgKiBAc3VtbWFyeSBOdW1iZXIgb2Ygc2Nyb2xsYmFyIGluZGV4IHVuaXRzIHJlcHJlc2VudGluZyBhIHBhZ2VmdWwuIFVzZWQgZXhjbHVzaXZlbHkgZm9yIHBhZ2luZyB1cCBhbmQgZG93biBhbmQgZm9yIHNldHRpbmcgdGh1bWIgc2l6ZSByZWxhdGl2ZSB0byBjb250ZW50IHNpemUuXG4gICAgICogQGRlc2MgU2V0IGJ5IHRoZSBjb25zdHJ1Y3Rvci4gU2VlIHRoZSBzaW1pbGFybHkgbmFtZWQgcHJvcGVydHkgaW4gdGhlIHtAbGluayBmaW5iYXJPcHRpb25zfSBvYmplY3QuXG4gICAgICpcbiAgICAgKiBDYW4gYWxzbyBiZSBnaXZlbiBhcyBhIHBhcmFtZXRlciB0byB0aGUge0BsaW5rIEZpbkJhciNyZXNpemV8cmVzaXplfSBtZXRob2QsIHdoaWNoIGlzIHBlcnRpbmVudCBiZWNhdXNlIGNvbnRlbnQgYXJlYSBzaXplIGNoYW5nZXMgYWZmZWN0IHRoZSBkZWZpbml0aW9uIG9mIGEgXCJwYWdlZnVsLlwiIEhvd2V2ZXIsIHlvdSBvbmx5IG5lZWQgdG8gZG8gdGhpcyBpZiB0aGlzIHZhbHVlIGlzIGJlaW5nIHVzZWQuIEl0IG5vdCB1c2VkIHdoZW46XG4gICAgICogKiB5b3UgZGVmaW5lIGBwYWdpbmcudXBgIGFuZCBgcGFnaW5nLmRvd25gXG4gICAgICogKiB5b3VyIHNjcm9sbGJhciBpcyB1c2luZyBgc2Nyb2xsUmVhbENvbnRlbnRgXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKiBAbWVtYmVyT2YgRmluQmFyLnByb3RvdHlwZVxuICAgICAqL1xuICAgIGluY3JlbWVudDogMSxcblxuICAgIC8qKlxuICAgICAqIERlZmF1bHQgdmFsdWUgb2YgbXVsdGlwbGllciBmb3IgYFdoZWVsRXZlbnQjZGVsdGFYYCAoaG9yaXpvbnRhbCBzY3JvbGxpbmcgZGVsdGEpLlxuICAgICAqIEBkZWZhdWx0XG4gICAgICogQG1lbWJlck9mIEZpbkJhci5wcm90b3R5cGVcbiAgICAgKi9cbiAgICBkZWx0YVhGYWN0b3I6IDEsXG5cbiAgICAvKipcbiAgICAgKiBEZWZhdWx0IHZhbHVlIG9mIG11bHRpcGxpZXIgZm9yIGBXaGVlbEV2ZW50I2RlbHRhWWAgKHZlcnRpY2FsIHNjcm9sbGluZyBkZWx0YSkuXG4gICAgICogQGRlZmF1bHRcbiAgICAgKiBAbWVtYmVyT2YgRmluQmFyLnByb3RvdHlwZVxuICAgICAqL1xuICAgIGRlbHRhWUZhY3RvcjogMSxcblxuICAgIC8qKlxuICAgICAqIERlZmF1bHQgdmFsdWUgb2YgbXVsdGlwbGllciBmb3IgYFdoZWVsRXZlbnQjZGVsdGFaYCAoZGVscHRoIHNjcm9sbGluZyBkZWx0YSkuXG4gICAgICogQGRlZmF1bHRcbiAgICAgKiBAbWVtYmVyT2YgRmluQmFyLnByb3RvdHlwZVxuICAgICAqL1xuICAgIGRlbHRhWkZhY3RvcjogMSxcblxuICAgIC8qKlxuICAgICAqIEBuYW1lIGJhclN0eWxlc1xuICAgICAqIEBzdW1tYXJ5IFNjcm9sbGJhciBzdHlsZXMgdG8gYmUgYXBwbGllZCBieSB7QGxpbmsgRmluQmFyI3Jlc2l6ZXxyZXNpemUoKX0uXG4gICAgICogQGRlc2MgU2V0IGJ5IHRoZSBjb25zdHJ1Y3Rvci4gU2VlIHRoZSBzaW1pbGFybHkgbmFtZWQgcHJvcGVydHkgaW4gdGhlIHtAbGluayBmaW5iYXJPcHRpb25zfSBvYmplY3QuXG4gICAgICpcbiAgICAgKiBUaGlzIGlzIGEgdmFsdWUgdG8gYmUgYXNzaWduZWQgdG8ge0BsaW5rIEZpbkJhciNzdHlsZXN8c3R5bGVzfSBvbiBlYWNoIGNhbGwgdG8ge0BsaW5rIEZpbkJhciNyZXNpemV8cmVzaXplKCl9LiBUaGF0IGlzLCBhIGhhc2ggb2YgdmFsdWVzIHRvIGJlIGNvcGllZCB0byB0aGUgc2Nyb2xsYmFyIGVsZW1lbnQncyBzdHlsZSBvYmplY3Qgb24gcmVzaXplOyBvciBgbnVsbGAgZm9yIG5vbmUuXG4gICAgICpcbiAgICAgKiBAc2VlIHtAbGluayBGaW5CYXIjc3R5bGV8c3R5bGV9XG4gICAgICogQHR5cGUge2ZpbmJhclN0eWxlc3xudWxsfVxuICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICovXG4gICAgYmFyU3R5bGVzOiBudWxsLFxuXG4gICAgLyoqXG4gICAgICogQG5hbWUgc3R5bGVcbiAgICAgKiBAc3VtbWFyeSBBZGRpdGlvbmFsIHNjcm9sbGJhciBzdHlsZXMuXG4gICAgICogQGRlc2MgU2VlIHR5cGUgZGVmaW5pdGlvbiBmb3IgbW9yZSBkZXRhaWxzLiBUaGVzZSBzdHlsZXMgYXJlIGFwcGxpZWQgZGlyZWN0bHkgdG8gdGhlIHNjcm9sbGJhcidzIGBiYXJgIGVsZW1lbnQuXG4gICAgICpcbiAgICAgKiBWYWx1ZXMgYXJlIGFkanVzdGVkIGFzIGZvbGxvd3MgYmVmb3JlIGJlaW5nIGFwcGxpZWQgdG8gdGhlIGVsZW1lbnQ6XG4gICAgICogMS4gSW5jbHVkZWQgXCJwc2V1ZG8tcHJvcGVydHlcIiBuYW1lcyBmcm9tIHRoZSBzY3JvbGxiYXIncyBvcmllbnRhdGlvbiBoYXNoLCB7QGxpbmsgRmluQmFyI29ofG9ofSwgYXJlIHRyYW5zbGF0ZWQgdG8gYWN0dWFsIHByb3BlcnR5IG5hbWVzIGJlZm9yZSBiZWluZyBhcHBsaWVkLlxuICAgICAqIDIuIFdoZW4gdGhlcmUgYXJlIG1hcmdpbnMsIHBlcmNlbnRhZ2VzIGFyZSB0cmFuc2xhdGVkIHRvIGFic29sdXRlIHBpeGVsIHZhbHVlcyBiZWNhdXNlIENTUyBpZ25vcmVzIG1hcmdpbnMgaW4gaXRzIHBlcmNlbnRhZ2UgY2FsY3VsYXRpb25zLlxuICAgICAqIDMuIElmIHlvdSBnaXZlIGEgdmFsdWUgd2l0aG91dCBhIHVuaXQgKGEgcmF3IG51bWJlciksIFwicHhcIiB1bml0IGlzIGFwcGVuZGVkLlxuICAgICAqXG4gICAgICogR2VuZXJhbCBub3RlczpcbiAgICAgKiAxLiBJdCBpcyBhbHdheXMgcHJlZmVyYWJsZSB0byBzcGVjaWZ5IHN0eWxlcyB2aWEgYSBzdHlsZXNoZWV0LiBPbmx5IHNldCB0aGlzIHByb3BlcnR5IHdoZW4geW91IG5lZWQgdG8gc3BlY2lmaWNhbGx5IG92ZXJyaWRlIChhKSBzdHlsZXNoZWV0IHZhbHVlKHMpLlxuICAgICAqIDIuIENhbiBiZSBzZXQgZGlyZWN0bHkgb3IgdmlhIGNhbGxzIHRvIHRoZSB7QGxpbmsgRmluQmFyI3Jlc2l6ZXxyZXNpemV9IG1ldGhvZC5cbiAgICAgKiAzLiBTaG91bGQgb25seSBiZSBzZXQgYWZ0ZXIgdGhlIHNjcm9sbGJhciBoYXMgYmVlbiBpbnNlcnRlZCBpbnRvIHRoZSBET00uXG4gICAgICogNC4gQmVmb3JlIGFwcGx5aW5nIHRoZXNlIG5ldyB2YWx1ZXMgdG8gdGhlIGVsZW1lbnQsIF9hbGxfIGluLWxpbmUgc3R5bGUgdmFsdWVzIGFyZSByZXNldCAoYnkgcmVtb3ZpbmcgdGhlIGVsZW1lbnQncyBgc3R5bGVgIGF0dHJpYnV0ZSksIGV4cG9zaW5nIGluaGVyaXRlZCB2YWx1ZXMgKGZyb20gc3R5bGVzaGVldHMpLlxuICAgICAqIDUuIEVtcHR5IG9iamVjdCBoYXMgbm8gZWZmZWN0LlxuICAgICAqIDYuIEZhbHNleSB2YWx1ZSBpbiBwbGFjZSBvZiBvYmplY3QgaGFzIG5vIGVmZmVjdC5cbiAgICAgKlxuICAgICAqID4gQ0FWRUFUOiBEbyBub3QgYXR0ZW1wdCB0byB0cmVhdCB0aGUgb2JqZWN0IHlvdSBhc3NpZ24gdG8gdGhpcyBwcm9wZXJ0eSBhcyBpZiBpdCB3ZXJlIGB0aGlzLmJhci5zdHlsZWAuIFNwZWNpZmljYWxseSwgY2hhbmdpbmcgdGhpcyBvYmplY3QgYWZ0ZXIgYXNzaWduaW5nIGl0IHdpbGwgaGF2ZSBubyBlZmZlY3Qgb24gdGhlIHNjcm9sbGJhci4gWW91IG11c3QgYXNzaWduIGl0IGFnYWluIGlmIHlvdSB3YW50IGl0IHRvIGhhdmUgYW4gZWZmZWN0LlxuICAgICAqXG4gICAgICogQHNlZSB7QGxpbmsgRmluQmFyI2JhclN0eWxlc3xiYXJTdHlsZXN9XG4gICAgICogQHR5cGUge2ZpbmJhclN0eWxlc31cbiAgICAgKiBAbWVtYmVyT2YgRmluQmFyLnByb3RvdHlwZVxuICAgICAqL1xuICAgIHNldCBzdHlsZShzdHlsZXMpIHtcbiAgICAgICAgdmFyIGtleXMgPSBPYmplY3Qua2V5cyhzdHlsZXMgPSBleHRlbmQoe30sIHN0eWxlcywgdGhpcy5fYXV4U3R5bGVzKSk7XG5cbiAgICAgICAgaWYgKGtleXMubGVuZ3RoKSB7XG4gICAgICAgICAgICB2YXIgYmFyID0gdGhpcy5iYXIsXG4gICAgICAgICAgICAgICAgYmFyUmVjdCA9IGJhci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSxcbiAgICAgICAgICAgICAgICBjb250YWluZXIgPSB0aGlzLmNvbnRhaW5lciB8fCBiYXIucGFyZW50RWxlbWVudCxcbiAgICAgICAgICAgICAgICBjb250YWluZXJSZWN0ID0gY29udGFpbmVyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLFxuICAgICAgICAgICAgICAgIG9oID0gdGhpcy5vaDtcblxuICAgICAgICAgICAgLy8gQmVmb3JlIGFwcGx5aW5nIG5ldyBzdHlsZXMsIHJldmVydCBhbGwgc3R5bGVzIHRvIHZhbHVlcyBpbmhlcml0ZWQgZnJvbSBzdHlsZXNoZWV0c1xuICAgICAgICAgICAgYmFyLnNldEF0dHJpYnV0ZSgnc3R5bGUnLCBCQVJfU1RZTEUpO1xuXG4gICAgICAgICAgICBrZXlzLmZvckVhY2goZnVuY3Rpb24gKGtleSkge1xuICAgICAgICAgICAgICAgIHZhciB2YWwgPSBzdHlsZXNba2V5XTtcblxuICAgICAgICAgICAgICAgIGlmIChrZXkgaW4gb2gpIHtcbiAgICAgICAgICAgICAgICAgICAga2V5ID0gb2hba2V5XTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIWlzTmFOKE51bWJlcih2YWwpKSkge1xuICAgICAgICAgICAgICAgICAgICB2YWwgPSAodmFsIHx8IDApICsgJ3B4JztcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKC8lJC8udGVzdCh2YWwpKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIFdoZW4gYmFyIHNpemUgZ2l2ZW4gYXMgcGVyY2VudGFnZSBvZiBjb250YWluZXIsIGlmIGJhciBoYXMgbWFyZ2lucywgcmVzdGF0ZSBzaXplIGluIHBpeGVscyBsZXNzIG1hcmdpbnMuXG4gICAgICAgICAgICAgICAgICAgIC8vIChJZiBsZWZ0IGFzIHBlcmNlbnRhZ2UsIENTUydzIGNhbGN1bGF0aW9uIHdpbGwgbm90IGV4Y2x1ZGUgbWFyZ2lucy4pXG4gICAgICAgICAgICAgICAgICAgIHZhciBvcmllbnRlZCA9IGF4aXNba2V5XSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbnMgPSBiYXJSZWN0W29yaWVudGVkLm1hcmdpbkxlYWRpbmddICsgYmFyUmVjdFtvcmllbnRlZC5tYXJnaW5UcmFpbGluZ107XG4gICAgICAgICAgICAgICAgICAgIGlmIChtYXJnaW5zKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2YWwgPSBwYXJzZUludCh2YWwsIDEwKSAvIDEwMCAqIGNvbnRhaW5lclJlY3Rbb3JpZW50ZWQuc2l6ZV0gLSBtYXJnaW5zICsgJ3B4JztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGJhci5zdHlsZVtrZXldID0gdmFsO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQHJlYWRvbmx5XG4gICAgICogQG5hbWUgcGFnaW5nXG4gICAgICogQHN1bW1hcnkgRW5hYmxlIHBhZ2UgdXAvZG4gY2xpY2tzLlxuICAgICAqIEBkZXNjIFNldCBieSB0aGUgY29uc3RydWN0b3IuIFNlZSB0aGUgc2ltaWxhcmx5IG5hbWVkIHByb3BlcnR5IGluIHRoZSB7QGxpbmsgZmluYmFyT3B0aW9uc30gb2JqZWN0LlxuICAgICAqXG4gICAgICogSWYgdHJ1dGh5LCBsaXN0ZW4gZm9yIGNsaWNrcyBpbiBwYWdlLXVwIGFuZCBwYWdlLWRvd24gcmVnaW9ucyBvZiBzY3JvbGxiYXIuXG4gICAgICpcbiAgICAgKiBJZiBhbiBvYmplY3QsIGNhbGwgYC5wYWdpbmcudXAoKWAgb24gcGFnZS11cCBjbGlja3MgYW5kIGAucGFnaW5nLmRvd24oKWAgd2lsbCBiZSBjYWxsZWQgb24gcGFnZS1kb3duIGNsaWNrcy5cbiAgICAgKlxuICAgICAqIENoYW5naW5nIHRoZSB0cnV0aGluZXNzIG9mIHRoaXMgdmFsdWUgYWZ0ZXIgaW5zdGFudGlhdGlvbiBjdXJyZW50bHkgaGFzIG5vIGVmZmVjdC5cbiAgICAgKiBAdHlwZSB7Ym9vbGVhbnxvYmplY3R9XG4gICAgICogQG1lbWJlck9mIEZpbkJhci5wcm90b3R5cGVcbiAgICAgKi9cbiAgICBwYWdpbmc6IHRydWUsXG5cbiAgICAvKipcbiAgICAgKiBAbmFtZSByYW5nZVxuICAgICAqIEBzdW1tYXJ5IFNldHRlciBmb3IgdGhlIG1pbmltdW0gYW5kIG1heGltdW0gc2Nyb2xsIHZhbHVlcy5cbiAgICAgKiBAZGVzYyBTZXQgYnkgdGhlIGNvbnN0cnVjdG9yLiBUaGVzZSB2YWx1ZXMgYXJlIHRoZSBsaW1pdHMgZm9yIHtAbGluayBGb29CYXIjaW5kZXh8aW5kZXh9LlxuICAgICAqXG4gICAgICogVGhlIHNldHRlciBhY2NlcHRzIGFuIG9iamVjdCB3aXRoIGV4YWN0bHkgdHdvIG51bWVyaWMgcHJvcGVydGllczogYC5taW5gIHdoaWNoIG11c3QgYmUgbGVzcyB0aGFuIGAubWF4YC4gVGhlIHZhbHVlcyBhcmUgZXh0cmFjdGVkIGFuZCB0aGUgb2JqZWN0IGlzIGRpc2NhcmRlZC5cbiAgICAgKlxuICAgICAqIFRoZSBnZXR0ZXIgcmV0dXJucyBhIG5ldyBvYmplY3Qgd2l0aCBgLm1pbmAgYW5kICcubWF4YC5cbiAgICAgKlxuICAgICAqIEB0eXBlIHtyYW5nZVR5cGV9XG4gICAgICogQG1lbWJlck9mIEZpbkJhci5wcm90b3R5cGVcbiAgICAgKi9cbiAgICBzZXQgcmFuZ2UocmFuZ2UpIHtcbiAgICAgICAgdmFsaWRSYW5nZShyYW5nZSk7XG4gICAgICAgIHRoaXMuX21pbiA9IHJhbmdlLm1pbjtcbiAgICAgICAgdGhpcy5fbWF4ID0gcmFuZ2UubWF4O1xuICAgICAgICB0aGlzLmNvbnRlbnRTaXplID0gcmFuZ2UubWF4IC0gcmFuZ2UubWluICsgMTtcbiAgICAgICAgdGhpcy5pbmRleCA9IHRoaXMuaW5kZXg7IC8vIHJlLWNsYW1wXG4gICAgfSxcbiAgICBnZXQgcmFuZ2UoKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBtaW46IHRoaXMuX21pbixcbiAgICAgICAgICAgIG1heDogdGhpcy5fbWF4XG4gICAgICAgIH07XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEBzdW1tYXJ5IEluZGV4IHZhbHVlIG9mIHRoZSBzY3JvbGxiYXIuXG4gICAgICogQGRlc2MgVGhpcyBpcyB0aGUgcG9zaXRpb24gb2YgdGhlIHNjcm9sbCB0aHVtYi5cbiAgICAgKlxuICAgICAqIFNldHRpbmcgdGhpcyB2YWx1ZSBjbGFtcHMgaXQgdG8ge0BsaW5rIEZpbkJhciNtaW58bWlufS4ue0BsaW5rIEZpbkJhciNtYXh8bWF4fSwgc2Nyb2xsIHRoZSBjb250ZW50LCBhbmQgbW92ZXMgdGh1bWIuXG4gICAgICpcbiAgICAgKiBHZXR0aW5nIHRoaXMgdmFsdWUgcmV0dXJucyB0aGUgY3VycmVudCBpbmRleC4gVGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgaW4gdGhlIHJhbmdlIGBtaW5gLi5gbWF4YC4gSXQgaXMgaW50ZW50aW9uYWxseSBub3Qgcm91bmRlZC5cbiAgICAgKlxuICAgICAqIFVzZSB0aGlzIHZhbHVlIGFzIGFuIGFsdGVybmF0aXZlIHRvIChvciBpbiBhZGRpdGlvbiB0bykgdXNpbmcgdGhlIHtAbGluayBGaW5CYXIjb25jaGFuZ2V8b25jaGFuZ2V9IGNhbGxiYWNrIGZ1bmN0aW9uLlxuICAgICAqXG4gICAgICogQHNlZSB7QGxpbmsgRmluQmFyI19zZXRTY3JvbGx8X3NldFNjcm9sbH1cbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICovXG4gICAgc2V0IGluZGV4KGlkeCkge1xuICAgICAgICBpZHggPSBNYXRoLm1pbih0aGlzLl9tYXgsIE1hdGgubWF4KHRoaXMuX21pbiwgaWR4KSk7IC8vIGNsYW1wIGl0XG4gICAgICAgIHRoaXMuX3NldFNjcm9sbChpZHgpO1xuICAgICAgICAvLyB0aGlzLl9zZXRUaHVtYlNpemUoKTtcbiAgICB9LFxuICAgIGdldCBpbmRleCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2luZGV4O1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBzdW1tYXJ5IE1vdmUgdGhlIHRodW1iLlxuICAgICAqIEBkZXNjIEFsc28gZGlzcGxheXMgdGhlIGluZGV4IHZhbHVlIGluIHRoZSB0ZXN0IHBhbmVsIGFuZCBpbnZva2VzIHRoZSBjYWxsYmFjay5cbiAgICAgKiBAcGFyYW0gaWR4IC0gVGhlIG5ldyBzY3JvbGwgaW5kZXgsIGEgdmFsdWUgaW4gdGhlIHJhbmdlIGBtaW5gLi5gbWF4YC5cbiAgICAgKiBAcGFyYW0gW3NjYWxlZD1mKGlkeCldIC0gVGhlIG5ldyB0aHVtYiBwb3NpdGlvbiBpbiBwaXhlbHMgYW5kIHNjYWxlZCByZWxhdGl2ZSB0byB0aGUgY29udGFpbmluZyB7QGxpbmsgRmluQmFyI2JhcnxiYXJ9IGVsZW1lbnQsIGkuZS4sIGEgcHJvcG9ydGlvbmFsIG51bWJlciBpbiB0aGUgcmFuZ2UgYDBgLi5gdGh1bWJNYXhgLiBXaGVuIG9taXR0ZWQsIGEgZnVuY3Rpb24gb2YgYGlkeGAgaXMgdXNlZC5cbiAgICAgKiBAbWVtYmVyT2YgRmluQmFyLnByb3RvdHlwZVxuICAgICAqL1xuICAgIF9zZXRTY3JvbGw6IGZ1bmN0aW9uIChpZHgsIHNjYWxlZCkge1xuICAgICAgICB0aGlzLl9pbmRleCA9IGlkeDtcblxuICAgICAgICAvLyBEaXNwbGF5IHRoZSBpbmRleCB2YWx1ZSBpbiB0aGUgdGVzdCBwYW5lbFxuICAgICAgICBpZiAodGhpcy50ZXN0UGFuZWxJdGVtICYmIHRoaXMudGVzdFBhbmVsSXRlbS5pbmRleCBpbnN0YW5jZW9mIEVsZW1lbnQpIHtcbiAgICAgICAgICAgIHRoaXMudGVzdFBhbmVsSXRlbS5pbmRleC5pbm5lckhUTUwgPSBNYXRoLnJvdW5kKGlkeCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDYWxsIHRoZSBjYWxsYmFja1xuICAgICAgICBpZiAodGhpcy5vbmNoYW5nZSkge1xuICAgICAgICAgICAgdGhpcy5vbmNoYW5nZS5jYWxsKHRoaXMsIE1hdGgucm91bmQoaWR4KSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBNb3ZlIHRoZSB0aHVtYlxuICAgICAgICBpZiAoc2NhbGVkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHNjYWxlZCA9IChpZHggLSB0aGlzLl9taW4pIC8gKHRoaXMuX21heCAtIHRoaXMuX21pbikgKiB0aGlzLl90aHVtYk1heDtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnRodW1iLnN0eWxlW3RoaXMub2gubGVhZGluZ10gPSBzY2FsZWQgKyAncHgnO1xuICAgIH0sXG5cbiAgICBzY3JvbGxSZWFsQ29udGVudDogZnVuY3Rpb24gKGlkeCkge1xuICAgICAgICB2YXIgY29udGFpbmVyUmVjdCA9IHRoaXMuY29udGVudC5wYXJlbnRFbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLFxuICAgICAgICAgICAgc2l6ZVByb3AgPSB0aGlzLm9oLnNpemUsXG4gICAgICAgICAgICBtYXhTY3JvbGwgPSBNYXRoLm1heCgwLCB0aGlzLmNvbnRlbnRbc2l6ZVByb3BdIC0gY29udGFpbmVyUmVjdFtzaXplUHJvcF0pLFxuICAgICAgICAgICAgLy9zY3JvbGwgPSBNYXRoLm1pbihpZHgsIG1heFNjcm9sbCk7XG4gICAgICAgICAgICBzY3JvbGwgPSAoaWR4IC0gdGhpcy5fbWluKSAvICh0aGlzLl9tYXggLSB0aGlzLl9taW4pICogbWF4U2Nyb2xsO1xuICAgICAgICAvL2NvbnNvbGUubG9nKCdzY3JvbGw6ICcgKyBzY3JvbGwpO1xuICAgICAgICB0aGlzLmNvbnRlbnQuc3R5bGVbdGhpcy5vaC5sZWFkaW5nXSA9IC1zY3JvbGwgKyAncHgnO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBAc3VtbWFyeSBSZWNhbGN1bGF0ZSB0aHVtYiBwb3NpdGlvbi5cbiAgICAgKlxuICAgICAqIEBkZXNjIFRoaXMgbWV0aG9kIHJlY2FsY3VsYXRlcyB0aGUgdGh1bWIgc2l6ZSBhbmQgcG9zaXRpb24uIENhbGwgaXQgb25jZSBhZnRlciBpbnNlcnRpbmcgeW91ciBzY3JvbGxiYXIgaW50byB0aGUgRE9NLCBhbmQgcmVwZWF0ZWRseSB3aGlsZSByZXNpemluZyB0aGUgc2Nyb2xsYmFyICh3aGljaCB0eXBpY2FsbHkgaGFwcGVucyB3aGVuIHRoZSBzY3JvbGxiYXIncyBwYXJlbnQgaXMgcmVzaXplZCBieSB1c2VyLlxuICAgICAqXG4gICAgICogPiBUaGlzIGZ1bmN0aW9uIHNoaWZ0cyBhcmdzIGlmIGZpcnN0IGFyZyBvbWl0dGVkLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtpbmNyZW1lbnQ9dGhpcy5pbmNyZW1lbnRdIC0gUmVzZXRzIHtAbGluayBGb29CYXIjaW5jcmVtZW50fGluY3JlbWVudH0gKHNlZSkuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge2ZpbmJhclN0eWxlc30gW2JhclN0eWxlcz10aGlzLmJhclN0eWxlc10gLSAoU2VlIHR5cGUgZGVmaW5pdGlvbiBmb3IgZGV0YWlscy4pIFNjcm9sbGJhciBzdHlsZXMgdG8gYmUgYXBwbGllZCB0byB0aGUgYmFyIGVsZW1lbnQuXG4gICAgICpcbiAgICAgKiBPbmx5IHNwZWNpZnkgYSBgYmFyU3R5bGVzYCBvYmplY3Qgd2hlbiB5b3UgbmVlZCB0byBvdmVycmlkZSBzdHlsZXNoZWV0IHZhbHVlcy4gSWYgcHJvdmlkZWQsIGJlY29tZXMgdGhlIG5ldyBkZWZhdWx0IChgdGhpcy5iYXJTdHlsZXNgKSwgZm9yIHVzZSBhcyBhIGRlZmF1bHQgb24gc3Vic2VxdWVudCBjYWxscy5cbiAgICAgKlxuICAgICAqIEl0IGlzIGdlbmVyYWxseSB0aGUgY2FzZSB0aGF0IHRoZSBzY3JvbGxiYXIncyBuZXcgcG9zaXRpb24gaXMgc3VmZmljaWVudGx5IGRlc2NyaWJlZCBieSB0aGUgY3VycmVudCBzdHlsZXMuIFRoZXJlZm9yZSwgaXQgaXMgdW51c3VhbCB0byBuZWVkIHRvIHByb3ZpZGUgYSBgYmFyU3R5bGVzYCBvYmplY3Qgb24gZXZlcnkgY2FsbCB0byBgcmVzaXplYC5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHtGaW5CYXJ9IFNlbGYgZm9yIGNoYWluaW5nLlxuICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICovXG4gICAgcmVzaXplOiBmdW5jdGlvbiAoaW5jcmVtZW50LCBiYXJTdHlsZXMpIHtcbiAgICAgICAgdmFyIGJhciA9IHRoaXMuYmFyO1xuXG4gICAgICAgIGlmICghYmFyLnBhcmVudE5vZGUpIHtcbiAgICAgICAgICAgIHJldHVybjsgLy8gbm90IGluIERPTSB5ZXQgc28gbm90aGluZyB0byBkb1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IHRoaXMuY29udGFpbmVyIHx8IGJhci5wYXJlbnRFbGVtZW50LFxuICAgICAgICAgICAgY29udGFpbmVyUmVjdCA9IGNvbnRhaW5lci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblxuICAgICAgICAvLyBzaGlmdCBhcmdzIGlmIGlmIDFzdCBhcmcgb21pdHRlZFxuICAgICAgICBpZiAodHlwZW9mIGluY3JlbWVudCA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIGJhclN0eWxlcyA9IGluY3JlbWVudDtcbiAgICAgICAgICAgIGluY3JlbWVudCA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc3R5bGUgPSB0aGlzLmJhclN0eWxlcyA9IGJhclN0eWxlcyB8fCB0aGlzLmJhclN0eWxlcztcblxuICAgICAgICAvLyBCb3VuZCB0byByZWFsIGNvbnRlbnQ6IENvbnRlbnQgd2FzIGdpdmVuIGJ1dCBubyBvbmNoYW5nZSBoYW5kbGVyLlxuICAgICAgICAvLyBTZXQgdXAgLm9uY2hhbmdlLCAuY29udGFpbmVyU2l6ZSwgYW5kIC5pbmNyZW1lbnQuXG4gICAgICAgIC8vIE5vdGUgdGhpcyBvbmx5IG1ha2VzIHNlbnNlIGlmIHlvdXIgaW5kZXggdW5pdCBpcyBwaXhlbHMuXG4gICAgICAgIGlmICh0aGlzLmNvbnRlbnQpIHtcbiAgICAgICAgICAgIGlmICghdGhpcy5vbmNoYW5nZSkge1xuICAgICAgICAgICAgICAgIHRoaXMub25jaGFuZ2UgPSB0aGlzLnNjcm9sbFJlYWxDb250ZW50O1xuICAgICAgICAgICAgICAgIHRoaXMuY29udGVudFNpemUgPSB0aGlzLmNvbnRlbnRbdGhpcy5vaC5zaXplXTtcbiAgICAgICAgICAgICAgICB0aGlzLl9taW4gPSAwO1xuICAgICAgICAgICAgICAgIHRoaXMuX21heCA9IHRoaXMuY29udGVudFNpemUgLSAxO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLm9uY2hhbmdlID09PSB0aGlzLnNjcm9sbFJlYWxDb250ZW50KSB7XG4gICAgICAgICAgICB0aGlzLmNvbnRhaW5lclNpemUgPSBjb250YWluZXJSZWN0W3RoaXMub2guc2l6ZV07XG4gICAgICAgICAgICB0aGlzLmluY3JlbWVudCA9IHRoaXMuY29udGFpbmVyU2l6ZSAvICh0aGlzLmNvbnRlbnRTaXplIC0gdGhpcy5jb250YWluZXJTaXplKSAqICh0aGlzLl9tYXggLSB0aGlzLl9taW4pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5jb250YWluZXJTaXplID0gMTtcbiAgICAgICAgICAgIHRoaXMuaW5jcmVtZW50ID0gaW5jcmVtZW50IHx8IHRoaXMuaW5jcmVtZW50O1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGluZGV4ID0gdGhpcy5pbmRleDtcbiAgICAgICAgdGhpcy50ZXN0UGFuZWxJdGVtID0gdGhpcy50ZXN0UGFuZWxJdGVtIHx8IHRoaXMuX2FkZFRlc3RQYW5lbEl0ZW0oKTtcbiAgICAgICAgdGhpcy5fc2V0VGh1bWJTaXplKCk7XG4gICAgICAgIHRoaXMuaW5kZXggPSBpbmRleDtcblxuICAgICAgICBpZiAodGhpcy5kZWx0YVByb3AgIT09IG51bGwpIHtcbiAgICAgICAgICAgIGNvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCd3aGVlbCcsIHRoaXMuX2JvdW5kLm9ud2hlZWwpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEBzdW1tYXJ5IFNob3J0ZW4gdHJhaWxpbmcgZW5kIG9mIHNjcm9sbGJhciBieSB0aGlja25lc3Mgb2Ygc29tZSBvdGhlciBzY3JvbGxiYXIuXG4gICAgICogQGRlc2MgSW4gdGhlIFwiY2xhc3NpY2FsXCIgc2NlbmFyaW8gd2hlcmUgdmVydGljYWwgc2Nyb2xsIGJhciBpcyBvbiB0aGUgcmlnaHQgYW5kIGhvcml6b250YWwgc2Nyb2xsYmFyIGlzIG9uIHRoZSBib3R0b20sIHlvdSB3YW50IHRvIHNob3J0ZW4gdGhlIFwidHJhaWxpbmcgZW5kXCIgKGJvdHRvbSBhbmQgcmlnaHQgZW5kcywgcmVzcGVjdGl2ZWx5KSBvZiBhdCBsZWFzdCBvbmUgb2YgdGhlbSBzbyB0aGV5IGRvbid0IG92ZXJsYXkuXG4gICAgICpcbiAgICAgKiBUaGlzIGNvbnZlbmllbmNlIGZ1bmN0aW9uIGlzIGFuIHByb2dyYW1tYXRpYyBhbHRlcm5hdGl2ZSB0byBoYXJkY29kaW5nIHRoZSBjb3JyZWN0IHN0eWxlIHdpdGggdGhlIGNvcnJlY3QgdmFsdWUgaW4geW91ciBzdHlsZXNoZWV0OyBvciBzZXR0aW5nIHRoZSBjb3JyZWN0IHN0eWxlIHdpdGggdGhlIGNvcnJlY3QgdmFsdWUgaW4gdGhlIHtAbGluayBGaW5CYXIjYmFyU3R5bGVzfGJhclN0eWxlc30gb2JqZWN0LlxuICAgICAqXG4gICAgICogQHNlZSB7QGxpbmsgRmluQmFyI2ZvcmVzaG9ydGVuQnl8Zm9yZXNob3J0ZW5CeX0uXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0ZpbkJhcnxudWxsfSBvdGhlckZpbkJhciAtIE90aGVyIHNjcm9sbGJhciB0byBhdm9pZCBieSBzaG9ydGVuaW5nIHRoaXMgb25lOyBgbnVsbGAgcmVtb3ZlcyB0aGUgdHJhaWxpbmcgc3BhY2VcbiAgICAgKiBAcmV0dXJucyB7RmluQmFyfSBGb3IgY2hhaW5pbmdcbiAgICAgKi9cbiAgICBzaG9ydGVuQnk6IGZ1bmN0aW9uIChvdGhlckZpbkJhcikgeyByZXR1cm4gdGhpcy5zaG9ydGVuRW5kQnkoJ3RyYWlsaW5nJywgb3RoZXJGaW5CYXIpOyB9LFxuXG4gICAgLyoqXG4gICAgICogQHN1bW1hcnkgU2hvcnRlbiBsZWFkaW5nIGVuZCBvZiBzY3JvbGxiYXIgYnkgdGhpY2tuZXNzIG9mIHNvbWUgb3RoZXIgc2Nyb2xsYmFyLlxuICAgICAqIEBkZXNjIFN1cHBvcnRzIG5vbi1jbGFzc2ljYWwgc2Nyb2xsYmFyIHNjZW5hcmlvcyB3aGVyZSB2ZXJ0aWNhbCBzY3JvbGwgYmFyIG1heSBiZSBvbiBsZWZ0IGFuZCBob3Jpem9udGFsIHNjcm9sbGJhciBtYXkgYmUgb24gdG9wLCBpbiB3aGljaCBjYXNlIHlvdSB3YW50IHRvIHNob3J0ZW4gdGhlIFwibGVhZGluZyBlbmRcIiByYXRoZXIgdGhhbiB0aGUgdHJhaWxpbmcgZW5kLlxuICAgICAqIEBzZWUge0BsaW5rIEZpbkJhciNzaG9ydGVuQnl8c2hvcnRlbkJ5fS5cbiAgICAgKiBAcGFyYW0ge0ZpbkJhcnxudWxsfSBvdGhlckZpbkJhciAtIE90aGVyIHNjcm9sbGJhciB0byBhdm9pZCBieSBzaG9ydGVuaW5nIHRoaXMgb25lOyBgbnVsbGAgcmVtb3ZlcyB0aGUgdHJhaWxpbmcgc3BhY2VcbiAgICAgKiBAcmV0dXJucyB7RmluQmFyfSBGb3IgY2hhaW5pbmdcbiAgICAgKi9cbiAgICBmb3Jlc2hvcnRlbkJ5OiBmdW5jdGlvbiAob3RoZXJGaW5CYXIpIHsgcmV0dXJuIHRoaXMuc2hvcnRlbkVuZEJ5KCdsZWFkaW5nJywgb3RoZXJGaW5CYXIpOyB9LFxuXG4gICAgLyoqXG4gICAgICogQHN1bW1hcnkgR2VuZXJhbGl6ZWQgc2hvcnRlbmluZyBmdW5jdGlvbi5cbiAgICAgKiBAc2VlIHtAbGluayBGaW5CYXIjc2hvcnRlbkJ5fHNob3J0ZW5CeX0uXG4gICAgICogQHNlZSB7QGxpbmsgRmluQmFyI2ZvcmVzaG9ydGVuQnl8Zm9yZXNob3J0ZW5CeX0uXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IHdoaWNoRW5kIC0gYSBDU1Mgc3R5bGUgcHJvcGVydHkgbmFtZSBvciBhbiBvcmllbnRhdGlvbiBoYXNoIG5hbWUgdGhhdCB0cmFuc2xhdGVzIHRvIGEgQ1NTIHN0eWxlIHByb3BlcnR5IG5hbWUuXG4gICAgICogQHBhcmFtIHtGaW5CYXJ8bnVsbH0gb3RoZXJGaW5CYXIgLSBPdGhlciBzY3JvbGxiYXIgdG8gYXZvaWQgYnkgc2hvcnRlbmluZyB0aGlzIG9uZTsgYG51bGxgIHJlbW92ZXMgdGhlIHRyYWlsaW5nIHNwYWNlXG4gICAgICogQHJldHVybnMge0ZpbkJhcn0gRm9yIGNoYWluaW5nXG4gICAgICovXG4gICAgc2hvcnRlbkVuZEJ5OiBmdW5jdGlvbiAod2hpY2hFbmQsIG90aGVyRmluQmFyKSB7XG4gICAgICAgIGlmICghb3RoZXJGaW5CYXIpIHtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLl9hdXhTdHlsZXM7XG4gICAgICAgIH0gZWxzZSBpZiAob3RoZXJGaW5CYXIgaW5zdGFuY2VvZiBGaW5CYXIgJiYgb3RoZXJGaW5CYXIub3JpZW50YXRpb24gIT09IHRoaXMub3JpZW50YXRpb24pIHtcbiAgICAgICAgICAgIHZhciBvdGhlclN0eWxlID0gd2luZG93LmdldENvbXB1dGVkU3R5bGUob3RoZXJGaW5CYXIuYmFyKSxcbiAgICAgICAgICAgICAgICBvb2ggPSBvcmllbnRhdGlvbkhhc2hlc1tvdGhlckZpbkJhci5vcmllbnRhdGlvbl07XG4gICAgICAgICAgICB0aGlzLl9hdXhTdHlsZXMgPSB7fTtcbiAgICAgICAgICAgIHRoaXMuX2F1eFN0eWxlc1t3aGljaEVuZF0gPSBvdGhlclN0eWxlW29vaC50aGlja25lc3NdO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzOyAvLyBmb3IgY2hhaW5pbmdcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAc3VtbWFyeSBTZXRzIHRoZSBwcm9wb3J0aW9uYWwgdGh1bWIgc2l6ZSBhbmQgaGlkZXMgdGh1bWIgd2hlbiAxMDAlLlxuICAgICAqIEBkZXNjIFRoZSB0aHVtYiBzaXplIGhhcyBhbiBhYnNvbHV0ZSBtaW5pbXVtIG9mIDIwIChwaXhlbHMpLlxuICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICovXG4gICAgX3NldFRodW1iU2l6ZTogZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgb2ggPSB0aGlzLm9oLFxuICAgICAgICAgICAgdGh1bWJDb21wID0gd2luZG93LmdldENvbXB1dGVkU3R5bGUodGhpcy50aHVtYiksXG4gICAgICAgICAgICB0aHVtYk1hcmdpbkxlYWRpbmcgPSBwYXJzZUludCh0aHVtYkNvbXBbb2gubWFyZ2luTGVhZGluZ10pLFxuICAgICAgICAgICAgdGh1bWJNYXJnaW5UcmFpbGluZyA9IHBhcnNlSW50KHRodW1iQ29tcFtvaC5tYXJnaW5UcmFpbGluZ10pLFxuICAgICAgICAgICAgdGh1bWJNYXJnaW5zID0gdGh1bWJNYXJnaW5MZWFkaW5nICsgdGh1bWJNYXJnaW5UcmFpbGluZyxcbiAgICAgICAgICAgIGJhclNpemUgPSB0aGlzLmJhci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVtvaC5zaXplXSxcbiAgICAgICAgICAgIHRodW1iU2l6ZSA9IE1hdGgubWF4KDIwLCBiYXJTaXplICogdGhpcy5jb250YWluZXJTaXplIC8gdGhpcy5jb250ZW50U2l6ZSk7XG5cbiAgICAgICAgaWYgKHRoaXMuY29udGFpbmVyU2l6ZSA8IHRoaXMuY29udGVudFNpemUpIHtcbiAgICAgICAgICAgIHRoaXMuYmFyLnN0eWxlLnZpc2liaWxpdHkgPSAndmlzaWJsZSc7XG4gICAgICAgICAgICB0aGlzLnRodW1iLnN0eWxlW29oLnNpemVdID0gdGh1bWJTaXplICsgJ3B4JztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuYmFyLnN0eWxlLnZpc2liaWxpdHkgPSAnaGlkZGVuJztcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBAcHJpdmF0ZVxuICAgICAgICAgKiBAbmFtZSBfdGh1bWJNYXhcbiAgICAgICAgICogQHN1bW1hcnkgTWF4aW11bSBvZmZzZXQgb2YgdGh1bWIncyBsZWFkaW5nIGVkZ2UuXG4gICAgICAgICAqIEBkZXNjIFRoaXMgaXMgdGhlIHBpeGVsIG9mZnNldCB3aXRoaW4gdGhlIHNjcm9sbGJhciBvZiB0aGUgdGh1bWIgd2hlbiBpdCBpcyBhdCBpdHMgbWF4aW11bSBwb3NpdGlvbiBhdCB0aGUgZXh0cmVtZSBlbmQgb2YgaXRzIHJhbmdlLlxuICAgICAgICAgKlxuICAgICAgICAgKiBUaGlzIHZhbHVlIHRha2VzIGludG8gYWNjb3VudCB0aGUgbmV3bHkgY2FsY3VsYXRlZCBzaXplIG9mIHRoZSB0aHVtYiBlbGVtZW50IChpbmNsdWRpbmcgaXRzIG1hcmdpbnMpIGFuZCB0aGUgaW5uZXIgc2l6ZSBvZiB0aGUgc2Nyb2xsYmFyICh0aGUgdGh1bWIncyBjb250YWluaW5nIGVsZW1lbnQsIGluY2x1ZGluZyBfaXRzXyBtYXJnaW5zKS5cbiAgICAgICAgICpcbiAgICAgICAgICogTk9URTogU2Nyb2xsYmFyIHBhZGRpbmcgaXMgbm90IHRha2VuIGludG8gYWNjb3VudCBhbmQgYXNzdW1lZCB0byBiZSAwIGluIHRoZSBjdXJyZW50IGltcGxlbWVudGF0aW9uIGFuZCBpcyBhc3N1bWVkIHRvIGJlIGAwYDsgdXNlIHRodW1iIG1hcmdpbnMgaW4gcGxhY2Ugb2Ygc2Nyb2xsYmFyIHBhZGRpbmcuXG4gICAgICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICAgICAqL1xuICAgICAgICB0aGlzLl90aHVtYk1heCA9IGJhclNpemUgLSB0aHVtYlNpemUgLSB0aHVtYk1hcmdpbnM7XG5cbiAgICAgICAgdGhpcy5fdGh1bWJNYXJnaW5MZWFkaW5nID0gdGh1bWJNYXJnaW5MZWFkaW5nOyAvLyB1c2VkIGluIG1vdXNlZG93blxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBAc3VtbWFyeSBSZW1vdmUgdGhlIHNjcm9sbGJhci5cbiAgICAgKiBAZGVzYyBVbmhvb2tzIGFsbCB0aGUgZXZlbnQgaGFuZGxlcnMgYW5kIHRoZW4gcmVtb3ZlcyB0aGUgZWxlbWVudCBmcm9tIHRoZSBET00uIEFsd2F5cyBjYWxsIHRoaXMgbWV0aG9kIHByaW9yIHRvIGRpc3Bvc2luZyBvZiB0aGUgc2Nyb2xsYmFyIG9iamVjdC5cbiAgICAgKiBAbWVtYmVyT2YgRmluQmFyLnByb3RvdHlwZVxuICAgICAqL1xuICAgIHJlbW92ZTogZnVuY3Rpb24gKCkge1xuICAgICAgICB0aGlzLmJhci5vbm1vdXNlZG93biA9IG51bGw7XG4gICAgICAgIHRoaXMuX3JlbW92ZUV2dCgnbW91c2Vtb3ZlJyk7XG4gICAgICAgIHRoaXMuX3JlbW92ZUV2dCgnbW91c2V1cCcpO1xuXG4gICAgICAgICh0aGlzLmNvbnRhaW5lciB8fCB0aGlzLmJhci5wYXJlbnRFbGVtZW50KS5fcmVtb3ZlRXZ0KCd3aGVlbCcpO1xuXG4gICAgICAgIHRoaXMuYmFyLm9uY2xpY2sgPVxuICAgICAgICAgICAgdGhpcy50aHVtYi5vbmNsaWNrID1cbiAgICAgICAgICAgICAgICB0aGlzLnRodW1iLm9ubW91c2VvdmVyID1cbiAgICAgICAgICAgICAgICAgICAgdGhpcy50aHVtYi50cmFuc2l0aW9uZW5kID1cbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMudGh1bWIub25tb3VzZW91dCA9IG51bGw7XG5cbiAgICAgICAgdGhpcy5iYXIucmVtb3ZlKCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQGZ1bmN0aW9uIF9hZGRUZXN0UGFuZWxJdGVtXG4gICAgICogQHN1bW1hcnkgQXBwZW5kIGEgdGVzdCBwYW5lbCBlbGVtZW50LlxuICAgICAqIEBkZXNjIElmIHRoZXJlIGlzIGEgdGVzdCBwYW5lbCBpbiB0aGUgRE9NICh0eXBpY2FsbHkgYW4gYDxvbD4uLi48L29sPmAgZWxlbWVudCkgd2l0aCBjbGFzcyBuYW1lcyBvZiBib3RoIGB0aGlzLmNsYXNzUHJlZml4YCBhbmQgYCd0ZXN0LXBhbmVsJ2AgKG9yLCBiYXJyaW5nIHRoYXQsIGFueSBlbGVtZW50IHdpdGggY2xhc3MgbmFtZSBgJ3Rlc3QtcGFuZWwnYCksIGFuIGA8bGk+Li4uPC9saT5gIGVsZW1lbnQgd2lsbCBiZSBjcmVhdGVkIGFuZCBhcHBlbmRlZCB0byBpdC4gVGhpcyBuZXcgZWxlbWVudCB3aWxsIGNvbnRhaW4gYSBzcGFuIGZvciBlYWNoIGNsYXNzIG5hbWUgZ2l2ZW4uXG4gICAgICpcbiAgICAgKiBZb3Ugc2hvdWxkIGRlZmluZSBhIENTUyBzZWxlY3RvciBgLmxpc3RlbmluZ2AgZm9yIHRoZXNlIHNwYW5zLiBUaGlzIGNsYXNzIHdpbGwgYmUgYWRkZWQgdG8gdGhlIHNwYW5zIHRvIGFsdGVyIHRoZWlyIGFwcGVhcmFuY2Ugd2hlbiBhIGxpc3RlbmVyIGlzIGFkZGVkIHdpdGggdGhhdCBjbGFzcyBuYW1lIChwcmVmaXhlZCB3aXRoICdvbicpLlxuICAgICAqXG4gICAgICogKFRoaXMgaXMgYW4gaW50ZXJuYWwgZnVuY3Rpb24gdGhhdCBpcyBjYWxsZWQgb25jZSBieSB0aGUgY29uc3RydWN0b3Igb24gZXZlcnkgaW5zdGFudGlhdGlvbi4pXG4gICAgICogQHJldHVybnMge0VsZW1lbnR8dW5kZWZpbmVkfSBUaGUgYXBwZW5kZWQgYDxsaT4uLi48L2xpPmAgZWxlbWVudCBvciBgdW5kZWZpbmVkYCBpZiB0aGVyZSBpcyBubyB0ZXN0IHBhbmVsLlxuICAgICAqIEBtZW1iZXJPZiBGaW5CYXIucHJvdG90eXBlXG4gICAgICovXG4gICAgX2FkZFRlc3RQYW5lbEl0ZW06IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgdmFyIHRlc3RQYW5lbEl0ZW0sXG4gICAgICAgICAgICB0ZXN0UGFuZWxFbGVtZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLicgKyB0aGlzLl9jbGFzc1ByZWZpeCArICcudGVzdC1wYW5lbCcpIHx8IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy50ZXN0LXBhbmVsJyk7XG5cbiAgICAgICAgaWYgKHRlc3RQYW5lbEVsZW1lbnQpIHtcbiAgICAgICAgICAgIHZhciB0ZXN0UGFuZWxJdGVtUGFydE5hbWVzID0gWyAnbW91c2Vkb3duJywgJ21vdXNlbW92ZScsICdtb3VzZXVwJywgJ2luZGV4JyBdLFxuICAgICAgICAgICAgICAgIGl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsaScpO1xuXG4gICAgICAgICAgICB0ZXN0UGFuZWxJdGVtUGFydE5hbWVzLmZvckVhY2goZnVuY3Rpb24gKHBhcnROYW1lKSB7XG4gICAgICAgICAgICAgICAgaXRlbS5pbm5lckhUTUwgKz0gJzxzcGFuIGNsYXNzPVwiJyArIHBhcnROYW1lICsgJ1wiPicgKyBwYXJ0TmFtZS5yZXBsYWNlKCdtb3VzZScsICcnKSArICc8L3NwYW4+JztcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICB0ZXN0UGFuZWxFbGVtZW50LmFwcGVuZENoaWxkKGl0ZW0pO1xuXG4gICAgICAgICAgICB0ZXN0UGFuZWxJdGVtID0ge307XG4gICAgICAgICAgICB0ZXN0UGFuZWxJdGVtUGFydE5hbWVzLmZvckVhY2goZnVuY3Rpb24gKHBhcnROYW1lKSB7XG4gICAgICAgICAgICAgICAgdGVzdFBhbmVsSXRlbVtwYXJ0TmFtZV0gPSBpdGVtLmdldEVsZW1lbnRzQnlDbGFzc05hbWUocGFydE5hbWUpWzBdO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGVzdFBhbmVsSXRlbTtcbiAgICB9LFxuXG4gICAgX2FkZEV2dDogZnVuY3Rpb24gKGV2dE5hbWUpIHtcbiAgICAgICAgdmFyIHNweSA9IHRoaXMudGVzdFBhbmVsSXRlbSAmJiB0aGlzLnRlc3RQYW5lbEl0ZW1bZXZ0TmFtZV07XG4gICAgICAgIGlmIChzcHkpIHsgc3B5LmNsYXNzTGlzdC5hZGQoJ2xpc3RlbmluZycpOyB9XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKGV2dE5hbWUsIHRoaXMuX2JvdW5kWydvbicgKyBldnROYW1lXSk7XG4gICAgfSxcblxuICAgIF9yZW1vdmVFdnQ6IGZ1bmN0aW9uIChldnROYW1lKSB7XG4gICAgICAgIHZhciBzcHkgPSB0aGlzLnRlc3RQYW5lbEl0ZW0gJiYgdGhpcy50ZXN0UGFuZWxJdGVtW2V2dE5hbWVdO1xuICAgICAgICBpZiAoc3B5KSB7IHNweS5jbGFzc0xpc3QucmVtb3ZlKCdsaXN0ZW5pbmcnKTsgfVxuICAgICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcihldnROYW1lLCB0aGlzLl9ib3VuZFsnb24nICsgZXZ0TmFtZV0pO1xuICAgIH1cbn07XG5cbmZ1bmN0aW9uIGV4dGVuZChvYmopIHtcbiAgICBmb3IgKHZhciBpID0gMTsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7ICsraSkge1xuICAgICAgICB2YXIgb2JqbiA9IGFyZ3VtZW50c1tpXTtcbiAgICAgICAgaWYgKG9iam4pIHtcbiAgICAgICAgICAgIGZvciAodmFyIGtleSBpbiBvYmpuKSB7XG4gICAgICAgICAgICAgICAgb2JqW2tleV0gPSBvYmpuW2tleV07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG9iajtcbn1cblxuZnVuY3Rpb24gdmFsaWRSYW5nZShyYW5nZSkge1xuICAgIHZhciBrZXlzID0gT2JqZWN0LmtleXMocmFuZ2UpLFxuICAgICAgICB2YWxpZCA9ICBrZXlzLmxlbmd0aCA9PT0gMiAmJlxuICAgICAgICAgICAgdHlwZW9mIHJhbmdlLm1pbiA9PT0gJ251bWJlcicgJiZcbiAgICAgICAgICAgIHR5cGVvZiByYW5nZS5tYXggPT09ICdudW1iZXInICYmXG4gICAgICAgICAgICByYW5nZS5taW4gPD0gcmFuZ2UubWF4O1xuXG4gICAgaWYgKCF2YWxpZCkge1xuICAgICAgICBlcnJvcignSW52YWxpZCAucmFuZ2Ugb2JqZWN0LicpO1xuICAgIH1cbn1cblxuLyoqXG4gKiBAcHJpdmF0ZVxuICogQG5hbWUgaGFuZGxlcnNUb0JlQm91bmRcbiAqIEB0eXBlIHtvYmplY3R9XG4gKiBAZGVzYyBUaGUgZnVuY3Rpb25zIGRlZmluZWQgaW4gdGhpcyBvYmplY3QgYXJlIGFsbCBET00gZXZlbnQgaGFuZGxlcnMgdGhhdCBhcmUgYm91bmQgYnkgdGhlIEZpbkJhciBjb25zdHJ1Y3RvciB0byBlYWNoIG5ldyBpbnN0YW5jZS4gSW4gb3RoZXIgd29yZHMsIHRoZSBgdGhpc2AgdmFsdWUgb2YgdGhlc2UgaGFuZGxlcnMsIG9uY2UgYm91bmQsIHJlZmVyIHRvIHRoZSBGaW5CYXIgb2JqZWN0IGFuZCBub3QgdG8gdGhlIGV2ZW50IGVtaXR0ZXIuIFwiRG8gbm90IGNvbnN1bWUgcmF3LlwiXG4gKi9cbnZhciBoYW5kbGVyc1RvQmVCb3VuZCA9IHtcbiAgICBzaG9ydFN0b3A6IGZ1bmN0aW9uIChldnQpIHtcbiAgICAgICAgZXZ0LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIH0sXG5cbiAgICBvbndoZWVsOiBmdW5jdGlvbiAoZXZ0KSB7XG4gICAgICAgIHRoaXMuaW5kZXggKz0gZXZ0W3RoaXMuZGVsdGFQcm9wXSAqIHRoaXNbdGhpcy5kZWx0YVByb3AgKyAnRmFjdG9yJ10gKiB0aGlzLm5vcm1hbDtcbiAgICAgICAgZXZ0LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICBldnQucHJldmVudERlZmF1bHQoKTtcbiAgICB9LFxuXG4gICAgb25jbGljazogZnVuY3Rpb24gKGV2dCkge1xuICAgICAgICB2YXIgdGh1bWJCb3ggPSB0aGlzLnRodW1iLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLFxuICAgICAgICAgICAgZ29pbmdVcCA9IGV2dFt0aGlzLm9oLmNvb3JkaW5hdGVdIDwgdGh1bWJCb3hbdGhpcy5vaC5sZWFkaW5nXTtcblxuICAgICAgICBpZiAodHlwZW9mIHRoaXMucGFnaW5nID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgdGhpcy5pbmRleCA9IHRoaXMucGFnaW5nW2dvaW5nVXAgPyAndXAnIDogJ2Rvd24nXShNYXRoLnJvdW5kKHRoaXMuaW5kZXgpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuaW5kZXggKz0gZ29pbmdVcCA/IC10aGlzLmluY3JlbWVudCA6IHRoaXMuaW5jcmVtZW50O1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gbWFrZSB0aGUgdGh1bWIgZ2xvdyBtb21lbnRhcmlseVxuICAgICAgICB0aGlzLnRodW1iLmNsYXNzTGlzdC5hZGQoJ2hvdmVyJyk7XG4gICAgICAgIHZhciBzZWxmID0gdGhpcztcbiAgICAgICAgdGhpcy50aHVtYi5hZGRFdmVudExpc3RlbmVyKCd0cmFuc2l0aW9uZW5kJywgZnVuY3Rpb24gd2FpdEZvckl0KCkge1xuICAgICAgICAgICAgdGhpcy5yZW1vdmVFdmVudExpc3RlbmVyKCd0cmFuc2l0aW9uZW5kJywgd2FpdEZvckl0KTtcbiAgICAgICAgICAgIHNlbGYuX2JvdW5kLm9ubW91c2V1cChldnQpO1xuICAgICAgICB9KTtcblxuICAgICAgICBldnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgfSxcblxuICAgIG9ubW91c2VvdmVyOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHRoaXMudGh1bWIuY2xhc3NMaXN0LmFkZCgnaG92ZXInKTtcbiAgICB9LFxuXG4gICAgb25tb3VzZW91dDogZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoIXRoaXMuZHJhZ2dpbmcpIHtcbiAgICAgICAgICAgIHRoaXMudGh1bWIuY2xhc3NMaXN0LnJlbW92ZSgnaG92ZXInKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBvbm1vdXNlZG93bjogZnVuY3Rpb24gKGV2dCkge1xuICAgICAgICB2YXIgdGh1bWJCb3ggPSB0aGlzLnRodW1iLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICB0aGlzLnBpbk9mZnNldCA9IGV2dFt0aGlzLm9oLmF4aXNdIC0gdGh1bWJCb3hbdGhpcy5vaC5sZWFkaW5nXSArIHRoaXMuYmFyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpW3RoaXMub2gubGVhZGluZ10gKyB0aGlzLl90aHVtYk1hcmdpbkxlYWRpbmc7XG4gICAgICAgIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZS5jdXJzb3IgPSAnZGVmYXVsdCc7XG5cbiAgICAgICAgdGhpcy5kcmFnZ2luZyA9IHRydWU7XG5cbiAgICAgICAgdGhpcy5fYWRkRXZ0KCdtb3VzZW1vdmUnKTtcbiAgICAgICAgdGhpcy5fYWRkRXZ0KCdtb3VzZXVwJyk7XG5cbiAgICAgICAgZXZ0LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICBldnQucHJldmVudERlZmF1bHQoKTtcbiAgICB9LFxuXG4gICAgb25tb3VzZW1vdmU6IGZ1bmN0aW9uIChldnQpIHtcbiAgICAgICAgaWYgKCEoZXZ0LmJ1dHRvbnMgJiAxKSkge1xuICAgICAgICAgICAgLy8gbW91c2UgYnV0dG9uIG1heSBoYXZlIGJlZW4gcmVsZWFzZWQgd2l0aG91dCBgb25tb3VzZXVwYCB0cmlnZ2VyaW5nIChzZWVcbiAgICAgICAgICAgIHdpbmRvdy5kaXNwYXRjaEV2ZW50KG5ldyBNb3VzZUV2ZW50KCdtb3VzZXVwJywgZXZ0KSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgc2NhbGVkID0gTWF0aC5taW4odGhpcy5fdGh1bWJNYXgsIE1hdGgubWF4KDAsIGV2dFt0aGlzLm9oLmF4aXNdIC0gdGhpcy5waW5PZmZzZXQpKTtcbiAgICAgICAgdmFyIGlkeCA9IHNjYWxlZCAvIHRoaXMuX3RodW1iTWF4ICogKHRoaXMuX21heCAtIHRoaXMuX21pbikgKyB0aGlzLl9taW47XG5cbiAgICAgICAgdGhpcy5fc2V0U2Nyb2xsKGlkeCwgc2NhbGVkKTtcblxuICAgICAgICBldnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgIGV2dC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH0sXG5cbiAgICBvbm1vdXNldXA6IGZ1bmN0aW9uIChldnQpIHtcbiAgICAgICAgdGhpcy5fcmVtb3ZlRXZ0KCdtb3VzZW1vdmUnKTtcbiAgICAgICAgdGhpcy5fcmVtb3ZlRXZ0KCdtb3VzZXVwJyk7XG5cbiAgICAgICAgdGhpcy5kcmFnZ2luZyA9IGZhbHNlO1xuXG4gICAgICAgIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZS5jdXJzb3IgPSAnYXV0byc7XG5cbiAgICAgICAgdmFyIHRodW1iQm94ID0gdGhpcy50aHVtYi5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgdGh1bWJCb3gubGVmdCA8PSBldnQuY2xpZW50WCAmJiBldnQuY2xpZW50WCA8PSB0aHVtYkJveC5yaWdodCAmJlxuICAgICAgICAgICAgdGh1bWJCb3gudG9wIDw9IGV2dC5jbGllbnRZICYmIGV2dC5jbGllbnRZIDw9IHRodW1iQm94LmJvdHRvbVxuICAgICAgICApIHtcbiAgICAgICAgICAgIHRoaXMuX2JvdW5kLm9ubW91c2VvdmVyKGV2dCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLl9ib3VuZC5vbm1vdXNlb3V0KGV2dCk7XG4gICAgICAgIH1cblxuICAgICAgICBldnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgIGV2dC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH1cbn07XG5cbi8qKlxuICogVGFibGUgb2Ygd2hlZWwgbm9ybWFscyB0byB3ZWJraXQuXG4gKlxuICogVGhpcyBvYmplY3QgaXMgYSBkaWN0aW9uYXJ5IG9mIHBsYXRmb3JtIGRpY3Rpb25hcmllcywga2V5ZWQgYnk6XG4gKiAqIGBtYWNgIOKAlCBtYWNPU1xuICogKiBgd2luYCDigJQgV2luZG93XG4gKlxuICogRWFjaCBwbGF0Zm9ybSBkaWN0aW9uYXJ5IGlzIGtleWVkIGJ5OlxuICogKiBgd2Via2l0YCDigJQgQ2hyb21lLCBPcGVyYSwgU2FmYXJpXG4gKiAqIGBtb3pgIOKAlCBGaXJlZm94XG4gKiAqIGBtc2Ag4oCUIElFIDExIF8oV2luZG93cyBvbmx5KV9cbiAqICogYGVkZ2VgIOKAlCBFZGdlIF8oV2luZG93cyBvbmx5KV9cbiAqXG4gKiBAdG9kbyBhZGQgYGxpbnV4YCBwbGF0Zm9ybVxuICogQHR5cGUge29iamVjdH1cbiAqL1xuRmluQmFyLm5vcm1hbHMgPSB7XG4gICAgbWFjOiB7XG4gICAgICAgIHdlYmtpdDogMS4wLFxuICAgICAgICBtb3o6IDM1XG4gICAgfSxcbiAgICB3aW46IHtcbiAgICAgICAgd2Via2l0OiAyLjYsXG4gICAgICAgIG1vejogODUsXG4gICAgICAgIG1zOiAyLjksXG4gICAgICAgIGVkZ2U6IDJcbiAgICB9XG59O1xuXG5mdW5jdGlvbiBnZXROb3JtYWwoKSB7XG4gICAgdmFyIG5hdiA9IHdpbmRvdy5uYXZpZ2F0b3IsIHVhID0gbmF2LnVzZXJBZ2VudDtcbiAgICB2YXIgcGxhdGZvcm0gPSBuYXYucGxhdGZvcm0uc3Vic3RyKDAsIDMpLnRvTG93ZXJDYXNlKCk7XG4gICAgdmFyIGJyb3dzZXIgPSAvRWRnZS8udGVzdCh1YSkgPyAnZWRnZScgOlxuICAgICAgICAvT3BlcmF8T1BSfENocm9tZXxTYWZhcmkvLnRlc3QodWEpID8gJ3dlYmtpdCcgOlxuICAgICAgICAgICAgL0ZpcmVmb3gvLnRlc3QodWEpID8gJ21veicgOlxuICAgICAgICAgICAgICAgIGRvY3VtZW50LmRvY3VtZW50TW9kZSA/ICdtcycgOiAvLyBpbnRlcm5ldCBleHBsb3JlclxuICAgICAgICAgICAgICAgICAgICB1bmRlZmluZWQ7XG4gICAgdmFyIHBsYXRmb3JtRGljdGlvbmFyeSA9IEZpbkJhci5ub3JtYWxzW3BsYXRmb3JtXSB8fCB7fTtcbiAgICByZXR1cm4gcGxhdGZvcm1EaWN0aW9uYXJ5W2Jyb3dzZXJdO1xufVxuXG52YXIgb3JpZW50YXRpb25IYXNoZXMgPSB7XG4gICAgdmVydGljYWw6IHtcbiAgICAgICAgY29vcmRpbmF0ZTogICAgICdjbGllbnRZJyxcbiAgICAgICAgYXhpczogICAgICAgICAgICdwYWdlWScsXG4gICAgICAgIHNpemU6ICAgICAgICAgICAnaGVpZ2h0JyxcbiAgICAgICAgb3V0c2lkZTogICAgICAgICdyaWdodCcsXG4gICAgICAgIGluc2lkZTogICAgICAgICAnbGVmdCcsXG4gICAgICAgIGxlYWRpbmc6ICAgICAgICAndG9wJyxcbiAgICAgICAgdHJhaWxpbmc6ICAgICAgICdib3R0b20nLFxuICAgICAgICBtYXJnaW5MZWFkaW5nOiAgJ21hcmdpblRvcCcsXG4gICAgICAgIG1hcmdpblRyYWlsaW5nOiAnbWFyZ2luQm90dG9tJyxcbiAgICAgICAgdGhpY2tuZXNzOiAgICAgICd3aWR0aCcsXG4gICAgICAgIGRlbHRhOiAgICAgICAgICAnZGVsdGFZJ1xuICAgIH0sXG4gICAgaG9yaXpvbnRhbDoge1xuICAgICAgICBjb29yZGluYXRlOiAgICAgJ2NsaWVudFgnLFxuICAgICAgICBheGlzOiAgICAgICAgICAgJ3BhZ2VYJyxcbiAgICAgICAgc2l6ZTogICAgICAgICAgICd3aWR0aCcsXG4gICAgICAgIG91dHNpZGU6ICAgICAgICAnYm90dG9tJyxcbiAgICAgICAgaW5zaWRlOiAgICAgICAgICd0b3AnLFxuICAgICAgICBsZWFkaW5nOiAgICAgICAgJ2xlZnQnLFxuICAgICAgICB0cmFpbGluZzogICAgICAgJ3JpZ2h0JyxcbiAgICAgICAgbWFyZ2luTGVhZGluZzogICdtYXJnaW5MZWZ0JyxcbiAgICAgICAgbWFyZ2luVHJhaWxpbmc6ICdtYXJnaW5SaWdodCcsXG4gICAgICAgIHRoaWNrbmVzczogICAgICAnaGVpZ2h0JyxcbiAgICAgICAgZGVsdGE6ICAgICAgICAgICdkZWx0YVgnXG4gICAgfVxufTtcblxudmFyIGF4aXMgPSB7XG4gICAgdG9wOiAgICAndmVydGljYWwnLFxuICAgIGJvdHRvbTogJ3ZlcnRpY2FsJyxcbiAgICBoZWlnaHQ6ICd2ZXJ0aWNhbCcsXG4gICAgbGVmdDogICAnaG9yaXpvbnRhbCcsXG4gICAgcmlnaHQ6ICAnaG9yaXpvbnRhbCcsXG4gICAgd2lkdGg6ICAnaG9yaXpvbnRhbCdcbn07XG5cbnZhciBjc3NGaW5CYXJzOyAvLyBkZWZpbml0aW9uIGluc2VydGVkIGJ5IGd1bHBmaWxlIGJldHdlZW4gZm9sbG93aW5nIGNvbW1lbnRzXG4vKiBpbmplY3Q6Y3NzICovXG5jc3NGaW5CYXJzID0gJ2Rpdi5maW5iYXItaG9yaXpvbnRhbCxkaXYuZmluYmFyLXZlcnRpY2Fse21hcmdpbjozcHh9ZGl2LmZpbmJhci1ob3Jpem9udGFsPi50aHVtYixkaXYuZmluYmFyLXZlcnRpY2FsPi50aHVtYntiYWNrZ3JvdW5kLWNvbG9yOiNkM2QzZDM7LXdlYmtpdC1ib3gtc2hhZG93OjAgMCAxcHggIzAwMDstbW96LWJveC1zaGFkb3c6MCAwIDFweCAjMDAwO2JveC1zaGFkb3c6MCAwIDFweCAjMDAwO2JvcmRlci1yYWRpdXM6NHB4O21hcmdpbjoycHg7b3BhY2l0eTouNDt0cmFuc2l0aW9uOm9wYWNpdHkgLjVzfWRpdi5maW5iYXItaG9yaXpvbnRhbD4udGh1bWIuaG92ZXIsZGl2LmZpbmJhci12ZXJ0aWNhbD4udGh1bWIuaG92ZXJ7b3BhY2l0eToxO3RyYW5zaXRpb246b3BhY2l0eSAuNXN9ZGl2LmZpbmJhci12ZXJ0aWNhbHt0b3A6MDtib3R0b206MDtyaWdodDowO3dpZHRoOjExcHh9ZGl2LmZpbmJhci12ZXJ0aWNhbD4udGh1bWJ7dG9wOjA7cmlnaHQ6MDt3aWR0aDo3cHh9ZGl2LmZpbmJhci1ob3Jpem9udGFse2xlZnQ6MDtyaWdodDowO2JvdHRvbTowO2hlaWdodDoxMXB4fWRpdi5maW5iYXItaG9yaXpvbnRhbD4udGh1bWJ7bGVmdDowO2JvdHRvbTowO2hlaWdodDo3cHh9Jztcbi8qIGVuZGluamVjdCAqL1xuXG5mdW5jdGlvbiBlcnJvcihtc2cpIHtcbiAgICB0aHJvdyAnZmluYmFyczogJyArIG1zZztcbn1cblxuLy8gSW50ZXJmYWNlXG5tb2R1bGUuZXhwb3J0cyA9IEZpbkJhcjtcbiJdfQ==
|