18349 lines
1.4 MiB
Plaintext
Executable File
18349 lines
1.4 MiB
Plaintext
Executable File
(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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.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){
|
|
function corslite(url, callback, cors) {
|
|
var sent = false;
|
|
|
|
if (typeof window.XMLHttpRequest === 'undefined') {
|
|
return callback(Error('Browser not supported'));
|
|
}
|
|
|
|
if (typeof cors === 'undefined') {
|
|
var m = url.match(/^\s*https?:\/\/[^\/]*/);
|
|
cors = m && (m[0] !== location.protocol + '//' + location.domain +
|
|
(location.port ? ':' + location.port : ''));
|
|
}
|
|
|
|
var x = new window.XMLHttpRequest();
|
|
|
|
function isSuccessful(status) {
|
|
return status >= 200 && status < 300 || status === 304;
|
|
}
|
|
|
|
if (cors && !('withCredentials' in x)) {
|
|
// IE8-9
|
|
x = new window.XDomainRequest();
|
|
|
|
// Ensure callback is never called synchronously, i.e., before
|
|
// x.send() returns (this has been observed in the wild).
|
|
// See https://github.com/mapbox/mapbox.js/issues/472
|
|
var original = callback;
|
|
callback = function() {
|
|
if (sent) {
|
|
original.apply(this, arguments);
|
|
} else {
|
|
var that = this, args = arguments;
|
|
setTimeout(function() {
|
|
original.apply(that, args);
|
|
}, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
function loaded() {
|
|
if (
|
|
// XDomainRequest
|
|
x.status === undefined ||
|
|
// modern browsers
|
|
isSuccessful(x.status)) callback.call(x, null, x);
|
|
else callback.call(x, x, null);
|
|
}
|
|
|
|
// Both `onreadystatechange` and `onload` can fire. `onreadystatechange`
|
|
// has [been supported for longer](http://stackoverflow.com/a/9181508/229001).
|
|
if ('onload' in x) {
|
|
x.onload = loaded;
|
|
} else {
|
|
x.onreadystatechange = function readystate() {
|
|
if (x.readyState === 4) {
|
|
loaded();
|
|
}
|
|
};
|
|
}
|
|
|
|
// Call the callback with the XMLHttpRequest object as an error and prevent
|
|
// it from ever being called again by reassigning it to `noop`
|
|
x.onerror = function error(evt) {
|
|
// XDomainRequest provides no evt parameter
|
|
callback.call(this, evt || true, null);
|
|
callback = function() { };
|
|
};
|
|
|
|
// IE9 must have onprogress be set to a unique function.
|
|
x.onprogress = function() { };
|
|
|
|
x.ontimeout = function(evt) {
|
|
callback.call(this, evt, null);
|
|
callback = function() { };
|
|
};
|
|
|
|
x.onabort = function(evt) {
|
|
callback.call(this, evt, null);
|
|
callback = function() { };
|
|
};
|
|
|
|
// GET is the only supported HTTP Verb by XDomainRequest and is the
|
|
// only one supported here.
|
|
x.open('GET', url, true);
|
|
|
|
// Send the request. Sending data is not supported.
|
|
x.send(null);
|
|
sent = true;
|
|
|
|
return x;
|
|
}
|
|
|
|
if (typeof module !== 'undefined') module.exports = corslite;
|
|
|
|
},{}],2:[function(require,module,exports){
|
|
module.exports = Array.isArray || function (arr) {
|
|
return Object.prototype.toString.call(arr) == '[object Array]';
|
|
};
|
|
|
|
},{}],3:[function(require,module,exports){
|
|
/*
|
|
Leaflet 1.0.2, a JS library for interactive maps. http://leafletjs.com
|
|
(c) 2010-2016 Vladimir Agafonkin, (c) 2010-2011 CloudMade
|
|
*/
|
|
(function (window, document, undefined) {
|
|
var L = {
|
|
version: "1.0.2"
|
|
};
|
|
|
|
function expose() {
|
|
var oldL = window.L;
|
|
|
|
L.noConflict = function () {
|
|
window.L = oldL;
|
|
return this;
|
|
};
|
|
|
|
window.L = L;
|
|
}
|
|
|
|
// define Leaflet for Node module pattern loaders, including Browserify
|
|
if (typeof module === 'object' && typeof module.exports === 'object') {
|
|
module.exports = L;
|
|
|
|
// define Leaflet as an AMD module
|
|
} else if (typeof define === 'function' && define.amd) {
|
|
define(L);
|
|
}
|
|
|
|
// define Leaflet as a global L variable, saving the original L to restore later if needed
|
|
if (typeof window !== 'undefined') {
|
|
expose();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* @namespace Util
|
|
*
|
|
* Various utility functions, used by Leaflet internally.
|
|
*/
|
|
|
|
L.Util = {
|
|
|
|
// @function extend(dest: Object, src?: Object): Object
|
|
// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
|
|
extend: function (dest) {
|
|
var i, j, len, src;
|
|
|
|
for (j = 1, len = arguments.length; j < len; j++) {
|
|
src = arguments[j];
|
|
for (i in src) {
|
|
dest[i] = src[i];
|
|
}
|
|
}
|
|
return dest;
|
|
},
|
|
|
|
// @function create(proto: Object, properties?: Object): Object
|
|
// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
|
|
create: Object.create || (function () {
|
|
function F() {}
|
|
return function (proto) {
|
|
F.prototype = proto;
|
|
return new F();
|
|
};
|
|
})(),
|
|
|
|
// @function bind(fn: Function, …): Function
|
|
// Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
|
|
// Has a `L.bind()` shortcut.
|
|
bind: function (fn, obj) {
|
|
var slice = Array.prototype.slice;
|
|
|
|
if (fn.bind) {
|
|
return fn.bind.apply(fn, slice.call(arguments, 1));
|
|
}
|
|
|
|
var args = slice.call(arguments, 2);
|
|
|
|
return function () {
|
|
return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
|
|
};
|
|
},
|
|
|
|
// @function stamp(obj: Object): Number
|
|
// Returns the unique ID of an object, assiging it one if it doesn't have it.
|
|
stamp: function (obj) {
|
|
/*eslint-disable */
|
|
obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId;
|
|
return obj._leaflet_id;
|
|
/*eslint-enable */
|
|
},
|
|
|
|
// @property lastId: Number
|
|
// Last unique ID used by [`stamp()`](#util-stamp)
|
|
lastId: 0,
|
|
|
|
// @function throttle(fn: Function, time: Number, context: Object): Function
|
|
// Returns a function which executes function `fn` with the given scope `context`
|
|
// (so that the `this` keyword refers to `context` inside `fn`'s code). The function
|
|
// `fn` will be called no more than one time per given amount of `time`. The arguments
|
|
// received by the bound function will be any arguments passed when binding the
|
|
// function, followed by any arguments passed when invoking the bound function.
|
|
// Has an `L.bind` shortcut.
|
|
throttle: function (fn, time, context) {
|
|
var lock, args, wrapperFn, later;
|
|
|
|
later = function () {
|
|
// reset lock and call if queued
|
|
lock = false;
|
|
if (args) {
|
|
wrapperFn.apply(context, args);
|
|
args = false;
|
|
}
|
|
};
|
|
|
|
wrapperFn = function () {
|
|
if (lock) {
|
|
// called too soon, queue to call later
|
|
args = arguments;
|
|
|
|
} else {
|
|
// call and lock until later
|
|
fn.apply(context, arguments);
|
|
setTimeout(later, time);
|
|
lock = true;
|
|
}
|
|
};
|
|
|
|
return wrapperFn;
|
|
},
|
|
|
|
// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
|
|
// Returns the number `num` modulo `range` in such a way so it lies within
|
|
// `range[0]` and `range[1]`. The returned value will be always smaller than
|
|
// `range[1]` unless `includeMax` is set to `true`.
|
|
wrapNum: function (x, range, includeMax) {
|
|
var max = range[1],
|
|
min = range[0],
|
|
d = max - min;
|
|
return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
|
|
},
|
|
|
|
// @function falseFn(): Function
|
|
// Returns a function which always returns `false`.
|
|
falseFn: function () { return false; },
|
|
|
|
// @function formatNum(num: Number, digits?: Number): Number
|
|
// Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default.
|
|
formatNum: function (num, digits) {
|
|
var pow = Math.pow(10, digits || 5);
|
|
return Math.round(num * pow) / pow;
|
|
},
|
|
|
|
// @function trim(str: String): String
|
|
// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
|
|
trim: function (str) {
|
|
return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
|
|
},
|
|
|
|
// @function splitWords(str: String): String[]
|
|
// Trims and splits the string on whitespace and returns the array of parts.
|
|
splitWords: function (str) {
|
|
return L.Util.trim(str).split(/\s+/);
|
|
},
|
|
|
|
// @function setOptions(obj: Object, options: Object): Object
|
|
// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
|
|
setOptions: function (obj, options) {
|
|
if (!obj.hasOwnProperty('options')) {
|
|
obj.options = obj.options ? L.Util.create(obj.options) : {};
|
|
}
|
|
for (var i in options) {
|
|
obj.options[i] = options[i];
|
|
}
|
|
return obj.options;
|
|
},
|
|
|
|
// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
|
|
// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
|
|
// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
|
|
// be appended at the end. If `uppercase` is `true`, the parameter names will
|
|
// be uppercased (e.g. `'?A=foo&B=bar'`)
|
|
getParamString: function (obj, existingUrl, uppercase) {
|
|
var params = [];
|
|
for (var i in obj) {
|
|
params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
|
|
}
|
|
return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
|
|
},
|
|
|
|
// @function template(str: String, data: Object): String
|
|
// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
|
|
// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
|
|
// `('Hello foo, bar')`. You can also specify functions instead of strings for
|
|
// data values — they will be evaluated passing `data` as an argument.
|
|
template: function (str, data) {
|
|
return str.replace(L.Util.templateRe, function (str, key) {
|
|
var value = data[key];
|
|
|
|
if (value === undefined) {
|
|
throw new Error('No value provided for variable ' + str);
|
|
|
|
} else if (typeof value === 'function') {
|
|
value = value(data);
|
|
}
|
|
return value;
|
|
});
|
|
},
|
|
|
|
templateRe: /\{ *([\w_\-]+) *\}/g,
|
|
|
|
// @function isArray(obj): Boolean
|
|
// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
|
|
isArray: Array.isArray || function (obj) {
|
|
return (Object.prototype.toString.call(obj) === '[object Array]');
|
|
},
|
|
|
|
// @function indexOf(array: Array, el: Object): Number
|
|
// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
|
|
indexOf: function (array, el) {
|
|
for (var i = 0; i < array.length; i++) {
|
|
if (array[i] === el) { return i; }
|
|
}
|
|
return -1;
|
|
},
|
|
|
|
// @property emptyImageUrl: String
|
|
// Data URI string containing a base64-encoded empty GIF image.
|
|
// Used as a hack to free memory from unused images on WebKit-powered
|
|
// mobile devices (by setting image `src` to this string).
|
|
emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
|
|
};
|
|
|
|
(function () {
|
|
// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
|
|
|
function getPrefixed(name) {
|
|
return window['webkit' + name] || window['moz' + name] || window['ms' + name];
|
|
}
|
|
|
|
var lastTime = 0;
|
|
|
|
// fallback for IE 7-8
|
|
function timeoutDefer(fn) {
|
|
var time = +new Date(),
|
|
timeToCall = Math.max(0, 16 - (time - lastTime));
|
|
|
|
lastTime = time + timeToCall;
|
|
return window.setTimeout(fn, timeToCall);
|
|
}
|
|
|
|
var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer,
|
|
cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
|
|
getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
|
|
|
|
|
|
// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
|
|
// Schedules `fn` to be executed when the browser repaints. `fn` is bound to
|
|
// `context` if given. When `immediate` is set, `fn` is called immediately if
|
|
// the browser doesn't have native support for
|
|
// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
|
|
// otherwise it's delayed. Returns a request ID that can be used to cancel the request.
|
|
L.Util.requestAnimFrame = function (fn, context, immediate) {
|
|
if (immediate && requestFn === timeoutDefer) {
|
|
fn.call(context);
|
|
} else {
|
|
return requestFn.call(window, L.bind(fn, context));
|
|
}
|
|
};
|
|
|
|
// @function cancelAnimFrame(id: Number): undefined
|
|
// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
|
|
L.Util.cancelAnimFrame = function (id) {
|
|
if (id) {
|
|
cancelFn.call(window, id);
|
|
}
|
|
};
|
|
})();
|
|
|
|
// shortcuts for most used utility functions
|
|
L.extend = L.Util.extend;
|
|
L.bind = L.Util.bind;
|
|
L.stamp = L.Util.stamp;
|
|
L.setOptions = L.Util.setOptions;
|
|
|
|
|
|
|
|
|
|
// @class Class
|
|
// @aka L.Class
|
|
|
|
// @section
|
|
// @uninheritable
|
|
|
|
// Thanks to John Resig and Dean Edwards for inspiration!
|
|
|
|
L.Class = function () {};
|
|
|
|
L.Class.extend = function (props) {
|
|
|
|
// @function extend(props: Object): Function
|
|
// [Extends the current class](#class-inheritance) given the properties to be included.
|
|
// Returns a Javascript function that is a class constructor (to be called with `new`).
|
|
var NewClass = function () {
|
|
|
|
// call the constructor
|
|
if (this.initialize) {
|
|
this.initialize.apply(this, arguments);
|
|
}
|
|
|
|
// call all constructor hooks
|
|
this.callInitHooks();
|
|
};
|
|
|
|
var parentProto = NewClass.__super__ = this.prototype;
|
|
|
|
var proto = L.Util.create(parentProto);
|
|
proto.constructor = NewClass;
|
|
|
|
NewClass.prototype = proto;
|
|
|
|
// inherit parent's statics
|
|
for (var i in this) {
|
|
if (this.hasOwnProperty(i) && i !== 'prototype') {
|
|
NewClass[i] = this[i];
|
|
}
|
|
}
|
|
|
|
// mix static properties into the class
|
|
if (props.statics) {
|
|
L.extend(NewClass, props.statics);
|
|
delete props.statics;
|
|
}
|
|
|
|
// mix includes into the prototype
|
|
if (props.includes) {
|
|
L.Util.extend.apply(null, [proto].concat(props.includes));
|
|
delete props.includes;
|
|
}
|
|
|
|
// merge options
|
|
if (proto.options) {
|
|
props.options = L.Util.extend(L.Util.create(proto.options), props.options);
|
|
}
|
|
|
|
// mix given properties into the prototype
|
|
L.extend(proto, props);
|
|
|
|
proto._initHooks = [];
|
|
|
|
// add method for calling all hooks
|
|
proto.callInitHooks = function () {
|
|
|
|
if (this._initHooksCalled) { return; }
|
|
|
|
if (parentProto.callInitHooks) {
|
|
parentProto.callInitHooks.call(this);
|
|
}
|
|
|
|
this._initHooksCalled = true;
|
|
|
|
for (var i = 0, len = proto._initHooks.length; i < len; i++) {
|
|
proto._initHooks[i].call(this);
|
|
}
|
|
};
|
|
|
|
return NewClass;
|
|
};
|
|
|
|
|
|
// @function include(properties: Object): this
|
|
// [Includes a mixin](#class-includes) into the current class.
|
|
L.Class.include = function (props) {
|
|
L.extend(this.prototype, props);
|
|
return this;
|
|
};
|
|
|
|
// @function mergeOptions(options: Object): this
|
|
// [Merges `options`](#class-options) into the defaults of the class.
|
|
L.Class.mergeOptions = function (options) {
|
|
L.extend(this.prototype.options, options);
|
|
return this;
|
|
};
|
|
|
|
// @function addInitHook(fn: Function): this
|
|
// Adds a [constructor hook](#class-constructor-hooks) to the class.
|
|
L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
|
|
var init = typeof fn === 'function' ? fn : function () {
|
|
this[fn].apply(this, args);
|
|
};
|
|
|
|
this.prototype._initHooks = this.prototype._initHooks || [];
|
|
this.prototype._initHooks.push(init);
|
|
return this;
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Evented
|
|
* @aka L.Evented
|
|
* @inherits Class
|
|
*
|
|
* A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* map.on('click', function(e) {
|
|
* alert(e.latlng);
|
|
* } );
|
|
* ```
|
|
*
|
|
* Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
|
|
*
|
|
* ```js
|
|
* function onClick(e) { ... }
|
|
*
|
|
* map.on('click', onClick);
|
|
* map.off('click', onClick);
|
|
* ```
|
|
*/
|
|
|
|
|
|
L.Evented = L.Class.extend({
|
|
|
|
/* @method on(type: String, fn: Function, context?: Object): this
|
|
* Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
|
|
*
|
|
* @alternative
|
|
* @method on(eventMap: Object): this
|
|
* Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
|
|
*/
|
|
on: function (types, fn, context) {
|
|
|
|
// types can be a map of types/handlers
|
|
if (typeof types === 'object') {
|
|
for (var type in types) {
|
|
// we don't process space-separated events here for performance;
|
|
// it's a hot path since Layer uses the on(obj) syntax
|
|
this._on(type, types[type], fn);
|
|
}
|
|
|
|
} else {
|
|
// types can be a string of space-separated words
|
|
types = L.Util.splitWords(types);
|
|
|
|
for (var i = 0, len = types.length; i < len; i++) {
|
|
this._on(types[i], fn, context);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
/* @method off(type: String, fn?: Function, context?: Object): this
|
|
* Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
|
|
*
|
|
* @alternative
|
|
* @method off(eventMap: Object): this
|
|
* Removes a set of type/listener pairs.
|
|
*
|
|
* @alternative
|
|
* @method off: this
|
|
* Removes all listeners to all events on the object.
|
|
*/
|
|
off: function (types, fn, context) {
|
|
|
|
if (!types) {
|
|
// clear all listeners if called without arguments
|
|
delete this._events;
|
|
|
|
} else if (typeof types === 'object') {
|
|
for (var type in types) {
|
|
this._off(type, types[type], fn);
|
|
}
|
|
|
|
} else {
|
|
types = L.Util.splitWords(types);
|
|
|
|
for (var i = 0, len = types.length; i < len; i++) {
|
|
this._off(types[i], fn, context);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// attach listener (without syntactic sugar now)
|
|
_on: function (type, fn, context) {
|
|
this._events = this._events || {};
|
|
|
|
/* get/init listeners for type */
|
|
var typeListeners = this._events[type];
|
|
if (!typeListeners) {
|
|
typeListeners = [];
|
|
this._events[type] = typeListeners;
|
|
}
|
|
|
|
if (context === this) {
|
|
// Less memory footprint.
|
|
context = undefined;
|
|
}
|
|
var newListener = {fn: fn, ctx: context},
|
|
listeners = typeListeners;
|
|
|
|
// check if fn already there
|
|
for (var i = 0, len = listeners.length; i < len; i++) {
|
|
if (listeners[i].fn === fn && listeners[i].ctx === context) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
listeners.push(newListener);
|
|
typeListeners.count++;
|
|
},
|
|
|
|
_off: function (type, fn, context) {
|
|
var listeners,
|
|
i,
|
|
len;
|
|
|
|
if (!this._events) { return; }
|
|
|
|
listeners = this._events[type];
|
|
|
|
if (!listeners) {
|
|
return;
|
|
}
|
|
|
|
if (!fn) {
|
|
// Set all removed listeners to noop so they are not called if remove happens in fire
|
|
for (i = 0, len = listeners.length; i < len; i++) {
|
|
listeners[i].fn = L.Util.falseFn;
|
|
}
|
|
// clear all listeners for a type if function isn't specified
|
|
delete this._events[type];
|
|
return;
|
|
}
|
|
|
|
if (context === this) {
|
|
context = undefined;
|
|
}
|
|
|
|
if (listeners) {
|
|
|
|
// find fn and remove it
|
|
for (i = 0, len = listeners.length; i < len; i++) {
|
|
var l = listeners[i];
|
|
if (l.ctx !== context) { continue; }
|
|
if (l.fn === fn) {
|
|
|
|
// set the removed listener to noop so that's not called if remove happens in fire
|
|
l.fn = L.Util.falseFn;
|
|
|
|
if (this._firingCount) {
|
|
/* copy array in case events are being fired */
|
|
this._events[type] = listeners = listeners.slice();
|
|
}
|
|
listeners.splice(i, 1);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// @method fire(type: String, data?: Object, propagate?: Boolean): this
|
|
// Fires an event of the specified type. You can optionally provide an data
|
|
// object — the first argument of the listener function will contain its
|
|
// properties. The event can optionally be propagated to event parents.
|
|
fire: function (type, data, propagate) {
|
|
if (!this.listens(type, propagate)) { return this; }
|
|
|
|
var event = L.Util.extend({}, data, {type: type, target: this});
|
|
|
|
if (this._events) {
|
|
var listeners = this._events[type];
|
|
|
|
if (listeners) {
|
|
this._firingCount = (this._firingCount + 1) || 1;
|
|
for (var i = 0, len = listeners.length; i < len; i++) {
|
|
var l = listeners[i];
|
|
l.fn.call(l.ctx || this, event);
|
|
}
|
|
|
|
this._firingCount--;
|
|
}
|
|
}
|
|
|
|
if (propagate) {
|
|
// propagate the event to parents (set with addEventParent)
|
|
this._propagateEvent(event);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method listens(type: String): Boolean
|
|
// Returns `true` if a particular event type has any listeners attached to it.
|
|
listens: function (type, propagate) {
|
|
var listeners = this._events && this._events[type];
|
|
if (listeners && listeners.length) { return true; }
|
|
|
|
if (propagate) {
|
|
// also check parents for listeners if event propagates
|
|
for (var id in this._eventParents) {
|
|
if (this._eventParents[id].listens(type, propagate)) { return true; }
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// @method once(…): this
|
|
// Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
|
|
once: function (types, fn, context) {
|
|
|
|
if (typeof types === 'object') {
|
|
for (var type in types) {
|
|
this.once(type, types[type], fn);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
var handler = L.bind(function () {
|
|
this
|
|
.off(types, fn, context)
|
|
.off(types, handler, context);
|
|
}, this);
|
|
|
|
// add a listener that's executed once and removed after that
|
|
return this
|
|
.on(types, fn, context)
|
|
.on(types, handler, context);
|
|
},
|
|
|
|
// @method addEventParent(obj: Evented): this
|
|
// Adds an event parent - an `Evented` that will receive propagated events
|
|
addEventParent: function (obj) {
|
|
this._eventParents = this._eventParents || {};
|
|
this._eventParents[L.stamp(obj)] = obj;
|
|
return this;
|
|
},
|
|
|
|
// @method removeEventParent(obj: Evented): this
|
|
// Removes an event parent, so it will stop receiving propagated events
|
|
removeEventParent: function (obj) {
|
|
if (this._eventParents) {
|
|
delete this._eventParents[L.stamp(obj)];
|
|
}
|
|
return this;
|
|
},
|
|
|
|
_propagateEvent: function (e) {
|
|
for (var id in this._eventParents) {
|
|
this._eventParents[id].fire(e.type, L.extend({layer: e.target}, e), true);
|
|
}
|
|
}
|
|
});
|
|
|
|
var proto = L.Evented.prototype;
|
|
|
|
// aliases; we should ditch those eventually
|
|
|
|
// @method addEventListener(…): this
|
|
// Alias to [`on(…)`](#evented-on)
|
|
proto.addEventListener = proto.on;
|
|
|
|
// @method removeEventListener(…): this
|
|
// Alias to [`off(…)`](#evented-off)
|
|
|
|
// @method clearAllEventListeners(…): this
|
|
// Alias to [`off()`](#evented-off)
|
|
proto.removeEventListener = proto.clearAllEventListeners = proto.off;
|
|
|
|
// @method addOneTimeEventListener(…): this
|
|
// Alias to [`once(…)`](#evented-once)
|
|
proto.addOneTimeEventListener = proto.once;
|
|
|
|
// @method fireEvent(…): this
|
|
// Alias to [`fire(…)`](#evented-fire)
|
|
proto.fireEvent = proto.fire;
|
|
|
|
// @method hasEventListeners(…): Boolean
|
|
// Alias to [`listens(…)`](#evented-listens)
|
|
proto.hasEventListeners = proto.listens;
|
|
|
|
L.Mixin = {Events: proto};
|
|
|
|
|
|
|
|
/*
|
|
* @namespace Browser
|
|
* @aka L.Browser
|
|
*
|
|
* A namespace with static properties for browser/feature detection used by Leaflet internally.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* if (L.Browser.ielt9) {
|
|
* alert('Upgrade your browser, dude!');
|
|
* }
|
|
* ```
|
|
*/
|
|
|
|
(function () {
|
|
|
|
var ua = navigator.userAgent.toLowerCase(),
|
|
doc = document.documentElement,
|
|
|
|
ie = 'ActiveXObject' in window,
|
|
|
|
webkit = ua.indexOf('webkit') !== -1,
|
|
phantomjs = ua.indexOf('phantom') !== -1,
|
|
android23 = ua.search('android [23]') !== -1,
|
|
chrome = ua.indexOf('chrome') !== -1,
|
|
gecko = ua.indexOf('gecko') !== -1 && !webkit && !window.opera && !ie,
|
|
|
|
win = navigator.platform.indexOf('Win') === 0,
|
|
|
|
mobile = typeof orientation !== 'undefined' || ua.indexOf('mobile') !== -1,
|
|
msPointer = !window.PointerEvent && window.MSPointerEvent,
|
|
pointer = window.PointerEvent || msPointer,
|
|
|
|
ie3d = ie && ('transition' in doc.style),
|
|
webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
|
|
gecko3d = 'MozPerspective' in doc.style,
|
|
opera12 = 'OTransition' in doc.style;
|
|
|
|
|
|
var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
|
|
(window.DocumentTouch && document instanceof window.DocumentTouch));
|
|
|
|
L.Browser = {
|
|
|
|
// @property ie: Boolean
|
|
// `true` for all Internet Explorer versions (not Edge).
|
|
ie: ie,
|
|
|
|
// @property ielt9: Boolean
|
|
// `true` for Internet Explorer versions less than 9.
|
|
ielt9: ie && !document.addEventListener,
|
|
|
|
// @property edge: Boolean
|
|
// `true` for the Edge web browser.
|
|
edge: 'msLaunchUri' in navigator && !('documentMode' in document),
|
|
|
|
// @property webkit: Boolean
|
|
// `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
|
|
webkit: webkit,
|
|
|
|
// @property gecko: Boolean
|
|
// `true` for gecko-based browsers like Firefox.
|
|
gecko: gecko,
|
|
|
|
// @property android: Boolean
|
|
// `true` for any browser running on an Android platform.
|
|
android: ua.indexOf('android') !== -1,
|
|
|
|
// @property android23: Boolean
|
|
// `true` for browsers running on Android 2 or Android 3.
|
|
android23: android23,
|
|
|
|
// @property chrome: Boolean
|
|
// `true` for the Chrome browser.
|
|
chrome: chrome,
|
|
|
|
// @property safari: Boolean
|
|
// `true` for the Safari browser.
|
|
safari: !chrome && ua.indexOf('safari') !== -1,
|
|
|
|
|
|
// @property win: Boolean
|
|
// `true` when the browser is running in a Windows platform
|
|
win: win,
|
|
|
|
|
|
// @property ie3d: Boolean
|
|
// `true` for all Internet Explorer versions supporting CSS transforms.
|
|
ie3d: ie3d,
|
|
|
|
// @property webkit3d: Boolean
|
|
// `true` for webkit-based browsers supporting CSS transforms.
|
|
webkit3d: webkit3d,
|
|
|
|
// @property gecko3d: Boolean
|
|
// `true` for gecko-based browsers supporting CSS transforms.
|
|
gecko3d: gecko3d,
|
|
|
|
// @property opera12: Boolean
|
|
// `true` for the Opera browser supporting CSS transforms (version 12 or later).
|
|
opera12: opera12,
|
|
|
|
// @property any3d: Boolean
|
|
// `true` for all browsers supporting CSS transforms.
|
|
any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantomjs,
|
|
|
|
|
|
// @property mobile: Boolean
|
|
// `true` for all browsers running in a mobile device.
|
|
mobile: mobile,
|
|
|
|
// @property mobileWebkit: Boolean
|
|
// `true` for all webkit-based browsers in a mobile device.
|
|
mobileWebkit: mobile && webkit,
|
|
|
|
// @property mobileWebkit3d: Boolean
|
|
// `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
|
|
mobileWebkit3d: mobile && webkit3d,
|
|
|
|
// @property mobileOpera: Boolean
|
|
// `true` for the Opera browser in a mobile device.
|
|
mobileOpera: mobile && window.opera,
|
|
|
|
// @property mobileGecko: Boolean
|
|
// `true` for gecko-based browsers running in a mobile device.
|
|
mobileGecko: mobile && gecko,
|
|
|
|
|
|
// @property touch: Boolean
|
|
// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
|
|
touch: !!touch,
|
|
|
|
// @property msPointer: Boolean
|
|
// `true` for browsers implementing the Microsoft touch events model (notably IE10).
|
|
msPointer: !!msPointer,
|
|
|
|
// @property pointer: Boolean
|
|
// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
|
|
pointer: !!pointer,
|
|
|
|
|
|
// @property retina: Boolean
|
|
// `true` for browsers on a high-resolution "retina" screen.
|
|
retina: (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1
|
|
};
|
|
|
|
}());
|
|
|
|
|
|
|
|
/*
|
|
* @class Point
|
|
* @aka L.Point
|
|
*
|
|
* Represents a point with `x` and `y` coordinates in pixels.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* var point = L.point(200, 300);
|
|
* ```
|
|
*
|
|
* All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
|
|
*
|
|
* ```js
|
|
* map.panBy([200, 300]);
|
|
* map.panBy(L.point(200, 300));
|
|
* ```
|
|
*/
|
|
|
|
L.Point = function (x, y, round) {
|
|
// @property x: Number; The `x` coordinate of the point
|
|
this.x = (round ? Math.round(x) : x);
|
|
// @property y: Number; The `y` coordinate of the point
|
|
this.y = (round ? Math.round(y) : y);
|
|
};
|
|
|
|
L.Point.prototype = {
|
|
|
|
// @method clone(): Point
|
|
// Returns a copy of the current point.
|
|
clone: function () {
|
|
return new L.Point(this.x, this.y);
|
|
},
|
|
|
|
// @method add(otherPoint: Point): Point
|
|
// Returns the result of addition of the current and the given points.
|
|
add: function (point) {
|
|
// non-destructive, returns a new point
|
|
return this.clone()._add(L.point(point));
|
|
},
|
|
|
|
_add: function (point) {
|
|
// destructive, used directly for performance in situations where it's safe to modify existing point
|
|
this.x += point.x;
|
|
this.y += point.y;
|
|
return this;
|
|
},
|
|
|
|
// @method subtract(otherPoint: Point): Point
|
|
// Returns the result of subtraction of the given point from the current.
|
|
subtract: function (point) {
|
|
return this.clone()._subtract(L.point(point));
|
|
},
|
|
|
|
_subtract: function (point) {
|
|
this.x -= point.x;
|
|
this.y -= point.y;
|
|
return this;
|
|
},
|
|
|
|
// @method divideBy(num: Number): Point
|
|
// Returns the result of division of the current point by the given number.
|
|
divideBy: function (num) {
|
|
return this.clone()._divideBy(num);
|
|
},
|
|
|
|
_divideBy: function (num) {
|
|
this.x /= num;
|
|
this.y /= num;
|
|
return this;
|
|
},
|
|
|
|
// @method multiplyBy(num: Number): Point
|
|
// Returns the result of multiplication of the current point by the given number.
|
|
multiplyBy: function (num) {
|
|
return this.clone()._multiplyBy(num);
|
|
},
|
|
|
|
_multiplyBy: function (num) {
|
|
this.x *= num;
|
|
this.y *= num;
|
|
return this;
|
|
},
|
|
|
|
// @method scaleBy(scale: Point): Point
|
|
// Multiply each coordinate of the current point by each coordinate of
|
|
// `scale`. In linear algebra terms, multiply the point by the
|
|
// [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
|
|
// defined by `scale`.
|
|
scaleBy: function (point) {
|
|
return new L.Point(this.x * point.x, this.y * point.y);
|
|
},
|
|
|
|
// @method unscaleBy(scale: Point): Point
|
|
// Inverse of `scaleBy`. Divide each coordinate of the current point by
|
|
// each coordinate of `scale`.
|
|
unscaleBy: function (point) {
|
|
return new L.Point(this.x / point.x, this.y / point.y);
|
|
},
|
|
|
|
// @method round(): Point
|
|
// Returns a copy of the current point with rounded coordinates.
|
|
round: function () {
|
|
return this.clone()._round();
|
|
},
|
|
|
|
_round: function () {
|
|
this.x = Math.round(this.x);
|
|
this.y = Math.round(this.y);
|
|
return this;
|
|
},
|
|
|
|
// @method floor(): Point
|
|
// Returns a copy of the current point with floored coordinates (rounded down).
|
|
floor: function () {
|
|
return this.clone()._floor();
|
|
},
|
|
|
|
_floor: function () {
|
|
this.x = Math.floor(this.x);
|
|
this.y = Math.floor(this.y);
|
|
return this;
|
|
},
|
|
|
|
// @method ceil(): Point
|
|
// Returns a copy of the current point with ceiled coordinates (rounded up).
|
|
ceil: function () {
|
|
return this.clone()._ceil();
|
|
},
|
|
|
|
_ceil: function () {
|
|
this.x = Math.ceil(this.x);
|
|
this.y = Math.ceil(this.y);
|
|
return this;
|
|
},
|
|
|
|
// @method distanceTo(otherPoint: Point): Number
|
|
// Returns the cartesian distance between the current and the given points.
|
|
distanceTo: function (point) {
|
|
point = L.point(point);
|
|
|
|
var x = point.x - this.x,
|
|
y = point.y - this.y;
|
|
|
|
return Math.sqrt(x * x + y * y);
|
|
},
|
|
|
|
// @method equals(otherPoint: Point): Boolean
|
|
// Returns `true` if the given point has the same coordinates.
|
|
equals: function (point) {
|
|
point = L.point(point);
|
|
|
|
return point.x === this.x &&
|
|
point.y === this.y;
|
|
},
|
|
|
|
// @method contains(otherPoint: Point): Boolean
|
|
// Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
|
|
contains: function (point) {
|
|
point = L.point(point);
|
|
|
|
return Math.abs(point.x) <= Math.abs(this.x) &&
|
|
Math.abs(point.y) <= Math.abs(this.y);
|
|
},
|
|
|
|
// @method toString(): String
|
|
// Returns a string representation of the point for debugging purposes.
|
|
toString: function () {
|
|
return 'Point(' +
|
|
L.Util.formatNum(this.x) + ', ' +
|
|
L.Util.formatNum(this.y) + ')';
|
|
}
|
|
};
|
|
|
|
// @factory L.point(x: Number, y: Number, round?: Boolean)
|
|
// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
|
|
|
|
// @alternative
|
|
// @factory L.point(coords: Number[])
|
|
// Expects an array of the form `[x, y]` instead.
|
|
|
|
// @alternative
|
|
// @factory L.point(coords: Object)
|
|
// Expects a plain object of the form `{x: Number, y: Number}` instead.
|
|
L.point = function (x, y, round) {
|
|
if (x instanceof L.Point) {
|
|
return x;
|
|
}
|
|
if (L.Util.isArray(x)) {
|
|
return new L.Point(x[0], x[1]);
|
|
}
|
|
if (x === undefined || x === null) {
|
|
return x;
|
|
}
|
|
if (typeof x === 'object' && 'x' in x && 'y' in x) {
|
|
return new L.Point(x.x, x.y);
|
|
}
|
|
return new L.Point(x, y, round);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Bounds
|
|
* @aka L.Bounds
|
|
*
|
|
* Represents a rectangular area in pixel coordinates.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* var p1 = L.point(10, 10),
|
|
* p2 = L.point(40, 60),
|
|
* bounds = L.bounds(p1, p2);
|
|
* ```
|
|
*
|
|
* All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
|
|
*
|
|
* ```js
|
|
* otherBounds.intersects([[10, 10], [40, 60]]);
|
|
* ```
|
|
*/
|
|
|
|
L.Bounds = function (a, b) {
|
|
if (!a) { return; }
|
|
|
|
var points = b ? [a, b] : a;
|
|
|
|
for (var i = 0, len = points.length; i < len; i++) {
|
|
this.extend(points[i]);
|
|
}
|
|
};
|
|
|
|
L.Bounds.prototype = {
|
|
// @method extend(point: Point): this
|
|
// Extends the bounds to contain the given point.
|
|
extend: function (point) { // (Point)
|
|
point = L.point(point);
|
|
|
|
// @property min: Point
|
|
// The top left corner of the rectangle.
|
|
// @property max: Point
|
|
// The bottom right corner of the rectangle.
|
|
if (!this.min && !this.max) {
|
|
this.min = point.clone();
|
|
this.max = point.clone();
|
|
} else {
|
|
this.min.x = Math.min(point.x, this.min.x);
|
|
this.max.x = Math.max(point.x, this.max.x);
|
|
this.min.y = Math.min(point.y, this.min.y);
|
|
this.max.y = Math.max(point.y, this.max.y);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method getCenter(round?: Boolean): Point
|
|
// Returns the center point of the bounds.
|
|
getCenter: function (round) {
|
|
return new L.Point(
|
|
(this.min.x + this.max.x) / 2,
|
|
(this.min.y + this.max.y) / 2, round);
|
|
},
|
|
|
|
// @method getBottomLeft(): Point
|
|
// Returns the bottom-left point of the bounds.
|
|
getBottomLeft: function () {
|
|
return new L.Point(this.min.x, this.max.y);
|
|
},
|
|
|
|
// @method getTopRight(): Point
|
|
// Returns the top-right point of the bounds.
|
|
getTopRight: function () { // -> Point
|
|
return new L.Point(this.max.x, this.min.y);
|
|
},
|
|
|
|
// @method getSize(): Point
|
|
// Returns the size of the given bounds
|
|
getSize: function () {
|
|
return this.max.subtract(this.min);
|
|
},
|
|
|
|
// @method contains(otherBounds: Bounds): Boolean
|
|
// Returns `true` if the rectangle contains the given one.
|
|
// @alternative
|
|
// @method contains(point: Point): Boolean
|
|
// Returns `true` if the rectangle contains the given point.
|
|
contains: function (obj) {
|
|
var min, max;
|
|
|
|
if (typeof obj[0] === 'number' || obj instanceof L.Point) {
|
|
obj = L.point(obj);
|
|
} else {
|
|
obj = L.bounds(obj);
|
|
}
|
|
|
|
if (obj instanceof L.Bounds) {
|
|
min = obj.min;
|
|
max = obj.max;
|
|
} else {
|
|
min = max = obj;
|
|
}
|
|
|
|
return (min.x >= this.min.x) &&
|
|
(max.x <= this.max.x) &&
|
|
(min.y >= this.min.y) &&
|
|
(max.y <= this.max.y);
|
|
},
|
|
|
|
// @method intersects(otherBounds: Bounds): Boolean
|
|
// Returns `true` if the rectangle intersects the given bounds. Two bounds
|
|
// intersect if they have at least one point in common.
|
|
intersects: function (bounds) { // (Bounds) -> Boolean
|
|
bounds = L.bounds(bounds);
|
|
|
|
var min = this.min,
|
|
max = this.max,
|
|
min2 = bounds.min,
|
|
max2 = bounds.max,
|
|
xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
|
|
yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
|
|
|
|
return xIntersects && yIntersects;
|
|
},
|
|
|
|
// @method overlaps(otherBounds: Bounds): Boolean
|
|
// Returns `true` if the rectangle overlaps the given bounds. Two bounds
|
|
// overlap if their intersection is an area.
|
|
overlaps: function (bounds) { // (Bounds) -> Boolean
|
|
bounds = L.bounds(bounds);
|
|
|
|
var min = this.min,
|
|
max = this.max,
|
|
min2 = bounds.min,
|
|
max2 = bounds.max,
|
|
xOverlaps = (max2.x > min.x) && (min2.x < max.x),
|
|
yOverlaps = (max2.y > min.y) && (min2.y < max.y);
|
|
|
|
return xOverlaps && yOverlaps;
|
|
},
|
|
|
|
isValid: function () {
|
|
return !!(this.min && this.max);
|
|
}
|
|
};
|
|
|
|
|
|
// @factory L.bounds(topLeft: Point, bottomRight: Point)
|
|
// Creates a Bounds object from two coordinates (usually top-left and bottom-right corners).
|
|
// @alternative
|
|
// @factory L.bounds(points: Point[])
|
|
// Creates a Bounds object from the points it contains
|
|
L.bounds = function (a, b) {
|
|
if (!a || a instanceof L.Bounds) {
|
|
return a;
|
|
}
|
|
return new L.Bounds(a, b);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Transformation
|
|
* @aka L.Transformation
|
|
*
|
|
* Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
|
|
* for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
|
|
* the reverse. Used by Leaflet in its projections code.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* var transformation = new L.Transformation(2, 5, -1, 10),
|
|
* p = L.point(1, 2),
|
|
* p2 = transformation.transform(p), // L.point(7, 8)
|
|
* p3 = transformation.untransform(p2); // L.point(1, 2)
|
|
* ```
|
|
*/
|
|
|
|
|
|
// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
|
|
// Creates a `Transformation` object with the given coefficients.
|
|
L.Transformation = function (a, b, c, d) {
|
|
this._a = a;
|
|
this._b = b;
|
|
this._c = c;
|
|
this._d = d;
|
|
};
|
|
|
|
L.Transformation.prototype = {
|
|
// @method transform(point: Point, scale?: Number): Point
|
|
// Returns a transformed point, optionally multiplied by the given scale.
|
|
// Only accepts actual `L.Point` instances, not arrays.
|
|
transform: function (point, scale) { // (Point, Number) -> Point
|
|
return this._transform(point.clone(), scale);
|
|
},
|
|
|
|
// destructive transform (faster)
|
|
_transform: function (point, scale) {
|
|
scale = scale || 1;
|
|
point.x = scale * (this._a * point.x + this._b);
|
|
point.y = scale * (this._c * point.y + this._d);
|
|
return point;
|
|
},
|
|
|
|
// @method untransform(point: Point, scale?: Number): Point
|
|
// Returns the reverse transformation of the given point, optionally divided
|
|
// by the given scale. Only accepts actual `L.Point` instances, not arrays.
|
|
untransform: function (point, scale) {
|
|
scale = scale || 1;
|
|
return new L.Point(
|
|
(point.x / scale - this._b) / this._a,
|
|
(point.y / scale - this._d) / this._c);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @namespace DomUtil
|
|
*
|
|
* Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
|
|
* tree, used by Leaflet internally.
|
|
*
|
|
* Most functions expecting or returning a `HTMLElement` also work for
|
|
* SVG elements. The only difference is that classes refer to CSS classes
|
|
* in HTML and SVG classes in SVG.
|
|
*/
|
|
|
|
L.DomUtil = {
|
|
|
|
// @function get(id: String|HTMLElement): HTMLElement
|
|
// Returns an element given its DOM id, or returns the element itself
|
|
// if it was passed directly.
|
|
get: function (id) {
|
|
return typeof id === 'string' ? document.getElementById(id) : id;
|
|
},
|
|
|
|
// @function getStyle(el: HTMLElement, styleAttrib: String): String
|
|
// Returns the value for a certain style attribute on an element,
|
|
// including computed values or values set through CSS.
|
|
getStyle: function (el, style) {
|
|
|
|
var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
|
|
|
|
if ((!value || value === 'auto') && document.defaultView) {
|
|
var css = document.defaultView.getComputedStyle(el, null);
|
|
value = css ? css[style] : null;
|
|
}
|
|
|
|
return value === 'auto' ? null : value;
|
|
},
|
|
|
|
// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
|
|
// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
|
|
create: function (tagName, className, container) {
|
|
|
|
var el = document.createElement(tagName);
|
|
el.className = className || '';
|
|
|
|
if (container) {
|
|
container.appendChild(el);
|
|
}
|
|
|
|
return el;
|
|
},
|
|
|
|
// @function remove(el: HTMLElement)
|
|
// Removes `el` from its parent element
|
|
remove: function (el) {
|
|
var parent = el.parentNode;
|
|
if (parent) {
|
|
parent.removeChild(el);
|
|
}
|
|
},
|
|
|
|
// @function empty(el: HTMLElement)
|
|
// Removes all of `el`'s children elements from `el`
|
|
empty: function (el) {
|
|
while (el.firstChild) {
|
|
el.removeChild(el.firstChild);
|
|
}
|
|
},
|
|
|
|
// @function toFront(el: HTMLElement)
|
|
// Makes `el` the last children of its parent, so it renders in front of the other children.
|
|
toFront: function (el) {
|
|
el.parentNode.appendChild(el);
|
|
},
|
|
|
|
// @function toBack(el: HTMLElement)
|
|
// Makes `el` the first children of its parent, so it renders back from the other children.
|
|
toBack: function (el) {
|
|
var parent = el.parentNode;
|
|
parent.insertBefore(el, parent.firstChild);
|
|
},
|
|
|
|
// @function hasClass(el: HTMLElement, name: String): Boolean
|
|
// Returns `true` if the element's class attribute contains `name`.
|
|
hasClass: function (el, name) {
|
|
if (el.classList !== undefined) {
|
|
return el.classList.contains(name);
|
|
}
|
|
var className = L.DomUtil.getClass(el);
|
|
return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
|
|
},
|
|
|
|
// @function addClass(el: HTMLElement, name: String)
|
|
// Adds `name` to the element's class attribute.
|
|
addClass: function (el, name) {
|
|
if (el.classList !== undefined) {
|
|
var classes = L.Util.splitWords(name);
|
|
for (var i = 0, len = classes.length; i < len; i++) {
|
|
el.classList.add(classes[i]);
|
|
}
|
|
} else if (!L.DomUtil.hasClass(el, name)) {
|
|
var className = L.DomUtil.getClass(el);
|
|
L.DomUtil.setClass(el, (className ? className + ' ' : '') + name);
|
|
}
|
|
},
|
|
|
|
// @function removeClass(el: HTMLElement, name: String)
|
|
// Removes `name` from the element's class attribute.
|
|
removeClass: function (el, name) {
|
|
if (el.classList !== undefined) {
|
|
el.classList.remove(name);
|
|
} else {
|
|
L.DomUtil.setClass(el, L.Util.trim((' ' + L.DomUtil.getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
|
|
}
|
|
},
|
|
|
|
// @function setClass(el: HTMLElement, name: String)
|
|
// Sets the element's class.
|
|
setClass: function (el, name) {
|
|
if (el.className.baseVal === undefined) {
|
|
el.className = name;
|
|
} else {
|
|
// in case of SVG element
|
|
el.className.baseVal = name;
|
|
}
|
|
},
|
|
|
|
// @function getClass(el: HTMLElement): String
|
|
// Returns the element's class.
|
|
getClass: function (el) {
|
|
return el.className.baseVal === undefined ? el.className : el.className.baseVal;
|
|
},
|
|
|
|
// @function setOpacity(el: HTMLElement, opacity: Number)
|
|
// Set the opacity of an element (including old IE support).
|
|
// `opacity` must be a number from `0` to `1`.
|
|
setOpacity: function (el, value) {
|
|
|
|
if ('opacity' in el.style) {
|
|
el.style.opacity = value;
|
|
|
|
} else if ('filter' in el.style) {
|
|
L.DomUtil._setOpacityIE(el, value);
|
|
}
|
|
},
|
|
|
|
_setOpacityIE: function (el, value) {
|
|
var filter = false,
|
|
filterName = 'DXImageTransform.Microsoft.Alpha';
|
|
|
|
// filters collection throws an error if we try to retrieve a filter that doesn't exist
|
|
try {
|
|
filter = el.filters.item(filterName);
|
|
} catch (e) {
|
|
// don't set opacity to 1 if we haven't already set an opacity,
|
|
// it isn't needed and breaks transparent pngs.
|
|
if (value === 1) { return; }
|
|
}
|
|
|
|
value = Math.round(value * 100);
|
|
|
|
if (filter) {
|
|
filter.Enabled = (value !== 100);
|
|
filter.Opacity = value;
|
|
} else {
|
|
el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
|
|
}
|
|
},
|
|
|
|
// @function testProp(props: String[]): String|false
|
|
// Goes through the array of style names and returns the first name
|
|
// that is a valid style name for an element. If no such name is found,
|
|
// it returns false. Useful for vendor-prefixed styles like `transform`.
|
|
testProp: function (props) {
|
|
|
|
var style = document.documentElement.style;
|
|
|
|
for (var i = 0; i < props.length; i++) {
|
|
if (props[i] in style) {
|
|
return props[i];
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
|
|
// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
|
|
// and optionally scaled by `scale`. Does not have an effect if the
|
|
// browser doesn't support 3D CSS transforms.
|
|
setTransform: function (el, offset, scale) {
|
|
var pos = offset || new L.Point(0, 0);
|
|
|
|
el.style[L.DomUtil.TRANSFORM] =
|
|
(L.Browser.ie3d ?
|
|
'translate(' + pos.x + 'px,' + pos.y + 'px)' :
|
|
'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
|
|
(scale ? ' scale(' + scale + ')' : '');
|
|
},
|
|
|
|
// @function setPosition(el: HTMLElement, position: Point)
|
|
// Sets the position of `el` to coordinates specified by `position`,
|
|
// using CSS translate or top/left positioning depending on the browser
|
|
// (used by Leaflet internally to position its layers).
|
|
setPosition: function (el, point) { // (HTMLElement, Point[, Boolean])
|
|
|
|
/*eslint-disable */
|
|
el._leaflet_pos = point;
|
|
/*eslint-enable */
|
|
|
|
if (L.Browser.any3d) {
|
|
L.DomUtil.setTransform(el, point);
|
|
} else {
|
|
el.style.left = point.x + 'px';
|
|
el.style.top = point.y + 'px';
|
|
}
|
|
},
|
|
|
|
// @function getPosition(el: HTMLElement): Point
|
|
// Returns the coordinates of an element previously positioned with setPosition.
|
|
getPosition: function (el) {
|
|
// this method is only used for elements previously positioned using setPosition,
|
|
// so it's safe to cache the position for performance
|
|
|
|
return el._leaflet_pos || new L.Point(0, 0);
|
|
}
|
|
};
|
|
|
|
|
|
(function () {
|
|
// prefix style property names
|
|
|
|
// @property TRANSFORM: String
|
|
// Vendor-prefixed fransform style name (e.g. `'webkitTransform'` for WebKit).
|
|
L.DomUtil.TRANSFORM = L.DomUtil.testProp(
|
|
['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
|
|
|
|
|
|
// webkitTransition comes first because some browser versions that drop vendor prefix don't do
|
|
// the same for the transitionend event, in particular the Android 4.1 stock browser
|
|
|
|
// @property TRANSITION: String
|
|
// Vendor-prefixed transform style name.
|
|
var transition = L.DomUtil.TRANSITION = L.DomUtil.testProp(
|
|
['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
|
|
|
|
L.DomUtil.TRANSITION_END =
|
|
transition === 'webkitTransition' || transition === 'OTransition' ? transition + 'End' : 'transitionend';
|
|
|
|
// @function disableTextSelection()
|
|
// Prevents the user from generating `selectstart` DOM events, usually generated
|
|
// when the user drags the mouse through a page with text. Used internally
|
|
// by Leaflet to override the behaviour of any click-and-drag interaction on
|
|
// the map. Affects drag interactions on the whole document.
|
|
|
|
// @function enableTextSelection()
|
|
// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
|
|
if ('onselectstart' in document) {
|
|
L.DomUtil.disableTextSelection = function () {
|
|
L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
|
|
};
|
|
L.DomUtil.enableTextSelection = function () {
|
|
L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
|
|
};
|
|
|
|
} else {
|
|
var userSelectProperty = L.DomUtil.testProp(
|
|
['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
|
|
|
|
L.DomUtil.disableTextSelection = function () {
|
|
if (userSelectProperty) {
|
|
var style = document.documentElement.style;
|
|
this._userSelect = style[userSelectProperty];
|
|
style[userSelectProperty] = 'none';
|
|
}
|
|
};
|
|
L.DomUtil.enableTextSelection = function () {
|
|
if (userSelectProperty) {
|
|
document.documentElement.style[userSelectProperty] = this._userSelect;
|
|
delete this._userSelect;
|
|
}
|
|
};
|
|
}
|
|
|
|
// @function disableImageDrag()
|
|
// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
|
|
// for `dragstart` DOM events, usually generated when the user drags an image.
|
|
L.DomUtil.disableImageDrag = function () {
|
|
L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
|
|
};
|
|
|
|
// @function enableImageDrag()
|
|
// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
|
|
L.DomUtil.enableImageDrag = function () {
|
|
L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
|
|
};
|
|
|
|
// @function preventOutline(el: HTMLElement)
|
|
// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
|
|
// of the element `el` invisible. Used internally by Leaflet to prevent
|
|
// focusable elements from displaying an outline when the user performs a
|
|
// drag interaction on them.
|
|
L.DomUtil.preventOutline = function (element) {
|
|
while (element.tabIndex === -1) {
|
|
element = element.parentNode;
|
|
}
|
|
if (!element || !element.style) { return; }
|
|
L.DomUtil.restoreOutline();
|
|
this._outlineElement = element;
|
|
this._outlineStyle = element.style.outline;
|
|
element.style.outline = 'none';
|
|
L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this);
|
|
};
|
|
|
|
// @function restoreOutline()
|
|
// Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
|
|
L.DomUtil.restoreOutline = function () {
|
|
if (!this._outlineElement) { return; }
|
|
this._outlineElement.style.outline = this._outlineStyle;
|
|
delete this._outlineElement;
|
|
delete this._outlineStyle;
|
|
L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this);
|
|
};
|
|
})();
|
|
|
|
|
|
|
|
/* @class LatLng
|
|
* @aka L.LatLng
|
|
*
|
|
* Represents a geographical point with a certain latitude and longitude.
|
|
*
|
|
* @example
|
|
*
|
|
* ```
|
|
* var latlng = L.latLng(50.5, 30.5);
|
|
* ```
|
|
*
|
|
* All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
|
|
*
|
|
* ```
|
|
* map.panTo([50, 30]);
|
|
* map.panTo({lon: 30, lat: 50});
|
|
* map.panTo({lat: 50, lng: 30});
|
|
* map.panTo(L.latLng(50, 30));
|
|
* ```
|
|
*/
|
|
|
|
L.LatLng = function (lat, lng, alt) {
|
|
if (isNaN(lat) || isNaN(lng)) {
|
|
throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
|
|
}
|
|
|
|
// @property lat: Number
|
|
// Latitude in degrees
|
|
this.lat = +lat;
|
|
|
|
// @property lng: Number
|
|
// Longitude in degrees
|
|
this.lng = +lng;
|
|
|
|
// @property alt: Number
|
|
// Altitude in meters (optional)
|
|
if (alt !== undefined) {
|
|
this.alt = +alt;
|
|
}
|
|
};
|
|
|
|
L.LatLng.prototype = {
|
|
// @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
|
|
// Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number.
|
|
equals: function (obj, maxMargin) {
|
|
if (!obj) { return false; }
|
|
|
|
obj = L.latLng(obj);
|
|
|
|
var margin = Math.max(
|
|
Math.abs(this.lat - obj.lat),
|
|
Math.abs(this.lng - obj.lng));
|
|
|
|
return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
|
|
},
|
|
|
|
// @method toString(): String
|
|
// Returns a string representation of the point (for debugging purposes).
|
|
toString: function (precision) {
|
|
return 'LatLng(' +
|
|
L.Util.formatNum(this.lat, precision) + ', ' +
|
|
L.Util.formatNum(this.lng, precision) + ')';
|
|
},
|
|
|
|
// @method distanceTo(otherLatLng: LatLng): Number
|
|
// Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula).
|
|
distanceTo: function (other) {
|
|
return L.CRS.Earth.distance(this, L.latLng(other));
|
|
},
|
|
|
|
// @method wrap(): LatLng
|
|
// Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
|
|
wrap: function () {
|
|
return L.CRS.Earth.wrapLatLng(this);
|
|
},
|
|
|
|
// @method toBounds(sizeInMeters: Number): LatLngBounds
|
|
// Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters` meters apart from the `LatLng`.
|
|
toBounds: function (sizeInMeters) {
|
|
var latAccuracy = 180 * sizeInMeters / 40075017,
|
|
lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
|
|
|
|
return L.latLngBounds(
|
|
[this.lat - latAccuracy, this.lng - lngAccuracy],
|
|
[this.lat + latAccuracy, this.lng + lngAccuracy]);
|
|
},
|
|
|
|
clone: function () {
|
|
return new L.LatLng(this.lat, this.lng, this.alt);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
|
|
// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
|
|
|
|
// @alternative
|
|
// @factory L.latLng(coords: Array): LatLng
|
|
// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
|
|
|
|
// @alternative
|
|
// @factory L.latLng(coords: Object): LatLng
|
|
// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
|
|
|
|
L.latLng = function (a, b, c) {
|
|
if (a instanceof L.LatLng) {
|
|
return a;
|
|
}
|
|
if (L.Util.isArray(a) && typeof a[0] !== 'object') {
|
|
if (a.length === 3) {
|
|
return new L.LatLng(a[0], a[1], a[2]);
|
|
}
|
|
if (a.length === 2) {
|
|
return new L.LatLng(a[0], a[1]);
|
|
}
|
|
return null;
|
|
}
|
|
if (a === undefined || a === null) {
|
|
return a;
|
|
}
|
|
if (typeof a === 'object' && 'lat' in a) {
|
|
return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
|
|
}
|
|
if (b === undefined) {
|
|
return null;
|
|
}
|
|
return new L.LatLng(a, b, c);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class LatLngBounds
|
|
* @aka L.LatLngBounds
|
|
*
|
|
* Represents a rectangular geographical area on a map.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* var corner1 = L.latLng(40.712, -74.227),
|
|
* corner2 = L.latLng(40.774, -74.125),
|
|
* bounds = L.latLngBounds(corner1, corner2);
|
|
* ```
|
|
*
|
|
* All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
|
|
*
|
|
* ```js
|
|
* map.fitBounds([
|
|
* [40.712, -74.227],
|
|
* [40.774, -74.125]
|
|
* ]);
|
|
* ```
|
|
*
|
|
* Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
|
|
*/
|
|
|
|
L.LatLngBounds = function (corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
|
|
if (!corner1) { return; }
|
|
|
|
var latlngs = corner2 ? [corner1, corner2] : corner1;
|
|
|
|
for (var i = 0, len = latlngs.length; i < len; i++) {
|
|
this.extend(latlngs[i]);
|
|
}
|
|
};
|
|
|
|
L.LatLngBounds.prototype = {
|
|
|
|
// @method extend(latlng: LatLng): this
|
|
// Extend the bounds to contain the given point
|
|
|
|
// @alternative
|
|
// @method extend(otherBounds: LatLngBounds): this
|
|
// Extend the bounds to contain the given bounds
|
|
extend: function (obj) {
|
|
var sw = this._southWest,
|
|
ne = this._northEast,
|
|
sw2, ne2;
|
|
|
|
if (obj instanceof L.LatLng) {
|
|
sw2 = obj;
|
|
ne2 = obj;
|
|
|
|
} else if (obj instanceof L.LatLngBounds) {
|
|
sw2 = obj._southWest;
|
|
ne2 = obj._northEast;
|
|
|
|
if (!sw2 || !ne2) { return this; }
|
|
|
|
} else {
|
|
return obj ? this.extend(L.latLng(obj) || L.latLngBounds(obj)) : this;
|
|
}
|
|
|
|
if (!sw && !ne) {
|
|
this._southWest = new L.LatLng(sw2.lat, sw2.lng);
|
|
this._northEast = new L.LatLng(ne2.lat, ne2.lng);
|
|
} else {
|
|
sw.lat = Math.min(sw2.lat, sw.lat);
|
|
sw.lng = Math.min(sw2.lng, sw.lng);
|
|
ne.lat = Math.max(ne2.lat, ne.lat);
|
|
ne.lng = Math.max(ne2.lng, ne.lng);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method pad(bufferRatio: Number): LatLngBounds
|
|
// Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
|
|
pad: function (bufferRatio) {
|
|
var sw = this._southWest,
|
|
ne = this._northEast,
|
|
heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
|
|
widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
|
|
|
|
return new L.LatLngBounds(
|
|
new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
|
|
new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
|
|
},
|
|
|
|
// @method getCenter(): LatLng
|
|
// Returns the center point of the bounds.
|
|
getCenter: function () {
|
|
return new L.LatLng(
|
|
(this._southWest.lat + this._northEast.lat) / 2,
|
|
(this._southWest.lng + this._northEast.lng) / 2);
|
|
},
|
|
|
|
// @method getSouthWest(): LatLng
|
|
// Returns the south-west point of the bounds.
|
|
getSouthWest: function () {
|
|
return this._southWest;
|
|
},
|
|
|
|
// @method getNorthEast(): LatLng
|
|
// Returns the north-east point of the bounds.
|
|
getNorthEast: function () {
|
|
return this._northEast;
|
|
},
|
|
|
|
// @method getNorthWest(): LatLng
|
|
// Returns the north-west point of the bounds.
|
|
getNorthWest: function () {
|
|
return new L.LatLng(this.getNorth(), this.getWest());
|
|
},
|
|
|
|
// @method getSouthEast(): LatLng
|
|
// Returns the south-east point of the bounds.
|
|
getSouthEast: function () {
|
|
return new L.LatLng(this.getSouth(), this.getEast());
|
|
},
|
|
|
|
// @method getWest(): Number
|
|
// Returns the west longitude of the bounds
|
|
getWest: function () {
|
|
return this._southWest.lng;
|
|
},
|
|
|
|
// @method getSouth(): Number
|
|
// Returns the south latitude of the bounds
|
|
getSouth: function () {
|
|
return this._southWest.lat;
|
|
},
|
|
|
|
// @method getEast(): Number
|
|
// Returns the east longitude of the bounds
|
|
getEast: function () {
|
|
return this._northEast.lng;
|
|
},
|
|
|
|
// @method getNorth(): Number
|
|
// Returns the north latitude of the bounds
|
|
getNorth: function () {
|
|
return this._northEast.lat;
|
|
},
|
|
|
|
// @method contains(otherBounds: LatLngBounds): Boolean
|
|
// Returns `true` if the rectangle contains the given one.
|
|
|
|
// @alternative
|
|
// @method contains (latlng: LatLng): Boolean
|
|
// Returns `true` if the rectangle contains the given point.
|
|
contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
|
|
if (typeof obj[0] === 'number' || obj instanceof L.LatLng) {
|
|
obj = L.latLng(obj);
|
|
} else {
|
|
obj = L.latLngBounds(obj);
|
|
}
|
|
|
|
var sw = this._southWest,
|
|
ne = this._northEast,
|
|
sw2, ne2;
|
|
|
|
if (obj instanceof L.LatLngBounds) {
|
|
sw2 = obj.getSouthWest();
|
|
ne2 = obj.getNorthEast();
|
|
} else {
|
|
sw2 = ne2 = obj;
|
|
}
|
|
|
|
return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
|
|
(sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
|
|
},
|
|
|
|
// @method intersects(otherBounds: LatLngBounds): Boolean
|
|
// Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
|
|
intersects: function (bounds) {
|
|
bounds = L.latLngBounds(bounds);
|
|
|
|
var sw = this._southWest,
|
|
ne = this._northEast,
|
|
sw2 = bounds.getSouthWest(),
|
|
ne2 = bounds.getNorthEast(),
|
|
|
|
latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
|
|
lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
|
|
|
|
return latIntersects && lngIntersects;
|
|
},
|
|
|
|
// @method overlaps(otherBounds: Bounds): Boolean
|
|
// Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
|
|
overlaps: function (bounds) {
|
|
bounds = L.latLngBounds(bounds);
|
|
|
|
var sw = this._southWest,
|
|
ne = this._northEast,
|
|
sw2 = bounds.getSouthWest(),
|
|
ne2 = bounds.getNorthEast(),
|
|
|
|
latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
|
|
lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
|
|
|
|
return latOverlaps && lngOverlaps;
|
|
},
|
|
|
|
// @method toBBoxString(): String
|
|
// Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
|
|
toBBoxString: function () {
|
|
return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
|
|
},
|
|
|
|
// @method equals(otherBounds: LatLngBounds): Boolean
|
|
// Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds.
|
|
equals: function (bounds) {
|
|
if (!bounds) { return false; }
|
|
|
|
bounds = L.latLngBounds(bounds);
|
|
|
|
return this._southWest.equals(bounds.getSouthWest()) &&
|
|
this._northEast.equals(bounds.getNorthEast());
|
|
},
|
|
|
|
// @method isValid(): Boolean
|
|
// Returns `true` if the bounds are properly initialized.
|
|
isValid: function () {
|
|
return !!(this._southWest && this._northEast);
|
|
}
|
|
};
|
|
|
|
// TODO International date line?
|
|
|
|
// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
|
|
// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
|
|
|
|
// @alternative
|
|
// @factory L.latLngBounds(latlngs: LatLng[])
|
|
// Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
|
|
L.latLngBounds = function (a, b) {
|
|
if (a instanceof L.LatLngBounds) {
|
|
return a;
|
|
}
|
|
return new L.LatLngBounds(a, b);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @namespace Projection
|
|
* @section
|
|
* Leaflet comes with a set of already defined Projections out of the box:
|
|
*
|
|
* @projection L.Projection.LonLat
|
|
*
|
|
* Equirectangular, or Plate Carree projection — the most simple projection,
|
|
* mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
|
|
* latitude. Also suitable for flat worlds, e.g. game maps. Used by the
|
|
* `EPSG:3395` and `Simple` CRS.
|
|
*/
|
|
|
|
L.Projection = {};
|
|
|
|
L.Projection.LonLat = {
|
|
project: function (latlng) {
|
|
return new L.Point(latlng.lng, latlng.lat);
|
|
},
|
|
|
|
unproject: function (point) {
|
|
return new L.LatLng(point.y, point.x);
|
|
},
|
|
|
|
bounds: L.bounds([-180, -90], [180, 90])
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @namespace Projection
|
|
* @projection L.Projection.SphericalMercator
|
|
*
|
|
* Spherical Mercator projection — the most common projection for online maps,
|
|
* used by almost all free and commercial tile providers. Assumes that Earth is
|
|
* a sphere. Used by the `EPSG:3857` CRS.
|
|
*/
|
|
|
|
L.Projection.SphericalMercator = {
|
|
|
|
R: 6378137,
|
|
MAX_LATITUDE: 85.0511287798,
|
|
|
|
project: function (latlng) {
|
|
var d = Math.PI / 180,
|
|
max = this.MAX_LATITUDE,
|
|
lat = Math.max(Math.min(max, latlng.lat), -max),
|
|
sin = Math.sin(lat * d);
|
|
|
|
return new L.Point(
|
|
this.R * latlng.lng * d,
|
|
this.R * Math.log((1 + sin) / (1 - sin)) / 2);
|
|
},
|
|
|
|
unproject: function (point) {
|
|
var d = 180 / Math.PI;
|
|
|
|
return new L.LatLng(
|
|
(2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
|
|
point.x * d / this.R);
|
|
},
|
|
|
|
bounds: (function () {
|
|
var d = 6378137 * Math.PI;
|
|
return L.bounds([-d, -d], [d, d]);
|
|
})()
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class CRS
|
|
* @aka L.CRS
|
|
* Abstract class that defines coordinate reference systems for projecting
|
|
* geographical points into pixel (screen) coordinates and back (and to
|
|
* coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
|
|
* [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
|
|
*
|
|
* Leaflet defines the most usual CRSs by default. If you want to use a
|
|
* CRS not defined by default, take a look at the
|
|
* [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
|
|
*/
|
|
|
|
L.CRS = {
|
|
// @method latLngToPoint(latlng: LatLng, zoom: Number): Point
|
|
// Projects geographical coordinates into pixel coordinates for a given zoom.
|
|
latLngToPoint: function (latlng, zoom) {
|
|
var projectedPoint = this.projection.project(latlng),
|
|
scale = this.scale(zoom);
|
|
|
|
return this.transformation._transform(projectedPoint, scale);
|
|
},
|
|
|
|
// @method pointToLatLng(point: Point, zoom: Number): LatLng
|
|
// The inverse of `latLngToPoint`. Projects pixel coordinates on a given
|
|
// zoom into geographical coordinates.
|
|
pointToLatLng: function (point, zoom) {
|
|
var scale = this.scale(zoom),
|
|
untransformedPoint = this.transformation.untransform(point, scale);
|
|
|
|
return this.projection.unproject(untransformedPoint);
|
|
},
|
|
|
|
// @method project(latlng: LatLng): Point
|
|
// Projects geographical coordinates into coordinates in units accepted for
|
|
// this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
|
|
project: function (latlng) {
|
|
return this.projection.project(latlng);
|
|
},
|
|
|
|
// @method unproject(point: Point): LatLng
|
|
// Given a projected coordinate returns the corresponding LatLng.
|
|
// The inverse of `project`.
|
|
unproject: function (point) {
|
|
return this.projection.unproject(point);
|
|
},
|
|
|
|
// @method scale(zoom: Number): Number
|
|
// Returns the scale used when transforming projected coordinates into
|
|
// pixel coordinates for a particular zoom. For example, it returns
|
|
// `256 * 2^zoom` for Mercator-based CRS.
|
|
scale: function (zoom) {
|
|
return 256 * Math.pow(2, zoom);
|
|
},
|
|
|
|
// @method zoom(scale: Number): Number
|
|
// Inverse of `scale()`, returns the zoom level corresponding to a scale
|
|
// factor of `scale`.
|
|
zoom: function (scale) {
|
|
return Math.log(scale / 256) / Math.LN2;
|
|
},
|
|
|
|
// @method getProjectedBounds(zoom: Number): Bounds
|
|
// Returns the projection's bounds scaled and transformed for the provided `zoom`.
|
|
getProjectedBounds: function (zoom) {
|
|
if (this.infinite) { return null; }
|
|
|
|
var b = this.projection.bounds,
|
|
s = this.scale(zoom),
|
|
min = this.transformation.transform(b.min, s),
|
|
max = this.transformation.transform(b.max, s);
|
|
|
|
return L.bounds(min, max);
|
|
},
|
|
|
|
// @method distance(latlng1: LatLng, latlng2: LatLng): Number
|
|
// Returns the distance between two geographical coordinates.
|
|
|
|
// @property code: String
|
|
// Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
|
|
//
|
|
// @property wrapLng: Number[]
|
|
// An array of two numbers defining whether the longitude (horizontal) coordinate
|
|
// axis wraps around a given range and how. Defaults to `[-180, 180]` in most
|
|
// geographical CRSs. If `undefined`, the longitude axis does not wrap around.
|
|
//
|
|
// @property wrapLat: Number[]
|
|
// Like `wrapLng`, but for the latitude (vertical) axis.
|
|
|
|
// wrapLng: [min, max],
|
|
// wrapLat: [min, max],
|
|
|
|
// @property infinite: Boolean
|
|
// If true, the coordinate space will be unbounded (infinite in both axes)
|
|
infinite: false,
|
|
|
|
// @method wrapLatLng(latlng: LatLng): LatLng
|
|
// Returns a `LatLng` where lat and lng has been wrapped according to the
|
|
// CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
|
|
wrapLatLng: function (latlng) {
|
|
var lng = this.wrapLng ? L.Util.wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
|
|
lat = this.wrapLat ? L.Util.wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
|
|
alt = latlng.alt;
|
|
|
|
return L.latLng(lat, lng, alt);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @namespace CRS
|
|
* @crs L.CRS.Simple
|
|
*
|
|
* A simple CRS that maps longitude and latitude into `x` and `y` directly.
|
|
* May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
|
|
* axis should still be inverted (going from bottom to top). `distance()` returns
|
|
* simple euclidean distance.
|
|
*/
|
|
|
|
L.CRS.Simple = L.extend({}, L.CRS, {
|
|
projection: L.Projection.LonLat,
|
|
transformation: new L.Transformation(1, 0, -1, 0),
|
|
|
|
scale: function (zoom) {
|
|
return Math.pow(2, zoom);
|
|
},
|
|
|
|
zoom: function (scale) {
|
|
return Math.log(scale) / Math.LN2;
|
|
},
|
|
|
|
distance: function (latlng1, latlng2) {
|
|
var dx = latlng2.lng - latlng1.lng,
|
|
dy = latlng2.lat - latlng1.lat;
|
|
|
|
return Math.sqrt(dx * dx + dy * dy);
|
|
},
|
|
|
|
infinite: true
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @namespace CRS
|
|
* @crs L.CRS.Earth
|
|
*
|
|
* Serves as the base for CRS that are global such that they cover the earth.
|
|
* Can only be used as the base for other CRS and cannot be used directly,
|
|
* since it does not have a `code`, `projection` or `transformation`. `distance()` returns
|
|
* meters.
|
|
*/
|
|
|
|
L.CRS.Earth = L.extend({}, L.CRS, {
|
|
wrapLng: [-180, 180],
|
|
|
|
// Mean Earth Radius, as recommended for use by
|
|
// the International Union of Geodesy and Geophysics,
|
|
// see http://rosettacode.org/wiki/Haversine_formula
|
|
R: 6371000,
|
|
|
|
// distance between two geographical points using spherical law of cosines approximation
|
|
distance: function (latlng1, latlng2) {
|
|
var rad = Math.PI / 180,
|
|
lat1 = latlng1.lat * rad,
|
|
lat2 = latlng2.lat * rad,
|
|
a = Math.sin(lat1) * Math.sin(lat2) +
|
|
Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad);
|
|
|
|
return this.R * Math.acos(Math.min(a, 1));
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @namespace CRS
|
|
* @crs L.CRS.EPSG3857
|
|
*
|
|
* The most common CRS for online maps, used by almost all free and commercial
|
|
* tile providers. Uses Spherical Mercator projection. Set in by default in
|
|
* Map's `crs` option.
|
|
*/
|
|
|
|
L.CRS.EPSG3857 = L.extend({}, L.CRS.Earth, {
|
|
code: 'EPSG:3857',
|
|
projection: L.Projection.SphericalMercator,
|
|
|
|
transformation: (function () {
|
|
var scale = 0.5 / (Math.PI * L.Projection.SphericalMercator.R);
|
|
return new L.Transformation(scale, 0.5, -scale, 0.5);
|
|
}())
|
|
});
|
|
|
|
L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
|
|
code: 'EPSG:900913'
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @namespace CRS
|
|
* @crs L.CRS.EPSG4326
|
|
*
|
|
* A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
|
|
*
|
|
* Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
|
|
* which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer`
|
|
* with this CRS, ensure that there are two 256x256 pixel tiles covering the
|
|
* whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
|
|
* or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
|
|
*/
|
|
|
|
L.CRS.EPSG4326 = L.extend({}, L.CRS.Earth, {
|
|
code: 'EPSG:4326',
|
|
projection: L.Projection.LonLat,
|
|
transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5)
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @class Map
|
|
* @aka L.Map
|
|
* @inherits Evented
|
|
*
|
|
* The central class of the API — it is used to create a map on a page and manipulate it.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* // initialize the map on the "map" div with a given center and zoom
|
|
* var map = L.map('map', {
|
|
* center: [51.505, -0.09],
|
|
* zoom: 13
|
|
* });
|
|
* ```
|
|
*
|
|
*/
|
|
|
|
L.Map = L.Evented.extend({
|
|
|
|
options: {
|
|
// @section Map State Options
|
|
// @option crs: CRS = L.CRS.EPSG3857
|
|
// The [Coordinate Reference System](#crs) to use. Don't change this if you're not
|
|
// sure what it means.
|
|
crs: L.CRS.EPSG3857,
|
|
|
|
// @option center: LatLng = undefined
|
|
// Initial geographic center of the map
|
|
center: undefined,
|
|
|
|
// @option zoom: Number = undefined
|
|
// Initial map zoom level
|
|
zoom: undefined,
|
|
|
|
// @option minZoom: Number = undefined
|
|
// Minimum zoom level of the map. Overrides any `minZoom` option set on map layers.
|
|
minZoom: undefined,
|
|
|
|
// @option maxZoom: Number = undefined
|
|
// Maximum zoom level of the map. Overrides any `maxZoom` option set on map layers.
|
|
maxZoom: undefined,
|
|
|
|
// @option layers: Layer[] = []
|
|
// Array of layers that will be added to the map initially
|
|
layers: [],
|
|
|
|
// @option maxBounds: LatLngBounds = null
|
|
// When this option is set, the map restricts the view to the given
|
|
// geographical bounds, bouncing the user back when he tries to pan
|
|
// outside the view. To set the restriction dynamically, use
|
|
// [`setMaxBounds`](#map-setmaxbounds) method.
|
|
maxBounds: undefined,
|
|
|
|
// @option renderer: Renderer = *
|
|
// The default method for drawing vector layers on the map. `L.SVG`
|
|
// or `L.Canvas` by default depending on browser support.
|
|
renderer: undefined,
|
|
|
|
|
|
// @section Animation Options
|
|
// @option zoomAnimation: Boolean = true
|
|
// Whether the map zoom animation is enabled. By default it's enabled
|
|
// in all browsers that support CSS3 Transitions except Android.
|
|
zoomAnimation: true,
|
|
|
|
// @option zoomAnimationThreshold: Number = 4
|
|
// Won't animate zoom if the zoom difference exceeds this value.
|
|
zoomAnimationThreshold: 4,
|
|
|
|
// @option fadeAnimation: Boolean = true
|
|
// Whether the tile fade animation is enabled. By default it's enabled
|
|
// in all browsers that support CSS3 Transitions except Android.
|
|
fadeAnimation: true,
|
|
|
|
// @option markerZoomAnimation: Boolean = true
|
|
// Whether markers animate their zoom with the zoom animation, if disabled
|
|
// they will disappear for the length of the animation. By default it's
|
|
// enabled in all browsers that support CSS3 Transitions except Android.
|
|
markerZoomAnimation: true,
|
|
|
|
// @option transform3DLimit: Number = 2^23
|
|
// Defines the maximum size of a CSS translation transform. The default
|
|
// value should not be changed unless a web browser positions layers in
|
|
// the wrong place after doing a large `panBy`.
|
|
transform3DLimit: 8388608, // Precision limit of a 32-bit float
|
|
|
|
// @section Interaction Options
|
|
// @option zoomSnap: Number = 1
|
|
// Forces the map's zoom level to always be a multiple of this, particularly
|
|
// right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
|
|
// By default, the zoom level snaps to the nearest integer; lower values
|
|
// (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
|
|
// means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
|
|
zoomSnap: 1,
|
|
|
|
// @option zoomDelta: Number = 1
|
|
// Controls how much the map's zoom level will change after a
|
|
// [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
|
|
// or `-` on the keyboard, or using the [zoom controls](#control-zoom).
|
|
// Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
|
|
zoomDelta: 1,
|
|
|
|
// @option trackResize: Boolean = true
|
|
// Whether the map automatically handles browser window resize to update itself.
|
|
trackResize: true
|
|
},
|
|
|
|
initialize: function (id, options) { // (HTMLElement or String, Object)
|
|
options = L.setOptions(this, options);
|
|
|
|
this._initContainer(id);
|
|
this._initLayout();
|
|
|
|
// hack for https://github.com/Leaflet/Leaflet/issues/1980
|
|
this._onResize = L.bind(this._onResize, this);
|
|
|
|
this._initEvents();
|
|
|
|
if (options.maxBounds) {
|
|
this.setMaxBounds(options.maxBounds);
|
|
}
|
|
|
|
if (options.zoom !== undefined) {
|
|
this._zoom = this._limitZoom(options.zoom);
|
|
}
|
|
|
|
if (options.center && options.zoom !== undefined) {
|
|
this.setView(L.latLng(options.center), options.zoom, {reset: true});
|
|
}
|
|
|
|
this._handlers = [];
|
|
this._layers = {};
|
|
this._zoomBoundLayers = {};
|
|
this._sizeChanged = true;
|
|
|
|
this.callInitHooks();
|
|
|
|
// don't animate on browsers without hardware-accelerated transitions or old Android/Opera
|
|
this._zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera &&
|
|
this.options.zoomAnimation;
|
|
|
|
// zoom transitions run with the same duration for all layers, so if one of transitionend events
|
|
// happens after starting zoom animation (propagating to the map pane), we know that it ended globally
|
|
if (this._zoomAnimated) {
|
|
this._createAnimProxy();
|
|
L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
|
|
}
|
|
|
|
this._addLayers(this.options.layers);
|
|
},
|
|
|
|
|
|
// @section Methods for modifying map state
|
|
|
|
// @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
|
|
// Sets the view of the map (geographical center and zoom) with the given
|
|
// animation options.
|
|
setView: function (center, zoom, options) {
|
|
|
|
zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
|
|
center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
|
|
options = options || {};
|
|
|
|
this._stop();
|
|
|
|
if (this._loaded && !options.reset && options !== true) {
|
|
|
|
if (options.animate !== undefined) {
|
|
options.zoom = L.extend({animate: options.animate}, options.zoom);
|
|
options.pan = L.extend({animate: options.animate, duration: options.duration}, options.pan);
|
|
}
|
|
|
|
// try animating pan or zoom
|
|
var moved = (this._zoom !== zoom) ?
|
|
this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
|
|
this._tryAnimatedPan(center, options.pan);
|
|
|
|
if (moved) {
|
|
// prevent resize handler call, the view will refresh after animation anyway
|
|
clearTimeout(this._sizeTimer);
|
|
return this;
|
|
}
|
|
}
|
|
|
|
// animation didn't start, just reset the map view
|
|
this._resetView(center, zoom);
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method setZoom(zoom: Number, options: Zoom/pan options): this
|
|
// Sets the zoom of the map.
|
|
setZoom: function (zoom, options) {
|
|
if (!this._loaded) {
|
|
this._zoom = zoom;
|
|
return this;
|
|
}
|
|
return this.setView(this.getCenter(), zoom, {zoom: options});
|
|
},
|
|
|
|
// @method zoomIn(delta?: Number, options?: Zoom options): this
|
|
// Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
|
|
zoomIn: function (delta, options) {
|
|
delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
|
|
return this.setZoom(this._zoom + delta, options);
|
|
},
|
|
|
|
// @method zoomOut(delta?: Number, options?: Zoom options): this
|
|
// Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
|
|
zoomOut: function (delta, options) {
|
|
delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
|
|
return this.setZoom(this._zoom - delta, options);
|
|
},
|
|
|
|
// @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
|
|
// Zooms the map while keeping a specified geographical point on the map
|
|
// stationary (e.g. used internally for scroll zoom and double-click zoom).
|
|
// @alternative
|
|
// @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
|
|
// Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
|
|
setZoomAround: function (latlng, zoom, options) {
|
|
var scale = this.getZoomScale(zoom),
|
|
viewHalf = this.getSize().divideBy(2),
|
|
containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
|
|
|
|
centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
|
|
newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
|
|
|
|
return this.setView(newCenter, zoom, {zoom: options});
|
|
},
|
|
|
|
_getBoundsCenterZoom: function (bounds, options) {
|
|
|
|
options = options || {};
|
|
bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
|
|
|
|
var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
|
|
paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
|
|
|
|
zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
|
|
|
|
zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
|
|
|
|
var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
|
|
|
|
swPoint = this.project(bounds.getSouthWest(), zoom),
|
|
nePoint = this.project(bounds.getNorthEast(), zoom),
|
|
center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
|
|
|
|
return {
|
|
center: center,
|
|
zoom: zoom
|
|
};
|
|
},
|
|
|
|
// @method fitBounds(bounds: LatLngBounds, options: fitBounds options): this
|
|
// Sets a map view that contains the given geographical bounds with the
|
|
// maximum zoom level possible.
|
|
fitBounds: function (bounds, options) {
|
|
|
|
bounds = L.latLngBounds(bounds);
|
|
|
|
if (!bounds.isValid()) {
|
|
throw new Error('Bounds are not valid.');
|
|
}
|
|
|
|
var target = this._getBoundsCenterZoom(bounds, options);
|
|
return this.setView(target.center, target.zoom, options);
|
|
},
|
|
|
|
// @method fitWorld(options?: fitBounds options): this
|
|
// Sets a map view that mostly contains the whole world with the maximum
|
|
// zoom level possible.
|
|
fitWorld: function (options) {
|
|
return this.fitBounds([[-90, -180], [90, 180]], options);
|
|
},
|
|
|
|
// @method panTo(latlng: LatLng, options?: Pan options): this
|
|
// Pans the map to a given center.
|
|
panTo: function (center, options) { // (LatLng)
|
|
return this.setView(center, this._zoom, {pan: options});
|
|
},
|
|
|
|
// @method panBy(offset: Point): this
|
|
// Pans the map by a given number of pixels (animated).
|
|
panBy: function (offset, options) {
|
|
offset = L.point(offset).round();
|
|
options = options || {};
|
|
|
|
if (!offset.x && !offset.y) {
|
|
return this.fire('moveend');
|
|
}
|
|
// If we pan too far, Chrome gets issues with tiles
|
|
// and makes them disappear or appear in the wrong place (slightly offset) #2602
|
|
if (options.animate !== true && !this.getSize().contains(offset)) {
|
|
this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
|
|
return this;
|
|
}
|
|
|
|
if (!this._panAnim) {
|
|
this._panAnim = new L.PosAnimation();
|
|
|
|
this._panAnim.on({
|
|
'step': this._onPanTransitionStep,
|
|
'end': this._onPanTransitionEnd
|
|
}, this);
|
|
}
|
|
|
|
// don't fire movestart if animating inertia
|
|
if (!options.noMoveStart) {
|
|
this.fire('movestart');
|
|
}
|
|
|
|
// animate pan unless animate: false specified
|
|
if (options.animate !== false) {
|
|
L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
|
|
|
|
var newPos = this._getMapPanePos().subtract(offset).round();
|
|
this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
|
|
} else {
|
|
this._rawPanBy(offset);
|
|
this.fire('move').fire('moveend');
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
|
|
// Sets the view of the map (geographical center and zoom) performing a smooth
|
|
// pan-zoom animation.
|
|
flyTo: function (targetCenter, targetZoom, options) {
|
|
|
|
options = options || {};
|
|
if (options.animate === false || !L.Browser.any3d) {
|
|
return this.setView(targetCenter, targetZoom, options);
|
|
}
|
|
|
|
this._stop();
|
|
|
|
var from = this.project(this.getCenter()),
|
|
to = this.project(targetCenter),
|
|
size = this.getSize(),
|
|
startZoom = this._zoom;
|
|
|
|
targetCenter = L.latLng(targetCenter);
|
|
targetZoom = targetZoom === undefined ? startZoom : targetZoom;
|
|
|
|
var w0 = Math.max(size.x, size.y),
|
|
w1 = w0 * this.getZoomScale(startZoom, targetZoom),
|
|
u1 = (to.distanceTo(from)) || 1,
|
|
rho = 1.42,
|
|
rho2 = rho * rho;
|
|
|
|
function r(i) {
|
|
var s1 = i ? -1 : 1,
|
|
s2 = i ? w1 : w0,
|
|
t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
|
|
b1 = 2 * s2 * rho2 * u1,
|
|
b = t1 / b1,
|
|
sq = Math.sqrt(b * b + 1) - b;
|
|
|
|
// workaround for floating point precision bug when sq = 0, log = -Infinite,
|
|
// thus triggering an infinite loop in flyTo
|
|
var log = sq < 0.000000001 ? -18 : Math.log(sq);
|
|
|
|
return log;
|
|
}
|
|
|
|
function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
|
|
function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
|
|
function tanh(n) { return sinh(n) / cosh(n); }
|
|
|
|
var r0 = r(0);
|
|
|
|
function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
|
|
function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
|
|
|
|
function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
|
|
|
|
var start = Date.now(),
|
|
S = (r(1) - r0) / rho,
|
|
duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
|
|
|
|
function frame() {
|
|
var t = (Date.now() - start) / duration,
|
|
s = easeOut(t) * S;
|
|
|
|
if (t <= 1) {
|
|
this._flyToFrame = L.Util.requestAnimFrame(frame, this);
|
|
|
|
this._move(
|
|
this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
|
|
this.getScaleZoom(w0 / w(s), startZoom),
|
|
{flyTo: true});
|
|
|
|
} else {
|
|
this
|
|
._move(targetCenter, targetZoom)
|
|
._moveEnd(true);
|
|
}
|
|
}
|
|
|
|
this._moveStart(true);
|
|
|
|
frame.call(this);
|
|
return this;
|
|
},
|
|
|
|
// @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
|
|
// Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
|
|
// but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
|
|
flyToBounds: function (bounds, options) {
|
|
var target = this._getBoundsCenterZoom(bounds, options);
|
|
return this.flyTo(target.center, target.zoom, options);
|
|
},
|
|
|
|
// @method setMaxBounds(bounds: Bounds): this
|
|
// Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
|
|
setMaxBounds: function (bounds) {
|
|
bounds = L.latLngBounds(bounds);
|
|
|
|
if (!bounds.isValid()) {
|
|
this.options.maxBounds = null;
|
|
return this.off('moveend', this._panInsideMaxBounds);
|
|
} else if (this.options.maxBounds) {
|
|
this.off('moveend', this._panInsideMaxBounds);
|
|
}
|
|
|
|
this.options.maxBounds = bounds;
|
|
|
|
if (this._loaded) {
|
|
this._panInsideMaxBounds();
|
|
}
|
|
|
|
return this.on('moveend', this._panInsideMaxBounds);
|
|
},
|
|
|
|
// @method setMinZoom(zoom: Number): this
|
|
// Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
|
|
setMinZoom: function (zoom) {
|
|
this.options.minZoom = zoom;
|
|
|
|
if (this._loaded && this.getZoom() < this.options.minZoom) {
|
|
return this.setZoom(zoom);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method setMaxZoom(zoom: Number): this
|
|
// Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
|
|
setMaxZoom: function (zoom) {
|
|
this.options.maxZoom = zoom;
|
|
|
|
if (this._loaded && (this.getZoom() > this.options.maxZoom)) {
|
|
return this.setZoom(zoom);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
|
|
// Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
|
|
panInsideBounds: function (bounds, options) {
|
|
this._enforcingBounds = true;
|
|
var center = this.getCenter(),
|
|
newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
|
|
|
|
if (!center.equals(newCenter)) {
|
|
this.panTo(newCenter, options);
|
|
}
|
|
|
|
this._enforcingBounds = false;
|
|
return this;
|
|
},
|
|
|
|
// @method invalidateSize(options: Zoom/Pan options): this
|
|
// Checks if the map container size changed and updates the map if so —
|
|
// call it after you've changed the map size dynamically, also animating
|
|
// pan by default. If `options.pan` is `false`, panning will not occur.
|
|
// If `options.debounceMoveend` is `true`, it will delay `moveend` event so
|
|
// that it doesn't happen often even if the method is called many
|
|
// times in a row.
|
|
|
|
// @alternative
|
|
// @method invalidateSize(animate: Boolean): this
|
|
// Checks if the map container size changed and updates the map if so —
|
|
// call it after you've changed the map size dynamically, also animating
|
|
// pan by default.
|
|
invalidateSize: function (options) {
|
|
if (!this._loaded) { return this; }
|
|
|
|
options = L.extend({
|
|
animate: false,
|
|
pan: true
|
|
}, options === true ? {animate: true} : options);
|
|
|
|
var oldSize = this.getSize();
|
|
this._sizeChanged = true;
|
|
this._lastCenter = null;
|
|
|
|
var newSize = this.getSize(),
|
|
oldCenter = oldSize.divideBy(2).round(),
|
|
newCenter = newSize.divideBy(2).round(),
|
|
offset = oldCenter.subtract(newCenter);
|
|
|
|
if (!offset.x && !offset.y) { return this; }
|
|
|
|
if (options.animate && options.pan) {
|
|
this.panBy(offset);
|
|
|
|
} else {
|
|
if (options.pan) {
|
|
this._rawPanBy(offset);
|
|
}
|
|
|
|
this.fire('move');
|
|
|
|
if (options.debounceMoveend) {
|
|
clearTimeout(this._sizeTimer);
|
|
this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
|
|
} else {
|
|
this.fire('moveend');
|
|
}
|
|
}
|
|
|
|
// @section Map state change events
|
|
// @event resize: ResizeEvent
|
|
// Fired when the map is resized.
|
|
return this.fire('resize', {
|
|
oldSize: oldSize,
|
|
newSize: newSize
|
|
});
|
|
},
|
|
|
|
// @section Methods for modifying map state
|
|
// @method stop(): this
|
|
// Stops the currently running `panTo` or `flyTo` animation, if any.
|
|
stop: function () {
|
|
this.setZoom(this._limitZoom(this._zoom));
|
|
if (!this.options.zoomSnap) {
|
|
this.fire('viewreset');
|
|
}
|
|
return this._stop();
|
|
},
|
|
|
|
// @section Geolocation methods
|
|
// @method locate(options?: Locate options): this
|
|
// Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
|
|
// event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
|
|
// and optionally sets the map view to the user's location with respect to
|
|
// detection accuracy (or to the world view if geolocation failed).
|
|
// Note that, if your page doesn't use HTTPS, this method will fail in
|
|
// modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
|
|
// See `Locate options` for more details.
|
|
locate: function (options) {
|
|
|
|
options = this._locateOptions = L.extend({
|
|
timeout: 10000,
|
|
watch: false
|
|
// setView: false
|
|
// maxZoom: <Number>
|
|
// maximumAge: 0
|
|
// enableHighAccuracy: false
|
|
}, options);
|
|
|
|
if (!('geolocation' in navigator)) {
|
|
this._handleGeolocationError({
|
|
code: 0,
|
|
message: 'Geolocation not supported.'
|
|
});
|
|
return this;
|
|
}
|
|
|
|
var onResponse = L.bind(this._handleGeolocationResponse, this),
|
|
onError = L.bind(this._handleGeolocationError, this);
|
|
|
|
if (options.watch) {
|
|
this._locationWatchId =
|
|
navigator.geolocation.watchPosition(onResponse, onError, options);
|
|
} else {
|
|
navigator.geolocation.getCurrentPosition(onResponse, onError, options);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method stopLocate(): this
|
|
// Stops watching location previously initiated by `map.locate({watch: true})`
|
|
// and aborts resetting the map view if map.locate was called with
|
|
// `{setView: true}`.
|
|
stopLocate: function () {
|
|
if (navigator.geolocation && navigator.geolocation.clearWatch) {
|
|
navigator.geolocation.clearWatch(this._locationWatchId);
|
|
}
|
|
if (this._locateOptions) {
|
|
this._locateOptions.setView = false;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
_handleGeolocationError: function (error) {
|
|
var c = error.code,
|
|
message = error.message ||
|
|
(c === 1 ? 'permission denied' :
|
|
(c === 2 ? 'position unavailable' : 'timeout'));
|
|
|
|
if (this._locateOptions.setView && !this._loaded) {
|
|
this.fitWorld();
|
|
}
|
|
|
|
// @section Location events
|
|
// @event locationerror: ErrorEvent
|
|
// Fired when geolocation (using the [`locate`](#map-locate) method) failed.
|
|
this.fire('locationerror', {
|
|
code: c,
|
|
message: 'Geolocation error: ' + message + '.'
|
|
});
|
|
},
|
|
|
|
_handleGeolocationResponse: function (pos) {
|
|
var lat = pos.coords.latitude,
|
|
lng = pos.coords.longitude,
|
|
latlng = new L.LatLng(lat, lng),
|
|
bounds = latlng.toBounds(pos.coords.accuracy),
|
|
options = this._locateOptions;
|
|
|
|
if (options.setView) {
|
|
var zoom = this.getBoundsZoom(bounds);
|
|
this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
|
|
}
|
|
|
|
var data = {
|
|
latlng: latlng,
|
|
bounds: bounds,
|
|
timestamp: pos.timestamp
|
|
};
|
|
|
|
for (var i in pos.coords) {
|
|
if (typeof pos.coords[i] === 'number') {
|
|
data[i] = pos.coords[i];
|
|
}
|
|
}
|
|
|
|
// @event locationfound: LocationEvent
|
|
// Fired when geolocation (using the [`locate`](#map-locate) method)
|
|
// went successfully.
|
|
this.fire('locationfound', data);
|
|
},
|
|
|
|
// TODO handler.addTo
|
|
// TODO Appropiate docs section?
|
|
// @section Other Methods
|
|
// @method addHandler(name: String, HandlerClass: Function): this
|
|
// Adds a new `Handler` to the map, given its name and constructor function.
|
|
addHandler: function (name, HandlerClass) {
|
|
if (!HandlerClass) { return this; }
|
|
|
|
var handler = this[name] = new HandlerClass(this);
|
|
|
|
this._handlers.push(handler);
|
|
|
|
if (this.options[name]) {
|
|
handler.enable();
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method remove(): this
|
|
// Destroys the map and clears all related event listeners.
|
|
remove: function () {
|
|
|
|
this._initEvents(true);
|
|
|
|
if (this._containerId !== this._container._leaflet_id) {
|
|
throw new Error('Map container is being reused by another instance');
|
|
}
|
|
|
|
try {
|
|
// throws error in IE6-8
|
|
delete this._container._leaflet_id;
|
|
delete this._containerId;
|
|
} catch (e) {
|
|
/*eslint-disable */
|
|
this._container._leaflet_id = undefined;
|
|
/*eslint-enable */
|
|
this._containerId = undefined;
|
|
}
|
|
|
|
L.DomUtil.remove(this._mapPane);
|
|
|
|
if (this._clearControlPos) {
|
|
this._clearControlPos();
|
|
}
|
|
|
|
this._clearHandlers();
|
|
|
|
if (this._loaded) {
|
|
// @section Map state change events
|
|
// @event unload: Event
|
|
// Fired when the map is destroyed with [remove](#map-remove) method.
|
|
this.fire('unload');
|
|
}
|
|
|
|
for (var i in this._layers) {
|
|
this._layers[i].remove();
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @section Other Methods
|
|
// @method createPane(name: String, container?: HTMLElement): HTMLElement
|
|
// Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
|
|
// then returns it. The pane is created as a children of `container`, or
|
|
// as a children of the main map pane if not set.
|
|
createPane: function (name, container) {
|
|
var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
|
|
pane = L.DomUtil.create('div', className, container || this._mapPane);
|
|
|
|
if (name) {
|
|
this._panes[name] = pane;
|
|
}
|
|
return pane;
|
|
},
|
|
|
|
// @section Methods for Getting Map State
|
|
|
|
// @method getCenter(): LatLng
|
|
// Returns the geographical center of the map view
|
|
getCenter: function () {
|
|
this._checkIfLoaded();
|
|
|
|
if (this._lastCenter && !this._moved()) {
|
|
return this._lastCenter;
|
|
}
|
|
return this.layerPointToLatLng(this._getCenterLayerPoint());
|
|
},
|
|
|
|
// @method getZoom(): Number
|
|
// Returns the current zoom level of the map view
|
|
getZoom: function () {
|
|
return this._zoom;
|
|
},
|
|
|
|
// @method getBounds(): LatLngBounds
|
|
// Returns the geographical bounds visible in the current map view
|
|
getBounds: function () {
|
|
var bounds = this.getPixelBounds(),
|
|
sw = this.unproject(bounds.getBottomLeft()),
|
|
ne = this.unproject(bounds.getTopRight());
|
|
|
|
return new L.LatLngBounds(sw, ne);
|
|
},
|
|
|
|
// @method getMinZoom(): Number
|
|
// Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
|
|
getMinZoom: function () {
|
|
return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
|
|
},
|
|
|
|
// @method getMaxZoom(): Number
|
|
// Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
|
|
getMaxZoom: function () {
|
|
return this.options.maxZoom === undefined ?
|
|
(this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
|
|
this.options.maxZoom;
|
|
},
|
|
|
|
// @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
|
|
// Returns the maximum zoom level on which the given bounds fit to the map
|
|
// view in its entirety. If `inside` (optional) is set to `true`, the method
|
|
// instead returns the minimum zoom level on which the map view fits into
|
|
// the given bounds in its entirety.
|
|
getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
|
|
bounds = L.latLngBounds(bounds);
|
|
padding = L.point(padding || [0, 0]);
|
|
|
|
var zoom = this.getZoom() || 0,
|
|
min = this.getMinZoom(),
|
|
max = this.getMaxZoom(),
|
|
nw = bounds.getNorthWest(),
|
|
se = bounds.getSouthEast(),
|
|
size = this.getSize().subtract(padding),
|
|
boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)),
|
|
snap = L.Browser.any3d ? this.options.zoomSnap : 1;
|
|
|
|
var scale = Math.min(size.x / boundsSize.x, size.y / boundsSize.y);
|
|
zoom = this.getScaleZoom(scale, zoom);
|
|
|
|
if (snap) {
|
|
zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
|
|
zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
|
|
}
|
|
|
|
return Math.max(min, Math.min(max, zoom));
|
|
},
|
|
|
|
// @method getSize(): Point
|
|
// Returns the current size of the map container (in pixels).
|
|
getSize: function () {
|
|
if (!this._size || this._sizeChanged) {
|
|
this._size = new L.Point(
|
|
this._container.clientWidth,
|
|
this._container.clientHeight);
|
|
|
|
this._sizeChanged = false;
|
|
}
|
|
return this._size.clone();
|
|
},
|
|
|
|
// @method getPixelBounds(): Bounds
|
|
// Returns the bounds of the current map view in projected pixel
|
|
// coordinates (sometimes useful in layer and overlay implementations).
|
|
getPixelBounds: function (center, zoom) {
|
|
var topLeftPoint = this._getTopLeftPoint(center, zoom);
|
|
return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
|
|
},
|
|
|
|
// TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
|
|
// the map pane? "left point of the map layer" can be confusing, specially
|
|
// since there can be negative offsets.
|
|
// @method getPixelOrigin(): Point
|
|
// Returns the projected pixel coordinates of the top left point of
|
|
// the map layer (useful in custom layer and overlay implementations).
|
|
getPixelOrigin: function () {
|
|
this._checkIfLoaded();
|
|
return this._pixelOrigin;
|
|
},
|
|
|
|
// @method getPixelWorldBounds(zoom?: Number): Bounds
|
|
// Returns the world's bounds in pixel coordinates for zoom level `zoom`.
|
|
// If `zoom` is omitted, the map's current zoom level is used.
|
|
getPixelWorldBounds: function (zoom) {
|
|
return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
|
|
},
|
|
|
|
// @section Other Methods
|
|
|
|
// @method getPane(pane: String|HTMLElement): HTMLElement
|
|
// Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
|
|
getPane: function (pane) {
|
|
return typeof pane === 'string' ? this._panes[pane] : pane;
|
|
},
|
|
|
|
// @method getPanes(): Object
|
|
// Returns a plain object containing the names of all [panes](#map-pane) as keys and
|
|
// the panes as values.
|
|
getPanes: function () {
|
|
return this._panes;
|
|
},
|
|
|
|
// @method getContainer: HTMLElement
|
|
// Returns the HTML element that contains the map.
|
|
getContainer: function () {
|
|
return this._container;
|
|
},
|
|
|
|
|
|
// @section Conversion Methods
|
|
|
|
// @method getZoomScale(toZoom: Number, fromZoom: Number): Number
|
|
// Returns the scale factor to be applied to a map transition from zoom level
|
|
// `fromZoom` to `toZoom`. Used internally to help with zoom animations.
|
|
getZoomScale: function (toZoom, fromZoom) {
|
|
// TODO replace with universal implementation after refactoring projections
|
|
var crs = this.options.crs;
|
|
fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
|
|
return crs.scale(toZoom) / crs.scale(fromZoom);
|
|
},
|
|
|
|
// @method getScaleZoom(scale: Number, fromZoom: Number): Number
|
|
// Returns the zoom level that the map would end up at, if it is at `fromZoom`
|
|
// level and everything is scaled by a factor of `scale`. Inverse of
|
|
// [`getZoomScale`](#map-getZoomScale).
|
|
getScaleZoom: function (scale, fromZoom) {
|
|
var crs = this.options.crs;
|
|
fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
|
|
var zoom = crs.zoom(scale * crs.scale(fromZoom));
|
|
return isNaN(zoom) ? Infinity : zoom;
|
|
},
|
|
|
|
// @method project(latlng: LatLng, zoom: Number): Point
|
|
// Projects a geographical coordinate `LatLng` according to the projection
|
|
// of the map's CRS, then scales it according to `zoom` and the CRS's
|
|
// `Transformation`. The result is pixel coordinate relative to
|
|
// the CRS origin.
|
|
project: function (latlng, zoom) {
|
|
zoom = zoom === undefined ? this._zoom : zoom;
|
|
return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
|
|
},
|
|
|
|
// @method unproject(point: Point, zoom: Number): LatLng
|
|
// Inverse of [`project`](#map-project).
|
|
unproject: function (point, zoom) {
|
|
zoom = zoom === undefined ? this._zoom : zoom;
|
|
return this.options.crs.pointToLatLng(L.point(point), zoom);
|
|
},
|
|
|
|
// @method layerPointToLatLng(point: Point): LatLng
|
|
// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
|
|
// returns the corresponding geographical coordinate (for the current zoom level).
|
|
layerPointToLatLng: function (point) {
|
|
var projectedPoint = L.point(point).add(this.getPixelOrigin());
|
|
return this.unproject(projectedPoint);
|
|
},
|
|
|
|
// @method latLngToLayerPoint(latlng: LatLng): Point
|
|
// Given a geographical coordinate, returns the corresponding pixel coordinate
|
|
// relative to the [origin pixel](#map-getpixelorigin).
|
|
latLngToLayerPoint: function (latlng) {
|
|
var projectedPoint = this.project(L.latLng(latlng))._round();
|
|
return projectedPoint._subtract(this.getPixelOrigin());
|
|
},
|
|
|
|
// @method wrapLatLng(latlng: LatLng): LatLng
|
|
// Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
|
|
// map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
|
|
// CRS's bounds.
|
|
// By default this means longitude is wrapped around the dateline so its
|
|
// value is between -180 and +180 degrees.
|
|
wrapLatLng: function (latlng) {
|
|
return this.options.crs.wrapLatLng(L.latLng(latlng));
|
|
},
|
|
|
|
// @method distance(latlng1: LatLng, latlng2: LatLng): Number
|
|
// Returns the distance between two geographical coordinates according to
|
|
// the map's CRS. By default this measures distance in meters.
|
|
distance: function (latlng1, latlng2) {
|
|
return this.options.crs.distance(L.latLng(latlng1), L.latLng(latlng2));
|
|
},
|
|
|
|
// @method containerPointToLayerPoint(point: Point): Point
|
|
// Given a pixel coordinate relative to the map container, returns the corresponding
|
|
// pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
|
|
containerPointToLayerPoint: function (point) { // (Point)
|
|
return L.point(point).subtract(this._getMapPanePos());
|
|
},
|
|
|
|
// @method layerPointToContainerPoint(point: Point): Point
|
|
// Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
|
|
// returns the corresponding pixel coordinate relative to the map container.
|
|
layerPointToContainerPoint: function (point) { // (Point)
|
|
return L.point(point).add(this._getMapPanePos());
|
|
},
|
|
|
|
// @method containerPointToLatLng(point: Point): Point
|
|
// Given a pixel coordinate relative to the map container, returns
|
|
// the corresponding geographical coordinate (for the current zoom level).
|
|
containerPointToLatLng: function (point) {
|
|
var layerPoint = this.containerPointToLayerPoint(L.point(point));
|
|
return this.layerPointToLatLng(layerPoint);
|
|
},
|
|
|
|
// @method latLngToContainerPoint(latlng: LatLng): Point
|
|
// Given a geographical coordinate, returns the corresponding pixel coordinate
|
|
// relative to the map container.
|
|
latLngToContainerPoint: function (latlng) {
|
|
return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
|
|
},
|
|
|
|
// @method mouseEventToContainerPoint(ev: MouseEvent): Point
|
|
// Given a MouseEvent object, returns the pixel coordinate relative to the
|
|
// map container where the event took place.
|
|
mouseEventToContainerPoint: function (e) {
|
|
return L.DomEvent.getMousePosition(e, this._container);
|
|
},
|
|
|
|
// @method mouseEventToLayerPoint(ev: MouseEvent): Point
|
|
// Given a MouseEvent object, returns the pixel coordinate relative to
|
|
// the [origin pixel](#map-getpixelorigin) where the event took place.
|
|
mouseEventToLayerPoint: function (e) {
|
|
return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
|
|
},
|
|
|
|
// @method mouseEventToLatLng(ev: MouseEvent): LatLng
|
|
// Given a MouseEvent object, returns geographical coordinate where the
|
|
// event took place.
|
|
mouseEventToLatLng: function (e) { // (MouseEvent)
|
|
return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
|
|
},
|
|
|
|
|
|
// map initialization methods
|
|
|
|
_initContainer: function (id) {
|
|
var container = this._container = L.DomUtil.get(id);
|
|
|
|
if (!container) {
|
|
throw new Error('Map container not found.');
|
|
} else if (container._leaflet_id) {
|
|
throw new Error('Map container is already initialized.');
|
|
}
|
|
|
|
L.DomEvent.addListener(container, 'scroll', this._onScroll, this);
|
|
this._containerId = L.Util.stamp(container);
|
|
},
|
|
|
|
_initLayout: function () {
|
|
var container = this._container;
|
|
|
|
this._fadeAnimated = this.options.fadeAnimation && L.Browser.any3d;
|
|
|
|
L.DomUtil.addClass(container, 'leaflet-container' +
|
|
(L.Browser.touch ? ' leaflet-touch' : '') +
|
|
(L.Browser.retina ? ' leaflet-retina' : '') +
|
|
(L.Browser.ielt9 ? ' leaflet-oldie' : '') +
|
|
(L.Browser.safari ? ' leaflet-safari' : '') +
|
|
(this._fadeAnimated ? ' leaflet-fade-anim' : ''));
|
|
|
|
var position = L.DomUtil.getStyle(container, 'position');
|
|
|
|
if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
|
|
container.style.position = 'relative';
|
|
}
|
|
|
|
this._initPanes();
|
|
|
|
if (this._initControlPos) {
|
|
this._initControlPos();
|
|
}
|
|
},
|
|
|
|
_initPanes: function () {
|
|
var panes = this._panes = {};
|
|
this._paneRenderers = {};
|
|
|
|
// @section
|
|
//
|
|
// Panes are DOM elements used to control the ordering of layers on the map. You
|
|
// can access panes with [`map.getPane`](#map-getpane) or
|
|
// [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
|
|
// [`map.createPane`](#map-createpane) method.
|
|
//
|
|
// Every map has the following default panes that differ only in zIndex.
|
|
//
|
|
// @pane mapPane: HTMLElement = 'auto'
|
|
// Pane that contains all other map panes
|
|
|
|
this._mapPane = this.createPane('mapPane', this._container);
|
|
L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
|
|
|
|
// @pane tilePane: HTMLElement = 200
|
|
// Pane for `GridLayer`s and `TileLayer`s
|
|
this.createPane('tilePane');
|
|
// @pane overlayPane: HTMLElement = 400
|
|
// Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
|
|
this.createPane('shadowPane');
|
|
// @pane shadowPane: HTMLElement = 500
|
|
// Pane for overlay shadows (e.g. `Marker` shadows)
|
|
this.createPane('overlayPane');
|
|
// @pane markerPane: HTMLElement = 600
|
|
// Pane for `Icon`s of `Marker`s
|
|
this.createPane('markerPane');
|
|
// @pane tooltipPane: HTMLElement = 650
|
|
// Pane for tooltip.
|
|
this.createPane('tooltipPane');
|
|
// @pane popupPane: HTMLElement = 700
|
|
// Pane for `Popup`s.
|
|
this.createPane('popupPane');
|
|
|
|
if (!this.options.markerZoomAnimation) {
|
|
L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
|
|
L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
|
|
}
|
|
},
|
|
|
|
|
|
// private methods that modify map state
|
|
|
|
// @section Map state change events
|
|
_resetView: function (center, zoom) {
|
|
L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
|
|
|
|
var loading = !this._loaded;
|
|
this._loaded = true;
|
|
zoom = this._limitZoom(zoom);
|
|
|
|
this.fire('viewprereset');
|
|
|
|
var zoomChanged = this._zoom !== zoom;
|
|
this
|
|
._moveStart(zoomChanged)
|
|
._move(center, zoom)
|
|
._moveEnd(zoomChanged);
|
|
|
|
// @event viewreset: Event
|
|
// Fired when the map needs to redraw its content (this usually happens
|
|
// on map zoom or load). Very useful for creating custom overlays.
|
|
this.fire('viewreset');
|
|
|
|
// @event load: Event
|
|
// Fired when the map is initialized (when its center and zoom are set
|
|
// for the first time).
|
|
if (loading) {
|
|
this.fire('load');
|
|
}
|
|
},
|
|
|
|
_moveStart: function (zoomChanged) {
|
|
// @event zoomstart: Event
|
|
// Fired when the map zoom is about to change (e.g. before zoom animation).
|
|
// @event movestart: Event
|
|
// Fired when the view of the map starts changing (e.g. user starts dragging the map).
|
|
if (zoomChanged) {
|
|
this.fire('zoomstart');
|
|
}
|
|
return this.fire('movestart');
|
|
},
|
|
|
|
_move: function (center, zoom, data) {
|
|
if (zoom === undefined) {
|
|
zoom = this._zoom;
|
|
}
|
|
var zoomChanged = this._zoom !== zoom;
|
|
|
|
this._zoom = zoom;
|
|
this._lastCenter = center;
|
|
this._pixelOrigin = this._getNewPixelOrigin(center);
|
|
|
|
// @event zoom: Event
|
|
// Fired repeatedly during any change in zoom level, including zoom
|
|
// and fly animations.
|
|
if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530
|
|
this.fire('zoom', data);
|
|
}
|
|
|
|
// @event move: Event
|
|
// Fired repeatedly during any movement of the map, including pan and
|
|
// fly animations.
|
|
return this.fire('move', data);
|
|
},
|
|
|
|
_moveEnd: function (zoomChanged) {
|
|
// @event zoomend: Event
|
|
// Fired when the map has changed, after any animations.
|
|
if (zoomChanged) {
|
|
this.fire('zoomend');
|
|
}
|
|
|
|
// @event moveend: Event
|
|
// Fired when the center of the map stops changing (e.g. user stopped
|
|
// dragging the map).
|
|
return this.fire('moveend');
|
|
},
|
|
|
|
_stop: function () {
|
|
L.Util.cancelAnimFrame(this._flyToFrame);
|
|
if (this._panAnim) {
|
|
this._panAnim.stop();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
_rawPanBy: function (offset) {
|
|
L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
|
|
},
|
|
|
|
_getZoomSpan: function () {
|
|
return this.getMaxZoom() - this.getMinZoom();
|
|
},
|
|
|
|
_panInsideMaxBounds: function () {
|
|
if (!this._enforcingBounds) {
|
|
this.panInsideBounds(this.options.maxBounds);
|
|
}
|
|
},
|
|
|
|
_checkIfLoaded: function () {
|
|
if (!this._loaded) {
|
|
throw new Error('Set map center and zoom first.');
|
|
}
|
|
},
|
|
|
|
// DOM event handling
|
|
|
|
// @section Interaction events
|
|
_initEvents: function (remove) {
|
|
if (!L.DomEvent) { return; }
|
|
|
|
this._targets = {};
|
|
this._targets[L.stamp(this._container)] = this;
|
|
|
|
var onOff = remove ? 'off' : 'on';
|
|
|
|
// @event click: MouseEvent
|
|
// Fired when the user clicks (or taps) the map.
|
|
// @event dblclick: MouseEvent
|
|
// Fired when the user double-clicks (or double-taps) the map.
|
|
// @event mousedown: MouseEvent
|
|
// Fired when the user pushes the mouse button on the map.
|
|
// @event mouseup: MouseEvent
|
|
// Fired when the user releases the mouse button on the map.
|
|
// @event mouseover: MouseEvent
|
|
// Fired when the mouse enters the map.
|
|
// @event mouseout: MouseEvent
|
|
// Fired when the mouse leaves the map.
|
|
// @event mousemove: MouseEvent
|
|
// Fired while the mouse moves over the map.
|
|
// @event contextmenu: MouseEvent
|
|
// Fired when the user pushes the right mouse button on the map, prevents
|
|
// default browser context menu from showing if there are listeners on
|
|
// this event. Also fired on mobile when the user holds a single touch
|
|
// for a second (also called long press).
|
|
// @event keypress: KeyboardEvent
|
|
// Fired when the user presses a key from the keyboard while the map is focused.
|
|
L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
|
|
'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
|
|
|
|
if (this.options.trackResize) {
|
|
L.DomEvent[onOff](window, 'resize', this._onResize, this);
|
|
}
|
|
|
|
if (L.Browser.any3d && this.options.transform3DLimit) {
|
|
this[onOff]('moveend', this._onMoveEnd);
|
|
}
|
|
},
|
|
|
|
_onResize: function () {
|
|
L.Util.cancelAnimFrame(this._resizeRequest);
|
|
this._resizeRequest = L.Util.requestAnimFrame(
|
|
function () { this.invalidateSize({debounceMoveend: true}); }, this);
|
|
},
|
|
|
|
_onScroll: function () {
|
|
this._container.scrollTop = 0;
|
|
this._container.scrollLeft = 0;
|
|
},
|
|
|
|
_onMoveEnd: function () {
|
|
var pos = this._getMapPanePos();
|
|
if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
|
|
// a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
|
|
this._resetView(this.getCenter(), this.getZoom());
|
|
}
|
|
},
|
|
|
|
_findEventTargets: function (e, type) {
|
|
var targets = [],
|
|
target,
|
|
isHover = type === 'mouseout' || type === 'mouseover',
|
|
src = e.target || e.srcElement,
|
|
dragging = false;
|
|
|
|
while (src) {
|
|
target = this._targets[L.stamp(src)];
|
|
if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
|
|
// Prevent firing click after you just dragged an object.
|
|
dragging = true;
|
|
break;
|
|
}
|
|
if (target && target.listens(type, true)) {
|
|
if (isHover && !L.DomEvent._isExternalTarget(src, e)) { break; }
|
|
targets.push(target);
|
|
if (isHover) { break; }
|
|
}
|
|
if (src === this._container) { break; }
|
|
src = src.parentNode;
|
|
}
|
|
if (!targets.length && !dragging && !isHover && L.DomEvent._isExternalTarget(src, e)) {
|
|
targets = [this];
|
|
}
|
|
return targets;
|
|
},
|
|
|
|
_handleDOMEvent: function (e) {
|
|
if (!this._loaded || L.DomEvent._skipped(e)) { return; }
|
|
|
|
var type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
|
|
|
|
if (type === 'mousedown') {
|
|
// prevents outline when clicking on keyboard-focusable element
|
|
L.DomUtil.preventOutline(e.target || e.srcElement);
|
|
}
|
|
|
|
this._fireDOMEvent(e, type);
|
|
},
|
|
|
|
_fireDOMEvent: function (e, type, targets) {
|
|
|
|
if (e.type === 'click') {
|
|
// Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
|
|
// @event preclick: MouseEvent
|
|
// Fired before mouse click on the map (sometimes useful when you
|
|
// want something to happen on click before any existing click
|
|
// handlers start running).
|
|
var synth = L.Util.extend({}, e);
|
|
synth.type = 'preclick';
|
|
this._fireDOMEvent(synth, synth.type, targets);
|
|
}
|
|
|
|
if (e._stopped) { return; }
|
|
|
|
// Find the layer the event is propagating from and its parents.
|
|
targets = (targets || []).concat(this._findEventTargets(e, type));
|
|
|
|
if (!targets.length) { return; }
|
|
|
|
var target = targets[0];
|
|
if (type === 'contextmenu' && target.listens(type, true)) {
|
|
L.DomEvent.preventDefault(e);
|
|
}
|
|
|
|
var data = {
|
|
originalEvent: e
|
|
};
|
|
|
|
if (e.type !== 'keypress') {
|
|
var isMarker = target instanceof L.Marker;
|
|
data.containerPoint = isMarker ?
|
|
this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
|
|
data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
|
|
data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
|
|
}
|
|
|
|
for (var i = 0; i < targets.length; i++) {
|
|
targets[i].fire(type, data, true);
|
|
if (data.originalEvent._stopped ||
|
|
(targets[i].options.nonBubblingEvents && L.Util.indexOf(targets[i].options.nonBubblingEvents, type) !== -1)) { return; }
|
|
}
|
|
},
|
|
|
|
_draggableMoved: function (obj) {
|
|
obj = obj.dragging && obj.dragging.enabled() ? obj : this;
|
|
return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
|
|
},
|
|
|
|
_clearHandlers: function () {
|
|
for (var i = 0, len = this._handlers.length; i < len; i++) {
|
|
this._handlers[i].disable();
|
|
}
|
|
},
|
|
|
|
// @section Other Methods
|
|
|
|
// @method whenReady(fn: Function, context?: Object): this
|
|
// Runs the given function `fn` when the map gets initialized with
|
|
// a view (center and zoom) and at least one layer, or immediately
|
|
// if it's already initialized, optionally passing a function context.
|
|
whenReady: function (callback, context) {
|
|
if (this._loaded) {
|
|
callback.call(context || this, {target: this});
|
|
} else {
|
|
this.on('load', callback, context);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
|
|
// private methods for getting map state
|
|
|
|
_getMapPanePos: function () {
|
|
return L.DomUtil.getPosition(this._mapPane) || new L.Point(0, 0);
|
|
},
|
|
|
|
_moved: function () {
|
|
var pos = this._getMapPanePos();
|
|
return pos && !pos.equals([0, 0]);
|
|
},
|
|
|
|
_getTopLeftPoint: function (center, zoom) {
|
|
var pixelOrigin = center && zoom !== undefined ?
|
|
this._getNewPixelOrigin(center, zoom) :
|
|
this.getPixelOrigin();
|
|
return pixelOrigin.subtract(this._getMapPanePos());
|
|
},
|
|
|
|
_getNewPixelOrigin: function (center, zoom) {
|
|
var viewHalf = this.getSize()._divideBy(2);
|
|
return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
|
|
},
|
|
|
|
_latLngToNewLayerPoint: function (latlng, zoom, center) {
|
|
var topLeft = this._getNewPixelOrigin(center, zoom);
|
|
return this.project(latlng, zoom)._subtract(topLeft);
|
|
},
|
|
|
|
_latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
|
|
var topLeft = this._getNewPixelOrigin(center, zoom);
|
|
return L.bounds([
|
|
this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
|
|
this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
|
|
this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
|
|
this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
|
|
]);
|
|
},
|
|
|
|
// layer point of the current center
|
|
_getCenterLayerPoint: function () {
|
|
return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
|
|
},
|
|
|
|
// offset of the specified place to the current center in pixels
|
|
_getCenterOffset: function (latlng) {
|
|
return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
|
|
},
|
|
|
|
// adjust center for view to get inside bounds
|
|
_limitCenter: function (center, zoom, bounds) {
|
|
|
|
if (!bounds) { return center; }
|
|
|
|
var centerPoint = this.project(center, zoom),
|
|
viewHalf = this.getSize().divideBy(2),
|
|
viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
|
|
offset = this._getBoundsOffset(viewBounds, bounds, zoom);
|
|
|
|
// If offset is less than a pixel, ignore.
|
|
// This prevents unstable projections from getting into
|
|
// an infinite loop of tiny offsets.
|
|
if (offset.round().equals([0, 0])) {
|
|
return center;
|
|
}
|
|
|
|
return this.unproject(centerPoint.add(offset), zoom);
|
|
},
|
|
|
|
// adjust offset for view to get inside bounds
|
|
_limitOffset: function (offset, bounds) {
|
|
if (!bounds) { return offset; }
|
|
|
|
var viewBounds = this.getPixelBounds(),
|
|
newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
|
|
|
|
return offset.add(this._getBoundsOffset(newBounds, bounds));
|
|
},
|
|
|
|
// returns offset needed for pxBounds to get inside maxBounds at a specified zoom
|
|
_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
|
|
var projectedMaxBounds = L.bounds(
|
|
this.project(maxBounds.getNorthEast(), zoom),
|
|
this.project(maxBounds.getSouthWest(), zoom)
|
|
),
|
|
minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
|
|
maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
|
|
|
|
dx = this._rebound(minOffset.x, -maxOffset.x),
|
|
dy = this._rebound(minOffset.y, -maxOffset.y);
|
|
|
|
return new L.Point(dx, dy);
|
|
},
|
|
|
|
_rebound: function (left, right) {
|
|
return left + right > 0 ?
|
|
Math.round(left - right) / 2 :
|
|
Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
|
|
},
|
|
|
|
_limitZoom: function (zoom) {
|
|
var min = this.getMinZoom(),
|
|
max = this.getMaxZoom(),
|
|
snap = L.Browser.any3d ? this.options.zoomSnap : 1;
|
|
if (snap) {
|
|
zoom = Math.round(zoom / snap) * snap;
|
|
}
|
|
return Math.max(min, Math.min(max, zoom));
|
|
},
|
|
|
|
_onPanTransitionStep: function () {
|
|
this.fire('move');
|
|
},
|
|
|
|
_onPanTransitionEnd: function () {
|
|
L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
|
|
this.fire('moveend');
|
|
},
|
|
|
|
_tryAnimatedPan: function (center, options) {
|
|
// difference between the new and current centers in pixels
|
|
var offset = this._getCenterOffset(center)._floor();
|
|
|
|
// don't animate too far unless animate: true specified in options
|
|
if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
|
|
|
|
this.panBy(offset, options);
|
|
|
|
return true;
|
|
},
|
|
|
|
_createAnimProxy: function () {
|
|
|
|
var proxy = this._proxy = L.DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated');
|
|
this._panes.mapPane.appendChild(proxy);
|
|
|
|
this.on('zoomanim', function (e) {
|
|
var prop = L.DomUtil.TRANSFORM,
|
|
transform = proxy.style[prop];
|
|
|
|
L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
|
|
|
|
// workaround for case when transform is the same and so transitionend event is not fired
|
|
if (transform === proxy.style[prop] && this._animatingZoom) {
|
|
this._onZoomTransitionEnd();
|
|
}
|
|
}, this);
|
|
|
|
this.on('load moveend', function () {
|
|
var c = this.getCenter(),
|
|
z = this.getZoom();
|
|
L.DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1));
|
|
}, this);
|
|
},
|
|
|
|
_catchTransitionEnd: function (e) {
|
|
if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
|
|
this._onZoomTransitionEnd();
|
|
}
|
|
},
|
|
|
|
_nothingToAnimate: function () {
|
|
return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
|
|
},
|
|
|
|
_tryAnimatedZoom: function (center, zoom, options) {
|
|
|
|
if (this._animatingZoom) { return true; }
|
|
|
|
options = options || {};
|
|
|
|
// don't animate if disabled, not supported or zoom difference is too large
|
|
if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
|
|
Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
|
|
|
|
// offset is the pixel coords of the zoom origin relative to the current center
|
|
var scale = this.getZoomScale(zoom),
|
|
offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
|
|
|
|
// don't animate if the zoom origin isn't within one screen from the current center, unless forced
|
|
if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
|
|
|
|
L.Util.requestAnimFrame(function () {
|
|
this
|
|
._moveStart(true)
|
|
._animateZoom(center, zoom, true);
|
|
}, this);
|
|
|
|
return true;
|
|
},
|
|
|
|
_animateZoom: function (center, zoom, startAnim, noUpdate) {
|
|
if (startAnim) {
|
|
this._animatingZoom = true;
|
|
|
|
// remember what center/zoom to set after animation
|
|
this._animateToCenter = center;
|
|
this._animateToZoom = zoom;
|
|
|
|
L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
|
|
}
|
|
|
|
// @event zoomanim: ZoomAnimEvent
|
|
// Fired on every frame of a zoom animation
|
|
this.fire('zoomanim', {
|
|
center: center,
|
|
zoom: zoom,
|
|
noUpdate: noUpdate
|
|
});
|
|
|
|
// Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
|
|
setTimeout(L.bind(this._onZoomTransitionEnd, this), 250);
|
|
},
|
|
|
|
_onZoomTransitionEnd: function () {
|
|
if (!this._animatingZoom) { return; }
|
|
|
|
L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
|
|
|
|
this._animatingZoom = false;
|
|
|
|
this._move(this._animateToCenter, this._animateToZoom);
|
|
|
|
// This anim frame should prevent an obscure iOS webkit tile loading race condition.
|
|
L.Util.requestAnimFrame(function () {
|
|
this._moveEnd(true);
|
|
}, this);
|
|
}
|
|
});
|
|
|
|
// @section
|
|
|
|
// @factory L.map(id: String, options?: Map options)
|
|
// Instantiates a map object given the DOM ID of a `<div>` element
|
|
// and optionally an object literal with `Map options`.
|
|
//
|
|
// @alternative
|
|
// @factory L.map(el: HTMLElement, options?: Map options)
|
|
// Instantiates a map object given an instance of a `<div>` HTML element
|
|
// and optionally an object literal with `Map options`.
|
|
L.map = function (id, options) {
|
|
return new L.Map(id, options);
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
* @class Layer
|
|
* @inherits Evented
|
|
* @aka L.Layer
|
|
* @aka ILayer
|
|
*
|
|
* A set of methods from the Layer base class that all Leaflet layers use.
|
|
* Inherits all methods, options and events from `L.Evented`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* var layer = L.Marker(latlng).addTo(map);
|
|
* layer.addTo(map);
|
|
* layer.remove();
|
|
* ```
|
|
*
|
|
* @event add: Event
|
|
* Fired after the layer is added to a map
|
|
*
|
|
* @event remove: Event
|
|
* Fired after the layer is removed from a map
|
|
*/
|
|
|
|
|
|
L.Layer = L.Evented.extend({
|
|
|
|
// Classes extending `L.Layer` will inherit the following options:
|
|
options: {
|
|
// @option pane: String = 'overlayPane'
|
|
// By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default.
|
|
pane: 'overlayPane',
|
|
nonBubblingEvents: [], // Array of events that should not be bubbled to DOM parents (like the map),
|
|
|
|
// @option attribution: String = null
|
|
// String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
|
|
attribution: null,
|
|
},
|
|
|
|
/* @section
|
|
* Classes extending `L.Layer` will inherit the following methods:
|
|
*
|
|
* @method addTo(map: Map): this
|
|
* Adds the layer to the given map
|
|
*/
|
|
addTo: function (map) {
|
|
map.addLayer(this);
|
|
return this;
|
|
},
|
|
|
|
// @method remove: this
|
|
// Removes the layer from the map it is currently active on.
|
|
remove: function () {
|
|
return this.removeFrom(this._map || this._mapToAdd);
|
|
},
|
|
|
|
// @method removeFrom(map: Map): this
|
|
// Removes the layer from the given map
|
|
removeFrom: function (obj) {
|
|
if (obj) {
|
|
obj.removeLayer(this);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method getPane(name? : String): HTMLElement
|
|
// Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
|
|
getPane: function (name) {
|
|
return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
|
|
},
|
|
|
|
addInteractiveTarget: function (targetEl) {
|
|
this._map._targets[L.stamp(targetEl)] = this;
|
|
return this;
|
|
},
|
|
|
|
removeInteractiveTarget: function (targetEl) {
|
|
delete this._map._targets[L.stamp(targetEl)];
|
|
return this;
|
|
},
|
|
|
|
// @method getAttribution: String
|
|
// Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
|
|
getAttribution: function () {
|
|
return this.options.attribution;
|
|
},
|
|
|
|
_layerAdd: function (e) {
|
|
var map = e.target;
|
|
|
|
// check in case layer gets added and then removed before the map is ready
|
|
if (!map.hasLayer(this)) { return; }
|
|
|
|
this._map = map;
|
|
this._zoomAnimated = map._zoomAnimated;
|
|
|
|
if (this.getEvents) {
|
|
var events = this.getEvents();
|
|
map.on(events, this);
|
|
this.once('remove', function () {
|
|
map.off(events, this);
|
|
}, this);
|
|
}
|
|
|
|
this.onAdd(map);
|
|
|
|
if (this.getAttribution && this._map.attributionControl) {
|
|
this._map.attributionControl.addAttribution(this.getAttribution());
|
|
}
|
|
|
|
this.fire('add');
|
|
map.fire('layeradd', {layer: this});
|
|
}
|
|
});
|
|
|
|
/* @section Extension methods
|
|
* @uninheritable
|
|
*
|
|
* Every layer should extend from `L.Layer` and (re-)implement the following methods.
|
|
*
|
|
* @method onAdd(map: Map): this
|
|
* Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer).
|
|
*
|
|
* @method onRemove(map: Map): this
|
|
* Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
|
|
*
|
|
* @method getEvents(): Object
|
|
* This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
|
|
*
|
|
* @method getAttribution(): String
|
|
* This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
|
|
*
|
|
* @method beforeAdd(map: Map): this
|
|
* Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only.
|
|
*/
|
|
|
|
|
|
/* @namespace Map
|
|
* @section Layer events
|
|
*
|
|
* @event layeradd: LayerEvent
|
|
* Fired when a new layer is added to the map.
|
|
*
|
|
* @event layerremove: LayerEvent
|
|
* Fired when some layer is removed from the map
|
|
*
|
|
* @section Methods for Layers and Controls
|
|
*/
|
|
L.Map.include({
|
|
// @method addLayer(layer: Layer): this
|
|
// Adds the given layer to the map
|
|
addLayer: function (layer) {
|
|
var id = L.stamp(layer);
|
|
if (this._layers[id]) { return this; }
|
|
this._layers[id] = layer;
|
|
|
|
layer._mapToAdd = this;
|
|
|
|
if (layer.beforeAdd) {
|
|
layer.beforeAdd(this);
|
|
}
|
|
|
|
this.whenReady(layer._layerAdd, layer);
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method removeLayer(layer: Layer): this
|
|
// Removes the given layer from the map.
|
|
removeLayer: function (layer) {
|
|
var id = L.stamp(layer);
|
|
|
|
if (!this._layers[id]) { return this; }
|
|
|
|
if (this._loaded) {
|
|
layer.onRemove(this);
|
|
}
|
|
|
|
if (layer.getAttribution && this.attributionControl) {
|
|
this.attributionControl.removeAttribution(layer.getAttribution());
|
|
}
|
|
|
|
delete this._layers[id];
|
|
|
|
if (this._loaded) {
|
|
this.fire('layerremove', {layer: layer});
|
|
layer.fire('remove');
|
|
}
|
|
|
|
layer._map = layer._mapToAdd = null;
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method hasLayer(layer: Layer): Boolean
|
|
// Returns `true` if the given layer is currently added to the map
|
|
hasLayer: function (layer) {
|
|
return !!layer && (L.stamp(layer) in this._layers);
|
|
},
|
|
|
|
/* @method eachLayer(fn: Function, context?: Object): this
|
|
* Iterates over the layers of the map, optionally specifying context of the iterator function.
|
|
* ```
|
|
* map.eachLayer(function(layer){
|
|
* layer.bindPopup('Hello');
|
|
* });
|
|
* ```
|
|
*/
|
|
eachLayer: function (method, context) {
|
|
for (var i in this._layers) {
|
|
method.call(context, this._layers[i]);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
_addLayers: function (layers) {
|
|
layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
|
|
|
|
for (var i = 0, len = layers.length; i < len; i++) {
|
|
this.addLayer(layers[i]);
|
|
}
|
|
},
|
|
|
|
_addZoomLimit: function (layer) {
|
|
if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
|
|
this._zoomBoundLayers[L.stamp(layer)] = layer;
|
|
this._updateZoomLevels();
|
|
}
|
|
},
|
|
|
|
_removeZoomLimit: function (layer) {
|
|
var id = L.stamp(layer);
|
|
|
|
if (this._zoomBoundLayers[id]) {
|
|
delete this._zoomBoundLayers[id];
|
|
this._updateZoomLevels();
|
|
}
|
|
},
|
|
|
|
_updateZoomLevels: function () {
|
|
var minZoom = Infinity,
|
|
maxZoom = -Infinity,
|
|
oldZoomSpan = this._getZoomSpan();
|
|
|
|
for (var i in this._zoomBoundLayers) {
|
|
var options = this._zoomBoundLayers[i].options;
|
|
|
|
minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
|
|
maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
|
|
}
|
|
|
|
this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
|
|
this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
|
|
|
|
// @section Map state change events
|
|
// @event zoomlevelschange: Event
|
|
// Fired when the number of zoomlevels on the map is changed due
|
|
// to adding or removing a layer.
|
|
if (oldZoomSpan !== this._getZoomSpan()) {
|
|
this.fire('zoomlevelschange');
|
|
}
|
|
|
|
if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
|
|
this.setZoom(this._layersMaxZoom);
|
|
}
|
|
if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
|
|
this.setZoom(this._layersMinZoom);
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @namespace DomEvent
|
|
* Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
|
|
*/
|
|
|
|
// Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
|
|
|
|
|
|
|
|
var eventsKey = '_leaflet_events';
|
|
|
|
L.DomEvent = {
|
|
|
|
// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
|
|
// Adds a listener function (`fn`) to a particular DOM event type of the
|
|
// element `el`. You can optionally specify the context of the listener
|
|
// (object the `this` keyword will point to). You can also pass several
|
|
// space-separated types (e.g. `'click dblclick'`).
|
|
|
|
// @alternative
|
|
// @function on(el: HTMLElement, eventMap: Object, context?: Object): this
|
|
// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
|
|
on: function (obj, types, fn, context) {
|
|
|
|
if (typeof types === 'object') {
|
|
for (var type in types) {
|
|
this._on(obj, type, types[type], fn);
|
|
}
|
|
} else {
|
|
types = L.Util.splitWords(types);
|
|
|
|
for (var i = 0, len = types.length; i < len; i++) {
|
|
this._on(obj, types[i], fn, context);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
|
|
// Removes a previously added listener function. If no function is specified,
|
|
// it will remove all the listeners of that particular DOM event from the element.
|
|
// Note that if you passed a custom context to on, you must pass the same
|
|
// context to `off` in order to remove the listener.
|
|
|
|
// @alternative
|
|
// @function off(el: HTMLElement, eventMap: Object, context?: Object): this
|
|
// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
|
|
off: function (obj, types, fn, context) {
|
|
|
|
if (typeof types === 'object') {
|
|
for (var type in types) {
|
|
this._off(obj, type, types[type], fn);
|
|
}
|
|
} else {
|
|
types = L.Util.splitWords(types);
|
|
|
|
for (var i = 0, len = types.length; i < len; i++) {
|
|
this._off(obj, types[i], fn, context);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
_on: function (obj, type, fn, context) {
|
|
var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : '');
|
|
|
|
if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
|
|
|
|
var handler = function (e) {
|
|
return fn.call(context || obj, e || window.event);
|
|
};
|
|
|
|
var originalHandler = handler;
|
|
|
|
if (L.Browser.pointer && type.indexOf('touch') === 0) {
|
|
this.addPointerListener(obj, type, handler, id);
|
|
|
|
} else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
|
|
this.addDoubleTapListener(obj, handler, id);
|
|
|
|
} else if ('addEventListener' in obj) {
|
|
|
|
if (type === 'mousewheel') {
|
|
obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
|
|
|
|
} else if ((type === 'mouseenter') || (type === 'mouseleave')) {
|
|
handler = function (e) {
|
|
e = e || window.event;
|
|
if (L.DomEvent._isExternalTarget(obj, e)) {
|
|
originalHandler(e);
|
|
}
|
|
};
|
|
obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
|
|
|
|
} else {
|
|
if (type === 'click' && L.Browser.android) {
|
|
handler = function (e) {
|
|
return L.DomEvent._filterClick(e, originalHandler);
|
|
};
|
|
}
|
|
obj.addEventListener(type, handler, false);
|
|
}
|
|
|
|
} else if ('attachEvent' in obj) {
|
|
obj.attachEvent('on' + type, handler);
|
|
}
|
|
|
|
obj[eventsKey] = obj[eventsKey] || {};
|
|
obj[eventsKey][id] = handler;
|
|
|
|
return this;
|
|
},
|
|
|
|
_off: function (obj, type, fn, context) {
|
|
|
|
var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : ''),
|
|
handler = obj[eventsKey] && obj[eventsKey][id];
|
|
|
|
if (!handler) { return this; }
|
|
|
|
if (L.Browser.pointer && type.indexOf('touch') === 0) {
|
|
this.removePointerListener(obj, type, id);
|
|
|
|
} else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
|
|
this.removeDoubleTapListener(obj, id);
|
|
|
|
} else if ('removeEventListener' in obj) {
|
|
|
|
if (type === 'mousewheel') {
|
|
obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
|
|
|
|
} else {
|
|
obj.removeEventListener(
|
|
type === 'mouseenter' ? 'mouseover' :
|
|
type === 'mouseleave' ? 'mouseout' : type, handler, false);
|
|
}
|
|
|
|
} else if ('detachEvent' in obj) {
|
|
obj.detachEvent('on' + type, handler);
|
|
}
|
|
|
|
obj[eventsKey][id] = null;
|
|
|
|
return this;
|
|
},
|
|
|
|
// @function stopPropagation(ev: DOMEvent): this
|
|
// Stop the given event from propagation to parent elements. Used inside the listener functions:
|
|
// ```js
|
|
// L.DomEvent.on(div, 'click', function (ev) {
|
|
// L.DomEvent.stopPropagation(ev);
|
|
// });
|
|
// ```
|
|
stopPropagation: function (e) {
|
|
|
|
if (e.stopPropagation) {
|
|
e.stopPropagation();
|
|
} else if (e.originalEvent) { // In case of Leaflet event.
|
|
e.originalEvent._stopped = true;
|
|
} else {
|
|
e.cancelBubble = true;
|
|
}
|
|
L.DomEvent._skipped(e);
|
|
|
|
return this;
|
|
},
|
|
|
|
// @function disableScrollPropagation(el: HTMLElement): this
|
|
// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
|
|
disableScrollPropagation: function (el) {
|
|
return L.DomEvent.on(el, 'mousewheel', L.DomEvent.stopPropagation);
|
|
},
|
|
|
|
// @function disableClickPropagation(el: HTMLElement): this
|
|
// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
|
|
// `'mousedown'` and `'touchstart'` events (plus browser variants).
|
|
disableClickPropagation: function (el) {
|
|
var stop = L.DomEvent.stopPropagation;
|
|
|
|
L.DomEvent.on(el, L.Draggable.START.join(' '), stop);
|
|
|
|
return L.DomEvent.on(el, {
|
|
click: L.DomEvent._fakeStop,
|
|
dblclick: stop
|
|
});
|
|
},
|
|
|
|
// @function preventDefault(ev: DOMEvent): this
|
|
// Prevents the default action of the DOM Event `ev` from happening (such as
|
|
// following a link in the href of the a element, or doing a POST request
|
|
// with page reload when a `<form>` is submitted).
|
|
// Use it inside listener functions.
|
|
preventDefault: function (e) {
|
|
|
|
if (e.preventDefault) {
|
|
e.preventDefault();
|
|
} else {
|
|
e.returnValue = false;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @function stop(ev): this
|
|
// Does `stopPropagation` and `preventDefault` at the same time.
|
|
stop: function (e) {
|
|
return L.DomEvent
|
|
.preventDefault(e)
|
|
.stopPropagation(e);
|
|
},
|
|
|
|
// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
|
|
// Gets normalized mouse position from a DOM event relative to the
|
|
// `container` or to the whole page if not specified.
|
|
getMousePosition: function (e, container) {
|
|
if (!container) {
|
|
return new L.Point(e.clientX, e.clientY);
|
|
}
|
|
|
|
var rect = container.getBoundingClientRect();
|
|
|
|
return new L.Point(
|
|
e.clientX - rect.left - container.clientLeft,
|
|
e.clientY - rect.top - container.clientTop);
|
|
},
|
|
|
|
// Chrome on Win scrolls double the pixels as in other platforms (see #4538),
|
|
// and Firefox scrolls device pixels, not CSS pixels
|
|
_wheelPxFactor: (L.Browser.win && L.Browser.chrome) ? 2 :
|
|
L.Browser.gecko ? window.devicePixelRatio :
|
|
1,
|
|
|
|
// @function getWheelDelta(ev: DOMEvent): Number
|
|
// Gets normalized wheel delta from a mousewheel DOM event, in vertical
|
|
// pixels scrolled (negative if scrolling down).
|
|
// Events from pointing devices without precise scrolling are mapped to
|
|
// a best guess of 60 pixels.
|
|
getWheelDelta: function (e) {
|
|
return (L.Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
|
|
(e.deltaY && e.deltaMode === 0) ? -e.deltaY / L.DomEvent._wheelPxFactor : // Pixels
|
|
(e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
|
|
(e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
|
|
(e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events
|
|
e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
|
|
(e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
|
|
e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
|
|
0;
|
|
},
|
|
|
|
_skipEvents: {},
|
|
|
|
_fakeStop: function (e) {
|
|
// fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e)
|
|
L.DomEvent._skipEvents[e.type] = true;
|
|
},
|
|
|
|
_skipped: function (e) {
|
|
var skipped = this._skipEvents[e.type];
|
|
// reset when checking, as it's only used in map container and propagates outside of the map
|
|
this._skipEvents[e.type] = false;
|
|
return skipped;
|
|
},
|
|
|
|
// check if element really left/entered the event target (for mouseenter/mouseleave)
|
|
_isExternalTarget: function (el, e) {
|
|
|
|
var related = e.relatedTarget;
|
|
|
|
if (!related) { return true; }
|
|
|
|
try {
|
|
while (related && (related !== el)) {
|
|
related = related.parentNode;
|
|
}
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
return (related !== el);
|
|
},
|
|
|
|
// this is a horrible workaround for a bug in Android where a single touch triggers two click events
|
|
_filterClick: function (e, handler) {
|
|
var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
|
|
elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
|
|
|
|
// are they closer together than 500ms yet more than 100ms?
|
|
// Android typically triggers them ~300ms apart while multiple listeners
|
|
// on the same event should be triggered far faster;
|
|
// or check if click is simulated on the element, and if it is, reject any non-simulated events
|
|
|
|
if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
|
|
L.DomEvent.stop(e);
|
|
return;
|
|
}
|
|
L.DomEvent._lastClick = timeStamp;
|
|
|
|
handler(e);
|
|
}
|
|
};
|
|
|
|
// @function addListener(…): this
|
|
// Alias to [`L.DomEvent.on`](#domevent-on)
|
|
L.DomEvent.addListener = L.DomEvent.on;
|
|
|
|
// @function removeListener(…): this
|
|
// Alias to [`L.DomEvent.off`](#domevent-off)
|
|
L.DomEvent.removeListener = L.DomEvent.off;
|
|
|
|
|
|
|
|
/*
|
|
* @class PosAnimation
|
|
* @aka L.PosAnimation
|
|
* @inherits Evented
|
|
* Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
|
|
*
|
|
* @example
|
|
* ```js
|
|
* var fx = new L.PosAnimation();
|
|
* fx.run(el, [300, 500], 0.5);
|
|
* ```
|
|
*
|
|
* @constructor L.PosAnimation()
|
|
* Creates a `PosAnimation` object.
|
|
*
|
|
*/
|
|
|
|
L.PosAnimation = L.Evented.extend({
|
|
|
|
// @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
|
|
// Run an animation of a given element to a new position, optionally setting
|
|
// duration in seconds (`0.25` by default) and easing linearity factor (3rd
|
|
// argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
|
|
// `0.5` by default).
|
|
run: function (el, newPos, duration, easeLinearity) {
|
|
this.stop();
|
|
|
|
this._el = el;
|
|
this._inProgress = true;
|
|
this._duration = duration || 0.25;
|
|
this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
|
|
|
|
this._startPos = L.DomUtil.getPosition(el);
|
|
this._offset = newPos.subtract(this._startPos);
|
|
this._startTime = +new Date();
|
|
|
|
// @event start: Event
|
|
// Fired when the animation starts
|
|
this.fire('start');
|
|
|
|
this._animate();
|
|
},
|
|
|
|
// @method stop()
|
|
// Stops the animation (if currently running).
|
|
stop: function () {
|
|
if (!this._inProgress) { return; }
|
|
|
|
this._step(true);
|
|
this._complete();
|
|
},
|
|
|
|
_animate: function () {
|
|
// animation loop
|
|
this._animId = L.Util.requestAnimFrame(this._animate, this);
|
|
this._step();
|
|
},
|
|
|
|
_step: function (round) {
|
|
var elapsed = (+new Date()) - this._startTime,
|
|
duration = this._duration * 1000;
|
|
|
|
if (elapsed < duration) {
|
|
this._runFrame(this._easeOut(elapsed / duration), round);
|
|
} else {
|
|
this._runFrame(1);
|
|
this._complete();
|
|
}
|
|
},
|
|
|
|
_runFrame: function (progress, round) {
|
|
var pos = this._startPos.add(this._offset.multiplyBy(progress));
|
|
if (round) {
|
|
pos._round();
|
|
}
|
|
L.DomUtil.setPosition(this._el, pos);
|
|
|
|
// @event step: Event
|
|
// Fired continuously during the animation.
|
|
this.fire('step');
|
|
},
|
|
|
|
_complete: function () {
|
|
L.Util.cancelAnimFrame(this._animId);
|
|
|
|
this._inProgress = false;
|
|
// @event end: Event
|
|
// Fired when the animation ends.
|
|
this.fire('end');
|
|
},
|
|
|
|
_easeOut: function (t) {
|
|
return 1 - Math.pow(1 - t, this._easeOutPower);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @namespace Projection
|
|
* @projection L.Projection.Mercator
|
|
*
|
|
* Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS.
|
|
*/
|
|
|
|
L.Projection.Mercator = {
|
|
R: 6378137,
|
|
R_MINOR: 6356752.314245179,
|
|
|
|
bounds: L.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
|
|
|
|
project: function (latlng) {
|
|
var d = Math.PI / 180,
|
|
r = this.R,
|
|
y = latlng.lat * d,
|
|
tmp = this.R_MINOR / r,
|
|
e = Math.sqrt(1 - tmp * tmp),
|
|
con = e * Math.sin(y);
|
|
|
|
var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
|
|
y = -r * Math.log(Math.max(ts, 1E-10));
|
|
|
|
return new L.Point(latlng.lng * d * r, y);
|
|
},
|
|
|
|
unproject: function (point) {
|
|
var d = 180 / Math.PI,
|
|
r = this.R,
|
|
tmp = this.R_MINOR / r,
|
|
e = Math.sqrt(1 - tmp * tmp),
|
|
ts = Math.exp(-point.y / r),
|
|
phi = Math.PI / 2 - 2 * Math.atan(ts);
|
|
|
|
for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
|
|
con = e * Math.sin(phi);
|
|
con = Math.pow((1 - con) / (1 + con), e / 2);
|
|
dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
|
|
phi += dphi;
|
|
}
|
|
|
|
return new L.LatLng(phi * d, point.x * d / r);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @namespace CRS
|
|
* @crs L.CRS.EPSG3395
|
|
*
|
|
* Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
|
|
*/
|
|
|
|
L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
|
|
code: 'EPSG:3395',
|
|
projection: L.Projection.Mercator,
|
|
|
|
transformation: (function () {
|
|
var scale = 0.5 / (Math.PI * L.Projection.Mercator.R);
|
|
return new L.Transformation(scale, 0.5, -scale, 0.5);
|
|
}())
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @class GridLayer
|
|
* @inherits Layer
|
|
* @aka L.GridLayer
|
|
*
|
|
* Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
|
|
* GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
|
|
*
|
|
*
|
|
* @section Synchronous usage
|
|
* @example
|
|
*
|
|
* To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
|
|
*
|
|
* ```js
|
|
* var CanvasLayer = L.GridLayer.extend({
|
|
* createTile: function(coords){
|
|
* // create a <canvas> element for drawing
|
|
* var tile = L.DomUtil.create('canvas', 'leaflet-tile');
|
|
*
|
|
* // setup tile width and height according to the options
|
|
* var size = this.getTileSize();
|
|
* tile.width = size.x;
|
|
* tile.height = size.y;
|
|
*
|
|
* // get a canvas context and draw something on it using coords.x, coords.y and coords.z
|
|
* var ctx = tile.getContext('2d');
|
|
*
|
|
* // return the tile so it can be rendered on screen
|
|
* return tile;
|
|
* }
|
|
* });
|
|
* ```
|
|
*
|
|
* @section Asynchronous usage
|
|
* @example
|
|
*
|
|
* Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
|
|
*
|
|
* ```js
|
|
* var CanvasLayer = L.GridLayer.extend({
|
|
* createTile: function(coords, done){
|
|
* var error;
|
|
*
|
|
* // create a <canvas> element for drawing
|
|
* var tile = L.DomUtil.create('canvas', 'leaflet-tile');
|
|
*
|
|
* // setup tile width and height according to the options
|
|
* var size = this.getTileSize();
|
|
* tile.width = size.x;
|
|
* tile.height = size.y;
|
|
*
|
|
* // draw something asynchronously and pass the tile to the done() callback
|
|
* setTimeout(function() {
|
|
* done(error, tile);
|
|
* }, 1000);
|
|
*
|
|
* return tile;
|
|
* }
|
|
* });
|
|
* ```
|
|
*
|
|
* @section
|
|
*/
|
|
|
|
|
|
L.GridLayer = L.Layer.extend({
|
|
|
|
// @section
|
|
// @aka GridLayer options
|
|
options: {
|
|
// @option tileSize: Number|Point = 256
|
|
// Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
|
|
tileSize: 256,
|
|
|
|
// @option opacity: Number = 1.0
|
|
// Opacity of the tiles. Can be used in the `createTile()` function.
|
|
opacity: 1,
|
|
|
|
// @option updateWhenIdle: Boolean = depends
|
|
// If `false`, new tiles are loaded during panning, otherwise only after it (for better performance). `true` by default on mobile browsers, otherwise `false`.
|
|
updateWhenIdle: L.Browser.mobile,
|
|
|
|
// @option updateWhenZooming: Boolean = true
|
|
// By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends.
|
|
updateWhenZooming: true,
|
|
|
|
// @option updateInterval: Number = 200
|
|
// Tiles will not update more than once every `updateInterval` milliseconds when panning.
|
|
updateInterval: 200,
|
|
|
|
// @option zIndex: Number = 1
|
|
// The explicit zIndex of the tile layer.
|
|
zIndex: 1,
|
|
|
|
// @option bounds: LatLngBounds = undefined
|
|
// If set, tiles will only be loaded inside the set `LatLngBounds`.
|
|
bounds: null,
|
|
|
|
// @option minZoom: Number = 0
|
|
// The minimum zoom level that tiles will be loaded at. By default the entire map.
|
|
minZoom: 0,
|
|
|
|
// @option maxZoom: Number = undefined
|
|
// The maximum zoom level that tiles will be loaded at.
|
|
maxZoom: undefined,
|
|
|
|
// @option noWrap: Boolean = false
|
|
// Whether the layer is wrapped around the antimeridian. If `true`, the
|
|
// GridLayer will only be displayed once at low zoom levels. Has no
|
|
// effect when the [map CRS](#map-crs) doesn't wrap around.
|
|
noWrap: false,
|
|
|
|
// @option pane: String = 'tilePane'
|
|
// `Map pane` where the grid layer will be added.
|
|
pane: 'tilePane',
|
|
|
|
// @option className: String = ''
|
|
// A custom class name to assign to the tile layer. Empty by default.
|
|
className: '',
|
|
|
|
// @option keepBuffer: Number = 2
|
|
// When panning the map, keep this many rows and columns of tiles before unloading them.
|
|
keepBuffer: 2
|
|
},
|
|
|
|
initialize: function (options) {
|
|
L.setOptions(this, options);
|
|
},
|
|
|
|
onAdd: function () {
|
|
this._initContainer();
|
|
|
|
this._levels = {};
|
|
this._tiles = {};
|
|
|
|
this._resetView();
|
|
this._update();
|
|
},
|
|
|
|
beforeAdd: function (map) {
|
|
map._addZoomLimit(this);
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
this._removeAllTiles();
|
|
L.DomUtil.remove(this._container);
|
|
map._removeZoomLimit(this);
|
|
this._container = null;
|
|
this._tileZoom = null;
|
|
},
|
|
|
|
// @method bringToFront: this
|
|
// Brings the tile layer to the top of all tile layers.
|
|
bringToFront: function () {
|
|
if (this._map) {
|
|
L.DomUtil.toFront(this._container);
|
|
this._setAutoZIndex(Math.max);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method bringToBack: this
|
|
// Brings the tile layer to the bottom of all tile layers.
|
|
bringToBack: function () {
|
|
if (this._map) {
|
|
L.DomUtil.toBack(this._container);
|
|
this._setAutoZIndex(Math.min);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method getContainer: HTMLElement
|
|
// Returns the HTML element that contains the tiles for this layer.
|
|
getContainer: function () {
|
|
return this._container;
|
|
},
|
|
|
|
// @method setOpacity(opacity: Number): this
|
|
// Changes the [opacity](#gridlayer-opacity) of the grid layer.
|
|
setOpacity: function (opacity) {
|
|
this.options.opacity = opacity;
|
|
this._updateOpacity();
|
|
return this;
|
|
},
|
|
|
|
// @method setZIndex(zIndex: Number): this
|
|
// Changes the [zIndex](#gridlayer-zindex) of the grid layer.
|
|
setZIndex: function (zIndex) {
|
|
this.options.zIndex = zIndex;
|
|
this._updateZIndex();
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method isLoading: Boolean
|
|
// Returns `true` if any tile in the grid layer has not finished loading.
|
|
isLoading: function () {
|
|
return this._loading;
|
|
},
|
|
|
|
// @method redraw: this
|
|
// Causes the layer to clear all the tiles and request them again.
|
|
redraw: function () {
|
|
if (this._map) {
|
|
this._removeAllTiles();
|
|
this._update();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
getEvents: function () {
|
|
var events = {
|
|
viewprereset: this._invalidateAll,
|
|
viewreset: this._resetView,
|
|
zoom: this._resetView,
|
|
moveend: this._onMoveEnd
|
|
};
|
|
|
|
if (!this.options.updateWhenIdle) {
|
|
// update tiles on move, but not more often than once per given interval
|
|
if (!this._onMove) {
|
|
this._onMove = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
|
|
}
|
|
|
|
events.move = this._onMove;
|
|
}
|
|
|
|
if (this._zoomAnimated) {
|
|
events.zoomanim = this._animateZoom;
|
|
}
|
|
|
|
return events;
|
|
},
|
|
|
|
// @section Extension methods
|
|
// Layers extending `GridLayer` shall reimplement the following method.
|
|
// @method createTile(coords: Object, done?: Function): HTMLElement
|
|
// Called only internally, must be overriden by classes extending `GridLayer`.
|
|
// Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
|
|
// is specified, it must be called when the tile has finished loading and drawing.
|
|
createTile: function () {
|
|
return document.createElement('div');
|
|
},
|
|
|
|
// @section
|
|
// @method getTileSize: Point
|
|
// Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
|
|
getTileSize: function () {
|
|
var s = this.options.tileSize;
|
|
return s instanceof L.Point ? s : new L.Point(s, s);
|
|
},
|
|
|
|
_updateZIndex: function () {
|
|
if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
|
|
this._container.style.zIndex = this.options.zIndex;
|
|
}
|
|
},
|
|
|
|
_setAutoZIndex: function (compare) {
|
|
// go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
|
|
|
|
var layers = this.getPane().children,
|
|
edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
|
|
|
|
for (var i = 0, len = layers.length, zIndex; i < len; i++) {
|
|
|
|
zIndex = layers[i].style.zIndex;
|
|
|
|
if (layers[i] !== this._container && zIndex) {
|
|
edgeZIndex = compare(edgeZIndex, +zIndex);
|
|
}
|
|
}
|
|
|
|
if (isFinite(edgeZIndex)) {
|
|
this.options.zIndex = edgeZIndex + compare(-1, 1);
|
|
this._updateZIndex();
|
|
}
|
|
},
|
|
|
|
_updateOpacity: function () {
|
|
if (!this._map) { return; }
|
|
|
|
// IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
|
|
if (L.Browser.ielt9) { return; }
|
|
|
|
L.DomUtil.setOpacity(this._container, this.options.opacity);
|
|
|
|
var now = +new Date(),
|
|
nextFrame = false,
|
|
willPrune = false;
|
|
|
|
for (var key in this._tiles) {
|
|
var tile = this._tiles[key];
|
|
if (!tile.current || !tile.loaded) { continue; }
|
|
|
|
var fade = Math.min(1, (now - tile.loaded) / 200);
|
|
|
|
L.DomUtil.setOpacity(tile.el, fade);
|
|
if (fade < 1) {
|
|
nextFrame = true;
|
|
} else {
|
|
if (tile.active) { willPrune = true; }
|
|
tile.active = true;
|
|
}
|
|
}
|
|
|
|
if (willPrune && !this._noPrune) { this._pruneTiles(); }
|
|
|
|
if (nextFrame) {
|
|
L.Util.cancelAnimFrame(this._fadeFrame);
|
|
this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
|
|
}
|
|
},
|
|
|
|
_initContainer: function () {
|
|
if (this._container) { return; }
|
|
|
|
this._container = L.DomUtil.create('div', 'leaflet-layer ' + (this.options.className || ''));
|
|
this._updateZIndex();
|
|
|
|
if (this.options.opacity < 1) {
|
|
this._updateOpacity();
|
|
}
|
|
|
|
this.getPane().appendChild(this._container);
|
|
},
|
|
|
|
_updateLevels: function () {
|
|
|
|
var zoom = this._tileZoom,
|
|
maxZoom = this.options.maxZoom;
|
|
|
|
if (zoom === undefined) { return undefined; }
|
|
|
|
for (var z in this._levels) {
|
|
if (this._levels[z].el.children.length || z === zoom) {
|
|
this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
|
|
} else {
|
|
L.DomUtil.remove(this._levels[z].el);
|
|
this._removeTilesAtZoom(z);
|
|
delete this._levels[z];
|
|
}
|
|
}
|
|
|
|
var level = this._levels[zoom],
|
|
map = this._map;
|
|
|
|
if (!level) {
|
|
level = this._levels[zoom] = {};
|
|
|
|
level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
|
|
level.el.style.zIndex = maxZoom;
|
|
|
|
level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
|
|
level.zoom = zoom;
|
|
|
|
this._setZoomTransform(level, map.getCenter(), map.getZoom());
|
|
|
|
// force the browser to consider the newly added element for transition
|
|
L.Util.falseFn(level.el.offsetWidth);
|
|
}
|
|
|
|
this._level = level;
|
|
|
|
return level;
|
|
},
|
|
|
|
_pruneTiles: function () {
|
|
if (!this._map) {
|
|
return;
|
|
}
|
|
|
|
var key, tile;
|
|
|
|
var zoom = this._map.getZoom();
|
|
if (zoom > this.options.maxZoom ||
|
|
zoom < this.options.minZoom) {
|
|
this._removeAllTiles();
|
|
return;
|
|
}
|
|
|
|
for (key in this._tiles) {
|
|
tile = this._tiles[key];
|
|
tile.retain = tile.current;
|
|
}
|
|
|
|
for (key in this._tiles) {
|
|
tile = this._tiles[key];
|
|
if (tile.current && !tile.active) {
|
|
var coords = tile.coords;
|
|
if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
|
|
this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (key in this._tiles) {
|
|
if (!this._tiles[key].retain) {
|
|
this._removeTile(key);
|
|
}
|
|
}
|
|
},
|
|
|
|
_removeTilesAtZoom: function (zoom) {
|
|
for (var key in this._tiles) {
|
|
if (this._tiles[key].coords.z !== zoom) {
|
|
continue;
|
|
}
|
|
this._removeTile(key);
|
|
}
|
|
},
|
|
|
|
_removeAllTiles: function () {
|
|
for (var key in this._tiles) {
|
|
this._removeTile(key);
|
|
}
|
|
},
|
|
|
|
_invalidateAll: function () {
|
|
for (var z in this._levels) {
|
|
L.DomUtil.remove(this._levels[z].el);
|
|
delete this._levels[z];
|
|
}
|
|
this._removeAllTiles();
|
|
|
|
this._tileZoom = null;
|
|
},
|
|
|
|
_retainParent: function (x, y, z, minZoom) {
|
|
var x2 = Math.floor(x / 2),
|
|
y2 = Math.floor(y / 2),
|
|
z2 = z - 1,
|
|
coords2 = new L.Point(+x2, +y2);
|
|
coords2.z = +z2;
|
|
|
|
var key = this._tileCoordsToKey(coords2),
|
|
tile = this._tiles[key];
|
|
|
|
if (tile && tile.active) {
|
|
tile.retain = true;
|
|
return true;
|
|
|
|
} else if (tile && tile.loaded) {
|
|
tile.retain = true;
|
|
}
|
|
|
|
if (z2 > minZoom) {
|
|
return this._retainParent(x2, y2, z2, minZoom);
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
_retainChildren: function (x, y, z, maxZoom) {
|
|
|
|
for (var i = 2 * x; i < 2 * x + 2; i++) {
|
|
for (var j = 2 * y; j < 2 * y + 2; j++) {
|
|
|
|
var coords = new L.Point(i, j);
|
|
coords.z = z + 1;
|
|
|
|
var key = this._tileCoordsToKey(coords),
|
|
tile = this._tiles[key];
|
|
|
|
if (tile && tile.active) {
|
|
tile.retain = true;
|
|
continue;
|
|
|
|
} else if (tile && tile.loaded) {
|
|
tile.retain = true;
|
|
}
|
|
|
|
if (z + 1 < maxZoom) {
|
|
this._retainChildren(i, j, z + 1, maxZoom);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
_resetView: function (e) {
|
|
var animating = e && (e.pinch || e.flyTo);
|
|
this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
|
|
},
|
|
|
|
_animateZoom: function (e) {
|
|
this._setView(e.center, e.zoom, true, e.noUpdate);
|
|
},
|
|
|
|
_setView: function (center, zoom, noPrune, noUpdate) {
|
|
var tileZoom = Math.round(zoom);
|
|
if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
|
|
(this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
|
|
tileZoom = undefined;
|
|
}
|
|
|
|
var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
|
|
|
|
if (!noUpdate || tileZoomChanged) {
|
|
|
|
this._tileZoom = tileZoom;
|
|
|
|
if (this._abortLoading) {
|
|
this._abortLoading();
|
|
}
|
|
|
|
this._updateLevels();
|
|
this._resetGrid();
|
|
|
|
if (tileZoom !== undefined) {
|
|
this._update(center);
|
|
}
|
|
|
|
if (!noPrune) {
|
|
this._pruneTiles();
|
|
}
|
|
|
|
// Flag to prevent _updateOpacity from pruning tiles during
|
|
// a zoom anim or a pinch gesture
|
|
this._noPrune = !!noPrune;
|
|
}
|
|
|
|
this._setZoomTransforms(center, zoom);
|
|
},
|
|
|
|
_setZoomTransforms: function (center, zoom) {
|
|
for (var i in this._levels) {
|
|
this._setZoomTransform(this._levels[i], center, zoom);
|
|
}
|
|
},
|
|
|
|
_setZoomTransform: function (level, center, zoom) {
|
|
var scale = this._map.getZoomScale(zoom, level.zoom),
|
|
translate = level.origin.multiplyBy(scale)
|
|
.subtract(this._map._getNewPixelOrigin(center, zoom)).round();
|
|
|
|
if (L.Browser.any3d) {
|
|
L.DomUtil.setTransform(level.el, translate, scale);
|
|
} else {
|
|
L.DomUtil.setPosition(level.el, translate);
|
|
}
|
|
},
|
|
|
|
_resetGrid: function () {
|
|
var map = this._map,
|
|
crs = map.options.crs,
|
|
tileSize = this._tileSize = this.getTileSize(),
|
|
tileZoom = this._tileZoom;
|
|
|
|
var bounds = this._map.getPixelWorldBounds(this._tileZoom);
|
|
if (bounds) {
|
|
this._globalTileRange = this._pxBoundsToTileRange(bounds);
|
|
}
|
|
|
|
this._wrapX = crs.wrapLng && !this.options.noWrap && [
|
|
Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
|
|
Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
|
|
];
|
|
this._wrapY = crs.wrapLat && !this.options.noWrap && [
|
|
Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
|
|
Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
|
|
];
|
|
},
|
|
|
|
_onMoveEnd: function () {
|
|
if (!this._map || this._map._animatingZoom) { return; }
|
|
|
|
this._update();
|
|
},
|
|
|
|
_getTiledPixelBounds: function (center) {
|
|
var map = this._map,
|
|
mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
|
|
scale = map.getZoomScale(mapZoom, this._tileZoom),
|
|
pixelCenter = map.project(center, this._tileZoom).floor(),
|
|
halfSize = map.getSize().divideBy(scale * 2);
|
|
|
|
return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
|
|
},
|
|
|
|
// Private method to load tiles in the grid's active zoom level according to map bounds
|
|
_update: function (center) {
|
|
var map = this._map;
|
|
if (!map) { return; }
|
|
var zoom = map.getZoom();
|
|
|
|
if (center === undefined) { center = map.getCenter(); }
|
|
if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom
|
|
|
|
var pixelBounds = this._getTiledPixelBounds(center),
|
|
tileRange = this._pxBoundsToTileRange(pixelBounds),
|
|
tileCenter = tileRange.getCenter(),
|
|
queue = [],
|
|
margin = this.options.keepBuffer,
|
|
noPruneRange = new L.Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
|
|
tileRange.getTopRight().add([margin, -margin]));
|
|
|
|
for (var key in this._tiles) {
|
|
var c = this._tiles[key].coords;
|
|
if (c.z !== this._tileZoom || !noPruneRange.contains(L.point(c.x, c.y))) {
|
|
this._tiles[key].current = false;
|
|
}
|
|
}
|
|
|
|
// _update just loads more tiles. If the tile zoom level differs too much
|
|
// from the map's, let _setView reset levels and prune old tiles.
|
|
if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
|
|
|
|
// create a queue of coordinates to load tiles from
|
|
for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
|
|
for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
|
|
var coords = new L.Point(i, j);
|
|
coords.z = this._tileZoom;
|
|
|
|
if (!this._isValidTile(coords)) { continue; }
|
|
|
|
var tile = this._tiles[this._tileCoordsToKey(coords)];
|
|
if (tile) {
|
|
tile.current = true;
|
|
} else {
|
|
queue.push(coords);
|
|
}
|
|
}
|
|
}
|
|
|
|
// sort tile queue to load tiles in order of their distance to center
|
|
queue.sort(function (a, b) {
|
|
return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
|
|
});
|
|
|
|
if (queue.length !== 0) {
|
|
// if it's the first batch of tiles to load
|
|
if (!this._loading) {
|
|
this._loading = true;
|
|
// @event loading: Event
|
|
// Fired when the grid layer starts loading tiles.
|
|
this.fire('loading');
|
|
}
|
|
|
|
// create DOM fragment to append tiles in one batch
|
|
var fragment = document.createDocumentFragment();
|
|
|
|
for (i = 0; i < queue.length; i++) {
|
|
this._addTile(queue[i], fragment);
|
|
}
|
|
|
|
this._level.el.appendChild(fragment);
|
|
}
|
|
},
|
|
|
|
_isValidTile: function (coords) {
|
|
var crs = this._map.options.crs;
|
|
|
|
if (!crs.infinite) {
|
|
// don't load tile if it's out of bounds and not wrapped
|
|
var bounds = this._globalTileRange;
|
|
if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
|
|
(!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
|
|
}
|
|
|
|
if (!this.options.bounds) { return true; }
|
|
|
|
// don't load tile if it doesn't intersect the bounds in options
|
|
var tileBounds = this._tileCoordsToBounds(coords);
|
|
return L.latLngBounds(this.options.bounds).overlaps(tileBounds);
|
|
},
|
|
|
|
_keyToBounds: function (key) {
|
|
return this._tileCoordsToBounds(this._keyToTileCoords(key));
|
|
},
|
|
|
|
// converts tile coordinates to its geographical bounds
|
|
_tileCoordsToBounds: function (coords) {
|
|
|
|
var map = this._map,
|
|
tileSize = this.getTileSize(),
|
|
|
|
nwPoint = coords.scaleBy(tileSize),
|
|
sePoint = nwPoint.add(tileSize),
|
|
|
|
nw = map.unproject(nwPoint, coords.z),
|
|
se = map.unproject(sePoint, coords.z);
|
|
|
|
if (!this.options.noWrap) {
|
|
nw = map.wrapLatLng(nw);
|
|
se = map.wrapLatLng(se);
|
|
}
|
|
|
|
return new L.LatLngBounds(nw, se);
|
|
},
|
|
|
|
// converts tile coordinates to key for the tile cache
|
|
_tileCoordsToKey: function (coords) {
|
|
return coords.x + ':' + coords.y + ':' + coords.z;
|
|
},
|
|
|
|
// converts tile cache key to coordinates
|
|
_keyToTileCoords: function (key) {
|
|
var k = key.split(':'),
|
|
coords = new L.Point(+k[0], +k[1]);
|
|
coords.z = +k[2];
|
|
return coords;
|
|
},
|
|
|
|
_removeTile: function (key) {
|
|
var tile = this._tiles[key];
|
|
if (!tile) { return; }
|
|
|
|
L.DomUtil.remove(tile.el);
|
|
|
|
delete this._tiles[key];
|
|
|
|
// @event tileunload: TileEvent
|
|
// Fired when a tile is removed (e.g. when a tile goes off the screen).
|
|
this.fire('tileunload', {
|
|
tile: tile.el,
|
|
coords: this._keyToTileCoords(key)
|
|
});
|
|
},
|
|
|
|
_initTile: function (tile) {
|
|
L.DomUtil.addClass(tile, 'leaflet-tile');
|
|
|
|
var tileSize = this.getTileSize();
|
|
tile.style.width = tileSize.x + 'px';
|
|
tile.style.height = tileSize.y + 'px';
|
|
|
|
tile.onselectstart = L.Util.falseFn;
|
|
tile.onmousemove = L.Util.falseFn;
|
|
|
|
// update opacity on tiles in IE7-8 because of filter inheritance problems
|
|
if (L.Browser.ielt9 && this.options.opacity < 1) {
|
|
L.DomUtil.setOpacity(tile, this.options.opacity);
|
|
}
|
|
|
|
// without this hack, tiles disappear after zoom on Chrome for Android
|
|
// https://github.com/Leaflet/Leaflet/issues/2078
|
|
if (L.Browser.android && !L.Browser.android23) {
|
|
tile.style.WebkitBackfaceVisibility = 'hidden';
|
|
}
|
|
},
|
|
|
|
_addTile: function (coords, container) {
|
|
var tilePos = this._getTilePos(coords),
|
|
key = this._tileCoordsToKey(coords);
|
|
|
|
var tile = this.createTile(this._wrapCoords(coords), L.bind(this._tileReady, this, coords));
|
|
|
|
this._initTile(tile);
|
|
|
|
// if createTile is defined with a second argument ("done" callback),
|
|
// we know that tile is async and will be ready later; otherwise
|
|
if (this.createTile.length < 2) {
|
|
// mark tile as ready, but delay one frame for opacity animation to happen
|
|
L.Util.requestAnimFrame(L.bind(this._tileReady, this, coords, null, tile));
|
|
}
|
|
|
|
L.DomUtil.setPosition(tile, tilePos);
|
|
|
|
// save tile in cache
|
|
this._tiles[key] = {
|
|
el: tile,
|
|
coords: coords,
|
|
current: true
|
|
};
|
|
|
|
container.appendChild(tile);
|
|
// @event tileloadstart: TileEvent
|
|
// Fired when a tile is requested and starts loading.
|
|
this.fire('tileloadstart', {
|
|
tile: tile,
|
|
coords: coords
|
|
});
|
|
},
|
|
|
|
_tileReady: function (coords, err, tile) {
|
|
if (!this._map) { return; }
|
|
|
|
if (err) {
|
|
// @event tileerror: TileErrorEvent
|
|
// Fired when there is an error loading a tile.
|
|
this.fire('tileerror', {
|
|
error: err,
|
|
tile: tile,
|
|
coords: coords
|
|
});
|
|
}
|
|
|
|
var key = this._tileCoordsToKey(coords);
|
|
|
|
tile = this._tiles[key];
|
|
if (!tile) { return; }
|
|
|
|
tile.loaded = +new Date();
|
|
if (this._map._fadeAnimated) {
|
|
L.DomUtil.setOpacity(tile.el, 0);
|
|
L.Util.cancelAnimFrame(this._fadeFrame);
|
|
this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
|
|
} else {
|
|
tile.active = true;
|
|
this._pruneTiles();
|
|
}
|
|
|
|
if (!err) {
|
|
L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded');
|
|
|
|
// @event tileload: TileEvent
|
|
// Fired when a tile loads.
|
|
this.fire('tileload', {
|
|
tile: tile.el,
|
|
coords: coords
|
|
});
|
|
}
|
|
|
|
if (this._noTilesToLoad()) {
|
|
this._loading = false;
|
|
// @event load: Event
|
|
// Fired when the grid layer loaded all visible tiles.
|
|
this.fire('load');
|
|
|
|
if (L.Browser.ielt9 || !this._map._fadeAnimated) {
|
|
L.Util.requestAnimFrame(this._pruneTiles, this);
|
|
} else {
|
|
// Wait a bit more than 0.2 secs (the duration of the tile fade-in)
|
|
// to trigger a pruning.
|
|
setTimeout(L.bind(this._pruneTiles, this), 250);
|
|
}
|
|
}
|
|
},
|
|
|
|
_getTilePos: function (coords) {
|
|
return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
|
|
},
|
|
|
|
_wrapCoords: function (coords) {
|
|
var newCoords = new L.Point(
|
|
this._wrapX ? L.Util.wrapNum(coords.x, this._wrapX) : coords.x,
|
|
this._wrapY ? L.Util.wrapNum(coords.y, this._wrapY) : coords.y);
|
|
newCoords.z = coords.z;
|
|
return newCoords;
|
|
},
|
|
|
|
_pxBoundsToTileRange: function (bounds) {
|
|
var tileSize = this.getTileSize();
|
|
return new L.Bounds(
|
|
bounds.min.unscaleBy(tileSize).floor(),
|
|
bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
|
|
},
|
|
|
|
_noTilesToLoad: function () {
|
|
for (var key in this._tiles) {
|
|
if (!this._tiles[key].loaded) { return false; }
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
|
|
// @factory L.gridLayer(options?: GridLayer options)
|
|
// Creates a new instance of GridLayer with the supplied options.
|
|
L.gridLayer = function (options) {
|
|
return new L.GridLayer(options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class TileLayer
|
|
* @inherits GridLayer
|
|
* @aka L.TileLayer
|
|
* Used to load and display tile layers on the map. Extends `GridLayer`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
|
|
* ```
|
|
*
|
|
* @section URL template
|
|
* @example
|
|
*
|
|
* A string of the following form:
|
|
*
|
|
* ```
|
|
* 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
|
|
* ```
|
|
*
|
|
* `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add @2x to the URL to load retina tiles.
|
|
*
|
|
* You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
|
|
*
|
|
* ```
|
|
* L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
|
|
* ```
|
|
*/
|
|
|
|
|
|
L.TileLayer = L.GridLayer.extend({
|
|
|
|
// @section
|
|
// @aka TileLayer options
|
|
options: {
|
|
// @option minZoom: Number = 0
|
|
// Minimum zoom number.
|
|
minZoom: 0,
|
|
|
|
// @option maxZoom: Number = 18
|
|
// Maximum zoom number.
|
|
maxZoom: 18,
|
|
|
|
// @option maxNativeZoom: Number = null
|
|
// Maximum zoom number the tile source has available. If it is specified,
|
|
// the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
|
|
// from `maxNativeZoom` level and auto-scaled.
|
|
maxNativeZoom: null,
|
|
|
|
// @option minNativeZoom: Number = null
|
|
// Minimum zoom number the tile source has available. If it is specified,
|
|
// the tiles on all zoom levels lower than `minNativeZoom` will be loaded
|
|
// from `minNativeZoom` level and auto-scaled.
|
|
minNativeZoom: null,
|
|
|
|
// @option subdomains: String|String[] = 'abc'
|
|
// Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.
|
|
subdomains: 'abc',
|
|
|
|
// @option errorTileUrl: String = ''
|
|
// URL to the tile image to show in place of the tile that failed to load.
|
|
errorTileUrl: '',
|
|
|
|
// @option zoomOffset: Number = 0
|
|
// The zoom number used in tile URLs will be offset with this value.
|
|
zoomOffset: 0,
|
|
|
|
// @option tms: Boolean = false
|
|
// If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
|
|
tms: false,
|
|
|
|
// @option zoomReverse: Boolean = false
|
|
// If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
|
|
zoomReverse: false,
|
|
|
|
// @option detectRetina: Boolean = false
|
|
// If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution.
|
|
detectRetina: false,
|
|
|
|
// @option crossOrigin: Boolean = false
|
|
// If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
|
|
crossOrigin: false
|
|
},
|
|
|
|
initialize: function (url, options) {
|
|
|
|
this._url = url;
|
|
|
|
options = L.setOptions(this, options);
|
|
|
|
// detecting retina displays, adjusting tileSize and zoom levels
|
|
if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
|
|
|
|
options.tileSize = Math.floor(options.tileSize / 2);
|
|
|
|
if (!options.zoomReverse) {
|
|
options.zoomOffset++;
|
|
options.maxZoom--;
|
|
} else {
|
|
options.zoomOffset--;
|
|
options.minZoom++;
|
|
}
|
|
|
|
options.minZoom = Math.max(0, options.minZoom);
|
|
}
|
|
|
|
if (typeof options.subdomains === 'string') {
|
|
options.subdomains = options.subdomains.split('');
|
|
}
|
|
|
|
// for https://github.com/Leaflet/Leaflet/issues/137
|
|
if (!L.Browser.android) {
|
|
this.on('tileunload', this._onTileRemove);
|
|
}
|
|
},
|
|
|
|
// @method setUrl(url: String, noRedraw?: Boolean): this
|
|
// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
|
|
setUrl: function (url, noRedraw) {
|
|
this._url = url;
|
|
|
|
if (!noRedraw) {
|
|
this.redraw();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method createTile(coords: Object, done?: Function): HTMLElement
|
|
// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
|
|
// to return an `<img>` HTML element with the appropiate image URL given `coords`. The `done`
|
|
// callback is called when the tile has been loaded.
|
|
createTile: function (coords, done) {
|
|
var tile = document.createElement('img');
|
|
|
|
L.DomEvent.on(tile, 'load', L.bind(this._tileOnLoad, this, done, tile));
|
|
L.DomEvent.on(tile, 'error', L.bind(this._tileOnError, this, done, tile));
|
|
|
|
if (this.options.crossOrigin) {
|
|
tile.crossOrigin = '';
|
|
}
|
|
|
|
/*
|
|
Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
|
|
http://www.w3.org/TR/WCAG20-TECHS/H67
|
|
*/
|
|
tile.alt = '';
|
|
|
|
/*
|
|
Set role="presentation" to force screen readers to ignore this
|
|
https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
|
|
*/
|
|
tile.setAttribute('role', 'presentation');
|
|
|
|
tile.src = this.getTileUrl(coords);
|
|
|
|
return tile;
|
|
},
|
|
|
|
// @section Extension methods
|
|
// @uninheritable
|
|
// Layers extending `TileLayer` might reimplement the following method.
|
|
// @method getTileUrl(coords: Object): String
|
|
// Called only internally, returns the URL for a tile given its coordinates.
|
|
// Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
|
|
getTileUrl: function (coords) {
|
|
var data = {
|
|
r: L.Browser.retina ? '@2x' : '',
|
|
s: this._getSubdomain(coords),
|
|
x: coords.x,
|
|
y: coords.y,
|
|
z: this._getZoomForUrl()
|
|
};
|
|
if (this._map && !this._map.options.crs.infinite) {
|
|
var invertedY = this._globalTileRange.max.y - coords.y;
|
|
if (this.options.tms) {
|
|
data['y'] = invertedY;
|
|
}
|
|
data['-y'] = invertedY;
|
|
}
|
|
|
|
return L.Util.template(this._url, L.extend(data, this.options));
|
|
},
|
|
|
|
_tileOnLoad: function (done, tile) {
|
|
// For https://github.com/Leaflet/Leaflet/issues/3332
|
|
if (L.Browser.ielt9) {
|
|
setTimeout(L.bind(done, this, null, tile), 0);
|
|
} else {
|
|
done(null, tile);
|
|
}
|
|
},
|
|
|
|
_tileOnError: function (done, tile, e) {
|
|
var errorUrl = this.options.errorTileUrl;
|
|
if (errorUrl) {
|
|
tile.src = errorUrl;
|
|
}
|
|
done(e, tile);
|
|
},
|
|
|
|
getTileSize: function () {
|
|
var map = this._map,
|
|
tileSize = L.GridLayer.prototype.getTileSize.call(this),
|
|
zoom = this._tileZoom + this.options.zoomOffset,
|
|
minNativeZoom = this.options.minNativeZoom,
|
|
maxNativeZoom = this.options.maxNativeZoom;
|
|
|
|
// decrease tile size when scaling below minNativeZoom
|
|
if (minNativeZoom !== null && zoom < minNativeZoom) {
|
|
return tileSize.divideBy(map.getZoomScale(minNativeZoom, zoom)).round();
|
|
}
|
|
|
|
// increase tile size when scaling above maxNativeZoom
|
|
if (maxNativeZoom !== null && zoom > maxNativeZoom) {
|
|
return tileSize.divideBy(map.getZoomScale(maxNativeZoom, zoom)).round();
|
|
}
|
|
|
|
return tileSize;
|
|
},
|
|
|
|
_onTileRemove: function (e) {
|
|
e.tile.onload = null;
|
|
},
|
|
|
|
_getZoomForUrl: function () {
|
|
var zoom = this._tileZoom,
|
|
maxZoom = this.options.maxZoom,
|
|
zoomReverse = this.options.zoomReverse,
|
|
zoomOffset = this.options.zoomOffset,
|
|
minNativeZoom = this.options.minNativeZoom,
|
|
maxNativeZoom = this.options.maxNativeZoom;
|
|
|
|
if (zoomReverse) {
|
|
zoom = maxZoom - zoom;
|
|
}
|
|
|
|
zoom += zoomOffset;
|
|
|
|
if (minNativeZoom !== null && zoom < minNativeZoom) {
|
|
return minNativeZoom;
|
|
}
|
|
|
|
if (maxNativeZoom !== null && zoom > maxNativeZoom) {
|
|
return maxNativeZoom;
|
|
}
|
|
|
|
return zoom;
|
|
},
|
|
|
|
_getSubdomain: function (tilePoint) {
|
|
var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
|
|
return this.options.subdomains[index];
|
|
},
|
|
|
|
// stops loading all tiles in the background layer
|
|
_abortLoading: function () {
|
|
var i, tile;
|
|
for (i in this._tiles) {
|
|
if (this._tiles[i].coords.z !== this._tileZoom) {
|
|
tile = this._tiles[i].el;
|
|
|
|
tile.onload = L.Util.falseFn;
|
|
tile.onerror = L.Util.falseFn;
|
|
|
|
if (!tile.complete) {
|
|
tile.src = L.Util.emptyImageUrl;
|
|
L.DomUtil.remove(tile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
|
|
// Instantiates a tile layer object given a `URL template` and optionally an options object.
|
|
|
|
L.tileLayer = function (url, options) {
|
|
return new L.TileLayer(url, options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class TileLayer.WMS
|
|
* @inherits TileLayer
|
|
* @aka L.TileLayer.WMS
|
|
* Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
|
|
* layers: 'nexrad-n0r-900913',
|
|
* format: 'image/png',
|
|
* transparent: true,
|
|
* attribution: "Weather data © 2012 IEM Nexrad"
|
|
* });
|
|
* ```
|
|
*/
|
|
|
|
L.TileLayer.WMS = L.TileLayer.extend({
|
|
|
|
// @section
|
|
// @aka TileLayer.WMS options
|
|
// If any custom options not documented here are used, they will be sent to the
|
|
// WMS server as extra parameters in each request URL. This can be useful for
|
|
// [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
|
|
defaultWmsParams: {
|
|
service: 'WMS',
|
|
request: 'GetMap',
|
|
|
|
// @option layers: String = ''
|
|
// **(required)** Comma-separated list of WMS layers to show.
|
|
layers: '',
|
|
|
|
// @option styles: String = ''
|
|
// Comma-separated list of WMS styles.
|
|
styles: '',
|
|
|
|
// @option format: String = 'image/jpeg'
|
|
// WMS image format (use `'image/png'` for layers with transparency).
|
|
format: 'image/jpeg',
|
|
|
|
// @option transparent: Boolean = false
|
|
// If `true`, the WMS service will return images with transparency.
|
|
transparent: false,
|
|
|
|
// @option version: String = '1.1.1'
|
|
// Version of the WMS service to use
|
|
version: '1.1.1'
|
|
},
|
|
|
|
options: {
|
|
// @option crs: CRS = null
|
|
// Coordinate Reference System to use for the WMS requests, defaults to
|
|
// map CRS. Don't change this if you're not sure what it means.
|
|
crs: null,
|
|
|
|
// @option uppercase: Boolean = false
|
|
// If `true`, WMS request parameter keys will be uppercase.
|
|
uppercase: false
|
|
},
|
|
|
|
initialize: function (url, options) {
|
|
|
|
this._url = url;
|
|
|
|
var wmsParams = L.extend({}, this.defaultWmsParams);
|
|
|
|
// all keys that are not TileLayer options go to WMS params
|
|
for (var i in options) {
|
|
if (!(i in this.options)) {
|
|
wmsParams[i] = options[i];
|
|
}
|
|
}
|
|
|
|
options = L.setOptions(this, options);
|
|
|
|
wmsParams.width = wmsParams.height = options.tileSize * (options.detectRetina && L.Browser.retina ? 2 : 1);
|
|
|
|
this.wmsParams = wmsParams;
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
|
|
this._crs = this.options.crs || map.options.crs;
|
|
this._wmsVersion = parseFloat(this.wmsParams.version);
|
|
|
|
var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
|
|
this.wmsParams[projectionKey] = this._crs.code;
|
|
|
|
L.TileLayer.prototype.onAdd.call(this, map);
|
|
},
|
|
|
|
getTileUrl: function (coords) {
|
|
|
|
var tileBounds = this._tileCoordsToBounds(coords),
|
|
nw = this._crs.project(tileBounds.getNorthWest()),
|
|
se = this._crs.project(tileBounds.getSouthEast()),
|
|
|
|
bbox = (this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ?
|
|
[se.y, nw.x, nw.y, se.x] :
|
|
[nw.x, se.y, se.x, nw.y]).join(','),
|
|
|
|
url = L.TileLayer.prototype.getTileUrl.call(this, coords);
|
|
|
|
return url +
|
|
L.Util.getParamString(this.wmsParams, url, this.options.uppercase) +
|
|
(this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
|
|
},
|
|
|
|
// @method setParams(params: Object, noRedraw?: Boolean): this
|
|
// Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
|
|
setParams: function (params, noRedraw) {
|
|
|
|
L.extend(this.wmsParams, params);
|
|
|
|
if (!noRedraw) {
|
|
this.redraw();
|
|
}
|
|
|
|
return this;
|
|
}
|
|
});
|
|
|
|
|
|
// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
|
|
// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
|
|
L.tileLayer.wms = function (url, options) {
|
|
return new L.TileLayer.WMS(url, options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class ImageOverlay
|
|
* @aka L.ImageOverlay
|
|
* @inherits Interactive layer
|
|
*
|
|
* Used to load and display a single image over specific bounds of the map. Extends `Layer`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
|
|
* imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
|
|
* L.imageOverlay(imageUrl, imageBounds).addTo(map);
|
|
* ```
|
|
*/
|
|
|
|
L.ImageOverlay = L.Layer.extend({
|
|
|
|
// @section
|
|
// @aka ImageOverlay options
|
|
options: {
|
|
// @option opacity: Number = 1.0
|
|
// The opacity of the image overlay.
|
|
opacity: 1,
|
|
|
|
// @option alt: String = ''
|
|
// Text for the `alt` attribute of the image (useful for accessibility).
|
|
alt: '',
|
|
|
|
// @option interactive: Boolean = false
|
|
// If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
|
|
interactive: false,
|
|
|
|
// @option crossOrigin: Boolean = false
|
|
// If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
|
|
crossOrigin: false
|
|
},
|
|
|
|
initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
|
|
this._url = url;
|
|
this._bounds = L.latLngBounds(bounds);
|
|
|
|
L.setOptions(this, options);
|
|
},
|
|
|
|
onAdd: function () {
|
|
if (!this._image) {
|
|
this._initImage();
|
|
|
|
if (this.options.opacity < 1) {
|
|
this._updateOpacity();
|
|
}
|
|
}
|
|
|
|
if (this.options.interactive) {
|
|
L.DomUtil.addClass(this._image, 'leaflet-interactive');
|
|
this.addInteractiveTarget(this._image);
|
|
}
|
|
|
|
this.getPane().appendChild(this._image);
|
|
this._reset();
|
|
},
|
|
|
|
onRemove: function () {
|
|
L.DomUtil.remove(this._image);
|
|
if (this.options.interactive) {
|
|
this.removeInteractiveTarget(this._image);
|
|
}
|
|
},
|
|
|
|
// @method setOpacity(opacity: Number): this
|
|
// Sets the opacity of the overlay.
|
|
setOpacity: function (opacity) {
|
|
this.options.opacity = opacity;
|
|
|
|
if (this._image) {
|
|
this._updateOpacity();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
setStyle: function (styleOpts) {
|
|
if (styleOpts.opacity) {
|
|
this.setOpacity(styleOpts.opacity);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method bringToFront(): this
|
|
// Brings the layer to the top of all overlays.
|
|
bringToFront: function () {
|
|
if (this._map) {
|
|
L.DomUtil.toFront(this._image);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method bringToBack(): this
|
|
// Brings the layer to the bottom of all overlays.
|
|
bringToBack: function () {
|
|
if (this._map) {
|
|
L.DomUtil.toBack(this._image);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method setUrl(url: String): this
|
|
// Changes the URL of the image.
|
|
setUrl: function (url) {
|
|
this._url = url;
|
|
|
|
if (this._image) {
|
|
this._image.src = url;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
setBounds: function (bounds) {
|
|
this._bounds = bounds;
|
|
|
|
if (this._map) {
|
|
this._reset();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
getEvents: function () {
|
|
var events = {
|
|
zoom: this._reset,
|
|
viewreset: this._reset
|
|
};
|
|
|
|
if (this._zoomAnimated) {
|
|
events.zoomanim = this._animateZoom;
|
|
}
|
|
|
|
return events;
|
|
},
|
|
|
|
getBounds: function () {
|
|
return this._bounds;
|
|
},
|
|
|
|
getElement: function () {
|
|
return this._image;
|
|
},
|
|
|
|
_initImage: function () {
|
|
var img = this._image = L.DomUtil.create('img',
|
|
'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : ''));
|
|
|
|
img.onselectstart = L.Util.falseFn;
|
|
img.onmousemove = L.Util.falseFn;
|
|
|
|
img.onload = L.bind(this.fire, this, 'load');
|
|
|
|
if (this.options.crossOrigin) {
|
|
img.crossOrigin = '';
|
|
}
|
|
|
|
img.src = this._url;
|
|
img.alt = this.options.alt;
|
|
},
|
|
|
|
_animateZoom: function (e) {
|
|
var scale = this._map.getZoomScale(e.zoom),
|
|
offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
|
|
|
|
L.DomUtil.setTransform(this._image, offset, scale);
|
|
},
|
|
|
|
_reset: function () {
|
|
var image = this._image,
|
|
bounds = new L.Bounds(
|
|
this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
|
|
this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
|
|
size = bounds.getSize();
|
|
|
|
L.DomUtil.setPosition(image, bounds.min);
|
|
|
|
image.style.width = size.x + 'px';
|
|
image.style.height = size.y + 'px';
|
|
},
|
|
|
|
_updateOpacity: function () {
|
|
L.DomUtil.setOpacity(this._image, this.options.opacity);
|
|
}
|
|
});
|
|
|
|
// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
|
|
// Instantiates an image overlay object given the URL of the image and the
|
|
// geographical bounds it is tied to.
|
|
L.imageOverlay = function (url, bounds, options) {
|
|
return new L.ImageOverlay(url, bounds, options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Icon
|
|
* @aka L.Icon
|
|
* @inherits Layer
|
|
*
|
|
* Represents an icon to provide when creating a marker.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* var myIcon = L.icon({
|
|
* iconUrl: 'my-icon.png',
|
|
* iconRetinaUrl: 'my-icon@2x.png',
|
|
* iconSize: [38, 95],
|
|
* iconAnchor: [22, 94],
|
|
* popupAnchor: [-3, -76],
|
|
* shadowUrl: 'my-icon-shadow.png',
|
|
* shadowRetinaUrl: 'my-icon-shadow@2x.png',
|
|
* shadowSize: [68, 95],
|
|
* shadowAnchor: [22, 94]
|
|
* });
|
|
*
|
|
* L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
|
|
* ```
|
|
*
|
|
* `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
|
|
*
|
|
*/
|
|
|
|
L.Icon = L.Class.extend({
|
|
|
|
/* @section
|
|
* @aka Icon options
|
|
*
|
|
* @option iconUrl: String = null
|
|
* **(required)** The URL to the icon image (absolute or relative to your script path).
|
|
*
|
|
* @option iconRetinaUrl: String = null
|
|
* The URL to a retina sized version of the icon image (absolute or relative to your
|
|
* script path). Used for Retina screen devices.
|
|
*
|
|
* @option iconSize: Point = null
|
|
* Size of the icon image in pixels.
|
|
*
|
|
* @option iconAnchor: Point = null
|
|
* The coordinates of the "tip" of the icon (relative to its top left corner). The icon
|
|
* will be aligned so that this point is at the marker's geographical location. Centered
|
|
* by default if size is specified, also can be set in CSS with negative margins.
|
|
*
|
|
* @option popupAnchor: Point = null
|
|
* The coordinates of the point from which popups will "open", relative to the icon anchor.
|
|
*
|
|
* @option shadowUrl: String = null
|
|
* The URL to the icon shadow image. If not specified, no shadow image will be created.
|
|
*
|
|
* @option shadowRetinaUrl: String = null
|
|
*
|
|
* @option shadowSize: Point = null
|
|
* Size of the shadow image in pixels.
|
|
*
|
|
* @option shadowAnchor: Point = null
|
|
* The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
|
|
* as iconAnchor if not specified).
|
|
*
|
|
* @option className: String = ''
|
|
* A custom class name to assign to both icon and shadow images. Empty by default.
|
|
*/
|
|
|
|
initialize: function (options) {
|
|
L.setOptions(this, options);
|
|
},
|
|
|
|
// @method createIcon(oldIcon?: HTMLElement): HTMLElement
|
|
// Called internally when the icon has to be shown, returns a `<img>` HTML element
|
|
// styled according to the options.
|
|
createIcon: function (oldIcon) {
|
|
return this._createIcon('icon', oldIcon);
|
|
},
|
|
|
|
// @method createShadow(oldIcon?: HTMLElement): HTMLElement
|
|
// As `createIcon`, but for the shadow beneath it.
|
|
createShadow: function (oldIcon) {
|
|
return this._createIcon('shadow', oldIcon);
|
|
},
|
|
|
|
_createIcon: function (name, oldIcon) {
|
|
var src = this._getIconUrl(name);
|
|
|
|
if (!src) {
|
|
if (name === 'icon') {
|
|
throw new Error('iconUrl not set in Icon options (see the docs).');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
|
|
this._setIconStyles(img, name);
|
|
|
|
return img;
|
|
},
|
|
|
|
_setIconStyles: function (img, name) {
|
|
var options = this.options;
|
|
var sizeOption = options[name + 'Size'];
|
|
|
|
if (typeof sizeOption === 'number') {
|
|
sizeOption = [sizeOption, sizeOption];
|
|
}
|
|
|
|
var size = L.point(sizeOption),
|
|
anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
|
|
size && size.divideBy(2, true));
|
|
|
|
img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
|
|
|
|
if (anchor) {
|
|
img.style.marginLeft = (-anchor.x) + 'px';
|
|
img.style.marginTop = (-anchor.y) + 'px';
|
|
}
|
|
|
|
if (size) {
|
|
img.style.width = size.x + 'px';
|
|
img.style.height = size.y + 'px';
|
|
}
|
|
},
|
|
|
|
_createImg: function (src, el) {
|
|
el = el || document.createElement('img');
|
|
el.src = src;
|
|
return el;
|
|
},
|
|
|
|
_getIconUrl: function (name) {
|
|
return L.Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
|
|
}
|
|
});
|
|
|
|
|
|
// @factory L.icon(options: Icon options)
|
|
// Creates an icon instance with the given options.
|
|
L.icon = function (options) {
|
|
return new L.Icon(options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @miniclass Icon.Default (Icon)
|
|
* @aka L.Icon.Default
|
|
* @section
|
|
*
|
|
* A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
|
|
* no icon is specified. Points to the blue marker image distributed with Leaflet
|
|
* releases.
|
|
*
|
|
* In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
|
|
* (which is a set of `Icon options`).
|
|
*
|
|
* If you want to _completely_ replace the default icon, override the
|
|
* `L.Marker.prototype.options.icon` with your own icon instead.
|
|
*/
|
|
|
|
L.Icon.Default = L.Icon.extend({
|
|
|
|
options: {
|
|
iconUrl: 'marker-icon.png',
|
|
iconRetinaUrl: 'marker-icon-2x.png',
|
|
shadowUrl: 'marker-shadow.png',
|
|
iconSize: [25, 41],
|
|
iconAnchor: [12, 41],
|
|
popupAnchor: [1, -34],
|
|
tooltipAnchor: [16, -28],
|
|
shadowSize: [41, 41]
|
|
},
|
|
|
|
_getIconUrl: function (name) {
|
|
if (!L.Icon.Default.imagePath) { // Deprecated, backwards-compatibility only
|
|
L.Icon.Default.imagePath = this._detectIconPath();
|
|
}
|
|
|
|
// @option imagePath: String
|
|
// `L.Icon.Default` will try to auto-detect the absolute location of the
|
|
// blue icon images. If you are placing these images in a non-standard
|
|
// way, set this option to point to the right absolute path.
|
|
return (this.options.imagePath || L.Icon.Default.imagePath) + L.Icon.prototype._getIconUrl.call(this, name);
|
|
},
|
|
|
|
_detectIconPath: function () {
|
|
var el = L.DomUtil.create('div', 'leaflet-default-icon-path', document.body);
|
|
var path = L.DomUtil.getStyle(el, 'background-image') ||
|
|
L.DomUtil.getStyle(el, 'backgroundImage'); // IE8
|
|
|
|
document.body.removeChild(el);
|
|
|
|
return path.indexOf('url') === 0 ?
|
|
path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '') : '';
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @class Marker
|
|
* @inherits Interactive layer
|
|
* @aka L.Marker
|
|
* L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* L.marker([50.5, 30.5]).addTo(map);
|
|
* ```
|
|
*/
|
|
|
|
L.Marker = L.Layer.extend({
|
|
|
|
// @section
|
|
// @aka Marker options
|
|
options: {
|
|
// @option icon: Icon = *
|
|
// Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. If not specified, a new `L.Icon.Default` is used.
|
|
icon: new L.Icon.Default(),
|
|
|
|
// Option inherited from "Interactive layer" abstract class
|
|
interactive: true,
|
|
|
|
// @option draggable: Boolean = false
|
|
// Whether the marker is draggable with mouse/touch or not.
|
|
draggable: false,
|
|
|
|
// @option keyboard: Boolean = true
|
|
// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
|
|
keyboard: true,
|
|
|
|
// @option title: String = ''
|
|
// Text for the browser tooltip that appear on marker hover (no tooltip by default).
|
|
title: '',
|
|
|
|
// @option alt: String = ''
|
|
// Text for the `alt` attribute of the icon image (useful for accessibility).
|
|
alt: '',
|
|
|
|
// @option zIndexOffset: Number = 0
|
|
// By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
|
|
zIndexOffset: 0,
|
|
|
|
// @option opacity: Number = 1.0
|
|
// The opacity of the marker.
|
|
opacity: 1,
|
|
|
|
// @option riseOnHover: Boolean = false
|
|
// If `true`, the marker will get on top of others when you hover the mouse over it.
|
|
riseOnHover: false,
|
|
|
|
// @option riseOffset: Number = 250
|
|
// The z-index offset used for the `riseOnHover` feature.
|
|
riseOffset: 250,
|
|
|
|
// @option pane: String = 'markerPane'
|
|
// `Map pane` where the markers icon will be added.
|
|
pane: 'markerPane',
|
|
|
|
// FIXME: shadowPane is no longer a valid option
|
|
nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu']
|
|
},
|
|
|
|
/* @section
|
|
*
|
|
* In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
|
|
*/
|
|
|
|
initialize: function (latlng, options) {
|
|
L.setOptions(this, options);
|
|
this._latlng = L.latLng(latlng);
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
|
|
|
|
if (this._zoomAnimated) {
|
|
map.on('zoomanim', this._animateZoom, this);
|
|
}
|
|
|
|
this._initIcon();
|
|
this.update();
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
if (this.dragging && this.dragging.enabled()) {
|
|
this.options.draggable = true;
|
|
this.dragging.removeHooks();
|
|
}
|
|
|
|
if (this._zoomAnimated) {
|
|
map.off('zoomanim', this._animateZoom, this);
|
|
}
|
|
|
|
this._removeIcon();
|
|
this._removeShadow();
|
|
},
|
|
|
|
getEvents: function () {
|
|
return {
|
|
zoom: this.update,
|
|
viewreset: this.update
|
|
};
|
|
},
|
|
|
|
// @method getLatLng: LatLng
|
|
// Returns the current geographical position of the marker.
|
|
getLatLng: function () {
|
|
return this._latlng;
|
|
},
|
|
|
|
// @method setLatLng(latlng: LatLng): this
|
|
// Changes the marker position to the given point.
|
|
setLatLng: function (latlng) {
|
|
var oldLatLng = this._latlng;
|
|
this._latlng = L.latLng(latlng);
|
|
this.update();
|
|
|
|
// @event move: Event
|
|
// Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
|
|
return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
|
|
},
|
|
|
|
// @method setZIndexOffset(offset: Number): this
|
|
// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
|
|
setZIndexOffset: function (offset) {
|
|
this.options.zIndexOffset = offset;
|
|
return this.update();
|
|
},
|
|
|
|
// @method setIcon(icon: Icon): this
|
|
// Changes the marker icon.
|
|
setIcon: function (icon) {
|
|
|
|
this.options.icon = icon;
|
|
|
|
if (this._map) {
|
|
this._initIcon();
|
|
this.update();
|
|
}
|
|
|
|
if (this._popup) {
|
|
this.bindPopup(this._popup, this._popup.options);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
getElement: function () {
|
|
return this._icon;
|
|
},
|
|
|
|
update: function () {
|
|
|
|
if (this._icon) {
|
|
var pos = this._map.latLngToLayerPoint(this._latlng).round();
|
|
this._setPos(pos);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
_initIcon: function () {
|
|
var options = this.options,
|
|
classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
|
|
|
|
var icon = options.icon.createIcon(this._icon),
|
|
addIcon = false;
|
|
|
|
// if we're not reusing the icon, remove the old one and init new one
|
|
if (icon !== this._icon) {
|
|
if (this._icon) {
|
|
this._removeIcon();
|
|
}
|
|
addIcon = true;
|
|
|
|
if (options.title) {
|
|
icon.title = options.title;
|
|
}
|
|
if (options.alt) {
|
|
icon.alt = options.alt;
|
|
}
|
|
}
|
|
|
|
L.DomUtil.addClass(icon, classToAdd);
|
|
|
|
if (options.keyboard) {
|
|
icon.tabIndex = '0';
|
|
}
|
|
|
|
this._icon = icon;
|
|
|
|
if (options.riseOnHover) {
|
|
this.on({
|
|
mouseover: this._bringToFront,
|
|
mouseout: this._resetZIndex
|
|
});
|
|
}
|
|
|
|
var newShadow = options.icon.createShadow(this._shadow),
|
|
addShadow = false;
|
|
|
|
if (newShadow !== this._shadow) {
|
|
this._removeShadow();
|
|
addShadow = true;
|
|
}
|
|
|
|
if (newShadow) {
|
|
L.DomUtil.addClass(newShadow, classToAdd);
|
|
}
|
|
this._shadow = newShadow;
|
|
|
|
|
|
if (options.opacity < 1) {
|
|
this._updateOpacity();
|
|
}
|
|
|
|
|
|
if (addIcon) {
|
|
this.getPane().appendChild(this._icon);
|
|
}
|
|
this._initInteraction();
|
|
if (newShadow && addShadow) {
|
|
this.getPane('shadowPane').appendChild(this._shadow);
|
|
}
|
|
},
|
|
|
|
_removeIcon: function () {
|
|
if (this.options.riseOnHover) {
|
|
this.off({
|
|
mouseover: this._bringToFront,
|
|
mouseout: this._resetZIndex
|
|
});
|
|
}
|
|
|
|
L.DomUtil.remove(this._icon);
|
|
this.removeInteractiveTarget(this._icon);
|
|
|
|
this._icon = null;
|
|
},
|
|
|
|
_removeShadow: function () {
|
|
if (this._shadow) {
|
|
L.DomUtil.remove(this._shadow);
|
|
}
|
|
this._shadow = null;
|
|
},
|
|
|
|
_setPos: function (pos) {
|
|
L.DomUtil.setPosition(this._icon, pos);
|
|
|
|
if (this._shadow) {
|
|
L.DomUtil.setPosition(this._shadow, pos);
|
|
}
|
|
|
|
this._zIndex = pos.y + this.options.zIndexOffset;
|
|
|
|
this._resetZIndex();
|
|
},
|
|
|
|
_updateZIndex: function (offset) {
|
|
this._icon.style.zIndex = this._zIndex + offset;
|
|
},
|
|
|
|
_animateZoom: function (opt) {
|
|
var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
|
|
|
|
this._setPos(pos);
|
|
},
|
|
|
|
_initInteraction: function () {
|
|
|
|
if (!this.options.interactive) { return; }
|
|
|
|
L.DomUtil.addClass(this._icon, 'leaflet-interactive');
|
|
|
|
this.addInteractiveTarget(this._icon);
|
|
|
|
if (L.Handler.MarkerDrag) {
|
|
var draggable = this.options.draggable;
|
|
if (this.dragging) {
|
|
draggable = this.dragging.enabled();
|
|
this.dragging.disable();
|
|
}
|
|
|
|
this.dragging = new L.Handler.MarkerDrag(this);
|
|
|
|
if (draggable) {
|
|
this.dragging.enable();
|
|
}
|
|
}
|
|
},
|
|
|
|
// @method setOpacity(opacity: Number): this
|
|
// Changes the opacity of the marker.
|
|
setOpacity: function (opacity) {
|
|
this.options.opacity = opacity;
|
|
if (this._map) {
|
|
this._updateOpacity();
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
_updateOpacity: function () {
|
|
var opacity = this.options.opacity;
|
|
|
|
L.DomUtil.setOpacity(this._icon, opacity);
|
|
|
|
if (this._shadow) {
|
|
L.DomUtil.setOpacity(this._shadow, opacity);
|
|
}
|
|
},
|
|
|
|
_bringToFront: function () {
|
|
this._updateZIndex(this.options.riseOffset);
|
|
},
|
|
|
|
_resetZIndex: function () {
|
|
this._updateZIndex(0);
|
|
},
|
|
|
|
_getPopupAnchor: function () {
|
|
return this.options.icon.options.popupAnchor || [0, 0];
|
|
},
|
|
|
|
_getTooltipAnchor: function () {
|
|
return this.options.icon.options.tooltipAnchor || [0, 0];
|
|
}
|
|
});
|
|
|
|
|
|
// factory L.marker(latlng: LatLng, options? : Marker options)
|
|
|
|
// @factory L.marker(latlng: LatLng, options? : Marker options)
|
|
// Instantiates a Marker object given a geographical point and optionally an options object.
|
|
L.marker = function (latlng, options) {
|
|
return new L.Marker(latlng, options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class DivIcon
|
|
* @aka L.DivIcon
|
|
* @inherits Icon
|
|
*
|
|
* Represents a lightweight icon for markers that uses a simple `<div>`
|
|
* element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
|
|
*
|
|
* @example
|
|
* ```js
|
|
* var myIcon = L.divIcon({className: 'my-div-icon'});
|
|
* // you can set .my-div-icon styles in CSS
|
|
*
|
|
* L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
|
|
* ```
|
|
*
|
|
* By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
|
|
*/
|
|
|
|
L.DivIcon = L.Icon.extend({
|
|
options: {
|
|
// @section
|
|
// @aka DivIcon options
|
|
iconSize: [12, 12], // also can be set through CSS
|
|
|
|
// iconAnchor: (Point),
|
|
// popupAnchor: (Point),
|
|
|
|
// @option html: String = ''
|
|
// Custom HTML code to put inside the div element, empty by default.
|
|
html: false,
|
|
|
|
// @option bgPos: Point = [0, 0]
|
|
// Optional relative position of the background, in pixels
|
|
bgPos: null,
|
|
|
|
className: 'leaflet-div-icon'
|
|
},
|
|
|
|
createIcon: function (oldIcon) {
|
|
var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
|
|
options = this.options;
|
|
|
|
div.innerHTML = options.html !== false ? options.html : '';
|
|
|
|
if (options.bgPos) {
|
|
var bgPos = L.point(options.bgPos);
|
|
div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
|
|
}
|
|
this._setIconStyles(div, 'icon');
|
|
|
|
return div;
|
|
},
|
|
|
|
createShadow: function () {
|
|
return null;
|
|
}
|
|
});
|
|
|
|
// @factory L.divIcon(options: DivIcon options)
|
|
// Creates a `DivIcon` instance with the given options.
|
|
L.divIcon = function (options) {
|
|
return new L.DivIcon(options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class DivOverlay
|
|
* @inherits Layer
|
|
* @aka L.DivOverlay
|
|
* Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
|
|
*/
|
|
|
|
// @namespace DivOverlay
|
|
L.DivOverlay = L.Layer.extend({
|
|
|
|
// @section
|
|
// @aka DivOverlay options
|
|
options: {
|
|
// @option offset: Point = Point(0, 7)
|
|
// The offset of the popup position. Useful to control the anchor
|
|
// of the popup when opening it on some overlays.
|
|
offset: [0, 7],
|
|
|
|
// @option className: String = ''
|
|
// A custom CSS class name to assign to the popup.
|
|
className: '',
|
|
|
|
// @option pane: String = 'popupPane'
|
|
// `Map pane` where the popup will be added.
|
|
pane: 'popupPane'
|
|
},
|
|
|
|
initialize: function (options, source) {
|
|
L.setOptions(this, options);
|
|
|
|
this._source = source;
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
this._zoomAnimated = map._zoomAnimated;
|
|
|
|
if (!this._container) {
|
|
this._initLayout();
|
|
}
|
|
|
|
if (map._fadeAnimated) {
|
|
L.DomUtil.setOpacity(this._container, 0);
|
|
}
|
|
|
|
clearTimeout(this._removeTimeout);
|
|
this.getPane().appendChild(this._container);
|
|
this.update();
|
|
|
|
if (map._fadeAnimated) {
|
|
L.DomUtil.setOpacity(this._container, 1);
|
|
}
|
|
|
|
this.bringToFront();
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
if (map._fadeAnimated) {
|
|
L.DomUtil.setOpacity(this._container, 0);
|
|
this._removeTimeout = setTimeout(L.bind(L.DomUtil.remove, L.DomUtil, this._container), 200);
|
|
} else {
|
|
L.DomUtil.remove(this._container);
|
|
}
|
|
},
|
|
|
|
// @namespace Popup
|
|
// @method getLatLng: LatLng
|
|
// Returns the geographical point of popup.
|
|
getLatLng: function () {
|
|
return this._latlng;
|
|
},
|
|
|
|
// @method setLatLng(latlng: LatLng): this
|
|
// Sets the geographical point where the popup will open.
|
|
setLatLng: function (latlng) {
|
|
this._latlng = L.latLng(latlng);
|
|
if (this._map) {
|
|
this._updatePosition();
|
|
this._adjustPan();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method getContent: String|HTMLElement
|
|
// Returns the content of the popup.
|
|
getContent: function () {
|
|
return this._content;
|
|
},
|
|
|
|
// @method setContent(htmlContent: String|HTMLElement|Function): this
|
|
// Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup.
|
|
setContent: function (content) {
|
|
this._content = content;
|
|
this.update();
|
|
return this;
|
|
},
|
|
|
|
// @method getElement: String|HTMLElement
|
|
// Alias for [getContent()](#popup-getcontent)
|
|
getElement: function () {
|
|
return this._container;
|
|
},
|
|
|
|
// @method update: null
|
|
// Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
|
|
update: function () {
|
|
if (!this._map) { return; }
|
|
|
|
this._container.style.visibility = 'hidden';
|
|
|
|
this._updateContent();
|
|
this._updateLayout();
|
|
this._updatePosition();
|
|
|
|
this._container.style.visibility = '';
|
|
|
|
this._adjustPan();
|
|
},
|
|
|
|
getEvents: function () {
|
|
var events = {
|
|
zoom: this._updatePosition,
|
|
viewreset: this._updatePosition
|
|
};
|
|
|
|
if (this._zoomAnimated) {
|
|
events.zoomanim = this._animateZoom;
|
|
}
|
|
return events;
|
|
},
|
|
|
|
// @method isOpen: Boolean
|
|
// Returns `true` when the popup is visible on the map.
|
|
isOpen: function () {
|
|
return !!this._map && this._map.hasLayer(this);
|
|
},
|
|
|
|
// @method bringToFront: this
|
|
// Brings this popup in front of other popups (in the same map pane).
|
|
bringToFront: function () {
|
|
if (this._map) {
|
|
L.DomUtil.toFront(this._container);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method bringToBack: this
|
|
// Brings this popup to the back of other popups (in the same map pane).
|
|
bringToBack: function () {
|
|
if (this._map) {
|
|
L.DomUtil.toBack(this._container);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
_updateContent: function () {
|
|
if (!this._content) { return; }
|
|
|
|
var node = this._contentNode;
|
|
var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
|
|
|
|
if (typeof content === 'string') {
|
|
node.innerHTML = content;
|
|
} else {
|
|
while (node.hasChildNodes()) {
|
|
node.removeChild(node.firstChild);
|
|
}
|
|
node.appendChild(content);
|
|
}
|
|
this.fire('contentupdate');
|
|
},
|
|
|
|
_updatePosition: function () {
|
|
if (!this._map) { return; }
|
|
|
|
var pos = this._map.latLngToLayerPoint(this._latlng),
|
|
offset = L.point(this.options.offset),
|
|
anchor = this._getAnchor();
|
|
|
|
if (this._zoomAnimated) {
|
|
L.DomUtil.setPosition(this._container, pos.add(anchor));
|
|
} else {
|
|
offset = offset.add(pos).add(anchor);
|
|
}
|
|
|
|
var bottom = this._containerBottom = -offset.y,
|
|
left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
|
|
|
|
// bottom position the popup in case the height of the popup changes (images loading etc)
|
|
this._container.style.bottom = bottom + 'px';
|
|
this._container.style.left = left + 'px';
|
|
},
|
|
|
|
_getAnchor: function () {
|
|
return [0, 0];
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @class Popup
|
|
* @inherits DivOverlay
|
|
* @aka L.Popup
|
|
* Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
|
|
* open popups while making sure that only one popup is open at one time
|
|
* (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
|
|
*
|
|
* @example
|
|
*
|
|
* If you want to just bind a popup to marker click and then open it, it's really easy:
|
|
*
|
|
* ```js
|
|
* marker.bindPopup(popupContent).openPopup();
|
|
* ```
|
|
* Path overlays like polylines also have a `bindPopup` method.
|
|
* Here's a more complicated way to open a popup on a map:
|
|
*
|
|
* ```js
|
|
* var popup = L.popup()
|
|
* .setLatLng(latlng)
|
|
* .setContent('<p>Hello world!<br />This is a nice popup.</p>')
|
|
* .openOn(map);
|
|
* ```
|
|
*/
|
|
|
|
|
|
// @namespace Popup
|
|
L.Popup = L.DivOverlay.extend({
|
|
|
|
// @section
|
|
// @aka Popup options
|
|
options: {
|
|
// @option maxWidth: Number = 300
|
|
// Max width of the popup, in pixels.
|
|
maxWidth: 300,
|
|
|
|
// @option minWidth: Number = 50
|
|
// Min width of the popup, in pixels.
|
|
minWidth: 50,
|
|
|
|
// @option maxHeight: Number = null
|
|
// If set, creates a scrollable container of the given height
|
|
// inside a popup if its content exceeds it.
|
|
maxHeight: null,
|
|
|
|
// @option autoPan: Boolean = true
|
|
// Set it to `false` if you don't want the map to do panning animation
|
|
// to fit the opened popup.
|
|
autoPan: true,
|
|
|
|
// @option autoPanPaddingTopLeft: Point = null
|
|
// The margin between the popup and the top left corner of the map
|
|
// view after autopanning was performed.
|
|
autoPanPaddingTopLeft: null,
|
|
|
|
// @option autoPanPaddingBottomRight: Point = null
|
|
// The margin between the popup and the bottom right corner of the map
|
|
// view after autopanning was performed.
|
|
autoPanPaddingBottomRight: null,
|
|
|
|
// @option autoPanPadding: Point = Point(5, 5)
|
|
// Equivalent of setting both top left and bottom right autopan padding to the same value.
|
|
autoPanPadding: [5, 5],
|
|
|
|
// @option keepInView: Boolean = false
|
|
// Set it to `true` if you want to prevent users from panning the popup
|
|
// off of the screen while it is open.
|
|
keepInView: false,
|
|
|
|
// @option closeButton: Boolean = true
|
|
// Controls the presence of a close button in the popup.
|
|
closeButton: true,
|
|
|
|
// @option autoClose: Boolean = true
|
|
// Set it to `false` if you want to override the default behavior of
|
|
// the popup closing when user clicks the map (set globally by
|
|
// the Map's [closePopupOnClick](#map-closepopuponclick) option).
|
|
autoClose: true,
|
|
|
|
// @option className: String = ''
|
|
// A custom CSS class name to assign to the popup.
|
|
className: ''
|
|
},
|
|
|
|
// @namespace Popup
|
|
// @method openOn(map: Map): this
|
|
// Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
|
|
openOn: function (map) {
|
|
map.openPopup(this);
|
|
return this;
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
L.DivOverlay.prototype.onAdd.call(this, map);
|
|
|
|
// @namespace Map
|
|
// @section Popup events
|
|
// @event popupopen: PopupEvent
|
|
// Fired when a popup is opened in the map
|
|
map.fire('popupopen', {popup: this});
|
|
|
|
if (this._source) {
|
|
// @namespace Layer
|
|
// @section Popup events
|
|
// @event popupopen: PopupEvent
|
|
// Fired when a popup bound to this layer is opened
|
|
this._source.fire('popupopen', {popup: this}, true);
|
|
// For non-path layers, we toggle the popup when clicking
|
|
// again the layer, so prevent the map to reopen it.
|
|
if (!(this._source instanceof L.Path)) {
|
|
this._source.on('preclick', L.DomEvent.stopPropagation);
|
|
}
|
|
}
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
L.DivOverlay.prototype.onRemove.call(this, map);
|
|
|
|
// @namespace Map
|
|
// @section Popup events
|
|
// @event popupclose: PopupEvent
|
|
// Fired when a popup in the map is closed
|
|
map.fire('popupclose', {popup: this});
|
|
|
|
if (this._source) {
|
|
// @namespace Layer
|
|
// @section Popup events
|
|
// @event popupclose: PopupEvent
|
|
// Fired when a popup bound to this layer is closed
|
|
this._source.fire('popupclose', {popup: this}, true);
|
|
if (!(this._source instanceof L.Path)) {
|
|
this._source.off('preclick', L.DomEvent.stopPropagation);
|
|
}
|
|
}
|
|
},
|
|
|
|
getEvents: function () {
|
|
var events = L.DivOverlay.prototype.getEvents.call(this);
|
|
|
|
if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
|
|
events.preclick = this._close;
|
|
}
|
|
|
|
if (this.options.keepInView) {
|
|
events.moveend = this._adjustPan;
|
|
}
|
|
|
|
return events;
|
|
},
|
|
|
|
_close: function () {
|
|
if (this._map) {
|
|
this._map.closePopup(this);
|
|
}
|
|
},
|
|
|
|
_initLayout: function () {
|
|
var prefix = 'leaflet-popup',
|
|
container = this._container = L.DomUtil.create('div',
|
|
prefix + ' ' + (this.options.className || '') +
|
|
' leaflet-zoom-animated');
|
|
|
|
if (this.options.closeButton) {
|
|
var closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container);
|
|
closeButton.href = '#close';
|
|
closeButton.innerHTML = '×';
|
|
|
|
L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
|
|
}
|
|
|
|
var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container);
|
|
this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
|
|
|
|
L.DomEvent
|
|
.disableClickPropagation(wrapper)
|
|
.disableScrollPropagation(this._contentNode)
|
|
.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);
|
|
|
|
this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
|
|
this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
|
|
},
|
|
|
|
_updateLayout: function () {
|
|
var container = this._contentNode,
|
|
style = container.style;
|
|
|
|
style.width = '';
|
|
style.whiteSpace = 'nowrap';
|
|
|
|
var width = container.offsetWidth;
|
|
width = Math.min(width, this.options.maxWidth);
|
|
width = Math.max(width, this.options.minWidth);
|
|
|
|
style.width = (width + 1) + 'px';
|
|
style.whiteSpace = '';
|
|
|
|
style.height = '';
|
|
|
|
var height = container.offsetHeight,
|
|
maxHeight = this.options.maxHeight,
|
|
scrolledClass = 'leaflet-popup-scrolled';
|
|
|
|
if (maxHeight && height > maxHeight) {
|
|
style.height = maxHeight + 'px';
|
|
L.DomUtil.addClass(container, scrolledClass);
|
|
} else {
|
|
L.DomUtil.removeClass(container, scrolledClass);
|
|
}
|
|
|
|
this._containerWidth = this._container.offsetWidth;
|
|
},
|
|
|
|
_animateZoom: function (e) {
|
|
var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
|
|
anchor = this._getAnchor();
|
|
L.DomUtil.setPosition(this._container, pos.add(anchor));
|
|
},
|
|
|
|
_adjustPan: function () {
|
|
if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
|
|
|
|
var map = this._map,
|
|
marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
|
|
containerHeight = this._container.offsetHeight + marginBottom,
|
|
containerWidth = this._containerWidth,
|
|
layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
|
|
|
|
layerPos._add(L.DomUtil.getPosition(this._container));
|
|
|
|
var containerPos = map.layerPointToContainerPoint(layerPos),
|
|
padding = L.point(this.options.autoPanPadding),
|
|
paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
|
|
paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
|
|
size = map.getSize(),
|
|
dx = 0,
|
|
dy = 0;
|
|
|
|
if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
|
|
dx = containerPos.x + containerWidth - size.x + paddingBR.x;
|
|
}
|
|
if (containerPos.x - dx - paddingTL.x < 0) { // left
|
|
dx = containerPos.x - paddingTL.x;
|
|
}
|
|
if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
|
|
dy = containerPos.y + containerHeight - size.y + paddingBR.y;
|
|
}
|
|
if (containerPos.y - dy - paddingTL.y < 0) { // top
|
|
dy = containerPos.y - paddingTL.y;
|
|
}
|
|
|
|
// @namespace Map
|
|
// @section Popup events
|
|
// @event autopanstart: Event
|
|
// Fired when the map starts autopanning when opening a popup.
|
|
if (dx || dy) {
|
|
map
|
|
.fire('autopanstart')
|
|
.panBy([dx, dy]);
|
|
}
|
|
},
|
|
|
|
_onCloseButtonClick: function (e) {
|
|
this._close();
|
|
L.DomEvent.stop(e);
|
|
},
|
|
|
|
_getAnchor: function () {
|
|
// Where should we anchor the popup on the source layer?
|
|
return L.point(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
|
|
}
|
|
|
|
});
|
|
|
|
// @namespace Popup
|
|
// @factory L.popup(options?: Popup options, source?: Layer)
|
|
// Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
|
|
L.popup = function (options, source) {
|
|
return new L.Popup(options, source);
|
|
};
|
|
|
|
|
|
/* @namespace Map
|
|
* @section Interaction Options
|
|
* @option closePopupOnClick: Boolean = true
|
|
* Set it to `false` if you don't want popups to close when user clicks the map.
|
|
*/
|
|
L.Map.mergeOptions({
|
|
closePopupOnClick: true
|
|
});
|
|
|
|
|
|
// @namespace Map
|
|
// @section Methods for Layers and Controls
|
|
L.Map.include({
|
|
// @method openPopup(popup: Popup): this
|
|
// Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
|
|
// @alternative
|
|
// @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
|
|
// Creates a popup with the specified content and options and opens it in the given point on a map.
|
|
openPopup: function (popup, latlng, options) {
|
|
if (!(popup instanceof L.Popup)) {
|
|
popup = new L.Popup(options).setContent(popup);
|
|
}
|
|
|
|
if (latlng) {
|
|
popup.setLatLng(latlng);
|
|
}
|
|
|
|
if (this.hasLayer(popup)) {
|
|
return this;
|
|
}
|
|
|
|
if (this._popup && this._popup.options.autoClose) {
|
|
this.closePopup();
|
|
}
|
|
|
|
this._popup = popup;
|
|
return this.addLayer(popup);
|
|
},
|
|
|
|
// @method closePopup(popup?: Popup): this
|
|
// Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
|
|
closePopup: function (popup) {
|
|
if (!popup || popup === this._popup) {
|
|
popup = this._popup;
|
|
this._popup = null;
|
|
}
|
|
if (popup) {
|
|
this.removeLayer(popup);
|
|
}
|
|
return this;
|
|
}
|
|
});
|
|
|
|
/*
|
|
* @namespace Layer
|
|
* @section Popup methods example
|
|
*
|
|
* All layers share a set of methods convenient for binding popups to it.
|
|
*
|
|
* ```js
|
|
* var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
|
|
* layer.openPopup();
|
|
* layer.closePopup();
|
|
* ```
|
|
*
|
|
* Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened.
|
|
*/
|
|
|
|
// @section Popup methods
|
|
L.Layer.include({
|
|
|
|
// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
|
|
// Binds a popup to the layer with the passed `content` and sets up the
|
|
// neccessary event listeners. If a `Function` is passed it will receive
|
|
// the layer as the first argument and should return a `String` or `HTMLElement`.
|
|
bindPopup: function (content, options) {
|
|
|
|
if (content instanceof L.Popup) {
|
|
L.setOptions(content, options);
|
|
this._popup = content;
|
|
content._source = this;
|
|
} else {
|
|
if (!this._popup || options) {
|
|
this._popup = new L.Popup(options, this);
|
|
}
|
|
this._popup.setContent(content);
|
|
}
|
|
|
|
if (!this._popupHandlersAdded) {
|
|
this.on({
|
|
click: this._openPopup,
|
|
remove: this.closePopup,
|
|
move: this._movePopup
|
|
});
|
|
this._popupHandlersAdded = true;
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method unbindPopup(): this
|
|
// Removes the popup previously bound with `bindPopup`.
|
|
unbindPopup: function () {
|
|
if (this._popup) {
|
|
this.off({
|
|
click: this._openPopup,
|
|
remove: this.closePopup,
|
|
move: this._movePopup
|
|
});
|
|
this._popupHandlersAdded = false;
|
|
this._popup = null;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method openPopup(latlng?: LatLng): this
|
|
// Opens the bound popup at the specificed `latlng` or at the default popup anchor if no `latlng` is passed.
|
|
openPopup: function (layer, latlng) {
|
|
if (!(layer instanceof L.Layer)) {
|
|
latlng = layer;
|
|
layer = this;
|
|
}
|
|
|
|
if (layer instanceof L.FeatureGroup) {
|
|
for (var id in this._layers) {
|
|
layer = this._layers[id];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!latlng) {
|
|
latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
|
|
}
|
|
|
|
if (this._popup && this._map) {
|
|
// set popup source to this layer
|
|
this._popup._source = layer;
|
|
|
|
// update the popup (content, layout, ect...)
|
|
this._popup.update();
|
|
|
|
// open the popup on the map
|
|
this._map.openPopup(this._popup, latlng);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method closePopup(): this
|
|
// Closes the popup bound to this layer if it is open.
|
|
closePopup: function () {
|
|
if (this._popup) {
|
|
this._popup._close();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method togglePopup(): this
|
|
// Opens or closes the popup bound to this layer depending on its current state.
|
|
togglePopup: function (target) {
|
|
if (this._popup) {
|
|
if (this._popup._map) {
|
|
this.closePopup();
|
|
} else {
|
|
this.openPopup(target);
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method isPopupOpen(): boolean
|
|
// Returns `true` if the popup bound to this layer is currently open.
|
|
isPopupOpen: function () {
|
|
return this._popup.isOpen();
|
|
},
|
|
|
|
// @method setPopupContent(content: String|HTMLElement|Popup): this
|
|
// Sets the content of the popup bound to this layer.
|
|
setPopupContent: function (content) {
|
|
if (this._popup) {
|
|
this._popup.setContent(content);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method getPopup(): Popup
|
|
// Returns the popup bound to this layer.
|
|
getPopup: function () {
|
|
return this._popup;
|
|
},
|
|
|
|
_openPopup: function (e) {
|
|
var layer = e.layer || e.target;
|
|
|
|
if (!this._popup) {
|
|
return;
|
|
}
|
|
|
|
if (!this._map) {
|
|
return;
|
|
}
|
|
|
|
// prevent map click
|
|
L.DomEvent.stop(e);
|
|
|
|
// if this inherits from Path its a vector and we can just
|
|
// open the popup at the new location
|
|
if (layer instanceof L.Path) {
|
|
this.openPopup(e.layer || e.target, e.latlng);
|
|
return;
|
|
}
|
|
|
|
// otherwise treat it like a marker and figure out
|
|
// if we should toggle it open/closed
|
|
if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
|
|
this.closePopup();
|
|
} else {
|
|
this.openPopup(layer, e.latlng);
|
|
}
|
|
},
|
|
|
|
_movePopup: function (e) {
|
|
this._popup.setLatLng(e.latlng);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @class Tooltip
|
|
* @inherits DivOverlay
|
|
* @aka L.Tooltip
|
|
* Used to display small texts on top of map layers.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* marker.bindTooltip("my tooltip text").openTooltip();
|
|
* ```
|
|
* Note about tooltip offset. Leaflet takes two options in consideration
|
|
* for computing tooltip offseting:
|
|
* - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
|
|
* Add a positive x offset to move the tooltip to the right, and a positive y offset to
|
|
* move it to the bottom. Negatives will move to the left and top.
|
|
* - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
|
|
* should adapt this value if you use a custom icon.
|
|
*/
|
|
|
|
|
|
// @namespace Tooltip
|
|
L.Tooltip = L.DivOverlay.extend({
|
|
|
|
// @section
|
|
// @aka Tooltip options
|
|
options: {
|
|
// @option pane: String = 'tooltipPane'
|
|
// `Map pane` where the tooltip will be added.
|
|
pane: 'tooltipPane',
|
|
|
|
// @option offset: Point = Point(0, 0)
|
|
// Optional offset of the tooltip position.
|
|
offset: [0, 0],
|
|
|
|
// @option direction: String = 'auto'
|
|
// Direction where to open the tooltip. Possible values are: `right`, `left`,
|
|
// `top`, `bottom`, `center`, `auto`.
|
|
// `auto` will dynamicaly switch between `right` and `left` according to the tooltip
|
|
// position on the map.
|
|
direction: 'auto',
|
|
|
|
// @option permanent: Boolean = false
|
|
// Whether to open the tooltip permanently or only on mouseover.
|
|
permanent: false,
|
|
|
|
// @option sticky: Boolean = false
|
|
// If true, the tooltip will follow the mouse instead of being fixed at the feature center.
|
|
sticky: false,
|
|
|
|
// @option interactive: Boolean = false
|
|
// If true, the tooltip will listen to the feature events.
|
|
interactive: false,
|
|
|
|
// @option opacity: Number = 0.9
|
|
// Tooltip container opacity.
|
|
opacity: 0.9
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
L.DivOverlay.prototype.onAdd.call(this, map);
|
|
this.setOpacity(this.options.opacity);
|
|
|
|
// @namespace Map
|
|
// @section Tooltip events
|
|
// @event tooltipopen: TooltipEvent
|
|
// Fired when a tooltip is opened in the map.
|
|
map.fire('tooltipopen', {tooltip: this});
|
|
|
|
if (this._source) {
|
|
// @namespace Layer
|
|
// @section Tooltip events
|
|
// @event tooltipopen: TooltipEvent
|
|
// Fired when a tooltip bound to this layer is opened.
|
|
this._source.fire('tooltipopen', {tooltip: this}, true);
|
|
}
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
L.DivOverlay.prototype.onRemove.call(this, map);
|
|
|
|
// @namespace Map
|
|
// @section Tooltip events
|
|
// @event tooltipclose: TooltipEvent
|
|
// Fired when a tooltip in the map is closed.
|
|
map.fire('tooltipclose', {tooltip: this});
|
|
|
|
if (this._source) {
|
|
// @namespace Layer
|
|
// @section Tooltip events
|
|
// @event tooltipclose: TooltipEvent
|
|
// Fired when a tooltip bound to this layer is closed.
|
|
this._source.fire('tooltipclose', {tooltip: this}, true);
|
|
}
|
|
},
|
|
|
|
getEvents: function () {
|
|
var events = L.DivOverlay.prototype.getEvents.call(this);
|
|
|
|
if (L.Browser.touch && !this.options.permanent) {
|
|
events.preclick = this._close;
|
|
}
|
|
|
|
return events;
|
|
},
|
|
|
|
_close: function () {
|
|
if (this._map) {
|
|
this._map.closeTooltip(this);
|
|
}
|
|
},
|
|
|
|
_initLayout: function () {
|
|
var prefix = 'leaflet-tooltip',
|
|
className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
|
|
|
|
this._contentNode = this._container = L.DomUtil.create('div', className);
|
|
},
|
|
|
|
_updateLayout: function () {},
|
|
|
|
_adjustPan: function () {},
|
|
|
|
_setPosition: function (pos) {
|
|
var map = this._map,
|
|
container = this._container,
|
|
centerPoint = map.latLngToContainerPoint(map.getCenter()),
|
|
tooltipPoint = map.layerPointToContainerPoint(pos),
|
|
direction = this.options.direction,
|
|
tooltipWidth = container.offsetWidth,
|
|
tooltipHeight = container.offsetHeight,
|
|
offset = L.point(this.options.offset),
|
|
anchor = this._getAnchor();
|
|
|
|
if (direction === 'top') {
|
|
pos = pos.add(L.point(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
|
|
} else if (direction === 'bottom') {
|
|
pos = pos.subtract(L.point(tooltipWidth / 2 - offset.x, -offset.y, true));
|
|
} else if (direction === 'center') {
|
|
pos = pos.subtract(L.point(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
|
|
} else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
|
|
direction = 'right';
|
|
pos = pos.add(L.point(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
|
|
} else {
|
|
direction = 'left';
|
|
pos = pos.subtract(L.point(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
|
|
}
|
|
|
|
L.DomUtil.removeClass(container, 'leaflet-tooltip-right');
|
|
L.DomUtil.removeClass(container, 'leaflet-tooltip-left');
|
|
L.DomUtil.removeClass(container, 'leaflet-tooltip-top');
|
|
L.DomUtil.removeClass(container, 'leaflet-tooltip-bottom');
|
|
L.DomUtil.addClass(container, 'leaflet-tooltip-' + direction);
|
|
L.DomUtil.setPosition(container, pos);
|
|
},
|
|
|
|
_updatePosition: function () {
|
|
var pos = this._map.latLngToLayerPoint(this._latlng);
|
|
this._setPosition(pos);
|
|
},
|
|
|
|
setOpacity: function (opacity) {
|
|
this.options.opacity = opacity;
|
|
|
|
if (this._container) {
|
|
L.DomUtil.setOpacity(this._container, opacity);
|
|
}
|
|
},
|
|
|
|
_animateZoom: function (e) {
|
|
var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
|
|
this._setPosition(pos);
|
|
},
|
|
|
|
_getAnchor: function () {
|
|
// Where should we anchor the tooltip on the source layer?
|
|
return L.point(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
|
|
}
|
|
|
|
});
|
|
|
|
// @namespace Tooltip
|
|
// @factory L.tooltip(options?: Tooltip options, source?: Layer)
|
|
// Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers.
|
|
L.tooltip = function (options, source) {
|
|
return new L.Tooltip(options, source);
|
|
};
|
|
|
|
// @namespace Map
|
|
// @section Methods for Layers and Controls
|
|
L.Map.include({
|
|
|
|
// @method openTooltip(tooltip: Tooltip): this
|
|
// Opens the specified tooltip.
|
|
// @alternative
|
|
// @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
|
|
// Creates a tooltip with the specified content and options and open it.
|
|
openTooltip: function (tooltip, latlng, options) {
|
|
if (!(tooltip instanceof L.Tooltip)) {
|
|
tooltip = new L.Tooltip(options).setContent(tooltip);
|
|
}
|
|
|
|
if (latlng) {
|
|
tooltip.setLatLng(latlng);
|
|
}
|
|
|
|
if (this.hasLayer(tooltip)) {
|
|
return this;
|
|
}
|
|
|
|
return this.addLayer(tooltip);
|
|
},
|
|
|
|
// @method closeTooltip(tooltip?: Tooltip): this
|
|
// Closes the tooltip given as parameter.
|
|
closeTooltip: function (tooltip) {
|
|
if (tooltip) {
|
|
this.removeLayer(tooltip);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
/*
|
|
* @namespace Layer
|
|
* @section Tooltip methods example
|
|
*
|
|
* All layers share a set of methods convenient for binding tooltips to it.
|
|
*
|
|
* ```js
|
|
* var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
|
|
* layer.openTooltip();
|
|
* layer.closeTooltip();
|
|
* ```
|
|
*/
|
|
|
|
// @section Tooltip methods
|
|
L.Layer.include({
|
|
|
|
// @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
|
|
// Binds a tooltip to the layer with the passed `content` and sets up the
|
|
// neccessary event listeners. If a `Function` is passed it will receive
|
|
// the layer as the first argument and should return a `String` or `HTMLElement`.
|
|
bindTooltip: function (content, options) {
|
|
|
|
if (content instanceof L.Tooltip) {
|
|
L.setOptions(content, options);
|
|
this._tooltip = content;
|
|
content._source = this;
|
|
} else {
|
|
if (!this._tooltip || options) {
|
|
this._tooltip = L.tooltip(options, this);
|
|
}
|
|
this._tooltip.setContent(content);
|
|
|
|
}
|
|
|
|
this._initTooltipInteractions();
|
|
|
|
if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
|
|
this.openTooltip();
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method unbindTooltip(): this
|
|
// Removes the tooltip previously bound with `bindTooltip`.
|
|
unbindTooltip: function () {
|
|
if (this._tooltip) {
|
|
this._initTooltipInteractions(true);
|
|
this.closeTooltip();
|
|
this._tooltip = null;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
_initTooltipInteractions: function (remove) {
|
|
if (!remove && this._tooltipHandlersAdded) { return; }
|
|
var onOff = remove ? 'off' : 'on',
|
|
events = {
|
|
remove: this.closeTooltip,
|
|
move: this._moveTooltip
|
|
};
|
|
if (!this._tooltip.options.permanent) {
|
|
events.mouseover = this._openTooltip;
|
|
events.mouseout = this.closeTooltip;
|
|
if (this._tooltip.options.sticky) {
|
|
events.mousemove = this._moveTooltip;
|
|
}
|
|
if (L.Browser.touch) {
|
|
events.click = this._openTooltip;
|
|
}
|
|
} else {
|
|
events.add = this._openTooltip;
|
|
}
|
|
this[onOff](events);
|
|
this._tooltipHandlersAdded = !remove;
|
|
},
|
|
|
|
// @method openTooltip(latlng?: LatLng): this
|
|
// Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed.
|
|
openTooltip: function (layer, latlng) {
|
|
if (!(layer instanceof L.Layer)) {
|
|
latlng = layer;
|
|
layer = this;
|
|
}
|
|
|
|
if (layer instanceof L.FeatureGroup) {
|
|
for (var id in this._layers) {
|
|
layer = this._layers[id];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!latlng) {
|
|
latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
|
|
}
|
|
|
|
if (this._tooltip && this._map) {
|
|
|
|
// set tooltip source to this layer
|
|
this._tooltip._source = layer;
|
|
|
|
// update the tooltip (content, layout, ect...)
|
|
this._tooltip.update();
|
|
|
|
// open the tooltip on the map
|
|
this._map.openTooltip(this._tooltip, latlng);
|
|
|
|
// Tooltip container may not be defined if not permanent and never
|
|
// opened.
|
|
if (this._tooltip.options.interactive && this._tooltip._container) {
|
|
L.DomUtil.addClass(this._tooltip._container, 'leaflet-clickable');
|
|
this.addInteractiveTarget(this._tooltip._container);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method closeTooltip(): this
|
|
// Closes the tooltip bound to this layer if it is open.
|
|
closeTooltip: function () {
|
|
if (this._tooltip) {
|
|
this._tooltip._close();
|
|
if (this._tooltip.options.interactive && this._tooltip._container) {
|
|
L.DomUtil.removeClass(this._tooltip._container, 'leaflet-clickable');
|
|
this.removeInteractiveTarget(this._tooltip._container);
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method toggleTooltip(): this
|
|
// Opens or closes the tooltip bound to this layer depending on its current state.
|
|
toggleTooltip: function (target) {
|
|
if (this._tooltip) {
|
|
if (this._tooltip._map) {
|
|
this.closeTooltip();
|
|
} else {
|
|
this.openTooltip(target);
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method isTooltipOpen(): boolean
|
|
// Returns `true` if the tooltip bound to this layer is currently open.
|
|
isTooltipOpen: function () {
|
|
return this._tooltip.isOpen();
|
|
},
|
|
|
|
// @method setTooltipContent(content: String|HTMLElement|Tooltip): this
|
|
// Sets the content of the tooltip bound to this layer.
|
|
setTooltipContent: function (content) {
|
|
if (this._tooltip) {
|
|
this._tooltip.setContent(content);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method getTooltip(): Tooltip
|
|
// Returns the tooltip bound to this layer.
|
|
getTooltip: function () {
|
|
return this._tooltip;
|
|
},
|
|
|
|
_openTooltip: function (e) {
|
|
var layer = e.layer || e.target;
|
|
|
|
if (!this._tooltip || !this._map) {
|
|
return;
|
|
}
|
|
this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
|
|
},
|
|
|
|
_moveTooltip: function (e) {
|
|
var latlng = e.latlng, containerPoint, layerPoint;
|
|
if (this._tooltip.options.sticky && e.originalEvent) {
|
|
containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
|
|
layerPoint = this._map.containerPointToLayerPoint(containerPoint);
|
|
latlng = this._map.layerPointToLatLng(layerPoint);
|
|
}
|
|
this._tooltip.setLatLng(latlng);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @class LayerGroup
|
|
* @aka L.LayerGroup
|
|
* @inherits Layer
|
|
*
|
|
* Used to group several layers and handle them as one. If you add it to the map,
|
|
* any layers added or removed from the group will be added/removed on the map as
|
|
* well. Extends `Layer`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* L.layerGroup([marker1, marker2])
|
|
* .addLayer(polyline)
|
|
* .addTo(map);
|
|
* ```
|
|
*/
|
|
|
|
L.LayerGroup = L.Layer.extend({
|
|
|
|
initialize: function (layers) {
|
|
this._layers = {};
|
|
|
|
var i, len;
|
|
|
|
if (layers) {
|
|
for (i = 0, len = layers.length; i < len; i++) {
|
|
this.addLayer(layers[i]);
|
|
}
|
|
}
|
|
},
|
|
|
|
// @method addLayer(layer: Layer): this
|
|
// Adds the given layer to the group.
|
|
addLayer: function (layer) {
|
|
var id = this.getLayerId(layer);
|
|
|
|
this._layers[id] = layer;
|
|
|
|
if (this._map) {
|
|
this._map.addLayer(layer);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method removeLayer(layer: Layer): this
|
|
// Removes the given layer from the group.
|
|
// @alternative
|
|
// @method removeLayer(id: Number): this
|
|
// Removes the layer with the given internal ID from the group.
|
|
removeLayer: function (layer) {
|
|
var id = layer in this._layers ? layer : this.getLayerId(layer);
|
|
|
|
if (this._map && this._layers[id]) {
|
|
this._map.removeLayer(this._layers[id]);
|
|
}
|
|
|
|
delete this._layers[id];
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method hasLayer(layer: Layer): Boolean
|
|
// Returns `true` if the given layer is currently added to the group.
|
|
hasLayer: function (layer) {
|
|
return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
|
|
},
|
|
|
|
// @method clearLayers(): this
|
|
// Removes all the layers from the group.
|
|
clearLayers: function () {
|
|
for (var i in this._layers) {
|
|
this.removeLayer(this._layers[i]);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method invoke(methodName: String, …): this
|
|
// Calls `methodName` on every layer contained in this group, passing any
|
|
// additional parameters. Has no effect if the layers contained do not
|
|
// implement `methodName`.
|
|
invoke: function (methodName) {
|
|
var args = Array.prototype.slice.call(arguments, 1),
|
|
i, layer;
|
|
|
|
for (i in this._layers) {
|
|
layer = this._layers[i];
|
|
|
|
if (layer[methodName]) {
|
|
layer[methodName].apply(layer, args);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
for (var i in this._layers) {
|
|
map.addLayer(this._layers[i]);
|
|
}
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
for (var i in this._layers) {
|
|
map.removeLayer(this._layers[i]);
|
|
}
|
|
},
|
|
|
|
// @method eachLayer(fn: Function, context?: Object): this
|
|
// Iterates over the layers of the group, optionally specifying context of the iterator function.
|
|
// ```js
|
|
// group.eachLayer(function (layer) {
|
|
// layer.bindPopup('Hello');
|
|
// });
|
|
// ```
|
|
eachLayer: function (method, context) {
|
|
for (var i in this._layers) {
|
|
method.call(context, this._layers[i]);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method getLayer(id: Number): Layer
|
|
// Returns the layer with the given internal ID.
|
|
getLayer: function (id) {
|
|
return this._layers[id];
|
|
},
|
|
|
|
// @method getLayers(): Layer[]
|
|
// Returns an array of all the layers added to the group.
|
|
getLayers: function () {
|
|
var layers = [];
|
|
|
|
for (var i in this._layers) {
|
|
layers.push(this._layers[i]);
|
|
}
|
|
return layers;
|
|
},
|
|
|
|
// @method setZIndex(zIndex: Number): this
|
|
// Calls `setZIndex` on every layer contained in this group, passing the z-index.
|
|
setZIndex: function (zIndex) {
|
|
return this.invoke('setZIndex', zIndex);
|
|
},
|
|
|
|
// @method getLayerId(layer: Layer): Number
|
|
// Returns the internal ID for a layer
|
|
getLayerId: function (layer) {
|
|
return L.stamp(layer);
|
|
}
|
|
});
|
|
|
|
|
|
// @factory L.layerGroup(layers: Layer[])
|
|
// Create a layer group, optionally given an initial set of layers.
|
|
L.layerGroup = function (layers) {
|
|
return new L.LayerGroup(layers);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class FeatureGroup
|
|
* @aka L.FeatureGroup
|
|
* @inherits LayerGroup
|
|
*
|
|
* Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
|
|
* * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
|
|
* * Events are propagated to the `FeatureGroup`, so if the group has an event
|
|
* handler, it will handle events from any of the layers. This includes mouse events
|
|
* and custom events.
|
|
* * Has `layeradd` and `layerremove` events
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* L.featureGroup([marker1, marker2, polyline])
|
|
* .bindPopup('Hello world!')
|
|
* .on('click', function() { alert('Clicked on a member of the group!'); })
|
|
* .addTo(map);
|
|
* ```
|
|
*/
|
|
|
|
L.FeatureGroup = L.LayerGroup.extend({
|
|
|
|
addLayer: function (layer) {
|
|
if (this.hasLayer(layer)) {
|
|
return this;
|
|
}
|
|
|
|
layer.addEventParent(this);
|
|
|
|
L.LayerGroup.prototype.addLayer.call(this, layer);
|
|
|
|
// @event layeradd: LayerEvent
|
|
// Fired when a layer is added to this `FeatureGroup`
|
|
return this.fire('layeradd', {layer: layer});
|
|
},
|
|
|
|
removeLayer: function (layer) {
|
|
if (!this.hasLayer(layer)) {
|
|
return this;
|
|
}
|
|
if (layer in this._layers) {
|
|
layer = this._layers[layer];
|
|
}
|
|
|
|
layer.removeEventParent(this);
|
|
|
|
L.LayerGroup.prototype.removeLayer.call(this, layer);
|
|
|
|
// @event layerremove: LayerEvent
|
|
// Fired when a layer is removed from this `FeatureGroup`
|
|
return this.fire('layerremove', {layer: layer});
|
|
},
|
|
|
|
// @method setStyle(style: Path options): this
|
|
// Sets the given path options to each layer of the group that has a `setStyle` method.
|
|
setStyle: function (style) {
|
|
return this.invoke('setStyle', style);
|
|
},
|
|
|
|
// @method bringToFront(): this
|
|
// Brings the layer group to the top of all other layers
|
|
bringToFront: function () {
|
|
return this.invoke('bringToFront');
|
|
},
|
|
|
|
// @method bringToBack(): this
|
|
// Brings the layer group to the top of all other layers
|
|
bringToBack: function () {
|
|
return this.invoke('bringToBack');
|
|
},
|
|
|
|
// @method getBounds(): LatLngBounds
|
|
// Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
|
|
getBounds: function () {
|
|
var bounds = new L.LatLngBounds();
|
|
|
|
for (var id in this._layers) {
|
|
var layer = this._layers[id];
|
|
bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
|
|
}
|
|
return bounds;
|
|
}
|
|
});
|
|
|
|
// @factory L.featureGroup(layers: Layer[])
|
|
// Create a feature group, optionally given an initial set of layers.
|
|
L.featureGroup = function (layers) {
|
|
return new L.FeatureGroup(layers);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Renderer
|
|
* @inherits Layer
|
|
* @aka L.Renderer
|
|
*
|
|
* Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
|
|
* DOM container of the renderer, its bounds, and its zoom animation.
|
|
*
|
|
* A `Renderer` works as an implicit layer group for all `Path`s - the renderer
|
|
* itself can be added or removed to the map. All paths use a renderer, which can
|
|
* be implicit (the map will decide the type of renderer and use it automatically)
|
|
* or explicit (using the [`renderer`](#path-renderer) option of the path).
|
|
*
|
|
* Do not use this class directly, use `SVG` and `Canvas` instead.
|
|
*
|
|
* @event update: Event
|
|
* Fired when the renderer updates its bounds, center and zoom, for example when
|
|
* its map has moved
|
|
*/
|
|
|
|
L.Renderer = L.Layer.extend({
|
|
|
|
// @section
|
|
// @aka Renderer options
|
|
options: {
|
|
// @option padding: Number = 0.1
|
|
// How much to extend the clip area around the map view (relative to its size)
|
|
// e.g. 0.1 would be 10% of map view in each direction
|
|
padding: 0.1
|
|
},
|
|
|
|
initialize: function (options) {
|
|
L.setOptions(this, options);
|
|
L.stamp(this);
|
|
this._layers = this._layers || {};
|
|
},
|
|
|
|
onAdd: function () {
|
|
if (!this._container) {
|
|
this._initContainer(); // defined by renderer implementations
|
|
|
|
if (this._zoomAnimated) {
|
|
L.DomUtil.addClass(this._container, 'leaflet-zoom-animated');
|
|
}
|
|
}
|
|
|
|
this.getPane().appendChild(this._container);
|
|
this._update();
|
|
this.on('update', this._updatePaths, this);
|
|
},
|
|
|
|
onRemove: function () {
|
|
L.DomUtil.remove(this._container);
|
|
this.off('update', this._updatePaths, this);
|
|
},
|
|
|
|
getEvents: function () {
|
|
var events = {
|
|
viewreset: this._reset,
|
|
zoom: this._onZoom,
|
|
moveend: this._update,
|
|
zoomend: this._onZoomEnd
|
|
};
|
|
if (this._zoomAnimated) {
|
|
events.zoomanim = this._onAnimZoom;
|
|
}
|
|
return events;
|
|
},
|
|
|
|
_onAnimZoom: function (ev) {
|
|
this._updateTransform(ev.center, ev.zoom);
|
|
},
|
|
|
|
_onZoom: function () {
|
|
this._updateTransform(this._map.getCenter(), this._map.getZoom());
|
|
},
|
|
|
|
_updateTransform: function (center, zoom) {
|
|
var scale = this._map.getZoomScale(zoom, this._zoom),
|
|
position = L.DomUtil.getPosition(this._container),
|
|
viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
|
|
currentCenterPoint = this._map.project(this._center, zoom),
|
|
destCenterPoint = this._map.project(center, zoom),
|
|
centerOffset = destCenterPoint.subtract(currentCenterPoint),
|
|
|
|
topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
|
|
|
|
if (L.Browser.any3d) {
|
|
L.DomUtil.setTransform(this._container, topLeftOffset, scale);
|
|
} else {
|
|
L.DomUtil.setPosition(this._container, topLeftOffset);
|
|
}
|
|
},
|
|
|
|
_reset: function () {
|
|
this._update();
|
|
this._updateTransform(this._center, this._zoom);
|
|
|
|
for (var id in this._layers) {
|
|
this._layers[id]._reset();
|
|
}
|
|
},
|
|
|
|
_onZoomEnd: function () {
|
|
for (var id in this._layers) {
|
|
this._layers[id]._project();
|
|
}
|
|
},
|
|
|
|
_updatePaths: function () {
|
|
for (var id in this._layers) {
|
|
this._layers[id]._update();
|
|
}
|
|
},
|
|
|
|
_update: function () {
|
|
// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
|
|
// Subclasses are responsible of firing the 'update' event.
|
|
var p = this.options.padding,
|
|
size = this._map.getSize(),
|
|
min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
|
|
|
|
this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
|
|
|
|
this._center = this._map.getCenter();
|
|
this._zoom = this._map.getZoom();
|
|
}
|
|
});
|
|
|
|
|
|
L.Map.include({
|
|
// @namespace Map; @method getRenderer(layer: Path): Renderer
|
|
// Returns the instance of `Renderer` that should be used to render the given
|
|
// `Path`. It will ensure that the `renderer` options of the map and paths
|
|
// are respected, and that the renderers do exist on the map.
|
|
getRenderer: function (layer) {
|
|
// @namespace Path; @option renderer: Renderer
|
|
// Use this specific instance of `Renderer` for this path. Takes
|
|
// precedence over the map's [default renderer](#map-renderer).
|
|
var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
|
|
|
|
if (!renderer) {
|
|
// @namespace Map; @option preferCanvas: Boolean = false
|
|
// Whether `Path`s should be rendered on a `Canvas` renderer.
|
|
// By default, all `Path`s are rendered in a `SVG` renderer.
|
|
renderer = this._renderer = (this.options.preferCanvas && L.canvas()) || L.svg();
|
|
}
|
|
|
|
if (!this.hasLayer(renderer)) {
|
|
this.addLayer(renderer);
|
|
}
|
|
return renderer;
|
|
},
|
|
|
|
_getPaneRenderer: function (name) {
|
|
if (name === 'overlayPane' || name === undefined) {
|
|
return false;
|
|
}
|
|
|
|
var renderer = this._paneRenderers[name];
|
|
if (renderer === undefined) {
|
|
renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
|
|
this._paneRenderers[name] = renderer;
|
|
}
|
|
return renderer;
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @class Path
|
|
* @aka L.Path
|
|
* @inherits Interactive layer
|
|
*
|
|
* An abstract class that contains options and constants shared between vector
|
|
* overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
|
|
*/
|
|
|
|
L.Path = L.Layer.extend({
|
|
|
|
// @section
|
|
// @aka Path options
|
|
options: {
|
|
// @option stroke: Boolean = true
|
|
// Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
|
|
stroke: true,
|
|
|
|
// @option color: String = '#3388ff'
|
|
// Stroke color
|
|
color: '#3388ff',
|
|
|
|
// @option weight: Number = 3
|
|
// Stroke width in pixels
|
|
weight: 3,
|
|
|
|
// @option opacity: Number = 1.0
|
|
// Stroke opacity
|
|
opacity: 1,
|
|
|
|
// @option lineCap: String= 'round'
|
|
// A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
|
|
lineCap: 'round',
|
|
|
|
// @option lineJoin: String = 'round'
|
|
// A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
|
|
lineJoin: 'round',
|
|
|
|
// @option dashArray: String = null
|
|
// A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
|
|
dashArray: null,
|
|
|
|
// @option dashOffset: String = null
|
|
// A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
|
|
dashOffset: null,
|
|
|
|
// @option fill: Boolean = depends
|
|
// Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
|
|
fill: false,
|
|
|
|
// @option fillColor: String = *
|
|
// Fill color. Defaults to the value of the [`color`](#path-color) option
|
|
fillColor: null,
|
|
|
|
// @option fillOpacity: Number = 0.2
|
|
// Fill opacity.
|
|
fillOpacity: 0.2,
|
|
|
|
// @option fillRule: String = 'evenodd'
|
|
// A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
|
|
fillRule: 'evenodd',
|
|
|
|
// className: '',
|
|
|
|
// Option inherited from "Interactive layer" abstract class
|
|
interactive: true
|
|
},
|
|
|
|
beforeAdd: function (map) {
|
|
// Renderer is set here because we need to call renderer.getEvents
|
|
// before this.getEvents.
|
|
this._renderer = map.getRenderer(this);
|
|
},
|
|
|
|
onAdd: function () {
|
|
this._renderer._initPath(this);
|
|
this._reset();
|
|
this._renderer._addPath(this);
|
|
},
|
|
|
|
onRemove: function () {
|
|
this._renderer._removePath(this);
|
|
},
|
|
|
|
// @method redraw(): this
|
|
// Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
|
|
redraw: function () {
|
|
if (this._map) {
|
|
this._renderer._updatePath(this);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method setStyle(style: Path options): this
|
|
// Changes the appearance of a Path based on the options in the `Path options` object.
|
|
setStyle: function (style) {
|
|
L.setOptions(this, style);
|
|
if (this._renderer) {
|
|
this._renderer._updateStyle(this);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method bringToFront(): this
|
|
// Brings the layer to the top of all path layers.
|
|
bringToFront: function () {
|
|
if (this._renderer) {
|
|
this._renderer._bringToFront(this);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// @method bringToBack(): this
|
|
// Brings the layer to the bottom of all path layers.
|
|
bringToBack: function () {
|
|
if (this._renderer) {
|
|
this._renderer._bringToBack(this);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
getElement: function () {
|
|
return this._path;
|
|
},
|
|
|
|
_reset: function () {
|
|
// defined in children classes
|
|
this._project();
|
|
this._update();
|
|
},
|
|
|
|
_clickTolerance: function () {
|
|
// used when doing hit detection for Canvas layers
|
|
return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @namespace LineUtil
|
|
*
|
|
* Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast.
|
|
*/
|
|
|
|
L.LineUtil = {
|
|
|
|
// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
|
|
// Improves rendering performance dramatically by lessening the number of points to draw.
|
|
|
|
// @function simplify(points: Point[], tolerance: Number): Point[]
|
|
// Dramatically reduces the number of points in a polyline while retaining
|
|
// its shape and returns a new array of simplified points, using the
|
|
// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
|
|
// Used for a huge performance boost when processing/displaying Leaflet polylines for
|
|
// each zoom level and also reducing visual noise. tolerance affects the amount of
|
|
// simplification (lesser value means higher quality but slower and with more points).
|
|
// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
|
|
simplify: function (points, tolerance) {
|
|
if (!tolerance || !points.length) {
|
|
return points.slice();
|
|
}
|
|
|
|
var sqTolerance = tolerance * tolerance;
|
|
|
|
// stage 1: vertex reduction
|
|
points = this._reducePoints(points, sqTolerance);
|
|
|
|
// stage 2: Douglas-Peucker simplification
|
|
points = this._simplifyDP(points, sqTolerance);
|
|
|
|
return points;
|
|
},
|
|
|
|
// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
|
|
// Returns the distance between point `p` and segment `p1` to `p2`.
|
|
pointToSegmentDistance: function (p, p1, p2) {
|
|
return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
|
|
},
|
|
|
|
// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
|
|
// Returns the closest point from a point `p` on a segment `p1` to `p2`.
|
|
closestPointOnSegment: function (p, p1, p2) {
|
|
return this._sqClosestPointOnSegment(p, p1, p2);
|
|
},
|
|
|
|
// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
|
|
_simplifyDP: function (points, sqTolerance) {
|
|
|
|
var len = points.length,
|
|
ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
|
|
markers = new ArrayConstructor(len);
|
|
|
|
markers[0] = markers[len - 1] = 1;
|
|
|
|
this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
|
|
|
|
var i,
|
|
newPoints = [];
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (markers[i]) {
|
|
newPoints.push(points[i]);
|
|
}
|
|
}
|
|
|
|
return newPoints;
|
|
},
|
|
|
|
_simplifyDPStep: function (points, markers, sqTolerance, first, last) {
|
|
|
|
var maxSqDist = 0,
|
|
index, i, sqDist;
|
|
|
|
for (i = first + 1; i <= last - 1; i++) {
|
|
sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
|
|
|
|
if (sqDist > maxSqDist) {
|
|
index = i;
|
|
maxSqDist = sqDist;
|
|
}
|
|
}
|
|
|
|
if (maxSqDist > sqTolerance) {
|
|
markers[index] = 1;
|
|
|
|
this._simplifyDPStep(points, markers, sqTolerance, first, index);
|
|
this._simplifyDPStep(points, markers, sqTolerance, index, last);
|
|
}
|
|
},
|
|
|
|
// reduce points that are too close to each other to a single point
|
|
_reducePoints: function (points, sqTolerance) {
|
|
var reducedPoints = [points[0]];
|
|
|
|
for (var i = 1, prev = 0, len = points.length; i < len; i++) {
|
|
if (this._sqDist(points[i], points[prev]) > sqTolerance) {
|
|
reducedPoints.push(points[i]);
|
|
prev = i;
|
|
}
|
|
}
|
|
if (prev < len - 1) {
|
|
reducedPoints.push(points[len - 1]);
|
|
}
|
|
return reducedPoints;
|
|
},
|
|
|
|
|
|
// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
|
|
// Clips the segment a to b by rectangular bounds with the
|
|
// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
|
|
// (modifying the segment points directly!). Used by Leaflet to only show polyline
|
|
// points that are on the screen or near, increasing performance.
|
|
clipSegment: function (a, b, bounds, useLastCode, round) {
|
|
var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
|
|
codeB = this._getBitCode(b, bounds),
|
|
|
|
codeOut, p, newCode;
|
|
|
|
// save 2nd code to avoid calculating it on the next segment
|
|
this._lastCode = codeB;
|
|
|
|
while (true) {
|
|
// if a,b is inside the clip window (trivial accept)
|
|
if (!(codeA | codeB)) {
|
|
return [a, b];
|
|
}
|
|
|
|
// if a,b is outside the clip window (trivial reject)
|
|
if (codeA & codeB) {
|
|
return false;
|
|
}
|
|
|
|
// other cases
|
|
codeOut = codeA || codeB;
|
|
p = this._getEdgeIntersection(a, b, codeOut, bounds, round);
|
|
newCode = this._getBitCode(p, bounds);
|
|
|
|
if (codeOut === codeA) {
|
|
a = p;
|
|
codeA = newCode;
|
|
} else {
|
|
b = p;
|
|
codeB = newCode;
|
|
}
|
|
}
|
|
},
|
|
|
|
_getEdgeIntersection: function (a, b, code, bounds, round) {
|
|
var dx = b.x - a.x,
|
|
dy = b.y - a.y,
|
|
min = bounds.min,
|
|
max = bounds.max,
|
|
x, y;
|
|
|
|
if (code & 8) { // top
|
|
x = a.x + dx * (max.y - a.y) / dy;
|
|
y = max.y;
|
|
|
|
} else if (code & 4) { // bottom
|
|
x = a.x + dx * (min.y - a.y) / dy;
|
|
y = min.y;
|
|
|
|
} else if (code & 2) { // right
|
|
x = max.x;
|
|
y = a.y + dy * (max.x - a.x) / dx;
|
|
|
|
} else if (code & 1) { // left
|
|
x = min.x;
|
|
y = a.y + dy * (min.x - a.x) / dx;
|
|
}
|
|
|
|
return new L.Point(x, y, round);
|
|
},
|
|
|
|
_getBitCode: function (p, bounds) {
|
|
var code = 0;
|
|
|
|
if (p.x < bounds.min.x) { // left
|
|
code |= 1;
|
|
} else if (p.x > bounds.max.x) { // right
|
|
code |= 2;
|
|
}
|
|
|
|
if (p.y < bounds.min.y) { // bottom
|
|
code |= 4;
|
|
} else if (p.y > bounds.max.y) { // top
|
|
code |= 8;
|
|
}
|
|
|
|
return code;
|
|
},
|
|
|
|
// square distance (to avoid unnecessary Math.sqrt calls)
|
|
_sqDist: function (p1, p2) {
|
|
var dx = p2.x - p1.x,
|
|
dy = p2.y - p1.y;
|
|
return dx * dx + dy * dy;
|
|
},
|
|
|
|
// return closest point on segment or distance to that point
|
|
_sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
|
|
var x = p1.x,
|
|
y = p1.y,
|
|
dx = p2.x - x,
|
|
dy = p2.y - y,
|
|
dot = dx * dx + dy * dy,
|
|
t;
|
|
|
|
if (dot > 0) {
|
|
t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
|
|
|
|
if (t > 1) {
|
|
x = p2.x;
|
|
y = p2.y;
|
|
} else if (t > 0) {
|
|
x += dx * t;
|
|
y += dy * t;
|
|
}
|
|
}
|
|
|
|
dx = p.x - x;
|
|
dy = p.y - y;
|
|
|
|
return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Polyline
|
|
* @aka L.Polyline
|
|
* @inherits Path
|
|
*
|
|
* A class for drawing polyline overlays on a map. Extends `Path`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* // create a red polyline from an array of LatLng points
|
|
* var latlngs = [
|
|
* [-122.68, 45.51],
|
|
* [-122.43, 37.77],
|
|
* [-118.2, 34.04]
|
|
* ];
|
|
*
|
|
* var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
|
|
*
|
|
* // zoom the map to the polyline
|
|
* map.fitBounds(polyline.getBounds());
|
|
* ```
|
|
*
|
|
* You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
|
|
*
|
|
* ```js
|
|
* // create a red polyline from an array of arrays of LatLng points
|
|
* var latlngs = [
|
|
* [[-122.68, 45.51],
|
|
* [-122.43, 37.77],
|
|
* [-118.2, 34.04]],
|
|
* [[-73.91, 40.78],
|
|
* [-87.62, 41.83],
|
|
* [-96.72, 32.76]]
|
|
* ];
|
|
* ```
|
|
*/
|
|
|
|
L.Polyline = L.Path.extend({
|
|
|
|
// @section
|
|
// @aka Polyline options
|
|
options: {
|
|
// @option smoothFactor: Number = 1.0
|
|
// How much to simplify the polyline on each zoom level. More means
|
|
// better performance and smoother look, and less means more accurate representation.
|
|
smoothFactor: 1.0,
|
|
|
|
// @option noClip: Boolean = false
|
|
// Disable polyline clipping.
|
|
noClip: false
|
|
},
|
|
|
|
initialize: function (latlngs, options) {
|
|
L.setOptions(this, options);
|
|
this._setLatLngs(latlngs);
|
|
},
|
|
|
|
// @method getLatLngs(): LatLng[]
|
|
// Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
|
|
getLatLngs: function () {
|
|
return this._latlngs;
|
|
},
|
|
|
|
// @method setLatLngs(latlngs: LatLng[]): this
|
|
// Replaces all the points in the polyline with the given array of geographical points.
|
|
setLatLngs: function (latlngs) {
|
|
this._setLatLngs(latlngs);
|
|
return this.redraw();
|
|
},
|
|
|
|
// @method isEmpty(): Boolean
|
|
// Returns `true` if the Polyline has no LatLngs.
|
|
isEmpty: function () {
|
|
return !this._latlngs.length;
|
|
},
|
|
|
|
closestLayerPoint: function (p) {
|
|
var minDistance = Infinity,
|
|
minPoint = null,
|
|
closest = L.LineUtil._sqClosestPointOnSegment,
|
|
p1, p2;
|
|
|
|
for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
|
|
var points = this._parts[j];
|
|
|
|
for (var i = 1, len = points.length; i < len; i++) {
|
|
p1 = points[i - 1];
|
|
p2 = points[i];
|
|
|
|
var sqDist = closest(p, p1, p2, true);
|
|
|
|
if (sqDist < minDistance) {
|
|
minDistance = sqDist;
|
|
minPoint = closest(p, p1, p2);
|
|
}
|
|
}
|
|
}
|
|
if (minPoint) {
|
|
minPoint.distance = Math.sqrt(minDistance);
|
|
}
|
|
return minPoint;
|
|
},
|
|
|
|
// @method getCenter(): LatLng
|
|
// Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
|
|
getCenter: function () {
|
|
// throws error when not yet added to map as this center calculation requires projected coordinates
|
|
if (!this._map) {
|
|
throw new Error('Must add layer to map before using getCenter()');
|
|
}
|
|
|
|
var i, halfDist, segDist, dist, p1, p2, ratio,
|
|
points = this._rings[0],
|
|
len = points.length;
|
|
|
|
if (!len) { return null; }
|
|
|
|
// polyline centroid algorithm; only uses the first ring if there are multiple
|
|
|
|
for (i = 0, halfDist = 0; i < len - 1; i++) {
|
|
halfDist += points[i].distanceTo(points[i + 1]) / 2;
|
|
}
|
|
|
|
// The line is so small in the current view that all points are on the same pixel.
|
|
if (halfDist === 0) {
|
|
return this._map.layerPointToLatLng(points[0]);
|
|
}
|
|
|
|
for (i = 0, dist = 0; i < len - 1; i++) {
|
|
p1 = points[i];
|
|
p2 = points[i + 1];
|
|
segDist = p1.distanceTo(p2);
|
|
dist += segDist;
|
|
|
|
if (dist > halfDist) {
|
|
ratio = (dist - halfDist) / segDist;
|
|
return this._map.layerPointToLatLng([
|
|
p2.x - ratio * (p2.x - p1.x),
|
|
p2.y - ratio * (p2.y - p1.y)
|
|
]);
|
|
}
|
|
}
|
|
},
|
|
|
|
// @method getBounds(): LatLngBounds
|
|
// Returns the `LatLngBounds` of the path.
|
|
getBounds: function () {
|
|
return this._bounds;
|
|
},
|
|
|
|
// @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
|
|
// Adds a given point to the polyline. By default, adds to the first ring of
|
|
// the polyline in case of a multi-polyline, but can be overridden by passing
|
|
// a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
|
|
addLatLng: function (latlng, latlngs) {
|
|
latlngs = latlngs || this._defaultShape();
|
|
latlng = L.latLng(latlng);
|
|
latlngs.push(latlng);
|
|
this._bounds.extend(latlng);
|
|
return this.redraw();
|
|
},
|
|
|
|
_setLatLngs: function (latlngs) {
|
|
this._bounds = new L.LatLngBounds();
|
|
this._latlngs = this._convertLatLngs(latlngs);
|
|
},
|
|
|
|
_defaultShape: function () {
|
|
return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];
|
|
},
|
|
|
|
// recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
|
|
_convertLatLngs: function (latlngs) {
|
|
var result = [],
|
|
flat = L.Polyline._flat(latlngs);
|
|
|
|
for (var i = 0, len = latlngs.length; i < len; i++) {
|
|
if (flat) {
|
|
result[i] = L.latLng(latlngs[i]);
|
|
this._bounds.extend(result[i]);
|
|
} else {
|
|
result[i] = this._convertLatLngs(latlngs[i]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
|
|
_project: function () {
|
|
var pxBounds = new L.Bounds();
|
|
this._rings = [];
|
|
this._projectLatlngs(this._latlngs, this._rings, pxBounds);
|
|
|
|
var w = this._clickTolerance(),
|
|
p = new L.Point(w, w);
|
|
|
|
if (this._bounds.isValid() && pxBounds.isValid()) {
|
|
pxBounds.min._subtract(p);
|
|
pxBounds.max._add(p);
|
|
this._pxBounds = pxBounds;
|
|
}
|
|
},
|
|
|
|
// recursively turns latlngs into a set of rings with projected coordinates
|
|
_projectLatlngs: function (latlngs, result, projectedBounds) {
|
|
var flat = latlngs[0] instanceof L.LatLng,
|
|
len = latlngs.length,
|
|
i, ring;
|
|
|
|
if (flat) {
|
|
ring = [];
|
|
for (i = 0; i < len; i++) {
|
|
ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
|
|
projectedBounds.extend(ring[i]);
|
|
}
|
|
result.push(ring);
|
|
} else {
|
|
for (i = 0; i < len; i++) {
|
|
this._projectLatlngs(latlngs[i], result, projectedBounds);
|
|
}
|
|
}
|
|
},
|
|
|
|
// clip polyline by renderer bounds so that we have less to render for performance
|
|
_clipPoints: function () {
|
|
var bounds = this._renderer._bounds;
|
|
|
|
this._parts = [];
|
|
if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
|
|
return;
|
|
}
|
|
|
|
if (this.options.noClip) {
|
|
this._parts = this._rings;
|
|
return;
|
|
}
|
|
|
|
var parts = this._parts,
|
|
i, j, k, len, len2, segment, points;
|
|
|
|
for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
|
|
points = this._rings[i];
|
|
|
|
for (j = 0, len2 = points.length; j < len2 - 1; j++) {
|
|
segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j, true);
|
|
|
|
if (!segment) { continue; }
|
|
|
|
parts[k] = parts[k] || [];
|
|
parts[k].push(segment[0]);
|
|
|
|
// if segment goes out of screen, or it's the last one, it's the end of the line part
|
|
if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
|
|
parts[k].push(segment[1]);
|
|
k++;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// simplify each clipped part of the polyline for performance
|
|
_simplifyPoints: function () {
|
|
var parts = this._parts,
|
|
tolerance = this.options.smoothFactor;
|
|
|
|
for (var i = 0, len = parts.length; i < len; i++) {
|
|
parts[i] = L.LineUtil.simplify(parts[i], tolerance);
|
|
}
|
|
},
|
|
|
|
_update: function () {
|
|
if (!this._map) { return; }
|
|
|
|
this._clipPoints();
|
|
this._simplifyPoints();
|
|
this._updatePath();
|
|
},
|
|
|
|
_updatePath: function () {
|
|
this._renderer._updatePoly(this);
|
|
}
|
|
});
|
|
|
|
// @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
|
|
// Instantiates a polyline object given an array of geographical points and
|
|
// optionally an options object. You can create a `Polyline` object with
|
|
// multiple separate lines (`MultiPolyline`) by passing an array of arrays
|
|
// of geographic points.
|
|
L.polyline = function (latlngs, options) {
|
|
return new L.Polyline(latlngs, options);
|
|
};
|
|
|
|
L.Polyline._flat = function (latlngs) {
|
|
// true if it's a flat array of latlngs; false if nested
|
|
return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @namespace PolyUtil
|
|
* Various utility functions for polygon geometries.
|
|
*/
|
|
|
|
L.PolyUtil = {};
|
|
|
|
/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
|
|
* Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
|
|
* Used by Leaflet to only show polygon points that are on the screen or near, increasing
|
|
* performance. Note that polygon points needs different algorithm for clipping
|
|
* than polyline, so there's a seperate method for it.
|
|
*/
|
|
L.PolyUtil.clipPolygon = function (points, bounds, round) {
|
|
var clippedPoints,
|
|
edges = [1, 4, 2, 8],
|
|
i, j, k,
|
|
a, b,
|
|
len, edge, p,
|
|
lu = L.LineUtil;
|
|
|
|
for (i = 0, len = points.length; i < len; i++) {
|
|
points[i]._code = lu._getBitCode(points[i], bounds);
|
|
}
|
|
|
|
// for each edge (left, bottom, right, top)
|
|
for (k = 0; k < 4; k++) {
|
|
edge = edges[k];
|
|
clippedPoints = [];
|
|
|
|
for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
|
|
a = points[i];
|
|
b = points[j];
|
|
|
|
// if a is inside the clip window
|
|
if (!(a._code & edge)) {
|
|
// if b is outside the clip window (a->b goes out of screen)
|
|
if (b._code & edge) {
|
|
p = lu._getEdgeIntersection(b, a, edge, bounds, round);
|
|
p._code = lu._getBitCode(p, bounds);
|
|
clippedPoints.push(p);
|
|
}
|
|
clippedPoints.push(a);
|
|
|
|
// else if b is inside the clip window (a->b enters the screen)
|
|
} else if (!(b._code & edge)) {
|
|
p = lu._getEdgeIntersection(b, a, edge, bounds, round);
|
|
p._code = lu._getBitCode(p, bounds);
|
|
clippedPoints.push(p);
|
|
}
|
|
}
|
|
points = clippedPoints;
|
|
}
|
|
|
|
return points;
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Polygon
|
|
* @aka L.Polygon
|
|
* @inherits Polyline
|
|
*
|
|
* A class for drawing polygon overlays on a map. Extends `Polyline`.
|
|
*
|
|
* Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points.
|
|
*
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* // create a red polygon from an array of LatLng points
|
|
* var latlngs = [[-111.03, 41],[-111.04, 45],[-104.05, 45],[-104.05, 41]];
|
|
*
|
|
* var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
|
|
*
|
|
* // zoom the map to the polygon
|
|
* map.fitBounds(polygon.getBounds());
|
|
* ```
|
|
*
|
|
* You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape:
|
|
*
|
|
* ```js
|
|
* var latlngs = [
|
|
* [[-111.03, 41],[-111.04, 45],[-104.05, 45],[-104.05, 41]], // outer ring
|
|
* [[-108.58,37.29],[-108.58,40.71],[-102.50,40.71],[-102.50,37.29]] // hole
|
|
* ];
|
|
* ```
|
|
*
|
|
* Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
|
|
*
|
|
* ```js
|
|
* var latlngs = [
|
|
* [ // first polygon
|
|
* [[-111.03, 41],[-111.04, 45],[-104.05, 45],[-104.05, 41]], // outer ring
|
|
* [[-108.58,37.29],[-108.58,40.71],[-102.50,40.71],[-102.50,37.29]] // hole
|
|
* ],
|
|
* [ // second polygon
|
|
* [[-109.05, 37],[-109.03, 41],[-102.05, 41],[-102.04, 37],[-109.05, 38]]
|
|
* ]
|
|
* ];
|
|
* ```
|
|
*/
|
|
|
|
L.Polygon = L.Polyline.extend({
|
|
|
|
options: {
|
|
fill: true
|
|
},
|
|
|
|
isEmpty: function () {
|
|
return !this._latlngs.length || !this._latlngs[0].length;
|
|
},
|
|
|
|
getCenter: function () {
|
|
// throws error when not yet added to map as this center calculation requires projected coordinates
|
|
if (!this._map) {
|
|
throw new Error('Must add layer to map before using getCenter()');
|
|
}
|
|
|
|
var i, j, p1, p2, f, area, x, y, center,
|
|
points = this._rings[0],
|
|
len = points.length;
|
|
|
|
if (!len) { return null; }
|
|
|
|
// polygon centroid algorithm; only uses the first ring if there are multiple
|
|
|
|
area = x = y = 0;
|
|
|
|
for (i = 0, j = len - 1; i < len; j = i++) {
|
|
p1 = points[i];
|
|
p2 = points[j];
|
|
|
|
f = p1.y * p2.x - p2.y * p1.x;
|
|
x += (p1.x + p2.x) * f;
|
|
y += (p1.y + p2.y) * f;
|
|
area += f * 3;
|
|
}
|
|
|
|
if (area === 0) {
|
|
// Polygon is so small that all points are on same pixel.
|
|
center = points[0];
|
|
} else {
|
|
center = [x / area, y / area];
|
|
}
|
|
return this._map.layerPointToLatLng(center);
|
|
},
|
|
|
|
_convertLatLngs: function (latlngs) {
|
|
var result = L.Polyline.prototype._convertLatLngs.call(this, latlngs),
|
|
len = result.length;
|
|
|
|
// remove last point if it equals first one
|
|
if (len >= 2 && result[0] instanceof L.LatLng && result[0].equals(result[len - 1])) {
|
|
result.pop();
|
|
}
|
|
return result;
|
|
},
|
|
|
|
_setLatLngs: function (latlngs) {
|
|
L.Polyline.prototype._setLatLngs.call(this, latlngs);
|
|
if (L.Polyline._flat(this._latlngs)) {
|
|
this._latlngs = [this._latlngs];
|
|
}
|
|
},
|
|
|
|
_defaultShape: function () {
|
|
return L.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
|
|
},
|
|
|
|
_clipPoints: function () {
|
|
// polygons need a different clipping algorithm so we redefine that
|
|
|
|
var bounds = this._renderer._bounds,
|
|
w = this.options.weight,
|
|
p = new L.Point(w, w);
|
|
|
|
// increase clip padding by stroke width to avoid stroke on clip edges
|
|
bounds = new L.Bounds(bounds.min.subtract(p), bounds.max.add(p));
|
|
|
|
this._parts = [];
|
|
if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
|
|
return;
|
|
}
|
|
|
|
if (this.options.noClip) {
|
|
this._parts = this._rings;
|
|
return;
|
|
}
|
|
|
|
for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
|
|
clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds, true);
|
|
if (clipped.length) {
|
|
this._parts.push(clipped);
|
|
}
|
|
}
|
|
},
|
|
|
|
_updatePath: function () {
|
|
this._renderer._updatePoly(this, true);
|
|
}
|
|
});
|
|
|
|
|
|
// @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
|
|
L.polygon = function (latlngs, options) {
|
|
return new L.Polygon(latlngs, options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
|
|
*/
|
|
|
|
/*
|
|
* @class Rectangle
|
|
* @aka L.Retangle
|
|
* @inherits Polygon
|
|
*
|
|
* A class for drawing rectangle overlays on a map. Extends `Polygon`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* // define rectangle geographical bounds
|
|
* var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
|
|
*
|
|
* // create an orange rectangle
|
|
* L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
|
|
*
|
|
* // zoom the map to the rectangle bounds
|
|
* map.fitBounds(bounds);
|
|
* ```
|
|
*
|
|
*/
|
|
|
|
|
|
L.Rectangle = L.Polygon.extend({
|
|
initialize: function (latLngBounds, options) {
|
|
L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
|
|
},
|
|
|
|
// @method setBounds(latLngBounds: LatLngBounds): this
|
|
// Redraws the rectangle with the passed bounds.
|
|
setBounds: function (latLngBounds) {
|
|
return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
|
|
},
|
|
|
|
_boundsToLatLngs: function (latLngBounds) {
|
|
latLngBounds = L.latLngBounds(latLngBounds);
|
|
return [
|
|
latLngBounds.getSouthWest(),
|
|
latLngBounds.getNorthWest(),
|
|
latLngBounds.getNorthEast(),
|
|
latLngBounds.getSouthEast()
|
|
];
|
|
}
|
|
});
|
|
|
|
|
|
// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
|
|
L.rectangle = function (latLngBounds, options) {
|
|
return new L.Rectangle(latLngBounds, options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class CircleMarker
|
|
* @aka L.CircleMarker
|
|
* @inherits Path
|
|
*
|
|
* A circle of a fixed size with radius specified in pixels. Extends `Path`.
|
|
*/
|
|
|
|
L.CircleMarker = L.Path.extend({
|
|
|
|
// @section
|
|
// @aka CircleMarker options
|
|
options: {
|
|
fill: true,
|
|
|
|
// @option radius: Number = 10
|
|
// Radius of the circle marker, in pixels
|
|
radius: 10
|
|
},
|
|
|
|
initialize: function (latlng, options) {
|
|
L.setOptions(this, options);
|
|
this._latlng = L.latLng(latlng);
|
|
this._radius = this.options.radius;
|
|
},
|
|
|
|
// @method setLatLng(latLng: LatLng): this
|
|
// Sets the position of a circle marker to a new location.
|
|
setLatLng: function (latlng) {
|
|
this._latlng = L.latLng(latlng);
|
|
this.redraw();
|
|
return this.fire('move', {latlng: this._latlng});
|
|
},
|
|
|
|
// @method getLatLng(): LatLng
|
|
// Returns the current geographical position of the circle marker
|
|
getLatLng: function () {
|
|
return this._latlng;
|
|
},
|
|
|
|
// @method setRadius(radius: Number): this
|
|
// Sets the radius of a circle marker. Units are in pixels.
|
|
setRadius: function (radius) {
|
|
this.options.radius = this._radius = radius;
|
|
return this.redraw();
|
|
},
|
|
|
|
// @method getRadius(): Number
|
|
// Returns the current radius of the circle
|
|
getRadius: function () {
|
|
return this._radius;
|
|
},
|
|
|
|
setStyle : function (options) {
|
|
var radius = options && options.radius || this._radius;
|
|
L.Path.prototype.setStyle.call(this, options);
|
|
this.setRadius(radius);
|
|
return this;
|
|
},
|
|
|
|
_project: function () {
|
|
this._point = this._map.latLngToLayerPoint(this._latlng);
|
|
this._updateBounds();
|
|
},
|
|
|
|
_updateBounds: function () {
|
|
var r = this._radius,
|
|
r2 = this._radiusY || r,
|
|
w = this._clickTolerance(),
|
|
p = [r + w, r2 + w];
|
|
this._pxBounds = new L.Bounds(this._point.subtract(p), this._point.add(p));
|
|
},
|
|
|
|
_update: function () {
|
|
if (this._map) {
|
|
this._updatePath();
|
|
}
|
|
},
|
|
|
|
_updatePath: function () {
|
|
this._renderer._updateCircle(this);
|
|
},
|
|
|
|
_empty: function () {
|
|
return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
|
|
}
|
|
});
|
|
|
|
|
|
// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
|
|
// Instantiates a circle marker object given a geographical point, and an optional options object.
|
|
L.circleMarker = function (latlng, options) {
|
|
return new L.CircleMarker(latlng, options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Circle
|
|
* @aka L.Circle
|
|
* @inherits CircleMarker
|
|
*
|
|
* A class for drawing circle overlays on a map. Extends `CircleMarker`.
|
|
*
|
|
* It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* L.circle([50.5, 30.5], {radius: 200}).addTo(map);
|
|
* ```
|
|
*/
|
|
|
|
L.Circle = L.CircleMarker.extend({
|
|
|
|
initialize: function (latlng, options, legacyOptions) {
|
|
if (typeof options === 'number') {
|
|
// Backwards compatibility with 0.7.x factory (latlng, radius, options?)
|
|
options = L.extend({}, legacyOptions, {radius: options});
|
|
}
|
|
L.setOptions(this, options);
|
|
this._latlng = L.latLng(latlng);
|
|
|
|
if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
|
|
|
|
// @section
|
|
// @aka Circle options
|
|
// @option radius: Number; Radius of the circle, in meters.
|
|
this._mRadius = this.options.radius;
|
|
},
|
|
|
|
// @method setRadius(radius: Number): this
|
|
// Sets the radius of a circle. Units are in meters.
|
|
setRadius: function (radius) {
|
|
this._mRadius = radius;
|
|
return this.redraw();
|
|
},
|
|
|
|
// @method getRadius(): Number
|
|
// Returns the current radius of a circle. Units are in meters.
|
|
getRadius: function () {
|
|
return this._mRadius;
|
|
},
|
|
|
|
// @method getBounds(): LatLngBounds
|
|
// Returns the `LatLngBounds` of the path.
|
|
getBounds: function () {
|
|
var half = [this._radius, this._radiusY || this._radius];
|
|
|
|
return new L.LatLngBounds(
|
|
this._map.layerPointToLatLng(this._point.subtract(half)),
|
|
this._map.layerPointToLatLng(this._point.add(half)));
|
|
},
|
|
|
|
setStyle: L.Path.prototype.setStyle,
|
|
|
|
_project: function () {
|
|
|
|
var lng = this._latlng.lng,
|
|
lat = this._latlng.lat,
|
|
map = this._map,
|
|
crs = map.options.crs;
|
|
|
|
if (crs.distance === L.CRS.Earth.distance) {
|
|
var d = Math.PI / 180,
|
|
latR = (this._mRadius / L.CRS.Earth.R) / d,
|
|
top = map.project([lat + latR, lng]),
|
|
bottom = map.project([lat - latR, lng]),
|
|
p = top.add(bottom).divideBy(2),
|
|
lat2 = map.unproject(p).lat,
|
|
lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
|
|
(Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
|
|
|
|
if (isNaN(lngR) || lngR === 0) {
|
|
lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
|
|
}
|
|
|
|
this._point = p.subtract(map.getPixelOrigin());
|
|
this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1);
|
|
this._radiusY = Math.max(Math.round(p.y - top.y), 1);
|
|
|
|
} else {
|
|
var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
|
|
|
|
this._point = map.latLngToLayerPoint(this._latlng);
|
|
this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
|
|
}
|
|
|
|
this._updateBounds();
|
|
}
|
|
});
|
|
|
|
// @factory L.circle(latlng: LatLng, options?: Circle options)
|
|
// Instantiates a circle object given a geographical point, and an options object
|
|
// which contains the circle radius.
|
|
// @alternative
|
|
// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
|
|
// Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
|
|
// Do not use in new applications or plugins.
|
|
L.circle = function (latlng, options, legacyOptions) {
|
|
return new L.Circle(latlng, options, legacyOptions);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class SVG
|
|
* @inherits Renderer
|
|
* @aka L.SVG
|
|
*
|
|
* Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
|
|
* Inherits `Renderer`.
|
|
*
|
|
* Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
|
|
* available in all web browsers, notably Android 2.x and 3.x.
|
|
*
|
|
* Although SVG is not available on IE7 and IE8, these browsers support
|
|
* [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
|
|
* (a now deprecated technology), and the SVG renderer will fall back to VML in
|
|
* this case.
|
|
*
|
|
* @example
|
|
*
|
|
* Use SVG by default for all paths in the map:
|
|
*
|
|
* ```js
|
|
* var map = L.map('map', {
|
|
* renderer: L.svg()
|
|
* });
|
|
* ```
|
|
*
|
|
* Use a SVG renderer with extra padding for specific vector geometries:
|
|
*
|
|
* ```js
|
|
* var map = L.map('map');
|
|
* var myRenderer = L.svg({ padding: 0.5 });
|
|
* var line = L.polyline( coordinates, { renderer: myRenderer } );
|
|
* var circle = L.circle( center, { renderer: myRenderer } );
|
|
* ```
|
|
*/
|
|
|
|
L.SVG = L.Renderer.extend({
|
|
|
|
getEvents: function () {
|
|
var events = L.Renderer.prototype.getEvents.call(this);
|
|
events.zoomstart = this._onZoomStart;
|
|
return events;
|
|
},
|
|
|
|
_initContainer: function () {
|
|
this._container = L.SVG.create('svg');
|
|
|
|
// makes it possible to click through svg root; we'll reset it back in individual paths
|
|
this._container.setAttribute('pointer-events', 'none');
|
|
|
|
this._rootGroup = L.SVG.create('g');
|
|
this._container.appendChild(this._rootGroup);
|
|
},
|
|
|
|
_onZoomStart: function () {
|
|
// Drag-then-pinch interactions might mess up the center and zoom.
|
|
// In this case, the easiest way to prevent this is re-do the renderer
|
|
// bounds and padding when the zooming starts.
|
|
this._update();
|
|
},
|
|
|
|
_update: function () {
|
|
if (this._map._animatingZoom && this._bounds) { return; }
|
|
|
|
L.Renderer.prototype._update.call(this);
|
|
|
|
var b = this._bounds,
|
|
size = b.getSize(),
|
|
container = this._container;
|
|
|
|
// set size of svg-container if changed
|
|
if (!this._svgSize || !this._svgSize.equals(size)) {
|
|
this._svgSize = size;
|
|
container.setAttribute('width', size.x);
|
|
container.setAttribute('height', size.y);
|
|
}
|
|
|
|
// movement: update container viewBox so that we don't have to change coordinates of individual layers
|
|
L.DomUtil.setPosition(container, b.min);
|
|
container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
|
|
|
|
this.fire('update');
|
|
},
|
|
|
|
// methods below are called by vector layers implementations
|
|
|
|
_initPath: function (layer) {
|
|
var path = layer._path = L.SVG.create('path');
|
|
|
|
// @namespace Path
|
|
// @option className: String = null
|
|
// Custom class name set on an element. Only for SVG renderer.
|
|
if (layer.options.className) {
|
|
L.DomUtil.addClass(path, layer.options.className);
|
|
}
|
|
|
|
if (layer.options.interactive) {
|
|
L.DomUtil.addClass(path, 'leaflet-interactive');
|
|
}
|
|
|
|
this._updateStyle(layer);
|
|
this._layers[L.stamp(layer)] = layer;
|
|
},
|
|
|
|
_addPath: function (layer) {
|
|
this._rootGroup.appendChild(layer._path);
|
|
layer.addInteractiveTarget(layer._path);
|
|
},
|
|
|
|
_removePath: function (layer) {
|
|
L.DomUtil.remove(layer._path);
|
|
layer.removeInteractiveTarget(layer._path);
|
|
delete this._layers[L.stamp(layer)];
|
|
},
|
|
|
|
_updatePath: function (layer) {
|
|
layer._project();
|
|
layer._update();
|
|
},
|
|
|
|
_updateStyle: function (layer) {
|
|
var path = layer._path,
|
|
options = layer.options;
|
|
|
|
if (!path) { return; }
|
|
|
|
if (options.stroke) {
|
|
path.setAttribute('stroke', options.color);
|
|
path.setAttribute('stroke-opacity', options.opacity);
|
|
path.setAttribute('stroke-width', options.weight);
|
|
path.setAttribute('stroke-linecap', options.lineCap);
|
|
path.setAttribute('stroke-linejoin', options.lineJoin);
|
|
|
|
if (options.dashArray) {
|
|
path.setAttribute('stroke-dasharray', options.dashArray);
|
|
} else {
|
|
path.removeAttribute('stroke-dasharray');
|
|
}
|
|
|
|
if (options.dashOffset) {
|
|
path.setAttribute('stroke-dashoffset', options.dashOffset);
|
|
} else {
|
|
path.removeAttribute('stroke-dashoffset');
|
|
}
|
|
} else {
|
|
path.setAttribute('stroke', 'none');
|
|
}
|
|
|
|
if (options.fill) {
|
|
path.setAttribute('fill', options.fillColor || options.color);
|
|
path.setAttribute('fill-opacity', options.fillOpacity);
|
|
path.setAttribute('fill-rule', options.fillRule || 'evenodd');
|
|
} else {
|
|
path.setAttribute('fill', 'none');
|
|
}
|
|
},
|
|
|
|
_updatePoly: function (layer, closed) {
|
|
this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed));
|
|
},
|
|
|
|
_updateCircle: function (layer) {
|
|
var p = layer._point,
|
|
r = layer._radius,
|
|
r2 = layer._radiusY || r,
|
|
arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
|
|
|
|
// drawing a circle with two half-arcs
|
|
var d = layer._empty() ? 'M0 0' :
|
|
'M' + (p.x - r) + ',' + p.y +
|
|
arc + (r * 2) + ',0 ' +
|
|
arc + (-r * 2) + ',0 ';
|
|
|
|
this._setPath(layer, d);
|
|
},
|
|
|
|
_setPath: function (layer, path) {
|
|
layer._path.setAttribute('d', path);
|
|
},
|
|
|
|
// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
|
|
_bringToFront: function (layer) {
|
|
L.DomUtil.toFront(layer._path);
|
|
},
|
|
|
|
_bringToBack: function (layer) {
|
|
L.DomUtil.toBack(layer._path);
|
|
}
|
|
});
|
|
|
|
|
|
// @namespace SVG; @section
|
|
// There are several static functions which can be called without instantiating L.SVG:
|
|
L.extend(L.SVG, {
|
|
// @function create(name: String): SVGElement
|
|
// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
|
|
// corresponding to the class name passed. For example, using 'line' will return
|
|
// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
|
|
create: function (name) {
|
|
return document.createElementNS('http://www.w3.org/2000/svg', name);
|
|
},
|
|
|
|
// @function pointsToPath(rings: Point[], closed: Boolean): String
|
|
// Generates a SVG path string for multiple rings, with each ring turning
|
|
// into "M..L..L.." instructions
|
|
pointsToPath: function (rings, closed) {
|
|
var str = '',
|
|
i, j, len, len2, points, p;
|
|
|
|
for (i = 0, len = rings.length; i < len; i++) {
|
|
points = rings[i];
|
|
|
|
for (j = 0, len2 = points.length; j < len2; j++) {
|
|
p = points[j];
|
|
str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
|
|
}
|
|
|
|
// closes the ring for polygons; "x" is VML syntax
|
|
str += closed ? (L.Browser.svg ? 'z' : 'x') : '';
|
|
}
|
|
|
|
// SVG complains about empty path strings
|
|
return str || 'M0 0';
|
|
}
|
|
});
|
|
|
|
// @namespace Browser; @property svg: Boolean
|
|
// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
|
|
L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect);
|
|
|
|
|
|
// @namespace SVG
|
|
// @factory L.svg(options?: Renderer options)
|
|
// Creates a SVG renderer with the given options.
|
|
L.svg = function (options) {
|
|
return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null;
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
|
|
*/
|
|
|
|
/*
|
|
* @class SVG
|
|
*
|
|
* Although SVG is not available on IE7 and IE8, these browsers support [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language), and the SVG renderer will fall back to VML in this case.
|
|
*
|
|
* VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
|
|
* with old versions of Internet Explorer.
|
|
*/
|
|
|
|
// @namespace Browser; @property vml: Boolean
|
|
// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
|
|
L.Browser.vml = !L.Browser.svg && (function () {
|
|
try {
|
|
var div = document.createElement('div');
|
|
div.innerHTML = '<v:shape adj="1"/>';
|
|
|
|
var shape = div.firstChild;
|
|
shape.style.behavior = 'url(#default#VML)';
|
|
|
|
return shape && (typeof shape.adj === 'object');
|
|
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}());
|
|
|
|
// redefine some SVG methods to handle VML syntax which is similar but with some differences
|
|
L.SVG.include(!L.Browser.vml ? {} : {
|
|
|
|
_initContainer: function () {
|
|
this._container = L.DomUtil.create('div', 'leaflet-vml-container');
|
|
},
|
|
|
|
_update: function () {
|
|
if (this._map._animatingZoom) { return; }
|
|
L.Renderer.prototype._update.call(this);
|
|
this.fire('update');
|
|
},
|
|
|
|
_initPath: function (layer) {
|
|
var container = layer._container = L.SVG.create('shape');
|
|
|
|
L.DomUtil.addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
|
|
|
|
container.coordsize = '1 1';
|
|
|
|
layer._path = L.SVG.create('path');
|
|
container.appendChild(layer._path);
|
|
|
|
this._updateStyle(layer);
|
|
},
|
|
|
|
_addPath: function (layer) {
|
|
var container = layer._container;
|
|
this._container.appendChild(container);
|
|
|
|
if (layer.options.interactive) {
|
|
layer.addInteractiveTarget(container);
|
|
}
|
|
},
|
|
|
|
_removePath: function (layer) {
|
|
var container = layer._container;
|
|
L.DomUtil.remove(container);
|
|
layer.removeInteractiveTarget(container);
|
|
},
|
|
|
|
_updateStyle: function (layer) {
|
|
var stroke = layer._stroke,
|
|
fill = layer._fill,
|
|
options = layer.options,
|
|
container = layer._container;
|
|
|
|
container.stroked = !!options.stroke;
|
|
container.filled = !!options.fill;
|
|
|
|
if (options.stroke) {
|
|
if (!stroke) {
|
|
stroke = layer._stroke = L.SVG.create('stroke');
|
|
}
|
|
container.appendChild(stroke);
|
|
stroke.weight = options.weight + 'px';
|
|
stroke.color = options.color;
|
|
stroke.opacity = options.opacity;
|
|
|
|
if (options.dashArray) {
|
|
stroke.dashStyle = L.Util.isArray(options.dashArray) ?
|
|
options.dashArray.join(' ') :
|
|
options.dashArray.replace(/( *, *)/g, ' ');
|
|
} else {
|
|
stroke.dashStyle = '';
|
|
}
|
|
stroke.endcap = options.lineCap.replace('butt', 'flat');
|
|
stroke.joinstyle = options.lineJoin;
|
|
|
|
} else if (stroke) {
|
|
container.removeChild(stroke);
|
|
layer._stroke = null;
|
|
}
|
|
|
|
if (options.fill) {
|
|
if (!fill) {
|
|
fill = layer._fill = L.SVG.create('fill');
|
|
}
|
|
container.appendChild(fill);
|
|
fill.color = options.fillColor || options.color;
|
|
fill.opacity = options.fillOpacity;
|
|
|
|
} else if (fill) {
|
|
container.removeChild(fill);
|
|
layer._fill = null;
|
|
}
|
|
},
|
|
|
|
_updateCircle: function (layer) {
|
|
var p = layer._point.round(),
|
|
r = Math.round(layer._radius),
|
|
r2 = Math.round(layer._radiusY || r);
|
|
|
|
this._setPath(layer, layer._empty() ? 'M0 0' :
|
|
'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
|
|
},
|
|
|
|
_setPath: function (layer, path) {
|
|
layer._path.v = path;
|
|
},
|
|
|
|
_bringToFront: function (layer) {
|
|
L.DomUtil.toFront(layer._container);
|
|
},
|
|
|
|
_bringToBack: function (layer) {
|
|
L.DomUtil.toBack(layer._container);
|
|
}
|
|
});
|
|
|
|
if (L.Browser.vml) {
|
|
L.SVG.create = (function () {
|
|
try {
|
|
document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
|
|
return function (name) {
|
|
return document.createElement('<lvml:' + name + ' class="lvml">');
|
|
};
|
|
} catch (e) {
|
|
return function (name) {
|
|
return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
|
|
};
|
|
}
|
|
})();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* @class Canvas
|
|
* @inherits Renderer
|
|
* @aka L.Canvas
|
|
*
|
|
* Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
|
|
* Inherits `Renderer`.
|
|
*
|
|
* Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
|
|
* available in all web browsers, notably IE8, and overlapping geometries might
|
|
* not display properly in some edge cases.
|
|
*
|
|
* @example
|
|
*
|
|
* Use Canvas by default for all paths in the map:
|
|
*
|
|
* ```js
|
|
* var map = L.map('map', {
|
|
* renderer: L.canvas()
|
|
* });
|
|
* ```
|
|
*
|
|
* Use a Canvas renderer with extra padding for specific vector geometries:
|
|
*
|
|
* ```js
|
|
* var map = L.map('map');
|
|
* var myRenderer = L.canvas({ padding: 0.5 });
|
|
* var line = L.polyline( coordinates, { renderer: myRenderer } );
|
|
* var circle = L.circle( center, { renderer: myRenderer } );
|
|
* ```
|
|
*/
|
|
|
|
L.Canvas = L.Renderer.extend({
|
|
|
|
onAdd: function () {
|
|
L.Renderer.prototype.onAdd.call(this);
|
|
|
|
// Redraw vectors since canvas is cleared upon removal,
|
|
// in case of removing the renderer itself from the map.
|
|
this._draw();
|
|
},
|
|
|
|
_initContainer: function () {
|
|
var container = this._container = document.createElement('canvas');
|
|
|
|
L.DomEvent
|
|
.on(container, 'mousemove', L.Util.throttle(this._onMouseMove, 32, this), this)
|
|
.on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this)
|
|
.on(container, 'mouseout', this._handleMouseOut, this);
|
|
|
|
this._ctx = container.getContext('2d');
|
|
},
|
|
|
|
_updatePaths: function () {
|
|
var layer;
|
|
this._redrawBounds = null;
|
|
for (var id in this._layers) {
|
|
layer = this._layers[id];
|
|
layer._update();
|
|
}
|
|
this._redraw();
|
|
},
|
|
|
|
_update: function () {
|
|
if (this._map._animatingZoom && this._bounds) { return; }
|
|
|
|
this._drawnLayers = {};
|
|
|
|
L.Renderer.prototype._update.call(this);
|
|
|
|
var b = this._bounds,
|
|
container = this._container,
|
|
size = b.getSize(),
|
|
m = L.Browser.retina ? 2 : 1;
|
|
|
|
L.DomUtil.setPosition(container, b.min);
|
|
|
|
// set canvas size (also clearing it); use double size on retina
|
|
container.width = m * size.x;
|
|
container.height = m * size.y;
|
|
container.style.width = size.x + 'px';
|
|
container.style.height = size.y + 'px';
|
|
|
|
if (L.Browser.retina) {
|
|
this._ctx.scale(2, 2);
|
|
}
|
|
|
|
// translate so we use the same path coordinates after canvas element moves
|
|
this._ctx.translate(-b.min.x, -b.min.y);
|
|
|
|
// Tell paths to redraw themselves
|
|
this.fire('update');
|
|
},
|
|
|
|
_initPath: function (layer) {
|
|
this._updateDashArray(layer);
|
|
this._layers[L.stamp(layer)] = layer;
|
|
|
|
var order = layer._order = {
|
|
layer: layer,
|
|
prev: this._drawLast,
|
|
next: null
|
|
};
|
|
if (this._drawLast) { this._drawLast.next = order; }
|
|
this._drawLast = order;
|
|
this._drawFirst = this._drawFirst || this._drawLast;
|
|
},
|
|
|
|
_addPath: function (layer) {
|
|
this._requestRedraw(layer);
|
|
},
|
|
|
|
_removePath: function (layer) {
|
|
var order = layer._order;
|
|
var next = order.next;
|
|
var prev = order.prev;
|
|
|
|
if (next) {
|
|
next.prev = prev;
|
|
} else {
|
|
this._drawLast = prev;
|
|
}
|
|
if (prev) {
|
|
prev.next = next;
|
|
} else {
|
|
this._drawFirst = next;
|
|
}
|
|
|
|
delete layer._order;
|
|
|
|
delete this._layers[L.stamp(layer)];
|
|
|
|
this._requestRedraw(layer);
|
|
},
|
|
|
|
_updatePath: function (layer) {
|
|
// Redraw the union of the layer's old pixel
|
|
// bounds and the new pixel bounds.
|
|
this._extendRedrawBounds(layer);
|
|
layer._project();
|
|
layer._update();
|
|
// The redraw will extend the redraw bounds
|
|
// with the new pixel bounds.
|
|
this._requestRedraw(layer);
|
|
},
|
|
|
|
_updateStyle: function (layer) {
|
|
this._updateDashArray(layer);
|
|
this._requestRedraw(layer);
|
|
},
|
|
|
|
_updateDashArray: function (layer) {
|
|
if (layer.options.dashArray) {
|
|
var parts = layer.options.dashArray.split(','),
|
|
dashArray = [],
|
|
i;
|
|
for (i = 0; i < parts.length; i++) {
|
|
dashArray.push(Number(parts[i]));
|
|
}
|
|
layer.options._dashArray = dashArray;
|
|
}
|
|
},
|
|
|
|
_requestRedraw: function (layer) {
|
|
if (!this._map) { return; }
|
|
|
|
this._extendRedrawBounds(layer);
|
|
this._redrawRequest = this._redrawRequest || L.Util.requestAnimFrame(this._redraw, this);
|
|
},
|
|
|
|
_extendRedrawBounds: function (layer) {
|
|
var padding = (layer.options.weight || 0) + 1;
|
|
this._redrawBounds = this._redrawBounds || new L.Bounds();
|
|
this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
|
|
this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
|
|
},
|
|
|
|
_redraw: function () {
|
|
this._redrawRequest = null;
|
|
|
|
this._clear(); // clear layers in redraw bounds
|
|
this._draw(); // draw layers
|
|
|
|
this._redrawBounds = null;
|
|
},
|
|
|
|
_clear: function () {
|
|
var bounds = this._redrawBounds;
|
|
if (bounds) {
|
|
var size = bounds.getSize();
|
|
this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
|
|
} else {
|
|
this._ctx.clearRect(0, 0, this._container.width, this._container.height);
|
|
}
|
|
},
|
|
|
|
_draw: function () {
|
|
var layer, bounds = this._redrawBounds;
|
|
this._ctx.save();
|
|
if (bounds) {
|
|
var size = bounds.getSize();
|
|
this._ctx.beginPath();
|
|
this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
|
|
this._ctx.clip();
|
|
}
|
|
|
|
this._drawing = true;
|
|
|
|
for (var order = this._drawFirst; order; order = order.next) {
|
|
layer = order.layer;
|
|
if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
|
|
layer._updatePath();
|
|
}
|
|
}
|
|
|
|
this._drawing = false;
|
|
|
|
this._ctx.restore(); // Restore state before clipping.
|
|
},
|
|
|
|
_updatePoly: function (layer, closed) {
|
|
if (!this._drawing) { return; }
|
|
|
|
var i, j, len2, p,
|
|
parts = layer._parts,
|
|
len = parts.length,
|
|
ctx = this._ctx;
|
|
|
|
if (!len) { return; }
|
|
|
|
this._drawnLayers[layer._leaflet_id] = layer;
|
|
|
|
ctx.beginPath();
|
|
|
|
if (ctx.setLineDash) {
|
|
ctx.setLineDash(layer.options && layer.options._dashArray || []);
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
for (j = 0, len2 = parts[i].length; j < len2; j++) {
|
|
p = parts[i][j];
|
|
ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
|
|
}
|
|
if (closed) {
|
|
ctx.closePath();
|
|
}
|
|
}
|
|
|
|
this._fillStroke(ctx, layer);
|
|
|
|
// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
|
|
},
|
|
|
|
_updateCircle: function (layer) {
|
|
|
|
if (!this._drawing || layer._empty()) { return; }
|
|
|
|
var p = layer._point,
|
|
ctx = this._ctx,
|
|
r = layer._radius,
|
|
s = (layer._radiusY || r) / r;
|
|
|
|
this._drawnLayers[layer._leaflet_id] = layer;
|
|
|
|
if (s !== 1) {
|
|
ctx.save();
|
|
ctx.scale(1, s);
|
|
}
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
|
|
|
|
if (s !== 1) {
|
|
ctx.restore();
|
|
}
|
|
|
|
this._fillStroke(ctx, layer);
|
|
},
|
|
|
|
_fillStroke: function (ctx, layer) {
|
|
var options = layer.options;
|
|
|
|
if (options.fill) {
|
|
ctx.globalAlpha = options.fillOpacity;
|
|
ctx.fillStyle = options.fillColor || options.color;
|
|
ctx.fill(options.fillRule || 'evenodd');
|
|
}
|
|
|
|
if (options.stroke && options.weight !== 0) {
|
|
ctx.globalAlpha = options.opacity;
|
|
ctx.lineWidth = options.weight;
|
|
ctx.strokeStyle = options.color;
|
|
ctx.lineCap = options.lineCap;
|
|
ctx.lineJoin = options.lineJoin;
|
|
ctx.stroke();
|
|
}
|
|
},
|
|
|
|
// Canvas obviously doesn't have mouse events for individual drawn objects,
|
|
// so we emulate that by calculating what's under the mouse on mousemove/click manually
|
|
|
|
_onClick: function (e) {
|
|
var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
|
|
|
|
for (var order = this._drawFirst; order; order = order.next) {
|
|
layer = order.layer;
|
|
if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
|
|
clickedLayer = layer;
|
|
}
|
|
}
|
|
if (clickedLayer) {
|
|
L.DomEvent._fakeStop(e);
|
|
this._fireEvent([clickedLayer], e);
|
|
}
|
|
},
|
|
|
|
_onMouseMove: function (e) {
|
|
if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
|
|
|
|
var point = this._map.mouseEventToLayerPoint(e);
|
|
this._handleMouseHover(e, point);
|
|
},
|
|
|
|
|
|
_handleMouseOut: function (e) {
|
|
var layer = this._hoveredLayer;
|
|
if (layer) {
|
|
// if we're leaving the layer, fire mouseout
|
|
L.DomUtil.removeClass(this._container, 'leaflet-interactive');
|
|
this._fireEvent([layer], e, 'mouseout');
|
|
this._hoveredLayer = null;
|
|
}
|
|
},
|
|
|
|
_handleMouseHover: function (e, point) {
|
|
var layer, candidateHoveredLayer;
|
|
|
|
for (var order = this._drawFirst; order; order = order.next) {
|
|
layer = order.layer;
|
|
if (layer.options.interactive && layer._containsPoint(point)) {
|
|
candidateHoveredLayer = layer;
|
|
}
|
|
}
|
|
|
|
if (candidateHoveredLayer !== this._hoveredLayer) {
|
|
this._handleMouseOut(e);
|
|
|
|
if (candidateHoveredLayer) {
|
|
L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
|
|
this._fireEvent([candidateHoveredLayer], e, 'mouseover');
|
|
this._hoveredLayer = candidateHoveredLayer;
|
|
}
|
|
}
|
|
|
|
if (this._hoveredLayer) {
|
|
this._fireEvent([this._hoveredLayer], e);
|
|
}
|
|
},
|
|
|
|
_fireEvent: function (layers, e, type) {
|
|
this._map._fireDOMEvent(e, type || e.type, layers);
|
|
},
|
|
|
|
_bringToFront: function (layer) {
|
|
var order = layer._order;
|
|
var next = order.next;
|
|
var prev = order.prev;
|
|
|
|
if (next) {
|
|
next.prev = prev;
|
|
} else {
|
|
// Already last
|
|
return;
|
|
}
|
|
if (prev) {
|
|
prev.next = next;
|
|
} else if (next) {
|
|
// Update first entry unless this is the
|
|
// signle entry
|
|
this._drawFirst = next;
|
|
}
|
|
|
|
order.prev = this._drawLast;
|
|
this._drawLast.next = order;
|
|
|
|
order.next = null;
|
|
this._drawLast = order;
|
|
|
|
this._requestRedraw(layer);
|
|
},
|
|
|
|
_bringToBack: function (layer) {
|
|
var order = layer._order;
|
|
var next = order.next;
|
|
var prev = order.prev;
|
|
|
|
if (prev) {
|
|
prev.next = next;
|
|
} else {
|
|
// Already first
|
|
return;
|
|
}
|
|
if (next) {
|
|
next.prev = prev;
|
|
} else if (prev) {
|
|
// Update last entry unless this is the
|
|
// signle entry
|
|
this._drawLast = prev;
|
|
}
|
|
|
|
order.prev = null;
|
|
|
|
order.next = this._drawFirst;
|
|
this._drawFirst.prev = order;
|
|
this._drawFirst = order;
|
|
|
|
this._requestRedraw(layer);
|
|
}
|
|
});
|
|
|
|
// @namespace Browser; @property canvas: Boolean
|
|
// `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
|
|
L.Browser.canvas = (function () {
|
|
return !!document.createElement('canvas').getContext;
|
|
}());
|
|
|
|
// @namespace Canvas
|
|
// @factory L.canvas(options?: Renderer options)
|
|
// Creates a Canvas renderer with the given options.
|
|
L.canvas = function (options) {
|
|
return L.Browser.canvas ? new L.Canvas(options) : null;
|
|
};
|
|
|
|
L.Polyline.prototype._containsPoint = function (p, closed) {
|
|
var i, j, k, len, len2, part,
|
|
w = this._clickTolerance();
|
|
|
|
if (!this._pxBounds.contains(p)) { return false; }
|
|
|
|
// hit detection for polylines
|
|
for (i = 0, len = this._parts.length; i < len; i++) {
|
|
part = this._parts[i];
|
|
|
|
for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
|
|
if (!closed && (j === 0)) { continue; }
|
|
|
|
if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
L.Polygon.prototype._containsPoint = function (p) {
|
|
var inside = false,
|
|
part, p1, p2, i, j, k, len, len2;
|
|
|
|
if (!this._pxBounds.contains(p)) { return false; }
|
|
|
|
// ray casting algorithm for detecting if point is in polygon
|
|
for (i = 0, len = this._parts.length; i < len; i++) {
|
|
part = this._parts[i];
|
|
|
|
for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
|
|
p1 = part[j];
|
|
p2 = part[k];
|
|
|
|
if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
|
|
inside = !inside;
|
|
}
|
|
}
|
|
}
|
|
|
|
// also check if it's on polygon stroke
|
|
return inside || L.Polyline.prototype._containsPoint.call(this, p, true);
|
|
};
|
|
|
|
L.CircleMarker.prototype._containsPoint = function (p) {
|
|
return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class GeoJSON
|
|
* @aka L.GeoJSON
|
|
* @inherits FeatureGroup
|
|
*
|
|
* Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
|
|
* GeoJSON data and display it on the map. Extends `FeatureGroup`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* L.geoJSON(data, {
|
|
* style: function (feature) {
|
|
* return {color: feature.properties.color};
|
|
* }
|
|
* }).bindPopup(function (layer) {
|
|
* return layer.feature.properties.description;
|
|
* }).addTo(map);
|
|
* ```
|
|
*/
|
|
|
|
L.GeoJSON = L.FeatureGroup.extend({
|
|
|
|
/* @section
|
|
* @aka GeoJSON options
|
|
*
|
|
* @option pointToLayer: Function = *
|
|
* A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
|
|
* called when data is added, passing the GeoJSON point feature and its `LatLng`.
|
|
* The default is to spawn a default `Marker`:
|
|
* ```js
|
|
* function(geoJsonPoint, latlng) {
|
|
* return L.marker(latlng);
|
|
* }
|
|
* ```
|
|
*
|
|
* @option style: Function = *
|
|
* A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
|
|
* called internally when data is added.
|
|
* The default value is to not override any defaults:
|
|
* ```js
|
|
* function (geoJsonFeature) {
|
|
* return {}
|
|
* }
|
|
* ```
|
|
*
|
|
* @option onEachFeature: Function = *
|
|
* A `Function` that will be called once for each created `Feature`, after it has
|
|
* been created and styled. Useful for attaching events and popups to features.
|
|
* The default is to do nothing with the newly created layers:
|
|
* ```js
|
|
* function (feature, layer) {}
|
|
* ```
|
|
*
|
|
* @option filter: Function = *
|
|
* A `Function` that will be used to decide whether to include a feature or not.
|
|
* The default is to include all features:
|
|
* ```js
|
|
* function (geoJsonFeature) {
|
|
* return true;
|
|
* }
|
|
* ```
|
|
* Note: dynamically changing the `filter` option will have effect only on newly
|
|
* added data. It will _not_ re-evaluate already included features.
|
|
*
|
|
* @option coordsToLatLng: Function = *
|
|
* A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
|
|
* The default is the `coordsToLatLng` static method.
|
|
*/
|
|
|
|
initialize: function (geojson, options) {
|
|
L.setOptions(this, options);
|
|
|
|
this._layers = {};
|
|
|
|
if (geojson) {
|
|
this.addData(geojson);
|
|
}
|
|
},
|
|
|
|
// @method addData( <GeoJSON> data ): this
|
|
// Adds a GeoJSON object to the layer.
|
|
addData: function (geojson) {
|
|
var features = L.Util.isArray(geojson) ? geojson : geojson.features,
|
|
i, len, feature;
|
|
|
|
if (features) {
|
|
for (i = 0, len = features.length; i < len; i++) {
|
|
// only add this if geometry or geometries are set and not null
|
|
feature = features[i];
|
|
if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
|
|
this.addData(feature);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
var options = this.options;
|
|
|
|
if (options.filter && !options.filter(geojson)) { return this; }
|
|
|
|
var layer = L.GeoJSON.geometryToLayer(geojson, options);
|
|
if (!layer) {
|
|
return this;
|
|
}
|
|
layer.feature = L.GeoJSON.asFeature(geojson);
|
|
|
|
layer.defaultOptions = layer.options;
|
|
this.resetStyle(layer);
|
|
|
|
if (options.onEachFeature) {
|
|
options.onEachFeature(geojson, layer);
|
|
}
|
|
|
|
return this.addLayer(layer);
|
|
},
|
|
|
|
// @method resetStyle( <Path> layer ): this
|
|
// Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
|
|
resetStyle: function (layer) {
|
|
// reset any custom styles
|
|
layer.options = L.Util.extend({}, layer.defaultOptions);
|
|
this._setLayerStyle(layer, this.options.style);
|
|
return this;
|
|
},
|
|
|
|
// @method setStyle( <Function> style ): this
|
|
// Changes styles of GeoJSON vector layers with the given style function.
|
|
setStyle: function (style) {
|
|
return this.eachLayer(function (layer) {
|
|
this._setLayerStyle(layer, style);
|
|
}, this);
|
|
},
|
|
|
|
_setLayerStyle: function (layer, style) {
|
|
if (typeof style === 'function') {
|
|
style = style(layer.feature);
|
|
}
|
|
if (layer.setStyle) {
|
|
layer.setStyle(style);
|
|
}
|
|
}
|
|
});
|
|
|
|
// @section
|
|
// There are several static functions which can be called without instantiating L.GeoJSON:
|
|
L.extend(L.GeoJSON, {
|
|
// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
|
|
// Creates a `Layer` from a given GeoJSON feature. Can use a custom
|
|
// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
|
|
// functions if provided as options.
|
|
geometryToLayer: function (geojson, options) {
|
|
|
|
var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
|
|
coords = geometry ? geometry.coordinates : null,
|
|
layers = [],
|
|
pointToLayer = options && options.pointToLayer,
|
|
coordsToLatLng = options && options.coordsToLatLng || this.coordsToLatLng,
|
|
latlng, latlngs, i, len;
|
|
|
|
if (!coords && !geometry) {
|
|
return null;
|
|
}
|
|
|
|
switch (geometry.type) {
|
|
case 'Point':
|
|
latlng = coordsToLatLng(coords);
|
|
return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
|
|
|
|
case 'MultiPoint':
|
|
for (i = 0, len = coords.length; i < len; i++) {
|
|
latlng = coordsToLatLng(coords[i]);
|
|
layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng));
|
|
}
|
|
return new L.FeatureGroup(layers);
|
|
|
|
case 'LineString':
|
|
case 'MultiLineString':
|
|
latlngs = this.coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, coordsToLatLng);
|
|
return new L.Polyline(latlngs, options);
|
|
|
|
case 'Polygon':
|
|
case 'MultiPolygon':
|
|
latlngs = this.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, coordsToLatLng);
|
|
return new L.Polygon(latlngs, options);
|
|
|
|
case 'GeometryCollection':
|
|
for (i = 0, len = geometry.geometries.length; i < len; i++) {
|
|
var layer = this.geometryToLayer({
|
|
geometry: geometry.geometries[i],
|
|
type: 'Feature',
|
|
properties: geojson.properties
|
|
}, options);
|
|
|
|
if (layer) {
|
|
layers.push(layer);
|
|
}
|
|
}
|
|
return new L.FeatureGroup(layers);
|
|
|
|
default:
|
|
throw new Error('Invalid GeoJSON object.');
|
|
}
|
|
},
|
|
|
|
// @function coordsToLatLng(coords: Array): LatLng
|
|
// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
|
|
// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
|
|
coordsToLatLng: function (coords) {
|
|
return new L.LatLng(coords[1], coords[0], coords[2]);
|
|
},
|
|
|
|
// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
|
|
// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
|
|
// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
|
|
// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
|
|
coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) {
|
|
var latlngs = [];
|
|
|
|
for (var i = 0, len = coords.length, latlng; i < len; i++) {
|
|
latlng = levelsDeep ?
|
|
this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) :
|
|
(coordsToLatLng || this.coordsToLatLng)(coords[i]);
|
|
|
|
latlngs.push(latlng);
|
|
}
|
|
|
|
return latlngs;
|
|
},
|
|
|
|
// @function latLngToCoords(latlng: LatLng): Array
|
|
// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
|
|
latLngToCoords: function (latlng) {
|
|
return latlng.alt !== undefined ?
|
|
[latlng.lng, latlng.lat, latlng.alt] :
|
|
[latlng.lng, latlng.lat];
|
|
},
|
|
|
|
// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
|
|
// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
|
|
// `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default.
|
|
latLngsToCoords: function (latlngs, levelsDeep, closed) {
|
|
var coords = [];
|
|
|
|
for (var i = 0, len = latlngs.length; i < len; i++) {
|
|
coords.push(levelsDeep ?
|
|
L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed) :
|
|
L.GeoJSON.latLngToCoords(latlngs[i]));
|
|
}
|
|
|
|
if (!levelsDeep && closed) {
|
|
coords.push(coords[0]);
|
|
}
|
|
|
|
return coords;
|
|
},
|
|
|
|
getFeature: function (layer, newGeometry) {
|
|
return layer.feature ?
|
|
L.extend({}, layer.feature, {geometry: newGeometry}) :
|
|
L.GeoJSON.asFeature(newGeometry);
|
|
},
|
|
|
|
// @function asFeature(geojson: Object): Object
|
|
// Normalize GeoJSON geometries/features into GeoJSON features.
|
|
asFeature: function (geojson) {
|
|
if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
|
|
return geojson;
|
|
}
|
|
|
|
return {
|
|
type: 'Feature',
|
|
properties: {},
|
|
geometry: geojson
|
|
};
|
|
}
|
|
});
|
|
|
|
var PointToGeoJSON = {
|
|
toGeoJSON: function () {
|
|
return L.GeoJSON.getFeature(this, {
|
|
type: 'Point',
|
|
coordinates: L.GeoJSON.latLngToCoords(this.getLatLng())
|
|
});
|
|
}
|
|
};
|
|
|
|
// @namespace Marker
|
|
// @method toGeoJSON(): Object
|
|
// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
|
|
L.Marker.include(PointToGeoJSON);
|
|
|
|
// @namespace CircleMarker
|
|
// @method toGeoJSON(): Object
|
|
// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
|
|
L.Circle.include(PointToGeoJSON);
|
|
L.CircleMarker.include(PointToGeoJSON);
|
|
|
|
|
|
// @namespace Polyline
|
|
// @method toGeoJSON(): Object
|
|
// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
|
|
L.Polyline.prototype.toGeoJSON = function () {
|
|
var multi = !L.Polyline._flat(this._latlngs);
|
|
|
|
var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 1 : 0);
|
|
|
|
return L.GeoJSON.getFeature(this, {
|
|
type: (multi ? 'Multi' : '') + 'LineString',
|
|
coordinates: coords
|
|
});
|
|
};
|
|
|
|
// @namespace Polygon
|
|
// @method toGeoJSON(): Object
|
|
// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
|
|
L.Polygon.prototype.toGeoJSON = function () {
|
|
var holes = !L.Polyline._flat(this._latlngs),
|
|
multi = holes && !L.Polyline._flat(this._latlngs[0]);
|
|
|
|
var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true);
|
|
|
|
if (!holes) {
|
|
coords = [coords];
|
|
}
|
|
|
|
return L.GeoJSON.getFeature(this, {
|
|
type: (multi ? 'Multi' : '') + 'Polygon',
|
|
coordinates: coords
|
|
});
|
|
};
|
|
|
|
|
|
// @namespace LayerGroup
|
|
L.LayerGroup.include({
|
|
toMultiPoint: function () {
|
|
var coords = [];
|
|
|
|
this.eachLayer(function (layer) {
|
|
coords.push(layer.toGeoJSON().geometry.coordinates);
|
|
});
|
|
|
|
return L.GeoJSON.getFeature(this, {
|
|
type: 'MultiPoint',
|
|
coordinates: coords
|
|
});
|
|
},
|
|
|
|
// @method toGeoJSON(): Object
|
|
// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `GeometryCollection`).
|
|
toGeoJSON: function () {
|
|
|
|
var type = this.feature && this.feature.geometry && this.feature.geometry.type;
|
|
|
|
if (type === 'MultiPoint') {
|
|
return this.toMultiPoint();
|
|
}
|
|
|
|
var isGeometryCollection = type === 'GeometryCollection',
|
|
jsons = [];
|
|
|
|
this.eachLayer(function (layer) {
|
|
if (layer.toGeoJSON) {
|
|
var json = layer.toGeoJSON();
|
|
jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json));
|
|
}
|
|
});
|
|
|
|
if (isGeometryCollection) {
|
|
return L.GeoJSON.getFeature(this, {
|
|
geometries: jsons,
|
|
type: 'GeometryCollection'
|
|
});
|
|
}
|
|
|
|
return {
|
|
type: 'FeatureCollection',
|
|
features: jsons
|
|
};
|
|
}
|
|
});
|
|
|
|
// @namespace GeoJSON
|
|
// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
|
|
// Creates a GeoJSON layer. Optionally accepts an object in
|
|
// [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
|
|
// (you can alternatively add it later with `addData` method) and an `options` object.
|
|
L.geoJSON = function (geojson, options) {
|
|
return new L.GeoJSON(geojson, options);
|
|
};
|
|
// Backward compatibility.
|
|
L.geoJson = L.geoJSON;
|
|
|
|
|
|
|
|
/*
|
|
* @class Draggable
|
|
* @aka L.Draggable
|
|
* @inherits Evented
|
|
*
|
|
* A class for making DOM elements draggable (including touch support).
|
|
* Used internally for map and marker dragging. Only works for elements
|
|
* that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
|
|
*
|
|
* @example
|
|
* ```js
|
|
* var draggable = new L.Draggable(elementToDrag);
|
|
* draggable.enable();
|
|
* ```
|
|
*/
|
|
|
|
L.Draggable = L.Evented.extend({
|
|
|
|
options: {
|
|
// @option clickTolerance: Number = 3
|
|
// The max number of pixels a user can shift the mouse pointer during a click
|
|
// for it to be considered a valid click (as opposed to a mouse drag).
|
|
clickTolerance: 3
|
|
},
|
|
|
|
statics: {
|
|
START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
|
|
END: {
|
|
mousedown: 'mouseup',
|
|
touchstart: 'touchend',
|
|
pointerdown: 'touchend',
|
|
MSPointerDown: 'touchend'
|
|
},
|
|
MOVE: {
|
|
mousedown: 'mousemove',
|
|
touchstart: 'touchmove',
|
|
pointerdown: 'touchmove',
|
|
MSPointerDown: 'touchmove'
|
|
}
|
|
},
|
|
|
|
// @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline: Boolean)
|
|
// Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
|
|
initialize: function (element, dragStartTarget, preventOutline) {
|
|
this._element = element;
|
|
this._dragStartTarget = dragStartTarget || element;
|
|
this._preventOutline = preventOutline;
|
|
},
|
|
|
|
// @method enable()
|
|
// Enables the dragging ability
|
|
enable: function () {
|
|
if (this._enabled) { return; }
|
|
|
|
L.DomEvent.on(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
|
|
|
|
this._enabled = true;
|
|
},
|
|
|
|
// @method disable()
|
|
// Disables the dragging ability
|
|
disable: function () {
|
|
if (!this._enabled) { return; }
|
|
|
|
// If we're currently dragging this draggable,
|
|
// disabling it counts as first ending the drag.
|
|
if (L.Draggable._dragging === this) {
|
|
this.finishDrag();
|
|
}
|
|
|
|
L.DomEvent.off(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
|
|
|
|
this._enabled = false;
|
|
this._moved = false;
|
|
},
|
|
|
|
_onDown: function (e) {
|
|
// Ignore simulated events, since we handle both touch and
|
|
// mouse explicitly; otherwise we risk getting duplicates of
|
|
// touch events, see #4315.
|
|
// Also ignore the event if disabled; this happens in IE11
|
|
// under some circumstances, see #3666.
|
|
if (e._simulated || !this._enabled) { return; }
|
|
|
|
this._moved = false;
|
|
|
|
if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; }
|
|
|
|
if (L.Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
|
|
L.Draggable._dragging = this; // Prevent dragging multiple objects at once.
|
|
|
|
if (this._preventOutline) {
|
|
L.DomUtil.preventOutline(this._element);
|
|
}
|
|
|
|
L.DomUtil.disableImageDrag();
|
|
L.DomUtil.disableTextSelection();
|
|
|
|
if (this._moving) { return; }
|
|
|
|
// @event down: Event
|
|
// Fired when a drag is about to start.
|
|
this.fire('down');
|
|
|
|
var first = e.touches ? e.touches[0] : e;
|
|
|
|
this._startPoint = new L.Point(first.clientX, first.clientY);
|
|
|
|
L.DomEvent
|
|
.on(document, L.Draggable.MOVE[e.type], this._onMove, this)
|
|
.on(document, L.Draggable.END[e.type], this._onUp, this);
|
|
},
|
|
|
|
_onMove: function (e) {
|
|
// Ignore simulated events, since we handle both touch and
|
|
// mouse explicitly; otherwise we risk getting duplicates of
|
|
// touch events, see #4315.
|
|
// Also ignore the event if disabled; this happens in IE11
|
|
// under some circumstances, see #3666.
|
|
if (e._simulated || !this._enabled) { return; }
|
|
|
|
if (e.touches && e.touches.length > 1) {
|
|
this._moved = true;
|
|
return;
|
|
}
|
|
|
|
var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
|
|
newPoint = new L.Point(first.clientX, first.clientY),
|
|
offset = newPoint.subtract(this._startPoint);
|
|
|
|
if (!offset.x && !offset.y) { return; }
|
|
if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
|
|
|
|
L.DomEvent.preventDefault(e);
|
|
|
|
if (!this._moved) {
|
|
// @event dragstart: Event
|
|
// Fired when a drag starts
|
|
this.fire('dragstart');
|
|
|
|
this._moved = true;
|
|
this._startPos = L.DomUtil.getPosition(this._element).subtract(offset);
|
|
|
|
L.DomUtil.addClass(document.body, 'leaflet-dragging');
|
|
|
|
this._lastTarget = e.target || e.srcElement;
|
|
// IE and Edge do not give the <use> element, so fetch it
|
|
// if necessary
|
|
if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
|
|
this._lastTarget = this._lastTarget.correspondingUseElement;
|
|
}
|
|
L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target');
|
|
}
|
|
|
|
this._newPos = this._startPos.add(offset);
|
|
this._moving = true;
|
|
|
|
L.Util.cancelAnimFrame(this._animRequest);
|
|
this._lastEvent = e;
|
|
this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true);
|
|
},
|
|
|
|
_updatePosition: function () {
|
|
var e = {originalEvent: this._lastEvent};
|
|
|
|
// @event predrag: Event
|
|
// Fired continuously during dragging *before* each corresponding
|
|
// update of the element's position.
|
|
this.fire('predrag', e);
|
|
L.DomUtil.setPosition(this._element, this._newPos);
|
|
|
|
// @event drag: Event
|
|
// Fired continuously during dragging.
|
|
this.fire('drag', e);
|
|
},
|
|
|
|
_onUp: function (e) {
|
|
// Ignore simulated events, since we handle both touch and
|
|
// mouse explicitly; otherwise we risk getting duplicates of
|
|
// touch events, see #4315.
|
|
// Also ignore the event if disabled; this happens in IE11
|
|
// under some circumstances, see #3666.
|
|
if (e._simulated || !this._enabled) { return; }
|
|
this.finishDrag();
|
|
},
|
|
|
|
finishDrag: function () {
|
|
L.DomUtil.removeClass(document.body, 'leaflet-dragging');
|
|
|
|
if (this._lastTarget) {
|
|
L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target');
|
|
this._lastTarget = null;
|
|
}
|
|
|
|
for (var i in L.Draggable.MOVE) {
|
|
L.DomEvent
|
|
.off(document, L.Draggable.MOVE[i], this._onMove, this)
|
|
.off(document, L.Draggable.END[i], this._onUp, this);
|
|
}
|
|
|
|
L.DomUtil.enableImageDrag();
|
|
L.DomUtil.enableTextSelection();
|
|
|
|
if (this._moved && this._moving) {
|
|
// ensure drag is not fired after dragend
|
|
L.Util.cancelAnimFrame(this._animRequest);
|
|
|
|
// @event dragend: DragEndEvent
|
|
// Fired when the drag ends.
|
|
this.fire('dragend', {
|
|
distance: this._newPos.distanceTo(this._startPos)
|
|
});
|
|
}
|
|
|
|
this._moving = false;
|
|
L.Draggable._dragging = false;
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
L.Handler is a base class for handler classes that are used internally to inject
|
|
interaction features like dragging to classes like Map and Marker.
|
|
*/
|
|
|
|
// @class Handler
|
|
// @aka L.Handler
|
|
// Abstract class for map interaction handlers
|
|
|
|
L.Handler = L.Class.extend({
|
|
initialize: function (map) {
|
|
this._map = map;
|
|
},
|
|
|
|
// @method enable(): this
|
|
// Enables the handler
|
|
enable: function () {
|
|
if (this._enabled) { return this; }
|
|
|
|
this._enabled = true;
|
|
this.addHooks();
|
|
return this;
|
|
},
|
|
|
|
// @method disable(): this
|
|
// Disables the handler
|
|
disable: function () {
|
|
if (!this._enabled) { return this; }
|
|
|
|
this._enabled = false;
|
|
this.removeHooks();
|
|
return this;
|
|
},
|
|
|
|
// @method enabled(): Boolean
|
|
// Returns `true` if the handler is enabled
|
|
enabled: function () {
|
|
return !!this._enabled;
|
|
}
|
|
|
|
// @section Extension methods
|
|
// Classes inheriting from `Handler` must implement the two following methods:
|
|
// @method addHooks()
|
|
// Called when the handler is enabled, should add event hooks.
|
|
// @method removeHooks()
|
|
// Called when the handler is disabled, should remove the event hooks added previously.
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
|
|
*/
|
|
|
|
// @namespace Map
|
|
// @section Interaction Options
|
|
L.Map.mergeOptions({
|
|
// @option dragging: Boolean = true
|
|
// Whether the map be draggable with mouse/touch or not.
|
|
dragging: true,
|
|
|
|
// @section Panning Inertia Options
|
|
// @option inertia: Boolean = *
|
|
// If enabled, panning of the map will have an inertia effect where
|
|
// the map builds momentum while dragging and continues moving in
|
|
// the same direction for some time. Feels especially nice on touch
|
|
// devices. Enabled by default unless running on old Android devices.
|
|
inertia: !L.Browser.android23,
|
|
|
|
// @option inertiaDeceleration: Number = 3000
|
|
// The rate with which the inertial movement slows down, in pixels/second².
|
|
inertiaDeceleration: 3400, // px/s^2
|
|
|
|
// @option inertiaMaxSpeed: Number = Infinity
|
|
// Max speed of the inertial movement, in pixels/second.
|
|
inertiaMaxSpeed: Infinity, // px/s
|
|
|
|
// @option easeLinearity: Number = 0.2
|
|
easeLinearity: 0.2,
|
|
|
|
// TODO refactor, move to CRS
|
|
// @option worldCopyJump: Boolean = false
|
|
// With this option enabled, the map tracks when you pan to another "copy"
|
|
// of the world and seamlessly jumps to the original one so that all overlays
|
|
// like markers and vector layers are still visible.
|
|
worldCopyJump: false,
|
|
|
|
// @option maxBoundsViscosity: Number = 0.0
|
|
// If `maxBounds` is set, this option will control how solid the bounds
|
|
// are when dragging the map around. The default value of `0.0` allows the
|
|
// user to drag outside the bounds at normal speed, higher values will
|
|
// slow down map dragging outside bounds, and `1.0` makes the bounds fully
|
|
// solid, preventing the user from dragging outside the bounds.
|
|
maxBoundsViscosity: 0.0
|
|
});
|
|
|
|
L.Map.Drag = L.Handler.extend({
|
|
addHooks: function () {
|
|
if (!this._draggable) {
|
|
var map = this._map;
|
|
|
|
this._draggable = new L.Draggable(map._mapPane, map._container);
|
|
|
|
this._draggable.on({
|
|
down: this._onDown,
|
|
dragstart: this._onDragStart,
|
|
drag: this._onDrag,
|
|
dragend: this._onDragEnd
|
|
}, this);
|
|
|
|
this._draggable.on('predrag', this._onPreDragLimit, this);
|
|
if (map.options.worldCopyJump) {
|
|
this._draggable.on('predrag', this._onPreDragWrap, this);
|
|
map.on('zoomend', this._onZoomEnd, this);
|
|
|
|
map.whenReady(this._onZoomEnd, this);
|
|
}
|
|
}
|
|
L.DomUtil.addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
|
|
this._draggable.enable();
|
|
this._positions = [];
|
|
this._times = [];
|
|
},
|
|
|
|
removeHooks: function () {
|
|
L.DomUtil.removeClass(this._map._container, 'leaflet-grab');
|
|
L.DomUtil.removeClass(this._map._container, 'leaflet-touch-drag');
|
|
this._draggable.disable();
|
|
},
|
|
|
|
moved: function () {
|
|
return this._draggable && this._draggable._moved;
|
|
},
|
|
|
|
moving: function () {
|
|
return this._draggable && this._draggable._moving;
|
|
},
|
|
|
|
_onDown: function () {
|
|
this._map._stop();
|
|
},
|
|
|
|
_onDragStart: function () {
|
|
var map = this._map;
|
|
|
|
if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
|
|
var bounds = L.latLngBounds(this._map.options.maxBounds);
|
|
|
|
this._offsetLimit = L.bounds(
|
|
this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
|
|
this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
|
|
.add(this._map.getSize()));
|
|
|
|
this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
|
|
} else {
|
|
this._offsetLimit = null;
|
|
}
|
|
|
|
map
|
|
.fire('movestart')
|
|
.fire('dragstart');
|
|
|
|
if (map.options.inertia) {
|
|
this._positions = [];
|
|
this._times = [];
|
|
}
|
|
},
|
|
|
|
_onDrag: function (e) {
|
|
if (this._map.options.inertia) {
|
|
var time = this._lastTime = +new Date(),
|
|
pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
|
|
|
|
this._positions.push(pos);
|
|
this._times.push(time);
|
|
|
|
if (time - this._times[0] > 50) {
|
|
this._positions.shift();
|
|
this._times.shift();
|
|
}
|
|
}
|
|
|
|
this._map
|
|
.fire('move', e)
|
|
.fire('drag', e);
|
|
},
|
|
|
|
_onZoomEnd: function () {
|
|
var pxCenter = this._map.getSize().divideBy(2),
|
|
pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
|
|
|
|
this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
|
|
this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
|
|
},
|
|
|
|
_viscousLimit: function (value, threshold) {
|
|
return value - (value - threshold) * this._viscosity;
|
|
},
|
|
|
|
_onPreDragLimit: function () {
|
|
if (!this._viscosity || !this._offsetLimit) { return; }
|
|
|
|
var offset = this._draggable._newPos.subtract(this._draggable._startPos);
|
|
|
|
var limit = this._offsetLimit;
|
|
if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
|
|
if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
|
|
if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
|
|
if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
|
|
|
|
this._draggable._newPos = this._draggable._startPos.add(offset);
|
|
},
|
|
|
|
_onPreDragWrap: function () {
|
|
// TODO refactor to be able to adjust map pane position after zoom
|
|
var worldWidth = this._worldWidth,
|
|
halfWidth = Math.round(worldWidth / 2),
|
|
dx = this._initialWorldOffset,
|
|
x = this._draggable._newPos.x,
|
|
newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
|
|
newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
|
|
newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
|
|
|
|
this._draggable._absPos = this._draggable._newPos.clone();
|
|
this._draggable._newPos.x = newX;
|
|
},
|
|
|
|
_onDragEnd: function (e) {
|
|
var map = this._map,
|
|
options = map.options,
|
|
|
|
noInertia = !options.inertia || this._times.length < 2;
|
|
|
|
map.fire('dragend', e);
|
|
|
|
if (noInertia) {
|
|
map.fire('moveend');
|
|
|
|
} else {
|
|
|
|
var direction = this._lastPos.subtract(this._positions[0]),
|
|
duration = (this._lastTime - this._times[0]) / 1000,
|
|
ease = options.easeLinearity,
|
|
|
|
speedVector = direction.multiplyBy(ease / duration),
|
|
speed = speedVector.distanceTo([0, 0]),
|
|
|
|
limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
|
|
limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
|
|
|
|
decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
|
|
offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
|
|
|
|
if (!offset.x && !offset.y) {
|
|
map.fire('moveend');
|
|
|
|
} else {
|
|
offset = map._limitOffset(offset, map.options.maxBounds);
|
|
|
|
L.Util.requestAnimFrame(function () {
|
|
map.panBy(offset, {
|
|
duration: decelerationDuration,
|
|
easeLinearity: ease,
|
|
noMoveStart: true,
|
|
animate: true
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// @section Handlers
|
|
// @property dragging: Handler
|
|
// Map dragging handler (by both mouse and touch).
|
|
L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
|
|
|
|
|
|
|
|
/*
|
|
* L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
|
|
*/
|
|
|
|
// @namespace Map
|
|
// @section Interaction Options
|
|
|
|
L.Map.mergeOptions({
|
|
// @option doubleClickZoom: Boolean|String = true
|
|
// Whether the map can be zoomed in by double clicking on it and
|
|
// zoomed out by double clicking while holding shift. If passed
|
|
// `'center'`, double-click zoom will zoom to the center of the
|
|
// view regardless of where the mouse was.
|
|
doubleClickZoom: true
|
|
});
|
|
|
|
L.Map.DoubleClickZoom = L.Handler.extend({
|
|
addHooks: function () {
|
|
this._map.on('dblclick', this._onDoubleClick, this);
|
|
},
|
|
|
|
removeHooks: function () {
|
|
this._map.off('dblclick', this._onDoubleClick, this);
|
|
},
|
|
|
|
_onDoubleClick: function (e) {
|
|
var map = this._map,
|
|
oldZoom = map.getZoom(),
|
|
delta = map.options.zoomDelta,
|
|
zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
|
|
|
|
if (map.options.doubleClickZoom === 'center') {
|
|
map.setZoom(zoom);
|
|
} else {
|
|
map.setZoomAround(e.containerPoint, zoom);
|
|
}
|
|
}
|
|
});
|
|
|
|
// @section Handlers
|
|
//
|
|
// Map properties include interaction handlers that allow you to control
|
|
// interaction behavior in runtime, enabling or disabling certain features such
|
|
// as dragging or touch zoom (see `Handler` methods). For example:
|
|
//
|
|
// ```js
|
|
// map.doubleClickZoom.disable();
|
|
// ```
|
|
//
|
|
// @property doubleClickZoom: Handler
|
|
// Double click zoom handler.
|
|
L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
|
|
|
|
|
|
|
|
/*
|
|
* L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
|
|
*/
|
|
|
|
// @namespace Map
|
|
// @section Interaction Options
|
|
L.Map.mergeOptions({
|
|
// @section Mousewheel options
|
|
// @option scrollWheelZoom: Boolean|String = true
|
|
// Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
|
|
// it will zoom to the center of the view regardless of where the mouse was.
|
|
scrollWheelZoom: true,
|
|
|
|
// @option wheelDebounceTime: Number = 40
|
|
// Limits the rate at which a wheel can fire (in milliseconds). By default
|
|
// user can't zoom via wheel more often than once per 40 ms.
|
|
wheelDebounceTime: 40,
|
|
|
|
// @option wheelPxPerZoomLevel: Number = 60
|
|
// How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
|
|
// mean a change of one full zoom level. Smaller values will make wheel-zooming
|
|
// faster (and vice versa).
|
|
wheelPxPerZoomLevel: 60
|
|
});
|
|
|
|
L.Map.ScrollWheelZoom = L.Handler.extend({
|
|
addHooks: function () {
|
|
L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
|
|
|
|
this._delta = 0;
|
|
},
|
|
|
|
removeHooks: function () {
|
|
L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll, this);
|
|
},
|
|
|
|
_onWheelScroll: function (e) {
|
|
var delta = L.DomEvent.getWheelDelta(e);
|
|
|
|
var debounce = this._map.options.wheelDebounceTime;
|
|
|
|
this._delta += delta;
|
|
this._lastMousePos = this._map.mouseEventToContainerPoint(e);
|
|
|
|
if (!this._startTime) {
|
|
this._startTime = +new Date();
|
|
}
|
|
|
|
var left = Math.max(debounce - (+new Date() - this._startTime), 0);
|
|
|
|
clearTimeout(this._timer);
|
|
this._timer = setTimeout(L.bind(this._performZoom, this), left);
|
|
|
|
L.DomEvent.stop(e);
|
|
},
|
|
|
|
_performZoom: function () {
|
|
var map = this._map,
|
|
zoom = map.getZoom(),
|
|
snap = this._map.options.zoomSnap || 0;
|
|
|
|
map._stop(); // stop panning and fly animations if any
|
|
|
|
// map the delta with a sigmoid function to -4..4 range leaning on -1..1
|
|
var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
|
|
d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
|
|
d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
|
|
delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
|
|
|
|
this._delta = 0;
|
|
this._startTime = null;
|
|
|
|
if (!delta) { return; }
|
|
|
|
if (map.options.scrollWheelZoom === 'center') {
|
|
map.setZoom(zoom + delta);
|
|
} else {
|
|
map.setZoomAround(this._lastMousePos, zoom + delta);
|
|
}
|
|
}
|
|
});
|
|
|
|
// @section Handlers
|
|
// @property scrollWheelZoom: Handler
|
|
// Scroll wheel zoom handler.
|
|
L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
|
|
|
|
|
|
|
|
/*
|
|
* Extends the event handling code with double tap support for mobile browsers.
|
|
*/
|
|
|
|
L.extend(L.DomEvent, {
|
|
|
|
_touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
|
|
_touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend',
|
|
|
|
// inspired by Zepto touch code by Thomas Fuchs
|
|
addDoubleTapListener: function (obj, handler, id) {
|
|
var last, touch,
|
|
doubleTap = false,
|
|
delay = 250;
|
|
|
|
function onTouchStart(e) {
|
|
var count;
|
|
|
|
if (L.Browser.pointer) {
|
|
count = L.DomEvent._pointersCount;
|
|
} else {
|
|
count = e.touches.length;
|
|
}
|
|
|
|
if (count > 1) { return; }
|
|
|
|
var now = Date.now(),
|
|
delta = now - (last || now);
|
|
|
|
touch = e.touches ? e.touches[0] : e;
|
|
doubleTap = (delta > 0 && delta <= delay);
|
|
last = now;
|
|
}
|
|
|
|
function onTouchEnd() {
|
|
if (doubleTap && !touch.cancelBubble) {
|
|
if (L.Browser.pointer) {
|
|
// work around .type being readonly with MSPointer* events
|
|
var newTouch = {},
|
|
prop, i;
|
|
|
|
for (i in touch) {
|
|
prop = touch[i];
|
|
newTouch[i] = prop && prop.bind ? prop.bind(touch) : prop;
|
|
}
|
|
touch = newTouch;
|
|
}
|
|
touch.type = 'dblclick';
|
|
handler(touch);
|
|
last = null;
|
|
}
|
|
}
|
|
|
|
var pre = '_leaflet_',
|
|
touchstart = this._touchstart,
|
|
touchend = this._touchend;
|
|
|
|
obj[pre + touchstart + id] = onTouchStart;
|
|
obj[pre + touchend + id] = onTouchEnd;
|
|
obj[pre + 'dblclick' + id] = handler;
|
|
|
|
obj.addEventListener(touchstart, onTouchStart, false);
|
|
obj.addEventListener(touchend, onTouchEnd, false);
|
|
|
|
// On some platforms (notably, chrome on win10 + touchscreen + mouse),
|
|
// the browser doesn't fire touchend/pointerup events but does fire
|
|
// native dblclicks. See #4127.
|
|
if (!L.Browser.edge) {
|
|
obj.addEventListener('dblclick', handler, false);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
removeDoubleTapListener: function (obj, id) {
|
|
var pre = '_leaflet_',
|
|
touchstart = obj[pre + this._touchstart + id],
|
|
touchend = obj[pre + this._touchend + id],
|
|
dblclick = obj[pre + 'dblclick' + id];
|
|
|
|
obj.removeEventListener(this._touchstart, touchstart, false);
|
|
obj.removeEventListener(this._touchend, touchend, false);
|
|
if (!L.Browser.edge) {
|
|
obj.removeEventListener('dblclick', dblclick, false);
|
|
}
|
|
|
|
return this;
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
|
|
*/
|
|
|
|
L.extend(L.DomEvent, {
|
|
|
|
POINTER_DOWN: L.Browser.msPointer ? 'MSPointerDown' : 'pointerdown',
|
|
POINTER_MOVE: L.Browser.msPointer ? 'MSPointerMove' : 'pointermove',
|
|
POINTER_UP: L.Browser.msPointer ? 'MSPointerUp' : 'pointerup',
|
|
POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel',
|
|
TAG_WHITE_LIST: ['INPUT', 'SELECT', 'OPTION'],
|
|
|
|
_pointers: {},
|
|
_pointersCount: 0,
|
|
|
|
// Provides a touch events wrapper for (ms)pointer events.
|
|
// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
|
|
|
|
addPointerListener: function (obj, type, handler, id) {
|
|
|
|
if (type === 'touchstart') {
|
|
this._addPointerStart(obj, handler, id);
|
|
|
|
} else if (type === 'touchmove') {
|
|
this._addPointerMove(obj, handler, id);
|
|
|
|
} else if (type === 'touchend') {
|
|
this._addPointerEnd(obj, handler, id);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
removePointerListener: function (obj, type, id) {
|
|
var handler = obj['_leaflet_' + type + id];
|
|
|
|
if (type === 'touchstart') {
|
|
obj.removeEventListener(this.POINTER_DOWN, handler, false);
|
|
|
|
} else if (type === 'touchmove') {
|
|
obj.removeEventListener(this.POINTER_MOVE, handler, false);
|
|
|
|
} else if (type === 'touchend') {
|
|
obj.removeEventListener(this.POINTER_UP, handler, false);
|
|
obj.removeEventListener(this.POINTER_CANCEL, handler, false);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
_addPointerStart: function (obj, handler, id) {
|
|
var onDown = L.bind(function (e) {
|
|
if (e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
|
|
// In IE11, some touch events needs to fire for form controls, or
|
|
// the controls will stop working. We keep a whitelist of tag names that
|
|
// need these events. For other target tags, we prevent default on the event.
|
|
if (this.TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
|
|
L.DomEvent.preventDefault(e);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this._handlePointer(e, handler);
|
|
}, this);
|
|
|
|
obj['_leaflet_touchstart' + id] = onDown;
|
|
obj.addEventListener(this.POINTER_DOWN, onDown, false);
|
|
|
|
// need to keep track of what pointers and how many are active to provide e.touches emulation
|
|
if (!this._pointerDocListener) {
|
|
var pointerUp = L.bind(this._globalPointerUp, this);
|
|
|
|
// we listen documentElement as any drags that end by moving the touch off the screen get fired there
|
|
document.documentElement.addEventListener(this.POINTER_DOWN, L.bind(this._globalPointerDown, this), true);
|
|
document.documentElement.addEventListener(this.POINTER_MOVE, L.bind(this._globalPointerMove, this), true);
|
|
document.documentElement.addEventListener(this.POINTER_UP, pointerUp, true);
|
|
document.documentElement.addEventListener(this.POINTER_CANCEL, pointerUp, true);
|
|
|
|
this._pointerDocListener = true;
|
|
}
|
|
},
|
|
|
|
_globalPointerDown: function (e) {
|
|
this._pointers[e.pointerId] = e;
|
|
this._pointersCount++;
|
|
},
|
|
|
|
_globalPointerMove: function (e) {
|
|
if (this._pointers[e.pointerId]) {
|
|
this._pointers[e.pointerId] = e;
|
|
}
|
|
},
|
|
|
|
_globalPointerUp: function (e) {
|
|
delete this._pointers[e.pointerId];
|
|
this._pointersCount--;
|
|
},
|
|
|
|
_handlePointer: function (e, handler) {
|
|
e.touches = [];
|
|
for (var i in this._pointers) {
|
|
e.touches.push(this._pointers[i]);
|
|
}
|
|
e.changedTouches = [e];
|
|
|
|
handler(e);
|
|
},
|
|
|
|
_addPointerMove: function (obj, handler, id) {
|
|
var onMove = L.bind(function (e) {
|
|
// don't fire touch moves when mouse isn't down
|
|
if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
|
|
|
|
this._handlePointer(e, handler);
|
|
}, this);
|
|
|
|
obj['_leaflet_touchmove' + id] = onMove;
|
|
obj.addEventListener(this.POINTER_MOVE, onMove, false);
|
|
},
|
|
|
|
_addPointerEnd: function (obj, handler, id) {
|
|
var onUp = L.bind(function (e) {
|
|
this._handlePointer(e, handler);
|
|
}, this);
|
|
|
|
obj['_leaflet_touchend' + id] = onUp;
|
|
obj.addEventListener(this.POINTER_UP, onUp, false);
|
|
obj.addEventListener(this.POINTER_CANCEL, onUp, false);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
|
|
*/
|
|
|
|
// @namespace Map
|
|
// @section Interaction Options
|
|
L.Map.mergeOptions({
|
|
// @section Touch interaction options
|
|
// @option touchZoom: Boolean|String = *
|
|
// Whether the map can be zoomed by touch-dragging with two fingers. If
|
|
// passed `'center'`, it will zoom to the center of the view regardless of
|
|
// where the touch events (fingers) were. Enabled for touch-capable web
|
|
// browsers except for old Androids.
|
|
touchZoom: L.Browser.touch && !L.Browser.android23,
|
|
|
|
// @option bounceAtZoomLimits: Boolean = true
|
|
// Set it to false if you don't want the map to zoom beyond min/max zoom
|
|
// and then bounce back when pinch-zooming.
|
|
bounceAtZoomLimits: true
|
|
});
|
|
|
|
L.Map.TouchZoom = L.Handler.extend({
|
|
addHooks: function () {
|
|
L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
|
|
L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
|
|
},
|
|
|
|
removeHooks: function () {
|
|
L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
|
|
L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
|
|
},
|
|
|
|
_onTouchStart: function (e) {
|
|
var map = this._map;
|
|
if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
|
|
|
|
var p1 = map.mouseEventToContainerPoint(e.touches[0]),
|
|
p2 = map.mouseEventToContainerPoint(e.touches[1]);
|
|
|
|
this._centerPoint = map.getSize()._divideBy(2);
|
|
this._startLatLng = map.containerPointToLatLng(this._centerPoint);
|
|
if (map.options.touchZoom !== 'center') {
|
|
this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
|
|
}
|
|
|
|
this._startDist = p1.distanceTo(p2);
|
|
this._startZoom = map.getZoom();
|
|
|
|
this._moved = false;
|
|
this._zooming = true;
|
|
|
|
map._stop();
|
|
|
|
L.DomEvent
|
|
.on(document, 'touchmove', this._onTouchMove, this)
|
|
.on(document, 'touchend', this._onTouchEnd, this);
|
|
|
|
L.DomEvent.preventDefault(e);
|
|
},
|
|
|
|
_onTouchMove: function (e) {
|
|
if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
|
|
|
|
var map = this._map,
|
|
p1 = map.mouseEventToContainerPoint(e.touches[0]),
|
|
p2 = map.mouseEventToContainerPoint(e.touches[1]),
|
|
scale = p1.distanceTo(p2) / this._startDist;
|
|
|
|
|
|
this._zoom = map.getScaleZoom(scale, this._startZoom);
|
|
|
|
if (!map.options.bounceAtZoomLimits && (
|
|
(this._zoom < map.getMinZoom() && scale < 1) ||
|
|
(this._zoom > map.getMaxZoom() && scale > 1))) {
|
|
this._zoom = map._limitZoom(this._zoom);
|
|
}
|
|
|
|
if (map.options.touchZoom === 'center') {
|
|
this._center = this._startLatLng;
|
|
if (scale === 1) { return; }
|
|
} else {
|
|
// Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
|
|
var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
|
|
if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
|
|
this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
|
|
}
|
|
|
|
if (!this._moved) {
|
|
map._moveStart(true);
|
|
this._moved = true;
|
|
}
|
|
|
|
L.Util.cancelAnimFrame(this._animRequest);
|
|
|
|
var moveFn = L.bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
|
|
this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
|
|
|
|
L.DomEvent.preventDefault(e);
|
|
},
|
|
|
|
_onTouchEnd: function () {
|
|
if (!this._moved || !this._zooming) {
|
|
this._zooming = false;
|
|
return;
|
|
}
|
|
|
|
this._zooming = false;
|
|
L.Util.cancelAnimFrame(this._animRequest);
|
|
|
|
L.DomEvent
|
|
.off(document, 'touchmove', this._onTouchMove)
|
|
.off(document, 'touchend', this._onTouchEnd);
|
|
|
|
// Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
|
|
if (this._map.options.zoomAnimation) {
|
|
this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
|
|
} else {
|
|
this._map._resetView(this._center, this._map._limitZoom(this._zoom));
|
|
}
|
|
}
|
|
});
|
|
|
|
// @section Handlers
|
|
// @property touchZoom: Handler
|
|
// Touch zoom handler.
|
|
L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
|
|
|
|
|
|
|
|
/*
|
|
* L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
|
|
*/
|
|
|
|
// @namespace Map
|
|
// @section Interaction Options
|
|
L.Map.mergeOptions({
|
|
// @section Touch interaction options
|
|
// @option tap: Boolean = true
|
|
// Enables mobile hacks for supporting instant taps (fixing 200ms click
|
|
// delay on iOS/Android) and touch holds (fired as `contextmenu` events).
|
|
tap: true,
|
|
|
|
// @option tapTolerance: Number = 15
|
|
// The max number of pixels a user can shift his finger during touch
|
|
// for it to be considered a valid tap.
|
|
tapTolerance: 15
|
|
});
|
|
|
|
L.Map.Tap = L.Handler.extend({
|
|
addHooks: function () {
|
|
L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this);
|
|
},
|
|
|
|
removeHooks: function () {
|
|
L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this);
|
|
},
|
|
|
|
_onDown: function (e) {
|
|
if (!e.touches) { return; }
|
|
|
|
L.DomEvent.preventDefault(e);
|
|
|
|
this._fireClick = true;
|
|
|
|
// don't simulate click or track longpress if more than 1 touch
|
|
if (e.touches.length > 1) {
|
|
this._fireClick = false;
|
|
clearTimeout(this._holdTimeout);
|
|
return;
|
|
}
|
|
|
|
var first = e.touches[0],
|
|
el = first.target;
|
|
|
|
this._startPos = this._newPos = new L.Point(first.clientX, first.clientY);
|
|
|
|
// if touching a link, highlight it
|
|
if (el.tagName && el.tagName.toLowerCase() === 'a') {
|
|
L.DomUtil.addClass(el, 'leaflet-active');
|
|
}
|
|
|
|
// simulate long hold but setting a timeout
|
|
this._holdTimeout = setTimeout(L.bind(function () {
|
|
if (this._isTapValid()) {
|
|
this._fireClick = false;
|
|
this._onUp();
|
|
this._simulateEvent('contextmenu', first);
|
|
}
|
|
}, this), 1000);
|
|
|
|
this._simulateEvent('mousedown', first);
|
|
|
|
L.DomEvent.on(document, {
|
|
touchmove: this._onMove,
|
|
touchend: this._onUp
|
|
}, this);
|
|
},
|
|
|
|
_onUp: function (e) {
|
|
clearTimeout(this._holdTimeout);
|
|
|
|
L.DomEvent.off(document, {
|
|
touchmove: this._onMove,
|
|
touchend: this._onUp
|
|
}, this);
|
|
|
|
if (this._fireClick && e && e.changedTouches) {
|
|
|
|
var first = e.changedTouches[0],
|
|
el = first.target;
|
|
|
|
if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
|
|
L.DomUtil.removeClass(el, 'leaflet-active');
|
|
}
|
|
|
|
this._simulateEvent('mouseup', first);
|
|
|
|
// simulate click if the touch didn't move too much
|
|
if (this._isTapValid()) {
|
|
this._simulateEvent('click', first);
|
|
}
|
|
}
|
|
},
|
|
|
|
_isTapValid: function () {
|
|
return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
|
|
},
|
|
|
|
_onMove: function (e) {
|
|
var first = e.touches[0];
|
|
this._newPos = new L.Point(first.clientX, first.clientY);
|
|
this._simulateEvent('mousemove', first);
|
|
},
|
|
|
|
_simulateEvent: function (type, e) {
|
|
var simulatedEvent = document.createEvent('MouseEvents');
|
|
|
|
simulatedEvent._simulated = true;
|
|
e.target._simulatedClick = true;
|
|
|
|
simulatedEvent.initMouseEvent(
|
|
type, true, true, window, 1,
|
|
e.screenX, e.screenY,
|
|
e.clientX, e.clientY,
|
|
false, false, false, false, 0, null);
|
|
|
|
e.target.dispatchEvent(simulatedEvent);
|
|
}
|
|
});
|
|
|
|
// @section Handlers
|
|
// @property tap: Handler
|
|
// Mobile touch hacks (quick tap and touch hold) handler.
|
|
if (L.Browser.touch && !L.Browser.pointer) {
|
|
L.Map.addInitHook('addHandler', 'tap', L.Map.Tap);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
|
|
* (zoom to a selected bounding box), enabled by default.
|
|
*/
|
|
|
|
// @namespace Map
|
|
// @section Interaction Options
|
|
L.Map.mergeOptions({
|
|
// @option boxZoom: Boolean = true
|
|
// Whether the map can be zoomed to a rectangular area specified by
|
|
// dragging the mouse while pressing the shift key.
|
|
boxZoom: true
|
|
});
|
|
|
|
L.Map.BoxZoom = L.Handler.extend({
|
|
initialize: function (map) {
|
|
this._map = map;
|
|
this._container = map._container;
|
|
this._pane = map._panes.overlayPane;
|
|
},
|
|
|
|
addHooks: function () {
|
|
L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
|
|
},
|
|
|
|
removeHooks: function () {
|
|
L.DomEvent.off(this._container, 'mousedown', this._onMouseDown, this);
|
|
},
|
|
|
|
moved: function () {
|
|
return this._moved;
|
|
},
|
|
|
|
_resetState: function () {
|
|
this._moved = false;
|
|
},
|
|
|
|
_onMouseDown: function (e) {
|
|
if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
|
|
|
|
this._resetState();
|
|
|
|
L.DomUtil.disableTextSelection();
|
|
L.DomUtil.disableImageDrag();
|
|
|
|
this._startPoint = this._map.mouseEventToContainerPoint(e);
|
|
|
|
L.DomEvent.on(document, {
|
|
contextmenu: L.DomEvent.stop,
|
|
mousemove: this._onMouseMove,
|
|
mouseup: this._onMouseUp,
|
|
keydown: this._onKeyDown
|
|
}, this);
|
|
},
|
|
|
|
_onMouseMove: function (e) {
|
|
if (!this._moved) {
|
|
this._moved = true;
|
|
|
|
this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._container);
|
|
L.DomUtil.addClass(this._container, 'leaflet-crosshair');
|
|
|
|
this._map.fire('boxzoomstart');
|
|
}
|
|
|
|
this._point = this._map.mouseEventToContainerPoint(e);
|
|
|
|
var bounds = new L.Bounds(this._point, this._startPoint),
|
|
size = bounds.getSize();
|
|
|
|
L.DomUtil.setPosition(this._box, bounds.min);
|
|
|
|
this._box.style.width = size.x + 'px';
|
|
this._box.style.height = size.y + 'px';
|
|
},
|
|
|
|
_finish: function () {
|
|
if (this._moved) {
|
|
L.DomUtil.remove(this._box);
|
|
L.DomUtil.removeClass(this._container, 'leaflet-crosshair');
|
|
}
|
|
|
|
L.DomUtil.enableTextSelection();
|
|
L.DomUtil.enableImageDrag();
|
|
|
|
L.DomEvent.off(document, {
|
|
contextmenu: L.DomEvent.stop,
|
|
mousemove: this._onMouseMove,
|
|
mouseup: this._onMouseUp,
|
|
keydown: this._onKeyDown
|
|
}, this);
|
|
},
|
|
|
|
_onMouseUp: function (e) {
|
|
if ((e.which !== 1) && (e.button !== 1)) { return; }
|
|
|
|
this._finish();
|
|
|
|
if (!this._moved) { return; }
|
|
// Postpone to next JS tick so internal click event handling
|
|
// still see it as "moved".
|
|
setTimeout(L.bind(this._resetState, this), 0);
|
|
|
|
var bounds = new L.LatLngBounds(
|
|
this._map.containerPointToLatLng(this._startPoint),
|
|
this._map.containerPointToLatLng(this._point));
|
|
|
|
this._map
|
|
.fitBounds(bounds)
|
|
.fire('boxzoomend', {boxZoomBounds: bounds});
|
|
},
|
|
|
|
_onKeyDown: function (e) {
|
|
if (e.keyCode === 27) {
|
|
this._finish();
|
|
}
|
|
}
|
|
});
|
|
|
|
// @section Handlers
|
|
// @property boxZoom: Handler
|
|
// Box (shift-drag with mouse) zoom handler.
|
|
L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
|
|
|
|
|
|
|
|
/*
|
|
* L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
|
|
*/
|
|
|
|
// @namespace Map
|
|
// @section Keyboard Navigation Options
|
|
L.Map.mergeOptions({
|
|
// @option keyboard: Boolean = true
|
|
// Makes the map focusable and allows users to navigate the map with keyboard
|
|
// arrows and `+`/`-` keys.
|
|
keyboard: true,
|
|
|
|
// @option keyboardPanDelta: Number = 80
|
|
// Amount of pixels to pan when pressing an arrow key.
|
|
keyboardPanDelta: 80
|
|
});
|
|
|
|
L.Map.Keyboard = L.Handler.extend({
|
|
|
|
keyCodes: {
|
|
left: [37],
|
|
right: [39],
|
|
down: [40],
|
|
up: [38],
|
|
zoomIn: [187, 107, 61, 171],
|
|
zoomOut: [189, 109, 54, 173]
|
|
},
|
|
|
|
initialize: function (map) {
|
|
this._map = map;
|
|
|
|
this._setPanDelta(map.options.keyboardPanDelta);
|
|
this._setZoomDelta(map.options.zoomDelta);
|
|
},
|
|
|
|
addHooks: function () {
|
|
var container = this._map._container;
|
|
|
|
// make the container focusable by tabbing
|
|
if (container.tabIndex <= 0) {
|
|
container.tabIndex = '0';
|
|
}
|
|
|
|
L.DomEvent.on(container, {
|
|
focus: this._onFocus,
|
|
blur: this._onBlur,
|
|
mousedown: this._onMouseDown
|
|
}, this);
|
|
|
|
this._map.on({
|
|
focus: this._addHooks,
|
|
blur: this._removeHooks
|
|
}, this);
|
|
},
|
|
|
|
removeHooks: function () {
|
|
this._removeHooks();
|
|
|
|
L.DomEvent.off(this._map._container, {
|
|
focus: this._onFocus,
|
|
blur: this._onBlur,
|
|
mousedown: this._onMouseDown
|
|
}, this);
|
|
|
|
this._map.off({
|
|
focus: this._addHooks,
|
|
blur: this._removeHooks
|
|
}, this);
|
|
},
|
|
|
|
_onMouseDown: function () {
|
|
if (this._focused) { return; }
|
|
|
|
var body = document.body,
|
|
docEl = document.documentElement,
|
|
top = body.scrollTop || docEl.scrollTop,
|
|
left = body.scrollLeft || docEl.scrollLeft;
|
|
|
|
this._map._container.focus();
|
|
|
|
window.scrollTo(left, top);
|
|
},
|
|
|
|
_onFocus: function () {
|
|
this._focused = true;
|
|
this._map.fire('focus');
|
|
},
|
|
|
|
_onBlur: function () {
|
|
this._focused = false;
|
|
this._map.fire('blur');
|
|
},
|
|
|
|
_setPanDelta: function (panDelta) {
|
|
var keys = this._panKeys = {},
|
|
codes = this.keyCodes,
|
|
i, len;
|
|
|
|
for (i = 0, len = codes.left.length; i < len; i++) {
|
|
keys[codes.left[i]] = [-1 * panDelta, 0];
|
|
}
|
|
for (i = 0, len = codes.right.length; i < len; i++) {
|
|
keys[codes.right[i]] = [panDelta, 0];
|
|
}
|
|
for (i = 0, len = codes.down.length; i < len; i++) {
|
|
keys[codes.down[i]] = [0, panDelta];
|
|
}
|
|
for (i = 0, len = codes.up.length; i < len; i++) {
|
|
keys[codes.up[i]] = [0, -1 * panDelta];
|
|
}
|
|
},
|
|
|
|
_setZoomDelta: function (zoomDelta) {
|
|
var keys = this._zoomKeys = {},
|
|
codes = this.keyCodes,
|
|
i, len;
|
|
|
|
for (i = 0, len = codes.zoomIn.length; i < len; i++) {
|
|
keys[codes.zoomIn[i]] = zoomDelta;
|
|
}
|
|
for (i = 0, len = codes.zoomOut.length; i < len; i++) {
|
|
keys[codes.zoomOut[i]] = -zoomDelta;
|
|
}
|
|
},
|
|
|
|
_addHooks: function () {
|
|
L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
|
|
},
|
|
|
|
_removeHooks: function () {
|
|
L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
|
|
},
|
|
|
|
_onKeyDown: function (e) {
|
|
if (e.altKey || e.ctrlKey || e.metaKey) { return; }
|
|
|
|
var key = e.keyCode,
|
|
map = this._map,
|
|
offset;
|
|
|
|
if (key in this._panKeys) {
|
|
|
|
if (map._panAnim && map._panAnim._inProgress) { return; }
|
|
|
|
offset = this._panKeys[key];
|
|
if (e.shiftKey) {
|
|
offset = L.point(offset).multiplyBy(3);
|
|
}
|
|
|
|
map.panBy(offset);
|
|
|
|
if (map.options.maxBounds) {
|
|
map.panInsideBounds(map.options.maxBounds);
|
|
}
|
|
|
|
} else if (key in this._zoomKeys) {
|
|
map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
|
|
|
|
} else if (key === 27) {
|
|
map.closePopup();
|
|
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
L.DomEvent.stop(e);
|
|
}
|
|
});
|
|
|
|
// @section Handlers
|
|
// @section Handlers
|
|
// @property keyboard: Handler
|
|
// Keyboard navigation handler.
|
|
L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
|
|
|
|
|
|
|
|
/*
|
|
* L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
|
|
*/
|
|
|
|
|
|
/* @namespace Marker
|
|
* @section Interaction handlers
|
|
*
|
|
* Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example:
|
|
*
|
|
* ```js
|
|
* marker.dragging.disable();
|
|
* ```
|
|
*
|
|
* @property dragging: Handler
|
|
* Marker dragging handler (by both mouse and touch).
|
|
*/
|
|
|
|
L.Handler.MarkerDrag = L.Handler.extend({
|
|
initialize: function (marker) {
|
|
this._marker = marker;
|
|
},
|
|
|
|
addHooks: function () {
|
|
var icon = this._marker._icon;
|
|
|
|
if (!this._draggable) {
|
|
this._draggable = new L.Draggable(icon, icon, true);
|
|
}
|
|
|
|
this._draggable.on({
|
|
dragstart: this._onDragStart,
|
|
drag: this._onDrag,
|
|
dragend: this._onDragEnd
|
|
}, this).enable();
|
|
|
|
L.DomUtil.addClass(icon, 'leaflet-marker-draggable');
|
|
},
|
|
|
|
removeHooks: function () {
|
|
this._draggable.off({
|
|
dragstart: this._onDragStart,
|
|
drag: this._onDrag,
|
|
dragend: this._onDragEnd
|
|
}, this).disable();
|
|
|
|
if (this._marker._icon) {
|
|
L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
|
|
}
|
|
},
|
|
|
|
moved: function () {
|
|
return this._draggable && this._draggable._moved;
|
|
},
|
|
|
|
_onDragStart: function () {
|
|
// @section Dragging events
|
|
// @event dragstart: Event
|
|
// Fired when the user starts dragging the marker.
|
|
|
|
// @event movestart: Event
|
|
// Fired when the marker starts moving (because of dragging).
|
|
|
|
this._oldLatLng = this._marker.getLatLng();
|
|
this._marker
|
|
.closePopup()
|
|
.fire('movestart')
|
|
.fire('dragstart');
|
|
},
|
|
|
|
_onDrag: function (e) {
|
|
var marker = this._marker,
|
|
shadow = marker._shadow,
|
|
iconPos = L.DomUtil.getPosition(marker._icon),
|
|
latlng = marker._map.layerPointToLatLng(iconPos);
|
|
|
|
// update shadow position
|
|
if (shadow) {
|
|
L.DomUtil.setPosition(shadow, iconPos);
|
|
}
|
|
|
|
marker._latlng = latlng;
|
|
e.latlng = latlng;
|
|
e.oldLatLng = this._oldLatLng;
|
|
|
|
// @event drag: Event
|
|
// Fired repeatedly while the user drags the marker.
|
|
marker
|
|
.fire('move', e)
|
|
.fire('drag', e);
|
|
},
|
|
|
|
_onDragEnd: function (e) {
|
|
// @event dragend: DragEndEvent
|
|
// Fired when the user stops dragging the marker.
|
|
|
|
// @event moveend: Event
|
|
// Fired when the marker stops moving (because of dragging).
|
|
delete this._oldLatLng;
|
|
this._marker
|
|
.fire('moveend')
|
|
.fire('dragend', e);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @class Control
|
|
* @aka L.Control
|
|
* @inherits Class
|
|
*
|
|
* L.Control is a base class for implementing map controls. Handles positioning.
|
|
* All other controls extend from this class.
|
|
*/
|
|
|
|
L.Control = L.Class.extend({
|
|
// @section
|
|
// @aka Control options
|
|
options: {
|
|
// @option position: String = 'topright'
|
|
// The position of the control (one of the map corners). Possible values are `'topleft'`,
|
|
// `'topright'`, `'bottomleft'` or `'bottomright'`
|
|
position: 'topright'
|
|
},
|
|
|
|
initialize: function (options) {
|
|
L.setOptions(this, options);
|
|
},
|
|
|
|
/* @section
|
|
* Classes extending L.Control will inherit the following methods:
|
|
*
|
|
* @method getPosition: string
|
|
* Returns the position of the control.
|
|
*/
|
|
getPosition: function () {
|
|
return this.options.position;
|
|
},
|
|
|
|
// @method setPosition(position: string): this
|
|
// Sets the position of the control.
|
|
setPosition: function (position) {
|
|
var map = this._map;
|
|
|
|
if (map) {
|
|
map.removeControl(this);
|
|
}
|
|
|
|
this.options.position = position;
|
|
|
|
if (map) {
|
|
map.addControl(this);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method getContainer: HTMLElement
|
|
// Returns the HTMLElement that contains the control.
|
|
getContainer: function () {
|
|
return this._container;
|
|
},
|
|
|
|
// @method addTo(map: Map): this
|
|
// Adds the control to the given map.
|
|
addTo: function (map) {
|
|
this.remove();
|
|
this._map = map;
|
|
|
|
var container = this._container = this.onAdd(map),
|
|
pos = this.getPosition(),
|
|
corner = map._controlCorners[pos];
|
|
|
|
L.DomUtil.addClass(container, 'leaflet-control');
|
|
|
|
if (pos.indexOf('bottom') !== -1) {
|
|
corner.insertBefore(container, corner.firstChild);
|
|
} else {
|
|
corner.appendChild(container);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method remove: this
|
|
// Removes the control from the map it is currently active on.
|
|
remove: function () {
|
|
if (!this._map) {
|
|
return this;
|
|
}
|
|
|
|
L.DomUtil.remove(this._container);
|
|
|
|
if (this.onRemove) {
|
|
this.onRemove(this._map);
|
|
}
|
|
|
|
this._map = null;
|
|
|
|
return this;
|
|
},
|
|
|
|
_refocusOnMap: function (e) {
|
|
// if map exists and event is not a keyboard event
|
|
if (this._map && e && e.screenX > 0 && e.screenY > 0) {
|
|
this._map.getContainer().focus();
|
|
}
|
|
}
|
|
});
|
|
|
|
L.control = function (options) {
|
|
return new L.Control(options);
|
|
};
|
|
|
|
/* @section Extension methods
|
|
* @uninheritable
|
|
*
|
|
* Every control should extend from `L.Control` and (re-)implement the following methods.
|
|
*
|
|
* @method onAdd(map: Map): HTMLElement
|
|
* Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
|
|
*
|
|
* @method onRemove(map: Map)
|
|
* Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
|
|
*/
|
|
|
|
/* @namespace Map
|
|
* @section Methods for Layers and Controls
|
|
*/
|
|
L.Map.include({
|
|
// @method addControl(control: Control): this
|
|
// Adds the given control to the map
|
|
addControl: function (control) {
|
|
control.addTo(this);
|
|
return this;
|
|
},
|
|
|
|
// @method removeControl(control: Control): this
|
|
// Removes the given control from the map
|
|
removeControl: function (control) {
|
|
control.remove();
|
|
return this;
|
|
},
|
|
|
|
_initControlPos: function () {
|
|
var corners = this._controlCorners = {},
|
|
l = 'leaflet-',
|
|
container = this._controlContainer =
|
|
L.DomUtil.create('div', l + 'control-container', this._container);
|
|
|
|
function createCorner(vSide, hSide) {
|
|
var className = l + vSide + ' ' + l + hSide;
|
|
|
|
corners[vSide + hSide] = L.DomUtil.create('div', className, container);
|
|
}
|
|
|
|
createCorner('top', 'left');
|
|
createCorner('top', 'right');
|
|
createCorner('bottom', 'left');
|
|
createCorner('bottom', 'right');
|
|
},
|
|
|
|
_clearControlPos: function () {
|
|
L.DomUtil.remove(this._controlContainer);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* @class Control.Zoom
|
|
* @aka L.Control.Zoom
|
|
* @inherits Control
|
|
*
|
|
* A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`.
|
|
*/
|
|
|
|
L.Control.Zoom = L.Control.extend({
|
|
// @section
|
|
// @aka Control.Zoom options
|
|
options: {
|
|
position: 'topleft',
|
|
|
|
// @option zoomInText: String = '+'
|
|
// The text set on the 'zoom in' button.
|
|
zoomInText: '+',
|
|
|
|
// @option zoomInTitle: String = 'Zoom in'
|
|
// The title set on the 'zoom in' button.
|
|
zoomInTitle: 'Zoom in',
|
|
|
|
// @option zoomOutText: String = '-'
|
|
// The text set on the 'zoom out' button.
|
|
zoomOutText: '-',
|
|
|
|
// @option zoomOutTitle: String = 'Zoom out'
|
|
// The title set on the 'zoom out' button.
|
|
zoomOutTitle: 'Zoom out'
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
var zoomName = 'leaflet-control-zoom',
|
|
container = L.DomUtil.create('div', zoomName + ' leaflet-bar'),
|
|
options = this.options;
|
|
|
|
this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle,
|
|
zoomName + '-in', container, this._zoomIn);
|
|
this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
|
|
zoomName + '-out', container, this._zoomOut);
|
|
|
|
this._updateDisabled();
|
|
map.on('zoomend zoomlevelschange', this._updateDisabled, this);
|
|
|
|
return container;
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
map.off('zoomend zoomlevelschange', this._updateDisabled, this);
|
|
},
|
|
|
|
disable: function () {
|
|
this._disabled = true;
|
|
this._updateDisabled();
|
|
return this;
|
|
},
|
|
|
|
enable: function () {
|
|
this._disabled = false;
|
|
this._updateDisabled();
|
|
return this;
|
|
},
|
|
|
|
_zoomIn: function (e) {
|
|
if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
|
|
this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
|
|
}
|
|
},
|
|
|
|
_zoomOut: function (e) {
|
|
if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
|
|
this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
|
|
}
|
|
},
|
|
|
|
_createButton: function (html, title, className, container, fn) {
|
|
var link = L.DomUtil.create('a', className, container);
|
|
link.innerHTML = html;
|
|
link.href = '#';
|
|
link.title = title;
|
|
|
|
/*
|
|
* Will force screen readers like VoiceOver to read this as "Zoom in - button"
|
|
*/
|
|
link.setAttribute('role', 'button');
|
|
link.setAttribute('aria-label', title);
|
|
|
|
L.DomEvent
|
|
.on(link, 'mousedown dblclick', L.DomEvent.stopPropagation)
|
|
.on(link, 'click', L.DomEvent.stop)
|
|
.on(link, 'click', fn, this)
|
|
.on(link, 'click', this._refocusOnMap, this);
|
|
|
|
return link;
|
|
},
|
|
|
|
_updateDisabled: function () {
|
|
var map = this._map,
|
|
className = 'leaflet-disabled';
|
|
|
|
L.DomUtil.removeClass(this._zoomInButton, className);
|
|
L.DomUtil.removeClass(this._zoomOutButton, className);
|
|
|
|
if (this._disabled || map._zoom === map.getMinZoom()) {
|
|
L.DomUtil.addClass(this._zoomOutButton, className);
|
|
}
|
|
if (this._disabled || map._zoom === map.getMaxZoom()) {
|
|
L.DomUtil.addClass(this._zoomInButton, className);
|
|
}
|
|
}
|
|
});
|
|
|
|
// @namespace Map
|
|
// @section Control options
|
|
// @option zoomControl: Boolean = true
|
|
// Whether a [zoom control](#control-zoom) is added to the map by default.
|
|
L.Map.mergeOptions({
|
|
zoomControl: true
|
|
});
|
|
|
|
L.Map.addInitHook(function () {
|
|
if (this.options.zoomControl) {
|
|
this.zoomControl = new L.Control.Zoom();
|
|
this.addControl(this.zoomControl);
|
|
}
|
|
});
|
|
|
|
// @namespace Control.Zoom
|
|
// @factory L.control.zoom(options: Control.Zoom options)
|
|
// Creates a zoom control
|
|
L.control.zoom = function (options) {
|
|
return new L.Control.Zoom(options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Control.Attribution
|
|
* @aka L.Control.Attribution
|
|
* @inherits Control
|
|
*
|
|
* The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control.
|
|
*/
|
|
|
|
L.Control.Attribution = L.Control.extend({
|
|
// @section
|
|
// @aka Control.Attribution options
|
|
options: {
|
|
position: 'bottomright',
|
|
|
|
// @option prefix: String = 'Leaflet'
|
|
// The HTML text shown before the attributions. Pass `false` to disable.
|
|
prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
|
|
},
|
|
|
|
initialize: function (options) {
|
|
L.setOptions(this, options);
|
|
|
|
this._attributions = {};
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
map.attributionControl = this;
|
|
this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
|
|
if (L.DomEvent) {
|
|
L.DomEvent.disableClickPropagation(this._container);
|
|
}
|
|
|
|
// TODO ugly, refactor
|
|
for (var i in map._layers) {
|
|
if (map._layers[i].getAttribution) {
|
|
this.addAttribution(map._layers[i].getAttribution());
|
|
}
|
|
}
|
|
|
|
this._update();
|
|
|
|
return this._container;
|
|
},
|
|
|
|
// @method setPrefix(prefix: String): this
|
|
// Sets the text before the attributions.
|
|
setPrefix: function (prefix) {
|
|
this.options.prefix = prefix;
|
|
this._update();
|
|
return this;
|
|
},
|
|
|
|
// @method addAttribution(text: String): this
|
|
// Adds an attribution text (e.g. `'Vector data © Mapbox'`).
|
|
addAttribution: function (text) {
|
|
if (!text) { return this; }
|
|
|
|
if (!this._attributions[text]) {
|
|
this._attributions[text] = 0;
|
|
}
|
|
this._attributions[text]++;
|
|
|
|
this._update();
|
|
|
|
return this;
|
|
},
|
|
|
|
// @method removeAttribution(text: String): this
|
|
// Removes an attribution text.
|
|
removeAttribution: function (text) {
|
|
if (!text) { return this; }
|
|
|
|
if (this._attributions[text]) {
|
|
this._attributions[text]--;
|
|
this._update();
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
_update: function () {
|
|
if (!this._map) { return; }
|
|
|
|
var attribs = [];
|
|
|
|
for (var i in this._attributions) {
|
|
if (this._attributions[i]) {
|
|
attribs.push(i);
|
|
}
|
|
}
|
|
|
|
var prefixAndAttribs = [];
|
|
|
|
if (this.options.prefix) {
|
|
prefixAndAttribs.push(this.options.prefix);
|
|
}
|
|
if (attribs.length) {
|
|
prefixAndAttribs.push(attribs.join(', '));
|
|
}
|
|
|
|
this._container.innerHTML = prefixAndAttribs.join(' | ');
|
|
}
|
|
});
|
|
|
|
// @namespace Map
|
|
// @section Control options
|
|
// @option attributionControl: Boolean = true
|
|
// Whether a [attribution control](#control-attribution) is added to the map by default.
|
|
L.Map.mergeOptions({
|
|
attributionControl: true
|
|
});
|
|
|
|
L.Map.addInitHook(function () {
|
|
if (this.options.attributionControl) {
|
|
new L.Control.Attribution().addTo(this);
|
|
}
|
|
});
|
|
|
|
// @namespace Control.Attribution
|
|
// @factory L.control.attribution(options: Control.Attribution options)
|
|
// Creates an attribution control.
|
|
L.control.attribution = function (options) {
|
|
return new L.Control.Attribution(options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Control.Scale
|
|
* @aka L.Control.Scale
|
|
* @inherits Control
|
|
*
|
|
* A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* L.control.scale().addTo(map);
|
|
* ```
|
|
*/
|
|
|
|
L.Control.Scale = L.Control.extend({
|
|
// @section
|
|
// @aka Control.Scale options
|
|
options: {
|
|
position: 'bottomleft',
|
|
|
|
// @option maxWidth: Number = 100
|
|
// Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
|
|
maxWidth: 100,
|
|
|
|
// @option metric: Boolean = True
|
|
// Whether to show the metric scale line (m/km).
|
|
metric: true,
|
|
|
|
// @option imperial: Boolean = True
|
|
// Whether to show the imperial scale line (mi/ft).
|
|
imperial: true
|
|
|
|
// @option updateWhenIdle: Boolean = false
|
|
// If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
var className = 'leaflet-control-scale',
|
|
container = L.DomUtil.create('div', className),
|
|
options = this.options;
|
|
|
|
this._addScales(options, className + '-line', container);
|
|
|
|
map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
|
|
map.whenReady(this._update, this);
|
|
|
|
return container;
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
|
|
},
|
|
|
|
_addScales: function (options, className, container) {
|
|
if (options.metric) {
|
|
this._mScale = L.DomUtil.create('div', className, container);
|
|
}
|
|
if (options.imperial) {
|
|
this._iScale = L.DomUtil.create('div', className, container);
|
|
}
|
|
},
|
|
|
|
_update: function () {
|
|
var map = this._map,
|
|
y = map.getSize().y / 2;
|
|
|
|
var maxMeters = map.distance(
|
|
map.containerPointToLatLng([0, y]),
|
|
map.containerPointToLatLng([this.options.maxWidth, y]));
|
|
|
|
this._updateScales(maxMeters);
|
|
},
|
|
|
|
_updateScales: function (maxMeters) {
|
|
if (this.options.metric && maxMeters) {
|
|
this._updateMetric(maxMeters);
|
|
}
|
|
if (this.options.imperial && maxMeters) {
|
|
this._updateImperial(maxMeters);
|
|
}
|
|
},
|
|
|
|
_updateMetric: function (maxMeters) {
|
|
var meters = this._getRoundNum(maxMeters),
|
|
label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
|
|
|
|
this._updateScale(this._mScale, label, meters / maxMeters);
|
|
},
|
|
|
|
_updateImperial: function (maxMeters) {
|
|
var maxFeet = maxMeters * 3.2808399,
|
|
maxMiles, miles, feet;
|
|
|
|
if (maxFeet > 5280) {
|
|
maxMiles = maxFeet / 5280;
|
|
miles = this._getRoundNum(maxMiles);
|
|
this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
|
|
|
|
} else {
|
|
feet = this._getRoundNum(maxFeet);
|
|
this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
|
|
}
|
|
},
|
|
|
|
_updateScale: function (scale, text, ratio) {
|
|
scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
|
|
scale.innerHTML = text;
|
|
},
|
|
|
|
_getRoundNum: function (num) {
|
|
var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
|
|
d = num / pow10;
|
|
|
|
d = d >= 10 ? 10 :
|
|
d >= 5 ? 5 :
|
|
d >= 3 ? 3 :
|
|
d >= 2 ? 2 : 1;
|
|
|
|
return pow10 * d;
|
|
}
|
|
});
|
|
|
|
|
|
// @factory L.control.scale(options?: Control.Scale options)
|
|
// Creates an scale control with the given options.
|
|
L.control.scale = function (options) {
|
|
return new L.Control.Scale(options);
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @class Control.Layers
|
|
* @aka L.Control.Layers
|
|
* @inherits Control
|
|
*
|
|
* The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control.html)). Extends `Control`.
|
|
*
|
|
* @example
|
|
*
|
|
* ```js
|
|
* var baseLayers = {
|
|
* "Mapbox": mapbox,
|
|
* "OpenStreetMap": osm
|
|
* };
|
|
*
|
|
* var overlays = {
|
|
* "Marker": marker,
|
|
* "Roads": roadsLayer
|
|
* };
|
|
*
|
|
* L.control.layers(baseLayers, overlays).addTo(map);
|
|
* ```
|
|
*
|
|
* The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
|
|
*
|
|
* ```js
|
|
* {
|
|
* "<someName1>": layer1,
|
|
* "<someName2>": layer2
|
|
* }
|
|
* ```
|
|
*
|
|
* The layer names can contain HTML, which allows you to add additional styling to the items:
|
|
*
|
|
* ```js
|
|
* {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
|
|
* ```
|
|
*/
|
|
|
|
|
|
L.Control.Layers = L.Control.extend({
|
|
// @section
|
|
// @aka Control.Layers options
|
|
options: {
|
|
// @option collapsed: Boolean = true
|
|
// If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
|
|
collapsed: true,
|
|
position: 'topright',
|
|
|
|
// @option autoZIndex: Boolean = true
|
|
// If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off.
|
|
autoZIndex: true,
|
|
|
|
// @option hideSingleBase: Boolean = false
|
|
// If `true`, the base layers in the control will be hidden when there is only one.
|
|
hideSingleBase: false,
|
|
|
|
// @option sortLayers: Boolean = false
|
|
// Whether to sort the layers. When `false`, layers will keep the order
|
|
// in which they were added to the control.
|
|
sortLayers: false,
|
|
|
|
// @option sortFunction: Function = *
|
|
// A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
|
|
// that will be used for sorting the layers, when `sortLayers` is `true`.
|
|
// The function receives both the `L.Layer` instances and their names, as in
|
|
// `sortFunction(layerA, layerB, nameA, nameB)`.
|
|
// By default, it sorts layers alphabetically by their name.
|
|
sortFunction: function (layerA, layerB, nameA, nameB) {
|
|
return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
|
|
}
|
|
},
|
|
|
|
initialize: function (baseLayers, overlays, options) {
|
|
L.setOptions(this, options);
|
|
|
|
this._layers = [];
|
|
this._lastZIndex = 0;
|
|
this._handlingClick = false;
|
|
|
|
for (var i in baseLayers) {
|
|
this._addLayer(baseLayers[i], i);
|
|
}
|
|
|
|
for (i in overlays) {
|
|
this._addLayer(overlays[i], i, true);
|
|
}
|
|
},
|
|
|
|
onAdd: function (map) {
|
|
this._initLayout();
|
|
this._update();
|
|
|
|
this._map = map;
|
|
map.on('zoomend', this._checkDisabledLayers, this);
|
|
|
|
return this._container;
|
|
},
|
|
|
|
onRemove: function () {
|
|
this._map.off('zoomend', this._checkDisabledLayers, this);
|
|
|
|
for (var i = 0; i < this._layers.length; i++) {
|
|
this._layers[i].layer.off('add remove', this._onLayerChange, this);
|
|
}
|
|
},
|
|
|
|
// @method addBaseLayer(layer: Layer, name: String): this
|
|
// Adds a base layer (radio button entry) with the given name to the control.
|
|
addBaseLayer: function (layer, name) {
|
|
this._addLayer(layer, name);
|
|
return (this._map) ? this._update() : this;
|
|
},
|
|
|
|
// @method addOverlay(layer: Layer, name: String): this
|
|
// Adds an overlay (checkbox entry) with the given name to the control.
|
|
addOverlay: function (layer, name) {
|
|
this._addLayer(layer, name, true);
|
|
return (this._map) ? this._update() : this;
|
|
},
|
|
|
|
// @method removeLayer(layer: Layer): this
|
|
// Remove the given layer from the control.
|
|
removeLayer: function (layer) {
|
|
layer.off('add remove', this._onLayerChange, this);
|
|
|
|
var obj = this._getLayer(L.stamp(layer));
|
|
if (obj) {
|
|
this._layers.splice(this._layers.indexOf(obj), 1);
|
|
}
|
|
return (this._map) ? this._update() : this;
|
|
},
|
|
|
|
// @method expand(): this
|
|
// Expand the control container if collapsed.
|
|
expand: function () {
|
|
L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
|
|
this._form.style.height = null;
|
|
var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
|
|
if (acceptableHeight < this._form.clientHeight) {
|
|
L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar');
|
|
this._form.style.height = acceptableHeight + 'px';
|
|
} else {
|
|
L.DomUtil.removeClass(this._form, 'leaflet-control-layers-scrollbar');
|
|
}
|
|
this._checkDisabledLayers();
|
|
return this;
|
|
},
|
|
|
|
// @method collapse(): this
|
|
// Collapse the control container if expanded.
|
|
collapse: function () {
|
|
L.DomUtil.removeClass(this._container, 'leaflet-control-layers-expanded');
|
|
return this;
|
|
},
|
|
|
|
_initLayout: function () {
|
|
var className = 'leaflet-control-layers',
|
|
container = this._container = L.DomUtil.create('div', className);
|
|
|
|
// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
|
|
container.setAttribute('aria-haspopup', true);
|
|
|
|
L.DomEvent.disableClickPropagation(container);
|
|
if (!L.Browser.touch) {
|
|
L.DomEvent.disableScrollPropagation(container);
|
|
}
|
|
|
|
var form = this._form = L.DomUtil.create('form', className + '-list');
|
|
|
|
if (!L.Browser.android) {
|
|
L.DomEvent.on(container, {
|
|
mouseenter: this.expand,
|
|
mouseleave: this.collapse
|
|
}, this);
|
|
}
|
|
|
|
var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
|
|
link.href = '#';
|
|
link.title = 'Layers';
|
|
|
|
if (L.Browser.touch) {
|
|
L.DomEvent
|
|
.on(link, 'click', L.DomEvent.stop)
|
|
.on(link, 'click', this.expand, this);
|
|
} else {
|
|
L.DomEvent.on(link, 'focus', this.expand, this);
|
|
}
|
|
|
|
// work around for Firefox Android issue https://github.com/Leaflet/Leaflet/issues/2033
|
|
L.DomEvent.on(form, 'click', function () {
|
|
setTimeout(L.bind(this._onInputClick, this), 0);
|
|
}, this);
|
|
|
|
this._map.on('click', this.collapse, this);
|
|
// TODO keyboard accessibility
|
|
|
|
if (!this.options.collapsed) {
|
|
this.expand();
|
|
}
|
|
|
|
this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
|
|
this._separator = L.DomUtil.create('div', className + '-separator', form);
|
|
this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
|
|
|
|
container.appendChild(form);
|
|
},
|
|
|
|
_getLayer: function (id) {
|
|
for (var i = 0; i < this._layers.length; i++) {
|
|
|
|
if (this._layers[i] && L.stamp(this._layers[i].layer) === id) {
|
|
return this._layers[i];
|
|
}
|
|
}
|
|
},
|
|
|
|
_addLayer: function (layer, name, overlay) {
|
|
layer.on('add remove', this._onLayerChange, this);
|
|
|
|
this._layers.push({
|
|
layer: layer,
|
|
name: name,
|
|
overlay: overlay
|
|
});
|
|
|
|
if (this.options.sortLayers) {
|
|
this._layers.sort(L.bind(function (a, b) {
|
|
return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
|
|
}, this));
|
|
}
|
|
|
|
if (this.options.autoZIndex && layer.setZIndex) {
|
|
this._lastZIndex++;
|
|
layer.setZIndex(this._lastZIndex);
|
|
}
|
|
},
|
|
|
|
_update: function () {
|
|
if (!this._container) { return this; }
|
|
|
|
L.DomUtil.empty(this._baseLayersList);
|
|
L.DomUtil.empty(this._overlaysList);
|
|
|
|
var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
|
|
|
|
for (i = 0; i < this._layers.length; i++) {
|
|
obj = this._layers[i];
|
|
this._addItem(obj);
|
|
overlaysPresent = overlaysPresent || obj.overlay;
|
|
baseLayersPresent = baseLayersPresent || !obj.overlay;
|
|
baseLayersCount += !obj.overlay ? 1 : 0;
|
|
}
|
|
|
|
// Hide base layers section if there's only one layer.
|
|
if (this.options.hideSingleBase) {
|
|
baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
|
|
this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
|
|
}
|
|
|
|
this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
|
|
|
|
return this;
|
|
},
|
|
|
|
_onLayerChange: function (e) {
|
|
if (!this._handlingClick) {
|
|
this._update();
|
|
}
|
|
|
|
var obj = this._getLayer(L.stamp(e.target));
|
|
|
|
// @namespace Map
|
|
// @section Layer events
|
|
// @event baselayerchange: LayersControlEvent
|
|
// Fired when the base layer is changed through the [layer control](#control-layers).
|
|
// @event overlayadd: LayersControlEvent
|
|
// Fired when an overlay is selected through the [layer control](#control-layers).
|
|
// @event overlayremove: LayersControlEvent
|
|
// Fired when an overlay is deselected through the [layer control](#control-layers).
|
|
// @namespace Control.Layers
|
|
var type = obj.overlay ?
|
|
(e.type === 'add' ? 'overlayadd' : 'overlayremove') :
|
|
(e.type === 'add' ? 'baselayerchange' : null);
|
|
|
|
if (type) {
|
|
this._map.fire(type, obj);
|
|
}
|
|
},
|
|
|
|
// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
|
|
_createRadioElement: function (name, checked) {
|
|
|
|
var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
|
|
name + '"' + (checked ? ' checked="checked"' : '') + '/>';
|
|
|
|
var radioFragment = document.createElement('div');
|
|
radioFragment.innerHTML = radioHtml;
|
|
|
|
return radioFragment.firstChild;
|
|
},
|
|
|
|
_addItem: function (obj) {
|
|
var label = document.createElement('label'),
|
|
checked = this._map.hasLayer(obj.layer),
|
|
input;
|
|
|
|
if (obj.overlay) {
|
|
input = document.createElement('input');
|
|
input.type = 'checkbox';
|
|
input.className = 'leaflet-control-layers-selector';
|
|
input.defaultChecked = checked;
|
|
} else {
|
|
input = this._createRadioElement('leaflet-base-layers', checked);
|
|
}
|
|
|
|
input.layerId = L.stamp(obj.layer);
|
|
|
|
L.DomEvent.on(input, 'click', this._onInputClick, this);
|
|
|
|
var name = document.createElement('span');
|
|
name.innerHTML = ' ' + obj.name;
|
|
|
|
// Helps from preventing layer control flicker when checkboxes are disabled
|
|
// https://github.com/Leaflet/Leaflet/issues/2771
|
|
var holder = document.createElement('div');
|
|
|
|
label.appendChild(holder);
|
|
holder.appendChild(input);
|
|
holder.appendChild(name);
|
|
|
|
var container = obj.overlay ? this._overlaysList : this._baseLayersList;
|
|
container.appendChild(label);
|
|
|
|
this._checkDisabledLayers();
|
|
return label;
|
|
},
|
|
|
|
_onInputClick: function () {
|
|
var inputs = this._form.getElementsByTagName('input'),
|
|
input, layer, hasLayer;
|
|
var addedLayers = [],
|
|
removedLayers = [];
|
|
|
|
this._handlingClick = true;
|
|
|
|
for (var i = inputs.length - 1; i >= 0; i--) {
|
|
input = inputs[i];
|
|
layer = this._getLayer(input.layerId).layer;
|
|
hasLayer = this._map.hasLayer(layer);
|
|
|
|
if (input.checked && !hasLayer) {
|
|
addedLayers.push(layer);
|
|
|
|
} else if (!input.checked && hasLayer) {
|
|
removedLayers.push(layer);
|
|
}
|
|
}
|
|
|
|
// Bugfix issue 2318: Should remove all old layers before readding new ones
|
|
for (i = 0; i < removedLayers.length; i++) {
|
|
this._map.removeLayer(removedLayers[i]);
|
|
}
|
|
for (i = 0; i < addedLayers.length; i++) {
|
|
this._map.addLayer(addedLayers[i]);
|
|
}
|
|
|
|
this._handlingClick = false;
|
|
|
|
this._refocusOnMap();
|
|
},
|
|
|
|
_checkDisabledLayers: function () {
|
|
var inputs = this._form.getElementsByTagName('input'),
|
|
input,
|
|
layer,
|
|
zoom = this._map.getZoom();
|
|
|
|
for (var i = inputs.length - 1; i >= 0; i--) {
|
|
input = inputs[i];
|
|
layer = this._getLayer(input.layerId).layer;
|
|
input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
|
|
(layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
|
|
|
|
}
|
|
},
|
|
|
|
_expand: function () {
|
|
// Backward compatibility, remove me in 1.1.
|
|
return this.expand();
|
|
},
|
|
|
|
_collapse: function () {
|
|
// Backward compatibility, remove me in 1.1.
|
|
return this.collapse();
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
|
|
// Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
|
|
L.control.layers = function (baseLayers, overlays, options) {
|
|
return new L.Control.Layers(baseLayers, overlays, options);
|
|
};
|
|
|
|
|
|
|
|
}(window, document));
|
|
|
|
},{}],4:[function(require,module,exports){
|
|
/*!
|
|
* mustache.js - Logic-less {{mustache}} templates with JavaScript
|
|
* http://github.com/janl/mustache.js
|
|
*/
|
|
|
|
/*global define: false Mustache: true*/
|
|
|
|
(function defineMustache (global, factory) {
|
|
if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') {
|
|
factory(exports); // CommonJS
|
|
} else if (typeof define === 'function' && define.amd) {
|
|
define(['exports'], factory); // AMD
|
|
} else {
|
|
global.Mustache = {};
|
|
factory(global.Mustache); // script, wsh, asp
|
|
}
|
|
}(this, function mustacheFactory (mustache) {
|
|
|
|
var objectToString = Object.prototype.toString;
|
|
var isArray = Array.isArray || function isArrayPolyfill (object) {
|
|
return objectToString.call(object) === '[object Array]';
|
|
};
|
|
|
|
function isFunction (object) {
|
|
return typeof object === 'function';
|
|
}
|
|
|
|
/**
|
|
* More correct typeof string handling array
|
|
* which normally returns typeof 'object'
|
|
*/
|
|
function typeStr (obj) {
|
|
return isArray(obj) ? 'array' : typeof obj;
|
|
}
|
|
|
|
function escapeRegExp (string) {
|
|
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
|
|
}
|
|
|
|
/**
|
|
* Null safe way of checking whether or not an object,
|
|
* including its prototype, has a given property
|
|
*/
|
|
function hasProperty (obj, propName) {
|
|
return obj != null && typeof obj === 'object' && (propName in obj);
|
|
}
|
|
|
|
// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
|
|
// See https://github.com/janl/mustache.js/issues/189
|
|
var regExpTest = RegExp.prototype.test;
|
|
function testRegExp (re, string) {
|
|
return regExpTest.call(re, string);
|
|
}
|
|
|
|
var nonSpaceRe = /\S/;
|
|
function isWhitespace (string) {
|
|
return !testRegExp(nonSpaceRe, string);
|
|
}
|
|
|
|
var entityMap = {
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": ''',
|
|
'/': '/',
|
|
'`': '`',
|
|
'=': '='
|
|
};
|
|
|
|
function escapeHtml (string) {
|
|
return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {
|
|
return entityMap[s];
|
|
});
|
|
}
|
|
|
|
var whiteRe = /\s*/;
|
|
var spaceRe = /\s+/;
|
|
var equalsRe = /\s*=/;
|
|
var curlyRe = /\s*\}/;
|
|
var tagRe = /#|\^|\/|>|\{|&|=|!/;
|
|
|
|
/**
|
|
* Breaks up the given `template` string into a tree of tokens. If the `tags`
|
|
* argument is given here it must be an array with two string values: the
|
|
* opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
|
|
* course, the default is to use mustaches (i.e. mustache.tags).
|
|
*
|
|
* A token is an array with at least 4 elements. The first element is the
|
|
* mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
|
|
* did not contain a symbol (i.e. {{myValue}}) this element is "name". For
|
|
* all text that appears outside a symbol this element is "text".
|
|
*
|
|
* The second element of a token is its "value". For mustache tags this is
|
|
* whatever else was inside the tag besides the opening symbol. For text tokens
|
|
* this is the text itself.
|
|
*
|
|
* The third and fourth elements of the token are the start and end indices,
|
|
* respectively, of the token in the original template.
|
|
*
|
|
* Tokens that are the root node of a subtree contain two more elements: 1) an
|
|
* array of tokens in the subtree and 2) the index in the original template at
|
|
* which the closing tag for that section begins.
|
|
*/
|
|
function parseTemplate (template, tags) {
|
|
if (!template)
|
|
return [];
|
|
|
|
var sections = []; // Stack to hold section tokens
|
|
var tokens = []; // Buffer to hold the tokens
|
|
var spaces = []; // Indices of whitespace tokens on the current line
|
|
var hasTag = false; // Is there a {{tag}} on the current line?
|
|
var nonSpace = false; // Is there a non-space char on the current line?
|
|
|
|
// Strips all whitespace tokens array for the current line
|
|
// if there was a {{#tag}} on it and otherwise only space.
|
|
function stripSpace () {
|
|
if (hasTag && !nonSpace) {
|
|
while (spaces.length)
|
|
delete tokens[spaces.pop()];
|
|
} else {
|
|
spaces = [];
|
|
}
|
|
|
|
hasTag = false;
|
|
nonSpace = false;
|
|
}
|
|
|
|
var openingTagRe, closingTagRe, closingCurlyRe;
|
|
function compileTags (tagsToCompile) {
|
|
if (typeof tagsToCompile === 'string')
|
|
tagsToCompile = tagsToCompile.split(spaceRe, 2);
|
|
|
|
if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)
|
|
throw new Error('Invalid tags: ' + tagsToCompile);
|
|
|
|
openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
|
|
closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
|
|
closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
|
|
}
|
|
|
|
compileTags(tags || mustache.tags);
|
|
|
|
var scanner = new Scanner(template);
|
|
|
|
var start, type, value, chr, token, openSection;
|
|
while (!scanner.eos()) {
|
|
start = scanner.pos;
|
|
|
|
// Match any text between tags.
|
|
value = scanner.scanUntil(openingTagRe);
|
|
|
|
if (value) {
|
|
for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
|
|
chr = value.charAt(i);
|
|
|
|
if (isWhitespace(chr)) {
|
|
spaces.push(tokens.length);
|
|
} else {
|
|
nonSpace = true;
|
|
}
|
|
|
|
tokens.push([ 'text', chr, start, start + 1 ]);
|
|
start += 1;
|
|
|
|
// Check for whitespace on the current line.
|
|
if (chr === '\n')
|
|
stripSpace();
|
|
}
|
|
}
|
|
|
|
// Match the opening tag.
|
|
if (!scanner.scan(openingTagRe))
|
|
break;
|
|
|
|
hasTag = true;
|
|
|
|
// Get the tag type.
|
|
type = scanner.scan(tagRe) || 'name';
|
|
scanner.scan(whiteRe);
|
|
|
|
// Get the tag value.
|
|
if (type === '=') {
|
|
value = scanner.scanUntil(equalsRe);
|
|
scanner.scan(equalsRe);
|
|
scanner.scanUntil(closingTagRe);
|
|
} else if (type === '{') {
|
|
value = scanner.scanUntil(closingCurlyRe);
|
|
scanner.scan(curlyRe);
|
|
scanner.scanUntil(closingTagRe);
|
|
type = '&';
|
|
} else {
|
|
value = scanner.scanUntil(closingTagRe);
|
|
}
|
|
|
|
// Match the closing tag.
|
|
if (!scanner.scan(closingTagRe))
|
|
throw new Error('Unclosed tag at ' + scanner.pos);
|
|
|
|
token = [ type, value, start, scanner.pos ];
|
|
tokens.push(token);
|
|
|
|
if (type === '#' || type === '^') {
|
|
sections.push(token);
|
|
} else if (type === '/') {
|
|
// Check section nesting.
|
|
openSection = sections.pop();
|
|
|
|
if (!openSection)
|
|
throw new Error('Unopened section "' + value + '" at ' + start);
|
|
|
|
if (openSection[1] !== value)
|
|
throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
|
|
} else if (type === 'name' || type === '{' || type === '&') {
|
|
nonSpace = true;
|
|
} else if (type === '=') {
|
|
// Set the tags for the next time around.
|
|
compileTags(value);
|
|
}
|
|
}
|
|
|
|
// Make sure there are no open sections when we're done.
|
|
openSection = sections.pop();
|
|
|
|
if (openSection)
|
|
throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
|
|
|
|
return nestTokens(squashTokens(tokens));
|
|
}
|
|
|
|
/**
|
|
* Combines the values of consecutive text tokens in the given `tokens` array
|
|
* to a single token.
|
|
*/
|
|
function squashTokens (tokens) {
|
|
var squashedTokens = [];
|
|
|
|
var token, lastToken;
|
|
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
|
token = tokens[i];
|
|
|
|
if (token) {
|
|
if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
|
|
lastToken[1] += token[1];
|
|
lastToken[3] = token[3];
|
|
} else {
|
|
squashedTokens.push(token);
|
|
lastToken = token;
|
|
}
|
|
}
|
|
}
|
|
|
|
return squashedTokens;
|
|
}
|
|
|
|
/**
|
|
* Forms the given array of `tokens` into a nested tree structure where
|
|
* tokens that represent a section have two additional items: 1) an array of
|
|
* all tokens that appear in that section and 2) the index in the original
|
|
* template that represents the end of that section.
|
|
*/
|
|
function nestTokens (tokens) {
|
|
var nestedTokens = [];
|
|
var collector = nestedTokens;
|
|
var sections = [];
|
|
|
|
var token, section;
|
|
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
|
token = tokens[i];
|
|
|
|
switch (token[0]) {
|
|
case '#':
|
|
case '^':
|
|
collector.push(token);
|
|
sections.push(token);
|
|
collector = token[4] = [];
|
|
break;
|
|
case '/':
|
|
section = sections.pop();
|
|
section[5] = token[2];
|
|
collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
|
|
break;
|
|
default:
|
|
collector.push(token);
|
|
}
|
|
}
|
|
|
|
return nestedTokens;
|
|
}
|
|
|
|
/**
|
|
* A simple string scanner that is used by the template parser to find
|
|
* tokens in template strings.
|
|
*/
|
|
function Scanner (string) {
|
|
this.string = string;
|
|
this.tail = string;
|
|
this.pos = 0;
|
|
}
|
|
|
|
/**
|
|
* Returns `true` if the tail is empty (end of string).
|
|
*/
|
|
Scanner.prototype.eos = function eos () {
|
|
return this.tail === '';
|
|
};
|
|
|
|
/**
|
|
* Tries to match the given regular expression at the current position.
|
|
* Returns the matched text if it can match, the empty string otherwise.
|
|
*/
|
|
Scanner.prototype.scan = function scan (re) {
|
|
var match = this.tail.match(re);
|
|
|
|
if (!match || match.index !== 0)
|
|
return '';
|
|
|
|
var string = match[0];
|
|
|
|
this.tail = this.tail.substring(string.length);
|
|
this.pos += string.length;
|
|
|
|
return string;
|
|
};
|
|
|
|
/**
|
|
* Skips all text until the given regular expression can be matched. Returns
|
|
* the skipped string, which is the entire tail if no match can be made.
|
|
*/
|
|
Scanner.prototype.scanUntil = function scanUntil (re) {
|
|
var index = this.tail.search(re), match;
|
|
|
|
switch (index) {
|
|
case -1:
|
|
match = this.tail;
|
|
this.tail = '';
|
|
break;
|
|
case 0:
|
|
match = '';
|
|
break;
|
|
default:
|
|
match = this.tail.substring(0, index);
|
|
this.tail = this.tail.substring(index);
|
|
}
|
|
|
|
this.pos += match.length;
|
|
|
|
return match;
|
|
};
|
|
|
|
/**
|
|
* Represents a rendering context by wrapping a view object and
|
|
* maintaining a reference to the parent context.
|
|
*/
|
|
function Context (view, parentContext) {
|
|
this.view = view;
|
|
this.cache = { '.': this.view };
|
|
this.parent = parentContext;
|
|
}
|
|
|
|
/**
|
|
* Creates a new context using the given view with this context
|
|
* as the parent.
|
|
*/
|
|
Context.prototype.push = function push (view) {
|
|
return new Context(view, this);
|
|
};
|
|
|
|
/**
|
|
* Returns the value of the given name in this context, traversing
|
|
* up the context hierarchy if the value is absent in this context's view.
|
|
*/
|
|
Context.prototype.lookup = function lookup (name) {
|
|
var cache = this.cache;
|
|
|
|
var value;
|
|
if (cache.hasOwnProperty(name)) {
|
|
value = cache[name];
|
|
} else {
|
|
var context = this, names, index, lookupHit = false;
|
|
|
|
while (context) {
|
|
if (name.indexOf('.') > 0) {
|
|
value = context.view;
|
|
names = name.split('.');
|
|
index = 0;
|
|
|
|
/**
|
|
* Using the dot notion path in `name`, we descend through the
|
|
* nested objects.
|
|
*
|
|
* To be certain that the lookup has been successful, we have to
|
|
* check if the last object in the path actually has the property
|
|
* we are looking for. We store the result in `lookupHit`.
|
|
*
|
|
* This is specially necessary for when the value has been set to
|
|
* `undefined` and we want to avoid looking up parent contexts.
|
|
**/
|
|
while (value != null && index < names.length) {
|
|
if (index === names.length - 1)
|
|
lookupHit = hasProperty(value, names[index]);
|
|
|
|
value = value[names[index++]];
|
|
}
|
|
} else {
|
|
value = context.view[name];
|
|
lookupHit = hasProperty(context.view, name);
|
|
}
|
|
|
|
if (lookupHit)
|
|
break;
|
|
|
|
context = context.parent;
|
|
}
|
|
|
|
cache[name] = value;
|
|
}
|
|
|
|
if (isFunction(value))
|
|
value = value.call(this.view);
|
|
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* A Writer knows how to take a stream of tokens and render them to a
|
|
* string, given a context. It also maintains a cache of templates to
|
|
* avoid the need to parse the same template twice.
|
|
*/
|
|
function Writer () {
|
|
this.cache = {};
|
|
}
|
|
|
|
/**
|
|
* Clears all cached templates in this writer.
|
|
*/
|
|
Writer.prototype.clearCache = function clearCache () {
|
|
this.cache = {};
|
|
};
|
|
|
|
/**
|
|
* Parses and caches the given `template` and returns the array of tokens
|
|
* that is generated from the parse.
|
|
*/
|
|
Writer.prototype.parse = function parse (template, tags) {
|
|
var cache = this.cache;
|
|
var tokens = cache[template];
|
|
|
|
if (tokens == null)
|
|
tokens = cache[template] = parseTemplate(template, tags);
|
|
|
|
return tokens;
|
|
};
|
|
|
|
/**
|
|
* High-level method that is used to render the given `template` with
|
|
* the given `view`.
|
|
*
|
|
* The optional `partials` argument may be an object that contains the
|
|
* names and templates of partials that are used in the template. It may
|
|
* also be a function that is used to load partial templates on the fly
|
|
* that takes a single argument: the name of the partial.
|
|
*/
|
|
Writer.prototype.render = function render (template, view, partials) {
|
|
var tokens = this.parse(template);
|
|
var context = (view instanceof Context) ? view : new Context(view);
|
|
return this.renderTokens(tokens, context, partials, template);
|
|
};
|
|
|
|
/**
|
|
* Low-level method that renders the given array of `tokens` using
|
|
* the given `context` and `partials`.
|
|
*
|
|
* Note: The `originalTemplate` is only ever used to extract the portion
|
|
* of the original template that was contained in a higher-order section.
|
|
* If the template doesn't use higher-order sections, this argument may
|
|
* be omitted.
|
|
*/
|
|
Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate) {
|
|
var buffer = '';
|
|
|
|
var token, symbol, value;
|
|
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
|
value = undefined;
|
|
token = tokens[i];
|
|
symbol = token[0];
|
|
|
|
if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);
|
|
else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);
|
|
else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate);
|
|
else if (symbol === '&') value = this.unescapedValue(token, context);
|
|
else if (symbol === 'name') value = this.escapedValue(token, context);
|
|
else if (symbol === 'text') value = this.rawValue(token);
|
|
|
|
if (value !== undefined)
|
|
buffer += value;
|
|
}
|
|
|
|
return buffer;
|
|
};
|
|
|
|
Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) {
|
|
var self = this;
|
|
var buffer = '';
|
|
var value = context.lookup(token[1]);
|
|
|
|
// This function is used to render an arbitrary template
|
|
// in the current context by higher-order sections.
|
|
function subRender (template) {
|
|
return self.render(template, context, partials);
|
|
}
|
|
|
|
if (!value) return;
|
|
|
|
if (isArray(value)) {
|
|
for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
|
|
buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);
|
|
}
|
|
} else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
|
|
buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);
|
|
} else if (isFunction(value)) {
|
|
if (typeof originalTemplate !== 'string')
|
|
throw new Error('Cannot use higher-order sections without the original template');
|
|
|
|
// Extract the portion of the original template that the section contains.
|
|
value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
|
|
|
|
if (value != null)
|
|
buffer += value;
|
|
} else {
|
|
buffer += this.renderTokens(token[4], context, partials, originalTemplate);
|
|
}
|
|
return buffer;
|
|
};
|
|
|
|
Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) {
|
|
var value = context.lookup(token[1]);
|
|
|
|
// Use JavaScript's definition of falsy. Include empty arrays.
|
|
// See https://github.com/janl/mustache.js/issues/186
|
|
if (!value || (isArray(value) && value.length === 0))
|
|
return this.renderTokens(token[4], context, partials, originalTemplate);
|
|
};
|
|
|
|
Writer.prototype.renderPartial = function renderPartial (token, context, partials) {
|
|
if (!partials) return;
|
|
|
|
var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
|
|
if (value != null)
|
|
return this.renderTokens(this.parse(value), context, partials, value);
|
|
};
|
|
|
|
Writer.prototype.unescapedValue = function unescapedValue (token, context) {
|
|
var value = context.lookup(token[1]);
|
|
if (value != null)
|
|
return value;
|
|
};
|
|
|
|
Writer.prototype.escapedValue = function escapedValue (token, context) {
|
|
var value = context.lookup(token[1]);
|
|
if (value != null)
|
|
return mustache.escape(value);
|
|
};
|
|
|
|
Writer.prototype.rawValue = function rawValue (token) {
|
|
return token[1];
|
|
};
|
|
|
|
mustache.name = 'mustache.js';
|
|
mustache.version = '2.2.1';
|
|
mustache.tags = [ '{{', '}}' ];
|
|
|
|
// All high-level mustache.* functions use this writer.
|
|
var defaultWriter = new Writer();
|
|
|
|
/**
|
|
* Clears all cached templates in the default writer.
|
|
*/
|
|
mustache.clearCache = function clearCache () {
|
|
return defaultWriter.clearCache();
|
|
};
|
|
|
|
/**
|
|
* Parses and caches the given template in the default writer and returns the
|
|
* array of tokens it contains. Doing this ahead of time avoids the need to
|
|
* parse templates on the fly as they are rendered.
|
|
*/
|
|
mustache.parse = function parse (template, tags) {
|
|
return defaultWriter.parse(template, tags);
|
|
};
|
|
|
|
/**
|
|
* Renders the `template` with the given `view` and `partials` using the
|
|
* default writer.
|
|
*/
|
|
mustache.render = function render (template, view, partials) {
|
|
if (typeof template !== 'string') {
|
|
throw new TypeError('Invalid template! Template should be a "string" ' +
|
|
'but "' + typeStr(template) + '" was given as the first ' +
|
|
'argument for mustache#render(template, view, partials)');
|
|
}
|
|
|
|
return defaultWriter.render(template, view, partials);
|
|
};
|
|
|
|
// This is here for backwards compatibility with 0.4.x.,
|
|
/*eslint-disable */ // eslint wants camel cased function name
|
|
mustache.to_html = function to_html (template, view, partials, send) {
|
|
/*eslint-enable*/
|
|
|
|
var result = mustache.render(template, view, partials);
|
|
|
|
if (isFunction(send)) {
|
|
send(result);
|
|
} else {
|
|
return result;
|
|
}
|
|
};
|
|
|
|
// Export the escaping function so that the user may override it.
|
|
// See https://github.com/janl/mustache.js/issues/244
|
|
mustache.escape = escapeHtml;
|
|
|
|
// Export these mainly for testing, but also for advanced usage.
|
|
mustache.Scanner = Scanner;
|
|
mustache.Context = Context;
|
|
mustache.Writer = Writer;
|
|
|
|
}));
|
|
|
|
},{}],5:[function(require,module,exports){
|
|
var html_sanitize = require('./sanitizer-bundle.js');
|
|
|
|
module.exports = function(_) {
|
|
if (!_) return '';
|
|
return html_sanitize(_, cleanUrl, cleanId);
|
|
};
|
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=255107
|
|
function cleanUrl(url) {
|
|
'use strict';
|
|
if (/^https?/.test(url.getScheme())) return url.toString();
|
|
if (/^mailto?/.test(url.getScheme())) return url.toString();
|
|
if ('data' == url.getScheme() && /^image/.test(url.getPath())) {
|
|
return url.toString();
|
|
}
|
|
}
|
|
|
|
function cleanId(id) { return id; }
|
|
|
|
},{"./sanitizer-bundle.js":6}],6:[function(require,module,exports){
|
|
|
|
// Copyright (C) 2010 Google Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview
|
|
* Implements RFC 3986 for parsing/formatting URIs.
|
|
*
|
|
* @author mikesamuel@gmail.com
|
|
* \@provides URI
|
|
* \@overrides window
|
|
*/
|
|
|
|
var URI = (function () {
|
|
|
|
/**
|
|
* creates a uri from the string form. The parser is relaxed, so special
|
|
* characters that aren't escaped but don't cause ambiguities will not cause
|
|
* parse failures.
|
|
*
|
|
* @return {URI|null}
|
|
*/
|
|
function parse(uriStr) {
|
|
var m = ('' + uriStr).match(URI_RE_);
|
|
if (!m) { return null; }
|
|
return new URI(
|
|
nullIfAbsent(m[1]),
|
|
nullIfAbsent(m[2]),
|
|
nullIfAbsent(m[3]),
|
|
nullIfAbsent(m[4]),
|
|
nullIfAbsent(m[5]),
|
|
nullIfAbsent(m[6]),
|
|
nullIfAbsent(m[7]));
|
|
}
|
|
|
|
|
|
/**
|
|
* creates a uri from the given parts.
|
|
*
|
|
* @param scheme {string} an unencoded scheme such as "http" or null
|
|
* @param credentials {string} unencoded user credentials or null
|
|
* @param domain {string} an unencoded domain name or null
|
|
* @param port {number} a port number in [1, 32768].
|
|
* -1 indicates no port, as does null.
|
|
* @param path {string} an unencoded path
|
|
* @param query {Array.<string>|string|null} a list of unencoded cgi
|
|
* parameters where even values are keys and odds the corresponding values
|
|
* or an unencoded query.
|
|
* @param fragment {string} an unencoded fragment without the "#" or null.
|
|
* @return {URI}
|
|
*/
|
|
function create(scheme, credentials, domain, port, path, query, fragment) {
|
|
var uri = new URI(
|
|
encodeIfExists2(scheme, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_),
|
|
encodeIfExists2(
|
|
credentials, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_),
|
|
encodeIfExists(domain),
|
|
port > 0 ? port.toString() : null,
|
|
encodeIfExists2(path, URI_DISALLOWED_IN_PATH_),
|
|
null,
|
|
encodeIfExists(fragment));
|
|
if (query) {
|
|
if ('string' === typeof query) {
|
|
uri.setRawQuery(query.replace(/[^?&=0-9A-Za-z_\-~.%]/g, encodeOne));
|
|
} else {
|
|
uri.setAllParameters(query);
|
|
}
|
|
}
|
|
return uri;
|
|
}
|
|
function encodeIfExists(unescapedPart) {
|
|
if ('string' == typeof unescapedPart) {
|
|
return encodeURIComponent(unescapedPart);
|
|
}
|
|
return null;
|
|
};
|
|
/**
|
|
* if unescapedPart is non null, then escapes any characters in it that aren't
|
|
* valid characters in a url and also escapes any special characters that
|
|
* appear in extra.
|
|
*
|
|
* @param unescapedPart {string}
|
|
* @param extra {RegExp} a character set of characters in [\01-\177].
|
|
* @return {string|null} null iff unescapedPart == null.
|
|
*/
|
|
function encodeIfExists2(unescapedPart, extra) {
|
|
if ('string' == typeof unescapedPart) {
|
|
return encodeURI(unescapedPart).replace(extra, encodeOne);
|
|
}
|
|
return null;
|
|
};
|
|
/** converts a character in [\01-\177] to its url encoded equivalent. */
|
|
function encodeOne(ch) {
|
|
var n = ch.charCodeAt(0);
|
|
return '%' + '0123456789ABCDEF'.charAt((n >> 4) & 0xf) +
|
|
'0123456789ABCDEF'.charAt(n & 0xf);
|
|
}
|
|
|
|
/**
|
|
* {@updoc
|
|
* $ normPath('foo/./bar')
|
|
* # 'foo/bar'
|
|
* $ normPath('./foo')
|
|
* # 'foo'
|
|
* $ normPath('foo/.')
|
|
* # 'foo'
|
|
* $ normPath('foo//bar')
|
|
* # 'foo/bar'
|
|
* }
|
|
*/
|
|
function normPath(path) {
|
|
return path.replace(/(^|\/)\.(?:\/|$)/g, '$1').replace(/\/{2,}/g, '/');
|
|
}
|
|
|
|
var PARENT_DIRECTORY_HANDLER = new RegExp(
|
|
''
|
|
// A path break
|
|
+ '(/|^)'
|
|
// followed by a non .. path element
|
|
// (cannot be . because normPath is used prior to this RegExp)
|
|
+ '(?:[^./][^/]*|\\.{2,}(?:[^./][^/]*)|\\.{3,}[^/]*)'
|
|
// followed by .. followed by a path break.
|
|
+ '/\\.\\.(?:/|$)');
|
|
|
|
var PARENT_DIRECTORY_HANDLER_RE = new RegExp(PARENT_DIRECTORY_HANDLER);
|
|
|
|
var EXTRA_PARENT_PATHS_RE = /^(?:\.\.\/)*(?:\.\.$)?/;
|
|
|
|
/**
|
|
* Normalizes its input path and collapses all . and .. sequences except for
|
|
* .. sequences that would take it above the root of the current parent
|
|
* directory.
|
|
* {@updoc
|
|
* $ collapse_dots('foo/../bar')
|
|
* # 'bar'
|
|
* $ collapse_dots('foo/./bar')
|
|
* # 'foo/bar'
|
|
* $ collapse_dots('foo/../bar/./../../baz')
|
|
* # 'baz'
|
|
* $ collapse_dots('../foo')
|
|
* # '../foo'
|
|
* $ collapse_dots('../foo').replace(EXTRA_PARENT_PATHS_RE, '')
|
|
* # 'foo'
|
|
* }
|
|
*/
|
|
function collapse_dots(path) {
|
|
if (path === null) { return null; }
|
|
var p = normPath(path);
|
|
// Only /../ left to flatten
|
|
var r = PARENT_DIRECTORY_HANDLER_RE;
|
|
// We replace with $1 which matches a / before the .. because this
|
|
// guarantees that:
|
|
// (1) we have at most 1 / between the adjacent place,
|
|
// (2) always have a slash if there is a preceding path section, and
|
|
// (3) we never turn a relative path into an absolute path.
|
|
for (var q; (q = p.replace(r, '$1')) != p; p = q) {};
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* resolves a relative url string to a base uri.
|
|
* @return {URI}
|
|
*/
|
|
function resolve(baseUri, relativeUri) {
|
|
// there are several kinds of relative urls:
|
|
// 1. //foo - replaces everything from the domain on. foo is a domain name
|
|
// 2. foo - replaces the last part of the path, the whole query and fragment
|
|
// 3. /foo - replaces the the path, the query and fragment
|
|
// 4. ?foo - replace the query and fragment
|
|
// 5. #foo - replace the fragment only
|
|
|
|
var absoluteUri = baseUri.clone();
|
|
// we satisfy these conditions by looking for the first part of relativeUri
|
|
// that is not blank and applying defaults to the rest
|
|
|
|
var overridden = relativeUri.hasScheme();
|
|
|
|
if (overridden) {
|
|
absoluteUri.setRawScheme(relativeUri.getRawScheme());
|
|
} else {
|
|
overridden = relativeUri.hasCredentials();
|
|
}
|
|
|
|
if (overridden) {
|
|
absoluteUri.setRawCredentials(relativeUri.getRawCredentials());
|
|
} else {
|
|
overridden = relativeUri.hasDomain();
|
|
}
|
|
|
|
if (overridden) {
|
|
absoluteUri.setRawDomain(relativeUri.getRawDomain());
|
|
} else {
|
|
overridden = relativeUri.hasPort();
|
|
}
|
|
|
|
var rawPath = relativeUri.getRawPath();
|
|
var simplifiedPath = collapse_dots(rawPath);
|
|
if (overridden) {
|
|
absoluteUri.setPort(relativeUri.getPort());
|
|
simplifiedPath = simplifiedPath
|
|
&& simplifiedPath.replace(EXTRA_PARENT_PATHS_RE, '');
|
|
} else {
|
|
overridden = !!rawPath;
|
|
if (overridden) {
|
|
// resolve path properly
|
|
if (simplifiedPath.charCodeAt(0) !== 0x2f /* / */) { // path is relative
|
|
var absRawPath = collapse_dots(absoluteUri.getRawPath() || '')
|
|
.replace(EXTRA_PARENT_PATHS_RE, '');
|
|
var slash = absRawPath.lastIndexOf('/') + 1;
|
|
simplifiedPath = collapse_dots(
|
|
(slash ? absRawPath.substring(0, slash) : '')
|
|
+ collapse_dots(rawPath))
|
|
.replace(EXTRA_PARENT_PATHS_RE, '');
|
|
}
|
|
} else {
|
|
simplifiedPath = simplifiedPath
|
|
&& simplifiedPath.replace(EXTRA_PARENT_PATHS_RE, '');
|
|
if (simplifiedPath !== rawPath) {
|
|
absoluteUri.setRawPath(simplifiedPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (overridden) {
|
|
absoluteUri.setRawPath(simplifiedPath);
|
|
} else {
|
|
overridden = relativeUri.hasQuery();
|
|
}
|
|
|
|
if (overridden) {
|
|
absoluteUri.setRawQuery(relativeUri.getRawQuery());
|
|
} else {
|
|
overridden = relativeUri.hasFragment();
|
|
}
|
|
|
|
if (overridden) {
|
|
absoluteUri.setRawFragment(relativeUri.getRawFragment());
|
|
}
|
|
|
|
return absoluteUri;
|
|
}
|
|
|
|
/**
|
|
* a mutable URI.
|
|
*
|
|
* This class contains setters and getters for the parts of the URI.
|
|
* The <tt>getXYZ</tt>/<tt>setXYZ</tt> methods return the decoded part -- so
|
|
* <code>uri.parse('/foo%20bar').getPath()</code> will return the decoded path,
|
|
* <tt>/foo bar</tt>.
|
|
*
|
|
* <p>The raw versions of fields are available too.
|
|
* <code>uri.parse('/foo%20bar').getRawPath()</code> will return the raw path,
|
|
* <tt>/foo%20bar</tt>. Use the raw setters with care, since
|
|
* <code>URI::toString</code> is not guaranteed to return a valid url if a
|
|
* raw setter was used.
|
|
*
|
|
* <p>All setters return <tt>this</tt> and so may be chained, a la
|
|
* <code>uri.parse('/foo').setFragment('part').toString()</code>.
|
|
*
|
|
* <p>You should not use this constructor directly -- please prefer the factory
|
|
* functions {@link uri.parse}, {@link uri.create}, {@link uri.resolve}
|
|
* instead.</p>
|
|
*
|
|
* <p>The parameters are all raw (assumed to be properly escaped) parts, and
|
|
* any (but not all) may be null. Undefined is not allowed.</p>
|
|
*
|
|
* @constructor
|
|
*/
|
|
function URI(
|
|
rawScheme,
|
|
rawCredentials, rawDomain, port,
|
|
rawPath, rawQuery, rawFragment) {
|
|
this.scheme_ = rawScheme;
|
|
this.credentials_ = rawCredentials;
|
|
this.domain_ = rawDomain;
|
|
this.port_ = port;
|
|
this.path_ = rawPath;
|
|
this.query_ = rawQuery;
|
|
this.fragment_ = rawFragment;
|
|
/**
|
|
* @type {Array|null}
|
|
*/
|
|
this.paramCache_ = null;
|
|
}
|
|
|
|
/** returns the string form of the url. */
|
|
URI.prototype.toString = function () {
|
|
var out = [];
|
|
if (null !== this.scheme_) { out.push(this.scheme_, ':'); }
|
|
if (null !== this.domain_) {
|
|
out.push('//');
|
|
if (null !== this.credentials_) { out.push(this.credentials_, '@'); }
|
|
out.push(this.domain_);
|
|
if (null !== this.port_) { out.push(':', this.port_.toString()); }
|
|
}
|
|
if (null !== this.path_) { out.push(this.path_); }
|
|
if (null !== this.query_) { out.push('?', this.query_); }
|
|
if (null !== this.fragment_) { out.push('#', this.fragment_); }
|
|
return out.join('');
|
|
};
|
|
|
|
URI.prototype.clone = function () {
|
|
return new URI(this.scheme_, this.credentials_, this.domain_, this.port_,
|
|
this.path_, this.query_, this.fragment_);
|
|
};
|
|
|
|
URI.prototype.getScheme = function () {
|
|
// HTML5 spec does not require the scheme to be lowercased but
|
|
// all common browsers except Safari lowercase the scheme.
|
|
return this.scheme_ && decodeURIComponent(this.scheme_).toLowerCase();
|
|
};
|
|
URI.prototype.getRawScheme = function () {
|
|
return this.scheme_;
|
|
};
|
|
URI.prototype.setScheme = function (newScheme) {
|
|
this.scheme_ = encodeIfExists2(
|
|
newScheme, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_);
|
|
return this;
|
|
};
|
|
URI.prototype.setRawScheme = function (newScheme) {
|
|
this.scheme_ = newScheme ? newScheme : null;
|
|
return this;
|
|
};
|
|
URI.prototype.hasScheme = function () {
|
|
return null !== this.scheme_;
|
|
};
|
|
|
|
|
|
URI.prototype.getCredentials = function () {
|
|
return this.credentials_ && decodeURIComponent(this.credentials_);
|
|
};
|
|
URI.prototype.getRawCredentials = function () {
|
|
return this.credentials_;
|
|
};
|
|
URI.prototype.setCredentials = function (newCredentials) {
|
|
this.credentials_ = encodeIfExists2(
|
|
newCredentials, URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_);
|
|
|
|
return this;
|
|
};
|
|
URI.prototype.setRawCredentials = function (newCredentials) {
|
|
this.credentials_ = newCredentials ? newCredentials : null;
|
|
return this;
|
|
};
|
|
URI.prototype.hasCredentials = function () {
|
|
return null !== this.credentials_;
|
|
};
|
|
|
|
|
|
URI.prototype.getDomain = function () {
|
|
return this.domain_ && decodeURIComponent(this.domain_);
|
|
};
|
|
URI.prototype.getRawDomain = function () {
|
|
return this.domain_;
|
|
};
|
|
URI.prototype.setDomain = function (newDomain) {
|
|
return this.setRawDomain(newDomain && encodeURIComponent(newDomain));
|
|
};
|
|
URI.prototype.setRawDomain = function (newDomain) {
|
|
this.domain_ = newDomain ? newDomain : null;
|
|
// Maintain the invariant that paths must start with a slash when the URI
|
|
// is not path-relative.
|
|
return this.setRawPath(this.path_);
|
|
};
|
|
URI.prototype.hasDomain = function () {
|
|
return null !== this.domain_;
|
|
};
|
|
|
|
|
|
URI.prototype.getPort = function () {
|
|
return this.port_ && decodeURIComponent(this.port_);
|
|
};
|
|
URI.prototype.setPort = function (newPort) {
|
|
if (newPort) {
|
|
newPort = Number(newPort);
|
|
if (newPort !== (newPort & 0xffff)) {
|
|
throw new Error('Bad port number ' + newPort);
|
|
}
|
|
this.port_ = '' + newPort;
|
|
} else {
|
|
this.port_ = null;
|
|
}
|
|
return this;
|
|
};
|
|
URI.prototype.hasPort = function () {
|
|
return null !== this.port_;
|
|
};
|
|
|
|
|
|
URI.prototype.getPath = function () {
|
|
return this.path_ && decodeURIComponent(this.path_);
|
|
};
|
|
URI.prototype.getRawPath = function () {
|
|
return this.path_;
|
|
};
|
|
URI.prototype.setPath = function (newPath) {
|
|
return this.setRawPath(encodeIfExists2(newPath, URI_DISALLOWED_IN_PATH_));
|
|
};
|
|
URI.prototype.setRawPath = function (newPath) {
|
|
if (newPath) {
|
|
newPath = String(newPath);
|
|
this.path_ =
|
|
// Paths must start with '/' unless this is a path-relative URL.
|
|
(!this.domain_ || /^\//.test(newPath)) ? newPath : '/' + newPath;
|
|
} else {
|
|
this.path_ = null;
|
|
}
|
|
return this;
|
|
};
|
|
URI.prototype.hasPath = function () {
|
|
return null !== this.path_;
|
|
};
|
|
|
|
|
|
URI.prototype.getQuery = function () {
|
|
// From http://www.w3.org/Addressing/URL/4_URI_Recommentations.html
|
|
// Within the query string, the plus sign is reserved as shorthand notation
|
|
// for a space.
|
|
return this.query_ && decodeURIComponent(this.query_).replace(/\+/g, ' ');
|
|
};
|
|
URI.prototype.getRawQuery = function () {
|
|
return this.query_;
|
|
};
|
|
URI.prototype.setQuery = function (newQuery) {
|
|
this.paramCache_ = null;
|
|
this.query_ = encodeIfExists(newQuery);
|
|
return this;
|
|
};
|
|
URI.prototype.setRawQuery = function (newQuery) {
|
|
this.paramCache_ = null;
|
|
this.query_ = newQuery ? newQuery : null;
|
|
return this;
|
|
};
|
|
URI.prototype.hasQuery = function () {
|
|
return null !== this.query_;
|
|
};
|
|
|
|
/**
|
|
* sets the query given a list of strings of the form
|
|
* [ key0, value0, key1, value1, ... ].
|
|
*
|
|
* <p><code>uri.setAllParameters(['a', 'b', 'c', 'd']).getQuery()</code>
|
|
* will yield <code>'a=b&c=d'</code>.
|
|
*/
|
|
URI.prototype.setAllParameters = function (params) {
|
|
if (typeof params === 'object') {
|
|
if (!(params instanceof Array)
|
|
&& (params instanceof Object
|
|
|| Object.prototype.toString.call(params) !== '[object Array]')) {
|
|
var newParams = [];
|
|
var i = -1;
|
|
for (var k in params) {
|
|
var v = params[k];
|
|
if ('string' === typeof v) {
|
|
newParams[++i] = k;
|
|
newParams[++i] = v;
|
|
}
|
|
}
|
|
params = newParams;
|
|
}
|
|
}
|
|
this.paramCache_ = null;
|
|
var queryBuf = [];
|
|
var separator = '';
|
|
for (var j = 0; j < params.length;) {
|
|
var k = params[j++];
|
|
var v = params[j++];
|
|
queryBuf.push(separator, encodeURIComponent(k.toString()));
|
|
separator = '&';
|
|
if (v) {
|
|
queryBuf.push('=', encodeURIComponent(v.toString()));
|
|
}
|
|
}
|
|
this.query_ = queryBuf.join('');
|
|
return this;
|
|
};
|
|
URI.prototype.checkParameterCache_ = function () {
|
|
if (!this.paramCache_) {
|
|
var q = this.query_;
|
|
if (!q) {
|
|
this.paramCache_ = [];
|
|
} else {
|
|
var cgiParams = q.split(/[&\?]/);
|
|
var out = [];
|
|
var k = -1;
|
|
for (var i = 0; i < cgiParams.length; ++i) {
|
|
var m = cgiParams[i].match(/^([^=]*)(?:=(.*))?$/);
|
|
// From http://www.w3.org/Addressing/URL/4_URI_Recommentations.html
|
|
// Within the query string, the plus sign is reserved as shorthand
|
|
// notation for a space.
|
|
out[++k] = decodeURIComponent(m[1]).replace(/\+/g, ' ');
|
|
out[++k] = decodeURIComponent(m[2] || '').replace(/\+/g, ' ');
|
|
}
|
|
this.paramCache_ = out;
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* sets the values of the named cgi parameters.
|
|
*
|
|
* <p>So, <code>uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])
|
|
* </code> yields <tt>foo?a=b&c=new&e=f</tt>.</p>
|
|
*
|
|
* @param key {string}
|
|
* @param values {Array.<string>} the new values. If values is a single string
|
|
* then it will be treated as the sole value.
|
|
*/
|
|
URI.prototype.setParameterValues = function (key, values) {
|
|
// be nice and avoid subtle bugs where [] operator on string performs charAt
|
|
// on some browsers and crashes on IE
|
|
if (typeof values === 'string') {
|
|
values = [ values ];
|
|
}
|
|
|
|
this.checkParameterCache_();
|
|
var newValueIndex = 0;
|
|
var pc = this.paramCache_;
|
|
var params = [];
|
|
for (var i = 0, k = 0; i < pc.length; i += 2) {
|
|
if (key === pc[i]) {
|
|
if (newValueIndex < values.length) {
|
|
params.push(key, values[newValueIndex++]);
|
|
}
|
|
} else {
|
|
params.push(pc[i], pc[i + 1]);
|
|
}
|
|
}
|
|
while (newValueIndex < values.length) {
|
|
params.push(key, values[newValueIndex++]);
|
|
}
|
|
this.setAllParameters(params);
|
|
return this;
|
|
};
|
|
URI.prototype.removeParameter = function (key) {
|
|
return this.setParameterValues(key, []);
|
|
};
|
|
/**
|
|
* returns the parameters specified in the query part of the uri as a list of
|
|
* keys and values like [ key0, value0, key1, value1, ... ].
|
|
*
|
|
* @return {Array.<string>}
|
|
*/
|
|
URI.prototype.getAllParameters = function () {
|
|
this.checkParameterCache_();
|
|
return this.paramCache_.slice(0, this.paramCache_.length);
|
|
};
|
|
/**
|
|
* returns the value<b>s</b> for a given cgi parameter as a list of decoded
|
|
* query parameter values.
|
|
* @return {Array.<string>}
|
|
*/
|
|
URI.prototype.getParameterValues = function (paramNameUnescaped) {
|
|
this.checkParameterCache_();
|
|
var values = [];
|
|
for (var i = 0; i < this.paramCache_.length; i += 2) {
|
|
if (paramNameUnescaped === this.paramCache_[i]) {
|
|
values.push(this.paramCache_[i + 1]);
|
|
}
|
|
}
|
|
return values;
|
|
};
|
|
/**
|
|
* returns a map of cgi parameter names to (non-empty) lists of values.
|
|
* @return {Object.<string,Array.<string>>}
|
|
*/
|
|
URI.prototype.getParameterMap = function (paramNameUnescaped) {
|
|
this.checkParameterCache_();
|
|
var paramMap = {};
|
|
for (var i = 0; i < this.paramCache_.length; i += 2) {
|
|
var key = this.paramCache_[i++],
|
|
value = this.paramCache_[i++];
|
|
if (!(key in paramMap)) {
|
|
paramMap[key] = [value];
|
|
} else {
|
|
paramMap[key].push(value);
|
|
}
|
|
}
|
|
return paramMap;
|
|
};
|
|
/**
|
|
* returns the first value for a given cgi parameter or null if the given
|
|
* parameter name does not appear in the query string.
|
|
* If the given parameter name does appear, but has no '<tt>=</tt>' following
|
|
* it, then the empty string will be returned.
|
|
* @return {string|null}
|
|
*/
|
|
URI.prototype.getParameterValue = function (paramNameUnescaped) {
|
|
this.checkParameterCache_();
|
|
for (var i = 0; i < this.paramCache_.length; i += 2) {
|
|
if (paramNameUnescaped === this.paramCache_[i]) {
|
|
return this.paramCache_[i + 1];
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
URI.prototype.getFragment = function () {
|
|
return this.fragment_ && decodeURIComponent(this.fragment_);
|
|
};
|
|
URI.prototype.getRawFragment = function () {
|
|
return this.fragment_;
|
|
};
|
|
URI.prototype.setFragment = function (newFragment) {
|
|
this.fragment_ = newFragment ? encodeURIComponent(newFragment) : null;
|
|
return this;
|
|
};
|
|
URI.prototype.setRawFragment = function (newFragment) {
|
|
this.fragment_ = newFragment ? newFragment : null;
|
|
return this;
|
|
};
|
|
URI.prototype.hasFragment = function () {
|
|
return null !== this.fragment_;
|
|
};
|
|
|
|
function nullIfAbsent(matchPart) {
|
|
return ('string' == typeof matchPart) && (matchPart.length > 0)
|
|
? matchPart
|
|
: null;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* a regular expression for breaking a URI into its component parts.
|
|
*
|
|
* <p>http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#RFC2234 says
|
|
* As the "first-match-wins" algorithm is identical to the "greedy"
|
|
* disambiguation method used by POSIX regular expressions, it is natural and
|
|
* commonplace to use a regular expression for parsing the potential five
|
|
* components of a URI reference.
|
|
*
|
|
* <p>The following line is the regular expression for breaking-down a
|
|
* well-formed URI reference into its components.
|
|
*
|
|
* <pre>
|
|
* ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
|
|
* 12 3 4 5 6 7 8 9
|
|
* </pre>
|
|
*
|
|
* <p>The numbers in the second line above are only to assist readability; they
|
|
* indicate the reference points for each subexpression (i.e., each paired
|
|
* parenthesis). We refer to the value matched for subexpression <n> as $<n>.
|
|
* For example, matching the above expression to
|
|
* <pre>
|
|
* http://www.ics.uci.edu/pub/ietf/uri/#Related
|
|
* </pre>
|
|
* results in the following subexpression matches:
|
|
* <pre>
|
|
* $1 = http:
|
|
* $2 = http
|
|
* $3 = //www.ics.uci.edu
|
|
* $4 = www.ics.uci.edu
|
|
* $5 = /pub/ietf/uri/
|
|
* $6 = <undefined>
|
|
* $7 = <undefined>
|
|
* $8 = #Related
|
|
* $9 = Related
|
|
* </pre>
|
|
* where <undefined> indicates that the component is not present, as is the
|
|
* case for the query component in the above example. Therefore, we can
|
|
* determine the value of the five components as
|
|
* <pre>
|
|
* scheme = $2
|
|
* authority = $4
|
|
* path = $5
|
|
* query = $7
|
|
* fragment = $9
|
|
* </pre>
|
|
*
|
|
* <p>msamuel: I have modified the regular expression slightly to expose the
|
|
* credentials, domain, and port separately from the authority.
|
|
* The modified version yields
|
|
* <pre>
|
|
* $1 = http scheme
|
|
* $2 = <undefined> credentials -\
|
|
* $3 = www.ics.uci.edu domain | authority
|
|
* $4 = <undefined> port -/
|
|
* $5 = /pub/ietf/uri/ path
|
|
* $6 = <undefined> query without ?
|
|
* $7 = Related fragment without #
|
|
* </pre>
|
|
*/
|
|
var URI_RE_ = new RegExp(
|
|
"^" +
|
|
"(?:" +
|
|
"([^:/?#]+)" + // scheme
|
|
":)?" +
|
|
"(?://" +
|
|
"(?:([^/?#]*)@)?" + // credentials
|
|
"([^/?#:@]*)" + // domain
|
|
"(?::([0-9]+))?" + // port
|
|
")?" +
|
|
"([^?#]+)?" + // path
|
|
"(?:\\?([^#]*))?" + // query
|
|
"(?:#(.*))?" + // fragment
|
|
"$"
|
|
);
|
|
|
|
var URI_DISALLOWED_IN_SCHEME_OR_CREDENTIALS_ = /[#\/\?@]/g;
|
|
var URI_DISALLOWED_IN_PATH_ = /[\#\?]/g;
|
|
|
|
URI.parse = parse;
|
|
URI.create = create;
|
|
URI.resolve = resolve;
|
|
URI.collapse_dots = collapse_dots; // Visible for testing.
|
|
|
|
// lightweight string-based api for loadModuleMaker
|
|
URI.utils = {
|
|
mimeTypeOf: function (uri) {
|
|
var uriObj = parse(uri);
|
|
if (/\.html$/.test(uriObj.getPath())) {
|
|
return 'text/html';
|
|
} else {
|
|
return 'application/javascript';
|
|
}
|
|
},
|
|
resolve: function (base, uri) {
|
|
if (base) {
|
|
return resolve(parse(base), parse(uri)).toString();
|
|
} else {
|
|
return '' + uri;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
return URI;
|
|
})();
|
|
|
|
// Copyright Google Inc.
|
|
// Licensed under the Apache Licence Version 2.0
|
|
// Autogenerated at Mon Feb 25 13:05:42 EST 2013
|
|
// @overrides window
|
|
// @provides html4
|
|
var html4 = {};
|
|
html4.atype = {
|
|
'NONE': 0,
|
|
'URI': 1,
|
|
'URI_FRAGMENT': 11,
|
|
'SCRIPT': 2,
|
|
'STYLE': 3,
|
|
'HTML': 12,
|
|
'ID': 4,
|
|
'IDREF': 5,
|
|
'IDREFS': 6,
|
|
'GLOBAL_NAME': 7,
|
|
'LOCAL_NAME': 8,
|
|
'CLASSES': 9,
|
|
'FRAME_TARGET': 10,
|
|
'MEDIA_QUERY': 13
|
|
};
|
|
html4[ 'atype' ] = html4.atype;
|
|
html4.ATTRIBS = {
|
|
'*::class': 9,
|
|
'*::dir': 0,
|
|
'*::draggable': 0,
|
|
'*::hidden': 0,
|
|
'*::id': 4,
|
|
'*::inert': 0,
|
|
'*::itemprop': 0,
|
|
'*::itemref': 6,
|
|
'*::itemscope': 0,
|
|
'*::lang': 0,
|
|
'*::onblur': 2,
|
|
'*::onchange': 2,
|
|
'*::onclick': 2,
|
|
'*::ondblclick': 2,
|
|
'*::onfocus': 2,
|
|
'*::onkeydown': 2,
|
|
'*::onkeypress': 2,
|
|
'*::onkeyup': 2,
|
|
'*::onload': 2,
|
|
'*::onmousedown': 2,
|
|
'*::onmousemove': 2,
|
|
'*::onmouseout': 2,
|
|
'*::onmouseover': 2,
|
|
'*::onmouseup': 2,
|
|
'*::onreset': 2,
|
|
'*::onscroll': 2,
|
|
'*::onselect': 2,
|
|
'*::onsubmit': 2,
|
|
'*::onunload': 2,
|
|
'*::spellcheck': 0,
|
|
'*::style': 3,
|
|
'*::title': 0,
|
|
'*::translate': 0,
|
|
'a::accesskey': 0,
|
|
'a::coords': 0,
|
|
'a::href': 1,
|
|
'a::hreflang': 0,
|
|
'a::name': 7,
|
|
'a::onblur': 2,
|
|
'a::onfocus': 2,
|
|
'a::shape': 0,
|
|
'a::tabindex': 0,
|
|
'a::target': 10,
|
|
'a::type': 0,
|
|
'area::accesskey': 0,
|
|
'area::alt': 0,
|
|
'area::coords': 0,
|
|
'area::href': 1,
|
|
'area::nohref': 0,
|
|
'area::onblur': 2,
|
|
'area::onfocus': 2,
|
|
'area::shape': 0,
|
|
'area::tabindex': 0,
|
|
'area::target': 10,
|
|
'audio::controls': 0,
|
|
'audio::loop': 0,
|
|
'audio::mediagroup': 5,
|
|
'audio::muted': 0,
|
|
'audio::preload': 0,
|
|
'bdo::dir': 0,
|
|
'blockquote::cite': 1,
|
|
'br::clear': 0,
|
|
'button::accesskey': 0,
|
|
'button::disabled': 0,
|
|
'button::name': 8,
|
|
'button::onblur': 2,
|
|
'button::onfocus': 2,
|
|
'button::tabindex': 0,
|
|
'button::type': 0,
|
|
'button::value': 0,
|
|
'canvas::height': 0,
|
|
'canvas::width': 0,
|
|
'caption::align': 0,
|
|
'col::align': 0,
|
|
'col::char': 0,
|
|
'col::charoff': 0,
|
|
'col::span': 0,
|
|
'col::valign': 0,
|
|
'col::width': 0,
|
|
'colgroup::align': 0,
|
|
'colgroup::char': 0,
|
|
'colgroup::charoff': 0,
|
|
'colgroup::span': 0,
|
|
'colgroup::valign': 0,
|
|
'colgroup::width': 0,
|
|
'command::checked': 0,
|
|
'command::command': 5,
|
|
'command::disabled': 0,
|
|
'command::icon': 1,
|
|
'command::label': 0,
|
|
'command::radiogroup': 0,
|
|
'command::type': 0,
|
|
'data::value': 0,
|
|
'del::cite': 1,
|
|
'del::datetime': 0,
|
|
'details::open': 0,
|
|
'dir::compact': 0,
|
|
'div::align': 0,
|
|
'dl::compact': 0,
|
|
'fieldset::disabled': 0,
|
|
'font::color': 0,
|
|
'font::face': 0,
|
|
'font::size': 0,
|
|
'form::accept': 0,
|
|
'form::action': 1,
|
|
'form::autocomplete': 0,
|
|
'form::enctype': 0,
|
|
'form::method': 0,
|
|
'form::name': 7,
|
|
'form::novalidate': 0,
|
|
'form::onreset': 2,
|
|
'form::onsubmit': 2,
|
|
'form::target': 10,
|
|
'h1::align': 0,
|
|
'h2::align': 0,
|
|
'h3::align': 0,
|
|
'h4::align': 0,
|
|
'h5::align': 0,
|
|
'h6::align': 0,
|
|
'hr::align': 0,
|
|
'hr::noshade': 0,
|
|
'hr::size': 0,
|
|
'hr::width': 0,
|
|
'iframe::align': 0,
|
|
'iframe::frameborder': 0,
|
|
'iframe::height': 0,
|
|
'iframe::marginheight': 0,
|
|
'iframe::marginwidth': 0,
|
|
'iframe::width': 0,
|
|
'img::align': 0,
|
|
'img::alt': 0,
|
|
'img::border': 0,
|
|
'img::height': 0,
|
|
'img::hspace': 0,
|
|
'img::ismap': 0,
|
|
'img::name': 7,
|
|
'img::src': 1,
|
|
'img::usemap': 11,
|
|
'img::vspace': 0,
|
|
'img::width': 0,
|
|
'input::accept': 0,
|
|
'input::accesskey': 0,
|
|
'input::align': 0,
|
|
'input::alt': 0,
|
|
'input::autocomplete': 0,
|
|
'input::checked': 0,
|
|
'input::disabled': 0,
|
|
'input::inputmode': 0,
|
|
'input::ismap': 0,
|
|
'input::list': 5,
|
|
'input::max': 0,
|
|
'input::maxlength': 0,
|
|
'input::min': 0,
|
|
'input::multiple': 0,
|
|
'input::name': 8,
|
|
'input::onblur': 2,
|
|
'input::onchange': 2,
|
|
'input::onfocus': 2,
|
|
'input::onselect': 2,
|
|
'input::placeholder': 0,
|
|
'input::readonly': 0,
|
|
'input::required': 0,
|
|
'input::size': 0,
|
|
'input::src': 1,
|
|
'input::step': 0,
|
|
'input::tabindex': 0,
|
|
'input::type': 0,
|
|
'input::usemap': 11,
|
|
'input::value': 0,
|
|
'ins::cite': 1,
|
|
'ins::datetime': 0,
|
|
'label::accesskey': 0,
|
|
'label::for': 5,
|
|
'label::onblur': 2,
|
|
'label::onfocus': 2,
|
|
'legend::accesskey': 0,
|
|
'legend::align': 0,
|
|
'li::type': 0,
|
|
'li::value': 0,
|
|
'map::name': 7,
|
|
'menu::compact': 0,
|
|
'menu::label': 0,
|
|
'menu::type': 0,
|
|
'meter::high': 0,
|
|
'meter::low': 0,
|
|
'meter::max': 0,
|
|
'meter::min': 0,
|
|
'meter::value': 0,
|
|
'ol::compact': 0,
|
|
'ol::reversed': 0,
|
|
'ol::start': 0,
|
|
'ol::type': 0,
|
|
'optgroup::disabled': 0,
|
|
'optgroup::label': 0,
|
|
'option::disabled': 0,
|
|
'option::label': 0,
|
|
'option::selected': 0,
|
|
'option::value': 0,
|
|
'output::for': 6,
|
|
'output::name': 8,
|
|
'p::align': 0,
|
|
'pre::width': 0,
|
|
'progress::max': 0,
|
|
'progress::min': 0,
|
|
'progress::value': 0,
|
|
'q::cite': 1,
|
|
'select::autocomplete': 0,
|
|
'select::disabled': 0,
|
|
'select::multiple': 0,
|
|
'select::name': 8,
|
|
'select::onblur': 2,
|
|
'select::onchange': 2,
|
|
'select::onfocus': 2,
|
|
'select::required': 0,
|
|
'select::size': 0,
|
|
'select::tabindex': 0,
|
|
'source::type': 0,
|
|
'table::align': 0,
|
|
'table::bgcolor': 0,
|
|
'table::border': 0,
|
|
'table::cellpadding': 0,
|
|
'table::cellspacing': 0,
|
|
'table::frame': 0,
|
|
'table::rules': 0,
|
|
'table::summary': 0,
|
|
'table::width': 0,
|
|
'tbody::align': 0,
|
|
'tbody::char': 0,
|
|
'tbody::charoff': 0,
|
|
'tbody::valign': 0,
|
|
'td::abbr': 0,
|
|
'td::align': 0,
|
|
'td::axis': 0,
|
|
'td::bgcolor': 0,
|
|
'td::char': 0,
|
|
'td::charoff': 0,
|
|
'td::colspan': 0,
|
|
'td::headers': 6,
|
|
'td::height': 0,
|
|
'td::nowrap': 0,
|
|
'td::rowspan': 0,
|
|
'td::scope': 0,
|
|
'td::valign': 0,
|
|
'td::width': 0,
|
|
'textarea::accesskey': 0,
|
|
'textarea::autocomplete': 0,
|
|
'textarea::cols': 0,
|
|
'textarea::disabled': 0,
|
|
'textarea::inputmode': 0,
|
|
'textarea::name': 8,
|
|
'textarea::onblur': 2,
|
|
'textarea::onchange': 2,
|
|
'textarea::onfocus': 2,
|
|
'textarea::onselect': 2,
|
|
'textarea::placeholder': 0,
|
|
'textarea::readonly': 0,
|
|
'textarea::required': 0,
|
|
'textarea::rows': 0,
|
|
'textarea::tabindex': 0,
|
|
'textarea::wrap': 0,
|
|
'tfoot::align': 0,
|
|
'tfoot::char': 0,
|
|
'tfoot::charoff': 0,
|
|
'tfoot::valign': 0,
|
|
'th::abbr': 0,
|
|
'th::align': 0,
|
|
'th::axis': 0,
|
|
'th::bgcolor': 0,
|
|
'th::char': 0,
|
|
'th::charoff': 0,
|
|
'th::colspan': 0,
|
|
'th::headers': 6,
|
|
'th::height': 0,
|
|
'th::nowrap': 0,
|
|
'th::rowspan': 0,
|
|
'th::scope': 0,
|
|
'th::valign': 0,
|
|
'th::width': 0,
|
|
'thead::align': 0,
|
|
'thead::char': 0,
|
|
'thead::charoff': 0,
|
|
'thead::valign': 0,
|
|
'tr::align': 0,
|
|
'tr::bgcolor': 0,
|
|
'tr::char': 0,
|
|
'tr::charoff': 0,
|
|
'tr::valign': 0,
|
|
'track::default': 0,
|
|
'track::kind': 0,
|
|
'track::label': 0,
|
|
'track::srclang': 0,
|
|
'ul::compact': 0,
|
|
'ul::type': 0,
|
|
'video::controls': 0,
|
|
'video::height': 0,
|
|
'video::loop': 0,
|
|
'video::mediagroup': 5,
|
|
'video::muted': 0,
|
|
'video::poster': 1,
|
|
'video::preload': 0,
|
|
'video::width': 0
|
|
};
|
|
html4[ 'ATTRIBS' ] = html4.ATTRIBS;
|
|
html4.eflags = {
|
|
'OPTIONAL_ENDTAG': 1,
|
|
'EMPTY': 2,
|
|
'CDATA': 4,
|
|
'RCDATA': 8,
|
|
'UNSAFE': 16,
|
|
'FOLDABLE': 32,
|
|
'SCRIPT': 64,
|
|
'STYLE': 128,
|
|
'VIRTUALIZED': 256
|
|
};
|
|
html4[ 'eflags' ] = html4.eflags;
|
|
// these are bitmasks of the eflags above.
|
|
html4.ELEMENTS = {
|
|
'a': 0,
|
|
'abbr': 0,
|
|
'acronym': 0,
|
|
'address': 0,
|
|
'applet': 272,
|
|
'area': 2,
|
|
'article': 0,
|
|
'aside': 0,
|
|
'audio': 0,
|
|
'b': 0,
|
|
'base': 274,
|
|
'basefont': 274,
|
|
'bdi': 0,
|
|
'bdo': 0,
|
|
'big': 0,
|
|
'blockquote': 0,
|
|
'body': 305,
|
|
'br': 2,
|
|
'button': 0,
|
|
'canvas': 0,
|
|
'caption': 0,
|
|
'center': 0,
|
|
'cite': 0,
|
|
'code': 0,
|
|
'col': 2,
|
|
'colgroup': 1,
|
|
'command': 2,
|
|
'data': 0,
|
|
'datalist': 0,
|
|
'dd': 1,
|
|
'del': 0,
|
|
'details': 0,
|
|
'dfn': 0,
|
|
'dialog': 272,
|
|
'dir': 0,
|
|
'div': 0,
|
|
'dl': 0,
|
|
'dt': 1,
|
|
'em': 0,
|
|
'fieldset': 0,
|
|
'figcaption': 0,
|
|
'figure': 0,
|
|
'font': 0,
|
|
'footer': 0,
|
|
'form': 0,
|
|
'frame': 274,
|
|
'frameset': 272,
|
|
'h1': 0,
|
|
'h2': 0,
|
|
'h3': 0,
|
|
'h4': 0,
|
|
'h5': 0,
|
|
'h6': 0,
|
|
'head': 305,
|
|
'header': 0,
|
|
'hgroup': 0,
|
|
'hr': 2,
|
|
'html': 305,
|
|
'i': 0,
|
|
'iframe': 16,
|
|
'img': 2,
|
|
'input': 2,
|
|
'ins': 0,
|
|
'isindex': 274,
|
|
'kbd': 0,
|
|
'keygen': 274,
|
|
'label': 0,
|
|
'legend': 0,
|
|
'li': 1,
|
|
'link': 274,
|
|
'map': 0,
|
|
'mark': 0,
|
|
'menu': 0,
|
|
'meta': 274,
|
|
'meter': 0,
|
|
'nav': 0,
|
|
'nobr': 0,
|
|
'noembed': 276,
|
|
'noframes': 276,
|
|
'noscript': 276,
|
|
'object': 272,
|
|
'ol': 0,
|
|
'optgroup': 0,
|
|
'option': 1,
|
|
'output': 0,
|
|
'p': 1,
|
|
'param': 274,
|
|
'pre': 0,
|
|
'progress': 0,
|
|
'q': 0,
|
|
's': 0,
|
|
'samp': 0,
|
|
'script': 84,
|
|
'section': 0,
|
|
'select': 0,
|
|
'small': 0,
|
|
'source': 2,
|
|
'span': 0,
|
|
'strike': 0,
|
|
'strong': 0,
|
|
'style': 148,
|
|
'sub': 0,
|
|
'summary': 0,
|
|
'sup': 0,
|
|
'table': 0,
|
|
'tbody': 1,
|
|
'td': 1,
|
|
'textarea': 8,
|
|
'tfoot': 1,
|
|
'th': 1,
|
|
'thead': 1,
|
|
'time': 0,
|
|
'title': 280,
|
|
'tr': 1,
|
|
'track': 2,
|
|
'tt': 0,
|
|
'u': 0,
|
|
'ul': 0,
|
|
'var': 0,
|
|
'video': 0,
|
|
'wbr': 2
|
|
};
|
|
html4[ 'ELEMENTS' ] = html4.ELEMENTS;
|
|
html4.ELEMENT_DOM_INTERFACES = {
|
|
'a': 'HTMLAnchorElement',
|
|
'abbr': 'HTMLElement',
|
|
'acronym': 'HTMLElement',
|
|
'address': 'HTMLElement',
|
|
'applet': 'HTMLAppletElement',
|
|
'area': 'HTMLAreaElement',
|
|
'article': 'HTMLElement',
|
|
'aside': 'HTMLElement',
|
|
'audio': 'HTMLAudioElement',
|
|
'b': 'HTMLElement',
|
|
'base': 'HTMLBaseElement',
|
|
'basefont': 'HTMLBaseFontElement',
|
|
'bdi': 'HTMLElement',
|
|
'bdo': 'HTMLElement',
|
|
'big': 'HTMLElement',
|
|
'blockquote': 'HTMLQuoteElement',
|
|
'body': 'HTMLBodyElement',
|
|
'br': 'HTMLBRElement',
|
|
'button': 'HTMLButtonElement',
|
|
'canvas': 'HTMLCanvasElement',
|
|
'caption': 'HTMLTableCaptionElement',
|
|
'center': 'HTMLElement',
|
|
'cite': 'HTMLElement',
|
|
'code': 'HTMLElement',
|
|
'col': 'HTMLTableColElement',
|
|
'colgroup': 'HTMLTableColElement',
|
|
'command': 'HTMLCommandElement',
|
|
'data': 'HTMLElement',
|
|
'datalist': 'HTMLDataListElement',
|
|
'dd': 'HTMLElement',
|
|
'del': 'HTMLModElement',
|
|
'details': 'HTMLDetailsElement',
|
|
'dfn': 'HTMLElement',
|
|
'dialog': 'HTMLDialogElement',
|
|
'dir': 'HTMLDirectoryElement',
|
|
'div': 'HTMLDivElement',
|
|
'dl': 'HTMLDListElement',
|
|
'dt': 'HTMLElement',
|
|
'em': 'HTMLElement',
|
|
'fieldset': 'HTMLFieldSetElement',
|
|
'figcaption': 'HTMLElement',
|
|
'figure': 'HTMLElement',
|
|
'font': 'HTMLFontElement',
|
|
'footer': 'HTMLElement',
|
|
'form': 'HTMLFormElement',
|
|
'frame': 'HTMLFrameElement',
|
|
'frameset': 'HTMLFrameSetElement',
|
|
'h1': 'HTMLHeadingElement',
|
|
'h2': 'HTMLHeadingElement',
|
|
'h3': 'HTMLHeadingElement',
|
|
'h4': 'HTMLHeadingElement',
|
|
'h5': 'HTMLHeadingElement',
|
|
'h6': 'HTMLHeadingElement',
|
|
'head': 'HTMLHeadElement',
|
|
'header': 'HTMLElement',
|
|
'hgroup': 'HTMLElement',
|
|
'hr': 'HTMLHRElement',
|
|
'html': 'HTMLHtmlElement',
|
|
'i': 'HTMLElement',
|
|
'iframe': 'HTMLIFrameElement',
|
|
'img': 'HTMLImageElement',
|
|
'input': 'HTMLInputElement',
|
|
'ins': 'HTMLModElement',
|
|
'isindex': 'HTMLUnknownElement',
|
|
'kbd': 'HTMLElement',
|
|
'keygen': 'HTMLKeygenElement',
|
|
'label': 'HTMLLabelElement',
|
|
'legend': 'HTMLLegendElement',
|
|
'li': 'HTMLLIElement',
|
|
'link': 'HTMLLinkElement',
|
|
'map': 'HTMLMapElement',
|
|
'mark': 'HTMLElement',
|
|
'menu': 'HTMLMenuElement',
|
|
'meta': 'HTMLMetaElement',
|
|
'meter': 'HTMLMeterElement',
|
|
'nav': 'HTMLElement',
|
|
'nobr': 'HTMLElement',
|
|
'noembed': 'HTMLElement',
|
|
'noframes': 'HTMLElement',
|
|
'noscript': 'HTMLElement',
|
|
'object': 'HTMLObjectElement',
|
|
'ol': 'HTMLOListElement',
|
|
'optgroup': 'HTMLOptGroupElement',
|
|
'option': 'HTMLOptionElement',
|
|
'output': 'HTMLOutputElement',
|
|
'p': 'HTMLParagraphElement',
|
|
'param': 'HTMLParamElement',
|
|
'pre': 'HTMLPreElement',
|
|
'progress': 'HTMLProgressElement',
|
|
'q': 'HTMLQuoteElement',
|
|
's': 'HTMLElement',
|
|
'samp': 'HTMLElement',
|
|
'script': 'HTMLScriptElement',
|
|
'section': 'HTMLElement',
|
|
'select': 'HTMLSelectElement',
|
|
'small': 'HTMLElement',
|
|
'source': 'HTMLSourceElement',
|
|
'span': 'HTMLSpanElement',
|
|
'strike': 'HTMLElement',
|
|
'strong': 'HTMLElement',
|
|
'style': 'HTMLStyleElement',
|
|
'sub': 'HTMLElement',
|
|
'summary': 'HTMLElement',
|
|
'sup': 'HTMLElement',
|
|
'table': 'HTMLTableElement',
|
|
'tbody': 'HTMLTableSectionElement',
|
|
'td': 'HTMLTableDataCellElement',
|
|
'textarea': 'HTMLTextAreaElement',
|
|
'tfoot': 'HTMLTableSectionElement',
|
|
'th': 'HTMLTableHeaderCellElement',
|
|
'thead': 'HTMLTableSectionElement',
|
|
'time': 'HTMLTimeElement',
|
|
'title': 'HTMLTitleElement',
|
|
'tr': 'HTMLTableRowElement',
|
|
'track': 'HTMLTrackElement',
|
|
'tt': 'HTMLElement',
|
|
'u': 'HTMLElement',
|
|
'ul': 'HTMLUListElement',
|
|
'var': 'HTMLElement',
|
|
'video': 'HTMLVideoElement',
|
|
'wbr': 'HTMLElement'
|
|
};
|
|
html4[ 'ELEMENT_DOM_INTERFACES' ] = html4.ELEMENT_DOM_INTERFACES;
|
|
html4.ueffects = {
|
|
'NOT_LOADED': 0,
|
|
'SAME_DOCUMENT': 1,
|
|
'NEW_DOCUMENT': 2
|
|
};
|
|
html4[ 'ueffects' ] = html4.ueffects;
|
|
html4.URIEFFECTS = {
|
|
'a::href': 2,
|
|
'area::href': 2,
|
|
'blockquote::cite': 0,
|
|
'command::icon': 1,
|
|
'del::cite': 0,
|
|
'form::action': 2,
|
|
'img::src': 1,
|
|
'input::src': 1,
|
|
'ins::cite': 0,
|
|
'q::cite': 0,
|
|
'video::poster': 1
|
|
};
|
|
html4[ 'URIEFFECTS' ] = html4.URIEFFECTS;
|
|
html4.ltypes = {
|
|
'UNSANDBOXED': 2,
|
|
'SANDBOXED': 1,
|
|
'DATA': 0
|
|
};
|
|
html4[ 'ltypes' ] = html4.ltypes;
|
|
html4.LOADERTYPES = {
|
|
'a::href': 2,
|
|
'area::href': 2,
|
|
'blockquote::cite': 2,
|
|
'command::icon': 1,
|
|
'del::cite': 2,
|
|
'form::action': 2,
|
|
'img::src': 1,
|
|
'input::src': 1,
|
|
'ins::cite': 2,
|
|
'q::cite': 2,
|
|
'video::poster': 1
|
|
};
|
|
html4[ 'LOADERTYPES' ] = html4.LOADERTYPES;
|
|
|
|
// Copyright (C) 2006 Google Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview
|
|
* An HTML sanitizer that can satisfy a variety of security policies.
|
|
*
|
|
* <p>
|
|
* The HTML sanitizer is built around a SAX parser and HTML element and
|
|
* attributes schemas.
|
|
*
|
|
* If the cssparser is loaded, inline styles are sanitized using the
|
|
* css property and value schemas. Else they are remove during
|
|
* sanitization.
|
|
*
|
|
* If it exists, uses parseCssDeclarations, sanitizeCssProperty, cssSchema
|
|
*
|
|
* @author mikesamuel@gmail.com
|
|
* @author jasvir@gmail.com
|
|
* \@requires html4, URI
|
|
* \@overrides window
|
|
* \@provides html, html_sanitize
|
|
*/
|
|
|
|
// The Turkish i seems to be a non-issue, but abort in case it is.
|
|
if ('I'.toLowerCase() !== 'i') { throw 'I/i problem'; }
|
|
|
|
/**
|
|
* \@namespace
|
|
*/
|
|
var html = (function(html4) {
|
|
|
|
// For closure compiler
|
|
var parseCssDeclarations, sanitizeCssProperty, cssSchema;
|
|
if ('undefined' !== typeof window) {
|
|
parseCssDeclarations = window['parseCssDeclarations'];
|
|
sanitizeCssProperty = window['sanitizeCssProperty'];
|
|
cssSchema = window['cssSchema'];
|
|
}
|
|
|
|
// The keys of this object must be 'quoted' or JSCompiler will mangle them!
|
|
// This is a partial list -- lookupEntity() uses the host browser's parser
|
|
// (when available) to implement full entity lookup.
|
|
// Note that entities are in general case-sensitive; the uppercase ones are
|
|
// explicitly defined by HTML5 (presumably as compatibility).
|
|
var ENTITIES = {
|
|
'lt': '<',
|
|
'LT': '<',
|
|
'gt': '>',
|
|
'GT': '>',
|
|
'amp': '&',
|
|
'AMP': '&',
|
|
'quot': '"',
|
|
'apos': '\'',
|
|
'nbsp': '\u00A0'
|
|
};
|
|
|
|
// Patterns for types of entity/character reference names.
|
|
var decimalEscapeRe = /^#(\d+)$/;
|
|
var hexEscapeRe = /^#x([0-9A-Fa-f]+)$/;
|
|
// contains every entity per http://www.w3.org/TR/2011/WD-html5-20110113/named-character-references.html
|
|
var safeEntityNameRe = /^[A-Za-z][A-za-z0-9]+$/;
|
|
// Used as a hook to invoke the browser's entity parsing. <textarea> is used
|
|
// because its content is parsed for entities but not tags.
|
|
// TODO(kpreid): This retrieval is a kludge and leads to silent loss of
|
|
// functionality if the document isn't available.
|
|
var entityLookupElement =
|
|
('undefined' !== typeof window && window['document'])
|
|
? window['document'].createElement('textarea') : null;
|
|
/**
|
|
* Decodes an HTML entity.
|
|
*
|
|
* {\@updoc
|
|
* $ lookupEntity('lt')
|
|
* # '<'
|
|
* $ lookupEntity('GT')
|
|
* # '>'
|
|
* $ lookupEntity('amp')
|
|
* # '&'
|
|
* $ lookupEntity('nbsp')
|
|
* # '\xA0'
|
|
* $ lookupEntity('apos')
|
|
* # "'"
|
|
* $ lookupEntity('quot')
|
|
* # '"'
|
|
* $ lookupEntity('#xa')
|
|
* # '\n'
|
|
* $ lookupEntity('#10')
|
|
* # '\n'
|
|
* $ lookupEntity('#x0a')
|
|
* # '\n'
|
|
* $ lookupEntity('#010')
|
|
* # '\n'
|
|
* $ lookupEntity('#x00A')
|
|
* # '\n'
|
|
* $ lookupEntity('Pi') // Known failure
|
|
* # '\u03A0'
|
|
* $ lookupEntity('pi') // Known failure
|
|
* # '\u03C0'
|
|
* }
|
|
*
|
|
* @param {string} name the content between the '&' and the ';'.
|
|
* @return {string} a single unicode code-point as a string.
|
|
*/
|
|
function lookupEntity(name) {
|
|
// TODO: entity lookup as specified by HTML5 actually depends on the
|
|
// presence of the ";".
|
|
if (ENTITIES.hasOwnProperty(name)) { return ENTITIES[name]; }
|
|
var m = name.match(decimalEscapeRe);
|
|
if (m) {
|
|
return String.fromCharCode(parseInt(m[1], 10));
|
|
} else if (!!(m = name.match(hexEscapeRe))) {
|
|
return String.fromCharCode(parseInt(m[1], 16));
|
|
} else if (entityLookupElement && safeEntityNameRe.test(name)) {
|
|
entityLookupElement.innerHTML = '&' + name + ';';
|
|
var text = entityLookupElement.textContent;
|
|
ENTITIES[name] = text;
|
|
return text;
|
|
} else {
|
|
return '&' + name + ';';
|
|
}
|
|
}
|
|
|
|
function decodeOneEntity(_, name) {
|
|
return lookupEntity(name);
|
|
}
|
|
|
|
var nulRe = /\0/g;
|
|
function stripNULs(s) {
|
|
return s.replace(nulRe, '');
|
|
}
|
|
|
|
var ENTITY_RE_1 = /&(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/g;
|
|
var ENTITY_RE_2 = /^(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/;
|
|
/**
|
|
* The plain text of a chunk of HTML CDATA which possibly containing.
|
|
*
|
|
* {\@updoc
|
|
* $ unescapeEntities('')
|
|
* # ''
|
|
* $ unescapeEntities('hello World!')
|
|
* # 'hello World!'
|
|
* $ unescapeEntities('1 < 2 && 4 > 3 ')
|
|
* # '1 < 2 && 4 > 3\n'
|
|
* $ unescapeEntities('<< <- unfinished entity>')
|
|
* # '<< <- unfinished entity>'
|
|
* $ unescapeEntities('/foo?bar=baz©=true') // & often unescaped in URLS
|
|
* # '/foo?bar=baz©=true'
|
|
* $ unescapeEntities('pi=ππ, Pi=Π\u03A0') // FIXME: known failure
|
|
* # 'pi=\u03C0\u03c0, Pi=\u03A0\u03A0'
|
|
* }
|
|
*
|
|
* @param {string} s a chunk of HTML CDATA. It must not start or end inside
|
|
* an HTML entity.
|
|
*/
|
|
function unescapeEntities(s) {
|
|
return s.replace(ENTITY_RE_1, decodeOneEntity);
|
|
}
|
|
|
|
var ampRe = /&/g;
|
|
var looseAmpRe = /&([^a-z#]|#(?:[^0-9x]|x(?:[^0-9a-f]|$)|$)|$)/gi;
|
|
var ltRe = /[<]/g;
|
|
var gtRe = />/g;
|
|
var quotRe = /\"/g;
|
|
|
|
/**
|
|
* Escapes HTML special characters in attribute values.
|
|
*
|
|
* {\@updoc
|
|
* $ escapeAttrib('')
|
|
* # ''
|
|
* $ escapeAttrib('"<<&==&>>"') // Do not just escape the first occurrence.
|
|
* # '"<<&==&>>"'
|
|
* $ escapeAttrib('Hello <World>!')
|
|
* # 'Hello <World>!'
|
|
* }
|
|
*/
|
|
function escapeAttrib(s) {
|
|
return ('' + s).replace(ampRe, '&').replace(ltRe, '<')
|
|
.replace(gtRe, '>').replace(quotRe, '"');
|
|
}
|
|
|
|
/**
|
|
* Escape entities in RCDATA that can be escaped without changing the meaning.
|
|
* {\@updoc
|
|
* $ normalizeRCData('1 < 2 && 3 > 4 && 5 < 7&8')
|
|
* # '1 < 2 && 3 > 4 && 5 < 7&8'
|
|
* }
|
|
*/
|
|
function normalizeRCData(rcdata) {
|
|
return rcdata
|
|
.replace(looseAmpRe, '&$1')
|
|
.replace(ltRe, '<')
|
|
.replace(gtRe, '>');
|
|
}
|
|
|
|
// TODO(felix8a): validate sanitizer regexs against the HTML5 grammar at
|
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html
|
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html
|
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html
|
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html
|
|
|
|
// We initially split input so that potentially meaningful characters
|
|
// like '<' and '>' are separate tokens, using a fast dumb process that
|
|
// ignores quoting. Then we walk that token stream, and when we see a
|
|
// '<' that's the start of a tag, we use ATTR_RE to extract tag
|
|
// attributes from the next token. That token will never have a '>'
|
|
// character. However, it might have an unbalanced quote character, and
|
|
// when we see that, we combine additional tokens to balance the quote.
|
|
|
|
var ATTR_RE = new RegExp(
|
|
'^\\s*' +
|
|
'([-.:\\w]+)' + // 1 = Attribute name
|
|
'(?:' + (
|
|
'\\s*(=)\\s*' + // 2 = Is there a value?
|
|
'(' + ( // 3 = Attribute value
|
|
// TODO(felix8a): maybe use backref to match quotes
|
|
'(\")[^\"]*(\"|$)' + // 4, 5 = Double-quoted string
|
|
'|' +
|
|
'(\')[^\']*(\'|$)' + // 6, 7 = Single-quoted string
|
|
'|' +
|
|
// Positive lookahead to prevent interpretation of
|
|
// <foo a= b=c> as <foo a='b=c'>
|
|
// TODO(felix8a): might be able to drop this case
|
|
'(?=[a-z][-\\w]*\\s*=)' +
|
|
'|' +
|
|
// Unquoted value that isn't an attribute name
|
|
// (since we didn't match the positive lookahead above)
|
|
'[^\"\'\\s]*' ) +
|
|
')' ) +
|
|
')?',
|
|
'i');
|
|
|
|
// false on IE<=8, true on most other browsers
|
|
var splitWillCapture = ('a,b'.split(/(,)/).length === 3);
|
|
|
|
// bitmask for tags with special parsing, like <script> and <textarea>
|
|
var EFLAGS_TEXT = html4.eflags['CDATA'] | html4.eflags['RCDATA'];
|
|
|
|
/**
|
|
* Given a SAX-like event handler, produce a function that feeds those
|
|
* events and a parameter to the event handler.
|
|
*
|
|
* The event handler has the form:{@code
|
|
* {
|
|
* // Name is an upper-case HTML tag name. Attribs is an array of
|
|
* // alternating upper-case attribute names, and attribute values. The
|
|
* // attribs array is reused by the parser. Param is the value passed to
|
|
* // the saxParser.
|
|
* startTag: function (name, attribs, param) { ... },
|
|
* endTag: function (name, param) { ... },
|
|
* pcdata: function (text, param) { ... },
|
|
* rcdata: function (text, param) { ... },
|
|
* cdata: function (text, param) { ... },
|
|
* startDoc: function (param) { ... },
|
|
* endDoc: function (param) { ... }
|
|
* }}
|
|
*
|
|
* @param {Object} handler a record containing event handlers.
|
|
* @return {function(string, Object)} A function that takes a chunk of HTML
|
|
* and a parameter. The parameter is passed on to the handler methods.
|
|
*/
|
|
function makeSaxParser(handler) {
|
|
// Accept quoted or unquoted keys (Closure compat)
|
|
var hcopy = {
|
|
cdata: handler.cdata || handler['cdata'],
|
|
comment: handler.comment || handler['comment'],
|
|
endDoc: handler.endDoc || handler['endDoc'],
|
|
endTag: handler.endTag || handler['endTag'],
|
|
pcdata: handler.pcdata || handler['pcdata'],
|
|
rcdata: handler.rcdata || handler['rcdata'],
|
|
startDoc: handler.startDoc || handler['startDoc'],
|
|
startTag: handler.startTag || handler['startTag']
|
|
};
|
|
return function(htmlText, param) {
|
|
return parse(htmlText, hcopy, param);
|
|
};
|
|
}
|
|
|
|
// Parsing strategy is to split input into parts that might be lexically
|
|
// meaningful (every ">" becomes a separate part), and then recombine
|
|
// parts if we discover they're in a different context.
|
|
|
|
// TODO(felix8a): Significant performance regressions from -legacy,
|
|
// tested on
|
|
// Chrome 18.0
|
|
// Firefox 11.0
|
|
// IE 6, 7, 8, 9
|
|
// Opera 11.61
|
|
// Safari 5.1.3
|
|
// Many of these are unusual patterns that are linearly slower and still
|
|
// pretty fast (eg 1ms to 5ms), so not necessarily worth fixing.
|
|
|
|
// TODO(felix8a): "<script> && && && ... <\/script>" is slower on all
|
|
// browsers. The hotspot is htmlSplit.
|
|
|
|
// TODO(felix8a): "<p title='>>>>...'><\/p>" is slower on all browsers.
|
|
// This is partly htmlSplit, but the hotspot is parseTagAndAttrs.
|
|
|
|
// TODO(felix8a): "<a><\/a><a><\/a>..." is slower on IE9.
|
|
// "<a>1<\/a><a>1<\/a>..." is faster, "<a><\/a>2<a><\/a>2..." is faster.
|
|
|
|
// TODO(felix8a): "<p<p<p..." is slower on IE[6-8]
|
|
|
|
var continuationMarker = {};
|
|
function parse(htmlText, handler, param) {
|
|
var m, p, tagName;
|
|
var parts = htmlSplit(htmlText);
|
|
var state = {
|
|
noMoreGT: false,
|
|
noMoreEndComments: false
|
|
};
|
|
parseCPS(handler, parts, 0, state, param);
|
|
}
|
|
|
|
function continuationMaker(h, parts, initial, state, param) {
|
|
return function () {
|
|
parseCPS(h, parts, initial, state, param);
|
|
};
|
|
}
|
|
|
|
function parseCPS(h, parts, initial, state, param) {
|
|
try {
|
|
if (h.startDoc && initial == 0) { h.startDoc(param); }
|
|
var m, p, tagName;
|
|
for (var pos = initial, end = parts.length; pos < end;) {
|
|
var current = parts[pos++];
|
|
var next = parts[pos];
|
|
switch (current) {
|
|
case '&':
|
|
if (ENTITY_RE_2.test(next)) {
|
|
if (h.pcdata) {
|
|
h.pcdata('&' + next, param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
pos++;
|
|
} else {
|
|
if (h.pcdata) { h.pcdata("&", param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
}
|
|
break;
|
|
case '<\/':
|
|
if (m = /^([-\w:]+)[^\'\"]*/.exec(next)) {
|
|
if (m[0].length === next.length && parts[pos + 1] === '>') {
|
|
// fast case, no attribute parsing needed
|
|
pos += 2;
|
|
tagName = m[1].toLowerCase();
|
|
if (h.endTag) {
|
|
h.endTag(tagName, param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
} else {
|
|
// slow case, need to parse attributes
|
|
// TODO(felix8a): do we really care about misparsing this?
|
|
pos = parseEndTag(
|
|
parts, pos, h, param, continuationMarker, state);
|
|
}
|
|
} else {
|
|
if (h.pcdata) {
|
|
h.pcdata('</', param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
}
|
|
break;
|
|
case '<':
|
|
if (m = /^([-\w:]+)\s*\/?/.exec(next)) {
|
|
if (m[0].length === next.length && parts[pos + 1] === '>') {
|
|
// fast case, no attribute parsing needed
|
|
pos += 2;
|
|
tagName = m[1].toLowerCase();
|
|
if (h.startTag) {
|
|
h.startTag(tagName, [], param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
// tags like <script> and <textarea> have special parsing
|
|
var eflags = html4.ELEMENTS[tagName];
|
|
if (eflags & EFLAGS_TEXT) {
|
|
var tag = { name: tagName, next: pos, eflags: eflags };
|
|
pos = parseText(
|
|
parts, tag, h, param, continuationMarker, state);
|
|
}
|
|
} else {
|
|
// slow case, need to parse attributes
|
|
pos = parseStartTag(
|
|
parts, pos, h, param, continuationMarker, state);
|
|
}
|
|
} else {
|
|
if (h.pcdata) {
|
|
h.pcdata('<', param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
}
|
|
break;
|
|
case '<\!--':
|
|
// The pathological case is n copies of '<\!--' without '-->', and
|
|
// repeated failure to find '-->' is quadratic. We avoid that by
|
|
// remembering when search for '-->' fails.
|
|
if (!state.noMoreEndComments) {
|
|
// A comment <\!--x--> is split into three tokens:
|
|
// '<\!--', 'x--', '>'
|
|
// We want to find the next '>' token that has a preceding '--'.
|
|
// pos is at the 'x--'.
|
|
for (p = pos + 1; p < end; p++) {
|
|
if (parts[p] === '>' && /--$/.test(parts[p - 1])) { break; }
|
|
}
|
|
if (p < end) {
|
|
if (h.comment) {
|
|
var comment = parts.slice(pos, p).join('');
|
|
h.comment(
|
|
comment.substr(0, comment.length - 2), param,
|
|
continuationMarker,
|
|
continuationMaker(h, parts, p + 1, state, param));
|
|
}
|
|
pos = p + 1;
|
|
} else {
|
|
state.noMoreEndComments = true;
|
|
}
|
|
}
|
|
if (state.noMoreEndComments) {
|
|
if (h.pcdata) {
|
|
h.pcdata('<!--', param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
}
|
|
break;
|
|
case '<\!':
|
|
if (!/^\w/.test(next)) {
|
|
if (h.pcdata) {
|
|
h.pcdata('<!', param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
} else {
|
|
// similar to noMoreEndComment logic
|
|
if (!state.noMoreGT) {
|
|
for (p = pos + 1; p < end; p++) {
|
|
if (parts[p] === '>') { break; }
|
|
}
|
|
if (p < end) {
|
|
pos = p + 1;
|
|
} else {
|
|
state.noMoreGT = true;
|
|
}
|
|
}
|
|
if (state.noMoreGT) {
|
|
if (h.pcdata) {
|
|
h.pcdata('<!', param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case '<?':
|
|
// similar to noMoreEndComment logic
|
|
if (!state.noMoreGT) {
|
|
for (p = pos + 1; p < end; p++) {
|
|
if (parts[p] === '>') { break; }
|
|
}
|
|
if (p < end) {
|
|
pos = p + 1;
|
|
} else {
|
|
state.noMoreGT = true;
|
|
}
|
|
}
|
|
if (state.noMoreGT) {
|
|
if (h.pcdata) {
|
|
h.pcdata('<?', param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
}
|
|
break;
|
|
case '>':
|
|
if (h.pcdata) {
|
|
h.pcdata(">", param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
break;
|
|
case '':
|
|
break;
|
|
default:
|
|
if (h.pcdata) {
|
|
h.pcdata(current, param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (h.endDoc) { h.endDoc(param); }
|
|
} catch (e) {
|
|
if (e !== continuationMarker) { throw e; }
|
|
}
|
|
}
|
|
|
|
// Split str into parts for the html parser.
|
|
function htmlSplit(str) {
|
|
// can't hoist this out of the function because of the re.exec loop.
|
|
var re = /(<\/|<\!--|<[!?]|[&<>])/g;
|
|
str += '';
|
|
if (splitWillCapture) {
|
|
return str.split(re);
|
|
} else {
|
|
var parts = [];
|
|
var lastPos = 0;
|
|
var m;
|
|
while ((m = re.exec(str)) !== null) {
|
|
parts.push(str.substring(lastPos, m.index));
|
|
parts.push(m[0]);
|
|
lastPos = m.index + m[0].length;
|
|
}
|
|
parts.push(str.substring(lastPos));
|
|
return parts;
|
|
}
|
|
}
|
|
|
|
function parseEndTag(parts, pos, h, param, continuationMarker, state) {
|
|
var tag = parseTagAndAttrs(parts, pos);
|
|
// drop unclosed tags
|
|
if (!tag) { return parts.length; }
|
|
if (h.endTag) {
|
|
h.endTag(tag.name, param, continuationMarker,
|
|
continuationMaker(h, parts, pos, state, param));
|
|
}
|
|
return tag.next;
|
|
}
|
|
|
|
function parseStartTag(parts, pos, h, param, continuationMarker, state) {
|
|
var tag = parseTagAndAttrs(parts, pos);
|
|
// drop unclosed tags
|
|
if (!tag) { return parts.length; }
|
|
if (h.startTag) {
|
|
h.startTag(tag.name, tag.attrs, param, continuationMarker,
|
|
continuationMaker(h, parts, tag.next, state, param));
|
|
}
|
|
// tags like <script> and <textarea> have special parsing
|
|
if (tag.eflags & EFLAGS_TEXT) {
|
|
return parseText(parts, tag, h, param, continuationMarker, state);
|
|
} else {
|
|
return tag.next;
|
|
}
|
|
}
|
|
|
|
var endTagRe = {};
|
|
|
|
// Tags like <script> and <textarea> are flagged as CDATA or RCDATA,
|
|
// which means everything is text until we see the correct closing tag.
|
|
function parseText(parts, tag, h, param, continuationMarker, state) {
|
|
var end = parts.length;
|
|
if (!endTagRe.hasOwnProperty(tag.name)) {
|
|
endTagRe[tag.name] = new RegExp('^' + tag.name + '(?:[\\s\\/]|$)', 'i');
|
|
}
|
|
var re = endTagRe[tag.name];
|
|
var first = tag.next;
|
|
var p = tag.next + 1;
|
|
for (; p < end; p++) {
|
|
if (parts[p - 1] === '<\/' && re.test(parts[p])) { break; }
|
|
}
|
|
if (p < end) { p -= 1; }
|
|
var buf = parts.slice(first, p).join('');
|
|
if (tag.eflags & html4.eflags['CDATA']) {
|
|
if (h.cdata) {
|
|
h.cdata(buf, param, continuationMarker,
|
|
continuationMaker(h, parts, p, state, param));
|
|
}
|
|
} else if (tag.eflags & html4.eflags['RCDATA']) {
|
|
if (h.rcdata) {
|
|
h.rcdata(normalizeRCData(buf), param, continuationMarker,
|
|
continuationMaker(h, parts, p, state, param));
|
|
}
|
|
} else {
|
|
throw new Error('bug');
|
|
}
|
|
return p;
|
|
}
|
|
|
|
// at this point, parts[pos-1] is either "<" or "<\/".
|
|
function parseTagAndAttrs(parts, pos) {
|
|
var m = /^([-\w:]+)/.exec(parts[pos]);
|
|
var tag = {};
|
|
tag.name = m[1].toLowerCase();
|
|
tag.eflags = html4.ELEMENTS[tag.name];
|
|
var buf = parts[pos].substr(m[0].length);
|
|
// Find the next '>'. We optimistically assume this '>' is not in a
|
|
// quoted context, and further down we fix things up if it turns out to
|
|
// be quoted.
|
|
var p = pos + 1;
|
|
var end = parts.length;
|
|
for (; p < end; p++) {
|
|
if (parts[p] === '>') { break; }
|
|
buf += parts[p];
|
|
}
|
|
if (end <= p) { return void 0; }
|
|
var attrs = [];
|
|
while (buf !== '') {
|
|
m = ATTR_RE.exec(buf);
|
|
if (!m) {
|
|
// No attribute found: skip garbage
|
|
buf = buf.replace(/^[\s\S][^a-z\s]*/, '');
|
|
|
|
} else if ((m[4] && !m[5]) || (m[6] && !m[7])) {
|
|
// Unterminated quote: slurp to the next unquoted '>'
|
|
var quote = m[4] || m[6];
|
|
var sawQuote = false;
|
|
var abuf = [buf, parts[p++]];
|
|
for (; p < end; p++) {
|
|
if (sawQuote) {
|
|
if (parts[p] === '>') { break; }
|
|
} else if (0 <= parts[p].indexOf(quote)) {
|
|
sawQuote = true;
|
|
}
|
|
abuf.push(parts[p]);
|
|
}
|
|
// Slurp failed: lose the garbage
|
|
if (end <= p) { break; }
|
|
// Otherwise retry attribute parsing
|
|
buf = abuf.join('');
|
|
continue;
|
|
|
|
} else {
|
|
// We have an attribute
|
|
var aName = m[1].toLowerCase();
|
|
var aValue = m[2] ? decodeValue(m[3]) : '';
|
|
attrs.push(aName, aValue);
|
|
buf = buf.substr(m[0].length);
|
|
}
|
|
}
|
|
tag.attrs = attrs;
|
|
tag.next = p + 1;
|
|
return tag;
|
|
}
|
|
|
|
function decodeValue(v) {
|
|
var q = v.charCodeAt(0);
|
|
if (q === 0x22 || q === 0x27) { // " or '
|
|
v = v.substr(1, v.length - 2);
|
|
}
|
|
return unescapeEntities(stripNULs(v));
|
|
}
|
|
|
|
/**
|
|
* Returns a function that strips unsafe tags and attributes from html.
|
|
* @param {function(string, Array.<string>): ?Array.<string>} tagPolicy
|
|
* A function that takes (tagName, attribs[]), where tagName is a key in
|
|
* html4.ELEMENTS and attribs is an array of alternating attribute names
|
|
* and values. It should return a record (as follows), or null to delete
|
|
* the element. It's okay for tagPolicy to modify the attribs array,
|
|
* but the same array is reused, so it should not be held between calls.
|
|
* Record keys:
|
|
* attribs: (required) Sanitized attributes array.
|
|
* tagName: Replacement tag name.
|
|
* @return {function(string, Array)} A function that sanitizes a string of
|
|
* HTML and appends result strings to the second argument, an array.
|
|
*/
|
|
function makeHtmlSanitizer(tagPolicy) {
|
|
var stack;
|
|
var ignoring;
|
|
var emit = function (text, out) {
|
|
if (!ignoring) { out.push(text); }
|
|
};
|
|
return makeSaxParser({
|
|
'startDoc': function(_) {
|
|
stack = [];
|
|
ignoring = false;
|
|
},
|
|
'startTag': function(tagNameOrig, attribs, out) {
|
|
if (ignoring) { return; }
|
|
if (!html4.ELEMENTS.hasOwnProperty(tagNameOrig)) { return; }
|
|
var eflagsOrig = html4.ELEMENTS[tagNameOrig];
|
|
if (eflagsOrig & html4.eflags['FOLDABLE']) {
|
|
return;
|
|
}
|
|
|
|
var decision = tagPolicy(tagNameOrig, attribs);
|
|
if (!decision) {
|
|
ignoring = !(eflagsOrig & html4.eflags['EMPTY']);
|
|
return;
|
|
} else if (typeof decision !== 'object') {
|
|
throw new Error('tagPolicy did not return object (old API?)');
|
|
}
|
|
if ('attribs' in decision) {
|
|
attribs = decision['attribs'];
|
|
} else {
|
|
throw new Error('tagPolicy gave no attribs');
|
|
}
|
|
var eflagsRep;
|
|
var tagNameRep;
|
|
if ('tagName' in decision) {
|
|
tagNameRep = decision['tagName'];
|
|
eflagsRep = html4.ELEMENTS[tagNameRep];
|
|
} else {
|
|
tagNameRep = tagNameOrig;
|
|
eflagsRep = eflagsOrig;
|
|
}
|
|
// TODO(mikesamuel): relying on tagPolicy not to insert unsafe
|
|
// attribute names.
|
|
|
|
// If this is an optional-end-tag element and either this element or its
|
|
// previous like sibling was rewritten, then insert a close tag to
|
|
// preserve structure.
|
|
if (eflagsOrig & html4.eflags['OPTIONAL_ENDTAG']) {
|
|
var onStack = stack[stack.length - 1];
|
|
if (onStack && onStack.orig === tagNameOrig &&
|
|
(onStack.rep !== tagNameRep || tagNameOrig !== tagNameRep)) {
|
|
out.push('<\/', onStack.rep, '>');
|
|
}
|
|
}
|
|
|
|
if (!(eflagsOrig & html4.eflags['EMPTY'])) {
|
|
stack.push({orig: tagNameOrig, rep: tagNameRep});
|
|
}
|
|
|
|
out.push('<', tagNameRep);
|
|
for (var i = 0, n = attribs.length; i < n; i += 2) {
|
|
var attribName = attribs[i],
|
|
value = attribs[i + 1];
|
|
if (value !== null && value !== void 0) {
|
|
out.push(' ', attribName, '="', escapeAttrib(value), '"');
|
|
}
|
|
}
|
|
out.push('>');
|
|
|
|
if ((eflagsOrig & html4.eflags['EMPTY'])
|
|
&& !(eflagsRep & html4.eflags['EMPTY'])) {
|
|
// replacement is non-empty, synthesize end tag
|
|
out.push('<\/', tagNameRep, '>');
|
|
}
|
|
},
|
|
'endTag': function(tagName, out) {
|
|
if (ignoring) {
|
|
ignoring = false;
|
|
return;
|
|
}
|
|
if (!html4.ELEMENTS.hasOwnProperty(tagName)) { return; }
|
|
var eflags = html4.ELEMENTS[tagName];
|
|
if (!(eflags & (html4.eflags['EMPTY'] | html4.eflags['FOLDABLE']))) {
|
|
var index;
|
|
if (eflags & html4.eflags['OPTIONAL_ENDTAG']) {
|
|
for (index = stack.length; --index >= 0;) {
|
|
var stackElOrigTag = stack[index].orig;
|
|
if (stackElOrigTag === tagName) { break; }
|
|
if (!(html4.ELEMENTS[stackElOrigTag] &
|
|
html4.eflags['OPTIONAL_ENDTAG'])) {
|
|
// Don't pop non optional end tags looking for a match.
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
for (index = stack.length; --index >= 0;) {
|
|
if (stack[index].orig === tagName) { break; }
|
|
}
|
|
}
|
|
if (index < 0) { return; } // Not opened.
|
|
for (var i = stack.length; --i > index;) {
|
|
var stackElRepTag = stack[i].rep;
|
|
if (!(html4.ELEMENTS[stackElRepTag] &
|
|
html4.eflags['OPTIONAL_ENDTAG'])) {
|
|
out.push('<\/', stackElRepTag, '>');
|
|
}
|
|
}
|
|
if (index < stack.length) {
|
|
tagName = stack[index].rep;
|
|
}
|
|
stack.length = index;
|
|
out.push('<\/', tagName, '>');
|
|
}
|
|
},
|
|
'pcdata': emit,
|
|
'rcdata': emit,
|
|
'cdata': emit,
|
|
'endDoc': function(out) {
|
|
for (; stack.length; stack.length--) {
|
|
out.push('<\/', stack[stack.length - 1].rep, '>');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
var ALLOWED_URI_SCHEMES = /^(?:https?|mailto|data)$/i;
|
|
|
|
function safeUri(uri, effect, ltype, hints, naiveUriRewriter) {
|
|
if (!naiveUriRewriter) { return null; }
|
|
try {
|
|
var parsed = URI.parse('' + uri);
|
|
if (parsed) {
|
|
if (!parsed.hasScheme() ||
|
|
ALLOWED_URI_SCHEMES.test(parsed.getScheme())) {
|
|
var safe = naiveUriRewriter(parsed, effect, ltype, hints);
|
|
return safe ? safe.toString() : null;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function log(logger, tagName, attribName, oldValue, newValue) {
|
|
if (!attribName) {
|
|
logger(tagName + " removed", {
|
|
change: "removed",
|
|
tagName: tagName
|
|
});
|
|
}
|
|
if (oldValue !== newValue) {
|
|
var changed = "changed";
|
|
if (oldValue && !newValue) {
|
|
changed = "removed";
|
|
} else if (!oldValue && newValue) {
|
|
changed = "added";
|
|
}
|
|
logger(tagName + "." + attribName + " " + changed, {
|
|
change: changed,
|
|
tagName: tagName,
|
|
attribName: attribName,
|
|
oldValue: oldValue,
|
|
newValue: newValue
|
|
});
|
|
}
|
|
}
|
|
|
|
function lookupAttribute(map, tagName, attribName) {
|
|
var attribKey;
|
|
attribKey = tagName + '::' + attribName;
|
|
if (map.hasOwnProperty(attribKey)) {
|
|
return map[attribKey];
|
|
}
|
|
attribKey = '*::' + attribName;
|
|
if (map.hasOwnProperty(attribKey)) {
|
|
return map[attribKey];
|
|
}
|
|
return void 0;
|
|
}
|
|
function getAttributeType(tagName, attribName) {
|
|
return lookupAttribute(html4.ATTRIBS, tagName, attribName);
|
|
}
|
|
function getLoaderType(tagName, attribName) {
|
|
return lookupAttribute(html4.LOADERTYPES, tagName, attribName);
|
|
}
|
|
function getUriEffect(tagName, attribName) {
|
|
return lookupAttribute(html4.URIEFFECTS, tagName, attribName);
|
|
}
|
|
|
|
/**
|
|
* Sanitizes attributes on an HTML tag.
|
|
* @param {string} tagName An HTML tag name in lowercase.
|
|
* @param {Array.<?string>} attribs An array of alternating names and values.
|
|
* @param {?function(?string): ?string} opt_naiveUriRewriter A transform to
|
|
* apply to URI attributes; it can return a new string value, or null to
|
|
* delete the attribute. If unspecified, URI attributes are deleted.
|
|
* @param {function(?string): ?string} opt_nmTokenPolicy A transform to apply
|
|
* to attributes containing HTML names, element IDs, and space-separated
|
|
* lists of classes; it can return a new string value, or null to delete
|
|
* the attribute. If unspecified, these attributes are kept unchanged.
|
|
* @return {Array.<?string>} The sanitized attributes as a list of alternating
|
|
* names and values, where a null value means to omit the attribute.
|
|
*/
|
|
function sanitizeAttribs(tagName, attribs,
|
|
opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) {
|
|
// TODO(felix8a): it's obnoxious that domado duplicates much of this
|
|
// TODO(felix8a): maybe consistently enforce constraints like target=
|
|
for (var i = 0; i < attribs.length; i += 2) {
|
|
var attribName = attribs[i];
|
|
var value = attribs[i + 1];
|
|
var oldValue = value;
|
|
var atype = null, attribKey;
|
|
if ((attribKey = tagName + '::' + attribName,
|
|
html4.ATTRIBS.hasOwnProperty(attribKey)) ||
|
|
(attribKey = '*::' + attribName,
|
|
html4.ATTRIBS.hasOwnProperty(attribKey))) {
|
|
atype = html4.ATTRIBS[attribKey];
|
|
}
|
|
if (atype !== null) {
|
|
switch (atype) {
|
|
case html4.atype['NONE']: break;
|
|
case html4.atype['SCRIPT']:
|
|
value = null;
|
|
if (opt_logger) {
|
|
log(opt_logger, tagName, attribName, oldValue, value);
|
|
}
|
|
break;
|
|
case html4.atype['STYLE']:
|
|
if ('undefined' === typeof parseCssDeclarations) {
|
|
value = null;
|
|
if (opt_logger) {
|
|
log(opt_logger, tagName, attribName, oldValue, value);
|
|
}
|
|
break;
|
|
}
|
|
var sanitizedDeclarations = [];
|
|
parseCssDeclarations(
|
|
value,
|
|
{
|
|
declaration: function (property, tokens) {
|
|
var normProp = property.toLowerCase();
|
|
var schema = cssSchema[normProp];
|
|
if (!schema) {
|
|
return;
|
|
}
|
|
sanitizeCssProperty(
|
|
normProp, schema, tokens,
|
|
opt_naiveUriRewriter
|
|
? function (url) {
|
|
return safeUri(
|
|
url, html4.ueffects.SAME_DOCUMENT,
|
|
html4.ltypes.SANDBOXED,
|
|
{
|
|
"TYPE": "CSS",
|
|
"CSS_PROP": normProp
|
|
}, opt_naiveUriRewriter);
|
|
}
|
|
: null);
|
|
sanitizedDeclarations.push(property + ': ' + tokens.join(' '));
|
|
}
|
|
});
|
|
value = sanitizedDeclarations.length > 0 ?
|
|
sanitizedDeclarations.join(' ; ') : null;
|
|
if (opt_logger) {
|
|
log(opt_logger, tagName, attribName, oldValue, value);
|
|
}
|
|
break;
|
|
case html4.atype['ID']:
|
|
case html4.atype['IDREF']:
|
|
case html4.atype['IDREFS']:
|
|
case html4.atype['GLOBAL_NAME']:
|
|
case html4.atype['LOCAL_NAME']:
|
|
case html4.atype['CLASSES']:
|
|
value = opt_nmTokenPolicy ? opt_nmTokenPolicy(value) : value;
|
|
if (opt_logger) {
|
|
log(opt_logger, tagName, attribName, oldValue, value);
|
|
}
|
|
break;
|
|
case html4.atype['URI']:
|
|
value = safeUri(value,
|
|
getUriEffect(tagName, attribName),
|
|
getLoaderType(tagName, attribName),
|
|
{
|
|
"TYPE": "MARKUP",
|
|
"XML_ATTR": attribName,
|
|
"XML_TAG": tagName
|
|
}, opt_naiveUriRewriter);
|
|
if (opt_logger) {
|
|
log(opt_logger, tagName, attribName, oldValue, value);
|
|
}
|
|
break;
|
|
case html4.atype['URI_FRAGMENT']:
|
|
if (value && '#' === value.charAt(0)) {
|
|
value = value.substring(1); // remove the leading '#'
|
|
value = opt_nmTokenPolicy ? opt_nmTokenPolicy(value) : value;
|
|
if (value !== null && value !== void 0) {
|
|
value = '#' + value; // restore the leading '#'
|
|
}
|
|
} else {
|
|
value = null;
|
|
}
|
|
if (opt_logger) {
|
|
log(opt_logger, tagName, attribName, oldValue, value);
|
|
}
|
|
break;
|
|
default:
|
|
value = null;
|
|
if (opt_logger) {
|
|
log(opt_logger, tagName, attribName, oldValue, value);
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
value = null;
|
|
if (opt_logger) {
|
|
log(opt_logger, tagName, attribName, oldValue, value);
|
|
}
|
|
}
|
|
attribs[i + 1] = value;
|
|
}
|
|
return attribs;
|
|
}
|
|
|
|
/**
|
|
* Creates a tag policy that omits all tags marked UNSAFE in html4-defs.js
|
|
* and applies the default attribute sanitizer with the supplied policy for
|
|
* URI attributes and NMTOKEN attributes.
|
|
* @param {?function(?string): ?string} opt_naiveUriRewriter A transform to
|
|
* apply to URI attributes. If not given, URI attributes are deleted.
|
|
* @param {function(?string): ?string} opt_nmTokenPolicy A transform to apply
|
|
* to attributes containing HTML names, element IDs, and space-separated
|
|
* lists of classes. If not given, such attributes are left unchanged.
|
|
* @return {function(string, Array.<?string>)} A tagPolicy suitable for
|
|
* passing to html.sanitize.
|
|
*/
|
|
function makeTagPolicy(
|
|
opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) {
|
|
return function(tagName, attribs) {
|
|
if (!(html4.ELEMENTS[tagName] & html4.eflags['UNSAFE'])) {
|
|
return {
|
|
'attribs': sanitizeAttribs(tagName, attribs,
|
|
opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger)
|
|
};
|
|
} else {
|
|
if (opt_logger) {
|
|
log(opt_logger, tagName, undefined, undefined, undefined);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Sanitizes HTML tags and attributes according to a given policy.
|
|
* @param {string} inputHtml The HTML to sanitize.
|
|
* @param {function(string, Array.<?string>)} tagPolicy A function that
|
|
* decides which tags to accept and sanitizes their attributes (see
|
|
* makeHtmlSanitizer above for details).
|
|
* @return {string} The sanitized HTML.
|
|
*/
|
|
function sanitizeWithPolicy(inputHtml, tagPolicy) {
|
|
var outputArray = [];
|
|
makeHtmlSanitizer(tagPolicy)(inputHtml, outputArray);
|
|
return outputArray.join('');
|
|
}
|
|
|
|
/**
|
|
* Strips unsafe tags and attributes from HTML.
|
|
* @param {string} inputHtml The HTML to sanitize.
|
|
* @param {?function(?string): ?string} opt_naiveUriRewriter A transform to
|
|
* apply to URI attributes. If not given, URI attributes are deleted.
|
|
* @param {function(?string): ?string} opt_nmTokenPolicy A transform to apply
|
|
* to attributes containing HTML names, element IDs, and space-separated
|
|
* lists of classes. If not given, such attributes are left unchanged.
|
|
*/
|
|
function sanitize(inputHtml,
|
|
opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) {
|
|
var tagPolicy = makeTagPolicy(
|
|
opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger);
|
|
return sanitizeWithPolicy(inputHtml, tagPolicy);
|
|
}
|
|
|
|
// Export both quoted and unquoted names for Closure linkage.
|
|
var html = {};
|
|
html.escapeAttrib = html['escapeAttrib'] = escapeAttrib;
|
|
html.makeHtmlSanitizer = html['makeHtmlSanitizer'] = makeHtmlSanitizer;
|
|
html.makeSaxParser = html['makeSaxParser'] = makeSaxParser;
|
|
html.makeTagPolicy = html['makeTagPolicy'] = makeTagPolicy;
|
|
html.normalizeRCData = html['normalizeRCData'] = normalizeRCData;
|
|
html.sanitize = html['sanitize'] = sanitize;
|
|
html.sanitizeAttribs = html['sanitizeAttribs'] = sanitizeAttribs;
|
|
html.sanitizeWithPolicy = html['sanitizeWithPolicy'] = sanitizeWithPolicy;
|
|
html.unescapeEntities = html['unescapeEntities'] = unescapeEntities;
|
|
return html;
|
|
})(html4);
|
|
|
|
var html_sanitize = html['sanitize'];
|
|
|
|
// Loosen restrictions of Caja's
|
|
// html-sanitizer to allow for styling
|
|
html4.ATTRIBS['*::style'] = 0;
|
|
html4.ELEMENTS['style'] = 0;
|
|
html4.ATTRIBS['a::target'] = 0;
|
|
html4.ELEMENTS['video'] = 0;
|
|
html4.ATTRIBS['video::src'] = 0;
|
|
html4.ATTRIBS['video::poster'] = 0;
|
|
html4.ATTRIBS['video::controls'] = 0;
|
|
html4.ELEMENTS['audio'] = 0;
|
|
html4.ATTRIBS['audio::src'] = 0;
|
|
html4.ATTRIBS['video::autoplay'] = 0;
|
|
html4.ATTRIBS['video::controls'] = 0;
|
|
|
|
if (typeof module !== 'undefined') {
|
|
module.exports = html_sanitize;
|
|
}
|
|
|
|
},{}],7:[function(require,module,exports){
|
|
module.exports={
|
|
"author": "Mapbox",
|
|
"name": "mapbox.js",
|
|
"description": "mapbox javascript api",
|
|
"version": "3.1.1",
|
|
"homepage": "http://mapbox.com/",
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "git://github.com/mapbox/mapbox.js.git"
|
|
},
|
|
"main": "src/index.js",
|
|
"dependencies": {
|
|
"corslite": "0.0.6",
|
|
"isarray": "0.0.1",
|
|
"leaflet": "1.0.2",
|
|
"mustache": "2.2.1",
|
|
"sanitize-caja": "0.1.4"
|
|
},
|
|
"scripts": {
|
|
"test": "eslint --no-eslintrc -c .eslintrc src && phantomjs node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js test/index.html"
|
|
},
|
|
"license": "BSD-3-Clause",
|
|
"devDependencies": {
|
|
"browserify": "^13.0.0",
|
|
"clean-css": "~2.0.7",
|
|
"cz-conventional-changelog": "1.2.0",
|
|
"eslint": "^0.23.0",
|
|
"expect.js": "0.3.1",
|
|
"happen": "0.1.3",
|
|
"leaflet-fullscreen": "0.0.4",
|
|
"leaflet-hash": "0.2.1",
|
|
"marked": "~0.3.0",
|
|
"minifyify": "^6.1.0",
|
|
"minimist": "0.0.5",
|
|
"mocha": "2.4.5",
|
|
"mocha-phantomjs-core": "2.0.1",
|
|
"phantomjs-prebuilt": "2.1.12",
|
|
"sinon": "1.10.2"
|
|
},
|
|
"optionalDependencies": {},
|
|
"engines": {
|
|
"node": "*"
|
|
},
|
|
"config": {
|
|
"commitizen": {
|
|
"path": "./node_modules/cz-conventional-changelog"
|
|
}
|
|
}
|
|
}
|
|
|
|
},{}],8:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
module.exports = {
|
|
HTTP_URL: 'http://a.tiles.mapbox.com/v4',
|
|
HTTPS_URL: 'https://a.tiles.mapbox.com/v4',
|
|
FORCE_HTTPS: false,
|
|
REQUIRE_ACCESS_TOKEN: true
|
|
};
|
|
|
|
},{}],9:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var util = require('./util'),
|
|
format_url = require('./format_url'),
|
|
request = require('./request'),
|
|
marker = require('./marker'),
|
|
simplestyle = require('./simplestyle');
|
|
|
|
// # featureLayer
|
|
//
|
|
// A layer of features, loaded from Mapbox or else. Adds the ability
|
|
// to reset features, filter them, and load them from a GeoJSON URL.
|
|
var FeatureLayer = L.FeatureGroup.extend({
|
|
options: {
|
|
filter: function() { return true; },
|
|
sanitizer: require('sanitize-caja'),
|
|
style: simplestyle.style,
|
|
popupOptions: { closeButton: false }
|
|
},
|
|
|
|
initialize: function(_, options) {
|
|
L.setOptions(this, options);
|
|
|
|
this._layers = {};
|
|
|
|
if (typeof _ === 'string') {
|
|
util.idUrl(_, this);
|
|
// javascript object of TileJSON data
|
|
} else if (_ && typeof _ === 'object') {
|
|
this.setGeoJSON(_);
|
|
}
|
|
},
|
|
|
|
setGeoJSON: function(_) {
|
|
this._geojson = _;
|
|
this.clearLayers();
|
|
this._initialize(_);
|
|
return this;
|
|
},
|
|
|
|
getGeoJSON: function() {
|
|
return this._geojson;
|
|
},
|
|
|
|
loadURL: function(url) {
|
|
if (this._request && 'abort' in this._request) this._request.abort();
|
|
this._request = request(url, L.bind(function(err, json) {
|
|
this._request = null;
|
|
if (err && err.type !== 'abort') {
|
|
util.log('could not load features at ' + url);
|
|
this.fire('error', {error: err});
|
|
} else if (json) {
|
|
this.setGeoJSON(json);
|
|
this.fire('ready');
|
|
}
|
|
}, this));
|
|
return this;
|
|
},
|
|
|
|
loadID: function(id) {
|
|
return this.loadURL(format_url('/v4/' + id + '/features.json', this.options.accessToken));
|
|
},
|
|
|
|
setFilter: function(_) {
|
|
this.options.filter = _;
|
|
if (this._geojson) {
|
|
this.clearLayers();
|
|
this._initialize(this._geojson);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
getFilter: function() {
|
|
return this.options.filter;
|
|
},
|
|
|
|
_initialize: function(json) {
|
|
var features = L.Util.isArray(json) ? json : json.features,
|
|
i, len;
|
|
|
|
if (features) {
|
|
for (i = 0, len = features.length; i < len; i++) {
|
|
// Only add this if geometry or geometries are set and not null
|
|
if (features[i].geometries || features[i].geometry || features[i].features) {
|
|
this._initialize(features[i]);
|
|
}
|
|
}
|
|
} else if (this.options.filter(json)) {
|
|
|
|
var opts = {accessToken: this.options.accessToken},
|
|
pointToLayer = this.options.pointToLayer || function(feature, latlon) {
|
|
return marker.style(feature, latlon, opts);
|
|
},
|
|
layer = L.GeoJSON.geometryToLayer(json, {
|
|
pointToLayer: pointToLayer
|
|
}),
|
|
popupHtml = marker.createPopup(json, this.options.sanitizer),
|
|
style = this.options.style,
|
|
defaultStyle = style === simplestyle.style;
|
|
|
|
if (style && 'setStyle' in layer &&
|
|
// if the style method is the simplestyle default, then
|
|
// never style L.Circle or L.CircleMarker because
|
|
// simplestyle has no rules over them, only over geometry
|
|
// primitives directly from GeoJSON
|
|
(!(defaultStyle && (layer instanceof L.Circle ||
|
|
layer instanceof L.CircleMarker)))) {
|
|
if (typeof style === 'function') {
|
|
style = style(json);
|
|
}
|
|
layer.setStyle(style);
|
|
}
|
|
|
|
layer.feature = json;
|
|
|
|
if (popupHtml) {
|
|
layer.bindPopup(popupHtml, this.options.popupOptions);
|
|
}
|
|
|
|
this.addLayer(layer);
|
|
}
|
|
}
|
|
});
|
|
|
|
module.exports.FeatureLayer = FeatureLayer;
|
|
|
|
module.exports.featureLayer = function(_, options) {
|
|
return new FeatureLayer(_, options);
|
|
};
|
|
|
|
},{"./format_url":11,"./marker":24,"./request":25,"./simplestyle":27,"./util":30,"sanitize-caja":5}],10:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var Feedback = L.Class.extend({
|
|
includes: L.Mixin.Events,
|
|
data: {},
|
|
record: function(data) {
|
|
L.extend(this.data, data);
|
|
this.fire('change');
|
|
}
|
|
});
|
|
|
|
module.exports = new Feedback();
|
|
|
|
},{}],11:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var config = require('./config'),
|
|
version = require('../package.json').version;
|
|
|
|
module.exports = function(path, accessToken) {
|
|
accessToken = accessToken || L.mapbox.accessToken;
|
|
|
|
if (!accessToken && config.REQUIRE_ACCESS_TOKEN) {
|
|
throw new Error('An API access token is required to use Mapbox.js. ' +
|
|
'See https://www.mapbox.com/mapbox.js/api/v' + version + '/api-access-tokens/');
|
|
}
|
|
|
|
var url = (document.location.protocol === 'https:' || config.FORCE_HTTPS) ? config.HTTPS_URL : config.HTTP_URL;
|
|
url = url.replace(/\/v4$/, '');
|
|
url += path;
|
|
|
|
if (config.REQUIRE_ACCESS_TOKEN) {
|
|
if (accessToken[0] === 's') {
|
|
throw new Error('Use a public access token (pk.*) with Mapbox.js, not a secret access token (sk.*). ' +
|
|
'See https://www.mapbox.com/mapbox.js/api/v' + version + '/api-access-tokens/');
|
|
}
|
|
|
|
url += url.indexOf('?') !== -1 ? '&access_token=' : '?access_token=';
|
|
url += accessToken;
|
|
}
|
|
|
|
return url;
|
|
};
|
|
|
|
module.exports.tileJSON = function(urlOrMapID, accessToken) {
|
|
|
|
if (urlOrMapID.indexOf('mapbox://styles') === 0) {
|
|
throw new Error('Styles created with Mapbox Studio need to be used with ' +
|
|
'L.mapbox.styleLayer, not L.mapbox.tileLayer');
|
|
}
|
|
|
|
if (urlOrMapID.indexOf('/') !== -1)
|
|
return urlOrMapID;
|
|
|
|
var url = module.exports('/v4/' + urlOrMapID + '.json', accessToken);
|
|
|
|
// TileJSON requests need a secure flag appended to their URLs so
|
|
// that the server knows to send SSL-ified resource references.
|
|
if (url.indexOf('https') === 0)
|
|
url += '&secure';
|
|
|
|
return url;
|
|
};
|
|
|
|
|
|
module.exports.style = function(styleURL, accessToken) {
|
|
if (styleURL.indexOf('mapbox://styles/') === -1) throw new Error('Incorrectly formatted Mapbox style at ' + styleURL);
|
|
|
|
var ownerIDStyle = styleURL.split('mapbox://styles/')[1];
|
|
var url = module.exports('/styles/v1/' + ownerIDStyle, accessToken)
|
|
.replace('http://', 'https://');
|
|
|
|
return url;
|
|
};
|
|
|
|
},{"../package.json":7,"./config":8}],12:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var isArray = require('isarray'),
|
|
util = require('./util'),
|
|
format_url = require('./format_url'),
|
|
feedback = require('./feedback'),
|
|
request = require('./request');
|
|
|
|
// Low-level geocoding interface - wraps specific API calls and their
|
|
// return values.
|
|
module.exports = function(url, options) {
|
|
if (!options) options = {};
|
|
var geocoder = {};
|
|
|
|
util.strict(url, 'string');
|
|
|
|
if (url.indexOf('/') === -1) {
|
|
url = format_url('/geocoding/v5/' + url + '/{query}.json', options.accessToken, 5);
|
|
}
|
|
|
|
function roundTo(latLng, precision) {
|
|
var mult = Math.pow(10, precision);
|
|
latLng.lat = Math.round(latLng.lat * mult) / mult;
|
|
latLng.lng = Math.round(latLng.lng * mult) / mult;
|
|
return latLng;
|
|
}
|
|
|
|
geocoder.getURL = function() {
|
|
return url;
|
|
};
|
|
|
|
geocoder.queryURL = function(_) {
|
|
var isObject = !(isArray(_) || typeof _ === 'string'),
|
|
query = isObject ? _.query : _;
|
|
|
|
if (isArray(query)) {
|
|
var parts = [];
|
|
for (var i = 0; i < query.length; i++) {
|
|
parts[i] = encodeURIComponent(query[i]);
|
|
}
|
|
query = parts.join(';');
|
|
} else {
|
|
query = encodeURIComponent(query);
|
|
}
|
|
|
|
feedback.record({ geocoding: query });
|
|
|
|
var url = L.Util.template(geocoder.getURL(), {query: query});
|
|
|
|
if (isObject) {
|
|
if (_.types) {
|
|
if (isArray(_.types)) {
|
|
url += '&types=' + _.types.join();
|
|
} else {
|
|
url += '&types=' + _.types;
|
|
}
|
|
}
|
|
|
|
if (_.country) {
|
|
if (isArray(_.country)) {
|
|
url += '&country=' + _.country.join();
|
|
} else {
|
|
url += '&country=' + _.country;
|
|
}
|
|
}
|
|
|
|
if (_.bbox) {
|
|
if (isArray(_.bbox)) {
|
|
url += '&bbox=' + _.bbox.join();
|
|
} else {
|
|
url += '&bbox=' + _.bbox;
|
|
}
|
|
}
|
|
|
|
if (_.proximity) {
|
|
var proximity = roundTo(L.latLng(_.proximity), 3);
|
|
url += '&proximity=' + proximity.lng + ',' + proximity.lat;
|
|
}
|
|
|
|
if (typeof _.autocomplete === 'boolean') {
|
|
url += '&autocomplete=' + _.autocomplete;
|
|
}
|
|
}
|
|
|
|
return url;
|
|
};
|
|
|
|
geocoder.query = function(_, callback) {
|
|
util.strict(callback, 'function');
|
|
|
|
request(geocoder.queryURL(_), function(err, json) {
|
|
if (json && (json.length || json.features)) {
|
|
var res = {
|
|
results: json
|
|
};
|
|
if (json.features && json.features.length) {
|
|
res.latlng = [
|
|
json.features[0].center[1],
|
|
json.features[0].center[0]];
|
|
|
|
if (json.features[0].bbox) {
|
|
res.bounds = json.features[0].bbox;
|
|
res.lbounds = util.lbounds(res.bounds);
|
|
}
|
|
}
|
|
callback(null, res);
|
|
} else callback(err || true);
|
|
});
|
|
|
|
return geocoder;
|
|
};
|
|
|
|
// a reverse geocode:
|
|
//
|
|
// geocoder.reverseQuery([80, 20])
|
|
geocoder.reverseQuery = function(_, callback) {
|
|
var q = '';
|
|
|
|
// sort through different ways people represent lat and lon pairs
|
|
function normalize(x) {
|
|
var latLng;
|
|
if (x.lat !== undefined && x.lng !== undefined) {
|
|
latLng = L.latLng(x.lat, x.lng);
|
|
} else if (x.lat !== undefined && x.lon !== undefined) {
|
|
latLng = L.latLng(x.lat, x.lon);
|
|
} else {
|
|
latLng = L.latLng(x[1], x[0]);
|
|
}
|
|
latLng = roundTo(latLng, 5);
|
|
return latLng.lng + ',' + latLng.lat;
|
|
}
|
|
|
|
if (_.length && _[0].length) {
|
|
for (var i = 0, pts = []; i < _.length; i++) {
|
|
pts.push(normalize(_[i]));
|
|
}
|
|
q = pts.join(';');
|
|
} else {
|
|
q = normalize(_);
|
|
}
|
|
|
|
request(geocoder.queryURL(q), function(err, json) {
|
|
callback(err, json);
|
|
});
|
|
|
|
return geocoder;
|
|
};
|
|
|
|
return geocoder;
|
|
};
|
|
|
|
},{"./feedback":10,"./format_url":11,"./request":25,"./util":30,"isarray":2}],13:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var geocoder = require('./geocoder'),
|
|
util = require('./util');
|
|
|
|
var GeocoderControl = L.Control.extend({
|
|
includes: L.Mixin.Events,
|
|
|
|
options: {
|
|
proximity: true,
|
|
position: 'topleft',
|
|
pointZoom: 16,
|
|
keepOpen: false,
|
|
autocomplete: false,
|
|
queryOptions: {}
|
|
},
|
|
|
|
initialize: function(_, options) {
|
|
L.Util.setOptions(this, options);
|
|
this.setURL(_);
|
|
this._updateSubmit = L.bind(this._updateSubmit, this);
|
|
this._updateAutocomplete = L.bind(this._updateAutocomplete, this);
|
|
this._chooseResult = L.bind(this._chooseResult, this);
|
|
},
|
|
|
|
setURL: function(_) {
|
|
this.geocoder = geocoder(_, {
|
|
accessToken: this.options.accessToken
|
|
});
|
|
return this;
|
|
},
|
|
|
|
getURL: function() {
|
|
return this.geocoder.getURL();
|
|
},
|
|
|
|
setID: function(_) {
|
|
return this.setURL(_);
|
|
},
|
|
|
|
setTileJSON: function(_) {
|
|
return this.setURL(_.geocoder);
|
|
},
|
|
|
|
_toggle: function(e) {
|
|
if (e) L.DomEvent.stop(e);
|
|
if (L.DomUtil.hasClass(this._container, 'active')) {
|
|
L.DomUtil.removeClass(this._container, 'active');
|
|
this._results.innerHTML = '';
|
|
this._input.blur();
|
|
} else {
|
|
L.DomUtil.addClass(this._container, 'active');
|
|
this._input.focus();
|
|
this._input.select();
|
|
}
|
|
},
|
|
|
|
_closeIfOpen: function() {
|
|
if (L.DomUtil.hasClass(this._container, 'active') &&
|
|
!this.options.keepOpen) {
|
|
L.DomUtil.removeClass(this._container, 'active');
|
|
this._results.innerHTML = '';
|
|
this._input.blur();
|
|
}
|
|
},
|
|
|
|
onAdd: function(map) {
|
|
|
|
var container = L.DomUtil.create('div', 'leaflet-control-mapbox-geocoder leaflet-bar leaflet-control'),
|
|
link = L.DomUtil.create('a', 'leaflet-control-mapbox-geocoder-toggle mapbox-icon mapbox-icon-geocoder', container),
|
|
results = L.DomUtil.create('div', 'leaflet-control-mapbox-geocoder-results', container),
|
|
wrap = L.DomUtil.create('div', 'leaflet-control-mapbox-geocoder-wrap', container),
|
|
form = L.DomUtil.create('form', 'leaflet-control-mapbox-geocoder-form', wrap),
|
|
input = L.DomUtil.create('input', '', form);
|
|
|
|
link.href = '#';
|
|
link.innerHTML = ' ';
|
|
|
|
input.type = 'text';
|
|
input.setAttribute('placeholder', 'Search');
|
|
|
|
L.DomEvent.addListener(form, 'submit', this._geocode, this);
|
|
L.DomEvent.addListener(input, 'keyup', this._autocomplete, this);
|
|
L.DomEvent.disableClickPropagation(container);
|
|
|
|
this._map = map;
|
|
this._results = results;
|
|
this._input = input;
|
|
this._form = form;
|
|
|
|
if (this.options.keepOpen) {
|
|
L.DomUtil.addClass(container, 'active');
|
|
} else {
|
|
this._map.on('click', this._closeIfOpen, this);
|
|
L.DomEvent.addListener(link, 'click', this._toggle, this);
|
|
}
|
|
|
|
return container;
|
|
},
|
|
|
|
_updateSubmit: function(err, resp) {
|
|
L.DomUtil.removeClass(this._container, 'searching');
|
|
this._results.innerHTML = '';
|
|
if (err || !resp) {
|
|
this.fire('error', {error: err});
|
|
} else {
|
|
var features = [];
|
|
if (resp.results && resp.results.features) {
|
|
features = resp.results.features;
|
|
}
|
|
if (features.length === 1) {
|
|
this.fire('autoselect', { feature: features[0] });
|
|
this.fire('found', {results: resp.results});
|
|
this._chooseResult(features[0]);
|
|
this._closeIfOpen();
|
|
} else if (features.length > 1) {
|
|
this.fire('found', {results: resp.results});
|
|
this._displayResults(features);
|
|
} else {
|
|
this.fire('notfound');
|
|
this._displayResults(features);
|
|
}
|
|
}
|
|
},
|
|
|
|
_updateAutocomplete: function(err, resp) {
|
|
this._results.innerHTML = '';
|
|
if (err || !resp) {
|
|
this.fire('error', {error: err});
|
|
} else {
|
|
var features = [];
|
|
if (resp.results && resp.results.features) {
|
|
features = resp.results.features;
|
|
}
|
|
if (features.length) {
|
|
this.fire('found', {results: resp.results});
|
|
} else {
|
|
this.fire('notfound');
|
|
}
|
|
this._displayResults(features);
|
|
}
|
|
},
|
|
|
|
_displayResults: function(features) {
|
|
for (var i = 0, l = Math.min(features.length, 5); i < l; i++) {
|
|
var feature = features[i];
|
|
var name = feature.place_name;
|
|
if (!name.length) continue;
|
|
|
|
var r = L.DomUtil.create('a', '', this._results);
|
|
var text = ('innerText' in r) ? 'innerText' : 'textContent';
|
|
r[text] = name;
|
|
r.setAttribute('title', name);
|
|
r.href = '#';
|
|
|
|
(L.bind(function(feature) {
|
|
L.DomEvent.addListener(r, 'click', function(e) {
|
|
this._chooseResult(feature);
|
|
L.DomEvent.stop(e);
|
|
this.fire('select', { feature: feature });
|
|
}, this);
|
|
}, this))(feature);
|
|
}
|
|
if (features.length > 5) {
|
|
var outof = L.DomUtil.create('span', '', this._results);
|
|
outof.innerHTML = 'Top 5 of ' + features.length + ' results';
|
|
}
|
|
},
|
|
|
|
_chooseResult: function(result) {
|
|
if (result.bbox) {
|
|
this._map.fitBounds(util.lbounds(result.bbox));
|
|
} else if (result.center) {
|
|
this._map.setView([result.center[1], result.center[0]], (this._map.getZoom() === undefined) ?
|
|
this.options.pointZoom :
|
|
Math.max(this._map.getZoom(), this.options.pointZoom));
|
|
}
|
|
},
|
|
|
|
_geocode: function(e) {
|
|
L.DomEvent.preventDefault(e);
|
|
if (this._input.value === '') return this._updateSubmit();
|
|
L.DomUtil.addClass(this._container, 'searching');
|
|
this.geocoder.query(L.Util.extend({
|
|
query: this._input.value,
|
|
proximity: this.options.proximity ? this._map.getCenter() : false
|
|
}, this.options.queryOptions), this._updateSubmit);
|
|
},
|
|
|
|
_autocomplete: function() {
|
|
if (!this.options.autocomplete) return;
|
|
if (this._input.value === '') return this._updateAutocomplete();
|
|
this.geocoder.query(L.Util.extend({
|
|
query: this._input.value,
|
|
proximity: this.options.proximity ? this._map.getCenter() : false
|
|
}, this.options.queryOptions), this._updateAutocomplete);
|
|
}
|
|
});
|
|
|
|
module.exports.GeocoderControl = GeocoderControl;
|
|
|
|
module.exports.geocoderControl = function(_, options) {
|
|
return new GeocoderControl(_, options);
|
|
};
|
|
|
|
},{"./geocoder":12,"./util":30}],14:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
function utfDecode(c) {
|
|
if (c >= 93) c--;
|
|
if (c >= 35) c--;
|
|
return c - 32;
|
|
}
|
|
|
|
module.exports = function(data) {
|
|
return function(x, y) {
|
|
if (!data) return;
|
|
var idx = utfDecode(data.grid[y].charCodeAt(x)),
|
|
key = data.keys[idx];
|
|
return data.data[key];
|
|
};
|
|
};
|
|
|
|
},{}],15:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var util = require('./util'),
|
|
Mustache = require('mustache');
|
|
|
|
var GridControl = L.Control.extend({
|
|
|
|
options: {
|
|
pinnable: true,
|
|
follow: false,
|
|
sanitizer: require('sanitize-caja'),
|
|
touchTeaser: true,
|
|
location: true
|
|
},
|
|
|
|
_currentContent: '',
|
|
|
|
// pinned means that this control is on a feature and the user has likely
|
|
// clicked. pinned will not become false unless the user clicks off
|
|
// of the feature onto another or clicks x
|
|
_pinned: false,
|
|
|
|
initialize: function(_, options) {
|
|
L.Util.setOptions(this, options);
|
|
util.strict_instance(_, L.Class, 'L.mapbox.gridLayer');
|
|
this._layer = _;
|
|
},
|
|
|
|
setTemplate: function(template) {
|
|
util.strict(template, 'string');
|
|
this.options.template = template;
|
|
return this;
|
|
},
|
|
|
|
_template: function(format, data) {
|
|
if (!data) return;
|
|
var template = this.options.template || this._layer.getTileJSON().template;
|
|
if (template) {
|
|
var d = {};
|
|
d['__' + format + '__'] = true;
|
|
return this.options.sanitizer(
|
|
Mustache.to_html(template, L.extend(d, data)));
|
|
}
|
|
},
|
|
|
|
// change the content of the tooltip HTML if it has changed, otherwise
|
|
// noop
|
|
_show: function(content, o) {
|
|
if (content === this._currentContent) return;
|
|
|
|
this._currentContent = content;
|
|
|
|
if (this.options.follow) {
|
|
this._popup.setContent(content)
|
|
.setLatLng(o.latLng);
|
|
if (this._map._popup !== this._popup) this._popup.openOn(this._map);
|
|
} else {
|
|
this._container.style.display = 'block';
|
|
this._contentWrapper.innerHTML = content;
|
|
}
|
|
},
|
|
|
|
hide: function() {
|
|
this._pinned = false;
|
|
this._currentContent = '';
|
|
|
|
this._map.closePopup();
|
|
this._container.style.display = 'none';
|
|
this._contentWrapper.innerHTML = '';
|
|
|
|
L.DomUtil.removeClass(this._container, 'closable');
|
|
|
|
return this;
|
|
},
|
|
|
|
_mouseover: function(o) {
|
|
if (o.data) {
|
|
L.DomUtil.addClass(this._map._container, 'map-clickable');
|
|
} else {
|
|
L.DomUtil.removeClass(this._map._container, 'map-clickable');
|
|
}
|
|
|
|
if (this._pinned) return;
|
|
|
|
var content = this._template('teaser', o.data);
|
|
if (content) {
|
|
this._show(content, o);
|
|
} else {
|
|
this.hide();
|
|
}
|
|
},
|
|
|
|
_mousemove: function(o) {
|
|
if (this._pinned) return;
|
|
if (!this.options.follow) return;
|
|
|
|
this._popup.setLatLng(o.latLng);
|
|
},
|
|
|
|
_navigateTo: function(url) {
|
|
window.top.location.href = url;
|
|
},
|
|
|
|
_click: function(o) {
|
|
|
|
var location_formatted = this._template('location', o.data);
|
|
if (this.options.location && location_formatted &&
|
|
location_formatted.search(/^https?:/) === 0) {
|
|
return this._navigateTo(this._template('location', o.data));
|
|
}
|
|
|
|
if (!this.options.pinnable) return;
|
|
|
|
var content = this._template('full', o.data);
|
|
|
|
if (!content && this.options.touchTeaser && L.Browser.touch) {
|
|
content = this._template('teaser', o.data);
|
|
}
|
|
|
|
if (content) {
|
|
L.DomUtil.addClass(this._container, 'closable');
|
|
this._pinned = true;
|
|
this._show(content, o);
|
|
} else if (this._pinned) {
|
|
L.DomUtil.removeClass(this._container, 'closable');
|
|
this._pinned = false;
|
|
this.hide();
|
|
}
|
|
},
|
|
|
|
_onPopupClose: function() {
|
|
this._currentContent = null;
|
|
this._pinned = false;
|
|
},
|
|
|
|
_createClosebutton: function(container, fn) {
|
|
var link = L.DomUtil.create('a', 'close', container);
|
|
|
|
link.innerHTML = 'close';
|
|
link.href = '#';
|
|
link.title = 'close';
|
|
|
|
L.DomEvent
|
|
.on(link, 'click', L.DomEvent.stopPropagation)
|
|
.on(link, 'mousedown', L.DomEvent.stopPropagation)
|
|
.on(link, 'dblclick', L.DomEvent.stopPropagation)
|
|
.on(link, 'click', L.DomEvent.preventDefault)
|
|
.on(link, 'click', fn, this);
|
|
|
|
return link;
|
|
},
|
|
|
|
onAdd: function(map) {
|
|
this._map = map;
|
|
|
|
var className = 'leaflet-control-grid map-tooltip',
|
|
container = L.DomUtil.create('div', className),
|
|
contentWrapper = L.DomUtil.create('div', 'map-tooltip-content');
|
|
|
|
// hide the container element initially
|
|
container.style.display = 'none';
|
|
this._createClosebutton(container, this.hide);
|
|
container.appendChild(contentWrapper);
|
|
|
|
this._contentWrapper = contentWrapper;
|
|
this._popup = new L.Popup({ autoPan: false, closeOnClick: false });
|
|
|
|
map.on('popupclose', this._onPopupClose, this);
|
|
|
|
L.DomEvent
|
|
.disableClickPropagation(container)
|
|
// allow people to scroll tooltips with mousewheel
|
|
.addListener(container, 'mousewheel', L.DomEvent.stopPropagation);
|
|
|
|
this._layer
|
|
.on('mouseover', this._mouseover, this)
|
|
.on('mousemove', this._mousemove, this)
|
|
.on('click', this._click, this);
|
|
|
|
return container;
|
|
},
|
|
|
|
onRemove: function (map) {
|
|
|
|
map.off('popupclose', this._onPopupClose, this);
|
|
|
|
this._layer
|
|
.off('mouseover', this._mouseover, this)
|
|
.off('mousemove', this._mousemove, this)
|
|
.off('click', this._click, this);
|
|
}
|
|
});
|
|
|
|
module.exports.GridControl = GridControl;
|
|
|
|
module.exports.gridControl = function(_, options) {
|
|
return new GridControl(_, options);
|
|
};
|
|
|
|
},{"./util":30,"mustache":4,"sanitize-caja":5}],16:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var util = require('./util'),
|
|
request = require('./request'),
|
|
grid = require('./grid');
|
|
|
|
// forked from danzel/L.UTFGrid
|
|
var GridLayer = L.Layer.extend({
|
|
includes: [require('./load_tilejson')],
|
|
|
|
options: {
|
|
template: function() { return ''; }
|
|
},
|
|
|
|
_mouseOn: null,
|
|
_tilejson: {},
|
|
_cache: {},
|
|
|
|
initialize: function(_, options) {
|
|
L.Util.setOptions(this, options);
|
|
this._loadTileJSON(_);
|
|
},
|
|
|
|
_setTileJSON: function(json) {
|
|
util.strict(json, 'object');
|
|
|
|
L.extend(this.options, {
|
|
grids: json.grids,
|
|
minZoom: json.minzoom,
|
|
maxZoom: json.maxzoom,
|
|
bounds: json.bounds && util.lbounds(json.bounds)
|
|
});
|
|
|
|
this._tilejson = json;
|
|
this._cache = {};
|
|
this._update();
|
|
|
|
return this;
|
|
},
|
|
|
|
getTileJSON: function() {
|
|
return this._tilejson;
|
|
},
|
|
|
|
active: function() {
|
|
return !!(this._map && this.options.grids && this.options.grids.length);
|
|
},
|
|
|
|
onAdd: function(map) {
|
|
this._map = map;
|
|
this._update();
|
|
|
|
this._map
|
|
.on('click', this._click, this)
|
|
.on('mousemove', this._move, this)
|
|
.on('moveend', this._update, this);
|
|
},
|
|
|
|
onRemove: function() {
|
|
this._map
|
|
.off('click', this._click, this)
|
|
.off('mousemove', this._move, this)
|
|
.off('moveend', this._update, this);
|
|
},
|
|
|
|
getData: function(latlng, callback) {
|
|
if (!this.active()) return;
|
|
|
|
var map = this._map,
|
|
point = map.project(latlng.wrap()),
|
|
tileSize = 256,
|
|
resolution = 4,
|
|
x = Math.floor(point.x / tileSize),
|
|
y = Math.floor(point.y / tileSize),
|
|
max = map.options.crs.scale(map.getZoom()) / tileSize;
|
|
|
|
x = (x + max) % max;
|
|
y = (y + max) % max;
|
|
|
|
this._getTile(map.getZoom(), x, y, function(grid) {
|
|
var gridX = Math.floor((point.x - (x * tileSize)) / resolution),
|
|
gridY = Math.floor((point.y - (y * tileSize)) / resolution);
|
|
|
|
callback(grid(gridX, gridY));
|
|
});
|
|
|
|
return this;
|
|
},
|
|
|
|
_click: function(e) {
|
|
this.getData(e.latlng, L.bind(function(data) {
|
|
this.fire('click', {
|
|
latLng: e.latlng,
|
|
data: data
|
|
});
|
|
}, this));
|
|
},
|
|
|
|
_move: function(e) {
|
|
this.getData(e.latlng, L.bind(function(data) {
|
|
if (data !== this._mouseOn) {
|
|
if (this._mouseOn) {
|
|
this.fire('mouseout', {
|
|
latLng: e.latlng,
|
|
data: this._mouseOn
|
|
});
|
|
}
|
|
|
|
this.fire('mouseover', {
|
|
latLng: e.latlng,
|
|
data: data
|
|
});
|
|
|
|
this._mouseOn = data;
|
|
} else {
|
|
this.fire('mousemove', {
|
|
latLng: e.latlng,
|
|
data: data
|
|
});
|
|
}
|
|
}, this));
|
|
},
|
|
|
|
_getTileURL: function(tilePoint) {
|
|
var urls = this.options.grids,
|
|
index = (tilePoint.x + tilePoint.y) % urls.length,
|
|
url = urls[index];
|
|
|
|
return L.Util.template(url, tilePoint);
|
|
},
|
|
|
|
// Load up all required json grid files
|
|
_update: function() {
|
|
if (!this.active()) return;
|
|
|
|
var bounds = this._map.getPixelBounds(),
|
|
z = this._map.getZoom(),
|
|
tileSize = 256;
|
|
|
|
if (z > this.options.maxZoom || z < this.options.minZoom) return;
|
|
|
|
var tileBounds = L.bounds(
|
|
bounds.min.divideBy(tileSize)._floor(),
|
|
bounds.max.divideBy(tileSize)._floor()),
|
|
max = this._map.options.crs.scale(z) / tileSize;
|
|
|
|
for (var x = tileBounds.min.x; x <= tileBounds.max.x; x++) {
|
|
for (var y = tileBounds.min.y; y <= tileBounds.max.y; y++) {
|
|
// x wrapped
|
|
this._getTile(z, ((x % max) + max) % max, ((y % max) + max) % max);
|
|
}
|
|
}
|
|
},
|
|
|
|
_getTile: function(z, x, y, callback) {
|
|
var key = z + '_' + x + '_' + y,
|
|
tilePoint = L.point(x, y);
|
|
|
|
tilePoint.z = z;
|
|
|
|
if (!this._tileShouldBeLoaded(tilePoint)) {
|
|
return;
|
|
}
|
|
|
|
if (key in this._cache) {
|
|
if (!callback) return;
|
|
|
|
if (typeof this._cache[key] === 'function') {
|
|
callback(this._cache[key]); // Already loaded
|
|
} else {
|
|
this._cache[key].push(callback); // Pending
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
this._cache[key] = [];
|
|
|
|
if (callback) {
|
|
this._cache[key].push(callback);
|
|
}
|
|
|
|
request(this._getTileURL(tilePoint), L.bind(function(err, json) {
|
|
var callbacks = this._cache[key];
|
|
this._cache[key] = grid(json);
|
|
for (var i = 0; i < callbacks.length; ++i) {
|
|
callbacks[i](this._cache[key]);
|
|
}
|
|
}, this));
|
|
},
|
|
|
|
_tileShouldBeLoaded: function(tilePoint) {
|
|
if (tilePoint.z > this.options.maxZoom || tilePoint.z < this.options.minZoom) {
|
|
return false;
|
|
}
|
|
|
|
if (this.options.bounds) {
|
|
var tileSize = 256,
|
|
nwPoint = tilePoint.multiplyBy(tileSize),
|
|
sePoint = nwPoint.add(new L.Point(tileSize, tileSize)),
|
|
nw = this._map.unproject(nwPoint),
|
|
se = this._map.unproject(sePoint),
|
|
bounds = new L.LatLngBounds([nw, se]);
|
|
|
|
if (!this.options.bounds.intersects(bounds)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
});
|
|
|
|
module.exports.GridLayer = GridLayer;
|
|
|
|
module.exports.gridLayer = function(_, options) {
|
|
return new GridLayer(_, options);
|
|
};
|
|
|
|
},{"./grid":14,"./load_tilejson":20,"./request":25,"./util":30}],17:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var leaflet = require('./leaflet');
|
|
|
|
require('./mapbox');
|
|
|
|
module.exports = leaflet;
|
|
|
|
},{"./leaflet":18,"./mapbox":22}],18:[function(require,module,exports){
|
|
module.exports = window.L = require('leaflet/dist/leaflet-src');
|
|
|
|
},{"leaflet/dist/leaflet-src":3}],19:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var LegendControl = L.Control.extend({
|
|
|
|
options: {
|
|
position: 'bottomright',
|
|
sanitizer: require('sanitize-caja')
|
|
},
|
|
|
|
initialize: function(options) {
|
|
L.setOptions(this, options);
|
|
this._legends = {};
|
|
},
|
|
|
|
onAdd: function() {
|
|
this._container = L.DomUtil.create('div', 'map-legends wax-legends');
|
|
L.DomEvent.disableClickPropagation(this._container);
|
|
|
|
this._update();
|
|
|
|
return this._container;
|
|
},
|
|
|
|
addLegend: function(text) {
|
|
if (!text) { return this; }
|
|
|
|
if (!this._legends[text]) {
|
|
this._legends[text] = 0;
|
|
}
|
|
|
|
this._legends[text]++;
|
|
return this._update();
|
|
},
|
|
|
|
removeLegend: function(text) {
|
|
if (!text) { return this; }
|
|
if (this._legends[text]) this._legends[text]--;
|
|
return this._update();
|
|
},
|
|
|
|
_update: function() {
|
|
if (!this._map) { return this; }
|
|
|
|
this._container.innerHTML = '';
|
|
var hide = 'none';
|
|
|
|
for (var i in this._legends) {
|
|
if (this._legends.hasOwnProperty(i) && this._legends[i]) {
|
|
var div = L.DomUtil.create('div', 'map-legend wax-legend', this._container);
|
|
div.innerHTML = this.options.sanitizer(i);
|
|
hide = 'block';
|
|
}
|
|
}
|
|
|
|
// hide the control entirely unless there is at least one legend;
|
|
// otherwise there will be a small grey blemish on the map.
|
|
this._container.style.display = hide;
|
|
|
|
return this;
|
|
}
|
|
});
|
|
|
|
module.exports.LegendControl = LegendControl;
|
|
|
|
module.exports.legendControl = function(options) {
|
|
return new LegendControl(options);
|
|
};
|
|
|
|
},{"sanitize-caja":5}],20:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var request = require('./request'),
|
|
format_url = require('./format_url'),
|
|
util = require('./util');
|
|
|
|
module.exports = {
|
|
_loadTileJSON: function(_) {
|
|
if (typeof _ === 'string') {
|
|
_ = format_url.tileJSON(_, this.options && this.options.accessToken);
|
|
request(_, L.bind(function(err, json) {
|
|
if (err) {
|
|
util.log('could not load TileJSON at ' + _);
|
|
this.fire('error', {error: err});
|
|
} else if (json) {
|
|
this._setTileJSON(json);
|
|
this.fire('ready');
|
|
}
|
|
}, this));
|
|
} else if (_ && typeof _ === 'object') {
|
|
this._setTileJSON(_);
|
|
}
|
|
}
|
|
};
|
|
|
|
},{"./format_url":11,"./request":25,"./util":30}],21:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var tileLayer = require('./tile_layer').tileLayer,
|
|
featureLayer = require('./feature_layer').featureLayer,
|
|
gridLayer = require('./grid_layer').gridLayer,
|
|
gridControl = require('./grid_control').gridControl,
|
|
shareControl = require('./share_control').shareControl,
|
|
legendControl = require('./legend_control').legendControl,
|
|
mapboxLogoControl = require('./mapbox_logo').mapboxLogoControl,
|
|
feedback = require('./feedback');
|
|
|
|
function withAccessToken(options, accessToken) {
|
|
if (!accessToken || options.accessToken)
|
|
return options;
|
|
return L.extend({accessToken: accessToken}, options);
|
|
}
|
|
|
|
var LMap = L.Map.extend({
|
|
includes: [require('./load_tilejson')],
|
|
|
|
options: {
|
|
tileLayer: {},
|
|
featureLayer: {},
|
|
gridLayer: {},
|
|
legendControl: {},
|
|
gridControl: {},
|
|
shareControl: false,
|
|
sanitizer: require('sanitize-caja')
|
|
},
|
|
|
|
_tilejson: {},
|
|
|
|
initialize: function(element, _, options) {
|
|
|
|
L.Map.prototype.initialize.call(this, element,
|
|
L.extend({}, L.Map.prototype.options, options));
|
|
|
|
// Disable the default 'Leaflet' text
|
|
if (this.attributionControl) {
|
|
this.attributionControl.setPrefix('');
|
|
|
|
var compact = this.options.attributionControl.compact;
|
|
// Set a compact display if map container width is < 640 or
|
|
// compact is set to `true` in attributionControl options.
|
|
if (compact || (compact !== false && this._container.offsetWidth <= 640)) {
|
|
L.DomUtil.addClass(this.attributionControl._container, 'leaflet-compact-attribution');
|
|
}
|
|
|
|
if (compact === undefined) {
|
|
this.on('resize', function() {
|
|
if (this._container.offsetWidth > 640) {
|
|
L.DomUtil.removeClass(this.attributionControl._container, 'leaflet-compact-attribution');
|
|
} else {
|
|
L.DomUtil.addClass(this.attributionControl._container, 'leaflet-compact-attribution');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
if (this.options.tileLayer) {
|
|
this.tileLayer = tileLayer(undefined,
|
|
withAccessToken(this.options.tileLayer, this.options.accessToken));
|
|
this.addLayer(this.tileLayer);
|
|
}
|
|
|
|
if (this.options.featureLayer) {
|
|
this.featureLayer = featureLayer(undefined,
|
|
withAccessToken(this.options.featureLayer, this.options.accessToken));
|
|
this.addLayer(this.featureLayer);
|
|
}
|
|
|
|
if (this.options.gridLayer) {
|
|
this.gridLayer = gridLayer(undefined,
|
|
withAccessToken(this.options.gridLayer, this.options.accessToken));
|
|
this.addLayer(this.gridLayer);
|
|
}
|
|
|
|
if (this.options.gridLayer && this.options.gridControl) {
|
|
this.gridControl = gridControl(this.gridLayer, this.options.gridControl);
|
|
this.addControl(this.gridControl);
|
|
}
|
|
|
|
if (this.options.legendControl) {
|
|
this.legendControl = legendControl(this.options.legendControl);
|
|
this.addControl(this.legendControl);
|
|
}
|
|
|
|
if (this.options.shareControl) {
|
|
this.shareControl = shareControl(undefined,
|
|
withAccessToken(this.options.shareControl, this.options.accessToken));
|
|
this.addControl(this.shareControl);
|
|
}
|
|
|
|
this._mapboxLogoControl = mapboxLogoControl(this.options.mapboxLogoControl);
|
|
this.addControl(this._mapboxLogoControl);
|
|
|
|
this._loadTileJSON(_);
|
|
|
|
this.on('layeradd', this._onLayerAdd, this)
|
|
.on('layerremove', this._onLayerRemove, this)
|
|
.on('moveend', this._updateMapFeedbackLink, this);
|
|
|
|
this.whenReady(function () {
|
|
feedback.on('change', this._updateMapFeedbackLink, this);
|
|
});
|
|
|
|
this.on('unload', function () {
|
|
feedback.off('change', this._updateMapFeedbackLink, this);
|
|
});
|
|
},
|
|
|
|
// use a javascript object of tilejson data to configure this layer
|
|
_setTileJSON: function(_) {
|
|
this._tilejson = _;
|
|
this._initialize(_);
|
|
return this;
|
|
},
|
|
|
|
getTileJSON: function() {
|
|
return this._tilejson;
|
|
},
|
|
|
|
_initialize: function(json) {
|
|
if (this.tileLayer) {
|
|
this.tileLayer._setTileJSON(json);
|
|
this._updateLayer(this.tileLayer);
|
|
}
|
|
|
|
if (this.featureLayer && !this.featureLayer.getGeoJSON() && json.data && json.data[0]) {
|
|
this.featureLayer.loadURL(json.data[0]);
|
|
}
|
|
|
|
if (this.gridLayer) {
|
|
this.gridLayer._setTileJSON(json);
|
|
this._updateLayer(this.gridLayer);
|
|
}
|
|
|
|
if (this.legendControl && json.legend) {
|
|
this.legendControl.addLegend(json.legend);
|
|
}
|
|
|
|
if (this.shareControl) {
|
|
this.shareControl._setTileJSON(json);
|
|
}
|
|
|
|
this._mapboxLogoControl._setTileJSON(json);
|
|
|
|
if (!this._loaded && json.center) {
|
|
var zoom = this.getZoom() !== undefined ? this.getZoom() : json.center[2],
|
|
center = L.latLng(json.center[1], json.center[0]);
|
|
|
|
this.setView(center, zoom);
|
|
}
|
|
},
|
|
|
|
_updateMapFeedbackLink: function() {
|
|
if (!this._controlContainer.getElementsByClassName) return;
|
|
var link = this._controlContainer.getElementsByClassName('mapbox-improve-map');
|
|
if (link.length && this._loaded) {
|
|
var center = this.getCenter().wrap();
|
|
var tilejson = this._tilejson || {};
|
|
var id = tilejson.id || '';
|
|
|
|
var hash = '#' + id + '/' +
|
|
center.lng.toFixed(3) + '/' +
|
|
center.lat.toFixed(3) + '/' +
|
|
this.getZoom();
|
|
|
|
for (var key in feedback.data) {
|
|
hash += '/' + key + '=' + feedback.data[key];
|
|
}
|
|
|
|
for (var i = 0; i < link.length; i++) {
|
|
link[i].hash = hash;
|
|
}
|
|
}
|
|
},
|
|
|
|
_onLayerAdd: function(e) {
|
|
if ('on' in e.layer) {
|
|
e.layer.on('ready', this._onLayerReady, this);
|
|
}
|
|
window.setTimeout(L.bind(this._updateMapFeedbackLink, this), 0); // Update after attribution control resets the HTML.
|
|
},
|
|
|
|
_onLayerRemove: function(e) {
|
|
if ('on' in e.layer) {
|
|
e.layer.off('ready', this._onLayerReady, this);
|
|
}
|
|
window.setTimeout(L.bind(this._updateMapFeedbackLink, this), 0); // Update after attribution control resets the HTML.
|
|
},
|
|
|
|
_onLayerReady: function(e) {
|
|
this._updateLayer(e.target);
|
|
},
|
|
|
|
_updateLayer: function(layer) {
|
|
if (!layer.options) return;
|
|
|
|
if (this.attributionControl && this._loaded && layer.getAttribution) {
|
|
this.attributionControl.addAttribution(layer.getAttribution());
|
|
}
|
|
|
|
if (!(L.stamp(layer) in this._zoomBoundLayers) &&
|
|
(layer.options.maxZoom || layer.options.minZoom)) {
|
|
this._zoomBoundLayers[L.stamp(layer)] = layer;
|
|
}
|
|
|
|
this._updateMapFeedbackLink();
|
|
this._updateZoomLevels();
|
|
}
|
|
});
|
|
|
|
module.exports.Map = LMap;
|
|
|
|
module.exports.map = function(element, _, options) {
|
|
return new LMap(element, _, options);
|
|
};
|
|
|
|
},{"./feature_layer":9,"./feedback":10,"./grid_control":15,"./grid_layer":16,"./legend_control":19,"./load_tilejson":20,"./mapbox_logo":23,"./share_control":26,"./tile_layer":29,"sanitize-caja":5}],22:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var geocoderControl = require('./geocoder_control'),
|
|
gridControl = require('./grid_control'),
|
|
featureLayer = require('./feature_layer'),
|
|
legendControl = require('./legend_control'),
|
|
shareControl = require('./share_control'),
|
|
tileLayer = require('./tile_layer'),
|
|
map = require('./map'),
|
|
gridLayer = require('./grid_layer'),
|
|
styleLayer = require('./style_layer');
|
|
|
|
L.mapbox = module.exports = {
|
|
VERSION: require('../package.json').version,
|
|
geocoder: require('./geocoder'),
|
|
marker: require('./marker'),
|
|
simplestyle: require('./simplestyle'),
|
|
tileLayer: tileLayer.tileLayer,
|
|
TileLayer: tileLayer.TileLayer,
|
|
styleLayer: styleLayer.styleLayer,
|
|
StyleLayer: styleLayer.StyleLayer,
|
|
shareControl: shareControl.shareControl,
|
|
ShareControl: shareControl.ShareControl,
|
|
legendControl: legendControl.legendControl,
|
|
LegendControl: legendControl.LegendControl,
|
|
geocoderControl: geocoderControl.geocoderControl,
|
|
GeocoderControl: geocoderControl.GeocoderControl,
|
|
gridControl: gridControl.gridControl,
|
|
GridControl: gridControl.GridControl,
|
|
gridLayer: gridLayer.gridLayer,
|
|
GridLayer: gridLayer.GridLayer,
|
|
featureLayer: featureLayer.featureLayer,
|
|
FeatureLayer: featureLayer.FeatureLayer,
|
|
map: map.map,
|
|
Map: map.Map,
|
|
config: require('./config'),
|
|
sanitize: require('sanitize-caja'),
|
|
template: require('mustache').to_html,
|
|
feedback: require('./feedback')
|
|
};
|
|
|
|
|
|
// Hardcode image path, because Leaflet's autodetection
|
|
// fails, because mapbox.js is not named leaflet.js
|
|
window.L.Icon.Default.imagePath =
|
|
// Detect bad-news protocols like file:// and hardcode
|
|
// to https if they're detected.
|
|
((document.location.protocol === 'https:' ||
|
|
document.location.protocol === 'http:') ? '' : 'https:') +
|
|
'//api.tiles.mapbox.com/mapbox.js/' + 'v' +
|
|
require('../package.json').version + '/images/';
|
|
|
|
},{"../package.json":7,"./config":8,"./feature_layer":9,"./feedback":10,"./geocoder":12,"./geocoder_control":13,"./grid_control":15,"./grid_layer":16,"./legend_control":19,"./map":21,"./marker":24,"./share_control":26,"./simplestyle":27,"./style_layer":28,"./tile_layer":29,"mustache":4,"sanitize-caja":5}],23:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var MapboxLogoControl = L.Control.extend({
|
|
|
|
options: {
|
|
position: 'bottomleft'
|
|
},
|
|
|
|
initialize: function(options) {
|
|
L.setOptions(this, options);
|
|
},
|
|
|
|
onAdd: function() {
|
|
this._container = L.DomUtil.create('div', 'mapbox-logo');
|
|
return this._container;
|
|
},
|
|
|
|
_setTileJSON: function(json) {
|
|
// Check if account referenced by the accessToken
|
|
// is asscociated with the Mapbox Logo
|
|
// as determined by mapbox-maps.
|
|
if (json.mapbox_logo) {
|
|
L.DomUtil.addClass(this._container, 'mapbox-logo-true');
|
|
}
|
|
}
|
|
});
|
|
|
|
module.exports.MapboxLogoControl = MapboxLogoControl;
|
|
|
|
module.exports.mapboxLogoControl = function(options) {
|
|
return new MapboxLogoControl(options);
|
|
};
|
|
|
|
},{}],24:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var format_url = require('./format_url'),
|
|
util = require('./util'),
|
|
sanitize = require('sanitize-caja');
|
|
|
|
// mapbox-related markers functionality
|
|
// provide an icon from mapbox's simple-style spec and hosted markers
|
|
// service
|
|
function icon(fp, options) {
|
|
fp = fp || {};
|
|
|
|
var sizes = {
|
|
small: [20, 50],
|
|
medium: [30, 70],
|
|
large: [35, 90]
|
|
},
|
|
size = fp['marker-size'] || 'medium',
|
|
symbol = ('marker-symbol' in fp && fp['marker-symbol'] !== '') ? '-' + fp['marker-symbol'] : '',
|
|
color = (fp['marker-color'] || '7e7e7e').replace('#', '');
|
|
|
|
return L.icon({
|
|
iconUrl: format_url('/v4/marker/' +
|
|
'pin-' + size.charAt(0) + symbol + '+' + color +
|
|
// detect and use retina markers, which are x2 resolution
|
|
(L.Browser.retina ? '@2x' : '') + '.png', options && options.accessToken),
|
|
iconSize: sizes[size],
|
|
iconAnchor: [sizes[size][0] / 2, sizes[size][1] / 2],
|
|
popupAnchor: [0, -sizes[size][1] / 2]
|
|
});
|
|
}
|
|
|
|
// a factory that provides markers for Leaflet from Mapbox's
|
|
// [simple-style specification](https://github.com/mapbox/simplestyle-spec)
|
|
// and [Markers API](http://mapbox.com/developers/api/#markers).
|
|
function style(f, latlon, options) {
|
|
return L.marker(latlon, {
|
|
icon: icon(f.properties, options),
|
|
title: util.strip_tags(
|
|
sanitize((f.properties && f.properties.title) || ''))
|
|
});
|
|
}
|
|
|
|
// Sanitize and format properties of a GeoJSON Feature object in order
|
|
// to form the HTML string used as the argument for `L.createPopup`
|
|
function createPopup(f, sanitizer) {
|
|
if (!f || !f.properties) return '';
|
|
var popup = '';
|
|
|
|
if (f.properties.title) {
|
|
popup += '<div class="marker-title">' + f.properties.title + '</div>';
|
|
}
|
|
|
|
if (f.properties.description) {
|
|
popup += '<div class="marker-description">' + f.properties.description + '</div>';
|
|
}
|
|
|
|
return (sanitizer || sanitize)(popup);
|
|
}
|
|
|
|
module.exports = {
|
|
icon: icon,
|
|
style: style,
|
|
createPopup: createPopup
|
|
};
|
|
|
|
},{"./format_url":11,"./util":30,"sanitize-caja":5}],25:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var corslite = require('corslite'),
|
|
strict = require('./util').strict,
|
|
config = require('./config');
|
|
|
|
var protocol = /^(https?:)?(?=\/\/(.|api)\.tiles\.mapbox\.com\/)/;
|
|
|
|
module.exports = function(url, callback) {
|
|
strict(url, 'string');
|
|
strict(callback, 'function');
|
|
|
|
url = url.replace(protocol, function(match, protocol) {
|
|
if (!('withCredentials' in new window.XMLHttpRequest())) {
|
|
// XDomainRequest in use; doesn't support cross-protocol requests
|
|
return document.location.protocol;
|
|
} else if (protocol === 'https:' || document.location.protocol === 'https:' || config.FORCE_HTTPS) {
|
|
return 'https:';
|
|
} else {
|
|
return 'http:';
|
|
}
|
|
});
|
|
|
|
function onload(err, resp) {
|
|
if (!err && resp) {
|
|
resp = JSON.parse(resp.responseText);
|
|
}
|
|
callback(err, resp);
|
|
}
|
|
|
|
return corslite(url, onload);
|
|
};
|
|
|
|
},{"./config":8,"./util":30,"corslite":1}],26:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var format_url = require('./format_url');
|
|
|
|
var ShareControl = L.Control.extend({
|
|
includes: [require('./load_tilejson')],
|
|
|
|
options: {
|
|
position: 'topleft',
|
|
url: ''
|
|
},
|
|
|
|
initialize: function(_, options) {
|
|
L.setOptions(this, options);
|
|
this._loadTileJSON(_);
|
|
},
|
|
|
|
_setTileJSON: function(json) {
|
|
this._tilejson = json;
|
|
},
|
|
|
|
onAdd: function(map) {
|
|
this._map = map;
|
|
|
|
var container = L.DomUtil.create('div', 'leaflet-control-mapbox-share leaflet-bar');
|
|
var link = L.DomUtil.create('a', 'mapbox-share mapbox-icon mapbox-icon-share', container);
|
|
link.href = '#';
|
|
|
|
this._modal = L.DomUtil.create('div', 'mapbox-modal', this._map._container);
|
|
this._mask = L.DomUtil.create('div', 'mapbox-modal-mask', this._modal);
|
|
this._content = L.DomUtil.create('div', 'mapbox-modal-content', this._modal);
|
|
|
|
L.DomEvent.addListener(link, 'click', this._shareClick, this);
|
|
L.DomEvent.disableClickPropagation(container);
|
|
|
|
this._map.on('mousedown', this._clickOut, this);
|
|
|
|
return container;
|
|
},
|
|
|
|
_clickOut: function(e) {
|
|
if (this._sharing) {
|
|
L.DomEvent.preventDefault(e);
|
|
L.DomUtil.removeClass(this._modal, 'active');
|
|
this._content.innerHTML = '';
|
|
this._sharing = null;
|
|
return;
|
|
}
|
|
},
|
|
|
|
_shareClick: function(e) {
|
|
L.DomEvent.stop(e);
|
|
if (this._sharing) return this._clickOut(e);
|
|
|
|
var tilejson = this._tilejson || this._map._tilejson || {},
|
|
url = encodeURIComponent(this.options.url || tilejson.webpage || window.location),
|
|
name = encodeURIComponent(tilejson.name),
|
|
image = format_url('/v4/' + tilejson.id + '/' + this._map.getCenter().lng + ',' + this._map.getCenter().lat + ',' + this._map.getZoom() + '/600x600.png', this.options.accessToken),
|
|
embed = format_url('/v4/' + tilejson.id + '.html', this.options.accessToken),
|
|
twitterURL = '//twitter.com/intent/tweet?status=' + name + ' ' + url,
|
|
facebookURL = '//www.facebook.com/sharer.php?u=' + url + '&t=' + name,
|
|
pinterestURL = '//www.pinterest.com/pin/create/button/?url=' + url + '&media=' + image + '&description=' + name,
|
|
embedValue = '<iframe width="100%" height="500px" frameBorder="0" src="' + embed + '"></iframe>',
|
|
embedLabel = 'Copy and paste this <strong>HTML code</strong> into documents to embed this map on web pages.';
|
|
|
|
function createShareButton(buttonClass, href, socialMediaName) {
|
|
var elem = document.createElement('a');
|
|
elem.setAttribute('class', buttonClass);
|
|
elem.setAttribute('href', href);
|
|
elem.setAttribute('target', '_blank');
|
|
socialMediaName = document.createTextNode(socialMediaName);
|
|
elem.appendChild(socialMediaName);
|
|
|
|
return elem;
|
|
}
|
|
|
|
L.DomUtil.addClass(this._modal, 'active');
|
|
|
|
this._sharing = L.DomUtil.create('div', 'mapbox-modal-body', this._content);
|
|
|
|
var twitterButton = createShareButton('mapbox-button mapbox-button-icon mapbox-icon-twitter', twitterURL, 'Twitter');
|
|
var facebookButton = createShareButton('mapbox-button mapbox-button-icon mapbox-icon-facebook', facebookURL, 'Facebook');
|
|
var pinterestButton = createShareButton('mapbox-button mapbox-button-icon mapbox-icon-pinterest', pinterestURL, 'Pinterest');
|
|
|
|
var shareHeader = document.createElement('h3');
|
|
var shareText = document.createTextNode('Share this map');
|
|
shareHeader.appendChild(shareText);
|
|
|
|
var shareButtons = document.createElement('div');
|
|
shareButtons.setAttribute('class', 'mapbox-share-buttons');
|
|
shareButtons.appendChild(facebookButton);
|
|
shareButtons.appendChild(twitterButton);
|
|
shareButtons.appendChild(pinterestButton);
|
|
|
|
this._sharing.appendChild(shareHeader);
|
|
this._sharing.appendChild(shareButtons);
|
|
|
|
var input = L.DomUtil.create('input', 'mapbox-embed', this._sharing);
|
|
input.type = 'text';
|
|
input.value = embedValue;
|
|
|
|
var label = L.DomUtil.create('label', 'mapbox-embed-description', this._sharing);
|
|
label.innerHTML = embedLabel;
|
|
|
|
var close = L.DomUtil.create('a', 'leaflet-popup-close-button', this._sharing);
|
|
close.href = '#';
|
|
|
|
L.DomEvent.disableClickPropagation(this._sharing);
|
|
L.DomEvent.addListener(close, 'click', this._clickOut, this);
|
|
L.DomEvent.addListener(input, 'click', function(e) {
|
|
e.target.focus();
|
|
e.target.select();
|
|
});
|
|
}
|
|
});
|
|
|
|
module.exports.ShareControl = ShareControl;
|
|
|
|
module.exports.shareControl = function(_, options) {
|
|
return new ShareControl(_, options);
|
|
};
|
|
|
|
},{"./format_url":11,"./load_tilejson":20}],27:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
// an implementation of the simplestyle spec for polygon and linestring features
|
|
// https://github.com/mapbox/simplestyle-spec
|
|
var defaults = {
|
|
stroke: '#555555',
|
|
'stroke-width': 2,
|
|
'stroke-opacity': 1,
|
|
fill: '#555555',
|
|
'fill-opacity': 0.5
|
|
};
|
|
|
|
var mapping = [
|
|
['stroke', 'color'],
|
|
['stroke-width', 'weight'],
|
|
['stroke-opacity', 'opacity'],
|
|
['fill', 'fillColor'],
|
|
['fill-opacity', 'fillOpacity']
|
|
];
|
|
|
|
function fallback(a, b) {
|
|
var c = {};
|
|
for (var k in b) {
|
|
if (a[k] === undefined) c[k] = b[k];
|
|
else c[k] = a[k];
|
|
}
|
|
return c;
|
|
}
|
|
|
|
function remap(a) {
|
|
var d = {};
|
|
for (var i = 0; i < mapping.length; i++) {
|
|
d[mapping[i][1]] = a[mapping[i][0]];
|
|
}
|
|
return d;
|
|
}
|
|
|
|
function style(feature) {
|
|
return remap(fallback(feature.properties || {}, defaults));
|
|
}
|
|
|
|
module.exports = {
|
|
style: style,
|
|
defaults: defaults
|
|
};
|
|
|
|
},{}],28:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var util = require('./util');
|
|
var format_url = require('./format_url');
|
|
var request = require('./request');
|
|
|
|
var StyleLayer = L.TileLayer.extend({
|
|
|
|
options: {
|
|
sanitizer: require('sanitize-caja')
|
|
},
|
|
|
|
initialize: function(_, options) {
|
|
L.TileLayer.prototype.initialize.call(this, undefined, L.extend({}, options, {
|
|
tileSize: 512,
|
|
zoomOffset: -1,
|
|
minNativeZoom: 0,
|
|
tms: false
|
|
}));
|
|
this._url = this._formatTileURL(_);
|
|
this._getAttribution(_);
|
|
},
|
|
|
|
_getAttribution: function(_) {
|
|
var styleURL = format_url.style(_, this.options && this.options.accessToken);
|
|
request(styleURL, L.bind(function(err, style) {
|
|
if (err) {
|
|
util.log('could not load Mapbox style at ' + styleURL);
|
|
this.fire('error', {error: err});
|
|
}
|
|
var sources = [];
|
|
for (var id in style.sources) {
|
|
var source = style.sources[id].url.split('mapbox://')[1];
|
|
sources.push(source);
|
|
}
|
|
request(format_url.tileJSON(sources.join(), this.options.accessToken), L.bind(function(err, json) {
|
|
if (err) {
|
|
util.log('could not load TileJSON at ' + _);
|
|
this.fire('error', {error: err});
|
|
} else if (json) {
|
|
util.strict(json, 'object');
|
|
|
|
this.options.attribution = this.options.sanitizer(json.attribution);
|
|
|
|
this._tilejson = json;
|
|
this.fire('ready');
|
|
}
|
|
}, this));
|
|
}, this));
|
|
},
|
|
|
|
// disable the setUrl function, which is not available on mapbox tilelayers
|
|
setUrl: null,
|
|
|
|
_formatTileURL: function(style) {
|
|
if (typeof style === 'string') {
|
|
if (style.indexOf('mapbox://styles/') === -1) {
|
|
util.log('Incorrectly formatted Mapbox style at ' + style);
|
|
this.fire('error');
|
|
}
|
|
var ownerIDStyle = style.split('mapbox://styles/')[1];
|
|
return format_url('/styles/v1/' + ownerIDStyle + '/tiles/{z}/{x}/{y}{r}', this.options.accessToken);
|
|
} else if (typeof style === 'object') {
|
|
return format_url('/styles/v1/' + style.owner + '/' + style.id + '/tiles/{z}/{x}/{y}{r}', this.options.accessToken);
|
|
}
|
|
}
|
|
});
|
|
|
|
module.exports.StyleLayer = StyleLayer;
|
|
|
|
module.exports.styleLayer = function(_, options) {
|
|
return new StyleLayer(_, options);
|
|
};
|
|
|
|
},{"./format_url":11,"./request":25,"./util":30,"sanitize-caja":5}],29:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
var util = require('./util');
|
|
var formatPattern = /\.((?:png|jpg)\d*)(?=$|\?)/;
|
|
|
|
var TileLayer = L.TileLayer.extend({
|
|
includes: [require('./load_tilejson')],
|
|
|
|
options: {
|
|
sanitizer: require('sanitize-caja')
|
|
},
|
|
|
|
// http://mapbox.com/developers/api/#image_quality
|
|
formats: [
|
|
'png', 'jpg',
|
|
// PNG
|
|
'png32', 'png64', 'png128', 'png256',
|
|
// JPG
|
|
'jpg70', 'jpg80', 'jpg90'],
|
|
|
|
scalePrefix: '@2x.',
|
|
|
|
initialize: function(_, options) {
|
|
L.TileLayer.prototype.initialize.call(this, undefined, options);
|
|
|
|
this._tilejson = {};
|
|
|
|
if (options && options.format) {
|
|
util.strict_oneof(options.format, this.formats);
|
|
}
|
|
|
|
this._loadTileJSON(_);
|
|
},
|
|
|
|
setFormat: function(_) {
|
|
util.strict(_, 'string');
|
|
this.options.format = _;
|
|
this.redraw();
|
|
return this;
|
|
},
|
|
|
|
// disable the setUrl function, which is not available on mapbox tilelayers
|
|
setUrl: null,
|
|
|
|
_setTileJSON: function(json) {
|
|
util.strict(json, 'object');
|
|
|
|
if (!this.options.format) {
|
|
var match = json.tiles[0].match(formatPattern);
|
|
if (match) {
|
|
this.options.format = match[1];
|
|
}
|
|
}
|
|
|
|
L.extend(this.options, {
|
|
tiles: json.tiles,
|
|
attribution: this.options.sanitizer(json.attribution),
|
|
minZoom: json.minzoom || 0,
|
|
maxZoom: json.maxzoom || 18,
|
|
tms: json.scheme === 'tms',
|
|
bounds: json.bounds && util.lbounds(json.bounds)
|
|
});
|
|
|
|
this._tilejson = json;
|
|
this.redraw();
|
|
return this;
|
|
},
|
|
|
|
getTileJSON: function() {
|
|
return this._tilejson;
|
|
},
|
|
|
|
// this is an exception to mapbox.js naming rules because it's called
|
|
// by `L.map`
|
|
getTileUrl: function(tilePoint) {
|
|
var tiles = this.options.tiles,
|
|
index = Math.floor(Math.abs(tilePoint.x + tilePoint.y) % tiles.length),
|
|
url = tiles[index];
|
|
|
|
var templated = L.Util.template(url, tilePoint);
|
|
if (!templated || !this.options.format) {
|
|
return templated;
|
|
} else {
|
|
return templated.replace(formatPattern,
|
|
(L.Browser.retina ? this.scalePrefix : '.') + this.options.format);
|
|
}
|
|
},
|
|
|
|
// TileJSON.TileLayers are added to the map immediately, so that they get
|
|
// the desired z-index, but do not update until the TileJSON has been loaded.
|
|
_update: function() {
|
|
if (this.options.tiles) {
|
|
L.TileLayer.prototype._update.call(this);
|
|
}
|
|
}
|
|
});
|
|
|
|
module.exports.TileLayer = TileLayer;
|
|
|
|
module.exports.tileLayer = function(_, options) {
|
|
return new TileLayer(_, options);
|
|
};
|
|
|
|
},{"./load_tilejson":20,"./util":30,"sanitize-caja":5}],30:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
function contains(item, list) {
|
|
if (!list || !list.length) return false;
|
|
for (var i = 0; i < list.length; i++) {
|
|
if (list[i] === item) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
module.exports = {
|
|
idUrl: function(_, t) {
|
|
if (_.indexOf('/') === -1) t.loadID(_);
|
|
else t.loadURL(_);
|
|
},
|
|
log: function(_) {
|
|
if (typeof console === 'object' &&
|
|
typeof console.error === 'function') {
|
|
console.error(_);
|
|
}
|
|
},
|
|
strict: function(_, type) {
|
|
if (typeof _ !== type) {
|
|
throw new Error('Invalid argument: ' + type + ' expected');
|
|
}
|
|
},
|
|
strict_instance: function(_, klass, name) {
|
|
if (!(_ instanceof klass)) {
|
|
throw new Error('Invalid argument: ' + name + ' expected');
|
|
}
|
|
},
|
|
strict_oneof: function(_, values) {
|
|
if (!contains(_, values)) {
|
|
throw new Error('Invalid argument: ' + _ + ' given, valid values are ' +
|
|
values.join(', '));
|
|
}
|
|
},
|
|
strip_tags: function(_) {
|
|
return _.replace(/<[^<]+>/g, '');
|
|
},
|
|
lbounds: function(_) {
|
|
// leaflet-compatible bounds, since leaflet does not do geojson
|
|
return new L.LatLngBounds([[_[1], _[0]], [_[3], _[2]]]);
|
|
}
|
|
};
|
|
|
|
},{}]},{},[17])
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvY29yc2xpdGUvY29yc2xpdGUuanMiLCJub2RlX21vZHVsZXMvaXNhcnJheS9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9sZWFmbGV0L2Rpc3QvbGVhZmxldC1zcmMuanMiLCJub2RlX21vZHVsZXMvbXVzdGFjaGUvbXVzdGFjaGUuanMiLCJub2RlX21vZHVsZXMvc2FuaXRpemUtY2FqYS9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9zYW5pdGl6ZS1jYWphL3Nhbml0aXplci1idW5kbGUuanMiLCJwYWNrYWdlLmpzb24iLCJzcmMvY29uZmlnLmpzIiwic3JjL2ZlYXR1cmVfbGF5ZXIuanMiLCJzcmMvZmVlZGJhY2suanMiLCJzcmMvZm9ybWF0X3VybC5qcyIsInNyYy9nZW9jb2Rlci5qcyIsInNyYy9nZW9jb2Rlcl9jb250cm9sLmpzIiwic3JjL2dyaWQuanMiLCJzcmMvZ3JpZF9jb250cm9sLmpzIiwic3JjL2dyaWRfbGF5ZXIuanMiLCJzcmMvaW5kZXguanMiLCJzcmMvbGVhZmxldC5qcyIsInNyYy9sZWdlbmRfY29udHJvbC5qcyIsInNyYy9sb2FkX3RpbGVqc29uLmpzIiwic3JjL21hcC5qcyIsInNyYy9tYXBib3guanMiLCJzcmMvbWFwYm94X2xvZ28uanMiLCJzcmMvbWFya2VyLmpzIiwic3JjL3JlcXVlc3QuanMiLCJzcmMvc2hhcmVfY29udHJvbC5qcyIsInNyYy9zaW1wbGVzdHlsZS5qcyIsInNyYy9zdHlsZV9sYXllci5qcyIsInNyYy90aWxlX2xheWVyLmpzIiwic3JjL3V0aWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3RkE7QUFDQTtBQUNBO0FBQ0E7O0FDSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2ozWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JuQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvNEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNSQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1TUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdE1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNQQTtBQUNBOztBQ0RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3hCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMU5BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25EQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdEdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsImZ1bmN0aW9uIGNvcnNsaXRlKHVybCwgY2FsbGJhY2ssIGNvcnMpIHtcbiAgICB2YXIgc2VudCA9IGZhbHNlO1xuXG4gICAgaWYgKHR5cGVvZiB3aW5kb3cuWE1MSHR0cFJlcXVlc3QgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIHJldHVybiBjYWxsYmFjayhFcnJvcignQnJvd3NlciBub3Qgc3VwcG9ydGVkJykpO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgY29ycyA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgdmFyIG0gPSB1cmwubWF0Y2goL15cXHMqaHR0cHM/OlxcL1xcL1teXFwvXSovKTtcbiAgICAgICAgY29ycyA9IG0gJiYgKG1bMF0gIT09IGxvY2F0aW9uLnByb3RvY29sICsgJy8vJyArIGxvY2F0aW9uLmRvbWFpbiArXG4gICAgICAgICAgICAgICAgKGxvY2F0aW9uLnBvcnQgPyAnOicgKyBsb2NhdGlvbi5wb3J0IDogJycpKTtcbiAgICB9XG5cbiAgICB2YXIgeCA9IG5ldyB3aW5kb3cuWE1MSHR0cFJlcXVlc3QoKTtcblxuICAgIGZ1bmN0aW9uIGlzU3VjY2Vzc2Z1bChzdGF0dXMpIHtcbiAgICAgICAgcmV0dXJuIHN0YXR1cyA+PSAyMDAgJiYgc3RhdHVzIDwgMzAwIHx8IHN0YXR1cyA9PT0gMzA0O1xuICAgIH1cblxuICAgIGlmIChjb3JzICYmICEoJ3dpdGhDcmVkZW50aWFscycgaW4geCkpIHtcbiAgICAgICAgLy8gSUU4LTlcbiAgICAgICAgeCA9IG5ldyB3aW5kb3cuWERvbWFpblJlcXVlc3QoKTtcblxuICAgICAgICAvLyBFbnN1cmUgY2FsbGJhY2sgaXMgbmV2ZXIgY2FsbGVkIHN5bmNocm9ub3VzbHksIGkuZS4sIGJlZm9yZVxuICAgICAgICAvLyB4LnNlbmQoKSByZXR1cm5zICh0aGlzIGhhcyBiZWVuIG9ic2VydmVkIGluIHRoZSB3aWxkKS5cbiAgICAgICAgLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9tYXBib3gvbWFwYm94LmpzL2lzc3Vlcy80NzJcbiAgICAgICAgdmFyIG9yaWdpbmFsID0gY2FsbGJhY2s7XG4gICAgICAgIGNhbGxiYWNrID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZiAoc2VudCkge1xuICAgICAgICAgICAgICAgIG9yaWdpbmFsLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHZhciB0aGF0ID0gdGhpcywgYXJncyA9IGFyZ3VtZW50cztcbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBvcmlnaW5hbC5hcHBseSh0aGF0LCBhcmdzKTtcbiAgICAgICAgICAgICAgICB9LCAwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIGxvYWRlZCgpIHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgLy8gWERvbWFpblJlcXVlc3RcbiAgICAgICAgICAgIHguc3RhdHVzID09PSB1bmRlZmluZWQgfHxcbiAgICAgICAgICAgIC8vIG1vZGVybiBicm93c2Vyc1xuICAgICAgICAgICAgaXNTdWNjZXNzZnVsKHguc3RhdHVzKSkgY2FsbGJhY2suY2FsbCh4LCBudWxsLCB4KTtcbiAgICAgICAgZWxzZSBjYWxsYmFjay5jYWxsKHgsIHgsIG51bGwpO1xuICAgIH1cblxuICAgIC8vIEJvdGggYG9ucmVhZHlzdGF0ZWNoYW5nZWAgYW5kIGBvbmxvYWRgIGNhbiBmaXJlLiBgb25yZWFkeXN0YXRlY2hhbmdlYFxuICAgIC8vIGhhcyBbYmVlbiBzdXBwb3J0ZWQgZm9yIGxvbmdlcl0oaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL2EvOTE4MTUwOC8yMjkwMDEpLlxuICAgIGlmICgnb25sb2FkJyBpbiB4KSB7XG4gICAgICAgIHgub25sb2FkID0gbG9hZGVkO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHgub25yZWFkeXN0YXRlY2hhbmdlID0gZnVuY3Rpb24gcmVhZHlzdGF0ZSgpIHtcbiAgICAgICAgICAgIGlmICh4LnJlYWR5U3RhdGUgPT09IDQpIHtcbiAgICAgICAgICAgICAgICBsb2FkZWQoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBDYWxsIHRoZSBjYWxsYmFjayB3aXRoIHRoZSBYTUxIdHRwUmVxdWVzdCBvYmplY3QgYXMgYW4gZXJyb3IgYW5kIHByZXZlbnRcbiAgICAvLyBpdCBmcm9tIGV2ZXIgYmVpbmcgY2FsbGVkIGFnYWluIGJ5IHJlYXNzaWduaW5nIGl0IHRvIGBub29wYFxuICAgIHgub25lcnJvciA9IGZ1bmN0aW9uIGVycm9yKGV2dCkge1xuICAgICAgICAvLyBYRG9tYWluUmVxdWVzdCBwcm92aWRlcyBubyBldnQgcGFyYW1ldGVyXG4gICAgICAgIGNhbGxiYWNrLmNhbGwodGhpcywgZXZ0IHx8IHRydWUsIG51bGwpO1xuICAgICAgICBjYWxsYmFjayA9IGZ1bmN0aW9uKCkgeyB9O1xuICAgIH07XG5cbiAgICAvLyBJRTkgbXVzdCBoYXZlIG9ucHJvZ3Jlc3MgYmUgc2V0IHRvIGEgdW5pcXVlIGZ1bmN0aW9uLlxuICAgIHgub25wcm9ncmVzcyA9IGZ1bmN0aW9uKCkgeyB9O1xuXG4gICAgeC5vbnRpbWVvdXQgPSBmdW5jdGlvbihldnQpIHtcbiAgICAgICAgY2FsbGJhY2suY2FsbCh0aGlzLCBldnQsIG51bGwpO1xuICAgICAgICBjYWxsYmFjayA9IGZ1bmN0aW9uKCkgeyB9O1xuICAgIH07XG5cbiAgICB4Lm9uYWJvcnQgPSBmdW5jdGlvbihldnQpIHtcbiAgICAgICAgY2FsbGJhY2suY2FsbCh0aGlzLCBldnQsIG51bGwpO1xuICAgICAgICBjYWxsYmFjayA9IGZ1bmN0aW9uKCkgeyB9O1xuICAgIH07XG5cbiAgICAvLyBHRVQgaXMgdGhlIG9ubHkgc3VwcG9ydGVkIEhUVFAgVmVyYiBieSBYRG9tYWluUmVxdWVzdCBhbmQgaXMgdGhlXG4gICAgLy8gb25seSBvbmUgc3VwcG9ydGVkIGhlcmUuXG4gICAgeC5vcGVuKCdHRVQnLCB1cmwsIHRydWUpO1xuXG4gICAgLy8gU2VuZCB0aGUgcmVxdWVzdC4gU2VuZGluZyBkYXRhIGlzIG5vdCBzdXBwb3J0ZWQuXG4gICAgeC5zZW5kKG51bGwpO1xuICAgIHNlbnQgPSB0cnVlO1xuXG4gICAgcmV0dXJuIHg7XG59XG5cbmlmICh0eXBlb2YgbW9kdWxlICE9PSAndW5kZWZpbmVkJykgbW9kdWxlLmV4cG9ydHMgPSBjb3JzbGl0ZTtcbiIsIm1vZHVsZS5leHBvcnRzID0gQXJyYXkuaXNBcnJheSB8fCBmdW5jdGlvbiAoYXJyKSB7XG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoYXJyKSA9PSAnW29iamVjdCBBcnJheV0nO1xufTtcbiIsIi8qXG4gTGVhZmxldCAxLjAuMiwgYSBKUyBsaWJyYXJ5IGZvciBpbnRlcmFjdGl2ZSBtYXBzLiBodHRwOi8vbGVhZmxldGpzLmNvbVxuIChjKSAyMDEwLTIwMTYgVmxhZGltaXIgQWdhZm9ua2luLCAoYykgMjAxMC0yMDExIENsb3VkTWFkZVxuKi9cbihmdW5jdGlvbiAod2luZG93LCBkb2N1bWVudCwgdW5kZWZpbmVkKSB7XHJcbnZhciBMID0ge1xyXG5cdHZlcnNpb246IFwiMS4wLjJcIlxyXG59O1xyXG5cclxuZnVuY3Rpb24gZXhwb3NlKCkge1xyXG5cdHZhciBvbGRMID0gd2luZG93Lkw7XHJcblxyXG5cdEwubm9Db25mbGljdCA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdHdpbmRvdy5MID0gb2xkTDtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH07XHJcblxyXG5cdHdpbmRvdy5MID0gTDtcclxufVxyXG5cclxuLy8gZGVmaW5lIExlYWZsZXQgZm9yIE5vZGUgbW9kdWxlIHBhdHRlcm4gbG9hZGVycywgaW5jbHVkaW5nIEJyb3dzZXJpZnlcclxuaWYgKHR5cGVvZiBtb2R1bGUgPT09ICdvYmplY3QnICYmIHR5cGVvZiBtb2R1bGUuZXhwb3J0cyA9PT0gJ29iamVjdCcpIHtcclxuXHRtb2R1bGUuZXhwb3J0cyA9IEw7XHJcblxyXG4vLyBkZWZpbmUgTGVhZmxldCBhcyBhbiBBTUQgbW9kdWxlXHJcbn0gZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kKSB7XHJcblx0ZGVmaW5lKEwpO1xyXG59XHJcblxyXG4vLyBkZWZpbmUgTGVhZmxldCBhcyBhIGdsb2JhbCBMIHZhcmlhYmxlLCBzYXZpbmcgdGhlIG9yaWdpbmFsIEwgdG8gcmVzdG9yZSBsYXRlciBpZiBuZWVkZWRcclxuaWYgKHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnKSB7XHJcblx0ZXhwb3NlKCk7XHJcbn1cclxuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBVdGlsXHJcbiAqXHJcbiAqIFZhcmlvdXMgdXRpbGl0eSBmdW5jdGlvbnMsIHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5LlxyXG4gKi9cclxuXHJcbkwuVXRpbCA9IHtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGV4dGVuZChkZXN0OiBPYmplY3QsIHNyYz86IE9iamVjdCk6IE9iamVjdFxyXG5cdC8vIE1lcmdlcyB0aGUgcHJvcGVydGllcyBvZiB0aGUgYHNyY2Agb2JqZWN0IChvciBtdWx0aXBsZSBvYmplY3RzKSBpbnRvIGBkZXN0YCBvYmplY3QgYW5kIHJldHVybnMgdGhlIGxhdHRlci4gSGFzIGFuIGBMLmV4dGVuZGAgc2hvcnRjdXQuXHJcblx0ZXh0ZW5kOiBmdW5jdGlvbiAoZGVzdCkge1xyXG5cdFx0dmFyIGksIGosIGxlbiwgc3JjO1xyXG5cclxuXHRcdGZvciAoaiA9IDEsIGxlbiA9IGFyZ3VtZW50cy5sZW5ndGg7IGogPCBsZW47IGorKykge1xyXG5cdFx0XHRzcmMgPSBhcmd1bWVudHNbal07XHJcblx0XHRcdGZvciAoaSBpbiBzcmMpIHtcclxuXHRcdFx0XHRkZXN0W2ldID0gc3JjW2ldO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gZGVzdDtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY3JlYXRlKHByb3RvOiBPYmplY3QsIHByb3BlcnRpZXM/OiBPYmplY3QpOiBPYmplY3RcclxuXHQvLyBDb21wYXRpYmlsaXR5IHBvbHlmaWxsIGZvciBbT2JqZWN0LmNyZWF0ZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvT2JqZWN0L2NyZWF0ZSlcclxuXHRjcmVhdGU6IE9iamVjdC5jcmVhdGUgfHwgKGZ1bmN0aW9uICgpIHtcclxuXHRcdGZ1bmN0aW9uIEYoKSB7fVxyXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIChwcm90bykge1xyXG5cdFx0XHRGLnByb3RvdHlwZSA9IHByb3RvO1xyXG5cdFx0XHRyZXR1cm4gbmV3IEYoKTtcclxuXHRcdH07XHJcblx0fSkoKSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGJpbmQoZm46IEZ1bmN0aW9uLCDigKYpOiBGdW5jdGlvblxyXG5cdC8vIFJldHVybnMgYSBuZXcgZnVuY3Rpb24gYm91bmQgdG8gdGhlIGFyZ3VtZW50cyBwYXNzZWQsIGxpa2UgW0Z1bmN0aW9uLnByb3RvdHlwZS5iaW5kXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9GdW5jdGlvbi9iaW5kKS5cclxuXHQvLyBIYXMgYSBgTC5iaW5kKClgIHNob3J0Y3V0LlxyXG5cdGJpbmQ6IGZ1bmN0aW9uIChmbiwgb2JqKSB7XHJcblx0XHR2YXIgc2xpY2UgPSBBcnJheS5wcm90b3R5cGUuc2xpY2U7XHJcblxyXG5cdFx0aWYgKGZuLmJpbmQpIHtcclxuXHRcdFx0cmV0dXJuIGZuLmJpbmQuYXBwbHkoZm4sIHNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIGFyZ3MgPSBzbGljZS5jYWxsKGFyZ3VtZW50cywgMik7XHJcblxyXG5cdFx0cmV0dXJuIGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0cmV0dXJuIGZuLmFwcGx5KG9iaiwgYXJncy5sZW5ndGggPyBhcmdzLmNvbmNhdChzbGljZS5jYWxsKGFyZ3VtZW50cykpIDogYXJndW1lbnRzKTtcclxuXHRcdH07XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHN0YW1wKG9iajogT2JqZWN0KTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgdW5pcXVlIElEIG9mIGFuIG9iamVjdCwgYXNzaWdpbmcgaXQgb25lIGlmIGl0IGRvZXNuJ3QgaGF2ZSBpdC5cclxuXHRzdGFtcDogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0Lyplc2xpbnQtZGlzYWJsZSAqL1xyXG5cdFx0b2JqLl9sZWFmbGV0X2lkID0gb2JqLl9sZWFmbGV0X2lkIHx8ICsrTC5VdGlsLmxhc3RJZDtcclxuXHRcdHJldHVybiBvYmouX2xlYWZsZXRfaWQ7XHJcblx0XHQvKmVzbGludC1lbmFibGUgKi9cclxuXHR9LFxyXG5cclxuXHQvLyBAcHJvcGVydHkgbGFzdElkOiBOdW1iZXJcclxuXHQvLyBMYXN0IHVuaXF1ZSBJRCB1c2VkIGJ5IFtgc3RhbXAoKWBdKCN1dGlsLXN0YW1wKVxyXG5cdGxhc3RJZDogMCxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRocm90dGxlKGZuOiBGdW5jdGlvbiwgdGltZTogTnVtYmVyLCBjb250ZXh0OiBPYmplY3QpOiBGdW5jdGlvblxyXG5cdC8vIFJldHVybnMgYSBmdW5jdGlvbiB3aGljaCBleGVjdXRlcyBmdW5jdGlvbiBgZm5gIHdpdGggdGhlIGdpdmVuIHNjb3BlIGBjb250ZXh0YFxyXG5cdC8vIChzbyB0aGF0IHRoZSBgdGhpc2Aga2V5d29yZCByZWZlcnMgdG8gYGNvbnRleHRgIGluc2lkZSBgZm5gJ3MgY29kZSkuIFRoZSBmdW5jdGlvblxyXG5cdC8vIGBmbmAgd2lsbCBiZSBjYWxsZWQgbm8gbW9yZSB0aGFuIG9uZSB0aW1lIHBlciBnaXZlbiBhbW91bnQgb2YgYHRpbWVgLiBUaGUgYXJndW1lbnRzXHJcblx0Ly8gcmVjZWl2ZWQgYnkgdGhlIGJvdW5kIGZ1bmN0aW9uIHdpbGwgYmUgYW55IGFyZ3VtZW50cyBwYXNzZWQgd2hlbiBiaW5kaW5nIHRoZVxyXG5cdC8vIGZ1bmN0aW9uLCBmb2xsb3dlZCBieSBhbnkgYXJndW1lbnRzIHBhc3NlZCB3aGVuIGludm9raW5nIHRoZSBib3VuZCBmdW5jdGlvbi5cclxuXHQvLyBIYXMgYW4gYEwuYmluZGAgc2hvcnRjdXQuXHJcblx0dGhyb3R0bGU6IGZ1bmN0aW9uIChmbiwgdGltZSwgY29udGV4dCkge1xyXG5cdFx0dmFyIGxvY2ssIGFyZ3MsIHdyYXBwZXJGbiwgbGF0ZXI7XHJcblxyXG5cdFx0bGF0ZXIgPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdC8vIHJlc2V0IGxvY2sgYW5kIGNhbGwgaWYgcXVldWVkXHJcblx0XHRcdGxvY2sgPSBmYWxzZTtcclxuXHRcdFx0aWYgKGFyZ3MpIHtcclxuXHRcdFx0XHR3cmFwcGVyRm4uYXBwbHkoY29udGV4dCwgYXJncyk7XHJcblx0XHRcdFx0YXJncyA9IGZhbHNlO1xyXG5cdFx0XHR9XHJcblx0XHR9O1xyXG5cclxuXHRcdHdyYXBwZXJGbiA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0aWYgKGxvY2spIHtcclxuXHRcdFx0XHQvLyBjYWxsZWQgdG9vIHNvb24sIHF1ZXVlIHRvIGNhbGwgbGF0ZXJcclxuXHRcdFx0XHRhcmdzID0gYXJndW1lbnRzO1xyXG5cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHQvLyBjYWxsIGFuZCBsb2NrIHVudGlsIGxhdGVyXHJcblx0XHRcdFx0Zm4uYXBwbHkoY29udGV4dCwgYXJndW1lbnRzKTtcclxuXHRcdFx0XHRzZXRUaW1lb3V0KGxhdGVyLCB0aW1lKTtcclxuXHRcdFx0XHRsb2NrID0gdHJ1ZTtcclxuXHRcdFx0fVxyXG5cdFx0fTtcclxuXHJcblx0XHRyZXR1cm4gd3JhcHBlckZuO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB3cmFwTnVtKG51bTogTnVtYmVyLCByYW5nZTogTnVtYmVyW10sIGluY2x1ZGVNYXg/OiBCb29sZWFuKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbnVtYmVyIGBudW1gIG1vZHVsbyBgcmFuZ2VgIGluIHN1Y2ggYSB3YXkgc28gaXQgbGllcyB3aXRoaW5cclxuXHQvLyBgcmFuZ2VbMF1gIGFuZCBgcmFuZ2VbMV1gLiBUaGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSBhbHdheXMgc21hbGxlciB0aGFuXHJcblx0Ly8gYHJhbmdlWzFdYCB1bmxlc3MgYGluY2x1ZGVNYXhgIGlzIHNldCB0byBgdHJ1ZWAuXHJcblx0d3JhcE51bTogZnVuY3Rpb24gKHgsIHJhbmdlLCBpbmNsdWRlTWF4KSB7XHJcblx0XHR2YXIgbWF4ID0gcmFuZ2VbMV0sXHJcblx0XHQgICAgbWluID0gcmFuZ2VbMF0sXHJcblx0XHQgICAgZCA9IG1heCAtIG1pbjtcclxuXHRcdHJldHVybiB4ID09PSBtYXggJiYgaW5jbHVkZU1heCA/IHggOiAoKHggLSBtaW4pICUgZCArIGQpICUgZCArIG1pbjtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZmFsc2VGbigpOiBGdW5jdGlvblxyXG5cdC8vIFJldHVybnMgYSBmdW5jdGlvbiB3aGljaCBhbHdheXMgcmV0dXJucyBgZmFsc2VgLlxyXG5cdGZhbHNlRm46IGZ1bmN0aW9uICgpIHsgcmV0dXJuIGZhbHNlOyB9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZm9ybWF0TnVtKG51bTogTnVtYmVyLCBkaWdpdHM/OiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBudW1iZXIgYG51bWAgcm91bmRlZCB0byBgZGlnaXRzYCBkZWNpbWFscywgb3IgdG8gNSBkZWNpbWFscyBieSBkZWZhdWx0LlxyXG5cdGZvcm1hdE51bTogZnVuY3Rpb24gKG51bSwgZGlnaXRzKSB7XHJcblx0XHR2YXIgcG93ID0gTWF0aC5wb3coMTAsIGRpZ2l0cyB8fCA1KTtcclxuXHRcdHJldHVybiBNYXRoLnJvdW5kKG51bSAqIHBvdykgLyBwb3c7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRyaW0oc3RyOiBTdHJpbmcpOiBTdHJpbmdcclxuXHQvLyBDb21wYXRpYmlsaXR5IHBvbHlmaWxsIGZvciBbU3RyaW5nLnByb3RvdHlwZS50cmltXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9TdHJpbmcvVHJpbSlcclxuXHR0cmltOiBmdW5jdGlvbiAoc3RyKSB7XHJcblx0XHRyZXR1cm4gc3RyLnRyaW0gPyBzdHIudHJpbSgpIDogc3RyLnJlcGxhY2UoL15cXHMrfFxccyskL2csICcnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gc3BsaXRXb3JkcyhzdHI6IFN0cmluZyk6IFN0cmluZ1tdXHJcblx0Ly8gVHJpbXMgYW5kIHNwbGl0cyB0aGUgc3RyaW5nIG9uIHdoaXRlc3BhY2UgYW5kIHJldHVybnMgdGhlIGFycmF5IG9mIHBhcnRzLlxyXG5cdHNwbGl0V29yZHM6IGZ1bmN0aW9uIChzdHIpIHtcclxuXHRcdHJldHVybiBMLlV0aWwudHJpbShzdHIpLnNwbGl0KC9cXHMrLyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNldE9wdGlvbnMob2JqOiBPYmplY3QsIG9wdGlvbnM6IE9iamVjdCk6IE9iamVjdFxyXG5cdC8vIE1lcmdlcyB0aGUgZ2l2ZW4gcHJvcGVydGllcyB0byB0aGUgYG9wdGlvbnNgIG9mIHRoZSBgb2JqYCBvYmplY3QsIHJldHVybmluZyB0aGUgcmVzdWx0aW5nIG9wdGlvbnMuIFNlZSBgQ2xhc3Mgb3B0aW9uc2AuIEhhcyBhbiBgTC5zZXRPcHRpb25zYCBzaG9ydGN1dC5cclxuXHRzZXRPcHRpb25zOiBmdW5jdGlvbiAob2JqLCBvcHRpb25zKSB7XHJcblx0XHRpZiAoIW9iai5oYXNPd25Qcm9wZXJ0eSgnb3B0aW9ucycpKSB7XHJcblx0XHRcdG9iai5vcHRpb25zID0gb2JqLm9wdGlvbnMgPyBMLlV0aWwuY3JlYXRlKG9iai5vcHRpb25zKSA6IHt9O1xyXG5cdFx0fVxyXG5cdFx0Zm9yICh2YXIgaSBpbiBvcHRpb25zKSB7XHJcblx0XHRcdG9iai5vcHRpb25zW2ldID0gb3B0aW9uc1tpXTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBvYmoub3B0aW9ucztcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0UGFyYW1TdHJpbmcob2JqOiBPYmplY3QsIGV4aXN0aW5nVXJsPzogU3RyaW5nLCB1cHBlcmNhc2U/OiBCb29sZWFuKTogU3RyaW5nXHJcblx0Ly8gQ29udmVydHMgYW4gb2JqZWN0IGludG8gYSBwYXJhbWV0ZXIgVVJMIHN0cmluZywgZS5nLiBge2E6IFwiZm9vXCIsIGI6IFwiYmFyXCJ9YFxyXG5cdC8vIHRyYW5zbGF0ZXMgdG8gYCc/YT1mb28mYj1iYXInYC4gSWYgYGV4aXN0aW5nVXJsYCBpcyBzZXQsIHRoZSBwYXJhbWV0ZXJzIHdpbGxcclxuXHQvLyBiZSBhcHBlbmRlZCBhdCB0aGUgZW5kLiBJZiBgdXBwZXJjYXNlYCBpcyBgdHJ1ZWAsIHRoZSBwYXJhbWV0ZXIgbmFtZXMgd2lsbFxyXG5cdC8vIGJlIHVwcGVyY2FzZWQgKGUuZy4gYCc/QT1mb28mQj1iYXInYClcclxuXHRnZXRQYXJhbVN0cmluZzogZnVuY3Rpb24gKG9iaiwgZXhpc3RpbmdVcmwsIHVwcGVyY2FzZSkge1xyXG5cdFx0dmFyIHBhcmFtcyA9IFtdO1xyXG5cdFx0Zm9yICh2YXIgaSBpbiBvYmopIHtcclxuXHRcdFx0cGFyYW1zLnB1c2goZW5jb2RlVVJJQ29tcG9uZW50KHVwcGVyY2FzZSA/IGkudG9VcHBlckNhc2UoKSA6IGkpICsgJz0nICsgZW5jb2RlVVJJQ29tcG9uZW50KG9ialtpXSkpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuICgoIWV4aXN0aW5nVXJsIHx8IGV4aXN0aW5nVXJsLmluZGV4T2YoJz8nKSA9PT0gLTEpID8gJz8nIDogJyYnKSArIHBhcmFtcy5qb2luKCcmJyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRlbXBsYXRlKHN0cjogU3RyaW5nLCBkYXRhOiBPYmplY3QpOiBTdHJpbmdcclxuXHQvLyBTaW1wbGUgdGVtcGxhdGluZyBmYWNpbGl0eSwgYWNjZXB0cyBhIHRlbXBsYXRlIHN0cmluZyBvZiB0aGUgZm9ybSBgJ0hlbGxvIHthfSwge2J9J2BcclxuXHQvLyBhbmQgYSBkYXRhIG9iamVjdCBsaWtlIGB7YTogJ2ZvbycsIGI6ICdiYXInfWAsIHJldHVybnMgZXZhbHVhdGVkIHN0cmluZ1xyXG5cdC8vIGAoJ0hlbGxvIGZvbywgYmFyJylgLiBZb3UgY2FuIGFsc28gc3BlY2lmeSBmdW5jdGlvbnMgaW5zdGVhZCBvZiBzdHJpbmdzIGZvclxyXG5cdC8vIGRhdGEgdmFsdWVzIOKAlCB0aGV5IHdpbGwgYmUgZXZhbHVhdGVkIHBhc3NpbmcgYGRhdGFgIGFzIGFuIGFyZ3VtZW50LlxyXG5cdHRlbXBsYXRlOiBmdW5jdGlvbiAoc3RyLCBkYXRhKSB7XHJcblx0XHRyZXR1cm4gc3RyLnJlcGxhY2UoTC5VdGlsLnRlbXBsYXRlUmUsIGZ1bmN0aW9uIChzdHIsIGtleSkge1xyXG5cdFx0XHR2YXIgdmFsdWUgPSBkYXRhW2tleV07XHJcblxyXG5cdFx0XHRpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRcdHRocm93IG5ldyBFcnJvcignTm8gdmFsdWUgcHJvdmlkZWQgZm9yIHZhcmlhYmxlICcgKyBzdHIpO1xyXG5cclxuXHRcdFx0fSBlbHNlIGlmICh0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicpIHtcclxuXHRcdFx0XHR2YWx1ZSA9IHZhbHVlKGRhdGEpO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiB2YWx1ZTtcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdHRlbXBsYXRlUmU6IC9cXHsgKihbXFx3X1xcLV0rKSAqXFx9L2csXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBpc0FycmF5KG9iaik6IEJvb2xlYW5cclxuXHQvLyBDb21wYXRpYmlsaXR5IHBvbHlmaWxsIGZvciBbQXJyYXkuaXNBcnJheV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvaXNBcnJheSlcclxuXHRpc0FycmF5OiBBcnJheS5pc0FycmF5IHx8IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHJldHVybiAoT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG9iaikgPT09ICdbb2JqZWN0IEFycmF5XScpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBpbmRleE9mKGFycmF5OiBBcnJheSwgZWw6IE9iamVjdCk6IE51bWJlclxyXG5cdC8vIENvbXBhdGliaWxpdHkgcG9seWZpbGwgZm9yIFtBcnJheS5wcm90b3R5cGUuaW5kZXhPZl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvQXJyYXkvaW5kZXhPZilcclxuXHRpbmRleE9mOiBmdW5jdGlvbiAoYXJyYXksIGVsKSB7XHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IGFycmF5Lmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdGlmIChhcnJheVtpXSA9PT0gZWwpIHsgcmV0dXJuIGk7IH1cclxuXHRcdH1cclxuXHRcdHJldHVybiAtMTtcclxuXHR9LFxyXG5cclxuXHQvLyBAcHJvcGVydHkgZW1wdHlJbWFnZVVybDogU3RyaW5nXHJcblx0Ly8gRGF0YSBVUkkgc3RyaW5nIGNvbnRhaW5pbmcgYSBiYXNlNjQtZW5jb2RlZCBlbXB0eSBHSUYgaW1hZ2UuXHJcblx0Ly8gVXNlZCBhcyBhIGhhY2sgdG8gZnJlZSBtZW1vcnkgZnJvbSB1bnVzZWQgaW1hZ2VzIG9uIFdlYktpdC1wb3dlcmVkXHJcblx0Ly8gbW9iaWxlIGRldmljZXMgKGJ5IHNldHRpbmcgaW1hZ2UgYHNyY2AgdG8gdGhpcyBzdHJpbmcpLlxyXG5cdGVtcHR5SW1hZ2VVcmw6ICdkYXRhOmltYWdlL2dpZjtiYXNlNjQsUjBsR09EbGhBUUFCQUFEL0FDd0FBQUFBQVFBQkFBQUNBRHM9J1xyXG59O1xyXG5cclxuKGZ1bmN0aW9uICgpIHtcclxuXHQvLyBpbnNwaXJlZCBieSBodHRwOi8vcGF1bGlyaXNoLmNvbS8yMDExL3JlcXVlc3RhbmltYXRpb25mcmFtZS1mb3Itc21hcnQtYW5pbWF0aW5nL1xyXG5cclxuXHRmdW5jdGlvbiBnZXRQcmVmaXhlZChuYW1lKSB7XHJcblx0XHRyZXR1cm4gd2luZG93Wyd3ZWJraXQnICsgbmFtZV0gfHwgd2luZG93Wydtb3onICsgbmFtZV0gfHwgd2luZG93WydtcycgKyBuYW1lXTtcclxuXHR9XHJcblxyXG5cdHZhciBsYXN0VGltZSA9IDA7XHJcblxyXG5cdC8vIGZhbGxiYWNrIGZvciBJRSA3LThcclxuXHRmdW5jdGlvbiB0aW1lb3V0RGVmZXIoZm4pIHtcclxuXHRcdHZhciB0aW1lID0gK25ldyBEYXRlKCksXHJcblx0XHQgICAgdGltZVRvQ2FsbCA9IE1hdGgubWF4KDAsIDE2IC0gKHRpbWUgLSBsYXN0VGltZSkpO1xyXG5cclxuXHRcdGxhc3RUaW1lID0gdGltZSArIHRpbWVUb0NhbGw7XHJcblx0XHRyZXR1cm4gd2luZG93LnNldFRpbWVvdXQoZm4sIHRpbWVUb0NhbGwpO1xyXG5cdH1cclxuXHJcblx0dmFyIHJlcXVlc3RGbiA9IHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgZ2V0UHJlZml4ZWQoJ1JlcXVlc3RBbmltYXRpb25GcmFtZScpIHx8IHRpbWVvdXREZWZlcixcclxuXHQgICAgY2FuY2VsRm4gPSB3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWUgfHwgZ2V0UHJlZml4ZWQoJ0NhbmNlbEFuaW1hdGlvbkZyYW1lJykgfHxcclxuXHQgICAgICAgICAgICAgICBnZXRQcmVmaXhlZCgnQ2FuY2VsUmVxdWVzdEFuaW1hdGlvbkZyYW1lJykgfHwgZnVuY3Rpb24gKGlkKSB7IHdpbmRvdy5jbGVhclRpbWVvdXQoaWQpOyB9O1xyXG5cclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHJlcXVlc3RBbmltRnJhbWUoZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0LCBpbW1lZGlhdGU/OiBCb29sZWFuKTogTnVtYmVyXHJcblx0Ly8gU2NoZWR1bGVzIGBmbmAgdG8gYmUgZXhlY3V0ZWQgd2hlbiB0aGUgYnJvd3NlciByZXBhaW50cy4gYGZuYCBpcyBib3VuZCB0b1xyXG5cdC8vIGBjb250ZXh0YCBpZiBnaXZlbi4gV2hlbiBgaW1tZWRpYXRlYCBpcyBzZXQsIGBmbmAgaXMgY2FsbGVkIGltbWVkaWF0ZWx5IGlmXHJcblx0Ly8gdGhlIGJyb3dzZXIgZG9lc24ndCBoYXZlIG5hdGl2ZSBzdXBwb3J0IGZvclxyXG5cdC8vIFtgd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZWBdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS93aW5kb3cvcmVxdWVzdEFuaW1hdGlvbkZyYW1lKSxcclxuXHQvLyBvdGhlcndpc2UgaXQncyBkZWxheWVkLiBSZXR1cm5zIGEgcmVxdWVzdCBJRCB0aGF0IGNhbiBiZSB1c2VkIHRvIGNhbmNlbCB0aGUgcmVxdWVzdC5cclxuXHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZSA9IGZ1bmN0aW9uIChmbiwgY29udGV4dCwgaW1tZWRpYXRlKSB7XHJcblx0XHRpZiAoaW1tZWRpYXRlICYmIHJlcXVlc3RGbiA9PT0gdGltZW91dERlZmVyKSB7XHJcblx0XHRcdGZuLmNhbGwoY29udGV4dCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRyZXR1cm4gcmVxdWVzdEZuLmNhbGwod2luZG93LCBMLmJpbmQoZm4sIGNvbnRleHQpKTtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gY2FuY2VsQW5pbUZyYW1lKGlkOiBOdW1iZXIpOiB1bmRlZmluZWRcclxuXHQvLyBDYW5jZWxzIGEgcHJldmlvdXMgYHJlcXVlc3RBbmltRnJhbWVgLiBTZWUgYWxzbyBbd2luZG93LmNhbmNlbEFuaW1hdGlvbkZyYW1lXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvd2luZG93L2NhbmNlbEFuaW1hdGlvbkZyYW1lKS5cclxuXHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lID0gZnVuY3Rpb24gKGlkKSB7XHJcblx0XHRpZiAoaWQpIHtcclxuXHRcdFx0Y2FuY2VsRm4uY2FsbCh3aW5kb3csIGlkKTtcclxuXHRcdH1cclxuXHR9O1xyXG59KSgpO1xyXG5cclxuLy8gc2hvcnRjdXRzIGZvciBtb3N0IHVzZWQgdXRpbGl0eSBmdW5jdGlvbnNcclxuTC5leHRlbmQgPSBMLlV0aWwuZXh0ZW5kO1xyXG5MLmJpbmQgPSBMLlV0aWwuYmluZDtcclxuTC5zdGFtcCA9IEwuVXRpbC5zdGFtcDtcclxuTC5zZXRPcHRpb25zID0gTC5VdGlsLnNldE9wdGlvbnM7XHJcblxuXG5cblxyXG4vLyBAY2xhc3MgQ2xhc3NcclxuLy8gQGFrYSBMLkNsYXNzXHJcblxyXG4vLyBAc2VjdGlvblxyXG4vLyBAdW5pbmhlcml0YWJsZVxyXG5cclxuLy8gVGhhbmtzIHRvIEpvaG4gUmVzaWcgYW5kIERlYW4gRWR3YXJkcyBmb3IgaW5zcGlyYXRpb24hXHJcblxyXG5MLkNsYXNzID0gZnVuY3Rpb24gKCkge307XHJcblxyXG5MLkNsYXNzLmV4dGVuZCA9IGZ1bmN0aW9uIChwcm9wcykge1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gZXh0ZW5kKHByb3BzOiBPYmplY3QpOiBGdW5jdGlvblxyXG5cdC8vIFtFeHRlbmRzIHRoZSBjdXJyZW50IGNsYXNzXSgjY2xhc3MtaW5oZXJpdGFuY2UpIGdpdmVuIHRoZSBwcm9wZXJ0aWVzIHRvIGJlIGluY2x1ZGVkLlxyXG5cdC8vIFJldHVybnMgYSBKYXZhc2NyaXB0IGZ1bmN0aW9uIHRoYXQgaXMgYSBjbGFzcyBjb25zdHJ1Y3RvciAodG8gYmUgY2FsbGVkIHdpdGggYG5ld2ApLlxyXG5cdHZhciBOZXdDbGFzcyA9IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHQvLyBjYWxsIHRoZSBjb25zdHJ1Y3RvclxyXG5cdFx0aWYgKHRoaXMuaW5pdGlhbGl6ZSkge1xyXG5cdFx0XHR0aGlzLmluaXRpYWxpemUuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBjYWxsIGFsbCBjb25zdHJ1Y3RvciBob29rc1xyXG5cdFx0dGhpcy5jYWxsSW5pdEhvb2tzKCk7XHJcblx0fTtcclxuXHJcblx0dmFyIHBhcmVudFByb3RvID0gTmV3Q2xhc3MuX19zdXBlcl9fID0gdGhpcy5wcm90b3R5cGU7XHJcblxyXG5cdHZhciBwcm90byA9IEwuVXRpbC5jcmVhdGUocGFyZW50UHJvdG8pO1xyXG5cdHByb3RvLmNvbnN0cnVjdG9yID0gTmV3Q2xhc3M7XHJcblxyXG5cdE5ld0NsYXNzLnByb3RvdHlwZSA9IHByb3RvO1xyXG5cclxuXHQvLyBpbmhlcml0IHBhcmVudCdzIHN0YXRpY3NcclxuXHRmb3IgKHZhciBpIGluIHRoaXMpIHtcclxuXHRcdGlmICh0aGlzLmhhc093blByb3BlcnR5KGkpICYmIGkgIT09ICdwcm90b3R5cGUnKSB7XHJcblx0XHRcdE5ld0NsYXNzW2ldID0gdGhpc1tpXTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdC8vIG1peCBzdGF0aWMgcHJvcGVydGllcyBpbnRvIHRoZSBjbGFzc1xyXG5cdGlmIChwcm9wcy5zdGF0aWNzKSB7XHJcblx0XHRMLmV4dGVuZChOZXdDbGFzcywgcHJvcHMuc3RhdGljcyk7XHJcblx0XHRkZWxldGUgcHJvcHMuc3RhdGljcztcclxuXHR9XHJcblxyXG5cdC8vIG1peCBpbmNsdWRlcyBpbnRvIHRoZSBwcm90b3R5cGVcclxuXHRpZiAocHJvcHMuaW5jbHVkZXMpIHtcclxuXHRcdEwuVXRpbC5leHRlbmQuYXBwbHkobnVsbCwgW3Byb3RvXS5jb25jYXQocHJvcHMuaW5jbHVkZXMpKTtcclxuXHRcdGRlbGV0ZSBwcm9wcy5pbmNsdWRlcztcclxuXHR9XHJcblxyXG5cdC8vIG1lcmdlIG9wdGlvbnNcclxuXHRpZiAocHJvdG8ub3B0aW9ucykge1xyXG5cdFx0cHJvcHMub3B0aW9ucyA9IEwuVXRpbC5leHRlbmQoTC5VdGlsLmNyZWF0ZShwcm90by5vcHRpb25zKSwgcHJvcHMub3B0aW9ucyk7XHJcblx0fVxyXG5cclxuXHQvLyBtaXggZ2l2ZW4gcHJvcGVydGllcyBpbnRvIHRoZSBwcm90b3R5cGVcclxuXHRMLmV4dGVuZChwcm90bywgcHJvcHMpO1xyXG5cclxuXHRwcm90by5faW5pdEhvb2tzID0gW107XHJcblxyXG5cdC8vIGFkZCBtZXRob2QgZm9yIGNhbGxpbmcgYWxsIGhvb2tzXHJcblx0cHJvdG8uY2FsbEluaXRIb29rcyA9IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHRpZiAodGhpcy5faW5pdEhvb2tzQ2FsbGVkKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdGlmIChwYXJlbnRQcm90by5jYWxsSW5pdEhvb2tzKSB7XHJcblx0XHRcdHBhcmVudFByb3RvLmNhbGxJbml0SG9va3MuY2FsbCh0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9pbml0SG9va3NDYWxsZWQgPSB0cnVlO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBwcm90by5faW5pdEhvb2tzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdHByb3RvLl9pbml0SG9va3NbaV0uY2FsbCh0aGlzKTtcclxuXHRcdH1cclxuXHR9O1xyXG5cclxuXHRyZXR1cm4gTmV3Q2xhc3M7XHJcbn07XHJcblxyXG5cclxuLy8gQGZ1bmN0aW9uIGluY2x1ZGUocHJvcGVydGllczogT2JqZWN0KTogdGhpc1xyXG4vLyBbSW5jbHVkZXMgYSBtaXhpbl0oI2NsYXNzLWluY2x1ZGVzKSBpbnRvIHRoZSBjdXJyZW50IGNsYXNzLlxyXG5MLkNsYXNzLmluY2x1ZGUgPSBmdW5jdGlvbiAocHJvcHMpIHtcclxuXHRMLmV4dGVuZCh0aGlzLnByb3RvdHlwZSwgcHJvcHMpO1xyXG5cdHJldHVybiB0aGlzO1xyXG59O1xyXG5cclxuLy8gQGZ1bmN0aW9uIG1lcmdlT3B0aW9ucyhvcHRpb25zOiBPYmplY3QpOiB0aGlzXHJcbi8vIFtNZXJnZXMgYG9wdGlvbnNgXSgjY2xhc3Mtb3B0aW9ucykgaW50byB0aGUgZGVmYXVsdHMgb2YgdGhlIGNsYXNzLlxyXG5MLkNsYXNzLm1lcmdlT3B0aW9ucyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0TC5leHRlbmQodGhpcy5wcm90b3R5cGUub3B0aW9ucywgb3B0aW9ucyk7XHJcblx0cmV0dXJuIHRoaXM7XHJcbn07XHJcblxyXG4vLyBAZnVuY3Rpb24gYWRkSW5pdEhvb2soZm46IEZ1bmN0aW9uKTogdGhpc1xyXG4vLyBBZGRzIGEgW2NvbnN0cnVjdG9yIGhvb2tdKCNjbGFzcy1jb25zdHJ1Y3Rvci1ob29rcykgdG8gdGhlIGNsYXNzLlxyXG5MLkNsYXNzLmFkZEluaXRIb29rID0gZnVuY3Rpb24gKGZuKSB7IC8vIChGdW5jdGlvbikgfHwgKFN0cmluZywgYXJncy4uLilcclxuXHR2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMSk7XHJcblxyXG5cdHZhciBpbml0ID0gdHlwZW9mIGZuID09PSAnZnVuY3Rpb24nID8gZm4gOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzW2ZuXS5hcHBseSh0aGlzLCBhcmdzKTtcclxuXHR9O1xyXG5cclxuXHR0aGlzLnByb3RvdHlwZS5faW5pdEhvb2tzID0gdGhpcy5wcm90b3R5cGUuX2luaXRIb29rcyB8fCBbXTtcclxuXHR0aGlzLnByb3RvdHlwZS5faW5pdEhvb2tzLnB1c2goaW5pdCk7XHJcblx0cmV0dXJuIHRoaXM7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBFdmVudGVkXHJcbiAqIEBha2EgTC5FdmVudGVkXHJcbiAqIEBpbmhlcml0cyBDbGFzc1xyXG4gKlxyXG4gKiBBIHNldCBvZiBtZXRob2RzIHNoYXJlZCBiZXR3ZWVuIGV2ZW50LXBvd2VyZWQgY2xhc3NlcyAobGlrZSBgTWFwYCBhbmQgYE1hcmtlcmApLiBHZW5lcmFsbHksIGV2ZW50cyBhbGxvdyB5b3UgdG8gZXhlY3V0ZSBzb21lIGZ1bmN0aW9uIHdoZW4gc29tZXRoaW5nIGhhcHBlbnMgd2l0aCBhbiBvYmplY3QgKGUuZy4gdGhlIHVzZXIgY2xpY2tzIG9uIHRoZSBtYXAsIGNhdXNpbmcgdGhlIG1hcCB0byBmaXJlIGAnY2xpY2snYCBldmVudCkuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIG1hcC5vbignY2xpY2snLCBmdW5jdGlvbihlKSB7XHJcbiAqIFx0YWxlcnQoZS5sYXRsbmcpO1xyXG4gKiB9ICk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBMZWFmbGV0IGRlYWxzIHdpdGggZXZlbnQgbGlzdGVuZXJzIGJ5IHJlZmVyZW5jZSwgc28gaWYgeW91IHdhbnQgdG8gYWRkIGEgbGlzdGVuZXIgYW5kIHRoZW4gcmVtb3ZlIGl0LCBkZWZpbmUgaXQgYXMgYSBmdW5jdGlvbjpcclxuICpcclxuICogYGBganNcclxuICogZnVuY3Rpb24gb25DbGljayhlKSB7IC4uLiB9XHJcbiAqXHJcbiAqIG1hcC5vbignY2xpY2snLCBvbkNsaWNrKTtcclxuICogbWFwLm9mZignY2xpY2snLCBvbkNsaWNrKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuXHJcbkwuRXZlbnRlZCA9IEwuQ2xhc3MuZXh0ZW5kKHtcclxuXHJcblx0LyogQG1ldGhvZCBvbih0eXBlOiBTdHJpbmcsIGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcclxuXHQgKiBBZGRzIGEgbGlzdGVuZXIgZnVuY3Rpb24gKGBmbmApIHRvIGEgcGFydGljdWxhciBldmVudCB0eXBlIG9mIHRoZSBvYmplY3QuIFlvdSBjYW4gb3B0aW9uYWxseSBzcGVjaWZ5IHRoZSBjb250ZXh0IG9mIHRoZSBsaXN0ZW5lciAob2JqZWN0IHRoZSB0aGlzIGtleXdvcmQgd2lsbCBwb2ludCB0bykuIFlvdSBjYW4gYWxzbyBwYXNzIHNldmVyYWwgc3BhY2Utc2VwYXJhdGVkIHR5cGVzIChlLmcuIGAnY2xpY2sgZGJsY2xpY2snYCkuXHJcblx0ICpcclxuXHQgKiBAYWx0ZXJuYXRpdmVcclxuXHQgKiBAbWV0aG9kIG9uKGV2ZW50TWFwOiBPYmplY3QpOiB0aGlzXHJcblx0ICogQWRkcyBhIHNldCBvZiB0eXBlL2xpc3RlbmVyIHBhaXJzLCBlLmcuIGB7Y2xpY2s6IG9uQ2xpY2ssIG1vdXNlbW92ZTogb25Nb3VzZU1vdmV9YFxyXG5cdCAqL1xyXG5cdG9uOiBmdW5jdGlvbiAodHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0Ly8gdHlwZXMgY2FuIGJlIGEgbWFwIG9mIHR5cGVzL2hhbmRsZXJzXHJcblx0XHRpZiAodHlwZW9mIHR5cGVzID09PSAnb2JqZWN0Jykge1xyXG5cdFx0XHRmb3IgKHZhciB0eXBlIGluIHR5cGVzKSB7XHJcblx0XHRcdFx0Ly8gd2UgZG9uJ3QgcHJvY2VzcyBzcGFjZS1zZXBhcmF0ZWQgZXZlbnRzIGhlcmUgZm9yIHBlcmZvcm1hbmNlO1xyXG5cdFx0XHRcdC8vIGl0J3MgYSBob3QgcGF0aCBzaW5jZSBMYXllciB1c2VzIHRoZSBvbihvYmopIHN5bnRheFxyXG5cdFx0XHRcdHRoaXMuX29uKHR5cGUsIHR5cGVzW3R5cGVdLCBmbik7XHJcblx0XHRcdH1cclxuXHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHQvLyB0eXBlcyBjYW4gYmUgYSBzdHJpbmcgb2Ygc3BhY2Utc2VwYXJhdGVkIHdvcmRzXHJcblx0XHRcdHR5cGVzID0gTC5VdGlsLnNwbGl0V29yZHModHlwZXMpO1xyXG5cclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHR5cGVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dGhpcy5fb24odHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8qIEBtZXRob2Qgb2ZmKHR5cGU6IFN0cmluZywgZm4/OiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcclxuXHQgKiBSZW1vdmVzIGEgcHJldmlvdXNseSBhZGRlZCBsaXN0ZW5lciBmdW5jdGlvbi4gSWYgbm8gZnVuY3Rpb24gaXMgc3BlY2lmaWVkLCBpdCB3aWxsIHJlbW92ZSBhbGwgdGhlIGxpc3RlbmVycyBvZiB0aGF0IHBhcnRpY3VsYXIgZXZlbnQgZnJvbSB0aGUgb2JqZWN0LiBOb3RlIHRoYXQgaWYgeW91IHBhc3NlZCBhIGN1c3RvbSBjb250ZXh0IHRvIGBvbmAsIHlvdSBtdXN0IHBhc3MgdGhlIHNhbWUgY29udGV4dCB0byBgb2ZmYCBpbiBvcmRlciB0byByZW1vdmUgdGhlIGxpc3RlbmVyLlxyXG5cdCAqXHJcblx0ICogQGFsdGVybmF0aXZlXHJcblx0ICogQG1ldGhvZCBvZmYoZXZlbnRNYXA6IE9iamVjdCk6IHRoaXNcclxuXHQgKiBSZW1vdmVzIGEgc2V0IG9mIHR5cGUvbGlzdGVuZXIgcGFpcnMuXHJcblx0ICpcclxuXHQgKiBAYWx0ZXJuYXRpdmVcclxuXHQgKiBAbWV0aG9kIG9mZjogdGhpc1xyXG5cdCAqIFJlbW92ZXMgYWxsIGxpc3RlbmVycyB0byBhbGwgZXZlbnRzIG9uIHRoZSBvYmplY3QuXHJcblx0ICovXHJcblx0b2ZmOiBmdW5jdGlvbiAodHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKCF0eXBlcykge1xyXG5cdFx0XHQvLyBjbGVhciBhbGwgbGlzdGVuZXJzIGlmIGNhbGxlZCB3aXRob3V0IGFyZ3VtZW50c1xyXG5cdFx0XHRkZWxldGUgdGhpcy5fZXZlbnRzO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAodHlwZW9mIHR5cGVzID09PSAnb2JqZWN0Jykge1xyXG5cdFx0XHRmb3IgKHZhciB0eXBlIGluIHR5cGVzKSB7XHJcblx0XHRcdFx0dGhpcy5fb2ZmKHR5cGUsIHR5cGVzW3R5cGVdLCBmbik7XHJcblx0XHRcdH1cclxuXHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0eXBlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0eXBlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuX29mZih0eXBlc1tpXSwgZm4sIGNvbnRleHQpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gYXR0YWNoIGxpc3RlbmVyICh3aXRob3V0IHN5bnRhY3RpYyBzdWdhciBub3cpXHJcblx0X29uOiBmdW5jdGlvbiAodHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHRcdHRoaXMuX2V2ZW50cyA9IHRoaXMuX2V2ZW50cyB8fCB7fTtcclxuXHJcblx0XHQvKiBnZXQvaW5pdCBsaXN0ZW5lcnMgZm9yIHR5cGUgKi9cclxuXHRcdHZhciB0eXBlTGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cdFx0aWYgKCF0eXBlTGlzdGVuZXJzKSB7XHJcblx0XHRcdHR5cGVMaXN0ZW5lcnMgPSBbXTtcclxuXHRcdFx0dGhpcy5fZXZlbnRzW3R5cGVdID0gdHlwZUxpc3RlbmVycztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoY29udGV4dCA9PT0gdGhpcykge1xyXG5cdFx0XHQvLyBMZXNzIG1lbW9yeSBmb290cHJpbnQuXHJcblx0XHRcdGNvbnRleHQgPSB1bmRlZmluZWQ7XHJcblx0XHR9XHJcblx0XHR2YXIgbmV3TGlzdGVuZXIgPSB7Zm46IGZuLCBjdHg6IGNvbnRleHR9LFxyXG5cdFx0ICAgIGxpc3RlbmVycyA9IHR5cGVMaXN0ZW5lcnM7XHJcblxyXG5cdFx0Ly8gY2hlY2sgaWYgZm4gYWxyZWFkeSB0aGVyZVxyXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGxpc3RlbmVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRpZiAobGlzdGVuZXJzW2ldLmZuID09PSBmbiAmJiBsaXN0ZW5lcnNbaV0uY3R4ID09PSBjb250ZXh0KSB7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0bGlzdGVuZXJzLnB1c2gobmV3TGlzdGVuZXIpO1xyXG5cdFx0dHlwZUxpc3RlbmVycy5jb3VudCsrO1xyXG5cdH0sXHJcblxyXG5cdF9vZmY6IGZ1bmN0aW9uICh0eXBlLCBmbiwgY29udGV4dCkge1xyXG5cdFx0dmFyIGxpc3RlbmVycyxcclxuXHRcdCAgICBpLFxyXG5cdFx0ICAgIGxlbjtcclxuXHJcblx0XHRpZiAoIXRoaXMuX2V2ZW50cykgeyByZXR1cm47IH1cclxuXHJcblx0XHRsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbdHlwZV07XHJcblxyXG5cdFx0aWYgKCFsaXN0ZW5lcnMpIHtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghZm4pIHtcclxuXHRcdFx0Ly8gU2V0IGFsbCByZW1vdmVkIGxpc3RlbmVycyB0byBub29wIHNvIHRoZXkgYXJlIG5vdCBjYWxsZWQgaWYgcmVtb3ZlIGhhcHBlbnMgaW4gZmlyZVxyXG5cdFx0XHRmb3IgKGkgPSAwLCBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHRsaXN0ZW5lcnNbaV0uZm4gPSBMLlV0aWwuZmFsc2VGbjtcclxuXHRcdFx0fVxyXG5cdFx0XHQvLyBjbGVhciBhbGwgbGlzdGVuZXJzIGZvciBhIHR5cGUgaWYgZnVuY3Rpb24gaXNuJ3Qgc3BlY2lmaWVkXHJcblx0XHRcdGRlbGV0ZSB0aGlzLl9ldmVudHNbdHlwZV07XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoY29udGV4dCA9PT0gdGhpcykge1xyXG5cdFx0XHRjb250ZXh0ID0gdW5kZWZpbmVkO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChsaXN0ZW5lcnMpIHtcclxuXHJcblx0XHRcdC8vIGZpbmQgZm4gYW5kIHJlbW92ZSBpdFxyXG5cdFx0XHRmb3IgKGkgPSAwLCBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHR2YXIgbCA9IGxpc3RlbmVyc1tpXTtcclxuXHRcdFx0XHRpZiAobC5jdHggIT09IGNvbnRleHQpIHsgY29udGludWU7IH1cclxuXHRcdFx0XHRpZiAobC5mbiA9PT0gZm4pIHtcclxuXHJcblx0XHRcdFx0XHQvLyBzZXQgdGhlIHJlbW92ZWQgbGlzdGVuZXIgdG8gbm9vcCBzbyB0aGF0J3Mgbm90IGNhbGxlZCBpZiByZW1vdmUgaGFwcGVucyBpbiBmaXJlXHJcblx0XHRcdFx0XHRsLmZuID0gTC5VdGlsLmZhbHNlRm47XHJcblxyXG5cdFx0XHRcdFx0aWYgKHRoaXMuX2ZpcmluZ0NvdW50KSB7XHJcblx0XHRcdFx0XHRcdC8qIGNvcHkgYXJyYXkgaW4gY2FzZSBldmVudHMgYXJlIGJlaW5nIGZpcmVkICovXHJcblx0XHRcdFx0XHRcdHRoaXMuX2V2ZW50c1t0eXBlXSA9IGxpc3RlbmVycyA9IGxpc3RlbmVycy5zbGljZSgpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0bGlzdGVuZXJzLnNwbGljZShpLCAxKTtcclxuXHJcblx0XHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmaXJlKHR5cGU6IFN0cmluZywgZGF0YT86IE9iamVjdCwgcHJvcGFnYXRlPzogQm9vbGVhbik6IHRoaXNcclxuXHQvLyBGaXJlcyBhbiBldmVudCBvZiB0aGUgc3BlY2lmaWVkIHR5cGUuIFlvdSBjYW4gb3B0aW9uYWxseSBwcm92aWRlIGFuIGRhdGFcclxuXHQvLyBvYmplY3Qg4oCUIHRoZSBmaXJzdCBhcmd1bWVudCBvZiB0aGUgbGlzdGVuZXIgZnVuY3Rpb24gd2lsbCBjb250YWluIGl0c1xyXG5cdC8vIHByb3BlcnRpZXMuIFRoZSBldmVudCBjYW4gb3B0aW9uYWxseSBiZSBwcm9wYWdhdGVkIHRvIGV2ZW50IHBhcmVudHMuXHJcblx0ZmlyZTogZnVuY3Rpb24gKHR5cGUsIGRhdGEsIHByb3BhZ2F0ZSkge1xyXG5cdFx0aWYgKCF0aGlzLmxpc3RlbnModHlwZSwgcHJvcGFnYXRlKSkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdHZhciBldmVudCA9IEwuVXRpbC5leHRlbmQoe30sIGRhdGEsIHt0eXBlOiB0eXBlLCB0YXJnZXQ6IHRoaXN9KTtcclxuXHJcblx0XHRpZiAodGhpcy5fZXZlbnRzKSB7XHJcblx0XHRcdHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbdHlwZV07XHJcblxyXG5cdFx0XHRpZiAobGlzdGVuZXJzKSB7XHJcblx0XHRcdFx0dGhpcy5fZmlyaW5nQ291bnQgPSAodGhpcy5fZmlyaW5nQ291bnQgKyAxKSB8fCAxO1xyXG5cdFx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHRcdHZhciBsID0gbGlzdGVuZXJzW2ldO1xyXG5cdFx0XHRcdFx0bC5mbi5jYWxsKGwuY3R4IHx8IHRoaXMsIGV2ZW50KTtcclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHRcdHRoaXMuX2ZpcmluZ0NvdW50LS07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRpZiAocHJvcGFnYXRlKSB7XHJcblx0XHRcdC8vIHByb3BhZ2F0ZSB0aGUgZXZlbnQgdG8gcGFyZW50cyAoc2V0IHdpdGggYWRkRXZlbnRQYXJlbnQpXHJcblx0XHRcdHRoaXMuX3Byb3BhZ2F0ZUV2ZW50KGV2ZW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGxpc3RlbnModHlwZTogU3RyaW5nKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIGEgcGFydGljdWxhciBldmVudCB0eXBlIGhhcyBhbnkgbGlzdGVuZXJzIGF0dGFjaGVkIHRvIGl0LlxyXG5cdGxpc3RlbnM6IGZ1bmN0aW9uICh0eXBlLCBwcm9wYWdhdGUpIHtcclxuXHRcdHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHMgJiYgdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cdFx0aWYgKGxpc3RlbmVycyAmJiBsaXN0ZW5lcnMubGVuZ3RoKSB7IHJldHVybiB0cnVlOyB9XHJcblxyXG5cdFx0aWYgKHByb3BhZ2F0ZSkge1xyXG5cdFx0XHQvLyBhbHNvIGNoZWNrIHBhcmVudHMgZm9yIGxpc3RlbmVycyBpZiBldmVudCBwcm9wYWdhdGVzXHJcblx0XHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2V2ZW50UGFyZW50cykge1xyXG5cdFx0XHRcdGlmICh0aGlzLl9ldmVudFBhcmVudHNbaWRdLmxpc3RlbnModHlwZSwgcHJvcGFnYXRlKSkgeyByZXR1cm4gdHJ1ZTsgfVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gZmFsc2U7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBvbmNlKOKApik6IHRoaXNcclxuXHQvLyBCZWhhdmVzIGFzIFtgb24o4oCmKWBdKCNldmVudGVkLW9uKSwgZXhjZXB0IHRoZSBsaXN0ZW5lciB3aWxsIG9ubHkgZ2V0IGZpcmVkIG9uY2UgYW5kIHRoZW4gcmVtb3ZlZC5cclxuXHRvbmNlOiBmdW5jdGlvbiAodHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdHRoaXMub25jZSh0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBoYW5kbGVyID0gTC5iaW5kKGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0dGhpc1xyXG5cdFx0XHQgICAgLm9mZih0eXBlcywgZm4sIGNvbnRleHQpXHJcblx0XHRcdCAgICAub2ZmKHR5cGVzLCBoYW5kbGVyLCBjb250ZXh0KTtcclxuXHRcdH0sIHRoaXMpO1xyXG5cclxuXHRcdC8vIGFkZCBhIGxpc3RlbmVyIHRoYXQncyBleGVjdXRlZCBvbmNlIGFuZCByZW1vdmVkIGFmdGVyIHRoYXRcclxuXHRcdHJldHVybiB0aGlzXHJcblx0XHQgICAgLm9uKHR5cGVzLCBmbiwgY29udGV4dClcclxuXHRcdCAgICAub24odHlwZXMsIGhhbmRsZXIsIGNvbnRleHQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkRXZlbnRQYXJlbnQob2JqOiBFdmVudGVkKTogdGhpc1xyXG5cdC8vIEFkZHMgYW4gZXZlbnQgcGFyZW50IC0gYW4gYEV2ZW50ZWRgIHRoYXQgd2lsbCByZWNlaXZlIHByb3BhZ2F0ZWQgZXZlbnRzXHJcblx0YWRkRXZlbnRQYXJlbnQ6IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHRoaXMuX2V2ZW50UGFyZW50cyA9IHRoaXMuX2V2ZW50UGFyZW50cyB8fCB7fTtcclxuXHRcdHRoaXMuX2V2ZW50UGFyZW50c1tMLnN0YW1wKG9iaildID0gb2JqO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVFdmVudFBhcmVudChvYmo6IEV2ZW50ZWQpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyBhbiBldmVudCBwYXJlbnQsIHNvIGl0IHdpbGwgc3RvcCByZWNlaXZpbmcgcHJvcGFnYXRlZCBldmVudHNcclxuXHRyZW1vdmVFdmVudFBhcmVudDogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0aWYgKHRoaXMuX2V2ZW50UGFyZW50cykge1xyXG5cdFx0XHRkZWxldGUgdGhpcy5fZXZlbnRQYXJlbnRzW0wuc3RhbXAob2JqKV07XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfcHJvcGFnYXRlRXZlbnQ6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9ldmVudFBhcmVudHMpIHtcclxuXHRcdFx0dGhpcy5fZXZlbnRQYXJlbnRzW2lkXS5maXJlKGUudHlwZSwgTC5leHRlbmQoe2xheWVyOiBlLnRhcmdldH0sIGUpLCB0cnVlKTtcclxuXHRcdH1cclxuXHR9XHJcbn0pO1xyXG5cclxudmFyIHByb3RvID0gTC5FdmVudGVkLnByb3RvdHlwZTtcclxuXHJcbi8vIGFsaWFzZXM7IHdlIHNob3VsZCBkaXRjaCB0aG9zZSBldmVudHVhbGx5XHJcblxyXG4vLyBAbWV0aG9kIGFkZEV2ZW50TGlzdGVuZXIo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYG9uKOKApilgXSgjZXZlbnRlZC1vbilcclxucHJvdG8uYWRkRXZlbnRMaXN0ZW5lciA9IHByb3RvLm9uO1xyXG5cclxuLy8gQG1ldGhvZCByZW1vdmVFdmVudExpc3RlbmVyKOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BvZmYo4oCmKWBdKCNldmVudGVkLW9mZilcclxuXHJcbi8vIEBtZXRob2QgY2xlYXJBbGxFdmVudExpc3RlbmVycyjigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgb2ZmKClgXSgjZXZlbnRlZC1vZmYpXHJcbnByb3RvLnJlbW92ZUV2ZW50TGlzdGVuZXIgPSBwcm90by5jbGVhckFsbEV2ZW50TGlzdGVuZXJzID0gcHJvdG8ub2ZmO1xyXG5cclxuLy8gQG1ldGhvZCBhZGRPbmVUaW1lRXZlbnRMaXN0ZW5lcijigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgb25jZSjigKYpYF0oI2V2ZW50ZWQtb25jZSlcclxucHJvdG8uYWRkT25lVGltZUV2ZW50TGlzdGVuZXIgPSBwcm90by5vbmNlO1xyXG5cclxuLy8gQG1ldGhvZCBmaXJlRXZlbnQo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYGZpcmUo4oCmKWBdKCNldmVudGVkLWZpcmUpXHJcbnByb3RvLmZpcmVFdmVudCA9IHByb3RvLmZpcmU7XHJcblxyXG4vLyBAbWV0aG9kIGhhc0V2ZW50TGlzdGVuZXJzKOKApik6IEJvb2xlYW5cclxuLy8gQWxpYXMgdG8gW2BsaXN0ZW5zKOKApilgXSgjZXZlbnRlZC1saXN0ZW5zKVxyXG5wcm90by5oYXNFdmVudExpc3RlbmVycyA9IHByb3RvLmxpc3RlbnM7XHJcblxyXG5MLk1peGluID0ge0V2ZW50czogcHJvdG99O1xyXG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIEJyb3dzZXJcclxuICogQGFrYSBMLkJyb3dzZXJcclxuICpcclxuICogQSBuYW1lc3BhY2Ugd2l0aCBzdGF0aWMgcHJvcGVydGllcyBmb3IgYnJvd3Nlci9mZWF0dXJlIGRldGVjdGlvbiB1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseS5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogaWYgKEwuQnJvd3Nlci5pZWx0OSkge1xyXG4gKiAgIGFsZXJ0KCdVcGdyYWRlIHlvdXIgYnJvd3NlciwgZHVkZSEnKTtcclxuICogfVxyXG4gKiBgYGBcclxuICovXHJcblxyXG4oZnVuY3Rpb24gKCkge1xyXG5cclxuXHR2YXIgdWEgPSBuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCksXHJcblx0ICAgIGRvYyA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCxcclxuXHJcblx0ICAgIGllID0gJ0FjdGl2ZVhPYmplY3QnIGluIHdpbmRvdyxcclxuXHJcblx0ICAgIHdlYmtpdCAgICA9IHVhLmluZGV4T2YoJ3dlYmtpdCcpICE9PSAtMSxcclxuXHQgICAgcGhhbnRvbWpzID0gdWEuaW5kZXhPZigncGhhbnRvbScpICE9PSAtMSxcclxuXHQgICAgYW5kcm9pZDIzID0gdWEuc2VhcmNoKCdhbmRyb2lkIFsyM10nKSAhPT0gLTEsXHJcblx0ICAgIGNocm9tZSAgICA9IHVhLmluZGV4T2YoJ2Nocm9tZScpICE9PSAtMSxcclxuXHQgICAgZ2Vja28gICAgID0gdWEuaW5kZXhPZignZ2Vja28nKSAhPT0gLTEgICYmICF3ZWJraXQgJiYgIXdpbmRvdy5vcGVyYSAmJiAhaWUsXHJcblxyXG5cdCAgICB3aW4gPSBuYXZpZ2F0b3IucGxhdGZvcm0uaW5kZXhPZignV2luJykgPT09IDAsXHJcblxyXG5cdCAgICBtb2JpbGUgPSB0eXBlb2Ygb3JpZW50YXRpb24gIT09ICd1bmRlZmluZWQnIHx8IHVhLmluZGV4T2YoJ21vYmlsZScpICE9PSAtMSxcclxuXHQgICAgbXNQb2ludGVyID0gIXdpbmRvdy5Qb2ludGVyRXZlbnQgJiYgd2luZG93Lk1TUG9pbnRlckV2ZW50LFxyXG5cdCAgICBwb2ludGVyID0gd2luZG93LlBvaW50ZXJFdmVudCB8fCBtc1BvaW50ZXIsXHJcblxyXG5cdCAgICBpZTNkID0gaWUgJiYgKCd0cmFuc2l0aW9uJyBpbiBkb2Muc3R5bGUpLFxyXG5cdCAgICB3ZWJraXQzZCA9ICgnV2ViS2l0Q1NTTWF0cml4JyBpbiB3aW5kb3cpICYmICgnbTExJyBpbiBuZXcgd2luZG93LldlYktpdENTU01hdHJpeCgpKSAmJiAhYW5kcm9pZDIzLFxyXG5cdCAgICBnZWNrbzNkID0gJ01velBlcnNwZWN0aXZlJyBpbiBkb2Muc3R5bGUsXHJcblx0ICAgIG9wZXJhMTIgPSAnT1RyYW5zaXRpb24nIGluIGRvYy5zdHlsZTtcclxuXHJcblxyXG5cdHZhciB0b3VjaCA9ICF3aW5kb3cuTF9OT19UT1VDSCAmJiAocG9pbnRlciB8fCAnb250b3VjaHN0YXJ0JyBpbiB3aW5kb3cgfHxcclxuXHRcdFx0KHdpbmRvdy5Eb2N1bWVudFRvdWNoICYmIGRvY3VtZW50IGluc3RhbmNlb2Ygd2luZG93LkRvY3VtZW50VG91Y2gpKTtcclxuXHJcblx0TC5Ccm93c2VyID0ge1xyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBpZTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgSW50ZXJuZXQgRXhwbG9yZXIgdmVyc2lvbnMgKG5vdCBFZGdlKS5cclxuXHRcdGllOiBpZSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgaWVsdDk6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgSW50ZXJuZXQgRXhwbG9yZXIgdmVyc2lvbnMgbGVzcyB0aGFuIDkuXHJcblx0XHRpZWx0OTogaWUgJiYgIWRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGVkZ2U6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIEVkZ2Ugd2ViIGJyb3dzZXIuXHJcblx0XHRlZGdlOiAnbXNMYXVuY2hVcmknIGluIG5hdmlnYXRvciAmJiAhKCdkb2N1bWVudE1vZGUnIGluIGRvY3VtZW50KSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgd2Via2l0OiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHdlYmtpdC1iYXNlZCBicm93c2VycyBsaWtlIENocm9tZSBhbmQgU2FmYXJpIChpbmNsdWRpbmcgbW9iaWxlIHZlcnNpb25zKS5cclxuXHRcdHdlYmtpdDogd2Via2l0LFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBnZWNrbzogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBnZWNrby1iYXNlZCBicm93c2VycyBsaWtlIEZpcmVmb3guXHJcblx0XHRnZWNrbzogZ2Vja28sXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGFuZHJvaWQ6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYW55IGJyb3dzZXIgcnVubmluZyBvbiBhbiBBbmRyb2lkIHBsYXRmb3JtLlxyXG5cdFx0YW5kcm9pZDogdWEuaW5kZXhPZignYW5kcm9pZCcpICE9PSAtMSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgYW5kcm9pZDIzOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGJyb3dzZXJzIHJ1bm5pbmcgb24gQW5kcm9pZCAyIG9yIEFuZHJvaWQgMy5cclxuXHRcdGFuZHJvaWQyMzogYW5kcm9pZDIzLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBjaHJvbWU6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIENocm9tZSBicm93c2VyLlxyXG5cdFx0Y2hyb21lOiBjaHJvbWUsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHNhZmFyaTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciB0aGUgU2FmYXJpIGJyb3dzZXIuXHJcblx0XHRzYWZhcmk6ICFjaHJvbWUgJiYgdWEuaW5kZXhPZignc2FmYXJpJykgIT09IC0xLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgd2luOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgd2hlbiB0aGUgYnJvd3NlciBpcyBydW5uaW5nIGluIGEgV2luZG93cyBwbGF0Zm9ybVxyXG5cdFx0d2luOiB3aW4sXHJcblxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBpZTNkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCBJbnRlcm5ldCBFeHBsb3JlciB2ZXJzaW9ucyBzdXBwb3J0aW5nIENTUyB0cmFuc2Zvcm1zLlxyXG5cdFx0aWUzZDogaWUzZCxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgd2Via2l0M2Q6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3Igd2Via2l0LWJhc2VkIGJyb3dzZXJzIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcblx0XHR3ZWJraXQzZDogd2Via2l0M2QsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGdlY2tvM2Q6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgZ2Vja28tYmFzZWQgYnJvd3NlcnMgc3VwcG9ydGluZyBDU1MgdHJhbnNmb3Jtcy5cclxuXHRcdGdlY2tvM2Q6IGdlY2tvM2QsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG9wZXJhMTI6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIE9wZXJhIGJyb3dzZXIgc3VwcG9ydGluZyBDU1MgdHJhbnNmb3JtcyAodmVyc2lvbiAxMiBvciBsYXRlcikuXHJcblx0XHRvcGVyYTEyOiBvcGVyYTEyLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBhbnkzZDogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgYnJvd3NlcnMgc3VwcG9ydGluZyBDU1MgdHJhbnNmb3Jtcy5cclxuXHRcdGFueTNkOiAhd2luZG93LkxfRElTQUJMRV8zRCAmJiAoaWUzZCB8fCB3ZWJraXQzZCB8fCBnZWNrbzNkKSAmJiAhb3BlcmExMiAmJiAhcGhhbnRvbWpzLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbW9iaWxlOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCBicm93c2VycyBydW5uaW5nIGluIGEgbW9iaWxlIGRldmljZS5cclxuXHRcdG1vYmlsZTogbW9iaWxlLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtb2JpbGVXZWJraXQ6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIHdlYmtpdC1iYXNlZCBicm93c2VycyBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcblx0XHRtb2JpbGVXZWJraXQ6IG1vYmlsZSAmJiB3ZWJraXQsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG1vYmlsZVdlYmtpdDNkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCB3ZWJraXQtYmFzZWQgYnJvd3NlcnMgaW4gYSBtb2JpbGUgZGV2aWNlIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcblx0XHRtb2JpbGVXZWJraXQzZDogbW9iaWxlICYmIHdlYmtpdDNkLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtb2JpbGVPcGVyYTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciB0aGUgT3BlcmEgYnJvd3NlciBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcblx0XHRtb2JpbGVPcGVyYTogbW9iaWxlICYmIHdpbmRvdy5vcGVyYSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbW9iaWxlR2Vja286IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgZ2Vja28tYmFzZWQgYnJvd3NlcnMgcnVubmluZyBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcblx0XHRtb2JpbGVHZWNrbzogbW9iaWxlICYmIGdlY2tvLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgdG91Y2g6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIGJyb3dzZXJzIHN1cHBvcnRpbmcgW3RvdWNoIGV2ZW50c10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL1RvdWNoX2V2ZW50cykuXHJcblx0XHR0b3VjaDogISF0b3VjaCxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbXNQb2ludGVyOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGJyb3dzZXJzIGltcGxlbWVudGluZyB0aGUgTWljcm9zb2Z0IHRvdWNoIGV2ZW50cyBtb2RlbCAobm90YWJseSBJRTEwKS5cclxuXHRcdG1zUG9pbnRlcjogISFtc1BvaW50ZXIsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHBvaW50ZXI6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIGJyb3dzZXJzIHN1cHBvcnRpbmcgW3BvaW50ZXIgZXZlbnRzXShodHRwczovL21zZG4ubWljcm9zb2Z0LmNvbS9lbi11cy9saWJyYXJ5L2RuNDMzMjQ0JTI4dj12cy44NSUyOS5hc3B4KS5cclxuXHRcdHBvaW50ZXI6ICEhcG9pbnRlcixcclxuXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHJldGluYTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBicm93c2VycyBvbiBhIGhpZ2gtcmVzb2x1dGlvbiBcInJldGluYVwiIHNjcmVlbi5cclxuXHRcdHJldGluYTogKHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvIHx8ICh3aW5kb3cuc2NyZWVuLmRldmljZVhEUEkgLyB3aW5kb3cuc2NyZWVuLmxvZ2ljYWxYRFBJKSkgPiAxXHJcblx0fTtcclxuXHJcbn0oKSk7XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBQb2ludFxyXG4gKiBAYWthIEwuUG9pbnRcclxuICpcclxuICogUmVwcmVzZW50cyBhIHBvaW50IHdpdGggYHhgIGFuZCBgeWAgY29vcmRpbmF0ZXMgaW4gcGl4ZWxzLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgcG9pbnQgPSBMLnBvaW50KDIwMCwgMzAwKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIEFsbCBMZWFmbGV0IG1ldGhvZHMgYW5kIG9wdGlvbnMgdGhhdCBhY2NlcHQgYFBvaW50YCBvYmplY3RzIGFsc28gYWNjZXB0IHRoZW0gaW4gYSBzaW1wbGUgQXJyYXkgZm9ybSAodW5sZXNzIG5vdGVkIG90aGVyd2lzZSksIHNvIHRoZXNlIGxpbmVzIGFyZSBlcXVpdmFsZW50OlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBtYXAucGFuQnkoWzIwMCwgMzAwXSk7XHJcbiAqIG1hcC5wYW5CeShMLnBvaW50KDIwMCwgMzAwKSk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuUG9pbnQgPSBmdW5jdGlvbiAoeCwgeSwgcm91bmQpIHtcclxuXHQvLyBAcHJvcGVydHkgeDogTnVtYmVyOyBUaGUgYHhgIGNvb3JkaW5hdGUgb2YgdGhlIHBvaW50XHJcblx0dGhpcy54ID0gKHJvdW5kID8gTWF0aC5yb3VuZCh4KSA6IHgpO1xyXG5cdC8vIEBwcm9wZXJ0eSB5OiBOdW1iZXI7IFRoZSBgeWAgY29vcmRpbmF0ZSBvZiB0aGUgcG9pbnRcclxuXHR0aGlzLnkgPSAocm91bmQgPyBNYXRoLnJvdW5kKHkpIDogeSk7XHJcbn07XHJcblxyXG5MLlBvaW50LnByb3RvdHlwZSA9IHtcclxuXHJcblx0Ly8gQG1ldGhvZCBjbG9uZSgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgYSBjb3B5IG9mIHRoZSBjdXJyZW50IHBvaW50LlxyXG5cdGNsb25lOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy54LCB0aGlzLnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkKG90aGVyUG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2YgYWRkaXRpb24gb2YgdGhlIGN1cnJlbnQgYW5kIHRoZSBnaXZlbiBwb2ludHMuXHJcblx0YWRkOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdC8vIG5vbi1kZXN0cnVjdGl2ZSwgcmV0dXJucyBhIG5ldyBwb2ludFxyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fYWRkKEwucG9pbnQocG9pbnQpKTtcclxuXHR9LFxyXG5cclxuXHRfYWRkOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdC8vIGRlc3RydWN0aXZlLCB1c2VkIGRpcmVjdGx5IGZvciBwZXJmb3JtYW5jZSBpbiBzaXR1YXRpb25zIHdoZXJlIGl0J3Mgc2FmZSB0byBtb2RpZnkgZXhpc3RpbmcgcG9pbnRcclxuXHRcdHRoaXMueCArPSBwb2ludC54O1xyXG5cdFx0dGhpcy55ICs9IHBvaW50Lnk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHN1YnRyYWN0KG90aGVyUG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2Ygc3VidHJhY3Rpb24gb2YgdGhlIGdpdmVuIHBvaW50IGZyb20gdGhlIGN1cnJlbnQuXHJcblx0c3VidHJhY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fc3VidHJhY3QoTC5wb2ludChwb2ludCkpO1xyXG5cdH0sXHJcblxyXG5cdF9zdWJ0cmFjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHR0aGlzLnggLT0gcG9pbnQueDtcclxuXHRcdHRoaXMueSAtPSBwb2ludC55O1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXZpZGVCeShudW06IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcmVzdWx0IG9mIGRpdmlzaW9uIG9mIHRoZSBjdXJyZW50IHBvaW50IGJ5IHRoZSBnaXZlbiBudW1iZXIuXHJcblx0ZGl2aWRlQnk6IGZ1bmN0aW9uIChudW0pIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX2RpdmlkZUJ5KG51bSk7XHJcblx0fSxcclxuXHJcblx0X2RpdmlkZUJ5OiBmdW5jdGlvbiAobnVtKSB7XHJcblx0XHR0aGlzLnggLz0gbnVtO1xyXG5cdFx0dGhpcy55IC89IG51bTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbXVsdGlwbHlCeShudW06IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcmVzdWx0IG9mIG11bHRpcGxpY2F0aW9uIG9mIHRoZSBjdXJyZW50IHBvaW50IGJ5IHRoZSBnaXZlbiBudW1iZXIuXHJcblx0bXVsdGlwbHlCeTogZnVuY3Rpb24gKG51bSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fbXVsdGlwbHlCeShudW0pO1xyXG5cdH0sXHJcblxyXG5cdF9tdWx0aXBseUJ5OiBmdW5jdGlvbiAobnVtKSB7XHJcblx0XHR0aGlzLnggKj0gbnVtO1xyXG5cdFx0dGhpcy55ICo9IG51bTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2NhbGVCeShzY2FsZTogUG9pbnQpOiBQb2ludFxyXG5cdC8vIE11bHRpcGx5IGVhY2ggY29vcmRpbmF0ZSBvZiB0aGUgY3VycmVudCBwb2ludCBieSBlYWNoIGNvb3JkaW5hdGUgb2ZcclxuXHQvLyBgc2NhbGVgLiBJbiBsaW5lYXIgYWxnZWJyYSB0ZXJtcywgbXVsdGlwbHkgdGhlIHBvaW50IGJ5IHRoZVxyXG5cdC8vIFtzY2FsaW5nIG1hdHJpeF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2NhbGluZ18lMjhnZW9tZXRyeSUyOSNNYXRyaXhfcmVwcmVzZW50YXRpb24pXHJcblx0Ly8gZGVmaW5lZCBieSBgc2NhbGVgLlxyXG5cdHNjYWxlQnk6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHRoaXMueCAqIHBvaW50LngsIHRoaXMueSAqIHBvaW50LnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW5zY2FsZUJ5KHNjYWxlOiBQb2ludCk6IFBvaW50XHJcblx0Ly8gSW52ZXJzZSBvZiBgc2NhbGVCeWAuIERpdmlkZSBlYWNoIGNvb3JkaW5hdGUgb2YgdGhlIGN1cnJlbnQgcG9pbnQgYnlcclxuXHQvLyBlYWNoIGNvb3JkaW5hdGUgb2YgYHNjYWxlYC5cclxuXHR1bnNjYWxlQnk6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHRoaXMueCAvIHBvaW50LngsIHRoaXMueSAvIHBvaW50LnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgcm91bmQoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIGEgY29weSBvZiB0aGUgY3VycmVudCBwb2ludCB3aXRoIHJvdW5kZWQgY29vcmRpbmF0ZXMuXHJcblx0cm91bmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX3JvdW5kKCk7XHJcblx0fSxcclxuXHJcblx0X3JvdW5kOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnggPSBNYXRoLnJvdW5kKHRoaXMueCk7XHJcblx0XHR0aGlzLnkgPSBNYXRoLnJvdW5kKHRoaXMueSk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGZsb29yKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIGNvcHkgb2YgdGhlIGN1cnJlbnQgcG9pbnQgd2l0aCBmbG9vcmVkIGNvb3JkaW5hdGVzIChyb3VuZGVkIGRvd24pLlxyXG5cdGZsb29yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5jbG9uZSgpLl9mbG9vcigpO1xyXG5cdH0sXHJcblxyXG5cdF9mbG9vcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy54ID0gTWF0aC5mbG9vcih0aGlzLngpO1xyXG5cdFx0dGhpcy55ID0gTWF0aC5mbG9vcih0aGlzLnkpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjZWlsKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIGNvcHkgb2YgdGhlIGN1cnJlbnQgcG9pbnQgd2l0aCBjZWlsZWQgY29vcmRpbmF0ZXMgKHJvdW5kZWQgdXApLlxyXG5cdGNlaWw6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX2NlaWwoKTtcclxuXHR9LFxyXG5cclxuXHRfY2VpbDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy54ID0gTWF0aC5jZWlsKHRoaXMueCk7XHJcblx0XHR0aGlzLnkgPSBNYXRoLmNlaWwodGhpcy55KTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzdGFuY2VUbyhvdGhlclBvaW50OiBQb2ludCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGNhcnRlc2lhbiBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBjdXJyZW50IGFuZCB0aGUgZ2l2ZW4gcG9pbnRzLlxyXG5cdGRpc3RhbmNlVG86IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cG9pbnQgPSBMLnBvaW50KHBvaW50KTtcclxuXHJcblx0XHR2YXIgeCA9IHBvaW50LnggLSB0aGlzLngsXHJcblx0XHQgICAgeSA9IHBvaW50LnkgLSB0aGlzLnk7XHJcblxyXG5cdFx0cmV0dXJuIE1hdGguc3FydCh4ICogeCArIHkgKiB5KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVxdWFscyhvdGhlclBvaW50OiBQb2ludCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZ2l2ZW4gcG9pbnQgaGFzIHRoZSBzYW1lIGNvb3JkaW5hdGVzLlxyXG5cdGVxdWFsczogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRwb2ludCA9IEwucG9pbnQocG9pbnQpO1xyXG5cclxuXHRcdHJldHVybiBwb2ludC54ID09PSB0aGlzLnggJiZcclxuXHRcdCAgICAgICBwb2ludC55ID09PSB0aGlzLnk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjb250YWlucyhvdGhlclBvaW50OiBQb2ludCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBib3RoIGNvb3JkaW5hdGVzIG9mIHRoZSBnaXZlbiBwb2ludCBhcmUgbGVzcyB0aGFuIHRoZSBjb3JyZXNwb25kaW5nIGN1cnJlbnQgcG9pbnQgY29vcmRpbmF0ZXMgKGluIGFic29sdXRlIHZhbHVlcykuXHJcblx0Y29udGFpbnM6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cG9pbnQgPSBMLnBvaW50KHBvaW50KTtcclxuXHJcblx0XHRyZXR1cm4gTWF0aC5hYnMocG9pbnQueCkgPD0gTWF0aC5hYnModGhpcy54KSAmJlxyXG5cdFx0ICAgICAgIE1hdGguYWJzKHBvaW50LnkpIDw9IE1hdGguYWJzKHRoaXMueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b1N0cmluZygpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwb2ludCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLlxyXG5cdHRvU3RyaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gJ1BvaW50KCcgK1xyXG5cdFx0ICAgICAgICBMLlV0aWwuZm9ybWF0TnVtKHRoaXMueCkgKyAnLCAnICtcclxuXHRcdCAgICAgICAgTC5VdGlsLmZvcm1hdE51bSh0aGlzLnkpICsgJyknO1xyXG5cdH1cclxufTtcclxuXHJcbi8vIEBmYWN0b3J5IEwucG9pbnQoeDogTnVtYmVyLCB5OiBOdW1iZXIsIHJvdW5kPzogQm9vbGVhbilcclxuLy8gQ3JlYXRlcyBhIFBvaW50IG9iamVjdCB3aXRoIHRoZSBnaXZlbiBgeGAgYW5kIGB5YCBjb29yZGluYXRlcy4gSWYgb3B0aW9uYWwgYHJvdW5kYCBpcyBzZXQgdG8gdHJ1ZSwgcm91bmRzIHRoZSBgeGAgYW5kIGB5YCB2YWx1ZXMuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5wb2ludChjb29yZHM6IE51bWJlcltdKVxyXG4vLyBFeHBlY3RzIGFuIGFycmF5IG9mIHRoZSBmb3JtIGBbeCwgeV1gIGluc3RlYWQuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5wb2ludChjb29yZHM6IE9iamVjdClcclxuLy8gRXhwZWN0cyBhIHBsYWluIG9iamVjdCBvZiB0aGUgZm9ybSBge3g6IE51bWJlciwgeTogTnVtYmVyfWAgaW5zdGVhZC5cclxuTC5wb2ludCA9IGZ1bmN0aW9uICh4LCB5LCByb3VuZCkge1xyXG5cdGlmICh4IGluc3RhbmNlb2YgTC5Qb2ludCkge1xyXG5cdFx0cmV0dXJuIHg7XHJcblx0fVxyXG5cdGlmIChMLlV0aWwuaXNBcnJheSh4KSkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHhbMF0sIHhbMV0pO1xyXG5cdH1cclxuXHRpZiAoeCA9PT0gdW5kZWZpbmVkIHx8IHggPT09IG51bGwpIHtcclxuXHRcdHJldHVybiB4O1xyXG5cdH1cclxuXHRpZiAodHlwZW9mIHggPT09ICdvYmplY3QnICYmICd4JyBpbiB4ICYmICd5JyBpbiB4KSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoeC54LCB4LnkpO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IEwuUG9pbnQoeCwgeSwgcm91bmQpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQm91bmRzXHJcbiAqIEBha2EgTC5Cb3VuZHNcclxuICpcclxuICogUmVwcmVzZW50cyBhIHJlY3Rhbmd1bGFyIGFyZWEgaW4gcGl4ZWwgY29vcmRpbmF0ZXMuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBwMSA9IEwucG9pbnQoMTAsIDEwKSxcclxuICogcDIgPSBMLnBvaW50KDQwLCA2MCksXHJcbiAqIGJvdW5kcyA9IEwuYm91bmRzKHAxLCBwMik7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBBbGwgTGVhZmxldCBtZXRob2RzIHRoYXQgYWNjZXB0IGBCb3VuZHNgIG9iamVjdHMgYWxzbyBhY2NlcHQgdGhlbSBpbiBhIHNpbXBsZSBBcnJheSBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlIGJvdW5kcyBleGFtcGxlIGFib3ZlIGNhbiBiZSBwYXNzZWQgbGlrZSB0aGlzOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBvdGhlckJvdW5kcy5pbnRlcnNlY3RzKFtbMTAsIDEwXSwgWzQwLCA2MF1dKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5Cb3VuZHMgPSBmdW5jdGlvbiAoYSwgYikge1xyXG5cdGlmICghYSkgeyByZXR1cm47IH1cclxuXHJcblx0dmFyIHBvaW50cyA9IGIgPyBbYSwgYl0gOiBhO1xyXG5cclxuXHRmb3IgKHZhciBpID0gMCwgbGVuID0gcG9pbnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHR0aGlzLmV4dGVuZChwb2ludHNbaV0pO1xyXG5cdH1cclxufTtcclxuXHJcbkwuQm91bmRzLnByb3RvdHlwZSA9IHtcclxuXHQvLyBAbWV0aG9kIGV4dGVuZChwb2ludDogUG9pbnQpOiB0aGlzXHJcblx0Ly8gRXh0ZW5kcyB0aGUgYm91bmRzIHRvIGNvbnRhaW4gdGhlIGdpdmVuIHBvaW50LlxyXG5cdGV4dGVuZDogZnVuY3Rpb24gKHBvaW50KSB7IC8vIChQb2ludClcclxuXHRcdHBvaW50ID0gTC5wb2ludChwb2ludCk7XHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG1pbjogUG9pbnRcclxuXHRcdC8vIFRoZSB0b3AgbGVmdCBjb3JuZXIgb2YgdGhlIHJlY3RhbmdsZS5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtYXg6IFBvaW50XHJcblx0XHQvLyBUaGUgYm90dG9tIHJpZ2h0IGNvcm5lciBvZiB0aGUgcmVjdGFuZ2xlLlxyXG5cdFx0aWYgKCF0aGlzLm1pbiAmJiAhdGhpcy5tYXgpIHtcclxuXHRcdFx0dGhpcy5taW4gPSBwb2ludC5jbG9uZSgpO1xyXG5cdFx0XHR0aGlzLm1heCA9IHBvaW50LmNsb25lKCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLm1pbi54ID0gTWF0aC5taW4ocG9pbnQueCwgdGhpcy5taW4ueCk7XHJcblx0XHRcdHRoaXMubWF4LnggPSBNYXRoLm1heChwb2ludC54LCB0aGlzLm1heC54KTtcclxuXHRcdFx0dGhpcy5taW4ueSA9IE1hdGgubWluKHBvaW50LnksIHRoaXMubWluLnkpO1xyXG5cdFx0XHR0aGlzLm1heC55ID0gTWF0aC5tYXgocG9pbnQueSwgdGhpcy5tYXgueSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENlbnRlcihyb3VuZD86IEJvb2xlYW4pOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIGNlbnRlciBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKHJvdW5kKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoXHJcblx0XHQgICAgICAgICh0aGlzLm1pbi54ICsgdGhpcy5tYXgueCkgLyAyLFxyXG5cdFx0ICAgICAgICAodGhpcy5taW4ueSArIHRoaXMubWF4LnkpIC8gMiwgcm91bmQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm90dG9tTGVmdCgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIGJvdHRvbS1sZWZ0IHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0Qm90dG9tTGVmdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHRoaXMubWluLngsIHRoaXMubWF4LnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0VG9wUmlnaHQoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSB0b3AtcmlnaHQgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXRUb3BSaWdodDogZnVuY3Rpb24gKCkgeyAvLyAtPiBQb2ludFxyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KHRoaXMubWF4LngsIHRoaXMubWluLnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0U2l6ZSgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHNpemUgb2YgdGhlIGdpdmVuIGJvdW5kc1xyXG5cdGdldFNpemU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm1heC5zdWJ0cmFjdCh0aGlzLm1pbik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjb250YWlucyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgY29udGFpbnMgdGhlIGdpdmVuIG9uZS5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zKHBvaW50OiBQb2ludCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGNvbnRhaW5zIHRoZSBnaXZlbiBwb2ludC5cclxuXHRjb250YWluczogZnVuY3Rpb24gKG9iaikge1xyXG5cdFx0dmFyIG1pbiwgbWF4O1xyXG5cclxuXHRcdGlmICh0eXBlb2Ygb2JqWzBdID09PSAnbnVtYmVyJyB8fCBvYmogaW5zdGFuY2VvZiBMLlBvaW50KSB7XHJcblx0XHRcdG9iaiA9IEwucG9pbnQob2JqKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG9iaiA9IEwuYm91bmRzKG9iaik7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG9iaiBpbnN0YW5jZW9mIEwuQm91bmRzKSB7XHJcblx0XHRcdG1pbiA9IG9iai5taW47XHJcblx0XHRcdG1heCA9IG9iai5tYXg7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRtaW4gPSBtYXggPSBvYmo7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIChtaW4ueCA+PSB0aGlzLm1pbi54KSAmJlxyXG5cdFx0ICAgICAgIChtYXgueCA8PSB0aGlzLm1heC54KSAmJlxyXG5cdFx0ICAgICAgIChtaW4ueSA+PSB0aGlzLm1pbi55KSAmJlxyXG5cdFx0ICAgICAgIChtYXgueSA8PSB0aGlzLm1heC55KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGludGVyc2VjdHMob3RoZXJCb3VuZHM6IEJvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGludGVyc2VjdHMgdGhlIGdpdmVuIGJvdW5kcy4gVHdvIGJvdW5kc1xyXG5cdC8vIGludGVyc2VjdCBpZiB0aGV5IGhhdmUgYXQgbGVhc3Qgb25lIHBvaW50IGluIGNvbW1vbi5cclxuXHRpbnRlcnNlY3RzOiBmdW5jdGlvbiAoYm91bmRzKSB7IC8vIChCb3VuZHMpIC0+IEJvb2xlYW5cclxuXHRcdGJvdW5kcyA9IEwuYm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0dmFyIG1pbiA9IHRoaXMubWluLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMubWF4LFxyXG5cdFx0ICAgIG1pbjIgPSBib3VuZHMubWluLFxyXG5cdFx0ICAgIG1heDIgPSBib3VuZHMubWF4LFxyXG5cdFx0ICAgIHhJbnRlcnNlY3RzID0gKG1heDIueCA+PSBtaW4ueCkgJiYgKG1pbjIueCA8PSBtYXgueCksXHJcblx0XHQgICAgeUludGVyc2VjdHMgPSAobWF4Mi55ID49IG1pbi55KSAmJiAobWluMi55IDw9IG1heC55KTtcclxuXHJcblx0XHRyZXR1cm4geEludGVyc2VjdHMgJiYgeUludGVyc2VjdHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBvdmVybGFwcyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgb3ZlcmxhcHMgdGhlIGdpdmVuIGJvdW5kcy4gVHdvIGJvdW5kc1xyXG5cdC8vIG92ZXJsYXAgaWYgdGhlaXIgaW50ZXJzZWN0aW9uIGlzIGFuIGFyZWEuXHJcblx0b3ZlcmxhcHM6IGZ1bmN0aW9uIChib3VuZHMpIHsgLy8gKEJvdW5kcykgLT4gQm9vbGVhblxyXG5cdFx0Ym91bmRzID0gTC5ib3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgbWluID0gdGhpcy5taW4sXHJcblx0XHQgICAgbWF4ID0gdGhpcy5tYXgsXHJcblx0XHQgICAgbWluMiA9IGJvdW5kcy5taW4sXHJcblx0XHQgICAgbWF4MiA9IGJvdW5kcy5tYXgsXHJcblx0XHQgICAgeE92ZXJsYXBzID0gKG1heDIueCA+IG1pbi54KSAmJiAobWluMi54IDwgbWF4LngpLFxyXG5cdFx0ICAgIHlPdmVybGFwcyA9IChtYXgyLnkgPiBtaW4ueSkgJiYgKG1pbjIueSA8IG1heC55KTtcclxuXHJcblx0XHRyZXR1cm4geE92ZXJsYXBzICYmIHlPdmVybGFwcztcclxuXHR9LFxyXG5cclxuXHRpc1ZhbGlkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gISEodGhpcy5taW4gJiYgdGhpcy5tYXgpO1xyXG5cdH1cclxufTtcclxuXHJcblxyXG4vLyBAZmFjdG9yeSBMLmJvdW5kcyh0b3BMZWZ0OiBQb2ludCwgYm90dG9tUmlnaHQ6IFBvaW50KVxyXG4vLyBDcmVhdGVzIGEgQm91bmRzIG9iamVjdCBmcm9tIHR3byBjb29yZGluYXRlcyAodXN1YWxseSB0b3AtbGVmdCBhbmQgYm90dG9tLXJpZ2h0IGNvcm5lcnMpLlxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5ib3VuZHMocG9pbnRzOiBQb2ludFtdKVxyXG4vLyBDcmVhdGVzIGEgQm91bmRzIG9iamVjdCBmcm9tIHRoZSBwb2ludHMgaXQgY29udGFpbnNcclxuTC5ib3VuZHMgPSBmdW5jdGlvbiAoYSwgYikge1xyXG5cdGlmICghYSB8fCBhIGluc3RhbmNlb2YgTC5Cb3VuZHMpIHtcclxuXHRcdHJldHVybiBhO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IEwuQm91bmRzKGEsIGIpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgVHJhbnNmb3JtYXRpb25cclxuICogQGFrYSBMLlRyYW5zZm9ybWF0aW9uXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYW4gYWZmaW5lIHRyYW5zZm9ybWF0aW9uOiBhIHNldCBvZiBjb2VmZmljaWVudHMgYGFgLCBgYmAsIGBjYCwgYGRgXHJcbiAqIGZvciB0cmFuc2Zvcm1pbmcgYSBwb2ludCBvZiBhIGZvcm0gYCh4LCB5KWAgaW50byBgKGEqeCArIGIsIGMqeSArIGQpYCBhbmQgZG9pbmdcclxuICogdGhlIHJldmVyc2UuIFVzZWQgYnkgTGVhZmxldCBpbiBpdHMgcHJvamVjdGlvbnMgY29kZS5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIHRyYW5zZm9ybWF0aW9uID0gbmV3IEwuVHJhbnNmb3JtYXRpb24oMiwgNSwgLTEsIDEwKSxcclxuICogXHRwID0gTC5wb2ludCgxLCAyKSxcclxuICogXHRwMiA9IHRyYW5zZm9ybWF0aW9uLnRyYW5zZm9ybShwKSwgLy8gIEwucG9pbnQoNywgOClcclxuICogXHRwMyA9IHRyYW5zZm9ybWF0aW9uLnVudHJhbnNmb3JtKHAyKTsgLy8gIEwucG9pbnQoMSwgMilcclxuICogYGBgXHJcbiAqL1xyXG5cclxuXHJcbi8vIGZhY3RvcnkgbmV3IEwuVHJhbnNmb3JtYXRpb24oYTogTnVtYmVyLCBiOiBOdW1iZXIsIGM6IE51bWJlciwgZDogTnVtYmVyKVxyXG4vLyBDcmVhdGVzIGEgYFRyYW5zZm9ybWF0aW9uYCBvYmplY3Qgd2l0aCB0aGUgZ2l2ZW4gY29lZmZpY2llbnRzLlxyXG5MLlRyYW5zZm9ybWF0aW9uID0gZnVuY3Rpb24gKGEsIGIsIGMsIGQpIHtcclxuXHR0aGlzLl9hID0gYTtcclxuXHR0aGlzLl9iID0gYjtcclxuXHR0aGlzLl9jID0gYztcclxuXHR0aGlzLl9kID0gZDtcclxufTtcclxuXHJcbkwuVHJhbnNmb3JtYXRpb24ucHJvdG90eXBlID0ge1xyXG5cdC8vIEBtZXRob2QgdHJhbnNmb3JtKHBvaW50OiBQb2ludCwgc2NhbGU/OiBOdW1iZXIpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgYSB0cmFuc2Zvcm1lZCBwb2ludCwgb3B0aW9uYWxseSBtdWx0aXBsaWVkIGJ5IHRoZSBnaXZlbiBzY2FsZS5cclxuXHQvLyBPbmx5IGFjY2VwdHMgYWN0dWFsIGBMLlBvaW50YCBpbnN0YW5jZXMsIG5vdCBhcnJheXMuXHJcblx0dHJhbnNmb3JtOiBmdW5jdGlvbiAocG9pbnQsIHNjYWxlKSB7IC8vIChQb2ludCwgTnVtYmVyKSAtPiBQb2ludFxyXG5cdFx0cmV0dXJuIHRoaXMuX3RyYW5zZm9ybShwb2ludC5jbG9uZSgpLCBzY2FsZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gZGVzdHJ1Y3RpdmUgdHJhbnNmb3JtIChmYXN0ZXIpXHJcblx0X3RyYW5zZm9ybTogZnVuY3Rpb24gKHBvaW50LCBzY2FsZSkge1xyXG5cdFx0c2NhbGUgPSBzY2FsZSB8fCAxO1xyXG5cdFx0cG9pbnQueCA9IHNjYWxlICogKHRoaXMuX2EgKiBwb2ludC54ICsgdGhpcy5fYik7XHJcblx0XHRwb2ludC55ID0gc2NhbGUgKiAodGhpcy5fYyAqIHBvaW50LnkgKyB0aGlzLl9kKTtcclxuXHRcdHJldHVybiBwb2ludDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVudHJhbnNmb3JtKHBvaW50OiBQb2ludCwgc2NhbGU/OiBOdW1iZXIpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHJldmVyc2UgdHJhbnNmb3JtYXRpb24gb2YgdGhlIGdpdmVuIHBvaW50LCBvcHRpb25hbGx5IGRpdmlkZWRcclxuXHQvLyBieSB0aGUgZ2l2ZW4gc2NhbGUuIE9ubHkgYWNjZXB0cyBhY3R1YWwgYEwuUG9pbnRgIGluc3RhbmNlcywgbm90IGFycmF5cy5cclxuXHR1bnRyYW5zZm9ybTogZnVuY3Rpb24gKHBvaW50LCBzY2FsZSkge1xyXG5cdFx0c2NhbGUgPSBzY2FsZSB8fCAxO1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KFxyXG5cdFx0ICAgICAgICAocG9pbnQueCAvIHNjYWxlIC0gdGhpcy5fYikgLyB0aGlzLl9hLFxyXG5cdFx0ICAgICAgICAocG9pbnQueSAvIHNjYWxlIC0gdGhpcy5fZCkgLyB0aGlzLl9jKTtcclxuXHR9XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgRG9tVXRpbFxyXG4gKlxyXG4gKiBVdGlsaXR5IGZ1bmN0aW9ucyB0byB3b3JrIHdpdGggdGhlIFtET01dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9Eb2N1bWVudF9PYmplY3RfTW9kZWwpXHJcbiAqIHRyZWUsIHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5LlxyXG4gKlxyXG4gKiBNb3N0IGZ1bmN0aW9ucyBleHBlY3Rpbmcgb3IgcmV0dXJuaW5nIGEgYEhUTUxFbGVtZW50YCBhbHNvIHdvcmsgZm9yXHJcbiAqIFNWRyBlbGVtZW50cy4gVGhlIG9ubHkgZGlmZmVyZW5jZSBpcyB0aGF0IGNsYXNzZXMgcmVmZXIgdG8gQ1NTIGNsYXNzZXNcclxuICogaW4gSFRNTCBhbmQgU1ZHIGNsYXNzZXMgaW4gU1ZHLlxyXG4gKi9cclxuXHJcbkwuRG9tVXRpbCA9IHtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldChpZDogU3RyaW5nfEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIGFuIGVsZW1lbnQgZ2l2ZW4gaXRzIERPTSBpZCwgb3IgcmV0dXJucyB0aGUgZWxlbWVudCBpdHNlbGZcclxuXHQvLyBpZiBpdCB3YXMgcGFzc2VkIGRpcmVjdGx5LlxyXG5cdGdldDogZnVuY3Rpb24gKGlkKSB7XHJcblx0XHRyZXR1cm4gdHlwZW9mIGlkID09PSAnc3RyaW5nJyA/IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKSA6IGlkO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBnZXRTdHlsZShlbDogSFRNTEVsZW1lbnQsIHN0eWxlQXR0cmliOiBTdHJpbmcpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIHRoZSB2YWx1ZSBmb3IgYSBjZXJ0YWluIHN0eWxlIGF0dHJpYnV0ZSBvbiBhbiBlbGVtZW50LFxyXG5cdC8vIGluY2x1ZGluZyBjb21wdXRlZCB2YWx1ZXMgb3IgdmFsdWVzIHNldCB0aHJvdWdoIENTUy5cclxuXHRnZXRTdHlsZTogZnVuY3Rpb24gKGVsLCBzdHlsZSkge1xyXG5cclxuXHRcdHZhciB2YWx1ZSA9IGVsLnN0eWxlW3N0eWxlXSB8fCAoZWwuY3VycmVudFN0eWxlICYmIGVsLmN1cnJlbnRTdHlsZVtzdHlsZV0pO1xyXG5cclxuXHRcdGlmICgoIXZhbHVlIHx8IHZhbHVlID09PSAnYXV0bycpICYmIGRvY3VtZW50LmRlZmF1bHRWaWV3KSB7XHJcblx0XHRcdHZhciBjc3MgPSBkb2N1bWVudC5kZWZhdWx0Vmlldy5nZXRDb21wdXRlZFN0eWxlKGVsLCBudWxsKTtcclxuXHRcdFx0dmFsdWUgPSBjc3MgPyBjc3Nbc3R5bGVdIDogbnVsbDtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdmFsdWUgPT09ICdhdXRvJyA/IG51bGwgOiB2YWx1ZTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY3JlYXRlKHRhZ05hbWU6IFN0cmluZywgY2xhc3NOYW1lPzogU3RyaW5nLCBjb250YWluZXI/OiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gQ3JlYXRlcyBhbiBIVE1MIGVsZW1lbnQgd2l0aCBgdGFnTmFtZWAsIHNldHMgaXRzIGNsYXNzIHRvIGBjbGFzc05hbWVgLCBhbmQgb3B0aW9uYWxseSBhcHBlbmRzIGl0IHRvIGBjb250YWluZXJgIGVsZW1lbnQuXHJcblx0Y3JlYXRlOiBmdW5jdGlvbiAodGFnTmFtZSwgY2xhc3NOYW1lLCBjb250YWluZXIpIHtcclxuXHJcblx0XHR2YXIgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHRhZ05hbWUpO1xyXG5cdFx0ZWwuY2xhc3NOYW1lID0gY2xhc3NOYW1lIHx8ICcnO1xyXG5cclxuXHRcdGlmIChjb250YWluZXIpIHtcclxuXHRcdFx0Y29udGFpbmVyLmFwcGVuZENoaWxkKGVsKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gZWw7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHJlbW92ZShlbDogSFRNTEVsZW1lbnQpXHJcblx0Ly8gUmVtb3ZlcyBgZWxgIGZyb20gaXRzIHBhcmVudCBlbGVtZW50XHJcblx0cmVtb3ZlOiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdHZhciBwYXJlbnQgPSBlbC5wYXJlbnROb2RlO1xyXG5cdFx0aWYgKHBhcmVudCkge1xyXG5cdFx0XHRwYXJlbnQucmVtb3ZlQ2hpbGQoZWwpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBlbXB0eShlbDogSFRNTEVsZW1lbnQpXHJcblx0Ly8gUmVtb3ZlcyBhbGwgb2YgYGVsYCdzIGNoaWxkcmVuIGVsZW1lbnRzIGZyb20gYGVsYFxyXG5cdGVtcHR5OiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdHdoaWxlIChlbC5maXJzdENoaWxkKSB7XHJcblx0XHRcdGVsLnJlbW92ZUNoaWxkKGVsLmZpcnN0Q2hpbGQpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0b0Zyb250KGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBNYWtlcyBgZWxgIHRoZSBsYXN0IGNoaWxkcmVuIG9mIGl0cyBwYXJlbnQsIHNvIGl0IHJlbmRlcnMgaW4gZnJvbnQgb2YgdGhlIG90aGVyIGNoaWxkcmVuLlxyXG5cdHRvRnJvbnQ6IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0ZWwucGFyZW50Tm9kZS5hcHBlbmRDaGlsZChlbCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRvQmFjayhlbDogSFRNTEVsZW1lbnQpXHJcblx0Ly8gTWFrZXMgYGVsYCB0aGUgZmlyc3QgY2hpbGRyZW4gb2YgaXRzIHBhcmVudCwgc28gaXQgcmVuZGVycyBiYWNrIGZyb20gdGhlIG90aGVyIGNoaWxkcmVuLlxyXG5cdHRvQmFjazogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHR2YXIgcGFyZW50ID0gZWwucGFyZW50Tm9kZTtcclxuXHRcdHBhcmVudC5pbnNlcnRCZWZvcmUoZWwsIHBhcmVudC5maXJzdENoaWxkKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gaGFzQ2xhc3MoZWw6IEhUTUxFbGVtZW50LCBuYW1lOiBTdHJpbmcpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGVsZW1lbnQncyBjbGFzcyBhdHRyaWJ1dGUgY29udGFpbnMgYG5hbWVgLlxyXG5cdGhhc0NsYXNzOiBmdW5jdGlvbiAoZWwsIG5hbWUpIHtcclxuXHRcdGlmIChlbC5jbGFzc0xpc3QgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRyZXR1cm4gZWwuY2xhc3NMaXN0LmNvbnRhaW5zKG5hbWUpO1xyXG5cdFx0fVxyXG5cdFx0dmFyIGNsYXNzTmFtZSA9IEwuRG9tVXRpbC5nZXRDbGFzcyhlbCk7XHJcblx0XHRyZXR1cm4gY2xhc3NOYW1lLmxlbmd0aCA+IDAgJiYgbmV3IFJlZ0V4cCgnKF58XFxcXHMpJyArIG5hbWUgKyAnKFxcXFxzfCQpJykudGVzdChjbGFzc05hbWUpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBhZGRDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbWU6IFN0cmluZylcclxuXHQvLyBBZGRzIGBuYW1lYCB0byB0aGUgZWxlbWVudCdzIGNsYXNzIGF0dHJpYnV0ZS5cclxuXHRhZGRDbGFzczogZnVuY3Rpb24gKGVsLCBuYW1lKSB7XHJcblx0XHRpZiAoZWwuY2xhc3NMaXN0ICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0dmFyIGNsYXNzZXMgPSBMLlV0aWwuc3BsaXRXb3JkcyhuYW1lKTtcclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGNsYXNzZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHRlbC5jbGFzc0xpc3QuYWRkKGNsYXNzZXNbaV0pO1xyXG5cdFx0XHR9XHJcblx0XHR9IGVsc2UgaWYgKCFMLkRvbVV0aWwuaGFzQ2xhc3MoZWwsIG5hbWUpKSB7XHJcblx0XHRcdHZhciBjbGFzc05hbWUgPSBMLkRvbVV0aWwuZ2V0Q2xhc3MoZWwpO1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0Q2xhc3MoZWwsIChjbGFzc05hbWUgPyBjbGFzc05hbWUgKyAnICcgOiAnJykgKyBuYW1lKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gcmVtb3ZlQ2xhc3MoZWw6IEhUTUxFbGVtZW50LCBuYW1lOiBTdHJpbmcpXHJcblx0Ly8gUmVtb3ZlcyBgbmFtZWAgZnJvbSB0aGUgZWxlbWVudCdzIGNsYXNzIGF0dHJpYnV0ZS5cclxuXHRyZW1vdmVDbGFzczogZnVuY3Rpb24gKGVsLCBuYW1lKSB7XHJcblx0XHRpZiAoZWwuY2xhc3NMaXN0ICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0ZWwuY2xhc3NMaXN0LnJlbW92ZShuYW1lKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRDbGFzcyhlbCwgTC5VdGlsLnRyaW0oKCcgJyArIEwuRG9tVXRpbC5nZXRDbGFzcyhlbCkgKyAnICcpLnJlcGxhY2UoJyAnICsgbmFtZSArICcgJywgJyAnKSkpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbWU6IFN0cmluZylcclxuXHQvLyBTZXRzIHRoZSBlbGVtZW50J3MgY2xhc3MuXHJcblx0c2V0Q2xhc3M6IGZ1bmN0aW9uIChlbCwgbmFtZSkge1xyXG5cdFx0aWYgKGVsLmNsYXNzTmFtZS5iYXNlVmFsID09PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0ZWwuY2xhc3NOYW1lID0gbmFtZTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdC8vIGluIGNhc2Ugb2YgU1ZHIGVsZW1lbnRcclxuXHRcdFx0ZWwuY2xhc3NOYW1lLmJhc2VWYWwgPSBuYW1lO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBnZXRDbGFzcyhlbDogSFRNTEVsZW1lbnQpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBlbGVtZW50J3MgY2xhc3MuXHJcblx0Z2V0Q2xhc3M6IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0cmV0dXJuIGVsLmNsYXNzTmFtZS5iYXNlVmFsID09PSB1bmRlZmluZWQgPyBlbC5jbGFzc05hbWUgOiBlbC5jbGFzc05hbWUuYmFzZVZhbDtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gc2V0T3BhY2l0eShlbDogSFRNTEVsZW1lbnQsIG9wYWNpdHk6IE51bWJlcilcclxuXHQvLyBTZXQgdGhlIG9wYWNpdHkgb2YgYW4gZWxlbWVudCAoaW5jbHVkaW5nIG9sZCBJRSBzdXBwb3J0KS5cclxuXHQvLyBgb3BhY2l0eWAgbXVzdCBiZSBhIG51bWJlciBmcm9tIGAwYCB0byBgMWAuXHJcblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKGVsLCB2YWx1ZSkge1xyXG5cclxuXHRcdGlmICgnb3BhY2l0eScgaW4gZWwuc3R5bGUpIHtcclxuXHRcdFx0ZWwuc3R5bGUub3BhY2l0eSA9IHZhbHVlO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoJ2ZpbHRlcicgaW4gZWwuc3R5bGUpIHtcclxuXHRcdFx0TC5Eb21VdGlsLl9zZXRPcGFjaXR5SUUoZWwsIHZhbHVlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfc2V0T3BhY2l0eUlFOiBmdW5jdGlvbiAoZWwsIHZhbHVlKSB7XHJcblx0XHR2YXIgZmlsdGVyID0gZmFsc2UsXHJcblx0XHQgICAgZmlsdGVyTmFtZSA9ICdEWEltYWdlVHJhbnNmb3JtLk1pY3Jvc29mdC5BbHBoYSc7XHJcblxyXG5cdFx0Ly8gZmlsdGVycyBjb2xsZWN0aW9uIHRocm93cyBhbiBlcnJvciBpZiB3ZSB0cnkgdG8gcmV0cmlldmUgYSBmaWx0ZXIgdGhhdCBkb2Vzbid0IGV4aXN0XHJcblx0XHR0cnkge1xyXG5cdFx0XHRmaWx0ZXIgPSBlbC5maWx0ZXJzLml0ZW0oZmlsdGVyTmFtZSk7XHJcblx0XHR9IGNhdGNoIChlKSB7XHJcblx0XHRcdC8vIGRvbid0IHNldCBvcGFjaXR5IHRvIDEgaWYgd2UgaGF2ZW4ndCBhbHJlYWR5IHNldCBhbiBvcGFjaXR5LFxyXG5cdFx0XHQvLyBpdCBpc24ndCBuZWVkZWQgYW5kIGJyZWFrcyB0cmFuc3BhcmVudCBwbmdzLlxyXG5cdFx0XHRpZiAodmFsdWUgPT09IDEpIHsgcmV0dXJuOyB9XHJcblx0XHR9XHJcblxyXG5cdFx0dmFsdWUgPSBNYXRoLnJvdW5kKHZhbHVlICogMTAwKTtcclxuXHJcblx0XHRpZiAoZmlsdGVyKSB7XHJcblx0XHRcdGZpbHRlci5FbmFibGVkID0gKHZhbHVlICE9PSAxMDApO1xyXG5cdFx0XHRmaWx0ZXIuT3BhY2l0eSA9IHZhbHVlO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZWwuc3R5bGUuZmlsdGVyICs9ICcgcHJvZ2lkOicgKyBmaWx0ZXJOYW1lICsgJyhvcGFjaXR5PScgKyB2YWx1ZSArICcpJztcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gdGVzdFByb3AocHJvcHM6IFN0cmluZ1tdKTogU3RyaW5nfGZhbHNlXHJcblx0Ly8gR29lcyB0aHJvdWdoIHRoZSBhcnJheSBvZiBzdHlsZSBuYW1lcyBhbmQgcmV0dXJucyB0aGUgZmlyc3QgbmFtZVxyXG5cdC8vIHRoYXQgaXMgYSB2YWxpZCBzdHlsZSBuYW1lIGZvciBhbiBlbGVtZW50LiBJZiBubyBzdWNoIG5hbWUgaXMgZm91bmQsXHJcblx0Ly8gaXQgcmV0dXJucyBmYWxzZS4gVXNlZnVsIGZvciB2ZW5kb3ItcHJlZml4ZWQgc3R5bGVzIGxpa2UgYHRyYW5zZm9ybWAuXHJcblx0dGVzdFByb3A6IGZ1bmN0aW9uIChwcm9wcykge1xyXG5cclxuXHRcdHZhciBzdHlsZSA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdGlmIChwcm9wc1tpXSBpbiBzdHlsZSkge1xyXG5cdFx0XHRcdHJldHVybiBwcm9wc1tpXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGZhbHNlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRUcmFuc2Zvcm0oZWw6IEhUTUxFbGVtZW50LCBvZmZzZXQ6IFBvaW50LCBzY2FsZT86IE51bWJlcilcclxuXHQvLyBSZXNldHMgdGhlIDNEIENTUyB0cmFuc2Zvcm0gb2YgYGVsYCBzbyBpdCBpcyB0cmFuc2xhdGVkIGJ5IGBvZmZzZXRgIHBpeGVsc1xyXG5cdC8vIGFuZCBvcHRpb25hbGx5IHNjYWxlZCBieSBgc2NhbGVgLiBEb2VzIG5vdCBoYXZlIGFuIGVmZmVjdCBpZiB0aGVcclxuXHQvLyBicm93c2VyIGRvZXNuJ3Qgc3VwcG9ydCAzRCBDU1MgdHJhbnNmb3Jtcy5cclxuXHRzZXRUcmFuc2Zvcm06IGZ1bmN0aW9uIChlbCwgb2Zmc2V0LCBzY2FsZSkge1xyXG5cdFx0dmFyIHBvcyA9IG9mZnNldCB8fCBuZXcgTC5Qb2ludCgwLCAwKTtcclxuXHJcblx0XHRlbC5zdHlsZVtMLkRvbVV0aWwuVFJBTlNGT1JNXSA9XHJcblx0XHRcdChMLkJyb3dzZXIuaWUzZCA/XHJcblx0XHRcdFx0J3RyYW5zbGF0ZSgnICsgcG9zLnggKyAncHgsJyArIHBvcy55ICsgJ3B4KScgOlxyXG5cdFx0XHRcdCd0cmFuc2xhdGUzZCgnICsgcG9zLnggKyAncHgsJyArIHBvcy55ICsgJ3B4LDApJykgK1xyXG5cdFx0XHQoc2NhbGUgPyAnIHNjYWxlKCcgKyBzY2FsZSArICcpJyA6ICcnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gc2V0UG9zaXRpb24oZWw6IEhUTUxFbGVtZW50LCBwb3NpdGlvbjogUG9pbnQpXHJcblx0Ly8gU2V0cyB0aGUgcG9zaXRpb24gb2YgYGVsYCB0byBjb29yZGluYXRlcyBzcGVjaWZpZWQgYnkgYHBvc2l0aW9uYCxcclxuXHQvLyB1c2luZyBDU1MgdHJhbnNsYXRlIG9yIHRvcC9sZWZ0IHBvc2l0aW9uaW5nIGRlcGVuZGluZyBvbiB0aGUgYnJvd3NlclxyXG5cdC8vICh1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseSB0byBwb3NpdGlvbiBpdHMgbGF5ZXJzKS5cclxuXHRzZXRQb3NpdGlvbjogZnVuY3Rpb24gKGVsLCBwb2ludCkgeyAvLyAoSFRNTEVsZW1lbnQsIFBvaW50WywgQm9vbGVhbl0pXHJcblxyXG5cdFx0Lyplc2xpbnQtZGlzYWJsZSAqL1xyXG5cdFx0ZWwuX2xlYWZsZXRfcG9zID0gcG9pbnQ7XHJcblx0XHQvKmVzbGludC1lbmFibGUgKi9cclxuXHJcblx0XHRpZiAoTC5Ccm93c2VyLmFueTNkKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0oZWwsIHBvaW50KTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGVsLnN0eWxlLmxlZnQgPSBwb2ludC54ICsgJ3B4JztcclxuXHRcdFx0ZWwuc3R5bGUudG9wID0gcG9pbnQueSArICdweCc7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldFBvc2l0aW9uKGVsOiBIVE1MRWxlbWVudCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgY29vcmRpbmF0ZXMgb2YgYW4gZWxlbWVudCBwcmV2aW91c2x5IHBvc2l0aW9uZWQgd2l0aCBzZXRQb3NpdGlvbi5cclxuXHRnZXRQb3NpdGlvbjogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHQvLyB0aGlzIG1ldGhvZCBpcyBvbmx5IHVzZWQgZm9yIGVsZW1lbnRzIHByZXZpb3VzbHkgcG9zaXRpb25lZCB1c2luZyBzZXRQb3NpdGlvbixcclxuXHRcdC8vIHNvIGl0J3Mgc2FmZSB0byBjYWNoZSB0aGUgcG9zaXRpb24gZm9yIHBlcmZvcm1hbmNlXHJcblxyXG5cdFx0cmV0dXJuIGVsLl9sZWFmbGV0X3BvcyB8fCBuZXcgTC5Qb2ludCgwLCAwKTtcclxuXHR9XHJcbn07XHJcblxyXG5cclxuKGZ1bmN0aW9uICgpIHtcclxuXHQvLyBwcmVmaXggc3R5bGUgcHJvcGVydHkgbmFtZXNcclxuXHJcblx0Ly8gQHByb3BlcnR5IFRSQU5TRk9STTogU3RyaW5nXHJcblx0Ly8gVmVuZG9yLXByZWZpeGVkIGZyYW5zZm9ybSBzdHlsZSBuYW1lIChlLmcuIGAnd2Via2l0VHJhbnNmb3JtJ2AgZm9yIFdlYktpdCkuXHJcblx0TC5Eb21VdGlsLlRSQU5TRk9STSA9IEwuRG9tVXRpbC50ZXN0UHJvcChcclxuXHRcdFx0Wyd0cmFuc2Zvcm0nLCAnV2Via2l0VHJhbnNmb3JtJywgJ09UcmFuc2Zvcm0nLCAnTW96VHJhbnNmb3JtJywgJ21zVHJhbnNmb3JtJ10pO1xyXG5cclxuXHJcblx0Ly8gd2Via2l0VHJhbnNpdGlvbiBjb21lcyBmaXJzdCBiZWNhdXNlIHNvbWUgYnJvd3NlciB2ZXJzaW9ucyB0aGF0IGRyb3AgdmVuZG9yIHByZWZpeCBkb24ndCBkb1xyXG5cdC8vIHRoZSBzYW1lIGZvciB0aGUgdHJhbnNpdGlvbmVuZCBldmVudCwgaW4gcGFydGljdWxhciB0aGUgQW5kcm9pZCA0LjEgc3RvY2sgYnJvd3NlclxyXG5cclxuXHQvLyBAcHJvcGVydHkgVFJBTlNJVElPTjogU3RyaW5nXHJcblx0Ly8gVmVuZG9yLXByZWZpeGVkIHRyYW5zZm9ybSBzdHlsZSBuYW1lLlxyXG5cdHZhciB0cmFuc2l0aW9uID0gTC5Eb21VdGlsLlRSQU5TSVRJT04gPSBMLkRvbVV0aWwudGVzdFByb3AoXHJcblx0XHRcdFsnd2Via2l0VHJhbnNpdGlvbicsICd0cmFuc2l0aW9uJywgJ09UcmFuc2l0aW9uJywgJ01velRyYW5zaXRpb24nLCAnbXNUcmFuc2l0aW9uJ10pO1xyXG5cclxuXHRMLkRvbVV0aWwuVFJBTlNJVElPTl9FTkQgPVxyXG5cdFx0XHR0cmFuc2l0aW9uID09PSAnd2Via2l0VHJhbnNpdGlvbicgfHwgdHJhbnNpdGlvbiA9PT0gJ09UcmFuc2l0aW9uJyA/IHRyYW5zaXRpb24gKyAnRW5kJyA6ICd0cmFuc2l0aW9uZW5kJztcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGRpc2FibGVUZXh0U2VsZWN0aW9uKClcclxuXHQvLyBQcmV2ZW50cyB0aGUgdXNlciBmcm9tIGdlbmVyYXRpbmcgYHNlbGVjdHN0YXJ0YCBET00gZXZlbnRzLCB1c3VhbGx5IGdlbmVyYXRlZFxyXG5cdC8vIHdoZW4gdGhlIHVzZXIgZHJhZ3MgdGhlIG1vdXNlIHRocm91Z2ggYSBwYWdlIHdpdGggdGV4dC4gVXNlZCBpbnRlcm5hbGx5XHJcblx0Ly8gYnkgTGVhZmxldCB0byBvdmVycmlkZSB0aGUgYmVoYXZpb3VyIG9mIGFueSBjbGljay1hbmQtZHJhZyBpbnRlcmFjdGlvbiBvblxyXG5cdC8vIHRoZSBtYXAuIEFmZmVjdHMgZHJhZyBpbnRlcmFjdGlvbnMgb24gdGhlIHdob2xlIGRvY3VtZW50LlxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZW5hYmxlVGV4dFNlbGVjdGlvbigpXHJcblx0Ly8gQ2FuY2VscyB0aGUgZWZmZWN0cyBvZiBhIHByZXZpb3VzIFtgTC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uYF0oI2RvbXV0aWwtZGlzYWJsZXRleHRzZWxlY3Rpb24pLlxyXG5cdGlmICgnb25zZWxlY3RzdGFydCcgaW4gZG9jdW1lbnQpIHtcclxuXHRcdEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbiA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0TC5Eb21FdmVudC5vbih3aW5kb3csICdzZWxlY3RzdGFydCcsIEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQpO1xyXG5cdFx0fTtcclxuXHRcdEwuRG9tVXRpbC5lbmFibGVUZXh0U2VsZWN0aW9uID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRMLkRvbUV2ZW50Lm9mZih3aW5kb3csICdzZWxlY3RzdGFydCcsIEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQpO1xyXG5cdFx0fTtcclxuXHJcblx0fSBlbHNlIHtcclxuXHRcdHZhciB1c2VyU2VsZWN0UHJvcGVydHkgPSBMLkRvbVV0aWwudGVzdFByb3AoXHJcblx0XHRcdFsndXNlclNlbGVjdCcsICdXZWJraXRVc2VyU2VsZWN0JywgJ09Vc2VyU2VsZWN0JywgJ01velVzZXJTZWxlY3QnLCAnbXNVc2VyU2VsZWN0J10pO1xyXG5cclxuXHRcdEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbiA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0aWYgKHVzZXJTZWxlY3RQcm9wZXJ0eSkge1xyXG5cdFx0XHRcdHZhciBzdHlsZSA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZTtcclxuXHRcdFx0XHR0aGlzLl91c2VyU2VsZWN0ID0gc3R5bGVbdXNlclNlbGVjdFByb3BlcnR5XTtcclxuXHRcdFx0XHRzdHlsZVt1c2VyU2VsZWN0UHJvcGVydHldID0gJ25vbmUnO1xyXG5cdFx0XHR9XHJcblx0XHR9O1xyXG5cdFx0TC5Eb21VdGlsLmVuYWJsZVRleHRTZWxlY3Rpb24gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdGlmICh1c2VyU2VsZWN0UHJvcGVydHkpIHtcclxuXHRcdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuc3R5bGVbdXNlclNlbGVjdFByb3BlcnR5XSA9IHRoaXMuX3VzZXJTZWxlY3Q7XHJcblx0XHRcdFx0ZGVsZXRlIHRoaXMuX3VzZXJTZWxlY3Q7XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblx0fVxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZGlzYWJsZUltYWdlRHJhZygpXHJcblx0Ly8gQXMgW2BMLkRvbVV0aWwuZGlzYWJsZVRleHRTZWxlY3Rpb25gXSgjZG9tdXRpbC1kaXNhYmxldGV4dHNlbGVjdGlvbiksIGJ1dFxyXG5cdC8vIGZvciBgZHJhZ3N0YXJ0YCBET00gZXZlbnRzLCB1c3VhbGx5IGdlbmVyYXRlZCB3aGVuIHRoZSB1c2VyIGRyYWdzIGFuIGltYWdlLlxyXG5cdEwuRG9tVXRpbC5kaXNhYmxlSW1hZ2VEcmFnID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21FdmVudC5vbih3aW5kb3csICdkcmFnc3RhcnQnLCBMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KTtcclxuXHR9O1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gZW5hYmxlSW1hZ2VEcmFnKClcclxuXHQvLyBDYW5jZWxzIHRoZSBlZmZlY3RzIG9mIGEgcHJldmlvdXMgW2BMLkRvbVV0aWwuZGlzYWJsZUltYWdlRHJhZ2BdKCNkb211dGlsLWRpc2FibGV0ZXh0c2VsZWN0aW9uKS5cclxuXHRMLkRvbVV0aWwuZW5hYmxlSW1hZ2VEcmFnID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21FdmVudC5vZmYod2luZG93LCAnZHJhZ3N0YXJ0JywgTC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdCk7XHJcblx0fTtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHByZXZlbnRPdXRsaW5lKGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBNYWtlcyB0aGUgW291dGxpbmVdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0NTUy9vdXRsaW5lKVxyXG5cdC8vIG9mIHRoZSBlbGVtZW50IGBlbGAgaW52aXNpYmxlLiBVc2VkIGludGVybmFsbHkgYnkgTGVhZmxldCB0byBwcmV2ZW50XHJcblx0Ly8gZm9jdXNhYmxlIGVsZW1lbnRzIGZyb20gZGlzcGxheWluZyBhbiBvdXRsaW5lIHdoZW4gdGhlIHVzZXIgcGVyZm9ybXMgYVxyXG5cdC8vIGRyYWcgaW50ZXJhY3Rpb24gb24gdGhlbS5cclxuXHRMLkRvbVV0aWwucHJldmVudE91dGxpbmUgPSBmdW5jdGlvbiAoZWxlbWVudCkge1xyXG5cdFx0d2hpbGUgKGVsZW1lbnQudGFiSW5kZXggPT09IC0xKSB7XHJcblx0XHRcdGVsZW1lbnQgPSBlbGVtZW50LnBhcmVudE5vZGU7XHJcblx0XHR9XHJcblx0XHRpZiAoIWVsZW1lbnQgfHwgIWVsZW1lbnQuc3R5bGUpIHsgcmV0dXJuOyB9XHJcblx0XHRMLkRvbVV0aWwucmVzdG9yZU91dGxpbmUoKTtcclxuXHRcdHRoaXMuX291dGxpbmVFbGVtZW50ID0gZWxlbWVudDtcclxuXHRcdHRoaXMuX291dGxpbmVTdHlsZSA9IGVsZW1lbnQuc3R5bGUub3V0bGluZTtcclxuXHRcdGVsZW1lbnQuc3R5bGUub3V0bGluZSA9ICdub25lJztcclxuXHRcdEwuRG9tRXZlbnQub24od2luZG93LCAna2V5ZG93bicsIEwuRG9tVXRpbC5yZXN0b3JlT3V0bGluZSwgdGhpcyk7XHJcblx0fTtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHJlc3RvcmVPdXRsaW5lKClcclxuXHQvLyBDYW5jZWxzIHRoZSBlZmZlY3RzIG9mIGEgcHJldmlvdXMgW2BMLkRvbVV0aWwucHJldmVudE91dGxpbmVgXSgpLlxyXG5cdEwuRG9tVXRpbC5yZXN0b3JlT3V0bGluZSA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fb3V0bGluZUVsZW1lbnQpIHsgcmV0dXJuOyB9XHJcblx0XHR0aGlzLl9vdXRsaW5lRWxlbWVudC5zdHlsZS5vdXRsaW5lID0gdGhpcy5fb3V0bGluZVN0eWxlO1xyXG5cdFx0ZGVsZXRlIHRoaXMuX291dGxpbmVFbGVtZW50O1xyXG5cdFx0ZGVsZXRlIHRoaXMuX291dGxpbmVTdHlsZTtcclxuXHRcdEwuRG9tRXZlbnQub2ZmKHdpbmRvdywgJ2tleWRvd24nLCBMLkRvbVV0aWwucmVzdG9yZU91dGxpbmUsIHRoaXMpO1xyXG5cdH07XHJcbn0pKCk7XHJcblxuXG5cbi8qIEBjbGFzcyBMYXRMbmdcclxuICogQGFrYSBMLkxhdExuZ1xyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgZ2VvZ3JhcGhpY2FsIHBvaW50IHdpdGggYSBjZXJ0YWluIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYFxyXG4gKiB2YXIgbGF0bG5nID0gTC5sYXRMbmcoNTAuNSwgMzAuNSk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBBbGwgTGVhZmxldCBtZXRob2RzIHRoYXQgYWNjZXB0IExhdExuZyBvYmplY3RzIGFsc28gYWNjZXB0IHRoZW0gaW4gYSBzaW1wbGUgQXJyYXkgZm9ybSBhbmQgc2ltcGxlIG9iamVjdCBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlc2UgbGluZXMgYXJlIGVxdWl2YWxlbnQ6XHJcbiAqXHJcbiAqIGBgYFxyXG4gKiBtYXAucGFuVG8oWzUwLCAzMF0pO1xyXG4gKiBtYXAucGFuVG8oe2xvbjogMzAsIGxhdDogNTB9KTtcclxuICogbWFwLnBhblRvKHtsYXQ6IDUwLCBsbmc6IDMwfSk7XHJcbiAqIG1hcC5wYW5UbyhMLmxhdExuZyg1MCwgMzApKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5MYXRMbmcgPSBmdW5jdGlvbiAobGF0LCBsbmcsIGFsdCkge1xyXG5cdGlmIChpc05hTihsYXQpIHx8IGlzTmFOKGxuZykpIHtcclxuXHRcdHRocm93IG5ldyBFcnJvcignSW52YWxpZCBMYXRMbmcgb2JqZWN0OiAoJyArIGxhdCArICcsICcgKyBsbmcgKyAnKScpO1xyXG5cdH1cclxuXHJcblx0Ly8gQHByb3BlcnR5IGxhdDogTnVtYmVyXHJcblx0Ly8gTGF0aXR1ZGUgaW4gZGVncmVlc1xyXG5cdHRoaXMubGF0ID0gK2xhdDtcclxuXHJcblx0Ly8gQHByb3BlcnR5IGxuZzogTnVtYmVyXHJcblx0Ly8gTG9uZ2l0dWRlIGluIGRlZ3JlZXNcclxuXHR0aGlzLmxuZyA9ICtsbmc7XHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBhbHQ6IE51bWJlclxyXG5cdC8vIEFsdGl0dWRlIGluIG1ldGVycyAob3B0aW9uYWwpXHJcblx0aWYgKGFsdCAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHR0aGlzLmFsdCA9ICthbHQ7XHJcblx0fVxyXG59O1xyXG5cclxuTC5MYXRMbmcucHJvdG90eXBlID0ge1xyXG5cdC8vIEBtZXRob2QgZXF1YWxzKG90aGVyTGF0TG5nOiBMYXRMbmcsIG1heE1hcmdpbj86IE51bWJlcik6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZ2l2ZW4gYExhdExuZ2AgcG9pbnQgaXMgYXQgdGhlIHNhbWUgcG9zaXRpb24gKHdpdGhpbiBhIHNtYWxsIG1hcmdpbiBvZiBlcnJvcikuIFRoZSBtYXJnaW4gb2YgZXJyb3IgY2FuIGJlIG92ZXJyaWRlbiBieSBzZXR0aW5nIGBtYXhNYXJnaW5gIHRvIGEgc21hbGwgbnVtYmVyLlxyXG5cdGVxdWFsczogZnVuY3Rpb24gKG9iaiwgbWF4TWFyZ2luKSB7XHJcblx0XHRpZiAoIW9iaikgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHRvYmogPSBMLmxhdExuZyhvYmopO1xyXG5cclxuXHRcdHZhciBtYXJnaW4gPSBNYXRoLm1heChcclxuXHRcdCAgICAgICAgTWF0aC5hYnModGhpcy5sYXQgLSBvYmoubGF0KSxcclxuXHRcdCAgICAgICAgTWF0aC5hYnModGhpcy5sbmcgLSBvYmoubG5nKSk7XHJcblxyXG5cdFx0cmV0dXJuIG1hcmdpbiA8PSAobWF4TWFyZ2luID09PSB1bmRlZmluZWQgPyAxLjBFLTkgOiBtYXhNYXJnaW4pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdG9TdHJpbmcoKTogU3RyaW5nXHJcblx0Ly8gUmV0dXJucyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgcG9pbnQgKGZvciBkZWJ1Z2dpbmcgcHVycG9zZXMpLlxyXG5cdHRvU3RyaW5nOiBmdW5jdGlvbiAocHJlY2lzaW9uKSB7XHJcblx0XHRyZXR1cm4gJ0xhdExuZygnICtcclxuXHRcdCAgICAgICAgTC5VdGlsLmZvcm1hdE51bSh0aGlzLmxhdCwgcHJlY2lzaW9uKSArICcsICcgK1xyXG5cdFx0ICAgICAgICBMLlV0aWwuZm9ybWF0TnVtKHRoaXMubG5nLCBwcmVjaXNpb24pICsgJyknO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzdGFuY2VUbyhvdGhlckxhdExuZzogTGF0TG5nKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZGlzdGFuY2UgKGluIG1ldGVycykgdG8gdGhlIGdpdmVuIGBMYXRMbmdgIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIFtIYXZlcnNpbmUgZm9ybXVsYV0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9IYXZlcnNpbmVfZm9ybXVsYSkuXHJcblx0ZGlzdGFuY2VUbzogZnVuY3Rpb24gKG90aGVyKSB7XHJcblx0XHRyZXR1cm4gTC5DUlMuRWFydGguZGlzdGFuY2UodGhpcywgTC5sYXRMbmcob3RoZXIpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXAoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyBhIG5ldyBgTGF0TG5nYCBvYmplY3Qgd2l0aCB0aGUgbG9uZ2l0dWRlIHdyYXBwZWQgc28gaXQncyBhbHdheXMgYmV0d2VlbiAtMTgwIGFuZCArMTgwIGRlZ3JlZXMuXHJcblx0d3JhcDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIEwuQ1JTLkVhcnRoLndyYXBMYXRMbmcodGhpcyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b0JvdW5kcyhzaXplSW5NZXRlcnM6IE51bWJlcik6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgYSBuZXcgYExhdExuZ0JvdW5kc2Agb2JqZWN0IGluIHdoaWNoIGVhY2ggYm91bmRhcnkgaXMgYHNpemVJbk1ldGVyc2AgbWV0ZXJzIGFwYXJ0IGZyb20gdGhlIGBMYXRMbmdgLlxyXG5cdHRvQm91bmRzOiBmdW5jdGlvbiAoc2l6ZUluTWV0ZXJzKSB7XHJcblx0XHR2YXIgbGF0QWNjdXJhY3kgPSAxODAgKiBzaXplSW5NZXRlcnMgLyA0MDA3NTAxNyxcclxuXHRcdCAgICBsbmdBY2N1cmFjeSA9IGxhdEFjY3VyYWN5IC8gTWF0aC5jb3MoKE1hdGguUEkgLyAxODApICogdGhpcy5sYXQpO1xyXG5cclxuXHRcdHJldHVybiBMLmxhdExuZ0JvdW5kcyhcclxuXHRcdCAgICAgICAgW3RoaXMubGF0IC0gbGF0QWNjdXJhY3ksIHRoaXMubG5nIC0gbG5nQWNjdXJhY3ldLFxyXG5cdFx0ICAgICAgICBbdGhpcy5sYXQgKyBsYXRBY2N1cmFjeSwgdGhpcy5sbmcgKyBsbmdBY2N1cmFjeV0pO1xyXG5cdH0sXHJcblxyXG5cdGNsb25lOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKHRoaXMubGF0LCB0aGlzLmxuZywgdGhpcy5hbHQpO1xyXG5cdH1cclxufTtcclxuXHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5sYXRMbmcobGF0aXR1ZGU6IE51bWJlciwgbG9uZ2l0dWRlOiBOdW1iZXIsIGFsdGl0dWRlPzogTnVtYmVyKTogTGF0TG5nXHJcbi8vIENyZWF0ZXMgYW4gb2JqZWN0IHJlcHJlc2VudGluZyBhIGdlb2dyYXBoaWNhbCBwb2ludCB3aXRoIHRoZSBnaXZlbiBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIChhbmQgb3B0aW9uYWxseSBhbHRpdHVkZSkuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5sYXRMbmcoY29vcmRzOiBBcnJheSk6IExhdExuZ1xyXG4vLyBFeHBlY3RzIGFuIGFycmF5IG9mIHRoZSBmb3JtIGBbTnVtYmVyLCBOdW1iZXJdYCBvciBgW051bWJlciwgTnVtYmVyLCBOdW1iZXJdYCBpbnN0ZWFkLlxyXG5cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nKGNvb3JkczogT2JqZWN0KTogTGF0TG5nXHJcbi8vIEV4cGVjdHMgYW4gcGxhaW4gb2JqZWN0IG9mIHRoZSBmb3JtIGB7bGF0OiBOdW1iZXIsIGxuZzogTnVtYmVyfWAgb3IgYHtsYXQ6IE51bWJlciwgbG5nOiBOdW1iZXIsIGFsdDogTnVtYmVyfWAgaW5zdGVhZC5cclxuXHJcbkwubGF0TG5nID0gZnVuY3Rpb24gKGEsIGIsIGMpIHtcclxuXHRpZiAoYSBpbnN0YW5jZW9mIEwuTGF0TG5nKSB7XHJcblx0XHRyZXR1cm4gYTtcclxuXHR9XHJcblx0aWYgKEwuVXRpbC5pc0FycmF5KGEpICYmIHR5cGVvZiBhWzBdICE9PSAnb2JqZWN0Jykge1xyXG5cdFx0aWYgKGEubGVuZ3RoID09PSAzKSB7XHJcblx0XHRcdHJldHVybiBuZXcgTC5MYXRMbmcoYVswXSwgYVsxXSwgYVsyXSk7XHJcblx0XHR9XHJcblx0XHRpZiAoYS5sZW5ndGggPT09IDIpIHtcclxuXHRcdFx0cmV0dXJuIG5ldyBMLkxhdExuZyhhWzBdLCBhWzFdKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBudWxsO1xyXG5cdH1cclxuXHRpZiAoYSA9PT0gdW5kZWZpbmVkIHx8IGEgPT09IG51bGwpIHtcclxuXHRcdHJldHVybiBhO1xyXG5cdH1cclxuXHRpZiAodHlwZW9mIGEgPT09ICdvYmplY3QnICYmICdsYXQnIGluIGEpIHtcclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcoYS5sYXQsICdsbmcnIGluIGEgPyBhLmxuZyA6IGEubG9uLCBhLmFsdCk7XHJcblx0fVxyXG5cdGlmIChiID09PSB1bmRlZmluZWQpIHtcclxuXHRcdHJldHVybiBudWxsO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IEwuTGF0TG5nKGEsIGIsIGMpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgTGF0TG5nQm91bmRzXHJcbiAqIEBha2EgTC5MYXRMbmdCb3VuZHNcclxuICpcclxuICogUmVwcmVzZW50cyBhIHJlY3Rhbmd1bGFyIGdlb2dyYXBoaWNhbCBhcmVhIG9uIGEgbWFwLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgY29ybmVyMSA9IEwubGF0TG5nKDQwLjcxMiwgLTc0LjIyNyksXHJcbiAqIGNvcm5lcjIgPSBMLmxhdExuZyg0MC43NzQsIC03NC4xMjUpLFxyXG4gKiBib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhjb3JuZXIxLCBjb3JuZXIyKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIEFsbCBMZWFmbGV0IG1ldGhvZHMgdGhhdCBhY2NlcHQgTGF0TG5nQm91bmRzIG9iamVjdHMgYWxzbyBhY2NlcHQgdGhlbSBpbiBhIHNpbXBsZSBBcnJheSBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlIGJvdW5kcyBleGFtcGxlIGFib3ZlIGNhbiBiZSBwYXNzZWQgbGlrZSB0aGlzOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBtYXAuZml0Qm91bmRzKFtcclxuICogXHRbNDAuNzEyLCAtNzQuMjI3XSxcclxuICogXHRbNDAuNzc0LCAtNzQuMTI1XVxyXG4gKiBdKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIENhdXRpb246IGlmIHRoZSBhcmVhIGNyb3NzZXMgdGhlIGFudGltZXJpZGlhbiAob2Z0ZW4gY29uZnVzZWQgd2l0aCB0aGUgSW50ZXJuYXRpb25hbCBEYXRlIExpbmUpLCB5b3UgbXVzdCBzcGVjaWZ5IGNvcm5lcnMgX291dHNpZGVfIHRoZSBbLTE4MCwgMTgwXSBkZWdyZWVzIGxvbmdpdHVkZSByYW5nZS5cclxuICovXHJcblxyXG5MLkxhdExuZ0JvdW5kcyA9IGZ1bmN0aW9uIChjb3JuZXIxLCBjb3JuZXIyKSB7IC8vIChMYXRMbmcsIExhdExuZykgb3IgKExhdExuZ1tdKVxyXG5cdGlmICghY29ybmVyMSkgeyByZXR1cm47IH1cclxuXHJcblx0dmFyIGxhdGxuZ3MgPSBjb3JuZXIyID8gW2Nvcm5lcjEsIGNvcm5lcjJdIDogY29ybmVyMTtcclxuXHJcblx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGxhdGxuZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdHRoaXMuZXh0ZW5kKGxhdGxuZ3NbaV0pO1xyXG5cdH1cclxufTtcclxuXHJcbkwuTGF0TG5nQm91bmRzLnByb3RvdHlwZSA9IHtcclxuXHJcblx0Ly8gQG1ldGhvZCBleHRlbmQobGF0bG5nOiBMYXRMbmcpOiB0aGlzXHJcblx0Ly8gRXh0ZW5kIHRoZSBib3VuZHMgdG8gY29udGFpbiB0aGUgZ2l2ZW4gcG9pbnRcclxuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCBleHRlbmQob3RoZXJCb3VuZHM6IExhdExuZ0JvdW5kcyk6IHRoaXNcclxuXHQvLyBFeHRlbmQgdGhlIGJvdW5kcyB0byBjb250YWluIHRoZSBnaXZlbiBib3VuZHNcclxuXHRleHRlbmQ6IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBzdzIsIG5lMjtcclxuXHJcblx0XHRpZiAob2JqIGluc3RhbmNlb2YgTC5MYXRMbmcpIHtcclxuXHRcdFx0c3cyID0gb2JqO1xyXG5cdFx0XHRuZTIgPSBvYmo7XHJcblxyXG5cdFx0fSBlbHNlIGlmIChvYmogaW5zdGFuY2VvZiBMLkxhdExuZ0JvdW5kcykge1xyXG5cdFx0XHRzdzIgPSBvYmouX3NvdXRoV2VzdDtcclxuXHRcdFx0bmUyID0gb2JqLl9ub3J0aEVhc3Q7XHJcblxyXG5cdFx0XHRpZiAoIXN3MiB8fCAhbmUyKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0cmV0dXJuIG9iaiA/IHRoaXMuZXh0ZW5kKEwubGF0TG5nKG9iaikgfHwgTC5sYXRMbmdCb3VuZHMob2JqKSkgOiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghc3cgJiYgIW5lKSB7XHJcblx0XHRcdHRoaXMuX3NvdXRoV2VzdCA9IG5ldyBMLkxhdExuZyhzdzIubGF0LCBzdzIubG5nKTtcclxuXHRcdFx0dGhpcy5fbm9ydGhFYXN0ID0gbmV3IEwuTGF0TG5nKG5lMi5sYXQsIG5lMi5sbmcpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0c3cubGF0ID0gTWF0aC5taW4oc3cyLmxhdCwgc3cubGF0KTtcclxuXHRcdFx0c3cubG5nID0gTWF0aC5taW4oc3cyLmxuZywgc3cubG5nKTtcclxuXHRcdFx0bmUubGF0ID0gTWF0aC5tYXgobmUyLmxhdCwgbmUubGF0KTtcclxuXHRcdFx0bmUubG5nID0gTWF0aC5tYXgobmUyLmxuZywgbmUubG5nKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBhZChidWZmZXJSYXRpbzogTnVtYmVyKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyBiaWdnZXIgYm91bmRzIGNyZWF0ZWQgYnkgZXh0ZW5kaW5nIHRoZSBjdXJyZW50IGJvdW5kcyBieSBhIGdpdmVuIHBlcmNlbnRhZ2UgaW4gZWFjaCBkaXJlY3Rpb24uXHJcblx0cGFkOiBmdW5jdGlvbiAoYnVmZmVyUmF0aW8pIHtcclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBoZWlnaHRCdWZmZXIgPSBNYXRoLmFicyhzdy5sYXQgLSBuZS5sYXQpICogYnVmZmVyUmF0aW8sXHJcblx0XHQgICAgd2lkdGhCdWZmZXIgPSBNYXRoLmFicyhzdy5sbmcgLSBuZS5sbmcpICogYnVmZmVyUmF0aW87XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZ0JvdW5kcyhcclxuXHRcdCAgICAgICAgbmV3IEwuTGF0TG5nKHN3LmxhdCAtIGhlaWdodEJ1ZmZlciwgc3cubG5nIC0gd2lkdGhCdWZmZXIpLFxyXG5cdFx0ICAgICAgICBuZXcgTC5MYXRMbmcobmUubGF0ICsgaGVpZ2h0QnVmZmVyLCBuZS5sbmcgKyB3aWR0aEJ1ZmZlcikpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIGNlbnRlciBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyhcclxuXHRcdCAgICAgICAgKHRoaXMuX3NvdXRoV2VzdC5sYXQgKyB0aGlzLl9ub3J0aEVhc3QubGF0KSAvIDIsXHJcblx0XHQgICAgICAgICh0aGlzLl9zb3V0aFdlc3QubG5nICsgdGhpcy5fbm9ydGhFYXN0LmxuZykgLyAyKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNvdXRoV2VzdCgpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBzb3V0aC13ZXN0IHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0U291dGhXZXN0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fc291dGhXZXN0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Tm9ydGhFYXN0KCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIG5vcnRoLWVhc3QgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXROb3J0aEVhc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9ub3J0aEVhc3Q7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXROb3J0aFdlc3QoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgbm9ydGgtd2VzdCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldE5vcnRoV2VzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyh0aGlzLmdldE5vcnRoKCksIHRoaXMuZ2V0V2VzdCgpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNvdXRoRWFzdCgpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBzb3V0aC1lYXN0IHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0U291dGhFYXN0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKHRoaXMuZ2V0U291dGgoKSwgdGhpcy5nZXRFYXN0KCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0V2VzdCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSB3ZXN0IGxvbmdpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0V2VzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3NvdXRoV2VzdC5sbmc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTb3V0aCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBzb3V0aCBsYXRpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0U291dGg6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9zb3V0aFdlc3QubGF0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0RWFzdCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBlYXN0IGxvbmdpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0RWFzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX25vcnRoRWFzdC5sbmc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXROb3J0aCgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBub3J0aCBsYXRpdHVkZSBvZiB0aGUgYm91bmRzXHJcblx0Z2V0Tm9ydGg6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9ub3J0aEVhc3QubGF0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY29udGFpbnMob3RoZXJCb3VuZHM6IExhdExuZ0JvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGNvbnRhaW5zIHRoZSBnaXZlbiBvbmUuXHJcblxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2QgY29udGFpbnMgKGxhdGxuZzogTGF0TG5nKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgY29udGFpbnMgdGhlIGdpdmVuIHBvaW50LlxyXG5cdGNvbnRhaW5zOiBmdW5jdGlvbiAob2JqKSB7IC8vIChMYXRMbmdCb3VuZHMpIG9yIChMYXRMbmcpIC0+IEJvb2xlYW5cclxuXHRcdGlmICh0eXBlb2Ygb2JqWzBdID09PSAnbnVtYmVyJyB8fCBvYmogaW5zdGFuY2VvZiBMLkxhdExuZykge1xyXG5cdFx0XHRvYmogPSBMLmxhdExuZyhvYmopO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0b2JqID0gTC5sYXRMbmdCb3VuZHMob2JqKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgc3cyLCBuZTI7XHJcblxyXG5cdFx0aWYgKG9iaiBpbnN0YW5jZW9mIEwuTGF0TG5nQm91bmRzKSB7XHJcblx0XHRcdHN3MiA9IG9iai5nZXRTb3V0aFdlc3QoKTtcclxuXHRcdFx0bmUyID0gb2JqLmdldE5vcnRoRWFzdCgpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0c3cyID0gbmUyID0gb2JqO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiAoc3cyLmxhdCA+PSBzdy5sYXQpICYmIChuZTIubGF0IDw9IG5lLmxhdCkgJiZcclxuXHRcdCAgICAgICAoc3cyLmxuZyA+PSBzdy5sbmcpICYmIChuZTIubG5nIDw9IG5lLmxuZyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpbnRlcnNlY3RzKG90aGVyQm91bmRzOiBMYXRMbmdCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBpbnRlcnNlY3RzIHRoZSBnaXZlbiBib3VuZHMuIFR3byBib3VuZHMgaW50ZXJzZWN0IGlmIHRoZXkgaGF2ZSBhdCBsZWFzdCBvbmUgcG9pbnQgaW4gY29tbW9uLlxyXG5cdGludGVyc2VjdHM6IGZ1bmN0aW9uIChib3VuZHMpIHtcclxuXHRcdGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0dmFyIHN3ID0gdGhpcy5fc291dGhXZXN0LFxyXG5cdFx0ICAgIG5lID0gdGhpcy5fbm9ydGhFYXN0LFxyXG5cdFx0ICAgIHN3MiA9IGJvdW5kcy5nZXRTb3V0aFdlc3QoKSxcclxuXHRcdCAgICBuZTIgPSBib3VuZHMuZ2V0Tm9ydGhFYXN0KCksXHJcblxyXG5cdFx0ICAgIGxhdEludGVyc2VjdHMgPSAobmUyLmxhdCA+PSBzdy5sYXQpICYmIChzdzIubGF0IDw9IG5lLmxhdCksXHJcblx0XHQgICAgbG5nSW50ZXJzZWN0cyA9IChuZTIubG5nID49IHN3LmxuZykgJiYgKHN3Mi5sbmcgPD0gbmUubG5nKTtcclxuXHJcblx0XHRyZXR1cm4gbGF0SW50ZXJzZWN0cyAmJiBsbmdJbnRlcnNlY3RzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgb3ZlcmxhcHMob3RoZXJCb3VuZHM6IEJvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIG92ZXJsYXBzIHRoZSBnaXZlbiBib3VuZHMuIFR3byBib3VuZHMgb3ZlcmxhcCBpZiB0aGVpciBpbnRlcnNlY3Rpb24gaXMgYW4gYXJlYS5cclxuXHRvdmVybGFwczogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0Ym91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgc3cyID0gYm91bmRzLmdldFNvdXRoV2VzdCgpLFxyXG5cdFx0ICAgIG5lMiA9IGJvdW5kcy5nZXROb3J0aEVhc3QoKSxcclxuXHJcblx0XHQgICAgbGF0T3ZlcmxhcHMgPSAobmUyLmxhdCA+IHN3LmxhdCkgJiYgKHN3Mi5sYXQgPCBuZS5sYXQpLFxyXG5cdFx0ICAgIGxuZ092ZXJsYXBzID0gKG5lMi5sbmcgPiBzdy5sbmcpICYmIChzdzIubG5nIDwgbmUubG5nKTtcclxuXHJcblx0XHRyZXR1cm4gbGF0T3ZlcmxhcHMgJiYgbG5nT3ZlcmxhcHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b0JCb3hTdHJpbmcoKTogU3RyaW5nXHJcblx0Ly8gUmV0dXJucyBhIHN0cmluZyB3aXRoIGJvdW5kaW5nIGJveCBjb29yZGluYXRlcyBpbiBhICdzb3V0aHdlc3RfbG5nLHNvdXRod2VzdF9sYXQsbm9ydGhlYXN0X2xuZyxub3J0aGVhc3RfbGF0JyBmb3JtYXQuIFVzZWZ1bCBmb3Igc2VuZGluZyByZXF1ZXN0cyB0byB3ZWIgc2VydmljZXMgdGhhdCByZXR1cm4gZ2VvIGRhdGEuXHJcblx0dG9CQm94U3RyaW5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gW3RoaXMuZ2V0V2VzdCgpLCB0aGlzLmdldFNvdXRoKCksIHRoaXMuZ2V0RWFzdCgpLCB0aGlzLmdldE5vcnRoKCldLmpvaW4oJywnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVxdWFscyhvdGhlckJvdW5kczogTGF0TG5nQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgaXMgZXF1aXZhbGVudCAod2l0aGluIGEgc21hbGwgbWFyZ2luIG9mIGVycm9yKSB0byB0aGUgZ2l2ZW4gYm91bmRzLlxyXG5cdGVxdWFsczogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIGZhbHNlOyB9XHJcblxyXG5cdFx0Ym91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy5fc291dGhXZXN0LmVxdWFscyhib3VuZHMuZ2V0U291dGhXZXN0KCkpICYmXHJcblx0XHQgICAgICAgdGhpcy5fbm9ydGhFYXN0LmVxdWFscyhib3VuZHMuZ2V0Tm9ydGhFYXN0KCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaXNWYWxpZCgpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGJvdW5kcyBhcmUgcHJvcGVybHkgaW5pdGlhbGl6ZWQuXHJcblx0aXNWYWxpZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuICEhKHRoaXMuX3NvdXRoV2VzdCAmJiB0aGlzLl9ub3J0aEVhc3QpO1xyXG5cdH1cclxufTtcclxuXHJcbi8vIFRPRE8gSW50ZXJuYXRpb25hbCBkYXRlIGxpbmU/XHJcblxyXG4vLyBAZmFjdG9yeSBMLmxhdExuZ0JvdW5kcyhjb3JuZXIxOiBMYXRMbmcsIGNvcm5lcjI6IExhdExuZylcclxuLy8gQ3JlYXRlcyBhIGBMYXRMbmdCb3VuZHNgIG9iamVjdCBieSBkZWZpbmluZyB0d28gZGlhZ29uYWxseSBvcHBvc2l0ZSBjb3JuZXJzIG9mIHRoZSByZWN0YW5nbGUuXHJcblxyXG4vLyBAYWx0ZXJuYXRpdmVcclxuLy8gQGZhY3RvcnkgTC5sYXRMbmdCb3VuZHMobGF0bG5nczogTGF0TG5nW10pXHJcbi8vIENyZWF0ZXMgYSBgTGF0TG5nQm91bmRzYCBvYmplY3QgZGVmaW5lZCBieSB0aGUgZ2VvZ3JhcGhpY2FsIHBvaW50cyBpdCBjb250YWlucy4gVmVyeSB1c2VmdWwgZm9yIHpvb21pbmcgdGhlIG1hcCB0byBmaXQgYSBwYXJ0aWN1bGFyIHNldCBvZiBsb2NhdGlvbnMgd2l0aCBbYGZpdEJvdW5kc2BdKCNtYXAtZml0Ym91bmRzKS5cclxuTC5sYXRMbmdCb3VuZHMgPSBmdW5jdGlvbiAoYSwgYikge1xyXG5cdGlmIChhIGluc3RhbmNlb2YgTC5MYXRMbmdCb3VuZHMpIHtcclxuXHRcdHJldHVybiBhO1xyXG5cdH1cclxuXHRyZXR1cm4gbmV3IEwuTGF0TG5nQm91bmRzKGEsIGIpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIFByb2plY3Rpb25cclxuICogQHNlY3Rpb25cclxuICogTGVhZmxldCBjb21lcyB3aXRoIGEgc2V0IG9mIGFscmVhZHkgZGVmaW5lZCBQcm9qZWN0aW9ucyBvdXQgb2YgdGhlIGJveDpcclxuICpcclxuICogQHByb2plY3Rpb24gTC5Qcm9qZWN0aW9uLkxvbkxhdFxyXG4gKlxyXG4gKiBFcXVpcmVjdGFuZ3VsYXIsIG9yIFBsYXRlIENhcnJlZSBwcm9qZWN0aW9uIOKAlCB0aGUgbW9zdCBzaW1wbGUgcHJvamVjdGlvbixcclxuICogbW9zdGx5IHVzZWQgYnkgR0lTIGVudGh1c2lhc3RzLiBEaXJlY3RseSBtYXBzIGB4YCBhcyBsb25naXR1ZGUsIGFuZCBgeWAgYXNcclxuICogbGF0aXR1ZGUuIEFsc28gc3VpdGFibGUgZm9yIGZsYXQgd29ybGRzLCBlLmcuIGdhbWUgbWFwcy4gVXNlZCBieSB0aGVcclxuICogYEVQU0c6MzM5NWAgYW5kIGBTaW1wbGVgIENSUy5cclxuICovXHJcblxyXG5MLlByb2plY3Rpb24gPSB7fTtcclxuXHJcbkwuUHJvamVjdGlvbi5Mb25MYXQgPSB7XHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KGxhdGxuZy5sbmcsIGxhdGxuZy5sYXQpO1xyXG5cdH0sXHJcblxyXG5cdHVucHJvamVjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKHBvaW50LnksIHBvaW50LngpO1xyXG5cdH0sXHJcblxyXG5cdGJvdW5kczogTC5ib3VuZHMoWy0xODAsIC05MF0sIFsxODAsIDkwXSlcclxufTtcclxuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBQcm9qZWN0aW9uXHJcbiAqIEBwcm9qZWN0aW9uIEwuUHJvamVjdGlvbi5TcGhlcmljYWxNZXJjYXRvclxyXG4gKlxyXG4gKiBTcGhlcmljYWwgTWVyY2F0b3IgcHJvamVjdGlvbiDigJQgdGhlIG1vc3QgY29tbW9uIHByb2plY3Rpb24gZm9yIG9ubGluZSBtYXBzLFxyXG4gKiB1c2VkIGJ5IGFsbW9zdCBhbGwgZnJlZSBhbmQgY29tbWVyY2lhbCB0aWxlIHByb3ZpZGVycy4gQXNzdW1lcyB0aGF0IEVhcnRoIGlzXHJcbiAqIGEgc3BoZXJlLiBVc2VkIGJ5IHRoZSBgRVBTRzozODU3YCBDUlMuXHJcbiAqL1xyXG5cclxuTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yID0ge1xyXG5cclxuXHRSOiA2Mzc4MTM3LFxyXG5cdE1BWF9MQVRJVFVERTogODUuMDUxMTI4Nzc5OCxcclxuXHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIGQgPSBNYXRoLlBJIC8gMTgwLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMuTUFYX0xBVElUVURFLFxyXG5cdFx0ICAgIGxhdCA9IE1hdGgubWF4KE1hdGgubWluKG1heCwgbGF0bG5nLmxhdCksIC1tYXgpLFxyXG5cdFx0ICAgIHNpbiA9IE1hdGguc2luKGxhdCAqIGQpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludChcclxuXHRcdFx0XHR0aGlzLlIgKiBsYXRsbmcubG5nICogZCxcclxuXHRcdFx0XHR0aGlzLlIgKiBNYXRoLmxvZygoMSArIHNpbikgLyAoMSAtIHNpbikpIC8gMik7XHJcblx0fSxcclxuXHJcblx0dW5wcm9qZWN0OiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHZhciBkID0gMTgwIC8gTWF0aC5QSTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKFxyXG5cdFx0XHQoMiAqIE1hdGguYXRhbihNYXRoLmV4cChwb2ludC55IC8gdGhpcy5SKSkgLSAoTWF0aC5QSSAvIDIpKSAqIGQsXHJcblx0XHRcdHBvaW50LnggKiBkIC8gdGhpcy5SKTtcclxuXHR9LFxyXG5cclxuXHRib3VuZHM6IChmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZCA9IDYzNzgxMzcgKiBNYXRoLlBJO1xyXG5cdFx0cmV0dXJuIEwuYm91bmRzKFstZCwgLWRdLCBbZCwgZF0pO1xyXG5cdH0pKClcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIENSU1xyXG4gKiBAYWthIEwuQ1JTXHJcbiAqIEFic3RyYWN0IGNsYXNzIHRoYXQgZGVmaW5lcyBjb29yZGluYXRlIHJlZmVyZW5jZSBzeXN0ZW1zIGZvciBwcm9qZWN0aW5nXHJcbiAqIGdlb2dyYXBoaWNhbCBwb2ludHMgaW50byBwaXhlbCAoc2NyZWVuKSBjb29yZGluYXRlcyBhbmQgYmFjayAoYW5kIHRvXHJcbiAqIGNvb3JkaW5hdGVzIGluIG90aGVyIHVuaXRzIGZvciBbV01TXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9XZWJfTWFwX1NlcnZpY2UpIHNlcnZpY2VzKS4gU2VlXHJcbiAqIFtzcGF0aWFsIHJlZmVyZW5jZSBzeXN0ZW1dKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29vcmRpbmF0ZV9yZWZlcmVuY2Vfc3lzdGVtKS5cclxuICpcclxuICogTGVhZmxldCBkZWZpbmVzIHRoZSBtb3N0IHVzdWFsIENSU3MgYnkgZGVmYXVsdC4gSWYgeW91IHdhbnQgdG8gdXNlIGFcclxuICogQ1JTIG5vdCBkZWZpbmVkIGJ5IGRlZmF1bHQsIHRha2UgYSBsb29rIGF0IHRoZVxyXG4gKiBbUHJvajRMZWFmbGV0XShodHRwczovL2dpdGh1Yi5jb20va2FydGVuYS9Qcm9qNExlYWZsZXQpIHBsdWdpbi5cclxuICovXHJcblxyXG5MLkNSUyA9IHtcclxuXHQvLyBAbWV0aG9kIGxhdExuZ1RvUG9pbnQobGF0bG5nOiBMYXRMbmcsIHpvb206IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUHJvamVjdHMgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzIGludG8gcGl4ZWwgY29vcmRpbmF0ZXMgZm9yIGEgZ2l2ZW4gem9vbS5cclxuXHRsYXRMbmdUb1BvaW50OiBmdW5jdGlvbiAobGF0bG5nLCB6b29tKSB7XHJcblx0XHR2YXIgcHJvamVjdGVkUG9pbnQgPSB0aGlzLnByb2plY3Rpb24ucHJvamVjdChsYXRsbmcpLFxyXG5cdFx0ICAgIHNjYWxlID0gdGhpcy5zY2FsZSh6b29tKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy50cmFuc2Zvcm1hdGlvbi5fdHJhbnNmb3JtKHByb2plY3RlZFBvaW50LCBzY2FsZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwb2ludFRvTGF0TG5nKHBvaW50OiBQb2ludCwgem9vbTogTnVtYmVyKTogTGF0TG5nXHJcblx0Ly8gVGhlIGludmVyc2Ugb2YgYGxhdExuZ1RvUG9pbnRgLiBQcm9qZWN0cyBwaXhlbCBjb29yZGluYXRlcyBvbiBhIGdpdmVuXHJcblx0Ly8gem9vbSBpbnRvIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlcy5cclxuXHRwb2ludFRvTGF0TG5nOiBmdW5jdGlvbiAocG9pbnQsIHpvb20pIHtcclxuXHRcdHZhciBzY2FsZSA9IHRoaXMuc2NhbGUoem9vbSksXHJcblx0XHQgICAgdW50cmFuc2Zvcm1lZFBvaW50ID0gdGhpcy50cmFuc2Zvcm1hdGlvbi51bnRyYW5zZm9ybShwb2ludCwgc2NhbGUpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzLnByb2plY3Rpb24udW5wcm9qZWN0KHVudHJhbnNmb3JtZWRQb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwcm9qZWN0KGxhdGxuZzogTGF0TG5nKTogUG9pbnRcclxuXHQvLyBQcm9qZWN0cyBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZXMgaW50byBjb29yZGluYXRlcyBpbiB1bml0cyBhY2NlcHRlZCBmb3JcclxuXHQvLyB0aGlzIENSUyAoZS5nLiBtZXRlcnMgZm9yIEVQU0c6Mzg1NywgZm9yIHBhc3NpbmcgaXQgdG8gV01TIHNlcnZpY2VzKS5cclxuXHRwcm9qZWN0OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0aW9uLnByb2plY3QobGF0bG5nKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVucHJvamVjdChwb2ludDogUG9pbnQpOiBMYXRMbmdcclxuXHQvLyBHaXZlbiBhIHByb2plY3RlZCBjb29yZGluYXRlIHJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmcgTGF0TG5nLlxyXG5cdC8vIFRoZSBpbnZlcnNlIG9mIGBwcm9qZWN0YC5cclxuXHR1bnByb2plY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0cmV0dXJuIHRoaXMucHJvamVjdGlvbi51bnByb2plY3QocG9pbnQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2NhbGUoem9vbTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgc2NhbGUgdXNlZCB3aGVuIHRyYW5zZm9ybWluZyBwcm9qZWN0ZWQgY29vcmRpbmF0ZXMgaW50b1xyXG5cdC8vIHBpeGVsIGNvb3JkaW5hdGVzIGZvciBhIHBhcnRpY3VsYXIgem9vbS4gRm9yIGV4YW1wbGUsIGl0IHJldHVybnNcclxuXHQvLyBgMjU2ICogMl56b29tYCBmb3IgTWVyY2F0b3ItYmFzZWQgQ1JTLlxyXG5cdHNjYWxlOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0cmV0dXJuIDI1NiAqIE1hdGgucG93KDIsIHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgem9vbShzY2FsZTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gSW52ZXJzZSBvZiBgc2NhbGUoKWAsIHJldHVybnMgdGhlIHpvb20gbGV2ZWwgY29ycmVzcG9uZGluZyB0byBhIHNjYWxlXHJcblx0Ly8gZmFjdG9yIG9mIGBzY2FsZWAuXHJcblx0em9vbTogZnVuY3Rpb24gKHNjYWxlKSB7XHJcblx0XHRyZXR1cm4gTWF0aC5sb2coc2NhbGUgLyAyNTYpIC8gTWF0aC5MTjI7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRQcm9qZWN0ZWRCb3VuZHMoem9vbTogTnVtYmVyKTogQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgcHJvamVjdGlvbidzIGJvdW5kcyBzY2FsZWQgYW5kIHRyYW5zZm9ybWVkIGZvciB0aGUgcHJvdmlkZWQgYHpvb21gLlxyXG5cdGdldFByb2plY3RlZEJvdW5kczogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdGlmICh0aGlzLmluZmluaXRlKSB7IHJldHVybiBudWxsOyB9XHJcblxyXG5cdFx0dmFyIGIgPSB0aGlzLnByb2plY3Rpb24uYm91bmRzLFxyXG5cdFx0ICAgIHMgPSB0aGlzLnNjYWxlKHpvb20pLFxyXG5cdFx0ICAgIG1pbiA9IHRoaXMudHJhbnNmb3JtYXRpb24udHJhbnNmb3JtKGIubWluLCBzKSxcclxuXHRcdCAgICBtYXggPSB0aGlzLnRyYW5zZm9ybWF0aW9uLnRyYW5zZm9ybShiLm1heCwgcyk7XHJcblxyXG5cdFx0cmV0dXJuIEwuYm91bmRzKG1pbiwgbWF4KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGRpc3RhbmNlKGxhdGxuZzE6IExhdExuZywgbGF0bG5nMjogTGF0TG5nKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0d28gZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzLlxyXG5cclxuXHQvLyBAcHJvcGVydHkgY29kZTogU3RyaW5nXHJcblx0Ly8gU3RhbmRhcmQgY29kZSBuYW1lIG9mIHRoZSBDUlMgcGFzc2VkIGludG8gV01TIHNlcnZpY2VzIChlLmcuIGAnRVBTRzozODU3J2ApXHJcblx0Ly9cclxuXHQvLyBAcHJvcGVydHkgd3JhcExuZzogTnVtYmVyW11cclxuXHQvLyBBbiBhcnJheSBvZiB0d28gbnVtYmVycyBkZWZpbmluZyB3aGV0aGVyIHRoZSBsb25naXR1ZGUgKGhvcml6b250YWwpIGNvb3JkaW5hdGVcclxuXHQvLyBheGlzIHdyYXBzIGFyb3VuZCBhIGdpdmVuIHJhbmdlIGFuZCBob3cuIERlZmF1bHRzIHRvIGBbLTE4MCwgMTgwXWAgaW4gbW9zdFxyXG5cdC8vIGdlb2dyYXBoaWNhbCBDUlNzLiBJZiBgdW5kZWZpbmVkYCwgdGhlIGxvbmdpdHVkZSBheGlzIGRvZXMgbm90IHdyYXAgYXJvdW5kLlxyXG5cdC8vXHJcblx0Ly8gQHByb3BlcnR5IHdyYXBMYXQ6IE51bWJlcltdXHJcblx0Ly8gTGlrZSBgd3JhcExuZ2AsIGJ1dCBmb3IgdGhlIGxhdGl0dWRlICh2ZXJ0aWNhbCkgYXhpcy5cclxuXHJcblx0Ly8gd3JhcExuZzogW21pbiwgbWF4XSxcclxuXHQvLyB3cmFwTGF0OiBbbWluLCBtYXhdLFxyXG5cclxuXHQvLyBAcHJvcGVydHkgaW5maW5pdGU6IEJvb2xlYW5cclxuXHQvLyBJZiB0cnVlLCB0aGUgY29vcmRpbmF0ZSBzcGFjZSB3aWxsIGJlIHVuYm91bmRlZCAoaW5maW5pdGUgaW4gYm90aCBheGVzKVxyXG5cdGluZmluaXRlOiBmYWxzZSxcclxuXHJcblx0Ly8gQG1ldGhvZCB3cmFwTGF0TG5nKGxhdGxuZzogTGF0TG5nKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyBhIGBMYXRMbmdgIHdoZXJlIGxhdCBhbmQgbG5nIGhhcyBiZWVuIHdyYXBwZWQgYWNjb3JkaW5nIHRvIHRoZVxyXG5cdC8vIENSUydzIGB3cmFwTGF0YCBhbmQgYHdyYXBMbmdgIHByb3BlcnRpZXMsIGlmIHRoZXkgYXJlIG91dHNpZGUgdGhlIENSUydzIGJvdW5kcy5cclxuXHR3cmFwTGF0TG5nOiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHR2YXIgbG5nID0gdGhpcy53cmFwTG5nID8gTC5VdGlsLndyYXBOdW0obGF0bG5nLmxuZywgdGhpcy53cmFwTG5nLCB0cnVlKSA6IGxhdGxuZy5sbmcsXHJcblx0XHQgICAgbGF0ID0gdGhpcy53cmFwTGF0ID8gTC5VdGlsLndyYXBOdW0obGF0bG5nLmxhdCwgdGhpcy53cmFwTGF0LCB0cnVlKSA6IGxhdGxuZy5sYXQsXHJcblx0XHQgICAgYWx0ID0gbGF0bG5nLmFsdDtcclxuXHJcblx0XHRyZXR1cm4gTC5sYXRMbmcobGF0LCBsbmcsIGFsdCk7XHJcblx0fVxyXG59O1xyXG5cblxuXG4vKlxuICogQG5hbWVzcGFjZSBDUlNcbiAqIEBjcnMgTC5DUlMuU2ltcGxlXG4gKlxuICogQSBzaW1wbGUgQ1JTIHRoYXQgbWFwcyBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGludG8gYHhgIGFuZCBgeWAgZGlyZWN0bHkuXG4gKiBNYXkgYmUgdXNlZCBmb3IgbWFwcyBvZiBmbGF0IHN1cmZhY2VzIChlLmcuIGdhbWUgbWFwcykuIE5vdGUgdGhhdCB0aGUgYHlgXG4gKiBheGlzIHNob3VsZCBzdGlsbCBiZSBpbnZlcnRlZCAoZ29pbmcgZnJvbSBib3R0b20gdG8gdG9wKS4gYGRpc3RhbmNlKClgIHJldHVybnNcbiAqIHNpbXBsZSBldWNsaWRlYW4gZGlzdGFuY2UuXG4gKi9cblxuTC5DUlMuU2ltcGxlID0gTC5leHRlbmQoe30sIEwuQ1JTLCB7XG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5Mb25MYXQsXG5cdHRyYW5zZm9ybWF0aW9uOiBuZXcgTC5UcmFuc2Zvcm1hdGlvbigxLCAwLCAtMSwgMCksXG5cblx0c2NhbGU6IGZ1bmN0aW9uICh6b29tKSB7XG5cdFx0cmV0dXJuIE1hdGgucG93KDIsIHpvb20pO1xuXHR9LFxuXG5cdHpvb206IGZ1bmN0aW9uIChzY2FsZSkge1xuXHRcdHJldHVybiBNYXRoLmxvZyhzY2FsZSkgLyBNYXRoLkxOMjtcblx0fSxcblxuXHRkaXN0YW5jZTogZnVuY3Rpb24gKGxhdGxuZzEsIGxhdGxuZzIpIHtcblx0XHR2YXIgZHggPSBsYXRsbmcyLmxuZyAtIGxhdGxuZzEubG5nLFxuXHRcdCAgICBkeSA9IGxhdGxuZzIubGF0IC0gbGF0bG5nMS5sYXQ7XG5cblx0XHRyZXR1cm4gTWF0aC5zcXJ0KGR4ICogZHggKyBkeSAqIGR5KTtcblx0fSxcblxuXHRpbmZpbml0ZTogdHJ1ZVxufSk7XG5cblxuXG4vKlxuICogQG5hbWVzcGFjZSBDUlNcbiAqIEBjcnMgTC5DUlMuRWFydGhcbiAqXG4gKiBTZXJ2ZXMgYXMgdGhlIGJhc2UgZm9yIENSUyB0aGF0IGFyZSBnbG9iYWwgc3VjaCB0aGF0IHRoZXkgY292ZXIgdGhlIGVhcnRoLlxuICogQ2FuIG9ubHkgYmUgdXNlZCBhcyB0aGUgYmFzZSBmb3Igb3RoZXIgQ1JTIGFuZCBjYW5ub3QgYmUgdXNlZCBkaXJlY3RseSxcbiAqIHNpbmNlIGl0IGRvZXMgbm90IGhhdmUgYSBgY29kZWAsIGBwcm9qZWN0aW9uYCBvciBgdHJhbnNmb3JtYXRpb25gLiBgZGlzdGFuY2UoKWAgcmV0dXJuc1xuICogbWV0ZXJzLlxuICovXG5cbkwuQ1JTLkVhcnRoID0gTC5leHRlbmQoe30sIEwuQ1JTLCB7XG5cdHdyYXBMbmc6IFstMTgwLCAxODBdLFxuXG5cdC8vIE1lYW4gRWFydGggUmFkaXVzLCBhcyByZWNvbW1lbmRlZCBmb3IgdXNlIGJ5XG5cdC8vIHRoZSBJbnRlcm5hdGlvbmFsIFVuaW9uIG9mIEdlb2Rlc3kgYW5kIEdlb3BoeXNpY3MsXG5cdC8vIHNlZSBodHRwOi8vcm9zZXR0YWNvZGUub3JnL3dpa2kvSGF2ZXJzaW5lX2Zvcm11bGFcblx0UjogNjM3MTAwMCxcblxuXHQvLyBkaXN0YW5jZSBiZXR3ZWVuIHR3byBnZW9ncmFwaGljYWwgcG9pbnRzIHVzaW5nIHNwaGVyaWNhbCBsYXcgb2YgY29zaW5lcyBhcHByb3hpbWF0aW9uXG5cdGRpc3RhbmNlOiBmdW5jdGlvbiAobGF0bG5nMSwgbGF0bG5nMikge1xuXHRcdHZhciByYWQgPSBNYXRoLlBJIC8gMTgwLFxuXHRcdCAgICBsYXQxID0gbGF0bG5nMS5sYXQgKiByYWQsXG5cdFx0ICAgIGxhdDIgPSBsYXRsbmcyLmxhdCAqIHJhZCxcblx0XHQgICAgYSA9IE1hdGguc2luKGxhdDEpICogTWF0aC5zaW4obGF0MikgK1xuXHRcdCAgICAgICAgTWF0aC5jb3MobGF0MSkgKiBNYXRoLmNvcyhsYXQyKSAqIE1hdGguY29zKChsYXRsbmcyLmxuZyAtIGxhdGxuZzEubG5nKSAqIHJhZCk7XG5cblx0XHRyZXR1cm4gdGhpcy5SICogTWF0aC5hY29zKE1hdGgubWluKGEsIDEpKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIENSU1xyXG4gKiBAY3JzIEwuQ1JTLkVQU0czODU3XHJcbiAqXHJcbiAqIFRoZSBtb3N0IGNvbW1vbiBDUlMgZm9yIG9ubGluZSBtYXBzLCB1c2VkIGJ5IGFsbW9zdCBhbGwgZnJlZSBhbmQgY29tbWVyY2lhbFxyXG4gKiB0aWxlIHByb3ZpZGVycy4gVXNlcyBTcGhlcmljYWwgTWVyY2F0b3IgcHJvamVjdGlvbi4gU2V0IGluIGJ5IGRlZmF1bHQgaW5cclxuICogTWFwJ3MgYGNyc2Agb3B0aW9uLlxyXG4gKi9cclxuXHJcbkwuQ1JTLkVQU0czODU3ID0gTC5leHRlbmQoe30sIEwuQ1JTLkVhcnRoLCB7XHJcblx0Y29kZTogJ0VQU0c6Mzg1NycsXHJcblx0cHJvamVjdGlvbjogTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yLFxyXG5cclxuXHR0cmFuc2Zvcm1hdGlvbjogKGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBzY2FsZSA9IDAuNSAvIChNYXRoLlBJICogTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yLlIpO1xyXG5cdFx0cmV0dXJuIG5ldyBMLlRyYW5zZm9ybWF0aW9uKHNjYWxlLCAwLjUsIC1zY2FsZSwgMC41KTtcclxuXHR9KCkpXHJcbn0pO1xyXG5cclxuTC5DUlMuRVBTRzkwMDkxMyA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FUFNHMzg1Nywge1xyXG5cdGNvZGU6ICdFUFNHOjkwMDkxMydcclxufSk7XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgQ1JTXHJcbiAqIEBjcnMgTC5DUlMuRVBTRzQzMjZcclxuICpcclxuICogQSBjb21tb24gQ1JTIGFtb25nIEdJUyBlbnRodXNpYXN0cy4gVXNlcyBzaW1wbGUgRXF1aXJlY3Rhbmd1bGFyIHByb2plY3Rpb24uXHJcbiAqXHJcbiAqIExlYWZsZXQgMS4wLnggY29tcGxpZXMgd2l0aCB0aGUgW1RNUyBjb29yZGluYXRlIHNjaGVtZSBmb3IgRVBTRzo0MzI2XShodHRwczovL3dpa2kub3NnZW8ub3JnL3dpa2kvVGlsZV9NYXBfU2VydmljZV9TcGVjaWZpY2F0aW9uI2dsb2JhbC1nZW9kZXRpYyksXHJcbiAqIHdoaWNoIGlzIGEgYnJlYWtpbmcgY2hhbmdlIGZyb20gMC43LnggYmVoYXZpb3VyLiAgSWYgeW91IGFyZSB1c2luZyBhIGBUaWxlTGF5ZXJgXHJcbiAqIHdpdGggdGhpcyBDUlMsIGVuc3VyZSB0aGF0IHRoZXJlIGFyZSB0d28gMjU2eDI1NiBwaXhlbCB0aWxlcyBjb3ZlcmluZyB0aGVcclxuICogd2hvbGUgZWFydGggYXQgem9vbSBsZXZlbCB6ZXJvLCBhbmQgdGhhdCB0aGUgdGlsZSBjb29yZGluYXRlIG9yaWdpbiBpcyAoLTE4MCwrOTApLFxyXG4gKiBvciAoLTE4MCwtOTApIGZvciBgVGlsZUxheWVyYHMgd2l0aCBbdGhlIGB0bXNgIG9wdGlvbl0oI3RpbGVsYXllci10bXMpIHNldC5cclxuICovXHJcblxyXG5MLkNSUy5FUFNHNDMyNiA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FYXJ0aCwge1xyXG5cdGNvZGU6ICdFUFNHOjQzMjYnLFxyXG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5Mb25MYXQsXHJcblx0dHJhbnNmb3JtYXRpb246IG5ldyBMLlRyYW5zZm9ybWF0aW9uKDEgLyAxODAsIDEsIC0xIC8gMTgwLCAwLjUpXHJcbn0pO1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgTWFwXHJcbiAqIEBha2EgTC5NYXBcclxuICogQGluaGVyaXRzIEV2ZW50ZWRcclxuICpcclxuICogVGhlIGNlbnRyYWwgY2xhc3Mgb2YgdGhlIEFQSSDigJQgaXQgaXMgdXNlZCB0byBjcmVhdGUgYSBtYXAgb24gYSBwYWdlIGFuZCBtYW5pcHVsYXRlIGl0LlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiAvLyBpbml0aWFsaXplIHRoZSBtYXAgb24gdGhlIFwibWFwXCIgZGl2IHdpdGggYSBnaXZlbiBjZW50ZXIgYW5kIHpvb21cclxuICogdmFyIG1hcCA9IEwubWFwKCdtYXAnLCB7XHJcbiAqIFx0Y2VudGVyOiBbNTEuNTA1LCAtMC4wOV0sXHJcbiAqIFx0em9vbTogMTNcclxuICogfSk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKi9cclxuXHJcbkwuTWFwID0gTC5FdmVudGVkLmV4dGVuZCh7XHJcblxyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBzZWN0aW9uIE1hcCBTdGF0ZSBPcHRpb25zXHJcblx0XHQvLyBAb3B0aW9uIGNyczogQ1JTID0gTC5DUlMuRVBTRzM4NTdcclxuXHRcdC8vIFRoZSBbQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtXSgjY3JzKSB0byB1c2UuIERvbid0IGNoYW5nZSB0aGlzIGlmIHlvdSdyZSBub3RcclxuXHRcdC8vIHN1cmUgd2hhdCBpdCBtZWFucy5cclxuXHRcdGNyczogTC5DUlMuRVBTRzM4NTcsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjZW50ZXI6IExhdExuZyA9IHVuZGVmaW5lZFxyXG5cdFx0Ly8gSW5pdGlhbCBnZW9ncmFwaGljIGNlbnRlciBvZiB0aGUgbWFwXHJcblx0XHRjZW50ZXI6IHVuZGVmaW5lZCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb206IE51bWJlciA9IHVuZGVmaW5lZFxyXG5cdFx0Ly8gSW5pdGlhbCBtYXAgem9vbSBsZXZlbFxyXG5cdFx0em9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWluWm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXHJcblx0XHQvLyBNaW5pbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcC4gT3ZlcnJpZGVzIGFueSBgbWluWm9vbWAgb3B0aW9uIHNldCBvbiBtYXAgbGF5ZXJzLlxyXG5cdFx0bWluWm9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWF4Wm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcC4gT3ZlcnJpZGVzIGFueSBgbWF4Wm9vbWAgb3B0aW9uIHNldCBvbiBtYXAgbGF5ZXJzLlxyXG5cdFx0bWF4Wm9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbGF5ZXJzOiBMYXllcltdID0gW11cclxuXHRcdC8vIEFycmF5IG9mIGxheWVycyB0aGF0IHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1hcCBpbml0aWFsbHlcclxuXHRcdGxheWVyczogW10sXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhCb3VuZHM6IExhdExuZ0JvdW5kcyA9IG51bGxcclxuXHRcdC8vIFdoZW4gdGhpcyBvcHRpb24gaXMgc2V0LCB0aGUgbWFwIHJlc3RyaWN0cyB0aGUgdmlldyB0byB0aGUgZ2l2ZW5cclxuXHRcdC8vIGdlb2dyYXBoaWNhbCBib3VuZHMsIGJvdW5jaW5nIHRoZSB1c2VyIGJhY2sgd2hlbiBoZSB0cmllcyB0byBwYW5cclxuXHRcdC8vIG91dHNpZGUgdGhlIHZpZXcuIFRvIHNldCB0aGUgcmVzdHJpY3Rpb24gZHluYW1pY2FsbHksIHVzZVxyXG5cdFx0Ly8gW2BzZXRNYXhCb3VuZHNgXSgjbWFwLXNldG1heGJvdW5kcykgbWV0aG9kLlxyXG5cdFx0bWF4Qm91bmRzOiB1bmRlZmluZWQsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiByZW5kZXJlcjogUmVuZGVyZXIgPSAqXHJcblx0XHQvLyBUaGUgZGVmYXVsdCBtZXRob2QgZm9yIGRyYXdpbmcgdmVjdG9yIGxheWVycyBvbiB0aGUgbWFwLiBgTC5TVkdgXHJcblx0XHQvLyBvciBgTC5DYW52YXNgIGJ5IGRlZmF1bHQgZGVwZW5kaW5nIG9uIGJyb3dzZXIgc3VwcG9ydC5cclxuXHRcdHJlbmRlcmVyOiB1bmRlZmluZWQsXHJcblxyXG5cclxuXHRcdC8vIEBzZWN0aW9uIEFuaW1hdGlvbiBPcHRpb25zXHJcblx0XHQvLyBAb3B0aW9uIHpvb21BbmltYXRpb246IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBXaGV0aGVyIHRoZSBtYXAgem9vbSBhbmltYXRpb24gaXMgZW5hYmxlZC4gQnkgZGVmYXVsdCBpdCdzIGVuYWJsZWRcclxuXHRcdC8vIGluIGFsbCBicm93c2VycyB0aGF0IHN1cHBvcnQgQ1NTMyBUcmFuc2l0aW9ucyBleGNlcHQgQW5kcm9pZC5cclxuXHRcdHpvb21BbmltYXRpb246IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tQW5pbWF0aW9uVGhyZXNob2xkOiBOdW1iZXIgPSA0XHJcblx0XHQvLyBXb24ndCBhbmltYXRlIHpvb20gaWYgdGhlIHpvb20gZGlmZmVyZW5jZSBleGNlZWRzIHRoaXMgdmFsdWUuXHJcblx0XHR6b29tQW5pbWF0aW9uVGhyZXNob2xkOiA0LFxyXG5cclxuXHRcdC8vIEBvcHRpb24gZmFkZUFuaW1hdGlvbjogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgdGhlIHRpbGUgZmFkZSBhbmltYXRpb24gaXMgZW5hYmxlZC4gQnkgZGVmYXVsdCBpdCdzIGVuYWJsZWRcclxuXHRcdC8vIGluIGFsbCBicm93c2VycyB0aGF0IHN1cHBvcnQgQ1NTMyBUcmFuc2l0aW9ucyBleGNlcHQgQW5kcm9pZC5cclxuXHRcdGZhZGVBbmltYXRpb246IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXJrZXJab29tQW5pbWF0aW9uOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciBtYXJrZXJzIGFuaW1hdGUgdGhlaXIgem9vbSB3aXRoIHRoZSB6b29tIGFuaW1hdGlvbiwgaWYgZGlzYWJsZWRcclxuXHRcdC8vIHRoZXkgd2lsbCBkaXNhcHBlYXIgZm9yIHRoZSBsZW5ndGggb2YgdGhlIGFuaW1hdGlvbi4gQnkgZGVmYXVsdCBpdCdzXHJcblx0XHQvLyBlbmFibGVkIGluIGFsbCBicm93c2VycyB0aGF0IHN1cHBvcnQgQ1NTMyBUcmFuc2l0aW9ucyBleGNlcHQgQW5kcm9pZC5cclxuXHRcdG1hcmtlclpvb21BbmltYXRpb246IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0cmFuc2Zvcm0zRExpbWl0OiBOdW1iZXIgPSAyXjIzXHJcblx0XHQvLyBEZWZpbmVzIHRoZSBtYXhpbXVtIHNpemUgb2YgYSBDU1MgdHJhbnNsYXRpb24gdHJhbnNmb3JtLiBUaGUgZGVmYXVsdFxyXG5cdFx0Ly8gdmFsdWUgc2hvdWxkIG5vdCBiZSBjaGFuZ2VkIHVubGVzcyBhIHdlYiBicm93c2VyIHBvc2l0aW9ucyBsYXllcnMgaW5cclxuXHRcdC8vIHRoZSB3cm9uZyBwbGFjZSBhZnRlciBkb2luZyBhIGxhcmdlIGBwYW5CeWAuXHJcblx0XHR0cmFuc2Zvcm0zRExpbWl0OiA4Mzg4NjA4LCAvLyBQcmVjaXNpb24gbGltaXQgb2YgYSAzMi1iaXQgZmxvYXRcclxuXHJcblx0XHQvLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXHJcblx0XHQvLyBAb3B0aW9uIHpvb21TbmFwOiBOdW1iZXIgPSAxXHJcblx0XHQvLyBGb3JjZXMgdGhlIG1hcCdzIHpvb20gbGV2ZWwgdG8gYWx3YXlzIGJlIGEgbXVsdGlwbGUgb2YgdGhpcywgcGFydGljdWxhcmx5XHJcblx0XHQvLyByaWdodCBhZnRlciBhIFtgZml0Qm91bmRzKClgXSgjbWFwLWZpdGJvdW5kcykgb3IgYSBwaW5jaC16b29tLlxyXG5cdFx0Ly8gQnkgZGVmYXVsdCwgdGhlIHpvb20gbGV2ZWwgc25hcHMgdG8gdGhlIG5lYXJlc3QgaW50ZWdlcjsgbG93ZXIgdmFsdWVzXHJcblx0XHQvLyAoZS5nLiBgMC41YCBvciBgMC4xYCkgYWxsb3cgZm9yIGdyZWF0ZXIgZ3JhbnVsYXJpdHkuIEEgdmFsdWUgb2YgYDBgXHJcblx0XHQvLyBtZWFucyB0aGUgem9vbSBsZXZlbCB3aWxsIG5vdCBiZSBzbmFwcGVkIGFmdGVyIGBmaXRCb3VuZHNgIG9yIGEgcGluY2gtem9vbS5cclxuXHRcdHpvb21TbmFwOiAxLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbURlbHRhOiBOdW1iZXIgPSAxXHJcblx0XHQvLyBDb250cm9scyBob3cgbXVjaCB0aGUgbWFwJ3Mgem9vbSBsZXZlbCB3aWxsIGNoYW5nZSBhZnRlciBhXHJcblx0XHQvLyBbYHpvb21JbigpYF0oI21hcC16b29taW4pLCBbYHpvb21PdXQoKWBdKCNtYXAtem9vbW91dCksIHByZXNzaW5nIGArYFxyXG5cdFx0Ly8gb3IgYC1gIG9uIHRoZSBrZXlib2FyZCwgb3IgdXNpbmcgdGhlIFt6b29tIGNvbnRyb2xzXSgjY29udHJvbC16b29tKS5cclxuXHRcdC8vIFZhbHVlcyBzbWFsbGVyIHRoYW4gYDFgIChlLmcuIGAwLjVgKSBhbGxvdyBmb3IgZ3JlYXRlciBncmFudWxhcml0eS5cclxuXHRcdHpvb21EZWx0YTogMSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHRyYWNrUmVzaXplOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFwIGF1dG9tYXRpY2FsbHkgaGFuZGxlcyBicm93c2VyIHdpbmRvdyByZXNpemUgdG8gdXBkYXRlIGl0c2VsZi5cclxuXHRcdHRyYWNrUmVzaXplOiB0cnVlXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGlkLCBvcHRpb25zKSB7IC8vIChIVE1MRWxlbWVudCBvciBTdHJpbmcsIE9iamVjdClcclxuXHRcdG9wdGlvbnMgPSBMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5faW5pdENvbnRhaW5lcihpZCk7XHJcblx0XHR0aGlzLl9pbml0TGF5b3V0KCk7XHJcblxyXG5cdFx0Ly8gaGFjayBmb3IgaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMTk4MFxyXG5cdFx0dGhpcy5fb25SZXNpemUgPSBMLmJpbmQodGhpcy5fb25SZXNpemUsIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX2luaXRFdmVudHMoKTtcclxuXHJcblx0XHRpZiAob3B0aW9ucy5tYXhCb3VuZHMpIHtcclxuXHRcdFx0dGhpcy5zZXRNYXhCb3VuZHMob3B0aW9ucy5tYXhCb3VuZHMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChvcHRpb25zLnpvb20gIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR0aGlzLl96b29tID0gdGhpcy5fbGltaXRab29tKG9wdGlvbnMuem9vbSk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMuY2VudGVyICYmIG9wdGlvbnMuem9vbSAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdHRoaXMuc2V0VmlldyhMLmxhdExuZyhvcHRpb25zLmNlbnRlciksIG9wdGlvbnMuem9vbSwge3Jlc2V0OiB0cnVlfSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faGFuZGxlcnMgPSBbXTtcclxuXHRcdHRoaXMuX2xheWVycyA9IHt9O1xyXG5cdFx0dGhpcy5fem9vbUJvdW5kTGF5ZXJzID0ge307XHJcblx0XHR0aGlzLl9zaXplQ2hhbmdlZCA9IHRydWU7XHJcblxyXG5cdFx0dGhpcy5jYWxsSW5pdEhvb2tzKCk7XHJcblxyXG5cdFx0Ly8gZG9uJ3QgYW5pbWF0ZSBvbiBicm93c2VycyB3aXRob3V0IGhhcmR3YXJlLWFjY2VsZXJhdGVkIHRyYW5zaXRpb25zIG9yIG9sZCBBbmRyb2lkL09wZXJhXHJcblx0XHR0aGlzLl96b29tQW5pbWF0ZWQgPSBMLkRvbVV0aWwuVFJBTlNJVElPTiAmJiBMLkJyb3dzZXIuYW55M2QgJiYgIUwuQnJvd3Nlci5tb2JpbGVPcGVyYSAmJlxyXG5cdFx0XHRcdHRoaXMub3B0aW9ucy56b29tQW5pbWF0aW9uO1xyXG5cclxuXHRcdC8vIHpvb20gdHJhbnNpdGlvbnMgcnVuIHdpdGggdGhlIHNhbWUgZHVyYXRpb24gZm9yIGFsbCBsYXllcnMsIHNvIGlmIG9uZSBvZiB0cmFuc2l0aW9uZW5kIGV2ZW50c1xyXG5cdFx0Ly8gaGFwcGVucyBhZnRlciBzdGFydGluZyB6b29tIGFuaW1hdGlvbiAocHJvcGFnYXRpbmcgdG8gdGhlIG1hcCBwYW5lKSwgd2Uga25vdyB0aGF0IGl0IGVuZGVkIGdsb2JhbGx5XHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdHRoaXMuX2NyZWF0ZUFuaW1Qcm94eSgpO1xyXG5cdFx0XHRMLkRvbUV2ZW50Lm9uKHRoaXMuX3Byb3h5LCBMLkRvbVV0aWwuVFJBTlNJVElPTl9FTkQsIHRoaXMuX2NhdGNoVHJhbnNpdGlvbkVuZCwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fYWRkTGF5ZXJzKHRoaXMub3B0aW9ucy5sYXllcnMpO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBAc2VjdGlvbiBNZXRob2RzIGZvciBtb2RpZnlpbmcgbWFwIHN0YXRlXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0VmlldyhjZW50ZXI6IExhdExuZywgem9vbTogTnVtYmVyLCBvcHRpb25zPzogWm9vbS9wYW4gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSB2aWV3IG9mIHRoZSBtYXAgKGdlb2dyYXBoaWNhbCBjZW50ZXIgYW5kIHpvb20pIHdpdGggdGhlIGdpdmVuXHJcblx0Ly8gYW5pbWF0aW9uIG9wdGlvbnMuXHJcblx0c2V0VmlldzogZnVuY3Rpb24gKGNlbnRlciwgem9vbSwgb3B0aW9ucykge1xyXG5cclxuXHRcdHpvb20gPSB6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogdGhpcy5fbGltaXRab29tKHpvb20pO1xyXG5cdFx0Y2VudGVyID0gdGhpcy5fbGltaXRDZW50ZXIoTC5sYXRMbmcoY2VudGVyKSwgem9vbSwgdGhpcy5vcHRpb25zLm1heEJvdW5kcyk7XHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHR0aGlzLl9zdG9wKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCAmJiAhb3B0aW9ucy5yZXNldCAmJiBvcHRpb25zICE9PSB0cnVlKSB7XHJcblxyXG5cdFx0XHRpZiAob3B0aW9ucy5hbmltYXRlICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb20gPSBMLmV4dGVuZCh7YW5pbWF0ZTogb3B0aW9ucy5hbmltYXRlfSwgb3B0aW9ucy56b29tKTtcclxuXHRcdFx0XHRvcHRpb25zLnBhbiA9IEwuZXh0ZW5kKHthbmltYXRlOiBvcHRpb25zLmFuaW1hdGUsIGR1cmF0aW9uOiBvcHRpb25zLmR1cmF0aW9ufSwgb3B0aW9ucy5wYW4pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyB0cnkgYW5pbWF0aW5nIHBhbiBvciB6b29tXHJcblx0XHRcdHZhciBtb3ZlZCA9ICh0aGlzLl96b29tICE9PSB6b29tKSA/XHJcblx0XHRcdFx0dGhpcy5fdHJ5QW5pbWF0ZWRab29tICYmIHRoaXMuX3RyeUFuaW1hdGVkWm9vbShjZW50ZXIsIHpvb20sIG9wdGlvbnMuem9vbSkgOlxyXG5cdFx0XHRcdHRoaXMuX3RyeUFuaW1hdGVkUGFuKGNlbnRlciwgb3B0aW9ucy5wYW4pO1xyXG5cclxuXHRcdFx0aWYgKG1vdmVkKSB7XHJcblx0XHRcdFx0Ly8gcHJldmVudCByZXNpemUgaGFuZGxlciBjYWxsLCB0aGUgdmlldyB3aWxsIHJlZnJlc2ggYWZ0ZXIgYW5pbWF0aW9uIGFueXdheVxyXG5cdFx0XHRcdGNsZWFyVGltZW91dCh0aGlzLl9zaXplVGltZXIpO1xyXG5cdFx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gYW5pbWF0aW9uIGRpZG4ndCBzdGFydCwganVzdCByZXNldCB0aGUgbWFwIHZpZXdcclxuXHRcdHRoaXMuX3Jlc2V0VmlldyhjZW50ZXIsIHpvb20pO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0Wm9vbSh6b29tOiBOdW1iZXIsIG9wdGlvbnM6IFpvb20vcGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgem9vbSBvZiB0aGUgbWFwLlxyXG5cdHNldFpvb206IGZ1bmN0aW9uICh6b29tLCBvcHRpb25zKSB7XHJcblx0XHRpZiAoIXRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHR0aGlzLl96b29tID0gem9vbTtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRWaWV3KHRoaXMuZ2V0Q2VudGVyKCksIHpvb20sIHt6b29tOiBvcHRpb25zfSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB6b29tSW4oZGVsdGE/OiBOdW1iZXIsIG9wdGlvbnM/OiBab29tIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gSW5jcmVhc2VzIHRoZSB6b29tIG9mIHRoZSBtYXAgYnkgYGRlbHRhYCAoW2B6b29tRGVsdGFgXSgjbWFwLXpvb21kZWx0YSkgYnkgZGVmYXVsdCkuXHJcblx0em9vbUluOiBmdW5jdGlvbiAoZGVsdGEsIG9wdGlvbnMpIHtcclxuXHRcdGRlbHRhID0gZGVsdGEgfHwgKEwuQnJvd3Nlci5hbnkzZCA/IHRoaXMub3B0aW9ucy56b29tRGVsdGEgOiAxKTtcclxuXHRcdHJldHVybiB0aGlzLnNldFpvb20odGhpcy5fem9vbSArIGRlbHRhLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHpvb21PdXQoZGVsdGE/OiBOdW1iZXIsIG9wdGlvbnM/OiBab29tIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gRGVjcmVhc2VzIHRoZSB6b29tIG9mIHRoZSBtYXAgYnkgYGRlbHRhYCAoW2B6b29tRGVsdGFgXSgjbWFwLXpvb21kZWx0YSkgYnkgZGVmYXVsdCkuXHJcblx0em9vbU91dDogZnVuY3Rpb24gKGRlbHRhLCBvcHRpb25zKSB7XHJcblx0XHRkZWx0YSA9IGRlbHRhIHx8IChMLkJyb3dzZXIuYW55M2QgPyB0aGlzLm9wdGlvbnMuem9vbURlbHRhIDogMSk7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHRoaXMuX3pvb20gLSBkZWx0YSwgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRab29tQXJvdW5kKGxhdGxuZzogTGF0TG5nLCB6b29tOiBOdW1iZXIsIG9wdGlvbnM6IFpvb20gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBab29tcyB0aGUgbWFwIHdoaWxlIGtlZXBpbmcgYSBzcGVjaWZpZWQgZ2VvZ3JhcGhpY2FsIHBvaW50IG9uIHRoZSBtYXBcclxuXHQvLyBzdGF0aW9uYXJ5IChlLmcuIHVzZWQgaW50ZXJuYWxseSBmb3Igc2Nyb2xsIHpvb20gYW5kIGRvdWJsZS1jbGljayB6b29tKS5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIHNldFpvb21Bcm91bmQob2Zmc2V0OiBQb2ludCwgem9vbTogTnVtYmVyLCBvcHRpb25zOiBab29tIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gWm9vbXMgdGhlIG1hcCB3aGlsZSBrZWVwaW5nIGEgc3BlY2lmaWVkIHBpeGVsIG9uIHRoZSBtYXAgKHJlbGF0aXZlIHRvIHRoZSB0b3AtbGVmdCBjb3JuZXIpIHN0YXRpb25hcnkuXHJcblx0c2V0Wm9vbUFyb3VuZDogZnVuY3Rpb24gKGxhdGxuZywgem9vbSwgb3B0aW9ucykge1xyXG5cdFx0dmFyIHNjYWxlID0gdGhpcy5nZXRab29tU2NhbGUoem9vbSksXHJcblx0XHQgICAgdmlld0hhbGYgPSB0aGlzLmdldFNpemUoKS5kaXZpZGVCeSgyKSxcclxuXHRcdCAgICBjb250YWluZXJQb2ludCA9IGxhdGxuZyBpbnN0YW5jZW9mIEwuUG9pbnQgPyBsYXRsbmcgOiB0aGlzLmxhdExuZ1RvQ29udGFpbmVyUG9pbnQobGF0bG5nKSxcclxuXHJcblx0XHQgICAgY2VudGVyT2Zmc2V0ID0gY29udGFpbmVyUG9pbnQuc3VidHJhY3Qodmlld0hhbGYpLm11bHRpcGx5QnkoMSAtIDEgLyBzY2FsZSksXHJcblx0XHQgICAgbmV3Q2VudGVyID0gdGhpcy5jb250YWluZXJQb2ludFRvTGF0TG5nKHZpZXdIYWxmLmFkZChjZW50ZXJPZmZzZXQpKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy5zZXRWaWV3KG5ld0NlbnRlciwgem9vbSwge3pvb206IG9wdGlvbnN9KTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0Qm91bmRzQ2VudGVyWm9vbTogZnVuY3Rpb24gKGJvdW5kcywgb3B0aW9ucykge1xyXG5cclxuXHRcdG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xyXG5cdFx0Ym91bmRzID0gYm91bmRzLmdldEJvdW5kcyA/IGJvdW5kcy5nZXRCb3VuZHMoKSA6IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0dmFyIHBhZGRpbmdUTCA9IEwucG9pbnQob3B0aW9ucy5wYWRkaW5nVG9wTGVmdCB8fCBvcHRpb25zLnBhZGRpbmcgfHwgWzAsIDBdKSxcclxuXHRcdCAgICBwYWRkaW5nQlIgPSBMLnBvaW50KG9wdGlvbnMucGFkZGluZ0JvdHRvbVJpZ2h0IHx8IG9wdGlvbnMucGFkZGluZyB8fCBbMCwgMF0pLFxyXG5cclxuXHRcdCAgICB6b29tID0gdGhpcy5nZXRCb3VuZHNab29tKGJvdW5kcywgZmFsc2UsIHBhZGRpbmdUTC5hZGQocGFkZGluZ0JSKSk7XHJcblxyXG5cdFx0em9vbSA9ICh0eXBlb2Ygb3B0aW9ucy5tYXhab29tID09PSAnbnVtYmVyJykgPyBNYXRoLm1pbihvcHRpb25zLm1heFpvb20sIHpvb20pIDogem9vbTtcclxuXHJcblx0XHR2YXIgcGFkZGluZ09mZnNldCA9IHBhZGRpbmdCUi5zdWJ0cmFjdChwYWRkaW5nVEwpLmRpdmlkZUJ5KDIpLFxyXG5cclxuXHRcdCAgICBzd1BvaW50ID0gdGhpcy5wcm9qZWN0KGJvdW5kcy5nZXRTb3V0aFdlc3QoKSwgem9vbSksXHJcblx0XHQgICAgbmVQb2ludCA9IHRoaXMucHJvamVjdChib3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLFxyXG5cdFx0ICAgIGNlbnRlciA9IHRoaXMudW5wcm9qZWN0KHN3UG9pbnQuYWRkKG5lUG9pbnQpLmRpdmlkZUJ5KDIpLmFkZChwYWRkaW5nT2Zmc2V0KSwgem9vbSk7XHJcblxyXG5cdFx0cmV0dXJuIHtcclxuXHRcdFx0Y2VudGVyOiBjZW50ZXIsXHJcblx0XHRcdHpvb206IHpvb21cclxuXHRcdH07XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmaXRCb3VuZHMoYm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM6IGZpdEJvdW5kcyBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgYSBtYXAgdmlldyB0aGF0IGNvbnRhaW5zIHRoZSBnaXZlbiBnZW9ncmFwaGljYWwgYm91bmRzIHdpdGggdGhlXHJcblx0Ly8gbWF4aW11bSB6b29tIGxldmVsIHBvc3NpYmxlLlxyXG5cdGZpdEJvdW5kczogZnVuY3Rpb24gKGJvdW5kcywgb3B0aW9ucykge1xyXG5cclxuXHRcdGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0aWYgKCFib3VuZHMuaXNWYWxpZCgpKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignQm91bmRzIGFyZSBub3QgdmFsaWQuJyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHRhcmdldCA9IHRoaXMuX2dldEJvdW5kc0NlbnRlclpvb20oYm91bmRzLCBvcHRpb25zKTtcclxuXHRcdHJldHVybiB0aGlzLnNldFZpZXcodGFyZ2V0LmNlbnRlciwgdGFyZ2V0Lnpvb20sIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZml0V29ybGQob3B0aW9ucz86IGZpdEJvdW5kcyBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgYSBtYXAgdmlldyB0aGF0IG1vc3RseSBjb250YWlucyB0aGUgd2hvbGUgd29ybGQgd2l0aCB0aGUgbWF4aW11bVxyXG5cdC8vIHpvb20gbGV2ZWwgcG9zc2libGUuXHJcblx0Zml0V29ybGQ6IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5maXRCb3VuZHMoW1stOTAsIC0xODBdLCBbOTAsIDE4MF1dLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBhblRvKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogUGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gUGFucyB0aGUgbWFwIHRvIGEgZ2l2ZW4gY2VudGVyLlxyXG5cdHBhblRvOiBmdW5jdGlvbiAoY2VudGVyLCBvcHRpb25zKSB7IC8vIChMYXRMbmcpXHJcblx0XHRyZXR1cm4gdGhpcy5zZXRWaWV3KGNlbnRlciwgdGhpcy5fem9vbSwge3Bhbjogb3B0aW9uc30pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcGFuQnkob2Zmc2V0OiBQb2ludCk6IHRoaXNcclxuXHQvLyBQYW5zIHRoZSBtYXAgYnkgYSBnaXZlbiBudW1iZXIgb2YgcGl4ZWxzIChhbmltYXRlZCkuXHJcblx0cGFuQnk6IGZ1bmN0aW9uIChvZmZzZXQsIG9wdGlvbnMpIHtcclxuXHRcdG9mZnNldCA9IEwucG9pbnQob2Zmc2V0KS5yb3VuZCgpO1xyXG5cdFx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcblxyXG5cdFx0aWYgKCFvZmZzZXQueCAmJiAhb2Zmc2V0LnkpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZWVuZCcpO1xyXG5cdFx0fVxyXG5cdFx0Ly8gSWYgd2UgcGFuIHRvbyBmYXIsIENocm9tZSBnZXRzIGlzc3VlcyB3aXRoIHRpbGVzXHJcblx0XHQvLyBhbmQgbWFrZXMgdGhlbSBkaXNhcHBlYXIgb3IgYXBwZWFyIGluIHRoZSB3cm9uZyBwbGFjZSAoc2xpZ2h0bHkgb2Zmc2V0KSAjMjYwMlxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gdHJ1ZSAmJiAhdGhpcy5nZXRTaXplKCkuY29udGFpbnMob2Zmc2V0KSkge1xyXG5cdFx0XHR0aGlzLl9yZXNldFZpZXcodGhpcy51bnByb2plY3QodGhpcy5wcm9qZWN0KHRoaXMuZ2V0Q2VudGVyKCkpLmFkZChvZmZzZXQpKSwgdGhpcy5nZXRab29tKCkpO1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXRoaXMuX3BhbkFuaW0pIHtcclxuXHRcdFx0dGhpcy5fcGFuQW5pbSA9IG5ldyBMLlBvc0FuaW1hdGlvbigpO1xyXG5cclxuXHRcdFx0dGhpcy5fcGFuQW5pbS5vbih7XHJcblx0XHRcdFx0J3N0ZXAnOiB0aGlzLl9vblBhblRyYW5zaXRpb25TdGVwLFxyXG5cdFx0XHRcdCdlbmQnOiB0aGlzLl9vblBhblRyYW5zaXRpb25FbmRcclxuXHRcdFx0fSwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gZG9uJ3QgZmlyZSBtb3Zlc3RhcnQgaWYgYW5pbWF0aW5nIGluZXJ0aWFcclxuXHRcdGlmICghb3B0aW9ucy5ub01vdmVTdGFydCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ21vdmVzdGFydCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIGFuaW1hdGUgcGFuIHVubGVzcyBhbmltYXRlOiBmYWxzZSBzcGVjaWZpZWRcclxuXHRcdGlmIChvcHRpb25zLmFuaW1hdGUgIT09IGZhbHNlKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC1wYW4tYW5pbScpO1xyXG5cclxuXHRcdFx0dmFyIG5ld1BvcyA9IHRoaXMuX2dldE1hcFBhbmVQb3MoKS5zdWJ0cmFjdChvZmZzZXQpLnJvdW5kKCk7XHJcblx0XHRcdHRoaXMuX3BhbkFuaW0ucnVuKHRoaXMuX21hcFBhbmUsIG5ld1Bvcywgb3B0aW9ucy5kdXJhdGlvbiB8fCAwLjI1LCBvcHRpb25zLmVhc2VMaW5lYXJpdHkpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0dGhpcy5fcmF3UGFuQnkob2Zmc2V0KTtcclxuXHRcdFx0dGhpcy5maXJlKCdtb3ZlJykuZmlyZSgnbW92ZWVuZCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZmx5VG8obGF0bG5nOiBMYXRMbmcsIHpvb20/OiBOdW1iZXIsIG9wdGlvbnM/OiBab29tL3BhbiBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHZpZXcgb2YgdGhlIG1hcCAoZ2VvZ3JhcGhpY2FsIGNlbnRlciBhbmQgem9vbSkgcGVyZm9ybWluZyBhIHNtb290aFxyXG5cdC8vIHBhbi16b29tIGFuaW1hdGlvbi5cclxuXHRmbHlUbzogZnVuY3Rpb24gKHRhcmdldENlbnRlciwgdGFyZ2V0Wm9vbSwgb3B0aW9ucykge1xyXG5cclxuXHRcdG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSA9PT0gZmFsc2UgfHwgIUwuQnJvd3Nlci5hbnkzZCkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5zZXRWaWV3KHRhcmdldENlbnRlciwgdGFyZ2V0Wm9vbSwgb3B0aW9ucyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fc3RvcCgpO1xyXG5cclxuXHRcdHZhciBmcm9tID0gdGhpcy5wcm9qZWN0KHRoaXMuZ2V0Q2VudGVyKCkpLFxyXG5cdFx0ICAgIHRvID0gdGhpcy5wcm9qZWN0KHRhcmdldENlbnRlciksXHJcblx0XHQgICAgc2l6ZSA9IHRoaXMuZ2V0U2l6ZSgpLFxyXG5cdFx0ICAgIHN0YXJ0Wm9vbSA9IHRoaXMuX3pvb207XHJcblxyXG5cdFx0dGFyZ2V0Q2VudGVyID0gTC5sYXRMbmcodGFyZ2V0Q2VudGVyKTtcclxuXHRcdHRhcmdldFpvb20gPSB0YXJnZXRab29tID09PSB1bmRlZmluZWQgPyBzdGFydFpvb20gOiB0YXJnZXRab29tO1xyXG5cclxuXHRcdHZhciB3MCA9IE1hdGgubWF4KHNpemUueCwgc2l6ZS55KSxcclxuXHRcdCAgICB3MSA9IHcwICogdGhpcy5nZXRab29tU2NhbGUoc3RhcnRab29tLCB0YXJnZXRab29tKSxcclxuXHRcdCAgICB1MSA9ICh0by5kaXN0YW5jZVRvKGZyb20pKSB8fCAxLFxyXG5cdFx0ICAgIHJobyA9IDEuNDIsXHJcblx0XHQgICAgcmhvMiA9IHJobyAqIHJobztcclxuXHJcblx0XHRmdW5jdGlvbiByKGkpIHtcclxuXHRcdFx0dmFyIHMxID0gaSA/IC0xIDogMSxcclxuXHRcdFx0ICAgIHMyID0gaSA/IHcxIDogdzAsXHJcblx0XHRcdCAgICB0MSA9IHcxICogdzEgLSB3MCAqIHcwICsgczEgKiByaG8yICogcmhvMiAqIHUxICogdTEsXHJcblx0XHRcdCAgICBiMSA9IDIgKiBzMiAqIHJobzIgKiB1MSxcclxuXHRcdFx0ICAgIGIgPSB0MSAvIGIxLFxyXG5cdFx0XHQgICAgc3EgPSBNYXRoLnNxcnQoYiAqIGIgKyAxKSAtIGI7XHJcblxyXG5cdFx0XHQgICAgLy8gd29ya2Fyb3VuZCBmb3IgZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uIGJ1ZyB3aGVuIHNxID0gMCwgbG9nID0gLUluZmluaXRlLFxyXG5cdFx0XHQgICAgLy8gdGh1cyB0cmlnZ2VyaW5nIGFuIGluZmluaXRlIGxvb3AgaW4gZmx5VG9cclxuXHRcdFx0ICAgIHZhciBsb2cgPSBzcSA8IDAuMDAwMDAwMDAxID8gLTE4IDogTWF0aC5sb2coc3EpO1xyXG5cclxuXHRcdFx0cmV0dXJuIGxvZztcclxuXHRcdH1cclxuXHJcblx0XHRmdW5jdGlvbiBzaW5oKG4pIHsgcmV0dXJuIChNYXRoLmV4cChuKSAtIE1hdGguZXhwKC1uKSkgLyAyOyB9XHJcblx0XHRmdW5jdGlvbiBjb3NoKG4pIHsgcmV0dXJuIChNYXRoLmV4cChuKSArIE1hdGguZXhwKC1uKSkgLyAyOyB9XHJcblx0XHRmdW5jdGlvbiB0YW5oKG4pIHsgcmV0dXJuIHNpbmgobikgLyBjb3NoKG4pOyB9XHJcblxyXG5cdFx0dmFyIHIwID0gcigwKTtcclxuXHJcblx0XHRmdW5jdGlvbiB3KHMpIHsgcmV0dXJuIHcwICogKGNvc2gocjApIC8gY29zaChyMCArIHJobyAqIHMpKTsgfVxyXG5cdFx0ZnVuY3Rpb24gdShzKSB7IHJldHVybiB3MCAqIChjb3NoKHIwKSAqIHRhbmgocjAgKyByaG8gKiBzKSAtIHNpbmgocjApKSAvIHJobzI7IH1cclxuXHJcblx0XHRmdW5jdGlvbiBlYXNlT3V0KHQpIHsgcmV0dXJuIDEgLSBNYXRoLnBvdygxIC0gdCwgMS41KTsgfVxyXG5cclxuXHRcdHZhciBzdGFydCA9IERhdGUubm93KCksXHJcblx0XHQgICAgUyA9IChyKDEpIC0gcjApIC8gcmhvLFxyXG5cdFx0ICAgIGR1cmF0aW9uID0gb3B0aW9ucy5kdXJhdGlvbiA/IDEwMDAgKiBvcHRpb25zLmR1cmF0aW9uIDogMTAwMCAqIFMgKiAwLjg7XHJcblxyXG5cdFx0ZnVuY3Rpb24gZnJhbWUoKSB7XHJcblx0XHRcdHZhciB0ID0gKERhdGUubm93KCkgLSBzdGFydCkgLyBkdXJhdGlvbixcclxuXHRcdFx0ICAgIHMgPSBlYXNlT3V0KHQpICogUztcclxuXHJcblx0XHRcdGlmICh0IDw9IDEpIHtcclxuXHRcdFx0XHR0aGlzLl9mbHlUb0ZyYW1lID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUoZnJhbWUsIHRoaXMpO1xyXG5cclxuXHRcdFx0XHR0aGlzLl9tb3ZlKFxyXG5cdFx0XHRcdFx0dGhpcy51bnByb2plY3QoZnJvbS5hZGQodG8uc3VidHJhY3QoZnJvbSkubXVsdGlwbHlCeSh1KHMpIC8gdTEpKSwgc3RhcnRab29tKSxcclxuXHRcdFx0XHRcdHRoaXMuZ2V0U2NhbGVab29tKHcwIC8gdyhzKSwgc3RhcnRab29tKSxcclxuXHRcdFx0XHRcdHtmbHlUbzogdHJ1ZX0pO1xyXG5cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHR0aGlzXHJcblx0XHRcdFx0XHQuX21vdmUodGFyZ2V0Q2VudGVyLCB0YXJnZXRab29tKVxyXG5cdFx0XHRcdFx0Ll9tb3ZlRW5kKHRydWUpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fbW92ZVN0YXJ0KHRydWUpO1xyXG5cclxuXHRcdGZyYW1lLmNhbGwodGhpcyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGZseVRvQm91bmRzKGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogZml0Qm91bmRzIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdmlldyBvZiB0aGUgbWFwIHdpdGggYSBzbW9vdGggYW5pbWF0aW9uIGxpa2UgW2BmbHlUb2BdKCNtYXAtZmx5dG8pLFxyXG5cdC8vIGJ1dCB0YWtlcyBhIGJvdW5kcyBwYXJhbWV0ZXIgbGlrZSBbYGZpdEJvdW5kc2BdKCNtYXAtZml0Ym91bmRzKS5cclxuXHRmbHlUb0JvdW5kczogZnVuY3Rpb24gKGJvdW5kcywgb3B0aW9ucykge1xyXG5cdFx0dmFyIHRhcmdldCA9IHRoaXMuX2dldEJvdW5kc0NlbnRlclpvb20oYm91bmRzLCBvcHRpb25zKTtcclxuXHRcdHJldHVybiB0aGlzLmZseVRvKHRhcmdldC5jZW50ZXIsIHRhcmdldC56b29tLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE1heEJvdW5kcyhib3VuZHM6IEJvdW5kcyk6IHRoaXNcclxuXHQvLyBSZXN0cmljdHMgdGhlIG1hcCB2aWV3IHRvIHRoZSBnaXZlbiBib3VuZHMgKHNlZSB0aGUgW21heEJvdW5kc10oI21hcC1tYXhib3VuZHMpIG9wdGlvbikuXHJcblx0c2V0TWF4Qm91bmRzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHRib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdGlmICghYm91bmRzLmlzVmFsaWQoKSkge1xyXG5cdFx0XHR0aGlzLm9wdGlvbnMubWF4Qm91bmRzID0gbnVsbDtcclxuXHRcdFx0cmV0dXJuIHRoaXMub2ZmKCdtb3ZlZW5kJywgdGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKTtcclxuXHRcdH0gZWxzZSBpZiAodGhpcy5vcHRpb25zLm1heEJvdW5kcykge1xyXG5cdFx0XHR0aGlzLm9mZignbW92ZWVuZCcsIHRoaXMuX3Bhbkluc2lkZU1heEJvdW5kcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5vcHRpb25zLm1heEJvdW5kcyA9IGJvdW5kcztcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdHRoaXMuX3Bhbkluc2lkZU1heEJvdW5kcygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzLm9uKCdtb3ZlZW5kJywgdGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE1pblpvb20oem9vbTogTnVtYmVyKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIGxvd2VyIGxpbWl0IGZvciB0aGUgYXZhaWxhYmxlIHpvb20gbGV2ZWxzIChzZWUgdGhlIFttaW5ab29tXSgjbWFwLW1pbnpvb20pIG9wdGlvbikuXHJcblx0c2V0TWluWm9vbTogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdHRoaXMub3B0aW9ucy5taW5ab29tID0gem9vbTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkICYmIHRoaXMuZ2V0Wm9vbSgpIDwgdGhpcy5vcHRpb25zLm1pblpvb20pIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMuc2V0Wm9vbSh6b29tKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE1heFpvb20oem9vbTogTnVtYmVyKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHVwcGVyIGxpbWl0IGZvciB0aGUgYXZhaWxhYmxlIHpvb20gbGV2ZWxzIChzZWUgdGhlIFttYXhab29tXSgjbWFwLW1heHpvb20pIG9wdGlvbikuXHJcblx0c2V0TWF4Wm9vbTogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdHRoaXMub3B0aW9ucy5tYXhab29tID0gem9vbTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkICYmICh0aGlzLmdldFpvb20oKSA+IHRoaXMub3B0aW9ucy5tYXhab29tKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHpvb20pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcGFuSW5zaWRlQm91bmRzKGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogUGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gUGFucyB0aGUgbWFwIHRvIHRoZSBjbG9zZXN0IHZpZXcgdGhhdCB3b3VsZCBsaWUgaW5zaWRlIHRoZSBnaXZlbiBib3VuZHMgKGlmIGl0J3Mgbm90IGFscmVhZHkpLCBjb250cm9sbGluZyB0aGUgYW5pbWF0aW9uIHVzaW5nIHRoZSBvcHRpb25zIHNwZWNpZmljLCBpZiBhbnkuXHJcblx0cGFuSW5zaWRlQm91bmRzOiBmdW5jdGlvbiAoYm91bmRzLCBvcHRpb25zKSB7XHJcblx0XHR0aGlzLl9lbmZvcmNpbmdCb3VuZHMgPSB0cnVlO1xyXG5cdFx0dmFyIGNlbnRlciA9IHRoaXMuZ2V0Q2VudGVyKCksXHJcblx0XHQgICAgbmV3Q2VudGVyID0gdGhpcy5fbGltaXRDZW50ZXIoY2VudGVyLCB0aGlzLl96b29tLCBMLmxhdExuZ0JvdW5kcyhib3VuZHMpKTtcclxuXHJcblx0XHRpZiAoIWNlbnRlci5lcXVhbHMobmV3Q2VudGVyKSkge1xyXG5cdFx0XHR0aGlzLnBhblRvKG5ld0NlbnRlciwgb3B0aW9ucyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fZW5mb3JjaW5nQm91bmRzID0gZmFsc2U7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGludmFsaWRhdGVTaXplKG9wdGlvbnM6IFpvb20vUGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gQ2hlY2tzIGlmIHRoZSBtYXAgY29udGFpbmVyIHNpemUgY2hhbmdlZCBhbmQgdXBkYXRlcyB0aGUgbWFwIGlmIHNvIOKAlFxyXG5cdC8vIGNhbGwgaXQgYWZ0ZXIgeW91J3ZlIGNoYW5nZWQgdGhlIG1hcCBzaXplIGR5bmFtaWNhbGx5LCBhbHNvIGFuaW1hdGluZ1xyXG5cdC8vIHBhbiBieSBkZWZhdWx0LiBJZiBgb3B0aW9ucy5wYW5gIGlzIGBmYWxzZWAsIHBhbm5pbmcgd2lsbCBub3Qgb2NjdXIuXHJcblx0Ly8gSWYgYG9wdGlvbnMuZGVib3VuY2VNb3ZlZW5kYCBpcyBgdHJ1ZWAsIGl0IHdpbGwgZGVsYXkgYG1vdmVlbmRgIGV2ZW50IHNvXHJcblx0Ly8gdGhhdCBpdCBkb2Vzbid0IGhhcHBlbiBvZnRlbiBldmVuIGlmIHRoZSBtZXRob2QgaXMgY2FsbGVkIG1hbnlcclxuXHQvLyB0aW1lcyBpbiBhIHJvdy5cclxuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCBpbnZhbGlkYXRlU2l6ZShhbmltYXRlOiBCb29sZWFuKTogdGhpc1xyXG5cdC8vIENoZWNrcyBpZiB0aGUgbWFwIGNvbnRhaW5lciBzaXplIGNoYW5nZWQgYW5kIHVwZGF0ZXMgdGhlIG1hcCBpZiBzbyDigJRcclxuXHQvLyBjYWxsIGl0IGFmdGVyIHlvdSd2ZSBjaGFuZ2VkIHRoZSBtYXAgc2l6ZSBkeW5hbWljYWxseSwgYWxzbyBhbmltYXRpbmdcclxuXHQvLyBwYW4gYnkgZGVmYXVsdC5cclxuXHRpbnZhbGlkYXRlU2l6ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRcdGlmICghdGhpcy5fbG9hZGVkKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0b3B0aW9ucyA9IEwuZXh0ZW5kKHtcclxuXHRcdFx0YW5pbWF0ZTogZmFsc2UsXHJcblx0XHRcdHBhbjogdHJ1ZVxyXG5cdFx0fSwgb3B0aW9ucyA9PT0gdHJ1ZSA/IHthbmltYXRlOiB0cnVlfSA6IG9wdGlvbnMpO1xyXG5cclxuXHRcdHZhciBvbGRTaXplID0gdGhpcy5nZXRTaXplKCk7XHJcblx0XHR0aGlzLl9zaXplQ2hhbmdlZCA9IHRydWU7XHJcblx0XHR0aGlzLl9sYXN0Q2VudGVyID0gbnVsbDtcclxuXHJcblx0XHR2YXIgbmV3U2l6ZSA9IHRoaXMuZ2V0U2l6ZSgpLFxyXG5cdFx0ICAgIG9sZENlbnRlciA9IG9sZFNpemUuZGl2aWRlQnkoMikucm91bmQoKSxcclxuXHRcdCAgICBuZXdDZW50ZXIgPSBuZXdTaXplLmRpdmlkZUJ5KDIpLnJvdW5kKCksXHJcblx0XHQgICAgb2Zmc2V0ID0gb2xkQ2VudGVyLnN1YnRyYWN0KG5ld0NlbnRlcik7XHJcblxyXG5cdFx0aWYgKCFvZmZzZXQueCAmJiAhb2Zmc2V0LnkpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRpZiAob3B0aW9ucy5hbmltYXRlICYmIG9wdGlvbnMucGFuKSB7XHJcblx0XHRcdHRoaXMucGFuQnkob2Zmc2V0KTtcclxuXHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRpZiAob3B0aW9ucy5wYW4pIHtcclxuXHRcdFx0XHR0aGlzLl9yYXdQYW5CeShvZmZzZXQpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR0aGlzLmZpcmUoJ21vdmUnKTtcclxuXHJcblx0XHRcdGlmIChvcHRpb25zLmRlYm91bmNlTW92ZWVuZCkge1xyXG5cdFx0XHRcdGNsZWFyVGltZW91dCh0aGlzLl9zaXplVGltZXIpO1xyXG5cdFx0XHRcdHRoaXMuX3NpemVUaW1lciA9IHNldFRpbWVvdXQoTC5iaW5kKHRoaXMuZmlyZSwgdGhpcywgJ21vdmVlbmQnKSwgMjAwKTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHR0aGlzLmZpcmUoJ21vdmVlbmQnKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEBzZWN0aW9uIE1hcCBzdGF0ZSBjaGFuZ2UgZXZlbnRzXHJcblx0XHQvLyBAZXZlbnQgcmVzaXplOiBSZXNpemVFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIGlzIHJlc2l6ZWQuXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdyZXNpemUnLCB7XHJcblx0XHRcdG9sZFNpemU6IG9sZFNpemUsXHJcblx0XHRcdG5ld1NpemU6IG5ld1NpemVcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIE1ldGhvZHMgZm9yIG1vZGlmeWluZyBtYXAgc3RhdGVcclxuXHQvLyBAbWV0aG9kIHN0b3AoKTogdGhpc1xyXG5cdC8vIFN0b3BzIHRoZSBjdXJyZW50bHkgcnVubmluZyBgcGFuVG9gIG9yIGBmbHlUb2AgYW5pbWF0aW9uLCBpZiBhbnkuXHJcblx0c3RvcDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5zZXRab29tKHRoaXMuX2xpbWl0Wm9vbSh0aGlzLl96b29tKSk7XHJcblx0XHRpZiAoIXRoaXMub3B0aW9ucy56b29tU25hcCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3ZpZXdyZXNldCcpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuX3N0b3AoKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAc2VjdGlvbiBHZW9sb2NhdGlvbiBtZXRob2RzXHJcblx0Ly8gQG1ldGhvZCBsb2NhdGUob3B0aW9ucz86IExvY2F0ZSBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFRyaWVzIHRvIGxvY2F0ZSB0aGUgdXNlciB1c2luZyB0aGUgR2VvbG9jYXRpb24gQVBJLCBmaXJpbmcgYSBbYGxvY2F0aW9uZm91bmRgXSgjbWFwLWxvY2F0aW9uZm91bmQpXHJcblx0Ly8gZXZlbnQgd2l0aCBsb2NhdGlvbiBkYXRhIG9uIHN1Y2Nlc3Mgb3IgYSBbYGxvY2F0aW9uZXJyb3JgXSgjbWFwLWxvY2F0aW9uZXJyb3IpIGV2ZW50IG9uIGZhaWx1cmUsXHJcblx0Ly8gYW5kIG9wdGlvbmFsbHkgc2V0cyB0aGUgbWFwIHZpZXcgdG8gdGhlIHVzZXIncyBsb2NhdGlvbiB3aXRoIHJlc3BlY3QgdG9cclxuXHQvLyBkZXRlY3Rpb24gYWNjdXJhY3kgKG9yIHRvIHRoZSB3b3JsZCB2aWV3IGlmIGdlb2xvY2F0aW9uIGZhaWxlZCkuXHJcblx0Ly8gTm90ZSB0aGF0LCBpZiB5b3VyIHBhZ2UgZG9lc24ndCB1c2UgSFRUUFMsIHRoaXMgbWV0aG9kIHdpbGwgZmFpbCBpblxyXG5cdC8vIG1vZGVybiBicm93c2VycyAoW0Nocm9tZSA1MCBhbmQgbmV3ZXJdKGh0dHBzOi8vc2l0ZXMuZ29vZ2xlLmNvbS9hL2Nocm9taXVtLm9yZy9kZXYvSG9tZS9jaHJvbWl1bS1zZWN1cml0eS9kZXByZWNhdGluZy1wb3dlcmZ1bC1mZWF0dXJlcy1vbi1pbnNlY3VyZS1vcmlnaW5zKSlcclxuXHQvLyBTZWUgYExvY2F0ZSBvcHRpb25zYCBmb3IgbW9yZSBkZXRhaWxzLlxyXG5cdGxvY2F0ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHJcblx0XHRvcHRpb25zID0gdGhpcy5fbG9jYXRlT3B0aW9ucyA9IEwuZXh0ZW5kKHtcclxuXHRcdFx0dGltZW91dDogMTAwMDAsXHJcblx0XHRcdHdhdGNoOiBmYWxzZVxyXG5cdFx0XHQvLyBzZXRWaWV3OiBmYWxzZVxyXG5cdFx0XHQvLyBtYXhab29tOiA8TnVtYmVyPlxyXG5cdFx0XHQvLyBtYXhpbXVtQWdlOiAwXHJcblx0XHRcdC8vIGVuYWJsZUhpZ2hBY2N1cmFjeTogZmFsc2VcclxuXHRcdH0sIG9wdGlvbnMpO1xyXG5cclxuXHRcdGlmICghKCdnZW9sb2NhdGlvbicgaW4gbmF2aWdhdG9yKSkge1xyXG5cdFx0XHR0aGlzLl9oYW5kbGVHZW9sb2NhdGlvbkVycm9yKHtcclxuXHRcdFx0XHRjb2RlOiAwLFxyXG5cdFx0XHRcdG1lc3NhZ2U6ICdHZW9sb2NhdGlvbiBub3Qgc3VwcG9ydGVkLidcclxuXHRcdFx0fSk7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBvblJlc3BvbnNlID0gTC5iaW5kKHRoaXMuX2hhbmRsZUdlb2xvY2F0aW9uUmVzcG9uc2UsIHRoaXMpLFxyXG5cdFx0ICAgIG9uRXJyb3IgPSBMLmJpbmQodGhpcy5faGFuZGxlR2VvbG9jYXRpb25FcnJvciwgdGhpcyk7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMud2F0Y2gpIHtcclxuXHRcdFx0dGhpcy5fbG9jYXRpb25XYXRjaElkID1cclxuXHRcdFx0ICAgICAgICBuYXZpZ2F0b3IuZ2VvbG9jYXRpb24ud2F0Y2hQb3NpdGlvbihvblJlc3BvbnNlLCBvbkVycm9yLCBvcHRpb25zKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG5hdmlnYXRvci5nZW9sb2NhdGlvbi5nZXRDdXJyZW50UG9zaXRpb24ob25SZXNwb25zZSwgb25FcnJvciwgb3B0aW9ucyk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHN0b3BMb2NhdGUoKTogdGhpc1xyXG5cdC8vIFN0b3BzIHdhdGNoaW5nIGxvY2F0aW9uIHByZXZpb3VzbHkgaW5pdGlhdGVkIGJ5IGBtYXAubG9jYXRlKHt3YXRjaDogdHJ1ZX0pYFxyXG5cdC8vIGFuZCBhYm9ydHMgcmVzZXR0aW5nIHRoZSBtYXAgdmlldyBpZiBtYXAubG9jYXRlIHdhcyBjYWxsZWQgd2l0aFxyXG5cdC8vIGB7c2V0VmlldzogdHJ1ZX1gLlxyXG5cdHN0b3BMb2NhdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmIChuYXZpZ2F0b3IuZ2VvbG9jYXRpb24gJiYgbmF2aWdhdG9yLmdlb2xvY2F0aW9uLmNsZWFyV2F0Y2gpIHtcclxuXHRcdFx0bmF2aWdhdG9yLmdlb2xvY2F0aW9uLmNsZWFyV2F0Y2godGhpcy5fbG9jYXRpb25XYXRjaElkKTtcclxuXHRcdH1cclxuXHRcdGlmICh0aGlzLl9sb2NhdGVPcHRpb25zKSB7XHJcblx0XHRcdHRoaXMuX2xvY2F0ZU9wdGlvbnMuc2V0VmlldyA9IGZhbHNlO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X2hhbmRsZUdlb2xvY2F0aW9uRXJyb3I6IGZ1bmN0aW9uIChlcnJvcikge1xyXG5cdFx0dmFyIGMgPSBlcnJvci5jb2RlLFxyXG5cdFx0ICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlIHx8XHJcblx0XHQgICAgICAgICAgICAoYyA9PT0gMSA/ICdwZXJtaXNzaW9uIGRlbmllZCcgOlxyXG5cdFx0ICAgICAgICAgICAgKGMgPT09IDIgPyAncG9zaXRpb24gdW5hdmFpbGFibGUnIDogJ3RpbWVvdXQnKSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xvY2F0ZU9wdGlvbnMuc2V0VmlldyAmJiAhdGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdHRoaXMuZml0V29ybGQoKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAc2VjdGlvbiBMb2NhdGlvbiBldmVudHNcclxuXHRcdC8vIEBldmVudCBsb2NhdGlvbmVycm9yOiBFcnJvckV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGdlb2xvY2F0aW9uICh1c2luZyB0aGUgW2Bsb2NhdGVgXSgjbWFwLWxvY2F0ZSkgbWV0aG9kKSBmYWlsZWQuXHJcblx0XHR0aGlzLmZpcmUoJ2xvY2F0aW9uZXJyb3InLCB7XHJcblx0XHRcdGNvZGU6IGMsXHJcblx0XHRcdG1lc3NhZ2U6ICdHZW9sb2NhdGlvbiBlcnJvcjogJyArIG1lc3NhZ2UgKyAnLidcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdF9oYW5kbGVHZW9sb2NhdGlvblJlc3BvbnNlOiBmdW5jdGlvbiAocG9zKSB7XHJcblx0XHR2YXIgbGF0ID0gcG9zLmNvb3Jkcy5sYXRpdHVkZSxcclxuXHRcdCAgICBsbmcgPSBwb3MuY29vcmRzLmxvbmdpdHVkZSxcclxuXHRcdCAgICBsYXRsbmcgPSBuZXcgTC5MYXRMbmcobGF0LCBsbmcpLFxyXG5cdFx0ICAgIGJvdW5kcyA9IGxhdGxuZy50b0JvdW5kcyhwb3MuY29vcmRzLmFjY3VyYWN5KSxcclxuXHRcdCAgICBvcHRpb25zID0gdGhpcy5fbG9jYXRlT3B0aW9ucztcclxuXHJcblx0XHRpZiAob3B0aW9ucy5zZXRWaWV3KSB7XHJcblx0XHRcdHZhciB6b29tID0gdGhpcy5nZXRCb3VuZHNab29tKGJvdW5kcyk7XHJcblx0XHRcdHRoaXMuc2V0VmlldyhsYXRsbmcsIG9wdGlvbnMubWF4Wm9vbSA/IE1hdGgubWluKHpvb20sIG9wdGlvbnMubWF4Wm9vbSkgOiB6b29tKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0bGF0bG5nOiBsYXRsbmcsXHJcblx0XHRcdGJvdW5kczogYm91bmRzLFxyXG5cdFx0XHR0aW1lc3RhbXA6IHBvcy50aW1lc3RhbXBcclxuXHRcdH07XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiBwb3MuY29vcmRzKSB7XHJcblx0XHRcdGlmICh0eXBlb2YgcG9zLmNvb3Jkc1tpXSA9PT0gJ251bWJlcicpIHtcclxuXHRcdFx0XHRkYXRhW2ldID0gcG9zLmNvb3Jkc1tpXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEBldmVudCBsb2NhdGlvbmZvdW5kOiBMb2NhdGlvbkV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGdlb2xvY2F0aW9uICh1c2luZyB0aGUgW2Bsb2NhdGVgXSgjbWFwLWxvY2F0ZSkgbWV0aG9kKVxyXG5cdFx0Ly8gd2VudCBzdWNjZXNzZnVsbHkuXHJcblx0XHR0aGlzLmZpcmUoJ2xvY2F0aW9uZm91bmQnLCBkYXRhKTtcclxuXHR9LFxyXG5cclxuXHQvLyBUT0RPIGhhbmRsZXIuYWRkVG9cclxuXHQvLyBUT0RPIEFwcHJvcGlhdGUgZG9jcyBzZWN0aW9uP1xyXG5cdC8vIEBzZWN0aW9uIE90aGVyIE1ldGhvZHNcclxuXHQvLyBAbWV0aG9kIGFkZEhhbmRsZXIobmFtZTogU3RyaW5nLCBIYW5kbGVyQ2xhc3M6IEZ1bmN0aW9uKTogdGhpc1xyXG5cdC8vIEFkZHMgYSBuZXcgYEhhbmRsZXJgIHRvIHRoZSBtYXAsIGdpdmVuIGl0cyBuYW1lIGFuZCBjb25zdHJ1Y3RvciBmdW5jdGlvbi5cclxuXHRhZGRIYW5kbGVyOiBmdW5jdGlvbiAobmFtZSwgSGFuZGxlckNsYXNzKSB7XHJcblx0XHRpZiAoIUhhbmRsZXJDbGFzcykgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdHZhciBoYW5kbGVyID0gdGhpc1tuYW1lXSA9IG5ldyBIYW5kbGVyQ2xhc3ModGhpcyk7XHJcblxyXG5cdFx0dGhpcy5faGFuZGxlcnMucHVzaChoYW5kbGVyKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zW25hbWVdKSB7XHJcblx0XHRcdGhhbmRsZXIuZW5hYmxlKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmUoKTogdGhpc1xyXG5cdC8vIERlc3Ryb3lzIHRoZSBtYXAgYW5kIGNsZWFycyBhbGwgcmVsYXRlZCBldmVudCBsaXN0ZW5lcnMuXHJcblx0cmVtb3ZlOiBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0dGhpcy5faW5pdEV2ZW50cyh0cnVlKTtcclxuXHJcblx0XHRpZiAodGhpcy5fY29udGFpbmVySWQgIT09IHRoaXMuX2NvbnRhaW5lci5fbGVhZmxldF9pZCkge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ01hcCBjb250YWluZXIgaXMgYmVpbmcgcmV1c2VkIGJ5IGFub3RoZXIgaW5zdGFuY2UnKTtcclxuXHRcdH1cclxuXHJcblx0XHR0cnkge1xyXG5cdFx0XHQvLyB0aHJvd3MgZXJyb3IgaW4gSUU2LThcclxuXHRcdFx0ZGVsZXRlIHRoaXMuX2NvbnRhaW5lci5fbGVhZmxldF9pZDtcclxuXHRcdFx0ZGVsZXRlIHRoaXMuX2NvbnRhaW5lcklkO1xyXG5cdFx0fSBjYXRjaCAoZSkge1xyXG5cdFx0XHQvKmVzbGludC1kaXNhYmxlICovXHJcblx0XHRcdHRoaXMuX2NvbnRhaW5lci5fbGVhZmxldF9pZCA9IHVuZGVmaW5lZDtcclxuXHRcdFx0Lyplc2xpbnQtZW5hYmxlICovXHJcblx0XHRcdHRoaXMuX2NvbnRhaW5lcklkID0gdW5kZWZpbmVkO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fbWFwUGFuZSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2NsZWFyQ29udHJvbFBvcykge1xyXG5cdFx0XHR0aGlzLl9jbGVhckNvbnRyb2xQb3MoKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9jbGVhckhhbmRsZXJzKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHQvLyBAc2VjdGlvbiBNYXAgc3RhdGUgY2hhbmdlIGV2ZW50c1xyXG5cdFx0XHQvLyBAZXZlbnQgdW5sb2FkOiBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaXMgZGVzdHJveWVkIHdpdGggW3JlbW92ZV0oI21hcC1yZW1vdmUpIG1ldGhvZC5cclxuXHRcdFx0dGhpcy5maXJlKCd1bmxvYWQnKTtcclxuXHRcdH1cclxuXHJcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHR0aGlzLl9sYXllcnNbaV0ucmVtb3ZlKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cdC8vIEBtZXRob2QgY3JlYXRlUGFuZShuYW1lOiBTdHJpbmcsIGNvbnRhaW5lcj86IEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBDcmVhdGVzIGEgbmV3IFttYXAgcGFuZV0oI21hcC1wYW5lKSB3aXRoIHRoZSBnaXZlbiBuYW1lIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeSxcclxuXHQvLyB0aGVuIHJldHVybnMgaXQuIFRoZSBwYW5lIGlzIGNyZWF0ZWQgYXMgYSBjaGlsZHJlbiBvZiBgY29udGFpbmVyYCwgb3JcclxuXHQvLyBhcyBhIGNoaWxkcmVuIG9mIHRoZSBtYWluIG1hcCBwYW5lIGlmIG5vdCBzZXQuXHJcblx0Y3JlYXRlUGFuZTogZnVuY3Rpb24gKG5hbWUsIGNvbnRhaW5lcikge1xyXG5cdFx0dmFyIGNsYXNzTmFtZSA9ICdsZWFmbGV0LXBhbmUnICsgKG5hbWUgPyAnIGxlYWZsZXQtJyArIG5hbWUucmVwbGFjZSgnUGFuZScsICcnKSArICctcGFuZScgOiAnJyksXHJcblx0XHQgICAgcGFuZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSwgY29udGFpbmVyIHx8IHRoaXMuX21hcFBhbmUpO1xyXG5cclxuXHRcdGlmIChuYW1lKSB7XHJcblx0XHRcdHRoaXMuX3BhbmVzW25hbWVdID0gcGFuZTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBwYW5lO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIE1ldGhvZHMgZm9yIEdldHRpbmcgTWFwIFN0YXRlXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIGdlb2dyYXBoaWNhbCBjZW50ZXIgb2YgdGhlIG1hcCB2aWV3XHJcblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9jaGVja0lmTG9hZGVkKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2xhc3RDZW50ZXIgJiYgIXRoaXMuX21vdmVkKCkpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXMuX2xhc3RDZW50ZXI7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9MYXRMbmcodGhpcy5fZ2V0Q2VudGVyTGF5ZXJQb2ludCgpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFpvb20oKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCB6b29tIGxldmVsIG9mIHRoZSBtYXAgdmlld1xyXG5cdGdldFpvb206IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl96b29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgdGhlIGdlb2dyYXBoaWNhbCBib3VuZHMgdmlzaWJsZSBpbiB0aGUgY3VycmVudCBtYXAgdmlld1xyXG5cdGdldEJvdW5kczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGJvdW5kcyA9IHRoaXMuZ2V0UGl4ZWxCb3VuZHMoKSxcclxuXHRcdCAgICBzdyA9IHRoaXMudW5wcm9qZWN0KGJvdW5kcy5nZXRCb3R0b21MZWZ0KCkpLFxyXG5cdFx0ICAgIG5lID0gdGhpcy51bnByb2plY3QoYm91bmRzLmdldFRvcFJpZ2h0KCkpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmdCb3VuZHMoc3csIG5lKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldE1pblpvb20oKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbWluaW11bSB6b29tIGxldmVsIG9mIHRoZSBtYXAgKGlmIHNldCBpbiB0aGUgYG1pblpvb21gIG9wdGlvbiBvZiB0aGUgbWFwIG9yIG9mIGFueSBsYXllcnMpLCBvciBgMGAgYnkgZGVmYXVsdC5cclxuXHRnZXRNaW5ab29tOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLm1pblpvb20gPT09IHVuZGVmaW5lZCA/IHRoaXMuX2xheWVyc01pblpvb20gfHwgMCA6IHRoaXMub3B0aW9ucy5taW5ab29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TWF4Wm9vbSgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBtYXhpbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcCAoaWYgc2V0IGluIHRoZSBgbWF4Wm9vbWAgb3B0aW9uIG9mIHRoZSBtYXAgb3Igb2YgYW55IGxheWVycykuXHJcblx0Z2V0TWF4Wm9vbTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5tYXhab29tID09PSB1bmRlZmluZWQgP1xyXG5cdFx0XHQodGhpcy5fbGF5ZXJzTWF4Wm9vbSA9PT0gdW5kZWZpbmVkID8gSW5maW5pdHkgOiB0aGlzLl9sYXllcnNNYXhab29tKSA6XHJcblx0XHRcdHRoaXMub3B0aW9ucy5tYXhab29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzWm9vbShib3VuZHM6IExhdExuZ0JvdW5kcywgaW5zaWRlPzogQm9vbGVhbik6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIG1heGltdW0gem9vbSBsZXZlbCBvbiB3aGljaCB0aGUgZ2l2ZW4gYm91bmRzIGZpdCB0byB0aGUgbWFwXHJcblx0Ly8gdmlldyBpbiBpdHMgZW50aXJldHkuIElmIGBpbnNpZGVgIChvcHRpb25hbCkgaXMgc2V0IHRvIGB0cnVlYCwgdGhlIG1ldGhvZFxyXG5cdC8vIGluc3RlYWQgcmV0dXJucyB0aGUgbWluaW11bSB6b29tIGxldmVsIG9uIHdoaWNoIHRoZSBtYXAgdmlldyBmaXRzIGludG9cclxuXHQvLyB0aGUgZ2l2ZW4gYm91bmRzIGluIGl0cyBlbnRpcmV0eS5cclxuXHRnZXRCb3VuZHNab29tOiBmdW5jdGlvbiAoYm91bmRzLCBpbnNpZGUsIHBhZGRpbmcpIHsgLy8gKExhdExuZ0JvdW5kc1ssIEJvb2xlYW4sIFBvaW50XSkgLT4gTnVtYmVyXHJcblx0XHRib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cdFx0cGFkZGluZyA9IEwucG9pbnQocGFkZGluZyB8fCBbMCwgMF0pO1xyXG5cclxuXHRcdHZhciB6b29tID0gdGhpcy5nZXRab29tKCkgfHwgMCxcclxuXHRcdCAgICBtaW4gPSB0aGlzLmdldE1pblpvb20oKSxcclxuXHRcdCAgICBtYXggPSB0aGlzLmdldE1heFpvb20oKSxcclxuXHRcdCAgICBudyA9IGJvdW5kcy5nZXROb3J0aFdlc3QoKSxcclxuXHRcdCAgICBzZSA9IGJvdW5kcy5nZXRTb3V0aEVhc3QoKSxcclxuXHRcdCAgICBzaXplID0gdGhpcy5nZXRTaXplKCkuc3VidHJhY3QocGFkZGluZyksXHJcblx0XHQgICAgYm91bmRzU2l6ZSA9IHRoaXMucHJvamVjdChzZSwgem9vbSkuc3VidHJhY3QodGhpcy5wcm9qZWN0KG53LCB6b29tKSksXHJcblx0XHQgICAgc25hcCA9IEwuQnJvd3Nlci5hbnkzZCA/IHRoaXMub3B0aW9ucy56b29tU25hcCA6IDE7XHJcblxyXG5cdFx0dmFyIHNjYWxlID0gTWF0aC5taW4oc2l6ZS54IC8gYm91bmRzU2l6ZS54LCBzaXplLnkgLyBib3VuZHNTaXplLnkpO1xyXG5cdFx0em9vbSA9IHRoaXMuZ2V0U2NhbGVab29tKHNjYWxlLCB6b29tKTtcclxuXHJcblx0XHRpZiAoc25hcCkge1xyXG5cdFx0XHR6b29tID0gTWF0aC5yb3VuZCh6b29tIC8gKHNuYXAgLyAxMDApKSAqIChzbmFwIC8gMTAwKTsgLy8gZG9uJ3QganVtcCBpZiB3aXRoaW4gMSUgb2YgYSBzbmFwIGxldmVsXHJcblx0XHRcdHpvb20gPSBpbnNpZGUgPyBNYXRoLmNlaWwoem9vbSAvIHNuYXApICogc25hcCA6IE1hdGguZmxvb3Ioem9vbSAvIHNuYXApICogc25hcDtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gTWF0aC5tYXgobWluLCBNYXRoLm1pbihtYXgsIHpvb20pKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNpemUoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBjdXJyZW50IHNpemUgb2YgdGhlIG1hcCBjb250YWluZXIgKGluIHBpeGVscykuXHJcblx0Z2V0U2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9zaXplIHx8IHRoaXMuX3NpemVDaGFuZ2VkKSB7XHJcblx0XHRcdHRoaXMuX3NpemUgPSBuZXcgTC5Qb2ludChcclxuXHRcdFx0XHR0aGlzLl9jb250YWluZXIuY2xpZW50V2lkdGgsXHJcblx0XHRcdFx0dGhpcy5fY29udGFpbmVyLmNsaWVudEhlaWdodCk7XHJcblxyXG5cdFx0XHR0aGlzLl9zaXplQ2hhbmdlZCA9IGZhbHNlO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuX3NpemUuY2xvbmUoKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFBpeGVsQm91bmRzKCk6IEJvdW5kc1xyXG5cdC8vIFJldHVybnMgdGhlIGJvdW5kcyBvZiB0aGUgY3VycmVudCBtYXAgdmlldyBpbiBwcm9qZWN0ZWQgcGl4ZWxcclxuXHQvLyBjb29yZGluYXRlcyAoc29tZXRpbWVzIHVzZWZ1bCBpbiBsYXllciBhbmQgb3ZlcmxheSBpbXBsZW1lbnRhdGlvbnMpLlxyXG5cdGdldFBpeGVsQm91bmRzOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHR2YXIgdG9wTGVmdFBvaW50ID0gdGhpcy5fZ2V0VG9wTGVmdFBvaW50KGNlbnRlciwgem9vbSk7XHJcblx0XHRyZXR1cm4gbmV3IEwuQm91bmRzKHRvcExlZnRQb2ludCwgdG9wTGVmdFBvaW50LmFkZCh0aGlzLmdldFNpemUoKSkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIFRPRE86IENoZWNrIHNlbWFudGljcyAtIGlzbid0IHRoZSBwaXhlbCBvcmlnaW4gdGhlIDAsMCBjb29yZCByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBtYXAgcGFuZT8gXCJsZWZ0IHBvaW50IG9mIHRoZSBtYXAgbGF5ZXJcIiBjYW4gYmUgY29uZnVzaW5nLCBzcGVjaWFsbHlcclxuXHQvLyBzaW5jZSB0aGVyZSBjYW4gYmUgbmVnYXRpdmUgb2Zmc2V0cy5cclxuXHQvLyBAbWV0aG9kIGdldFBpeGVsT3JpZ2luKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcHJvamVjdGVkIHBpeGVsIGNvb3JkaW5hdGVzIG9mIHRoZSB0b3AgbGVmdCBwb2ludCBvZlxyXG5cdC8vIHRoZSBtYXAgbGF5ZXIgKHVzZWZ1bCBpbiBjdXN0b20gbGF5ZXIgYW5kIG92ZXJsYXkgaW1wbGVtZW50YXRpb25zKS5cclxuXHRnZXRQaXhlbE9yaWdpbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fY2hlY2tJZkxvYWRlZCgpO1xyXG5cdFx0cmV0dXJuIHRoaXMuX3BpeGVsT3JpZ2luO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UGl4ZWxXb3JsZEJvdW5kcyh6b29tPzogTnVtYmVyKTogQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgd29ybGQncyBib3VuZHMgaW4gcGl4ZWwgY29vcmRpbmF0ZXMgZm9yIHpvb20gbGV2ZWwgYHpvb21gLlxyXG5cdC8vIElmIGB6b29tYCBpcyBvbWl0dGVkLCB0aGUgbWFwJ3MgY3VycmVudCB6b29tIGxldmVsIGlzIHVzZWQuXHJcblx0Z2V0UGl4ZWxXb3JsZEJvdW5kczogZnVuY3Rpb24gKHpvb20pIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLmdldFByb2plY3RlZEJvdW5kcyh6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLmdldFpvb20oKSA6IHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIE90aGVyIE1ldGhvZHNcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRQYW5lKHBhbmU6IFN0cmluZ3xIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gUmV0dXJucyBhIFttYXAgcGFuZV0oI21hcC1wYW5lKSwgZ2l2ZW4gaXRzIG5hbWUgb3IgaXRzIEhUTUwgZWxlbWVudCAoaXRzIGlkZW50aXR5KS5cclxuXHRnZXRQYW5lOiBmdW5jdGlvbiAocGFuZSkge1xyXG5cdFx0cmV0dXJuIHR5cGVvZiBwYW5lID09PSAnc3RyaW5nJyA/IHRoaXMuX3BhbmVzW3BhbmVdIDogcGFuZTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFBhbmVzKCk6IE9iamVjdFxyXG5cdC8vIFJldHVybnMgYSBwbGFpbiBvYmplY3QgY29udGFpbmluZyB0aGUgbmFtZXMgb2YgYWxsIFtwYW5lc10oI21hcC1wYW5lKSBhcyBrZXlzIGFuZFxyXG5cdC8vIHRoZSBwYW5lcyBhcyB2YWx1ZXMuXHJcblx0Z2V0UGFuZXM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9wYW5lcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENvbnRhaW5lcjogSFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBIVE1MIGVsZW1lbnQgdGhhdCBjb250YWlucyB0aGUgbWFwLlxyXG5cdGdldENvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHJcblx0Ly8gQHNlY3Rpb24gQ29udmVyc2lvbiBNZXRob2RzXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Wm9vbVNjYWxlKHRvWm9vbTogTnVtYmVyLCBmcm9tWm9vbTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgc2NhbGUgZmFjdG9yIHRvIGJlIGFwcGxpZWQgdG8gYSBtYXAgdHJhbnNpdGlvbiBmcm9tIHpvb20gbGV2ZWxcclxuXHQvLyBgZnJvbVpvb21gIHRvIGB0b1pvb21gLiBVc2VkIGludGVybmFsbHkgdG8gaGVscCB3aXRoIHpvb20gYW5pbWF0aW9ucy5cclxuXHRnZXRab29tU2NhbGU6IGZ1bmN0aW9uICh0b1pvb20sIGZyb21ab29tKSB7XHJcblx0XHQvLyBUT0RPIHJlcGxhY2Ugd2l0aCB1bml2ZXJzYWwgaW1wbGVtZW50YXRpb24gYWZ0ZXIgcmVmYWN0b3JpbmcgcHJvamVjdGlvbnNcclxuXHRcdHZhciBjcnMgPSB0aGlzLm9wdGlvbnMuY3JzO1xyXG5cdFx0ZnJvbVpvb20gPSBmcm9tWm9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fem9vbSA6IGZyb21ab29tO1xyXG5cdFx0cmV0dXJuIGNycy5zY2FsZSh0b1pvb20pIC8gY3JzLnNjYWxlKGZyb21ab29tKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFNjYWxlWm9vbShzY2FsZTogTnVtYmVyLCBmcm9tWm9vbTogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgem9vbSBsZXZlbCB0aGF0IHRoZSBtYXAgd291bGQgZW5kIHVwIGF0LCBpZiBpdCBpcyBhdCBgZnJvbVpvb21gXHJcblx0Ly8gbGV2ZWwgYW5kIGV2ZXJ5dGhpbmcgaXMgc2NhbGVkIGJ5IGEgZmFjdG9yIG9mIGBzY2FsZWAuIEludmVyc2Ugb2ZcclxuXHQvLyBbYGdldFpvb21TY2FsZWBdKCNtYXAtZ2V0Wm9vbVNjYWxlKS5cclxuXHRnZXRTY2FsZVpvb206IGZ1bmN0aW9uIChzY2FsZSwgZnJvbVpvb20pIHtcclxuXHRcdHZhciBjcnMgPSB0aGlzLm9wdGlvbnMuY3JzO1xyXG5cdFx0ZnJvbVpvb20gPSBmcm9tWm9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fem9vbSA6IGZyb21ab29tO1xyXG5cdFx0dmFyIHpvb20gPSBjcnMuem9vbShzY2FsZSAqIGNycy5zY2FsZShmcm9tWm9vbSkpO1xyXG5cdFx0cmV0dXJuIGlzTmFOKHpvb20pID8gSW5maW5pdHkgOiB6b29tO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcHJvamVjdChsYXRsbmc6IExhdExuZywgem9vbTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBQcm9qZWN0cyBhIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIGBMYXRMbmdgIGFjY29yZGluZyB0byB0aGUgcHJvamVjdGlvblxyXG5cdC8vIG9mIHRoZSBtYXAncyBDUlMsIHRoZW4gc2NhbGVzIGl0IGFjY29yZGluZyB0byBgem9vbWAgYW5kIHRoZSBDUlMnc1xyXG5cdC8vIGBUcmFuc2Zvcm1hdGlvbmAuIFRoZSByZXN1bHQgaXMgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBDUlMgb3JpZ2luLlxyXG5cdHByb2plY3Q6IGZ1bmN0aW9uIChsYXRsbmcsIHpvb20pIHtcclxuXHRcdHpvb20gPSB6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogem9vbTtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLmxhdExuZ1RvUG9pbnQoTC5sYXRMbmcobGF0bG5nKSwgem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB1bnByb2plY3QocG9pbnQ6IFBvaW50LCB6b29tOiBOdW1iZXIpOiBMYXRMbmdcclxuXHQvLyBJbnZlcnNlIG9mIFtgcHJvamVjdGBdKCNtYXAtcHJvamVjdCkuXHJcblx0dW5wcm9qZWN0OiBmdW5jdGlvbiAocG9pbnQsIHpvb20pIHtcclxuXHRcdHpvb20gPSB6b29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogem9vbTtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLnBvaW50VG9MYXRMbmcoTC5wb2ludChwb2ludCksIHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbGF5ZXJQb2ludFRvTGF0TG5nKHBvaW50OiBQb2ludCk6IExhdExuZ1xyXG5cdC8vIEdpdmVuIGEgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGUgW29yaWdpbiBwaXhlbF0oI21hcC1nZXRwaXhlbG9yaWdpbiksXHJcblx0Ly8gcmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZSAoZm9yIHRoZSBjdXJyZW50IHpvb20gbGV2ZWwpLlxyXG5cdGxheWVyUG9pbnRUb0xhdExuZzogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHR2YXIgcHJvamVjdGVkUG9pbnQgPSBMLnBvaW50KHBvaW50KS5hZGQodGhpcy5nZXRQaXhlbE9yaWdpbigpKTtcclxuXHRcdHJldHVybiB0aGlzLnVucHJvamVjdChwcm9qZWN0ZWRQb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsYXRMbmdUb0xheWVyUG9pbnQobGF0bG5nOiBMYXRMbmcpOiBQb2ludFxyXG5cdC8vIEdpdmVuIGEgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUsIHJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmcgcGl4ZWwgY29vcmRpbmF0ZVxyXG5cdC8vIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKS5cclxuXHRsYXRMbmdUb0xheWVyUG9pbnQ6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHZhciBwcm9qZWN0ZWRQb2ludCA9IHRoaXMucHJvamVjdChMLmxhdExuZyhsYXRsbmcpKS5fcm91bmQoKTtcclxuXHRcdHJldHVybiBwcm9qZWN0ZWRQb2ludC5fc3VidHJhY3QodGhpcy5nZXRQaXhlbE9yaWdpbigpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXBMYXRMbmcobGF0bG5nOiBMYXRMbmcpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIGEgYExhdExuZ2Agd2hlcmUgYGxhdGAgYW5kIGBsbmdgIGhhcyBiZWVuIHdyYXBwZWQgYWNjb3JkaW5nIHRvIHRoZVxyXG5cdC8vIG1hcCdzIENSUydzIGB3cmFwTGF0YCBhbmQgYHdyYXBMbmdgIHByb3BlcnRpZXMsIGlmIHRoZXkgYXJlIG91dHNpZGUgdGhlXHJcblx0Ly8gQ1JTJ3MgYm91bmRzLlxyXG5cdC8vIEJ5IGRlZmF1bHQgdGhpcyBtZWFucyBsb25naXR1ZGUgaXMgd3JhcHBlZCBhcm91bmQgdGhlIGRhdGVsaW5lIHNvIGl0c1xyXG5cdC8vIHZhbHVlIGlzIGJldHdlZW4gLTE4MCBhbmQgKzE4MCBkZWdyZWVzLlxyXG5cdHdyYXBMYXRMbmc6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuY3JzLndyYXBMYXRMbmcoTC5sYXRMbmcobGF0bG5nKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXN0YW5jZShsYXRsbmcxOiBMYXRMbmcsIGxhdGxuZzI6IExhdExuZyk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGRpc3RhbmNlIGJldHdlZW4gdHdvIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlcyBhY2NvcmRpbmcgdG9cclxuXHQvLyB0aGUgbWFwJ3MgQ1JTLiBCeSBkZWZhdWx0IHRoaXMgbWVhc3VyZXMgZGlzdGFuY2UgaW4gbWV0ZXJzLlxyXG5cdGRpc3RhbmNlOiBmdW5jdGlvbiAobGF0bG5nMSwgbGF0bG5nMikge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMuZGlzdGFuY2UoTC5sYXRMbmcobGF0bG5nMSksIEwubGF0TG5nKGxhdGxuZzIpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KHBvaW50OiBQb2ludCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBtYXAgY29udGFpbmVyLCByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nXHJcblx0Ly8gcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGUgW29yaWdpbiBwaXhlbF0oI21hcC1nZXRwaXhlbG9yaWdpbikuXHJcblx0Y29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQ6IGZ1bmN0aW9uIChwb2ludCkgeyAvLyAoUG9pbnQpXHJcblx0XHRyZXR1cm4gTC5wb2ludChwb2ludCkuc3VidHJhY3QodGhpcy5fZ2V0TWFwUGFuZVBvcygpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGxheWVyUG9pbnRUb0NvbnRhaW5lclBvaW50KHBvaW50OiBQb2ludCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKSxcclxuXHQvLyByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIHBpeGVsIGNvb3JkaW5hdGUgcmVsYXRpdmUgdG8gdGhlIG1hcCBjb250YWluZXIuXHJcblx0bGF5ZXJQb2ludFRvQ29udGFpbmVyUG9pbnQ6IGZ1bmN0aW9uIChwb2ludCkgeyAvLyAoUG9pbnQpXHJcblx0XHRyZXR1cm4gTC5wb2ludChwb2ludCkuYWRkKHRoaXMuX2dldE1hcFBhbmVQb3MoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjb250YWluZXJQb2ludFRvTGF0TG5nKHBvaW50OiBQb2ludCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBtYXAgY29udGFpbmVyLCByZXR1cm5zXHJcblx0Ly8gdGhlIGNvcnJlc3BvbmRpbmcgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUgKGZvciB0aGUgY3VycmVudCB6b29tIGxldmVsKS5cclxuXHRjb250YWluZXJQb2ludFRvTGF0TG5nOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHZhciBsYXllclBvaW50ID0gdGhpcy5jb250YWluZXJQb2ludFRvTGF5ZXJQb2ludChMLnBvaW50KHBvaW50KSk7XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9MYXRMbmcobGF5ZXJQb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsYXRMbmdUb0NvbnRhaW5lclBvaW50KGxhdGxuZzogTGF0TG5nKTogUG9pbnRcclxuXHQvLyBHaXZlbiBhIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlLCByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIHBpeGVsIGNvb3JkaW5hdGVcclxuXHQvLyByZWxhdGl2ZSB0byB0aGUgbWFwIGNvbnRhaW5lci5cclxuXHRsYXRMbmdUb0NvbnRhaW5lclBvaW50OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9Db250YWluZXJQb2ludCh0aGlzLmxhdExuZ1RvTGF5ZXJQb2ludChMLmxhdExuZyhsYXRsbmcpKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChldjogTW91c2VFdmVudCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBNb3VzZUV2ZW50IG9iamVjdCwgcmV0dXJucyB0aGUgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGVcclxuXHQvLyBtYXAgY29udGFpbmVyIHdoZXJlIHRoZSBldmVudCB0b29rIHBsYWNlLlxyXG5cdG1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnQuZ2V0TW91c2VQb3NpdGlvbihlLCB0aGlzLl9jb250YWluZXIpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbW91c2VFdmVudFRvTGF5ZXJQb2ludChldjogTW91c2VFdmVudCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBNb3VzZUV2ZW50IG9iamVjdCwgcmV0dXJucyB0aGUgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKSB3aGVyZSB0aGUgZXZlbnQgdG9vayBwbGFjZS5cclxuXHRtb3VzZUV2ZW50VG9MYXllclBvaW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQodGhpcy5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtb3VzZUV2ZW50VG9MYXRMbmcoZXY6IE1vdXNlRXZlbnQpOiBMYXRMbmdcclxuXHQvLyBHaXZlbiBhIE1vdXNlRXZlbnQgb2JqZWN0LCByZXR1cm5zIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIHdoZXJlIHRoZVxyXG5cdC8vIGV2ZW50IHRvb2sgcGxhY2UuXHJcblx0bW91c2VFdmVudFRvTGF0TG5nOiBmdW5jdGlvbiAoZSkgeyAvLyAoTW91c2VFdmVudClcclxuXHRcdHJldHVybiB0aGlzLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLm1vdXNlRXZlbnRUb0xheWVyUG9pbnQoZSkpO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBtYXAgaW5pdGlhbGl6YXRpb24gbWV0aG9kc1xyXG5cclxuXHRfaW5pdENvbnRhaW5lcjogZnVuY3Rpb24gKGlkKSB7XHJcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmdldChpZCk7XHJcblxyXG5cdFx0aWYgKCFjb250YWluZXIpIHtcclxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdNYXAgY29udGFpbmVyIG5vdCBmb3VuZC4nKTtcclxuXHRcdH0gZWxzZSBpZiAoY29udGFpbmVyLl9sZWFmbGV0X2lkKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignTWFwIGNvbnRhaW5lciBpcyBhbHJlYWR5IGluaXRpYWxpemVkLicpO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIoY29udGFpbmVyLCAnc2Nyb2xsJywgdGhpcy5fb25TY3JvbGwsIHRoaXMpO1xyXG5cdFx0dGhpcy5fY29udGFpbmVySWQgPSBMLlV0aWwuc3RhbXAoY29udGFpbmVyKTtcclxuXHR9LFxyXG5cclxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcjtcclxuXHJcblx0XHR0aGlzLl9mYWRlQW5pbWF0ZWQgPSB0aGlzLm9wdGlvbnMuZmFkZUFuaW1hdGlvbiAmJiBMLkJyb3dzZXIuYW55M2Q7XHJcblxyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtY29udGFpbmVyJyArXHJcblx0XHRcdChMLkJyb3dzZXIudG91Y2ggPyAnIGxlYWZsZXQtdG91Y2gnIDogJycpICtcclxuXHRcdFx0KEwuQnJvd3Nlci5yZXRpbmEgPyAnIGxlYWZsZXQtcmV0aW5hJyA6ICcnKSArXHJcblx0XHRcdChMLkJyb3dzZXIuaWVsdDkgPyAnIGxlYWZsZXQtb2xkaWUnIDogJycpICtcclxuXHRcdFx0KEwuQnJvd3Nlci5zYWZhcmkgPyAnIGxlYWZsZXQtc2FmYXJpJyA6ICcnKSArXHJcblx0XHRcdCh0aGlzLl9mYWRlQW5pbWF0ZWQgPyAnIGxlYWZsZXQtZmFkZS1hbmltJyA6ICcnKSk7XHJcblxyXG5cdFx0dmFyIHBvc2l0aW9uID0gTC5Eb21VdGlsLmdldFN0eWxlKGNvbnRhaW5lciwgJ3Bvc2l0aW9uJyk7XHJcblxyXG5cdFx0aWYgKHBvc2l0aW9uICE9PSAnYWJzb2x1dGUnICYmIHBvc2l0aW9uICE9PSAncmVsYXRpdmUnICYmIHBvc2l0aW9uICE9PSAnZml4ZWQnKSB7XHJcblx0XHRcdGNvbnRhaW5lci5zdHlsZS5wb3NpdGlvbiA9ICdyZWxhdGl2ZSc7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdFBhbmVzKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2luaXRDb250cm9sUG9zKSB7XHJcblx0XHRcdHRoaXMuX2luaXRDb250cm9sUG9zKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2luaXRQYW5lczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBhbmVzID0gdGhpcy5fcGFuZXMgPSB7fTtcclxuXHRcdHRoaXMuX3BhbmVSZW5kZXJlcnMgPSB7fTtcclxuXHJcblx0XHQvLyBAc2VjdGlvblxyXG5cdFx0Ly9cclxuXHRcdC8vIFBhbmVzIGFyZSBET00gZWxlbWVudHMgdXNlZCB0byBjb250cm9sIHRoZSBvcmRlcmluZyBvZiBsYXllcnMgb24gdGhlIG1hcC4gWW91XHJcblx0XHQvLyBjYW4gYWNjZXNzIHBhbmVzIHdpdGggW2BtYXAuZ2V0UGFuZWBdKCNtYXAtZ2V0cGFuZSkgb3JcclxuXHRcdC8vIFtgbWFwLmdldFBhbmVzYF0oI21hcC1nZXRwYW5lcykgbWV0aG9kcy4gTmV3IHBhbmVzIGNhbiBiZSBjcmVhdGVkIHdpdGggdGhlXHJcblx0XHQvLyBbYG1hcC5jcmVhdGVQYW5lYF0oI21hcC1jcmVhdGVwYW5lKSBtZXRob2QuXHJcblx0XHQvL1xyXG5cdFx0Ly8gRXZlcnkgbWFwIGhhcyB0aGUgZm9sbG93aW5nIGRlZmF1bHQgcGFuZXMgdGhhdCBkaWZmZXIgb25seSBpbiB6SW5kZXguXHJcblx0XHQvL1xyXG5cdFx0Ly8gQHBhbmUgbWFwUGFuZTogSFRNTEVsZW1lbnQgPSAnYXV0bydcclxuXHRcdC8vIFBhbmUgdGhhdCBjb250YWlucyBhbGwgb3RoZXIgbWFwIHBhbmVzXHJcblxyXG5cdFx0dGhpcy5fbWFwUGFuZSA9IHRoaXMuY3JlYXRlUGFuZSgnbWFwUGFuZScsIHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSwgbmV3IEwuUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdC8vIEBwYW5lIHRpbGVQYW5lOiBIVE1MRWxlbWVudCA9IDIwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYEdyaWRMYXllcmBzIGFuZCBgVGlsZUxheWVyYHNcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgndGlsZVBhbmUnKTtcclxuXHRcdC8vIEBwYW5lIG92ZXJsYXlQYW5lOiBIVE1MRWxlbWVudCA9IDQwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgdmVjdG9yIG92ZXJsYXlzIChgUGF0aGBzKSwgbGlrZSBgUG9seWxpbmVgcyBhbmQgYFBvbHlnb25gc1xyXG5cdFx0dGhpcy5jcmVhdGVQYW5lKCdzaGFkb3dQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSBzaGFkb3dQYW5lOiBIVE1MRWxlbWVudCA9IDUwMFxyXG5cdFx0Ly8gUGFuZSBmb3Igb3ZlcmxheSBzaGFkb3dzIChlLmcuIGBNYXJrZXJgIHNoYWRvd3MpXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ292ZXJsYXlQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSBtYXJrZXJQYW5lOiBIVE1MRWxlbWVudCA9IDYwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYEljb25gcyBvZiBgTWFya2VyYHNcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgnbWFya2VyUGFuZScpO1xyXG5cdFx0Ly8gQHBhbmUgdG9vbHRpcFBhbmU6IEhUTUxFbGVtZW50ID0gNjUwXHJcblx0XHQvLyBQYW5lIGZvciB0b29sdGlwLlxyXG5cdFx0dGhpcy5jcmVhdGVQYW5lKCd0b29sdGlwUGFuZScpO1xyXG5cdFx0Ly8gQHBhbmUgcG9wdXBQYW5lOiBIVE1MRWxlbWVudCA9IDcwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYFBvcHVwYHMuXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ3BvcHVwUGFuZScpO1xyXG5cclxuXHRcdGlmICghdGhpcy5vcHRpb25zLm1hcmtlclpvb21BbmltYXRpb24pIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHBhbmVzLm1hcmtlclBhbmUsICdsZWFmbGV0LXpvb20taGlkZScpO1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MocGFuZXMuc2hhZG93UGFuZSwgJ2xlYWZsZXQtem9vbS1oaWRlJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIHByaXZhdGUgbWV0aG9kcyB0aGF0IG1vZGlmeSBtYXAgc3RhdGVcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWFwIHN0YXRlIGNoYW5nZSBldmVudHNcclxuXHRfcmVzZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSwgbmV3IEwuUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdHZhciBsb2FkaW5nID0gIXRoaXMuX2xvYWRlZDtcclxuXHRcdHRoaXMuX2xvYWRlZCA9IHRydWU7XHJcblx0XHR6b29tID0gdGhpcy5fbGltaXRab29tKHpvb20pO1xyXG5cclxuXHRcdHRoaXMuZmlyZSgndmlld3ByZXJlc2V0Jyk7XHJcblxyXG5cdFx0dmFyIHpvb21DaGFuZ2VkID0gdGhpcy5fem9vbSAhPT0gem9vbTtcclxuXHRcdHRoaXNcclxuXHRcdFx0Ll9tb3ZlU3RhcnQoem9vbUNoYW5nZWQpXHJcblx0XHRcdC5fbW92ZShjZW50ZXIsIHpvb20pXHJcblx0XHRcdC5fbW92ZUVuZCh6b29tQ2hhbmdlZCk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IHZpZXdyZXNldDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcCBuZWVkcyB0byByZWRyYXcgaXRzIGNvbnRlbnQgKHRoaXMgdXN1YWxseSBoYXBwZW5zXHJcblx0XHQvLyBvbiBtYXAgem9vbSBvciBsb2FkKS4gVmVyeSB1c2VmdWwgZm9yIGNyZWF0aW5nIGN1c3RvbSBvdmVybGF5cy5cclxuXHRcdHRoaXMuZmlyZSgndmlld3Jlc2V0Jyk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxvYWQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaXMgaW5pdGlhbGl6ZWQgKHdoZW4gaXRzIGNlbnRlciBhbmQgem9vbSBhcmUgc2V0XHJcblx0XHQvLyBmb3IgdGhlIGZpcnN0IHRpbWUpLlxyXG5cdFx0aWYgKGxvYWRpbmcpIHtcclxuXHRcdFx0dGhpcy5maXJlKCdsb2FkJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X21vdmVTdGFydDogZnVuY3Rpb24gKHpvb21DaGFuZ2VkKSB7XHJcblx0XHQvLyBAZXZlbnQgem9vbXN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIHpvb20gaXMgYWJvdXQgdG8gY2hhbmdlIChlLmcuIGJlZm9yZSB6b29tIGFuaW1hdGlvbikuXHJcblx0XHQvLyBAZXZlbnQgbW92ZXN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdmlldyBvZiB0aGUgbWFwIHN0YXJ0cyBjaGFuZ2luZyAoZS5nLiB1c2VyIHN0YXJ0cyBkcmFnZ2luZyB0aGUgbWFwKS5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb21zdGFydCcpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZXN0YXJ0Jyk7XHJcblx0fSxcclxuXHJcblx0X21vdmU6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIGRhdGEpIHtcclxuXHRcdGlmICh6b29tID09PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0em9vbSA9IHRoaXMuX3pvb207XHJcblx0XHR9XHJcblx0XHR2YXIgem9vbUNoYW5nZWQgPSB0aGlzLl96b29tICE9PSB6b29tO1xyXG5cclxuXHRcdHRoaXMuX3pvb20gPSB6b29tO1xyXG5cdFx0dGhpcy5fbGFzdENlbnRlciA9IGNlbnRlcjtcclxuXHRcdHRoaXMuX3BpeGVsT3JpZ2luID0gdGhpcy5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyKTtcclxuXHJcblx0XHQvLyBAZXZlbnQgem9vbTogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHJlcGVhdGVkbHkgZHVyaW5nIGFueSBjaGFuZ2UgaW4gem9vbSBsZXZlbCwgaW5jbHVkaW5nIHpvb21cclxuXHRcdC8vIGFuZCBmbHkgYW5pbWF0aW9ucy5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCB8fCAoZGF0YSAmJiBkYXRhLnBpbmNoKSkge1x0Ly8gQWx3YXlzIGZpcmUgJ3pvb20nIGlmIHBpbmNoaW5nIGJlY2F1c2UgIzM1MzBcclxuXHRcdFx0dGhpcy5maXJlKCd6b29tJywgZGF0YSk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQGV2ZW50IG1vdmU6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCByZXBlYXRlZGx5IGR1cmluZyBhbnkgbW92ZW1lbnQgb2YgdGhlIG1hcCwgaW5jbHVkaW5nIHBhbiBhbmRcclxuXHRcdC8vIGZseSBhbmltYXRpb25zLlxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZScsIGRhdGEpO1xyXG5cdH0sXHJcblxyXG5cdF9tb3ZlRW5kOiBmdW5jdGlvbiAoem9vbUNoYW5nZWQpIHtcclxuXHRcdC8vIEBldmVudCB6b29tZW5kOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIGhhcyBjaGFuZ2VkLCBhZnRlciBhbnkgYW5pbWF0aW9ucy5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb21lbmQnKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgbW92ZWVuZDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIGNlbnRlciBvZiB0aGUgbWFwIHN0b3BzIGNoYW5naW5nIChlLmcuIHVzZXIgc3RvcHBlZFxyXG5cdFx0Ly8gZHJhZ2dpbmcgdGhlIG1hcCkuXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0fSxcclxuXHJcblx0X3N0b3A6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fZmx5VG9GcmFtZSk7XHJcblx0XHRpZiAodGhpcy5fcGFuQW5pbSkge1xyXG5cdFx0XHR0aGlzLl9wYW5BbmltLnN0b3AoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9yYXdQYW5CeTogZnVuY3Rpb24gKG9mZnNldCkge1xyXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX21hcFBhbmUsIHRoaXMuX2dldE1hcFBhbmVQb3MoKS5zdWJ0cmFjdChvZmZzZXQpKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0Wm9vbVNwYW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmdldE1heFpvb20oKSAtIHRoaXMuZ2V0TWluWm9vbSgpO1xyXG5cdH0sXHJcblxyXG5cdF9wYW5JbnNpZGVNYXhCb3VuZHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fZW5mb3JjaW5nQm91bmRzKSB7XHJcblx0XHRcdHRoaXMucGFuSW5zaWRlQm91bmRzKHRoaXMub3B0aW9ucy5tYXhCb3VuZHMpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9jaGVja0lmTG9hZGVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ1NldCBtYXAgY2VudGVyIGFuZCB6b29tIGZpcnN0LicpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIERPTSBldmVudCBoYW5kbGluZ1xyXG5cclxuXHQvLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBldmVudHNcclxuXHRfaW5pdEV2ZW50czogZnVuY3Rpb24gKHJlbW92ZSkge1xyXG5cdFx0aWYgKCFMLkRvbUV2ZW50KSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHRoaXMuX3RhcmdldHMgPSB7fTtcclxuXHRcdHRoaXMuX3RhcmdldHNbTC5zdGFtcCh0aGlzLl9jb250YWluZXIpXSA9IHRoaXM7XHJcblxyXG5cdFx0dmFyIG9uT2ZmID0gcmVtb3ZlID8gJ29mZicgOiAnb24nO1xyXG5cclxuXHRcdC8vIEBldmVudCBjbGljazogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciBjbGlja3MgKG9yIHRhcHMpIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgZGJsY2xpY2s6IE1vdXNlRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgZG91YmxlLWNsaWNrcyAob3IgZG91YmxlLXRhcHMpIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2Vkb3duOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHB1c2hlcyB0aGUgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2V1cDogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciByZWxlYXNlcyB0aGUgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2VvdmVyOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtb3VzZSBlbnRlcnMgdGhlIG1hcC5cclxuXHRcdC8vIEBldmVudCBtb3VzZW91dDogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbW91c2UgbGVhdmVzIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2Vtb3ZlOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGlsZSB0aGUgbW91c2UgbW92ZXMgb3ZlciB0aGUgbWFwLlxyXG5cdFx0Ly8gQGV2ZW50IGNvbnRleHRtZW51OiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHB1c2hlcyB0aGUgcmlnaHQgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAsIHByZXZlbnRzXHJcblx0XHQvLyBkZWZhdWx0IGJyb3dzZXIgY29udGV4dCBtZW51IGZyb20gc2hvd2luZyBpZiB0aGVyZSBhcmUgbGlzdGVuZXJzIG9uXHJcblx0XHQvLyB0aGlzIGV2ZW50LiBBbHNvIGZpcmVkIG9uIG1vYmlsZSB3aGVuIHRoZSB1c2VyIGhvbGRzIGEgc2luZ2xlIHRvdWNoXHJcblx0XHQvLyBmb3IgYSBzZWNvbmQgKGFsc28gY2FsbGVkIGxvbmcgcHJlc3MpLlxyXG5cdFx0Ly8gQGV2ZW50IGtleXByZXNzOiBLZXlib2FyZEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHByZXNzZXMgYSBrZXkgZnJvbSB0aGUga2V5Ym9hcmQgd2hpbGUgdGhlIG1hcCBpcyBmb2N1c2VkLlxyXG5cdFx0TC5Eb21FdmVudFtvbk9mZl0odGhpcy5fY29udGFpbmVyLCAnY2xpY2sgZGJsY2xpY2sgbW91c2Vkb3duIG1vdXNldXAgJyArXHJcblx0XHRcdCdtb3VzZW92ZXIgbW91c2VvdXQgbW91c2Vtb3ZlIGNvbnRleHRtZW51IGtleXByZXNzJywgdGhpcy5faGFuZGxlRE9NRXZlbnQsIHRoaXMpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMudHJhY2tSZXNpemUpIHtcclxuXHRcdFx0TC5Eb21FdmVudFtvbk9mZl0od2luZG93LCAncmVzaXplJywgdGhpcy5fb25SZXNpemUsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QgJiYgdGhpcy5vcHRpb25zLnRyYW5zZm9ybTNETGltaXQpIHtcclxuXHRcdFx0dGhpc1tvbk9mZl0oJ21vdmVlbmQnLCB0aGlzLl9vbk1vdmVFbmQpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9vblJlc2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9yZXNpemVSZXF1ZXN0KTtcclxuXHRcdHRoaXMuX3Jlc2l6ZVJlcXVlc3QgPSBMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShcclxuXHRcdCAgICAgICAgZnVuY3Rpb24gKCkgeyB0aGlzLmludmFsaWRhdGVTaXplKHtkZWJvdW5jZU1vdmVlbmQ6IHRydWV9KTsgfSwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0X29uU2Nyb2xsOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9jb250YWluZXIuc2Nyb2xsVG9wICA9IDA7XHJcblx0XHR0aGlzLl9jb250YWluZXIuc2Nyb2xsTGVmdCA9IDA7XHJcblx0fSxcclxuXHJcblx0X29uTW92ZUVuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX2dldE1hcFBhbmVQb3MoKTtcclxuXHRcdGlmIChNYXRoLm1heChNYXRoLmFicyhwb3MueCksIE1hdGguYWJzKHBvcy55KSkgPj0gdGhpcy5vcHRpb25zLnRyYW5zZm9ybTNETGltaXQpIHtcclxuXHRcdFx0Ly8gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTIwMzg3MyBidXQgV2Via2l0IGFsc28gaGF2ZVxyXG5cdFx0XHQvLyBhIHBpeGVsIG9mZnNldCBvbiB2ZXJ5IGhpZ2ggdmFsdWVzLCBzZWU6IGh0dHA6Ly9qc2ZpZGRsZS5uZXQvZGc2cjVoaGIvXHJcblx0XHRcdHRoaXMuX3Jlc2V0Vmlldyh0aGlzLmdldENlbnRlcigpLCB0aGlzLmdldFpvb20oKSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2ZpbmRFdmVudFRhcmdldHM6IGZ1bmN0aW9uIChlLCB0eXBlKSB7XHJcblx0XHR2YXIgdGFyZ2V0cyA9IFtdLFxyXG5cdFx0ICAgIHRhcmdldCxcclxuXHRcdCAgICBpc0hvdmVyID0gdHlwZSA9PT0gJ21vdXNlb3V0JyB8fCB0eXBlID09PSAnbW91c2VvdmVyJyxcclxuXHRcdCAgICBzcmMgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQsXHJcblx0XHQgICAgZHJhZ2dpbmcgPSBmYWxzZTtcclxuXHJcblx0XHR3aGlsZSAoc3JjKSB7XHJcblx0XHRcdHRhcmdldCA9IHRoaXMuX3RhcmdldHNbTC5zdGFtcChzcmMpXTtcclxuXHRcdFx0aWYgKHRhcmdldCAmJiAodHlwZSA9PT0gJ2NsaWNrJyB8fCB0eXBlID09PSAncHJlY2xpY2snKSAmJiAhZS5fc2ltdWxhdGVkICYmIHRoaXMuX2RyYWdnYWJsZU1vdmVkKHRhcmdldCkpIHtcclxuXHRcdFx0XHQvLyBQcmV2ZW50IGZpcmluZyBjbGljayBhZnRlciB5b3UganVzdCBkcmFnZ2VkIGFuIG9iamVjdC5cclxuXHRcdFx0XHRkcmFnZ2luZyA9IHRydWU7XHJcblx0XHRcdFx0YnJlYWs7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKHRhcmdldCAmJiB0YXJnZXQubGlzdGVucyh0eXBlLCB0cnVlKSkge1xyXG5cdFx0XHRcdGlmIChpc0hvdmVyICYmICFMLkRvbUV2ZW50Ll9pc0V4dGVybmFsVGFyZ2V0KHNyYywgZSkpIHsgYnJlYWs7IH1cclxuXHRcdFx0XHR0YXJnZXRzLnB1c2godGFyZ2V0KTtcclxuXHRcdFx0XHRpZiAoaXNIb3ZlcikgeyBicmVhazsgfVxyXG5cdFx0XHR9XHJcblx0XHRcdGlmIChzcmMgPT09IHRoaXMuX2NvbnRhaW5lcikgeyBicmVhazsgfVxyXG5cdFx0XHRzcmMgPSBzcmMucGFyZW50Tm9kZTtcclxuXHRcdH1cclxuXHRcdGlmICghdGFyZ2V0cy5sZW5ndGggJiYgIWRyYWdnaW5nICYmICFpc0hvdmVyICYmIEwuRG9tRXZlbnQuX2lzRXh0ZXJuYWxUYXJnZXQoc3JjLCBlKSkge1xyXG5cdFx0XHR0YXJnZXRzID0gW3RoaXNdO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRhcmdldHM7XHJcblx0fSxcclxuXHJcblx0X2hhbmRsZURPTUV2ZW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9sb2FkZWQgfHwgTC5Eb21FdmVudC5fc2tpcHBlZChlKSkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgdHlwZSA9IGUudHlwZSA9PT0gJ2tleXByZXNzJyAmJiBlLmtleUNvZGUgPT09IDEzID8gJ2NsaWNrJyA6IGUudHlwZTtcclxuXHJcblx0XHRpZiAodHlwZSA9PT0gJ21vdXNlZG93bicpIHtcclxuXHRcdFx0Ly8gcHJldmVudHMgb3V0bGluZSB3aGVuIGNsaWNraW5nIG9uIGtleWJvYXJkLWZvY3VzYWJsZSBlbGVtZW50XHJcblx0XHRcdEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZShlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2ZpcmVET01FdmVudChlLCB0eXBlKTtcclxuXHR9LFxyXG5cclxuXHRfZmlyZURPTUV2ZW50OiBmdW5jdGlvbiAoZSwgdHlwZSwgdGFyZ2V0cykge1xyXG5cclxuXHRcdGlmIChlLnR5cGUgPT09ICdjbGljaycpIHtcclxuXHRcdFx0Ly8gRmlyZSBhIHN5bnRoZXRpYyAncHJlY2xpY2snIGV2ZW50IHdoaWNoIHByb3BhZ2F0ZXMgdXAgKG1haW5seSBmb3IgY2xvc2luZyBwb3B1cHMpLlxyXG5cdFx0XHQvLyBAZXZlbnQgcHJlY2xpY2s6IE1vdXNlRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgYmVmb3JlIG1vdXNlIGNsaWNrIG9uIHRoZSBtYXAgKHNvbWV0aW1lcyB1c2VmdWwgd2hlbiB5b3VcclxuXHRcdFx0Ly8gd2FudCBzb21ldGhpbmcgdG8gaGFwcGVuIG9uIGNsaWNrIGJlZm9yZSBhbnkgZXhpc3RpbmcgY2xpY2tcclxuXHRcdFx0Ly8gaGFuZGxlcnMgc3RhcnQgcnVubmluZykuXHJcblx0XHRcdHZhciBzeW50aCA9IEwuVXRpbC5leHRlbmQoe30sIGUpO1xyXG5cdFx0XHRzeW50aC50eXBlID0gJ3ByZWNsaWNrJztcclxuXHRcdFx0dGhpcy5fZmlyZURPTUV2ZW50KHN5bnRoLCBzeW50aC50eXBlLCB0YXJnZXRzKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoZS5fc3RvcHBlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHQvLyBGaW5kIHRoZSBsYXllciB0aGUgZXZlbnQgaXMgcHJvcGFnYXRpbmcgZnJvbSBhbmQgaXRzIHBhcmVudHMuXHJcblx0XHR0YXJnZXRzID0gKHRhcmdldHMgfHwgW10pLmNvbmNhdCh0aGlzLl9maW5kRXZlbnRUYXJnZXRzKGUsIHR5cGUpKTtcclxuXHJcblx0XHRpZiAoIXRhcmdldHMubGVuZ3RoKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciB0YXJnZXQgPSB0YXJnZXRzWzBdO1xyXG5cdFx0aWYgKHR5cGUgPT09ICdjb250ZXh0bWVudScgJiYgdGFyZ2V0Lmxpc3RlbnModHlwZSwgdHJ1ZSkpIHtcclxuXHRcdFx0TC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdChlKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0b3JpZ2luYWxFdmVudDogZVxyXG5cdFx0fTtcclxuXHJcblx0XHRpZiAoZS50eXBlICE9PSAna2V5cHJlc3MnKSB7XHJcblx0XHRcdHZhciBpc01hcmtlciA9IHRhcmdldCBpbnN0YW5jZW9mIEwuTWFya2VyO1xyXG5cdFx0XHRkYXRhLmNvbnRhaW5lclBvaW50ID0gaXNNYXJrZXIgP1xyXG5cdFx0XHRcdFx0dGhpcy5sYXRMbmdUb0NvbnRhaW5lclBvaW50KHRhcmdldC5nZXRMYXRMbmcoKSkgOiB0aGlzLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xyXG5cdFx0XHRkYXRhLmxheWVyUG9pbnQgPSB0aGlzLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KGRhdGEuY29udGFpbmVyUG9pbnQpO1xyXG5cdFx0XHRkYXRhLmxhdGxuZyA9IGlzTWFya2VyID8gdGFyZ2V0LmdldExhdExuZygpIDogdGhpcy5sYXllclBvaW50VG9MYXRMbmcoZGF0YS5sYXllclBvaW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHRhcmdldHMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGFyZ2V0c1tpXS5maXJlKHR5cGUsIGRhdGEsIHRydWUpO1xyXG5cdFx0XHRpZiAoZGF0YS5vcmlnaW5hbEV2ZW50Ll9zdG9wcGVkIHx8XHJcblx0XHRcdFx0KHRhcmdldHNbaV0ub3B0aW9ucy5ub25CdWJibGluZ0V2ZW50cyAmJiBMLlV0aWwuaW5kZXhPZih0YXJnZXRzW2ldLm9wdGlvbnMubm9uQnViYmxpbmdFdmVudHMsIHR5cGUpICE9PSAtMSkpIHsgcmV0dXJuOyB9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2RyYWdnYWJsZU1vdmVkOiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHRvYmogPSBvYmouZHJhZ2dpbmcgJiYgb2JqLmRyYWdnaW5nLmVuYWJsZWQoKSA/IG9iaiA6IHRoaXM7XHJcblx0XHRyZXR1cm4gKG9iai5kcmFnZ2luZyAmJiBvYmouZHJhZ2dpbmcubW92ZWQoKSkgfHwgKHRoaXMuYm94Wm9vbSAmJiB0aGlzLmJveFpvb20ubW92ZWQoKSk7XHJcblx0fSxcclxuXHJcblx0X2NsZWFySGFuZGxlcnM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0aGlzLl9oYW5kbGVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHR0aGlzLl9oYW5kbGVyc1tpXS5kaXNhYmxlKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cclxuXHQvLyBAbWV0aG9kIHdoZW5SZWFkeShmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gUnVucyB0aGUgZ2l2ZW4gZnVuY3Rpb24gYGZuYCB3aGVuIHRoZSBtYXAgZ2V0cyBpbml0aWFsaXplZCB3aXRoXHJcblx0Ly8gYSB2aWV3IChjZW50ZXIgYW5kIHpvb20pIGFuZCBhdCBsZWFzdCBvbmUgbGF5ZXIsIG9yIGltbWVkaWF0ZWx5XHJcblx0Ly8gaWYgaXQncyBhbHJlYWR5IGluaXRpYWxpemVkLCBvcHRpb25hbGx5IHBhc3NpbmcgYSBmdW5jdGlvbiBjb250ZXh0LlxyXG5cdHdoZW5SZWFkeTogZnVuY3Rpb24gKGNhbGxiYWNrLCBjb250ZXh0KSB7XHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdGNhbGxiYWNrLmNhbGwoY29udGV4dCB8fCB0aGlzLCB7dGFyZ2V0OiB0aGlzfSk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLm9uKCdsb2FkJywgY2FsbGJhY2ssIGNvbnRleHQpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIHByaXZhdGUgbWV0aG9kcyBmb3IgZ2V0dGluZyBtYXAgc3RhdGVcclxuXHJcblx0X2dldE1hcFBhbmVQb3M6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBMLkRvbVV0aWwuZ2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSkgfHwgbmV3IEwuUG9pbnQoMCwgMCk7XHJcblx0fSxcclxuXHJcblx0X21vdmVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgcG9zID0gdGhpcy5fZ2V0TWFwUGFuZVBvcygpO1xyXG5cdFx0cmV0dXJuIHBvcyAmJiAhcG9zLmVxdWFscyhbMCwgMF0pO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRUb3BMZWZ0UG9pbnQ6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcclxuXHRcdHZhciBwaXhlbE9yaWdpbiA9IGNlbnRlciAmJiB6b29tICE9PSB1bmRlZmluZWQgP1xyXG5cdFx0XHR0aGlzLl9nZXROZXdQaXhlbE9yaWdpbihjZW50ZXIsIHpvb20pIDpcclxuXHRcdFx0dGhpcy5nZXRQaXhlbE9yaWdpbigpO1xyXG5cdFx0cmV0dXJuIHBpeGVsT3JpZ2luLnN1YnRyYWN0KHRoaXMuX2dldE1hcFBhbmVQb3MoKSk7XHJcblx0fSxcclxuXHJcblx0X2dldE5ld1BpeGVsT3JpZ2luOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHR2YXIgdmlld0hhbGYgPSB0aGlzLmdldFNpemUoKS5fZGl2aWRlQnkoMik7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0KGNlbnRlciwgem9vbSkuX3N1YnRyYWN0KHZpZXdIYWxmKS5fYWRkKHRoaXMuX2dldE1hcFBhbmVQb3MoKSkuX3JvdW5kKCk7XHJcblx0fSxcclxuXHJcblx0X2xhdExuZ1RvTmV3TGF5ZXJQb2ludDogZnVuY3Rpb24gKGxhdGxuZywgem9vbSwgY2VudGVyKSB7XHJcblx0XHR2YXIgdG9wTGVmdCA9IHRoaXMuX2dldE5ld1BpeGVsT3JpZ2luKGNlbnRlciwgem9vbSk7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0KGxhdGxuZywgem9vbSkuX3N1YnRyYWN0KHRvcExlZnQpO1xyXG5cdH0sXHJcblxyXG5cdF9sYXRMbmdCb3VuZHNUb05ld0xheWVyQm91bmRzOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzLCB6b29tLCBjZW50ZXIpIHtcclxuXHRcdHZhciB0b3BMZWZ0ID0gdGhpcy5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyLCB6b29tKTtcclxuXHRcdHJldHVybiBMLmJvdW5kcyhbXHJcblx0XHRcdHRoaXMucHJvamVjdChsYXRMbmdCb3VuZHMuZ2V0U291dGhXZXN0KCksIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KSxcclxuXHRcdFx0dGhpcy5wcm9qZWN0KGxhdExuZ0JvdW5kcy5nZXROb3J0aFdlc3QoKSwgem9vbSkuX3N1YnRyYWN0KHRvcExlZnQpLFxyXG5cdFx0XHR0aGlzLnByb2plY3QobGF0TG5nQm91bmRzLmdldFNvdXRoRWFzdCgpLCB6b29tKS5fc3VidHJhY3QodG9wTGVmdCksXHJcblx0XHRcdHRoaXMucHJvamVjdChsYXRMbmdCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KVxyXG5cdFx0XSk7XHJcblx0fSxcclxuXHJcblx0Ly8gbGF5ZXIgcG9pbnQgb2YgdGhlIGN1cnJlbnQgY2VudGVyXHJcblx0X2dldENlbnRlckxheWVyUG9pbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KHRoaXMuZ2V0U2l6ZSgpLl9kaXZpZGVCeSgyKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gb2Zmc2V0IG9mIHRoZSBzcGVjaWZpZWQgcGxhY2UgdG8gdGhlIGN1cnJlbnQgY2VudGVyIGluIHBpeGVsc1xyXG5cdF9nZXRDZW50ZXJPZmZzZXQ6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLmxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmcpLnN1YnRyYWN0KHRoaXMuX2dldENlbnRlckxheWVyUG9pbnQoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gYWRqdXN0IGNlbnRlciBmb3IgdmlldyB0byBnZXQgaW5zaWRlIGJvdW5kc1xyXG5cdF9saW1pdENlbnRlcjogZnVuY3Rpb24gKGNlbnRlciwgem9vbSwgYm91bmRzKSB7XHJcblxyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIGNlbnRlcjsgfVxyXG5cclxuXHRcdHZhciBjZW50ZXJQb2ludCA9IHRoaXMucHJvamVjdChjZW50ZXIsIHpvb20pLFxyXG5cdFx0ICAgIHZpZXdIYWxmID0gdGhpcy5nZXRTaXplKCkuZGl2aWRlQnkoMiksXHJcblx0XHQgICAgdmlld0JvdW5kcyA9IG5ldyBMLkJvdW5kcyhjZW50ZXJQb2ludC5zdWJ0cmFjdCh2aWV3SGFsZiksIGNlbnRlclBvaW50LmFkZCh2aWV3SGFsZikpLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX2dldEJvdW5kc09mZnNldCh2aWV3Qm91bmRzLCBib3VuZHMsIHpvb20pO1xyXG5cclxuXHRcdC8vIElmIG9mZnNldCBpcyBsZXNzIHRoYW4gYSBwaXhlbCwgaWdub3JlLlxyXG5cdFx0Ly8gVGhpcyBwcmV2ZW50cyB1bnN0YWJsZSBwcm9qZWN0aW9ucyBmcm9tIGdldHRpbmcgaW50b1xyXG5cdFx0Ly8gYW4gaW5maW5pdGUgbG9vcCBvZiB0aW55IG9mZnNldHMuXHJcblx0XHRpZiAob2Zmc2V0LnJvdW5kKCkuZXF1YWxzKFswLCAwXSkpIHtcclxuXHRcdFx0cmV0dXJuIGNlbnRlcjtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcy51bnByb2plY3QoY2VudGVyUG9pbnQuYWRkKG9mZnNldCksIHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIGFkanVzdCBvZmZzZXQgZm9yIHZpZXcgdG8gZ2V0IGluc2lkZSBib3VuZHNcclxuXHRfbGltaXRPZmZzZXQ6IGZ1bmN0aW9uIChvZmZzZXQsIGJvdW5kcykge1xyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIG9mZnNldDsgfVxyXG5cclxuXHRcdHZhciB2aWV3Qm91bmRzID0gdGhpcy5nZXRQaXhlbEJvdW5kcygpLFxyXG5cdFx0ICAgIG5ld0JvdW5kcyA9IG5ldyBMLkJvdW5kcyh2aWV3Qm91bmRzLm1pbi5hZGQob2Zmc2V0KSwgdmlld0JvdW5kcy5tYXguYWRkKG9mZnNldCkpO1xyXG5cclxuXHRcdHJldHVybiBvZmZzZXQuYWRkKHRoaXMuX2dldEJvdW5kc09mZnNldChuZXdCb3VuZHMsIGJvdW5kcykpO1xyXG5cdH0sXHJcblxyXG5cdC8vIHJldHVybnMgb2Zmc2V0IG5lZWRlZCBmb3IgcHhCb3VuZHMgdG8gZ2V0IGluc2lkZSBtYXhCb3VuZHMgYXQgYSBzcGVjaWZpZWQgem9vbVxyXG5cdF9nZXRCb3VuZHNPZmZzZXQ6IGZ1bmN0aW9uIChweEJvdW5kcywgbWF4Qm91bmRzLCB6b29tKSB7XHJcblx0XHR2YXIgcHJvamVjdGVkTWF4Qm91bmRzID0gTC5ib3VuZHMoXHJcblx0XHQgICAgICAgIHRoaXMucHJvamVjdChtYXhCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLFxyXG5cdFx0ICAgICAgICB0aGlzLnByb2plY3QobWF4Qm91bmRzLmdldFNvdXRoV2VzdCgpLCB6b29tKVxyXG5cdFx0ICAgICksXHJcblx0XHQgICAgbWluT2Zmc2V0ID0gcHJvamVjdGVkTWF4Qm91bmRzLm1pbi5zdWJ0cmFjdChweEJvdW5kcy5taW4pLFxyXG5cdFx0ICAgIG1heE9mZnNldCA9IHByb2plY3RlZE1heEJvdW5kcy5tYXguc3VidHJhY3QocHhCb3VuZHMubWF4KSxcclxuXHJcblx0XHQgICAgZHggPSB0aGlzLl9yZWJvdW5kKG1pbk9mZnNldC54LCAtbWF4T2Zmc2V0LngpLFxyXG5cdFx0ICAgIGR5ID0gdGhpcy5fcmVib3VuZChtaW5PZmZzZXQueSwgLW1heE9mZnNldC55KTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoZHgsIGR5KTtcclxuXHR9LFxyXG5cclxuXHRfcmVib3VuZDogZnVuY3Rpb24gKGxlZnQsIHJpZ2h0KSB7XHJcblx0XHRyZXR1cm4gbGVmdCArIHJpZ2h0ID4gMCA/XHJcblx0XHRcdE1hdGgucm91bmQobGVmdCAtIHJpZ2h0KSAvIDIgOlxyXG5cdFx0XHRNYXRoLm1heCgwLCBNYXRoLmNlaWwobGVmdCkpIC0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcihyaWdodCkpO1xyXG5cdH0sXHJcblxyXG5cdF9saW1pdFpvb206IGZ1bmN0aW9uICh6b29tKSB7XHJcblx0XHR2YXIgbWluID0gdGhpcy5nZXRNaW5ab29tKCksXHJcblx0XHQgICAgbWF4ID0gdGhpcy5nZXRNYXhab29tKCksXHJcblx0XHQgICAgc25hcCA9IEwuQnJvd3Nlci5hbnkzZCA/IHRoaXMub3B0aW9ucy56b29tU25hcCA6IDE7XHJcblx0XHRpZiAoc25hcCkge1xyXG5cdFx0XHR6b29tID0gTWF0aC5yb3VuZCh6b29tIC8gc25hcCkgKiBzbmFwO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIE1hdGgubWF4KG1pbiwgTWF0aC5taW4obWF4LCB6b29tKSk7XHJcblx0fSxcclxuXHJcblx0X29uUGFuVHJhbnNpdGlvblN0ZXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuZmlyZSgnbW92ZScpO1xyXG5cdH0sXHJcblxyXG5cdF9vblBhblRyYW5zaXRpb25FbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC1wYW4tYW5pbScpO1xyXG5cdFx0dGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0fSxcclxuXHJcblx0X3RyeUFuaW1hdGVkUGFuOiBmdW5jdGlvbiAoY2VudGVyLCBvcHRpb25zKSB7XHJcblx0XHQvLyBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG5ldyBhbmQgY3VycmVudCBjZW50ZXJzIGluIHBpeGVsc1xyXG5cdFx0dmFyIG9mZnNldCA9IHRoaXMuX2dldENlbnRlck9mZnNldChjZW50ZXIpLl9mbG9vcigpO1xyXG5cclxuXHRcdC8vIGRvbid0IGFuaW1hdGUgdG9vIGZhciB1bmxlc3MgYW5pbWF0ZTogdHJ1ZSBzcGVjaWZpZWQgaW4gb3B0aW9uc1xyXG5cdFx0aWYgKChvcHRpb25zICYmIG9wdGlvbnMuYW5pbWF0ZSkgIT09IHRydWUgJiYgIXRoaXMuZ2V0U2l6ZSgpLmNvbnRhaW5zKG9mZnNldCkpIHsgcmV0dXJuIGZhbHNlOyB9XHJcblxyXG5cdFx0dGhpcy5wYW5CeShvZmZzZXQsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHJldHVybiB0cnVlO1xyXG5cdH0sXHJcblxyXG5cdF9jcmVhdGVBbmltUHJveHk6IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHR2YXIgcHJveHkgPSB0aGlzLl9wcm94eSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXByb3h5IGxlYWZsZXQtem9vbS1hbmltYXRlZCcpO1xyXG5cdFx0dGhpcy5fcGFuZXMubWFwUGFuZS5hcHBlbmRDaGlsZChwcm94eSk7XHJcblxyXG5cdFx0dGhpcy5vbignem9vbWFuaW0nLCBmdW5jdGlvbiAoZSkge1xyXG5cdFx0XHR2YXIgcHJvcCA9IEwuRG9tVXRpbC5UUkFOU0ZPUk0sXHJcblx0XHRcdCAgICB0cmFuc2Zvcm0gPSBwcm94eS5zdHlsZVtwcm9wXTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0ocHJveHksIHRoaXMucHJvamVjdChlLmNlbnRlciwgZS56b29tKSwgdGhpcy5nZXRab29tU2NhbGUoZS56b29tLCAxKSk7XHJcblxyXG5cdFx0XHQvLyB3b3JrYXJvdW5kIGZvciBjYXNlIHdoZW4gdHJhbnNmb3JtIGlzIHRoZSBzYW1lIGFuZCBzbyB0cmFuc2l0aW9uZW5kIGV2ZW50IGlzIG5vdCBmaXJlZFxyXG5cdFx0XHRpZiAodHJhbnNmb3JtID09PSBwcm94eS5zdHlsZVtwcm9wXSAmJiB0aGlzLl9hbmltYXRpbmdab29tKSB7XHJcblx0XHRcdFx0dGhpcy5fb25ab29tVHJhbnNpdGlvbkVuZCgpO1xyXG5cdFx0XHR9XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLm9uKCdsb2FkIG1vdmVlbmQnLCBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHZhciBjID0gdGhpcy5nZXRDZW50ZXIoKSxcclxuXHRcdFx0ICAgIHogPSB0aGlzLmdldFpvb20oKTtcclxuXHRcdFx0TC5Eb21VdGlsLnNldFRyYW5zZm9ybShwcm94eSwgdGhpcy5wcm9qZWN0KGMsIHopLCB0aGlzLmdldFpvb21TY2FsZSh6LCAxKSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRfY2F0Y2hUcmFuc2l0aW9uRW5kOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20gJiYgZS5wcm9wZXJ0eU5hbWUuaW5kZXhPZigndHJhbnNmb3JtJykgPj0gMCkge1xyXG5cdFx0XHR0aGlzLl9vblpvb21UcmFuc2l0aW9uRW5kKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X25vdGhpbmdUb0FuaW1hdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhdGhpcy5fY29udGFpbmVyLmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcpLmxlbmd0aDtcclxuXHR9LFxyXG5cclxuXHRfdHJ5QW5pbWF0ZWRab29tOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuIHRydWU7IH1cclxuXHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIGRpc2FibGVkLCBub3Qgc3VwcG9ydGVkIG9yIHpvb20gZGlmZmVyZW5jZSBpcyB0b28gbGFyZ2VcclxuXHRcdGlmICghdGhpcy5fem9vbUFuaW1hdGVkIHx8IG9wdGlvbnMuYW5pbWF0ZSA9PT0gZmFsc2UgfHwgdGhpcy5fbm90aGluZ1RvQW5pbWF0ZSgpIHx8XHJcblx0XHQgICAgICAgIE1hdGguYWJzKHpvb20gLSB0aGlzLl96b29tKSA+IHRoaXMub3B0aW9ucy56b29tQW5pbWF0aW9uVGhyZXNob2xkKSB7IHJldHVybiBmYWxzZTsgfVxyXG5cclxuXHRcdC8vIG9mZnNldCBpcyB0aGUgcGl4ZWwgY29vcmRzIG9mIHRoZSB6b29tIG9yaWdpbiByZWxhdGl2ZSB0byB0aGUgY3VycmVudCBjZW50ZXJcclxuXHRcdHZhciBzY2FsZSA9IHRoaXMuZ2V0Wm9vbVNjYWxlKHpvb20pLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX2dldENlbnRlck9mZnNldChjZW50ZXIpLl9kaXZpZGVCeSgxIC0gMSAvIHNjYWxlKTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIHRoZSB6b29tIG9yaWdpbiBpc24ndCB3aXRoaW4gb25lIHNjcmVlbiBmcm9tIHRoZSBjdXJyZW50IGNlbnRlciwgdW5sZXNzIGZvcmNlZFxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gdHJ1ZSAmJiAhdGhpcy5nZXRTaXplKCkuY29udGFpbnMob2Zmc2V0KSkgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXNcclxuXHRcdFx0ICAgIC5fbW92ZVN0YXJ0KHRydWUpXHJcblx0XHRcdCAgICAuX2FuaW1hdGVab29tKGNlbnRlciwgem9vbSwgdHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHRyZXR1cm4gdHJ1ZTtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIHN0YXJ0QW5pbSwgbm9VcGRhdGUpIHtcclxuXHRcdGlmIChzdGFydEFuaW0pIHtcclxuXHRcdFx0dGhpcy5fYW5pbWF0aW5nWm9vbSA9IHRydWU7XHJcblxyXG5cdFx0XHQvLyByZW1lbWJlciB3aGF0IGNlbnRlci96b29tIHRvIHNldCBhZnRlciBhbmltYXRpb25cclxuXHRcdFx0dGhpcy5fYW5pbWF0ZVRvQ2VudGVyID0gY2VudGVyO1xyXG5cdFx0XHR0aGlzLl9hbmltYXRlVG9ab29tID0gem9vbTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC16b29tLWFuaW0nKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgem9vbWFuaW06IFpvb21BbmltRXZlbnRcclxuXHRcdC8vIEZpcmVkIG9uIGV2ZXJ5IGZyYW1lIG9mIGEgem9vbSBhbmltYXRpb25cclxuXHRcdHRoaXMuZmlyZSgnem9vbWFuaW0nLCB7XHJcblx0XHRcdGNlbnRlcjogY2VudGVyLFxyXG5cdFx0XHR6b29tOiB6b29tLFxyXG5cdFx0XHRub1VwZGF0ZTogbm9VcGRhdGVcclxuXHRcdH0pO1xyXG5cclxuXHRcdC8vIFdvcmsgYXJvdW5kIHdlYmtpdCBub3QgZmlyaW5nICd0cmFuc2l0aW9uZW5kJywgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzM2ODksIDI2OTNcclxuXHRcdHNldFRpbWVvdXQoTC5iaW5kKHRoaXMuX29uWm9vbVRyYW5zaXRpb25FbmQsIHRoaXMpLCAyNTApO1xyXG5cdH0sXHJcblxyXG5cdF9vblpvb21UcmFuc2l0aW9uRW5kOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcFBhbmUsICdsZWFmbGV0LXpvb20tYW5pbScpO1xyXG5cclxuXHRcdHRoaXMuX2FuaW1hdGluZ1pvb20gPSBmYWxzZTtcclxuXHJcblx0XHR0aGlzLl9tb3ZlKHRoaXMuX2FuaW1hdGVUb0NlbnRlciwgdGhpcy5fYW5pbWF0ZVRvWm9vbSk7XHJcblxyXG5cdFx0Ly8gVGhpcyBhbmltIGZyYW1lIHNob3VsZCBwcmV2ZW50IGFuIG9ic2N1cmUgaU9TIHdlYmtpdCB0aWxlIGxvYWRpbmcgcmFjZSBjb25kaXRpb24uXHJcblx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXMuX21vdmVFbmQodHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQHNlY3Rpb25cclxuXHJcbi8vIEBmYWN0b3J5IEwubWFwKGlkOiBTdHJpbmcsIG9wdGlvbnM/OiBNYXAgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGEgbWFwIG9iamVjdCBnaXZlbiB0aGUgRE9NIElEIG9mIGEgYDxkaXY+YCBlbGVtZW50XHJcbi8vIGFuZCBvcHRpb25hbGx5IGFuIG9iamVjdCBsaXRlcmFsIHdpdGggYE1hcCBvcHRpb25zYC5cclxuLy9cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubWFwKGVsOiBIVE1MRWxlbWVudCwgb3B0aW9ucz86IE1hcCBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYSBtYXAgb2JqZWN0IGdpdmVuIGFuIGluc3RhbmNlIG9mIGEgYDxkaXY+YCBIVE1MIGVsZW1lbnRcclxuLy8gYW5kIG9wdGlvbmFsbHkgYW4gb2JqZWN0IGxpdGVyYWwgd2l0aCBgTWFwIG9wdGlvbnNgLlxyXG5MLm1hcCA9IGZ1bmN0aW9uIChpZCwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5NYXAoaWQsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG5cbi8qXG4gKiBAY2xhc3MgTGF5ZXJcbiAqIEBpbmhlcml0cyBFdmVudGVkXG4gKiBAYWthIEwuTGF5ZXJcbiAqIEBha2EgSUxheWVyXG4gKlxuICogQSBzZXQgb2YgbWV0aG9kcyBmcm9tIHRoZSBMYXllciBiYXNlIGNsYXNzIHRoYXQgYWxsIExlYWZsZXQgbGF5ZXJzIHVzZS5cbiAqIEluaGVyaXRzIGFsbCBtZXRob2RzLCBvcHRpb25zIGFuZCBldmVudHMgZnJvbSBgTC5FdmVudGVkYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbGF5ZXIgPSBMLk1hcmtlcihsYXRsbmcpLmFkZFRvKG1hcCk7XG4gKiBsYXllci5hZGRUbyhtYXApO1xuICogbGF5ZXIucmVtb3ZlKCk7XG4gKiBgYGBcbiAqXG4gKiBAZXZlbnQgYWRkOiBFdmVudFxuICogRmlyZWQgYWZ0ZXIgdGhlIGxheWVyIGlzIGFkZGVkIHRvIGEgbWFwXG4gKlxuICogQGV2ZW50IHJlbW92ZTogRXZlbnRcbiAqIEZpcmVkIGFmdGVyIHRoZSBsYXllciBpcyByZW1vdmVkIGZyb20gYSBtYXBcbiAqL1xuXG5cbkwuTGF5ZXIgPSBMLkV2ZW50ZWQuZXh0ZW5kKHtcblxuXHQvLyBDbGFzc2VzIGV4dGVuZGluZyBgTC5MYXllcmAgd2lsbCBpbmhlcml0IHRoZSBmb2xsb3dpbmcgb3B0aW9uczpcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gcGFuZTogU3RyaW5nID0gJ292ZXJsYXlQYW5lJ1xuXHRcdC8vIEJ5IGRlZmF1bHQgdGhlIGxheWVyIHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1hcCdzIFtvdmVybGF5IHBhbmVdKCNtYXAtb3ZlcmxheXBhbmUpLiBPdmVycmlkaW5nIHRoaXMgb3B0aW9uIHdpbGwgY2F1c2UgdGhlIGxheWVyIHRvIGJlIHBsYWNlZCBvbiBhbm90aGVyIHBhbmUgYnkgZGVmYXVsdC5cblx0XHRwYW5lOiAnb3ZlcmxheVBhbmUnLFxuXHRcdG5vbkJ1YmJsaW5nRXZlbnRzOiBbXSwgIC8vIEFycmF5IG9mIGV2ZW50cyB0aGF0IHNob3VsZCBub3QgYmUgYnViYmxlZCB0byBET00gcGFyZW50cyAobGlrZSB0aGUgbWFwKSxcblxuXHRcdC8vIEBvcHRpb24gYXR0cmlidXRpb246IFN0cmluZyA9IG51bGxcblx0XHQvLyBTdHJpbmcgdG8gYmUgc2hvd24gaW4gdGhlIGF0dHJpYnV0aW9uIGNvbnRyb2wsIGRlc2NyaWJlcyB0aGUgbGF5ZXIgZGF0YSwgZS5nLiBcIsKpIE1hcGJveFwiLlxuXHRcdGF0dHJpYnV0aW9uOiBudWxsLFxuXHR9LFxuXG5cdC8qIEBzZWN0aW9uXG5cdCAqIENsYXNzZXMgZXh0ZW5kaW5nIGBMLkxheWVyYCB3aWxsIGluaGVyaXQgdGhlIGZvbGxvd2luZyBtZXRob2RzOlxuXHQgKlxuXHQgKiBAbWV0aG9kIGFkZFRvKG1hcDogTWFwKTogdGhpc1xuXHQgKiBBZGRzIHRoZSBsYXllciB0byB0aGUgZ2l2ZW4gbWFwXG5cdCAqL1xuXHRhZGRUbzogZnVuY3Rpb24gKG1hcCkge1xuXHRcdG1hcC5hZGRMYXllcih0aGlzKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHJlbW92ZTogdGhpc1xuXHQvLyBSZW1vdmVzIHRoZSBsYXllciBmcm9tIHRoZSBtYXAgaXQgaXMgY3VycmVudGx5IGFjdGl2ZSBvbi5cblx0cmVtb3ZlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMucmVtb3ZlRnJvbSh0aGlzLl9tYXAgfHwgdGhpcy5fbWFwVG9BZGQpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVtb3ZlRnJvbShtYXA6IE1hcCk6IHRoaXNcblx0Ly8gUmVtb3ZlcyB0aGUgbGF5ZXIgZnJvbSB0aGUgZ2l2ZW4gbWFwXG5cdHJlbW92ZUZyb206IGZ1bmN0aW9uIChvYmopIHtcblx0XHRpZiAob2JqKSB7XG5cdFx0XHRvYmoucmVtb3ZlTGF5ZXIodGhpcyk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0UGFuZShuYW1lPyA6IFN0cmluZyk6IEhUTUxFbGVtZW50XG5cdC8vIFJldHVybnMgdGhlIGBIVE1MRWxlbWVudGAgcmVwcmVzZW50aW5nIHRoZSBuYW1lZCBwYW5lIG9uIHRoZSBtYXAuIElmIGBuYW1lYCBpcyBvbWl0dGVkLCByZXR1cm5zIHRoZSBwYW5lIGZvciB0aGlzIGxheWVyLlxuXHRnZXRQYW5lOiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdHJldHVybiB0aGlzLl9tYXAuZ2V0UGFuZShuYW1lID8gKHRoaXMub3B0aW9uc1tuYW1lXSB8fCBuYW1lKSA6IHRoaXMub3B0aW9ucy5wYW5lKTtcblx0fSxcblxuXHRhZGRJbnRlcmFjdGl2ZVRhcmdldDogZnVuY3Rpb24gKHRhcmdldEVsKSB7XG5cdFx0dGhpcy5fbWFwLl90YXJnZXRzW0wuc3RhbXAodGFyZ2V0RWwpXSA9IHRoaXM7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0cmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQ6IGZ1bmN0aW9uICh0YXJnZXRFbCkge1xuXHRcdGRlbGV0ZSB0aGlzLl9tYXAuX3RhcmdldHNbTC5zdGFtcCh0YXJnZXRFbCldO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0QXR0cmlidXRpb246IFN0cmluZ1xuXHQvLyBVc2VkIGJ5IHRoZSBgYXR0cmlidXRpb24gY29udHJvbGAsIHJldHVybnMgdGhlIFthdHRyaWJ1dGlvbiBvcHRpb25dKCNncmlkbGF5ZXItYXR0cmlidXRpb24pLlxuXHRnZXRBdHRyaWJ1dGlvbjogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuYXR0cmlidXRpb247XG5cdH0sXG5cblx0X2xheWVyQWRkOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBtYXAgPSBlLnRhcmdldDtcblxuXHRcdC8vIGNoZWNrIGluIGNhc2UgbGF5ZXIgZ2V0cyBhZGRlZCBhbmQgdGhlbiByZW1vdmVkIGJlZm9yZSB0aGUgbWFwIGlzIHJlYWR5XG5cdFx0aWYgKCFtYXAuaGFzTGF5ZXIodGhpcykpIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9tYXAgPSBtYXA7XG5cdFx0dGhpcy5fem9vbUFuaW1hdGVkID0gbWFwLl96b29tQW5pbWF0ZWQ7XG5cblx0XHRpZiAodGhpcy5nZXRFdmVudHMpIHtcblx0XHRcdHZhciBldmVudHMgPSB0aGlzLmdldEV2ZW50cygpO1xuXHRcdFx0bWFwLm9uKGV2ZW50cywgdGhpcyk7XG5cdFx0XHR0aGlzLm9uY2UoJ3JlbW92ZScsIGZ1bmN0aW9uICgpIHtcblx0XHRcdFx0bWFwLm9mZihldmVudHMsIHRoaXMpO1xuXHRcdFx0fSwgdGhpcyk7XG5cdFx0fVxuXG5cdFx0dGhpcy5vbkFkZChtYXApO1xuXG5cdFx0aWYgKHRoaXMuZ2V0QXR0cmlidXRpb24gJiYgdGhpcy5fbWFwLmF0dHJpYnV0aW9uQ29udHJvbCkge1xuXHRcdFx0dGhpcy5fbWFwLmF0dHJpYnV0aW9uQ29udHJvbC5hZGRBdHRyaWJ1dGlvbih0aGlzLmdldEF0dHJpYnV0aW9uKCkpO1xuXHRcdH1cblxuXHRcdHRoaXMuZmlyZSgnYWRkJyk7XG5cdFx0bWFwLmZpcmUoJ2xheWVyYWRkJywge2xheWVyOiB0aGlzfSk7XG5cdH1cbn0pO1xuXG4vKiBAc2VjdGlvbiBFeHRlbnNpb24gbWV0aG9kc1xuICogQHVuaW5oZXJpdGFibGVcbiAqXG4gKiBFdmVyeSBsYXllciBzaG91bGQgZXh0ZW5kIGZyb20gYEwuTGF5ZXJgIGFuZCAocmUtKWltcGxlbWVudCB0aGUgZm9sbG93aW5nIG1ldGhvZHMuXG4gKlxuICogQG1ldGhvZCBvbkFkZChtYXA6IE1hcCk6IHRoaXNcbiAqIFNob3VsZCBjb250YWluIGNvZGUgdGhhdCBjcmVhdGVzIERPTSBlbGVtZW50cyBmb3IgdGhlIGxheWVyLCBhZGRzIHRoZW0gdG8gYG1hcCBwYW5lc2Agd2hlcmUgdGhleSBzaG91bGQgYmVsb25nIGFuZCBwdXRzIGxpc3RlbmVycyBvbiByZWxldmFudCBtYXAgZXZlbnRzLiBDYWxsZWQgb24gW2BtYXAuYWRkTGF5ZXIobGF5ZXIpYF0oI21hcC1hZGRsYXllcikuXG4gKlxuICogQG1ldGhvZCBvblJlbW92ZShtYXA6IE1hcCk6IHRoaXNcbiAqIFNob3VsZCBjb250YWluIGFsbCBjbGVhbiB1cCBjb2RlIHRoYXQgcmVtb3ZlcyB0aGUgbGF5ZXIncyBlbGVtZW50cyBmcm9tIHRoZSBET00gYW5kIHJlbW92ZXMgbGlzdGVuZXJzIHByZXZpb3VzbHkgYWRkZWQgaW4gW2BvbkFkZGBdKCNsYXllci1vbmFkZCkuIENhbGxlZCBvbiBbYG1hcC5yZW1vdmVMYXllcihsYXllcilgXSgjbWFwLXJlbW92ZWxheWVyKS5cbiAqXG4gKiBAbWV0aG9kIGdldEV2ZW50cygpOiBPYmplY3RcbiAqIFRoaXMgb3B0aW9uYWwgbWV0aG9kIHNob3VsZCByZXR1cm4gYW4gb2JqZWN0IGxpa2UgYHsgdmlld3Jlc2V0OiB0aGlzLl9yZXNldCB9YCBmb3IgW2BhZGRFdmVudExpc3RlbmVyYF0oI2V2ZW50ZWQtYWRkZXZlbnRsaXN0ZW5lcikuIFRoZSBldmVudCBoYW5kbGVycyBpbiB0aGlzIG9iamVjdCB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgYWRkZWQgYW5kIHJlbW92ZWQgZnJvbSB0aGUgbWFwIHdpdGggeW91ciBsYXllci5cbiAqXG4gKiBAbWV0aG9kIGdldEF0dHJpYnV0aW9uKCk6IFN0cmluZ1xuICogVGhpcyBvcHRpb25hbCBtZXRob2Qgc2hvdWxkIHJldHVybiBhIHN0cmluZyBjb250YWluaW5nIEhUTUwgdG8gYmUgc2hvd24gb24gdGhlIGBBdHRyaWJ1dGlvbiBjb250cm9sYCB3aGVuZXZlciB0aGUgbGF5ZXIgaXMgdmlzaWJsZS5cbiAqXG4gKiBAbWV0aG9kIGJlZm9yZUFkZChtYXA6IE1hcCk6IHRoaXNcbiAqIE9wdGlvbmFsIG1ldGhvZC4gQ2FsbGVkIG9uIFtgbWFwLmFkZExheWVyKGxheWVyKWBdKCNtYXAtYWRkbGF5ZXIpLCBiZWZvcmUgdGhlIGxheWVyIGlzIGFkZGVkIHRvIHRoZSBtYXAsIGJlZm9yZSBldmVudHMgYXJlIGluaXRpYWxpemVkLCB3aXRob3V0IHdhaXRpbmcgdW50aWwgdGhlIG1hcCBpcyBpbiBhIHVzYWJsZSBzdGF0ZS4gVXNlIGZvciBlYXJseSBpbml0aWFsaXphdGlvbiBvbmx5LlxuICovXG5cblxuLyogQG5hbWVzcGFjZSBNYXBcbiAqIEBzZWN0aW9uIExheWVyIGV2ZW50c1xuICpcbiAqIEBldmVudCBsYXllcmFkZDogTGF5ZXJFdmVudFxuICogRmlyZWQgd2hlbiBhIG5ldyBsYXllciBpcyBhZGRlZCB0byB0aGUgbWFwLlxuICpcbiAqIEBldmVudCBsYXllcnJlbW92ZTogTGF5ZXJFdmVudFxuICogRmlyZWQgd2hlbiBzb21lIGxheWVyIGlzIHJlbW92ZWQgZnJvbSB0aGUgbWFwXG4gKlxuICogQHNlY3Rpb24gTWV0aG9kcyBmb3IgTGF5ZXJzIGFuZCBDb250cm9sc1xuICovXG5MLk1hcC5pbmNsdWRlKHtcblx0Ly8gQG1ldGhvZCBhZGRMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXG5cdC8vIEFkZHMgdGhlIGdpdmVuIGxheWVyIHRvIHRoZSBtYXBcblx0YWRkTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBpZCA9IEwuc3RhbXAobGF5ZXIpO1xuXHRcdGlmICh0aGlzLl9sYXllcnNbaWRdKSB7IHJldHVybiB0aGlzOyB9XG5cdFx0dGhpcy5fbGF5ZXJzW2lkXSA9IGxheWVyO1xuXG5cdFx0bGF5ZXIuX21hcFRvQWRkID0gdGhpcztcblxuXHRcdGlmIChsYXllci5iZWZvcmVBZGQpIHtcblx0XHRcdGxheWVyLmJlZm9yZUFkZCh0aGlzKTtcblx0XHR9XG5cblx0XHR0aGlzLndoZW5SZWFkeShsYXllci5fbGF5ZXJBZGQsIGxheWVyKTtcblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVtb3ZlTGF5ZXIobGF5ZXI6IExheWVyKTogdGhpc1xuXHQvLyBSZW1vdmVzIHRoZSBnaXZlbiBsYXllciBmcm9tIHRoZSBtYXAuXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgaWQgPSBMLnN0YW1wKGxheWVyKTtcblxuXHRcdGlmICghdGhpcy5fbGF5ZXJzW2lkXSkgeyByZXR1cm4gdGhpczsgfVxuXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCkge1xuXHRcdFx0bGF5ZXIub25SZW1vdmUodGhpcyk7XG5cdFx0fVxuXG5cdFx0aWYgKGxheWVyLmdldEF0dHJpYnV0aW9uICYmIHRoaXMuYXR0cmlidXRpb25Db250cm9sKSB7XG5cdFx0XHR0aGlzLmF0dHJpYnV0aW9uQ29udHJvbC5yZW1vdmVBdHRyaWJ1dGlvbihsYXllci5nZXRBdHRyaWJ1dGlvbigpKTtcblx0XHR9XG5cblx0XHRkZWxldGUgdGhpcy5fbGF5ZXJzW2lkXTtcblxuXHRcdGlmICh0aGlzLl9sb2FkZWQpIHtcblx0XHRcdHRoaXMuZmlyZSgnbGF5ZXJyZW1vdmUnLCB7bGF5ZXI6IGxheWVyfSk7XG5cdFx0XHRsYXllci5maXJlKCdyZW1vdmUnKTtcblx0XHR9XG5cblx0XHRsYXllci5fbWFwID0gbGF5ZXIuX21hcFRvQWRkID0gbnVsbDtcblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgaGFzTGF5ZXIobGF5ZXI6IExheWVyKTogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZ2l2ZW4gbGF5ZXIgaXMgY3VycmVudGx5IGFkZGVkIHRvIHRoZSBtYXBcblx0aGFzTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHJldHVybiAhIWxheWVyICYmIChMLnN0YW1wKGxheWVyKSBpbiB0aGlzLl9sYXllcnMpO1xuXHR9LFxuXG5cdC8qIEBtZXRob2QgZWFjaExheWVyKGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcblx0ICogSXRlcmF0ZXMgb3ZlciB0aGUgbGF5ZXJzIG9mIHRoZSBtYXAsIG9wdGlvbmFsbHkgc3BlY2lmeWluZyBjb250ZXh0IG9mIHRoZSBpdGVyYXRvciBmdW5jdGlvbi5cblx0ICogYGBgXG5cdCAqIG1hcC5lYWNoTGF5ZXIoZnVuY3Rpb24obGF5ZXIpe1xuXHQgKiAgICAgbGF5ZXIuYmluZFBvcHVwKCdIZWxsbycpO1xuXHQgKiB9KTtcblx0ICogYGBgXG5cdCAqL1xuXHRlYWNoTGF5ZXI6IGZ1bmN0aW9uIChtZXRob2QsIGNvbnRleHQpIHtcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0bWV0aG9kLmNhbGwoY29udGV4dCwgdGhpcy5fbGF5ZXJzW2ldKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X2FkZExheWVyczogZnVuY3Rpb24gKGxheWVycykge1xuXHRcdGxheWVycyA9IGxheWVycyA/IChMLlV0aWwuaXNBcnJheShsYXllcnMpID8gbGF5ZXJzIDogW2xheWVyc10pIDogW107XG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF5ZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHR0aGlzLmFkZExheWVyKGxheWVyc1tpXSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9hZGRab29tTGltaXQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdGlmIChpc05hTihsYXllci5vcHRpb25zLm1heFpvb20pIHx8ICFpc05hTihsYXllci5vcHRpb25zLm1pblpvb20pKSB7XG5cdFx0XHR0aGlzLl96b29tQm91bmRMYXllcnNbTC5zdGFtcChsYXllcildID0gbGF5ZXI7XG5cdFx0XHR0aGlzLl91cGRhdGVab29tTGV2ZWxzKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZW1vdmVab29tTGltaXQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBpZCA9IEwuc3RhbXAobGF5ZXIpO1xuXG5cdFx0aWYgKHRoaXMuX3pvb21Cb3VuZExheWVyc1tpZF0pIHtcblx0XHRcdGRlbGV0ZSB0aGlzLl96b29tQm91bmRMYXllcnNbaWRdO1xuXHRcdFx0dGhpcy5fdXBkYXRlWm9vbUxldmVscygpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlWm9vbUxldmVsczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBtaW5ab29tID0gSW5maW5pdHksXG5cdFx0ICAgIG1heFpvb20gPSAtSW5maW5pdHksXG5cdFx0ICAgIG9sZFpvb21TcGFuID0gdGhpcy5fZ2V0Wm9vbVNwYW4oKTtcblxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fem9vbUJvdW5kTGF5ZXJzKSB7XG5cdFx0XHR2YXIgb3B0aW9ucyA9IHRoaXMuX3pvb21Cb3VuZExheWVyc1tpXS5vcHRpb25zO1xuXG5cdFx0XHRtaW5ab29tID0gb3B0aW9ucy5taW5ab29tID09PSB1bmRlZmluZWQgPyBtaW5ab29tIDogTWF0aC5taW4obWluWm9vbSwgb3B0aW9ucy5taW5ab29tKTtcblx0XHRcdG1heFpvb20gPSBvcHRpb25zLm1heFpvb20gPT09IHVuZGVmaW5lZCA/IG1heFpvb20gOiBNYXRoLm1heChtYXhab29tLCBvcHRpb25zLm1heFpvb20pO1xuXHRcdH1cblxuXHRcdHRoaXMuX2xheWVyc01heFpvb20gPSBtYXhab29tID09PSAtSW5maW5pdHkgPyB1bmRlZmluZWQgOiBtYXhab29tO1xuXHRcdHRoaXMuX2xheWVyc01pblpvb20gPSBtaW5ab29tID09PSBJbmZpbml0eSA/IHVuZGVmaW5lZCA6IG1pblpvb207XG5cblx0XHQvLyBAc2VjdGlvbiBNYXAgc3RhdGUgY2hhbmdlIGV2ZW50c1xuXHRcdC8vIEBldmVudCB6b29tbGV2ZWxzY2hhbmdlOiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG51bWJlciBvZiB6b29tbGV2ZWxzIG9uIHRoZSBtYXAgaXMgY2hhbmdlZCBkdWVcblx0XHQvLyB0byBhZGRpbmcgb3IgcmVtb3ZpbmcgYSBsYXllci5cblx0XHRpZiAob2xkWm9vbVNwYW4gIT09IHRoaXMuX2dldFpvb21TcGFuKCkpIHtcblx0XHRcdHRoaXMuZmlyZSgnem9vbWxldmVsc2NoYW5nZScpO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLm9wdGlvbnMubWF4Wm9vbSA9PT0gdW5kZWZpbmVkICYmIHRoaXMuX2xheWVyc01heFpvb20gJiYgdGhpcy5nZXRab29tKCkgPiB0aGlzLl9sYXllcnNNYXhab29tKSB7XG5cdFx0XHR0aGlzLnNldFpvb20odGhpcy5fbGF5ZXJzTWF4Wm9vbSk7XG5cdFx0fVxuXHRcdGlmICh0aGlzLm9wdGlvbnMubWluWm9vbSA9PT0gdW5kZWZpbmVkICYmIHRoaXMuX2xheWVyc01pblpvb20gJiYgdGhpcy5nZXRab29tKCkgPCB0aGlzLl9sYXllcnNNaW5ab29tKSB7XG5cdFx0XHR0aGlzLnNldFpvb20odGhpcy5fbGF5ZXJzTWluWm9vbSk7XG5cdFx0fVxuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgRG9tRXZlbnRcclxuICogVXRpbGl0eSBmdW5jdGlvbnMgdG8gd29yayB3aXRoIHRoZSBbRE9NIGV2ZW50c10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0V2ZW50KSwgdXNlZCBieSBMZWFmbGV0IGludGVybmFsbHkuXHJcbiAqL1xyXG5cclxuLy8gSW5zcGlyZWQgYnkgSm9obiBSZXNpZywgRGVhbiBFZHdhcmRzIGFuZCBZVUkgYWRkRXZlbnQgaW1wbGVtZW50YXRpb25zLlxyXG5cclxuXHJcblxyXG52YXIgZXZlbnRzS2V5ID0gJ19sZWFmbGV0X2V2ZW50cyc7XHJcblxyXG5MLkRvbUV2ZW50ID0ge1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gb24oZWw6IEhUTUxFbGVtZW50LCB0eXBlczogU3RyaW5nLCBmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIChgZm5gKSB0byBhIHBhcnRpY3VsYXIgRE9NIGV2ZW50IHR5cGUgb2YgdGhlXHJcblx0Ly8gZWxlbWVudCBgZWxgLiBZb3UgY2FuIG9wdGlvbmFsbHkgc3BlY2lmeSB0aGUgY29udGV4dCBvZiB0aGUgbGlzdGVuZXJcclxuXHQvLyAob2JqZWN0IHRoZSBgdGhpc2Aga2V5d29yZCB3aWxsIHBvaW50IHRvKS4gWW91IGNhbiBhbHNvIHBhc3Mgc2V2ZXJhbFxyXG5cdC8vIHNwYWNlLXNlcGFyYXRlZCB0eXBlcyAoZS5nLiBgJ2NsaWNrIGRibGNsaWNrJ2ApLlxyXG5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAZnVuY3Rpb24gb24oZWw6IEhUTUxFbGVtZW50LCBldmVudE1hcDogT2JqZWN0LCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xyXG5cdC8vIEFkZHMgYSBzZXQgb2YgdHlwZS9saXN0ZW5lciBwYWlycywgZS5nLiBge2NsaWNrOiBvbkNsaWNrLCBtb3VzZW1vdmU6IG9uTW91c2VNb3ZlfWBcclxuXHRvbjogZnVuY3Rpb24gKG9iaiwgdHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdHRoaXMuX29uKG9iaiwgdHlwZSwgdHlwZXNbdHlwZV0sIGZuKTtcclxuXHRcdFx0fVxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0dHlwZXMgPSBMLlV0aWwuc3BsaXRXb3Jkcyh0eXBlcyk7XHJcblxyXG5cdFx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gdHlwZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHR0aGlzLl9vbihvYmosIHR5cGVzW2ldLCBmbiwgY29udGV4dCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gb2ZmKGVsOiBIVE1MRWxlbWVudCwgdHlwZXM6IFN0cmluZywgZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYSBwcmV2aW91c2x5IGFkZGVkIGxpc3RlbmVyIGZ1bmN0aW9uLiBJZiBubyBmdW5jdGlvbiBpcyBzcGVjaWZpZWQsXHJcblx0Ly8gaXQgd2lsbCByZW1vdmUgYWxsIHRoZSBsaXN0ZW5lcnMgb2YgdGhhdCBwYXJ0aWN1bGFyIERPTSBldmVudCBmcm9tIHRoZSBlbGVtZW50LlxyXG5cdC8vIE5vdGUgdGhhdCBpZiB5b3UgcGFzc2VkIGEgY3VzdG9tIGNvbnRleHQgdG8gb24sIHlvdSBtdXN0IHBhc3MgdGhlIHNhbWVcclxuXHQvLyBjb250ZXh0IHRvIGBvZmZgIGluIG9yZGVyIHRvIHJlbW92ZSB0aGUgbGlzdGVuZXIuXHJcblxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBmdW5jdGlvbiBvZmYoZWw6IEhUTUxFbGVtZW50LCBldmVudE1hcDogT2JqZWN0LCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYSBzZXQgb2YgdHlwZS9saXN0ZW5lciBwYWlycywgZS5nLiBge2NsaWNrOiBvbkNsaWNrLCBtb3VzZW1vdmU6IG9uTW91c2VNb3ZlfWBcclxuXHRvZmY6IGZ1bmN0aW9uIChvYmosIHR5cGVzLCBmbiwgY29udGV4dCkge1xyXG5cclxuXHRcdGlmICh0eXBlb2YgdHlwZXMgPT09ICdvYmplY3QnKSB7XHJcblx0XHRcdGZvciAodmFyIHR5cGUgaW4gdHlwZXMpIHtcclxuXHRcdFx0XHR0aGlzLl9vZmYob2JqLCB0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0eXBlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0eXBlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuX29mZihvYmosIHR5cGVzW2ldLCBmbiwgY29udGV4dCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfb246IGZ1bmN0aW9uIChvYmosIHR5cGUsIGZuLCBjb250ZXh0KSB7XHJcblx0XHR2YXIgaWQgPSB0eXBlICsgTC5zdGFtcChmbikgKyAoY29udGV4dCA/ICdfJyArIEwuc3RhbXAoY29udGV4dCkgOiAnJyk7XHJcblxyXG5cdFx0aWYgKG9ialtldmVudHNLZXldICYmIG9ialtldmVudHNLZXldW2lkXSkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdHZhciBoYW5kbGVyID0gZnVuY3Rpb24gKGUpIHtcclxuXHRcdFx0cmV0dXJuIGZuLmNhbGwoY29udGV4dCB8fCBvYmosIGUgfHwgd2luZG93LmV2ZW50KTtcclxuXHRcdH07XHJcblxyXG5cdFx0dmFyIG9yaWdpbmFsSGFuZGxlciA9IGhhbmRsZXI7XHJcblxyXG5cdFx0aWYgKEwuQnJvd3Nlci5wb2ludGVyICYmIHR5cGUuaW5kZXhPZigndG91Y2gnKSA9PT0gMCkge1xyXG5cdFx0XHR0aGlzLmFkZFBvaW50ZXJMaXN0ZW5lcihvYmosIHR5cGUsIGhhbmRsZXIsIGlkKTtcclxuXHJcblx0XHR9IGVsc2UgaWYgKEwuQnJvd3Nlci50b3VjaCAmJiAodHlwZSA9PT0gJ2RibGNsaWNrJykgJiYgdGhpcy5hZGREb3VibGVUYXBMaXN0ZW5lcikge1xyXG5cdFx0XHR0aGlzLmFkZERvdWJsZVRhcExpc3RlbmVyKG9iaiwgaGFuZGxlciwgaWQpO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoJ2FkZEV2ZW50TGlzdGVuZXInIGluIG9iaikge1xyXG5cclxuXHRcdFx0aWYgKHR5cGUgPT09ICdtb3VzZXdoZWVsJykge1xyXG5cdFx0XHRcdG9iai5hZGRFdmVudExpc3RlbmVyKCdvbndoZWVsJyBpbiBvYmogPyAnd2hlZWwnIDogJ21vdXNld2hlZWwnLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdFx0XHR9IGVsc2UgaWYgKCh0eXBlID09PSAnbW91c2VlbnRlcicpIHx8ICh0eXBlID09PSAnbW91c2VsZWF2ZScpKSB7XHJcblx0XHRcdFx0aGFuZGxlciA9IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRcdFx0XHRlID0gZSB8fCB3aW5kb3cuZXZlbnQ7XHJcblx0XHRcdFx0XHRpZiAoTC5Eb21FdmVudC5faXNFeHRlcm5hbFRhcmdldChvYmosIGUpKSB7XHJcblx0XHRcdFx0XHRcdG9yaWdpbmFsSGFuZGxlcihlKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9O1xyXG5cdFx0XHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHR5cGUgPT09ICdtb3VzZWVudGVyJyA/ICdtb3VzZW92ZXInIDogJ21vdXNlb3V0JywgaGFuZGxlciwgZmFsc2UpO1xyXG5cclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRpZiAodHlwZSA9PT0gJ2NsaWNrJyAmJiBMLkJyb3dzZXIuYW5kcm9pZCkge1xyXG5cdFx0XHRcdFx0aGFuZGxlciA9IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRcdFx0XHRcdHJldHVybiBMLkRvbUV2ZW50Ll9maWx0ZXJDbGljayhlLCBvcmlnaW5hbEhhbmRsZXIpO1xyXG5cdFx0XHRcdFx0fTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodHlwZSwgaGFuZGxlciwgZmFsc2UpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0fSBlbHNlIGlmICgnYXR0YWNoRXZlbnQnIGluIG9iaikge1xyXG5cdFx0XHRvYmouYXR0YWNoRXZlbnQoJ29uJyArIHR5cGUsIGhhbmRsZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdG9ialtldmVudHNLZXldID0gb2JqW2V2ZW50c0tleV0gfHwge307XHJcblx0XHRvYmpbZXZlbnRzS2V5XVtpZF0gPSBoYW5kbGVyO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9vZmY6IGZ1bmN0aW9uIChvYmosIHR5cGUsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0dmFyIGlkID0gdHlwZSArIEwuc3RhbXAoZm4pICsgKGNvbnRleHQgPyAnXycgKyBMLnN0YW1wKGNvbnRleHQpIDogJycpLFxyXG5cdFx0ICAgIGhhbmRsZXIgPSBvYmpbZXZlbnRzS2V5XSAmJiBvYmpbZXZlbnRzS2V5XVtpZF07XHJcblxyXG5cdFx0aWYgKCFoYW5kbGVyKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0aWYgKEwuQnJvd3Nlci5wb2ludGVyICYmIHR5cGUuaW5kZXhPZigndG91Y2gnKSA9PT0gMCkge1xyXG5cdFx0XHR0aGlzLnJlbW92ZVBvaW50ZXJMaXN0ZW5lcihvYmosIHR5cGUsIGlkKTtcclxuXHJcblx0XHR9IGVsc2UgaWYgKEwuQnJvd3Nlci50b3VjaCAmJiAodHlwZSA9PT0gJ2RibGNsaWNrJykgJiYgdGhpcy5yZW1vdmVEb3VibGVUYXBMaXN0ZW5lcikge1xyXG5cdFx0XHR0aGlzLnJlbW92ZURvdWJsZVRhcExpc3RlbmVyKG9iaiwgaWQpO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoJ3JlbW92ZUV2ZW50TGlzdGVuZXInIGluIG9iaikge1xyXG5cclxuXHRcdFx0aWYgKHR5cGUgPT09ICdtb3VzZXdoZWVsJykge1xyXG5cdFx0XHRcdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKCdvbndoZWVsJyBpbiBvYmogPyAnd2hlZWwnIDogJ21vdXNld2hlZWwnLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKFxyXG5cdFx0XHRcdFx0dHlwZSA9PT0gJ21vdXNlZW50ZXInID8gJ21vdXNlb3ZlcicgOlxyXG5cdFx0XHRcdFx0dHlwZSA9PT0gJ21vdXNlbGVhdmUnID8gJ21vdXNlb3V0JyA6IHR5cGUsIGhhbmRsZXIsIGZhbHNlKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdH0gZWxzZSBpZiAoJ2RldGFjaEV2ZW50JyBpbiBvYmopIHtcclxuXHRcdFx0b2JqLmRldGFjaEV2ZW50KCdvbicgKyB0eXBlLCBoYW5kbGVyKTtcclxuXHRcdH1cclxuXHJcblx0XHRvYmpbZXZlbnRzS2V5XVtpZF0gPSBudWxsO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzdG9wUHJvcGFnYXRpb24oZXY6IERPTUV2ZW50KTogdGhpc1xyXG5cdC8vIFN0b3AgdGhlIGdpdmVuIGV2ZW50IGZyb20gcHJvcGFnYXRpb24gdG8gcGFyZW50IGVsZW1lbnRzLiBVc2VkIGluc2lkZSB0aGUgbGlzdGVuZXIgZnVuY3Rpb25zOlxyXG5cdC8vIGBgYGpzXHJcblx0Ly8gTC5Eb21FdmVudC5vbihkaXYsICdjbGljaycsIGZ1bmN0aW9uIChldikge1xyXG5cdC8vIFx0TC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24oZXYpO1xyXG5cdC8vIH0pO1xyXG5cdC8vIGBgYFxyXG5cdHN0b3BQcm9wYWdhdGlvbjogZnVuY3Rpb24gKGUpIHtcclxuXHJcblx0XHRpZiAoZS5zdG9wUHJvcGFnYXRpb24pIHtcclxuXHRcdFx0ZS5zdG9wUHJvcGFnYXRpb24oKTtcclxuXHRcdH0gZWxzZSBpZiAoZS5vcmlnaW5hbEV2ZW50KSB7ICAvLyBJbiBjYXNlIG9mIExlYWZsZXQgZXZlbnQuXHJcblx0XHRcdGUub3JpZ2luYWxFdmVudC5fc3RvcHBlZCA9IHRydWU7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRlLmNhbmNlbEJ1YmJsZSA9IHRydWU7XHJcblx0XHR9XHJcblx0XHRMLkRvbUV2ZW50Ll9za2lwcGVkKGUpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBkaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb24oZWw6IEhUTUxFbGVtZW50KTogdGhpc1xyXG5cdC8vIEFkZHMgYHN0b3BQcm9wYWdhdGlvbmAgdG8gdGhlIGVsZW1lbnQncyBgJ21vdXNld2hlZWwnYCBldmVudHMgKHBsdXMgYnJvd3NlciB2YXJpYW50cykuXHJcblx0ZGlzYWJsZVNjcm9sbFByb3BhZ2F0aW9uOiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdHJldHVybiBMLkRvbUV2ZW50Lm9uKGVsLCAnbW91c2V3aGVlbCcsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZGlzYWJsZUNsaWNrUHJvcGFnYXRpb24oZWw6IEhUTUxFbGVtZW50KTogdGhpc1xyXG5cdC8vIEFkZHMgYHN0b3BQcm9wYWdhdGlvbmAgdG8gdGhlIGVsZW1lbnQncyBgJ2NsaWNrJ2AsIGAnZG91YmxlY2xpY2snYCxcclxuXHQvLyBgJ21vdXNlZG93bidgIGFuZCBgJ3RvdWNoc3RhcnQnYCBldmVudHMgKHBsdXMgYnJvd3NlciB2YXJpYW50cykuXHJcblx0ZGlzYWJsZUNsaWNrUHJvcGFnYXRpb246IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0dmFyIHN0b3AgPSBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbjtcclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9uKGVsLCBMLkRyYWdnYWJsZS5TVEFSVC5qb2luKCcgJyksIHN0b3ApO1xyXG5cclxuXHRcdHJldHVybiBMLkRvbUV2ZW50Lm9uKGVsLCB7XHJcblx0XHRcdGNsaWNrOiBMLkRvbUV2ZW50Ll9mYWtlU3RvcCxcclxuXHRcdFx0ZGJsY2xpY2s6IHN0b3BcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBwcmV2ZW50RGVmYXVsdChldjogRE9NRXZlbnQpOiB0aGlzXHJcblx0Ly8gUHJldmVudHMgdGhlIGRlZmF1bHQgYWN0aW9uIG9mIHRoZSBET00gRXZlbnQgYGV2YCBmcm9tIGhhcHBlbmluZyAoc3VjaCBhc1xyXG5cdC8vIGZvbGxvd2luZyBhIGxpbmsgaW4gdGhlIGhyZWYgb2YgdGhlIGEgZWxlbWVudCwgb3IgZG9pbmcgYSBQT1NUIHJlcXVlc3RcclxuXHQvLyB3aXRoIHBhZ2UgcmVsb2FkIHdoZW4gYSBgPGZvcm0+YCBpcyBzdWJtaXR0ZWQpLlxyXG5cdC8vIFVzZSBpdCBpbnNpZGUgbGlzdGVuZXIgZnVuY3Rpb25zLlxyXG5cdHByZXZlbnREZWZhdWx0OiBmdW5jdGlvbiAoZSkge1xyXG5cclxuXHRcdGlmIChlLnByZXZlbnREZWZhdWx0KSB7XHJcblx0XHRcdGUucHJldmVudERlZmF1bHQoKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGUucmV0dXJuVmFsdWUgPSBmYWxzZTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzdG9wKGV2KTogdGhpc1xyXG5cdC8vIERvZXMgYHN0b3BQcm9wYWdhdGlvbmAgYW5kIGBwcmV2ZW50RGVmYXVsdGAgYXQgdGhlIHNhbWUgdGltZS5cclxuXHRzdG9wOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnRcclxuXHRcdFx0LnByZXZlbnREZWZhdWx0KGUpXHJcblx0XHRcdC5zdG9wUHJvcGFnYXRpb24oZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldE1vdXNlUG9zaXRpb24oZXY6IERPTUV2ZW50LCBjb250YWluZXI/OiBIVE1MRWxlbWVudCk6IFBvaW50XHJcblx0Ly8gR2V0cyBub3JtYWxpemVkIG1vdXNlIHBvc2l0aW9uIGZyb20gYSBET00gZXZlbnQgcmVsYXRpdmUgdG8gdGhlXHJcblx0Ly8gYGNvbnRhaW5lcmAgb3IgdG8gdGhlIHdob2xlIHBhZ2UgaWYgbm90IHNwZWNpZmllZC5cclxuXHRnZXRNb3VzZVBvc2l0aW9uOiBmdW5jdGlvbiAoZSwgY29udGFpbmVyKSB7XHJcblx0XHRpZiAoIWNvbnRhaW5lcikge1xyXG5cdFx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoZS5jbGllbnRYLCBlLmNsaWVudFkpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciByZWN0ID0gY29udGFpbmVyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludChcclxuXHRcdFx0ZS5jbGllbnRYIC0gcmVjdC5sZWZ0IC0gY29udGFpbmVyLmNsaWVudExlZnQsXHJcblx0XHRcdGUuY2xpZW50WSAtIHJlY3QudG9wIC0gY29udGFpbmVyLmNsaWVudFRvcCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQ2hyb21lIG9uIFdpbiBzY3JvbGxzIGRvdWJsZSB0aGUgcGl4ZWxzIGFzIGluIG90aGVyIHBsYXRmb3JtcyAoc2VlICM0NTM4KSxcclxuXHQvLyBhbmQgRmlyZWZveCBzY3JvbGxzIGRldmljZSBwaXhlbHMsIG5vdCBDU1MgcGl4ZWxzXHJcblx0X3doZWVsUHhGYWN0b3I6IChMLkJyb3dzZXIud2luICYmIEwuQnJvd3Nlci5jaHJvbWUpID8gMiA6XHJcblx0ICAgICAgICAgICAgICAgIEwuQnJvd3Nlci5nZWNrbyA/IHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvIDpcclxuXHQgICAgICAgICAgICAgICAgMSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldFdoZWVsRGVsdGEoZXY6IERPTUV2ZW50KTogTnVtYmVyXHJcblx0Ly8gR2V0cyBub3JtYWxpemVkIHdoZWVsIGRlbHRhIGZyb20gYSBtb3VzZXdoZWVsIERPTSBldmVudCwgaW4gdmVydGljYWxcclxuXHQvLyBwaXhlbHMgc2Nyb2xsZWQgKG5lZ2F0aXZlIGlmIHNjcm9sbGluZyBkb3duKS5cclxuXHQvLyBFdmVudHMgZnJvbSBwb2ludGluZyBkZXZpY2VzIHdpdGhvdXQgcHJlY2lzZSBzY3JvbGxpbmcgYXJlIG1hcHBlZCB0b1xyXG5cdC8vIGEgYmVzdCBndWVzcyBvZiA2MCBwaXhlbHMuXHJcblx0Z2V0V2hlZWxEZWx0YTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHJldHVybiAoTC5Ccm93c2VyLmVkZ2UpID8gZS53aGVlbERlbHRhWSAvIDIgOiAvLyBEb24ndCB0cnVzdCB3aW5kb3ctZ2VvbWV0cnktYmFzZWQgZGVsdGFcclxuXHRcdCAgICAgICAoZS5kZWx0YVkgJiYgZS5kZWx0YU1vZGUgPT09IDApID8gLWUuZGVsdGFZIC8gTC5Eb21FdmVudC5fd2hlZWxQeEZhY3RvciA6IC8vIFBpeGVsc1xyXG5cdFx0ICAgICAgIChlLmRlbHRhWSAmJiBlLmRlbHRhTW9kZSA9PT0gMSkgPyAtZS5kZWx0YVkgKiAyMCA6IC8vIExpbmVzXHJcblx0XHQgICAgICAgKGUuZGVsdGFZICYmIGUuZGVsdGFNb2RlID09PSAyKSA/IC1lLmRlbHRhWSAqIDYwIDogLy8gUGFnZXNcclxuXHRcdCAgICAgICAoZS5kZWx0YVggfHwgZS5kZWx0YVopID8gMCA6XHQvLyBTa2lwIGhvcml6b250YWwvZGVwdGggd2hlZWwgZXZlbnRzXHJcblx0XHQgICAgICAgZS53aGVlbERlbHRhID8gKGUud2hlZWxEZWx0YVkgfHwgZS53aGVlbERlbHRhKSAvIDIgOiAvLyBMZWdhY3kgSUUgcGl4ZWxzXHJcblx0XHQgICAgICAgKGUuZGV0YWlsICYmIE1hdGguYWJzKGUuZGV0YWlsKSA8IDMyNzY1KSA/IC1lLmRldGFpbCAqIDIwIDogLy8gTGVnYWN5IE1veiBsaW5lc1xyXG5cdFx0ICAgICAgIGUuZGV0YWlsID8gZS5kZXRhaWwgLyAtMzI3NjUgKiA2MCA6IC8vIExlZ2FjeSBNb3ogcGFnZXNcclxuXHRcdCAgICAgICAwO1xyXG5cdH0sXHJcblxyXG5cdF9za2lwRXZlbnRzOiB7fSxcclxuXHJcblx0X2Zha2VTdG9wOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gZmFrZXMgc3RvcFByb3BhZ2F0aW9uIGJ5IHNldHRpbmcgYSBzcGVjaWFsIGV2ZW50IGZsYWcsIGNoZWNrZWQvcmVzZXQgd2l0aCBMLkRvbUV2ZW50Ll9za2lwcGVkKGUpXHJcblx0XHRMLkRvbUV2ZW50Ll9za2lwRXZlbnRzW2UudHlwZV0gPSB0cnVlO1xyXG5cdH0sXHJcblxyXG5cdF9za2lwcGVkOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dmFyIHNraXBwZWQgPSB0aGlzLl9za2lwRXZlbnRzW2UudHlwZV07XHJcblx0XHQvLyByZXNldCB3aGVuIGNoZWNraW5nLCBhcyBpdCdzIG9ubHkgdXNlZCBpbiBtYXAgY29udGFpbmVyIGFuZCBwcm9wYWdhdGVzIG91dHNpZGUgb2YgdGhlIG1hcFxyXG5cdFx0dGhpcy5fc2tpcEV2ZW50c1tlLnR5cGVdID0gZmFsc2U7XHJcblx0XHRyZXR1cm4gc2tpcHBlZDtcclxuXHR9LFxyXG5cclxuXHQvLyBjaGVjayBpZiBlbGVtZW50IHJlYWxseSBsZWZ0L2VudGVyZWQgdGhlIGV2ZW50IHRhcmdldCAoZm9yIG1vdXNlZW50ZXIvbW91c2VsZWF2ZSlcclxuXHRfaXNFeHRlcm5hbFRhcmdldDogZnVuY3Rpb24gKGVsLCBlKSB7XHJcblxyXG5cdFx0dmFyIHJlbGF0ZWQgPSBlLnJlbGF0ZWRUYXJnZXQ7XHJcblxyXG5cdFx0aWYgKCFyZWxhdGVkKSB7IHJldHVybiB0cnVlOyB9XHJcblxyXG5cdFx0dHJ5IHtcclxuXHRcdFx0d2hpbGUgKHJlbGF0ZWQgJiYgKHJlbGF0ZWQgIT09IGVsKSkge1xyXG5cdFx0XHRcdHJlbGF0ZWQgPSByZWxhdGVkLnBhcmVudE5vZGU7XHJcblx0XHRcdH1cclxuXHRcdH0gY2F0Y2ggKGVycikge1xyXG5cdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gKHJlbGF0ZWQgIT09IGVsKTtcclxuXHR9LFxyXG5cclxuXHQvLyB0aGlzIGlzIGEgaG9ycmlibGUgd29ya2Fyb3VuZCBmb3IgYSBidWcgaW4gQW5kcm9pZCB3aGVyZSBhIHNpbmdsZSB0b3VjaCB0cmlnZ2VycyB0d28gY2xpY2sgZXZlbnRzXHJcblx0X2ZpbHRlckNsaWNrOiBmdW5jdGlvbiAoZSwgaGFuZGxlcikge1xyXG5cdFx0dmFyIHRpbWVTdGFtcCA9IChlLnRpbWVTdGFtcCB8fCAoZS5vcmlnaW5hbEV2ZW50ICYmIGUub3JpZ2luYWxFdmVudC50aW1lU3RhbXApKSxcclxuXHRcdCAgICBlbGFwc2VkID0gTC5Eb21FdmVudC5fbGFzdENsaWNrICYmICh0aW1lU3RhbXAgLSBMLkRvbUV2ZW50Ll9sYXN0Q2xpY2spO1xyXG5cclxuXHRcdC8vIGFyZSB0aGV5IGNsb3NlciB0b2dldGhlciB0aGFuIDUwMG1zIHlldCBtb3JlIHRoYW4gMTAwbXM/XHJcblx0XHQvLyBBbmRyb2lkIHR5cGljYWxseSB0cmlnZ2VycyB0aGVtIH4zMDBtcyBhcGFydCB3aGlsZSBtdWx0aXBsZSBsaXN0ZW5lcnNcclxuXHRcdC8vIG9uIHRoZSBzYW1lIGV2ZW50IHNob3VsZCBiZSB0cmlnZ2VyZWQgZmFyIGZhc3RlcjtcclxuXHRcdC8vIG9yIGNoZWNrIGlmIGNsaWNrIGlzIHNpbXVsYXRlZCBvbiB0aGUgZWxlbWVudCwgYW5kIGlmIGl0IGlzLCByZWplY3QgYW55IG5vbi1zaW11bGF0ZWQgZXZlbnRzXHJcblxyXG5cdFx0aWYgKChlbGFwc2VkICYmIGVsYXBzZWQgPiAxMDAgJiYgZWxhcHNlZCA8IDUwMCkgfHwgKGUudGFyZ2V0Ll9zaW11bGF0ZWRDbGljayAmJiAhZS5fc2ltdWxhdGVkKSkge1xyXG5cdFx0XHRMLkRvbUV2ZW50LnN0b3AoZSk7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHRcdEwuRG9tRXZlbnQuX2xhc3RDbGljayA9IHRpbWVTdGFtcDtcclxuXHJcblx0XHRoYW5kbGVyKGUpO1xyXG5cdH1cclxufTtcclxuXHJcbi8vIEBmdW5jdGlvbiBhZGRMaXN0ZW5lcijigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgTC5Eb21FdmVudC5vbmBdKCNkb21ldmVudC1vbilcclxuTC5Eb21FdmVudC5hZGRMaXN0ZW5lciA9IEwuRG9tRXZlbnQub247XHJcblxyXG4vLyBAZnVuY3Rpb24gcmVtb3ZlTGlzdGVuZXIo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYEwuRG9tRXZlbnQub2ZmYF0oI2RvbWV2ZW50LW9mZilcclxuTC5Eb21FdmVudC5yZW1vdmVMaXN0ZW5lciA9IEwuRG9tRXZlbnQub2ZmO1xyXG5cblxuXG4vKlxuICogQGNsYXNzIFBvc0FuaW1hdGlvblxuICogQGFrYSBMLlBvc0FuaW1hdGlvblxuICogQGluaGVyaXRzIEV2ZW50ZWRcbiAqIFVzZWQgaW50ZXJuYWxseSBmb3IgcGFubmluZyBhbmltYXRpb25zLCB1dGlsaXppbmcgQ1NTMyBUcmFuc2l0aW9ucyBmb3IgbW9kZXJuIGJyb3dzZXJzIGFuZCBhIHRpbWVyIGZhbGxiYWNrIGZvciBJRTYtOS5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBganNcbiAqIHZhciBmeCA9IG5ldyBMLlBvc0FuaW1hdGlvbigpO1xuICogZngucnVuKGVsLCBbMzAwLCA1MDBdLCAwLjUpO1xuICogYGBgXG4gKlxuICogQGNvbnN0cnVjdG9yIEwuUG9zQW5pbWF0aW9uKClcbiAqIENyZWF0ZXMgYSBgUG9zQW5pbWF0aW9uYCBvYmplY3QuXG4gKlxuICovXG5cbkwuUG9zQW5pbWF0aW9uID0gTC5FdmVudGVkLmV4dGVuZCh7XG5cblx0Ly8gQG1ldGhvZCBydW4oZWw6IEhUTUxFbGVtZW50LCBuZXdQb3M6IFBvaW50LCBkdXJhdGlvbj86IE51bWJlciwgZWFzZUxpbmVhcml0eT86IE51bWJlcilcblx0Ly8gUnVuIGFuIGFuaW1hdGlvbiBvZiBhIGdpdmVuIGVsZW1lbnQgdG8gYSBuZXcgcG9zaXRpb24sIG9wdGlvbmFsbHkgc2V0dGluZ1xuXHQvLyBkdXJhdGlvbiBpbiBzZWNvbmRzIChgMC4yNWAgYnkgZGVmYXVsdCkgYW5kIGVhc2luZyBsaW5lYXJpdHkgZmFjdG9yICgzcmRcblx0Ly8gYXJndW1lbnQgb2YgdGhlIFtjdWJpYyBiZXppZXIgY3VydmVdKGh0dHA6Ly9jdWJpYy1iZXppZXIuY29tLyMwLDAsLjUsMSksXG5cdC8vIGAwLjVgIGJ5IGRlZmF1bHQpLlxuXHRydW46IGZ1bmN0aW9uIChlbCwgbmV3UG9zLCBkdXJhdGlvbiwgZWFzZUxpbmVhcml0eSkge1xuXHRcdHRoaXMuc3RvcCgpO1xuXG5cdFx0dGhpcy5fZWwgPSBlbDtcblx0XHR0aGlzLl9pblByb2dyZXNzID0gdHJ1ZTtcblx0XHR0aGlzLl9kdXJhdGlvbiA9IGR1cmF0aW9uIHx8IDAuMjU7XG5cdFx0dGhpcy5fZWFzZU91dFBvd2VyID0gMSAvIE1hdGgubWF4KGVhc2VMaW5lYXJpdHkgfHwgMC41LCAwLjIpO1xuXG5cdFx0dGhpcy5fc3RhcnRQb3MgPSBMLkRvbVV0aWwuZ2V0UG9zaXRpb24oZWwpO1xuXHRcdHRoaXMuX29mZnNldCA9IG5ld1Bvcy5zdWJ0cmFjdCh0aGlzLl9zdGFydFBvcyk7XG5cdFx0dGhpcy5fc3RhcnRUaW1lID0gK25ldyBEYXRlKCk7XG5cblx0XHQvLyBAZXZlbnQgc3RhcnQ6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYW5pbWF0aW9uIHN0YXJ0c1xuXHRcdHRoaXMuZmlyZSgnc3RhcnQnKTtcblxuXHRcdHRoaXMuX2FuaW1hdGUoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHN0b3AoKVxuXHQvLyBTdG9wcyB0aGUgYW5pbWF0aW9uIChpZiBjdXJyZW50bHkgcnVubmluZykuXG5cdHN0b3A6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX2luUHJvZ3Jlc3MpIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9zdGVwKHRydWUpO1xuXHRcdHRoaXMuX2NvbXBsZXRlKCk7XG5cdH0sXG5cblx0X2FuaW1hdGU6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBhbmltYXRpb24gbG9vcFxuXHRcdHRoaXMuX2FuaW1JZCA9IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX2FuaW1hdGUsIHRoaXMpO1xuXHRcdHRoaXMuX3N0ZXAoKTtcblx0fSxcblxuXHRfc3RlcDogZnVuY3Rpb24gKHJvdW5kKSB7XG5cdFx0dmFyIGVsYXBzZWQgPSAoK25ldyBEYXRlKCkpIC0gdGhpcy5fc3RhcnRUaW1lLFxuXHRcdCAgICBkdXJhdGlvbiA9IHRoaXMuX2R1cmF0aW9uICogMTAwMDtcblxuXHRcdGlmIChlbGFwc2VkIDwgZHVyYXRpb24pIHtcblx0XHRcdHRoaXMuX3J1bkZyYW1lKHRoaXMuX2Vhc2VPdXQoZWxhcHNlZCAvIGR1cmF0aW9uKSwgcm91bmQpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9ydW5GcmFtZSgxKTtcblx0XHRcdHRoaXMuX2NvbXBsZXRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9ydW5GcmFtZTogZnVuY3Rpb24gKHByb2dyZXNzLCByb3VuZCkge1xuXHRcdHZhciBwb3MgPSB0aGlzLl9zdGFydFBvcy5hZGQodGhpcy5fb2Zmc2V0Lm11bHRpcGx5QnkocHJvZ3Jlc3MpKTtcblx0XHRpZiAocm91bmQpIHtcblx0XHRcdHBvcy5fcm91bmQoKTtcblx0XHR9XG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2VsLCBwb3MpO1xuXG5cdFx0Ly8gQGV2ZW50IHN0ZXA6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgY29udGludW91c2x5IGR1cmluZyB0aGUgYW5pbWF0aW9uLlxuXHRcdHRoaXMuZmlyZSgnc3RlcCcpO1xuXHR9LFxuXG5cdF9jb21wbGV0ZTogZnVuY3Rpb24gKCkge1xuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fYW5pbUlkKTtcblxuXHRcdHRoaXMuX2luUHJvZ3Jlc3MgPSBmYWxzZTtcblx0XHQvLyBAZXZlbnQgZW5kOiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIGFuaW1hdGlvbiBlbmRzLlxuXHRcdHRoaXMuZmlyZSgnZW5kJyk7XG5cdH0sXG5cblx0X2Vhc2VPdXQ6IGZ1bmN0aW9uICh0KSB7XG5cdFx0cmV0dXJuIDEgLSBNYXRoLnBvdygxIC0gdCwgdGhpcy5fZWFzZU91dFBvd2VyKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIFByb2plY3Rpb25cclxuICogQHByb2plY3Rpb24gTC5Qcm9qZWN0aW9uLk1lcmNhdG9yXHJcbiAqXHJcbiAqIEVsbGlwdGljYWwgTWVyY2F0b3IgcHJvamVjdGlvbiDigJQgbW9yZSBjb21wbGV4IHRoYW4gU3BoZXJpY2FsIE1lcmNhdG9yLiBUYWtlcyBpbnRvIGFjY291bnQgdGhhdCBFYXJ0aCBpcyBhIGdlb2lkLCBub3QgYSBwZXJmZWN0IHNwaGVyZS4gVXNlZCBieSB0aGUgRVBTRzozMzk1IENSUy5cclxuICovXHJcblxyXG5MLlByb2plY3Rpb24uTWVyY2F0b3IgPSB7XHJcblx0UjogNjM3ODEzNyxcclxuXHRSX01JTk9SOiA2MzU2NzUyLjMxNDI0NTE3OSxcclxuXHJcblx0Ym91bmRzOiBMLmJvdW5kcyhbLTIwMDM3NTA4LjM0Mjc5LCAtMTU0OTY1NzAuNzM5NzJdLCBbMjAwMzc1MDguMzQyNzksIDE4NzY0NjU2LjIzMTM4XSksXHJcblxyXG5cdHByb2plY3Q6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHZhciBkID0gTWF0aC5QSSAvIDE4MCxcclxuXHRcdCAgICByID0gdGhpcy5SLFxyXG5cdFx0ICAgIHkgPSBsYXRsbmcubGF0ICogZCxcclxuXHRcdCAgICB0bXAgPSB0aGlzLlJfTUlOT1IgLyByLFxyXG5cdFx0ICAgIGUgPSBNYXRoLnNxcnQoMSAtIHRtcCAqIHRtcCksXHJcblx0XHQgICAgY29uID0gZSAqIE1hdGguc2luKHkpO1xyXG5cclxuXHRcdHZhciB0cyA9IE1hdGgudGFuKE1hdGguUEkgLyA0IC0geSAvIDIpIC8gTWF0aC5wb3coKDEgLSBjb24pIC8gKDEgKyBjb24pLCBlIC8gMik7XHJcblx0XHR5ID0gLXIgKiBNYXRoLmxvZyhNYXRoLm1heCh0cywgMUUtMTApKTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQobGF0bG5nLmxuZyAqIGQgKiByLCB5KTtcclxuXHR9LFxyXG5cclxuXHR1bnByb2plY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0dmFyIGQgPSAxODAgLyBNYXRoLlBJLFxyXG5cdFx0ICAgIHIgPSB0aGlzLlIsXHJcblx0XHQgICAgdG1wID0gdGhpcy5SX01JTk9SIC8gcixcclxuXHRcdCAgICBlID0gTWF0aC5zcXJ0KDEgLSB0bXAgKiB0bXApLFxyXG5cdFx0ICAgIHRzID0gTWF0aC5leHAoLXBvaW50LnkgLyByKSxcclxuXHRcdCAgICBwaGkgPSBNYXRoLlBJIC8gMiAtIDIgKiBNYXRoLmF0YW4odHMpO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwLCBkcGhpID0gMC4xLCBjb247IGkgPCAxNSAmJiBNYXRoLmFicyhkcGhpKSA+IDFlLTc7IGkrKykge1xyXG5cdFx0XHRjb24gPSBlICogTWF0aC5zaW4ocGhpKTtcclxuXHRcdFx0Y29uID0gTWF0aC5wb3coKDEgLSBjb24pIC8gKDEgKyBjb24pLCBlIC8gMik7XHJcblx0XHRcdGRwaGkgPSBNYXRoLlBJIC8gMiAtIDIgKiBNYXRoLmF0YW4odHMgKiBjb24pIC0gcGhpO1xyXG5cdFx0XHRwaGkgKz0gZHBoaTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKHBoaSAqIGQsIHBvaW50LnggKiBkIC8gcik7XHJcblx0fVxyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIENSU1xyXG4gKiBAY3JzIEwuQ1JTLkVQU0czMzk1XHJcbiAqXHJcbiAqIFJhcmVseSB1c2VkIGJ5IHNvbWUgY29tbWVyY2lhbCB0aWxlIHByb3ZpZGVycy4gVXNlcyBFbGxpcHRpY2FsIE1lcmNhdG9yIHByb2plY3Rpb24uXHJcbiAqL1xyXG5cclxuTC5DUlMuRVBTRzMzOTUgPSBMLmV4dGVuZCh7fSwgTC5DUlMuRWFydGgsIHtcclxuXHRjb2RlOiAnRVBTRzozMzk1JyxcclxuXHRwcm9qZWN0aW9uOiBMLlByb2plY3Rpb24uTWVyY2F0b3IsXHJcblxyXG5cdHRyYW5zZm9ybWF0aW9uOiAoZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHNjYWxlID0gMC41IC8gKE1hdGguUEkgKiBMLlByb2plY3Rpb24uTWVyY2F0b3IuUik7XHJcblx0XHRyZXR1cm4gbmV3IEwuVHJhbnNmb3JtYXRpb24oc2NhbGUsIDAuNSwgLXNjYWxlLCAwLjUpO1xyXG5cdH0oKSlcclxufSk7XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgR3JpZExheWVyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcbiAqIEBha2EgTC5HcmlkTGF5ZXJcbiAqXG4gKiBHZW5lcmljIGNsYXNzIGZvciBoYW5kbGluZyBhIHRpbGVkIGdyaWQgb2YgSFRNTCBlbGVtZW50cy4gVGhpcyBpcyB0aGUgYmFzZSBjbGFzcyBmb3IgYWxsIHRpbGUgbGF5ZXJzIGFuZCByZXBsYWNlcyBgVGlsZUxheWVyLkNhbnZhc2AuXG4gKiBHcmlkTGF5ZXIgY2FuIGJlIGV4dGVuZGVkIHRvIGNyZWF0ZSBhIHRpbGVkIGdyaWQgb2YgSFRNTCBlbGVtZW50cyBsaWtlIGA8Y2FudmFzPmAsIGA8aW1nPmAgb3IgYDxkaXY+YC4gR3JpZExheWVyIHdpbGwgaGFuZGxlIGNyZWF0aW5nIGFuZCBhbmltYXRpbmcgdGhlc2UgRE9NIGVsZW1lbnRzIGZvciB5b3UuXG4gKlxuICpcbiAqIEBzZWN0aW9uIFN5bmNocm9ub3VzIHVzYWdlXG4gKiBAZXhhbXBsZVxuICpcbiAqIFRvIGNyZWF0ZSBhIGN1c3RvbSBsYXllciwgZXh0ZW5kIEdyaWRMYXllciBhbmQgaW1wbGVtZW50IHRoZSBgY3JlYXRlVGlsZSgpYCBtZXRob2QsIHdoaWNoIHdpbGwgYmUgcGFzc2VkIGEgYFBvaW50YCBvYmplY3Qgd2l0aCB0aGUgYHhgLCBgeWAsIGFuZCBgemAgKHpvb20gbGV2ZWwpIGNvb3JkaW5hdGVzIHRvIGRyYXcgeW91ciB0aWxlLlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgQ2FudmFzTGF5ZXIgPSBMLkdyaWRMYXllci5leHRlbmQoe1xuICogICAgIGNyZWF0ZVRpbGU6IGZ1bmN0aW9uKGNvb3Jkcyl7XG4gKiAgICAgICAgIC8vIGNyZWF0ZSBhIDxjYW52YXM+IGVsZW1lbnQgZm9yIGRyYXdpbmdcbiAqICAgICAgICAgdmFyIHRpbGUgPSBMLkRvbVV0aWwuY3JlYXRlKCdjYW52YXMnLCAnbGVhZmxldC10aWxlJyk7XG4gKlxuICogICAgICAgICAvLyBzZXR1cCB0aWxlIHdpZHRoIGFuZCBoZWlnaHQgYWNjb3JkaW5nIHRvIHRoZSBvcHRpb25zXG4gKiAgICAgICAgIHZhciBzaXplID0gdGhpcy5nZXRUaWxlU2l6ZSgpO1xuICogICAgICAgICB0aWxlLndpZHRoID0gc2l6ZS54O1xuICogICAgICAgICB0aWxlLmhlaWdodCA9IHNpemUueTtcbiAqXG4gKiAgICAgICAgIC8vIGdldCBhIGNhbnZhcyBjb250ZXh0IGFuZCBkcmF3IHNvbWV0aGluZyBvbiBpdCB1c2luZyBjb29yZHMueCwgY29vcmRzLnkgYW5kIGNvb3Jkcy56XG4gKiAgICAgICAgIHZhciBjdHggPSB0aWxlLmdldENvbnRleHQoJzJkJyk7XG4gKlxuICogICAgICAgICAvLyByZXR1cm4gdGhlIHRpbGUgc28gaXQgY2FuIGJlIHJlbmRlcmVkIG9uIHNjcmVlblxuICogICAgICAgICByZXR1cm4gdGlsZTtcbiAqICAgICB9XG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqIEBzZWN0aW9uIEFzeW5jaHJvbm91cyB1c2FnZVxuICogQGV4YW1wbGVcbiAqXG4gKiBUaWxlIGNyZWF0aW9uIGNhbiBhbHNvIGJlIGFzeW5jaHJvbm91cywgdGhpcyBpcyB1c2VmdWwgd2hlbiB1c2luZyBhIHRoaXJkLXBhcnR5IGRyYXdpbmcgbGlicmFyeS4gT25jZSB0aGUgdGlsZSBpcyBmaW5pc2hlZCBkcmF3aW5nIGl0IGNhbiBiZSBwYXNzZWQgdG8gdGhlIGBkb25lKClgIGNhbGxiYWNrLlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgQ2FudmFzTGF5ZXIgPSBMLkdyaWRMYXllci5leHRlbmQoe1xuICogICAgIGNyZWF0ZVRpbGU6IGZ1bmN0aW9uKGNvb3JkcywgZG9uZSl7XG4gKiAgICAgICAgIHZhciBlcnJvcjtcbiAqXG4gKiAgICAgICAgIC8vIGNyZWF0ZSBhIDxjYW52YXM+IGVsZW1lbnQgZm9yIGRyYXdpbmdcbiAqICAgICAgICAgdmFyIHRpbGUgPSBMLkRvbVV0aWwuY3JlYXRlKCdjYW52YXMnLCAnbGVhZmxldC10aWxlJyk7XG4gKlxuICogICAgICAgICAvLyBzZXR1cCB0aWxlIHdpZHRoIGFuZCBoZWlnaHQgYWNjb3JkaW5nIHRvIHRoZSBvcHRpb25zXG4gKiAgICAgICAgIHZhciBzaXplID0gdGhpcy5nZXRUaWxlU2l6ZSgpO1xuICogICAgICAgICB0aWxlLndpZHRoID0gc2l6ZS54O1xuICogICAgICAgICB0aWxlLmhlaWdodCA9IHNpemUueTtcbiAqXG4gKiAgICAgICAgIC8vIGRyYXcgc29tZXRoaW5nIGFzeW5jaHJvbm91c2x5IGFuZCBwYXNzIHRoZSB0aWxlIHRvIHRoZSBkb25lKCkgY2FsbGJhY2tcbiAqICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAqICAgICAgICAgICAgIGRvbmUoZXJyb3IsIHRpbGUpO1xuICogICAgICAgICB9LCAxMDAwKTtcbiAqXG4gKiAgICAgICAgIHJldHVybiB0aWxlO1xuICogICAgIH1cbiAqIH0pO1xuICogYGBgXG4gKlxuICogQHNlY3Rpb25cbiAqL1xuXG5cbkwuR3JpZExheWVyID0gTC5MYXllci5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgR3JpZExheWVyIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gdGlsZVNpemU6IE51bWJlcnxQb2ludCA9IDI1NlxuXHRcdC8vIFdpZHRoIGFuZCBoZWlnaHQgb2YgdGlsZXMgaW4gdGhlIGdyaWQuIFVzZSBhIG51bWJlciBpZiB3aWR0aCBhbmQgaGVpZ2h0IGFyZSBlcXVhbCwgb3IgYEwucG9pbnQod2lkdGgsIGhlaWdodClgIG90aGVyd2lzZS5cblx0XHR0aWxlU2l6ZTogMjU2LFxuXG5cdFx0Ly8gQG9wdGlvbiBvcGFjaXR5OiBOdW1iZXIgPSAxLjBcblx0XHQvLyBPcGFjaXR5IG9mIHRoZSB0aWxlcy4gQ2FuIGJlIHVzZWQgaW4gdGhlIGBjcmVhdGVUaWxlKClgIGZ1bmN0aW9uLlxuXHRcdG9wYWNpdHk6IDEsXG5cblx0XHQvLyBAb3B0aW9uIHVwZGF0ZVdoZW5JZGxlOiBCb29sZWFuID0gZGVwZW5kc1xuXHRcdC8vIElmIGBmYWxzZWAsIG5ldyB0aWxlcyBhcmUgbG9hZGVkIGR1cmluZyBwYW5uaW5nLCBvdGhlcndpc2Ugb25seSBhZnRlciBpdCAoZm9yIGJldHRlciBwZXJmb3JtYW5jZSkuIGB0cnVlYCBieSBkZWZhdWx0IG9uIG1vYmlsZSBicm93c2Vycywgb3RoZXJ3aXNlIGBmYWxzZWAuXG5cdFx0dXBkYXRlV2hlbklkbGU6IEwuQnJvd3Nlci5tb2JpbGUsXG5cblx0XHQvLyBAb3B0aW9uIHVwZGF0ZVdoZW5ab29taW5nOiBCb29sZWFuID0gdHJ1ZVxuXHRcdC8vIEJ5IGRlZmF1bHQsIGEgc21vb3RoIHpvb20gYW5pbWF0aW9uIChkdXJpbmcgYSBbdG91Y2ggem9vbV0oI21hcC10b3VjaHpvb20pIG9yIGEgW2BmbHlUbygpYF0oI21hcC1mbHl0bykpIHdpbGwgdXBkYXRlIGdyaWQgbGF5ZXJzIGV2ZXJ5IGludGVnZXIgem9vbSBsZXZlbC4gU2V0dGluZyB0aGlzIG9wdGlvbiB0byBgZmFsc2VgIHdpbGwgdXBkYXRlIHRoZSBncmlkIGxheWVyIG9ubHkgd2hlbiB0aGUgc21vb3RoIGFuaW1hdGlvbiBlbmRzLlxuXHRcdHVwZGF0ZVdoZW5ab29taW5nOiB0cnVlLFxuXG5cdFx0Ly8gQG9wdGlvbiB1cGRhdGVJbnRlcnZhbDogTnVtYmVyID0gMjAwXG5cdFx0Ly8gVGlsZXMgd2lsbCBub3QgdXBkYXRlIG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IGB1cGRhdGVJbnRlcnZhbGAgbWlsbGlzZWNvbmRzIHdoZW4gcGFubmluZy5cblx0XHR1cGRhdGVJbnRlcnZhbDogMjAwLFxuXG5cdFx0Ly8gQG9wdGlvbiB6SW5kZXg6IE51bWJlciA9IDFcblx0XHQvLyBUaGUgZXhwbGljaXQgekluZGV4IG9mIHRoZSB0aWxlIGxheWVyLlxuXHRcdHpJbmRleDogMSxcblxuXHRcdC8vIEBvcHRpb24gYm91bmRzOiBMYXRMbmdCb3VuZHMgPSB1bmRlZmluZWRcblx0XHQvLyBJZiBzZXQsIHRpbGVzIHdpbGwgb25seSBiZSBsb2FkZWQgaW5zaWRlIHRoZSBzZXQgYExhdExuZ0JvdW5kc2AuXG5cdFx0Ym91bmRzOiBudWxsLFxuXG5cdFx0Ly8gQG9wdGlvbiBtaW5ab29tOiBOdW1iZXIgPSAwXG5cdFx0Ly8gVGhlIG1pbmltdW0gem9vbSBsZXZlbCB0aGF0IHRpbGVzIHdpbGwgYmUgbG9hZGVkIGF0LiBCeSBkZWZhdWx0IHRoZSBlbnRpcmUgbWFwLlxuXHRcdG1pblpvb206IDAsXG5cblx0XHQvLyBAb3B0aW9uIG1heFpvb206IE51bWJlciA9IHVuZGVmaW5lZFxuXHRcdC8vIFRoZSBtYXhpbXVtIHpvb20gbGV2ZWwgdGhhdCB0aWxlcyB3aWxsIGJlIGxvYWRlZCBhdC5cblx0XHRtYXhab29tOiB1bmRlZmluZWQsXG5cblx0XHQvLyBAb3B0aW9uIG5vV3JhcDogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gV2hldGhlciB0aGUgbGF5ZXIgaXMgd3JhcHBlZCBhcm91bmQgdGhlIGFudGltZXJpZGlhbi4gSWYgYHRydWVgLCB0aGVcblx0XHQvLyBHcmlkTGF5ZXIgd2lsbCBvbmx5IGJlIGRpc3BsYXllZCBvbmNlIGF0IGxvdyB6b29tIGxldmVscy4gSGFzIG5vXG5cdFx0Ly8gZWZmZWN0IHdoZW4gdGhlIFttYXAgQ1JTXSgjbWFwLWNycykgZG9lc24ndCB3cmFwIGFyb3VuZC5cblx0XHRub1dyYXA6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAndGlsZVBhbmUnXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgZ3JpZCBsYXllciB3aWxsIGJlIGFkZGVkLlxuXHRcdHBhbmU6ICd0aWxlUGFuZScsXG5cblx0XHQvLyBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gJydcblx0XHQvLyBBIGN1c3RvbSBjbGFzcyBuYW1lIHRvIGFzc2lnbiB0byB0aGUgdGlsZSBsYXllci4gRW1wdHkgYnkgZGVmYXVsdC5cblx0XHRjbGFzc05hbWU6ICcnLFxuXG5cdFx0Ly8gQG9wdGlvbiBrZWVwQnVmZmVyOiBOdW1iZXIgPSAyXG5cdFx0Ly8gV2hlbiBwYW5uaW5nIHRoZSBtYXAsIGtlZXAgdGhpcyBtYW55IHJvd3MgYW5kIGNvbHVtbnMgb2YgdGlsZXMgYmVmb3JlIHVubG9hZGluZyB0aGVtLlxuXHRcdGtlZXBCdWZmZXI6IDJcblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2luaXRDb250YWluZXIoKTtcblxuXHRcdHRoaXMuX2xldmVscyA9IHt9O1xuXHRcdHRoaXMuX3RpbGVzID0ge307XG5cblx0XHR0aGlzLl9yZXNldFZpZXcoKTtcblx0XHR0aGlzLl91cGRhdGUoKTtcblx0fSxcblxuXHRiZWZvcmVBZGQ6IGZ1bmN0aW9uIChtYXApIHtcblx0XHRtYXAuX2FkZFpvb21MaW1pdCh0aGlzKTtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHRoaXMuX3JlbW92ZUFsbFRpbGVzKCk7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xuXHRcdG1hcC5fcmVtb3ZlWm9vbUxpbWl0KHRoaXMpO1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IG51bGw7XG5cdFx0dGhpcy5fdGlsZVpvb20gPSBudWxsO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250OiB0aGlzXG5cdC8vIEJyaW5ncyB0aGUgdGlsZSBsYXllciB0byB0aGUgdG9wIG9mIGFsbCB0aWxlIGxheWVycy5cblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0TC5Eb21VdGlsLnRvRnJvbnQodGhpcy5fY29udGFpbmVyKTtcblx0XHRcdHRoaXMuX3NldEF1dG9aSW5kZXgoTWF0aC5tYXgpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGJyaW5nVG9CYWNrOiB0aGlzXG5cdC8vIEJyaW5ncyB0aGUgdGlsZSBsYXllciB0byB0aGUgYm90dG9tIG9mIGFsbCB0aWxlIGxheWVycy5cblx0YnJpbmdUb0JhY2s6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHRMLkRvbVV0aWwudG9CYWNrKHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHR0aGlzLl9zZXRBdXRvWkluZGV4KE1hdGgubWluKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRDb250YWluZXI6IEhUTUxFbGVtZW50XG5cdC8vIFJldHVybnMgdGhlIEhUTUwgZWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSB0aWxlcyBmb3IgdGhpcyBsYXllci5cblx0Z2V0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBbb3BhY2l0eV0oI2dyaWRsYXllci1vcGFjaXR5KSBvZiB0aGUgZ3JpZCBsYXllci5cblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKG9wYWNpdHkpIHtcblx0XHR0aGlzLm9wdGlvbnMub3BhY2l0eSA9IG9wYWNpdHk7XG5cdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0WkluZGV4KHpJbmRleDogTnVtYmVyKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBbekluZGV4XSgjZ3JpZGxheWVyLXppbmRleCkgb2YgdGhlIGdyaWQgbGF5ZXIuXG5cdHNldFpJbmRleDogZnVuY3Rpb24gKHpJbmRleCkge1xuXHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSB6SW5kZXg7XG5cdFx0dGhpcy5fdXBkYXRlWkluZGV4KCk7XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGlzTG9hZGluZzogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBhbnkgdGlsZSBpbiB0aGUgZ3JpZCBsYXllciBoYXMgbm90IGZpbmlzaGVkIGxvYWRpbmcuXG5cdGlzTG9hZGluZzogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sb2FkaW5nO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVkcmF3OiB0aGlzXG5cdC8vIENhdXNlcyB0aGUgbGF5ZXIgdG8gY2xlYXIgYWxsIHRoZSB0aWxlcyBhbmQgcmVxdWVzdCB0aGVtIGFnYWluLlxuXHRyZWRyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0dGhpcy5fdXBkYXRlKCk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSB7XG5cdFx0XHR2aWV3cHJlcmVzZXQ6IHRoaXMuX2ludmFsaWRhdGVBbGwsXG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdHpvb206IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdG1vdmVlbmQ6IHRoaXMuX29uTW92ZUVuZFxuXHRcdH07XG5cblx0XHRpZiAoIXRoaXMub3B0aW9ucy51cGRhdGVXaGVuSWRsZSkge1xuXHRcdFx0Ly8gdXBkYXRlIHRpbGVzIG9uIG1vdmUsIGJ1dCBub3QgbW9yZSBvZnRlbiB0aGFuIG9uY2UgcGVyIGdpdmVuIGludGVydmFsXG5cdFx0XHRpZiAoIXRoaXMuX29uTW92ZSkge1xuXHRcdFx0XHR0aGlzLl9vbk1vdmUgPSBMLlV0aWwudGhyb3R0bGUodGhpcy5fb25Nb3ZlRW5kLCB0aGlzLm9wdGlvbnMudXBkYXRlSW50ZXJ2YWwsIHRoaXMpO1xuXHRcdFx0fVxuXG5cdFx0XHRldmVudHMubW92ZSA9IHRoaXMuX29uTW92ZTtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9hbmltYXRlWm9vbTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXG5cdC8vIExheWVycyBleHRlbmRpbmcgYEdyaWRMYXllcmAgc2hhbGwgcmVpbXBsZW1lbnQgdGhlIGZvbGxvd2luZyBtZXRob2QuXG5cdC8vIEBtZXRob2QgY3JlYXRlVGlsZShjb29yZHM6IE9iamVjdCwgZG9uZT86IEZ1bmN0aW9uKTogSFRNTEVsZW1lbnRcblx0Ly8gQ2FsbGVkIG9ubHkgaW50ZXJuYWxseSwgbXVzdCBiZSBvdmVycmlkZW4gYnkgY2xhc3NlcyBleHRlbmRpbmcgYEdyaWRMYXllcmAuXG5cdC8vIFJldHVybnMgdGhlIGBIVE1MRWxlbWVudGAgY29ycmVzcG9uZGluZyB0byB0aGUgZ2l2ZW4gYGNvb3Jkc2AuIElmIHRoZSBgZG9uZWAgY2FsbGJhY2tcblx0Ly8gaXMgc3BlY2lmaWVkLCBpdCBtdXN0IGJlIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBmaW5pc2hlZCBsb2FkaW5nIGFuZCBkcmF3aW5nLlxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuXHR9LFxuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVNpemU6IFBvaW50XG5cdC8vIE5vcm1hbGl6ZXMgdGhlIFt0aWxlU2l6ZSBvcHRpb25dKCNncmlkbGF5ZXItdGlsZXNpemUpIGludG8gYSBwb2ludC4gVXNlZCBieSB0aGUgYGNyZWF0ZVRpbGUoKWAgbWV0aG9kLlxuXHRnZXRUaWxlU2l6ZTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBzID0gdGhpcy5vcHRpb25zLnRpbGVTaXplO1xuXHRcdHJldHVybiBzIGluc3RhbmNlb2YgTC5Qb2ludCA/IHMgOiBuZXcgTC5Qb2ludChzLCBzKTtcblx0fSxcblxuXHRfdXBkYXRlWkluZGV4OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lciAmJiB0aGlzLm9wdGlvbnMuekluZGV4ICE9PSB1bmRlZmluZWQgJiYgdGhpcy5vcHRpb25zLnpJbmRleCAhPT0gbnVsbCkge1xuXHRcdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLnpJbmRleCA9IHRoaXMub3B0aW9ucy56SW5kZXg7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRBdXRvWkluZGV4OiBmdW5jdGlvbiAoY29tcGFyZSkge1xuXHRcdC8vIGdvIHRocm91Z2ggYWxsIG90aGVyIGxheWVycyBvZiB0aGUgc2FtZSBwYW5lLCBzZXQgekluZGV4IHRvIG1heCArIDEgKGZyb250KSBvciBtaW4gLSAxIChiYWNrKVxuXG5cdFx0dmFyIGxheWVycyA9IHRoaXMuZ2V0UGFuZSgpLmNoaWxkcmVuLFxuXHRcdCAgICBlZGdlWkluZGV4ID0gLWNvbXBhcmUoLUluZmluaXR5LCBJbmZpbml0eSk7IC8vIC1JbmZpbml0eSBmb3IgbWF4LCBJbmZpbml0eSBmb3IgbWluXG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF5ZXJzLmxlbmd0aCwgekluZGV4OyBpIDwgbGVuOyBpKyspIHtcblxuXHRcdFx0ekluZGV4ID0gbGF5ZXJzW2ldLnN0eWxlLnpJbmRleDtcblxuXHRcdFx0aWYgKGxheWVyc1tpXSAhPT0gdGhpcy5fY29udGFpbmVyICYmIHpJbmRleCkge1xuXHRcdFx0XHRlZGdlWkluZGV4ID0gY29tcGFyZShlZGdlWkluZGV4LCArekluZGV4KTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoaXNGaW5pdGUoZWRnZVpJbmRleCkpIHtcblx0XHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSBlZGdlWkluZGV4ICsgY29tcGFyZSgtMSwgMSk7XG5cdFx0XHR0aGlzLl91cGRhdGVaSW5kZXgoKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZU9wYWNpdHk6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cblxuXHRcdC8vIElFIGRvZXNuJ3QgaW5oZXJpdCBmaWx0ZXIgb3BhY2l0eSBwcm9wZXJseSwgc28gd2UncmUgZm9yY2VkIHRvIHNldCBpdCBvbiB0aWxlc1xuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkpIHsgcmV0dXJuOyB9XG5cblx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblxuXHRcdHZhciBub3cgPSArbmV3IERhdGUoKSxcblx0XHQgICAgbmV4dEZyYW1lID0gZmFsc2UsXG5cdFx0ICAgIHdpbGxQcnVuZSA9IGZhbHNlO1xuXG5cdFx0Zm9yICh2YXIga2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR2YXIgdGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0XHRpZiAoIXRpbGUuY3VycmVudCB8fCAhdGlsZS5sb2FkZWQpIHsgY29udGludWU7IH1cblxuXHRcdFx0dmFyIGZhZGUgPSBNYXRoLm1pbigxLCAobm93IC0gdGlsZS5sb2FkZWQpIC8gMjAwKTtcblxuXHRcdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGlsZS5lbCwgZmFkZSk7XG5cdFx0XHRpZiAoZmFkZSA8IDEpIHtcblx0XHRcdFx0bmV4dEZyYW1lID0gdHJ1ZTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGlmICh0aWxlLmFjdGl2ZSkgeyB3aWxsUHJ1bmUgPSB0cnVlOyB9XG5cdFx0XHRcdHRpbGUuYWN0aXZlID0gdHJ1ZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAod2lsbFBydW5lICYmICF0aGlzLl9ub1BydW5lKSB7IHRoaXMuX3BydW5lVGlsZXMoKTsgfVxuXG5cdFx0aWYgKG5leHRGcmFtZSkge1xuXHRcdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9mYWRlRnJhbWUpO1xuXHRcdFx0dGhpcy5fZmFkZUZyYW1lID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fdXBkYXRlT3BhY2l0eSwgdGhpcyk7XG5cdFx0fVxuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lcikgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWxheWVyICcgKyAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSB8fCAnJykpO1xuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgpO1xuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5vcGFjaXR5IDwgMSkge1xuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2NvbnRhaW5lcik7XG5cdH0sXG5cblx0X3VwZGF0ZUxldmVsczogZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHpvb20gPSB0aGlzLl90aWxlWm9vbSxcblx0XHQgICAgbWF4Wm9vbSA9IHRoaXMub3B0aW9ucy5tYXhab29tO1xuXG5cdFx0aWYgKHpvb20gPT09IHVuZGVmaW5lZCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9XG5cblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0aWYgKHRoaXMuX2xldmVsc1t6XS5lbC5jaGlsZHJlbi5sZW5ndGggfHwgeiA9PT0gem9vbSkge1xuXHRcdFx0XHR0aGlzLl9sZXZlbHNbel0uZWwuc3R5bGUuekluZGV4ID0gbWF4Wm9vbSAtIE1hdGguYWJzKHpvb20gLSB6KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fbGV2ZWxzW3pdLmVsKTtcblx0XHRcdFx0dGhpcy5fcmVtb3ZlVGlsZXNBdFpvb20oeik7XG5cdFx0XHRcdGRlbGV0ZSB0aGlzLl9sZXZlbHNbel07XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dmFyIGxldmVsID0gdGhpcy5fbGV2ZWxzW3pvb21dLFxuXHRcdCAgICBtYXAgPSB0aGlzLl9tYXA7XG5cblx0XHRpZiAoIWxldmVsKSB7XG5cdFx0XHRsZXZlbCA9IHRoaXMuX2xldmVsc1t6b29tXSA9IHt9O1xuXG5cdFx0XHRsZXZlbC5lbCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXRpbGUtY29udGFpbmVyIGxlYWZsZXQtem9vbS1hbmltYXRlZCcsIHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHRsZXZlbC5lbC5zdHlsZS56SW5kZXggPSBtYXhab29tO1xuXG5cdFx0XHRsZXZlbC5vcmlnaW4gPSBtYXAucHJvamVjdChtYXAudW5wcm9qZWN0KG1hcC5nZXRQaXhlbE9yaWdpbigpKSwgem9vbSkucm91bmQoKTtcblx0XHRcdGxldmVsLnpvb20gPSB6b29tO1xuXG5cdFx0XHR0aGlzLl9zZXRab29tVHJhbnNmb3JtKGxldmVsLCBtYXAuZ2V0Q2VudGVyKCksIG1hcC5nZXRab29tKCkpO1xuXG5cdFx0XHQvLyBmb3JjZSB0aGUgYnJvd3NlciB0byBjb25zaWRlciB0aGUgbmV3bHkgYWRkZWQgZWxlbWVudCBmb3IgdHJhbnNpdGlvblxuXHRcdFx0TC5VdGlsLmZhbHNlRm4obGV2ZWwuZWwub2Zmc2V0V2lkdGgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2xldmVsID0gbGV2ZWw7XG5cblx0XHRyZXR1cm4gbGV2ZWw7XG5cdH0sXG5cblx0X3BydW5lVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBrZXksIHRpbGU7XG5cblx0XHR2YXIgem9vbSA9IHRoaXMuX21hcC5nZXRab29tKCk7XG5cdFx0aWYgKHpvb20gPiB0aGlzLm9wdGlvbnMubWF4Wm9vbSB8fFxuXHRcdFx0em9vbSA8IHRoaXMub3B0aW9ucy5taW5ab29tKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdHRpbGUucmV0YWluID0gdGlsZS5jdXJyZW50O1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdGlmICh0aWxlLmN1cnJlbnQgJiYgIXRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdHZhciBjb29yZHMgPSB0aWxlLmNvb3Jkcztcblx0XHRcdFx0aWYgKCF0aGlzLl9yZXRhaW5QYXJlbnQoY29vcmRzLngsIGNvb3Jkcy55LCBjb29yZHMueiwgY29vcmRzLnogLSA1KSkge1xuXHRcdFx0XHRcdHRoaXMuX3JldGFpbkNoaWxkcmVuKGNvb3Jkcy54LCBjb29yZHMueSwgY29vcmRzLnosIGNvb3Jkcy56ICsgMik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRmb3IgKGtleSBpbiB0aGlzLl90aWxlcykge1xuXHRcdFx0aWYgKCF0aGlzLl90aWxlc1trZXldLnJldGFpbikge1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVUaWxlKGtleSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9yZW1vdmVUaWxlc0F0Wm9vbTogZnVuY3Rpb24gKHpvb20pIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdGlmICh0aGlzLl90aWxlc1trZXldLmNvb3Jkcy56ICE9PSB6b29tKSB7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fcmVtb3ZlVGlsZShrZXkpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVtb3ZlQWxsVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdHRoaXMuX3JlbW92ZVRpbGUoa2V5KTtcblx0XHR9XG5cdH0sXG5cblx0X2ludmFsaWRhdGVBbGw6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9sZXZlbHNbel0uZWwpO1xuXHRcdFx0ZGVsZXRlIHRoaXMuX2xldmVsc1t6XTtcblx0XHR9XG5cdFx0dGhpcy5fcmVtb3ZlQWxsVGlsZXMoKTtcblxuXHRcdHRoaXMuX3RpbGVab29tID0gbnVsbDtcblx0fSxcblxuXHRfcmV0YWluUGFyZW50OiBmdW5jdGlvbiAoeCwgeSwgeiwgbWluWm9vbSkge1xuXHRcdHZhciB4MiA9IE1hdGguZmxvb3IoeCAvIDIpLFxuXHRcdCAgICB5MiA9IE1hdGguZmxvb3IoeSAvIDIpLFxuXHRcdCAgICB6MiA9IHogLSAxLFxuXHRcdCAgICBjb29yZHMyID0gbmV3IEwuUG9pbnQoK3gyLCAreTIpO1xuXHRcdGNvb3JkczIueiA9ICt6MjtcblxuXHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzMiksXG5cdFx0ICAgIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0aWYgKHRpbGUgJiYgdGlsZS5hY3RpdmUpIHtcblx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdHJldHVybiB0cnVlO1xuXG5cdFx0fSBlbHNlIGlmICh0aWxlICYmIHRpbGUubG9hZGVkKSB7XG5cdFx0XHR0aWxlLnJldGFpbiA9IHRydWU7XG5cdFx0fVxuXG5cdFx0aWYgKHoyID4gbWluWm9vbSkge1xuXHRcdFx0cmV0dXJuIHRoaXMuX3JldGFpblBhcmVudCh4MiwgeTIsIHoyLCBtaW5ab29tKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0X3JldGFpbkNoaWxkcmVuOiBmdW5jdGlvbiAoeCwgeSwgeiwgbWF4Wm9vbSkge1xuXG5cdFx0Zm9yICh2YXIgaSA9IDIgKiB4OyBpIDwgMiAqIHggKyAyOyBpKyspIHtcblx0XHRcdGZvciAodmFyIGogPSAyICogeTsgaiA8IDIgKiB5ICsgMjsgaisrKSB7XG5cblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBMLlBvaW50KGksIGopO1xuXHRcdFx0XHRjb29yZHMueiA9IHogKyAxO1xuXG5cdFx0XHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKSxcblx0XHRcdFx0ICAgIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0XHRcdGlmICh0aWxlICYmIHRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdFx0dGlsZS5yZXRhaW4gPSB0cnVlO1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGlsZSAmJiB0aWxlLmxvYWRlZCkge1xuXHRcdFx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICh6ICsgMSA8IG1heFpvb20pIHtcblx0XHRcdFx0XHR0aGlzLl9yZXRhaW5DaGlsZHJlbihpLCBqLCB6ICsgMSwgbWF4Wm9vbSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0X3Jlc2V0VmlldzogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgYW5pbWF0aW5nID0gZSAmJiAoZS5waW5jaCB8fCBlLmZseVRvKTtcblx0XHR0aGlzLl9zZXRWaWV3KHRoaXMuX21hcC5nZXRDZW50ZXIoKSwgdGhpcy5fbWFwLmdldFpvb20oKSwgYW5pbWF0aW5nLCBhbmltYXRpbmcpO1xuXHR9LFxuXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKGUpIHtcblx0XHR0aGlzLl9zZXRWaWV3KGUuY2VudGVyLCBlLnpvb20sIHRydWUsIGUubm9VcGRhdGUpO1xuXHR9LFxuXG5cdF9zZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBub1BydW5lLCBub1VwZGF0ZSkge1xuXHRcdHZhciB0aWxlWm9vbSA9IE1hdGgucm91bmQoem9vbSk7XG5cdFx0aWYgKCh0aGlzLm9wdGlvbnMubWF4Wm9vbSAhPT0gdW5kZWZpbmVkICYmIHRpbGVab29tID4gdGhpcy5vcHRpb25zLm1heFpvb20pIHx8XG5cdFx0ICAgICh0aGlzLm9wdGlvbnMubWluWm9vbSAhPT0gdW5kZWZpbmVkICYmIHRpbGVab29tIDwgdGhpcy5vcHRpb25zLm1pblpvb20pKSB7XG5cdFx0XHR0aWxlWm9vbSA9IHVuZGVmaW5lZDtcblx0XHR9XG5cblx0XHR2YXIgdGlsZVpvb21DaGFuZ2VkID0gdGhpcy5vcHRpb25zLnVwZGF0ZVdoZW5ab29taW5nICYmICh0aWxlWm9vbSAhPT0gdGhpcy5fdGlsZVpvb20pO1xuXG5cdFx0aWYgKCFub1VwZGF0ZSB8fCB0aWxlWm9vbUNoYW5nZWQpIHtcblxuXHRcdFx0dGhpcy5fdGlsZVpvb20gPSB0aWxlWm9vbTtcblxuXHRcdFx0aWYgKHRoaXMuX2Fib3J0TG9hZGluZykge1xuXHRcdFx0XHR0aGlzLl9hYm9ydExvYWRpbmcoKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fdXBkYXRlTGV2ZWxzKCk7XG5cdFx0XHR0aGlzLl9yZXNldEdyaWQoKTtcblxuXHRcdFx0aWYgKHRpbGVab29tICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdFx0dGhpcy5fdXBkYXRlKGNlbnRlcik7XG5cdFx0XHR9XG5cblx0XHRcdGlmICghbm9QcnVuZSkge1xuXHRcdFx0XHR0aGlzLl9wcnVuZVRpbGVzKCk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEZsYWcgdG8gcHJldmVudCBfdXBkYXRlT3BhY2l0eSBmcm9tIHBydW5pbmcgdGlsZXMgZHVyaW5nXG5cdFx0XHQvLyBhIHpvb20gYW5pbSBvciBhIHBpbmNoIGdlc3R1cmVcblx0XHRcdHRoaXMuX25vUHJ1bmUgPSAhIW5vUHJ1bmU7XG5cdFx0fVxuXG5cdFx0dGhpcy5fc2V0Wm9vbVRyYW5zZm9ybXMoY2VudGVyLCB6b29tKTtcblx0fSxcblxuXHRfc2V0Wm9vbVRyYW5zZm9ybXM6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0dGhpcy5fc2V0Wm9vbVRyYW5zZm9ybSh0aGlzLl9sZXZlbHNbaV0sIGNlbnRlciwgem9vbSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRab29tVHJhbnNmb3JtOiBmdW5jdGlvbiAobGV2ZWwsIGNlbnRlciwgem9vbSkge1xuXHRcdHZhciBzY2FsZSA9IHRoaXMuX21hcC5nZXRab29tU2NhbGUoem9vbSwgbGV2ZWwuem9vbSksXG5cdFx0ICAgIHRyYW5zbGF0ZSA9IGxldmVsLm9yaWdpbi5tdWx0aXBseUJ5KHNjYWxlKVxuXHRcdCAgICAgICAgLnN1YnRyYWN0KHRoaXMuX21hcC5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyLCB6b29tKSkucm91bmQoKTtcblxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0obGV2ZWwuZWwsIHRyYW5zbGF0ZSwgc2NhbGUpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24obGV2ZWwuZWwsIHRyYW5zbGF0ZSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXNldEdyaWQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBjcnMgPSBtYXAub3B0aW9ucy5jcnMsXG5cdFx0ICAgIHRpbGVTaXplID0gdGhpcy5fdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCksXG5cdFx0ICAgIHRpbGVab29tID0gdGhpcy5fdGlsZVpvb207XG5cblx0XHR2YXIgYm91bmRzID0gdGhpcy5fbWFwLmdldFBpeGVsV29ybGRCb3VuZHModGhpcy5fdGlsZVpvb20pO1xuXHRcdGlmIChib3VuZHMpIHtcblx0XHRcdHRoaXMuX2dsb2JhbFRpbGVSYW5nZSA9IHRoaXMuX3B4Qm91bmRzVG9UaWxlUmFuZ2UoYm91bmRzKTtcblx0XHR9XG5cblx0XHR0aGlzLl93cmFwWCA9IGNycy53cmFwTG5nICYmICF0aGlzLm9wdGlvbnMubm9XcmFwICYmIFtcblx0XHRcdE1hdGguZmxvb3IobWFwLnByb2plY3QoWzAsIGNycy53cmFwTG5nWzBdXSwgdGlsZVpvb20pLnggLyB0aWxlU2l6ZS54KSxcblx0XHRcdE1hdGguY2VpbChtYXAucHJvamVjdChbMCwgY3JzLndyYXBMbmdbMV1dLCB0aWxlWm9vbSkueCAvIHRpbGVTaXplLnkpXG5cdFx0XTtcblx0XHR0aGlzLl93cmFwWSA9IGNycy53cmFwTGF0ICYmICF0aGlzLm9wdGlvbnMubm9XcmFwICYmIFtcblx0XHRcdE1hdGguZmxvb3IobWFwLnByb2plY3QoW2Nycy53cmFwTGF0WzBdLCAwXSwgdGlsZVpvb20pLnkgLyB0aWxlU2l6ZS54KSxcblx0XHRcdE1hdGguY2VpbChtYXAucHJvamVjdChbY3JzLndyYXBMYXRbMV0sIDBdLCB0aWxlWm9vbSkueSAvIHRpbGVTaXplLnkpXG5cdFx0XTtcblx0fSxcblxuXHRfb25Nb3ZlRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXAgfHwgdGhpcy5fbWFwLl9hbmltYXRpbmdab29tKSB7IHJldHVybjsgfVxuXG5cdFx0dGhpcy5fdXBkYXRlKCk7XG5cdH0sXG5cblx0X2dldFRpbGVkUGl4ZWxCb3VuZHM6IGZ1bmN0aW9uIChjZW50ZXIpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBtYXBab29tID0gbWFwLl9hbmltYXRpbmdab29tID8gTWF0aC5tYXgobWFwLl9hbmltYXRlVG9ab29tLCBtYXAuZ2V0Wm9vbSgpKSA6IG1hcC5nZXRab29tKCksXG5cdFx0ICAgIHNjYWxlID0gbWFwLmdldFpvb21TY2FsZShtYXBab29tLCB0aGlzLl90aWxlWm9vbSksXG5cdFx0ICAgIHBpeGVsQ2VudGVyID0gbWFwLnByb2plY3QoY2VudGVyLCB0aGlzLl90aWxlWm9vbSkuZmxvb3IoKSxcblx0XHQgICAgaGFsZlNpemUgPSBtYXAuZ2V0U2l6ZSgpLmRpdmlkZUJ5KHNjYWxlICogMik7XG5cblx0XHRyZXR1cm4gbmV3IEwuQm91bmRzKHBpeGVsQ2VudGVyLnN1YnRyYWN0KGhhbGZTaXplKSwgcGl4ZWxDZW50ZXIuYWRkKGhhbGZTaXplKSk7XG5cdH0sXG5cblx0Ly8gUHJpdmF0ZSBtZXRob2QgdG8gbG9hZCB0aWxlcyBpbiB0aGUgZ3JpZCdzIGFjdGl2ZSB6b29tIGxldmVsIGFjY29yZGluZyB0byBtYXAgYm91bmRzXG5cdF91cGRhdGU6IGZ1bmN0aW9uIChjZW50ZXIpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXHRcdGlmICghbWFwKSB7IHJldHVybjsgfVxuXHRcdHZhciB6b29tID0gbWFwLmdldFpvb20oKTtcblxuXHRcdGlmIChjZW50ZXIgPT09IHVuZGVmaW5lZCkgeyBjZW50ZXIgPSBtYXAuZ2V0Q2VudGVyKCk7IH1cblx0XHRpZiAodGhpcy5fdGlsZVpvb20gPT09IHVuZGVmaW5lZCkgeyByZXR1cm47IH1cdC8vIGlmIG91dCBvZiBtaW56b29tL21heHpvb21cblxuXHRcdHZhciBwaXhlbEJvdW5kcyA9IHRoaXMuX2dldFRpbGVkUGl4ZWxCb3VuZHMoY2VudGVyKSxcblx0XHQgICAgdGlsZVJhbmdlID0gdGhpcy5fcHhCb3VuZHNUb1RpbGVSYW5nZShwaXhlbEJvdW5kcyksXG5cdFx0ICAgIHRpbGVDZW50ZXIgPSB0aWxlUmFuZ2UuZ2V0Q2VudGVyKCksXG5cdFx0ICAgIHF1ZXVlID0gW10sXG5cdFx0ICAgIG1hcmdpbiA9IHRoaXMub3B0aW9ucy5rZWVwQnVmZmVyLFxuXHRcdCAgICBub1BydW5lUmFuZ2UgPSBuZXcgTC5Cb3VuZHModGlsZVJhbmdlLmdldEJvdHRvbUxlZnQoKS5zdWJ0cmFjdChbbWFyZ2luLCAtbWFyZ2luXSksXG5cdFx0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGlsZVJhbmdlLmdldFRvcFJpZ2h0KCkuYWRkKFttYXJnaW4sIC1tYXJnaW5dKSk7XG5cblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdHZhciBjID0gdGhpcy5fdGlsZXNba2V5XS5jb29yZHM7XG5cdFx0XHRpZiAoYy56ICE9PSB0aGlzLl90aWxlWm9vbSB8fCAhbm9QcnVuZVJhbmdlLmNvbnRhaW5zKEwucG9pbnQoYy54LCBjLnkpKSkge1xuXHRcdFx0XHR0aGlzLl90aWxlc1trZXldLmN1cnJlbnQgPSBmYWxzZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBfdXBkYXRlIGp1c3QgbG9hZHMgbW9yZSB0aWxlcy4gSWYgdGhlIHRpbGUgem9vbSBsZXZlbCBkaWZmZXJzIHRvbyBtdWNoXG5cdFx0Ly8gZnJvbSB0aGUgbWFwJ3MsIGxldCBfc2V0VmlldyByZXNldCBsZXZlbHMgYW5kIHBydW5lIG9sZCB0aWxlcy5cblx0XHRpZiAoTWF0aC5hYnMoem9vbSAtIHRoaXMuX3RpbGVab29tKSA+IDEpIHsgdGhpcy5fc2V0VmlldyhjZW50ZXIsIHpvb20pOyByZXR1cm47IH1cblxuXHRcdC8vIGNyZWF0ZSBhIHF1ZXVlIG9mIGNvb3JkaW5hdGVzIHRvIGxvYWQgdGlsZXMgZnJvbVxuXHRcdGZvciAodmFyIGogPSB0aWxlUmFuZ2UubWluLnk7IGogPD0gdGlsZVJhbmdlLm1heC55OyBqKyspIHtcblx0XHRcdGZvciAodmFyIGkgPSB0aWxlUmFuZ2UubWluLng7IGkgPD0gdGlsZVJhbmdlLm1heC54OyBpKyspIHtcblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBMLlBvaW50KGksIGopO1xuXHRcdFx0XHRjb29yZHMueiA9IHRoaXMuX3RpbGVab29tO1xuXG5cdFx0XHRcdGlmICghdGhpcy5faXNWYWxpZFRpbGUoY29vcmRzKSkgeyBjb250aW51ZTsgfVxuXG5cdFx0XHRcdHZhciB0aWxlID0gdGhpcy5fdGlsZXNbdGhpcy5fdGlsZUNvb3Jkc1RvS2V5KGNvb3JkcyldO1xuXHRcdFx0XHRpZiAodGlsZSkge1xuXHRcdFx0XHRcdHRpbGUuY3VycmVudCA9IHRydWU7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0cXVldWUucHVzaChjb29yZHMpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gc29ydCB0aWxlIHF1ZXVlIHRvIGxvYWQgdGlsZXMgaW4gb3JkZXIgb2YgdGhlaXIgZGlzdGFuY2UgdG8gY2VudGVyXG5cdFx0cXVldWUuc29ydChmdW5jdGlvbiAoYSwgYikge1xuXHRcdFx0cmV0dXJuIGEuZGlzdGFuY2VUbyh0aWxlQ2VudGVyKSAtIGIuZGlzdGFuY2VUbyh0aWxlQ2VudGVyKTtcblx0XHR9KTtcblxuXHRcdGlmIChxdWV1ZS5sZW5ndGggIT09IDApIHtcblx0XHRcdC8vIGlmIGl0J3MgdGhlIGZpcnN0IGJhdGNoIG9mIHRpbGVzIHRvIGxvYWRcblx0XHRcdGlmICghdGhpcy5fbG9hZGluZykge1xuXHRcdFx0XHR0aGlzLl9sb2FkaW5nID0gdHJ1ZTtcblx0XHRcdFx0Ly8gQGV2ZW50IGxvYWRpbmc6IEV2ZW50XG5cdFx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIGdyaWQgbGF5ZXIgc3RhcnRzIGxvYWRpbmcgdGlsZXMuXG5cdFx0XHRcdHRoaXMuZmlyZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBjcmVhdGUgRE9NIGZyYWdtZW50IHRvIGFwcGVuZCB0aWxlcyBpbiBvbmUgYmF0Y2hcblx0XHRcdHZhciBmcmFnbWVudCA9IGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtcblxuXHRcdFx0Zm9yIChpID0gMDsgaSA8IHF1ZXVlLmxlbmd0aDsgaSsrKSB7XG5cdFx0XHRcdHRoaXMuX2FkZFRpbGUocXVldWVbaV0sIGZyYWdtZW50KTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fbGV2ZWwuZWwuYXBwZW5kQ2hpbGQoZnJhZ21lbnQpO1xuXHRcdH1cblx0fSxcblxuXHRfaXNWYWxpZFRpbGU6IGZ1bmN0aW9uIChjb29yZHMpIHtcblx0XHR2YXIgY3JzID0gdGhpcy5fbWFwLm9wdGlvbnMuY3JzO1xuXG5cdFx0aWYgKCFjcnMuaW5maW5pdGUpIHtcblx0XHRcdC8vIGRvbid0IGxvYWQgdGlsZSBpZiBpdCdzIG91dCBvZiBib3VuZHMgYW5kIG5vdCB3cmFwcGVkXG5cdFx0XHR2YXIgYm91bmRzID0gdGhpcy5fZ2xvYmFsVGlsZVJhbmdlO1xuXHRcdFx0aWYgKCghY3JzLndyYXBMbmcgJiYgKGNvb3Jkcy54IDwgYm91bmRzLm1pbi54IHx8IGNvb3Jkcy54ID4gYm91bmRzLm1heC54KSkgfHxcblx0XHRcdCAgICAoIWNycy53cmFwTGF0ICYmIChjb29yZHMueSA8IGJvdW5kcy5taW4ueSB8fCBjb29yZHMueSA+IGJvdW5kcy5tYXgueSkpKSB7IHJldHVybiBmYWxzZTsgfVxuXHRcdH1cblxuXHRcdGlmICghdGhpcy5vcHRpb25zLmJvdW5kcykgeyByZXR1cm4gdHJ1ZTsgfVxuXG5cdFx0Ly8gZG9uJ3QgbG9hZCB0aWxlIGlmIGl0IGRvZXNuJ3QgaW50ZXJzZWN0IHRoZSBib3VuZHMgaW4gb3B0aW9uc1xuXHRcdHZhciB0aWxlQm91bmRzID0gdGhpcy5fdGlsZUNvb3Jkc1RvQm91bmRzKGNvb3Jkcyk7XG5cdFx0cmV0dXJuIEwubGF0TG5nQm91bmRzKHRoaXMub3B0aW9ucy5ib3VuZHMpLm92ZXJsYXBzKHRpbGVCb3VuZHMpO1xuXHR9LFxuXG5cdF9rZXlUb0JvdW5kczogZnVuY3Rpb24gKGtleSkge1xuXHRcdHJldHVybiB0aGlzLl90aWxlQ29vcmRzVG9Cb3VuZHModGhpcy5fa2V5VG9UaWxlQ29vcmRzKGtleSkpO1xuXHR9LFxuXG5cdC8vIGNvbnZlcnRzIHRpbGUgY29vcmRpbmF0ZXMgdG8gaXRzIGdlb2dyYXBoaWNhbCBib3VuZHNcblx0X3RpbGVDb29yZHNUb0JvdW5kczogZnVuY3Rpb24gKGNvb3Jkcykge1xuXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCksXG5cblx0XHQgICAgbndQb2ludCA9IGNvb3Jkcy5zY2FsZUJ5KHRpbGVTaXplKSxcblx0XHQgICAgc2VQb2ludCA9IG53UG9pbnQuYWRkKHRpbGVTaXplKSxcblxuXHRcdCAgICBudyA9IG1hcC51bnByb2plY3QobndQb2ludCwgY29vcmRzLnopLFxuXHRcdCAgICBzZSA9IG1hcC51bnByb2plY3Qoc2VQb2ludCwgY29vcmRzLnopO1xuXG5cdFx0aWYgKCF0aGlzLm9wdGlvbnMubm9XcmFwKSB7XG5cdFx0XHRudyA9IG1hcC53cmFwTGF0TG5nKG53KTtcblx0XHRcdHNlID0gbWFwLndyYXBMYXRMbmcoc2UpO1xuXHRcdH1cblxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmdCb3VuZHMobncsIHNlKTtcblx0fSxcblxuXHQvLyBjb252ZXJ0cyB0aWxlIGNvb3JkaW5hdGVzIHRvIGtleSBmb3IgdGhlIHRpbGUgY2FjaGVcblx0X3RpbGVDb29yZHNUb0tleTogZnVuY3Rpb24gKGNvb3Jkcykge1xuXHRcdHJldHVybiBjb29yZHMueCArICc6JyArIGNvb3Jkcy55ICsgJzonICsgY29vcmRzLno7XG5cdH0sXG5cblx0Ly8gY29udmVydHMgdGlsZSBjYWNoZSBrZXkgdG8gY29vcmRpbmF0ZXNcblx0X2tleVRvVGlsZUNvb3JkczogZnVuY3Rpb24gKGtleSkge1xuXHRcdHZhciBrID0ga2V5LnNwbGl0KCc6JyksXG5cdFx0ICAgIGNvb3JkcyA9IG5ldyBMLlBvaW50KCtrWzBdLCAra1sxXSk7XG5cdFx0Y29vcmRzLnogPSAra1syXTtcblx0XHRyZXR1cm4gY29vcmRzO1xuXHR9LFxuXG5cdF9yZW1vdmVUaWxlOiBmdW5jdGlvbiAoa2V5KSB7XG5cdFx0dmFyIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXHRcdGlmICghdGlsZSkgeyByZXR1cm47IH1cblxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGlsZS5lbCk7XG5cblx0XHRkZWxldGUgdGhpcy5fdGlsZXNba2V5XTtcblxuXHRcdC8vIEBldmVudCB0aWxldW5sb2FkOiBUaWxlRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIGEgdGlsZSBpcyByZW1vdmVkIChlLmcuIHdoZW4gYSB0aWxlIGdvZXMgb2ZmIHRoZSBzY3JlZW4pLlxuXHRcdHRoaXMuZmlyZSgndGlsZXVubG9hZCcsIHtcblx0XHRcdHRpbGU6IHRpbGUuZWwsXG5cdFx0XHRjb29yZHM6IHRoaXMuX2tleVRvVGlsZUNvb3JkcyhrZXkpXG5cdFx0fSk7XG5cdH0sXG5cblx0X2luaXRUaWxlOiBmdW5jdGlvbiAodGlsZSkge1xuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aWxlLCAnbGVhZmxldC10aWxlJyk7XG5cblx0XHR2YXIgdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCk7XG5cdFx0dGlsZS5zdHlsZS53aWR0aCA9IHRpbGVTaXplLnggKyAncHgnO1xuXHRcdHRpbGUuc3R5bGUuaGVpZ2h0ID0gdGlsZVNpemUueSArICdweCc7XG5cblx0XHR0aWxlLm9uc2VsZWN0c3RhcnQgPSBMLlV0aWwuZmFsc2VGbjtcblx0XHR0aWxlLm9ubW91c2Vtb3ZlID0gTC5VdGlsLmZhbHNlRm47XG5cblx0XHQvLyB1cGRhdGUgb3BhY2l0eSBvbiB0aWxlcyBpbiBJRTctOCBiZWNhdXNlIG9mIGZpbHRlciBpbmhlcml0YW5jZSBwcm9ibGVtc1xuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkgJiYgdGhpcy5vcHRpb25zLm9wYWNpdHkgPCAxKSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aWxlLCB0aGlzLm9wdGlvbnMub3BhY2l0eSk7XG5cdFx0fVxuXG5cdFx0Ly8gd2l0aG91dCB0aGlzIGhhY2ssIHRpbGVzIGRpc2FwcGVhciBhZnRlciB6b29tIG9uIENocm9tZSBmb3IgQW5kcm9pZFxuXHRcdC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzIwNzhcblx0XHRpZiAoTC5Ccm93c2VyLmFuZHJvaWQgJiYgIUwuQnJvd3Nlci5hbmRyb2lkMjMpIHtcblx0XHRcdHRpbGUuc3R5bGUuV2Via2l0QmFja2ZhY2VWaXNpYmlsaXR5ID0gJ2hpZGRlbic7XG5cdFx0fVxuXHR9LFxuXG5cdF9hZGRUaWxlOiBmdW5jdGlvbiAoY29vcmRzLCBjb250YWluZXIpIHtcblx0XHR2YXIgdGlsZVBvcyA9IHRoaXMuX2dldFRpbGVQb3MoY29vcmRzKSxcblx0XHQgICAga2V5ID0gdGhpcy5fdGlsZUNvb3Jkc1RvS2V5KGNvb3Jkcyk7XG5cblx0XHR2YXIgdGlsZSA9IHRoaXMuY3JlYXRlVGlsZSh0aGlzLl93cmFwQ29vcmRzKGNvb3JkcyksIEwuYmluZCh0aGlzLl90aWxlUmVhZHksIHRoaXMsIGNvb3JkcykpO1xuXG5cdFx0dGhpcy5faW5pdFRpbGUodGlsZSk7XG5cblx0XHQvLyBpZiBjcmVhdGVUaWxlIGlzIGRlZmluZWQgd2l0aCBhIHNlY29uZCBhcmd1bWVudCAoXCJkb25lXCIgY2FsbGJhY2spLFxuXHRcdC8vIHdlIGtub3cgdGhhdCB0aWxlIGlzIGFzeW5jIGFuZCB3aWxsIGJlIHJlYWR5IGxhdGVyOyBvdGhlcndpc2Vcblx0XHRpZiAodGhpcy5jcmVhdGVUaWxlLmxlbmd0aCA8IDIpIHtcblx0XHRcdC8vIG1hcmsgdGlsZSBhcyByZWFkeSwgYnV0IGRlbGF5IG9uZSBmcmFtZSBmb3Igb3BhY2l0eSBhbmltYXRpb24gdG8gaGFwcGVuXG5cdFx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShMLmJpbmQodGhpcy5fdGlsZVJlYWR5LCB0aGlzLCBjb29yZHMsIG51bGwsIHRpbGUpKTtcblx0XHR9XG5cblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGlsZSwgdGlsZVBvcyk7XG5cblx0XHQvLyBzYXZlIHRpbGUgaW4gY2FjaGVcblx0XHR0aGlzLl90aWxlc1trZXldID0ge1xuXHRcdFx0ZWw6IHRpbGUsXG5cdFx0XHRjb29yZHM6IGNvb3Jkcyxcblx0XHRcdGN1cnJlbnQ6IHRydWVcblx0XHR9O1xuXG5cdFx0Y29udGFpbmVyLmFwcGVuZENoaWxkKHRpbGUpO1xuXHRcdC8vIEBldmVudCB0aWxlbG9hZHN0YXJ0OiBUaWxlRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIGEgdGlsZSBpcyByZXF1ZXN0ZWQgYW5kIHN0YXJ0cyBsb2FkaW5nLlxuXHRcdHRoaXMuZmlyZSgndGlsZWxvYWRzdGFydCcsIHtcblx0XHRcdHRpbGU6IHRpbGUsXG5cdFx0XHRjb29yZHM6IGNvb3Jkc1xuXHRcdH0pO1xuXHR9LFxuXG5cdF90aWxlUmVhZHk6IGZ1bmN0aW9uIChjb29yZHMsIGVyciwgdGlsZSkge1xuXHRcdGlmICghdGhpcy5fbWFwKSB7IHJldHVybjsgfVxuXG5cdFx0aWYgKGVycikge1xuXHRcdFx0Ly8gQGV2ZW50IHRpbGVlcnJvcjogVGlsZUVycm9yRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlcmUgaXMgYW4gZXJyb3IgbG9hZGluZyBhIHRpbGUuXG5cdFx0XHR0aGlzLmZpcmUoJ3RpbGVlcnJvcicsIHtcblx0XHRcdFx0ZXJyb3I6IGVycixcblx0XHRcdFx0dGlsZTogdGlsZSxcblx0XHRcdFx0Y29vcmRzOiBjb29yZHNcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKTtcblxuXHRcdHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXHRcdGlmICghdGlsZSkgeyByZXR1cm47IH1cblxuXHRcdHRpbGUubG9hZGVkID0gK25ldyBEYXRlKCk7XG5cdFx0aWYgKHRoaXMuX21hcC5fZmFkZUFuaW1hdGVkKSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aWxlLmVsLCAwKTtcblx0XHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fZmFkZUZyYW1lKTtcblx0XHRcdHRoaXMuX2ZhZGVGcmFtZSA9IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX3VwZGF0ZU9wYWNpdHksIHRoaXMpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aWxlLmFjdGl2ZSA9IHRydWU7XG5cdFx0XHR0aGlzLl9wcnVuZVRpbGVzKCk7XG5cdFx0fVxuXG5cdFx0aWYgKCFlcnIpIHtcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aWxlLmVsLCAnbGVhZmxldC10aWxlLWxvYWRlZCcpO1xuXG5cdFx0XHQvLyBAZXZlbnQgdGlsZWxvYWQ6IFRpbGVFdmVudFxuXHRcdFx0Ly8gRmlyZWQgd2hlbiBhIHRpbGUgbG9hZHMuXG5cdFx0XHR0aGlzLmZpcmUoJ3RpbGVsb2FkJywge1xuXHRcdFx0XHR0aWxlOiB0aWxlLmVsLFxuXHRcdFx0XHRjb29yZHM6IGNvb3Jkc1xuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMuX25vVGlsZXNUb0xvYWQoKSkge1xuXHRcdFx0dGhpcy5fbG9hZGluZyA9IGZhbHNlO1xuXHRcdFx0Ly8gQGV2ZW50IGxvYWQ6IEV2ZW50XG5cdFx0XHQvLyBGaXJlZCB3aGVuIHRoZSBncmlkIGxheWVyIGxvYWRlZCBhbGwgdmlzaWJsZSB0aWxlcy5cblx0XHRcdHRoaXMuZmlyZSgnbG9hZCcpO1xuXG5cdFx0XHRpZiAoTC5Ccm93c2VyLmllbHQ5IHx8ICF0aGlzLl9tYXAuX2ZhZGVBbmltYXRlZCkge1xuXHRcdFx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZSh0aGlzLl9wcnVuZVRpbGVzLCB0aGlzKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdC8vIFdhaXQgYSBiaXQgbW9yZSB0aGFuIDAuMiBzZWNzICh0aGUgZHVyYXRpb24gb2YgdGhlIHRpbGUgZmFkZS1pbilcblx0XHRcdFx0Ly8gdG8gdHJpZ2dlciBhIHBydW5pbmcuXG5cdFx0XHRcdHNldFRpbWVvdXQoTC5iaW5kKHRoaXMuX3BydW5lVGlsZXMsIHRoaXMpLCAyNTApO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRfZ2V0VGlsZVBvczogZnVuY3Rpb24gKGNvb3Jkcykge1xuXHRcdHJldHVybiBjb29yZHMuc2NhbGVCeSh0aGlzLmdldFRpbGVTaXplKCkpLnN1YnRyYWN0KHRoaXMuX2xldmVsLm9yaWdpbik7XG5cdH0sXG5cblx0X3dyYXBDb29yZHM6IGZ1bmN0aW9uIChjb29yZHMpIHtcblx0XHR2YXIgbmV3Q29vcmRzID0gbmV3IEwuUG9pbnQoXG5cdFx0XHR0aGlzLl93cmFwWCA/IEwuVXRpbC53cmFwTnVtKGNvb3Jkcy54LCB0aGlzLl93cmFwWCkgOiBjb29yZHMueCxcblx0XHRcdHRoaXMuX3dyYXBZID8gTC5VdGlsLndyYXBOdW0oY29vcmRzLnksIHRoaXMuX3dyYXBZKSA6IGNvb3Jkcy55KTtcblx0XHRuZXdDb29yZHMueiA9IGNvb3Jkcy56O1xuXHRcdHJldHVybiBuZXdDb29yZHM7XG5cdH0sXG5cblx0X3B4Qm91bmRzVG9UaWxlUmFuZ2U6IGZ1bmN0aW9uIChib3VuZHMpIHtcblx0XHR2YXIgdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCk7XG5cdFx0cmV0dXJuIG5ldyBMLkJvdW5kcyhcblx0XHRcdGJvdW5kcy5taW4udW5zY2FsZUJ5KHRpbGVTaXplKS5mbG9vcigpLFxuXHRcdFx0Ym91bmRzLm1heC51bnNjYWxlQnkodGlsZVNpemUpLmNlaWwoKS5zdWJ0cmFjdChbMSwgMV0pKTtcblx0fSxcblxuXHRfbm9UaWxlc1RvTG9hZDogZnVuY3Rpb24gKCkge1xuXHRcdGZvciAodmFyIGtleSBpbiB0aGlzLl90aWxlcykge1xuXHRcdFx0aWYgKCF0aGlzLl90aWxlc1trZXldLmxvYWRlZCkgeyByZXR1cm4gZmFsc2U7IH1cblx0XHR9XG5cdFx0cmV0dXJuIHRydWU7XG5cdH1cbn0pO1xuXG4vLyBAZmFjdG9yeSBMLmdyaWRMYXllcihvcHRpb25zPzogR3JpZExheWVyIG9wdGlvbnMpXG4vLyBDcmVhdGVzIGEgbmV3IGluc3RhbmNlIG9mIEdyaWRMYXllciB3aXRoIHRoZSBzdXBwbGllZCBvcHRpb25zLlxuTC5ncmlkTGF5ZXIgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuR3JpZExheWVyKG9wdGlvbnMpO1xufTtcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBUaWxlTGF5ZXJcclxuICogQGluaGVyaXRzIEdyaWRMYXllclxyXG4gKiBAYWthIEwuVGlsZUxheWVyXHJcbiAqIFVzZWQgdG8gbG9hZCBhbmQgZGlzcGxheSB0aWxlIGxheWVycyBvbiB0aGUgbWFwLiBFeHRlbmRzIGBHcmlkTGF5ZXJgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLnRpbGVMYXllcignaHR0cDovL3tzfS50aWxlLm9zbS5vcmcve3p9L3t4fS97eX0ucG5nP3tmb299Jywge2ZvbzogJ2Jhcid9KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICpcclxuICogQHNlY3Rpb24gVVJMIHRlbXBsYXRlXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIEEgc3RyaW5nIG9mIHRoZSBmb2xsb3dpbmcgZm9ybTpcclxuICpcclxuICogYGBgXHJcbiAqICdodHRwOi8ve3N9LnNvbWVkb21haW4uY29tL2JsYWJsYS97en0ve3h9L3t5fXtyfS5wbmcnXHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBge3N9YCBtZWFucyBvbmUgb2YgdGhlIGF2YWlsYWJsZSBzdWJkb21haW5zICh1c2VkIHNlcXVlbnRpYWxseSB0byBoZWxwIHdpdGggYnJvd3NlciBwYXJhbGxlbCByZXF1ZXN0cyBwZXIgZG9tYWluIGxpbWl0YXRpb247IHN1YmRvbWFpbiB2YWx1ZXMgYXJlIHNwZWNpZmllZCBpbiBvcHRpb25zOyBgYWAsIGBiYCBvciBgY2AgYnkgZGVmYXVsdCwgY2FuIGJlIG9taXR0ZWQpLCBge3p9YCDigJQgem9vbSBsZXZlbCwgYHt4fWAgYW5kIGB7eX1gIOKAlCB0aWxlIGNvb3JkaW5hdGVzLiBge3J9YCBjYW4gYmUgdXNlZCB0byBhZGQgQDJ4IHRvIHRoZSBVUkwgdG8gbG9hZCByZXRpbmEgdGlsZXMuXHJcbiAqXHJcbiAqIFlvdSBjYW4gdXNlIGN1c3RvbSBrZXlzIGluIHRoZSB0ZW1wbGF0ZSwgd2hpY2ggd2lsbCBiZSBbZXZhbHVhdGVkXSgjdXRpbC10ZW1wbGF0ZSkgZnJvbSBUaWxlTGF5ZXIgb3B0aW9ucywgbGlrZSB0aGlzOlxyXG4gKlxyXG4gKiBgYGBcclxuICogTC50aWxlTGF5ZXIoJ2h0dHA6Ly97c30uc29tZWRvbWFpbi5jb20ve2Zvb30ve3p9L3t4fS97eX0ucG5nJywge2ZvbzogJ2Jhcid9KTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuXHJcbkwuVGlsZUxheWVyID0gTC5HcmlkTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFRpbGVMYXllciBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBtaW5ab29tOiBOdW1iZXIgPSAwXHJcblx0XHQvLyBNaW5pbXVtIHpvb20gbnVtYmVyLlxyXG5cdFx0bWluWm9vbTogMCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1heFpvb206IE51bWJlciA9IDE4XHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbnVtYmVyLlxyXG5cdFx0bWF4Wm9vbTogMTgsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhOYXRpdmVab29tOiBOdW1iZXIgPSBudWxsXHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbnVtYmVyIHRoZSB0aWxlIHNvdXJjZSBoYXMgYXZhaWxhYmxlLiBJZiBpdCBpcyBzcGVjaWZpZWQsXHJcblx0XHQvLyB0aGUgdGlsZXMgb24gYWxsIHpvb20gbGV2ZWxzIGhpZ2hlciB0aGFuIGBtYXhOYXRpdmVab29tYCB3aWxsIGJlIGxvYWRlZFxyXG5cdFx0Ly8gZnJvbSBgbWF4TmF0aXZlWm9vbWAgbGV2ZWwgYW5kIGF1dG8tc2NhbGVkLlxyXG5cdFx0bWF4TmF0aXZlWm9vbTogbnVsbCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1pbk5hdGl2ZVpvb206IE51bWJlciA9IG51bGxcclxuXHRcdC8vIE1pbmltdW0gem9vbSBudW1iZXIgdGhlIHRpbGUgc291cmNlIGhhcyBhdmFpbGFibGUuIElmIGl0IGlzIHNwZWNpZmllZCxcclxuXHRcdC8vIHRoZSB0aWxlcyBvbiBhbGwgem9vbSBsZXZlbHMgbG93ZXIgdGhhbiBgbWluTmF0aXZlWm9vbWAgd2lsbCBiZSBsb2FkZWRcclxuXHRcdC8vIGZyb20gYG1pbk5hdGl2ZVpvb21gIGxldmVsIGFuZCBhdXRvLXNjYWxlZC5cclxuXHRcdG1pbk5hdGl2ZVpvb206IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBzdWJkb21haW5zOiBTdHJpbmd8U3RyaW5nW10gPSAnYWJjJ1xyXG5cdFx0Ly8gU3ViZG9tYWlucyBvZiB0aGUgdGlsZSBzZXJ2aWNlLiBDYW4gYmUgcGFzc2VkIGluIHRoZSBmb3JtIG9mIG9uZSBzdHJpbmcgKHdoZXJlIGVhY2ggbGV0dGVyIGlzIGEgc3ViZG9tYWluIG5hbWUpIG9yIGFuIGFycmF5IG9mIHN0cmluZ3MuXHJcblx0XHRzdWJkb21haW5zOiAnYWJjJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGVycm9yVGlsZVVybDogU3RyaW5nID0gJydcclxuXHRcdC8vIFVSTCB0byB0aGUgdGlsZSBpbWFnZSB0byBzaG93IGluIHBsYWNlIG9mIHRoZSB0aWxlIHRoYXQgZmFpbGVkIHRvIGxvYWQuXHJcblx0XHRlcnJvclRpbGVVcmw6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbU9mZnNldDogTnVtYmVyID0gMFxyXG5cdFx0Ly8gVGhlIHpvb20gbnVtYmVyIHVzZWQgaW4gdGlsZSBVUkxzIHdpbGwgYmUgb2Zmc2V0IHdpdGggdGhpcyB2YWx1ZS5cclxuXHRcdHpvb21PZmZzZXQ6IDAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0bXM6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCBpbnZlcnNlcyBZIGF4aXMgbnVtYmVyaW5nIGZvciB0aWxlcyAodHVybiB0aGlzIG9uIGZvciBbVE1TXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9UaWxlX01hcF9TZXJ2aWNlKSBzZXJ2aWNlcykuXHJcblx0XHR0bXM6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbVJldmVyc2U6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgc2V0IHRvIHRydWUsIHRoZSB6b29tIG51bWJlciB1c2VkIGluIHRpbGUgVVJMcyB3aWxsIGJlIHJldmVyc2VkIChgbWF4Wm9vbSAtIHpvb21gIGluc3RlYWQgb2YgYHpvb21gKVxyXG5cdFx0em9vbVJldmVyc2U6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gZGV0ZWN0UmV0aW5hOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCBhbmQgdXNlciBpcyBvbiBhIHJldGluYSBkaXNwbGF5LCBpdCB3aWxsIHJlcXVlc3QgZm91ciB0aWxlcyBvZiBoYWxmIHRoZSBzcGVjaWZpZWQgc2l6ZSBhbmQgYSBiaWdnZXIgem9vbSBsZXZlbCBpbiBwbGFjZSBvZiBvbmUgdG8gdXRpbGl6ZSB0aGUgaGlnaCByZXNvbHV0aW9uLlxyXG5cdFx0ZGV0ZWN0UmV0aW5hOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNyb3NzT3JpZ2luOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIHRydWUsIGFsbCB0aWxlcyB3aWxsIGhhdmUgdGhlaXIgY3Jvc3NPcmlnaW4gYXR0cmlidXRlIHNldCB0byAnJy4gVGhpcyBpcyBuZWVkZWQgaWYgeW91IHdhbnQgdG8gYWNjZXNzIHRpbGUgcGl4ZWwgZGF0YS5cclxuXHRcdGNyb3NzT3JpZ2luOiBmYWxzZVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblxyXG5cdFx0b3B0aW9ucyA9IEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHJcblx0XHQvLyBkZXRlY3RpbmcgcmV0aW5hIGRpc3BsYXlzLCBhZGp1c3RpbmcgdGlsZVNpemUgYW5kIHpvb20gbGV2ZWxzXHJcblx0XHRpZiAob3B0aW9ucy5kZXRlY3RSZXRpbmEgJiYgTC5Ccm93c2VyLnJldGluYSAmJiBvcHRpb25zLm1heFpvb20gPiAwKSB7XHJcblxyXG5cdFx0XHRvcHRpb25zLnRpbGVTaXplID0gTWF0aC5mbG9vcihvcHRpb25zLnRpbGVTaXplIC8gMik7XHJcblxyXG5cdFx0XHRpZiAoIW9wdGlvbnMuem9vbVJldmVyc2UpIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb21PZmZzZXQrKztcclxuXHRcdFx0XHRvcHRpb25zLm1heFpvb20tLTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb21PZmZzZXQtLTtcclxuXHRcdFx0XHRvcHRpb25zLm1pblpvb20rKztcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0b3B0aW9ucy5taW5ab29tID0gTWF0aC5tYXgoMCwgb3B0aW9ucy5taW5ab29tKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodHlwZW9mIG9wdGlvbnMuc3ViZG9tYWlucyA9PT0gJ3N0cmluZycpIHtcclxuXHRcdFx0b3B0aW9ucy5zdWJkb21haW5zID0gb3B0aW9ucy5zdWJkb21haW5zLnNwbGl0KCcnKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBmb3IgaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMTM3XHJcblx0XHRpZiAoIUwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdHRoaXMub24oJ3RpbGV1bmxvYWQnLCB0aGlzLl9vblRpbGVSZW1vdmUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0VXJsKHVybDogU3RyaW5nLCBub1JlZHJhdz86IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gVXBkYXRlcyB0aGUgbGF5ZXIncyBVUkwgdGVtcGxhdGUgYW5kIHJlZHJhd3MgaXQgKHVubGVzcyBgbm9SZWRyYXdgIGlzIHNldCB0byBgdHJ1ZWApLlxyXG5cdHNldFVybDogZnVuY3Rpb24gKHVybCwgbm9SZWRyYXcpIHtcclxuXHRcdHRoaXMuX3VybCA9IHVybDtcclxuXHJcblx0XHRpZiAoIW5vUmVkcmF3KSB7XHJcblx0XHRcdHRoaXMucmVkcmF3KCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNyZWF0ZVRpbGUoY29vcmRzOiBPYmplY3QsIGRvbmU/OiBGdW5jdGlvbik6IEhUTUxFbGVtZW50XHJcblx0Ly8gQ2FsbGVkIG9ubHkgaW50ZXJuYWxseSwgb3ZlcnJpZGVzIEdyaWRMYXllcidzIFtgY3JlYXRlVGlsZSgpYF0oI2dyaWRsYXllci1jcmVhdGV0aWxlKVxyXG5cdC8vIHRvIHJldHVybiBhbiBgPGltZz5gIEhUTUwgZWxlbWVudCB3aXRoIHRoZSBhcHByb3BpYXRlIGltYWdlIFVSTCBnaXZlbiBgY29vcmRzYC4gVGhlIGBkb25lYFxyXG5cdC8vIGNhbGxiYWNrIGlzIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBiZWVuIGxvYWRlZC5cclxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoY29vcmRzLCBkb25lKSB7XHJcblx0XHR2YXIgdGlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnQub24odGlsZSwgJ2xvYWQnLCBMLmJpbmQodGhpcy5fdGlsZU9uTG9hZCwgdGhpcywgZG9uZSwgdGlsZSkpO1xyXG5cdFx0TC5Eb21FdmVudC5vbih0aWxlLCAnZXJyb3InLCBMLmJpbmQodGhpcy5fdGlsZU9uRXJyb3IsIHRoaXMsIGRvbmUsIHRpbGUpKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmNyb3NzT3JpZ2luKSB7XHJcblx0XHRcdHRpbGUuY3Jvc3NPcmlnaW4gPSAnJztcclxuXHRcdH1cclxuXHJcblx0XHQvKlxyXG5cdFx0IEFsdCB0YWcgaXMgc2V0IHRvIGVtcHR5IHN0cmluZyB0byBrZWVwIHNjcmVlbiByZWFkZXJzIGZyb20gcmVhZGluZyBVUkwgYW5kIGZvciBjb21wbGlhbmNlIHJlYXNvbnNcclxuXHRcdCBodHRwOi8vd3d3LnczLm9yZy9UUi9XQ0FHMjAtVEVDSFMvSDY3XHJcblx0XHQqL1xyXG5cdFx0dGlsZS5hbHQgPSAnJztcclxuXHJcblx0XHQvKlxyXG5cdFx0IFNldCByb2xlPVwicHJlc2VudGF0aW9uXCIgdG8gZm9yY2Ugc2NyZWVuIHJlYWRlcnMgdG8gaWdub3JlIHRoaXNcclxuXHRcdCBodHRwczovL3d3dy53My5vcmcvVFIvd2FpLWFyaWEvcm9sZXMjdGV4dGFsdGVybmF0aXZlY29tcHV0YXRpb25cclxuXHRcdCovXHJcblx0XHR0aWxlLnNldEF0dHJpYnV0ZSgncm9sZScsICdwcmVzZW50YXRpb24nKTtcclxuXHJcblx0XHR0aWxlLnNyYyA9IHRoaXMuZ2V0VGlsZVVybChjb29yZHMpO1xyXG5cclxuXHRcdHJldHVybiB0aWxlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXHJcblx0Ly8gQHVuaW5oZXJpdGFibGVcclxuXHQvLyBMYXllcnMgZXh0ZW5kaW5nIGBUaWxlTGF5ZXJgIG1pZ2h0IHJlaW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgbWV0aG9kLlxyXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVVybChjb29yZHM6IE9iamVjdCk6IFN0cmluZ1xyXG5cdC8vIENhbGxlZCBvbmx5IGludGVybmFsbHksIHJldHVybnMgdGhlIFVSTCBmb3IgYSB0aWxlIGdpdmVuIGl0cyBjb29yZGluYXRlcy5cclxuXHQvLyBDbGFzc2VzIGV4dGVuZGluZyBgVGlsZUxheWVyYCBjYW4gb3ZlcnJpZGUgdGhpcyBmdW5jdGlvbiB0byBwcm92aWRlIGN1c3RvbSB0aWxlIFVSTCBuYW1pbmcgc2NoZW1lcy5cclxuXHRnZXRUaWxlVXJsOiBmdW5jdGlvbiAoY29vcmRzKSB7XHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0cjogTC5Ccm93c2VyLnJldGluYSA/ICdAMngnIDogJycsXHJcblx0XHRcdHM6IHRoaXMuX2dldFN1YmRvbWFpbihjb29yZHMpLFxyXG5cdFx0XHR4OiBjb29yZHMueCxcclxuXHRcdFx0eTogY29vcmRzLnksXHJcblx0XHRcdHo6IHRoaXMuX2dldFpvb21Gb3JVcmwoKVxyXG5cdFx0fTtcclxuXHRcdGlmICh0aGlzLl9tYXAgJiYgIXRoaXMuX21hcC5vcHRpb25zLmNycy5pbmZpbml0ZSkge1xyXG5cdFx0XHR2YXIgaW52ZXJ0ZWRZID0gdGhpcy5fZ2xvYmFsVGlsZVJhbmdlLm1heC55IC0gY29vcmRzLnk7XHJcblx0XHRcdGlmICh0aGlzLm9wdGlvbnMudG1zKSB7XHJcblx0XHRcdFx0ZGF0YVsneSddID0gaW52ZXJ0ZWRZO1xyXG5cdFx0XHR9XHJcblx0XHRcdGRhdGFbJy15J10gPSBpbnZlcnRlZFk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIEwuVXRpbC50ZW1wbGF0ZSh0aGlzLl91cmwsIEwuZXh0ZW5kKGRhdGEsIHRoaXMub3B0aW9ucykpO1xyXG5cdH0sXHJcblxyXG5cdF90aWxlT25Mb2FkOiBmdW5jdGlvbiAoZG9uZSwgdGlsZSkge1xyXG5cdFx0Ly8gRm9yIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzMzMzJcclxuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkpIHtcclxuXHRcdFx0c2V0VGltZW91dChMLmJpbmQoZG9uZSwgdGhpcywgbnVsbCwgdGlsZSksIDApO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZG9uZShudWxsLCB0aWxlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfdGlsZU9uRXJyb3I6IGZ1bmN0aW9uIChkb25lLCB0aWxlLCBlKSB7XHJcblx0XHR2YXIgZXJyb3JVcmwgPSB0aGlzLm9wdGlvbnMuZXJyb3JUaWxlVXJsO1xyXG5cdFx0aWYgKGVycm9yVXJsKSB7XHJcblx0XHRcdHRpbGUuc3JjID0gZXJyb3JVcmw7XHJcblx0XHR9XHJcblx0XHRkb25lKGUsIHRpbGUpO1xyXG5cdH0sXHJcblxyXG5cdGdldFRpbGVTaXplOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxyXG5cdFx0dGlsZVNpemUgPSBMLkdyaWRMYXllci5wcm90b3R5cGUuZ2V0VGlsZVNpemUuY2FsbCh0aGlzKSxcclxuXHRcdHpvb20gPSB0aGlzLl90aWxlWm9vbSArIHRoaXMub3B0aW9ucy56b29tT2Zmc2V0LFxyXG5cdFx0bWluTmF0aXZlWm9vbSA9IHRoaXMub3B0aW9ucy5taW5OYXRpdmVab29tLFxyXG5cdFx0bWF4TmF0aXZlWm9vbSA9IHRoaXMub3B0aW9ucy5tYXhOYXRpdmVab29tO1xyXG5cclxuXHRcdC8vIGRlY3JlYXNlIHRpbGUgc2l6ZSB3aGVuIHNjYWxpbmcgYmVsb3cgbWluTmF0aXZlWm9vbVxyXG5cdFx0aWYgKG1pbk5hdGl2ZVpvb20gIT09IG51bGwgJiYgem9vbSA8IG1pbk5hdGl2ZVpvb20pIHtcclxuXHRcdFx0cmV0dXJuIHRpbGVTaXplLmRpdmlkZUJ5KG1hcC5nZXRab29tU2NhbGUobWluTmF0aXZlWm9vbSwgem9vbSkpLnJvdW5kKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gaW5jcmVhc2UgdGlsZSBzaXplIHdoZW4gc2NhbGluZyBhYm92ZSBtYXhOYXRpdmVab29tXHJcblx0XHRpZiAobWF4TmF0aXZlWm9vbSAhPT0gbnVsbCAmJiB6b29tID4gbWF4TmF0aXZlWm9vbSkge1xyXG5cdFx0XHRyZXR1cm4gdGlsZVNpemUuZGl2aWRlQnkobWFwLmdldFpvb21TY2FsZShtYXhOYXRpdmVab29tLCB6b29tKSkucm91bmQoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGlsZVNpemU7XHJcblx0fSxcclxuXHJcblx0X29uVGlsZVJlbW92ZTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdGUudGlsZS5vbmxvYWQgPSBudWxsO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRab29tRm9yVXJsOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgem9vbSA9IHRoaXMuX3RpbGVab29tLFxyXG5cdFx0bWF4Wm9vbSA9IHRoaXMub3B0aW9ucy5tYXhab29tLFxyXG5cdFx0em9vbVJldmVyc2UgPSB0aGlzLm9wdGlvbnMuem9vbVJldmVyc2UsXHJcblx0XHR6b29tT2Zmc2V0ID0gdGhpcy5vcHRpb25zLnpvb21PZmZzZXQsXHJcblx0XHRtaW5OYXRpdmVab29tID0gdGhpcy5vcHRpb25zLm1pbk5hdGl2ZVpvb20sXHJcblx0XHRtYXhOYXRpdmVab29tID0gdGhpcy5vcHRpb25zLm1heE5hdGl2ZVpvb207XHJcblxyXG5cdFx0aWYgKHpvb21SZXZlcnNlKSB7XHJcblx0XHRcdHpvb20gPSBtYXhab29tIC0gem9vbTtcclxuXHRcdH1cclxuXHJcblx0XHR6b29tICs9IHpvb21PZmZzZXQ7XHJcblxyXG5cdFx0aWYgKG1pbk5hdGl2ZVpvb20gIT09IG51bGwgJiYgem9vbSA8IG1pbk5hdGl2ZVpvb20pIHtcclxuXHRcdFx0cmV0dXJuIG1pbk5hdGl2ZVpvb207XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG1heE5hdGl2ZVpvb20gIT09IG51bGwgJiYgem9vbSA+IG1heE5hdGl2ZVpvb20pIHtcclxuXHRcdFx0cmV0dXJuIG1heE5hdGl2ZVpvb207XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHpvb207XHJcblx0fSxcclxuXHJcblx0X2dldFN1YmRvbWFpbjogZnVuY3Rpb24gKHRpbGVQb2ludCkge1xyXG5cdFx0dmFyIGluZGV4ID0gTWF0aC5hYnModGlsZVBvaW50LnggKyB0aWxlUG9pbnQueSkgJSB0aGlzLm9wdGlvbnMuc3ViZG9tYWlucy5sZW5ndGg7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLnN1YmRvbWFpbnNbaW5kZXhdO1xyXG5cdH0sXHJcblxyXG5cdC8vIHN0b3BzIGxvYWRpbmcgYWxsIHRpbGVzIGluIHRoZSBiYWNrZ3JvdW5kIGxheWVyXHJcblx0X2Fib3J0TG9hZGluZzogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGksIHRpbGU7XHJcblx0XHRmb3IgKGkgaW4gdGhpcy5fdGlsZXMpIHtcclxuXHRcdFx0aWYgKHRoaXMuX3RpbGVzW2ldLmNvb3Jkcy56ICE9PSB0aGlzLl90aWxlWm9vbSkge1xyXG5cdFx0XHRcdHRpbGUgPSB0aGlzLl90aWxlc1tpXS5lbDtcclxuXHJcblx0XHRcdFx0dGlsZS5vbmxvYWQgPSBMLlV0aWwuZmFsc2VGbjtcclxuXHRcdFx0XHR0aWxlLm9uZXJyb3IgPSBMLlV0aWwuZmFsc2VGbjtcclxuXHJcblx0XHRcdFx0aWYgKCF0aWxlLmNvbXBsZXRlKSB7XHJcblx0XHRcdFx0XHR0aWxlLnNyYyA9IEwuVXRpbC5lbXB0eUltYWdlVXJsO1xyXG5cdFx0XHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aWxlKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9XHJcbn0pO1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwudGlsZWxheWVyKHVybFRlbXBsYXRlOiBTdHJpbmcsIG9wdGlvbnM/OiBUaWxlTGF5ZXIgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGEgdGlsZSBsYXllciBvYmplY3QgZ2l2ZW4gYSBgVVJMIHRlbXBsYXRlYCBhbmQgb3B0aW9uYWxseSBhbiBvcHRpb25zIG9iamVjdC5cclxuXHJcbkwudGlsZUxheWVyID0gZnVuY3Rpb24gKHVybCwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5UaWxlTGF5ZXIodXJsLCBvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIFRpbGVMYXllci5XTVNcclxuICogQGluaGVyaXRzIFRpbGVMYXllclxyXG4gKiBAYWthIEwuVGlsZUxheWVyLldNU1xyXG4gKiBVc2VkIHRvIGRpc3BsYXkgW1dNU10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvV2ViX01hcF9TZXJ2aWNlKSBzZXJ2aWNlcyBhcyB0aWxlIGxheWVycyBvbiB0aGUgbWFwLiBFeHRlbmRzIGBUaWxlTGF5ZXJgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgbmV4cmFkID0gTC50aWxlTGF5ZXIud21zKFwiaHR0cDovL21lc29uZXQuYWdyb24uaWFzdGF0ZS5lZHUvY2dpLWJpbi93bXMvbmV4cmFkL24wci5jZ2lcIiwge1xyXG4gKiBcdGxheWVyczogJ25leHJhZC1uMHItOTAwOTEzJyxcclxuICogXHRmb3JtYXQ6ICdpbWFnZS9wbmcnLFxyXG4gKiBcdHRyYW5zcGFyZW50OiB0cnVlLFxyXG4gKiBcdGF0dHJpYnV0aW9uOiBcIldlYXRoZXIgZGF0YSDCqSAyMDEyIElFTSBOZXhyYWRcIlxyXG4gKiB9KTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5UaWxlTGF5ZXIuV01TID0gTC5UaWxlTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFRpbGVMYXllci5XTVMgb3B0aW9uc1xyXG5cdC8vIElmIGFueSBjdXN0b20gb3B0aW9ucyBub3QgZG9jdW1lbnRlZCBoZXJlIGFyZSB1c2VkLCB0aGV5IHdpbGwgYmUgc2VudCB0byB0aGVcclxuXHQvLyBXTVMgc2VydmVyIGFzIGV4dHJhIHBhcmFtZXRlcnMgaW4gZWFjaCByZXF1ZXN0IFVSTC4gVGhpcyBjYW4gYmUgdXNlZnVsIGZvclxyXG5cdC8vIFtub24tc3RhbmRhcmQgdmVuZG9yIFdNUyBwYXJhbWV0ZXJzXShodHRwOi8vZG9jcy5nZW9zZXJ2ZXIub3JnL3N0YWJsZS9lbi91c2VyL3NlcnZpY2VzL3dtcy92ZW5kb3IuaHRtbCkuXHJcblx0ZGVmYXVsdFdtc1BhcmFtczoge1xyXG5cdFx0c2VydmljZTogJ1dNUycsXHJcblx0XHRyZXF1ZXN0OiAnR2V0TWFwJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGxheWVyczogU3RyaW5nID0gJydcclxuXHRcdC8vICoqKHJlcXVpcmVkKSoqIENvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIFdNUyBsYXllcnMgdG8gc2hvdy5cclxuXHRcdGxheWVyczogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBzdHlsZXM6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBDb21tYS1zZXBhcmF0ZWQgbGlzdCBvZiBXTVMgc3R5bGVzLlxyXG5cdFx0c3R5bGVzOiAnJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGZvcm1hdDogU3RyaW5nID0gJ2ltYWdlL2pwZWcnXHJcblx0XHQvLyBXTVMgaW1hZ2UgZm9ybWF0ICh1c2UgYCdpbWFnZS9wbmcnYCBmb3IgbGF5ZXJzIHdpdGggdHJhbnNwYXJlbmN5KS5cclxuXHRcdGZvcm1hdDogJ2ltYWdlL2pwZWcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdHJhbnNwYXJlbnQ6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCB0aGUgV01TIHNlcnZpY2Ugd2lsbCByZXR1cm4gaW1hZ2VzIHdpdGggdHJhbnNwYXJlbmN5LlxyXG5cdFx0dHJhbnNwYXJlbnQ6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdmVyc2lvbjogU3RyaW5nID0gJzEuMS4xJ1xyXG5cdFx0Ly8gVmVyc2lvbiBvZiB0aGUgV01TIHNlcnZpY2UgdG8gdXNlXHJcblx0XHR2ZXJzaW9uOiAnMS4xLjEnXHJcblx0fSxcclxuXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBjcnM6IENSUyA9IG51bGxcclxuXHRcdC8vIENvb3JkaW5hdGUgUmVmZXJlbmNlIFN5c3RlbSB0byB1c2UgZm9yIHRoZSBXTVMgcmVxdWVzdHMsIGRlZmF1bHRzIHRvXHJcblx0XHQvLyBtYXAgQ1JTLiBEb24ndCBjaGFuZ2UgdGhpcyBpZiB5b3UncmUgbm90IHN1cmUgd2hhdCBpdCBtZWFucy5cclxuXHRcdGNyczogbnVsbCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHVwcGVyY2FzZTogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIFdNUyByZXF1ZXN0IHBhcmFtZXRlciBrZXlzIHdpbGwgYmUgdXBwZXJjYXNlLlxyXG5cdFx0dXBwZXJjYXNlOiBmYWxzZVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblxyXG5cdFx0dmFyIHdtc1BhcmFtcyA9IEwuZXh0ZW5kKHt9LCB0aGlzLmRlZmF1bHRXbXNQYXJhbXMpO1xyXG5cclxuXHRcdC8vIGFsbCBrZXlzIHRoYXQgYXJlIG5vdCBUaWxlTGF5ZXIgb3B0aW9ucyBnbyB0byBXTVMgcGFyYW1zXHJcblx0XHRmb3IgKHZhciBpIGluIG9wdGlvbnMpIHtcclxuXHRcdFx0aWYgKCEoaSBpbiB0aGlzLm9wdGlvbnMpKSB7XHJcblx0XHRcdFx0d21zUGFyYW1zW2ldID0gb3B0aW9uc1tpXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdG9wdGlvbnMgPSBMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0d21zUGFyYW1zLndpZHRoID0gd21zUGFyYW1zLmhlaWdodCA9IG9wdGlvbnMudGlsZVNpemUgKiAob3B0aW9ucy5kZXRlY3RSZXRpbmEgJiYgTC5Ccm93c2VyLnJldGluYSA/IDIgOiAxKTtcclxuXHJcblx0XHR0aGlzLndtc1BhcmFtcyA9IHdtc1BhcmFtcztcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cclxuXHRcdHRoaXMuX2NycyA9IHRoaXMub3B0aW9ucy5jcnMgfHwgbWFwLm9wdGlvbnMuY3JzO1xyXG5cdFx0dGhpcy5fd21zVmVyc2lvbiA9IHBhcnNlRmxvYXQodGhpcy53bXNQYXJhbXMudmVyc2lvbik7XHJcblxyXG5cdFx0dmFyIHByb2plY3Rpb25LZXkgPSB0aGlzLl93bXNWZXJzaW9uID49IDEuMyA/ICdjcnMnIDogJ3Nycyc7XHJcblx0XHR0aGlzLndtc1BhcmFtc1twcm9qZWN0aW9uS2V5XSA9IHRoaXMuX2Nycy5jb2RlO1xyXG5cclxuXHRcdEwuVGlsZUxheWVyLnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMsIG1hcCk7XHJcblx0fSxcclxuXHJcblx0Z2V0VGlsZVVybDogZnVuY3Rpb24gKGNvb3Jkcykge1xyXG5cclxuXHRcdHZhciB0aWxlQm91bmRzID0gdGhpcy5fdGlsZUNvb3Jkc1RvQm91bmRzKGNvb3JkcyksXHJcblx0XHQgICAgbncgPSB0aGlzLl9jcnMucHJvamVjdCh0aWxlQm91bmRzLmdldE5vcnRoV2VzdCgpKSxcclxuXHRcdCAgICBzZSA9IHRoaXMuX2Nycy5wcm9qZWN0KHRpbGVCb3VuZHMuZ2V0U291dGhFYXN0KCkpLFxyXG5cclxuXHRcdCAgICBiYm94ID0gKHRoaXMuX3dtc1ZlcnNpb24gPj0gMS4zICYmIHRoaXMuX2NycyA9PT0gTC5DUlMuRVBTRzQzMjYgP1xyXG5cdFx0XHQgICAgW3NlLnksIG53LngsIG53LnksIHNlLnhdIDpcclxuXHRcdFx0ICAgIFtudy54LCBzZS55LCBzZS54LCBudy55XSkuam9pbignLCcpLFxyXG5cclxuXHRcdCAgICB1cmwgPSBMLlRpbGVMYXllci5wcm90b3R5cGUuZ2V0VGlsZVVybC5jYWxsKHRoaXMsIGNvb3Jkcyk7XHJcblxyXG5cdFx0cmV0dXJuIHVybCArXHJcblx0XHRcdEwuVXRpbC5nZXRQYXJhbVN0cmluZyh0aGlzLndtc1BhcmFtcywgdXJsLCB0aGlzLm9wdGlvbnMudXBwZXJjYXNlKSArXHJcblx0XHRcdCh0aGlzLm9wdGlvbnMudXBwZXJjYXNlID8gJyZCQk9YPScgOiAnJmJib3g9JykgKyBiYm94O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0UGFyYW1zKHBhcmFtczogT2JqZWN0LCBub1JlZHJhdz86IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gTWVyZ2VzIGFuIG9iamVjdCB3aXRoIHRoZSBuZXcgcGFyYW1ldGVycyBhbmQgcmUtcmVxdWVzdHMgdGlsZXMgb24gdGhlIGN1cnJlbnQgc2NyZWVuICh1bmxlc3MgYG5vUmVkcmF3YCB3YXMgc2V0IHRvIHRydWUpLlxyXG5cdHNldFBhcmFtczogZnVuY3Rpb24gKHBhcmFtcywgbm9SZWRyYXcpIHtcclxuXHJcblx0XHRMLmV4dGVuZCh0aGlzLndtc1BhcmFtcywgcGFyYW1zKTtcclxuXHJcblx0XHRpZiAoIW5vUmVkcmF3KSB7XHJcblx0XHRcdHRoaXMucmVkcmF3KCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fVxyXG59KTtcclxuXHJcblxyXG4vLyBAZmFjdG9yeSBMLnRpbGVMYXllci53bXMoYmFzZVVybDogU3RyaW5nLCBvcHRpb25zOiBUaWxlTGF5ZXIuV01TIG9wdGlvbnMpXHJcbi8vIEluc3RhbnRpYXRlcyBhIFdNUyB0aWxlIGxheWVyIG9iamVjdCBnaXZlbiBhIGJhc2UgVVJMIG9mIHRoZSBXTVMgc2VydmljZSBhbmQgYSBXTVMgcGFyYW1ldGVycy9vcHRpb25zIG9iamVjdC5cclxuTC50aWxlTGF5ZXIud21zID0gZnVuY3Rpb24gKHVybCwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5UaWxlTGF5ZXIuV01TKHVybCwgb3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBJbWFnZU92ZXJsYXlcclxuICogQGFrYSBMLkltYWdlT3ZlcmxheVxyXG4gKiBAaW5oZXJpdHMgSW50ZXJhY3RpdmUgbGF5ZXJcclxuICpcclxuICogVXNlZCB0byBsb2FkIGFuZCBkaXNwbGF5IGEgc2luZ2xlIGltYWdlIG92ZXIgc3BlY2lmaWMgYm91bmRzIG9mIHRoZSBtYXAuIEV4dGVuZHMgYExheWVyYC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIGltYWdlVXJsID0gJ2h0dHA6Ly93d3cubGliLnV0ZXhhcy5lZHUvbWFwcy9oaXN0b3JpY2FsL25ld2Fya19ual8xOTIyLmpwZycsXHJcbiAqIFx0aW1hZ2VCb3VuZHMgPSBbWzQwLjcxMjIxNiwgLTc0LjIyNjU1XSwgWzQwLjc3Mzk0MSwgLTc0LjEyNTQ0XV07XHJcbiAqIEwuaW1hZ2VPdmVybGF5KGltYWdlVXJsLCBpbWFnZUJvdW5kcykuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5JbWFnZU92ZXJsYXkgPSBMLkxheWVyLmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBJbWFnZU92ZXJsYXkgb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMS4wXHJcblx0XHQvLyBUaGUgb3BhY2l0eSBvZiB0aGUgaW1hZ2Ugb3ZlcmxheS5cclxuXHRcdG9wYWNpdHk6IDEsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhbHQ6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBUZXh0IGZvciB0aGUgYGFsdGAgYXR0cmlidXRlIG9mIHRoZSBpbWFnZSAodXNlZnVsIGZvciBhY2Nlc3NpYmlsaXR5KS5cclxuXHRcdGFsdDogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBpbnRlcmFjdGl2ZTogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBpbWFnZSBvdmVybGF5IHdpbGwgZW1pdCBbbW91c2UgZXZlbnRzXSgjaW50ZXJhY3RpdmUtbGF5ZXIpIHdoZW4gY2xpY2tlZCBvciBob3ZlcmVkLlxyXG5cdFx0aW50ZXJhY3RpdmU6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY3Jvc3NPcmlnaW46IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgdHJ1ZSwgdGhlIGltYWdlIHdpbGwgaGF2ZSBpdHMgY3Jvc3NPcmlnaW4gYXR0cmlidXRlIHNldCB0byAnJy4gVGhpcyBpcyBuZWVkZWQgaWYgeW91IHdhbnQgdG8gYWNjZXNzIGltYWdlIHBpeGVsIGRhdGEuXHJcblx0XHRjcm9zc09yaWdpbjogZmFsc2VcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAodXJsLCBib3VuZHMsIG9wdGlvbnMpIHsgLy8gKFN0cmluZywgTGF0TG5nQm91bmRzLCBPYmplY3QpXHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblx0XHR0aGlzLl9ib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9pbWFnZSkge1xyXG5cdFx0XHR0aGlzLl9pbml0SW1hZ2UoKTtcclxuXHJcblx0XHRcdGlmICh0aGlzLm9wdGlvbnMub3BhY2l0eSA8IDEpIHtcclxuXHRcdFx0XHR0aGlzLl91cGRhdGVPcGFjaXR5KCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmludGVyYWN0aXZlKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9pbWFnZSwgJ2xlYWZsZXQtaW50ZXJhY3RpdmUnKTtcclxuXHRcdFx0dGhpcy5hZGRJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl9pbWFnZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5nZXRQYW5lKCkuYXBwZW5kQ2hpbGQodGhpcy5faW1hZ2UpO1xyXG5cdFx0dGhpcy5fcmVzZXQoKTtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9pbWFnZSk7XHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmludGVyYWN0aXZlKSB7XHJcblx0XHRcdHRoaXMucmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQodGhpcy5faW1hZ2UpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0T3BhY2l0eShvcGFjaXR5OiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgb3BhY2l0eSBvZiB0aGUgb3ZlcmxheS5cclxuXHRzZXRPcGFjaXR5OiBmdW5jdGlvbiAob3BhY2l0eSkge1xyXG5cdFx0dGhpcy5vcHRpb25zLm9wYWNpdHkgPSBvcGFjaXR5O1xyXG5cclxuXHRcdGlmICh0aGlzLl9pbWFnZSkge1xyXG5cdFx0XHR0aGlzLl91cGRhdGVPcGFjaXR5KCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRzZXRTdHlsZTogZnVuY3Rpb24gKHN0eWxlT3B0cykge1xyXG5cdFx0aWYgKHN0eWxlT3B0cy5vcGFjaXR5KSB7XHJcblx0XHRcdHRoaXMuc2V0T3BhY2l0eShzdHlsZU9wdHMub3BhY2l0eSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGJyaW5nVG9Gcm9udCgpOiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoZSBsYXllciB0byB0aGUgdG9wIG9mIGFsbCBvdmVybGF5cy5cclxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0TC5Eb21VdGlsLnRvRnJvbnQodGhpcy5faW1hZ2UpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvQmFjaygpOiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoZSBsYXllciB0byB0aGUgYm90dG9tIG9mIGFsbCBvdmVybGF5cy5cclxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHRMLkRvbVV0aWwudG9CYWNrKHRoaXMuX2ltYWdlKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0VXJsKHVybDogU3RyaW5nKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgdGhlIFVSTCBvZiB0aGUgaW1hZ2UuXHJcblx0c2V0VXJsOiBmdW5jdGlvbiAodXJsKSB7XHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2ltYWdlKSB7XHJcblx0XHRcdHRoaXMuX2ltYWdlLnNyYyA9IHVybDtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdHNldEJvdW5kczogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0dGhpcy5fYm91bmRzID0gYm91bmRzO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fcmVzZXQoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGV2ZW50cyA9IHtcclxuXHRcdFx0em9vbTogdGhpcy5fcmVzZXQsXHJcblx0XHRcdHZpZXdyZXNldDogdGhpcy5fcmVzZXRcclxuXHRcdH07XHJcblxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9hbmltYXRlWm9vbTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gZXZlbnRzO1xyXG5cdH0sXHJcblxyXG5cdGdldEJvdW5kczogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2JvdW5kcztcclxuXHR9LFxyXG5cclxuXHRnZXRFbGVtZW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5faW1hZ2U7XHJcblx0fSxcclxuXHJcblx0X2luaXRJbWFnZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGltZyA9IHRoaXMuX2ltYWdlID0gTC5Eb21VdGlsLmNyZWF0ZSgnaW1nJyxcclxuXHRcdFx0XHQnbGVhZmxldC1pbWFnZS1sYXllciAnICsgKHRoaXMuX3pvb21BbmltYXRlZCA/ICdsZWFmbGV0LXpvb20tYW5pbWF0ZWQnIDogJycpKTtcclxuXHJcblx0XHRpbWcub25zZWxlY3RzdGFydCA9IEwuVXRpbC5mYWxzZUZuO1xyXG5cdFx0aW1nLm9ubW91c2Vtb3ZlID0gTC5VdGlsLmZhbHNlRm47XHJcblxyXG5cdFx0aW1nLm9ubG9hZCA9IEwuYmluZCh0aGlzLmZpcmUsIHRoaXMsICdsb2FkJyk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5jcm9zc09yaWdpbikge1xyXG5cdFx0XHRpbWcuY3Jvc3NPcmlnaW4gPSAnJztcclxuXHRcdH1cclxuXHJcblx0XHRpbWcuc3JjID0gdGhpcy5fdXJsO1xyXG5cdFx0aW1nLmFsdCA9IHRoaXMub3B0aW9ucy5hbHQ7XHJcblx0fSxcclxuXHJcblx0X2FuaW1hdGVab29tOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dmFyIHNjYWxlID0gdGhpcy5fbWFwLmdldFpvb21TY2FsZShlLnpvb20pLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX21hcC5fbGF0TG5nQm91bmRzVG9OZXdMYXllckJvdW5kcyh0aGlzLl9ib3VuZHMsIGUuem9vbSwgZS5jZW50ZXIpLm1pbjtcclxuXHJcblx0XHRMLkRvbVV0aWwuc2V0VHJhbnNmb3JtKHRoaXMuX2ltYWdlLCBvZmZzZXQsIHNjYWxlKTtcclxuXHR9LFxyXG5cclxuXHRfcmVzZXQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBpbWFnZSA9IHRoaXMuX2ltYWdlLFxyXG5cdFx0ICAgIGJvdW5kcyA9IG5ldyBMLkJvdW5kcyhcclxuXHRcdCAgICAgICAgdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0aGlzLl9ib3VuZHMuZ2V0Tm9ydGhXZXN0KCkpLFxyXG5cdFx0ICAgICAgICB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2JvdW5kcy5nZXRTb3V0aEVhc3QoKSkpLFxyXG5cdFx0ICAgIHNpemUgPSBib3VuZHMuZ2V0U2l6ZSgpO1xyXG5cclxuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbihpbWFnZSwgYm91bmRzLm1pbik7XHJcblxyXG5cdFx0aW1hZ2Uuc3R5bGUud2lkdGggID0gc2l6ZS54ICsgJ3B4JztcclxuXHRcdGltYWdlLnN0eWxlLmhlaWdodCA9IHNpemUueSArICdweCc7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZU9wYWNpdHk6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2ltYWdlLCB0aGlzLm9wdGlvbnMub3BhY2l0eSk7XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBmYWN0b3J5IEwuaW1hZ2VPdmVybGF5KGltYWdlVXJsOiBTdHJpbmcsIGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogSW1hZ2VPdmVybGF5IG9wdGlvbnMpXHJcbi8vIEluc3RhbnRpYXRlcyBhbiBpbWFnZSBvdmVybGF5IG9iamVjdCBnaXZlbiB0aGUgVVJMIG9mIHRoZSBpbWFnZSBhbmQgdGhlXHJcbi8vIGdlb2dyYXBoaWNhbCBib3VuZHMgaXQgaXMgdGllZCB0by5cclxuTC5pbWFnZU92ZXJsYXkgPSBmdW5jdGlvbiAodXJsLCBib3VuZHMsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuSW1hZ2VPdmVybGF5KHVybCwgYm91bmRzLCBvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIEljb25cclxuICogQGFrYSBMLkljb25cclxuICogQGluaGVyaXRzIExheWVyXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYW4gaWNvbiB0byBwcm92aWRlIHdoZW4gY3JlYXRpbmcgYSBtYXJrZXIuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBteUljb24gPSBMLmljb24oe1xyXG4gKiAgICAgaWNvblVybDogJ215LWljb24ucG5nJyxcclxuICogICAgIGljb25SZXRpbmFVcmw6ICdteS1pY29uQDJ4LnBuZycsXHJcbiAqICAgICBpY29uU2l6ZTogWzM4LCA5NV0sXHJcbiAqICAgICBpY29uQW5jaG9yOiBbMjIsIDk0XSxcclxuICogICAgIHBvcHVwQW5jaG9yOiBbLTMsIC03Nl0sXHJcbiAqICAgICBzaGFkb3dVcmw6ICdteS1pY29uLXNoYWRvdy5wbmcnLFxyXG4gKiAgICAgc2hhZG93UmV0aW5hVXJsOiAnbXktaWNvbi1zaGFkb3dAMngucG5nJyxcclxuICogICAgIHNoYWRvd1NpemU6IFs2OCwgOTVdLFxyXG4gKiAgICAgc2hhZG93QW5jaG9yOiBbMjIsIDk0XVxyXG4gKiB9KTtcclxuICpcclxuICogTC5tYXJrZXIoWzUwLjUwNSwgMzAuNTddLCB7aWNvbjogbXlJY29ufSkuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIGBMLkljb24uRGVmYXVsdGAgZXh0ZW5kcyBgTC5JY29uYCBhbmQgaXMgdGhlIGJsdWUgaWNvbiBMZWFmbGV0IHVzZXMgZm9yIG1hcmtlcnMgYnkgZGVmYXVsdC5cclxuICpcclxuICovXHJcblxyXG5MLkljb24gPSBMLkNsYXNzLmV4dGVuZCh7XHJcblxyXG5cdC8qIEBzZWN0aW9uXHJcblx0ICogQGFrYSBJY29uIG9wdGlvbnNcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gaWNvblVybDogU3RyaW5nID0gbnVsbFxyXG5cdCAqICoqKHJlcXVpcmVkKSoqIFRoZSBVUkwgdG8gdGhlIGljb24gaW1hZ2UgKGFic29sdXRlIG9yIHJlbGF0aXZlIHRvIHlvdXIgc2NyaXB0IHBhdGgpLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBpY29uUmV0aW5hVXJsOiBTdHJpbmcgPSBudWxsXHJcblx0ICogVGhlIFVSTCB0byBhIHJldGluYSBzaXplZCB2ZXJzaW9uIG9mIHRoZSBpY29uIGltYWdlIChhYnNvbHV0ZSBvciByZWxhdGl2ZSB0byB5b3VyXHJcblx0ICogc2NyaXB0IHBhdGgpLiBVc2VkIGZvciBSZXRpbmEgc2NyZWVuIGRldmljZXMuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIGljb25TaXplOiBQb2ludCA9IG51bGxcclxuXHQgKiBTaXplIG9mIHRoZSBpY29uIGltYWdlIGluIHBpeGVscy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gaWNvbkFuY2hvcjogUG9pbnQgPSBudWxsXHJcblx0ICogVGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBcInRpcFwiIG9mIHRoZSBpY29uIChyZWxhdGl2ZSB0byBpdHMgdG9wIGxlZnQgY29ybmVyKS4gVGhlIGljb25cclxuXHQgKiB3aWxsIGJlIGFsaWduZWQgc28gdGhhdCB0aGlzIHBvaW50IGlzIGF0IHRoZSBtYXJrZXIncyBnZW9ncmFwaGljYWwgbG9jYXRpb24uIENlbnRlcmVkXHJcblx0ICogYnkgZGVmYXVsdCBpZiBzaXplIGlzIHNwZWNpZmllZCwgYWxzbyBjYW4gYmUgc2V0IGluIENTUyB3aXRoIG5lZ2F0aXZlIG1hcmdpbnMuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHBvcHVwQW5jaG9yOiBQb2ludCA9IG51bGxcclxuXHQgKiBUaGUgY29vcmRpbmF0ZXMgb2YgdGhlIHBvaW50IGZyb20gd2hpY2ggcG9wdXBzIHdpbGwgXCJvcGVuXCIsIHJlbGF0aXZlIHRvIHRoZSBpY29uIGFuY2hvci5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gc2hhZG93VXJsOiBTdHJpbmcgPSBudWxsXHJcblx0ICogVGhlIFVSTCB0byB0aGUgaWNvbiBzaGFkb3cgaW1hZ2UuIElmIG5vdCBzcGVjaWZpZWQsIG5vIHNoYWRvdyBpbWFnZSB3aWxsIGJlIGNyZWF0ZWQuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHNoYWRvd1JldGluYVVybDogU3RyaW5nID0gbnVsbFxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBzaGFkb3dTaXplOiBQb2ludCA9IG51bGxcclxuXHQgKiBTaXplIG9mIHRoZSBzaGFkb3cgaW1hZ2UgaW4gcGl4ZWxzLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBzaGFkb3dBbmNob3I6IFBvaW50ID0gbnVsbFxyXG5cdCAqIFRoZSBjb29yZGluYXRlcyBvZiB0aGUgXCJ0aXBcIiBvZiB0aGUgc2hhZG93IChyZWxhdGl2ZSB0byBpdHMgdG9wIGxlZnQgY29ybmVyKSAodGhlIHNhbWVcclxuXHQgKiBhcyBpY29uQW5jaG9yIGlmIG5vdCBzcGVjaWZpZWQpLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9ICcnXHJcblx0ICogQSBjdXN0b20gY2xhc3MgbmFtZSB0byBhc3NpZ24gdG8gYm90aCBpY29uIGFuZCBzaGFkb3cgaW1hZ2VzLiBFbXB0eSBieSBkZWZhdWx0LlxyXG5cdCAqL1xyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY3JlYXRlSWNvbihvbGRJY29uPzogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudFxyXG5cdC8vIENhbGxlZCBpbnRlcm5hbGx5IHdoZW4gdGhlIGljb24gaGFzIHRvIGJlIHNob3duLCByZXR1cm5zIGEgYDxpbWc+YCBIVE1MIGVsZW1lbnRcclxuXHQvLyBzdHlsZWQgYWNjb3JkaW5nIHRvIHRoZSBvcHRpb25zLlxyXG5cdGNyZWF0ZUljb246IGZ1bmN0aW9uIChvbGRJY29uKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY3JlYXRlSWNvbignaWNvbicsIG9sZEljb24pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY3JlYXRlU2hhZG93KG9sZEljb24/OiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gQXMgYGNyZWF0ZUljb25gLCBidXQgZm9yIHRoZSBzaGFkb3cgYmVuZWF0aCBpdC5cclxuXHRjcmVhdGVTaGFkb3c6IGZ1bmN0aW9uIChvbGRJY29uKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY3JlYXRlSWNvbignc2hhZG93Jywgb2xkSWNvbik7XHJcblx0fSxcclxuXHJcblx0X2NyZWF0ZUljb246IGZ1bmN0aW9uIChuYW1lLCBvbGRJY29uKSB7XHJcblx0XHR2YXIgc3JjID0gdGhpcy5fZ2V0SWNvblVybChuYW1lKTtcclxuXHJcblx0XHRpZiAoIXNyYykge1xyXG5cdFx0XHRpZiAobmFtZSA9PT0gJ2ljb24nKSB7XHJcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKCdpY29uVXJsIG5vdCBzZXQgaW4gSWNvbiBvcHRpb25zIChzZWUgdGhlIGRvY3MpLicpO1xyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBpbWcgPSB0aGlzLl9jcmVhdGVJbWcoc3JjLCBvbGRJY29uICYmIG9sZEljb24udGFnTmFtZSA9PT0gJ0lNRycgPyBvbGRJY29uIDogbnVsbCk7XHJcblx0XHR0aGlzLl9zZXRJY29uU3R5bGVzKGltZywgbmFtZSk7XHJcblxyXG5cdFx0cmV0dXJuIGltZztcclxuXHR9LFxyXG5cclxuXHRfc2V0SWNvblN0eWxlczogZnVuY3Rpb24gKGltZywgbmFtZSkge1xyXG5cdFx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XHJcblx0XHR2YXIgc2l6ZU9wdGlvbiA9IG9wdGlvbnNbbmFtZSArICdTaXplJ107XHJcblxyXG5cdFx0aWYgKHR5cGVvZiBzaXplT3B0aW9uID09PSAnbnVtYmVyJykge1xyXG5cdFx0XHRzaXplT3B0aW9uID0gW3NpemVPcHRpb24sIHNpemVPcHRpb25dO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBzaXplID0gTC5wb2ludChzaXplT3B0aW9uKSxcclxuXHRcdCAgICBhbmNob3IgPSBMLnBvaW50KG5hbWUgPT09ICdzaGFkb3cnICYmIG9wdGlvbnMuc2hhZG93QW5jaG9yIHx8IG9wdGlvbnMuaWNvbkFuY2hvciB8fFxyXG5cdFx0ICAgICAgICAgICAgc2l6ZSAmJiBzaXplLmRpdmlkZUJ5KDIsIHRydWUpKTtcclxuXHJcblx0XHRpbWcuY2xhc3NOYW1lID0gJ2xlYWZsZXQtbWFya2VyLScgKyBuYW1lICsgJyAnICsgKG9wdGlvbnMuY2xhc3NOYW1lIHx8ICcnKTtcclxuXHJcblx0XHRpZiAoYW5jaG9yKSB7XHJcblx0XHRcdGltZy5zdHlsZS5tYXJnaW5MZWZ0ID0gKC1hbmNob3IueCkgKyAncHgnO1xyXG5cdFx0XHRpbWcuc3R5bGUubWFyZ2luVG9wICA9ICgtYW5jaG9yLnkpICsgJ3B4JztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoc2l6ZSkge1xyXG5cdFx0XHRpbWcuc3R5bGUud2lkdGggID0gc2l6ZS54ICsgJ3B4JztcclxuXHRcdFx0aW1nLnN0eWxlLmhlaWdodCA9IHNpemUueSArICdweCc7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2NyZWF0ZUltZzogZnVuY3Rpb24gKHNyYywgZWwpIHtcclxuXHRcdGVsID0gZWwgfHwgZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7XHJcblx0XHRlbC5zcmMgPSBzcmM7XHJcblx0XHRyZXR1cm4gZWw7XHJcblx0fSxcclxuXHJcblx0X2dldEljb25Vcmw6IGZ1bmN0aW9uIChuYW1lKSB7XHJcblx0XHRyZXR1cm4gTC5Ccm93c2VyLnJldGluYSAmJiB0aGlzLm9wdGlvbnNbbmFtZSArICdSZXRpbmFVcmwnXSB8fCB0aGlzLm9wdGlvbnNbbmFtZSArICdVcmwnXTtcclxuXHR9XHJcbn0pO1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwuaWNvbihvcHRpb25zOiBJY29uIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYW4gaWNvbiBpbnN0YW5jZSB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxyXG5MLmljb24gPSBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5JY29uKG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG4vKlxuICogQG1pbmljbGFzcyBJY29uLkRlZmF1bHQgKEljb24pXG4gKiBAYWthIEwuSWNvbi5EZWZhdWx0XG4gKiBAc2VjdGlvblxuICpcbiAqIEEgdHJpdmlhbCBzdWJjbGFzcyBvZiBgSWNvbmAsIHJlcHJlc2VudHMgdGhlIGljb24gdG8gdXNlIGluIGBNYXJrZXJgcyB3aGVuXG4gKiBubyBpY29uIGlzIHNwZWNpZmllZC4gUG9pbnRzIHRvIHRoZSBibHVlIG1hcmtlciBpbWFnZSBkaXN0cmlidXRlZCB3aXRoIExlYWZsZXRcbiAqIHJlbGVhc2VzLlxuICpcbiAqIEluIG9yZGVyIHRvIGN1c3RvbWl6ZSB0aGUgZGVmYXVsdCBpY29uLCBqdXN0IGNoYW5nZSB0aGUgcHJvcGVydGllcyBvZiBgTC5JY29uLkRlZmF1bHQucHJvdG90eXBlLm9wdGlvbnNgXG4gKiAod2hpY2ggaXMgYSBzZXQgb2YgYEljb24gb3B0aW9uc2ApLlxuICpcbiAqIElmIHlvdSB3YW50IHRvIF9jb21wbGV0ZWx5XyByZXBsYWNlIHRoZSBkZWZhdWx0IGljb24sIG92ZXJyaWRlIHRoZVxuICogYEwuTWFya2VyLnByb3RvdHlwZS5vcHRpb25zLmljb25gIHdpdGggeW91ciBvd24gaWNvbiBpbnN0ZWFkLlxuICovXG5cbkwuSWNvbi5EZWZhdWx0ID0gTC5JY29uLmV4dGVuZCh7XG5cblx0b3B0aW9uczoge1xuXHRcdGljb25Vcmw6ICAgICAgICdtYXJrZXItaWNvbi5wbmcnLFxuXHRcdGljb25SZXRpbmFVcmw6ICdtYXJrZXItaWNvbi0yeC5wbmcnLFxuXHRcdHNoYWRvd1VybDogICAgICdtYXJrZXItc2hhZG93LnBuZycsXG5cdFx0aWNvblNpemU6ICAgIFsyNSwgNDFdLFxuXHRcdGljb25BbmNob3I6ICBbMTIsIDQxXSxcblx0XHRwb3B1cEFuY2hvcjogWzEsIC0zNF0sXG5cdFx0dG9vbHRpcEFuY2hvcjogWzE2LCAtMjhdLFxuXHRcdHNoYWRvd1NpemU6ICBbNDEsIDQxXVxuXHR9LFxuXG5cdF9nZXRJY29uVXJsOiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdGlmICghTC5JY29uLkRlZmF1bHQuaW1hZ2VQYXRoKSB7XHQvLyBEZXByZWNhdGVkLCBiYWNrd2FyZHMtY29tcGF0aWJpbGl0eSBvbmx5XG5cdFx0XHRMLkljb24uRGVmYXVsdC5pbWFnZVBhdGggPSB0aGlzLl9kZXRlY3RJY29uUGF0aCgpO1xuXHRcdH1cblxuXHRcdC8vIEBvcHRpb24gaW1hZ2VQYXRoOiBTdHJpbmdcblx0XHQvLyBgTC5JY29uLkRlZmF1bHRgIHdpbGwgdHJ5IHRvIGF1dG8tZGV0ZWN0IHRoZSBhYnNvbHV0ZSBsb2NhdGlvbiBvZiB0aGVcblx0XHQvLyBibHVlIGljb24gaW1hZ2VzLiBJZiB5b3UgYXJlIHBsYWNpbmcgdGhlc2UgaW1hZ2VzIGluIGEgbm9uLXN0YW5kYXJkXG5cdFx0Ly8gd2F5LCBzZXQgdGhpcyBvcHRpb24gdG8gcG9pbnQgdG8gdGhlIHJpZ2h0IGFic29sdXRlIHBhdGguXG5cdFx0cmV0dXJuICh0aGlzLm9wdGlvbnMuaW1hZ2VQYXRoIHx8IEwuSWNvbi5EZWZhdWx0LmltYWdlUGF0aCkgKyBMLkljb24ucHJvdG90eXBlLl9nZXRJY29uVXJsLmNhbGwodGhpcywgbmFtZSk7XG5cdH0sXG5cblx0X2RldGVjdEljb25QYXRoOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGVsID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgICdsZWFmbGV0LWRlZmF1bHQtaWNvbi1wYXRoJywgZG9jdW1lbnQuYm9keSk7XG5cdFx0dmFyIHBhdGggPSBMLkRvbVV0aWwuZ2V0U3R5bGUoZWwsICdiYWNrZ3JvdW5kLWltYWdlJykgfHxcblx0XHQgICAgICAgICAgIEwuRG9tVXRpbC5nZXRTdHlsZShlbCwgJ2JhY2tncm91bmRJbWFnZScpO1x0Ly8gSUU4XG5cblx0XHRkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKGVsKTtcblxuXHRcdHJldHVybiBwYXRoLmluZGV4T2YoJ3VybCcpID09PSAwID9cblx0XHRcdHBhdGgucmVwbGFjZSgvXnVybFxcKFtcXFwiXFwnXT8vLCAnJykucmVwbGFjZSgvbWFya2VyLWljb25cXC5wbmdbXFxcIlxcJ10/XFwpJC8sICcnKSA6ICcnO1xuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBNYXJrZXJcclxuICogQGluaGVyaXRzIEludGVyYWN0aXZlIGxheWVyXHJcbiAqIEBha2EgTC5NYXJrZXJcclxuICogTC5NYXJrZXIgaXMgdXNlZCB0byBkaXNwbGF5IGNsaWNrYWJsZS9kcmFnZ2FibGUgaWNvbnMgb24gdGhlIG1hcC4gRXh0ZW5kcyBgTGF5ZXJgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLm1hcmtlcihbNTAuNSwgMzAuNV0pLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuTWFya2VyID0gTC5MYXllci5leHRlbmQoe1xyXG5cclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgTWFya2VyIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIGljb246IEljb24gPSAqXHJcblx0XHQvLyBJY29uIGNsYXNzIHRvIHVzZSBmb3IgcmVuZGVyaW5nIHRoZSBtYXJrZXIuIFNlZSBbSWNvbiBkb2N1bWVudGF0aW9uXSgjTC5JY29uKSBmb3IgZGV0YWlscyBvbiBob3cgdG8gY3VzdG9taXplIHRoZSBtYXJrZXIgaWNvbi4gSWYgbm90IHNwZWNpZmllZCwgYSBuZXcgYEwuSWNvbi5EZWZhdWx0YCBpcyB1c2VkLlxyXG5cdFx0aWNvbjogbmV3IEwuSWNvbi5EZWZhdWx0KCksXHJcblxyXG5cdFx0Ly8gT3B0aW9uIGluaGVyaXRlZCBmcm9tIFwiSW50ZXJhY3RpdmUgbGF5ZXJcIiBhYnN0cmFjdCBjbGFzc1xyXG5cdFx0aW50ZXJhY3RpdmU6IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBkcmFnZ2FibGU6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFya2VyIGlzIGRyYWdnYWJsZSB3aXRoIG1vdXNlL3RvdWNoIG9yIG5vdC5cclxuXHRcdGRyYWdnYWJsZTogZmFsc2UsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBrZXlib2FyZDogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgdGhlIG1hcmtlciBjYW4gYmUgdGFiYmVkIHRvIHdpdGggYSBrZXlib2FyZCBhbmQgY2xpY2tlZCBieSBwcmVzc2luZyBlbnRlci5cclxuXHRcdGtleWJvYXJkOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdGl0bGU6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBUZXh0IGZvciB0aGUgYnJvd3NlciB0b29sdGlwIHRoYXQgYXBwZWFyIG9uIG1hcmtlciBob3ZlciAobm8gdG9vbHRpcCBieSBkZWZhdWx0KS5cclxuXHRcdHRpdGxlOiAnJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGFsdDogU3RyaW5nID0gJydcclxuXHRcdC8vIFRleHQgZm9yIHRoZSBgYWx0YCBhdHRyaWJ1dGUgb2YgdGhlIGljb24gaW1hZ2UgKHVzZWZ1bCBmb3IgYWNjZXNzaWJpbGl0eSkuXHJcblx0XHRhbHQ6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gekluZGV4T2Zmc2V0OiBOdW1iZXIgPSAwXHJcblx0XHQvLyBCeSBkZWZhdWx0LCBtYXJrZXIgaW1hZ2VzIHpJbmRleCBpcyBzZXQgYXV0b21hdGljYWxseSBiYXNlZCBvbiBpdHMgbGF0aXR1ZGUuIFVzZSB0aGlzIG9wdGlvbiBpZiB5b3Ugd2FudCB0byBwdXQgdGhlIG1hcmtlciBvbiB0b3Agb2YgYWxsIG90aGVycyAob3IgYmVsb3cpLCBzcGVjaWZ5aW5nIGEgaGlnaCB2YWx1ZSBsaWtlIGAxMDAwYCAob3IgaGlnaCBuZWdhdGl2ZSB2YWx1ZSwgcmVzcGVjdGl2ZWx5KS5cclxuXHRcdHpJbmRleE9mZnNldDogMCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG9wYWNpdHk6IE51bWJlciA9IDEuMFxyXG5cdFx0Ly8gVGhlIG9wYWNpdHkgb2YgdGhlIG1hcmtlci5cclxuXHRcdG9wYWNpdHk6IDEsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiByaXNlT25Ib3ZlcjogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBtYXJrZXIgd2lsbCBnZXQgb24gdG9wIG9mIG90aGVycyB3aGVuIHlvdSBob3ZlciB0aGUgbW91c2Ugb3ZlciBpdC5cclxuXHRcdHJpc2VPbkhvdmVyOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHJpc2VPZmZzZXQ6IE51bWJlciA9IDI1MFxyXG5cdFx0Ly8gVGhlIHotaW5kZXggb2Zmc2V0IHVzZWQgZm9yIHRoZSBgcmlzZU9uSG92ZXJgIGZlYXR1cmUuXHJcblx0XHRyaXNlT2Zmc2V0OiAyNTAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAnbWFya2VyUGFuZSdcclxuXHRcdC8vIGBNYXAgcGFuZWAgd2hlcmUgdGhlIG1hcmtlcnMgaWNvbiB3aWxsIGJlIGFkZGVkLlxyXG5cdFx0cGFuZTogJ21hcmtlclBhbmUnLFxyXG5cclxuXHRcdC8vIEZJWE1FOiBzaGFkb3dQYW5lIGlzIG5vIGxvbmdlciBhIHZhbGlkIG9wdGlvblxyXG5cdFx0bm9uQnViYmxpbmdFdmVudHM6IFsnY2xpY2snLCAnZGJsY2xpY2snLCAnbW91c2VvdmVyJywgJ21vdXNlb3V0JywgJ2NvbnRleHRtZW51J11cclxuXHR9LFxyXG5cclxuXHQvKiBAc2VjdGlvblxyXG5cdCAqXHJcblx0ICogSW4gYWRkaXRpb24gdG8gW3NoYXJlZCBsYXllciBtZXRob2RzXSgjTGF5ZXIpIGxpa2UgYGFkZFRvKClgIGFuZCBgcmVtb3ZlKClgIGFuZCBbcG9wdXAgbWV0aG9kc10oI1BvcHVwKSBsaWtlIGJpbmRQb3B1cCgpIHlvdSBjYW4gYWxzbyB1c2UgdGhlIGZvbGxvd2luZyBtZXRob2RzOlxyXG5cdCAqL1xyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobGF0bG5nLCBvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblx0XHR0aGlzLl9sYXRsbmcgPSBMLmxhdExuZyhsYXRsbmcpO1xyXG5cdH0sXHJcblxyXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHR0aGlzLl96b29tQW5pbWF0ZWQgPSB0aGlzLl96b29tQW5pbWF0ZWQgJiYgbWFwLm9wdGlvbnMubWFya2VyWm9vbUFuaW1hdGlvbjtcclxuXHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdG1hcC5vbignem9vbWFuaW0nLCB0aGlzLl9hbmltYXRlWm9vbSwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdEljb24oKTtcclxuXHRcdHRoaXMudXBkYXRlKCk7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdGlmICh0aGlzLmRyYWdnaW5nICYmIHRoaXMuZHJhZ2dpbmcuZW5hYmxlZCgpKSB7XHJcblx0XHRcdHRoaXMub3B0aW9ucy5kcmFnZ2FibGUgPSB0cnVlO1xyXG5cdFx0XHR0aGlzLmRyYWdnaW5nLnJlbW92ZUhvb2tzKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHRtYXAub2ZmKCd6b29tYW5pbScsIHRoaXMuX2FuaW1hdGVab29tLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9yZW1vdmVJY29uKCk7XHJcblx0XHR0aGlzLl9yZW1vdmVTaGFkb3coKTtcclxuXHR9LFxyXG5cclxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB7XHJcblx0XHRcdHpvb206IHRoaXMudXBkYXRlLFxyXG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMudXBkYXRlXHJcblx0XHR9O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TGF0TG5nOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBjdXJyZW50IGdlb2dyYXBoaWNhbCBwb3NpdGlvbiBvZiB0aGUgbWFya2VyLlxyXG5cdGdldExhdExuZzogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2xhdGxuZztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldExhdExuZyhsYXRsbmc6IExhdExuZyk6IHRoaXNcclxuXHQvLyBDaGFuZ2VzIHRoZSBtYXJrZXIgcG9zaXRpb24gdG8gdGhlIGdpdmVuIHBvaW50LlxyXG5cdHNldExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIG9sZExhdExuZyA9IHRoaXMuX2xhdGxuZztcclxuXHRcdHRoaXMuX2xhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XHJcblx0XHR0aGlzLnVwZGF0ZSgpO1xyXG5cclxuXHRcdC8vIEBldmVudCBtb3ZlOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFya2VyIGlzIG1vdmVkIHZpYSBbYHNldExhdExuZ2BdKCNtYXJrZXItc2V0bGF0bG5nKSBvciBieSBbZHJhZ2dpbmddKCNtYXJrZXItZHJhZ2dpbmcpLiBPbGQgYW5kIG5ldyBjb29yZGluYXRlcyBhcmUgaW5jbHVkZWQgaW4gZXZlbnQgYXJndW1lbnRzIGFzIGBvbGRMYXRMbmdgLCBgbGF0bG5nYC5cclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ21vdmUnLCB7b2xkTGF0TG5nOiBvbGRMYXRMbmcsIGxhdGxuZzogdGhpcy5fbGF0bG5nfSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRaSW5kZXhPZmZzZXQob2Zmc2V0OiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyB0aGUgW3pJbmRleCBvZmZzZXRdKCNtYXJrZXItemluZGV4b2Zmc2V0KSBvZiB0aGUgbWFya2VyLlxyXG5cdHNldFpJbmRleE9mZnNldDogZnVuY3Rpb24gKG9mZnNldCkge1xyXG5cdFx0dGhpcy5vcHRpb25zLnpJbmRleE9mZnNldCA9IG9mZnNldDtcclxuXHRcdHJldHVybiB0aGlzLnVwZGF0ZSgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0SWNvbihpY29uOiBJY29uKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgdGhlIG1hcmtlciBpY29uLlxyXG5cdHNldEljb246IGZ1bmN0aW9uIChpY29uKSB7XHJcblxyXG5cdFx0dGhpcy5vcHRpb25zLmljb24gPSBpY29uO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5faW5pdEljb24oKTtcclxuXHRcdFx0dGhpcy51cGRhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fcG9wdXApIHtcclxuXHRcdFx0dGhpcy5iaW5kUG9wdXAodGhpcy5fcG9wdXAsIHRoaXMuX3BvcHVwLm9wdGlvbnMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGdldEVsZW1lbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9pY29uO1xyXG5cdH0sXHJcblxyXG5cdHVwZGF0ZTogZnVuY3Rpb24gKCkge1xyXG5cclxuXHRcdGlmICh0aGlzLl9pY29uKSB7XHJcblx0XHRcdHZhciBwb3MgPSB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZykucm91bmQoKTtcclxuXHRcdFx0dGhpcy5fc2V0UG9zKHBvcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X2luaXRJY29uOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMub3B0aW9ucyxcclxuXHRcdCAgICBjbGFzc1RvQWRkID0gJ2xlYWZsZXQtem9vbS0nICsgKHRoaXMuX3pvb21BbmltYXRlZCA/ICdhbmltYXRlZCcgOiAnaGlkZScpO1xyXG5cclxuXHRcdHZhciBpY29uID0gb3B0aW9ucy5pY29uLmNyZWF0ZUljb24odGhpcy5faWNvbiksXHJcblx0XHQgICAgYWRkSWNvbiA9IGZhbHNlO1xyXG5cclxuXHRcdC8vIGlmIHdlJ3JlIG5vdCByZXVzaW5nIHRoZSBpY29uLCByZW1vdmUgdGhlIG9sZCBvbmUgYW5kIGluaXQgbmV3IG9uZVxyXG5cdFx0aWYgKGljb24gIT09IHRoaXMuX2ljb24pIHtcclxuXHRcdFx0aWYgKHRoaXMuX2ljb24pIHtcclxuXHRcdFx0XHR0aGlzLl9yZW1vdmVJY29uKCk7XHJcblx0XHRcdH1cclxuXHRcdFx0YWRkSWNvbiA9IHRydWU7XHJcblxyXG5cdFx0XHRpZiAob3B0aW9ucy50aXRsZSkge1xyXG5cdFx0XHRcdGljb24udGl0bGUgPSBvcHRpb25zLnRpdGxlO1xyXG5cdFx0XHR9XHJcblx0XHRcdGlmIChvcHRpb25zLmFsdCkge1xyXG5cdFx0XHRcdGljb24uYWx0ID0gb3B0aW9ucy5hbHQ7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoaWNvbiwgY2xhc3NUb0FkZCk7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMua2V5Ym9hcmQpIHtcclxuXHRcdFx0aWNvbi50YWJJbmRleCA9ICcwJztcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9pY29uID0gaWNvbjtcclxuXHJcblx0XHRpZiAob3B0aW9ucy5yaXNlT25Ib3Zlcikge1xyXG5cdFx0XHR0aGlzLm9uKHtcclxuXHRcdFx0XHRtb3VzZW92ZXI6IHRoaXMuX2JyaW5nVG9Gcm9udCxcclxuXHRcdFx0XHRtb3VzZW91dDogdGhpcy5fcmVzZXRaSW5kZXhcclxuXHRcdFx0fSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIG5ld1NoYWRvdyA9IG9wdGlvbnMuaWNvbi5jcmVhdGVTaGFkb3codGhpcy5fc2hhZG93KSxcclxuXHRcdCAgICBhZGRTaGFkb3cgPSBmYWxzZTtcclxuXHJcblx0XHRpZiAobmV3U2hhZG93ICE9PSB0aGlzLl9zaGFkb3cpIHtcclxuXHRcdFx0dGhpcy5fcmVtb3ZlU2hhZG93KCk7XHJcblx0XHRcdGFkZFNoYWRvdyA9IHRydWU7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG5ld1NoYWRvdykge1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MobmV3U2hhZG93LCBjbGFzc1RvQWRkKTtcclxuXHRcdH1cclxuXHRcdHRoaXMuX3NoYWRvdyA9IG5ld1NoYWRvdztcclxuXHJcblxyXG5cdFx0aWYgKG9wdGlvbnMub3BhY2l0eSA8IDEpIHtcclxuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xyXG5cdFx0fVxyXG5cclxuXHJcblx0XHRpZiAoYWRkSWNvbikge1xyXG5cdFx0XHR0aGlzLmdldFBhbmUoKS5hcHBlbmRDaGlsZCh0aGlzLl9pY29uKTtcclxuXHRcdH1cclxuXHRcdHRoaXMuX2luaXRJbnRlcmFjdGlvbigpO1xyXG5cdFx0aWYgKG5ld1NoYWRvdyAmJiBhZGRTaGFkb3cpIHtcclxuXHRcdFx0dGhpcy5nZXRQYW5lKCdzaGFkb3dQYW5lJykuYXBwZW5kQ2hpbGQodGhpcy5fc2hhZG93KTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfcmVtb3ZlSWNvbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5yaXNlT25Ib3Zlcikge1xyXG5cdFx0XHR0aGlzLm9mZih7XHJcblx0XHRcdFx0bW91c2VvdmVyOiB0aGlzLl9icmluZ1RvRnJvbnQsXHJcblx0XHRcdFx0bW91c2VvdXQ6IHRoaXMuX3Jlc2V0WkluZGV4XHJcblx0XHRcdH0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5faWNvbik7XHJcblx0XHR0aGlzLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX2ljb24pO1xyXG5cclxuXHRcdHRoaXMuX2ljb24gPSBudWxsO1xyXG5cdH0sXHJcblxyXG5cdF9yZW1vdmVTaGFkb3c6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9zaGFkb3cpIHtcclxuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9zaGFkb3cpO1xyXG5cdFx0fVxyXG5cdFx0dGhpcy5fc2hhZG93ID0gbnVsbDtcclxuXHR9LFxyXG5cclxuXHRfc2V0UG9zOiBmdW5jdGlvbiAocG9zKSB7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5faWNvbiwgcG9zKTtcclxuXHJcblx0XHRpZiAodGhpcy5fc2hhZG93KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aGlzLl9zaGFkb3csIHBvcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fekluZGV4ID0gcG9zLnkgKyB0aGlzLm9wdGlvbnMuekluZGV4T2Zmc2V0O1xyXG5cclxuXHRcdHRoaXMuX3Jlc2V0WkluZGV4KCk7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZVpJbmRleDogZnVuY3Rpb24gKG9mZnNldCkge1xyXG5cdFx0dGhpcy5faWNvbi5zdHlsZS56SW5kZXggPSB0aGlzLl96SW5kZXggKyBvZmZzZXQ7XHJcblx0fSxcclxuXHJcblx0X2FuaW1hdGVab29tOiBmdW5jdGlvbiAob3B0KSB7XHJcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLl9sYXRMbmdUb05ld0xheWVyUG9pbnQodGhpcy5fbGF0bG5nLCBvcHQuem9vbSwgb3B0LmNlbnRlcikucm91bmQoKTtcclxuXHJcblx0XHR0aGlzLl9zZXRQb3MocG9zKTtcclxuXHR9LFxyXG5cclxuXHRfaW5pdEludGVyYWN0aW9uOiBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0aWYgKCF0aGlzLm9wdGlvbnMuaW50ZXJhY3RpdmUpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX2ljb24sICdsZWFmbGV0LWludGVyYWN0aXZlJyk7XHJcblxyXG5cdFx0dGhpcy5hZGRJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl9pY29uKTtcclxuXHJcblx0XHRpZiAoTC5IYW5kbGVyLk1hcmtlckRyYWcpIHtcclxuXHRcdFx0dmFyIGRyYWdnYWJsZSA9IHRoaXMub3B0aW9ucy5kcmFnZ2FibGU7XHJcblx0XHRcdGlmICh0aGlzLmRyYWdnaW5nKSB7XHJcblx0XHRcdFx0ZHJhZ2dhYmxlID0gdGhpcy5kcmFnZ2luZy5lbmFibGVkKCk7XHJcblx0XHRcdFx0dGhpcy5kcmFnZ2luZy5kaXNhYmxlKCk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHRoaXMuZHJhZ2dpbmcgPSBuZXcgTC5IYW5kbGVyLk1hcmtlckRyYWcodGhpcyk7XHJcblxyXG5cdFx0XHRpZiAoZHJhZ2dhYmxlKSB7XHJcblx0XHRcdFx0dGhpcy5kcmFnZ2luZy5lbmFibGUoKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0T3BhY2l0eShvcGFjaXR5OiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyB0aGUgb3BhY2l0eSBvZiB0aGUgbWFya2VyLlxyXG5cdHNldE9wYWNpdHk6IGZ1bmN0aW9uIChvcGFjaXR5KSB7XHJcblx0XHR0aGlzLm9wdGlvbnMub3BhY2l0eSA9IG9wYWNpdHk7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdHRoaXMuX3VwZGF0ZU9wYWNpdHkoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlT3BhY2l0eTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIG9wYWNpdHkgPSB0aGlzLm9wdGlvbnMub3BhY2l0eTtcclxuXHJcblx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9pY29uLCBvcGFjaXR5KTtcclxuXHJcblx0XHRpZiAodGhpcy5fc2hhZG93KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX3NoYWRvdywgb3BhY2l0eSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2JyaW5nVG9Gcm9udDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fdXBkYXRlWkluZGV4KHRoaXMub3B0aW9ucy5yaXNlT2Zmc2V0KTtcclxuXHR9LFxyXG5cclxuXHRfcmVzZXRaSW5kZXg6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgwKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0UG9wdXBBbmNob3I6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuaWNvbi5vcHRpb25zLnBvcHVwQW5jaG9yIHx8IFswLCAwXTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0VG9vbHRpcEFuY2hvcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5pY29uLm9wdGlvbnMudG9vbHRpcEFuY2hvciB8fCBbMCwgMF07XHJcblx0fVxyXG59KTtcclxuXHJcblxyXG4vLyBmYWN0b3J5IEwubWFya2VyKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPyA6IE1hcmtlciBvcHRpb25zKVxyXG5cclxuLy8gQGZhY3RvcnkgTC5tYXJrZXIobGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/IDogTWFya2VyIG9wdGlvbnMpXHJcbi8vIEluc3RhbnRpYXRlcyBhIE1hcmtlciBvYmplY3QgZ2l2ZW4gYSBnZW9ncmFwaGljYWwgcG9pbnQgYW5kIG9wdGlvbmFsbHkgYW4gb3B0aW9ucyBvYmplY3QuXHJcbkwubWFya2VyID0gZnVuY3Rpb24gKGxhdGxuZywgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5NYXJrZXIobGF0bG5nLCBvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBEaXZJY29uXG4gKiBAYWthIEwuRGl2SWNvblxuICogQGluaGVyaXRzIEljb25cbiAqXG4gKiBSZXByZXNlbnRzIGEgbGlnaHR3ZWlnaHQgaWNvbiBmb3IgbWFya2VycyB0aGF0IHVzZXMgYSBzaW1wbGUgYDxkaXY+YFxuICogZWxlbWVudCBpbnN0ZWFkIG9mIGFuIGltYWdlLiBJbmhlcml0cyBmcm9tIGBJY29uYCBidXQgaWdub3JlcyB0aGUgYGljb25VcmxgIGFuZCBzaGFkb3cgb3B0aW9ucy5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBganNcbiAqIHZhciBteUljb24gPSBMLmRpdkljb24oe2NsYXNzTmFtZTogJ215LWRpdi1pY29uJ30pO1xuICogLy8geW91IGNhbiBzZXQgLm15LWRpdi1pY29uIHN0eWxlcyBpbiBDU1NcbiAqXG4gKiBMLm1hcmtlcihbNTAuNTA1LCAzMC41N10sIHtpY29uOiBteUljb259KS5hZGRUbyhtYXApO1xuICogYGBgXG4gKlxuICogQnkgZGVmYXVsdCwgaXQgaGFzIGEgJ2xlYWZsZXQtZGl2LWljb24nIENTUyBjbGFzcyBhbmQgaXMgc3R5bGVkIGFzIGEgbGl0dGxlIHdoaXRlIHNxdWFyZSB3aXRoIGEgc2hhZG93LlxuICovXG5cbkwuRGl2SWNvbiA9IEwuSWNvbi5leHRlbmQoe1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQHNlY3Rpb25cblx0XHQvLyBAYWthIERpdkljb24gb3B0aW9uc1xuXHRcdGljb25TaXplOiBbMTIsIDEyXSwgLy8gYWxzbyBjYW4gYmUgc2V0IHRocm91Z2ggQ1NTXG5cblx0XHQvLyBpY29uQW5jaG9yOiAoUG9pbnQpLFxuXHRcdC8vIHBvcHVwQW5jaG9yOiAoUG9pbnQpLFxuXG5cdFx0Ly8gQG9wdGlvbiBodG1sOiBTdHJpbmcgPSAnJ1xuXHRcdC8vIEN1c3RvbSBIVE1MIGNvZGUgdG8gcHV0IGluc2lkZSB0aGUgZGl2IGVsZW1lbnQsIGVtcHR5IGJ5IGRlZmF1bHQuXG5cdFx0aHRtbDogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIGJnUG9zOiBQb2ludCA9IFswLCAwXVxuXHRcdC8vIE9wdGlvbmFsIHJlbGF0aXZlIHBvc2l0aW9uIG9mIHRoZSBiYWNrZ3JvdW5kLCBpbiBwaXhlbHNcblx0XHRiZ1BvczogbnVsbCxcblxuXHRcdGNsYXNzTmFtZTogJ2xlYWZsZXQtZGl2LWljb24nXG5cdH0sXG5cblx0Y3JlYXRlSWNvbjogZnVuY3Rpb24gKG9sZEljb24pIHtcblx0XHR2YXIgZGl2ID0gKG9sZEljb24gJiYgb2xkSWNvbi50YWdOYW1lID09PSAnRElWJykgPyBvbGRJY29uIDogZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JyksXG5cdFx0ICAgIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XG5cblx0XHRkaXYuaW5uZXJIVE1MID0gb3B0aW9ucy5odG1sICE9PSBmYWxzZSA/IG9wdGlvbnMuaHRtbCA6ICcnO1xuXG5cdFx0aWYgKG9wdGlvbnMuYmdQb3MpIHtcblx0XHRcdHZhciBiZ1BvcyA9IEwucG9pbnQob3B0aW9ucy5iZ1Bvcyk7XG5cdFx0XHRkaXYuc3R5bGUuYmFja2dyb3VuZFBvc2l0aW9uID0gKC1iZ1Bvcy54KSArICdweCAnICsgKC1iZ1Bvcy55KSArICdweCc7XG5cdFx0fVxuXHRcdHRoaXMuX3NldEljb25TdHlsZXMoZGl2LCAnaWNvbicpO1xuXG5cdFx0cmV0dXJuIGRpdjtcblx0fSxcblxuXHRjcmVhdGVTaGFkb3c6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gbnVsbDtcblx0fVxufSk7XG5cbi8vIEBmYWN0b3J5IEwuZGl2SWNvbihvcHRpb25zOiBEaXZJY29uIG9wdGlvbnMpXG4vLyBDcmVhdGVzIGEgYERpdkljb25gIGluc3RhbmNlIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXG5MLmRpdkljb24gPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuRGl2SWNvbihvcHRpb25zKTtcbn07XG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgRGl2T3ZlcmxheVxyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcclxuICogQGFrYSBMLkRpdk92ZXJsYXlcclxuICogQmFzZSBtb2RlbCBmb3IgTC5Qb3B1cCBhbmQgTC5Ub29sdGlwLiBJbmhlcml0IGZyb20gaXQgZm9yIGN1c3RvbSBwb3B1cCBsaWtlIHBsdWdpbnMuXHJcbiAqL1xyXG5cclxuLy8gQG5hbWVzcGFjZSBEaXZPdmVybGF5XHJcbkwuRGl2T3ZlcmxheSA9IEwuTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIERpdk92ZXJsYXkgb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gb2Zmc2V0OiBQb2ludCA9IFBvaW50KDAsIDcpXHJcblx0XHQvLyBUaGUgb2Zmc2V0IG9mIHRoZSBwb3B1cCBwb3NpdGlvbi4gVXNlZnVsIHRvIGNvbnRyb2wgdGhlIGFuY2hvclxyXG5cdFx0Ly8gb2YgdGhlIHBvcHVwIHdoZW4gb3BlbmluZyBpdCBvbiBzb21lIG92ZXJsYXlzLlxyXG5cdFx0b2Zmc2V0OiBbMCwgN10sXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBBIGN1c3RvbSBDU1MgY2xhc3MgbmFtZSB0byBhc3NpZ24gdG8gdGhlIHBvcHVwLlxyXG5cdFx0Y2xhc3NOYW1lOiAnJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHBhbmU6IFN0cmluZyA9ICdwb3B1cFBhbmUnXHJcblx0XHQvLyBgTWFwIHBhbmVgIHdoZXJlIHRoZSBwb3B1cCB3aWxsIGJlIGFkZGVkLlxyXG5cdFx0cGFuZTogJ3BvcHVwUGFuZSdcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucywgc291cmNlKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fc291cmNlID0gc291cmNlO1xyXG5cdH0sXHJcblxyXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHR0aGlzLl96b29tQW5pbWF0ZWQgPSBtYXAuX3pvb21BbmltYXRlZDtcclxuXHJcblx0XHRpZiAoIXRoaXMuX2NvbnRhaW5lcikge1xyXG5cdFx0XHR0aGlzLl9pbml0TGF5b3V0KCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG1hcC5fZmFkZUFuaW1hdGVkKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgMCk7XHJcblx0XHR9XHJcblxyXG5cdFx0Y2xlYXJUaW1lb3V0KHRoaXMuX3JlbW92ZVRpbWVvdXQpO1xyXG5cdFx0dGhpcy5nZXRQYW5lKCkuYXBwZW5kQ2hpbGQodGhpcy5fY29udGFpbmVyKTtcclxuXHRcdHRoaXMudXBkYXRlKCk7XHJcblxyXG5cdFx0aWYgKG1hcC5fZmFkZUFuaW1hdGVkKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgMSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5icmluZ1RvRnJvbnQoKTtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0aWYgKG1hcC5fZmFkZUFuaW1hdGVkKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgMCk7XHJcblx0XHRcdHRoaXMuX3JlbW92ZVRpbWVvdXQgPSBzZXRUaW1lb3V0KEwuYmluZChMLkRvbVV0aWwucmVtb3ZlLCBMLkRvbVV0aWwsIHRoaXMuX2NvbnRhaW5lciksIDIwMCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlKHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG5hbWVzcGFjZSBQb3B1cFxyXG5cdC8vIEBtZXRob2QgZ2V0TGF0TG5nOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBnZW9ncmFwaGljYWwgcG9pbnQgb2YgcG9wdXAuXHJcblx0Z2V0TGF0TG5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbGF0bG5nO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TGF0TG5nKGxhdGxuZzogTGF0TG5nKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIGdlb2dyYXBoaWNhbCBwb2ludCB3aGVyZSB0aGUgcG9wdXAgd2lsbCBvcGVuLlxyXG5cdHNldExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fdXBkYXRlUG9zaXRpb24oKTtcclxuXHRcdFx0dGhpcy5fYWRqdXN0UGFuKCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudFxyXG5cdC8vIFJldHVybnMgdGhlIGNvbnRlbnQgb2YgdGhlIHBvcHVwLlxyXG5cdGdldENvbnRlbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9jb250ZW50O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0Q29udGVudChodG1sQ29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50fEZ1bmN0aW9uKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIEhUTUwgY29udGVudCBvZiB0aGUgcG9wdXAuIElmIGEgZnVuY3Rpb24gaXMgcGFzc2VkIHRoZSBzb3VyY2UgbGF5ZXIgd2lsbCBiZSBwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uLiBUaGUgZnVuY3Rpb24gc2hvdWxkIHJldHVybiBhIGBTdHJpbmdgIG9yIGBIVE1MRWxlbWVudGAgdG8gYmUgdXNlZCBpbiB0aGUgcG9wdXAuXHJcblx0c2V0Q29udGVudDogZnVuY3Rpb24gKGNvbnRlbnQpIHtcclxuXHRcdHRoaXMuX2NvbnRlbnQgPSBjb250ZW50O1xyXG5cdFx0dGhpcy51cGRhdGUoKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0RWxlbWVudDogU3RyaW5nfEhUTUxFbGVtZW50XHJcblx0Ly8gQWxpYXMgZm9yIFtnZXRDb250ZW50KCldKCNwb3B1cC1nZXRjb250ZW50KVxyXG5cdGdldEVsZW1lbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9jb250YWluZXI7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB1cGRhdGU6IG51bGxcclxuXHQvLyBVcGRhdGVzIHRoZSBwb3B1cCBjb250ZW50LCBsYXlvdXQgYW5kIHBvc2l0aW9uLiBVc2VmdWwgZm9yIHVwZGF0aW5nIHRoZSBwb3B1cCBhZnRlciBzb21ldGhpbmcgaW5zaWRlIGNoYW5nZWQsIGUuZy4gaW1hZ2UgbG9hZGVkLlxyXG5cdHVwZGF0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLnZpc2liaWxpdHkgPSAnaGlkZGVuJztcclxuXHJcblx0XHR0aGlzLl91cGRhdGVDb250ZW50KCk7XHJcblx0XHR0aGlzLl91cGRhdGVMYXlvdXQoKTtcclxuXHRcdHRoaXMuX3VwZGF0ZVBvc2l0aW9uKCk7XHJcblxyXG5cdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLnZpc2liaWxpdHkgPSAnJztcclxuXHJcblx0XHR0aGlzLl9hZGp1c3RQYW4oKTtcclxuXHR9LFxyXG5cclxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBldmVudHMgPSB7XHJcblx0XHRcdHpvb206IHRoaXMuX3VwZGF0ZVBvc2l0aW9uLFxyXG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3VwZGF0ZVBvc2l0aW9uXHJcblx0XHR9O1xyXG5cclxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcclxuXHRcdFx0ZXZlbnRzLnpvb21hbmltID0gdGhpcy5fYW5pbWF0ZVpvb207XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gZXZlbnRzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaXNPcGVuOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgd2hlbiB0aGUgcG9wdXAgaXMgdmlzaWJsZSBvbiB0aGUgbWFwLlxyXG5cdGlzT3BlbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuICEhdGhpcy5fbWFwICYmIHRoaXMuX21hcC5oYXNMYXllcih0aGlzKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGJyaW5nVG9Gcm9udDogdGhpc1xyXG5cdC8vIEJyaW5ncyB0aGlzIHBvcHVwIGluIGZyb250IG9mIG90aGVyIHBvcHVwcyAoaW4gdGhlIHNhbWUgbWFwIHBhbmUpLlxyXG5cdGJyaW5nVG9Gcm9udDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHRMLkRvbVV0aWwudG9Gcm9udCh0aGlzLl9jb250YWluZXIpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvQmFjazogdGhpc1xyXG5cdC8vIEJyaW5ncyB0aGlzIHBvcHVwIHRvIHRoZSBiYWNrIG9mIG90aGVyIHBvcHVwcyAoaW4gdGhlIHNhbWUgbWFwIHBhbmUpLlxyXG5cdGJyaW5nVG9CYWNrOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdEwuRG9tVXRpbC50b0JhY2sodGhpcy5fY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVDb250ZW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2NvbnRlbnQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dmFyIG5vZGUgPSB0aGlzLl9jb250ZW50Tm9kZTtcclxuXHRcdHZhciBjb250ZW50ID0gKHR5cGVvZiB0aGlzLl9jb250ZW50ID09PSAnZnVuY3Rpb24nKSA/IHRoaXMuX2NvbnRlbnQodGhpcy5fc291cmNlIHx8IHRoaXMpIDogdGhpcy5fY29udGVudDtcclxuXHJcblx0XHRpZiAodHlwZW9mIGNvbnRlbnQgPT09ICdzdHJpbmcnKSB7XHJcblx0XHRcdG5vZGUuaW5uZXJIVE1MID0gY29udGVudDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHdoaWxlIChub2RlLmhhc0NoaWxkTm9kZXMoKSkge1xyXG5cdFx0XHRcdG5vZGUucmVtb3ZlQ2hpbGQobm9kZS5maXJzdENoaWxkKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRub2RlLmFwcGVuZENoaWxkKGNvbnRlbnQpO1xyXG5cdFx0fVxyXG5cdFx0dGhpcy5maXJlKCdjb250ZW50dXBkYXRlJyk7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZVBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0aGlzLl9sYXRsbmcpLFxyXG5cdFx0ICAgIG9mZnNldCA9IEwucG9pbnQodGhpcy5vcHRpb25zLm9mZnNldCksXHJcblx0XHQgICAgYW5jaG9yID0gdGhpcy5fZ2V0QW5jaG9yKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fY29udGFpbmVyLCBwb3MuYWRkKGFuY2hvcikpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0b2Zmc2V0ID0gb2Zmc2V0LmFkZChwb3MpLmFkZChhbmNob3IpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBib3R0b20gPSB0aGlzLl9jb250YWluZXJCb3R0b20gPSAtb2Zmc2V0LnksXHJcblx0XHQgICAgbGVmdCA9IHRoaXMuX2NvbnRhaW5lckxlZnQgPSAtTWF0aC5yb3VuZCh0aGlzLl9jb250YWluZXJXaWR0aCAvIDIpICsgb2Zmc2V0Lng7XHJcblxyXG5cdFx0Ly8gYm90dG9tIHBvc2l0aW9uIHRoZSBwb3B1cCBpbiBjYXNlIHRoZSBoZWlnaHQgb2YgdGhlIHBvcHVwIGNoYW5nZXMgKGltYWdlcyBsb2FkaW5nIGV0YylcclxuXHRcdHRoaXMuX2NvbnRhaW5lci5zdHlsZS5ib3R0b20gPSBib3R0b20gKyAncHgnO1xyXG5cdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLmxlZnQgPSBsZWZ0ICsgJ3B4JztcclxuXHR9LFxyXG5cclxuXHRfZ2V0QW5jaG9yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gWzAsIDBdO1xyXG5cdH1cclxuXHJcbn0pO1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgUG9wdXBcclxuICogQGluaGVyaXRzIERpdk92ZXJsYXlcclxuICogQGFrYSBMLlBvcHVwXHJcbiAqIFVzZWQgdG8gb3BlbiBwb3B1cHMgaW4gY2VydGFpbiBwbGFjZXMgb2YgdGhlIG1hcC4gVXNlIFtNYXAub3BlblBvcHVwXSgjbWFwLW9wZW5wb3B1cCkgdG9cclxuICogb3BlbiBwb3B1cHMgd2hpbGUgbWFraW5nIHN1cmUgdGhhdCBvbmx5IG9uZSBwb3B1cCBpcyBvcGVuIGF0IG9uZSB0aW1lXHJcbiAqIChyZWNvbW1lbmRlZCBmb3IgdXNhYmlsaXR5KSwgb3IgdXNlIFtNYXAuYWRkTGF5ZXJdKCNtYXAtYWRkbGF5ZXIpIHRvIG9wZW4gYXMgbWFueSBhcyB5b3Ugd2FudC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogSWYgeW91IHdhbnQgdG8ganVzdCBiaW5kIGEgcG9wdXAgdG8gbWFya2VyIGNsaWNrIGFuZCB0aGVuIG9wZW4gaXQsIGl0J3MgcmVhbGx5IGVhc3k6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIG1hcmtlci5iaW5kUG9wdXAocG9wdXBDb250ZW50KS5vcGVuUG9wdXAoKTtcclxuICogYGBgXHJcbiAqIFBhdGggb3ZlcmxheXMgbGlrZSBwb2x5bGluZXMgYWxzbyBoYXZlIGEgYGJpbmRQb3B1cGAgbWV0aG9kLlxyXG4gKiBIZXJlJ3MgYSBtb3JlIGNvbXBsaWNhdGVkIHdheSB0byBvcGVuIGEgcG9wdXAgb24gYSBtYXA6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBwb3B1cCA9IEwucG9wdXAoKVxyXG4gKiBcdC5zZXRMYXRMbmcobGF0bG5nKVxyXG4gKiBcdC5zZXRDb250ZW50KCc8cD5IZWxsbyB3b3JsZCE8YnIgLz5UaGlzIGlzIGEgbmljZSBwb3B1cC48L3A+JylcclxuICogXHQub3Blbk9uKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcblxyXG4vLyBAbmFtZXNwYWNlIFBvcHVwXHJcbkwuUG9wdXAgPSBMLkRpdk92ZXJsYXkuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFBvcHVwIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIG1heFdpZHRoOiBOdW1iZXIgPSAzMDBcclxuXHRcdC8vIE1heCB3aWR0aCBvZiB0aGUgcG9wdXAsIGluIHBpeGVscy5cclxuXHRcdG1heFdpZHRoOiAzMDAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtaW5XaWR0aDogTnVtYmVyID0gNTBcclxuXHRcdC8vIE1pbiB3aWR0aCBvZiB0aGUgcG9wdXAsIGluIHBpeGVscy5cclxuXHRcdG1pbldpZHRoOiA1MCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1heEhlaWdodDogTnVtYmVyID0gbnVsbFxyXG5cdFx0Ly8gSWYgc2V0LCBjcmVhdGVzIGEgc2Nyb2xsYWJsZSBjb250YWluZXIgb2YgdGhlIGdpdmVuIGhlaWdodFxyXG5cdFx0Ly8gaW5zaWRlIGEgcG9wdXAgaWYgaXRzIGNvbnRlbnQgZXhjZWVkcyBpdC5cclxuXHRcdG1heEhlaWdodDogbnVsbCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGF1dG9QYW46IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBTZXQgaXQgdG8gYGZhbHNlYCBpZiB5b3UgZG9uJ3Qgd2FudCB0aGUgbWFwIHRvIGRvIHBhbm5pbmcgYW5pbWF0aW9uXHJcblx0XHQvLyB0byBmaXQgdGhlIG9wZW5lZCBwb3B1cC5cclxuXHRcdGF1dG9QYW46IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuUGFkZGluZ1RvcExlZnQ6IFBvaW50ID0gbnVsbFxyXG5cdFx0Ly8gVGhlIG1hcmdpbiBiZXR3ZWVuIHRoZSBwb3B1cCBhbmQgdGhlIHRvcCBsZWZ0IGNvcm5lciBvZiB0aGUgbWFwXHJcblx0XHQvLyB2aWV3IGFmdGVyIGF1dG9wYW5uaW5nIHdhcyBwZXJmb3JtZWQuXHJcblx0XHRhdXRvUGFuUGFkZGluZ1RvcExlZnQ6IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuUGFkZGluZ0JvdHRvbVJpZ2h0OiBQb2ludCA9IG51bGxcclxuXHRcdC8vIFRoZSBtYXJnaW4gYmV0d2VlbiB0aGUgcG9wdXAgYW5kIHRoZSBib3R0b20gcmlnaHQgY29ybmVyIG9mIHRoZSBtYXBcclxuXHRcdC8vIHZpZXcgYWZ0ZXIgYXV0b3Bhbm5pbmcgd2FzIHBlcmZvcm1lZC5cclxuXHRcdGF1dG9QYW5QYWRkaW5nQm90dG9tUmlnaHQ6IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuUGFkZGluZzogUG9pbnQgPSBQb2ludCg1LCA1KVxyXG5cdFx0Ly8gRXF1aXZhbGVudCBvZiBzZXR0aW5nIGJvdGggdG9wIGxlZnQgYW5kIGJvdHRvbSByaWdodCBhdXRvcGFuIHBhZGRpbmcgdG8gdGhlIHNhbWUgdmFsdWUuXHJcblx0XHRhdXRvUGFuUGFkZGluZzogWzUsIDVdLFxyXG5cclxuXHRcdC8vIEBvcHRpb24ga2VlcEluVmlldzogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBTZXQgaXQgdG8gYHRydWVgIGlmIHlvdSB3YW50IHRvIHByZXZlbnQgdXNlcnMgZnJvbSBwYW5uaW5nIHRoZSBwb3B1cFxyXG5cdFx0Ly8gb2ZmIG9mIHRoZSBzY3JlZW4gd2hpbGUgaXQgaXMgb3Blbi5cclxuXHRcdGtlZXBJblZpZXc6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY2xvc2VCdXR0b246IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBDb250cm9scyB0aGUgcHJlc2VuY2Ugb2YgYSBjbG9zZSBidXR0b24gaW4gdGhlIHBvcHVwLlxyXG5cdFx0Y2xvc2VCdXR0b246IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvQ2xvc2U6IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBTZXQgaXQgdG8gYGZhbHNlYCBpZiB5b3Ugd2FudCB0byBvdmVycmlkZSB0aGUgZGVmYXVsdCBiZWhhdmlvciBvZlxyXG5cdFx0Ly8gdGhlIHBvcHVwIGNsb3Npbmcgd2hlbiB1c2VyIGNsaWNrcyB0aGUgbWFwIChzZXQgZ2xvYmFsbHkgYnlcclxuXHRcdC8vIHRoZSBNYXAncyBbY2xvc2VQb3B1cE9uQ2xpY2tdKCNtYXAtY2xvc2Vwb3B1cG9uY2xpY2spIG9wdGlvbikuXHJcblx0XHRhdXRvQ2xvc2U6IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9ICcnXHJcblx0XHQvLyBBIGN1c3RvbSBDU1MgY2xhc3MgbmFtZSB0byBhc3NpZ24gdG8gdGhlIHBvcHVwLlxyXG5cdFx0Y2xhc3NOYW1lOiAnJ1xyXG5cdH0sXHJcblxyXG5cdC8vIEBuYW1lc3BhY2UgUG9wdXBcclxuXHQvLyBAbWV0aG9kIG9wZW5PbihtYXA6IE1hcCk6IHRoaXNcclxuXHQvLyBBZGRzIHRoZSBwb3B1cCB0byB0aGUgbWFwIGFuZCBjbG9zZXMgdGhlIHByZXZpb3VzIG9uZS4gVGhlIHNhbWUgYXMgYG1hcC5vcGVuUG9wdXAocG9wdXApYC5cclxuXHRvcGVuT246IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdG1hcC5vcGVuUG9wdXAodGhpcyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0TC5EaXZPdmVybGF5LnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMsIG1hcCk7XHJcblxyXG5cdFx0Ly8gQG5hbWVzcGFjZSBNYXBcclxuXHRcdC8vIEBzZWN0aW9uIFBvcHVwIGV2ZW50c1xyXG5cdFx0Ly8gQGV2ZW50IHBvcHVwb3BlbjogUG9wdXBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhIHBvcHVwIGlzIG9wZW5lZCBpbiB0aGUgbWFwXHJcblx0XHRtYXAuZmlyZSgncG9wdXBvcGVuJywge3BvcHVwOiB0aGlzfSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3NvdXJjZSkge1xyXG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXHJcblx0XHRcdC8vIEBzZWN0aW9uIFBvcHVwIGV2ZW50c1xyXG5cdFx0XHQvLyBAZXZlbnQgcG9wdXBvcGVuOiBQb3B1cEV2ZW50XHJcblx0XHRcdC8vIEZpcmVkIHdoZW4gYSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyIGlzIG9wZW5lZFxyXG5cdFx0XHR0aGlzLl9zb3VyY2UuZmlyZSgncG9wdXBvcGVuJywge3BvcHVwOiB0aGlzfSwgdHJ1ZSk7XHJcblx0XHRcdC8vIEZvciBub24tcGF0aCBsYXllcnMsIHdlIHRvZ2dsZSB0aGUgcG9wdXAgd2hlbiBjbGlja2luZ1xyXG5cdFx0XHQvLyBhZ2FpbiB0aGUgbGF5ZXIsIHNvIHByZXZlbnQgdGhlIG1hcCB0byByZW9wZW4gaXQuXHJcblx0XHRcdGlmICghKHRoaXMuX3NvdXJjZSBpbnN0YW5jZW9mIEwuUGF0aCkpIHtcclxuXHRcdFx0XHR0aGlzLl9zb3VyY2Uub24oJ3ByZWNsaWNrJywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdEwuRGl2T3ZlcmxheS5wcm90b3R5cGUub25SZW1vdmUuY2FsbCh0aGlzLCBtYXApO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdC8vIEBldmVudCBwb3B1cGNsb3NlOiBQb3B1cEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgcG9wdXAgaW4gdGhlIG1hcCBpcyBjbG9zZWRcclxuXHRcdG1hcC5maXJlKCdwb3B1cGNsb3NlJywge3BvcHVwOiB0aGlzfSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3NvdXJjZSkge1xyXG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXHJcblx0XHRcdC8vIEBzZWN0aW9uIFBvcHVwIGV2ZW50c1xyXG5cdFx0XHQvLyBAZXZlbnQgcG9wdXBjbG9zZTogUG9wdXBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllciBpcyBjbG9zZWRcclxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3BvcHVwY2xvc2UnLCB7cG9wdXA6IHRoaXN9LCB0cnVlKTtcclxuXHRcdFx0aWYgKCEodGhpcy5fc291cmNlIGluc3RhbmNlb2YgTC5QYXRoKSkge1xyXG5cdFx0XHRcdHRoaXMuX3NvdXJjZS5vZmYoJ3ByZWNsaWNrJywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZXZlbnRzID0gTC5EaXZPdmVybGF5LnByb3RvdHlwZS5nZXRFdmVudHMuY2FsbCh0aGlzKTtcclxuXHJcblx0XHRpZiAoJ2Nsb3NlT25DbGljaycgaW4gdGhpcy5vcHRpb25zID8gdGhpcy5vcHRpb25zLmNsb3NlT25DbGljayA6IHRoaXMuX21hcC5vcHRpb25zLmNsb3NlUG9wdXBPbkNsaWNrKSB7XHJcblx0XHRcdGV2ZW50cy5wcmVjbGljayA9IHRoaXMuX2Nsb3NlO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMua2VlcEluVmlldykge1xyXG5cdFx0XHRldmVudHMubW92ZWVuZCA9IHRoaXMuX2FkanVzdFBhbjtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gZXZlbnRzO1xyXG5cdH0sXHJcblxyXG5cdF9jbG9zZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0aGlzLl9tYXAuY2xvc2VQb3B1cCh0aGlzKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHByZWZpeCA9ICdsZWFmbGV0LXBvcHVwJyxcclxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLFxyXG5cdFx0XHRwcmVmaXggKyAnICcgKyAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSB8fCAnJykgK1xyXG5cdFx0XHQnIGxlYWZsZXQtem9vbS1hbmltYXRlZCcpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuY2xvc2VCdXR0b24pIHtcclxuXHRcdFx0dmFyIGNsb3NlQnV0dG9uID0gdGhpcy5fY2xvc2VCdXR0b24gPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgcHJlZml4ICsgJy1jbG9zZS1idXR0b24nLCBjb250YWluZXIpO1xyXG5cdFx0XHRjbG9zZUJ1dHRvbi5ocmVmID0gJyNjbG9zZSc7XHJcblx0XHRcdGNsb3NlQnV0dG9uLmlubmVySFRNTCA9ICcmIzIxNTsnO1xyXG5cclxuXHRcdFx0TC5Eb21FdmVudC5vbihjbG9zZUJ1dHRvbiwgJ2NsaWNrJywgdGhpcy5fb25DbG9zZUJ1dHRvbkNsaWNrLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgd3JhcHBlciA9IHRoaXMuX3dyYXBwZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBwcmVmaXggKyAnLWNvbnRlbnQtd3JhcHBlcicsIGNvbnRhaW5lcik7XHJcblx0XHR0aGlzLl9jb250ZW50Tm9kZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIHByZWZpeCArICctY29udGVudCcsIHdyYXBwZXIpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnRcclxuXHRcdFx0LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKHdyYXBwZXIpXHJcblx0XHRcdC5kaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb24odGhpcy5fY29udGVudE5vZGUpXHJcblx0XHRcdC5vbih3cmFwcGVyLCAnY29udGV4dG1lbnUnLCBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbik7XHJcblxyXG5cdFx0dGhpcy5fdGlwQ29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgcHJlZml4ICsgJy10aXAtY29udGFpbmVyJywgY29udGFpbmVyKTtcclxuXHRcdHRoaXMuX3RpcCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIHByZWZpeCArICctdGlwJywgdGhpcy5fdGlwQ29udGFpbmVyKTtcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlTGF5b3V0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fY29udGVudE5vZGUsXHJcblx0XHQgICAgc3R5bGUgPSBjb250YWluZXIuc3R5bGU7XHJcblxyXG5cdFx0c3R5bGUud2lkdGggPSAnJztcclxuXHRcdHN0eWxlLndoaXRlU3BhY2UgPSAnbm93cmFwJztcclxuXHJcblx0XHR2YXIgd2lkdGggPSBjb250YWluZXIub2Zmc2V0V2lkdGg7XHJcblx0XHR3aWR0aCA9IE1hdGgubWluKHdpZHRoLCB0aGlzLm9wdGlvbnMubWF4V2lkdGgpO1xyXG5cdFx0d2lkdGggPSBNYXRoLm1heCh3aWR0aCwgdGhpcy5vcHRpb25zLm1pbldpZHRoKTtcclxuXHJcblx0XHRzdHlsZS53aWR0aCA9ICh3aWR0aCArIDEpICsgJ3B4JztcclxuXHRcdHN0eWxlLndoaXRlU3BhY2UgPSAnJztcclxuXHJcblx0XHRzdHlsZS5oZWlnaHQgPSAnJztcclxuXHJcblx0XHR2YXIgaGVpZ2h0ID0gY29udGFpbmVyLm9mZnNldEhlaWdodCxcclxuXHRcdCAgICBtYXhIZWlnaHQgPSB0aGlzLm9wdGlvbnMubWF4SGVpZ2h0LFxyXG5cdFx0ICAgIHNjcm9sbGVkQ2xhc3MgPSAnbGVhZmxldC1wb3B1cC1zY3JvbGxlZCc7XHJcblxyXG5cdFx0aWYgKG1heEhlaWdodCAmJiBoZWlnaHQgPiBtYXhIZWlnaHQpIHtcclxuXHRcdFx0c3R5bGUuaGVpZ2h0ID0gbWF4SGVpZ2h0ICsgJ3B4JztcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgc2Nyb2xsZWRDbGFzcyk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3MoY29udGFpbmVyLCBzY3JvbGxlZENsYXNzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9jb250YWluZXJXaWR0aCA9IHRoaXMuX2NvbnRhaW5lci5vZmZzZXRXaWR0aDtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLl9sYXRMbmdUb05ld0xheWVyUG9pbnQodGhpcy5fbGF0bG5nLCBlLnpvb20sIGUuY2VudGVyKSxcclxuXHRcdCAgICBhbmNob3IgPSB0aGlzLl9nZXRBbmNob3IoKTtcclxuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aGlzLl9jb250YWluZXIsIHBvcy5hZGQoYW5jaG9yKSk7XHJcblx0fSxcclxuXHJcblx0X2FkanVzdFBhbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLm9wdGlvbnMuYXV0b1BhbiB8fCAodGhpcy5fbWFwLl9wYW5BbmltICYmIHRoaXMuX21hcC5fcGFuQW5pbS5faW5Qcm9ncmVzcykpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcclxuXHRcdCAgICBtYXJnaW5Cb3R0b20gPSBwYXJzZUludChMLkRvbVV0aWwuZ2V0U3R5bGUodGhpcy5fY29udGFpbmVyLCAnbWFyZ2luQm90dG9tJyksIDEwKSB8fCAwLFxyXG5cdFx0ICAgIGNvbnRhaW5lckhlaWdodCA9IHRoaXMuX2NvbnRhaW5lci5vZmZzZXRIZWlnaHQgKyBtYXJnaW5Cb3R0b20sXHJcblx0XHQgICAgY29udGFpbmVyV2lkdGggPSB0aGlzLl9jb250YWluZXJXaWR0aCxcclxuXHRcdCAgICBsYXllclBvcyA9IG5ldyBMLlBvaW50KHRoaXMuX2NvbnRhaW5lckxlZnQsIC1jb250YWluZXJIZWlnaHQgLSB0aGlzLl9jb250YWluZXJCb3R0b20pO1xyXG5cclxuXHRcdGxheWVyUG9zLl9hZGQoTC5Eb21VdGlsLmdldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lcikpO1xyXG5cclxuXHRcdHZhciBjb250YWluZXJQb3MgPSBtYXAubGF5ZXJQb2ludFRvQ29udGFpbmVyUG9pbnQobGF5ZXJQb3MpLFxyXG5cdFx0ICAgIHBhZGRpbmcgPSBMLnBvaW50KHRoaXMub3B0aW9ucy5hdXRvUGFuUGFkZGluZyksXHJcblx0XHQgICAgcGFkZGluZ1RMID0gTC5wb2ludCh0aGlzLm9wdGlvbnMuYXV0b1BhblBhZGRpbmdUb3BMZWZ0IHx8IHBhZGRpbmcpLFxyXG5cdFx0ICAgIHBhZGRpbmdCUiA9IEwucG9pbnQodGhpcy5vcHRpb25zLmF1dG9QYW5QYWRkaW5nQm90dG9tUmlnaHQgfHwgcGFkZGluZyksXHJcblx0XHQgICAgc2l6ZSA9IG1hcC5nZXRTaXplKCksXHJcblx0XHQgICAgZHggPSAwLFxyXG5cdFx0ICAgIGR5ID0gMDtcclxuXHJcblx0XHRpZiAoY29udGFpbmVyUG9zLnggKyBjb250YWluZXJXaWR0aCArIHBhZGRpbmdCUi54ID4gc2l6ZS54KSB7IC8vIHJpZ2h0XHJcblx0XHRcdGR4ID0gY29udGFpbmVyUG9zLnggKyBjb250YWluZXJXaWR0aCAtIHNpemUueCArIHBhZGRpbmdCUi54O1xyXG5cdFx0fVxyXG5cdFx0aWYgKGNvbnRhaW5lclBvcy54IC0gZHggLSBwYWRkaW5nVEwueCA8IDApIHsgLy8gbGVmdFxyXG5cdFx0XHRkeCA9IGNvbnRhaW5lclBvcy54IC0gcGFkZGluZ1RMLng7XHJcblx0XHR9XHJcblx0XHRpZiAoY29udGFpbmVyUG9zLnkgKyBjb250YWluZXJIZWlnaHQgKyBwYWRkaW5nQlIueSA+IHNpemUueSkgeyAvLyBib3R0b21cclxuXHRcdFx0ZHkgPSBjb250YWluZXJQb3MueSArIGNvbnRhaW5lckhlaWdodCAtIHNpemUueSArIHBhZGRpbmdCUi55O1xyXG5cdFx0fVxyXG5cdFx0aWYgKGNvbnRhaW5lclBvcy55IC0gZHkgLSBwYWRkaW5nVEwueSA8IDApIHsgLy8gdG9wXHJcblx0XHRcdGR5ID0gY29udGFpbmVyUG9zLnkgLSBwYWRkaW5nVEwueTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxyXG5cdFx0Ly8gQHNlY3Rpb24gUG9wdXAgZXZlbnRzXHJcblx0XHQvLyBAZXZlbnQgYXV0b3BhbnN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIHN0YXJ0cyBhdXRvcGFubmluZyB3aGVuIG9wZW5pbmcgYSBwb3B1cC5cclxuXHRcdGlmIChkeCB8fCBkeSkge1xyXG5cdFx0XHRtYXBcclxuXHRcdFx0ICAgIC5maXJlKCdhdXRvcGFuc3RhcnQnKVxyXG5cdFx0XHQgICAgLnBhbkJ5KFtkeCwgZHldKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfb25DbG9zZUJ1dHRvbkNsaWNrOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dGhpcy5fY2xvc2UoKTtcclxuXHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0QW5jaG9yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHQvLyBXaGVyZSBzaG91bGQgd2UgYW5jaG9yIHRoZSBwb3B1cCBvbiB0aGUgc291cmNlIGxheWVyP1xyXG5cdFx0cmV0dXJuIEwucG9pbnQodGhpcy5fc291cmNlICYmIHRoaXMuX3NvdXJjZS5fZ2V0UG9wdXBBbmNob3IgPyB0aGlzLl9zb3VyY2UuX2dldFBvcHVwQW5jaG9yKCkgOiBbMCwgMF0pO1xyXG5cdH1cclxuXHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBQb3B1cFxyXG4vLyBAZmFjdG9yeSBMLnBvcHVwKG9wdGlvbnM/OiBQb3B1cCBvcHRpb25zLCBzb3VyY2U/OiBMYXllcilcclxuLy8gSW5zdGFudGlhdGVzIGEgYFBvcHVwYCBvYmplY3QgZ2l2ZW4gYW4gb3B0aW9uYWwgYG9wdGlvbnNgIG9iamVjdCB0aGF0IGRlc2NyaWJlcyBpdHMgYXBwZWFyYW5jZSBhbmQgbG9jYXRpb24gYW5kIGFuIG9wdGlvbmFsIGBzb3VyY2VgIG9iamVjdCB0aGF0IGlzIHVzZWQgdG8gdGFnIHRoZSBwb3B1cCB3aXRoIGEgcmVmZXJlbmNlIHRvIHRoZSBMYXllciB0byB3aGljaCBpdCByZWZlcnMuXHJcbkwucG9wdXAgPSBmdW5jdGlvbiAob3B0aW9ucywgc291cmNlKSB7XHJcblx0cmV0dXJuIG5ldyBMLlBvcHVwKG9wdGlvbnMsIHNvdXJjZSk7XHJcbn07XHJcblxyXG5cclxuLyogQG5hbWVzcGFjZSBNYXBcclxuICogQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xyXG4gKiBAb3B0aW9uIGNsb3NlUG9wdXBPbkNsaWNrOiBCb29sZWFuID0gdHJ1ZVxyXG4gKiBTZXQgaXQgdG8gYGZhbHNlYCBpZiB5b3UgZG9uJ3Qgd2FudCBwb3B1cHMgdG8gY2xvc2Ugd2hlbiB1c2VyIGNsaWNrcyB0aGUgbWFwLlxyXG4gKi9cclxuTC5NYXAubWVyZ2VPcHRpb25zKHtcclxuXHRjbG9zZVBvcHVwT25DbGljazogdHJ1ZVxyXG59KTtcclxuXHJcblxyXG4vLyBAbmFtZXNwYWNlIE1hcFxyXG4vLyBAc2VjdGlvbiBNZXRob2RzIGZvciBMYXllcnMgYW5kIENvbnRyb2xzXHJcbkwuTWFwLmluY2x1ZGUoe1xyXG5cdC8vIEBtZXRob2Qgb3BlblBvcHVwKHBvcHVwOiBQb3B1cCk6IHRoaXNcclxuXHQvLyBPcGVucyB0aGUgc3BlY2lmaWVkIHBvcHVwIHdoaWxlIGNsb3NpbmcgdGhlIHByZXZpb3VzbHkgb3BlbmVkICh0byBtYWtlIHN1cmUgb25seSBvbmUgaXMgb3BlbmVkIGF0IG9uZSB0aW1lIGZvciB1c2FiaWxpdHkpLlxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2Qgb3BlblBvcHVwKGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudCwgbGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBQb3B1cCBvcHRpb25zKTogdGhpc1xyXG5cdC8vIENyZWF0ZXMgYSBwb3B1cCB3aXRoIHRoZSBzcGVjaWZpZWQgY29udGVudCBhbmQgb3B0aW9ucyBhbmQgb3BlbnMgaXQgaW4gdGhlIGdpdmVuIHBvaW50IG9uIGEgbWFwLlxyXG5cdG9wZW5Qb3B1cDogZnVuY3Rpb24gKHBvcHVwLCBsYXRsbmcsIG9wdGlvbnMpIHtcclxuXHRcdGlmICghKHBvcHVwIGluc3RhbmNlb2YgTC5Qb3B1cCkpIHtcclxuXHRcdFx0cG9wdXAgPSBuZXcgTC5Qb3B1cChvcHRpb25zKS5zZXRDb250ZW50KHBvcHVwKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAobGF0bG5nKSB7XHJcblx0XHRcdHBvcHVwLnNldExhdExuZyhsYXRsbmcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLmhhc0xheWVyKHBvcHVwKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fcG9wdXAgJiYgdGhpcy5fcG9wdXAub3B0aW9ucy5hdXRvQ2xvc2UpIHtcclxuXHRcdFx0dGhpcy5jbG9zZVBvcHVwKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fcG9wdXAgPSBwb3B1cDtcclxuXHRcdHJldHVybiB0aGlzLmFkZExheWVyKHBvcHVwKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNsb3NlUG9wdXAocG9wdXA/OiBQb3B1cCk6IHRoaXNcclxuXHQvLyBDbG9zZXMgdGhlIHBvcHVwIHByZXZpb3VzbHkgb3BlbmVkIHdpdGggW29wZW5Qb3B1cF0oI21hcC1vcGVucG9wdXApIChvciB0aGUgZ2l2ZW4gb25lKS5cclxuXHRjbG9zZVBvcHVwOiBmdW5jdGlvbiAocG9wdXApIHtcclxuXHRcdGlmICghcG9wdXAgfHwgcG9wdXAgPT09IHRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdHBvcHVwID0gdGhpcy5fcG9wdXA7XHJcblx0XHRcdHRoaXMuX3BvcHVwID0gbnVsbDtcclxuXHRcdH1cclxuXHRcdGlmIChwb3B1cCkge1xyXG5cdFx0XHR0aGlzLnJlbW92ZUxheWVyKHBvcHVwKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH1cclxufSk7XHJcblxyXG4vKlxyXG4gKiBAbmFtZXNwYWNlIExheWVyXHJcbiAqIEBzZWN0aW9uIFBvcHVwIG1ldGhvZHMgZXhhbXBsZVxyXG4gKlxyXG4gKiBBbGwgbGF5ZXJzIHNoYXJlIGEgc2V0IG9mIG1ldGhvZHMgY29udmVuaWVudCBmb3IgYmluZGluZyBwb3B1cHMgdG8gaXQuXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBsYXllciA9IEwuUG9seWdvbihsYXRsbmdzKS5iaW5kUG9wdXAoJ0hpIFRoZXJlIScpLmFkZFRvKG1hcCk7XHJcbiAqIGxheWVyLm9wZW5Qb3B1cCgpO1xyXG4gKiBsYXllci5jbG9zZVBvcHVwKCk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBQb3B1cHMgd2lsbCBhbHNvIGJlIGF1dG9tYXRpY2FsbHkgb3BlbmVkIHdoZW4gdGhlIGxheWVyIGlzIGNsaWNrZWQgb24gYW5kIGNsb3NlZCB3aGVuIHRoZSBsYXllciBpcyByZW1vdmVkIGZyb20gdGhlIG1hcCBvciBhbm90aGVyIHBvcHVwIGlzIG9wZW5lZC5cclxuICovXHJcblxyXG4vLyBAc2VjdGlvbiBQb3B1cCBtZXRob2RzXHJcbkwuTGF5ZXIuaW5jbHVkZSh7XHJcblxyXG5cdC8vIEBtZXRob2QgYmluZFBvcHVwKGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxGdW5jdGlvbnxQb3B1cCwgb3B0aW9ucz86IFBvcHVwIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gQmluZHMgYSBwb3B1cCB0byB0aGUgbGF5ZXIgd2l0aCB0aGUgcGFzc2VkIGBjb250ZW50YCBhbmQgc2V0cyB1cCB0aGVcclxuXHQvLyBuZWNjZXNzYXJ5IGV2ZW50IGxpc3RlbmVycy4gSWYgYSBgRnVuY3Rpb25gIGlzIHBhc3NlZCBpdCB3aWxsIHJlY2VpdmVcclxuXHQvLyB0aGUgbGF5ZXIgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IGFuZCBzaG91bGQgcmV0dXJuIGEgYFN0cmluZ2Agb3IgYEhUTUxFbGVtZW50YC5cclxuXHRiaW5kUG9wdXA6IGZ1bmN0aW9uIChjb250ZW50LCBvcHRpb25zKSB7XHJcblxyXG5cdFx0aWYgKGNvbnRlbnQgaW5zdGFuY2VvZiBMLlBvcHVwKSB7XHJcblx0XHRcdEwuc2V0T3B0aW9ucyhjb250ZW50LCBvcHRpb25zKTtcclxuXHRcdFx0dGhpcy5fcG9wdXAgPSBjb250ZW50O1xyXG5cdFx0XHRjb250ZW50Ll9zb3VyY2UgPSB0aGlzO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0aWYgKCF0aGlzLl9wb3B1cCB8fCBvcHRpb25zKSB7XHJcblx0XHRcdFx0dGhpcy5fcG9wdXAgPSBuZXcgTC5Qb3B1cChvcHRpb25zLCB0aGlzKTtcclxuXHRcdFx0fVxyXG5cdFx0XHR0aGlzLl9wb3B1cC5zZXRDb250ZW50KGNvbnRlbnQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghdGhpcy5fcG9wdXBIYW5kbGVyc0FkZGVkKSB7XHJcblx0XHRcdHRoaXMub24oe1xyXG5cdFx0XHRcdGNsaWNrOiB0aGlzLl9vcGVuUG9wdXAsXHJcblx0XHRcdFx0cmVtb3ZlOiB0aGlzLmNsb3NlUG9wdXAsXHJcblx0XHRcdFx0bW92ZTogdGhpcy5fbW92ZVBvcHVwXHJcblx0XHRcdH0pO1xyXG5cdFx0XHR0aGlzLl9wb3B1cEhhbmRsZXJzQWRkZWQgPSB0cnVlO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW5iaW5kUG9wdXAoKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIHBvcHVwIHByZXZpb3VzbHkgYm91bmQgd2l0aCBgYmluZFBvcHVwYC5cclxuXHR1bmJpbmRQb3B1cDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdHRoaXMub2ZmKHtcclxuXHRcdFx0XHRjbGljazogdGhpcy5fb3BlblBvcHVwLFxyXG5cdFx0XHRcdHJlbW92ZTogdGhpcy5jbG9zZVBvcHVwLFxyXG5cdFx0XHRcdG1vdmU6IHRoaXMuX21vdmVQb3B1cFxyXG5cdFx0XHR9KTtcclxuXHRcdFx0dGhpcy5fcG9wdXBIYW5kbGVyc0FkZGVkID0gZmFsc2U7XHJcblx0XHRcdHRoaXMuX3BvcHVwID0gbnVsbDtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgb3BlblBvcHVwKGxhdGxuZz86IExhdExuZyk6IHRoaXNcclxuXHQvLyBPcGVucyB0aGUgYm91bmQgcG9wdXAgYXQgdGhlIHNwZWNpZmljZWQgYGxhdGxuZ2Agb3IgYXQgdGhlIGRlZmF1bHQgcG9wdXAgYW5jaG9yIGlmIG5vIGBsYXRsbmdgIGlzIHBhc3NlZC5cclxuXHRvcGVuUG9wdXA6IGZ1bmN0aW9uIChsYXllciwgbGF0bG5nKSB7XHJcblx0XHRpZiAoIShsYXllciBpbnN0YW5jZW9mIEwuTGF5ZXIpKSB7XHJcblx0XHRcdGxhdGxuZyA9IGxheWVyO1xyXG5cdFx0XHRsYXllciA9IHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGxheWVyIGluc3RhbmNlb2YgTC5GZWF0dXJlR3JvdXApIHtcclxuXHRcdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdFx0bGF5ZXIgPSB0aGlzLl9sYXllcnNbaWRdO1xyXG5cdFx0XHRcdGJyZWFrO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCFsYXRsbmcpIHtcclxuXHRcdFx0bGF0bG5nID0gbGF5ZXIuZ2V0Q2VudGVyID8gbGF5ZXIuZ2V0Q2VudGVyKCkgOiBsYXllci5nZXRMYXRMbmcoKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fcG9wdXAgJiYgdGhpcy5fbWFwKSB7XHJcblx0XHRcdC8vIHNldCBwb3B1cCBzb3VyY2UgdG8gdGhpcyBsYXllclxyXG5cdFx0XHR0aGlzLl9wb3B1cC5fc291cmNlID0gbGF5ZXI7XHJcblxyXG5cdFx0XHQvLyB1cGRhdGUgdGhlIHBvcHVwIChjb250ZW50LCBsYXlvdXQsIGVjdC4uLilcclxuXHRcdFx0dGhpcy5fcG9wdXAudXBkYXRlKCk7XHJcblxyXG5cdFx0XHQvLyBvcGVuIHRoZSBwb3B1cCBvbiB0aGUgbWFwXHJcblx0XHRcdHRoaXMuX21hcC5vcGVuUG9wdXAodGhpcy5fcG9wdXAsIGxhdGxuZyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjbG9zZVBvcHVwKCk6IHRoaXNcclxuXHQvLyBDbG9zZXMgdGhlIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaWYgaXQgaXMgb3Blbi5cclxuXHRjbG9zZVBvcHVwOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fcG9wdXApIHtcclxuXHRcdFx0dGhpcy5fcG9wdXAuX2Nsb3NlKCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHRvZ2dsZVBvcHVwKCk6IHRoaXNcclxuXHQvLyBPcGVucyBvciBjbG9zZXMgdGhlIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgZGVwZW5kaW5nIG9uIGl0cyBjdXJyZW50IHN0YXRlLlxyXG5cdHRvZ2dsZVBvcHVwOiBmdW5jdGlvbiAodGFyZ2V0KSB7XHJcblx0XHRpZiAodGhpcy5fcG9wdXApIHtcclxuXHRcdFx0aWYgKHRoaXMuX3BvcHVwLl9tYXApIHtcclxuXHRcdFx0XHR0aGlzLmNsb3NlUG9wdXAoKTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHR0aGlzLm9wZW5Qb3B1cCh0YXJnZXQpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGlzUG9wdXBPcGVuKCk6IGJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllciBpcyBjdXJyZW50bHkgb3Blbi5cclxuXHRpc1BvcHVwT3BlbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3BvcHVwLmlzT3BlbigpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0UG9wdXBDb250ZW50KGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxQb3B1cCk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBjb250ZW50IG9mIHRoZSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyLlxyXG5cdHNldFBvcHVwQ29udGVudDogZnVuY3Rpb24gKGNvbnRlbnQpIHtcclxuXHRcdGlmICh0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHR0aGlzLl9wb3B1cC5zZXRDb250ZW50KGNvbnRlbnQpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRQb3B1cCgpOiBQb3B1cFxyXG5cdC8vIFJldHVybnMgdGhlIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIuXHJcblx0Z2V0UG9wdXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9wb3B1cDtcclxuXHR9LFxyXG5cclxuXHRfb3BlblBvcHVwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dmFyIGxheWVyID0gZS5sYXllciB8fCBlLnRhcmdldDtcclxuXHJcblx0XHRpZiAoIXRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXRoaXMuX21hcCkge1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gcHJldmVudCBtYXAgY2xpY2tcclxuXHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcclxuXHJcblx0XHQvLyBpZiB0aGlzIGluaGVyaXRzIGZyb20gUGF0aCBpdHMgYSB2ZWN0b3IgYW5kIHdlIGNhbiBqdXN0XHJcblx0XHQvLyBvcGVuIHRoZSBwb3B1cCBhdCB0aGUgbmV3IGxvY2F0aW9uXHJcblx0XHRpZiAobGF5ZXIgaW5zdGFuY2VvZiBMLlBhdGgpIHtcclxuXHRcdFx0dGhpcy5vcGVuUG9wdXAoZS5sYXllciB8fCBlLnRhcmdldCwgZS5sYXRsbmcpO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gb3RoZXJ3aXNlIHRyZWF0IGl0IGxpa2UgYSBtYXJrZXIgYW5kIGZpZ3VyZSBvdXRcclxuXHRcdC8vIGlmIHdlIHNob3VsZCB0b2dnbGUgaXQgb3Blbi9jbG9zZWRcclxuXHRcdGlmICh0aGlzLl9tYXAuaGFzTGF5ZXIodGhpcy5fcG9wdXApICYmIHRoaXMuX3BvcHVwLl9zb3VyY2UgPT09IGxheWVyKSB7XHJcblx0XHRcdHRoaXMuY2xvc2VQb3B1cCgpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0dGhpcy5vcGVuUG9wdXAobGF5ZXIsIGUubGF0bG5nKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfbW92ZVBvcHVwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dGhpcy5fcG9wdXAuc2V0TGF0TG5nKGUubGF0bG5nKTtcclxuXHR9XHJcbn0pO1xyXG5cblxuXG4vKlxuICogQGNsYXNzIFRvb2x0aXBcbiAqIEBpbmhlcml0cyBEaXZPdmVybGF5XG4gKiBAYWthIEwuVG9vbHRpcFxuICogVXNlZCB0byBkaXNwbGF5IHNtYWxsIHRleHRzIG9uIHRvcCBvZiBtYXAgbGF5ZXJzLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIG1hcmtlci5iaW5kVG9vbHRpcChcIm15IHRvb2x0aXAgdGV4dFwiKS5vcGVuVG9vbHRpcCgpO1xuICogYGBgXG4gKiBOb3RlIGFib3V0IHRvb2x0aXAgb2Zmc2V0LiBMZWFmbGV0IHRha2VzIHR3byBvcHRpb25zIGluIGNvbnNpZGVyYXRpb25cbiAqIGZvciBjb21wdXRpbmcgdG9vbHRpcCBvZmZzZXRpbmc6XG4gKiAtIHRoZSBgb2Zmc2V0YCBUb29sdGlwIG9wdGlvbjogaXQgZGVmYXVsdHMgdG8gWzAsIDBdLCBhbmQgaXQncyBzcGVjaWZpYyB0byBvbmUgdG9vbHRpcC5cbiAqICAgQWRkIGEgcG9zaXRpdmUgeCBvZmZzZXQgdG8gbW92ZSB0aGUgdG9vbHRpcCB0byB0aGUgcmlnaHQsIGFuZCBhIHBvc2l0aXZlIHkgb2Zmc2V0IHRvXG4gKiAgIG1vdmUgaXQgdG8gdGhlIGJvdHRvbS4gTmVnYXRpdmVzIHdpbGwgbW92ZSB0byB0aGUgbGVmdCBhbmQgdG9wLlxuICogLSB0aGUgYHRvb2x0aXBBbmNob3JgIEljb24gb3B0aW9uOiB0aGlzIHdpbGwgb25seSBiZSBjb25zaWRlcmVkIGZvciBNYXJrZXIuIFlvdVxuICogICBzaG91bGQgYWRhcHQgdGhpcyB2YWx1ZSBpZiB5b3UgdXNlIGEgY3VzdG9tIGljb24uXG4gKi9cblxuXG4vLyBAbmFtZXNwYWNlIFRvb2x0aXBcbkwuVG9vbHRpcCA9IEwuRGl2T3ZlcmxheS5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgVG9vbHRpcCBvcHRpb25zXG5cdG9wdGlvbnM6IHtcblx0XHQvLyBAb3B0aW9uIHBhbmU6IFN0cmluZyA9ICd0b29sdGlwUGFuZSdcblx0XHQvLyBgTWFwIHBhbmVgIHdoZXJlIHRoZSB0b29sdGlwIHdpbGwgYmUgYWRkZWQuXG5cdFx0cGFuZTogJ3Rvb2x0aXBQYW5lJyxcblxuXHRcdC8vIEBvcHRpb24gb2Zmc2V0OiBQb2ludCA9IFBvaW50KDAsIDApXG5cdFx0Ly8gT3B0aW9uYWwgb2Zmc2V0IG9mIHRoZSB0b29sdGlwIHBvc2l0aW9uLlxuXHRcdG9mZnNldDogWzAsIDBdLFxuXG5cdFx0Ly8gQG9wdGlvbiBkaXJlY3Rpb246IFN0cmluZyA9ICdhdXRvJ1xuXHRcdC8vIERpcmVjdGlvbiB3aGVyZSB0byBvcGVuIHRoZSB0b29sdGlwLiBQb3NzaWJsZSB2YWx1ZXMgYXJlOiBgcmlnaHRgLCBgbGVmdGAsXG5cdFx0Ly8gYHRvcGAsIGBib3R0b21gLCBgY2VudGVyYCwgYGF1dG9gLlxuXHRcdC8vIGBhdXRvYCB3aWxsIGR5bmFtaWNhbHkgc3dpdGNoIGJldHdlZW4gYHJpZ2h0YCBhbmQgYGxlZnRgIGFjY29yZGluZyB0byB0aGUgdG9vbHRpcFxuXHRcdC8vIHBvc2l0aW9uIG9uIHRoZSBtYXAuXG5cdFx0ZGlyZWN0aW9uOiAnYXV0bycsXG5cblx0XHQvLyBAb3B0aW9uIHBlcm1hbmVudDogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gV2hldGhlciB0byBvcGVuIHRoZSB0b29sdGlwIHBlcm1hbmVudGx5IG9yIG9ubHkgb24gbW91c2VvdmVyLlxuXHRcdHBlcm1hbmVudDogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIHN0aWNreTogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gSWYgdHJ1ZSwgdGhlIHRvb2x0aXAgd2lsbCBmb2xsb3cgdGhlIG1vdXNlIGluc3RlYWQgb2YgYmVpbmcgZml4ZWQgYXQgdGhlIGZlYXR1cmUgY2VudGVyLlxuXHRcdHN0aWNreTogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIGludGVyYWN0aXZlOiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBJZiB0cnVlLCB0aGUgdG9vbHRpcCB3aWxsIGxpc3RlbiB0byB0aGUgZmVhdHVyZSBldmVudHMuXG5cdFx0aW50ZXJhY3RpdmU6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBvcGFjaXR5OiBOdW1iZXIgPSAwLjlcblx0XHQvLyBUb29sdGlwIGNvbnRhaW5lciBvcGFjaXR5LlxuXHRcdG9wYWNpdHk6IDAuOVxuXHR9LFxuXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0TC5EaXZPdmVybGF5LnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMsIG1hcCk7XG5cdFx0dGhpcy5zZXRPcGFjaXR5KHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXG5cdFx0Ly8gQHNlY3Rpb24gVG9vbHRpcCBldmVudHNcblx0XHQvLyBAZXZlbnQgdG9vbHRpcG9wZW46IFRvb2x0aXBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gYSB0b29sdGlwIGlzIG9wZW5lZCBpbiB0aGUgbWFwLlxuXHRcdG1hcC5maXJlKCd0b29sdGlwb3BlbicsIHt0b29sdGlwOiB0aGlzfSk7XG5cblx0XHRpZiAodGhpcy5fc291cmNlKSB7XG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXG5cdFx0XHQvLyBAc2VjdGlvbiBUb29sdGlwIGV2ZW50c1xuXHRcdFx0Ly8gQGV2ZW50IHRvb2x0aXBvcGVuOiBUb29sdGlwRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gYSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgb3BlbmVkLlxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3Rvb2x0aXBvcGVuJywge3Rvb2x0aXA6IHRoaXN9LCB0cnVlKTtcblx0XHR9XG5cdH0sXG5cblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHRMLkRpdk92ZXJsYXkucHJvdG90eXBlLm9uUmVtb3ZlLmNhbGwodGhpcywgbWFwKTtcblxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXG5cdFx0Ly8gQHNlY3Rpb24gVG9vbHRpcCBldmVudHNcblx0XHQvLyBAZXZlbnQgdG9vbHRpcGNsb3NlOiBUb29sdGlwRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIGEgdG9vbHRpcCBpbiB0aGUgbWFwIGlzIGNsb3NlZC5cblx0XHRtYXAuZmlyZSgndG9vbHRpcGNsb3NlJywge3Rvb2x0aXA6IHRoaXN9KTtcblxuXHRcdGlmICh0aGlzLl9zb3VyY2UpIHtcblx0XHRcdC8vIEBuYW1lc3BhY2UgTGF5ZXJcblx0XHRcdC8vIEBzZWN0aW9uIFRvb2x0aXAgZXZlbnRzXG5cdFx0XHQvLyBAZXZlbnQgdG9vbHRpcGNsb3NlOiBUb29sdGlwRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gYSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgY2xvc2VkLlxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3Rvb2x0aXBjbG9zZScsIHt0b29sdGlwOiB0aGlzfSwgdHJ1ZSk7XG5cdFx0fVxuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSBMLkRpdk92ZXJsYXkucHJvdG90eXBlLmdldEV2ZW50cy5jYWxsKHRoaXMpO1xuXG5cdFx0aWYgKEwuQnJvd3Nlci50b3VjaCAmJiAhdGhpcy5vcHRpb25zLnBlcm1hbmVudCkge1xuXHRcdFx0ZXZlbnRzLnByZWNsaWNrID0gdGhpcy5fY2xvc2U7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGV2ZW50cztcblx0fSxcblxuXHRfY2xvc2U6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0aGlzLl9tYXAuY2xvc2VUb29sdGlwKHRoaXMpO1xuXHRcdH1cblx0fSxcblxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBwcmVmaXggPSAnbGVhZmxldC10b29sdGlwJyxcblx0XHQgICAgY2xhc3NOYW1lID0gcHJlZml4ICsgJyAnICsgKHRoaXMub3B0aW9ucy5jbGFzc05hbWUgfHwgJycpICsgJyBsZWFmbGV0LXpvb20tJyArICh0aGlzLl96b29tQW5pbWF0ZWQgPyAnYW5pbWF0ZWQnIDogJ2hpZGUnKTtcblxuXHRcdHRoaXMuX2NvbnRlbnROb2RlID0gdGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lKTtcblx0fSxcblxuXHRfdXBkYXRlTGF5b3V0OiBmdW5jdGlvbiAoKSB7fSxcblxuXHRfYWRqdXN0UGFuOiBmdW5jdGlvbiAoKSB7fSxcblxuXHRfc2V0UG9zaXRpb246IGZ1bmN0aW9uIChwb3MpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIsXG5cdFx0ICAgIGNlbnRlclBvaW50ID0gbWFwLmxhdExuZ1RvQ29udGFpbmVyUG9pbnQobWFwLmdldENlbnRlcigpKSxcblx0XHQgICAgdG9vbHRpcFBvaW50ID0gbWFwLmxheWVyUG9pbnRUb0NvbnRhaW5lclBvaW50KHBvcyksXG5cdFx0ICAgIGRpcmVjdGlvbiA9IHRoaXMub3B0aW9ucy5kaXJlY3Rpb24sXG5cdFx0ICAgIHRvb2x0aXBXaWR0aCA9IGNvbnRhaW5lci5vZmZzZXRXaWR0aCxcblx0XHQgICAgdG9vbHRpcEhlaWdodCA9IGNvbnRhaW5lci5vZmZzZXRIZWlnaHQsXG5cdFx0ICAgIG9mZnNldCA9IEwucG9pbnQodGhpcy5vcHRpb25zLm9mZnNldCksXG5cdFx0ICAgIGFuY2hvciA9IHRoaXMuX2dldEFuY2hvcigpO1xuXG5cdFx0aWYgKGRpcmVjdGlvbiA9PT0gJ3RvcCcpIHtcblx0XHRcdHBvcyA9IHBvcy5hZGQoTC5wb2ludCgtdG9vbHRpcFdpZHRoIC8gMiArIG9mZnNldC54LCAtdG9vbHRpcEhlaWdodCArIG9mZnNldC55ICsgYW5jaG9yLnksIHRydWUpKTtcblx0XHR9IGVsc2UgaWYgKGRpcmVjdGlvbiA9PT0gJ2JvdHRvbScpIHtcblx0XHRcdHBvcyA9IHBvcy5zdWJ0cmFjdChMLnBvaW50KHRvb2x0aXBXaWR0aCAvIDIgLSBvZmZzZXQueCwgLW9mZnNldC55LCB0cnVlKSk7XG5cdFx0fSBlbHNlIGlmIChkaXJlY3Rpb24gPT09ICdjZW50ZXInKSB7XG5cdFx0XHRwb3MgPSBwb3Muc3VidHJhY3QoTC5wb2ludCh0b29sdGlwV2lkdGggLyAyICsgb2Zmc2V0LngsIHRvb2x0aXBIZWlnaHQgLyAyIC0gYW5jaG9yLnkgKyBvZmZzZXQueSwgdHJ1ZSkpO1xuXHRcdH0gZWxzZSBpZiAoZGlyZWN0aW9uID09PSAncmlnaHQnIHx8IGRpcmVjdGlvbiA9PT0gJ2F1dG8nICYmIHRvb2x0aXBQb2ludC54IDwgY2VudGVyUG9pbnQueCkge1xuXHRcdFx0ZGlyZWN0aW9uID0gJ3JpZ2h0Jztcblx0XHRcdHBvcyA9IHBvcy5hZGQoTC5wb2ludChvZmZzZXQueCArIGFuY2hvci54LCBhbmNob3IueSAtIHRvb2x0aXBIZWlnaHQgLyAyICsgb2Zmc2V0LnksIHRydWUpKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0ZGlyZWN0aW9uID0gJ2xlZnQnO1xuXHRcdFx0cG9zID0gcG9zLnN1YnRyYWN0KEwucG9pbnQodG9vbHRpcFdpZHRoICsgYW5jaG9yLnggLSBvZmZzZXQueCwgdG9vbHRpcEhlaWdodCAvIDIgLSBhbmNob3IueSAtIG9mZnNldC55LCB0cnVlKSk7XG5cdFx0fVxuXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtdG9vbHRpcC1yaWdodCcpO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtbGVmdCcpO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtdG9wJyk7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtdG9vbHRpcC1ib3R0b20nKTtcblx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC10b29sdGlwLScgKyBkaXJlY3Rpb24pO1xuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbihjb250YWluZXIsIHBvcyk7XG5cdH0sXG5cblx0X3VwZGF0ZVBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fbGF0bG5nKTtcblx0XHR0aGlzLl9zZXRQb3NpdGlvbihwb3MpO1xuXHR9LFxuXG5cdHNldE9wYWNpdHk6IGZ1bmN0aW9uIChvcGFjaXR5KSB7XG5cdFx0dGhpcy5vcHRpb25zLm9wYWNpdHkgPSBvcGFjaXR5O1xuXG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lcikge1xuXHRcdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGhpcy5fY29udGFpbmVyLCBvcGFjaXR5KTtcblx0XHR9XG5cdH0sXG5cblx0X2FuaW1hdGVab29tOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBwb3MgPSB0aGlzLl9tYXAuX2xhdExuZ1RvTmV3TGF5ZXJQb2ludCh0aGlzLl9sYXRsbmcsIGUuem9vbSwgZS5jZW50ZXIpO1xuXHRcdHRoaXMuX3NldFBvc2l0aW9uKHBvcyk7XG5cdH0sXG5cblx0X2dldEFuY2hvcjogZnVuY3Rpb24gKCkge1xuXHRcdC8vIFdoZXJlIHNob3VsZCB3ZSBhbmNob3IgdGhlIHRvb2x0aXAgb24gdGhlIHNvdXJjZSBsYXllcj9cblx0XHRyZXR1cm4gTC5wb2ludCh0aGlzLl9zb3VyY2UgJiYgdGhpcy5fc291cmNlLl9nZXRUb29sdGlwQW5jaG9yICYmICF0aGlzLm9wdGlvbnMuc3RpY2t5ID8gdGhpcy5fc291cmNlLl9nZXRUb29sdGlwQW5jaG9yKCkgOiBbMCwgMF0pO1xuXHR9XG5cbn0pO1xuXG4vLyBAbmFtZXNwYWNlIFRvb2x0aXBcbi8vIEBmYWN0b3J5IEwudG9vbHRpcChvcHRpb25zPzogVG9vbHRpcCBvcHRpb25zLCBzb3VyY2U/OiBMYXllcilcbi8vIEluc3RhbnRpYXRlcyBhIFRvb2x0aXAgb2JqZWN0IGdpdmVuIGFuIG9wdGlvbmFsIGBvcHRpb25zYCBvYmplY3QgdGhhdCBkZXNjcmliZXMgaXRzIGFwcGVhcmFuY2UgYW5kIGxvY2F0aW9uIGFuZCBhbiBvcHRpb25hbCBgc291cmNlYCBvYmplY3QgdGhhdCBpcyB1c2VkIHRvIHRhZyB0aGUgdG9vbHRpcCB3aXRoIGEgcmVmZXJlbmNlIHRvIHRoZSBMYXllciB0byB3aGljaCBpdCByZWZlcnMuXG5MLnRvb2x0aXAgPSBmdW5jdGlvbiAob3B0aW9ucywgc291cmNlKSB7XG5cdHJldHVybiBuZXcgTC5Ub29sdGlwKG9wdGlvbnMsIHNvdXJjZSk7XG59O1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgTGF5ZXJzIGFuZCBDb250cm9sc1xuTC5NYXAuaW5jbHVkZSh7XG5cblx0Ly8gQG1ldGhvZCBvcGVuVG9vbHRpcCh0b29sdGlwOiBUb29sdGlwKTogdGhpc1xuXHQvLyBPcGVucyB0aGUgc3BlY2lmaWVkIHRvb2x0aXAuXG5cdC8vIEBhbHRlcm5hdGl2ZVxuXHQvLyBAbWV0aG9kIG9wZW5Ub29sdGlwKGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudCwgbGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBUb29sdGlwIG9wdGlvbnMpOiB0aGlzXG5cdC8vIENyZWF0ZXMgYSB0b29sdGlwIHdpdGggdGhlIHNwZWNpZmllZCBjb250ZW50IGFuZCBvcHRpb25zIGFuZCBvcGVuIGl0LlxuXHRvcGVuVG9vbHRpcDogZnVuY3Rpb24gKHRvb2x0aXAsIGxhdGxuZywgb3B0aW9ucykge1xuXHRcdGlmICghKHRvb2x0aXAgaW5zdGFuY2VvZiBMLlRvb2x0aXApKSB7XG5cdFx0XHR0b29sdGlwID0gbmV3IEwuVG9vbHRpcChvcHRpb25zKS5zZXRDb250ZW50KHRvb2x0aXApO1xuXHRcdH1cblxuXHRcdGlmIChsYXRsbmcpIHtcblx0XHRcdHRvb2x0aXAuc2V0TGF0TG5nKGxhdGxuZyk7XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMuaGFzTGF5ZXIodG9vbHRpcCkpIHtcblx0XHRcdHJldHVybiB0aGlzO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzLmFkZExheWVyKHRvb2x0aXApO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgY2xvc2VUb29sdGlwKHRvb2x0aXA/OiBUb29sdGlwKTogdGhpc1xuXHQvLyBDbG9zZXMgdGhlIHRvb2x0aXAgZ2l2ZW4gYXMgcGFyYW1ldGVyLlxuXHRjbG9zZVRvb2x0aXA6IGZ1bmN0aW9uICh0b29sdGlwKSB7XG5cdFx0aWYgKHRvb2x0aXApIHtcblx0XHRcdHRoaXMucmVtb3ZlTGF5ZXIodG9vbHRpcCk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cbn0pO1xuXG4vKlxuICogQG5hbWVzcGFjZSBMYXllclxuICogQHNlY3Rpb24gVG9vbHRpcCBtZXRob2RzIGV4YW1wbGVcbiAqXG4gKiBBbGwgbGF5ZXJzIHNoYXJlIGEgc2V0IG9mIG1ldGhvZHMgY29udmVuaWVudCBmb3IgYmluZGluZyB0b29sdGlwcyB0byBpdC5cbiAqXG4gKiBgYGBqc1xuICogdmFyIGxheWVyID0gTC5Qb2x5Z29uKGxhdGxuZ3MpLmJpbmRUb29sdGlwKCdIaSBUaGVyZSEnKS5hZGRUbyhtYXApO1xuICogbGF5ZXIub3BlblRvb2x0aXAoKTtcbiAqIGxheWVyLmNsb3NlVG9vbHRpcCgpO1xuICogYGBgXG4gKi9cblxuLy8gQHNlY3Rpb24gVG9vbHRpcCBtZXRob2RzXG5MLkxheWVyLmluY2x1ZGUoe1xuXG5cdC8vIEBtZXRob2QgYmluZFRvb2x0aXAoY29udGVudDogU3RyaW5nfEhUTUxFbGVtZW50fEZ1bmN0aW9ufFRvb2x0aXAsIG9wdGlvbnM/OiBUb29sdGlwIG9wdGlvbnMpOiB0aGlzXG5cdC8vIEJpbmRzIGEgdG9vbHRpcCB0byB0aGUgbGF5ZXIgd2l0aCB0aGUgcGFzc2VkIGBjb250ZW50YCBhbmQgc2V0cyB1cCB0aGVcblx0Ly8gbmVjY2Vzc2FyeSBldmVudCBsaXN0ZW5lcnMuIElmIGEgYEZ1bmN0aW9uYCBpcyBwYXNzZWQgaXQgd2lsbCByZWNlaXZlXG5cdC8vIHRoZSBsYXllciBhcyB0aGUgZmlyc3QgYXJndW1lbnQgYW5kIHNob3VsZCByZXR1cm4gYSBgU3RyaW5nYCBvciBgSFRNTEVsZW1lbnRgLlxuXHRiaW5kVG9vbHRpcDogZnVuY3Rpb24gKGNvbnRlbnQsIG9wdGlvbnMpIHtcblxuXHRcdGlmIChjb250ZW50IGluc3RhbmNlb2YgTC5Ub29sdGlwKSB7XG5cdFx0XHRMLnNldE9wdGlvbnMoY29udGVudCwgb3B0aW9ucyk7XG5cdFx0XHR0aGlzLl90b29sdGlwID0gY29udGVudDtcblx0XHRcdGNvbnRlbnQuX3NvdXJjZSA9IHRoaXM7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGlmICghdGhpcy5fdG9vbHRpcCB8fCBvcHRpb25zKSB7XG5cdFx0XHRcdHRoaXMuX3Rvb2x0aXAgPSBMLnRvb2x0aXAob3B0aW9ucywgdGhpcyk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLl90b29sdGlwLnNldENvbnRlbnQoY29udGVudCk7XG5cblx0XHR9XG5cblx0XHR0aGlzLl9pbml0VG9vbHRpcEludGVyYWN0aW9ucygpO1xuXG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXAub3B0aW9ucy5wZXJtYW5lbnQgJiYgdGhpcy5fbWFwICYmIHRoaXMuX21hcC5oYXNMYXllcih0aGlzKSkge1xuXHRcdFx0dGhpcy5vcGVuVG9vbHRpcCgpO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgdW5iaW5kVG9vbHRpcCgpOiB0aGlzXG5cdC8vIFJlbW92ZXMgdGhlIHRvb2x0aXAgcHJldmlvdXNseSBib3VuZCB3aXRoIGBiaW5kVG9vbHRpcGAuXG5cdHVuYmluZFRvb2x0aXA6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fdG9vbHRpcCkge1xuXHRcdFx0dGhpcy5faW5pdFRvb2x0aXBJbnRlcmFjdGlvbnModHJ1ZSk7XG5cdFx0XHR0aGlzLmNsb3NlVG9vbHRpcCgpO1xuXHRcdFx0dGhpcy5fdG9vbHRpcCA9IG51bGw7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdF9pbml0VG9vbHRpcEludGVyYWN0aW9uczogZnVuY3Rpb24gKHJlbW92ZSkge1xuXHRcdGlmICghcmVtb3ZlICYmIHRoaXMuX3Rvb2x0aXBIYW5kbGVyc0FkZGVkKSB7IHJldHVybjsgfVxuXHRcdHZhciBvbk9mZiA9IHJlbW92ZSA/ICdvZmYnIDogJ29uJyxcblx0XHQgICAgZXZlbnRzID0ge1xuXHRcdFx0cmVtb3ZlOiB0aGlzLmNsb3NlVG9vbHRpcCxcblx0XHRcdG1vdmU6IHRoaXMuX21vdmVUb29sdGlwXG5cdFx0ICAgIH07XG5cdFx0aWYgKCF0aGlzLl90b29sdGlwLm9wdGlvbnMucGVybWFuZW50KSB7XG5cdFx0XHRldmVudHMubW91c2VvdmVyID0gdGhpcy5fb3BlblRvb2x0aXA7XG5cdFx0XHRldmVudHMubW91c2VvdXQgPSB0aGlzLmNsb3NlVG9vbHRpcDtcblx0XHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMuc3RpY2t5KSB7XG5cdFx0XHRcdGV2ZW50cy5tb3VzZW1vdmUgPSB0aGlzLl9tb3ZlVG9vbHRpcDtcblx0XHRcdH1cblx0XHRcdGlmIChMLkJyb3dzZXIudG91Y2gpIHtcblx0XHRcdFx0ZXZlbnRzLmNsaWNrID0gdGhpcy5fb3BlblRvb2x0aXA7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdGV2ZW50cy5hZGQgPSB0aGlzLl9vcGVuVG9vbHRpcDtcblx0XHR9XG5cdFx0dGhpc1tvbk9mZl0oZXZlbnRzKTtcblx0XHR0aGlzLl90b29sdGlwSGFuZGxlcnNBZGRlZCA9ICFyZW1vdmU7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBvcGVuVG9vbHRpcChsYXRsbmc/OiBMYXRMbmcpOiB0aGlzXG5cdC8vIE9wZW5zIHRoZSBib3VuZCB0b29sdGlwIGF0IHRoZSBzcGVjaWZpY2VkIGBsYXRsbmdgIG9yIGF0IHRoZSBkZWZhdWx0IHRvb2x0aXAgYW5jaG9yIGlmIG5vIGBsYXRsbmdgIGlzIHBhc3NlZC5cblx0b3BlblRvb2x0aXA6IGZ1bmN0aW9uIChsYXllciwgbGF0bG5nKSB7XG5cdFx0aWYgKCEobGF5ZXIgaW5zdGFuY2VvZiBMLkxheWVyKSkge1xuXHRcdFx0bGF0bG5nID0gbGF5ZXI7XG5cdFx0XHRsYXllciA9IHRoaXM7XG5cdFx0fVxuXG5cdFx0aWYgKGxheWVyIGluc3RhbmNlb2YgTC5GZWF0dXJlR3JvdXApIHtcblx0XHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0XHRsYXllciA9IHRoaXMuX2xheWVyc1tpZF07XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICghbGF0bG5nKSB7XG5cdFx0XHRsYXRsbmcgPSBsYXllci5nZXRDZW50ZXIgPyBsYXllci5nZXRDZW50ZXIoKSA6IGxheWVyLmdldExhdExuZygpO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLl90b29sdGlwICYmIHRoaXMuX21hcCkge1xuXG5cdFx0XHQvLyBzZXQgdG9vbHRpcCBzb3VyY2UgdG8gdGhpcyBsYXllclxuXHRcdFx0dGhpcy5fdG9vbHRpcC5fc291cmNlID0gbGF5ZXI7XG5cblx0XHRcdC8vIHVwZGF0ZSB0aGUgdG9vbHRpcCAoY29udGVudCwgbGF5b3V0LCBlY3QuLi4pXG5cdFx0XHR0aGlzLl90b29sdGlwLnVwZGF0ZSgpO1xuXG5cdFx0XHQvLyBvcGVuIHRoZSB0b29sdGlwIG9uIHRoZSBtYXBcblx0XHRcdHRoaXMuX21hcC5vcGVuVG9vbHRpcCh0aGlzLl90b29sdGlwLCBsYXRsbmcpO1xuXG5cdFx0XHQvLyBUb29sdGlwIGNvbnRhaW5lciBtYXkgbm90IGJlIGRlZmluZWQgaWYgbm90IHBlcm1hbmVudCBhbmQgbmV2ZXJcblx0XHRcdC8vIG9wZW5lZC5cblx0XHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMuaW50ZXJhY3RpdmUgJiYgdGhpcy5fdG9vbHRpcC5fY29udGFpbmVyKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl90b29sdGlwLl9jb250YWluZXIsICdsZWFmbGV0LWNsaWNrYWJsZScpO1xuXHRcdFx0XHR0aGlzLmFkZEludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcik7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBjbG9zZVRvb2x0aXAoKTogdGhpc1xuXHQvLyBDbG9zZXMgdGhlIHRvb2x0aXAgYm91bmQgdG8gdGhpcyBsYXllciBpZiBpdCBpcyBvcGVuLlxuXHRjbG9zZVRvb2x0aXA6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fdG9vbHRpcCkge1xuXHRcdFx0dGhpcy5fdG9vbHRpcC5fY2xvc2UoKTtcblx0XHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMuaW50ZXJhY3RpdmUgJiYgdGhpcy5fdG9vbHRpcC5fY29udGFpbmVyKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl90b29sdGlwLl9jb250YWluZXIsICdsZWFmbGV0LWNsaWNrYWJsZScpO1xuXHRcdFx0XHR0aGlzLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcik7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgdG9nZ2xlVG9vbHRpcCgpOiB0aGlzXG5cdC8vIE9wZW5zIG9yIGNsb3NlcyB0aGUgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyIGRlcGVuZGluZyBvbiBpdHMgY3VycmVudCBzdGF0ZS5cblx0dG9nZ2xlVG9vbHRpcDogZnVuY3Rpb24gKHRhcmdldCkge1xuXHRcdGlmICh0aGlzLl90b29sdGlwKSB7XG5cdFx0XHRpZiAodGhpcy5fdG9vbHRpcC5fbWFwKSB7XG5cdFx0XHRcdHRoaXMuY2xvc2VUb29sdGlwKCk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0aGlzLm9wZW5Ub29sdGlwKHRhcmdldCk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgaXNUb29sdGlwT3BlbigpOiBib29sZWFuXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgY3VycmVudGx5IG9wZW4uXG5cdGlzVG9vbHRpcE9wZW46IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fdG9vbHRpcC5pc09wZW4oKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldFRvb2x0aXBDb250ZW50KGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxUb29sdGlwKTogdGhpc1xuXHQvLyBTZXRzIHRoZSBjb250ZW50IG9mIHRoZSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIuXG5cdHNldFRvb2x0aXBDb250ZW50OiBmdW5jdGlvbiAoY29udGVudCkge1xuXHRcdGlmICh0aGlzLl90b29sdGlwKSB7XG5cdFx0XHR0aGlzLl90b29sdGlwLnNldENvbnRlbnQoY29udGVudCk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0VG9vbHRpcCgpOiBUb29sdGlwXG5cdC8vIFJldHVybnMgdGhlIHRvb2x0aXAgYm91bmQgdG8gdGhpcyBsYXllci5cblx0Z2V0VG9vbHRpcDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl90b29sdGlwO1xuXHR9LFxuXG5cdF9vcGVuVG9vbHRpcDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbGF5ZXIgPSBlLmxheWVyIHx8IGUudGFyZ2V0O1xuXG5cdFx0aWYgKCF0aGlzLl90b29sdGlwIHx8ICF0aGlzLl9tYXApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0dGhpcy5vcGVuVG9vbHRpcChsYXllciwgdGhpcy5fdG9vbHRpcC5vcHRpb25zLnN0aWNreSA/IGUubGF0bG5nIDogdW5kZWZpbmVkKTtcblx0fSxcblxuXHRfbW92ZVRvb2x0aXA6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGxhdGxuZyA9IGUubGF0bG5nLCBjb250YWluZXJQb2ludCwgbGF5ZXJQb2ludDtcblx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLnN0aWNreSAmJiBlLm9yaWdpbmFsRXZlbnQpIHtcblx0XHRcdGNvbnRhaW5lclBvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUub3JpZ2luYWxFdmVudCk7XG5cdFx0XHRsYXllclBvaW50ID0gdGhpcy5fbWFwLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KGNvbnRhaW5lclBvaW50KTtcblx0XHRcdGxhdGxuZyA9IHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcobGF5ZXJQb2ludCk7XG5cdFx0fVxuXHRcdHRoaXMuX3Rvb2x0aXAuc2V0TGF0TG5nKGxhdGxuZyk7XG5cdH1cbn0pO1xuXG5cblxuLypcclxuICogQGNsYXNzIExheWVyR3JvdXBcclxuICogQGFrYSBMLkxheWVyR3JvdXBcclxuICogQGluaGVyaXRzIExheWVyXHJcbiAqXHJcbiAqIFVzZWQgdG8gZ3JvdXAgc2V2ZXJhbCBsYXllcnMgYW5kIGhhbmRsZSB0aGVtIGFzIG9uZS4gSWYgeW91IGFkZCBpdCB0byB0aGUgbWFwLFxyXG4gKiBhbnkgbGF5ZXJzIGFkZGVkIG9yIHJlbW92ZWQgZnJvbSB0aGUgZ3JvdXAgd2lsbCBiZSBhZGRlZC9yZW1vdmVkIG9uIHRoZSBtYXAgYXNcclxuICogd2VsbC4gRXh0ZW5kcyBgTGF5ZXJgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLmxheWVyR3JvdXAoW21hcmtlcjEsIG1hcmtlcjJdKVxyXG4gKiBcdC5hZGRMYXllcihwb2x5bGluZSlcclxuICogXHQuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5MYXllckdyb3VwID0gTC5MYXllci5leHRlbmQoe1xyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobGF5ZXJzKSB7XHJcblx0XHR0aGlzLl9sYXllcnMgPSB7fTtcclxuXHJcblx0XHR2YXIgaSwgbGVuO1xyXG5cclxuXHRcdGlmIChsYXllcnMpIHtcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gbGF5ZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dGhpcy5hZGRMYXllcihsYXllcnNbaV0pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXHJcblx0Ly8gQWRkcyB0aGUgZ2l2ZW4gbGF5ZXIgdG8gdGhlIGdyb3VwLlxyXG5cdGFkZExheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdHZhciBpZCA9IHRoaXMuZ2V0TGF5ZXJJZChsYXllcik7XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJzW2lkXSA9IGxheWVyO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fbWFwLmFkZExheWVyKGxheWVyKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZUxheWVyKGxheWVyOiBMYXllcik6IHRoaXNcclxuXHQvLyBSZW1vdmVzIHRoZSBnaXZlbiBsYXllciBmcm9tIHRoZSBncm91cC5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIHJlbW92ZUxheWVyKGlkOiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyB0aGUgbGF5ZXIgd2l0aCB0aGUgZ2l2ZW4gaW50ZXJuYWwgSUQgZnJvbSB0aGUgZ3JvdXAuXHJcblx0cmVtb3ZlTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0dmFyIGlkID0gbGF5ZXIgaW4gdGhpcy5fbGF5ZXJzID8gbGF5ZXIgOiB0aGlzLmdldExheWVySWQobGF5ZXIpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXAgJiYgdGhpcy5fbGF5ZXJzW2lkXSkge1xyXG5cdFx0XHR0aGlzLl9tYXAucmVtb3ZlTGF5ZXIodGhpcy5fbGF5ZXJzW2lkXSk7XHJcblx0XHR9XHJcblxyXG5cdFx0ZGVsZXRlIHRoaXMuX2xheWVyc1tpZF07XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBoYXNMYXllcihsYXllcjogTGF5ZXIpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGdpdmVuIGxheWVyIGlzIGN1cnJlbnRseSBhZGRlZCB0byB0aGUgZ3JvdXAuXHJcblx0aGFzTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0cmV0dXJuICEhbGF5ZXIgJiYgKGxheWVyIGluIHRoaXMuX2xheWVycyB8fCB0aGlzLmdldExheWVySWQobGF5ZXIpIGluIHRoaXMuX2xheWVycyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjbGVhckxheWVycygpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyBhbGwgdGhlIGxheWVycyBmcm9tIHRoZSBncm91cC5cclxuXHRjbGVhckxheWVyczogZnVuY3Rpb24gKCkge1xyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0dGhpcy5yZW1vdmVMYXllcih0aGlzLl9sYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpbnZva2UobWV0aG9kTmFtZTogU3RyaW5nLCDigKYpOiB0aGlzXHJcblx0Ly8gQ2FsbHMgYG1ldGhvZE5hbWVgIG9uIGV2ZXJ5IGxheWVyIGNvbnRhaW5lZCBpbiB0aGlzIGdyb3VwLCBwYXNzaW5nIGFueVxyXG5cdC8vIGFkZGl0aW9uYWwgcGFyYW1ldGVycy4gSGFzIG5vIGVmZmVjdCBpZiB0aGUgbGF5ZXJzIGNvbnRhaW5lZCBkbyBub3RcclxuXHQvLyBpbXBsZW1lbnQgYG1ldGhvZE5hbWVgLlxyXG5cdGludm9rZTogZnVuY3Rpb24gKG1ldGhvZE5hbWUpIHtcclxuXHRcdHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSxcclxuXHRcdCAgICBpLCBsYXllcjtcclxuXHJcblx0XHRmb3IgKGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdGxheWVyID0gdGhpcy5fbGF5ZXJzW2ldO1xyXG5cclxuXHRcdFx0aWYgKGxheWVyW21ldGhvZE5hbWVdKSB7XHJcblx0XHRcdFx0bGF5ZXJbbWV0aG9kTmFtZV0uYXBwbHkobGF5ZXIsIGFyZ3MpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdG1hcC5hZGRMYXllcih0aGlzLl9sYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRtYXAucmVtb3ZlTGF5ZXIodGhpcy5fbGF5ZXJzW2ldKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVhY2hMYXllcihmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gSXRlcmF0ZXMgb3ZlciB0aGUgbGF5ZXJzIG9mIHRoZSBncm91cCwgb3B0aW9uYWxseSBzcGVjaWZ5aW5nIGNvbnRleHQgb2YgdGhlIGl0ZXJhdG9yIGZ1bmN0aW9uLlxyXG5cdC8vIGBgYGpzXHJcblx0Ly8gZ3JvdXAuZWFjaExheWVyKGZ1bmN0aW9uIChsYXllcikge1xyXG5cdC8vIFx0bGF5ZXIuYmluZFBvcHVwKCdIZWxsbycpO1xyXG5cdC8vIH0pO1xyXG5cdC8vIGBgYFxyXG5cdGVhY2hMYXllcjogZnVuY3Rpb24gKG1ldGhvZCwgY29udGV4dCkge1xyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0bWV0aG9kLmNhbGwoY29udGV4dCwgdGhpcy5fbGF5ZXJzW2ldKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TGF5ZXIoaWQ6IE51bWJlcik6IExheWVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbGF5ZXIgd2l0aCB0aGUgZ2l2ZW4gaW50ZXJuYWwgSUQuXHJcblx0Z2V0TGF5ZXI6IGZ1bmN0aW9uIChpZCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2xheWVyc1tpZF07XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRMYXllcnMoKTogTGF5ZXJbXVxyXG5cdC8vIFJldHVybnMgYW4gYXJyYXkgb2YgYWxsIHRoZSBsYXllcnMgYWRkZWQgdG8gdGhlIGdyb3VwLlxyXG5cdGdldExheWVyczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGxheWVycyA9IFtdO1xyXG5cclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdGxheWVycy5wdXNoKHRoaXMuX2xheWVyc1tpXSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gbGF5ZXJzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0WkluZGV4KHpJbmRleDogTnVtYmVyKTogdGhpc1xyXG5cdC8vIENhbGxzIGBzZXRaSW5kZXhgIG9uIGV2ZXJ5IGxheWVyIGNvbnRhaW5lZCBpbiB0aGlzIGdyb3VwLCBwYXNzaW5nIHRoZSB6LWluZGV4LlxyXG5cdHNldFpJbmRleDogZnVuY3Rpb24gKHpJbmRleCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdzZXRaSW5kZXgnLCB6SW5kZXgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TGF5ZXJJZChsYXllcjogTGF5ZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBpbnRlcm5hbCBJRCBmb3IgYSBsYXllclxyXG5cdGdldExheWVySWQ6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0cmV0dXJuIEwuc3RhbXAobGF5ZXIpO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5sYXllckdyb3VwKGxheWVyczogTGF5ZXJbXSlcclxuLy8gQ3JlYXRlIGEgbGF5ZXIgZ3JvdXAsIG9wdGlvbmFsbHkgZ2l2ZW4gYW4gaW5pdGlhbCBzZXQgb2YgbGF5ZXJzLlxyXG5MLmxheWVyR3JvdXAgPSBmdW5jdGlvbiAobGF5ZXJzKSB7XHJcblx0cmV0dXJuIG5ldyBMLkxheWVyR3JvdXAobGF5ZXJzKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIEZlYXR1cmVHcm91cFxyXG4gKiBAYWthIEwuRmVhdHVyZUdyb3VwXHJcbiAqIEBpbmhlcml0cyBMYXllckdyb3VwXHJcbiAqXHJcbiAqIEV4dGVuZGVkIGBMYXllckdyb3VwYCB0aGF0IG1ha2VzIGl0IGVhc2llciB0byBkbyB0aGUgc2FtZSB0aGluZyB0byBhbGwgaXRzIG1lbWJlciBsYXllcnM6XHJcbiAqICAqIFtgYmluZFBvcHVwYF0oI2xheWVyLWJpbmRwb3B1cCkgYmluZHMgYSBwb3B1cCB0byBhbGwgb2YgdGhlIGxheWVycyBhdCBvbmNlIChsaWtld2lzZSB3aXRoIFtgYmluZFRvb2x0aXBgXSgjbGF5ZXItYmluZHRvb2x0aXApKVxyXG4gKiAgKiBFdmVudHMgYXJlIHByb3BhZ2F0ZWQgdG8gdGhlIGBGZWF0dXJlR3JvdXBgLCBzbyBpZiB0aGUgZ3JvdXAgaGFzIGFuIGV2ZW50XHJcbiAqIGhhbmRsZXIsIGl0IHdpbGwgaGFuZGxlIGV2ZW50cyBmcm9tIGFueSBvZiB0aGUgbGF5ZXJzLiBUaGlzIGluY2x1ZGVzIG1vdXNlIGV2ZW50c1xyXG4gKiBhbmQgY3VzdG9tIGV2ZW50cy5cclxuICogICogSGFzIGBsYXllcmFkZGAgYW5kIGBsYXllcnJlbW92ZWAgZXZlbnRzXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIEwuZmVhdHVyZUdyb3VwKFttYXJrZXIxLCBtYXJrZXIyLCBwb2x5bGluZV0pXHJcbiAqIFx0LmJpbmRQb3B1cCgnSGVsbG8gd29ybGQhJylcclxuICogXHQub24oJ2NsaWNrJywgZnVuY3Rpb24oKSB7IGFsZXJ0KCdDbGlja2VkIG9uIGEgbWVtYmVyIG9mIHRoZSBncm91cCEnKTsgfSlcclxuICogXHQuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5GZWF0dXJlR3JvdXAgPSBMLkxheWVyR3JvdXAuZXh0ZW5kKHtcclxuXHJcblx0YWRkTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0aWYgKHRoaXMuaGFzTGF5ZXIobGF5ZXIpKSB7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGxheWVyLmFkZEV2ZW50UGFyZW50KHRoaXMpO1xyXG5cclxuXHRcdEwuTGF5ZXJHcm91cC5wcm90b3R5cGUuYWRkTGF5ZXIuY2FsbCh0aGlzLCBsYXllcik7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxheWVyYWRkOiBMYXllckV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgbGF5ZXIgaXMgYWRkZWQgdG8gdGhpcyBgRmVhdHVyZUdyb3VwYFxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbGF5ZXJhZGQnLCB7bGF5ZXI6IGxheWVyfSk7XHJcblx0fSxcclxuXHJcblx0cmVtb3ZlTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0aWYgKCF0aGlzLmhhc0xheWVyKGxheWVyKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHRcdGlmIChsYXllciBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0bGF5ZXIgPSB0aGlzLl9sYXllcnNbbGF5ZXJdO1xyXG5cdFx0fVxyXG5cclxuXHRcdGxheWVyLnJlbW92ZUV2ZW50UGFyZW50KHRoaXMpO1xyXG5cclxuXHRcdEwuTGF5ZXJHcm91cC5wcm90b3R5cGUucmVtb3ZlTGF5ZXIuY2FsbCh0aGlzLCBsYXllcik7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxheWVycmVtb3ZlOiBMYXllckV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgbGF5ZXIgaXMgcmVtb3ZlZCBmcm9tIHRoaXMgYEZlYXR1cmVHcm91cGBcclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ2xheWVycmVtb3ZlJywge2xheWVyOiBsYXllcn0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0U3R5bGUoc3R5bGU6IFBhdGggb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBnaXZlbiBwYXRoIG9wdGlvbnMgdG8gZWFjaCBsYXllciBvZiB0aGUgZ3JvdXAgdGhhdCBoYXMgYSBgc2V0U3R5bGVgIG1ldGhvZC5cclxuXHRzZXRTdHlsZTogZnVuY3Rpb24gKHN0eWxlKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5pbnZva2UoJ3NldFN0eWxlJywgc3R5bGUpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250KCk6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhlIGxheWVyIGdyb3VwIHRvIHRoZSB0b3Agb2YgYWxsIG90aGVyIGxheWVyc1xyXG5cdGJyaW5nVG9Gcm9udDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdicmluZ1RvRnJvbnQnKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGJyaW5nVG9CYWNrKCk6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhlIGxheWVyIGdyb3VwIHRvIHRoZSB0b3Agb2YgYWxsIG90aGVyIGxheWVyc1xyXG5cdGJyaW5nVG9CYWNrOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5pbnZva2UoJ2JyaW5nVG9CYWNrJyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHMoKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgTGF0TG5nQm91bmRzIG9mIHRoZSBGZWF0dXJlIEdyb3VwIChjcmVhdGVkIGZyb20gYm91bmRzIGFuZCBjb29yZGluYXRlcyBvZiBpdHMgY2hpbGRyZW4pLlxyXG5cdGdldEJvdW5kczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGJvdW5kcyA9IG5ldyBMLkxhdExuZ0JvdW5kcygpO1xyXG5cclxuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHR2YXIgbGF5ZXIgPSB0aGlzLl9sYXllcnNbaWRdO1xyXG5cdFx0XHRib3VuZHMuZXh0ZW5kKGxheWVyLmdldEJvdW5kcyA/IGxheWVyLmdldEJvdW5kcygpIDogbGF5ZXIuZ2V0TGF0TG5nKCkpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGJvdW5kcztcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQGZhY3RvcnkgTC5mZWF0dXJlR3JvdXAobGF5ZXJzOiBMYXllcltdKVxyXG4vLyBDcmVhdGUgYSBmZWF0dXJlIGdyb3VwLCBvcHRpb25hbGx5IGdpdmVuIGFuIGluaXRpYWwgc2V0IG9mIGxheWVycy5cclxuTC5mZWF0dXJlR3JvdXAgPSBmdW5jdGlvbiAobGF5ZXJzKSB7XHJcblx0cmV0dXJuIG5ldyBMLkZlYXR1cmVHcm91cChsYXllcnMpO1xyXG59O1xyXG5cblxuXG4vKlxuICogQGNsYXNzIFJlbmRlcmVyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcbiAqIEBha2EgTC5SZW5kZXJlclxuICpcbiAqIEJhc2UgY2xhc3MgZm9yIHZlY3RvciByZW5kZXJlciBpbXBsZW1lbnRhdGlvbnMgKGBTVkdgLCBgQ2FudmFzYCkuIEhhbmRsZXMgdGhlXG4gKiBET00gY29udGFpbmVyIG9mIHRoZSByZW5kZXJlciwgaXRzIGJvdW5kcywgYW5kIGl0cyB6b29tIGFuaW1hdGlvbi5cbiAqXG4gKiBBIGBSZW5kZXJlcmAgd29ya3MgYXMgYW4gaW1wbGljaXQgbGF5ZXIgZ3JvdXAgZm9yIGFsbCBgUGF0aGBzIC0gdGhlIHJlbmRlcmVyXG4gKiBpdHNlbGYgY2FuIGJlIGFkZGVkIG9yIHJlbW92ZWQgdG8gdGhlIG1hcC4gQWxsIHBhdGhzIHVzZSBhIHJlbmRlcmVyLCB3aGljaCBjYW5cbiAqIGJlIGltcGxpY2l0ICh0aGUgbWFwIHdpbGwgZGVjaWRlIHRoZSB0eXBlIG9mIHJlbmRlcmVyIGFuZCB1c2UgaXQgYXV0b21hdGljYWxseSlcbiAqIG9yIGV4cGxpY2l0ICh1c2luZyB0aGUgW2ByZW5kZXJlcmBdKCNwYXRoLXJlbmRlcmVyKSBvcHRpb24gb2YgdGhlIHBhdGgpLlxuICpcbiAqIERvIG5vdCB1c2UgdGhpcyBjbGFzcyBkaXJlY3RseSwgdXNlIGBTVkdgIGFuZCBgQ2FudmFzYCBpbnN0ZWFkLlxuICpcbiAqIEBldmVudCB1cGRhdGU6IEV2ZW50XG4gKiBGaXJlZCB3aGVuIHRoZSByZW5kZXJlciB1cGRhdGVzIGl0cyBib3VuZHMsIGNlbnRlciBhbmQgem9vbSwgZm9yIGV4YW1wbGUgd2hlblxuICogaXRzIG1hcCBoYXMgbW92ZWRcbiAqL1xuXG5MLlJlbmRlcmVyID0gTC5MYXllci5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgUmVuZGVyZXIgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBwYWRkaW5nOiBOdW1iZXIgPSAwLjFcblx0XHQvLyBIb3cgbXVjaCB0byBleHRlbmQgdGhlIGNsaXAgYXJlYSBhcm91bmQgdGhlIG1hcCB2aWV3IChyZWxhdGl2ZSB0byBpdHMgc2l6ZSlcblx0XHQvLyBlLmcuIDAuMSB3b3VsZCBiZSAxMCUgb2YgbWFwIHZpZXcgaW4gZWFjaCBkaXJlY3Rpb25cblx0XHRwYWRkaW5nOiAwLjFcblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0XHRMLnN0YW1wKHRoaXMpO1xuXHRcdHRoaXMuX2xheWVycyA9IHRoaXMuX2xheWVycyB8fCB7fTtcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fY29udGFpbmVyKSB7XG5cdFx0XHR0aGlzLl9pbml0Q29udGFpbmVyKCk7IC8vIGRlZmluZWQgYnkgcmVuZGVyZXIgaW1wbGVtZW50YXRpb25zXG5cblx0XHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcblx0XHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0dGhpcy5fdXBkYXRlKCk7XG5cdFx0dGhpcy5vbigndXBkYXRlJywgdGhpcy5fdXBkYXRlUGF0aHMsIHRoaXMpO1xuXHR9LFxuXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xuXHRcdHRoaXMub2ZmKCd1cGRhdGUnLCB0aGlzLl91cGRhdGVQYXRocywgdGhpcyk7XG5cdH0sXG5cblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGV2ZW50cyA9IHtcblx0XHRcdHZpZXdyZXNldDogdGhpcy5fcmVzZXQsXG5cdFx0XHR6b29tOiB0aGlzLl9vblpvb20sXG5cdFx0XHRtb3ZlZW5kOiB0aGlzLl91cGRhdGUsXG5cdFx0XHR6b29tZW5kOiB0aGlzLl9vblpvb21FbmRcblx0XHR9O1xuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcblx0XHRcdGV2ZW50cy56b29tYW5pbSA9IHRoaXMuX29uQW5pbVpvb207XG5cdFx0fVxuXHRcdHJldHVybiBldmVudHM7XG5cdH0sXG5cblx0X29uQW5pbVpvb206IGZ1bmN0aW9uIChldikge1xuXHRcdHRoaXMuX3VwZGF0ZVRyYW5zZm9ybShldi5jZW50ZXIsIGV2Lnpvb20pO1xuXHR9LFxuXG5cdF9vblpvb206IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl91cGRhdGVUcmFuc2Zvcm0odGhpcy5fbWFwLmdldENlbnRlcigpLCB0aGlzLl9tYXAuZ2V0Wm9vbSgpKTtcblx0fSxcblxuXHRfdXBkYXRlVHJhbnNmb3JtOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XG5cdFx0dmFyIHNjYWxlID0gdGhpcy5fbWFwLmdldFpvb21TY2FsZSh6b29tLCB0aGlzLl96b29tKSxcblx0XHQgICAgcG9zaXRpb24gPSBMLkRvbVV0aWwuZ2V0UG9zaXRpb24odGhpcy5fY29udGFpbmVyKSxcblx0XHQgICAgdmlld0hhbGYgPSB0aGlzLl9tYXAuZ2V0U2l6ZSgpLm11bHRpcGx5QnkoMC41ICsgdGhpcy5vcHRpb25zLnBhZGRpbmcpLFxuXHRcdCAgICBjdXJyZW50Q2VudGVyUG9pbnQgPSB0aGlzLl9tYXAucHJvamVjdCh0aGlzLl9jZW50ZXIsIHpvb20pLFxuXHRcdCAgICBkZXN0Q2VudGVyUG9pbnQgPSB0aGlzLl9tYXAucHJvamVjdChjZW50ZXIsIHpvb20pLFxuXHRcdCAgICBjZW50ZXJPZmZzZXQgPSBkZXN0Q2VudGVyUG9pbnQuc3VidHJhY3QoY3VycmVudENlbnRlclBvaW50KSxcblxuXHRcdCAgICB0b3BMZWZ0T2Zmc2V0ID0gdmlld0hhbGYubXVsdGlwbHlCeSgtc2NhbGUpLmFkZChwb3NpdGlvbikuYWRkKHZpZXdIYWxmKS5zdWJ0cmFjdChjZW50ZXJPZmZzZXQpO1xuXG5cdFx0aWYgKEwuQnJvd3Nlci5hbnkzZCkge1xuXHRcdFx0TC5Eb21VdGlsLnNldFRyYW5zZm9ybSh0aGlzLl9jb250YWluZXIsIHRvcExlZnRPZmZzZXQsIHNjYWxlKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lciwgdG9wTGVmdE9mZnNldCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXNldDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHRcdHRoaXMuX3VwZGF0ZVRyYW5zZm9ybSh0aGlzLl9jZW50ZXIsIHRoaXMuX3pvb20pO1xuXG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHR0aGlzLl9sYXllcnNbaWRdLl9yZXNldCgpO1xuXHRcdH1cblx0fSxcblxuXHRfb25ab29tRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHR0aGlzLl9sYXllcnNbaWRdLl9wcm9qZWN0KCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVQYXRoczogZnVuY3Rpb24gKCkge1xuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0dGhpcy5fbGF5ZXJzW2lkXS5fdXBkYXRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBVcGRhdGUgcGl4ZWwgYm91bmRzIG9mIHJlbmRlcmVyIGNvbnRhaW5lciAoZm9yIHBvc2l0aW9uaW5nL3NpemluZy9jbGlwcGluZyBsYXRlcilcblx0XHQvLyBTdWJjbGFzc2VzIGFyZSByZXNwb25zaWJsZSBvZiBmaXJpbmcgdGhlICd1cGRhdGUnIGV2ZW50LlxuXHRcdHZhciBwID0gdGhpcy5vcHRpb25zLnBhZGRpbmcsXG5cdFx0ICAgIHNpemUgPSB0aGlzLl9tYXAuZ2V0U2l6ZSgpLFxuXHRcdCAgICBtaW4gPSB0aGlzLl9tYXAuY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQoc2l6ZS5tdWx0aXBseUJ5KC1wKSkucm91bmQoKTtcblxuXHRcdHRoaXMuX2JvdW5kcyA9IG5ldyBMLkJvdW5kcyhtaW4sIG1pbi5hZGQoc2l6ZS5tdWx0aXBseUJ5KDEgKyBwICogMikpLnJvdW5kKCkpO1xuXG5cdFx0dGhpcy5fY2VudGVyID0gdGhpcy5fbWFwLmdldENlbnRlcigpO1xuXHRcdHRoaXMuX3pvb20gPSB0aGlzLl9tYXAuZ2V0Wm9vbSgpO1xuXHR9XG59KTtcblxuXG5MLk1hcC5pbmNsdWRlKHtcblx0Ly8gQG5hbWVzcGFjZSBNYXA7IEBtZXRob2QgZ2V0UmVuZGVyZXIobGF5ZXI6IFBhdGgpOiBSZW5kZXJlclxuXHQvLyBSZXR1cm5zIHRoZSBpbnN0YW5jZSBvZiBgUmVuZGVyZXJgIHRoYXQgc2hvdWxkIGJlIHVzZWQgdG8gcmVuZGVyIHRoZSBnaXZlblxuXHQvLyBgUGF0aGAuIEl0IHdpbGwgZW5zdXJlIHRoYXQgdGhlIGByZW5kZXJlcmAgb3B0aW9ucyBvZiB0aGUgbWFwIGFuZCBwYXRoc1xuXHQvLyBhcmUgcmVzcGVjdGVkLCBhbmQgdGhhdCB0aGUgcmVuZGVyZXJzIGRvIGV4aXN0IG9uIHRoZSBtYXAuXG5cdGdldFJlbmRlcmVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHQvLyBAbmFtZXNwYWNlIFBhdGg7IEBvcHRpb24gcmVuZGVyZXI6IFJlbmRlcmVyXG5cdFx0Ly8gVXNlIHRoaXMgc3BlY2lmaWMgaW5zdGFuY2Ugb2YgYFJlbmRlcmVyYCBmb3IgdGhpcyBwYXRoLiBUYWtlc1xuXHRcdC8vIHByZWNlZGVuY2Ugb3ZlciB0aGUgbWFwJ3MgW2RlZmF1bHQgcmVuZGVyZXJdKCNtYXAtcmVuZGVyZXIpLlxuXHRcdHZhciByZW5kZXJlciA9IGxheWVyLm9wdGlvbnMucmVuZGVyZXIgfHwgdGhpcy5fZ2V0UGFuZVJlbmRlcmVyKGxheWVyLm9wdGlvbnMucGFuZSkgfHwgdGhpcy5vcHRpb25zLnJlbmRlcmVyIHx8IHRoaXMuX3JlbmRlcmVyO1xuXG5cdFx0aWYgKCFyZW5kZXJlcikge1xuXHRcdFx0Ly8gQG5hbWVzcGFjZSBNYXA7IEBvcHRpb24gcHJlZmVyQ2FudmFzOiBCb29sZWFuID0gZmFsc2Vcblx0XHRcdC8vIFdoZXRoZXIgYFBhdGhgcyBzaG91bGQgYmUgcmVuZGVyZWQgb24gYSBgQ2FudmFzYCByZW5kZXJlci5cblx0XHRcdC8vIEJ5IGRlZmF1bHQsIGFsbCBgUGF0aGBzIGFyZSByZW5kZXJlZCBpbiBhIGBTVkdgIHJlbmRlcmVyLlxuXHRcdFx0cmVuZGVyZXIgPSB0aGlzLl9yZW5kZXJlciA9ICh0aGlzLm9wdGlvbnMucHJlZmVyQ2FudmFzICYmIEwuY2FudmFzKCkpIHx8IEwuc3ZnKCk7XG5cdFx0fVxuXG5cdFx0aWYgKCF0aGlzLmhhc0xheWVyKHJlbmRlcmVyKSkge1xuXHRcdFx0dGhpcy5hZGRMYXllcihyZW5kZXJlcik7XG5cdFx0fVxuXHRcdHJldHVybiByZW5kZXJlcjtcblx0fSxcblxuXHRfZ2V0UGFuZVJlbmRlcmVyOiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdGlmIChuYW1lID09PSAnb3ZlcmxheVBhbmUnIHx8IG5hbWUgPT09IHVuZGVmaW5lZCkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdHZhciByZW5kZXJlciA9IHRoaXMuX3BhbmVSZW5kZXJlcnNbbmFtZV07XG5cdFx0aWYgKHJlbmRlcmVyID09PSB1bmRlZmluZWQpIHtcblx0XHRcdHJlbmRlcmVyID0gKEwuU1ZHICYmIEwuc3ZnKHtwYW5lOiBuYW1lfSkpIHx8IChMLkNhbnZhcyAmJiBMLmNhbnZhcyh7cGFuZTogbmFtZX0pKTtcblx0XHRcdHRoaXMuX3BhbmVSZW5kZXJlcnNbbmFtZV0gPSByZW5kZXJlcjtcblx0XHR9XG5cdFx0cmV0dXJuIHJlbmRlcmVyO1xuXHR9XG59KTtcblxuXG5cbi8qXG4gKiBAY2xhc3MgUGF0aFxuICogQGFrYSBMLlBhdGhcbiAqIEBpbmhlcml0cyBJbnRlcmFjdGl2ZSBsYXllclxuICpcbiAqIEFuIGFic3RyYWN0IGNsYXNzIHRoYXQgY29udGFpbnMgb3B0aW9ucyBhbmQgY29uc3RhbnRzIHNoYXJlZCBiZXR3ZWVuIHZlY3RvclxuICogb3ZlcmxheXMgKFBvbHlnb24sIFBvbHlsaW5lLCBDaXJjbGUpLiBEbyBub3QgdXNlIGl0IGRpcmVjdGx5LiBFeHRlbmRzIGBMYXllcmAuXG4gKi9cblxuTC5QYXRoID0gTC5MYXllci5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgUGF0aCBvcHRpb25zXG5cdG9wdGlvbnM6IHtcblx0XHQvLyBAb3B0aW9uIHN0cm9rZTogQm9vbGVhbiA9IHRydWVcblx0XHQvLyBXaGV0aGVyIHRvIGRyYXcgc3Ryb2tlIGFsb25nIHRoZSBwYXRoLiBTZXQgaXQgdG8gYGZhbHNlYCB0byBkaXNhYmxlIGJvcmRlcnMgb24gcG9seWdvbnMgb3IgY2lyY2xlcy5cblx0XHRzdHJva2U6IHRydWUsXG5cblx0XHQvLyBAb3B0aW9uIGNvbG9yOiBTdHJpbmcgPSAnIzMzODhmZidcblx0XHQvLyBTdHJva2UgY29sb3Jcblx0XHRjb2xvcjogJyMzMzg4ZmYnLFxuXG5cdFx0Ly8gQG9wdGlvbiB3ZWlnaHQ6IE51bWJlciA9IDNcblx0XHQvLyBTdHJva2Ugd2lkdGggaW4gcGl4ZWxzXG5cdFx0d2VpZ2h0OiAzLFxuXG5cdFx0Ly8gQG9wdGlvbiBvcGFjaXR5OiBOdW1iZXIgPSAxLjBcblx0XHQvLyBTdHJva2Ugb3BhY2l0eVxuXHRcdG9wYWNpdHk6IDEsXG5cblx0XHQvLyBAb3B0aW9uIGxpbmVDYXA6IFN0cmluZz0gJ3JvdW5kJ1xuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyBbc2hhcGUgdG8gYmUgdXNlZCBhdCB0aGUgZW5kXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9TVkcvQXR0cmlidXRlL3N0cm9rZS1saW5lY2FwKSBvZiB0aGUgc3Ryb2tlLlxuXHRcdGxpbmVDYXA6ICdyb3VuZCcsXG5cblx0XHQvLyBAb3B0aW9uIGxpbmVKb2luOiBTdHJpbmcgPSAncm91bmQnXG5cdFx0Ly8gQSBzdHJpbmcgdGhhdCBkZWZpbmVzIFtzaGFwZSB0byBiZSB1c2VkIGF0IHRoZSBjb3JuZXJzXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9TVkcvQXR0cmlidXRlL3N0cm9rZS1saW5lam9pbikgb2YgdGhlIHN0cm9rZS5cblx0XHRsaW5lSm9pbjogJ3JvdW5kJyxcblxuXHRcdC8vIEBvcHRpb24gZGFzaEFycmF5OiBTdHJpbmcgPSBudWxsXG5cdFx0Ly8gQSBzdHJpbmcgdGhhdCBkZWZpbmVzIHRoZSBzdHJva2UgW2Rhc2ggcGF0dGVybl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtZGFzaGFycmF5KS4gRG9lc24ndCB3b3JrIG9uIGBDYW52YXNgLXBvd2VyZWQgbGF5ZXJzIGluIFtzb21lIG9sZCBicm93c2Vyc10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0NhbnZhc1JlbmRlcmluZ0NvbnRleHQyRC9zZXRMaW5lRGFzaCNCcm93c2VyX2NvbXBhdGliaWxpdHkpLlxuXHRcdGRhc2hBcnJheTogbnVsbCxcblxuXHRcdC8vIEBvcHRpb24gZGFzaE9mZnNldDogU3RyaW5nID0gbnVsbFxuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyB0aGUgW2Rpc3RhbmNlIGludG8gdGhlIGRhc2ggcGF0dGVybiB0byBzdGFydCB0aGUgZGFzaF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtZGFzaG9mZnNldCkuIERvZXNuJ3Qgd29yayBvbiBgQ2FudmFzYC1wb3dlcmVkIGxheWVycyBpbiBbc29tZSBvbGQgYnJvd3NlcnNdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9DYW52YXNSZW5kZXJpbmdDb250ZXh0MkQvc2V0TGluZURhc2gjQnJvd3Nlcl9jb21wYXRpYmlsaXR5KS5cblx0XHRkYXNoT2Zmc2V0OiBudWxsLFxuXG5cdFx0Ly8gQG9wdGlvbiBmaWxsOiBCb29sZWFuID0gZGVwZW5kc1xuXHRcdC8vIFdoZXRoZXIgdG8gZmlsbCB0aGUgcGF0aCB3aXRoIGNvbG9yLiBTZXQgaXQgdG8gYGZhbHNlYCB0byBkaXNhYmxlIGZpbGxpbmcgb24gcG9seWdvbnMgb3IgY2lyY2xlcy5cblx0XHRmaWxsOiBmYWxzZSxcblxuXHRcdC8vIEBvcHRpb24gZmlsbENvbG9yOiBTdHJpbmcgPSAqXG5cdFx0Ly8gRmlsbCBjb2xvci4gRGVmYXVsdHMgdG8gdGhlIHZhbHVlIG9mIHRoZSBbYGNvbG9yYF0oI3BhdGgtY29sb3IpIG9wdGlvblxuXHRcdGZpbGxDb2xvcjogbnVsbCxcblxuXHRcdC8vIEBvcHRpb24gZmlsbE9wYWNpdHk6IE51bWJlciA9IDAuMlxuXHRcdC8vIEZpbGwgb3BhY2l0eS5cblx0XHRmaWxsT3BhY2l0eTogMC4yLFxuXG5cdFx0Ly8gQG9wdGlvbiBmaWxsUnVsZTogU3RyaW5nID0gJ2V2ZW5vZGQnXG5cdFx0Ly8gQSBzdHJpbmcgdGhhdCBkZWZpbmVzIFtob3cgdGhlIGluc2lkZSBvZiBhIHNoYXBlXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9TVkcvQXR0cmlidXRlL2ZpbGwtcnVsZSkgaXMgZGV0ZXJtaW5lZC5cblx0XHRmaWxsUnVsZTogJ2V2ZW5vZGQnLFxuXG5cdFx0Ly8gY2xhc3NOYW1lOiAnJyxcblxuXHRcdC8vIE9wdGlvbiBpbmhlcml0ZWQgZnJvbSBcIkludGVyYWN0aXZlIGxheWVyXCIgYWJzdHJhY3QgY2xhc3Ncblx0XHRpbnRlcmFjdGl2ZTogdHJ1ZVxuXHR9LFxuXG5cdGJlZm9yZUFkZDogZnVuY3Rpb24gKG1hcCkge1xuXHRcdC8vIFJlbmRlcmVyIGlzIHNldCBoZXJlIGJlY2F1c2Ugd2UgbmVlZCB0byBjYWxsIHJlbmRlcmVyLmdldEV2ZW50c1xuXHRcdC8vIGJlZm9yZSB0aGlzLmdldEV2ZW50cy5cblx0XHR0aGlzLl9yZW5kZXJlciA9IG1hcC5nZXRSZW5kZXJlcih0aGlzKTtcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbmRlcmVyLl9pbml0UGF0aCh0aGlzKTtcblx0XHR0aGlzLl9yZXNldCgpO1xuXHRcdHRoaXMuX3JlbmRlcmVyLl9hZGRQYXRoKHRoaXMpO1xuXHR9LFxuXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcmVuZGVyZXIuX3JlbW92ZVBhdGgodGhpcyk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCByZWRyYXcoKTogdGhpc1xuXHQvLyBSZWRyYXdzIHRoZSBsYXllci4gU29tZXRpbWVzIHVzZWZ1bCBhZnRlciB5b3UgY2hhbmdlZCB0aGUgY29vcmRpbmF0ZXMgdGhhdCB0aGUgcGF0aCB1c2VzLlxuXHRyZWRyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlUGF0aCh0aGlzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRTdHlsZShzdHlsZTogUGF0aCBvcHRpb25zKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBhcHBlYXJhbmNlIG9mIGEgUGF0aCBiYXNlZCBvbiB0aGUgb3B0aW9ucyBpbiB0aGUgYFBhdGggb3B0aW9uc2Agb2JqZWN0LlxuXHRzZXRTdHlsZTogZnVuY3Rpb24gKHN0eWxlKSB7XG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIHN0eWxlKTtcblx0XHRpZiAodGhpcy5fcmVuZGVyZXIpIHtcblx0XHRcdHRoaXMuX3JlbmRlcmVyLl91cGRhdGVTdHlsZSh0aGlzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBicmluZ1RvRnJvbnQoKTogdGhpc1xuXHQvLyBCcmluZ3MgdGhlIGxheWVyIHRvIHRoZSB0b3Agb2YgYWxsIHBhdGggbGF5ZXJzLlxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fcmVuZGVyZXIpIHtcblx0XHRcdHRoaXMuX3JlbmRlcmVyLl9icmluZ1RvRnJvbnQodGhpcyk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYnJpbmdUb0JhY2soKTogdGhpc1xuXHQvLyBCcmluZ3MgdGhlIGxheWVyIHRvIHRoZSBib3R0b20gb2YgYWxsIHBhdGggbGF5ZXJzLlxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9yZW5kZXJlcikge1xuXHRcdFx0dGhpcy5fcmVuZGVyZXIuX2JyaW5nVG9CYWNrKHRoaXMpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRnZXRFbGVtZW50OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3BhdGg7XG5cdH0sXG5cblx0X3Jlc2V0OiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gZGVmaW5lZCBpbiBjaGlsZHJlbiBjbGFzc2VzXG5cdFx0dGhpcy5fcHJvamVjdCgpO1xuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHR9LFxuXG5cdF9jbGlja1RvbGVyYW5jZTogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHVzZWQgd2hlbiBkb2luZyBoaXQgZGV0ZWN0aW9uIGZvciBDYW52YXMgbGF5ZXJzXG5cdFx0cmV0dXJuICh0aGlzLm9wdGlvbnMuc3Ryb2tlID8gdGhpcy5vcHRpb25zLndlaWdodCAvIDIgOiAwKSArIChMLkJyb3dzZXIudG91Y2ggPyAxMCA6IDApO1xuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgTGluZVV0aWxcclxuICpcclxuICogVmFyaW91cyB1dGlsaXR5IGZ1bmN0aW9ucyBmb3IgcG9seWluZSBwb2ludHMgcHJvY2Vzc2luZywgdXNlZCBieSBMZWFmbGV0IGludGVybmFsbHkgdG8gbWFrZSBwb2x5bGluZXMgbGlnaHRuaW5nLWZhc3QuXHJcbiAqL1xyXG5cclxuTC5MaW5lVXRpbCA9IHtcclxuXHJcblx0Ly8gU2ltcGxpZnkgcG9seWxpbmUgd2l0aCB2ZXJ0ZXggcmVkdWN0aW9uIGFuZCBEb3VnbGFzLVBldWNrZXIgc2ltcGxpZmljYXRpb24uXHJcblx0Ly8gSW1wcm92ZXMgcmVuZGVyaW5nIHBlcmZvcm1hbmNlIGRyYW1hdGljYWxseSBieSBsZXNzZW5pbmcgdGhlIG51bWJlciBvZiBwb2ludHMgdG8gZHJhdy5cclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNpbXBsaWZ5KHBvaW50czogUG9pbnRbXSwgdG9sZXJhbmNlOiBOdW1iZXIpOiBQb2ludFtdXHJcblx0Ly8gRHJhbWF0aWNhbGx5IHJlZHVjZXMgdGhlIG51bWJlciBvZiBwb2ludHMgaW4gYSBwb2x5bGluZSB3aGlsZSByZXRhaW5pbmdcclxuXHQvLyBpdHMgc2hhcGUgYW5kIHJldHVybnMgYSBuZXcgYXJyYXkgb2Ygc2ltcGxpZmllZCBwb2ludHMsIHVzaW5nIHRoZVxyXG5cdC8vIFtEb3VnbGFzLVBldWNrZXIgYWxnb3JpdGhtXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0RvdWdsYXMtUGV1Y2tlcl9hbGdvcml0aG0pLlxyXG5cdC8vIFVzZWQgZm9yIGEgaHVnZSBwZXJmb3JtYW5jZSBib29zdCB3aGVuIHByb2Nlc3NpbmcvZGlzcGxheWluZyBMZWFmbGV0IHBvbHlsaW5lcyBmb3JcclxuXHQvLyBlYWNoIHpvb20gbGV2ZWwgYW5kIGFsc28gcmVkdWNpbmcgdmlzdWFsIG5vaXNlLiB0b2xlcmFuY2UgYWZmZWN0cyB0aGUgYW1vdW50IG9mXHJcblx0Ly8gc2ltcGxpZmljYXRpb24gKGxlc3NlciB2YWx1ZSBtZWFucyBoaWdoZXIgcXVhbGl0eSBidXQgc2xvd2VyIGFuZCB3aXRoIG1vcmUgcG9pbnRzKS5cclxuXHQvLyBBbHNvIHJlbGVhc2VkIGFzIGEgc2VwYXJhdGVkIG1pY3JvLWxpYnJhcnkgW1NpbXBsaWZ5LmpzXShodHRwOi8vbW91cm5lci5naXRodWIuY29tL3NpbXBsaWZ5LWpzLykuXHJcblx0c2ltcGxpZnk6IGZ1bmN0aW9uIChwb2ludHMsIHRvbGVyYW5jZSkge1xyXG5cdFx0aWYgKCF0b2xlcmFuY2UgfHwgIXBvaW50cy5sZW5ndGgpIHtcclxuXHRcdFx0cmV0dXJuIHBvaW50cy5zbGljZSgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBzcVRvbGVyYW5jZSA9IHRvbGVyYW5jZSAqIHRvbGVyYW5jZTtcclxuXHJcblx0XHQvLyBzdGFnZSAxOiB2ZXJ0ZXggcmVkdWN0aW9uXHJcblx0XHRwb2ludHMgPSB0aGlzLl9yZWR1Y2VQb2ludHMocG9pbnRzLCBzcVRvbGVyYW5jZSk7XHJcblxyXG5cdFx0Ly8gc3RhZ2UgMjogRG91Z2xhcy1QZXVja2VyIHNpbXBsaWZpY2F0aW9uXHJcblx0XHRwb2ludHMgPSB0aGlzLl9zaW1wbGlmeURQKHBvaW50cywgc3FUb2xlcmFuY2UpO1xyXG5cclxuXHRcdHJldHVybiBwb2ludHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHBvaW50VG9TZWdtZW50RGlzdGFuY2UocDogUG9pbnQsIHAxOiBQb2ludCwgcDI6IFBvaW50KTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZGlzdGFuY2UgYmV0d2VlbiBwb2ludCBgcGAgYW5kIHNlZ21lbnQgYHAxYCB0byBgcDJgLlxyXG5cdHBvaW50VG9TZWdtZW50RGlzdGFuY2U6ICBmdW5jdGlvbiAocCwgcDEsIHAyKSB7XHJcblx0XHRyZXR1cm4gTWF0aC5zcXJ0KHRoaXMuX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50KHAsIHAxLCBwMiwgdHJ1ZSkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBjbG9zZXN0UG9pbnRPblNlZ21lbnQocDogUG9pbnQsIHAxOiBQb2ludCwgcDI6IFBvaW50KTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgY2xvc2VzdCBwb2ludCBmcm9tIGEgcG9pbnQgYHBgIG9uIGEgc2VnbWVudCBgcDFgIHRvIGBwMmAuXHJcblx0Y2xvc2VzdFBvaW50T25TZWdtZW50OiBmdW5jdGlvbiAocCwgcDEsIHAyKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fc3FDbG9zZXN0UG9pbnRPblNlZ21lbnQocCwgcDEsIHAyKTtcclxuXHR9LFxyXG5cclxuXHQvLyBEb3VnbGFzLVBldWNrZXIgc2ltcGxpZmljYXRpb24sIHNlZSBodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0RvdWdsYXMtUGV1Y2tlcl9hbGdvcml0aG1cclxuXHRfc2ltcGxpZnlEUDogZnVuY3Rpb24gKHBvaW50cywgc3FUb2xlcmFuY2UpIHtcclxuXHJcblx0XHR2YXIgbGVuID0gcG9pbnRzLmxlbmd0aCxcclxuXHRcdCAgICBBcnJheUNvbnN0cnVjdG9yID0gdHlwZW9mIFVpbnQ4QXJyYXkgIT09IHVuZGVmaW5lZCArICcnID8gVWludDhBcnJheSA6IEFycmF5LFxyXG5cdFx0ICAgIG1hcmtlcnMgPSBuZXcgQXJyYXlDb25zdHJ1Y3RvcihsZW4pO1xyXG5cclxuXHRcdG1hcmtlcnNbMF0gPSBtYXJrZXJzW2xlbiAtIDFdID0gMTtcclxuXHJcblx0XHR0aGlzLl9zaW1wbGlmeURQU3RlcChwb2ludHMsIG1hcmtlcnMsIHNxVG9sZXJhbmNlLCAwLCBsZW4gLSAxKTtcclxuXHJcblx0XHR2YXIgaSxcclxuXHRcdCAgICBuZXdQb2ludHMgPSBbXTtcclxuXHJcblx0XHRmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0aWYgKG1hcmtlcnNbaV0pIHtcclxuXHRcdFx0XHRuZXdQb2ludHMucHVzaChwb2ludHNbaV0pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIG5ld1BvaW50cztcclxuXHR9LFxyXG5cclxuXHRfc2ltcGxpZnlEUFN0ZXA6IGZ1bmN0aW9uIChwb2ludHMsIG1hcmtlcnMsIHNxVG9sZXJhbmNlLCBmaXJzdCwgbGFzdCkge1xyXG5cclxuXHRcdHZhciBtYXhTcURpc3QgPSAwLFxyXG5cdFx0ICAgIGluZGV4LCBpLCBzcURpc3Q7XHJcblxyXG5cdFx0Zm9yIChpID0gZmlyc3QgKyAxOyBpIDw9IGxhc3QgLSAxOyBpKyspIHtcclxuXHRcdFx0c3FEaXN0ID0gdGhpcy5fc3FDbG9zZXN0UG9pbnRPblNlZ21lbnQocG9pbnRzW2ldLCBwb2ludHNbZmlyc3RdLCBwb2ludHNbbGFzdF0sIHRydWUpO1xyXG5cclxuXHRcdFx0aWYgKHNxRGlzdCA+IG1heFNxRGlzdCkge1xyXG5cdFx0XHRcdGluZGV4ID0gaTtcclxuXHRcdFx0XHRtYXhTcURpc3QgPSBzcURpc3Q7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRpZiAobWF4U3FEaXN0ID4gc3FUb2xlcmFuY2UpIHtcclxuXHRcdFx0bWFya2Vyc1tpbmRleF0gPSAxO1xyXG5cclxuXHRcdFx0dGhpcy5fc2ltcGxpZnlEUFN0ZXAocG9pbnRzLCBtYXJrZXJzLCBzcVRvbGVyYW5jZSwgZmlyc3QsIGluZGV4KTtcclxuXHRcdFx0dGhpcy5fc2ltcGxpZnlEUFN0ZXAocG9pbnRzLCBtYXJrZXJzLCBzcVRvbGVyYW5jZSwgaW5kZXgsIGxhc3QpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIHJlZHVjZSBwb2ludHMgdGhhdCBhcmUgdG9vIGNsb3NlIHRvIGVhY2ggb3RoZXIgdG8gYSBzaW5nbGUgcG9pbnRcclxuXHRfcmVkdWNlUG9pbnRzOiBmdW5jdGlvbiAocG9pbnRzLCBzcVRvbGVyYW5jZSkge1xyXG5cdFx0dmFyIHJlZHVjZWRQb2ludHMgPSBbcG9pbnRzWzBdXTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMSwgcHJldiA9IDAsIGxlbiA9IHBvaW50cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRpZiAodGhpcy5fc3FEaXN0KHBvaW50c1tpXSwgcG9pbnRzW3ByZXZdKSA+IHNxVG9sZXJhbmNlKSB7XHJcblx0XHRcdFx0cmVkdWNlZFBvaW50cy5wdXNoKHBvaW50c1tpXSk7XHJcblx0XHRcdFx0cHJldiA9IGk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdGlmIChwcmV2IDwgbGVuIC0gMSkge1xyXG5cdFx0XHRyZWR1Y2VkUG9pbnRzLnB1c2gocG9pbnRzW2xlbiAtIDFdKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiByZWR1Y2VkUG9pbnRzO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY2xpcFNlZ21lbnQoYTogUG9pbnQsIGI6IFBvaW50LCBib3VuZHM6IEJvdW5kcywgdXNlTGFzdENvZGU/OiBCb29sZWFuLCByb3VuZD86IEJvb2xlYW4pOiBQb2ludFtdfEJvb2xlYW5cclxuXHQvLyBDbGlwcyB0aGUgc2VnbWVudCBhIHRvIGIgYnkgcmVjdGFuZ3VsYXIgYm91bmRzIHdpdGggdGhlXHJcblx0Ly8gW0NvaGVuLVN1dGhlcmxhbmQgYWxnb3JpdGhtXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db2hlbiVFMiU4MCU5M1N1dGhlcmxhbmRfYWxnb3JpdGhtKVxyXG5cdC8vIChtb2RpZnlpbmcgdGhlIHNlZ21lbnQgcG9pbnRzIGRpcmVjdGx5ISkuIFVzZWQgYnkgTGVhZmxldCB0byBvbmx5IHNob3cgcG9seWxpbmVcclxuXHQvLyBwb2ludHMgdGhhdCBhcmUgb24gdGhlIHNjcmVlbiBvciBuZWFyLCBpbmNyZWFzaW5nIHBlcmZvcm1hbmNlLlxyXG5cdGNsaXBTZWdtZW50OiBmdW5jdGlvbiAoYSwgYiwgYm91bmRzLCB1c2VMYXN0Q29kZSwgcm91bmQpIHtcclxuXHRcdHZhciBjb2RlQSA9IHVzZUxhc3RDb2RlID8gdGhpcy5fbGFzdENvZGUgOiB0aGlzLl9nZXRCaXRDb2RlKGEsIGJvdW5kcyksXHJcblx0XHQgICAgY29kZUIgPSB0aGlzLl9nZXRCaXRDb2RlKGIsIGJvdW5kcyksXHJcblxyXG5cdFx0ICAgIGNvZGVPdXQsIHAsIG5ld0NvZGU7XHJcblxyXG5cdFx0Ly8gc2F2ZSAybmQgY29kZSB0byBhdm9pZCBjYWxjdWxhdGluZyBpdCBvbiB0aGUgbmV4dCBzZWdtZW50XHJcblx0XHR0aGlzLl9sYXN0Q29kZSA9IGNvZGVCO1xyXG5cclxuXHRcdHdoaWxlICh0cnVlKSB7XHJcblx0XHRcdC8vIGlmIGEsYiBpcyBpbnNpZGUgdGhlIGNsaXAgd2luZG93ICh0cml2aWFsIGFjY2VwdClcclxuXHRcdFx0aWYgKCEoY29kZUEgfCBjb2RlQikpIHtcclxuXHRcdFx0XHRyZXR1cm4gW2EsIGJdO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBpZiBhLGIgaXMgb3V0c2lkZSB0aGUgY2xpcCB3aW5kb3cgKHRyaXZpYWwgcmVqZWN0KVxyXG5cdFx0XHRpZiAoY29kZUEgJiBjb2RlQikge1xyXG5cdFx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gb3RoZXIgY2FzZXNcclxuXHRcdFx0Y29kZU91dCA9IGNvZGVBIHx8IGNvZGVCO1xyXG5cdFx0XHRwID0gdGhpcy5fZ2V0RWRnZUludGVyc2VjdGlvbihhLCBiLCBjb2RlT3V0LCBib3VuZHMsIHJvdW5kKTtcclxuXHRcdFx0bmV3Q29kZSA9IHRoaXMuX2dldEJpdENvZGUocCwgYm91bmRzKTtcclxuXHJcblx0XHRcdGlmIChjb2RlT3V0ID09PSBjb2RlQSkge1xyXG5cdFx0XHRcdGEgPSBwO1xyXG5cdFx0XHRcdGNvZGVBID0gbmV3Q29kZTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRiID0gcDtcclxuXHRcdFx0XHRjb2RlQiA9IG5ld0NvZGU7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfZ2V0RWRnZUludGVyc2VjdGlvbjogZnVuY3Rpb24gKGEsIGIsIGNvZGUsIGJvdW5kcywgcm91bmQpIHtcclxuXHRcdHZhciBkeCA9IGIueCAtIGEueCxcclxuXHRcdCAgICBkeSA9IGIueSAtIGEueSxcclxuXHRcdCAgICBtaW4gPSBib3VuZHMubWluLFxyXG5cdFx0ICAgIG1heCA9IGJvdW5kcy5tYXgsXHJcblx0XHQgICAgeCwgeTtcclxuXHJcblx0XHRpZiAoY29kZSAmIDgpIHsgLy8gdG9wXHJcblx0XHRcdHggPSBhLnggKyBkeCAqIChtYXgueSAtIGEueSkgLyBkeTtcclxuXHRcdFx0eSA9IG1heC55O1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoY29kZSAmIDQpIHsgLy8gYm90dG9tXHJcblx0XHRcdHggPSBhLnggKyBkeCAqIChtaW4ueSAtIGEueSkgLyBkeTtcclxuXHRcdFx0eSA9IG1pbi55O1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoY29kZSAmIDIpIHsgLy8gcmlnaHRcclxuXHRcdFx0eCA9IG1heC54O1xyXG5cdFx0XHR5ID0gYS55ICsgZHkgKiAobWF4LnggLSBhLngpIC8gZHg7XHJcblxyXG5cdFx0fSBlbHNlIGlmIChjb2RlICYgMSkgeyAvLyBsZWZ0XHJcblx0XHRcdHggPSBtaW4ueDtcclxuXHRcdFx0eSA9IGEueSArIGR5ICogKG1pbi54IC0gYS54KSAvIGR4O1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludCh4LCB5LCByb3VuZCk7XHJcblx0fSxcclxuXHJcblx0X2dldEJpdENvZGU6IGZ1bmN0aW9uIChwLCBib3VuZHMpIHtcclxuXHRcdHZhciBjb2RlID0gMDtcclxuXHJcblx0XHRpZiAocC54IDwgYm91bmRzLm1pbi54KSB7IC8vIGxlZnRcclxuXHRcdFx0Y29kZSB8PSAxO1xyXG5cdFx0fSBlbHNlIGlmIChwLnggPiBib3VuZHMubWF4LngpIHsgLy8gcmlnaHRcclxuXHRcdFx0Y29kZSB8PSAyO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChwLnkgPCBib3VuZHMubWluLnkpIHsgLy8gYm90dG9tXHJcblx0XHRcdGNvZGUgfD0gNDtcclxuXHRcdH0gZWxzZSBpZiAocC55ID4gYm91bmRzLm1heC55KSB7IC8vIHRvcFxyXG5cdFx0XHRjb2RlIHw9IDg7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIGNvZGU7XHJcblx0fSxcclxuXHJcblx0Ly8gc3F1YXJlIGRpc3RhbmNlICh0byBhdm9pZCB1bm5lY2Vzc2FyeSBNYXRoLnNxcnQgY2FsbHMpXHJcblx0X3NxRGlzdDogZnVuY3Rpb24gKHAxLCBwMikge1xyXG5cdFx0dmFyIGR4ID0gcDIueCAtIHAxLngsXHJcblx0XHQgICAgZHkgPSBwMi55IC0gcDEueTtcclxuXHRcdHJldHVybiBkeCAqIGR4ICsgZHkgKiBkeTtcclxuXHR9LFxyXG5cclxuXHQvLyByZXR1cm4gY2xvc2VzdCBwb2ludCBvbiBzZWdtZW50IG9yIGRpc3RhbmNlIHRvIHRoYXQgcG9pbnRcclxuXHRfc3FDbG9zZXN0UG9pbnRPblNlZ21lbnQ6IGZ1bmN0aW9uIChwLCBwMSwgcDIsIHNxRGlzdCkge1xyXG5cdFx0dmFyIHggPSBwMS54LFxyXG5cdFx0ICAgIHkgPSBwMS55LFxyXG5cdFx0ICAgIGR4ID0gcDIueCAtIHgsXHJcblx0XHQgICAgZHkgPSBwMi55IC0geSxcclxuXHRcdCAgICBkb3QgPSBkeCAqIGR4ICsgZHkgKiBkeSxcclxuXHRcdCAgICB0O1xyXG5cclxuXHRcdGlmIChkb3QgPiAwKSB7XHJcblx0XHRcdHQgPSAoKHAueCAtIHgpICogZHggKyAocC55IC0geSkgKiBkeSkgLyBkb3Q7XHJcblxyXG5cdFx0XHRpZiAodCA+IDEpIHtcclxuXHRcdFx0XHR4ID0gcDIueDtcclxuXHRcdFx0XHR5ID0gcDIueTtcclxuXHRcdFx0fSBlbHNlIGlmICh0ID4gMCkge1xyXG5cdFx0XHRcdHggKz0gZHggKiB0O1xyXG5cdFx0XHRcdHkgKz0gZHkgKiB0O1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0ZHggPSBwLnggLSB4O1xyXG5cdFx0ZHkgPSBwLnkgLSB5O1xyXG5cclxuXHRcdHJldHVybiBzcURpc3QgPyBkeCAqIGR4ICsgZHkgKiBkeSA6IG5ldyBMLlBvaW50KHgsIHkpO1xyXG5cdH1cclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBQb2x5bGluZVxuICogQGFrYSBMLlBvbHlsaW5lXG4gKiBAaW5oZXJpdHMgUGF0aFxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgcG9seWxpbmUgb3ZlcmxheXMgb24gYSBtYXAuIEV4dGVuZHMgYFBhdGhgLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIC8vIGNyZWF0ZSBhIHJlZCBwb2x5bGluZSBmcm9tIGFuIGFycmF5IG9mIExhdExuZyBwb2ludHNcbiAqIHZhciBsYXRsbmdzID0gW1xuICogXHRbLTEyMi42OCwgNDUuNTFdLFxuICogXHRbLTEyMi40MywgMzcuNzddLFxuICogXHRbLTExOC4yLCAzNC4wNF1cbiAqIF07XG4gKlxuICogdmFyIHBvbHlsaW5lID0gTC5wb2x5bGluZShsYXRsbmdzLCB7Y29sb3I6ICdyZWQnfSkuYWRkVG8obWFwKTtcbiAqXG4gKiAvLyB6b29tIHRoZSBtYXAgdG8gdGhlIHBvbHlsaW5lXG4gKiBtYXAuZml0Qm91bmRzKHBvbHlsaW5lLmdldEJvdW5kcygpKTtcbiAqIGBgYFxuICpcbiAqIFlvdSBjYW4gYWxzbyBwYXNzIGEgbXVsdGktZGltZW5zaW9uYWwgYXJyYXkgdG8gcmVwcmVzZW50IGEgYE11bHRpUG9seWxpbmVgIHNoYXBlOlxuICpcbiAqIGBgYGpzXG4gKiAvLyBjcmVhdGUgYSByZWQgcG9seWxpbmUgZnJvbSBhbiBhcnJheSBvZiBhcnJheXMgb2YgTGF0TG5nIHBvaW50c1xuICogdmFyIGxhdGxuZ3MgPSBbXG4gKiBcdFtbLTEyMi42OCwgNDUuNTFdLFxuICogXHQgWy0xMjIuNDMsIDM3Ljc3XSxcbiAqIFx0IFstMTE4LjIsIDM0LjA0XV0sXG4gKiBcdFtbLTczLjkxLCA0MC43OF0sXG4gKiBcdCBbLTg3LjYyLCA0MS44M10sXG4gKiBcdCBbLTk2LjcyLCAzMi43Nl1dXG4gKiBdO1xuICogYGBgXG4gKi9cblxuTC5Qb2x5bGluZSA9IEwuUGF0aC5leHRlbmQoe1xuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgUG9seWxpbmUgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBzbW9vdGhGYWN0b3I6IE51bWJlciA9IDEuMFxuXHRcdC8vIEhvdyBtdWNoIHRvIHNpbXBsaWZ5IHRoZSBwb2x5bGluZSBvbiBlYWNoIHpvb20gbGV2ZWwuIE1vcmUgbWVhbnNcblx0XHQvLyBiZXR0ZXIgcGVyZm9ybWFuY2UgYW5kIHNtb290aGVyIGxvb2ssIGFuZCBsZXNzIG1lYW5zIG1vcmUgYWNjdXJhdGUgcmVwcmVzZW50YXRpb24uXG5cdFx0c21vb3RoRmFjdG9yOiAxLjAsXG5cblx0XHQvLyBAb3B0aW9uIG5vQ2xpcDogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gRGlzYWJsZSBwb2x5bGluZSBjbGlwcGluZy5cblx0XHRub0NsaXA6IGZhbHNlXG5cdH0sXG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGxhdGxuZ3MsIG9wdGlvbnMpIHtcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdFx0dGhpcy5fc2V0TGF0TG5ncyhsYXRsbmdzKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldExhdExuZ3MoKTogTGF0TG5nW11cblx0Ly8gUmV0dXJucyBhbiBhcnJheSBvZiB0aGUgcG9pbnRzIGluIHRoZSBwYXRoLCBvciBuZXN0ZWQgYXJyYXlzIG9mIHBvaW50cyBpbiBjYXNlIG9mIG11bHRpLXBvbHlsaW5lLlxuXHRnZXRMYXRMbmdzOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2xhdGxuZ3M7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRMYXRMbmdzKGxhdGxuZ3M6IExhdExuZ1tdKTogdGhpc1xuXHQvLyBSZXBsYWNlcyBhbGwgdGhlIHBvaW50cyBpbiB0aGUgcG9seWxpbmUgd2l0aCB0aGUgZ2l2ZW4gYXJyYXkgb2YgZ2VvZ3JhcGhpY2FsIHBvaW50cy5cblx0c2V0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHR0aGlzLl9zZXRMYXRMbmdzKGxhdGxuZ3MpO1xuXHRcdHJldHVybiB0aGlzLnJlZHJhdygpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgaXNFbXB0eSgpOiBCb29sZWFuXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBQb2x5bGluZSBoYXMgbm8gTGF0TG5ncy5cblx0aXNFbXB0eTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiAhdGhpcy5fbGF0bG5ncy5sZW5ndGg7XG5cdH0sXG5cblx0Y2xvc2VzdExheWVyUG9pbnQ6IGZ1bmN0aW9uIChwKSB7XG5cdFx0dmFyIG1pbkRpc3RhbmNlID0gSW5maW5pdHksXG5cdFx0ICAgIG1pblBvaW50ID0gbnVsbCxcblx0XHQgICAgY2xvc2VzdCA9IEwuTGluZVV0aWwuX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50LFxuXHRcdCAgICBwMSwgcDI7XG5cblx0XHRmb3IgKHZhciBqID0gMCwgakxlbiA9IHRoaXMuX3BhcnRzLmxlbmd0aDsgaiA8IGpMZW47IGorKykge1xuXHRcdFx0dmFyIHBvaW50cyA9IHRoaXMuX3BhcnRzW2pdO1xuXG5cdFx0XHRmb3IgKHZhciBpID0gMSwgbGVuID0gcG9pbnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRcdHAxID0gcG9pbnRzW2kgLSAxXTtcblx0XHRcdFx0cDIgPSBwb2ludHNbaV07XG5cblx0XHRcdFx0dmFyIHNxRGlzdCA9IGNsb3Nlc3QocCwgcDEsIHAyLCB0cnVlKTtcblxuXHRcdFx0XHRpZiAoc3FEaXN0IDwgbWluRGlzdGFuY2UpIHtcblx0XHRcdFx0XHRtaW5EaXN0YW5jZSA9IHNxRGlzdDtcblx0XHRcdFx0XHRtaW5Qb2ludCA9IGNsb3Nlc3QocCwgcDEsIHAyKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAobWluUG9pbnQpIHtcblx0XHRcdG1pblBvaW50LmRpc3RhbmNlID0gTWF0aC5zcXJ0KG1pbkRpc3RhbmNlKTtcblx0XHR9XG5cdFx0cmV0dXJuIG1pblBvaW50O1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKCk6IExhdExuZ1xuXHQvLyBSZXR1cm5zIHRoZSBjZW50ZXIgKFtjZW50cm9pZF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DZW50cm9pZCkpIG9mIHRoZSBwb2x5bGluZS5cblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gdGhyb3dzIGVycm9yIHdoZW4gbm90IHlldCBhZGRlZCB0byBtYXAgYXMgdGhpcyBjZW50ZXIgY2FsY3VsYXRpb24gcmVxdWlyZXMgcHJvamVjdGVkIGNvb3JkaW5hdGVzXG5cdFx0aWYgKCF0aGlzLl9tYXApIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcignTXVzdCBhZGQgbGF5ZXIgdG8gbWFwIGJlZm9yZSB1c2luZyBnZXRDZW50ZXIoKScpO1xuXHRcdH1cblxuXHRcdHZhciBpLCBoYWxmRGlzdCwgc2VnRGlzdCwgZGlzdCwgcDEsIHAyLCByYXRpbyxcblx0XHQgICAgcG9pbnRzID0gdGhpcy5fcmluZ3NbMF0sXG5cdFx0ICAgIGxlbiA9IHBvaW50cy5sZW5ndGg7XG5cblx0XHRpZiAoIWxlbikgeyByZXR1cm4gbnVsbDsgfVxuXG5cdFx0Ly8gcG9seWxpbmUgY2VudHJvaWQgYWxnb3JpdGhtOyBvbmx5IHVzZXMgdGhlIGZpcnN0IHJpbmcgaWYgdGhlcmUgYXJlIG11bHRpcGxlXG5cblx0XHRmb3IgKGkgPSAwLCBoYWxmRGlzdCA9IDA7IGkgPCBsZW4gLSAxOyBpKyspIHtcblx0XHRcdGhhbGZEaXN0ICs9IHBvaW50c1tpXS5kaXN0YW5jZVRvKHBvaW50c1tpICsgMV0pIC8gMjtcblx0XHR9XG5cblx0XHQvLyBUaGUgbGluZSBpcyBzbyBzbWFsbCBpbiB0aGUgY3VycmVudCB2aWV3IHRoYXQgYWxsIHBvaW50cyBhcmUgb24gdGhlIHNhbWUgcGl4ZWwuXG5cdFx0aWYgKGhhbGZEaXN0ID09PSAwKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyhwb2ludHNbMF0pO1xuXHRcdH1cblxuXHRcdGZvciAoaSA9IDAsIGRpc3QgPSAwOyBpIDwgbGVuIC0gMTsgaSsrKSB7XG5cdFx0XHRwMSA9IHBvaW50c1tpXTtcblx0XHRcdHAyID0gcG9pbnRzW2kgKyAxXTtcblx0XHRcdHNlZ0Rpc3QgPSBwMS5kaXN0YW5jZVRvKHAyKTtcblx0XHRcdGRpc3QgKz0gc2VnRGlzdDtcblxuXHRcdFx0aWYgKGRpc3QgPiBoYWxmRGlzdCkge1xuXHRcdFx0XHRyYXRpbyA9IChkaXN0IC0gaGFsZkRpc3QpIC8gc2VnRGlzdDtcblx0XHRcdFx0cmV0dXJuIHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcoW1xuXHRcdFx0XHRcdHAyLnggLSByYXRpbyAqIChwMi54IC0gcDEueCksXG5cdFx0XHRcdFx0cDIueSAtIHJhdGlvICogKHAyLnkgLSBwMS55KVxuXHRcdFx0XHRdKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHMoKTogTGF0TG5nQm91bmRzXG5cdC8vIFJldHVybnMgdGhlIGBMYXRMbmdCb3VuZHNgIG9mIHRoZSBwYXRoLlxuXHRnZXRCb3VuZHM6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fYm91bmRzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYWRkTGF0TG5nKGxhdGxuZzogTGF0TG5nLCBsYXRsbmdzPyBMYXRMbmdbXSk6IHRoaXNcblx0Ly8gQWRkcyBhIGdpdmVuIHBvaW50IHRvIHRoZSBwb2x5bGluZS4gQnkgZGVmYXVsdCwgYWRkcyB0byB0aGUgZmlyc3QgcmluZyBvZlxuXHQvLyB0aGUgcG9seWxpbmUgaW4gY2FzZSBvZiBhIG11bHRpLXBvbHlsaW5lLCBidXQgY2FuIGJlIG92ZXJyaWRkZW4gYnkgcGFzc2luZ1xuXHQvLyBhIHNwZWNpZmljIHJpbmcgYXMgYSBMYXRMbmcgYXJyYXkgKHRoYXQgeW91IGNhbiBlYXJsaWVyIGFjY2VzcyB3aXRoIFtgZ2V0TGF0TG5nc2BdKCNwb2x5bGluZS1nZXRsYXRsbmdzKSkuXG5cdGFkZExhdExuZzogZnVuY3Rpb24gKGxhdGxuZywgbGF0bG5ncykge1xuXHRcdGxhdGxuZ3MgPSBsYXRsbmdzIHx8IHRoaXMuX2RlZmF1bHRTaGFwZSgpO1xuXHRcdGxhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XG5cdFx0bGF0bG5ncy5wdXNoKGxhdGxuZyk7XG5cdFx0dGhpcy5fYm91bmRzLmV4dGVuZChsYXRsbmcpO1xuXHRcdHJldHVybiB0aGlzLnJlZHJhdygpO1xuXHR9LFxuXG5cdF9zZXRMYXRMbmdzOiBmdW5jdGlvbiAobGF0bG5ncykge1xuXHRcdHRoaXMuX2JvdW5kcyA9IG5ldyBMLkxhdExuZ0JvdW5kcygpO1xuXHRcdHRoaXMuX2xhdGxuZ3MgPSB0aGlzLl9jb252ZXJ0TGF0TG5ncyhsYXRsbmdzKTtcblx0fSxcblxuXHRfZGVmYXVsdFNoYXBlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIEwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5ncykgPyB0aGlzLl9sYXRsbmdzIDogdGhpcy5fbGF0bG5nc1swXTtcblx0fSxcblxuXHQvLyByZWN1cnNpdmVseSBjb252ZXJ0IGxhdGxuZ3MgaW5wdXQgaW50byBhY3R1YWwgTGF0TG5nIGluc3RhbmNlczsgY2FsY3VsYXRlIGJvdW5kcyBhbG9uZyB0aGUgd2F5XG5cdF9jb252ZXJ0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHR2YXIgcmVzdWx0ID0gW10sXG5cdFx0ICAgIGZsYXQgPSBMLlBvbHlsaW5lLl9mbGF0KGxhdGxuZ3MpO1xuXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGxhdGxuZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGlmIChmbGF0KSB7XG5cdFx0XHRcdHJlc3VsdFtpXSA9IEwubGF0TG5nKGxhdGxuZ3NbaV0pO1xuXHRcdFx0XHR0aGlzLl9ib3VuZHMuZXh0ZW5kKHJlc3VsdFtpXSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRyZXN1bHRbaV0gPSB0aGlzLl9jb252ZXJ0TGF0TG5ncyhsYXRsbmdzW2ldKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gcmVzdWx0O1xuXHR9LFxuXG5cdF9wcm9qZWN0OiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIHB4Qm91bmRzID0gbmV3IEwuQm91bmRzKCk7XG5cdFx0dGhpcy5fcmluZ3MgPSBbXTtcblx0XHR0aGlzLl9wcm9qZWN0TGF0bG5ncyh0aGlzLl9sYXRsbmdzLCB0aGlzLl9yaW5ncywgcHhCb3VuZHMpO1xuXG5cdFx0dmFyIHcgPSB0aGlzLl9jbGlja1RvbGVyYW5jZSgpLFxuXHRcdCAgICBwID0gbmV3IEwuUG9pbnQodywgdyk7XG5cblx0XHRpZiAodGhpcy5fYm91bmRzLmlzVmFsaWQoKSAmJiBweEJvdW5kcy5pc1ZhbGlkKCkpIHtcblx0XHRcdHB4Qm91bmRzLm1pbi5fc3VidHJhY3QocCk7XG5cdFx0XHRweEJvdW5kcy5tYXguX2FkZChwKTtcblx0XHRcdHRoaXMuX3B4Qm91bmRzID0gcHhCb3VuZHM7XG5cdFx0fVxuXHR9LFxuXG5cdC8vIHJlY3Vyc2l2ZWx5IHR1cm5zIGxhdGxuZ3MgaW50byBhIHNldCBvZiByaW5ncyB3aXRoIHByb2plY3RlZCBjb29yZGluYXRlc1xuXHRfcHJvamVjdExhdGxuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzLCByZXN1bHQsIHByb2plY3RlZEJvdW5kcykge1xuXHRcdHZhciBmbGF0ID0gbGF0bG5nc1swXSBpbnN0YW5jZW9mIEwuTGF0TG5nLFxuXHRcdCAgICBsZW4gPSBsYXRsbmdzLmxlbmd0aCxcblx0XHQgICAgaSwgcmluZztcblxuXHRcdGlmIChmbGF0KSB7XG5cdFx0XHRyaW5nID0gW107XG5cdFx0XHRmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdFx0cmluZ1tpXSA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQobGF0bG5nc1tpXSk7XG5cdFx0XHRcdHByb2plY3RlZEJvdW5kcy5leHRlbmQocmluZ1tpXSk7XG5cdFx0XHR9XG5cdFx0XHRyZXN1bHQucHVzaChyaW5nKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Zm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRcdHRoaXMuX3Byb2plY3RMYXRsbmdzKGxhdGxuZ3NbaV0sIHJlc3VsdCwgcHJvamVjdGVkQm91bmRzKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0Ly8gY2xpcCBwb2x5bGluZSBieSByZW5kZXJlciBib3VuZHMgc28gdGhhdCB3ZSBoYXZlIGxlc3MgdG8gcmVuZGVyIGZvciBwZXJmb3JtYW5jZVxuXHRfY2xpcFBvaW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBib3VuZHMgPSB0aGlzLl9yZW5kZXJlci5fYm91bmRzO1xuXG5cdFx0dGhpcy5fcGFydHMgPSBbXTtcblx0XHRpZiAoIXRoaXMuX3B4Qm91bmRzIHx8ICF0aGlzLl9weEJvdW5kcy5pbnRlcnNlY3RzKGJvdW5kcykpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5vcHRpb25zLm5vQ2xpcCkge1xuXHRcdFx0dGhpcy5fcGFydHMgPSB0aGlzLl9yaW5ncztcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR2YXIgcGFydHMgPSB0aGlzLl9wYXJ0cyxcblx0XHQgICAgaSwgaiwgaywgbGVuLCBsZW4yLCBzZWdtZW50LCBwb2ludHM7XG5cblx0XHRmb3IgKGkgPSAwLCBrID0gMCwgbGVuID0gdGhpcy5fcmluZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdHBvaW50cyA9IHRoaXMuX3JpbmdzW2ldO1xuXG5cdFx0XHRmb3IgKGogPSAwLCBsZW4yID0gcG9pbnRzLmxlbmd0aDsgaiA8IGxlbjIgLSAxOyBqKyspIHtcblx0XHRcdFx0c2VnbWVudCA9IEwuTGluZVV0aWwuY2xpcFNlZ21lbnQocG9pbnRzW2pdLCBwb2ludHNbaiArIDFdLCBib3VuZHMsIGosIHRydWUpO1xuXG5cdFx0XHRcdGlmICghc2VnbWVudCkgeyBjb250aW51ZTsgfVxuXG5cdFx0XHRcdHBhcnRzW2tdID0gcGFydHNba10gfHwgW107XG5cdFx0XHRcdHBhcnRzW2tdLnB1c2goc2VnbWVudFswXSk7XG5cblx0XHRcdFx0Ly8gaWYgc2VnbWVudCBnb2VzIG91dCBvZiBzY3JlZW4sIG9yIGl0J3MgdGhlIGxhc3Qgb25lLCBpdCdzIHRoZSBlbmQgb2YgdGhlIGxpbmUgcGFydFxuXHRcdFx0XHRpZiAoKHNlZ21lbnRbMV0gIT09IHBvaW50c1tqICsgMV0pIHx8IChqID09PSBsZW4yIC0gMikpIHtcblx0XHRcdFx0XHRwYXJ0c1trXS5wdXNoKHNlZ21lbnRbMV0pO1xuXHRcdFx0XHRcdGsrKztcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHQvLyBzaW1wbGlmeSBlYWNoIGNsaXBwZWQgcGFydCBvZiB0aGUgcG9seWxpbmUgZm9yIHBlcmZvcm1hbmNlXG5cdF9zaW1wbGlmeVBvaW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBwYXJ0cyA9IHRoaXMuX3BhcnRzLFxuXHRcdCAgICB0b2xlcmFuY2UgPSB0aGlzLm9wdGlvbnMuc21vb3RoRmFjdG9yO1xuXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHBhcnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRwYXJ0c1tpXSA9IEwuTGluZVV0aWwuc2ltcGxpZnkocGFydHNbaV0sIHRvbGVyYW5jZSk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2NsaXBQb2ludHMoKTtcblx0XHR0aGlzLl9zaW1wbGlmeVBvaW50cygpO1xuXHRcdHRoaXMuX3VwZGF0ZVBhdGgoKTtcblx0fSxcblxuXHRfdXBkYXRlUGF0aDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbmRlcmVyLl91cGRhdGVQb2x5KHRoaXMpO1xuXHR9XG59KTtcblxuLy8gQGZhY3RvcnkgTC5wb2x5bGluZShsYXRsbmdzOiBMYXRMbmdbXSwgb3B0aW9ucz86IFBvbHlsaW5lIG9wdGlvbnMpXG4vLyBJbnN0YW50aWF0ZXMgYSBwb2x5bGluZSBvYmplY3QgZ2l2ZW4gYW4gYXJyYXkgb2YgZ2VvZ3JhcGhpY2FsIHBvaW50cyBhbmRcbi8vIG9wdGlvbmFsbHkgYW4gb3B0aW9ucyBvYmplY3QuIFlvdSBjYW4gY3JlYXRlIGEgYFBvbHlsaW5lYCBvYmplY3Qgd2l0aFxuLy8gbXVsdGlwbGUgc2VwYXJhdGUgbGluZXMgKGBNdWx0aVBvbHlsaW5lYCkgYnkgcGFzc2luZyBhbiBhcnJheSBvZiBhcnJheXNcbi8vIG9mIGdlb2dyYXBoaWMgcG9pbnRzLlxuTC5wb2x5bGluZSA9IGZ1bmN0aW9uIChsYXRsbmdzLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5Qb2x5bGluZShsYXRsbmdzLCBvcHRpb25zKTtcbn07XG5cbkwuUG9seWxpbmUuX2ZsYXQgPSBmdW5jdGlvbiAobGF0bG5ncykge1xuXHQvLyB0cnVlIGlmIGl0J3MgYSBmbGF0IGFycmF5IG9mIGxhdGxuZ3M7IGZhbHNlIGlmIG5lc3RlZFxuXHRyZXR1cm4gIUwuVXRpbC5pc0FycmF5KGxhdGxuZ3NbMF0pIHx8ICh0eXBlb2YgbGF0bG5nc1swXVswXSAhPT0gJ29iamVjdCcgJiYgdHlwZW9mIGxhdGxuZ3NbMF1bMF0gIT09ICd1bmRlZmluZWQnKTtcbn07XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIFBvbHlVdGlsXHJcbiAqIFZhcmlvdXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIHBvbHlnb24gZ2VvbWV0cmllcy5cclxuICovXHJcblxyXG5MLlBvbHlVdGlsID0ge307XHJcblxyXG4vKiBAZnVuY3Rpb24gY2xpcFBvbHlnb24ocG9pbnRzOiBQb2ludFtdLCBib3VuZHM6IEJvdW5kcywgcm91bmQ/OiBCb29sZWFuKTogUG9pbnRbXVxyXG4gKiBDbGlwcyB0aGUgcG9seWdvbiBnZW9tZXRyeSBkZWZpbmVkIGJ5IHRoZSBnaXZlbiBgcG9pbnRzYCBieSB0aGUgZ2l2ZW4gYm91bmRzICh1c2luZyB0aGUgW1N1dGhlcmxhbmQtSG9kZ2VtYW4gYWxnb3JpdGhtXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdXRoZXJsYW5kJUUyJTgwJTkzSG9kZ21hbl9hbGdvcml0aG0pKS5cclxuICogVXNlZCBieSBMZWFmbGV0IHRvIG9ubHkgc2hvdyBwb2x5Z29uIHBvaW50cyB0aGF0IGFyZSBvbiB0aGUgc2NyZWVuIG9yIG5lYXIsIGluY3JlYXNpbmdcclxuICogcGVyZm9ybWFuY2UuIE5vdGUgdGhhdCBwb2x5Z29uIHBvaW50cyBuZWVkcyBkaWZmZXJlbnQgYWxnb3JpdGhtIGZvciBjbGlwcGluZ1xyXG4gKiB0aGFuIHBvbHlsaW5lLCBzbyB0aGVyZSdzIGEgc2VwZXJhdGUgbWV0aG9kIGZvciBpdC5cclxuICovXHJcbkwuUG9seVV0aWwuY2xpcFBvbHlnb24gPSBmdW5jdGlvbiAocG9pbnRzLCBib3VuZHMsIHJvdW5kKSB7XHJcblx0dmFyIGNsaXBwZWRQb2ludHMsXHJcblx0ICAgIGVkZ2VzID0gWzEsIDQsIDIsIDhdLFxyXG5cdCAgICBpLCBqLCBrLFxyXG5cdCAgICBhLCBiLFxyXG5cdCAgICBsZW4sIGVkZ2UsIHAsXHJcblx0ICAgIGx1ID0gTC5MaW5lVXRpbDtcclxuXHJcblx0Zm9yIChpID0gMCwgbGVuID0gcG9pbnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRwb2ludHNbaV0uX2NvZGUgPSBsdS5fZ2V0Qml0Q29kZShwb2ludHNbaV0sIGJvdW5kcyk7XHJcblx0fVxyXG5cclxuXHQvLyBmb3IgZWFjaCBlZGdlIChsZWZ0LCBib3R0b20sIHJpZ2h0LCB0b3ApXHJcblx0Zm9yIChrID0gMDsgayA8IDQ7IGsrKykge1xyXG5cdFx0ZWRnZSA9IGVkZ2VzW2tdO1xyXG5cdFx0Y2xpcHBlZFBvaW50cyA9IFtdO1xyXG5cclxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IHBvaW50cy5sZW5ndGgsIGogPSBsZW4gLSAxOyBpIDwgbGVuOyBqID0gaSsrKSB7XHJcblx0XHRcdGEgPSBwb2ludHNbaV07XHJcblx0XHRcdGIgPSBwb2ludHNbal07XHJcblxyXG5cdFx0XHQvLyBpZiBhIGlzIGluc2lkZSB0aGUgY2xpcCB3aW5kb3dcclxuXHRcdFx0aWYgKCEoYS5fY29kZSAmIGVkZ2UpKSB7XHJcblx0XHRcdFx0Ly8gaWYgYiBpcyBvdXRzaWRlIHRoZSBjbGlwIHdpbmRvdyAoYS0+YiBnb2VzIG91dCBvZiBzY3JlZW4pXHJcblx0XHRcdFx0aWYgKGIuX2NvZGUgJiBlZGdlKSB7XHJcblx0XHRcdFx0XHRwID0gbHUuX2dldEVkZ2VJbnRlcnNlY3Rpb24oYiwgYSwgZWRnZSwgYm91bmRzLCByb3VuZCk7XHJcblx0XHRcdFx0XHRwLl9jb2RlID0gbHUuX2dldEJpdENvZGUocCwgYm91bmRzKTtcclxuXHRcdFx0XHRcdGNsaXBwZWRQb2ludHMucHVzaChwKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0Y2xpcHBlZFBvaW50cy5wdXNoKGEpO1xyXG5cclxuXHRcdFx0Ly8gZWxzZSBpZiBiIGlzIGluc2lkZSB0aGUgY2xpcCB3aW5kb3cgKGEtPmIgZW50ZXJzIHRoZSBzY3JlZW4pXHJcblx0XHRcdH0gZWxzZSBpZiAoIShiLl9jb2RlICYgZWRnZSkpIHtcclxuXHRcdFx0XHRwID0gbHUuX2dldEVkZ2VJbnRlcnNlY3Rpb24oYiwgYSwgZWRnZSwgYm91bmRzLCByb3VuZCk7XHJcblx0XHRcdFx0cC5fY29kZSA9IGx1Ll9nZXRCaXRDb2RlKHAsIGJvdW5kcyk7XHJcblx0XHRcdFx0Y2xpcHBlZFBvaW50cy5wdXNoKHApO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRwb2ludHMgPSBjbGlwcGVkUG9pbnRzO1xyXG5cdH1cclxuXHJcblx0cmV0dXJuIHBvaW50cztcclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBQb2x5Z29uXG4gKiBAYWthIEwuUG9seWdvblxuICogQGluaGVyaXRzIFBvbHlsaW5lXG4gKlxuICogQSBjbGFzcyBmb3IgZHJhd2luZyBwb2x5Z29uIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBQb2x5bGluZWAuXG4gKlxuICogTm90ZSB0aGF0IHBvaW50cyB5b3UgcGFzcyB3aGVuIGNyZWF0aW5nIGEgcG9seWdvbiBzaG91bGRuJ3QgaGF2ZSBhbiBhZGRpdGlvbmFsIGxhc3QgcG9pbnQgZXF1YWwgdG8gdGhlIGZpcnN0IG9uZSDigJQgaXQncyBiZXR0ZXIgdG8gZmlsdGVyIG91dCBzdWNoIHBvaW50cy5cbiAqXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBgYGBqc1xuICogLy8gY3JlYXRlIGEgcmVkIHBvbHlnb24gZnJvbSBhbiBhcnJheSBvZiBMYXRMbmcgcG9pbnRzXG4gKiB2YXIgbGF0bG5ncyA9IFtbLTExMS4wMywgNDFdLFstMTExLjA0LCA0NV0sWy0xMDQuMDUsIDQ1XSxbLTEwNC4wNSwgNDFdXTtcbiAqXG4gKiB2YXIgcG9seWdvbiA9IEwucG9seWdvbihsYXRsbmdzLCB7Y29sb3I6ICdyZWQnfSkuYWRkVG8obWFwKTtcbiAqXG4gKiAvLyB6b29tIHRoZSBtYXAgdG8gdGhlIHBvbHlnb25cbiAqIG1hcC5maXRCb3VuZHMocG9seWdvbi5nZXRCb3VuZHMoKSk7XG4gKiBgYGBcbiAqXG4gKiBZb3UgY2FuIGFsc28gcGFzcyBhbiBhcnJheSBvZiBhcnJheXMgb2YgbGF0bG5ncywgd2l0aCB0aGUgZmlyc3QgYXJyYXkgcmVwcmVzZW50aW5nIHRoZSBvdXRlciBzaGFwZSBhbmQgdGhlIG90aGVyIGFycmF5cyByZXByZXNlbnRpbmcgaG9sZXMgaW4gdGhlIG91dGVyIHNoYXBlOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbGF0bG5ncyA9IFtcbiAqICAgW1stMTExLjAzLCA0MV0sWy0xMTEuMDQsIDQ1XSxbLTEwNC4wNSwgNDVdLFstMTA0LjA1LCA0MV1dLCAvLyBvdXRlciByaW5nXG4gKiAgIFtbLTEwOC41OCwzNy4yOV0sWy0xMDguNTgsNDAuNzFdLFstMTAyLjUwLDQwLjcxXSxbLTEwMi41MCwzNy4yOV1dIC8vIGhvbGVcbiAqIF07XG4gKiBgYGBcbiAqXG4gKiBBZGRpdGlvbmFsbHksIHlvdSBjYW4gcGFzcyBhIG11bHRpLWRpbWVuc2lvbmFsIGFycmF5IHRvIHJlcHJlc2VudCBhIE11bHRpUG9seWdvbiBzaGFwZS5cbiAqXG4gKiBgYGBqc1xuICogdmFyIGxhdGxuZ3MgPSBbXG4gKiAgIFsgLy8gZmlyc3QgcG9seWdvblxuICogICAgIFtbLTExMS4wMywgNDFdLFstMTExLjA0LCA0NV0sWy0xMDQuMDUsIDQ1XSxbLTEwNC4wNSwgNDFdXSwgLy8gb3V0ZXIgcmluZ1xuICogICAgIFtbLTEwOC41OCwzNy4yOV0sWy0xMDguNTgsNDAuNzFdLFstMTAyLjUwLDQwLjcxXSxbLTEwMi41MCwzNy4yOV1dIC8vIGhvbGVcbiAqICAgXSxcbiAqICAgWyAvLyBzZWNvbmQgcG9seWdvblxuICogICAgIFtbLTEwOS4wNSwgMzddLFstMTA5LjAzLCA0MV0sWy0xMDIuMDUsIDQxXSxbLTEwMi4wNCwgMzddLFstMTA5LjA1LCAzOF1dXG4gKiAgIF1cbiAqIF07XG4gKiBgYGBcbiAqL1xuXG5MLlBvbHlnb24gPSBMLlBvbHlsaW5lLmV4dGVuZCh7XG5cblx0b3B0aW9uczoge1xuXHRcdGZpbGw6IHRydWVcblx0fSxcblxuXHRpc0VtcHR5OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuICF0aGlzLl9sYXRsbmdzLmxlbmd0aCB8fCAhdGhpcy5fbGF0bG5nc1swXS5sZW5ndGg7XG5cdH0sXG5cblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gdGhyb3dzIGVycm9yIHdoZW4gbm90IHlldCBhZGRlZCB0byBtYXAgYXMgdGhpcyBjZW50ZXIgY2FsY3VsYXRpb24gcmVxdWlyZXMgcHJvamVjdGVkIGNvb3JkaW5hdGVzXG5cdFx0aWYgKCF0aGlzLl9tYXApIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcignTXVzdCBhZGQgbGF5ZXIgdG8gbWFwIGJlZm9yZSB1c2luZyBnZXRDZW50ZXIoKScpO1xuXHRcdH1cblxuXHRcdHZhciBpLCBqLCBwMSwgcDIsIGYsIGFyZWEsIHgsIHksIGNlbnRlcixcblx0XHQgICAgcG9pbnRzID0gdGhpcy5fcmluZ3NbMF0sXG5cdFx0ICAgIGxlbiA9IHBvaW50cy5sZW5ndGg7XG5cblx0XHRpZiAoIWxlbikgeyByZXR1cm4gbnVsbDsgfVxuXG5cdFx0Ly8gcG9seWdvbiBjZW50cm9pZCBhbGdvcml0aG07IG9ubHkgdXNlcyB0aGUgZmlyc3QgcmluZyBpZiB0aGVyZSBhcmUgbXVsdGlwbGVcblxuXHRcdGFyZWEgPSB4ID0geSA9IDA7XG5cblx0XHRmb3IgKGkgPSAwLCBqID0gbGVuIC0gMTsgaSA8IGxlbjsgaiA9IGkrKykge1xuXHRcdFx0cDEgPSBwb2ludHNbaV07XG5cdFx0XHRwMiA9IHBvaW50c1tqXTtcblxuXHRcdFx0ZiA9IHAxLnkgKiBwMi54IC0gcDIueSAqIHAxLng7XG5cdFx0XHR4ICs9IChwMS54ICsgcDIueCkgKiBmO1xuXHRcdFx0eSArPSAocDEueSArIHAyLnkpICogZjtcblx0XHRcdGFyZWEgKz0gZiAqIDM7XG5cdFx0fVxuXG5cdFx0aWYgKGFyZWEgPT09IDApIHtcblx0XHRcdC8vIFBvbHlnb24gaXMgc28gc21hbGwgdGhhdCBhbGwgcG9pbnRzIGFyZSBvbiBzYW1lIHBpeGVsLlxuXHRcdFx0Y2VudGVyID0gcG9pbnRzWzBdO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRjZW50ZXIgPSBbeCAvIGFyZWEsIHkgLyBhcmVhXTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcoY2VudGVyKTtcblx0fSxcblxuXHRfY29udmVydExhdExuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzKSB7XG5cdFx0dmFyIHJlc3VsdCA9IEwuUG9seWxpbmUucHJvdG90eXBlLl9jb252ZXJ0TGF0TG5ncy5jYWxsKHRoaXMsIGxhdGxuZ3MpLFxuXHRcdCAgICBsZW4gPSByZXN1bHQubGVuZ3RoO1xuXG5cdFx0Ly8gcmVtb3ZlIGxhc3QgcG9pbnQgaWYgaXQgZXF1YWxzIGZpcnN0IG9uZVxuXHRcdGlmIChsZW4gPj0gMiAmJiByZXN1bHRbMF0gaW5zdGFuY2VvZiBMLkxhdExuZyAmJiByZXN1bHRbMF0uZXF1YWxzKHJlc3VsdFtsZW4gLSAxXSkpIHtcblx0XHRcdHJlc3VsdC5wb3AoKTtcblx0XHR9XG5cdFx0cmV0dXJuIHJlc3VsdDtcblx0fSxcblxuXHRfc2V0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHRMLlBvbHlsaW5lLnByb3RvdHlwZS5fc2V0TGF0TG5ncy5jYWxsKHRoaXMsIGxhdGxuZ3MpO1xuXHRcdGlmIChMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3MpKSB7XG5cdFx0XHR0aGlzLl9sYXRsbmdzID0gW3RoaXMuX2xhdGxuZ3NdO1xuXHRcdH1cblx0fSxcblxuXHRfZGVmYXVsdFNoYXBlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIEwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5nc1swXSkgPyB0aGlzLl9sYXRsbmdzWzBdIDogdGhpcy5fbGF0bG5nc1swXVswXTtcblx0fSxcblxuXHRfY2xpcFBvaW50czogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHBvbHlnb25zIG5lZWQgYSBkaWZmZXJlbnQgY2xpcHBpbmcgYWxnb3JpdGhtIHNvIHdlIHJlZGVmaW5lIHRoYXRcblxuXHRcdHZhciBib3VuZHMgPSB0aGlzLl9yZW5kZXJlci5fYm91bmRzLFxuXHRcdCAgICB3ID0gdGhpcy5vcHRpb25zLndlaWdodCxcblx0XHQgICAgcCA9IG5ldyBMLlBvaW50KHcsIHcpO1xuXG5cdFx0Ly8gaW5jcmVhc2UgY2xpcCBwYWRkaW5nIGJ5IHN0cm9rZSB3aWR0aCB0byBhdm9pZCBzdHJva2Ugb24gY2xpcCBlZGdlc1xuXHRcdGJvdW5kcyA9IG5ldyBMLkJvdW5kcyhib3VuZHMubWluLnN1YnRyYWN0KHApLCBib3VuZHMubWF4LmFkZChwKSk7XG5cblx0XHR0aGlzLl9wYXJ0cyA9IFtdO1xuXHRcdGlmICghdGhpcy5fcHhCb3VuZHMgfHwgIXRoaXMuX3B4Qm91bmRzLmludGVyc2VjdHMoYm91bmRzKSkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLm9wdGlvbnMubm9DbGlwKSB7XG5cdFx0XHR0aGlzLl9wYXJ0cyA9IHRoaXMuX3JpbmdzO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0aGlzLl9yaW5ncy5sZW5ndGgsIGNsaXBwZWQ7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0Y2xpcHBlZCA9IEwuUG9seVV0aWwuY2xpcFBvbHlnb24odGhpcy5fcmluZ3NbaV0sIGJvdW5kcywgdHJ1ZSk7XG5cdFx0XHRpZiAoY2xpcHBlZC5sZW5ndGgpIHtcblx0XHRcdFx0dGhpcy5fcGFydHMucHVzaChjbGlwcGVkKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlUG9seSh0aGlzLCB0cnVlKTtcblx0fVxufSk7XG5cblxuLy8gQGZhY3RvcnkgTC5wb2x5Z29uKGxhdGxuZ3M6IExhdExuZ1tdLCBvcHRpb25zPzogUG9seWxpbmUgb3B0aW9ucylcbkwucG9seWdvbiA9IGZ1bmN0aW9uIChsYXRsbmdzLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5Qb2x5Z29uKGxhdGxuZ3MsIG9wdGlvbnMpO1xufTtcblxuXG5cbi8qXG4gKiBMLlJlY3RhbmdsZSBleHRlbmRzIFBvbHlnb24gYW5kIGNyZWF0ZXMgYSByZWN0YW5nbGUgd2hlbiBwYXNzZWQgYSBMYXRMbmdCb3VuZHMgb2JqZWN0LlxuICovXG5cbi8qXG4gKiBAY2xhc3MgUmVjdGFuZ2xlXG4gKiBAYWthIEwuUmV0YW5nbGVcbiAqIEBpbmhlcml0cyBQb2x5Z29uXG4gKlxuICogQSBjbGFzcyBmb3IgZHJhd2luZyByZWN0YW5nbGUgb3ZlcmxheXMgb24gYSBtYXAuIEV4dGVuZHMgYFBvbHlnb25gLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIC8vIGRlZmluZSByZWN0YW5nbGUgZ2VvZ3JhcGhpY2FsIGJvdW5kc1xuICogdmFyIGJvdW5kcyA9IFtbNTQuNTU5MzIyLCAtNS43Njc4MjJdLCBbNTYuMTIxMDYwNCwgLTMuMDIxMjQwXV07XG4gKlxuICogLy8gY3JlYXRlIGFuIG9yYW5nZSByZWN0YW5nbGVcbiAqIEwucmVjdGFuZ2xlKGJvdW5kcywge2NvbG9yOiBcIiNmZjc4MDBcIiwgd2VpZ2h0OiAxfSkuYWRkVG8obWFwKTtcbiAqXG4gKiAvLyB6b29tIHRoZSBtYXAgdG8gdGhlIHJlY3RhbmdsZSBib3VuZHNcbiAqIG1hcC5maXRCb3VuZHMoYm91bmRzKTtcbiAqIGBgYFxuICpcbiAqL1xuXG5cbkwuUmVjdGFuZ2xlID0gTC5Qb2x5Z29uLmV4dGVuZCh7XG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRMbmdCb3VuZHMsIG9wdGlvbnMpIHtcblx0XHRMLlBvbHlnb24ucHJvdG90eXBlLmluaXRpYWxpemUuY2FsbCh0aGlzLCB0aGlzLl9ib3VuZHNUb0xhdExuZ3MobGF0TG5nQm91bmRzKSwgb3B0aW9ucyk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRCb3VuZHMobGF0TG5nQm91bmRzOiBMYXRMbmdCb3VuZHMpOiB0aGlzXG5cdC8vIFJlZHJhd3MgdGhlIHJlY3RhbmdsZSB3aXRoIHRoZSBwYXNzZWQgYm91bmRzLlxuXHRzZXRCb3VuZHM6IGZ1bmN0aW9uIChsYXRMbmdCb3VuZHMpIHtcblx0XHRyZXR1cm4gdGhpcy5zZXRMYXRMbmdzKHRoaXMuX2JvdW5kc1RvTGF0TG5ncyhsYXRMbmdCb3VuZHMpKTtcblx0fSxcblxuXHRfYm91bmRzVG9MYXRMbmdzOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzKSB7XG5cdFx0bGF0TG5nQm91bmRzID0gTC5sYXRMbmdCb3VuZHMobGF0TG5nQm91bmRzKTtcblx0XHRyZXR1cm4gW1xuXHRcdFx0bGF0TG5nQm91bmRzLmdldFNvdXRoV2VzdCgpLFxuXHRcdFx0bGF0TG5nQm91bmRzLmdldE5vcnRoV2VzdCgpLFxuXHRcdFx0bGF0TG5nQm91bmRzLmdldE5vcnRoRWFzdCgpLFxuXHRcdFx0bGF0TG5nQm91bmRzLmdldFNvdXRoRWFzdCgpXG5cdFx0XTtcblx0fVxufSk7XG5cblxuLy8gQGZhY3RvcnkgTC5yZWN0YW5nbGUobGF0TG5nQm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM/OiBQb2x5bGluZSBvcHRpb25zKVxuTC5yZWN0YW5nbGUgPSBmdW5jdGlvbiAobGF0TG5nQm91bmRzLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5SZWN0YW5nbGUobGF0TG5nQm91bmRzLCBvcHRpb25zKTtcbn07XG5cblxuXG4vKlxuICogQGNsYXNzIENpcmNsZU1hcmtlclxuICogQGFrYSBMLkNpcmNsZU1hcmtlclxuICogQGluaGVyaXRzIFBhdGhcbiAqXG4gKiBBIGNpcmNsZSBvZiBhIGZpeGVkIHNpemUgd2l0aCByYWRpdXMgc3BlY2lmaWVkIGluIHBpeGVscy4gRXh0ZW5kcyBgUGF0aGAuXG4gKi9cblxuTC5DaXJjbGVNYXJrZXIgPSBMLlBhdGguZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIENpcmNsZU1hcmtlciBvcHRpb25zXG5cdG9wdGlvbnM6IHtcblx0XHRmaWxsOiB0cnVlLFxuXG5cdFx0Ly8gQG9wdGlvbiByYWRpdXM6IE51bWJlciA9IDEwXG5cdFx0Ly8gUmFkaXVzIG9mIHRoZSBjaXJjbGUgbWFya2VyLCBpbiBwaXhlbHNcblx0XHRyYWRpdXM6IDEwXG5cdH0sXG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGxhdGxuZywgb3B0aW9ucykge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0XHR0aGlzLl9sYXRsbmcgPSBMLmxhdExuZyhsYXRsbmcpO1xuXHRcdHRoaXMuX3JhZGl1cyA9IHRoaXMub3B0aW9ucy5yYWRpdXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRMYXRMbmcobGF0TG5nOiBMYXRMbmcpOiB0aGlzXG5cdC8vIFNldHMgdGhlIHBvc2l0aW9uIG9mIGEgY2lyY2xlIG1hcmtlciB0byBhIG5ldyBsb2NhdGlvbi5cblx0c2V0TGF0TG5nOiBmdW5jdGlvbiAobGF0bG5nKSB7XG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcblx0XHR0aGlzLnJlZHJhdygpO1xuXHRcdHJldHVybiB0aGlzLmZpcmUoJ21vdmUnLCB7bGF0bG5nOiB0aGlzLl9sYXRsbmd9KTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldExhdExuZygpOiBMYXRMbmdcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCBnZW9ncmFwaGljYWwgcG9zaXRpb24gb2YgdGhlIGNpcmNsZSBtYXJrZXJcblx0Z2V0TGF0TG5nOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2xhdGxuZztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldFJhZGl1cyhyYWRpdXM6IE51bWJlcik6IHRoaXNcblx0Ly8gU2V0cyB0aGUgcmFkaXVzIG9mIGEgY2lyY2xlIG1hcmtlci4gVW5pdHMgYXJlIGluIHBpeGVscy5cblx0c2V0UmFkaXVzOiBmdW5jdGlvbiAocmFkaXVzKSB7XG5cdFx0dGhpcy5vcHRpb25zLnJhZGl1cyA9IHRoaXMuX3JhZGl1cyA9IHJhZGl1cztcblx0XHRyZXR1cm4gdGhpcy5yZWRyYXcoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldFJhZGl1cygpOiBOdW1iZXJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCByYWRpdXMgb2YgdGhlIGNpcmNsZVxuXHRnZXRSYWRpdXM6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fcmFkaXVzO1xuXHR9LFxuXG5cdHNldFN0eWxlIDogZnVuY3Rpb24gKG9wdGlvbnMpIHtcblx0XHR2YXIgcmFkaXVzID0gb3B0aW9ucyAmJiBvcHRpb25zLnJhZGl1cyB8fCB0aGlzLl9yYWRpdXM7XG5cdFx0TC5QYXRoLnByb3RvdHlwZS5zZXRTdHlsZS5jYWxsKHRoaXMsIG9wdGlvbnMpO1xuXHRcdHRoaXMuc2V0UmFkaXVzKHJhZGl1cyk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X3Byb2plY3Q6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9wb2ludCA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fbGF0bG5nKTtcblx0XHR0aGlzLl91cGRhdGVCb3VuZHMoKTtcblx0fSxcblxuXHRfdXBkYXRlQm91bmRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIHIgPSB0aGlzLl9yYWRpdXMsXG5cdFx0ICAgIHIyID0gdGhpcy5fcmFkaXVzWSB8fCByLFxuXHRcdCAgICB3ID0gdGhpcy5fY2xpY2tUb2xlcmFuY2UoKSxcblx0XHQgICAgcCA9IFtyICsgdywgcjIgKyB3XTtcblx0XHR0aGlzLl9weEJvdW5kcyA9IG5ldyBMLkJvdW5kcyh0aGlzLl9wb2ludC5zdWJ0cmFjdChwKSwgdGhpcy5fcG9pbnQuYWRkKHApKTtcblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0dGhpcy5fdXBkYXRlUGF0aCgpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlUGF0aDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbmRlcmVyLl91cGRhdGVDaXJjbGUodGhpcyk7XG5cdH0sXG5cblx0X2VtcHR5OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3JhZGl1cyAmJiAhdGhpcy5fcmVuZGVyZXIuX2JvdW5kcy5pbnRlcnNlY3RzKHRoaXMuX3B4Qm91bmRzKTtcblx0fVxufSk7XG5cblxuLy8gQGZhY3RvcnkgTC5jaXJjbGVNYXJrZXIobGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBDaXJjbGVNYXJrZXIgb3B0aW9ucylcbi8vIEluc3RhbnRpYXRlcyBhIGNpcmNsZSBtYXJrZXIgb2JqZWN0IGdpdmVuIGEgZ2VvZ3JhcGhpY2FsIHBvaW50LCBhbmQgYW4gb3B0aW9uYWwgb3B0aW9ucyBvYmplY3QuXG5MLmNpcmNsZU1hcmtlciA9IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcblx0cmV0dXJuIG5ldyBMLkNpcmNsZU1hcmtlcihsYXRsbmcsIG9wdGlvbnMpO1xufTtcblxuXG5cbi8qXG4gKiBAY2xhc3MgQ2lyY2xlXG4gKiBAYWthIEwuQ2lyY2xlXG4gKiBAaW5oZXJpdHMgQ2lyY2xlTWFya2VyXG4gKlxuICogQSBjbGFzcyBmb3IgZHJhd2luZyBjaXJjbGUgb3ZlcmxheXMgb24gYSBtYXAuIEV4dGVuZHMgYENpcmNsZU1hcmtlcmAuXG4gKlxuICogSXQncyBhbiBhcHByb3hpbWF0aW9uIGFuZCBzdGFydHMgdG8gZGl2ZXJnZSBmcm9tIGEgcmVhbCBjaXJjbGUgY2xvc2VyIHRvIHBvbGVzIChkdWUgdG8gcHJvamVjdGlvbiBkaXN0b3J0aW9uKS5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiBMLmNpcmNsZShbNTAuNSwgMzAuNV0sIHtyYWRpdXM6IDIwMH0pLmFkZFRvKG1hcCk7XG4gKiBgYGBcbiAqL1xuXG5MLkNpcmNsZSA9IEwuQ2lyY2xlTWFya2VyLmV4dGVuZCh7XG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGxhdGxuZywgb3B0aW9ucywgbGVnYWN5T3B0aW9ucykge1xuXHRcdGlmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ251bWJlcicpIHtcblx0XHRcdC8vIEJhY2t3YXJkcyBjb21wYXRpYmlsaXR5IHdpdGggMC43LnggZmFjdG9yeSAobGF0bG5nLCByYWRpdXMsIG9wdGlvbnM/KVxuXHRcdFx0b3B0aW9ucyA9IEwuZXh0ZW5kKHt9LCBsZWdhY3lPcHRpb25zLCB7cmFkaXVzOiBvcHRpb25zfSk7XG5cdFx0fVxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0XHR0aGlzLl9sYXRsbmcgPSBMLmxhdExuZyhsYXRsbmcpO1xuXG5cdFx0aWYgKGlzTmFOKHRoaXMub3B0aW9ucy5yYWRpdXMpKSB7IHRocm93IG5ldyBFcnJvcignQ2lyY2xlIHJhZGl1cyBjYW5ub3QgYmUgTmFOJyk7IH1cblxuXHRcdC8vIEBzZWN0aW9uXG5cdFx0Ly8gQGFrYSBDaXJjbGUgb3B0aW9uc1xuXHRcdC8vIEBvcHRpb24gcmFkaXVzOiBOdW1iZXI7IFJhZGl1cyBvZiB0aGUgY2lyY2xlLCBpbiBtZXRlcnMuXG5cdFx0dGhpcy5fbVJhZGl1cyA9IHRoaXMub3B0aW9ucy5yYWRpdXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRSYWRpdXMocmFkaXVzOiBOdW1iZXIpOiB0aGlzXG5cdC8vIFNldHMgdGhlIHJhZGl1cyBvZiBhIGNpcmNsZS4gVW5pdHMgYXJlIGluIG1ldGVycy5cblx0c2V0UmFkaXVzOiBmdW5jdGlvbiAocmFkaXVzKSB7XG5cdFx0dGhpcy5fbVJhZGl1cyA9IHJhZGl1cztcblx0XHRyZXR1cm4gdGhpcy5yZWRyYXcoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldFJhZGl1cygpOiBOdW1iZXJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCByYWRpdXMgb2YgYSBjaXJjbGUuIFVuaXRzIGFyZSBpbiBtZXRlcnMuXG5cdGdldFJhZGl1czogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9tUmFkaXVzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xuXHQvLyBSZXR1cm5zIHRoZSBgTGF0TG5nQm91bmRzYCBvZiB0aGUgcGF0aC5cblx0Z2V0Qm91bmRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGhhbGYgPSBbdGhpcy5fcmFkaXVzLCB0aGlzLl9yYWRpdXNZIHx8IHRoaXMuX3JhZGl1c107XG5cblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nQm91bmRzKFxuXHRcdFx0dGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLl9wb2ludC5zdWJ0cmFjdChoYWxmKSksXG5cdFx0XHR0aGlzLl9tYXAubGF5ZXJQb2ludFRvTGF0TG5nKHRoaXMuX3BvaW50LmFkZChoYWxmKSkpO1xuXHR9LFxuXG5cdHNldFN0eWxlOiBMLlBhdGgucHJvdG90eXBlLnNldFN0eWxlLFxuXG5cdF9wcm9qZWN0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHR2YXIgbG5nID0gdGhpcy5fbGF0bG5nLmxuZyxcblx0XHQgICAgbGF0ID0gdGhpcy5fbGF0bG5nLmxhdCxcblx0XHQgICAgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBjcnMgPSBtYXAub3B0aW9ucy5jcnM7XG5cblx0XHRpZiAoY3JzLmRpc3RhbmNlID09PSBMLkNSUy5FYXJ0aC5kaXN0YW5jZSkge1xuXHRcdFx0dmFyIGQgPSBNYXRoLlBJIC8gMTgwLFxuXHRcdFx0ICAgIGxhdFIgPSAodGhpcy5fbVJhZGl1cyAvIEwuQ1JTLkVhcnRoLlIpIC8gZCxcblx0XHRcdCAgICB0b3AgPSBtYXAucHJvamVjdChbbGF0ICsgbGF0UiwgbG5nXSksXG5cdFx0XHQgICAgYm90dG9tID0gbWFwLnByb2plY3QoW2xhdCAtIGxhdFIsIGxuZ10pLFxuXHRcdFx0ICAgIHAgPSB0b3AuYWRkKGJvdHRvbSkuZGl2aWRlQnkoMiksXG5cdFx0XHQgICAgbGF0MiA9IG1hcC51bnByb2plY3QocCkubGF0LFxuXHRcdFx0ICAgIGxuZ1IgPSBNYXRoLmFjb3MoKE1hdGguY29zKGxhdFIgKiBkKSAtIE1hdGguc2luKGxhdCAqIGQpICogTWF0aC5zaW4obGF0MiAqIGQpKSAvXG5cdFx0XHQgICAgICAgICAgICAoTWF0aC5jb3MobGF0ICogZCkgKiBNYXRoLmNvcyhsYXQyICogZCkpKSAvIGQ7XG5cblx0XHRcdGlmIChpc05hTihsbmdSKSB8fCBsbmdSID09PSAwKSB7XG5cdFx0XHRcdGxuZ1IgPSBsYXRSIC8gTWF0aC5jb3MoTWF0aC5QSSAvIDE4MCAqIGxhdCk7IC8vIEZhbGxiYWNrIGZvciBlZGdlIGNhc2UsICMyNDI1XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX3BvaW50ID0gcC5zdWJ0cmFjdChtYXAuZ2V0UGl4ZWxPcmlnaW4oKSk7XG5cdFx0XHR0aGlzLl9yYWRpdXMgPSBpc05hTihsbmdSKSA/IDAgOiBNYXRoLm1heChNYXRoLnJvdW5kKHAueCAtIG1hcC5wcm9qZWN0KFtsYXQyLCBsbmcgLSBsbmdSXSkueCksIDEpO1xuXHRcdFx0dGhpcy5fcmFkaXVzWSA9IE1hdGgubWF4KE1hdGgucm91bmQocC55IC0gdG9wLnkpLCAxKTtcblxuXHRcdH0gZWxzZSB7XG5cdFx0XHR2YXIgbGF0bG5nMiA9IGNycy51bnByb2plY3QoY3JzLnByb2plY3QodGhpcy5fbGF0bG5nKS5zdWJ0cmFjdChbdGhpcy5fbVJhZGl1cywgMF0pKTtcblxuXHRcdFx0dGhpcy5fcG9pbnQgPSBtYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZyk7XG5cdFx0XHR0aGlzLl9yYWRpdXMgPSB0aGlzLl9wb2ludC54IC0gbWFwLmxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmcyKS54O1xuXHRcdH1cblxuXHRcdHRoaXMuX3VwZGF0ZUJvdW5kcygpO1xuXHR9XG59KTtcblxuLy8gQGZhY3RvcnkgTC5jaXJjbGUobGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBDaXJjbGUgb3B0aW9ucylcbi8vIEluc3RhbnRpYXRlcyBhIGNpcmNsZSBvYmplY3QgZ2l2ZW4gYSBnZW9ncmFwaGljYWwgcG9pbnQsIGFuZCBhbiBvcHRpb25zIG9iamVjdFxuLy8gd2hpY2ggY29udGFpbnMgdGhlIGNpcmNsZSByYWRpdXMuXG4vLyBAYWx0ZXJuYXRpdmVcbi8vIEBmYWN0b3J5IEwuY2lyY2xlKGxhdGxuZzogTGF0TG5nLCByYWRpdXM6IE51bWJlciwgb3B0aW9ucz86IENpcmNsZSBvcHRpb25zKVxuLy8gT2Jzb2xldGUgd2F5IG9mIGluc3RhbnRpYXRpbmcgYSBjaXJjbGUsIGZvciBjb21wYXRpYmlsaXR5IHdpdGggMC43LnggY29kZS5cbi8vIERvIG5vdCB1c2UgaW4gbmV3IGFwcGxpY2F0aW9ucyBvciBwbHVnaW5zLlxuTC5jaXJjbGUgPSBmdW5jdGlvbiAobGF0bG5nLCBvcHRpb25zLCBsZWdhY3lPcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5DaXJjbGUobGF0bG5nLCBvcHRpb25zLCBsZWdhY3lPcHRpb25zKTtcbn07XG5cblxuXG4vKlxuICogQGNsYXNzIFNWR1xuICogQGluaGVyaXRzIFJlbmRlcmVyXG4gKiBAYWthIEwuU1ZHXG4gKlxuICogQWxsb3dzIHZlY3RvciBsYXllcnMgdG8gYmUgZGlzcGxheWVkIHdpdGggW1NWR10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHKS5cbiAqIEluaGVyaXRzIGBSZW5kZXJlcmAuXG4gKlxuICogRHVlIHRvIFt0ZWNobmljYWwgbGltaXRhdGlvbnNdKGh0dHA6Ly9jYW5pdXNlLmNvbS8jc2VhcmNoPXN2ZyksIFNWRyBpcyBub3RcbiAqIGF2YWlsYWJsZSBpbiBhbGwgd2ViIGJyb3dzZXJzLCBub3RhYmx5IEFuZHJvaWQgMi54IGFuZCAzLnguXG4gKlxuICogQWx0aG91Z2ggU1ZHIGlzIG5vdCBhdmFpbGFibGUgb24gSUU3IGFuZCBJRTgsIHRoZXNlIGJyb3dzZXJzIHN1cHBvcnRcbiAqIFtWTUxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1ZlY3Rvcl9NYXJrdXBfTGFuZ3VhZ2UpXG4gKiAoYSBub3cgZGVwcmVjYXRlZCB0ZWNobm9sb2d5KSwgYW5kIHRoZSBTVkcgcmVuZGVyZXIgd2lsbCBmYWxsIGJhY2sgdG8gVk1MIGluXG4gKiB0aGlzIGNhc2UuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBVc2UgU1ZHIGJ5IGRlZmF1bHQgZm9yIGFsbCBwYXRocyBpbiB0aGUgbWFwOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcsIHtcbiAqIFx0cmVuZGVyZXI6IEwuc3ZnKClcbiAqIH0pO1xuICogYGBgXG4gKlxuICogVXNlIGEgU1ZHIHJlbmRlcmVyIHdpdGggZXh0cmEgcGFkZGluZyBmb3Igc3BlY2lmaWMgdmVjdG9yIGdlb21ldHJpZXM6XG4gKlxuICogYGBganNcbiAqIHZhciBtYXAgPSBMLm1hcCgnbWFwJyk7XG4gKiB2YXIgbXlSZW5kZXJlciA9IEwuc3ZnKHsgcGFkZGluZzogMC41IH0pO1xuICogdmFyIGxpbmUgPSBMLnBvbHlsaW5lKCBjb29yZGluYXRlcywgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiB2YXIgY2lyY2xlID0gTC5jaXJjbGUoIGNlbnRlciwgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiBgYGBcbiAqL1xuXG5MLlNWRyA9IEwuUmVuZGVyZXIuZXh0ZW5kKHtcblxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgZXZlbnRzID0gTC5SZW5kZXJlci5wcm90b3R5cGUuZ2V0RXZlbnRzLmNhbGwodGhpcyk7XG5cdFx0ZXZlbnRzLnpvb21zdGFydCA9IHRoaXMuX29uWm9vbVN0YXJ0O1xuXHRcdHJldHVybiBldmVudHM7XG5cdH0sXG5cblx0X2luaXRDb250YWluZXI6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9jb250YWluZXIgPSBMLlNWRy5jcmVhdGUoJ3N2ZycpO1xuXG5cdFx0Ly8gbWFrZXMgaXQgcG9zc2libGUgdG8gY2xpY2sgdGhyb3VnaCBzdmcgcm9vdDsgd2UnbGwgcmVzZXQgaXQgYmFjayBpbiBpbmRpdmlkdWFsIHBhdGhzXG5cdFx0dGhpcy5fY29udGFpbmVyLnNldEF0dHJpYnV0ZSgncG9pbnRlci1ldmVudHMnLCAnbm9uZScpO1xuXG5cdFx0dGhpcy5fcm9vdEdyb3VwID0gTC5TVkcuY3JlYXRlKCdnJyk7XG5cdFx0dGhpcy5fY29udGFpbmVyLmFwcGVuZENoaWxkKHRoaXMuX3Jvb3RHcm91cCk7XG5cdH0sXG5cblx0X29uWm9vbVN0YXJ0OiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gRHJhZy10aGVuLXBpbmNoIGludGVyYWN0aW9ucyBtaWdodCBtZXNzIHVwIHRoZSBjZW50ZXIgYW5kIHpvb20uXG5cdFx0Ly8gSW4gdGhpcyBjYXNlLCB0aGUgZWFzaWVzdCB3YXkgdG8gcHJldmVudCB0aGlzIGlzIHJlLWRvIHRoZSByZW5kZXJlclxuXHRcdC8vICAgYm91bmRzIGFuZCBwYWRkaW5nIHdoZW4gdGhlIHpvb21pbmcgc3RhcnRzLlxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwLl9hbmltYXRpbmdab29tICYmIHRoaXMuX2JvdW5kcykgeyByZXR1cm47IH1cblxuXHRcdEwuUmVuZGVyZXIucHJvdG90eXBlLl91cGRhdGUuY2FsbCh0aGlzKTtcblxuXHRcdHZhciBiID0gdGhpcy5fYm91bmRzLFxuXHRcdCAgICBzaXplID0gYi5nZXRTaXplKCksXG5cdFx0ICAgIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcjtcblxuXHRcdC8vIHNldCBzaXplIG9mIHN2Zy1jb250YWluZXIgaWYgY2hhbmdlZFxuXHRcdGlmICghdGhpcy5fc3ZnU2l6ZSB8fCAhdGhpcy5fc3ZnU2l6ZS5lcXVhbHMoc2l6ZSkpIHtcblx0XHRcdHRoaXMuX3N2Z1NpemUgPSBzaXplO1xuXHRcdFx0Y29udGFpbmVyLnNldEF0dHJpYnV0ZSgnd2lkdGgnLCBzaXplLngpO1xuXHRcdFx0Y29udGFpbmVyLnNldEF0dHJpYnV0ZSgnaGVpZ2h0Jywgc2l6ZS55KTtcblx0XHR9XG5cblx0XHQvLyBtb3ZlbWVudDogdXBkYXRlIGNvbnRhaW5lciB2aWV3Qm94IHNvIHRoYXQgd2UgZG9uJ3QgaGF2ZSB0byBjaGFuZ2UgY29vcmRpbmF0ZXMgb2YgaW5kaXZpZHVhbCBsYXllcnNcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24oY29udGFpbmVyLCBiLm1pbik7XG5cdFx0Y29udGFpbmVyLnNldEF0dHJpYnV0ZSgndmlld0JveCcsIFtiLm1pbi54LCBiLm1pbi55LCBzaXplLngsIHNpemUueV0uam9pbignICcpKTtcblxuXHRcdHRoaXMuZmlyZSgndXBkYXRlJyk7XG5cdH0sXG5cblx0Ly8gbWV0aG9kcyBiZWxvdyBhcmUgY2FsbGVkIGJ5IHZlY3RvciBsYXllcnMgaW1wbGVtZW50YXRpb25zXG5cblx0X2luaXRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcGF0aCA9IGxheWVyLl9wYXRoID0gTC5TVkcuY3JlYXRlKCdwYXRoJyk7XG5cblx0XHQvLyBAbmFtZXNwYWNlIFBhdGhcblx0XHQvLyBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gbnVsbFxuXHRcdC8vIEN1c3RvbSBjbGFzcyBuYW1lIHNldCBvbiBhbiBlbGVtZW50LiBPbmx5IGZvciBTVkcgcmVuZGVyZXIuXG5cdFx0aWYgKGxheWVyLm9wdGlvbnMuY2xhc3NOYW1lKSB7XG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MocGF0aCwgbGF5ZXIub3B0aW9ucy5jbGFzc05hbWUpO1xuXHRcdH1cblxuXHRcdGlmIChsYXllci5vcHRpb25zLmludGVyYWN0aXZlKSB7XG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MocGF0aCwgJ2xlYWZsZXQtaW50ZXJhY3RpdmUnKTtcblx0XHR9XG5cblx0XHR0aGlzLl91cGRhdGVTdHlsZShsYXllcik7XG5cdFx0dGhpcy5fbGF5ZXJzW0wuc3RhbXAobGF5ZXIpXSA9IGxheWVyO1xuXHR9LFxuXG5cdF9hZGRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0aGlzLl9yb290R3JvdXAuYXBwZW5kQ2hpbGQobGF5ZXIuX3BhdGgpO1xuXHRcdGxheWVyLmFkZEludGVyYWN0aXZlVGFyZ2V0KGxheWVyLl9wYXRoKTtcblx0fSxcblxuXHRfcmVtb3ZlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZShsYXllci5fcGF0aCk7XG5cdFx0bGF5ZXIucmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQobGF5ZXIuX3BhdGgpO1xuXHRcdGRlbGV0ZSB0aGlzLl9sYXllcnNbTC5zdGFtcChsYXllcildO1xuXHR9LFxuXG5cdF91cGRhdGVQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRsYXllci5fcHJvamVjdCgpO1xuXHRcdGxheWVyLl91cGRhdGUoKTtcblx0fSxcblxuXHRfdXBkYXRlU3R5bGU6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBwYXRoID0gbGF5ZXIuX3BhdGgsXG5cdFx0ICAgIG9wdGlvbnMgPSBsYXllci5vcHRpb25zO1xuXG5cdFx0aWYgKCFwYXRoKSB7IHJldHVybjsgfVxuXG5cdFx0aWYgKG9wdGlvbnMuc3Ryb2tlKSB7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlJywgb3B0aW9ucy5jb2xvcik7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLW9wYWNpdHknLCBvcHRpb25zLm9wYWNpdHkpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS13aWR0aCcsIG9wdGlvbnMud2VpZ2h0KTtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdzdHJva2UtbGluZWNhcCcsIG9wdGlvbnMubGluZUNhcCk7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLWxpbmVqb2luJywgb3B0aW9ucy5saW5lSm9pbik7XG5cblx0XHRcdGlmIChvcHRpb25zLmRhc2hBcnJheSkge1xuXHRcdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLWRhc2hhcnJheScsIG9wdGlvbnMuZGFzaEFycmF5KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHBhdGgucmVtb3ZlQXR0cmlidXRlKCdzdHJva2UtZGFzaGFycmF5Jyk7XG5cdFx0XHR9XG5cblx0XHRcdGlmIChvcHRpb25zLmRhc2hPZmZzZXQpIHtcblx0XHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS1kYXNob2Zmc2V0Jywgb3B0aW9ucy5kYXNoT2Zmc2V0KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHBhdGgucmVtb3ZlQXR0cmlidXRlKCdzdHJva2UtZGFzaG9mZnNldCcpO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlJywgJ25vbmUnKTtcblx0XHR9XG5cblx0XHRpZiAob3B0aW9ucy5maWxsKSB7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnZmlsbCcsIG9wdGlvbnMuZmlsbENvbG9yIHx8IG9wdGlvbnMuY29sb3IpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwtb3BhY2l0eScsIG9wdGlvbnMuZmlsbE9wYWNpdHkpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwtcnVsZScsIG9wdGlvbnMuZmlsbFJ1bGUgfHwgJ2V2ZW5vZGQnKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwnLCAnbm9uZScpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlUG9seTogZnVuY3Rpb24gKGxheWVyLCBjbG9zZWQpIHtcblx0XHR0aGlzLl9zZXRQYXRoKGxheWVyLCBMLlNWRy5wb2ludHNUb1BhdGgobGF5ZXIuX3BhcnRzLCBjbG9zZWQpKTtcblx0fSxcblxuXHRfdXBkYXRlQ2lyY2xlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcCA9IGxheWVyLl9wb2ludCxcblx0XHQgICAgciA9IGxheWVyLl9yYWRpdXMsXG5cdFx0ICAgIHIyID0gbGF5ZXIuX3JhZGl1c1kgfHwgcixcblx0XHQgICAgYXJjID0gJ2EnICsgciArICcsJyArIHIyICsgJyAwIDEsMCAnO1xuXG5cdFx0Ly8gZHJhd2luZyBhIGNpcmNsZSB3aXRoIHR3byBoYWxmLWFyY3Ncblx0XHR2YXIgZCA9IGxheWVyLl9lbXB0eSgpID8gJ00wIDAnIDpcblx0XHRcdFx0J00nICsgKHAueCAtIHIpICsgJywnICsgcC55ICtcblx0XHRcdFx0YXJjICsgKHIgKiAyKSArICcsMCAnICtcblx0XHRcdFx0YXJjICsgKC1yICogMikgKyAnLDAgJztcblxuXHRcdHRoaXMuX3NldFBhdGgobGF5ZXIsIGQpO1xuXHR9LFxuXG5cdF9zZXRQYXRoOiBmdW5jdGlvbiAobGF5ZXIsIHBhdGgpIHtcblx0XHRsYXllci5fcGF0aC5zZXRBdHRyaWJ1dGUoJ2QnLCBwYXRoKTtcblx0fSxcblxuXHQvLyBTVkcgZG9lcyBub3QgaGF2ZSB0aGUgY29uY2VwdCBvZiB6SW5kZXggc28gd2UgcmVzb3J0IHRvIGNoYW5naW5nIHRoZSBET00gb3JkZXIgb2YgZWxlbWVudHNcblx0X2JyaW5nVG9Gcm9udDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0TC5Eb21VdGlsLnRvRnJvbnQobGF5ZXIuX3BhdGgpO1xuXHR9LFxuXG5cdF9icmluZ1RvQmFjazogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0TC5Eb21VdGlsLnRvQmFjayhsYXllci5fcGF0aCk7XG5cdH1cbn0pO1xuXG5cbi8vIEBuYW1lc3BhY2UgU1ZHOyBAc2VjdGlvblxuLy8gVGhlcmUgYXJlIHNldmVyYWwgc3RhdGljIGZ1bmN0aW9ucyB3aGljaCBjYW4gYmUgY2FsbGVkIHdpdGhvdXQgaW5zdGFudGlhdGluZyBMLlNWRzpcbkwuZXh0ZW5kKEwuU1ZHLCB7XG5cdC8vIEBmdW5jdGlvbiBjcmVhdGUobmFtZTogU3RyaW5nKTogU1ZHRWxlbWVudFxuXHQvLyBSZXR1cm5zIGEgaW5zdGFuY2Ugb2YgW1NWR0VsZW1lbnRdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9TVkdFbGVtZW50KSxcblx0Ly8gY29ycmVzcG9uZGluZyB0byB0aGUgY2xhc3MgbmFtZSBwYXNzZWQuIEZvciBleGFtcGxlLCB1c2luZyAnbGluZScgd2lsbCByZXR1cm5cblx0Ly8gYW4gaW5zdGFuY2Ugb2YgW1NWR0xpbmVFbGVtZW50XShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvU1ZHTGluZUVsZW1lbnQpLlxuXHRjcmVhdGU6IGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUygnaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnLCBuYW1lKTtcblx0fSxcblxuXHQvLyBAZnVuY3Rpb24gcG9pbnRzVG9QYXRoKHJpbmdzOiBQb2ludFtdLCBjbG9zZWQ6IEJvb2xlYW4pOiBTdHJpbmdcblx0Ly8gR2VuZXJhdGVzIGEgU1ZHIHBhdGggc3RyaW5nIGZvciBtdWx0aXBsZSByaW5ncywgd2l0aCBlYWNoIHJpbmcgdHVybmluZ1xuXHQvLyBpbnRvIFwiTS4uTC4uTC4uXCIgaW5zdHJ1Y3Rpb25zXG5cdHBvaW50c1RvUGF0aDogZnVuY3Rpb24gKHJpbmdzLCBjbG9zZWQpIHtcblx0XHR2YXIgc3RyID0gJycsXG5cdFx0ICAgIGksIGosIGxlbiwgbGVuMiwgcG9pbnRzLCBwO1xuXG5cdFx0Zm9yIChpID0gMCwgbGVuID0gcmluZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdHBvaW50cyA9IHJpbmdzW2ldO1xuXG5cdFx0XHRmb3IgKGogPSAwLCBsZW4yID0gcG9pbnRzLmxlbmd0aDsgaiA8IGxlbjI7IGorKykge1xuXHRcdFx0XHRwID0gcG9pbnRzW2pdO1xuXHRcdFx0XHRzdHIgKz0gKGogPyAnTCcgOiAnTScpICsgcC54ICsgJyAnICsgcC55O1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBjbG9zZXMgdGhlIHJpbmcgZm9yIHBvbHlnb25zOyBcInhcIiBpcyBWTUwgc3ludGF4XG5cdFx0XHRzdHIgKz0gY2xvc2VkID8gKEwuQnJvd3Nlci5zdmcgPyAneicgOiAneCcpIDogJyc7XG5cdFx0fVxuXG5cdFx0Ly8gU1ZHIGNvbXBsYWlucyBhYm91dCBlbXB0eSBwYXRoIHN0cmluZ3Ncblx0XHRyZXR1cm4gc3RyIHx8ICdNMCAwJztcblx0fVxufSk7XG5cbi8vIEBuYW1lc3BhY2UgQnJvd3NlcjsgQHByb3BlcnR5IHN2ZzogQm9vbGVhblxuLy8gYHRydWVgIHdoZW4gdGhlIGJyb3dzZXIgc3VwcG9ydHMgW1NWR10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHKS5cbkwuQnJvd3Nlci5zdmcgPSAhIShkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMgJiYgTC5TVkcuY3JlYXRlKCdzdmcnKS5jcmVhdGVTVkdSZWN0KTtcblxuXG4vLyBAbmFtZXNwYWNlIFNWR1xuLy8gQGZhY3RvcnkgTC5zdmcob3B0aW9ucz86IFJlbmRlcmVyIG9wdGlvbnMpXG4vLyBDcmVhdGVzIGEgU1ZHIHJlbmRlcmVyIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXG5MLnN2ZyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdHJldHVybiBMLkJyb3dzZXIuc3ZnIHx8IEwuQnJvd3Nlci52bWwgPyBuZXcgTC5TVkcob3B0aW9ucykgOiBudWxsO1xufTtcblxuXG5cbi8qXG4gKiBUaGFua3MgdG8gRG1pdHJ5IEJhcmFub3Zza3kgYW5kIGhpcyBSYXBoYWVsIGxpYnJhcnkgZm9yIGluc3BpcmF0aW9uIVxuICovXG5cbi8qXG4gKiBAY2xhc3MgU1ZHXG4gKlxuICogQWx0aG91Z2ggU1ZHIGlzIG5vdCBhdmFpbGFibGUgb24gSUU3IGFuZCBJRTgsIHRoZXNlIGJyb3dzZXJzIHN1cHBvcnQgW1ZNTF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVmVjdG9yX01hcmt1cF9MYW5ndWFnZSksIGFuZCB0aGUgU1ZHIHJlbmRlcmVyIHdpbGwgZmFsbCBiYWNrIHRvIFZNTCBpbiB0aGlzIGNhc2UuXG4gKlxuICogVk1MIHdhcyBkZXByZWNhdGVkIGluIDIwMTIsIHdoaWNoIG1lYW5zIFZNTCBmdW5jdGlvbmFsaXR5IGV4aXN0cyBvbmx5IGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eVxuICogd2l0aCBvbGQgdmVyc2lvbnMgb2YgSW50ZXJuZXQgRXhwbG9yZXIuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBCcm93c2VyOyBAcHJvcGVydHkgdm1sOiBCb29sZWFuXG4vLyBgdHJ1ZWAgaWYgdGhlIGJyb3dzZXIgc3VwcG9ydHMgW1ZNTF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVmVjdG9yX01hcmt1cF9MYW5ndWFnZSkuXG5MLkJyb3dzZXIudm1sID0gIUwuQnJvd3Nlci5zdmcgJiYgKGZ1bmN0aW9uICgpIHtcblx0dHJ5IHtcblx0XHR2YXIgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG5cdFx0ZGl2LmlubmVySFRNTCA9ICc8djpzaGFwZSBhZGo9XCIxXCIvPic7XG5cblx0XHR2YXIgc2hhcGUgPSBkaXYuZmlyc3RDaGlsZDtcblx0XHRzaGFwZS5zdHlsZS5iZWhhdmlvciA9ICd1cmwoI2RlZmF1bHQjVk1MKSc7XG5cblx0XHRyZXR1cm4gc2hhcGUgJiYgKHR5cGVvZiBzaGFwZS5hZGogPT09ICdvYmplY3QnKTtcblxuXHR9IGNhdGNoIChlKSB7XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG59KCkpO1xuXG4vLyByZWRlZmluZSBzb21lIFNWRyBtZXRob2RzIHRvIGhhbmRsZSBWTUwgc3ludGF4IHdoaWNoIGlzIHNpbWlsYXIgYnV0IHdpdGggc29tZSBkaWZmZXJlbmNlc1xuTC5TVkcuaW5jbHVkZSghTC5Ccm93c2VyLnZtbCA/IHt9IDoge1xuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgJ2xlYWZsZXQtdm1sLWNvbnRhaW5lcicpO1xuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwLl9hbmltYXRpbmdab29tKSB7IHJldHVybjsgfVxuXHRcdEwuUmVuZGVyZXIucHJvdG90eXBlLl91cGRhdGUuY2FsbCh0aGlzKTtcblx0XHR0aGlzLmZpcmUoJ3VwZGF0ZScpO1xuXHR9LFxuXG5cdF9pbml0UGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IGxheWVyLl9jb250YWluZXIgPSBMLlNWRy5jcmVhdGUoJ3NoYXBlJyk7XG5cblx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC12bWwtc2hhcGUgJyArICh0aGlzLm9wdGlvbnMuY2xhc3NOYW1lIHx8ICcnKSk7XG5cblx0XHRjb250YWluZXIuY29vcmRzaXplID0gJzEgMSc7XG5cblx0XHRsYXllci5fcGF0aCA9IEwuU1ZHLmNyZWF0ZSgncGF0aCcpO1xuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChsYXllci5fcGF0aCk7XG5cblx0XHR0aGlzLl91cGRhdGVTdHlsZShsYXllcik7XG5cdH0sXG5cblx0X2FkZFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBjb250YWluZXIgPSBsYXllci5fY29udGFpbmVyO1xuXHRcdHRoaXMuX2NvbnRhaW5lci5hcHBlbmRDaGlsZChjb250YWluZXIpO1xuXG5cdFx0aWYgKGxheWVyLm9wdGlvbnMuaW50ZXJhY3RpdmUpIHtcblx0XHRcdGxheWVyLmFkZEludGVyYWN0aXZlVGFyZ2V0KGNvbnRhaW5lcik7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZW1vdmVQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgY29udGFpbmVyID0gbGF5ZXIuX2NvbnRhaW5lcjtcblx0XHRMLkRvbVV0aWwucmVtb3ZlKGNvbnRhaW5lcik7XG5cdFx0bGF5ZXIucmVtb3ZlSW50ZXJhY3RpdmVUYXJnZXQoY29udGFpbmVyKTtcblx0fSxcblxuXHRfdXBkYXRlU3R5bGU6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBzdHJva2UgPSBsYXllci5fc3Ryb2tlLFxuXHRcdCAgICBmaWxsID0gbGF5ZXIuX2ZpbGwsXG5cdFx0ICAgIG9wdGlvbnMgPSBsYXllci5vcHRpb25zLFxuXHRcdCAgICBjb250YWluZXIgPSBsYXllci5fY29udGFpbmVyO1xuXG5cdFx0Y29udGFpbmVyLnN0cm9rZWQgPSAhIW9wdGlvbnMuc3Ryb2tlO1xuXHRcdGNvbnRhaW5lci5maWxsZWQgPSAhIW9wdGlvbnMuZmlsbDtcblxuXHRcdGlmIChvcHRpb25zLnN0cm9rZSkge1xuXHRcdFx0aWYgKCFzdHJva2UpIHtcblx0XHRcdFx0c3Ryb2tlID0gbGF5ZXIuX3N0cm9rZSA9IEwuU1ZHLmNyZWF0ZSgnc3Ryb2tlJyk7XG5cdFx0XHR9XG5cdFx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQoc3Ryb2tlKTtcblx0XHRcdHN0cm9rZS53ZWlnaHQgPSBvcHRpb25zLndlaWdodCArICdweCc7XG5cdFx0XHRzdHJva2UuY29sb3IgPSBvcHRpb25zLmNvbG9yO1xuXHRcdFx0c3Ryb2tlLm9wYWNpdHkgPSBvcHRpb25zLm9wYWNpdHk7XG5cblx0XHRcdGlmIChvcHRpb25zLmRhc2hBcnJheSkge1xuXHRcdFx0XHRzdHJva2UuZGFzaFN0eWxlID0gTC5VdGlsLmlzQXJyYXkob3B0aW9ucy5kYXNoQXJyYXkpID9cblx0XHRcdFx0ICAgIG9wdGlvbnMuZGFzaEFycmF5LmpvaW4oJyAnKSA6XG5cdFx0XHRcdCAgICBvcHRpb25zLmRhc2hBcnJheS5yZXBsYWNlKC8oICosICopL2csICcgJyk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRzdHJva2UuZGFzaFN0eWxlID0gJyc7XG5cdFx0XHR9XG5cdFx0XHRzdHJva2UuZW5kY2FwID0gb3B0aW9ucy5saW5lQ2FwLnJlcGxhY2UoJ2J1dHQnLCAnZmxhdCcpO1xuXHRcdFx0c3Ryb2tlLmpvaW5zdHlsZSA9IG9wdGlvbnMubGluZUpvaW47XG5cblx0XHR9IGVsc2UgaWYgKHN0cm9rZSkge1xuXHRcdFx0Y29udGFpbmVyLnJlbW92ZUNoaWxkKHN0cm9rZSk7XG5cdFx0XHRsYXllci5fc3Ryb2tlID0gbnVsbDtcblx0XHR9XG5cblx0XHRpZiAob3B0aW9ucy5maWxsKSB7XG5cdFx0XHRpZiAoIWZpbGwpIHtcblx0XHRcdFx0ZmlsbCA9IGxheWVyLl9maWxsID0gTC5TVkcuY3JlYXRlKCdmaWxsJyk7XG5cdFx0XHR9XG5cdFx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQoZmlsbCk7XG5cdFx0XHRmaWxsLmNvbG9yID0gb3B0aW9ucy5maWxsQ29sb3IgfHwgb3B0aW9ucy5jb2xvcjtcblx0XHRcdGZpbGwub3BhY2l0eSA9IG9wdGlvbnMuZmlsbE9wYWNpdHk7XG5cblx0XHR9IGVsc2UgaWYgKGZpbGwpIHtcblx0XHRcdGNvbnRhaW5lci5yZW1vdmVDaGlsZChmaWxsKTtcblx0XHRcdGxheWVyLl9maWxsID0gbnVsbDtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZUNpcmNsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHAgPSBsYXllci5fcG9pbnQucm91bmQoKSxcblx0XHQgICAgciA9IE1hdGgucm91bmQobGF5ZXIuX3JhZGl1cyksXG5cdFx0ICAgIHIyID0gTWF0aC5yb3VuZChsYXllci5fcmFkaXVzWSB8fCByKTtcblxuXHRcdHRoaXMuX3NldFBhdGgobGF5ZXIsIGxheWVyLl9lbXB0eSgpID8gJ00wIDAnIDpcblx0XHRcdFx0J0FMICcgKyBwLnggKyAnLCcgKyBwLnkgKyAnICcgKyByICsgJywnICsgcjIgKyAnIDAsJyArICg2NTUzNSAqIDM2MCkpO1xuXHR9LFxuXG5cdF9zZXRQYXRoOiBmdW5jdGlvbiAobGF5ZXIsIHBhdGgpIHtcblx0XHRsYXllci5fcGF0aC52ID0gcGF0aDtcblx0fSxcblxuXHRfYnJpbmdUb0Zyb250OiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRMLkRvbVV0aWwudG9Gcm9udChsYXllci5fY29udGFpbmVyKTtcblx0fSxcblxuXHRfYnJpbmdUb0JhY2s6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC50b0JhY2sobGF5ZXIuX2NvbnRhaW5lcik7XG5cdH1cbn0pO1xuXG5pZiAoTC5Ccm93c2VyLnZtbCkge1xuXHRMLlNWRy5jcmVhdGUgPSAoZnVuY3Rpb24gKCkge1xuXHRcdHRyeSB7XG5cdFx0XHRkb2N1bWVudC5uYW1lc3BhY2VzLmFkZCgnbHZtbCcsICd1cm46c2NoZW1hcy1taWNyb3NvZnQtY29tOnZtbCcpO1xuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0XHRcdHJldHVybiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCc8bHZtbDonICsgbmFtZSArICcgY2xhc3M9XCJsdm1sXCI+Jyk7XG5cdFx0XHR9O1xuXHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdHJldHVybiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdFx0XHRyZXR1cm4gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnPCcgKyBuYW1lICsgJyB4bWxucz1cInVybjpzY2hlbWFzLW1pY3Jvc29mdC5jb206dm1sXCIgY2xhc3M9XCJsdm1sXCI+Jyk7XG5cdFx0XHR9O1xuXHRcdH1cblx0fSkoKTtcbn1cblxuXG5cbi8qXG4gKiBAY2xhc3MgQ2FudmFzXG4gKiBAaW5oZXJpdHMgUmVuZGVyZXJcbiAqIEBha2EgTC5DYW52YXNcbiAqXG4gKiBBbGxvd3MgdmVjdG9yIGxheWVycyB0byBiZSBkaXNwbGF5ZWQgd2l0aCBbYDxjYW52YXM+YF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0NhbnZhc19BUEkpLlxuICogSW5oZXJpdHMgYFJlbmRlcmVyYC5cbiAqXG4gKiBEdWUgdG8gW3RlY2huaWNhbCBsaW1pdGF0aW9uc10oaHR0cDovL2Nhbml1c2UuY29tLyNzZWFyY2g9Y2FudmFzKSwgQ2FudmFzIGlzIG5vdFxuICogYXZhaWxhYmxlIGluIGFsbCB3ZWIgYnJvd3NlcnMsIG5vdGFibHkgSUU4LCBhbmQgb3ZlcmxhcHBpbmcgZ2VvbWV0cmllcyBtaWdodFxuICogbm90IGRpc3BsYXkgcHJvcGVybHkgaW4gc29tZSBlZGdlIGNhc2VzLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogVXNlIENhbnZhcyBieSBkZWZhdWx0IGZvciBhbGwgcGF0aHMgaW4gdGhlIG1hcDpcbiAqXG4gKiBgYGBqc1xuICogdmFyIG1hcCA9IEwubWFwKCdtYXAnLCB7XG4gKiBcdHJlbmRlcmVyOiBMLmNhbnZhcygpXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqIFVzZSBhIENhbnZhcyByZW5kZXJlciB3aXRoIGV4dHJhIHBhZGRpbmcgZm9yIHNwZWNpZmljIHZlY3RvciBnZW9tZXRyaWVzOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcpO1xuICogdmFyIG15UmVuZGVyZXIgPSBMLmNhbnZhcyh7IHBhZGRpbmc6IDAuNSB9KTtcbiAqIHZhciBsaW5lID0gTC5wb2x5bGluZSggY29vcmRpbmF0ZXMsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogdmFyIGNpcmNsZSA9IEwuY2lyY2xlKCBjZW50ZXIsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogYGBgXG4gKi9cblxuTC5DYW52YXMgPSBMLlJlbmRlcmVyLmV4dGVuZCh7XG5cblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMpO1xuXG5cdFx0Ly8gUmVkcmF3IHZlY3RvcnMgc2luY2UgY2FudmFzIGlzIGNsZWFyZWQgdXBvbiByZW1vdmFsLFxuXHRcdC8vIGluIGNhc2Ugb2YgcmVtb3ZpbmcgdGhlIHJlbmRlcmVyIGl0c2VsZiBmcm9tIHRoZSBtYXAuXG5cdFx0dGhpcy5fZHJhdygpO1xuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuXG5cdFx0TC5Eb21FdmVudFxuXHRcdFx0Lm9uKGNvbnRhaW5lciwgJ21vdXNlbW92ZScsIEwuVXRpbC50aHJvdHRsZSh0aGlzLl9vbk1vdXNlTW92ZSwgMzIsIHRoaXMpLCB0aGlzKVxuXHRcdFx0Lm9uKGNvbnRhaW5lciwgJ2NsaWNrIGRibGNsaWNrIG1vdXNlZG93biBtb3VzZXVwIGNvbnRleHRtZW51JywgdGhpcy5fb25DbGljaywgdGhpcylcblx0XHRcdC5vbihjb250YWluZXIsICdtb3VzZW91dCcsIHRoaXMuX2hhbmRsZU1vdXNlT3V0LCB0aGlzKTtcblxuXHRcdHRoaXMuX2N0eCA9IGNvbnRhaW5lci5nZXRDb250ZXh0KCcyZCcpO1xuXHR9LFxuXG5cdF91cGRhdGVQYXRoczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBsYXllcjtcblx0XHR0aGlzLl9yZWRyYXdCb3VuZHMgPSBudWxsO1xuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0bGF5ZXIgPSB0aGlzLl9sYXllcnNbaWRdO1xuXHRcdFx0bGF5ZXIuX3VwZGF0ZSgpO1xuXHRcdH1cblx0XHR0aGlzLl9yZWRyYXcoKTtcblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcC5fYW5pbWF0aW5nWm9vbSAmJiB0aGlzLl9ib3VuZHMpIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9kcmF3bkxheWVycyA9IHt9O1xuXG5cdFx0TC5SZW5kZXJlci5wcm90b3R5cGUuX3VwZGF0ZS5jYWxsKHRoaXMpO1xuXG5cdFx0dmFyIGIgPSB0aGlzLl9ib3VuZHMsXG5cdFx0ICAgIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcixcblx0XHQgICAgc2l6ZSA9IGIuZ2V0U2l6ZSgpLFxuXHRcdCAgICBtID0gTC5Ccm93c2VyLnJldGluYSA/IDIgOiAxO1xuXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKGNvbnRhaW5lciwgYi5taW4pO1xuXG5cdFx0Ly8gc2V0IGNhbnZhcyBzaXplIChhbHNvIGNsZWFyaW5nIGl0KTsgdXNlIGRvdWJsZSBzaXplIG9uIHJldGluYVxuXHRcdGNvbnRhaW5lci53aWR0aCA9IG0gKiBzaXplLng7XG5cdFx0Y29udGFpbmVyLmhlaWdodCA9IG0gKiBzaXplLnk7XG5cdFx0Y29udGFpbmVyLnN0eWxlLndpZHRoID0gc2l6ZS54ICsgJ3B4Jztcblx0XHRjb250YWluZXIuc3R5bGUuaGVpZ2h0ID0gc2l6ZS55ICsgJ3B4JztcblxuXHRcdGlmIChMLkJyb3dzZXIucmV0aW5hKSB7XG5cdFx0XHR0aGlzLl9jdHguc2NhbGUoMiwgMik7XG5cdFx0fVxuXG5cdFx0Ly8gdHJhbnNsYXRlIHNvIHdlIHVzZSB0aGUgc2FtZSBwYXRoIGNvb3JkaW5hdGVzIGFmdGVyIGNhbnZhcyBlbGVtZW50IG1vdmVzXG5cdFx0dGhpcy5fY3R4LnRyYW5zbGF0ZSgtYi5taW4ueCwgLWIubWluLnkpO1xuXG5cdFx0Ly8gVGVsbCBwYXRocyB0byByZWRyYXcgdGhlbXNlbHZlc1xuXHRcdHRoaXMuZmlyZSgndXBkYXRlJyk7XG5cdH0sXG5cblx0X2luaXRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0aGlzLl91cGRhdGVEYXNoQXJyYXkobGF5ZXIpO1xuXHRcdHRoaXMuX2xheWVyc1tMLnN0YW1wKGxheWVyKV0gPSBsYXllcjtcblxuXHRcdHZhciBvcmRlciA9IGxheWVyLl9vcmRlciA9IHtcblx0XHRcdGxheWVyOiBsYXllcixcblx0XHRcdHByZXY6IHRoaXMuX2RyYXdMYXN0LFxuXHRcdFx0bmV4dDogbnVsbFxuXHRcdH07XG5cdFx0aWYgKHRoaXMuX2RyYXdMYXN0KSB7IHRoaXMuX2RyYXdMYXN0Lm5leHQgPSBvcmRlcjsgfVxuXHRcdHRoaXMuX2RyYXdMYXN0ID0gb3JkZXI7XG5cdFx0dGhpcy5fZHJhd0ZpcnN0ID0gdGhpcy5fZHJhd0ZpcnN0IHx8IHRoaXMuX2RyYXdMYXN0O1xuXHR9LFxuXG5cdF9hZGRQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fSxcblxuXHRfcmVtb3ZlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyO1xuXHRcdHZhciBuZXh0ID0gb3JkZXIubmV4dDtcblx0XHR2YXIgcHJldiA9IG9yZGVyLnByZXY7XG5cblx0XHRpZiAobmV4dCkge1xuXHRcdFx0bmV4dC5wcmV2ID0gcHJldjtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fZHJhd0xhc3QgPSBwcmV2O1xuXHRcdH1cblx0XHRpZiAocHJldikge1xuXHRcdFx0cHJldi5uZXh0ID0gbmV4dDtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fZHJhd0ZpcnN0ID0gbmV4dDtcblx0XHR9XG5cblx0XHRkZWxldGUgbGF5ZXIuX29yZGVyO1xuXG5cdFx0ZGVsZXRlIHRoaXMuX2xheWVyc1tMLnN0YW1wKGxheWVyKV07XG5cblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fSxcblxuXHRfdXBkYXRlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0Ly8gUmVkcmF3IHRoZSB1bmlvbiBvZiB0aGUgbGF5ZXIncyBvbGQgcGl4ZWxcblx0XHQvLyBib3VuZHMgYW5kIHRoZSBuZXcgcGl4ZWwgYm91bmRzLlxuXHRcdHRoaXMuX2V4dGVuZFJlZHJhd0JvdW5kcyhsYXllcik7XG5cdFx0bGF5ZXIuX3Byb2plY3QoKTtcblx0XHRsYXllci5fdXBkYXRlKCk7XG5cdFx0Ly8gVGhlIHJlZHJhdyB3aWxsIGV4dGVuZCB0aGUgcmVkcmF3IGJvdW5kc1xuXHRcdC8vIHdpdGggdGhlIG5ldyBwaXhlbCBib3VuZHMuXG5cdFx0dGhpcy5fcmVxdWVzdFJlZHJhdyhsYXllcik7XG5cdH0sXG5cblx0X3VwZGF0ZVN0eWxlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR0aGlzLl91cGRhdGVEYXNoQXJyYXkobGF5ZXIpO1xuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF91cGRhdGVEYXNoQXJyYXk6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdGlmIChsYXllci5vcHRpb25zLmRhc2hBcnJheSkge1xuXHRcdFx0dmFyIHBhcnRzID0gbGF5ZXIub3B0aW9ucy5kYXNoQXJyYXkuc3BsaXQoJywnKSxcblx0XHRcdCAgICBkYXNoQXJyYXkgPSBbXSxcblx0XHRcdCAgICBpO1xuXHRcdFx0Zm9yIChpID0gMDsgaSA8IHBhcnRzLmxlbmd0aDsgaSsrKSB7XG5cdFx0XHRcdGRhc2hBcnJheS5wdXNoKE51bWJlcihwYXJ0c1tpXSkpO1xuXHRcdFx0fVxuXHRcdFx0bGF5ZXIub3B0aW9ucy5fZGFzaEFycmF5ID0gZGFzaEFycmF5O1xuXHRcdH1cblx0fSxcblxuXHRfcmVxdWVzdFJlZHJhdzogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9leHRlbmRSZWRyYXdCb3VuZHMobGF5ZXIpO1xuXHRcdHRoaXMuX3JlZHJhd1JlcXVlc3QgPSB0aGlzLl9yZWRyYXdSZXF1ZXN0IHx8IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX3JlZHJhdywgdGhpcyk7XG5cdH0sXG5cblx0X2V4dGVuZFJlZHJhd0JvdW5kczogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHBhZGRpbmcgPSAobGF5ZXIub3B0aW9ucy53ZWlnaHQgfHwgMCkgKyAxO1xuXHRcdHRoaXMuX3JlZHJhd0JvdW5kcyA9IHRoaXMuX3JlZHJhd0JvdW5kcyB8fCBuZXcgTC5Cb3VuZHMoKTtcblx0XHR0aGlzLl9yZWRyYXdCb3VuZHMuZXh0ZW5kKGxheWVyLl9weEJvdW5kcy5taW4uc3VidHJhY3QoW3BhZGRpbmcsIHBhZGRpbmddKSk7XG5cdFx0dGhpcy5fcmVkcmF3Qm91bmRzLmV4dGVuZChsYXllci5fcHhCb3VuZHMubWF4LmFkZChbcGFkZGluZywgcGFkZGluZ10pKTtcblx0fSxcblxuXHRfcmVkcmF3OiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcmVkcmF3UmVxdWVzdCA9IG51bGw7XG5cblx0XHR0aGlzLl9jbGVhcigpOyAvLyBjbGVhciBsYXllcnMgaW4gcmVkcmF3IGJvdW5kc1xuXHRcdHRoaXMuX2RyYXcoKTsgLy8gZHJhdyBsYXllcnNcblxuXHRcdHRoaXMuX3JlZHJhd0JvdW5kcyA9IG51bGw7XG5cdH0sXG5cblx0X2NsZWFyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGJvdW5kcyA9IHRoaXMuX3JlZHJhd0JvdW5kcztcblx0XHRpZiAoYm91bmRzKSB7XG5cdFx0XHR2YXIgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XG5cdFx0XHR0aGlzLl9jdHguY2xlYXJSZWN0KGJvdW5kcy5taW4ueCwgYm91bmRzLm1pbi55LCBzaXplLngsIHNpemUueSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX2N0eC5jbGVhclJlY3QoMCwgMCwgdGhpcy5fY29udGFpbmVyLndpZHRoLCB0aGlzLl9jb250YWluZXIuaGVpZ2h0KTtcblx0XHR9XG5cdH0sXG5cblx0X2RyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbGF5ZXIsIGJvdW5kcyA9IHRoaXMuX3JlZHJhd0JvdW5kcztcblx0XHR0aGlzLl9jdHguc2F2ZSgpO1xuXHRcdGlmIChib3VuZHMpIHtcblx0XHRcdHZhciBzaXplID0gYm91bmRzLmdldFNpemUoKTtcblx0XHRcdHRoaXMuX2N0eC5iZWdpblBhdGgoKTtcblx0XHRcdHRoaXMuX2N0eC5yZWN0KGJvdW5kcy5taW4ueCwgYm91bmRzLm1pbi55LCBzaXplLngsIHNpemUueSk7XG5cdFx0XHR0aGlzLl9jdHguY2xpcCgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2RyYXdpbmcgPSB0cnVlO1xuXG5cdFx0Zm9yICh2YXIgb3JkZXIgPSB0aGlzLl9kcmF3Rmlyc3Q7IG9yZGVyOyBvcmRlciA9IG9yZGVyLm5leHQpIHtcblx0XHRcdGxheWVyID0gb3JkZXIubGF5ZXI7XG5cdFx0XHRpZiAoIWJvdW5kcyB8fCAobGF5ZXIuX3B4Qm91bmRzICYmIGxheWVyLl9weEJvdW5kcy5pbnRlcnNlY3RzKGJvdW5kcykpKSB7XG5cdFx0XHRcdGxheWVyLl91cGRhdGVQYXRoKCk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dGhpcy5fZHJhd2luZyA9IGZhbHNlO1xuXG5cdFx0dGhpcy5fY3R4LnJlc3RvcmUoKTsgIC8vIFJlc3RvcmUgc3RhdGUgYmVmb3JlIGNsaXBwaW5nLlxuXHR9LFxuXG5cdF91cGRhdGVQb2x5OiBmdW5jdGlvbiAobGF5ZXIsIGNsb3NlZCkge1xuXHRcdGlmICghdGhpcy5fZHJhd2luZykgeyByZXR1cm47IH1cblxuXHRcdHZhciBpLCBqLCBsZW4yLCBwLFxuXHRcdCAgICBwYXJ0cyA9IGxheWVyLl9wYXJ0cyxcblx0XHQgICAgbGVuID0gcGFydHMubGVuZ3RoLFxuXHRcdCAgICBjdHggPSB0aGlzLl9jdHg7XG5cblx0XHRpZiAoIWxlbikgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2RyYXduTGF5ZXJzW2xheWVyLl9sZWFmbGV0X2lkXSA9IGxheWVyO1xuXG5cdFx0Y3R4LmJlZ2luUGF0aCgpO1xuXG5cdFx0aWYgKGN0eC5zZXRMaW5lRGFzaCkge1xuXHRcdFx0Y3R4LnNldExpbmVEYXNoKGxheWVyLm9wdGlvbnMgJiYgbGF5ZXIub3B0aW9ucy5fZGFzaEFycmF5IHx8IFtdKTtcblx0XHR9XG5cblx0XHRmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGZvciAoaiA9IDAsIGxlbjIgPSBwYXJ0c1tpXS5sZW5ndGg7IGogPCBsZW4yOyBqKyspIHtcblx0XHRcdFx0cCA9IHBhcnRzW2ldW2pdO1xuXHRcdFx0XHRjdHhbaiA/ICdsaW5lVG8nIDogJ21vdmVUbyddKHAueCwgcC55KTtcblx0XHRcdH1cblx0XHRcdGlmIChjbG9zZWQpIHtcblx0XHRcdFx0Y3R4LmNsb3NlUGF0aCgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuX2ZpbGxTdHJva2UoY3R4LCBsYXllcik7XG5cblx0XHQvLyBUT0RPIG9wdGltaXphdGlvbjogMSBmaWxsL3N0cm9rZSBmb3IgYWxsIGZlYXR1cmVzIHdpdGggZXF1YWwgc3R5bGUgaW5zdGVhZCBvZiAxIGZvciBlYWNoIGZlYXR1cmVcblx0fSxcblxuXHRfdXBkYXRlQ2lyY2xlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblxuXHRcdGlmICghdGhpcy5fZHJhd2luZyB8fCBsYXllci5fZW1wdHkoKSkgeyByZXR1cm47IH1cblxuXHRcdHZhciBwID0gbGF5ZXIuX3BvaW50LFxuXHRcdCAgICBjdHggPSB0aGlzLl9jdHgsXG5cdFx0ICAgIHIgPSBsYXllci5fcmFkaXVzLFxuXHRcdCAgICBzID0gKGxheWVyLl9yYWRpdXNZIHx8IHIpIC8gcjtcblxuXHRcdHRoaXMuX2RyYXduTGF5ZXJzW2xheWVyLl9sZWFmbGV0X2lkXSA9IGxheWVyO1xuXG5cdFx0aWYgKHMgIT09IDEpIHtcblx0XHRcdGN0eC5zYXZlKCk7XG5cdFx0XHRjdHguc2NhbGUoMSwgcyk7XG5cdFx0fVxuXG5cdFx0Y3R4LmJlZ2luUGF0aCgpO1xuXHRcdGN0eC5hcmMocC54LCBwLnkgLyBzLCByLCAwLCBNYXRoLlBJICogMiwgZmFsc2UpO1xuXG5cdFx0aWYgKHMgIT09IDEpIHtcblx0XHRcdGN0eC5yZXN0b3JlKCk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fZmlsbFN0cm9rZShjdHgsIGxheWVyKTtcblx0fSxcblxuXHRfZmlsbFN0cm9rZTogZnVuY3Rpb24gKGN0eCwgbGF5ZXIpIHtcblx0XHR2YXIgb3B0aW9ucyA9IGxheWVyLm9wdGlvbnM7XG5cblx0XHRpZiAob3B0aW9ucy5maWxsKSB7XG5cdFx0XHRjdHguZ2xvYmFsQWxwaGEgPSBvcHRpb25zLmZpbGxPcGFjaXR5O1xuXHRcdFx0Y3R4LmZpbGxTdHlsZSA9IG9wdGlvbnMuZmlsbENvbG9yIHx8IG9wdGlvbnMuY29sb3I7XG5cdFx0XHRjdHguZmlsbChvcHRpb25zLmZpbGxSdWxlIHx8ICdldmVub2RkJyk7XG5cdFx0fVxuXG5cdFx0aWYgKG9wdGlvbnMuc3Ryb2tlICYmIG9wdGlvbnMud2VpZ2h0ICE9PSAwKSB7XG5cdFx0XHRjdHguZ2xvYmFsQWxwaGEgPSBvcHRpb25zLm9wYWNpdHk7XG5cdFx0XHRjdHgubGluZVdpZHRoID0gb3B0aW9ucy53ZWlnaHQ7XG5cdFx0XHRjdHguc3Ryb2tlU3R5bGUgPSBvcHRpb25zLmNvbG9yO1xuXHRcdFx0Y3R4LmxpbmVDYXAgPSBvcHRpb25zLmxpbmVDYXA7XG5cdFx0XHRjdHgubGluZUpvaW4gPSBvcHRpb25zLmxpbmVKb2luO1xuXHRcdFx0Y3R4LnN0cm9rZSgpO1xuXHRcdH1cblx0fSxcblxuXHQvLyBDYW52YXMgb2J2aW91c2x5IGRvZXNuJ3QgaGF2ZSBtb3VzZSBldmVudHMgZm9yIGluZGl2aWR1YWwgZHJhd24gb2JqZWN0cyxcblx0Ly8gc28gd2UgZW11bGF0ZSB0aGF0IGJ5IGNhbGN1bGF0aW5nIHdoYXQncyB1bmRlciB0aGUgbW91c2Ugb24gbW91c2Vtb3ZlL2NsaWNrIG1hbnVhbGx5XG5cblx0X29uQ2xpY2s6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIHBvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0xheWVyUG9pbnQoZSksIGxheWVyLCBjbGlja2VkTGF5ZXI7XG5cblx0XHRmb3IgKHZhciBvcmRlciA9IHRoaXMuX2RyYXdGaXJzdDsgb3JkZXI7IG9yZGVyID0gb3JkZXIubmV4dCkge1xuXHRcdFx0bGF5ZXIgPSBvcmRlci5sYXllcjtcblx0XHRcdGlmIChsYXllci5vcHRpb25zLmludGVyYWN0aXZlICYmIGxheWVyLl9jb250YWluc1BvaW50KHBvaW50KSAmJiAhdGhpcy5fbWFwLl9kcmFnZ2FibGVNb3ZlZChsYXllcikpIHtcblx0XHRcdFx0Y2xpY2tlZExheWVyID0gbGF5ZXI7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGlmIChjbGlja2VkTGF5ZXIpICB7XG5cdFx0XHRMLkRvbUV2ZW50Ll9mYWtlU3RvcChlKTtcblx0XHRcdHRoaXMuX2ZpcmVFdmVudChbY2xpY2tlZExheWVyXSwgZSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9vbk1vdXNlTW92ZTogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIXRoaXMuX21hcCB8fCB0aGlzLl9tYXAuZHJhZ2dpbmcubW92aW5nKCkgfHwgdGhpcy5fbWFwLl9hbmltYXRpbmdab29tKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIHBvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0xheWVyUG9pbnQoZSk7XG5cdFx0dGhpcy5faGFuZGxlTW91c2VIb3ZlcihlLCBwb2ludCk7XG5cdH0sXG5cblxuXHRfaGFuZGxlTW91c2VPdXQ6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGxheWVyID0gdGhpcy5faG92ZXJlZExheWVyO1xuXHRcdGlmIChsYXllcikge1xuXHRcdFx0Ly8gaWYgd2UncmUgbGVhdmluZyB0aGUgbGF5ZXIsIGZpcmUgbW91c2VvdXRcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWludGVyYWN0aXZlJyk7XG5cdFx0XHR0aGlzLl9maXJlRXZlbnQoW2xheWVyXSwgZSwgJ21vdXNlb3V0Jyk7XG5cdFx0XHR0aGlzLl9ob3ZlcmVkTGF5ZXIgPSBudWxsO1xuXHRcdH1cblx0fSxcblxuXHRfaGFuZGxlTW91c2VIb3ZlcjogZnVuY3Rpb24gKGUsIHBvaW50KSB7XG5cdFx0dmFyIGxheWVyLCBjYW5kaWRhdGVIb3ZlcmVkTGF5ZXI7XG5cblx0XHRmb3IgKHZhciBvcmRlciA9IHRoaXMuX2RyYXdGaXJzdDsgb3JkZXI7IG9yZGVyID0gb3JkZXIubmV4dCkge1xuXHRcdFx0bGF5ZXIgPSBvcmRlci5sYXllcjtcblx0XHRcdGlmIChsYXllci5vcHRpb25zLmludGVyYWN0aXZlICYmIGxheWVyLl9jb250YWluc1BvaW50KHBvaW50KSkge1xuXHRcdFx0XHRjYW5kaWRhdGVIb3ZlcmVkTGF5ZXIgPSBsYXllcjtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoY2FuZGlkYXRlSG92ZXJlZExheWVyICE9PSB0aGlzLl9ob3ZlcmVkTGF5ZXIpIHtcblx0XHRcdHRoaXMuX2hhbmRsZU1vdXNlT3V0KGUpO1xuXG5cdFx0XHRpZiAoY2FuZGlkYXRlSG92ZXJlZExheWVyKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWludGVyYWN0aXZlJyk7IC8vIGNoYW5nZSBjdXJzb3Jcblx0XHRcdFx0dGhpcy5fZmlyZUV2ZW50KFtjYW5kaWRhdGVIb3ZlcmVkTGF5ZXJdLCBlLCAnbW91c2VvdmVyJyk7XG5cdFx0XHRcdHRoaXMuX2hvdmVyZWRMYXllciA9IGNhbmRpZGF0ZUhvdmVyZWRMYXllcjtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAodGhpcy5faG92ZXJlZExheWVyKSB7XG5cdFx0XHR0aGlzLl9maXJlRXZlbnQoW3RoaXMuX2hvdmVyZWRMYXllcl0sIGUpO1xuXHRcdH1cblx0fSxcblxuXHRfZmlyZUV2ZW50OiBmdW5jdGlvbiAobGF5ZXJzLCBlLCB0eXBlKSB7XG5cdFx0dGhpcy5fbWFwLl9maXJlRE9NRXZlbnQoZSwgdHlwZSB8fCBlLnR5cGUsIGxheWVycyk7XG5cdH0sXG5cblx0X2JyaW5nVG9Gcm9udDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyO1xuXHRcdHZhciBuZXh0ID0gb3JkZXIubmV4dDtcblx0XHR2YXIgcHJldiA9IG9yZGVyLnByZXY7XG5cblx0XHRpZiAobmV4dCkge1xuXHRcdFx0bmV4dC5wcmV2ID0gcHJldjtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gQWxyZWFkeSBsYXN0XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGlmIChwcmV2KSB7XG5cdFx0XHRwcmV2Lm5leHQgPSBuZXh0O1xuXHRcdH0gZWxzZSBpZiAobmV4dCkge1xuXHRcdFx0Ly8gVXBkYXRlIGZpcnN0IGVudHJ5IHVubGVzcyB0aGlzIGlzIHRoZVxuXHRcdFx0Ly8gc2lnbmxlIGVudHJ5XG5cdFx0XHR0aGlzLl9kcmF3Rmlyc3QgPSBuZXh0O1xuXHRcdH1cblxuXHRcdG9yZGVyLnByZXYgPSB0aGlzLl9kcmF3TGFzdDtcblx0XHR0aGlzLl9kcmF3TGFzdC5uZXh0ID0gb3JkZXI7XG5cblx0XHRvcmRlci5uZXh0ID0gbnVsbDtcblx0XHR0aGlzLl9kcmF3TGFzdCA9IG9yZGVyO1xuXG5cdFx0dGhpcy5fcmVxdWVzdFJlZHJhdyhsYXllcik7XG5cdH0sXG5cblx0X2JyaW5nVG9CYWNrOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgb3JkZXIgPSBsYXllci5fb3JkZXI7XG5cdFx0dmFyIG5leHQgPSBvcmRlci5uZXh0O1xuXHRcdHZhciBwcmV2ID0gb3JkZXIucHJldjtcblxuXHRcdGlmIChwcmV2KSB7XG5cdFx0XHRwcmV2Lm5leHQgPSBuZXh0O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBBbHJlYWR5IGZpcnN0XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRuZXh0LnByZXYgPSBwcmV2O1xuXHRcdH0gZWxzZSBpZiAocHJldikge1xuXHRcdFx0Ly8gVXBkYXRlIGxhc3QgZW50cnkgdW5sZXNzIHRoaXMgaXMgdGhlXG5cdFx0XHQvLyBzaWdubGUgZW50cnlcblx0XHRcdHRoaXMuX2RyYXdMYXN0ID0gcHJldjtcblx0XHR9XG5cblx0XHRvcmRlci5wcmV2ID0gbnVsbDtcblxuXHRcdG9yZGVyLm5leHQgPSB0aGlzLl9kcmF3Rmlyc3Q7XG5cdFx0dGhpcy5fZHJhd0ZpcnN0LnByZXYgPSBvcmRlcjtcblx0XHR0aGlzLl9kcmF3Rmlyc3QgPSBvcmRlcjtcblxuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9XG59KTtcblxuLy8gQG5hbWVzcGFjZSBCcm93c2VyOyBAcHJvcGVydHkgY2FudmFzOiBCb29sZWFuXG4vLyBgdHJ1ZWAgd2hlbiB0aGUgYnJvd3NlciBzdXBwb3J0cyBbYDxjYW52YXM+YF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0NhbnZhc19BUEkpLlxuTC5Ccm93c2VyLmNhbnZhcyA9IChmdW5jdGlvbiAoKSB7XG5cdHJldHVybiAhIWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpLmdldENvbnRleHQ7XG59KCkpO1xuXG4vLyBAbmFtZXNwYWNlIENhbnZhc1xuLy8gQGZhY3RvcnkgTC5jYW52YXMob3B0aW9ucz86IFJlbmRlcmVyIG9wdGlvbnMpXG4vLyBDcmVhdGVzIGEgQ2FudmFzIHJlbmRlcmVyIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXG5MLmNhbnZhcyA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdHJldHVybiBMLkJyb3dzZXIuY2FudmFzID8gbmV3IEwuQ2FudmFzKG9wdGlvbnMpIDogbnVsbDtcbn07XG5cbkwuUG9seWxpbmUucHJvdG90eXBlLl9jb250YWluc1BvaW50ID0gZnVuY3Rpb24gKHAsIGNsb3NlZCkge1xuXHR2YXIgaSwgaiwgaywgbGVuLCBsZW4yLCBwYXJ0LFxuXHQgICAgdyA9IHRoaXMuX2NsaWNrVG9sZXJhbmNlKCk7XG5cblx0aWYgKCF0aGlzLl9weEJvdW5kcy5jb250YWlucyhwKSkgeyByZXR1cm4gZmFsc2U7IH1cblxuXHQvLyBoaXQgZGV0ZWN0aW9uIGZvciBwb2x5bGluZXNcblx0Zm9yIChpID0gMCwgbGVuID0gdGhpcy5fcGFydHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRwYXJ0ID0gdGhpcy5fcGFydHNbaV07XG5cblx0XHRmb3IgKGogPSAwLCBsZW4yID0gcGFydC5sZW5ndGgsIGsgPSBsZW4yIC0gMTsgaiA8IGxlbjI7IGsgPSBqKyspIHtcblx0XHRcdGlmICghY2xvc2VkICYmIChqID09PSAwKSkgeyBjb250aW51ZTsgfVxuXG5cdFx0XHRpZiAoTC5MaW5lVXRpbC5wb2ludFRvU2VnbWVudERpc3RhbmNlKHAsIHBhcnRba10sIHBhcnRbal0pIDw9IHcpIHtcblx0XHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cdHJldHVybiBmYWxzZTtcbn07XG5cbkwuUG9seWdvbi5wcm90b3R5cGUuX2NvbnRhaW5zUG9pbnQgPSBmdW5jdGlvbiAocCkge1xuXHR2YXIgaW5zaWRlID0gZmFsc2UsXG5cdCAgICBwYXJ0LCBwMSwgcDIsIGksIGosIGssIGxlbiwgbGVuMjtcblxuXHRpZiAoIXRoaXMuX3B4Qm91bmRzLmNvbnRhaW5zKHApKSB7IHJldHVybiBmYWxzZTsgfVxuXG5cdC8vIHJheSBjYXN0aW5nIGFsZ29yaXRobSBmb3IgZGV0ZWN0aW5nIGlmIHBvaW50IGlzIGluIHBvbHlnb25cblx0Zm9yIChpID0gMCwgbGVuID0gdGhpcy5fcGFydHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRwYXJ0ID0gdGhpcy5fcGFydHNbaV07XG5cblx0XHRmb3IgKGogPSAwLCBsZW4yID0gcGFydC5sZW5ndGgsIGsgPSBsZW4yIC0gMTsgaiA8IGxlbjI7IGsgPSBqKyspIHtcblx0XHRcdHAxID0gcGFydFtqXTtcblx0XHRcdHAyID0gcGFydFtrXTtcblxuXHRcdFx0aWYgKCgocDEueSA+IHAueSkgIT09IChwMi55ID4gcC55KSkgJiYgKHAueCA8IChwMi54IC0gcDEueCkgKiAocC55IC0gcDEueSkgLyAocDIueSAtIHAxLnkpICsgcDEueCkpIHtcblx0XHRcdFx0aW5zaWRlID0gIWluc2lkZTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHQvLyBhbHNvIGNoZWNrIGlmIGl0J3Mgb24gcG9seWdvbiBzdHJva2Vcblx0cmV0dXJuIGluc2lkZSB8fCBMLlBvbHlsaW5lLnByb3RvdHlwZS5fY29udGFpbnNQb2ludC5jYWxsKHRoaXMsIHAsIHRydWUpO1xufTtcblxuTC5DaXJjbGVNYXJrZXIucHJvdG90eXBlLl9jb250YWluc1BvaW50ID0gZnVuY3Rpb24gKHApIHtcblx0cmV0dXJuIHAuZGlzdGFuY2VUbyh0aGlzLl9wb2ludCkgPD0gdGhpcy5fcmFkaXVzICsgdGhpcy5fY2xpY2tUb2xlcmFuY2UoKTtcbn07XG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgR2VvSlNPTlxyXG4gKiBAYWthIEwuR2VvSlNPTlxyXG4gKiBAaW5oZXJpdHMgRmVhdHVyZUdyb3VwXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYSBHZW9KU09OIG9iamVjdCBvciBhbiBhcnJheSBvZiBHZW9KU09OIG9iamVjdHMuIEFsbG93cyB5b3UgdG8gcGFyc2VcclxuICogR2VvSlNPTiBkYXRhIGFuZCBkaXNwbGF5IGl0IG9uIHRoZSBtYXAuIEV4dGVuZHMgYEZlYXR1cmVHcm91cGAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIEwuZ2VvSlNPTihkYXRhLCB7XHJcbiAqIFx0c3R5bGU6IGZ1bmN0aW9uIChmZWF0dXJlKSB7XHJcbiAqIFx0XHRyZXR1cm4ge2NvbG9yOiBmZWF0dXJlLnByb3BlcnRpZXMuY29sb3J9O1xyXG4gKiBcdH1cclxuICogfSkuYmluZFBvcHVwKGZ1bmN0aW9uIChsYXllcikge1xyXG4gKiBcdHJldHVybiBsYXllci5mZWF0dXJlLnByb3BlcnRpZXMuZGVzY3JpcHRpb247XHJcbiAqIH0pLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuR2VvSlNPTiA9IEwuRmVhdHVyZUdyb3VwLmV4dGVuZCh7XHJcblxyXG5cdC8qIEBzZWN0aW9uXHJcblx0ICogQGFrYSBHZW9KU09OIG9wdGlvbnNcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gcG9pbnRUb0xheWVyOiBGdW5jdGlvbiA9ICpcclxuXHQgKiBBIGBGdW5jdGlvbmAgZGVmaW5pbmcgaG93IEdlb0pTT04gcG9pbnRzIHNwYXduIExlYWZsZXQgbGF5ZXJzLiBJdCBpcyBpbnRlcm5hbGx5XHJcblx0ICogY2FsbGVkIHdoZW4gZGF0YSBpcyBhZGRlZCwgcGFzc2luZyB0aGUgR2VvSlNPTiBwb2ludCBmZWF0dXJlIGFuZCBpdHMgYExhdExuZ2AuXHJcblx0ICogVGhlIGRlZmF1bHQgaXMgdG8gc3Bhd24gYSBkZWZhdWx0IGBNYXJrZXJgOlxyXG5cdCAqIGBgYGpzXHJcblx0ICogZnVuY3Rpb24oZ2VvSnNvblBvaW50LCBsYXRsbmcpIHtcclxuXHQgKiBcdHJldHVybiBMLm1hcmtlcihsYXRsbmcpO1xyXG5cdCAqIH1cclxuXHQgKiBgYGBcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gc3R5bGU6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCBkZWZpbmluZyB0aGUgYFBhdGggb3B0aW9uc2AgZm9yIHN0eWxpbmcgR2VvSlNPTiBsaW5lcyBhbmQgcG9seWdvbnMsXHJcblx0ICogY2FsbGVkIGludGVybmFsbHkgd2hlbiBkYXRhIGlzIGFkZGVkLlxyXG5cdCAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIHRvIG5vdCBvdmVycmlkZSBhbnkgZGVmYXVsdHM6XHJcblx0ICogYGBganNcclxuXHQgKiBmdW5jdGlvbiAoZ2VvSnNvbkZlYXR1cmUpIHtcclxuXHQgKiBcdHJldHVybiB7fVxyXG5cdCAqIH1cclxuXHQgKiBgYGBcclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gb25FYWNoRmVhdHVyZTogRnVuY3Rpb24gPSAqXHJcblx0ICogQSBgRnVuY3Rpb25gIHRoYXQgd2lsbCBiZSBjYWxsZWQgb25jZSBmb3IgZWFjaCBjcmVhdGVkIGBGZWF0dXJlYCwgYWZ0ZXIgaXQgaGFzXHJcblx0ICogYmVlbiBjcmVhdGVkIGFuZCBzdHlsZWQuIFVzZWZ1bCBmb3IgYXR0YWNoaW5nIGV2ZW50cyBhbmQgcG9wdXBzIHRvIGZlYXR1cmVzLlxyXG5cdCAqIFRoZSBkZWZhdWx0IGlzIHRvIGRvIG5vdGhpbmcgd2l0aCB0aGUgbmV3bHkgY3JlYXRlZCBsYXllcnM6XHJcblx0ICogYGBganNcclxuXHQgKiBmdW5jdGlvbiAoZmVhdHVyZSwgbGF5ZXIpIHt9XHJcblx0ICogYGBgXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIGZpbHRlcjogRnVuY3Rpb24gPSAqXHJcblx0ICogQSBgRnVuY3Rpb25gIHRoYXQgd2lsbCBiZSB1c2VkIHRvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgYSBmZWF0dXJlIG9yIG5vdC5cclxuXHQgKiBUaGUgZGVmYXVsdCBpcyB0byBpbmNsdWRlIGFsbCBmZWF0dXJlczpcclxuXHQgKiBgYGBqc1xyXG5cdCAqIGZ1bmN0aW9uIChnZW9Kc29uRmVhdHVyZSkge1xyXG5cdCAqIFx0cmV0dXJuIHRydWU7XHJcblx0ICogfVxyXG5cdCAqIGBgYFxyXG5cdCAqIE5vdGU6IGR5bmFtaWNhbGx5IGNoYW5naW5nIHRoZSBgZmlsdGVyYCBvcHRpb24gd2lsbCBoYXZlIGVmZmVjdCBvbmx5IG9uIG5ld2x5XHJcblx0ICogYWRkZWQgZGF0YS4gSXQgd2lsbCBfbm90XyByZS1ldmFsdWF0ZSBhbHJlYWR5IGluY2x1ZGVkIGZlYXR1cmVzLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBjb29yZHNUb0xhdExuZzogRnVuY3Rpb24gPSAqXHJcblx0ICogQSBgRnVuY3Rpb25gIHRoYXQgd2lsbCBiZSB1c2VkIGZvciBjb252ZXJ0aW5nIEdlb0pTT04gY29vcmRpbmF0ZXMgdG8gYExhdExuZ2BzLlxyXG5cdCAqIFRoZSBkZWZhdWx0IGlzIHRoZSBgY29vcmRzVG9MYXRMbmdgIHN0YXRpYyBtZXRob2QuXHJcblx0ICovXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChnZW9qc29uLCBvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJzID0ge307XHJcblxyXG5cdFx0aWYgKGdlb2pzb24pIHtcclxuXHRcdFx0dGhpcy5hZGREYXRhKGdlb2pzb24pO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkRGF0YSggPEdlb0pTT04+IGRhdGEgKTogdGhpc1xyXG5cdC8vIEFkZHMgYSBHZW9KU09OIG9iamVjdCB0byB0aGUgbGF5ZXIuXHJcblx0YWRkRGF0YTogZnVuY3Rpb24gKGdlb2pzb24pIHtcclxuXHRcdHZhciBmZWF0dXJlcyA9IEwuVXRpbC5pc0FycmF5KGdlb2pzb24pID8gZ2VvanNvbiA6IGdlb2pzb24uZmVhdHVyZXMsXHJcblx0XHQgICAgaSwgbGVuLCBmZWF0dXJlO1xyXG5cclxuXHRcdGlmIChmZWF0dXJlcykge1xyXG5cdFx0XHRmb3IgKGkgPSAwLCBsZW4gPSBmZWF0dXJlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdC8vIG9ubHkgYWRkIHRoaXMgaWYgZ2VvbWV0cnkgb3IgZ2VvbWV0cmllcyBhcmUgc2V0IGFuZCBub3QgbnVsbFxyXG5cdFx0XHRcdGZlYXR1cmUgPSBmZWF0dXJlc1tpXTtcclxuXHRcdFx0XHRpZiAoZmVhdHVyZS5nZW9tZXRyaWVzIHx8IGZlYXR1cmUuZ2VvbWV0cnkgfHwgZmVhdHVyZS5mZWF0dXJlcyB8fCBmZWF0dXJlLmNvb3JkaW5hdGVzKSB7XHJcblx0XHRcdFx0XHR0aGlzLmFkZERhdGEoZmVhdHVyZSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBvcHRpb25zID0gdGhpcy5vcHRpb25zO1xyXG5cclxuXHRcdGlmIChvcHRpb25zLmZpbHRlciAmJiAhb3B0aW9ucy5maWx0ZXIoZ2VvanNvbikpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHR2YXIgbGF5ZXIgPSBMLkdlb0pTT04uZ2VvbWV0cnlUb0xheWVyKGdlb2pzb24sIG9wdGlvbnMpO1xyXG5cdFx0aWYgKCFsYXllcikge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHRcdGxheWVyLmZlYXR1cmUgPSBMLkdlb0pTT04uYXNGZWF0dXJlKGdlb2pzb24pO1xyXG5cclxuXHRcdGxheWVyLmRlZmF1bHRPcHRpb25zID0gbGF5ZXIub3B0aW9ucztcclxuXHRcdHRoaXMucmVzZXRTdHlsZShsYXllcik7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMub25FYWNoRmVhdHVyZSkge1xyXG5cdFx0XHRvcHRpb25zLm9uRWFjaEZlYXR1cmUoZ2VvanNvbiwgbGF5ZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzLmFkZExheWVyKGxheWVyKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlc2V0U3R5bGUoIDxQYXRoPiBsYXllciApOiB0aGlzXHJcblx0Ly8gUmVzZXRzIHRoZSBnaXZlbiB2ZWN0b3IgbGF5ZXIncyBzdHlsZSB0byB0aGUgb3JpZ2luYWwgR2VvSlNPTiBzdHlsZSwgdXNlZnVsIGZvciByZXNldHRpbmcgc3R5bGUgYWZ0ZXIgaG92ZXIgZXZlbnRzLlxyXG5cdHJlc2V0U3R5bGU6IGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0Ly8gcmVzZXQgYW55IGN1c3RvbSBzdHlsZXNcclxuXHRcdGxheWVyLm9wdGlvbnMgPSBMLlV0aWwuZXh0ZW5kKHt9LCBsYXllci5kZWZhdWx0T3B0aW9ucyk7XHJcblx0XHR0aGlzLl9zZXRMYXllclN0eWxlKGxheWVyLCB0aGlzLm9wdGlvbnMuc3R5bGUpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRTdHlsZSggPEZ1bmN0aW9uPiBzdHlsZSApOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyBzdHlsZXMgb2YgR2VvSlNPTiB2ZWN0b3IgbGF5ZXJzIHdpdGggdGhlIGdpdmVuIHN0eWxlIGZ1bmN0aW9uLlxyXG5cdHNldFN0eWxlOiBmdW5jdGlvbiAoc3R5bGUpIHtcclxuXHRcdHJldHVybiB0aGlzLmVhY2hMYXllcihmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdFx0dGhpcy5fc2V0TGF5ZXJTdHlsZShsYXllciwgc3R5bGUpO1xyXG5cdFx0fSwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0X3NldExheWVyU3R5bGU6IGZ1bmN0aW9uIChsYXllciwgc3R5bGUpIHtcclxuXHRcdGlmICh0eXBlb2Ygc3R5bGUgPT09ICdmdW5jdGlvbicpIHtcclxuXHRcdFx0c3R5bGUgPSBzdHlsZShsYXllci5mZWF0dXJlKTtcclxuXHRcdH1cclxuXHRcdGlmIChsYXllci5zZXRTdHlsZSkge1xyXG5cdFx0XHRsYXllci5zZXRTdHlsZShzdHlsZSk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBzZWN0aW9uXHJcbi8vIFRoZXJlIGFyZSBzZXZlcmFsIHN0YXRpYyBmdW5jdGlvbnMgd2hpY2ggY2FuIGJlIGNhbGxlZCB3aXRob3V0IGluc3RhbnRpYXRpbmcgTC5HZW9KU09OOlxyXG5MLmV4dGVuZChMLkdlb0pTT04sIHtcclxuXHQvLyBAZnVuY3Rpb24gZ2VvbWV0cnlUb0xheWVyKGZlYXR1cmVEYXRhOiBPYmplY3QsIG9wdGlvbnM/OiBHZW9KU09OIG9wdGlvbnMpOiBMYXllclxyXG5cdC8vIENyZWF0ZXMgYSBgTGF5ZXJgIGZyb20gYSBnaXZlbiBHZW9KU09OIGZlYXR1cmUuIENhbiB1c2UgYSBjdXN0b21cclxuXHQvLyBbYHBvaW50VG9MYXllcmBdKCNnZW9qc29uLXBvaW50dG9sYXllcikgYW5kL29yIFtgY29vcmRzVG9MYXRMbmdgXSgjZ2VvanNvbi1jb29yZHN0b2xhdGxuZylcclxuXHQvLyBmdW5jdGlvbnMgaWYgcHJvdmlkZWQgYXMgb3B0aW9ucy5cclxuXHRnZW9tZXRyeVRvTGF5ZXI6IGZ1bmN0aW9uIChnZW9qc29uLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0dmFyIGdlb21ldHJ5ID0gZ2VvanNvbi50eXBlID09PSAnRmVhdHVyZScgPyBnZW9qc29uLmdlb21ldHJ5IDogZ2VvanNvbixcclxuXHRcdCAgICBjb29yZHMgPSBnZW9tZXRyeSA/IGdlb21ldHJ5LmNvb3JkaW5hdGVzIDogbnVsbCxcclxuXHRcdCAgICBsYXllcnMgPSBbXSxcclxuXHRcdCAgICBwb2ludFRvTGF5ZXIgPSBvcHRpb25zICYmIG9wdGlvbnMucG9pbnRUb0xheWVyLFxyXG5cdFx0ICAgIGNvb3Jkc1RvTGF0TG5nID0gb3B0aW9ucyAmJiBvcHRpb25zLmNvb3Jkc1RvTGF0TG5nIHx8IHRoaXMuY29vcmRzVG9MYXRMbmcsXHJcblx0XHQgICAgbGF0bG5nLCBsYXRsbmdzLCBpLCBsZW47XHJcblxyXG5cdFx0aWYgKCFjb29yZHMgJiYgIWdlb21ldHJ5KSB7XHJcblx0XHRcdHJldHVybiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHN3aXRjaCAoZ2VvbWV0cnkudHlwZSkge1xyXG5cdFx0Y2FzZSAnUG9pbnQnOlxyXG5cdFx0XHRsYXRsbmcgPSBjb29yZHNUb0xhdExuZyhjb29yZHMpO1xyXG5cdFx0XHRyZXR1cm4gcG9pbnRUb0xheWVyID8gcG9pbnRUb0xheWVyKGdlb2pzb24sIGxhdGxuZykgOiBuZXcgTC5NYXJrZXIobGF0bG5nKTtcclxuXHJcblx0XHRjYXNlICdNdWx0aVBvaW50JzpcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gY29vcmRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0bGF0bG5nID0gY29vcmRzVG9MYXRMbmcoY29vcmRzW2ldKTtcclxuXHRcdFx0XHRsYXllcnMucHVzaChwb2ludFRvTGF5ZXIgPyBwb2ludFRvTGF5ZXIoZ2VvanNvbiwgbGF0bG5nKSA6IG5ldyBMLk1hcmtlcihsYXRsbmcpKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gbmV3IEwuRmVhdHVyZUdyb3VwKGxheWVycyk7XHJcblxyXG5cdFx0Y2FzZSAnTGluZVN0cmluZyc6XHJcblx0XHRjYXNlICdNdWx0aUxpbmVTdHJpbmcnOlxyXG5cdFx0XHRsYXRsbmdzID0gdGhpcy5jb29yZHNUb0xhdExuZ3MoY29vcmRzLCBnZW9tZXRyeS50eXBlID09PSAnTGluZVN0cmluZycgPyAwIDogMSwgY29vcmRzVG9MYXRMbmcpO1xyXG5cdFx0XHRyZXR1cm4gbmV3IEwuUG9seWxpbmUobGF0bG5ncywgb3B0aW9ucyk7XHJcblxyXG5cdFx0Y2FzZSAnUG9seWdvbic6XHJcblx0XHRjYXNlICdNdWx0aVBvbHlnb24nOlxyXG5cdFx0XHRsYXRsbmdzID0gdGhpcy5jb29yZHNUb0xhdExuZ3MoY29vcmRzLCBnZW9tZXRyeS50eXBlID09PSAnUG9seWdvbicgPyAxIDogMiwgY29vcmRzVG9MYXRMbmcpO1xyXG5cdFx0XHRyZXR1cm4gbmV3IEwuUG9seWdvbihsYXRsbmdzLCBvcHRpb25zKTtcclxuXHJcblx0XHRjYXNlICdHZW9tZXRyeUNvbGxlY3Rpb24nOlxyXG5cdFx0XHRmb3IgKGkgPSAwLCBsZW4gPSBnZW9tZXRyeS5nZW9tZXRyaWVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dmFyIGxheWVyID0gdGhpcy5nZW9tZXRyeVRvTGF5ZXIoe1xyXG5cdFx0XHRcdFx0Z2VvbWV0cnk6IGdlb21ldHJ5Lmdlb21ldHJpZXNbaV0sXHJcblx0XHRcdFx0XHR0eXBlOiAnRmVhdHVyZScsXHJcblx0XHRcdFx0XHRwcm9wZXJ0aWVzOiBnZW9qc29uLnByb3BlcnRpZXNcclxuXHRcdFx0XHR9LCBvcHRpb25zKTtcclxuXHJcblx0XHRcdFx0aWYgKGxheWVyKSB7XHJcblx0XHRcdFx0XHRsYXllcnMucHVzaChsYXllcik7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHRcdHJldHVybiBuZXcgTC5GZWF0dXJlR3JvdXAobGF5ZXJzKTtcclxuXHJcblx0XHRkZWZhdWx0OlxyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgR2VvSlNPTiBvYmplY3QuJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGNvb3Jkc1RvTGF0TG5nKGNvb3JkczogQXJyYXkpOiBMYXRMbmdcclxuXHQvLyBDcmVhdGVzIGEgYExhdExuZ2Agb2JqZWN0IGZyb20gYW4gYXJyYXkgb2YgMiBudW1iZXJzIChsb25naXR1ZGUsIGxhdGl0dWRlKVxyXG5cdC8vIG9yIDMgbnVtYmVycyAobG9uZ2l0dWRlLCBsYXRpdHVkZSwgYWx0aXR1ZGUpIHVzZWQgaW4gR2VvSlNPTiBmb3IgcG9pbnRzLlxyXG5cdGNvb3Jkc1RvTGF0TG5nOiBmdW5jdGlvbiAoY29vcmRzKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKGNvb3Jkc1sxXSwgY29vcmRzWzBdLCBjb29yZHNbMl0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBjb29yZHNUb0xhdExuZ3MoY29vcmRzOiBBcnJheSwgbGV2ZWxzRGVlcD86IE51bWJlciwgY29vcmRzVG9MYXRMbmc/OiBGdW5jdGlvbik6IEFycmF5XHJcblx0Ly8gQ3JlYXRlcyBhIG11bHRpZGltZW5zaW9uYWwgYXJyYXkgb2YgYExhdExuZ2BzIGZyb20gYSBHZW9KU09OIGNvb3JkaW5hdGVzIGFycmF5LlxyXG5cdC8vIGBsZXZlbHNEZWVwYCBzcGVjaWZpZXMgdGhlIG5lc3RpbmcgbGV2ZWwgKDAgaXMgZm9yIGFuIGFycmF5IG9mIHBvaW50cywgMSBmb3IgYW4gYXJyYXkgb2YgYXJyYXlzIG9mIHBvaW50cywgZXRjLiwgMCBieSBkZWZhdWx0KS5cclxuXHQvLyBDYW4gdXNlIGEgY3VzdG9tIFtgY29vcmRzVG9MYXRMbmdgXSgjZ2VvanNvbi1jb29yZHN0b2xhdGxuZykgZnVuY3Rpb24uXHJcblx0Y29vcmRzVG9MYXRMbmdzOiBmdW5jdGlvbiAoY29vcmRzLCBsZXZlbHNEZWVwLCBjb29yZHNUb0xhdExuZykge1xyXG5cdFx0dmFyIGxhdGxuZ3MgPSBbXTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gY29vcmRzLmxlbmd0aCwgbGF0bG5nOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0bGF0bG5nID0gbGV2ZWxzRGVlcCA/XHJcblx0XHRcdCAgICAgICAgdGhpcy5jb29yZHNUb0xhdExuZ3MoY29vcmRzW2ldLCBsZXZlbHNEZWVwIC0gMSwgY29vcmRzVG9MYXRMbmcpIDpcclxuXHRcdFx0ICAgICAgICAoY29vcmRzVG9MYXRMbmcgfHwgdGhpcy5jb29yZHNUb0xhdExuZykoY29vcmRzW2ldKTtcclxuXHJcblx0XHRcdGxhdGxuZ3MucHVzaChsYXRsbmcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBsYXRsbmdzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBsYXRMbmdUb0Nvb3JkcyhsYXRsbmc6IExhdExuZyk6IEFycmF5XHJcblx0Ly8gUmV2ZXJzZSBvZiBbYGNvb3Jkc1RvTGF0TG5nYF0oI2dlb2pzb24tY29vcmRzdG9sYXRsbmcpXHJcblx0bGF0TG5nVG9Db29yZHM6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiBsYXRsbmcuYWx0ICE9PSB1bmRlZmluZWQgP1xyXG5cdFx0XHRcdFtsYXRsbmcubG5nLCBsYXRsbmcubGF0LCBsYXRsbmcuYWx0XSA6XHJcblx0XHRcdFx0W2xhdGxuZy5sbmcsIGxhdGxuZy5sYXRdO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBsYXRMbmdzVG9Db29yZHMobGF0bG5nczogQXJyYXksIGxldmVsc0RlZXA/OiBOdW1iZXIsIGNsb3NlZD86IEJvb2xlYW4pOiBBcnJheVxyXG5cdC8vIFJldmVyc2Ugb2YgW2Bjb29yZHNUb0xhdExuZ3NgXSgjZ2VvanNvbi1jb29yZHN0b2xhdGxuZ3MpXHJcblx0Ly8gYGNsb3NlZGAgZGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSBmaXJzdCBwb2ludCBzaG91bGQgYmUgYXBwZW5kZWQgdG8gdGhlIGVuZCBvZiB0aGUgYXJyYXkgdG8gY2xvc2UgdGhlIGZlYXR1cmUsIG9ubHkgdXNlZCB3aGVuIGBsZXZlbHNEZWVwYCBpcyAwLiBGYWxzZSBieSBkZWZhdWx0LlxyXG5cdGxhdExuZ3NUb0Nvb3JkczogZnVuY3Rpb24gKGxhdGxuZ3MsIGxldmVsc0RlZXAsIGNsb3NlZCkge1xyXG5cdFx0dmFyIGNvb3JkcyA9IFtdO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsYXRsbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdGNvb3Jkcy5wdXNoKGxldmVsc0RlZXAgP1xyXG5cdFx0XHRcdEwuR2VvSlNPTi5sYXRMbmdzVG9Db29yZHMobGF0bG5nc1tpXSwgbGV2ZWxzRGVlcCAtIDEsIGNsb3NlZCkgOlxyXG5cdFx0XHRcdEwuR2VvSlNPTi5sYXRMbmdUb0Nvb3JkcyhsYXRsbmdzW2ldKSk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCFsZXZlbHNEZWVwICYmIGNsb3NlZCkge1xyXG5cdFx0XHRjb29yZHMucHVzaChjb29yZHNbMF0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBjb29yZHM7XHJcblx0fSxcclxuXHJcblx0Z2V0RmVhdHVyZTogZnVuY3Rpb24gKGxheWVyLCBuZXdHZW9tZXRyeSkge1xyXG5cdFx0cmV0dXJuIGxheWVyLmZlYXR1cmUgP1xyXG5cdFx0XHRcdEwuZXh0ZW5kKHt9LCBsYXllci5mZWF0dXJlLCB7Z2VvbWV0cnk6IG5ld0dlb21ldHJ5fSkgOlxyXG5cdFx0XHRcdEwuR2VvSlNPTi5hc0ZlYXR1cmUobmV3R2VvbWV0cnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBhc0ZlYXR1cmUoZ2VvanNvbjogT2JqZWN0KTogT2JqZWN0XHJcblx0Ly8gTm9ybWFsaXplIEdlb0pTT04gZ2VvbWV0cmllcy9mZWF0dXJlcyBpbnRvIEdlb0pTT04gZmVhdHVyZXMuXHJcblx0YXNGZWF0dXJlOiBmdW5jdGlvbiAoZ2VvanNvbikge1xyXG5cdFx0aWYgKGdlb2pzb24udHlwZSA9PT0gJ0ZlYXR1cmUnIHx8IGdlb2pzb24udHlwZSA9PT0gJ0ZlYXR1cmVDb2xsZWN0aW9uJykge1xyXG5cdFx0XHRyZXR1cm4gZ2VvanNvbjtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4ge1xyXG5cdFx0XHR0eXBlOiAnRmVhdHVyZScsXHJcblx0XHRcdHByb3BlcnRpZXM6IHt9LFxyXG5cdFx0XHRnZW9tZXRyeTogZ2VvanNvblxyXG5cdFx0fTtcclxuXHR9XHJcbn0pO1xyXG5cclxudmFyIFBvaW50VG9HZW9KU09OID0ge1xyXG5cdHRvR2VvSlNPTjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIEwuR2VvSlNPTi5nZXRGZWF0dXJlKHRoaXMsIHtcclxuXHRcdFx0dHlwZTogJ1BvaW50JyxcclxuXHRcdFx0Y29vcmRpbmF0ZXM6IEwuR2VvSlNPTi5sYXRMbmdUb0Nvb3Jkcyh0aGlzLmdldExhdExuZygpKVxyXG5cdFx0fSk7XHJcblx0fVxyXG59O1xyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXJrZXJcclxuLy8gQG1ldGhvZCB0b0dlb0pTT04oKTogT2JqZWN0XHJcbi8vIFJldHVybnMgYSBbYEdlb0pTT05gXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dlb0pTT04pIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBtYXJrZXIgKGFzIGEgR2VvSlNPTiBgUG9pbnRgIEZlYXR1cmUpLlxyXG5MLk1hcmtlci5pbmNsdWRlKFBvaW50VG9HZW9KU09OKTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgQ2lyY2xlTWFya2VyXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG4vLyBSZXR1cm5zIGEgW2BHZW9KU09OYF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9KU09OKSByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2lyY2xlIG1hcmtlciAoYXMgYSBHZW9KU09OIGBQb2ludGAgRmVhdHVyZSkuXHJcbkwuQ2lyY2xlLmluY2x1ZGUoUG9pbnRUb0dlb0pTT04pO1xyXG5MLkNpcmNsZU1hcmtlci5pbmNsdWRlKFBvaW50VG9HZW9KU09OKTtcclxuXHJcblxyXG4vLyBAbmFtZXNwYWNlIFBvbHlsaW5lXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG4vLyBSZXR1cm5zIGEgW2BHZW9KU09OYF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9KU09OKSByZXByZXNlbnRhdGlvbiBvZiB0aGUgcG9seWxpbmUgKGFzIGEgR2VvSlNPTiBgTGluZVN0cmluZ2Agb3IgYE11bHRpTGluZVN0cmluZ2AgRmVhdHVyZSkuXHJcbkwuUG9seWxpbmUucHJvdG90eXBlLnRvR2VvSlNPTiA9IGZ1bmN0aW9uICgpIHtcclxuXHR2YXIgbXVsdGkgPSAhTC5Qb2x5bGluZS5fZmxhdCh0aGlzLl9sYXRsbmdzKTtcclxuXHJcblx0dmFyIGNvb3JkcyA9IEwuR2VvSlNPTi5sYXRMbmdzVG9Db29yZHModGhpcy5fbGF0bG5ncywgbXVsdGkgPyAxIDogMCk7XHJcblxyXG5cdHJldHVybiBMLkdlb0pTT04uZ2V0RmVhdHVyZSh0aGlzLCB7XHJcblx0XHR0eXBlOiAobXVsdGkgPyAnTXVsdGknIDogJycpICsgJ0xpbmVTdHJpbmcnLFxyXG5cdFx0Y29vcmRpbmF0ZXM6IGNvb3Jkc1xyXG5cdH0pO1xyXG59O1xyXG5cclxuLy8gQG5hbWVzcGFjZSBQb2x5Z29uXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG4vLyBSZXR1cm5zIGEgW2BHZW9KU09OYF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9KU09OKSByZXByZXNlbnRhdGlvbiBvZiB0aGUgcG9seWdvbiAoYXMgYSBHZW9KU09OIGBQb2x5Z29uYCBvciBgTXVsdGlQb2x5Z29uYCBGZWF0dXJlKS5cclxuTC5Qb2x5Z29uLnByb3RvdHlwZS50b0dlb0pTT04gPSBmdW5jdGlvbiAoKSB7XHJcblx0dmFyIGhvbGVzID0gIUwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5ncyksXHJcblx0ICAgIG11bHRpID0gaG9sZXMgJiYgIUwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5nc1swXSk7XHJcblxyXG5cdHZhciBjb29yZHMgPSBMLkdlb0pTT04ubGF0TG5nc1RvQ29vcmRzKHRoaXMuX2xhdGxuZ3MsIG11bHRpID8gMiA6IGhvbGVzID8gMSA6IDAsIHRydWUpO1xyXG5cclxuXHRpZiAoIWhvbGVzKSB7XHJcblx0XHRjb29yZHMgPSBbY29vcmRzXTtcclxuXHR9XHJcblxyXG5cdHJldHVybiBMLkdlb0pTT04uZ2V0RmVhdHVyZSh0aGlzLCB7XHJcblx0XHR0eXBlOiAobXVsdGkgPyAnTXVsdGknIDogJycpICsgJ1BvbHlnb24nLFxyXG5cdFx0Y29vcmRpbmF0ZXM6IGNvb3Jkc1xyXG5cdH0pO1xyXG59O1xyXG5cclxuXHJcbi8vIEBuYW1lc3BhY2UgTGF5ZXJHcm91cFxyXG5MLkxheWVyR3JvdXAuaW5jbHVkZSh7XHJcblx0dG9NdWx0aVBvaW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY29vcmRzID0gW107XHJcblxyXG5cdFx0dGhpcy5lYWNoTGF5ZXIoZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRcdGNvb3Jkcy5wdXNoKGxheWVyLnRvR2VvSlNPTigpLmdlb21ldHJ5LmNvb3JkaW5hdGVzKTtcclxuXHRcdH0pO1xyXG5cclxuXHRcdHJldHVybiBMLkdlb0pTT04uZ2V0RmVhdHVyZSh0aGlzLCB7XHJcblx0XHRcdHR5cGU6ICdNdWx0aVBvaW50JyxcclxuXHRcdFx0Y29vcmRpbmF0ZXM6IGNvb3Jkc1xyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b0dlb0pTT04oKTogT2JqZWN0XHJcblx0Ly8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIGxheWVyIGdyb3VwIChhcyBhIEdlb0pTT04gYEdlb21ldHJ5Q29sbGVjdGlvbmApLlxyXG5cdHRvR2VvSlNPTjogZnVuY3Rpb24gKCkge1xyXG5cclxuXHRcdHZhciB0eXBlID0gdGhpcy5mZWF0dXJlICYmIHRoaXMuZmVhdHVyZS5nZW9tZXRyeSAmJiB0aGlzLmZlYXR1cmUuZ2VvbWV0cnkudHlwZTtcclxuXHJcblx0XHRpZiAodHlwZSA9PT0gJ011bHRpUG9pbnQnKSB7XHJcblx0XHRcdHJldHVybiB0aGlzLnRvTXVsdGlQb2ludCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBpc0dlb21ldHJ5Q29sbGVjdGlvbiA9IHR5cGUgPT09ICdHZW9tZXRyeUNvbGxlY3Rpb24nLFxyXG5cdFx0ICAgIGpzb25zID0gW107XHJcblxyXG5cdFx0dGhpcy5lYWNoTGF5ZXIoZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRcdGlmIChsYXllci50b0dlb0pTT04pIHtcclxuXHRcdFx0XHR2YXIganNvbiA9IGxheWVyLnRvR2VvSlNPTigpO1xyXG5cdFx0XHRcdGpzb25zLnB1c2goaXNHZW9tZXRyeUNvbGxlY3Rpb24gPyBqc29uLmdlb21ldHJ5IDogTC5HZW9KU09OLmFzRmVhdHVyZShqc29uKSk7XHJcblx0XHRcdH1cclxuXHRcdH0pO1xyXG5cclxuXHRcdGlmIChpc0dlb21ldHJ5Q29sbGVjdGlvbikge1xyXG5cdFx0XHRyZXR1cm4gTC5HZW9KU09OLmdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0XHRcdGdlb21ldHJpZXM6IGpzb25zLFxyXG5cdFx0XHRcdHR5cGU6ICdHZW9tZXRyeUNvbGxlY3Rpb24nXHJcblx0XHRcdH0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB7XHJcblx0XHRcdHR5cGU6ICdGZWF0dXJlQ29sbGVjdGlvbicsXHJcblx0XHRcdGZlYXR1cmVzOiBqc29uc1xyXG5cdFx0fTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBHZW9KU09OXHJcbi8vIEBmYWN0b3J5IEwuZ2VvSlNPTihnZW9qc29uPzogT2JqZWN0LCBvcHRpb25zPzogR2VvSlNPTiBvcHRpb25zKVxyXG4vLyBDcmVhdGVzIGEgR2VvSlNPTiBsYXllci4gT3B0aW9uYWxseSBhY2NlcHRzIGFuIG9iamVjdCBpblxyXG4vLyBbR2VvSlNPTiBmb3JtYXRdKGh0dHA6Ly9nZW9qc29uLm9yZy9nZW9qc29uLXNwZWMuaHRtbCkgdG8gZGlzcGxheSBvbiB0aGUgbWFwXHJcbi8vICh5b3UgY2FuIGFsdGVybmF0aXZlbHkgYWRkIGl0IGxhdGVyIHdpdGggYGFkZERhdGFgIG1ldGhvZCkgYW5kIGFuIGBvcHRpb25zYCBvYmplY3QuXHJcbkwuZ2VvSlNPTiA9IGZ1bmN0aW9uIChnZW9qc29uLCBvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkdlb0pTT04oZ2VvanNvbiwgb3B0aW9ucyk7XHJcbn07XHJcbi8vIEJhY2t3YXJkIGNvbXBhdGliaWxpdHkuXHJcbkwuZ2VvSnNvbiA9IEwuZ2VvSlNPTjtcclxuXG5cblxuLypcclxuICogQGNsYXNzIERyYWdnYWJsZVxyXG4gKiBAYWthIEwuRHJhZ2dhYmxlXHJcbiAqIEBpbmhlcml0cyBFdmVudGVkXHJcbiAqXHJcbiAqIEEgY2xhc3MgZm9yIG1ha2luZyBET00gZWxlbWVudHMgZHJhZ2dhYmxlIChpbmNsdWRpbmcgdG91Y2ggc3VwcG9ydCkuXHJcbiAqIFVzZWQgaW50ZXJuYWxseSBmb3IgbWFwIGFuZCBtYXJrZXIgZHJhZ2dpbmcuIE9ubHkgd29ya3MgZm9yIGVsZW1lbnRzXHJcbiAqIHRoYXQgd2VyZSBwb3NpdGlvbmVkIHdpdGggW2BMLkRvbVV0aWwuc2V0UG9zaXRpb25gXSgjZG9tdXRpbC1zZXRwb3NpdGlvbikuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBkcmFnZ2FibGUgPSBuZXcgTC5EcmFnZ2FibGUoZWxlbWVudFRvRHJhZyk7XHJcbiAqIGRyYWdnYWJsZS5lbmFibGUoKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5EcmFnZ2FibGUgPSBMLkV2ZW50ZWQuZXh0ZW5kKHtcclxuXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBjbGlja1RvbGVyYW5jZTogTnVtYmVyID0gM1xyXG5cdFx0Ly8gVGhlIG1heCBudW1iZXIgb2YgcGl4ZWxzIGEgdXNlciBjYW4gc2hpZnQgdGhlIG1vdXNlIHBvaW50ZXIgZHVyaW5nIGEgY2xpY2tcclxuXHRcdC8vIGZvciBpdCB0byBiZSBjb25zaWRlcmVkIGEgdmFsaWQgY2xpY2sgKGFzIG9wcG9zZWQgdG8gYSBtb3VzZSBkcmFnKS5cclxuXHRcdGNsaWNrVG9sZXJhbmNlOiAzXHJcblx0fSxcclxuXHJcblx0c3RhdGljczoge1xyXG5cdFx0U1RBUlQ6IEwuQnJvd3Nlci50b3VjaCA/IFsndG91Y2hzdGFydCcsICdtb3VzZWRvd24nXSA6IFsnbW91c2Vkb3duJ10sXHJcblx0XHRFTkQ6IHtcclxuXHRcdFx0bW91c2Vkb3duOiAnbW91c2V1cCcsXHJcblx0XHRcdHRvdWNoc3RhcnQ6ICd0b3VjaGVuZCcsXHJcblx0XHRcdHBvaW50ZXJkb3duOiAndG91Y2hlbmQnLFxyXG5cdFx0XHRNU1BvaW50ZXJEb3duOiAndG91Y2hlbmQnXHJcblx0XHR9LFxyXG5cdFx0TU9WRToge1xyXG5cdFx0XHRtb3VzZWRvd246ICdtb3VzZW1vdmUnLFxyXG5cdFx0XHR0b3VjaHN0YXJ0OiAndG91Y2htb3ZlJyxcclxuXHRcdFx0cG9pbnRlcmRvd246ICd0b3VjaG1vdmUnLFxyXG5cdFx0XHRNU1BvaW50ZXJEb3duOiAndG91Y2htb3ZlJ1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBjb25zdHJ1Y3RvciBMLkRyYWdnYWJsZShlbDogSFRNTEVsZW1lbnQsIGRyYWdIYW5kbGU/OiBIVE1MRWxlbWVudCwgcHJldmVudE91dGxpbmU6IEJvb2xlYW4pXHJcblx0Ly8gQ3JlYXRlcyBhIGBEcmFnZ2FibGVgIG9iamVjdCBmb3IgbW92aW5nIGBlbGAgd2hlbiB5b3Ugc3RhcnQgZHJhZ2dpbmcgdGhlIGBkcmFnSGFuZGxlYCBlbGVtZW50IChlcXVhbHMgYGVsYCBpdHNlbGYgYnkgZGVmYXVsdCkuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGVsZW1lbnQsIGRyYWdTdGFydFRhcmdldCwgcHJldmVudE91dGxpbmUpIHtcclxuXHRcdHRoaXMuX2VsZW1lbnQgPSBlbGVtZW50O1xyXG5cdFx0dGhpcy5fZHJhZ1N0YXJ0VGFyZ2V0ID0gZHJhZ1N0YXJ0VGFyZ2V0IHx8IGVsZW1lbnQ7XHJcblx0XHR0aGlzLl9wcmV2ZW50T3V0bGluZSA9IHByZXZlbnRPdXRsaW5lO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZW5hYmxlKClcclxuXHQvLyBFbmFibGVzIHRoZSBkcmFnZ2luZyBhYmlsaXR5XHJcblx0ZW5hYmxlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fZW5hYmxlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9uKHRoaXMuX2RyYWdTdGFydFRhcmdldCwgTC5EcmFnZ2FibGUuU1RBUlQuam9pbignICcpLCB0aGlzLl9vbkRvd24sIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX2VuYWJsZWQgPSB0cnVlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzYWJsZSgpXHJcblx0Ly8gRGlzYWJsZXMgdGhlIGRyYWdnaW5nIGFiaWxpdHlcclxuXHRkaXNhYmxlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0Ly8gSWYgd2UncmUgY3VycmVudGx5IGRyYWdnaW5nIHRoaXMgZHJhZ2dhYmxlLFxyXG5cdFx0Ly8gZGlzYWJsaW5nIGl0IGNvdW50cyBhcyBmaXJzdCBlbmRpbmcgdGhlIGRyYWcuXHJcblx0XHRpZiAoTC5EcmFnZ2FibGUuX2RyYWdnaW5nID09PSB0aGlzKSB7XHJcblx0XHRcdHRoaXMuZmluaXNoRHJhZygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX2RyYWdTdGFydFRhcmdldCwgTC5EcmFnZ2FibGUuU1RBUlQuam9pbignICcpLCB0aGlzLl9vbkRvd24sIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX2VuYWJsZWQgPSBmYWxzZTtcclxuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XHJcblx0fSxcclxuXHJcblx0X29uRG93bjogZnVuY3Rpb24gKGUpIHtcclxuXHRcdC8vIElnbm9yZSBzaW11bGF0ZWQgZXZlbnRzLCBzaW5jZSB3ZSBoYW5kbGUgYm90aCB0b3VjaCBhbmRcclxuXHRcdC8vIG1vdXNlIGV4cGxpY2l0bHk7IG90aGVyd2lzZSB3ZSByaXNrIGdldHRpbmcgZHVwbGljYXRlcyBvZlxyXG5cdFx0Ly8gdG91Y2ggZXZlbnRzLCBzZWUgIzQzMTUuXHJcblx0XHQvLyBBbHNvIGlnbm9yZSB0aGUgZXZlbnQgaWYgZGlzYWJsZWQ7IHRoaXMgaGFwcGVucyBpbiBJRTExXHJcblx0XHQvLyB1bmRlciBzb21lIGNpcmN1bXN0YW5jZXMsIHNlZSAjMzY2Ni5cclxuXHRcdGlmIChlLl9zaW11bGF0ZWQgfHwgIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dGhpcy5fbW92ZWQgPSBmYWxzZTtcclxuXHJcblx0XHRpZiAoTC5Eb21VdGlsLmhhc0NsYXNzKHRoaXMuX2VsZW1lbnQsICdsZWFmbGV0LXpvb20tYW5pbScpKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdGlmIChMLkRyYWdnYWJsZS5fZHJhZ2dpbmcgfHwgZS5zaGlmdEtleSB8fCAoKGUud2hpY2ggIT09IDEpICYmIChlLmJ1dHRvbiAhPT0gMSkgJiYgIWUudG91Y2hlcykpIHsgcmV0dXJuOyB9XHJcblx0XHRMLkRyYWdnYWJsZS5fZHJhZ2dpbmcgPSB0aGlzOyAgLy8gUHJldmVudCBkcmFnZ2luZyBtdWx0aXBsZSBvYmplY3RzIGF0IG9uY2UuXHJcblxyXG5cdFx0aWYgKHRoaXMuX3ByZXZlbnRPdXRsaW5lKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZSh0aGlzLl9lbGVtZW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwuZGlzYWJsZUltYWdlRHJhZygpO1xyXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX21vdmluZykgeyByZXR1cm47IH1cclxuXHJcblx0XHQvLyBAZXZlbnQgZG93bjogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gYSBkcmFnIGlzIGFib3V0IHRvIHN0YXJ0LlxyXG5cdFx0dGhpcy5maXJlKCdkb3duJyk7XHJcblxyXG5cdFx0dmFyIGZpcnN0ID0gZS50b3VjaGVzID8gZS50b3VjaGVzWzBdIDogZTtcclxuXHJcblx0XHR0aGlzLl9zdGFydFBvaW50ID0gbmV3IEwuUG9pbnQoZmlyc3QuY2xpZW50WCwgZmlyc3QuY2xpZW50WSk7XHJcblxyXG5cdFx0TC5Eb21FdmVudFxyXG5cdFx0XHQub24oZG9jdW1lbnQsIEwuRHJhZ2dhYmxlLk1PVkVbZS50eXBlXSwgdGhpcy5fb25Nb3ZlLCB0aGlzKVxyXG5cdFx0XHQub24oZG9jdW1lbnQsIEwuRHJhZ2dhYmxlLkVORFtlLnR5cGVdLCB0aGlzLl9vblVwLCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRfb25Nb3ZlOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gSWdub3JlIHNpbXVsYXRlZCBldmVudHMsIHNpbmNlIHdlIGhhbmRsZSBib3RoIHRvdWNoIGFuZFxyXG5cdFx0Ly8gbW91c2UgZXhwbGljaXRseTsgb3RoZXJ3aXNlIHdlIHJpc2sgZ2V0dGluZyBkdXBsaWNhdGVzIG9mXHJcblx0XHQvLyB0b3VjaCBldmVudHMsIHNlZSAjNDMxNS5cclxuXHRcdC8vIEFsc28gaWdub3JlIHRoZSBldmVudCBpZiBkaXNhYmxlZDsgdGhpcyBoYXBwZW5zIGluIElFMTFcclxuXHRcdC8vIHVuZGVyIHNvbWUgY2lyY3Vtc3RhbmNlcywgc2VlICMzNjY2LlxyXG5cdFx0aWYgKGUuX3NpbXVsYXRlZCB8fCAhdGhpcy5fZW5hYmxlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAoZS50b3VjaGVzICYmIGUudG91Y2hlcy5sZW5ndGggPiAxKSB7XHJcblx0XHRcdHRoaXMuX21vdmVkID0gdHJ1ZTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBmaXJzdCA9IChlLnRvdWNoZXMgJiYgZS50b3VjaGVzLmxlbmd0aCA9PT0gMSA/IGUudG91Y2hlc1swXSA6IGUpLFxyXG5cdFx0ICAgIG5ld1BvaW50ID0gbmV3IEwuUG9pbnQoZmlyc3QuY2xpZW50WCwgZmlyc3QuY2xpZW50WSksXHJcblx0XHQgICAgb2Zmc2V0ID0gbmV3UG9pbnQuc3VidHJhY3QodGhpcy5fc3RhcnRQb2ludCk7XHJcblxyXG5cdFx0aWYgKCFvZmZzZXQueCAmJiAhb2Zmc2V0LnkpIHsgcmV0dXJuOyB9XHJcblx0XHRpZiAoTWF0aC5hYnMob2Zmc2V0LngpICsgTWF0aC5hYnMob2Zmc2V0LnkpIDwgdGhpcy5vcHRpb25zLmNsaWNrVG9sZXJhbmNlKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9tb3ZlZCkge1xyXG5cdFx0XHQvLyBAZXZlbnQgZHJhZ3N0YXJ0OiBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgZHJhZyBzdGFydHNcclxuXHRcdFx0dGhpcy5maXJlKCdkcmFnc3RhcnQnKTtcclxuXHJcblx0XHRcdHRoaXMuX21vdmVkID0gdHJ1ZTtcclxuXHRcdFx0dGhpcy5fc3RhcnRQb3MgPSBMLkRvbVV0aWwuZ2V0UG9zaXRpb24odGhpcy5fZWxlbWVudCkuc3VidHJhY3Qob2Zmc2V0KTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhkb2N1bWVudC5ib2R5LCAnbGVhZmxldC1kcmFnZ2luZycpO1xyXG5cclxuXHRcdFx0dGhpcy5fbGFzdFRhcmdldCA9IGUudGFyZ2V0IHx8IGUuc3JjRWxlbWVudDtcclxuXHRcdFx0Ly8gSUUgYW5kIEVkZ2UgZG8gbm90IGdpdmUgdGhlIDx1c2U+IGVsZW1lbnQsIHNvIGZldGNoIGl0XHJcblx0XHRcdC8vIGlmIG5lY2Vzc2FyeVxyXG5cdFx0XHRpZiAoKHdpbmRvdy5TVkdFbGVtZW50SW5zdGFuY2UpICYmICh0aGlzLl9sYXN0VGFyZ2V0IGluc3RhbmNlb2YgU1ZHRWxlbWVudEluc3RhbmNlKSkge1xyXG5cdFx0XHRcdHRoaXMuX2xhc3RUYXJnZXQgPSB0aGlzLl9sYXN0VGFyZ2V0LmNvcnJlc3BvbmRpbmdVc2VFbGVtZW50O1xyXG5cdFx0XHR9XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9sYXN0VGFyZ2V0LCAnbGVhZmxldC1kcmFnLXRhcmdldCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX25ld1BvcyA9IHRoaXMuX3N0YXJ0UG9zLmFkZChvZmZzZXQpO1xyXG5cdFx0dGhpcy5fbW92aW5nID0gdHJ1ZTtcclxuXHJcblx0XHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lKHRoaXMuX2FuaW1SZXF1ZXN0KTtcclxuXHRcdHRoaXMuX2xhc3RFdmVudCA9IGU7XHJcblx0XHR0aGlzLl9hbmltUmVxdWVzdCA9IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX3VwZGF0ZVBvc2l0aW9uLCB0aGlzLCB0cnVlKTtcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlUG9zaXRpb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBlID0ge29yaWdpbmFsRXZlbnQ6IHRoaXMuX2xhc3RFdmVudH07XHJcblxyXG5cdFx0Ly8gQGV2ZW50IHByZWRyYWc6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCBjb250aW51b3VzbHkgZHVyaW5nIGRyYWdnaW5nICpiZWZvcmUqIGVhY2ggY29ycmVzcG9uZGluZ1xyXG5cdFx0Ly8gdXBkYXRlIG9mIHRoZSBlbGVtZW50J3MgcG9zaXRpb24uXHJcblx0XHR0aGlzLmZpcmUoJ3ByZWRyYWcnLCBlKTtcclxuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aGlzLl9lbGVtZW50LCB0aGlzLl9uZXdQb3MpO1xyXG5cclxuXHRcdC8vIEBldmVudCBkcmFnOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgY29udGludW91c2x5IGR1cmluZyBkcmFnZ2luZy5cclxuXHRcdHRoaXMuZmlyZSgnZHJhZycsIGUpO1xyXG5cdH0sXHJcblxyXG5cdF9vblVwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gSWdub3JlIHNpbXVsYXRlZCBldmVudHMsIHNpbmNlIHdlIGhhbmRsZSBib3RoIHRvdWNoIGFuZFxyXG5cdFx0Ly8gbW91c2UgZXhwbGljaXRseTsgb3RoZXJ3aXNlIHdlIHJpc2sgZ2V0dGluZyBkdXBsaWNhdGVzIG9mXHJcblx0XHQvLyB0b3VjaCBldmVudHMsIHNlZSAjNDMxNS5cclxuXHRcdC8vIEFsc28gaWdub3JlIHRoZSBldmVudCBpZiBkaXNhYmxlZDsgdGhpcyBoYXBwZW5zIGluIElFMTFcclxuXHRcdC8vIHVuZGVyIHNvbWUgY2lyY3Vtc3RhbmNlcywgc2VlICMzNjY2LlxyXG5cdFx0aWYgKGUuX3NpbXVsYXRlZCB8fCAhdGhpcy5fZW5hYmxlZCkgeyByZXR1cm47IH1cclxuXHRcdHRoaXMuZmluaXNoRHJhZygpO1xyXG5cdH0sXHJcblxyXG5cdGZpbmlzaERyYWc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhkb2N1bWVudC5ib2R5LCAnbGVhZmxldC1kcmFnZ2luZycpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sYXN0VGFyZ2V0KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9sYXN0VGFyZ2V0LCAnbGVhZmxldC1kcmFnLXRhcmdldCcpO1xyXG5cdFx0XHR0aGlzLl9sYXN0VGFyZ2V0ID0gbnVsbDtcclxuXHRcdH1cclxuXHJcblx0XHRmb3IgKHZhciBpIGluIEwuRHJhZ2dhYmxlLk1PVkUpIHtcclxuXHRcdFx0TC5Eb21FdmVudFxyXG5cdFx0XHRcdC5vZmYoZG9jdW1lbnQsIEwuRHJhZ2dhYmxlLk1PVkVbaV0sIHRoaXMuX29uTW92ZSwgdGhpcylcclxuXHRcdFx0XHQub2ZmKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5FTkRbaV0sIHRoaXMuX29uVXAsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5lbmFibGVJbWFnZURyYWcoKTtcclxuXHRcdEwuRG9tVXRpbC5lbmFibGVUZXh0U2VsZWN0aW9uKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX21vdmVkICYmIHRoaXMuX21vdmluZykge1xyXG5cdFx0XHQvLyBlbnN1cmUgZHJhZyBpcyBub3QgZmlyZWQgYWZ0ZXIgZHJhZ2VuZFxyXG5cdFx0XHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lKHRoaXMuX2FuaW1SZXF1ZXN0KTtcclxuXHJcblx0XHRcdC8vIEBldmVudCBkcmFnZW5kOiBEcmFnRW5kRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgd2hlbiB0aGUgZHJhZyBlbmRzLlxyXG5cdFx0XHR0aGlzLmZpcmUoJ2RyYWdlbmQnLCB7XHJcblx0XHRcdFx0ZGlzdGFuY2U6IHRoaXMuX25ld1Bvcy5kaXN0YW5jZVRvKHRoaXMuX3N0YXJ0UG9zKVxyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9tb3ZpbmcgPSBmYWxzZTtcclxuXHRcdEwuRHJhZ2dhYmxlLl9kcmFnZ2luZyA9IGZhbHNlO1xyXG5cdH1cclxuXHJcbn0pO1xyXG5cblxuXG4vKlxuXHRMLkhhbmRsZXIgaXMgYSBiYXNlIGNsYXNzIGZvciBoYW5kbGVyIGNsYXNzZXMgdGhhdCBhcmUgdXNlZCBpbnRlcm5hbGx5IHRvIGluamVjdFxuXHRpbnRlcmFjdGlvbiBmZWF0dXJlcyBsaWtlIGRyYWdnaW5nIHRvIGNsYXNzZXMgbGlrZSBNYXAgYW5kIE1hcmtlci5cbiovXG5cbi8vIEBjbGFzcyBIYW5kbGVyXG4vLyBAYWthIEwuSGFuZGxlclxuLy8gQWJzdHJhY3QgY2xhc3MgZm9yIG1hcCBpbnRlcmFjdGlvbiBoYW5kbGVyc1xuXG5MLkhhbmRsZXIgPSBMLkNsYXNzLmV4dGVuZCh7XG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHR0aGlzLl9tYXAgPSBtYXA7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBlbmFibGUoKTogdGhpc1xuXHQvLyBFbmFibGVzIHRoZSBoYW5kbGVyXG5cdGVuYWJsZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9lbmFibGVkKSB7IHJldHVybiB0aGlzOyB9XG5cblx0XHR0aGlzLl9lbmFibGVkID0gdHJ1ZTtcblx0XHR0aGlzLmFkZEhvb2tzKCk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBkaXNhYmxlKCk6IHRoaXNcblx0Ly8gRGlzYWJsZXMgdGhlIGhhbmRsZXJcblx0ZGlzYWJsZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fZW5hYmxlZCkgeyByZXR1cm4gdGhpczsgfVxuXG5cdFx0dGhpcy5fZW5hYmxlZCA9IGZhbHNlO1xuXHRcdHRoaXMucmVtb3ZlSG9va3MoKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGVuYWJsZWQoKTogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgaGFuZGxlciBpcyBlbmFibGVkXG5cdGVuYWJsZWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gISF0aGlzLl9lbmFibGVkO1xuXHR9XG5cblx0Ly8gQHNlY3Rpb24gRXh0ZW5zaW9uIG1ldGhvZHNcblx0Ly8gQ2xhc3NlcyBpbmhlcml0aW5nIGZyb20gYEhhbmRsZXJgIG11c3QgaW1wbGVtZW50IHRoZSB0d28gZm9sbG93aW5nIG1ldGhvZHM6XG5cdC8vIEBtZXRob2QgYWRkSG9va3MoKVxuXHQvLyBDYWxsZWQgd2hlbiB0aGUgaGFuZGxlciBpcyBlbmFibGVkLCBzaG91bGQgYWRkIGV2ZW50IGhvb2tzLlxuXHQvLyBAbWV0aG9kIHJlbW92ZUhvb2tzKClcblx0Ly8gQ2FsbGVkIHdoZW4gdGhlIGhhbmRsZXIgaXMgZGlzYWJsZWQsIHNob3VsZCByZW1vdmUgdGhlIGV2ZW50IGhvb2tzIGFkZGVkIHByZXZpb3VzbHkuXG59KTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuTWFwRHJhZyBpcyB1c2VkIHRvIG1ha2UgdGhlIG1hcCBkcmFnZ2FibGUgKHdpdGggcGFubmluZyBpbmVydGlhKSwgZW5hYmxlZCBieSBkZWZhdWx0LlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAb3B0aW9uIGRyYWdnaW5nOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBXaGV0aGVyIHRoZSBtYXAgYmUgZHJhZ2dhYmxlIHdpdGggbW91c2UvdG91Y2ggb3Igbm90LlxuXHRkcmFnZ2luZzogdHJ1ZSxcblxuXHQvLyBAc2VjdGlvbiBQYW5uaW5nIEluZXJ0aWEgT3B0aW9uc1xuXHQvLyBAb3B0aW9uIGluZXJ0aWE6IEJvb2xlYW4gPSAqXG5cdC8vIElmIGVuYWJsZWQsIHBhbm5pbmcgb2YgdGhlIG1hcCB3aWxsIGhhdmUgYW4gaW5lcnRpYSBlZmZlY3Qgd2hlcmVcblx0Ly8gdGhlIG1hcCBidWlsZHMgbW9tZW50dW0gd2hpbGUgZHJhZ2dpbmcgYW5kIGNvbnRpbnVlcyBtb3ZpbmcgaW5cblx0Ly8gdGhlIHNhbWUgZGlyZWN0aW9uIGZvciBzb21lIHRpbWUuIEZlZWxzIGVzcGVjaWFsbHkgbmljZSBvbiB0b3VjaFxuXHQvLyBkZXZpY2VzLiBFbmFibGVkIGJ5IGRlZmF1bHQgdW5sZXNzIHJ1bm5pbmcgb24gb2xkIEFuZHJvaWQgZGV2aWNlcy5cblx0aW5lcnRpYTogIUwuQnJvd3Nlci5hbmRyb2lkMjMsXG5cblx0Ly8gQG9wdGlvbiBpbmVydGlhRGVjZWxlcmF0aW9uOiBOdW1iZXIgPSAzMDAwXG5cdC8vIFRoZSByYXRlIHdpdGggd2hpY2ggdGhlIGluZXJ0aWFsIG1vdmVtZW50IHNsb3dzIGRvd24sIGluIHBpeGVscy9zZWNvbmTCsi5cblx0aW5lcnRpYURlY2VsZXJhdGlvbjogMzQwMCwgLy8gcHgvc14yXG5cblx0Ly8gQG9wdGlvbiBpbmVydGlhTWF4U3BlZWQ6IE51bWJlciA9IEluZmluaXR5XG5cdC8vIE1heCBzcGVlZCBvZiB0aGUgaW5lcnRpYWwgbW92ZW1lbnQsIGluIHBpeGVscy9zZWNvbmQuXG5cdGluZXJ0aWFNYXhTcGVlZDogSW5maW5pdHksIC8vIHB4L3NcblxuXHQvLyBAb3B0aW9uIGVhc2VMaW5lYXJpdHk6IE51bWJlciA9IDAuMlxuXHRlYXNlTGluZWFyaXR5OiAwLjIsXG5cblx0Ly8gVE9ETyByZWZhY3RvciwgbW92ZSB0byBDUlNcblx0Ly8gQG9wdGlvbiB3b3JsZENvcHlKdW1wOiBCb29sZWFuID0gZmFsc2Vcblx0Ly8gV2l0aCB0aGlzIG9wdGlvbiBlbmFibGVkLCB0aGUgbWFwIHRyYWNrcyB3aGVuIHlvdSBwYW4gdG8gYW5vdGhlciBcImNvcHlcIlxuXHQvLyBvZiB0aGUgd29ybGQgYW5kIHNlYW1sZXNzbHkganVtcHMgdG8gdGhlIG9yaWdpbmFsIG9uZSBzbyB0aGF0IGFsbCBvdmVybGF5c1xuXHQvLyBsaWtlIG1hcmtlcnMgYW5kIHZlY3RvciBsYXllcnMgYXJlIHN0aWxsIHZpc2libGUuXG5cdHdvcmxkQ29weUp1bXA6IGZhbHNlLFxuXG5cdC8vIEBvcHRpb24gbWF4Qm91bmRzVmlzY29zaXR5OiBOdW1iZXIgPSAwLjBcblx0Ly8gSWYgYG1heEJvdW5kc2AgaXMgc2V0LCB0aGlzIG9wdGlvbiB3aWxsIGNvbnRyb2wgaG93IHNvbGlkIHRoZSBib3VuZHNcblx0Ly8gYXJlIHdoZW4gZHJhZ2dpbmcgdGhlIG1hcCBhcm91bmQuIFRoZSBkZWZhdWx0IHZhbHVlIG9mIGAwLjBgIGFsbG93cyB0aGVcblx0Ly8gdXNlciB0byBkcmFnIG91dHNpZGUgdGhlIGJvdW5kcyBhdCBub3JtYWwgc3BlZWQsIGhpZ2hlciB2YWx1ZXMgd2lsbFxuXHQvLyBzbG93IGRvd24gbWFwIGRyYWdnaW5nIG91dHNpZGUgYm91bmRzLCBhbmQgYDEuMGAgbWFrZXMgdGhlIGJvdW5kcyBmdWxseVxuXHQvLyBzb2xpZCwgcHJldmVudGluZyB0aGUgdXNlciBmcm9tIGRyYWdnaW5nIG91dHNpZGUgdGhlIGJvdW5kcy5cblx0bWF4Qm91bmRzVmlzY29zaXR5OiAwLjBcbn0pO1xuXG5MLk1hcC5EcmFnID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9kcmFnZ2FibGUpIHtcblx0XHRcdHZhciBtYXAgPSB0aGlzLl9tYXA7XG5cblx0XHRcdHRoaXMuX2RyYWdnYWJsZSA9IG5ldyBMLkRyYWdnYWJsZShtYXAuX21hcFBhbmUsIG1hcC5fY29udGFpbmVyKTtcblxuXHRcdFx0dGhpcy5fZHJhZ2dhYmxlLm9uKHtcblx0XHRcdFx0ZG93bjogdGhpcy5fb25Eb3duLFxuXHRcdFx0XHRkcmFnc3RhcnQ6IHRoaXMuX29uRHJhZ1N0YXJ0LFxuXHRcdFx0XHRkcmFnOiB0aGlzLl9vbkRyYWcsXG5cdFx0XHRcdGRyYWdlbmQ6IHRoaXMuX29uRHJhZ0VuZFxuXHRcdFx0fSwgdGhpcyk7XG5cblx0XHRcdHRoaXMuX2RyYWdnYWJsZS5vbigncHJlZHJhZycsIHRoaXMuX29uUHJlRHJhZ0xpbWl0LCB0aGlzKTtcblx0XHRcdGlmIChtYXAub3B0aW9ucy53b3JsZENvcHlKdW1wKSB7XG5cdFx0XHRcdHRoaXMuX2RyYWdnYWJsZS5vbigncHJlZHJhZycsIHRoaXMuX29uUHJlRHJhZ1dyYXAsIHRoaXMpO1xuXHRcdFx0XHRtYXAub24oJ3pvb21lbmQnLCB0aGlzLl9vblpvb21FbmQsIHRoaXMpO1xuXG5cdFx0XHRcdG1hcC53aGVuUmVhZHkodGhpcy5fb25ab29tRW5kLCB0aGlzKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC1ncmFiIGxlYWZsZXQtdG91Y2gtZHJhZycpO1xuXHRcdHRoaXMuX2RyYWdnYWJsZS5lbmFibGUoKTtcblx0XHR0aGlzLl9wb3NpdGlvbnMgPSBbXTtcblx0XHR0aGlzLl90aW1lcyA9IFtdO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC1ncmFiJyk7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC10b3VjaC1kcmFnJyk7XG5cdFx0dGhpcy5fZHJhZ2dhYmxlLmRpc2FibGUoKTtcblx0fSxcblxuXHRtb3ZlZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9kcmFnZ2FibGUgJiYgdGhpcy5fZHJhZ2dhYmxlLl9tb3ZlZDtcblx0fSxcblxuXHRtb3Zpbmc6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fZHJhZ2dhYmxlICYmIHRoaXMuX2RyYWdnYWJsZS5fbW92aW5nO1xuXHR9LFxuXG5cdF9vbkRvd246IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9tYXAuX3N0b3AoKTtcblx0fSxcblxuXHRfb25EcmFnU3RhcnQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXG5cdFx0aWYgKHRoaXMuX21hcC5vcHRpb25zLm1heEJvdW5kcyAmJiB0aGlzLl9tYXAub3B0aW9ucy5tYXhCb3VuZHNWaXNjb3NpdHkpIHtcblx0XHRcdHZhciBib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyh0aGlzLl9tYXAub3B0aW9ucy5tYXhCb3VuZHMpO1xuXG5cdFx0XHR0aGlzLl9vZmZzZXRMaW1pdCA9IEwuYm91bmRzKFxuXHRcdFx0XHR0aGlzLl9tYXAubGF0TG5nVG9Db250YWluZXJQb2ludChib3VuZHMuZ2V0Tm9ydGhXZXN0KCkpLm11bHRpcGx5QnkoLTEpLFxuXHRcdFx0XHR0aGlzLl9tYXAubGF0TG5nVG9Db250YWluZXJQb2ludChib3VuZHMuZ2V0U291dGhFYXN0KCkpLm11bHRpcGx5QnkoLTEpXG5cdFx0XHRcdFx0LmFkZCh0aGlzLl9tYXAuZ2V0U2l6ZSgpKSk7XG5cblx0XHRcdHRoaXMuX3Zpc2Nvc2l0eSA9IE1hdGgubWluKDEuMCwgTWF0aC5tYXgoMC4wLCB0aGlzLl9tYXAub3B0aW9ucy5tYXhCb3VuZHNWaXNjb3NpdHkpKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fb2Zmc2V0TGltaXQgPSBudWxsO1xuXHRcdH1cblxuXHRcdG1hcFxuXHRcdCAgICAuZmlyZSgnbW92ZXN0YXJ0Jylcblx0XHQgICAgLmZpcmUoJ2RyYWdzdGFydCcpO1xuXG5cdFx0aWYgKG1hcC5vcHRpb25zLmluZXJ0aWEpIHtcblx0XHRcdHRoaXMuX3Bvc2l0aW9ucyA9IFtdO1xuXHRcdFx0dGhpcy5fdGltZXMgPSBbXTtcblx0XHR9XG5cdH0sXG5cblx0X29uRHJhZzogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAodGhpcy5fbWFwLm9wdGlvbnMuaW5lcnRpYSkge1xuXHRcdFx0dmFyIHRpbWUgPSB0aGlzLl9sYXN0VGltZSA9ICtuZXcgRGF0ZSgpLFxuXHRcdFx0ICAgIHBvcyA9IHRoaXMuX2xhc3RQb3MgPSB0aGlzLl9kcmFnZ2FibGUuX2Fic1BvcyB8fCB0aGlzLl9kcmFnZ2FibGUuX25ld1BvcztcblxuXHRcdFx0dGhpcy5fcG9zaXRpb25zLnB1c2gocG9zKTtcblx0XHRcdHRoaXMuX3RpbWVzLnB1c2godGltZSk7XG5cblx0XHRcdGlmICh0aW1lIC0gdGhpcy5fdGltZXNbMF0gPiA1MCkge1xuXHRcdFx0XHR0aGlzLl9wb3NpdGlvbnMuc2hpZnQoKTtcblx0XHRcdFx0dGhpcy5fdGltZXMuc2hpZnQoKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLl9tYXBcblx0XHQgICAgLmZpcmUoJ21vdmUnLCBlKVxuXHRcdCAgICAuZmlyZSgnZHJhZycsIGUpO1xuXHR9LFxuXG5cdF9vblpvb21FbmQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcHhDZW50ZXIgPSB0aGlzLl9tYXAuZ2V0U2l6ZSgpLmRpdmlkZUJ5KDIpLFxuXHRcdCAgICBweFdvcmxkQ2VudGVyID0gdGhpcy5fbWFwLmxhdExuZ1RvTGF5ZXJQb2ludChbMCwgMF0pO1xuXG5cdFx0dGhpcy5faW5pdGlhbFdvcmxkT2Zmc2V0ID0gcHhXb3JsZENlbnRlci5zdWJ0cmFjdChweENlbnRlcikueDtcblx0XHR0aGlzLl93b3JsZFdpZHRoID0gdGhpcy5fbWFwLmdldFBpeGVsV29ybGRCb3VuZHMoKS5nZXRTaXplKCkueDtcblx0fSxcblxuXHRfdmlzY291c0xpbWl0OiBmdW5jdGlvbiAodmFsdWUsIHRocmVzaG9sZCkge1xuXHRcdHJldHVybiB2YWx1ZSAtICh2YWx1ZSAtIHRocmVzaG9sZCkgKiB0aGlzLl92aXNjb3NpdHk7XG5cdH0sXG5cblx0X29uUHJlRHJhZ0xpbWl0OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl92aXNjb3NpdHkgfHwgIXRoaXMuX29mZnNldExpbWl0KSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIG9mZnNldCA9IHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zLnN1YnRyYWN0KHRoaXMuX2RyYWdnYWJsZS5fc3RhcnRQb3MpO1xuXG5cdFx0dmFyIGxpbWl0ID0gdGhpcy5fb2Zmc2V0TGltaXQ7XG5cdFx0aWYgKG9mZnNldC54IDwgbGltaXQubWluLngpIHsgb2Zmc2V0LnggPSB0aGlzLl92aXNjb3VzTGltaXQob2Zmc2V0LngsIGxpbWl0Lm1pbi54KTsgfVxuXHRcdGlmIChvZmZzZXQueSA8IGxpbWl0Lm1pbi55KSB7IG9mZnNldC55ID0gdGhpcy5fdmlzY291c0xpbWl0KG9mZnNldC55LCBsaW1pdC5taW4ueSk7IH1cblx0XHRpZiAob2Zmc2V0LnggPiBsaW1pdC5tYXgueCkgeyBvZmZzZXQueCA9IHRoaXMuX3Zpc2NvdXNMaW1pdChvZmZzZXQueCwgbGltaXQubWF4LngpOyB9XG5cdFx0aWYgKG9mZnNldC55ID4gbGltaXQubWF4LnkpIHsgb2Zmc2V0LnkgPSB0aGlzLl92aXNjb3VzTGltaXQob2Zmc2V0LnksIGxpbWl0Lm1heC55KTsgfVxuXG5cdFx0dGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3MgPSB0aGlzLl9kcmFnZ2FibGUuX3N0YXJ0UG9zLmFkZChvZmZzZXQpO1xuXHR9LFxuXG5cdF9vblByZURyYWdXcmFwOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gVE9ETyByZWZhY3RvciB0byBiZSBhYmxlIHRvIGFkanVzdCBtYXAgcGFuZSBwb3NpdGlvbiBhZnRlciB6b29tXG5cdFx0dmFyIHdvcmxkV2lkdGggPSB0aGlzLl93b3JsZFdpZHRoLFxuXHRcdCAgICBoYWxmV2lkdGggPSBNYXRoLnJvdW5kKHdvcmxkV2lkdGggLyAyKSxcblx0XHQgICAgZHggPSB0aGlzLl9pbml0aWFsV29ybGRPZmZzZXQsXG5cdFx0ICAgIHggPSB0aGlzLl9kcmFnZ2FibGUuX25ld1Bvcy54LFxuXHRcdCAgICBuZXdYMSA9ICh4IC0gaGFsZldpZHRoICsgZHgpICUgd29ybGRXaWR0aCArIGhhbGZXaWR0aCAtIGR4LFxuXHRcdCAgICBuZXdYMiA9ICh4ICsgaGFsZldpZHRoICsgZHgpICUgd29ybGRXaWR0aCAtIGhhbGZXaWR0aCAtIGR4LFxuXHRcdCAgICBuZXdYID0gTWF0aC5hYnMobmV3WDEgKyBkeCkgPCBNYXRoLmFicyhuZXdYMiArIGR4KSA/IG5ld1gxIDogbmV3WDI7XG5cblx0XHR0aGlzLl9kcmFnZ2FibGUuX2Fic1BvcyA9IHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zLmNsb25lKCk7XG5cdFx0dGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3MueCA9IG5ld1g7XG5cdH0sXG5cblx0X29uRHJhZ0VuZDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBvcHRpb25zID0gbWFwLm9wdGlvbnMsXG5cblx0XHQgICAgbm9JbmVydGlhID0gIW9wdGlvbnMuaW5lcnRpYSB8fCB0aGlzLl90aW1lcy5sZW5ndGggPCAyO1xuXG5cdFx0bWFwLmZpcmUoJ2RyYWdlbmQnLCBlKTtcblxuXHRcdGlmIChub0luZXJ0aWEpIHtcblx0XHRcdG1hcC5maXJlKCdtb3ZlZW5kJyk7XG5cblx0XHR9IGVsc2Uge1xuXG5cdFx0XHR2YXIgZGlyZWN0aW9uID0gdGhpcy5fbGFzdFBvcy5zdWJ0cmFjdCh0aGlzLl9wb3NpdGlvbnNbMF0pLFxuXHRcdFx0ICAgIGR1cmF0aW9uID0gKHRoaXMuX2xhc3RUaW1lIC0gdGhpcy5fdGltZXNbMF0pIC8gMTAwMCxcblx0XHRcdCAgICBlYXNlID0gb3B0aW9ucy5lYXNlTGluZWFyaXR5LFxuXG5cdFx0XHQgICAgc3BlZWRWZWN0b3IgPSBkaXJlY3Rpb24ubXVsdGlwbHlCeShlYXNlIC8gZHVyYXRpb24pLFxuXHRcdFx0ICAgIHNwZWVkID0gc3BlZWRWZWN0b3IuZGlzdGFuY2VUbyhbMCwgMF0pLFxuXG5cdFx0XHQgICAgbGltaXRlZFNwZWVkID0gTWF0aC5taW4ob3B0aW9ucy5pbmVydGlhTWF4U3BlZWQsIHNwZWVkKSxcblx0XHRcdCAgICBsaW1pdGVkU3BlZWRWZWN0b3IgPSBzcGVlZFZlY3Rvci5tdWx0aXBseUJ5KGxpbWl0ZWRTcGVlZCAvIHNwZWVkKSxcblxuXHRcdFx0ICAgIGRlY2VsZXJhdGlvbkR1cmF0aW9uID0gbGltaXRlZFNwZWVkIC8gKG9wdGlvbnMuaW5lcnRpYURlY2VsZXJhdGlvbiAqIGVhc2UpLFxuXHRcdFx0ICAgIG9mZnNldCA9IGxpbWl0ZWRTcGVlZFZlY3Rvci5tdWx0aXBseUJ5KC1kZWNlbGVyYXRpb25EdXJhdGlvbiAvIDIpLnJvdW5kKCk7XG5cblx0XHRcdGlmICghb2Zmc2V0LnggJiYgIW9mZnNldC55KSB7XG5cdFx0XHRcdG1hcC5maXJlKCdtb3ZlZW5kJyk7XG5cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdG9mZnNldCA9IG1hcC5fbGltaXRPZmZzZXQob2Zmc2V0LCBtYXAub3B0aW9ucy5tYXhCb3VuZHMpO1xuXG5cdFx0XHRcdEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKGZ1bmN0aW9uICgpIHtcblx0XHRcdFx0XHRtYXAucGFuQnkob2Zmc2V0LCB7XG5cdFx0XHRcdFx0XHRkdXJhdGlvbjogZGVjZWxlcmF0aW9uRHVyYXRpb24sXG5cdFx0XHRcdFx0XHRlYXNlTGluZWFyaXR5OiBlYXNlLFxuXHRcdFx0XHRcdFx0bm9Nb3ZlU3RhcnQ6IHRydWUsXG5cdFx0XHRcdFx0XHRhbmltYXRlOiB0cnVlXG5cdFx0XHRcdFx0fSk7XG5cdFx0XHRcdH0pO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgZHJhZ2dpbmc6IEhhbmRsZXJcbi8vIE1hcCBkcmFnZ2luZyBoYW5kbGVyIChieSBib3RoIG1vdXNlIGFuZCB0b3VjaCkuXG5MLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICdkcmFnZ2luZycsIEwuTWFwLkRyYWcpO1xuXG5cblxuLypcbiAqIEwuSGFuZGxlci5Eb3VibGVDbGlja1pvb20gaXMgdXNlZCB0byBoYW5kbGUgZG91YmxlLWNsaWNrIHpvb20gb24gdGhlIG1hcCwgZW5hYmxlZCBieSBkZWZhdWx0LlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5cbkwuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBvcHRpb24gZG91YmxlQ2xpY2tab29tOiBCb29sZWFufFN0cmluZyA9IHRydWVcblx0Ly8gV2hldGhlciB0aGUgbWFwIGNhbiBiZSB6b29tZWQgaW4gYnkgZG91YmxlIGNsaWNraW5nIG9uIGl0IGFuZFxuXHQvLyB6b29tZWQgb3V0IGJ5IGRvdWJsZSBjbGlja2luZyB3aGlsZSBob2xkaW5nIHNoaWZ0LiBJZiBwYXNzZWRcblx0Ly8gYCdjZW50ZXInYCwgZG91YmxlLWNsaWNrIHpvb20gd2lsbCB6b29tIHRvIHRoZSBjZW50ZXIgb2YgdGhlXG5cdC8vICB2aWV3IHJlZ2FyZGxlc3Mgb2Ygd2hlcmUgdGhlIG1vdXNlIHdhcy5cblx0ZG91YmxlQ2xpY2tab29tOiB0cnVlXG59KTtcblxuTC5NYXAuRG91YmxlQ2xpY2tab29tID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fbWFwLm9uKCdkYmxjbGljaycsIHRoaXMuX29uRG91YmxlQ2xpY2ssIHRoaXMpO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fbWFwLm9mZignZGJsY2xpY2snLCB0aGlzLl9vbkRvdWJsZUNsaWNrLCB0aGlzKTtcblx0fSxcblxuXHRfb25Eb3VibGVDbGljazogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBvbGRab29tID0gbWFwLmdldFpvb20oKSxcblx0XHQgICAgZGVsdGEgPSBtYXAub3B0aW9ucy56b29tRGVsdGEsXG5cdFx0ICAgIHpvb20gPSBlLm9yaWdpbmFsRXZlbnQuc2hpZnRLZXkgPyBvbGRab29tIC0gZGVsdGEgOiBvbGRab29tICsgZGVsdGE7XG5cblx0XHRpZiAobWFwLm9wdGlvbnMuZG91YmxlQ2xpY2tab29tID09PSAnY2VudGVyJykge1xuXHRcdFx0bWFwLnNldFpvb20oem9vbSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdG1hcC5zZXRab29tQXJvdW5kKGUuY29udGFpbmVyUG9pbnQsIHpvb20pO1xuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vL1xuLy8gTWFwIHByb3BlcnRpZXMgaW5jbHVkZSBpbnRlcmFjdGlvbiBoYW5kbGVycyB0aGF0IGFsbG93IHlvdSB0byBjb250cm9sXG4vLyBpbnRlcmFjdGlvbiBiZWhhdmlvciBpbiBydW50aW1lLCBlbmFibGluZyBvciBkaXNhYmxpbmcgY2VydGFpbiBmZWF0dXJlcyBzdWNoXG4vLyBhcyBkcmFnZ2luZyBvciB0b3VjaCB6b29tIChzZWUgYEhhbmRsZXJgIG1ldGhvZHMpLiBGb3IgZXhhbXBsZTpcbi8vXG4vLyBgYGBqc1xuLy8gbWFwLmRvdWJsZUNsaWNrWm9vbS5kaXNhYmxlKCk7XG4vLyBgYGBcbi8vXG4vLyBAcHJvcGVydHkgZG91YmxlQ2xpY2tab29tOiBIYW5kbGVyXG4vLyBEb3VibGUgY2xpY2sgem9vbSBoYW5kbGVyLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAnZG91YmxlQ2xpY2tab29tJywgTC5NYXAuRG91YmxlQ2xpY2tab29tKTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuU2Nyb2xsV2hlZWxab29tIGlzIHVzZWQgYnkgTC5NYXAgdG8gZW5hYmxlIG1vdXNlIHNjcm9sbCB3aGVlbCB6b29tIG9uIHRoZSBtYXAuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBzZWN0aW9uIE1vdXNld2hlZWwgb3B0aW9uc1xuXHQvLyBAb3B0aW9uIHNjcm9sbFdoZWVsWm9vbTogQm9vbGVhbnxTdHJpbmcgPSB0cnVlXG5cdC8vIFdoZXRoZXIgdGhlIG1hcCBjYW4gYmUgem9vbWVkIGJ5IHVzaW5nIHRoZSBtb3VzZSB3aGVlbC4gSWYgcGFzc2VkIGAnY2VudGVyJ2AsXG5cdC8vIGl0IHdpbGwgem9vbSB0byB0aGUgY2VudGVyIG9mIHRoZSB2aWV3IHJlZ2FyZGxlc3Mgb2Ygd2hlcmUgdGhlIG1vdXNlIHdhcy5cblx0c2Nyb2xsV2hlZWxab29tOiB0cnVlLFxuXG5cdC8vIEBvcHRpb24gd2hlZWxEZWJvdW5jZVRpbWU6IE51bWJlciA9IDQwXG5cdC8vIExpbWl0cyB0aGUgcmF0ZSBhdCB3aGljaCBhIHdoZWVsIGNhbiBmaXJlIChpbiBtaWxsaXNlY29uZHMpLiBCeSBkZWZhdWx0XG5cdC8vIHVzZXIgY2FuJ3Qgem9vbSB2aWEgd2hlZWwgbW9yZSBvZnRlbiB0aGFuIG9uY2UgcGVyIDQwIG1zLlxuXHR3aGVlbERlYm91bmNlVGltZTogNDAsXG5cblx0Ly8gQG9wdGlvbiB3aGVlbFB4UGVyWm9vbUxldmVsOiBOdW1iZXIgPSA2MFxuXHQvLyBIb3cgbWFueSBzY3JvbGwgcGl4ZWxzIChhcyByZXBvcnRlZCBieSBbTC5Eb21FdmVudC5nZXRXaGVlbERlbHRhXSgjZG9tZXZlbnQtZ2V0d2hlZWxkZWx0YSkpXG5cdC8vIG1lYW4gYSBjaGFuZ2Ugb2Ygb25lIGZ1bGwgem9vbSBsZXZlbC4gU21hbGxlciB2YWx1ZXMgd2lsbCBtYWtlIHdoZWVsLXpvb21pbmdcblx0Ly8gZmFzdGVyIChhbmQgdmljZSB2ZXJzYSkuXG5cdHdoZWVsUHhQZXJab29tTGV2ZWw6IDYwXG59KTtcblxuTC5NYXAuU2Nyb2xsV2hlZWxab29tID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21FdmVudC5vbih0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ21vdXNld2hlZWwnLCB0aGlzLl9vbldoZWVsU2Nyb2xsLCB0aGlzKTtcblxuXHRcdHRoaXMuX2RlbHRhID0gMDtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbW91c2V3aGVlbCcsIHRoaXMuX29uV2hlZWxTY3JvbGwsIHRoaXMpO1xuXHR9LFxuXG5cdF9vbldoZWVsU2Nyb2xsOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBkZWx0YSA9IEwuRG9tRXZlbnQuZ2V0V2hlZWxEZWx0YShlKTtcblxuXHRcdHZhciBkZWJvdW5jZSA9IHRoaXMuX21hcC5vcHRpb25zLndoZWVsRGVib3VuY2VUaW1lO1xuXG5cdFx0dGhpcy5fZGVsdGEgKz0gZGVsdGE7XG5cdFx0dGhpcy5fbGFzdE1vdXNlUG9zID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xuXG5cdFx0aWYgKCF0aGlzLl9zdGFydFRpbWUpIHtcblx0XHRcdHRoaXMuX3N0YXJ0VGltZSA9ICtuZXcgRGF0ZSgpO1xuXHRcdH1cblxuXHRcdHZhciBsZWZ0ID0gTWF0aC5tYXgoZGVib3VuY2UgLSAoK25ldyBEYXRlKCkgLSB0aGlzLl9zdGFydFRpbWUpLCAwKTtcblxuXHRcdGNsZWFyVGltZW91dCh0aGlzLl90aW1lcik7XG5cdFx0dGhpcy5fdGltZXIgPSBzZXRUaW1lb3V0KEwuYmluZCh0aGlzLl9wZXJmb3JtWm9vbSwgdGhpcyksIGxlZnQpO1xuXG5cdFx0TC5Eb21FdmVudC5zdG9wKGUpO1xuXHR9LFxuXG5cdF9wZXJmb3JtWm9vbTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXAsXG5cdFx0ICAgIHpvb20gPSBtYXAuZ2V0Wm9vbSgpLFxuXHRcdCAgICBzbmFwID0gdGhpcy5fbWFwLm9wdGlvbnMuem9vbVNuYXAgfHwgMDtcblxuXHRcdG1hcC5fc3RvcCgpOyAvLyBzdG9wIHBhbm5pbmcgYW5kIGZseSBhbmltYXRpb25zIGlmIGFueVxuXG5cdFx0Ly8gbWFwIHRoZSBkZWx0YSB3aXRoIGEgc2lnbW9pZCBmdW5jdGlvbiB0byAtNC4uNCByYW5nZSBsZWFuaW5nIG9uIC0xLi4xXG5cdFx0dmFyIGQyID0gdGhpcy5fZGVsdGEgLyAodGhpcy5fbWFwLm9wdGlvbnMud2hlZWxQeFBlclpvb21MZXZlbCAqIDQpLFxuXHRcdCAgICBkMyA9IDQgKiBNYXRoLmxvZygyIC8gKDEgKyBNYXRoLmV4cCgtTWF0aC5hYnMoZDIpKSkpIC8gTWF0aC5MTjIsXG5cdFx0ICAgIGQ0ID0gc25hcCA/IE1hdGguY2VpbChkMyAvIHNuYXApICogc25hcCA6IGQzLFxuXHRcdCAgICBkZWx0YSA9IG1hcC5fbGltaXRab29tKHpvb20gKyAodGhpcy5fZGVsdGEgPiAwID8gZDQgOiAtZDQpKSAtIHpvb207XG5cblx0XHR0aGlzLl9kZWx0YSA9IDA7XG5cdFx0dGhpcy5fc3RhcnRUaW1lID0gbnVsbDtcblxuXHRcdGlmICghZGVsdGEpIHsgcmV0dXJuOyB9XG5cblx0XHRpZiAobWFwLm9wdGlvbnMuc2Nyb2xsV2hlZWxab29tID09PSAnY2VudGVyJykge1xuXHRcdFx0bWFwLnNldFpvb20oem9vbSArIGRlbHRhKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0bWFwLnNldFpvb21Bcm91bmQodGhpcy5fbGFzdE1vdXNlUG9zLCB6b29tICsgZGVsdGEpO1xuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgc2Nyb2xsV2hlZWxab29tOiBIYW5kbGVyXG4vLyBTY3JvbGwgd2hlZWwgem9vbSBoYW5kbGVyLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAnc2Nyb2xsV2hlZWxab29tJywgTC5NYXAuU2Nyb2xsV2hlZWxab29tKTtcblxuXG5cbi8qXHJcbiAqIEV4dGVuZHMgdGhlIGV2ZW50IGhhbmRsaW5nIGNvZGUgd2l0aCBkb3VibGUgdGFwIHN1cHBvcnQgZm9yIG1vYmlsZSBicm93c2Vycy5cclxuICovXHJcblxyXG5MLmV4dGVuZChMLkRvbUV2ZW50LCB7XHJcblxyXG5cdF90b3VjaHN0YXJ0OiBMLkJyb3dzZXIubXNQb2ludGVyID8gJ01TUG9pbnRlckRvd24nIDogTC5Ccm93c2VyLnBvaW50ZXIgPyAncG9pbnRlcmRvd24nIDogJ3RvdWNoc3RhcnQnLFxyXG5cdF90b3VjaGVuZDogTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJVcCcgOiBMLkJyb3dzZXIucG9pbnRlciA/ICdwb2ludGVydXAnIDogJ3RvdWNoZW5kJyxcclxuXHJcblx0Ly8gaW5zcGlyZWQgYnkgWmVwdG8gdG91Y2ggY29kZSBieSBUaG9tYXMgRnVjaHNcclxuXHRhZGREb3VibGVUYXBMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgaGFuZGxlciwgaWQpIHtcclxuXHRcdHZhciBsYXN0LCB0b3VjaCxcclxuXHRcdCAgICBkb3VibGVUYXAgPSBmYWxzZSxcclxuXHRcdCAgICBkZWxheSA9IDI1MDtcclxuXHJcblx0XHRmdW5jdGlvbiBvblRvdWNoU3RhcnQoZSkge1xyXG5cdFx0XHR2YXIgY291bnQ7XHJcblxyXG5cdFx0XHRpZiAoTC5Ccm93c2VyLnBvaW50ZXIpIHtcclxuXHRcdFx0XHRjb3VudCA9IEwuRG9tRXZlbnQuX3BvaW50ZXJzQ291bnQ7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0Y291bnQgPSBlLnRvdWNoZXMubGVuZ3RoO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRpZiAoY291bnQgPiAxKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdFx0dmFyIG5vdyA9IERhdGUubm93KCksXHJcblx0XHRcdCAgICBkZWx0YSA9IG5vdyAtIChsYXN0IHx8IG5vdyk7XHJcblxyXG5cdFx0XHR0b3VjaCA9IGUudG91Y2hlcyA/IGUudG91Y2hlc1swXSA6IGU7XHJcblx0XHRcdGRvdWJsZVRhcCA9IChkZWx0YSA+IDAgJiYgZGVsdGEgPD0gZGVsYXkpO1xyXG5cdFx0XHRsYXN0ID0gbm93O1xyXG5cdFx0fVxyXG5cclxuXHRcdGZ1bmN0aW9uIG9uVG91Y2hFbmQoKSB7XHJcblx0XHRcdGlmIChkb3VibGVUYXAgJiYgIXRvdWNoLmNhbmNlbEJ1YmJsZSkge1xyXG5cdFx0XHRcdGlmIChMLkJyb3dzZXIucG9pbnRlcikge1xyXG5cdFx0XHRcdFx0Ly8gd29yayBhcm91bmQgLnR5cGUgYmVpbmcgcmVhZG9ubHkgd2l0aCBNU1BvaW50ZXIqIGV2ZW50c1xyXG5cdFx0XHRcdFx0dmFyIG5ld1RvdWNoID0ge30sXHJcblx0XHRcdFx0XHQgICAgcHJvcCwgaTtcclxuXHJcblx0XHRcdFx0XHRmb3IgKGkgaW4gdG91Y2gpIHtcclxuXHRcdFx0XHRcdFx0cHJvcCA9IHRvdWNoW2ldO1xyXG5cdFx0XHRcdFx0XHRuZXdUb3VjaFtpXSA9IHByb3AgJiYgcHJvcC5iaW5kID8gcHJvcC5iaW5kKHRvdWNoKSA6IHByb3A7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR0b3VjaCA9IG5ld1RvdWNoO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHR0b3VjaC50eXBlID0gJ2RibGNsaWNrJztcclxuXHRcdFx0XHRoYW5kbGVyKHRvdWNoKTtcclxuXHRcdFx0XHRsYXN0ID0gbnVsbDtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBwcmUgPSAnX2xlYWZsZXRfJyxcclxuXHRcdCAgICB0b3VjaHN0YXJ0ID0gdGhpcy5fdG91Y2hzdGFydCxcclxuXHRcdCAgICB0b3VjaGVuZCA9IHRoaXMuX3RvdWNoZW5kO1xyXG5cclxuXHRcdG9ialtwcmUgKyB0b3VjaHN0YXJ0ICsgaWRdID0gb25Ub3VjaFN0YXJ0O1xyXG5cdFx0b2JqW3ByZSArIHRvdWNoZW5kICsgaWRdID0gb25Ub3VjaEVuZDtcclxuXHRcdG9ialtwcmUgKyAnZGJsY2xpY2snICsgaWRdID0gaGFuZGxlcjtcclxuXHJcblx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcih0b3VjaHN0YXJ0LCBvblRvdWNoU3RhcnQsIGZhbHNlKTtcclxuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRvdWNoZW5kLCBvblRvdWNoRW5kLCBmYWxzZSk7XHJcblxyXG5cdFx0Ly8gT24gc29tZSBwbGF0Zm9ybXMgKG5vdGFibHksIGNocm9tZSBvbiB3aW4xMCArIHRvdWNoc2NyZWVuICsgbW91c2UpLFxyXG5cdFx0Ly8gdGhlIGJyb3dzZXIgZG9lc24ndCBmaXJlIHRvdWNoZW5kL3BvaW50ZXJ1cCBldmVudHMgYnV0IGRvZXMgZmlyZVxyXG5cdFx0Ly8gbmF0aXZlIGRibGNsaWNrcy4gU2VlICM0MTI3LlxyXG5cdFx0aWYgKCFMLkJyb3dzZXIuZWRnZSkge1xyXG5cdFx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcignZGJsY2xpY2snLCBoYW5kbGVyLCBmYWxzZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0cmVtb3ZlRG91YmxlVGFwTGlzdGVuZXI6IGZ1bmN0aW9uIChvYmosIGlkKSB7XHJcblx0XHR2YXIgcHJlID0gJ19sZWFmbGV0XycsXHJcblx0XHQgICAgdG91Y2hzdGFydCA9IG9ialtwcmUgKyB0aGlzLl90b3VjaHN0YXJ0ICsgaWRdLFxyXG5cdFx0ICAgIHRvdWNoZW5kID0gb2JqW3ByZSArIHRoaXMuX3RvdWNoZW5kICsgaWRdLFxyXG5cdFx0ICAgIGRibGNsaWNrID0gb2JqW3ByZSArICdkYmxjbGljaycgKyBpZF07XHJcblxyXG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5fdG91Y2hzdGFydCwgdG91Y2hzdGFydCwgZmFsc2UpO1xyXG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5fdG91Y2hlbmQsIHRvdWNoZW5kLCBmYWxzZSk7XHJcblx0XHRpZiAoIUwuQnJvd3Nlci5lZGdlKSB7XHJcblx0XHRcdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGRibGNsaWNrLCBmYWxzZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fVxyXG59KTtcclxuXG5cblxuLypcbiAqIEV4dGVuZHMgTC5Eb21FdmVudCB0byBwcm92aWRlIHRvdWNoIHN1cHBvcnQgZm9yIEludGVybmV0IEV4cGxvcmVyIGFuZCBXaW5kb3dzLWJhc2VkIGRldmljZXMuXG4gKi9cblxuTC5leHRlbmQoTC5Eb21FdmVudCwge1xuXG5cdFBPSU5URVJfRE9XTjogICBMLkJyb3dzZXIubXNQb2ludGVyID8gJ01TUG9pbnRlckRvd24nICAgOiAncG9pbnRlcmRvd24nLFxuXHRQT0lOVEVSX01PVkU6ICAgTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJNb3ZlJyAgIDogJ3BvaW50ZXJtb3ZlJyxcblx0UE9JTlRFUl9VUDogICAgIEwuQnJvd3Nlci5tc1BvaW50ZXIgPyAnTVNQb2ludGVyVXAnICAgICA6ICdwb2ludGVydXAnLFxuXHRQT0lOVEVSX0NBTkNFTDogTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJDYW5jZWwnIDogJ3BvaW50ZXJjYW5jZWwnLFxuXHRUQUdfV0hJVEVfTElTVDogWydJTlBVVCcsICdTRUxFQ1QnLCAnT1BUSU9OJ10sXG5cblx0X3BvaW50ZXJzOiB7fSxcblx0X3BvaW50ZXJzQ291bnQ6IDAsXG5cblx0Ly8gUHJvdmlkZXMgYSB0b3VjaCBldmVudHMgd3JhcHBlciBmb3IgKG1zKXBvaW50ZXIgZXZlbnRzLlxuXHQvLyByZWYgaHR0cDovL3d3dy53My5vcmcvVFIvcG9pbnRlcmV2ZW50cy8gaHR0cHM6Ly93d3cudzMub3JnL0J1Z3MvUHVibGljL3Nob3dfYnVnLmNnaT9pZD0yMjg5MFxuXG5cdGFkZFBvaW50ZXJMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgaGFuZGxlciwgaWQpIHtcblxuXHRcdGlmICh0eXBlID09PSAndG91Y2hzdGFydCcpIHtcblx0XHRcdHRoaXMuX2FkZFBvaW50ZXJTdGFydChvYmosIGhhbmRsZXIsIGlkKTtcblxuXHRcdH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNobW92ZScpIHtcblx0XHRcdHRoaXMuX2FkZFBvaW50ZXJNb3ZlKG9iaiwgaGFuZGxlciwgaWQpO1xuXG5cdFx0fSBlbHNlIGlmICh0eXBlID09PSAndG91Y2hlbmQnKSB7XG5cdFx0XHR0aGlzLl9hZGRQb2ludGVyRW5kKG9iaiwgaGFuZGxlciwgaWQpO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdHJlbW92ZVBvaW50ZXJMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgaWQpIHtcblx0XHR2YXIgaGFuZGxlciA9IG9ialsnX2xlYWZsZXRfJyArIHR5cGUgKyBpZF07XG5cblx0XHRpZiAodHlwZSA9PT0gJ3RvdWNoc3RhcnQnKSB7XG5cdFx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfRE9XTiwgaGFuZGxlciwgZmFsc2UpO1xuXG5cdFx0fSBlbHNlIGlmICh0eXBlID09PSAndG91Y2htb3ZlJykge1xuXHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX01PVkUsIGhhbmRsZXIsIGZhbHNlKTtcblxuXHRcdH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNoZW5kJykge1xuXHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX1VQLCBoYW5kbGVyLCBmYWxzZSk7XG5cdFx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfQ0FOQ0VMLCBoYW5kbGVyLCBmYWxzZSk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X2FkZFBvaW50ZXJTdGFydDogZnVuY3Rpb24gKG9iaiwgaGFuZGxlciwgaWQpIHtcblx0XHR2YXIgb25Eb3duID0gTC5iaW5kKGZ1bmN0aW9uIChlKSB7XG5cdFx0XHRpZiAoZS5wb2ludGVyVHlwZSAhPT0gJ21vdXNlJyAmJiBlLnBvaW50ZXJUeXBlICE9PSBlLk1TUE9JTlRFUl9UWVBFX01PVVNFKSB7XG5cdFx0XHRcdC8vIEluIElFMTEsIHNvbWUgdG91Y2ggZXZlbnRzIG5lZWRzIHRvIGZpcmUgZm9yIGZvcm0gY29udHJvbHMsIG9yXG5cdFx0XHRcdC8vIHRoZSBjb250cm9scyB3aWxsIHN0b3Agd29ya2luZy4gV2Uga2VlcCBhIHdoaXRlbGlzdCBvZiB0YWcgbmFtZXMgdGhhdFxuXHRcdFx0XHQvLyBuZWVkIHRoZXNlIGV2ZW50cy4gRm9yIG90aGVyIHRhcmdldCB0YWdzLCB3ZSBwcmV2ZW50IGRlZmF1bHQgb24gdGhlIGV2ZW50LlxuXHRcdFx0XHRpZiAodGhpcy5UQUdfV0hJVEVfTElTVC5pbmRleE9mKGUudGFyZ2V0LnRhZ05hbWUpIDwgMCkge1xuXHRcdFx0XHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX2hhbmRsZVBvaW50ZXIoZSwgaGFuZGxlcik7XG5cdFx0fSwgdGhpcyk7XG5cblx0XHRvYmpbJ19sZWFmbGV0X3RvdWNoc3RhcnQnICsgaWRdID0gb25Eb3duO1xuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9ET1dOLCBvbkRvd24sIGZhbHNlKTtcblxuXHRcdC8vIG5lZWQgdG8ga2VlcCB0cmFjayBvZiB3aGF0IHBvaW50ZXJzIGFuZCBob3cgbWFueSBhcmUgYWN0aXZlIHRvIHByb3ZpZGUgZS50b3VjaGVzIGVtdWxhdGlvblxuXHRcdGlmICghdGhpcy5fcG9pbnRlckRvY0xpc3RlbmVyKSB7XG5cdFx0XHR2YXIgcG9pbnRlclVwID0gTC5iaW5kKHRoaXMuX2dsb2JhbFBvaW50ZXJVcCwgdGhpcyk7XG5cblx0XHRcdC8vIHdlIGxpc3RlbiBkb2N1bWVudEVsZW1lbnQgYXMgYW55IGRyYWdzIHRoYXQgZW5kIGJ5IG1vdmluZyB0aGUgdG91Y2ggb2ZmIHRoZSBzY3JlZW4gZ2V0IGZpcmVkIHRoZXJlXG5cdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfRE9XTiwgTC5iaW5kKHRoaXMuX2dsb2JhbFBvaW50ZXJEb3duLCB0aGlzKSwgdHJ1ZSk7XG5cdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfTU9WRSwgTC5iaW5kKHRoaXMuX2dsb2JhbFBvaW50ZXJNb3ZlLCB0aGlzKSwgdHJ1ZSk7XG5cdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfVVAsIHBvaW50ZXJVcCwgdHJ1ZSk7XG5cdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfQ0FOQ0VMLCBwb2ludGVyVXAsIHRydWUpO1xuXG5cdFx0XHR0aGlzLl9wb2ludGVyRG9jTGlzdGVuZXIgPSB0cnVlO1xuXHRcdH1cblx0fSxcblxuXHRfZ2xvYmFsUG9pbnRlckRvd246IGZ1bmN0aW9uIChlKSB7XG5cdFx0dGhpcy5fcG9pbnRlcnNbZS5wb2ludGVySWRdID0gZTtcblx0XHR0aGlzLl9wb2ludGVyc0NvdW50Kys7XG5cdH0sXG5cblx0X2dsb2JhbFBvaW50ZXJNb3ZlOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmICh0aGlzLl9wb2ludGVyc1tlLnBvaW50ZXJJZF0pIHtcblx0XHRcdHRoaXMuX3BvaW50ZXJzW2UucG9pbnRlcklkXSA9IGU7XG5cdFx0fVxuXHR9LFxuXG5cdF9nbG9iYWxQb2ludGVyVXA6IGZ1bmN0aW9uIChlKSB7XG5cdFx0ZGVsZXRlIHRoaXMuX3BvaW50ZXJzW2UucG9pbnRlcklkXTtcblx0XHR0aGlzLl9wb2ludGVyc0NvdW50LS07XG5cdH0sXG5cblx0X2hhbmRsZVBvaW50ZXI6IGZ1bmN0aW9uIChlLCBoYW5kbGVyKSB7XG5cdFx0ZS50b3VjaGVzID0gW107XG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9wb2ludGVycykge1xuXHRcdFx0ZS50b3VjaGVzLnB1c2godGhpcy5fcG9pbnRlcnNbaV0pO1xuXHRcdH1cblx0XHRlLmNoYW5nZWRUb3VjaGVzID0gW2VdO1xuXG5cdFx0aGFuZGxlcihlKTtcblx0fSxcblxuXHRfYWRkUG9pbnRlck1vdmU6IGZ1bmN0aW9uIChvYmosIGhhbmRsZXIsIGlkKSB7XG5cdFx0dmFyIG9uTW92ZSA9IEwuYmluZChmdW5jdGlvbiAoZSkge1xuXHRcdFx0Ly8gZG9uJ3QgZmlyZSB0b3VjaCBtb3ZlcyB3aGVuIG1vdXNlIGlzbid0IGRvd25cblx0XHRcdGlmICgoZS5wb2ludGVyVHlwZSA9PT0gZS5NU1BPSU5URVJfVFlQRV9NT1VTRSB8fCBlLnBvaW50ZXJUeXBlID09PSAnbW91c2UnKSAmJiBlLmJ1dHRvbnMgPT09IDApIHsgcmV0dXJuOyB9XG5cblx0XHRcdHRoaXMuX2hhbmRsZVBvaW50ZXIoZSwgaGFuZGxlcik7XG5cdFx0fSwgdGhpcyk7XG5cblx0XHRvYmpbJ19sZWFmbGV0X3RvdWNobW92ZScgKyBpZF0gPSBvbk1vdmU7XG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX01PVkUsIG9uTW92ZSwgZmFsc2UpO1xuXHR9LFxuXG5cdF9hZGRQb2ludGVyRW5kOiBmdW5jdGlvbiAob2JqLCBoYW5kbGVyLCBpZCkge1xuXHRcdHZhciBvblVwID0gTC5iaW5kKGZ1bmN0aW9uIChlKSB7XG5cdFx0XHR0aGlzLl9oYW5kbGVQb2ludGVyKGUsIGhhbmRsZXIpO1xuXHRcdH0sIHRoaXMpO1xuXG5cdFx0b2JqWydfbGVhZmxldF90b3VjaGVuZCcgKyBpZF0gPSBvblVwO1xuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9VUCwgb25VcCwgZmFsc2UpO1xuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9DQU5DRUwsIG9uVXAsIGZhbHNlKTtcblx0fVxufSk7XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLlRvdWNoWm9vbSBpcyB1c2VkIGJ5IEwuTWFwIHRvIGFkZCBwaW5jaCB6b29tIG9uIHN1cHBvcnRlZCBtb2JpbGUgYnJvd3NlcnMuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBzZWN0aW9uIFRvdWNoIGludGVyYWN0aW9uIG9wdGlvbnNcblx0Ly8gQG9wdGlvbiB0b3VjaFpvb206IEJvb2xlYW58U3RyaW5nID0gKlxuXHQvLyBXaGV0aGVyIHRoZSBtYXAgY2FuIGJlIHpvb21lZCBieSB0b3VjaC1kcmFnZ2luZyB3aXRoIHR3byBmaW5nZXJzLiBJZlxuXHQvLyBwYXNzZWQgYCdjZW50ZXInYCwgaXQgd2lsbCB6b29tIHRvIHRoZSBjZW50ZXIgb2YgdGhlIHZpZXcgcmVnYXJkbGVzcyBvZlxuXHQvLyB3aGVyZSB0aGUgdG91Y2ggZXZlbnRzIChmaW5nZXJzKSB3ZXJlLiBFbmFibGVkIGZvciB0b3VjaC1jYXBhYmxlIHdlYlxuXHQvLyBicm93c2VycyBleGNlcHQgZm9yIG9sZCBBbmRyb2lkcy5cblx0dG91Y2hab29tOiBMLkJyb3dzZXIudG91Y2ggJiYgIUwuQnJvd3Nlci5hbmRyb2lkMjMsXG5cblx0Ly8gQG9wdGlvbiBib3VuY2VBdFpvb21MaW1pdHM6IEJvb2xlYW4gPSB0cnVlXG5cdC8vIFNldCBpdCB0byBmYWxzZSBpZiB5b3UgZG9uJ3Qgd2FudCB0aGUgbWFwIHRvIHpvb20gYmV5b25kIG1pbi9tYXggem9vbVxuXHQvLyBhbmQgdGhlbiBib3VuY2UgYmFjayB3aGVuIHBpbmNoLXpvb21pbmcuXG5cdGJvdW5jZUF0Wm9vbUxpbWl0czogdHJ1ZVxufSk7XG5cbkwuTWFwLlRvdWNoWm9vbSA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtdG91Y2gtem9vbScpO1xuXHRcdEwuRG9tRXZlbnQub24odGhpcy5fbWFwLl9jb250YWluZXIsICd0b3VjaHN0YXJ0JywgdGhpcy5fb25Ub3VjaFN0YXJ0LCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtdG91Y2gtem9vbScpO1xuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX21hcC5fY29udGFpbmVyLCAndG91Y2hzdGFydCcsIHRoaXMuX29uVG91Y2hTdGFydCwgdGhpcyk7XG5cdH0sXG5cblx0X29uVG91Y2hTdGFydDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXHRcdGlmICghZS50b3VjaGVzIHx8IGUudG91Y2hlcy5sZW5ndGggIT09IDIgfHwgbWFwLl9hbmltYXRpbmdab29tIHx8IHRoaXMuX3pvb21pbmcpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgcDEgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzBdKSxcblx0XHQgICAgcDIgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzFdKTtcblxuXHRcdHRoaXMuX2NlbnRlclBvaW50ID0gbWFwLmdldFNpemUoKS5fZGl2aWRlQnkoMik7XG5cdFx0dGhpcy5fc3RhcnRMYXRMbmcgPSBtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyh0aGlzLl9jZW50ZXJQb2ludCk7XG5cdFx0aWYgKG1hcC5vcHRpb25zLnRvdWNoWm9vbSAhPT0gJ2NlbnRlcicpIHtcblx0XHRcdHRoaXMuX3BpbmNoU3RhcnRMYXRMbmcgPSBtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyhwMS5hZGQocDIpLl9kaXZpZGVCeSgyKSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fc3RhcnREaXN0ID0gcDEuZGlzdGFuY2VUbyhwMik7XG5cdFx0dGhpcy5fc3RhcnRab29tID0gbWFwLmdldFpvb20oKTtcblxuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XG5cdFx0dGhpcy5fem9vbWluZyA9IHRydWU7XG5cblx0XHRtYXAuX3N0b3AoKTtcblxuXHRcdEwuRG9tRXZlbnRcblx0XHQgICAgLm9uKGRvY3VtZW50LCAndG91Y2htb3ZlJywgdGhpcy5fb25Ub3VjaE1vdmUsIHRoaXMpXG5cdFx0ICAgIC5vbihkb2N1bWVudCwgJ3RvdWNoZW5kJywgdGhpcy5fb25Ub3VjaEVuZCwgdGhpcyk7XG5cblx0XHRMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xuXHR9LFxuXG5cdF9vblRvdWNoTW92ZTogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIWUudG91Y2hlcyB8fCBlLnRvdWNoZXMubGVuZ3RoICE9PSAyIHx8ICF0aGlzLl96b29taW5nKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgcDEgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzBdKSxcblx0XHQgICAgcDIgPSBtYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZS50b3VjaGVzWzFdKSxcblx0XHQgICAgc2NhbGUgPSBwMS5kaXN0YW5jZVRvKHAyKSAvIHRoaXMuX3N0YXJ0RGlzdDtcblxuXG5cdFx0dGhpcy5fem9vbSA9IG1hcC5nZXRTY2FsZVpvb20oc2NhbGUsIHRoaXMuX3N0YXJ0Wm9vbSk7XG5cblx0XHRpZiAoIW1hcC5vcHRpb25zLmJvdW5jZUF0Wm9vbUxpbWl0cyAmJiAoXG5cdFx0XHQodGhpcy5fem9vbSA8IG1hcC5nZXRNaW5ab29tKCkgJiYgc2NhbGUgPCAxKSB8fFxuXHRcdFx0KHRoaXMuX3pvb20gPiBtYXAuZ2V0TWF4Wm9vbSgpICYmIHNjYWxlID4gMSkpKSB7XG5cdFx0XHR0aGlzLl96b29tID0gbWFwLl9saW1pdFpvb20odGhpcy5fem9vbSk7XG5cdFx0fVxuXG5cdFx0aWYgKG1hcC5vcHRpb25zLnRvdWNoWm9vbSA9PT0gJ2NlbnRlcicpIHtcblx0XHRcdHRoaXMuX2NlbnRlciA9IHRoaXMuX3N0YXJ0TGF0TG5nO1xuXHRcdFx0aWYgKHNjYWxlID09PSAxKSB7IHJldHVybjsgfVxuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBHZXQgZGVsdGEgZnJvbSBwaW5jaCB0byBjZW50ZXIsIHNvIGNlbnRlckxhdExuZyBpcyBkZWx0YSBhcHBsaWVkIHRvIGluaXRpYWwgcGluY2hMYXRMbmdcblx0XHRcdHZhciBkZWx0YSA9IHAxLl9hZGQocDIpLl9kaXZpZGVCeSgyKS5fc3VidHJhY3QodGhpcy5fY2VudGVyUG9pbnQpO1xuXHRcdFx0aWYgKHNjYWxlID09PSAxICYmIGRlbHRhLnggPT09IDAgJiYgZGVsdGEueSA9PT0gMCkgeyByZXR1cm47IH1cblx0XHRcdHRoaXMuX2NlbnRlciA9IG1hcC51bnByb2plY3QobWFwLnByb2plY3QodGhpcy5fcGluY2hTdGFydExhdExuZywgdGhpcy5fem9vbSkuc3VidHJhY3QoZGVsdGEpLCB0aGlzLl96b29tKTtcblx0XHR9XG5cblx0XHRpZiAoIXRoaXMuX21vdmVkKSB7XG5cdFx0XHRtYXAuX21vdmVTdGFydCh0cnVlKTtcblx0XHRcdHRoaXMuX21vdmVkID0gdHJ1ZTtcblx0XHR9XG5cblx0XHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lKHRoaXMuX2FuaW1SZXF1ZXN0KTtcblxuXHRcdHZhciBtb3ZlRm4gPSBMLmJpbmQobWFwLl9tb3ZlLCBtYXAsIHRoaXMuX2NlbnRlciwgdGhpcy5fem9vbSwge3BpbmNoOiB0cnVlLCByb3VuZDogZmFsc2V9KTtcblx0XHR0aGlzLl9hbmltUmVxdWVzdCA9IEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKG1vdmVGbiwgdGhpcywgdHJ1ZSk7XG5cblx0XHRMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xuXHR9LFxuXG5cdF9vblRvdWNoRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9tb3ZlZCB8fCAhdGhpcy5fem9vbWluZykge1xuXHRcdFx0dGhpcy5fem9vbWluZyA9IGZhbHNlO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHRoaXMuX3pvb21pbmcgPSBmYWxzZTtcblx0XHRMLlV0aWwuY2FuY2VsQW5pbUZyYW1lKHRoaXMuX2FuaW1SZXF1ZXN0KTtcblxuXHRcdEwuRG9tRXZlbnRcblx0XHQgICAgLm9mZihkb2N1bWVudCwgJ3RvdWNobW92ZScsIHRoaXMuX29uVG91Y2hNb3ZlKVxuXHRcdCAgICAub2ZmKGRvY3VtZW50LCAndG91Y2hlbmQnLCB0aGlzLl9vblRvdWNoRW5kKTtcblxuXHRcdC8vIFBpbmNoIHVwZGF0ZXMgR3JpZExheWVycycgbGV2ZWxzIG9ubHkgd2hlbiB6b29tU25hcCBpcyBvZmYsIHNvIHpvb21TbmFwIGJlY29tZXMgbm9VcGRhdGUuXG5cdFx0aWYgKHRoaXMuX21hcC5vcHRpb25zLnpvb21BbmltYXRpb24pIHtcblx0XHRcdHRoaXMuX21hcC5fYW5pbWF0ZVpvb20odGhpcy5fY2VudGVyLCB0aGlzLl9tYXAuX2xpbWl0Wm9vbSh0aGlzLl96b29tKSwgdHJ1ZSwgdGhpcy5fbWFwLm9wdGlvbnMuem9vbVNuYXApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9tYXAuX3Jlc2V0Vmlldyh0aGlzLl9jZW50ZXIsIHRoaXMuX21hcC5fbGltaXRab29tKHRoaXMuX3pvb20pKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IHRvdWNoWm9vbTogSGFuZGxlclxuLy8gVG91Y2ggem9vbSBoYW5kbGVyLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAndG91Y2hab29tJywgTC5NYXAuVG91Y2hab29tKTtcblxuXG5cbi8qXG4gKiBMLk1hcC5UYXAgaXMgdXNlZCB0byBlbmFibGUgbW9iaWxlIGhhY2tzIGxpa2UgcXVpY2sgdGFwcyBhbmQgbG9uZyBob2xkLlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAc2VjdGlvbiBUb3VjaCBpbnRlcmFjdGlvbiBvcHRpb25zXG5cdC8vIEBvcHRpb24gdGFwOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBFbmFibGVzIG1vYmlsZSBoYWNrcyBmb3Igc3VwcG9ydGluZyBpbnN0YW50IHRhcHMgKGZpeGluZyAyMDBtcyBjbGlja1xuXHQvLyBkZWxheSBvbiBpT1MvQW5kcm9pZCkgYW5kIHRvdWNoIGhvbGRzIChmaXJlZCBhcyBgY29udGV4dG1lbnVgIGV2ZW50cykuXG5cdHRhcDogdHJ1ZSxcblxuXHQvLyBAb3B0aW9uIHRhcFRvbGVyYW5jZTogTnVtYmVyID0gMTVcblx0Ly8gVGhlIG1heCBudW1iZXIgb2YgcGl4ZWxzIGEgdXNlciBjYW4gc2hpZnQgaGlzIGZpbmdlciBkdXJpbmcgdG91Y2hcblx0Ly8gZm9yIGl0IHRvIGJlIGNvbnNpZGVyZWQgYSB2YWxpZCB0YXAuXG5cdHRhcFRvbGVyYW5jZTogMTVcbn0pO1xuXG5MLk1hcC5UYXAgPSBMLkhhbmRsZXIuZXh0ZW5kKHtcblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9uKHRoaXMuX21hcC5fY29udGFpbmVyLCAndG91Y2hzdGFydCcsIHRoaXMuX29uRG93biwgdGhpcyk7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ3RvdWNoc3RhcnQnLCB0aGlzLl9vbkRvd24sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbkRvd246IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKCFlLnRvdWNoZXMpIHsgcmV0dXJuOyB9XG5cblx0XHRMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xuXG5cdFx0dGhpcy5fZmlyZUNsaWNrID0gdHJ1ZTtcblxuXHRcdC8vIGRvbid0IHNpbXVsYXRlIGNsaWNrIG9yIHRyYWNrIGxvbmdwcmVzcyBpZiBtb3JlIHRoYW4gMSB0b3VjaFxuXHRcdGlmIChlLnRvdWNoZXMubGVuZ3RoID4gMSkge1xuXHRcdFx0dGhpcy5fZmlyZUNsaWNrID0gZmFsc2U7XG5cdFx0XHRjbGVhclRpbWVvdXQodGhpcy5faG9sZFRpbWVvdXQpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBmaXJzdCA9IGUudG91Y2hlc1swXSxcblx0XHQgICAgZWwgPSBmaXJzdC50YXJnZXQ7XG5cblx0XHR0aGlzLl9zdGFydFBvcyA9IHRoaXMuX25ld1BvcyA9IG5ldyBMLlBvaW50KGZpcnN0LmNsaWVudFgsIGZpcnN0LmNsaWVudFkpO1xuXG5cdFx0Ly8gaWYgdG91Y2hpbmcgYSBsaW5rLCBoaWdobGlnaHQgaXRcblx0XHRpZiAoZWwudGFnTmFtZSAmJiBlbC50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09ICdhJykge1xuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKGVsLCAnbGVhZmxldC1hY3RpdmUnKTtcblx0XHR9XG5cblx0XHQvLyBzaW11bGF0ZSBsb25nIGhvbGQgYnV0IHNldHRpbmcgYSB0aW1lb3V0XG5cdFx0dGhpcy5faG9sZFRpbWVvdXQgPSBzZXRUaW1lb3V0KEwuYmluZChmdW5jdGlvbiAoKSB7XG5cdFx0XHRpZiAodGhpcy5faXNUYXBWYWxpZCgpKSB7XG5cdFx0XHRcdHRoaXMuX2ZpcmVDbGljayA9IGZhbHNlO1xuXHRcdFx0XHR0aGlzLl9vblVwKCk7XG5cdFx0XHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ2NvbnRleHRtZW51JywgZmlyc3QpO1xuXHRcdFx0fVxuXHRcdH0sIHRoaXMpLCAxMDAwKTtcblxuXHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ21vdXNlZG93bicsIGZpcnN0KTtcblxuXHRcdEwuRG9tRXZlbnQub24oZG9jdW1lbnQsIHtcblx0XHRcdHRvdWNobW92ZTogdGhpcy5fb25Nb3ZlLFxuXHRcdFx0dG91Y2hlbmQ6IHRoaXMuX29uVXBcblx0XHR9LCB0aGlzKTtcblx0fSxcblxuXHRfb25VcDogZnVuY3Rpb24gKGUpIHtcblx0XHRjbGVhclRpbWVvdXQodGhpcy5faG9sZFRpbWVvdXQpO1xuXG5cdFx0TC5Eb21FdmVudC5vZmYoZG9jdW1lbnQsIHtcblx0XHRcdHRvdWNobW92ZTogdGhpcy5fb25Nb3ZlLFxuXHRcdFx0dG91Y2hlbmQ6IHRoaXMuX29uVXBcblx0XHR9LCB0aGlzKTtcblxuXHRcdGlmICh0aGlzLl9maXJlQ2xpY2sgJiYgZSAmJiBlLmNoYW5nZWRUb3VjaGVzKSB7XG5cblx0XHRcdHZhciBmaXJzdCA9IGUuY2hhbmdlZFRvdWNoZXNbMF0sXG5cdFx0XHQgICAgZWwgPSBmaXJzdC50YXJnZXQ7XG5cblx0XHRcdGlmIChlbCAmJiBlbC50YWdOYW1lICYmIGVsLnRhZ05hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2EnKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhlbCwgJ2xlYWZsZXQtYWN0aXZlJyk7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ21vdXNldXAnLCBmaXJzdCk7XG5cblx0XHRcdC8vIHNpbXVsYXRlIGNsaWNrIGlmIHRoZSB0b3VjaCBkaWRuJ3QgbW92ZSB0b28gbXVjaFxuXHRcdFx0aWYgKHRoaXMuX2lzVGFwVmFsaWQoKSkge1xuXHRcdFx0XHR0aGlzLl9zaW11bGF0ZUV2ZW50KCdjbGljaycsIGZpcnN0KTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0X2lzVGFwVmFsaWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fbmV3UG9zLmRpc3RhbmNlVG8odGhpcy5fc3RhcnRQb3MpIDw9IHRoaXMuX21hcC5vcHRpb25zLnRhcFRvbGVyYW5jZTtcblx0fSxcblxuXHRfb25Nb3ZlOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBmaXJzdCA9IGUudG91Y2hlc1swXTtcblx0XHR0aGlzLl9uZXdQb3MgPSBuZXcgTC5Qb2ludChmaXJzdC5jbGllbnRYLCBmaXJzdC5jbGllbnRZKTtcblx0XHR0aGlzLl9zaW11bGF0ZUV2ZW50KCdtb3VzZW1vdmUnLCBmaXJzdCk7XG5cdH0sXG5cblx0X3NpbXVsYXRlRXZlbnQ6IGZ1bmN0aW9uICh0eXBlLCBlKSB7XG5cdFx0dmFyIHNpbXVsYXRlZEV2ZW50ID0gZG9jdW1lbnQuY3JlYXRlRXZlbnQoJ01vdXNlRXZlbnRzJyk7XG5cblx0XHRzaW11bGF0ZWRFdmVudC5fc2ltdWxhdGVkID0gdHJ1ZTtcblx0XHRlLnRhcmdldC5fc2ltdWxhdGVkQ2xpY2sgPSB0cnVlO1xuXG5cdFx0c2ltdWxhdGVkRXZlbnQuaW5pdE1vdXNlRXZlbnQoXG5cdFx0ICAgICAgICB0eXBlLCB0cnVlLCB0cnVlLCB3aW5kb3csIDEsXG5cdFx0ICAgICAgICBlLnNjcmVlblgsIGUuc2NyZWVuWSxcblx0XHQgICAgICAgIGUuY2xpZW50WCwgZS5jbGllbnRZLFxuXHRcdCAgICAgICAgZmFsc2UsIGZhbHNlLCBmYWxzZSwgZmFsc2UsIDAsIG51bGwpO1xuXG5cdFx0ZS50YXJnZXQuZGlzcGF0Y2hFdmVudChzaW11bGF0ZWRFdmVudCk7XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IHRhcDogSGFuZGxlclxuLy8gTW9iaWxlIHRvdWNoIGhhY2tzIChxdWljayB0YXAgYW5kIHRvdWNoIGhvbGQpIGhhbmRsZXIuXG5pZiAoTC5Ccm93c2VyLnRvdWNoICYmICFMLkJyb3dzZXIucG9pbnRlcikge1xuXHRMLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICd0YXAnLCBMLk1hcC5UYXApO1xufVxuXG5cblxuLypcbiAqIEwuSGFuZGxlci5Cb3hab29tIGlzIHVzZWQgdG8gYWRkIHNoaWZ0LWRyYWcgem9vbSBpbnRlcmFjdGlvbiB0byB0aGUgbWFwXG4gKiAoem9vbSB0byBhIHNlbGVjdGVkIGJvdW5kaW5nIGJveCksIGVuYWJsZWQgYnkgZGVmYXVsdC5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuTC5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQG9wdGlvbiBib3hab29tOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBXaGV0aGVyIHRoZSBtYXAgY2FuIGJlIHpvb21lZCB0byBhIHJlY3Rhbmd1bGFyIGFyZWEgc3BlY2lmaWVkIGJ5XG5cdC8vIGRyYWdnaW5nIHRoZSBtb3VzZSB3aGlsZSBwcmVzc2luZyB0aGUgc2hpZnQga2V5LlxuXHRib3hab29tOiB0cnVlXG59KTtcblxuTC5NYXAuQm94Wm9vbSA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0dGhpcy5fbWFwID0gbWFwO1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IG1hcC5fY29udGFpbmVyO1xuXHRcdHRoaXMuX3BhbmUgPSBtYXAuX3BhbmVzLm92ZXJsYXlQYW5lO1xuXHR9LFxuXG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21FdmVudC5vbih0aGlzLl9jb250YWluZXIsICdtb3VzZWRvd24nLCB0aGlzLl9vbk1vdXNlRG93biwgdGhpcyk7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9jb250YWluZXIsICdtb3VzZWRvd24nLCB0aGlzLl9vbk1vdXNlRG93biwgdGhpcyk7XG5cdH0sXG5cblx0bW92ZWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fbW92ZWQ7XG5cdH0sXG5cblx0X3Jlc2V0U3RhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9tb3ZlZCA9IGZhbHNlO1xuXHR9LFxuXG5cdF9vbk1vdXNlRG93bjogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIWUuc2hpZnRLZXkgfHwgKChlLndoaWNoICE9PSAxKSAmJiAoZS5idXR0b24gIT09IDEpKSkgeyByZXR1cm4gZmFsc2U7IH1cblxuXHRcdHRoaXMuX3Jlc2V0U3RhdGUoKTtcblxuXHRcdEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbigpO1xuXHRcdEwuRG9tVXRpbC5kaXNhYmxlSW1hZ2VEcmFnKCk7XG5cblx0XHR0aGlzLl9zdGFydFBvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xuXG5cdFx0TC5Eb21FdmVudC5vbihkb2N1bWVudCwge1xuXHRcdFx0Y29udGV4dG1lbnU6IEwuRG9tRXZlbnQuc3RvcCxcblx0XHRcdG1vdXNlbW92ZTogdGhpcy5fb25Nb3VzZU1vdmUsXG5cdFx0XHRtb3VzZXVwOiB0aGlzLl9vbk1vdXNlVXAsXG5cdFx0XHRrZXlkb3duOiB0aGlzLl9vbktleURvd25cblx0XHR9LCB0aGlzKTtcblx0fSxcblxuXHRfb25Nb3VzZU1vdmU6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKCF0aGlzLl9tb3ZlZCkge1xuXHRcdFx0dGhpcy5fbW92ZWQgPSB0cnVlO1xuXG5cdFx0XHR0aGlzLl9ib3ggPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbGVhZmxldC16b29tLWJveCcsIHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1jcm9zc2hhaXInKTtcblxuXHRcdFx0dGhpcy5fbWFwLmZpcmUoJ2JveHpvb21zdGFydCcpO1xuXHRcdH1cblxuXHRcdHRoaXMuX3BvaW50ID0gdGhpcy5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xuXG5cdFx0dmFyIGJvdW5kcyA9IG5ldyBMLkJvdW5kcyh0aGlzLl9wb2ludCwgdGhpcy5fc3RhcnRQb2ludCksXG5cdFx0ICAgIHNpemUgPSBib3VuZHMuZ2V0U2l6ZSgpO1xuXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2JveCwgYm91bmRzLm1pbik7XG5cblx0XHR0aGlzLl9ib3guc3R5bGUud2lkdGggID0gc2l6ZS54ICsgJ3B4Jztcblx0XHR0aGlzLl9ib3guc3R5bGUuaGVpZ2h0ID0gc2l6ZS55ICsgJ3B4Jztcblx0fSxcblxuXHRfZmluaXNoOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21vdmVkKSB7XG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlKHRoaXMuX2JveCk7XG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1jcm9zc2hhaXInKTtcblx0XHR9XG5cblx0XHRMLkRvbVV0aWwuZW5hYmxlVGV4dFNlbGVjdGlvbigpO1xuXHRcdEwuRG9tVXRpbC5lbmFibGVJbWFnZURyYWcoKTtcblxuXHRcdEwuRG9tRXZlbnQub2ZmKGRvY3VtZW50LCB7XG5cdFx0XHRjb250ZXh0bWVudTogTC5Eb21FdmVudC5zdG9wLFxuXHRcdFx0bW91c2Vtb3ZlOiB0aGlzLl9vbk1vdXNlTW92ZSxcblx0XHRcdG1vdXNldXA6IHRoaXMuX29uTW91c2VVcCxcblx0XHRcdGtleWRvd246IHRoaXMuX29uS2V5RG93blxuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbk1vdXNlVXA6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKChlLndoaWNoICE9PSAxKSAmJiAoZS5idXR0b24gIT09IDEpKSB7IHJldHVybjsgfVxuXG5cdFx0dGhpcy5fZmluaXNoKCk7XG5cblx0XHRpZiAoIXRoaXMuX21vdmVkKSB7IHJldHVybjsgfVxuXHRcdC8vIFBvc3Rwb25lIHRvIG5leHQgSlMgdGljayBzbyBpbnRlcm5hbCBjbGljayBldmVudCBoYW5kbGluZ1xuXHRcdC8vIHN0aWxsIHNlZSBpdCBhcyBcIm1vdmVkXCIuXG5cdFx0c2V0VGltZW91dChMLmJpbmQodGhpcy5fcmVzZXRTdGF0ZSwgdGhpcyksIDApO1xuXG5cdFx0dmFyIGJvdW5kcyA9IG5ldyBMLkxhdExuZ0JvdW5kcyhcblx0XHQgICAgICAgIHRoaXMuX21hcC5jb250YWluZXJQb2ludFRvTGF0TG5nKHRoaXMuX3N0YXJ0UG9pbnQpLFxuXHRcdCAgICAgICAgdGhpcy5fbWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcodGhpcy5fcG9pbnQpKTtcblxuXHRcdHRoaXMuX21hcFxuXHRcdFx0LmZpdEJvdW5kcyhib3VuZHMpXG5cdFx0XHQuZmlyZSgnYm94em9vbWVuZCcsIHtib3hab29tQm91bmRzOiBib3VuZHN9KTtcblx0fSxcblxuXHRfb25LZXlEb3duOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmIChlLmtleUNvZGUgPT09IDI3KSB7XG5cdFx0XHR0aGlzLl9maW5pc2goKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IGJveFpvb206IEhhbmRsZXJcbi8vIEJveCAoc2hpZnQtZHJhZyB3aXRoIG1vdXNlKSB6b29tIGhhbmRsZXIuXG5MLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICdib3hab29tJywgTC5NYXAuQm94Wm9vbSk7XG5cblxuXG4vKlxuICogTC5NYXAuS2V5Ym9hcmQgaXMgaGFuZGxpbmcga2V5Ym9hcmQgaW50ZXJhY3Rpb24gd2l0aCB0aGUgbWFwLCBlbmFibGVkIGJ5IGRlZmF1bHQuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEtleWJvYXJkIE5hdmlnYXRpb24gT3B0aW9uc1xuTC5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQG9wdGlvbiBrZXlib2FyZDogQm9vbGVhbiA9IHRydWVcblx0Ly8gTWFrZXMgdGhlIG1hcCBmb2N1c2FibGUgYW5kIGFsbG93cyB1c2VycyB0byBuYXZpZ2F0ZSB0aGUgbWFwIHdpdGgga2V5Ym9hcmRcblx0Ly8gYXJyb3dzIGFuZCBgK2AvYC1gIGtleXMuXG5cdGtleWJvYXJkOiB0cnVlLFxuXG5cdC8vIEBvcHRpb24ga2V5Ym9hcmRQYW5EZWx0YTogTnVtYmVyID0gODBcblx0Ly8gQW1vdW50IG9mIHBpeGVscyB0byBwYW4gd2hlbiBwcmVzc2luZyBhbiBhcnJvdyBrZXkuXG5cdGtleWJvYXJkUGFuRGVsdGE6IDgwXG59KTtcblxuTC5NYXAuS2V5Ym9hcmQgPSBMLkhhbmRsZXIuZXh0ZW5kKHtcblxuXHRrZXlDb2Rlczoge1xuXHRcdGxlZnQ6ICAgIFszN10sXG5cdFx0cmlnaHQ6ICAgWzM5XSxcblx0XHRkb3duOiAgICBbNDBdLFxuXHRcdHVwOiAgICAgIFszOF0sXG5cdFx0em9vbUluOiAgWzE4NywgMTA3LCA2MSwgMTcxXSxcblx0XHR6b29tT3V0OiBbMTg5LCAxMDksIDU0LCAxNzNdXG5cdH0sXG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHRoaXMuX21hcCA9IG1hcDtcblxuXHRcdHRoaXMuX3NldFBhbkRlbHRhKG1hcC5vcHRpb25zLmtleWJvYXJkUGFuRGVsdGEpO1xuXHRcdHRoaXMuX3NldFpvb21EZWx0YShtYXAub3B0aW9ucy56b29tRGVsdGEpO1xuXHR9LFxuXG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX21hcC5fY29udGFpbmVyO1xuXG5cdFx0Ly8gbWFrZSB0aGUgY29udGFpbmVyIGZvY3VzYWJsZSBieSB0YWJiaW5nXG5cdFx0aWYgKGNvbnRhaW5lci50YWJJbmRleCA8PSAwKSB7XG5cdFx0XHRjb250YWluZXIudGFiSW5kZXggPSAnMCc7XG5cdFx0fVxuXG5cdFx0TC5Eb21FdmVudC5vbihjb250YWluZXIsIHtcblx0XHRcdGZvY3VzOiB0aGlzLl9vbkZvY3VzLFxuXHRcdFx0Ymx1cjogdGhpcy5fb25CbHVyLFxuXHRcdFx0bW91c2Vkb3duOiB0aGlzLl9vbk1vdXNlRG93blxuXHRcdH0sIHRoaXMpO1xuXG5cdFx0dGhpcy5fbWFwLm9uKHtcblx0XHRcdGZvY3VzOiB0aGlzLl9hZGRIb29rcyxcblx0XHRcdGJsdXI6IHRoaXMuX3JlbW92ZUhvb2tzXG5cdFx0fSwgdGhpcyk7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW1vdmVIb29rcygpO1xuXG5cdFx0TC5Eb21FdmVudC5vZmYodGhpcy5fbWFwLl9jb250YWluZXIsIHtcblx0XHRcdGZvY3VzOiB0aGlzLl9vbkZvY3VzLFxuXHRcdFx0Ymx1cjogdGhpcy5fb25CbHVyLFxuXHRcdFx0bW91c2Vkb3duOiB0aGlzLl9vbk1vdXNlRG93blxuXHRcdH0sIHRoaXMpO1xuXG5cdFx0dGhpcy5fbWFwLm9mZih7XG5cdFx0XHRmb2N1czogdGhpcy5fYWRkSG9va3MsXG5cdFx0XHRibHVyOiB0aGlzLl9yZW1vdmVIb29rc1xuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbk1vdXNlRG93bjogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9mb2N1c2VkKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIGJvZHkgPSBkb2N1bWVudC5ib2R5LFxuXHRcdCAgICBkb2NFbCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCxcblx0XHQgICAgdG9wID0gYm9keS5zY3JvbGxUb3AgfHwgZG9jRWwuc2Nyb2xsVG9wLFxuXHRcdCAgICBsZWZ0ID0gYm9keS5zY3JvbGxMZWZ0IHx8IGRvY0VsLnNjcm9sbExlZnQ7XG5cblx0XHR0aGlzLl9tYXAuX2NvbnRhaW5lci5mb2N1cygpO1xuXG5cdFx0d2luZG93LnNjcm9sbFRvKGxlZnQsIHRvcCk7XG5cdH0sXG5cblx0X29uRm9jdXM6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9mb2N1c2VkID0gdHJ1ZTtcblx0XHR0aGlzLl9tYXAuZmlyZSgnZm9jdXMnKTtcblx0fSxcblxuXHRfb25CbHVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fZm9jdXNlZCA9IGZhbHNlO1xuXHRcdHRoaXMuX21hcC5maXJlKCdibHVyJyk7XG5cdH0sXG5cblx0X3NldFBhbkRlbHRhOiBmdW5jdGlvbiAocGFuRGVsdGEpIHtcblx0XHR2YXIga2V5cyA9IHRoaXMuX3BhbktleXMgPSB7fSxcblx0XHQgICAgY29kZXMgPSB0aGlzLmtleUNvZGVzLFxuXHRcdCAgICBpLCBsZW47XG5cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy5sZWZ0Lmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLmxlZnRbaV1dID0gWy0xICogcGFuRGVsdGEsIDBdO1xuXHRcdH1cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy5yaWdodC5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0a2V5c1tjb2Rlcy5yaWdodFtpXV0gPSBbcGFuRGVsdGEsIDBdO1xuXHRcdH1cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy5kb3duLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLmRvd25baV1dID0gWzAsIHBhbkRlbHRhXTtcblx0XHR9XG5cdFx0Zm9yIChpID0gMCwgbGVuID0gY29kZXMudXAubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMudXBbaV1dID0gWzAsIC0xICogcGFuRGVsdGFdO1xuXHRcdH1cblx0fSxcblxuXHRfc2V0Wm9vbURlbHRhOiBmdW5jdGlvbiAoem9vbURlbHRhKSB7XG5cdFx0dmFyIGtleXMgPSB0aGlzLl96b29tS2V5cyA9IHt9LFxuXHRcdCAgICBjb2RlcyA9IHRoaXMua2V5Q29kZXMsXG5cdFx0ICAgIGksIGxlbjtcblxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLnpvb21Jbi5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0a2V5c1tjb2Rlcy56b29tSW5baV1dID0gem9vbURlbHRhO1xuXHRcdH1cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy56b29tT3V0Lmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLnpvb21PdXRbaV1dID0gLXpvb21EZWx0YTtcblx0XHR9XG5cdH0sXG5cblx0X2FkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21FdmVudC5vbihkb2N1bWVudCwgJ2tleWRvd24nLCB0aGlzLl9vbktleURvd24sIHRoaXMpO1xuXHR9LFxuXG5cdF9yZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub2ZmKGRvY3VtZW50LCAna2V5ZG93bicsIHRoaXMuX29uS2V5RG93biwgdGhpcyk7XG5cdH0sXG5cblx0X29uS2V5RG93bjogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoZS5hbHRLZXkgfHwgZS5jdHJsS2V5IHx8IGUubWV0YUtleSkgeyByZXR1cm47IH1cblxuXHRcdHZhciBrZXkgPSBlLmtleUNvZGUsXG5cdFx0ICAgIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgb2Zmc2V0O1xuXG5cdFx0aWYgKGtleSBpbiB0aGlzLl9wYW5LZXlzKSB7XG5cblx0XHRcdGlmIChtYXAuX3BhbkFuaW0gJiYgbWFwLl9wYW5BbmltLl9pblByb2dyZXNzKSB7IHJldHVybjsgfVxuXG5cdFx0XHRvZmZzZXQgPSB0aGlzLl9wYW5LZXlzW2tleV07XG5cdFx0XHRpZiAoZS5zaGlmdEtleSkge1xuXHRcdFx0XHRvZmZzZXQgPSBMLnBvaW50KG9mZnNldCkubXVsdGlwbHlCeSgzKTtcblx0XHRcdH1cblxuXHRcdFx0bWFwLnBhbkJ5KG9mZnNldCk7XG5cblx0XHRcdGlmIChtYXAub3B0aW9ucy5tYXhCb3VuZHMpIHtcblx0XHRcdFx0bWFwLnBhbkluc2lkZUJvdW5kcyhtYXAub3B0aW9ucy5tYXhCb3VuZHMpO1xuXHRcdFx0fVxuXG5cdFx0fSBlbHNlIGlmIChrZXkgaW4gdGhpcy5fem9vbUtleXMpIHtcblx0XHRcdG1hcC5zZXRab29tKG1hcC5nZXRab29tKCkgKyAoZS5zaGlmdEtleSA/IDMgOiAxKSAqIHRoaXMuX3pvb21LZXlzW2tleV0pO1xuXG5cdFx0fSBlbHNlIGlmIChrZXkgPT09IDI3KSB7XG5cdFx0XHRtYXAuY2xvc2VQb3B1cCgpO1xuXG5cdFx0fSBlbHNlIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRMLkRvbUV2ZW50LnN0b3AoZSk7XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHNlY3Rpb24gSGFuZGxlcnNcbi8vIEBwcm9wZXJ0eSBrZXlib2FyZDogSGFuZGxlclxuLy8gS2V5Ym9hcmQgbmF2aWdhdGlvbiBoYW5kbGVyLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAna2V5Ym9hcmQnLCBMLk1hcC5LZXlib2FyZCk7XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLk1hcmtlckRyYWcgaXMgdXNlZCBpbnRlcm5hbGx5IGJ5IEwuTWFya2VyIHRvIG1ha2UgdGhlIG1hcmtlcnMgZHJhZ2dhYmxlLlxuICovXG5cblxuLyogQG5hbWVzcGFjZSBNYXJrZXJcbiAqIEBzZWN0aW9uIEludGVyYWN0aW9uIGhhbmRsZXJzXG4gKlxuICogSW50ZXJhY3Rpb24gaGFuZGxlcnMgYXJlIHByb3BlcnRpZXMgb2YgYSBtYXJrZXIgaW5zdGFuY2UgdGhhdCBhbGxvdyB5b3UgdG8gY29udHJvbCBpbnRlcmFjdGlvbiBiZWhhdmlvciBpbiBydW50aW1lLCBlbmFibGluZyBvciBkaXNhYmxpbmcgY2VydGFpbiBmZWF0dXJlcyBzdWNoIGFzIGRyYWdnaW5nIChzZWUgYEhhbmRsZXJgIG1ldGhvZHMpLiBFeGFtcGxlOlxuICpcbiAqIGBgYGpzXG4gKiBtYXJrZXIuZHJhZ2dpbmcuZGlzYWJsZSgpO1xuICogYGBgXG4gKlxuICogQHByb3BlcnR5IGRyYWdnaW5nOiBIYW5kbGVyXG4gKiBNYXJrZXIgZHJhZ2dpbmcgaGFuZGxlciAoYnkgYm90aCBtb3VzZSBhbmQgdG91Y2gpLlxuICovXG5cbkwuSGFuZGxlci5NYXJrZXJEcmFnID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChtYXJrZXIpIHtcblx0XHR0aGlzLl9tYXJrZXIgPSBtYXJrZXI7XG5cdH0sXG5cblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgaWNvbiA9IHRoaXMuX21hcmtlci5faWNvbjtcblxuXHRcdGlmICghdGhpcy5fZHJhZ2dhYmxlKSB7XG5cdFx0XHR0aGlzLl9kcmFnZ2FibGUgPSBuZXcgTC5EcmFnZ2FibGUoaWNvbiwgaWNvbiwgdHJ1ZSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fZHJhZ2dhYmxlLm9uKHtcblx0XHRcdGRyYWdzdGFydDogdGhpcy5fb25EcmFnU3RhcnQsXG5cdFx0XHRkcmFnOiB0aGlzLl9vbkRyYWcsXG5cdFx0XHRkcmFnZW5kOiB0aGlzLl9vbkRyYWdFbmRcblx0XHR9LCB0aGlzKS5lbmFibGUoKTtcblxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhpY29uLCAnbGVhZmxldC1tYXJrZXItZHJhZ2dhYmxlJyk7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9kcmFnZ2FibGUub2ZmKHtcblx0XHRcdGRyYWdzdGFydDogdGhpcy5fb25EcmFnU3RhcnQsXG5cdFx0XHRkcmFnOiB0aGlzLl9vbkRyYWcsXG5cdFx0XHRkcmFnZW5kOiB0aGlzLl9vbkRyYWdFbmRcblx0XHR9LCB0aGlzKS5kaXNhYmxlKCk7XG5cblx0XHRpZiAodGhpcy5fbWFya2VyLl9pY29uKSB7XG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fbWFya2VyLl9pY29uLCAnbGVhZmxldC1tYXJrZXItZHJhZ2dhYmxlJyk7XG5cdFx0fVxuXHR9LFxuXG5cdG1vdmVkOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2RyYWdnYWJsZSAmJiB0aGlzLl9kcmFnZ2FibGUuX21vdmVkO1xuXHR9LFxuXG5cdF9vbkRyYWdTdGFydDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIEBzZWN0aW9uIERyYWdnaW5nIGV2ZW50c1xuXHRcdC8vIEBldmVudCBkcmFnc3RhcnQ6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciBzdGFydHMgZHJhZ2dpbmcgdGhlIG1hcmtlci5cblxuXHRcdC8vIEBldmVudCBtb3Zlc3RhcnQ6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFya2VyIHN0YXJ0cyBtb3ZpbmcgKGJlY2F1c2Ugb2YgZHJhZ2dpbmcpLlxuXG5cdFx0dGhpcy5fb2xkTGF0TG5nID0gdGhpcy5fbWFya2VyLmdldExhdExuZygpO1xuXHRcdHRoaXMuX21hcmtlclxuXHRcdCAgICAuY2xvc2VQb3B1cCgpXG5cdFx0ICAgIC5maXJlKCdtb3Zlc3RhcnQnKVxuXHRcdCAgICAuZmlyZSgnZHJhZ3N0YXJ0Jyk7XG5cdH0sXG5cblx0X29uRHJhZzogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgbWFya2VyID0gdGhpcy5fbWFya2VyLFxuXHRcdCAgICBzaGFkb3cgPSBtYXJrZXIuX3NoYWRvdyxcblx0XHQgICAgaWNvblBvcyA9IEwuRG9tVXRpbC5nZXRQb3NpdGlvbihtYXJrZXIuX2ljb24pLFxuXHRcdCAgICBsYXRsbmcgPSBtYXJrZXIuX21hcC5sYXllclBvaW50VG9MYXRMbmcoaWNvblBvcyk7XG5cblx0XHQvLyB1cGRhdGUgc2hhZG93IHBvc2l0aW9uXG5cdFx0aWYgKHNoYWRvdykge1xuXHRcdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHNoYWRvdywgaWNvblBvcyk7XG5cdFx0fVxuXG5cdFx0bWFya2VyLl9sYXRsbmcgPSBsYXRsbmc7XG5cdFx0ZS5sYXRsbmcgPSBsYXRsbmc7XG5cdFx0ZS5vbGRMYXRMbmcgPSB0aGlzLl9vbGRMYXRMbmc7XG5cblx0XHQvLyBAZXZlbnQgZHJhZzogRXZlbnRcblx0XHQvLyBGaXJlZCByZXBlYXRlZGx5IHdoaWxlIHRoZSB1c2VyIGRyYWdzIHRoZSBtYXJrZXIuXG5cdFx0bWFya2VyXG5cdFx0ICAgIC5maXJlKCdtb3ZlJywgZSlcblx0XHQgICAgLmZpcmUoJ2RyYWcnLCBlKTtcblx0fSxcblxuXHRfb25EcmFnRW5kOiBmdW5jdGlvbiAoZSkge1xuXHRcdC8vIEBldmVudCBkcmFnZW5kOiBEcmFnRW5kRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHN0b3BzIGRyYWdnaW5nIHRoZSBtYXJrZXIuXG5cblx0XHQvLyBAZXZlbnQgbW92ZWVuZDogRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXJrZXIgc3RvcHMgbW92aW5nIChiZWNhdXNlIG9mIGRyYWdnaW5nKS5cblx0XHRkZWxldGUgdGhpcy5fb2xkTGF0TG5nO1xuXHRcdHRoaXMuX21hcmtlclxuXHRcdCAgICAuZmlyZSgnbW92ZWVuZCcpXG5cdFx0ICAgIC5maXJlKCdkcmFnZW5kJywgZSk7XG5cdH1cbn0pO1xuXG5cblxuLypcclxuICogQGNsYXNzIENvbnRyb2xcclxuICogQGFrYSBMLkNvbnRyb2xcclxuICogQGluaGVyaXRzIENsYXNzXHJcbiAqXHJcbiAqIEwuQ29udHJvbCBpcyBhIGJhc2UgY2xhc3MgZm9yIGltcGxlbWVudGluZyBtYXAgY29udHJvbHMuIEhhbmRsZXMgcG9zaXRpb25pbmcuXHJcbiAqIEFsbCBvdGhlciBjb250cm9scyBleHRlbmQgZnJvbSB0aGlzIGNsYXNzLlxyXG4gKi9cclxuXHJcbkwuQ29udHJvbCA9IEwuQ2xhc3MuZXh0ZW5kKHtcclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgQ29udHJvbCBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBwb3NpdGlvbjogU3RyaW5nID0gJ3RvcHJpZ2h0J1xyXG5cdFx0Ly8gVGhlIHBvc2l0aW9uIG9mIHRoZSBjb250cm9sIChvbmUgb2YgdGhlIG1hcCBjb3JuZXJzKS4gUG9zc2libGUgdmFsdWVzIGFyZSBgJ3RvcGxlZnQnYCxcclxuXHRcdC8vIGAndG9wcmlnaHQnYCwgYCdib3R0b21sZWZ0J2Agb3IgYCdib3R0b21yaWdodCdgXHJcblx0XHRwb3NpdGlvbjogJ3RvcHJpZ2h0J1xyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0LyogQHNlY3Rpb25cclxuXHQgKiBDbGFzc2VzIGV4dGVuZGluZyBMLkNvbnRyb2wgd2lsbCBpbmhlcml0IHRoZSBmb2xsb3dpbmcgbWV0aG9kczpcclxuXHQgKlxyXG5cdCAqIEBtZXRob2QgZ2V0UG9zaXRpb246IHN0cmluZ1xyXG5cdCAqIFJldHVybnMgdGhlIHBvc2l0aW9uIG9mIHRoZSBjb250cm9sLlxyXG5cdCAqL1xyXG5cdGdldFBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLnBvc2l0aW9uO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0UG9zaXRpb24ocG9zaXRpb246IHN0cmluZyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBwb3NpdGlvbiBvZiB0aGUgY29udHJvbC5cclxuXHRzZXRQb3NpdGlvbjogZnVuY3Rpb24gKHBvc2l0aW9uKSB7XHJcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xyXG5cclxuXHRcdGlmIChtYXApIHtcclxuXHRcdFx0bWFwLnJlbW92ZUNvbnRyb2wodGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5vcHRpb25zLnBvc2l0aW9uID0gcG9zaXRpb247XHJcblxyXG5cdFx0aWYgKG1hcCkge1xyXG5cdFx0XHRtYXAuYWRkQ29udHJvbCh0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENvbnRhaW5lcjogSFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBIVE1MRWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSBjb250cm9sLlxyXG5cdGdldENvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZFRvKG1hcDogTWFwKTogdGhpc1xyXG5cdC8vIEFkZHMgdGhlIGNvbnRyb2wgdG8gdGhlIGdpdmVuIG1hcC5cclxuXHRhZGRUbzogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dGhpcy5yZW1vdmUoKTtcclxuXHRcdHRoaXMuX21hcCA9IG1hcDtcclxuXHJcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyID0gdGhpcy5vbkFkZChtYXApLFxyXG5cdFx0ICAgIHBvcyA9IHRoaXMuZ2V0UG9zaXRpb24oKSxcclxuXHRcdCAgICBjb3JuZXIgPSBtYXAuX2NvbnRyb2xDb3JuZXJzW3Bvc107XHJcblxyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtY29udHJvbCcpO1xyXG5cclxuXHRcdGlmIChwb3MuaW5kZXhPZignYm90dG9tJykgIT09IC0xKSB7XHJcblx0XHRcdGNvcm5lci5pbnNlcnRCZWZvcmUoY29udGFpbmVyLCBjb3JuZXIuZmlyc3RDaGlsZCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRjb3JuZXIuYXBwZW5kQ2hpbGQoY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIGNvbnRyb2wgZnJvbSB0aGUgbWFwIGl0IGlzIGN1cnJlbnRseSBhY3RpdmUgb24uXHJcblx0cmVtb3ZlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX21hcCkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwucmVtb3ZlKHRoaXMuX2NvbnRhaW5lcik7XHJcblxyXG5cdFx0aWYgKHRoaXMub25SZW1vdmUpIHtcclxuXHRcdFx0dGhpcy5vblJlbW92ZSh0aGlzLl9tYXApO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX21hcCA9IG51bGw7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X3JlZm9jdXNPbk1hcDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdC8vIGlmIG1hcCBleGlzdHMgYW5kIGV2ZW50IGlzIG5vdCBhIGtleWJvYXJkIGV2ZW50XHJcblx0XHRpZiAodGhpcy5fbWFwICYmIGUgJiYgZS5zY3JlZW5YID4gMCAmJiBlLnNjcmVlblkgPiAwKSB7XHJcblx0XHRcdHRoaXMuX21hcC5nZXRDb250YWluZXIoKS5mb2N1cygpO1xyXG5cdFx0fVxyXG5cdH1cclxufSk7XHJcblxyXG5MLmNvbnRyb2wgPSBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5Db250cm9sKG9wdGlvbnMpO1xyXG59O1xyXG5cclxuLyogQHNlY3Rpb24gRXh0ZW5zaW9uIG1ldGhvZHNcclxuICogQHVuaW5oZXJpdGFibGVcclxuICpcclxuICogRXZlcnkgY29udHJvbCBzaG91bGQgZXh0ZW5kIGZyb20gYEwuQ29udHJvbGAgYW5kIChyZS0paW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgbWV0aG9kcy5cclxuICpcclxuICogQG1ldGhvZCBvbkFkZChtYXA6IE1hcCk6IEhUTUxFbGVtZW50XHJcbiAqIFNob3VsZCByZXR1cm4gdGhlIGNvbnRhaW5lciBET00gZWxlbWVudCBmb3IgdGhlIGNvbnRyb2wgYW5kIGFkZCBsaXN0ZW5lcnMgb24gcmVsZXZhbnQgbWFwIGV2ZW50cy4gQ2FsbGVkIG9uIFtgY29udHJvbC5hZGRUbyhtYXApYF0oI2NvbnRyb2wtYWRkVG8pLlxyXG4gKlxyXG4gKiBAbWV0aG9kIG9uUmVtb3ZlKG1hcDogTWFwKVxyXG4gKiBPcHRpb25hbCBtZXRob2QuIFNob3VsZCBjb250YWluIGFsbCBjbGVhbiB1cCBjb2RlIHRoYXQgcmVtb3ZlcyB0aGUgbGlzdGVuZXJzIHByZXZpb3VzbHkgYWRkZWQgaW4gW2BvbkFkZGBdKCNjb250cm9sLW9uYWRkKS4gQ2FsbGVkIG9uIFtgY29udHJvbC5yZW1vdmUoKWBdKCNjb250cm9sLXJlbW92ZSkuXHJcbiAqL1xyXG5cclxuLyogQG5hbWVzcGFjZSBNYXBcclxuICogQHNlY3Rpb24gTWV0aG9kcyBmb3IgTGF5ZXJzIGFuZCBDb250cm9sc1xyXG4gKi9cclxuTC5NYXAuaW5jbHVkZSh7XHJcblx0Ly8gQG1ldGhvZCBhZGRDb250cm9sKGNvbnRyb2w6IENvbnRyb2wpOiB0aGlzXHJcblx0Ly8gQWRkcyB0aGUgZ2l2ZW4gY29udHJvbCB0byB0aGUgbWFwXHJcblx0YWRkQ29udHJvbDogZnVuY3Rpb24gKGNvbnRyb2wpIHtcclxuXHRcdGNvbnRyb2wuYWRkVG8odGhpcyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZUNvbnRyb2woY29udHJvbDogQ29udHJvbCk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIHRoZSBnaXZlbiBjb250cm9sIGZyb20gdGhlIG1hcFxyXG5cdHJlbW92ZUNvbnRyb2w6IGZ1bmN0aW9uIChjb250cm9sKSB7XHJcblx0XHRjb250cm9sLnJlbW92ZSgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X2luaXRDb250cm9sUG9zOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY29ybmVycyA9IHRoaXMuX2NvbnRyb2xDb3JuZXJzID0ge30sXHJcblx0XHQgICAgbCA9ICdsZWFmbGV0LScsXHJcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udHJvbENvbnRhaW5lciA9XHJcblx0XHQgICAgICAgICAgICBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBsICsgJ2NvbnRyb2wtY29udGFpbmVyJywgdGhpcy5fY29udGFpbmVyKTtcclxuXHJcblx0XHRmdW5jdGlvbiBjcmVhdGVDb3JuZXIodlNpZGUsIGhTaWRlKSB7XHJcblx0XHRcdHZhciBjbGFzc05hbWUgPSBsICsgdlNpZGUgKyAnICcgKyBsICsgaFNpZGU7XHJcblxyXG5cdFx0XHRjb3JuZXJzW3ZTaWRlICsgaFNpZGVdID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lLCBjb250YWluZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGNyZWF0ZUNvcm5lcigndG9wJywgJ2xlZnQnKTtcclxuXHRcdGNyZWF0ZUNvcm5lcigndG9wJywgJ3JpZ2h0Jyk7XHJcblx0XHRjcmVhdGVDb3JuZXIoJ2JvdHRvbScsICdsZWZ0Jyk7XHJcblx0XHRjcmVhdGVDb3JuZXIoJ2JvdHRvbScsICdyaWdodCcpO1xyXG5cdH0sXHJcblxyXG5cdF9jbGVhckNvbnRyb2xQb3M6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fY29udHJvbENvbnRhaW5lcik7XHJcblx0fVxyXG59KTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIENvbnRyb2wuWm9vbVxyXG4gKiBAYWthIEwuQ29udHJvbC5ab29tXHJcbiAqIEBpbmhlcml0cyBDb250cm9sXHJcbiAqXHJcbiAqIEEgYmFzaWMgem9vbSBjb250cm9sIHdpdGggdHdvIGJ1dHRvbnMgKHpvb20gaW4gYW5kIHpvb20gb3V0KS4gSXQgaXMgcHV0IG9uIHRoZSBtYXAgYnkgZGVmYXVsdCB1bmxlc3MgeW91IHNldCBpdHMgW2B6b29tQ29udHJvbGAgb3B0aW9uXSgjbWFwLXpvb21jb250cm9sKSB0byBgZmFsc2VgLiBFeHRlbmRzIGBDb250cm9sYC5cclxuICovXHJcblxyXG5MLkNvbnRyb2wuWm9vbSA9IEwuQ29udHJvbC5leHRlbmQoe1xyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBDb250cm9sLlpvb20gb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdHBvc2l0aW9uOiAndG9wbGVmdCcsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tSW5UZXh0OiBTdHJpbmcgPSAnKydcclxuXHRcdC8vIFRoZSB0ZXh0IHNldCBvbiB0aGUgJ3pvb20gaW4nIGJ1dHRvbi5cclxuXHRcdHpvb21JblRleHQ6ICcrJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21JblRpdGxlOiBTdHJpbmcgPSAnWm9vbSBpbidcclxuXHRcdC8vIFRoZSB0aXRsZSBzZXQgb24gdGhlICd6b29tIGluJyBidXR0b24uXHJcblx0XHR6b29tSW5UaXRsZTogJ1pvb20gaW4nLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbU91dFRleHQ6IFN0cmluZyA9ICctJ1xyXG5cdFx0Ly8gVGhlIHRleHQgc2V0IG9uIHRoZSAnem9vbSBvdXQnIGJ1dHRvbi5cclxuXHRcdHpvb21PdXRUZXh0OiAnLScsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tT3V0VGl0bGU6IFN0cmluZyA9ICdab29tIG91dCdcclxuXHRcdC8vIFRoZSB0aXRsZSBzZXQgb24gdGhlICd6b29tIG91dCcgYnV0dG9uLlxyXG5cdFx0em9vbU91dFRpdGxlOiAnWm9vbSBvdXQnXHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHZhciB6b29tTmFtZSA9ICdsZWFmbGV0LWNvbnRyb2wtem9vbScsXHJcblx0XHQgICAgY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2Jywgem9vbU5hbWUgKyAnIGxlYWZsZXQtYmFyJyksXHJcblx0XHQgICAgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcclxuXHJcblx0XHR0aGlzLl96b29tSW5CdXR0b24gID0gdGhpcy5fY3JlYXRlQnV0dG9uKG9wdGlvbnMuem9vbUluVGV4dCwgb3B0aW9ucy56b29tSW5UaXRsZSxcclxuXHRcdCAgICAgICAgem9vbU5hbWUgKyAnLWluJywgIGNvbnRhaW5lciwgdGhpcy5fem9vbUluKTtcclxuXHRcdHRoaXMuX3pvb21PdXRCdXR0b24gPSB0aGlzLl9jcmVhdGVCdXR0b24ob3B0aW9ucy56b29tT3V0VGV4dCwgb3B0aW9ucy56b29tT3V0VGl0bGUsXHJcblx0XHQgICAgICAgIHpvb21OYW1lICsgJy1vdXQnLCBjb250YWluZXIsIHRoaXMuX3pvb21PdXQpO1xyXG5cclxuXHRcdHRoaXMuX3VwZGF0ZURpc2FibGVkKCk7XHJcblx0XHRtYXAub24oJ3pvb21lbmQgem9vbWxldmVsc2NoYW5nZScsIHRoaXMuX3VwZGF0ZURpc2FibGVkLCB0aGlzKTtcclxuXHJcblx0XHRyZXR1cm4gY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRtYXAub2ZmKCd6b29tZW5kIHpvb21sZXZlbHNjaGFuZ2UnLCB0aGlzLl91cGRhdGVEaXNhYmxlZCwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0ZGlzYWJsZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fZGlzYWJsZWQgPSB0cnVlO1xyXG5cdFx0dGhpcy5fdXBkYXRlRGlzYWJsZWQoKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGVuYWJsZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fZGlzYWJsZWQgPSBmYWxzZTtcclxuXHRcdHRoaXMuX3VwZGF0ZURpc2FibGVkKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfem9vbUluOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9kaXNhYmxlZCAmJiB0aGlzLl9tYXAuX3pvb20gPCB0aGlzLl9tYXAuZ2V0TWF4Wm9vbSgpKSB7XHJcblx0XHRcdHRoaXMuX21hcC56b29tSW4odGhpcy5fbWFwLm9wdGlvbnMuem9vbURlbHRhICogKGUuc2hpZnRLZXkgPyAzIDogMSkpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF96b29tT3V0OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9kaXNhYmxlZCAmJiB0aGlzLl9tYXAuX3pvb20gPiB0aGlzLl9tYXAuZ2V0TWluWm9vbSgpKSB7XHJcblx0XHRcdHRoaXMuX21hcC56b29tT3V0KHRoaXMuX21hcC5vcHRpb25zLnpvb21EZWx0YSAqIChlLnNoaWZ0S2V5ID8gMyA6IDEpKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfY3JlYXRlQnV0dG9uOiBmdW5jdGlvbiAoaHRtbCwgdGl0bGUsIGNsYXNzTmFtZSwgY29udGFpbmVyLCBmbikge1xyXG5cdFx0dmFyIGxpbmsgPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgY2xhc3NOYW1lLCBjb250YWluZXIpO1xyXG5cdFx0bGluay5pbm5lckhUTUwgPSBodG1sO1xyXG5cdFx0bGluay5ocmVmID0gJyMnO1xyXG5cdFx0bGluay50aXRsZSA9IHRpdGxlO1xyXG5cclxuXHRcdC8qXHJcblx0XHQgKiBXaWxsIGZvcmNlIHNjcmVlbiByZWFkZXJzIGxpa2UgVm9pY2VPdmVyIHRvIHJlYWQgdGhpcyBhcyBcIlpvb20gaW4gLSBidXR0b25cIlxyXG5cdFx0ICovXHJcblx0XHRsaW5rLnNldEF0dHJpYnV0ZSgncm9sZScsICdidXR0b24nKTtcclxuXHRcdGxpbmsuc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgdGl0bGUpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnRcclxuXHRcdCAgICAub24obGluaywgJ21vdXNlZG93biBkYmxjbGljaycsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKVxyXG5cdFx0ICAgIC5vbihsaW5rLCAnY2xpY2snLCBMLkRvbUV2ZW50LnN0b3ApXHJcblx0XHQgICAgLm9uKGxpbmssICdjbGljaycsIGZuLCB0aGlzKVxyXG5cdFx0ICAgIC5vbihsaW5rLCAnY2xpY2snLCB0aGlzLl9yZWZvY3VzT25NYXAsIHRoaXMpO1xyXG5cclxuXHRcdHJldHVybiBsaW5rO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVEaXNhYmxlZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcclxuXHRcdCAgICBjbGFzc05hbWUgPSAnbGVhZmxldC1kaXNhYmxlZCc7XHJcblxyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX3pvb21JbkJ1dHRvbiwgY2xhc3NOYW1lKTtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl96b29tT3V0QnV0dG9uLCBjbGFzc05hbWUpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9kaXNhYmxlZCB8fCBtYXAuX3pvb20gPT09IG1hcC5nZXRNaW5ab29tKCkpIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX3pvb21PdXRCdXR0b24sIGNsYXNzTmFtZSk7XHJcblx0XHR9XHJcblx0XHRpZiAodGhpcy5fZGlzYWJsZWQgfHwgbWFwLl96b29tID09PSBtYXAuZ2V0TWF4Wm9vbSgpKSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl96b29tSW5CdXR0b24sIGNsYXNzTmFtZSk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgTWFwXHJcbi8vIEBzZWN0aW9uIENvbnRyb2wgb3B0aW9uc1xyXG4vLyBAb3B0aW9uIHpvb21Db250cm9sOiBCb29sZWFuID0gdHJ1ZVxyXG4vLyBXaGV0aGVyIGEgW3pvb20gY29udHJvbF0oI2NvbnRyb2wtem9vbSkgaXMgYWRkZWQgdG8gdGhlIG1hcCBieSBkZWZhdWx0LlxyXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xyXG5cdHpvb21Db250cm9sOiB0cnVlXHJcbn0pO1xyXG5cclxuTC5NYXAuYWRkSW5pdEhvb2soZnVuY3Rpb24gKCkge1xyXG5cdGlmICh0aGlzLm9wdGlvbnMuem9vbUNvbnRyb2wpIHtcclxuXHRcdHRoaXMuem9vbUNvbnRyb2wgPSBuZXcgTC5Db250cm9sLlpvb20oKTtcclxuXHRcdHRoaXMuYWRkQ29udHJvbCh0aGlzLnpvb21Db250cm9sKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBDb250cm9sLlpvb21cclxuLy8gQGZhY3RvcnkgTC5jb250cm9sLnpvb20ob3B0aW9uczogQ29udHJvbC5ab29tIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYSB6b29tIGNvbnRyb2xcclxuTC5jb250cm9sLnpvb20gPSBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5Db250cm9sLlpvb20ob3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBDb250cm9sLkF0dHJpYnV0aW9uXHJcbiAqIEBha2EgTC5Db250cm9sLkF0dHJpYnV0aW9uXHJcbiAqIEBpbmhlcml0cyBDb250cm9sXHJcbiAqXHJcbiAqIFRoZSBhdHRyaWJ1dGlvbiBjb250cm9sIGFsbG93cyB5b3UgdG8gZGlzcGxheSBhdHRyaWJ1dGlvbiBkYXRhIGluIGEgc21hbGwgdGV4dCBib3ggb24gYSBtYXAuIEl0IGlzIHB1dCBvbiB0aGUgbWFwIGJ5IGRlZmF1bHQgdW5sZXNzIHlvdSBzZXQgaXRzIFtgYXR0cmlidXRpb25Db250cm9sYCBvcHRpb25dKCNtYXAtYXR0cmlidXRpb25jb250cm9sKSB0byBgZmFsc2VgLCBhbmQgaXQgZmV0Y2hlcyBhdHRyaWJ1dGlvbiB0ZXh0cyBmcm9tIGxheWVycyB3aXRoIHRoZSBbYGdldEF0dHJpYnV0aW9uYCBtZXRob2RdKCNsYXllci1nZXRhdHRyaWJ1dGlvbikgYXV0b21hdGljYWxseS4gRXh0ZW5kcyBDb250cm9sLlxyXG4gKi9cclxuXHJcbkwuQ29udHJvbC5BdHRyaWJ1dGlvbiA9IEwuQ29udHJvbC5leHRlbmQoe1xyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBDb250cm9sLkF0dHJpYnV0aW9uIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHRwb3NpdGlvbjogJ2JvdHRvbXJpZ2h0JyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHByZWZpeDogU3RyaW5nID0gJ0xlYWZsZXQnXHJcblx0XHQvLyBUaGUgSFRNTCB0ZXh0IHNob3duIGJlZm9yZSB0aGUgYXR0cmlidXRpb25zLiBQYXNzIGBmYWxzZWAgdG8gZGlzYWJsZS5cclxuXHRcdHByZWZpeDogJzxhIGhyZWY9XCJodHRwOi8vbGVhZmxldGpzLmNvbVwiIHRpdGxlPVwiQSBKUyBsaWJyYXJ5IGZvciBpbnRlcmFjdGl2ZSBtYXBzXCI+TGVhZmxldDwvYT4nXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHJcblx0XHR0aGlzLl9hdHRyaWJ1dGlvbnMgPSB7fTtcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0bWFwLmF0dHJpYnV0aW9uQ29udHJvbCA9IHRoaXM7XHJcblx0XHR0aGlzLl9jb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbGVhZmxldC1jb250cm9sLWF0dHJpYnV0aW9uJyk7XHJcblx0XHRpZiAoTC5Eb21FdmVudCkge1xyXG5cdFx0XHRMLkRvbUV2ZW50LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gVE9ETyB1Z2x5LCByZWZhY3RvclxyXG5cdFx0Zm9yICh2YXIgaSBpbiBtYXAuX2xheWVycykge1xyXG5cdFx0XHRpZiAobWFwLl9sYXllcnNbaV0uZ2V0QXR0cmlidXRpb24pIHtcclxuXHRcdFx0XHR0aGlzLmFkZEF0dHJpYnV0aW9uKG1hcC5fbGF5ZXJzW2ldLmdldEF0dHJpYnV0aW9uKCkpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fdXBkYXRlKCk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFByZWZpeChwcmVmaXg6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSB0ZXh0IGJlZm9yZSB0aGUgYXR0cmlidXRpb25zLlxyXG5cdHNldFByZWZpeDogZnVuY3Rpb24gKHByZWZpeCkge1xyXG5cdFx0dGhpcy5vcHRpb25zLnByZWZpeCA9IHByZWZpeDtcclxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRBdHRyaWJ1dGlvbih0ZXh0OiBTdHJpbmcpOiB0aGlzXHJcblx0Ly8gQWRkcyBhbiBhdHRyaWJ1dGlvbiB0ZXh0IChlLmcuIGAnVmVjdG9yIGRhdGEgJmNvcHk7IE1hcGJveCdgKS5cclxuXHRhZGRBdHRyaWJ1dGlvbjogZnVuY3Rpb24gKHRleHQpIHtcclxuXHRcdGlmICghdGV4dCkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdGlmICghdGhpcy5fYXR0cmlidXRpb25zW3RleHRdKSB7XHJcblx0XHRcdHRoaXMuX2F0dHJpYnV0aW9uc1t0ZXh0XSA9IDA7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9hdHRyaWJ1dGlvbnNbdGV4dF0rKztcclxuXHJcblx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZUF0dHJpYnV0aW9uKHRleHQ6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIGFuIGF0dHJpYnV0aW9uIHRleHQuXHJcblx0cmVtb3ZlQXR0cmlidXRpb246IGZ1bmN0aW9uICh0ZXh0KSB7XHJcblx0XHRpZiAoIXRleHQpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRpZiAodGhpcy5fYXR0cmlidXRpb25zW3RleHRdKSB7XHJcblx0XHRcdHRoaXMuX2F0dHJpYnV0aW9uc1t0ZXh0XS0tO1xyXG5cdFx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgYXR0cmlicyA9IFtdO1xyXG5cclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fYXR0cmlidXRpb25zKSB7XHJcblx0XHRcdGlmICh0aGlzLl9hdHRyaWJ1dGlvbnNbaV0pIHtcclxuXHRcdFx0XHRhdHRyaWJzLnB1c2goaSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHR2YXIgcHJlZml4QW5kQXR0cmlicyA9IFtdO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMucHJlZml4KSB7XHJcblx0XHRcdHByZWZpeEFuZEF0dHJpYnMucHVzaCh0aGlzLm9wdGlvbnMucHJlZml4KTtcclxuXHRcdH1cclxuXHRcdGlmIChhdHRyaWJzLmxlbmd0aCkge1xyXG5cdFx0XHRwcmVmaXhBbmRBdHRyaWJzLnB1c2goYXR0cmlicy5qb2luKCcsICcpKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9jb250YWluZXIuaW5uZXJIVE1MID0gcHJlZml4QW5kQXR0cmlicy5qb2luKCcgfCAnKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXBcclxuLy8gQHNlY3Rpb24gQ29udHJvbCBvcHRpb25zXHJcbi8vIEBvcHRpb24gYXR0cmlidXRpb25Db250cm9sOiBCb29sZWFuID0gdHJ1ZVxyXG4vLyBXaGV0aGVyIGEgW2F0dHJpYnV0aW9uIGNvbnRyb2xdKCNjb250cm9sLWF0dHJpYnV0aW9uKSBpcyBhZGRlZCB0byB0aGUgbWFwIGJ5IGRlZmF1bHQuXHJcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XHJcblx0YXR0cmlidXRpb25Db250cm9sOiB0cnVlXHJcbn0pO1xyXG5cclxuTC5NYXAuYWRkSW5pdEhvb2soZnVuY3Rpb24gKCkge1xyXG5cdGlmICh0aGlzLm9wdGlvbnMuYXR0cmlidXRpb25Db250cm9sKSB7XHJcblx0XHRuZXcgTC5Db250cm9sLkF0dHJpYnV0aW9uKCkuYWRkVG8odGhpcyk7XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgQ29udHJvbC5BdHRyaWJ1dGlvblxyXG4vLyBAZmFjdG9yeSBMLmNvbnRyb2wuYXR0cmlidXRpb24ob3B0aW9uczogQ29udHJvbC5BdHRyaWJ1dGlvbiBvcHRpb25zKVxyXG4vLyBDcmVhdGVzIGFuIGF0dHJpYnV0aW9uIGNvbnRyb2wuXHJcbkwuY29udHJvbC5hdHRyaWJ1dGlvbiA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkNvbnRyb2wuQXR0cmlidXRpb24ob3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgQ29udHJvbC5TY2FsZVxuICogQGFrYSBMLkNvbnRyb2wuU2NhbGVcbiAqIEBpbmhlcml0cyBDb250cm9sXG4gKlxuICogQSBzaW1wbGUgc2NhbGUgY29udHJvbCB0aGF0IHNob3dzIHRoZSBzY2FsZSBvZiB0aGUgY3VycmVudCBjZW50ZXIgb2Ygc2NyZWVuIGluIG1ldHJpYyAobS9rbSkgYW5kIGltcGVyaWFsIChtaS9mdCkgc3lzdGVtcy4gRXh0ZW5kcyBgQ29udHJvbGAuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBgYGBqc1xuICogTC5jb250cm9sLnNjYWxlKCkuYWRkVG8obWFwKTtcbiAqIGBgYFxuICovXG5cbkwuQ29udHJvbC5TY2FsZSA9IEwuQ29udHJvbC5leHRlbmQoe1xuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIENvbnRyb2wuU2NhbGUgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0cG9zaXRpb246ICdib3R0b21sZWZ0JyxcblxuXHRcdC8vIEBvcHRpb24gbWF4V2lkdGg6IE51bWJlciA9IDEwMFxuXHRcdC8vIE1heGltdW0gd2lkdGggb2YgdGhlIGNvbnRyb2wgaW4gcGl4ZWxzLiBUaGUgd2lkdGggaXMgc2V0IGR5bmFtaWNhbGx5IHRvIHNob3cgcm91bmQgdmFsdWVzIChlLmcuIDEwMCwgMjAwLCA1MDApLlxuXHRcdG1heFdpZHRoOiAxMDAsXG5cblx0XHQvLyBAb3B0aW9uIG1ldHJpYzogQm9vbGVhbiA9IFRydWVcblx0XHQvLyBXaGV0aGVyIHRvIHNob3cgdGhlIG1ldHJpYyBzY2FsZSBsaW5lIChtL2ttKS5cblx0XHRtZXRyaWM6IHRydWUsXG5cblx0XHQvLyBAb3B0aW9uIGltcGVyaWFsOiBCb29sZWFuID0gVHJ1ZVxuXHRcdC8vIFdoZXRoZXIgdG8gc2hvdyB0aGUgaW1wZXJpYWwgc2NhbGUgbGluZSAobWkvZnQpLlxuXHRcdGltcGVyaWFsOiB0cnVlXG5cblx0XHQvLyBAb3B0aW9uIHVwZGF0ZVdoZW5JZGxlOiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBjb250cm9sIGlzIHVwZGF0ZWQgb24gW2Btb3ZlZW5kYF0oI21hcC1tb3ZlZW5kKSwgb3RoZXJ3aXNlIGl0J3MgYWx3YXlzIHVwLXRvLWRhdGUgKHVwZGF0ZWQgb24gW2Btb3ZlYF0oI21hcC1tb3ZlKSkuXG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcblx0XHR2YXIgY2xhc3NOYW1lID0gJ2xlYWZsZXQtY29udHJvbC1zY2FsZScsXG5cdFx0ICAgIGNvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSksXG5cdFx0ICAgIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XG5cblx0XHR0aGlzLl9hZGRTY2FsZXMob3B0aW9ucywgY2xhc3NOYW1lICsgJy1saW5lJywgY29udGFpbmVyKTtcblxuXHRcdG1hcC5vbihvcHRpb25zLnVwZGF0ZVdoZW5JZGxlID8gJ21vdmVlbmQnIDogJ21vdmUnLCB0aGlzLl91cGRhdGUsIHRoaXMpO1xuXHRcdG1hcC53aGVuUmVhZHkodGhpcy5fdXBkYXRlLCB0aGlzKTtcblxuXHRcdHJldHVybiBjb250YWluZXI7XG5cdH0sXG5cblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHRtYXAub2ZmKHRoaXMub3B0aW9ucy51cGRhdGVXaGVuSWRsZSA/ICdtb3ZlZW5kJyA6ICdtb3ZlJywgdGhpcy5fdXBkYXRlLCB0aGlzKTtcblx0fSxcblxuXHRfYWRkU2NhbGVzOiBmdW5jdGlvbiAob3B0aW9ucywgY2xhc3NOYW1lLCBjb250YWluZXIpIHtcblx0XHRpZiAob3B0aW9ucy5tZXRyaWMpIHtcblx0XHRcdHRoaXMuX21TY2FsZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSwgY29udGFpbmVyKTtcblx0XHR9XG5cdFx0aWYgKG9wdGlvbnMuaW1wZXJpYWwpIHtcblx0XHRcdHRoaXMuX2lTY2FsZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSwgY29udGFpbmVyKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXAsXG5cdFx0ICAgIHkgPSBtYXAuZ2V0U2l6ZSgpLnkgLyAyO1xuXG5cdFx0dmFyIG1heE1ldGVycyA9IG1hcC5kaXN0YW5jZShcblx0XHRcdFx0bWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcoWzAsIHldKSxcblx0XHRcdFx0bWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcoW3RoaXMub3B0aW9ucy5tYXhXaWR0aCwgeV0pKTtcblxuXHRcdHRoaXMuX3VwZGF0ZVNjYWxlcyhtYXhNZXRlcnMpO1xuXHR9LFxuXG5cdF91cGRhdGVTY2FsZXM6IGZ1bmN0aW9uIChtYXhNZXRlcnMpIHtcblx0XHRpZiAodGhpcy5vcHRpb25zLm1ldHJpYyAmJiBtYXhNZXRlcnMpIHtcblx0XHRcdHRoaXMuX3VwZGF0ZU1ldHJpYyhtYXhNZXRlcnMpO1xuXHRcdH1cblx0XHRpZiAodGhpcy5vcHRpb25zLmltcGVyaWFsICYmIG1heE1ldGVycykge1xuXHRcdFx0dGhpcy5fdXBkYXRlSW1wZXJpYWwobWF4TWV0ZXJzKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZU1ldHJpYzogZnVuY3Rpb24gKG1heE1ldGVycykge1xuXHRcdHZhciBtZXRlcnMgPSB0aGlzLl9nZXRSb3VuZE51bShtYXhNZXRlcnMpLFxuXHRcdCAgICBsYWJlbCA9IG1ldGVycyA8IDEwMDAgPyBtZXRlcnMgKyAnIG0nIDogKG1ldGVycyAvIDEwMDApICsgJyBrbSc7XG5cblx0XHR0aGlzLl91cGRhdGVTY2FsZSh0aGlzLl9tU2NhbGUsIGxhYmVsLCBtZXRlcnMgLyBtYXhNZXRlcnMpO1xuXHR9LFxuXG5cdF91cGRhdGVJbXBlcmlhbDogZnVuY3Rpb24gKG1heE1ldGVycykge1xuXHRcdHZhciBtYXhGZWV0ID0gbWF4TWV0ZXJzICogMy4yODA4Mzk5LFxuXHRcdCAgICBtYXhNaWxlcywgbWlsZXMsIGZlZXQ7XG5cblx0XHRpZiAobWF4RmVldCA+IDUyODApIHtcblx0XHRcdG1heE1pbGVzID0gbWF4RmVldCAvIDUyODA7XG5cdFx0XHRtaWxlcyA9IHRoaXMuX2dldFJvdW5kTnVtKG1heE1pbGVzKTtcblx0XHRcdHRoaXMuX3VwZGF0ZVNjYWxlKHRoaXMuX2lTY2FsZSwgbWlsZXMgKyAnIG1pJywgbWlsZXMgLyBtYXhNaWxlcyk7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0ZmVldCA9IHRoaXMuX2dldFJvdW5kTnVtKG1heEZlZXQpO1xuXHRcdFx0dGhpcy5fdXBkYXRlU2NhbGUodGhpcy5faVNjYWxlLCBmZWV0ICsgJyBmdCcsIGZlZXQgLyBtYXhGZWV0KTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVNjYWxlOiBmdW5jdGlvbiAoc2NhbGUsIHRleHQsIHJhdGlvKSB7XG5cdFx0c2NhbGUuc3R5bGUud2lkdGggPSBNYXRoLnJvdW5kKHRoaXMub3B0aW9ucy5tYXhXaWR0aCAqIHJhdGlvKSArICdweCc7XG5cdFx0c2NhbGUuaW5uZXJIVE1MID0gdGV4dDtcblx0fSxcblxuXHRfZ2V0Um91bmROdW06IGZ1bmN0aW9uIChudW0pIHtcblx0XHR2YXIgcG93MTAgPSBNYXRoLnBvdygxMCwgKE1hdGguZmxvb3IobnVtKSArICcnKS5sZW5ndGggLSAxKSxcblx0XHQgICAgZCA9IG51bSAvIHBvdzEwO1xuXG5cdFx0ZCA9IGQgPj0gMTAgPyAxMCA6XG5cdFx0ICAgIGQgPj0gNSA/IDUgOlxuXHRcdCAgICBkID49IDMgPyAzIDpcblx0XHQgICAgZCA+PSAyID8gMiA6IDE7XG5cblx0XHRyZXR1cm4gcG93MTAgKiBkO1xuXHR9XG59KTtcblxuXG4vLyBAZmFjdG9yeSBMLmNvbnRyb2wuc2NhbGUob3B0aW9ucz86IENvbnRyb2wuU2NhbGUgb3B0aW9ucylcbi8vIENyZWF0ZXMgYW4gc2NhbGUgY29udHJvbCB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuTC5jb250cm9sLnNjYWxlID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcblx0cmV0dXJuIG5ldyBMLkNvbnRyb2wuU2NhbGUob3B0aW9ucyk7XG59O1xuXG5cblxuLypcclxuICogQGNsYXNzIENvbnRyb2wuTGF5ZXJzXHJcbiAqIEBha2EgTC5Db250cm9sLkxheWVyc1xyXG4gKiBAaW5oZXJpdHMgQ29udHJvbFxyXG4gKlxyXG4gKiBUaGUgbGF5ZXJzIGNvbnRyb2wgZ2l2ZXMgdXNlcnMgdGhlIGFiaWxpdHkgdG8gc3dpdGNoIGJldHdlZW4gZGlmZmVyZW50IGJhc2UgbGF5ZXJzIGFuZCBzd2l0Y2ggb3ZlcmxheXMgb24vb2ZmIChjaGVjayBvdXQgdGhlIFtkZXRhaWxlZCBleGFtcGxlXShodHRwOi8vbGVhZmxldGpzLmNvbS9leGFtcGxlcy9sYXllcnMtY29udHJvbC5odG1sKSkuIEV4dGVuZHMgYENvbnRyb2xgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgYmFzZUxheWVycyA9IHtcclxuICogXHRcIk1hcGJveFwiOiBtYXBib3gsXHJcbiAqIFx0XCJPcGVuU3RyZWV0TWFwXCI6IG9zbVxyXG4gKiB9O1xyXG4gKlxyXG4gKiB2YXIgb3ZlcmxheXMgPSB7XHJcbiAqIFx0XCJNYXJrZXJcIjogbWFya2VyLFxyXG4gKiBcdFwiUm9hZHNcIjogcm9hZHNMYXllclxyXG4gKiB9O1xyXG4gKlxyXG4gKiBMLmNvbnRyb2wubGF5ZXJzKGJhc2VMYXllcnMsIG92ZXJsYXlzKS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICpcclxuICogVGhlIGBiYXNlTGF5ZXJzYCBhbmQgYG92ZXJsYXlzYCBwYXJhbWV0ZXJzIGFyZSBvYmplY3QgbGl0ZXJhbHMgd2l0aCBsYXllciBuYW1lcyBhcyBrZXlzIGFuZCBgTGF5ZXJgIG9iamVjdHMgYXMgdmFsdWVzOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB7XHJcbiAqICAgICBcIjxzb21lTmFtZTE+XCI6IGxheWVyMSxcclxuICogICAgIFwiPHNvbWVOYW1lMj5cIjogbGF5ZXIyXHJcbiAqIH1cclxuICogYGBgXHJcbiAqXHJcbiAqIFRoZSBsYXllciBuYW1lcyBjYW4gY29udGFpbiBIVE1MLCB3aGljaCBhbGxvd3MgeW91IHRvIGFkZCBhZGRpdGlvbmFsIHN0eWxpbmcgdG8gdGhlIGl0ZW1zOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB7XCI8aW1nIHNyYz0nbXktbGF5ZXItaWNvbicgLz4gPHNwYW4gY2xhc3M9J215LWxheWVyLWl0ZW0nPk15IExheWVyPC9zcGFuPlwiOiBteUxheWVyfVxyXG4gKiBgYGBcclxuICovXHJcblxyXG5cclxuTC5Db250cm9sLkxheWVycyA9IEwuQ29udHJvbC5leHRlbmQoe1xyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBDb250cm9sLkxheWVycyBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBjb2xsYXBzZWQ6IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBjb250cm9sIHdpbGwgYmUgY29sbGFwc2VkIGludG8gYW4gaWNvbiBhbmQgZXhwYW5kZWQgb24gbW91c2UgaG92ZXIgb3IgdG91Y2guXHJcblx0XHRjb2xsYXBzZWQ6IHRydWUsXHJcblx0XHRwb3NpdGlvbjogJ3RvcHJpZ2h0JyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGF1dG9aSW5kZXg6IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBJZiBgdHJ1ZWAsIHRoZSBjb250cm9sIHdpbGwgYXNzaWduIHpJbmRleGVzIGluIGluY3JlYXNpbmcgb3JkZXIgdG8gYWxsIG9mIGl0cyBsYXllcnMgc28gdGhhdCB0aGUgb3JkZXIgaXMgcHJlc2VydmVkIHdoZW4gc3dpdGNoaW5nIHRoZW0gb24vb2ZmLlxyXG5cdFx0YXV0b1pJbmRleDogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGhpZGVTaW5nbGVCYXNlOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGJhc2UgbGF5ZXJzIGluIHRoZSBjb250cm9sIHdpbGwgYmUgaGlkZGVuIHdoZW4gdGhlcmUgaXMgb25seSBvbmUuXHJcblx0XHRoaWRlU2luZ2xlQmFzZTogZmFsc2UsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBzb3J0TGF5ZXJzOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIFdoZXRoZXIgdG8gc29ydCB0aGUgbGF5ZXJzLiBXaGVuIGBmYWxzZWAsIGxheWVycyB3aWxsIGtlZXAgdGhlIG9yZGVyXHJcblx0XHQvLyBpbiB3aGljaCB0aGV5IHdlcmUgYWRkZWQgdG8gdGhlIGNvbnRyb2wuXHJcblx0XHRzb3J0TGF5ZXJzOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHNvcnRGdW5jdGlvbjogRnVuY3Rpb24gPSAqXHJcblx0XHQvLyBBIFtjb21wYXJlIGZ1bmN0aW9uXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9BcnJheS9zb3J0KVxyXG5cdFx0Ly8gdGhhdCB3aWxsIGJlIHVzZWQgZm9yIHNvcnRpbmcgdGhlIGxheWVycywgd2hlbiBgc29ydExheWVyc2AgaXMgYHRydWVgLlxyXG5cdFx0Ly8gVGhlIGZ1bmN0aW9uIHJlY2VpdmVzIGJvdGggdGhlIGBMLkxheWVyYCBpbnN0YW5jZXMgYW5kIHRoZWlyIG5hbWVzLCBhcyBpblxyXG5cdFx0Ly8gYHNvcnRGdW5jdGlvbihsYXllckEsIGxheWVyQiwgbmFtZUEsIG5hbWVCKWAuXHJcblx0XHQvLyBCeSBkZWZhdWx0LCBpdCBzb3J0cyBsYXllcnMgYWxwaGFiZXRpY2FsbHkgYnkgdGhlaXIgbmFtZS5cclxuXHRcdHNvcnRGdW5jdGlvbjogZnVuY3Rpb24gKGxheWVyQSwgbGF5ZXJCLCBuYW1lQSwgbmFtZUIpIHtcclxuXHRcdFx0cmV0dXJuIG5hbWVBIDwgbmFtZUIgPyAtMSA6IChuYW1lQiA8IG5hbWVBID8gMSA6IDApO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChiYXNlTGF5ZXJzLCBvdmVybGF5cywgb3B0aW9ucykge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX2xheWVycyA9IFtdO1xyXG5cdFx0dGhpcy5fbGFzdFpJbmRleCA9IDA7XHJcblx0XHR0aGlzLl9oYW5kbGluZ0NsaWNrID0gZmFsc2U7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiBiYXNlTGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMuX2FkZExheWVyKGJhc2VMYXllcnNbaV0sIGkpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGZvciAoaSBpbiBvdmVybGF5cykge1xyXG5cdFx0XHR0aGlzLl9hZGRMYXllcihvdmVybGF5c1tpXSwgaSwgdHJ1ZSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHRoaXMuX2luaXRMYXlvdXQoKTtcclxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cclxuXHRcdHRoaXMuX21hcCA9IG1hcDtcclxuXHRcdG1hcC5vbignem9vbWVuZCcsIHRoaXMuX2NoZWNrRGlzYWJsZWRMYXllcnMsIHRoaXMpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzLl9jb250YWluZXI7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX21hcC5vZmYoJ3pvb21lbmQnLCB0aGlzLl9jaGVja0Rpc2FibGVkTGF5ZXJzLCB0aGlzKTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMuX2xheWVycy5sZW5ndGg7IGkrKykge1xyXG5cdFx0XHR0aGlzLl9sYXllcnNbaV0ubGF5ZXIub2ZmKCdhZGQgcmVtb3ZlJywgdGhpcy5fb25MYXllckNoYW5nZSwgdGhpcyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRCYXNlTGF5ZXIobGF5ZXI6IExheWVyLCBuYW1lOiBTdHJpbmcpOiB0aGlzXHJcblx0Ly8gQWRkcyBhIGJhc2UgbGF5ZXIgKHJhZGlvIGJ1dHRvbiBlbnRyeSkgd2l0aCB0aGUgZ2l2ZW4gbmFtZSB0byB0aGUgY29udHJvbC5cclxuXHRhZGRCYXNlTGF5ZXI6IGZ1bmN0aW9uIChsYXllciwgbmFtZSkge1xyXG5cdFx0dGhpcy5fYWRkTGF5ZXIobGF5ZXIsIG5hbWUpO1xyXG5cdFx0cmV0dXJuICh0aGlzLl9tYXApID8gdGhpcy5fdXBkYXRlKCkgOiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkT3ZlcmxheShsYXllcjogTGF5ZXIsIG5hbWU6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBBZGRzIGFuIG92ZXJsYXkgKGNoZWNrYm94IGVudHJ5KSB3aXRoIHRoZSBnaXZlbiBuYW1lIHRvIHRoZSBjb250cm9sLlxyXG5cdGFkZE92ZXJsYXk6IGZ1bmN0aW9uIChsYXllciwgbmFtZSkge1xyXG5cdFx0dGhpcy5fYWRkTGF5ZXIobGF5ZXIsIG5hbWUsIHRydWUpO1xyXG5cdFx0cmV0dXJuICh0aGlzLl9tYXApID8gdGhpcy5fdXBkYXRlKCkgOiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlTGF5ZXIobGF5ZXI6IExheWVyKTogdGhpc1xyXG5cdC8vIFJlbW92ZSB0aGUgZ2l2ZW4gbGF5ZXIgZnJvbSB0aGUgY29udHJvbC5cclxuXHRyZW1vdmVMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRsYXllci5vZmYoJ2FkZCByZW1vdmUnLCB0aGlzLl9vbkxheWVyQ2hhbmdlLCB0aGlzKTtcclxuXHJcblx0XHR2YXIgb2JqID0gdGhpcy5fZ2V0TGF5ZXIoTC5zdGFtcChsYXllcikpO1xyXG5cdFx0aWYgKG9iaikge1xyXG5cdFx0XHR0aGlzLl9sYXllcnMuc3BsaWNlKHRoaXMuX2xheWVycy5pbmRleE9mKG9iaiksIDEpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuICh0aGlzLl9tYXApID8gdGhpcy5fdXBkYXRlKCkgOiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZXhwYW5kKCk6IHRoaXNcclxuXHQvLyBFeHBhbmQgdGhlIGNvbnRyb2wgY29udGFpbmVyIGlmIGNvbGxhcHNlZC5cclxuXHRleHBhbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWNvbnRyb2wtbGF5ZXJzLWV4cGFuZGVkJyk7XHJcblx0XHR0aGlzLl9mb3JtLnN0eWxlLmhlaWdodCA9IG51bGw7XHJcblx0XHR2YXIgYWNjZXB0YWJsZUhlaWdodCA9IHRoaXMuX21hcC5nZXRTaXplKCkueSAtICh0aGlzLl9jb250YWluZXIub2Zmc2V0VG9wICsgNTApO1xyXG5cdFx0aWYgKGFjY2VwdGFibGVIZWlnaHQgPCB0aGlzLl9mb3JtLmNsaWVudEhlaWdodCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fZm9ybSwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtc2Nyb2xsYmFyJyk7XHJcblx0XHRcdHRoaXMuX2Zvcm0uc3R5bGUuaGVpZ2h0ID0gYWNjZXB0YWJsZUhlaWdodCArICdweCc7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fZm9ybSwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtc2Nyb2xsYmFyJyk7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9jaGVja0Rpc2FibGVkTGF5ZXJzKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbGxhcHNlKCk6IHRoaXNcclxuXHQvLyBDb2xsYXBzZSB0aGUgY29udHJvbCBjb250YWluZXIgaWYgZXhwYW5kZWQuXHJcblx0Y29sbGFwc2U6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWNvbnRyb2wtbGF5ZXJzLWV4cGFuZGVkJyk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNsYXNzTmFtZSA9ICdsZWFmbGV0LWNvbnRyb2wtbGF5ZXJzJyxcclxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUpO1xyXG5cclxuXHRcdC8vIG1ha2VzIHRoaXMgd29yayBvbiBJRSB0b3VjaCBkZXZpY2VzIGJ5IHN0b3BwaW5nIGl0IGZyb20gZmlyaW5nIGEgbW91c2VvdXQgZXZlbnQgd2hlbiB0aGUgdG91Y2ggaXMgcmVsZWFzZWRcclxuXHRcdGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ2FyaWEtaGFzcG9wdXAnLCB0cnVlKTtcclxuXHJcblx0XHRMLkRvbUV2ZW50LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKGNvbnRhaW5lcik7XHJcblx0XHRpZiAoIUwuQnJvd3Nlci50b3VjaCkge1xyXG5cdFx0XHRMLkRvbUV2ZW50LmRpc2FibGVTY3JvbGxQcm9wYWdhdGlvbihjb250YWluZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBmb3JtID0gdGhpcy5fZm9ybSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2Zvcm0nLCBjbGFzc05hbWUgKyAnLWxpc3QnKTtcclxuXHJcblx0XHRpZiAoIUwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdEwuRG9tRXZlbnQub24oY29udGFpbmVyLCB7XHJcblx0XHRcdFx0bW91c2VlbnRlcjogdGhpcy5leHBhbmQsXHJcblx0XHRcdFx0bW91c2VsZWF2ZTogdGhpcy5jb2xsYXBzZVxyXG5cdFx0XHR9LCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgbGluayA9IHRoaXMuX2xheWVyc0xpbmsgPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgY2xhc3NOYW1lICsgJy10b2dnbGUnLCBjb250YWluZXIpO1xyXG5cdFx0bGluay5ocmVmID0gJyMnO1xyXG5cdFx0bGluay50aXRsZSA9ICdMYXllcnMnO1xyXG5cclxuXHRcdGlmIChMLkJyb3dzZXIudG91Y2gpIHtcclxuXHRcdFx0TC5Eb21FdmVudFxyXG5cdFx0XHQgICAgLm9uKGxpbmssICdjbGljaycsIEwuRG9tRXZlbnQuc3RvcClcclxuXHRcdFx0ICAgIC5vbihsaW5rLCAnY2xpY2snLCB0aGlzLmV4cGFuZCwgdGhpcyk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRMLkRvbUV2ZW50Lm9uKGxpbmssICdmb2N1cycsIHRoaXMuZXhwYW5kLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyB3b3JrIGFyb3VuZCBmb3IgRmlyZWZveCBBbmRyb2lkIGlzc3VlIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzIwMzNcclxuXHRcdEwuRG9tRXZlbnQub24oZm9ybSwgJ2NsaWNrJywgZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRzZXRUaW1lb3V0KEwuYmluZCh0aGlzLl9vbklucHV0Q2xpY2ssIHRoaXMpLCAwKTtcclxuXHRcdH0sIHRoaXMpO1xyXG5cclxuXHRcdHRoaXMuX21hcC5vbignY2xpY2snLCB0aGlzLmNvbGxhcHNlLCB0aGlzKTtcclxuXHRcdC8vIFRPRE8ga2V5Ym9hcmQgYWNjZXNzaWJpbGl0eVxyXG5cclxuXHRcdGlmICghdGhpcy5vcHRpb25zLmNvbGxhcHNlZCkge1xyXG5cdFx0XHR0aGlzLmV4cGFuZCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2Jhc2VMYXllcnNMaXN0ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lICsgJy1iYXNlJywgZm9ybSk7XHJcblx0XHR0aGlzLl9zZXBhcmF0b3IgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUgKyAnLXNlcGFyYXRvcicsIGZvcm0pO1xyXG5cdFx0dGhpcy5fb3ZlcmxheXNMaXN0ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lICsgJy1vdmVybGF5cycsIGZvcm0pO1xyXG5cclxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChmb3JtKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0TGF5ZXI6IGZ1bmN0aW9uIChpZCkge1xyXG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLl9sYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHJcblx0XHRcdGlmICh0aGlzLl9sYXllcnNbaV0gJiYgTC5zdGFtcCh0aGlzLl9sYXllcnNbaV0ubGF5ZXIpID09PSBpZCkge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLl9sYXllcnNbaV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfYWRkTGF5ZXI6IGZ1bmN0aW9uIChsYXllciwgbmFtZSwgb3ZlcmxheSkge1xyXG5cdFx0bGF5ZXIub24oJ2FkZCByZW1vdmUnLCB0aGlzLl9vbkxheWVyQ2hhbmdlLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9sYXllcnMucHVzaCh7XHJcblx0XHRcdGxheWVyOiBsYXllcixcclxuXHRcdFx0bmFtZTogbmFtZSxcclxuXHRcdFx0b3ZlcmxheTogb3ZlcmxheVxyXG5cdFx0fSk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5zb3J0TGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMuX2xheWVycy5zb3J0KEwuYmluZChmdW5jdGlvbiAoYSwgYikge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLm9wdGlvbnMuc29ydEZ1bmN0aW9uKGEubGF5ZXIsIGIubGF5ZXIsIGEubmFtZSwgYi5uYW1lKTtcclxuXHRcdFx0fSwgdGhpcykpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuYXV0b1pJbmRleCAmJiBsYXllci5zZXRaSW5kZXgpIHtcclxuXHRcdFx0dGhpcy5fbGFzdFpJbmRleCsrO1xyXG5cdFx0XHRsYXllci5zZXRaSW5kZXgodGhpcy5fbGFzdFpJbmRleCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9jb250YWluZXIpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRMLkRvbVV0aWwuZW1wdHkodGhpcy5fYmFzZUxheWVyc0xpc3QpO1xyXG5cdFx0TC5Eb21VdGlsLmVtcHR5KHRoaXMuX292ZXJsYXlzTGlzdCk7XHJcblxyXG5cdFx0dmFyIGJhc2VMYXllcnNQcmVzZW50LCBvdmVybGF5c1ByZXNlbnQsIGksIG9iaiwgYmFzZUxheWVyc0NvdW50ID0gMDtcclxuXHJcblx0XHRmb3IgKGkgPSAwOyBpIDwgdGhpcy5fbGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdG9iaiA9IHRoaXMuX2xheWVyc1tpXTtcclxuXHRcdFx0dGhpcy5fYWRkSXRlbShvYmopO1xyXG5cdFx0XHRvdmVybGF5c1ByZXNlbnQgPSBvdmVybGF5c1ByZXNlbnQgfHwgb2JqLm92ZXJsYXk7XHJcblx0XHRcdGJhc2VMYXllcnNQcmVzZW50ID0gYmFzZUxheWVyc1ByZXNlbnQgfHwgIW9iai5vdmVybGF5O1xyXG5cdFx0XHRiYXNlTGF5ZXJzQ291bnQgKz0gIW9iai5vdmVybGF5ID8gMSA6IDA7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gSGlkZSBiYXNlIGxheWVycyBzZWN0aW9uIGlmIHRoZXJlJ3Mgb25seSBvbmUgbGF5ZXIuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmhpZGVTaW5nbGVCYXNlKSB7XHJcblx0XHRcdGJhc2VMYXllcnNQcmVzZW50ID0gYmFzZUxheWVyc1ByZXNlbnQgJiYgYmFzZUxheWVyc0NvdW50ID4gMTtcclxuXHRcdFx0dGhpcy5fYmFzZUxheWVyc0xpc3Quc3R5bGUuZGlzcGxheSA9IGJhc2VMYXllcnNQcmVzZW50ID8gJycgOiAnbm9uZSc7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fc2VwYXJhdG9yLnN0eWxlLmRpc3BsYXkgPSBvdmVybGF5c1ByZXNlbnQgJiYgYmFzZUxheWVyc1ByZXNlbnQgPyAnJyA6ICdub25lJztcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfb25MYXllckNoYW5nZTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdGlmICghdGhpcy5faGFuZGxpbmdDbGljaykge1xyXG5cdFx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgb2JqID0gdGhpcy5fZ2V0TGF5ZXIoTC5zdGFtcChlLnRhcmdldCkpO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBMYXllciBldmVudHNcclxuXHRcdC8vIEBldmVudCBiYXNlbGF5ZXJjaGFuZ2U6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYmFzZSBsYXllciBpcyBjaGFuZ2VkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlhZGQ6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIHNlbGVjdGVkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlyZW1vdmU6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIGRlc2VsZWN0ZWQgdGhyb3VnaCB0aGUgW2xheWVyIGNvbnRyb2xdKCNjb250cm9sLWxheWVycykuXHJcblx0XHQvLyBAbmFtZXNwYWNlIENvbnRyb2wuTGF5ZXJzXHJcblx0XHR2YXIgdHlwZSA9IG9iai5vdmVybGF5ID9cclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnb3ZlcmxheWFkZCcgOiAnb3ZlcmxheXJlbW92ZScpIDpcclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnYmFzZWxheWVyY2hhbmdlJyA6IG51bGwpO1xyXG5cclxuXHRcdGlmICh0eXBlKSB7XHJcblx0XHRcdHRoaXMuX21hcC5maXJlKHR5cGUsIG9iaik7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gSUU3IGJ1Z3Mgb3V0IGlmIHlvdSBjcmVhdGUgYSByYWRpbyBkeW5hbWljYWxseSwgc28geW91IGhhdmUgdG8gZG8gaXQgdGhpcyBoYWNreSB3YXkgKHNlZSBodHRwOi8vYml0Lmx5L1BxWUxCZSlcclxuXHRfY3JlYXRlUmFkaW9FbGVtZW50OiBmdW5jdGlvbiAobmFtZSwgY2hlY2tlZCkge1xyXG5cclxuXHRcdHZhciByYWRpb0h0bWwgPSAnPGlucHV0IHR5cGU9XCJyYWRpb1wiIGNsYXNzPVwibGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3RvclwiIG5hbWU9XCInICtcclxuXHRcdFx0XHRuYW1lICsgJ1wiJyArIChjaGVja2VkID8gJyBjaGVja2VkPVwiY2hlY2tlZFwiJyA6ICcnKSArICcvPic7XHJcblxyXG5cdFx0dmFyIHJhZGlvRnJhZ21lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHRcdHJhZGlvRnJhZ21lbnQuaW5uZXJIVE1MID0gcmFkaW9IdG1sO1xyXG5cclxuXHRcdHJldHVybiByYWRpb0ZyYWdtZW50LmZpcnN0Q2hpbGQ7XHJcblx0fSxcclxuXHJcblx0X2FkZEl0ZW06IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHZhciBsYWJlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xhYmVsJyksXHJcblx0XHQgICAgY2hlY2tlZCA9IHRoaXMuX21hcC5oYXNMYXllcihvYmoubGF5ZXIpLFxyXG5cdFx0ICAgIGlucHV0O1xyXG5cclxuXHRcdGlmIChvYmoub3ZlcmxheSkge1xyXG5cdFx0XHRpbnB1dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7XHJcblx0XHRcdGlucHV0LnR5cGUgPSAnY2hlY2tib3gnO1xyXG5cdFx0XHRpbnB1dC5jbGFzc05hbWUgPSAnbGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3Rvcic7XHJcblx0XHRcdGlucHV0LmRlZmF1bHRDaGVja2VkID0gY2hlY2tlZDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGlucHV0ID0gdGhpcy5fY3JlYXRlUmFkaW9FbGVtZW50KCdsZWFmbGV0LWJhc2UtbGF5ZXJzJywgY2hlY2tlZCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aW5wdXQubGF5ZXJJZCA9IEwuc3RhbXAob2JqLmxheWVyKTtcclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9uKGlucHV0LCAnY2xpY2snLCB0aGlzLl9vbklucHV0Q2xpY2ssIHRoaXMpO1xyXG5cclxuXHRcdHZhciBuYW1lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xyXG5cdFx0bmFtZS5pbm5lckhUTUwgPSAnICcgKyBvYmoubmFtZTtcclxuXHJcblx0XHQvLyBIZWxwcyBmcm9tIHByZXZlbnRpbmcgbGF5ZXIgY29udHJvbCBmbGlja2VyIHdoZW4gY2hlY2tib3hlcyBhcmUgZGlzYWJsZWRcclxuXHRcdC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzI3NzFcclxuXHRcdHZhciBob2xkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHJcblx0XHRsYWJlbC5hcHBlbmRDaGlsZChob2xkZXIpO1xyXG5cdFx0aG9sZGVyLmFwcGVuZENoaWxkKGlucHV0KTtcclxuXHRcdGhvbGRlci5hcHBlbmRDaGlsZChuYW1lKTtcclxuXHJcblx0XHR2YXIgY29udGFpbmVyID0gb2JqLm92ZXJsYXkgPyB0aGlzLl9vdmVybGF5c0xpc3QgOiB0aGlzLl9iYXNlTGF5ZXJzTGlzdDtcclxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChsYWJlbCk7XHJcblxyXG5cdFx0dGhpcy5fY2hlY2tEaXNhYmxlZExheWVycygpO1xyXG5cdFx0cmV0dXJuIGxhYmVsO1xyXG5cdH0sXHJcblxyXG5cdF9vbklucHV0Q2xpY2s6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBpbnB1dHMgPSB0aGlzLl9mb3JtLmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbnB1dCcpLFxyXG5cdFx0ICAgIGlucHV0LCBsYXllciwgaGFzTGF5ZXI7XHJcblx0XHR2YXIgYWRkZWRMYXllcnMgPSBbXSxcclxuXHRcdCAgICByZW1vdmVkTGF5ZXJzID0gW107XHJcblxyXG5cdFx0dGhpcy5faGFuZGxpbmdDbGljayA9IHRydWU7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IGlucHV0cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xyXG5cdFx0XHRpbnB1dCA9IGlucHV0c1tpXTtcclxuXHRcdFx0bGF5ZXIgPSB0aGlzLl9nZXRMYXllcihpbnB1dC5sYXllcklkKS5sYXllcjtcclxuXHRcdFx0aGFzTGF5ZXIgPSB0aGlzLl9tYXAuaGFzTGF5ZXIobGF5ZXIpO1xyXG5cclxuXHRcdFx0aWYgKGlucHV0LmNoZWNrZWQgJiYgIWhhc0xheWVyKSB7XHJcblx0XHRcdFx0YWRkZWRMYXllcnMucHVzaChsYXllcik7XHJcblxyXG5cdFx0XHR9IGVsc2UgaWYgKCFpbnB1dC5jaGVja2VkICYmIGhhc0xheWVyKSB7XHJcblx0XHRcdFx0cmVtb3ZlZExheWVycy5wdXNoKGxheWVyKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEJ1Z2ZpeCBpc3N1ZSAyMzE4OiBTaG91bGQgcmVtb3ZlIGFsbCBvbGQgbGF5ZXJzIGJlZm9yZSByZWFkZGluZyBuZXcgb25lc1xyXG5cdFx0Zm9yIChpID0gMDsgaSA8IHJlbW92ZWRMYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGhpcy5fbWFwLnJlbW92ZUxheWVyKHJlbW92ZWRMYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0Zm9yIChpID0gMDsgaSA8IGFkZGVkTGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdHRoaXMuX21hcC5hZGRMYXllcihhZGRlZExheWVyc1tpXSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faGFuZGxpbmdDbGljayA9IGZhbHNlO1xyXG5cclxuXHRcdHRoaXMuX3JlZm9jdXNPbk1hcCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jaGVja0Rpc2FibGVkTGF5ZXJzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgaW5wdXRzID0gdGhpcy5fZm9ybS5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaW5wdXQnKSxcclxuXHRcdCAgICBpbnB1dCxcclxuXHRcdCAgICBsYXllcixcclxuXHRcdCAgICB6b29tID0gdGhpcy5fbWFwLmdldFpvb20oKTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gaW5wdXRzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XHJcblx0XHRcdGlucHV0ID0gaW5wdXRzW2ldO1xyXG5cdFx0XHRsYXllciA9IHRoaXMuX2dldExheWVyKGlucHV0LmxheWVySWQpLmxheWVyO1xyXG5cdFx0XHRpbnB1dC5kaXNhYmxlZCA9IChsYXllci5vcHRpb25zLm1pblpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tIDwgbGF5ZXIub3B0aW9ucy5taW5ab29tKSB8fFxyXG5cdFx0XHQgICAgICAgICAgICAgICAgIChsYXllci5vcHRpb25zLm1heFpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tID4gbGF5ZXIub3B0aW9ucy5tYXhab29tKTtcclxuXHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2V4cGFuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gQmFja3dhcmQgY29tcGF0aWJpbGl0eSwgcmVtb3ZlIG1lIGluIDEuMS5cclxuXHRcdHJldHVybiB0aGlzLmV4cGFuZCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jb2xsYXBzZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gQmFja3dhcmQgY29tcGF0aWJpbGl0eSwgcmVtb3ZlIG1lIGluIDEuMS5cclxuXHRcdHJldHVybiB0aGlzLmNvbGxhcHNlKCk7XHJcblx0fVxyXG5cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5jb250cm9sLmxheWVycyhiYXNlbGF5ZXJzPzogT2JqZWN0LCBvdmVybGF5cz86IE9iamVjdCwgb3B0aW9ucz86IENvbnRyb2wuTGF5ZXJzIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYW4gYXR0cmlidXRpb24gY29udHJvbCB3aXRoIHRoZSBnaXZlbiBsYXllcnMuIEJhc2UgbGF5ZXJzIHdpbGwgYmUgc3dpdGNoZWQgd2l0aCByYWRpbyBidXR0b25zLCB3aGlsZSBvdmVybGF5cyB3aWxsIGJlIHN3aXRjaGVkIHdpdGggY2hlY2tib3hlcy4gTm90ZSB0aGF0IGFsbCBiYXNlIGxheWVycyBzaG91bGQgYmUgcGFzc2VkIGluIHRoZSBiYXNlIGxheWVycyBvYmplY3QsIGJ1dCBvbmx5IG9uZSBzaG91bGQgYmUgYWRkZWQgdG8gdGhlIG1hcCBkdXJpbmcgbWFwIGluc3RhbnRpYXRpb24uXHJcbkwuY29udHJvbC5sYXllcnMgPSBmdW5jdGlvbiAoYmFzZUxheWVycywgb3ZlcmxheXMsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuQ29udHJvbC5MYXllcnMoYmFzZUxheWVycywgb3ZlcmxheXMsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG59KHdpbmRvdywgZG9jdW1lbnQpKTtcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPWxlYWZsZXQtc3JjLm1hcCIsIi8qIVxuICogbXVzdGFjaGUuanMgLSBMb2dpYy1sZXNzIHt7bXVzdGFjaGV9fSB0ZW1wbGF0ZXMgd2l0aCBKYXZhU2NyaXB0XG4gKiBodHRwOi8vZ2l0aHViLmNvbS9qYW5sL211c3RhY2hlLmpzXG4gKi9cblxuLypnbG9iYWwgZGVmaW5lOiBmYWxzZSBNdXN0YWNoZTogdHJ1ZSovXG5cbihmdW5jdGlvbiBkZWZpbmVNdXN0YWNoZSAoZ2xvYmFsLCBmYWN0b3J5KSB7XG4gIGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcgJiYgZXhwb3J0cyAmJiB0eXBlb2YgZXhwb3J0cy5ub2RlTmFtZSAhPT0gJ3N0cmluZycpIHtcbiAgICBmYWN0b3J5KGV4cG9ydHMpOyAvLyBDb21tb25KU1xuICB9IGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCkge1xuICAgIGRlZmluZShbJ2V4cG9ydHMnXSwgZmFjdG9yeSk7IC8vIEFNRFxuICB9IGVsc2Uge1xuICAgIGdsb2JhbC5NdXN0YWNoZSA9IHt9O1xuICAgIGZhY3RvcnkoZ2xvYmFsLk11c3RhY2hlKTsgLy8gc2NyaXB0LCB3c2gsIGFzcFxuICB9XG59KHRoaXMsIGZ1bmN0aW9uIG11c3RhY2hlRmFjdG9yeSAobXVzdGFjaGUpIHtcblxuICB2YXIgb2JqZWN0VG9TdHJpbmcgPSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nO1xuICB2YXIgaXNBcnJheSA9IEFycmF5LmlzQXJyYXkgfHwgZnVuY3Rpb24gaXNBcnJheVBvbHlmaWxsIChvYmplY3QpIHtcbiAgICByZXR1cm4gb2JqZWN0VG9TdHJpbmcuY2FsbChvYmplY3QpID09PSAnW29iamVjdCBBcnJheV0nO1xuICB9O1xuXG4gIGZ1bmN0aW9uIGlzRnVuY3Rpb24gKG9iamVjdCkge1xuICAgIHJldHVybiB0eXBlb2Ygb2JqZWN0ID09PSAnZnVuY3Rpb24nO1xuICB9XG5cbiAgLyoqXG4gICAqIE1vcmUgY29ycmVjdCB0eXBlb2Ygc3RyaW5nIGhhbmRsaW5nIGFycmF5XG4gICAqIHdoaWNoIG5vcm1hbGx5IHJldHVybnMgdHlwZW9mICdvYmplY3QnXG4gICAqL1xuICBmdW5jdGlvbiB0eXBlU3RyIChvYmopIHtcbiAgICByZXR1cm4gaXNBcnJheShvYmopID8gJ2FycmF5JyA6IHR5cGVvZiBvYmo7XG4gIH1cblxuICBmdW5jdGlvbiBlc2NhcGVSZWdFeHAgKHN0cmluZykge1xuICAgIHJldHVybiBzdHJpbmcucmVwbGFjZSgvW1xcLVxcW1xcXXt9KCkqKz8uLFxcXFxcXF4kfCNcXHNdL2csICdcXFxcJCYnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBOdWxsIHNhZmUgd2F5IG9mIGNoZWNraW5nIHdoZXRoZXIgb3Igbm90IGFuIG9iamVjdCxcbiAgICogaW5jbHVkaW5nIGl0cyBwcm90b3R5cGUsIGhhcyBhIGdpdmVuIHByb3BlcnR5XG4gICAqL1xuICBmdW5jdGlvbiBoYXNQcm9wZXJ0eSAob2JqLCBwcm9wTmFtZSkge1xuICAgIHJldHVybiBvYmogIT0gbnVsbCAmJiB0eXBlb2Ygb2JqID09PSAnb2JqZWN0JyAmJiAocHJvcE5hbWUgaW4gb2JqKTtcbiAgfVxuXG4gIC8vIFdvcmthcm91bmQgZm9yIGh0dHBzOi8vaXNzdWVzLmFwYWNoZS5vcmcvamlyYS9icm93c2UvQ09VQ0hEQi01NzdcbiAgLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9qYW5sL211c3RhY2hlLmpzL2lzc3Vlcy8xODlcbiAgdmFyIHJlZ0V4cFRlc3QgPSBSZWdFeHAucHJvdG90eXBlLnRlc3Q7XG4gIGZ1bmN0aW9uIHRlc3RSZWdFeHAgKHJlLCBzdHJpbmcpIHtcbiAgICByZXR1cm4gcmVnRXhwVGVzdC5jYWxsKHJlLCBzdHJpbmcpO1xuICB9XG5cbiAgdmFyIG5vblNwYWNlUmUgPSAvXFxTLztcbiAgZnVuY3Rpb24gaXNXaGl0ZXNwYWNlIChzdHJpbmcpIHtcbiAgICByZXR1cm4gIXRlc3RSZWdFeHAobm9uU3BhY2VSZSwgc3RyaW5nKTtcbiAgfVxuXG4gIHZhciBlbnRpdHlNYXAgPSB7XG4gICAgJyYnOiAnJmFtcDsnLFxuICAgICc8JzogJyZsdDsnLFxuICAgICc+JzogJyZndDsnLFxuICAgICdcIic6ICcmcXVvdDsnLFxuICAgIFwiJ1wiOiAnJiMzOTsnLFxuICAgICcvJzogJyYjeDJGOycsXG4gICAgJ2AnOiAnJiN4NjA7JyxcbiAgICAnPSc6ICcmI3gzRDsnXG4gIH07XG5cbiAgZnVuY3Rpb24gZXNjYXBlSHRtbCAoc3RyaW5nKSB7XG4gICAgcmV0dXJuIFN0cmluZyhzdHJpbmcpLnJlcGxhY2UoL1smPD5cIidgPVxcL10vZywgZnVuY3Rpb24gZnJvbUVudGl0eU1hcCAocykge1xuICAgICAgcmV0dXJuIGVudGl0eU1hcFtzXTtcbiAgICB9KTtcbiAgfVxuXG4gIHZhciB3aGl0ZVJlID0gL1xccyovO1xuICB2YXIgc3BhY2VSZSA9IC9cXHMrLztcbiAgdmFyIGVxdWFsc1JlID0gL1xccyo9LztcbiAgdmFyIGN1cmx5UmUgPSAvXFxzKlxcfS87XG4gIHZhciB0YWdSZSA9IC8jfFxcXnxcXC98PnxcXHt8Jnw9fCEvO1xuXG4gIC8qKlxuICAgKiBCcmVha3MgdXAgdGhlIGdpdmVuIGB0ZW1wbGF0ZWAgc3RyaW5nIGludG8gYSB0cmVlIG9mIHRva2Vucy4gSWYgdGhlIGB0YWdzYFxuICAgKiBhcmd1bWVudCBpcyBnaXZlbiBoZXJlIGl0IG11c3QgYmUgYW4gYXJyYXkgd2l0aCB0d28gc3RyaW5nIHZhbHVlczogdGhlXG4gICAqIG9wZW5pbmcgYW5kIGNsb3NpbmcgdGFncyB1c2VkIGluIHRoZSB0ZW1wbGF0ZSAoZS5nLiBbIFwiPCVcIiwgXCIlPlwiIF0pLiBPZlxuICAgKiBjb3Vyc2UsIHRoZSBkZWZhdWx0IGlzIHRvIHVzZSBtdXN0YWNoZXMgKGkuZS4gbXVzdGFjaGUudGFncykuXG4gICAqXG4gICAqIEEgdG9rZW4gaXMgYW4gYXJyYXkgd2l0aCBhdCBsZWFzdCA0IGVsZW1lbnRzLiBUaGUgZmlyc3QgZWxlbWVudCBpcyB0aGVcbiAgICogbXVzdGFjaGUgc3ltYm9sIHRoYXQgd2FzIHVzZWQgaW5zaWRlIHRoZSB0YWcsIGUuZy4gXCIjXCIgb3IgXCImXCIuIElmIHRoZSB0YWdcbiAgICogZGlkIG5vdCBjb250YWluIGEgc3ltYm9sIChpLmUuIHt7bXlWYWx1ZX19KSB0aGlzIGVsZW1lbnQgaXMgXCJuYW1lXCIuIEZvclxuICAgKiBhbGwgdGV4dCB0aGF0IGFwcGVhcnMgb3V0c2lkZSBhIHN5bWJvbCB0aGlzIGVsZW1lbnQgaXMgXCJ0ZXh0XCIuXG4gICAqXG4gICAqIFRoZSBzZWNvbmQgZWxlbWVudCBvZiBhIHRva2VuIGlzIGl0cyBcInZhbHVlXCIuIEZvciBtdXN0YWNoZSB0YWdzIHRoaXMgaXNcbiAgICogd2hhdGV2ZXIgZWxzZSB3YXMgaW5zaWRlIHRoZSB0YWcgYmVzaWRlcyB0aGUgb3BlbmluZyBzeW1ib2wuIEZvciB0ZXh0IHRva2Vuc1xuICAgKiB0aGlzIGlzIHRoZSB0ZXh0IGl0c2VsZi5cbiAgICpcbiAgICogVGhlIHRoaXJkIGFuZCBmb3VydGggZWxlbWVudHMgb2YgdGhlIHRva2VuIGFyZSB0aGUgc3RhcnQgYW5kIGVuZCBpbmRpY2VzLFxuICAgKiByZXNwZWN0aXZlbHksIG9mIHRoZSB0b2tlbiBpbiB0aGUgb3JpZ2luYWwgdGVtcGxhdGUuXG4gICAqXG4gICAqIFRva2VucyB0aGF0IGFyZSB0aGUgcm9vdCBub2RlIG9mIGEgc3VidHJlZSBjb250YWluIHR3byBtb3JlIGVsZW1lbnRzOiAxKSBhblxuICAgKiBhcnJheSBvZiB0b2tlbnMgaW4gdGhlIHN1YnRyZWUgYW5kIDIpIHRoZSBpbmRleCBpbiB0aGUgb3JpZ2luYWwgdGVtcGxhdGUgYXRcbiAgICogd2hpY2ggdGhlIGNsb3NpbmcgdGFnIGZvciB0aGF0IHNlY3Rpb24gYmVnaW5zLlxuICAgKi9cbiAgZnVuY3Rpb24gcGFyc2VUZW1wbGF0ZSAodGVtcGxhdGUsIHRhZ3MpIHtcbiAgICBpZiAoIXRlbXBsYXRlKVxuICAgICAgcmV0dXJuIFtdO1xuXG4gICAgdmFyIHNlY3Rpb25zID0gW107ICAgICAvLyBTdGFjayB0byBob2xkIHNlY3Rpb24gdG9rZW5zXG4gICAgdmFyIHRva2VucyA9IFtdOyAgICAgICAvLyBCdWZmZXIgdG8gaG9sZCB0aGUgdG9rZW5zXG4gICAgdmFyIHNwYWNlcyA9IFtdOyAgICAgICAvLyBJbmRpY2VzIG9mIHdoaXRlc3BhY2UgdG9rZW5zIG9uIHRoZSBjdXJyZW50IGxpbmVcbiAgICB2YXIgaGFzVGFnID0gZmFsc2U7ICAgIC8vIElzIHRoZXJlIGEge3t0YWd9fSBvbiB0aGUgY3VycmVudCBsaW5lP1xuICAgIHZhciBub25TcGFjZSA9IGZhbHNlOyAgLy8gSXMgdGhlcmUgYSBub24tc3BhY2UgY2hhciBvbiB0aGUgY3VycmVudCBsaW5lP1xuXG4gICAgLy8gU3RyaXBzIGFsbCB3aGl0ZXNwYWNlIHRva2VucyBhcnJheSBmb3IgdGhlIGN1cnJlbnQgbGluZVxuICAgIC8vIGlmIHRoZXJlIHdhcyBhIHt7I3RhZ319IG9uIGl0IGFuZCBvdGhlcndpc2Ugb25seSBzcGFjZS5cbiAgICBmdW5jdGlvbiBzdHJpcFNwYWNlICgpIHtcbiAgICAgIGlmIChoYXNUYWcgJiYgIW5vblNwYWNlKSB7XG4gICAgICAgIHdoaWxlIChzcGFjZXMubGVuZ3RoKVxuICAgICAgICAgIGRlbGV0ZSB0b2tlbnNbc3BhY2VzLnBvcCgpXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNwYWNlcyA9IFtdO1xuICAgICAgfVxuXG4gICAgICBoYXNUYWcgPSBmYWxzZTtcbiAgICAgIG5vblNwYWNlID0gZmFsc2U7XG4gICAgfVxuXG4gICAgdmFyIG9wZW5pbmdUYWdSZSwgY2xvc2luZ1RhZ1JlLCBjbG9zaW5nQ3VybHlSZTtcbiAgICBmdW5jdGlvbiBjb21waWxlVGFncyAodGFnc1RvQ29tcGlsZSkge1xuICAgICAgaWYgKHR5cGVvZiB0YWdzVG9Db21waWxlID09PSAnc3RyaW5nJylcbiAgICAgICAgdGFnc1RvQ29tcGlsZSA9IHRhZ3NUb0NvbXBpbGUuc3BsaXQoc3BhY2VSZSwgMik7XG5cbiAgICAgIGlmICghaXNBcnJheSh0YWdzVG9Db21waWxlKSB8fCB0YWdzVG9Db21waWxlLmxlbmd0aCAhPT0gMilcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHRhZ3M6ICcgKyB0YWdzVG9Db21waWxlKTtcblxuICAgICAgb3BlbmluZ1RhZ1JlID0gbmV3IFJlZ0V4cChlc2NhcGVSZWdFeHAodGFnc1RvQ29tcGlsZVswXSkgKyAnXFxcXHMqJyk7XG4gICAgICBjbG9zaW5nVGFnUmUgPSBuZXcgUmVnRXhwKCdcXFxccyonICsgZXNjYXBlUmVnRXhwKHRhZ3NUb0NvbXBpbGVbMV0pKTtcbiAgICAgIGNsb3NpbmdDdXJseVJlID0gbmV3IFJlZ0V4cCgnXFxcXHMqJyArIGVzY2FwZVJlZ0V4cCgnfScgKyB0YWdzVG9Db21waWxlWzFdKSk7XG4gICAgfVxuXG4gICAgY29tcGlsZVRhZ3ModGFncyB8fCBtdXN0YWNoZS50YWdzKTtcblxuICAgIHZhciBzY2FubmVyID0gbmV3IFNjYW5uZXIodGVtcGxhdGUpO1xuXG4gICAgdmFyIHN0YXJ0LCB0eXBlLCB2YWx1ZSwgY2hyLCB0b2tlbiwgb3BlblNlY3Rpb247XG4gICAgd2hpbGUgKCFzY2FubmVyLmVvcygpKSB7XG4gICAgICBzdGFydCA9IHNjYW5uZXIucG9zO1xuXG4gICAgICAvLyBNYXRjaCBhbnkgdGV4dCBiZXR3ZWVuIHRhZ3MuXG4gICAgICB2YWx1ZSA9IHNjYW5uZXIuc2NhblVudGlsKG9wZW5pbmdUYWdSZSk7XG5cbiAgICAgIGlmICh2YWx1ZSkge1xuICAgICAgICBmb3IgKHZhciBpID0gMCwgdmFsdWVMZW5ndGggPSB2YWx1ZS5sZW5ndGg7IGkgPCB2YWx1ZUxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgY2hyID0gdmFsdWUuY2hhckF0KGkpO1xuXG4gICAgICAgICAgaWYgKGlzV2hpdGVzcGFjZShjaHIpKSB7XG4gICAgICAgICAgICBzcGFjZXMucHVzaCh0b2tlbnMubGVuZ3RoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbm9uU3BhY2UgPSB0cnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRva2Vucy5wdXNoKFsgJ3RleHQnLCBjaHIsIHN0YXJ0LCBzdGFydCArIDEgXSk7XG4gICAgICAgICAgc3RhcnQgKz0gMTtcblxuICAgICAgICAgIC8vIENoZWNrIGZvciB3aGl0ZXNwYWNlIG9uIHRoZSBjdXJyZW50IGxpbmUuXG4gICAgICAgICAgaWYgKGNociA9PT0gJ1xcbicpXG4gICAgICAgICAgICBzdHJpcFNwYWNlKCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gTWF0Y2ggdGhlIG9wZW5pbmcgdGFnLlxuICAgICAgaWYgKCFzY2FubmVyLnNjYW4ob3BlbmluZ1RhZ1JlKSlcbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGhhc1RhZyA9IHRydWU7XG5cbiAgICAgIC8vIEdldCB0aGUgdGFnIHR5cGUuXG4gICAgICB0eXBlID0gc2Nhbm5lci5zY2FuKHRhZ1JlKSB8fCAnbmFtZSc7XG4gICAgICBzY2FubmVyLnNjYW4od2hpdGVSZSk7XG5cbiAgICAgIC8vIEdldCB0aGUgdGFnIHZhbHVlLlxuICAgICAgaWYgKHR5cGUgPT09ICc9Jykge1xuICAgICAgICB2YWx1ZSA9IHNjYW5uZXIuc2NhblVudGlsKGVxdWFsc1JlKTtcbiAgICAgICAgc2Nhbm5lci5zY2FuKGVxdWFsc1JlKTtcbiAgICAgICAgc2Nhbm5lci5zY2FuVW50aWwoY2xvc2luZ1RhZ1JlKTtcbiAgICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gJ3snKSB7XG4gICAgICAgIHZhbHVlID0gc2Nhbm5lci5zY2FuVW50aWwoY2xvc2luZ0N1cmx5UmUpO1xuICAgICAgICBzY2FubmVyLnNjYW4oY3VybHlSZSk7XG4gICAgICAgIHNjYW5uZXIuc2NhblVudGlsKGNsb3NpbmdUYWdSZSk7XG4gICAgICAgIHR5cGUgPSAnJic7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YWx1ZSA9IHNjYW5uZXIuc2NhblVudGlsKGNsb3NpbmdUYWdSZSk7XG4gICAgICB9XG5cbiAgICAgIC8vIE1hdGNoIHRoZSBjbG9zaW5nIHRhZy5cbiAgICAgIGlmICghc2Nhbm5lci5zY2FuKGNsb3NpbmdUYWdSZSkpXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignVW5jbG9zZWQgdGFnIGF0ICcgKyBzY2FubmVyLnBvcyk7XG5cbiAgICAgIHRva2VuID0gWyB0eXBlLCB2YWx1ZSwgc3RhcnQsIHNjYW5uZXIucG9zIF07XG4gICAgICB0b2tlbnMucHVzaCh0b2tlbik7XG5cbiAgICAgIGlmICh0eXBlID09PSAnIycgfHwgdHlwZSA9PT0gJ14nKSB7XG4gICAgICAgIHNlY3Rpb25zLnB1c2godG9rZW4pO1xuICAgICAgfSBlbHNlIGlmICh0eXBlID09PSAnLycpIHtcbiAgICAgICAgLy8gQ2hlY2sgc2VjdGlvbiBuZXN0aW5nLlxuICAgICAgICBvcGVuU2VjdGlvbiA9IHNlY3Rpb25zLnBvcCgpO1xuXG4gICAgICAgIGlmICghb3BlblNlY3Rpb24pXG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbm9wZW5lZCBzZWN0aW9uIFwiJyArIHZhbHVlICsgJ1wiIGF0ICcgKyBzdGFydCk7XG5cbiAgICAgICAgaWYgKG9wZW5TZWN0aW9uWzFdICE9PSB2YWx1ZSlcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VuY2xvc2VkIHNlY3Rpb24gXCInICsgb3BlblNlY3Rpb25bMV0gKyAnXCIgYXQgJyArIHN0YXJ0KTtcbiAgICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gJ25hbWUnIHx8IHR5cGUgPT09ICd7JyB8fCB0eXBlID09PSAnJicpIHtcbiAgICAgICAgbm9uU3BhY2UgPSB0cnVlO1xuICAgICAgfSBlbHNlIGlmICh0eXBlID09PSAnPScpIHtcbiAgICAgICAgLy8gU2V0IHRoZSB0YWdzIGZvciB0aGUgbmV4dCB0aW1lIGFyb3VuZC5cbiAgICAgICAgY29tcGlsZVRhZ3ModmFsdWUpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIE1ha2Ugc3VyZSB0aGVyZSBhcmUgbm8gb3BlbiBzZWN0aW9ucyB3aGVuIHdlJ3JlIGRvbmUuXG4gICAgb3BlblNlY3Rpb24gPSBzZWN0aW9ucy5wb3AoKTtcblxuICAgIGlmIChvcGVuU2VjdGlvbilcbiAgICAgIHRocm93IG5ldyBFcnJvcignVW5jbG9zZWQgc2VjdGlvbiBcIicgKyBvcGVuU2VjdGlvblsxXSArICdcIiBhdCAnICsgc2Nhbm5lci5wb3MpO1xuXG4gICAgcmV0dXJuIG5lc3RUb2tlbnMoc3F1YXNoVG9rZW5zKHRva2VucykpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbWJpbmVzIHRoZSB2YWx1ZXMgb2YgY29uc2VjdXRpdmUgdGV4dCB0b2tlbnMgaW4gdGhlIGdpdmVuIGB0b2tlbnNgIGFycmF5XG4gICAqIHRvIGEgc2luZ2xlIHRva2VuLlxuICAgKi9cbiAgZnVuY3Rpb24gc3F1YXNoVG9rZW5zICh0b2tlbnMpIHtcbiAgICB2YXIgc3F1YXNoZWRUb2tlbnMgPSBbXTtcblxuICAgIHZhciB0b2tlbiwgbGFzdFRva2VuO1xuICAgIGZvciAodmFyIGkgPSAwLCBudW1Ub2tlbnMgPSB0b2tlbnMubGVuZ3RoOyBpIDwgbnVtVG9rZW5zOyArK2kpIHtcbiAgICAgIHRva2VuID0gdG9rZW5zW2ldO1xuXG4gICAgICBpZiAodG9rZW4pIHtcbiAgICAgICAgaWYgKHRva2VuWzBdID09PSAndGV4dCcgJiYgbGFzdFRva2VuICYmIGxhc3RUb2tlblswXSA9PT0gJ3RleHQnKSB7XG4gICAgICAgICAgbGFzdFRva2VuWzFdICs9IHRva2VuWzFdO1xuICAgICAgICAgIGxhc3RUb2tlblszXSA9IHRva2VuWzNdO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHNxdWFzaGVkVG9rZW5zLnB1c2godG9rZW4pO1xuICAgICAgICAgIGxhc3RUb2tlbiA9IHRva2VuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHNxdWFzaGVkVG9rZW5zO1xuICB9XG5cbiAgLyoqXG4gICAqIEZvcm1zIHRoZSBnaXZlbiBhcnJheSBvZiBgdG9rZW5zYCBpbnRvIGEgbmVzdGVkIHRyZWUgc3RydWN0dXJlIHdoZXJlXG4gICAqIHRva2VucyB0aGF0IHJlcHJlc2VudCBhIHNlY3Rpb24gaGF2ZSB0d28gYWRkaXRpb25hbCBpdGVtczogMSkgYW4gYXJyYXkgb2ZcbiAgICogYWxsIHRva2VucyB0aGF0IGFwcGVhciBpbiB0aGF0IHNlY3Rpb24gYW5kIDIpIHRoZSBpbmRleCBpbiB0aGUgb3JpZ2luYWxcbiAgICogdGVtcGxhdGUgdGhhdCByZXByZXNlbnRzIHRoZSBlbmQgb2YgdGhhdCBzZWN0aW9uLlxuICAgKi9cbiAgZnVuY3Rpb24gbmVzdFRva2VucyAodG9rZW5zKSB7XG4gICAgdmFyIG5lc3RlZFRva2VucyA9IFtdO1xuICAgIHZhciBjb2xsZWN0b3IgPSBuZXN0ZWRUb2tlbnM7XG4gICAgdmFyIHNlY3Rpb25zID0gW107XG5cbiAgICB2YXIgdG9rZW4sIHNlY3Rpb247XG4gICAgZm9yICh2YXIgaSA9IDAsIG51bVRva2VucyA9IHRva2Vucy5sZW5ndGg7IGkgPCBudW1Ub2tlbnM7ICsraSkge1xuICAgICAgdG9rZW4gPSB0b2tlbnNbaV07XG5cbiAgICAgIHN3aXRjaCAodG9rZW5bMF0pIHtcbiAgICAgICAgY2FzZSAnIyc6XG4gICAgICAgIGNhc2UgJ14nOlxuICAgICAgICAgIGNvbGxlY3Rvci5wdXNoKHRva2VuKTtcbiAgICAgICAgICBzZWN0aW9ucy5wdXNoKHRva2VuKTtcbiAgICAgICAgICBjb2xsZWN0b3IgPSB0b2tlbls0XSA9IFtdO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICcvJzpcbiAgICAgICAgICBzZWN0aW9uID0gc2VjdGlvbnMucG9wKCk7XG4gICAgICAgICAgc2VjdGlvbls1XSA9IHRva2VuWzJdO1xuICAgICAgICAgIGNvbGxlY3RvciA9IHNlY3Rpb25zLmxlbmd0aCA+IDAgPyBzZWN0aW9uc1tzZWN0aW9ucy5sZW5ndGggLSAxXVs0XSA6IG5lc3RlZFRva2VucztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICBjb2xsZWN0b3IucHVzaCh0b2tlbik7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG5lc3RlZFRva2VucztcbiAgfVxuXG4gIC8qKlxuICAgKiBBIHNpbXBsZSBzdHJpbmcgc2Nhbm5lciB0aGF0IGlzIHVzZWQgYnkgdGhlIHRlbXBsYXRlIHBhcnNlciB0byBmaW5kXG4gICAqIHRva2VucyBpbiB0ZW1wbGF0ZSBzdHJpbmdzLlxuICAgKi9cbiAgZnVuY3Rpb24gU2Nhbm5lciAoc3RyaW5nKSB7XG4gICAgdGhpcy5zdHJpbmcgPSBzdHJpbmc7XG4gICAgdGhpcy50YWlsID0gc3RyaW5nO1xuICAgIHRoaXMucG9zID0gMDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgdGFpbCBpcyBlbXB0eSAoZW5kIG9mIHN0cmluZykuXG4gICAqL1xuICBTY2FubmVyLnByb3RvdHlwZS5lb3MgPSBmdW5jdGlvbiBlb3MgKCkge1xuICAgIHJldHVybiB0aGlzLnRhaWwgPT09ICcnO1xuICB9O1xuXG4gIC8qKlxuICAgKiBUcmllcyB0byBtYXRjaCB0aGUgZ2l2ZW4gcmVndWxhciBleHByZXNzaW9uIGF0IHRoZSBjdXJyZW50IHBvc2l0aW9uLlxuICAgKiBSZXR1cm5zIHRoZSBtYXRjaGVkIHRleHQgaWYgaXQgY2FuIG1hdGNoLCB0aGUgZW1wdHkgc3RyaW5nIG90aGVyd2lzZS5cbiAgICovXG4gIFNjYW5uZXIucHJvdG90eXBlLnNjYW4gPSBmdW5jdGlvbiBzY2FuIChyZSkge1xuICAgIHZhciBtYXRjaCA9IHRoaXMudGFpbC5tYXRjaChyZSk7XG5cbiAgICBpZiAoIW1hdGNoIHx8IG1hdGNoLmluZGV4ICE9PSAwKVxuICAgICAgcmV0dXJuICcnO1xuXG4gICAgdmFyIHN0cmluZyA9IG1hdGNoWzBdO1xuXG4gICAgdGhpcy50YWlsID0gdGhpcy50YWlsLnN1YnN0cmluZyhzdHJpbmcubGVuZ3RoKTtcbiAgICB0aGlzLnBvcyArPSBzdHJpbmcubGVuZ3RoO1xuXG4gICAgcmV0dXJuIHN0cmluZztcbiAgfTtcblxuICAvKipcbiAgICogU2tpcHMgYWxsIHRleHQgdW50aWwgdGhlIGdpdmVuIHJlZ3VsYXIgZXhwcmVzc2lvbiBjYW4gYmUgbWF0Y2hlZC4gUmV0dXJuc1xuICAgKiB0aGUgc2tpcHBlZCBzdHJpbmcsIHdoaWNoIGlzIHRoZSBlbnRpcmUgdGFpbCBpZiBubyBtYXRjaCBjYW4gYmUgbWFkZS5cbiAgICovXG4gIFNjYW5uZXIucHJvdG90eXBlLnNjYW5VbnRpbCA9IGZ1bmN0aW9uIHNjYW5VbnRpbCAocmUpIHtcbiAgICB2YXIgaW5kZXggPSB0aGlzLnRhaWwuc2VhcmNoKHJlKSwgbWF0Y2g7XG5cbiAgICBzd2l0Y2ggKGluZGV4KSB7XG4gICAgICBjYXNlIC0xOlxuICAgICAgICBtYXRjaCA9IHRoaXMudGFpbDtcbiAgICAgICAgdGhpcy50YWlsID0gJyc7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAwOlxuICAgICAgICBtYXRjaCA9ICcnO1xuICAgICAgICBicmVhaztcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIG1hdGNoID0gdGhpcy50YWlsLnN1YnN0cmluZygwLCBpbmRleCk7XG4gICAgICAgIHRoaXMudGFpbCA9IHRoaXMudGFpbC5zdWJzdHJpbmcoaW5kZXgpO1xuICAgIH1cblxuICAgIHRoaXMucG9zICs9IG1hdGNoLmxlbmd0aDtcblxuICAgIHJldHVybiBtYXRjaDtcbiAgfTtcblxuICAvKipcbiAgICogUmVwcmVzZW50cyBhIHJlbmRlcmluZyBjb250ZXh0IGJ5IHdyYXBwaW5nIGEgdmlldyBvYmplY3QgYW5kXG4gICAqIG1haW50YWluaW5nIGEgcmVmZXJlbmNlIHRvIHRoZSBwYXJlbnQgY29udGV4dC5cbiAgICovXG4gIGZ1bmN0aW9uIENvbnRleHQgKHZpZXcsIHBhcmVudENvbnRleHQpIHtcbiAgICB0aGlzLnZpZXcgPSB2aWV3O1xuICAgIHRoaXMuY2FjaGUgPSB7ICcuJzogdGhpcy52aWV3IH07XG4gICAgdGhpcy5wYXJlbnQgPSBwYXJlbnRDb250ZXh0O1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBuZXcgY29udGV4dCB1c2luZyB0aGUgZ2l2ZW4gdmlldyB3aXRoIHRoaXMgY29udGV4dFxuICAgKiBhcyB0aGUgcGFyZW50LlxuICAgKi9cbiAgQ29udGV4dC5wcm90b3R5cGUucHVzaCA9IGZ1bmN0aW9uIHB1c2ggKHZpZXcpIHtcbiAgICByZXR1cm4gbmV3IENvbnRleHQodmlldywgdGhpcyk7XG4gIH07XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIHZhbHVlIG9mIHRoZSBnaXZlbiBuYW1lIGluIHRoaXMgY29udGV4dCwgdHJhdmVyc2luZ1xuICAgKiB1cCB0aGUgY29udGV4dCBoaWVyYXJjaHkgaWYgdGhlIHZhbHVlIGlzIGFic2VudCBpbiB0aGlzIGNvbnRleHQncyB2aWV3LlxuICAgKi9cbiAgQ29udGV4dC5wcm90b3R5cGUubG9va3VwID0gZnVuY3Rpb24gbG9va3VwIChuYW1lKSB7XG4gICAgdmFyIGNhY2hlID0gdGhpcy5jYWNoZTtcblxuICAgIHZhciB2YWx1ZTtcbiAgICBpZiAoY2FjaGUuaGFzT3duUHJvcGVydHkobmFtZSkpIHtcbiAgICAgIHZhbHVlID0gY2FjaGVbbmFtZV07XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciBjb250ZXh0ID0gdGhpcywgbmFtZXMsIGluZGV4LCBsb29rdXBIaXQgPSBmYWxzZTtcblxuICAgICAgd2hpbGUgKGNvbnRleHQpIHtcbiAgICAgICAgaWYgKG5hbWUuaW5kZXhPZignLicpID4gMCkge1xuICAgICAgICAgIHZhbHVlID0gY29udGV4dC52aWV3O1xuICAgICAgICAgIG5hbWVzID0gbmFtZS5zcGxpdCgnLicpO1xuICAgICAgICAgIGluZGV4ID0gMDtcblxuICAgICAgICAgIC8qKlxuICAgICAgICAgICAqIFVzaW5nIHRoZSBkb3Qgbm90aW9uIHBhdGggaW4gYG5hbWVgLCB3ZSBkZXNjZW5kIHRocm91Z2ggdGhlXG4gICAgICAgICAgICogbmVzdGVkIG9iamVjdHMuXG4gICAgICAgICAgICpcbiAgICAgICAgICAgKiBUbyBiZSBjZXJ0YWluIHRoYXQgdGhlIGxvb2t1cCBoYXMgYmVlbiBzdWNjZXNzZnVsLCB3ZSBoYXZlIHRvXG4gICAgICAgICAgICogY2hlY2sgaWYgdGhlIGxhc3Qgb2JqZWN0IGluIHRoZSBwYXRoIGFjdHVhbGx5IGhhcyB0aGUgcHJvcGVydHlcbiAgICAgICAgICAgKiB3ZSBhcmUgbG9va2luZyBmb3IuIFdlIHN0b3JlIHRoZSByZXN1bHQgaW4gYGxvb2t1cEhpdGAuXG4gICAgICAgICAgICpcbiAgICAgICAgICAgKiBUaGlzIGlzIHNwZWNpYWxseSBuZWNlc3NhcnkgZm9yIHdoZW4gdGhlIHZhbHVlIGhhcyBiZWVuIHNldCB0b1xuICAgICAgICAgICAqIGB1bmRlZmluZWRgIGFuZCB3ZSB3YW50IHRvIGF2b2lkIGxvb2tpbmcgdXAgcGFyZW50IGNvbnRleHRzLlxuICAgICAgICAgICAqKi9cbiAgICAgICAgICB3aGlsZSAodmFsdWUgIT0gbnVsbCAmJiBpbmRleCA8IG5hbWVzLmxlbmd0aCkge1xuICAgICAgICAgICAgaWYgKGluZGV4ID09PSBuYW1lcy5sZW5ndGggLSAxKVxuICAgICAgICAgICAgICBsb29rdXBIaXQgPSBoYXNQcm9wZXJ0eSh2YWx1ZSwgbmFtZXNbaW5kZXhdKTtcblxuICAgICAgICAgICAgdmFsdWUgPSB2YWx1ZVtuYW1lc1tpbmRleCsrXV07XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHZhbHVlID0gY29udGV4dC52aWV3W25hbWVdO1xuICAgICAgICAgIGxvb2t1cEhpdCA9IGhhc1Byb3BlcnR5KGNvbnRleHQudmlldywgbmFtZSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAobG9va3VwSGl0KVxuICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgIGNvbnRleHQgPSBjb250ZXh0LnBhcmVudDtcbiAgICAgIH1cblxuICAgICAgY2FjaGVbbmFtZV0gPSB2YWx1ZTtcbiAgICB9XG5cbiAgICBpZiAoaXNGdW5jdGlvbih2YWx1ZSkpXG4gICAgICB2YWx1ZSA9IHZhbHVlLmNhbGwodGhpcy52aWV3KTtcblxuICAgIHJldHVybiB2YWx1ZTtcbiAgfTtcblxuICAvKipcbiAgICogQSBXcml0ZXIga25vd3MgaG93IHRvIHRha2UgYSBzdHJlYW0gb2YgdG9rZW5zIGFuZCByZW5kZXIgdGhlbSB0byBhXG4gICAqIHN0cmluZywgZ2l2ZW4gYSBjb250ZXh0LiBJdCBhbHNvIG1haW50YWlucyBhIGNhY2hlIG9mIHRlbXBsYXRlcyB0b1xuICAgKiBhdm9pZCB0aGUgbmVlZCB0byBwYXJzZSB0aGUgc2FtZSB0ZW1wbGF0ZSB0d2ljZS5cbiAgICovXG4gIGZ1bmN0aW9uIFdyaXRlciAoKSB7XG4gICAgdGhpcy5jYWNoZSA9IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFycyBhbGwgY2FjaGVkIHRlbXBsYXRlcyBpbiB0aGlzIHdyaXRlci5cbiAgICovXG4gIFdyaXRlci5wcm90b3R5cGUuY2xlYXJDYWNoZSA9IGZ1bmN0aW9uIGNsZWFyQ2FjaGUgKCkge1xuICAgIHRoaXMuY2FjaGUgPSB7fTtcbiAgfTtcblxuICAvKipcbiAgICogUGFyc2VzIGFuZCBjYWNoZXMgdGhlIGdpdmVuIGB0ZW1wbGF0ZWAgYW5kIHJldHVybnMgdGhlIGFycmF5IG9mIHRva2Vuc1xuICAgKiB0aGF0IGlzIGdlbmVyYXRlZCBmcm9tIHRoZSBwYXJzZS5cbiAgICovXG4gIFdyaXRlci5wcm90b3R5cGUucGFyc2UgPSBmdW5jdGlvbiBwYXJzZSAodGVtcGxhdGUsIHRhZ3MpIHtcbiAgICB2YXIgY2FjaGUgPSB0aGlzLmNhY2hlO1xuICAgIHZhciB0b2tlbnMgPSBjYWNoZVt0ZW1wbGF0ZV07XG5cbiAgICBpZiAodG9rZW5zID09IG51bGwpXG4gICAgICB0b2tlbnMgPSBjYWNoZVt0ZW1wbGF0ZV0gPSBwYXJzZVRlbXBsYXRlKHRlbXBsYXRlLCB0YWdzKTtcblxuICAgIHJldHVybiB0b2tlbnM7XG4gIH07XG5cbiAgLyoqXG4gICAqIEhpZ2gtbGV2ZWwgbWV0aG9kIHRoYXQgaXMgdXNlZCB0byByZW5kZXIgdGhlIGdpdmVuIGB0ZW1wbGF0ZWAgd2l0aFxuICAgKiB0aGUgZ2l2ZW4gYHZpZXdgLlxuICAgKlxuICAgKiBUaGUgb3B0aW9uYWwgYHBhcnRpYWxzYCBhcmd1bWVudCBtYXkgYmUgYW4gb2JqZWN0IHRoYXQgY29udGFpbnMgdGhlXG4gICAqIG5hbWVzIGFuZCB0ZW1wbGF0ZXMgb2YgcGFydGlhbHMgdGhhdCBhcmUgdXNlZCBpbiB0aGUgdGVtcGxhdGUuIEl0IG1heVxuICAgKiBhbHNvIGJlIGEgZnVuY3Rpb24gdGhhdCBpcyB1c2VkIHRvIGxvYWQgcGFydGlhbCB0ZW1wbGF0ZXMgb24gdGhlIGZseVxuICAgKiB0aGF0IHRha2VzIGEgc2luZ2xlIGFyZ3VtZW50OiB0aGUgbmFtZSBvZiB0aGUgcGFydGlhbC5cbiAgICovXG4gIFdyaXRlci5wcm90b3R5cGUucmVuZGVyID0gZnVuY3Rpb24gcmVuZGVyICh0ZW1wbGF0ZSwgdmlldywgcGFydGlhbHMpIHtcbiAgICB2YXIgdG9rZW5zID0gdGhpcy5wYXJzZSh0ZW1wbGF0ZSk7XG4gICAgdmFyIGNvbnRleHQgPSAodmlldyBpbnN0YW5jZW9mIENvbnRleHQpID8gdmlldyA6IG5ldyBDb250ZXh0KHZpZXcpO1xuICAgIHJldHVybiB0aGlzLnJlbmRlclRva2Vucyh0b2tlbnMsIGNvbnRleHQsIHBhcnRpYWxzLCB0ZW1wbGF0ZSk7XG4gIH07XG5cbiAgLyoqXG4gICAqIExvdy1sZXZlbCBtZXRob2QgdGhhdCByZW5kZXJzIHRoZSBnaXZlbiBhcnJheSBvZiBgdG9rZW5zYCB1c2luZ1xuICAgKiB0aGUgZ2l2ZW4gYGNvbnRleHRgIGFuZCBgcGFydGlhbHNgLlxuICAgKlxuICAgKiBOb3RlOiBUaGUgYG9yaWdpbmFsVGVtcGxhdGVgIGlzIG9ubHkgZXZlciB1c2VkIHRvIGV4dHJhY3QgdGhlIHBvcnRpb25cbiAgICogb2YgdGhlIG9yaWdpbmFsIHRlbXBsYXRlIHRoYXQgd2FzIGNvbnRhaW5lZCBpbiBhIGhpZ2hlci1vcmRlciBzZWN0aW9uLlxuICAgKiBJZiB0aGUgdGVtcGxhdGUgZG9lc24ndCB1c2UgaGlnaGVyLW9yZGVyIHNlY3Rpb25zLCB0aGlzIGFyZ3VtZW50IG1heVxuICAgKiBiZSBvbWl0dGVkLlxuICAgKi9cbiAgV3JpdGVyLnByb3RvdHlwZS5yZW5kZXJUb2tlbnMgPSBmdW5jdGlvbiByZW5kZXJUb2tlbnMgKHRva2VucywgY29udGV4dCwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpIHtcbiAgICB2YXIgYnVmZmVyID0gJyc7XG5cbiAgICB2YXIgdG9rZW4sIHN5bWJvbCwgdmFsdWU7XG4gICAgZm9yICh2YXIgaSA9IDAsIG51bVRva2VucyA9IHRva2Vucy5sZW5ndGg7IGkgPCBudW1Ub2tlbnM7ICsraSkge1xuICAgICAgdmFsdWUgPSB1bmRlZmluZWQ7XG4gICAgICB0b2tlbiA9IHRva2Vuc1tpXTtcbiAgICAgIHN5bWJvbCA9IHRva2VuWzBdO1xuXG4gICAgICBpZiAoc3ltYm9sID09PSAnIycpIHZhbHVlID0gdGhpcy5yZW5kZXJTZWN0aW9uKHRva2VuLCBjb250ZXh0LCBwYXJ0aWFscywgb3JpZ2luYWxUZW1wbGF0ZSk7XG4gICAgICBlbHNlIGlmIChzeW1ib2wgPT09ICdeJykgdmFsdWUgPSB0aGlzLnJlbmRlckludmVydGVkKHRva2VuLCBjb250ZXh0LCBwYXJ0aWFscywgb3JpZ2luYWxUZW1wbGF0ZSk7XG4gICAgICBlbHNlIGlmIChzeW1ib2wgPT09ICc+JykgdmFsdWUgPSB0aGlzLnJlbmRlclBhcnRpYWwodG9rZW4sIGNvbnRleHQsIHBhcnRpYWxzLCBvcmlnaW5hbFRlbXBsYXRlKTtcbiAgICAgIGVsc2UgaWYgKHN5bWJvbCA9PT0gJyYnKSB2YWx1ZSA9IHRoaXMudW5lc2NhcGVkVmFsdWUodG9rZW4sIGNvbnRleHQpO1xuICAgICAgZWxzZSBpZiAoc3ltYm9sID09PSAnbmFtZScpIHZhbHVlID0gdGhpcy5lc2NhcGVkVmFsdWUodG9rZW4sIGNvbnRleHQpO1xuICAgICAgZWxzZSBpZiAoc3ltYm9sID09PSAndGV4dCcpIHZhbHVlID0gdGhpcy5yYXdWYWx1ZSh0b2tlbik7XG5cbiAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkKVxuICAgICAgICBidWZmZXIgKz0gdmFsdWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIGJ1ZmZlcjtcbiAgfTtcblxuICBXcml0ZXIucHJvdG90eXBlLnJlbmRlclNlY3Rpb24gPSBmdW5jdGlvbiByZW5kZXJTZWN0aW9uICh0b2tlbiwgY29udGV4dCwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIGJ1ZmZlciA9ICcnO1xuICAgIHZhciB2YWx1ZSA9IGNvbnRleHQubG9va3VwKHRva2VuWzFdKTtcblxuICAgIC8vIFRoaXMgZnVuY3Rpb24gaXMgdXNlZCB0byByZW5kZXIgYW4gYXJiaXRyYXJ5IHRlbXBsYXRlXG4gICAgLy8gaW4gdGhlIGN1cnJlbnQgY29udGV4dCBieSBoaWdoZXItb3JkZXIgc2VjdGlvbnMuXG4gICAgZnVuY3Rpb24gc3ViUmVuZGVyICh0ZW1wbGF0ZSkge1xuICAgICAgcmV0dXJuIHNlbGYucmVuZGVyKHRlbXBsYXRlLCBjb250ZXh0LCBwYXJ0aWFscyk7XG4gICAgfVxuXG4gICAgaWYgKCF2YWx1ZSkgcmV0dXJuO1xuXG4gICAgaWYgKGlzQXJyYXkodmFsdWUpKSB7XG4gICAgICBmb3IgKHZhciBqID0gMCwgdmFsdWVMZW5ndGggPSB2YWx1ZS5sZW5ndGg7IGogPCB2YWx1ZUxlbmd0aDsgKytqKSB7XG4gICAgICAgIGJ1ZmZlciArPSB0aGlzLnJlbmRlclRva2Vucyh0b2tlbls0XSwgY29udGV4dC5wdXNoKHZhbHVlW2pdKSwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyB8fCB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnIHx8IHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIGJ1ZmZlciArPSB0aGlzLnJlbmRlclRva2Vucyh0b2tlbls0XSwgY29udGV4dC5wdXNoKHZhbHVlKSwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpO1xuICAgIH0gZWxzZSBpZiAoaXNGdW5jdGlvbih2YWx1ZSkpIHtcbiAgICAgIGlmICh0eXBlb2Ygb3JpZ2luYWxUZW1wbGF0ZSAhPT0gJ3N0cmluZycpXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IHVzZSBoaWdoZXItb3JkZXIgc2VjdGlvbnMgd2l0aG91dCB0aGUgb3JpZ2luYWwgdGVtcGxhdGUnKTtcblxuICAgICAgLy8gRXh0cmFjdCB0aGUgcG9ydGlvbiBvZiB0aGUgb3JpZ2luYWwgdGVtcGxhdGUgdGhhdCB0aGUgc2VjdGlvbiBjb250YWlucy5cbiAgICAgIHZhbHVlID0gdmFsdWUuY2FsbChjb250ZXh0LnZpZXcsIG9yaWdpbmFsVGVtcGxhdGUuc2xpY2UodG9rZW5bM10sIHRva2VuWzVdKSwgc3ViUmVuZGVyKTtcblxuICAgICAgaWYgKHZhbHVlICE9IG51bGwpXG4gICAgICAgIGJ1ZmZlciArPSB2YWx1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgYnVmZmVyICs9IHRoaXMucmVuZGVyVG9rZW5zKHRva2VuWzRdLCBjb250ZXh0LCBwYXJ0aWFscywgb3JpZ2luYWxUZW1wbGF0ZSk7XG4gICAgfVxuICAgIHJldHVybiBidWZmZXI7XG4gIH07XG5cbiAgV3JpdGVyLnByb3RvdHlwZS5yZW5kZXJJbnZlcnRlZCA9IGZ1bmN0aW9uIHJlbmRlckludmVydGVkICh0b2tlbiwgY29udGV4dCwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpIHtcbiAgICB2YXIgdmFsdWUgPSBjb250ZXh0Lmxvb2t1cCh0b2tlblsxXSk7XG5cbiAgICAvLyBVc2UgSmF2YVNjcmlwdCdzIGRlZmluaXRpb24gb2YgZmFsc3kuIEluY2x1ZGUgZW1wdHkgYXJyYXlzLlxuICAgIC8vIFNlZSBodHRwczovL2dpdGh1Yi5jb20vamFubC9tdXN0YWNoZS5qcy9pc3N1ZXMvMTg2XG4gICAgaWYgKCF2YWx1ZSB8fCAoaXNBcnJheSh2YWx1ZSkgJiYgdmFsdWUubGVuZ3RoID09PSAwKSlcbiAgICAgIHJldHVybiB0aGlzLnJlbmRlclRva2Vucyh0b2tlbls0XSwgY29udGV4dCwgcGFydGlhbHMsIG9yaWdpbmFsVGVtcGxhdGUpO1xuICB9O1xuXG4gIFdyaXRlci5wcm90b3R5cGUucmVuZGVyUGFydGlhbCA9IGZ1bmN0aW9uIHJlbmRlclBhcnRpYWwgKHRva2VuLCBjb250ZXh0LCBwYXJ0aWFscykge1xuICAgIGlmICghcGFydGlhbHMpIHJldHVybjtcblxuICAgIHZhciB2YWx1ZSA9IGlzRnVuY3Rpb24ocGFydGlhbHMpID8gcGFydGlhbHModG9rZW5bMV0pIDogcGFydGlhbHNbdG9rZW5bMV1dO1xuICAgIGlmICh2YWx1ZSAhPSBudWxsKVxuICAgICAgcmV0dXJuIHRoaXMucmVuZGVyVG9rZW5zKHRoaXMucGFyc2UodmFsdWUpLCBjb250ZXh0LCBwYXJ0aWFscywgdmFsdWUpO1xuICB9O1xuXG4gIFdyaXRlci5wcm90b3R5cGUudW5lc2NhcGVkVmFsdWUgPSBmdW5jdGlvbiB1bmVzY2FwZWRWYWx1ZSAodG9rZW4sIGNvbnRleHQpIHtcbiAgICB2YXIgdmFsdWUgPSBjb250ZXh0Lmxvb2t1cCh0b2tlblsxXSk7XG4gICAgaWYgKHZhbHVlICE9IG51bGwpXG4gICAgICByZXR1cm4gdmFsdWU7XG4gIH07XG5cbiAgV3JpdGVyLnByb3RvdHlwZS5lc2NhcGVkVmFsdWUgPSBmdW5jdGlvbiBlc2NhcGVkVmFsdWUgKHRva2VuLCBjb250ZXh0KSB7XG4gICAgdmFyIHZhbHVlID0gY29udGV4dC5sb29rdXAodG9rZW5bMV0pO1xuICAgIGlmICh2YWx1ZSAhPSBudWxsKVxuICAgICAgcmV0dXJuIG11c3RhY2hlLmVzY2FwZSh2YWx1ZSk7XG4gIH07XG5cbiAgV3JpdGVyLnByb3RvdHlwZS5yYXdWYWx1ZSA9IGZ1bmN0aW9uIHJhd1ZhbHVlICh0b2tlbikge1xuICAgIHJldHVybiB0b2tlblsxXTtcbiAgfTtcblxuICBtdXN0YWNoZS5uYW1lID0gJ211c3RhY2hlLmpzJztcbiAgbXVzdGFjaGUudmVyc2lvbiA9ICcyLjIuMSc7XG4gIG11c3RhY2hlLnRhZ3MgPSBbICd7eycsICd9fScgXTtcblxuICAvLyBBbGwgaGlnaC1sZXZlbCBtdXN0YWNoZS4qIGZ1bmN0aW9ucyB1c2UgdGhpcyB3cml0ZXIuXG4gIHZhciBkZWZhdWx0V3JpdGVyID0gbmV3IFdyaXRlcigpO1xuXG4gIC8qKlxuICAgKiBDbGVhcnMgYWxsIGNhY2hlZCB0ZW1wbGF0ZXMgaW4gdGhlIGRlZmF1bHQgd3JpdGVyLlxuICAgKi9cbiAgbXVzdGFjaGUuY2xlYXJDYWNoZSA9IGZ1bmN0aW9uIGNsZWFyQ2FjaGUgKCkge1xuICAgIHJldHVybiBkZWZhdWx0V3JpdGVyLmNsZWFyQ2FjaGUoKTtcbiAgfTtcblxuICAvKipcbiAgICogUGFyc2VzIGFuZCBjYWNoZXMgdGhlIGdpdmVuIHRlbXBsYXRlIGluIHRoZSBkZWZhdWx0IHdyaXRlciBhbmQgcmV0dXJucyB0aGVcbiAgICogYXJyYXkgb2YgdG9rZW5zIGl0IGNvbnRhaW5zLiBEb2luZyB0aGlzIGFoZWFkIG9mIHRpbWUgYXZvaWRzIHRoZSBuZWVkIHRvXG4gICAqIHBhcnNlIHRlbXBsYXRlcyBvbiB0aGUgZmx5IGFzIHRoZXkgYXJlIHJlbmRlcmVkLlxuICAgKi9cbiAgbXVzdGFjaGUucGFyc2UgPSBmdW5jdGlvbiBwYXJzZSAodGVtcGxhdGUsIHRhZ3MpIHtcbiAgICByZXR1cm4gZGVmYXVsdFdyaXRlci5wYXJzZSh0ZW1wbGF0ZSwgdGFncyk7XG4gIH07XG5cbiAgLyoqXG4gICAqIFJlbmRlcnMgdGhlIGB0ZW1wbGF0ZWAgd2l0aCB0aGUgZ2l2ZW4gYHZpZXdgIGFuZCBgcGFydGlhbHNgIHVzaW5nIHRoZVxuICAgKiBkZWZhdWx0IHdyaXRlci5cbiAgICovXG4gIG11c3RhY2hlLnJlbmRlciA9IGZ1bmN0aW9uIHJlbmRlciAodGVtcGxhdGUsIHZpZXcsIHBhcnRpYWxzKSB7XG4gICAgaWYgKHR5cGVvZiB0ZW1wbGF0ZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0ludmFsaWQgdGVtcGxhdGUhIFRlbXBsYXRlIHNob3VsZCBiZSBhIFwic3RyaW5nXCIgJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICdidXQgXCInICsgdHlwZVN0cih0ZW1wbGF0ZSkgKyAnXCIgd2FzIGdpdmVuIGFzIHRoZSBmaXJzdCAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FyZ3VtZW50IGZvciBtdXN0YWNoZSNyZW5kZXIodGVtcGxhdGUsIHZpZXcsIHBhcnRpYWxzKScpO1xuICAgIH1cblxuICAgIHJldHVybiBkZWZhdWx0V3JpdGVyLnJlbmRlcih0ZW1wbGF0ZSwgdmlldywgcGFydGlhbHMpO1xuICB9O1xuXG4gIC8vIFRoaXMgaXMgaGVyZSBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkgd2l0aCAwLjQueC4sXG4gIC8qZXNsaW50LWRpc2FibGUgKi8gLy8gZXNsaW50IHdhbnRzIGNhbWVsIGNhc2VkIGZ1bmN0aW9uIG5hbWVcbiAgbXVzdGFjaGUudG9faHRtbCA9IGZ1bmN0aW9uIHRvX2h0bWwgKHRlbXBsYXRlLCB2aWV3LCBwYXJ0aWFscywgc2VuZCkge1xuICAgIC8qZXNsaW50LWVuYWJsZSovXG5cbiAgICB2YXIgcmVzdWx0ID0gbXVzdGFjaGUucmVuZGVyKHRlbXBsYXRlLCB2aWV3LCBwYXJ0aWFscyk7XG5cbiAgICBpZiAoaXNGdW5jdGlvbihzZW5kKSkge1xuICAgICAgc2VuZChyZXN1bHQpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgfTtcblxuICAvLyBFeHBvcnQgdGhlIGVzY2FwaW5nIGZ1bmN0aW9uIHNvIHRoYXQgdGhlIHVzZXIgbWF5IG92ZXJyaWRlIGl0LlxuICAvLyBTZWUgaHR0cHM6Ly9naXRodWIuY29tL2phbmwvbXVzdGFjaGUuanMvaXNzdWVzLzI0NFxuICBtdXN0YWNoZS5lc2NhcGUgPSBlc2NhcGVIdG1sO1xuXG4gIC8vIEV4cG9ydCB0aGVzZSBtYWlubHkgZm9yIHRlc3RpbmcsIGJ1dCBhbHNvIGZvciBhZHZhbmNlZCB1c2FnZS5cbiAgbXVzdGFjaGUuU2Nhbm5lciA9IFNjYW5uZXI7XG4gIG11c3RhY2hlLkNvbnRleHQgPSBDb250ZXh0O1xuICBtdXN0YWNoZS5Xcml0ZXIgPSBXcml0ZXI7XG5cbn0pKTtcbiIsInZhciBodG1sX3Nhbml0aXplID0gcmVxdWlyZSgnLi9zYW5pdGl6ZXItYnVuZGxlLmpzJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oXykge1xuICAgIGlmICghXykgcmV0dXJuICcnO1xuICAgIHJldHVybiBodG1sX3Nhbml0aXplKF8sIGNsZWFuVXJsLCBjbGVhbklkKTtcbn07XG5cbi8vIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTI1NTEwN1xuZnVuY3Rpb24gY2xlYW5VcmwodXJsKSB7XG4gICAgJ3VzZSBzdHJpY3QnO1xuICAgIGlmICgvXmh0dHBzPy8udGVzdCh1cmwuZ2V0U2NoZW1lKCkpKSByZXR1cm4gdXJsLnRvU3RyaW5nKCk7XG4gICAgaWYgKC9ebWFpbHRvPy8udGVzdCh1cmwuZ2V0U2NoZW1lKCkpKSByZXR1cm4gdXJsLnRvU3RyaW5nKCk7XG4gICAgaWYgKCdkYXRhJyA9PSB1cmwuZ2V0U2NoZW1lKCkgJiYgL15pbWFnZS8udGVzdCh1cmwuZ2V0UGF0aCgpKSkge1xuICAgICAgICByZXR1cm4gdXJsLnRvU3RyaW5nKCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBjbGVhbklkKGlkKSB7IHJldHVybiBpZDsgfVxuIiwiXG4vLyBDb3B5cmlnaHQgKEMpIDIwMTAgR29vZ2xlIEluYy5cbi8vXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuLy8geW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuLy8gWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4vL1xuLy8gICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbi8vXG4vLyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4vLyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4vLyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbi8vIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbi8vIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuXG4vKipcbiAqIEBmaWxlb3ZlcnZpZXdcbiAqIEltcGxlbWVudHMgUkZDIDM5ODYgZm9yIHBhcnNpbmcvZm9ybWF0dGluZyBVUklzLlxuICpcbiAqIEBhdXRob3IgbWlrZXNhbXVlbEBnbWFpbC5jb21cbiAqIFxcQHByb3ZpZGVzIFVSSVxuICogXFxAb3ZlcnJpZGVzIHdpbmRvd1xuICovXG5cbnZhciBVUkkgPSAoZnVuY3Rpb24gKCkge1xuXG4vKipcbiAqIGNyZWF0ZXMgYSB1cmkgZnJvbSB0aGUgc3RyaW5nIGZvcm0uICBUaGUgcGFyc2VyIGlzIHJlbGF4ZWQsIHNvIHNwZWNpYWxcbiAqIGNoYXJhY3RlcnMgdGhhdCBhcmVuJ3QgZXNjYXBlZCBidXQgZG9uJ3QgY2F1c2UgYW1iaWd1aXRpZXMgd2lsbCBub3QgY2F1c2VcbiAqIHBhcnNlIGZhaWx1cmVzLlxuICpcbiAqIEByZXR1cm4ge1VSSXxudWxsfVxuICovXG5mdW5jdGlvbiBwYXJzZSh1cmlTdHIpIHtcbiAgdmFyIG0gPSAoJycgKyB1cmlTdHIpLm1hdGNoKFVSSV9SRV8pO1xuICBpZiAoIW0pIHsgcmV0dXJuIG51bGw7IH1cbiAgcmV0dXJuIG5ldyBVUkkoXG4gICAgICBudWxsSWZBYnNlbnQobVsxXSksXG4gICAgICBudWxsSWZBYnNlbnQobVsyXSksXG4gICAgICBudWxsSWZBYnNlbnQobVszXSksXG4gICAgICBudWxsSWZBYnNlbnQobVs0XSksXG4gICAgICBudWxsSWZBYnNlbnQobVs1XSksXG4gICAgICBudWxsSWZBYnNlbnQobVs2XSksXG4gICAgICBudWxsSWZBYnNlbnQobVs3XSkpO1xufVxuXG5cbi8qKlxuICogY3JlYXRlcyBhIHVyaSBmcm9tIHRoZSBnaXZlbiBwYXJ0cy5cbiAqXG4gKiBAcGFyYW0gc2NoZW1lIHtzdHJpbmd9IGFuIHVuZW5jb2RlZCBzY2hlbWUgc3VjaCBhcyBcImh0dHBcIiBvciBudWxsXG4gKiBAcGFyYW0gY3JlZGVudGlhbHMge3N0cmluZ30gdW5lbmNvZGVkIHVzZXIgY3JlZGVudGlhbHMgb3IgbnVsbFxuICogQHBhcmFtIGRvbWFpbiB7c3RyaW5nfSBhbiB1bmVuY29kZWQgZG9tYWluIG5hbWUgb3IgbnVsbFxuICogQHBhcmFtIHBvcnQge251bWJlcn0gYSBwb3J0IG51bWJlciBpbiBbMSwgMzI3NjhdLlxuICogICAgLTEgaW5kaWNhdGVzIG5vIHBvcnQsIGFzIGRvZXMgbnVsbC5cbiAqIEBwYXJhbSBwYXRoIHtzdHJpbmd9IGFuIHVuZW5jb2RlZCBwYXRoXG4gKiBAcGFyYW0gcXVlcnkge0FycmF5LjxzdHJpbmc+fHN0cmluZ3xudWxsfSBhIGxpc3Qgb2YgdW5lbmNvZGVkIGNnaVxuICogICBwYXJhbWV0ZXJzIHdoZXJlIGV2ZW4gdmFsdWVzIGFyZSBrZXlzIGFuZCBvZGRzIHRoZSBjb3JyZXNwb25kaW5nIHZhbHVlc1xuICogICBvciBhbiB1bmVuY29kZWQgcXVlcnkuXG4gKiBAcGFyYW0gZnJhZ21lbnQge3N0cmluZ30gYW4gdW5lbmNvZGVkIGZyYWdtZW50IHdpdGhvdXQgdGhlIFwiI1wiIG9yIG51bGwuXG4gKiBAcmV0dXJuIHtVUkl9XG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZShzY2hlbWUsIGNyZWRlbnRpYWxzLCBkb21haW4sIHBvcnQsIHBhdGgsIHF1ZXJ5LCBmcmFnbWVudCkge1xuICB2YXIgdXJpID0gbmV3IFVSSShcbiAgICAgIGVuY29kZUlmRXhpc3RzMihzY2hlbWUsIFVSSV9ESVNBTExPV0VEX0lOX1NDSEVNRV9PUl9DUkVERU5USUFMU18pLFxuICAgICAgZW5jb2RlSWZFeGlzdHMyKFxuICAgICAgICAgIGNyZWRlbnRpYWxzLCBVUklfRElTQUxMT1dFRF9JTl9TQ0hFTUVfT1JfQ1JFREVOVElBTFNfKSxcbiAgICAgIGVuY29kZUlmRXhpc3RzKGRvbWFpbiksXG4gICAgICBwb3J0ID4gMCA/IHBvcnQudG9TdHJpbmcoKSA6IG51bGwsXG4gICAgICBlbmNvZGVJZkV4aXN0czIocGF0aCwgVVJJX0RJU0FMTE9XRURfSU5fUEFUSF8pLFxuICAgICAgbnVsbCxcbiAgICAgIGVuY29kZUlmRXhpc3RzKGZyYWdtZW50KSk7XG4gIGlmIChxdWVyeSkge1xuICAgIGlmICgnc3RyaW5nJyA9PT0gdHlwZW9mIHF1ZXJ5KSB7XG4gICAgICB1cmkuc2V0UmF3UXVlcnkocXVlcnkucmVwbGFjZSgvW14/Jj0wLTlBLVphLXpfXFwtfi4lXS9nLCBlbmNvZGVPbmUpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdXJpLnNldEFsbFBhcmFtZXRlcnMocXVlcnkpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gdXJpO1xufVxuZnVuY3Rpb24gZW5jb2RlSWZFeGlzdHModW5lc2NhcGVkUGFydCkge1xuICBpZiAoJ3N0cmluZycgPT0gdHlwZW9mIHVuZXNjYXBlZFBhcnQpIHtcbiAgICByZXR1cm4gZW5jb2RlVVJJQ29tcG9uZW50KHVuZXNjYXBlZFBhcnQpO1xuICB9XG4gIHJldHVybiBudWxsO1xufTtcbi8qKlxuICogaWYgdW5lc2NhcGVkUGFydCBpcyBub24gbnVsbCwgdGhlbiBlc2NhcGVzIGFueSBjaGFyYWN0ZXJzIGluIGl0IHRoYXQgYXJlbid0XG4gKiB2YWxpZCBjaGFyYWN0ZXJzIGluIGEgdXJsIGFuZCBhbHNvIGVzY2FwZXMgYW55IHNwZWNpYWwgY2hhcmFjdGVycyB0aGF0XG4gKiBhcHBlYXIgaW4gZXh0cmEuXG4gKlxuICogQHBhcmFtIHVuZXNjYXBlZFBhcnQge3N0cmluZ31cbiAqIEBwYXJhbSBleHRyYSB7UmVnRXhwfSBhIGNoYXJhY3RlciBzZXQgb2YgY2hhcmFjdGVycyBpbiBbXFwwMS1cXDE3N10uXG4gKiBAcmV0dXJuIHtzdHJpbmd8bnVsbH0gbnVsbCBpZmYgdW5lc2NhcGVkUGFydCA9PSBudWxsLlxuICovXG5mdW5jdGlvbiBlbmNvZGVJZkV4aXN0czIodW5lc2NhcGVkUGFydCwgZXh0cmEpIHtcbiAgaWYgKCdzdHJpbmcnID09IHR5cGVvZiB1bmVzY2FwZWRQYXJ0KSB7XG4gICAgcmV0dXJuIGVuY29kZVVSSSh1bmVzY2FwZWRQYXJ0KS5yZXBsYWNlKGV4dHJhLCBlbmNvZGVPbmUpO1xuICB9XG4gIHJldHVybiBudWxsO1xufTtcbi8qKiBjb252ZXJ0cyBhIGNoYXJhY3RlciBpbiBbXFwwMS1cXDE3N10gdG8gaXRzIHVybCBlbmNvZGVkIGVxdWl2YWxlbnQuICovXG5mdW5jdGlvbiBlbmNvZGVPbmUoY2gpIHtcbiAgdmFyIG4gPSBjaC5jaGFyQ29kZUF0KDApO1xuICByZXR1cm4gJyUnICsgJzAxMjM0NTY3ODlBQkNERUYnLmNoYXJBdCgobiA+PiA0KSAmIDB4ZikgK1xuICAgICAgJzAxMjM0NTY3ODlBQkNERUYnLmNoYXJBdChuICYgMHhmKTtcbn1cblxuLyoqXG4gKiB7QHVwZG9jXG4gKiAgJCBub3JtUGF0aCgnZm9vLy4vYmFyJylcbiAqICAjICdmb28vYmFyJ1xuICogICQgbm9ybVBhdGgoJy4vZm9vJylcbiAqICAjICdmb28nXG4gKiAgJCBub3JtUGF0aCgnZm9vLy4nKVxuICogICMgJ2ZvbydcbiAqICAkIG5vcm1QYXRoKCdmb28vL2JhcicpXG4gKiAgIyAnZm9vL2JhcidcbiAqIH1cbiAqL1xuZnVuY3Rpb24gbm9ybVBhdGgocGF0aCkge1xuICByZXR1cm4gcGF0aC5yZXBsYWNlKC8oXnxcXC8pXFwuKD86XFwvfCQpL2csICckMScpLnJlcGxhY2UoL1xcL3syLH0vZywgJy8nKTtcbn1cblxudmFyIFBBUkVOVF9ESVJFQ1RPUllfSEFORExFUiA9IG5ldyBSZWdFeHAoXG4gICAgJydcbiAgICAvLyBBIHBhdGggYnJlYWtcbiAgICArICcoL3xeKSdcbiAgICAvLyBmb2xsb3dlZCBieSBhIG5vbiAuLiBwYXRoIGVsZW1lbnRcbiAgICAvLyAoY2Fubm90IGJlIC4gYmVjYXVzZSBub3JtUGF0aCBpcyB1c2VkIHByaW9yIHRvIHRoaXMgUmVnRXhwKVxuICAgICsgJyg/OlteLi9dW14vXSp8XFxcXC57Mix9KD86W14uL11bXi9dKil8XFxcXC57Myx9W14vXSopJ1xuICAgIC8vIGZvbGxvd2VkIGJ5IC4uIGZvbGxvd2VkIGJ5IGEgcGF0aCBicmVhay5cbiAgICArICcvXFxcXC5cXFxcLig/Oi98JCknKTtcblxudmFyIFBBUkVOVF9ESVJFQ1RPUllfSEFORExFUl9SRSA9IG5ldyBSZWdFeHAoUEFSRU5UX0RJUkVDVE9SWV9IQU5ETEVSKTtcblxudmFyIEVYVFJBX1BBUkVOVF9QQVRIU19SRSA9IC9eKD86XFwuXFwuXFwvKSooPzpcXC5cXC4kKT8vO1xuXG4vKipcbiAqIE5vcm1hbGl6ZXMgaXRzIGlucHV0IHBhdGggYW5kIGNvbGxhcHNlcyBhbGwgLiBhbmQgLi4gc2VxdWVuY2VzIGV4Y2VwdCBmb3JcbiAqIC4uIHNlcXVlbmNlcyB0aGF0IHdvdWxkIHRha2UgaXQgYWJvdmUgdGhlIHJvb3Qgb2YgdGhlIGN1cnJlbnQgcGFyZW50XG4gKiBkaXJlY3RvcnkuXG4gKiB7QHVwZG9jXG4gKiAgJCBjb2xsYXBzZV9kb3RzKCdmb28vLi4vYmFyJylcbiAqICAjICdiYXInXG4gKiAgJCBjb2xsYXBzZV9kb3RzKCdmb28vLi9iYXInKVxuICogICMgJ2Zvby9iYXInXG4gKiAgJCBjb2xsYXBzZV9kb3RzKCdmb28vLi4vYmFyLy4vLi4vLi4vYmF6JylcbiAqICAjICdiYXonXG4gKiAgJCBjb2xsYXBzZV9kb3RzKCcuLi9mb28nKVxuICogICMgJy4uL2ZvbydcbiAqICAkIGNvbGxhcHNlX2RvdHMoJy4uL2ZvbycpLnJlcGxhY2UoRVhUUkFfUEFSRU5UX1BBVEhTX1JFLCAnJylcbiAqICAjICdmb28nXG4gKiB9XG4gKi9cbmZ1bmN0aW9uIGNvbGxhcHNlX2RvdHMocGF0aCkge1xuICBpZiAocGF0aCA9PT0gbnVsbCkgeyByZXR1cm4gbnVsbDsgfVxuICB2YXIgcCA9IG5vcm1QYXRoKHBhdGgpO1xuICAvLyBPbmx5IC8uLi8gbGVmdCB0byBmbGF0dGVuXG4gIHZhciByID0gUEFSRU5UX0RJUkVDVE9SWV9IQU5ETEVSX1JFO1xuICAvLyBXZSByZXBsYWNlIHdpdGggJDEgd2hpY2ggbWF0Y2hlcyBhIC8gYmVmb3JlIHRoZSAuLiBiZWNhdXNlIHRoaXNcbiAgLy8gZ3VhcmFudGVlcyB0aGF0OlxuICAvLyAoMSkgd2UgaGF2ZSBhdCBtb3N0IDEgLyBiZXR3ZWVuIHRoZSBhZGphY2VudCBwbGFjZSxcbiAgLy8gKDIpIGFsd2F5cyBoYXZlIGEgc2xhc2ggaWYgdGhlcmUgaXMgYSBwcmVjZWRpbmcgcGF0aCBzZWN0aW9uLCBhbmRcbiAgLy8gKDMpIHdlIG5ldmVyIHR1cm4gYSByZWxhdGl2ZSBwYXRoIGludG8gYW4gYWJzb2x1dGUgcGF0aC5cbiAgZm9yICh2YXIgcTsgKHEgPSBwLnJlcGxhY2UociwgJyQxJykpICE9IHA7IHAgPSBxKSB7fTtcbiAgcmV0dXJuIHA7XG59XG5cbi8qKlxuICogcmVzb2x2ZXMgYSByZWxhdGl2ZSB1cmwgc3RyaW5nIHRvIGEgYmFzZSB1cmkuXG4gKiBAcmV0dXJuIHtVUkl9XG4gKi9cbmZ1bmN0aW9uIHJlc29sdmUoYmFzZVVyaSwgcmVsYXRpdmVVcmkpIHtcbiAgLy8gdGhlcmUgYXJlIHNldmVyYWwga2luZHMgb2YgcmVsYXRpdmUgdXJsczpcbiAgLy8gMS4gLy9mb28gLSByZXBsYWNlcyBldmVyeXRoaW5nIGZyb20gdGhlIGRvbWFpbiBvbi4gIGZvbyBpcyBhIGRvbWFpbiBuYW1lXG4gIC8vIDIuIGZvbyAtIHJlcGxhY2VzIHRoZSBsYXN0IHBhcnQgb2YgdGhlIHBhdGgsIHRoZSB3aG9sZSBxdWVyeSBhbmQgZnJhZ21lbnRcbiAgLy8gMy4gL2ZvbyAtIHJlcGxhY2VzIHRoZSB0aGUgcGF0aCwgdGhlIHF1ZXJ5IGFuZCBmcmFnbWVudFxuICAvLyA0LiA/Zm9vIC0gcmVwbGFjZSB0aGUgcXVlcnkgYW5kIGZyYWdtZW50XG4gIC8vIDUuICNmb28gLSByZXBsYWNlIHRoZSBmcmFnbWVudCBvbmx5XG5cbiAgdmFyIGFic29sdXRlVXJpID0gYmFzZVVyaS5jbG9uZSgpO1xuICAvLyB3ZSBzYXRpc2Z5IHRoZXNlIGNvbmRpdGlvbnMgYnkgbG9va2luZyBmb3IgdGhlIGZpcnN0IHBhcnQgb2YgcmVsYXRpdmVVcmlcbiAgLy8gdGhhdCBpcyBub3QgYmxhbmsgYW5kIGFwcGx5aW5nIGRlZmF1bHRzIHRvIHRoZSByZXN0XG5cbiAgdmFyIG92ZXJyaWRkZW4gPSByZWxhdGl2ZVVyaS5oYXNTY2hlbWUoKTtcblxuICBpZiAob3ZlcnJpZGRlbikge1xuICAgIGFic29sdXRlVXJpLnNldFJhd1NjaGVtZShyZWxhdGl2ZVVyaS5nZXRSYXdTY2hlbWUoKSk7XG4gIH0gZWxzZSB7XG4gICAgb3ZlcnJpZGRlbiA9IHJlbGF0aXZlVXJpLmhhc0NyZWRlbnRpYWxzKCk7XG4gIH1cblxuICBpZiAob3ZlcnJpZGRlbikge1xuICAgIGFic29sdXRlVXJpLnNldFJhd0NyZWRlbnRpYWxzKHJlbGF0aXZlVXJpLmdldFJhd0NyZWRlbnRpYWxzKCkpO1xuICB9IGVsc2Uge1xuICAgIG92ZXJyaWRkZW4gPSByZWxhdGl2ZVVyaS5oYXNEb21haW4oKTtcbiAgfVxuXG4gIGlmIChvdmVycmlkZGVuKSB7XG4gICAgYWJzb2x1dGVVcmkuc2V0UmF3RG9tYWluKHJlbGF0aXZlVXJpLmdldFJhd0RvbWFpbigpKTtcbiAgfSBlbHNlIHtcbiAgICBvdmVycmlkZGVuID0gcmVsYXRpdmVVcmkuaGFzUG9ydCgpO1xuICB9XG5cbiAgdmFyIHJhd1BhdGggPSByZWxhdGl2ZVVyaS5nZXRSYXdQYXRoKCk7XG4gIHZhciBzaW1wbGlmaWVkUGF0aCA9IGNvbGxhcHNlX2RvdHMocmF3UGF0aCk7XG4gIGlmIChvdmVycmlkZGVuKSB7XG4gICAgYWJzb2x1dGVVcmkuc2V0UG9ydChyZWxhdGl2ZVVyaS5nZXRQb3J0KCkpO1xuICAgIHNpbXBsaWZpZWRQYXRoID0gc2ltcGxpZmllZFBhdGhcbiAgICAgICAgJiYgc2ltcGxpZmllZFBhdGgucmVwbGFjZShFWFRSQV9QQVJFTlRfUEFUSFNfUkUsICcnKTtcbiAgfSBlbHNlIHtcbiAgICBvdmVycmlkZGVuID0gISFyYXdQYXRoO1xuICAgIGlmIChvdmVycmlkZGVuKSB7XG4gICAgICAvLyByZXNvbHZlIHBhdGggcHJvcGVybHlcbiAgICAgIGlmIChzaW1wbGlmaWVkUGF0aC5jaGFyQ29kZUF0KDApICE9PSAweDJmIC8qIC8gKi8pIHsgIC8vIHBhdGggaXMgcmVsYXRpdmVcbiAgICAgICAgdmFyIGFic1Jhd1BhdGggPSBjb2xsYXBzZV9kb3RzKGFic29sdXRlVXJpLmdldFJhd1BhdGgoKSB8fCAnJylcbiAgICAgICAgICAgIC5yZXBsYWNlKEVYVFJBX1BBUkVOVF9QQVRIU19SRSwgJycpO1xuICAgICAgICB2YXIgc2xhc2ggPSBhYnNSYXdQYXRoLmxhc3RJbmRleE9mKCcvJykgKyAxO1xuICAgICAgICBzaW1wbGlmaWVkUGF0aCA9IGNvbGxhcHNlX2RvdHMoXG4gICAgICAgICAgICAoc2xhc2ggPyBhYnNSYXdQYXRoLnN1YnN0cmluZygwLCBzbGFzaCkgOiAnJylcbiAgICAgICAgICAgICsgY29sbGFwc2VfZG90cyhyYXdQYXRoKSlcbiAgICAgICAgICAgIC5yZXBsYWNlKEVYVFJBX1BBUkVOVF9QQVRIU19SRSwgJycpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBzaW1wbGlmaWVkUGF0aCA9IHNpbXBsaWZpZWRQYXRoXG4gICAgICAgICAgJiYgc2ltcGxpZmllZFBhdGgucmVwbGFjZShFWFRSQV9QQVJFTlRfUEFUSFNfUkUsICcnKTtcbiAgICAgIGlmIChzaW1wbGlmaWVkUGF0aCAhPT0gcmF3UGF0aCkge1xuICAgICAgICBhYnNvbHV0ZVVyaS5zZXRSYXdQYXRoKHNpbXBsaWZpZWRQYXRoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBpZiAob3ZlcnJpZGRlbikge1xuICAgIGFic29sdXRlVXJpLnNldFJhd1BhdGgoc2ltcGxpZmllZFBhdGgpO1xuICB9IGVsc2Uge1xuICAgIG92ZXJyaWRkZW4gPSByZWxhdGl2ZVVyaS5oYXNRdWVyeSgpO1xuICB9XG5cbiAgaWYgKG92ZXJyaWRkZW4pIHtcbiAgICBhYnNvbHV0ZVVyaS5zZXRSYXdRdWVyeShyZWxhdGl2ZVVyaS5nZXRSYXdRdWVyeSgpKTtcbiAgfSBlbHNlIHtcbiAgICBvdmVycmlkZGVuID0gcmVsYXRpdmVVcmkuaGFzRnJhZ21lbnQoKTtcbiAgfVxuXG4gIGlmIChvdmVycmlkZGVuKSB7XG4gICAgYWJzb2x1dGVVcmkuc2V0UmF3RnJhZ21lbnQocmVsYXRpdmVVcmkuZ2V0UmF3RnJhZ21lbnQoKSk7XG4gIH1cblxuICByZXR1cm4gYWJzb2x1dGVVcmk7XG59XG5cbi8qKlxuICogYSBtdXRhYmxlIFVSSS5cbiAqXG4gKiBUaGlzIGNsYXNzIGNvbnRhaW5zIHNldHRlcnMgYW5kIGdldHRlcnMgZm9yIHRoZSBwYXJ0cyBvZiB0aGUgVVJJLlxuICogVGhlIDx0dD5nZXRYWVo8L3R0Pi88dHQ+c2V0WFlaPC90dD4gbWV0aG9kcyByZXR1cm4gdGhlIGRlY29kZWQgcGFydCAtLSBzb1xuICogPGNvZGU+dXJpLnBhcnNlKCcvZm9vJTIwYmFyJykuZ2V0UGF0aCgpPC9jb2RlPiB3aWxsIHJldHVybiB0aGUgZGVjb2RlZCBwYXRoLFxuICogPHR0Pi9mb28gYmFyPC90dD4uXG4gKlxuICogPHA+VGhlIHJhdyB2ZXJzaW9ucyBvZiBmaWVsZHMgYXJlIGF2YWlsYWJsZSB0b28uXG4gKiA8Y29kZT51cmkucGFyc2UoJy9mb28lMjBiYXInKS5nZXRSYXdQYXRoKCk8L2NvZGU+IHdpbGwgcmV0dXJuIHRoZSByYXcgcGF0aCxcbiAqIDx0dD4vZm9vJTIwYmFyPC90dD4uICBVc2UgdGhlIHJhdyBzZXR0ZXJzIHdpdGggY2FyZSwgc2luY2VcbiAqIDxjb2RlPlVSSTo6dG9TdHJpbmc8L2NvZGU+IGlzIG5vdCBndWFyYW50ZWVkIHRvIHJldHVybiBhIHZhbGlkIHVybCBpZiBhXG4gKiByYXcgc2V0dGVyIHdhcyB1c2VkLlxuICpcbiAqIDxwPkFsbCBzZXR0ZXJzIHJldHVybiA8dHQ+dGhpczwvdHQ+IGFuZCBzbyBtYXkgYmUgY2hhaW5lZCwgYSBsYVxuICogPGNvZGU+dXJpLnBhcnNlKCcvZm9vJykuc2V0RnJhZ21lbnQoJ3BhcnQnKS50b1N0cmluZygpPC9jb2RlPi5cbiAqXG4gKiA8cD5Zb3Ugc2hvdWxkIG5vdCB1c2UgdGhpcyBjb25zdHJ1Y3RvciBkaXJlY3RseSAtLSBwbGVhc2UgcHJlZmVyIHRoZSBmYWN0b3J5XG4gKiBmdW5jdGlvbnMge0BsaW5rIHVyaS5wYXJzZX0sIHtAbGluayB1cmkuY3JlYXRlfSwge0BsaW5rIHVyaS5yZXNvbHZlfVxuICogaW5zdGVhZC48L3A+XG4gKlxuICogPHA+VGhlIHBhcmFtZXRlcnMgYXJlIGFsbCByYXcgKGFzc3VtZWQgdG8gYmUgcHJvcGVybHkgZXNjYXBlZCkgcGFydHMsIGFuZFxuICogYW55IChidXQgbm90IGFsbCkgbWF5IGJlIG51bGwuICBVbmRlZmluZWQgaXMgbm90IGFsbG93ZWQuPC9wPlxuICpcbiAqIEBjb25zdHJ1Y3RvclxuICovXG5mdW5jdGlvbiBVUkkoXG4gICAgcmF3U2NoZW1lLFxuICAgIHJhd0NyZWRlbnRpYWxzLCByYXdEb21haW4sIHBvcnQsXG4gICAgcmF3UGF0aCwgcmF3UXVlcnksIHJhd0ZyYWdtZW50KSB7XG4gIHRoaXMuc2NoZW1lXyA9IHJhd1NjaGVtZTtcbiAgdGhpcy5jcmVkZW50aWFsc18gPSByYXdDcmVkZW50aWFscztcbiAgdGhpcy5kb21haW5fID0gcmF3RG9tYWluO1xuICB0aGlzLnBvcnRfID0gcG9ydDtcbiAgdGhpcy5wYXRoXyA9IHJhd1BhdGg7XG4gIHRoaXMucXVlcnlfID0gcmF3UXVlcnk7XG4gIHRoaXMuZnJhZ21lbnRfID0gcmF3RnJhZ21lbnQ7XG4gIC8qKlxuICAgKiBAdHlwZSB7QXJyYXl8bnVsbH1cbiAgICovXG4gIHRoaXMucGFyYW1DYWNoZV8gPSBudWxsO1xufVxuXG4vKiogcmV0dXJucyB0aGUgc3RyaW5nIGZvcm0gb2YgdGhlIHVybC4gKi9cblVSSS5wcm90b3R5cGUudG9TdHJpbmcgPSBmdW5jdGlvbiAoKSB7XG4gIHZhciBvdXQgPSBbXTtcbiAgaWYgKG51bGwgIT09IHRoaXMuc2NoZW1lXykgeyBvdXQucHVzaCh0aGlzLnNjaGVtZV8sICc6Jyk7IH1cbiAgaWYgKG51bGwgIT09IHRoaXMuZG9tYWluXykge1xuICAgIG91dC5wdXNoKCcvLycpO1xuICAgIGlmIChudWxsICE9PSB0aGlzLmNyZWRlbnRpYWxzXykgeyBvdXQucHVzaCh0aGlzLmNyZWRlbnRpYWxzXywgJ0AnKTsgfVxuICAgIG91dC5wdXNoKHRoaXMuZG9tYWluXyk7XG4gICAgaWYgKG51bGwgIT09IHRoaXMucG9ydF8pIHsgb3V0LnB1c2goJzonLCB0aGlzLnBvcnRfLnRvU3RyaW5nKCkpOyB9XG4gIH1cbiAgaWYgKG51bGwgIT09IHRoaXMucGF0aF8pIHsgb3V0LnB1c2godGhpcy5wYXRoXyk7IH1cbiAgaWYgKG51bGwgIT09IHRoaXMucXVlcnlfKSB7IG91dC5wdXNoKCc/JywgdGhpcy5xdWVyeV8pOyB9XG4gIGlmIChudWxsICE9PSB0aGlzLmZyYWdtZW50XykgeyBvdXQucHVzaCgnIycsIHRoaXMuZnJhZ21lbnRfKTsgfVxuICByZXR1cm4gb3V0LmpvaW4oJycpO1xufTtcblxuVVJJLnByb3RvdHlwZS5jbG9uZSA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIG5ldyBVUkkodGhpcy5zY2hlbWVfLCB0aGlzLmNyZWRlbnRpYWxzXywgdGhpcy5kb21haW5fLCB0aGlzLnBvcnRfLFxuICAgICAgICAgICAgICAgICB0aGlzLnBhdGhfLCB0aGlzLnF1ZXJ5XywgdGhpcy5mcmFnbWVudF8pO1xufTtcblxuVVJJLnByb3RvdHlwZS5nZXRTY2hlbWUgPSBmdW5jdGlvbiAoKSB7XG4gIC8vIEhUTUw1IHNwZWMgZG9lcyBub3QgcmVxdWlyZSB0aGUgc2NoZW1lIHRvIGJlIGxvd2VyY2FzZWQgYnV0XG4gIC8vIGFsbCBjb21tb24gYnJvd3NlcnMgZXhjZXB0IFNhZmFyaSBsb3dlcmNhc2UgdGhlIHNjaGVtZS5cbiAgcmV0dXJuIHRoaXMuc2NoZW1lXyAmJiBkZWNvZGVVUklDb21wb25lbnQodGhpcy5zY2hlbWVfKS50b0xvd2VyQ2FzZSgpO1xufTtcblVSSS5wcm90b3R5cGUuZ2V0UmF3U2NoZW1lID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5zY2hlbWVfO1xufTtcblVSSS5wcm90b3R5cGUuc2V0U2NoZW1lID0gZnVuY3Rpb24gKG5ld1NjaGVtZSkge1xuICB0aGlzLnNjaGVtZV8gPSBlbmNvZGVJZkV4aXN0czIoXG4gICAgICBuZXdTY2hlbWUsIFVSSV9ESVNBTExPV0VEX0lOX1NDSEVNRV9PUl9DUkVERU5USUFMU18pO1xuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLnNldFJhd1NjaGVtZSA9IGZ1bmN0aW9uIChuZXdTY2hlbWUpIHtcbiAgdGhpcy5zY2hlbWVfID0gbmV3U2NoZW1lID8gbmV3U2NoZW1lIDogbnVsbDtcbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNTY2hlbWUgPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBudWxsICE9PSB0aGlzLnNjaGVtZV87XG59O1xuXG5cblVSSS5wcm90b3R5cGUuZ2V0Q3JlZGVudGlhbHMgPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiB0aGlzLmNyZWRlbnRpYWxzXyAmJiBkZWNvZGVVUklDb21wb25lbnQodGhpcy5jcmVkZW50aWFsc18pO1xufTtcblVSSS5wcm90b3R5cGUuZ2V0UmF3Q3JlZGVudGlhbHMgPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiB0aGlzLmNyZWRlbnRpYWxzXztcbn07XG5VUkkucHJvdG90eXBlLnNldENyZWRlbnRpYWxzID0gZnVuY3Rpb24gKG5ld0NyZWRlbnRpYWxzKSB7XG4gIHRoaXMuY3JlZGVudGlhbHNfID0gZW5jb2RlSWZFeGlzdHMyKFxuICAgICAgbmV3Q3JlZGVudGlhbHMsIFVSSV9ESVNBTExPV0VEX0lOX1NDSEVNRV9PUl9DUkVERU5USUFMU18pO1xuXG4gIHJldHVybiB0aGlzO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UmF3Q3JlZGVudGlhbHMgPSBmdW5jdGlvbiAobmV3Q3JlZGVudGlhbHMpIHtcbiAgdGhpcy5jcmVkZW50aWFsc18gPSBuZXdDcmVkZW50aWFscyA/IG5ld0NyZWRlbnRpYWxzIDogbnVsbDtcbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNDcmVkZW50aWFscyA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIG51bGwgIT09IHRoaXMuY3JlZGVudGlhbHNfO1xufTtcblxuXG5VUkkucHJvdG90eXBlLmdldERvbWFpbiA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIHRoaXMuZG9tYWluXyAmJiBkZWNvZGVVUklDb21wb25lbnQodGhpcy5kb21haW5fKTtcbn07XG5VUkkucHJvdG90eXBlLmdldFJhd0RvbWFpbiA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIHRoaXMuZG9tYWluXztcbn07XG5VUkkucHJvdG90eXBlLnNldERvbWFpbiA9IGZ1bmN0aW9uIChuZXdEb21haW4pIHtcbiAgcmV0dXJuIHRoaXMuc2V0UmF3RG9tYWluKG5ld0RvbWFpbiAmJiBlbmNvZGVVUklDb21wb25lbnQobmV3RG9tYWluKSk7XG59O1xuVVJJLnByb3RvdHlwZS5zZXRSYXdEb21haW4gPSBmdW5jdGlvbiAobmV3RG9tYWluKSB7XG4gIHRoaXMuZG9tYWluXyA9IG5ld0RvbWFpbiA/IG5ld0RvbWFpbiA6IG51bGw7XG4gIC8vIE1haW50YWluIHRoZSBpbnZhcmlhbnQgdGhhdCBwYXRocyBtdXN0IHN0YXJ0IHdpdGggYSBzbGFzaCB3aGVuIHRoZSBVUklcbiAgLy8gaXMgbm90IHBhdGgtcmVsYXRpdmUuXG4gIHJldHVybiB0aGlzLnNldFJhd1BhdGgodGhpcy5wYXRoXyk7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNEb21haW4gPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBudWxsICE9PSB0aGlzLmRvbWFpbl87XG59O1xuXG5cblVSSS5wcm90b3R5cGUuZ2V0UG9ydCA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIHRoaXMucG9ydF8gJiYgZGVjb2RlVVJJQ29tcG9uZW50KHRoaXMucG9ydF8pO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UG9ydCA9IGZ1bmN0aW9uIChuZXdQb3J0KSB7XG4gIGlmIChuZXdQb3J0KSB7XG4gICAgbmV3UG9ydCA9IE51bWJlcihuZXdQb3J0KTtcbiAgICBpZiAobmV3UG9ydCAhPT0gKG5ld1BvcnQgJiAweGZmZmYpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0JhZCBwb3J0IG51bWJlciAnICsgbmV3UG9ydCk7XG4gICAgfVxuICAgIHRoaXMucG9ydF8gPSAnJyArIG5ld1BvcnQ7XG4gIH0gZWxzZSB7XG4gICAgdGhpcy5wb3J0XyA9IG51bGw7XG4gIH1cbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNQb3J0ID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gbnVsbCAhPT0gdGhpcy5wb3J0Xztcbn07XG5cblxuVVJJLnByb3RvdHlwZS5nZXRQYXRoID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5wYXRoXyAmJiBkZWNvZGVVUklDb21wb25lbnQodGhpcy5wYXRoXyk7XG59O1xuVVJJLnByb3RvdHlwZS5nZXRSYXdQYXRoID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5wYXRoXztcbn07XG5VUkkucHJvdG90eXBlLnNldFBhdGggPSBmdW5jdGlvbiAobmV3UGF0aCkge1xuICByZXR1cm4gdGhpcy5zZXRSYXdQYXRoKGVuY29kZUlmRXhpc3RzMihuZXdQYXRoLCBVUklfRElTQUxMT1dFRF9JTl9QQVRIXykpO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UmF3UGF0aCA9IGZ1bmN0aW9uIChuZXdQYXRoKSB7XG4gIGlmIChuZXdQYXRoKSB7XG4gICAgbmV3UGF0aCA9IFN0cmluZyhuZXdQYXRoKTtcbiAgICB0aGlzLnBhdGhfID0gXG4gICAgICAvLyBQYXRocyBtdXN0IHN0YXJ0IHdpdGggJy8nIHVubGVzcyB0aGlzIGlzIGEgcGF0aC1yZWxhdGl2ZSBVUkwuXG4gICAgICAoIXRoaXMuZG9tYWluXyB8fCAvXlxcLy8udGVzdChuZXdQYXRoKSkgPyBuZXdQYXRoIDogJy8nICsgbmV3UGF0aDtcbiAgfSBlbHNlIHtcbiAgICB0aGlzLnBhdGhfID0gbnVsbDtcbiAgfVxuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLmhhc1BhdGggPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBudWxsICE9PSB0aGlzLnBhdGhfO1xufTtcblxuXG5VUkkucHJvdG90eXBlLmdldFF1ZXJ5ID0gZnVuY3Rpb24gKCkge1xuICAvLyBGcm9tIGh0dHA6Ly93d3cudzMub3JnL0FkZHJlc3NpbmcvVVJMLzRfVVJJX1JlY29tbWVudGF0aW9ucy5odG1sXG4gIC8vIFdpdGhpbiB0aGUgcXVlcnkgc3RyaW5nLCB0aGUgcGx1cyBzaWduIGlzIHJlc2VydmVkIGFzIHNob3J0aGFuZCBub3RhdGlvblxuICAvLyBmb3IgYSBzcGFjZS5cbiAgcmV0dXJuIHRoaXMucXVlcnlfICYmIGRlY29kZVVSSUNvbXBvbmVudCh0aGlzLnF1ZXJ5XykucmVwbGFjZSgvXFwrL2csICcgJyk7XG59O1xuVVJJLnByb3RvdHlwZS5nZXRSYXdRdWVyeSA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIHRoaXMucXVlcnlfO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UXVlcnkgPSBmdW5jdGlvbiAobmV3UXVlcnkpIHtcbiAgdGhpcy5wYXJhbUNhY2hlXyA9IG51bGw7XG4gIHRoaXMucXVlcnlfID0gZW5jb2RlSWZFeGlzdHMobmV3UXVlcnkpO1xuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLnNldFJhd1F1ZXJ5ID0gZnVuY3Rpb24gKG5ld1F1ZXJ5KSB7XG4gIHRoaXMucGFyYW1DYWNoZV8gPSBudWxsO1xuICB0aGlzLnF1ZXJ5XyA9IG5ld1F1ZXJ5ID8gbmV3UXVlcnkgOiBudWxsO1xuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLmhhc1F1ZXJ5ID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gbnVsbCAhPT0gdGhpcy5xdWVyeV87XG59O1xuXG4vKipcbiAqIHNldHMgdGhlIHF1ZXJ5IGdpdmVuIGEgbGlzdCBvZiBzdHJpbmdzIG9mIHRoZSBmb3JtXG4gKiBbIGtleTAsIHZhbHVlMCwga2V5MSwgdmFsdWUxLCAuLi4gXS5cbiAqXG4gKiA8cD48Y29kZT51cmkuc2V0QWxsUGFyYW1ldGVycyhbJ2EnLCAnYicsICdjJywgJ2QnXSkuZ2V0UXVlcnkoKTwvY29kZT5cbiAqIHdpbGwgeWllbGQgPGNvZGU+J2E9YiZjPWQnPC9jb2RlPi5cbiAqL1xuVVJJLnByb3RvdHlwZS5zZXRBbGxQYXJhbWV0ZXJzID0gZnVuY3Rpb24gKHBhcmFtcykge1xuICBpZiAodHlwZW9mIHBhcmFtcyA9PT0gJ29iamVjdCcpIHtcbiAgICBpZiAoIShwYXJhbXMgaW5zdGFuY2VvZiBBcnJheSlcbiAgICAgICAgJiYgKHBhcmFtcyBpbnN0YW5jZW9mIE9iamVjdFxuICAgICAgICAgICAgfHwgT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHBhcmFtcykgIT09ICdbb2JqZWN0IEFycmF5XScpKSB7XG4gICAgICB2YXIgbmV3UGFyYW1zID0gW107XG4gICAgICB2YXIgaSA9IC0xO1xuICAgICAgZm9yICh2YXIgayBpbiBwYXJhbXMpIHtcbiAgICAgICAgdmFyIHYgPSBwYXJhbXNba107XG4gICAgICAgIGlmICgnc3RyaW5nJyA9PT0gdHlwZW9mIHYpIHtcbiAgICAgICAgICBuZXdQYXJhbXNbKytpXSA9IGs7XG4gICAgICAgICAgbmV3UGFyYW1zWysraV0gPSB2O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBwYXJhbXMgPSBuZXdQYXJhbXM7XG4gICAgfVxuICB9XG4gIHRoaXMucGFyYW1DYWNoZV8gPSBudWxsO1xuICB2YXIgcXVlcnlCdWYgPSBbXTtcbiAgdmFyIHNlcGFyYXRvciA9ICcnO1xuICBmb3IgKHZhciBqID0gMDsgaiA8IHBhcmFtcy5sZW5ndGg7KSB7XG4gICAgdmFyIGsgPSBwYXJhbXNbaisrXTtcbiAgICB2YXIgdiA9IHBhcmFtc1tqKytdO1xuICAgIHF1ZXJ5QnVmLnB1c2goc2VwYXJhdG9yLCBlbmNvZGVVUklDb21wb25lbnQoay50b1N0cmluZygpKSk7XG4gICAgc2VwYXJhdG9yID0gJyYnO1xuICAgIGlmICh2KSB7XG4gICAgICBxdWVyeUJ1Zi5wdXNoKCc9JywgZW5jb2RlVVJJQ29tcG9uZW50KHYudG9TdHJpbmcoKSkpO1xuICAgIH1cbiAgfVxuICB0aGlzLnF1ZXJ5XyA9IHF1ZXJ5QnVmLmpvaW4oJycpO1xuICByZXR1cm4gdGhpcztcbn07XG5VUkkucHJvdG90eXBlLmNoZWNrUGFyYW1ldGVyQ2FjaGVfID0gZnVuY3Rpb24gKCkge1xuICBpZiAoIXRoaXMucGFyYW1DYWNoZV8pIHtcbiAgICB2YXIgcSA9IHRoaXMucXVlcnlfO1xuICAgIGlmICghcSkge1xuICAgICAgdGhpcy5wYXJhbUNhY2hlXyA9IFtdO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgY2dpUGFyYW1zID0gcS5zcGxpdCgvWyZcXD9dLyk7XG4gICAgICB2YXIgb3V0ID0gW107XG4gICAgICB2YXIgayA9IC0xO1xuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjZ2lQYXJhbXMubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgdmFyIG0gPSBjZ2lQYXJhbXNbaV0ubWF0Y2goL14oW149XSopKD86PSguKikpPyQvKTtcbiAgICAgICAgLy8gRnJvbSBodHRwOi8vd3d3LnczLm9yZy9BZGRyZXNzaW5nL1VSTC80X1VSSV9SZWNvbW1lbnRhdGlvbnMuaHRtbFxuICAgICAgICAvLyBXaXRoaW4gdGhlIHF1ZXJ5IHN0cmluZywgdGhlIHBsdXMgc2lnbiBpcyByZXNlcnZlZCBhcyBzaG9ydGhhbmRcbiAgICAgICAgLy8gbm90YXRpb24gZm9yIGEgc3BhY2UuXG4gICAgICAgIG91dFsrK2tdID0gZGVjb2RlVVJJQ29tcG9uZW50KG1bMV0pLnJlcGxhY2UoL1xcKy9nLCAnICcpO1xuICAgICAgICBvdXRbKytrXSA9IGRlY29kZVVSSUNvbXBvbmVudChtWzJdIHx8ICcnKS5yZXBsYWNlKC9cXCsvZywgJyAnKTtcbiAgICAgIH1cbiAgICAgIHRoaXMucGFyYW1DYWNoZV8gPSBvdXQ7XG4gICAgfVxuICB9XG59O1xuLyoqXG4gKiBzZXRzIHRoZSB2YWx1ZXMgb2YgdGhlIG5hbWVkIGNnaSBwYXJhbWV0ZXJzLlxuICpcbiAqIDxwPlNvLCA8Y29kZT51cmkucGFyc2UoJ2Zvbz9hPWImYz1kJmU9ZicpLnNldFBhcmFtZXRlclZhbHVlcygnYycsIFsnbmV3J10pXG4gKiA8L2NvZGU+IHlpZWxkcyA8dHQ+Zm9vP2E9YiZjPW5ldyZlPWY8L3R0Pi48L3A+XG4gKlxuICogQHBhcmFtIGtleSB7c3RyaW5nfVxuICogQHBhcmFtIHZhbHVlcyB7QXJyYXkuPHN0cmluZz59IHRoZSBuZXcgdmFsdWVzLiAgSWYgdmFsdWVzIGlzIGEgc2luZ2xlIHN0cmluZ1xuICogICB0aGVuIGl0IHdpbGwgYmUgdHJlYXRlZCBhcyB0aGUgc29sZSB2YWx1ZS5cbiAqL1xuVVJJLnByb3RvdHlwZS5zZXRQYXJhbWV0ZXJWYWx1ZXMgPSBmdW5jdGlvbiAoa2V5LCB2YWx1ZXMpIHtcbiAgLy8gYmUgbmljZSBhbmQgYXZvaWQgc3VidGxlIGJ1Z3Mgd2hlcmUgW10gb3BlcmF0b3Igb24gc3RyaW5nIHBlcmZvcm1zIGNoYXJBdFxuICAvLyBvbiBzb21lIGJyb3dzZXJzIGFuZCBjcmFzaGVzIG9uIElFXG4gIGlmICh0eXBlb2YgdmFsdWVzID09PSAnc3RyaW5nJykge1xuICAgIHZhbHVlcyA9IFsgdmFsdWVzIF07XG4gIH1cblxuICB0aGlzLmNoZWNrUGFyYW1ldGVyQ2FjaGVfKCk7XG4gIHZhciBuZXdWYWx1ZUluZGV4ID0gMDtcbiAgdmFyIHBjID0gdGhpcy5wYXJhbUNhY2hlXztcbiAgdmFyIHBhcmFtcyA9IFtdO1xuICBmb3IgKHZhciBpID0gMCwgayA9IDA7IGkgPCBwYy5sZW5ndGg7IGkgKz0gMikge1xuICAgIGlmIChrZXkgPT09IHBjW2ldKSB7XG4gICAgICBpZiAobmV3VmFsdWVJbmRleCA8IHZhbHVlcy5sZW5ndGgpIHtcbiAgICAgICAgcGFyYW1zLnB1c2goa2V5LCB2YWx1ZXNbbmV3VmFsdWVJbmRleCsrXSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhcmFtcy5wdXNoKHBjW2ldLCBwY1tpICsgMV0pO1xuICAgIH1cbiAgfVxuICB3aGlsZSAobmV3VmFsdWVJbmRleCA8IHZhbHVlcy5sZW5ndGgpIHtcbiAgICBwYXJhbXMucHVzaChrZXksIHZhbHVlc1tuZXdWYWx1ZUluZGV4KytdKTtcbiAgfVxuICB0aGlzLnNldEFsbFBhcmFtZXRlcnMocGFyYW1zKTtcbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5yZW1vdmVQYXJhbWV0ZXIgPSBmdW5jdGlvbiAoa2V5KSB7XG4gIHJldHVybiB0aGlzLnNldFBhcmFtZXRlclZhbHVlcyhrZXksIFtdKTtcbn07XG4vKipcbiAqIHJldHVybnMgdGhlIHBhcmFtZXRlcnMgc3BlY2lmaWVkIGluIHRoZSBxdWVyeSBwYXJ0IG9mIHRoZSB1cmkgYXMgYSBsaXN0IG9mXG4gKiBrZXlzIGFuZCB2YWx1ZXMgbGlrZSBbIGtleTAsIHZhbHVlMCwga2V5MSwgdmFsdWUxLCAuLi4gXS5cbiAqXG4gKiBAcmV0dXJuIHtBcnJheS48c3RyaW5nPn1cbiAqL1xuVVJJLnByb3RvdHlwZS5nZXRBbGxQYXJhbWV0ZXJzID0gZnVuY3Rpb24gKCkge1xuICB0aGlzLmNoZWNrUGFyYW1ldGVyQ2FjaGVfKCk7XG4gIHJldHVybiB0aGlzLnBhcmFtQ2FjaGVfLnNsaWNlKDAsIHRoaXMucGFyYW1DYWNoZV8ubGVuZ3RoKTtcbn07XG4vKipcbiAqIHJldHVybnMgdGhlIHZhbHVlPGI+czwvYj4gZm9yIGEgZ2l2ZW4gY2dpIHBhcmFtZXRlciBhcyBhIGxpc3Qgb2YgZGVjb2RlZFxuICogcXVlcnkgcGFyYW1ldGVyIHZhbHVlcy5cbiAqIEByZXR1cm4ge0FycmF5LjxzdHJpbmc+fVxuICovXG5VUkkucHJvdG90eXBlLmdldFBhcmFtZXRlclZhbHVlcyA9IGZ1bmN0aW9uIChwYXJhbU5hbWVVbmVzY2FwZWQpIHtcbiAgdGhpcy5jaGVja1BhcmFtZXRlckNhY2hlXygpO1xuICB2YXIgdmFsdWVzID0gW107XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5wYXJhbUNhY2hlXy5sZW5ndGg7IGkgKz0gMikge1xuICAgIGlmIChwYXJhbU5hbWVVbmVzY2FwZWQgPT09IHRoaXMucGFyYW1DYWNoZV9baV0pIHtcbiAgICAgIHZhbHVlcy5wdXNoKHRoaXMucGFyYW1DYWNoZV9baSArIDFdKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHZhbHVlcztcbn07XG4vKipcbiAqIHJldHVybnMgYSBtYXAgb2YgY2dpIHBhcmFtZXRlciBuYW1lcyB0byAobm9uLWVtcHR5KSBsaXN0cyBvZiB2YWx1ZXMuXG4gKiBAcmV0dXJuIHtPYmplY3QuPHN0cmluZyxBcnJheS48c3RyaW5nPj59XG4gKi9cblVSSS5wcm90b3R5cGUuZ2V0UGFyYW1ldGVyTWFwID0gZnVuY3Rpb24gKHBhcmFtTmFtZVVuZXNjYXBlZCkge1xuICB0aGlzLmNoZWNrUGFyYW1ldGVyQ2FjaGVfKCk7XG4gIHZhciBwYXJhbU1hcCA9IHt9O1xuICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMucGFyYW1DYWNoZV8ubGVuZ3RoOyBpICs9IDIpIHtcbiAgICB2YXIga2V5ID0gdGhpcy5wYXJhbUNhY2hlX1tpKytdLFxuICAgICAgdmFsdWUgPSB0aGlzLnBhcmFtQ2FjaGVfW2krK107XG4gICAgaWYgKCEoa2V5IGluIHBhcmFtTWFwKSkge1xuICAgICAgcGFyYW1NYXBba2V5XSA9IFt2YWx1ZV07XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhcmFtTWFwW2tleV0ucHVzaCh2YWx1ZSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBwYXJhbU1hcDtcbn07XG4vKipcbiAqIHJldHVybnMgdGhlIGZpcnN0IHZhbHVlIGZvciBhIGdpdmVuIGNnaSBwYXJhbWV0ZXIgb3IgbnVsbCBpZiB0aGUgZ2l2ZW5cbiAqIHBhcmFtZXRlciBuYW1lIGRvZXMgbm90IGFwcGVhciBpbiB0aGUgcXVlcnkgc3RyaW5nLlxuICogSWYgdGhlIGdpdmVuIHBhcmFtZXRlciBuYW1lIGRvZXMgYXBwZWFyLCBidXQgaGFzIG5vICc8dHQ+PTwvdHQ+JyBmb2xsb3dpbmdcbiAqIGl0LCB0aGVuIHRoZSBlbXB0eSBzdHJpbmcgd2lsbCBiZSByZXR1cm5lZC5cbiAqIEByZXR1cm4ge3N0cmluZ3xudWxsfVxuICovXG5VUkkucHJvdG90eXBlLmdldFBhcmFtZXRlclZhbHVlID0gZnVuY3Rpb24gKHBhcmFtTmFtZVVuZXNjYXBlZCkge1xuICB0aGlzLmNoZWNrUGFyYW1ldGVyQ2FjaGVfKCk7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5wYXJhbUNhY2hlXy5sZW5ndGg7IGkgKz0gMikge1xuICAgIGlmIChwYXJhbU5hbWVVbmVzY2FwZWQgPT09IHRoaXMucGFyYW1DYWNoZV9baV0pIHtcbiAgICAgIHJldHVybiB0aGlzLnBhcmFtQ2FjaGVfW2kgKyAxXTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59O1xuXG5VUkkucHJvdG90eXBlLmdldEZyYWdtZW50ID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5mcmFnbWVudF8gJiYgZGVjb2RlVVJJQ29tcG9uZW50KHRoaXMuZnJhZ21lbnRfKTtcbn07XG5VUkkucHJvdG90eXBlLmdldFJhd0ZyYWdtZW50ID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5mcmFnbWVudF87XG59O1xuVVJJLnByb3RvdHlwZS5zZXRGcmFnbWVudCA9IGZ1bmN0aW9uIChuZXdGcmFnbWVudCkge1xuICB0aGlzLmZyYWdtZW50XyA9IG5ld0ZyYWdtZW50ID8gZW5jb2RlVVJJQ29tcG9uZW50KG5ld0ZyYWdtZW50KSA6IG51bGw7XG4gIHJldHVybiB0aGlzO1xufTtcblVSSS5wcm90b3R5cGUuc2V0UmF3RnJhZ21lbnQgPSBmdW5jdGlvbiAobmV3RnJhZ21lbnQpIHtcbiAgdGhpcy5mcmFnbWVudF8gPSBuZXdGcmFnbWVudCA/IG5ld0ZyYWdtZW50IDogbnVsbDtcbiAgcmV0dXJuIHRoaXM7XG59O1xuVVJJLnByb3RvdHlwZS5oYXNGcmFnbWVudCA9IGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIG51bGwgIT09IHRoaXMuZnJhZ21lbnRfO1xufTtcblxuZnVuY3Rpb24gbnVsbElmQWJzZW50KG1hdGNoUGFydCkge1xuICByZXR1cm4gKCdzdHJpbmcnID09IHR5cGVvZiBtYXRjaFBhcnQpICYmIChtYXRjaFBhcnQubGVuZ3RoID4gMClcbiAgICAgICAgID8gbWF0Y2hQYXJ0XG4gICAgICAgICA6IG51bGw7XG59XG5cblxuXG5cbi8qKlxuICogYSByZWd1bGFyIGV4cHJlc3Npb24gZm9yIGJyZWFraW5nIGEgVVJJIGludG8gaXRzIGNvbXBvbmVudCBwYXJ0cy5cbiAqXG4gKiA8cD5odHRwOi8vd3d3LmdiaXYuY29tL3Byb3RvY29scy91cmkvcmZjL3JmYzM5ODYuaHRtbCNSRkMyMjM0IHNheXNcbiAqIEFzIHRoZSBcImZpcnN0LW1hdGNoLXdpbnNcIiBhbGdvcml0aG0gaXMgaWRlbnRpY2FsIHRvIHRoZSBcImdyZWVkeVwiXG4gKiBkaXNhbWJpZ3VhdGlvbiBtZXRob2QgdXNlZCBieSBQT1NJWCByZWd1bGFyIGV4cHJlc3Npb25zLCBpdCBpcyBuYXR1cmFsIGFuZFxuICogY29tbW9ucGxhY2UgdG8gdXNlIGEgcmVndWxhciBleHByZXNzaW9uIGZvciBwYXJzaW5nIHRoZSBwb3RlbnRpYWwgZml2ZVxuICogY29tcG9uZW50cyBvZiBhIFVSSSByZWZlcmVuY2UuXG4gKlxuICogPHA+VGhlIGZvbGxvd2luZyBsaW5lIGlzIHRoZSByZWd1bGFyIGV4cHJlc3Npb24gZm9yIGJyZWFraW5nLWRvd24gYVxuICogd2VsbC1mb3JtZWQgVVJJIHJlZmVyZW5jZSBpbnRvIGl0cyBjb21wb25lbnRzLlxuICpcbiAqIDxwcmU+XG4gKiBeKChbXjovPyNdKyk6KT8oLy8oW14vPyNdKikpPyhbXj8jXSopKFxcPyhbXiNdKikpPygjKC4qKSk/XG4gKiAgMTIgICAgICAgICAgICAzICA0ICAgICAgICAgIDUgICAgICAgNiAgNyAgICAgICAgOCA5XG4gKiA8L3ByZT5cbiAqXG4gKiA8cD5UaGUgbnVtYmVycyBpbiB0aGUgc2Vjb25kIGxpbmUgYWJvdmUgYXJlIG9ubHkgdG8gYXNzaXN0IHJlYWRhYmlsaXR5OyB0aGV5XG4gKiBpbmRpY2F0ZSB0aGUgcmVmZXJlbmNlIHBvaW50cyBmb3IgZWFjaCBzdWJleHByZXNzaW9uIChpLmUuLCBlYWNoIHBhaXJlZFxuICogcGFyZW50aGVzaXMpLiBXZSByZWZlciB0byB0aGUgdmFsdWUgbWF0Y2hlZCBmb3Igc3ViZXhwcmVzc2lvbiA8bj4gYXMgJDxuPi5cbiAqIEZvciBleGFtcGxlLCBtYXRjaGluZyB0aGUgYWJvdmUgZXhwcmVzc2lvbiB0b1xuICogPHByZT5cbiAqICAgICBodHRwOi8vd3d3Lmljcy51Y2kuZWR1L3B1Yi9pZXRmL3VyaS8jUmVsYXRlZFxuICogPC9wcmU+XG4gKiByZXN1bHRzIGluIHRoZSBmb2xsb3dpbmcgc3ViZXhwcmVzc2lvbiBtYXRjaGVzOlxuICogPHByZT5cbiAqICAgICQxID0gaHR0cDpcbiAqICAgICQyID0gaHR0cFxuICogICAgJDMgPSAvL3d3dy5pY3MudWNpLmVkdVxuICogICAgJDQgPSB3d3cuaWNzLnVjaS5lZHVcbiAqICAgICQ1ID0gL3B1Yi9pZXRmL3VyaS9cbiAqICAgICQ2ID0gPHVuZGVmaW5lZD5cbiAqICAgICQ3ID0gPHVuZGVmaW5lZD5cbiAqICAgICQ4ID0gI1JlbGF0ZWRcbiAqICAgICQ5ID0gUmVsYXRlZFxuICogPC9wcmU+XG4gKiB3aGVyZSA8dW5kZWZpbmVkPiBpbmRpY2F0ZXMgdGhhdCB0aGUgY29tcG9uZW50IGlzIG5vdCBwcmVzZW50LCBhcyBpcyB0aGVcbiAqIGNhc2UgZm9yIHRoZSBxdWVyeSBjb21wb25lbnQgaW4gdGhlIGFib3ZlIGV4YW1wbGUuIFRoZXJlZm9yZSwgd2UgY2FuXG4gKiBkZXRlcm1pbmUgdGhlIHZhbHVlIG9mIHRoZSBmaXZlIGNvbXBvbmVudHMgYXNcbiAqIDxwcmU+XG4gKiAgICBzY2hlbWUgICAgPSAkMlxuICogICAgYXV0aG9yaXR5ID0gJDRcbiAqICAgIHBhdGggICAgICA9ICQ1XG4gKiAgICBxdWVyeSAgICAgPSAkN1xuICogICAgZnJhZ21lbnQgID0gJDlcbiAqIDwvcHJlPlxuICpcbiAqIDxwPm1zYW11ZWw6IEkgaGF2ZSBtb2RpZmllZCB0aGUgcmVndWxhciBleHByZXNzaW9uIHNsaWdodGx5IHRvIGV4cG9zZSB0aGVcbiAqIGNyZWRlbnRpYWxzLCBkb21haW4sIGFuZCBwb3J0IHNlcGFyYXRlbHkgZnJvbSB0aGUgYXV0aG9yaXR5LlxuICogVGhlIG1vZGlmaWVkIHZlcnNpb24geWllbGRzXG4gKiA8cHJlPlxuICogICAgJDEgPSBodHRwICAgICAgICAgICAgICBzY2hlbWVcbiAqICAgICQyID0gPHVuZGVmaW5lZD4gICAgICAgY3JlZGVudGlhbHMgLVxcXG4gKiAgICAkMyA9IHd3dy5pY3MudWNpLmVkdSAgIGRvbWFpbiAgICAgICB8IGF1dGhvcml0eVxuICogICAgJDQgPSA8dW5kZWZpbmVkPiAgICAgICBwb3J0ICAgICAgICAtL1xuICogICAgJDUgPSAvcHViL2lldGYvdXJpLyAgICBwYXRoXG4gKiAgICAkNiA9IDx1bmRlZmluZWQ+ICAgICAgIHF1ZXJ5IHdpdGhvdXQgP1xuICogICAgJDcgPSBSZWxhdGVkICAgICAgICAgICBmcmFnbWVudCB3aXRob3V0ICNcbiAqIDwvcHJlPlxuICovXG52YXIgVVJJX1JFXyA9IG5ldyBSZWdFeHAoXG4gICAgICBcIl5cIiArXG4gICAgICBcIig/OlwiICtcbiAgICAgICAgXCIoW146Lz8jXSspXCIgKyAgICAgICAgIC8vIHNjaGVtZVxuICAgICAgXCI6KT9cIiArXG4gICAgICBcIig/Oi8vXCIgK1xuICAgICAgICBcIig/OihbXi8/I10qKUApP1wiICsgICAgLy8gY3JlZGVudGlhbHNcbiAgICAgICAgXCIoW14vPyM6QF0qKVwiICsgICAgICAgIC8vIGRvbWFpblxuICAgICAgICBcIig/OjooWzAtOV0rKSk/XCIgKyAgICAgLy8gcG9ydFxuICAgICAgXCIpP1wiICtcbiAgICAgIFwiKFtePyNdKyk/XCIgKyAgICAgICAgICAgIC8vIHBhdGhcbiAgICAgIFwiKD86XFxcXD8oW14jXSopKT9cIiArICAgICAgLy8gcXVlcnlcbiAgICAgIFwiKD86IyguKikpP1wiICsgICAgICAgICAgIC8vIGZyYWdtZW50XG4gICAgICBcIiRcIlxuICAgICAgKTtcblxudmFyIFVSSV9ESVNBTExPV0VEX0lOX1NDSEVNRV9PUl9DUkVERU5USUFMU18gPSAvWyNcXC9cXD9AXS9nO1xudmFyIFVSSV9ESVNBTExPV0VEX0lOX1BBVEhfID0gL1tcXCNcXD9dL2c7XG5cblVSSS5wYXJzZSA9IHBhcnNlO1xuVVJJLmNyZWF0ZSA9IGNyZWF0ZTtcblVSSS5yZXNvbHZlID0gcmVzb2x2ZTtcblVSSS5jb2xsYXBzZV9kb3RzID0gY29sbGFwc2VfZG90czsgIC8vIFZpc2libGUgZm9yIHRlc3RpbmcuXG5cbi8vIGxpZ2h0d2VpZ2h0IHN0cmluZy1iYXNlZCBhcGkgZm9yIGxvYWRNb2R1bGVNYWtlclxuVVJJLnV0aWxzID0ge1xuICBtaW1lVHlwZU9mOiBmdW5jdGlvbiAodXJpKSB7XG4gICAgdmFyIHVyaU9iaiA9IHBhcnNlKHVyaSk7XG4gICAgaWYgKC9cXC5odG1sJC8udGVzdCh1cmlPYmouZ2V0UGF0aCgpKSkge1xuICAgICAgcmV0dXJuICd0ZXh0L2h0bWwnO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gJ2FwcGxpY2F0aW9uL2phdmFzY3JpcHQnO1xuICAgIH1cbiAgfSxcbiAgcmVzb2x2ZTogZnVuY3Rpb24gKGJhc2UsIHVyaSkge1xuICAgIGlmIChiYXNlKSB7XG4gICAgICByZXR1cm4gcmVzb2x2ZShwYXJzZShiYXNlKSwgcGFyc2UodXJpKSkudG9TdHJpbmcoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuICcnICsgdXJpO1xuICAgIH1cbiAgfVxufTtcblxuXG5yZXR1cm4gVVJJO1xufSkoKTtcblxuLy8gQ29weXJpZ2h0IEdvb2dsZSBJbmMuXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2VuY2UgVmVyc2lvbiAyLjBcbi8vIEF1dG9nZW5lcmF0ZWQgYXQgTW9uIEZlYiAyNSAxMzowNTo0MiBFU1QgMjAxM1xuLy8gQG92ZXJyaWRlcyB3aW5kb3dcbi8vIEBwcm92aWRlcyBodG1sNFxudmFyIGh0bWw0ID0ge307XG5odG1sNC5hdHlwZSA9IHtcbiAgJ05PTkUnOiAwLFxuICAnVVJJJzogMSxcbiAgJ1VSSV9GUkFHTUVOVCc6IDExLFxuICAnU0NSSVBUJzogMixcbiAgJ1NUWUxFJzogMyxcbiAgJ0hUTUwnOiAxMixcbiAgJ0lEJzogNCxcbiAgJ0lEUkVGJzogNSxcbiAgJ0lEUkVGUyc6IDYsXG4gICdHTE9CQUxfTkFNRSc6IDcsXG4gICdMT0NBTF9OQU1FJzogOCxcbiAgJ0NMQVNTRVMnOiA5LFxuICAnRlJBTUVfVEFSR0VUJzogMTAsXG4gICdNRURJQV9RVUVSWSc6IDEzXG59O1xuaHRtbDRbICdhdHlwZScgXSA9IGh0bWw0LmF0eXBlO1xuaHRtbDQuQVRUUklCUyA9IHtcbiAgJyo6OmNsYXNzJzogOSxcbiAgJyo6OmRpcic6IDAsXG4gICcqOjpkcmFnZ2FibGUnOiAwLFxuICAnKjo6aGlkZGVuJzogMCxcbiAgJyo6OmlkJzogNCxcbiAgJyo6OmluZXJ0JzogMCxcbiAgJyo6Oml0ZW1wcm9wJzogMCxcbiAgJyo6Oml0ZW1yZWYnOiA2LFxuICAnKjo6aXRlbXNjb3BlJzogMCxcbiAgJyo6OmxhbmcnOiAwLFxuICAnKjo6b25ibHVyJzogMixcbiAgJyo6Om9uY2hhbmdlJzogMixcbiAgJyo6Om9uY2xpY2snOiAyLFxuICAnKjo6b25kYmxjbGljayc6IDIsXG4gICcqOjpvbmZvY3VzJzogMixcbiAgJyo6Om9ua2V5ZG93bic6IDIsXG4gICcqOjpvbmtleXByZXNzJzogMixcbiAgJyo6Om9ua2V5dXAnOiAyLFxuICAnKjo6b25sb2FkJzogMixcbiAgJyo6Om9ubW91c2Vkb3duJzogMixcbiAgJyo6Om9ubW91c2Vtb3ZlJzogMixcbiAgJyo6Om9ubW91c2VvdXQnOiAyLFxuICAnKjo6b25tb3VzZW92ZXInOiAyLFxuICAnKjo6b25tb3VzZXVwJzogMixcbiAgJyo6Om9ucmVzZXQnOiAyLFxuICAnKjo6b25zY3JvbGwnOiAyLFxuICAnKjo6b25zZWxlY3QnOiAyLFxuICAnKjo6b25zdWJtaXQnOiAyLFxuICAnKjo6b251bmxvYWQnOiAyLFxuICAnKjo6c3BlbGxjaGVjayc6IDAsXG4gICcqOjpzdHlsZSc6IDMsXG4gICcqOjp0aXRsZSc6IDAsXG4gICcqOjp0cmFuc2xhdGUnOiAwLFxuICAnYTo6YWNjZXNza2V5JzogMCxcbiAgJ2E6OmNvb3Jkcyc6IDAsXG4gICdhOjpocmVmJzogMSxcbiAgJ2E6OmhyZWZsYW5nJzogMCxcbiAgJ2E6Om5hbWUnOiA3LFxuICAnYTo6b25ibHVyJzogMixcbiAgJ2E6Om9uZm9jdXMnOiAyLFxuICAnYTo6c2hhcGUnOiAwLFxuICAnYTo6dGFiaW5kZXgnOiAwLFxuICAnYTo6dGFyZ2V0JzogMTAsXG4gICdhOjp0eXBlJzogMCxcbiAgJ2FyZWE6OmFjY2Vzc2tleSc6IDAsXG4gICdhcmVhOjphbHQnOiAwLFxuICAnYXJlYTo6Y29vcmRzJzogMCxcbiAgJ2FyZWE6OmhyZWYnOiAxLFxuICAnYXJlYTo6bm9ocmVmJzogMCxcbiAgJ2FyZWE6Om9uYmx1cic6IDIsXG4gICdhcmVhOjpvbmZvY3VzJzogMixcbiAgJ2FyZWE6OnNoYXBlJzogMCxcbiAgJ2FyZWE6OnRhYmluZGV4JzogMCxcbiAgJ2FyZWE6OnRhcmdldCc6IDEwLFxuICAnYXVkaW86OmNvbnRyb2xzJzogMCxcbiAgJ2F1ZGlvOjpsb29wJzogMCxcbiAgJ2F1ZGlvOjptZWRpYWdyb3VwJzogNSxcbiAgJ2F1ZGlvOjptdXRlZCc6IDAsXG4gICdhdWRpbzo6cHJlbG9hZCc6IDAsXG4gICdiZG86OmRpcic6IDAsXG4gICdibG9ja3F1b3RlOjpjaXRlJzogMSxcbiAgJ2JyOjpjbGVhcic6IDAsXG4gICdidXR0b246OmFjY2Vzc2tleSc6IDAsXG4gICdidXR0b246OmRpc2FibGVkJzogMCxcbiAgJ2J1dHRvbjo6bmFtZSc6IDgsXG4gICdidXR0b246Om9uYmx1cic6IDIsXG4gICdidXR0b246Om9uZm9jdXMnOiAyLFxuICAnYnV0dG9uOjp0YWJpbmRleCc6IDAsXG4gICdidXR0b246OnR5cGUnOiAwLFxuICAnYnV0dG9uOjp2YWx1ZSc6IDAsXG4gICdjYW52YXM6OmhlaWdodCc6IDAsXG4gICdjYW52YXM6OndpZHRoJzogMCxcbiAgJ2NhcHRpb246OmFsaWduJzogMCxcbiAgJ2NvbDo6YWxpZ24nOiAwLFxuICAnY29sOjpjaGFyJzogMCxcbiAgJ2NvbDo6Y2hhcm9mZic6IDAsXG4gICdjb2w6OnNwYW4nOiAwLFxuICAnY29sOjp2YWxpZ24nOiAwLFxuICAnY29sOjp3aWR0aCc6IDAsXG4gICdjb2xncm91cDo6YWxpZ24nOiAwLFxuICAnY29sZ3JvdXA6OmNoYXInOiAwLFxuICAnY29sZ3JvdXA6OmNoYXJvZmYnOiAwLFxuICAnY29sZ3JvdXA6OnNwYW4nOiAwLFxuICAnY29sZ3JvdXA6OnZhbGlnbic6IDAsXG4gICdjb2xncm91cDo6d2lkdGgnOiAwLFxuICAnY29tbWFuZDo6Y2hlY2tlZCc6IDAsXG4gICdjb21tYW5kOjpjb21tYW5kJzogNSxcbiAgJ2NvbW1hbmQ6OmRpc2FibGVkJzogMCxcbiAgJ2NvbW1hbmQ6Omljb24nOiAxLFxuICAnY29tbWFuZDo6bGFiZWwnOiAwLFxuICAnY29tbWFuZDo6cmFkaW9ncm91cCc6IDAsXG4gICdjb21tYW5kOjp0eXBlJzogMCxcbiAgJ2RhdGE6OnZhbHVlJzogMCxcbiAgJ2RlbDo6Y2l0ZSc6IDEsXG4gICdkZWw6OmRhdGV0aW1lJzogMCxcbiAgJ2RldGFpbHM6Om9wZW4nOiAwLFxuICAnZGlyOjpjb21wYWN0JzogMCxcbiAgJ2Rpdjo6YWxpZ24nOiAwLFxuICAnZGw6OmNvbXBhY3QnOiAwLFxuICAnZmllbGRzZXQ6OmRpc2FibGVkJzogMCxcbiAgJ2ZvbnQ6OmNvbG9yJzogMCxcbiAgJ2ZvbnQ6OmZhY2UnOiAwLFxuICAnZm9udDo6c2l6ZSc6IDAsXG4gICdmb3JtOjphY2NlcHQnOiAwLFxuICAnZm9ybTo6YWN0aW9uJzogMSxcbiAgJ2Zvcm06OmF1dG9jb21wbGV0ZSc6IDAsXG4gICdmb3JtOjplbmN0eXBlJzogMCxcbiAgJ2Zvcm06Om1ldGhvZCc6IDAsXG4gICdmb3JtOjpuYW1lJzogNyxcbiAgJ2Zvcm06Om5vdmFsaWRhdGUnOiAwLFxuICAnZm9ybTo6b25yZXNldCc6IDIsXG4gICdmb3JtOjpvbnN1Ym1pdCc6IDIsXG4gICdmb3JtOjp0YXJnZXQnOiAxMCxcbiAgJ2gxOjphbGlnbic6IDAsXG4gICdoMjo6YWxpZ24nOiAwLFxuICAnaDM6OmFsaWduJzogMCxcbiAgJ2g0OjphbGlnbic6IDAsXG4gICdoNTo6YWxpZ24nOiAwLFxuICAnaDY6OmFsaWduJzogMCxcbiAgJ2hyOjphbGlnbic6IDAsXG4gICdocjo6bm9zaGFkZSc6IDAsXG4gICdocjo6c2l6ZSc6IDAsXG4gICdocjo6d2lkdGgnOiAwLFxuICAnaWZyYW1lOjphbGlnbic6IDAsXG4gICdpZnJhbWU6OmZyYW1lYm9yZGVyJzogMCxcbiAgJ2lmcmFtZTo6aGVpZ2h0JzogMCxcbiAgJ2lmcmFtZTo6bWFyZ2luaGVpZ2h0JzogMCxcbiAgJ2lmcmFtZTo6bWFyZ2lud2lkdGgnOiAwLFxuICAnaWZyYW1lOjp3aWR0aCc6IDAsXG4gICdpbWc6OmFsaWduJzogMCxcbiAgJ2ltZzo6YWx0JzogMCxcbiAgJ2ltZzo6Ym9yZGVyJzogMCxcbiAgJ2ltZzo6aGVpZ2h0JzogMCxcbiAgJ2ltZzo6aHNwYWNlJzogMCxcbiAgJ2ltZzo6aXNtYXAnOiAwLFxuICAnaW1nOjpuYW1lJzogNyxcbiAgJ2ltZzo6c3JjJzogMSxcbiAgJ2ltZzo6dXNlbWFwJzogMTEsXG4gICdpbWc6OnZzcGFjZSc6IDAsXG4gICdpbWc6OndpZHRoJzogMCxcbiAgJ2lucHV0OjphY2NlcHQnOiAwLFxuICAnaW5wdXQ6OmFjY2Vzc2tleSc6IDAsXG4gICdpbnB1dDo6YWxpZ24nOiAwLFxuICAnaW5wdXQ6OmFsdCc6IDAsXG4gICdpbnB1dDo6YXV0b2NvbXBsZXRlJzogMCxcbiAgJ2lucHV0OjpjaGVja2VkJzogMCxcbiAgJ2lucHV0OjpkaXNhYmxlZCc6IDAsXG4gICdpbnB1dDo6aW5wdXRtb2RlJzogMCxcbiAgJ2lucHV0Ojppc21hcCc6IDAsXG4gICdpbnB1dDo6bGlzdCc6IDUsXG4gICdpbnB1dDo6bWF4JzogMCxcbiAgJ2lucHV0OjptYXhsZW5ndGgnOiAwLFxuICAnaW5wdXQ6Om1pbic6IDAsXG4gICdpbnB1dDo6bXVsdGlwbGUnOiAwLFxuICAnaW5wdXQ6Om5hbWUnOiA4LFxuICAnaW5wdXQ6Om9uYmx1cic6IDIsXG4gICdpbnB1dDo6b25jaGFuZ2UnOiAyLFxuICAnaW5wdXQ6Om9uZm9jdXMnOiAyLFxuICAnaW5wdXQ6Om9uc2VsZWN0JzogMixcbiAgJ2lucHV0OjpwbGFjZWhvbGRlcic6IDAsXG4gICdpbnB1dDo6cmVhZG9ubHknOiAwLFxuICAnaW5wdXQ6OnJlcXVpcmVkJzogMCxcbiAgJ2lucHV0OjpzaXplJzogMCxcbiAgJ2lucHV0OjpzcmMnOiAxLFxuICAnaW5wdXQ6OnN0ZXAnOiAwLFxuICAnaW5wdXQ6OnRhYmluZGV4JzogMCxcbiAgJ2lucHV0Ojp0eXBlJzogMCxcbiAgJ2lucHV0Ojp1c2VtYXAnOiAxMSxcbiAgJ2lucHV0Ojp2YWx1ZSc6IDAsXG4gICdpbnM6OmNpdGUnOiAxLFxuICAnaW5zOjpkYXRldGltZSc6IDAsXG4gICdsYWJlbDo6YWNjZXNza2V5JzogMCxcbiAgJ2xhYmVsOjpmb3InOiA1LFxuICAnbGFiZWw6Om9uYmx1cic6IDIsXG4gICdsYWJlbDo6b25mb2N1cyc6IDIsXG4gICdsZWdlbmQ6OmFjY2Vzc2tleSc6IDAsXG4gICdsZWdlbmQ6OmFsaWduJzogMCxcbiAgJ2xpOjp0eXBlJzogMCxcbiAgJ2xpOjp2YWx1ZSc6IDAsXG4gICdtYXA6Om5hbWUnOiA3LFxuICAnbWVudTo6Y29tcGFjdCc6IDAsXG4gICdtZW51OjpsYWJlbCc6IDAsXG4gICdtZW51Ojp0eXBlJzogMCxcbiAgJ21ldGVyOjpoaWdoJzogMCxcbiAgJ21ldGVyOjpsb3cnOiAwLFxuICAnbWV0ZXI6Om1heCc6IDAsXG4gICdtZXRlcjo6bWluJzogMCxcbiAgJ21ldGVyOjp2YWx1ZSc6IDAsXG4gICdvbDo6Y29tcGFjdCc6IDAsXG4gICdvbDo6cmV2ZXJzZWQnOiAwLFxuICAnb2w6OnN0YXJ0JzogMCxcbiAgJ29sOjp0eXBlJzogMCxcbiAgJ29wdGdyb3VwOjpkaXNhYmxlZCc6IDAsXG4gICdvcHRncm91cDo6bGFiZWwnOiAwLFxuICAnb3B0aW9uOjpkaXNhYmxlZCc6IDAsXG4gICdvcHRpb246OmxhYmVsJzogMCxcbiAgJ29wdGlvbjo6c2VsZWN0ZWQnOiAwLFxuICAnb3B0aW9uOjp2YWx1ZSc6IDAsXG4gICdvdXRwdXQ6OmZvcic6IDYsXG4gICdvdXRwdXQ6Om5hbWUnOiA4LFxuICAncDo6YWxpZ24nOiAwLFxuICAncHJlOjp3aWR0aCc6IDAsXG4gICdwcm9ncmVzczo6bWF4JzogMCxcbiAgJ3Byb2dyZXNzOjptaW4nOiAwLFxuICAncHJvZ3Jlc3M6OnZhbHVlJzogMCxcbiAgJ3E6OmNpdGUnOiAxLFxuICAnc2VsZWN0OjphdXRvY29tcGxldGUnOiAwLFxuICAnc2VsZWN0OjpkaXNhYmxlZCc6IDAsXG4gICdzZWxlY3Q6Om11bHRpcGxlJzogMCxcbiAgJ3NlbGVjdDo6bmFtZSc6IDgsXG4gICdzZWxlY3Q6Om9uYmx1cic6IDIsXG4gICdzZWxlY3Q6Om9uY2hhbmdlJzogMixcbiAgJ3NlbGVjdDo6b25mb2N1cyc6IDIsXG4gICdzZWxlY3Q6OnJlcXVpcmVkJzogMCxcbiAgJ3NlbGVjdDo6c2l6ZSc6IDAsXG4gICdzZWxlY3Q6OnRhYmluZGV4JzogMCxcbiAgJ3NvdXJjZTo6dHlwZSc6IDAsXG4gICd0YWJsZTo6YWxpZ24nOiAwLFxuICAndGFibGU6OmJnY29sb3InOiAwLFxuICAndGFibGU6OmJvcmRlcic6IDAsXG4gICd0YWJsZTo6Y2VsbHBhZGRpbmcnOiAwLFxuICAndGFibGU6OmNlbGxzcGFjaW5nJzogMCxcbiAgJ3RhYmxlOjpmcmFtZSc6IDAsXG4gICd0YWJsZTo6cnVsZXMnOiAwLFxuICAndGFibGU6OnN1bW1hcnknOiAwLFxuICAndGFibGU6OndpZHRoJzogMCxcbiAgJ3Rib2R5OjphbGlnbic6IDAsXG4gICd0Ym9keTo6Y2hhcic6IDAsXG4gICd0Ym9keTo6Y2hhcm9mZic6IDAsXG4gICd0Ym9keTo6dmFsaWduJzogMCxcbiAgJ3RkOjphYmJyJzogMCxcbiAgJ3RkOjphbGlnbic6IDAsXG4gICd0ZDo6YXhpcyc6IDAsXG4gICd0ZDo6Ymdjb2xvcic6IDAsXG4gICd0ZDo6Y2hhcic6IDAsXG4gICd0ZDo6Y2hhcm9mZic6IDAsXG4gICd0ZDo6Y29sc3Bhbic6IDAsXG4gICd0ZDo6aGVhZGVycyc6IDYsXG4gICd0ZDo6aGVpZ2h0JzogMCxcbiAgJ3RkOjpub3dyYXAnOiAwLFxuICAndGQ6OnJvd3NwYW4nOiAwLFxuICAndGQ6OnNjb3BlJzogMCxcbiAgJ3RkOjp2YWxpZ24nOiAwLFxuICAndGQ6OndpZHRoJzogMCxcbiAgJ3RleHRhcmVhOjphY2Nlc3NrZXknOiAwLFxuICAndGV4dGFyZWE6OmF1dG9jb21wbGV0ZSc6IDAsXG4gICd0ZXh0YXJlYTo6Y29scyc6IDAsXG4gICd0ZXh0YXJlYTo6ZGlzYWJsZWQnOiAwLFxuICAndGV4dGFyZWE6OmlucHV0bW9kZSc6IDAsXG4gICd0ZXh0YXJlYTo6bmFtZSc6IDgsXG4gICd0ZXh0YXJlYTo6b25ibHVyJzogMixcbiAgJ3RleHRhcmVhOjpvbmNoYW5nZSc6IDIsXG4gICd0ZXh0YXJlYTo6b25mb2N1cyc6IDIsXG4gICd0ZXh0YXJlYTo6b25zZWxlY3QnOiAyLFxuICAndGV4dGFyZWE6OnBsYWNlaG9sZGVyJzogMCxcbiAgJ3RleHRhcmVhOjpyZWFkb25seSc6IDAsXG4gICd0ZXh0YXJlYTo6cmVxdWlyZWQnOiAwLFxuICAndGV4dGFyZWE6OnJvd3MnOiAwLFxuICAndGV4dGFyZWE6OnRhYmluZGV4JzogMCxcbiAgJ3RleHRhcmVhOjp3cmFwJzogMCxcbiAgJ3Rmb290OjphbGlnbic6IDAsXG4gICd0Zm9vdDo6Y2hhcic6IDAsXG4gICd0Zm9vdDo6Y2hhcm9mZic6IDAsXG4gICd0Zm9vdDo6dmFsaWduJzogMCxcbiAgJ3RoOjphYmJyJzogMCxcbiAgJ3RoOjphbGlnbic6IDAsXG4gICd0aDo6YXhpcyc6IDAsXG4gICd0aDo6Ymdjb2xvcic6IDAsXG4gICd0aDo6Y2hhcic6IDAsXG4gICd0aDo6Y2hhcm9mZic6IDAsXG4gICd0aDo6Y29sc3Bhbic6IDAsXG4gICd0aDo6aGVhZGVycyc6IDYsXG4gICd0aDo6aGVpZ2h0JzogMCxcbiAgJ3RoOjpub3dyYXAnOiAwLFxuICAndGg6OnJvd3NwYW4nOiAwLFxuICAndGg6OnNjb3BlJzogMCxcbiAgJ3RoOjp2YWxpZ24nOiAwLFxuICAndGg6OndpZHRoJzogMCxcbiAgJ3RoZWFkOjphbGlnbic6IDAsXG4gICd0aGVhZDo6Y2hhcic6IDAsXG4gICd0aGVhZDo6Y2hhcm9mZic6IDAsXG4gICd0aGVhZDo6dmFsaWduJzogMCxcbiAgJ3RyOjphbGlnbic6IDAsXG4gICd0cjo6Ymdjb2xvcic6IDAsXG4gICd0cjo6Y2hhcic6IDAsXG4gICd0cjo6Y2hhcm9mZic6IDAsXG4gICd0cjo6dmFsaWduJzogMCxcbiAgJ3RyYWNrOjpkZWZhdWx0JzogMCxcbiAgJ3RyYWNrOjpraW5kJzogMCxcbiAgJ3RyYWNrOjpsYWJlbCc6IDAsXG4gICd0cmFjazo6c3JjbGFuZyc6IDAsXG4gICd1bDo6Y29tcGFjdCc6IDAsXG4gICd1bDo6dHlwZSc6IDAsXG4gICd2aWRlbzo6Y29udHJvbHMnOiAwLFxuICAndmlkZW86OmhlaWdodCc6IDAsXG4gICd2aWRlbzo6bG9vcCc6IDAsXG4gICd2aWRlbzo6bWVkaWFncm91cCc6IDUsXG4gICd2aWRlbzo6bXV0ZWQnOiAwLFxuICAndmlkZW86OnBvc3Rlcic6IDEsXG4gICd2aWRlbzo6cHJlbG9hZCc6IDAsXG4gICd2aWRlbzo6d2lkdGgnOiAwXG59O1xuaHRtbDRbICdBVFRSSUJTJyBdID0gaHRtbDQuQVRUUklCUztcbmh0bWw0LmVmbGFncyA9IHtcbiAgJ09QVElPTkFMX0VORFRBRyc6IDEsXG4gICdFTVBUWSc6IDIsXG4gICdDREFUQSc6IDQsXG4gICdSQ0RBVEEnOiA4LFxuICAnVU5TQUZFJzogMTYsXG4gICdGT0xEQUJMRSc6IDMyLFxuICAnU0NSSVBUJzogNjQsXG4gICdTVFlMRSc6IDEyOCxcbiAgJ1ZJUlRVQUxJWkVEJzogMjU2XG59O1xuaHRtbDRbICdlZmxhZ3MnIF0gPSBodG1sNC5lZmxhZ3M7XG4vLyB0aGVzZSBhcmUgYml0bWFza3Mgb2YgdGhlIGVmbGFncyBhYm92ZS5cbmh0bWw0LkVMRU1FTlRTID0ge1xuICAnYSc6IDAsXG4gICdhYmJyJzogMCxcbiAgJ2Fjcm9ueW0nOiAwLFxuICAnYWRkcmVzcyc6IDAsXG4gICdhcHBsZXQnOiAyNzIsXG4gICdhcmVhJzogMixcbiAgJ2FydGljbGUnOiAwLFxuICAnYXNpZGUnOiAwLFxuICAnYXVkaW8nOiAwLFxuICAnYic6IDAsXG4gICdiYXNlJzogMjc0LFxuICAnYmFzZWZvbnQnOiAyNzQsXG4gICdiZGknOiAwLFxuICAnYmRvJzogMCxcbiAgJ2JpZyc6IDAsXG4gICdibG9ja3F1b3RlJzogMCxcbiAgJ2JvZHknOiAzMDUsXG4gICdicic6IDIsXG4gICdidXR0b24nOiAwLFxuICAnY2FudmFzJzogMCxcbiAgJ2NhcHRpb24nOiAwLFxuICAnY2VudGVyJzogMCxcbiAgJ2NpdGUnOiAwLFxuICAnY29kZSc6IDAsXG4gICdjb2wnOiAyLFxuICAnY29sZ3JvdXAnOiAxLFxuICAnY29tbWFuZCc6IDIsXG4gICdkYXRhJzogMCxcbiAgJ2RhdGFsaXN0JzogMCxcbiAgJ2RkJzogMSxcbiAgJ2RlbCc6IDAsXG4gICdkZXRhaWxzJzogMCxcbiAgJ2Rmbic6IDAsXG4gICdkaWFsb2cnOiAyNzIsXG4gICdkaXInOiAwLFxuICAnZGl2JzogMCxcbiAgJ2RsJzogMCxcbiAgJ2R0JzogMSxcbiAgJ2VtJzogMCxcbiAgJ2ZpZWxkc2V0JzogMCxcbiAgJ2ZpZ2NhcHRpb24nOiAwLFxuICAnZmlndXJlJzogMCxcbiAgJ2ZvbnQnOiAwLFxuICAnZm9vdGVyJzogMCxcbiAgJ2Zvcm0nOiAwLFxuICAnZnJhbWUnOiAyNzQsXG4gICdmcmFtZXNldCc6IDI3MixcbiAgJ2gxJzogMCxcbiAgJ2gyJzogMCxcbiAgJ2gzJzogMCxcbiAgJ2g0JzogMCxcbiAgJ2g1JzogMCxcbiAgJ2g2JzogMCxcbiAgJ2hlYWQnOiAzMDUsXG4gICdoZWFkZXInOiAwLFxuICAnaGdyb3VwJzogMCxcbiAgJ2hyJzogMixcbiAgJ2h0bWwnOiAzMDUsXG4gICdpJzogMCxcbiAgJ2lmcmFtZSc6IDE2LFxuICAnaW1nJzogMixcbiAgJ2lucHV0JzogMixcbiAgJ2lucyc6IDAsXG4gICdpc2luZGV4JzogMjc0LFxuICAna2JkJzogMCxcbiAgJ2tleWdlbic6IDI3NCxcbiAgJ2xhYmVsJzogMCxcbiAgJ2xlZ2VuZCc6IDAsXG4gICdsaSc6IDEsXG4gICdsaW5rJzogMjc0LFxuICAnbWFwJzogMCxcbiAgJ21hcmsnOiAwLFxuICAnbWVudSc6IDAsXG4gICdtZXRhJzogMjc0LFxuICAnbWV0ZXInOiAwLFxuICAnbmF2JzogMCxcbiAgJ25vYnInOiAwLFxuICAnbm9lbWJlZCc6IDI3NixcbiAgJ25vZnJhbWVzJzogMjc2LFxuICAnbm9zY3JpcHQnOiAyNzYsXG4gICdvYmplY3QnOiAyNzIsXG4gICdvbCc6IDAsXG4gICdvcHRncm91cCc6IDAsXG4gICdvcHRpb24nOiAxLFxuICAnb3V0cHV0JzogMCxcbiAgJ3AnOiAxLFxuICAncGFyYW0nOiAyNzQsXG4gICdwcmUnOiAwLFxuICAncHJvZ3Jlc3MnOiAwLFxuICAncSc6IDAsXG4gICdzJzogMCxcbiAgJ3NhbXAnOiAwLFxuICAnc2NyaXB0JzogODQsXG4gICdzZWN0aW9uJzogMCxcbiAgJ3NlbGVjdCc6IDAsXG4gICdzbWFsbCc6IDAsXG4gICdzb3VyY2UnOiAyLFxuICAnc3Bhbic6IDAsXG4gICdzdHJpa2UnOiAwLFxuICAnc3Ryb25nJzogMCxcbiAgJ3N0eWxlJzogMTQ4LFxuICAnc3ViJzogMCxcbiAgJ3N1bW1hcnknOiAwLFxuICAnc3VwJzogMCxcbiAgJ3RhYmxlJzogMCxcbiAgJ3Rib2R5JzogMSxcbiAgJ3RkJzogMSxcbiAgJ3RleHRhcmVhJzogOCxcbiAgJ3Rmb290JzogMSxcbiAgJ3RoJzogMSxcbiAgJ3RoZWFkJzogMSxcbiAgJ3RpbWUnOiAwLFxuICAndGl0bGUnOiAyODAsXG4gICd0cic6IDEsXG4gICd0cmFjayc6IDIsXG4gICd0dCc6IDAsXG4gICd1JzogMCxcbiAgJ3VsJzogMCxcbiAgJ3Zhcic6IDAsXG4gICd2aWRlbyc6IDAsXG4gICd3YnInOiAyXG59O1xuaHRtbDRbICdFTEVNRU5UUycgXSA9IGh0bWw0LkVMRU1FTlRTO1xuaHRtbDQuRUxFTUVOVF9ET01fSU5URVJGQUNFUyA9IHtcbiAgJ2EnOiAnSFRNTEFuY2hvckVsZW1lbnQnLFxuICAnYWJicic6ICdIVE1MRWxlbWVudCcsXG4gICdhY3JvbnltJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2FkZHJlc3MnOiAnSFRNTEVsZW1lbnQnLFxuICAnYXBwbGV0JzogJ0hUTUxBcHBsZXRFbGVtZW50JyxcbiAgJ2FyZWEnOiAnSFRNTEFyZWFFbGVtZW50JyxcbiAgJ2FydGljbGUnOiAnSFRNTEVsZW1lbnQnLFxuICAnYXNpZGUnOiAnSFRNTEVsZW1lbnQnLFxuICAnYXVkaW8nOiAnSFRNTEF1ZGlvRWxlbWVudCcsXG4gICdiJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2Jhc2UnOiAnSFRNTEJhc2VFbGVtZW50JyxcbiAgJ2Jhc2Vmb250JzogJ0hUTUxCYXNlRm9udEVsZW1lbnQnLFxuICAnYmRpJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2Jkbyc6ICdIVE1MRWxlbWVudCcsXG4gICdiaWcnOiAnSFRNTEVsZW1lbnQnLFxuICAnYmxvY2txdW90ZSc6ICdIVE1MUXVvdGVFbGVtZW50JyxcbiAgJ2JvZHknOiAnSFRNTEJvZHlFbGVtZW50JyxcbiAgJ2JyJzogJ0hUTUxCUkVsZW1lbnQnLFxuICAnYnV0dG9uJzogJ0hUTUxCdXR0b25FbGVtZW50JyxcbiAgJ2NhbnZhcyc6ICdIVE1MQ2FudmFzRWxlbWVudCcsXG4gICdjYXB0aW9uJzogJ0hUTUxUYWJsZUNhcHRpb25FbGVtZW50JyxcbiAgJ2NlbnRlcic6ICdIVE1MRWxlbWVudCcsXG4gICdjaXRlJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2NvZGUnOiAnSFRNTEVsZW1lbnQnLFxuICAnY29sJzogJ0hUTUxUYWJsZUNvbEVsZW1lbnQnLFxuICAnY29sZ3JvdXAnOiAnSFRNTFRhYmxlQ29sRWxlbWVudCcsXG4gICdjb21tYW5kJzogJ0hUTUxDb21tYW5kRWxlbWVudCcsXG4gICdkYXRhJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2RhdGFsaXN0JzogJ0hUTUxEYXRhTGlzdEVsZW1lbnQnLFxuICAnZGQnOiAnSFRNTEVsZW1lbnQnLFxuICAnZGVsJzogJ0hUTUxNb2RFbGVtZW50JyxcbiAgJ2RldGFpbHMnOiAnSFRNTERldGFpbHNFbGVtZW50JyxcbiAgJ2Rmbic6ICdIVE1MRWxlbWVudCcsXG4gICdkaWFsb2cnOiAnSFRNTERpYWxvZ0VsZW1lbnQnLFxuICAnZGlyJzogJ0hUTUxEaXJlY3RvcnlFbGVtZW50JyxcbiAgJ2Rpdic6ICdIVE1MRGl2RWxlbWVudCcsXG4gICdkbCc6ICdIVE1MRExpc3RFbGVtZW50JyxcbiAgJ2R0JzogJ0hUTUxFbGVtZW50JyxcbiAgJ2VtJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2ZpZWxkc2V0JzogJ0hUTUxGaWVsZFNldEVsZW1lbnQnLFxuICAnZmlnY2FwdGlvbic6ICdIVE1MRWxlbWVudCcsXG4gICdmaWd1cmUnOiAnSFRNTEVsZW1lbnQnLFxuICAnZm9udCc6ICdIVE1MRm9udEVsZW1lbnQnLFxuICAnZm9vdGVyJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2Zvcm0nOiAnSFRNTEZvcm1FbGVtZW50JyxcbiAgJ2ZyYW1lJzogJ0hUTUxGcmFtZUVsZW1lbnQnLFxuICAnZnJhbWVzZXQnOiAnSFRNTEZyYW1lU2V0RWxlbWVudCcsXG4gICdoMSc6ICdIVE1MSGVhZGluZ0VsZW1lbnQnLFxuICAnaDInOiAnSFRNTEhlYWRpbmdFbGVtZW50JyxcbiAgJ2gzJzogJ0hUTUxIZWFkaW5nRWxlbWVudCcsXG4gICdoNCc6ICdIVE1MSGVhZGluZ0VsZW1lbnQnLFxuICAnaDUnOiAnSFRNTEhlYWRpbmdFbGVtZW50JyxcbiAgJ2g2JzogJ0hUTUxIZWFkaW5nRWxlbWVudCcsXG4gICdoZWFkJzogJ0hUTUxIZWFkRWxlbWVudCcsXG4gICdoZWFkZXInOiAnSFRNTEVsZW1lbnQnLFxuICAnaGdyb3VwJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2hyJzogJ0hUTUxIUkVsZW1lbnQnLFxuICAnaHRtbCc6ICdIVE1MSHRtbEVsZW1lbnQnLFxuICAnaSc6ICdIVE1MRWxlbWVudCcsXG4gICdpZnJhbWUnOiAnSFRNTElGcmFtZUVsZW1lbnQnLFxuICAnaW1nJzogJ0hUTUxJbWFnZUVsZW1lbnQnLFxuICAnaW5wdXQnOiAnSFRNTElucHV0RWxlbWVudCcsXG4gICdpbnMnOiAnSFRNTE1vZEVsZW1lbnQnLFxuICAnaXNpbmRleCc6ICdIVE1MVW5rbm93bkVsZW1lbnQnLFxuICAna2JkJzogJ0hUTUxFbGVtZW50JyxcbiAgJ2tleWdlbic6ICdIVE1MS2V5Z2VuRWxlbWVudCcsXG4gICdsYWJlbCc6ICdIVE1MTGFiZWxFbGVtZW50JyxcbiAgJ2xlZ2VuZCc6ICdIVE1MTGVnZW5kRWxlbWVudCcsXG4gICdsaSc6ICdIVE1MTElFbGVtZW50JyxcbiAgJ2xpbmsnOiAnSFRNTExpbmtFbGVtZW50JyxcbiAgJ21hcCc6ICdIVE1MTWFwRWxlbWVudCcsXG4gICdtYXJrJzogJ0hUTUxFbGVtZW50JyxcbiAgJ21lbnUnOiAnSFRNTE1lbnVFbGVtZW50JyxcbiAgJ21ldGEnOiAnSFRNTE1ldGFFbGVtZW50JyxcbiAgJ21ldGVyJzogJ0hUTUxNZXRlckVsZW1lbnQnLFxuICAnbmF2JzogJ0hUTUxFbGVtZW50JyxcbiAgJ25vYnInOiAnSFRNTEVsZW1lbnQnLFxuICAnbm9lbWJlZCc6ICdIVE1MRWxlbWVudCcsXG4gICdub2ZyYW1lcyc6ICdIVE1MRWxlbWVudCcsXG4gICdub3NjcmlwdCc6ICdIVE1MRWxlbWVudCcsXG4gICdvYmplY3QnOiAnSFRNTE9iamVjdEVsZW1lbnQnLFxuICAnb2wnOiAnSFRNTE9MaXN0RWxlbWVudCcsXG4gICdvcHRncm91cCc6ICdIVE1MT3B0R3JvdXBFbGVtZW50JyxcbiAgJ29wdGlvbic6ICdIVE1MT3B0aW9uRWxlbWVudCcsXG4gICdvdXRwdXQnOiAnSFRNTE91dHB1dEVsZW1lbnQnLFxuICAncCc6ICdIVE1MUGFyYWdyYXBoRWxlbWVudCcsXG4gICdwYXJhbSc6ICdIVE1MUGFyYW1FbGVtZW50JyxcbiAgJ3ByZSc6ICdIVE1MUHJlRWxlbWVudCcsXG4gICdwcm9ncmVzcyc6ICdIVE1MUHJvZ3Jlc3NFbGVtZW50JyxcbiAgJ3EnOiAnSFRNTFF1b3RlRWxlbWVudCcsXG4gICdzJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3NhbXAnOiAnSFRNTEVsZW1lbnQnLFxuICAnc2NyaXB0JzogJ0hUTUxTY3JpcHRFbGVtZW50JyxcbiAgJ3NlY3Rpb24nOiAnSFRNTEVsZW1lbnQnLFxuICAnc2VsZWN0JzogJ0hUTUxTZWxlY3RFbGVtZW50JyxcbiAgJ3NtYWxsJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3NvdXJjZSc6ICdIVE1MU291cmNlRWxlbWVudCcsXG4gICdzcGFuJzogJ0hUTUxTcGFuRWxlbWVudCcsXG4gICdzdHJpa2UnOiAnSFRNTEVsZW1lbnQnLFxuICAnc3Ryb25nJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3N0eWxlJzogJ0hUTUxTdHlsZUVsZW1lbnQnLFxuICAnc3ViJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3N1bW1hcnknOiAnSFRNTEVsZW1lbnQnLFxuICAnc3VwJzogJ0hUTUxFbGVtZW50JyxcbiAgJ3RhYmxlJzogJ0hUTUxUYWJsZUVsZW1lbnQnLFxuICAndGJvZHknOiAnSFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQnLFxuICAndGQnOiAnSFRNTFRhYmxlRGF0YUNlbGxFbGVtZW50JyxcbiAgJ3RleHRhcmVhJzogJ0hUTUxUZXh0QXJlYUVsZW1lbnQnLFxuICAndGZvb3QnOiAnSFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQnLFxuICAndGgnOiAnSFRNTFRhYmxlSGVhZGVyQ2VsbEVsZW1lbnQnLFxuICAndGhlYWQnOiAnSFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQnLFxuICAndGltZSc6ICdIVE1MVGltZUVsZW1lbnQnLFxuICAndGl0bGUnOiAnSFRNTFRpdGxlRWxlbWVudCcsXG4gICd0cic6ICdIVE1MVGFibGVSb3dFbGVtZW50JyxcbiAgJ3RyYWNrJzogJ0hUTUxUcmFja0VsZW1lbnQnLFxuICAndHQnOiAnSFRNTEVsZW1lbnQnLFxuICAndSc6ICdIVE1MRWxlbWVudCcsXG4gICd1bCc6ICdIVE1MVUxpc3RFbGVtZW50JyxcbiAgJ3Zhcic6ICdIVE1MRWxlbWVudCcsXG4gICd2aWRlbyc6ICdIVE1MVmlkZW9FbGVtZW50JyxcbiAgJ3dicic6ICdIVE1MRWxlbWVudCdcbn07XG5odG1sNFsgJ0VMRU1FTlRfRE9NX0lOVEVSRkFDRVMnIF0gPSBodG1sNC5FTEVNRU5UX0RPTV9JTlRFUkZBQ0VTO1xuaHRtbDQudWVmZmVjdHMgPSB7XG4gICdOT1RfTE9BREVEJzogMCxcbiAgJ1NBTUVfRE9DVU1FTlQnOiAxLFxuICAnTkVXX0RPQ1VNRU5UJzogMlxufTtcbmh0bWw0WyAndWVmZmVjdHMnIF0gPSBodG1sNC51ZWZmZWN0cztcbmh0bWw0LlVSSUVGRkVDVFMgPSB7XG4gICdhOjpocmVmJzogMixcbiAgJ2FyZWE6OmhyZWYnOiAyLFxuICAnYmxvY2txdW90ZTo6Y2l0ZSc6IDAsXG4gICdjb21tYW5kOjppY29uJzogMSxcbiAgJ2RlbDo6Y2l0ZSc6IDAsXG4gICdmb3JtOjphY3Rpb24nOiAyLFxuICAnaW1nOjpzcmMnOiAxLFxuICAnaW5wdXQ6OnNyYyc6IDEsXG4gICdpbnM6OmNpdGUnOiAwLFxuICAncTo6Y2l0ZSc6IDAsXG4gICd2aWRlbzo6cG9zdGVyJzogMVxufTtcbmh0bWw0WyAnVVJJRUZGRUNUUycgXSA9IGh0bWw0LlVSSUVGRkVDVFM7XG5odG1sNC5sdHlwZXMgPSB7XG4gICdVTlNBTkRCT1hFRCc6IDIsXG4gICdTQU5EQk9YRUQnOiAxLFxuICAnREFUQSc6IDBcbn07XG5odG1sNFsgJ2x0eXBlcycgXSA9IGh0bWw0Lmx0eXBlcztcbmh0bWw0LkxPQURFUlRZUEVTID0ge1xuICAnYTo6aHJlZic6IDIsXG4gICdhcmVhOjpocmVmJzogMixcbiAgJ2Jsb2NrcXVvdGU6OmNpdGUnOiAyLFxuICAnY29tbWFuZDo6aWNvbic6IDEsXG4gICdkZWw6OmNpdGUnOiAyLFxuICAnZm9ybTo6YWN0aW9uJzogMixcbiAgJ2ltZzo6c3JjJzogMSxcbiAgJ2lucHV0OjpzcmMnOiAxLFxuICAnaW5zOjpjaXRlJzogMixcbiAgJ3E6OmNpdGUnOiAyLFxuICAndmlkZW86OnBvc3Rlcic6IDFcbn07XG5odG1sNFsgJ0xPQURFUlRZUEVTJyBdID0gaHRtbDQuTE9BREVSVFlQRVM7XG5cbi8vIENvcHlyaWdodCAoQykgMjAwNiBHb29nbGUgSW5jLlxuLy9cbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4vLyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4vLyBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbi8vXG4vLyAgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuLy9cbi8vIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbi8vIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbi8vIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuLy8gU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuLy8gbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG5cbi8qKlxuICogQGZpbGVvdmVydmlld1xuICogQW4gSFRNTCBzYW5pdGl6ZXIgdGhhdCBjYW4gc2F0aXNmeSBhIHZhcmlldHkgb2Ygc2VjdXJpdHkgcG9saWNpZXMuXG4gKlxuICogPHA+XG4gKiBUaGUgSFRNTCBzYW5pdGl6ZXIgaXMgYnVpbHQgYXJvdW5kIGEgU0FYIHBhcnNlciBhbmQgSFRNTCBlbGVtZW50IGFuZFxuICogYXR0cmlidXRlcyBzY2hlbWFzLlxuICpcbiAqIElmIHRoZSBjc3NwYXJzZXIgaXMgbG9hZGVkLCBpbmxpbmUgc3R5bGVzIGFyZSBzYW5pdGl6ZWQgdXNpbmcgdGhlXG4gKiBjc3MgcHJvcGVydHkgYW5kIHZhbHVlIHNjaGVtYXMuICBFbHNlIHRoZXkgYXJlIHJlbW92ZSBkdXJpbmdcbiAqIHNhbml0aXphdGlvbi5cbiAqXG4gKiBJZiBpdCBleGlzdHMsIHVzZXMgcGFyc2VDc3NEZWNsYXJhdGlvbnMsIHNhbml0aXplQ3NzUHJvcGVydHksICBjc3NTY2hlbWFcbiAqXG4gKiBAYXV0aG9yIG1pa2VzYW11ZWxAZ21haWwuY29tXG4gKiBAYXV0aG9yIGphc3ZpckBnbWFpbC5jb21cbiAqIFxcQHJlcXVpcmVzIGh0bWw0LCBVUklcbiAqIFxcQG92ZXJyaWRlcyB3aW5kb3dcbiAqIFxcQHByb3ZpZGVzIGh0bWwsIGh0bWxfc2FuaXRpemVcbiAqL1xuXG4vLyBUaGUgVHVya2lzaCBpIHNlZW1zIHRvIGJlIGEgbm9uLWlzc3VlLCBidXQgYWJvcnQgaW4gY2FzZSBpdCBpcy5cbmlmICgnSScudG9Mb3dlckNhc2UoKSAhPT0gJ2knKSB7IHRocm93ICdJL2kgcHJvYmxlbSc7IH1cblxuLyoqXG4gKiBcXEBuYW1lc3BhY2VcbiAqL1xudmFyIGh0bWwgPSAoZnVuY3Rpb24oaHRtbDQpIHtcblxuICAvLyBGb3IgY2xvc3VyZSBjb21waWxlclxuICB2YXIgcGFyc2VDc3NEZWNsYXJhdGlvbnMsIHNhbml0aXplQ3NzUHJvcGVydHksIGNzc1NjaGVtYTtcbiAgaWYgKCd1bmRlZmluZWQnICE9PSB0eXBlb2Ygd2luZG93KSB7XG4gICAgcGFyc2VDc3NEZWNsYXJhdGlvbnMgPSB3aW5kb3dbJ3BhcnNlQ3NzRGVjbGFyYXRpb25zJ107XG4gICAgc2FuaXRpemVDc3NQcm9wZXJ0eSA9IHdpbmRvd1snc2FuaXRpemVDc3NQcm9wZXJ0eSddO1xuICAgIGNzc1NjaGVtYSA9IHdpbmRvd1snY3NzU2NoZW1hJ107XG4gIH1cblxuICAvLyBUaGUga2V5cyBvZiB0aGlzIG9iamVjdCBtdXN0IGJlICdxdW90ZWQnIG9yIEpTQ29tcGlsZXIgd2lsbCBtYW5nbGUgdGhlbSFcbiAgLy8gVGhpcyBpcyBhIHBhcnRpYWwgbGlzdCAtLSBsb29rdXBFbnRpdHkoKSB1c2VzIHRoZSBob3N0IGJyb3dzZXIncyBwYXJzZXJcbiAgLy8gKHdoZW4gYXZhaWxhYmxlKSB0byBpbXBsZW1lbnQgZnVsbCBlbnRpdHkgbG9va3VwLlxuICAvLyBOb3RlIHRoYXQgZW50aXRpZXMgYXJlIGluIGdlbmVyYWwgY2FzZS1zZW5zaXRpdmU7IHRoZSB1cHBlcmNhc2Ugb25lcyBhcmVcbiAgLy8gZXhwbGljaXRseSBkZWZpbmVkIGJ5IEhUTUw1IChwcmVzdW1hYmx5IGFzIGNvbXBhdGliaWxpdHkpLlxuICB2YXIgRU5USVRJRVMgPSB7XG4gICAgJ2x0JzogJzwnLFxuICAgICdMVCc6ICc8JyxcbiAgICAnZ3QnOiAnPicsXG4gICAgJ0dUJzogJz4nLFxuICAgICdhbXAnOiAnJicsXG4gICAgJ0FNUCc6ICcmJyxcbiAgICAncXVvdCc6ICdcIicsXG4gICAgJ2Fwb3MnOiAnXFwnJyxcbiAgICAnbmJzcCc6ICdcXHUwMEEwJ1xuICB9O1xuXG4gIC8vIFBhdHRlcm5zIGZvciB0eXBlcyBvZiBlbnRpdHkvY2hhcmFjdGVyIHJlZmVyZW5jZSBuYW1lcy5cbiAgdmFyIGRlY2ltYWxFc2NhcGVSZSA9IC9eIyhcXGQrKSQvO1xuICB2YXIgaGV4RXNjYXBlUmUgPSAvXiN4KFswLTlBLUZhLWZdKykkLztcbiAgLy8gY29udGFpbnMgZXZlcnkgZW50aXR5IHBlciBodHRwOi8vd3d3LnczLm9yZy9UUi8yMDExL1dELWh0bWw1LTIwMTEwMTEzL25hbWVkLWNoYXJhY3Rlci1yZWZlcmVuY2VzLmh0bWxcbiAgdmFyIHNhZmVFbnRpdHlOYW1lUmUgPSAvXltBLVphLXpdW0EtemEtejAtOV0rJC87XG4gIC8vIFVzZWQgYXMgYSBob29rIHRvIGludm9rZSB0aGUgYnJvd3NlcidzIGVudGl0eSBwYXJzaW5nLiA8dGV4dGFyZWE+IGlzIHVzZWRcbiAgLy8gYmVjYXVzZSBpdHMgY29udGVudCBpcyBwYXJzZWQgZm9yIGVudGl0aWVzIGJ1dCBub3QgdGFncy5cbiAgLy8gVE9ETyhrcHJlaWQpOiBUaGlzIHJldHJpZXZhbCBpcyBhIGtsdWRnZSBhbmQgbGVhZHMgdG8gc2lsZW50IGxvc3Mgb2ZcbiAgLy8gZnVuY3Rpb25hbGl0eSBpZiB0aGUgZG9jdW1lbnQgaXNuJ3QgYXZhaWxhYmxlLlxuICB2YXIgZW50aXR5TG9va3VwRWxlbWVudCA9XG4gICAgICAoJ3VuZGVmaW5lZCcgIT09IHR5cGVvZiB3aW5kb3cgJiYgd2luZG93Wydkb2N1bWVudCddKVxuICAgICAgICAgID8gd2luZG93Wydkb2N1bWVudCddLmNyZWF0ZUVsZW1lbnQoJ3RleHRhcmVhJykgOiBudWxsO1xuICAvKipcbiAgICogRGVjb2RlcyBhbiBIVE1MIGVudGl0eS5cbiAgICpcbiAgICoge1xcQHVwZG9jXG4gICAqICQgbG9va3VwRW50aXR5KCdsdCcpXG4gICAqICMgJzwnXG4gICAqICQgbG9va3VwRW50aXR5KCdHVCcpXG4gICAqICMgJz4nXG4gICAqICQgbG9va3VwRW50aXR5KCdhbXAnKVxuICAgKiAjICcmJ1xuICAgKiAkIGxvb2t1cEVudGl0eSgnbmJzcCcpXG4gICAqICMgJ1xceEEwJ1xuICAgKiAkIGxvb2t1cEVudGl0eSgnYXBvcycpXG4gICAqICMgXCInXCJcbiAgICogJCBsb29rdXBFbnRpdHkoJ3F1b3QnKVxuICAgKiAjICdcIidcbiAgICogJCBsb29rdXBFbnRpdHkoJyN4YScpXG4gICAqICMgJ1xcbidcbiAgICogJCBsb29rdXBFbnRpdHkoJyMxMCcpXG4gICAqICMgJ1xcbidcbiAgICogJCBsb29rdXBFbnRpdHkoJyN4MGEnKVxuICAgKiAjICdcXG4nXG4gICAqICQgbG9va3VwRW50aXR5KCcjMDEwJylcbiAgICogIyAnXFxuJ1xuICAgKiAkIGxvb2t1cEVudGl0eSgnI3gwMEEnKVxuICAgKiAjICdcXG4nXG4gICAqICQgbG9va3VwRW50aXR5KCdQaScpICAgICAgLy8gS25vd24gZmFpbHVyZVxuICAgKiAjICdcXHUwM0EwJ1xuICAgKiAkIGxvb2t1cEVudGl0eSgncGknKSAgICAgIC8vIEtub3duIGZhaWx1cmVcbiAgICogIyAnXFx1MDNDMCdcbiAgICogfVxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZSB0aGUgY29udGVudCBiZXR3ZWVuIHRoZSAnJicgYW5kIHRoZSAnOycuXG4gICAqIEByZXR1cm4ge3N0cmluZ30gYSBzaW5nbGUgdW5pY29kZSBjb2RlLXBvaW50IGFzIGEgc3RyaW5nLlxuICAgKi9cbiAgZnVuY3Rpb24gbG9va3VwRW50aXR5KG5hbWUpIHtcbiAgICAvLyBUT0RPOiBlbnRpdHkgbG9va3VwIGFzIHNwZWNpZmllZCBieSBIVE1MNSBhY3R1YWxseSBkZXBlbmRzIG9uIHRoZVxuICAgIC8vIHByZXNlbmNlIG9mIHRoZSBcIjtcIi5cbiAgICBpZiAoRU5USVRJRVMuaGFzT3duUHJvcGVydHkobmFtZSkpIHsgcmV0dXJuIEVOVElUSUVTW25hbWVdOyB9XG4gICAgdmFyIG0gPSBuYW1lLm1hdGNoKGRlY2ltYWxFc2NhcGVSZSk7XG4gICAgaWYgKG0pIHtcbiAgICAgIHJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlKHBhcnNlSW50KG1bMV0sIDEwKSk7XG4gICAgfSBlbHNlIGlmICghIShtID0gbmFtZS5tYXRjaChoZXhFc2NhcGVSZSkpKSB7XG4gICAgICByZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZShwYXJzZUludChtWzFdLCAxNikpO1xuICAgIH0gZWxzZSBpZiAoZW50aXR5TG9va3VwRWxlbWVudCAmJiBzYWZlRW50aXR5TmFtZVJlLnRlc3QobmFtZSkpIHtcbiAgICAgIGVudGl0eUxvb2t1cEVsZW1lbnQuaW5uZXJIVE1MID0gJyYnICsgbmFtZSArICc7JztcbiAgICAgIHZhciB0ZXh0ID0gZW50aXR5TG9va3VwRWxlbWVudC50ZXh0Q29udGVudDtcbiAgICAgIEVOVElUSUVTW25hbWVdID0gdGV4dDtcbiAgICAgIHJldHVybiB0ZXh0O1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gJyYnICsgbmFtZSArICc7JztcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBkZWNvZGVPbmVFbnRpdHkoXywgbmFtZSkge1xuICAgIHJldHVybiBsb29rdXBFbnRpdHkobmFtZSk7XG4gIH1cblxuICB2YXIgbnVsUmUgPSAvXFwwL2c7XG4gIGZ1bmN0aW9uIHN0cmlwTlVMcyhzKSB7XG4gICAgcmV0dXJuIHMucmVwbGFjZShudWxSZSwgJycpO1xuICB9XG5cbiAgdmFyIEVOVElUWV9SRV8xID0gLyYoI1swLTldK3wjW3hYXVswLTlBLUZhLWZdK3xcXHcrKTsvZztcbiAgdmFyIEVOVElUWV9SRV8yID0gL14oI1swLTldK3wjW3hYXVswLTlBLUZhLWZdK3xcXHcrKTsvO1xuICAvKipcbiAgICogVGhlIHBsYWluIHRleHQgb2YgYSBjaHVuayBvZiBIVE1MIENEQVRBIHdoaWNoIHBvc3NpYmx5IGNvbnRhaW5pbmcuXG4gICAqXG4gICAqIHtcXEB1cGRvY1xuICAgKiAkIHVuZXNjYXBlRW50aXRpZXMoJycpXG4gICAqICMgJydcbiAgICogJCB1bmVzY2FwZUVudGl0aWVzKCdoZWxsbyBXb3JsZCEnKVxuICAgKiAjICdoZWxsbyBXb3JsZCEnXG4gICAqICQgdW5lc2NhcGVFbnRpdGllcygnMSAmbHQ7IDIgJmFtcDsmQU1QOyA0ICZndDsgMyYjMTA7JylcbiAgICogIyAnMSA8IDIgJiYgNCA+IDNcXG4nXG4gICAqICQgdW5lc2NhcGVFbnRpdGllcygnJmx0OyZsdCA8LSB1bmZpbmlzaGVkIGVudGl0eSZndDsnKVxuICAgKiAjICc8Jmx0IDwtIHVuZmluaXNoZWQgZW50aXR5PidcbiAgICogJCB1bmVzY2FwZUVudGl0aWVzKCcvZm9vP2Jhcj1iYXomY29weT10cnVlJykgIC8vICYgb2Z0ZW4gdW5lc2NhcGVkIGluIFVSTFNcbiAgICogIyAnL2Zvbz9iYXI9YmF6JmNvcHk9dHJ1ZSdcbiAgICogJCB1bmVzY2FwZUVudGl0aWVzKCdwaT0mcGk7JiN4M2MwOywgUGk9JlBpO1xcdTAzQTAnKSAvLyBGSVhNRToga25vd24gZmFpbHVyZVxuICAgKiAjICdwaT1cXHUwM0MwXFx1MDNjMCwgUGk9XFx1MDNBMFxcdTAzQTAnXG4gICAqIH1cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IHMgYSBjaHVuayBvZiBIVE1MIENEQVRBLiAgSXQgbXVzdCBub3Qgc3RhcnQgb3IgZW5kIGluc2lkZVxuICAgKiAgICAgYW4gSFRNTCBlbnRpdHkuXG4gICAqL1xuICBmdW5jdGlvbiB1bmVzY2FwZUVudGl0aWVzKHMpIHtcbiAgICByZXR1cm4gcy5yZXBsYWNlKEVOVElUWV9SRV8xLCBkZWNvZGVPbmVFbnRpdHkpO1xuICB9XG5cbiAgdmFyIGFtcFJlID0gLyYvZztcbiAgdmFyIGxvb3NlQW1wUmUgPSAvJihbXmEteiNdfCMoPzpbXjAtOXhdfHgoPzpbXjAtOWEtZl18JCl8JCl8JCkvZ2k7XG4gIHZhciBsdFJlID0gL1s8XS9nO1xuICB2YXIgZ3RSZSA9IC8+L2c7XG4gIHZhciBxdW90UmUgPSAvXFxcIi9nO1xuXG4gIC8qKlxuICAgKiBFc2NhcGVzIEhUTUwgc3BlY2lhbCBjaGFyYWN0ZXJzIGluIGF0dHJpYnV0ZSB2YWx1ZXMuXG4gICAqXG4gICAqIHtcXEB1cGRvY1xuICAgKiAkIGVzY2FwZUF0dHJpYignJylcbiAgICogIyAnJ1xuICAgKiAkIGVzY2FwZUF0dHJpYignXCI8PCY9PSY+PlwiJykgIC8vIERvIG5vdCBqdXN0IGVzY2FwZSB0aGUgZmlyc3Qgb2NjdXJyZW5jZS5cbiAgICogIyAnJiMzNDsmbHQ7Jmx0OyZhbXA7JiM2MTsmIzYxOyZhbXA7Jmd0OyZndDsmIzM0OydcbiAgICogJCBlc2NhcGVBdHRyaWIoJ0hlbGxvIDxXb3JsZD4hJylcbiAgICogIyAnSGVsbG8gJmx0O1dvcmxkJmd0OyEnXG4gICAqIH1cbiAgICovXG4gIGZ1bmN0aW9uIGVzY2FwZUF0dHJpYihzKSB7XG4gICAgcmV0dXJuICgnJyArIHMpLnJlcGxhY2UoYW1wUmUsICcmYW1wOycpLnJlcGxhY2UobHRSZSwgJyZsdDsnKVxuICAgICAgICAucmVwbGFjZShndFJlLCAnJmd0OycpLnJlcGxhY2UocXVvdFJlLCAnJiMzNDsnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFc2NhcGUgZW50aXRpZXMgaW4gUkNEQVRBIHRoYXQgY2FuIGJlIGVzY2FwZWQgd2l0aG91dCBjaGFuZ2luZyB0aGUgbWVhbmluZy5cbiAgICoge1xcQHVwZG9jXG4gICAqICQgbm9ybWFsaXplUkNEYXRhKCcxIDwgMiAmJmFtcDsgMyA+IDQgJmFtcDsmIDUgJmx0OyA3JjgnKVxuICAgKiAjICcxICZsdDsgMiAmYW1wOyZhbXA7IDMgJmd0OyA0ICZhbXA7JmFtcDsgNSAmbHQ7IDcmYW1wOzgnXG4gICAqIH1cbiAgICovXG4gIGZ1bmN0aW9uIG5vcm1hbGl6ZVJDRGF0YShyY2RhdGEpIHtcbiAgICByZXR1cm4gcmNkYXRhXG4gICAgICAgIC5yZXBsYWNlKGxvb3NlQW1wUmUsICcmYW1wOyQxJylcbiAgICAgICAgLnJlcGxhY2UobHRSZSwgJyZsdDsnKVxuICAgICAgICAucmVwbGFjZShndFJlLCAnJmd0OycpO1xuICB9XG5cbiAgLy8gVE9ETyhmZWxpeDhhKTogdmFsaWRhdGUgc2FuaXRpemVyIHJlZ2V4cyBhZ2FpbnN0IHRoZSBIVE1MNSBncmFtbWFyIGF0XG4gIC8vIGh0dHA6Ly93d3cud2hhdHdnLm9yZy9zcGVjcy93ZWItYXBwcy9jdXJyZW50LXdvcmsvbXVsdGlwYWdlL3N5bnRheC5odG1sXG4gIC8vIGh0dHA6Ly93d3cud2hhdHdnLm9yZy9zcGVjcy93ZWItYXBwcy9jdXJyZW50LXdvcmsvbXVsdGlwYWdlL3BhcnNpbmcuaHRtbFxuICAvLyBodHRwOi8vd3d3LndoYXR3Zy5vcmcvc3BlY3Mvd2ViLWFwcHMvY3VycmVudC13b3JrL211bHRpcGFnZS90b2tlbml6YXRpb24uaHRtbFxuICAvLyBodHRwOi8vd3d3LndoYXR3Zy5vcmcvc3BlY3Mvd2ViLWFwcHMvY3VycmVudC13b3JrL211bHRpcGFnZS90cmVlLWNvbnN0cnVjdGlvbi5odG1sXG5cbiAgLy8gV2UgaW5pdGlhbGx5IHNwbGl0IGlucHV0IHNvIHRoYXQgcG90ZW50aWFsbHkgbWVhbmluZ2Z1bCBjaGFyYWN0ZXJzXG4gIC8vIGxpa2UgJzwnIGFuZCAnPicgYXJlIHNlcGFyYXRlIHRva2VucywgdXNpbmcgYSBmYXN0IGR1bWIgcHJvY2VzcyB0aGF0XG4gIC8vIGlnbm9yZXMgcXVvdGluZy4gIFRoZW4gd2Ugd2FsayB0aGF0IHRva2VuIHN0cmVhbSwgYW5kIHdoZW4gd2Ugc2VlIGFcbiAgLy8gJzwnIHRoYXQncyB0aGUgc3RhcnQgb2YgYSB0YWcsIHdlIHVzZSBBVFRSX1JFIHRvIGV4dHJhY3QgdGFnXG4gIC8vIGF0dHJpYnV0ZXMgZnJvbSB0aGUgbmV4dCB0b2tlbi4gIFRoYXQgdG9rZW4gd2lsbCBuZXZlciBoYXZlIGEgJz4nXG4gIC8vIGNoYXJhY3Rlci4gIEhvd2V2ZXIsIGl0IG1pZ2h0IGhhdmUgYW4gdW5iYWxhbmNlZCBxdW90ZSBjaGFyYWN0ZXIsIGFuZFxuICAvLyB3aGVuIHdlIHNlZSB0aGF0LCB3ZSBjb21iaW5lIGFkZGl0aW9uYWwgdG9rZW5zIHRvIGJhbGFuY2UgdGhlIHF1b3RlLlxuXG4gIHZhciBBVFRSX1JFID0gbmV3IFJlZ0V4cChcbiAgICAnXlxcXFxzKicgK1xuICAgICcoWy0uOlxcXFx3XSspJyArICAgICAgICAgICAgIC8vIDEgPSBBdHRyaWJ1dGUgbmFtZVxuICAgICcoPzonICsgKFxuICAgICAgJ1xcXFxzKig9KVxcXFxzKicgKyAgICAgICAgICAgLy8gMiA9IElzIHRoZXJlIGEgdmFsdWU/XG4gICAgICAnKCcgKyAoICAgICAgICAgICAgICAgICAgIC8vIDMgPSBBdHRyaWJ1dGUgdmFsdWVcbiAgICAgICAgLy8gVE9ETyhmZWxpeDhhKTogbWF5YmUgdXNlIGJhY2tyZWYgdG8gbWF0Y2ggcXVvdGVzXG4gICAgICAgICcoXFxcIilbXlxcXCJdKihcXFwifCQpJyArICAgIC8vIDQsIDUgPSBEb3VibGUtcXVvdGVkIHN0cmluZ1xuICAgICAgICAnfCcgK1xuICAgICAgICAnKFxcJylbXlxcJ10qKFxcJ3wkKScgKyAgICAvLyA2LCA3ID0gU2luZ2xlLXF1b3RlZCBzdHJpbmdcbiAgICAgICAgJ3wnICtcbiAgICAgICAgLy8gUG9zaXRpdmUgbG9va2FoZWFkIHRvIHByZXZlbnQgaW50ZXJwcmV0YXRpb24gb2ZcbiAgICAgICAgLy8gPGZvbyBhPSBiPWM+IGFzIDxmb28gYT0nYj1jJz5cbiAgICAgICAgLy8gVE9ETyhmZWxpeDhhKTogbWlnaHQgYmUgYWJsZSB0byBkcm9wIHRoaXMgY2FzZVxuICAgICAgICAnKD89W2Etel1bLVxcXFx3XSpcXFxccyo9KScgK1xuICAgICAgICAnfCcgK1xuICAgICAgICAvLyBVbnF1b3RlZCB2YWx1ZSB0aGF0IGlzbid0IGFuIGF0dHJpYnV0ZSBuYW1lXG4gICAgICAgIC8vIChzaW5jZSB3ZSBkaWRuJ3QgbWF0Y2ggdGhlIHBvc2l0aXZlIGxvb2thaGVhZCBhYm92ZSlcbiAgICAgICAgJ1teXFxcIlxcJ1xcXFxzXSonICkgK1xuICAgICAgJyknICkgK1xuICAgICcpPycsXG4gICAgJ2knKTtcblxuICAvLyBmYWxzZSBvbiBJRTw9OCwgdHJ1ZSBvbiBtb3N0IG90aGVyIGJyb3dzZXJzXG4gIHZhciBzcGxpdFdpbGxDYXB0dXJlID0gKCdhLGInLnNwbGl0KC8oLCkvKS5sZW5ndGggPT09IDMpO1xuXG4gIC8vIGJpdG1hc2sgZm9yIHRhZ3Mgd2l0aCBzcGVjaWFsIHBhcnNpbmcsIGxpa2UgPHNjcmlwdD4gYW5kIDx0ZXh0YXJlYT5cbiAgdmFyIEVGTEFHU19URVhUID0gaHRtbDQuZWZsYWdzWydDREFUQSddIHwgaHRtbDQuZWZsYWdzWydSQ0RBVEEnXTtcblxuICAvKipcbiAgICogR2l2ZW4gYSBTQVgtbGlrZSBldmVudCBoYW5kbGVyLCBwcm9kdWNlIGEgZnVuY3Rpb24gdGhhdCBmZWVkcyB0aG9zZVxuICAgKiBldmVudHMgYW5kIGEgcGFyYW1ldGVyIHRvIHRoZSBldmVudCBoYW5kbGVyLlxuICAgKlxuICAgKiBUaGUgZXZlbnQgaGFuZGxlciBoYXMgdGhlIGZvcm06e0Bjb2RlXG4gICAqIHtcbiAgICogICAvLyBOYW1lIGlzIGFuIHVwcGVyLWNhc2UgSFRNTCB0YWcgbmFtZS4gIEF0dHJpYnMgaXMgYW4gYXJyYXkgb2ZcbiAgICogICAvLyBhbHRlcm5hdGluZyB1cHBlci1jYXNlIGF0dHJpYnV0ZSBuYW1lcywgYW5kIGF0dHJpYnV0ZSB2YWx1ZXMuICBUaGVcbiAgICogICAvLyBhdHRyaWJzIGFycmF5IGlzIHJldXNlZCBieSB0aGUgcGFyc2VyLiAgUGFyYW0gaXMgdGhlIHZhbHVlIHBhc3NlZCB0b1xuICAgKiAgIC8vIHRoZSBzYXhQYXJzZXIuXG4gICAqICAgc3RhcnRUYWc6IGZ1bmN0aW9uIChuYW1lLCBhdHRyaWJzLCBwYXJhbSkgeyAuLi4gfSxcbiAgICogICBlbmRUYWc6ICAgZnVuY3Rpb24gKG5hbWUsIHBhcmFtKSB7IC4uLiB9LFxuICAgKiAgIHBjZGF0YTogICBmdW5jdGlvbiAodGV4dCwgcGFyYW0pIHsgLi4uIH0sXG4gICAqICAgcmNkYXRhOiAgIGZ1bmN0aW9uICh0ZXh0LCBwYXJhbSkgeyAuLi4gfSxcbiAgICogICBjZGF0YTogICAgZnVuY3Rpb24gKHRleHQsIHBhcmFtKSB7IC4uLiB9LFxuICAgKiAgIHN0YXJ0RG9jOiBmdW5jdGlvbiAocGFyYW0pIHsgLi4uIH0sXG4gICAqICAgZW5kRG9jOiAgIGZ1bmN0aW9uIChwYXJhbSkgeyAuLi4gfVxuICAgKiB9fVxuICAgKlxuICAgKiBAcGFyYW0ge09iamVjdH0gaGFuZGxlciBhIHJlY29yZCBjb250YWluaW5nIGV2ZW50IGhhbmRsZXJzLlxuICAgKiBAcmV0dXJuIHtmdW5jdGlvbihzdHJpbmcsIE9iamVjdCl9IEEgZnVuY3Rpb24gdGhhdCB0YWtlcyBhIGNodW5rIG9mIEhUTUxcbiAgICogICAgIGFuZCBhIHBhcmFtZXRlci4gIFRoZSBwYXJhbWV0ZXIgaXMgcGFzc2VkIG9uIHRvIHRoZSBoYW5kbGVyIG1ldGhvZHMuXG4gICAqL1xuICBmdW5jdGlvbiBtYWtlU2F4UGFyc2VyKGhhbmRsZXIpIHtcbiAgICAvLyBBY2NlcHQgcXVvdGVkIG9yIHVucXVvdGVkIGtleXMgKENsb3N1cmUgY29tcGF0KVxuICAgIHZhciBoY29weSA9IHtcbiAgICAgIGNkYXRhOiBoYW5kbGVyLmNkYXRhIHx8IGhhbmRsZXJbJ2NkYXRhJ10sXG4gICAgICBjb21tZW50OiBoYW5kbGVyLmNvbW1lbnQgfHwgaGFuZGxlclsnY29tbWVudCddLFxuICAgICAgZW5kRG9jOiBoYW5kbGVyLmVuZERvYyB8fCBoYW5kbGVyWydlbmREb2MnXSxcbiAgICAgIGVuZFRhZzogaGFuZGxlci5lbmRUYWcgfHwgaGFuZGxlclsnZW5kVGFnJ10sXG4gICAgICBwY2RhdGE6IGhhbmRsZXIucGNkYXRhIHx8IGhhbmRsZXJbJ3BjZGF0YSddLFxuICAgICAgcmNkYXRhOiBoYW5kbGVyLnJjZGF0YSB8fCBoYW5kbGVyWydyY2RhdGEnXSxcbiAgICAgIHN0YXJ0RG9jOiBoYW5kbGVyLnN0YXJ0RG9jIHx8IGhhbmRsZXJbJ3N0YXJ0RG9jJ10sXG4gICAgICBzdGFydFRhZzogaGFuZGxlci5zdGFydFRhZyB8fCBoYW5kbGVyWydzdGFydFRhZyddXG4gICAgfTtcbiAgICByZXR1cm4gZnVuY3Rpb24oaHRtbFRleHQsIHBhcmFtKSB7XG4gICAgICByZXR1cm4gcGFyc2UoaHRtbFRleHQsIGhjb3B5LCBwYXJhbSk7XG4gICAgfTtcbiAgfVxuXG4gIC8vIFBhcnNpbmcgc3RyYXRlZ3kgaXMgdG8gc3BsaXQgaW5wdXQgaW50byBwYXJ0cyB0aGF0IG1pZ2h0IGJlIGxleGljYWxseVxuICAvLyBtZWFuaW5nZnVsIChldmVyeSBcIj5cIiBiZWNvbWVzIGEgc2VwYXJhdGUgcGFydCksIGFuZCB0aGVuIHJlY29tYmluZVxuICAvLyBwYXJ0cyBpZiB3ZSBkaXNjb3ZlciB0aGV5J3JlIGluIGEgZGlmZmVyZW50IGNvbnRleHQuXG5cbiAgLy8gVE9ETyhmZWxpeDhhKTogU2lnbmlmaWNhbnQgcGVyZm9ybWFuY2UgcmVncmVzc2lvbnMgZnJvbSAtbGVnYWN5LFxuICAvLyB0ZXN0ZWQgb25cbiAgLy8gICAgQ2hyb21lIDE4LjBcbiAgLy8gICAgRmlyZWZveCAxMS4wXG4gIC8vICAgIElFIDYsIDcsIDgsIDlcbiAgLy8gICAgT3BlcmEgMTEuNjFcbiAgLy8gICAgU2FmYXJpIDUuMS4zXG4gIC8vIE1hbnkgb2YgdGhlc2UgYXJlIHVudXN1YWwgcGF0dGVybnMgdGhhdCBhcmUgbGluZWFybHkgc2xvd2VyIGFuZCBzdGlsbFxuICAvLyBwcmV0dHkgZmFzdCAoZWcgMW1zIHRvIDVtcyksIHNvIG5vdCBuZWNlc3NhcmlseSB3b3J0aCBmaXhpbmcuXG5cbiAgLy8gVE9ETyhmZWxpeDhhKTogXCI8c2NyaXB0PiAmJiAmJiAmJiAuLi4gPFxcL3NjcmlwdD5cIiBpcyBzbG93ZXIgb24gYWxsXG4gIC8vIGJyb3dzZXJzLiAgVGhlIGhvdHNwb3QgaXMgaHRtbFNwbGl0LlxuXG4gIC8vIFRPRE8oZmVsaXg4YSk6IFwiPHAgdGl0bGU9Jz4+Pj4uLi4nPjxcXC9wPlwiIGlzIHNsb3dlciBvbiBhbGwgYnJvd3NlcnMuXG4gIC8vIFRoaXMgaXMgcGFydGx5IGh0bWxTcGxpdCwgYnV0IHRoZSBob3RzcG90IGlzIHBhcnNlVGFnQW5kQXR0cnMuXG5cbiAgLy8gVE9ETyhmZWxpeDhhKTogXCI8YT48XFwvYT48YT48XFwvYT4uLi5cIiBpcyBzbG93ZXIgb24gSUU5LlxuICAvLyBcIjxhPjE8XFwvYT48YT4xPFxcL2E+Li4uXCIgaXMgZmFzdGVyLCBcIjxhPjxcXC9hPjI8YT48XFwvYT4yLi4uXCIgaXMgZmFzdGVyLlxuXG4gIC8vIFRPRE8oZmVsaXg4YSk6IFwiPHA8cDxwLi4uXCIgaXMgc2xvd2VyIG9uIElFWzYtOF1cblxuICB2YXIgY29udGludWF0aW9uTWFya2VyID0ge307XG4gIGZ1bmN0aW9uIHBhcnNlKGh0bWxUZXh0LCBoYW5kbGVyLCBwYXJhbSkge1xuICAgIHZhciBtLCBwLCB0YWdOYW1lO1xuICAgIHZhciBwYXJ0cyA9IGh0bWxTcGxpdChodG1sVGV4dCk7XG4gICAgdmFyIHN0YXRlID0ge1xuICAgICAgbm9Nb3JlR1Q6IGZhbHNlLFxuICAgICAgbm9Nb3JlRW5kQ29tbWVudHM6IGZhbHNlXG4gICAgfTtcbiAgICBwYXJzZUNQUyhoYW5kbGVyLCBwYXJ0cywgMCwgc3RhdGUsIHBhcmFtKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBpbml0aWFsLCBzdGF0ZSwgcGFyYW0pIHtcbiAgICByZXR1cm4gZnVuY3Rpb24gKCkge1xuICAgICAgcGFyc2VDUFMoaCwgcGFydHMsIGluaXRpYWwsIHN0YXRlLCBwYXJhbSk7XG4gICAgfTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHBhcnNlQ1BTKGgsIHBhcnRzLCBpbml0aWFsLCBzdGF0ZSwgcGFyYW0pIHtcbiAgICB0cnkge1xuICAgICAgaWYgKGguc3RhcnREb2MgJiYgaW5pdGlhbCA9PSAwKSB7IGguc3RhcnREb2MocGFyYW0pOyB9XG4gICAgICB2YXIgbSwgcCwgdGFnTmFtZTtcbiAgICAgIGZvciAodmFyIHBvcyA9IGluaXRpYWwsIGVuZCA9IHBhcnRzLmxlbmd0aDsgcG9zIDwgZW5kOykge1xuICAgICAgICB2YXIgY3VycmVudCA9IHBhcnRzW3BvcysrXTtcbiAgICAgICAgdmFyIG5leHQgPSBwYXJ0c1twb3NdO1xuICAgICAgICBzd2l0Y2ggKGN1cnJlbnQpIHtcbiAgICAgICAgY2FzZSAnJic6XG4gICAgICAgICAgaWYgKEVOVElUWV9SRV8yLnRlc3QobmV4dCkpIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgICBoLnBjZGF0YSgnJicgKyBuZXh0LCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgICAgICAgIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBwb3MsIHN0YXRlLCBwYXJhbSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcG9zKys7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkgeyBoLnBjZGF0YShcIiZhbXA7XCIsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICc8XFwvJzpcbiAgICAgICAgICBpZiAobSA9IC9eKFstXFx3Ol0rKVteXFwnXFxcIl0qLy5leGVjKG5leHQpKSB7XG4gICAgICAgICAgICBpZiAobVswXS5sZW5ndGggPT09IG5leHQubGVuZ3RoICYmIHBhcnRzW3BvcyArIDFdID09PSAnPicpIHtcbiAgICAgICAgICAgICAgLy8gZmFzdCBjYXNlLCBubyBhdHRyaWJ1dGUgcGFyc2luZyBuZWVkZWRcbiAgICAgICAgICAgICAgcG9zICs9IDI7XG4gICAgICAgICAgICAgIHRhZ05hbWUgPSBtWzFdLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgICAgICAgIGlmIChoLmVuZFRhZykge1xuICAgICAgICAgICAgICAgIGguZW5kVGFnKHRhZ05hbWUsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgLy8gc2xvdyBjYXNlLCBuZWVkIHRvIHBhcnNlIGF0dHJpYnV0ZXNcbiAgICAgICAgICAgICAgLy8gVE9ETyhmZWxpeDhhKTogZG8gd2UgcmVhbGx5IGNhcmUgYWJvdXQgbWlzcGFyc2luZyB0aGlzP1xuICAgICAgICAgICAgICBwb3MgPSBwYXJzZUVuZFRhZyhcbiAgICAgICAgICAgICAgICBwYXJ0cywgcG9zLCBoLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLCBzdGF0ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgICBoLnBjZGF0YSgnJmx0Oy8nLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgICAgICAgIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBwb3MsIHN0YXRlLCBwYXJhbSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnPCc6XG4gICAgICAgICAgaWYgKG0gPSAvXihbLVxcdzpdKylcXHMqXFwvPy8uZXhlYyhuZXh0KSkge1xuICAgICAgICAgICAgaWYgKG1bMF0ubGVuZ3RoID09PSBuZXh0Lmxlbmd0aCAmJiBwYXJ0c1twb3MgKyAxXSA9PT0gJz4nKSB7XG4gICAgICAgICAgICAgIC8vIGZhc3QgY2FzZSwgbm8gYXR0cmlidXRlIHBhcnNpbmcgbmVlZGVkXG4gICAgICAgICAgICAgIHBvcyArPSAyO1xuICAgICAgICAgICAgICB0YWdOYW1lID0gbVsxXS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICAgICAgICBpZiAoaC5zdGFydFRhZykge1xuICAgICAgICAgICAgICAgIGguc3RhcnRUYWcodGFnTmFtZSwgW10sIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAvLyB0YWdzIGxpa2UgPHNjcmlwdD4gYW5kIDx0ZXh0YXJlYT4gaGF2ZSBzcGVjaWFsIHBhcnNpbmdcbiAgICAgICAgICAgICAgdmFyIGVmbGFncyA9IGh0bWw0LkVMRU1FTlRTW3RhZ05hbWVdO1xuICAgICAgICAgICAgICBpZiAoZWZsYWdzICYgRUZMQUdTX1RFWFQpIHtcbiAgICAgICAgICAgICAgICB2YXIgdGFnID0geyBuYW1lOiB0YWdOYW1lLCBuZXh0OiBwb3MsIGVmbGFnczogZWZsYWdzIH07XG4gICAgICAgICAgICAgICAgcG9zID0gcGFyc2VUZXh0KFxuICAgICAgICAgICAgICAgICAgcGFydHMsIHRhZywgaCwgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlciwgc3RhdGUpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAvLyBzbG93IGNhc2UsIG5lZWQgdG8gcGFyc2UgYXR0cmlidXRlc1xuICAgICAgICAgICAgICBwb3MgPSBwYXJzZVN0YXJ0VGFnKFxuICAgICAgICAgICAgICAgIHBhcnRzLCBwb3MsIGgsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsIHN0YXRlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKGgucGNkYXRhKSB7XG4gICAgICAgICAgICAgIGgucGNkYXRhKCcmbHQ7JywgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJzxcXCEtLSc6XG4gICAgICAgICAgLy8gVGhlIHBhdGhvbG9naWNhbCBjYXNlIGlzIG4gY29waWVzIG9mICc8XFwhLS0nIHdpdGhvdXQgJy0tPicsIGFuZFxuICAgICAgICAgIC8vIHJlcGVhdGVkIGZhaWx1cmUgdG8gZmluZCAnLS0+JyBpcyBxdWFkcmF0aWMuICBXZSBhdm9pZCB0aGF0IGJ5XG4gICAgICAgICAgLy8gcmVtZW1iZXJpbmcgd2hlbiBzZWFyY2ggZm9yICctLT4nIGZhaWxzLlxuICAgICAgICAgIGlmICghc3RhdGUubm9Nb3JlRW5kQ29tbWVudHMpIHtcbiAgICAgICAgICAgIC8vIEEgY29tbWVudCA8XFwhLS14LS0+IGlzIHNwbGl0IGludG8gdGhyZWUgdG9rZW5zOlxuICAgICAgICAgICAgLy8gICAnPFxcIS0tJywgJ3gtLScsICc+J1xuICAgICAgICAgICAgLy8gV2Ugd2FudCB0byBmaW5kIHRoZSBuZXh0ICc+JyB0b2tlbiB0aGF0IGhhcyBhIHByZWNlZGluZyAnLS0nLlxuICAgICAgICAgICAgLy8gcG9zIGlzIGF0IHRoZSAneC0tJy5cbiAgICAgICAgICAgIGZvciAocCA9IHBvcyArIDE7IHAgPCBlbmQ7IHArKykge1xuICAgICAgICAgICAgICBpZiAocGFydHNbcF0gPT09ICc+JyAmJiAvLS0kLy50ZXN0KHBhcnRzW3AgLSAxXSkpIHsgYnJlYWs7IH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChwIDwgZW5kKSB7XG4gICAgICAgICAgICAgIGlmIChoLmNvbW1lbnQpIHtcbiAgICAgICAgICAgICAgICB2YXIgY29tbWVudCA9IHBhcnRzLnNsaWNlKHBvcywgcCkuam9pbignJyk7XG4gICAgICAgICAgICAgICAgaC5jb21tZW50KFxuICAgICAgICAgICAgICAgICAgY29tbWVudC5zdWJzdHIoMCwgY29tbWVudC5sZW5ndGggLSAyKSwgcGFyYW0sXG4gICAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcCArIDEsIHN0YXRlLCBwYXJhbSkpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHBvcyA9IHAgKyAxO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc3RhdGUubm9Nb3JlRW5kQ29tbWVudHMgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc3RhdGUubm9Nb3JlRW5kQ29tbWVudHMpIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgICBoLnBjZGF0YSgnJmx0OyEtLScsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsXG4gICAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICc8XFwhJzpcbiAgICAgICAgICBpZiAoIS9eXFx3Ly50ZXN0KG5leHQpKSB7XG4gICAgICAgICAgICBpZiAoaC5wY2RhdGEpIHtcbiAgICAgICAgICAgICAgaC5wY2RhdGEoJyZsdDshJywgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gc2ltaWxhciB0byBub01vcmVFbmRDb21tZW50IGxvZ2ljXG4gICAgICAgICAgICBpZiAoIXN0YXRlLm5vTW9yZUdUKSB7XG4gICAgICAgICAgICAgIGZvciAocCA9IHBvcyArIDE7IHAgPCBlbmQ7IHArKykge1xuICAgICAgICAgICAgICAgIGlmIChwYXJ0c1twXSA9PT0gJz4nKSB7IGJyZWFrOyB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKHAgPCBlbmQpIHtcbiAgICAgICAgICAgICAgICBwb3MgPSBwICsgMTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzdGF0ZS5ub01vcmVHVCA9IHRydWU7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChzdGF0ZS5ub01vcmVHVCkge1xuICAgICAgICAgICAgICBpZiAoaC5wY2RhdGEpIHtcbiAgICAgICAgICAgICAgICBoLnBjZGF0YSgnJmx0OyEnLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJzw/JzpcbiAgICAgICAgICAvLyBzaW1pbGFyIHRvIG5vTW9yZUVuZENvbW1lbnQgbG9naWNcbiAgICAgICAgICBpZiAoIXN0YXRlLm5vTW9yZUdUKSB7XG4gICAgICAgICAgICBmb3IgKHAgPSBwb3MgKyAxOyBwIDwgZW5kOyBwKyspIHtcbiAgICAgICAgICAgICAgaWYgKHBhcnRzW3BdID09PSAnPicpIHsgYnJlYWs7IH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChwIDwgZW5kKSB7XG4gICAgICAgICAgICAgIHBvcyA9IHAgKyAxO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc3RhdGUubm9Nb3JlR1QgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc3RhdGUubm9Nb3JlR1QpIHtcbiAgICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgICBoLnBjZGF0YSgnJmx0Oz8nLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgICAgICAgIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBwb3MsIHN0YXRlLCBwYXJhbSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnPic6XG4gICAgICAgICAgaWYgKGgucGNkYXRhKSB7XG4gICAgICAgICAgICBoLnBjZGF0YShcIiZndDtcIiwgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICcnOlxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIGlmIChoLnBjZGF0YSkge1xuICAgICAgICAgICAgaC5wY2RhdGEoY3VycmVudCwgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICAgICAgY29udGludWF0aW9uTWFrZXIoaCwgcGFydHMsIHBvcywgc3RhdGUsIHBhcmFtKSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoaC5lbmREb2MpIHsgaC5lbmREb2MocGFyYW0pOyB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgaWYgKGUgIT09IGNvbnRpbnVhdGlvbk1hcmtlcikgeyB0aHJvdyBlOyB9XG4gICAgfVxuICB9XG5cbiAgLy8gU3BsaXQgc3RyIGludG8gcGFydHMgZm9yIHRoZSBodG1sIHBhcnNlci5cbiAgZnVuY3Rpb24gaHRtbFNwbGl0KHN0cikge1xuICAgIC8vIGNhbid0IGhvaXN0IHRoaXMgb3V0IG9mIHRoZSBmdW5jdGlvbiBiZWNhdXNlIG9mIHRoZSByZS5leGVjIGxvb3AuXG4gICAgdmFyIHJlID0gLyg8XFwvfDxcXCEtLXw8WyE/XXxbJjw+XSkvZztcbiAgICBzdHIgKz0gJyc7XG4gICAgaWYgKHNwbGl0V2lsbENhcHR1cmUpIHtcbiAgICAgIHJldHVybiBzdHIuc3BsaXQocmUpO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgcGFydHMgPSBbXTtcbiAgICAgIHZhciBsYXN0UG9zID0gMDtcbiAgICAgIHZhciBtO1xuICAgICAgd2hpbGUgKChtID0gcmUuZXhlYyhzdHIpKSAhPT0gbnVsbCkge1xuICAgICAgICBwYXJ0cy5wdXNoKHN0ci5zdWJzdHJpbmcobGFzdFBvcywgbS5pbmRleCkpO1xuICAgICAgICBwYXJ0cy5wdXNoKG1bMF0pO1xuICAgICAgICBsYXN0UG9zID0gbS5pbmRleCArIG1bMF0ubGVuZ3RoO1xuICAgICAgfVxuICAgICAgcGFydHMucHVzaChzdHIuc3Vic3RyaW5nKGxhc3RQb3MpKTtcbiAgICAgIHJldHVybiBwYXJ0cztcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBwYXJzZUVuZFRhZyhwYXJ0cywgcG9zLCBoLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLCBzdGF0ZSkge1xuICAgIHZhciB0YWcgPSBwYXJzZVRhZ0FuZEF0dHJzKHBhcnRzLCBwb3MpO1xuICAgIC8vIGRyb3AgdW5jbG9zZWQgdGFnc1xuICAgIGlmICghdGFnKSB7IHJldHVybiBwYXJ0cy5sZW5ndGg7IH1cbiAgICBpZiAoaC5lbmRUYWcpIHtcbiAgICAgIGguZW5kVGFnKHRhZy5uYW1lLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcG9zLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICB9XG4gICAgcmV0dXJuIHRhZy5uZXh0O1xuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VTdGFydFRhZyhwYXJ0cywgcG9zLCBoLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLCBzdGF0ZSkge1xuICAgIHZhciB0YWcgPSBwYXJzZVRhZ0FuZEF0dHJzKHBhcnRzLCBwb3MpO1xuICAgIC8vIGRyb3AgdW5jbG9zZWQgdGFnc1xuICAgIGlmICghdGFnKSB7IHJldHVybiBwYXJ0cy5sZW5ndGg7IH1cbiAgICBpZiAoaC5zdGFydFRhZykge1xuICAgICAgaC5zdGFydFRhZyh0YWcubmFtZSwgdGFnLmF0dHJzLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgdGFnLm5leHQsIHN0YXRlLCBwYXJhbSkpO1xuICAgIH1cbiAgICAvLyB0YWdzIGxpa2UgPHNjcmlwdD4gYW5kIDx0ZXh0YXJlYT4gaGF2ZSBzcGVjaWFsIHBhcnNpbmdcbiAgICBpZiAodGFnLmVmbGFncyAmIEVGTEFHU19URVhUKSB7XG4gICAgICByZXR1cm4gcGFyc2VUZXh0KHBhcnRzLCB0YWcsIGgsIHBhcmFtLCBjb250aW51YXRpb25NYXJrZXIsIHN0YXRlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHRhZy5uZXh0O1xuICAgIH1cbiAgfVxuXG4gIHZhciBlbmRUYWdSZSA9IHt9O1xuXG4gIC8vIFRhZ3MgbGlrZSA8c2NyaXB0PiBhbmQgPHRleHRhcmVhPiBhcmUgZmxhZ2dlZCBhcyBDREFUQSBvciBSQ0RBVEEsXG4gIC8vIHdoaWNoIG1lYW5zIGV2ZXJ5dGhpbmcgaXMgdGV4dCB1bnRpbCB3ZSBzZWUgdGhlIGNvcnJlY3QgY2xvc2luZyB0YWcuXG4gIGZ1bmN0aW9uIHBhcnNlVGV4dChwYXJ0cywgdGFnLCBoLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLCBzdGF0ZSkge1xuICAgIHZhciBlbmQgPSBwYXJ0cy5sZW5ndGg7XG4gICAgaWYgKCFlbmRUYWdSZS5oYXNPd25Qcm9wZXJ0eSh0YWcubmFtZSkpIHtcbiAgICAgIGVuZFRhZ1JlW3RhZy5uYW1lXSA9IG5ldyBSZWdFeHAoJ14nICsgdGFnLm5hbWUgKyAnKD86W1xcXFxzXFxcXC9dfCQpJywgJ2knKTtcbiAgICB9XG4gICAgdmFyIHJlID0gZW5kVGFnUmVbdGFnLm5hbWVdO1xuICAgIHZhciBmaXJzdCA9IHRhZy5uZXh0O1xuICAgIHZhciBwID0gdGFnLm5leHQgKyAxO1xuICAgIGZvciAoOyBwIDwgZW5kOyBwKyspIHtcbiAgICAgIGlmIChwYXJ0c1twIC0gMV0gPT09ICc8XFwvJyAmJiByZS50ZXN0KHBhcnRzW3BdKSkgeyBicmVhazsgfVxuICAgIH1cbiAgICBpZiAocCA8IGVuZCkgeyBwIC09IDE7IH1cbiAgICB2YXIgYnVmID0gcGFydHMuc2xpY2UoZmlyc3QsIHApLmpvaW4oJycpO1xuICAgIGlmICh0YWcuZWZsYWdzICYgaHRtbDQuZWZsYWdzWydDREFUQSddKSB7XG4gICAgICBpZiAoaC5jZGF0YSkge1xuICAgICAgICBoLmNkYXRhKGJ1ZiwgcGFyYW0sIGNvbnRpbnVhdGlvbk1hcmtlcixcbiAgICAgICAgICBjb250aW51YXRpb25NYWtlcihoLCBwYXJ0cywgcCwgc3RhdGUsIHBhcmFtKSk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0YWcuZWZsYWdzICYgaHRtbDQuZWZsYWdzWydSQ0RBVEEnXSkge1xuICAgICAgaWYgKGgucmNkYXRhKSB7XG4gICAgICAgIGgucmNkYXRhKG5vcm1hbGl6ZVJDRGF0YShidWYpLCBwYXJhbSwgY29udGludWF0aW9uTWFya2VyLFxuICAgICAgICAgIGNvbnRpbnVhdGlvbk1ha2VyKGgsIHBhcnRzLCBwLCBzdGF0ZSwgcGFyYW0pKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdidWcnKTtcbiAgICB9XG4gICAgcmV0dXJuIHA7XG4gIH1cblxuICAvLyBhdCB0aGlzIHBvaW50LCBwYXJ0c1twb3MtMV0gaXMgZWl0aGVyIFwiPFwiIG9yIFwiPFxcL1wiLlxuICBmdW5jdGlvbiBwYXJzZVRhZ0FuZEF0dHJzKHBhcnRzLCBwb3MpIHtcbiAgICB2YXIgbSA9IC9eKFstXFx3Ol0rKS8uZXhlYyhwYXJ0c1twb3NdKTtcbiAgICB2YXIgdGFnID0ge307XG4gICAgdGFnLm5hbWUgPSBtWzFdLnRvTG93ZXJDYXNlKCk7XG4gICAgdGFnLmVmbGFncyA9IGh0bWw0LkVMRU1FTlRTW3RhZy5uYW1lXTtcbiAgICB2YXIgYnVmID0gcGFydHNbcG9zXS5zdWJzdHIobVswXS5sZW5ndGgpO1xuICAgIC8vIEZpbmQgdGhlIG5leHQgJz4nLiAgV2Ugb3B0aW1pc3RpY2FsbHkgYXNzdW1lIHRoaXMgJz4nIGlzIG5vdCBpbiBhXG4gICAgLy8gcXVvdGVkIGNvbnRleHQsIGFuZCBmdXJ0aGVyIGRvd24gd2UgZml4IHRoaW5ncyB1cCBpZiBpdCB0dXJucyBvdXQgdG9cbiAgICAvLyBiZSBxdW90ZWQuXG4gICAgdmFyIHAgPSBwb3MgKyAxO1xuICAgIHZhciBlbmQgPSBwYXJ0cy5sZW5ndGg7XG4gICAgZm9yICg7IHAgPCBlbmQ7IHArKykge1xuICAgICAgaWYgKHBhcnRzW3BdID09PSAnPicpIHsgYnJlYWs7IH1cbiAgICAgIGJ1ZiArPSBwYXJ0c1twXTtcbiAgICB9XG4gICAgaWYgKGVuZCA8PSBwKSB7IHJldHVybiB2b2lkIDA7IH1cbiAgICB2YXIgYXR0cnMgPSBbXTtcbiAgICB3aGlsZSAoYnVmICE9PSAnJykge1xuICAgICAgbSA9IEFUVFJfUkUuZXhlYyhidWYpO1xuICAgICAgaWYgKCFtKSB7XG4gICAgICAgIC8vIE5vIGF0dHJpYnV0ZSBmb3VuZDogc2tpcCBnYXJiYWdlXG4gICAgICAgIGJ1ZiA9IGJ1Zi5yZXBsYWNlKC9eW1xcc1xcU11bXmEtelxcc10qLywgJycpO1xuXG4gICAgICB9IGVsc2UgaWYgKChtWzRdICYmICFtWzVdKSB8fCAobVs2XSAmJiAhbVs3XSkpIHtcbiAgICAgICAgLy8gVW50ZXJtaW5hdGVkIHF1b3RlOiBzbHVycCB0byB0aGUgbmV4dCB1bnF1b3RlZCAnPidcbiAgICAgICAgdmFyIHF1b3RlID0gbVs0XSB8fCBtWzZdO1xuICAgICAgICB2YXIgc2F3UXVvdGUgPSBmYWxzZTtcbiAgICAgICAgdmFyIGFidWYgPSBbYnVmLCBwYXJ0c1twKytdXTtcbiAgICAgICAgZm9yICg7IHAgPCBlbmQ7IHArKykge1xuICAgICAgICAgIGlmIChzYXdRdW90ZSkge1xuICAgICAgICAgICAgaWYgKHBhcnRzW3BdID09PSAnPicpIHsgYnJlYWs7IH1cbiAgICAgICAgICB9IGVsc2UgaWYgKDAgPD0gcGFydHNbcF0uaW5kZXhPZihxdW90ZSkpIHtcbiAgICAgICAgICAgIHNhd1F1b3RlID0gdHJ1ZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgYWJ1Zi5wdXNoKHBhcnRzW3BdKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBTbHVycCBmYWlsZWQ6IGxvc2UgdGhlIGdhcmJhZ2VcbiAgICAgICAgaWYgKGVuZCA8PSBwKSB7IGJyZWFrOyB9XG4gICAgICAgIC8vIE90aGVyd2lzZSByZXRyeSBhdHRyaWJ1dGUgcGFyc2luZ1xuICAgICAgICBidWYgPSBhYnVmLmpvaW4oJycpO1xuICAgICAgICBjb250aW51ZTtcblxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gV2UgaGF2ZSBhbiBhdHRyaWJ1dGVcbiAgICAgICAgdmFyIGFOYW1lID0gbVsxXS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICB2YXIgYVZhbHVlID0gbVsyXSA/IGRlY29kZVZhbHVlKG1bM10pIDogJyc7XG4gICAgICAgIGF0dHJzLnB1c2goYU5hbWUsIGFWYWx1ZSk7XG4gICAgICAgIGJ1ZiA9IGJ1Zi5zdWJzdHIobVswXS5sZW5ndGgpO1xuICAgICAgfVxuICAgIH1cbiAgICB0YWcuYXR0cnMgPSBhdHRycztcbiAgICB0YWcubmV4dCA9IHAgKyAxO1xuICAgIHJldHVybiB0YWc7XG4gIH1cblxuICBmdW5jdGlvbiBkZWNvZGVWYWx1ZSh2KSB7XG4gICAgdmFyIHEgPSB2LmNoYXJDb2RlQXQoMCk7XG4gICAgaWYgKHEgPT09IDB4MjIgfHwgcSA9PT0gMHgyNykgeyAvLyBcIiBvciAnXG4gICAgICB2ID0gdi5zdWJzdHIoMSwgdi5sZW5ndGggLSAyKTtcbiAgICB9XG4gICAgcmV0dXJuIHVuZXNjYXBlRW50aXRpZXMoc3RyaXBOVUxzKHYpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGEgZnVuY3Rpb24gdGhhdCBzdHJpcHMgdW5zYWZlIHRhZ3MgYW5kIGF0dHJpYnV0ZXMgZnJvbSBodG1sLlxuICAgKiBAcGFyYW0ge2Z1bmN0aW9uKHN0cmluZywgQXJyYXkuPHN0cmluZz4pOiA/QXJyYXkuPHN0cmluZz59IHRhZ1BvbGljeVxuICAgKiAgICAgQSBmdW5jdGlvbiB0aGF0IHRha2VzICh0YWdOYW1lLCBhdHRyaWJzW10pLCB3aGVyZSB0YWdOYW1lIGlzIGEga2V5IGluXG4gICAqICAgICBodG1sNC5FTEVNRU5UUyBhbmQgYXR0cmlicyBpcyBhbiBhcnJheSBvZiBhbHRlcm5hdGluZyBhdHRyaWJ1dGUgbmFtZXNcbiAgICogICAgIGFuZCB2YWx1ZXMuICBJdCBzaG91bGQgcmV0dXJuIGEgcmVjb3JkIChhcyBmb2xsb3dzKSwgb3IgbnVsbCB0byBkZWxldGVcbiAgICogICAgIHRoZSBlbGVtZW50LiAgSXQncyBva2F5IGZvciB0YWdQb2xpY3kgdG8gbW9kaWZ5IHRoZSBhdHRyaWJzIGFycmF5LFxuICAgKiAgICAgYnV0IHRoZSBzYW1lIGFycmF5IGlzIHJldXNlZCwgc28gaXQgc2hvdWxkIG5vdCBiZSBoZWxkIGJldHdlZW4gY2FsbHMuXG4gICAqICAgICBSZWNvcmQga2V5czpcbiAgICogICAgICAgIGF0dHJpYnM6IChyZXF1aXJlZCkgU2FuaXRpemVkIGF0dHJpYnV0ZXMgYXJyYXkuXG4gICAqICAgICAgICB0YWdOYW1lOiBSZXBsYWNlbWVudCB0YWcgbmFtZS5cbiAgICogQHJldHVybiB7ZnVuY3Rpb24oc3RyaW5nLCBBcnJheSl9IEEgZnVuY3Rpb24gdGhhdCBzYW5pdGl6ZXMgYSBzdHJpbmcgb2ZcbiAgICogICAgIEhUTUwgYW5kIGFwcGVuZHMgcmVzdWx0IHN0cmluZ3MgdG8gdGhlIHNlY29uZCBhcmd1bWVudCwgYW4gYXJyYXkuXG4gICAqL1xuICBmdW5jdGlvbiBtYWtlSHRtbFNhbml0aXplcih0YWdQb2xpY3kpIHtcbiAgICB2YXIgc3RhY2s7XG4gICAgdmFyIGlnbm9yaW5nO1xuICAgIHZhciBlbWl0ID0gZnVuY3Rpb24gKHRleHQsIG91dCkge1xuICAgICAgaWYgKCFpZ25vcmluZykgeyBvdXQucHVzaCh0ZXh0KTsgfVxuICAgIH07XG4gICAgcmV0dXJuIG1ha2VTYXhQYXJzZXIoe1xuICAgICAgJ3N0YXJ0RG9jJzogZnVuY3Rpb24oXykge1xuICAgICAgICBzdGFjayA9IFtdO1xuICAgICAgICBpZ25vcmluZyA9IGZhbHNlO1xuICAgICAgfSxcbiAgICAgICdzdGFydFRhZyc6IGZ1bmN0aW9uKHRhZ05hbWVPcmlnLCBhdHRyaWJzLCBvdXQpIHtcbiAgICAgICAgaWYgKGlnbm9yaW5nKSB7IHJldHVybjsgfVxuICAgICAgICBpZiAoIWh0bWw0LkVMRU1FTlRTLmhhc093blByb3BlcnR5KHRhZ05hbWVPcmlnKSkgeyByZXR1cm47IH1cbiAgICAgICAgdmFyIGVmbGFnc09yaWcgPSBodG1sNC5FTEVNRU5UU1t0YWdOYW1lT3JpZ107XG4gICAgICAgIGlmIChlZmxhZ3NPcmlnICYgaHRtbDQuZWZsYWdzWydGT0xEQUJMRSddKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGRlY2lzaW9uID0gdGFnUG9saWN5KHRhZ05hbWVPcmlnLCBhdHRyaWJzKTtcbiAgICAgICAgaWYgKCFkZWNpc2lvbikge1xuICAgICAgICAgIGlnbm9yaW5nID0gIShlZmxhZ3NPcmlnICYgaHRtbDQuZWZsYWdzWydFTVBUWSddKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGRlY2lzaW9uICE9PSAnb2JqZWN0Jykge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcigndGFnUG9saWN5IGRpZCBub3QgcmV0dXJuIG9iamVjdCAob2xkIEFQST8pJyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCdhdHRyaWJzJyBpbiBkZWNpc2lvbikge1xuICAgICAgICAgIGF0dHJpYnMgPSBkZWNpc2lvblsnYXR0cmlicyddO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcigndGFnUG9saWN5IGdhdmUgbm8gYXR0cmlicycpO1xuICAgICAgICB9XG4gICAgICAgIHZhciBlZmxhZ3NSZXA7XG4gICAgICAgIHZhciB0YWdOYW1lUmVwO1xuICAgICAgICBpZiAoJ3RhZ05hbWUnIGluIGRlY2lzaW9uKSB7XG4gICAgICAgICAgdGFnTmFtZVJlcCA9IGRlY2lzaW9uWyd0YWdOYW1lJ107XG4gICAgICAgICAgZWZsYWdzUmVwID0gaHRtbDQuRUxFTUVOVFNbdGFnTmFtZVJlcF07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGFnTmFtZVJlcCA9IHRhZ05hbWVPcmlnO1xuICAgICAgICAgIGVmbGFnc1JlcCA9IGVmbGFnc09yaWc7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVE9ETyhtaWtlc2FtdWVsKTogcmVseWluZyBvbiB0YWdQb2xpY3kgbm90IHRvIGluc2VydCB1bnNhZmVcbiAgICAgICAgLy8gYXR0cmlidXRlIG5hbWVzLlxuXG4gICAgICAgIC8vIElmIHRoaXMgaXMgYW4gb3B0aW9uYWwtZW5kLXRhZyBlbGVtZW50IGFuZCBlaXRoZXIgdGhpcyBlbGVtZW50IG9yIGl0c1xuICAgICAgICAvLyBwcmV2aW91cyBsaWtlIHNpYmxpbmcgd2FzIHJld3JpdHRlbiwgdGhlbiBpbnNlcnQgYSBjbG9zZSB0YWcgdG9cbiAgICAgICAgLy8gcHJlc2VydmUgc3RydWN0dXJlLlxuICAgICAgICBpZiAoZWZsYWdzT3JpZyAmIGh0bWw0LmVmbGFnc1snT1BUSU9OQUxfRU5EVEFHJ10pIHtcbiAgICAgICAgICB2YXIgb25TdGFjayA9IHN0YWNrW3N0YWNrLmxlbmd0aCAtIDFdO1xuICAgICAgICAgIGlmIChvblN0YWNrICYmIG9uU3RhY2sub3JpZyA9PT0gdGFnTmFtZU9yaWcgJiZcbiAgICAgICAgICAgICAgKG9uU3RhY2sucmVwICE9PSB0YWdOYW1lUmVwIHx8IHRhZ05hbWVPcmlnICE9PSB0YWdOYW1lUmVwKSkge1xuICAgICAgICAgICAgICAgIG91dC5wdXNoKCc8XFwvJywgb25TdGFjay5yZXAsICc+Jyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCEoZWZsYWdzT3JpZyAmIGh0bWw0LmVmbGFnc1snRU1QVFknXSkpIHtcbiAgICAgICAgICBzdGFjay5wdXNoKHtvcmlnOiB0YWdOYW1lT3JpZywgcmVwOiB0YWdOYW1lUmVwfSk7XG4gICAgICAgIH1cblxuICAgICAgICBvdXQucHVzaCgnPCcsIHRhZ05hbWVSZXApO1xuICAgICAgICBmb3IgKHZhciBpID0gMCwgbiA9IGF0dHJpYnMubGVuZ3RoOyBpIDwgbjsgaSArPSAyKSB7XG4gICAgICAgICAgdmFyIGF0dHJpYk5hbWUgPSBhdHRyaWJzW2ldLFxuICAgICAgICAgICAgICB2YWx1ZSA9IGF0dHJpYnNbaSArIDFdO1xuICAgICAgICAgIGlmICh2YWx1ZSAhPT0gbnVsbCAmJiB2YWx1ZSAhPT0gdm9pZCAwKSB7XG4gICAgICAgICAgICBvdXQucHVzaCgnICcsIGF0dHJpYk5hbWUsICc9XCInLCBlc2NhcGVBdHRyaWIodmFsdWUpLCAnXCInKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgb3V0LnB1c2goJz4nKTtcblxuICAgICAgICBpZiAoKGVmbGFnc09yaWcgJiBodG1sNC5lZmxhZ3NbJ0VNUFRZJ10pXG4gICAgICAgICAgICAmJiAhKGVmbGFnc1JlcCAmIGh0bWw0LmVmbGFnc1snRU1QVFknXSkpIHtcbiAgICAgICAgICAvLyByZXBsYWNlbWVudCBpcyBub24tZW1wdHksIHN5bnRoZXNpemUgZW5kIHRhZ1xuICAgICAgICAgIG91dC5wdXNoKCc8XFwvJywgdGFnTmFtZVJlcCwgJz4nKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgICdlbmRUYWcnOiBmdW5jdGlvbih0YWdOYW1lLCBvdXQpIHtcbiAgICAgICAgaWYgKGlnbm9yaW5nKSB7XG4gICAgICAgICAgaWdub3JpbmcgPSBmYWxzZTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFodG1sNC5FTEVNRU5UUy5oYXNPd25Qcm9wZXJ0eSh0YWdOYW1lKSkgeyByZXR1cm47IH1cbiAgICAgICAgdmFyIGVmbGFncyA9IGh0bWw0LkVMRU1FTlRTW3RhZ05hbWVdO1xuICAgICAgICBpZiAoIShlZmxhZ3MgJiAoaHRtbDQuZWZsYWdzWydFTVBUWSddIHwgaHRtbDQuZWZsYWdzWydGT0xEQUJMRSddKSkpIHtcbiAgICAgICAgICB2YXIgaW5kZXg7XG4gICAgICAgICAgaWYgKGVmbGFncyAmIGh0bWw0LmVmbGFnc1snT1BUSU9OQUxfRU5EVEFHJ10pIHtcbiAgICAgICAgICAgIGZvciAoaW5kZXggPSBzdGFjay5sZW5ndGg7IC0taW5kZXggPj0gMDspIHtcbiAgICAgICAgICAgICAgdmFyIHN0YWNrRWxPcmlnVGFnID0gc3RhY2tbaW5kZXhdLm9yaWc7XG4gICAgICAgICAgICAgIGlmIChzdGFja0VsT3JpZ1RhZyA9PT0gdGFnTmFtZSkgeyBicmVhazsgfVxuICAgICAgICAgICAgICBpZiAoIShodG1sNC5FTEVNRU5UU1tzdGFja0VsT3JpZ1RhZ10gJlxuICAgICAgICAgICAgICAgICAgICBodG1sNC5lZmxhZ3NbJ09QVElPTkFMX0VORFRBRyddKSkge1xuICAgICAgICAgICAgICAgIC8vIERvbid0IHBvcCBub24gb3B0aW9uYWwgZW5kIHRhZ3MgbG9va2luZyBmb3IgYSBtYXRjaC5cbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZm9yIChpbmRleCA9IHN0YWNrLmxlbmd0aDsgLS1pbmRleCA+PSAwOykge1xuICAgICAgICAgICAgICBpZiAoc3RhY2tbaW5kZXhdLm9yaWcgPT09IHRhZ05hbWUpIHsgYnJlYWs7IH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGluZGV4IDwgMCkgeyByZXR1cm47IH0gIC8vIE5vdCBvcGVuZWQuXG4gICAgICAgICAgZm9yICh2YXIgaSA9IHN0YWNrLmxlbmd0aDsgLS1pID4gaW5kZXg7KSB7XG4gICAgICAgICAgICB2YXIgc3RhY2tFbFJlcFRhZyA9IHN0YWNrW2ldLnJlcDtcbiAgICAgICAgICAgIGlmICghKGh0bWw0LkVMRU1FTlRTW3N0YWNrRWxSZXBUYWddICZcbiAgICAgICAgICAgICAgICAgIGh0bWw0LmVmbGFnc1snT1BUSU9OQUxfRU5EVEFHJ10pKSB7XG4gICAgICAgICAgICAgIG91dC5wdXNoKCc8XFwvJywgc3RhY2tFbFJlcFRhZywgJz4nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGluZGV4IDwgc3RhY2subGVuZ3RoKSB7XG4gICAgICAgICAgICB0YWdOYW1lID0gc3RhY2tbaW5kZXhdLnJlcDtcbiAgICAgICAgICB9XG4gICAgICAgICAgc3RhY2subGVuZ3RoID0gaW5kZXg7XG4gICAgICAgICAgb3V0LnB1c2goJzxcXC8nLCB0YWdOYW1lLCAnPicpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgJ3BjZGF0YSc6IGVtaXQsXG4gICAgICAncmNkYXRhJzogZW1pdCxcbiAgICAgICdjZGF0YSc6IGVtaXQsXG4gICAgICAnZW5kRG9jJzogZnVuY3Rpb24ob3V0KSB7XG4gICAgICAgIGZvciAoOyBzdGFjay5sZW5ndGg7IHN0YWNrLmxlbmd0aC0tKSB7XG4gICAgICAgICAgb3V0LnB1c2goJzxcXC8nLCBzdGFja1tzdGFjay5sZW5ndGggLSAxXS5yZXAsICc+Jyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIHZhciBBTExPV0VEX1VSSV9TQ0hFTUVTID0gL14oPzpodHRwcz98bWFpbHRvfGRhdGEpJC9pO1xuXG4gIGZ1bmN0aW9uIHNhZmVVcmkodXJpLCBlZmZlY3QsIGx0eXBlLCBoaW50cywgbmFpdmVVcmlSZXdyaXRlcikge1xuICAgIGlmICghbmFpdmVVcmlSZXdyaXRlcikgeyByZXR1cm4gbnVsbDsgfVxuICAgIHRyeSB7XG4gICAgICB2YXIgcGFyc2VkID0gVVJJLnBhcnNlKCcnICsgdXJpKTtcbiAgICAgIGlmIChwYXJzZWQpIHtcbiAgICAgICAgaWYgKCFwYXJzZWQuaGFzU2NoZW1lKCkgfHxcbiAgICAgICAgICAgIEFMTE9XRURfVVJJX1NDSEVNRVMudGVzdChwYXJzZWQuZ2V0U2NoZW1lKCkpKSB7XG4gICAgICAgICAgdmFyIHNhZmUgPSBuYWl2ZVVyaVJld3JpdGVyKHBhcnNlZCwgZWZmZWN0LCBsdHlwZSwgaGludHMpO1xuICAgICAgICAgIHJldHVybiBzYWZlID8gc2FmZS50b1N0cmluZygpIDogbnVsbDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxvZyhsb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCBuZXdWYWx1ZSkge1xuICAgIGlmICghYXR0cmliTmFtZSkge1xuICAgICAgbG9nZ2VyKHRhZ05hbWUgKyBcIiByZW1vdmVkXCIsIHtcbiAgICAgICAgY2hhbmdlOiBcInJlbW92ZWRcIixcbiAgICAgICAgdGFnTmFtZTogdGFnTmFtZVxuICAgICAgfSk7XG4gICAgfVxuICAgIGlmIChvbGRWYWx1ZSAhPT0gbmV3VmFsdWUpIHtcbiAgICAgIHZhciBjaGFuZ2VkID0gXCJjaGFuZ2VkXCI7XG4gICAgICBpZiAob2xkVmFsdWUgJiYgIW5ld1ZhbHVlKSB7XG4gICAgICAgIGNoYW5nZWQgPSBcInJlbW92ZWRcIjtcbiAgICAgIH0gZWxzZSBpZiAoIW9sZFZhbHVlICYmIG5ld1ZhbHVlKSAge1xuICAgICAgICBjaGFuZ2VkID0gXCJhZGRlZFwiO1xuICAgICAgfVxuICAgICAgbG9nZ2VyKHRhZ05hbWUgKyBcIi5cIiArIGF0dHJpYk5hbWUgKyBcIiBcIiArIGNoYW5nZWQsIHtcbiAgICAgICAgY2hhbmdlOiBjaGFuZ2VkLFxuICAgICAgICB0YWdOYW1lOiB0YWdOYW1lLFxuICAgICAgICBhdHRyaWJOYW1lOiBhdHRyaWJOYW1lLFxuICAgICAgICBvbGRWYWx1ZTogb2xkVmFsdWUsXG4gICAgICAgIG5ld1ZhbHVlOiBuZXdWYWx1ZVxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gbG9va3VwQXR0cmlidXRlKG1hcCwgdGFnTmFtZSwgYXR0cmliTmFtZSkge1xuICAgIHZhciBhdHRyaWJLZXk7XG4gICAgYXR0cmliS2V5ID0gdGFnTmFtZSArICc6OicgKyBhdHRyaWJOYW1lO1xuICAgIGlmIChtYXAuaGFzT3duUHJvcGVydHkoYXR0cmliS2V5KSkge1xuICAgICAgcmV0dXJuIG1hcFthdHRyaWJLZXldO1xuICAgIH1cbiAgICBhdHRyaWJLZXkgPSAnKjo6JyArIGF0dHJpYk5hbWU7XG4gICAgaWYgKG1hcC5oYXNPd25Qcm9wZXJ0eShhdHRyaWJLZXkpKSB7XG4gICAgICByZXR1cm4gbWFwW2F0dHJpYktleV07XG4gICAgfVxuICAgIHJldHVybiB2b2lkIDA7XG4gIH1cbiAgZnVuY3Rpb24gZ2V0QXR0cmlidXRlVHlwZSh0YWdOYW1lLCBhdHRyaWJOYW1lKSB7XG4gICAgcmV0dXJuIGxvb2t1cEF0dHJpYnV0ZShodG1sNC5BVFRSSUJTLCB0YWdOYW1lLCBhdHRyaWJOYW1lKTtcbiAgfVxuICBmdW5jdGlvbiBnZXRMb2FkZXJUeXBlKHRhZ05hbWUsIGF0dHJpYk5hbWUpIHtcbiAgICByZXR1cm4gbG9va3VwQXR0cmlidXRlKGh0bWw0LkxPQURFUlRZUEVTLCB0YWdOYW1lLCBhdHRyaWJOYW1lKTtcbiAgfVxuICBmdW5jdGlvbiBnZXRVcmlFZmZlY3QodGFnTmFtZSwgYXR0cmliTmFtZSkge1xuICAgIHJldHVybiBsb29rdXBBdHRyaWJ1dGUoaHRtbDQuVVJJRUZGRUNUUywgdGFnTmFtZSwgYXR0cmliTmFtZSk7XG4gIH1cblxuICAvKipcbiAgICogU2FuaXRpemVzIGF0dHJpYnV0ZXMgb24gYW4gSFRNTCB0YWcuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0YWdOYW1lIEFuIEhUTUwgdGFnIG5hbWUgaW4gbG93ZXJjYXNlLlxuICAgKiBAcGFyYW0ge0FycmF5Ljw/c3RyaW5nPn0gYXR0cmlicyBBbiBhcnJheSBvZiBhbHRlcm5hdGluZyBuYW1lcyBhbmQgdmFsdWVzLlxuICAgKiBAcGFyYW0gez9mdW5jdGlvbig/c3RyaW5nKTogP3N0cmluZ30gb3B0X25haXZlVXJpUmV3cml0ZXIgQSB0cmFuc2Zvcm0gdG9cbiAgICogICAgIGFwcGx5IHRvIFVSSSBhdHRyaWJ1dGVzOyBpdCBjYW4gcmV0dXJuIGEgbmV3IHN0cmluZyB2YWx1ZSwgb3IgbnVsbCB0b1xuICAgKiAgICAgZGVsZXRlIHRoZSBhdHRyaWJ1dGUuICBJZiB1bnNwZWNpZmllZCwgVVJJIGF0dHJpYnV0ZXMgYXJlIGRlbGV0ZWQuXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb24oP3N0cmluZyk6ID9zdHJpbmd9IG9wdF9ubVRva2VuUG9saWN5IEEgdHJhbnNmb3JtIHRvIGFwcGx5XG4gICAqICAgICB0byBhdHRyaWJ1dGVzIGNvbnRhaW5pbmcgSFRNTCBuYW1lcywgZWxlbWVudCBJRHMsIGFuZCBzcGFjZS1zZXBhcmF0ZWRcbiAgICogICAgIGxpc3RzIG9mIGNsYXNzZXM7IGl0IGNhbiByZXR1cm4gYSBuZXcgc3RyaW5nIHZhbHVlLCBvciBudWxsIHRvIGRlbGV0ZVxuICAgKiAgICAgdGhlIGF0dHJpYnV0ZS4gIElmIHVuc3BlY2lmaWVkLCB0aGVzZSBhdHRyaWJ1dGVzIGFyZSBrZXB0IHVuY2hhbmdlZC5cbiAgICogQHJldHVybiB7QXJyYXkuPD9zdHJpbmc+fSBUaGUgc2FuaXRpemVkIGF0dHJpYnV0ZXMgYXMgYSBsaXN0IG9mIGFsdGVybmF0aW5nXG4gICAqICAgICBuYW1lcyBhbmQgdmFsdWVzLCB3aGVyZSBhIG51bGwgdmFsdWUgbWVhbnMgdG8gb21pdCB0aGUgYXR0cmlidXRlLlxuICAgKi9cbiAgZnVuY3Rpb24gc2FuaXRpemVBdHRyaWJzKHRhZ05hbWUsIGF0dHJpYnMsXG4gICAgb3B0X25haXZlVXJpUmV3cml0ZXIsIG9wdF9ubVRva2VuUG9saWN5LCBvcHRfbG9nZ2VyKSB7XG4gICAgLy8gVE9ETyhmZWxpeDhhKTogaXQncyBvYm5veGlvdXMgdGhhdCBkb21hZG8gZHVwbGljYXRlcyBtdWNoIG9mIHRoaXNcbiAgICAvLyBUT0RPKGZlbGl4OGEpOiBtYXliZSBjb25zaXN0ZW50bHkgZW5mb3JjZSBjb25zdHJhaW50cyBsaWtlIHRhcmdldD1cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGF0dHJpYnMubGVuZ3RoOyBpICs9IDIpIHtcbiAgICAgIHZhciBhdHRyaWJOYW1lID0gYXR0cmlic1tpXTtcbiAgICAgIHZhciB2YWx1ZSA9IGF0dHJpYnNbaSArIDFdO1xuICAgICAgdmFyIG9sZFZhbHVlID0gdmFsdWU7XG4gICAgICB2YXIgYXR5cGUgPSBudWxsLCBhdHRyaWJLZXk7XG4gICAgICBpZiAoKGF0dHJpYktleSA9IHRhZ05hbWUgKyAnOjonICsgYXR0cmliTmFtZSxcbiAgICAgICAgICAgaHRtbDQuQVRUUklCUy5oYXNPd25Qcm9wZXJ0eShhdHRyaWJLZXkpKSB8fFxuICAgICAgICAgIChhdHRyaWJLZXkgPSAnKjo6JyArIGF0dHJpYk5hbWUsXG4gICAgICAgICAgIGh0bWw0LkFUVFJJQlMuaGFzT3duUHJvcGVydHkoYXR0cmliS2V5KSkpIHtcbiAgICAgICAgYXR5cGUgPSBodG1sNC5BVFRSSUJTW2F0dHJpYktleV07XG4gICAgICB9XG4gICAgICBpZiAoYXR5cGUgIT09IG51bGwpIHtcbiAgICAgICAgc3dpdGNoIChhdHlwZSkge1xuICAgICAgICAgIGNhc2UgaHRtbDQuYXR5cGVbJ05PTkUnXTogYnJlYWs7XG4gICAgICAgICAgY2FzZSBodG1sNC5hdHlwZVsnU0NSSVBUJ106XG4gICAgICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgICAgICBpZiAob3B0X2xvZ2dlcikge1xuICAgICAgICAgICAgICBsb2cob3B0X2xvZ2dlciwgdGFnTmFtZSwgYXR0cmliTmFtZSwgb2xkVmFsdWUsIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgaHRtbDQuYXR5cGVbJ1NUWUxFJ106XG4gICAgICAgICAgICBpZiAoJ3VuZGVmaW5lZCcgPT09IHR5cGVvZiBwYXJzZUNzc0RlY2xhcmF0aW9ucykge1xuICAgICAgICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgICAgICAgIGlmIChvcHRfbG9nZ2VyKSB7XG4gICAgICAgICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG5cdCAgICAgIH1cbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2YXIgc2FuaXRpemVkRGVjbGFyYXRpb25zID0gW107XG4gICAgICAgICAgICBwYXJzZUNzc0RlY2xhcmF0aW9ucyhcbiAgICAgICAgICAgICAgICB2YWx1ZSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBkZWNsYXJhdGlvbjogZnVuY3Rpb24gKHByb3BlcnR5LCB0b2tlbnMpIHtcbiAgICAgICAgICAgICAgICAgICAgdmFyIG5vcm1Qcm9wID0gcHJvcGVydHkudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgdmFyIHNjaGVtYSA9IGNzc1NjaGVtYVtub3JtUHJvcF07XG4gICAgICAgICAgICAgICAgICAgIGlmICghc2NoZW1hKSB7XG4gICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHNhbml0aXplQ3NzUHJvcGVydHkoXG4gICAgICAgICAgICAgICAgICAgICAgICBub3JtUHJvcCwgc2NoZW1hLCB0b2tlbnMsXG4gICAgICAgICAgICAgICAgICAgICAgICBvcHRfbmFpdmVVcmlSZXdyaXRlclxuICAgICAgICAgICAgICAgICAgICAgICAgPyBmdW5jdGlvbiAodXJsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHNhZmVVcmkoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVybCwgaHRtbDQudWVmZmVjdHMuU0FNRV9ET0NVTUVOVCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHRtbDQubHR5cGVzLlNBTkRCT1hFRCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiVFlQRVwiOiBcIkNTU1wiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiQ1NTX1BST1BcIjogbm9ybVByb3BcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgb3B0X25haXZlVXJpUmV3cml0ZXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICA6IG51bGwpO1xuICAgICAgICAgICAgICAgICAgICBzYW5pdGl6ZWREZWNsYXJhdGlvbnMucHVzaChwcm9wZXJ0eSArICc6ICcgKyB0b2tlbnMuam9pbignICcpKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHZhbHVlID0gc2FuaXRpemVkRGVjbGFyYXRpb25zLmxlbmd0aCA+IDAgP1xuICAgICAgICAgICAgICBzYW5pdGl6ZWREZWNsYXJhdGlvbnMuam9pbignIDsgJykgOiBudWxsO1xuICAgICAgICAgICAgaWYgKG9wdF9sb2dnZXIpIHtcbiAgICAgICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIGh0bWw0LmF0eXBlWydJRCddOlxuICAgICAgICAgIGNhc2UgaHRtbDQuYXR5cGVbJ0lEUkVGJ106XG4gICAgICAgICAgY2FzZSBodG1sNC5hdHlwZVsnSURSRUZTJ106XG4gICAgICAgICAgY2FzZSBodG1sNC5hdHlwZVsnR0xPQkFMX05BTUUnXTpcbiAgICAgICAgICBjYXNlIGh0bWw0LmF0eXBlWydMT0NBTF9OQU1FJ106XG4gICAgICAgICAgY2FzZSBodG1sNC5hdHlwZVsnQ0xBU1NFUyddOlxuICAgICAgICAgICAgdmFsdWUgPSBvcHRfbm1Ub2tlblBvbGljeSA/IG9wdF9ubVRva2VuUG9saWN5KHZhbHVlKSA6IHZhbHVlO1xuICAgICAgICAgICAgaWYgKG9wdF9sb2dnZXIpIHtcbiAgICAgICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIGh0bWw0LmF0eXBlWydVUkknXTpcbiAgICAgICAgICAgIHZhbHVlID0gc2FmZVVyaSh2YWx1ZSxcbiAgICAgICAgICAgICAgZ2V0VXJpRWZmZWN0KHRhZ05hbWUsIGF0dHJpYk5hbWUpLFxuICAgICAgICAgICAgICBnZXRMb2FkZXJUeXBlKHRhZ05hbWUsIGF0dHJpYk5hbWUpLFxuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgXCJUWVBFXCI6IFwiTUFSS1VQXCIsXG4gICAgICAgICAgICAgICAgXCJYTUxfQVRUUlwiOiBhdHRyaWJOYW1lLFxuICAgICAgICAgICAgICAgIFwiWE1MX1RBR1wiOiB0YWdOYW1lXG4gICAgICAgICAgICAgIH0sIG9wdF9uYWl2ZVVyaVJld3JpdGVyKTtcbiAgICAgICAgICAgICAgaWYgKG9wdF9sb2dnZXIpIHtcbiAgICAgICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIGh0bWw0LmF0eXBlWydVUklfRlJBR01FTlQnXTpcbiAgICAgICAgICAgIGlmICh2YWx1ZSAmJiAnIycgPT09IHZhbHVlLmNoYXJBdCgwKSkge1xuICAgICAgICAgICAgICB2YWx1ZSA9IHZhbHVlLnN1YnN0cmluZygxKTsgIC8vIHJlbW92ZSB0aGUgbGVhZGluZyAnIydcbiAgICAgICAgICAgICAgdmFsdWUgPSBvcHRfbm1Ub2tlblBvbGljeSA/IG9wdF9ubVRva2VuUG9saWN5KHZhbHVlKSA6IHZhbHVlO1xuICAgICAgICAgICAgICBpZiAodmFsdWUgIT09IG51bGwgJiYgdmFsdWUgIT09IHZvaWQgMCkge1xuICAgICAgICAgICAgICAgIHZhbHVlID0gJyMnICsgdmFsdWU7ICAvLyByZXN0b3JlIHRoZSBsZWFkaW5nICcjJ1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAob3B0X2xvZ2dlcikge1xuICAgICAgICAgICAgICBsb2cob3B0X2xvZ2dlciwgdGFnTmFtZSwgYXR0cmliTmFtZSwgb2xkVmFsdWUsIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgICAgICBpZiAob3B0X2xvZ2dlcikge1xuICAgICAgICAgICAgICBsb2cob3B0X2xvZ2dlciwgdGFnTmFtZSwgYXR0cmliTmFtZSwgb2xkVmFsdWUsIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YWx1ZSA9IG51bGw7XG4gICAgICAgIGlmIChvcHRfbG9nZ2VyKSB7XG4gICAgICAgICAgbG9nKG9wdF9sb2dnZXIsIHRhZ05hbWUsIGF0dHJpYk5hbWUsIG9sZFZhbHVlLCB2YWx1ZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGF0dHJpYnNbaSArIDFdID0gdmFsdWU7XG4gICAgfVxuICAgIHJldHVybiBhdHRyaWJzO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSB0YWcgcG9saWN5IHRoYXQgb21pdHMgYWxsIHRhZ3MgbWFya2VkIFVOU0FGRSBpbiBodG1sNC1kZWZzLmpzXG4gICAqIGFuZCBhcHBsaWVzIHRoZSBkZWZhdWx0IGF0dHJpYnV0ZSBzYW5pdGl6ZXIgd2l0aCB0aGUgc3VwcGxpZWQgcG9saWN5IGZvclxuICAgKiBVUkkgYXR0cmlidXRlcyBhbmQgTk1UT0tFTiBhdHRyaWJ1dGVzLlxuICAgKiBAcGFyYW0gez9mdW5jdGlvbig/c3RyaW5nKTogP3N0cmluZ30gb3B0X25haXZlVXJpUmV3cml0ZXIgQSB0cmFuc2Zvcm0gdG9cbiAgICogICAgIGFwcGx5IHRvIFVSSSBhdHRyaWJ1dGVzLiAgSWYgbm90IGdpdmVuLCBVUkkgYXR0cmlidXRlcyBhcmUgZGVsZXRlZC5cbiAgICogQHBhcmFtIHtmdW5jdGlvbig/c3RyaW5nKTogP3N0cmluZ30gb3B0X25tVG9rZW5Qb2xpY3kgQSB0cmFuc2Zvcm0gdG8gYXBwbHlcbiAgICogICAgIHRvIGF0dHJpYnV0ZXMgY29udGFpbmluZyBIVE1MIG5hbWVzLCBlbGVtZW50IElEcywgYW5kIHNwYWNlLXNlcGFyYXRlZFxuICAgKiAgICAgbGlzdHMgb2YgY2xhc3Nlcy4gIElmIG5vdCBnaXZlbiwgc3VjaCBhdHRyaWJ1dGVzIGFyZSBsZWZ0IHVuY2hhbmdlZC5cbiAgICogQHJldHVybiB7ZnVuY3Rpb24oc3RyaW5nLCBBcnJheS48P3N0cmluZz4pfSBBIHRhZ1BvbGljeSBzdWl0YWJsZSBmb3JcbiAgICogICAgIHBhc3NpbmcgdG8gaHRtbC5zYW5pdGl6ZS5cbiAgICovXG4gIGZ1bmN0aW9uIG1ha2VUYWdQb2xpY3koXG4gICAgb3B0X25haXZlVXJpUmV3cml0ZXIsIG9wdF9ubVRva2VuUG9saWN5LCBvcHRfbG9nZ2VyKSB7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKHRhZ05hbWUsIGF0dHJpYnMpIHtcbiAgICAgIGlmICghKGh0bWw0LkVMRU1FTlRTW3RhZ05hbWVdICYgaHRtbDQuZWZsYWdzWydVTlNBRkUnXSkpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAnYXR0cmlicyc6IHNhbml0aXplQXR0cmlicyh0YWdOYW1lLCBhdHRyaWJzLFxuICAgICAgICAgICAgb3B0X25haXZlVXJpUmV3cml0ZXIsIG9wdF9ubVRva2VuUG9saWN5LCBvcHRfbG9nZ2VyKVxuICAgICAgICB9O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKG9wdF9sb2dnZXIpIHtcbiAgICAgICAgICBsb2cob3B0X2xvZ2dlciwgdGFnTmFtZSwgdW5kZWZpbmVkLCB1bmRlZmluZWQsIHVuZGVmaW5lZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFNhbml0aXplcyBIVE1MIHRhZ3MgYW5kIGF0dHJpYnV0ZXMgYWNjb3JkaW5nIHRvIGEgZ2l2ZW4gcG9saWN5LlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRIdG1sIFRoZSBIVE1MIHRvIHNhbml0aXplLlxuICAgKiBAcGFyYW0ge2Z1bmN0aW9uKHN0cmluZywgQXJyYXkuPD9zdHJpbmc+KX0gdGFnUG9saWN5IEEgZnVuY3Rpb24gdGhhdFxuICAgKiAgICAgZGVjaWRlcyB3aGljaCB0YWdzIHRvIGFjY2VwdCBhbmQgc2FuaXRpemVzIHRoZWlyIGF0dHJpYnV0ZXMgKHNlZVxuICAgKiAgICAgbWFrZUh0bWxTYW5pdGl6ZXIgYWJvdmUgZm9yIGRldGFpbHMpLlxuICAgKiBAcmV0dXJuIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTC5cbiAgICovXG4gIGZ1bmN0aW9uIHNhbml0aXplV2l0aFBvbGljeShpbnB1dEh0bWwsIHRhZ1BvbGljeSkge1xuICAgIHZhciBvdXRwdXRBcnJheSA9IFtdO1xuICAgIG1ha2VIdG1sU2FuaXRpemVyKHRhZ1BvbGljeSkoaW5wdXRIdG1sLCBvdXRwdXRBcnJheSk7XG4gICAgcmV0dXJuIG91dHB1dEFycmF5LmpvaW4oJycpO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0cmlwcyB1bnNhZmUgdGFncyBhbmQgYXR0cmlidXRlcyBmcm9tIEhUTUwuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dEh0bWwgVGhlIEhUTUwgdG8gc2FuaXRpemUuXG4gICAqIEBwYXJhbSB7P2Z1bmN0aW9uKD9zdHJpbmcpOiA/c3RyaW5nfSBvcHRfbmFpdmVVcmlSZXdyaXRlciBBIHRyYW5zZm9ybSB0b1xuICAgKiAgICAgYXBwbHkgdG8gVVJJIGF0dHJpYnV0ZXMuICBJZiBub3QgZ2l2ZW4sIFVSSSBhdHRyaWJ1dGVzIGFyZSBkZWxldGVkLlxuICAgKiBAcGFyYW0ge2Z1bmN0aW9uKD9zdHJpbmcpOiA/c3RyaW5nfSBvcHRfbm1Ub2tlblBvbGljeSBBIHRyYW5zZm9ybSB0byBhcHBseVxuICAgKiAgICAgdG8gYXR0cmlidXRlcyBjb250YWluaW5nIEhUTUwgbmFtZXMsIGVsZW1lbnQgSURzLCBhbmQgc3BhY2Utc2VwYXJhdGVkXG4gICAqICAgICBsaXN0cyBvZiBjbGFzc2VzLiAgSWYgbm90IGdpdmVuLCBzdWNoIGF0dHJpYnV0ZXMgYXJlIGxlZnQgdW5jaGFuZ2VkLlxuICAgKi9cbiAgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXRIdG1sLFxuICAgIG9wdF9uYWl2ZVVyaVJld3JpdGVyLCBvcHRfbm1Ub2tlblBvbGljeSwgb3B0X2xvZ2dlcikge1xuICAgIHZhciB0YWdQb2xpY3kgPSBtYWtlVGFnUG9saWN5KFxuICAgICAgb3B0X25haXZlVXJpUmV3cml0ZXIsIG9wdF9ubVRva2VuUG9saWN5LCBvcHRfbG9nZ2VyKTtcbiAgICByZXR1cm4gc2FuaXRpemVXaXRoUG9saWN5KGlucHV0SHRtbCwgdGFnUG9saWN5KTtcbiAgfVxuXG4gIC8vIEV4cG9ydCBib3RoIHF1b3RlZCBhbmQgdW5xdW90ZWQgbmFtZXMgZm9yIENsb3N1cmUgbGlua2FnZS5cbiAgdmFyIGh0bWwgPSB7fTtcbiAgaHRtbC5lc2NhcGVBdHRyaWIgPSBodG1sWydlc2NhcGVBdHRyaWInXSA9IGVzY2FwZUF0dHJpYjtcbiAgaHRtbC5tYWtlSHRtbFNhbml0aXplciA9IGh0bWxbJ21ha2VIdG1sU2FuaXRpemVyJ10gPSBtYWtlSHRtbFNhbml0aXplcjtcbiAgaHRtbC5tYWtlU2F4UGFyc2VyID0gaHRtbFsnbWFrZVNheFBhcnNlciddID0gbWFrZVNheFBhcnNlcjtcbiAgaHRtbC5tYWtlVGFnUG9saWN5ID0gaHRtbFsnbWFrZVRhZ1BvbGljeSddID0gbWFrZVRhZ1BvbGljeTtcbiAgaHRtbC5ub3JtYWxpemVSQ0RhdGEgPSBodG1sWydub3JtYWxpemVSQ0RhdGEnXSA9IG5vcm1hbGl6ZVJDRGF0YTtcbiAgaHRtbC5zYW5pdGl6ZSA9IGh0bWxbJ3Nhbml0aXplJ10gPSBzYW5pdGl6ZTtcbiAgaHRtbC5zYW5pdGl6ZUF0dHJpYnMgPSBodG1sWydzYW5pdGl6ZUF0dHJpYnMnXSA9IHNhbml0aXplQXR0cmlicztcbiAgaHRtbC5zYW5pdGl6ZVdpdGhQb2xpY3kgPSBodG1sWydzYW5pdGl6ZVdpdGhQb2xpY3knXSA9IHNhbml0aXplV2l0aFBvbGljeTtcbiAgaHRtbC51bmVzY2FwZUVudGl0aWVzID0gaHRtbFsndW5lc2NhcGVFbnRpdGllcyddID0gdW5lc2NhcGVFbnRpdGllcztcbiAgcmV0dXJuIGh0bWw7XG59KShodG1sNCk7XG5cbnZhciBodG1sX3Nhbml0aXplID0gaHRtbFsnc2FuaXRpemUnXTtcblxuLy8gTG9vc2VuIHJlc3RyaWN0aW9ucyBvZiBDYWphJ3Ncbi8vIGh0bWwtc2FuaXRpemVyIHRvIGFsbG93IGZvciBzdHlsaW5nXG5odG1sNC5BVFRSSUJTWycqOjpzdHlsZSddID0gMDtcbmh0bWw0LkVMRU1FTlRTWydzdHlsZSddID0gMDtcbmh0bWw0LkFUVFJJQlNbJ2E6OnRhcmdldCddID0gMDtcbmh0bWw0LkVMRU1FTlRTWyd2aWRlbyddID0gMDtcbmh0bWw0LkFUVFJJQlNbJ3ZpZGVvOjpzcmMnXSA9IDA7XG5odG1sNC5BVFRSSUJTWyd2aWRlbzo6cG9zdGVyJ10gPSAwO1xuaHRtbDQuQVRUUklCU1sndmlkZW86OmNvbnRyb2xzJ10gPSAwO1xuaHRtbDQuRUxFTUVOVFNbJ2F1ZGlvJ10gPSAwO1xuaHRtbDQuQVRUUklCU1snYXVkaW86OnNyYyddID0gMDtcbmh0bWw0LkFUVFJJQlNbJ3ZpZGVvOjphdXRvcGxheSddID0gMDtcbmh0bWw0LkFUVFJJQlNbJ3ZpZGVvOjpjb250cm9scyddID0gMDtcblxuaWYgKHR5cGVvZiBtb2R1bGUgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgbW9kdWxlLmV4cG9ydHMgPSBodG1sX3Nhbml0aXplO1xufVxuIiwibW9kdWxlLmV4cG9ydHM9e1xuICBcImF1dGhvclwiOiBcIk1hcGJveFwiLFxuICBcIm5hbWVcIjogXCJtYXBib3guanNcIixcbiAgXCJkZXNjcmlwdGlvblwiOiBcIm1hcGJveCBqYXZhc2NyaXB0IGFwaVwiLFxuICBcInZlcnNpb25cIjogXCIzLjEuMVwiLFxuICBcImhvbWVwYWdlXCI6IFwiaHR0cDovL21hcGJveC5jb20vXCIsXG4gIFwicmVwb3NpdG9yeVwiOiB7XG4gICAgXCJ0eXBlXCI6IFwiZ2l0XCIsXG4gICAgXCJ1cmxcIjogXCJnaXQ6Ly9naXRodWIuY29tL21hcGJveC9tYXBib3guanMuZ2l0XCJcbiAgfSxcbiAgXCJtYWluXCI6IFwic3JjL2luZGV4LmpzXCIsXG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcImNvcnNsaXRlXCI6IFwiMC4wLjZcIixcbiAgICBcImlzYXJyYXlcIjogXCIwLjAuMVwiLFxuICAgIFwibGVhZmxldFwiOiBcIjEuMC4yXCIsXG4gICAgXCJtdXN0YWNoZVwiOiBcIjIuMi4xXCIsXG4gICAgXCJzYW5pdGl6ZS1jYWphXCI6IFwiMC4xLjRcIlxuICB9LFxuICBcInNjcmlwdHNcIjoge1xuICAgIFwidGVzdFwiOiBcImVzbGludCAtLW5vLWVzbGludHJjIC1jIC5lc2xpbnRyYyBzcmMgJiYgcGhhbnRvbWpzIG5vZGVfbW9kdWxlcy9tb2NoYS1waGFudG9tanMtY29yZS9tb2NoYS1waGFudG9tanMtY29yZS5qcyB0ZXN0L2luZGV4Lmh0bWxcIlxuICB9LFxuICBcImxpY2Vuc2VcIjogXCJCU0QtMy1DbGF1c2VcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge1xuICAgIFwiYnJvd3NlcmlmeVwiOiBcIl4xMy4wLjBcIixcbiAgICBcImNsZWFuLWNzc1wiOiBcIn4yLjAuN1wiLFxuICAgIFwiY3otY29udmVudGlvbmFsLWNoYW5nZWxvZ1wiOiBcIjEuMi4wXCIsXG4gICAgXCJlc2xpbnRcIjogXCJeMC4yMy4wXCIsXG4gICAgXCJleHBlY3QuanNcIjogXCIwLjMuMVwiLFxuICAgIFwiaGFwcGVuXCI6IFwiMC4xLjNcIixcbiAgICBcImxlYWZsZXQtZnVsbHNjcmVlblwiOiBcIjAuMC40XCIsXG4gICAgXCJsZWFmbGV0LWhhc2hcIjogXCIwLjIuMVwiLFxuICAgIFwibWFya2VkXCI6IFwifjAuMy4wXCIsXG4gICAgXCJtaW5pZnlpZnlcIjogXCJeNi4xLjBcIixcbiAgICBcIm1pbmltaXN0XCI6IFwiMC4wLjVcIixcbiAgICBcIm1vY2hhXCI6IFwiMi40LjVcIixcbiAgICBcIm1vY2hhLXBoYW50b21qcy1jb3JlXCI6IFwiMi4wLjFcIixcbiAgICBcInBoYW50b21qcy1wcmVidWlsdFwiOiBcIjIuMS4xMlwiLFxuICAgIFwic2lub25cIjogXCIxLjEwLjJcIlxuICB9LFxuICBcIm9wdGlvbmFsRGVwZW5kZW5jaWVzXCI6IHt9LFxuICBcImVuZ2luZXNcIjoge1xuICAgIFwibm9kZVwiOiBcIipcIlxuICB9LFxuICBcImNvbmZpZ1wiOiB7XG4gICAgXCJjb21taXRpemVuXCI6IHtcbiAgICAgIFwicGF0aFwiOiBcIi4vbm9kZV9tb2R1bGVzL2N6LWNvbnZlbnRpb25hbC1jaGFuZ2Vsb2dcIlxuICAgIH1cbiAgfVxufVxuIiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBIVFRQX1VSTDogJ2h0dHA6Ly9hLnRpbGVzLm1hcGJveC5jb20vdjQnLFxuICAgIEhUVFBTX1VSTDogJ2h0dHBzOi8vYS50aWxlcy5tYXBib3guY29tL3Y0JyxcbiAgICBGT1JDRV9IVFRQUzogZmFsc2UsXG4gICAgUkVRVUlSRV9BQ0NFU1NfVE9LRU46IHRydWVcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciB1dGlsID0gcmVxdWlyZSgnLi91dGlsJyksXG4gICAgZm9ybWF0X3VybCA9IHJlcXVpcmUoJy4vZm9ybWF0X3VybCcpLFxuICAgIHJlcXVlc3QgPSByZXF1aXJlKCcuL3JlcXVlc3QnKSxcbiAgICBtYXJrZXIgPSByZXF1aXJlKCcuL21hcmtlcicpLFxuICAgIHNpbXBsZXN0eWxlID0gcmVxdWlyZSgnLi9zaW1wbGVzdHlsZScpO1xuXG4vLyAjIGZlYXR1cmVMYXllclxuLy9cbi8vIEEgbGF5ZXIgb2YgZmVhdHVyZXMsIGxvYWRlZCBmcm9tIE1hcGJveCBvciBlbHNlLiBBZGRzIHRoZSBhYmlsaXR5XG4vLyB0byByZXNldCBmZWF0dXJlcywgZmlsdGVyIHRoZW0sIGFuZCBsb2FkIHRoZW0gZnJvbSBhIEdlb0pTT04gVVJMLlxudmFyIEZlYXR1cmVMYXllciA9IEwuRmVhdHVyZUdyb3VwLmV4dGVuZCh7XG4gICAgb3B0aW9uczoge1xuICAgICAgICBmaWx0ZXI6IGZ1bmN0aW9uKCkgeyByZXR1cm4gdHJ1ZTsgfSxcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJyksXG4gICAgICAgIHN0eWxlOiBzaW1wbGVzdHlsZS5zdHlsZSxcbiAgICAgICAgcG9wdXBPcHRpb25zOiB7IGNsb3NlQnV0dG9uOiBmYWxzZSB9XG4gICAgfSxcblxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICAgICAgTC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuXG4gICAgICAgIHRoaXMuX2xheWVycyA9IHt9O1xuXG4gICAgICAgIGlmICh0eXBlb2YgXyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHV0aWwuaWRVcmwoXywgdGhpcyk7XG4gICAgICAgIC8vIGphdmFzY3JpcHQgb2JqZWN0IG9mIFRpbGVKU09OIGRhdGFcbiAgICAgICAgfSBlbHNlIGlmIChfICYmIHR5cGVvZiBfID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgdGhpcy5zZXRHZW9KU09OKF8pO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIHNldEdlb0pTT046IGZ1bmN0aW9uKF8pIHtcbiAgICAgICAgdGhpcy5fZ2VvanNvbiA9IF87XG4gICAgICAgIHRoaXMuY2xlYXJMYXllcnMoKTtcbiAgICAgICAgdGhpcy5faW5pdGlhbGl6ZShfKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIGdldEdlb0pTT046IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fZ2VvanNvbjtcbiAgICB9LFxuXG4gICAgbG9hZFVSTDogZnVuY3Rpb24odXJsKSB7XG4gICAgICAgIGlmICh0aGlzLl9yZXF1ZXN0ICYmICdhYm9ydCcgaW4gdGhpcy5fcmVxdWVzdCkgdGhpcy5fcmVxdWVzdC5hYm9ydCgpO1xuICAgICAgICB0aGlzLl9yZXF1ZXN0ID0gcmVxdWVzdCh1cmwsIEwuYmluZChmdW5jdGlvbihlcnIsIGpzb24pIHtcbiAgICAgICAgICAgIHRoaXMuX3JlcXVlc3QgPSBudWxsO1xuICAgICAgICAgICAgaWYgKGVyciAmJiBlcnIudHlwZSAhPT0gJ2Fib3J0Jykge1xuICAgICAgICAgICAgICAgIHV0aWwubG9nKCdjb3VsZCBub3QgbG9hZCBmZWF0dXJlcyBhdCAnICsgdXJsKTtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ2Vycm9yJywge2Vycm9yOiBlcnJ9KTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoanNvbikge1xuICAgICAgICAgICAgICAgIHRoaXMuc2V0R2VvSlNPTihqc29uKTtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ3JlYWR5Jyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIHRoaXMpKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIGxvYWRJRDogZnVuY3Rpb24oaWQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubG9hZFVSTChmb3JtYXRfdXJsKCcvdjQvJyArIGlkICsgJy9mZWF0dXJlcy5qc29uJywgdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuKSk7XG4gICAgfSxcblxuICAgIHNldEZpbHRlcjogZnVuY3Rpb24oXykge1xuICAgICAgICB0aGlzLm9wdGlvbnMuZmlsdGVyID0gXztcbiAgICAgICAgaWYgKHRoaXMuX2dlb2pzb24pIHtcbiAgICAgICAgICAgIHRoaXMuY2xlYXJMYXllcnMoKTtcbiAgICAgICAgICAgIHRoaXMuX2luaXRpYWxpemUodGhpcy5fZ2VvanNvbik7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIGdldEZpbHRlcjogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLm9wdGlvbnMuZmlsdGVyO1xuICAgIH0sXG5cbiAgICBfaW5pdGlhbGl6ZTogZnVuY3Rpb24oanNvbikge1xuICAgICAgICB2YXIgZmVhdHVyZXMgPSBMLlV0aWwuaXNBcnJheShqc29uKSA/IGpzb24gOiBqc29uLmZlYXR1cmVzLFxuICAgICAgICAgICAgaSwgbGVuO1xuXG4gICAgICAgIGlmIChmZWF0dXJlcykge1xuICAgICAgICAgICAgZm9yIChpID0gMCwgbGVuID0gZmVhdHVyZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgICAgICAgICAvLyBPbmx5IGFkZCB0aGlzIGlmIGdlb21ldHJ5IG9yIGdlb21ldHJpZXMgYXJlIHNldCBhbmQgbm90IG51bGxcbiAgICAgICAgICAgICAgICBpZiAoZmVhdHVyZXNbaV0uZ2VvbWV0cmllcyB8fCBmZWF0dXJlc1tpXS5nZW9tZXRyeSB8fCBmZWF0dXJlc1tpXS5mZWF0dXJlcykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLl9pbml0aWFsaXplKGZlYXR1cmVzW2ldKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAodGhpcy5vcHRpb25zLmZpbHRlcihqc29uKSkge1xuXG4gICAgICAgICAgICB2YXIgb3B0cyA9IHthY2Nlc3NUb2tlbjogdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VufSxcbiAgICAgICAgICAgICAgICBwb2ludFRvTGF5ZXIgPSB0aGlzLm9wdGlvbnMucG9pbnRUb0xheWVyIHx8IGZ1bmN0aW9uKGZlYXR1cmUsIGxhdGxvbikge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIG1hcmtlci5zdHlsZShmZWF0dXJlLCBsYXRsb24sIG9wdHMpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgbGF5ZXIgPSBMLkdlb0pTT04uZ2VvbWV0cnlUb0xheWVyKGpzb24sIHtcbiAgICAgICAgICAgICAgICAgICAgcG9pbnRUb0xheWVyOiBwb2ludFRvTGF5ZXJcbiAgICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgICAgICBwb3B1cEh0bWwgPSBtYXJrZXIuY3JlYXRlUG9wdXAoanNvbiwgdGhpcy5vcHRpb25zLnNhbml0aXplciksXG4gICAgICAgICAgICAgICAgc3R5bGUgPSB0aGlzLm9wdGlvbnMuc3R5bGUsXG4gICAgICAgICAgICAgICAgZGVmYXVsdFN0eWxlID0gc3R5bGUgPT09IHNpbXBsZXN0eWxlLnN0eWxlO1xuXG4gICAgICAgICAgICBpZiAoc3R5bGUgJiYgJ3NldFN0eWxlJyBpbiBsYXllciAmJlxuICAgICAgICAgICAgICAgIC8vIGlmIHRoZSBzdHlsZSBtZXRob2QgaXMgdGhlIHNpbXBsZXN0eWxlIGRlZmF1bHQsIHRoZW5cbiAgICAgICAgICAgICAgICAvLyBuZXZlciBzdHlsZSBMLkNpcmNsZSBvciBMLkNpcmNsZU1hcmtlciBiZWNhdXNlXG4gICAgICAgICAgICAgICAgLy8gc2ltcGxlc3R5bGUgaGFzIG5vIHJ1bGVzIG92ZXIgdGhlbSwgb25seSBvdmVyIGdlb21ldHJ5XG4gICAgICAgICAgICAgICAgLy8gcHJpbWl0aXZlcyBkaXJlY3RseSBmcm9tIEdlb0pTT05cbiAgICAgICAgICAgICAgICAoIShkZWZhdWx0U3R5bGUgJiYgKGxheWVyIGluc3RhbmNlb2YgTC5DaXJjbGUgfHxcbiAgICAgICAgICAgICAgICAgIGxheWVyIGluc3RhbmNlb2YgTC5DaXJjbGVNYXJrZXIpKSkpIHtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHN0eWxlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgICAgIHN0eWxlID0gc3R5bGUoanNvbik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGxheWVyLnNldFN0eWxlKHN0eWxlKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbGF5ZXIuZmVhdHVyZSA9IGpzb247XG5cbiAgICAgICAgICAgIGlmIChwb3B1cEh0bWwpIHtcbiAgICAgICAgICAgICAgICBsYXllci5iaW5kUG9wdXAocG9wdXBIdG1sLCB0aGlzLm9wdGlvbnMucG9wdXBPcHRpb25zKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhpcy5hZGRMYXllcihsYXllcik7XG4gICAgICAgIH1cbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMuRmVhdHVyZUxheWVyID0gRmVhdHVyZUxheWVyO1xuXG5tb2R1bGUuZXhwb3J0cy5mZWF0dXJlTGF5ZXIgPSBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIG5ldyBGZWF0dXJlTGF5ZXIoXywgb3B0aW9ucyk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgRmVlZGJhY2sgPSBMLkNsYXNzLmV4dGVuZCh7XG4gICAgaW5jbHVkZXM6IEwuTWl4aW4uRXZlbnRzLFxuICAgIGRhdGE6IHt9LFxuICAgIHJlY29yZDogZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICBMLmV4dGVuZCh0aGlzLmRhdGEsIGRhdGEpO1xuICAgICAgICB0aGlzLmZpcmUoJ2NoYW5nZScpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IG5ldyBGZWVkYmFjaygpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgY29uZmlnID0gcmVxdWlyZSgnLi9jb25maWcnKSxcbiAgICB2ZXJzaW9uID0gcmVxdWlyZSgnLi4vcGFja2FnZS5qc29uJykudmVyc2lvbjtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbihwYXRoLCBhY2Nlc3NUb2tlbikge1xuICAgIGFjY2Vzc1Rva2VuID0gYWNjZXNzVG9rZW4gfHwgTC5tYXBib3guYWNjZXNzVG9rZW47XG5cbiAgICBpZiAoIWFjY2Vzc1Rva2VuICYmIGNvbmZpZy5SRVFVSVJFX0FDQ0VTU19UT0tFTikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0FuIEFQSSBhY2Nlc3MgdG9rZW4gaXMgcmVxdWlyZWQgdG8gdXNlIE1hcGJveC5qcy4gJyArXG4gICAgICAgICAgICAnU2VlIGh0dHBzOi8vd3d3Lm1hcGJveC5jb20vbWFwYm94LmpzL2FwaS92JyArIHZlcnNpb24gKyAnL2FwaS1hY2Nlc3MtdG9rZW5zLycpO1xuICAgIH1cblxuICAgIHZhciB1cmwgPSAoZG9jdW1lbnQubG9jYXRpb24ucHJvdG9jb2wgPT09ICdodHRwczonIHx8IGNvbmZpZy5GT1JDRV9IVFRQUykgPyBjb25maWcuSFRUUFNfVVJMIDogY29uZmlnLkhUVFBfVVJMO1xuICAgIHVybCA9IHVybC5yZXBsYWNlKC9cXC92NCQvLCAnJyk7XG4gICAgdXJsICs9IHBhdGg7XG5cbiAgICBpZiAoY29uZmlnLlJFUVVJUkVfQUNDRVNTX1RPS0VOKSB7XG4gICAgICAgIGlmIChhY2Nlc3NUb2tlblswXSA9PT0gJ3MnKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VzZSBhIHB1YmxpYyBhY2Nlc3MgdG9rZW4gKHBrLiopIHdpdGggTWFwYm94LmpzLCBub3QgYSBzZWNyZXQgYWNjZXNzIHRva2VuIChzay4qKS4gJyArXG4gICAgICAgICAgICAgICAgJ1NlZSBodHRwczovL3d3dy5tYXBib3guY29tL21hcGJveC5qcy9hcGkvdicgKyB2ZXJzaW9uICsgJy9hcGktYWNjZXNzLXRva2Vucy8nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHVybCArPSB1cmwuaW5kZXhPZignPycpICE9PSAtMSA/ICcmYWNjZXNzX3Rva2VuPScgOiAnP2FjY2Vzc190b2tlbj0nO1xuICAgICAgICB1cmwgKz0gYWNjZXNzVG9rZW47XG4gICAgfVxuXG4gICAgcmV0dXJuIHVybDtcbn07XG5cbm1vZHVsZS5leHBvcnRzLnRpbGVKU09OID0gZnVuY3Rpb24odXJsT3JNYXBJRCwgYWNjZXNzVG9rZW4pIHtcblxuICAgIGlmICh1cmxPck1hcElELmluZGV4T2YoJ21hcGJveDovL3N0eWxlcycpID09PSAwKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignU3R5bGVzIGNyZWF0ZWQgd2l0aCBNYXBib3ggU3R1ZGlvIG5lZWQgdG8gYmUgdXNlZCB3aXRoICcgK1xuICAgICAgICAgICAgJ0wubWFwYm94LnN0eWxlTGF5ZXIsIG5vdCBMLm1hcGJveC50aWxlTGF5ZXInKTtcbiAgICB9XG5cbiAgICBpZiAodXJsT3JNYXBJRC5pbmRleE9mKCcvJykgIT09IC0xKVxuICAgICAgICByZXR1cm4gdXJsT3JNYXBJRDtcblxuICAgIHZhciB1cmwgPSBtb2R1bGUuZXhwb3J0cygnL3Y0LycgKyB1cmxPck1hcElEICsgJy5qc29uJywgYWNjZXNzVG9rZW4pO1xuXG4gICAgLy8gVGlsZUpTT04gcmVxdWVzdHMgbmVlZCBhIHNlY3VyZSBmbGFnIGFwcGVuZGVkIHRvIHRoZWlyIFVSTHMgc29cbiAgICAvLyB0aGF0IHRoZSBzZXJ2ZXIga25vd3MgdG8gc2VuZCBTU0wtaWZpZWQgcmVzb3VyY2UgcmVmZXJlbmNlcy5cbiAgICBpZiAodXJsLmluZGV4T2YoJ2h0dHBzJykgPT09IDApXG4gICAgICAgIHVybCArPSAnJnNlY3VyZSc7XG5cbiAgICByZXR1cm4gdXJsO1xufTtcblxuXG5tb2R1bGUuZXhwb3J0cy5zdHlsZSA9IGZ1bmN0aW9uKHN0eWxlVVJMLCBhY2Nlc3NUb2tlbikge1xuICAgIGlmIChzdHlsZVVSTC5pbmRleE9mKCdtYXBib3g6Ly9zdHlsZXMvJykgPT09IC0xKSB0aHJvdyBuZXcgRXJyb3IoJ0luY29ycmVjdGx5IGZvcm1hdHRlZCBNYXBib3ggc3R5bGUgYXQgJyArIHN0eWxlVVJMKTtcblxuICAgIHZhciBvd25lcklEU3R5bGUgPSBzdHlsZVVSTC5zcGxpdCgnbWFwYm94Oi8vc3R5bGVzLycpWzFdO1xuICAgIHZhciB1cmwgPSBtb2R1bGUuZXhwb3J0cygnL3N0eWxlcy92MS8nICsgb3duZXJJRFN0eWxlLCBhY2Nlc3NUb2tlbilcbiAgICAgICAgLnJlcGxhY2UoJ2h0dHA6Ly8nLCAnaHR0cHM6Ly8nKTtcblxuICAgIHJldHVybiB1cmw7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgaXNBcnJheSA9IHJlcXVpcmUoJ2lzYXJyYXknKSxcbiAgICB1dGlsID0gcmVxdWlyZSgnLi91dGlsJyksXG4gICAgZm9ybWF0X3VybCA9IHJlcXVpcmUoJy4vZm9ybWF0X3VybCcpLFxuICAgIGZlZWRiYWNrID0gcmVxdWlyZSgnLi9mZWVkYmFjaycpLFxuICAgIHJlcXVlc3QgPSByZXF1aXJlKCcuL3JlcXVlc3QnKTtcblxuLy8gTG93LWxldmVsIGdlb2NvZGluZyBpbnRlcmZhY2UgLSB3cmFwcyBzcGVjaWZpYyBBUEkgY2FsbHMgYW5kIHRoZWlyXG4vLyByZXR1cm4gdmFsdWVzLlxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbih1cmwsIG9wdGlvbnMpIHtcbiAgICBpZiAoIW9wdGlvbnMpIG9wdGlvbnMgPSB7fTtcbiAgICB2YXIgZ2VvY29kZXIgPSB7fTtcblxuICAgIHV0aWwuc3RyaWN0KHVybCwgJ3N0cmluZycpO1xuXG4gICAgaWYgKHVybC5pbmRleE9mKCcvJykgPT09IC0xKSB7XG4gICAgICAgIHVybCA9IGZvcm1hdF91cmwoJy9nZW9jb2RpbmcvdjUvJyArIHVybCArICcve3F1ZXJ5fS5qc29uJywgb3B0aW9ucy5hY2Nlc3NUb2tlbiwgNSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcm91bmRUbyhsYXRMbmcsIHByZWNpc2lvbikge1xuICAgICAgICB2YXIgbXVsdCA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24pO1xuICAgICAgICBsYXRMbmcubGF0ID0gTWF0aC5yb3VuZChsYXRMbmcubGF0ICogbXVsdCkgLyBtdWx0O1xuICAgICAgICBsYXRMbmcubG5nID0gTWF0aC5yb3VuZChsYXRMbmcubG5nICogbXVsdCkgLyBtdWx0O1xuICAgICAgICByZXR1cm4gbGF0TG5nO1xuICAgIH1cblxuICAgIGdlb2NvZGVyLmdldFVSTCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gdXJsO1xuICAgIH07XG5cbiAgICBnZW9jb2Rlci5xdWVyeVVSTCA9IGZ1bmN0aW9uKF8pIHtcbiAgICAgICAgdmFyIGlzT2JqZWN0ID0gIShpc0FycmF5KF8pIHx8IHR5cGVvZiBfID09PSAnc3RyaW5nJyksXG4gICAgICAgICAgICBxdWVyeSA9IGlzT2JqZWN0ID8gXy5xdWVyeSA6IF87XG5cbiAgICAgICAgaWYgKGlzQXJyYXkocXVlcnkpKSB7XG4gICAgICAgICAgICB2YXIgcGFydHMgPSBbXTtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcXVlcnkubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBwYXJ0c1tpXSA9IGVuY29kZVVSSUNvbXBvbmVudChxdWVyeVtpXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBxdWVyeSA9IHBhcnRzLmpvaW4oJzsnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHF1ZXJ5ID0gZW5jb2RlVVJJQ29tcG9uZW50KHF1ZXJ5KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZlZWRiYWNrLnJlY29yZCh7IGdlb2NvZGluZzogcXVlcnkgfSk7XG5cbiAgICAgICAgdmFyIHVybCA9IEwuVXRpbC50ZW1wbGF0ZShnZW9jb2Rlci5nZXRVUkwoKSwge3F1ZXJ5OiBxdWVyeX0pO1xuXG4gICAgICAgIGlmIChpc09iamVjdCkge1xuICAgICAgICAgICAgaWYgKF8udHlwZXMpIHtcbiAgICAgICAgICAgICAgICBpZiAoaXNBcnJheShfLnR5cGVzKSkge1xuICAgICAgICAgICAgICAgICAgICB1cmwgKz0gJyZ0eXBlcz0nICsgXy50eXBlcy5qb2luKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdXJsICs9ICcmdHlwZXM9JyArIF8udHlwZXM7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoXy5jb3VudHJ5KSB7XG4gICAgICAgICAgICAgICAgaWYgKGlzQXJyYXkoXy5jb3VudHJ5KSkge1xuICAgICAgICAgICAgICAgICAgICB1cmwgKz0gJyZjb3VudHJ5PScgKyBfLmNvdW50cnkuam9pbigpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHVybCArPSAnJmNvdW50cnk9JyArIF8uY291bnRyeTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChfLmJib3gpIHtcbiAgICAgICAgICAgICAgICBpZiAoaXNBcnJheShfLmJib3gpKSB7XG4gICAgICAgICAgICAgICAgICAgIHVybCArPSAnJmJib3g9JyArIF8uYmJveC5qb2luKCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdXJsICs9ICcmYmJveD0nICsgXy5iYm94O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKF8ucHJveGltaXR5KSB7XG4gICAgICAgICAgICAgICAgdmFyIHByb3hpbWl0eSA9IHJvdW5kVG8oTC5sYXRMbmcoXy5wcm94aW1pdHkpLCAzKTtcbiAgICAgICAgICAgICAgICB1cmwgKz0gJyZwcm94aW1pdHk9JyArIHByb3hpbWl0eS5sbmcgKyAnLCcgKyBwcm94aW1pdHkubGF0O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodHlwZW9mIF8uYXV0b2NvbXBsZXRlID09PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgICAgICB1cmwgKz0gJyZhdXRvY29tcGxldGU9JyArIF8uYXV0b2NvbXBsZXRlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHVybDtcbiAgICB9O1xuXG4gICAgZ2VvY29kZXIucXVlcnkgPSBmdW5jdGlvbihfLCBjYWxsYmFjaykge1xuICAgICAgICB1dGlsLnN0cmljdChjYWxsYmFjaywgJ2Z1bmN0aW9uJyk7XG5cbiAgICAgICAgcmVxdWVzdChnZW9jb2Rlci5xdWVyeVVSTChfKSwgZnVuY3Rpb24oZXJyLCBqc29uKSB7XG4gICAgICAgICAgICBpZiAoanNvbiAmJiAoanNvbi5sZW5ndGggfHwganNvbi5mZWF0dXJlcykpIHtcbiAgICAgICAgICAgICAgICB2YXIgcmVzID0ge1xuICAgICAgICAgICAgICAgICAgICByZXN1bHRzOiBqc29uXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBpZiAoanNvbi5mZWF0dXJlcyAmJiBqc29uLmZlYXR1cmVzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICByZXMubGF0bG5nID0gW1xuICAgICAgICAgICAgICAgICAgICAgICAganNvbi5mZWF0dXJlc1swXS5jZW50ZXJbMV0sXG4gICAgICAgICAgICAgICAgICAgICAgICBqc29uLmZlYXR1cmVzWzBdLmNlbnRlclswXV07XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGpzb24uZmVhdHVyZXNbMF0uYmJveCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzLmJvdW5kcyA9IGpzb24uZmVhdHVyZXNbMF0uYmJveDtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlcy5sYm91bmRzID0gdXRpbC5sYm91bmRzKHJlcy5ib3VuZHMpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNhbGxiYWNrKG51bGwsIHJlcyk7XG4gICAgICAgICAgICB9IGVsc2UgY2FsbGJhY2soZXJyIHx8IHRydWUpO1xuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gZ2VvY29kZXI7XG4gICAgfTtcblxuICAgIC8vIGEgcmV2ZXJzZSBnZW9jb2RlOlxuICAgIC8vXG4gICAgLy8gIGdlb2NvZGVyLnJldmVyc2VRdWVyeShbODAsIDIwXSlcbiAgICBnZW9jb2Rlci5yZXZlcnNlUXVlcnkgPSBmdW5jdGlvbihfLCBjYWxsYmFjaykge1xuICAgICAgICB2YXIgcSA9ICcnO1xuXG4gICAgICAgIC8vIHNvcnQgdGhyb3VnaCBkaWZmZXJlbnQgd2F5cyBwZW9wbGUgcmVwcmVzZW50IGxhdCBhbmQgbG9uIHBhaXJzXG4gICAgICAgIGZ1bmN0aW9uIG5vcm1hbGl6ZSh4KSB7XG4gICAgICAgICAgICB2YXIgbGF0TG5nO1xuICAgICAgICAgICAgaWYgKHgubGF0ICE9PSB1bmRlZmluZWQgJiYgeC5sbmcgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGxhdExuZyA9IEwubGF0TG5nKHgubGF0LCB4LmxuZyk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHgubGF0ICE9PSB1bmRlZmluZWQgJiYgeC5sb24gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIGxhdExuZyA9IEwubGF0TG5nKHgubGF0LCB4Lmxvbik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGxhdExuZyA9IEwubGF0TG5nKHhbMV0sIHhbMF0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGF0TG5nID0gcm91bmRUbyhsYXRMbmcsIDUpO1xuICAgICAgICAgICAgcmV0dXJuIGxhdExuZy5sbmcgKyAnLCcgKyBsYXRMbmcubGF0O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKF8ubGVuZ3RoICYmIF9bMF0ubGVuZ3RoKSB7XG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMCwgcHRzID0gW107IGkgPCBfLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgcHRzLnB1c2gobm9ybWFsaXplKF9baV0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHEgPSBwdHMuam9pbignOycpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcSA9IG5vcm1hbGl6ZShfKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJlcXVlc3QoZ2VvY29kZXIucXVlcnlVUkwocSksIGZ1bmN0aW9uKGVyciwganNvbikge1xuICAgICAgICAgICAgY2FsbGJhY2soZXJyLCBqc29uKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIGdlb2NvZGVyO1xuICAgIH07XG5cbiAgICByZXR1cm4gZ2VvY29kZXI7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgZ2VvY29kZXIgPSByZXF1aXJlKCcuL2dlb2NvZGVyJyksXG4gICAgdXRpbCA9IHJlcXVpcmUoJy4vdXRpbCcpO1xuXG52YXIgR2VvY29kZXJDb250cm9sID0gTC5Db250cm9sLmV4dGVuZCh7XG4gICAgaW5jbHVkZXM6IEwuTWl4aW4uRXZlbnRzLFxuXG4gICAgb3B0aW9uczoge1xuICAgICAgICBwcm94aW1pdHk6IHRydWUsXG4gICAgICAgIHBvc2l0aW9uOiAndG9wbGVmdCcsXG4gICAgICAgIHBvaW50Wm9vbTogMTYsXG4gICAgICAgIGtlZXBPcGVuOiBmYWxzZSxcbiAgICAgICAgYXV0b2NvbXBsZXRlOiBmYWxzZSxcbiAgICAgICAgcXVlcnlPcHRpb25zOiB7fVxuICAgIH0sXG5cbiAgICBpbml0aWFsaXplOiBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgICAgIEwuVXRpbC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuICAgICAgICB0aGlzLnNldFVSTChfKTtcbiAgICAgICAgdGhpcy5fdXBkYXRlU3VibWl0ID0gTC5iaW5kKHRoaXMuX3VwZGF0ZVN1Ym1pdCwgdGhpcyk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZUF1dG9jb21wbGV0ZSA9IEwuYmluZCh0aGlzLl91cGRhdGVBdXRvY29tcGxldGUsIHRoaXMpO1xuICAgICAgICB0aGlzLl9jaG9vc2VSZXN1bHQgPSBMLmJpbmQodGhpcy5fY2hvb3NlUmVzdWx0LCB0aGlzKTtcbiAgICB9LFxuXG4gICAgc2V0VVJMOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIHRoaXMuZ2VvY29kZXIgPSBnZW9jb2RlcihfLCB7XG4gICAgICAgICAgICBhY2Nlc3NUb2tlbjogdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgZ2V0VVJMOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2VvY29kZXIuZ2V0VVJMKCk7XG4gICAgfSxcblxuICAgIHNldElEOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnNldFVSTChfKTtcbiAgICB9LFxuXG4gICAgc2V0VGlsZUpTT046IGZ1bmN0aW9uKF8pIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2V0VVJMKF8uZ2VvY29kZXIpO1xuICAgIH0sXG5cbiAgICBfdG9nZ2xlOiBmdW5jdGlvbihlKSB7XG4gICAgICAgIGlmIChlKSBMLkRvbUV2ZW50LnN0b3AoZSk7XG4gICAgICAgIGlmIChMLkRvbVV0aWwuaGFzQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnYWN0aXZlJykpIHtcbiAgICAgICAgICAgIEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdhY3RpdmUnKTtcbiAgICAgICAgICAgIHRoaXMuX3Jlc3VsdHMuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgICAgICB0aGlzLl9pbnB1dC5ibHVyKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnYWN0aXZlJyk7XG4gICAgICAgICAgICB0aGlzLl9pbnB1dC5mb2N1cygpO1xuICAgICAgICAgICAgdGhpcy5faW5wdXQuc2VsZWN0KCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgX2Nsb3NlSWZPcGVuOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKEwuRG9tVXRpbC5oYXNDbGFzcyh0aGlzLl9jb250YWluZXIsICdhY3RpdmUnKSAmJlxuICAgICAgICAgICAgIXRoaXMub3B0aW9ucy5rZWVwT3Blbikge1xuICAgICAgICAgICAgTC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2FjdGl2ZScpO1xuICAgICAgICAgICAgdGhpcy5fcmVzdWx0cy5pbm5lckhUTUwgPSAnJztcbiAgICAgICAgICAgIHRoaXMuX2lucHV0LmJsdXIoKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBvbkFkZDogZnVuY3Rpb24obWFwKSB7XG5cbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtbWFwYm94LWdlb2NvZGVyIGxlYWZsZXQtYmFyIGxlYWZsZXQtY29udHJvbCcpLFxuICAgICAgICAgICAgbGluayA9IEwuRG9tVXRpbC5jcmVhdGUoJ2EnLCAnbGVhZmxldC1jb250cm9sLW1hcGJveC1nZW9jb2Rlci10b2dnbGUgbWFwYm94LWljb24gbWFwYm94LWljb24tZ2VvY29kZXInLCBjb250YWluZXIpLFxuICAgICAgICAgICAgcmVzdWx0cyA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtbWFwYm94LWdlb2NvZGVyLXJlc3VsdHMnLCBjb250YWluZXIpLFxuICAgICAgICAgICAgd3JhcCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtbWFwYm94LWdlb2NvZGVyLXdyYXAnLCBjb250YWluZXIpLFxuICAgICAgICAgICAgZm9ybSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2Zvcm0nLCAnbGVhZmxldC1jb250cm9sLW1hcGJveC1nZW9jb2Rlci1mb3JtJywgd3JhcCksXG4gICAgICAgICAgICBpbnB1dCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2lucHV0JywgJycsIGZvcm0pO1xuXG4gICAgICAgIGxpbmsuaHJlZiA9ICcjJztcbiAgICAgICAgbGluay5pbm5lckhUTUwgPSAnJm5ic3A7JztcblxuICAgICAgICBpbnB1dC50eXBlID0gJ3RleHQnO1xuICAgICAgICBpbnB1dC5zZXRBdHRyaWJ1dGUoJ3BsYWNlaG9sZGVyJywgJ1NlYXJjaCcpO1xuXG4gICAgICAgIEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIoZm9ybSwgJ3N1Ym1pdCcsIHRoaXMuX2dlb2NvZGUsIHRoaXMpO1xuICAgICAgICBMLkRvbUV2ZW50LmFkZExpc3RlbmVyKGlucHV0LCAna2V5dXAnLCB0aGlzLl9hdXRvY29tcGxldGUsIHRoaXMpO1xuICAgICAgICBMLkRvbUV2ZW50LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKGNvbnRhaW5lcik7XG5cbiAgICAgICAgdGhpcy5fbWFwID0gbWFwO1xuICAgICAgICB0aGlzLl9yZXN1bHRzID0gcmVzdWx0cztcbiAgICAgICAgdGhpcy5faW5wdXQgPSBpbnB1dDtcbiAgICAgICAgdGhpcy5fZm9ybSA9IGZvcm07XG5cbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucy5rZWVwT3Blbikge1xuICAgICAgICAgICAgTC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2FjdGl2ZScpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fbWFwLm9uKCdjbGljaycsIHRoaXMuX2Nsb3NlSWZPcGVuLCB0aGlzKTtcbiAgICAgICAgICAgIEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIobGluaywgJ2NsaWNrJywgdGhpcy5fdG9nZ2xlLCB0aGlzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBjb250YWluZXI7XG4gICAgfSxcblxuICAgIF91cGRhdGVTdWJtaXQ6IGZ1bmN0aW9uKGVyciwgcmVzcCkge1xuICAgICAgICBMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnc2VhcmNoaW5nJyk7XG4gICAgICAgIHRoaXMuX3Jlc3VsdHMuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIGlmIChlcnIgfHwgIXJlc3ApIHtcbiAgICAgICAgICAgIHRoaXMuZmlyZSgnZXJyb3InLCB7ZXJyb3I6IGVycn0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdmFyIGZlYXR1cmVzID0gW107XG4gICAgICAgICAgICBpZiAocmVzcC5yZXN1bHRzICYmIHJlc3AucmVzdWx0cy5mZWF0dXJlcykge1xuICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gcmVzcC5yZXN1bHRzLmZlYXR1cmVzO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGZlYXR1cmVzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgnYXV0b3NlbGVjdCcsIHsgZmVhdHVyZTogZmVhdHVyZXNbMF0gfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdmb3VuZCcsIHtyZXN1bHRzOiByZXNwLnJlc3VsdHN9KTtcbiAgICAgICAgICAgICAgICB0aGlzLl9jaG9vc2VSZXN1bHQoZmVhdHVyZXNbMF0pO1xuICAgICAgICAgICAgICAgIHRoaXMuX2Nsb3NlSWZPcGVuKCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGZlYXR1cmVzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ2ZvdW5kJywge3Jlc3VsdHM6IHJlc3AucmVzdWx0c30pO1xuICAgICAgICAgICAgICAgIHRoaXMuX2Rpc3BsYXlSZXN1bHRzKGZlYXR1cmVzKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdub3Rmb3VuZCcpO1xuICAgICAgICAgICAgICAgIHRoaXMuX2Rpc3BsYXlSZXN1bHRzKGZlYXR1cmVzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfdXBkYXRlQXV0b2NvbXBsZXRlOiBmdW5jdGlvbihlcnIsIHJlc3ApIHtcbiAgICAgICAgdGhpcy5fcmVzdWx0cy5pbm5lckhUTUwgPSAnJztcbiAgICAgICAgaWYgKGVyciB8fCAhcmVzcCkge1xuICAgICAgICAgICAgdGhpcy5maXJlKCdlcnJvcicsIHtlcnJvcjogZXJyfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgZmVhdHVyZXMgPSBbXTtcbiAgICAgICAgICAgIGlmIChyZXNwLnJlc3VsdHMgJiYgcmVzcC5yZXN1bHRzLmZlYXR1cmVzKSB7XG4gICAgICAgICAgICAgICAgZmVhdHVyZXMgPSByZXNwLnJlc3VsdHMuZmVhdHVyZXM7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoZmVhdHVyZXMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdmb3VuZCcsIHtyZXN1bHRzOiByZXNwLnJlc3VsdHN9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdub3Rmb3VuZCcpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5fZGlzcGxheVJlc3VsdHMoZmVhdHVyZXMpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIF9kaXNwbGF5UmVzdWx0czogZnVuY3Rpb24oZmVhdHVyZXMpIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIGwgPSBNYXRoLm1pbihmZWF0dXJlcy5sZW5ndGgsIDUpOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgZmVhdHVyZSA9IGZlYXR1cmVzW2ldO1xuICAgICAgICAgICAgdmFyIG5hbWUgPSBmZWF0dXJlLnBsYWNlX25hbWU7XG4gICAgICAgICAgICBpZiAoIW5hbWUubGVuZ3RoKSBjb250aW51ZTtcblxuICAgICAgICAgICAgdmFyIHIgPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgJycsIHRoaXMuX3Jlc3VsdHMpO1xuICAgICAgICAgICAgdmFyIHRleHQgPSAoJ2lubmVyVGV4dCcgaW4gcikgPyAnaW5uZXJUZXh0JyA6ICd0ZXh0Q29udGVudCc7XG4gICAgICAgICAgICByW3RleHRdID0gbmFtZTtcbiAgICAgICAgICAgIHIuc2V0QXR0cmlidXRlKCd0aXRsZScsIG5hbWUpO1xuICAgICAgICAgICAgci5ocmVmID0gJyMnO1xuXG4gICAgICAgICAgICAoTC5iaW5kKGZ1bmN0aW9uKGZlYXR1cmUpIHtcbiAgICAgICAgICAgICAgICBMLkRvbUV2ZW50LmFkZExpc3RlbmVyKHIsICdjbGljaycsIGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fY2hvb3NlUmVzdWx0KGZlYXR1cmUpO1xuICAgICAgICAgICAgICAgICAgICBMLkRvbUV2ZW50LnN0b3AoZSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgnc2VsZWN0JywgeyBmZWF0dXJlOiBmZWF0dXJlIH0pO1xuICAgICAgICAgICAgICAgIH0sIHRoaXMpO1xuICAgICAgICAgICAgfSwgdGhpcykpKGZlYXR1cmUpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChmZWF0dXJlcy5sZW5ndGggPiA1KSB7XG4gICAgICAgICAgICB2YXIgb3V0b2YgPSBMLkRvbVV0aWwuY3JlYXRlKCdzcGFuJywgJycsIHRoaXMuX3Jlc3VsdHMpO1xuICAgICAgICAgICAgb3V0b2YuaW5uZXJIVE1MID0gJ1RvcCA1IG9mICcgKyBmZWF0dXJlcy5sZW5ndGggKyAnICByZXN1bHRzJztcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfY2hvb3NlUmVzdWx0OiBmdW5jdGlvbihyZXN1bHQpIHtcbiAgICAgICAgaWYgKHJlc3VsdC5iYm94KSB7XG4gICAgICAgICAgICB0aGlzLl9tYXAuZml0Qm91bmRzKHV0aWwubGJvdW5kcyhyZXN1bHQuYmJveCkpO1xuICAgICAgICB9IGVsc2UgaWYgKHJlc3VsdC5jZW50ZXIpIHtcbiAgICAgICAgICAgIHRoaXMuX21hcC5zZXRWaWV3KFtyZXN1bHQuY2VudGVyWzFdLCByZXN1bHQuY2VudGVyWzBdXSwgKHRoaXMuX21hcC5nZXRab29tKCkgPT09IHVuZGVmaW5lZCkgP1xuICAgICAgICAgICAgICAgIHRoaXMub3B0aW9ucy5wb2ludFpvb20gOlxuICAgICAgICAgICAgICAgIE1hdGgubWF4KHRoaXMuX21hcC5nZXRab29tKCksIHRoaXMub3B0aW9ucy5wb2ludFpvb20pKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfZ2VvY29kZTogZnVuY3Rpb24oZSkge1xuICAgICAgICBMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xuICAgICAgICBpZiAodGhpcy5faW5wdXQudmFsdWUgPT09ICcnKSByZXR1cm4gdGhpcy5fdXBkYXRlU3VibWl0KCk7XG4gICAgICAgIEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdzZWFyY2hpbmcnKTtcbiAgICAgICAgdGhpcy5nZW9jb2Rlci5xdWVyeShMLlV0aWwuZXh0ZW5kKHtcbiAgICAgICAgICAgIHF1ZXJ5OiB0aGlzLl9pbnB1dC52YWx1ZSxcbiAgICAgICAgICAgIHByb3hpbWl0eTogdGhpcy5vcHRpb25zLnByb3hpbWl0eSA/IHRoaXMuX21hcC5nZXRDZW50ZXIoKSA6IGZhbHNlXG4gICAgICAgIH0sIHRoaXMub3B0aW9ucy5xdWVyeU9wdGlvbnMpLCB0aGlzLl91cGRhdGVTdWJtaXQpO1xuICAgIH0sXG5cbiAgICBfYXV0b2NvbXBsZXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKCF0aGlzLm9wdGlvbnMuYXV0b2NvbXBsZXRlKSByZXR1cm47XG4gICAgICAgIGlmICh0aGlzLl9pbnB1dC52YWx1ZSA9PT0gJycpIHJldHVybiB0aGlzLl91cGRhdGVBdXRvY29tcGxldGUoKTtcbiAgICAgICAgdGhpcy5nZW9jb2Rlci5xdWVyeShMLlV0aWwuZXh0ZW5kKHtcbiAgICAgICAgICAgIHF1ZXJ5OiB0aGlzLl9pbnB1dC52YWx1ZSxcbiAgICAgICAgICAgIHByb3hpbWl0eTogdGhpcy5vcHRpb25zLnByb3hpbWl0eSA/IHRoaXMuX21hcC5nZXRDZW50ZXIoKSA6IGZhbHNlXG4gICAgICAgIH0sIHRoaXMub3B0aW9ucy5xdWVyeU9wdGlvbnMpLCB0aGlzLl91cGRhdGVBdXRvY29tcGxldGUpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5HZW9jb2RlckNvbnRyb2wgPSBHZW9jb2RlckNvbnRyb2w7XG5cbm1vZHVsZS5leHBvcnRzLmdlb2NvZGVyQ29udHJvbCA9IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICByZXR1cm4gbmV3IEdlb2NvZGVyQ29udHJvbChfLCBvcHRpb25zKTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbmZ1bmN0aW9uIHV0ZkRlY29kZShjKSB7XG4gICAgaWYgKGMgPj0gOTMpIGMtLTtcbiAgICBpZiAoYyA+PSAzNSkgYy0tO1xuICAgIHJldHVybiBjIC0gMzI7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24oZGF0YSkge1xuICAgIHJldHVybiBmdW5jdGlvbih4LCB5KSB7XG4gICAgICAgIGlmICghZGF0YSkgcmV0dXJuO1xuICAgICAgICB2YXIgaWR4ID0gdXRmRGVjb2RlKGRhdGEuZ3JpZFt5XS5jaGFyQ29kZUF0KHgpKSxcbiAgICAgICAgICAgIGtleSA9IGRhdGEua2V5c1tpZHhdO1xuICAgICAgICByZXR1cm4gZGF0YS5kYXRhW2tleV07XG4gICAgfTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciB1dGlsID0gcmVxdWlyZSgnLi91dGlsJyksXG4gICAgTXVzdGFjaGUgPSByZXF1aXJlKCdtdXN0YWNoZScpO1xuXG52YXIgR3JpZENvbnRyb2wgPSBMLkNvbnRyb2wuZXh0ZW5kKHtcblxuICAgIG9wdGlvbnM6IHtcbiAgICAgICAgcGlubmFibGU6IHRydWUsXG4gICAgICAgIGZvbGxvdzogZmFsc2UsXG4gICAgICAgIHNhbml0aXplcjogcmVxdWlyZSgnc2FuaXRpemUtY2FqYScpLFxuICAgICAgICB0b3VjaFRlYXNlcjogdHJ1ZSxcbiAgICAgICAgbG9jYXRpb246IHRydWVcbiAgICB9LFxuXG4gICAgX2N1cnJlbnRDb250ZW50OiAnJyxcblxuICAgIC8vIHBpbm5lZCBtZWFucyB0aGF0IHRoaXMgY29udHJvbCBpcyBvbiBhIGZlYXR1cmUgYW5kIHRoZSB1c2VyIGhhcyBsaWtlbHlcbiAgICAvLyBjbGlja2VkLiBwaW5uZWQgd2lsbCBub3QgYmVjb21lIGZhbHNlIHVubGVzcyB0aGUgdXNlciBjbGlja3Mgb2ZmXG4gICAgLy8gb2YgdGhlIGZlYXR1cmUgb250byBhbm90aGVyIG9yIGNsaWNrcyB4XG4gICAgX3Bpbm5lZDogZmFsc2UsXG5cbiAgICBpbml0aWFsaXplOiBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgICAgIEwuVXRpbC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuICAgICAgICB1dGlsLnN0cmljdF9pbnN0YW5jZShfLCBMLkNsYXNzLCAnTC5tYXBib3guZ3JpZExheWVyJyk7XG4gICAgICAgIHRoaXMuX2xheWVyID0gXztcbiAgICB9LFxuXG4gICAgc2V0VGVtcGxhdGU6IGZ1bmN0aW9uKHRlbXBsYXRlKSB7XG4gICAgICAgIHV0aWwuc3RyaWN0KHRlbXBsYXRlLCAnc3RyaW5nJyk7XG4gICAgICAgIHRoaXMub3B0aW9ucy50ZW1wbGF0ZSA9IHRlbXBsYXRlO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgX3RlbXBsYXRlOiBmdW5jdGlvbihmb3JtYXQsIGRhdGEpIHtcbiAgICAgICAgaWYgKCFkYXRhKSByZXR1cm47XG4gICAgICAgIHZhciB0ZW1wbGF0ZSA9IHRoaXMub3B0aW9ucy50ZW1wbGF0ZSB8fCB0aGlzLl9sYXllci5nZXRUaWxlSlNPTigpLnRlbXBsYXRlO1xuICAgICAgICBpZiAodGVtcGxhdGUpIHtcbiAgICAgICAgICAgIHZhciBkID0ge307XG4gICAgICAgICAgICBkWydfXycgKyBmb3JtYXQgKyAnX18nXSA9IHRydWU7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5vcHRpb25zLnNhbml0aXplcihcbiAgICAgICAgICAgICAgICBNdXN0YWNoZS50b19odG1sKHRlbXBsYXRlLCBMLmV4dGVuZChkLCBkYXRhKSkpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8vIGNoYW5nZSB0aGUgY29udGVudCBvZiB0aGUgdG9vbHRpcCBIVE1MIGlmIGl0IGhhcyBjaGFuZ2VkLCBvdGhlcndpc2VcbiAgICAvLyBub29wXG4gICAgX3Nob3c6IGZ1bmN0aW9uKGNvbnRlbnQsIG8pIHtcbiAgICAgICAgaWYgKGNvbnRlbnQgPT09IHRoaXMuX2N1cnJlbnRDb250ZW50KSByZXR1cm47XG5cbiAgICAgICAgdGhpcy5fY3VycmVudENvbnRlbnQgPSBjb250ZW50O1xuXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMuZm9sbG93KSB7XG4gICAgICAgICAgICB0aGlzLl9wb3B1cC5zZXRDb250ZW50KGNvbnRlbnQpXG4gICAgICAgICAgICAgICAgLnNldExhdExuZyhvLmxhdExuZyk7XG4gICAgICAgICAgICBpZiAodGhpcy5fbWFwLl9wb3B1cCAhPT0gdGhpcy5fcG9wdXApIHRoaXMuX3BvcHVwLm9wZW5Pbih0aGlzLl9tYXApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fY29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xuICAgICAgICAgICAgdGhpcy5fY29udGVudFdyYXBwZXIuaW5uZXJIVE1MID0gY29udGVudDtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBoaWRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdGhpcy5fcGlubmVkID0gZmFsc2U7XG4gICAgICAgIHRoaXMuX2N1cnJlbnRDb250ZW50ID0gJyc7XG5cbiAgICAgICAgdGhpcy5fbWFwLmNsb3NlUG9wdXAoKTtcbiAgICAgICAgdGhpcy5fY29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgICAgIHRoaXMuX2NvbnRlbnRXcmFwcGVyLmlubmVySFRNTCA9ICcnO1xuXG4gICAgICAgIEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdjbG9zYWJsZScpO1xuXG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBfbW91c2VvdmVyOiBmdW5jdGlvbihvKSB7XG4gICAgICAgIGlmIChvLmRhdGEpIHtcbiAgICAgICAgICAgIEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ21hcC1jbGlja2FibGUnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ21hcC1jbGlja2FibGUnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLl9waW5uZWQpIHJldHVybjtcblxuICAgICAgICB2YXIgY29udGVudCA9IHRoaXMuX3RlbXBsYXRlKCd0ZWFzZXInLCBvLmRhdGEpO1xuICAgICAgICBpZiAoY29udGVudCkge1xuICAgICAgICAgICAgdGhpcy5fc2hvdyhjb250ZW50LCBvKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuaGlkZSgpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIF9tb3VzZW1vdmU6IGZ1bmN0aW9uKG8pIHtcbiAgICAgICAgaWYgKHRoaXMuX3Bpbm5lZCkgcmV0dXJuO1xuICAgICAgICBpZiAoIXRoaXMub3B0aW9ucy5mb2xsb3cpIHJldHVybjtcblxuICAgICAgICB0aGlzLl9wb3B1cC5zZXRMYXRMbmcoby5sYXRMbmcpO1xuICAgIH0sXG5cbiAgICBfbmF2aWdhdGVUbzogZnVuY3Rpb24odXJsKSB7XG4gICAgICAgIHdpbmRvdy50b3AubG9jYXRpb24uaHJlZiA9IHVybDtcbiAgICB9LFxuXG4gICAgX2NsaWNrOiBmdW5jdGlvbihvKSB7XG5cbiAgICAgICAgdmFyIGxvY2F0aW9uX2Zvcm1hdHRlZCA9IHRoaXMuX3RlbXBsYXRlKCdsb2NhdGlvbicsIG8uZGF0YSk7XG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMubG9jYXRpb24gJiYgbG9jYXRpb25fZm9ybWF0dGVkICYmXG4gICAgICAgICAgICBsb2NhdGlvbl9mb3JtYXR0ZWQuc2VhcmNoKC9eaHR0cHM/Oi8pID09PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fbmF2aWdhdGVUbyh0aGlzLl90ZW1wbGF0ZSgnbG9jYXRpb24nLCBvLmRhdGEpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghdGhpcy5vcHRpb25zLnBpbm5hYmxlKSByZXR1cm47XG5cbiAgICAgICAgdmFyIGNvbnRlbnQgPSB0aGlzLl90ZW1wbGF0ZSgnZnVsbCcsIG8uZGF0YSk7XG5cbiAgICAgICAgaWYgKCFjb250ZW50ICYmIHRoaXMub3B0aW9ucy50b3VjaFRlYXNlciAmJiBMLkJyb3dzZXIudG91Y2gpIHtcbiAgICAgICAgICAgIGNvbnRlbnQgPSB0aGlzLl90ZW1wbGF0ZSgndGVhc2VyJywgby5kYXRhKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjb250ZW50KSB7XG4gICAgICAgICAgICBMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnY2xvc2FibGUnKTtcbiAgICAgICAgICAgIHRoaXMuX3Bpbm5lZCA9IHRydWU7XG4gICAgICAgICAgICB0aGlzLl9zaG93KGNvbnRlbnQsIG8pO1xuICAgICAgICB9IGVsc2UgaWYgKHRoaXMuX3Bpbm5lZCkge1xuICAgICAgICAgICAgTC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2Nsb3NhYmxlJyk7XG4gICAgICAgICAgICB0aGlzLl9waW5uZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuaGlkZSgpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIF9vblBvcHVwQ2xvc2U6IGZ1bmN0aW9uKCkge1xuICAgICAgICB0aGlzLl9jdXJyZW50Q29udGVudCA9IG51bGw7XG4gICAgICAgIHRoaXMuX3Bpbm5lZCA9IGZhbHNlO1xuICAgIH0sXG5cbiAgICBfY3JlYXRlQ2xvc2VidXR0b246IGZ1bmN0aW9uKGNvbnRhaW5lciwgZm4pIHtcbiAgICAgICAgdmFyIGxpbmsgPSBMLkRvbVV0aWwuY3JlYXRlKCdhJywgJ2Nsb3NlJywgY29udGFpbmVyKTtcblxuICAgICAgICBsaW5rLmlubmVySFRNTCA9ICdjbG9zZSc7XG4gICAgICAgIGxpbmsuaHJlZiA9ICcjJztcbiAgICAgICAgbGluay50aXRsZSA9ICdjbG9zZSc7XG5cbiAgICAgICAgTC5Eb21FdmVudFxuICAgICAgICAgICAgLm9uKGxpbmssICdjbGljaycsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKVxuICAgICAgICAgICAgLm9uKGxpbmssICdtb3VzZWRvd24nLCBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbilcbiAgICAgICAgICAgIC5vbihsaW5rLCAnZGJsY2xpY2snLCBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbilcbiAgICAgICAgICAgIC5vbihsaW5rLCAnY2xpY2snLCBMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KVxuICAgICAgICAgICAgLm9uKGxpbmssICdjbGljaycsIGZuLCB0aGlzKTtcblxuICAgICAgICByZXR1cm4gbGluaztcbiAgICB9LFxuXG4gICAgb25BZGQ6IGZ1bmN0aW9uKG1hcCkge1xuICAgICAgICB0aGlzLl9tYXAgPSBtYXA7XG5cbiAgICAgICAgdmFyIGNsYXNzTmFtZSA9ICdsZWFmbGV0LWNvbnRyb2wtZ3JpZCBtYXAtdG9vbHRpcCcsXG4gICAgICAgICAgICBjb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUpLFxuICAgICAgICAgICAgY29udGVudFdyYXBwZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwLXRvb2x0aXAtY29udGVudCcpO1xuXG4gICAgICAgIC8vIGhpZGUgdGhlIGNvbnRhaW5lciBlbGVtZW50IGluaXRpYWxseVxuICAgICAgICBjb250YWluZXIuc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgICAgICAgdGhpcy5fY3JlYXRlQ2xvc2VidXR0b24oY29udGFpbmVyLCB0aGlzLmhpZGUpO1xuICAgICAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQoY29udGVudFdyYXBwZXIpO1xuXG4gICAgICAgIHRoaXMuX2NvbnRlbnRXcmFwcGVyID0gY29udGVudFdyYXBwZXI7XG4gICAgICAgIHRoaXMuX3BvcHVwID0gbmV3IEwuUG9wdXAoeyBhdXRvUGFuOiBmYWxzZSwgY2xvc2VPbkNsaWNrOiBmYWxzZSB9KTtcblxuICAgICAgICBtYXAub24oJ3BvcHVwY2xvc2UnLCB0aGlzLl9vblBvcHVwQ2xvc2UsIHRoaXMpO1xuXG4gICAgICAgIEwuRG9tRXZlbnRcbiAgICAgICAgICAgIC5kaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbihjb250YWluZXIpXG4gICAgICAgICAgICAvLyBhbGxvdyBwZW9wbGUgdG8gc2Nyb2xsIHRvb2x0aXBzIHdpdGggbW91c2V3aGVlbFxuICAgICAgICAgICAgLmFkZExpc3RlbmVyKGNvbnRhaW5lciwgJ21vdXNld2hlZWwnLCBMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbik7XG5cbiAgICAgICAgdGhpcy5fbGF5ZXJcbiAgICAgICAgICAgIC5vbignbW91c2VvdmVyJywgdGhpcy5fbW91c2VvdmVyLCB0aGlzKVxuICAgICAgICAgICAgLm9uKCdtb3VzZW1vdmUnLCB0aGlzLl9tb3VzZW1vdmUsIHRoaXMpXG4gICAgICAgICAgICAub24oJ2NsaWNrJywgdGhpcy5fY2xpY2ssIHRoaXMpO1xuXG4gICAgICAgIHJldHVybiBjb250YWluZXI7XG4gICAgfSxcblxuICAgIG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XG5cbiAgICAgICAgbWFwLm9mZigncG9wdXBjbG9zZScsIHRoaXMuX29uUG9wdXBDbG9zZSwgdGhpcyk7XG5cbiAgICAgICAgdGhpcy5fbGF5ZXJcbiAgICAgICAgICAgIC5vZmYoJ21vdXNlb3ZlcicsIHRoaXMuX21vdXNlb3ZlciwgdGhpcylcbiAgICAgICAgICAgIC5vZmYoJ21vdXNlbW92ZScsIHRoaXMuX21vdXNlbW92ZSwgdGhpcylcbiAgICAgICAgICAgIC5vZmYoJ2NsaWNrJywgdGhpcy5fY2xpY2ssIHRoaXMpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5HcmlkQ29udHJvbCA9IEdyaWRDb250cm9sO1xuXG5tb2R1bGUuZXhwb3J0cy5ncmlkQ29udHJvbCA9IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICByZXR1cm4gbmV3IEdyaWRDb250cm9sKF8sIG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIHV0aWwgPSByZXF1aXJlKCcuL3V0aWwnKSxcbiAgICByZXF1ZXN0ID0gcmVxdWlyZSgnLi9yZXF1ZXN0JyksXG4gICAgZ3JpZCA9IHJlcXVpcmUoJy4vZ3JpZCcpO1xuXG4vLyBmb3JrZWQgZnJvbSBkYW56ZWwvTC5VVEZHcmlkXG52YXIgR3JpZExheWVyID0gTC5MYXllci5leHRlbmQoe1xuICAgIGluY2x1ZGVzOiBbcmVxdWlyZSgnLi9sb2FkX3RpbGVqc29uJyldLFxuXG4gICAgb3B0aW9uczoge1xuICAgICAgICB0ZW1wbGF0ZTogZnVuY3Rpb24oKSB7IHJldHVybiAnJzsgfVxuICAgIH0sXG5cbiAgICBfbW91c2VPbjogbnVsbCxcbiAgICBfdGlsZWpzb246IHt9LFxuICAgIF9jYWNoZToge30sXG5cbiAgICBpbml0aWFsaXplOiBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgICAgIEwuVXRpbC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuICAgICAgICB0aGlzLl9sb2FkVGlsZUpTT04oXyk7XG4gICAgfSxcblxuICAgIF9zZXRUaWxlSlNPTjogZnVuY3Rpb24oanNvbikge1xuICAgICAgICB1dGlsLnN0cmljdChqc29uLCAnb2JqZWN0Jyk7XG5cbiAgICAgICAgTC5leHRlbmQodGhpcy5vcHRpb25zLCB7XG4gICAgICAgICAgICBncmlkczoganNvbi5ncmlkcyxcbiAgICAgICAgICAgIG1pblpvb206IGpzb24ubWluem9vbSxcbiAgICAgICAgICAgIG1heFpvb206IGpzb24ubWF4em9vbSxcbiAgICAgICAgICAgIGJvdW5kczoganNvbi5ib3VuZHMgJiYgdXRpbC5sYm91bmRzKGpzb24uYm91bmRzKVxuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLl90aWxlanNvbiA9IGpzb247XG4gICAgICAgIHRoaXMuX2NhY2hlID0ge307XG4gICAgICAgIHRoaXMuX3VwZGF0ZSgpO1xuXG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBnZXRUaWxlSlNPTjogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl90aWxlanNvbjtcbiAgICB9LFxuXG4gICAgYWN0aXZlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuICEhKHRoaXMuX21hcCAmJiB0aGlzLm9wdGlvbnMuZ3JpZHMgJiYgdGhpcy5vcHRpb25zLmdyaWRzLmxlbmd0aCk7XG4gICAgfSxcblxuICAgIG9uQWRkOiBmdW5jdGlvbihtYXApIHtcbiAgICAgICAgdGhpcy5fbWFwID0gbWFwO1xuICAgICAgICB0aGlzLl91cGRhdGUoKTtcblxuICAgICAgICB0aGlzLl9tYXBcbiAgICAgICAgICAgIC5vbignY2xpY2snLCB0aGlzLl9jbGljaywgdGhpcylcbiAgICAgICAgICAgIC5vbignbW91c2Vtb3ZlJywgdGhpcy5fbW92ZSwgdGhpcylcbiAgICAgICAgICAgIC5vbignbW92ZWVuZCcsIHRoaXMuX3VwZGF0ZSwgdGhpcyk7XG4gICAgfSxcblxuICAgIG9uUmVtb3ZlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdGhpcy5fbWFwXG4gICAgICAgICAgICAub2ZmKCdjbGljaycsIHRoaXMuX2NsaWNrLCB0aGlzKVxuICAgICAgICAgICAgLm9mZignbW91c2Vtb3ZlJywgdGhpcy5fbW92ZSwgdGhpcylcbiAgICAgICAgICAgIC5vZmYoJ21vdmVlbmQnLCB0aGlzLl91cGRhdGUsIHRoaXMpO1xuICAgIH0sXG5cbiAgICBnZXREYXRhOiBmdW5jdGlvbihsYXRsbmcsIGNhbGxiYWNrKSB7XG4gICAgICAgIGlmICghdGhpcy5hY3RpdmUoKSkgcmV0dXJuO1xuXG4gICAgICAgIHZhciBtYXAgPSB0aGlzLl9tYXAsXG4gICAgICAgICAgICBwb2ludCA9IG1hcC5wcm9qZWN0KGxhdGxuZy53cmFwKCkpLFxuICAgICAgICAgICAgdGlsZVNpemUgPSAyNTYsXG4gICAgICAgICAgICByZXNvbHV0aW9uID0gNCxcbiAgICAgICAgICAgIHggPSBNYXRoLmZsb29yKHBvaW50LnggLyB0aWxlU2l6ZSksXG4gICAgICAgICAgICB5ID0gTWF0aC5mbG9vcihwb2ludC55IC8gdGlsZVNpemUpLFxuICAgICAgICAgICAgbWF4ID0gbWFwLm9wdGlvbnMuY3JzLnNjYWxlKG1hcC5nZXRab29tKCkpIC8gdGlsZVNpemU7XG5cbiAgICAgICAgeCA9ICh4ICsgbWF4KSAlIG1heDtcbiAgICAgICAgeSA9ICh5ICsgbWF4KSAlIG1heDtcblxuICAgICAgICB0aGlzLl9nZXRUaWxlKG1hcC5nZXRab29tKCksIHgsIHksIGZ1bmN0aW9uKGdyaWQpIHtcbiAgICAgICAgICAgIHZhciBncmlkWCA9IE1hdGguZmxvb3IoKHBvaW50LnggLSAoeCAqIHRpbGVTaXplKSkgLyByZXNvbHV0aW9uKSxcbiAgICAgICAgICAgICAgICBncmlkWSA9IE1hdGguZmxvb3IoKHBvaW50LnkgLSAoeSAqIHRpbGVTaXplKSkgLyByZXNvbHV0aW9uKTtcblxuICAgICAgICAgICAgY2FsbGJhY2soZ3JpZChncmlkWCwgZ3JpZFkpKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9jbGljazogZnVuY3Rpb24oZSkge1xuICAgICAgICB0aGlzLmdldERhdGEoZS5sYXRsbmcsIEwuYmluZChmdW5jdGlvbihkYXRhKSB7XG4gICAgICAgICAgICB0aGlzLmZpcmUoJ2NsaWNrJywge1xuICAgICAgICAgICAgICAgIGxhdExuZzogZS5sYXRsbmcsXG4gICAgICAgICAgICAgICAgZGF0YTogZGF0YVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0sIHRoaXMpKTtcbiAgICB9LFxuXG4gICAgX21vdmU6IGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgdGhpcy5nZXREYXRhKGUubGF0bG5nLCBMLmJpbmQoZnVuY3Rpb24oZGF0YSkge1xuICAgICAgICAgICAgaWYgKGRhdGEgIT09IHRoaXMuX21vdXNlT24pIHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5fbW91c2VPbikge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ21vdXNlb3V0Jywge1xuICAgICAgICAgICAgICAgICAgICAgICAgbGF0TG5nOiBlLmxhdGxuZyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHRoaXMuX21vdXNlT25cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdtb3VzZW92ZXInLCB7XG4gICAgICAgICAgICAgICAgICAgIGxhdExuZzogZS5sYXRsbmcsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IGRhdGFcbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgIHRoaXMuX21vdXNlT24gPSBkYXRhO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ21vdXNlbW92ZScsIHtcbiAgICAgICAgICAgICAgICAgICAgbGF0TG5nOiBlLmxhdGxuZyxcbiAgICAgICAgICAgICAgICAgICAgZGF0YTogZGF0YVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LCB0aGlzKSk7XG4gICAgfSxcblxuICAgIF9nZXRUaWxlVVJMOiBmdW5jdGlvbih0aWxlUG9pbnQpIHtcbiAgICAgICAgdmFyIHVybHMgPSB0aGlzLm9wdGlvbnMuZ3JpZHMsXG4gICAgICAgICAgICBpbmRleCA9ICh0aWxlUG9pbnQueCArIHRpbGVQb2ludC55KSAlIHVybHMubGVuZ3RoLFxuICAgICAgICAgICAgdXJsID0gdXJsc1tpbmRleF07XG5cbiAgICAgICAgcmV0dXJuIEwuVXRpbC50ZW1wbGF0ZSh1cmwsIHRpbGVQb2ludCk7XG4gICAgfSxcblxuICAgIC8vIExvYWQgdXAgYWxsIHJlcXVpcmVkIGpzb24gZ3JpZCBmaWxlc1xuICAgIF91cGRhdGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoIXRoaXMuYWN0aXZlKCkpIHJldHVybjtcblxuICAgICAgICB2YXIgYm91bmRzID0gdGhpcy5fbWFwLmdldFBpeGVsQm91bmRzKCksXG4gICAgICAgICAgICB6ID0gdGhpcy5fbWFwLmdldFpvb20oKSxcbiAgICAgICAgICAgIHRpbGVTaXplID0gMjU2O1xuXG4gICAgICAgIGlmICh6ID4gdGhpcy5vcHRpb25zLm1heFpvb20gfHwgeiA8IHRoaXMub3B0aW9ucy5taW5ab29tKSByZXR1cm47XG5cbiAgICAgICAgdmFyIHRpbGVCb3VuZHMgPSBMLmJvdW5kcyhcbiAgICAgICAgICAgICAgICBib3VuZHMubWluLmRpdmlkZUJ5KHRpbGVTaXplKS5fZmxvb3IoKSxcbiAgICAgICAgICAgICAgICBib3VuZHMubWF4LmRpdmlkZUJ5KHRpbGVTaXplKS5fZmxvb3IoKSksXG4gICAgICAgICAgICBtYXggPSB0aGlzLl9tYXAub3B0aW9ucy5jcnMuc2NhbGUoeikgLyB0aWxlU2l6ZTtcblxuICAgICAgICBmb3IgKHZhciB4ID0gdGlsZUJvdW5kcy5taW4ueDsgeCA8PSB0aWxlQm91bmRzLm1heC54OyB4KyspIHtcbiAgICAgICAgICAgIGZvciAodmFyIHkgPSB0aWxlQm91bmRzLm1pbi55OyB5IDw9IHRpbGVCb3VuZHMubWF4Lnk7IHkrKykge1xuICAgICAgICAgICAgICAgIC8vIHggd3JhcHBlZFxuICAgICAgICAgICAgICAgIHRoaXMuX2dldFRpbGUoeiwgKCh4ICUgbWF4KSArIG1heCkgJSBtYXgsICgoeSAlIG1heCkgKyBtYXgpICUgbWF4KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfZ2V0VGlsZTogZnVuY3Rpb24oeiwgeCwgeSwgY2FsbGJhY2spIHtcbiAgICAgICAgdmFyIGtleSA9IHogKyAnXycgKyB4ICsgJ18nICsgeSxcbiAgICAgICAgICAgIHRpbGVQb2ludCA9IEwucG9pbnQoeCwgeSk7XG5cbiAgICAgICAgdGlsZVBvaW50LnogPSB6O1xuXG4gICAgICAgIGlmICghdGhpcy5fdGlsZVNob3VsZEJlTG9hZGVkKHRpbGVQb2ludCkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChrZXkgaW4gdGhpcy5fY2FjaGUpIHtcbiAgICAgICAgICAgIGlmICghY2FsbGJhY2spIHJldHVybjtcblxuICAgICAgICAgICAgaWYgKHR5cGVvZiB0aGlzLl9jYWNoZVtrZXldID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2sodGhpcy5fY2FjaGVba2V5XSk7IC8vIEFscmVhZHkgbG9hZGVkXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMuX2NhY2hlW2tleV0ucHVzaChjYWxsYmFjayk7IC8vIFBlbmRpbmdcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fY2FjaGVba2V5XSA9IFtdO1xuXG4gICAgICAgIGlmIChjYWxsYmFjaykge1xuICAgICAgICAgICAgdGhpcy5fY2FjaGVba2V5XS5wdXNoKGNhbGxiYWNrKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJlcXVlc3QodGhpcy5fZ2V0VGlsZVVSTCh0aWxlUG9pbnQpLCBMLmJpbmQoZnVuY3Rpb24oZXJyLCBqc29uKSB7XG4gICAgICAgICAgICB2YXIgY2FsbGJhY2tzID0gdGhpcy5fY2FjaGVba2V5XTtcbiAgICAgICAgICAgIHRoaXMuX2NhY2hlW2tleV0gPSBncmlkKGpzb24pO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjYWxsYmFja3MubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFja3NbaV0odGhpcy5fY2FjaGVba2V5XSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIHRoaXMpKTtcbiAgICB9LFxuXG4gICAgX3RpbGVTaG91bGRCZUxvYWRlZDogZnVuY3Rpb24odGlsZVBvaW50KSB7XG4gICAgICAgIGlmICh0aWxlUG9pbnQueiA+IHRoaXMub3B0aW9ucy5tYXhab29tIHx8IHRpbGVQb2ludC56IDwgdGhpcy5vcHRpb25zLm1pblpvb20pIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMuYm91bmRzKSB7XG4gICAgICAgICAgICB2YXIgdGlsZVNpemUgPSAyNTYsXG4gICAgICAgICAgICAgICAgbndQb2ludCA9IHRpbGVQb2ludC5tdWx0aXBseUJ5KHRpbGVTaXplKSxcbiAgICAgICAgICAgICAgICBzZVBvaW50ID0gbndQb2ludC5hZGQobmV3IEwuUG9pbnQodGlsZVNpemUsIHRpbGVTaXplKSksXG4gICAgICAgICAgICAgICAgbncgPSB0aGlzLl9tYXAudW5wcm9qZWN0KG53UG9pbnQpLFxuICAgICAgICAgICAgICAgIHNlID0gdGhpcy5fbWFwLnVucHJvamVjdChzZVBvaW50KSxcbiAgICAgICAgICAgICAgICBib3VuZHMgPSBuZXcgTC5MYXRMbmdCb3VuZHMoW253LCBzZV0pO1xuXG4gICAgICAgICAgICBpZiAoIXRoaXMub3B0aW9ucy5ib3VuZHMuaW50ZXJzZWN0cyhib3VuZHMpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzLkdyaWRMYXllciA9IEdyaWRMYXllcjtcblxubW9kdWxlLmV4cG9ydHMuZ3JpZExheWVyID0gZnVuY3Rpb24oXywgb3B0aW9ucykge1xuICAgIHJldHVybiBuZXcgR3JpZExheWVyKF8sIG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGxlYWZsZXQgPSByZXF1aXJlKCcuL2xlYWZsZXQnKTtcblxucmVxdWlyZSgnLi9tYXBib3gnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBsZWFmbGV0O1xuIiwibW9kdWxlLmV4cG9ydHMgPSB3aW5kb3cuTCA9IHJlcXVpcmUoJ2xlYWZsZXQvZGlzdC9sZWFmbGV0LXNyYycpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgTGVnZW5kQ29udHJvbCA9IEwuQ29udHJvbC5leHRlbmQoe1xuXG4gICAgb3B0aW9uczoge1xuICAgICAgICBwb3NpdGlvbjogJ2JvdHRvbXJpZ2h0JyxcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJylcbiAgICB9LFxuXG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24ob3B0aW9ucykge1xuICAgICAgICBMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG4gICAgICAgIHRoaXMuX2xlZ2VuZHMgPSB7fTtcbiAgICB9LFxuXG4gICAgb25BZGQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICB0aGlzLl9jb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwLWxlZ2VuZHMgd2F4LWxlZ2VuZHMnKTtcbiAgICAgICAgTC5Eb21FdmVudC5kaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbih0aGlzLl9jb250YWluZXIpO1xuXG4gICAgICAgIHRoaXMuX3VwZGF0ZSgpO1xuXG4gICAgICAgIHJldHVybiB0aGlzLl9jb250YWluZXI7XG4gICAgfSxcblxuICAgIGFkZExlZ2VuZDogZnVuY3Rpb24odGV4dCkge1xuICAgICAgICBpZiAoIXRleHQpIHsgcmV0dXJuIHRoaXM7IH1cblxuICAgICAgICBpZiAoIXRoaXMuX2xlZ2VuZHNbdGV4dF0pIHtcbiAgICAgICAgICAgIHRoaXMuX2xlZ2VuZHNbdGV4dF0gPSAwO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fbGVnZW5kc1t0ZXh0XSsrO1xuICAgICAgICByZXR1cm4gdGhpcy5fdXBkYXRlKCk7XG4gICAgfSxcblxuICAgIHJlbW92ZUxlZ2VuZDogZnVuY3Rpb24odGV4dCkge1xuICAgICAgICBpZiAoIXRleHQpIHsgcmV0dXJuIHRoaXM7IH1cbiAgICAgICAgaWYgKHRoaXMuX2xlZ2VuZHNbdGV4dF0pIHRoaXMuX2xlZ2VuZHNbdGV4dF0tLTtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3VwZGF0ZSgpO1xuICAgIH0sXG5cbiAgICBfdXBkYXRlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuIHRoaXM7IH1cblxuICAgICAgICB0aGlzLl9jb250YWluZXIuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIHZhciBoaWRlID0gJ25vbmUnO1xuXG4gICAgICAgIGZvciAodmFyIGkgaW4gdGhpcy5fbGVnZW5kcykge1xuICAgICAgICAgICAgaWYgKHRoaXMuX2xlZ2VuZHMuaGFzT3duUHJvcGVydHkoaSkgJiYgdGhpcy5fbGVnZW5kc1tpXSkge1xuICAgICAgICAgICAgICAgIHZhciBkaXYgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwLWxlZ2VuZCB3YXgtbGVnZW5kJywgdGhpcy5fY29udGFpbmVyKTtcbiAgICAgICAgICAgICAgICBkaXYuaW5uZXJIVE1MID0gdGhpcy5vcHRpb25zLnNhbml0aXplcihpKTtcbiAgICAgICAgICAgICAgICBoaWRlID0gJ2Jsb2NrJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGhpZGUgdGhlIGNvbnRyb2wgZW50aXJlbHkgdW5sZXNzIHRoZXJlIGlzIGF0IGxlYXN0IG9uZSBsZWdlbmQ7XG4gICAgICAgIC8vIG90aGVyd2lzZSB0aGVyZSB3aWxsIGJlIGEgc21hbGwgZ3JleSBibGVtaXNoIG9uIHRoZSBtYXAuXG4gICAgICAgIHRoaXMuX2NvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID0gaGlkZTtcblxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMuTGVnZW5kQ29udHJvbCA9IExlZ2VuZENvbnRyb2w7XG5cbm1vZHVsZS5leHBvcnRzLmxlZ2VuZENvbnRyb2wgPSBmdW5jdGlvbihvcHRpb25zKSB7XG4gICAgcmV0dXJuIG5ldyBMZWdlbmRDb250cm9sKG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIHJlcXVlc3QgPSByZXF1aXJlKCcuL3JlcXVlc3QnKSxcbiAgICBmb3JtYXRfdXJsID0gcmVxdWlyZSgnLi9mb3JtYXRfdXJsJyksXG4gICAgdXRpbCA9IHJlcXVpcmUoJy4vdXRpbCcpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBfbG9hZFRpbGVKU09OOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIGlmICh0eXBlb2YgXyA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIF8gPSBmb3JtYXRfdXJsLnRpbGVKU09OKF8sIHRoaXMub3B0aW9ucyAmJiB0aGlzLm9wdGlvbnMuYWNjZXNzVG9rZW4pO1xuICAgICAgICAgICAgcmVxdWVzdChfLCBMLmJpbmQoZnVuY3Rpb24oZXJyLCBqc29uKSB7XG4gICAgICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgICAgICB1dGlsLmxvZygnY291bGQgbm90IGxvYWQgVGlsZUpTT04gYXQgJyArIF8pO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ2Vycm9yJywge2Vycm9yOiBlcnJ9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGpzb24pIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fc2V0VGlsZUpTT04oanNvbik7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgncmVhZHknKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LCB0aGlzKSk7XG4gICAgICAgIH0gZWxzZSBpZiAoXyAmJiB0eXBlb2YgXyA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHRoaXMuX3NldFRpbGVKU09OKF8pO1xuICAgICAgICB9XG4gICAgfVxufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIHRpbGVMYXllciA9IHJlcXVpcmUoJy4vdGlsZV9sYXllcicpLnRpbGVMYXllcixcbiAgICBmZWF0dXJlTGF5ZXIgPSByZXF1aXJlKCcuL2ZlYXR1cmVfbGF5ZXInKS5mZWF0dXJlTGF5ZXIsXG4gICAgZ3JpZExheWVyID0gcmVxdWlyZSgnLi9ncmlkX2xheWVyJykuZ3JpZExheWVyLFxuICAgIGdyaWRDb250cm9sID0gcmVxdWlyZSgnLi9ncmlkX2NvbnRyb2wnKS5ncmlkQ29udHJvbCxcbiAgICBzaGFyZUNvbnRyb2wgPSByZXF1aXJlKCcuL3NoYXJlX2NvbnRyb2wnKS5zaGFyZUNvbnRyb2wsXG4gICAgbGVnZW5kQ29udHJvbCA9IHJlcXVpcmUoJy4vbGVnZW5kX2NvbnRyb2wnKS5sZWdlbmRDb250cm9sLFxuICAgIG1hcGJveExvZ29Db250cm9sID0gcmVxdWlyZSgnLi9tYXBib3hfbG9nbycpLm1hcGJveExvZ29Db250cm9sLFxuICAgIGZlZWRiYWNrID0gcmVxdWlyZSgnLi9mZWVkYmFjaycpO1xuXG5mdW5jdGlvbiB3aXRoQWNjZXNzVG9rZW4ob3B0aW9ucywgYWNjZXNzVG9rZW4pIHtcbiAgICBpZiAoIWFjY2Vzc1Rva2VuIHx8IG9wdGlvbnMuYWNjZXNzVG9rZW4pXG4gICAgICAgIHJldHVybiBvcHRpb25zO1xuICAgIHJldHVybiBMLmV4dGVuZCh7YWNjZXNzVG9rZW46IGFjY2Vzc1Rva2VufSwgb3B0aW9ucyk7XG59XG5cbnZhciBMTWFwID0gTC5NYXAuZXh0ZW5kKHtcbiAgICBpbmNsdWRlczogW3JlcXVpcmUoJy4vbG9hZF90aWxlanNvbicpXSxcblxuICAgIG9wdGlvbnM6IHtcbiAgICAgICAgdGlsZUxheWVyOiB7fSxcbiAgICAgICAgZmVhdHVyZUxheWVyOiB7fSxcbiAgICAgICAgZ3JpZExheWVyOiB7fSxcbiAgICAgICAgbGVnZW5kQ29udHJvbDoge30sXG4gICAgICAgIGdyaWRDb250cm9sOiB7fSxcbiAgICAgICAgc2hhcmVDb250cm9sOiBmYWxzZSxcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJylcbiAgICB9LFxuXG4gICAgX3RpbGVqc29uOiB7fSxcblxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKGVsZW1lbnQsIF8sIG9wdGlvbnMpIHtcblxuICAgICAgICBMLk1hcC5wcm90b3R5cGUuaW5pdGlhbGl6ZS5jYWxsKHRoaXMsIGVsZW1lbnQsXG4gICAgICAgICAgICBMLmV4dGVuZCh7fSwgTC5NYXAucHJvdG90eXBlLm9wdGlvbnMsIG9wdGlvbnMpKTtcblxuICAgICAgICAvLyBEaXNhYmxlIHRoZSBkZWZhdWx0ICdMZWFmbGV0JyB0ZXh0XG4gICAgICAgIGlmICh0aGlzLmF0dHJpYnV0aW9uQ29udHJvbCkge1xuICAgICAgICAgICAgdGhpcy5hdHRyaWJ1dGlvbkNvbnRyb2wuc2V0UHJlZml4KCcnKTtcblxuICAgICAgICAgICAgdmFyIGNvbXBhY3QgPSB0aGlzLm9wdGlvbnMuYXR0cmlidXRpb25Db250cm9sLmNvbXBhY3Q7XG4gICAgICAgICAgICAvLyBTZXQgYSBjb21wYWN0IGRpc3BsYXkgaWYgbWFwIGNvbnRhaW5lciB3aWR0aCBpcyA8IDY0MCBvclxuICAgICAgICAgICAgLy8gY29tcGFjdCBpcyBzZXQgdG8gYHRydWVgIGluIGF0dHJpYnV0aW9uQ29udHJvbCBvcHRpb25zLlxuICAgICAgICAgICAgaWYgKGNvbXBhY3QgfHwgKGNvbXBhY3QgIT09IGZhbHNlICYmIHRoaXMuX2NvbnRhaW5lci5vZmZzZXRXaWR0aCA8PSA2NDApKSB7XG4gICAgICAgICAgICAgICAgTC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuYXR0cmlidXRpb25Db250cm9sLl9jb250YWluZXIsICdsZWFmbGV0LWNvbXBhY3QtYXR0cmlidXRpb24nKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGNvbXBhY3QgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHRoaXMub24oJ3Jlc2l6ZScsIGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5fY29udGFpbmVyLm9mZnNldFdpZHRoID4gNjQwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5hdHRyaWJ1dGlvbkNvbnRyb2wuX2NvbnRhaW5lciwgJ2xlYWZsZXQtY29tcGFjdC1hdHRyaWJ1dGlvbicpO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgTC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuYXR0cmlidXRpb25Db250cm9sLl9jb250YWluZXIsICdsZWFmbGV0LWNvbXBhY3QtYXR0cmlidXRpb24nKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucy50aWxlTGF5ZXIpIHtcbiAgICAgICAgICAgIHRoaXMudGlsZUxheWVyID0gdGlsZUxheWVyKHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICB3aXRoQWNjZXNzVG9rZW4odGhpcy5vcHRpb25zLnRpbGVMYXllciwgdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuKSk7XG4gICAgICAgICAgICB0aGlzLmFkZExheWVyKHRoaXMudGlsZUxheWVyKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMuZmVhdHVyZUxheWVyKSB7XG4gICAgICAgICAgICB0aGlzLmZlYXR1cmVMYXllciA9IGZlYXR1cmVMYXllcih1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgd2l0aEFjY2Vzc1Rva2VuKHRoaXMub3B0aW9ucy5mZWF0dXJlTGF5ZXIsIHRoaXMub3B0aW9ucy5hY2Nlc3NUb2tlbikpO1xuICAgICAgICAgICAgdGhpcy5hZGRMYXllcih0aGlzLmZlYXR1cmVMYXllcik7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5vcHRpb25zLmdyaWRMYXllcikge1xuICAgICAgICAgICAgdGhpcy5ncmlkTGF5ZXIgPSBncmlkTGF5ZXIodW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIHdpdGhBY2Nlc3NUb2tlbih0aGlzLm9wdGlvbnMuZ3JpZExheWVyLCB0aGlzLm9wdGlvbnMuYWNjZXNzVG9rZW4pKTtcbiAgICAgICAgICAgIHRoaXMuYWRkTGF5ZXIodGhpcy5ncmlkTGF5ZXIpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucy5ncmlkTGF5ZXIgJiYgdGhpcy5vcHRpb25zLmdyaWRDb250cm9sKSB7XG4gICAgICAgICAgICB0aGlzLmdyaWRDb250cm9sID0gZ3JpZENvbnRyb2wodGhpcy5ncmlkTGF5ZXIsIHRoaXMub3B0aW9ucy5ncmlkQ29udHJvbCk7XG4gICAgICAgICAgICB0aGlzLmFkZENvbnRyb2wodGhpcy5ncmlkQ29udHJvbCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5vcHRpb25zLmxlZ2VuZENvbnRyb2wpIHtcbiAgICAgICAgICAgIHRoaXMubGVnZW5kQ29udHJvbCA9IGxlZ2VuZENvbnRyb2wodGhpcy5vcHRpb25zLmxlZ2VuZENvbnRyb2wpO1xuICAgICAgICAgICAgdGhpcy5hZGRDb250cm9sKHRoaXMubGVnZW5kQ29udHJvbCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5vcHRpb25zLnNoYXJlQ29udHJvbCkge1xuICAgICAgICAgICAgdGhpcy5zaGFyZUNvbnRyb2wgPSBzaGFyZUNvbnRyb2wodW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIHdpdGhBY2Nlc3NUb2tlbih0aGlzLm9wdGlvbnMuc2hhcmVDb250cm9sLCB0aGlzLm9wdGlvbnMuYWNjZXNzVG9rZW4pKTtcbiAgICAgICAgICAgIHRoaXMuYWRkQ29udHJvbCh0aGlzLnNoYXJlQ29udHJvbCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9tYXBib3hMb2dvQ29udHJvbCA9IG1hcGJveExvZ29Db250cm9sKHRoaXMub3B0aW9ucy5tYXBib3hMb2dvQ29udHJvbCk7XG4gICAgICAgIHRoaXMuYWRkQ29udHJvbCh0aGlzLl9tYXBib3hMb2dvQ29udHJvbCk7XG5cbiAgICAgICAgdGhpcy5fbG9hZFRpbGVKU09OKF8pO1xuXG4gICAgICAgIHRoaXMub24oJ2xheWVyYWRkJywgdGhpcy5fb25MYXllckFkZCwgdGhpcylcbiAgICAgICAgICAgIC5vbignbGF5ZXJyZW1vdmUnLCB0aGlzLl9vbkxheWVyUmVtb3ZlLCB0aGlzKVxuICAgICAgICAgICAgLm9uKCdtb3ZlZW5kJywgdGhpcy5fdXBkYXRlTWFwRmVlZGJhY2tMaW5rLCB0aGlzKTtcblxuICAgICAgICB0aGlzLndoZW5SZWFkeShmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBmZWVkYmFjay5vbignY2hhbmdlJywgdGhpcy5fdXBkYXRlTWFwRmVlZGJhY2tMaW5rLCB0aGlzKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5vbigndW5sb2FkJywgZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgZmVlZGJhY2sub2ZmKCdjaGFuZ2UnLCB0aGlzLl91cGRhdGVNYXBGZWVkYmFja0xpbmssIHRoaXMpO1xuICAgICAgICB9KTtcbiAgICB9LFxuXG4gICAgLy8gdXNlIGEgamF2YXNjcmlwdCBvYmplY3Qgb2YgdGlsZWpzb24gZGF0YSB0byBjb25maWd1cmUgdGhpcyBsYXllclxuICAgIF9zZXRUaWxlSlNPTjogZnVuY3Rpb24oXykge1xuICAgICAgICB0aGlzLl90aWxlanNvbiA9IF87XG4gICAgICAgIHRoaXMuX2luaXRpYWxpemUoXyk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBnZXRUaWxlSlNPTjogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl90aWxlanNvbjtcbiAgICB9LFxuXG4gICAgX2luaXRpYWxpemU6IGZ1bmN0aW9uKGpzb24pIHtcbiAgICAgICAgaWYgKHRoaXMudGlsZUxheWVyKSB7XG4gICAgICAgICAgICB0aGlzLnRpbGVMYXllci5fc2V0VGlsZUpTT04oanNvbik7XG4gICAgICAgICAgICB0aGlzLl91cGRhdGVMYXllcih0aGlzLnRpbGVMYXllcik7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5mZWF0dXJlTGF5ZXIgJiYgIXRoaXMuZmVhdHVyZUxheWVyLmdldEdlb0pTT04oKSAmJiBqc29uLmRhdGEgJiYganNvbi5kYXRhWzBdKSB7XG4gICAgICAgICAgICB0aGlzLmZlYXR1cmVMYXllci5sb2FkVVJMKGpzb24uZGF0YVswXSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5ncmlkTGF5ZXIpIHtcbiAgICAgICAgICAgIHRoaXMuZ3JpZExheWVyLl9zZXRUaWxlSlNPTihqc29uKTtcbiAgICAgICAgICAgIHRoaXMuX3VwZGF0ZUxheWVyKHRoaXMuZ3JpZExheWVyKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLmxlZ2VuZENvbnRyb2wgJiYganNvbi5sZWdlbmQpIHtcbiAgICAgICAgICAgIHRoaXMubGVnZW5kQ29udHJvbC5hZGRMZWdlbmQoanNvbi5sZWdlbmQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuc2hhcmVDb250cm9sKSB7XG4gICAgICAgICAgICB0aGlzLnNoYXJlQ29udHJvbC5fc2V0VGlsZUpTT04oanNvbik7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9tYXBib3hMb2dvQ29udHJvbC5fc2V0VGlsZUpTT04oanNvbik7XG5cbiAgICAgICAgaWYgKCF0aGlzLl9sb2FkZWQgJiYganNvbi5jZW50ZXIpIHtcbiAgICAgICAgICAgIHZhciB6b29tID0gdGhpcy5nZXRab29tKCkgIT09IHVuZGVmaW5lZCA/IHRoaXMuZ2V0Wm9vbSgpIDoganNvbi5jZW50ZXJbMl0sXG4gICAgICAgICAgICAgICAgY2VudGVyID0gTC5sYXRMbmcoanNvbi5jZW50ZXJbMV0sIGpzb24uY2VudGVyWzBdKTtcblxuICAgICAgICAgICAgdGhpcy5zZXRWaWV3KGNlbnRlciwgem9vbSk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgX3VwZGF0ZU1hcEZlZWRiYWNrTGluazogZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmICghdGhpcy5fY29udHJvbENvbnRhaW5lci5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKSByZXR1cm47XG4gICAgICAgIHZhciBsaW5rID0gdGhpcy5fY29udHJvbENvbnRhaW5lci5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCdtYXBib3gtaW1wcm92ZS1tYXAnKTtcbiAgICAgICAgaWYgKGxpbmsubGVuZ3RoICYmIHRoaXMuX2xvYWRlZCkge1xuICAgICAgICAgICAgdmFyIGNlbnRlciA9IHRoaXMuZ2V0Q2VudGVyKCkud3JhcCgpO1xuICAgICAgICAgICAgdmFyIHRpbGVqc29uID0gdGhpcy5fdGlsZWpzb24gfHwge307XG4gICAgICAgICAgICB2YXIgaWQgPSB0aWxlanNvbi5pZCB8fCAnJztcblxuICAgICAgICAgICAgdmFyIGhhc2ggPSAnIycgKyBpZCArICcvJyArXG4gICAgICAgICAgICAgICAgY2VudGVyLmxuZy50b0ZpeGVkKDMpICsgJy8nICtcbiAgICAgICAgICAgICAgICBjZW50ZXIubGF0LnRvRml4ZWQoMykgKyAnLycgK1xuICAgICAgICAgICAgICAgIHRoaXMuZ2V0Wm9vbSgpO1xuXG4gICAgICAgICAgICBmb3IgKHZhciBrZXkgaW4gZmVlZGJhY2suZGF0YSkge1xuICAgICAgICAgICAgICAgIGhhc2ggKz0gJy8nICsga2V5ICsgJz0nICsgZmVlZGJhY2suZGF0YVtrZXldO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmsubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBsaW5rW2ldLmhhc2ggPSBoYXNoO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSxcblxuICAgIF9vbkxheWVyQWRkOiBmdW5jdGlvbihlKSB7XG4gICAgICAgIGlmICgnb24nIGluIGUubGF5ZXIpIHtcbiAgICAgICAgICAgIGUubGF5ZXIub24oJ3JlYWR5JywgdGhpcy5fb25MYXllclJlYWR5LCB0aGlzKTtcbiAgICAgICAgfVxuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChMLmJpbmQodGhpcy5fdXBkYXRlTWFwRmVlZGJhY2tMaW5rLCB0aGlzKSwgMCk7IC8vIFVwZGF0ZSBhZnRlciBhdHRyaWJ1dGlvbiBjb250cm9sIHJlc2V0cyB0aGUgSFRNTC5cbiAgICB9LFxuXG4gICAgX29uTGF5ZXJSZW1vdmU6IGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgaWYgKCdvbicgaW4gZS5sYXllcikge1xuICAgICAgICAgICAgZS5sYXllci5vZmYoJ3JlYWR5JywgdGhpcy5fb25MYXllclJlYWR5LCB0aGlzKTtcbiAgICAgICAgfVxuICAgICAgICB3aW5kb3cuc2V0VGltZW91dChMLmJpbmQodGhpcy5fdXBkYXRlTWFwRmVlZGJhY2tMaW5rLCB0aGlzKSwgMCk7IC8vIFVwZGF0ZSBhZnRlciBhdHRyaWJ1dGlvbiBjb250cm9sIHJlc2V0cyB0aGUgSFRNTC5cbiAgICB9LFxuXG4gICAgX29uTGF5ZXJSZWFkeTogZnVuY3Rpb24oZSkge1xuICAgICAgICB0aGlzLl91cGRhdGVMYXllcihlLnRhcmdldCk7XG4gICAgfSxcblxuICAgIF91cGRhdGVMYXllcjogZnVuY3Rpb24obGF5ZXIpIHtcbiAgICAgICAgaWYgKCFsYXllci5vcHRpb25zKSByZXR1cm47XG5cbiAgICAgICAgaWYgKHRoaXMuYXR0cmlidXRpb25Db250cm9sICYmIHRoaXMuX2xvYWRlZCAmJiBsYXllci5nZXRBdHRyaWJ1dGlvbikge1xuICAgICAgICAgICAgdGhpcy5hdHRyaWJ1dGlvbkNvbnRyb2wuYWRkQXR0cmlidXRpb24obGF5ZXIuZ2V0QXR0cmlidXRpb24oKSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIShMLnN0YW1wKGxheWVyKSBpbiB0aGlzLl96b29tQm91bmRMYXllcnMpICYmXG4gICAgICAgICAgICAgICAgKGxheWVyLm9wdGlvbnMubWF4Wm9vbSB8fCBsYXllci5vcHRpb25zLm1pblpvb20pKSB7XG4gICAgICAgICAgICB0aGlzLl96b29tQm91bmRMYXllcnNbTC5zdGFtcChsYXllcildID0gbGF5ZXI7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl91cGRhdGVNYXBGZWVkYmFja0xpbmsoKTtcbiAgICAgICAgdGhpcy5fdXBkYXRlWm9vbUxldmVscygpO1xuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5NYXAgPSBMTWFwO1xuXG5tb2R1bGUuZXhwb3J0cy5tYXAgPSBmdW5jdGlvbihlbGVtZW50LCBfLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIG5ldyBMTWFwKGVsZW1lbnQsIF8sIG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGdlb2NvZGVyQ29udHJvbCA9IHJlcXVpcmUoJy4vZ2VvY29kZXJfY29udHJvbCcpLFxuICAgIGdyaWRDb250cm9sID0gcmVxdWlyZSgnLi9ncmlkX2NvbnRyb2wnKSxcbiAgICBmZWF0dXJlTGF5ZXIgPSByZXF1aXJlKCcuL2ZlYXR1cmVfbGF5ZXInKSxcbiAgICBsZWdlbmRDb250cm9sID0gcmVxdWlyZSgnLi9sZWdlbmRfY29udHJvbCcpLFxuICAgIHNoYXJlQ29udHJvbCA9IHJlcXVpcmUoJy4vc2hhcmVfY29udHJvbCcpLFxuICAgIHRpbGVMYXllciA9IHJlcXVpcmUoJy4vdGlsZV9sYXllcicpLFxuICAgIG1hcCA9IHJlcXVpcmUoJy4vbWFwJyksXG4gICAgZ3JpZExheWVyID0gcmVxdWlyZSgnLi9ncmlkX2xheWVyJyksXG4gICAgc3R5bGVMYXllciA9IHJlcXVpcmUoJy4vc3R5bGVfbGF5ZXInKTtcblxuTC5tYXBib3ggPSBtb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBWRVJTSU9OOiByZXF1aXJlKCcuLi9wYWNrYWdlLmpzb24nKS52ZXJzaW9uLFxuICAgIGdlb2NvZGVyOiByZXF1aXJlKCcuL2dlb2NvZGVyJyksXG4gICAgbWFya2VyOiByZXF1aXJlKCcuL21hcmtlcicpLFxuICAgIHNpbXBsZXN0eWxlOiByZXF1aXJlKCcuL3NpbXBsZXN0eWxlJyksXG4gICAgdGlsZUxheWVyOiB0aWxlTGF5ZXIudGlsZUxheWVyLFxuICAgIFRpbGVMYXllcjogdGlsZUxheWVyLlRpbGVMYXllcixcbiAgICBzdHlsZUxheWVyOiBzdHlsZUxheWVyLnN0eWxlTGF5ZXIsXG4gICAgU3R5bGVMYXllcjogc3R5bGVMYXllci5TdHlsZUxheWVyLFxuICAgIHNoYXJlQ29udHJvbDogc2hhcmVDb250cm9sLnNoYXJlQ29udHJvbCxcbiAgICBTaGFyZUNvbnRyb2w6IHNoYXJlQ29udHJvbC5TaGFyZUNvbnRyb2wsXG4gICAgbGVnZW5kQ29udHJvbDogbGVnZW5kQ29udHJvbC5sZWdlbmRDb250cm9sLFxuICAgIExlZ2VuZENvbnRyb2w6IGxlZ2VuZENvbnRyb2wuTGVnZW5kQ29udHJvbCxcbiAgICBnZW9jb2RlckNvbnRyb2w6IGdlb2NvZGVyQ29udHJvbC5nZW9jb2RlckNvbnRyb2wsXG4gICAgR2VvY29kZXJDb250cm9sOiBnZW9jb2RlckNvbnRyb2wuR2VvY29kZXJDb250cm9sLFxuICAgIGdyaWRDb250cm9sOiBncmlkQ29udHJvbC5ncmlkQ29udHJvbCxcbiAgICBHcmlkQ29udHJvbDogZ3JpZENvbnRyb2wuR3JpZENvbnRyb2wsXG4gICAgZ3JpZExheWVyOiBncmlkTGF5ZXIuZ3JpZExheWVyLFxuICAgIEdyaWRMYXllcjogZ3JpZExheWVyLkdyaWRMYXllcixcbiAgICBmZWF0dXJlTGF5ZXI6IGZlYXR1cmVMYXllci5mZWF0dXJlTGF5ZXIsXG4gICAgRmVhdHVyZUxheWVyOiBmZWF0dXJlTGF5ZXIuRmVhdHVyZUxheWVyLFxuICAgIG1hcDogbWFwLm1hcCxcbiAgICBNYXA6IG1hcC5NYXAsXG4gICAgY29uZmlnOiByZXF1aXJlKCcuL2NvbmZpZycpLFxuICAgIHNhbml0aXplOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJyksXG4gICAgdGVtcGxhdGU6IHJlcXVpcmUoJ211c3RhY2hlJykudG9faHRtbCxcbiAgICBmZWVkYmFjazogcmVxdWlyZSgnLi9mZWVkYmFjaycpXG59O1xuXG5cbi8vIEhhcmRjb2RlIGltYWdlIHBhdGgsIGJlY2F1c2UgTGVhZmxldCdzIGF1dG9kZXRlY3Rpb25cbi8vIGZhaWxzLCBiZWNhdXNlIG1hcGJveC5qcyBpcyBub3QgbmFtZWQgbGVhZmxldC5qc1xud2luZG93LkwuSWNvbi5EZWZhdWx0LmltYWdlUGF0aCA9XG4gICAgLy8gRGV0ZWN0IGJhZC1uZXdzIHByb3RvY29scyBsaWtlIGZpbGU6Ly8gYW5kIGhhcmRjb2RlXG4gICAgLy8gdG8gaHR0cHMgaWYgdGhleSdyZSBkZXRlY3RlZC5cbiAgICAoKGRvY3VtZW50LmxvY2F0aW9uLnByb3RvY29sID09PSAnaHR0cHM6JyB8fFxuICAgIGRvY3VtZW50LmxvY2F0aW9uLnByb3RvY29sID09PSAnaHR0cDonKSA/ICcnIDogJ2h0dHBzOicpICtcbiAgICAnLy9hcGkudGlsZXMubWFwYm94LmNvbS9tYXBib3guanMvJyArICd2JyArXG4gICAgcmVxdWlyZSgnLi4vcGFja2FnZS5qc29uJykudmVyc2lvbiArICcvaW1hZ2VzLyc7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBNYXBib3hMb2dvQ29udHJvbCA9IEwuQ29udHJvbC5leHRlbmQoe1xuXG4gICAgb3B0aW9uczoge1xuICAgICAgICBwb3NpdGlvbjogJ2JvdHRvbWxlZnQnXG4gICAgfSxcblxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKG9wdGlvbnMpIHtcbiAgICAgICAgTC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuICAgIH0sXG5cbiAgICBvbkFkZDogZnVuY3Rpb24oKSB7XG4gICAgICAgIHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdtYXBib3gtbG9nbycpO1xuICAgICAgICByZXR1cm4gdGhpcy5fY29udGFpbmVyO1xuICAgIH0sXG5cbiAgICBfc2V0VGlsZUpTT046IGZ1bmN0aW9uKGpzb24pIHtcbiAgICAgICAgLy8gQ2hlY2sgaWYgYWNjb3VudCByZWZlcmVuY2VkIGJ5IHRoZSBhY2Nlc3NUb2tlblxuICAgICAgICAvLyBpcyBhc3Njb2NpYXRlZCB3aXRoIHRoZSBNYXBib3ggTG9nb1xuICAgICAgICAvLyBhcyBkZXRlcm1pbmVkIGJ5IG1hcGJveC1tYXBzLlxuICAgICAgICBpZiAoanNvbi5tYXBib3hfbG9nbykge1xuICAgICAgICAgICAgTC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ21hcGJveC1sb2dvLXRydWUnKTtcbiAgICAgICAgfVxuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5NYXBib3hMb2dvQ29udHJvbCA9IE1hcGJveExvZ29Db250cm9sO1xuXG5tb2R1bGUuZXhwb3J0cy5tYXBib3hMb2dvQ29udHJvbCA9IGZ1bmN0aW9uKG9wdGlvbnMpIHtcbiAgICByZXR1cm4gbmV3IE1hcGJveExvZ29Db250cm9sKG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGZvcm1hdF91cmwgPSByZXF1aXJlKCcuL2Zvcm1hdF91cmwnKSxcbiAgICB1dGlsID0gcmVxdWlyZSgnLi91dGlsJyksXG4gICAgc2FuaXRpemUgPSByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJyk7XG5cbi8vIG1hcGJveC1yZWxhdGVkIG1hcmtlcnMgZnVuY3Rpb25hbGl0eVxuLy8gcHJvdmlkZSBhbiBpY29uIGZyb20gbWFwYm94J3Mgc2ltcGxlLXN0eWxlIHNwZWMgYW5kIGhvc3RlZCBtYXJrZXJzXG4vLyBzZXJ2aWNlXG5mdW5jdGlvbiBpY29uKGZwLCBvcHRpb25zKSB7XG4gICAgZnAgPSBmcCB8fCB7fTtcblxuICAgIHZhciBzaXplcyA9IHtcbiAgICAgICAgICAgIHNtYWxsOiBbMjAsIDUwXSxcbiAgICAgICAgICAgIG1lZGl1bTogWzMwLCA3MF0sXG4gICAgICAgICAgICBsYXJnZTogWzM1LCA5MF1cbiAgICAgICAgfSxcbiAgICAgICAgc2l6ZSA9IGZwWydtYXJrZXItc2l6ZSddIHx8ICdtZWRpdW0nLFxuICAgICAgICBzeW1ib2wgPSAoJ21hcmtlci1zeW1ib2wnIGluIGZwICYmIGZwWydtYXJrZXItc3ltYm9sJ10gIT09ICcnKSA/ICctJyArIGZwWydtYXJrZXItc3ltYm9sJ10gOiAnJyxcbiAgICAgICAgY29sb3IgPSAoZnBbJ21hcmtlci1jb2xvciddIHx8ICc3ZTdlN2UnKS5yZXBsYWNlKCcjJywgJycpO1xuXG4gICAgcmV0dXJuIEwuaWNvbih7XG4gICAgICAgIGljb25Vcmw6IGZvcm1hdF91cmwoJy92NC9tYXJrZXIvJyArXG4gICAgICAgICAgICAncGluLScgKyBzaXplLmNoYXJBdCgwKSArIHN5bWJvbCArICcrJyArIGNvbG9yICtcbiAgICAgICAgICAgIC8vIGRldGVjdCBhbmQgdXNlIHJldGluYSBtYXJrZXJzLCB3aGljaCBhcmUgeDIgcmVzb2x1dGlvblxuICAgICAgICAgICAgKEwuQnJvd3Nlci5yZXRpbmEgPyAnQDJ4JyA6ICcnKSArICcucG5nJywgb3B0aW9ucyAmJiBvcHRpb25zLmFjY2Vzc1Rva2VuKSxcbiAgICAgICAgaWNvblNpemU6IHNpemVzW3NpemVdLFxuICAgICAgICBpY29uQW5jaG9yOiBbc2l6ZXNbc2l6ZV1bMF0gLyAyLCBzaXplc1tzaXplXVsxXSAvIDJdLFxuICAgICAgICBwb3B1cEFuY2hvcjogWzAsIC1zaXplc1tzaXplXVsxXSAvIDJdXG4gICAgfSk7XG59XG5cbi8vIGEgZmFjdG9yeSB0aGF0IHByb3ZpZGVzIG1hcmtlcnMgZm9yIExlYWZsZXQgZnJvbSBNYXBib3gnc1xuLy8gW3NpbXBsZS1zdHlsZSBzcGVjaWZpY2F0aW9uXShodHRwczovL2dpdGh1Yi5jb20vbWFwYm94L3NpbXBsZXN0eWxlLXNwZWMpXG4vLyBhbmQgW01hcmtlcnMgQVBJXShodHRwOi8vbWFwYm94LmNvbS9kZXZlbG9wZXJzL2FwaS8jbWFya2VycykuXG5mdW5jdGlvbiBzdHlsZShmLCBsYXRsb24sIG9wdGlvbnMpIHtcbiAgICByZXR1cm4gTC5tYXJrZXIobGF0bG9uLCB7XG4gICAgICAgIGljb246IGljb24oZi5wcm9wZXJ0aWVzLCBvcHRpb25zKSxcbiAgICAgICAgdGl0bGU6IHV0aWwuc3RyaXBfdGFncyhcbiAgICAgICAgICAgIHNhbml0aXplKChmLnByb3BlcnRpZXMgJiYgZi5wcm9wZXJ0aWVzLnRpdGxlKSB8fCAnJykpXG4gICAgfSk7XG59XG5cbi8vIFNhbml0aXplIGFuZCBmb3JtYXQgcHJvcGVydGllcyBvZiBhIEdlb0pTT04gRmVhdHVyZSBvYmplY3QgaW4gb3JkZXJcbi8vIHRvIGZvcm0gdGhlIEhUTUwgc3RyaW5nIHVzZWQgYXMgdGhlIGFyZ3VtZW50IGZvciBgTC5jcmVhdGVQb3B1cGBcbmZ1bmN0aW9uIGNyZWF0ZVBvcHVwKGYsIHNhbml0aXplcikge1xuICAgIGlmICghZiB8fCAhZi5wcm9wZXJ0aWVzKSByZXR1cm4gJyc7XG4gICAgdmFyIHBvcHVwID0gJyc7XG5cbiAgICBpZiAoZi5wcm9wZXJ0aWVzLnRpdGxlKSB7XG4gICAgICAgIHBvcHVwICs9ICc8ZGl2IGNsYXNzPVwibWFya2VyLXRpdGxlXCI+JyArIGYucHJvcGVydGllcy50aXRsZSArICc8L2Rpdj4nO1xuICAgIH1cblxuICAgIGlmIChmLnByb3BlcnRpZXMuZGVzY3JpcHRpb24pIHtcbiAgICAgICAgcG9wdXAgKz0gJzxkaXYgY2xhc3M9XCJtYXJrZXItZGVzY3JpcHRpb25cIj4nICsgZi5wcm9wZXJ0aWVzLmRlc2NyaXB0aW9uICsgJzwvZGl2Pic7XG4gICAgfVxuXG4gICAgcmV0dXJuIChzYW5pdGl6ZXIgfHwgc2FuaXRpemUpKHBvcHVwKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgaWNvbjogaWNvbixcbiAgICBzdHlsZTogc3R5bGUsXG4gICAgY3JlYXRlUG9wdXA6IGNyZWF0ZVBvcHVwXG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgY29yc2xpdGUgPSByZXF1aXJlKCdjb3JzbGl0ZScpLFxuICAgIHN0cmljdCA9IHJlcXVpcmUoJy4vdXRpbCcpLnN0cmljdCxcbiAgICBjb25maWcgPSByZXF1aXJlKCcuL2NvbmZpZycpO1xuXG52YXIgcHJvdG9jb2wgPSAvXihodHRwcz86KT8oPz1cXC9cXC8oLnxhcGkpXFwudGlsZXNcXC5tYXBib3hcXC5jb21cXC8pLztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbih1cmwsIGNhbGxiYWNrKSB7XG4gICAgc3RyaWN0KHVybCwgJ3N0cmluZycpO1xuICAgIHN0cmljdChjYWxsYmFjaywgJ2Z1bmN0aW9uJyk7XG5cbiAgICB1cmwgPSB1cmwucmVwbGFjZShwcm90b2NvbCwgZnVuY3Rpb24obWF0Y2gsIHByb3RvY29sKSB7XG4gICAgICAgIGlmICghKCd3aXRoQ3JlZGVudGlhbHMnIGluIG5ldyB3aW5kb3cuWE1MSHR0cFJlcXVlc3QoKSkpIHtcbiAgICAgICAgICAgIC8vIFhEb21haW5SZXF1ZXN0IGluIHVzZTsgZG9lc24ndCBzdXBwb3J0IGNyb3NzLXByb3RvY29sIHJlcXVlc3RzXG4gICAgICAgICAgICByZXR1cm4gZG9jdW1lbnQubG9jYXRpb24ucHJvdG9jb2w7XG4gICAgICAgIH0gZWxzZSBpZiAocHJvdG9jb2wgPT09ICdodHRwczonIHx8IGRvY3VtZW50LmxvY2F0aW9uLnByb3RvY29sID09PSAnaHR0cHM6JyB8fCBjb25maWcuRk9SQ0VfSFRUUFMpIHtcbiAgICAgICAgICAgIHJldHVybiAnaHR0cHM6JztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiAnaHR0cDonO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICBmdW5jdGlvbiBvbmxvYWQoZXJyLCByZXNwKSB7XG4gICAgICAgIGlmICghZXJyICYmIHJlc3ApIHtcbiAgICAgICAgICAgIHJlc3AgPSBKU09OLnBhcnNlKHJlc3AucmVzcG9uc2VUZXh0KTtcbiAgICAgICAgfVxuICAgICAgICBjYWxsYmFjayhlcnIsIHJlc3ApO1xuICAgIH1cblxuICAgIHJldHVybiBjb3JzbGl0ZSh1cmwsIG9ubG9hZCk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgZm9ybWF0X3VybCA9IHJlcXVpcmUoJy4vZm9ybWF0X3VybCcpO1xuXG52YXIgU2hhcmVDb250cm9sID0gTC5Db250cm9sLmV4dGVuZCh7XG4gICAgaW5jbHVkZXM6IFtyZXF1aXJlKCcuL2xvYWRfdGlsZWpzb24nKV0sXG5cbiAgICBvcHRpb25zOiB7XG4gICAgICAgIHBvc2l0aW9uOiAndG9wbGVmdCcsXG4gICAgICAgIHVybDogJydcbiAgICB9LFxuXG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24oXywgb3B0aW9ucykge1xuICAgICAgICBMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG4gICAgICAgIHRoaXMuX2xvYWRUaWxlSlNPTihfKTtcbiAgICB9LFxuXG4gICAgX3NldFRpbGVKU09OOiBmdW5jdGlvbihqc29uKSB7XG4gICAgICAgIHRoaXMuX3RpbGVqc29uID0ganNvbjtcbiAgICB9LFxuXG4gICAgb25BZGQ6IGZ1bmN0aW9uKG1hcCkge1xuICAgICAgICB0aGlzLl9tYXAgPSBtYXA7XG5cbiAgICAgICAgdmFyIGNvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtbWFwYm94LXNoYXJlIGxlYWZsZXQtYmFyJyk7XG4gICAgICAgIHZhciBsaW5rID0gTC5Eb21VdGlsLmNyZWF0ZSgnYScsICdtYXBib3gtc2hhcmUgbWFwYm94LWljb24gbWFwYm94LWljb24tc2hhcmUnLCBjb250YWluZXIpO1xuICAgICAgICBsaW5rLmhyZWYgPSAnIyc7XG5cbiAgICAgICAgdGhpcy5fbW9kYWwgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwYm94LW1vZGFsJywgdGhpcy5fbWFwLl9jb250YWluZXIpO1xuICAgICAgICB0aGlzLl9tYXNrID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgJ21hcGJveC1tb2RhbC1tYXNrJywgdGhpcy5fbW9kYWwpO1xuICAgICAgICB0aGlzLl9jb250ZW50ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgJ21hcGJveC1tb2RhbC1jb250ZW50JywgdGhpcy5fbW9kYWwpO1xuXG4gICAgICAgIEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIobGluaywgJ2NsaWNrJywgdGhpcy5fc2hhcmVDbGljaywgdGhpcyk7XG4gICAgICAgIEwuRG9tRXZlbnQuZGlzYWJsZUNsaWNrUHJvcGFnYXRpb24oY29udGFpbmVyKTtcblxuICAgICAgICB0aGlzLl9tYXAub24oJ21vdXNlZG93bicsIHRoaXMuX2NsaWNrT3V0LCB0aGlzKTtcblxuICAgICAgICByZXR1cm4gY29udGFpbmVyO1xuICAgIH0sXG5cbiAgICBfY2xpY2tPdXQ6IGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgaWYgKHRoaXMuX3NoYXJpbmcpIHtcbiAgICAgICAgICAgIEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG4gICAgICAgICAgICBMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fbW9kYWwsICdhY3RpdmUnKTtcbiAgICAgICAgICAgIHRoaXMuX2NvbnRlbnQuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgICAgICB0aGlzLl9zaGFyaW5nID0gbnVsbDtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICBfc2hhcmVDbGljazogZnVuY3Rpb24oZSkge1xuICAgICAgICBMLkRvbUV2ZW50LnN0b3AoZSk7XG4gICAgICAgIGlmICh0aGlzLl9zaGFyaW5nKSByZXR1cm4gdGhpcy5fY2xpY2tPdXQoZSk7XG5cbiAgICAgICAgdmFyIHRpbGVqc29uID0gdGhpcy5fdGlsZWpzb24gfHwgdGhpcy5fbWFwLl90aWxlanNvbiB8fCB7fSxcbiAgICAgICAgICAgIHVybCA9IGVuY29kZVVSSUNvbXBvbmVudCh0aGlzLm9wdGlvbnMudXJsIHx8IHRpbGVqc29uLndlYnBhZ2UgfHwgd2luZG93LmxvY2F0aW9uKSxcbiAgICAgICAgICAgIG5hbWUgPSBlbmNvZGVVUklDb21wb25lbnQodGlsZWpzb24ubmFtZSksXG4gICAgICAgICAgICBpbWFnZSA9IGZvcm1hdF91cmwoJy92NC8nICsgdGlsZWpzb24uaWQgKyAnLycgKyB0aGlzLl9tYXAuZ2V0Q2VudGVyKCkubG5nICsgJywnICsgdGhpcy5fbWFwLmdldENlbnRlcigpLmxhdCArICcsJyArIHRoaXMuX21hcC5nZXRab29tKCkgKyAnLzYwMHg2MDAucG5nJywgdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuKSxcbiAgICAgICAgICAgIGVtYmVkID0gZm9ybWF0X3VybCgnL3Y0LycgKyB0aWxlanNvbi5pZCArICcuaHRtbCcsIHRoaXMub3B0aW9ucy5hY2Nlc3NUb2tlbiksXG4gICAgICAgICAgICB0d2l0dGVyVVJMID0gJy8vdHdpdHRlci5jb20vaW50ZW50L3R3ZWV0P3N0YXR1cz0nICsgbmFtZSArICcgJyArIHVybCxcbiAgICAgICAgICAgIGZhY2Vib29rVVJMID0gJy8vd3d3LmZhY2Vib29rLmNvbS9zaGFyZXIucGhwP3U9JyArIHVybCArICcmdD0nICsgbmFtZSxcbiAgICAgICAgICAgIHBpbnRlcmVzdFVSTCA9ICcvL3d3dy5waW50ZXJlc3QuY29tL3Bpbi9jcmVhdGUvYnV0dG9uLz91cmw9JyArIHVybCArICcmbWVkaWE9JyArIGltYWdlICsgJyZkZXNjcmlwdGlvbj0nICsgbmFtZSxcbiAgICAgICAgICAgIGVtYmVkVmFsdWUgPSAnPGlmcmFtZSB3aWR0aD1cIjEwMCVcIiBoZWlnaHQ9XCI1MDBweFwiIGZyYW1lQm9yZGVyPVwiMFwiIHNyYz1cIicgKyBlbWJlZCArICdcIj48L2lmcmFtZT4nLFxuICAgICAgICAgICAgZW1iZWRMYWJlbCA9ICdDb3B5IGFuZCBwYXN0ZSB0aGlzIDxzdHJvbmc+SFRNTCBjb2RlPC9zdHJvbmc+IGludG8gZG9jdW1lbnRzIHRvIGVtYmVkIHRoaXMgbWFwIG9uIHdlYiBwYWdlcy4nO1xuXG4gICAgICAgIGZ1bmN0aW9uIGNyZWF0ZVNoYXJlQnV0dG9uKGJ1dHRvbkNsYXNzLCBocmVmLCBzb2NpYWxNZWRpYU5hbWUpIHtcbiAgICAgICAgICAgIHZhciBlbGVtID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYScpO1xuICAgICAgICAgICAgZWxlbS5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgYnV0dG9uQ2xhc3MpO1xuICAgICAgICAgICAgZWxlbS5zZXRBdHRyaWJ1dGUoJ2hyZWYnLCBocmVmKTtcbiAgICAgICAgICAgIGVsZW0uc2V0QXR0cmlidXRlKCd0YXJnZXQnLCAnX2JsYW5rJyk7XG4gICAgICAgICAgICBzb2NpYWxNZWRpYU5hbWUgPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShzb2NpYWxNZWRpYU5hbWUpO1xuICAgICAgICAgICAgZWxlbS5hcHBlbmRDaGlsZChzb2NpYWxNZWRpYU5hbWUpO1xuXG4gICAgICAgICAgICByZXR1cm4gZWxlbTtcbiAgICAgICAgfVxuXG4gICAgICAgIEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tb2RhbCwgJ2FjdGl2ZScpO1xuXG4gICAgICAgIHRoaXMuX3NoYXJpbmcgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAnbWFwYm94LW1vZGFsLWJvZHknLCB0aGlzLl9jb250ZW50KTtcblxuICAgICAgICB2YXIgdHdpdHRlckJ1dHRvbiA9IGNyZWF0ZVNoYXJlQnV0dG9uKCdtYXBib3gtYnV0dG9uIG1hcGJveC1idXR0b24taWNvbiBtYXBib3gtaWNvbi10d2l0dGVyJywgdHdpdHRlclVSTCwgJ1R3aXR0ZXInKTtcbiAgICAgICAgdmFyIGZhY2Vib29rQnV0dG9uID0gY3JlYXRlU2hhcmVCdXR0b24oJ21hcGJveC1idXR0b24gbWFwYm94LWJ1dHRvbi1pY29uIG1hcGJveC1pY29uLWZhY2Vib29rJywgZmFjZWJvb2tVUkwsICdGYWNlYm9vaycpO1xuICAgICAgICB2YXIgcGludGVyZXN0QnV0dG9uID0gY3JlYXRlU2hhcmVCdXR0b24oJ21hcGJveC1idXR0b24gbWFwYm94LWJ1dHRvbi1pY29uIG1hcGJveC1pY29uLXBpbnRlcmVzdCcsIHBpbnRlcmVzdFVSTCwgJ1BpbnRlcmVzdCcpO1xuXG4gICAgICAgIHZhciBzaGFyZUhlYWRlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2gzJyk7XG4gICAgICAgIHZhciBzaGFyZVRleHQgPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSgnU2hhcmUgdGhpcyBtYXAnKTtcbiAgICAgICAgc2hhcmVIZWFkZXIuYXBwZW5kQ2hpbGQoc2hhcmVUZXh0KTtcblxuICAgICAgICB2YXIgc2hhcmVCdXR0b25zID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIHNoYXJlQnV0dG9ucy5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgJ21hcGJveC1zaGFyZS1idXR0b25zJyk7XG4gICAgICAgIHNoYXJlQnV0dG9ucy5hcHBlbmRDaGlsZChmYWNlYm9va0J1dHRvbik7XG4gICAgICAgIHNoYXJlQnV0dG9ucy5hcHBlbmRDaGlsZCh0d2l0dGVyQnV0dG9uKTtcbiAgICAgICAgc2hhcmVCdXR0b25zLmFwcGVuZENoaWxkKHBpbnRlcmVzdEJ1dHRvbik7XG5cbiAgICAgICAgdGhpcy5fc2hhcmluZy5hcHBlbmRDaGlsZChzaGFyZUhlYWRlcik7XG4gICAgICAgIHRoaXMuX3NoYXJpbmcuYXBwZW5kQ2hpbGQoc2hhcmVCdXR0b25zKTtcblxuICAgICAgICB2YXIgaW5wdXQgPSBMLkRvbVV0aWwuY3JlYXRlKCdpbnB1dCcsICdtYXBib3gtZW1iZWQnLCB0aGlzLl9zaGFyaW5nKTtcbiAgICAgICAgaW5wdXQudHlwZSA9ICd0ZXh0JztcbiAgICAgICAgaW5wdXQudmFsdWUgPSBlbWJlZFZhbHVlO1xuXG4gICAgICAgIHZhciBsYWJlbCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2xhYmVsJywgJ21hcGJveC1lbWJlZC1kZXNjcmlwdGlvbicsIHRoaXMuX3NoYXJpbmcpO1xuICAgICAgICBsYWJlbC5pbm5lckhUTUwgPSBlbWJlZExhYmVsO1xuXG4gICAgICAgIHZhciBjbG9zZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2EnLCAnbGVhZmxldC1wb3B1cC1jbG9zZS1idXR0b24nLCB0aGlzLl9zaGFyaW5nKTtcbiAgICAgICAgY2xvc2UuaHJlZiA9ICcjJztcblxuICAgICAgICBMLkRvbUV2ZW50LmRpc2FibGVDbGlja1Byb3BhZ2F0aW9uKHRoaXMuX3NoYXJpbmcpO1xuICAgICAgICBMLkRvbUV2ZW50LmFkZExpc3RlbmVyKGNsb3NlLCAnY2xpY2snLCB0aGlzLl9jbGlja091dCwgdGhpcyk7XG4gICAgICAgIEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIoaW5wdXQsICdjbGljaycsIGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgICAgIGUudGFyZ2V0LmZvY3VzKCk7XG4gICAgICAgICAgICBlLnRhcmdldC5zZWxlY3QoKTtcbiAgICAgICAgfSk7XG4gICAgfVxufSk7XG5cbm1vZHVsZS5leHBvcnRzLlNoYXJlQ29udHJvbCA9IFNoYXJlQ29udHJvbDtcblxubW9kdWxlLmV4cG9ydHMuc2hhcmVDb250cm9sID0gZnVuY3Rpb24oXywgb3B0aW9ucykge1xuICAgIHJldHVybiBuZXcgU2hhcmVDb250cm9sKF8sIG9wdGlvbnMpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxuLy8gYW4gaW1wbGVtZW50YXRpb24gb2YgdGhlIHNpbXBsZXN0eWxlIHNwZWMgZm9yIHBvbHlnb24gYW5kIGxpbmVzdHJpbmcgZmVhdHVyZXNcbi8vIGh0dHBzOi8vZ2l0aHViLmNvbS9tYXBib3gvc2ltcGxlc3R5bGUtc3BlY1xudmFyIGRlZmF1bHRzID0ge1xuICAgIHN0cm9rZTogJyM1NTU1NTUnLFxuICAgICdzdHJva2Utd2lkdGgnOiAyLFxuICAgICdzdHJva2Utb3BhY2l0eSc6IDEsXG4gICAgZmlsbDogJyM1NTU1NTUnLFxuICAgICdmaWxsLW9wYWNpdHknOiAwLjVcbn07XG5cbnZhciBtYXBwaW5nID0gW1xuICAgIFsnc3Ryb2tlJywgJ2NvbG9yJ10sXG4gICAgWydzdHJva2Utd2lkdGgnLCAnd2VpZ2h0J10sXG4gICAgWydzdHJva2Utb3BhY2l0eScsICdvcGFjaXR5J10sXG4gICAgWydmaWxsJywgJ2ZpbGxDb2xvciddLFxuICAgIFsnZmlsbC1vcGFjaXR5JywgJ2ZpbGxPcGFjaXR5J11cbl07XG5cbmZ1bmN0aW9uIGZhbGxiYWNrKGEsIGIpIHtcbiAgICB2YXIgYyA9IHt9O1xuICAgIGZvciAodmFyIGsgaW4gYikge1xuICAgICAgICBpZiAoYVtrXSA9PT0gdW5kZWZpbmVkKSBjW2tdID0gYltrXTtcbiAgICAgICAgZWxzZSBjW2tdID0gYVtrXTtcbiAgICB9XG4gICAgcmV0dXJuIGM7XG59XG5cbmZ1bmN0aW9uIHJlbWFwKGEpIHtcbiAgICB2YXIgZCA9IHt9O1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbWFwcGluZy5sZW5ndGg7IGkrKykge1xuICAgICAgICBkW21hcHBpbmdbaV1bMV1dID0gYVttYXBwaW5nW2ldWzBdXTtcbiAgICB9XG4gICAgcmV0dXJuIGQ7XG59XG5cbmZ1bmN0aW9uIHN0eWxlKGZlYXR1cmUpIHtcbiAgICByZXR1cm4gcmVtYXAoZmFsbGJhY2soZmVhdHVyZS5wcm9wZXJ0aWVzIHx8IHt9LCBkZWZhdWx0cykpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBzdHlsZTogc3R5bGUsXG4gICAgZGVmYXVsdHM6IGRlZmF1bHRzXG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgdXRpbCA9IHJlcXVpcmUoJy4vdXRpbCcpO1xudmFyIGZvcm1hdF91cmwgPSByZXF1aXJlKCcuL2Zvcm1hdF91cmwnKTtcbnZhciByZXF1ZXN0ID0gcmVxdWlyZSgnLi9yZXF1ZXN0Jyk7XG5cbnZhciBTdHlsZUxheWVyID0gTC5UaWxlTGF5ZXIuZXh0ZW5kKHtcblxuICAgIG9wdGlvbnM6IHtcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJylcbiAgICB9LFxuXG4gICAgaW5pdGlhbGl6ZTogZnVuY3Rpb24oXywgb3B0aW9ucykge1xuICAgICAgICBMLlRpbGVMYXllci5wcm90b3R5cGUuaW5pdGlhbGl6ZS5jYWxsKHRoaXMsIHVuZGVmaW5lZCwgTC5leHRlbmQoe30sIG9wdGlvbnMsIHtcbiAgICAgICAgICAgIHRpbGVTaXplOiA1MTIsXG4gICAgICAgICAgICB6b29tT2Zmc2V0OiAtMSxcbiAgICAgICAgICAgIG1pbk5hdGl2ZVpvb206IDAsXG4gICAgICAgICAgICB0bXM6IGZhbHNlXG4gICAgICAgIH0pKTtcbiAgICAgICAgdGhpcy5fdXJsID0gdGhpcy5fZm9ybWF0VGlsZVVSTChfKTtcbiAgICAgICAgdGhpcy5fZ2V0QXR0cmlidXRpb24oXyk7XG4gICAgfSxcblxuICAgIF9nZXRBdHRyaWJ1dGlvbjogZnVuY3Rpb24oXykge1xuICAgICAgICB2YXIgc3R5bGVVUkwgPSBmb3JtYXRfdXJsLnN0eWxlKF8sIHRoaXMub3B0aW9ucyAmJiB0aGlzLm9wdGlvbnMuYWNjZXNzVG9rZW4pO1xuICAgICAgICByZXF1ZXN0KHN0eWxlVVJMLCBMLmJpbmQoZnVuY3Rpb24oZXJyLCBzdHlsZSkge1xuICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgIHV0aWwubG9nKCdjb3VsZCBub3QgbG9hZCBNYXBib3ggc3R5bGUgYXQgJyArIHN0eWxlVVJMKTtcbiAgICAgICAgICAgICAgICB0aGlzLmZpcmUoJ2Vycm9yJywge2Vycm9yOiBlcnJ9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciBzb3VyY2VzID0gW107XG4gICAgICAgICAgICBmb3IgKHZhciBpZCBpbiBzdHlsZS5zb3VyY2VzKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNvdXJjZSA9IHN0eWxlLnNvdXJjZXNbaWRdLnVybC5zcGxpdCgnbWFwYm94Oi8vJylbMV07XG4gICAgICAgICAgICAgICAgc291cmNlcy5wdXNoKHNvdXJjZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXF1ZXN0KGZvcm1hdF91cmwudGlsZUpTT04oc291cmNlcy5qb2luKCksIHRoaXMub3B0aW9ucy5hY2Nlc3NUb2tlbiksIEwuYmluZChmdW5jdGlvbihlcnIsIGpzb24pIHtcbiAgICAgICAgICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgIHV0aWwubG9nKCdjb3VsZCBub3QgbG9hZCBUaWxlSlNPTiBhdCAnICsgXyk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgnZXJyb3InLCB7ZXJyb3I6IGVycn0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoanNvbikge1xuICAgICAgICAgICAgICAgICAgICB1dGlsLnN0cmljdChqc29uLCAnb2JqZWN0Jyk7XG5cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vcHRpb25zLmF0dHJpYnV0aW9uID0gdGhpcy5vcHRpb25zLnNhbml0aXplcihqc29uLmF0dHJpYnV0aW9uKTtcblxuICAgICAgICAgICAgICAgICAgICB0aGlzLl90aWxlanNvbiA9IGpzb247XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmlyZSgncmVhZHknKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LCB0aGlzKSk7XG4gICAgICAgIH0sIHRoaXMpKTtcbiAgICB9LFxuXG4gICAgLy8gZGlzYWJsZSB0aGUgc2V0VXJsIGZ1bmN0aW9uLCB3aGljaCBpcyBub3QgYXZhaWxhYmxlIG9uIG1hcGJveCB0aWxlbGF5ZXJzXG4gICAgc2V0VXJsOiBudWxsLFxuXG4gICAgX2Zvcm1hdFRpbGVVUkw6IGZ1bmN0aW9uKHN0eWxlKSB7XG4gICAgICAgIGlmICh0eXBlb2Ygc3R5bGUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICBpZiAoc3R5bGUuaW5kZXhPZignbWFwYm94Oi8vc3R5bGVzLycpID09PSAtMSkge1xuICAgICAgICAgICAgICAgIHV0aWwubG9nKCdJbmNvcnJlY3RseSBmb3JtYXR0ZWQgTWFwYm94IHN0eWxlIGF0ICcgKyBzdHlsZSk7XG4gICAgICAgICAgICAgICAgdGhpcy5maXJlKCdlcnJvcicpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIG93bmVySURTdHlsZSA9IHN0eWxlLnNwbGl0KCdtYXBib3g6Ly9zdHlsZXMvJylbMV07XG4gICAgICAgICAgICByZXR1cm4gZm9ybWF0X3VybCgnL3N0eWxlcy92MS8nICsgb3duZXJJRFN0eWxlICsgJy90aWxlcy97en0ve3h9L3t5fXtyfScsIHRoaXMub3B0aW9ucy5hY2Nlc3NUb2tlbik7XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHN0eWxlID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgICAgcmV0dXJuIGZvcm1hdF91cmwoJy9zdHlsZXMvdjEvJyArIHN0eWxlLm93bmVyICsgJy8nICsgc3R5bGUuaWQgKyAnL3RpbGVzL3t6fS97eH0ve3l9e3J9JywgdGhpcy5vcHRpb25zLmFjY2Vzc1Rva2VuKTtcbiAgICAgICAgfVxuICAgIH1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cy5TdHlsZUxheWVyID0gU3R5bGVMYXllcjtcblxubW9kdWxlLmV4cG9ydHMuc3R5bGVMYXllciA9IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICByZXR1cm4gbmV3IFN0eWxlTGF5ZXIoXywgb3B0aW9ucyk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgdXRpbCA9IHJlcXVpcmUoJy4vdXRpbCcpO1xudmFyIGZvcm1hdFBhdHRlcm4gPSAvXFwuKCg/OnBuZ3xqcGcpXFxkKikoPz0kfFxcPykvO1xuXG52YXIgVGlsZUxheWVyID0gTC5UaWxlTGF5ZXIuZXh0ZW5kKHtcbiAgICBpbmNsdWRlczogW3JlcXVpcmUoJy4vbG9hZF90aWxlanNvbicpXSxcblxuICAgIG9wdGlvbnM6IHtcbiAgICAgICAgc2FuaXRpemVyOiByZXF1aXJlKCdzYW5pdGl6ZS1jYWphJylcbiAgICB9LFxuXG4gICAgLy8gaHR0cDovL21hcGJveC5jb20vZGV2ZWxvcGVycy9hcGkvI2ltYWdlX3F1YWxpdHlcbiAgICBmb3JtYXRzOiBbXG4gICAgICAgICdwbmcnLCAnanBnJyxcbiAgICAgICAgLy8gUE5HXG4gICAgICAgICdwbmczMicsICdwbmc2NCcsICdwbmcxMjgnLCAncG5nMjU2JyxcbiAgICAgICAgLy8gSlBHXG4gICAgICAgICdqcGc3MCcsICdqcGc4MCcsICdqcGc5MCddLFxuXG4gICAgc2NhbGVQcmVmaXg6ICdAMnguJyxcblxuICAgIGluaXRpYWxpemU6IGZ1bmN0aW9uKF8sIG9wdGlvbnMpIHtcbiAgICAgICAgTC5UaWxlTGF5ZXIucHJvdG90eXBlLmluaXRpYWxpemUuY2FsbCh0aGlzLCB1bmRlZmluZWQsIG9wdGlvbnMpO1xuXG4gICAgICAgIHRoaXMuX3RpbGVqc29uID0ge307XG5cbiAgICAgICAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5mb3JtYXQpIHtcbiAgICAgICAgICAgIHV0aWwuc3RyaWN0X29uZW9mKG9wdGlvbnMuZm9ybWF0LCB0aGlzLmZvcm1hdHMpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fbG9hZFRpbGVKU09OKF8pO1xuICAgIH0sXG5cbiAgICBzZXRGb3JtYXQ6IGZ1bmN0aW9uKF8pIHtcbiAgICAgICAgdXRpbC5zdHJpY3QoXywgJ3N0cmluZycpO1xuICAgICAgICB0aGlzLm9wdGlvbnMuZm9ybWF0ID0gXztcbiAgICAgICAgdGhpcy5yZWRyYXcoKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIC8vIGRpc2FibGUgdGhlIHNldFVybCBmdW5jdGlvbiwgd2hpY2ggaXMgbm90IGF2YWlsYWJsZSBvbiBtYXBib3ggdGlsZWxheWVyc1xuICAgIHNldFVybDogbnVsbCxcblxuICAgIF9zZXRUaWxlSlNPTjogZnVuY3Rpb24oanNvbikge1xuICAgICAgICB1dGlsLnN0cmljdChqc29uLCAnb2JqZWN0Jyk7XG5cbiAgICAgICAgaWYgKCF0aGlzLm9wdGlvbnMuZm9ybWF0KSB7XG4gICAgICAgICAgdmFyIG1hdGNoID0ganNvbi50aWxlc1swXS5tYXRjaChmb3JtYXRQYXR0ZXJuKTtcbiAgICAgICAgICBpZiAobWF0Y2gpIHtcbiAgICAgICAgICAgICAgdGhpcy5vcHRpb25zLmZvcm1hdCA9IG1hdGNoWzFdO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIEwuZXh0ZW5kKHRoaXMub3B0aW9ucywge1xuICAgICAgICAgICAgdGlsZXM6IGpzb24udGlsZXMsXG4gICAgICAgICAgICBhdHRyaWJ1dGlvbjogdGhpcy5vcHRpb25zLnNhbml0aXplcihqc29uLmF0dHJpYnV0aW9uKSxcbiAgICAgICAgICAgIG1pblpvb206IGpzb24ubWluem9vbSB8fCAwLFxuICAgICAgICAgICAgbWF4Wm9vbToganNvbi5tYXh6b29tIHx8IDE4LFxuICAgICAgICAgICAgdG1zOiBqc29uLnNjaGVtZSA9PT0gJ3RtcycsXG4gICAgICAgICAgICBib3VuZHM6IGpzb24uYm91bmRzICYmIHV0aWwubGJvdW5kcyhqc29uLmJvdW5kcylcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5fdGlsZWpzb24gPSBqc29uO1xuICAgICAgICB0aGlzLnJlZHJhdygpO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgZ2V0VGlsZUpTT046IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fdGlsZWpzb247XG4gICAgfSxcblxuICAgIC8vIHRoaXMgaXMgYW4gZXhjZXB0aW9uIHRvIG1hcGJveC5qcyBuYW1pbmcgcnVsZXMgYmVjYXVzZSBpdCdzIGNhbGxlZFxuICAgIC8vIGJ5IGBMLm1hcGBcbiAgICBnZXRUaWxlVXJsOiBmdW5jdGlvbih0aWxlUG9pbnQpIHtcbiAgICAgICAgdmFyIHRpbGVzID0gdGhpcy5vcHRpb25zLnRpbGVzLFxuICAgICAgICAgICAgaW5kZXggPSBNYXRoLmZsb29yKE1hdGguYWJzKHRpbGVQb2ludC54ICsgdGlsZVBvaW50LnkpICUgdGlsZXMubGVuZ3RoKSxcbiAgICAgICAgICAgIHVybCA9IHRpbGVzW2luZGV4XTtcblxuICAgICAgICB2YXIgdGVtcGxhdGVkID0gTC5VdGlsLnRlbXBsYXRlKHVybCwgdGlsZVBvaW50KTtcbiAgICAgICAgaWYgKCF0ZW1wbGF0ZWQgfHwgIXRoaXMub3B0aW9ucy5mb3JtYXQpIHtcbiAgICAgICAgICAgIHJldHVybiB0ZW1wbGF0ZWQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gdGVtcGxhdGVkLnJlcGxhY2UoZm9ybWF0UGF0dGVybixcbiAgICAgICAgICAgICAgICAoTC5Ccm93c2VyLnJldGluYSA/IHRoaXMuc2NhbGVQcmVmaXggOiAnLicpICsgdGhpcy5vcHRpb25zLmZvcm1hdCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLy8gVGlsZUpTT04uVGlsZUxheWVycyBhcmUgYWRkZWQgdG8gdGhlIG1hcCBpbW1lZGlhdGVseSwgc28gdGhhdCB0aGV5IGdldFxuICAgIC8vIHRoZSBkZXNpcmVkIHotaW5kZXgsIGJ1dCBkbyBub3QgdXBkYXRlIHVudGlsIHRoZSBUaWxlSlNPTiBoYXMgYmVlbiBsb2FkZWQuXG4gICAgX3VwZGF0ZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmICh0aGlzLm9wdGlvbnMudGlsZXMpIHtcbiAgICAgICAgICAgIEwuVGlsZUxheWVyLnByb3RvdHlwZS5fdXBkYXRlLmNhbGwodGhpcyk7XG4gICAgICAgIH1cbiAgICB9XG59KTtcblxubW9kdWxlLmV4cG9ydHMuVGlsZUxheWVyID0gVGlsZUxheWVyO1xuXG5tb2R1bGUuZXhwb3J0cy50aWxlTGF5ZXIgPSBmdW5jdGlvbihfLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIG5ldyBUaWxlTGF5ZXIoXywgb3B0aW9ucyk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG5mdW5jdGlvbiBjb250YWlucyhpdGVtLCBsaXN0KSB7XG4gICAgaWYgKCFsaXN0IHx8ICFsaXN0Lmxlbmd0aCkgcmV0dXJuIGZhbHNlO1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGlzdC5sZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAobGlzdFtpXSA9PT0gaXRlbSkgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgaWRVcmw6IGZ1bmN0aW9uKF8sIHQpIHtcbiAgICAgICAgaWYgKF8uaW5kZXhPZignLycpID09PSAtMSkgdC5sb2FkSUQoXyk7XG4gICAgICAgIGVsc2UgdC5sb2FkVVJMKF8pO1xuICAgIH0sXG4gICAgbG9nOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIGlmICh0eXBlb2YgY29uc29sZSA9PT0gJ29iamVjdCcgJiZcbiAgICAgICAgICAgIHR5cGVvZiBjb25zb2xlLmVycm9yID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKF8pO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzdHJpY3Q6IGZ1bmN0aW9uKF8sIHR5cGUpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBfICE9PSB0eXBlKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgYXJndW1lbnQ6ICcgKyB0eXBlICsgJyBleHBlY3RlZCcpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzdHJpY3RfaW5zdGFuY2U6IGZ1bmN0aW9uKF8sIGtsYXNzLCBuYW1lKSB7XG4gICAgICAgIGlmICghKF8gaW5zdGFuY2VvZiBrbGFzcykpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBhcmd1bWVudDogJyArIG5hbWUgKyAnIGV4cGVjdGVkJyk7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIHN0cmljdF9vbmVvZjogZnVuY3Rpb24oXywgdmFsdWVzKSB7XG4gICAgICAgIGlmICghY29udGFpbnMoXywgdmFsdWVzKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGFyZ3VtZW50OiAnICsgXyArICcgZ2l2ZW4sIHZhbGlkIHZhbHVlcyBhcmUgJyArXG4gICAgICAgICAgICAgICAgdmFsdWVzLmpvaW4oJywgJykpO1xuICAgICAgICB9XG4gICAgfSxcbiAgICBzdHJpcF90YWdzOiBmdW5jdGlvbihfKSB7XG4gICAgICAgIHJldHVybiBfLnJlcGxhY2UoLzxbXjxdKz4vZywgJycpO1xuICAgIH0sXG4gICAgbGJvdW5kczogZnVuY3Rpb24oXykge1xuICAgICAgICAvLyBsZWFmbGV0LWNvbXBhdGlibGUgYm91bmRzLCBzaW5jZSBsZWFmbGV0IGRvZXMgbm90IGRvIGdlb2pzb25cbiAgICAgICAgcmV0dXJuIG5ldyBMLkxhdExuZ0JvdW5kcyhbW19bMV0sIF9bMF1dLCBbX1szXSwgX1syXV1dKTtcbiAgICB9XG59O1xuIl19
|