diff --git a/.gitignore b/.gitignore index 362a460..aa8eb86 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,5 @@ typings/ # own *.code-workspace +.history/ + diff --git a/README.md b/README.md index 33ea4ff..97eba19 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ # iwmlib +## Build + +Be sure that Python 3 is installed (for building the 3rd party library) and that you have installed Rollupjs globally `npm i rollup -g`. + +To build the two iwmlib files type `npm run build`. The files will be generated in the dist folder (iwmlib.js and iwmlib.pixi.js). If you want to watch the files for a continuously build type `npm run watch`. +To build the 3rd party library type `npm run 3rdparty`. The file will be generated in the dist folder (iwmlib.3rdparty.js). diff --git a/all.js b/all.js deleted file mode 100644 index 642c1a2..0000000 --- a/all.js +++ /dev/null @@ -1,7373 +0,0 @@ -(function () { - 'use strict'; - - // In order to test this interface implementation run jsc interface.js - - class Interface { - // Abstract interface that should be extended in interface subclasses. - // By convention all interfaces should start with an upper 'I' - - static implementationError(klass) { - let interfaceKeys = Reflect.ownKeys(this.prototype); - let classKeys = Reflect.ownKeys(klass.prototype); - for(let key of interfaceKeys) { - let interfaceDesc = this.prototype[key]; - let classDesc = klass.prototype[key]; - if (typeof(classDesc) == 'undefined') - return 'Missing ' + key - } - return null - } - - static implementedBy(klass) { - // In the first step only checks whether the methods of this - // interface are all implemented by the given class - let error = this.implementationError(klass); - return error == null - } - - // TODO: Specify optional methods - // static optionalMethods() { - // return [this.onMouseWheel] - // } - } - - /** Basic Application object to be used as a singleton. - Provides an interface for automatic testing and common device specific - feature detection. - */ - - class IApp extends Interface { - /** Build the app by registering event handlers, - * adding DOM elements, instanciating templates, etc... - */ - setup() { return this } - - /** Run the application by starting a main loop, ... - */ - run() { return this } - } - - class App extends Object { - /** Override this method to build your app. - */ - setup() { - return this - } - - /** Start and run the application. Override this method with everything - that is needed to maintain your App, main loops, etc. - */ - run() { - return this - } - - /** Defines all test suites. Overwrite this method to ensure that - all testable aspects of your App are evaluated. - */ - allTests() { - console.log('Overwrite App.allTests()'); - } - - /** Run all tests. Should return 'ok' and the amount of time needed to - run App.allTests() or a failure message with diagnostic error decription. - @return {array} - array with 'ok' as first element and needed time as - second element or "Tests failed" and an error string - */ - runTests() { - var start = performance.now(); - try { - this.allTests(); - var end = performance.now(); - return ['ok', end - start] - } - catch(e) { - console.trace(); - return ['Tests failed', e.message] - } - } - } - - IApp.implementedBy(App); - - // Allows browsers to perform doctests. - // Uses the code highlight package from http://highlightjs.readthedocs.io - // if available - - var docTestLogMessages = []; - - Array.prototype.equals = function(array) { - return this.length == array.length && - this.every( function(this_i,i) { return this_i == array[i] } ) - }; - - class Doctest { - - static assert(value) { - if (!value) { - throw new Error('Assertion violated') - } - } - - static pprint(obj) { - if (obj === null) - return 'null' - let stringified = obj.toString(); - if (stringified == '[object Object]') - return JSON.stringify(obj) - return stringified - } - - static expect(expr, value) { - if (this.pprint(expr) != this.pprint(value)) { - //throw new Error("got `" + expr + "` but expected `" + value + "`.") - throw new Error('got `' + this.pprint(expr) + '` but expected `' + this.pprint(value) + '`.') - } - } - - static expectError(error, message) { - let index = error.toString().indexOf(message); - if (index < 0) { - throw new Error('got `' + message + '` but expected `' + error + '`.') - } - } - - static expectLog(...messages) { - // if (!docTestLogMessages.equals(messages)) { - docTestLogMessages.forEach((msg, i) => { - if (msg != messages[i]) - throw new Error('Unexpected log message: `' + messages[i] + '`.') - }); - // throw new Error('Uups') - //} - } - - static log(message) { - docTestLogMessages.push(message); - } - - static highlight(code) { - if (typeof(hljs) == 'undefined') - return code - return hljs.highlight('javascript', code) - } - - static stripLeadingLines(code) { - let result = []; - let informative = false; - for(let line of code.split('\n')) { - if (line.trim().length > 0) { - informative = true; - } - if (informative) - result.push(line); - } - return result.join('\n') - } - - static event(type='mouse', {clientX = 0, clientY = 0} = {}) { - if (type.startsWith('mouse')) { - return new MouseEvent(type, { clientX, clientY }) - } - return { type, clientX, clientY } - } - - static run(replaceExpect=false) { - if (typeof(hljs) != 'undefined') { - hljs.initHighlighting(); - } - let doctests = document.querySelectorAll('.doctest'); - for(let i=0; i>> ').trim(); - if (line.endsWith(')') || line.endsWith(',')) { - line = line.slice(0, -1); - } - } - better.push(line); - } - pre.innerHTML = better.join('\n'); // text.value.replace(re, ">>> $1\n$2") - doctest.parentNode.replaceChild(pre, doctest); - } - } - } - - // Needed to make Doctest visible in modules - //window.Doctest = Doctest - - var recordedErrors = new Map(); - - class Errors { - - static countErrors() { - let total = 0; - for(let error of recordedErrors.keys()) { - total += recordedErrors.get(error).size; - } - return total - } - - static setStyle(element, styles) { - for(let key in styles) { - element.style[key] = styles[key]; - } - } - - static appendError(error, source) { - if (recordedErrors.has(error)) { - let sources = recordedErrors.get(error); - sources.add(source); - } - else { - recordedErrors.set(error, new Set([source])); - } - } - - static showErrors() { - if (this.countErrors() == 0) { - return - } - let errors = document.getElementById('runtime-errors'); - if (errors == null) { - errors = document.createElement('div'); - errors.setAttribute('id', 'runtime-errors'); - this.setStyle(document.body, { - border: '2px solid red' - }); - this.setStyle(errors, {position: 'absolute', - top: '0px', - padding: '8px', - width: '100%', - background: 'red', - color: 'white'}); - document.body.appendChild(errors); - let counter = document.createElement('div'); - counter.setAttribute('id', 'runtime-errors-counter'); - this.setStyle(counter, {borderRadius: '50%', - width: '32px', - height: '32px', - background: 'white', - color: 'red', - fontSize: '18px', - textAlign: 'center', - lineHeight: '32px', - verticalAlign: 'middle'}); - counter.innerHTML = '1'; - errors.appendChild(counter); - - let header = document.createElement('div'); - this.setStyle(header, {position: 'absolute', - top: '6px', - left: '48px', - height: '44px', - fontSize: '32px'}); - header.innerHTML = 'Runtime Errors'; - errors.appendChild(header); - errors.addEventListener('click', this.toggleErrors.bind(this)); - } - let counter = document.getElementById('runtime-errors-counter'); - counter.innerHTML = this.countErrors(); - } - - static expandErrors() { - let errors = document.getElementById('runtime-errors'); - for(let error of recordedErrors.keys()) { - for(var source of recordedErrors.get(error)) { - if (typeof(source) == 'undefined') { - source = 'See console for details'; - return - } - let info = document.createElement('div'); - info.className = 'info'; - info.style.wordWrap = 'break-word'; - info.innerHTML = error + `
${source}`; - errors.appendChild(info); - } - } - } - - static toggleErrors() { - let errors = document.getElementById('runtime-errors'); - let infos = errors.querySelectorAll('.info'); - if (infos.length > 0) { - infos.forEach((info) => errors.removeChild(info)); - } - else { - this.expandErrors(); - } - } - - static removeError(event) { - console.log('removeError', event); - if (recordedErrors.has(event.error)) { - let sources = recordedErrors.get(event.error); - sources.delete(event.source); - console.log('sources', sources); - } - } - - static registerGlobalErrorHandler() { - // Register more informative error handler - window.addEventListener('error', (event) => { - // if (typeof(event.error) == 'undefined') { - // console.info("Catched undefined error", event) - // } - this.appendError(event.error, event.filename); - }, true); - - document.addEventListener('DOMContentLoaded', (event) => { - this.showErrors(); - }); - } - - static registerFrameAwaitErrors() { - let iframes = document.getElementsByTagName('iframe'); - for(let i=0; i { - this.appendError('Cannot load iframe', target.src);}, - frameErrorTimeout); - target.onload = () => { - clearTimeout(target.iframeTimeout); - }; - } - } - } - - Errors.registerGlobalErrorHandler(); - - class Events { - - static stop(event) { - event.preventDefault(); - event.stopPropagation(); - } - - static extractPoint(event) { - switch (event.constructor.name) { - case 'TouchEvent': - for (let i = 0; i < event.targetTouches.length; i++) { - let t = event.targetTouches[i]; - return { x: t.clientX, y: t.clientY } - } - break - default: - return { x: event.clientX, y: event.clientY } - } - } - - static isCaptured(event) { - if (event.__capturedBy) - return true - return false - } - - static capturedBy(event, obj) { - event.__capturedBy = obj; - } - - static isMouseDown(event) { - // Attempts to clone the which attribute of events failed in WebKit. May - // be this is a bug or a security feature. Workaround: we introduce - // a mouseDownSubstitute attribute that can be assigned to cloned - // events after instantiation. - if (Reflect.has(event, 'mouseDownSubstitute')) - return event.mouseDownSubstitute - return event.buttons || event.which - } - - static isSimulatedEvent(event) { - return Reflect.has(event, 'mouseDownSubstitute') - } - - static isMouseRightClick(event) { - return event.buttons || event.which - } - - static extractTouches(targets) { - let touches = []; - for (let i = 0; i < targets.length; i++) { - let t = targets[i]; - touches.push({ - targetSelector: this.selector(t.target), - identifier: t.identifier, - screenX: t.screenX, - screenY: t.screenY, - clientX: t.clientX, - clientY: t.clientY, - pageX: t.pageX, - pageY: t.pageY - }); - } - return touches - } - - static createTouchList(targets) { - let touches = []; - for (let i = 0; i < targets.length; i++) { - let t = targets[i]; - let touchTarget = document.elementFromPoint(t.pageX, t.pageY); - let touch = new Touch(undefined, touchTarget, t.identifier, - t.pageX, t.pageY, t.screenX, t.screenY); - touches.push(touch); - } - return new TouchList(...touches) - } - - static extractEvent(timestamp, event) { - let targetSelector = this.selector(event.target); - let infos = { - type: event.type, - time: timestamp, - constructor: event.constructor, - data: { - targetSelector: targetSelector, - view: event.view, - mouseDownSubstitute: event.buttons || event.which, // which cannot be cloned directly - bubbles: event.bubbles, - cancelable: event.cancelable, - screenX: event.screenX, - screenY: event.screenY, - clientX: event.clientX, - clientY: event.clientY, - layerX: event.layerX, - layerY: event.layerY, - pageX: event.pageX, - pageY: event.pageY, - ctrlKey: event.ctrlKey, - altKey: event.altKey, - shiftKey: event.shiftKey, - metaKey: event.metaKey - } - }; - if (event.type.startsWith('touch')) { - // On Safari-WebKit the TouchEvent has layerX, layerY coordinates - let data = infos.data; - data.targetTouches = this.extractTouches(event.targetTouches); - data.changedTouches = this.extractTouches(event.changedTouches); - data.touches = this.extractTouches(event.touches); - } - if (event.type.startsWith('pointer')) { - let data = infos.data; - data.pointerId = event.pointerId; - data.pointerType = event.pointerType; - } - if (Events.debug) { - Events.extracted.push(this.toLine(event)); - } - return infos - } - - static cloneEvent(type, constructor, data) { - if (type.startsWith('touch')) { - // We need to find target from layerX, layerY - //var target = document.querySelector(data.targetSelector) - // elementFromPoint(data.layerX, data.layerY) - //data.target = target - data.targetTouches = this.createTouchList(data.targetTouches); - data.changedTouches = this.createTouchList(data.changedTouches); - data.touches = this.createTouchList(data.touches); - } - // We need to find target from pageX, pageY which are only - // available after construction. They seem to getter items. - - let clone = Reflect.construct(constructor, [type, data]); - clone.mouseDownSubstitute = data.mouseDownSubstitute; - return clone - } - - static simulateEvent(type, constructor, data) { - data.target = document.querySelector(data.targetSelector); - let clone = this.cloneEvent(type, constructor, data); - if (data.target != null) { - data.target.dispatchEvent(clone); - } - if (Events.debug) { - Events.simulated.push(this.toLine(clone)); - } - } - - static toLine(event) { - return `${event.type} #${event.target.id} ${event.clientX} ${event.clientY}` - let result = event.type; - let selector = this.selector(event.target); - result += ' selector: ' + selector; - if (event.target != document.querySelector(selector)) - console.log('Cannot resolve', selector); - let keys = ['layerX', 'layerY', 'pageX', 'pageY', 'clientX', 'clientY']; - for (let key of keys) { - try { - result += ' ' + key + ':' + event[key]; - } - catch (e) { - console.log('Invalid key: ' + key); - } - } - return result - } - - static compareExtractedWithSimulated() { - if (this.extracted.length != this.simulated.length) { - alert('Unequal length of extracted [' + this.extracted.length + - '] and simulated events [' + this.simulated.length + '].'); - } - else { - for (let i = 0; i < this.extracted.length; i++) { - var extracted = this.extracted[i]; - var simulated = this.simulated[i]; - if (extracted != simulated) { - console.log('Events differ:' + extracted + '|' + simulated); - } - } - } - } - - static selector(context) { - return OptimalSelect.select(context) - } - - static reset() { - this.extracted = []; - this.simulated = []; - } - - static resetSimulated() { - this.simulated = []; - } - - static showExtractedEvents(event) { - if (!event.shiftKey) { - return - } - if (this.popup == null) { - let element = document.createElement('div'); - Elements.setStyle(element, { - position: 'absolute', - width: '480px', - height: '640px', - overflow: 'auto', - backgroundColor: 'lightgray' - }); - document.body.appendChild(element); - this.popup = element; - } - this.popup.innerHTML = ''; - for (let line of this.extracted) { - let div = document.createElement('div'); - div.innerHTML = line; - this.popup.appendChild(div); - } - let div = document.createElement('div'); - div.innerHTML = '------------ Simulated -----------'; - this.popup.appendChild(div); - for (let line of this.simulated) { - let div = document.createElement('div'); - div.innerHTML = line; - this.popup.appendChild(div); - } - Elements.setStyle(this.popup, - { left: event.clientX + 'px', top: event.clientY + 'px' }); - } - } - - Events.popup = null; - Events.debug = true; - Events.extracted = []; - Events.simulated = []; - Events.simulationRunning = false; - - class EventRecorder { - - constructor() { - this.recording = []; - this.recorded = []; - this.step = 0; - } - - record(event) { - let length = this.recording.length; - if (length == 0) { - this.startTime = event.timeStamp; - Events.reset(); - } - else { - let last = this.recording[length - 1]; - if (event.timeStamp < last.time) { - console.log('warning: wrong temporal order'); - } - } - let t = event.timeStamp - this.startTime; - this.recording.push(Events.extractEvent(t, event)); - } - - stopRecording() { - this.recorded = this.recording; - this.recording = []; - console.log('Recorded ' + this.recorded.length + ' events'); - } - - startReplay(whileCondition = null, onComplete = null) { - this.step = 0; - Events.resetSimulated(); - console.log('Start replay'); - Events.simulationRunning = true; - this.replay(whileCondition, onComplete); - } - - replay(whileCondition = null, onComplete = null) { - if (this.step < this.recorded.length) { - let { type, time, constructor, data } = this.recorded[this.step]; - Events.simulateEvent(type, constructor, data); - - this.step += 1; - let dt = 0; - if (this.step < this.recorded.length) { - var next = this.recorded[this.step]; - dt = next.time - time; - if (dt < 0) { - console.log('warning: wrong temporal order'); - } - } - if (whileCondition == null || whileCondition()) { - let delta = Math.round(dt); - setTimeout(() => this.replay(whileCondition, onComplete), delta); - } - } - else { - console.log('Played ' + this.step + ' events' + onComplete); - Events.simulationRunning = false; - if (onComplete != null) { - onComplete(); - } - //Events.compareExtractedWithSimulated() - } - } - } - - /* globals WebKitPoint */ - - /** Tests whether an object is empty - * @param {Object} obj - the object to be tested - * @return {boolean} - */ - function isEmpty(obj) { - // > isEmpty({}) - // true - for (let i in obj) { - return false - } - return true - } - - function lerp(start, stop, amt) { - return amt * (stop - start) + start - } - - - // Returns a function, that, as long as it continues to be invoked, will not - // be triggered. The function will be called after it stops being called for - // N milliseconds. If `immediate` is passed, trigger the function on the - // leading edge, instead of the trailing. - // Taken from: https://davidwalsh.name/essential-javascript-functions - function debounce(func, wait, immediate) { - let timeout; - return function () { - let context = this, - args = arguments; - let later = function () { - timeout = null; - if (!immediate) func.apply(context, args); - }; - let callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - } - } - - /** Returns an id that is guaranteed to be unique within the livetime of the - * application - * @return {string} - */ - let _idGenerator = 0; - function getId$1() { - return 'id' + _idGenerator++ - } - - function randomInt(min = 0, max = 100) { - return Math.floor(Math.random() * (max - min + 1) + min) - } - - function randomFloat(min = 0.0, max = 1.0) { - return Math.random() * (max - min) + min - } - - class Dates { - - static create(fullYear, month, day) { - return new Date(Date.UTC(fullYear, month, day)) - } - - static daysInMonth(date) { - return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate() - } - - static startYearRange(date) { - return new Date(Date.UTC(date.getFullYear() - 1, 11, 31, 23, 59, 59, 999)) - } - - static endYearRange(date) { - return new Date(Date.UTC(date.getFullYear() + 1, 0, 1)) - } - - static prevYear(date, offset = 1) { - return this.create(date.getFullYear() - offset, 0, 1) - } - - static nextYear(date, offset = 1) { - return this.create(date.getFullYear() + offset, 0, 1) - } - - static nextMonth(date) { - return this.create(date.getFullYear(), date.getMonth() + 1, 1) - } - - static nextDay(date) { - return this.create( - date.getFullYear(), - date.getMonth(), - date.getDate() + 1 - ) - } - - static nextHour(date) { - // See http://stackoverflow.com/questions/1050720/adding-hours-to-javascript-date-object - return new Date(date.getTime() + 60 * 60 * 1000) - } - - static nextMinute(date) { - // See above - return new Date(date.getTime() + 60 * 1000) - } - - static nextSecond(date) { - // See above - return new Date(date.getTime() + 1000) - } - - static nextMillisecond(date) { - // See above - return new Date(date.getTime() + 1) - } - - static *iterYears(start, end) { - let date = this.create(start.getFullYear(), 0, 1); - while (date <= end) { - yield date; - date = this.nextYear(date); - } - yield date; - } - - static *iterMonths(year, limit = 12) { - let month = 0; - while (month < limit) { - let date = this.create(year.getFullYear(), month, 1); - yield date; - month += 1; - } - } - - static *iterMonthsOfYears(years) { - for (let year of years) { - for (let month of this.iterMonths(year)) { - yield month; - } - } - } - - static *iterDays(month) { - let day = 1; - let limit = Dates.daysInMonth(month); - while (day <= limit) { - let date = this.create(month.getFullYear(), month.getMonth(), day); - yield date; - day += 1; - } - } - - static *iterDaysOfMonths(months) { - for (let month of months) { - for (let day of this.iterDays(month)) { - yield day; - } - } - } - } - /* Color conversion functions */ - - class Colors { - // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb - - static rgb2num(red, green, blue) { - let rgb = blue | (green << 8) | (red << 16); - return 0x000000 + rgb - } - - static rgb2hex(red, green, blue) { - let rgb = blue | (green << 8) | (red << 16); - return '#' + (0x1000000 + rgb).toString(16).slice(1) - } - - static hex2rgb(hex) { - // long version - let r = hex.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i); - if (r) { - return r.slice(1, 4).map(x => { - return parseInt(x, 16) - }) - } - // short version - r = hex.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i); - if (r) { - return r.slice(1, 4).map(x => { - return 0x11 * parseInt(x, 16) - }) - } - return null - } - - static rgb(r, g, b) { - return { r, g, b } - } - - static string2hex(str) { - return parseInt('0x' + str.slice(1)) - } - - static lerp(rgb1, rgb2, amount) { - return { - r: Math.round(lerp(rgb1.r, rgb2.r, amount)), - g: Math.round(lerp(rgb1.g, rgb2.g, amount)), - b: Math.round(lerp(rgb1.b, rgb2.b, amount)) - } - } - - static get violet() { - return Colors.rgb2num(89, 34, 131) - } - - static get steelblue() { - return Colors.rgb2num(0, 130, 164) - } - - static get ochre() { - return Colors.rgb2num(181, 157, 0) - } - - static get turquoise() { - return Colors.rgb2num(34, 164, 131) - } - - static get eminence() { - return Colors.rgb2num(150, 60, 134) - } - - static random() { - let r = Math.round(Math.random() * 255); - let g = Math.round(Math.random() * 255); - let b = Math.round(Math.random() * 255); - return Colors.rgb2num(r, g, b) - } - } - - class Cycle extends Array { - constructor(...items) { - super(); - for (let item of items) { - this.push(item); - } - this.index = 0; - } - - next() { - if (this.index == this.length) { - this.index = 0; - } - return this[this.index++] - } - - current() { - if (this.index === this.length) { - this.index = 0; - } - return this[this.index] - } - } - - /** Static methods to compute 2D points with x and y coordinates. - */ - class Points { - static length(a) { - return Math.sqrt(a.x * a.x + a.y * a.y) - } - - static normalize(p) { - let len = this.length(p); - return this.multiplyScalar(p, 1 / len) - } - - static mean(a, b) { - return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 } - } - - static subtract(a, b) { - return { x: a.x - b.x, y: a.y - b.y } - } - - static multiply(a, b) { - return { x: a.x * b.x, y: a.y * b.y } - } - - static divide(a, b) { - return { x: a.x / b.x, y: a.y / b.y } - } - - static multiplyScalar(a, b) { - return { x: a.x * b, y: a.y * b } - } - - static add(a, b) { - return { x: a.x + b.x, y: a.y + b.y } - } - - static negate(p) { - return { x: -p.x, y: -p.y } - } - - static angle(p1, p2) { - return Math.atan2(p1.y - p2.y, p1.x - p2.x) - } - - static normalizedAngle(p1, p2) { - return Angle.normalize(this.angle(p1, p2)) - } - - static normalized2Angle(p1, p2) { - return Angle.normalize2(this.angle(p1, p2)) - } - - static arc(p, alpha, radius) { - return { - x: p.x + radius * Math.cos(alpha), - y: p.y + radius * Math.sin(alpha) - } - } - - static distance(a, b) { - let dx = a.x - b.x; - let dy = a.y - b.y; - return Math.sqrt(dx * dx + dy * dy) - } - - static fromPageToNode(element, p) { - // if (window.webkitConvertPointFromPageToNode) { - // return window.webkitConvertPointFromPageToNode(element, - // new WebKitPoint(p.x, p.y)) - // } - return window.convertPointFromPageToNode(element, p.x, p.y) - } - - static fromNodeToPage(element, p) { - // if (window.webkitConvertPointFromNodeToPage) { - // return window.webkitConvertPointFromNodeToPage(element, - // new WebKitPoint(p.x, p.y)) - // } - return window.convertPointFromNodeToPage(element, p.x, p.y) - } - } - - /** - * A helper class for common set operations. - * - * @export - * @class Sets - */ - class Sets { - - /** - * Returns the intersection of all sets - * https://stackoverflow.com/questions/31930894/javascript-set-data-structure-intersect - * @static - * @param {*} sets - * @returns - * @memberof Sets - */ - static intersect(...sets) { - if (!sets.length) return new Set() - const i = sets.reduce((m, s, i) => s.size < sets[m].size ? i : m, 0); - const [smallest] = sets.splice(i, 1); - const res = new Set(); - for (let val of smallest) - if (sets.every(s => s.has(val))) - res.add(val); - return res - } - - /** - * Returns the union of all sets - * - * @static - * @param {*} sets - * @returns - * @memberof Sets - */ - static union(...sets) { - let result = new Set(); - for (let set of sets) { - for (let m of set) { - result.add(m); - } - } - return result - } - - /** - * Returns the difference of the given sets. Starts with the first set and removing all elements of the following sets. - * - * @static - * @param {*} set - * @param {*} sets - * @returns - * @memberof Sets - */ - static difference(set, ...sets) { - let result = new Set(); - for (let m of set) { - result.add(m); - } - for (let s of sets) { - for (let m of s) { - result.delete(m); - } - } - return result - } - } - - /** Static methods to compute angles. - */ - class Angle { - - static normalize(angle) { - let TAU = Math.PI * 2.0; - while (angle > Math.PI) { - angle -= TAU; - } - while (angle < -Math.PI) { - angle += TAU; - } - return angle - } - - static normalize2(angle) { - let TAU = Math.PI * 2.0; - while (angle > TAU) { - angle -= TAU; - } - while (angle < 0) { - angle += TAU; - } - return angle - } - - static normalizeDegree(angle) { - let full = 360.0; - while (angle > 180.0) { - angle -= full; - } - while (angle < -180.0) { - angle += full; - } - return angle - } - - static normalizedDiff(a, b) { - return this.normalize(this.diff(a, b)) - } - - static normalized2Diff(a, b) { - return this.normalize2(this.diff(a, b)) - } - - static diff(a, b) { - return Math.atan2(Math.sin(a - b), Math.cos(a - b)) - } - - static degree2radian(degree) { - return Math.PI * degree / 180.0 - } - - static radian2degree(rad) { - return 180.0 / Math.PI * rad - } - } - - class Elements$1 { - static setStyle(element, styles) { - for (let key in styles) { - element.style[key] = styles[key]; - } - } - - static addClass(element, cssClass) { - element.classList.add(cssClass); - } - - static removeClass(element, cssClass) { - element.classList.remove(cssClass); - } - - static toggleClass(element, cssClass) { - element.classList.toggle(cssClass); - } - - static hasClass(element, cssClass) { - return element.classList.contains(cssClass) - } - } - - class MapProxy { - /* This class is needed if we want to use the interaction classes - in Firefox 45.8 and modern Browsers. - - A workaround for https://github.com/babel/babel/issues/2334 - */ - constructor() { - this.map = new Map(); - } - - get size() { - return this.map.size - } - - get(key) { - return this.map.get(key) - } - - set(key, value) { - return this.map.set(key, value) - } - - delete(key) { - return this.map.delete(key) - } - - clear() { - return this.map.clear() - } - - has(key) { - return this.map.has(key) - } - - keys() { - return this.map.keys() - } - - values() { - return this.map.values() - } - - entries() { - return this.map.entries() - } - - forEach(func) { - this.map.forEach(func); - } - } - - /* Based om https://gist.github.com/cwleonard/e124d63238bda7a3cbfa */ - class Polygon { - /* - * This is the Polygon constructor. All points are center-relative. - */ - constructor(center) { - this.points = new Array(); - this.center = center; - } - - /* - * Point x and y values should be relative to the center. - */ - addPoint(p) { - this.points.push(p); - } - - /* - * Point x and y values should be absolute coordinates. - */ - addAbsolutePoint(p) { - this.points.push({ x: p.x - this.center.x, y: p.y - this.center.y }); - } - - /* - * Returns the number of sides. Equal to the number of vertices. - */ - getNumberOfSides() { - return this.points.length - } - - /* - * rotate the polygon by a number of radians - */ - rotate(rads) { - for (let i = 0; i < this.points.length; i++) { - let x = this.points[i].x; - let y = this.points[i].y; - this.points[i].x = Math.cos(rads) * x - Math.sin(rads) * y; - this.points[i].y = Math.sin(rads) * x + Math.cos(rads) * y; - } - } - - /* - * The draw function takes as a parameter a Context object from - * a Canvas element and draws the polygon on it. - */ - draw(context, { lineWidth = 2, stroke = '#000000', fill = null } = {}) { - context.beginPath(); - context.moveTo( - this.points[0].x + this.center.x, - this.points[0].y + this.center.y - ); - for (let i = 1; i < this.points.length; i++) { - context.lineTo( - this.points[i].x + this.center.x, - this.points[i].y + this.center.y - ); - } - context.closePath(); - context.lineWidth = lineWidth; - if (stroke) { - context.strokeStyle = stroke; - context.stroke(); - } - if (fill) { - context.fillStyle = fill; - context.fill(); - } - } - - absolutePoints() { - let result = new Array(); - for (let p of this.points) { - result.push(Points.add(p, this.center)); - } - return result - } - - flatAbsolutePoints() { - let result = new Array(); - for (let p of this.points) { - let a = Points.add(p, this.center); - result.push(a.x); - result.push(a.y); - } - return result - } - - /* - * This function returns true if the given point is inside the polygon, - * and false otherwise. - */ - containsPoint(pnt) { - let nvert = this.points.length; - let testx = pnt.x; - let testy = pnt.y; - - let vertx = new Array(); - for (let q = 0; q < this.points.length; q++) { - vertx.push(this.points[q].x + this.center.x); - } - - let verty = new Array(); - for (let w = 0; w < this.points.length; w++) { - verty.push(this.points[w].y + this.center.y); - } - - let i, - j = 0; - let c = false; - for (i = 0, j = nvert - 1; i < nvert; j = i++) { - if ( - verty[i] > testy != verty[j] > testy && - testx < - (vertx[j] - vertx[i]) * - (testy - verty[i]) / - (verty[j] - verty[i]) + - vertx[i] - ) - c = !c; - } - return c - } - - multiplyScalar(scale) { - let center = Points.multiplyScalar(this.center, scale); - let clone = new Polygon(center); - for (let p of this.points) { - clone.addPoint(Points.multiplyScalar(p, scale)); - } - return clone - } - - /* - * To detect intersection with another Polygon object, this - * function uses the Separating Axis Theorem. It returns false - * if there is no intersection, or an object if there is. The object - * contains 2 fields, overlap and axis. Moving the polygon by overlap - * on axis will get the polygons out of intersection. - */ - intersectsWith(other) { - let axis = { x: 0, y: 0 }; - let tmp, minA, maxA, minB, maxB; - let side, i; - let smallest = null; - let overlap = 99999999; - - /* test polygon A's sides */ - for (side = 0; side < this.getNumberOfSides(); side++) { - /* get the axis that we will project onto */ - if (side == 0) { - axis.x = - this.points[this.getNumberOfSides() - 1].y - - this.points[0].y; - axis.y = - this.points[0].x - - this.points[this.getNumberOfSides() - 1].x; - } else { - axis.x = this.points[side - 1].y - this.points[side].y; - axis.y = this.points[side].x - this.points[side - 1].x; - } - - /* normalize the axis */ - tmp = Math.sqrt(axis.x * axis.x + axis.y * axis.y); - axis.x /= tmp; - axis.y /= tmp; - - /* project polygon A onto axis to determine the min/max */ - minA = maxA = this.points[0].x * axis.x + this.points[0].y * axis.y; - for (i = 1; i < this.getNumberOfSides(); i++) { - tmp = this.points[i].x * axis.x + this.points[i].y * axis.y; - if (tmp > maxA) maxA = tmp; - else if (tmp < minA) minA = tmp; - } - /* correct for offset */ - tmp = this.center.x * axis.x + this.center.y * axis.y; - minA += tmp; - maxA += tmp; - - /* project polygon B onto axis to determine the min/max */ - minB = maxB = - other.points[0].x * axis.x + other.points[0].y * axis.y; - for (i = 1; i < other.getNumberOfSides(); i++) { - tmp = other.points[i].x * axis.x + other.points[i].y * axis.y; - if (tmp > maxB) maxB = tmp; - else if (tmp < minB) minB = tmp; - } - /* correct for offset */ - tmp = other.center.x * axis.x + other.center.y * axis.y; - minB += tmp; - maxB += tmp; - - /* test if lines intersect, if not, return false */ - if (maxA < minB || minA > maxB) { - return false - } else { - let o = maxA > maxB ? maxB - minA : maxA - minB; - if (o < overlap) { - overlap = o; - smallest = { x: axis.x, y: axis.y }; - } - } - } - - /* test polygon B's sides */ - for (side = 0; side < other.getNumberOfSides(); side++) { - /* get the axis that we will project onto */ - if (side == 0) { - axis.x = - other.points[other.getNumberOfSides() - 1].y - - other.points[0].y; - axis.y = - other.points[0].x - - other.points[other.getNumberOfSides() - 1].x; - } else { - axis.x = other.points[side - 1].y - other.points[side].y; - axis.y = other.points[side].x - other.points[side - 1].x; - } - - /* normalize the axis */ - tmp = Math.sqrt(axis.x * axis.x + axis.y * axis.y); - axis.x /= tmp; - axis.y /= tmp; - - /* project polygon A onto axis to determine the min/max */ - minA = maxA = this.points[0].x * axis.x + this.points[0].y * axis.y; - for (i = 1; i < this.getNumberOfSides(); i++) { - tmp = this.points[i].x * axis.x + this.points[i].y * axis.y; - if (tmp > maxA) maxA = tmp; - else if (tmp < minA) minA = tmp; - } - /* correct for offset */ - tmp = this.center.x * axis.x + this.center.y * axis.y; - minA += tmp; - maxA += tmp; - - /* project polygon B onto axis to determine the min/max */ - minB = maxB = - other.points[0].x * axis.x + other.points[0].y * axis.y; - for (i = 1; i < other.getNumberOfSides(); i++) { - tmp = other.points[i].x * axis.x + other.points[i].y * axis.y; - if (tmp > maxB) maxB = tmp; - else if (tmp < minB) minB = tmp; - } - /* correct for offset */ - tmp = other.center.x * axis.x + other.center.y * axis.y; - minB += tmp; - maxB += tmp; - - /* test if lines intersect, if not, return false */ - if (maxA < minB || minA > maxB) { - return false - } else { - let o = maxA > maxB ? maxB - minA : maxA - minB; - if (o < overlap) { - overlap = o; - smallest = { x: axis.x, y: axis.y }; - } - } - } - return { overlap: overlap + 0.001, axis: smallest } - } - - static fromPoints(points) { - let min = { x: Number.MAX_VALUE, y: Number.MAX_VALUE }; - let max = { x: Number.MIN_VALUE, y: Number.MIN_VALUE }; - for (let p of points) { - min.x = Math.min(p.x, min.x); - max.x = Math.max(p.x, max.x); - min.y = Math.min(p.y, min.y); - max.y = Math.max(p.y, max.y); - } - let center = Points.mean(min, max); - let polygon = new Polygon(center); - for (let p of points) { - polygon.addAbsolutePoint(p); - } - return polygon - } - } - - - /** - * Util functions to deal with DOMRects. - */ - class Rect { - - - /** - * Test if a given point is contained by the provided Rect. - * - * @static - * @param {DOMRect} rect - Rectangle to check the collision with. - * @param {Point} point - Point that should be tested. - * @returns {boolean} - True if point is inside of rect, otherwise false. - * @memberof Rect - */ - static contains(rect, point) { - return (point.x > rect.left && - point.x < rect.x + rect.right - && point.y > rect.top && point.y < rect.bottom) - } - - - /** - *Returns the position of an rect as point object. - * - * @static - * @param {Rect} rect - The rectangle we want to get the position from. - * @returns {Point} - Returns the position as Point. - * @memberof Rect - */ - static getPosition(rect) { - return { x: rect.x, y: rect.y } - } - } - - /** String utility functions */ - - class Strings { - - static toUpperCaseFirstChar(str) { - return str.substr(0, 1).toUpperCase() + str.substr(1) - } - - static toLowerCaseFirstChar(str) { - return str.substr(0, 1).toLowerCase() + str.substr(1) - } - - static toUpperCaseEachWord(str, delim = ' ') { - return str.split(delim).map((v) => v.toUpperCaseFirstChar()).join(delim) - } - - static toLowerCaseEachWord(str, delim = ' ') { - return str.split(delim).map((v) => v.toLowerCaseFirstChar()).join(delim) - } - - } - - /* globals Hammer, propagating */ - - /** Interaction patterns - - See interaction.html for explanation - */ - - class IInteractionTarget extends Interface { - capture(event) { - return typeof true - } - - onStart(event, interaction) { } - onMove(event, interaction) { } - onEnd(event, interaction) { } - - onMouseWheel(event) { } - } - - class IInteractionMapperTarget extends Interface { - capture(event) { - return typeof true - } - - findTarget(event, local, global) { - return IInteractionTarget - } - } - - class PointMap extends MapProxy { - // Collects touch points, mouse coordinates, etc. as key value pairs. - // Keys are pointer and touch ids, the special "mouse" key. - // Values are points, i.e. all objects with numeric x and y properties. - constructor(points = {}) { - super(); - for (let key in points) { - this.set(key, points[key]); - } - } - - toString() { - let points = []; - for (let key of this.keys()) { - let value = this.get(key); - points.push(`${key}:{x:${value.x}, y:${value.y}}`); - } - let attrs = points.join(', '); - return `[PointMap ${attrs}]` - } - - clone() { - let result = new PointMap(); - for (let key of this.keys()) { - let value = this.get(key); - result.set(key, { x: value.x, y: value.y }); - } - return result - } - - keyOf(value) { - for (let key of this.keys()) { - let p = this.get(key); - if (p.x == value.x && p.y == value.y) { - return key - } - } - return null - } - - firstKey() { - for (let key of this.keys()) { - return key - } - return null - } - - first() { - for (let key of this.keys()) { - return this.get(key) - } - return null - } - - farthests() { - if (this.size == 0) { - return null - } - let pairs = []; - for (let key of this.keys()) { - let p = this.get(key); - p.key = key; - for (let k of this.keys()) { - let q = this.get(k); - q.key = k; - pairs.push([p, q]); - } - } - let sorted = pairs.sort((a, b) => { - return Points.distance(b[0], b[1]) - Points.distance(a[0], a[1]) - }); - return sorted[0] - } - - mean() { - if (this.size == 0) { - return null - } - let x = 0.0, - y = 0.0; - for (let p of this.values()) { - x += p.x; - y += p.y; - } - return { x: x / this.size, y: y / this.size } - } - } - - class InteractionDelta { - constructor(x, y, zoom, rotate, about) { - this.x = x; - this.y = y; - this.zoom = zoom; - this.rotate = rotate; - this.about = about; - } - - toString() { - let values = []; - for (let key of Object.keys(this)) { - let value = this[key]; - if (key == 'about') { - values.push(`${key}:{x:${value.x}, y:${value.y}}`); - } else { - values.push(`${key}:${value}`); - } - } - let attrs = values.join(', '); - return `[InteractionDelta ${attrs}]` - } - } - - class InteractionPoints { - constructor(parent = null) { - this.parent = parent; - this.current = new PointMap(); - this.previous = new PointMap(); - this.start = new PointMap(); - this.ended = new PointMap(); - this.timestamps = new Map(); - } - - moved(key) { - let current = this.current.get(key); - let previous = this.previous.get(key); - return Points.subtract(current, previous) - } - - move() { - let current = this.current.mean(); - let previous = this.previous.mean(); - return Points.subtract(current, previous) - } - - /** - * Computes the delta between previous and current angles. Corrects - * value that are larger than 45° - * @param {*} a - * @param {*} b - * @returns delta - */ - diffAngle(a, b) { - let alpha = Math.atan2(Math.sin(a - b), Math.cos(a - b)); - if (Math.abs(alpha) > Math.PI / 4) { - alpha -= Math.PI; - } - return alpha - } - - /** - * Computes the delta between interaction points at t and t+1. - * - * @returns InteractionDelta - * @memberof InteractionPoints - */ - delta() { - let csize = this.current.size; - let psize = this.previous.size; - if (csize >= 2 && csize == psize) { - // Reduce to the two farthests points - let current = this.current.farthests(); - - let c1 = current[0]; - let c2 = current[1]; - - let p1 = this.previous.get(c1.key); - let p2 = this.previous.get(c2.key); - - //let p1 = previous[0] - //let p2 = previous[1] - - let d1 = Points.subtract(c1, p1); - let d2 = Points.subtract(c2, p2); - let cm = Points.mean(c1, c2); - //let pm = Points.mean(p1, p2) - // UO: Using the mean lead to jumps between time slices with 3 and 2 fingers - // We use the mean of deltas instead - let delta = Points.mean(d1, d2); //Points.subtract(cm, pm) - let zoom = 1.0; - let distance1 = Points.distance(p1, p2); - let distance2 = Points.distance(c1, c2); - if (distance1 != 0 && distance2 != 0) { - zoom = distance2 / distance1; - } - let currentAngle = Points.angle(c1, c2); - let previousAngle = Points.angle(p1, p2); - let alpha = this.diffAngle(currentAngle, previousAngle); - return new InteractionDelta(delta.x, delta.y, zoom, alpha, cm) - } else if (csize == 1 && psize == 1 && this.current.firstKey() == this.previous.firstKey()) { - // We need to ensure that the keys are the same - let current = this.current.first(); - let previous = this.previous.first(); - let delta = Points.subtract(current, previous); - return new InteractionDelta(delta.x, delta.y, 1.0, 0.0, current) - } - return null - } - - started(key, point) { - this.current.set(key, point); - this.start.set(key, point); - this.previous.set(key, point); - this.timestamps.set(key, performance.now()); - } - - update(key, point) { - // Returns true iff the key is new - this.current.set(key, point); - if (!this.start.has(key)) { - this.start.set(key, point); - this.previous.set(key, point); - this.timestamps.set(key, performance.now()); - return true - } - return false - } - - updatePrevious() { - for (let key of this.current.keys()) { - this.previous.set(key, this.current.get(key)); - } - } - - stop(key, point) { - if (this.current.has(key)) { - this.current.delete(key); - this.previous.delete(key); - this.ended.set(key, point); - } - } - - finish(key, point) { - this.current.delete(key); - this.previous.delete(key); - this.start.delete(key); - this.timestamps.delete(key); - this.ended.delete(key); - } - - isFinished() { - return this.current.size == 0 - } - - isNoLongerTwoFinger() { - return this.previous.size > 1 && this.current.size < 2 - } - - isTap(key) { - return this.parent.isTap(key) - } - - isDoubleTap(key) { - return this.parent.isDoubleTap(key) - } - - isLongPress(key) { - return this.parent.isLongPress(key) - } - } - - class Interaction extends InteractionPoints { - constructor(tapDistance = 10, tapDuration = 250.0, longPressTime = 500.0) { - super(); - this.tapDistance = tapDistance; - this.tapCounts = new Map(); - this.tapPositions = new Map(); - this.tapTimestamps = new Map(); - this.tapDuration = tapDuration; - this.longPressTime = longPressTime; - this.targets = new Map(); - this.subInteractions = new Map(); // target:Object : InteractionPoints - } - - stop(key, point) { - super.stop(key, point); - for (let points of this.subInteractions.values()) { - points.stop(key, point); - } - } - - addTarget(key, target) { - this.targets.set(key, target); - this.subInteractions.set(target, new InteractionPoints(this)); - } - - removeTarget(key) { - let target = this.targets.get(key); - this.targets.delete(key); - // Only remove target if no keys are refering to the target - let remove = true; - for (let t of this.targets.values()) { - if (target === t) { - remove = false; - } - } - if (remove) { - this.subInteractions.delete(target); - } - } - - finish(key, point) { - super.finish(key, point); - this.removeTarget(key); - } - - mapInteraction(points, aspects, mappingFunc) { - // Map centrally registered points to target interactions - // Returns an array of [target, updated subInteraction] pairs - let result = new Map(); - for (let key in points) { - if (this.targets.has(key)) { - let target = this.targets.get(key); - if (this.subInteractions.has(target)) { - let interaction = this.subInteractions.get(target); - for (let aspect of aspects) { - let pointMap = this[aspect]; - let point = pointMap.get(key); - let mapped = mappingFunc(point, target); - interaction[aspect].set(key, mapped); - } - result.set(target, interaction); - } - } - } - return result - } - - registerTap(key, point) { - if (this.tapCounts.has(key)) { - let count = this.tapCounts.get(key); - this.tapCounts.set(key, count+1); - } - else { - this.tapCounts.set(key, 1); - } - this.tapPositions.set(key, point); - this.tapTimestamps.set(key, performance.now()); - } - - unregisterTap(key) { - this.tapCounts.delete(key); - this.tapPositions.delete(key); - this.tapTimestamps.delete(key); - } - - isTap(key) { - let ended = this.ended.get(key); - let start = this.start.get(key); - if ( - start && - ended && - Points.distance(ended, start) < this.tapDistance - ) { - let t1 = this.timestamps.get(key); - let tookLong = performance.now() > t1 + this.longPressTime; - if (tookLong) { - return false - } - return true - } - return false - } - - isDoubleTap(key) { - let ended = this.ended.get(key); - if (this.tapCounts.has(key) && this.tapCounts.get(key) > 2) { - this.unregisterTap(key); - } - if (this.tapPositions.has(key)) { - let pos = this.tapPositions.get(key); - if (Points.distance(ended, pos) > this.tapDistance) { - this.unregisterTap(key); - } - } - if (this.tapTimestamps.has(key) && performance.now() > this.tapTimestamps.get(key) + this.tapDuration) { - //console.log("tap too long") - this.unregisterTap(key); - } - let result = false; - if (this.isTap(key)) { - - this.registerTap(key, ended); - result = this.tapCounts.get(key) == 2; - } - else { - this.unregisterTap(key); - } - //console.log("isDoubleTap", this.tapCounts.get(key), result) - return result - } - - isAnyTap() { - for (let key of this.ended.keys()) { - if (this.isTap(key)) return true - } - return false - } - - isLongPress(key) { - let ended = this.ended.get(key); - let start = this.start.get(key); - if ( - start && - ended && - Points.distance(ended, start) < this.tapDistance - ) { - let t1 = this.timestamps.get(key); - let tookLong = performance.now() > t1 + this.longPressTime; - if (tookLong) { - return true - } - return false - } - return false - } - - isAnyLongPress() { - for (let key of this.ended.keys()) { - if (this.isLongPress(key)) return true - } - return false - } - - isStylus(key) { - return key === 'stylus' - } - } - - /** - * This class implements the main delegate functionality: All necessary event handlers are registered for the - * given element. Uses PointerEvents if available or TouchEvents on iOS. The fallback is on mouse events. - * Collects the events if the interaction target captures the start event (i.e. declares that - * the target wants the start event as well as all following move and end evcents.) - * - * @export - * @class InteractionDelegate - */ - class InteractionDelegate { - // Long press: http://stackoverflow.com/questions/1930895/how-long-is-the-event-onlongpress-in-the-android - // Stylus support: https://w3c.github.io/touch-events/ - - /** - * Creates an instance of InteractionDelegate. - * @param {any} element - * @param {any} target - * @param {any} [{ mouseWheelElement = null, useCapture = true, capturePointerEvents = true, debug = false }={}] - * @memberof InteractionDelegate - */ - constructor( - element, - target, - { mouseWheelElement = null, useCapture = true, capturePointerEvents = true, cancelOnWindowOut = true, debug = false } = {} - ) { - this.debug = debug; - this.interaction = new Interaction(); - this.element = element; - this.mouseWheelElement = mouseWheelElement || element; - this.target = target; - this.useCapture = useCapture; - this.capturePointerEvents = capturePointerEvents; - this.cancelOnWindowOut = cancelOnWindowOut; - this.setupInteraction(); - } - - setupInteraction() { - if (this.debug) { - let error = this.targetInterface.implementationError( - this.target.constructor - ); - if (error != null) { - throw new Error('Expected IInteractionTarget: ' + error) - } - } - this.setupTouchInteraction(); - this.setupMouseWheelInteraction(); - } - - get targetInterface() { - return IInteractionTarget - } - - setupTouchInteraction() { - let element = this.element; - let useCapture = this.useCapture; - if (window.PointerEvent) { - if (this.debug) console.log('Pointer API' + window.PointerEvent); - element.addEventListener( - 'pointerdown', - e => { - if (this.debug) console.log('pointerdown', e.pointerId); - if (this.capture(e)) { - if (this.capturePointerEvents) { - try { - element.setPointerCapture(e.pointerId); - } catch (e) { } - } - this.onStart(e); - } - }, - useCapture - ); - element.addEventListener( - 'pointermove', - e => { - if (this.debug) console.log('pointermove', e.pointerId); - - if ( - e.pointerType == 'touch' || - (e.pointerType == 'mouse' && Events.isMouseDown(e)) - ) { - // this.capture(e) && - if (this.debug) - console.log('pointermove captured', e.pointerId); - this.onMove(e); - } - }, - useCapture - ); - element.addEventListener( - 'pointerup', - e => { - if (this.debug) console.log('pointerup'); - this.onEnd(e); - if (this.capturePointerEvents) { - try { - element.releasePointerCapture(e.pointerId); - } catch (e) { } - } - }, - useCapture - ); - element.addEventListener( - 'pointercancel', - e => { - if (this.debug) console.log('pointercancel'); - this.onEnd(e); - if (this.capturePointerEvents) - element.releasePointerCapture(e.pointerId); - }, - useCapture - ); - - if (!this.capturePointerEvents) { - element.addEventListener( - 'pointerleave', - e => { - if (this.debug) console.log('pointerleave'); - if (e.target == element) this.onEnd(e); - }, - useCapture - ); - } - - if (!this.capturePointerEvents) { - element.addEventListener( - 'pointerout', - e => { - if (this.debug) console.log('pointerout'); - if (e.target == element) this.onEnd(e); - }, - useCapture - ); - } - - if (this.cancelOnWindowOut) { - window.addEventListener( - 'pointerout', - e => { - if (e.target == element) { - this.onEnd(e); - } - }, - useCapture); - } - - } else if (window.TouchEvent) { - if (this.debug) console.log('Touch API'); - element.addEventListener( - 'touchstart', - e => { - if (this.debug) - console.log('touchstart', this.touchPoints(e)); - if (this.capture(e)) { - for (let touch of e.changedTouches) { - this.onStart(touch); - } - } - }, - useCapture - ); - element.addEventListener( - 'touchmove', - e => { - if (this.debug) - console.log('touchmove', this.touchPoints(e), e); - for (let touch of e.changedTouches) { - this.onMove(touch); - } - for (let touch of e.targetTouches) { - this.onMove(touch); - } - }, - useCapture - ); - element.addEventListener( - 'touchend', - e => { - if (this.debug) console.log('touchend', this.touchPoints(e)); - for (let touch of e.changedTouches) { - this.onEnd(touch); - } - }, - useCapture - ); - element.addEventListener( - 'touchcancel', - e => { - if (this.debug) - console.log( - 'touchcancel', - e.targetTouches.length, - e.changedTouches.length - ); - for (let touch of e.changedTouches) { - this.onEnd(touch); - } - }, - useCapture - ); - } else { - if (this.debug) console.log('Mouse API'); - - element.addEventListener( - 'mousedown', - e => { - if (this.debug) console.log('mousedown', e); - if (this.capture(e)) { - this.onStart(e); - } - }, - useCapture - ); - element.addEventListener( - 'mousemove', - e => { - // Dow we only use move events if the mouse is down? - // HOver effects have to be implemented by other means - // && Events.isMouseDown(e)) - - if (Events.isMouseDown(e)) { - if (this.debug) - console.log('mousemove', e); - this.onMove(e); - } - }, - useCapture - ); - element.addEventListener( - 'mouseup', - e => { - if (this.debug) console.log('mouseup', e); - this.onEnd(e); - }, - true - ); - - if (!this.capturePointerEvents) { - element.addEventListener( - 'mouseout', - e => { - if (e.target == element) { - this.onEnd(e); - console.warn("Shouldn't happen: mouseout ends interaction"); - } - - }, - useCapture - ); - } - if (this.cancelOnWindowOut) { - window.addEventListener( - 'mouseout', - e => { - if (e.target == element) { - this.onEnd(e); - } - }, - useCapture); - } - } - } - - isDescendant(parent, child) { - if (parent == child) return true - let node = child.parentNode; - while (node != null) { - if (node == parent) { - return true - } - node = node.parentNode; - } - return false - } - - touchPoints(event) { - let result = []; - for (let touch of event.changedTouches) { - result.push(this.extractPoint(touch)); - } - return result - } - - setupMouseWheelInteraction() { - this.mouseWheelElement.addEventListener( - 'mousewheel', - this.onMouseWheel.bind(this), - true - ); - this.mouseWheelElement.addEventListener( - 'DOMMouseScroll', - this.onMouseWheel.bind(this), - true - ); - } - - onMouseWheel(event) { - if (this.capture(event) && this.target.onMouseWheel) { - this.target.onMouseWheel(event); - } - } - - onStart(event) { - let extracted = this.extractPoint(event); - this.startInteraction(event, extracted); - this.target.onStart(event, this.interaction); - } - - onMove(event) { - let extracted = this.extractPoint(event, 'all'); - this.updateInteraction(event, extracted); - this.target.onMove(event, this.interaction); - this.interaction.updatePrevious(); - } - - onEnd(event) { - let extracted = this.extractPoint(event, 'changedTouches'); - this.endInteraction(event, extracted); - this.target.onEnd(event, this.interaction); - this.finishInteraction(event, extracted); - } - - /** - * Asks the target whether the event should be captured - * - * @param {any} event - * @returns {bool} - * @memberof InteractionDelegate - */ - capture(event) { - if (Events.isCaptured(event)) { - return false - } - let captured = this.target.capture(event); - return captured - } - - getPosition(event) { - return { x: event.clientX, y: event.clientY } - } - - extractPoint(event, touchEventKey = 'all') { - // 'targetTouches' - let result = {}; - switch (event.constructor.name) { - case 'MouseEvent': - let buttons = event.buttons || event.which; - if (buttons) result['mouse'] = this.getPosition(event); - break - case 'PointerEvent': - result[event.pointerId.toString()] = this.getPosition(event); - break - case 'Touch': - let id = - event.touchType === 'stylus' - ? 'stylus' - : event.identifier.toString(); - result[id] = this.getPosition(event); - break - // case 'TouchEvent': - // // Needs to be observed: Perhaps changedTouches are all we need. If so - // // we can remove the touchEventKey default parameter - // if (touchEventKey == 'all') { - // for(let t of event.targetTouches) { - // result[t.identifier.toString()] = this.getPosition(t) - // } - // for(let t of event.changedTouches) { - // result[t.identifier.toString()] = this.getPosition(t) - // } - // } - // else { - // for(let t of event.changedTouches) { - // result[t.identifier.toString()] = this.getPosition(t) - // } - // } - // break - default: - break - } - return result - } - - interactionStarted(event, key, point) { - // Callback: can be overwritten - } - - interactionEnded(event, key, point) { - // Callback: can be overwritten - } - - interactionFinished(event, key, point) { } - - startInteraction(event, extracted) { - for (let key in extracted) { - let point = extracted[key]; - this.interaction.started(key, point); - this.interactionStarted(event, key, point); - } - } - - updateInteraction(event, extracted) { - for (let key in extracted) { - let point = extracted[key]; - let updated = this.interaction.update(key, point); - if (updated) { - console.warn("new pointer in updateInteraction shouldn't happen"); - this.interactionStarted(event, key, point); - } - } - } - - endInteraction(event, ended) { - for (let key in ended) { - let point = ended[key]; - this.interaction.stop(key, point); - this.interactionEnded(event, key, point); - } - } - - finishInteraction(event, ended) { - for (let key in ended) { - let point = ended[key]; - this.interaction.finish(key, point); - this.interactionFinished(event, key, point); - } - } - } - /** - * A special InteractionDelegate that maps events to specific parts of - * the interaction target. The InteractionTarget must implement a findTarget - * method that returns an object implementing the IInteractionTarget interface. - * - * If the InteractionTarget also implements a mapPositionToPoint method this - * is used to map the points to the local coordinate space of the the target. - * - * This makes it easier to lookup elements and relate events to local - * positions. - * - * @export - * @class InteractionMapper - * @extends {InteractionDelegate} - */ - class InteractionMapper$1 extends InteractionDelegate { - - constructor( - element, - target, - { tapDistance = 10, longPressTime = 500.0, useCapture = true, mouseWheelElement = null } = {} - ) { - super(element, target, { tapDistance, useCapture, longPressTime, mouseWheelElement }); - } - - get targetInterface() { - return IInteractionMapperTarget - } - - mapPositionToPoint(point, element = null) { - if (this.target.mapPositionToPoint) { - return this.target.mapPositionToPoint(point, element) - } - return point - } - - interactionStarted(event, key, point) { - if (this.target.findTarget) { - let local = this.mapPositionToPoint(point); - let found = this.target.findTarget(event, local, point); - if (found != null) { - this.interaction.addTarget(key, found); - } - } - } - - onMouseWheel(event) { - if (this.capture(event)) { - if (this.target.findTarget) { - let point = this.getPosition(event); - let local = this.mapPositionToPoint(point); - let found = this.target.findTarget(event, local, point); - if (found != null && found.onMouseWheel) { - found.onMouseWheel(event); - return - } - } - if (this.target.onMouseWheel) { - this.target.onMouseWheel(event); - } - } - } - - onStart(event) { - let extracted = this.extractPoint(event); - this.startInteraction(event, extracted); - let mapped = this.interaction.mapInteraction( - extracted, - ['current', 'start'], - this.mapPositionToPoint.bind(this) - ); - for (let [target, interaction] of mapped.entries()) { - target.onStart(event, interaction); - } - } - - onMove(event) { - let extracted = this.extractPoint(event, 'all'); - this.updateInteraction(event, extracted); - let mapped = this.interaction.mapInteraction( - extracted, - ['current', 'previous'], - this.mapPositionToPoint.bind(this) - ); - for (let [target, interaction] of mapped.entries()) { - target.onMove(event, interaction); - interaction.updatePrevious(); - } - this.interaction.updatePrevious(); - } - - onEnd(event) { - let extracted = this.extractPoint(event, 'changedTouches'); - this.endInteraction(event, extracted); - let mapped = this.interaction.mapInteraction( - extracted, - ['ended'], - this.mapPositionToPoint.bind(this) - ); - for (let [target, interaction] of mapped.entries()) { - target.onEnd(event, interaction); - } - this.finishInteraction(event, extracted); - } - - /** - * - * - * @static - * @param {string|array} types - An event type, an array of event types or event types seperated by a space sign. The following - * events are possible: - * pan, panstart, panmove, panend, pancancel, panleft, panright, panup, pandown - * pinch, pinchstart, pinchmove, pinchend, pinchcancel, pinchin, pinchout - * press, pressup - * rotate, rotatestart, rotatemove, rotateend, rotatecancel - * swipe, swipeleft, swiperight, swipeup, swipedown - * tap - * @param {HTMLElement|HTMLElement[]} elements - An HTML element or an array of HTML elements. - * @param {function} [cb] - The callback. A function which is executed after the event occurs. Receives the event object as the - * first paramter - * @param {object} [opts] - An options object. See the hammer documentation for more details. - */ - static on(types, elements, cb, opts = {}) { - opts = Object.assign({}, { - - }, opts); - - if (typeof Hammer === 'undefined') { - console.error('Hammer.js not found!'); - return this - } - - // convert to array - types = Array.isArray(types) ? types : types.split(/\s/); - if (elements instanceof NodeList || elements instanceof HTMLCollection) { - elements = Array.from(elements); - } - elements = Array.isArray(elements) ? elements : [elements]; - - for (let i = 0; i < types.length; i++) { - - const type = types[i].toLowerCase(); - - // list of hammer events - const useHammer = /^(tap|doubletap|press|pan|swipe|pinch|rotate).*$/.test(type); - - // if it is a hammer event - if (useHammer) { - - for (let j = 0; j < elements.length; j++) { - - // if(elements[j].tagName == "svg") return false; - - let hammer = new Hammer(elements[j], opts); - - if (window.propagating !== 'undefined') { - hammer = propagating(hammer); - } - - // recognizers - if (type.startsWith('pan')) { - hammer.get('pan').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts)); - } else if (type.startsWith('pinch')) { - hammer.get('pinch').set(Object.assign({ enable: true }, opts)); - } else if (type.startsWith('press')) { - hammer.get('press').set(opts); - } else if (type.startsWith('rotate')) { - hammer.get('rotate').set(Object.assign({ enable: true }, opts)); - } else if (type.startsWith('swipe')) { - hammer.get('swipe').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts)); - } else if (type.startsWith('tap')) { - hammer.get('tap').set(opts); - } - - hammer.on(type, event => { - cb(event); - }); - } - - } else { - - for (let j = 0; j < elements.length; j++) { - Hammer.on(elements[j], type, event => { - cb(event); - }); - } - } - } - - return this - } - } - - window.InteractionMapper = InteractionMapper$1; - - /** Report capabilities with guaranteed values. - */ - class Capabilities { - - /** Returns the browser userAgent. - @return {string} - */ - static get userAgent() { - return navigator.userAgent || 'Unknown Agent' - } - - /** Tests whether the app is running on a mobile device. - Implemented as a readonly attribute. - @return {boolean} - */ - static get isMobile() { - return (/Mobi/.test(navigator.userAgent)) - } - - /** Tests whether the app is running on a iOS device. - Implemented as a readonly attribute. - @return {boolean} - */ - static get isIOS() { - return (/iPad|iPhone|iPod/.test(navigator.userAgent)) && !window.MSStream - } - - /** Tests whether the app is running in a Safari environment. - See https://stackoverflow.com/questions/7944460/detect-safari-browser - Implemented as a readonly attribute. - @return {boolean} - */ - static get isSafari() { - return navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && navigator.userAgent && !navigator.userAgent.match('CriOS') - } - - /** - * Distincts if the app is running inside electron or not. - * - * source: https://discuss.atom.io/t/detect-electron-or-web-page-running/33180/3 - */ - static get isElectron() { - return typeof process != 'undefined' && process.versions && process.versions.electron !== undefined - } - - /** Returns the display resolution. Necessary for retina displays. - @return {number} - */ - static get devicePixelRatio() { - return window.devicePixelRatio || 1 - } - - /** Returns true if the device is a multi-touch table. This method is currently not universal usable and not sure! - @return {boolean} - */ - static get isMultiTouchTable() { - return Capabilities.devicePixelRatio > 2 && Capabilities.isMobile === false && /Windows/i.test(Capabilities.userAgent) - } - - /** Returns true if mouse events are supported - @return {boolean} - */ - static supportsMouseEvents() { - return typeof(window.MouseEvent) != 'undefined' - } - - /** Returns true if touch events are supported - @return {boolean} - */ - static supportsTouchEvents() { - return typeof(window.TouchEvent) != 'undefined' - } - - /** Returns true if pointer events are supported - @return {boolean} - */ - static supportsPointerEvents() { - return typeof(window.PointerEvent) != 'undefined' - } - - /** Returns true if DOM templates are supported - @return {boolean} - */ - static supportsTemplate() { - return 'content' in document.createElement('template'); - } - } - - /** Basic tests for Capabilities. - */ - class CapabilitiesTests { - - static testConfirm() { - let bool = confirm('Please confirm'); - document.getElementById('demo').innerHTML = (bool) ? 'Confirmed' : 'Not confirmed'; - } - - static testPrompt() { - let person = prompt('Please enter your name', 'Harry Potter'); - if (person != null) { - demo.innerHTML = - 'Hello ' + person + '! How are you today?'; - } - } - - static testUserAgent() { - let agent = 'User-agent: ' + Capabilities.userAgent; - user_agent.innerHTML = agent; - } - - static testDevicePixelRatio() { - let value = 'Device Pixel Ratio: ' + Capabilities.devicePixelRatio; - device_pixel_ratio.innerHTML = value; - } - - static testMultiTouchTable() { - let value = 'Is the device a multi-touch table? ' + Capabilities.isMultiTouchTable; - multi_touch_table.innerHTML = value; - } - - static testSupportedEvents() { - let events = []; - if (Capabilities.supportsMouseEvents()) { - events.push('MouseEvents'); - } - if (Capabilities.supportsTouchEvents()) { - events.push('TouchEvents'); - } - if (Capabilities.supportsPointerEvents()) { - events.push('PointerEvents'); - } - supported_events.innerHTML = 'Supported Events: ' + events.join(', '); - } - - static testAll() { - this.testUserAgent(); - this.testDevicePixelRatio(); - this.testMultiTouchTable(); - this.testSupportedEvents(); - } - } - - /* Optional global variables, needed in DocTests. */ - window.Capabilities = Capabilities; - window.CapabilitiesTests = CapabilitiesTests; - - /** - * A base class for scatter specific events. - * - * @constructor - * @param {name} String - The name of the event - * @param {target} Object - The target of the event - */ - class BaseEvent { - constructor(name, target) { - this.name = name; - this.target = target; - } - } - - // Event types - const START = 'onStart'; - const UPDATE = 'onUpdate'; - const END = 'onEnd'; - - /** - * A scatter event that describes how the scatter has changed. - * - * @constructor - * @param {target} Object - The target scatter of the event - * @param {optional} Object - Optional parameter - */ - class ScatterEvent extends BaseEvent { - constructor( - target, - { - translate = { x: 0, y: 0 }, - scale = null, - rotate = 0, - about = null, - fast = false, - type = null - } = {} - ) { - super('scatterTransformed', { target: target }); - this.translate = translate; - this.scale = scale; - this.rotate = rotate; - this.about = about; - this.fast = fast; - this.type = type; - } - - toString() { - return ( - "Event('scatterTransformed', scale: " + - this.scale + - ' about: ' + - this.about.x + - ', ' + - this.about.y + - ')' - ) - } - } - - /** - * A scatter resize event that describes how the scatter has changed. - * - * @constructor - * @param {target} Object - The target scatter of the event - * @param {optional} Object - Optional parameter - */ - class ResizeEvent extends BaseEvent { - constructor(target, { width = 0, height = 0 } = {}) { - super('scatterResized', { width: width, height: height }); - this.width = width; - this.height = height; - } - - toString() { - return ( - 'Event(scatterResized width: ' + - this.width + - 'height: ' + - this.height + - ')' - ) - } - } - - /** - * A abstract base class that implements the throwable behavior of a scatter - * object. - * - * @constructor - */ - class Throwable { - constructor({ - movableX = true, - movableY = true, - throwVisibility = 44, - throwDamping = 0.95, - autoThrow = true, - onThrowFinished = null - } = {}) { - this.movableX = movableX; - this.movableY = movableY; - this.throwVisibility = throwVisibility; - this.throwDamping = throwDamping; - this.autoThrow = autoThrow; - this.velocities = []; - this.velocity = null; - this.timestamp = null; - this.onThrowFinished = onThrowFinished; - //console.log("onThrowFinished", onThrowFinished) - } - - observeVelocity() { - this.lastframe = performance.now(); - } - - addVelocity(delta, buffer = 5) { - let t = performance.now(); - let dt = t - this.lastframe; - this.lastframe = t; - if (dt > 0) { - // Avoid division by zero errors later on - let velocity = { t: t, dt: dt, dx: delta.x, dy: delta.y }; - this.velocities.push(velocity); - while (this.velocities.length > buffer) { - this.velocities.shift(); - } - } - } - - meanVelocity(milliseconds = 30) { - this.addVelocity({ x: 0, y: 0 }); - let sum = { x: 0, y: 0 }; - let count = 0; - let t = 0; - for (let i = this.velocities.length - 1; i > 0; i--) { - let v = this.velocities[i]; - t += v.dt; - let nv = { x: v.dx / v.dt, y: v.dy / v.dt }; - sum = Points.add(sum, nv); - count += 1; - if (t > milliseconds) { - break - } - } - if (count === 0) return sum // empty vector - return Points.multiplyScalar(sum, 1 / count) - } - - killAnimation() { - this.velocity = null; - this.velocities = []; - } - - startThrow() { - this.velocity = this.meanVelocity(); - if (this.velocity != null) { - // Call next velocity to ansure that specializations - // that use keepOnStage are called - this.velocity = this.nextVelocity(this.velocity); - if (this.autoThrow) this.animateThrow(performance.now()); - } else { - this.onDragComplete(); - } - } - - animateThrow(time) { - if (this.velocity != null) { - let t = performance.now(); - let dt = t - this.lastframe; - this.lastframe = t; - // console.log("animateThrow", dt) - let next = this.nextVelocity(this.velocity); - let prevLength = Points.length(this.velocity); - let nextLength = Points.length(next); - if (nextLength > prevLength) { - let factor = nextLength / prevLength; - next = Points.multiplyScalar(next, 1 / factor); - console.log('Prevent acceleration', factor, this.velocity, next); - } - this.velocity = next; - let d = Points.multiplyScalar(this.velocity, dt); - this._move(d); - - this.onDragUpdate(d); - if (dt == 0 || this.needsAnimation()) { - requestAnimationFrame(this.animateThrow.bind(this)); - return - } else { - if (this.isOutside()) { - requestAnimationFrame(this.animateThrow.bind(this)); - return - } - } - } - this.onDragComplete(); - if (this.onThrowFinished != null) { - this.onThrowFinished(); - } - } - - needsAnimation() { - if (this.velocity == null) { - return false - } - return Points.length(this.velocity) > 0.01 - } - - nextVelocity(velocity) { - // Must be overwritten: computes the changed velocity. Implement - // damping, collison detection, etc. here - let next = Points.multiplyScalar(velocity, this.throwDamping); - return { - x: (this.movableX) ? next.x : 0, - y: (this.movableY) ? next.y : 0 - } - } - - _move(delta) { - // Overwrite if necessary - } - - onDragComplete() { - // Overwrite if necessary - } - - onDragUpdate(delta) { - // Overwrite if necessary - } - } - - class AbstractScatter extends Throwable { - constructor({ - minScale = 0.1, - maxScale = 1.0, - startScale = 1.0, - autoBringToFront = true, - autoThrow = true, - translatable = true, - scalable = true, - rotatable = true, - resizable = false, - movableX = true, - movableY = true, - throwVisibility = 44, - throwDamping = 0.95, - overdoScaling = 1, - mouseZoomFactor = 1.1, - rotationDegrees = null, - rotation = null, - onTransform = null, - interactive = true, - onClose = null, - onThrowFinished = null, - scaleAutoClose = false, - scaleCloseThreshold = 0.10, - scaleCloseBuffer = 0.05 - } = {}) { - if (rotationDegrees != null && rotation != null) { - throw new Error('Use rotationDegrees or rotation but not both') - } else if (rotation != null) { - rotationDegrees = Angle.radian2degree(rotation); - } else if (rotationDegrees == null) { - rotationDegrees = 0; - } - super({ - movableX, - movableY, - throwVisibility, - throwDamping, - autoThrow, - onThrowFinished - }); - - /** - * Closes the card when the minScale is reached and the - * card is released. Card can be saved by scaling it up again. - */ - this.scaleAutoClose = scaleAutoClose; - this.scaleCloseThreshold = scaleCloseThreshold; - this.scaleCloseBuffer = scaleCloseBuffer; - this.scaleAutoCloseTimeout = null; - - this.interactive = interactive; - this.startRotationDegrees = rotationDegrees; - this.startScale = startScale; // Needed to reset object - this.minScale = minScale; - this.maxScale = maxScale; - this.overdoScaling = overdoScaling; - this.translatable = translatable; - if (!translatable) { - this.movableX = false; - this.movableY = false; - } - this.scalable = scalable; - this.rotatable = rotatable; - this.resizable = resizable; - this.mouseZoomFactor = mouseZoomFactor; - this.autoBringToFront = autoBringToFront; - this.dragging = false; - this.onTransform = onTransform != null ? [onTransform] : null; - this.onClose = onClose != null ? [onClose] : null; - } - - addCloseEventCallback(callback) { - if (this.onClose == null) { - this.onClose = []; - } - this.onClose.push(callback); - } - - addTransformEventCallback(callback) { - if (this.onTransform == null) { - this.onTransform = []; - } - this.onTransform.push(callback); - } - - startGesture(interaction) { - this.bringToFront(); - this.killAnimation(); - this.observeVelocity(); - return true - } - - close() { - if (this.onClose) { - this.onClose.forEach(callback => callback(this)); - } - } - - gesture(interaction) { - let delta = interaction.delta(); - //console.log("gesture", delta) - if (delta != null) { - this.addVelocity(delta); - this.transform(delta, delta.zoom, delta.rotate, delta.about); - if (delta.zoom != 1) this.interactionAnchor = delta.about; - } - } - - get polygon() { - let w2 = this.width * this.scale / 2; - let h2 = this.height * this.scale / 2; - let center = this.center; - let polygon = new Polygon(center); - polygon.addPoint({ x: -w2, y: -h2 }); - polygon.addPoint({ x: w2, y: -h2 }); - polygon.addPoint({ x: w2, y: h2 }); - polygon.addPoint({ x: -w2, y: h2 }); - polygon.rotate(this.rotation); - return polygon - } - - isOutside() { - let stagePolygon = this.containerPolygon; - if (stagePolygon == null) - return false - let polygon = this.polygon; - if (polygon == null) - return false - let result = stagePolygon.intersectsWith(polygon); - return result === false || result.overlap < this.throwVisibility - } - - recenter() { - // Return a small vector that guarantees that the scatter is moving - // towards the center of the stage - let center = this.center; - let target = this.container.center; - let delta = Points.subtract(target, center); - return Points.normalize(delta) - } - - nextVelocity(velocity) { - return this.keepOnStage(velocity) - } - - bouncing() { - // Implements the bouncing behavior of the scatter. Moves the scatter - // to the center of the stage if the scatter is outside the stage or - // not within the limits of the throwVisibility. - - let stagePolygon = this.containerPolygon; - let polygon = this.polygon; - let result = stagePolygon.intersectsWith(polygon); - if (result === false || result.overlap < this.throwVisibility) { - let cv = this.recenter(); - let recentered = false; - while (result === false || result.overlap < this.throwVisibility) { - polygon.center.x += cv.x; - polygon.center.y += cv.y; - this._move(cv); - result = stagePolygon.intersectsWith(polygon); - recentered = true; - } - return recentered - } - return false - } - - keepOnStage(velocity, collision = 0.5) { - let stagePolygon = this.containerPolygon; - if (!stagePolygon) return - let polygon = this.polygon; - let bounced = this.bouncing(); - if (bounced) { - let stage = this.containerBounds; - let x = this.center.x; - let y = this.center.y; - let dx = this.movableX ? velocity.x : 0; - let dy = this.movableY ? velocity.y : 0; - let factor = this.throwDamping; - // if (recentered) { - if (x < 0) { - dx = -dx; - factor = collision; - } - if (x > stage.width) { - dx = -dx; - factor = collision; - } - if (y < 0) { - dy = -dy; - factor = collision; - } - if (y > stage.height) { - dy = -dy; - factor = collision; - } - // } - return Points.multiplyScalar({ x: dx, y: dy }, factor) - } - return super.nextVelocity(velocity) - } - - endGesture(interaction) { - this.startThrow(); - this._checkAutoClose(); - } - - _checkAutoClose() { - if (this.scaleAutoClose) - if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) { - this.zoom(this.minScale, { animate: 0.2, onComplete: this.close.bind(this) }); - } else if (this.scale < this.minScale + this.scaleCloseThreshold) { - this.zoom(this.minScale + this.scaleCloseThreshold, { animate: 0.4 }); - } - } - - rotateDegrees(degrees, anchor) { - let rad = Angle.degree2radian(degrees); - this.rotate(rad, anchor); - } - - rotate(rad, anchor) { - this.transform({ x: 0, y: 0 }, 1.0, rad, anchor); - } - - move(d, { animate = 0 } = {}) { - if (this.translatable) { - if (animate > 0) { - let startPos = this.position; - TweenLite.to(this, animate, { - x: '+=' + d.x, - y: '+=' + d.y, - /* scale: scale, uo: not defined, why was this here? */ - onUpdate: e => { - let p = this.position; - let dx = p.x - startPos.x; - let dy = p.x - startPos.y; - this.onMoved(dx, dy); - } - }); - } else { - this._move(d); - this.onMoved(d.x, d.y); - } - } - } - - moveTo(p, { animate = 0 } = {}) { - let c = this.origin; - let delta = Points.subtract(p, c); - this.move(delta, { animate: animate }); - } - - centerAt(p, { animate = 0 } = {}) { - let c = this.center; - let delta = Points.subtract(p, c); - this.move(delta, { animate: animate }); - } - - zoom( - scale, - { - animate = 0, - about = null, - delay = 0, - x = null, - y = null, - onComplete = null - } = {} - ) { - let anchor = about || this.center; - if (scale != this.scale) { - if (animate > 0) { - TweenLite.to(this, animate, { - scale: scale, - delay: delay, - onComplete: onComplete, - onUpdate: this.onZoomed.bind(this) - }); - } else { - this.scale = scale; - this.onZoomed(anchor); - } - } - } - - _move(delta) { - this.x += this.movableX ? delta.x : 0; - this.y += this.movableX ? delta.y : 0; - } - - transform(translate, zoom, rotate, anchor) { - let delta = { - x: this.movableX ? translate.x : 0, - y: this.movableY ? translate.y : 0 - }; - if (this.resizable) var vzoom = zoom; - if (!this.translatable) delta = { x: 0, y: 0 }; - if (!this.rotatable) rotate = 0; - if (!this.scalable) zoom = 1.0; - if (zoom == 1.0 && rotate == 0) { - this._move(delta); - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: delta, - scale: this.scale, - rotate: 0, - about: anchor, - fast: false, - type: UPDATE - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - return - } - let origin = this.rotationOrigin; - let beta = Points.angle(origin, anchor); - let distance = Points.distance(origin, anchor); - let { scale: newScale, zoom: thresholdedZoom } = this.calculateScale(zoom); - - let newOrigin = Points.arc(anchor, beta + rotate, distance * thresholdedZoom); - let extra = Points.subtract(newOrigin, origin); - let offset = Points.subtract(anchor, origin); - this._move(offset); - this.scale = newScale; - this.rotation += rotate; - offset = Points.negate(offset); - offset = Points.add(offset, extra); - offset = Points.add(offset, translate); - this._move(offset); - - delta.x += extra.x; - delta.y += extra.y; - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: delta, - scale: newScale, - rotate: rotate, - about: anchor - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - if (this.resizable) { - this.resizeAfterTransform(vzoom); - } - } - - /** - * For a given zoom, a new scale is calculated, taking - * min and max scale into account. - * - * @param {number} zoom - The zoom factor, to scale the object with. - * @returns {object} - Returns an object containing the a value for a valid scale and the corrected zoom factor. - */ - calculateScale(zoom) { - let scale = this.scale * zoom; - - let minScale = this.minScale / this.overdoScaling; - let maxScale = this.maxScale * this.overdoScaling; - if (scale < minScale) { - scale = minScale; - zoom = scale / this.scale; - } - if (scale > maxScale) { - scale = maxScale; - zoom = scale / this.scale; - } - - if (this.scaleAutoClose) - this._updateTransparency(); - - return { zoom, scale } - } - - _updateTransparency() { - if (this.scale < this.minScale + this.scaleCloseThreshold) { - let transparency = this.calculateScaleTransparency(); - this.element.style.opacity = transparency; - } else this.element.style.opacity = 1; - } - - calculateScaleTransparency() { - let transparency = (this.scale - this.minScale) / this.scaleCloseThreshold; - transparency = (transparency > 1) ? 1 : (transparency < 0) ? 0 : transparency; - return transparency - } - - resizeAfterTransform(zoom) { - // Overwrite this in subclasses. - } - - validScale(scale) { - scale = Math.max(scale, this.minScale); - scale = Math.min(scale, this.maxScale); - return scale - } - - animateZoomBounce(dt = 1) { - if (this.zoomAnchor != null) { - let zoom = 1; - let amount = Math.min(0.01, 0.3 * dt / 100000.0); - if (this.scale < this.minScale) zoom = 1 + amount; - if (this.scale > this.maxScale) zoom = 1 - amount; - if (zoom != 1) { - this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor); - requestAnimationFrame(dt => { - this.animateZoomBounce(dt); - }); - return - } - this.zoomAnchor = null; - } - } - - checkScaling(about, delay = 0) { - this.zoomAnchor = about; - clearTimeout(this.animateZoomBounce.bind(this)); - setTimeout(this.animateZoomBounce.bind(this), delay); - } - - onMouseWheel(event) { - if (event.claimedByScatter) { - if (event.claimedByScatter != this) return - } - this.killAnimation(); - this.targetScale = null; - let direction = event.detail < 0 || event.wheelDelta > 0; - let globalPoint = { x: event.clientX, y: event.clientY }; - let centerPoint = this.mapPositionToContainerPoint(globalPoint); - if (event.shiftKey) { - let degrees = direction ? 5 : -5; - let rad = Angle.degree2radian(degrees); - return this.transform({ x: 0, y: 0 }, 1.0, rad, centerPoint) - } - const zoomFactor = this.mouseZoomFactor; - let zoom = direction ? zoomFactor : 1 / zoomFactor; - this.transform({ x: 0, y: 0 }, zoom, 0, centerPoint); - this.checkScaling(centerPoint, 200); - - if (this.scaleAutoClose) { - if (this.scale <= this.minScale + this.scaleCloseThreshold) { - - if (this.scaleAutoCloseTimeout) clearTimeout(this.scaleAutoCloseTimeout); - this.scaleAutoCloseTimeout = setTimeout(() => { - this._checkAutoClose(); - }, 600); - } - this._updateTransparency(); - } - // - // if (this.onTransform != null) { - // let event = new ScatterEvent(this, { - // translate: {x: 0, y: 0}, - // scale: this.scale, - // rotate: 0, - // about: null, - // fast: false, - // type: ZOOM - // }) - // this.onTransform.forEach(function(f) { - // f(event) - // }) - // } - } - - onStart(event, interaction) { - - if (this.startGesture(interaction)) { - this.dragging = true; - this.interactionAnchor = null; - } - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: { x: 0, y: 0 }, - scale: this.scale, - rotate: 0, - about: null, - fast: false, - type: START - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onMove(event, interaction) { - /** As long as mouseout && mouseleave interrupt we cannot be sure that - * dragging remains correct. - */ - if (this.dragging) { - this.gesture(interaction); - } - } - - onEnd(event, interaction) { - //console.log("Scatter.onEnd", this.dragging) - if (interaction.isFinished()) { - this.endGesture(interaction); - this.dragging = false; - for (let key of interaction.ended.keys()) { - if (interaction.isTap(key)) { - let point = interaction.ended.get(key); - this.onTap(event, interaction, point); - } - } - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: { x: 0, y: 0 }, - scale: this.scale, - rotate: 0, - about: null, - fast: false, - type: END - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - let about = this.interactionAnchor; - if (about != null) { - this.checkScaling(about, 100); - } - } - - onTap(event, interaction, point) { } - - onDragUpdate(delta) { - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - fast: true, - translate: delta, - scale: this.scale, - about: this.currentAbout, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onDragComplete() { - if (this.onTransform) { - let event = new ScatterEvent(this, { - scale: this.scale, - about: this.currentAbout, - fast: false, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onMoved(dx, dy, about) { - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: { x: dx, y: dy }, - about: about, - fast: true, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onResizing() { - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - scale: this.scale, - fast: false, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onZoomed(about) { - - if (this.scaleAutoClose) - this._updateTransparency(); - - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - scale: this.scale, - about: about, - fast: false, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - } - - /** A container for scatter objects, which uses a single InteractionMapper - * for all children. This reduces the number of registered event handlers - * and covers the common use case that multiple objects are scattered - * on the same level. - */ - class DOMScatterContainer { - /** - * @constructor - * @param {DOM node} element - DOM element that receives events - * @param {Bool} stopEvents - Whether events should be stopped or propagated - * @param {Bool} claimEvents - Whether events should be marked as claimed - * if findTarget return as non-null value. - * @param {String} [touchAction=none] - CSS to set touch action style, needed to prevent - * pointer cancel events. Use null if the - * the touch action should not be set. - */ - constructor( - element, - { stopEvents = 'auto', claimEvents = true, useCapture = true, touchAction = 'none' } = {} - ) { - this.onCapture = null; - this.element = element; - if (stopEvents === 'auto') { - if (Capabilities.isSafari) { - document.addEventListener( - 'touchmove', - event => this.preventPinch(event), - false - ); - stopEvents = false; - } else { - stopEvents = true; - } - } - this.stopEvents = stopEvents; - this.claimEvents = claimEvents; - if (touchAction !== null) { - Elements$1.setStyle(element, { touchAction }); - } - this.scatter = new Map(); - this.delegate = new InteractionMapper$1(element, this, { - useCapture, - mouseWheelElement: window - }); - - if (typeof debugCanvas !== 'undefined') { - requestAnimationFrame(dt => { - this.showTouches(dt); - }); - } - } - - showTouches(dt) { - let resolution = window.devicePixelRatio; - let canvas = debugCanvas; - let current = this.delegate.interaction.current; - let context = canvas.getContext('2d'); - let radius = 20 * resolution; - context.clearRect(0, 0, canvas.width, canvas.height); - context.fillStyle = 'rgba(0, 0, 0, 0.3)'; - context.lineWidth = 2; - context.strokeStyle = '#003300'; - for (let [key, point] of current.entries()) { - let local = point; - context.beginPath(); - context.arc( - local.x * resolution, - local.y * resolution, - radius, - 0, - 2 * Math.PI, - false - ); - context.fill(); - context.stroke(); - } - requestAnimationFrame(dt => { - this.showTouches(dt); - }); - } - - preventPinch(event) { - event = event.originalEvent || event; - if (event.scale !== 1) { - event.preventDefault(); - } - } - - add(scatter) { - this.scatter.set(scatter.element, scatter); - } - - capture(event) { - if (this.onCapture) { - return this.onCapture(event) - } - if (event.target == this.element && this.stopEvents) { - Events.stop(event); - } - return true - } - - mapPositionToPoint(point) { - return Points.fromPageToNode(this.element, point) - } - - isDescendant(parent, child, clickable = false) { - if (parent == child) return true - let node = child.parentNode; - while (node != null) { - if (!clickable && node.onclick) { - return false - } - if (node == parent) { - return true - } - node = node.parentNode; - } - return false - } - - findTarget(event, local, global) { - /*** Note that elementFromPoint works with clientX, clientY, not pageX, pageY - The important point is that event should not be used, since the TouchEvent - points are hidden in sub objects. - ***/ - let found = document.elementFromPoint(global.x, global.y); - for (let target of this.scatter.values()) { - if (target.interactive && this.isDescendant(target.element, found)) { - if (this.stopEvents) Events.stop(event); - if (this.claimEvents) event.claimedByScatter = target; - return target - } - } - return null - } - - get center() { - let r = this.bounds; - let w2 = r.width / 2; - let h2 = r.height / 2; - return { x: w2, y: h2 } - } - - get bounds() { - return this.element.getBoundingClientRect() - } - - get polygon() { - let r = this.bounds; - let w2 = r.width / 2; - let h2 = r.height / 2; - let center = { x: w2, y: h2 }; - let polygon = new Polygon(center); - polygon.addPoint({ x: -w2, y: -h2 }); - polygon.addPoint({ x: w2, y: -h2 }); - polygon.addPoint({ x: w2, y: h2 }); - polygon.addPoint({ x: -w2, y: h2 }); - return polygon - } - } - - - class DOMScatter extends AbstractScatter { - constructor( - element, - container, - { - startScale = 1.0, - minScale = 0.1, - maxScale = 1.0, - overdoScaling = 1.5, - autoBringToFront = true, - translatable = true, - scalable = true, - rotatable = true, - movableX = true, - movableY = true, - rotationDegrees = null, - rotation = null, - onTransform = null, - transformOrigin = 'center center', - // extras which are in part needed - x = 0, - y = 0, - width = null, // required - height = null, // required - resizable = false, - simulateClick = false, - verbose = true, - onResize = null, - touchAction = 'none', - throwVisibility = 44, - throwDamping = 0.95, - autoThrow = true, - scaleAutoClose = false, - onClose = null, - scaleCloseThreshold = 0.10, - scaleCloseBuffer = 0.05 - } = {} - ) { - super({ - minScale, - maxScale, - startScale, - overdoScaling, - autoBringToFront, - translatable, - scalable, - rotatable, - movableX, - movableY, - resizable, - rotationDegrees, - rotation, - onTransform, - throwVisibility, - throwDamping, - autoThrow, - scaleAutoClose, - scaleCloseThreshold, - scaleCloseBuffer, - onClose - }); - if (container == null || width == null || height == null) { - throw new Error('Invalid value: null') - } - element.scatter = this; - this.element = element; - this.x = x; - this.y = y; - this.oldX = 0; - this.oldY = 0; - this.meanX = x; - this.meanY = y; - this.width = width; - this.height = height; - this.throwVisibility = Math.min(width, height, throwVisibility); - this.container = container; - this.simulateClick = simulateClick; - this.scale = startScale; - this.rotationDegrees = this.startRotationDegrees; - this.transformOrigin = transformOrigin; - this.initialValues = { - x: x, - y: y, - width: width, - height: height, - scale: startScale, - rotation: this.startRotationDegrees, - transformOrigin: transformOrigin - }; - - - // For tweenlite we need initial values in _gsTransform - TweenLite.set(element, this.initialValues); - this.onResize = onResize; - this.verbose = verbose; - if (touchAction !== null) { - Elements$1.setStyle(element, { touchAction }); - } - this.resizeButton = null; - if (resizable) { - let button = document.createElement("div"); - button.style.position = "absolute"; - button.style.right = "0px"; - button.style.bottom = "0px"; - button.style.width = "50px"; - button.style.height = "50px"; - // button.style.borderRadius = "100% 0px 0px 0px"; - // button.style.background = this.element.style.backgroundColor - button.className = "interactiveElement"; - this.element.appendChild(button); - - button.addEventListener('pointerdown', (e) => { - this.startResize(e); - }); - - button.addEventListener('pointermove', (e) => { - this.resize(e); - }); - - button.addEventListener('pointerup', (e) => { - this.stopResize(e); - }); - this.resizeButton = button; - } - container.add(this); - } - - /** Returns geometry data as object. **/ - getState() { - return { - scale: this.scale, - x: this.x, - y: this.y, - rotation: this.rotation - } - } - - close() { - super.close(); - let parent = this.element.parentNode; - if (parent) parent.removeChild(this.element); - } - - get rotationOrigin() { - return this.center - } - - get x() { - return this._x - } - - get y() { - return this._y - } - - set x(value) { - this._x = value; - TweenLite.set(this.element, { x: value }); - } - - set y(value) { - this._y = value; - TweenLite.set(this.element, { y: value }); - } - - get position() { - let transform = this.element._gsTransform; - let x = transform.x; - let y = transform.y; - return { x, y } - } - - get origin() { - let p = this.fromNodeToPage(0, 0); - return Points.fromPageToNode(this.container.element, p) - } - - get bounds() { - let stage = this.container.element.getBoundingClientRect(); - let rect = this.element.getBoundingClientRect(); - return { - top: rect.top - stage.top, - left: rect.left - stage.left, - width: rect.width, - height: rect.height - } - } - - get center() { - let r = this.bounds; - let w2 = r.width / 2; - let h2 = r.height / 2; - // if (this.resizable) { - // w2 *= this.scale - // h2 *= this.scale - // } - var x = r.left + w2; - var y = r.top + h2; - return { x, y } - } - - set rotation(radians) { - let rad = radians; // Angle.normalize(radians) - let degrees = Angle.radian2degree(rad); - TweenLite.set(this.element, { rotation: degrees }); - this._rotation = rad; - } - - set rotationDegrees(degrees) { - let deg = degrees; // Angle.normalizeDegree(degrees) - TweenLite.set(this.element, { rotation: deg }); - this._rotation = Angle.degree2radian(deg); - } - - get rotation() { - return this._rotation - } - - get rotationDegrees() { - return this._rotation - } - - set scale(scale) { - TweenLite.set(this.element, { - scale: scale, - transformOrigin: this.transformOrigin - }); - this._scale = scale; - } - - get scale() { - return this._scale - } - - get containerBounds() { - return this.container.bounds - } - - get containerPolygon() { - return this.container.polygon - } - - mapPositionToContainerPoint(point) { - return this.container.mapPositionToPoint(point) - } - - capture(event) { - return true - } - - reset() { - TweenLite.set(this.element, this.initialValues); - } - - hide() { - TweenLite.to(this.element, 0.1, { - display: 'none', - onComplete: e => { - this.element.parentNode.removeChild(this.element); - } - }); - } - - show() { - TweenLite.set(this.element, { display: 'block' }); - } - - showAt(p, rotationDegrees) { - TweenLite.set(this.element, { - display: 'block', - x: p.x, - y: p.y, - rotation: rotationDegrees, - transformOrigin: this.transformOrigin - }); - } - - bringToFront() { - // this.element.parentNode.appendChild(this.element) - // uo: On Chome and Electon appendChild leads to flicker - TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ }); - } - - toggleVideo(element) { - if (element.paused) { - element.play(); - } else { - element.pause(); - } - } - - onTap(event, interaction, point) { - if (this.simulateClick) { - let p = Points.fromPageToNode(this.element, point); - let iframe = this.element.querySelector('iframe'); - if (iframe) { - let doc = iframe.contentWindow.document; - let element = doc.elementFromPoint(p.x, p.y); - if (element == null) { - return - } - switch (element.tagName) { - case 'VIDEO': - console.log(element.currentSrc); - if (PopupMenu) { - PopupMenu.open( - { - Fullscreen: () => - window.open(element.currentSrc), - Play: () => element.play() - }, - { x, y } - ); - } else { - this.toggleVideo(element); - } - break - default: - element.click(); - } - } - } - } - - isDescendant(parent, child) { - let node = child.parentNode; - while (node != null) { - if (node == parent) { - return true - } - node = node.parentNode; - } - return false - } - - fromPageToNode(x, y) { - return Points.fromPageToNode(this.element, { x, y }) - } - - fromNodeToPage(x, y) { - return Points.fromNodeToPage(this.element, { x, y }) - } - - _move(delta) { - // UO: We need to keep TweenLite's _gsTransform and the private - // _x and _y attributes aligned - let x = this.element._gsTransform.x; - let y = this.element._gsTransform.y; - if (this.movableX) { - x += delta.x; - } - if (this.movableY) { - y += delta.y; - } - this._x = x; - this._y = y; - TweenLite.set(this.element, { x: x, y: y }); - } - - resizeAfterTransform(zoom) { - // let w = this.width * this.scale - // let h = this.height * this.scale - // TweenLite.set(this.element, { width: w, height: h }) - if (this.onResize) { - let event = new ResizeEvent(this, { width: w, height: h }); - this.onResize(event); - } - if (this.resizeButton != null) ; - } - - startResize(e) { - e.preventDefault(); - let event = new CustomEvent('resizeStarted'); - - let oldPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; - this.bringToFront(); - - this.element.style.transformOrigin = "0% 0%"; - - let newPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; - - let offset = Points.subtract(oldPostition, newPostition); - - this.oldX = e.clientX; - this.oldY = e.clientY; - - e.target.setAttribute('resizing', "true"); - e.target.setPointerCapture(e.pointerId); - - TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } }); - TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } }); - - this.element.dispatchEvent(event); - } - - resize(e) { - e.preventDefault(); - - let rotation = Angle.radian2degree(this.rotation); - rotation = (rotation + 360) % 360; - let event = new CustomEvent('resized'); - if (e.target.getAttribute('resizing') == "true") { - - let deltaX = (e.clientX - this.oldX); - let deltaY = (e.clientY - this.oldY); - - let r = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); - let phi = Angle.radian2degree(Math.atan2(deltaX, deltaY)); - - phi = ((phi) + 630) % 360; - let rot = ((rotation + 90) + 630) % 360; - - let diffAngle = ((0 + rot) + 360) % 360; - let phiCorrected = (phi + diffAngle + 360) % 360; - - let resizeW = r * Math.cos(Angle.degree2radian(phiCorrected)); - let resizeH = -r * Math.sin(Angle.degree2radian(phiCorrected)); - - if (this.element.offsetWidth + resizeW / this.scale > this.width * 0.3 && this.element.offsetHeight + resizeH / this.scale > this.height * 0.3) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, height: this.element.offsetHeight + resizeH / this.scale }); - - this.oldX = e.clientX; - this.oldY = e.clientY; - this.onResizing(); - - this.element.dispatchEvent(event); - } - } - - stopResize(e) { - e.preventDefault(); - - let event = new CustomEvent('resizeEnded'); - let oldPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; - this.element.style.transformOrigin = "50% 50%"; - let newPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; - let offset = Points.subtract(oldPostition, newPostition); - - TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } }); - TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } }); - - e.target.setAttribute('resizing', "false"); - - this.element.dispatchEvent(event); - } - } - - DOMScatter.zIndex = 1000; - - class CardLoader { - constructor( - src, - { - x = 0, - y = 0, - width = 1000, - height = 800, - maxWidth = null, - maxHeight = null, - scale = 1, - minScale = 0.5, - maxScale = 1.5, - rotation = 0 - } = {} - ) { - this.src = src; - this.x = x; - this.y = y; - this.scale = scale; - this.rotation = 0; - this.maxScale = maxScale; - this.minScale = minScale; - this.wantedWidth = width; - this.wantedHeight = height; - this.maxWidth = maxWidth != null ? maxWidth : window.innerWidth; - this.maxHeight = maxHeight != null ? maxHeight : window.innerHeight; - this.addedNode = null; - console.log({ - - width, - height, - maxWidth, - maxHeight, - - }); - } - - unload() { - if (this.addedNode) { - this.addedNode.remove(); - this.addedNode = null; - } - } - } - - class PDFLoader extends CardLoader { - constructor(src, { width = 1640, height = 800, scale = 1 } = {}) { - super(src, { width, height, scale }); - if (typeof PDFJS == 'undefined') { - alert('PDF.js needed'); - } - } - - load(domNode) { - return new Promise((resolve, reject) => { - PDFJS.getDocument(this.src).then(pdf => { - pdf.getPage(1).then(page => { - let scale = this.scale * app.renderer.resolution; - let invScale = 1 / scale; - let viewport = page.getViewport(scale); - - // Prepare canvas using PDF page dimensions. - let canvas = document.createElement('canvas'); - let context = canvas.getContext('2d'); - canvas.height = viewport.height; - canvas.width = viewport.width; - - // Render PDF page into canvas context. - let renderContext = { - canvasContext: context, - viewport: viewport - }; - page.render(renderContext); - domNode.appendChild(canvas); - this.wantedWidth = canvas.width; - this.wantedHeight = canvas.height; - this.scale = invScale; - this.addedNode = canvas; - resolve(this); - }); - }); - }) - } - } - - class ImageLoader extends CardLoader { - load(domNode) { - return new Promise((resolve, reject) => { - let isImage = domNode instanceof HTMLImageElement; - let image = isImage ? domNode : document.createElement('img'); - image.onload = e => { - if (!isImage) { - domNode.appendChild(image); - this.addedNode = image; - } - this.wantedWidth = image.naturalWidth; - this.wantedHeight = image.naturalHeight; - - let scaleW = this.maxWidth / image.naturalWidth; - let scaleH = this.maxHeight / image.naturalHeight; - this.scale = Math.min(this.maxScale, Math.min(scaleW, scaleH)); - image.setAttribute('draggable', false); - image.width = image.naturalWidth; - image.height = image.naturalHeight; - resolve(this); - }; - image.onerror = e => { - reject(this); - }; - image.src = this.src; - }) - } - } - - class FrameLoader extends CardLoader { - load(domNode) { - return new Promise((resolve, reject) => { - let isFrame = domNode instanceof HTMLIFrameElement; - let iframe = isFrame ? domNode : document.createElement('iframe'); - console.log('FrameLoader.load', isFrame, iframe, this.src); - iframe.frameBorder = 0; - iframe.style.scrolling = false; - iframe.width = this.wantedWidth; - iframe.height = this.wantedHeight; - if (!isFrame) { - // Unlike img onload is only triggered if the iframe is part of the DOM tree - domNode.appendChild(iframe); - this.addedNode = iframe; - } - iframe.onload = e => { - resolve(this); - }; - iframe.onerror = e => { - reject(this); - }; - iframe.src = this.src; - }) - } - } - - class HTMLLoader extends CardLoader { - load(domNode) { - return new Promise((resolve, reject) => { - let xhr = new XMLHttpRequest(); - xhr.open('GET', this.src, false); - xhr.onload = e => { - domNode.innerHTML = xhr.response; - this.addedNode = domNode.firstElementChild; - let { width, height } = this.size(this.addedNode); - console.log("HTMLLoader.load", { added: this.addedNode, width, height }); - if (width) - this.wantedWidth = width || this.wantedWidth; - if (height) - this.wantedHeight = height || this.wantedHeight; - resolve(this); - }; - xhr.onerror = e => { - reject(this); - }; - xhr.send(); - }) - } - - /** - * Tries to determine the size of the addedNode. - * Checks for explicit width and height style attributes. - * - * Overwrite this method if you want to extract values from other infos. - * - * @returns { width: int, height: int } - * @memberof HTMLLoader - */ - size(node) { - let width = parseInt(node.style.width) || null; - let height = parseInt(node.style.height) || null; - return { width, height } - } - } - - class DOMFlip { - constructor( - domScatterContainer, - flipTemplate, - frontLoader, - backLoader, - { - closeOnMinScale = false, - flipDuration = 1, - fadeDuration = 0.2, - overdoScaling = 1, - autoLoad = false, - center = null, - preloadBack = false, - translatable = true, - scalable = true, - rotatable = true, - onFront = null, - onBack = null, - onClose = null, - onUpdate = null, - onRemoved = null, - onLoaded = null - } = {} - ) { - this.domScatterContainer = domScatterContainer; - this.id = getId$1(); - this.flipDuration = flipDuration; - this.fadeDuration = fadeDuration; - this.closeOnMinScale = closeOnMinScale; - this.flipTemplate = flipTemplate; - this.frontLoader = frontLoader; - this.backLoader = backLoader; - this.translatable = translatable; - this.scalable = scalable; - this.rotatable = rotatable; - this.onFrontFlipped = onFront; - this.onBackFlipped = onBack; - this.onClose = onClose; - this.onRemoved = onRemoved; - this.onUpdate = onUpdate; - this.onLoaded = onLoaded; - this.center = center; - this.preloadBack = preloadBack; - this.overdoScaling = overdoScaling; - if (autoLoad) { - this.load(); - } - } - - load() { - return new Promise((resolve, reject) => { - let t = this.flipTemplate; - let dom = this.domScatterContainer.element; - let wrapper = t.content.querySelector('.flipWrapper'); - wrapper.id = this.id; - let clone = document.importNode(t.content, true); - dom.appendChild(clone); - // We cannot use the document fragment itself because it - // is not part of the main dom tree. After the appendChild - // call we can access the new dom element by id - this.cardWrapper = dom.querySelector('#' + this.id); - let front = this.cardWrapper.querySelector('.front'); - this.frontLoader.load(front).then(loader => { - this.frontLoaded(loader).then((obj) => { - if (this.onLoaded) this.onLoaded(); - resolve(this); - }); - }); - }) - } - - frontLoaded(loader) { - return new Promise((resolve, reject) => { - let scatter = new DOMScatter( - this.cardWrapper, - this.domScatterContainer, - { - x: loader.x, - y: loader.y, - startScale: loader.scale, - scale: loader.scale, - maxScale: loader.maxScale, - minScale: loader.minScale, - width: loader.wantedWidth, - height: loader.wantedHeight, - rotation: loader.rotation, - translatable: this.translatable, - scalable: this.scalable, - rotatable: this.rotatable, - overdoScaling: this.overdoScaling - } - ); - - if (this.center) { - scatter.centerAt(this.center); - } - - if (this.closeOnMinScale) { - - const removeOnMinScale = function () { - if (scatter.scale <= scatter.minScale) { - this.flippable.close(); - - // 'Disable' overdoscaling to avoid weird jumps on close. - scatter.minScale /= scatter.overdoScaling; - scatter.overdoScaling = 1; - - //Remove callback - if (scatter.onTransform) { - let callbackIdx = scatter.onTransform.indexOf(removeOnMinScale); - scatter.onTransform.splice(callbackIdx, 1); - } - } - - }.bind(this); - - - - scatter.addTransformEventCallback(removeOnMinScale); - } - - let flippable = new DOMFlippable(this.cardWrapper, scatter, this); - let back = this.cardWrapper.querySelector('.back'); - - if (this.preloadBack) { - this.backLoader.load(back).then(loader => { - this.setupFlippable(flippable, loader); - }); - } - this.flippable = flippable; - resolve(this); - }) - } - - centerAt(p) { - this.center = p; - this.flippable.centerAt(p); - } - - zoom(scale) { - this.flippable.zoom(scale); - } - - setupFlippable(flippable, loader) { - flippable.wantedWidth = loader.wantedWidth; - flippable.wantedHeight = loader.wantedHeight; - flippable.wantedScale = loader.scale; - flippable.minScale = loader.minScale; - flippable.maxScale = loader.maxScale; - flippable.scaleButtons(); - } - - start({ targetCenter = null } = {}) { - console.log('DOMFlip.start', targetCenter); - if (this.preloadBack) this.flippable.start({ duration: this.flipDuration, targetCenter }); - else { - let back = this.cardWrapper.querySelector('.back'); - let flippable = this.flippable; - this.backLoader.load(back).then(loader => { - this.setupFlippable(flippable, loader); - flippable.start({ duration: this.flipDuration, targetCenter }); - }); - } - } - - fadeOutAndRemove() { - TweenLite.to(this.cardWrapper, this.fadeDuration, { - opacity: 0, - onComplete: () => { - this.cardWrapper.remove(); - } - }); - } - - closed() { - this.unload(); - } - - unload() { - if (!this.preloadBack) { - this.backLoader.unload(); - } - } - } - - class DOMFlippable { - constructor(element, scatter, flip) { - // Set log to console.log or a custom log function - // define data structures to store our touchpoints in - - this.element = element; - this.flip = flip; - this.card = element.querySelector('.flipCard'); - this.front = element.querySelector('.front'); - this.back = element.querySelector('.back'); - this.flipped = false; - this.scatter = scatter; - this.onFrontFlipped = flip.onFrontFlipped; - this.onBackFlipped = flip.onBackFlipped; - this.onClose = flip.onClose; - this.onRemoved = flip.onRemoved; - this.onUpdate = flip.onUpdate; - - this.flipDuration = flip.flipDuration; - this.fadeDuration = flip.fadeDuration; - scatter.addTransformEventCallback(this.scatterTransformed.bind(this)); - console.log('lib.DOMFlippable', 5000); - TweenLite.set(this.element, { perspective: 5000 }); - TweenLite.set(this.card, { transformStyle: 'preserve-3d' }); - TweenLite.set(this.back, { rotationY: -180 }); - TweenLite.set([this.back, this.front], { - backfaceVisibility: 'hidden', - perspective: 5000 - }); - TweenLite.set(this.front, { visibility: 'visible' }); - this.infoBtn = element.querySelector('.infoBtn'); - this.backBtn = element.querySelector('.backBtn'); - this.closeBtn = element.querySelector('.closeBtn'); - /* Buttons are not guaranteed to exist. */ - if (this.infoBtn) { - InteractionMapper$1.on('tap', this.infoBtn, event => this.flip.start()); - - this.enable(this.infoBtn); - } - if (this.backBtn) { - InteractionMapper$1.on('tap', this.backBtn, event => this.start()); - } - if (this.closeBtn) { - InteractionMapper$1.on('tap', this.closeBtn, event => this.close()); - this.enable(this.closeBtn); - } - this.scaleButtons(); - this.bringToFront(); - } - - close() { - this.disable(this.infoBtn); - this.disable(this.closeBtn); - if (this.onClose) { - this.onClose(this); - this.flip.closed(); - } else { - this.scatter.zoom(0.1, { - animate: this.fadeDuration, - onComplete: () => { - this.element.remove(); - this.flip.closed(); - if (this.onRemoved) { - this.onRemoved.call(this); - } - } - }); - } - } - - showFront() { - TweenLite.set(this.front, { visibility: 'visible' }); - } - - centerAt(p) { - this.scatter.centerAt(p); - } - - zoom(scale) { - this.scatter.zoom(scale); - } - - get buttonScale() { - let iscale = 1.0; - - if (this.scatter != null) { - let scale = this.scatter.scale || 1; - iscale = 1.0 / scale; - } - return iscale - } - - scaleButtons() { - //This also works for svgs. - // if (this.infoBtn) - // this.infoBtn.style.transform = "scale(" + this.buttonScale + ")" - - // if (this.backBtn) - // this.backBtn.style.transform = "scale(" + this.buttonScale + ")" - - // if (this.closeBtn) - // this.closeBtn.style.transform = "scale(" + this.buttonScale + ")" - - console.log(this.buttonScale); - //// This did not work with svgs! - TweenLite.set([this.infoBtn, this.backBtn, this.closeBtn], { - scale: this.buttonScale - }); - } - - bringToFront() { - this.scatter.bringToFront(); - TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ }); - } - - clickInfo() { - this.bringToFront(); - this.infoBtn.click(); - } - - scatterTransformed(event) { - this.scaleButtons(); - } - - targetRotation(alpha) { - let ortho = 90; - let rest = alpha % ortho; - let delta = 0.0; - if (rest > ortho / 2.0) { - delta = ortho - rest; - } else { - delta = -rest; - } - return delta - } - - infoValues(info) { - let startX = this.element._gsTransform.x; - let startY = this.element._gsTransform.y; - let startAngle = this.element._gsTransform.rotation; - let startScale = this.element._gsTransform.scaleX; - let w = this.element.style.width; - let h = this.element.style.height; - console.log(info, startX, startY, startAngle, startScale, w, h); - } - - show(element, duration = 0, alpha = 1) { - if (element) { - TweenLite.to(element, duration, { autoAlpha: alpha }); // visibility: 'visible', display: 'initial'}) - } - } - - hide(element, duration = 0, alpha = 0) { - if (element) { - TweenLite.to(element, duration, { autoAlpha: alpha }); // {visibility: 'hidden', display: 'none'}) - } - } - - - - enable(button) { - this.show(button, this.fadeDuration); - if (button) { - TweenLite.set(button, { pointerEvents: 'auto' }); - } - } - - disable(button) { - this.hide(button, this.fadeDuration); - if (button) { - TweenLite.set(button, { pointerEvents: 'none' }); - } - } - - start({ targetCenter = null } = {}) { - this.bringToFront(); - if (!this.flipped) { - this.startX = this.element._gsTransform.x; - this.startY = this.element._gsTransform.y; - this.startAngle = this.element._gsTransform.rotation; - this.startScale = this.element._gsTransform.scaleX; - this.startWidth = this.element.style.width; - this.startHeight = this.element.style.height; - this.scatterStartWidth = this.scatter.width; - this.scatterStartHeight = this.scatter.height; - this.show(this.back); - this.disable(this.infoBtn); - this.disable(this.closeBtn); - } else { - this.show(this.front, this.fadeDuration); - this.disable(this.backBtn); - } - let { scalable, translatable, rotatable } = this.scatter; - this.saved = { scalable, translatable, rotatable }; - this.scatter.scalable = false; - this.scatter.translatable = false; - this.scatter.rotatable = false; - this.scatter.killAnimation(); - - this.flipped = !this.flipped; - let targetY = this.flipped ? 180 : 0; - let targetZ = this.flipped - ? this.startAngle + this.targetRotation(this.startAngle) - : this.startAngle; - let targetScale = this.flipped ? this.wantedScale : this.startScale; - let w = this.flipped ? this.wantedWidth : this.startWidth; - let h = this.flipped ? this.wantedHeight : this.startHeight; - let dw = this.wantedWidth - this.scatter.width; - let dh = this.wantedHeight - this.scatter.height; - let tc = targetCenter; - let xx = tc != null ? tc.x - w / 2 : this.startX - dw / 2; - let yy = tc != null ? tc.y - h / 2 : this.startY - dh / 2; - let x = this.flipped ? xx : this.startX; - let y = this.flipped ? yy : this.startY; - - console.log("DOMFlippable.start", this.flipped, targetCenter, x, y, this.saved); - let onUpdate = this.onUpdate !== null ? () => this.onUpdate(this) : null; - console.log(this.flipDuration); - TweenLite.to(this.card, this.flipDuration, { - rotationY: targetY, - ease: Power1.easeOut, - transformOrigin: '50% 50%', - onUpdate, - onComplete: e => { - if (this.flipped) { - //this.hide(this.front) - this.enable(this.backBtn); - this.show(this.backBtn); - - if (this.onFrontFlipped) { - this.onFrontFlipped(this); - } - } else { - - if (this.onBackFlipped == null) { - this.enable(this.infoBtn, this.fadeDuration); - this.enable(this.closeBtn, this.fadeDuration); - } else { - this.onBackFlipped(this); - } - this.flip.unload(); - } - this.scatter.scale = targetScale; - this.scaleButtons(); - this.scatter.rotationDegrees = targetZ; - this.scatter.width = this.flipped ? w : this.scatterStartWidth; - this.scatter.height = this.flipped ? h : this.scatterStartHeight; - - let { scalable, translatable, rotatable } = this.saved; - this.scatter.scalable = scalable; - this.scatter.translatable = translatable; - this.scatter.rotatable = rotatable; - }, - force3D: true - }); - - // See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/ - TweenLite.to(this.element, this.flipDuration / 2, { - scale: targetScale, - ease: Power1.easeOut, - rotationZ: targetZ + '_short', - transformOrigin: '50% 50%', - width: w, - height: h, - x: x, - y: y, - onComplete: e => { - if (this.flipped) { - this.hide(this.front); - // this.hide(this.infoBtn) - } else { - this.hide(this.back); - // this.show(this.infoBtn) - } - } - }); - } - } - - class Index { - - constructor(template, pages, notfound='thumbnails/notfound.png') { - this.template = template; - this.pages = pages; - this.notfound = notfound; - } - - setup() { - for(let pair of this.pages) { - let [title, src] = pair; - let id = getId(); - pair.push(id); - let t = this.template; - let wrapper = t.content.querySelector('.wrapper'); - wrapper.id = id; - let clone = document.importNode(t.content, true); - container.appendChild(clone); - wrapper = container.querySelector('#'+id); - - let icon = wrapper.querySelector('.icon'); - - icon.onerror = (e) => { - if (this.notfound) - icon.src = this.notfound; - }; - let iconSrc = src.replace('.html', '.png'); - //console.log("iconSrc", iconSrc) - if (iconSrc.endsWith('index.png')) { - icon.src = iconSrc.replace('index.png', 'thumbnail.png'); - } else if (iconSrc.endsWith('test.png')) { - icon.src = iconSrc.replace('test.png', 'thumbnail.test.png'); - } else { - icon.src = 'thumbnails/' + iconSrc; - } - wrapper.href = src; - let titleDiv = wrapper.querySelector('.title'); - titleDiv.innerText = title; - } - } - - frames() { - if (this.pages.length == 0) - return - let [title, src, id] = this.pages.shift(); - let iframe = document.createElement('iframe'); - iframe.frameborder = 0; - let wrapper = document.getElementById(id); - let icon = wrapper.querySelector('.icon'); - - icon.parentNode.replaceChild(iframe, icon); - iframe.onload = (e) => { - this.frames(); - }; - iframe.src = src + window.location.search; - } - - load() { - this.setup(); - if (window.location.search.startsWith('?test')) - this.frames(); - } - - loadAndTest() { - this.setup(); - if (!Capabilities.isMobile) - this.frames(); - } - } - - /** Basic class for poppable elements that need to be closed as soon as one poppable is - * shown. - */ - class Poppable { - - /** Register the poppable element in a context. Closes previously registered ones. - * @param {*} context - */ - register(context) { - let registered = Poppable.get(context); - if (registered != null) { - registered.close(); - } - Poppable.set(context, this); - } - - /** - * Unregister object from context - * @param {*} context - */ - unregister(context) { - Poppable.delete(context); - } - - /** - * Returns the given poppable in a context - * @static - * @param {*} context - * @returns - * @memberof Poppable - */ - static get(context) { - return Poppable.registrations.get(context) - } - - /** Sets the poppable in the given context - * @static - * @param {*} context - * @param {*} poppable - * @returns - * @memberof Poppable - */ - static set(context, poppable) { - return Poppable.registrations.set(context, poppable) - } - - /** Test whether a poppable exists in the given context - * - * @param {*} context - */ - static has(context) { - return Poppable.registrations.has(context) - } - - /** - * Removes the poppable from the given context. - * - * @static - * @param {*} context - * @memberof Poppable - */ - static delete(context) { - Poppable.registrations.delete(context); - } - - /** All poppable must implement a close method. */ - close() { - console.error("Must be implemented"); - } - } - - Poppable.registrations = new Map(); - - /** A Popup that shows text labels, images, or html - */ - class Popup extends Poppable { - /** - * Creates an instance of Popup. - * @param {any} [{ - * parent = null, - The DOM parent element. - * content = null, - A dict object with type strings (text, img, html) as keys - * and corresponding values. - * context = window, - A context object for poppable elements - * fontSize = "1em", - Describes the font size as CSS value - * fontFamily = "Arial", - Describes the font family as CSS value - * padding = 16, - {number || string} padding - Describes the padding as CSS value - * notchSize = 10, - {number || string} notchSize - Describes the size of the notch (callout) as CSS value - * switchPos = false, - * minWidth = null, - * maxWidth = 800, - * backgroundColor = "#EEE", - The color of the background as CSS value - * normalColor = "#444", - normalColor - The color of textitems as CSS value - * notchPosition = "bottomLeft", - * zIndex = 0, - * keepWithin = null, - Ensure that popup is visible within the bounds of the given container - * autoClose = true, - Autoclose the Popup on tap - * closeIcon = null, - * resizeIcon = null, - * closeCommand = null, - * draggable = false - * noStyle = false - When true, prevents the popup from doing any aesthetic manipulations to the DOM leaving the styling completely to the style sheets. - * }={}] - * @memberof Popup - */ - constructor({ - parent = null, - content = null, - context = window, - fontSize = "1em", - fontFamily = "Arial", - padding = 16, - notchSize = 10, - switchPos = false, - minWidth = null, - maxWidth = 800, - backgroundColor = "#EEE", - normalColor = "#444", - notchPosition = "bottomCenter", - zIndex = 0, - keepWithin = null, - autoClose = true, - closeIcon = null, - resizeIcon = null, - closeCommand = null, - draggable = false, - posOffset = 0, - targetBoundingBox = null, - useEventPosWithBoundingBox = false, - interactive = false, - onResize = null, - onMove = null, - noStyle = false, - hideOnUp = true, - } = {}) { - super(); - this.context = context; - this.noStyle = noStyle; - this.hideOnUp = hideOnUp; - this.padding = padding; - this.notchPosition = notchPosition; - this.notchSize = notchSize; - this.switchPos = switchPos; - this.fontSize = fontSize; - this.fontFamily = fontFamily; - this.minWidth = minWidth; - this.maxWidth = maxWidth; - this.normalColor = normalColor; - this.backgroundColor = backgroundColor; - this.keepWithin = keepWithin; - this.autoClose = autoClose; - this.resizeIcon = resizeIcon; - this.closeIcon = closeIcon; - this.closeCommand = closeCommand; - this.zIndex = zIndex; - this.parent = parent || document.body; - this.draggable = draggable; - this.posOffset = posOffset; - this.targetBoundingBox = targetBoundingBox; - this.useEventPosWithBoundingBox = useEventPosWithBoundingBox; - this.currentPos = null; - this.insertedNode = null; - this.loaded = false; - this.interactive = interactive; - this.onload = null; - this.onResize = onResize; - this.onMove = onMove; - if (content) { - this.show(content); - } - } - - /** Setup popup with a dictionary of content types and contents. - * @param {Object} content - A dict object with type strings (text, img, html) as keys - * and corresponding values. - * @return {Popup} this - */ - setup(content) { - //console.log("Popup.setup", this.draggable) - this.content = content; - this.items = {}; - this.element = document.createElement("div"); - this.element.classList.add("popup"); - this.setAlpha(this.element, 0); - // this.element.style.opacity = 0 - Elements$1.addClass(this.element, "unselectable"); - this.notch = document.createElement("div"); - Elements$1.setStyle(this.notch, this.notchStyle()); - this.notch.className = "notch"; - this.setupDraggable(); - if (this.closeIcon) { - let img = document.createElement("img"); - img.setAttribute("draggable", false); - img.src = this.closeIcon; - img.style.position = "absolute"; - img.style.right = "0px"; - img.style.top = "0px"; - img.style.width = "16px"; - img.style.height = "16px"; - img.onclick = e => { - this.close(); - }; - this.element.appendChild(img); - } - if (this.resizeIcon) { - let img = document.createElement("img"); - img.style.position = "absolute"; - img.style.right = "0px"; - img.style.bottom = "0px"; - img.style.width = "16px"; - img.style.height = "16px"; - img.src = this.resizeIcon; - img.setAttribute("draggable", true); - img.ondragstart = e => { - this.currentPos = { x: e.clientX, y: e.clientY }; - return true - }; - img.ondrag = e => { - e.preventDefault(); - - let target = this.element.querySelector("iframe") || this.element; - let delta = { - x: e.clientX - this.currentPos.x, - y: e.clientY - this.currentPos.y - }; - - this.currentPos = { x: e.clientX, y: e.clientY }; - if (delta.x == 0 && delta.y == 0) - return - - let rect = target.getBoundingClientRect(); - let width = rect.width + delta.x; - let height = rect.height + delta.y; - target.style.width = width + "px"; - target.style.height = height + "px"; - - switch (this.notchPosition) { - case "bottomLeft": - case "bottomCenter": - let bottom = parseFloat(this.element.style.bottom); - this.element.style.bottom = bottom - delta.y + "px"; - break - default: - break - } - //console.log("onResize", this.onResize) - if (this.onResize) { - this.onResize({ target, delta, width, height }); - } - }; - img.ondragend = e => { }; - this.element.appendChild(img); - } - - - for (let key in content) { - switch (key) { - case "selector": - break - case "text": - let text = document.createElement("span"); - this.element.appendChild(text); - text.innerHTML = content[key]; - Elements$1.setStyle(text, { color: this.normalColor }); - Elements$1.addClass(text, "unselectable"); - Elements$1.addClass(text, "PopupContent"); - this.insertedNode = text; - this.loaded = true; - break - case "img": - alert("img to be implemented"); - break - case "iframe": - let iframe = document.createElement("iframe"); - iframe.setAttribute("frameBorder", 0); - iframe.src = content[key]; - iframe.onload = e => { - let body = iframe.contentWindow.document.body; - let observer = new MutationObserver(() => { - this.iframeChanged(iframe); - }); - observer.observe(iframe.contentWindow.document, { - attributes: true, - subtree: true, - childList: true, - characterData: true - }); - let w = Math.max(body.scrollWidth, body.offsetWidth); - let h = Math.max(body.scrollHeight, body.offsetHeight); - iframe.style.width = w + "px"; - iframe.style.height = h + "px"; - this.layoutAfterInsert(); - if (this.onload != null) { - this.onload(); - } - this.loaded = true; - }; - this.element.appendChild(iframe); - Elements$1.addClass(iframe, "PopupContent"); - this.insertIntoDOM(); - return - case "html": - this.loaded = false; - let div = document.createElement("div"); - Elements$1.addClass(div, "PopupContent"); - this.element.appendChild(div); - div.innerHTML = content.html; - //console.log("insert", content) - let selector = content.selector; - if (selector) { - this.insertedNode = div.querySelector(selector); - if (this.insertedNode == null) { - div.innerHTML = `

Popup content not found. Missing ${selector}

`; - this.insertedNode = div.firstElementChild; - } - } - else { - this.insertedNode = div.firstElementChild || div; - } - this.setAlpha(this.insertedNode, 0); - let images = this.element.querySelectorAll('img'); - let total = 0; - if (images.length > 0) { - let count = 0; - for (let image of images) { - if (!image.complete && !image.src.startsWith('data:')) { - total += 1; - console.log("image not complete", image.src); - image.onload = e => { - count += 1; - if (count == total) { - this.loaded = true; - if (this.onload != null) { - this.onload(); - } - } - }; - } - } - } - if (total == 0) { - this.loaded = true; - } - break - case "node": - this.loaded = true; - Elements$1.addClass(content.node, "PopupContent"); - this.element.appendChild(content.node); - this.insertedNode = content.node; - this.setAlpha(this.insertedNode, 0); - break - default: - alert("Unexpected content type: " + key); - break - } - } - this.insertIntoDOM(); - this.layoutAfterInsert(); - this.setupEventHandler(); - return this - } - - handleClose(e) { - let closing = this.closingEvent(e); - if (closing) { - this.close(); - } - else { - this.setupCloseHandler(); - } - } - - setupCloseHandler() { - let close = this.handleClose; - if (this.hideOnUp) { - if (window.PointerEvent) - this.parent.addEventListener("pointerup", close.bind(this), { capture: true, once: true }); - else if (window.TouchEvent) - this.parent.addEventListener("touchend", close.bind(this), { capture: true, once: true }); - else - this.parent.addEventListener("mouseup", close.bind(this), { capture: true, once: true }); - } else { - if (window.PointerEvent) - this.parent.addEventListener("pointerdown", close.bind(this), { capture: true, once: true }); - else if (window.TouchEvent) - this.parent.addEventListener("touchstart", close.bind(this), { capture: true, once: true }); - else - this.parent.addEventListener("mousedown", close.bind(this), { capture: true, once: true }); - } - } - - setupEventHandler() { - if (this.autoClose) { - this.setupCloseHandler(); - } - } - - closingEvent(e) { - if (this.interactive) { - let node = e.target.closest(".PopupContent"); - return node == null - } - return true - } - - iframeChanged(iframe) { - let body = iframe.contentWindow.document.body; - let w = Math.max(body.scrollWidth, body.offsetWidth); - let h = Math.max(body.scrollHeight, body.offsetHeight); - iframe.style.width = w + "px"; - iframe.style.height = h + "px"; - } - - setupDraggable() { - if (this.draggable) { - let target = this.element; - target.setAttribute("draggable", true); - target.ondragstart = e => { - this.currentPos = { x: e.clientX, y: e.clientY }; - var img = document.createElement('img'); - img.src = 'data:image/gifbase64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; - e.dataTransfer.setDragImage(img, 0, 0); - }; - target.ondrag = e => { - e.preventDefault(); - let delta = { - x: e.clientX - this.currentPos.x, - y: e.clientY - this.currentPos.y - }; - this.currentPos = { x: e.clientX, y: e.clientY }; - let left = parseFloat(target.style.left); - let top = parseFloat(target.style.top); - - target.style.left = left + delta.x + 'px'; - target.style.top = top + delta.y + 'px'; - - //console.log("Popup.ondrag", target, event.target) - if (this.onMove) { - this.onMove({ target, delta }); - } - - this.lastDrag = { left, top }; - }; - target.ondragend = e => { - target.style.left = this.lastDrag.left + 'px'; - target.style.top = this.lastDrag.top + 'px'; - this.currentPos = null; - }; - } - } - - moveDragged(target) { - - } - - insertIntoDOM(layout = true) { - this.setAlpha(this.insertedNode, 0); - this.element.appendChild(this.notch); - this.parent.appendChild(this.element); - } - - layoutAfterInsert() { - Elements$1.setStyle(this.element, this.defaultStyle()); - this.layout(); - //this.element.style.opacity = 1 - } - - /** Layout the menu items. Needed only in the subclass. - */ - layout() { } - - - remove() { - if (this.parent.contains(this.element)) - this.parent.removeChild(this.element); - this.unregister(this.context); - } - - /** Close and remove the Popup from the DOM tree. - */ - close() { - //console.log("Popup.close", this.closeCommand) - this.unregister(this.context); - if (this.closeCommand) { - this.closeCommand(this, () => this.remove()); - } - else { - this.remove(); - } - } - - /** - * Set the alpha value to show or hide the popup. Uses CSS transitions. - * (A former dependency on TweenLite has beeen removed.) - * - * @param {*} targets - * @param {*} value - * @memberof Popup - */ - setAlpha(targets, value) { - let objs = (targets instanceof Array) ? targets : [targets]; - for (let obj of objs) { - if (value) { - obj.style.transition = "opacity 0.2s ease-in"; - } - obj.style.opacity = value; - } - // if (value) { - // TweenLite.to(targets, 0.2, { autoAlpha: value }) - // } - // else { - // TweenLite.set(targets, { autoAlpha: 0 }) - // } - } - - /** - * Starts a fade in animation. - * - * @memberof Popup - */ - fadeIn() { - this.setAlpha([this.element, this.insertedNode], 1); - } - - /** Shows the Popup with the given commands at the specified point. - * @param {Object} content - A dict object with type strings (text, img, html) as keys - * and corresponding values. - * @param {Point} point - The position as x, y coordinates {px}. - * @return {Popup} this - */ - showAt(content, point) { - this.setup(content); - console.log("showAt", this.loaded); - if (this.loaded) { - this.placeAt(point); - this.fadeIn(); - } - else { - this.setAlpha([this.element, this.insertedNode], 0); - this.onload = () => { - this.layoutAfterInsert(); - this.placeAt(point); - this.fadeIn(); - }; - } - return this - } - - /** - * Place the origin, i.e. the upper left corner at the given position using CSS styles. - * - * @param {any} x - * @param {any} y - * @memberof Popup - */ - placeOrigin(x, y) { - Elements$1.setStyle(this.element, { left: x + "px", top: y + "px" }); - } - - /** - * Calculate the local coordinates within the keepWithin container. - * - * @param {any} x - * @param {any} y - * @returns - * @memberof Popup - */ - localPointWithin(x, y, width, height) { - let pt = { x, y }; - return pt - } - - withinDimensions() { - return { - width: this.keepWithin.offsetWidth, - height: this.keepWithin.offsetHeight - } - } - - localDimensions() { - return { - width: this.element.offsetWidth, - height: this.element.offsetHeight - } - } - - /** - * Returns the notch position depending on the local coordinates within the keepWithin container - * Divides the space vertically into top, center, bottom and horizontally into left, center, right - * - * @param {any} x - * @param {any} y - * @returns - * @memberof Popup - */ - notchPositionWithin(x, y) { - let horizontal = "Center"; - let vertical = "center"; - let { width, height } = this.withinDimensions(); - let local = this.localPointWithin(x, y, width, height); - if (local.y < height * 0.33) { - vertical = "top"; - } - if (local.y > height * 0.66) { - vertical = "bottom"; - } - if (local.x < width * 0.33) { - horizontal = "Left"; - } - if (local.x > width * 0.66) { - horizontal = "Right"; - } - let result = vertical + horizontal; - if (result == "centerCenter") - return this.notchPosition - return result - } - - placeAt(point) { - let x = point.x; - let y = point.y; - let notchPosition = this.notchPosition; - if (this.keepWithin != null) { - notchPosition = this.notchPositionWithin(x, y); - } - Elements$1.setStyle(this.notch, this.notchStyle(notchPosition)); - this.notch.className = "notch " + notchPosition; - let { width, height } = this.localDimensions(); - - //if targetBoundingBox is set, popup is placed next to the rectangle - if (this.targetBoundingBox) { - let bbTop = this.targetBoundingBox.y; - let bbBottom = this.targetBoundingBox.y + this.targetBoundingBox.height; - let bbLeft = this.targetBoundingBox.x; - let bbRight = this.targetBoundingBox.x + this.targetBoundingBox.width; - //console.log("place popup with bb set:", x, y, bbTop, bbBottom, bbLeft, bbRight) - switch (notchPosition) { - case "bottomLeft": - case "bottomRight": - case "bottomCenter": - y = bbTop; - if (!this.useEventPosWithBoundingBox) - x = (bbLeft + bbRight) / 2; - break - case "topLeft": - case "topRight": - case "topCenter": - y = bbBottom; - if (!this.useEventPosWithBoundingBox) - x = (bbLeft + bbRight) / 2; - break - case "centerRight": - x = bbLeft; - if (!this.useEventPosWithBoundingBox) - y = (bbTop + bbBottom) / 2; - break - case "centerLeft": - x = bbRight; - if (!this.useEventPosWithBoundingBox) - y = (bbTop + bbBottom) / 2; - break - default: - break - } - } - - //calculate position depending on several (optional) parameters - switch (notchPosition) { - case "bottomLeft": - x -= this.padding; - x -= this.notchSize; - y -= height; - y -= this.notchSize * 2; - y -= this.posOffset; - break - case "bottomRight": - x -= width; - x += this.padding; - x += this.notchSize; - y -= height; - y -= this.notchSize * 2; - y -= this.posOffset; - break - case "bottomCenter": - x -= width / 2; - y -= height; - y -= this.notchSize * 2; - y -= this.posOffset; - break - case "topLeft": - x -= this.padding; - x -= this.notchSize; - y += this.notchSize * 2; - y += this.posOffset; - break - case "topRight": - x -= width; - x += this.padding; - x += this.notchSize; - y += this.notchSize * 2; - y += this.posOffset; - break - case "topCenter": - x -= width / 2; - y += this.notchSize * 2; - y += this.posOffset; - break - case "centerRight": - x -= width + this.notchSize * 2; - x -= this.posOffset; - y -= height / 2; - break - case "centerLeft": - //console.log("height", height) - y -= height / 2; - x += this.notchSize * 2; - x += this.posOffset; - break - default: - break - } - this.placeOrigin(x, y); - } - - /** Shows the Popup with the given commands at the current position. - * @param {Object} content - A dict object with type strings (text, img, html) as keys - * and corresponding values. - * @return {Popup} this - */ - show(content) { - this.setup(content); - this.fadeIn(); - return this - } - - /** Configuration object. Return default styles as CSS values. - */ - defaultStyle() { - let padding = this.padding; - let style = { - maxWidth: this.maxWidth + "px", - zIndex: this.zIndex, - position: "absolute", - }; - if (this.minWidth) { - style.minWidth = this.minWidth + "px"; - } - if (!this.noStyle) { - Object.assign(style, { - borderRadius: Math.round(this.padding / 2) + "px", - backgroundColor: this.backgroundColor, - padding: this.padding + "px", - boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)", - fontFamily: this.fontFamily, - fontSize: this.fontSize, - stroke: "black", - fill: "white" - }); - } - - return style - } - - /** Configuration object. Return notch styles as CSS values. - */ - notchStyle(notchPosition = null) { - if (notchPosition == null) { - notchPosition = this.notchPosition; - } - let width = 0; - let height = 0; - let left = this.padding; - let size = this.localDimensions(); - if (notchPosition.endsWith("Right")) { - left = size.width - this.padding - this.notchSize * 2; - } - if (notchPosition.endsWith("Center")) { - left = size.width / 2 - this.notchSize; - } - left = Math.round(left) + 'px'; - - if (notchPosition.startsWith("bottom")) { - if (this.noStyle) { - return { - width, - height, - left, - bottom: -this.notchSize + "px", - position: "absolute", - borderStyle: "solid", - borderTopWidth: this.notchSize + "px", - borderRight: this.notchSize + "px solid transparent", - borderLeft: this.notchSize + "px solid transparent", - borderBottom: 0 - } - - } else { - return { - width, - height, - left, - boxShadow: "0 12px 15px rgba(0, 0, 0, 0.1)", - bottom: -this.notchSize + "px", - position: "absolute", - borderTop: this.notchSize + "px solid " + this.backgroundColor, - borderRight: this.notchSize + "px solid transparent", - borderLeft: this.notchSize + "px solid transparent", - borderBottom: 0 - } - } - } - if (notchPosition.startsWith("top")) { - if (this.noStyle) { - return { - width, - height, - left, - top: -this.notchSize + "px", - position: "absolute", - borderStyle: "solid", - borderBottomWidth: this.notchSize + "px", - borderRight: this.notchSize + "px solid transparent", - borderLeft: this.notchSize + "px solid transparent", - borderTop: 0 - } - } else { - return { - width, - height, - left, - top: -this.notchSize + "px", - position: "absolute", - borderBottom: this.notchSize + "px solid " + this.backgroundColor, - borderRight: this.notchSize + "px solid transparent", - borderLeft: this.notchSize + "px solid transparent", - borderTop: 0 - } - } - } - - if (this.noStyle) { - - if (notchPosition.endsWith("Left")) { - left = -this.notchSize * 2 + "px"; - } - - if (notchPosition.endsWith("Right")) { - left = size.width + "px"; - } - - - let top = size.height / 2 - this.notchSize; - top = Math.round(top) + 'px'; - - - return { - width, - height, - left, - top, - borderRightWidth: this.notchSize, - borderLeftWidth: this.notchSize, - position: "absolute", - borderTop: this.notchSize + "px solid transparent", - borderBottom: this.notchSize + "px solid transparent" - } - - } else { - let borderRight = this.notchSize + "px solid transparent"; - let borderLeft = this.notchSize + "px solid transparent"; - let top = size.height / 2 - this.notchSize; - if (notchPosition.endsWith("Left")) { - left = -this.notchSize * 2 + "px"; - borderRight = this.notchSize + "px solid " + this.backgroundColor; - this.element.style.boxShadow = "15px 10px 15px rgba(0, 0, 0, 0.3)"; - } - if (notchPosition.endsWith("Right")) { - left = size.width + "px"; - borderLeft = this.notchSize + "px solid " + this.backgroundColor; - this.element.style.boxShadow = "15px 5px 15px rgba(0, 0, 0, 0.3)"; - } - - top = Math.round(top) + 'px'; - - - return { - width, - height, - left, - top, - borderRight, - borderLeft, - // boxShadow, - position: "absolute", - borderTop: this.notchSize + "px solid transparent", - borderBottom: this.notchSize + "px solid transparent" - } - } - } - - /** Convenient static methods to show and reuse a Popup implemented - * as a class variable. - * @param {Object} content - A dict object with type strings (text, img, html) as keys - * and corresponding values. - * @param {Point} point - The position as x, y coordinates {px}. - * @param {boolean} autoClose - Autoclose the menu after selecting an item. - */ - static open( - content, - point, - { - parent = null, - context = window, - fontSize = "1em", - fontFamily = "Arial", - padding = 16, - notchSize = 10, - switchPos = false, - minWidth = null, - maxWidth = 800, - backgroundColor = "#EEE", - zIndex = 0, - normalColor = "#444", - closeIcon = null, - resizeIcon = null, - closeCommand = null, - autoClose = true, - keepWithin = null, - draggable = false, - posOffset = 0, - targetBoundingBox = null, - useEventPosWithBoundingBox = false, - interactive = false, - onResize = null, - onMove = null - } = {} - ) { - - - let notchPosition = (switchPos && point.y < 50) ? "topCenter" : "bottomCenter"; - let popup = new Popup({ - parent, - context, - fontFamily, - fontSize, - padding, - notchSize, - switchPos, - minWidth, - maxWidth, - backgroundColor, - normalColor, - notchPosition, - zIndex, - autoClose, - keepWithin, - closeCommand, - closeIcon, - resizeIcon, - draggable, - posOffset, - targetBoundingBox, - useEventPosWithBoundingBox, - interactive, - onResize, - onMove - }); - popup.register(context); - popup.showAt(content, point); - return popup - } - - /** Convenient static method to close the Popup implemented - * as a class variable. Calls the close command. - */ - static closePopup(context = window) { - let popup = Poppable.registrations.get(context); - if (popup != null) { - popup.close(); - } - } - - /** Convenient static methods to remove the Popup implemented - * as a class variable. Removes the popup without performing the close command. - */ - static remove(context = window) { - let popup = Poppable.registrations.get(context); - if (popup != null) { - popup.remove(); - } - } - - /** - * Convenient static method to compute the clicked rect of objects that have multiple clients rects. - * Needed to position popups correctly above objects with line breaks, e.g. spans - * - * @static - * @param {*} event - * @returns {*} DOMRect - * @memberof Popup - */ - static targetRect(event) { - let target = event.target; - let x = event.pageX; - let y = event.pageY; - for (let rect of target.getClientRects()) { - let withinX = x >= rect.left && x <= rect.left + rect.width; - let withinY = y >= rect.top && y <= rect.top + rect.height; - if (withinX && withinY) { - return rect - } - } - return null - } - - /** - * Convenient static method to compute the center of objects that have multiple clients rects. - * Needed to position popups correctly above objects with line breaks, e.g. spans - * - * @static - * @param {*} event - * @returns {*} Point - * @memberof Popup - */ - static targetCenter(event) { - let target = event.target; - let x = event.pageX; - let y = event.pageY; - let rect = Popup.targetRect(event); - if (rect != null) { - x = rect.left + rect.width / 2; - y = rect.top + rect.height / 2; - } - return { x, y } - } - } - - /** A Popup Menu that shows text labels in a vertical row. - */ - class PopupMenu$1 extends Popup { - /** - * The constructor. - * @constructor - * @param {DOM Element} parent - The DOM parent element. - * @param {Object} commands - A dict object with command label strings as keys - * and command functions as values. - * @param {string} fontSize - Describes the font size as CSS value - * @param {number || string} padding - Describes the padding as CSS value - * @param {number || string} notchSize - Describes the size of the notch (callout) as CSS value - * @param {string} highlightColor - The color of highlighted menu items as CSS value - * @param {string} backgroundColor - The color of the background as CSS value - * @param {string} normalColor - The color of normal menu items as CSS value - * @param {DOM Element} keepWithin - The container to stay within - * @param {boolean} autoClose - Autoclose the menu after selecting an item - */ - constructor({ parent = null, - commands = null, - fontSize = '1em', - fontFamily = 'Arial', - padding = 16, - zIndex = 1, - spacing = '0px', - switchPos = false, - notchSize = 10, - maxWidth = 800, - backgroundColor = '#EEE', - normalColor = '#444', - highlightColor = 'black', - notchPosition = 'bottomLeft', - keepWithin = null, - autoClose = true } = {}) { - super({ parent, fontSize, fontFamily, padding, notchSize, notchPosition, backgroundColor, keepWithin, normalColor, autoClose }); - this.commands = commands; - this.zIndex = zIndex; - this.switchPos = switchPos; - this.spacing = spacing; - this.highlightColor = highlightColor; - } - - /** Setup menu with a dictionary of command labels and command functions. - * @param {Object} commands - A dict object with command label strings as keys - * and command functions as values. - * @return {PopupMenu} this - */ - setup(commands) { - - this.commands = commands; - this.items = {}; - this.element = document.createElement('div'); - this.element.style.zIndex = this.zIndex; - Elements$1.addClass(this.element, 'unselectable'); - this.notch = document.createElement('div'); - Elements$1.setStyle(this.notch, this.notchStyle()); - for (let key in commands) { - let item = document.createElement('div'); - this.element.appendChild(item); - item.innerHTML = key; - item.style.paddingBottom = item.style.paddingTop = this.spacing; - Elements$1.setStyle(item, { color: this.normalColor, cursor: 'default' }); - Elements$1.addClass(item, 'unselectable'); - Elements$1.addClass(item, 'popupMenuItem'); - this.items[key] = item; - item.onclick = (event) => { this.perform(key); }; - item.ontap = (event) => { this.perform(key); }; - item.onmouseover = (event) => { this.over(event, key); }; - item.onmouseout = (event) => { this.out(event, key); }; - } - - this.element.appendChild(this.notch); - this.parent.appendChild(this.element); - this.insertedNode = this.element; - Elements$1.setStyle(this.element, this.defaultStyle()); - this.layout(); - return this - } - - /** Execute a menu command. - * @param {string} key - The selected key. - */ - perform(key) { - let func = this.commands[key]; - if (this.autoClose) { - this.close(); - } - setTimeout(() => { - func.call(); - }, 20); - } - - /** Update the menu item denoted by key. - * @param {string} key - The selected key. - * @param {boolean} highlight - Show the item highlighted. - */ - update(key, highlight = false) { - let text = this.items[key]; - text.style.color = (highlight) ? this.highlightColor : this.normalColor; - } - - /** Mouse over handöer. - * @param {Event} event - The mouse event. - * @param {boolean} key - The selected key. - */ - over(event, key) { - for (let k in this.items) { - this.update(k, k == key); - } - } - - /** Mouse out handöer. - * @param {Event} event - The mouse event. - * @param {boolean} key - The selected key. - */ - out(event, key) { - this.update(key); - } - - /** Shows the PopupMenu with the given commands at the specified point. - * @param {Object} commands - A dict object with command label strings as keys - * and command functions as values. - * @param {Point} point - The position as x, y coordinates {px}. - * @return {PopupMenu} this - */ - showAt(commands, point) { - this.show(commands); - this.placeAt(point); - return this - } - - /** Convenient static methods to show and reuse a PopupMenu implemented - * as a class variable. - * @param {Object} commands - A dict object with command label strings as keys - * and command functions as values. - * @param {Point} point - The position as x, y coordinates {px}. - * @param {string} fontSize - Describes the font size as CSS value - * @param {number || string} padding - Describes the padding as CSS value - * @param {number || string} notchSize - Describes the size of the notch (callout) as CSS value - * @param {string} highlightColor - The color of highlighted menu items as CSS value - * @param {string} backgroundColor - The color of the background as CSS value - * @param {string} normalColor - The color of normal menu items as CSS value - * @param {boolean} autoClose - Autoclose the menu after selecting an item - */ - static open(commands, point, { parent = null, - context = window, - fontSize = '1em', - fontFamily = 'Arial', - padding = 16, - zIndex = 1, - spacing = '0px', - switchPos = false, - notchSize = 10, - maxWidth = 800, - keepWithin = null, - backgroundColor = '#EEE', - normalColor = '#444', - autoClose = true } = {}) { - - let registered = Poppable.get(context); - if (registered) { - this.closePopup(); - return - } - console.log("open", point); - let notchPosition = (point.y < 50 && switchPos) ? 'topCenter' : 'bottomCenter'; - let popup = new PopupMenu$1({ - parent, fontSize, padding, zIndex, spacing, switchPos, notchSize, - notchPosition, - maxWidth, backgroundColor, normalColor, - notchPosition, keepWithin, autoClose - }); - popup.showAt(commands, point); - popup.register(context); - popup.closeEventListener = (e) => { - if (this.eventOutside(e)) - this.closePopup(context); - }; - if (autoClose) { - context.addEventListener('mousedown', popup.closeEventListener, true); - context.addEventListener('touchstart', popup.closeEventListener, true); - context.addEventListener('pointerdown', popup.closeEventListener, true); - } - } - - static eventOutside(e) { - return !Elements$1.hasClass(e.target, 'popupMenuItem') - } - - /** Convenient static methods to close the PopupMenu implemented - * as a class variable. - */ - static closePopup(context=window) { - let registered = Poppable.get(context); - if (registered) { - registered.close(); - context.removeEventListener('mousedown', registered.closeEventListener); - context.removeEventListener('touchstart', registered.closeEventListener); - context.removeEventListener('pointerdown', registered.closeEventListener); - } - } - } - - class FrameContainer { - - constructor(element) { - this.element = element; - this.delegate = new InteractionMapper(element, this, - { mouseWheelElement: window}); - } - - capture(event) { - return true - } - - findTarget(event, local, global) { - let found = document.elementFromPoint(global.x, global.y); - let iframe = found.querySelector('iframe'); - if (iframe) { - let p = Points.fromPageToNode(found, global); - let doc = iframe.contentWindow.document; - let target = doc.elementFromPoint(p.x, p.y); - if (target != null) { - console.log('iframe element', target); - return new FrameTarget(iframe, target) - } - } - return null - } - } - - class FrameTarget { - - constructor(frame, target, debug=false) { - this.frame = frame; - this.target = target; - this.debug = debug; - } - - capture(event) { - return true - } - - simulateMouseEvent(type, point) { - let p = Points.fromPageToNode(this.frame, point); - let event = new MouseEvent(type, { - view: this.frame.contentWindow, - bubbles: true, - cancelable: true, - clientX: p.x, - clientY: p.y}); - this.target.dispatchEvent(event); - } - - createTouchList(pointMap) { - let touches = []; - let doc = this.frame.contentWindow.document; - for(let key of pointMap.keys()) { - let point = pointMap.get(key); - let p = Points.fromPageToNode(this.frame, point); - let touchTarget = doc.elementFromPoint(p.x, p.y); - let touch = new Touch(undefined, touchTarget, key, - p.x, p.y, p.x, p.y); - touches.push(touch); - } - return new TouchList(...touches) - } - - simulateTouchEventChrome(type, point, pointMap) { - let doc = this.frame.contentWindow.document; - let p = Points.fromPageToNode(this.frame, point); - let touchTarget = doc.elementFromPoint(p.x, p.y); - const touchObj = new Touch({ - identifier: Date.now(), - target: touchTarget, - clientX: p.x, - clientY: p.y, - pageX: p.x, - pageY: p.y, - radiusX: 2.5, - radiusY: 2.5, - rotationAngle: 10, - force: 0.5, - }); - - const touchEvent = new TouchEvent(type, { - cancelable: true, - bubbles: true, - touches: [touchObj], - targetTouches: [touchObj], - changedTouches: [touchObj], - shiftKey: false, - }); - if (this.debug) console.log("simulateTouchEventChrome", touchEvent); - this.target.dispatchEvent(touchEvent); - } - - simulateTouchEventSafari(type, point, pointMap, touchEventKey='targetTouches') { - let p = Points.fromPageToNode(this.frame, point); - let data = { view: this.frame.contentWindow, - bubbles: true, - cancelable: true, - clientX: p.x, - clientY: p.y}; - data[touchEventKey] = this.createTouchList(pointMap); - let event = new TouchEvent(type, data); - if (this.debug) console.log("simulateTouchEventChrome", touchEvent); - this.target.dispatchEvent(event); - } - - simulateTouchEvent(type, point, pointMap, touchEventKey='targetTouches') { - if (Capabilities.isSafari) { - this.simulateTouchEventSafari(type, point, pointMap, touchEventKey); - } - else { - this.simulateTouchEventChrome(type, point, pointMap); - } - } - - isMouseLikeEvent(event) { - return event.type.startsWith('mouse') || event.type.startsWith('pointer') - } - - onStart(event, interaction) { - if (this.debug) console.log('onStart', this.frame.parentNode); - for(let [key, point] of interaction.current.entries()) { - if (this.isMouseLikeEvent(event)) { - this.simulateMouseEvent('mousedown', point); - } - else { - this.simulateTouchEvent('touchstart', point, - interaction.current); - return - } - } - } - - onMove(event, interaction) { - if (this.debug) console.log('onMove'); - for(let [key, point] of interaction.current.entries()) { - if (this.isMouseLikeEvent(event)) { - this.simulateMouseEvent('mousemove', point); - } - else { - this.simulateTouchEvent('touchmove', point, - interaction.current); - return - } - } - } - - onEnd(event, interaction) { - if (this.debug) console.log('onEnd'); - for(let [key, point] of interaction.current.entries()) { - if (this.isMouseLikeEvent(event)) { - this.simulateMouseEvent('mouseend', point); - } - else { - this.simulateTouchEvent('touchend', point, - interaction.ended, 'changedTouches'); - return - } - } - } - } - - class Inspect { - // Code inspection functions - - static allScriptSources() - { - let sources = []; - let scripts = document.getElementsByTagName('script'); - for (let i = 0; i < scripts.length; i++) { - console.dir(scripts[i]); - sources.push(scripts[i]); - } - return sources - } - } - - /* globals Power0 */ - /* eslint no-console: ["error", { allow: ["log", "info", "warn", "error"] }] */ - - /** - * Imports - */ - - /** - * A class that can be used to perform automated user interface tests. - * - * @example - * // Create the UITest object - * const test = new UITest({ - * timeScale: 2 - * }) - * - * // Add an action to the test case - * test.tap(button, {eventType: 'click'}) - * - * // Start the test case - * test.start() - * - * @class - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/uitest.html|DocTest} - */ - class UITest { - - /** - * Creates an instance of an UITest. - * - * In the background, the class UITest uses the Greensock TimelineMax class. The opts object is passed directly to the TimelineMax class, so it can use any key that uses the TimelineMax class. - * - * @constructor - * @param {object} [opts] - An options object to specify the behaviour of the test case. - * @param {number} [opts.timeScale=1] - The speed at which the test should run, see https://greensock.com/docs/TimelineMax/timeScale(). - * @param {string} [opts.eventType=auto] - The type of events which should be used. Possible values: pointer, touch, mouse, auto. If set to auto, the eventType is set depending on the support of the browser used. - * @param {boolean} [opts.debug=false] - If set to true, multiple informations will be print to the console. - * @param {number} [opts.defaultInterval] - The interval used when no action is specified for an action. - */ - constructor(opts = {}) { - - this.opts = Object.assign({}, { - timeScale: 1, - eventType: 'auto', - debug: false, - defaultInterval: null - }, opts); - - // timeline - //-------------------- - this._timeline = new TimelineMax(Object.assign({}, { - paused: true - }, this.opts)); - this._timeline.timeScale(this.opts.timeScale); - - // eventType - //-------------------- - if (this.opts.eventType === 'auto') { - if (window.PointerEvent) { - this.opts.eventType = 'pointer'; - } else if ('ontouchstart' in window) { - this.opts.eventType = 'touch'; - } else { - this.opts.eventType = 'mouse'; - } - } - - if (this.opts.debug) { - console.log(`Event type: ${this.opts.eventType}`); - } - - this._timelinePositions = [0]; - this._actions = 0; - - // setup - //----------------- - this.setup(); - } - - /** - * Generates the required structure. - * - * @private - * @return {UITest} A reference to the UITest for chaining. - */ - setup() { - return this - } - - /** - * Gets the Greensock TimelineMax object, used in the background of UITest. - * - * @member {TimelineMax} - */ - get timeline() { - return this._timeline - } - - /** - * Starts the test case and executes the corresponding statements in the specified order. - * - * @return {UITest} A reference to the UITest for chaining. - */ - start() { - this._timeline.play(); - return this - } - - /** - * Stops the test case and stops executing any further instructions. - * - * @return {UITest} A reference to the UITest for chaining. - */ - stop() { - this._timeline.pause(); - return this - } - - /** - * Clears all instructions of the test case. - * - * @return {UITest} A reference to the UITest for chaining. - */ - clear() { - this._timeline.clear(); - return this - } - - /** - * Restarts the test case. - * - * @return {UITest} A reference to the UITest for chaining. - */ - restart() { - this._timeline.restart(); - return this - } - - /** - * Executes a tap event (pointerdown, pointerup) on a specific element. - * - * @param {HTMLElement|string} element - The HTML element on which the event is to be executed, e.g. button, document, h2, canvas, etc. or an selector string. If a selector has been specified, it is evaluated immediately before the event is called using the querySelector method. - * @param {number[]|object|PIXI.DisplayObject} [position=The center of the element.] - The local position of the event in the context of the specified HTML element. If no position is specified, the center of the HTML element is used. The position can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. - * @param {number} [timelinePosition=One second after the last action.] - The position in seconds when the event should be triggered, see shttps://greensock.com/docs/TimelineMax/addCallback(). - * @param {object} [opts] - An options object to specify the behaviour of the action. - * @param {function} [opts.onStart] - A function that runs after the first event is fired. Will not be fired if only one event is running (for example, a click event). Receives the fired event object as the first parameter. The test case (UITest) is bound to this. - * @param {function} [opts.onComplete] - A function that runs after the second event is fired. Always fired, even if only one event is running (for example, a click event). Receives the fired event object as the first parameter. The test case (UITest) is bound to this. - * @param {string[]} [opts.eventTypes=['pointerdown', 'pointerup']] - The event types to use. If no types are specified, the event types specified in the UITest constructor are used (or auto if not specified). - * @param {string} [opts.eventType] - If you want the tap method to fire only one event (for example, a click event), you can specify the opts.eventType parameter. If eventType is not null, the parameter opts.eventTypes is ignored. - * @param {Window|Frame} [opts.context=window] - The context within which the optionally specified element selector should be executed. - * @param {boolean} [opts.bubbles=true] - The Event property bubbles indicates whether the event bubbles up through the DOM or not. - * @param {boolean} [opts.cancelable=true] - Events' cancelable property indicates if the event can be canceled, and therefore prevented as if the event never happened. If the event is not cancelable, then its cancelable property will be false and the event listener cannot stop the event from occurring. - */ - tap(element, position, timelinePosition, opts = {}) { - - // arguments - //-------------------- - [position, timelinePosition, opts] = this.reorderArguments(arguments); - this._timelinePositions.push(timelinePosition); - - // debug - //-------------------- - if (this.opts.debug) console.log('tap params', {element, position, timelinePosition, opts}); - - // opts - //-------------------- - opts = Object.assign({}, { - onStart: null, - onComplete: null, - eventTypes: this.resolveEvents(['down', 'up']), - eventType: null, - context: window, - bubbles: true, - cancelable: true - }, opts); - - if (opts.eventType) { - opts.eventTypes = opts.eventType; - } - opts.eventTypes = Array.isArray(opts.eventTypes) ? opts.eventTypes : [opts.eventTypes]; - - // timeline - //-------------------- - this._timeline.addCallback(position => { - - // element - //-------------------- - const elem = Util.extractElement(opts.context, element); - - // position - //-------------------- - if (position === null) { - const rect = elem.getBoundingClientRect(); - position = [rect.width / 2, rect.height / 2]; - } - - // coords - //-------------------- - const coords = Util.extractPosition(position); - if (this.opts.debug) console.log('local coords', coords); - - // eventTypes - //-------------------- - if (opts.eventTypes.length === 1) { - opts.eventTypes.unshift(null); - } - - // event opts - //-------------------- - const eventOpts = {bubbles: opts.bubbles, cancelable: opts.cancelable}; - - if (opts.eventTypes[0]) { - - // create and dispatch event - //-------------------- - const eventStart = Event.create(elem, coords, opts.eventTypes[0], eventOpts); - if (this.opts.debug) console.log('dispatch event', eventStart); - elem.dispatchEvent(eventStart); - - // onStart - //-------------------- - if (opts.onStart) { - opts.onStart.call(this, eventStart); - } - } - - // create and dispatch event - //-------------------- - const eventComplete = Event.create(elem, coords, opts.eventTypes[1], eventOpts); - if (this.opts.debug) console.log('dispatch event', eventComplete); - elem.dispatchEvent(eventComplete); - - // onComplete - //-------------------- - if (opts.onComplete) { - opts.onComplete.call(this, eventComplete); - } - - }, timelinePosition, [position]); - - this._actions++; - - return this - } - - /** - * Executes a pan event (pointerdown, pointermove, pointerup) on a specific element. - * - * @param {HTMLElement|string} element - The HTML element on which the event is to be executed, e.g. button, document, h2, canvas, etc. or an selector string. If a selector has been specified, it is evaluated immediately before the event is called using the querySelector method. - * @param {number[]|object|PIXI.DisplayObject} [position=The center of the element.] - The local position of the event in the context of the specified HTML element. If no position is specified, the center of the HTML element is used. The position can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. - * @param {number} [timelinePosition=One second after the last action.] - The position in seconds when the event should be triggered, see shttps://greensock.com/docs/TimelineMax/addCallback(). - * @param {object} [opts] - An options object to specify the behaviour of the action. - * @param {function} [opts.onStart] - A function that runs after the first event is fired. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. - * @param {function} [opts.onUpdate] - A function that runs after each execution of the second event. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. - * @param {function} [opts.onComplete] - A function that runs after the third event is fired. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. - * @param {number[]|object|PIXI.DisplayObject} [opts.to={x: 0, y: 0}] - The target of the pan process. The position can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. - * @param {number} [opts.duration=1] - The duration of the pan animation in seconds, see https://greensock.com/docs/TweenLite/duration(). - * @param {Ease} [opts.ease=Power0.easeNone] - The easing of the pan animation, see https://greensock.com/docs/Easing. - * @param {string[]} [opts.eventTypes=['pointerdown', 'pointermove', 'pointerup']] - The event types to use. If no types are specified, the event types specified in the UITest constructor are used (or auto if not specified). - * @param {Window|Frame} [opts.context=window] - The context within which the optionally specified element selector should be executed. - * @param {boolean} [opts.bubbles=true] - The Event property bubbles indicates whether the event bubbles up through the DOM or not. - * @param {boolean} [opts.cancelable=true] - Events' cancelable property indicates if the event can be canceled, and therefore prevented as if the event never happened. If the event is not cancelable, then its cancelable property will be false and the event listener cannot stop the event from occurring. - */ - pan(element, position, timelinePosition, opts = {}) { - - // arguments - //-------------------- - [position, timelinePosition, opts] = this.reorderArguments(arguments); - this._timelinePositions.push(timelinePosition); - - // debug - //-------------------- - if (this.opts.debug) console.log('tap params', {element, position, timelinePosition, opts}); - - // opts - //-------------------- - opts = Object.assign({}, { - onStart: null, - onUpdate: null, - onComplete: null, - to: {x: 0, y: 0}, - duration: 1, - ease: Power0.easeNone, - eventTypes: this.resolveEvents(['down', 'move', 'up']), - context: window, - bubbles: true, - cancelable: true - }, opts); - - // timeline - //-------------------- - this._timeline.addCallback(position => { - - // element - //-------------------- - const elem = Util.extractElement(opts.context, element); - - // coords - //-------------------- - const from = Util.extractPosition(position); - - // event opts - //-------------------- - const eventOpts = {bubbles: opts.bubbles, cancelable: opts.cancelable}; - - const gsOpts = { - ease: opts.ease, - onStart: () => { - - // create and dispatch event - //-------------------- - const event = Event.create(elem, from, opts.eventTypes[0], eventOpts); - if (this.opts.debug) console.log('dispatch event', event); - elem.dispatchEvent(event); - - // onStart - //-------------------- - if (opts.onStart) { - opts.onStart.call(this, event); - } - }, - onUpdate: () => { - - // create and dispatch event - //-------------------- - const event = Event.create(elem, from, opts.eventTypes[1], eventOpts); - if (this.opts.debug) console.log('dispatch event', event); - elem.dispatchEvent(event); - - // onUpdate - //-------------------- - if (opts.onUpdate) { - opts.onUpdate.call(this, event); - } - }, - onComplete: () => { - - // create and dispatch event - //-------------------- - const event = Event.create(elem, from, opts.eventTypes[2], eventOpts); - if (this.opts.debug) console.log('dispatch event', event); - elem.dispatchEvent(event); - - // onComplete - //-------------------- - if (opts.onComplete) { - opts.onComplete.call(this, event); - } - } - }; - - // to - //-------------------- - const object = Util.extractTo(opts); - Object.assign(gsOpts, object); - - // drag animation - //-------------------- - TweenMax.to(from, opts.duration, gsOpts); - - }, timelinePosition, [position]); - - this._actions++; - - return this - } - - /** - * Executes a pinch event (pointerdown, pointermove, pointerup) on a specific element with two "fingers" simultaneously. - * - * @param {HTMLElement|string} element - The HTML element on which the event is to be executed, e.g. button, document, h2, canvas, etc. or an selector string. If a selector has been specified, it is evaluated immediately before the event is called using the querySelector method. - * @param {number[]|object|PIXI.DisplayObject} [position=The center of the element.] - The local position of the event in the context of the specified HTML element. If no position is specified, the center of the HTML element is used. The position can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. - * @param {number} [timelinePosition=One second after the last action.] - The position in seconds when the event should be triggered, see shttps://greensock.com/docs/TimelineMax/addCallback(). - * @param {object} [opts] - An options object to specify the behaviour of the action. - * @param {function} [opts.onStart] - A function that runs after the first events are fired. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. - * @param {function} [opts.onUpdate] - A function that runs after each execution of the second events. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. - * @param {function} [opts.onComplete] - A function that runs after the third events are fired. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. - * @param {boolean} [opts.doubleCallbacks=false] - The callbacks onStart, onUpdate and onComplete will be fired only for one finger. If set to true, the events will be fired for both fingers. - * @param {number} [opts.distance=100] - The distance in pixels, how far the two "fingers" should move apart. If to or bezier specified, distance is ignored. - * @param {number[][]|object[]|PIXI.DisplayObject[]} [opts.to] - The targets of the pinch process. The position must be an array with two entries. An entry can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. If bezier is specified, to is ignored. - * @param {number[][]|object[]|PIXI.DisplayObject[]} [opts.bezier] - The targets of the pinch process. The position must be an array with two entries. An entry may be an array of positions or a bezier object (https://greensock.com/docs/Plugins/BezierPlugin). A position in the array or the values array of the bezier object can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. If bezier is specified, to is ignored. - * @param {number} [opts.duration=1] - The duration of the pan animation in seconds, see https://greensock.com/docs/TweenLite/duration(). - * @param {Ease} [opts.ease=Power0.easeNone] - The easing of the pan animation, see https://greensock.com/docs/Easing. - * @param {string[]} [opts.eventTypes=['pointerdown', 'pointermove', 'pointerup']] - The event types to use. If no types are specified, the event types specified in the UITest constructor are used (or auto if not specified). - * @param {Window|Frame} [opts.context=window] - The context within which the optionally specified element selector should be executed. - * @param {boolean} [opts.bubbles=true] - The Event property bubbles indicates whether the event bubbles up through the DOM or not. - * @param {boolean} [opts.cancelable=true] - Events' cancelable property indicates if the event can be canceled, and therefore prevented as if the event never happened. If the event is not cancelable, then its cancelable property will be false and the event listener cannot stop the event from occurring. - */ - pinch(element, position, timelinePosition, opts = {}) { - - // arguments - //-------------------- - [position, timelinePosition, opts] = this.reorderArguments(arguments); - this._timelinePositions.push(timelinePosition); - - // debug - //-------------------- - if (this.opts.debug) console.log('tap params', {element, position, timelinePosition, opts}); - - // opts - //-------------------- - opts = Object.assign({}, { - onStart: null, - onUpdate: null, - onComplete: null, - doubleCallbacks: false, - duration: 1, - distance: 100, - to: null, - bezier: null, - ease: Power0.easeNone, - eventTypes: this.resolveEvents(['down', 'move', 'up']), - context: window, - bubbles: true, - cancelable: true - }, opts); - - // timeline - //-------------------- - this._timeline.addCallback(position => { - - // element - //-------------------- - const elem = Util.extractElement(opts.context, element); - - // from - //-------------------- - let from1 = null; - let from2 = null; - - if (Array.isArray(position) && !Util.isNumber(position[0])) { - from1 = Util.extractPosition(position[0]); - from2 = Util.extractPosition(position[1]); - } else { - from1 = Util.extractPosition(position); - from2 = {x: from1.x, y: from1.y}; - } - - // to - //-------------------- - let gsOpts1 = {}; - let gsOpts2 = {}; - - if (opts.to || opts.bezier) { - [gsOpts1, gsOpts2] = Util.extractMultiTo(opts); - } else { - const distance = opts.distance != null ? opts.distance : 100; - gsOpts1.x = from1.x - distance / 2; - gsOpts1.y = from1.y; - gsOpts2.x = from2.x + distance / 2; - gsOpts2.y = from2.y; - } - - // pointers - //-------------------- - const pointers = new Map(); - pointers.set(0, {element: from1, gsOpts: gsOpts1}); - pointers.set(1, {element: from2, gsOpts: gsOpts2}); - - // loop - //-------------------- - pointers.forEach((value, key) => { - - // from - //-------------------- - const from = value.element; - - // event opts - //-------------------- - const eventOpts = {bubbles: opts.bubbles, cancelable: opts.cancelable, pointerId: key, isPrimary: key === 0}; - - const gsOpts = { - ease: opts.ease, - onStart: () => { - - // create and dispatch event - //-------------------- - const event = Event.create(elem, from, opts.eventTypes[0], eventOpts); - if (this.opts.debug) console.log('dispatch event', event); - elem.dispatchEvent(event); - - // onStart - //-------------------- - if (opts.onStart && (opts.doubleCallbacks || key === 0)) { - opts.onStart.call(this, event); - } - }, - onUpdate: () => { - - // create and dispatch event - //-------------------- - const event = Event.create(elem, from, opts.eventTypes[1], eventOpts); - if (this.opts.debug) console.log('dispatch event', event); - elem.dispatchEvent(event); - - // onUpdate - //-------------------- - if (opts.onUpdate && (opts.doubleCallbacks || key === 0)) { - opts.onUpdate.call(this, event); - } - }, - onComplete: () => { - - // create and dispatch event - //-------------------- - const event = Event.create(elem, from, opts.eventTypes[2], eventOpts); - if (this.opts.debug) console.log('dispatch event', event); - elem.dispatchEvent(event); - - // onComplete - //-------------------- - if (opts.onComplete && (opts.doubleCallbacks || key === 0)) { - opts.onComplete.call(this, event); - } - } - }; - - // to - //-------------------- - Object.assign(gsOpts, value.gsOpts); - - // drag animation - //-------------------- - TweenMax.to(from, opts.duration, gsOpts); - }); - - }, timelinePosition, [position]); - - this._actions++; - - return this - } - - // /** - // * Adds a tap event to the timeline. - // * - // * @return {UITest} A reference to the uitest for chaining. - // */ - // rotate() { - // return this - // } - - // /** - // * Adds a tap event to the timeline. - // * - // * @return {UITest} A reference to the uitest for chaining. - // */ - // swipe() { - // return this - // } - - // /** - // * Adds a tap event to the timeline. - // * - // * @return {UITest} A reference to the uitest for chaining. - // */ - // press() { - // return this - // } - - // /** - // * Adds a tap event to the timeline. - // * - // * @return {UITest} A reference to the uitest for chaining. - // */ - // event() { - // return this - // } - - /** - * Sorts the parameters so that the second, third, and fourth parameters can be optional (and possibly slip forward). - * - * @private - * @param {arguments} params - The arguments which were passed to the function. - * @returns {array} - Returns an array of the position, the timelinePosition and the opts object. - */ - reorderArguments(params) { - - // first parameter - //-------------------- - const element = params[0]; - - // other parameter - //-------------------- - let position = null; - let timelinePosition = null; - let opts = null; - - // second parameter - //-------------------- - if (Util.isNumber(params[1])) { - timelinePosition = params[1]; - } else if (Util.isObject(params[1]) && !Util.isPixiDisplayObject(params[1]) && (params[1].x == null || params[1].y == null)) { - opts = params[1]; - } else if (params[1] != null) { - position = params[1]; - } - - // third parameter - //-------------------- - if (Util.isNumber(params[2])) { - timelinePosition = params[2]; - } else if (Util.isObject(params[2])) { - opts = params[2]; - } - - // fourth parameter - //-------------------- - if (Util.isObject(params[3])) { - opts = params[3]; - } - - if (timelinePosition === null) { - if (this.opts.defaultInterval === null && this._actions > 1) { - throw new Error('No execution time was specified for this action, and a default interval was not set in the class constructor!') - } - timelinePosition = Math.max(...this._timelinePositions) + (this.opts.defaultInterval || 1); - } - - if (opts === null) { - opts = {}; - } - - return [position, timelinePosition, opts] - } - - /** - * Converts event type shortcuts to real event names. - * - * @private - * @param {string[]} events - An array of event types. - */ - resolveEvents(events) { - - const data = []; - - if (this.opts.eventType === 'pointer') { - events.forEach(it => { - if (it === 'down') { - data.push('pointerdown'); - } else if (it === 'move') { - data.push('pointermove'); - } else if (it === 'up') { - data.push('pointerup'); - } - }); - } else if (this.opts.eventType === 'touch') { - events.forEach(it => { - if (it === 'down') { - data.push('touchstart'); - } else if (it === 'move') { - data.push('touchmove'); - } else if (it === 'up') { - data.push('touchend'); - } - }); - } else { - events.forEach(it => { - if (it === 'down') { - data.push('mousedown'); - } else if (it === 'move') { - data.push('mousemove'); - } else if (it === 'up') { - data.push('mouseup'); - } - }); - } - - return data - } - } - - /** - * Helper class. - * - * @example - * // Checks if a thing is a number. - * const num = Util.isNumber(20) - * - * @private - * @ignore - * @class - */ - class Util { - - /** - * Resolves the element from a specific context. - * - * @static - * @param {Window|Frame} context - The context within which the optionally specified element selector should be executed. - * @return {HTMLElement|string} element - The HTML element on which the event is to be executed, e.g. button, document, h2, canvas, etc. or an selector string. If a selector has been specified, it is evaluated immediately before the event is called using the querySelector method. - */ - static extractElement(context, element) { - - const cont = Util.isFrame(context) ? context.contentDocument : context.document; - const elem = Util.isString(element) ? cont.querySelector(element) : element; - - return elem - } - - /** - * Extracts the position of the second parameter. - * - * @static - * @param {object} object - Something were the coords should be extracted. - * @return {object} - Returns an object with the keys x and y. - */ - static extractPosition(object) { - - // event coords - //-------------------- - const position = {x: 0, y: 0}; - - // get the position - //-------------------- - if (!object) { - position.x = 0; - position.y = 0; - } else if (typeof object.getBounds === 'function') { - const bounds = object.getBounds(); - position.x = bounds.x + bounds.width / 2; - position.y = bounds.y + bounds.height / 2; - } else if (Array.isArray(object)) { - position.x = object[0]; - position.y = object[1]; - } else if (object.x != null && object.y != null) { - position.x = object.x; - position.y = object.y; - } - - return position - } - - /** - * Extracts the to or bezier key. - * - * @static - * @param {object} opts - An options object where to or bezier should be extracted. - * @return {object} - Returns an object with the to or bezier keys. - */ - static extractTo(opts) { - - const object = {}; - - if (opts.bezier) { - - let bezier = null; - - if (Array.isArray(opts.bezier)) { - bezier = { - values: opts.bezier.map(it => Util.extractPosition(it)), - type: 'thru' - }; - } else { - opts.bezier.values = opts.bezier.values.map(it => Util.extractPosition(it)); - bezier = opts.bezier; - } - - object.bezier = bezier; - } else { - const to = Util.extractPosition(opts.to); - object.x = to.x; - object.y = to.y; - } - - return object - } - - /** - * Extracts multiple to or bezier keys. - * - * @static - * @param {object} opts - An options object where to or bezier should be extracted. - * @return {object[]} - Returns an array of objects with the keys x and y. - */ - static extractMultiTo(opts) { - - const objects = []; - - if (opts.bezier) { - - opts.bezier.forEach(it => { - - let bezier = null; - - if (Array.isArray(it)) { - bezier = { - values: it.map(it => Util.extractPosition(it)), - type: 'thru' - }; - } else { - it.values = it.values.map(it => Util.extractPosition(it)); - bezier = it; - } - - objects.push({ - bezier - }); - }); - - } else { - - opts.to.forEach(it => { - const to = Util.extractPosition(it); - objects.push({ - x: to.x, - y: to.y - }); - }); - } - - return objects - } - - /** - * Checks if a thing is a string. - * - * @static - * @param {object} object - The object to test for. - * @return {boolean} - true if the thing is a string, otherwise false. - */ - static isString(object) { - return typeof object === 'string' - } - - /** - * Checks if a thing is a number. - * - * @static - * @param {object} object - The object to test for. - * @return {boolean} - true if the thing is a number, otherwise false. - */ - static isNumber(object) { - return typeof object === 'number' - } - - /** - * Checks if a thing is an object. - * - * @static - * @param {object} object - The object to test for. - * @return {boolean} - true if the thing is an object, otherwise false. - */ - static isObject(object) { - return typeof object === 'object' && !Array.isArray(object) - } - - /** - * Checks if a thing is an PIXI.DisplayObject. - * - * @static - * @param {object} object - The object to test for. - * @return {boolean} - true if the thing is a PIXI.DisplayObject, otherwise false. - */ - static isPixiDisplayObject(object) { - return typeof object.getBounds === 'function' && typeof object.renderWebGL === 'function' && typeof object.setTransform === 'function' - } - - /** - * Checks if a thing is a frame. - * - * @static - * @param {object} object - The object to test for. - * @return {boolean} - true if the thing is a frame, otherwise false. - */ - static isFrame(object) { - return object.contentDocument != null - } - } - - /** - * Event helper class. - * - * @example - * // Creates an event object. - * const event = Event.create(h2, {x: 5, y: 10}, 'touchstart') - * - * @private - * @ignore - * @class - */ - class Event { - - /** - * Creates an event object. - * - * @static - * @param {HTMLElement} target - The element on which the event should be executed. - * @param {object} position - The local position of the event in relation to the target. The object must have the keys x and y. - * @param {string} type - The type of the event, see https://developer.mozilla.org/de/docs/Web/Events - * @param {object} opts - An options object. Every paramter of the event object can be overridden, see e.g. https://developer.mozilla.org/de/docs/Web/API/MouseEvent for all the properties. - */ - static create(target, position = {x: 0, y: 0}, type = 'pointerup', opts = {}) { - - const rect = typeof target.getBoundingClientRect === 'function' ? target.getBoundingClientRect() : {x: 0, y: 0}; - - // EventInit - const eventOpts = { - bubbles: true, - cancelable: true, - composed: false - }; - - // UIEventInit - const uiEventOpts = { - detail: 0, - view: window - }; - - // MouseEvent - const mouseEventOpts = { - screenX: window.screenX + target.offsetLeft + position.x, - screenY: window.screenY + target.offsetTop + position.y, - clientX: rect.x + position.x, - clientY: rect.y + position.y, - ctrlKey: false, - shiftKey: false, - altKey: false, - metaKey: false, - button: 0, - buttons: 1, - relatedTarget: null, - region: null - }; - - // TouchEvent - const touchEventOpts = { - touches: [], - targetTouches: [], - changedTouches: [], - ctrlKey: false, - shiftKey: false, - altKey: false, - metaKey: false - }; - - // PointerEvent - const pointerEventOpts = { - pointerId: 0, - width: 1, - height: 1, - pressure: 0, - tangentialPressure: 0, - tiltX: 0, - tiltY: 0, - twist: 0, - pointerType: 'touch', - isPrimary: true - }; - - if (type.startsWith('pointer')) { - return new PointerEvent(type, Object.assign({}, eventOpts, uiEventOpts, mouseEventOpts, pointerEventOpts, opts)) - } else if (type.startsWith('touch')) { - return new TouchEvent(type, Object.assign({}, eventOpts, uiEventOpts, touchEventOpts, opts)) - } else { - return new MouseEvent(type, Object.assign({}, eventOpts, uiEventOpts, mouseEventOpts, opts)) - } - } - } - - /* Needed to ensure that rollup.js includes class definitions and the classes - are visible inside doctests. - */ - - window.AbstractScatter = AbstractScatter; - window.Angle = Angle; - window.App = App; - window.BaseEvent = BaseEvent; - window.Capabilities = Capabilities; - window.CapabilitiesTests = CapabilitiesTests; - window.Colors = Colors; - window.Cycle = Cycle; - - window.DOMFlip = DOMFlip; - window.DOMFlippable = DOMFlippable; - window.CardLoader = CardLoader; - window.PDFLoader = PDFLoader; - window.HTMLLoader = HTMLLoader; - window.ImageLoader = ImageLoader; - window.FrameLoader = FrameLoader; - - window.DOMScatter = DOMScatter; - window.DOMScatterContainer = DOMScatterContainer; - window.Dates = Dates; - window.Doctest = Doctest; - window.Elements = Elements$1; - window.Errors = Errors; - window.EventRecorder = EventRecorder; - window.Events = Events; - window.FrameContainer = FrameContainer; - window.FrameTarget = FrameTarget; - window.IApp = IApp; - window.IInteractionMapperTarget = IInteractionMapperTarget; - window.IInteractionTarget = IInteractionTarget; - window.Index = Index; - window.Inspect = Inspect; - window.Interaction = Interaction; - window.InteractionDelegate = InteractionDelegate; - window.InteractionDelta = InteractionDelta; - window.InteractionMapper = InteractionMapper$1; - window.InteractionPoints = InteractionPoints; - window.Interface = Interface; - window.PointMap = PointMap; - window.Rect = Rect; - window.Points = Points; - window.Polygon = Polygon; - window.Poppable = Poppable; - window.Popup = Popup; - window.PopupMenu = PopupMenu$1; - window.ResizeEvent = ResizeEvent; - window.ScatterEvent = ScatterEvent; - window.Sets = Sets; - window.Strings = Strings; - window.UITest = UITest; - window.getId = getId$1; - window.isEmpty = isEmpty; - window.lerp = lerp; - window.debounce = debounce; - window.randomInt = randomInt; - window.randomFloat = randomFloat; - -}()); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxsLmpzIiwic291cmNlcyI6WyJpbnRlcmZhY2UuanMiLCJhcHAuanMiLCJkb2N0ZXN0LmpzIiwiZXJyb3JzLmpzIiwiZXZlbnRzLmpzIiwidXRpbHMuanMiLCJpbnRlcmFjdGlvbi5qcyIsImNhcGFiaWxpdGllcy5qcyIsInNjYXR0ZXIuanMiLCJmbGlwcGFibGUuanMiLCJpbmRleC5qcyIsInBvcHBhYmxlLmpzIiwicG9wdXAuanMiLCJwb3B1cG1lbnUuanMiLCJmcmFtZXMuanMiLCJpbnNwZWN0LmpzIiwidWl0ZXN0LmpzIiwiYnVuZGxlLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIEluIG9yZGVyIHRvIHRlc3QgdGhpcyBpbnRlcmZhY2UgaW1wbGVtZW50YXRpb24gcnVuIGpzYyBpbnRlcmZhY2UuanNcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgSW50ZXJmYWNlIHtcbiAgICAvLyBBYnN0cmFjdCBpbnRlcmZhY2UgdGhhdCBzaG91bGQgYmUgZXh0ZW5kZWQgaW4gaW50ZXJmYWNlIHN1YmNsYXNzZXMuXG4gICAgLy8gQnkgY29udmVudGlvbiBhbGwgaW50ZXJmYWNlcyBzaG91bGQgc3RhcnQgd2l0aCBhbiB1cHBlciAnSSdcblxuICAgIHN0YXRpYyBpbXBsZW1lbnRhdGlvbkVycm9yKGtsYXNzKSB7XG4gICAgICAgIGxldCBpbnRlcmZhY2VLZXlzID0gUmVmbGVjdC5vd25LZXlzKHRoaXMucHJvdG90eXBlKVxuICAgICAgICBsZXQgY2xhc3NLZXlzID0gUmVmbGVjdC5vd25LZXlzKGtsYXNzLnByb3RvdHlwZSlcbiAgICAgICAgZm9yKGxldCBrZXkgb2YgaW50ZXJmYWNlS2V5cykge1xuICAgICAgICAgICAgbGV0IGludGVyZmFjZURlc2MgPSB0aGlzLnByb3RvdHlwZVtrZXldXG4gICAgICAgICAgICBsZXQgY2xhc3NEZXNjID0ga2xhc3MucHJvdG90eXBlW2tleV1cbiAgICAgICAgICAgIGlmICh0eXBlb2YoY2xhc3NEZXNjKSA9PSAndW5kZWZpbmVkJylcbiAgICAgICAgICAgICAgICByZXR1cm4gJ01pc3NpbmcgJyArIGtleVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgfVxuXG4gICAgc3RhdGljIGltcGxlbWVudGVkQnkoa2xhc3MpIHtcbiAgICAgICAgLy8gSW4gdGhlIGZpcnN0IHN0ZXAgb25seSBjaGVja3Mgd2hldGhlciB0aGUgbWV0aG9kcyBvZiB0aGlzXG4gICAgICAgIC8vIGludGVyZmFjZSBhcmUgYWxsIGltcGxlbWVudGVkIGJ5IHRoZSBnaXZlbiBjbGFzc1xuICAgICAgICBsZXQgZXJyb3IgPSB0aGlzLmltcGxlbWVudGF0aW9uRXJyb3Ioa2xhc3MpXG4gICAgICAgIHJldHVybiBlcnJvciA9PSBudWxsXG4gICAgfVxuXG4gICAgICAgIC8vIFRPRE86IFNwZWNpZnkgb3B0aW9uYWwgbWV0aG9kc1xuLy8gICAgIHN0YXRpYyBvcHRpb25hbE1ldGhvZHMoKSB7XG4vLyAgICAgICAgIHJldHVybiBbdGhpcy5vbk1vdXNlV2hlZWxdXG4vLyAgICAgfVxufVxuIiwiaW1wb3J0IEludGVyZmFjZSBmcm9tICcuL2ludGVyZmFjZS5qcydcblxuLyoqIEJhc2ljIEFwcGxpY2F0aW9uIG9iamVjdCB0byBiZSB1c2VkIGFzIGEgc2luZ2xldG9uLlxuICAgIFByb3ZpZGVzIGFuIGludGVyZmFjZSBmb3IgYXV0b21hdGljIHRlc3RpbmcgYW5kIGNvbW1vbiBkZXZpY2Ugc3BlY2lmaWNcbiAgICBmZWF0dXJlIGRldGVjdGlvbi5cbiovXG5cbmV4cG9ydCBjbGFzcyBJQXBwIGV4dGVuZHMgSW50ZXJmYWNlIHtcbiAgICAvKiogQnVpbGQgdGhlIGFwcCBieSByZWdpc3RlcmluZyBldmVudCBoYW5kbGVycyxcbiAgICAgKiAgIGFkZGluZyBET00gZWxlbWVudHMsIGluc3RhbmNpYXRpbmcgdGVtcGxhdGVzLCBldGMuLi5cbiAgICAgKi9cbiAgICBzZXR1cCgpIHsgcmV0dXJuIHRoaXMgfVxuXG4gICAgLyoqIFJ1biB0aGUgYXBwbGljYXRpb24gYnkgc3RhcnRpbmcgYSBtYWluIGxvb3AsIC4uLlxuICAgICAqL1xuICAgIHJ1bigpIHsgcmV0dXJuIHRoaXMgfVxufVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBBcHAgZXh0ZW5kcyBPYmplY3Qge1xuICAgIC8qKiBPdmVycmlkZSB0aGlzIG1ldGhvZCB0byBidWlsZCB5b3VyIGFwcC5cbiAgICAqL1xuICAgIHNldHVwKCkge1xuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKiBTdGFydCBhbmQgcnVuIHRoZSBhcHBsaWNhdGlvbi4gT3ZlcnJpZGUgdGhpcyBtZXRob2Qgd2l0aCBldmVyeXRoaW5nXG4gICAgdGhhdCBpcyBuZWVkZWQgdG8gbWFpbnRhaW4geW91ciBBcHAsIG1haW4gbG9vcHMsIGV0Yy5cbiAgICAqL1xuICAgIHJ1bigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKiogRGVmaW5lcyBhbGwgdGVzdCBzdWl0ZXMuIE92ZXJ3cml0ZSB0aGlzIG1ldGhvZCB0byBlbnN1cmUgdGhhdFxuICAgIGFsbCB0ZXN0YWJsZSBhc3BlY3RzIG9mIHlvdXIgQXBwIGFyZSBldmFsdWF0ZWQuXG4gICAgKi9cbiAgICBhbGxUZXN0cygpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ092ZXJ3cml0ZSBBcHAuYWxsVGVzdHMoKScpXG4gICAgfVxuXG4gICAgLyoqIFJ1biBhbGwgdGVzdHMuIFNob3VsZCByZXR1cm4gJ29rJyBhbmQgdGhlIGFtb3VudCBvZiB0aW1lIG5lZWRlZCB0b1xuICAgIHJ1biBBcHAuYWxsVGVzdHMoKSBvciBhIGZhaWx1cmUgbWVzc2FnZSB3aXRoIGRpYWdub3N0aWMgZXJyb3IgZGVjcmlwdGlvbi5cbiAgICBAcmV0dXJuIHthcnJheX0gLSBhcnJheSB3aXRoICdvaycgYXMgZmlyc3QgZWxlbWVudCBhbmQgbmVlZGVkIHRpbWUgYXNcbiAgICAgICAgICAgICAgICAgICAgICBzZWNvbmQgZWxlbWVudCBvciBcIlRlc3RzIGZhaWxlZFwiIGFuZCBhbiBlcnJvciBzdHJpbmdcbiAgICAqL1xuICAgIHJ1blRlc3RzKCkge1xuICAgICAgICB2YXIgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKVxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgdGhpcy5hbGxUZXN0cygpXG4gICAgICAgICAgICB2YXIgZW5kID0gcGVyZm9ybWFuY2Uubm93KClcbiAgICAgICAgICAgIHJldHVybiBbJ29rJywgZW5kIC0gc3RhcnRdXG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2goZSkge1xuICAgICAgICAgICAgY29uc29sZS50cmFjZSgpXG4gICAgICAgICAgICByZXR1cm4gWydUZXN0cyBmYWlsZWQnLCBlLm1lc3NhZ2VdXG4gICAgICAgIH1cbiAgICB9XG59XG5cbklBcHAuaW1wbGVtZW50ZWRCeShBcHApXG4iLCIvLyBBbGxvd3MgYnJvd3NlcnMgdG8gcGVyZm9ybSBkb2N0ZXN0cy5cbi8vIFVzZXMgdGhlIGNvZGUgaGlnaGxpZ2h0IHBhY2thZ2UgZnJvbSBodHRwOi8vaGlnaGxpZ2h0anMucmVhZHRoZWRvY3MuaW9cbi8vIGlmIGF2YWlsYWJsZVxuXG52YXIgZG9jVGVzdExvZ01lc3NhZ2VzID0gW11cblxuQXJyYXkucHJvdG90eXBlLmVxdWFscyA9IGZ1bmN0aW9uKGFycmF5KSB7XG4gICAgcmV0dXJuIHRoaXMubGVuZ3RoID09IGFycmF5Lmxlbmd0aCAmJlxuICAgICAgICAgdGhpcy5ldmVyeSggZnVuY3Rpb24odGhpc19pLGkpIHsgcmV0dXJuIHRoaXNfaSA9PSBhcnJheVtpXSB9IClcbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRG9jdGVzdCB7XG5cbiAgICBzdGF0aWMgYXNzZXJ0KHZhbHVlKSB7XG4gICAgICAgIGlmICghdmFsdWUpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQXNzZXJ0aW9uIHZpb2xhdGVkJylcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyBwcHJpbnQob2JqKSB7XG4gICAgICAgIGlmIChvYmogPT09IG51bGwpXG4gICAgICAgICAgICByZXR1cm4gJ251bGwnXG4gICAgICAgIGxldCBzdHJpbmdpZmllZCA9IG9iai50b1N0cmluZygpXG4gICAgICAgIGlmIChzdHJpbmdpZmllZCA9PSAnW29iamVjdCBPYmplY3RdJylcbiAgICAgICAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShvYmopXG4gICAgICAgIHJldHVybiBzdHJpbmdpZmllZFxuICAgIH1cblxuICAgIHN0YXRpYyBleHBlY3QoZXhwciwgdmFsdWUpIHtcbiAgICAgICAgaWYgKHRoaXMucHByaW50KGV4cHIpICE9IHRoaXMucHByaW50KHZhbHVlKSkge1xuICAgICAgICAgICAgLy90aHJvdyBuZXcgRXJyb3IoXCJnb3QgYFwiICsgZXhwciArIFwiYCBidXQgZXhwZWN0ZWQgYFwiICsgdmFsdWUgKyBcImAuXCIpXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2dvdCBgJyArIHRoaXMucHByaW50KGV4cHIpICsgJ2AgYnV0IGV4cGVjdGVkIGAnICsgdGhpcy5wcHJpbnQodmFsdWUpICsgJ2AuJylcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyBleHBlY3RFcnJvcihlcnJvciwgbWVzc2FnZSkge1xuICAgICAgICBsZXQgaW5kZXggPSBlcnJvci50b1N0cmluZygpLmluZGV4T2YobWVzc2FnZSlcbiAgICAgICAgaWYgKGluZGV4IDwgMCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdnb3QgYCcgKyBtZXNzYWdlICsgJ2AgYnV0IGV4cGVjdGVkIGAnICsgZXJyb3IgKyAnYC4nKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIGV4cGVjdExvZyguLi5tZXNzYWdlcykge1xuICAgICAgIC8vIGlmICghZG9jVGVzdExvZ01lc3NhZ2VzLmVxdWFscyhtZXNzYWdlcykpIHtcbiAgICAgICAgICAgIGRvY1Rlc3RMb2dNZXNzYWdlcy5mb3JFYWNoKChtc2csIGkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAobXNnICE9IG1lc3NhZ2VzW2ldKVxuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VuZXhwZWN0ZWQgbG9nIG1lc3NhZ2U6IGAnICsgbWVzc2FnZXNbaV0gKyAnYC4nKVxuICAgICAgICAgICAgfSlcbiAgICAgICAgLy8gICAgdGhyb3cgbmV3IEVycm9yKCdVdXBzJylcbiAgICAgICAgLy99XG4gICAgfVxuXG4gICAgc3RhdGljIGxvZyhtZXNzYWdlKSB7XG4gICAgICAgIGRvY1Rlc3RMb2dNZXNzYWdlcy5wdXNoKG1lc3NhZ2UpXG4gICAgfVxuXG4gICAgc3RhdGljIGhpZ2hsaWdodChjb2RlKSB7XG4gICAgICAgIGlmICh0eXBlb2YoaGxqcykgPT0gJ3VuZGVmaW5lZCcpXG4gICAgICAgICAgICByZXR1cm4gY29kZVxuICAgICAgICByZXR1cm4gaGxqcy5oaWdobGlnaHQoJ2phdmFzY3JpcHQnLCBjb2RlKVxuICAgIH1cblxuICAgIHN0YXRpYyBzdHJpcExlYWRpbmdMaW5lcyhjb2RlKSB7XG4gICAgICAgIGxldCByZXN1bHQgPSBbXVxuICAgICAgICBsZXQgaW5mb3JtYXRpdmUgPSBmYWxzZVxuICAgICAgICBmb3IobGV0IGxpbmUgb2YgY29kZS5zcGxpdCgnXFxuJykpIHtcbiAgICAgICAgICAgIGlmIChsaW5lLnRyaW0oKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgaW5mb3JtYXRpdmUgPSB0cnVlXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoaW5mb3JtYXRpdmUpXG4gICAgICAgICAgICAgICAgcmVzdWx0LnB1c2gobGluZSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0LmpvaW4oJ1xcbicpXG4gICAgfVxuXG4gICAgc3RhdGljIGV2ZW50KHR5cGU9J21vdXNlJywge2NsaWVudFggPSAwLCBjbGllbnRZID0gMH0gPSB7fSkge1xuICAgICAgICBpZiAodHlwZS5zdGFydHNXaXRoKCdtb3VzZScpKSB7XG4gICAgICAgICAgICByZXR1cm4gbmV3IE1vdXNlRXZlbnQodHlwZSwgeyBjbGllbnRYLCBjbGllbnRZIH0pXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHsgdHlwZSwgY2xpZW50WCwgY2xpZW50WSB9XG4gICAgfVxuXG4gICAgc3RhdGljIHJ1bihyZXBsYWNlRXhwZWN0PWZhbHNlKSB7XG4gICAgICAgIGlmICh0eXBlb2YoaGxqcykgIT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIGhsanMuaW5pdEhpZ2hsaWdodGluZygpXG4gICAgICAgIH1cbiAgICAgICAgbGV0IGRvY3Rlc3RzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmRvY3Rlc3QnKVxuICAgICAgICBmb3IobGV0IGk9MDsgaTxkb2N0ZXN0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgbGV0IGRvY3Rlc3QgPSBkb2N0ZXN0c1tpXVxuICAgICAgICAgICAgbGV0IGNvZGUgPSB0aGlzLnN0cmlwTGVhZGluZ0xpbmVzKGRvY3Rlc3QuaW5uZXJIVE1MKVxuICAgICAgICAgICAgbGV0IHRleHQgPSB0aGlzLmhpZ2hsaWdodChjb2RlKVxuICAgICAgICAgICAgbGV0IHByZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ByZScpXG4gICAgICAgICAgICAvLyBTZWUgaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMDY4MjgwL2phdmFzY3JpcHQtcmVnZXgtbXVsdGlsaW5lLWZsYWctZG9lc250LXdvcmtcbiAgICAgICAgICAgIC8vIGxldCByZSA9IC9Eb2N0ZXN0XFwuZXhwZWN0XFwoKFtcXHNcXFNdKilbXFwsXFxzXFxTXSooW1xcc1xcU10qKVxcKS9nXG4gICAgICAgICAgICBsZXQgbGluZXMgPSB0ZXh0LnZhbHVlLnNwbGl0KCdcXG4nKVxuICAgICAgICAgICAgbGV0IGJldHRlciA9IFtdXG4gICAgICAgICAgICBmb3IobGV0IGxpbmUgb2YgbGluZXMpIHtcbiAgICAgICAgICAgICAgICBpZiAocmVwbGFjZUV4cGVjdCAmJiBsaW5lLnRyaW0oKS5zdGFydHNXaXRoKCdEb2N0ZXN0LmV4cGVjdCgnKSkge1xuICAgICAgICAgICAgICAgICAgICBsaW5lID0gbGluZS5yZXBsYWNlKC9Eb2N0ZXN0XFwuZXhwZWN0XFwoLywgJz4+PiAnKS50cmltKClcbiAgICAgICAgICAgICAgICAgICAgaWYgKGxpbmUuZW5kc1dpdGgoJyknKSB8fCBsaW5lLmVuZHNXaXRoKCcsJykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaW5lLnNsaWNlKDAsIC0xKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJldHRlci5wdXNoKGxpbmUpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBwcmUuaW5uZXJIVE1MID0gYmV0dGVyLmpvaW4oJ1xcbicpIC8vIHRleHQudmFsdWUucmVwbGFjZShyZSwgXCI+Pj4gJDFcXG4kMlwiKVxuICAgICAgICAgICAgZG9jdGVzdC5wYXJlbnROb2RlLnJlcGxhY2VDaGlsZChwcmUsIGRvY3Rlc3QpXG4gICAgICAgIH1cbiAgICB9XG59XG5cbi8vIE5lZWRlZCB0byBtYWtlIERvY3Rlc3QgdmlzaWJsZSBpbiBtb2R1bGVzXG4vL3dpbmRvdy5Eb2N0ZXN0ID0gRG9jdGVzdFxuIiwidmFyIHJlY29yZGVkRXJyb3JzID0gbmV3IE1hcCgpXG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEVycm9ycyB7XG5cbiAgICBzdGF0aWMgY291bnRFcnJvcnMoKSB7XG4gICAgICAgIGxldCB0b3RhbCA9IDBcbiAgICAgICAgZm9yKGxldCBlcnJvciBvZiByZWNvcmRlZEVycm9ycy5rZXlzKCkpIHtcbiAgICAgICAgICAgIHRvdGFsICs9IHJlY29yZGVkRXJyb3JzLmdldChlcnJvcikuc2l6ZVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0b3RhbFxuICAgIH1cblxuICAgIHN0YXRpYyBzZXRTdHlsZShlbGVtZW50LCBzdHlsZXMpIHtcbiAgICAgICAgZm9yKGxldCBrZXkgaW4gc3R5bGVzKSB7XG4gICAgICAgICAgICBlbGVtZW50LnN0eWxlW2tleV0gPSBzdHlsZXNba2V5XVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIGFwcGVuZEVycm9yKGVycm9yLCBzb3VyY2UpIHtcbiAgICAgICAgaWYgKHJlY29yZGVkRXJyb3JzLmhhcyhlcnJvcikpIHtcbiAgICAgICAgICAgIGxldCBzb3VyY2VzID0gcmVjb3JkZWRFcnJvcnMuZ2V0KGVycm9yKVxuICAgICAgICAgICAgc291cmNlcy5hZGQoc291cmNlKVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgcmVjb3JkZWRFcnJvcnMuc2V0KGVycm9yLCBuZXcgU2V0KFtzb3VyY2VdKSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyBzaG93RXJyb3JzKCkge1xuICAgICAgICBpZiAodGhpcy5jb3VudEVycm9ycygpID09IDApIHtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIGxldCBlcnJvcnMgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncnVudGltZS1lcnJvcnMnKVxuICAgICAgICBpZiAoZXJyb3JzID09IG51bGwpIHtcbiAgICAgICAgICAgIGVycm9ycyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpXG4gICAgICAgICAgICBlcnJvcnMuc2V0QXR0cmlidXRlKCdpZCcsICdydW50aW1lLWVycm9ycycpXG4gICAgICAgICAgICB0aGlzLnNldFN0eWxlKGRvY3VtZW50LmJvZHksIHtcbiAgICAgICAgICAgICAgICBib3JkZXI6ICcycHggc29saWQgcmVkJ1xuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHRoaXMuc2V0U3R5bGUoZXJyb3JzLCB7cG9zaXRpb246ICdhYnNvbHV0ZScsXG4gICAgICAgICAgICAgICAgdG9wOiAnMHB4JyxcbiAgICAgICAgICAgICAgICBwYWRkaW5nOiAnOHB4JyxcbiAgICAgICAgICAgICAgICB3aWR0aDogJzEwMCUnLFxuICAgICAgICAgICAgICAgIGJhY2tncm91bmQ6ICdyZWQnLFxuICAgICAgICAgICAgICAgIGNvbG9yOiAnd2hpdGUnfSlcbiAgICAgICAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoZXJyb3JzKVxuICAgICAgICAgICAgbGV0IGNvdW50ZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKVxuICAgICAgICAgICAgY291bnRlci5zZXRBdHRyaWJ1dGUoJ2lkJywgJ3J1bnRpbWUtZXJyb3JzLWNvdW50ZXInKVxuICAgICAgICAgICAgdGhpcy5zZXRTdHlsZShjb3VudGVyLCB7Ym9yZGVyUmFkaXVzOiAnNTAlJyxcbiAgICAgICAgICAgICAgICB3aWR0aDogJzMycHgnLFxuICAgICAgICAgICAgICAgIGhlaWdodDogJzMycHgnLFxuICAgICAgICAgICAgICAgIGJhY2tncm91bmQ6ICd3aGl0ZScsXG4gICAgICAgICAgICAgICAgY29sb3I6ICdyZWQnLFxuICAgICAgICAgICAgICAgIGZvbnRTaXplOiAnMThweCcsXG4gICAgICAgICAgICAgICAgdGV4dEFsaWduOiAnY2VudGVyJyxcbiAgICAgICAgICAgICAgICBsaW5lSGVpZ2h0OiAnMzJweCcsXG4gICAgICAgICAgICAgICAgdmVydGljYWxBbGlnbjogJ21pZGRsZSd9KVxuICAgICAgICAgICAgY291bnRlci5pbm5lckhUTUwgPSAnMSdcbiAgICAgICAgICAgIGVycm9ycy5hcHBlbmRDaGlsZChjb3VudGVyKVxuXG4gICAgICAgICAgICBsZXQgaGVhZGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICAgICAgICAgIHRoaXMuc2V0U3R5bGUoaGVhZGVyLCB7cG9zaXRpb246ICdhYnNvbHV0ZScsXG4gICAgICAgICAgICAgICAgdG9wOiAnNnB4JyxcbiAgICAgICAgICAgICAgICBsZWZ0OiAnNDhweCcsXG4gICAgICAgICAgICAgICAgaGVpZ2h0OiAnNDRweCcsXG4gICAgICAgICAgICAgICAgZm9udFNpemU6ICczMnB4J30pXG4gICAgICAgICAgICBoZWFkZXIuaW5uZXJIVE1MID0gJ1J1bnRpbWUgRXJyb3JzJ1xuICAgICAgICAgICAgZXJyb3JzLmFwcGVuZENoaWxkKGhlYWRlcilcbiAgICAgICAgICAgIGVycm9ycy5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIHRoaXMudG9nZ2xlRXJyb3JzLmJpbmQodGhpcykpXG4gICAgICAgIH1cbiAgICAgICAgbGV0IGNvdW50ZXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncnVudGltZS1lcnJvcnMtY291bnRlcicpXG4gICAgICAgIGNvdW50ZXIuaW5uZXJIVE1MID0gdGhpcy5jb3VudEVycm9ycygpXG4gICAgfVxuXG4gICAgc3RhdGljIGV4cGFuZEVycm9ycygpIHtcbiAgICAgICAgbGV0IGVycm9ycyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdydW50aW1lLWVycm9ycycpXG4gICAgICAgIGZvcihsZXQgZXJyb3Igb2YgcmVjb3JkZWRFcnJvcnMua2V5cygpKSB7XG4gICAgICAgICAgICBmb3IodmFyIHNvdXJjZSBvZiByZWNvcmRlZEVycm9ycy5nZXQoZXJyb3IpKSB7XG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZihzb3VyY2UpID09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICAgICAgICAgIHNvdXJjZSA9ICdTZWUgY29uc29sZSBmb3IgZGV0YWlscydcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGxldCBpbmZvID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICAgICAgICAgICAgICBpbmZvLmNsYXNzTmFtZSA9ICdpbmZvJ1xuICAgICAgICAgICAgICAgIGluZm8uc3R5bGUud29yZFdyYXAgPSAnYnJlYWstd29yZCdcbiAgICAgICAgICAgICAgICBpbmZvLmlubmVySFRNTCA9IGVycm9yICsgYDxici8+PHNtYWxsPiR7c291cmNlfTwvc21hbGw+YFxuICAgICAgICAgICAgICAgIGVycm9ycy5hcHBlbmRDaGlsZChpbmZvKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIHRvZ2dsZUVycm9ycygpIHtcbiAgICAgICAgbGV0IGVycm9ycyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdydW50aW1lLWVycm9ycycpXG4gICAgICAgIGxldCBpbmZvcyA9IGVycm9ycy5xdWVyeVNlbGVjdG9yQWxsKCcuaW5mbycpXG4gICAgICAgIGlmIChpbmZvcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBpbmZvcy5mb3JFYWNoKChpbmZvKSA9PiBlcnJvcnMucmVtb3ZlQ2hpbGQoaW5mbykpXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmV4cGFuZEVycm9ycygpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgcmVtb3ZlRXJyb3IoZXZlbnQpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ3JlbW92ZUVycm9yJywgZXZlbnQpXG4gICAgICAgIGlmIChyZWNvcmRlZEVycm9ycy5oYXMoZXZlbnQuZXJyb3IpKSB7XG4gICAgICAgICAgICBsZXQgc291cmNlcyA9IHJlY29yZGVkRXJyb3JzLmdldChldmVudC5lcnJvcilcbiAgICAgICAgICAgIHNvdXJjZXMuZGVsZXRlKGV2ZW50LnNvdXJjZSlcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdzb3VyY2VzJywgc291cmNlcylcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyByZWdpc3Rlckdsb2JhbEVycm9ySGFuZGxlcigpIHtcbiAgICAgICAgLy8gUmVnaXN0ZXIgbW9yZSBpbmZvcm1hdGl2ZSBlcnJvciBoYW5kbGVyXG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdlcnJvcicsIChldmVudCkgPT4ge1xuICAgICAgICAvLyAgICAgaWYgKHR5cGVvZihldmVudC5lcnJvcikgPT0gJ3VuZGVmaW5lZCcpIHtcbi8vICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oXCJDYXRjaGVkIHVuZGVmaW5lZCBlcnJvclwiLCBldmVudClcbi8vICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuYXBwZW5kRXJyb3IoZXZlbnQuZXJyb3IsIGV2ZW50LmZpbGVuYW1lKVxuICAgICAgICB9LCB0cnVlKVxuXG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgIHRoaXMuc2hvd0Vycm9ycygpXG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgc3RhdGljIHJlZ2lzdGVyRnJhbWVBd2FpdEVycm9ycygpIHtcbiAgICAgICAgbGV0IGlmcmFtZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaWZyYW1lJylcbiAgICAgICAgZm9yKGxldCBpPTA7IGk8aWZyYW1lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgbGV0IHRhcmdldCA9IGlmcmFtZXNbaV1cbiAgICAgICAgICAgIHRhcmdldC5pZnJhbWVUaW1lb3V0ID0gc2V0VGltZW91dChcbiAgICAgICAgICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuYXBwZW5kRXJyb3IoJ0Nhbm5vdCBsb2FkIGlmcmFtZScsIHRhcmdldC5zcmMpfSxcbiAgICAgICAgICAgICAgICBmcmFtZUVycm9yVGltZW91dClcbiAgICAgICAgICAgIHRhcmdldC5vbmxvYWQgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRhcmdldC5pZnJhbWVUaW1lb3V0KVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxufVxuXG5FcnJvcnMucmVnaXN0ZXJHbG9iYWxFcnJvckhhbmRsZXIoKVxuIiwiXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBFdmVudHMge1xuXG4gICAgc3RhdGljIHN0b3AoZXZlbnQpIHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKVxuICAgICAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKVxuICAgIH1cblxuICAgIHN0YXRpYyBleHRyYWN0UG9pbnQoZXZlbnQpIHtcbiAgICAgICAgc3dpdGNoIChldmVudC5jb25zdHJ1Y3Rvci5uYW1lKSB7XG4gICAgICAgICAgICBjYXNlICdUb3VjaEV2ZW50JzpcbiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGV2ZW50LnRhcmdldFRvdWNoZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IHQgPSBldmVudC50YXJnZXRUb3VjaGVzW2ldXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB7IHg6IHQuY2xpZW50WCwgeTogdC5jbGllbnRZIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgeDogZXZlbnQuY2xpZW50WCwgeTogZXZlbnQuY2xpZW50WSB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgaXNDYXB0dXJlZChldmVudCkge1xuICAgICAgICBpZiAoZXZlbnQuX19jYXB0dXJlZEJ5KVxuICAgICAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgc3RhdGljIGNhcHR1cmVkQnkoZXZlbnQsIG9iaikge1xuICAgICAgICBldmVudC5fX2NhcHR1cmVkQnkgPSBvYmpcbiAgICB9XG5cbiAgICBzdGF0aWMgaXNNb3VzZURvd24oZXZlbnQpIHtcbiAgICAgICAgLy8gQXR0ZW1wdHMgdG8gY2xvbmUgdGhlIHdoaWNoIGF0dHJpYnV0ZSBvZiBldmVudHMgZmFpbGVkIGluIFdlYktpdC4gTWF5XG4gICAgICAgIC8vIGJlIHRoaXMgaXMgYSBidWcgb3IgYSBzZWN1cml0eSBmZWF0dXJlLiBXb3JrYXJvdW5kOiB3ZSBpbnRyb2R1Y2VcbiAgICAgICAgLy8gYSBtb3VzZURvd25TdWJzdGl0dXRlIGF0dHJpYnV0ZSB0aGF0IGNhbiBiZSBhc3NpZ25lZCB0byBjbG9uZWRcbiAgICAgICAgLy8gZXZlbnRzIGFmdGVyIGluc3RhbnRpYXRpb24uXG4gICAgICAgIGlmIChSZWZsZWN0LmhhcyhldmVudCwgJ21vdXNlRG93blN1YnN0aXR1dGUnKSlcbiAgICAgICAgICAgIHJldHVybiBldmVudC5tb3VzZURvd25TdWJzdGl0dXRlXG4gICAgICAgIHJldHVybiBldmVudC5idXR0b25zIHx8IGV2ZW50LndoaWNoXG4gICAgfVxuXG4gICAgc3RhdGljIGlzU2ltdWxhdGVkRXZlbnQoZXZlbnQpIHtcbiAgICAgICAgcmV0dXJuIFJlZmxlY3QuaGFzKGV2ZW50LCAnbW91c2VEb3duU3Vic3RpdHV0ZScpXG4gICAgfVxuXG4gICAgc3RhdGljIGlzTW91c2VSaWdodENsaWNrKGV2ZW50KSB7XG4gICAgICAgIHJldHVybiBldmVudC5idXR0b25zIHx8IGV2ZW50LndoaWNoXG4gICAgfVxuXG4gICAgc3RhdGljIGV4dHJhY3RUb3VjaGVzKHRhcmdldHMpIHtcbiAgICAgICAgbGV0IHRvdWNoZXMgPSBbXVxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRhcmdldHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGxldCB0ID0gdGFyZ2V0c1tpXVxuICAgICAgICAgICAgdG91Y2hlcy5wdXNoKHtcbiAgICAgICAgICAgICAgICB0YXJnZXRTZWxlY3RvcjogdGhpcy5zZWxlY3Rvcih0LnRhcmdldCksXG4gICAgICAgICAgICAgICAgaWRlbnRpZmllcjogdC5pZGVudGlmaWVyLFxuICAgICAgICAgICAgICAgIHNjcmVlblg6IHQuc2NyZWVuWCxcbiAgICAgICAgICAgICAgICBzY3JlZW5ZOiB0LnNjcmVlblksXG4gICAgICAgICAgICAgICAgY2xpZW50WDogdC5jbGllbnRYLFxuICAgICAgICAgICAgICAgIGNsaWVudFk6IHQuY2xpZW50WSxcbiAgICAgICAgICAgICAgICBwYWdlWDogdC5wYWdlWCxcbiAgICAgICAgICAgICAgICBwYWdlWTogdC5wYWdlWVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdG91Y2hlc1xuICAgIH1cblxuICAgIHN0YXRpYyBjcmVhdGVUb3VjaExpc3QodGFyZ2V0cykge1xuICAgICAgICBsZXQgdG91Y2hlcyA9IFtdXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGFyZ2V0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgbGV0IHQgPSB0YXJnZXRzW2ldXG4gICAgICAgICAgICBsZXQgdG91Y2hUYXJnZXQgPSBkb2N1bWVudC5lbGVtZW50RnJvbVBvaW50KHQucGFnZVgsIHQucGFnZVkpXG4gICAgICAgICAgICBsZXQgdG91Y2ggPSBuZXcgVG91Y2godW5kZWZpbmVkLCB0b3VjaFRhcmdldCwgdC5pZGVudGlmaWVyLFxuICAgICAgICAgICAgICAgIHQucGFnZVgsIHQucGFnZVksIHQuc2NyZWVuWCwgdC5zY3JlZW5ZKVxuICAgICAgICAgICAgdG91Y2hlcy5wdXNoKHRvdWNoKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBuZXcgVG91Y2hMaXN0KC4uLnRvdWNoZXMpXG4gICAgfVxuXG4gICAgc3RhdGljIGV4dHJhY3RFdmVudCh0aW1lc3RhbXAsIGV2ZW50KSB7XG4gICAgICAgIGxldCB0YXJnZXRTZWxlY3RvciA9IHRoaXMuc2VsZWN0b3IoZXZlbnQudGFyZ2V0KVxuICAgICAgICBsZXQgaW5mb3MgPSB7XG4gICAgICAgICAgICB0eXBlOiBldmVudC50eXBlLFxuICAgICAgICAgICAgdGltZTogdGltZXN0YW1wLFxuICAgICAgICAgICAgY29uc3RydWN0b3I6IGV2ZW50LmNvbnN0cnVjdG9yLFxuICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgIHRhcmdldFNlbGVjdG9yOiB0YXJnZXRTZWxlY3RvcixcbiAgICAgICAgICAgICAgICB2aWV3OiBldmVudC52aWV3LFxuICAgICAgICAgICAgICAgIG1vdXNlRG93blN1YnN0aXR1dGU6IGV2ZW50LmJ1dHRvbnMgfHwgZXZlbnQud2hpY2gsIC8vIHdoaWNoIGNhbm5vdCBiZSBjbG9uZWQgZGlyZWN0bHlcbiAgICAgICAgICAgICAgICBidWJibGVzOiBldmVudC5idWJibGVzLFxuICAgICAgICAgICAgICAgIGNhbmNlbGFibGU6IGV2ZW50LmNhbmNlbGFibGUsXG4gICAgICAgICAgICAgICAgc2NyZWVuWDogZXZlbnQuc2NyZWVuWCxcbiAgICAgICAgICAgICAgICBzY3JlZW5ZOiBldmVudC5zY3JlZW5ZLFxuICAgICAgICAgICAgICAgIGNsaWVudFg6IGV2ZW50LmNsaWVudFgsXG4gICAgICAgICAgICAgICAgY2xpZW50WTogZXZlbnQuY2xpZW50WSxcbiAgICAgICAgICAgICAgICBsYXllclg6IGV2ZW50LmxheWVyWCxcbiAgICAgICAgICAgICAgICBsYXllclk6IGV2ZW50LmxheWVyWSxcbiAgICAgICAgICAgICAgICBwYWdlWDogZXZlbnQucGFnZVgsXG4gICAgICAgICAgICAgICAgcGFnZVk6IGV2ZW50LnBhZ2VZLFxuICAgICAgICAgICAgICAgIGN0cmxLZXk6IGV2ZW50LmN0cmxLZXksXG4gICAgICAgICAgICAgICAgYWx0S2V5OiBldmVudC5hbHRLZXksXG4gICAgICAgICAgICAgICAgc2hpZnRLZXk6IGV2ZW50LnNoaWZ0S2V5LFxuICAgICAgICAgICAgICAgIG1ldGFLZXk6IGV2ZW50Lm1ldGFLZXlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoZXZlbnQudHlwZS5zdGFydHNXaXRoKCd0b3VjaCcpKSB7XG4gICAgICAgICAgICAvLyBPbiBTYWZhcmktV2ViS2l0IHRoZSBUb3VjaEV2ZW50IGhhcyBsYXllclgsIGxheWVyWSBjb29yZGluYXRlc1xuICAgICAgICAgICAgbGV0IGRhdGEgPSBpbmZvcy5kYXRhXG4gICAgICAgICAgICBkYXRhLnRhcmdldFRvdWNoZXMgPSB0aGlzLmV4dHJhY3RUb3VjaGVzKGV2ZW50LnRhcmdldFRvdWNoZXMpXG4gICAgICAgICAgICBkYXRhLmNoYW5nZWRUb3VjaGVzID0gdGhpcy5leHRyYWN0VG91Y2hlcyhldmVudC5jaGFuZ2VkVG91Y2hlcylcbiAgICAgICAgICAgIGRhdGEudG91Y2hlcyA9IHRoaXMuZXh0cmFjdFRvdWNoZXMoZXZlbnQudG91Y2hlcylcbiAgICAgICAgfVxuICAgICAgICBpZiAoZXZlbnQudHlwZS5zdGFydHNXaXRoKCdwb2ludGVyJykpIHtcbiAgICAgICAgICAgIGxldCBkYXRhID0gaW5mb3MuZGF0YVxuICAgICAgICAgICAgZGF0YS5wb2ludGVySWQgPSBldmVudC5wb2ludGVySWRcbiAgICAgICAgICAgIGRhdGEucG9pbnRlclR5cGUgPSBldmVudC5wb2ludGVyVHlwZVxuICAgICAgICB9XG4gICAgICAgIGlmIChFdmVudHMuZGVidWcpIHtcbiAgICAgICAgICAgIEV2ZW50cy5leHRyYWN0ZWQucHVzaCh0aGlzLnRvTGluZShldmVudCkpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGluZm9zXG4gICAgfVxuXG4gICAgc3RhdGljIGNsb25lRXZlbnQodHlwZSwgY29uc3RydWN0b3IsIGRhdGEpIHtcbiAgICAgICAgaWYgKHR5cGUuc3RhcnRzV2l0aCgndG91Y2gnKSkge1xuICAgICAgICAgICAgLy8gV2UgbmVlZCB0byBmaW5kIHRhcmdldCBmcm9tIGxheWVyWCwgbGF5ZXJZXG4gICAgICAgICAgICAvL3ZhciB0YXJnZXQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGRhdGEudGFyZ2V0U2VsZWN0b3IpXG4gICAgICAgICAgICAvLyBlbGVtZW50RnJvbVBvaW50KGRhdGEubGF5ZXJYLCBkYXRhLmxheWVyWSlcbiAgICAgICAgICAgIC8vZGF0YS50YXJnZXQgPSB0YXJnZXRcbiAgICAgICAgICAgIGRhdGEudGFyZ2V0VG91Y2hlcyA9IHRoaXMuY3JlYXRlVG91Y2hMaXN0KGRhdGEudGFyZ2V0VG91Y2hlcylcbiAgICAgICAgICAgIGRhdGEuY2hhbmdlZFRvdWNoZXMgPSB0aGlzLmNyZWF0ZVRvdWNoTGlzdChkYXRhLmNoYW5nZWRUb3VjaGVzKVxuICAgICAgICAgICAgZGF0YS50b3VjaGVzID0gdGhpcy5jcmVhdGVUb3VjaExpc3QoZGF0YS50b3VjaGVzKVxuICAgICAgICB9XG4gICAgICAgIC8vIFdlIG5lZWQgdG8gZmluZCB0YXJnZXQgZnJvbSBwYWdlWCwgcGFnZVkgd2hpY2ggYXJlIG9ubHlcbiAgICAgICAgLy8gYXZhaWxhYmxlIGFmdGVyIGNvbnN0cnVjdGlvbi4gVGhleSBzZWVtIHRvIGdldHRlciBpdGVtcy5cblxuICAgICAgICBsZXQgY2xvbmUgPSBSZWZsZWN0LmNvbnN0cnVjdChjb25zdHJ1Y3RvciwgW3R5cGUsIGRhdGFdKVxuICAgICAgICBjbG9uZS5tb3VzZURvd25TdWJzdGl0dXRlID0gZGF0YS5tb3VzZURvd25TdWJzdGl0dXRlXG4gICAgICAgIHJldHVybiBjbG9uZVxuICAgIH1cblxuICAgIHN0YXRpYyBzaW11bGF0ZUV2ZW50KHR5cGUsIGNvbnN0cnVjdG9yLCBkYXRhKSB7XG4gICAgICAgIGRhdGEudGFyZ2V0ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihkYXRhLnRhcmdldFNlbGVjdG9yKVxuICAgICAgICBsZXQgY2xvbmUgPSB0aGlzLmNsb25lRXZlbnQodHlwZSwgY29uc3RydWN0b3IsIGRhdGEpXG4gICAgICAgIGlmIChkYXRhLnRhcmdldCAhPSBudWxsKSB7XG4gICAgICAgICAgICBkYXRhLnRhcmdldC5kaXNwYXRjaEV2ZW50KGNsb25lKVxuICAgICAgICB9XG4gICAgICAgIGlmIChFdmVudHMuZGVidWcpIHtcbiAgICAgICAgICAgIEV2ZW50cy5zaW11bGF0ZWQucHVzaCh0aGlzLnRvTGluZShjbG9uZSkpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgdG9MaW5lKGV2ZW50KSB7XG4gICAgICAgIHJldHVybiBgJHtldmVudC50eXBlfSAjJHtldmVudC50YXJnZXQuaWR9ICR7ZXZlbnQuY2xpZW50WH0gJHtldmVudC5jbGllbnRZfWBcbiAgICAgICAgbGV0IHJlc3VsdCA9IGV2ZW50LnR5cGVcbiAgICAgICAgbGV0IHNlbGVjdG9yID0gdGhpcy5zZWxlY3RvcihldmVudC50YXJnZXQpXG4gICAgICAgIHJlc3VsdCArPSAnIHNlbGVjdG9yOiAnICsgc2VsZWN0b3JcbiAgICAgICAgaWYgKGV2ZW50LnRhcmdldCAhPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKHNlbGVjdG9yKSlcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdDYW5ub3QgcmVzb2x2ZScsIHNlbGVjdG9yKVxuICAgICAgICBsZXQga2V5cyA9IFsnbGF5ZXJYJywgJ2xheWVyWScsICdwYWdlWCcsICdwYWdlWScsICdjbGllbnRYJywgJ2NsaWVudFknXVxuICAgICAgICBmb3IgKGxldCBrZXkgb2Yga2V5cykge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICByZXN1bHQgKz0gJyAnICsga2V5ICsgJzonICsgZXZlbnRba2V5XVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnSW52YWxpZCBrZXk6ICcgKyBrZXkpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgIH1cblxuICAgIHN0YXRpYyBjb21wYXJlRXh0cmFjdGVkV2l0aFNpbXVsYXRlZCgpIHtcbiAgICAgICAgdmFyIGRpZmZzID0gMFxuICAgICAgICBpZiAodGhpcy5leHRyYWN0ZWQubGVuZ3RoICE9IHRoaXMuc2ltdWxhdGVkLmxlbmd0aCkge1xuICAgICAgICAgICAgYWxlcnQoJ1VuZXF1YWwgbGVuZ3RoIG9mIGV4dHJhY3RlZCBbJyArIHRoaXMuZXh0cmFjdGVkLmxlbmd0aCArXG4gICAgICAgICAgICAgICAgJ10gYW5kIHNpbXVsYXRlZCBldmVudHMgWycgKyB0aGlzLnNpbXVsYXRlZC5sZW5ndGggKyAnXS4nKVxuICAgICAgICAgICAgZGlmZnMgKz0gMVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmV4dHJhY3RlZC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHZhciBleHRyYWN0ZWQgPSB0aGlzLmV4dHJhY3RlZFtpXVxuICAgICAgICAgICAgICAgIHZhciBzaW11bGF0ZWQgPSB0aGlzLnNpbXVsYXRlZFtpXVxuICAgICAgICAgICAgICAgIGlmIChleHRyYWN0ZWQgIT0gc2ltdWxhdGVkKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdFdmVudHMgZGlmZmVyOicgKyBleHRyYWN0ZWQgKyAnfCcgKyBzaW11bGF0ZWQpXG4gICAgICAgICAgICAgICAgICAgIGRpZmZzICs9IDFcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgc2VsZWN0b3IoY29udGV4dCkge1xuICAgICAgICByZXR1cm4gT3B0aW1hbFNlbGVjdC5zZWxlY3QoY29udGV4dClcbiAgICB9XG5cbiAgICBzdGF0aWMgcmVzZXQoKSB7XG4gICAgICAgIHRoaXMuZXh0cmFjdGVkID0gW11cbiAgICAgICAgdGhpcy5zaW11bGF0ZWQgPSBbXVxuICAgIH1cblxuICAgIHN0YXRpYyByZXNldFNpbXVsYXRlZCgpIHtcbiAgICAgICAgdGhpcy5zaW11bGF0ZWQgPSBbXVxuICAgIH1cblxuICAgIHN0YXRpYyBzaG93RXh0cmFjdGVkRXZlbnRzKGV2ZW50KSB7XG4gICAgICAgIGlmICghZXZlbnQuc2hpZnRLZXkpIHtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnBvcHVwID09IG51bGwpIHtcbiAgICAgICAgICAgIGxldCBlbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICAgICAgICAgIEVsZW1lbnRzLnNldFN0eWxlKGVsZW1lbnQsIHtcbiAgICAgICAgICAgICAgICBwb3NpdGlvbjogJ2Fic29sdXRlJyxcbiAgICAgICAgICAgICAgICB3aWR0aDogJzQ4MHB4JyxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6ICc2NDBweCcsXG4gICAgICAgICAgICAgICAgb3ZlcmZsb3c6ICdhdXRvJyxcbiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3I6ICdsaWdodGdyYXknXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChlbGVtZW50KVxuICAgICAgICAgICAgdGhpcy5wb3B1cCA9IGVsZW1lbnRcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnBvcHVwLmlubmVySFRNTCA9ICcnXG4gICAgICAgIGZvciAobGV0IGxpbmUgb2YgdGhpcy5leHRyYWN0ZWQpIHtcbiAgICAgICAgICAgIGxldCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKVxuICAgICAgICAgICAgZGl2LmlubmVySFRNTCA9IGxpbmVcbiAgICAgICAgICAgIHRoaXMucG9wdXAuYXBwZW5kQ2hpbGQoZGl2KVxuICAgICAgICB9XG4gICAgICAgIGxldCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKVxuICAgICAgICBkaXYuaW5uZXJIVE1MID0gJy0tLS0tLS0tLS0tLSBTaW11bGF0ZWQgLS0tLS0tLS0tLS0nXG4gICAgICAgIHRoaXMucG9wdXAuYXBwZW5kQ2hpbGQoZGl2KVxuICAgICAgICBmb3IgKGxldCBsaW5lIG9mIHRoaXMuc2ltdWxhdGVkKSB7XG4gICAgICAgICAgICBsZXQgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICAgICAgICAgIGRpdi5pbm5lckhUTUwgPSBsaW5lXG4gICAgICAgICAgICB0aGlzLnBvcHVwLmFwcGVuZENoaWxkKGRpdilcbiAgICAgICAgfVxuICAgICAgICBFbGVtZW50cy5zZXRTdHlsZSh0aGlzLnBvcHVwLFxuICAgICAgICAgICAgeyBsZWZ0OiBldmVudC5jbGllbnRYICsgJ3B4JywgdG9wOiBldmVudC5jbGllbnRZICsgJ3B4JyB9KVxuICAgIH1cbn1cblxuRXZlbnRzLnBvcHVwID0gbnVsbFxuRXZlbnRzLmRlYnVnID0gdHJ1ZVxuRXZlbnRzLmV4dHJhY3RlZCA9IFtdXG5FdmVudHMuc2ltdWxhdGVkID0gW11cbkV2ZW50cy5zaW11bGF0aW9uUnVubmluZyA9IGZhbHNlXG5cbmV4cG9ydCBjbGFzcyBFdmVudFJlY29yZGVyIHtcblxuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnJlY29yZGluZyA9IFtdXG4gICAgICAgIHRoaXMucmVjb3JkZWQgPSBbXVxuICAgICAgICB0aGlzLnN0ZXAgPSAwXG4gICAgfVxuXG4gICAgcmVjb3JkKGV2ZW50KSB7XG4gICAgICAgIGxldCBsZW5ndGggPSB0aGlzLnJlY29yZGluZy5sZW5ndGhcbiAgICAgICAgaWYgKGxlbmd0aCA9PSAwKSB7XG4gICAgICAgICAgICB0aGlzLnN0YXJ0VGltZSA9IGV2ZW50LnRpbWVTdGFtcFxuICAgICAgICAgICAgRXZlbnRzLnJlc2V0KClcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGxldCBsYXN0ID0gdGhpcy5yZWNvcmRpbmdbbGVuZ3RoIC0gMV1cbiAgICAgICAgICAgIGlmIChldmVudC50aW1lU3RhbXAgPCBsYXN0LnRpbWUpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnd2FybmluZzogd3JvbmcgdGVtcG9yYWwgb3JkZXInKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGxldCB0ID0gZXZlbnQudGltZVN0YW1wIC0gdGhpcy5zdGFydFRpbWVcbiAgICAgICAgdGhpcy5yZWNvcmRpbmcucHVzaChFdmVudHMuZXh0cmFjdEV2ZW50KHQsIGV2ZW50KSlcbiAgICB9XG5cbiAgICBzdG9wUmVjb3JkaW5nKCkge1xuICAgICAgICB0aGlzLnJlY29yZGVkID0gdGhpcy5yZWNvcmRpbmdcbiAgICAgICAgdGhpcy5yZWNvcmRpbmcgPSBbXVxuICAgICAgICBjb25zb2xlLmxvZygnUmVjb3JkZWQgJyArIHRoaXMucmVjb3JkZWQubGVuZ3RoICsgJyBldmVudHMnKVxuICAgIH1cblxuICAgIHN0YXJ0UmVwbGF5KHdoaWxlQ29uZGl0aW9uID0gbnVsbCwgb25Db21wbGV0ZSA9IG51bGwpIHtcbiAgICAgICAgdGhpcy5zdGVwID0gMFxuICAgICAgICBFdmVudHMucmVzZXRTaW11bGF0ZWQoKVxuICAgICAgICBjb25zb2xlLmxvZygnU3RhcnQgcmVwbGF5JylcbiAgICAgICAgRXZlbnRzLnNpbXVsYXRpb25SdW5uaW5nID0gdHJ1ZVxuICAgICAgICB0aGlzLnJlcGxheSh3aGlsZUNvbmRpdGlvbiwgb25Db21wbGV0ZSlcbiAgICB9XG5cbiAgICByZXBsYXkod2hpbGVDb25kaXRpb24gPSBudWxsLCBvbkNvbXBsZXRlID0gbnVsbCkge1xuICAgICAgICBpZiAodGhpcy5zdGVwIDwgdGhpcy5yZWNvcmRlZC5sZW5ndGgpIHtcbiAgICAgICAgICAgIGxldCB7IHR5cGUsIHRpbWUsIGNvbnN0cnVjdG9yLCBkYXRhIH0gPSB0aGlzLnJlY29yZGVkW3RoaXMuc3RlcF1cbiAgICAgICAgICAgIEV2ZW50cy5zaW11bGF0ZUV2ZW50KHR5cGUsIGNvbnN0cnVjdG9yLCBkYXRhKVxuICAgICAgICAgICAgXG4gICAgICAgICAgICB0aGlzLnN0ZXAgKz0gMVxuICAgICAgICAgICAgbGV0IGR0ID0gMFxuICAgICAgICAgICAgaWYgKHRoaXMuc3RlcCA8IHRoaXMucmVjb3JkZWQubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdmFyIG5leHQgPSB0aGlzLnJlY29yZGVkW3RoaXMuc3RlcF1cbiAgICAgICAgICAgICAgICBkdCA9IG5leHQudGltZSAtIHRpbWVcbiAgICAgICAgICAgICAgICBpZiAoZHQgPCAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCd3YXJuaW5nOiB3cm9uZyB0ZW1wb3JhbCBvcmRlcicpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHdoaWxlQ29uZGl0aW9uID09IG51bGwgfHwgd2hpbGVDb25kaXRpb24oKSkge1xuICAgICAgICAgICAgICAgIGxldCBkZWx0YSA9IE1hdGgucm91bmQoZHQpXG4gICAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLnJlcGxheSh3aGlsZUNvbmRpdGlvbiwgb25Db21wbGV0ZSksIGRlbHRhKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1BsYXllZCAnICsgdGhpcy5zdGVwICsgJyBldmVudHMnICsgb25Db21wbGV0ZSlcbiAgICAgICAgICAgIEV2ZW50cy5zaW11bGF0aW9uUnVubmluZyA9IGZhbHNlXG4gICAgICAgICAgICBpZiAob25Db21wbGV0ZSAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgb25Db21wbGV0ZSgpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvL0V2ZW50cy5jb21wYXJlRXh0cmFjdGVkV2l0aFNpbXVsYXRlZCgpXG4gICAgICAgIH1cbiAgICB9XG59XG5cbiIsIi8qIGdsb2JhbHMgV2ViS2l0UG9pbnQgKi9cblxuLyoqIFRlc3RzIHdoZXRoZXIgYW4gb2JqZWN0IGlzIGVtcHR5XG4gKiBAcGFyYW0ge09iamVjdH0gb2JqIC0gdGhlIG9iamVjdCB0byBiZSB0ZXN0ZWRcbiAqIEByZXR1cm4ge2Jvb2xlYW59XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0VtcHR5KG9iaikge1xuICAgIC8vID4gaXNFbXB0eSh7fSlcbiAgICAvLyB0cnVlXG4gICAgZm9yIChsZXQgaSBpbiBvYmopIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICAgIHJldHVybiB0cnVlXG59XG5cbi8qKiBSZXR1cm5zIGEgdW5pdmVyc2FsIHVuaXF1ZSBpZFxuICogQHJldHVybiB7c3RyaW5nfVxuICogU2VlIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzEwNTAzNC9jcmVhdGUtZ3VpZC11dWlkLWluLWphdmFzY3JpcHQvMjE5NjMxMzYjMjE5NjMxMzZcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHV1aWQoKSB7XG4gICAgcmV0dXJuICd4eHh4eHh4eC14eHh4LTR4eHgteXh4eC14eHh4eHh4eHh4eHgnLnJlcGxhY2UoL1t4eV0vZywgYyA9PiB7XG4gICAgICAgIGxldCByID0gKE1hdGgucmFuZG9tKCkgKiAxNikgfCAwLFxuICAgICAgICAgICAgdiA9IGMgPT0gJ3gnID8gciA6IChyICYgMHgzKSB8IDB4OFxuICAgICAgICByZXR1cm4gdi50b1N0cmluZygxNilcbiAgICB9KVxufVxuXG5leHBvcnQgZnVuY3Rpb24gbGVycChzdGFydCwgc3RvcCwgYW10KSB7XG4gICAgcmV0dXJuIGFtdCAqIChzdG9wIC0gc3RhcnQpICsgc3RhcnRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNhbXBsZShwb3B1bGF0aW9uLCBrKSB7XG4gICAgLypcbiAgICAgICAgRnJvbSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xOTI2OTU0NS9ob3ctdG8tZ2V0LW4tbm8tZWxlbWVudHMtcmFuZG9tbHktZnJvbS1hbi1hcnJheS8zODU3MTEzMlxuICAgICAgICBcbiAgICAgICAgQ2hvb3NlcyBrIHVuaXF1ZSByYW5kb20gZWxlbWVudHMgZnJvbSBhIHBvcHVsYXRpb24gc2VxdWVuY2Ugb3Igc2V0LlxuXG4gICAgICAgIFJldHVybnMgYSBuZXcgbGlzdCBjb250YWluaW5nIGVsZW1lbnRzIGZyb20gdGhlIHBvcHVsYXRpb24gd2hpbGVcbiAgICAgICAgbGVhdmluZyB0aGUgb3JpZ2luYWwgcG9wdWxhdGlvbiB1bmNoYW5nZWQuICBUaGUgcmVzdWx0aW5nIGxpc3QgaXNcbiAgICAgICAgaW4gc2VsZWN0aW9uIG9yZGVyIHNvIHRoYXQgYWxsIHN1Yi1zbGljZXMgd2lsbCBhbHNvIGJlIHZhbGlkIHJhbmRvbVxuICAgICAgICBzYW1wbGVzLiAgVGhpcyBhbGxvd3MgcmFmZmxlIHdpbm5lcnMgKHRoZSBzYW1wbGUpIHRvIGJlIHBhcnRpdGlvbmVkXG4gICAgICAgIGludG8gZ3JhbmQgcHJpemUgYW5kIHNlY29uZCBwbGFjZSB3aW5uZXJzICh0aGUgc3Vic2xpY2VzKS5cblxuICAgICAgICBNZW1iZXJzIG9mIHRoZSBwb3B1bGF0aW9uIG5lZWQgbm90IGJlIGhhc2hhYmxlIG9yIHVuaXF1ZS4gIElmIHRoZVxuICAgICAgICBwb3B1bGF0aW9uIGNvbnRhaW5zIHJlcGVhdHMsIHRoZW4gZWFjaCBvY2N1cnJlbmNlIGlzIGEgcG9zc2libGVcbiAgICAgICAgc2VsZWN0aW9uIGluIHRoZSBzYW1wbGUuXG5cbiAgICAgICAgVG8gY2hvb3NlIGEgc2FtcGxlIGluIGEgcmFuZ2Ugb2YgaW50ZWdlcnMsIHVzZSByYW5nZSBhcyBhbiBhcmd1bWVudC5cbiAgICAgICAgVGhpcyBpcyBlc3BlY2lhbGx5IGZhc3QgYW5kIHNwYWNlIGVmZmljaWVudCBmb3Igc2FtcGxpbmcgZnJvbSBhXG4gICAgICAgIGxhcmdlIHBvcHVsYXRpb246ICAgc2FtcGxlKHJhbmdlKDEwMDAwMDAwKSwgNjApXG5cbiAgICAgICAgU2FtcGxpbmcgd2l0aG91dCByZXBsYWNlbWVudCBlbnRhaWxzIHRyYWNraW5nIGVpdGhlciBwb3RlbnRpYWxcbiAgICAgICAgc2VsZWN0aW9ucyAodGhlIHBvb2wpIGluIGEgbGlzdCBvciBwcmV2aW91cyBzZWxlY3Rpb25zIGluIGEgc2V0LlxuXG4gICAgICAgIFdoZW4gdGhlIG51bWJlciBvZiBzZWxlY3Rpb25zIGlzIHNtYWxsIGNvbXBhcmVkIHRvIHRoZVxuICAgICAgICBwb3B1bGF0aW9uLCB0aGVuIHRyYWNraW5nIHNlbGVjdGlvbnMgaXMgZWZmaWNpZW50LCByZXF1aXJpbmdcbiAgICAgICAgb25seSBhIHNtYWxsIHNldCBhbmQgYW4gb2NjYXNpb25hbCByZXNlbGVjdGlvbi4gIEZvclxuICAgICAgICBhIGxhcmdlciBudW1iZXIgb2Ygc2VsZWN0aW9ucywgdGhlIHBvb2wgdHJhY2tpbmcgbWV0aG9kIGlzXG4gICAgICAgIHByZWZlcnJlZCBzaW5jZSB0aGUgbGlzdCB0YWtlcyBsZXNzIHNwYWNlIHRoYW4gdGhlXG4gICAgICAgIHNldCBhbmQgaXQgZG9lc24ndCBzdWZmZXIgZnJvbSBmcmVxdWVudCByZXNlbGVjdGlvbnMuXG4gICAgKi9cblxuICAgIGlmICghQXJyYXkuaXNBcnJheShwb3B1bGF0aW9uKSlcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlBvcHVsYXRpb24gbXVzdCBiZSBhbiBhcnJheS5cIilcbiAgICBsZXQgbiA9IHBvcHVsYXRpb24ubGVuZ3RoXG4gICAgaWYgKGsgPCAwIHx8IGsgPiBuKVxuICAgICAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcihcIlNhbXBsZSBsYXJnZXIgdGhhbiBwb3B1bGF0aW9uIG9yIGlzIG5lZ2F0aXZlXCIpXG5cbiAgICBsZXQgcmVzdWx0ID0gbmV3IEFycmF5KGspXG4gICAgbGV0IHNldHNpemUgPSAyMSAgIC8vIHNpemUgb2YgYSBzbWFsbCBzZXQgbWludXMgc2l6ZSBvZiBhbiBlbXB0eSBsaXN0XG5cbiAgICBpZiAoayA+IDUpXG4gICAgICAgIHNldHNpemUgKz0gTWF0aC5wb3coNCwgTWF0aC5jZWlsKE1hdGgubG9nKGsgKiAzLCA0KSkpXG5cbiAgICBpZiAobiA8PSBzZXRzaXplKSB7XG4gICAgICAgIC8vIEFuIG4tbGVuZ3RoIGxpc3QgaXMgc21hbGxlciB0aGFuIGEgay1sZW5ndGggc2V0XG4gICAgICAgIGxldCBwb29sID0gcG9wdWxhdGlvbi5zbGljZSgpXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgazsgaSsrKSB7ICAgICAgICAgIC8vIGludmFyaWFudDogIG5vbi1zZWxlY3RlZCBhdCBbMCxuLWkpXG4gICAgICAgICAgICBsZXQgaiA9IE1hdGgucmFuZG9tKCkgKiAobiAtIGkpIHwgMFxuICAgICAgICAgICAgcmVzdWx0W2ldID0gcG9vbFtqXVxuICAgICAgICAgICAgcG9vbFtqXSA9IHBvb2xbbiAtIGkgLSAxXSAgICAgICAvLyBtb3ZlIG5vbi1zZWxlY3RlZCBpdGVtIGludG8gdmFjYW5jeVxuICAgICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgICAgbGV0IHNlbGVjdGVkID0gbmV3IFNldCgpXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgazsgaSsrKSB7XG4gICAgICAgICAgICBsZXQgaiA9IE1hdGgucmFuZG9tKCkgKiAobiAtIGkpIHwgMFxuICAgICAgICAgICAgd2hpbGUgKHNlbGVjdGVkLmhhcyhqKSkge1xuICAgICAgICAgICAgICAgIGogPSBNYXRoLnJhbmRvbSgpICogKG4gLSBpKSB8IDBcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlbGVjdGVkLmFkZChqKVxuICAgICAgICAgICAgcmVzdWx0W2ldID0gcG9wdWxhdGlvbltqXVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdFxufVxuXG5cbi8vIFJldHVybnMgYSBmdW5jdGlvbiwgdGhhdCwgYXMgbG9uZyBhcyBpdCBjb250aW51ZXMgdG8gYmUgaW52b2tlZCwgd2lsbCBub3Rcbi8vIGJlIHRyaWdnZXJlZC4gVGhlIGZ1bmN0aW9uIHdpbGwgYmUgY2FsbGVkIGFmdGVyIGl0IHN0b3BzIGJlaW5nIGNhbGxlZCBmb3Jcbi8vIE4gbWlsbGlzZWNvbmRzLiBJZiBgaW1tZWRpYXRlYCBpcyBwYXNzZWQsIHRyaWdnZXIgdGhlIGZ1bmN0aW9uIG9uIHRoZVxuLy8gbGVhZGluZyBlZGdlLCBpbnN0ZWFkIG9mIHRoZSB0cmFpbGluZy5cbi8vIFRha2VuIGZyb206IGh0dHBzOi8vZGF2aWR3YWxzaC5uYW1lL2Vzc2VudGlhbC1qYXZhc2NyaXB0LWZ1bmN0aW9uc1xuZXhwb3J0IGZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIGltbWVkaWF0ZSkge1xuICAgIGxldCB0aW1lb3V0XG4gICAgcmV0dXJuIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbGV0IGNvbnRleHQgPSB0aGlzLFxuICAgICAgICAgICAgYXJncyA9IGFyZ3VtZW50c1xuICAgICAgICBsZXQgbGF0ZXIgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB0aW1lb3V0ID0gbnVsbFxuICAgICAgICAgICAgaWYgKCFpbW1lZGlhdGUpIGZ1bmMuYXBwbHkoY29udGV4dCwgYXJncylcbiAgICAgICAgfVxuICAgICAgICBsZXQgY2FsbE5vdyA9IGltbWVkaWF0ZSAmJiAhdGltZW91dFxuICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dClcbiAgICAgICAgdGltZW91dCA9IHNldFRpbWVvdXQobGF0ZXIsIHdhaXQpXG4gICAgICAgIGlmIChjYWxsTm93KSBmdW5jLmFwcGx5KGNvbnRleHQsIGFyZ3MpXG4gICAgfVxufVxuXG4vKiogUmV0dXJucyBhbiBpZCB0aGF0IGlzIGd1YXJhbnRlZWQgdG8gYmUgdW5pcXVlIHdpdGhpbiB0aGUgbGl2ZXRpbWUgb2YgdGhlXG4gKiBhcHBsaWNhdGlvblxuICogQHJldHVybiB7c3RyaW5nfVxuICovXG5sZXQgX2lkR2VuZXJhdG9yID0gMFxuZXhwb3J0IGZ1bmN0aW9uIGdldElkKCkge1xuICAgIHJldHVybiAnaWQnICsgX2lkR2VuZXJhdG9yKytcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJhbmRvbUludChtaW4gPSAwLCBtYXggPSAxMDApIHtcbiAgICByZXR1cm4gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogKG1heCAtIG1pbiArIDEpICsgbWluKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmFuZG9tRmxvYXQobWluID0gMC4wLCBtYXggPSAxLjApIHtcbiAgICByZXR1cm4gTWF0aC5yYW5kb20oKSAqIChtYXggLSBtaW4pICsgbWluXG59XG5cbmV4cG9ydCBjbGFzcyBEYXRlcyB7XG5cbiAgICBzdGF0aWMgY3JlYXRlKGZ1bGxZZWFyLCBtb250aCwgZGF5KSB7XG4gICAgICAgIHJldHVybiBuZXcgRGF0ZShEYXRlLlVUQyhmdWxsWWVhciwgbW9udGgsIGRheSkpXG4gICAgfVxuXG4gICAgc3RhdGljIGRheXNJbk1vbnRoKGRhdGUpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBEYXRlKGRhdGUuZ2V0RnVsbFllYXIoKSwgZGF0ZS5nZXRNb250aCgpICsgMSwgMCkuZ2V0RGF0ZSgpXG4gICAgfVxuXG4gICAgc3RhdGljIHN0YXJ0WWVhclJhbmdlKGRhdGUpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBEYXRlKERhdGUuVVRDKGRhdGUuZ2V0RnVsbFllYXIoKSAtIDEsIDExLCAzMSwgMjMsIDU5LCA1OSwgOTk5KSlcbiAgICB9XG5cbiAgICBzdGF0aWMgZW5kWWVhclJhbmdlKGRhdGUpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBEYXRlKERhdGUuVVRDKGRhdGUuZ2V0RnVsbFllYXIoKSArIDEsIDAsIDEpKVxuICAgIH1cblxuICAgIHN0YXRpYyBwcmV2WWVhcihkYXRlLCBvZmZzZXQgPSAxKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNyZWF0ZShkYXRlLmdldEZ1bGxZZWFyKCkgLSBvZmZzZXQsIDAsIDEpXG4gICAgfVxuXG4gICAgc3RhdGljIG5leHRZZWFyKGRhdGUsIG9mZnNldCA9IDEpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlKGRhdGUuZ2V0RnVsbFllYXIoKSArIG9mZnNldCwgMCwgMSlcbiAgICB9XG5cbiAgICBzdGF0aWMgbmV4dE1vbnRoKGRhdGUpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlKGRhdGUuZ2V0RnVsbFllYXIoKSwgZGF0ZS5nZXRNb250aCgpICsgMSwgMSlcbiAgICB9XG5cbiAgICBzdGF0aWMgbmV4dERheShkYXRlKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNyZWF0ZShcbiAgICAgICAgICAgIGRhdGUuZ2V0RnVsbFllYXIoKSxcbiAgICAgICAgICAgIGRhdGUuZ2V0TW9udGgoKSxcbiAgICAgICAgICAgIGRhdGUuZ2V0RGF0ZSgpICsgMVxuICAgICAgICApXG4gICAgfVxuXG4gICAgc3RhdGljIG5leHRIb3VyKGRhdGUpIHtcbiAgICAgICAgLy8gU2VlIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTA1MDcyMC9hZGRpbmctaG91cnMtdG8tamF2YXNjcmlwdC1kYXRlLW9iamVjdFxuICAgICAgICByZXR1cm4gbmV3IERhdGUoZGF0ZS5nZXRUaW1lKCkgKyA2MCAqIDYwICogMTAwMClcbiAgICB9XG5cbiAgICBzdGF0aWMgbmV4dE1pbnV0ZShkYXRlKSB7XG4gICAgICAgIC8vIFNlZSBhYm92ZVxuICAgICAgICByZXR1cm4gbmV3IERhdGUoZGF0ZS5nZXRUaW1lKCkgKyA2MCAqIDEwMDApXG4gICAgfVxuXG4gICAgc3RhdGljIG5leHRTZWNvbmQoZGF0ZSkge1xuICAgICAgICAvLyBTZWUgYWJvdmVcbiAgICAgICAgcmV0dXJuIG5ldyBEYXRlKGRhdGUuZ2V0VGltZSgpICsgMTAwMClcbiAgICB9XG5cbiAgICBzdGF0aWMgbmV4dE1pbGxpc2Vjb25kKGRhdGUpIHtcbiAgICAgICAgLy8gU2VlIGFib3ZlXG4gICAgICAgIHJldHVybiBuZXcgRGF0ZShkYXRlLmdldFRpbWUoKSArIDEpXG4gICAgfVxuXG4gICAgc3RhdGljICppdGVyWWVhcnMoc3RhcnQsIGVuZCkge1xuICAgICAgICBsZXQgZGF0ZSA9IHRoaXMuY3JlYXRlKHN0YXJ0LmdldEZ1bGxZZWFyKCksIDAsIDEpXG4gICAgICAgIHdoaWxlIChkYXRlIDw9IGVuZCkge1xuICAgICAgICAgICAgeWllbGQgZGF0ZVxuICAgICAgICAgICAgZGF0ZSA9IHRoaXMubmV4dFllYXIoZGF0ZSlcbiAgICAgICAgfVxuICAgICAgICB5aWVsZCBkYXRlXG4gICAgfVxuXG4gICAgc3RhdGljICppdGVyTW9udGhzKHllYXIsIGxpbWl0ID0gMTIpIHtcbiAgICAgICAgbGV0IG1vbnRoID0gMFxuICAgICAgICB3aGlsZSAobW9udGggPCBsaW1pdCkge1xuICAgICAgICAgICAgbGV0IGRhdGUgPSB0aGlzLmNyZWF0ZSh5ZWFyLmdldEZ1bGxZZWFyKCksIG1vbnRoLCAxKVxuICAgICAgICAgICAgeWllbGQgZGF0ZVxuICAgICAgICAgICAgbW9udGggKz0gMVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljICppdGVyTW9udGhzT2ZZZWFycyh5ZWFycykge1xuICAgICAgICBmb3IgKGxldCB5ZWFyIG9mIHllYXJzKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBtb250aCBvZiB0aGlzLml0ZXJNb250aHMoeWVhcikpIHtcbiAgICAgICAgICAgICAgICB5aWVsZCBtb250aFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljICppdGVyRGF5cyhtb250aCkge1xuICAgICAgICBsZXQgZGF5ID0gMVxuICAgICAgICBsZXQgbGltaXQgPSBEYXRlcy5kYXlzSW5Nb250aChtb250aClcbiAgICAgICAgd2hpbGUgKGRheSA8PSBsaW1pdCkge1xuICAgICAgICAgICAgbGV0IGRhdGUgPSB0aGlzLmNyZWF0ZShtb250aC5nZXRGdWxsWWVhcigpLCBtb250aC5nZXRNb250aCgpLCBkYXkpXG4gICAgICAgICAgICB5aWVsZCBkYXRlXG4gICAgICAgICAgICBkYXkgKz0gMVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljICppdGVyRGF5c09mTW9udGhzKG1vbnRocykge1xuICAgICAgICBmb3IgKGxldCBtb250aCBvZiBtb250aHMpIHtcbiAgICAgICAgICAgIGZvciAobGV0IGRheSBvZiB0aGlzLml0ZXJEYXlzKG1vbnRoKSkge1xuICAgICAgICAgICAgICAgIHlpZWxkIGRheVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxufVxuLyogQ29sb3IgY29udmVyc2lvbiBmdW5jdGlvbnMgKi9cblxuZXhwb3J0IGNsYXNzIENvbG9ycyB7XG4gICAgLy8gaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy81NjIzODM4L3JnYi10by1oZXgtYW5kLWhleC10by1yZ2JcblxuICAgIHN0YXRpYyByZ2IybnVtKHJlZCwgZ3JlZW4sIGJsdWUpIHtcbiAgICAgICAgbGV0IHJnYiA9IGJsdWUgfCAoZ3JlZW4gPDwgOCkgfCAocmVkIDw8IDE2KVxuICAgICAgICByZXR1cm4gMHgwMDAwMDAgKyByZ2JcbiAgICB9XG5cbiAgICBzdGF0aWMgcmdiMmhleChyZWQsIGdyZWVuLCBibHVlKSB7XG4gICAgICAgIGxldCByZ2IgPSBibHVlIHwgKGdyZWVuIDw8IDgpIHwgKHJlZCA8PCAxNilcbiAgICAgICAgcmV0dXJuICcjJyArICgweDEwMDAwMDAgKyByZ2IpLnRvU3RyaW5nKDE2KS5zbGljZSgxKVxuICAgIH1cblxuICAgIHN0YXRpYyBoZXgycmdiKGhleCkge1xuICAgICAgICAvLyBsb25nIHZlcnNpb25cbiAgICAgICAgbGV0IHIgPSBoZXgubWF0Y2goL14jKFswLTlhLWZdezJ9KShbMC05YS1mXXsyfSkoWzAtOWEtZl17Mn0pJC9pKVxuICAgICAgICBpZiAocikge1xuICAgICAgICAgICAgcmV0dXJuIHIuc2xpY2UoMSwgNCkubWFwKHggPT4ge1xuICAgICAgICAgICAgICAgIHJldHVybiBwYXJzZUludCh4LCAxNilcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgICAgLy8gc2hvcnQgdmVyc2lvblxuICAgICAgICByID0gaGV4Lm1hdGNoKC9eIyhbMC05YS1mXSkoWzAtOWEtZl0pKFswLTlhLWZdKSQvaSlcbiAgICAgICAgaWYgKHIpIHtcbiAgICAgICAgICAgIHJldHVybiByLnNsaWNlKDEsIDQpLm1hcCh4ID0+IHtcbiAgICAgICAgICAgICAgICByZXR1cm4gMHgxMSAqIHBhcnNlSW50KHgsIDE2KVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIHN0YXRpYyByZ2IociwgZywgYikge1xuICAgICAgICByZXR1cm4geyByLCBnLCBiIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgc3RyaW5nMmhleChzdHIpIHtcbiAgICAgICAgcmV0dXJuIHBhcnNlSW50KCcweCcgKyBzdHIuc2xpY2UoMSkpXG4gICAgfVxuXG4gICAgc3RhdGljIGxlcnAocmdiMSwgcmdiMiwgYW1vdW50KSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICByOiBNYXRoLnJvdW5kKGxlcnAocmdiMS5yLCByZ2IyLnIsIGFtb3VudCkpLFxuICAgICAgICAgICAgZzogTWF0aC5yb3VuZChsZXJwKHJnYjEuZywgcmdiMi5nLCBhbW91bnQpKSxcbiAgICAgICAgICAgIGI6IE1hdGgucm91bmQobGVycChyZ2IxLmIsIHJnYjIuYiwgYW1vdW50KSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyBnZXQgdmlvbGV0KCkge1xuICAgICAgICByZXR1cm4gQ29sb3JzLnJnYjJudW0oODksIDM0LCAxMzEpXG4gICAgfVxuXG4gICAgc3RhdGljIGdldCBzdGVlbGJsdWUoKSB7XG4gICAgICAgIHJldHVybiBDb2xvcnMucmdiMm51bSgwLCAxMzAsIDE2NClcbiAgICB9XG5cbiAgICBzdGF0aWMgZ2V0IG9jaHJlKCkge1xuICAgICAgICByZXR1cm4gQ29sb3JzLnJnYjJudW0oMTgxLCAxNTcsIDApXG4gICAgfVxuXG4gICAgc3RhdGljIGdldCB0dXJxdW9pc2UoKSB7XG4gICAgICAgIHJldHVybiBDb2xvcnMucmdiMm51bSgzNCwgMTY0LCAxMzEpXG4gICAgfVxuXG4gICAgc3RhdGljIGdldCBlbWluZW5jZSgpIHtcbiAgICAgICAgcmV0dXJuIENvbG9ycy5yZ2IybnVtKDE1MCwgNjAsIDEzNClcbiAgICB9XG5cbiAgICBzdGF0aWMgcmFuZG9tKCkge1xuICAgICAgICBsZXQgciA9IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIDI1NSlcbiAgICAgICAgbGV0IGcgPSBNYXRoLnJvdW5kKE1hdGgucmFuZG9tKCkgKiAyNTUpXG4gICAgICAgIGxldCBiID0gTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogMjU1KVxuICAgICAgICByZXR1cm4gQ29sb3JzLnJnYjJudW0ociwgZywgYilcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBDeWNsZSBleHRlbmRzIEFycmF5IHtcbiAgICBjb25zdHJ1Y3RvciguLi5pdGVtcykge1xuICAgICAgICBzdXBlcigpXG4gICAgICAgIGZvciAobGV0IGl0ZW0gb2YgaXRlbXMpIHtcbiAgICAgICAgICAgIHRoaXMucHVzaChpdGVtKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuaW5kZXggPSAwXG4gICAgfVxuXG4gICAgbmV4dCgpIHtcbiAgICAgICAgaWYgKHRoaXMuaW5kZXggPT0gdGhpcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHRoaXMuaW5kZXggPSAwXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXNbdGhpcy5pbmRleCsrXVxuICAgIH1cblxuICAgIGN1cnJlbnQoKSB7XG4gICAgICAgIGlmICh0aGlzLmluZGV4ID09PSB0aGlzLmxlbmd0aCkge1xuICAgICAgICAgICAgdGhpcy5pbmRleCA9IDBcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpc1t0aGlzLmluZGV4XVxuICAgIH1cbn1cblxuLyoqIFN0YXRpYyBtZXRob2RzIHRvIGNvbXB1dGUgMkQgcG9pbnRzIHdpdGggeCBhbmQgeSBjb29yZGluYXRlcy5cbiAqL1xuZXhwb3J0IGNsYXNzIFBvaW50cyB7XG4gICAgc3RhdGljIGxlbmd0aChhKSB7XG4gICAgICAgIHJldHVybiBNYXRoLnNxcnQoYS54ICogYS54ICsgYS55ICogYS55KVxuICAgIH1cblxuICAgIHN0YXRpYyBub3JtYWxpemUocCkge1xuICAgICAgICBsZXQgbGVuID0gdGhpcy5sZW5ndGgocClcbiAgICAgICAgcmV0dXJuIHRoaXMubXVsdGlwbHlTY2FsYXIocCwgMSAvIGxlbilcbiAgICB9XG5cbiAgICBzdGF0aWMgbWVhbihhLCBiKSB7XG4gICAgICAgIHJldHVybiB7IHg6IChhLnggKyBiLngpIC8gMiwgeTogKGEueSArIGIueSkgLyAyIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgc3VidHJhY3QoYSwgYikge1xuICAgICAgICByZXR1cm4geyB4OiBhLnggLSBiLngsIHk6IGEueSAtIGIueSB9XG4gICAgfVxuXG4gICAgc3RhdGljIG11bHRpcGx5KGEsIGIpIHtcbiAgICAgICAgcmV0dXJuIHsgeDogYS54ICogYi54LCB5OiBhLnkgKiBiLnkgfVxuICAgIH1cblxuICAgIHN0YXRpYyBkaXZpZGUoYSwgYikge1xuICAgICAgICByZXR1cm4geyB4OiBhLnggLyBiLngsIHk6IGEueSAvIGIueSB9XG4gICAgfVxuXG4gICAgc3RhdGljIG11bHRpcGx5U2NhbGFyKGEsIGIpIHtcbiAgICAgICAgcmV0dXJuIHsgeDogYS54ICogYiwgeTogYS55ICogYiB9XG4gICAgfVxuXG4gICAgc3RhdGljIGFkZChhLCBiKSB7XG4gICAgICAgIHJldHVybiB7IHg6IGEueCArIGIueCwgeTogYS55ICsgYi55IH1cbiAgICB9XG5cbiAgICBzdGF0aWMgbmVnYXRlKHApIHtcbiAgICAgICAgcmV0dXJuIHsgeDogLXAueCwgeTogLXAueSB9XG4gICAgfVxuXG4gICAgc3RhdGljIGFuZ2xlKHAxLCBwMikge1xuICAgICAgICByZXR1cm4gTWF0aC5hdGFuMihwMS55IC0gcDIueSwgcDEueCAtIHAyLngpXG4gICAgfVxuXG4gICAgc3RhdGljIG5vcm1hbGl6ZWRBbmdsZShwMSwgcDIpIHtcbiAgICAgICAgcmV0dXJuIEFuZ2xlLm5vcm1hbGl6ZSh0aGlzLmFuZ2xlKHAxLCBwMikpXG4gICAgfVxuXG4gICAgc3RhdGljIG5vcm1hbGl6ZWQyQW5nbGUocDEsIHAyKSB7XG4gICAgICAgIHJldHVybiBBbmdsZS5ub3JtYWxpemUyKHRoaXMuYW5nbGUocDEsIHAyKSlcbiAgICB9XG5cbiAgICBzdGF0aWMgYXJjKHAsIGFscGhhLCByYWRpdXMpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHg6IHAueCArIHJhZGl1cyAqIE1hdGguY29zKGFscGhhKSxcbiAgICAgICAgICAgIHk6IHAueSArIHJhZGl1cyAqIE1hdGguc2luKGFscGhhKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIGRpc3RhbmNlKGEsIGIpIHtcbiAgICAgICAgbGV0IGR4ID0gYS54IC0gYi54XG4gICAgICAgIGxldCBkeSA9IGEueSAtIGIueVxuICAgICAgICByZXR1cm4gTWF0aC5zcXJ0KGR4ICogZHggKyBkeSAqIGR5KVxuICAgIH1cblxuICAgIHN0YXRpYyBmcm9tUGFnZVRvTm9kZShlbGVtZW50LCBwKSB7XG4gICAgICAgIC8vICAgIGlmICh3aW5kb3cud2Via2l0Q29udmVydFBvaW50RnJvbVBhZ2VUb05vZGUpIHtcbiAgICAgICAgLy8gICAgICAgICAgICAgcmV0dXJuIHdpbmRvdy53ZWJraXRDb252ZXJ0UG9pbnRGcm9tUGFnZVRvTm9kZShlbGVtZW50LFxuICAgICAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IFdlYktpdFBvaW50KHAueCwgcC55KSlcbiAgICAgICAgLy8gICAgICAgICB9XG4gICAgICAgIHJldHVybiB3aW5kb3cuY29udmVydFBvaW50RnJvbVBhZ2VUb05vZGUoZWxlbWVudCwgcC54LCBwLnkpXG4gICAgfVxuXG4gICAgc3RhdGljIGZyb21Ob2RlVG9QYWdlKGVsZW1lbnQsIHApIHtcbiAgICAgICAgLy8gIGlmICh3aW5kb3cud2Via2l0Q29udmVydFBvaW50RnJvbU5vZGVUb1BhZ2UpIHtcbiAgICAgICAgLy8gICAgICAgICAgICAgcmV0dXJuIHdpbmRvdy53ZWJraXRDb252ZXJ0UG9pbnRGcm9tTm9kZVRvUGFnZShlbGVtZW50LFxuICAgICAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IFdlYktpdFBvaW50KHAueCwgcC55KSlcbiAgICAgICAgLy8gICAgICAgICB9XG4gICAgICAgIHJldHVybiB3aW5kb3cuY29udmVydFBvaW50RnJvbU5vZGVUb1BhZ2UoZWxlbWVudCwgcC54LCBwLnkpXG4gICAgfVxufVxuXG4vKipcbiAqIEEgaGVscGVyIGNsYXNzIGZvciBjb21tb24gc2V0IG9wZXJhdGlvbnMuXG4gKlxuICogQGV4cG9ydFxuICogQGNsYXNzIFNldHNcbiAqL1xuZXhwb3J0IGNsYXNzIFNldHMge1xuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgaW50ZXJzZWN0aW9uIG9mIGFsbCBzZXRzXG4gICAgICogaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzE5MzA4OTQvamF2YXNjcmlwdC1zZXQtZGF0YS1zdHJ1Y3R1cmUtaW50ZXJzZWN0XG4gICAgICogQHN0YXRpY1xuICAgICAqIEBwYXJhbSB7Kn0gc2V0c1xuICAgICAqIEByZXR1cm5zXG4gICAgICogQG1lbWJlcm9mIFNldHNcbiAgICAgKi9cbiAgICBzdGF0aWMgaW50ZXJzZWN0KC4uLnNldHMpIHtcbiAgICAgICAgaWYgKCFzZXRzLmxlbmd0aCkgcmV0dXJuIG5ldyBTZXQoKVxuICAgICAgICBjb25zdCBpID0gc2V0cy5yZWR1Y2UoKG0sIHMsIGkpID0+IHMuc2l6ZSA8IHNldHNbbV0uc2l6ZSA/IGkgOiBtLCAwKVxuICAgICAgICBjb25zdCBbc21hbGxlc3RdID0gc2V0cy5zcGxpY2UoaSwgMSlcbiAgICAgICAgY29uc3QgcmVzID0gbmV3IFNldCgpXG4gICAgICAgIGZvciAobGV0IHZhbCBvZiBzbWFsbGVzdClcbiAgICAgICAgICAgIGlmIChzZXRzLmV2ZXJ5KHMgPT4gcy5oYXModmFsKSkpXG4gICAgICAgICAgICAgICAgcmVzLmFkZCh2YWwpXG4gICAgICAgIHJldHVybiByZXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSB1bmlvbiBvZiBhbGwgc2V0c1xuICAgICAqXG4gICAgICogQHN0YXRpY1xuICAgICAqIEBwYXJhbSB7Kn0gc2V0c1xuICAgICAqIEByZXR1cm5zXG4gICAgICogQG1lbWJlcm9mIFNldHNcbiAgICAgKi9cbiAgICBzdGF0aWMgdW5pb24oLi4uc2V0cykge1xuICAgICAgICBsZXQgcmVzdWx0ID0gbmV3IFNldCgpXG4gICAgICAgIGZvciAobGV0IHNldCBvZiBzZXRzKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBtIG9mIHNldCkge1xuICAgICAgICAgICAgICAgIHJlc3VsdC5hZGQobSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgZGlmZmVyZW5jZSBvZiB0aGUgZ2l2ZW4gc2V0cy4gU3RhcnRzIHdpdGggdGhlIGZpcnN0IHNldCBhbmQgcmVtb3ZpbmcgYWxsIGVsZW1lbnRzIG9mIHRoZSBmb2xsb3dpbmcgc2V0cy5cbiAgICAgKlxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0geyp9IHNldFxuICAgICAqIEBwYXJhbSB7Kn0gc2V0c1xuICAgICAqIEByZXR1cm5zXG4gICAgICogQG1lbWJlcm9mIFNldHNcbiAgICAgKi9cbiAgICBzdGF0aWMgZGlmZmVyZW5jZShzZXQsIC4uLnNldHMpIHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IG5ldyBTZXQoKVxuICAgICAgICBmb3IgKGxldCBtIG9mIHNldCkge1xuICAgICAgICAgICAgcmVzdWx0LmFkZChtKVxuICAgICAgICB9XG4gICAgICAgIGZvciAobGV0IHMgb2Ygc2V0cykge1xuICAgICAgICAgICAgZm9yIChsZXQgbSBvZiBzKSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0LmRlbGV0ZShtKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG59XG5cbi8qKiBTdGF0aWMgbWV0aG9kcyB0byBjb21wdXRlIGFuZ2xlcy5cbiAqL1xuZXhwb3J0IGNsYXNzIEFuZ2xlIHtcblxuICAgIHN0YXRpYyBub3JtYWxpemUoYW5nbGUpIHtcbiAgICAgICAgbGV0IFRBVSA9IE1hdGguUEkgKiAyLjBcbiAgICAgICAgd2hpbGUgKGFuZ2xlID4gTWF0aC5QSSkge1xuICAgICAgICAgICAgYW5nbGUgLT0gVEFVXG4gICAgICAgIH1cbiAgICAgICAgd2hpbGUgKGFuZ2xlIDwgLU1hdGguUEkpIHtcbiAgICAgICAgICAgIGFuZ2xlICs9IFRBVVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBhbmdsZVxuICAgIH1cblxuICAgIHN0YXRpYyBub3JtYWxpemUyKGFuZ2xlKSB7XG4gICAgICAgIGxldCBUQVUgPSBNYXRoLlBJICogMi4wXG4gICAgICAgIHdoaWxlIChhbmdsZSA+IFRBVSkge1xuICAgICAgICAgICAgYW5nbGUgLT0gVEFVXG4gICAgICAgIH1cbiAgICAgICAgd2hpbGUgKGFuZ2xlIDwgMCkge1xuICAgICAgICAgICAgYW5nbGUgKz0gVEFVXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGFuZ2xlXG4gICAgfVxuXG4gICAgc3RhdGljIG5vcm1hbGl6ZURlZ3JlZShhbmdsZSkge1xuICAgICAgICBsZXQgZnVsbCA9IDM2MC4wXG4gICAgICAgIHdoaWxlIChhbmdsZSA+IDE4MC4wKSB7XG4gICAgICAgICAgICBhbmdsZSAtPSBmdWxsXG4gICAgICAgIH1cbiAgICAgICAgd2hpbGUgKGFuZ2xlIDwgLTE4MC4wKSB7XG4gICAgICAgICAgICBhbmdsZSArPSBmdWxsXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGFuZ2xlXG4gICAgfVxuXG4gICAgc3RhdGljIG5vcm1hbGl6ZWREaWZmKGEsIGIpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9ybWFsaXplKHRoaXMuZGlmZihhLCBiKSlcbiAgICB9XG5cbiAgICBzdGF0aWMgbm9ybWFsaXplZDJEaWZmKGEsIGIpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9ybWFsaXplMih0aGlzLmRpZmYoYSwgYikpXG4gICAgfVxuXG4gICAgc3RhdGljIGRpZmYoYSwgYikge1xuICAgICAgICByZXR1cm4gTWF0aC5hdGFuMihNYXRoLnNpbihhIC0gYiksIE1hdGguY29zKGEgLSBiKSlcbiAgICB9XG5cbiAgICBzdGF0aWMgZGVncmVlMnJhZGlhbihkZWdyZWUpIHtcbiAgICAgICAgcmV0dXJuIE1hdGguUEkgKiBkZWdyZWUgLyAxODAuMFxuICAgIH1cblxuICAgIHN0YXRpYyByYWRpYW4yZGVncmVlKHJhZCkge1xuICAgICAgICByZXR1cm4gMTgwLjAgLyBNYXRoLlBJICogcmFkXG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgRWxlbWVudHMge1xuICAgIHN0YXRpYyBzZXRTdHlsZShlbGVtZW50LCBzdHlsZXMpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IGluIHN0eWxlcykge1xuICAgICAgICAgICAgZWxlbWVudC5zdHlsZVtrZXldID0gc3R5bGVzW2tleV1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyBhZGRDbGFzcyhlbGVtZW50LCBjc3NDbGFzcykge1xuICAgICAgICBlbGVtZW50LmNsYXNzTGlzdC5hZGQoY3NzQ2xhc3MpXG4gICAgfVxuXG4gICAgc3RhdGljIHJlbW92ZUNsYXNzKGVsZW1lbnQsIGNzc0NsYXNzKSB7XG4gICAgICAgIGVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZShjc3NDbGFzcylcbiAgICB9XG5cbiAgICBzdGF0aWMgdG9nZ2xlQ2xhc3MoZWxlbWVudCwgY3NzQ2xhc3MpIHtcbiAgICAgICAgZWxlbWVudC5jbGFzc0xpc3QudG9nZ2xlKGNzc0NsYXNzKVxuICAgIH1cblxuICAgIHN0YXRpYyBoYXNDbGFzcyhlbGVtZW50LCBjc3NDbGFzcykge1xuICAgICAgICByZXR1cm4gZWxlbWVudC5jbGFzc0xpc3QuY29udGFpbnMoY3NzQ2xhc3MpXG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgTWFwUHJveHkge1xuICAgIC8qIFRoaXMgY2xhc3MgaXMgbmVlZGVkIGlmIHdlIHdhbnQgdG8gdXNlIHRoZSBpbnRlcmFjdGlvbiBjbGFzc2VzXG4gICAgaW4gRmlyZWZveCA0NS44IGFuZCBtb2Rlcm4gQnJvd3NlcnMuXG5cbiAgICBBIHdvcmthcm91bmQgZm9yIGh0dHBzOi8vZ2l0aHViLmNvbS9iYWJlbC9iYWJlbC9pc3N1ZXMvMjMzNFxuICAqL1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLm1hcCA9IG5ldyBNYXAoKVxuICAgIH1cblxuICAgIGdldCBzaXplKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5tYXAuc2l6ZVxuICAgIH1cblxuICAgIGdldChrZXkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFwLmdldChrZXkpXG4gICAgfVxuXG4gICAgc2V0KGtleSwgdmFsdWUpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFwLnNldChrZXksIHZhbHVlKVxuICAgIH1cblxuICAgIGRlbGV0ZShrZXkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFwLmRlbGV0ZShrZXkpXG4gICAgfVxuXG4gICAgY2xlYXIoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLm1hcC5jbGVhcigpXG4gICAgfVxuXG4gICAgaGFzKGtleSkge1xuICAgICAgICByZXR1cm4gdGhpcy5tYXAuaGFzKGtleSlcbiAgICB9XG5cbiAgICBrZXlzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5tYXAua2V5cygpXG4gICAgfVxuXG4gICAgdmFsdWVzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5tYXAudmFsdWVzKClcbiAgICB9XG5cbiAgICBlbnRyaWVzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5tYXAuZW50cmllcygpXG4gICAgfVxuXG4gICAgZm9yRWFjaChmdW5jKSB7XG4gICAgICAgIHRoaXMubWFwLmZvckVhY2goZnVuYylcbiAgICB9XG59XG5cbi8qIEJhc2VkIG9tIGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL2N3bGVvbmFyZC9lMTI0ZDYzMjM4YmRhN2EzY2JmYSAqL1xuZXhwb3J0IGNsYXNzIFBvbHlnb24ge1xuICAgIC8qXG4gICAgICogIFRoaXMgaXMgdGhlIFBvbHlnb24gY29uc3RydWN0b3IuIEFsbCBwb2ludHMgYXJlIGNlbnRlci1yZWxhdGl2ZS5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihjZW50ZXIpIHtcbiAgICAgICAgdGhpcy5wb2ludHMgPSBuZXcgQXJyYXkoKVxuICAgICAgICB0aGlzLmNlbnRlciA9IGNlbnRlclxuICAgIH1cblxuICAgIC8qXG4gICAgICogIFBvaW50IHggYW5kIHkgdmFsdWVzIHNob3VsZCBiZSByZWxhdGl2ZSB0byB0aGUgY2VudGVyLlxuICAgICAqL1xuICAgIGFkZFBvaW50KHApIHtcbiAgICAgICAgdGhpcy5wb2ludHMucHVzaChwKVxuICAgIH1cblxuICAgIC8qXG4gICAgICogIFBvaW50IHggYW5kIHkgdmFsdWVzIHNob3VsZCBiZSBhYnNvbHV0ZSBjb29yZGluYXRlcy5cbiAgICAgKi9cbiAgICBhZGRBYnNvbHV0ZVBvaW50KHApIHtcbiAgICAgICAgdGhpcy5wb2ludHMucHVzaCh7IHg6IHAueCAtIHRoaXMuY2VudGVyLngsIHk6IHAueSAtIHRoaXMuY2VudGVyLnkgfSlcbiAgICB9XG5cbiAgICAvKlxuICAgICAqIFJldHVybnMgdGhlIG51bWJlciBvZiBzaWRlcy4gRXF1YWwgdG8gdGhlIG51bWJlciBvZiB2ZXJ0aWNlcy5cbiAgICAgKi9cbiAgICBnZXROdW1iZXJPZlNpZGVzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5wb2ludHMubGVuZ3RoXG4gICAgfVxuXG4gICAgLypcbiAgICAgKiByb3RhdGUgdGhlIHBvbHlnb24gYnkgYSBudW1iZXIgb2YgcmFkaWFuc1xuICAgICAqL1xuICAgIHJvdGF0ZShyYWRzKSB7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5wb2ludHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGxldCB4ID0gdGhpcy5wb2ludHNbaV0ueFxuICAgICAgICAgICAgbGV0IHkgPSB0aGlzLnBvaW50c1tpXS55XG4gICAgICAgICAgICB0aGlzLnBvaW50c1tpXS54ID0gTWF0aC5jb3MocmFkcykgKiB4IC0gTWF0aC5zaW4ocmFkcykgKiB5XG4gICAgICAgICAgICB0aGlzLnBvaW50c1tpXS55ID0gTWF0aC5zaW4ocmFkcykgKiB4ICsgTWF0aC5jb3MocmFkcykgKiB5XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKlxuICAgICAqICBUaGUgZHJhdyBmdW5jdGlvbiB0YWtlcyBhcyBhIHBhcmFtZXRlciBhIENvbnRleHQgb2JqZWN0IGZyb21cbiAgICAgKiAgYSBDYW52YXMgZWxlbWVudCBhbmQgZHJhd3MgdGhlIHBvbHlnb24gb24gaXQuXG4gICAgICovXG4gICAgZHJhdyhjb250ZXh0LCB7IGxpbmVXaWR0aCA9IDIsIHN0cm9rZSA9ICcjMDAwMDAwJywgZmlsbCA9IG51bGwgfSA9IHt9KSB7XG4gICAgICAgIGNvbnRleHQuYmVnaW5QYXRoKClcbiAgICAgICAgY29udGV4dC5tb3ZlVG8oXG4gICAgICAgICAgICB0aGlzLnBvaW50c1swXS54ICsgdGhpcy5jZW50ZXIueCxcbiAgICAgICAgICAgIHRoaXMucG9pbnRzWzBdLnkgKyB0aGlzLmNlbnRlci55XG4gICAgICAgIClcbiAgICAgICAgZm9yIChsZXQgaSA9IDE7IGkgPCB0aGlzLnBvaW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgY29udGV4dC5saW5lVG8oXG4gICAgICAgICAgICAgICAgdGhpcy5wb2ludHNbaV0ueCArIHRoaXMuY2VudGVyLngsXG4gICAgICAgICAgICAgICAgdGhpcy5wb2ludHNbaV0ueSArIHRoaXMuY2VudGVyLnlcbiAgICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgICBjb250ZXh0LmNsb3NlUGF0aCgpXG4gICAgICAgIGNvbnRleHQubGluZVdpZHRoID0gbGluZVdpZHRoXG4gICAgICAgIGlmIChzdHJva2UpIHtcbiAgICAgICAgICAgIGNvbnRleHQuc3Ryb2tlU3R5bGUgPSBzdHJva2VcbiAgICAgICAgICAgIGNvbnRleHQuc3Ryb2tlKClcbiAgICAgICAgfVxuICAgICAgICBpZiAoZmlsbCkge1xuICAgICAgICAgICAgY29udGV4dC5maWxsU3R5bGUgPSBmaWxsXG4gICAgICAgICAgICBjb250ZXh0LmZpbGwoKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgYWJzb2x1dGVQb2ludHMoKSB7XG4gICAgICAgIGxldCByZXN1bHQgPSBuZXcgQXJyYXkoKVxuICAgICAgICBmb3IgKGxldCBwIG9mIHRoaXMucG9pbnRzKSB7XG4gICAgICAgICAgICByZXN1bHQucHVzaChQb2ludHMuYWRkKHAsIHRoaXMuY2VudGVyKSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgfVxuXG4gICAgZmxhdEFic29sdXRlUG9pbnRzKCkge1xuICAgICAgICBsZXQgcmVzdWx0ID0gbmV3IEFycmF5KClcbiAgICAgICAgZm9yIChsZXQgcCBvZiB0aGlzLnBvaW50cykge1xuICAgICAgICAgICAgbGV0IGEgPSBQb2ludHMuYWRkKHAsIHRoaXMuY2VudGVyKVxuICAgICAgICAgICAgcmVzdWx0LnB1c2goYS54KVxuICAgICAgICAgICAgcmVzdWx0LnB1c2goYS55KVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG5cbiAgICAvKlxuICAgICAqICBUaGlzIGZ1bmN0aW9uIHJldHVybnMgdHJ1ZSBpZiB0aGUgZ2l2ZW4gcG9pbnQgaXMgaW5zaWRlIHRoZSBwb2x5Z29uLFxuICAgICAqICBhbmQgZmFsc2Ugb3RoZXJ3aXNlLlxuICAgICAqL1xuICAgIGNvbnRhaW5zUG9pbnQocG50KSB7XG4gICAgICAgIGxldCBudmVydCA9IHRoaXMucG9pbnRzLmxlbmd0aFxuICAgICAgICBsZXQgdGVzdHggPSBwbnQueFxuICAgICAgICBsZXQgdGVzdHkgPSBwbnQueVxuXG4gICAgICAgIGxldCB2ZXJ0eCA9IG5ldyBBcnJheSgpXG4gICAgICAgIGZvciAobGV0IHEgPSAwOyBxIDwgdGhpcy5wb2ludHMubGVuZ3RoOyBxKyspIHtcbiAgICAgICAgICAgIHZlcnR4LnB1c2godGhpcy5wb2ludHNbcV0ueCArIHRoaXMuY2VudGVyLngpXG4gICAgICAgIH1cblxuICAgICAgICBsZXQgdmVydHkgPSBuZXcgQXJyYXkoKVxuICAgICAgICBmb3IgKGxldCB3ID0gMDsgdyA8IHRoaXMucG9pbnRzLmxlbmd0aDsgdysrKSB7XG4gICAgICAgICAgICB2ZXJ0eS5wdXNoKHRoaXMucG9pbnRzW3ddLnkgKyB0aGlzLmNlbnRlci55KVxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGksXG4gICAgICAgICAgICBqID0gMFxuICAgICAgICBsZXQgYyA9IGZhbHNlXG4gICAgICAgIGZvciAoaSA9IDAsIGogPSBudmVydCAtIDE7IGkgPCBudmVydDsgaiA9IGkrKykge1xuICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgIHZlcnR5W2ldID4gdGVzdHkgIT0gdmVydHlbal0gPiB0ZXN0eSAmJlxuICAgICAgICAgICAgICAgIHRlc3R4IDxcbiAgICAgICAgICAgICAgICAodmVydHhbal0gLSB2ZXJ0eFtpXSkgKlxuICAgICAgICAgICAgICAgICh0ZXN0eSAtIHZlcnR5W2ldKSAvXG4gICAgICAgICAgICAgICAgKHZlcnR5W2pdIC0gdmVydHlbaV0pICtcbiAgICAgICAgICAgICAgICB2ZXJ0eFtpXVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIGMgPSAhY1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBjXG4gICAgfVxuXG4gICAgbXVsdGlwbHlTY2FsYXIoc2NhbGUpIHtcbiAgICAgICAgbGV0IGNlbnRlciA9IFBvaW50cy5tdWx0aXBseVNjYWxhcih0aGlzLmNlbnRlciwgc2NhbGUpXG4gICAgICAgIGxldCBjbG9uZSA9IG5ldyBQb2x5Z29uKGNlbnRlcilcbiAgICAgICAgZm9yIChsZXQgcCBvZiB0aGlzLnBvaW50cykge1xuICAgICAgICAgICAgY2xvbmUuYWRkUG9pbnQoUG9pbnRzLm11bHRpcGx5U2NhbGFyKHAsIHNjYWxlKSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY2xvbmVcbiAgICB9XG5cbiAgICAvKlxuICAgICAqICBUbyBkZXRlY3QgaW50ZXJzZWN0aW9uIHdpdGggYW5vdGhlciBQb2x5Z29uIG9iamVjdCwgdGhpc1xuICAgICAqICBmdW5jdGlvbiB1c2VzIHRoZSBTZXBhcmF0aW5nIEF4aXMgVGhlb3JlbS4gSXQgcmV0dXJucyBmYWxzZVxuICAgICAqICBpZiB0aGVyZSBpcyBubyBpbnRlcnNlY3Rpb24sIG9yIGFuIG9iamVjdCBpZiB0aGVyZSBpcy4gVGhlIG9iamVjdFxuICAgICAqICBjb250YWlucyAyIGZpZWxkcywgb3ZlcmxhcCBhbmQgYXhpcy4gTW92aW5nIHRoZSBwb2x5Z29uIGJ5IG92ZXJsYXBcbiAgICAgKiAgb24gYXhpcyB3aWxsIGdldCB0aGUgcG9seWdvbnMgb3V0IG9mIGludGVyc2VjdGlvbi5cbiAgICAgKi9cbiAgICBpbnRlcnNlY3RzV2l0aChvdGhlcikge1xuICAgICAgICBsZXQgYXhpcyA9IHsgeDogMCwgeTogMCB9XG4gICAgICAgIGxldCB0bXAsIG1pbkEsIG1heEEsIG1pbkIsIG1heEJcbiAgICAgICAgbGV0IHNpZGUsIGlcbiAgICAgICAgbGV0IHNtYWxsZXN0ID0gbnVsbFxuICAgICAgICBsZXQgb3ZlcmxhcCA9IDk5OTk5OTk5XG5cbiAgICAgICAgLyogdGVzdCBwb2x5Z29uIEEncyBzaWRlcyAqL1xuICAgICAgICBmb3IgKHNpZGUgPSAwOyBzaWRlIDwgdGhpcy5nZXROdW1iZXJPZlNpZGVzKCk7IHNpZGUrKykge1xuICAgICAgICAgICAgLyogZ2V0IHRoZSBheGlzIHRoYXQgd2Ugd2lsbCBwcm9qZWN0IG9udG8gKi9cbiAgICAgICAgICAgIGlmIChzaWRlID09IDApIHtcbiAgICAgICAgICAgICAgICBheGlzLnggPVxuICAgICAgICAgICAgICAgICAgICB0aGlzLnBvaW50c1t0aGlzLmdldE51bWJlck9mU2lkZXMoKSAtIDFdLnkgLVxuICAgICAgICAgICAgICAgICAgICB0aGlzLnBvaW50c1swXS55XG4gICAgICAgICAgICAgICAgYXhpcy55ID1cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wb2ludHNbMF0ueCAtXG4gICAgICAgICAgICAgICAgICAgIHRoaXMucG9pbnRzW3RoaXMuZ2V0TnVtYmVyT2ZTaWRlcygpIC0gMV0ueFxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBheGlzLnggPSB0aGlzLnBvaW50c1tzaWRlIC0gMV0ueSAtIHRoaXMucG9pbnRzW3NpZGVdLnlcbiAgICAgICAgICAgICAgICBheGlzLnkgPSB0aGlzLnBvaW50c1tzaWRlXS54IC0gdGhpcy5wb2ludHNbc2lkZSAtIDFdLnhcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLyogbm9ybWFsaXplIHRoZSBheGlzICovXG4gICAgICAgICAgICB0bXAgPSBNYXRoLnNxcnQoYXhpcy54ICogYXhpcy54ICsgYXhpcy55ICogYXhpcy55KVxuICAgICAgICAgICAgYXhpcy54IC89IHRtcFxuICAgICAgICAgICAgYXhpcy55IC89IHRtcFxuXG4gICAgICAgICAgICAvKiBwcm9qZWN0IHBvbHlnb24gQSBvbnRvIGF4aXMgdG8gZGV0ZXJtaW5lIHRoZSBtaW4vbWF4ICovXG4gICAgICAgICAgICBtaW5BID0gbWF4QSA9IHRoaXMucG9pbnRzWzBdLnggKiBheGlzLnggKyB0aGlzLnBvaW50c1swXS55ICogYXhpcy55XG4gICAgICAgICAgICBmb3IgKGkgPSAxOyBpIDwgdGhpcy5nZXROdW1iZXJPZlNpZGVzKCk7IGkrKykge1xuICAgICAgICAgICAgICAgIHRtcCA9IHRoaXMucG9pbnRzW2ldLnggKiBheGlzLnggKyB0aGlzLnBvaW50c1tpXS55ICogYXhpcy55XG4gICAgICAgICAgICAgICAgaWYgKHRtcCA+IG1heEEpIG1heEEgPSB0bXBcbiAgICAgICAgICAgICAgICBlbHNlIGlmICh0bXAgPCBtaW5BKSBtaW5BID0gdG1wXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvKiBjb3JyZWN0IGZvciBvZmZzZXQgKi9cbiAgICAgICAgICAgIHRtcCA9IHRoaXMuY2VudGVyLnggKiBheGlzLnggKyB0aGlzLmNlbnRlci55ICogYXhpcy55XG4gICAgICAgICAgICBtaW5BICs9IHRtcFxuICAgICAgICAgICAgbWF4QSArPSB0bXBcblxuICAgICAgICAgICAgLyogcHJvamVjdCBwb2x5Z29uIEIgb250byBheGlzIHRvIGRldGVybWluZSB0aGUgbWluL21heCAqL1xuICAgICAgICAgICAgbWluQiA9IG1heEIgPVxuICAgICAgICAgICAgICAgIG90aGVyLnBvaW50c1swXS54ICogYXhpcy54ICsgb3RoZXIucG9pbnRzWzBdLnkgKiBheGlzLnlcbiAgICAgICAgICAgIGZvciAoaSA9IDE7IGkgPCBvdGhlci5nZXROdW1iZXJPZlNpZGVzKCk7IGkrKykge1xuICAgICAgICAgICAgICAgIHRtcCA9IG90aGVyLnBvaW50c1tpXS54ICogYXhpcy54ICsgb3RoZXIucG9pbnRzW2ldLnkgKiBheGlzLnlcbiAgICAgICAgICAgICAgICBpZiAodG1wID4gbWF4QikgbWF4QiA9IHRtcFxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKHRtcCA8IG1pbkIpIG1pbkIgPSB0bXBcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8qIGNvcnJlY3QgZm9yIG9mZnNldCAqL1xuICAgICAgICAgICAgdG1wID0gb3RoZXIuY2VudGVyLnggKiBheGlzLnggKyBvdGhlci5jZW50ZXIueSAqIGF4aXMueVxuICAgICAgICAgICAgbWluQiArPSB0bXBcbiAgICAgICAgICAgIG1heEIgKz0gdG1wXG5cbiAgICAgICAgICAgIC8qIHRlc3QgaWYgbGluZXMgaW50ZXJzZWN0LCBpZiBub3QsIHJldHVybiBmYWxzZSAqL1xuICAgICAgICAgICAgaWYgKG1heEEgPCBtaW5CIHx8IG1pbkEgPiBtYXhCKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGxldCBvID0gbWF4QSA+IG1heEIgPyBtYXhCIC0gbWluQSA6IG1heEEgLSBtaW5CXG4gICAgICAgICAgICAgICAgaWYgKG8gPCBvdmVybGFwKSB7XG4gICAgICAgICAgICAgICAgICAgIG92ZXJsYXAgPSBvXG4gICAgICAgICAgICAgICAgICAgIHNtYWxsZXN0ID0geyB4OiBheGlzLngsIHk6IGF4aXMueSB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLyogdGVzdCBwb2x5Z29uIEIncyBzaWRlcyAqL1xuICAgICAgICBmb3IgKHNpZGUgPSAwOyBzaWRlIDwgb3RoZXIuZ2V0TnVtYmVyT2ZTaWRlcygpOyBzaWRlKyspIHtcbiAgICAgICAgICAgIC8qIGdldCB0aGUgYXhpcyB0aGF0IHdlIHdpbGwgcHJvamVjdCBvbnRvICovXG4gICAgICAgICAgICBpZiAoc2lkZSA9PSAwKSB7XG4gICAgICAgICAgICAgICAgYXhpcy54ID1cbiAgICAgICAgICAgICAgICAgICAgb3RoZXIucG9pbnRzW290aGVyLmdldE51bWJlck9mU2lkZXMoKSAtIDFdLnkgLVxuICAgICAgICAgICAgICAgICAgICBvdGhlci5wb2ludHNbMF0ueVxuICAgICAgICAgICAgICAgIGF4aXMueSA9XG4gICAgICAgICAgICAgICAgICAgIG90aGVyLnBvaW50c1swXS54IC1cbiAgICAgICAgICAgICAgICAgICAgb3RoZXIucG9pbnRzW290aGVyLmdldE51bWJlck9mU2lkZXMoKSAtIDFdLnhcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgYXhpcy54ID0gb3RoZXIucG9pbnRzW3NpZGUgLSAxXS55IC0gb3RoZXIucG9pbnRzW3NpZGVdLnlcbiAgICAgICAgICAgICAgICBheGlzLnkgPSBvdGhlci5wb2ludHNbc2lkZV0ueCAtIG90aGVyLnBvaW50c1tzaWRlIC0gMV0ueFxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvKiBub3JtYWxpemUgdGhlIGF4aXMgKi9cbiAgICAgICAgICAgIHRtcCA9IE1hdGguc3FydChheGlzLnggKiBheGlzLnggKyBheGlzLnkgKiBheGlzLnkpXG4gICAgICAgICAgICBheGlzLnggLz0gdG1wXG4gICAgICAgICAgICBheGlzLnkgLz0gdG1wXG5cbiAgICAgICAgICAgIC8qIHByb2plY3QgcG9seWdvbiBBIG9udG8gYXhpcyB0byBkZXRlcm1pbmUgdGhlIG1pbi9tYXggKi9cbiAgICAgICAgICAgIG1pbkEgPSBtYXhBID0gdGhpcy5wb2ludHNbMF0ueCAqIGF4aXMueCArIHRoaXMucG9pbnRzWzBdLnkgKiBheGlzLnlcbiAgICAgICAgICAgIGZvciAoaSA9IDE7IGkgPCB0aGlzLmdldE51bWJlck9mU2lkZXMoKTsgaSsrKSB7XG4gICAgICAgICAgICAgICAgdG1wID0gdGhpcy5wb2ludHNbaV0ueCAqIGF4aXMueCArIHRoaXMucG9pbnRzW2ldLnkgKiBheGlzLnlcbiAgICAgICAgICAgICAgICBpZiAodG1wID4gbWF4QSkgbWF4QSA9IHRtcFxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKHRtcCA8IG1pbkEpIG1pbkEgPSB0bXBcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8qIGNvcnJlY3QgZm9yIG9mZnNldCAqL1xuICAgICAgICAgICAgdG1wID0gdGhpcy5jZW50ZXIueCAqIGF4aXMueCArIHRoaXMuY2VudGVyLnkgKiBheGlzLnlcbiAgICAgICAgICAgIG1pbkEgKz0gdG1wXG4gICAgICAgICAgICBtYXhBICs9IHRtcFxuXG4gICAgICAgICAgICAvKiBwcm9qZWN0IHBvbHlnb24gQiBvbnRvIGF4aXMgdG8gZGV0ZXJtaW5lIHRoZSBtaW4vbWF4ICovXG4gICAgICAgICAgICBtaW5CID0gbWF4QiA9XG4gICAgICAgICAgICAgICAgb3RoZXIucG9pbnRzWzBdLnggKiBheGlzLnggKyBvdGhlci5wb2ludHNbMF0ueSAqIGF4aXMueVxuICAgICAgICAgICAgZm9yIChpID0gMTsgaSA8IG90aGVyLmdldE51bWJlck9mU2lkZXMoKTsgaSsrKSB7XG4gICAgICAgICAgICAgICAgdG1wID0gb3RoZXIucG9pbnRzW2ldLnggKiBheGlzLnggKyBvdGhlci5wb2ludHNbaV0ueSAqIGF4aXMueVxuICAgICAgICAgICAgICAgIGlmICh0bXAgPiBtYXhCKSBtYXhCID0gdG1wXG4gICAgICAgICAgICAgICAgZWxzZSBpZiAodG1wIDwgbWluQikgbWluQiA9IHRtcFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLyogY29ycmVjdCBmb3Igb2Zmc2V0ICovXG4gICAgICAgICAgICB0bXAgPSBvdGhlci5jZW50ZXIueCAqIGF4aXMueCArIG90aGVyLmNlbnRlci55ICogYXhpcy55XG4gICAgICAgICAgICBtaW5CICs9IHRtcFxuICAgICAgICAgICAgbWF4QiArPSB0bXBcblxuICAgICAgICAgICAgLyogdGVzdCBpZiBsaW5lcyBpbnRlcnNlY3QsIGlmIG5vdCwgcmV0dXJuIGZhbHNlICovXG4gICAgICAgICAgICBpZiAobWF4QSA8IG1pbkIgfHwgbWluQSA+IG1heEIpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbGV0IG8gPSBtYXhBID4gbWF4QiA/IG1heEIgLSBtaW5BIDogbWF4QSAtIG1pbkJcbiAgICAgICAgICAgICAgICBpZiAobyA8IG92ZXJsYXApIHtcbiAgICAgICAgICAgICAgICAgICAgb3ZlcmxhcCA9IG9cbiAgICAgICAgICAgICAgICAgICAgc21hbGxlc3QgPSB7IHg6IGF4aXMueCwgeTogYXhpcy55IH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHsgb3ZlcmxhcDogb3ZlcmxhcCArIDAuMDAxLCBheGlzOiBzbWFsbGVzdCB9XG4gICAgfVxuXG4gICAgc3RhdGljIGZyb21Qb2ludHMocG9pbnRzKSB7XG4gICAgICAgIGxldCBtaW4gPSB7IHg6IE51bWJlci5NQVhfVkFMVUUsIHk6IE51bWJlci5NQVhfVkFMVUUgfVxuICAgICAgICBsZXQgbWF4ID0geyB4OiBOdW1iZXIuTUlOX1ZBTFVFLCB5OiBOdW1iZXIuTUlOX1ZBTFVFIH1cbiAgICAgICAgZm9yIChsZXQgcCBvZiBwb2ludHMpIHtcbiAgICAgICAgICAgIG1pbi54ID0gTWF0aC5taW4ocC54LCBtaW4ueClcbiAgICAgICAgICAgIG1heC54ID0gTWF0aC5tYXgocC54LCBtYXgueClcbiAgICAgICAgICAgIG1pbi55ID0gTWF0aC5taW4ocC55LCBtaW4ueSlcbiAgICAgICAgICAgIG1heC55ID0gTWF0aC5tYXgocC55LCBtYXgueSlcbiAgICAgICAgfVxuICAgICAgICBsZXQgY2VudGVyID0gUG9pbnRzLm1lYW4obWluLCBtYXgpXG4gICAgICAgIGxldCBwb2x5Z29uID0gbmV3IFBvbHlnb24oY2VudGVyKVxuICAgICAgICBmb3IgKGxldCBwIG9mIHBvaW50cykge1xuICAgICAgICAgICAgcG9seWdvbi5hZGRBYnNvbHV0ZVBvaW50KHApXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHBvbHlnb25cbiAgICB9XG59XG5cblxuLyoqXG4gKiBVdGlsIGZ1bmN0aW9ucyB0byBkZWFsIHdpdGggRE9NUmVjdHMuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZWN0IHtcblxuXG4gICAgLyoqXG4gICAgICogVGVzdCBpZiBhIGdpdmVuIHBvaW50IGlzIGNvbnRhaW5lZCBieSB0aGUgcHJvdmlkZWQgUmVjdC5cbiAgICAgKlxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge0RPTVJlY3R9IHJlY3QgLSBSZWN0YW5nbGUgdG8gY2hlY2sgdGhlIGNvbGxpc2lvbiB3aXRoLlxuICAgICAqIEBwYXJhbSB7UG9pbnR9IHBvaW50IC0gUG9pbnQgdGhhdCBzaG91bGQgYmUgdGVzdGVkLlxuICAgICAqIEByZXR1cm5zIHtib29sZWFufSAtIFRydWUgaWYgcG9pbnQgaXMgaW5zaWRlIG9mIHJlY3QsIG90aGVyd2lzZSBmYWxzZS5cbiAgICAgKiBAbWVtYmVyb2YgUmVjdFxuICAgICAqL1xuICAgIHN0YXRpYyBjb250YWlucyhyZWN0LCBwb2ludCkge1xuICAgICAgICByZXR1cm4gKHBvaW50LnggPiByZWN0LmxlZnQgJiZcbiAgICAgICAgICAgIHBvaW50LnggPCByZWN0LnggKyByZWN0LnJpZ2h0XG4gICAgICAgICAgICAmJiBwb2ludC55ID4gcmVjdC50b3AgJiYgcG9pbnQueSA8IHJlY3QuYm90dG9tKVxuICAgIH1cblxuXG4gICAgLyoqXG4gICAgICpSZXR1cm5zIHRoZSBwb3NpdGlvbiBvZiBhbiByZWN0IGFzIHBvaW50IG9iamVjdC5cbiAgICAgKlxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge1JlY3R9IHJlY3QgLSBUaGUgcmVjdGFuZ2xlIHdlIHdhbnQgdG8gZ2V0IHRoZSBwb3NpdGlvbiBmcm9tLlxuICAgICAqIEByZXR1cm5zIHtQb2ludH0gLSBSZXR1cm5zIHRoZSBwb3NpdGlvbiBhcyBQb2ludC5cbiAgICAgKiBAbWVtYmVyb2YgUmVjdFxuICAgICAqL1xuICAgIHN0YXRpYyBnZXRQb3NpdGlvbihyZWN0KSB7XG4gICAgICAgIHJldHVybiB7IHg6IHJlY3QueCwgeTogcmVjdC55IH1cbiAgICB9XG59XG5cbi8qKiBTdHJpbmcgdXRpbGl0eSBmdW5jdGlvbnMgKi9cblxuZXhwb3J0IGNsYXNzIFN0cmluZ3Mge1xuXG4gICAgc3RhdGljIHRvVXBwZXJDYXNlRmlyc3RDaGFyKHN0cikge1xuICAgICAgICByZXR1cm4gc3RyLnN1YnN0cigwLCAxKS50b1VwcGVyQ2FzZSgpICsgc3RyLnN1YnN0cigxKVxuICAgIH1cblxuICAgIHN0YXRpYyB0b0xvd2VyQ2FzZUZpcnN0Q2hhcihzdHIpIHtcbiAgICAgICAgcmV0dXJuIHN0ci5zdWJzdHIoMCwgMSkudG9Mb3dlckNhc2UoKSArIHN0ci5zdWJzdHIoMSlcbiAgICB9XG5cbiAgICBzdGF0aWMgdG9VcHBlckNhc2VFYWNoV29yZChzdHIsIGRlbGltID0gJyAnKSB7XG4gICAgICAgIHJldHVybiBzdHIuc3BsaXQoZGVsaW0pLm1hcCgodikgPT4gdi50b1VwcGVyQ2FzZUZpcnN0Q2hhcigpKS5qb2luKGRlbGltKVxuICAgIH1cblxuICAgIHN0YXRpYyB0b0xvd2VyQ2FzZUVhY2hXb3JkKHN0ciwgZGVsaW0gPSAnICcpIHtcbiAgICAgICAgcmV0dXJuIHN0ci5zcGxpdChkZWxpbSkubWFwKCh2KSA9PiB2LnRvTG93ZXJDYXNlRmlyc3RDaGFyKCkpLmpvaW4oZGVsaW0pXG4gICAgfVxuXG59XG5cblxuXG4iLCIvKiBnbG9iYWxzIEhhbW1lciwgcHJvcGFnYXRpbmcgKi9cbi8qZXNsaW50IG5vLWNvbnNvbGU6IFtcImVycm9yXCIsIHsgYWxsb3c6IFtcImxvZ1wiLCBcIndhcm5cIiwgXCJpbmZvXCIsIFwiZXJyb3JcIl0gfV0qL1xuXG5pbXBvcnQgSW50ZXJmYWNlIGZyb20gJy4vaW50ZXJmYWNlLmpzJ1xuaW1wb3J0IHsgUG9pbnRzLCBBbmdsZSwgTWFwUHJveHkgfSBmcm9tICcuL3V0aWxzLmpzJ1xuaW1wb3J0IEV2ZW50cyBmcm9tICcuL2V2ZW50cy5qcydcblxuLyoqIEludGVyYWN0aW9uIHBhdHRlcm5zXG5cbiAgICBTZWUgaW50ZXJhY3Rpb24uaHRtbCBmb3IgZXhwbGFuYXRpb25cbiovXG5cbmV4cG9ydCBjbGFzcyBJSW50ZXJhY3Rpb25UYXJnZXQgZXh0ZW5kcyBJbnRlcmZhY2Uge1xuICAgIGNhcHR1cmUoZXZlbnQpIHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZiB0cnVlXG4gICAgfVxuXG4gICAgb25TdGFydChldmVudCwgaW50ZXJhY3Rpb24pIHsgfVxuICAgIG9uTW92ZShldmVudCwgaW50ZXJhY3Rpb24pIHsgfVxuICAgIG9uRW5kKGV2ZW50LCBpbnRlcmFjdGlvbikgeyB9XG5cbiAgICBvbk1vdXNlV2hlZWwoZXZlbnQpIHsgfVxufVxuXG5leHBvcnQgY2xhc3MgSUludGVyYWN0aW9uTWFwcGVyVGFyZ2V0IGV4dGVuZHMgSW50ZXJmYWNlIHtcbiAgICBjYXB0dXJlKGV2ZW50KSB7XG4gICAgICAgIHJldHVybiB0eXBlb2YgdHJ1ZVxuICAgIH1cblxuICAgIGZpbmRUYXJnZXQoZXZlbnQsIGxvY2FsLCBnbG9iYWwpIHtcbiAgICAgICAgcmV0dXJuIElJbnRlcmFjdGlvblRhcmdldFxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIFBvaW50TWFwIGV4dGVuZHMgTWFwUHJveHkge1xuICAgIC8vIENvbGxlY3RzIHRvdWNoIHBvaW50cywgbW91c2UgY29vcmRpbmF0ZXMsIGV0Yy4gYXMga2V5IHZhbHVlIHBhaXJzLlxuICAgIC8vIEtleXMgYXJlIHBvaW50ZXIgYW5kIHRvdWNoIGlkcywgdGhlIHNwZWNpYWwgXCJtb3VzZVwiIGtleS5cbiAgICAvLyBWYWx1ZXMgYXJlIHBvaW50cywgaS5lLiBhbGwgb2JqZWN0cyB3aXRoIG51bWVyaWMgeCBhbmQgeSBwcm9wZXJ0aWVzLlxuICAgIGNvbnN0cnVjdG9yKHBvaW50cyA9IHt9KSB7XG4gICAgICAgIHN1cGVyKClcbiAgICAgICAgZm9yIChsZXQga2V5IGluIHBvaW50cykge1xuICAgICAgICAgICAgdGhpcy5zZXQoa2V5LCBwb2ludHNba2V5XSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHRvU3RyaW5nKCkge1xuICAgICAgICBsZXQgcG9pbnRzID0gW11cbiAgICAgICAgZm9yIChsZXQga2V5IG9mIHRoaXMua2V5cygpKSB7XG4gICAgICAgICAgICBsZXQgdmFsdWUgPSB0aGlzLmdldChrZXkpXG4gICAgICAgICAgICBwb2ludHMucHVzaChgJHtrZXl9Ont4OiR7dmFsdWUueH0sIHk6JHt2YWx1ZS55fX1gKVxuICAgICAgICB9XG4gICAgICAgIGxldCBhdHRycyA9IHBvaW50cy5qb2luKCcsICcpXG4gICAgICAgIHJldHVybiBgW1BvaW50TWFwICR7YXR0cnN9XWBcbiAgICB9XG5cbiAgICBjbG9uZSgpIHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IG5ldyBQb2ludE1hcCgpXG4gICAgICAgIGZvciAobGV0IGtleSBvZiB0aGlzLmtleXMoKSkge1xuICAgICAgICAgICAgbGV0IHZhbHVlID0gdGhpcy5nZXQoa2V5KVxuICAgICAgICAgICAgcmVzdWx0LnNldChrZXksIHsgeDogdmFsdWUueCwgeTogdmFsdWUueSB9KVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG5cbiAgICBrZXlPZih2YWx1ZSkge1xuICAgICAgICBmb3IgKGxldCBrZXkgb2YgdGhpcy5rZXlzKCkpIHtcbiAgICAgICAgICAgIGxldCBwID0gdGhpcy5nZXQoa2V5KVxuICAgICAgICAgICAgaWYgKHAueCA9PSB2YWx1ZS54ICYmIHAueSA9PSB2YWx1ZS55KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGtleVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgfVxuXG4gICAgZmlyc3RLZXkoKSB7XG4gICAgICAgIGZvciAobGV0IGtleSBvZiB0aGlzLmtleXMoKSkge1xuICAgICAgICAgICAgcmV0dXJuIGtleVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgfVxuXG4gICAgZmlyc3QoKSB7XG4gICAgICAgIGZvciAobGV0IGtleSBvZiB0aGlzLmtleXMoKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuZ2V0KGtleSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIGZhcnRoZXN0cygpIHtcbiAgICAgICAgaWYgKHRoaXMuc2l6ZSA9PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICB9XG4gICAgICAgIGxldCBwYWlycyA9IFtdXG4gICAgICAgIGZvciAobGV0IGtleSBvZiB0aGlzLmtleXMoKSkge1xuICAgICAgICAgICAgbGV0IHAgPSB0aGlzLmdldChrZXkpXG4gICAgICAgICAgICBwLmtleSA9IGtleVxuICAgICAgICAgICAgZm9yIChsZXQgayBvZiB0aGlzLmtleXMoKSkge1xuICAgICAgICAgICAgICAgIGxldCBxID0gdGhpcy5nZXQoaylcbiAgICAgICAgICAgICAgICBxLmtleSA9IGtcbiAgICAgICAgICAgICAgICBwYWlycy5wdXNoKFtwLCBxXSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBsZXQgc29ydGVkID0gcGFpcnMuc29ydCgoYSwgYikgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIFBvaW50cy5kaXN0YW5jZShiWzBdLCBiWzFdKSAtIFBvaW50cy5kaXN0YW5jZShhWzBdLCBhWzFdKVxuICAgICAgICB9KVxuICAgICAgICByZXR1cm4gc29ydGVkWzBdXG4gICAgfVxuXG4gICAgbWVhbigpIHtcbiAgICAgICAgaWYgKHRoaXMuc2l6ZSA9PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICB9XG4gICAgICAgIGxldCB4ID0gMC4wLFxuICAgICAgICAgICAgeSA9IDAuMFxuICAgICAgICBmb3IgKGxldCBwIG9mIHRoaXMudmFsdWVzKCkpIHtcbiAgICAgICAgICAgIHggKz0gcC54XG4gICAgICAgICAgICB5ICs9IHAueVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7IHg6IHggLyB0aGlzLnNpemUsIHk6IHkgLyB0aGlzLnNpemUgfVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIEludGVyYWN0aW9uRGVsdGEge1xuICAgIGNvbnN0cnVjdG9yKHgsIHksIHpvb20sIHJvdGF0ZSwgYWJvdXQpIHtcbiAgICAgICAgdGhpcy54ID0geFxuICAgICAgICB0aGlzLnkgPSB5XG4gICAgICAgIHRoaXMuem9vbSA9IHpvb21cbiAgICAgICAgdGhpcy5yb3RhdGUgPSByb3RhdGVcbiAgICAgICAgdGhpcy5hYm91dCA9IGFib3V0XG4gICAgfVxuXG4gICAgdG9TdHJpbmcoKSB7XG4gICAgICAgIGxldCB2YWx1ZXMgPSBbXVxuICAgICAgICBmb3IgKGxldCBrZXkgb2YgT2JqZWN0LmtleXModGhpcykpIHtcbiAgICAgICAgICAgIGxldCB2YWx1ZSA9IHRoaXNba2V5XVxuICAgICAgICAgICAgaWYgKGtleSA9PSAnYWJvdXQnKSB7XG4gICAgICAgICAgICAgICAgdmFsdWVzLnB1c2goYCR7a2V5fTp7eDoke3ZhbHVlLnh9LCB5OiR7dmFsdWUueX19YClcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdmFsdWVzLnB1c2goYCR7a2V5fToke3ZhbHVlfWApXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGV0IGF0dHJzID0gdmFsdWVzLmpvaW4oJywgJylcbiAgICAgICAgcmV0dXJuIGBbSW50ZXJhY3Rpb25EZWx0YSAke2F0dHJzfV1gXG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgSW50ZXJhY3Rpb25Qb2ludHMge1xuICAgIGNvbnN0cnVjdG9yKHBhcmVudCA9IG51bGwpIHtcbiAgICAgICAgdGhpcy5wYXJlbnQgPSBwYXJlbnRcbiAgICAgICAgdGhpcy5jdXJyZW50ID0gbmV3IFBvaW50TWFwKClcbiAgICAgICAgdGhpcy5wcmV2aW91cyA9IG5ldyBQb2ludE1hcCgpXG4gICAgICAgIHRoaXMuc3RhcnQgPSBuZXcgUG9pbnRNYXAoKVxuICAgICAgICB0aGlzLmVuZGVkID0gbmV3IFBvaW50TWFwKClcbiAgICAgICAgdGhpcy50aW1lc3RhbXBzID0gbmV3IE1hcCgpXG4gICAgfVxuXG4gICAgbW92ZWQoa2V5KSB7XG4gICAgICAgIGxldCBjdXJyZW50ID0gdGhpcy5jdXJyZW50LmdldChrZXkpXG4gICAgICAgIGxldCBwcmV2aW91cyA9IHRoaXMucHJldmlvdXMuZ2V0KGtleSlcbiAgICAgICAgcmV0dXJuIFBvaW50cy5zdWJ0cmFjdChjdXJyZW50LCBwcmV2aW91cylcbiAgICB9XG5cbiAgICBtb3ZlKCkge1xuICAgICAgICBsZXQgY3VycmVudCA9IHRoaXMuY3VycmVudC5tZWFuKClcbiAgICAgICAgbGV0IHByZXZpb3VzID0gdGhpcy5wcmV2aW91cy5tZWFuKClcbiAgICAgICAgcmV0dXJuIFBvaW50cy5zdWJ0cmFjdChjdXJyZW50LCBwcmV2aW91cylcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDb21wdXRlcyB0aGUgZGVsdGEgYmV0d2VlbiBwcmV2aW91cyBhbmQgY3VycmVudCBhbmdsZXMuIENvcnJlY3RzXG4gICAgICogdmFsdWUgdGhhdCBhcmUgbGFyZ2VyIHRoYW4gNDXCsFxuICAgICAqIEBwYXJhbSB7Kn0gYVxuICAgICAqIEBwYXJhbSB7Kn0gYlxuICAgICAqIEByZXR1cm5zIGRlbHRhXG4gICAgICovXG4gICAgZGlmZkFuZ2xlKGEsIGIpIHtcbiAgICAgICAgbGV0IGFscGhhID0gTWF0aC5hdGFuMihNYXRoLnNpbihhIC0gYiksIE1hdGguY29zKGEgLSBiKSlcbiAgICAgICAgaWYgKE1hdGguYWJzKGFscGhhKSA+IE1hdGguUEkgLyA0KSB7XG4gICAgICAgICAgICBhbHBoYSAtPSBNYXRoLlBJXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGFscGhhXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ29tcHV0ZXMgdGhlIGRlbHRhIGJldHdlZW4gaW50ZXJhY3Rpb24gcG9pbnRzIGF0IHQgYW5kIHQrMS5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIEludGVyYWN0aW9uRGVsdGFcbiAgICAgKiBAbWVtYmVyb2YgSW50ZXJhY3Rpb25Qb2ludHNcbiAgICAgKi9cbiAgICBkZWx0YSgpIHtcbiAgICAgICAgbGV0IGNzaXplID0gdGhpcy5jdXJyZW50LnNpemVcbiAgICAgICAgbGV0IHBzaXplID0gdGhpcy5wcmV2aW91cy5zaXplXG4gICAgICAgIGlmIChjc2l6ZSA+PSAyICYmIGNzaXplID09IHBzaXplKSB7XG4gICAgICAgICAgICAvLyBSZWR1Y2UgdG8gdGhlIHR3byBmYXJ0aGVzdHMgcG9pbnRzXG4gICAgICAgICAgICBsZXQgY3VycmVudCA9IHRoaXMuY3VycmVudC5mYXJ0aGVzdHMoKVxuXG4gICAgICAgICAgICBsZXQgYzEgPSBjdXJyZW50WzBdXG4gICAgICAgICAgICBsZXQgYzIgPSBjdXJyZW50WzFdXG5cbiAgICAgICAgICAgIGxldCBwMSA9IHRoaXMucHJldmlvdXMuZ2V0KGMxLmtleSlcbiAgICAgICAgICAgIGxldCBwMiA9IHRoaXMucHJldmlvdXMuZ2V0KGMyLmtleSlcblxuICAgICAgICAgICAgLy9sZXQgcDEgPSBwcmV2aW91c1swXVxuICAgICAgICAgICAgLy9sZXQgcDIgPSBwcmV2aW91c1sxXVxuXG4gICAgICAgICAgICBsZXQgZDEgPSBQb2ludHMuc3VidHJhY3QoYzEsIHAxKVxuICAgICAgICAgICAgbGV0IGQyID0gUG9pbnRzLnN1YnRyYWN0KGMyLCBwMilcbiAgICAgICAgICAgIGxldCBjbSA9IFBvaW50cy5tZWFuKGMxLCBjMilcbiAgICAgICAgICAgIC8vbGV0IHBtID0gUG9pbnRzLm1lYW4ocDEsIHAyKVxuICAgICAgICAgICAgLy8gVU86IFVzaW5nIHRoZSBtZWFuIGxlYWQgdG8ganVtcHMgYmV0d2VlbiB0aW1lIHNsaWNlcyB3aXRoIDMgYW5kIDIgZmluZ2Vyc1xuICAgICAgICAgICAgLy8gV2UgdXNlIHRoZSBtZWFuIG9mIGRlbHRhcyBpbnN0ZWFkXG4gICAgICAgICAgICBsZXQgZGVsdGEgPSBQb2ludHMubWVhbihkMSwgZDIpIC8vUG9pbnRzLnN1YnRyYWN0KGNtLCBwbSlcbiAgICAgICAgICAgIGxldCB6b29tID0gMS4wXG4gICAgICAgICAgICBsZXQgZGlzdGFuY2UxID0gUG9pbnRzLmRpc3RhbmNlKHAxLCBwMilcbiAgICAgICAgICAgIGxldCBkaXN0YW5jZTIgPSBQb2ludHMuZGlzdGFuY2UoYzEsIGMyKVxuICAgICAgICAgICAgaWYgKGRpc3RhbmNlMSAhPSAwICYmIGRpc3RhbmNlMiAhPSAwKSB7XG4gICAgICAgICAgICAgICAgem9vbSA9IGRpc3RhbmNlMiAvIGRpc3RhbmNlMVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGV0IGN1cnJlbnRBbmdsZSA9IFBvaW50cy5hbmdsZShjMSwgYzIpXG4gICAgICAgICAgICBsZXQgcHJldmlvdXNBbmdsZSA9IFBvaW50cy5hbmdsZShwMSwgcDIpXG4gICAgICAgICAgICBsZXQgYWxwaGEgPSB0aGlzLmRpZmZBbmdsZShjdXJyZW50QW5nbGUsIHByZXZpb3VzQW5nbGUpXG4gICAgICAgICAgICByZXR1cm4gbmV3IEludGVyYWN0aW9uRGVsdGEoZGVsdGEueCwgZGVsdGEueSwgem9vbSwgYWxwaGEsIGNtKVxuICAgICAgICB9IGVsc2UgaWYgKGNzaXplID09IDEgJiYgcHNpemUgPT0gMSAmJiB0aGlzLmN1cnJlbnQuZmlyc3RLZXkoKSA9PSB0aGlzLnByZXZpb3VzLmZpcnN0S2V5KCkpIHtcbiAgICAgICAgICAgIC8vIFdlIG5lZWQgdG8gZW5zdXJlIHRoYXQgdGhlIGtleXMgYXJlIHRoZSBzYW1lXG4gICAgICAgICAgICBsZXQgY3VycmVudCA9IHRoaXMuY3VycmVudC5maXJzdCgpXG4gICAgICAgICAgICBsZXQgcHJldmlvdXMgPSB0aGlzLnByZXZpb3VzLmZpcnN0KClcbiAgICAgICAgICAgIGxldCBkZWx0YSA9IFBvaW50cy5zdWJ0cmFjdChjdXJyZW50LCBwcmV2aW91cylcbiAgICAgICAgICAgIHJldHVybiBuZXcgSW50ZXJhY3Rpb25EZWx0YShkZWx0YS54LCBkZWx0YS55LCAxLjAsIDAuMCwgY3VycmVudClcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIHN0YXJ0ZWQoa2V5LCBwb2ludCkge1xuICAgICAgICB0aGlzLmN1cnJlbnQuc2V0KGtleSwgcG9pbnQpXG4gICAgICAgIHRoaXMuc3RhcnQuc2V0KGtleSwgcG9pbnQpXG4gICAgICAgIHRoaXMucHJldmlvdXMuc2V0KGtleSwgcG9pbnQpXG4gICAgICAgIHRoaXMudGltZXN0YW1wcy5zZXQoa2V5LCBwZXJmb3JtYW5jZS5ub3coKSlcbiAgICB9XG5cbiAgICB1cGRhdGUoa2V5LCBwb2ludCkge1xuICAgICAgICAvLyBSZXR1cm5zIHRydWUgaWZmIHRoZSBrZXkgaXMgbmV3XG4gICAgICAgIHRoaXMuY3VycmVudC5zZXQoa2V5LCBwb2ludClcbiAgICAgICAgaWYgKCF0aGlzLnN0YXJ0LmhhcyhrZXkpKSB7XG4gICAgICAgICAgICB0aGlzLnN0YXJ0LnNldChrZXksIHBvaW50KVxuICAgICAgICAgICAgdGhpcy5wcmV2aW91cy5zZXQoa2V5LCBwb2ludClcbiAgICAgICAgICAgIHRoaXMudGltZXN0YW1wcy5zZXQoa2V5LCBwZXJmb3JtYW5jZS5ub3coKSlcbiAgICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgdXBkYXRlUHJldmlvdXMoKSB7XG4gICAgICAgIGZvciAobGV0IGtleSBvZiB0aGlzLmN1cnJlbnQua2V5cygpKSB7XG4gICAgICAgICAgICB0aGlzLnByZXZpb3VzLnNldChrZXksIHRoaXMuY3VycmVudC5nZXQoa2V5KSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0b3Aoa2V5LCBwb2ludCkge1xuICAgICAgICBpZiAodGhpcy5jdXJyZW50LmhhcyhrZXkpKSB7XG4gICAgICAgICAgICB0aGlzLmN1cnJlbnQuZGVsZXRlKGtleSlcbiAgICAgICAgICAgIHRoaXMucHJldmlvdXMuZGVsZXRlKGtleSlcbiAgICAgICAgICAgIHRoaXMuZW5kZWQuc2V0KGtleSwgcG9pbnQpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmaW5pc2goa2V5LCBwb2ludCkge1xuICAgICAgICB0aGlzLmN1cnJlbnQuZGVsZXRlKGtleSlcbiAgICAgICAgdGhpcy5wcmV2aW91cy5kZWxldGUoa2V5KVxuICAgICAgICB0aGlzLnN0YXJ0LmRlbGV0ZShrZXkpXG4gICAgICAgIHRoaXMudGltZXN0YW1wcy5kZWxldGUoa2V5KVxuICAgICAgICB0aGlzLmVuZGVkLmRlbGV0ZShrZXkpXG4gICAgfVxuXG4gICAgaXNGaW5pc2hlZCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY3VycmVudC5zaXplID09IDBcbiAgICB9XG5cbiAgICBpc05vTG9uZ2VyVHdvRmluZ2VyKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5wcmV2aW91cy5zaXplID4gMSAmJiB0aGlzLmN1cnJlbnQuc2l6ZSA8IDJcbiAgICB9XG5cbiAgICBpc1RhcChrZXkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGFyZW50LmlzVGFwKGtleSlcbiAgICB9XG5cbiAgICBpc0RvdWJsZVRhcChrZXkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGFyZW50LmlzRG91YmxlVGFwKGtleSlcbiAgICB9XG5cbiAgICBpc0xvbmdQcmVzcyhrZXkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGFyZW50LmlzTG9uZ1ByZXNzKGtleSlcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBJbnRlcmFjdGlvbiBleHRlbmRzIEludGVyYWN0aW9uUG9pbnRzIHtcbiAgICBjb25zdHJ1Y3Rvcih0YXBEaXN0YW5jZSA9IDEwLCB0YXBEdXJhdGlvbiA9IDI1MC4wLCBsb25nUHJlc3NUaW1lID0gNTAwLjApIHtcbiAgICAgICAgc3VwZXIoKVxuICAgICAgICB0aGlzLnRhcERpc3RhbmNlID0gdGFwRGlzdGFuY2VcbiAgICAgICAgdGhpcy50YXBDb3VudHMgPSBuZXcgTWFwKClcbiAgICAgICAgdGhpcy50YXBQb3NpdGlvbnMgPSBuZXcgTWFwKClcbiAgICAgICAgdGhpcy50YXBUaW1lc3RhbXBzID0gbmV3IE1hcCgpXG4gICAgICAgIHRoaXMudGFwRHVyYXRpb24gPSB0YXBEdXJhdGlvblxuICAgICAgICB0aGlzLmxvbmdQcmVzc1RpbWUgPSBsb25nUHJlc3NUaW1lXG4gICAgICAgIHRoaXMudGFyZ2V0cyA9IG5ldyBNYXAoKVxuICAgICAgICB0aGlzLnN1YkludGVyYWN0aW9ucyA9IG5ldyBNYXAoKSAvLyB0YXJnZXQ6T2JqZWN0IDogSW50ZXJhY3Rpb25Qb2ludHNcbiAgICB9XG5cbiAgICBzdG9wKGtleSwgcG9pbnQpIHtcbiAgICAgICAgc3VwZXIuc3RvcChrZXksIHBvaW50KVxuICAgICAgICBmb3IgKGxldCBwb2ludHMgb2YgdGhpcy5zdWJJbnRlcmFjdGlvbnMudmFsdWVzKCkpIHtcbiAgICAgICAgICAgIHBvaW50cy5zdG9wKGtleSwgcG9pbnQpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBhZGRUYXJnZXQoa2V5LCB0YXJnZXQpIHtcbiAgICAgICAgdGhpcy50YXJnZXRzLnNldChrZXksIHRhcmdldClcbiAgICAgICAgdGhpcy5zdWJJbnRlcmFjdGlvbnMuc2V0KHRhcmdldCwgbmV3IEludGVyYWN0aW9uUG9pbnRzKHRoaXMpKVxuICAgIH1cblxuICAgIHJlbW92ZVRhcmdldChrZXkpIHtcbiAgICAgICAgbGV0IHRhcmdldCA9IHRoaXMudGFyZ2V0cy5nZXQoa2V5KVxuICAgICAgICB0aGlzLnRhcmdldHMuZGVsZXRlKGtleSlcbiAgICAgICAgLy8gT25seSByZW1vdmUgdGFyZ2V0IGlmIG5vIGtleXMgYXJlIHJlZmVyaW5nIHRvIHRoZSB0YXJnZXRcbiAgICAgICAgbGV0IHJlbW92ZSA9IHRydWVcbiAgICAgICAgZm9yIChsZXQgdCBvZiB0aGlzLnRhcmdldHMudmFsdWVzKCkpIHtcbiAgICAgICAgICAgIGlmICh0YXJnZXQgPT09IHQpIHtcbiAgICAgICAgICAgICAgICByZW1vdmUgPSBmYWxzZVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChyZW1vdmUpIHtcbiAgICAgICAgICAgIHRoaXMuc3ViSW50ZXJhY3Rpb25zLmRlbGV0ZSh0YXJnZXQpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmaW5pc2goa2V5LCBwb2ludCkge1xuICAgICAgICBzdXBlci5maW5pc2goa2V5LCBwb2ludClcbiAgICAgICAgdGhpcy5yZW1vdmVUYXJnZXQoa2V5KVxuICAgIH1cblxuICAgIG1hcEludGVyYWN0aW9uKHBvaW50cywgYXNwZWN0cywgbWFwcGluZ0Z1bmMpIHtcbiAgICAgICAgLy8gTWFwIGNlbnRyYWxseSByZWdpc3RlcmVkIHBvaW50cyB0byB0YXJnZXQgaW50ZXJhY3Rpb25zXG4gICAgICAgIC8vIFJldHVybnMgYW4gYXJyYXkgb2YgW3RhcmdldCwgdXBkYXRlZCBzdWJJbnRlcmFjdGlvbl0gcGFpcnNcbiAgICAgICAgbGV0IHJlc3VsdCA9IG5ldyBNYXAoKVxuICAgICAgICBmb3IgKGxldCBrZXkgaW4gcG9pbnRzKSB7XG4gICAgICAgICAgICBpZiAodGhpcy50YXJnZXRzLmhhcyhrZXkpKSB7XG4gICAgICAgICAgICAgICAgbGV0IHRhcmdldCA9IHRoaXMudGFyZ2V0cy5nZXQoa2V5KVxuICAgICAgICAgICAgICAgIGlmICh0aGlzLnN1YkludGVyYWN0aW9ucy5oYXModGFyZ2V0KSkge1xuICAgICAgICAgICAgICAgICAgICBsZXQgaW50ZXJhY3Rpb24gPSB0aGlzLnN1YkludGVyYWN0aW9ucy5nZXQodGFyZ2V0KVxuICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCBhc3BlY3Qgb2YgYXNwZWN0cykge1xuICAgICAgICAgICAgICAgICAgICAgICAgbGV0IHBvaW50TWFwID0gdGhpc1thc3BlY3RdXG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgcG9pbnQgPSBwb2ludE1hcC5nZXQoa2V5KVxuICAgICAgICAgICAgICAgICAgICAgICAgbGV0IG1hcHBlZCA9IG1hcHBpbmdGdW5jKHBvaW50LCB0YXJnZXQpXG4gICAgICAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGlvblthc3BlY3RdLnNldChrZXksIG1hcHBlZClcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICByZXN1bHQuc2V0KHRhcmdldCwgaW50ZXJhY3Rpb24pXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG5cbiAgICByZWdpc3RlclRhcChrZXksIHBvaW50KSB7XG4gICAgICAgIGlmICh0aGlzLnRhcENvdW50cy5oYXMoa2V5KSkge1xuICAgICAgICAgICAgbGV0IGNvdW50ID0gdGhpcy50YXBDb3VudHMuZ2V0KGtleSlcbiAgICAgICAgICAgIHRoaXMudGFwQ291bnRzLnNldChrZXksIGNvdW50KzEpXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnRhcENvdW50cy5zZXQoa2V5LCAxKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMudGFwUG9zaXRpb25zLnNldChrZXksIHBvaW50KVxuICAgICAgICB0aGlzLnRhcFRpbWVzdGFtcHMuc2V0KGtleSwgcGVyZm9ybWFuY2Uubm93KCkpXG4gICAgfVxuXG4gICAgdW5yZWdpc3RlclRhcChrZXkpIHtcbiAgICAgICAgdGhpcy50YXBDb3VudHMuZGVsZXRlKGtleSlcbiAgICAgICAgdGhpcy50YXBQb3NpdGlvbnMuZGVsZXRlKGtleSlcbiAgICAgICAgdGhpcy50YXBUaW1lc3RhbXBzLmRlbGV0ZShrZXkpXG4gICAgfVxuXG4gICAgaXNUYXAoa2V5KSB7XG4gICAgICAgIGxldCBlbmRlZCA9IHRoaXMuZW5kZWQuZ2V0KGtleSlcbiAgICAgICAgbGV0IHN0YXJ0ID0gdGhpcy5zdGFydC5nZXQoa2V5KVxuICAgICAgICBpZiAoXG4gICAgICAgICAgICBzdGFydCAmJlxuICAgICAgICAgICAgZW5kZWQgJiZcbiAgICAgICAgICAgIFBvaW50cy5kaXN0YW5jZShlbmRlZCwgc3RhcnQpIDwgdGhpcy50YXBEaXN0YW5jZVxuICAgICAgICApIHtcbiAgICAgICAgICAgIGxldCB0MSA9IHRoaXMudGltZXN0YW1wcy5nZXQoa2V5KVxuICAgICAgICAgICAgbGV0IHRvb2tMb25nID0gcGVyZm9ybWFuY2Uubm93KCkgPiB0MSArIHRoaXMubG9uZ1ByZXNzVGltZVxuICAgICAgICAgICAgaWYgKHRvb2tMb25nKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIGlzRG91YmxlVGFwKGtleSkge1xuICAgICAgICBsZXQgZW5kZWQgPSB0aGlzLmVuZGVkLmdldChrZXkpXG4gICAgICAgIGlmICh0aGlzLnRhcENvdW50cy5oYXMoa2V5KSAmJiB0aGlzLnRhcENvdW50cy5nZXQoa2V5KSA+IDIpIHtcbiAgICAgICAgICAgIHRoaXMudW5yZWdpc3RlclRhcChrZXkpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMudGFwUG9zaXRpb25zLmhhcyhrZXkpKSB7XG4gICAgICAgICAgICBsZXQgcG9zID0gdGhpcy50YXBQb3NpdGlvbnMuZ2V0KGtleSlcbiAgICAgICAgICAgIGlmIChQb2ludHMuZGlzdGFuY2UoZW5kZWQsIHBvcykgPiB0aGlzLnRhcERpc3RhbmNlKSB7XG4gICAgICAgICAgICAgICAgdGhpcy51bnJlZ2lzdGVyVGFwKGtleSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy50YXBUaW1lc3RhbXBzLmhhcyhrZXkpICYmIHBlcmZvcm1hbmNlLm5vdygpID4gdGhpcy50YXBUaW1lc3RhbXBzLmdldChrZXkpICsgdGhpcy50YXBEdXJhdGlvbikge1xuICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhcInRhcCB0b28gbG9uZ1wiKVxuICAgICAgICAgICAgdGhpcy51bnJlZ2lzdGVyVGFwKGtleSlcbiAgICAgICAgfVxuICAgICAgICBsZXQgcmVzdWx0ID0gZmFsc2VcbiAgICAgICAgaWYgKHRoaXMuaXNUYXAoa2V5KSkge1xuICAgICAgICAgICAgXG4gICAgICAgICAgICB0aGlzLnJlZ2lzdGVyVGFwKGtleSwgZW5kZWQpXG4gICAgICAgICAgICByZXN1bHQgPSB0aGlzLnRhcENvdW50cy5nZXQoa2V5KSA9PSAyXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnVucmVnaXN0ZXJUYXAoa2V5KVxuICAgICAgICB9XG4gICAgICAgIC8vY29uc29sZS5sb2coXCJpc0RvdWJsZVRhcFwiLCB0aGlzLnRhcENvdW50cy5nZXQoa2V5KSwgcmVzdWx0KVxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgfVxuXG4gICAgaXNBbnlUYXAoKSB7XG4gICAgICAgIGZvciAobGV0IGtleSBvZiB0aGlzLmVuZGVkLmtleXMoKSkge1xuICAgICAgICAgICAgaWYgKHRoaXMuaXNUYXAoa2V5KSkgcmV0dXJuIHRydWVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICBpc0xvbmdQcmVzcyhrZXkpIHtcbiAgICAgICAgbGV0IGVuZGVkID0gdGhpcy5lbmRlZC5nZXQoa2V5KVxuICAgICAgICBsZXQgc3RhcnQgPSB0aGlzLnN0YXJ0LmdldChrZXkpXG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIHN0YXJ0ICYmXG4gICAgICAgICAgICBlbmRlZCAmJlxuICAgICAgICAgICAgUG9pbnRzLmRpc3RhbmNlKGVuZGVkLCBzdGFydCkgPCB0aGlzLnRhcERpc3RhbmNlXG4gICAgICAgICkge1xuICAgICAgICAgICAgbGV0IHQxID0gdGhpcy50aW1lc3RhbXBzLmdldChrZXkpXG4gICAgICAgICAgICBsZXQgdG9va0xvbmcgPSBwZXJmb3JtYW5jZS5ub3coKSA+IHQxICsgdGhpcy5sb25nUHJlc3NUaW1lXG4gICAgICAgICAgICBpZiAodG9va0xvbmcpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgaXNBbnlMb25nUHJlc3MoKSB7XG4gICAgICAgIGZvciAobGV0IGtleSBvZiB0aGlzLmVuZGVkLmtleXMoKSkge1xuICAgICAgICAgICAgaWYgKHRoaXMuaXNMb25nUHJlc3Moa2V5KSkgcmV0dXJuIHRydWVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICBpc1N0eWx1cyhrZXkpIHtcbiAgICAgICAgcmV0dXJuIGtleSA9PT0gJ3N0eWx1cydcbiAgICB9XG59XG5cbi8qKlxuICogVGhpcyBjbGFzcyBpbXBsZW1lbnRzIHRoZSBtYWluIGRlbGVnYXRlIGZ1bmN0aW9uYWxpdHk6IEFsbCBuZWNlc3NhcnkgZXZlbnQgaGFuZGxlcnMgYXJlIHJlZ2lzdGVyZWQgZm9yIHRoZVxuICogZ2l2ZW4gZWxlbWVudC4gVXNlcyBQb2ludGVyRXZlbnRzIGlmIGF2YWlsYWJsZSBvciBUb3VjaEV2ZW50cyBvbiBpT1MuIFRoZSBmYWxsYmFjayBpcyBvbiBtb3VzZSBldmVudHMuXG4gKiBDb2xsZWN0cyB0aGUgZXZlbnRzIGlmIHRoZSBpbnRlcmFjdGlvbiB0YXJnZXQgY2FwdHVyZXMgdGhlIHN0YXJ0IGV2ZW50IChpLmUuIGRlY2xhcmVzIHRoYXRcbiAqIHRoZSB0YXJnZXQgd2FudHMgdGhlIHN0YXJ0IGV2ZW50IGFzIHdlbGwgYXMgYWxsIGZvbGxvd2luZyBtb3ZlIGFuZCBlbmQgZXZjZW50cy4pXG4gKlxuICogQGV4cG9ydFxuICogQGNsYXNzIEludGVyYWN0aW9uRGVsZWdhdGVcbiAqL1xuZXhwb3J0IGNsYXNzIEludGVyYWN0aW9uRGVsZWdhdGUge1xuICAgIC8vIExvbmcgcHJlc3M6IGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTkzMDg5NS9ob3ctbG9uZy1pcy10aGUtZXZlbnQtb25sb25ncHJlc3MtaW4tdGhlLWFuZHJvaWRcbiAgICAvLyBTdHlsdXMgc3VwcG9ydDogaHR0cHM6Ly93M2MuZ2l0aHViLmlvL3RvdWNoLWV2ZW50cy9cblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgSW50ZXJhY3Rpb25EZWxlZ2F0ZS5cbiAgICAgKiBAcGFyYW0ge2FueX0gZWxlbWVudFxuICAgICAqIEBwYXJhbSB7YW55fSB0YXJnZXRcbiAgICAgKiBAcGFyYW0ge2FueX0gW3sgbW91c2VXaGVlbEVsZW1lbnQgPSBudWxsLCB1c2VDYXB0dXJlID0gdHJ1ZSwgY2FwdHVyZVBvaW50ZXJFdmVudHMgPSB0cnVlLCBkZWJ1ZyA9IGZhbHNlIH09e31dXG4gICAgICogQG1lbWJlcm9mIEludGVyYWN0aW9uRGVsZWdhdGVcbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgZWxlbWVudCxcbiAgICAgICAgdGFyZ2V0LFxuICAgICAgICB7IG1vdXNlV2hlZWxFbGVtZW50ID0gbnVsbCwgdXNlQ2FwdHVyZSA9IHRydWUsIGNhcHR1cmVQb2ludGVyRXZlbnRzID0gdHJ1ZSwgY2FuY2VsT25XaW5kb3dPdXQgPSB0cnVlLCBkZWJ1ZyA9IGZhbHNlIH0gPSB7fVxuICAgICkge1xuICAgICAgICB0aGlzLmRlYnVnID0gZGVidWdcbiAgICAgICAgdGhpcy5pbnRlcmFjdGlvbiA9IG5ldyBJbnRlcmFjdGlvbigpXG4gICAgICAgIHRoaXMuZWxlbWVudCA9IGVsZW1lbnRcbiAgICAgICAgdGhpcy5tb3VzZVdoZWVsRWxlbWVudCA9IG1vdXNlV2hlZWxFbGVtZW50IHx8IGVsZW1lbnRcbiAgICAgICAgdGhpcy50YXJnZXQgPSB0YXJnZXRcbiAgICAgICAgdGhpcy51c2VDYXB0dXJlID0gdXNlQ2FwdHVyZVxuICAgICAgICB0aGlzLmNhcHR1cmVQb2ludGVyRXZlbnRzID0gY2FwdHVyZVBvaW50ZXJFdmVudHNcbiAgICAgICAgdGhpcy5jYW5jZWxPbldpbmRvd091dCA9IGNhbmNlbE9uV2luZG93T3V0XG4gICAgICAgIHRoaXMuc2V0dXBJbnRlcmFjdGlvbigpXG4gICAgfVxuXG4gICAgc2V0dXBJbnRlcmFjdGlvbigpIHtcbiAgICAgICAgaWYgKHRoaXMuZGVidWcpIHtcbiAgICAgICAgICAgIGxldCBlcnJvciA9IHRoaXMudGFyZ2V0SW50ZXJmYWNlLmltcGxlbWVudGF0aW9uRXJyb3IoXG4gICAgICAgICAgICAgICAgdGhpcy50YXJnZXQuY29uc3RydWN0b3JcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIGlmIChlcnJvciAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdFeHBlY3RlZCBJSW50ZXJhY3Rpb25UYXJnZXQ6ICcgKyBlcnJvcilcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLnNldHVwVG91Y2hJbnRlcmFjdGlvbigpXG4gICAgICAgIHRoaXMuc2V0dXBNb3VzZVdoZWVsSW50ZXJhY3Rpb24oKVxuICAgIH1cblxuICAgIGdldCB0YXJnZXRJbnRlcmZhY2UoKSB7XG4gICAgICAgIHJldHVybiBJSW50ZXJhY3Rpb25UYXJnZXRcbiAgICB9XG5cbiAgICBzZXR1cFRvdWNoSW50ZXJhY3Rpb24oKSB7XG4gICAgICAgIGxldCBlbGVtZW50ID0gdGhpcy5lbGVtZW50XG4gICAgICAgIGxldCB1c2VDYXB0dXJlID0gdGhpcy51c2VDYXB0dXJlXG4gICAgICAgIGlmICh3aW5kb3cuUG9pbnRlckV2ZW50KSB7XG4gICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ1BvaW50ZXIgQVBJJyArIHdpbmRvdy5Qb2ludGVyRXZlbnQpXG4gICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgJ3BvaW50ZXJkb3duJyxcbiAgICAgICAgICAgICAgICBlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKCdwb2ludGVyZG93bicsIGUucG9pbnRlcklkKVxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5jYXB0dXJlKGUpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5jYXB0dXJlUG9pbnRlckV2ZW50cykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW1lbnQuc2V0UG9pbnRlckNhcHR1cmUoZS5wb2ludGVySWQpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkgeyB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uU3RhcnQoZSlcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgdXNlQ2FwdHVyZVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgICAgICdwb2ludGVybW92ZScsXG4gICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygncG9pbnRlcm1vdmUnLCBlLnBvaW50ZXJJZClcblxuICAgICAgICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgICAgICAgICBlLnBvaW50ZXJUeXBlID09ICd0b3VjaCcgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgIChlLnBvaW50ZXJUeXBlID09ICdtb3VzZScgJiYgRXZlbnRzLmlzTW91c2VEb3duKGUpKVxuICAgICAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIHRoaXMuY2FwdHVyZShlKSAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3BvaW50ZXJtb3ZlIGNhcHR1cmVkJywgZS5wb2ludGVySWQpXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uTW92ZShlKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgJ3BvaW50ZXJ1cCcsXG4gICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygncG9pbnRlcnVwJylcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vbkVuZChlKVxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5jYXB0dXJlUG9pbnRlckV2ZW50cykge1xuICAgICAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbGVtZW50LnJlbGVhc2VQb2ludGVyQ2FwdHVyZShlLnBvaW50ZXJJZClcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHsgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgJ3BvaW50ZXJjYW5jZWwnLFxuICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ3BvaW50ZXJjYW5jZWwnKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLm9uRW5kKGUpXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmNhcHR1cmVQb2ludGVyRXZlbnRzKVxuICAgICAgICAgICAgICAgICAgICAgICAgZWxlbWVudC5yZWxlYXNlUG9pbnRlckNhcHR1cmUoZS5wb2ludGVySWQpXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICApXG5cbiAgICAgICAgICAgIGlmICghdGhpcy5jYXB0dXJlUG9pbnRlckV2ZW50cykge1xuICAgICAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAgICAgJ3BvaW50ZXJsZWF2ZScsXG4gICAgICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKCdwb2ludGVybGVhdmUnKVxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGUudGFyZ2V0ID09IGVsZW1lbnQpIHRoaXMub25FbmQoZSlcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgdXNlQ2FwdHVyZVxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKCF0aGlzLmNhcHR1cmVQb2ludGVyRXZlbnRzKSB7XG4gICAgICAgICAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgICAgICAgICAncG9pbnRlcm91dCcsXG4gICAgICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKCdwb2ludGVyb3V0JylcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlLnRhcmdldCA9PSBlbGVtZW50KSB0aGlzLm9uRW5kKGUpXG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHVzZUNhcHR1cmVcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLmNhbmNlbE9uV2luZG93T3V0KSB7XG4gICAgICAgICAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgICAgICdwb2ludGVyb3V0JyxcbiAgICAgICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoZS50YXJnZXQgPT0gZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25FbmQoZSlcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgdXNlQ2FwdHVyZSlcbiAgICAgICAgICAgIH1cblxuICAgICAgICB9IGVsc2UgaWYgKHdpbmRvdy5Ub3VjaEV2ZW50KSB7XG4gICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ1RvdWNoIEFQSScpXG4gICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgJ3RvdWNoc3RhcnQnLFxuICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZylcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCd0b3VjaHN0YXJ0JywgdGhpcy50b3VjaFBvaW50cyhlKSlcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuY2FwdHVyZShlKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9yIChsZXQgdG91Y2ggb2YgZS5jaGFuZ2VkVG91Y2hlcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25TdGFydCh0b3VjaClcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgdXNlQ2FwdHVyZVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgICAgICd0b3VjaG1vdmUnLFxuICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZylcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCd0b3VjaG1vdmUnLCB0aGlzLnRvdWNoUG9pbnRzKGUpLCBlKVxuICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCB0b3VjaCBvZiBlLmNoYW5nZWRUb3VjaGVzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uTW92ZSh0b3VjaClcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCB0b3VjaCBvZiBlLnRhcmdldFRvdWNoZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25Nb3ZlKHRvdWNoKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgJ3RvdWNoZW5kJyxcbiAgICAgICAgICAgICAgICBlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKCd0b3VjaGVuZCcsIHRoaXMudG91Y2hQb2ludHMoZSkpXG4gICAgICAgICAgICAgICAgICAgIGZvciAobGV0IHRvdWNoIG9mIGUuY2hhbmdlZFRvdWNoZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25FbmQodG91Y2gpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHVzZUNhcHR1cmVcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAndG91Y2hjYW5jZWwnLFxuICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZylcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICd0b3VjaGNhbmNlbCcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZS50YXJnZXRUb3VjaGVzLmxlbmd0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlLmNoYW5nZWRUb3VjaGVzLmxlbmd0aFxuICAgICAgICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCB0b3VjaCBvZiBlLmNoYW5nZWRUb3VjaGVzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uRW5kKHRvdWNoKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICApXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ01vdXNlIEFQSScpXG5cbiAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAnbW91c2Vkb3duJyxcbiAgICAgICAgICAgICAgICBlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKCdtb3VzZWRvd24nLCBlKVxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5jYXB0dXJlKGUpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uU3RhcnQoZSlcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgdXNlQ2FwdHVyZVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgICAgICdtb3VzZW1vdmUnLFxuICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICAvLyBEb3cgd2Ugb25seSB1c2UgbW92ZSBldmVudHMgaWYgdGhlIG1vdXNlIGlzIGRvd24/XG4gICAgICAgICAgICAgICAgICAgIC8vIEhPdmVyIGVmZmVjdHMgaGF2ZSB0byBiZSBpbXBsZW1lbnRlZCBieSBvdGhlciBtZWFuc1xuICAgICAgICAgICAgICAgICAgICAvLyAmJiBFdmVudHMuaXNNb3VzZURvd24oZSkpXG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKEV2ZW50cy5pc01vdXNlRG93bihlKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ21vdXNlbW92ZScsIGUpXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uTW92ZShlKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgJ21vdXNldXAnLFxuICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ21vdXNldXAnLCBlKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLm9uRW5kKGUpXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB0cnVlXG4gICAgICAgICAgICApXG5cbiAgICAgICAgICAgIGlmICghdGhpcy5jYXB0dXJlUG9pbnRlckV2ZW50cykge1xuICAgICAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAgICAgJ21vdXNlb3V0JyxcbiAgICAgICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoZS50YXJnZXQgPT0gZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25FbmQoZSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJTaG91bGRuJ3QgaGFwcGVuOiBtb3VzZW91dCBlbmRzIGludGVyYWN0aW9uXCIpXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgdXNlQ2FwdHVyZVxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0aGlzLmNhbmNlbE9uV2luZG93T3V0KSB7XG4gICAgICAgICAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgICAgICdtb3VzZW91dCcsXG4gICAgICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGUudGFyZ2V0ID09IGVsZW1lbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uRW5kKGUpXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHVzZUNhcHR1cmUpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpc0Rlc2NlbmRhbnQocGFyZW50LCBjaGlsZCkge1xuICAgICAgICBpZiAocGFyZW50ID09IGNoaWxkKSByZXR1cm4gdHJ1ZVxuICAgICAgICBsZXQgbm9kZSA9IGNoaWxkLnBhcmVudE5vZGVcbiAgICAgICAgd2hpbGUgKG5vZGUgIT0gbnVsbCkge1xuICAgICAgICAgICAgaWYgKG5vZGUgPT0gcGFyZW50KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG5vZGUgPSBub2RlLnBhcmVudE5vZGVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICB0b3VjaFBvaW50cyhldmVudCkge1xuICAgICAgICBsZXQgcmVzdWx0ID0gW11cbiAgICAgICAgZm9yIChsZXQgdG91Y2ggb2YgZXZlbnQuY2hhbmdlZFRvdWNoZXMpIHtcbiAgICAgICAgICAgIHJlc3VsdC5wdXNoKHRoaXMuZXh0cmFjdFBvaW50KHRvdWNoKSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgfVxuXG4gICAgc2V0dXBNb3VzZVdoZWVsSW50ZXJhY3Rpb24oKSB7XG4gICAgICAgIHRoaXMubW91c2VXaGVlbEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICdtb3VzZXdoZWVsJyxcbiAgICAgICAgICAgIHRoaXMub25Nb3VzZVdoZWVsLmJpbmQodGhpcyksXG4gICAgICAgICAgICB0cnVlXG4gICAgICAgIClcbiAgICAgICAgdGhpcy5tb3VzZVdoZWVsRWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgJ0RPTU1vdXNlU2Nyb2xsJyxcbiAgICAgICAgICAgIHRoaXMub25Nb3VzZVdoZWVsLmJpbmQodGhpcyksXG4gICAgICAgICAgICB0cnVlXG4gICAgICAgIClcbiAgICB9XG5cbiAgICBvbk1vdXNlV2hlZWwoZXZlbnQpIHtcbiAgICAgICAgaWYgKHRoaXMuY2FwdHVyZShldmVudCkgJiYgdGhpcy50YXJnZXQub25Nb3VzZVdoZWVsKSB7XG4gICAgICAgICAgICB0aGlzLnRhcmdldC5vbk1vdXNlV2hlZWwoZXZlbnQpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUud2FybignVGFyZ2V0IGhhcyBubyBvbk1vdXNlV2hlZWwgY2FsbGJhY2snKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25TdGFydChldmVudCkge1xuICAgICAgICBsZXQgZXh0cmFjdGVkID0gdGhpcy5leHRyYWN0UG9pbnQoZXZlbnQpXG4gICAgICAgIHRoaXMuc3RhcnRJbnRlcmFjdGlvbihldmVudCwgZXh0cmFjdGVkKVxuICAgICAgICB0aGlzLnRhcmdldC5vblN0YXJ0KGV2ZW50LCB0aGlzLmludGVyYWN0aW9uKVxuICAgIH1cblxuICAgIG9uTW92ZShldmVudCkge1xuICAgICAgICBsZXQgZXh0cmFjdGVkID0gdGhpcy5leHRyYWN0UG9pbnQoZXZlbnQsICdhbGwnKVxuICAgICAgICB0aGlzLnVwZGF0ZUludGVyYWN0aW9uKGV2ZW50LCBleHRyYWN0ZWQpXG4gICAgICAgIHRoaXMudGFyZ2V0Lm9uTW92ZShldmVudCwgdGhpcy5pbnRlcmFjdGlvbilcbiAgICAgICAgdGhpcy5pbnRlcmFjdGlvbi51cGRhdGVQcmV2aW91cygpXG4gICAgfVxuXG4gICAgb25FbmQoZXZlbnQpIHtcbiAgICAgICAgbGV0IGV4dHJhY3RlZCA9IHRoaXMuZXh0cmFjdFBvaW50KGV2ZW50LCAnY2hhbmdlZFRvdWNoZXMnKVxuICAgICAgICB0aGlzLmVuZEludGVyYWN0aW9uKGV2ZW50LCBleHRyYWN0ZWQpXG4gICAgICAgIHRoaXMudGFyZ2V0Lm9uRW5kKGV2ZW50LCB0aGlzLmludGVyYWN0aW9uKVxuICAgICAgICB0aGlzLmZpbmlzaEludGVyYWN0aW9uKGV2ZW50LCBleHRyYWN0ZWQpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQXNrcyB0aGUgdGFyZ2V0IHdoZXRoZXIgdGhlIGV2ZW50IHNob3VsZCBiZSBjYXB0dXJlZFxuICAgICAqXG4gICAgICogQHBhcmFtIHthbnl9IGV2ZW50XG4gICAgICogQHJldHVybnMge2Jvb2x9XG4gICAgICogQG1lbWJlcm9mIEludGVyYWN0aW9uRGVsZWdhdGVcbiAgICAgKi9cbiAgICBjYXB0dXJlKGV2ZW50KSB7XG4gICAgICAgIGlmIChFdmVudHMuaXNDYXB0dXJlZChldmVudCkpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgICB9XG4gICAgICAgIGxldCBjYXB0dXJlZCA9IHRoaXMudGFyZ2V0LmNhcHR1cmUoZXZlbnQpXG4gICAgICAgIHJldHVybiBjYXB0dXJlZFxuICAgIH1cblxuICAgIGdldFBvc2l0aW9uKGV2ZW50KSB7XG4gICAgICAgIHJldHVybiB7IHg6IGV2ZW50LmNsaWVudFgsIHk6IGV2ZW50LmNsaWVudFkgfVxuICAgIH1cblxuICAgIGV4dHJhY3RQb2ludChldmVudCwgdG91Y2hFdmVudEtleSA9ICdhbGwnKSB7XG4gICAgICAgIC8vICd0YXJnZXRUb3VjaGVzJ1xuICAgICAgICBsZXQgcmVzdWx0ID0ge31cbiAgICAgICAgc3dpdGNoIChldmVudC5jb25zdHJ1Y3Rvci5uYW1lKSB7XG4gICAgICAgICAgICBjYXNlICdNb3VzZUV2ZW50JzpcbiAgICAgICAgICAgICAgICBsZXQgYnV0dG9ucyA9IGV2ZW50LmJ1dHRvbnMgfHwgZXZlbnQud2hpY2hcbiAgICAgICAgICAgICAgICBpZiAoYnV0dG9ucykgcmVzdWx0Wydtb3VzZSddID0gdGhpcy5nZXRQb3NpdGlvbihldmVudClcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgY2FzZSAnUG9pbnRlckV2ZW50JzpcbiAgICAgICAgICAgICAgICByZXN1bHRbZXZlbnQucG9pbnRlcklkLnRvU3RyaW5nKCldID0gdGhpcy5nZXRQb3NpdGlvbihldmVudClcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgY2FzZSAnVG91Y2gnOlxuICAgICAgICAgICAgICAgIGxldCBpZCA9XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50LnRvdWNoVHlwZSA9PT0gJ3N0eWx1cydcbiAgICAgICAgICAgICAgICAgICAgICAgID8gJ3N0eWx1cydcbiAgICAgICAgICAgICAgICAgICAgICAgIDogZXZlbnQuaWRlbnRpZmllci50b1N0cmluZygpXG4gICAgICAgICAgICAgICAgcmVzdWx0W2lkXSA9IHRoaXMuZ2V0UG9zaXRpb24oZXZlbnQpXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIC8vICAgICAgICAgICAgIGNhc2UgJ1RvdWNoRXZlbnQnOlxuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgIC8vIE5lZWRzIHRvIGJlIG9ic2VydmVkOiBQZXJoYXBzIGNoYW5nZWRUb3VjaGVzIGFyZSBhbGwgd2UgbmVlZC4gSWYgc29cbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICAvLyB3ZSBjYW4gcmVtb3ZlIHRoZSB0b3VjaEV2ZW50S2V5IGRlZmF1bHQgcGFyYW1ldGVyXG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgaWYgKHRvdWNoRXZlbnRLZXkgPT0gJ2FsbCcpIHtcbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgZm9yKGxldCB0IG9mIGV2ZW50LnRhcmdldFRvdWNoZXMpIHtcbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdFt0LmlkZW50aWZpZXIudG9TdHJpbmcoKV0gPSB0aGlzLmdldFBvc2l0aW9uKHQpXG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgZm9yKGxldCB0IG9mIGV2ZW50LmNoYW5nZWRUb3VjaGVzKSB7XG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRbdC5pZGVudGlmaWVyLnRvU3RyaW5nKCldID0gdGhpcy5nZXRQb3NpdGlvbih0KVxuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgICAgICBmb3IobGV0IHQgb2YgZXZlbnQuY2hhbmdlZFRvdWNoZXMpIHtcbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdFt0LmlkZW50aWZpZXIudG9TdHJpbmcoKV0gPSB0aGlzLmdldFBvc2l0aW9uKHQpXG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgfVxuXG4gICAgaW50ZXJhY3Rpb25TdGFydGVkKGV2ZW50LCBrZXksIHBvaW50KSB7XG4gICAgICAgIC8vIENhbGxiYWNrOiBjYW4gYmUgb3ZlcndyaXR0ZW5cbiAgICB9XG5cbiAgICBpbnRlcmFjdGlvbkVuZGVkKGV2ZW50LCBrZXksIHBvaW50KSB7XG4gICAgICAgIC8vIENhbGxiYWNrOiBjYW4gYmUgb3ZlcndyaXR0ZW5cbiAgICB9XG5cbiAgICBpbnRlcmFjdGlvbkZpbmlzaGVkKGV2ZW50LCBrZXksIHBvaW50KSB7IH1cblxuICAgIHN0YXJ0SW50ZXJhY3Rpb24oZXZlbnQsIGV4dHJhY3RlZCkge1xuICAgICAgICBmb3IgKGxldCBrZXkgaW4gZXh0cmFjdGVkKSB7XG4gICAgICAgICAgICBsZXQgcG9pbnQgPSBleHRyYWN0ZWRba2V5XVxuICAgICAgICAgICAgdGhpcy5pbnRlcmFjdGlvbi5zdGFydGVkKGtleSwgcG9pbnQpXG4gICAgICAgICAgICB0aGlzLmludGVyYWN0aW9uU3RhcnRlZChldmVudCwga2V5LCBwb2ludClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHVwZGF0ZUludGVyYWN0aW9uKGV2ZW50LCBleHRyYWN0ZWQpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IGluIGV4dHJhY3RlZCkge1xuICAgICAgICAgICAgbGV0IHBvaW50ID0gZXh0cmFjdGVkW2tleV1cbiAgICAgICAgICAgIGxldCB1cGRhdGVkID0gdGhpcy5pbnRlcmFjdGlvbi51cGRhdGUoa2V5LCBwb2ludClcbiAgICAgICAgICAgIGlmICh1cGRhdGVkKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwibmV3IHBvaW50ZXIgaW4gdXBkYXRlSW50ZXJhY3Rpb24gc2hvdWxkbid0IGhhcHBlblwiKVxuICAgICAgICAgICAgICAgIHRoaXMuaW50ZXJhY3Rpb25TdGFydGVkKGV2ZW50LCBrZXksIHBvaW50KVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZW5kSW50ZXJhY3Rpb24oZXZlbnQsIGVuZGVkKSB7XG4gICAgICAgIGZvciAobGV0IGtleSBpbiBlbmRlZCkge1xuICAgICAgICAgICAgbGV0IHBvaW50ID0gZW5kZWRba2V5XVxuICAgICAgICAgICAgdGhpcy5pbnRlcmFjdGlvbi5zdG9wKGtleSwgcG9pbnQpXG4gICAgICAgICAgICB0aGlzLmludGVyYWN0aW9uRW5kZWQoZXZlbnQsIGtleSwgcG9pbnQpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmaW5pc2hJbnRlcmFjdGlvbihldmVudCwgZW5kZWQpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IGluIGVuZGVkKSB7XG4gICAgICAgICAgICBsZXQgcG9pbnQgPSBlbmRlZFtrZXldXG4gICAgICAgICAgICB0aGlzLmludGVyYWN0aW9uLmZpbmlzaChrZXksIHBvaW50KVxuICAgICAgICAgICAgdGhpcy5pbnRlcmFjdGlvbkZpbmlzaGVkKGV2ZW50LCBrZXksIHBvaW50KVxuICAgICAgICB9XG4gICAgfVxufVxuLyoqXG4gKiBBIHNwZWNpYWwgSW50ZXJhY3Rpb25EZWxlZ2F0ZSB0aGF0IG1hcHMgZXZlbnRzIHRvIHNwZWNpZmljIHBhcnRzIG9mXG4gKiB0aGUgaW50ZXJhY3Rpb24gdGFyZ2V0LiBUaGUgSW50ZXJhY3Rpb25UYXJnZXQgbXVzdCBpbXBsZW1lbnQgYSBmaW5kVGFyZ2V0XG4gKiBtZXRob2QgdGhhdCByZXR1cm5zIGFuIG9iamVjdCBpbXBsZW1lbnRpbmcgdGhlIElJbnRlcmFjdGlvblRhcmdldCBpbnRlcmZhY2UuXG4gKlxuICogSWYgdGhlIEludGVyYWN0aW9uVGFyZ2V0IGFsc28gaW1wbGVtZW50cyBhIG1hcFBvc2l0aW9uVG9Qb2ludCBtZXRob2QgdGhpc1xuICogaXMgdXNlZCB0byBtYXAgdGhlIHBvaW50cyB0byB0aGUgbG9jYWwgY29vcmRpbmF0ZSBzcGFjZSBvZiB0aGUgdGhlIHRhcmdldC5cbiAqXG4gKiBUaGlzIG1ha2VzIGl0IGVhc2llciB0byBsb29rdXAgZWxlbWVudHMgYW5kIHJlbGF0ZSBldmVudHMgdG8gbG9jYWxcbiAqIHBvc2l0aW9ucy5cbiAqXG4gKiBAZXhwb3J0XG4gKiBAY2xhc3MgSW50ZXJhY3Rpb25NYXBwZXJcbiAqIEBleHRlbmRzIHtJbnRlcmFjdGlvbkRlbGVnYXRlfVxuICovXG5leHBvcnQgY2xhc3MgSW50ZXJhY3Rpb25NYXBwZXIgZXh0ZW5kcyBJbnRlcmFjdGlvbkRlbGVnYXRlIHtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBlbGVtZW50LFxuICAgICAgICB0YXJnZXQsXG4gICAgICAgIHsgdGFwRGlzdGFuY2UgPSAxMCwgbG9uZ1ByZXNzVGltZSA9IDUwMC4wLCB1c2VDYXB0dXJlID0gdHJ1ZSwgbW91c2VXaGVlbEVsZW1lbnQgPSBudWxsIH0gPSB7fVxuICAgICkge1xuICAgICAgICBzdXBlcihlbGVtZW50LCB0YXJnZXQsIHsgdGFwRGlzdGFuY2UsIHVzZUNhcHR1cmUsIGxvbmdQcmVzc1RpbWUsIG1vdXNlV2hlZWxFbGVtZW50IH0pXG4gICAgfVxuXG4gICAgZ2V0IHRhcmdldEludGVyZmFjZSgpIHtcbiAgICAgICAgcmV0dXJuIElJbnRlcmFjdGlvbk1hcHBlclRhcmdldFxuICAgIH1cblxuICAgIG1hcFBvc2l0aW9uVG9Qb2ludChwb2ludCwgZWxlbWVudCA9IG51bGwpIHtcbiAgICAgICAgaWYgKHRoaXMudGFyZ2V0Lm1hcFBvc2l0aW9uVG9Qb2ludCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMudGFyZ2V0Lm1hcFBvc2l0aW9uVG9Qb2ludChwb2ludCwgZWxlbWVudClcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcG9pbnRcbiAgICB9XG5cbiAgICBpbnRlcmFjdGlvblN0YXJ0ZWQoZXZlbnQsIGtleSwgcG9pbnQpIHtcbiAgICAgICAgaWYgKHRoaXMudGFyZ2V0LmZpbmRUYXJnZXQpIHtcbiAgICAgICAgICAgIGxldCBsb2NhbCA9IHRoaXMubWFwUG9zaXRpb25Ub1BvaW50KHBvaW50KVxuICAgICAgICAgICAgbGV0IGZvdW5kID0gdGhpcy50YXJnZXQuZmluZFRhcmdldChldmVudCwgbG9jYWwsIHBvaW50KVxuICAgICAgICAgICAgaWYgKGZvdW5kICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmludGVyYWN0aW9uLmFkZFRhcmdldChrZXksIGZvdW5kKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25Nb3VzZVdoZWVsKGV2ZW50KSB7XG4gICAgICAgIGlmICh0aGlzLmNhcHR1cmUoZXZlbnQpKSB7XG4gICAgICAgICAgICBpZiAodGhpcy50YXJnZXQuZmluZFRhcmdldCkge1xuICAgICAgICAgICAgICAgIGxldCBwb2ludCA9IHRoaXMuZ2V0UG9zaXRpb24oZXZlbnQpXG4gICAgICAgICAgICAgICAgbGV0IGxvY2FsID0gdGhpcy5tYXBQb3NpdGlvblRvUG9pbnQocG9pbnQpXG4gICAgICAgICAgICAgICAgbGV0IGZvdW5kID0gdGhpcy50YXJnZXQuZmluZFRhcmdldChldmVudCwgbG9jYWwsIHBvaW50KVxuICAgICAgICAgICAgICAgIGlmIChmb3VuZCAhPSBudWxsICYmIGZvdW5kLm9uTW91c2VXaGVlbCkge1xuICAgICAgICAgICAgICAgICAgICBmb3VuZC5vbk1vdXNlV2hlZWwoZXZlbnQpXG4gICAgICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0aGlzLnRhcmdldC5vbk1vdXNlV2hlZWwpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnRhcmdldC5vbk1vdXNlV2hlZWwoZXZlbnQpXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vY29uc29sZS53YXJuKCdUYXJnZXQgaGFzIG5vIG9uTW91c2VXaGVlbCBjYWxsYmFjaycsIHRoaXMudGFyZ2V0KVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25TdGFydChldmVudCkge1xuICAgICAgICBsZXQgZXh0cmFjdGVkID0gdGhpcy5leHRyYWN0UG9pbnQoZXZlbnQpXG4gICAgICAgIHRoaXMuc3RhcnRJbnRlcmFjdGlvbihldmVudCwgZXh0cmFjdGVkKVxuICAgICAgICBsZXQgbWFwcGVkID0gdGhpcy5pbnRlcmFjdGlvbi5tYXBJbnRlcmFjdGlvbihcbiAgICAgICAgICAgIGV4dHJhY3RlZCxcbiAgICAgICAgICAgIFsnY3VycmVudCcsICdzdGFydCddLFxuICAgICAgICAgICAgdGhpcy5tYXBQb3NpdGlvblRvUG9pbnQuYmluZCh0aGlzKVxuICAgICAgICApXG4gICAgICAgIGZvciAobGV0IFt0YXJnZXQsIGludGVyYWN0aW9uXSBvZiBtYXBwZWQuZW50cmllcygpKSB7XG4gICAgICAgICAgICB0YXJnZXQub25TdGFydChldmVudCwgaW50ZXJhY3Rpb24pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvbk1vdmUoZXZlbnQpIHtcbiAgICAgICAgbGV0IGV4dHJhY3RlZCA9IHRoaXMuZXh0cmFjdFBvaW50KGV2ZW50LCAnYWxsJylcbiAgICAgICAgdGhpcy51cGRhdGVJbnRlcmFjdGlvbihldmVudCwgZXh0cmFjdGVkKVxuICAgICAgICBsZXQgbWFwcGVkID0gdGhpcy5pbnRlcmFjdGlvbi5tYXBJbnRlcmFjdGlvbihcbiAgICAgICAgICAgIGV4dHJhY3RlZCxcbiAgICAgICAgICAgIFsnY3VycmVudCcsICdwcmV2aW91cyddLFxuICAgICAgICAgICAgdGhpcy5tYXBQb3NpdGlvblRvUG9pbnQuYmluZCh0aGlzKVxuICAgICAgICApXG4gICAgICAgIGZvciAobGV0IFt0YXJnZXQsIGludGVyYWN0aW9uXSBvZiBtYXBwZWQuZW50cmllcygpKSB7XG4gICAgICAgICAgICB0YXJnZXQub25Nb3ZlKGV2ZW50LCBpbnRlcmFjdGlvbilcbiAgICAgICAgICAgIGludGVyYWN0aW9uLnVwZGF0ZVByZXZpb3VzKClcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmludGVyYWN0aW9uLnVwZGF0ZVByZXZpb3VzKClcbiAgICB9XG5cbiAgICBvbkVuZChldmVudCkge1xuICAgICAgICBsZXQgZXh0cmFjdGVkID0gdGhpcy5leHRyYWN0UG9pbnQoZXZlbnQsICdjaGFuZ2VkVG91Y2hlcycpXG4gICAgICAgIHRoaXMuZW5kSW50ZXJhY3Rpb24oZXZlbnQsIGV4dHJhY3RlZClcbiAgICAgICAgbGV0IG1hcHBlZCA9IHRoaXMuaW50ZXJhY3Rpb24ubWFwSW50ZXJhY3Rpb24oXG4gICAgICAgICAgICBleHRyYWN0ZWQsXG4gICAgICAgICAgICBbJ2VuZGVkJ10sXG4gICAgICAgICAgICB0aGlzLm1hcFBvc2l0aW9uVG9Qb2ludC5iaW5kKHRoaXMpXG4gICAgICAgIClcbiAgICAgICAgZm9yIChsZXQgW3RhcmdldCwgaW50ZXJhY3Rpb25dIG9mIG1hcHBlZC5lbnRyaWVzKCkpIHtcbiAgICAgICAgICAgIHRhcmdldC5vbkVuZChldmVudCwgaW50ZXJhY3Rpb24pXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5maW5pc2hJbnRlcmFjdGlvbihldmVudCwgZXh0cmFjdGVkKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqXG4gICAgICpcbiAgICAgKiBAc3RhdGljXG4gICAgICogQHBhcmFtIHtzdHJpbmd8YXJyYXl9IHR5cGVzIC0gQW4gZXZlbnQgdHlwZSwgYW4gYXJyYXkgb2YgZXZlbnQgdHlwZXMgb3IgZXZlbnQgdHlwZXMgc2VwZXJhdGVkIGJ5IGEgc3BhY2Ugc2lnbi4gVGhlIGZvbGxvd2luZ1xuICAgICAqICAgICBldmVudHMgYXJlIHBvc3NpYmxlOlxuICAgICAqICAgICAgICAgcGFuLCBwYW5zdGFydCwgcGFubW92ZSwgcGFuZW5kLCBwYW5jYW5jZWwsIHBhbmxlZnQsIHBhbnJpZ2h0LCBwYW51cCwgcGFuZG93blxuICAgICAqICAgICAgICAgcGluY2gsIHBpbmNoc3RhcnQsIHBpbmNobW92ZSwgcGluY2hlbmQsIHBpbmNoY2FuY2VsLCBwaW5jaGluLCBwaW5jaG91dFxuICAgICAqICAgICAgICAgcHJlc3MsIHByZXNzdXBcbiAgICAgKiAgICAgICAgIHJvdGF0ZSwgcm90YXRlc3RhcnQsIHJvdGF0ZW1vdmUsIHJvdGF0ZWVuZCwgcm90YXRlY2FuY2VsXG4gICAgICogICAgICAgICBzd2lwZSwgc3dpcGVsZWZ0LCBzd2lwZXJpZ2h0LCBzd2lwZXVwLCBzd2lwZWRvd25cbiAgICAgKiAgICAgICAgIHRhcFxuICAgICAqIEBwYXJhbSB7SFRNTEVsZW1lbnR8SFRNTEVsZW1lbnRbXX0gZWxlbWVudHMgLSBBbiBIVE1MIGVsZW1lbnQgb3IgYW4gYXJyYXkgb2YgSFRNTCBlbGVtZW50cy5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbY2JdIC0gVGhlIGNhbGxiYWNrLiBBIGZ1bmN0aW9uIHdoaWNoIGlzIGV4ZWN1dGVkIGFmdGVyIHRoZSBldmVudCBvY2N1cnMuIFJlY2VpdmVzIHRoZSBldmVudCBvYmplY3QgYXMgdGhlXG4gICAgICogICAgIGZpcnN0IHBhcmFtdGVyXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzXSAtIEFuIG9wdGlvbnMgb2JqZWN0LiBTZWUgdGhlIGhhbW1lciBkb2N1bWVudGF0aW9uIGZvciBtb3JlIGRldGFpbHMuXG4gICAgICovXG4gICAgc3RhdGljIG9uKHR5cGVzLCBlbGVtZW50cywgY2IsIG9wdHMgPSB7fSkge1xuICAgICAgICBvcHRzID0gT2JqZWN0LmFzc2lnbih7fSwge1xuXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgaWYgKHR5cGVvZiBIYW1tZXIgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdIYW1tZXIuanMgbm90IGZvdW5kIScpXG4gICAgICAgICAgICByZXR1cm4gdGhpc1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gY29udmVydCB0byBhcnJheVxuICAgICAgICB0eXBlcyA9IEFycmF5LmlzQXJyYXkodHlwZXMpID8gdHlwZXMgOiB0eXBlcy5zcGxpdCgvXFxzLylcbiAgICAgICAgaWYgKGVsZW1lbnRzIGluc3RhbmNlb2YgTm9kZUxpc3QgfHwgZWxlbWVudHMgaW5zdGFuY2VvZiBIVE1MQ29sbGVjdGlvbikge1xuICAgICAgICAgICAgZWxlbWVudHMgPSBBcnJheS5mcm9tKGVsZW1lbnRzKVxuICAgICAgICB9XG4gICAgICAgIGVsZW1lbnRzID0gQXJyYXkuaXNBcnJheShlbGVtZW50cykgPyBlbGVtZW50cyA6IFtlbGVtZW50c11cblxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHR5cGVzLmxlbmd0aDsgaSsrKSB7XG5cbiAgICAgICAgICAgIGNvbnN0IHR5cGUgPSB0eXBlc1tpXS50b0xvd2VyQ2FzZSgpXG5cbiAgICAgICAgICAgIC8vIGxpc3Qgb2YgaGFtbWVyIGV2ZW50c1xuICAgICAgICAgICAgY29uc3QgdXNlSGFtbWVyID0gL14odGFwfGRvdWJsZXRhcHxwcmVzc3xwYW58c3dpcGV8cGluY2h8cm90YXRlKS4qJC8udGVzdCh0eXBlKVxuXG4gICAgICAgICAgICAvLyBpZiBpdCBpcyBhIGhhbW1lciBldmVudFxuICAgICAgICAgICAgaWYgKHVzZUhhbW1lcikge1xuXG4gICAgICAgICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBlbGVtZW50cy5sZW5ndGg7IGorKykge1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIGlmKGVsZW1lbnRzW2pdLnRhZ05hbWUgPT0gXCJzdmdcIikgcmV0dXJuIGZhbHNlO1xuXG4gICAgICAgICAgICAgICAgICAgIGxldCBoYW1tZXIgPSBuZXcgSGFtbWVyKGVsZW1lbnRzW2pdLCBvcHRzKVxuXG4gICAgICAgICAgICAgICAgICAgIGlmICh3aW5kb3cucHJvcGFnYXRpbmcgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBoYW1tZXIgPSBwcm9wYWdhdGluZyhoYW1tZXIpXG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyByZWNvZ25pemVyc1xuICAgICAgICAgICAgICAgICAgICBpZiAodHlwZS5zdGFydHNXaXRoKCdwYW4nKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaGFtbWVyLmdldCgncGFuJykuc2V0KE9iamVjdC5hc3NpZ24oeyBkaXJlY3Rpb246IEhhbW1lci5ESVJFQ1RJT05fQUxMIH0sIG9wdHMpKVxuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHR5cGUuc3RhcnRzV2l0aCgncGluY2gnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaGFtbWVyLmdldCgncGluY2gnKS5zZXQoT2JqZWN0LmFzc2lnbih7IGVuYWJsZTogdHJ1ZSB9LCBvcHRzKSlcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlLnN0YXJ0c1dpdGgoJ3ByZXNzJykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGhhbW1lci5nZXQoJ3ByZXNzJykuc2V0KG9wdHMpXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodHlwZS5zdGFydHNXaXRoKCdyb3RhdGUnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaGFtbWVyLmdldCgncm90YXRlJykuc2V0KE9iamVjdC5hc3NpZ24oeyBlbmFibGU6IHRydWUgfSwgb3B0cykpXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodHlwZS5zdGFydHNXaXRoKCdzd2lwZScpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBoYW1tZXIuZ2V0KCdzd2lwZScpLnNldChPYmplY3QuYXNzaWduKHsgZGlyZWN0aW9uOiBIYW1tZXIuRElSRUNUSU9OX0FMTCB9LCBvcHRzKSlcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlLnN0YXJ0c1dpdGgoJ3RhcCcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBoYW1tZXIuZ2V0KCd0YXAnKS5zZXQob3B0cylcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGhhbW1lci5vbih0eXBlLCBldmVudCA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYihldmVudClcbiAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IGVsZW1lbnRzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgICAgIEhhbW1lci5vbihlbGVtZW50c1tqXSwgdHlwZSwgZXZlbnQgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgY2IoZXZlbnQpXG4gICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG59XG5cbndpbmRvdy5JbnRlcmFjdGlvbk1hcHBlciA9IEludGVyYWN0aW9uTWFwcGVyXG4iLCIvKiogUmVwb3J0IGNhcGFiaWxpdGllcyB3aXRoIGd1YXJhbnRlZWQgdmFsdWVzLlxuICovXG5leHBvcnQgY2xhc3MgQ2FwYWJpbGl0aWVzIHtcblxuICAgIC8qKiBSZXR1cm5zIHRoZSBicm93c2VyIHVzZXJBZ2VudC5cbiAgICBAcmV0dXJuIHtzdHJpbmd9XG4gICAgKi9cbiAgICBzdGF0aWMgZ2V0IHVzZXJBZ2VudCgpIHtcbiAgICAgICAgcmV0dXJuIG5hdmlnYXRvci51c2VyQWdlbnQgfHwgJ1Vua25vd24gQWdlbnQnXG4gICAgfVxuXG4gICAgLyoqIFRlc3RzIHdoZXRoZXIgdGhlIGFwcCBpcyBydW5uaW5nIG9uIGEgbW9iaWxlIGRldmljZS5cbiAgICBJbXBsZW1lbnRlZCBhcyBhIHJlYWRvbmx5IGF0dHJpYnV0ZS5cbiAgICBAcmV0dXJuIHtib29sZWFufVxuICAgICovXG4gICAgc3RhdGljIGdldCBpc01vYmlsZSgpIHtcbiAgICAgICAgcmV0dXJuICgvTW9iaS8udGVzdChuYXZpZ2F0b3IudXNlckFnZW50KSlcbiAgICB9XG5cbiAgICAvKiogVGVzdHMgd2hldGhlciB0aGUgYXBwIGlzIHJ1bm5pbmcgb24gYSBpT1MgZGV2aWNlLlxuICAgIEltcGxlbWVudGVkIGFzIGEgcmVhZG9ubHkgYXR0cmlidXRlLlxuICAgIEByZXR1cm4ge2Jvb2xlYW59XG4gICAgKi9cbiAgICBzdGF0aWMgZ2V0IGlzSU9TKCkge1xuICAgICAgICByZXR1cm4gKC9pUGFkfGlQaG9uZXxpUG9kLy50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpKSAmJiAhd2luZG93Lk1TU3RyZWFtXG4gICAgfVxuXG4gICAgLyoqIFRlc3RzIHdoZXRoZXIgdGhlIGFwcCBpcyBydW5uaW5nIGluIGEgU2FmYXJpIGVudmlyb25tZW50LlxuICAgIFNlZSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy83OTQ0NDYwL2RldGVjdC1zYWZhcmktYnJvd3NlclxuICAgIEltcGxlbWVudGVkIGFzIGEgcmVhZG9ubHkgYXR0cmlidXRlLlxuICAgIEByZXR1cm4ge2Jvb2xlYW59XG4gICAgKi9cbiAgICBzdGF0aWMgZ2V0IGlzU2FmYXJpKCkge1xuICAgICAgICByZXR1cm4gbmF2aWdhdG9yLnZlbmRvciAmJiBuYXZpZ2F0b3IudmVuZG9yLmluZGV4T2YoJ0FwcGxlJykgPiAtMSAmJiBuYXZpZ2F0b3IudXNlckFnZW50ICYmICFuYXZpZ2F0b3IudXNlckFnZW50Lm1hdGNoKCdDcmlPUycpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRGlzdGluY3RzIGlmIHRoZSBhcHAgaXMgcnVubmluZyBpbnNpZGUgZWxlY3Ryb24gb3Igbm90LlxuICAgICAqIFxuICAgICAqIHNvdXJjZTogaHR0cHM6Ly9kaXNjdXNzLmF0b20uaW8vdC9kZXRlY3QtZWxlY3Ryb24tb3Itd2ViLXBhZ2UtcnVubmluZy8zMzE4MC8zXG4gICAgICovXG4gICAgc3RhdGljIGdldCBpc0VsZWN0cm9uKCkge1xuICAgICAgICByZXR1cm4gdHlwZW9mIHByb2Nlc3MgIT0gJ3VuZGVmaW5lZCcgJiYgcHJvY2Vzcy52ZXJzaW9ucyAmJiBwcm9jZXNzLnZlcnNpb25zLmVsZWN0cm9uICE9PSB1bmRlZmluZWRcbiAgICB9XG5cbiAgICAvKiogUmV0dXJucyB0aGUgZGlzcGxheSByZXNvbHV0aW9uLiBOZWNlc3NhcnkgZm9yIHJldGluYSBkaXNwbGF5cy5cbiAgICBAcmV0dXJuIHtudW1iZXJ9XG4gICAgKi9cbiAgICBzdGF0aWMgZ2V0IGRldmljZVBpeGVsUmF0aW8oKSB7XG4gICAgICAgIHJldHVybiB3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyB8fCAxXG4gICAgfVxuXG4gICAgLyoqIFJldHVybnMgdHJ1ZSBpZiB0aGUgZGV2aWNlIGlzIGEgbXVsdGktdG91Y2ggdGFibGUuIFRoaXMgbWV0aG9kIGlzIGN1cnJlbnRseSBub3QgdW5pdmVyc2FsIHVzYWJsZSBhbmQgbm90IHN1cmUhXG4gICAgQHJldHVybiB7Ym9vbGVhbn1cbiAgICAqL1xuICAgIHN0YXRpYyBnZXQgaXNNdWx0aVRvdWNoVGFibGUoKSB7XG4gICAgICAgIHJldHVybiBDYXBhYmlsaXRpZXMuZGV2aWNlUGl4ZWxSYXRpbyA+IDIgJiYgQ2FwYWJpbGl0aWVzLmlzTW9iaWxlID09PSBmYWxzZSAmJiAvV2luZG93cy9pLnRlc3QoQ2FwYWJpbGl0aWVzLnVzZXJBZ2VudClcbiAgICB9XG5cbiAgICAvKiogUmV0dXJucyB0cnVlIGlmIG1vdXNlIGV2ZW50cyBhcmUgc3VwcG9ydGVkXG4gICAgQHJldHVybiB7Ym9vbGVhbn1cbiAgICAqL1xuICAgIHN0YXRpYyBzdXBwb3J0c01vdXNlRXZlbnRzKCkge1xuICAgICAgICByZXR1cm4gdHlwZW9mKHdpbmRvdy5Nb3VzZUV2ZW50KSAhPSAndW5kZWZpbmVkJ1xuICAgIH1cblxuICAgIC8qKiBSZXR1cm5zIHRydWUgaWYgdG91Y2ggZXZlbnRzIGFyZSBzdXBwb3J0ZWRcbiAgICBAcmV0dXJuIHtib29sZWFufVxuICAgICovXG4gICAgc3RhdGljIHN1cHBvcnRzVG91Y2hFdmVudHMoKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2Yod2luZG93LlRvdWNoRXZlbnQpICE9ICd1bmRlZmluZWQnXG4gICAgfVxuXG4gICAgLyoqIFJldHVybnMgdHJ1ZSBpZiBwb2ludGVyIGV2ZW50cyBhcmUgc3VwcG9ydGVkXG4gICAgQHJldHVybiB7Ym9vbGVhbn1cbiAgICAqL1xuICAgIHN0YXRpYyBzdXBwb3J0c1BvaW50ZXJFdmVudHMoKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2Yod2luZG93LlBvaW50ZXJFdmVudCkgIT0gJ3VuZGVmaW5lZCdcbiAgICB9XG5cbiAgICAvKiogUmV0dXJucyB0cnVlIGlmIERPTSB0ZW1wbGF0ZXMgYXJlIHN1cHBvcnRlZFxuICAgIEByZXR1cm4ge2Jvb2xlYW59XG4gICAgKi9cbiAgICBzdGF0aWMgc3VwcG9ydHNUZW1wbGF0ZSgpIHtcbiAgICAgICAgcmV0dXJuICdjb250ZW50JyBpbiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZW1wbGF0ZScpO1xuICAgIH1cbn1cblxuLyoqIEJhc2ljIHRlc3RzIGZvciBDYXBhYmlsaXRpZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBDYXBhYmlsaXRpZXNUZXN0cyB7XG5cbiAgICBzdGF0aWMgdGVzdENvbmZpcm0oKSB7XG4gICAgICAgIGxldCBib29sID0gY29uZmlybSgnUGxlYXNlIGNvbmZpcm0nKVxuICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnZGVtbycpLmlubmVySFRNTCA9IChib29sKSA/ICdDb25maXJtZWQnIDogJ05vdCBjb25maXJtZWQnXG4gICAgfVxuXG4gICAgc3RhdGljIHRlc3RQcm9tcHQoKSB7XG4gICAgICAgIGxldCBwZXJzb24gPSBwcm9tcHQoJ1BsZWFzZSBlbnRlciB5b3VyIG5hbWUnLCAnSGFycnkgUG90dGVyJylcbiAgICAgICAgaWYgKHBlcnNvbiAhPSBudWxsKSB7XG4gICAgICAgICAgICBkZW1vLmlubmVySFRNTCA9XG4gICAgICAgICAgICAnSGVsbG8gJyArIHBlcnNvbiArICchIEhvdyBhcmUgeW91IHRvZGF5PydcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyB0ZXN0VXNlckFnZW50KCkge1xuICAgICAgICBsZXQgYWdlbnQgPSAnVXNlci1hZ2VudDogJyArIENhcGFiaWxpdGllcy51c2VyQWdlbnRcbiAgICAgICAgdXNlcl9hZ2VudC5pbm5lckhUTUwgPSBhZ2VudFxuICAgIH1cblxuICAgIHN0YXRpYyB0ZXN0RGV2aWNlUGl4ZWxSYXRpbygpIHtcbiAgICAgICAgbGV0IHZhbHVlID0gJ0RldmljZSBQaXhlbCBSYXRpbzogJyArIENhcGFiaWxpdGllcy5kZXZpY2VQaXhlbFJhdGlvXG4gICAgICAgIGRldmljZV9waXhlbF9yYXRpby5pbm5lckhUTUwgPSB2YWx1ZVxuICAgIH1cblxuICAgIHN0YXRpYyB0ZXN0TXVsdGlUb3VjaFRhYmxlKCkge1xuICAgICAgICBsZXQgdmFsdWUgPSAnSXMgdGhlIGRldmljZSBhIG11bHRpLXRvdWNoIHRhYmxlPyAnICsgQ2FwYWJpbGl0aWVzLmlzTXVsdGlUb3VjaFRhYmxlXG4gICAgICAgIG11bHRpX3RvdWNoX3RhYmxlLmlubmVySFRNTCA9IHZhbHVlXG4gICAgfVxuXG4gICAgc3RhdGljIHRlc3RTdXBwb3J0ZWRFdmVudHMoKSB7XG4gICAgICAgIGxldCBldmVudHMgPSBbXVxuICAgICAgICBpZiAoQ2FwYWJpbGl0aWVzLnN1cHBvcnRzTW91c2VFdmVudHMoKSkge1xuICAgICAgICAgICAgZXZlbnRzLnB1c2goJ01vdXNlRXZlbnRzJylcbiAgICAgICAgfVxuICAgICAgICBpZiAoQ2FwYWJpbGl0aWVzLnN1cHBvcnRzVG91Y2hFdmVudHMoKSkge1xuICAgICAgICAgICAgZXZlbnRzLnB1c2goJ1RvdWNoRXZlbnRzJylcbiAgICAgICAgfVxuICAgICAgICBpZiAoQ2FwYWJpbGl0aWVzLnN1cHBvcnRzUG9pbnRlckV2ZW50cygpKSB7XG4gICAgICAgICAgICBldmVudHMucHVzaCgnUG9pbnRlckV2ZW50cycpXG4gICAgICAgIH1cbiAgICAgICAgc3VwcG9ydGVkX2V2ZW50cy5pbm5lckhUTUwgPSAnU3VwcG9ydGVkIEV2ZW50czogJyArIGV2ZW50cy5qb2luKCcsICcpXG4gICAgfVxuXG4gICAgc3RhdGljIHRlc3RBbGwoKSB7XG4gICAgICAgIHRoaXMudGVzdFVzZXJBZ2VudCgpXG4gICAgICAgIHRoaXMudGVzdERldmljZVBpeGVsUmF0aW8oKVxuICAgICAgICB0aGlzLnRlc3RNdWx0aVRvdWNoVGFibGUoKVxuICAgICAgICB0aGlzLnRlc3RTdXBwb3J0ZWRFdmVudHMoKVxuICAgIH1cbn1cblxuLyogT3B0aW9uYWwgZ2xvYmFsIHZhcmlhYmxlcywgbmVlZGVkIGluIERvY1Rlc3RzLiAqL1xud2luZG93LkNhcGFiaWxpdGllcyA9IENhcGFiaWxpdGllc1xud2luZG93LkNhcGFiaWxpdGllc1Rlc3RzID0gQ2FwYWJpbGl0aWVzVGVzdHNcbiIsImltcG9ydCB7IFBvaW50cywgUG9seWdvbiwgQW5nbGUsIEVsZW1lbnRzIH0gZnJvbSAnLi91dGlscy5qcydcbmltcG9ydCBFdmVudHMgZnJvbSAnLi9ldmVudHMuanMnXG5pbXBvcnQgeyBJbnRlcmFjdGlvbk1hcHBlciB9IGZyb20gJy4vaW50ZXJhY3Rpb24uanMnXG5pbXBvcnQgeyBDYXBhYmlsaXRpZXMgfSBmcm9tICcuL2NhcGFiaWxpdGllcy5qcydcblxuLyoqXG4gKiBBIGJhc2UgY2xhc3MgZm9yIHNjYXR0ZXIgc3BlY2lmaWMgZXZlbnRzLlxuICpcbiAqIEBjb25zdHJ1Y3RvclxuICogQHBhcmFtIHtuYW1lfSBTdHJpbmcgLSBUaGUgbmFtZSBvZiB0aGUgZXZlbnRcbiAqIEBwYXJhbSB7dGFyZ2V0fSBPYmplY3QgLSBUaGUgdGFyZ2V0IG9mIHRoZSBldmVudFxuICovXG5leHBvcnQgY2xhc3MgQmFzZUV2ZW50IHtcbiAgICBjb25zdHJ1Y3RvcihuYW1lLCB0YXJnZXQpIHtcbiAgICAgICAgdGhpcy5uYW1lID0gbmFtZVxuICAgICAgICB0aGlzLnRhcmdldCA9IHRhcmdldFxuICAgIH1cbn1cblxuLy8gRXZlbnQgdHlwZXNcbmNvbnN0IFNUQVJUID0gJ29uU3RhcnQnXG5jb25zdCBVUERBVEUgPSAnb25VcGRhdGUnXG5jb25zdCBFTkQgPSAnb25FbmQnXG5jb25zdCBaT09NID0gJ29uWm9vbSdcbmNvbnN0IE1PVkUgPSAnb25Nb3ZlJ1xuXG4vKipcbiAqIEEgc2NhdHRlciBldmVudCB0aGF0IGRlc2NyaWJlcyBob3cgdGhlIHNjYXR0ZXIgaGFzIGNoYW5nZWQuXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKiBAcGFyYW0ge3RhcmdldH0gT2JqZWN0IC0gVGhlIHRhcmdldCBzY2F0dGVyIG9mIHRoZSBldmVudFxuICogQHBhcmFtIHtvcHRpb25hbH0gT2JqZWN0IC0gT3B0aW9uYWwgcGFyYW1ldGVyXG4gKi9cbmV4cG9ydCBjbGFzcyBTY2F0dGVyRXZlbnQgZXh0ZW5kcyBCYXNlRXZlbnQge1xuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICB0YXJnZXQsXG4gICAgICAgIHtcbiAgICAgICAgICAgIHRyYW5zbGF0ZSA9IHsgeDogMCwgeTogMCB9LFxuICAgICAgICAgICAgc2NhbGUgPSBudWxsLFxuICAgICAgICAgICAgcm90YXRlID0gMCxcbiAgICAgICAgICAgIGFib3V0ID0gbnVsbCxcbiAgICAgICAgICAgIGZhc3QgPSBmYWxzZSxcbiAgICAgICAgICAgIHR5cGUgPSBudWxsXG4gICAgICAgIH0gPSB7fVxuICAgICkge1xuICAgICAgICBzdXBlcignc2NhdHRlclRyYW5zZm9ybWVkJywgeyB0YXJnZXQ6IHRhcmdldCB9KVxuICAgICAgICB0aGlzLnRyYW5zbGF0ZSA9IHRyYW5zbGF0ZVxuICAgICAgICB0aGlzLnNjYWxlID0gc2NhbGVcbiAgICAgICAgdGhpcy5yb3RhdGUgPSByb3RhdGVcbiAgICAgICAgdGhpcy5hYm91dCA9IGFib3V0XG4gICAgICAgIHRoaXMuZmFzdCA9IGZhc3RcbiAgICAgICAgdGhpcy50eXBlID0gdHlwZVxuICAgIH1cblxuICAgIHRvU3RyaW5nKCkge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgXCJFdmVudCgnc2NhdHRlclRyYW5zZm9ybWVkJywgc2NhbGU6IFwiICtcbiAgICAgICAgICAgIHRoaXMuc2NhbGUgK1xuICAgICAgICAgICAgJyBhYm91dDogJyArXG4gICAgICAgICAgICB0aGlzLmFib3V0LnggK1xuICAgICAgICAgICAgJywgJyArXG4gICAgICAgICAgICB0aGlzLmFib3V0LnkgK1xuICAgICAgICAgICAgJyknXG4gICAgICAgIClcbiAgICB9XG59XG5cbi8qKlxuICogQSBzY2F0dGVyIHJlc2l6ZSBldmVudCB0aGF0IGRlc2NyaWJlcyBob3cgdGhlIHNjYXR0ZXIgaGFzIGNoYW5nZWQuXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKiBAcGFyYW0ge3RhcmdldH0gT2JqZWN0IC0gVGhlIHRhcmdldCBzY2F0dGVyIG9mIHRoZSBldmVudFxuICogQHBhcmFtIHtvcHRpb25hbH0gT2JqZWN0IC0gT3B0aW9uYWwgcGFyYW1ldGVyXG4gKi9cbmV4cG9ydCBjbGFzcyBSZXNpemVFdmVudCBleHRlbmRzIEJhc2VFdmVudCB7XG4gICAgY29uc3RydWN0b3IodGFyZ2V0LCB7IHdpZHRoID0gMCwgaGVpZ2h0ID0gMCB9ID0ge30pIHtcbiAgICAgICAgc3VwZXIoJ3NjYXR0ZXJSZXNpemVkJywgeyB3aWR0aDogd2lkdGgsIGhlaWdodDogaGVpZ2h0IH0pXG4gICAgICAgIHRoaXMud2lkdGggPSB3aWR0aFxuICAgICAgICB0aGlzLmhlaWdodCA9IGhlaWdodFxuICAgIH1cblxuICAgIHRvU3RyaW5nKCkge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgJ0V2ZW50KHNjYXR0ZXJSZXNpemVkIHdpZHRoOiAnICtcbiAgICAgICAgICAgIHRoaXMud2lkdGggK1xuICAgICAgICAgICAgJ2hlaWdodDogJyArXG4gICAgICAgICAgICB0aGlzLmhlaWdodCArXG4gICAgICAgICAgICAnKSdcbiAgICAgICAgKVxuICAgIH1cbn1cblxuLyoqXG4gKiBBIGFic3RyYWN0IGJhc2UgY2xhc3MgdGhhdCBpbXBsZW1lbnRzIHRoZSB0aHJvd2FibGUgYmVoYXZpb3Igb2YgYSBzY2F0dGVyXG4gKiBvYmplY3QuXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmNsYXNzIFRocm93YWJsZSB7XG4gICAgY29uc3RydWN0b3Ioe1xuICAgICAgICBtb3ZhYmxlWCA9IHRydWUsXG4gICAgICAgIG1vdmFibGVZID0gdHJ1ZSxcbiAgICAgICAgdGhyb3dWaXNpYmlsaXR5ID0gNDQsXG4gICAgICAgIHRocm93RGFtcGluZyA9IDAuOTUsXG4gICAgICAgIGF1dG9UaHJvdyA9IHRydWUsXG4gICAgICAgIG9uVGhyb3dGaW5pc2hlZCA9IG51bGxcbiAgICB9ID0ge30pIHtcbiAgICAgICAgdGhpcy5tb3ZhYmxlWCA9IG1vdmFibGVYXG4gICAgICAgIHRoaXMubW92YWJsZVkgPSBtb3ZhYmxlWVxuICAgICAgICB0aGlzLnRocm93VmlzaWJpbGl0eSA9IHRocm93VmlzaWJpbGl0eVxuICAgICAgICB0aGlzLnRocm93RGFtcGluZyA9IHRocm93RGFtcGluZ1xuICAgICAgICB0aGlzLmF1dG9UaHJvdyA9IGF1dG9UaHJvd1xuICAgICAgICB0aGlzLnZlbG9jaXRpZXMgPSBbXVxuICAgICAgICB0aGlzLnZlbG9jaXR5ID0gbnVsbFxuICAgICAgICB0aGlzLnRpbWVzdGFtcCA9IG51bGxcbiAgICAgICAgdGhpcy5vblRocm93RmluaXNoZWQgPSBvblRocm93RmluaXNoZWRcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcIm9uVGhyb3dGaW5pc2hlZFwiLCBvblRocm93RmluaXNoZWQpXG4gICAgfVxuXG4gICAgb2JzZXJ2ZVZlbG9jaXR5KCkge1xuICAgICAgICB0aGlzLmxhc3RmcmFtZSA9IHBlcmZvcm1hbmNlLm5vdygpXG4gICAgfVxuXG4gICAgYWRkVmVsb2NpdHkoZGVsdGEsIGJ1ZmZlciA9IDUpIHtcbiAgICAgICAgbGV0IHQgPSBwZXJmb3JtYW5jZS5ub3coKVxuICAgICAgICBsZXQgZHQgPSB0IC0gdGhpcy5sYXN0ZnJhbWVcbiAgICAgICAgdGhpcy5sYXN0ZnJhbWUgPSB0XG4gICAgICAgIGlmIChkdCA+IDApIHtcbiAgICAgICAgICAgIC8vIEF2b2lkIGRpdmlzaW9uIGJ5IHplcm8gZXJyb3JzIGxhdGVyIG9uXG4gICAgICAgICAgICBsZXQgdmVsb2NpdHkgPSB7IHQ6IHQsIGR0OiBkdCwgZHg6IGRlbHRhLngsIGR5OiBkZWx0YS55IH1cbiAgICAgICAgICAgIHRoaXMudmVsb2NpdGllcy5wdXNoKHZlbG9jaXR5KVxuICAgICAgICAgICAgd2hpbGUgKHRoaXMudmVsb2NpdGllcy5sZW5ndGggPiBidWZmZXIpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnZlbG9jaXRpZXMuc2hpZnQoKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgbWVhblZlbG9jaXR5KG1pbGxpc2Vjb25kcyA9IDMwKSB7XG4gICAgICAgIHRoaXMuYWRkVmVsb2NpdHkoeyB4OiAwLCB5OiAwIH0pXG4gICAgICAgIGxldCBzdW0gPSB7IHg6IDAsIHk6IDAgfVxuICAgICAgICBsZXQgY291bnQgPSAwXG4gICAgICAgIGxldCB0ID0gMFxuICAgICAgICBmb3IgKGxldCBpID0gdGhpcy52ZWxvY2l0aWVzLmxlbmd0aCAtIDE7IGkgPiAwOyBpLS0pIHtcbiAgICAgICAgICAgIGxldCB2ID0gdGhpcy52ZWxvY2l0aWVzW2ldXG4gICAgICAgICAgICB0ICs9IHYuZHRcbiAgICAgICAgICAgIGxldCBudiA9IHsgeDogdi5keCAvIHYuZHQsIHk6IHYuZHkgLyB2LmR0IH1cbiAgICAgICAgICAgIHN1bSA9IFBvaW50cy5hZGQoc3VtLCBudilcbiAgICAgICAgICAgIGNvdW50ICs9IDFcbiAgICAgICAgICAgIGlmICh0ID4gbWlsbGlzZWNvbmRzKSB7XG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoY291bnQgPT09IDApIHJldHVybiBzdW0gLy8gZW1wdHkgdmVjdG9yXG4gICAgICAgIHJldHVybiBQb2ludHMubXVsdGlwbHlTY2FsYXIoc3VtLCAxIC8gY291bnQpXG4gICAgfVxuXG4gICAga2lsbEFuaW1hdGlvbigpIHtcbiAgICAgICAgdGhpcy52ZWxvY2l0eSA9IG51bGxcbiAgICAgICAgdGhpcy52ZWxvY2l0aWVzID0gW11cbiAgICB9XG5cbiAgICBzdGFydFRocm93KCkge1xuICAgICAgICB0aGlzLnZlbG9jaXR5ID0gdGhpcy5tZWFuVmVsb2NpdHkoKVxuICAgICAgICBpZiAodGhpcy52ZWxvY2l0eSAhPSBudWxsKSB7XG4gICAgICAgICAgICAvLyBDYWxsIG5leHQgdmVsb2NpdHkgdG8gYW5zdXJlIHRoYXQgc3BlY2lhbGl6YXRpb25zXG4gICAgICAgICAgICAvLyB0aGF0IHVzZSBrZWVwT25TdGFnZSBhcmUgY2FsbGVkXG4gICAgICAgICAgICB0aGlzLnZlbG9jaXR5ID0gdGhpcy5uZXh0VmVsb2NpdHkodGhpcy52ZWxvY2l0eSlcbiAgICAgICAgICAgIGlmICh0aGlzLmF1dG9UaHJvdykgdGhpcy5hbmltYXRlVGhyb3cocGVyZm9ybWFuY2Uubm93KCkpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLm9uRHJhZ0NvbXBsZXRlKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGFuaW1hdGVUaHJvdyh0aW1lKSB7XG4gICAgICAgIGlmICh0aGlzLnZlbG9jaXR5ICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxldCB0ID0gcGVyZm9ybWFuY2Uubm93KClcbiAgICAgICAgICAgIGxldCBkdCA9IHQgLSB0aGlzLmxhc3RmcmFtZVxuICAgICAgICAgICAgdGhpcy5sYXN0ZnJhbWUgPSB0XG4gICAgICAgICAgICAvLyBjb25zb2xlLmxvZyhcImFuaW1hdGVUaHJvd1wiLCBkdClcbiAgICAgICAgICAgIGxldCBuZXh0ID0gdGhpcy5uZXh0VmVsb2NpdHkodGhpcy52ZWxvY2l0eSlcbiAgICAgICAgICAgIGxldCBwcmV2TGVuZ3RoID0gUG9pbnRzLmxlbmd0aCh0aGlzLnZlbG9jaXR5KVxuICAgICAgICAgICAgbGV0IG5leHRMZW5ndGggPSBQb2ludHMubGVuZ3RoKG5leHQpXG4gICAgICAgICAgICBpZiAobmV4dExlbmd0aCA+IHByZXZMZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBsZXQgZmFjdG9yID0gbmV4dExlbmd0aCAvIHByZXZMZW5ndGhcbiAgICAgICAgICAgICAgICBuZXh0ID0gUG9pbnRzLm11bHRpcGx5U2NhbGFyKG5leHQsIDEgLyBmYWN0b3IpXG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ1ByZXZlbnQgYWNjZWxlcmF0aW9uJywgZmFjdG9yLCB0aGlzLnZlbG9jaXR5LCBuZXh0KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy52ZWxvY2l0eSA9IG5leHRcbiAgICAgICAgICAgIGxldCBkID0gUG9pbnRzLm11bHRpcGx5U2NhbGFyKHRoaXMudmVsb2NpdHksIGR0KVxuICAgICAgICAgICAgdGhpcy5fbW92ZShkKVxuXG4gICAgICAgICAgICB0aGlzLm9uRHJhZ1VwZGF0ZShkKVxuICAgICAgICAgICAgaWYgKGR0ID09IDAgfHwgdGhpcy5uZWVkc0FuaW1hdGlvbigpKSB7XG4gICAgICAgICAgICAgICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKHRoaXMuYW5pbWF0ZVRocm93LmJpbmQodGhpcykpXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmlzT3V0c2lkZSgpKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSh0aGlzLmFuaW1hdGVUaHJvdy5iaW5kKHRoaXMpKVxuICAgICAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5vbkRyYWdDb21wbGV0ZSgpXG4gICAgICAgIGlmICh0aGlzLm9uVGhyb3dGaW5pc2hlZCAhPSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9uVGhyb3dGaW5pc2hlZCgpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBuZWVkc0FuaW1hdGlvbigpIHtcbiAgICAgICAgaWYgKHRoaXMudmVsb2NpdHkgPT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFBvaW50cy5sZW5ndGgodGhpcy52ZWxvY2l0eSkgPiAwLjAxXG4gICAgfVxuXG4gICAgbmV4dFZlbG9jaXR5KHZlbG9jaXR5KSB7XG4gICAgICAgIC8vIE11c3QgYmUgb3ZlcndyaXR0ZW46IGNvbXB1dGVzIHRoZSBjaGFuZ2VkIHZlbG9jaXR5LiBJbXBsZW1lbnRcbiAgICAgICAgLy8gZGFtcGluZywgY29sbGlzb24gZGV0ZWN0aW9uLCBldGMuIGhlcmVcbiAgICAgICAgbGV0IG5leHQgPSBQb2ludHMubXVsdGlwbHlTY2FsYXIodmVsb2NpdHksIHRoaXMudGhyb3dEYW1waW5nKVxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgeDogKHRoaXMubW92YWJsZVgpID8gbmV4dC54IDogMCxcbiAgICAgICAgICAgIHk6ICh0aGlzLm1vdmFibGVZKSA/IG5leHQueSA6IDBcbiAgICAgICAgfVxuICAgIH1cblxuICAgIF9tb3ZlKGRlbHRhKSB7XG4gICAgICAgIC8vIE92ZXJ3cml0ZSBpZiBuZWNlc3NhcnlcbiAgICB9XG5cbiAgICBvbkRyYWdDb21wbGV0ZSgpIHtcbiAgICAgICAgLy8gT3ZlcndyaXRlIGlmIG5lY2Vzc2FyeVxuICAgIH1cblxuICAgIG9uRHJhZ1VwZGF0ZShkZWx0YSkge1xuICAgICAgICAvLyBPdmVyd3JpdGUgaWYgbmVjZXNzYXJ5XG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgQWJzdHJhY3RTY2F0dGVyIGV4dGVuZHMgVGhyb3dhYmxlIHtcbiAgICBjb25zdHJ1Y3Rvcih7XG4gICAgICAgIG1pblNjYWxlID0gMC4xLFxuICAgICAgICBtYXhTY2FsZSA9IDEuMCxcbiAgICAgICAgc3RhcnRTY2FsZSA9IDEuMCxcbiAgICAgICAgYXV0b0JyaW5nVG9Gcm9udCA9IHRydWUsXG4gICAgICAgIGF1dG9UaHJvdyA9IHRydWUsXG4gICAgICAgIHRyYW5zbGF0YWJsZSA9IHRydWUsXG4gICAgICAgIHNjYWxhYmxlID0gdHJ1ZSxcbiAgICAgICAgcm90YXRhYmxlID0gdHJ1ZSxcbiAgICAgICAgcmVzaXphYmxlID0gZmFsc2UsXG4gICAgICAgIG1vdmFibGVYID0gdHJ1ZSxcbiAgICAgICAgbW92YWJsZVkgPSB0cnVlLFxuICAgICAgICB0aHJvd1Zpc2liaWxpdHkgPSA0NCxcbiAgICAgICAgdGhyb3dEYW1waW5nID0gMC45NSxcbiAgICAgICAgb3ZlcmRvU2NhbGluZyA9IDEsXG4gICAgICAgIG1vdXNlWm9vbUZhY3RvciA9IDEuMSxcbiAgICAgICAgcm90YXRpb25EZWdyZWVzID0gbnVsbCxcbiAgICAgICAgcm90YXRpb24gPSBudWxsLFxuICAgICAgICBvblRyYW5zZm9ybSA9IG51bGwsXG4gICAgICAgIGludGVyYWN0aXZlID0gdHJ1ZSxcbiAgICAgICAgb25DbG9zZSA9IG51bGwsXG4gICAgICAgIG9uVGhyb3dGaW5pc2hlZCA9IG51bGwsXG4gICAgICAgIHNjYWxlQXV0b0Nsb3NlID0gZmFsc2UsXG4gICAgICAgIHNjYWxlQ2xvc2VUaHJlc2hvbGQgPSAwLjEwLFxuICAgICAgICBzY2FsZUNsb3NlQnVmZmVyID0gMC4wNVxuICAgIH0gPSB7fSkge1xuICAgICAgICBpZiAocm90YXRpb25EZWdyZWVzICE9IG51bGwgJiYgcm90YXRpb24gIT0gbnVsbCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdVc2Ugcm90YXRpb25EZWdyZWVzIG9yIHJvdGF0aW9uIGJ1dCBub3QgYm90aCcpXG4gICAgICAgIH0gZWxzZSBpZiAocm90YXRpb24gIT0gbnVsbCkge1xuICAgICAgICAgICAgcm90YXRpb25EZWdyZWVzID0gQW5nbGUucmFkaWFuMmRlZ3JlZShyb3RhdGlvbilcbiAgICAgICAgfSBlbHNlIGlmIChyb3RhdGlvbkRlZ3JlZXMgPT0gbnVsbCkge1xuICAgICAgICAgICAgcm90YXRpb25EZWdyZWVzID0gMFxuICAgICAgICB9XG4gICAgICAgIHN1cGVyKHtcbiAgICAgICAgICAgIG1vdmFibGVYLFxuICAgICAgICAgICAgbW92YWJsZVksXG4gICAgICAgICAgICB0aHJvd1Zpc2liaWxpdHksXG4gICAgICAgICAgICB0aHJvd0RhbXBpbmcsXG4gICAgICAgICAgICBhdXRvVGhyb3csXG4gICAgICAgICAgICBvblRocm93RmluaXNoZWRcbiAgICAgICAgfSlcblxuICAgICAgICAvKipcbiAgICAgICAgICogQ2xvc2VzIHRoZSBjYXJkIHdoZW4gdGhlIG1pblNjYWxlIGlzIHJlYWNoZWQgYW5kIHRoZSBcbiAgICAgICAgICogY2FyZCBpcyByZWxlYXNlZC4gQ2FyZCBjYW4gYmUgc2F2ZWQgYnkgc2NhbGluZyBpdCB1cCBhZ2Fpbi5cbiAgICAgICAgICovXG4gICAgICAgIHRoaXMuc2NhbGVBdXRvQ2xvc2UgPSBzY2FsZUF1dG9DbG9zZVxuICAgICAgICB0aGlzLnNjYWxlQ2xvc2VUaHJlc2hvbGQgPSBzY2FsZUNsb3NlVGhyZXNob2xkXG4gICAgICAgIHRoaXMuc2NhbGVDbG9zZUJ1ZmZlciA9IHNjYWxlQ2xvc2VCdWZmZXJcbiAgICAgICAgdGhpcy5zY2FsZUF1dG9DbG9zZVRpbWVvdXQgPSBudWxsXG5cbiAgICAgICAgdGhpcy5pbnRlcmFjdGl2ZSA9IGludGVyYWN0aXZlXG4gICAgICAgIHRoaXMuc3RhcnRSb3RhdGlvbkRlZ3JlZXMgPSByb3RhdGlvbkRlZ3JlZXNcbiAgICAgICAgdGhpcy5zdGFydFNjYWxlID0gc3RhcnRTY2FsZSAvLyBOZWVkZWQgdG8gcmVzZXQgb2JqZWN0XG4gICAgICAgIHRoaXMubWluU2NhbGUgPSBtaW5TY2FsZVxuICAgICAgICB0aGlzLm1heFNjYWxlID0gbWF4U2NhbGVcbiAgICAgICAgdGhpcy5vdmVyZG9TY2FsaW5nID0gb3ZlcmRvU2NhbGluZ1xuICAgICAgICB0aGlzLnRyYW5zbGF0YWJsZSA9IHRyYW5zbGF0YWJsZVxuICAgICAgICBpZiAoIXRyYW5zbGF0YWJsZSkge1xuICAgICAgICAgICAgdGhpcy5tb3ZhYmxlWCA9IGZhbHNlXG4gICAgICAgICAgICB0aGlzLm1vdmFibGVZID0gZmFsc2VcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnNjYWxhYmxlID0gc2NhbGFibGVcbiAgICAgICAgdGhpcy5yb3RhdGFibGUgPSByb3RhdGFibGVcbiAgICAgICAgdGhpcy5yZXNpemFibGUgPSByZXNpemFibGVcbiAgICAgICAgdGhpcy5tb3VzZVpvb21GYWN0b3IgPSBtb3VzZVpvb21GYWN0b3JcbiAgICAgICAgdGhpcy5hdXRvQnJpbmdUb0Zyb250ID0gYXV0b0JyaW5nVG9Gcm9udFxuICAgICAgICB0aGlzLmRyYWdnaW5nID0gZmFsc2VcbiAgICAgICAgdGhpcy5vblRyYW5zZm9ybSA9IG9uVHJhbnNmb3JtICE9IG51bGwgPyBbb25UcmFuc2Zvcm1dIDogbnVsbFxuICAgICAgICB0aGlzLm9uQ2xvc2UgPSBvbkNsb3NlICE9IG51bGwgPyBbb25DbG9zZV0gOiBudWxsXG4gICAgfVxuXG4gICAgYWRkQ2xvc2VFdmVudENhbGxiYWNrKGNhbGxiYWNrKSB7XG4gICAgICAgIGlmICh0aGlzLm9uQ2xvc2UgPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5vbkNsb3NlID0gW11cbiAgICAgICAgfVxuICAgICAgICB0aGlzLm9uQ2xvc2UucHVzaChjYWxsYmFjaylcbiAgICB9XG5cbiAgICBhZGRUcmFuc2Zvcm1FdmVudENhbGxiYWNrKGNhbGxiYWNrKSB7XG4gICAgICAgIGlmICh0aGlzLm9uVHJhbnNmb3JtID09IG51bGwpIHtcbiAgICAgICAgICAgIHRoaXMub25UcmFuc2Zvcm0gPSBbXVxuICAgICAgICB9XG4gICAgICAgIHRoaXMub25UcmFuc2Zvcm0ucHVzaChjYWxsYmFjaylcbiAgICB9XG5cbiAgICBzdGFydEdlc3R1cmUoaW50ZXJhY3Rpb24pIHtcbiAgICAgICAgdGhpcy5icmluZ1RvRnJvbnQoKVxuICAgICAgICB0aGlzLmtpbGxBbmltYXRpb24oKVxuICAgICAgICB0aGlzLm9ic2VydmVWZWxvY2l0eSgpXG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgfVxuXG4gICAgY2xvc2UoKSB7XG4gICAgICAgIGlmICh0aGlzLm9uQ2xvc2UpIHtcbiAgICAgICAgICAgIHRoaXMub25DbG9zZS5mb3JFYWNoKGNhbGxiYWNrID0+IGNhbGxiYWNrKHRoaXMpKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZ2VzdHVyZShpbnRlcmFjdGlvbikge1xuICAgICAgICBsZXQgZGVsdGEgPSBpbnRlcmFjdGlvbi5kZWx0YSgpXG4gICAgICAgIC8vY29uc29sZS5sb2coXCJnZXN0dXJlXCIsIGRlbHRhKVxuICAgICAgICBpZiAoZGVsdGEgIT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5hZGRWZWxvY2l0eShkZWx0YSlcbiAgICAgICAgICAgIHRoaXMudHJhbnNmb3JtKGRlbHRhLCBkZWx0YS56b29tLCBkZWx0YS5yb3RhdGUsIGRlbHRhLmFib3V0KVxuICAgICAgICAgICAgaWYgKGRlbHRhLnpvb20gIT0gMSkgdGhpcy5pbnRlcmFjdGlvbkFuY2hvciA9IGRlbHRhLmFib3V0XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBnZXQgcG9seWdvbigpIHtcbiAgICAgICAgbGV0IHcyID0gdGhpcy53aWR0aCAqIHRoaXMuc2NhbGUgLyAyXG4gICAgICAgIGxldCBoMiA9IHRoaXMuaGVpZ2h0ICogdGhpcy5zY2FsZSAvIDJcbiAgICAgICAgbGV0IGNlbnRlciA9IHRoaXMuY2VudGVyXG4gICAgICAgIGxldCBwb2x5Z29uID0gbmV3IFBvbHlnb24oY2VudGVyKVxuICAgICAgICBwb2x5Z29uLmFkZFBvaW50KHsgeDogLXcyLCB5OiAtaDIgfSlcbiAgICAgICAgcG9seWdvbi5hZGRQb2ludCh7IHg6IHcyLCB5OiAtaDIgfSlcbiAgICAgICAgcG9seWdvbi5hZGRQb2ludCh7IHg6IHcyLCB5OiBoMiB9KVxuICAgICAgICBwb2x5Z29uLmFkZFBvaW50KHsgeDogLXcyLCB5OiBoMiB9KVxuICAgICAgICBwb2x5Z29uLnJvdGF0ZSh0aGlzLnJvdGF0aW9uKVxuICAgICAgICByZXR1cm4gcG9seWdvblxuICAgIH1cblxuICAgIGlzT3V0c2lkZSgpIHtcbiAgICAgICAgbGV0IHN0YWdlUG9seWdvbiA9IHRoaXMuY29udGFpbmVyUG9seWdvblxuICAgICAgICBpZiAoc3RhZ2VQb2x5Z29uID09IG51bGwpXG4gICAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgbGV0IHBvbHlnb24gPSB0aGlzLnBvbHlnb25cbiAgICAgICAgaWYgKHBvbHlnb24gPT0gbnVsbClcbiAgICAgICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgICBsZXQgcmVzdWx0ID0gc3RhZ2VQb2x5Z29uLmludGVyc2VjdHNXaXRoKHBvbHlnb24pXG4gICAgICAgIHJldHVybiByZXN1bHQgPT09IGZhbHNlIHx8IHJlc3VsdC5vdmVybGFwIDwgdGhpcy50aHJvd1Zpc2liaWxpdHlcbiAgICB9XG5cbiAgICByZWNlbnRlcigpIHtcbiAgICAgICAgLy8gUmV0dXJuIGEgc21hbGwgdmVjdG9yIHRoYXQgZ3VhcmFudGVlcyB0aGF0IHRoZSBzY2F0dGVyIGlzIG1vdmluZ1xuICAgICAgICAvLyB0b3dhcmRzIHRoZSBjZW50ZXIgb2YgdGhlIHN0YWdlXG4gICAgICAgIGxldCBjZW50ZXIgPSB0aGlzLmNlbnRlclxuICAgICAgICBsZXQgdGFyZ2V0ID0gdGhpcy5jb250YWluZXIuY2VudGVyXG4gICAgICAgIGxldCBkZWx0YSA9IFBvaW50cy5zdWJ0cmFjdCh0YXJnZXQsIGNlbnRlcilcbiAgICAgICAgcmV0dXJuIFBvaW50cy5ub3JtYWxpemUoZGVsdGEpXG4gICAgfVxuXG4gICAgbmV4dFZlbG9jaXR5KHZlbG9jaXR5KSB7XG4gICAgICAgIHJldHVybiB0aGlzLmtlZXBPblN0YWdlKHZlbG9jaXR5KVxuICAgIH1cblxuICAgIGJvdW5jaW5nKCkge1xuICAgICAgICAvLyBJbXBsZW1lbnRzIHRoZSBib3VuY2luZyBiZWhhdmlvciBvZiB0aGUgc2NhdHRlci4gTW92ZXMgdGhlIHNjYXR0ZXJcbiAgICAgICAgLy8gdG8gdGhlIGNlbnRlciBvZiB0aGUgc3RhZ2UgaWYgdGhlIHNjYXR0ZXIgaXMgb3V0c2lkZSB0aGUgc3RhZ2Ugb3JcbiAgICAgICAgLy8gbm90IHdpdGhpbiB0aGUgbGltaXRzIG9mIHRoZSB0aHJvd1Zpc2liaWxpdHkuXG5cbiAgICAgICAgbGV0IHN0YWdlUG9seWdvbiA9IHRoaXMuY29udGFpbmVyUG9seWdvblxuICAgICAgICBsZXQgcG9seWdvbiA9IHRoaXMucG9seWdvblxuICAgICAgICBsZXQgcmVzdWx0ID0gc3RhZ2VQb2x5Z29uLmludGVyc2VjdHNXaXRoKHBvbHlnb24pXG4gICAgICAgIGlmIChyZXN1bHQgPT09IGZhbHNlIHx8IHJlc3VsdC5vdmVybGFwIDwgdGhpcy50aHJvd1Zpc2liaWxpdHkpIHtcbiAgICAgICAgICAgIGxldCBjdiA9IHRoaXMucmVjZW50ZXIoKVxuICAgICAgICAgICAgbGV0IHJlY2VudGVyZWQgPSBmYWxzZVxuICAgICAgICAgICAgd2hpbGUgKHJlc3VsdCA9PT0gZmFsc2UgfHwgcmVzdWx0Lm92ZXJsYXAgPCB0aGlzLnRocm93VmlzaWJpbGl0eSkge1xuICAgICAgICAgICAgICAgIHBvbHlnb24uY2VudGVyLnggKz0gY3YueFxuICAgICAgICAgICAgICAgIHBvbHlnb24uY2VudGVyLnkgKz0gY3YueVxuICAgICAgICAgICAgICAgIHRoaXMuX21vdmUoY3YpXG4gICAgICAgICAgICAgICAgcmVzdWx0ID0gc3RhZ2VQb2x5Z29uLmludGVyc2VjdHNXaXRoKHBvbHlnb24pXG4gICAgICAgICAgICAgICAgcmVjZW50ZXJlZCA9IHRydWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiByZWNlbnRlcmVkXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAga2VlcE9uU3RhZ2UodmVsb2NpdHksIGNvbGxpc2lvbiA9IDAuNSkge1xuICAgICAgICBsZXQgc3RhZ2VQb2x5Z29uID0gdGhpcy5jb250YWluZXJQb2x5Z29uXG4gICAgICAgIGlmICghc3RhZ2VQb2x5Z29uKSByZXR1cm5cbiAgICAgICAgbGV0IHBvbHlnb24gPSB0aGlzLnBvbHlnb25cbiAgICAgICAgbGV0IGJvdW5jZWQgPSB0aGlzLmJvdW5jaW5nKClcbiAgICAgICAgaWYgKGJvdW5jZWQpIHtcbiAgICAgICAgICAgIGxldCBzdGFnZSA9IHRoaXMuY29udGFpbmVyQm91bmRzXG4gICAgICAgICAgICBsZXQgeCA9IHRoaXMuY2VudGVyLnhcbiAgICAgICAgICAgIGxldCB5ID0gdGhpcy5jZW50ZXIueVxuICAgICAgICAgICAgbGV0IGR4ID0gdGhpcy5tb3ZhYmxlWCA/IHZlbG9jaXR5LnggOiAwXG4gICAgICAgICAgICBsZXQgZHkgPSB0aGlzLm1vdmFibGVZID8gdmVsb2NpdHkueSA6IDBcbiAgICAgICAgICAgIGxldCBmYWN0b3IgPSB0aGlzLnRocm93RGFtcGluZ1xuICAgICAgICAgICAgLy8gaWYgKHJlY2VudGVyZWQpIHtcbiAgICAgICAgICAgIGlmICh4IDwgMCkge1xuICAgICAgICAgICAgICAgIGR4ID0gLWR4XG4gICAgICAgICAgICAgICAgZmFjdG9yID0gY29sbGlzaW9uXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoeCA+IHN0YWdlLndpZHRoKSB7XG4gICAgICAgICAgICAgICAgZHggPSAtZHhcbiAgICAgICAgICAgICAgICBmYWN0b3IgPSBjb2xsaXNpb25cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh5IDwgMCkge1xuICAgICAgICAgICAgICAgIGR5ID0gLWR5XG4gICAgICAgICAgICAgICAgZmFjdG9yID0gY29sbGlzaW9uXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoeSA+IHN0YWdlLmhlaWdodCkge1xuICAgICAgICAgICAgICAgIGR5ID0gLWR5XG4gICAgICAgICAgICAgICAgZmFjdG9yID0gY29sbGlzaW9uXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyB9XG4gICAgICAgICAgICByZXR1cm4gUG9pbnRzLm11bHRpcGx5U2NhbGFyKHsgeDogZHgsIHk6IGR5IH0sIGZhY3RvcilcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gc3VwZXIubmV4dFZlbG9jaXR5KHZlbG9jaXR5KVxuICAgIH1cblxuICAgIGVuZEdlc3R1cmUoaW50ZXJhY3Rpb24pIHtcbiAgICAgICAgdGhpcy5zdGFydFRocm93KClcbiAgICAgICAgdGhpcy5fY2hlY2tBdXRvQ2xvc2UoKVxuICAgIH1cblxuICAgIF9jaGVja0F1dG9DbG9zZSgpIHtcbiAgICAgICAgaWYgKHRoaXMuc2NhbGVBdXRvQ2xvc2UpXG4gICAgICAgICAgICBpZiAodGhpcy5zY2FsZSA8IHRoaXMubWluU2NhbGUgKyB0aGlzLnNjYWxlQ2xvc2VUaHJlc2hvbGQgLSB0aGlzLnNjYWxlQ2xvc2VCdWZmZXIpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnpvb20odGhpcy5taW5TY2FsZSwgeyBhbmltYXRlOiAwLjIsIG9uQ29tcGxldGU6IHRoaXMuY2xvc2UuYmluZCh0aGlzKSB9KVxuICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLnNjYWxlIDwgdGhpcy5taW5TY2FsZSArIHRoaXMuc2NhbGVDbG9zZVRocmVzaG9sZCkge1xuICAgICAgICAgICAgICAgIHRoaXMuem9vbSh0aGlzLm1pblNjYWxlICsgdGhpcy5zY2FsZUNsb3NlVGhyZXNob2xkLCB7IGFuaW1hdGU6IDAuNCB9KVxuICAgICAgICAgICAgfVxuICAgIH1cblxuICAgIHJvdGF0ZURlZ3JlZXMoZGVncmVlcywgYW5jaG9yKSB7XG4gICAgICAgIGxldCByYWQgPSBBbmdsZS5kZWdyZWUycmFkaWFuKGRlZ3JlZXMpXG4gICAgICAgIHRoaXMucm90YXRlKHJhZCwgYW5jaG9yKVxuICAgIH1cblxuICAgIHJvdGF0ZShyYWQsIGFuY2hvcikge1xuICAgICAgICB0aGlzLnRyYW5zZm9ybSh7IHg6IDAsIHk6IDAgfSwgMS4wLCByYWQsIGFuY2hvcilcbiAgICB9XG5cbiAgICBtb3ZlKGQsIHsgYW5pbWF0ZSA9IDAgfSA9IHt9KSB7XG4gICAgICAgIGlmICh0aGlzLnRyYW5zbGF0YWJsZSkge1xuICAgICAgICAgICAgaWYgKGFuaW1hdGUgPiAwKSB7XG4gICAgICAgICAgICAgICAgbGV0IHN0YXJ0UG9zID0gdGhpcy5wb3NpdGlvblxuICAgICAgICAgICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLCBhbmltYXRlLCB7XG4gICAgICAgICAgICAgICAgICAgIHg6ICcrPScgKyBkLngsXG4gICAgICAgICAgICAgICAgICAgIHk6ICcrPScgKyBkLnksXG4gICAgICAgICAgICAgICAgICAgIC8qIHNjYWxlOiBzY2FsZSwgdW86IG5vdCBkZWZpbmVkLCB3aHkgd2FzIHRoaXMgaGVyZT8gKi9cbiAgICAgICAgICAgICAgICAgICAgb25VcGRhdGU6IGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgbGV0IHAgPSB0aGlzLnBvc2l0aW9uXG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgZHggPSBwLnggLSBzdGFydFBvcy54XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgZHkgPSBwLnggLSBzdGFydFBvcy55XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uTW92ZWQoZHgsIGR5KVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fbW92ZShkKVxuICAgICAgICAgICAgICAgIHRoaXMub25Nb3ZlZChkLngsIGQueSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIG1vdmVUbyhwLCB7IGFuaW1hdGUgPSAwIH0gPSB7fSkge1xuICAgICAgICBsZXQgYyA9IHRoaXMub3JpZ2luXG4gICAgICAgIGxldCBkZWx0YSA9IFBvaW50cy5zdWJ0cmFjdChwLCBjKVxuICAgICAgICB0aGlzLm1vdmUoZGVsdGEsIHsgYW5pbWF0ZTogYW5pbWF0ZSB9KVxuICAgIH1cblxuICAgIGNlbnRlckF0KHAsIHsgYW5pbWF0ZSA9IDAgfSA9IHt9KSB7XG4gICAgICAgIGxldCBjID0gdGhpcy5jZW50ZXJcbiAgICAgICAgbGV0IGRlbHRhID0gUG9pbnRzLnN1YnRyYWN0KHAsIGMpXG4gICAgICAgIHRoaXMubW92ZShkZWx0YSwgeyBhbmltYXRlOiBhbmltYXRlIH0pXG4gICAgfVxuXG4gICAgem9vbShcbiAgICAgICAgc2NhbGUsXG4gICAgICAgIHtcbiAgICAgICAgICAgIGFuaW1hdGUgPSAwLFxuICAgICAgICAgICAgYWJvdXQgPSBudWxsLFxuICAgICAgICAgICAgZGVsYXkgPSAwLFxuICAgICAgICAgICAgeCA9IG51bGwsXG4gICAgICAgICAgICB5ID0gbnVsbCxcbiAgICAgICAgICAgIG9uQ29tcGxldGUgPSBudWxsXG4gICAgICAgIH0gPSB7fVxuICAgICkge1xuICAgICAgICBsZXQgYW5jaG9yID0gYWJvdXQgfHwgdGhpcy5jZW50ZXJcbiAgICAgICAgaWYgKHNjYWxlICE9IHRoaXMuc2NhbGUpIHtcbiAgICAgICAgICAgIGlmIChhbmltYXRlID4gMCkge1xuICAgICAgICAgICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLCBhbmltYXRlLCB7XG4gICAgICAgICAgICAgICAgICAgIHNjYWxlOiBzY2FsZSxcbiAgICAgICAgICAgICAgICAgICAgZGVsYXk6IGRlbGF5LFxuICAgICAgICAgICAgICAgICAgICBvbkNvbXBsZXRlOiBvbkNvbXBsZXRlLFxuICAgICAgICAgICAgICAgICAgICBvblVwZGF0ZTogdGhpcy5vblpvb21lZC5iaW5kKHRoaXMpXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zY2FsZSA9IHNjYWxlXG4gICAgICAgICAgICAgICAgdGhpcy5vblpvb21lZChhbmNob3IpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBfbW92ZShkZWx0YSkge1xuICAgICAgICB0aGlzLnggKz0gdGhpcy5tb3ZhYmxlWCA/IGRlbHRhLnggOiAwXG4gICAgICAgIHRoaXMueSArPSB0aGlzLm1vdmFibGVYID8gZGVsdGEueSA6IDBcbiAgICB9XG5cbiAgICB0cmFuc2Zvcm0odHJhbnNsYXRlLCB6b29tLCByb3RhdGUsIGFuY2hvcikge1xuICAgICAgICBsZXQgZGVsdGEgPSB7XG4gICAgICAgICAgICB4OiB0aGlzLm1vdmFibGVYID8gdHJhbnNsYXRlLnggOiAwLFxuICAgICAgICAgICAgeTogdGhpcy5tb3ZhYmxlWSA/IHRyYW5zbGF0ZS55IDogMFxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnJlc2l6YWJsZSkgdmFyIHZ6b29tID0gem9vbVxuICAgICAgICBpZiAoIXRoaXMudHJhbnNsYXRhYmxlKSBkZWx0YSA9IHsgeDogMCwgeTogMCB9XG4gICAgICAgIGlmICghdGhpcy5yb3RhdGFibGUpIHJvdGF0ZSA9IDBcbiAgICAgICAgaWYgKCF0aGlzLnNjYWxhYmxlKSB6b29tID0gMS4wXG4gICAgICAgIGlmICh6b29tID09IDEuMCAmJiByb3RhdGUgPT0gMCkge1xuICAgICAgICAgICAgdGhpcy5fbW92ZShkZWx0YSlcbiAgICAgICAgICAgIGlmICh0aGlzLm9uVHJhbnNmb3JtICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgICAgICAgICAgICAgdHJhbnNsYXRlOiBkZWx0YSxcbiAgICAgICAgICAgICAgICAgICAgc2NhbGU6IHRoaXMuc2NhbGUsXG4gICAgICAgICAgICAgICAgICAgIHJvdGF0ZTogMCxcbiAgICAgICAgICAgICAgICAgICAgYWJvdXQ6IGFuY2hvcixcbiAgICAgICAgICAgICAgICAgICAgZmFzdDogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIHR5cGU6IFVQREFURVxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgdGhpcy5vblRyYW5zZm9ybS5mb3JFYWNoKGZ1bmN0aW9uIChmKSB7XG4gICAgICAgICAgICAgICAgICAgIGYoZXZlbnQpXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIGxldCBvcmlnaW4gPSB0aGlzLnJvdGF0aW9uT3JpZ2luXG4gICAgICAgIGxldCBiZXRhID0gUG9pbnRzLmFuZ2xlKG9yaWdpbiwgYW5jaG9yKVxuICAgICAgICBsZXQgZGlzdGFuY2UgPSBQb2ludHMuZGlzdGFuY2Uob3JpZ2luLCBhbmNob3IpXG4gICAgICAgIGxldCB7IHNjYWxlOiBuZXdTY2FsZSwgem9vbTogdGhyZXNob2xkZWRab29tIH0gPSB0aGlzLmNhbGN1bGF0ZVNjYWxlKHpvb20pXG5cbiAgICAgICAgbGV0IG5ld09yaWdpbiA9IFBvaW50cy5hcmMoYW5jaG9yLCBiZXRhICsgcm90YXRlLCBkaXN0YW5jZSAqIHRocmVzaG9sZGVkWm9vbSlcbiAgICAgICAgbGV0IGV4dHJhID0gUG9pbnRzLnN1YnRyYWN0KG5ld09yaWdpbiwgb3JpZ2luKVxuICAgICAgICBsZXQgb2Zmc2V0ID0gUG9pbnRzLnN1YnRyYWN0KGFuY2hvciwgb3JpZ2luKVxuICAgICAgICB0aGlzLl9tb3ZlKG9mZnNldClcbiAgICAgICAgdGhpcy5zY2FsZSA9IG5ld1NjYWxlXG4gICAgICAgIHRoaXMucm90YXRpb24gKz0gcm90YXRlXG4gICAgICAgIG9mZnNldCA9IFBvaW50cy5uZWdhdGUob2Zmc2V0KVxuICAgICAgICBvZmZzZXQgPSBQb2ludHMuYWRkKG9mZnNldCwgZXh0cmEpXG4gICAgICAgIG9mZnNldCA9IFBvaW50cy5hZGQob2Zmc2V0LCB0cmFuc2xhdGUpXG4gICAgICAgIHRoaXMuX21vdmUob2Zmc2V0KVxuXG4gICAgICAgIGRlbHRhLnggKz0gZXh0cmEueFxuICAgICAgICBkZWx0YS55ICs9IGV4dHJhLnlcbiAgICAgICAgaWYgKHRoaXMub25UcmFuc2Zvcm0gIT0gbnVsbCkge1xuICAgICAgICAgICAgbGV0IGV2ZW50ID0gbmV3IFNjYXR0ZXJFdmVudCh0aGlzLCB7XG4gICAgICAgICAgICAgICAgdHJhbnNsYXRlOiBkZWx0YSxcbiAgICAgICAgICAgICAgICBzY2FsZTogbmV3U2NhbGUsXG4gICAgICAgICAgICAgICAgcm90YXRlOiByb3RhdGUsXG4gICAgICAgICAgICAgICAgYWJvdXQ6IGFuY2hvclxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHRoaXMub25UcmFuc2Zvcm0uZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICAgICAgICAgIGYoZXZlbnQpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnJlc2l6YWJsZSkge1xuICAgICAgICAgICAgdGhpcy5yZXNpemVBZnRlclRyYW5zZm9ybSh2em9vbSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZvciBhIGdpdmVuIHpvb20sIGEgbmV3IHNjYWxlIGlzIGNhbGN1bGF0ZWQsIHRha2luZ1xuICAgICAqIG1pbiBhbmQgbWF4IHNjYWxlIGludG8gYWNjb3VudC5cbiAgICAgKiBcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gem9vbSAtIFRoZSB6b29tIGZhY3RvciwgdG8gc2NhbGUgdGhlIG9iamVjdCB3aXRoLlxuICAgICAqIEByZXR1cm5zIHtvYmplY3R9IC0gUmV0dXJucyBhbiBvYmplY3QgY29udGFpbmluZyB0aGUgYSB2YWx1ZSBmb3IgYSB2YWxpZCBzY2FsZSBhbmQgdGhlIGNvcnJlY3RlZCB6b29tIGZhY3Rvci5cbiAgICAgKi9cbiAgICBjYWxjdWxhdGVTY2FsZSh6b29tKSB7XG4gICAgICAgIGxldCBzY2FsZSA9IHRoaXMuc2NhbGUgKiB6b29tXG5cbiAgICAgICAgbGV0IG1pblNjYWxlID0gdGhpcy5taW5TY2FsZSAvIHRoaXMub3ZlcmRvU2NhbGluZ1xuICAgICAgICBsZXQgbWF4U2NhbGUgPSB0aGlzLm1heFNjYWxlICogdGhpcy5vdmVyZG9TY2FsaW5nXG4gICAgICAgIGlmIChzY2FsZSA8IG1pblNjYWxlKSB7XG4gICAgICAgICAgICBzY2FsZSA9IG1pblNjYWxlXG4gICAgICAgICAgICB6b29tID0gc2NhbGUgLyB0aGlzLnNjYWxlXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHNjYWxlID4gbWF4U2NhbGUpIHtcbiAgICAgICAgICAgIHNjYWxlID0gbWF4U2NhbGVcbiAgICAgICAgICAgIHpvb20gPSBzY2FsZSAvIHRoaXMuc2NhbGVcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLnNjYWxlQXV0b0Nsb3NlKVxuICAgICAgICAgICAgdGhpcy5fdXBkYXRlVHJhbnNwYXJlbmN5KClcblxuICAgICAgICByZXR1cm4geyB6b29tLCBzY2FsZSB9XG4gICAgfVxuXG4gICAgX3VwZGF0ZVRyYW5zcGFyZW5jeSgpIHtcbiAgICAgICAgaWYgKHRoaXMuc2NhbGUgPCB0aGlzLm1pblNjYWxlICsgdGhpcy5zY2FsZUNsb3NlVGhyZXNob2xkKSB7XG4gICAgICAgICAgICBsZXQgdHJhbnNwYXJlbmN5ID0gdGhpcy5jYWxjdWxhdGVTY2FsZVRyYW5zcGFyZW5jeSgpXG4gICAgICAgICAgICB0aGlzLmVsZW1lbnQuc3R5bGUub3BhY2l0eSA9IHRyYW5zcGFyZW5jeVxuICAgICAgICB9IGVsc2UgdGhpcy5lbGVtZW50LnN0eWxlLm9wYWNpdHkgPSAxXG4gICAgfVxuXG4gICAgY2FsY3VsYXRlU2NhbGVUcmFuc3BhcmVuY3koKSB7XG4gICAgICAgIGxldCB0cmFuc3BhcmVuY3kgPSAodGhpcy5zY2FsZSAtIHRoaXMubWluU2NhbGUpIC8gdGhpcy5zY2FsZUNsb3NlVGhyZXNob2xkXG4gICAgICAgIHRyYW5zcGFyZW5jeSA9ICh0cmFuc3BhcmVuY3kgPiAxKSA/IDEgOiAodHJhbnNwYXJlbmN5IDwgMCkgPyAwIDogdHJhbnNwYXJlbmN5XG4gICAgICAgIHJldHVybiB0cmFuc3BhcmVuY3lcbiAgICB9XG5cbiAgICByZXNpemVBZnRlclRyYW5zZm9ybSh6b29tKSB7XG4gICAgICAgIC8vIE92ZXJ3cml0ZSB0aGlzIGluIHN1YmNsYXNzZXMuXG4gICAgfVxuXG4gICAgdmFsaWRTY2FsZShzY2FsZSkge1xuICAgICAgICBzY2FsZSA9IE1hdGgubWF4KHNjYWxlLCB0aGlzLm1pblNjYWxlKVxuICAgICAgICBzY2FsZSA9IE1hdGgubWluKHNjYWxlLCB0aGlzLm1heFNjYWxlKVxuICAgICAgICByZXR1cm4gc2NhbGVcbiAgICB9XG5cbiAgICBhbmltYXRlWm9vbUJvdW5jZShkdCA9IDEpIHtcbiAgICAgICAgaWYgKHRoaXMuem9vbUFuY2hvciAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgem9vbSA9IDFcbiAgICAgICAgICAgIGxldCBhbW91bnQgPSBNYXRoLm1pbigwLjAxLCAwLjMgKiBkdCAvIDEwMDAwMC4wKVxuICAgICAgICAgICAgaWYgKHRoaXMuc2NhbGUgPCB0aGlzLm1pblNjYWxlKSB6b29tID0gMSArIGFtb3VudFxuICAgICAgICAgICAgaWYgKHRoaXMuc2NhbGUgPiB0aGlzLm1heFNjYWxlKSB6b29tID0gMSAtIGFtb3VudFxuICAgICAgICAgICAgaWYgKHpvb20gIT0gMSkge1xuICAgICAgICAgICAgICAgIHRoaXMudHJhbnNmb3JtKHsgeDogMCwgeTogMCB9LCB6b29tLCAwLCB0aGlzLnpvb21BbmNob3IpXG4gICAgICAgICAgICAgICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGR0ID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5hbmltYXRlWm9vbUJvdW5jZShkdClcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy56b29tQW5jaG9yID0gbnVsbFxuICAgICAgICB9XG4gICAgfVxuXG4gICAgY2hlY2tTY2FsaW5nKGFib3V0LCBkZWxheSA9IDApIHtcbiAgICAgICAgdGhpcy56b29tQW5jaG9yID0gYWJvdXRcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuYW5pbWF0ZVpvb21Cb3VuY2UuYmluZCh0aGlzKSlcbiAgICAgICAgc2V0VGltZW91dCh0aGlzLmFuaW1hdGVab29tQm91bmNlLmJpbmQodGhpcyksIGRlbGF5KVxuICAgIH1cblxuICAgIG9uTW91c2VXaGVlbChldmVudCkge1xuICAgICAgICBpZiAoZXZlbnQuY2xhaW1lZEJ5U2NhdHRlcikge1xuICAgICAgICAgICAgaWYgKGV2ZW50LmNsYWltZWRCeVNjYXR0ZXIgIT0gdGhpcykgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5raWxsQW5pbWF0aW9uKClcbiAgICAgICAgdGhpcy50YXJnZXRTY2FsZSA9IG51bGxcbiAgICAgICAgbGV0IGRpcmVjdGlvbiA9IGV2ZW50LmRldGFpbCA8IDAgfHwgZXZlbnQud2hlZWxEZWx0YSA+IDBcbiAgICAgICAgbGV0IGdsb2JhbFBvaW50ID0geyB4OiBldmVudC5jbGllbnRYLCB5OiBldmVudC5jbGllbnRZIH1cbiAgICAgICAgbGV0IGNlbnRlclBvaW50ID0gdGhpcy5tYXBQb3NpdGlvblRvQ29udGFpbmVyUG9pbnQoZ2xvYmFsUG9pbnQpXG4gICAgICAgIGlmIChldmVudC5zaGlmdEtleSkge1xuICAgICAgICAgICAgbGV0IGRlZ3JlZXMgPSBkaXJlY3Rpb24gPyA1IDogLTVcbiAgICAgICAgICAgIGxldCByYWQgPSBBbmdsZS5kZWdyZWUycmFkaWFuKGRlZ3JlZXMpXG4gICAgICAgICAgICByZXR1cm4gdGhpcy50cmFuc2Zvcm0oeyB4OiAwLCB5OiAwIH0sIDEuMCwgcmFkLCBjZW50ZXJQb2ludClcbiAgICAgICAgfVxuICAgICAgICBjb25zdCB6b29tRmFjdG9yID0gdGhpcy5tb3VzZVpvb21GYWN0b3JcbiAgICAgICAgbGV0IHpvb20gPSBkaXJlY3Rpb24gPyB6b29tRmFjdG9yIDogMSAvIHpvb21GYWN0b3JcbiAgICAgICAgdGhpcy50cmFuc2Zvcm0oeyB4OiAwLCB5OiAwIH0sIHpvb20sIDAsIGNlbnRlclBvaW50KVxuICAgICAgICB0aGlzLmNoZWNrU2NhbGluZyhjZW50ZXJQb2ludCwgMjAwKVxuXG4gICAgICAgIGlmICh0aGlzLnNjYWxlQXV0b0Nsb3NlKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5zY2FsZSA8PSB0aGlzLm1pblNjYWxlICsgdGhpcy5zY2FsZUNsb3NlVGhyZXNob2xkKSB7XG5cbiAgICAgICAgICAgICAgICBpZiAodGhpcy5zY2FsZUF1dG9DbG9zZVRpbWVvdXQpIGNsZWFyVGltZW91dCh0aGlzLnNjYWxlQXV0b0Nsb3NlVGltZW91dClcbiAgICAgICAgICAgICAgICB0aGlzLnNjYWxlQXV0b0Nsb3NlVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLl9jaGVja0F1dG9DbG9zZSgpXG4gICAgICAgICAgICAgICAgfSwgNjAwKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5fdXBkYXRlVHJhbnNwYXJlbmN5KClcbiAgICAgICAgfVxuICAgICAgICAvL1xuICAgICAgICAvLyAgICAgICAgIGlmICh0aGlzLm9uVHJhbnNmb3JtICE9IG51bGwpIHtcbiAgICAgICAgLy8gICAgICAgICAgICAgbGV0IGV2ZW50ID0gbmV3IFNjYXR0ZXJFdmVudCh0aGlzLCB7XG4gICAgICAgIC8vICAgICAgICAgICAgICAgICB0cmFuc2xhdGU6IHt4OiAwLCB5OiAwfSxcbiAgICAgICAgLy8gICAgICAgICAgICAgICAgIHNjYWxlOiB0aGlzLnNjYWxlLFxuICAgICAgICAvLyAgICAgICAgICAgICAgICAgcm90YXRlOiAwLFxuICAgICAgICAvLyAgICAgICAgICAgICAgICAgYWJvdXQ6IG51bGwsXG4gICAgICAgIC8vICAgICAgICAgICAgICAgICBmYXN0OiBmYWxzZSxcbiAgICAgICAgLy8gICAgICAgICAgICAgICAgIHR5cGU6IFpPT01cbiAgICAgICAgLy8gICAgICAgICAgICAgfSlcbiAgICAgICAgLy8gICAgICAgICAgICAgdGhpcy5vblRyYW5zZm9ybS5mb3JFYWNoKGZ1bmN0aW9uKGYpIHtcbiAgICAgICAgLy8gICAgICAgICAgICAgICAgIGYoZXZlbnQpXG4gICAgICAgIC8vICAgICAgICAgICAgIH0pXG4gICAgICAgIC8vICAgICAgICAgfVxuICAgIH1cblxuICAgIG9uU3RhcnQoZXZlbnQsIGludGVyYWN0aW9uKSB7XG5cbiAgICAgICAgaWYgKHRoaXMuc3RhcnRHZXN0dXJlKGludGVyYWN0aW9uKSkge1xuICAgICAgICAgICAgdGhpcy5kcmFnZ2luZyA9IHRydWVcbiAgICAgICAgICAgIHRoaXMuaW50ZXJhY3Rpb25BbmNob3IgPSBudWxsXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMub25UcmFuc2Zvcm0gIT0gbnVsbCkge1xuICAgICAgICAgICAgbGV0IGV2ZW50ID0gbmV3IFNjYXR0ZXJFdmVudCh0aGlzLCB7XG4gICAgICAgICAgICAgICAgdHJhbnNsYXRlOiB7IHg6IDAsIHk6IDAgfSxcbiAgICAgICAgICAgICAgICBzY2FsZTogdGhpcy5zY2FsZSxcbiAgICAgICAgICAgICAgICByb3RhdGU6IDAsXG4gICAgICAgICAgICAgICAgYWJvdXQ6IG51bGwsXG4gICAgICAgICAgICAgICAgZmFzdDogZmFsc2UsXG4gICAgICAgICAgICAgICAgdHlwZTogU1RBUlRcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB0aGlzLm9uVHJhbnNmb3JtLmZvckVhY2goZnVuY3Rpb24gKGYpIHtcbiAgICAgICAgICAgICAgICBmKGV2ZW50KVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uTW92ZShldmVudCwgaW50ZXJhY3Rpb24pIHtcbiAgICAgICAgLyoqIEFzIGxvbmcgYXMgbW91c2VvdXQgJiYgbW91c2VsZWF2ZSBpbnRlcnJ1cHQgd2UgY2Fubm90IGJlIHN1cmUgdGhhdFxuICAgICAgICAgKiBkcmFnZ2luZyByZW1haW5zIGNvcnJlY3QuXG4gICAgICAgICAqL1xuICAgICAgICBpZiAodGhpcy5kcmFnZ2luZykge1xuICAgICAgICAgICAgdGhpcy5nZXN0dXJlKGludGVyYWN0aW9uKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25FbmQoZXZlbnQsIGludGVyYWN0aW9uKSB7XG4gICAgICAgIC8vY29uc29sZS5sb2coXCJTY2F0dGVyLm9uRW5kXCIsIHRoaXMuZHJhZ2dpbmcpXG4gICAgICAgIGlmIChpbnRlcmFjdGlvbi5pc0ZpbmlzaGVkKCkpIHtcbiAgICAgICAgICAgIHRoaXMuZW5kR2VzdHVyZShpbnRlcmFjdGlvbilcbiAgICAgICAgICAgIHRoaXMuZHJhZ2dpbmcgPSBmYWxzZVxuICAgICAgICAgICAgZm9yIChsZXQga2V5IG9mIGludGVyYWN0aW9uLmVuZGVkLmtleXMoKSkge1xuICAgICAgICAgICAgICAgIGlmIChpbnRlcmFjdGlvbi5pc1RhcChrZXkpKSB7XG4gICAgICAgICAgICAgICAgICAgIGxldCBwb2ludCA9IGludGVyYWN0aW9uLmVuZGVkLmdldChrZXkpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMub25UYXAoZXZlbnQsIGludGVyYWN0aW9uLCBwb2ludClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAodGhpcy5vblRyYW5zZm9ybSAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgbGV0IGV2ZW50ID0gbmV3IFNjYXR0ZXJFdmVudCh0aGlzLCB7XG4gICAgICAgICAgICAgICAgICAgIHRyYW5zbGF0ZTogeyB4OiAwLCB5OiAwIH0sXG4gICAgICAgICAgICAgICAgICAgIHNjYWxlOiB0aGlzLnNjYWxlLFxuICAgICAgICAgICAgICAgICAgICByb3RhdGU6IDAsXG4gICAgICAgICAgICAgICAgICAgIGFib3V0OiBudWxsLFxuICAgICAgICAgICAgICAgICAgICBmYXN0OiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogRU5EXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICB0aGlzLm9uVHJhbnNmb3JtLmZvckVhY2goZnVuY3Rpb24gKGYpIHtcbiAgICAgICAgICAgICAgICAgICAgZihldmVudClcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGxldCBhYm91dCA9IHRoaXMuaW50ZXJhY3Rpb25BbmNob3JcbiAgICAgICAgaWYgKGFib3V0ICE9IG51bGwpIHtcbiAgICAgICAgICAgIHRoaXMuY2hlY2tTY2FsaW5nKGFib3V0LCAxMDApXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvblRhcChldmVudCwgaW50ZXJhY3Rpb24sIHBvaW50KSB7IH1cblxuICAgIG9uRHJhZ1VwZGF0ZShkZWx0YSkge1xuICAgICAgICBpZiAodGhpcy5vblRyYW5zZm9ybSAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgICAgICAgICBmYXN0OiB0cnVlLFxuICAgICAgICAgICAgICAgIHRyYW5zbGF0ZTogZGVsdGEsXG4gICAgICAgICAgICAgICAgc2NhbGU6IHRoaXMuc2NhbGUsXG4gICAgICAgICAgICAgICAgYWJvdXQ6IHRoaXMuY3VycmVudEFib3V0LFxuICAgICAgICAgICAgICAgIHR5cGU6IG51bGxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB0aGlzLm9uVHJhbnNmb3JtLmZvckVhY2goZnVuY3Rpb24gKGYpIHtcbiAgICAgICAgICAgICAgICBmKGV2ZW50KVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uRHJhZ0NvbXBsZXRlKCkge1xuICAgICAgICBpZiAodGhpcy5vblRyYW5zZm9ybSkge1xuICAgICAgICAgICAgbGV0IGV2ZW50ID0gbmV3IFNjYXR0ZXJFdmVudCh0aGlzLCB7XG4gICAgICAgICAgICAgICAgc2NhbGU6IHRoaXMuc2NhbGUsXG4gICAgICAgICAgICAgICAgYWJvdXQ6IHRoaXMuY3VycmVudEFib3V0LFxuICAgICAgICAgICAgICAgIGZhc3Q6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHR5cGU6IG51bGxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB0aGlzLm9uVHJhbnNmb3JtLmZvckVhY2goZnVuY3Rpb24gKGYpIHtcbiAgICAgICAgICAgICAgICBmKGV2ZW50KVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uTW92ZWQoZHgsIGR5LCBhYm91dCkge1xuICAgICAgICBpZiAodGhpcy5vblRyYW5zZm9ybSAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGU6IHsgeDogZHgsIHk6IGR5IH0sXG4gICAgICAgICAgICAgICAgYWJvdXQ6IGFib3V0LFxuICAgICAgICAgICAgICAgIGZhc3Q6IHRydWUsXG4gICAgICAgICAgICAgICAgdHlwZTogbnVsbFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHRoaXMub25UcmFuc2Zvcm0uZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICAgICAgICAgIGYoZXZlbnQpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25SZXNpemluZygpIHtcbiAgICAgICAgaWYgKHRoaXMub25UcmFuc2Zvcm0gIT0gbnVsbCkge1xuICAgICAgICAgICAgbGV0IGV2ZW50ID0gbmV3IFNjYXR0ZXJFdmVudCh0aGlzLCB7XG4gICAgICAgICAgICAgICAgc2NhbGU6IHRoaXMuc2NhbGUsXG4gICAgICAgICAgICAgICAgZmFzdDogZmFsc2UsXG4gICAgICAgICAgICAgICAgdHlwZTogbnVsbFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLm9uVHJhbnNmb3JtLmZvckVhY2goZnVuY3Rpb24gKGYpIHtcbiAgICAgICAgICAgICAgICBmKGV2ZW50KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25ab29tZWQoYWJvdXQpIHtcblxuICAgICAgICBpZiAodGhpcy5zY2FsZUF1dG9DbG9zZSlcbiAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVRyYW5zcGFyZW5jeSgpXG5cbiAgICAgICAgaWYgKHRoaXMub25UcmFuc2Zvcm0gIT0gbnVsbCkge1xuICAgICAgICAgICAgbGV0IGV2ZW50ID0gbmV3IFNjYXR0ZXJFdmVudCh0aGlzLCB7XG4gICAgICAgICAgICAgICAgc2NhbGU6IHRoaXMuc2NhbGUsXG4gICAgICAgICAgICAgICAgYWJvdXQ6IGFib3V0LFxuICAgICAgICAgICAgICAgIGZhc3Q6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHR5cGU6IG51bGxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB0aGlzLm9uVHJhbnNmb3JtLmZvckVhY2goZnVuY3Rpb24gKGYpIHtcbiAgICAgICAgICAgICAgICBmKGV2ZW50KVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqIEEgY29udGFpbmVyIGZvciBzY2F0dGVyIG9iamVjdHMsIHdoaWNoIHVzZXMgYSBzaW5nbGUgSW50ZXJhY3Rpb25NYXBwZXJcbiAqIGZvciBhbGwgY2hpbGRyZW4uIFRoaXMgcmVkdWNlcyB0aGUgbnVtYmVyIG9mIHJlZ2lzdGVyZWQgZXZlbnQgaGFuZGxlcnNcbiAqIGFuZCBjb3ZlcnMgdGhlIGNvbW1vbiB1c2UgY2FzZSB0aGF0IG11bHRpcGxlIG9iamVjdHMgYXJlIHNjYXR0ZXJlZFxuICogb24gdGhlIHNhbWUgbGV2ZWwuXG4gKi9cbmV4cG9ydCBjbGFzcyBET01TY2F0dGVyQ29udGFpbmVyIHtcbiAgICAvKipcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge0RPTSBub2RlfSBlbGVtZW50IC0gRE9NIGVsZW1lbnQgdGhhdCByZWNlaXZlcyBldmVudHNcbiAgICAgKiBAcGFyYW0ge0Jvb2x9IHN0b3BFdmVudHMgLSAgV2hldGhlciBldmVudHMgc2hvdWxkIGJlIHN0b3BwZWQgb3IgcHJvcGFnYXRlZFxuICAgICAqIEBwYXJhbSB7Qm9vbH0gY2xhaW1FdmVudHMgLSBXaGV0aGVyIGV2ZW50cyBzaG91bGQgYmUgbWFya2VkIGFzIGNsYWltZWRcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgZmluZFRhcmdldCByZXR1cm4gYXMgbm9uLW51bGwgdmFsdWUuXG4gICAgICogQHBhcmFtIHtTdHJpbmd9IFt0b3VjaEFjdGlvbj1ub25lXSAtIENTUyB0byBzZXQgdG91Y2ggYWN0aW9uIHN0eWxlLCBuZWVkZWQgdG8gcHJldmVudFxuICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRlciBjYW5jZWwgZXZlbnRzLiBVc2UgbnVsbCBpZiB0aGVcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZSB0b3VjaCBhY3Rpb24gc2hvdWxkIG5vdCBiZSBzZXQuXG4gICAgICovXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIGVsZW1lbnQsXG4gICAgICAgIHsgc3RvcEV2ZW50cyA9ICdhdXRvJywgY2xhaW1FdmVudHMgPSB0cnVlLCB1c2VDYXB0dXJlID0gdHJ1ZSwgdG91Y2hBY3Rpb24gPSAnbm9uZScgfSA9IHt9XG4gICAgKSB7XG4gICAgICAgIHRoaXMub25DYXB0dXJlID0gbnVsbFxuICAgICAgICB0aGlzLmVsZW1lbnQgPSBlbGVtZW50XG4gICAgICAgIGlmIChzdG9wRXZlbnRzID09PSAnYXV0bycpIHtcbiAgICAgICAgICAgIGlmIChDYXBhYmlsaXRpZXMuaXNTYWZhcmkpIHtcbiAgICAgICAgICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgICAgICAgICAndG91Y2htb3ZlJyxcbiAgICAgICAgICAgICAgICAgICAgZXZlbnQgPT4gdGhpcy5wcmV2ZW50UGluY2goZXZlbnQpLFxuICAgICAgICAgICAgICAgICAgICBmYWxzZVxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICBzdG9wRXZlbnRzID0gZmFsc2VcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc3RvcEV2ZW50cyA9IHRydWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLnN0b3BFdmVudHMgPSBzdG9wRXZlbnRzXG4gICAgICAgIHRoaXMuY2xhaW1FdmVudHMgPSBjbGFpbUV2ZW50c1xuICAgICAgICBpZiAodG91Y2hBY3Rpb24gIT09IG51bGwpIHtcbiAgICAgICAgICAgIEVsZW1lbnRzLnNldFN0eWxlKGVsZW1lbnQsIHsgdG91Y2hBY3Rpb24gfSlcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnNjYXR0ZXIgPSBuZXcgTWFwKClcbiAgICAgICAgdGhpcy5kZWxlZ2F0ZSA9IG5ldyBJbnRlcmFjdGlvbk1hcHBlcihlbGVtZW50LCB0aGlzLCB7XG4gICAgICAgICAgICB1c2VDYXB0dXJlLFxuICAgICAgICAgICAgbW91c2VXaGVlbEVsZW1lbnQ6IHdpbmRvd1xuICAgICAgICB9KVxuXG4gICAgICAgIGlmICh0eXBlb2YgZGVidWdDYW52YXMgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoZHQgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc2hvd1RvdWNoZXMoZHQpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc2hvd1RvdWNoZXMoZHQpIHtcbiAgICAgICAgbGV0IHJlc29sdXRpb24gPSB3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpb1xuICAgICAgICBsZXQgY2FudmFzID0gZGVidWdDYW52YXNcbiAgICAgICAgbGV0IGN1cnJlbnQgPSB0aGlzLmRlbGVnYXRlLmludGVyYWN0aW9uLmN1cnJlbnRcbiAgICAgICAgbGV0IGNvbnRleHQgPSBjYW52YXMuZ2V0Q29udGV4dCgnMmQnKVxuICAgICAgICBsZXQgcmFkaXVzID0gMjAgKiByZXNvbHV0aW9uXG4gICAgICAgIGNvbnRleHQuY2xlYXJSZWN0KDAsIDAsIGNhbnZhcy53aWR0aCwgY2FudmFzLmhlaWdodClcbiAgICAgICAgY29udGV4dC5maWxsU3R5bGUgPSAncmdiYSgwLCAwLCAwLCAwLjMpJ1xuICAgICAgICBjb250ZXh0LmxpbmVXaWR0aCA9IDJcbiAgICAgICAgY29udGV4dC5zdHJva2VTdHlsZSA9ICcjMDAzMzAwJ1xuICAgICAgICBmb3IgKGxldCBba2V5LCBwb2ludF0gb2YgY3VycmVudC5lbnRyaWVzKCkpIHtcbiAgICAgICAgICAgIGxldCBsb2NhbCA9IHBvaW50XG4gICAgICAgICAgICBjb250ZXh0LmJlZ2luUGF0aCgpXG4gICAgICAgICAgICBjb250ZXh0LmFyYyhcbiAgICAgICAgICAgICAgICBsb2NhbC54ICogcmVzb2x1dGlvbixcbiAgICAgICAgICAgICAgICBsb2NhbC55ICogcmVzb2x1dGlvbixcbiAgICAgICAgICAgICAgICByYWRpdXMsXG4gICAgICAgICAgICAgICAgMCxcbiAgICAgICAgICAgICAgICAyICogTWF0aC5QSSxcbiAgICAgICAgICAgICAgICBmYWxzZVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgY29udGV4dC5maWxsKClcbiAgICAgICAgICAgIGNvbnRleHQuc3Ryb2tlKClcbiAgICAgICAgfVxuICAgICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoZHQgPT4ge1xuICAgICAgICAgICAgdGhpcy5zaG93VG91Y2hlcyhkdClcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICBwcmV2ZW50UGluY2goZXZlbnQpIHtcbiAgICAgICAgZXZlbnQgPSBldmVudC5vcmlnaW5hbEV2ZW50IHx8IGV2ZW50XG4gICAgICAgIGlmIChldmVudC5zY2FsZSAhPT0gMSkge1xuICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgYWRkKHNjYXR0ZXIpIHtcbiAgICAgICAgdGhpcy5zY2F0dGVyLnNldChzY2F0dGVyLmVsZW1lbnQsIHNjYXR0ZXIpXG4gICAgfVxuXG4gICAgY2FwdHVyZShldmVudCkge1xuICAgICAgICBpZiAodGhpcy5vbkNhcHR1cmUpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLm9uQ2FwdHVyZShldmVudClcbiAgICAgICAgfVxuICAgICAgICBpZiAoZXZlbnQudGFyZ2V0ID09IHRoaXMuZWxlbWVudCAmJiB0aGlzLnN0b3BFdmVudHMpIHtcbiAgICAgICAgICAgIEV2ZW50cy5zdG9wKGV2ZW50KVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgfVxuXG4gICAgbWFwUG9zaXRpb25Ub1BvaW50KHBvaW50KSB7XG4gICAgICAgIHJldHVybiBQb2ludHMuZnJvbVBhZ2VUb05vZGUodGhpcy5lbGVtZW50LCBwb2ludClcbiAgICB9XG5cbiAgICBpc0Rlc2NlbmRhbnQocGFyZW50LCBjaGlsZCwgY2xpY2thYmxlID0gZmFsc2UpIHtcbiAgICAgICAgaWYgKHBhcmVudCA9PSBjaGlsZCkgcmV0dXJuIHRydWVcbiAgICAgICAgbGV0IG5vZGUgPSBjaGlsZC5wYXJlbnROb2RlXG4gICAgICAgIHdoaWxlIChub2RlICE9IG51bGwpIHtcbiAgICAgICAgICAgIGlmICghY2xpY2thYmxlICYmIG5vZGUub25jbGljaykge1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKG5vZGUgPT0gcGFyZW50KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG5vZGUgPSBub2RlLnBhcmVudE5vZGVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICBmaW5kVGFyZ2V0KGV2ZW50LCBsb2NhbCwgZ2xvYmFsKSB7XG4gICAgICAgIC8qKiogTm90ZSB0aGF0IGVsZW1lbnRGcm9tUG9pbnQgd29ya3Mgd2l0aCBjbGllbnRYLCBjbGllbnRZLCBub3QgcGFnZVgsIHBhZ2VZXG4gICAgICAgIFRoZSBpbXBvcnRhbnQgcG9pbnQgaXMgdGhhdCBldmVudCBzaG91bGQgbm90IGJlIHVzZWQsIHNpbmNlIHRoZSBUb3VjaEV2ZW50XG4gICAgICAgIHBvaW50cyBhcmUgaGlkZGVuIGluIHN1YiBvYmplY3RzLlxuICAgICAgICAqKiovXG4gICAgICAgIGxldCBmb3VuZCA9IGRvY3VtZW50LmVsZW1lbnRGcm9tUG9pbnQoZ2xvYmFsLngsIGdsb2JhbC55KVxuICAgICAgICBmb3IgKGxldCB0YXJnZXQgb2YgdGhpcy5zY2F0dGVyLnZhbHVlcygpKSB7XG4gICAgICAgICAgICBpZiAodGFyZ2V0LmludGVyYWN0aXZlICYmIHRoaXMuaXNEZXNjZW5kYW50KHRhcmdldC5lbGVtZW50LCBmb3VuZCkpIHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5zdG9wRXZlbnRzKSBFdmVudHMuc3RvcChldmVudClcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5jbGFpbUV2ZW50cykgZXZlbnQuY2xhaW1lZEJ5U2NhdHRlciA9IHRhcmdldFxuICAgICAgICAgICAgICAgIHJldHVybiB0YXJnZXRcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIGdldCBjZW50ZXIoKSB7XG4gICAgICAgIGxldCByID0gdGhpcy5ib3VuZHNcbiAgICAgICAgbGV0IHcyID0gci53aWR0aCAvIDJcbiAgICAgICAgbGV0IGgyID0gci5oZWlnaHQgLyAyXG4gICAgICAgIHJldHVybiB7IHg6IHcyLCB5OiBoMiB9XG4gICAgfVxuXG4gICAgZ2V0IGJvdW5kcygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICAgIH1cblxuICAgIGdldCBwb2x5Z29uKCkge1xuICAgICAgICBsZXQgciA9IHRoaXMuYm91bmRzXG4gICAgICAgIGxldCB3MiA9IHIud2lkdGggLyAyXG4gICAgICAgIGxldCBoMiA9IHIuaGVpZ2h0IC8gMlxuICAgICAgICBsZXQgY2VudGVyID0geyB4OiB3MiwgeTogaDIgfVxuICAgICAgICBsZXQgcG9seWdvbiA9IG5ldyBQb2x5Z29uKGNlbnRlcilcbiAgICAgICAgcG9seWdvbi5hZGRQb2ludCh7IHg6IC13MiwgeTogLWgyIH0pXG4gICAgICAgIHBvbHlnb24uYWRkUG9pbnQoeyB4OiB3MiwgeTogLWgyIH0pXG4gICAgICAgIHBvbHlnb24uYWRkUG9pbnQoeyB4OiB3MiwgeTogaDIgfSlcbiAgICAgICAgcG9seWdvbi5hZGRQb2ludCh7IHg6IC13MiwgeTogaDIgfSlcbiAgICAgICAgcmV0dXJuIHBvbHlnb25cbiAgICB9XG59XG5cblxuZXhwb3J0IGNsYXNzIERPTVNjYXR0ZXIgZXh0ZW5kcyBBYnN0cmFjdFNjYXR0ZXIge1xuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBlbGVtZW50LFxuICAgICAgICBjb250YWluZXIsXG4gICAgICAgIHtcbiAgICAgICAgICAgIHN0YXJ0U2NhbGUgPSAxLjAsXG4gICAgICAgICAgICBtaW5TY2FsZSA9IDAuMSxcbiAgICAgICAgICAgIG1heFNjYWxlID0gMS4wLFxuICAgICAgICAgICAgb3ZlcmRvU2NhbGluZyA9IDEuNSxcbiAgICAgICAgICAgIGF1dG9CcmluZ1RvRnJvbnQgPSB0cnVlLFxuICAgICAgICAgICAgdHJhbnNsYXRhYmxlID0gdHJ1ZSxcbiAgICAgICAgICAgIHNjYWxhYmxlID0gdHJ1ZSxcbiAgICAgICAgICAgIHJvdGF0YWJsZSA9IHRydWUsXG4gICAgICAgICAgICBtb3ZhYmxlWCA9IHRydWUsXG4gICAgICAgICAgICBtb3ZhYmxlWSA9IHRydWUsXG4gICAgICAgICAgICByb3RhdGlvbkRlZ3JlZXMgPSBudWxsLFxuICAgICAgICAgICAgcm90YXRpb24gPSBudWxsLFxuICAgICAgICAgICAgb25UcmFuc2Zvcm0gPSBudWxsLFxuICAgICAgICAgICAgdHJhbnNmb3JtT3JpZ2luID0gJ2NlbnRlciBjZW50ZXInLFxuICAgICAgICAgICAgLy8gZXh0cmFzIHdoaWNoIGFyZSBpbiBwYXJ0IG5lZWRlZFxuICAgICAgICAgICAgeCA9IDAsXG4gICAgICAgICAgICB5ID0gMCxcbiAgICAgICAgICAgIHdpZHRoID0gbnVsbCwgLy8gcmVxdWlyZWRcbiAgICAgICAgICAgIGhlaWdodCA9IG51bGwsICAvLyByZXF1aXJlZFxuICAgICAgICAgICAgcmVzaXphYmxlID0gZmFsc2UsXG4gICAgICAgICAgICBzaW11bGF0ZUNsaWNrID0gZmFsc2UsXG4gICAgICAgICAgICB2ZXJib3NlID0gdHJ1ZSxcbiAgICAgICAgICAgIG9uUmVzaXplID0gbnVsbCxcbiAgICAgICAgICAgIHRvdWNoQWN0aW9uID0gJ25vbmUnLFxuICAgICAgICAgICAgdGhyb3dWaXNpYmlsaXR5ID0gNDQsXG4gICAgICAgICAgICB0aHJvd0RhbXBpbmcgPSAwLjk1LFxuICAgICAgICAgICAgYXV0b1Rocm93ID0gdHJ1ZSxcbiAgICAgICAgICAgIHNjYWxlQXV0b0Nsb3NlID0gZmFsc2UsXG4gICAgICAgICAgICBvbkNsb3NlID0gbnVsbCxcbiAgICAgICAgICAgIHNjYWxlQ2xvc2VUaHJlc2hvbGQgPSAwLjEwLFxuICAgICAgICAgICAgc2NhbGVDbG9zZUJ1ZmZlciA9IDAuMDVcbiAgICAgICAgfSA9IHt9XG4gICAgKSB7XG4gICAgICAgIHN1cGVyKHtcbiAgICAgICAgICAgIG1pblNjYWxlLFxuICAgICAgICAgICAgbWF4U2NhbGUsXG4gICAgICAgICAgICBzdGFydFNjYWxlLFxuICAgICAgICAgICAgb3ZlcmRvU2NhbGluZyxcbiAgICAgICAgICAgIGF1dG9CcmluZ1RvRnJvbnQsXG4gICAgICAgICAgICB0cmFuc2xhdGFibGUsXG4gICAgICAgICAgICBzY2FsYWJsZSxcbiAgICAgICAgICAgIHJvdGF0YWJsZSxcbiAgICAgICAgICAgIG1vdmFibGVYLFxuICAgICAgICAgICAgbW92YWJsZVksXG4gICAgICAgICAgICByZXNpemFibGUsXG4gICAgICAgICAgICByb3RhdGlvbkRlZ3JlZXMsXG4gICAgICAgICAgICByb3RhdGlvbixcbiAgICAgICAgICAgIG9uVHJhbnNmb3JtLFxuICAgICAgICAgICAgdGhyb3dWaXNpYmlsaXR5LFxuICAgICAgICAgICAgdGhyb3dEYW1waW5nLFxuICAgICAgICAgICAgYXV0b1Rocm93LFxuICAgICAgICAgICAgc2NhbGVBdXRvQ2xvc2UsXG4gICAgICAgICAgICBzY2FsZUNsb3NlVGhyZXNob2xkLFxuICAgICAgICAgICAgc2NhbGVDbG9zZUJ1ZmZlcixcbiAgICAgICAgICAgIG9uQ2xvc2VcbiAgICAgICAgfSlcbiAgICAgICAgaWYgKGNvbnRhaW5lciA9PSBudWxsIHx8IHdpZHRoID09IG51bGwgfHwgaGVpZ2h0ID09IG51bGwpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCB2YWx1ZTogbnVsbCcpXG4gICAgICAgIH1cbiAgICAgICAgZWxlbWVudC5zY2F0dGVyID0gdGhpc1xuICAgICAgICB0aGlzLmVsZW1lbnQgPSBlbGVtZW50XG4gICAgICAgIHRoaXMueCA9IHhcbiAgICAgICAgdGhpcy55ID0geVxuICAgICAgICB0aGlzLm9sZFggPSAwXG4gICAgICAgIHRoaXMub2xkWSA9IDBcbiAgICAgICAgdGhpcy5tZWFuWCA9IHhcbiAgICAgICAgdGhpcy5tZWFuWSA9IHlcbiAgICAgICAgdGhpcy53aWR0aCA9IHdpZHRoXG4gICAgICAgIHRoaXMuaGVpZ2h0ID0gaGVpZ2h0XG4gICAgICAgIHRoaXMudGhyb3dWaXNpYmlsaXR5ID0gTWF0aC5taW4od2lkdGgsIGhlaWdodCwgdGhyb3dWaXNpYmlsaXR5KVxuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IGNvbnRhaW5lclxuICAgICAgICB0aGlzLnNpbXVsYXRlQ2xpY2sgPSBzaW11bGF0ZUNsaWNrXG4gICAgICAgIHRoaXMuc2NhbGUgPSBzdGFydFNjYWxlXG4gICAgICAgIHRoaXMucm90YXRpb25EZWdyZWVzID0gdGhpcy5zdGFydFJvdGF0aW9uRGVncmVlc1xuICAgICAgICB0aGlzLnRyYW5zZm9ybU9yaWdpbiA9IHRyYW5zZm9ybU9yaWdpblxuICAgICAgICB0aGlzLmluaXRpYWxWYWx1ZXMgPSB7XG4gICAgICAgICAgICB4OiB4LFxuICAgICAgICAgICAgeTogeSxcbiAgICAgICAgICAgIHdpZHRoOiB3aWR0aCxcbiAgICAgICAgICAgIGhlaWdodDogaGVpZ2h0LFxuICAgICAgICAgICAgc2NhbGU6IHN0YXJ0U2NhbGUsXG4gICAgICAgICAgICByb3RhdGlvbjogdGhpcy5zdGFydFJvdGF0aW9uRGVncmVlcyxcbiAgICAgICAgICAgIHRyYW5zZm9ybU9yaWdpbjogdHJhbnNmb3JtT3JpZ2luXG4gICAgICAgIH1cblxuXG4gICAgICAgIC8vIEZvciB0d2VlbmxpdGUgd2UgbmVlZCBpbml0aWFsIHZhbHVlcyBpbiBfZ3NUcmFuc2Zvcm1cbiAgICAgICAgVHdlZW5MaXRlLnNldChlbGVtZW50LCB0aGlzLmluaXRpYWxWYWx1ZXMpXG4gICAgICAgIHRoaXMub25SZXNpemUgPSBvblJlc2l6ZVxuICAgICAgICB0aGlzLnZlcmJvc2UgPSB2ZXJib3NlXG4gICAgICAgIGlmICh0b3VjaEFjdGlvbiAhPT0gbnVsbCkge1xuICAgICAgICAgICAgRWxlbWVudHMuc2V0U3R5bGUoZWxlbWVudCwgeyB0b3VjaEFjdGlvbiB9KVxuICAgICAgICB9XG4gICAgICAgIHRoaXMucmVzaXplQnV0dG9uID0gbnVsbFxuICAgICAgICBpZiAocmVzaXphYmxlKSB7XG4gICAgICAgICAgICBsZXQgYnV0dG9uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKVxuICAgICAgICAgICAgYnV0dG9uLnN0eWxlLnBvc2l0aW9uID0gXCJhYnNvbHV0ZVwiXG4gICAgICAgICAgICBidXR0b24uc3R5bGUucmlnaHQgPSBcIjBweFwiXG4gICAgICAgICAgICBidXR0b24uc3R5bGUuYm90dG9tID0gXCIwcHhcIlxuICAgICAgICAgICAgYnV0dG9uLnN0eWxlLndpZHRoID0gXCI1MHB4XCI7XG4gICAgICAgICAgICBidXR0b24uc3R5bGUuaGVpZ2h0ID0gXCI1MHB4XCI7XG4gICAgICAgICAgICAvLyBidXR0b24uc3R5bGUuYm9yZGVyUmFkaXVzID0gXCIxMDAlIDBweCAwcHggMHB4XCI7XG4gICAgICAgICAgICAvLyBidXR0b24uc3R5bGUuYmFja2dyb3VuZCA9IHRoaXMuZWxlbWVudC5zdHlsZS5iYWNrZ3JvdW5kQ29sb3JcbiAgICAgICAgICAgIGJ1dHRvbi5jbGFzc05hbWUgPSBcImludGVyYWN0aXZlRWxlbWVudFwiXG4gICAgICAgICAgICB0aGlzLmVsZW1lbnQuYXBwZW5kQ2hpbGQoYnV0dG9uKVxuXG4gICAgICAgICAgICBidXR0b24uYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcmRvd24nLCAoZSkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc3RhcnRSZXNpemUoZSlcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgIGJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVybW92ZScsIChlKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5yZXNpemUoZSlcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgIGJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVydXAnLCAoZSkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc3RvcFJlc2l6ZShlKVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHRoaXMucmVzaXplQnV0dG9uID0gYnV0dG9uXG4gICAgICAgIH1cbiAgICAgICAgY29udGFpbmVyLmFkZCh0aGlzKVxuICAgIH1cblxuICAgIC8qKiBSZXR1cm5zIGdlb21ldHJ5IGRhdGEgYXMgb2JqZWN0LiAqKi9cbiAgICBnZXRTdGF0ZSgpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHNjYWxlOiB0aGlzLnNjYWxlLFxuICAgICAgICAgICAgeDogdGhpcy54LFxuICAgICAgICAgICAgeTogdGhpcy55LFxuICAgICAgICAgICAgcm90YXRpb246IHRoaXMucm90YXRpb25cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGNsb3NlKCkge1xuICAgICAgICBzdXBlci5jbG9zZSgpXG4gICAgICAgIGxldCBwYXJlbnQgPSB0aGlzLmVsZW1lbnQucGFyZW50Tm9kZVxuICAgICAgICBpZiAocGFyZW50KSBwYXJlbnQucmVtb3ZlQ2hpbGQodGhpcy5lbGVtZW50KVxuICAgIH1cblxuICAgIGdldCByb3RhdGlvbk9yaWdpbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY2VudGVyXG4gICAgfVxuXG4gICAgZ2V0IHgoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl94XG4gICAgfVxuXG4gICAgZ2V0IHkoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl95XG4gICAgfVxuXG4gICAgc2V0IHgodmFsdWUpIHtcbiAgICAgICAgdGhpcy5feCA9IHZhbHVlXG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7IHg6IHZhbHVlIH0pXG4gICAgfVxuXG4gICAgc2V0IHkodmFsdWUpIHtcbiAgICAgICAgdGhpcy5feSA9IHZhbHVlXG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7IHk6IHZhbHVlIH0pXG4gICAgfVxuXG4gICAgZ2V0IHBvc2l0aW9uKCkge1xuICAgICAgICBsZXQgdHJhbnNmb3JtID0gdGhpcy5lbGVtZW50Ll9nc1RyYW5zZm9ybVxuICAgICAgICBsZXQgeCA9IHRyYW5zZm9ybS54XG4gICAgICAgIGxldCB5ID0gdHJhbnNmb3JtLnlcbiAgICAgICAgcmV0dXJuIHsgeCwgeSB9XG4gICAgfVxuXG4gICAgZ2V0IG9yaWdpbigpIHtcbiAgICAgICAgbGV0IHAgPSB0aGlzLmZyb21Ob2RlVG9QYWdlKDAsIDApXG4gICAgICAgIHJldHVybiBQb2ludHMuZnJvbVBhZ2VUb05vZGUodGhpcy5jb250YWluZXIuZWxlbWVudCwgcClcbiAgICB9XG5cbiAgICBnZXQgYm91bmRzKCkge1xuICAgICAgICBsZXQgc3RhZ2UgPSB0aGlzLmNvbnRhaW5lci5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpXG4gICAgICAgIGxldCByZWN0ID0gdGhpcy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0b3A6IHJlY3QudG9wIC0gc3RhZ2UudG9wLFxuICAgICAgICAgICAgbGVmdDogcmVjdC5sZWZ0IC0gc3RhZ2UubGVmdCxcbiAgICAgICAgICAgIHdpZHRoOiByZWN0LndpZHRoLFxuICAgICAgICAgICAgaGVpZ2h0OiByZWN0LmhlaWdodFxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZ2V0IGNlbnRlcigpIHtcbiAgICAgICAgbGV0IHIgPSB0aGlzLmJvdW5kc1xuICAgICAgICBsZXQgdzIgPSByLndpZHRoIC8gMlxuICAgICAgICBsZXQgaDIgPSByLmhlaWdodCAvIDJcbiAgICAgICAgLy8gICBpZiAodGhpcy5yZXNpemFibGUpIHtcbiAgICAgICAgLy8gICAgICAgICAgICAgdzIgKj0gdGhpcy5zY2FsZVxuICAgICAgICAvLyAgICAgICAgICAgICBoMiAqPSB0aGlzLnNjYWxlXG4gICAgICAgIC8vICAgICAgICAgfVxuICAgICAgICB2YXIgeCA9IHIubGVmdCArIHcyXG4gICAgICAgIHZhciB5ID0gci50b3AgKyBoMlxuICAgICAgICByZXR1cm4geyB4LCB5IH1cbiAgICB9XG5cbiAgICBzZXQgcm90YXRpb24ocmFkaWFucykge1xuICAgICAgICBsZXQgcmFkID0gcmFkaWFucyAvLyBBbmdsZS5ub3JtYWxpemUocmFkaWFucylcbiAgICAgICAgbGV0IGRlZ3JlZXMgPSBBbmdsZS5yYWRpYW4yZGVncmVlKHJhZClcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmVsZW1lbnQsIHsgcm90YXRpb246IGRlZ3JlZXMgfSlcbiAgICAgICAgdGhpcy5fcm90YXRpb24gPSByYWRcbiAgICB9XG5cbiAgICBzZXQgcm90YXRpb25EZWdyZWVzKGRlZ3JlZXMpIHtcbiAgICAgICAgbGV0IGRlZyA9IGRlZ3JlZXMgLy8gQW5nbGUubm9ybWFsaXplRGVncmVlKGRlZ3JlZXMpXG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7IHJvdGF0aW9uOiBkZWcgfSlcbiAgICAgICAgdGhpcy5fcm90YXRpb24gPSBBbmdsZS5kZWdyZWUycmFkaWFuKGRlZylcbiAgICB9XG5cbiAgICBnZXQgcm90YXRpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9yb3RhdGlvblxuICAgIH1cblxuICAgIGdldCByb3RhdGlvbkRlZ3JlZXMoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9yb3RhdGlvblxuICAgIH1cblxuICAgIHNldCBzY2FsZShzY2FsZSkge1xuICAgICAgICBUd2VlbkxpdGUuc2V0KHRoaXMuZWxlbWVudCwge1xuICAgICAgICAgICAgc2NhbGU6IHNjYWxlLFxuICAgICAgICAgICAgdHJhbnNmb3JtT3JpZ2luOiB0aGlzLnRyYW5zZm9ybU9yaWdpblxuICAgICAgICB9KVxuICAgICAgICB0aGlzLl9zY2FsZSA9IHNjYWxlXG4gICAgfVxuXG4gICAgZ2V0IHNjYWxlKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fc2NhbGVcbiAgICB9XG5cbiAgICBnZXQgY29udGFpbmVyQm91bmRzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jb250YWluZXIuYm91bmRzXG4gICAgfVxuXG4gICAgZ2V0IGNvbnRhaW5lclBvbHlnb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnRhaW5lci5wb2x5Z29uXG4gICAgfVxuXG4gICAgbWFwUG9zaXRpb25Ub0NvbnRhaW5lclBvaW50KHBvaW50KSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnRhaW5lci5tYXBQb3NpdGlvblRvUG9pbnQocG9pbnQpXG4gICAgfVxuXG4gICAgY2FwdHVyZShldmVudCkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgIH1cblxuICAgIHJlc2V0KCkge1xuICAgICAgICBUd2VlbkxpdGUuc2V0KHRoaXMuZWxlbWVudCwgdGhpcy5pbml0aWFsVmFsdWVzKVxuICAgIH1cblxuICAgIGhpZGUoKSB7XG4gICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmVsZW1lbnQsIDAuMSwge1xuICAgICAgICAgICAgZGlzcGxheTogJ25vbmUnLFxuICAgICAgICAgICAgb25Db21wbGV0ZTogZSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5lbGVtZW50LnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQodGhpcy5lbGVtZW50KVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIHNob3coKSB7XG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7IGRpc3BsYXk6ICdibG9jaycgfSlcbiAgICB9XG5cbiAgICBzaG93QXQocCwgcm90YXRpb25EZWdyZWVzKSB7XG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7XG4gICAgICAgICAgICBkaXNwbGF5OiAnYmxvY2snLFxuICAgICAgICAgICAgeDogcC54LFxuICAgICAgICAgICAgeTogcC55LFxuICAgICAgICAgICAgcm90YXRpb246IHJvdGF0aW9uRGVncmVlcyxcbiAgICAgICAgICAgIHRyYW5zZm9ybU9yaWdpbjogdGhpcy50cmFuc2Zvcm1PcmlnaW5cbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICBicmluZ1RvRnJvbnQoKSB7XG4gICAgICAgIC8vIHRoaXMuZWxlbWVudC5wYXJlbnROb2RlLmFwcGVuZENoaWxkKHRoaXMuZWxlbWVudClcbiAgICAgICAgLy8gdW86IE9uIENob21lIGFuZCBFbGVjdG9uIGFwcGVuZENoaWxkIGxlYWRzIHRvIGZsaWNrZXJcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmVsZW1lbnQsIHsgekluZGV4OiBET01TY2F0dGVyLnpJbmRleCsrIH0pXG4gICAgfVxuXG4gICAgdG9nZ2xlVmlkZW8oZWxlbWVudCkge1xuICAgICAgICBpZiAoZWxlbWVudC5wYXVzZWQpIHtcbiAgICAgICAgICAgIGVsZW1lbnQucGxheSgpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBlbGVtZW50LnBhdXNlKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uVGFwKGV2ZW50LCBpbnRlcmFjdGlvbiwgcG9pbnQpIHtcbiAgICAgICAgaWYgKHRoaXMuc2ltdWxhdGVDbGljaykge1xuICAgICAgICAgICAgbGV0IHAgPSBQb2ludHMuZnJvbVBhZ2VUb05vZGUodGhpcy5lbGVtZW50LCBwb2ludClcbiAgICAgICAgICAgIGxldCBpZnJhbWUgPSB0aGlzLmVsZW1lbnQucXVlcnlTZWxlY3RvcignaWZyYW1lJylcbiAgICAgICAgICAgIGlmIChpZnJhbWUpIHtcbiAgICAgICAgICAgICAgICBsZXQgZG9jID0gaWZyYW1lLmNvbnRlbnRXaW5kb3cuZG9jdW1lbnRcbiAgICAgICAgICAgICAgICBsZXQgZWxlbWVudCA9IGRvYy5lbGVtZW50RnJvbVBvaW50KHAueCwgcC55KVxuICAgICAgICAgICAgICAgIGlmIChlbGVtZW50ID09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHN3aXRjaCAoZWxlbWVudC50YWdOYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIGNhc2UgJ1ZJREVPJzpcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGVsZW1lbnQuY3VycmVudFNyYylcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChQb3B1cE1lbnUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBQb3B1cE1lbnUub3BlbihcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRnVsbHNjcmVlbjogKCkgPT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aW5kb3cub3BlbihlbGVtZW50LmN1cnJlbnRTcmMpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGxheTogKCkgPT4gZWxlbWVudC5wbGF5KClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeyB4LCB5IH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMudG9nZ2xlVmlkZW8oZWxlbWVudClcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtZW50LmNsaWNrKClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpc0Rlc2NlbmRhbnQocGFyZW50LCBjaGlsZCkge1xuICAgICAgICBsZXQgbm9kZSA9IGNoaWxkLnBhcmVudE5vZGVcbiAgICAgICAgd2hpbGUgKG5vZGUgIT0gbnVsbCkge1xuICAgICAgICAgICAgaWYgKG5vZGUgPT0gcGFyZW50KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG5vZGUgPSBub2RlLnBhcmVudE5vZGVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICBmcm9tUGFnZVRvTm9kZSh4LCB5KSB7XG4gICAgICAgIHJldHVybiBQb2ludHMuZnJvbVBhZ2VUb05vZGUodGhpcy5lbGVtZW50LCB7IHgsIHkgfSlcbiAgICB9XG5cbiAgICBmcm9tTm9kZVRvUGFnZSh4LCB5KSB7XG4gICAgICAgIHJldHVybiBQb2ludHMuZnJvbU5vZGVUb1BhZ2UodGhpcy5lbGVtZW50LCB7IHgsIHkgfSlcbiAgICB9XG5cbiAgICBfbW92ZShkZWx0YSkge1xuICAgICAgICAvLyBVTzogV2UgbmVlZCB0byBrZWVwIFR3ZWVuTGl0ZSdzIF9nc1RyYW5zZm9ybSBhbmQgdGhlIHByaXZhdGVcbiAgICAgICAgLy8gX3ggYW5kIF95IGF0dHJpYnV0ZXMgYWxpZ25lZFxuICAgICAgICBsZXQgeCA9IHRoaXMuZWxlbWVudC5fZ3NUcmFuc2Zvcm0ueFxuICAgICAgICBsZXQgeSA9IHRoaXMuZWxlbWVudC5fZ3NUcmFuc2Zvcm0ueVxuICAgICAgICBpZiAodGhpcy5tb3ZhYmxlWCkge1xuICAgICAgICAgICAgeCArPSBkZWx0YS54XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMubW92YWJsZVkpIHtcbiAgICAgICAgICAgIHkgKz0gZGVsdGEueVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuX3ggPSB4XG4gICAgICAgIHRoaXMuX3kgPSB5XG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7IHg6IHgsIHk6IHkgfSlcbiAgICB9XG5cbiAgICByZXNpemVBZnRlclRyYW5zZm9ybSh6b29tKSB7XG4gICAgICAgIC8vICBsZXQgdyA9IHRoaXMud2lkdGggKiB0aGlzLnNjYWxlXG4gICAgICAgIC8vICAgICAgICAgbGV0IGggPSB0aGlzLmhlaWdodCAqIHRoaXMuc2NhbGVcbiAgICAgICAgLy8gICAgICAgICBUd2VlbkxpdGUuc2V0KHRoaXMuZWxlbWVudCwgeyB3aWR0aDogdywgaGVpZ2h0OiBoIH0pXG4gICAgICAgIGlmICh0aGlzLm9uUmVzaXplKSB7XG4gICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgUmVzaXplRXZlbnQodGhpcywgeyB3aWR0aDogdywgaGVpZ2h0OiBoIH0pXG4gICAgICAgICAgICB0aGlzLm9uUmVzaXplKGV2ZW50KVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnJlc2l6ZUJ1dHRvbiAhPSBudWxsKSB7XG4gICAgICAgICAgICAvLyB0aGlzLnJlc2l6ZUJ1dHRvbi5zdHlsZS53aWR0aCA9IDUwL3RoaXMuc2NhbGUrXCJweFwiXG4gICAgICAgICAgICAvLyB0aGlzLnJlc2l6ZUJ1dHRvbi5zdHlsZS5oZWlnaHQgPSA1MC90aGlzLnNjYWxlK1wicHhcIlxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhcnRSZXNpemUoZSkge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgbGV0IGV2ZW50ID0gbmV3IEN1c3RvbUV2ZW50KCdyZXNpemVTdGFydGVkJylcblxuICAgICAgICBsZXQgb2xkUG9zdGl0aW9uID0geyB4OiAkKHRoaXMuZWxlbWVudCkucG9zaXRpb24oKS5sZWZ0LCB5OiAkKHRoaXMuZWxlbWVudCkucG9zaXRpb24oKS50b3AgfVxuICAgICAgICB0aGlzLmJyaW5nVG9Gcm9udCgpXG5cbiAgICAgICAgdGhpcy5lbGVtZW50LnN0eWxlLnRyYW5zZm9ybU9yaWdpbiA9IFwiMCUgMCVcIlxuXG4gICAgICAgIGxldCBuZXdQb3N0aXRpb24gPSB7IHg6ICQodGhpcy5lbGVtZW50KS5wb3NpdGlvbigpLmxlZnQsIHk6ICQodGhpcy5lbGVtZW50KS5wb3NpdGlvbigpLnRvcCB9XG5cbiAgICAgICAgbGV0IG9mZnNldCA9IFBvaW50cy5zdWJ0cmFjdChvbGRQb3N0aXRpb24sIG5ld1Bvc3RpdGlvbilcblxuICAgICAgICB0aGlzLm9sZFggPSBlLmNsaWVudFhcbiAgICAgICAgdGhpcy5vbGRZID0gZS5jbGllbnRZXG5cbiAgICAgICAgZS50YXJnZXQuc2V0QXR0cmlidXRlKCdyZXNpemluZycsIFwidHJ1ZVwiKVxuICAgICAgICBlLnRhcmdldC5zZXRQb2ludGVyQ2FwdHVyZShlLnBvaW50ZXJJZClcblxuICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5lbGVtZW50LCAwLCB7IGNzczogeyBsZWZ0OiBcIis9XCIgKyBvZmZzZXQueCArIFwicHhcIiB9IH0pXG4gICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmVsZW1lbnQsIDAsIHsgY3NzOiB7IHRvcDogXCIrPVwiICsgb2Zmc2V0LnkgKyBcInB4XCIgfSB9KVxuXG4gICAgICAgIHRoaXMuZWxlbWVudC5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICB9XG5cbiAgICByZXNpemUoZSkge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcblxuICAgICAgICBsZXQgcm90YXRpb24gPSBBbmdsZS5yYWRpYW4yZGVncmVlKHRoaXMucm90YXRpb24pXG4gICAgICAgIHJvdGF0aW9uID0gKHJvdGF0aW9uICsgMzYwKSAlIDM2MFxuICAgICAgICBsZXQgZXZlbnQgPSBuZXcgQ3VzdG9tRXZlbnQoJ3Jlc2l6ZWQnKVxuICAgICAgICBpZiAoZS50YXJnZXQuZ2V0QXR0cmlidXRlKCdyZXNpemluZycpID09IFwidHJ1ZVwiKSB7XG5cbiAgICAgICAgICAgIGxldCBkZWx0YVggPSAoZS5jbGllbnRYIC0gdGhpcy5vbGRYKVxuICAgICAgICAgICAgbGV0IGRlbHRhWSA9IChlLmNsaWVudFkgLSB0aGlzLm9sZFkpXG5cbiAgICAgICAgICAgIGxldCByID0gTWF0aC5zcXJ0KE1hdGgucG93KGRlbHRhWCwgMikgKyBNYXRoLnBvdyhkZWx0YVksIDIpKVxuICAgICAgICAgICAgbGV0IHBoaSA9IEFuZ2xlLnJhZGlhbjJkZWdyZWUoTWF0aC5hdGFuMihkZWx0YVgsIGRlbHRhWSkpXG5cbiAgICAgICAgICAgIHBoaSA9ICgocGhpKSArIDYzMCkgJSAzNjBcbiAgICAgICAgICAgIGxldCByb3QgPSAoKHJvdGF0aW9uICsgOTApICsgNjMwKSAlIDM2MFxuXG4gICAgICAgICAgICBsZXQgZGlmZkFuZ2xlID0gKCgwICsgcm90KSArIDM2MCkgJSAzNjBcbiAgICAgICAgICAgIGxldCBwaGlDb3JyZWN0ZWQgPSAocGhpICsgZGlmZkFuZ2xlICsgMzYwKSAlIDM2MFxuXG4gICAgICAgICAgICBsZXQgcmVzaXplVyA9IHIgKiBNYXRoLmNvcyhBbmdsZS5kZWdyZWUycmFkaWFuKHBoaUNvcnJlY3RlZCkpXG4gICAgICAgICAgICBsZXQgcmVzaXplSCA9IC1yICogTWF0aC5zaW4oQW5nbGUuZGVncmVlMnJhZGlhbihwaGlDb3JyZWN0ZWQpKVxuXG4gICAgICAgICAgICBpZiAodGhpcy5lbGVtZW50Lm9mZnNldFdpZHRoICsgcmVzaXplVyAvIHRoaXMuc2NhbGUgPiB0aGlzLndpZHRoICogMC4zICYmIHRoaXMuZWxlbWVudC5vZmZzZXRIZWlnaHQgKyByZXNpemVIIC8gdGhpcy5zY2FsZSA+IHRoaXMuaGVpZ2h0ICogMC4zKSBUd2VlbkxpdGUudG8odGhpcy5lbGVtZW50LCAwLCB7IHdpZHRoOiB0aGlzLmVsZW1lbnQub2Zmc2V0V2lkdGggKyByZXNpemVXIC8gdGhpcy5zY2FsZSwgaGVpZ2h0OiB0aGlzLmVsZW1lbnQub2Zmc2V0SGVpZ2h0ICsgcmVzaXplSCAvIHRoaXMuc2NhbGUgfSk7XG5cbiAgICAgICAgICAgIHRoaXMub2xkWCA9IGUuY2xpZW50WFxuICAgICAgICAgICAgdGhpcy5vbGRZID0gZS5jbGllbnRZXG4gICAgICAgICAgICB0aGlzLm9uUmVzaXppbmcoKVxuXG4gICAgICAgICAgICB0aGlzLmVsZW1lbnQuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdG9wUmVzaXplKGUpIHtcbiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG5cbiAgICAgICAgbGV0IGV2ZW50ID0gbmV3IEN1c3RvbUV2ZW50KCdyZXNpemVFbmRlZCcpXG4gICAgICAgIGxldCBvbGRQb3N0aXRpb24gPSB7IHg6ICQodGhpcy5lbGVtZW50KS5wb3NpdGlvbigpLmxlZnQsIHk6ICQodGhpcy5lbGVtZW50KS5wb3NpdGlvbigpLnRvcCB9XG4gICAgICAgIHRoaXMuZWxlbWVudC5zdHlsZS50cmFuc2Zvcm1PcmlnaW4gPSBcIjUwJSA1MCVcIlxuICAgICAgICBsZXQgbmV3UG9zdGl0aW9uID0geyB4OiAkKHRoaXMuZWxlbWVudCkucG9zaXRpb24oKS5sZWZ0LCB5OiAkKHRoaXMuZWxlbWVudCkucG9zaXRpb24oKS50b3AgfVxuICAgICAgICBsZXQgb2Zmc2V0ID0gUG9pbnRzLnN1YnRyYWN0KG9sZFBvc3RpdGlvbiwgbmV3UG9zdGl0aW9uKVxuXG4gICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmVsZW1lbnQsIDAsIHsgY3NzOiB7IGxlZnQ6IFwiKz1cIiArIG9mZnNldC54ICsgXCJweFwiIH0gfSlcbiAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMuZWxlbWVudCwgMCwgeyBjc3M6IHsgdG9wOiBcIis9XCIgKyBvZmZzZXQueSArIFwicHhcIiB9IH0pXG5cbiAgICAgICAgZS50YXJnZXQuc2V0QXR0cmlidXRlKCdyZXNpemluZycsIFwiZmFsc2VcIilcblxuICAgICAgICB0aGlzLmVsZW1lbnQuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgfVxufVxuXG5ET01TY2F0dGVyLnpJbmRleCA9IDEwMDBcbiIsImltcG9ydCB7IGdldElkIH0gZnJvbSAnLi91dGlscy5qcydcbmltcG9ydCB7IERPTVNjYXR0ZXIgfSBmcm9tICcuL3NjYXR0ZXIuanMnXG5pbXBvcnQgeyBJbnRlcmFjdGlvbk1hcHBlciB9IGZyb20gJy4vaW50ZXJhY3Rpb24uanMnXG5cbmV4cG9ydCBjbGFzcyBDYXJkTG9hZGVyIHtcbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgc3JjLFxuICAgICAgICB7XG4gICAgICAgICAgICB4ID0gMCxcbiAgICAgICAgICAgIHkgPSAwLFxuICAgICAgICAgICAgd2lkdGggPSAxMDAwLFxuICAgICAgICAgICAgaGVpZ2h0ID0gODAwLFxuICAgICAgICAgICAgbWF4V2lkdGggPSBudWxsLFxuICAgICAgICAgICAgbWF4SGVpZ2h0ID0gbnVsbCxcbiAgICAgICAgICAgIHNjYWxlID0gMSxcbiAgICAgICAgICAgIG1pblNjYWxlID0gMC41LFxuICAgICAgICAgICAgbWF4U2NhbGUgPSAxLjUsXG4gICAgICAgICAgICByb3RhdGlvbiA9IDBcbiAgICAgICAgfSA9IHt9XG4gICAgKSB7XG4gICAgICAgIHRoaXMuc3JjID0gc3JjXG4gICAgICAgIHRoaXMueCA9IHhcbiAgICAgICAgdGhpcy55ID0geVxuICAgICAgICB0aGlzLnNjYWxlID0gc2NhbGVcbiAgICAgICAgdGhpcy5yb3RhdGlvbiA9IDBcbiAgICAgICAgdGhpcy5tYXhTY2FsZSA9IG1heFNjYWxlXG4gICAgICAgIHRoaXMubWluU2NhbGUgPSBtaW5TY2FsZVxuICAgICAgICB0aGlzLndhbnRlZFdpZHRoID0gd2lkdGhcbiAgICAgICAgdGhpcy53YW50ZWRIZWlnaHQgPSBoZWlnaHRcbiAgICAgICAgdGhpcy5tYXhXaWR0aCA9IG1heFdpZHRoICE9IG51bGwgPyBtYXhXaWR0aCA6IHdpbmRvdy5pbm5lcldpZHRoXG4gICAgICAgIHRoaXMubWF4SGVpZ2h0ID0gbWF4SGVpZ2h0ICE9IG51bGwgPyBtYXhIZWlnaHQgOiB3aW5kb3cuaW5uZXJIZWlnaHRcbiAgICAgICAgdGhpcy5hZGRlZE5vZGUgPSBudWxsXG4gICAgICAgIGNvbnNvbGUubG9nKHtcbiAgICAgICAgIFxuICAgICAgICAgICAgd2lkdGgsXG4gICAgICAgICAgICBoZWlnaHQsXG4gICAgICAgICAgICBtYXhXaWR0aCxcbiAgICAgICAgICAgIG1heEhlaWdodCxcbiAgICAgICAgICAgXG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgdW5sb2FkKCkge1xuICAgICAgICBpZiAodGhpcy5hZGRlZE5vZGUpIHtcbiAgICAgICAgICAgIHRoaXMuYWRkZWROb2RlLnJlbW92ZSgpXG4gICAgICAgICAgICB0aGlzLmFkZGVkTm9kZSA9IG51bGxcbiAgICAgICAgfVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIFBERkxvYWRlciBleHRlbmRzIENhcmRMb2FkZXIge1xuICAgIGNvbnN0cnVjdG9yKHNyYywgeyB3aWR0aCA9IDE2NDAsIGhlaWdodCA9IDgwMCwgc2NhbGUgPSAxIH0gPSB7fSkge1xuICAgICAgICBzdXBlcihzcmMsIHsgd2lkdGgsIGhlaWdodCwgc2NhbGUgfSlcbiAgICAgICAgaWYgKHR5cGVvZiBQREZKUyA9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgYWxlcnQoJ1BERi5qcyBuZWVkZWQnKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgbG9hZChkb21Ob2RlKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBQREZKUy5nZXREb2N1bWVudCh0aGlzLnNyYykudGhlbihwZGYgPT4ge1xuICAgICAgICAgICAgICAgIHBkZi5nZXRQYWdlKDEpLnRoZW4ocGFnZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGxldCBzY2FsZSA9IHRoaXMuc2NhbGUgKiBhcHAucmVuZGVyZXIucmVzb2x1dGlvblxuICAgICAgICAgICAgICAgICAgICBsZXQgaW52U2NhbGUgPSAxIC8gc2NhbGVcbiAgICAgICAgICAgICAgICAgICAgbGV0IHZpZXdwb3J0ID0gcGFnZS5nZXRWaWV3cG9ydChzY2FsZSlcblxuICAgICAgICAgICAgICAgICAgICAvLyBQcmVwYXJlIGNhbnZhcyB1c2luZyBQREYgcGFnZSBkaW1lbnNpb25zLlxuICAgICAgICAgICAgICAgICAgICBsZXQgY2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJylcbiAgICAgICAgICAgICAgICAgICAgbGV0IGNvbnRleHQgPSBjYW52YXMuZ2V0Q29udGV4dCgnMmQnKVxuICAgICAgICAgICAgICAgICAgICBjYW52YXMuaGVpZ2h0ID0gdmlld3BvcnQuaGVpZ2h0XG4gICAgICAgICAgICAgICAgICAgIGNhbnZhcy53aWR0aCA9IHZpZXdwb3J0LndpZHRoXG5cbiAgICAgICAgICAgICAgICAgICAgLy8gUmVuZGVyIFBERiBwYWdlIGludG8gY2FudmFzIGNvbnRleHQuXG4gICAgICAgICAgICAgICAgICAgIGxldCByZW5kZXJDb250ZXh0ID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FudmFzQ29udGV4dDogY29udGV4dCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHZpZXdwb3J0OiB2aWV3cG9ydFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHBhZ2UucmVuZGVyKHJlbmRlckNvbnRleHQpXG4gICAgICAgICAgICAgICAgICAgIGRvbU5vZGUuYXBwZW5kQ2hpbGQoY2FudmFzKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLndhbnRlZFdpZHRoID0gY2FudmFzLndpZHRoXG4gICAgICAgICAgICAgICAgICAgIHRoaXMud2FudGVkSGVpZ2h0ID0gY2FudmFzLmhlaWdodFxuICAgICAgICAgICAgICAgICAgICB0aGlzLnNjYWxlID0gaW52U2NhbGVcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5hZGRlZE5vZGUgPSBjYW52YXNcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh0aGlzKVxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9KVxuICAgICAgICB9KVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIEltYWdlTG9hZGVyIGV4dGVuZHMgQ2FyZExvYWRlciB7XG4gICAgbG9hZChkb21Ob2RlKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBsZXQgaXNJbWFnZSA9IGRvbU5vZGUgaW5zdGFuY2VvZiBIVE1MSW1hZ2VFbGVtZW50XG4gICAgICAgICAgICBsZXQgaW1hZ2UgPSBpc0ltYWdlID8gZG9tTm9kZSA6IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpXG4gICAgICAgICAgICBpbWFnZS5vbmxvYWQgPSBlID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoIWlzSW1hZ2UpIHtcbiAgICAgICAgICAgICAgICAgICAgZG9tTm9kZS5hcHBlbmRDaGlsZChpbWFnZSlcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5hZGRlZE5vZGUgPSBpbWFnZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aGlzLndhbnRlZFdpZHRoID0gaW1hZ2UubmF0dXJhbFdpZHRoXG4gICAgICAgICAgICAgICAgdGhpcy53YW50ZWRIZWlnaHQgPSBpbWFnZS5uYXR1cmFsSGVpZ2h0XG5cbiAgICAgICAgICAgICAgICBsZXQgc2NhbGVXID0gdGhpcy5tYXhXaWR0aCAvIGltYWdlLm5hdHVyYWxXaWR0aFxuICAgICAgICAgICAgICAgIGxldCBzY2FsZUggPSB0aGlzLm1heEhlaWdodCAvIGltYWdlLm5hdHVyYWxIZWlnaHRcbiAgICAgICAgICAgICAgICB0aGlzLnNjYWxlID0gTWF0aC5taW4odGhpcy5tYXhTY2FsZSwgTWF0aC5taW4oc2NhbGVXLCBzY2FsZUgpKVxuICAgICAgICAgICAgICAgIGltYWdlLnNldEF0dHJpYnV0ZSgnZHJhZ2dhYmxlJywgZmFsc2UpXG4gICAgICAgICAgICAgICAgaW1hZ2Uud2lkdGggPSBpbWFnZS5uYXR1cmFsV2lkdGhcbiAgICAgICAgICAgICAgICBpbWFnZS5oZWlnaHQgPSBpbWFnZS5uYXR1cmFsSGVpZ2h0XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh0aGlzKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaW1hZ2Uub25lcnJvciA9IGUgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdCh0aGlzKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaW1hZ2Uuc3JjID0gdGhpcy5zcmNcbiAgICAgICAgfSlcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBGcmFtZUxvYWRlciBleHRlbmRzIENhcmRMb2FkZXIge1xuICAgIGxvYWQoZG9tTm9kZSkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgbGV0IGlzRnJhbWUgPSBkb21Ob2RlIGluc3RhbmNlb2YgSFRNTElGcmFtZUVsZW1lbnRcbiAgICAgICAgICAgIGxldCBpZnJhbWUgPSBpc0ZyYW1lID8gZG9tTm9kZSA6IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lmcmFtZScpXG4gICAgICAgICAgICBjb25zb2xlLmxvZygnRnJhbWVMb2FkZXIubG9hZCcsIGlzRnJhbWUsIGlmcmFtZSwgdGhpcy5zcmMpXG4gICAgICAgICAgICBpZnJhbWUuZnJhbWVCb3JkZXIgPSAwXG4gICAgICAgICAgICBpZnJhbWUuc3R5bGUuc2Nyb2xsaW5nID0gZmFsc2VcbiAgICAgICAgICAgIGlmcmFtZS53aWR0aCA9IHRoaXMud2FudGVkV2lkdGhcbiAgICAgICAgICAgIGlmcmFtZS5oZWlnaHQgPSB0aGlzLndhbnRlZEhlaWdodFxuICAgICAgICAgICAgaWYgKCFpc0ZyYW1lKSB7XG4gICAgICAgICAgICAgICAgLy8gVW5saWtlIGltZyBvbmxvYWQgaXMgb25seSB0cmlnZ2VyZWQgaWYgdGhlIGlmcmFtZSBpcyBwYXJ0IG9mIHRoZSBET00gdHJlZVxuICAgICAgICAgICAgICAgIGRvbU5vZGUuYXBwZW5kQ2hpbGQoaWZyYW1lKVxuICAgICAgICAgICAgICAgIHRoaXMuYWRkZWROb2RlID0gaWZyYW1lXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZnJhbWUub25sb2FkID0gZSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh0aGlzKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWZyYW1lLm9uZXJyb3IgPSBlID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QodGhpcylcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmcmFtZS5zcmMgPSB0aGlzLnNyY1xuICAgICAgICB9KVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIEhUTUxMb2FkZXIgZXh0ZW5kcyBDYXJkTG9hZGVyIHtcbiAgICBsb2FkKGRvbU5vZGUpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGxldCB4aHIgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKVxuICAgICAgICAgICAgeGhyLm9wZW4oJ0dFVCcsIHRoaXMuc3JjLCBmYWxzZSlcbiAgICAgICAgICAgIHhoci5vbmxvYWQgPSBlID0+IHtcbiAgICAgICAgICAgICAgICBkb21Ob2RlLmlubmVySFRNTCA9IHhoci5yZXNwb25zZVxuICAgICAgICAgICAgICAgIHRoaXMuYWRkZWROb2RlID0gZG9tTm9kZS5maXJzdEVsZW1lbnRDaGlsZFxuICAgICAgICAgICAgICAgIGxldCB7IHdpZHRoLCBoZWlnaHQgfSA9IHRoaXMuc2l6ZSh0aGlzLmFkZGVkTm9kZSlcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIkhUTUxMb2FkZXIubG9hZFwiLCB7IGFkZGVkOiB0aGlzLmFkZGVkTm9kZSwgd2lkdGgsIGhlaWdodCB9KVxuICAgICAgICAgICAgICAgIGlmICh3aWR0aClcbiAgICAgICAgICAgICAgICAgICAgdGhpcy53YW50ZWRXaWR0aCA9IHdpZHRoIHx8IHRoaXMud2FudGVkV2lkdGhcbiAgICAgICAgICAgICAgICBpZiAoaGVpZ2h0KVxuICAgICAgICAgICAgICAgICAgICB0aGlzLndhbnRlZEhlaWdodCA9IGhlaWdodCB8fCB0aGlzLndhbnRlZEhlaWdodFxuICAgICAgICAgICAgICAgIHJlc29sdmUodGhpcylcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHhoci5vbmVycm9yID0gZSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KHRoaXMpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB4aHIuc2VuZCgpXG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVHJpZXMgdG8gZGV0ZXJtaW5lIHRoZSBzaXplIG9mIHRoZSBhZGRlZE5vZGUuXG4gICAgICogQ2hlY2tzIGZvciBleHBsaWNpdCB3aWR0aCBhbmQgaGVpZ2h0IHN0eWxlIGF0dHJpYnV0ZXMuXG4gICAgICogXG4gICAgICogT3ZlcndyaXRlIHRoaXMgbWV0aG9kIGlmIHlvdSB3YW50IHRvIGV4dHJhY3QgdmFsdWVzIGZyb20gb3RoZXIgaW5mb3MuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB7IHdpZHRoOiBpbnQsIGhlaWdodDogaW50IH1cbiAgICAgKiBAbWVtYmVyb2YgSFRNTExvYWRlclxuICAgICAqL1xuICAgIHNpemUobm9kZSkge1xuICAgICAgICBsZXQgd2lkdGggPSBwYXJzZUludChub2RlLnN0eWxlLndpZHRoKSB8fCBudWxsXG4gICAgICAgIGxldCBoZWlnaHQgPSBwYXJzZUludChub2RlLnN0eWxlLmhlaWdodCkgfHwgbnVsbFxuICAgICAgICByZXR1cm4geyB3aWR0aCwgaGVpZ2h0IH1cbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBET01GbGlwIHtcbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgZG9tU2NhdHRlckNvbnRhaW5lcixcbiAgICAgICAgZmxpcFRlbXBsYXRlLFxuICAgICAgICBmcm9udExvYWRlcixcbiAgICAgICAgYmFja0xvYWRlcixcbiAgICAgICAge1xuICAgICAgICAgICAgY2xvc2VPbk1pblNjYWxlID0gZmFsc2UsXG4gICAgICAgICAgICBmbGlwRHVyYXRpb24gPSAxLFxuICAgICAgICAgICAgZmFkZUR1cmF0aW9uID0gMC4yLFxuICAgICAgICAgICAgb3ZlcmRvU2NhbGluZyA9IDEsXG4gICAgICAgICAgICBhdXRvTG9hZCA9IGZhbHNlLFxuICAgICAgICAgICAgY2VudGVyID0gbnVsbCxcbiAgICAgICAgICAgIHByZWxvYWRCYWNrID0gZmFsc2UsXG4gICAgICAgICAgICB0cmFuc2xhdGFibGUgPSB0cnVlLFxuICAgICAgICAgICAgc2NhbGFibGUgPSB0cnVlLFxuICAgICAgICAgICAgcm90YXRhYmxlID0gdHJ1ZSxcbiAgICAgICAgICAgIG9uRnJvbnQgPSBudWxsLFxuICAgICAgICAgICAgb25CYWNrID0gbnVsbCxcbiAgICAgICAgICAgIG9uQ2xvc2UgPSBudWxsLFxuICAgICAgICAgICAgb25VcGRhdGUgPSBudWxsLFxuICAgICAgICAgICAgb25SZW1vdmVkID0gbnVsbCxcbiAgICAgICAgICAgIG9uTG9hZGVkID0gbnVsbFxuICAgICAgICB9ID0ge31cbiAgICApIHtcbiAgICAgICAgdGhpcy5kb21TY2F0dGVyQ29udGFpbmVyID0gZG9tU2NhdHRlckNvbnRhaW5lclxuICAgICAgICB0aGlzLmlkID0gZ2V0SWQoKVxuICAgICAgICB0aGlzLmZsaXBEdXJhdGlvbiA9IGZsaXBEdXJhdGlvblxuICAgICAgICB0aGlzLmZhZGVEdXJhdGlvbiA9IGZhZGVEdXJhdGlvblxuICAgICAgICB0aGlzLmNsb3NlT25NaW5TY2FsZSA9IGNsb3NlT25NaW5TY2FsZVxuICAgICAgICB0aGlzLmZsaXBUZW1wbGF0ZSA9IGZsaXBUZW1wbGF0ZVxuICAgICAgICB0aGlzLmZyb250TG9hZGVyID0gZnJvbnRMb2FkZXJcbiAgICAgICAgdGhpcy5iYWNrTG9hZGVyID0gYmFja0xvYWRlclxuICAgICAgICB0aGlzLnRyYW5zbGF0YWJsZSA9IHRyYW5zbGF0YWJsZVxuICAgICAgICB0aGlzLnNjYWxhYmxlID0gc2NhbGFibGVcbiAgICAgICAgdGhpcy5yb3RhdGFibGUgPSByb3RhdGFibGVcbiAgICAgICAgdGhpcy5vbkZyb250RmxpcHBlZCA9IG9uRnJvbnRcbiAgICAgICAgdGhpcy5vbkJhY2tGbGlwcGVkID0gb25CYWNrXG4gICAgICAgIHRoaXMub25DbG9zZSA9IG9uQ2xvc2VcbiAgICAgICAgdGhpcy5vblJlbW92ZWQgPSBvblJlbW92ZWRcbiAgICAgICAgdGhpcy5vblVwZGF0ZSA9IG9uVXBkYXRlXG4gICAgICAgIHRoaXMub25Mb2FkZWQgPSBvbkxvYWRlZFxuICAgICAgICB0aGlzLmNlbnRlciA9IGNlbnRlclxuICAgICAgICB0aGlzLnByZWxvYWRCYWNrID0gcHJlbG9hZEJhY2tcbiAgICAgICAgdGhpcy5vdmVyZG9TY2FsaW5nID0gb3ZlcmRvU2NhbGluZ1xuICAgICAgICBpZiAoYXV0b0xvYWQpIHtcbiAgICAgICAgICAgIHRoaXMubG9hZCgpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBsb2FkKCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgbGV0IHQgPSB0aGlzLmZsaXBUZW1wbGF0ZVxuICAgICAgICAgICAgbGV0IGRvbSA9IHRoaXMuZG9tU2NhdHRlckNvbnRhaW5lci5lbGVtZW50XG4gICAgICAgICAgICBsZXQgd3JhcHBlciA9IHQuY29udGVudC5xdWVyeVNlbGVjdG9yKCcuZmxpcFdyYXBwZXInKVxuICAgICAgICAgICAgd3JhcHBlci5pZCA9IHRoaXMuaWRcbiAgICAgICAgICAgIGxldCBjbG9uZSA9IGRvY3VtZW50LmltcG9ydE5vZGUodC5jb250ZW50LCB0cnVlKVxuICAgICAgICAgICAgZG9tLmFwcGVuZENoaWxkKGNsb25lKVxuICAgICAgICAgICAgLy8gV2UgY2Fubm90IHVzZSB0aGUgZG9jdW1lbnQgZnJhZ21lbnQgaXRzZWxmIGJlY2F1c2UgaXRcbiAgICAgICAgICAgIC8vIGlzIG5vdCBwYXJ0IG9mIHRoZSBtYWluIGRvbSB0cmVlLiBBZnRlciB0aGUgYXBwZW5kQ2hpbGRcbiAgICAgICAgICAgIC8vIGNhbGwgd2UgY2FuIGFjY2VzcyB0aGUgbmV3IGRvbSBlbGVtZW50IGJ5IGlkXG4gICAgICAgICAgICB0aGlzLmNhcmRXcmFwcGVyID0gZG9tLnF1ZXJ5U2VsZWN0b3IoJyMnICsgdGhpcy5pZClcbiAgICAgICAgICAgIGxldCBmcm9udCA9IHRoaXMuY2FyZFdyYXBwZXIucXVlcnlTZWxlY3RvcignLmZyb250JylcbiAgICAgICAgICAgIHRoaXMuZnJvbnRMb2FkZXIubG9hZChmcm9udCkudGhlbihsb2FkZXIgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuZnJvbnRMb2FkZWQobG9hZGVyKS50aGVuKChvYmopID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMub25Mb2FkZWQpIHRoaXMub25Mb2FkZWQoKVxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHRoaXMpXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgZnJvbnRMb2FkZWQobG9hZGVyKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBsZXQgc2NhdHRlciA9IG5ldyBET01TY2F0dGVyKFxuICAgICAgICAgICAgICAgIHRoaXMuY2FyZFdyYXBwZXIsXG4gICAgICAgICAgICAgICAgdGhpcy5kb21TY2F0dGVyQ29udGFpbmVyLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgeDogbG9hZGVyLngsXG4gICAgICAgICAgICAgICAgICAgIHk6IGxvYWRlci55LFxuICAgICAgICAgICAgICAgICAgICBzdGFydFNjYWxlOiBsb2FkZXIuc2NhbGUsXG4gICAgICAgICAgICAgICAgICAgIHNjYWxlOiBsb2FkZXIuc2NhbGUsXG4gICAgICAgICAgICAgICAgICAgIG1heFNjYWxlOiBsb2FkZXIubWF4U2NhbGUsXG4gICAgICAgICAgICAgICAgICAgIG1pblNjYWxlOiBsb2FkZXIubWluU2NhbGUsXG4gICAgICAgICAgICAgICAgICAgIHdpZHRoOiBsb2FkZXIud2FudGVkV2lkdGgsXG4gICAgICAgICAgICAgICAgICAgIGhlaWdodDogbG9hZGVyLndhbnRlZEhlaWdodCxcbiAgICAgICAgICAgICAgICAgICAgcm90YXRpb246IGxvYWRlci5yb3RhdGlvbixcbiAgICAgICAgICAgICAgICAgICAgdHJhbnNsYXRhYmxlOiB0aGlzLnRyYW5zbGF0YWJsZSxcbiAgICAgICAgICAgICAgICAgICAgc2NhbGFibGU6IHRoaXMuc2NhbGFibGUsXG4gICAgICAgICAgICAgICAgICAgIHJvdGF0YWJsZTogdGhpcy5yb3RhdGFibGUsXG4gICAgICAgICAgICAgICAgICAgIG92ZXJkb1NjYWxpbmc6IHRoaXMub3ZlcmRvU2NhbGluZ1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIClcblxuICAgICAgICAgICAgaWYgKHRoaXMuY2VudGVyKSB7XG4gICAgICAgICAgICAgICAgc2NhdHRlci5jZW50ZXJBdCh0aGlzLmNlbnRlcilcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMuY2xvc2VPbk1pblNjYWxlKSB7XG5cbiAgICAgICAgICAgICAgICBjb25zdCByZW1vdmVPbk1pblNjYWxlID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoc2NhdHRlci5zY2FsZSA8PSBzY2F0dGVyLm1pblNjYWxlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmZsaXBwYWJsZS5jbG9zZSgpXG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vICdEaXNhYmxlJyBvdmVyZG9zY2FsaW5nIHRvIGF2b2lkIHdlaXJkIGp1bXBzIG9uIGNsb3NlLlxuICAgICAgICAgICAgICAgICAgICAgICAgc2NhdHRlci5taW5TY2FsZSAvPSBzY2F0dGVyLm92ZXJkb1NjYWxpbmdcbiAgICAgICAgICAgICAgICAgICAgICAgIHNjYXR0ZXIub3ZlcmRvU2NhbGluZyA9IDFcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy9SZW1vdmUgY2FsbGJhY2tcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChzY2F0dGVyLm9uVHJhbnNmb3JtKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGNhbGxiYWNrSWR4ID0gc2NhdHRlci5vblRyYW5zZm9ybS5pbmRleE9mKHJlbW92ZU9uTWluU2NhbGUpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhdHRlci5vblRyYW5zZm9ybS5zcGxpY2UoY2FsbGJhY2tJZHgsIDEpXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIH0uYmluZCh0aGlzKVxuXG5cblxuICAgICAgICAgICAgICAgIHNjYXR0ZXIuYWRkVHJhbnNmb3JtRXZlbnRDYWxsYmFjayhyZW1vdmVPbk1pblNjYWxlKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBsZXQgZmxpcHBhYmxlID0gbmV3IERPTUZsaXBwYWJsZSh0aGlzLmNhcmRXcmFwcGVyLCBzY2F0dGVyLCB0aGlzKVxuICAgICAgICAgICAgbGV0IGJhY2sgPSB0aGlzLmNhcmRXcmFwcGVyLnF1ZXJ5U2VsZWN0b3IoJy5iYWNrJylcblxuICAgICAgICAgICAgaWYgKHRoaXMucHJlbG9hZEJhY2spIHtcbiAgICAgICAgICAgICAgICB0aGlzLmJhY2tMb2FkZXIubG9hZChiYWNrKS50aGVuKGxvYWRlciA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2V0dXBGbGlwcGFibGUoZmxpcHBhYmxlLCBsb2FkZXIpXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuZmxpcHBhYmxlID0gZmxpcHBhYmxlXG4gICAgICAgICAgICByZXNvbHZlKHRoaXMpXG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgY2VudGVyQXQocCkge1xuICAgICAgICB0aGlzLmNlbnRlciA9IHBcbiAgICAgICAgdGhpcy5mbGlwcGFibGUuY2VudGVyQXQocClcbiAgICB9XG5cbiAgICB6b29tKHNjYWxlKSB7XG4gICAgICAgIHRoaXMuZmxpcHBhYmxlLnpvb20oc2NhbGUpXG4gICAgfVxuXG4gICAgc2V0dXBGbGlwcGFibGUoZmxpcHBhYmxlLCBsb2FkZXIpIHtcbiAgICAgICAgZmxpcHBhYmxlLndhbnRlZFdpZHRoID0gbG9hZGVyLndhbnRlZFdpZHRoXG4gICAgICAgIGZsaXBwYWJsZS53YW50ZWRIZWlnaHQgPSBsb2FkZXIud2FudGVkSGVpZ2h0XG4gICAgICAgIGZsaXBwYWJsZS53YW50ZWRTY2FsZSA9IGxvYWRlci5zY2FsZVxuICAgICAgICBmbGlwcGFibGUubWluU2NhbGUgPSBsb2FkZXIubWluU2NhbGVcbiAgICAgICAgZmxpcHBhYmxlLm1heFNjYWxlID0gbG9hZGVyLm1heFNjYWxlXG4gICAgICAgIGZsaXBwYWJsZS5zY2FsZUJ1dHRvbnMoKVxuICAgIH1cblxuICAgIHN0YXJ0KHsgdGFyZ2V0Q2VudGVyID0gbnVsbCB9ID0ge30pIHtcbiAgICAgICAgY29uc29sZS5sb2coJ0RPTUZsaXAuc3RhcnQnLCB0YXJnZXRDZW50ZXIpXG4gICAgICAgIGlmICh0aGlzLnByZWxvYWRCYWNrKSB0aGlzLmZsaXBwYWJsZS5zdGFydCh7IGR1cmF0aW9uOiB0aGlzLmZsaXBEdXJhdGlvbiwgdGFyZ2V0Q2VudGVyIH0pXG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgbGV0IGJhY2sgPSB0aGlzLmNhcmRXcmFwcGVyLnF1ZXJ5U2VsZWN0b3IoJy5iYWNrJylcbiAgICAgICAgICAgIGxldCBmbGlwcGFibGUgPSB0aGlzLmZsaXBwYWJsZVxuICAgICAgICAgICAgdGhpcy5iYWNrTG9hZGVyLmxvYWQoYmFjaykudGhlbihsb2FkZXIgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc2V0dXBGbGlwcGFibGUoZmxpcHBhYmxlLCBsb2FkZXIpXG4gICAgICAgICAgICAgICAgZmxpcHBhYmxlLnN0YXJ0KHsgZHVyYXRpb246IHRoaXMuZmxpcER1cmF0aW9uLCB0YXJnZXRDZW50ZXIgfSlcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmYWRlT3V0QW5kUmVtb3ZlKCkge1xuICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5jYXJkV3JhcHBlciwgdGhpcy5mYWRlRHVyYXRpb24sIHtcbiAgICAgICAgICAgIG9wYWNpdHk6IDAsXG4gICAgICAgICAgICBvbkNvbXBsZXRlOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5jYXJkV3JhcHBlci5yZW1vdmUoKVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIGNsb3NlZCgpIHtcbiAgICAgICAgdGhpcy51bmxvYWQoKVxuICAgIH1cblxuICAgIHVubG9hZCgpIHtcbiAgICAgICAgaWYgKCF0aGlzLnByZWxvYWRCYWNrKSB7XG4gICAgICAgICAgICB0aGlzLmJhY2tMb2FkZXIudW5sb2FkKClcbiAgICAgICAgfVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIERPTUZsaXBwYWJsZSB7XG4gICAgY29uc3RydWN0b3IoZWxlbWVudCwgc2NhdHRlciwgZmxpcCkge1xuICAgICAgICAvLyBTZXQgbG9nIHRvIGNvbnNvbGUubG9nIG9yIGEgY3VzdG9tIGxvZyBmdW5jdGlvblxuICAgICAgICAvLyBkZWZpbmUgZGF0YSBzdHJ1Y3R1cmVzIHRvIHN0b3JlIG91ciB0b3VjaHBvaW50cyBpblxuXG4gICAgICAgIHRoaXMuZWxlbWVudCA9IGVsZW1lbnRcbiAgICAgICAgdGhpcy5mbGlwID0gZmxpcFxuICAgICAgICB0aGlzLmNhcmQgPSBlbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJy5mbGlwQ2FyZCcpXG4gICAgICAgIHRoaXMuZnJvbnQgPSBlbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJy5mcm9udCcpXG4gICAgICAgIHRoaXMuYmFjayA9IGVsZW1lbnQucXVlcnlTZWxlY3RvcignLmJhY2snKVxuICAgICAgICB0aGlzLmZsaXBwZWQgPSBmYWxzZVxuICAgICAgICB0aGlzLnNjYXR0ZXIgPSBzY2F0dGVyXG4gICAgICAgIHRoaXMub25Gcm9udEZsaXBwZWQgPSBmbGlwLm9uRnJvbnRGbGlwcGVkXG4gICAgICAgIHRoaXMub25CYWNrRmxpcHBlZCA9IGZsaXAub25CYWNrRmxpcHBlZFxuICAgICAgICB0aGlzLm9uQ2xvc2UgPSBmbGlwLm9uQ2xvc2VcbiAgICAgICAgdGhpcy5vblJlbW92ZWQgPSBmbGlwLm9uUmVtb3ZlZFxuICAgICAgICB0aGlzLm9uVXBkYXRlID0gZmxpcC5vblVwZGF0ZVxuXG4gICAgICAgIHRoaXMuZmxpcER1cmF0aW9uID0gZmxpcC5mbGlwRHVyYXRpb25cbiAgICAgICAgdGhpcy5mYWRlRHVyYXRpb24gPSBmbGlwLmZhZGVEdXJhdGlvblxuICAgICAgICBzY2F0dGVyLmFkZFRyYW5zZm9ybUV2ZW50Q2FsbGJhY2sodGhpcy5zY2F0dGVyVHJhbnNmb3JtZWQuYmluZCh0aGlzKSlcbiAgICAgICAgY29uc29sZS5sb2coJ2xpYi5ET01GbGlwcGFibGUnLCA1MDAwKVxuICAgICAgICBUd2VlbkxpdGUuc2V0KHRoaXMuZWxlbWVudCwgeyBwZXJzcGVjdGl2ZTogNTAwMCB9KVxuICAgICAgICBUd2VlbkxpdGUuc2V0KHRoaXMuY2FyZCwgeyB0cmFuc2Zvcm1TdHlsZTogJ3ByZXNlcnZlLTNkJyB9KVxuICAgICAgICBUd2VlbkxpdGUuc2V0KHRoaXMuYmFjaywgeyByb3RhdGlvblk6IC0xODAgfSlcbiAgICAgICAgVHdlZW5MaXRlLnNldChbdGhpcy5iYWNrLCB0aGlzLmZyb250XSwge1xuICAgICAgICAgICAgYmFja2ZhY2VWaXNpYmlsaXR5OiAnaGlkZGVuJyxcbiAgICAgICAgICAgIHBlcnNwZWN0aXZlOiA1MDAwXG4gICAgICAgIH0pXG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5mcm9udCwgeyB2aXNpYmlsaXR5OiAndmlzaWJsZScgfSlcbiAgICAgICAgdGhpcy5pbmZvQnRuID0gZWxlbWVudC5xdWVyeVNlbGVjdG9yKCcuaW5mb0J0bicpXG4gICAgICAgIHRoaXMuYmFja0J0biA9IGVsZW1lbnQucXVlcnlTZWxlY3RvcignLmJhY2tCdG4nKVxuICAgICAgICB0aGlzLmNsb3NlQnRuID0gZWxlbWVudC5xdWVyeVNlbGVjdG9yKCcuY2xvc2VCdG4nKVxuICAgICAgICAvKiBCdXR0b25zIGFyZSBub3QgZ3VhcmFudGVlZCB0byBleGlzdC4gKi9cbiAgICAgICAgaWYgKHRoaXMuaW5mb0J0bikge1xuICAgICAgICAgICAgSW50ZXJhY3Rpb25NYXBwZXIub24oJ3RhcCcsIHRoaXMuaW5mb0J0biwgZXZlbnQgPT4gdGhpcy5mbGlwLnN0YXJ0KCkpXG5cbiAgICAgICAgICAgIHRoaXMuZW5hYmxlKHRoaXMuaW5mb0J0bilcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5iYWNrQnRuKSB7XG4gICAgICAgICAgICBJbnRlcmFjdGlvbk1hcHBlci5vbigndGFwJywgdGhpcy5iYWNrQnRuLCBldmVudCA9PiB0aGlzLnN0YXJ0KCkpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuY2xvc2VCdG4pIHtcbiAgICAgICAgICAgIEludGVyYWN0aW9uTWFwcGVyLm9uKCd0YXAnLCB0aGlzLmNsb3NlQnRuLCBldmVudCA9PiB0aGlzLmNsb3NlKCkpXG4gICAgICAgICAgICB0aGlzLmVuYWJsZSh0aGlzLmNsb3NlQnRuKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuc2NhbGVCdXR0b25zKClcbiAgICAgICAgdGhpcy5icmluZ1RvRnJvbnQoKVxuICAgIH1cblxuICAgIGNsb3NlKCkge1xuICAgICAgICB0aGlzLmRpc2FibGUodGhpcy5pbmZvQnRuKVxuICAgICAgICB0aGlzLmRpc2FibGUodGhpcy5jbG9zZUJ0bilcbiAgICAgICAgaWYgKHRoaXMub25DbG9zZSkge1xuICAgICAgICAgICAgdGhpcy5vbkNsb3NlKHRoaXMpXG4gICAgICAgICAgICB0aGlzLmZsaXAuY2xvc2VkKClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc2NhdHRlci56b29tKDAuMSwge1xuICAgICAgICAgICAgICAgIGFuaW1hdGU6IHRoaXMuZmFkZUR1cmF0aW9uLFxuICAgICAgICAgICAgICAgIG9uQ29tcGxldGU6ICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5lbGVtZW50LnJlbW92ZSgpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmxpcC5jbG9zZWQoKVxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5vblJlbW92ZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25SZW1vdmVkLmNhbGwodGhpcylcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzaG93RnJvbnQoKSB7XG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5mcm9udCwgeyB2aXNpYmlsaXR5OiAndmlzaWJsZScgfSlcbiAgICB9XG5cbiAgICBjZW50ZXJBdChwKSB7XG4gICAgICAgIHRoaXMuc2NhdHRlci5jZW50ZXJBdChwKVxuICAgIH1cblxuICAgIHpvb20oc2NhbGUpIHtcbiAgICAgICAgdGhpcy5zY2F0dGVyLnpvb20oc2NhbGUpXG4gICAgfVxuXG4gICAgZ2V0IGJ1dHRvblNjYWxlKCkge1xuICAgICAgICBsZXQgaXNjYWxlID0gMS4wXG5cbiAgICAgICAgaWYgKHRoaXMuc2NhdHRlciAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgc2NhbGUgPSB0aGlzLnNjYXR0ZXIuc2NhbGUgfHwgMVxuICAgICAgICAgICAgaXNjYWxlID0gMS4wIC8gc2NhbGVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gaXNjYWxlXG4gICAgfVxuXG4gICAgc2NhbGVCdXR0b25zKCkge1xuICAgICAgICAvL1RoaXMgYWxzbyB3b3JrcyBmb3Igc3Zncy5cbiAgICAgICAgLy8gaWYgKHRoaXMuaW5mb0J0bilcbiAgICAgICAgLy8gICAgIHRoaXMuaW5mb0J0bi5zdHlsZS50cmFuc2Zvcm0gPSBcInNjYWxlKFwiICsgdGhpcy5idXR0b25TY2FsZSArIFwiKVwiXG5cbiAgICAgICAgLy8gaWYgKHRoaXMuYmFja0J0bilcbiAgICAgICAgLy8gICAgIHRoaXMuYmFja0J0bi5zdHlsZS50cmFuc2Zvcm0gPSBcInNjYWxlKFwiICsgdGhpcy5idXR0b25TY2FsZSArIFwiKVwiXG5cbiAgICAgICAgLy8gaWYgKHRoaXMuY2xvc2VCdG4pXG4gICAgICAgIC8vICAgICB0aGlzLmNsb3NlQnRuLnN0eWxlLnRyYW5zZm9ybSA9IFwic2NhbGUoXCIgKyB0aGlzLmJ1dHRvblNjYWxlICsgXCIpXCJcblxuICAgICAgICBjb25zb2xlLmxvZyh0aGlzLmJ1dHRvblNjYWxlKVxuICAgICAgICAvLy8vIFRoaXMgZGlkIG5vdCB3b3JrIHdpdGggc3ZncyFcbiAgICAgICAgVHdlZW5MaXRlLnNldChbdGhpcy5pbmZvQnRuLCB0aGlzLmJhY2tCdG4sIHRoaXMuY2xvc2VCdG5dLCB7XG4gICAgICAgICAgICBzY2FsZTogdGhpcy5idXR0b25TY2FsZVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIGJyaW5nVG9Gcm9udCgpIHtcbiAgICAgICAgdGhpcy5zY2F0dGVyLmJyaW5nVG9Gcm9udCgpXG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7IHpJbmRleDogRE9NU2NhdHRlci56SW5kZXgrKyB9KVxuICAgIH1cblxuICAgIGNsaWNrSW5mbygpIHtcbiAgICAgICAgdGhpcy5icmluZ1RvRnJvbnQoKVxuICAgICAgICB0aGlzLmluZm9CdG4uY2xpY2soKVxuICAgIH1cblxuICAgIHNjYXR0ZXJUcmFuc2Zvcm1lZChldmVudCkge1xuICAgICAgICB0aGlzLnNjYWxlQnV0dG9ucygpXG4gICAgfVxuXG4gICAgdGFyZ2V0Um90YXRpb24oYWxwaGEpIHtcbiAgICAgICAgbGV0IG9ydGhvID0gOTBcbiAgICAgICAgbGV0IHJlc3QgPSBhbHBoYSAlIG9ydGhvXG4gICAgICAgIGxldCBkZWx0YSA9IDAuMFxuICAgICAgICBpZiAocmVzdCA+IG9ydGhvIC8gMi4wKSB7XG4gICAgICAgICAgICBkZWx0YSA9IG9ydGhvIC0gcmVzdFxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZGVsdGEgPSAtcmVzdFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkZWx0YVxuICAgIH1cblxuICAgIGluZm9WYWx1ZXMoaW5mbykge1xuICAgICAgICBsZXQgc3RhcnRYID0gdGhpcy5lbGVtZW50Ll9nc1RyYW5zZm9ybS54XG4gICAgICAgIGxldCBzdGFydFkgPSB0aGlzLmVsZW1lbnQuX2dzVHJhbnNmb3JtLnlcbiAgICAgICAgbGV0IHN0YXJ0QW5nbGUgPSB0aGlzLmVsZW1lbnQuX2dzVHJhbnNmb3JtLnJvdGF0aW9uXG4gICAgICAgIGxldCBzdGFydFNjYWxlID0gdGhpcy5lbGVtZW50Ll9nc1RyYW5zZm9ybS5zY2FsZVhcbiAgICAgICAgbGV0IHcgPSB0aGlzLmVsZW1lbnQuc3R5bGUud2lkdGhcbiAgICAgICAgbGV0IGggPSB0aGlzLmVsZW1lbnQuc3R5bGUuaGVpZ2h0XG4gICAgICAgIGNvbnNvbGUubG9nKGluZm8sIHN0YXJ0WCwgc3RhcnRZLCBzdGFydEFuZ2xlLCBzdGFydFNjYWxlLCB3LCBoKVxuICAgIH1cblxuICAgIHNob3coZWxlbWVudCwgZHVyYXRpb24gPSAwLCBhbHBoYSA9IDEpIHtcbiAgICAgICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgICAgIFR3ZWVuTGl0ZS50byhlbGVtZW50LCBkdXJhdGlvbiwgeyBhdXRvQWxwaGE6IGFscGhhIH0pIC8vIHZpc2liaWxpdHk6ICd2aXNpYmxlJywgZGlzcGxheTogJ2luaXRpYWwnfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGhpZGUoZWxlbWVudCwgZHVyYXRpb24gPSAwLCBhbHBoYSA9IDApIHtcbiAgICAgICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgICAgIFR3ZWVuTGl0ZS50byhlbGVtZW50LCBkdXJhdGlvbiwgeyBhdXRvQWxwaGE6IGFscGhhIH0pIC8vIHt2aXNpYmlsaXR5OiAnaGlkZGVuJywgZGlzcGxheTogJ25vbmUnfSlcbiAgICAgICAgfVxuICAgIH1cblxuXG5cbiAgICBlbmFibGUoYnV0dG9uKSB7XG4gICAgICAgIHRoaXMuc2hvdyhidXR0b24sIHRoaXMuZmFkZUR1cmF0aW9uKVxuICAgICAgICBpZiAoYnV0dG9uKSB7XG4gICAgICAgICAgICBUd2VlbkxpdGUuc2V0KGJ1dHRvbiwgeyBwb2ludGVyRXZlbnRzOiAnYXV0bycgfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGRpc2FibGUoYnV0dG9uKSB7XG4gICAgICAgIHRoaXMuaGlkZShidXR0b24sIHRoaXMuZmFkZUR1cmF0aW9uKVxuICAgICAgICBpZiAoYnV0dG9uKSB7XG4gICAgICAgICAgICBUd2VlbkxpdGUuc2V0KGJ1dHRvbiwgeyBwb2ludGVyRXZlbnRzOiAnbm9uZScgfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXJ0KHsgdGFyZ2V0Q2VudGVyID0gbnVsbCB9ID0ge30pIHtcbiAgICAgICAgdGhpcy5icmluZ1RvRnJvbnQoKVxuICAgICAgICBpZiAoIXRoaXMuZmxpcHBlZCkge1xuICAgICAgICAgICAgdGhpcy5zdGFydFggPSB0aGlzLmVsZW1lbnQuX2dzVHJhbnNmb3JtLnhcbiAgICAgICAgICAgIHRoaXMuc3RhcnRZID0gdGhpcy5lbGVtZW50Ll9nc1RyYW5zZm9ybS55XG4gICAgICAgICAgICB0aGlzLnN0YXJ0QW5nbGUgPSB0aGlzLmVsZW1lbnQuX2dzVHJhbnNmb3JtLnJvdGF0aW9uXG4gICAgICAgICAgICB0aGlzLnN0YXJ0U2NhbGUgPSB0aGlzLmVsZW1lbnQuX2dzVHJhbnNmb3JtLnNjYWxlWFxuICAgICAgICAgICAgdGhpcy5zdGFydFdpZHRoID0gdGhpcy5lbGVtZW50LnN0eWxlLndpZHRoXG4gICAgICAgICAgICB0aGlzLnN0YXJ0SGVpZ2h0ID0gdGhpcy5lbGVtZW50LnN0eWxlLmhlaWdodFxuICAgICAgICAgICAgdGhpcy5zY2F0dGVyU3RhcnRXaWR0aCA9IHRoaXMuc2NhdHRlci53aWR0aFxuICAgICAgICAgICAgdGhpcy5zY2F0dGVyU3RhcnRIZWlnaHQgPSB0aGlzLnNjYXR0ZXIuaGVpZ2h0XG4gICAgICAgICAgICB0aGlzLnNob3codGhpcy5iYWNrKVxuICAgICAgICAgICAgdGhpcy5kaXNhYmxlKHRoaXMuaW5mb0J0bilcbiAgICAgICAgICAgIHRoaXMuZGlzYWJsZSh0aGlzLmNsb3NlQnRuKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5zaG93KHRoaXMuZnJvbnQsIHRoaXMuZmFkZUR1cmF0aW9uKVxuICAgICAgICAgICAgdGhpcy5kaXNhYmxlKHRoaXMuYmFja0J0bilcbiAgICAgICAgfVxuICAgICAgICBsZXQgeyBzY2FsYWJsZSwgdHJhbnNsYXRhYmxlLCByb3RhdGFibGUgfSA9IHRoaXMuc2NhdHRlclxuICAgICAgICB0aGlzLnNhdmVkID0geyBzY2FsYWJsZSwgdHJhbnNsYXRhYmxlLCByb3RhdGFibGUgfVxuICAgICAgICB0aGlzLnNjYXR0ZXIuc2NhbGFibGUgPSBmYWxzZVxuICAgICAgICB0aGlzLnNjYXR0ZXIudHJhbnNsYXRhYmxlID0gZmFsc2VcbiAgICAgICAgdGhpcy5zY2F0dGVyLnJvdGF0YWJsZSA9IGZhbHNlXG4gICAgICAgIHRoaXMuc2NhdHRlci5raWxsQW5pbWF0aW9uKClcblxuICAgICAgICB0aGlzLmZsaXBwZWQgPSAhdGhpcy5mbGlwcGVkXG4gICAgICAgIGxldCB0YXJnZXRZID0gdGhpcy5mbGlwcGVkID8gMTgwIDogMFxuICAgICAgICBsZXQgdGFyZ2V0WiA9IHRoaXMuZmxpcHBlZFxuICAgICAgICAgICAgPyB0aGlzLnN0YXJ0QW5nbGUgKyB0aGlzLnRhcmdldFJvdGF0aW9uKHRoaXMuc3RhcnRBbmdsZSlcbiAgICAgICAgICAgIDogdGhpcy5zdGFydEFuZ2xlXG4gICAgICAgIGxldCB0YXJnZXRTY2FsZSA9IHRoaXMuZmxpcHBlZCA/IHRoaXMud2FudGVkU2NhbGUgOiB0aGlzLnN0YXJ0U2NhbGVcbiAgICAgICAgbGV0IHcgPSB0aGlzLmZsaXBwZWQgPyB0aGlzLndhbnRlZFdpZHRoIDogdGhpcy5zdGFydFdpZHRoXG4gICAgICAgIGxldCBoID0gdGhpcy5mbGlwcGVkID8gdGhpcy53YW50ZWRIZWlnaHQgOiB0aGlzLnN0YXJ0SGVpZ2h0XG4gICAgICAgIGxldCBkdyA9IHRoaXMud2FudGVkV2lkdGggLSB0aGlzLnNjYXR0ZXIud2lkdGhcbiAgICAgICAgbGV0IGRoID0gdGhpcy53YW50ZWRIZWlnaHQgLSB0aGlzLnNjYXR0ZXIuaGVpZ2h0XG4gICAgICAgIGxldCB0YyA9IHRhcmdldENlbnRlclxuICAgICAgICBsZXQgeHggPSB0YyAhPSBudWxsID8gdGMueCAtIHcgLyAyIDogdGhpcy5zdGFydFggLSBkdyAvIDJcbiAgICAgICAgbGV0IHl5ID0gdGMgIT0gbnVsbCA/IHRjLnkgLSBoIC8gMiA6IHRoaXMuc3RhcnRZIC0gZGggLyAyXG4gICAgICAgIGxldCB4ID0gdGhpcy5mbGlwcGVkID8geHggOiB0aGlzLnN0YXJ0WFxuICAgICAgICBsZXQgeSA9IHRoaXMuZmxpcHBlZCA/IHl5IDogdGhpcy5zdGFydFlcblxuICAgICAgICBjb25zb2xlLmxvZyhcIkRPTUZsaXBwYWJsZS5zdGFydFwiLCB0aGlzLmZsaXBwZWQsIHRhcmdldENlbnRlciwgeCwgeSwgdGhpcy5zYXZlZClcbiAgICAgICAgbGV0IG9uVXBkYXRlID0gdGhpcy5vblVwZGF0ZSAhPT0gbnVsbCA/ICgpID0+IHRoaXMub25VcGRhdGUodGhpcykgOiBudWxsXG4gICAgICAgIGNvbnNvbGUubG9nKHRoaXMuZmxpcER1cmF0aW9uKVxuICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5jYXJkLCB0aGlzLmZsaXBEdXJhdGlvbiwge1xuICAgICAgICAgICAgcm90YXRpb25ZOiB0YXJnZXRZLFxuICAgICAgICAgICAgZWFzZTogUG93ZXIxLmVhc2VPdXQsXG4gICAgICAgICAgICB0cmFuc2Zvcm1PcmlnaW46ICc1MCUgNTAlJyxcbiAgICAgICAgICAgIG9uVXBkYXRlLFxuICAgICAgICAgICAgb25Db21wbGV0ZTogZSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuZmxpcHBlZCkge1xuICAgICAgICAgICAgICAgICAgICAvL3RoaXMuaGlkZSh0aGlzLmZyb250KVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmVuYWJsZSh0aGlzLmJhY2tCdG4pXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2hvdyh0aGlzLmJhY2tCdG4pXG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMub25Gcm9udEZsaXBwZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25Gcm9udEZsaXBwZWQodGhpcylcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMub25CYWNrRmxpcHBlZCA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVuYWJsZSh0aGlzLmluZm9CdG4sIHRoaXMuZmFkZUR1cmF0aW9uKVxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5lbmFibGUodGhpcy5jbG9zZUJ0biwgdGhpcy5mYWRlRHVyYXRpb24pXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uQmFja0ZsaXBwZWQodGhpcylcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmZsaXAudW5sb2FkKClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5zY2F0dGVyLnNjYWxlID0gdGFyZ2V0U2NhbGVcbiAgICAgICAgICAgICAgICB0aGlzLnNjYWxlQnV0dG9ucygpXG4gICAgICAgICAgICAgICAgdGhpcy5zY2F0dGVyLnJvdGF0aW9uRGVncmVlcyA9IHRhcmdldFpcbiAgICAgICAgICAgICAgICB0aGlzLnNjYXR0ZXIud2lkdGggPSB0aGlzLmZsaXBwZWQgPyB3IDogdGhpcy5zY2F0dGVyU3RhcnRXaWR0aFxuICAgICAgICAgICAgICAgIHRoaXMuc2NhdHRlci5oZWlnaHQgPSB0aGlzLmZsaXBwZWQgPyBoIDogdGhpcy5zY2F0dGVyU3RhcnRIZWlnaHRcblxuICAgICAgICAgICAgICAgIGxldCB7IHNjYWxhYmxlLCB0cmFuc2xhdGFibGUsIHJvdGF0YWJsZSB9ID0gdGhpcy5zYXZlZFxuICAgICAgICAgICAgICAgIHRoaXMuc2NhdHRlci5zY2FsYWJsZSA9IHNjYWxhYmxlXG4gICAgICAgICAgICAgICAgdGhpcy5zY2F0dGVyLnRyYW5zbGF0YWJsZSA9IHRyYW5zbGF0YWJsZVxuICAgICAgICAgICAgICAgIHRoaXMuc2NhdHRlci5yb3RhdGFibGUgPSByb3RhdGFibGVcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmb3JjZTNEOiB0cnVlXG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gU2VlIGh0dHBzOi8vZ3JlZW5zb2NrLmNvbS9mb3J1bXMvdG9waWMvNzk5Ny1yb3RhdGUtdGhlLXNob3J0ZXN0LXdheS9cbiAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMuZWxlbWVudCwgdGhpcy5mbGlwRHVyYXRpb24gLyAyLCB7XG4gICAgICAgICAgICBzY2FsZTogdGFyZ2V0U2NhbGUsXG4gICAgICAgICAgICBlYXNlOiBQb3dlcjEuZWFzZU91dCxcbiAgICAgICAgICAgIHJvdGF0aW9uWjogdGFyZ2V0WiArICdfc2hvcnQnLFxuICAgICAgICAgICAgdHJhbnNmb3JtT3JpZ2luOiAnNTAlIDUwJScsXG4gICAgICAgICAgICB3aWR0aDogdyxcbiAgICAgICAgICAgIGhlaWdodDogaCxcbiAgICAgICAgICAgIHg6IHgsXG4gICAgICAgICAgICB5OiB5LFxuICAgICAgICAgICAgb25Db21wbGV0ZTogZSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuZmxpcHBlZCkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmhpZGUodGhpcy5mcm9udClcbiAgICAgICAgICAgICAgICAgICAgLy8gdGhpcy5oaWRlKHRoaXMuaW5mb0J0bilcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmhpZGUodGhpcy5iYWNrKVxuICAgICAgICAgICAgICAgICAgICAvLyB0aGlzLnNob3codGhpcy5pbmZvQnRuKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICB9XG59XG4iLCJpbXBvcnQge0NhcGFiaWxpdGllc30gZnJvbSAnLi9jYXBhYmlsaXRpZXMuanMnXG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEluZGV4IHtcblxuICAgIGNvbnN0cnVjdG9yKHRlbXBsYXRlLCBwYWdlcywgbm90Zm91bmQ9J3RodW1ibmFpbHMvbm90Zm91bmQucG5nJykge1xuICAgICAgICB0aGlzLnRlbXBsYXRlID0gdGVtcGxhdGVcbiAgICAgICAgdGhpcy5wYWdlcyA9IHBhZ2VzXG4gICAgICAgIHRoaXMubm90Zm91bmQgPSBub3Rmb3VuZFxuICAgIH1cblxuICAgIHNldHVwKCkge1xuICAgICAgICBmb3IobGV0IHBhaXIgb2YgdGhpcy5wYWdlcykge1xuICAgICAgICAgICAgbGV0IFt0aXRsZSwgc3JjXSA9IHBhaXJcbiAgICAgICAgICAgIGxldCBpZCA9IGdldElkKClcbiAgICAgICAgICAgIHBhaXIucHVzaChpZClcbiAgICAgICAgICAgIGxldCB0ID0gdGhpcy50ZW1wbGF0ZVxuICAgICAgICAgICAgbGV0IHdyYXBwZXIgPSB0LmNvbnRlbnQucXVlcnlTZWxlY3RvcignLndyYXBwZXInKVxuICAgICAgICAgICAgd3JhcHBlci5pZCA9IGlkXG4gICAgICAgICAgICBsZXQgY2xvbmUgPSBkb2N1bWVudC5pbXBvcnROb2RlKHQuY29udGVudCwgdHJ1ZSlcbiAgICAgICAgICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZChjbG9uZSlcbiAgICAgICAgICAgIHdyYXBwZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3RvcignIycraWQpXG5cbiAgICAgICAgICAgIGxldCBpY29uID0gd3JhcHBlci5xdWVyeVNlbGVjdG9yKCcuaWNvbicpXG5cbiAgICAgICAgICAgIGljb24ub25lcnJvciA9IChlKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMubm90Zm91bmQpXG4gICAgICAgICAgICAgICAgICAgIGljb24uc3JjID0gdGhpcy5ub3Rmb3VuZFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGV0IGljb25TcmMgPSBzcmMucmVwbGFjZSgnLmh0bWwnLCAnLnBuZycpXG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKFwiaWNvblNyY1wiLCBpY29uU3JjKVxuICAgICAgICAgICAgaWYgKGljb25TcmMuZW5kc1dpdGgoJ2luZGV4LnBuZycpKSB7XG4gICAgICAgICAgICAgICAgaWNvbi5zcmMgPSBpY29uU3JjLnJlcGxhY2UoJ2luZGV4LnBuZycsICd0aHVtYm5haWwucG5nJylcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoaWNvblNyYy5lbmRzV2l0aCgndGVzdC5wbmcnKSkge1xuICAgICAgICAgICAgICAgIGljb24uc3JjID0gaWNvblNyYy5yZXBsYWNlKCd0ZXN0LnBuZycsICd0aHVtYm5haWwudGVzdC5wbmcnKVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpY29uLnNyYyA9ICd0aHVtYm5haWxzLycgKyBpY29uU3JjXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB3cmFwcGVyLmhyZWYgPSBzcmNcbiAgICAgICAgICAgIGxldCB0aXRsZURpdiA9IHdyYXBwZXIucXVlcnlTZWxlY3RvcignLnRpdGxlJylcbiAgICAgICAgICAgIHRpdGxlRGl2LmlubmVyVGV4dCA9IHRpdGxlXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmcmFtZXMoKSB7XG4gICAgICAgIGlmICh0aGlzLnBhZ2VzLmxlbmd0aCA9PSAwKVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIGxldCBbdGl0bGUsIHNyYywgaWRdID0gdGhpcy5wYWdlcy5zaGlmdCgpXG4gICAgICAgIGxldCBpZnJhbWUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpZnJhbWUnKVxuICAgICAgICBpZnJhbWUuZnJhbWVib3JkZXIgPSAwXG4gICAgICAgIGxldCB3cmFwcGVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoaWQpXG4gICAgICAgIGxldCBpY29uID0gd3JhcHBlci5xdWVyeVNlbGVjdG9yKCcuaWNvbicpXG5cbiAgICAgICAgaWNvbi5wYXJlbnROb2RlLnJlcGxhY2VDaGlsZChpZnJhbWUsIGljb24pXG4gICAgICAgIGlmcmFtZS5vbmxvYWQgPSAoZSkgPT4ge1xuICAgICAgICAgICAgdGhpcy5mcmFtZXMoKVxuICAgICAgICB9XG4gICAgICAgIGlmcmFtZS5zcmMgPSBzcmMgKyB3aW5kb3cubG9jYXRpb24uc2VhcmNoXG4gICAgfVxuXG4gICAgbG9hZCgpIHtcbiAgICAgICAgdGhpcy5zZXR1cCgpXG4gICAgICAgIGlmICh3aW5kb3cubG9jYXRpb24uc2VhcmNoLnN0YXJ0c1dpdGgoJz90ZXN0JykpXG4gICAgICAgICAgICB0aGlzLmZyYW1lcygpXG4gICAgfVxuXG4gICAgbG9hZEFuZFRlc3QoKSB7XG4gICAgICAgIHRoaXMuc2V0dXAoKVxuICAgICAgICBpZiAoIUNhcGFiaWxpdGllcy5pc01vYmlsZSlcbiAgICAgICAgICAgIHRoaXMuZnJhbWVzKClcbiAgICB9XG59XG4iLCIvKiogQmFzaWMgY2xhc3MgZm9yIHBvcHBhYmxlIGVsZW1lbnRzIHRoYXQgbmVlZCB0byBiZSBjbG9zZWQgYXMgc29vbiBhcyBvbmUgcG9wcGFibGUgaXNcbiAqIHNob3duLlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBQb3BwYWJsZSB7XG5cbiAgICAvKiogUmVnaXN0ZXIgdGhlIHBvcHBhYmxlIGVsZW1lbnQgaW4gYSBjb250ZXh0LiBDbG9zZXMgcHJldmlvdXNseSByZWdpc3RlcmVkIG9uZXMuXG4gICAgICogQHBhcmFtIHsqfSBjb250ZXh0IFxuICAgICAqL1xuICAgIHJlZ2lzdGVyKGNvbnRleHQpIHtcbiAgICAgICAgbGV0IHJlZ2lzdGVyZWQgPSBQb3BwYWJsZS5nZXQoY29udGV4dClcbiAgICAgICAgaWYgKHJlZ2lzdGVyZWQgIT0gbnVsbCkge1xuICAgICAgICAgICAgcmVnaXN0ZXJlZC5jbG9zZSgpXG4gICAgICAgIH1cbiAgICAgICAgUG9wcGFibGUuc2V0KGNvbnRleHQsIHRoaXMpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVW5yZWdpc3RlciBvYmplY3QgZnJvbSBjb250ZXh0XG4gICAgICogQHBhcmFtIHsqfSBjb250ZXh0IFxuICAgICAqL1xuICAgIHVucmVnaXN0ZXIoY29udGV4dCkge1xuICAgICAgICBQb3BwYWJsZS5kZWxldGUoY29udGV4dClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBnaXZlbiBwb3BwYWJsZSBpbiBhIGNvbnRleHRcbiAgICAgKiBAc3RhdGljXG4gICAgICogQHBhcmFtIHsqfSBjb250ZXh0XG4gICAgICogQHJldHVybnNcbiAgICAgKiBAbWVtYmVyb2YgUG9wcGFibGVcbiAgICAgKi9cbiAgICBzdGF0aWMgZ2V0KGNvbnRleHQpIHtcbiAgICAgICAgcmV0dXJuIFBvcHBhYmxlLnJlZ2lzdHJhdGlvbnMuZ2V0KGNvbnRleHQpXG4gICAgfVxuXG4gICAgLyoqIFNldHMgdGhlIHBvcHBhYmxlIGluIHRoZSBnaXZlbiBjb250ZXh0IFxuICAgICogQHN0YXRpY1xuICAgICogQHBhcmFtIHsqfSBjb250ZXh0XG4gICAgKiBAcGFyYW0geyp9IHBvcHBhYmxlXG4gICAgKiBAcmV0dXJuc1xuICAgICogQG1lbWJlcm9mIFBvcHBhYmxlXG4gICAgKi9cbiAgICBzdGF0aWMgc2V0KGNvbnRleHQsIHBvcHBhYmxlKSB7XG4gICAgICAgIHJldHVybiBQb3BwYWJsZS5yZWdpc3RyYXRpb25zLnNldChjb250ZXh0LCBwb3BwYWJsZSlcbiAgICB9XG5cbiAgICAvKiogVGVzdCB3aGV0aGVyIGEgcG9wcGFibGUgZXhpc3RzIGluIHRoZSBnaXZlbiBjb250ZXh0XG4gICAgICogXG4gICAgICogQHBhcmFtIHsqfSBjb250ZXh0IFxuICAgICAqL1xuICAgIHN0YXRpYyBoYXMoY29udGV4dCkge1xuICAgICAgICByZXR1cm4gUG9wcGFibGUucmVnaXN0cmF0aW9ucy5oYXMoY29udGV4dClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIHRoZSBwb3BwYWJsZSBmcm9tIHRoZSBnaXZlbiBjb250ZXh0LlxuICAgICAqXG4gICAgICogQHN0YXRpY1xuICAgICAqIEBwYXJhbSB7Kn0gY29udGV4dFxuICAgICAqIEBtZW1iZXJvZiBQb3BwYWJsZVxuICAgICAqL1xuICAgIHN0YXRpYyBkZWxldGUoY29udGV4dCkge1xuICAgICAgICBQb3BwYWJsZS5yZWdpc3RyYXRpb25zLmRlbGV0ZShjb250ZXh0KVxuICAgIH1cblxuICAgIC8qKiBBbGwgcG9wcGFibGUgbXVzdCBpbXBsZW1lbnQgYSBjbG9zZSBtZXRob2QuICovXG4gICAgY2xvc2UoKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXCJNdXN0IGJlIGltcGxlbWVudGVkXCIpXG4gICAgfVxufVxuXG5Qb3BwYWJsZS5yZWdpc3RyYXRpb25zID0gbmV3IE1hcCgpIiwiaW1wb3J0IHsgRWxlbWVudHMgfSBmcm9tIFwiLi91dGlscy5qc1wiXG5pbXBvcnQgUG9wcGFibGUgZnJvbSBcIi4vcG9wcGFibGUuanNcIlxuXG4vKiogQSBQb3B1cCB0aGF0IHNob3dzIHRleHQgbGFiZWxzLCBpbWFnZXMsIG9yIGh0bWxcbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgUG9wdXAgZXh0ZW5kcyBQb3BwYWJsZSB7XG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBQb3B1cC5cbiAgICAgKiBAcGFyYW0ge2FueX0gW3tcbiAgICAgKiAgICAgcGFyZW50ID0gbnVsbCwgICAgIC0gVGhlIERPTSBwYXJlbnQgZWxlbWVudC5cbiAgICAgKiAgICAgY29udGVudCA9IG51bGwsICAgIC0gQSBkaWN0IG9iamVjdCB3aXRoIHR5cGUgc3RyaW5ncyAodGV4dCwgaW1nLCBodG1sKSBhcyBrZXlzXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgIGFuZCBjb3JyZXNwb25kaW5nIHZhbHVlcy5cbiAgICAgKiAgICAgY29udGV4dCA9IHdpbmRvdywgICAgLSBBIGNvbnRleHQgb2JqZWN0IGZvciBwb3BwYWJsZSBlbGVtZW50c1xuICAgICAqICAgICBmb250U2l6ZSA9IFwiMWVtXCIsICAtIERlc2NyaWJlcyB0aGUgZm9udCBzaXplIGFzIENTUyB2YWx1ZVxuICAgICAqICAgICBmb250RmFtaWx5ID0gXCJBcmlhbFwiLCAtIERlc2NyaWJlcyB0aGUgZm9udCBmYW1pbHkgYXMgQ1NTIHZhbHVlXG4gICAgICogICAgIHBhZGRpbmcgPSAxNiwgICAgICAtIHtudW1iZXIgfHwgc3RyaW5nfSBwYWRkaW5nIC0gRGVzY3JpYmVzIHRoZSBwYWRkaW5nIGFzIENTUyB2YWx1ZVxuICAgICAqICAgICBub3RjaFNpemUgPSAxMCwgICAgLSB7bnVtYmVyIHx8IHN0cmluZ30gbm90Y2hTaXplIC0gRGVzY3JpYmVzIHRoZSBzaXplIG9mIHRoZSBub3RjaCAoY2FsbG91dCkgYXMgQ1NTIHZhbHVlXG4gICAgICogICAgIHN3aXRjaFBvcyA9IGZhbHNlLFxuICAgICAqICAgICBtaW5XaWR0aCA9IG51bGwsXG4gICAgICogICAgIG1heFdpZHRoID0gODAwLFxuICAgICAqICAgICBiYWNrZ3JvdW5kQ29sb3IgPSBcIiNFRUVcIiwgIC0gVGhlIGNvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kIGFzIENTUyB2YWx1ZVxuICAgICAqICAgICBub3JtYWxDb2xvciA9IFwiIzQ0NFwiLCAtIG5vcm1hbENvbG9yICAtIFRoZSBjb2xvciBvZiB0ZXh0aXRlbXMgYXMgQ1NTIHZhbHVlXG4gICAgICogICAgIG5vdGNoUG9zaXRpb24gPSBcImJvdHRvbUxlZnRcIixcbiAgICAgKiAgICAgekluZGV4ID0gMCxcbiAgICAgKiAgICAga2VlcFdpdGhpbiA9IG51bGwsIC0gRW5zdXJlIHRoYXQgcG9wdXAgaXMgdmlzaWJsZSB3aXRoaW4gdGhlIGJvdW5kcyBvZiB0aGUgZ2l2ZW4gY29udGFpbmVyXG4gICAgICogICAgIGF1dG9DbG9zZSA9IHRydWUsICAtIEF1dG9jbG9zZSB0aGUgUG9wdXAgb24gdGFwXG4gICAgICogICAgIGNsb3NlSWNvbiA9IG51bGwsXG4gICAgICogICAgIHJlc2l6ZUljb24gPSBudWxsLFxuICAgICAqICAgICBjbG9zZUNvbW1hbmQgPSBudWxsLFxuICAgICAqICAgICBkcmFnZ2FibGUgPSBmYWxzZVxuICAgICAqICAgICAgbm9TdHlsZSA9IGZhbHNlIC0gV2hlbiB0cnVlLCBwcmV2ZW50cyB0aGUgcG9wdXAgZnJvbSBkb2luZyBhbnkgYWVzdGhldGljIG1hbmlwdWxhdGlvbnMgdG8gdGhlIERPTSBsZWF2aW5nIHRoZSBzdHlsaW5nIGNvbXBsZXRlbHkgdG8gdGhlIHN0eWxlIHNoZWV0cy5cbiAgICAgKiB9PXt9XVxuICAgICAqIEBtZW1iZXJvZiBQb3B1cFxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKHtcbiAgICAgICAgcGFyZW50ID0gbnVsbCxcbiAgICAgICAgY29udGVudCA9IG51bGwsXG4gICAgICAgIGNvbnRleHQgPSB3aW5kb3csXG4gICAgICAgIGZvbnRTaXplID0gXCIxZW1cIixcbiAgICAgICAgZm9udEZhbWlseSA9IFwiQXJpYWxcIixcbiAgICAgICAgcGFkZGluZyA9IDE2LFxuICAgICAgICBub3RjaFNpemUgPSAxMCxcbiAgICAgICAgc3dpdGNoUG9zID0gZmFsc2UsXG4gICAgICAgIG1pbldpZHRoID0gbnVsbCxcbiAgICAgICAgbWF4V2lkdGggPSA4MDAsXG4gICAgICAgIGJhY2tncm91bmRDb2xvciA9IFwiI0VFRVwiLFxuICAgICAgICBub3JtYWxDb2xvciA9IFwiIzQ0NFwiLFxuICAgICAgICBub3RjaFBvc2l0aW9uID0gXCJib3R0b21DZW50ZXJcIixcbiAgICAgICAgekluZGV4ID0gMCxcbiAgICAgICAga2VlcFdpdGhpbiA9IG51bGwsXG4gICAgICAgIGF1dG9DbG9zZSA9IHRydWUsXG4gICAgICAgIGNsb3NlSWNvbiA9IG51bGwsXG4gICAgICAgIHJlc2l6ZUljb24gPSBudWxsLFxuICAgICAgICBjbG9zZUNvbW1hbmQgPSBudWxsLFxuICAgICAgICBkcmFnZ2FibGUgPSBmYWxzZSxcbiAgICAgICAgcG9zT2Zmc2V0ID0gMCxcbiAgICAgICAgdGFyZ2V0Qm91bmRpbmdCb3ggPSBudWxsLFxuICAgICAgICB1c2VFdmVudFBvc1dpdGhCb3VuZGluZ0JveCA9IGZhbHNlLFxuICAgICAgICBpbnRlcmFjdGl2ZSA9IGZhbHNlLFxuICAgICAgICBvblJlc2l6ZSA9IG51bGwsXG4gICAgICAgIG9uTW92ZSA9IG51bGwsXG4gICAgICAgIG5vU3R5bGUgPSBmYWxzZSxcbiAgICAgICAgaGlkZU9uVXAgPSB0cnVlLFxuICAgIH0gPSB7fSkge1xuICAgICAgICBzdXBlcigpXG4gICAgICAgIHRoaXMuY29udGV4dCA9IGNvbnRleHRcbiAgICAgICAgdGhpcy5ub1N0eWxlID0gbm9TdHlsZVxuICAgICAgICB0aGlzLmhpZGVPblVwID0gaGlkZU9uVXBcbiAgICAgICAgdGhpcy5wYWRkaW5nID0gcGFkZGluZ1xuICAgICAgICB0aGlzLm5vdGNoUG9zaXRpb24gPSBub3RjaFBvc2l0aW9uXG4gICAgICAgIHRoaXMubm90Y2hTaXplID0gbm90Y2hTaXplXG4gICAgICAgIHRoaXMuc3dpdGNoUG9zID0gc3dpdGNoUG9zXG4gICAgICAgIHRoaXMuZm9udFNpemUgPSBmb250U2l6ZVxuICAgICAgICB0aGlzLmZvbnRGYW1pbHkgPSBmb250RmFtaWx5XG4gICAgICAgIHRoaXMubWluV2lkdGggPSBtaW5XaWR0aFxuICAgICAgICB0aGlzLm1heFdpZHRoID0gbWF4V2lkdGhcbiAgICAgICAgdGhpcy5ub3JtYWxDb2xvciA9IG5vcm1hbENvbG9yXG4gICAgICAgIHRoaXMuYmFja2dyb3VuZENvbG9yID0gYmFja2dyb3VuZENvbG9yXG4gICAgICAgIHRoaXMua2VlcFdpdGhpbiA9IGtlZXBXaXRoaW5cbiAgICAgICAgdGhpcy5hdXRvQ2xvc2UgPSBhdXRvQ2xvc2VcbiAgICAgICAgdGhpcy5yZXNpemVJY29uID0gcmVzaXplSWNvblxuICAgICAgICB0aGlzLmNsb3NlSWNvbiA9IGNsb3NlSWNvblxuICAgICAgICB0aGlzLmNsb3NlQ29tbWFuZCA9IGNsb3NlQ29tbWFuZFxuICAgICAgICB0aGlzLnpJbmRleCA9IHpJbmRleFxuICAgICAgICB0aGlzLnBhcmVudCA9IHBhcmVudCB8fCBkb2N1bWVudC5ib2R5XG4gICAgICAgIHRoaXMuZHJhZ2dhYmxlID0gZHJhZ2dhYmxlXG4gICAgICAgIHRoaXMucG9zT2Zmc2V0ID0gcG9zT2Zmc2V0XG4gICAgICAgIHRoaXMudGFyZ2V0Qm91bmRpbmdCb3ggPSB0YXJnZXRCb3VuZGluZ0JveFxuICAgICAgICB0aGlzLnVzZUV2ZW50UG9zV2l0aEJvdW5kaW5nQm94ID0gdXNlRXZlbnRQb3NXaXRoQm91bmRpbmdCb3hcbiAgICAgICAgdGhpcy5jdXJyZW50UG9zID0gbnVsbFxuICAgICAgICB0aGlzLmluc2VydGVkTm9kZSA9IG51bGxcbiAgICAgICAgdGhpcy5sb2FkZWQgPSBmYWxzZVxuICAgICAgICB0aGlzLmludGVyYWN0aXZlID0gaW50ZXJhY3RpdmVcbiAgICAgICAgdGhpcy5vbmxvYWQgPSBudWxsXG4gICAgICAgIHRoaXMub25SZXNpemUgPSBvblJlc2l6ZVxuICAgICAgICB0aGlzLm9uTW92ZSA9IG9uTW92ZVxuICAgICAgICBpZiAoY29udGVudCkge1xuICAgICAgICAgICAgdGhpcy5zaG93KGNvbnRlbnQpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogU2V0dXAgcG9wdXAgd2l0aCBhIGRpY3Rpb25hcnkgb2YgY29udGVudCB0eXBlcyBhbmQgY29udGVudHMuXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGNvbnRlbnQgLSBBIGRpY3Qgb2JqZWN0IHdpdGggdHlwZSBzdHJpbmdzICh0ZXh0LCBpbWcsIGh0bWwpIGFzIGtleXNcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgY29ycmVzcG9uZGluZyB2YWx1ZXMuXG4gICAgICogQHJldHVybiB7UG9wdXB9IHRoaXNcbiAgICAgKi9cbiAgICBzZXR1cChjb250ZW50KSB7XG4gICAgICAgIC8vY29uc29sZS5sb2coXCJQb3B1cC5zZXR1cFwiLCB0aGlzLmRyYWdnYWJsZSlcbiAgICAgICAgdGhpcy5jb250ZW50ID0gY29udGVudFxuICAgICAgICB0aGlzLml0ZW1zID0ge31cbiAgICAgICAgdGhpcy5lbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKVxuICAgICAgICB0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LmFkZChcInBvcHVwXCIpXG4gICAgICAgIHRoaXMuc2V0QWxwaGEodGhpcy5lbGVtZW50LCAwKVxuICAgICAgICAvLyB0aGlzLmVsZW1lbnQuc3R5bGUub3BhY2l0eSA9IDBcbiAgICAgICAgRWxlbWVudHMuYWRkQ2xhc3ModGhpcy5lbGVtZW50LCBcInVuc2VsZWN0YWJsZVwiKVxuICAgICAgICB0aGlzLm5vdGNoID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKVxuICAgICAgICBFbGVtZW50cy5zZXRTdHlsZSh0aGlzLm5vdGNoLCB0aGlzLm5vdGNoU3R5bGUoKSlcbiAgICAgICAgdGhpcy5ub3RjaC5jbGFzc05hbWUgPSBcIm5vdGNoXCJcbiAgICAgICAgdGhpcy5zZXR1cERyYWdnYWJsZSgpXG4gICAgICAgIGlmICh0aGlzLmNsb3NlSWNvbikge1xuICAgICAgICAgICAgbGV0IGltZyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJpbWdcIilcbiAgICAgICAgICAgIGltZy5zZXRBdHRyaWJ1dGUoXCJkcmFnZ2FibGVcIiwgZmFsc2UpXG4gICAgICAgICAgICBpbWcuc3JjID0gdGhpcy5jbG9zZUljb25cbiAgICAgICAgICAgIGltZy5zdHlsZS5wb3NpdGlvbiA9IFwiYWJzb2x1dGVcIlxuICAgICAgICAgICAgaW1nLnN0eWxlLnJpZ2h0ID0gXCIwcHhcIlxuICAgICAgICAgICAgaW1nLnN0eWxlLnRvcCA9IFwiMHB4XCJcbiAgICAgICAgICAgIGltZy5zdHlsZS53aWR0aCA9IFwiMTZweFwiXG4gICAgICAgICAgICBpbWcuc3R5bGUuaGVpZ2h0ID0gXCIxNnB4XCJcbiAgICAgICAgICAgIGltZy5vbmNsaWNrID0gZSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5jbG9zZSgpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmVsZW1lbnQuYXBwZW5kQ2hpbGQoaW1nKVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnJlc2l6ZUljb24pIHtcbiAgICAgICAgICAgIGxldCBpbWcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiaW1nXCIpXG4gICAgICAgICAgICBpbWcuc3R5bGUucG9zaXRpb24gPSBcImFic29sdXRlXCJcbiAgICAgICAgICAgIGltZy5zdHlsZS5yaWdodCA9IFwiMHB4XCJcbiAgICAgICAgICAgIGltZy5zdHlsZS5ib3R0b20gPSBcIjBweFwiXG4gICAgICAgICAgICBpbWcuc3R5bGUud2lkdGggPSBcIjE2cHhcIlxuICAgICAgICAgICAgaW1nLnN0eWxlLmhlaWdodCA9IFwiMTZweFwiXG4gICAgICAgICAgICBpbWcuc3JjID0gdGhpcy5yZXNpemVJY29uXG4gICAgICAgICAgICBpbWcuc2V0QXR0cmlidXRlKFwiZHJhZ2dhYmxlXCIsIHRydWUpXG4gICAgICAgICAgICBpbWcub25kcmFnc3RhcnQgPSBlID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRQb3MgPSB7IHg6IGUuY2xpZW50WCwgeTogZS5jbGllbnRZIH1cbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaW1nLm9uZHJhZyA9IGUgPT4ge1xuICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKVxuXG4gICAgICAgICAgICAgICAgbGV0IHRhcmdldCA9IHRoaXMuZWxlbWVudC5xdWVyeVNlbGVjdG9yKFwiaWZyYW1lXCIpIHx8IHRoaXMuZWxlbWVudFxuICAgICAgICAgICAgICAgIGxldCBkZWx0YSA9IHtcbiAgICAgICAgICAgICAgICAgICAgeDogZS5jbGllbnRYIC0gdGhpcy5jdXJyZW50UG9zLngsXG4gICAgICAgICAgICAgICAgICAgIHk6IGUuY2xpZW50WSAtIHRoaXMuY3VycmVudFBvcy55XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdGhpcy5jdXJyZW50UG9zID0geyB4OiBlLmNsaWVudFgsIHk6IGUuY2xpZW50WSB9XG4gICAgICAgICAgICAgICAgaWYgKGRlbHRhLnggPT0gMCAmJiBkZWx0YS55ID09IDApXG4gICAgICAgICAgICAgICAgICAgIHJldHVyblxuXG4gICAgICAgICAgICAgICAgbGV0IHJlY3QgPSB0YXJnZXQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICAgICAgICAgICAgICBsZXQgd2lkdGggPSByZWN0LndpZHRoICsgZGVsdGEueFxuICAgICAgICAgICAgICAgIGxldCBoZWlnaHQgPSByZWN0LmhlaWdodCArIGRlbHRhLnlcbiAgICAgICAgICAgICAgICB0YXJnZXQuc3R5bGUud2lkdGggPSB3aWR0aCArIFwicHhcIlxuICAgICAgICAgICAgICAgIHRhcmdldC5zdHlsZS5oZWlnaHQgPSBoZWlnaHQgKyBcInB4XCJcblxuICAgICAgICAgICAgICAgIHN3aXRjaCAodGhpcy5ub3RjaFBvc2l0aW9uKSB7XG4gICAgICAgICAgICAgICAgICAgIGNhc2UgXCJib3R0b21MZWZ0XCI6XG4gICAgICAgICAgICAgICAgICAgIGNhc2UgXCJib3R0b21DZW50ZXJcIjpcbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBib3R0b20gPSBwYXJzZUZsb2F0KHRoaXMuZWxlbWVudC5zdHlsZS5ib3R0b20pXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVsZW1lbnQuc3R5bGUuYm90dG9tID0gYm90dG9tIC0gZGVsdGEueSArIFwicHhcIlxuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCJvblJlc2l6ZVwiLCB0aGlzLm9uUmVzaXplKVxuICAgICAgICAgICAgICAgIGlmICh0aGlzLm9uUmVzaXplKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMub25SZXNpemUoeyB0YXJnZXQsIGRlbHRhLCB3aWR0aCwgaGVpZ2h0IH0pXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaW1nLm9uZHJhZ2VuZCA9IGUgPT4geyB9XG4gICAgICAgICAgICB0aGlzLmVsZW1lbnQuYXBwZW5kQ2hpbGQoaW1nKVxuICAgICAgICB9XG5cblxuICAgICAgICBmb3IgKGxldCBrZXkgaW4gY29udGVudCkge1xuICAgICAgICAgICAgc3dpdGNoIChrZXkpIHtcbiAgICAgICAgICAgICAgICBjYXNlIFwic2VsZWN0b3JcIjpcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICBjYXNlIFwidGV4dFwiOlxuICAgICAgICAgICAgICAgICAgICBsZXQgdGV4dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJzcGFuXCIpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZWxlbWVudC5hcHBlbmRDaGlsZCh0ZXh0KVxuICAgICAgICAgICAgICAgICAgICB0ZXh0LmlubmVySFRNTCA9IGNvbnRlbnRba2V5XVxuICAgICAgICAgICAgICAgICAgICBFbGVtZW50cy5zZXRTdHlsZSh0ZXh0LCB7IGNvbG9yOiB0aGlzLm5vcm1hbENvbG9yIH0pXG4gICAgICAgICAgICAgICAgICAgIEVsZW1lbnRzLmFkZENsYXNzKHRleHQsIFwidW5zZWxlY3RhYmxlXCIpXG4gICAgICAgICAgICAgICAgICAgIEVsZW1lbnRzLmFkZENsYXNzKHRleHQsIFwiUG9wdXBDb250ZW50XCIpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaW5zZXJ0ZWROb2RlID0gdGV4dFxuICAgICAgICAgICAgICAgICAgICB0aGlzLmxvYWRlZCA9IHRydWVcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICBjYXNlIFwiaW1nXCI6XG4gICAgICAgICAgICAgICAgICAgIGFsZXJ0KFwiaW1nIHRvIGJlIGltcGxlbWVudGVkXCIpXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgY2FzZSBcImlmcmFtZVwiOlxuICAgICAgICAgICAgICAgICAgICBsZXQgaWZyYW1lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImlmcmFtZVwiKVxuICAgICAgICAgICAgICAgICAgICBpZnJhbWUuc2V0QXR0cmlidXRlKFwiZnJhbWVCb3JkZXJcIiwgMClcbiAgICAgICAgICAgICAgICAgICAgaWZyYW1lLnNyYyA9IGNvbnRlbnRba2V5XVxuICAgICAgICAgICAgICAgICAgICBpZnJhbWUub25sb2FkID0gZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgYm9keSA9IGlmcmFtZS5jb250ZW50V2luZG93LmRvY3VtZW50LmJvZHlcbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBvYnNlcnZlciA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmlmcmFtZUNoYW5nZWQoaWZyYW1lKVxuICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLm9ic2VydmUoaWZyYW1lLmNvbnRlbnRXaW5kb3cuZG9jdW1lbnQsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdHRyaWJ1dGVzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRyZWU6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hpbGRMaXN0OiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoYXJhY3RlckRhdGE6IHRydWVcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgdyA9IE1hdGgubWF4KGJvZHkuc2Nyb2xsV2lkdGgsIGJvZHkub2Zmc2V0V2lkdGgpXG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgaCA9IE1hdGgubWF4KGJvZHkuc2Nyb2xsSGVpZ2h0LCBib2R5Lm9mZnNldEhlaWdodClcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmcmFtZS5zdHlsZS53aWR0aCA9IHcgKyBcInB4XCJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmcmFtZS5zdHlsZS5oZWlnaHQgPSBoICsgXCJweFwiXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxheW91dEFmdGVySW5zZXJ0KClcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm9ubG9hZCAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vbmxvYWQoKVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2FkZWQgPSB0cnVlXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5lbGVtZW50LmFwcGVuZENoaWxkKGlmcmFtZSlcbiAgICAgICAgICAgICAgICAgICAgRWxlbWVudHMuYWRkQ2xhc3MoaWZyYW1lLCBcIlBvcHVwQ29udGVudFwiKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmluc2VydEludG9ET00oKVxuICAgICAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgICAgICBjYXNlIFwiaHRtbFwiOlxuICAgICAgICAgICAgICAgICAgICB0aGlzLmxvYWRlZCA9IGZhbHNlXG4gICAgICAgICAgICAgICAgICAgIGxldCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpXG4gICAgICAgICAgICAgICAgICAgIEVsZW1lbnRzLmFkZENsYXNzKGRpdiwgXCJQb3B1cENvbnRlbnRcIilcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5lbGVtZW50LmFwcGVuZENoaWxkKGRpdilcbiAgICAgICAgICAgICAgICAgICAgZGl2LmlubmVySFRNTCA9IGNvbnRlbnQuaHRtbFxuICAgICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKFwiaW5zZXJ0XCIsIGNvbnRlbnQpXG4gICAgICAgICAgICAgICAgICAgIGxldCBzZWxlY3RvciA9IGNvbnRlbnQuc2VsZWN0b3JcbiAgICAgICAgICAgICAgICAgICAgaWYgKHNlbGVjdG9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluc2VydGVkTm9kZSA9IGRpdi5xdWVyeVNlbGVjdG9yKHNlbGVjdG9yKVxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuaW5zZXJ0ZWROb2RlID09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXYuaW5uZXJIVE1MID0gYDxwIHN0eWxlPVwiY29sb3I6cmVkO1wiPlBvcHVwIGNvbnRlbnQgbm90IGZvdW5kLiBNaXNzaW5nICR7c2VsZWN0b3J9PC9wPmBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmluc2VydGVkTm9kZSA9IGRpdi5maXJzdEVsZW1lbnRDaGlsZFxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5pbnNlcnRlZE5vZGUgPSBkaXYuZmlyc3RFbGVtZW50Q2hpbGQgfHwgZGl2XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXRBbHBoYSh0aGlzLmluc2VydGVkTm9kZSwgMClcbiAgICAgICAgICAgICAgICAgICAgbGV0IGltYWdlcyA9IHRoaXMuZWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKCdpbWcnKVxuICAgICAgICAgICAgICAgICAgICBsZXQgdG90YWwgPSAwXG4gICAgICAgICAgICAgICAgICAgIGlmIChpbWFnZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGNvdW50ID0gMFxuICAgICAgICAgICAgICAgICAgICAgICAgZm9yIChsZXQgaW1hZ2Ugb2YgaW1hZ2VzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFpbWFnZS5jb21wbGV0ZSAmJiAhaW1hZ2Uuc3JjLnN0YXJ0c1dpdGgoJ2RhdGE6JykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG90YWwgKz0gMVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcImltYWdlIG5vdCBjb21wbGV0ZVwiLCBpbWFnZS5zcmMpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGltYWdlLm9ubG9hZCA9IGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnQgKz0gMVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNvdW50ID09IHRvdGFsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2FkZWQgPSB0cnVlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMub25sb2FkICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vbmxvYWQoKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZiAodG90YWwgPT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2FkZWQgPSB0cnVlXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICBjYXNlIFwibm9kZVwiOlxuICAgICAgICAgICAgICAgICAgICB0aGlzLmxvYWRlZCA9IHRydWVcbiAgICAgICAgICAgICAgICAgICAgRWxlbWVudHMuYWRkQ2xhc3MoY29udGVudC5ub2RlLCBcIlBvcHVwQ29udGVudFwiKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmVsZW1lbnQuYXBwZW5kQ2hpbGQoY29udGVudC5ub2RlKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmluc2VydGVkTm9kZSA9IGNvbnRlbnQubm9kZVxuICAgICAgICAgICAgICAgICAgICB0aGlzLnNldEFscGhhKHRoaXMuaW5zZXJ0ZWROb2RlLCAwKVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgIGFsZXJ0KFwiVW5leHBlY3RlZCBjb250ZW50IHR5cGU6IFwiICsga2V5KVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuaW5zZXJ0SW50b0RPTSgpXG4gICAgICAgIHRoaXMubGF5b3V0QWZ0ZXJJbnNlcnQoKVxuICAgICAgICB0aGlzLnNldHVwRXZlbnRIYW5kbGVyKClcbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICBoYW5kbGVDbG9zZShlKSB7XG4gICAgICAgIGxldCBjbG9zaW5nID0gdGhpcy5jbG9zaW5nRXZlbnQoZSlcbiAgICAgICAgaWYgKGNsb3NpbmcpIHtcbiAgICAgICAgICAgIHRoaXMuY2xvc2UoKVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5zZXR1cENsb3NlSGFuZGxlcigpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzZXR1cENsb3NlSGFuZGxlcigpIHtcbiAgICAgICAgbGV0IGNsb3NlID0gdGhpcy5oYW5kbGVDbG9zZVxuICAgICAgICBpZiAodGhpcy5oaWRlT25VcCkge1xuICAgICAgICAgICAgaWYgKHdpbmRvdy5Qb2ludGVyRXZlbnQpXG4gICAgICAgICAgICAgICAgdGhpcy5wYXJlbnQuYWRkRXZlbnRMaXN0ZW5lcihcInBvaW50ZXJ1cFwiLCBjbG9zZS5iaW5kKHRoaXMpLCB7IGNhcHR1cmU6IHRydWUsIG9uY2U6IHRydWUgfSlcbiAgICAgICAgICAgIGVsc2UgaWYgKHdpbmRvdy5Ub3VjaEV2ZW50KVxuICAgICAgICAgICAgICAgIHRoaXMucGFyZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJ0b3VjaGVuZFwiLCBjbG9zZS5iaW5kKHRoaXMpLCB7IGNhcHR1cmU6IHRydWUsIG9uY2U6IHRydWUgfSlcbiAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICB0aGlzLnBhcmVudC5hZGRFdmVudExpc3RlbmVyKFwibW91c2V1cFwiLCBjbG9zZS5iaW5kKHRoaXMpLCB7IGNhcHR1cmU6IHRydWUsIG9uY2U6IHRydWUgfSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICh3aW5kb3cuUG9pbnRlckV2ZW50KVxuICAgICAgICAgICAgICAgIHRoaXMucGFyZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJwb2ludGVyZG93blwiLCBjbG9zZS5iaW5kKHRoaXMpLCB7IGNhcHR1cmU6IHRydWUsIG9uY2U6IHRydWUgfSlcbiAgICAgICAgICAgIGVsc2UgaWYgKHdpbmRvdy5Ub3VjaEV2ZW50KVxuICAgICAgICAgICAgICAgIHRoaXMucGFyZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJ0b3VjaHN0YXJ0XCIsIGNsb3NlLmJpbmQodGhpcyksIHsgY2FwdHVyZTogdHJ1ZSwgb25jZTogdHJ1ZSB9KVxuICAgICAgICAgICAgZWxzZVxuICAgICAgICAgICAgICAgIHRoaXMucGFyZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJtb3VzZWRvd25cIiwgY2xvc2UuYmluZCh0aGlzKSwgeyBjYXB0dXJlOiB0cnVlLCBvbmNlOiB0cnVlIH0pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzZXR1cEV2ZW50SGFuZGxlcigpIHtcbiAgICAgICAgaWYgKHRoaXMuYXV0b0Nsb3NlKSB7XG4gICAgICAgICAgICB0aGlzLnNldHVwQ2xvc2VIYW5kbGVyKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGNsb3NpbmdFdmVudChlKSB7XG4gICAgICAgIGlmICh0aGlzLmludGVyYWN0aXZlKSB7XG4gICAgICAgICAgICBsZXQgbm9kZSA9IGUudGFyZ2V0LmNsb3Nlc3QoXCIuUG9wdXBDb250ZW50XCIpXG4gICAgICAgICAgICByZXR1cm4gbm9kZSA9PSBudWxsXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG5cbiAgICBpZnJhbWVDaGFuZ2VkKGlmcmFtZSkge1xuICAgICAgICBsZXQgYm9keSA9IGlmcmFtZS5jb250ZW50V2luZG93LmRvY3VtZW50LmJvZHlcbiAgICAgICAgbGV0IHcgPSBNYXRoLm1heChib2R5LnNjcm9sbFdpZHRoLCBib2R5Lm9mZnNldFdpZHRoKVxuICAgICAgICBsZXQgaCA9IE1hdGgubWF4KGJvZHkuc2Nyb2xsSGVpZ2h0LCBib2R5Lm9mZnNldEhlaWdodClcbiAgICAgICAgaWZyYW1lLnN0eWxlLndpZHRoID0gdyArIFwicHhcIlxuICAgICAgICBpZnJhbWUuc3R5bGUuaGVpZ2h0ID0gaCArIFwicHhcIlxuICAgIH1cblxuICAgIHNldHVwRHJhZ2dhYmxlKCkge1xuICAgICAgICBpZiAodGhpcy5kcmFnZ2FibGUpIHtcbiAgICAgICAgICAgIGxldCB0YXJnZXQgPSB0aGlzLmVsZW1lbnRcbiAgICAgICAgICAgIHRhcmdldC5zZXRBdHRyaWJ1dGUoXCJkcmFnZ2FibGVcIiwgdHJ1ZSlcbiAgICAgICAgICAgIHRhcmdldC5vbmRyYWdzdGFydCA9IGUgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuY3VycmVudFBvcyA9IHsgeDogZS5jbGllbnRYLCB5OiBlLmNsaWVudFkgfVxuICAgICAgICAgICAgICAgIHZhciBpbWcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKVxuICAgICAgICAgICAgICAgIGltZy5zcmMgPSAnZGF0YTppbWFnZS9naWZiYXNlNjQsUjBsR09EbGhBUUFCQUlBQUFBQUFBUC8vL3lINUJBRUFBQUFBTEFBQUFBQUJBQUVBQUFJQlJBQTcnXG4gICAgICAgICAgICAgICAgZS5kYXRhVHJhbnNmZXIuc2V0RHJhZ0ltYWdlKGltZywgMCwgMClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRhcmdldC5vbmRyYWcgPSBlID0+IHtcbiAgICAgICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgICAgICAgICBsZXQgZGVsdGEgPSB7XG4gICAgICAgICAgICAgICAgICAgIHg6IGUuY2xpZW50WCAtIHRoaXMuY3VycmVudFBvcy54LFxuICAgICAgICAgICAgICAgICAgICB5OiBlLmNsaWVudFkgLSB0aGlzLmN1cnJlbnRQb3MueVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRQb3MgPSB7IHg6IGUuY2xpZW50WCwgeTogZS5jbGllbnRZIH1cbiAgICAgICAgICAgICAgICBsZXQgbGVmdCA9IHBhcnNlRmxvYXQodGFyZ2V0LnN0eWxlLmxlZnQpXG4gICAgICAgICAgICAgICAgbGV0IHRvcCA9IHBhcnNlRmxvYXQodGFyZ2V0LnN0eWxlLnRvcClcblxuICAgICAgICAgICAgICAgIHRhcmdldC5zdHlsZS5sZWZ0ID0gbGVmdCArIGRlbHRhLnggKyAncHgnXG4gICAgICAgICAgICAgICAgdGFyZ2V0LnN0eWxlLnRvcCA9IHRvcCArIGRlbHRhLnkgKyAncHgnXG5cbiAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKFwiUG9wdXAub25kcmFnXCIsIHRhcmdldCwgZXZlbnQudGFyZ2V0KVxuICAgICAgICAgICAgICAgIGlmICh0aGlzLm9uTW92ZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLm9uTW92ZSh7IHRhcmdldCwgZGVsdGEgfSlcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLmxhc3REcmFnID0geyBsZWZ0LCB0b3AgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGFyZ2V0Lm9uZHJhZ2VuZCA9IGUgPT4ge1xuICAgICAgICAgICAgICAgIHRhcmdldC5zdHlsZS5sZWZ0ID0gdGhpcy5sYXN0RHJhZy5sZWZ0ICsgJ3B4J1xuICAgICAgICAgICAgICAgIHRhcmdldC5zdHlsZS50b3AgPSB0aGlzLmxhc3REcmFnLnRvcCArICdweCdcbiAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRQb3MgPSBudWxsXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBtb3ZlRHJhZ2dlZCh0YXJnZXQpIHtcblxuICAgIH1cblxuICAgIGluc2VydEludG9ET00obGF5b3V0ID0gdHJ1ZSkge1xuICAgICAgICB0aGlzLnNldEFscGhhKHRoaXMuaW5zZXJ0ZWROb2RlLCAwKVxuICAgICAgICB0aGlzLmVsZW1lbnQuYXBwZW5kQ2hpbGQodGhpcy5ub3RjaClcbiAgICAgICAgdGhpcy5wYXJlbnQuYXBwZW5kQ2hpbGQodGhpcy5lbGVtZW50KVxuICAgIH1cblxuICAgIGxheW91dEFmdGVySW5zZXJ0KCkge1xuICAgICAgICBFbGVtZW50cy5zZXRTdHlsZSh0aGlzLmVsZW1lbnQsIHRoaXMuZGVmYXVsdFN0eWxlKCkpXG4gICAgICAgIHRoaXMubGF5b3V0KClcbiAgICAgICAgLy90aGlzLmVsZW1lbnQuc3R5bGUub3BhY2l0eSA9IDFcbiAgICB9XG5cbiAgICAvKiogTGF5b3V0IHRoZSBtZW51IGl0ZW1zLiBOZWVkZWQgb25seSBpbiB0aGUgc3ViY2xhc3MuXG4gICAgICovXG4gICAgbGF5b3V0KCkgeyB9XG5cblxuICAgIHJlbW92ZSgpIHtcbiAgICAgICAgaWYgKHRoaXMucGFyZW50LmNvbnRhaW5zKHRoaXMuZWxlbWVudCkpXG4gICAgICAgICAgICB0aGlzLnBhcmVudC5yZW1vdmVDaGlsZCh0aGlzLmVsZW1lbnQpXG4gICAgICAgIHRoaXMudW5yZWdpc3Rlcih0aGlzLmNvbnRleHQpXG4gICAgfVxuXG4gICAgLyoqIENsb3NlIGFuZCByZW1vdmUgdGhlIFBvcHVwIGZyb20gdGhlIERPTSB0cmVlLlxuICAgICAqL1xuICAgIGNsb3NlKCkge1xuICAgICAgICAvL2NvbnNvbGUubG9nKFwiUG9wdXAuY2xvc2VcIiwgdGhpcy5jbG9zZUNvbW1hbmQpXG4gICAgICAgIHRoaXMudW5yZWdpc3Rlcih0aGlzLmNvbnRleHQpXG4gICAgICAgIGlmICh0aGlzLmNsb3NlQ29tbWFuZCkge1xuICAgICAgICAgICAgdGhpcy5jbG9zZUNvbW1hbmQodGhpcywgKCkgPT4gdGhpcy5yZW1vdmUoKSlcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMucmVtb3ZlKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldCB0aGUgYWxwaGEgdmFsdWUgdG8gc2hvdyBvciBoaWRlIHRoZSBwb3B1cC4gVXNlcyBDU1MgdHJhbnNpdGlvbnMuXG4gICAgICogKEEgZm9ybWVyIGRlcGVuZGVuY3kgb24gVHdlZW5MaXRlIGhhcyBiZWVlbiByZW1vdmVkLilcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7Kn0gdGFyZ2V0c1xuICAgICAqIEBwYXJhbSB7Kn0gdmFsdWVcbiAgICAgKiBAbWVtYmVyb2YgUG9wdXBcbiAgICAgKi9cbiAgICBzZXRBbHBoYSh0YXJnZXRzLCB2YWx1ZSkge1xuICAgICAgICBsZXQgb2JqcyA9ICh0YXJnZXRzIGluc3RhbmNlb2YgQXJyYXkpID8gdGFyZ2V0cyA6IFt0YXJnZXRzXVxuICAgICAgICBmb3IgKGxldCBvYmogb2Ygb2Jqcykge1xuICAgICAgICAgICAgaWYgKHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgb2JqLnN0eWxlLnRyYW5zaXRpb24gPSBcIm9wYWNpdHkgMC4ycyBlYXNlLWluXCJcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG9iai5zdHlsZS5vcGFjaXR5ID0gdmFsdWVcbiAgICAgICAgfVxuICAgICAgICAvLyBpZiAodmFsdWUpIHtcbiAgICAgICAgLy8gICAgIFR3ZWVuTGl0ZS50byh0YXJnZXRzLCAwLjIsICB7IGF1dG9BbHBoYTogdmFsdWUgfSlcbiAgICAgICAgLy8gfVxuICAgICAgICAvLyBlbHNlIHtcbiAgICAgICAgLy8gICAgIFR3ZWVuTGl0ZS5zZXQodGFyZ2V0cywgeyBhdXRvQWxwaGE6IDAgfSlcbiAgICAgICAgLy8gfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFN0YXJ0cyBhIGZhZGUgaW4gYW5pbWF0aW9uLlxuICAgICAqXG4gICAgICogQG1lbWJlcm9mIFBvcHVwXG4gICAgICovXG4gICAgZmFkZUluKCkge1xuICAgICAgICB0aGlzLnNldEFscGhhKFt0aGlzLmVsZW1lbnQsIHRoaXMuaW5zZXJ0ZWROb2RlXSwgMSlcbiAgICB9XG5cbiAgICAvKiogU2hvd3MgdGhlIFBvcHVwIHdpdGggdGhlIGdpdmVuIGNvbW1hbmRzIGF0IHRoZSBzcGVjaWZpZWQgcG9pbnQuXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGNvbnRlbnQgLSBBIGRpY3Qgb2JqZWN0IHdpdGggdHlwZSBzdHJpbmdzICh0ZXh0LCBpbWcsIGh0bWwpIGFzIGtleXNcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgY29ycmVzcG9uZGluZyB2YWx1ZXMuXG4gICAgICogQHBhcmFtIHtQb2ludH0gcG9pbnQgLSBUaGUgcG9zaXRpb24gYXMgeCwgeSBjb29yZGluYXRlcyB7cHh9LlxuICAgICAqIEByZXR1cm4ge1BvcHVwfSB0aGlzXG4gICAgICovXG4gICAgc2hvd0F0KGNvbnRlbnQsIHBvaW50KSB7XG4gICAgICAgIHRoaXMuc2V0dXAoY29udGVudClcbiAgICAgICAgY29uc29sZS5sb2coXCJzaG93QXRcIiwgdGhpcy5sb2FkZWQpXG4gICAgICAgIGlmICh0aGlzLmxvYWRlZCkge1xuICAgICAgICAgICAgdGhpcy5wbGFjZUF0KHBvaW50KVxuICAgICAgICAgICAgdGhpcy5mYWRlSW4oKVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5zZXRBbHBoYShbdGhpcy5lbGVtZW50LCB0aGlzLmluc2VydGVkTm9kZV0sIDApXG4gICAgICAgICAgICB0aGlzLm9ubG9hZCA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmxheW91dEFmdGVySW5zZXJ0KClcbiAgICAgICAgICAgICAgICB0aGlzLnBsYWNlQXQocG9pbnQpXG4gICAgICAgICAgICAgICAgdGhpcy5mYWRlSW4oKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogIFBsYWNlIHRoZSBvcmlnaW4sIGkuZS4gdGhlIHVwcGVyIGxlZnQgY29ybmVyIGF0IHRoZSBnaXZlbiBwb3NpdGlvbiB1c2luZyBDU1Mgc3R5bGVzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHthbnl9IHhcbiAgICAgKiBAcGFyYW0ge2FueX0geVxuICAgICAqIEBtZW1iZXJvZiBQb3B1cFxuICAgICAqL1xuICAgIHBsYWNlT3JpZ2luKHgsIHkpIHtcbiAgICAgICAgRWxlbWVudHMuc2V0U3R5bGUodGhpcy5lbGVtZW50LCB7IGxlZnQ6IHggKyBcInB4XCIsIHRvcDogeSArIFwicHhcIiB9KVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZSB0aGUgbG9jYWwgY29vcmRpbmF0ZXMgd2l0aGluIHRoZSBrZWVwV2l0aGluIGNvbnRhaW5lci5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7YW55fSB4XG4gICAgICogQHBhcmFtIHthbnl9IHlcbiAgICAgKiBAcmV0dXJuc1xuICAgICAqIEBtZW1iZXJvZiBQb3B1cFxuICAgICAqL1xuICAgIGxvY2FsUG9pbnRXaXRoaW4oeCwgeSwgd2lkdGgsIGhlaWdodCkge1xuICAgICAgICBsZXQgcHQgPSB7IHgsIHkgfVxuICAgICAgICByZXR1cm4gcHRcbiAgICB9XG5cbiAgICB3aXRoaW5EaW1lbnNpb25zKCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgd2lkdGg6IHRoaXMua2VlcFdpdGhpbi5vZmZzZXRXaWR0aCxcbiAgICAgICAgICAgIGhlaWdodDogdGhpcy5rZWVwV2l0aGluLm9mZnNldEhlaWdodFxuICAgICAgICB9XG4gICAgfVxuXG4gICAgbG9jYWxEaW1lbnNpb25zKCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgd2lkdGg6IHRoaXMuZWxlbWVudC5vZmZzZXRXaWR0aCxcbiAgICAgICAgICAgIGhlaWdodDogdGhpcy5lbGVtZW50Lm9mZnNldEhlaWdodFxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbm90Y2ggcG9zaXRpb24gZGVwZW5kaW5nIG9uIHRoZSBsb2NhbCBjb29yZGluYXRlcyB3aXRoaW4gdGhlIGtlZXBXaXRoaW4gY29udGFpbmVyXG4gICAgICogRGl2aWRlcyB0aGUgc3BhY2UgdmVydGljYWxseSBpbnRvIHRvcCwgY2VudGVyLCBib3R0b20gYW5kIGhvcml6b250YWxseSBpbnRvIGxlZnQsIGNlbnRlciwgcmlnaHRcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7YW55fSB4XG4gICAgICogQHBhcmFtIHthbnl9IHlcbiAgICAgKiBAcmV0dXJuc1xuICAgICAqIEBtZW1iZXJvZiBQb3B1cFxuICAgICAqL1xuICAgIG5vdGNoUG9zaXRpb25XaXRoaW4oeCwgeSkge1xuICAgICAgICBsZXQgaG9yaXpvbnRhbCA9IFwiQ2VudGVyXCJcbiAgICAgICAgbGV0IHZlcnRpY2FsID0gXCJjZW50ZXJcIlxuICAgICAgICBsZXQgeyB3aWR0aCwgaGVpZ2h0IH0gPSB0aGlzLndpdGhpbkRpbWVuc2lvbnMoKVxuICAgICAgICBsZXQgbG9jYWwgPSB0aGlzLmxvY2FsUG9pbnRXaXRoaW4oeCwgeSwgd2lkdGgsIGhlaWdodClcbiAgICAgICAgaWYgKGxvY2FsLnkgPCBoZWlnaHQgKiAwLjMzKSB7XG4gICAgICAgICAgICB2ZXJ0aWNhbCA9IFwidG9wXCJcbiAgICAgICAgfVxuICAgICAgICBpZiAobG9jYWwueSA+IGhlaWdodCAqIDAuNjYpIHtcbiAgICAgICAgICAgIHZlcnRpY2FsID0gXCJib3R0b21cIlxuICAgICAgICB9XG4gICAgICAgIGlmIChsb2NhbC54IDwgd2lkdGggKiAwLjMzKSB7XG4gICAgICAgICAgICBob3Jpem9udGFsID0gXCJMZWZ0XCJcbiAgICAgICAgfVxuICAgICAgICBpZiAobG9jYWwueCA+IHdpZHRoICogMC42Nikge1xuICAgICAgICAgICAgaG9yaXpvbnRhbCA9IFwiUmlnaHRcIlxuICAgICAgICB9XG4gICAgICAgIGxldCByZXN1bHQgPSB2ZXJ0aWNhbCArIGhvcml6b250YWxcbiAgICAgICAgaWYgKHJlc3VsdCA9PSBcImNlbnRlckNlbnRlclwiKVxuICAgICAgICAgICAgcmV0dXJuIHRoaXMubm90Y2hQb3NpdGlvblxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgfVxuXG4gICAgcGxhY2VBdChwb2ludCkge1xuICAgICAgICBsZXQgeCA9IHBvaW50LnhcbiAgICAgICAgbGV0IHkgPSBwb2ludC55XG4gICAgICAgIGxldCBub3RjaFBvc2l0aW9uID0gdGhpcy5ub3RjaFBvc2l0aW9uXG4gICAgICAgIGlmICh0aGlzLmtlZXBXaXRoaW4gIT0gbnVsbCkge1xuICAgICAgICAgICAgbm90Y2hQb3NpdGlvbiA9IHRoaXMubm90Y2hQb3NpdGlvbldpdGhpbih4LCB5KVxuICAgICAgICB9XG4gICAgICAgIEVsZW1lbnRzLnNldFN0eWxlKHRoaXMubm90Y2gsIHRoaXMubm90Y2hTdHlsZShub3RjaFBvc2l0aW9uKSlcbiAgICAgICAgdGhpcy5ub3RjaC5jbGFzc05hbWUgPSBcIm5vdGNoIFwiICsgbm90Y2hQb3NpdGlvblxuICAgICAgICBsZXQgeyB3aWR0aCwgaGVpZ2h0IH0gPSB0aGlzLmxvY2FsRGltZW5zaW9ucygpXG5cbiAgICAgICAgLy9pZiB0YXJnZXRCb3VuZGluZ0JveCBpcyBzZXQsIHBvcHVwIGlzIHBsYWNlZCBuZXh0IHRvIHRoZSByZWN0YW5nbGVcbiAgICAgICAgaWYgKHRoaXMudGFyZ2V0Qm91bmRpbmdCb3gpIHtcbiAgICAgICAgICAgIGxldCBiYlRvcCA9IHRoaXMudGFyZ2V0Qm91bmRpbmdCb3gueVxuICAgICAgICAgICAgbGV0IGJiQm90dG9tID0gdGhpcy50YXJnZXRCb3VuZGluZ0JveC55ICsgdGhpcy50YXJnZXRCb3VuZGluZ0JveC5oZWlnaHRcbiAgICAgICAgICAgIGxldCBiYkxlZnQgPSB0aGlzLnRhcmdldEJvdW5kaW5nQm94LnhcbiAgICAgICAgICAgIGxldCBiYlJpZ2h0ID0gdGhpcy50YXJnZXRCb3VuZGluZ0JveC54ICsgdGhpcy50YXJnZXRCb3VuZGluZ0JveC53aWR0aFxuICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhcInBsYWNlIHBvcHVwIHdpdGggYmIgc2V0OlwiLCB4LCB5LCBiYlRvcCwgYmJCb3R0b20sIGJiTGVmdCwgYmJSaWdodClcbiAgICAgICAgICAgIHN3aXRjaCAobm90Y2hQb3NpdGlvbikge1xuICAgICAgICAgICAgICAgIGNhc2UgXCJib3R0b21MZWZ0XCI6XG4gICAgICAgICAgICAgICAgY2FzZSBcImJvdHRvbVJpZ2h0XCI6XG4gICAgICAgICAgICAgICAgY2FzZSBcImJvdHRvbUNlbnRlclwiOlxuICAgICAgICAgICAgICAgICAgICB5ID0gYmJUb3BcbiAgICAgICAgICAgICAgICAgICAgaWYgKCF0aGlzLnVzZUV2ZW50UG9zV2l0aEJvdW5kaW5nQm94KVxuICAgICAgICAgICAgICAgICAgICAgICAgeCA9IChiYkxlZnQgKyBiYlJpZ2h0KSAvIDJcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICBjYXNlIFwidG9wTGVmdFwiOlxuICAgICAgICAgICAgICAgIGNhc2UgXCJ0b3BSaWdodFwiOlxuICAgICAgICAgICAgICAgIGNhc2UgXCJ0b3BDZW50ZXJcIjpcbiAgICAgICAgICAgICAgICAgICAgeSA9IGJiQm90dG9tXG4gICAgICAgICAgICAgICAgICAgIGlmICghdGhpcy51c2VFdmVudFBvc1dpdGhCb3VuZGluZ0JveClcbiAgICAgICAgICAgICAgICAgICAgICAgIHggPSAoYmJMZWZ0ICsgYmJSaWdodCkgLyAyXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgY2FzZSBcImNlbnRlclJpZ2h0XCI6XG4gICAgICAgICAgICAgICAgICAgIHggPSBiYkxlZnRcbiAgICAgICAgICAgICAgICAgICAgaWYgKCF0aGlzLnVzZUV2ZW50UG9zV2l0aEJvdW5kaW5nQm94KVxuICAgICAgICAgICAgICAgICAgICAgICAgeSA9IChiYlRvcCArIGJiQm90dG9tKSAvIDJcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICBjYXNlIFwiY2VudGVyTGVmdFwiOlxuICAgICAgICAgICAgICAgICAgICB4ID0gYmJSaWdodFxuICAgICAgICAgICAgICAgICAgICBpZiAoIXRoaXMudXNlRXZlbnRQb3NXaXRoQm91bmRpbmdCb3gpXG4gICAgICAgICAgICAgICAgICAgICAgICB5ID0gKGJiVG9wICsgYmJCb3R0b20pIC8gMlxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvL2NhbGN1bGF0ZSBwb3NpdGlvbiBkZXBlbmRpbmcgb24gc2V2ZXJhbCAob3B0aW9uYWwpIHBhcmFtZXRlcnNcbiAgICAgICAgc3dpdGNoIChub3RjaFBvc2l0aW9uKSB7XG4gICAgICAgICAgICBjYXNlIFwiYm90dG9tTGVmdFwiOlxuICAgICAgICAgICAgICAgIHggLT0gdGhpcy5wYWRkaW5nXG4gICAgICAgICAgICAgICAgeCAtPSB0aGlzLm5vdGNoU2l6ZVxuICAgICAgICAgICAgICAgIHkgLT0gaGVpZ2h0XG4gICAgICAgICAgICAgICAgeSAtPSB0aGlzLm5vdGNoU2l6ZSAqIDJcbiAgICAgICAgICAgICAgICB5IC09IHRoaXMucG9zT2Zmc2V0XG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGNhc2UgXCJib3R0b21SaWdodFwiOlxuICAgICAgICAgICAgICAgIHggLT0gd2lkdGhcbiAgICAgICAgICAgICAgICB4ICs9IHRoaXMucGFkZGluZ1xuICAgICAgICAgICAgICAgIHggKz0gdGhpcy5ub3RjaFNpemVcbiAgICAgICAgICAgICAgICB5IC09IGhlaWdodFxuICAgICAgICAgICAgICAgIHkgLT0gdGhpcy5ub3RjaFNpemUgKiAyXG4gICAgICAgICAgICAgICAgeSAtPSB0aGlzLnBvc09mZnNldFxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlIFwiYm90dG9tQ2VudGVyXCI6XG4gICAgICAgICAgICAgICAgeCAtPSB3aWR0aCAvIDJcbiAgICAgICAgICAgICAgICB5IC09IGhlaWdodFxuICAgICAgICAgICAgICAgIHkgLT0gdGhpcy5ub3RjaFNpemUgKiAyXG4gICAgICAgICAgICAgICAgeSAtPSB0aGlzLnBvc09mZnNldFxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlIFwidG9wTGVmdFwiOlxuICAgICAgICAgICAgICAgIHggLT0gdGhpcy5wYWRkaW5nXG4gICAgICAgICAgICAgICAgeCAtPSB0aGlzLm5vdGNoU2l6ZVxuICAgICAgICAgICAgICAgIHkgKz0gdGhpcy5ub3RjaFNpemUgKiAyXG4gICAgICAgICAgICAgICAgeSArPSB0aGlzLnBvc09mZnNldFxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlIFwidG9wUmlnaHRcIjpcbiAgICAgICAgICAgICAgICB4IC09IHdpZHRoXG4gICAgICAgICAgICAgICAgeCArPSB0aGlzLnBhZGRpbmdcbiAgICAgICAgICAgICAgICB4ICs9IHRoaXMubm90Y2hTaXplXG4gICAgICAgICAgICAgICAgeSArPSB0aGlzLm5vdGNoU2l6ZSAqIDJcbiAgICAgICAgICAgICAgICB5ICs9IHRoaXMucG9zT2Zmc2V0XG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGNhc2UgXCJ0b3BDZW50ZXJcIjpcbiAgICAgICAgICAgICAgICB4IC09IHdpZHRoIC8gMlxuICAgICAgICAgICAgICAgIHkgKz0gdGhpcy5ub3RjaFNpemUgKiAyXG4gICAgICAgICAgICAgICAgeSArPSB0aGlzLnBvc09mZnNldFxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlIFwiY2VudGVyUmlnaHRcIjpcbiAgICAgICAgICAgICAgICB4IC09IHdpZHRoICsgdGhpcy5ub3RjaFNpemUgKiAyXG4gICAgICAgICAgICAgICAgeCAtPSB0aGlzLnBvc09mZnNldFxuICAgICAgICAgICAgICAgIHkgLT0gaGVpZ2h0IC8gMlxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlIFwiY2VudGVyTGVmdFwiOlxuICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCJoZWlnaHRcIiwgaGVpZ2h0KVxuICAgICAgICAgICAgICAgIHkgLT0gaGVpZ2h0IC8gMlxuICAgICAgICAgICAgICAgIHggKz0gdGhpcy5ub3RjaFNpemUgKiAyXG4gICAgICAgICAgICAgICAgeCArPSB0aGlzLnBvc09mZnNldFxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5wbGFjZU9yaWdpbih4LCB5KVxuICAgIH1cblxuICAgIC8qKiBTaG93cyB0aGUgUG9wdXAgd2l0aCB0aGUgZ2l2ZW4gY29tbWFuZHMgYXQgdGhlIGN1cnJlbnQgcG9zaXRpb24uXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGNvbnRlbnQgLSBBIGRpY3Qgb2JqZWN0IHdpdGggdHlwZSBzdHJpbmdzICh0ZXh0LCBpbWcsIGh0bWwpIGFzIGtleXNcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgY29ycmVzcG9uZGluZyB2YWx1ZXMuXG4gICAgICogQHJldHVybiB7UG9wdXB9IHRoaXNcbiAgICAgKi9cbiAgICBzaG93KGNvbnRlbnQpIHtcbiAgICAgICAgdGhpcy5zZXR1cChjb250ZW50KVxuICAgICAgICB0aGlzLmZhZGVJbigpXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqIENvbmZpZ3VyYXRpb24gb2JqZWN0LiBSZXR1cm4gZGVmYXVsdCBzdHlsZXMgYXMgQ1NTIHZhbHVlcy5cbiAgICAgKi9cbiAgICBkZWZhdWx0U3R5bGUoKSB7XG4gICAgICAgIGxldCBwYWRkaW5nID0gdGhpcy5wYWRkaW5nXG4gICAgICAgIGxldCBzdHlsZSA9IHtcbiAgICAgICAgICAgIG1heFdpZHRoOiB0aGlzLm1heFdpZHRoICsgXCJweFwiLFxuICAgICAgICAgICAgekluZGV4OiB0aGlzLnpJbmRleCxcbiAgICAgICAgICAgIHBvc2l0aW9uOiBcImFic29sdXRlXCIsXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMubWluV2lkdGgpIHtcbiAgICAgICAgICAgIHN0eWxlLm1pbldpZHRoID0gdGhpcy5taW5XaWR0aCArIFwicHhcIlxuICAgICAgICB9XG4gICAgICAgIGlmICghdGhpcy5ub1N0eWxlKSB7XG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKHN0eWxlLCB7XG4gICAgICAgICAgICAgICAgYm9yZGVyUmFkaXVzOiBNYXRoLnJvdW5kKHRoaXMucGFkZGluZyAvIDIpICsgXCJweFwiLFxuICAgICAgICAgICAgICAgIGJhY2tncm91bmRDb2xvcjogdGhpcy5iYWNrZ3JvdW5kQ29sb3IsXG4gICAgICAgICAgICAgICAgcGFkZGluZzogdGhpcy5wYWRkaW5nICsgXCJweFwiLFxuICAgICAgICAgICAgICAgIGJveFNoYWRvdzogXCIwIDEwcHggMTVweCByZ2JhKDAsIDAsIDAsIDAuMylcIixcbiAgICAgICAgICAgICAgICBmb250RmFtaWx5OiB0aGlzLmZvbnRGYW1pbHksXG4gICAgICAgICAgICAgICAgZm9udFNpemU6IHRoaXMuZm9udFNpemUsXG4gICAgICAgICAgICAgICAgc3Ryb2tlOiBcImJsYWNrXCIsXG4gICAgICAgICAgICAgICAgZmlsbDogXCJ3aGl0ZVwiXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHN0eWxlXG4gICAgfVxuXG4gICAgLyoqIENvbmZpZ3VyYXRpb24gb2JqZWN0LiBSZXR1cm4gbm90Y2ggc3R5bGVzIGFzIENTUyB2YWx1ZXMuXG4gICAgICovXG4gICAgbm90Y2hTdHlsZShub3RjaFBvc2l0aW9uID0gbnVsbCkge1xuICAgICAgICBpZiAobm90Y2hQb3NpdGlvbiA9PSBudWxsKSB7XG4gICAgICAgICAgICBub3RjaFBvc2l0aW9uID0gdGhpcy5ub3RjaFBvc2l0aW9uXG4gICAgICAgIH1cbiAgICAgICAgbGV0IHdpZHRoID0gMFxuICAgICAgICBsZXQgaGVpZ2h0ID0gMFxuICAgICAgICBsZXQgbGVmdCA9IHRoaXMucGFkZGluZ1xuICAgICAgICBsZXQgc2l6ZSA9IHRoaXMubG9jYWxEaW1lbnNpb25zKClcbiAgICAgICAgaWYgKG5vdGNoUG9zaXRpb24uZW5kc1dpdGgoXCJSaWdodFwiKSkge1xuICAgICAgICAgICAgbGVmdCA9IHNpemUud2lkdGggLSB0aGlzLnBhZGRpbmcgLSB0aGlzLm5vdGNoU2l6ZSAqIDJcbiAgICAgICAgfVxuICAgICAgICBpZiAobm90Y2hQb3NpdGlvbi5lbmRzV2l0aChcIkNlbnRlclwiKSkge1xuICAgICAgICAgICAgbGVmdCA9IHNpemUud2lkdGggLyAyIC0gdGhpcy5ub3RjaFNpemVcbiAgICAgICAgfVxuICAgICAgICBsZWZ0ID0gTWF0aC5yb3VuZChsZWZ0KSArICdweCdcbiAgICAgICAgbGV0IGJvcmRlckJvdHRvbSA9IDBcbiAgICAgICAgbGV0IGJvcmRlclRvcCA9IDBcblxuICAgICAgICBpZiAobm90Y2hQb3NpdGlvbi5zdGFydHNXaXRoKFwiYm90dG9tXCIpKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5ub1N0eWxlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgICAgd2lkdGgsXG4gICAgICAgICAgICAgICAgICAgIGhlaWdodCxcbiAgICAgICAgICAgICAgICAgICAgbGVmdCxcbiAgICAgICAgICAgICAgICAgICAgYm90dG9tOiAtdGhpcy5ub3RjaFNpemUgKyBcInB4XCIsXG4gICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBcImFic29sdXRlXCIsXG4gICAgICAgICAgICAgICAgICAgIGJvcmRlclN0eWxlOiBcInNvbGlkXCIsXG4gICAgICAgICAgICAgICAgICAgIGJvcmRlclRvcFdpZHRoOiB0aGlzLm5vdGNoU2l6ZSArIFwicHhcIixcbiAgICAgICAgICAgICAgICAgICAgYm9yZGVyUmlnaHQ6IHRoaXMubm90Y2hTaXplICsgXCJweCBzb2xpZCB0cmFuc3BhcmVudFwiLFxuICAgICAgICAgICAgICAgICAgICBib3JkZXJMZWZ0OiB0aGlzLm5vdGNoU2l6ZSArIFwicHggc29saWQgdHJhbnNwYXJlbnRcIixcbiAgICAgICAgICAgICAgICAgICAgYm9yZGVyQm90dG9tOiAwXG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICAgIHdpZHRoLFxuICAgICAgICAgICAgICAgICAgICBoZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgIGxlZnQsXG4gICAgICAgICAgICAgICAgICAgIGJveFNoYWRvdzogXCIwIDEycHggMTVweCByZ2JhKDAsIDAsIDAsIDAuMSlcIixcbiAgICAgICAgICAgICAgICAgICAgYm90dG9tOiAtdGhpcy5ub3RjaFNpemUgKyBcInB4XCIsXG4gICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBcImFic29sdXRlXCIsXG4gICAgICAgICAgICAgICAgICAgIGJvcmRlclRvcDogdGhpcy5ub3RjaFNpemUgKyBcInB4IHNvbGlkIFwiICsgdGhpcy5iYWNrZ3JvdW5kQ29sb3IsXG4gICAgICAgICAgICAgICAgICAgIGJvcmRlclJpZ2h0OiB0aGlzLm5vdGNoU2l6ZSArIFwicHggc29saWQgdHJhbnNwYXJlbnRcIixcbiAgICAgICAgICAgICAgICAgICAgYm9yZGVyTGVmdDogdGhpcy5ub3RjaFNpemUgKyBcInB4IHNvbGlkIHRyYW5zcGFyZW50XCIsXG4gICAgICAgICAgICAgICAgICAgIGJvcmRlckJvdHRvbTogMFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAobm90Y2hQb3NpdGlvbi5zdGFydHNXaXRoKFwidG9wXCIpKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5ub1N0eWxlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgICAgd2lkdGgsXG4gICAgICAgICAgICAgICAgICAgIGhlaWdodCxcbiAgICAgICAgICAgICAgICAgICAgbGVmdCxcbiAgICAgICAgICAgICAgICAgICAgdG9wOiAtdGhpcy5ub3RjaFNpemUgKyBcInB4XCIsXG4gICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBcImFic29sdXRlXCIsXG4gICAgICAgICAgICAgICAgICAgIGJvcmRlclN0eWxlOiBcInNvbGlkXCIsXG4gICAgICAgICAgICAgICAgICAgIGJvcmRlckJvdHRvbVdpZHRoOiB0aGlzLm5vdGNoU2l6ZSArIFwicHhcIixcbiAgICAgICAgICAgICAgICAgICAgYm9yZGVyUmlnaHQ6IHRoaXMubm90Y2hTaXplICsgXCJweCBzb2xpZCB0cmFuc3BhcmVudFwiLFxuICAgICAgICAgICAgICAgICAgICBib3JkZXJMZWZ0OiB0aGlzLm5vdGNoU2l6ZSArIFwicHggc29saWQgdHJhbnNwYXJlbnRcIixcbiAgICAgICAgICAgICAgICAgICAgYm9yZGVyVG9wOiAwXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICB3aWR0aCxcbiAgICAgICAgICAgICAgICAgICAgaGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICBsZWZ0LFxuICAgICAgICAgICAgICAgICAgICB0b3A6IC10aGlzLm5vdGNoU2l6ZSArIFwicHhcIixcbiAgICAgICAgICAgICAgICAgICAgcG9zaXRpb246IFwiYWJzb2x1dGVcIixcbiAgICAgICAgICAgICAgICAgICAgYm9yZGVyQm90dG9tOiB0aGlzLm5vdGNoU2l6ZSArIFwicHggc29saWQgXCIgKyB0aGlzLmJhY2tncm91bmRDb2xvcixcbiAgICAgICAgICAgICAgICAgICAgYm9yZGVyUmlnaHQ6IHRoaXMubm90Y2hTaXplICsgXCJweCBzb2xpZCB0cmFuc3BhcmVudFwiLFxuICAgICAgICAgICAgICAgICAgICBib3JkZXJMZWZ0OiB0aGlzLm5vdGNoU2l6ZSArIFwicHggc29saWQgdHJhbnNwYXJlbnRcIixcbiAgICAgICAgICAgICAgICAgICAgYm9yZGVyVG9wOiAwXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMubm9TdHlsZSkge1xuXG4gICAgICAgICAgICBpZiAobm90Y2hQb3NpdGlvbi5lbmRzV2l0aChcIkxlZnRcIikpIHtcbiAgICAgICAgICAgICAgICBsZWZ0ID0gLXRoaXMubm90Y2hTaXplICogMiArIFwicHhcIlxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAobm90Y2hQb3NpdGlvbi5lbmRzV2l0aChcIlJpZ2h0XCIpKSB7XG4gICAgICAgICAgICAgICAgbGVmdCA9IHNpemUud2lkdGggKyBcInB4XCJcbiAgICAgICAgICAgIH1cblxuXG4gICAgICAgICAgICBsZXQgdG9wID0gc2l6ZS5oZWlnaHQgLyAyIC0gdGhpcy5ub3RjaFNpemVcbiAgICAgICAgICAgIHRvcCA9IE1hdGgucm91bmQodG9wKSArICdweCdcblxuXG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHdpZHRoLFxuICAgICAgICAgICAgICAgIGhlaWdodCxcbiAgICAgICAgICAgICAgICBsZWZ0LFxuICAgICAgICAgICAgICAgIHRvcCxcbiAgICAgICAgICAgICAgICBib3JkZXJSaWdodFdpZHRoOiB0aGlzLm5vdGNoU2l6ZSxcbiAgICAgICAgICAgICAgICBib3JkZXJMZWZ0V2lkdGg6IHRoaXMubm90Y2hTaXplLFxuICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBcImFic29sdXRlXCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyVG9wOiB0aGlzLm5vdGNoU2l6ZSArIFwicHggc29saWQgdHJhbnNwYXJlbnRcIixcbiAgICAgICAgICAgICAgICBib3JkZXJCb3R0b206IHRoaXMubm90Y2hTaXplICsgXCJweCBzb2xpZCB0cmFuc3BhcmVudFwiXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGxldCBib3JkZXJSaWdodCA9IHRoaXMubm90Y2hTaXplICsgXCJweCBzb2xpZCB0cmFuc3BhcmVudFwiXG4gICAgICAgICAgICBsZXQgYm9yZGVyTGVmdCA9IHRoaXMubm90Y2hTaXplICsgXCJweCBzb2xpZCB0cmFuc3BhcmVudFwiXG4gICAgICAgICAgICBsZXQgdG9wID0gc2l6ZS5oZWlnaHQgLyAyIC0gdGhpcy5ub3RjaFNpemVcbiAgICAgICAgICAgIGlmIChub3RjaFBvc2l0aW9uLmVuZHNXaXRoKFwiTGVmdFwiKSkge1xuICAgICAgICAgICAgICAgIGxlZnQgPSAtdGhpcy5ub3RjaFNpemUgKiAyICsgXCJweFwiXG4gICAgICAgICAgICAgICAgYm9yZGVyUmlnaHQgPSB0aGlzLm5vdGNoU2l6ZSArIFwicHggc29saWQgXCIgKyB0aGlzLmJhY2tncm91bmRDb2xvclxuICAgICAgICAgICAgICAgIHRoaXMuZWxlbWVudC5zdHlsZS5ib3hTaGFkb3cgPSBcIjE1cHggMTBweCAxNXB4ICByZ2JhKDAsIDAsIDAsIDAuMylcIlxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKG5vdGNoUG9zaXRpb24uZW5kc1dpdGgoXCJSaWdodFwiKSkge1xuICAgICAgICAgICAgICAgIGxlZnQgPSBzaXplLndpZHRoICsgXCJweFwiXG4gICAgICAgICAgICAgICAgYm9yZGVyTGVmdCA9IHRoaXMubm90Y2hTaXplICsgXCJweCBzb2xpZCBcIiArIHRoaXMuYmFja2dyb3VuZENvbG9yXG4gICAgICAgICAgICAgICAgdGhpcy5lbGVtZW50LnN0eWxlLmJveFNoYWRvdyA9IFwiMTVweCA1cHggMTVweCByZ2JhKDAsIDAsIDAsIDAuMylcIlxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0b3AgPSBNYXRoLnJvdW5kKHRvcCkgKyAncHgnXG5cblxuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICB3aWR0aCxcbiAgICAgICAgICAgICAgICBoZWlnaHQsXG4gICAgICAgICAgICAgICAgbGVmdCxcbiAgICAgICAgICAgICAgICB0b3AsXG4gICAgICAgICAgICAgICAgYm9yZGVyUmlnaHQsXG4gICAgICAgICAgICAgICAgYm9yZGVyTGVmdCxcbiAgICAgICAgICAgICAgICAvLyAgYm94U2hhZG93LFxuICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBcImFic29sdXRlXCIsXG4gICAgICAgICAgICAgICAgYm9yZGVyVG9wOiB0aGlzLm5vdGNoU2l6ZSArIFwicHggc29saWQgdHJhbnNwYXJlbnRcIixcbiAgICAgICAgICAgICAgICBib3JkZXJCb3R0b206IHRoaXMubm90Y2hTaXplICsgXCJweCBzb2xpZCB0cmFuc3BhcmVudFwiXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogQ29udmVuaWVudCBzdGF0aWMgbWV0aG9kcyB0byBzaG93IGFuZCByZXVzZSBhIFBvcHVwIGltcGxlbWVudGVkXG4gICAgICogYXMgYSBjbGFzcyB2YXJpYWJsZS5cbiAgICAgKiBAcGFyYW0ge09iamVjdH0gY29udGVudCAtIEEgZGljdCBvYmplY3Qgd2l0aCB0eXBlIHN0cmluZ3MgKHRleHQsIGltZywgaHRtbCkgYXMga2V5c1xuICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZCBjb3JyZXNwb25kaW5nIHZhbHVlcy5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBwb2ludCAtIFRoZSBwb3NpdGlvbiBhcyB4LCB5IGNvb3JkaW5hdGVzIHtweH0uXG4gICAgICogQHBhcmFtIHtib29sZWFufSBhdXRvQ2xvc2UgLSBBdXRvY2xvc2UgdGhlIG1lbnUgYWZ0ZXIgc2VsZWN0aW5nIGFuIGl0ZW0uXG4gICAgICovXG4gICAgc3RhdGljIG9wZW4oXG4gICAgICAgIGNvbnRlbnQsXG4gICAgICAgIHBvaW50LFxuICAgICAgICB7XG4gICAgICAgICAgICBwYXJlbnQgPSBudWxsLFxuICAgICAgICAgICAgY29udGV4dCA9IHdpbmRvdyxcbiAgICAgICAgICAgIGZvbnRTaXplID0gXCIxZW1cIixcbiAgICAgICAgICAgIGZvbnRGYW1pbHkgPSBcIkFyaWFsXCIsXG4gICAgICAgICAgICBwYWRkaW5nID0gMTYsXG4gICAgICAgICAgICBub3RjaFNpemUgPSAxMCxcbiAgICAgICAgICAgIHN3aXRjaFBvcyA9IGZhbHNlLFxuICAgICAgICAgICAgbWluV2lkdGggPSBudWxsLFxuICAgICAgICAgICAgbWF4V2lkdGggPSA4MDAsXG4gICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3IgPSBcIiNFRUVcIixcbiAgICAgICAgICAgIHpJbmRleCA9IDAsXG4gICAgICAgICAgICBub3JtYWxDb2xvciA9IFwiIzQ0NFwiLFxuICAgICAgICAgICAgY2xvc2VJY29uID0gbnVsbCxcbiAgICAgICAgICAgIHJlc2l6ZUljb24gPSBudWxsLFxuICAgICAgICAgICAgY2xvc2VDb21tYW5kID0gbnVsbCxcbiAgICAgICAgICAgIGF1dG9DbG9zZSA9IHRydWUsXG4gICAgICAgICAgICBrZWVwV2l0aGluID0gbnVsbCxcbiAgICAgICAgICAgIGRyYWdnYWJsZSA9IGZhbHNlLFxuICAgICAgICAgICAgcG9zT2Zmc2V0ID0gMCxcbiAgICAgICAgICAgIHRhcmdldEJvdW5kaW5nQm94ID0gbnVsbCxcbiAgICAgICAgICAgIHVzZUV2ZW50UG9zV2l0aEJvdW5kaW5nQm94ID0gZmFsc2UsXG4gICAgICAgICAgICBpbnRlcmFjdGl2ZSA9IGZhbHNlLFxuICAgICAgICAgICAgb25SZXNpemUgPSBudWxsLFxuICAgICAgICAgICAgb25Nb3ZlID0gbnVsbFxuICAgICAgICB9ID0ge31cbiAgICApIHtcblxuXG4gICAgICAgIGxldCBub3RjaFBvc2l0aW9uID0gKHN3aXRjaFBvcyAmJiBwb2ludC55IDwgNTApID8gXCJ0b3BDZW50ZXJcIiA6IFwiYm90dG9tQ2VudGVyXCJcbiAgICAgICAgbGV0IHBvcHVwID0gbmV3IFBvcHVwKHtcbiAgICAgICAgICAgIHBhcmVudCxcbiAgICAgICAgICAgIGNvbnRleHQsXG4gICAgICAgICAgICBmb250RmFtaWx5LFxuICAgICAgICAgICAgZm9udFNpemUsXG4gICAgICAgICAgICBwYWRkaW5nLFxuICAgICAgICAgICAgbm90Y2hTaXplLFxuICAgICAgICAgICAgc3dpdGNoUG9zLFxuICAgICAgICAgICAgbWluV2lkdGgsXG4gICAgICAgICAgICBtYXhXaWR0aCxcbiAgICAgICAgICAgIGJhY2tncm91bmRDb2xvcixcbiAgICAgICAgICAgIG5vcm1hbENvbG9yLFxuICAgICAgICAgICAgbm90Y2hQb3NpdGlvbixcbiAgICAgICAgICAgIHpJbmRleCxcbiAgICAgICAgICAgIGF1dG9DbG9zZSxcbiAgICAgICAgICAgIGtlZXBXaXRoaW4sXG4gICAgICAgICAgICBjbG9zZUNvbW1hbmQsXG4gICAgICAgICAgICBjbG9zZUljb24sXG4gICAgICAgICAgICByZXNpemVJY29uLFxuICAgICAgICAgICAgZHJhZ2dhYmxlLFxuICAgICAgICAgICAgcG9zT2Zmc2V0LFxuICAgICAgICAgICAgdGFyZ2V0Qm91bmRpbmdCb3gsXG4gICAgICAgICAgICB1c2VFdmVudFBvc1dpdGhCb3VuZGluZ0JveCxcbiAgICAgICAgICAgIGludGVyYWN0aXZlLFxuICAgICAgICAgICAgb25SZXNpemUsXG4gICAgICAgICAgICBvbk1vdmVcbiAgICAgICAgfSlcbiAgICAgICAgcG9wdXAucmVnaXN0ZXIoY29udGV4dClcbiAgICAgICAgcG9wdXAuc2hvd0F0KGNvbnRlbnQsIHBvaW50KVxuICAgICAgICByZXR1cm4gcG9wdXBcbiAgICB9XG5cbiAgICAvKiogQ29udmVuaWVudCBzdGF0aWMgbWV0aG9kIHRvIGNsb3NlIHRoZSBQb3B1cCBpbXBsZW1lbnRlZFxuICAgICAqIGFzIGEgY2xhc3MgdmFyaWFibGUuIENhbGxzIHRoZSBjbG9zZSBjb21tYW5kLlxuICAgICAqL1xuICAgIHN0YXRpYyBjbG9zZVBvcHVwKGNvbnRleHQgPSB3aW5kb3cpIHtcbiAgICAgICAgbGV0IHBvcHVwID0gUG9wcGFibGUucmVnaXN0cmF0aW9ucy5nZXQoY29udGV4dClcbiAgICAgICAgaWYgKHBvcHVwICE9IG51bGwpIHtcbiAgICAgICAgICAgIHBvcHVwLmNsb3NlKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKiBDb252ZW5pZW50IHN0YXRpYyBtZXRob2RzIHRvIHJlbW92ZSB0aGUgUG9wdXAgaW1wbGVtZW50ZWRcbiAgICAgKiBhcyBhIGNsYXNzIHZhcmlhYmxlLiBSZW1vdmVzIHRoZSBwb3B1cCB3aXRob3V0IHBlcmZvcm1pbmcgdGhlIGNsb3NlIGNvbW1hbmQuXG4gICAgICovXG4gICAgc3RhdGljIHJlbW92ZShjb250ZXh0ID0gd2luZG93KSB7XG4gICAgICAgIGxldCBwb3B1cCA9IFBvcHBhYmxlLnJlZ2lzdHJhdGlvbnMuZ2V0KGNvbnRleHQpXG4gICAgICAgIGlmIChwb3B1cCAhPSBudWxsKSB7XG4gICAgICAgICAgICBwb3B1cC5yZW1vdmUoKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ29udmVuaWVudCBzdGF0aWMgbWV0aG9kIHRvIGNvbXB1dGUgdGhlIGNsaWNrZWQgcmVjdCBvZiBvYmplY3RzIHRoYXQgaGF2ZSBtdWx0aXBsZSBjbGllbnRzIHJlY3RzLlxuICAgICAqIE5lZWRlZCB0byBwb3NpdGlvbiBwb3B1cHMgY29ycmVjdGx5IGFib3ZlIG9iamVjdHMgd2l0aCBsaW5lIGJyZWFrcywgZS5nLiBzcGFuc1xuICAgICAqXG4gICAgICogQHN0YXRpY1xuICAgICAqIEBwYXJhbSB7Kn0gZXZlbnRcbiAgICAgKiBAcmV0dXJucyB7Kn0gRE9NUmVjdFxuICAgICAqIEBtZW1iZXJvZiBQb3B1cFxuICAgICAqL1xuICAgIHN0YXRpYyB0YXJnZXRSZWN0KGV2ZW50KSB7XG4gICAgICAgIGxldCB0YXJnZXQgPSBldmVudC50YXJnZXRcbiAgICAgICAgbGV0IHggPSBldmVudC5wYWdlWFxuICAgICAgICBsZXQgeSA9IGV2ZW50LnBhZ2VZXG4gICAgICAgIGZvciAobGV0IHJlY3Qgb2YgdGFyZ2V0LmdldENsaWVudFJlY3RzKCkpIHtcbiAgICAgICAgICAgIGxldCB3aXRoaW5YID0geCA+PSByZWN0LmxlZnQgJiYgeCA8PSByZWN0LmxlZnQgKyByZWN0LndpZHRoXG4gICAgICAgICAgICBsZXQgd2l0aGluWSA9IHkgPj0gcmVjdC50b3AgJiYgeSA8PSByZWN0LnRvcCArIHJlY3QuaGVpZ2h0XG4gICAgICAgICAgICBpZiAod2l0aGluWCAmJiB3aXRoaW5ZKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHJlY3RcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENvbnZlbmllbnQgc3RhdGljIG1ldGhvZCB0byBjb21wdXRlIHRoZSBjZW50ZXIgb2Ygb2JqZWN0cyB0aGF0IGhhdmUgbXVsdGlwbGUgY2xpZW50cyByZWN0cy5cbiAgICAgKiBOZWVkZWQgdG8gcG9zaXRpb24gcG9wdXBzIGNvcnJlY3RseSBhYm92ZSBvYmplY3RzIHdpdGggbGluZSBicmVha3MsIGUuZy4gc3BhbnNcbiAgICAgKlxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0geyp9IGV2ZW50XG4gICAgICogQHJldHVybnMgeyp9IFBvaW50XG4gICAgICogQG1lbWJlcm9mIFBvcHVwXG4gICAgICovXG4gICAgc3RhdGljIHRhcmdldENlbnRlcihldmVudCkge1xuICAgICAgICBsZXQgdGFyZ2V0ID0gZXZlbnQudGFyZ2V0XG4gICAgICAgIGxldCB4ID0gZXZlbnQucGFnZVhcbiAgICAgICAgbGV0IHkgPSBldmVudC5wYWdlWVxuICAgICAgICBsZXQgcmVjdCA9IFBvcHVwLnRhcmdldFJlY3QoZXZlbnQpXG4gICAgICAgIGlmIChyZWN0ICE9IG51bGwpIHtcbiAgICAgICAgICAgIHggPSByZWN0LmxlZnQgKyByZWN0LndpZHRoIC8gMlxuICAgICAgICAgICAgeSA9IHJlY3QudG9wICsgcmVjdC5oZWlnaHQgLyAyXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHsgeCwgeSB9XG4gICAgfVxufVxuIiwiaW1wb3J0IFBvcHBhYmxlIGZyb20gJy4vcG9wcGFibGUuanMnXG5pbXBvcnQgUG9wdXAgZnJvbSAnLi9wb3B1cC5qcydcbmltcG9ydCB7IEVsZW1lbnRzIH0gZnJvbSAnLi91dGlscy5qcydcblxuLyoqIEEgUG9wdXAgTWVudSB0aGF0IHNob3dzIHRleHQgbGFiZWxzIGluIGEgdmVydGljYWwgcm93LlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBQb3B1cE1lbnUgZXh0ZW5kcyBQb3B1cCB7XG4gICAgLyoqXG4gICAgKiBUaGUgY29uc3RydWN0b3IuXG4gICAgKiBAY29uc3RydWN0b3JcbiAgICAqIEBwYXJhbSB7RE9NIEVsZW1lbnR9IHBhcmVudCAtIFRoZSBET00gcGFyZW50IGVsZW1lbnQuXG4gICAgKiBAcGFyYW0ge09iamVjdH0gY29tbWFuZHMgLSBBIGRpY3Qgb2JqZWN0IHdpdGggY29tbWFuZCBsYWJlbCBzdHJpbmdzIGFzIGtleXNcbiAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZCBjb21tYW5kIGZ1bmN0aW9ucyBhcyB2YWx1ZXMuXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gZm9udFNpemUgLSBEZXNjcmliZXMgdGhlIGZvbnQgc2l6ZSBhcyBDU1MgdmFsdWVcbiAgICAqIEBwYXJhbSB7bnVtYmVyIHx8IHN0cmluZ30gcGFkZGluZyAtIERlc2NyaWJlcyB0aGUgcGFkZGluZyBhcyBDU1MgdmFsdWVcbiAgICAqIEBwYXJhbSB7bnVtYmVyIHx8IHN0cmluZ30gbm90Y2hTaXplIC0gRGVzY3JpYmVzIHRoZSBzaXplIG9mIHRoZSBub3RjaCAoY2FsbG91dCkgYXMgQ1NTIHZhbHVlXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gaGlnaGxpZ2h0Q29sb3IgIC0gVGhlIGNvbG9yIG9mIGhpZ2hsaWdodGVkIG1lbnUgaXRlbXMgYXMgQ1NTIHZhbHVlXG4gICAgKiBAcGFyYW0ge3N0cmluZ30gYmFja2dyb3VuZENvbG9yICAtIFRoZSBjb2xvciBvZiB0aGUgYmFja2dyb3VuZCBhcyBDU1MgdmFsdWVcbiAgICAqIEBwYXJhbSB7c3RyaW5nfSBub3JtYWxDb2xvciAgLSBUaGUgY29sb3Igb2Ygbm9ybWFsIG1lbnUgaXRlbXMgYXMgQ1NTIHZhbHVlXG4gICAgKiBAcGFyYW0ge0RPTSBFbGVtZW50fSBrZWVwV2l0aGluICAtIFRoZSBjb250YWluZXIgdG8gc3RheSB3aXRoaW5cbiAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gYXV0b0Nsb3NlICAtIEF1dG9jbG9zZSB0aGUgbWVudSBhZnRlciBzZWxlY3RpbmcgYW4gaXRlbVxuICAgICovXG4gICAgY29uc3RydWN0b3IoeyBwYXJlbnQgPSBudWxsLFxuICAgICAgICBjb21tYW5kcyA9IG51bGwsXG4gICAgICAgIGZvbnRTaXplID0gJzFlbScsXG4gICAgICAgIGZvbnRGYW1pbHkgPSAnQXJpYWwnLFxuICAgICAgICBwYWRkaW5nID0gMTYsXG4gICAgICAgIHpJbmRleCA9IDEsXG4gICAgICAgIHNwYWNpbmcgPSAnMHB4JyxcbiAgICAgICAgc3dpdGNoUG9zID0gZmFsc2UsXG4gICAgICAgIG5vdGNoU2l6ZSA9IDEwLFxuICAgICAgICBtYXhXaWR0aCA9IDgwMCxcbiAgICAgICAgYmFja2dyb3VuZENvbG9yID0gJyNFRUUnLFxuICAgICAgICBub3JtYWxDb2xvciA9ICcjNDQ0JyxcbiAgICAgICAgaGlnaGxpZ2h0Q29sb3IgPSAnYmxhY2snLFxuICAgICAgICBub3RjaFBvc2l0aW9uID0gJ2JvdHRvbUxlZnQnLFxuICAgICAgICBrZWVwV2l0aGluID0gbnVsbCxcbiAgICAgICAgYXV0b0Nsb3NlID0gdHJ1ZSB9ID0ge30pIHtcbiAgICAgICAgc3VwZXIoeyBwYXJlbnQsIGZvbnRTaXplLCBmb250RmFtaWx5LCBwYWRkaW5nLCBub3RjaFNpemUsIG5vdGNoUG9zaXRpb24sIGJhY2tncm91bmRDb2xvciwga2VlcFdpdGhpbiwgbm9ybWFsQ29sb3IsIGF1dG9DbG9zZSB9KVxuICAgICAgICB0aGlzLmNvbW1hbmRzID0gY29tbWFuZHNcbiAgICAgICAgdGhpcy56SW5kZXggPSB6SW5kZXhcbiAgICAgICAgdGhpcy5zd2l0Y2hQb3MgPSBzd2l0Y2hQb3NcbiAgICAgICAgdGhpcy5zcGFjaW5nID0gc3BhY2luZ1xuICAgICAgICB0aGlzLmhpZ2hsaWdodENvbG9yID0gaGlnaGxpZ2h0Q29sb3JcbiAgICB9XG5cbiAgICAvKiogU2V0dXAgbWVudSB3aXRoIGEgZGljdGlvbmFyeSBvZiBjb21tYW5kIGxhYmVscyBhbmQgY29tbWFuZCBmdW5jdGlvbnMuXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGNvbW1hbmRzIC0gQSBkaWN0IG9iamVjdCB3aXRoIGNvbW1hbmQgbGFiZWwgc3RyaW5ncyBhcyBrZXlzXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5kIGNvbW1hbmQgZnVuY3Rpb25zIGFzIHZhbHVlcy5cbiAgICAgKiBAcmV0dXJuIHtQb3B1cE1lbnV9IHRoaXNcbiAgICAgKi9cbiAgICBzZXR1cChjb21tYW5kcykge1xuXG4gICAgICAgIHRoaXMuY29tbWFuZHMgPSBjb21tYW5kc1xuICAgICAgICB0aGlzLml0ZW1zID0ge31cbiAgICAgICAgdGhpcy5lbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICAgICAgdGhpcy5lbGVtZW50LnN0eWxlLnpJbmRleCA9IHRoaXMuekluZGV4XG4gICAgICAgIEVsZW1lbnRzLmFkZENsYXNzKHRoaXMuZWxlbWVudCwgJ3Vuc2VsZWN0YWJsZScpXG4gICAgICAgIHRoaXMubm90Y2ggPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKVxuICAgICAgICBFbGVtZW50cy5zZXRTdHlsZSh0aGlzLm5vdGNoLCB0aGlzLm5vdGNoU3R5bGUoKSlcbiAgICAgICAgZm9yIChsZXQga2V5IGluIGNvbW1hbmRzKSB7XG4gICAgICAgICAgICBsZXQgaXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpXG4gICAgICAgICAgICB0aGlzLmVsZW1lbnQuYXBwZW5kQ2hpbGQoaXRlbSlcbiAgICAgICAgICAgIGl0ZW0uaW5uZXJIVE1MID0ga2V5XG4gICAgICAgICAgICBpdGVtLnN0eWxlLnBhZGRpbmdCb3R0b20gPSBpdGVtLnN0eWxlLnBhZGRpbmdUb3AgPSB0aGlzLnNwYWNpbmdcbiAgICAgICAgICAgIEVsZW1lbnRzLnNldFN0eWxlKGl0ZW0sIHsgY29sb3I6IHRoaXMubm9ybWFsQ29sb3IsIGN1cnNvcjogJ2RlZmF1bHQnIH0pXG4gICAgICAgICAgICBFbGVtZW50cy5hZGRDbGFzcyhpdGVtLCAndW5zZWxlY3RhYmxlJylcbiAgICAgICAgICAgIEVsZW1lbnRzLmFkZENsYXNzKGl0ZW0sICdwb3B1cE1lbnVJdGVtJylcbiAgICAgICAgICAgIHRoaXMuaXRlbXNba2V5XSA9IGl0ZW1cbiAgICAgICAgICAgIGl0ZW0ub25jbGljayA9IChldmVudCkgPT4geyB0aGlzLnBlcmZvcm0oa2V5KSB9XG4gICAgICAgICAgICBpdGVtLm9udGFwID0gKGV2ZW50KSA9PiB7IHRoaXMucGVyZm9ybShrZXkpIH1cbiAgICAgICAgICAgIGl0ZW0ub25tb3VzZW92ZXIgPSAoZXZlbnQpID0+IHsgdGhpcy5vdmVyKGV2ZW50LCBrZXkpIH1cbiAgICAgICAgICAgIGl0ZW0ub25tb3VzZW91dCA9IChldmVudCkgPT4geyB0aGlzLm91dChldmVudCwga2V5KSB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmVsZW1lbnQuYXBwZW5kQ2hpbGQodGhpcy5ub3RjaClcbiAgICAgICAgdGhpcy5wYXJlbnQuYXBwZW5kQ2hpbGQodGhpcy5lbGVtZW50KVxuICAgICAgICB0aGlzLmluc2VydGVkTm9kZSA9IHRoaXMuZWxlbWVudFxuICAgICAgICBFbGVtZW50cy5zZXRTdHlsZSh0aGlzLmVsZW1lbnQsIHRoaXMuZGVmYXVsdFN0eWxlKCkpXG4gICAgICAgIHRoaXMubGF5b3V0KClcbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKiogRXhlY3V0ZSBhIG1lbnUgY29tbWFuZC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30ga2V5IC0gVGhlIHNlbGVjdGVkIGtleS5cbiAgICAgKi9cbiAgICBwZXJmb3JtKGtleSkge1xuICAgICAgICBsZXQgZnVuYyA9IHRoaXMuY29tbWFuZHNba2V5XVxuICAgICAgICBpZiAodGhpcy5hdXRvQ2xvc2UpIHtcbiAgICAgICAgICAgIHRoaXMuY2xvc2UoKVxuICAgICAgICB9XG4gICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgZnVuYy5jYWxsKClcbiAgICAgICAgfSwgMjApXG4gICAgfVxuXG4gICAgLyoqIFVwZGF0ZSB0aGUgbWVudSBpdGVtIGRlbm90ZWQgYnkga2V5LlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBrZXkgLSBUaGUgc2VsZWN0ZWQga2V5LlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gaGlnaGxpZ2h0IC0gU2hvdyB0aGUgaXRlbSBoaWdobGlnaHRlZC5cbiAgICAgKi9cbiAgICB1cGRhdGUoa2V5LCBoaWdobGlnaHQgPSBmYWxzZSkge1xuICAgICAgICBsZXQgdGV4dCA9IHRoaXMuaXRlbXNba2V5XVxuICAgICAgICB0ZXh0LnN0eWxlLmNvbG9yID0gKGhpZ2hsaWdodCkgPyB0aGlzLmhpZ2hsaWdodENvbG9yIDogdGhpcy5ub3JtYWxDb2xvclxuICAgIH1cblxuICAgIC8qKiBNb3VzZSBvdmVyIGhhbmTDtmVyLlxuICAgICAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50IC0gVGhlIG1vdXNlIGV2ZW50LlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0ga2V5IC0gVGhlIHNlbGVjdGVkIGtleS5cbiAgICAgKi9cbiAgICBvdmVyKGV2ZW50LCBrZXkpIHtcbiAgICAgICAgZm9yIChsZXQgayBpbiB0aGlzLml0ZW1zKSB7XG4gICAgICAgICAgICB0aGlzLnVwZGF0ZShrLCBrID09IGtleSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKiBNb3VzZSBvdXQgaGFuZMO2ZXIuXG4gICAgICogQHBhcmFtIHtFdmVudH0gZXZlbnQgLSBUaGUgbW91c2UgZXZlbnQuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBrZXkgLSBUaGUgc2VsZWN0ZWQga2V5LlxuICAgICAqL1xuICAgIG91dChldmVudCwga2V5KSB7XG4gICAgICAgIHRoaXMudXBkYXRlKGtleSlcbiAgICB9XG5cbiAgICAvKiogU2hvd3MgdGhlIFBvcHVwTWVudSB3aXRoIHRoZSBnaXZlbiBjb21tYW5kcyBhdCB0aGUgc3BlY2lmaWVkIHBvaW50LlxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjb21tYW5kcyAtIEEgZGljdCBvYmplY3Qgd2l0aCBjb21tYW5kIGxhYmVsIHN0cmluZ3MgYXMga2V5c1xuICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZCBjb21tYW5kIGZ1bmN0aW9ucyBhcyB2YWx1ZXMuXG4gICAgICogQHBhcmFtIHtQb2ludH0gcG9pbnQgLSBUaGUgcG9zaXRpb24gYXMgeCwgeSBjb29yZGluYXRlcyB7cHh9LlxuICAgICAqIEByZXR1cm4ge1BvcHVwTWVudX0gdGhpc1xuICAgICovXG4gICAgc2hvd0F0KGNvbW1hbmRzLCBwb2ludCkge1xuICAgICAgICB0aGlzLnNob3coY29tbWFuZHMpXG4gICAgICAgIHRoaXMucGxhY2VBdChwb2ludClcbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKiogQ29udmVuaWVudCBzdGF0aWMgbWV0aG9kcyB0byBzaG93IGFuZCByZXVzZSBhIFBvcHVwTWVudSBpbXBsZW1lbnRlZFxuICAgICAqIGFzIGEgY2xhc3MgdmFyaWFibGUuXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGNvbW1hbmRzIC0gQSBkaWN0IG9iamVjdCB3aXRoIGNvbW1hbmQgbGFiZWwgc3RyaW5ncyBhcyBrZXlzXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5kIGNvbW1hbmQgZnVuY3Rpb25zIGFzIHZhbHVlcy5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBwb2ludCAtIFRoZSBwb3NpdGlvbiBhcyB4LCB5IGNvb3JkaW5hdGVzIHtweH0uXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGZvbnRTaXplIC0gRGVzY3JpYmVzIHRoZSBmb250IHNpemUgYXMgQ1NTIHZhbHVlXG4gICAgICogQHBhcmFtIHtudW1iZXIgfHwgc3RyaW5nfSBwYWRkaW5nIC0gRGVzY3JpYmVzIHRoZSBwYWRkaW5nIGFzIENTUyB2YWx1ZVxuICAgICAqIEBwYXJhbSB7bnVtYmVyIHx8IHN0cmluZ30gbm90Y2hTaXplIC0gRGVzY3JpYmVzIHRoZSBzaXplIG9mIHRoZSBub3RjaCAoY2FsbG91dCkgYXMgQ1NTIHZhbHVlXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGhpZ2hsaWdodENvbG9yICAtIFRoZSBjb2xvciBvZiBoaWdobGlnaHRlZCBtZW51IGl0ZW1zIGFzIENTUyB2YWx1ZVxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBiYWNrZ3JvdW5kQ29sb3IgIC0gVGhlIGNvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kIGFzIENTUyB2YWx1ZVxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBub3JtYWxDb2xvciAgLSBUaGUgY29sb3Igb2Ygbm9ybWFsIG1lbnUgaXRlbXMgYXMgQ1NTIHZhbHVlXG4gICAgICogQHBhcmFtIHtib29sZWFufSBhdXRvQ2xvc2UgIC0gQXV0b2Nsb3NlIHRoZSBtZW51IGFmdGVyIHNlbGVjdGluZyBhbiBpdGVtXG4gICAgICovXG4gICAgc3RhdGljIG9wZW4oY29tbWFuZHMsIHBvaW50LCB7IHBhcmVudCA9IG51bGwsXG4gICAgICAgIGNvbnRleHQgPSB3aW5kb3csXG4gICAgICAgIGZvbnRTaXplID0gJzFlbScsXG4gICAgICAgIGZvbnRGYW1pbHkgPSAnQXJpYWwnLFxuICAgICAgICBwYWRkaW5nID0gMTYsXG4gICAgICAgIHpJbmRleCA9IDEsXG4gICAgICAgIHNwYWNpbmcgPSAnMHB4JyxcbiAgICAgICAgc3dpdGNoUG9zID0gZmFsc2UsXG4gICAgICAgIG5vdGNoU2l6ZSA9IDEwLFxuICAgICAgICBtYXhXaWR0aCA9IDgwMCxcbiAgICAgICAga2VlcFdpdGhpbiA9IG51bGwsXG4gICAgICAgIGJhY2tncm91bmRDb2xvciA9ICcjRUVFJyxcbiAgICAgICAgbm9ybWFsQ29sb3IgPSAnIzQ0NCcsXG4gICAgICAgIGF1dG9DbG9zZSA9IHRydWUgfSA9IHt9KSB7XG5cbiAgICAgICAgbGV0IHJlZ2lzdGVyZWQgPSBQb3BwYWJsZS5nZXQoY29udGV4dClcbiAgICAgICAgaWYgKHJlZ2lzdGVyZWQpIHtcbiAgICAgICAgICAgIHRoaXMuY2xvc2VQb3B1cCgpXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmxvZyhcIm9wZW5cIiwgcG9pbnQpXG4gICAgICAgIGxldCBub3RjaFBvc2l0aW9uID0gKHBvaW50LnkgPCA1MCAmJiBzd2l0Y2hQb3MpID8gJ3RvcENlbnRlcicgOiAnYm90dG9tQ2VudGVyJ1xuICAgICAgICBsZXQgcG9wdXAgPSBuZXcgUG9wdXBNZW51KHtcbiAgICAgICAgICAgIHBhcmVudCwgZm9udFNpemUsIHBhZGRpbmcsIHpJbmRleCwgc3BhY2luZywgc3dpdGNoUG9zLCBub3RjaFNpemUsXG4gICAgICAgICAgICBub3RjaFBvc2l0aW9uLFxuICAgICAgICAgICAgbWF4V2lkdGgsIGJhY2tncm91bmRDb2xvciwgbm9ybWFsQ29sb3IsXG4gICAgICAgICAgICBub3RjaFBvc2l0aW9uLCBrZWVwV2l0aGluLCBhdXRvQ2xvc2VcbiAgICAgICAgfSlcbiAgICAgICAgcG9wdXAuc2hvd0F0KGNvbW1hbmRzLCBwb2ludClcbiAgICAgICAgcG9wdXAucmVnaXN0ZXIoY29udGV4dClcbiAgICAgICAgcG9wdXAuY2xvc2VFdmVudExpc3RlbmVyID0gKGUpID0+IHtcbiAgICAgICAgICAgIGlmICh0aGlzLmV2ZW50T3V0c2lkZShlKSlcbiAgICAgICAgICAgICAgICB0aGlzLmNsb3NlUG9wdXAoY29udGV4dClcbiAgICAgICAgfVxuICAgICAgICBpZiAoYXV0b0Nsb3NlKSB7XG4gICAgICAgICAgICBjb250ZXh0LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIHBvcHVwLmNsb3NlRXZlbnRMaXN0ZW5lciwgdHJ1ZSlcbiAgICAgICAgICAgIGNvbnRleHQuYWRkRXZlbnRMaXN0ZW5lcigndG91Y2hzdGFydCcsIHBvcHVwLmNsb3NlRXZlbnRMaXN0ZW5lciwgdHJ1ZSlcbiAgICAgICAgICAgIGNvbnRleHQuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcmRvd24nLCBwb3B1cC5jbG9zZUV2ZW50TGlzdGVuZXIsIHRydWUpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgZXZlbnRPdXRzaWRlKGUpIHtcbiAgICAgICAgcmV0dXJuICFFbGVtZW50cy5oYXNDbGFzcyhlLnRhcmdldCwgJ3BvcHVwTWVudUl0ZW0nKVxuICAgIH1cblxuICAgIC8qKiBDb252ZW5pZW50IHN0YXRpYyBtZXRob2RzIHRvIGNsb3NlIHRoZSBQb3B1cE1lbnUgaW1wbGVtZW50ZWRcbiAgICAgKiBhcyBhIGNsYXNzIHZhcmlhYmxlLlxuICAgICAqL1xuICAgIHN0YXRpYyBjbG9zZVBvcHVwKGNvbnRleHQ9d2luZG93KSB7XG4gICAgICAgIGxldCByZWdpc3RlcmVkID0gUG9wcGFibGUuZ2V0KGNvbnRleHQpXG4gICAgICAgIGlmIChyZWdpc3RlcmVkKSB7XG4gICAgICAgICAgICByZWdpc3RlcmVkLmNsb3NlKClcbiAgICAgICAgICAgIGNvbnRleHQucmVtb3ZlRXZlbnRMaXN0ZW5lcignbW91c2Vkb3duJywgcmVnaXN0ZXJlZC5jbG9zZUV2ZW50TGlzdGVuZXIpXG4gICAgICAgICAgICBjb250ZXh0LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3RvdWNoc3RhcnQnLCByZWdpc3RlcmVkLmNsb3NlRXZlbnRMaXN0ZW5lcilcbiAgICAgICAgICAgIGNvbnRleHQucmVtb3ZlRXZlbnRMaXN0ZW5lcigncG9pbnRlcmRvd24nLCByZWdpc3RlcmVkLmNsb3NlRXZlbnRMaXN0ZW5lcilcbiAgICAgICAgfVxuICAgIH1cbn1cbiIsImltcG9ydCB7UG9pbnRzfSBmcm9tICcuL3V0aWxzLmpzJ1xuaW1wb3J0IHtDYXBhYmlsaXRpZXN9IGZyb20gJy4vY2FwYWJpbGl0aWVzLmpzJ1xuXG5leHBvcnQgY2xhc3MgRnJhbWVDb250YWluZXIge1xuXG4gICAgY29uc3RydWN0b3IoZWxlbWVudCkge1xuICAgICAgICB0aGlzLmVsZW1lbnQgPSBlbGVtZW50XG4gICAgICAgIHRoaXMuZGVsZWdhdGUgPSBuZXcgSW50ZXJhY3Rpb25NYXBwZXIoZWxlbWVudCwgdGhpcyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7IG1vdXNlV2hlZWxFbGVtZW50OiB3aW5kb3d9KVxuICAgIH1cblxuICAgIGNhcHR1cmUoZXZlbnQpIHtcbiAgICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG5cbiAgICBmaW5kVGFyZ2V0KGV2ZW50LCBsb2NhbCwgZ2xvYmFsKSB7XG4gICAgICAgIGxldCBmb3VuZCA9IGRvY3VtZW50LmVsZW1lbnRGcm9tUG9pbnQoZ2xvYmFsLngsIGdsb2JhbC55KVxuICAgICAgICBsZXQgaWZyYW1lID0gZm91bmQucXVlcnlTZWxlY3RvcignaWZyYW1lJylcbiAgICAgICAgaWYgKGlmcmFtZSkge1xuICAgICAgICAgICAgbGV0IHAgPSBQb2ludHMuZnJvbVBhZ2VUb05vZGUoZm91bmQsIGdsb2JhbClcbiAgICAgICAgICAgIGxldCBkb2MgPSBpZnJhbWUuY29udGVudFdpbmRvdy5kb2N1bWVudFxuICAgICAgICAgICAgbGV0IHRhcmdldCA9IGRvYy5lbGVtZW50RnJvbVBvaW50KHAueCwgcC55KVxuICAgICAgICAgICAgaWYgKHRhcmdldCAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ2lmcmFtZSBlbGVtZW50JywgdGFyZ2V0KVxuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgRnJhbWVUYXJnZXQoaWZyYW1lLCB0YXJnZXQpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBGcmFtZVRhcmdldCB7XG5cbiAgICBjb25zdHJ1Y3RvcihmcmFtZSwgdGFyZ2V0LCBkZWJ1Zz1mYWxzZSkge1xuICAgICAgICB0aGlzLmZyYW1lID0gZnJhbWVcbiAgICAgICAgdGhpcy50YXJnZXQgPSB0YXJnZXRcbiAgICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnXG4gICAgfVxuXG4gICAgY2FwdHVyZShldmVudCkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgIH1cblxuICAgIHNpbXVsYXRlTW91c2VFdmVudCh0eXBlLCBwb2ludCkge1xuICAgICAgICBsZXQgcCA9IFBvaW50cy5mcm9tUGFnZVRvTm9kZSh0aGlzLmZyYW1lLCBwb2ludClcbiAgICAgICAgbGV0IGV2ZW50ID0gbmV3IE1vdXNlRXZlbnQodHlwZSwge1xuICAgICAgICAgICAgdmlldzogdGhpcy5mcmFtZS5jb250ZW50V2luZG93LFxuICAgICAgICAgICAgYnViYmxlczogdHJ1ZSxcbiAgICAgICAgICAgIGNhbmNlbGFibGU6IHRydWUsXG4gICAgICAgICAgICBjbGllbnRYOiBwLngsXG4gICAgICAgICAgICBjbGllbnRZOiBwLnl9KVxuICAgICAgICB0aGlzLnRhcmdldC5kaXNwYXRjaEV2ZW50KGV2ZW50KVxuICAgIH1cblxuICAgIGNyZWF0ZVRvdWNoTGlzdChwb2ludE1hcCkge1xuICAgICAgICBsZXQgdG91Y2hlcyA9IFtdXG4gICAgICAgIGxldCBkb2MgPSB0aGlzLmZyYW1lLmNvbnRlbnRXaW5kb3cuZG9jdW1lbnRcbiAgICAgICAgZm9yKGxldCBrZXkgb2YgcG9pbnRNYXAua2V5cygpKSB7XG4gICAgICAgICAgICBsZXQgcG9pbnQgPSBwb2ludE1hcC5nZXQoa2V5KVxuICAgICAgICAgICAgbGV0IHAgPSBQb2ludHMuZnJvbVBhZ2VUb05vZGUodGhpcy5mcmFtZSwgcG9pbnQpXG4gICAgICAgICAgICBsZXQgdG91Y2hUYXJnZXQgPSBkb2MuZWxlbWVudEZyb21Qb2ludChwLngsIHAueSlcbiAgICAgICAgICAgIGxldCB0b3VjaCA9IG5ldyBUb3VjaCh1bmRlZmluZWQsIHRvdWNoVGFyZ2V0LCBrZXksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcC54LCBwLnksIHAueCwgcC55KVxuICAgICAgICAgICAgdG91Y2hlcy5wdXNoKHRvdWNoKVxuICAgICAgICB9XG4gICAgICAgcmV0dXJuIG5ldyBUb3VjaExpc3QoLi4udG91Y2hlcylcbiAgICB9XG5cbiAgICBzaW11bGF0ZVRvdWNoRXZlbnRDaHJvbWUodHlwZSwgcG9pbnQsIHBvaW50TWFwKSB7XG4gICAgICAgIGxldCBkb2MgPSB0aGlzLmZyYW1lLmNvbnRlbnRXaW5kb3cuZG9jdW1lbnRcbiAgICAgICAgbGV0IHAgPSBQb2ludHMuZnJvbVBhZ2VUb05vZGUodGhpcy5mcmFtZSwgcG9pbnQpXG4gICAgICAgIGxldCB0b3VjaFRhcmdldCA9IGRvYy5lbGVtZW50RnJvbVBvaW50KHAueCwgcC55KVxuICAgICAgICBjb25zdCB0b3VjaE9iaiA9IG5ldyBUb3VjaCh7XG4gICAgICAgICAgICBpZGVudGlmaWVyOiBEYXRlLm5vdygpLFxuICAgICAgICAgICAgdGFyZ2V0OiB0b3VjaFRhcmdldCxcbiAgICAgICAgICAgIGNsaWVudFg6IHAueCxcbiAgICAgICAgICAgIGNsaWVudFk6IHAueSxcbiAgICAgICAgICAgIHBhZ2VYOiBwLngsXG4gICAgICAgICAgICBwYWdlWTogcC55LFxuICAgICAgICAgICAgcmFkaXVzWDogMi41LFxuICAgICAgICAgICAgcmFkaXVzWTogMi41LFxuICAgICAgICAgICAgcm90YXRpb25BbmdsZTogMTAsXG4gICAgICAgICAgICBmb3JjZTogMC41LFxuICAgICAgICB9KTtcblxuICAgICAgICBjb25zdCB0b3VjaEV2ZW50ID0gbmV3IFRvdWNoRXZlbnQodHlwZSwge1xuICAgICAgICAgICAgY2FuY2VsYWJsZTogdHJ1ZSxcbiAgICAgICAgICAgIGJ1YmJsZXM6IHRydWUsXG4gICAgICAgICAgICB0b3VjaGVzOiBbdG91Y2hPYmpdLFxuICAgICAgICAgICAgdGFyZ2V0VG91Y2hlczogW3RvdWNoT2JqXSxcbiAgICAgICAgICAgIGNoYW5nZWRUb3VjaGVzOiBbdG91Y2hPYmpdLFxuICAgICAgICAgICAgc2hpZnRLZXk6IGZhbHNlLFxuICAgICAgICB9KTtcbiAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKFwic2ltdWxhdGVUb3VjaEV2ZW50Q2hyb21lXCIsIHRvdWNoRXZlbnQpXG4gICAgICAgIHRoaXMudGFyZ2V0LmRpc3BhdGNoRXZlbnQodG91Y2hFdmVudCk7XG4gICAgfVxuXG4gICAgc2ltdWxhdGVUb3VjaEV2ZW50U2FmYXJpKHR5cGUsIHBvaW50LCBwb2ludE1hcCwgdG91Y2hFdmVudEtleT0ndGFyZ2V0VG91Y2hlcycpIHtcbiAgICAgICAgbGV0IHAgPSBQb2ludHMuZnJvbVBhZ2VUb05vZGUodGhpcy5mcmFtZSwgcG9pbnQpXG4gICAgICAgIGxldCBkYXRhID0geyB2aWV3OiB0aGlzLmZyYW1lLmNvbnRlbnRXaW5kb3csXG4gICAgICAgICAgICBidWJibGVzOiB0cnVlLFxuICAgICAgICAgICAgY2FuY2VsYWJsZTogdHJ1ZSxcbiAgICAgICAgICAgIGNsaWVudFg6IHAueCxcbiAgICAgICAgICAgIGNsaWVudFk6IHAueX1cbiAgICAgICAgZGF0YVt0b3VjaEV2ZW50S2V5XSA9IHRoaXMuY3JlYXRlVG91Y2hMaXN0KHBvaW50TWFwKVxuICAgICAgICBsZXQgZXZlbnQgPSBuZXcgVG91Y2hFdmVudCh0eXBlLCBkYXRhKVxuICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coXCJzaW11bGF0ZVRvdWNoRXZlbnRDaHJvbWVcIiwgdG91Y2hFdmVudClcbiAgICAgICAgdGhpcy50YXJnZXQuZGlzcGF0Y2hFdmVudChldmVudClcbiAgICB9XG5cbiAgICBzaW11bGF0ZVRvdWNoRXZlbnQodHlwZSwgcG9pbnQsIHBvaW50TWFwLCB0b3VjaEV2ZW50S2V5PSd0YXJnZXRUb3VjaGVzJykge1xuICAgICAgICBpZiAoQ2FwYWJpbGl0aWVzLmlzU2FmYXJpKSB7XG4gICAgICAgICAgICB0aGlzLnNpbXVsYXRlVG91Y2hFdmVudFNhZmFyaSh0eXBlLCBwb2ludCwgcG9pbnRNYXAsIHRvdWNoRXZlbnRLZXkpXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnNpbXVsYXRlVG91Y2hFdmVudENocm9tZSh0eXBlLCBwb2ludCwgcG9pbnRNYXApXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpc01vdXNlTGlrZUV2ZW50KGV2ZW50KSB7XG4gICAgICAgIHJldHVybiBldmVudC50eXBlLnN0YXJ0c1dpdGgoJ21vdXNlJykgfHwgZXZlbnQudHlwZS5zdGFydHNXaXRoKCdwb2ludGVyJylcbiAgICB9XG5cbiAgICBvblN0YXJ0KGV2ZW50LCBpbnRlcmFjdGlvbikge1xuICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ29uU3RhcnQnLCB0aGlzLmZyYW1lLnBhcmVudE5vZGUpXG4gICAgICAgIGZvcihsZXQgW2tleSwgcG9pbnRdIG9mIGludGVyYWN0aW9uLmN1cnJlbnQuZW50cmllcygpKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5pc01vdXNlTGlrZUV2ZW50KGV2ZW50KSkge1xuICAgICAgICAgICAgICAgIHRoaXMuc2ltdWxhdGVNb3VzZUV2ZW50KCdtb3VzZWRvd24nLCBwb2ludClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMuc2ltdWxhdGVUb3VjaEV2ZW50KCd0b3VjaHN0YXJ0JywgcG9pbnQsXG4gICAgICAgICAgICAgICAgICAgIGludGVyYWN0aW9uLmN1cnJlbnQpXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvbk1vdmUoZXZlbnQsIGludGVyYWN0aW9uKSB7XG4gICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygnb25Nb3ZlJylcbiAgICAgICAgZm9yKGxldCBba2V5LCBwb2ludF0gb2YgaW50ZXJhY3Rpb24uY3VycmVudC5lbnRyaWVzKCkpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmlzTW91c2VMaWtlRXZlbnQoZXZlbnQpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zaW11bGF0ZU1vdXNlRXZlbnQoJ21vdXNlbW92ZScsIHBvaW50KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zaW11bGF0ZVRvdWNoRXZlbnQoJ3RvdWNobW92ZScsIHBvaW50LFxuICAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGlvbi5jdXJyZW50KVxuICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25FbmQoZXZlbnQsIGludGVyYWN0aW9uKSB7XG4gICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygnb25FbmQnKVxuICAgICAgICBmb3IobGV0IFtrZXksIHBvaW50XSBvZiBpbnRlcmFjdGlvbi5jdXJyZW50LmVudHJpZXMoKSkge1xuICAgICAgICAgICAgaWYgKHRoaXMuaXNNb3VzZUxpa2VFdmVudChldmVudCkpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnNpbXVsYXRlTW91c2VFdmVudCgnbW91c2VlbmQnLCBwb2ludClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMuc2ltdWxhdGVUb3VjaEV2ZW50KCd0b3VjaGVuZCcsIHBvaW50LFxuICAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGlvbi5lbmRlZCwgJ2NoYW5nZWRUb3VjaGVzJylcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbn1cbiIsImV4cG9ydCBjbGFzcyBJbnNwZWN0IHtcbiAgICAvLyBDb2RlIGluc3BlY3Rpb24gZnVuY3Rpb25zXG5cbiAgICBzdGF0aWMgYWxsU2NyaXB0U291cmNlcygpXG4gICAge1xuICAgICAgICBsZXQgc291cmNlcyA9IFtdXG4gICAgICAgIGxldCBzY3JpcHRzID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3NjcmlwdCcpXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc2NyaXB0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgY29uc29sZS5kaXIoc2NyaXB0c1tpXSlcbiAgICAgICAgICAgIHNvdXJjZXMucHVzaChzY3JpcHRzW2ldKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBzb3VyY2VzXG4gICAgfVxufVxuIiwiLyogZ2xvYmFscyBQb3dlcjAgKi9cbi8qIGVzbGludCBuby1jb25zb2xlOiBbXCJlcnJvclwiLCB7IGFsbG93OiBbXCJsb2dcIiwgXCJpbmZvXCIsIFwid2FyblwiLCBcImVycm9yXCJdIH1dICovXG5cbi8qKlxuICogSW1wb3J0c1xuICovXG5cbi8qKlxuICogQSBjbGFzcyB0aGF0IGNhbiBiZSB1c2VkIHRvIHBlcmZvcm0gYXV0b21hdGVkIHVzZXIgaW50ZXJmYWNlIHRlc3RzLlxuICpcbiAqIEBleGFtcGxlXG4gKiAvLyBDcmVhdGUgdGhlIFVJVGVzdCBvYmplY3RcbiAqIGNvbnN0IHRlc3QgPSBuZXcgVUlUZXN0KHtcbiAqICAgICB0aW1lU2NhbGU6IDJcbiAqIH0pXG4gKlxuICogLy8gQWRkIGFuIGFjdGlvbiB0byB0aGUgdGVzdCBjYXNlXG4gKiB0ZXN0LnRhcChidXR0b24sIHtldmVudFR5cGU6ICdjbGljayd9KVxuICogXG4gKiAvLyBTdGFydCB0aGUgdGVzdCBjYXNlXG4gKiB0ZXN0LnN0YXJ0KClcbiAqXG4gKiBAY2xhc3NcbiAqIEBzZWUge0BsaW5rIGh0dHBzOi8vd3d3Lml3bS10dWViaW5nZW4uZGUvaXdtYnJvd3Nlci9saWIvcGl4aS91aXRlc3QuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBVSVRlc3Qge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhbiBVSVRlc3QuXG4gICAgICogXG4gICAgICogSW4gdGhlIGJhY2tncm91bmQsIHRoZSBjbGFzcyBVSVRlc3QgdXNlcyB0aGUgR3JlZW5zb2NrIFRpbWVsaW5lTWF4IGNsYXNzLiBUaGUgb3B0cyBvYmplY3QgaXMgcGFzc2VkIGRpcmVjdGx5IHRvIHRoZSBUaW1lbGluZU1heCBjbGFzcywgc28gaXQgY2FuIHVzZSBhbnkga2V5IHRoYXQgdXNlcyB0aGUgVGltZWxpbmVNYXggY2xhc3MuXG4gICAgICpcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0aGUgYmVoYXZpb3VyIG9mIHRoZSB0ZXN0IGNhc2UuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnRpbWVTY2FsZT0xXSAtIFRoZSBzcGVlZCBhdCB3aGljaCB0aGUgdGVzdCBzaG91bGQgcnVuLCBzZWUgaHR0cHM6Ly9ncmVlbnNvY2suY29tL2RvY3MvVGltZWxpbmVNYXgvdGltZVNjYWxlKCkuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLmV2ZW50VHlwZT1hdXRvXSAtIFRoZSB0eXBlIG9mIGV2ZW50cyB3aGljaCBzaG91bGQgYmUgdXNlZC4gUG9zc2libGUgdmFsdWVzOiBwb2ludGVyLCB0b3VjaCwgbW91c2UsIGF1dG8uIElmIHNldCB0byBhdXRvLCB0aGUgZXZlbnRUeXBlIGlzIHNldCBkZXBlbmRpbmcgb24gdGhlIHN1cHBvcnQgb2YgdGhlIGJyb3dzZXIgdXNlZC5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmRlYnVnPWZhbHNlXSAtIElmIHNldCB0byB0cnVlLCBtdWx0aXBsZSBpbmZvcm1hdGlvbnMgd2lsbCBiZSBwcmludCB0byB0aGUgY29uc29sZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZGVmYXVsdEludGVydmFsXSAtIFRoZSBpbnRlcnZhbCB1c2VkIHdoZW4gbm8gYWN0aW9uIGlzIHNwZWNpZmllZCBmb3IgYW4gYWN0aW9uLlxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKG9wdHMgPSB7fSkge1xuXG4gICAgICAgIHRoaXMub3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIHtcbiAgICAgICAgICAgIHRpbWVTY2FsZTogMSxcbiAgICAgICAgICAgIGV2ZW50VHlwZTogJ2F1dG8nLFxuICAgICAgICAgICAgZGVidWc6IGZhbHNlLFxuICAgICAgICAgICAgZGVmYXVsdEludGVydmFsOiBudWxsXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgLy8gdGltZWxpbmVcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLl90aW1lbGluZSA9IG5ldyBUaW1lbGluZU1heChPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBwYXVzZWQ6IHRydWVcbiAgICAgICAgfSwgdGhpcy5vcHRzKSlcbiAgICAgICAgdGhpcy5fdGltZWxpbmUudGltZVNjYWxlKHRoaXMub3B0cy50aW1lU2NhbGUpXG5cbiAgICAgICAgLy8gZXZlbnRUeXBlXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgaWYgKHRoaXMub3B0cy5ldmVudFR5cGUgPT09ICdhdXRvJykge1xuICAgICAgICAgICAgaWYgKHdpbmRvdy5Qb2ludGVyRXZlbnQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9wdHMuZXZlbnRUeXBlID0gJ3BvaW50ZXInXG4gICAgICAgICAgICB9IGVsc2UgaWYgKCdvbnRvdWNoc3RhcnQnIGluIHdpbmRvdykge1xuICAgICAgICAgICAgICAgIHRoaXMub3B0cy5ldmVudFR5cGUgPSAndG91Y2gnXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMub3B0cy5ldmVudFR5cGUgPSAnbW91c2UnXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5vcHRzLmRlYnVnKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgRXZlbnQgdHlwZTogJHt0aGlzLm9wdHMuZXZlbnRUeXBlfWApXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl90aW1lbGluZVBvc2l0aW9ucyA9IFswXVxuICAgICAgICB0aGlzLl9hY3Rpb25zID0gMFxuXG4gICAgICAgIC8vIHNldHVwXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5zZXR1cCgpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2VuZXJhdGVzIHRoZSByZXF1aXJlZCBzdHJ1Y3R1cmUuXG4gICAgICpcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge1VJVGVzdH0gQSByZWZlcmVuY2UgdG8gdGhlIFVJVGVzdCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgc2V0dXAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0cyB0aGUgR3JlZW5zb2NrIFRpbWVsaW5lTWF4IG9iamVjdCwgdXNlZCBpbiB0aGUgYmFja2dyb3VuZCBvZiBVSVRlc3QuXG4gICAgICpcbiAgICAgKiBAbWVtYmVyIHtUaW1lbGluZU1heH1cbiAgICAgKi9cbiAgICBnZXQgdGltZWxpbmUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl90aW1lbGluZVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFN0YXJ0cyB0aGUgdGVzdCBjYXNlIGFuZCBleGVjdXRlcyB0aGUgY29ycmVzcG9uZGluZyBzdGF0ZW1lbnRzIGluIHRoZSBzcGVjaWZpZWQgb3JkZXIuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtVSVRlc3R9IEEgcmVmZXJlbmNlIHRvIHRoZSBVSVRlc3QgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHN0YXJ0KCkge1xuICAgICAgICB0aGlzLl90aW1lbGluZS5wbGF5KClcbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTdG9wcyB0aGUgdGVzdCBjYXNlIGFuZCBzdG9wcyBleGVjdXRpbmcgYW55IGZ1cnRoZXIgaW5zdHJ1Y3Rpb25zLlxuICAgICAqXG4gICAgICogQHJldHVybiB7VUlUZXN0fSBBIHJlZmVyZW5jZSB0byB0aGUgVUlUZXN0IGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzdG9wKCkge1xuICAgICAgICB0aGlzLl90aW1lbGluZS5wYXVzZSgpXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2xlYXJzIGFsbCBpbnN0cnVjdGlvbnMgb2YgdGhlIHRlc3QgY2FzZS5cbiAgICAgKiBcbiAgICAgKiBAcmV0dXJuIHtVSVRlc3R9IEEgcmVmZXJlbmNlIHRvIHRoZSBVSVRlc3QgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGNsZWFyKCkge1xuICAgICAgICB0aGlzLl90aW1lbGluZS5jbGVhcigpXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVzdGFydHMgdGhlIHRlc3QgY2FzZS5cbiAgICAgKiBcbiAgICAgKiBAcmV0dXJuIHtVSVRlc3R9IEEgcmVmZXJlbmNlIHRvIHRoZSBVSVRlc3QgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHJlc3RhcnQoKSB7XG4gICAgICAgIHRoaXMuX3RpbWVsaW5lLnJlc3RhcnQoKVxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEV4ZWN1dGVzIGEgdGFwIGV2ZW50IChwb2ludGVyZG93biwgcG9pbnRlcnVwKSBvbiBhIHNwZWNpZmljIGVsZW1lbnQuXG4gICAgICogXG4gICAgICogQHBhcmFtIHtIVE1MRWxlbWVudHxzdHJpbmd9IGVsZW1lbnQgLSBUaGUgSFRNTCBlbGVtZW50IG9uIHdoaWNoIHRoZSBldmVudCBpcyB0byBiZSBleGVjdXRlZCwgZS5nLiBidXR0b24sIGRvY3VtZW50LCBoMiwgY2FudmFzLCBldGMuIG9yIGFuIHNlbGVjdG9yIHN0cmluZy4gSWYgYSBzZWxlY3RvciBoYXMgYmVlbiBzcGVjaWZpZWQsIGl0IGlzIGV2YWx1YXRlZCBpbW1lZGlhdGVseSBiZWZvcmUgdGhlIGV2ZW50IGlzIGNhbGxlZCB1c2luZyB0aGUgcXVlcnlTZWxlY3RvciBtZXRob2QuXG4gICAgICogQHBhcmFtIHtudW1iZXJbXXxvYmplY3R8UElYSS5EaXNwbGF5T2JqZWN0fSBbcG9zaXRpb249VGhlIGNlbnRlciBvZiB0aGUgZWxlbWVudC5dIC0gVGhlIGxvY2FsIHBvc2l0aW9uIG9mIHRoZSBldmVudCBpbiB0aGUgY29udGV4dCBvZiB0aGUgc3BlY2lmaWVkIEhUTUwgZWxlbWVudC4gSWYgbm8gcG9zaXRpb24gaXMgc3BlY2lmaWVkLCB0aGUgY2VudGVyIG9mIHRoZSBIVE1MIGVsZW1lbnQgaXMgdXNlZC4gVGhlIHBvc2l0aW9uIGNhbiBiZSBzcGVjaWZpZWQgYXMgYW4gYXJyYXkgb2YgbnVtYmVycywgYXMgYW4gb2JqZWN0IHdpdGggdGhlIHR3byBwcm9wZXJ0aWVzIHggYW5kIHksIG9yIGFzIGEgUElYSS5EaXNwbGF5IG9iamVjdC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW3RpbWVsaW5lUG9zaXRpb249T25lIHNlY29uZCBhZnRlciB0aGUgbGFzdCBhY3Rpb24uXSAtIFRoZSBwb3NpdGlvbiBpbiBzZWNvbmRzIHdoZW4gdGhlIGV2ZW50IHNob3VsZCBiZSB0cmlnZ2VyZWQsIHNlZSBzaHR0cHM6Ly9ncmVlbnNvY2suY29tL2RvY3MvVGltZWxpbmVNYXgvYWRkQ2FsbGJhY2soKS5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0aGUgYmVoYXZpb3VyIG9mIHRoZSBhY3Rpb24uXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbn0gW29wdHMub25TdGFydF0gLSBBIGZ1bmN0aW9uIHRoYXQgcnVucyBhZnRlciB0aGUgZmlyc3QgZXZlbnQgaXMgZmlyZWQuIFdpbGwgbm90IGJlIGZpcmVkIGlmIG9ubHkgb25lIGV2ZW50IGlzIHJ1bm5pbmcgKGZvciBleGFtcGxlLCBhIGNsaWNrIGV2ZW50KS4gUmVjZWl2ZXMgdGhlIGZpcmVkIGV2ZW50IG9iamVjdCBhcyB0aGUgZmlyc3QgcGFyYW1ldGVyLiBUaGUgdGVzdCBjYXNlIChVSVRlc3QpIGlzIGJvdW5kIHRvIHRoaXMuXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbn0gW29wdHMub25Db21wbGV0ZV0gLSBBIGZ1bmN0aW9uIHRoYXQgcnVucyBhZnRlciB0aGUgc2Vjb25kIGV2ZW50IGlzIGZpcmVkLiBBbHdheXMgZmlyZWQsIGV2ZW4gaWYgb25seSBvbmUgZXZlbnQgaXMgcnVubmluZyAoZm9yIGV4YW1wbGUsIGEgY2xpY2sgZXZlbnQpLiBSZWNlaXZlcyB0aGUgZmlyZWQgZXZlbnQgb2JqZWN0IGFzIHRoZSBmaXJzdCBwYXJhbWV0ZXIuIFRoZSB0ZXN0IGNhc2UgKFVJVGVzdCkgaXMgYm91bmQgdG8gdGhpcy5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbb3B0cy5ldmVudFR5cGVzPVsncG9pbnRlcmRvd24nLCAncG9pbnRlcnVwJ11dIC0gVGhlIGV2ZW50IHR5cGVzIHRvIHVzZS4gSWYgbm8gdHlwZXMgYXJlIHNwZWNpZmllZCwgdGhlIGV2ZW50IHR5cGVzIHNwZWNpZmllZCBpbiB0aGUgVUlUZXN0IGNvbnN0cnVjdG9yIGFyZSB1c2VkIChvciBhdXRvIGlmIG5vdCBzcGVjaWZpZWQpLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5ldmVudFR5cGVdIC0gSWYgeW91IHdhbnQgdGhlIHRhcCBtZXRob2QgdG8gZmlyZSBvbmx5IG9uZSBldmVudCAoZm9yIGV4YW1wbGUsIGEgY2xpY2sgZXZlbnQpLCB5b3UgY2FuIHNwZWNpZnkgdGhlIG9wdHMuZXZlbnRUeXBlIHBhcmFtZXRlci4gSWYgZXZlbnRUeXBlIGlzIG5vdCBudWxsLCB0aGUgcGFyYW1ldGVyIG9wdHMuZXZlbnRUeXBlcyBpcyBpZ25vcmVkLlxuICAgICAqIEBwYXJhbSB7V2luZG93fEZyYW1lfSBbb3B0cy5jb250ZXh0PXdpbmRvd10gLSBUaGUgY29udGV4dCB3aXRoaW4gd2hpY2ggdGhlIG9wdGlvbmFsbHkgc3BlY2lmaWVkIGVsZW1lbnQgc2VsZWN0b3Igc2hvdWxkIGJlIGV4ZWN1dGVkLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuYnViYmxlcz10cnVlXSAtIFRoZSBFdmVudCBwcm9wZXJ0eSBidWJibGVzIGluZGljYXRlcyB3aGV0aGVyIHRoZSBldmVudCBidWJibGVzIHVwIHRocm91Z2ggdGhlIERPTSBvciBub3QuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5jYW5jZWxhYmxlPXRydWVdIC0gRXZlbnRzJyBjYW5jZWxhYmxlIHByb3BlcnR5IGluZGljYXRlcyBpZiB0aGUgZXZlbnQgY2FuIGJlIGNhbmNlbGVkLCBhbmQgdGhlcmVmb3JlIHByZXZlbnRlZCBhcyBpZiB0aGUgZXZlbnQgbmV2ZXIgaGFwcGVuZWQuIElmIHRoZSBldmVudCBpcyBub3QgY2FuY2VsYWJsZSwgdGhlbiBpdHMgY2FuY2VsYWJsZSBwcm9wZXJ0eSB3aWxsIGJlIGZhbHNlIGFuZCB0aGUgZXZlbnQgbGlzdGVuZXIgY2Fubm90IHN0b3AgdGhlIGV2ZW50IGZyb20gb2NjdXJyaW5nLlxuICAgICAqL1xuICAgIHRhcChlbGVtZW50LCBwb3NpdGlvbiwgdGltZWxpbmVQb3NpdGlvbiwgb3B0cyA9IHt9KSB7XG4gICAgICAgIFxuICAgICAgICAvLyBhcmd1bWVudHNcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBbcG9zaXRpb24sIHRpbWVsaW5lUG9zaXRpb24sIG9wdHNdID0gdGhpcy5yZW9yZGVyQXJndW1lbnRzKGFyZ3VtZW50cylcbiAgICAgICAgdGhpcy5fdGltZWxpbmVQb3NpdGlvbnMucHVzaCh0aW1lbGluZVBvc2l0aW9uKVxuXG4gICAgICAgIC8vIGRlYnVnXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgaWYgKHRoaXMub3B0cy5kZWJ1ZykgY29uc29sZS5sb2coJ3RhcCBwYXJhbXMnLCB7ZWxlbWVudCwgcG9zaXRpb24sIHRpbWVsaW5lUG9zaXRpb24sIG9wdHN9KVxuXG4gICAgICAgIC8vIG9wdHNcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBvcHRzID0gT2JqZWN0LmFzc2lnbih7fSwge1xuICAgICAgICAgICAgb25TdGFydDogbnVsbCxcbiAgICAgICAgICAgIG9uQ29tcGxldGU6IG51bGwsXG4gICAgICAgICAgICBldmVudFR5cGVzOiB0aGlzLnJlc29sdmVFdmVudHMoWydkb3duJywgJ3VwJ10pLFxuICAgICAgICAgICAgZXZlbnRUeXBlOiBudWxsLFxuICAgICAgICAgICAgY29udGV4dDogd2luZG93LFxuICAgICAgICAgICAgYnViYmxlczogdHJ1ZSxcbiAgICAgICAgICAgIGNhbmNlbGFibGU6IHRydWVcbiAgICAgICAgfSwgb3B0cylcblxuICAgICAgICBpZiAob3B0cy5ldmVudFR5cGUpIHtcbiAgICAgICAgICAgIG9wdHMuZXZlbnRUeXBlcyA9IG9wdHMuZXZlbnRUeXBlXG4gICAgICAgIH1cbiAgICAgICAgb3B0cy5ldmVudFR5cGVzID0gQXJyYXkuaXNBcnJheShvcHRzLmV2ZW50VHlwZXMpID8gb3B0cy5ldmVudFR5cGVzIDogW29wdHMuZXZlbnRUeXBlc11cblxuICAgICAgICAvLyB0aW1lbGluZVxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuX3RpbWVsaW5lLmFkZENhbGxiYWNrKHBvc2l0aW9uID0+IHtcblxuICAgICAgICAgICAgLy8gZWxlbWVudFxuICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgY29uc3QgZWxlbSA9IFV0aWwuZXh0cmFjdEVsZW1lbnQob3B0cy5jb250ZXh0LCBlbGVtZW50KVxuXG4gICAgICAgICAgICAvLyBwb3NpdGlvblxuICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgaWYgKHBvc2l0aW9uID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVjdCA9IGVsZW0uZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IFtyZWN0LndpZHRoIC8gMiwgcmVjdC5oZWlnaHQgLyAyXVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBjb29yZHNcbiAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgIGNvbnN0IGNvb3JkcyA9IFV0aWwuZXh0cmFjdFBvc2l0aW9uKHBvc2l0aW9uKVxuICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5kZWJ1ZykgY29uc29sZS5sb2coJ2xvY2FsIGNvb3JkcycsIGNvb3JkcylcblxuICAgICAgICAgICAgLy8gZXZlbnRUeXBlc1xuICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgaWYgKG9wdHMuZXZlbnRUeXBlcy5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgICAgICAgICBvcHRzLmV2ZW50VHlwZXMudW5zaGlmdChudWxsKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBldmVudCBvcHRzXG4gICAgICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgICAgICBjb25zdCBldmVudE9wdHMgPSB7YnViYmxlczogb3B0cy5idWJibGVzLCBjYW5jZWxhYmxlOiBvcHRzLmNhbmNlbGFibGV9XG5cbiAgICAgICAgICAgIGlmIChvcHRzLmV2ZW50VHlwZXNbMF0pIHtcblxuICAgICAgICAgICAgICAgIC8vIGNyZWF0ZSBhbmQgZGlzcGF0Y2ggZXZlbnRcbiAgICAgICAgICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgICAgICAgICAgY29uc3QgZXZlbnRTdGFydCA9IEV2ZW50LmNyZWF0ZShlbGVtLCBjb29yZHMsIG9wdHMuZXZlbnRUeXBlc1swXSwgZXZlbnRPcHRzKVxuICAgICAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuZGVidWcpIGNvbnNvbGUubG9nKCdkaXNwYXRjaCBldmVudCcsIGV2ZW50U3RhcnQpXG4gICAgICAgICAgICAgICAgZWxlbS5kaXNwYXRjaEV2ZW50KGV2ZW50U3RhcnQpXG5cbiAgICAgICAgICAgICAgICAvLyBvblN0YXJ0XG4gICAgICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgICAgIGlmIChvcHRzLm9uU3RhcnQpIHtcbiAgICAgICAgICAgICAgICAgICAgb3B0cy5vblN0YXJ0LmNhbGwodGhpcywgZXZlbnRTdGFydClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIGNyZWF0ZSBhbmQgZGlzcGF0Y2ggZXZlbnRcbiAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgIGNvbnN0IGV2ZW50Q29tcGxldGUgPSBFdmVudC5jcmVhdGUoZWxlbSwgY29vcmRzLCBvcHRzLmV2ZW50VHlwZXNbMV0sIGV2ZW50T3B0cylcbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuZGVidWcpIGNvbnNvbGUubG9nKCdkaXNwYXRjaCBldmVudCcsIGV2ZW50Q29tcGxldGUpXG4gICAgICAgICAgICBlbGVtLmRpc3BhdGNoRXZlbnQoZXZlbnRDb21wbGV0ZSlcblxuICAgICAgICAgICAgLy8gb25Db21wbGV0ZVxuICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgaWYgKG9wdHMub25Db21wbGV0ZSkge1xuICAgICAgICAgICAgICAgIG9wdHMub25Db21wbGV0ZS5jYWxsKHRoaXMsIGV2ZW50Q29tcGxldGUpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgfSwgdGltZWxpbmVQb3NpdGlvbiwgW3Bvc2l0aW9uXSlcblxuICAgICAgICB0aGlzLl9hY3Rpb25zKytcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEV4ZWN1dGVzIGEgcGFuIGV2ZW50IChwb2ludGVyZG93biwgcG9pbnRlcm1vdmUsIHBvaW50ZXJ1cCkgb24gYSBzcGVjaWZpYyBlbGVtZW50LlxuICAgICAqIFxuICAgICAqIEBwYXJhbSB7SFRNTEVsZW1lbnR8c3RyaW5nfSBlbGVtZW50IC0gVGhlIEhUTUwgZWxlbWVudCBvbiB3aGljaCB0aGUgZXZlbnQgaXMgdG8gYmUgZXhlY3V0ZWQsIGUuZy4gYnV0dG9uLCBkb2N1bWVudCwgaDIsIGNhbnZhcywgZXRjLiBvciBhbiBzZWxlY3RvciBzdHJpbmcuIElmIGEgc2VsZWN0b3IgaGFzIGJlZW4gc3BlY2lmaWVkLCBpdCBpcyBldmFsdWF0ZWQgaW1tZWRpYXRlbHkgYmVmb3JlIHRoZSBldmVudCBpcyBjYWxsZWQgdXNpbmcgdGhlIHF1ZXJ5U2VsZWN0b3IgbWV0aG9kLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyW118b2JqZWN0fFBJWEkuRGlzcGxheU9iamVjdH0gW3Bvc2l0aW9uPVRoZSBjZW50ZXIgb2YgdGhlIGVsZW1lbnQuXSAtIFRoZSBsb2NhbCBwb3NpdGlvbiBvZiB0aGUgZXZlbnQgaW4gdGhlIGNvbnRleHQgb2YgdGhlIHNwZWNpZmllZCBIVE1MIGVsZW1lbnQuIElmIG5vIHBvc2l0aW9uIGlzIHNwZWNpZmllZCwgdGhlIGNlbnRlciBvZiB0aGUgSFRNTCBlbGVtZW50IGlzIHVzZWQuIFRoZSBwb3NpdGlvbiBjYW4gYmUgc3BlY2lmaWVkIGFzIGFuIGFycmF5IG9mIG51bWJlcnMsIGFzIGFuIG9iamVjdCB3aXRoIHRoZSB0d28gcHJvcGVydGllcyB4IGFuZCB5LCBvciBhcyBhIFBJWEkuRGlzcGxheSBvYmplY3QuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFt0aW1lbGluZVBvc2l0aW9uPU9uZSBzZWNvbmQgYWZ0ZXIgdGhlIGxhc3QgYWN0aW9uLl0gLSBUaGUgcG9zaXRpb24gaW4gc2Vjb25kcyB3aGVuIHRoZSBldmVudCBzaG91bGQgYmUgdHJpZ2dlcmVkLCBzZWUgc2h0dHBzOi8vZ3JlZW5zb2NrLmNvbS9kb2NzL1RpbWVsaW5lTWF4L2FkZENhbGxiYWNrKCkuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzXSAtIEFuIG9wdGlvbnMgb2JqZWN0IHRvIHNwZWNpZnkgdGhlIGJlaGF2aW91ciBvZiB0aGUgYWN0aW9uLlxuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLm9uU3RhcnRdIC0gQSBmdW5jdGlvbiB0aGF0IHJ1bnMgYWZ0ZXIgdGhlIGZpcnN0IGV2ZW50IGlzIGZpcmVkLiBSZWNlaXZlcyB0aGUgZmlyZWQgZXZlbnQgb2JqZWN0IGFzIHRoZSBmaXJzdCBwYXJhbWV0ZXIuIFRoZSB0ZXN0IGNhc2UgKFVJVGVzdCkgaXMgYm91bmQgdG8gdGhpcy5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy5vblVwZGF0ZV0gLSBBIGZ1bmN0aW9uIHRoYXQgcnVucyBhZnRlciBlYWNoIGV4ZWN1dGlvbiBvZiB0aGUgc2Vjb25kIGV2ZW50LiBSZWNlaXZlcyB0aGUgZmlyZWQgZXZlbnQgb2JqZWN0IGFzIHRoZSBmaXJzdCBwYXJhbWV0ZXIuIFRoZSB0ZXN0IGNhc2UgKFVJVGVzdCkgaXMgYm91bmQgdG8gdGhpcy5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy5vbkNvbXBsZXRlXSAtIEEgZnVuY3Rpb24gdGhhdCBydW5zIGFmdGVyIHRoZSB0aGlyZCBldmVudCBpcyBmaXJlZC4gUmVjZWl2ZXMgdGhlIGZpcmVkIGV2ZW50IG9iamVjdCBhcyB0aGUgZmlyc3QgcGFyYW1ldGVyLiBUaGUgdGVzdCBjYXNlIChVSVRlc3QpIGlzIGJvdW5kIHRvIHRoaXMuXG4gICAgICogQHBhcmFtIHtudW1iZXJbXXxvYmplY3R8UElYSS5EaXNwbGF5T2JqZWN0fSBbb3B0cy50bz17eDogMCwgeTogMH1dIC0gVGhlIHRhcmdldCBvZiB0aGUgcGFuIHByb2Nlc3MuIFRoZSBwb3NpdGlvbiBjYW4gYmUgc3BlY2lmaWVkIGFzIGFuIGFycmF5IG9mIG51bWJlcnMsIGFzIGFuIG9iamVjdCB3aXRoIHRoZSB0d28gcHJvcGVydGllcyB4IGFuZCB5LCBvciBhcyBhIFBJWEkuRGlzcGxheSBvYmplY3QuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmR1cmF0aW9uPTFdIC0gVGhlIGR1cmF0aW9uIG9mIHRoZSBwYW4gYW5pbWF0aW9uIGluIHNlY29uZHMsIHNlZSBodHRwczovL2dyZWVuc29jay5jb20vZG9jcy9Ud2VlbkxpdGUvZHVyYXRpb24oKS5cbiAgICAgKiBAcGFyYW0ge0Vhc2V9IFtvcHRzLmVhc2U9UG93ZXIwLmVhc2VOb25lXSAtIFRoZSBlYXNpbmcgb2YgdGhlIHBhbiBhbmltYXRpb24sIHNlZSBodHRwczovL2dyZWVuc29jay5jb20vZG9jcy9FYXNpbmcuXG4gICAgICogQHBhcmFtIHtzdHJpbmdbXX0gW29wdHMuZXZlbnRUeXBlcz1bJ3BvaW50ZXJkb3duJywgJ3BvaW50ZXJtb3ZlJywgJ3BvaW50ZXJ1cCddXSAtIFRoZSBldmVudCB0eXBlcyB0byB1c2UuIElmIG5vIHR5cGVzIGFyZSBzcGVjaWZpZWQsIHRoZSBldmVudCB0eXBlcyBzcGVjaWZpZWQgaW4gdGhlIFVJVGVzdCBjb25zdHJ1Y3RvciBhcmUgdXNlZCAob3IgYXV0byBpZiBub3Qgc3BlY2lmaWVkKS5cbiAgICAgKiBAcGFyYW0ge1dpbmRvd3xGcmFtZX0gW29wdHMuY29udGV4dD13aW5kb3ddIC0gVGhlIGNvbnRleHQgd2l0aGluIHdoaWNoIHRoZSBvcHRpb25hbGx5IHNwZWNpZmllZCBlbGVtZW50IHNlbGVjdG9yIHNob3VsZCBiZSBleGVjdXRlZC5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmJ1YmJsZXM9dHJ1ZV0gLSBUaGUgRXZlbnQgcHJvcGVydHkgYnViYmxlcyBpbmRpY2F0ZXMgd2hldGhlciB0aGUgZXZlbnQgYnViYmxlcyB1cCB0aHJvdWdoIHRoZSBET00gb3Igbm90LlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuY2FuY2VsYWJsZT10cnVlXSAtIEV2ZW50cycgY2FuY2VsYWJsZSBwcm9wZXJ0eSBpbmRpY2F0ZXMgaWYgdGhlIGV2ZW50IGNhbiBiZSBjYW5jZWxlZCwgYW5kIHRoZXJlZm9yZSBwcmV2ZW50ZWQgYXMgaWYgdGhlIGV2ZW50IG5ldmVyIGhhcHBlbmVkLiBJZiB0aGUgZXZlbnQgaXMgbm90IGNhbmNlbGFibGUsIHRoZW4gaXRzIGNhbmNlbGFibGUgcHJvcGVydHkgd2lsbCBiZSBmYWxzZSBhbmQgdGhlIGV2ZW50IGxpc3RlbmVyIGNhbm5vdCBzdG9wIHRoZSBldmVudCBmcm9tIG9jY3VycmluZy5cbiAgICAgKi9cbiAgICBwYW4oZWxlbWVudCwgcG9zaXRpb24sIHRpbWVsaW5lUG9zaXRpb24sIG9wdHMgPSB7fSkge1xuXG4gICAgICAgIC8vIGFyZ3VtZW50c1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIFtwb3NpdGlvbiwgdGltZWxpbmVQb3NpdGlvbiwgb3B0c10gPSB0aGlzLnJlb3JkZXJBcmd1bWVudHMoYXJndW1lbnRzKVxuICAgICAgICB0aGlzLl90aW1lbGluZVBvc2l0aW9ucy5wdXNoKHRpbWVsaW5lUG9zaXRpb24pXG5cbiAgICAgICAgLy8gZGVidWdcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLmRlYnVnKSBjb25zb2xlLmxvZygndGFwIHBhcmFtcycsIHtlbGVtZW50LCBwb3NpdGlvbiwgdGltZWxpbmVQb3NpdGlvbiwgb3B0c30pXG5cbiAgICAgICAgLy8gb3B0c1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIG9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBvblN0YXJ0OiBudWxsLFxuICAgICAgICAgICAgb25VcGRhdGU6IG51bGwsXG4gICAgICAgICAgICBvbkNvbXBsZXRlOiBudWxsLFxuICAgICAgICAgICAgdG86IHt4OiAwLCB5OiAwfSxcbiAgICAgICAgICAgIGR1cmF0aW9uOiAxLFxuICAgICAgICAgICAgZWFzZTogUG93ZXIwLmVhc2VOb25lLFxuICAgICAgICAgICAgZXZlbnRUeXBlczogdGhpcy5yZXNvbHZlRXZlbnRzKFsnZG93bicsICdtb3ZlJywgJ3VwJ10pLFxuICAgICAgICAgICAgY29udGV4dDogd2luZG93LFxuICAgICAgICAgICAgYnViYmxlczogdHJ1ZSxcbiAgICAgICAgICAgIGNhbmNlbGFibGU6IHRydWVcbiAgICAgICAgfSwgb3B0cylcblxuICAgICAgICAvLyB0aW1lbGluZVxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuX3RpbWVsaW5lLmFkZENhbGxiYWNrKHBvc2l0aW9uID0+IHtcblxuICAgICAgICAgICAgLy8gZWxlbWVudFxuICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgY29uc3QgZWxlbSA9IFV0aWwuZXh0cmFjdEVsZW1lbnQob3B0cy5jb250ZXh0LCBlbGVtZW50KVxuXG4gICAgICAgICAgICAvLyBjb29yZHNcbiAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgIGNvbnN0IGZyb20gPSBVdGlsLmV4dHJhY3RQb3NpdGlvbihwb3NpdGlvbilcblxuICAgICAgICAgICAgLy8gZXZlbnQgb3B0c1xuICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgY29uc3QgZXZlbnRPcHRzID0ge2J1YmJsZXM6IG9wdHMuYnViYmxlcywgY2FuY2VsYWJsZTogb3B0cy5jYW5jZWxhYmxlfVxuXG4gICAgICAgICAgICBjb25zdCBnc09wdHMgPSB7XG4gICAgICAgICAgICAgICAgZWFzZTogb3B0cy5lYXNlLFxuICAgICAgICAgICAgICAgIG9uU3RhcnQ6ICgpID0+IHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBjcmVhdGUgYW5kIGRpc3BhdGNoIGV2ZW50XG4gICAgICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBFdmVudC5jcmVhdGUoZWxlbSwgZnJvbSwgb3B0cy5ldmVudFR5cGVzWzBdLCBldmVudE9wdHMpXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuZGVidWcpIGNvbnNvbGUubG9nKCdkaXNwYXRjaCBldmVudCcsIGV2ZW50KVxuICAgICAgICAgICAgICAgICAgICBlbGVtLmRpc3BhdGNoRXZlbnQoZXZlbnQpXG5cbiAgICAgICAgICAgICAgICAgICAgLy8gb25TdGFydFxuICAgICAgICAgICAgICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgICAgICAgICAgICAgIGlmIChvcHRzLm9uU3RhcnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG9wdHMub25TdGFydC5jYWxsKHRoaXMsIGV2ZW50KVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBvblVwZGF0ZTogKCkgPT4ge1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIGNyZWF0ZSBhbmQgZGlzcGF0Y2ggZXZlbnRcbiAgICAgICAgICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgICAgICAgICBjb25zdCBldmVudCA9IEV2ZW50LmNyZWF0ZShlbGVtLCBmcm9tLCBvcHRzLmV2ZW50VHlwZXNbMV0sIGV2ZW50T3B0cylcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5kZWJ1ZykgY29uc29sZS5sb2coJ2Rpc3BhdGNoIGV2ZW50JywgZXZlbnQpXG4gICAgICAgICAgICAgICAgICAgIGVsZW0uZGlzcGF0Y2hFdmVudChldmVudClcblxuICAgICAgICAgICAgICAgICAgICAvLyBvblVwZGF0ZVxuICAgICAgICAgICAgICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgICAgICAgICAgICAgIGlmIChvcHRzLm9uVXBkYXRlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBvcHRzLm9uVXBkYXRlLmNhbGwodGhpcywgZXZlbnQpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIG9uQ29tcGxldGU6ICgpID0+IHtcblxuICAgICAgICAgICAgICAgICAgICAvLyBjcmVhdGUgYW5kIGRpc3BhdGNoIGV2ZW50XG4gICAgICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBFdmVudC5jcmVhdGUoZWxlbSwgZnJvbSwgb3B0cy5ldmVudFR5cGVzWzJdLCBldmVudE9wdHMpXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuZGVidWcpIGNvbnNvbGUubG9nKCdkaXNwYXRjaCBldmVudCcsIGV2ZW50KVxuICAgICAgICAgICAgICAgICAgICBlbGVtLmRpc3BhdGNoRXZlbnQoZXZlbnQpXG5cbiAgICAgICAgICAgICAgICAgICAgLy8gb25Db21wbGV0ZVxuICAgICAgICAgICAgICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgICAgICAgICAgICAgIGlmIChvcHRzLm9uQ29tcGxldGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG9wdHMub25Db21wbGV0ZS5jYWxsKHRoaXMsIGV2ZW50KVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyB0b1xuICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgY29uc3Qgb2JqZWN0ID0gVXRpbC5leHRyYWN0VG8ob3B0cylcbiAgICAgICAgICAgIE9iamVjdC5hc3NpZ24oZ3NPcHRzLCBvYmplY3QpXG5cbiAgICAgICAgICAgIC8vIGRyYWcgYW5pbWF0aW9uXG4gICAgICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgICAgICBUd2Vlbk1heC50byhmcm9tLCBvcHRzLmR1cmF0aW9uLCBnc09wdHMpXG5cbiAgICAgICAgfSwgdGltZWxpbmVQb3NpdGlvbiwgW3Bvc2l0aW9uXSlcbiAgICAgICAgXG4gICAgICAgIHRoaXMuX2FjdGlvbnMrK1xuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRXhlY3V0ZXMgYSBwaW5jaCBldmVudCAocG9pbnRlcmRvd24sIHBvaW50ZXJtb3ZlLCBwb2ludGVydXApIG9uIGEgc3BlY2lmaWMgZWxlbWVudCB3aXRoIHR3byBcImZpbmdlcnNcIiBzaW11bHRhbmVvdXNseS5cbiAgICAgKiBcbiAgICAgKiBAcGFyYW0ge0hUTUxFbGVtZW50fHN0cmluZ30gZWxlbWVudCAtIFRoZSBIVE1MIGVsZW1lbnQgb24gd2hpY2ggdGhlIGV2ZW50IGlzIHRvIGJlIGV4ZWN1dGVkLCBlLmcuIGJ1dHRvbiwgZG9jdW1lbnQsIGgyLCBjYW52YXMsIGV0Yy4gb3IgYW4gc2VsZWN0b3Igc3RyaW5nLiBJZiBhIHNlbGVjdG9yIGhhcyBiZWVuIHNwZWNpZmllZCwgaXQgaXMgZXZhbHVhdGVkIGltbWVkaWF0ZWx5IGJlZm9yZSB0aGUgZXZlbnQgaXMgY2FsbGVkIHVzaW5nIHRoZSBxdWVyeVNlbGVjdG9yIG1ldGhvZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcltdfG9iamVjdHxQSVhJLkRpc3BsYXlPYmplY3R9IFtwb3NpdGlvbj1UaGUgY2VudGVyIG9mIHRoZSBlbGVtZW50Ll0gLSBUaGUgbG9jYWwgcG9zaXRpb24gb2YgdGhlIGV2ZW50IGluIHRoZSBjb250ZXh0IG9mIHRoZSBzcGVjaWZpZWQgSFRNTCBlbGVtZW50LiBJZiBubyBwb3NpdGlvbiBpcyBzcGVjaWZpZWQsIHRoZSBjZW50ZXIgb2YgdGhlIEhUTUwgZWxlbWVudCBpcyB1c2VkLiBUaGUgcG9zaXRpb24gY2FuIGJlIHNwZWNpZmllZCBhcyBhbiBhcnJheSBvZiBudW1iZXJzLCBhcyBhbiBvYmplY3Qgd2l0aCB0aGUgdHdvIHByb3BlcnRpZXMgeCBhbmQgeSwgb3IgYXMgYSBQSVhJLkRpc3BsYXkgb2JqZWN0LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbdGltZWxpbmVQb3NpdGlvbj1PbmUgc2Vjb25kIGFmdGVyIHRoZSBsYXN0IGFjdGlvbi5dIC0gVGhlIHBvc2l0aW9uIGluIHNlY29uZHMgd2hlbiB0aGUgZXZlbnQgc2hvdWxkIGJlIHRyaWdnZXJlZCwgc2VlIHNodHRwczovL2dyZWVuc29jay5jb20vZG9jcy9UaW1lbGluZU1heC9hZGRDYWxsYmFjaygpLlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0c10gLSBBbiBvcHRpb25zIG9iamVjdCB0byBzcGVjaWZ5IHRoZSBiZWhhdmlvdXIgb2YgdGhlIGFjdGlvbi5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy5vblN0YXJ0XSAtIEEgZnVuY3Rpb24gdGhhdCBydW5zIGFmdGVyIHRoZSBmaXJzdCBldmVudHMgYXJlIGZpcmVkLiBSZWNlaXZlcyB0aGUgZmlyZWQgZXZlbnQgb2JqZWN0IGFzIHRoZSBmaXJzdCBwYXJhbWV0ZXIuIFRoZSB0ZXN0IGNhc2UgKFVJVGVzdCkgaXMgYm91bmQgdG8gdGhpcy5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy5vblVwZGF0ZV0gLSBBIGZ1bmN0aW9uIHRoYXQgcnVucyBhZnRlciBlYWNoIGV4ZWN1dGlvbiBvZiB0aGUgc2Vjb25kIGV2ZW50cy4gUmVjZWl2ZXMgdGhlIGZpcmVkIGV2ZW50IG9iamVjdCBhcyB0aGUgZmlyc3QgcGFyYW1ldGVyLiBUaGUgdGVzdCBjYXNlIChVSVRlc3QpIGlzIGJvdW5kIHRvIHRoaXMuXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbn0gW29wdHMub25Db21wbGV0ZV0gLSBBIGZ1bmN0aW9uIHRoYXQgcnVucyBhZnRlciB0aGUgdGhpcmQgZXZlbnRzIGFyZSBmaXJlZC4gUmVjZWl2ZXMgdGhlIGZpcmVkIGV2ZW50IG9iamVjdCBhcyB0aGUgZmlyc3QgcGFyYW1ldGVyLiBUaGUgdGVzdCBjYXNlIChVSVRlc3QpIGlzIGJvdW5kIHRvIHRoaXMuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5kb3VibGVDYWxsYmFja3M9ZmFsc2VdIC0gVGhlIGNhbGxiYWNrcyBvblN0YXJ0LCBvblVwZGF0ZSBhbmQgb25Db21wbGV0ZSB3aWxsIGJlIGZpcmVkIG9ubHkgZm9yIG9uZSBmaW5nZXIuIElmIHNldCB0byB0cnVlLCB0aGUgZXZlbnRzIHdpbGwgYmUgZmlyZWQgZm9yIGJvdGggZmluZ2Vycy5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZGlzdGFuY2U9MTAwXSAtIFRoZSBkaXN0YW5jZSBpbiBwaXhlbHMsIGhvdyBmYXIgdGhlIHR3byBcImZpbmdlcnNcIiBzaG91bGQgbW92ZSBhcGFydC4gSWYgdG8gb3IgYmV6aWVyIHNwZWNpZmllZCwgZGlzdGFuY2UgaXMgaWdub3JlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcltdW118b2JqZWN0W118UElYSS5EaXNwbGF5T2JqZWN0W119IFtvcHRzLnRvXSAtIFRoZSB0YXJnZXRzIG9mIHRoZSBwaW5jaCBwcm9jZXNzLiBUaGUgcG9zaXRpb24gbXVzdCBiZSBhbiBhcnJheSB3aXRoIHR3byBlbnRyaWVzLiBBbiBlbnRyeSBjYW4gYmUgc3BlY2lmaWVkIGFzIGFuIGFycmF5IG9mIG51bWJlcnMsIGFzIGFuIG9iamVjdCB3aXRoIHRoZSB0d28gcHJvcGVydGllcyB4IGFuZCB5LCBvciBhcyBhIFBJWEkuRGlzcGxheSBvYmplY3QuIElmIGJlemllciBpcyBzcGVjaWZpZWQsIHRvIGlzIGlnbm9yZWQuXG4gICAgICogQHBhcmFtIHtudW1iZXJbXVtdfG9iamVjdFtdfFBJWEkuRGlzcGxheU9iamVjdFtdfSBbb3B0cy5iZXppZXJdIC0gVGhlIHRhcmdldHMgb2YgdGhlIHBpbmNoIHByb2Nlc3MuIFRoZSBwb3NpdGlvbiBtdXN0IGJlIGFuIGFycmF5IHdpdGggdHdvIGVudHJpZXMuIEFuIGVudHJ5IG1heSBiZSBhbiBhcnJheSBvZiBwb3NpdGlvbnMgb3IgYSBiZXppZXIgb2JqZWN0IChodHRwczovL2dyZWVuc29jay5jb20vZG9jcy9QbHVnaW5zL0JlemllclBsdWdpbikuIEEgcG9zaXRpb24gaW4gdGhlIGFycmF5IG9yIHRoZSB2YWx1ZXMgYXJyYXkgb2YgdGhlIGJlemllciBvYmplY3QgY2FuIGJlIHNwZWNpZmllZCBhcyBhbiBhcnJheSBvZiBudW1iZXJzLCBhcyBhbiBvYmplY3Qgd2l0aCB0aGUgdHdvIHByb3BlcnRpZXMgeCBhbmQgeSwgb3IgYXMgYSBQSVhJLkRpc3BsYXkgb2JqZWN0LiBJZiBiZXppZXIgaXMgc3BlY2lmaWVkLCB0byBpcyBpZ25vcmVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5kdXJhdGlvbj0xXSAtIFRoZSBkdXJhdGlvbiBvZiB0aGUgcGFuIGFuaW1hdGlvbiBpbiBzZWNvbmRzLCBzZWUgaHR0cHM6Ly9ncmVlbnNvY2suY29tL2RvY3MvVHdlZW5MaXRlL2R1cmF0aW9uKCkuXG4gICAgICogQHBhcmFtIHtFYXNlfSBbb3B0cy5lYXNlPVBvd2VyMC5lYXNlTm9uZV0gLSBUaGUgZWFzaW5nIG9mIHRoZSBwYW4gYW5pbWF0aW9uLCBzZWUgaHR0cHM6Ly9ncmVlbnNvY2suY29tL2RvY3MvRWFzaW5nLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nW119IFtvcHRzLmV2ZW50VHlwZXM9Wydwb2ludGVyZG93bicsICdwb2ludGVybW92ZScsICdwb2ludGVydXAnXV0gLSBUaGUgZXZlbnQgdHlwZXMgdG8gdXNlLiBJZiBubyB0eXBlcyBhcmUgc3BlY2lmaWVkLCB0aGUgZXZlbnQgdHlwZXMgc3BlY2lmaWVkIGluIHRoZSBVSVRlc3QgY29uc3RydWN0b3IgYXJlIHVzZWQgKG9yIGF1dG8gaWYgbm90IHNwZWNpZmllZCkuXG4gICAgICogQHBhcmFtIHtXaW5kb3d8RnJhbWV9IFtvcHRzLmNvbnRleHQ9d2luZG93XSAtIFRoZSBjb250ZXh0IHdpdGhpbiB3aGljaCB0aGUgb3B0aW9uYWxseSBzcGVjaWZpZWQgZWxlbWVudCBzZWxlY3RvciBzaG91bGQgYmUgZXhlY3V0ZWQuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5idWJibGVzPXRydWVdIC0gVGhlIEV2ZW50IHByb3BlcnR5IGJ1YmJsZXMgaW5kaWNhdGVzIHdoZXRoZXIgdGhlIGV2ZW50IGJ1YmJsZXMgdXAgdGhyb3VnaCB0aGUgRE9NIG9yIG5vdC5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmNhbmNlbGFibGU9dHJ1ZV0gLSBFdmVudHMnIGNhbmNlbGFibGUgcHJvcGVydHkgaW5kaWNhdGVzIGlmIHRoZSBldmVudCBjYW4gYmUgY2FuY2VsZWQsIGFuZCB0aGVyZWZvcmUgcHJldmVudGVkIGFzIGlmIHRoZSBldmVudCBuZXZlciBoYXBwZW5lZC4gSWYgdGhlIGV2ZW50IGlzIG5vdCBjYW5jZWxhYmxlLCB0aGVuIGl0cyBjYW5jZWxhYmxlIHByb3BlcnR5IHdpbGwgYmUgZmFsc2UgYW5kIHRoZSBldmVudCBsaXN0ZW5lciBjYW5ub3Qgc3RvcCB0aGUgZXZlbnQgZnJvbSBvY2N1cnJpbmcuXG4gICAgICovXG4gICAgcGluY2goZWxlbWVudCwgcG9zaXRpb24sIHRpbWVsaW5lUG9zaXRpb24sIG9wdHMgPSB7fSkge1xuXG4gICAgICAgIC8vIGFyZ3VtZW50c1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIFtwb3NpdGlvbiwgdGltZWxpbmVQb3NpdGlvbiwgb3B0c10gPSB0aGlzLnJlb3JkZXJBcmd1bWVudHMoYXJndW1lbnRzKVxuICAgICAgICB0aGlzLl90aW1lbGluZVBvc2l0aW9ucy5wdXNoKHRpbWVsaW5lUG9zaXRpb24pXG5cbiAgICAgICAgLy8gZGVidWdcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLmRlYnVnKSBjb25zb2xlLmxvZygndGFwIHBhcmFtcycsIHtlbGVtZW50LCBwb3NpdGlvbiwgdGltZWxpbmVQb3NpdGlvbiwgb3B0c30pXG5cbiAgICAgICAgLy8gb3B0c1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIG9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBvblN0YXJ0OiBudWxsLFxuICAgICAgICAgICAgb25VcGRhdGU6IG51bGwsXG4gICAgICAgICAgICBvbkNvbXBsZXRlOiBudWxsLFxuICAgICAgICAgICAgZG91YmxlQ2FsbGJhY2tzOiBmYWxzZSxcbiAgICAgICAgICAgIGR1cmF0aW9uOiAxLFxuICAgICAgICAgICAgZGlzdGFuY2U6IDEwMCxcbiAgICAgICAgICAgIHRvOiBudWxsLFxuICAgICAgICAgICAgYmV6aWVyOiBudWxsLFxuICAgICAgICAgICAgZWFzZTogUG93ZXIwLmVhc2VOb25lLFxuICAgICAgICAgICAgZXZlbnRUeXBlczogdGhpcy5yZXNvbHZlRXZlbnRzKFsnZG93bicsICdtb3ZlJywgJ3VwJ10pLFxuICAgICAgICAgICAgY29udGV4dDogd2luZG93LFxuICAgICAgICAgICAgYnViYmxlczogdHJ1ZSxcbiAgICAgICAgICAgIGNhbmNlbGFibGU6IHRydWVcbiAgICAgICAgfSwgb3B0cylcblxuICAgICAgICAvLyB0aW1lbGluZVxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuX3RpbWVsaW5lLmFkZENhbGxiYWNrKHBvc2l0aW9uID0+IHtcblxuICAgICAgICAgICAgLy8gZWxlbWVudFxuICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgY29uc3QgZWxlbSA9IFV0aWwuZXh0cmFjdEVsZW1lbnQob3B0cy5jb250ZXh0LCBlbGVtZW50KVxuXG4gICAgICAgICAgICAvLyBmcm9tXG4gICAgICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgICAgICBsZXQgZnJvbTEgPSBudWxsXG4gICAgICAgICAgICBsZXQgZnJvbTIgPSBudWxsXG5cbiAgICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KHBvc2l0aW9uKSAmJiAhVXRpbC5pc051bWJlcihwb3NpdGlvblswXSkpIHtcbiAgICAgICAgICAgICAgICBmcm9tMSA9IFV0aWwuZXh0cmFjdFBvc2l0aW9uKHBvc2l0aW9uWzBdKVxuICAgICAgICAgICAgICAgIGZyb20yID0gVXRpbC5leHRyYWN0UG9zaXRpb24ocG9zaXRpb25bMV0pXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGZyb20xID0gVXRpbC5leHRyYWN0UG9zaXRpb24ocG9zaXRpb24pXG4gICAgICAgICAgICAgICAgZnJvbTIgPSB7eDogZnJvbTEueCwgeTogZnJvbTEueX1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gdG9cbiAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgIGxldCBnc09wdHMxID0ge31cbiAgICAgICAgICAgIGxldCBnc09wdHMyID0ge31cblxuICAgICAgICAgICAgaWYgKG9wdHMudG8gfHwgb3B0cy5iZXppZXIpIHtcbiAgICAgICAgICAgICAgICBbZ3NPcHRzMSwgZ3NPcHRzMl0gPSBVdGlsLmV4dHJhY3RNdWx0aVRvKG9wdHMpXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRpc3RhbmNlID0gb3B0cy5kaXN0YW5jZSAhPSBudWxsID8gb3B0cy5kaXN0YW5jZSA6IDEwMFxuICAgICAgICAgICAgICAgIGdzT3B0czEueCA9IGZyb20xLnggLSBkaXN0YW5jZSAvIDJcbiAgICAgICAgICAgICAgICBnc09wdHMxLnkgPSBmcm9tMS55XG4gICAgICAgICAgICAgICAgZ3NPcHRzMi54ID0gZnJvbTIueCArIGRpc3RhbmNlIC8gMlxuICAgICAgICAgICAgICAgIGdzT3B0czIueSA9IGZyb20yLnlcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gcG9pbnRlcnNcbiAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgIGNvbnN0IHBvaW50ZXJzID0gbmV3IE1hcCgpXG4gICAgICAgICAgICBwb2ludGVycy5zZXQoMCwge2VsZW1lbnQ6IGZyb20xLCBnc09wdHM6IGdzT3B0czF9KVxuICAgICAgICAgICAgcG9pbnRlcnMuc2V0KDEsIHtlbGVtZW50OiBmcm9tMiwgZ3NPcHRzOiBnc09wdHMyfSlcblxuICAgICAgICAgICAgLy8gbG9vcFxuICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgcG9pbnRlcnMuZm9yRWFjaCgodmFsdWUsIGtleSkgPT4ge1xuXG4gICAgICAgICAgICAgICAgLy8gZnJvbVxuICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICBjb25zdCBmcm9tID0gdmFsdWUuZWxlbWVudFxuXG4gICAgICAgICAgICAgICAgLy8gZXZlbnQgb3B0c1xuICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICBjb25zdCBldmVudE9wdHMgPSB7YnViYmxlczogb3B0cy5idWJibGVzLCBjYW5jZWxhYmxlOiBvcHRzLmNhbmNlbGFibGUsIHBvaW50ZXJJZDoga2V5LCBpc1ByaW1hcnk6IGtleSA9PT0gMH1cblxuICAgICAgICAgICAgICAgIGNvbnN0IGdzT3B0cyA9IHtcbiAgICAgICAgICAgICAgICAgICAgZWFzZTogb3B0cy5lYXNlLFxuICAgICAgICAgICAgICAgICAgICBvblN0YXJ0OiAoKSA9PiB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGNyZWF0ZSBhbmQgZGlzcGF0Y2ggZXZlbnRcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gRXZlbnQuY3JlYXRlKGVsZW0sIGZyb20sIG9wdHMuZXZlbnRUeXBlc1swXSwgZXZlbnRPcHRzKVxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5kZWJ1ZykgY29uc29sZS5sb2coJ2Rpc3BhdGNoIGV2ZW50JywgZXZlbnQpXG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmRpc3BhdGNoRXZlbnQoZXZlbnQpXG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIG9uU3RhcnRcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChvcHRzLm9uU3RhcnQgJiYgKG9wdHMuZG91YmxlQ2FsbGJhY2tzIHx8IGtleSA9PT0gMCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRzLm9uU3RhcnQuY2FsbCh0aGlzLCBldmVudClcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgb25VcGRhdGU6ICgpID0+IHtcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gY3JlYXRlIGFuZCBkaXNwYXRjaCBldmVudFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBFdmVudC5jcmVhdGUoZWxlbSwgZnJvbSwgb3B0cy5ldmVudFR5cGVzWzFdLCBldmVudE9wdHMpXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5vcHRzLmRlYnVnKSBjb25zb2xlLmxvZygnZGlzcGF0Y2ggZXZlbnQnLCBldmVudClcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW0uZGlzcGF0Y2hFdmVudChldmVudClcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gb25VcGRhdGVcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChvcHRzLm9uVXBkYXRlICYmIChvcHRzLmRvdWJsZUNhbGxiYWNrcyB8fCBrZXkgPT09IDApKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0cy5vblVwZGF0ZS5jYWxsKHRoaXMsIGV2ZW50KVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBvbkNvbXBsZXRlOiAoKSA9PiB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGNyZWF0ZSBhbmQgZGlzcGF0Y2ggZXZlbnRcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gRXZlbnQuY3JlYXRlKGVsZW0sIGZyb20sIG9wdHMuZXZlbnRUeXBlc1syXSwgZXZlbnRPcHRzKVxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5kZWJ1ZykgY29uc29sZS5sb2coJ2Rpc3BhdGNoIGV2ZW50JywgZXZlbnQpXG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtLmRpc3BhdGNoRXZlbnQoZXZlbnQpXG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIG9uQ29tcGxldGVcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChvcHRzLm9uQ29tcGxldGUgJiYgKG9wdHMuZG91YmxlQ2FsbGJhY2tzIHx8IGtleSA9PT0gMCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRzLm9uQ29tcGxldGUuY2FsbCh0aGlzLCBldmVudClcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIHRvXG4gICAgICAgICAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICAgICAgICAgIE9iamVjdC5hc3NpZ24oZ3NPcHRzLCB2YWx1ZS5nc09wdHMpXG5cbiAgICAgICAgICAgICAgICAvLyBkcmFnIGFuaW1hdGlvblxuICAgICAgICAgICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgICAgICAgICBUd2Vlbk1heC50byhmcm9tLCBvcHRzLmR1cmF0aW9uLCBnc09wdHMpXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgIH0sIHRpbWVsaW5lUG9zaXRpb24sIFtwb3NpdGlvbl0pXG4gICAgICAgIFxuICAgICAgICB0aGlzLl9hY3Rpb25zKytcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8vIC8qKlxuICAgIC8vICAqIEFkZHMgYSB0YXAgZXZlbnQgdG8gdGhlIHRpbWVsaW5lLlxuICAgIC8vICAqXG4gICAgLy8gICogQHJldHVybiB7VUlUZXN0fSBBIHJlZmVyZW5jZSB0byB0aGUgdWl0ZXN0IGZvciBjaGFpbmluZy5cbiAgICAvLyAgKi9cbiAgICAvLyByb3RhdGUoKSB7XG4gICAgLy8gICAgIHJldHVybiB0aGlzXG4gICAgLy8gfVxuXG4gICAgLy8gLyoqXG4gICAgLy8gICogQWRkcyBhIHRhcCBldmVudCB0byB0aGUgdGltZWxpbmUuXG4gICAgLy8gICpcbiAgICAvLyAgKiBAcmV0dXJuIHtVSVRlc3R9IEEgcmVmZXJlbmNlIHRvIHRoZSB1aXRlc3QgZm9yIGNoYWluaW5nLlxuICAgIC8vICAqL1xuICAgIC8vIHN3aXBlKCkge1xuICAgIC8vICAgICByZXR1cm4gdGhpc1xuICAgIC8vIH1cblxuICAgIC8vIC8qKlxuICAgIC8vICAqIEFkZHMgYSB0YXAgZXZlbnQgdG8gdGhlIHRpbWVsaW5lLlxuICAgIC8vICAqXG4gICAgLy8gICogQHJldHVybiB7VUlUZXN0fSBBIHJlZmVyZW5jZSB0byB0aGUgdWl0ZXN0IGZvciBjaGFpbmluZy5cbiAgICAvLyAgKi9cbiAgICAvLyBwcmVzcygpIHtcbiAgICAvLyAgICAgcmV0dXJuIHRoaXNcbiAgICAvLyB9XG5cbiAgICAvLyAvKipcbiAgICAvLyAgKiBBZGRzIGEgdGFwIGV2ZW50IHRvIHRoZSB0aW1lbGluZS5cbiAgICAvLyAgKlxuICAgIC8vICAqIEByZXR1cm4ge1VJVGVzdH0gQSByZWZlcmVuY2UgdG8gdGhlIHVpdGVzdCBmb3IgY2hhaW5pbmcuXG4gICAgLy8gICovXG4gICAgLy8gZXZlbnQoKSB7XG4gICAgLy8gICAgIHJldHVybiB0aGlzXG4gICAgLy8gfVxuXG4gICAgLyoqXG4gICAgICogU29ydHMgdGhlIHBhcmFtZXRlcnMgc28gdGhhdCB0aGUgc2Vjb25kLCB0aGlyZCwgYW5kIGZvdXJ0aCBwYXJhbWV0ZXJzIGNhbiBiZSBvcHRpb25hbCAoYW5kIHBvc3NpYmx5IHNsaXAgZm9yd2FyZCkuXG4gICAgICogXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcGFyYW0ge2FyZ3VtZW50c30gcGFyYW1zIC0gVGhlIGFyZ3VtZW50cyB3aGljaCB3ZXJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXG4gICAgICogQHJldHVybnMge2FycmF5fSAtIFJldHVybnMgYW4gYXJyYXkgb2YgdGhlIHBvc2l0aW9uLCB0aGUgdGltZWxpbmVQb3NpdGlvbiBhbmQgdGhlIG9wdHMgb2JqZWN0LlxuICAgICAqL1xuICAgIHJlb3JkZXJBcmd1bWVudHMocGFyYW1zKSB7XG5cbiAgICAgICAgLy8gZmlyc3QgcGFyYW1ldGVyXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgY29uc3QgZWxlbWVudCA9IHBhcmFtc1swXVxuXG4gICAgICAgIC8vIG90aGVyIHBhcmFtZXRlclxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGxldCBwb3NpdGlvbiA9IG51bGxcbiAgICAgICAgbGV0IHRpbWVsaW5lUG9zaXRpb24gPSBudWxsXG4gICAgICAgIGxldCBvcHRzID0gbnVsbFxuICAgICAgICBcbiAgICAgICAgLy8gc2Vjb25kIHBhcmFtZXRlclxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGlmIChVdGlsLmlzTnVtYmVyKHBhcmFtc1sxXSkpIHtcbiAgICAgICAgICAgIHRpbWVsaW5lUG9zaXRpb24gPSBwYXJhbXNbMV1cbiAgICAgICAgfSBlbHNlIGlmIChVdGlsLmlzT2JqZWN0KHBhcmFtc1sxXSkgJiYgIVV0aWwuaXNQaXhpRGlzcGxheU9iamVjdChwYXJhbXNbMV0pICYmIChwYXJhbXNbMV0ueCA9PSBudWxsIHx8IHBhcmFtc1sxXS55ID09IG51bGwpKSB7XG4gICAgICAgICAgICBvcHRzID0gcGFyYW1zWzFdXG4gICAgICAgIH0gZWxzZSBpZiAocGFyYW1zWzFdICE9IG51bGwpIHtcbiAgICAgICAgICAgIHBvc2l0aW9uID0gcGFyYW1zWzFdXG4gICAgICAgIH1cblxuICAgICAgICAvLyB0aGlyZCBwYXJhbWV0ZXJcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAoVXRpbC5pc051bWJlcihwYXJhbXNbMl0pKSB7XG4gICAgICAgICAgICB0aW1lbGluZVBvc2l0aW9uID0gcGFyYW1zWzJdXG4gICAgICAgIH0gZWxzZSBpZiAoVXRpbC5pc09iamVjdChwYXJhbXNbMl0pKSB7XG4gICAgICAgICAgICBvcHRzID0gcGFyYW1zWzJdXG4gICAgICAgIH1cblxuICAgICAgICAvLyBmb3VydGggcGFyYW1ldGVyXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgaWYgKFV0aWwuaXNPYmplY3QocGFyYW1zWzNdKSkge1xuICAgICAgICAgICAgb3B0cyA9IHBhcmFtc1szXVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gZGVmYXVsdHNcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAocG9zaXRpb24gPT09IG51bGwpIHtcbiAgICAgICAgICAgIC8vIHdpbGwgbGF0ZXIgYmUgZmlsbGVkLi4uXG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGltZWxpbmVQb3NpdGlvbiA9PT0gbnVsbCkge1xuICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5kZWZhdWx0SW50ZXJ2YWwgPT09IG51bGwgJiYgdGhpcy5fYWN0aW9ucyA+IDEpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIGV4ZWN1dGlvbiB0aW1lIHdhcyBzcGVjaWZpZWQgZm9yIHRoaXMgYWN0aW9uLCBhbmQgYSBkZWZhdWx0IGludGVydmFsIHdhcyBub3Qgc2V0IGluIHRoZSBjbGFzcyBjb25zdHJ1Y3RvciEnKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGltZWxpbmVQb3NpdGlvbiA9IE1hdGgubWF4KC4uLnRoaXMuX3RpbWVsaW5lUG9zaXRpb25zKSArICh0aGlzLm9wdHMuZGVmYXVsdEludGVydmFsIHx8IDEpXG4gICAgICAgIH1cblxuICAgICAgICBpZiAob3B0cyA9PT0gbnVsbCkge1xuICAgICAgICAgICAgb3B0cyA9IHt9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gW3Bvc2l0aW9uLCB0aW1lbGluZVBvc2l0aW9uLCBvcHRzXVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENvbnZlcnRzIGV2ZW50IHR5cGUgc2hvcnRjdXRzIHRvIHJlYWwgZXZlbnQgbmFtZXMuXG4gICAgICogXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBldmVudHMgLSBBbiBhcnJheSBvZiBldmVudCB0eXBlcy5cbiAgICAgKi9cbiAgICByZXNvbHZlRXZlbnRzKGV2ZW50cykge1xuICAgICAgICBcbiAgICAgICAgY29uc3QgZGF0YSA9IFtdXG5cbiAgICAgICAgaWYgKHRoaXMub3B0cy5ldmVudFR5cGUgPT09ICdwb2ludGVyJykge1xuICAgICAgICAgICAgZXZlbnRzLmZvckVhY2goaXQgPT4ge1xuICAgICAgICAgICAgICAgIGlmIChpdCA9PT0gJ2Rvd24nKSB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEucHVzaCgncG9pbnRlcmRvd24nKVxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoaXQgPT09ICdtb3ZlJykge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLnB1c2goJ3BvaW50ZXJtb3ZlJylcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGl0ID09PSAndXAnKSB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEucHVzaCgncG9pbnRlcnVwJylcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuICAgICAgICB9IGVsc2UgaWYgKHRoaXMub3B0cy5ldmVudFR5cGUgPT09ICd0b3VjaCcpIHtcbiAgICAgICAgICAgIGV2ZW50cy5mb3JFYWNoKGl0ID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoaXQgPT09ICdkb3duJykge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLnB1c2goJ3RvdWNoc3RhcnQnKVxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoaXQgPT09ICdtb3ZlJykge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLnB1c2goJ3RvdWNobW92ZScpXG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChpdCA9PT0gJ3VwJykge1xuICAgICAgICAgICAgICAgICAgICBkYXRhLnB1c2goJ3RvdWNoZW5kJylcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZXZlbnRzLmZvckVhY2goaXQgPT4ge1xuICAgICAgICAgICAgICAgIGlmIChpdCA9PT0gJ2Rvd24nKSB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGEucHVzaCgnbW91c2Vkb3duJylcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGl0ID09PSAnbW92ZScpIHtcbiAgICAgICAgICAgICAgICAgICAgZGF0YS5wdXNoKCdtb3VzZW1vdmUnKVxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoaXQgPT09ICd1cCcpIHtcbiAgICAgICAgICAgICAgICAgICAgZGF0YS5wdXNoKCdtb3VzZXVwJylcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGRhdGFcbiAgICB9XG59XG5cbi8qKlxuICogSGVscGVyIGNsYXNzLlxuICpcbiAqIEBleGFtcGxlXG4gKiAvLyBDaGVja3MgaWYgYSB0aGluZyBpcyBhIG51bWJlci5cbiAqIGNvbnN0IG51bSA9IFV0aWwuaXNOdW1iZXIoMjApXG4gKlxuICogQHByaXZhdGVcbiAqIEBpZ25vcmVcbiAqIEBjbGFzc1xuICovXG5jbGFzcyBVdGlsIHtcblxuICAgIC8qKlxuICAgICAqIFJlc29sdmVzIHRoZSBlbGVtZW50IGZyb20gYSBzcGVjaWZpYyBjb250ZXh0LlxuICAgICAqIFxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge1dpbmRvd3xGcmFtZX0gY29udGV4dCAtIFRoZSBjb250ZXh0IHdpdGhpbiB3aGljaCB0aGUgb3B0aW9uYWxseSBzcGVjaWZpZWQgZWxlbWVudCBzZWxlY3RvciBzaG91bGQgYmUgZXhlY3V0ZWQuXG4gICAgICogQHJldHVybiB7SFRNTEVsZW1lbnR8c3RyaW5nfSBlbGVtZW50IC0gVGhlIEhUTUwgZWxlbWVudCBvbiB3aGljaCB0aGUgZXZlbnQgaXMgdG8gYmUgZXhlY3V0ZWQsIGUuZy4gYnV0dG9uLCBkb2N1bWVudCwgaDIsIGNhbnZhcywgZXRjLiBvciBhbiBzZWxlY3RvciBzdHJpbmcuIElmIGEgc2VsZWN0b3IgaGFzIGJlZW4gc3BlY2lmaWVkLCBpdCBpcyBldmFsdWF0ZWQgaW1tZWRpYXRlbHkgYmVmb3JlIHRoZSBldmVudCBpcyBjYWxsZWQgdXNpbmcgdGhlIHF1ZXJ5U2VsZWN0b3IgbWV0aG9kLlxuICAgICAqL1xuICAgIHN0YXRpYyBleHRyYWN0RWxlbWVudChjb250ZXh0LCBlbGVtZW50KSB7XG5cbiAgICAgICAgY29uc3QgY29udCA9IFV0aWwuaXNGcmFtZShjb250ZXh0KSA/IGNvbnRleHQuY29udGVudERvY3VtZW50IDogY29udGV4dC5kb2N1bWVudFxuICAgICAgICBjb25zdCBlbGVtID0gVXRpbC5pc1N0cmluZyhlbGVtZW50KSA/IGNvbnQucXVlcnlTZWxlY3RvcihlbGVtZW50KSA6IGVsZW1lbnRcblxuICAgICAgICByZXR1cm4gZWxlbVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEV4dHJhY3RzIHRoZSBwb3NpdGlvbiBvZiB0aGUgc2Vjb25kIHBhcmFtZXRlci5cbiAgICAgKiBcbiAgICAgKiBAc3RhdGljXG4gICAgICogQHBhcmFtIHtvYmplY3R9IG9iamVjdCAtIFNvbWV0aGluZyB3ZXJlIHRoZSBjb29yZHMgc2hvdWxkIGJlIGV4dHJhY3RlZC5cbiAgICAgKiBAcmV0dXJuIHtvYmplY3R9IC0gUmV0dXJucyBhbiBvYmplY3Qgd2l0aCB0aGUga2V5cyB4IGFuZCB5LlxuICAgICAqL1xuICAgIHN0YXRpYyBleHRyYWN0UG9zaXRpb24ob2JqZWN0KSB7XG5cbiAgICAgICAgLy8gZXZlbnQgY29vcmRzXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgY29uc3QgcG9zaXRpb24gPSB7eDogMCwgeTogMH1cblxuICAgICAgICAvLyBnZXQgdGhlIHBvc2l0aW9uXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgaWYgKCFvYmplY3QpIHtcbiAgICAgICAgICAgIHBvc2l0aW9uLnggPSAwXG4gICAgICAgICAgICBwb3NpdGlvbi55ID0gMFxuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBvYmplY3QuZ2V0Qm91bmRzID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICBjb25zdCBib3VuZHMgPSBvYmplY3QuZ2V0Qm91bmRzKClcbiAgICAgICAgICAgIHBvc2l0aW9uLnggPSBib3VuZHMueCArIGJvdW5kcy53aWR0aCAvIDJcbiAgICAgICAgICAgIHBvc2l0aW9uLnkgPSBib3VuZHMueSArIGJvdW5kcy5oZWlnaHQgLyAyXG4gICAgICAgIH0gZWxzZSBpZiAoQXJyYXkuaXNBcnJheShvYmplY3QpKSB7XG4gICAgICAgICAgICBwb3NpdGlvbi54ID0gb2JqZWN0WzBdXG4gICAgICAgICAgICBwb3NpdGlvbi55ID0gb2JqZWN0WzFdXG4gICAgICAgIH0gZWxzZSBpZiAob2JqZWN0LnggIT0gbnVsbCAmJiBvYmplY3QueSAhPSBudWxsKSB7XG4gICAgICAgICAgICBwb3NpdGlvbi54ID0gb2JqZWN0LnhcbiAgICAgICAgICAgIHBvc2l0aW9uLnkgPSBvYmplY3QueVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHBvc2l0aW9uXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRXh0cmFjdHMgdGhlIHRvIG9yIGJlemllciBrZXkuXG4gICAgICogXG4gICAgICogQHN0YXRpY1xuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRzIC0gQW4gb3B0aW9ucyBvYmplY3Qgd2hlcmUgdG8gb3IgYmV6aWVyIHNob3VsZCBiZSBleHRyYWN0ZWQuXG4gICAgICogQHJldHVybiB7b2JqZWN0fSAtIFJldHVybnMgYW4gb2JqZWN0IHdpdGggdGhlIHRvIG9yIGJlemllciBrZXlzLlxuICAgICAqL1xuICAgIHN0YXRpYyBleHRyYWN0VG8ob3B0cykge1xuXG4gICAgICAgIGNvbnN0IG9iamVjdCA9IHt9XG5cbiAgICAgICAgaWYgKG9wdHMuYmV6aWVyKSB7XG5cbiAgICAgICAgICAgIGxldCBiZXppZXIgPSBudWxsXG5cbiAgICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KG9wdHMuYmV6aWVyKSkge1xuICAgICAgICAgICAgICAgIGJlemllciA9IHtcbiAgICAgICAgICAgICAgICAgICAgdmFsdWVzOiBvcHRzLmJlemllci5tYXAoaXQgPT4gVXRpbC5leHRyYWN0UG9zaXRpb24oaXQpKSxcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3RocnUnXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBvcHRzLmJlemllci52YWx1ZXMgPSBvcHRzLmJlemllci52YWx1ZXMubWFwKGl0ID0+IFV0aWwuZXh0cmFjdFBvc2l0aW9uKGl0KSlcbiAgICAgICAgICAgICAgICBiZXppZXIgPSBvcHRzLmJlemllclxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBvYmplY3QuYmV6aWVyID0gYmV6aWVyXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCB0byA9IFV0aWwuZXh0cmFjdFBvc2l0aW9uKG9wdHMudG8pXG4gICAgICAgICAgICBvYmplY3QueCA9IHRvLnhcbiAgICAgICAgICAgIG9iamVjdC55ID0gdG8ueVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG9iamVjdFxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEV4dHJhY3RzIG11bHRpcGxlIHRvIG9yIGJlemllciBrZXlzLlxuICAgICAqIFxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0cyAtIEFuIG9wdGlvbnMgb2JqZWN0IHdoZXJlIHRvIG9yIGJlemllciBzaG91bGQgYmUgZXh0cmFjdGVkLlxuICAgICAqIEByZXR1cm4ge29iamVjdFtdfSAtIFJldHVybnMgYW4gYXJyYXkgb2Ygb2JqZWN0cyB3aXRoIHRoZSBrZXlzIHggYW5kIHkuXG4gICAgICovXG4gICAgc3RhdGljIGV4dHJhY3RNdWx0aVRvKG9wdHMpIHtcblxuICAgICAgICBjb25zdCBvYmplY3RzID0gW11cblxuICAgICAgICBpZiAob3B0cy5iZXppZXIpIHtcblxuICAgICAgICAgICAgb3B0cy5iZXppZXIuZm9yRWFjaChpdCA9PiB7XG5cbiAgICAgICAgICAgICAgICBsZXQgYmV6aWVyID0gbnVsbFxuXG4gICAgICAgICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkoaXQpKSB7XG4gICAgICAgICAgICAgICAgICAgIGJlemllciA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlczogaXQubWFwKGl0ID0+IFV0aWwuZXh0cmFjdFBvc2l0aW9uKGl0KSksXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAndGhydSdcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGl0LnZhbHVlcyA9IGl0LnZhbHVlcy5tYXAoaXQgPT4gVXRpbC5leHRyYWN0UG9zaXRpb24oaXQpKVxuICAgICAgICAgICAgICAgICAgICBiZXppZXIgPSBpdFxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIG9iamVjdHMucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgIGJlemllclxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICAgIG9wdHMudG8uZm9yRWFjaChpdCA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgdG8gPSBVdGlsLmV4dHJhY3RQb3NpdGlvbihpdClcbiAgICAgICAgICAgICAgICBvYmplY3RzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICB4OiB0by54LFxuICAgICAgICAgICAgICAgICAgICB5OiB0by55XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gb2JqZWN0c1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrcyBpZiBhIHRoaW5nIGlzIGEgc3RyaW5nLlxuICAgICAqIFxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gb2JqZWN0IC0gVGhlIG9iamVjdCB0byB0ZXN0IGZvci5cbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSAtIHRydWUgaWYgdGhlIHRoaW5nIGlzIGEgc3RyaW5nLCBvdGhlcndpc2UgZmFsc2UuXG4gICAgICovXG4gICAgc3RhdGljIGlzU3RyaW5nKG9iamVjdCkge1xuICAgICAgICByZXR1cm4gdHlwZW9mIG9iamVjdCA9PT0gJ3N0cmluZydcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVja3MgaWYgYSB0aGluZyBpcyBhIG51bWJlci5cbiAgICAgKiBcbiAgICAgKiBAc3RhdGljXG4gICAgICogQHBhcmFtIHtvYmplY3R9IG9iamVjdCAtIFRoZSBvYmplY3QgdG8gdGVzdCBmb3IuXG4gICAgICogQHJldHVybiB7Ym9vbGVhbn0gLSB0cnVlIGlmIHRoZSB0aGluZyBpcyBhIG51bWJlciwgb3RoZXJ3aXNlIGZhbHNlLlxuICAgICAqL1xuICAgIHN0YXRpYyBpc051bWJlcihvYmplY3QpIHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZiBvYmplY3QgPT09ICdudW1iZXInXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2hlY2tzIGlmIGEgdGhpbmcgaXMgYW4gb2JqZWN0LlxuICAgICAqIFxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gb2JqZWN0IC0gVGhlIG9iamVjdCB0byB0ZXN0IGZvci5cbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSAtIHRydWUgaWYgdGhlIHRoaW5nIGlzIGFuIG9iamVjdCwgb3RoZXJ3aXNlIGZhbHNlLlxuICAgICAqL1xuICAgIHN0YXRpYyBpc09iamVjdChvYmplY3QpIHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZiBvYmplY3QgPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KG9iamVjdClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVja3MgaWYgYSB0aGluZyBpcyBhbiBQSVhJLkRpc3BsYXlPYmplY3QuXG4gICAgICogXG4gICAgICogQHN0YXRpY1xuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvYmplY3QgLSBUaGUgb2JqZWN0IHRvIHRlc3QgZm9yLlxuICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IC0gdHJ1ZSBpZiB0aGUgdGhpbmcgaXMgYSBQSVhJLkRpc3BsYXlPYmplY3QsIG90aGVyd2lzZSBmYWxzZS5cbiAgICAgKi9cbiAgICBzdGF0aWMgaXNQaXhpRGlzcGxheU9iamVjdChvYmplY3QpIHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZiBvYmplY3QuZ2V0Qm91bmRzID09PSAnZnVuY3Rpb24nICYmIHR5cGVvZiBvYmplY3QucmVuZGVyV2ViR0wgPT09ICdmdW5jdGlvbicgJiYgdHlwZW9mIG9iamVjdC5zZXRUcmFuc2Zvcm0gPT09ICdmdW5jdGlvbidcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVja3MgaWYgYSB0aGluZyBpcyBhIGZyYW1lLlxuICAgICAqIFxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gb2JqZWN0IC0gVGhlIG9iamVjdCB0byB0ZXN0IGZvci5cbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSAtIHRydWUgaWYgdGhlIHRoaW5nIGlzIGEgZnJhbWUsIG90aGVyd2lzZSBmYWxzZS5cbiAgICAgKi9cbiAgICBzdGF0aWMgaXNGcmFtZShvYmplY3QpIHtcbiAgICAgICAgcmV0dXJuIG9iamVjdC5jb250ZW50RG9jdW1lbnQgIT0gbnVsbFxuICAgIH1cbn1cblxuLyoqXG4gKiBFdmVudCBoZWxwZXIgY2xhc3MuXG4gKlxuICogQGV4YW1wbGVcbiAqIC8vIENyZWF0ZXMgYW4gZXZlbnQgb2JqZWN0LlxuICogY29uc3QgZXZlbnQgPSBFdmVudC5jcmVhdGUoaDIsIHt4OiA1LCB5OiAxMH0sICd0b3VjaHN0YXJ0JylcbiAqXG4gKiBAcHJpdmF0ZVxuICogQGlnbm9yZVxuICogQGNsYXNzXG4gKi9cbmNsYXNzIEV2ZW50IHtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gZXZlbnQgb2JqZWN0LlxuICAgICAqIFxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSB0YXJnZXQgLSBUaGUgZWxlbWVudCBvbiB3aGljaCB0aGUgZXZlbnQgc2hvdWxkIGJlIGV4ZWN1dGVkLlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBwb3NpdGlvbiAtIFRoZSBsb2NhbCBwb3NpdGlvbiBvZiB0aGUgZXZlbnQgaW4gcmVsYXRpb24gdG8gdGhlIHRhcmdldC4gVGhlIG9iamVjdCBtdXN0IGhhdmUgdGhlIGtleXMgeCBhbmQgeS5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSB0eXBlIG9mIHRoZSBldmVudCwgc2VlIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RlL2RvY3MvV2ViL0V2ZW50c1xuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRzIC0gQW4gb3B0aW9ucyBvYmplY3QuIEV2ZXJ5IHBhcmFtdGVyIG9mIHRoZSBldmVudCBvYmplY3QgY2FuIGJlIG92ZXJyaWRkZW4sIHNlZSBlLmcuIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RlL2RvY3MvV2ViL0FQSS9Nb3VzZUV2ZW50IGZvciBhbGwgdGhlIHByb3BlcnRpZXMuXG4gICAgICovXG4gICAgc3RhdGljIGNyZWF0ZSh0YXJnZXQsIHBvc2l0aW9uID0ge3g6IDAsIHk6IDB9LCB0eXBlID0gJ3BvaW50ZXJ1cCcsIG9wdHMgPSB7fSkge1xuXG4gICAgICAgIGNvbnN0IHJlY3QgPSB0eXBlb2YgdGFyZ2V0LmdldEJvdW5kaW5nQ2xpZW50UmVjdCA9PT0gJ2Z1bmN0aW9uJyA/IHRhcmdldC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSA6IHt4OiAwLCB5OiAwfVxuXG4gICAgICAgIC8vIEV2ZW50SW5pdFxuICAgICAgICBjb25zdCBldmVudE9wdHMgPSB7XG4gICAgICAgICAgICBidWJibGVzOiB0cnVlLFxuICAgICAgICAgICAgY2FuY2VsYWJsZTogdHJ1ZSxcbiAgICAgICAgICAgIGNvbXBvc2VkOiBmYWxzZVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gVUlFdmVudEluaXRcbiAgICAgICAgY29uc3QgdWlFdmVudE9wdHMgPSB7XG4gICAgICAgICAgICBkZXRhaWw6IDAsXG4gICAgICAgICAgICB2aWV3OiB3aW5kb3dcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIE1vdXNlRXZlbnRcbiAgICAgICAgY29uc3QgbW91c2VFdmVudE9wdHMgPSB7XG4gICAgICAgICAgICBzY3JlZW5YOiB3aW5kb3cuc2NyZWVuWCArIHRhcmdldC5vZmZzZXRMZWZ0ICsgcG9zaXRpb24ueCxcbiAgICAgICAgICAgIHNjcmVlblk6IHdpbmRvdy5zY3JlZW5ZICsgdGFyZ2V0Lm9mZnNldFRvcCArIHBvc2l0aW9uLnksXG4gICAgICAgICAgICBjbGllbnRYOiByZWN0LnggKyBwb3NpdGlvbi54LFxuICAgICAgICAgICAgY2xpZW50WTogcmVjdC55ICsgcG9zaXRpb24ueSxcbiAgICAgICAgICAgIGN0cmxLZXk6IGZhbHNlLFxuICAgICAgICAgICAgc2hpZnRLZXk6IGZhbHNlLFxuICAgICAgICAgICAgYWx0S2V5OiBmYWxzZSxcbiAgICAgICAgICAgIG1ldGFLZXk6IGZhbHNlLFxuICAgICAgICAgICAgYnV0dG9uOiAwLFxuICAgICAgICAgICAgYnV0dG9uczogMSxcbiAgICAgICAgICAgIHJlbGF0ZWRUYXJnZXQ6IG51bGwsXG4gICAgICAgICAgICByZWdpb246IG51bGxcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFRvdWNoRXZlbnRcbiAgICAgICAgY29uc3QgdG91Y2hFdmVudE9wdHMgPSB7XG4gICAgICAgICAgICB0b3VjaGVzOiBbXSxcbiAgICAgICAgICAgIHRhcmdldFRvdWNoZXM6IFtdLFxuICAgICAgICAgICAgY2hhbmdlZFRvdWNoZXM6IFtdLFxuICAgICAgICAgICAgY3RybEtleTogZmFsc2UsXG4gICAgICAgICAgICBzaGlmdEtleTogZmFsc2UsXG4gICAgICAgICAgICBhbHRLZXk6IGZhbHNlLFxuICAgICAgICAgICAgbWV0YUtleTogZmFsc2VcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFBvaW50ZXJFdmVudFxuICAgICAgICBjb25zdCBwb2ludGVyRXZlbnRPcHRzID0ge1xuICAgICAgICAgICAgcG9pbnRlcklkOiAwLFxuICAgICAgICAgICAgd2lkdGg6IDEsXG4gICAgICAgICAgICBoZWlnaHQ6IDEsXG4gICAgICAgICAgICBwcmVzc3VyZTogMCxcbiAgICAgICAgICAgIHRhbmdlbnRpYWxQcmVzc3VyZTogMCxcbiAgICAgICAgICAgIHRpbHRYOiAwLFxuICAgICAgICAgICAgdGlsdFk6IDAsXG4gICAgICAgICAgICB0d2lzdDogMCxcbiAgICAgICAgICAgIHBvaW50ZXJUeXBlOiAndG91Y2gnLFxuICAgICAgICAgICAgaXNQcmltYXJ5OiB0cnVlXG4gICAgICAgIH1cblxuICAgICAgICBpZiAodHlwZS5zdGFydHNXaXRoKCdwb2ludGVyJykpIHtcbiAgICAgICAgICAgIHJldHVybiBuZXcgUG9pbnRlckV2ZW50KHR5cGUsIE9iamVjdC5hc3NpZ24oe30sIGV2ZW50T3B0cywgdWlFdmVudE9wdHMsIG1vdXNlRXZlbnRPcHRzLCBwb2ludGVyRXZlbnRPcHRzLCBvcHRzKSlcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlLnN0YXJ0c1dpdGgoJ3RvdWNoJykpIHtcbiAgICAgICAgICAgIHJldHVybiBuZXcgVG91Y2hFdmVudCh0eXBlLCBPYmplY3QuYXNzaWduKHt9LCBldmVudE9wdHMsIHVpRXZlbnRPcHRzLCB0b3VjaEV2ZW50T3B0cywgb3B0cykpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gbmV3IE1vdXNlRXZlbnQodHlwZSwgT2JqZWN0LmFzc2lnbih7fSwgZXZlbnRPcHRzLCB1aUV2ZW50T3B0cywgbW91c2VFdmVudE9wdHMsIG9wdHMpKVxuICAgICAgICB9XG4gICAgfVxufVxuIiwiaW1wb3J0IEFwcCBmcm9tICcuL2FwcC5qcydcbmltcG9ydCBEb2N0ZXN0IGZyb20gJy4vZG9jdGVzdC5qcydcbmltcG9ydCBFcnJvcnMgZnJvbSAnLi9lcnJvcnMuanMnXG5pbXBvcnQgRXZlbnRzIGZyb20gJy4vZXZlbnRzLmpzJ1xuaW1wb3J0IHtET01GbGlwLCBET01GbGlwcGFibGUsIENhcmRMb2FkZXIsIFBERkxvYWRlciwgSW1hZ2VMb2FkZXIsIEZyYW1lTG9hZGVyLCBIVE1MTG9hZGVyfSBmcm9tICcuL2ZsaXBwYWJsZS5qcydcbmltcG9ydCBJbmRleCBmcm9tICcuL2luZGV4LmpzJ1xuaW1wb3J0IEludGVyZmFjZSBmcm9tICcuL2ludGVyZmFjZS5qcydcbmltcG9ydCBQb3BwYWJsZSBmcm9tICcuL3BvcHBhYmxlLmpzJ1xuaW1wb3J0IFBvcHVwTWVudSBmcm9tICcuL3BvcHVwbWVudS5qcydcbmltcG9ydCBQb3B1cCBmcm9tICcuL3BvcHVwLmpzJ1xuaW1wb3J0IHtJQXBwfSBmcm9tICcuL2FwcC5qcydcbmltcG9ydCB7Q2FwYWJpbGl0aWVzLCBDYXBhYmlsaXRpZXNUZXN0c30gZnJvbSAnLi9jYXBhYmlsaXRpZXMuanMnXG5pbXBvcnQge0V2ZW50UmVjb3JkZXJ9IGZyb20gJy4vZXZlbnRzLmpzJ1xuaW1wb3J0IHtGcmFtZUNvbnRhaW5lciwgRnJhbWVUYXJnZXR9IGZyb20gJy4vZnJhbWVzLmpzJ1xuaW1wb3J0IHtJbnNwZWN0fSBmcm9tICcuL2luc3BlY3QuanMnXG5pbXBvcnQge1BvaW50TWFwLCBJbnRlcmFjdGlvblBvaW50cywgSW50ZXJhY3Rpb24sIElJbnRlcmFjdGlvblRhcmdldCwgSW50ZXJhY3Rpb25EZWx0YSwgSW50ZXJhY3Rpb25NYXBwZXIsIEludGVyYWN0aW9uRGVsZWdhdGUsIElJbnRlcmFjdGlvbk1hcHBlclRhcmdldH0gZnJvbSAnLi9pbnRlcmFjdGlvbi5qcydcbmltcG9ydCB7UmVzaXplRXZlbnQsIERPTVNjYXR0ZXJDb250YWluZXIsIEFic3RyYWN0U2NhdHRlciwgRE9NU2NhdHRlciwgU2NhdHRlckV2ZW50LCBCYXNlRXZlbnR9IGZyb20gJy4vc2NhdHRlci5qcydcbmltcG9ydCB7Q3ljbGUsIENvbG9ycywgRWxlbWVudHMsIEFuZ2xlLCBEYXRlcywgUG9pbnRzLCBQb2x5Z29uLCBSZWN0LCBTZXRzLCBTdHJpbmdzLCBpc0VtcHR5LCBnZXRJZCwgbGVycCwgZGVib3VuY2UsIHJhbmRvbUludCwgcmFuZG9tRmxvYXR9IGZyb20gJy4vdXRpbHMuanMnXG5pbXBvcnQgVUlUZXN0IGZyb20gJy4vdWl0ZXN0LmpzJ1xuXG4vKiBOZWVkZWQgdG8gZW5zdXJlIHRoYXQgcm9sbHVwLmpzIGluY2x1ZGVzIGNsYXNzIGRlZmluaXRpb25zIGFuZCB0aGUgY2xhc3Nlc1xuYXJlIHZpc2libGUgaW5zaWRlIGRvY3Rlc3RzLlxuKi9cblxud2luZG93LkFic3RyYWN0U2NhdHRlciA9IEFic3RyYWN0U2NhdHRlclxud2luZG93LkFuZ2xlID0gQW5nbGVcbndpbmRvdy5BcHAgPSBBcHBcbndpbmRvdy5CYXNlRXZlbnQgPSBCYXNlRXZlbnRcbndpbmRvdy5DYXBhYmlsaXRpZXMgPSBDYXBhYmlsaXRpZXNcbndpbmRvdy5DYXBhYmlsaXRpZXNUZXN0cyA9IENhcGFiaWxpdGllc1Rlc3RzXG53aW5kb3cuQ29sb3JzID0gQ29sb3JzXG53aW5kb3cuQ3ljbGUgPSBDeWNsZVxuXG53aW5kb3cuRE9NRmxpcCA9IERPTUZsaXBcbndpbmRvdy5ET01GbGlwcGFibGUgPSBET01GbGlwcGFibGVcbndpbmRvdy5DYXJkTG9hZGVyID0gQ2FyZExvYWRlclxud2luZG93LlBERkxvYWRlciA9IFBERkxvYWRlclxud2luZG93LkhUTUxMb2FkZXIgPSBIVE1MTG9hZGVyXG53aW5kb3cuSW1hZ2VMb2FkZXIgPSBJbWFnZUxvYWRlclxud2luZG93LkZyYW1lTG9hZGVyID0gRnJhbWVMb2FkZXJcblxud2luZG93LkRPTVNjYXR0ZXIgPSBET01TY2F0dGVyXG53aW5kb3cuRE9NU2NhdHRlckNvbnRhaW5lciA9IERPTVNjYXR0ZXJDb250YWluZXJcbndpbmRvdy5EYXRlcyA9IERhdGVzXG53aW5kb3cuRG9jdGVzdCA9IERvY3Rlc3RcbndpbmRvdy5FbGVtZW50cyA9IEVsZW1lbnRzXG53aW5kb3cuRXJyb3JzID0gRXJyb3JzXG53aW5kb3cuRXZlbnRSZWNvcmRlciA9IEV2ZW50UmVjb3JkZXJcbndpbmRvdy5FdmVudHMgPSBFdmVudHNcbndpbmRvdy5GcmFtZUNvbnRhaW5lciA9IEZyYW1lQ29udGFpbmVyXG53aW5kb3cuRnJhbWVUYXJnZXQgPSBGcmFtZVRhcmdldFxud2luZG93LklBcHAgPSBJQXBwXG53aW5kb3cuSUludGVyYWN0aW9uTWFwcGVyVGFyZ2V0ID0gSUludGVyYWN0aW9uTWFwcGVyVGFyZ2V0XG53aW5kb3cuSUludGVyYWN0aW9uVGFyZ2V0ID0gSUludGVyYWN0aW9uVGFyZ2V0XG53aW5kb3cuSW5kZXggPSBJbmRleFxud2luZG93Lkluc3BlY3QgPSBJbnNwZWN0XG53aW5kb3cuSW50ZXJhY3Rpb24gPSBJbnRlcmFjdGlvblxud2luZG93LkludGVyYWN0aW9uRGVsZWdhdGUgPSBJbnRlcmFjdGlvbkRlbGVnYXRlXG53aW5kb3cuSW50ZXJhY3Rpb25EZWx0YSA9IEludGVyYWN0aW9uRGVsdGFcbndpbmRvdy5JbnRlcmFjdGlvbk1hcHBlciA9IEludGVyYWN0aW9uTWFwcGVyXG53aW5kb3cuSW50ZXJhY3Rpb25Qb2ludHMgPSBJbnRlcmFjdGlvblBvaW50c1xud2luZG93LkludGVyZmFjZSA9IEludGVyZmFjZVxud2luZG93LlBvaW50TWFwID0gUG9pbnRNYXBcbndpbmRvdy5SZWN0ID0gUmVjdFxud2luZG93LlBvaW50cyA9IFBvaW50c1xud2luZG93LlBvbHlnb24gPSBQb2x5Z29uXG53aW5kb3cuUG9wcGFibGUgPSBQb3BwYWJsZVxud2luZG93LlBvcHVwID0gUG9wdXBcbndpbmRvdy5Qb3B1cE1lbnUgPSBQb3B1cE1lbnVcbndpbmRvdy5SZXNpemVFdmVudCA9IFJlc2l6ZUV2ZW50XG53aW5kb3cuU2NhdHRlckV2ZW50ID0gU2NhdHRlckV2ZW50XG53aW5kb3cuU2V0cyA9IFNldHNcbndpbmRvdy5TdHJpbmdzID0gU3RyaW5nc1xud2luZG93LlVJVGVzdCA9IFVJVGVzdFxud2luZG93LmdldElkID0gZ2V0SWRcbndpbmRvdy5pc0VtcHR5ID0gaXNFbXB0eVxud2luZG93LmxlcnAgPSBsZXJwXG53aW5kb3cuZGVib3VuY2UgPSBkZWJvdW5jZVxud2luZG93LnJhbmRvbUludCA9IHJhbmRvbUludFxud2luZG93LnJhbmRvbUZsb2F0ID0gcmFuZG9tRmxvYXRcbiJdLCJuYW1lcyI6WyJnZXRJZCIsIkVsZW1lbnRzIiwiSW50ZXJhY3Rpb25NYXBwZXIiLCJQb3B1cE1lbnUiXSwibWFwcGluZ3MiOiI7OztJQUFBOztBQUVBLElBQWUsTUFBTSxTQUFTLENBQUM7SUFDL0I7SUFDQTs7SUFFQSxJQUFJLE9BQU8sbUJBQW1CLENBQUMsS0FBSyxFQUFFO0lBQ3RDLFFBQVEsSUFBSSxhQUFhLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFDO0lBQzNELFFBQVEsSUFBSSxTQUFTLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFDO0lBQ3hELFFBQVEsSUFBSSxJQUFJLEdBQUcsSUFBSSxhQUFhLEVBQUU7SUFDdEMsWUFBWSxJQUFJLGFBQWEsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsRUFBQztJQUNuRCxZQUFZLElBQUksU0FBUyxHQUFHLEtBQUssQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFDO0lBQ2hELFlBQVksSUFBSSxPQUFPLFNBQVMsQ0FBQyxJQUFJLFdBQVc7SUFDaEQsZ0JBQWdCLE9BQU8sVUFBVSxHQUFHLEdBQUc7SUFDdkMsU0FBUztJQUNULFFBQVEsT0FBTyxJQUFJO0lBQ25CLEtBQUs7O0lBRUwsSUFBSSxPQUFPLGFBQWEsQ0FBQyxLQUFLLEVBQUU7SUFDaEM7SUFDQTtJQUNBLFFBQVEsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEtBQUssRUFBQztJQUNuRCxRQUFRLE9BQU8sS0FBSyxJQUFJLElBQUk7SUFDNUIsS0FBSzs7SUFFTDtJQUNBO0lBQ0E7SUFDQTtJQUNBLENBQUM7O0lDM0JEO0lBQ0E7SUFDQTtJQUNBOztBQUVBLElBQU8sTUFBTSxJQUFJLFNBQVMsU0FBUyxDQUFDO0lBQ3BDO0lBQ0E7SUFDQTtJQUNBLElBQUksS0FB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QyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDL0MsS0FBSzs7SUFFTCxJQUFJLE9BQU8sSUFBSSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUU7SUFDdEIsUUFBUSxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDM0QsS0FBSzs7SUFFTCxJQUFJLE9BQU8sYUFBYSxDQUFDLE1BQU0sRUFBRTtJQUNqQyxRQUFRLE9BQU8sSUFBSSxDQUFDLEVBQUUsR0FBRyxNQUFNLEdBQUcsS0FBSztJQUN2QyxLQUFLOztJQUVMLElBQUksT0FBTyxhQUFhLENBQUMsR0FBRyxFQUFFO0lBQzlCLFFBQVEsT0FBTyxLQUFLLEdBQUcsSUFBSSxDQUFDLEVBQUUsR0FBRyxHQUFHO0lBQ3BDLEtBQUs7SUFDTCxDQUFDOztBQUVELElBQU8sTUFBTUMsVUFBUSxDQUFDO0lBQ3RCLElBQUksT0FBTyxRQUFRLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRTtJQUNyQyxRQUFRLEtBQUssSUFBSSxHQUFHLElBQUksTUFBTSxFQUFFO0lBQ2hDLFlBQVksT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsR0FBRyxFQUFDO0lBQzVDLFNBQVM7SUFDVCxLQUFLOztJQUVMLElBQUksT0FBTyxRQUFRLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRTtJQUN2QyxRQUFRLE9BQU8sQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBQztJQUN2QyxLQUFLOztJQUVMLElBQUksT0FBTyxXQUFXLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRTtJQUMxQyxRQUFRLE9BQU8sQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBQztJQUMxQyxLQUFLOztJQUVMLElBQUksT0FBTyxXQUFXLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRTtJQUMxQyxRQUFR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TDtJQUNBO0lBQ0E7SUFDQSxJQUFJLE1BQU0sQ0FBQyxJQUFJLEVBQUU7SUFDakIsUUFBUSxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7SUFDckQsWUFBWSxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUM7SUFDcEMsWUFBWSxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUM7SUFDcEMsWUFBWSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUM7SUFDdEUsWUFBWSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUM7SUFDdEUsU0FBUztJQUNULEtBQUs7O0lBRUw7SUFDQTtJQUNBO0lBQ0E7SUFDQSxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsRUFBRSxTQUFTLEdBQUcsQ0FBQyxFQUFFLE1BQU0sR0FBRyxTQUFTLEVBQUUsSUFBSSxHQUFHLElBQUksRUFBRSxHQUFHLEVBQUUsRUFBRTtJQUMzRSxRQUFRLE9BQU8sQ0FBQyxTQUFTLEdBQUU7SUFDM0IsUUFBUSxPQUFPLENBQUMsTUFBTTtJQUN0QixZQUFZLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUM1QyxZQUFZLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElB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 diff --git a/3rdparty/all.js b/dist/iwmlib.3rdparty.js similarity index 100% rename from 3rdparty/all.js rename to dist/iwmlib.3rdparty.js diff --git a/dist/iwmlib.js b/dist/iwmlib.js new file mode 100644 index 0000000..d346075 --- /dev/null +++ b/dist/iwmlib.js @@ -0,0 +1,7379 @@ +(function () { + 'use strict'; + + // In order to test this interface implementation run jsc interface.js + + class Interface { + // Abstract interface that should be extended in interface subclasses. + // By convention all interfaces should start with an upper 'I' + + static implementationError(klass) { + let interfaceKeys = Reflect.ownKeys(this.prototype); + let classKeys = Reflect.ownKeys(klass.prototype); + for(let key of interfaceKeys) { + let interfaceDesc = this.prototype[key]; + let classDesc = klass.prototype[key]; + if (typeof(classDesc) == 'undefined') + return 'Missing ' + key + } + return null + } + + static implementedBy(klass) { + // In the first step only checks whether the methods of this + // interface are all implemented by the given class + let error = this.implementationError(klass); + return error == null + } + + // TODO: Specify optional methods + // static optionalMethods() { + // return [this.onMouseWheel] + // } + } + + /** Basic Application object to be used as a singleton. + Provides an interface for automatic testing and common device specific + feature detection. + */ + + class IApp extends Interface { + /** Build the app by registering event handlers, + * adding DOM elements, instanciating templates, etc... + */ + setup() { return this } + + /** Run the application by starting a main loop, ... + */ + run() { return this } + } + + class App extends Object { + /** Override this method to build your app. + */ + setup() { + return this + } + + /** Start and run the application. Override this method with everything + that is needed to maintain your App, main loops, etc. + */ + run() { + return this + } + + /** Defines all test suites. Overwrite this method to ensure that + all testable aspects of your App are evaluated. + */ + allTests() { + console.log('Overwrite App.allTests()'); + } + + /** Run all tests. Should return 'ok' and the amount of time needed to + run App.allTests() or a failure message with diagnostic error decription. + @return {array} - array with 'ok' as first element and needed time as + second element or "Tests failed" and an error string + */ + runTests() { + var start = performance.now(); + try { + this.allTests(); + var end = performance.now(); + return ['ok', end - start] + } + catch(e) { + console.trace(); + return ['Tests failed', e.message] + } + } + } + + IApp.implementedBy(App); + + // Allows browsers to perform doctests. + // Uses the code highlight package from http://highlightjs.readthedocs.io + // if available + + var docTestLogMessages = []; + + Array.prototype.equals = function(array) { + return this.length == array.length && + this.every( function(this_i,i) { return this_i == array[i] } ) + }; + + class Doctest { + + static assert(value) { + if (!value) { + throw new Error('Assertion violated') + } + } + + static pprint(obj) { + if (obj === null) + return 'null' + let stringified = obj.toString(); + if (stringified == '[object Object]') + return JSON.stringify(obj) + return stringified + } + + static expect(expr, value) { + if (this.pprint(expr) != this.pprint(value)) { + //throw new Error("got `" + expr + "` but expected `" + value + "`.") + throw new Error('got `' + this.pprint(expr) + '` but expected `' + this.pprint(value) + '`.') + } + } + + static expectError(error, message) { + let index = error.toString().indexOf(message); + if (index < 0) { + throw new Error('got `' + message + '` but expected `' + error + '`.') + } + } + + static expectLog(...messages) { + // if (!docTestLogMessages.equals(messages)) { + docTestLogMessages.forEach((msg, i) => { + if (msg != messages[i]) + throw new Error('Unexpected log message: `' + messages[i] + '`.') + }); + // throw new Error('Uups') + //} + } + + static log(message) { + docTestLogMessages.push(message); + } + + static highlight(code) { + if (typeof(hljs) == 'undefined') + return code + return hljs.highlight('javascript', code) + } + + static stripLeadingLines(code) { + let result = []; + let informative = false; + for(let line of code.split('\n')) { + if (line.trim().length > 0) { + informative = true; + } + if (informative) + result.push(line); + } + return result.join('\n') + } + + static event(type='mouse', {clientX = 0, clientY = 0} = {}) { + if (type.startsWith('mouse')) { + return new MouseEvent(type, { clientX, clientY }) + } + return { type, clientX, clientY } + } + + static run(replaceExpect=false) { + if (typeof(hljs) != 'undefined') { + hljs.initHighlighting(); + } + let doctests = document.querySelectorAll('.doctest'); + for(let i=0; i>> ').trim(); + if (line.endsWith(')') || line.endsWith(',')) { + line = line.slice(0, -1); + } + } + better.push(line); + } + pre.innerHTML = better.join('\n'); // text.value.replace(re, ">>> $1\n$2") + doctest.parentNode.replaceChild(pre, doctest); + } + } + } + + // Needed to make Doctest visible in modules + //window.Doctest = Doctest + + var recordedErrors = new Map(); + + class Errors { + + static countErrors() { + let total = 0; + for(let error of recordedErrors.keys()) { + total += recordedErrors.get(error).size; + } + return total + } + + static setStyle(element, styles) { + for(let key in styles) { + element.style[key] = styles[key]; + } + } + + static appendError(error, source) { + if (recordedErrors.has(error)) { + let sources = recordedErrors.get(error); + sources.add(source); + } + else { + recordedErrors.set(error, new Set([source])); + } + } + + static showErrors() { + if (this.countErrors() == 0) { + return + } + let errors = document.getElementById('runtime-errors'); + if (errors == null) { + errors = document.createElement('div'); + errors.setAttribute('id', 'runtime-errors'); + this.setStyle(document.body, { + border: '2px solid red' + }); + this.setStyle(errors, {position: 'absolute', + top: '0px', + padding: '8px', + width: '100%', + background: 'red', + color: 'white'}); + document.body.appendChild(errors); + let counter = document.createElement('div'); + counter.setAttribute('id', 'runtime-errors-counter'); + this.setStyle(counter, {borderRadius: '50%', + width: '32px', + height: '32px', + background: 'white', + color: 'red', + fontSize: '18px', + textAlign: 'center', + lineHeight: '32px', + verticalAlign: 'middle'}); + counter.innerHTML = '1'; + errors.appendChild(counter); + + let header = document.createElement('div'); + this.setStyle(header, {position: 'absolute', + top: '6px', + left: '48px', + height: '44px', + fontSize: '32px'}); + header.innerHTML = 'Runtime Errors'; + errors.appendChild(header); + errors.addEventListener('click', this.toggleErrors.bind(this)); + } + let counter = document.getElementById('runtime-errors-counter'); + counter.innerHTML = this.countErrors(); + } + + static expandErrors() { + let errors = document.getElementById('runtime-errors'); + for(let error of recordedErrors.keys()) { + for(var source of recordedErrors.get(error)) { + if (typeof(source) == 'undefined') { + source = 'See console for details'; + return + } + let info = document.createElement('div'); + info.className = 'info'; + info.style.wordWrap = 'break-word'; + info.innerHTML = error + `
${source}`; + errors.appendChild(info); + } + } + } + + static toggleErrors() { + let errors = document.getElementById('runtime-errors'); + let infos = errors.querySelectorAll('.info'); + if (infos.length > 0) { + infos.forEach((info) => errors.removeChild(info)); + } + else { + this.expandErrors(); + } + } + + static removeError(event) { + console.log('removeError', event); + if (recordedErrors.has(event.error)) { + let sources = recordedErrors.get(event.error); + sources.delete(event.source); + console.log('sources', sources); + } + } + + static registerGlobalErrorHandler() { + // Register more informative error handler + window.addEventListener('error', (event) => { + // if (typeof(event.error) == 'undefined') { + // console.info("Catched undefined error", event) + // } + this.appendError(event.error, event.filename); + }, true); + + document.addEventListener('DOMContentLoaded', (event) => { + this.showErrors(); + }); + } + + static registerFrameAwaitErrors() { + let iframes = document.getElementsByTagName('iframe'); + for(let i=0; i { + this.appendError('Cannot load iframe', target.src);}, + frameErrorTimeout); + target.onload = () => { + clearTimeout(target.iframeTimeout); + }; + } + } + } + + Errors.registerGlobalErrorHandler(); + + class Events { + + static stop(event) { + event.preventDefault(); + event.stopPropagation(); + } + + static extractPoint(event) { + switch (event.constructor.name) { + case 'TouchEvent': + for (let i = 0; i < event.targetTouches.length; i++) { + let t = event.targetTouches[i]; + return { x: t.clientX, y: t.clientY } + } + break + default: + return { x: event.clientX, y: event.clientY } + } + } + + static isCaptured(event) { + if (event.__capturedBy) + return true + return false + } + + static capturedBy(event, obj) { + event.__capturedBy = obj; + } + + static isPointerDown(event) { + // According to + // https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events + // pointer events use the buttons feature to represent pressed buttons + return event.buttons + } + + static isMouseDown(event) { + // Attempts to clone the which attribute of events failed in WebKit. May + // be this is a bug or a security feature. Workaround: we introduce + // a mouseDownSubstitute attribute that can be assigned to cloned + // events after instantiation. + if (Reflect.has(event, 'mouseDownSubstitute')) + return event.mouseDownSubstitute + return event.buttons || event.which + } + + static isSimulatedEvent(event) { + return Reflect.has(event, 'mouseDownSubstitute') + } + + static isMouseRightClick(event) { + return event.buttons || event.which + } + + static extractTouches(targets) { + let touches = []; + for (let i = 0; i < targets.length; i++) { + let t = targets[i]; + touches.push({ + targetSelector: this.selector(t.target), + identifier: t.identifier, + screenX: t.screenX, + screenY: t.screenY, + clientX: t.clientX, + clientY: t.clientY, + pageX: t.pageX, + pageY: t.pageY + }); + } + return touches + } + + static createTouchList(targets) { + let touches = []; + for (let i = 0; i < targets.length; i++) { + let t = targets[i]; + let touchTarget = document.elementFromPoint(t.pageX, t.pageY); + let touch = new Touch(undefined, touchTarget, t.identifier, + t.pageX, t.pageY, t.screenX, t.screenY); + touches.push(touch); + } + return new TouchList(...touches) + } + + static extractEvent(timestamp, event) { + let targetSelector = this.selector(event.target); + let infos = { + type: event.type, + time: timestamp, + constructor: event.constructor, + data: { + targetSelector: targetSelector, + view: event.view, + mouseDownSubstitute: event.buttons || event.which, // which cannot be cloned directly + bubbles: event.bubbles, + cancelable: event.cancelable, + screenX: event.screenX, + screenY: event.screenY, + clientX: event.clientX, + clientY: event.clientY, + layerX: event.layerX, + layerY: event.layerY, + pageX: event.pageX, + pageY: event.pageY, + ctrlKey: event.ctrlKey, + altKey: event.altKey, + shiftKey: event.shiftKey, + metaKey: event.metaKey + } + }; + if (event.type.startsWith('touch')) { + // On Safari-WebKit the TouchEvent has layerX, layerY coordinates + let data = infos.data; + data.targetTouches = this.extractTouches(event.targetTouches); + data.changedTouches = this.extractTouches(event.changedTouches); + data.touches = this.extractTouches(event.touches); + } + if (event.type.startsWith('pointer')) { + let data = infos.data; + data.pointerId = event.pointerId; + data.pointerType = event.pointerType; + } + if (Events.debug) { + Events.extracted.push(this.toLine(event)); + } + return infos + } + + static cloneEvent(type, constructor, data) { + if (type.startsWith('touch')) { + // We need to find target from layerX, layerY + //var target = document.querySelector(data.targetSelector) + // elementFromPoint(data.layerX, data.layerY) + //data.target = target + data.targetTouches = this.createTouchList(data.targetTouches); + data.changedTouches = this.createTouchList(data.changedTouches); + data.touches = this.createTouchList(data.touches); + } + // We need to find target from pageX, pageY which are only + // available after construction. They seem to getter items. + + let clone = Reflect.construct(constructor, [type, data]); + clone.mouseDownSubstitute = data.mouseDownSubstitute; + return clone + } + + static simulateEvent(type, constructor, data) { + data.target = document.querySelector(data.targetSelector); + let clone = this.cloneEvent(type, constructor, data); + if (data.target != null) { + data.target.dispatchEvent(clone); + } + if (Events.debug) { + Events.simulated.push(this.toLine(clone)); + } + } + + static toLine(event) { + return `${event.type} #${event.target.id} ${event.clientX} ${event.clientY}` + let result = event.type; + let selector = this.selector(event.target); + result += ' selector: ' + selector; + if (event.target != document.querySelector(selector)) + console.log('Cannot resolve', selector); + let keys = ['layerX', 'layerY', 'pageX', 'pageY', 'clientX', 'clientY']; + for (let key of keys) { + try { + result += ' ' + key + ':' + event[key]; + } + catch (e) { + console.log('Invalid key: ' + key); + } + } + return result + } + + static compareExtractedWithSimulated() { + if (this.extracted.length != this.simulated.length) { + alert('Unequal length of extracted [' + this.extracted.length + + '] and simulated events [' + this.simulated.length + '].'); + } + else { + for (let i = 0; i < this.extracted.length; i++) { + var extracted = this.extracted[i]; + var simulated = this.simulated[i]; + if (extracted != simulated) { + console.log('Events differ:' + extracted + '|' + simulated); + } + } + } + } + + static selector(context) { + return OptimalSelect.select(context) + } + + static reset() { + this.extracted = []; + this.simulated = []; + } + + static resetSimulated() { + this.simulated = []; + } + + static showExtractedEvents(event) { + if (!event.shiftKey) { + return + } + if (this.popup == null) { + let element = document.createElement('div'); + Elements.setStyle(element, { + position: 'absolute', + width: '480px', + height: '640px', + overflow: 'auto', + backgroundColor: 'lightgray' + }); + document.body.appendChild(element); + this.popup = element; + } + this.popup.innerHTML = ''; + for (let line of this.extracted) { + let div = document.createElement('div'); + div.innerHTML = line; + this.popup.appendChild(div); + } + let div = document.createElement('div'); + div.innerHTML = '------------ Simulated -----------'; + this.popup.appendChild(div); + for (let line of this.simulated) { + let div = document.createElement('div'); + div.innerHTML = line; + this.popup.appendChild(div); + } + Elements.setStyle(this.popup, + { left: event.clientX + 'px', top: event.clientY + 'px' }); + } + } + + Events.popup = null; + Events.debug = true; + Events.extracted = []; + Events.simulated = []; + Events.simulationRunning = false; + + class EventRecorder { + + constructor() { + this.recording = []; + this.recorded = []; + this.step = 0; + } + + record(event) { + let length = this.recording.length; + if (length == 0) { + this.startTime = event.timeStamp; + Events.reset(); + } + else { + let last = this.recording[length - 1]; + if (event.timeStamp < last.time) { + console.log('warning: wrong temporal order'); + } + } + let t = event.timeStamp - this.startTime; + this.recording.push(Events.extractEvent(t, event)); + } + + stopRecording() { + this.recorded = this.recording; + this.recording = []; + console.log('Recorded ' + this.recorded.length + ' events'); + } + + startReplay(whileCondition = null, onComplete = null) { + this.step = 0; + Events.resetSimulated(); + console.log('Start replay'); + Events.simulationRunning = true; + this.replay(whileCondition, onComplete); + } + + replay(whileCondition = null, onComplete = null) { + if (this.step < this.recorded.length) { + let { type, time, constructor, data } = this.recorded[this.step]; + Events.simulateEvent(type, constructor, data); + + this.step += 1; + let dt = 0; + if (this.step < this.recorded.length) { + var next = this.recorded[this.step]; + dt = next.time - time; + if (dt < 0) { + console.log('warning: wrong temporal order'); + } + } + if (whileCondition == null || whileCondition()) { + let delta = Math.round(dt); + setTimeout(() => this.replay(whileCondition, onComplete), delta); + } + } + else { + console.log('Played ' + this.step + ' events' + onComplete); + Events.simulationRunning = false; + if (onComplete != null) { + onComplete(); + } + //Events.compareExtractedWithSimulated() + } + } + } + + /* globals WebKitPoint */ + + /** Tests whether an object is empty + * @param {Object} obj - the object to be tested + * @return {boolean} + */ + function isEmpty(obj) { + // > isEmpty({}) + // true + for (let i in obj) { + return false + } + return true + } + + function lerp(start, stop, amt) { + return amt * (stop - start) + start + } + + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + // Taken from: https://davidwalsh.name/essential-javascript-functions + function debounce(func, wait, immediate) { + let timeout; + return function () { + let context = this, + args = arguments; + let later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + let callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + } + } + + /** Returns an id that is guaranteed to be unique within the livetime of the + * application + * @return {string} + */ + let _idGenerator = 0; + function getId$1() { + return 'id' + _idGenerator++ + } + + function randomInt(min = 0, max = 100) { + return Math.floor(Math.random() * (max - min + 1) + min) + } + + function randomFloat(min = 0.0, max = 1.0) { + return Math.random() * (max - min) + min + } + + class Dates { + + static create(fullYear, month, day) { + return new Date(Date.UTC(fullYear, month, day)) + } + + static daysInMonth(date) { + return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate() + } + + static startYearRange(date) { + return new Date(Date.UTC(date.getFullYear() - 1, 11, 31, 23, 59, 59, 999)) + } + + static endYearRange(date) { + return new Date(Date.UTC(date.getFullYear() + 1, 0, 1)) + } + + static prevYear(date, offset = 1) { + return this.create(date.getFullYear() - offset, 0, 1) + } + + static nextYear(date, offset = 1) { + return this.create(date.getFullYear() + offset, 0, 1) + } + + static nextMonth(date) { + return this.create(date.getFullYear(), date.getMonth() + 1, 1) + } + + static nextDay(date) { + return this.create( + date.getFullYear(), + date.getMonth(), + date.getDate() + 1 + ) + } + + static nextHour(date) { + // See http://stackoverflow.com/questions/1050720/adding-hours-to-javascript-date-object + return new Date(date.getTime() + 60 * 60 * 1000) + } + + static nextMinute(date) { + // See above + return new Date(date.getTime() + 60 * 1000) + } + + static nextSecond(date) { + // See above + return new Date(date.getTime() + 1000) + } + + static nextMillisecond(date) { + // See above + return new Date(date.getTime() + 1) + } + + static *iterYears(start, end) { + let date = this.create(start.getFullYear(), 0, 1); + while (date <= end) { + yield date; + date = this.nextYear(date); + } + yield date; + } + + static *iterMonths(year, limit = 12) { + let month = 0; + while (month < limit) { + let date = this.create(year.getFullYear(), month, 1); + yield date; + month += 1; + } + } + + static *iterMonthsOfYears(years) { + for (let year of years) { + for (let month of this.iterMonths(year)) { + yield month; + } + } + } + + static *iterDays(month) { + let day = 1; + let limit = Dates.daysInMonth(month); + while (day <= limit) { + let date = this.create(month.getFullYear(), month.getMonth(), day); + yield date; + day += 1; + } + } + + static *iterDaysOfMonths(months) { + for (let month of months) { + for (let day of this.iterDays(month)) { + yield day; + } + } + } + } + /* Color conversion functions */ + + class Colors { + // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + + static rgb2num(red, green, blue) { + let rgb = blue | (green << 8) | (red << 16); + return 0x000000 + rgb + } + + static rgb2hex(red, green, blue) { + let rgb = blue | (green << 8) | (red << 16); + return '#' + (0x1000000 + rgb).toString(16).slice(1) + } + + static hex2rgb(hex) { + // long version + let r = hex.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i); + if (r) { + return r.slice(1, 4).map(x => { + return parseInt(x, 16) + }) + } + // short version + r = hex.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i); + if (r) { + return r.slice(1, 4).map(x => { + return 0x11 * parseInt(x, 16) + }) + } + return null + } + + static rgb(r, g, b) { + return { r, g, b } + } + + static string2hex(str) { + return parseInt('0x' + str.slice(1)) + } + + static lerp(rgb1, rgb2, amount) { + return { + r: Math.round(lerp(rgb1.r, rgb2.r, amount)), + g: Math.round(lerp(rgb1.g, rgb2.g, amount)), + b: Math.round(lerp(rgb1.b, rgb2.b, amount)) + } + } + + static get violet() { + return Colors.rgb2num(89, 34, 131) + } + + static get steelblue() { + return Colors.rgb2num(0, 130, 164) + } + + static get ochre() { + return Colors.rgb2num(181, 157, 0) + } + + static get turquoise() { + return Colors.rgb2num(34, 164, 131) + } + + static get eminence() { + return Colors.rgb2num(150, 60, 134) + } + + static random() { + let r = Math.round(Math.random() * 255); + let g = Math.round(Math.random() * 255); + let b = Math.round(Math.random() * 255); + return Colors.rgb2num(r, g, b) + } + } + + class Cycle extends Array { + constructor(...items) { + super(); + for (let item of items) { + this.push(item); + } + this.index = 0; + } + + next() { + if (this.index == this.length) { + this.index = 0; + } + return this[this.index++] + } + + current() { + if (this.index === this.length) { + this.index = 0; + } + return this[this.index] + } + } + + /** Static methods to compute 2D points with x and y coordinates. + */ + class Points { + static length(a) { + return Math.sqrt(a.x * a.x + a.y * a.y) + } + + static normalize(p) { + let len = this.length(p); + return this.multiplyScalar(p, 1 / len) + } + + static mean(a, b) { + return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 } + } + + static subtract(a, b) { + return { x: a.x - b.x, y: a.y - b.y } + } + + static multiply(a, b) { + return { x: a.x * b.x, y: a.y * b.y } + } + + static divide(a, b) { + return { x: a.x / b.x, y: a.y / b.y } + } + + static multiplyScalar(a, b) { + return { x: a.x * b, y: a.y * b } + } + + static add(a, b) { + return { x: a.x + b.x, y: a.y + b.y } + } + + static negate(p) { + return { x: -p.x, y: -p.y } + } + + static angle(p1, p2) { + return Math.atan2(p1.y - p2.y, p1.x - p2.x) + } + + static normalizedAngle(p1, p2) { + return Angle.normalize(this.angle(p1, p2)) + } + + static normalized2Angle(p1, p2) { + return Angle.normalize2(this.angle(p1, p2)) + } + + static arc(p, alpha, radius) { + return { + x: p.x + radius * Math.cos(alpha), + y: p.y + radius * Math.sin(alpha) + } + } + + static distance(a, b) { + let dx = a.x - b.x; + let dy = a.y - b.y; + return Math.sqrt(dx * dx + dy * dy) + } + + static fromPageToNode(element, p) { + // if (window.webkitConvertPointFromPageToNode) { + // return window.webkitConvertPointFromPageToNode(element, + // new WebKitPoint(p.x, p.y)) + // } + return window.convertPointFromPageToNode(element, p.x, p.y) + } + + static fromNodeToPage(element, p) { + // if (window.webkitConvertPointFromNodeToPage) { + // return window.webkitConvertPointFromNodeToPage(element, + // new WebKitPoint(p.x, p.y)) + // } + return window.convertPointFromNodeToPage(element, p.x, p.y) + } + } + + /** + * A helper class for common set operations. + * + * @export + * @class Sets + */ + class Sets { + + /** + * Returns the intersection of all sets + * https://stackoverflow.com/questions/31930894/javascript-set-data-structure-intersect + * @static + * @param {*} sets + * @returns + * @memberof Sets + */ + static intersect(...sets) { + if (!sets.length) return new Set() + const i = sets.reduce((m, s, i) => s.size < sets[m].size ? i : m, 0); + const [smallest] = sets.splice(i, 1); + const res = new Set(); + for (let val of smallest) + if (sets.every(s => s.has(val))) + res.add(val); + return res + } + + /** + * Returns the union of all sets + * + * @static + * @param {*} sets + * @returns + * @memberof Sets + */ + static union(...sets) { + let result = new Set(); + for (let set of sets) { + for (let m of set) { + result.add(m); + } + } + return result + } + + /** + * Returns the difference of the given sets. Starts with the first set and removing all elements of the following sets. + * + * @static + * @param {*} set + * @param {*} sets + * @returns + * @memberof Sets + */ + static difference(set, ...sets) { + let result = new Set(); + for (let m of set) { + result.add(m); + } + for (let s of sets) { + for (let m of s) { + result.delete(m); + } + } + return result + } + } + + /** Static methods to compute angles. + */ + class Angle { + + static normalize(angle) { + let TAU = Math.PI * 2.0; + while (angle > Math.PI) { + angle -= TAU; + } + while (angle < -Math.PI) { + angle += TAU; + } + return angle + } + + static normalize2(angle) { + let TAU = Math.PI * 2.0; + while (angle > TAU) { + angle -= TAU; + } + while (angle < 0) { + angle += TAU; + } + return angle + } + + static normalizeDegree(angle) { + let full = 360.0; + while (angle > 180.0) { + angle -= full; + } + while (angle < -180.0) { + angle += full; + } + return angle + } + + static normalizedDiff(a, b) { + return this.normalize(this.diff(a, b)) + } + + static normalized2Diff(a, b) { + return this.normalize2(this.diff(a, b)) + } + + static diff(a, b) { + return Math.atan2(Math.sin(a - b), Math.cos(a - b)) + } + + static degree2radian(degree) { + return Math.PI * degree / 180.0 + } + + static radian2degree(rad) { + return 180.0 / Math.PI * rad + } + } + + class Elements$1 { + static setStyle(element, styles) { + for (let key in styles) { + element.style[key] = styles[key]; + } + } + + static addClass(element, cssClass) { + element.classList.add(cssClass); + } + + static removeClass(element, cssClass) { + element.classList.remove(cssClass); + } + + static toggleClass(element, cssClass) { + element.classList.toggle(cssClass); + } + + static hasClass(element, cssClass) { + return element.classList.contains(cssClass) + } + } + + class MapProxy { + /* This class is needed if we want to use the interaction classes + in Firefox 45.8 and modern Browsers. + + A workaround for https://github.com/babel/babel/issues/2334 + */ + constructor() { + this.map = new Map(); + } + + get size() { + return this.map.size + } + + get(key) { + return this.map.get(key) + } + + set(key, value) { + return this.map.set(key, value) + } + + delete(key) { + return this.map.delete(key) + } + + clear() { + return this.map.clear() + } + + has(key) { + return this.map.has(key) + } + + keys() { + return this.map.keys() + } + + values() { + return this.map.values() + } + + entries() { + return this.map.entries() + } + + forEach(func) { + this.map.forEach(func); + } + } + + /* Based om https://gist.github.com/cwleonard/e124d63238bda7a3cbfa */ + class Polygon { + /* + * This is the Polygon constructor. All points are center-relative. + */ + constructor(center) { + this.points = new Array(); + this.center = center; + } + + /* + * Point x and y values should be relative to the center. + */ + addPoint(p) { + this.points.push(p); + } + + /* + * Point x and y values should be absolute coordinates. + */ + addAbsolutePoint(p) { + this.points.push({ x: p.x - this.center.x, y: p.y - this.center.y }); + } + + /* + * Returns the number of sides. Equal to the number of vertices. + */ + getNumberOfSides() { + return this.points.length + } + + /* + * rotate the polygon by a number of radians + */ + rotate(rads) { + for (let i = 0; i < this.points.length; i++) { + let x = this.points[i].x; + let y = this.points[i].y; + this.points[i].x = Math.cos(rads) * x - Math.sin(rads) * y; + this.points[i].y = Math.sin(rads) * x + Math.cos(rads) * y; + } + } + + /* + * The draw function takes as a parameter a Context object from + * a Canvas element and draws the polygon on it. + */ + draw(context, { lineWidth = 2, stroke = '#000000', fill = null } = {}) { + context.beginPath(); + context.moveTo( + this.points[0].x + this.center.x, + this.points[0].y + this.center.y + ); + for (let i = 1; i < this.points.length; i++) { + context.lineTo( + this.points[i].x + this.center.x, + this.points[i].y + this.center.y + ); + } + context.closePath(); + context.lineWidth = lineWidth; + if (stroke) { + context.strokeStyle = stroke; + context.stroke(); + } + if (fill) { + context.fillStyle = fill; + context.fill(); + } + } + + absolutePoints() { + let result = new Array(); + for (let p of this.points) { + result.push(Points.add(p, this.center)); + } + return result + } + + flatAbsolutePoints() { + let result = new Array(); + for (let p of this.points) { + let a = Points.add(p, this.center); + result.push(a.x); + result.push(a.y); + } + return result + } + + /* + * This function returns true if the given point is inside the polygon, + * and false otherwise. + */ + containsPoint(pnt) { + let nvert = this.points.length; + let testx = pnt.x; + let testy = pnt.y; + + let vertx = new Array(); + for (let q = 0; q < this.points.length; q++) { + vertx.push(this.points[q].x + this.center.x); + } + + let verty = new Array(); + for (let w = 0; w < this.points.length; w++) { + verty.push(this.points[w].y + this.center.y); + } + + let i, + j = 0; + let c = false; + for (i = 0, j = nvert - 1; i < nvert; j = i++) { + if ( + verty[i] > testy != verty[j] > testy && + testx < + (vertx[j] - vertx[i]) * + (testy - verty[i]) / + (verty[j] - verty[i]) + + vertx[i] + ) + c = !c; + } + return c + } + + multiplyScalar(scale) { + let center = Points.multiplyScalar(this.center, scale); + let clone = new Polygon(center); + for (let p of this.points) { + clone.addPoint(Points.multiplyScalar(p, scale)); + } + return clone + } + + /* + * To detect intersection with another Polygon object, this + * function uses the Separating Axis Theorem. It returns false + * if there is no intersection, or an object if there is. The object + * contains 2 fields, overlap and axis. Moving the polygon by overlap + * on axis will get the polygons out of intersection. + */ + intersectsWith(other) { + let axis = { x: 0, y: 0 }; + let tmp, minA, maxA, minB, maxB; + let side, i; + let smallest = null; + let overlap = 99999999; + + /* test polygon A's sides */ + for (side = 0; side < this.getNumberOfSides(); side++) { + /* get the axis that we will project onto */ + if (side == 0) { + axis.x = + this.points[this.getNumberOfSides() - 1].y - + this.points[0].y; + axis.y = + this.points[0].x - + this.points[this.getNumberOfSides() - 1].x; + } else { + axis.x = this.points[side - 1].y - this.points[side].y; + axis.y = this.points[side].x - this.points[side - 1].x; + } + + /* normalize the axis */ + tmp = Math.sqrt(axis.x * axis.x + axis.y * axis.y); + axis.x /= tmp; + axis.y /= tmp; + + /* project polygon A onto axis to determine the min/max */ + minA = maxA = this.points[0].x * axis.x + this.points[0].y * axis.y; + for (i = 1; i < this.getNumberOfSides(); i++) { + tmp = this.points[i].x * axis.x + this.points[i].y * axis.y; + if (tmp > maxA) maxA = tmp; + else if (tmp < minA) minA = tmp; + } + /* correct for offset */ + tmp = this.center.x * axis.x + this.center.y * axis.y; + minA += tmp; + maxA += tmp; + + /* project polygon B onto axis to determine the min/max */ + minB = maxB = + other.points[0].x * axis.x + other.points[0].y * axis.y; + for (i = 1; i < other.getNumberOfSides(); i++) { + tmp = other.points[i].x * axis.x + other.points[i].y * axis.y; + if (tmp > maxB) maxB = tmp; + else if (tmp < minB) minB = tmp; + } + /* correct for offset */ + tmp = other.center.x * axis.x + other.center.y * axis.y; + minB += tmp; + maxB += tmp; + + /* test if lines intersect, if not, return false */ + if (maxA < minB || minA > maxB) { + return false + } else { + let o = maxA > maxB ? maxB - minA : maxA - minB; + if (o < overlap) { + overlap = o; + smallest = { x: axis.x, y: axis.y }; + } + } + } + + /* test polygon B's sides */ + for (side = 0; side < other.getNumberOfSides(); side++) { + /* get the axis that we will project onto */ + if (side == 0) { + axis.x = + other.points[other.getNumberOfSides() - 1].y - + other.points[0].y; + axis.y = + other.points[0].x - + other.points[other.getNumberOfSides() - 1].x; + } else { + axis.x = other.points[side - 1].y - other.points[side].y; + axis.y = other.points[side].x - other.points[side - 1].x; + } + + /* normalize the axis */ + tmp = Math.sqrt(axis.x * axis.x + axis.y * axis.y); + axis.x /= tmp; + axis.y /= tmp; + + /* project polygon A onto axis to determine the min/max */ + minA = maxA = this.points[0].x * axis.x + this.points[0].y * axis.y; + for (i = 1; i < this.getNumberOfSides(); i++) { + tmp = this.points[i].x * axis.x + this.points[i].y * axis.y; + if (tmp > maxA) maxA = tmp; + else if (tmp < minA) minA = tmp; + } + /* correct for offset */ + tmp = this.center.x * axis.x + this.center.y * axis.y; + minA += tmp; + maxA += tmp; + + /* project polygon B onto axis to determine the min/max */ + minB = maxB = + other.points[0].x * axis.x + other.points[0].y * axis.y; + for (i = 1; i < other.getNumberOfSides(); i++) { + tmp = other.points[i].x * axis.x + other.points[i].y * axis.y; + if (tmp > maxB) maxB = tmp; + else if (tmp < minB) minB = tmp; + } + /* correct for offset */ + tmp = other.center.x * axis.x + other.center.y * axis.y; + minB += tmp; + maxB += tmp; + + /* test if lines intersect, if not, return false */ + if (maxA < minB || minA > maxB) { + return false + } else { + let o = maxA > maxB ? maxB - minA : maxA - minB; + if (o < overlap) { + overlap = o; + smallest = { x: axis.x, y: axis.y }; + } + } + } + return { overlap: overlap + 0.001, axis: smallest } + } + + static fromPoints(points) { + let min = { x: Number.MAX_VALUE, y: Number.MAX_VALUE }; + let max = { x: Number.MIN_VALUE, y: Number.MIN_VALUE }; + for (let p of points) { + min.x = Math.min(p.x, min.x); + max.x = Math.max(p.x, max.x); + min.y = Math.min(p.y, min.y); + max.y = Math.max(p.y, max.y); + } + let center = Points.mean(min, max); + let polygon = new Polygon(center); + for (let p of points) { + polygon.addAbsolutePoint(p); + } + return polygon + } + } + + + /** + * Util functions to deal with DOMRects. + */ + class Rect { + + + /** + * Test if a given point is contained by the provided Rect. + * + * @static + * @param {DOMRect} rect - Rectangle to check the collision with. + * @param {Point} point - Point that should be tested. + * @returns {boolean} - True if point is inside of rect, otherwise false. + * @memberof Rect + */ + static contains(rect, point) { + return (point.x > rect.left && + point.x < rect.x + rect.right + && point.y > rect.top && point.y < rect.bottom) + } + + + /** + *Returns the position of an rect as point object. + * + * @static + * @param {Rect} rect - The rectangle we want to get the position from. + * @returns {Point} - Returns the position as Point. + * @memberof Rect + */ + static getPosition(rect) { + return { x: rect.x, y: rect.y } + } + } + + /** String utility functions */ + + class Strings { + + static toUpperCaseFirstChar(str) { + return str.substr(0, 1).toUpperCase() + str.substr(1) + } + + static toLowerCaseFirstChar(str) { + return str.substr(0, 1).toLowerCase() + str.substr(1) + } + + static toUpperCaseEachWord(str, delim = ' ') { + return str.split(delim).map((v) => v.toUpperCaseFirstChar()).join(delim) + } + + static toLowerCaseEachWord(str, delim = ' ') { + return str.split(delim).map((v) => v.toLowerCaseFirstChar()).join(delim) + } + + } + + /* globals Hammer, propagating */ + + /** Interaction patterns + + See interaction.html for explanation + */ + + class IInteractionTarget extends Interface { + capture(event) { + return typeof true + } + + onStart(event, interaction) { } + onMove(event, interaction) { } + onEnd(event, interaction) { } + + onMouseWheel(event) { } + } + + class IInteractionMapperTarget extends Interface { + capture(event) { + return typeof true + } + + findTarget(event, local, global) { + return IInteractionTarget + } + } + + class PointMap extends MapProxy { + // Collects touch points, mouse coordinates, etc. as key value pairs. + // Keys are pointer and touch ids, the special "mouse" key. + // Values are points, i.e. all objects with numeric x and y properties. + constructor(points = {}) { + super(); + for (let key in points) { + this.set(key, points[key]); + } + } + + toString() { + let points = []; + for (let key of this.keys()) { + let value = this.get(key); + points.push(`${key}:{x:${value.x}, y:${value.y}}`); + } + let attrs = points.join(', '); + return `[PointMap ${attrs}]` + } + + clone() { + let result = new PointMap(); + for (let key of this.keys()) { + let value = this.get(key); + result.set(key, { x: value.x, y: value.y }); + } + return result + } + + keyOf(value) { + for (let key of this.keys()) { + let p = this.get(key); + if (p.x == value.x && p.y == value.y) { + return key + } + } + return null + } + + firstKey() { + for (let key of this.keys()) { + return key + } + return null + } + + first() { + for (let key of this.keys()) { + return this.get(key) + } + return null + } + + farthests() { + if (this.size == 0) { + return null + } + let pairs = []; + for (let key of this.keys()) { + let p = this.get(key); + p.key = key; + for (let k of this.keys()) { + let q = this.get(k); + q.key = k; + pairs.push([p, q]); + } + } + let sorted = pairs.sort((a, b) => { + return Points.distance(b[0], b[1]) - Points.distance(a[0], a[1]) + }); + return sorted[0] + } + + mean() { + if (this.size == 0) { + return null + } + let x = 0.0, + y = 0.0; + for (let p of this.values()) { + x += p.x; + y += p.y; + } + return { x: x / this.size, y: y / this.size } + } + } + + class InteractionDelta { + constructor(x, y, zoom, rotate, about) { + this.x = x; + this.y = y; + this.zoom = zoom; + this.rotate = rotate; + this.about = about; + } + + toString() { + let values = []; + for (let key of Object.keys(this)) { + let value = this[key]; + if (key == 'about') { + values.push(`${key}:{x:${value.x}, y:${value.y}}`); + } else { + values.push(`${key}:${value}`); + } + } + let attrs = values.join(', '); + return `[InteractionDelta ${attrs}]` + } + } + + class InteractionPoints { + constructor(parent = null) { + this.parent = parent; + this.current = new PointMap(); + this.previous = new PointMap(); + this.start = new PointMap(); + this.ended = new PointMap(); + this.timestamps = new Map(); + } + + moved(key) { + let current = this.current.get(key); + let previous = this.previous.get(key); + return Points.subtract(current, previous) + } + + move() { + let current = this.current.mean(); + let previous = this.previous.mean(); + return Points.subtract(current, previous) + } + + /** + * Computes the delta between previous and current angles. Corrects + * value that are larger than 45° + * @param {*} a + * @param {*} b + * @returns delta + */ + diffAngle(a, b) { + let alpha = Math.atan2(Math.sin(a - b), Math.cos(a - b)); + if (Math.abs(alpha) > Math.PI / 4) { + alpha -= Math.PI; + } + return alpha + } + + /** + * Computes the delta between interaction points at t and t+1. + * + * @returns InteractionDelta + * @memberof InteractionPoints + */ + delta() { + let csize = this.current.size; + let psize = this.previous.size; + if (csize >= 2 && csize == psize) { + // Reduce to the two farthests points + let current = this.current.farthests(); + + let c1 = current[0]; + let c2 = current[1]; + + let p1 = this.previous.get(c1.key); + let p2 = this.previous.get(c2.key); + + //let p1 = previous[0] + //let p2 = previous[1] + + let d1 = Points.subtract(c1, p1); + let d2 = Points.subtract(c2, p2); + let cm = Points.mean(c1, c2); + //let pm = Points.mean(p1, p2) + // UO: Using the mean lead to jumps between time slices with 3 and 2 fingers + // We use the mean of deltas instead + let delta = Points.mean(d1, d2); //Points.subtract(cm, pm) + let zoom = 1.0; + let distance1 = Points.distance(p1, p2); + let distance2 = Points.distance(c1, c2); + if (distance1 != 0 && distance2 != 0) { + zoom = distance2 / distance1; + } + let currentAngle = Points.angle(c1, c2); + let previousAngle = Points.angle(p1, p2); + let alpha = this.diffAngle(currentAngle, previousAngle); + return new InteractionDelta(delta.x, delta.y, zoom, alpha, cm) + } else if (csize == 1 && psize == 1 && this.current.firstKey() == this.previous.firstKey()) { + // We need to ensure that the keys are the same + let current = this.current.first(); + let previous = this.previous.first(); + let delta = Points.subtract(current, previous); + return new InteractionDelta(delta.x, delta.y, 1.0, 0.0, current) + } + return null + } + + started(key, point) { + this.current.set(key, point); + this.start.set(key, point); + this.previous.set(key, point); + this.timestamps.set(key, performance.now()); + } + + update(key, point) { + // Returns true iff the key is new + this.current.set(key, point); + if (!this.start.has(key)) { + this.start.set(key, point); + this.previous.set(key, point); + this.timestamps.set(key, performance.now()); + return true + } + return false + } + + updatePrevious() { + for (let key of this.current.keys()) { + this.previous.set(key, this.current.get(key)); + } + } + + stop(key, point) { + if (this.current.has(key)) { + this.current.delete(key); + this.previous.delete(key); + this.ended.set(key, point); + } + } + + finish(key, point) { + this.current.delete(key); + this.previous.delete(key); + this.start.delete(key); + this.timestamps.delete(key); + this.ended.delete(key); + } + + isFinished() { + return this.current.size == 0 + } + + isNoLongerTwoFinger() { + return this.previous.size > 1 && this.current.size < 2 + } + + isTap(key) { + return this.parent.isTap(key) + } + + isDoubleTap(key) { + return this.parent.isDoubleTap(key) + } + + isLongPress(key) { + return this.parent.isLongPress(key) + } + } + + class Interaction extends InteractionPoints { + constructor(tapDistance = 10, tapDuration = 250.0, longPressTime = 500.0) { + super(); + this.tapDistance = tapDistance; + this.tapCounts = new Map(); + this.tapPositions = new Map(); + this.tapTimestamps = new Map(); + this.tapDuration = tapDuration; + this.longPressTime = longPressTime; + this.targets = new Map(); + this.subInteractions = new Map(); // target:Object : InteractionPoints + } + + stop(key, point) { + super.stop(key, point); + for (let points of this.subInteractions.values()) { + points.stop(key, point); + } + } + + addTarget(key, target) { + this.targets.set(key, target); + this.subInteractions.set(target, new InteractionPoints(this)); + } + + removeTarget(key) { + let target = this.targets.get(key); + this.targets.delete(key); + // Only remove target if no keys are refering to the target + let remove = true; + for (let t of this.targets.values()) { + if (target === t) { + remove = false; + } + } + if (remove) { + this.subInteractions.delete(target); + } + } + + finish(key, point) { + super.finish(key, point); + this.removeTarget(key); + } + + mapInteraction(points, aspects, mappingFunc) { + // Map centrally registered points to target interactions + // Returns an array of [target, updated subInteraction] pairs + let result = new Map(); + for (let key in points) { + if (this.targets.has(key)) { + let target = this.targets.get(key); + if (this.subInteractions.has(target)) { + let interaction = this.subInteractions.get(target); + for (let aspect of aspects) { + let pointMap = this[aspect]; + let point = pointMap.get(key); + let mapped = mappingFunc(point, target); + interaction[aspect].set(key, mapped); + } + result.set(target, interaction); + } + } + } + return result + } + + registerTap(key, point) { + if (this.tapCounts.has(key)) { + let count = this.tapCounts.get(key); + this.tapCounts.set(key, count+1); + } + else { + this.tapCounts.set(key, 1); + } + this.tapPositions.set(key, point); + this.tapTimestamps.set(key, performance.now()); + } + + unregisterTap(key) { + this.tapCounts.delete(key); + this.tapPositions.delete(key); + this.tapTimestamps.delete(key); + } + + isTap(key) { + let ended = this.ended.get(key); + let start = this.start.get(key); + if ( + start && + ended && + Points.distance(ended, start) < this.tapDistance + ) { + let t1 = this.timestamps.get(key); + let tookLong = performance.now() > t1 + this.longPressTime; + if (tookLong) { + return false + } + return true + } + return false + } + + isDoubleTap(key) { + let ended = this.ended.get(key); + if (this.tapCounts.has(key) && this.tapCounts.get(key) > 2) { + this.unregisterTap(key); + } + if (this.tapPositions.has(key)) { + let pos = this.tapPositions.get(key); + if (Points.distance(ended, pos) > this.tapDistance) { + this.unregisterTap(key); + } + } + if (this.tapTimestamps.has(key) && performance.now() > this.tapTimestamps.get(key) + this.tapDuration) { + //console.log("tap too long") + this.unregisterTap(key); + } + let result = false; + if (this.isTap(key)) { + + this.registerTap(key, ended); + result = this.tapCounts.get(key) == 2; + } + else { + this.unregisterTap(key); + } + //console.log("isDoubleTap", this.tapCounts.get(key), result) + return result + } + + isAnyTap() { + for (let key of this.ended.keys()) { + if (this.isTap(key)) return true + } + return false + } + + isLongPress(key) { + let ended = this.ended.get(key); + let start = this.start.get(key); + if ( + start && + ended && + Points.distance(ended, start) < this.tapDistance + ) { + let t1 = this.timestamps.get(key); + let tookLong = performance.now() > t1 + this.longPressTime; + if (tookLong) { + return true + } + return false + } + return false + } + + isAnyLongPress() { + for (let key of this.ended.keys()) { + if (this.isLongPress(key)) return true + } + return false + } + + isStylus(key) { + return key === 'stylus' + } + } + + /** + * This class implements the main delegate functionality: All necessary event handlers are registered for the + * given element. Uses PointerEvents if available or TouchEvents on iOS. The fallback is on mouse events. + * Collects the events if the interaction target captures the start event (i.e. declares that + * the target wants the start event as well as all following move and end evcents.) + * + * @export + * @class InteractionDelegate + */ + class InteractionDelegate { + // Long press: http://stackoverflow.com/questions/1930895/how-long-is-the-event-onlongpress-in-the-android + // Stylus support: https://w3c.github.io/touch-events/ + + /** + * Creates an instance of InteractionDelegate. + * @param {any} element + * @param {any} target + * @param {any} [{ mouseWheelElement = null, useCapture = true, capturePointerEvents = true, debug = false }={}] + * @memberof InteractionDelegate + */ + constructor( + element, + target, + { mouseWheelElement = null, useCapture = true, capturePointerEvents = true, cancelOnWindowOut = true, debug = false } = {} + ) { + this.debug = debug; + this.interaction = new Interaction(); + this.element = element; + this.mouseWheelElement = mouseWheelElement || element; + this.target = target; + this.useCapture = useCapture; + this.capturePointerEvents = capturePointerEvents; + this.cancelOnWindowOut = cancelOnWindowOut; + this.setupInteraction(); + } + + setupInteraction() { + if (this.debug) { + let error = this.targetInterface.implementationError( + this.target.constructor + ); + if (error != null) { + throw new Error('Expected IInteractionTarget: ' + error) + } + } + this.setupTouchInteraction(); + this.setupMouseWheelInteraction(); + } + + get targetInterface() { + return IInteractionTarget + } + + setupTouchInteraction() { + let element = this.element; + let useCapture = this.useCapture; + if (window.PointerEvent) { + if (this.debug) console.log('Pointer API' + window.PointerEvent); + element.addEventListener( + 'pointerdown', + e => { + if (this.debug) console.log('pointerdown', e.pointerId); + if (this.capture(e)) { + if (this.capturePointerEvents) { + try { + element.setPointerCapture(e.pointerId); + } catch (e) { } + } + this.onStart(e); + } + }, + useCapture + ); + element.addEventListener( + 'pointermove', + e => { + if (this.debug) console.log('pointermove', e.pointerId, e.pointerType); + + if ( + e.pointerType == 'touch' || + (e.pointerType == 'mouse' && Events.isPointerDown(e)) + ) { + // this.capture(e) && + if (this.debug) + console.log('pointermove captured', e.pointerId); + this.onMove(e); + } + }, + useCapture + ); + element.addEventListener( + 'pointerup', + e => { + if (this.debug) console.log('pointerup'); + this.onEnd(e); + if (this.capturePointerEvents) { + try { + element.releasePointerCapture(e.pointerId); + } catch (e) { } + } + }, + useCapture + ); + element.addEventListener( + 'pointercancel', + e => { + if (this.debug) console.log('pointercancel'); + this.onEnd(e); + if (this.capturePointerEvents) + element.releasePointerCapture(e.pointerId); + }, + useCapture + ); + + if (!this.capturePointerEvents) { + element.addEventListener( + 'pointerleave', + e => { + if (this.debug) console.log('pointerleave'); + if (e.target == element) this.onEnd(e); + }, + useCapture + ); + } + + if (!this.capturePointerEvents) { + element.addEventListener( + 'pointerout', + e => { + if (this.debug) console.log('pointerout'); + if (e.target == element) this.onEnd(e); + }, + useCapture + ); + } + + if (this.cancelOnWindowOut) { + window.addEventListener( + 'pointerout', + e => { + if (e.target == element) { + this.onEnd(e); + } + }, + useCapture); + } + + } else if (window.TouchEvent) { + if (this.debug) console.log('Touch API'); + element.addEventListener( + 'touchstart', + e => { + if (this.debug) + console.log('touchstart', this.touchPoints(e)); + if (this.capture(e)) { + for (let touch of e.changedTouches) { + this.onStart(touch); + } + } + }, + useCapture + ); + element.addEventListener( + 'touchmove', + e => { + if (this.debug) + console.log('touchmove', this.touchPoints(e), e); + for (let touch of e.changedTouches) { + this.onMove(touch); + } + for (let touch of e.targetTouches) { + this.onMove(touch); + } + }, + useCapture + ); + element.addEventListener( + 'touchend', + e => { + if (this.debug) console.log('touchend', this.touchPoints(e)); + for (let touch of e.changedTouches) { + this.onEnd(touch); + } + }, + useCapture + ); + element.addEventListener( + 'touchcancel', + e => { + if (this.debug) + console.log( + 'touchcancel', + e.targetTouches.length, + e.changedTouches.length + ); + for (let touch of e.changedTouches) { + this.onEnd(touch); + } + }, + useCapture + ); + } else { + if (this.debug) console.log('Mouse API'); + + element.addEventListener( + 'mousedown', + e => { + if (this.debug) console.log('mousedown', e); + if (this.capture(e)) { + this.onStart(e); + } + }, + useCapture + ); + element.addEventListener( + 'mousemove', + e => { + // Dow we only use move events if the mouse is down? + // HOver effects have to be implemented by other means + // && Events.isMouseDown(e)) + + if (Events.isMouseDown(e)) { + if (this.debug) + console.log('mousemove', e); + this.onMove(e); + } + }, + useCapture + ); + element.addEventListener( + 'mouseup', + e => { + if (this.debug) console.log('mouseup', e); + this.onEnd(e); + }, + true + ); + + if (!this.capturePointerEvents) { + element.addEventListener( + 'mouseout', + e => { + if (e.target == element) { + this.onEnd(e); + console.warn("Shouldn't happen: mouseout ends interaction"); + } + + }, + useCapture + ); + } + if (this.cancelOnWindowOut) { + window.addEventListener( + 'mouseout', + e => { + if (e.target == element) { + this.onEnd(e); + } + }, + useCapture); + } + } + } + + isDescendant(parent, child) { + if (parent == child) return true + let node = child.parentNode; + while (node != null) { + if (node == parent) { + return true + } + node = node.parentNode; + } + return false + } + + touchPoints(event) { + let result = []; + for (let touch of event.changedTouches) { + result.push(this.extractPoint(touch)); + } + return result + } + + setupMouseWheelInteraction() { + this.mouseWheelElement.addEventListener( + 'mousewheel', + this.onMouseWheel.bind(this), + true + ); + this.mouseWheelElement.addEventListener( + 'DOMMouseScroll', + this.onMouseWheel.bind(this), + true + ); + } + + onMouseWheel(event) { + if (this.capture(event) && this.target.onMouseWheel) { + this.target.onMouseWheel(event); + } + } + + onStart(event) { + let extracted = this.extractPoint(event); + this.startInteraction(event, extracted); + this.target.onStart(event, this.interaction); + } + + onMove(event) { + let extracted = this.extractPoint(event, 'all'); + this.updateInteraction(event, extracted); + this.target.onMove(event, this.interaction); + this.interaction.updatePrevious(); + } + + onEnd(event) { + let extracted = this.extractPoint(event, 'changedTouches'); + this.endInteraction(event, extracted); + this.target.onEnd(event, this.interaction); + this.finishInteraction(event, extracted); + } + + /** + * Asks the target whether the event should be captured + * + * @param {any} event + * @returns {bool} + * @memberof InteractionDelegate + */ + capture(event) { + if (Events.isCaptured(event)) { + return false + } + let captured = this.target.capture(event); + return captured + } + + getPosition(event) { + return { x: event.clientX, y: event.clientY } + } + + extractPoint(event, touchEventKey = 'all') { + // 'targetTouches' + let result = {}; + switch (event.constructor.name) { + case 'MouseEvent': + let buttons = event.buttons || event.which; + if (buttons) result['mouse'] = this.getPosition(event); + break + case 'PointerEvent': + result[event.pointerId.toString()] = this.getPosition(event); + break + case 'Touch': + let id = + event.touchType === 'stylus' + ? 'stylus' + : event.identifier.toString(); + result[id] = this.getPosition(event); + break + // case 'TouchEvent': + // // Needs to be observed: Perhaps changedTouches are all we need. If so + // // we can remove the touchEventKey default parameter + // if (touchEventKey == 'all') { + // for(let t of event.targetTouches) { + // result[t.identifier.toString()] = this.getPosition(t) + // } + // for(let t of event.changedTouches) { + // result[t.identifier.toString()] = this.getPosition(t) + // } + // } + // else { + // for(let t of event.changedTouches) { + // result[t.identifier.toString()] = this.getPosition(t) + // } + // } + // break + default: + break + } + return result + } + + interactionStarted(event, key, point) { + // Callback: can be overwritten + } + + interactionEnded(event, key, point) { + // Callback: can be overwritten + } + + interactionFinished(event, key, point) { } + + startInteraction(event, extracted) { + for (let key in extracted) { + let point = extracted[key]; + this.interaction.started(key, point); + this.interactionStarted(event, key, point); + } + } + + updateInteraction(event, extracted) { + for (let key in extracted) { + let point = extracted[key]; + let updated = this.interaction.update(key, point); + if (updated) { + console.warn("new pointer in updateInteraction shouldn't happen", key); + this.interactionStarted(event, key, point); + } + } + } + + endInteraction(event, ended) { + for (let key in ended) { + let point = ended[key]; + this.interaction.stop(key, point); + this.interactionEnded(event, key, point); + } + } + + finishInteraction(event, ended) { + for (let key in ended) { + let point = ended[key]; + this.interaction.finish(key, point); + this.interactionFinished(event, key, point); + } + } + } + /** + * A special InteractionDelegate that maps events to specific parts of + * the interaction target. The InteractionTarget must implement a findTarget + * method that returns an object implementing the IInteractionTarget interface. + * + * If the InteractionTarget also implements a mapPositionToPoint method this + * is used to map the points to the local coordinate space of the the target. + * + * This makes it easier to lookup elements and relate events to local + * positions. + * + * @export + * @class InteractionMapper + * @extends {InteractionDelegate} + */ + class InteractionMapper$1 extends InteractionDelegate { + + constructor( + element, + target, + { tapDistance = 10, longPressTime = 500.0, useCapture = true, mouseWheelElement = null } = {} + ) { + super(element, target, { tapDistance, useCapture, longPressTime, mouseWheelElement }); + } + + get targetInterface() { + return IInteractionMapperTarget + } + + mapPositionToPoint(point, element = null) { + if (this.target.mapPositionToPoint) { + return this.target.mapPositionToPoint(point, element) + } + return point + } + + interactionStarted(event, key, point) { + if (this.target.findTarget) { + let local = this.mapPositionToPoint(point); + let found = this.target.findTarget(event, local, point); + if (found != null) { + this.interaction.addTarget(key, found); + } + } + } + + onMouseWheel(event) { + if (this.capture(event)) { + if (this.target.findTarget) { + let point = this.getPosition(event); + let local = this.mapPositionToPoint(point); + let found = this.target.findTarget(event, local, point); + if (found != null && found.onMouseWheel) { + found.onMouseWheel(event); + return + } + } + if (this.target.onMouseWheel) { + this.target.onMouseWheel(event); + } + } + } + + onStart(event) { + let extracted = this.extractPoint(event); + this.startInteraction(event, extracted); + let mapped = this.interaction.mapInteraction( + extracted, + ['current', 'start'], + this.mapPositionToPoint.bind(this) + ); + for (let [target, interaction] of mapped.entries()) { + target.onStart(event, interaction); + } + } + + onMove(event) { + let extracted = this.extractPoint(event, 'all'); + this.updateInteraction(event, extracted); + let mapped = this.interaction.mapInteraction( + extracted, + ['current', 'previous'], + this.mapPositionToPoint.bind(this) + ); + for (let [target, interaction] of mapped.entries()) { + target.onMove(event, interaction); + interaction.updatePrevious(); + } + this.interaction.updatePrevious(); + } + + onEnd(event) { + let extracted = this.extractPoint(event, 'changedTouches'); + this.endInteraction(event, extracted); + let mapped = this.interaction.mapInteraction( + extracted, + ['ended'], + this.mapPositionToPoint.bind(this) + ); + for (let [target, interaction] of mapped.entries()) { + target.onEnd(event, interaction); + } + this.finishInteraction(event, extracted); + } + + /** + * + * + * @static + * @param {string|array} types - An event type, an array of event types or event types seperated by a space sign. The following + * events are possible: + * pan, panstart, panmove, panend, pancancel, panleft, panright, panup, pandown + * pinch, pinchstart, pinchmove, pinchend, pinchcancel, pinchin, pinchout + * press, pressup + * rotate, rotatestart, rotatemove, rotateend, rotatecancel + * swipe, swipeleft, swiperight, swipeup, swipedown + * tap + * @param {HTMLElement|HTMLElement[]} elements - An HTML element or an array of HTML elements. + * @param {function} [cb] - The callback. A function which is executed after the event occurs. Receives the event object as the + * first paramter + * @param {object} [opts] - An options object. See the hammer documentation for more details. + */ + static on(types, elements, cb, opts = {}) { + opts = Object.assign({}, { + + }, opts); + + if (typeof Hammer === 'undefined') { + console.error('Hammer.js not found!'); + return this + } + + // convert to array + types = Array.isArray(types) ? types : types.split(/\s/); + if (elements instanceof NodeList || elements instanceof HTMLCollection) { + elements = Array.from(elements); + } + elements = Array.isArray(elements) ? elements : [elements]; + + for (let i = 0; i < types.length; i++) { + + const type = types[i].toLowerCase(); + + // list of hammer events + const useHammer = /^(tap|doubletap|press|pan|swipe|pinch|rotate).*$/.test(type); + + // if it is a hammer event + if (useHammer) { + + for (let j = 0; j < elements.length; j++) { + + // if(elements[j].tagName == "svg") return false; + + let hammer = new Hammer(elements[j], opts); + + if (window.propagating !== 'undefined') { + hammer = propagating(hammer); + } + + // recognizers + if (type.startsWith('pan')) { + hammer.get('pan').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts)); + } else if (type.startsWith('pinch')) { + hammer.get('pinch').set(Object.assign({ enable: true }, opts)); + } else if (type.startsWith('press')) { + hammer.get('press').set(opts); + } else if (type.startsWith('rotate')) { + hammer.get('rotate').set(Object.assign({ enable: true }, opts)); + } else if (type.startsWith('swipe')) { + hammer.get('swipe').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts)); + } else if (type.startsWith('tap')) { + hammer.get('tap').set(opts); + } + + hammer.on(type, event => { + cb(event); + }); + } + + } else { + + for (let j = 0; j < elements.length; j++) { + Hammer.on(elements[j], type, event => { + cb(event); + }); + } + } + } + + return this + } + } + + window.InteractionMapper = InteractionMapper$1; + + /** Report capabilities with guaranteed values. + */ + class Capabilities { + + /** Returns the browser userAgent. + @return {string} + */ + static get userAgent() { + return navigator.userAgent || 'Unknown Agent' + } + + /** Tests whether the app is running on a mobile device. + Implemented as a readonly attribute. + @return {boolean} + */ + static get isMobile() { + return (/Mobi/.test(navigator.userAgent)) + } + + /** Tests whether the app is running on a iOS device. + Implemented as a readonly attribute. + @return {boolean} + */ + static get isIOS() { + return (/iPad|iPhone|iPod/.test(navigator.userAgent)) && !window.MSStream + } + + /** Tests whether the app is running in a Safari environment. + See https://stackoverflow.com/questions/7944460/detect-safari-browser + Implemented as a readonly attribute. + @return {boolean} + */ + static get isSafari() { + return navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && navigator.userAgent && !navigator.userAgent.match('CriOS') + } + + /** + * Distincts if the app is running inside electron or not. + * + * source: https://discuss.atom.io/t/detect-electron-or-web-page-running/33180/3 + */ + static get isElectron() { + return typeof process != 'undefined' && process.versions && process.versions.electron !== undefined + } + + /** Returns the display resolution. Necessary for retina displays. + @return {number} + */ + static get devicePixelRatio() { + return window.devicePixelRatio || 1 + } + + /** Returns true if the device is a multi-touch table. This method is currently not universal usable and not sure! + @return {boolean} + */ + static get isMultiTouchTable() { + return Capabilities.devicePixelRatio > 2 && Capabilities.isMobile === false && /Windows/i.test(Capabilities.userAgent) + } + + /** Returns true if mouse events are supported + @return {boolean} + */ + static supportsMouseEvents() { + return typeof(window.MouseEvent) != 'undefined' + } + + /** Returns true if touch events are supported + @return {boolean} + */ + static supportsTouchEvents() { + return typeof(window.TouchEvent) != 'undefined' + } + + /** Returns true if pointer events are supported + @return {boolean} + */ + static supportsPointerEvents() { + return typeof(window.PointerEvent) != 'undefined' + } + + /** Returns true if DOM templates are supported + @return {boolean} + */ + static supportsTemplate() { + return 'content' in document.createElement('template'); + } + } + + /** Basic tests for Capabilities. + */ + class CapabilitiesTests { + + static testConfirm() { + let bool = confirm('Please confirm'); + document.getElementById('demo').innerHTML = (bool) ? 'Confirmed' : 'Not confirmed'; + } + + static testPrompt() { + let person = prompt('Please enter your name', 'Harry Potter'); + if (person != null) { + demo.innerHTML = + 'Hello ' + person + '! How are you today?'; + } + } + + static testUserAgent() { + let agent = 'User-agent: ' + Capabilities.userAgent; + user_agent.innerHTML = agent; + } + + static testDevicePixelRatio() { + let value = 'Device Pixel Ratio: ' + Capabilities.devicePixelRatio; + device_pixel_ratio.innerHTML = value; + } + + static testMultiTouchTable() { + let value = 'Is the device a multi-touch table? ' + Capabilities.isMultiTouchTable; + multi_touch_table.innerHTML = value; + } + + static testSupportedEvents() { + let events = []; + if (Capabilities.supportsMouseEvents()) { + events.push('MouseEvents'); + } + if (Capabilities.supportsTouchEvents()) { + events.push('TouchEvents'); + } + if (Capabilities.supportsPointerEvents()) { + events.push('PointerEvents'); + } + supported_events.innerHTML = 'Supported Events: ' + events.join(', '); + } + + static testAll() { + this.testUserAgent(); + this.testDevicePixelRatio(); + this.testMultiTouchTable(); + this.testSupportedEvents(); + } + } + + /* Optional global variables, needed in DocTests. */ + window.Capabilities = Capabilities; + window.CapabilitiesTests = CapabilitiesTests; + + /** + * A base class for scatter specific events. + * + * @constructor + * @param {name} String - The name of the event + * @param {target} Object - The target of the event + */ + class BaseEvent { + constructor(name, target) { + this.name = name; + this.target = target; + } + } + + // Event types + const START = 'onStart'; + const UPDATE = 'onUpdate'; + const END = 'onEnd'; + + /** + * A scatter event that describes how the scatter has changed. + * + * @constructor + * @param {target} Object - The target scatter of the event + * @param {optional} Object - Optional parameter + */ + class ScatterEvent extends BaseEvent { + constructor( + target, + { + translate = { x: 0, y: 0 }, + scale = null, + rotate = 0, + about = null, + fast = false, + type = null + } = {} + ) { + super('scatterTransformed', { target: target }); + this.translate = translate; + this.scale = scale; + this.rotate = rotate; + this.about = about; + this.fast = fast; + this.type = type; + } + + toString() { + return ( + "Event('scatterTransformed', scale: " + + this.scale + + ' about: ' + + this.about.x + + ', ' + + this.about.y + + ')' + ) + } + } + + /** + * A scatter resize event that describes how the scatter has changed. + * + * @constructor + * @param {target} Object - The target scatter of the event + * @param {optional} Object - Optional parameter + */ + class ResizeEvent extends BaseEvent { + constructor(target, { width = 0, height = 0 } = {}) { + super('scatterResized', { width: width, height: height }); + this.width = width; + this.height = height; + } + + toString() { + return ( + 'Event(scatterResized width: ' + + this.width + + 'height: ' + + this.height + + ')' + ) + } + } + + /** + * A abstract base class that implements the throwable behavior of a scatter + * object. + * + * @constructor + */ + class Throwable { + constructor({ + movableX = true, + movableY = true, + throwVisibility = 44, + throwDamping = 0.95, + autoThrow = true, + onThrowFinished = null + } = {}) { + this.movableX = movableX; + this.movableY = movableY; + this.throwVisibility = throwVisibility; + this.throwDamping = throwDamping; + this.autoThrow = autoThrow; + this.velocities = []; + this.velocity = null; + this.timestamp = null; + this.onThrowFinished = onThrowFinished; + //console.log("onThrowFinished", onThrowFinished) + } + + observeVelocity() { + this.lastframe = performance.now(); + } + + addVelocity(delta, buffer = 5) { + let t = performance.now(); + let dt = t - this.lastframe; + this.lastframe = t; + if (dt > 0) { + // Avoid division by zero errors later on + let velocity = { t: t, dt: dt, dx: delta.x, dy: delta.y }; + this.velocities.push(velocity); + while (this.velocities.length > buffer) { + this.velocities.shift(); + } + } + } + + meanVelocity(milliseconds = 30) { + this.addVelocity({ x: 0, y: 0 }); + let sum = { x: 0, y: 0 }; + let count = 0; + let t = 0; + for (let i = this.velocities.length - 1; i > 0; i--) { + let v = this.velocities[i]; + t += v.dt; + let nv = { x: v.dx / v.dt, y: v.dy / v.dt }; + sum = Points.add(sum, nv); + count += 1; + if (t > milliseconds) { + break + } + } + if (count === 0) return sum // empty vector + return Points.multiplyScalar(sum, 1 / count) + } + + killAnimation() { + this.velocity = null; + this.velocities = []; + } + + startThrow() { + this.velocity = this.meanVelocity(); + if (this.velocity != null) { + // Call next velocity to ansure that specializations + // that use keepOnStage are called + this.velocity = this.nextVelocity(this.velocity); + if (this.autoThrow) this.animateThrow(performance.now()); + } else { + this.onDragComplete(); + } + } + + animateThrow(time) { + if (this.velocity != null) { + let t = performance.now(); + let dt = t - this.lastframe; + this.lastframe = t; + // console.log("animateThrow", dt) + let next = this.nextVelocity(this.velocity); + let prevLength = Points.length(this.velocity); + let nextLength = Points.length(next); + if (nextLength > prevLength) { + let factor = nextLength / prevLength; + next = Points.multiplyScalar(next, 1 / factor); + console.log('Prevent acceleration', factor, this.velocity, next); + } + this.velocity = next; + let d = Points.multiplyScalar(this.velocity, dt); + this._move(d); + + this.onDragUpdate(d); + if (dt == 0 || this.needsAnimation()) { + requestAnimationFrame(this.animateThrow.bind(this)); + return + } else { + if (this.isOutside()) { + requestAnimationFrame(this.animateThrow.bind(this)); + return + } + } + } + this.onDragComplete(); + if (this.onThrowFinished != null) { + this.onThrowFinished(); + } + } + + needsAnimation() { + if (this.velocity == null) { + return false + } + return Points.length(this.velocity) > 0.01 + } + + nextVelocity(velocity) { + // Must be overwritten: computes the changed velocity. Implement + // damping, collison detection, etc. here + let next = Points.multiplyScalar(velocity, this.throwDamping); + return { + x: (this.movableX) ? next.x : 0, + y: (this.movableY) ? next.y : 0 + } + } + + _move(delta) { + // Overwrite if necessary + } + + onDragComplete() { + // Overwrite if necessary + } + + onDragUpdate(delta) { + // Overwrite if necessary + } + } + + class AbstractScatter extends Throwable { + constructor({ + minScale = 0.1, + maxScale = 1.0, + startScale = 1.0, + autoBringToFront = true, + autoThrow = true, + translatable = true, + scalable = true, + rotatable = true, + resizable = false, + movableX = true, + movableY = true, + throwVisibility = 44, + throwDamping = 0.95, + overdoScaling = 1, + mouseZoomFactor = 1.1, + rotationDegrees = null, + rotation = null, + onTransform = null, + interactive = true, + onClose = null, + onThrowFinished = null, + scaleAutoClose = false, + scaleCloseThreshold = 0.10, + scaleCloseBuffer = 0.05 + } = {}) { + if (rotationDegrees != null && rotation != null) { + throw new Error('Use rotationDegrees or rotation but not both') + } else if (rotation != null) { + rotationDegrees = Angle.radian2degree(rotation); + } else if (rotationDegrees == null) { + rotationDegrees = 0; + } + super({ + movableX, + movableY, + throwVisibility, + throwDamping, + autoThrow, + onThrowFinished + }); + + /** + * Closes the card when the minScale is reached and the + * card is released. Card can be saved by scaling it up again. + */ + this.scaleAutoClose = scaleAutoClose; + this.scaleCloseThreshold = scaleCloseThreshold; + this.scaleCloseBuffer = scaleCloseBuffer; + this.scaleAutoCloseTimeout = null; + + this.interactive = interactive; + this.startRotationDegrees = rotationDegrees; + this.startScale = startScale; // Needed to reset object + this.minScale = minScale; + this.maxScale = maxScale; + this.overdoScaling = overdoScaling; + this.translatable = translatable; + if (!translatable) { + this.movableX = false; + this.movableY = false; + } + this.scalable = scalable; + this.rotatable = rotatable; + this.resizable = resizable; + this.mouseZoomFactor = mouseZoomFactor; + this.autoBringToFront = autoBringToFront; + this.dragging = false; + this.onTransform = onTransform != null ? [onTransform] : null; + this.onClose = onClose != null ? [onClose] : null; + } + + addCloseEventCallback(callback) { + if (this.onClose == null) { + this.onClose = []; + } + this.onClose.push(callback); + } + + addTransformEventCallback(callback) { + if (this.onTransform == null) { + this.onTransform = []; + } + this.onTransform.push(callback); + } + + startGesture(interaction) { + this.bringToFront(); + this.killAnimation(); + this.observeVelocity(); + return true + } + + close() { + if (this.onClose) { + this.onClose.forEach(callback => callback(this)); + } + } + + gesture(interaction) { + let delta = interaction.delta(); + //console.log("gesture", delta) + if (delta != null) { + this.addVelocity(delta); + this.transform(delta, delta.zoom, delta.rotate, delta.about); + if (delta.zoom != 1) this.interactionAnchor = delta.about; + } + } + + get polygon() { + let w2 = this.width * this.scale / 2; + let h2 = this.height * this.scale / 2; + let center = this.center; + let polygon = new Polygon(center); + polygon.addPoint({ x: -w2, y: -h2 }); + polygon.addPoint({ x: w2, y: -h2 }); + polygon.addPoint({ x: w2, y: h2 }); + polygon.addPoint({ x: -w2, y: h2 }); + polygon.rotate(this.rotation); + return polygon + } + + isOutside() { + let stagePolygon = this.containerPolygon; + if (stagePolygon == null) + return false + let polygon = this.polygon; + if (polygon == null) + return false + let result = stagePolygon.intersectsWith(polygon); + return result === false || result.overlap < this.throwVisibility + } + + recenter() { + // Return a small vector that guarantees that the scatter is moving + // towards the center of the stage + let center = this.center; + let target = this.container.center; + let delta = Points.subtract(target, center); + return Points.normalize(delta) + } + + nextVelocity(velocity) { + return this.keepOnStage(velocity) + } + + bouncing() { + // Implements the bouncing behavior of the scatter. Moves the scatter + // to the center of the stage if the scatter is outside the stage or + // not within the limits of the throwVisibility. + + let stagePolygon = this.containerPolygon; + let polygon = this.polygon; + let result = stagePolygon.intersectsWith(polygon); + if (result === false || result.overlap < this.throwVisibility) { + let cv = this.recenter(); + let recentered = false; + while (result === false || result.overlap < this.throwVisibility) { + polygon.center.x += cv.x; + polygon.center.y += cv.y; + this._move(cv); + result = stagePolygon.intersectsWith(polygon); + recentered = true; + } + return recentered + } + return false + } + + keepOnStage(velocity, collision = 0.5) { + let stagePolygon = this.containerPolygon; + if (!stagePolygon) return + let polygon = this.polygon; + let bounced = this.bouncing(); + if (bounced) { + let stage = this.containerBounds; + let x = this.center.x; + let y = this.center.y; + let dx = this.movableX ? velocity.x : 0; + let dy = this.movableY ? velocity.y : 0; + let factor = this.throwDamping; + // if (recentered) { + if (x < 0) { + dx = -dx; + factor = collision; + } + if (x > stage.width) { + dx = -dx; + factor = collision; + } + if (y < 0) { + dy = -dy; + factor = collision; + } + if (y > stage.height) { + dy = -dy; + factor = collision; + } + // } + return Points.multiplyScalar({ x: dx, y: dy }, factor) + } + return super.nextVelocity(velocity) + } + + endGesture(interaction) { + this.startThrow(); + this._checkAutoClose(); + } + + _checkAutoClose() { + if (this.scaleAutoClose) + if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) { + this.zoom(this.minScale, { animate: 0.2, onComplete: this.close.bind(this) }); + } else if (this.scale < this.minScale + this.scaleCloseThreshold) { + this.zoom(this.minScale + this.scaleCloseThreshold, { animate: 0.4 }); + } + } + + rotateDegrees(degrees, anchor) { + let rad = Angle.degree2radian(degrees); + this.rotate(rad, anchor); + } + + rotate(rad, anchor) { + this.transform({ x: 0, y: 0 }, 1.0, rad, anchor); + } + + move(d, { animate = 0 } = {}) { + if (this.translatable) { + if (animate > 0) { + let startPos = this.position; + TweenLite.to(this, animate, { + x: '+=' + d.x, + y: '+=' + d.y, + /* scale: scale, uo: not defined, why was this here? */ + onUpdate: e => { + let p = this.position; + let dx = p.x - startPos.x; + let dy = p.x - startPos.y; + this.onMoved(dx, dy); + } + }); + } else { + this._move(d); + this.onMoved(d.x, d.y); + } + } + } + + moveTo(p, { animate = 0 } = {}) { + let c = this.origin; + let delta = Points.subtract(p, c); + this.move(delta, { animate: animate }); + } + + centerAt(p, { animate = 0 } = {}) { + let c = this.center; + let delta = Points.subtract(p, c); + this.move(delta, { animate: animate }); + } + + zoom( + scale, + { + animate = 0, + about = null, + delay = 0, + x = null, + y = null, + onComplete = null + } = {} + ) { + let anchor = about || this.center; + if (scale != this.scale) { + if (animate > 0) { + TweenLite.to(this, animate, { + scale: scale, + delay: delay, + onComplete: onComplete, + onUpdate: this.onZoomed.bind(this) + }); + } else { + this.scale = scale; + this.onZoomed(anchor); + } + } + } + + _move(delta) { + this.x += this.movableX ? delta.x : 0; + this.y += this.movableX ? delta.y : 0; + } + + transform(translate, zoom, rotate, anchor) { + let delta = { + x: this.movableX ? translate.x : 0, + y: this.movableY ? translate.y : 0 + }; + if (this.resizable) var vzoom = zoom; + if (!this.translatable) delta = { x: 0, y: 0 }; + if (!this.rotatable) rotate = 0; + if (!this.scalable) zoom = 1.0; + if (zoom == 1.0 && rotate == 0) { + this._move(delta); + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: delta, + scale: this.scale, + rotate: 0, + about: anchor, + fast: false, + type: UPDATE + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + return + } + let origin = this.rotationOrigin; + let beta = Points.angle(origin, anchor); + let distance = Points.distance(origin, anchor); + let { scale: newScale, zoom: thresholdedZoom } = this.calculateScale(zoom); + + let newOrigin = Points.arc(anchor, beta + rotate, distance * thresholdedZoom); + let extra = Points.subtract(newOrigin, origin); + let offset = Points.subtract(anchor, origin); + this._move(offset); + this.scale = newScale; + this.rotation += rotate; + offset = Points.negate(offset); + offset = Points.add(offset, extra); + offset = Points.add(offset, translate); + this._move(offset); + + delta.x += extra.x; + delta.y += extra.y; + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: delta, + scale: newScale, + rotate: rotate, + about: anchor + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + if (this.resizable) { + this.resizeAfterTransform(vzoom); + } + } + + /** + * For a given zoom, a new scale is calculated, taking + * min and max scale into account. + * + * @param {number} zoom - The zoom factor, to scale the object with. + * @returns {object} - Returns an object containing the a value for a valid scale and the corrected zoom factor. + */ + calculateScale(zoom) { + let scale = this.scale * zoom; + + let minScale = this.minScale / this.overdoScaling; + let maxScale = this.maxScale * this.overdoScaling; + if (scale < minScale) { + scale = minScale; + zoom = scale / this.scale; + } + if (scale > maxScale) { + scale = maxScale; + zoom = scale / this.scale; + } + + if (this.scaleAutoClose) + this._updateTransparency(); + + return { zoom, scale } + } + + _updateTransparency() { + if (this.scale < this.minScale + this.scaleCloseThreshold) { + let transparency = this.calculateScaleTransparency(); + this.element.style.opacity = transparency; + } else this.element.style.opacity = 1; + } + + calculateScaleTransparency() { + let transparency = (this.scale - this.minScale) / this.scaleCloseThreshold; + transparency = (transparency > 1) ? 1 : (transparency < 0) ? 0 : transparency; + return transparency + } + + resizeAfterTransform(zoom) { + // Overwrite this in subclasses. + } + + validScale(scale) { + scale = Math.max(scale, this.minScale); + scale = Math.min(scale, this.maxScale); + return scale + } + + animateZoomBounce(dt = 1) { + if (this.zoomAnchor != null) { + let zoom = 1; + let amount = Math.min(0.01, 0.3 * dt / 100000.0); + if (this.scale < this.minScale) zoom = 1 + amount; + if (this.scale > this.maxScale) zoom = 1 - amount; + if (zoom != 1) { + this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor); + requestAnimationFrame(dt => { + this.animateZoomBounce(dt); + }); + return + } + this.zoomAnchor = null; + } + } + + checkScaling(about, delay = 0) { + this.zoomAnchor = about; + clearTimeout(this.animateZoomBounce.bind(this)); + setTimeout(this.animateZoomBounce.bind(this), delay); + } + + onMouseWheel(event) { + if (event.claimedByScatter) { + if (event.claimedByScatter != this) return + } + this.killAnimation(); + this.targetScale = null; + let direction = event.detail < 0 || event.wheelDelta > 0; + let globalPoint = { x: event.clientX, y: event.clientY }; + let centerPoint = this.mapPositionToContainerPoint(globalPoint); + if (event.shiftKey) { + let degrees = direction ? 5 : -5; + let rad = Angle.degree2radian(degrees); + return this.transform({ x: 0, y: 0 }, 1.0, rad, centerPoint) + } + const zoomFactor = this.mouseZoomFactor; + let zoom = direction ? zoomFactor : 1 / zoomFactor; + this.transform({ x: 0, y: 0 }, zoom, 0, centerPoint); + this.checkScaling(centerPoint, 200); + + if (this.scaleAutoClose) { + if (this.scale <= this.minScale + this.scaleCloseThreshold) { + + if (this.scaleAutoCloseTimeout) clearTimeout(this.scaleAutoCloseTimeout); + this.scaleAutoCloseTimeout = setTimeout(() => { + this._checkAutoClose(); + }, 600); + } + this._updateTransparency(); + } + // + // if (this.onTransform != null) { + // let event = new ScatterEvent(this, { + // translate: {x: 0, y: 0}, + // scale: this.scale, + // rotate: 0, + // about: null, + // fast: false, + // type: ZOOM + // }) + // this.onTransform.forEach(function(f) { + // f(event) + // }) + // } + } + + onStart(event, interaction) { + + if (this.startGesture(interaction)) { + this.dragging = true; + this.interactionAnchor = null; + } + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: { x: 0, y: 0 }, + scale: this.scale, + rotate: 0, + about: null, + fast: false, + type: START + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onMove(event, interaction) { + /** As long as mouseout && mouseleave interrupt we cannot be sure that + * dragging remains correct. + */ + if (this.dragging) { + this.gesture(interaction); + } + } + + onEnd(event, interaction) { + //console.log("Scatter.onEnd", this.dragging) + if (interaction.isFinished()) { + this.endGesture(interaction); + this.dragging = false; + for (let key of interaction.ended.keys()) { + if (interaction.isTap(key)) { + let point = interaction.ended.get(key); + this.onTap(event, interaction, point); + } + } + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: { x: 0, y: 0 }, + scale: this.scale, + rotate: 0, + about: null, + fast: false, + type: END + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + let about = this.interactionAnchor; + if (about != null) { + this.checkScaling(about, 100); + } + } + + onTap(event, interaction, point) { } + + onDragUpdate(delta) { + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + fast: true, + translate: delta, + scale: this.scale, + about: this.currentAbout, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onDragComplete() { + if (this.onTransform) { + let event = new ScatterEvent(this, { + scale: this.scale, + about: this.currentAbout, + fast: false, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onMoved(dx, dy, about) { + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: { x: dx, y: dy }, + about: about, + fast: true, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onResizing() { + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + scale: this.scale, + fast: false, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onZoomed(about) { + + if (this.scaleAutoClose) + this._updateTransparency(); + + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + scale: this.scale, + about: about, + fast: false, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + } + + /** A container for scatter objects, which uses a single InteractionMapper + * for all children. This reduces the number of registered event handlers + * and covers the common use case that multiple objects are scattered + * on the same level. + */ + class DOMScatterContainer { + /** + * @constructor + * @param {DOM node} element - DOM element that receives events + * @param {Bool} stopEvents - Whether events should be stopped or propagated + * @param {Bool} claimEvents - Whether events should be marked as claimed + * if findTarget return as non-null value. + * @param {String} [touchAction=none] - CSS to set touch action style, needed to prevent + * pointer cancel events. Use null if the + * the touch action should not be set. + */ + constructor( + element, + { stopEvents = 'auto', claimEvents = true, useCapture = true, touchAction = 'none' } = {} + ) { + this.onCapture = null; + this.element = element; + if (stopEvents === 'auto') { + if (Capabilities.isSafari) { + document.addEventListener( + 'touchmove', + event => this.preventPinch(event), + false + ); + stopEvents = false; + } else { + stopEvents = true; + } + } + this.stopEvents = stopEvents; + this.claimEvents = claimEvents; + if (touchAction !== null) { + Elements$1.setStyle(element, { touchAction }); + } + this.scatter = new Map(); + this.delegate = new InteractionMapper$1(element, this, { + useCapture, + mouseWheelElement: window + }); + + if (typeof debugCanvas !== 'undefined') { + requestAnimationFrame(dt => { + this.showTouches(dt); + }); + } + } + + showTouches(dt) { + let resolution = window.devicePixelRatio; + let canvas = debugCanvas; + let current = this.delegate.interaction.current; + let context = canvas.getContext('2d'); + let radius = 20 * resolution; + context.clearRect(0, 0, canvas.width, canvas.height); + context.fillStyle = 'rgba(0, 0, 0, 0.3)'; + context.lineWidth = 2; + context.strokeStyle = '#003300'; + for (let [key, point] of current.entries()) { + let local = point; + context.beginPath(); + context.arc( + local.x * resolution, + local.y * resolution, + radius, + 0, + 2 * Math.PI, + false + ); + context.fill(); + context.stroke(); + } + requestAnimationFrame(dt => { + this.showTouches(dt); + }); + } + + preventPinch(event) { + event = event.originalEvent || event; + if (event.scale !== 1) { + event.preventDefault(); + } + } + + add(scatter) { + this.scatter.set(scatter.element, scatter); + } + + capture(event) { + if (this.onCapture) { + return this.onCapture(event) + } + if (event.target == this.element && this.stopEvents) { + Events.stop(event); + } + return true + } + + mapPositionToPoint(point) { + return Points.fromPageToNode(this.element, point) + } + + isDescendant(parent, child, clickable = false) { + if (parent == child) return true + let node = child.parentNode; + while (node != null) { + if (!clickable && node.onclick) { + return false + } + if (node == parent) { + return true + } + node = node.parentNode; + } + return false + } + + findTarget(event, local, global) { + /*** Note that elementFromPoint works with clientX, clientY, not pageX, pageY + The important point is that event should not be used, since the TouchEvent + points are hidden in sub objects. + ***/ + let found = document.elementFromPoint(global.x, global.y); + for (let target of this.scatter.values()) { + if (target.interactive && this.isDescendant(target.element, found)) { + if (this.stopEvents) Events.stop(event); + if (this.claimEvents) event.claimedByScatter = target; + return target + } + } + return null + } + + get center() { + let r = this.bounds; + let w2 = r.width / 2; + let h2 = r.height / 2; + return { x: w2, y: h2 } + } + + get bounds() { + return this.element.getBoundingClientRect() + } + + get polygon() { + let r = this.bounds; + let w2 = r.width / 2; + let h2 = r.height / 2; + let center = { x: w2, y: h2 }; + let polygon = new Polygon(center); + polygon.addPoint({ x: -w2, y: -h2 }); + polygon.addPoint({ x: w2, y: -h2 }); + polygon.addPoint({ x: w2, y: h2 }); + polygon.addPoint({ x: -w2, y: h2 }); + return polygon + } + } + + + class DOMScatter extends AbstractScatter { + constructor( + element, + container, + { + startScale = 1.0, + minScale = 0.1, + maxScale = 1.0, + overdoScaling = 1.5, + autoBringToFront = true, + translatable = true, + scalable = true, + rotatable = true, + movableX = true, + movableY = true, + rotationDegrees = null, + rotation = null, + onTransform = null, + transformOrigin = 'center center', + // extras which are in part needed + x = 0, + y = 0, + width = null, // required + height = null, // required + resizable = false, + simulateClick = false, + verbose = true, + onResize = null, + touchAction = 'none', + throwVisibility = 44, + throwDamping = 0.95, + autoThrow = true, + scaleAutoClose = false, + onClose = null, + scaleCloseThreshold = 0.10, + scaleCloseBuffer = 0.05 + } = {} + ) { + super({ + minScale, + maxScale, + startScale, + overdoScaling, + autoBringToFront, + translatable, + scalable, + rotatable, + movableX, + movableY, + resizable, + rotationDegrees, + rotation, + onTransform, + throwVisibility, + throwDamping, + autoThrow, + scaleAutoClose, + scaleCloseThreshold, + scaleCloseBuffer, + onClose + }); + if (container == null || width == null || height == null) { + throw new Error('Invalid value: null') + } + element.scatter = this; + this.element = element; + this.x = x; + this.y = y; + this.oldX = 0; + this.oldY = 0; + this.meanX = x; + this.meanY = y; + this.width = width; + this.height = height; + this.throwVisibility = Math.min(width, height, throwVisibility); + this.container = container; + this.simulateClick = simulateClick; + this.scale = startScale; + this.rotationDegrees = this.startRotationDegrees; + this.transformOrigin = transformOrigin; + this.initialValues = { + x: x, + y: y, + width: width, + height: height, + scale: startScale, + rotation: this.startRotationDegrees, + transformOrigin: transformOrigin + }; + + + // For tweenlite we need initial values in _gsTransform + TweenLite.set(element, this.initialValues); + this.onResize = onResize; + this.verbose = verbose; + if (touchAction !== null) { + Elements$1.setStyle(element, { touchAction }); + } + this.resizeButton = null; + if (resizable) { + let button = document.createElement("div"); + button.style.position = "absolute"; + button.style.right = "0px"; + button.style.bottom = "0px"; + button.style.width = "50px"; + button.style.height = "50px"; + // button.style.borderRadius = "100% 0px 0px 0px"; + // button.style.background = this.element.style.backgroundColor + button.className = "interactiveElement"; + this.element.appendChild(button); + + button.addEventListener('pointerdown', (e) => { + this.startResize(e); + }); + + button.addEventListener('pointermove', (e) => { + this.resize(e); + }); + + button.addEventListener('pointerup', (e) => { + this.stopResize(e); + }); + this.resizeButton = button; + } + container.add(this); + } + + /** Returns geometry data as object. **/ + getState() { + return { + scale: this.scale, + x: this.x, + y: this.y, + rotation: this.rotation + } + } + + close() { + super.close(); + let parent = this.element.parentNode; + if (parent) parent.removeChild(this.element); + } + + get rotationOrigin() { + return this.center + } + + get x() { + return this._x + } + + get y() { + return this._y + } + + set x(value) { + this._x = value; + TweenLite.set(this.element, { x: value }); + } + + set y(value) { + this._y = value; + TweenLite.set(this.element, { y: value }); + } + + get position() { + let transform = this.element._gsTransform; + let x = transform.x; + let y = transform.y; + return { x, y } + } + + get origin() { + let p = this.fromNodeToPage(0, 0); + return Points.fromPageToNode(this.container.element, p) + } + + get bounds() { + let stage = this.container.element.getBoundingClientRect(); + let rect = this.element.getBoundingClientRect(); + return { + top: rect.top - stage.top, + left: rect.left - stage.left, + width: rect.width, + height: rect.height + } + } + + get center() { + let r = this.bounds; + let w2 = r.width / 2; + let h2 = r.height / 2; + // if (this.resizable) { + // w2 *= this.scale + // h2 *= this.scale + // } + var x = r.left + w2; + var y = r.top + h2; + return { x, y } + } + + set rotation(radians) { + let rad = radians; // Angle.normalize(radians) + let degrees = Angle.radian2degree(rad); + TweenLite.set(this.element, { rotation: degrees }); + this._rotation = rad; + } + + set rotationDegrees(degrees) { + let deg = degrees; // Angle.normalizeDegree(degrees) + TweenLite.set(this.element, { rotation: deg }); + this._rotation = Angle.degree2radian(deg); + } + + get rotation() { + return this._rotation + } + + get rotationDegrees() { + return this._rotation + } + + set scale(scale) { + TweenLite.set(this.element, { + scale: scale, + transformOrigin: this.transformOrigin + }); + this._scale = scale; + } + + get scale() { + return this._scale + } + + get containerBounds() { + return this.container.bounds + } + + get containerPolygon() { + return this.container.polygon + } + + mapPositionToContainerPoint(point) { + return this.container.mapPositionToPoint(point) + } + + capture(event) { + return true + } + + reset() { + TweenLite.set(this.element, this.initialValues); + } + + hide() { + TweenLite.to(this.element, 0.1, { + display: 'none', + onComplete: e => { + this.element.parentNode.removeChild(this.element); + } + }); + } + + show() { + TweenLite.set(this.element, { display: 'block' }); + } + + showAt(p, rotationDegrees) { + TweenLite.set(this.element, { + display: 'block', + x: p.x, + y: p.y, + rotation: rotationDegrees, + transformOrigin: this.transformOrigin + }); + } + + bringToFront() { + // this.element.parentNode.appendChild(this.element) + // uo: On Chome and Electon appendChild leads to flicker + TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ }); + } + + toggleVideo(element) { + if (element.paused) { + element.play(); + } else { + element.pause(); + } + } + + onTap(event, interaction, point) { + if (this.simulateClick) { + let p = Points.fromPageToNode(this.element, point); + let iframe = this.element.querySelector('iframe'); + if (iframe) { + let doc = iframe.contentWindow.document; + let element = doc.elementFromPoint(p.x, p.y); + if (element == null) { + return + } + switch (element.tagName) { + case 'VIDEO': + console.log(element.currentSrc); + if (PopupMenu) { + PopupMenu.open( + { + Fullscreen: () => + window.open(element.currentSrc), + Play: () => element.play() + }, + { x, y } + ); + } else { + this.toggleVideo(element); + } + break + default: + element.click(); + } + } + } + } + + isDescendant(parent, child) { + let node = child.parentNode; + while (node != null) { + if (node == parent) { + return true + } + node = node.parentNode; + } + return false + } + + fromPageToNode(x, y) { + return Points.fromPageToNode(this.element, { x, y }) + } + + fromNodeToPage(x, y) { + return Points.fromNodeToPage(this.element, { x, y }) + } + + _move(delta) { + // UO: We need to keep TweenLite's _gsTransform and the private + // _x and _y attributes aligned + let x = this.element._gsTransform.x; + let y = this.element._gsTransform.y; + if (this.movableX) { + x += delta.x; + } + if (this.movableY) { + y += delta.y; + } + this._x = x; + this._y = y; + TweenLite.set(this.element, { x: x, y: y }); + } + + resizeAfterTransform(zoom) { + // let w = this.width * this.scale + // let h = this.height * this.scale + // TweenLite.set(this.element, { width: w, height: h }) + if (this.onResize) { + let event = new ResizeEvent(this, { width: w, height: h }); + this.onResize(event); + } + if (this.resizeButton != null) ; + } + + startResize(e) { + e.preventDefault(); + let event = new CustomEvent('resizeStarted'); + + let oldPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; + this.bringToFront(); + + this.element.style.transformOrigin = "0% 0%"; + + let newPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; + + let offset = Points.subtract(oldPostition, newPostition); + + this.oldX = e.clientX; + this.oldY = e.clientY; + + e.target.setAttribute('resizing', "true"); + e.target.setPointerCapture(e.pointerId); + + TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } }); + TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } }); + + this.element.dispatchEvent(event); + } + + resize(e) { + e.preventDefault(); + + let rotation = Angle.radian2degree(this.rotation); + rotation = (rotation + 360) % 360; + let event = new CustomEvent('resized'); + if (e.target.getAttribute('resizing') == "true") { + + let deltaX = (e.clientX - this.oldX); + let deltaY = (e.clientY - this.oldY); + + let r = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); + let phi = Angle.radian2degree(Math.atan2(deltaX, deltaY)); + + phi = ((phi) + 630) % 360; + let rot = ((rotation + 90) + 630) % 360; + + let diffAngle = ((0 + rot) + 360) % 360; + let phiCorrected = (phi + diffAngle + 360) % 360; + + let resizeW = r * Math.cos(Angle.degree2radian(phiCorrected)); + let resizeH = -r * Math.sin(Angle.degree2radian(phiCorrected)); + + if (this.element.offsetWidth + resizeW / this.scale > this.width * 0.3 && this.element.offsetHeight + resizeH / this.scale > this.height * 0.3) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, height: this.element.offsetHeight + resizeH / this.scale }); + + this.oldX = e.clientX; + this.oldY = e.clientY; + this.onResizing(); + + this.element.dispatchEvent(event); + } + } + + stopResize(e) { + e.preventDefault(); + + let event = new CustomEvent('resizeEnded'); + let oldPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; + this.element.style.transformOrigin = "50% 50%"; + let newPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; + let offset = Points.subtract(oldPostition, newPostition); + + TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } }); + TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } }); + + e.target.setAttribute('resizing', "false"); + + this.element.dispatchEvent(event); + } + } + + DOMScatter.zIndex = 1000; + + class CardLoader { + constructor( + src, + { + x = 0, + y = 0, + width = 1000, + height = 800, + maxWidth = null, + maxHeight = null, + scale = 1, + minScale = 0.5, + maxScale = 1.5, + rotation = 0 + } = {} + ) { + this.src = src; + this.x = x; + this.y = y; + this.scale = scale; + this.rotation = 0; + this.maxScale = maxScale; + this.minScale = minScale; + this.wantedWidth = width; + this.wantedHeight = height; + this.maxWidth = maxWidth != null ? maxWidth : window.innerWidth; + this.maxHeight = maxHeight != null ? maxHeight : window.innerHeight; + this.addedNode = null; + console.log({ + + width, + height, + maxWidth, + maxHeight, + + }); + } + + unload() { + if (this.addedNode) { + this.addedNode.remove(); + this.addedNode = null; + } + } + } + + class PDFLoader extends CardLoader { + constructor(src, { width = 1640, height = 800, scale = 1 } = {}) { + super(src, { width, height, scale }); + if (typeof PDFJS == 'undefined') { + alert('PDF.js needed'); + } + } + + load(domNode) { + return new Promise((resolve, reject) => { + PDFJS.getDocument(this.src).then(pdf => { + pdf.getPage(1).then(page => { + let scale = this.scale * app.renderer.resolution; + let invScale = 1 / scale; + let viewport = page.getViewport(scale); + + // Prepare canvas using PDF page dimensions. + let canvas = document.createElement('canvas'); + let context = canvas.getContext('2d'); + canvas.height = viewport.height; + canvas.width = viewport.width; + + // Render PDF page into canvas context. + let renderContext = { + canvasContext: context, + viewport: viewport + }; + page.render(renderContext); + domNode.appendChild(canvas); + this.wantedWidth = canvas.width; + this.wantedHeight = canvas.height; + this.scale = invScale; + this.addedNode = canvas; + resolve(this); + }); + }); + }) + } + } + + class ImageLoader extends CardLoader { + load(domNode) { + return new Promise((resolve, reject) => { + let isImage = domNode instanceof HTMLImageElement; + let image = isImage ? domNode : document.createElement('img'); + image.onload = e => { + if (!isImage) { + domNode.appendChild(image); + this.addedNode = image; + } + this.wantedWidth = image.naturalWidth; + this.wantedHeight = image.naturalHeight; + + let scaleW = this.maxWidth / image.naturalWidth; + let scaleH = this.maxHeight / image.naturalHeight; + this.scale = Math.min(this.maxScale, Math.min(scaleW, scaleH)); + image.setAttribute('draggable', false); + image.width = image.naturalWidth; + image.height = image.naturalHeight; + resolve(this); + }; + image.onerror = e => { + reject(this); + }; + image.src = this.src; + }) + } + } + + class FrameLoader extends CardLoader { + load(domNode) { + return new Promise((resolve, reject) => { + let isFrame = domNode instanceof HTMLIFrameElement; + let iframe = isFrame ? domNode : document.createElement('iframe'); + console.log('FrameLoader.load', isFrame, iframe, this.src); + iframe.frameBorder = 0; + iframe.style.scrolling = false; + iframe.width = this.wantedWidth; + iframe.height = this.wantedHeight; + if (!isFrame) { + // Unlike img onload is only triggered if the iframe is part of the DOM tree + domNode.appendChild(iframe); + this.addedNode = iframe; + } + iframe.onload = e => { + resolve(this); + }; + iframe.onerror = e => { + reject(this); + }; + iframe.src = this.src; + }) + } + } + + class HTMLLoader extends CardLoader { + load(domNode) { + return new Promise((resolve, reject) => { + let xhr = new XMLHttpRequest(); + xhr.open('GET', this.src, false); + xhr.onload = e => { + domNode.innerHTML = xhr.response; + this.addedNode = domNode.firstElementChild; + let { width, height } = this.size(this.addedNode); + console.log("HTMLLoader.load", { added: this.addedNode, width, height }); + if (width) + this.wantedWidth = width || this.wantedWidth; + if (height) + this.wantedHeight = height || this.wantedHeight; + resolve(this); + }; + xhr.onerror = e => { + reject(this); + }; + xhr.send(); + }) + } + + /** + * Tries to determine the size of the addedNode. + * Checks for explicit width and height style attributes. + * + * Overwrite this method if you want to extract values from other infos. + * + * @returns { width: int, height: int } + * @memberof HTMLLoader + */ + size(node) { + let width = parseInt(node.style.width) || null; + let height = parseInt(node.style.height) || null; + return { width, height } + } + } + + class DOMFlip { + constructor( + domScatterContainer, + flipTemplate, + frontLoader, + backLoader, + { + closeOnMinScale = false, + flipDuration = 1, + fadeDuration = 0.2, + overdoScaling = 1, + autoLoad = false, + center = null, + preloadBack = false, + translatable = true, + scalable = true, + rotatable = true, + onFront = null, + onBack = null, + onClose = null, + onUpdate = null, + onRemoved = null, + onLoaded = null + } = {} + ) { + this.domScatterContainer = domScatterContainer; + this.id = getId$1(); + this.flipDuration = flipDuration; + this.fadeDuration = fadeDuration; + this.closeOnMinScale = closeOnMinScale; + this.flipTemplate = flipTemplate; + this.frontLoader = frontLoader; + this.backLoader = backLoader; + this.translatable = translatable; + this.scalable = scalable; + this.rotatable = rotatable; + this.onFrontFlipped = onFront; + this.onBackFlipped = onBack; + this.onClose = onClose; + this.onRemoved = onRemoved; + this.onUpdate = onUpdate; + this.onLoaded = onLoaded; + this.center = center; + this.preloadBack = preloadBack; + this.overdoScaling = overdoScaling; + if (autoLoad) { + this.load(); + } + } + + load() { + return new Promise((resolve, reject) => { + let t = this.flipTemplate; + let dom = this.domScatterContainer.element; + let wrapper = t.content.querySelector('.flipWrapper'); + wrapper.id = this.id; + let clone = document.importNode(t.content, true); + dom.appendChild(clone); + // We cannot use the document fragment itself because it + // is not part of the main dom tree. After the appendChild + // call we can access the new dom element by id + this.cardWrapper = dom.querySelector('#' + this.id); + let front = this.cardWrapper.querySelector('.front'); + this.frontLoader.load(front).then(loader => { + this.frontLoaded(loader).then((obj) => { + if (this.onLoaded) this.onLoaded(); + resolve(this); + }); + }); + }) + } + + frontLoaded(loader) { + return new Promise((resolve, reject) => { + let scatter = new DOMScatter( + this.cardWrapper, + this.domScatterContainer, + { + x: loader.x, + y: loader.y, + startScale: loader.scale, + scale: loader.scale, + maxScale: loader.maxScale, + minScale: loader.minScale, + width: loader.wantedWidth, + height: loader.wantedHeight, + rotation: loader.rotation, + translatable: this.translatable, + scalable: this.scalable, + rotatable: this.rotatable, + overdoScaling: this.overdoScaling + } + ); + + if (this.center) { + scatter.centerAt(this.center); + } + + if (this.closeOnMinScale) { + + const removeOnMinScale = function () { + if (scatter.scale <= scatter.minScale) { + this.flippable.close(); + + // 'Disable' overdoscaling to avoid weird jumps on close. + scatter.minScale /= scatter.overdoScaling; + scatter.overdoScaling = 1; + + //Remove callback + if (scatter.onTransform) { + let callbackIdx = scatter.onTransform.indexOf(removeOnMinScale); + scatter.onTransform.splice(callbackIdx, 1); + } + } + + }.bind(this); + + + + scatter.addTransformEventCallback(removeOnMinScale); + } + + let flippable = new DOMFlippable(this.cardWrapper, scatter, this); + let back = this.cardWrapper.querySelector('.back'); + + if (this.preloadBack) { + this.backLoader.load(back).then(loader => { + this.setupFlippable(flippable, loader); + }); + } + this.flippable = flippable; + resolve(this); + }) + } + + centerAt(p) { + this.center = p; + this.flippable.centerAt(p); + } + + zoom(scale) { + this.flippable.zoom(scale); + } + + setupFlippable(flippable, loader) { + flippable.wantedWidth = loader.wantedWidth; + flippable.wantedHeight = loader.wantedHeight; + flippable.wantedScale = loader.scale; + flippable.minScale = loader.minScale; + flippable.maxScale = loader.maxScale; + flippable.scaleButtons(); + } + + start({ targetCenter = null } = {}) { + console.log('DOMFlip.start', targetCenter); + if (this.preloadBack) this.flippable.start({ duration: this.flipDuration, targetCenter }); + else { + let back = this.cardWrapper.querySelector('.back'); + let flippable = this.flippable; + this.backLoader.load(back).then(loader => { + this.setupFlippable(flippable, loader); + flippable.start({ duration: this.flipDuration, targetCenter }); + }); + } + } + + fadeOutAndRemove() { + TweenLite.to(this.cardWrapper, this.fadeDuration, { + opacity: 0, + onComplete: () => { + this.cardWrapper.remove(); + } + }); + } + + closed() { + this.unload(); + } + + unload() { + if (!this.preloadBack) { + this.backLoader.unload(); + } + } + } + + class DOMFlippable { + constructor(element, scatter, flip) { + // Set log to console.log or a custom log function + // define data structures to store our touchpoints in + + this.element = element; + this.flip = flip; + this.card = element.querySelector('.flipCard'); + this.front = element.querySelector('.front'); + this.back = element.querySelector('.back'); + this.flipped = false; + this.scatter = scatter; + this.onFrontFlipped = flip.onFrontFlipped; + this.onBackFlipped = flip.onBackFlipped; + this.onClose = flip.onClose; + this.onRemoved = flip.onRemoved; + this.onUpdate = flip.onUpdate; + + this.flipDuration = flip.flipDuration; + this.fadeDuration = flip.fadeDuration; + scatter.addTransformEventCallback(this.scatterTransformed.bind(this)); + console.log('lib.DOMFlippable', 5000); + TweenLite.set(this.element, { perspective: 5000 }); + TweenLite.set(this.card, { transformStyle: 'preserve-3d' }); + TweenLite.set(this.back, { rotationY: -180 }); + TweenLite.set([this.back, this.front], { + backfaceVisibility: 'hidden', + perspective: 5000 + }); + TweenLite.set(this.front, { visibility: 'visible' }); + this.infoBtn = element.querySelector('.infoBtn'); + this.backBtn = element.querySelector('.backBtn'); + this.closeBtn = element.querySelector('.closeBtn'); + /* Buttons are not guaranteed to exist. */ + if (this.infoBtn) { + InteractionMapper$1.on('tap', this.infoBtn, event => this.flip.start()); + + this.enable(this.infoBtn); + } + if (this.backBtn) { + InteractionMapper$1.on('tap', this.backBtn, event => this.start()); + } + if (this.closeBtn) { + InteractionMapper$1.on('tap', this.closeBtn, event => this.close()); + this.enable(this.closeBtn); + } + this.scaleButtons(); + this.bringToFront(); + } + + close() { + this.disable(this.infoBtn); + this.disable(this.closeBtn); + if (this.onClose) { + this.onClose(this); + this.flip.closed(); + } else { + this.scatter.zoom(0.1, { + animate: this.fadeDuration, + onComplete: () => { + this.element.remove(); + this.flip.closed(); + if (this.onRemoved) { + this.onRemoved.call(this); + } + } + }); + } + } + + showFront() { + TweenLite.set(this.front, { visibility: 'visible' }); + } + + centerAt(p) { + this.scatter.centerAt(p); + } + + zoom(scale) { + this.scatter.zoom(scale); + } + + get buttonScale() { + let iscale = 1.0; + + if (this.scatter != null) { + let scale = this.scatter.scale || 1; + iscale = 1.0 / scale; + } + return iscale + } + + scaleButtons() { + //This also works for svgs. + // if (this.infoBtn) + // this.infoBtn.style.transform = "scale(" + this.buttonScale + ")" + + // if (this.backBtn) + // this.backBtn.style.transform = "scale(" + this.buttonScale + ")" + + // if (this.closeBtn) + // this.closeBtn.style.transform = "scale(" + this.buttonScale + ")" + + console.log(this.buttonScale); + //// This did not work with svgs! + TweenLite.set([this.infoBtn, this.backBtn, this.closeBtn], { + scale: this.buttonScale + }); + } + + bringToFront() { + this.scatter.bringToFront(); + TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ }); + } + + clickInfo() { + this.bringToFront(); + this.infoBtn.click(); + } + + scatterTransformed(event) { + this.scaleButtons(); + } + + targetRotation(alpha) { + let ortho = 90; + let rest = alpha % ortho; + let delta = 0.0; + if (rest > ortho / 2.0) { + delta = ortho - rest; + } else { + delta = -rest; + } + return delta + } + + infoValues(info) { + let startX = this.element._gsTransform.x; + let startY = this.element._gsTransform.y; + let startAngle = this.element._gsTransform.rotation; + let startScale = this.element._gsTransform.scaleX; + let w = this.element.style.width; + let h = this.element.style.height; + console.log(info, startX, startY, startAngle, startScale, w, h); + } + + show(element, duration = 0, alpha = 1) { + if (element) { + TweenLite.to(element, duration, { autoAlpha: alpha }); // visibility: 'visible', display: 'initial'}) + } + } + + hide(element, duration = 0, alpha = 0) { + if (element) { + TweenLite.to(element, duration, { autoAlpha: alpha }); // {visibility: 'hidden', display: 'none'}) + } + } + + + + enable(button) { + this.show(button, this.fadeDuration); + if (button) { + TweenLite.set(button, { pointerEvents: 'auto' }); + } + } + + disable(button) { + this.hide(button, this.fadeDuration); + if (button) { + TweenLite.set(button, { pointerEvents: 'none' }); + } + } + + start({ targetCenter = null } = {}) { + this.bringToFront(); + if (!this.flipped) { + this.startX = this.element._gsTransform.x; + this.startY = this.element._gsTransform.y; + this.startAngle = this.element._gsTransform.rotation; + this.startScale = this.element._gsTransform.scaleX; + this.startWidth = this.element.style.width; + this.startHeight = this.element.style.height; + this.scatterStartWidth = this.scatter.width; + this.scatterStartHeight = this.scatter.height; + this.show(this.back); + this.disable(this.infoBtn); + this.disable(this.closeBtn); + } else { + this.show(this.front, this.fadeDuration); + this.disable(this.backBtn); + } + let { scalable, translatable, rotatable } = this.scatter; + this.saved = { scalable, translatable, rotatable }; + this.scatter.scalable = false; + this.scatter.translatable = false; + this.scatter.rotatable = false; + this.scatter.killAnimation(); + + this.flipped = !this.flipped; + let targetY = this.flipped ? 180 : 0; + let targetZ = this.flipped + ? this.startAngle + this.targetRotation(this.startAngle) + : this.startAngle; + let targetScale = this.flipped ? this.wantedScale : this.startScale; + let w = this.flipped ? this.wantedWidth : this.startWidth; + let h = this.flipped ? this.wantedHeight : this.startHeight; + let dw = this.wantedWidth - this.scatter.width; + let dh = this.wantedHeight - this.scatter.height; + let tc = targetCenter; + let xx = tc != null ? tc.x - w / 2 : this.startX - dw / 2; + let yy = tc != null ? tc.y - h / 2 : this.startY - dh / 2; + let x = this.flipped ? xx : this.startX; + let y = this.flipped ? yy : this.startY; + + console.log("DOMFlippable.start", this.flipped, targetCenter, x, y, this.saved); + let onUpdate = this.onUpdate !== null ? () => this.onUpdate(this) : null; + console.log(this.flipDuration); + TweenLite.to(this.card, this.flipDuration, { + rotationY: targetY, + ease: Power1.easeOut, + transformOrigin: '50% 50%', + onUpdate, + onComplete: e => { + if (this.flipped) { + //this.hide(this.front) + this.enable(this.backBtn); + this.show(this.backBtn); + + if (this.onFrontFlipped) { + this.onFrontFlipped(this); + } + } else { + + if (this.onBackFlipped == null) { + this.enable(this.infoBtn, this.fadeDuration); + this.enable(this.closeBtn, this.fadeDuration); + } else { + this.onBackFlipped(this); + } + this.flip.unload(); + } + this.scatter.scale = targetScale; + this.scaleButtons(); + this.scatter.rotationDegrees = targetZ; + this.scatter.width = this.flipped ? w : this.scatterStartWidth; + this.scatter.height = this.flipped ? h : this.scatterStartHeight; + + let { scalable, translatable, rotatable } = this.saved; + this.scatter.scalable = scalable; + this.scatter.translatable = translatable; + this.scatter.rotatable = rotatable; + }, + force3D: true + }); + + // See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/ + TweenLite.to(this.element, this.flipDuration / 2, { + scale: targetScale, + ease: Power1.easeOut, + rotationZ: targetZ + '_short', + transformOrigin: '50% 50%', + width: w, + height: h, + x: x, + y: y, + onComplete: e => { + if (this.flipped) { + this.hide(this.front); + // this.hide(this.infoBtn) + } else { + this.hide(this.back); + // this.show(this.infoBtn) + } + } + }); + } + } + + class Index { + + constructor(template, pages, notfound='thumbnails/notfound.png') { + this.template = template; + this.pages = pages; + this.notfound = notfound; + } + + setup() { + for(let pair of this.pages) { + let [title, src] = pair; + let id = getId(); + pair.push(id); + let t = this.template; + let wrapper = t.content.querySelector('.wrapper'); + wrapper.id = id; + let clone = document.importNode(t.content, true); + container.appendChild(clone); + wrapper = container.querySelector('#'+id); + + let icon = wrapper.querySelector('.icon'); + + icon.onerror = (e) => { + if (this.notfound) + icon.src = this.notfound; + }; + let iconSrc = src.replace('.html', '.png'); + //console.log("iconSrc", iconSrc) + if (iconSrc.endsWith('index.png')) { + icon.src = iconSrc.replace('index.png', 'thumbnail.png'); + } else if (iconSrc.endsWith('test.png')) { + icon.src = iconSrc.replace('test.png', 'thumbnail.test.png'); + } else { + icon.src = 'thumbnails/' + iconSrc; + } + wrapper.href = src; + let titleDiv = wrapper.querySelector('.title'); + titleDiv.innerText = title; + } + } + + frames() { + if (this.pages.length == 0) + return + let [title, src, id] = this.pages.shift(); + let iframe = document.createElement('iframe'); + iframe.frameborder = 0; + let wrapper = document.getElementById(id); + let icon = wrapper.querySelector('.icon'); + + icon.parentNode.replaceChild(iframe, icon); + iframe.onload = (e) => { + this.frames(); + }; + iframe.src = src + window.location.search; + } + + load() { + this.setup(); + if (window.location.search.startsWith('?test')) + this.frames(); + } + + loadAndTest() { + this.setup(); + if (!Capabilities.isMobile) + this.frames(); + } + } + + /** Basic class for poppable elements that need to be closed as soon as one poppable is + * shown. + */ + class Poppable { + + /** Register the poppable element in a context. Closes previously registered ones. + * @param {*} context + */ + register(context) { + let registered = Poppable.get(context); + if (registered != null) { + registered.close(); + } + Poppable.set(context, this); + } + + /** + * Unregister object from context + * @param {*} context + */ + unregister(context) { + Poppable.delete(context); + } + + /** + * Returns the given poppable in a context + * @static + * @param {*} context + * @returns + * @memberof Poppable + */ + static get(context) { + return Poppable.registrations.get(context) + } + + /** Sets the poppable in the given context + * @static + * @param {*} context + * @param {*} poppable + * @returns + * @memberof Poppable + */ + static set(context, poppable) { + return Poppable.registrations.set(context, poppable) + } + + /** Test whether a poppable exists in the given context + * + * @param {*} context + */ + static has(context) { + return Poppable.registrations.has(context) + } + + /** + * Removes the poppable from the given context. + * + * @static + * @param {*} context + * @memberof Poppable + */ + static delete(context) { + Poppable.registrations.delete(context); + } + + /** All poppable must implement a close method. */ + close() { + console.error("Must be implemented"); + } + } + + Poppable.registrations = new Map(); + + /** A Popup that shows text labels, images, or html + */ + class Popup extends Poppable { + /** + * Creates an instance of Popup. + * @param {any} [{ + * parent = null, - The DOM parent element. + * content = null, - A dict object with type strings (text, img, html) as keys + * and corresponding values. + * context = window, - A context object for poppable elements + * fontSize = "1em", - Describes the font size as CSS value + * fontFamily = "Arial", - Describes the font family as CSS value + * padding = 16, - {number || string} padding - Describes the padding as CSS value + * notchSize = 10, - {number || string} notchSize - Describes the size of the notch (callout) as CSS value + * switchPos = false, + * minWidth = null, + * maxWidth = 800, + * backgroundColor = "#EEE", - The color of the background as CSS value + * normalColor = "#444", - normalColor - The color of textitems as CSS value + * notchPosition = "bottomLeft", + * zIndex = 0, + * keepWithin = null, - Ensure that popup is visible within the bounds of the given container + * autoClose = true, - Autoclose the Popup on tap + * closeIcon = null, + * resizeIcon = null, + * closeCommand = null, + * draggable = false + * noStyle = false - When true, prevents the popup from doing any aesthetic manipulations to the DOM leaving the styling completely to the style sheets. + * }={}] + * @memberof Popup + */ + constructor({ + parent = null, + content = null, + context = window, + fontSize = "1em", + fontFamily = "Arial", + padding = 16, + notchSize = 10, + switchPos = false, + minWidth = null, + maxWidth = 800, + backgroundColor = "#EEE", + normalColor = "#444", + notchPosition = "bottomCenter", + zIndex = 0, + keepWithin = null, + autoClose = true, + closeIcon = null, + resizeIcon = null, + closeCommand = null, + draggable = false, + posOffset = 0, + targetBoundingBox = null, + useEventPosWithBoundingBox = false, + interactive = false, + onResize = null, + onMove = null, + noStyle = false, + hideOnUp = true, + } = {}) { + super(); + this.context = context; + this.noStyle = noStyle; + this.hideOnUp = hideOnUp; + this.padding = padding; + this.notchPosition = notchPosition; + this.notchSize = notchSize; + this.switchPos = switchPos; + this.fontSize = fontSize; + this.fontFamily = fontFamily; + this.minWidth = minWidth; + this.maxWidth = maxWidth; + this.normalColor = normalColor; + this.backgroundColor = backgroundColor; + this.keepWithin = keepWithin; + this.autoClose = autoClose; + this.resizeIcon = resizeIcon; + this.closeIcon = closeIcon; + this.closeCommand = closeCommand; + this.zIndex = zIndex; + this.parent = parent || document.body; + this.draggable = draggable; + this.posOffset = posOffset; + this.targetBoundingBox = targetBoundingBox; + this.useEventPosWithBoundingBox = useEventPosWithBoundingBox; + this.currentPos = null; + this.insertedNode = null; + this.loaded = false; + this.interactive = interactive; + this.onload = null; + this.onResize = onResize; + this.onMove = onMove; + if (content) { + this.show(content); + } + } + + /** Setup popup with a dictionary of content types and contents. + * @param {Object} content - A dict object with type strings (text, img, html) as keys + * and corresponding values. + * @return {Popup} this + */ + setup(content) { + //console.log("Popup.setup", this.draggable) + this.content = content; + this.items = {}; + this.element = document.createElement("div"); + this.element.classList.add("popup"); + this.setAlpha(this.element, 0); + // this.element.style.opacity = 0 + Elements$1.addClass(this.element, "unselectable"); + this.notch = document.createElement("div"); + Elements$1.setStyle(this.notch, this.notchStyle()); + this.notch.className = "notch"; + this.setupDraggable(); + if (this.closeIcon) { + let img = document.createElement("img"); + img.setAttribute("draggable", false); + img.src = this.closeIcon; + img.style.position = "absolute"; + img.style.right = "0px"; + img.style.top = "0px"; + img.style.width = "16px"; + img.style.height = "16px"; + img.onclick = e => { + this.close(); + }; + this.element.appendChild(img); + } + if (this.resizeIcon) { + let img = document.createElement("img"); + img.style.position = "absolute"; + img.style.right = "0px"; + img.style.bottom = "0px"; + img.style.width = "16px"; + img.style.height = "16px"; + img.src = this.resizeIcon; + img.setAttribute("draggable", true); + img.ondragstart = e => { + this.currentPos = { x: e.clientX, y: e.clientY }; + return true + }; + img.ondrag = e => { + e.preventDefault(); + + let target = this.element.querySelector("iframe") || this.element; + let delta = { + x: e.clientX - this.currentPos.x, + y: e.clientY - this.currentPos.y + }; + + this.currentPos = { x: e.clientX, y: e.clientY }; + if (delta.x == 0 && delta.y == 0) + return + + let rect = target.getBoundingClientRect(); + let width = rect.width + delta.x; + let height = rect.height + delta.y; + target.style.width = width + "px"; + target.style.height = height + "px"; + + switch (this.notchPosition) { + case "bottomLeft": + case "bottomCenter": + let bottom = parseFloat(this.element.style.bottom); + this.element.style.bottom = bottom - delta.y + "px"; + break + default: + break + } + //console.log("onResize", this.onResize) + if (this.onResize) { + this.onResize({ target, delta, width, height }); + } + }; + img.ondragend = e => { }; + this.element.appendChild(img); + } + + + for (let key in content) { + switch (key) { + case "selector": + break + case "text": + let text = document.createElement("span"); + this.element.appendChild(text); + text.innerHTML = content[key]; + Elements$1.setStyle(text, { color: this.normalColor }); + Elements$1.addClass(text, "unselectable"); + Elements$1.addClass(text, "PopupContent"); + this.insertedNode = text; + this.loaded = true; + break + case "img": + alert("img to be implemented"); + break + case "iframe": + let iframe = document.createElement("iframe"); + iframe.setAttribute("frameBorder", 0); + iframe.src = content[key]; + iframe.onload = e => { + let body = iframe.contentWindow.document.body; + let observer = new MutationObserver(() => { + this.iframeChanged(iframe); + }); + observer.observe(iframe.contentWindow.document, { + attributes: true, + subtree: true, + childList: true, + characterData: true + }); + let w = Math.max(body.scrollWidth, body.offsetWidth); + let h = Math.max(body.scrollHeight, body.offsetHeight); + iframe.style.width = w + "px"; + iframe.style.height = h + "px"; + this.layoutAfterInsert(); + if (this.onload != null) { + this.onload(); + } + this.loaded = true; + }; + this.element.appendChild(iframe); + Elements$1.addClass(iframe, "PopupContent"); + this.insertIntoDOM(); + return + case "html": + this.loaded = false; + let div = document.createElement("div"); + Elements$1.addClass(div, "PopupContent"); + this.element.appendChild(div); + div.innerHTML = content.html; + //console.log("insert", content) + let selector = content.selector; + if (selector) { + this.insertedNode = div.querySelector(selector); + if (this.insertedNode == null) { + div.innerHTML = `

Popup content not found. Missing ${selector}

`; + this.insertedNode = div.firstElementChild; + } + } + else { + this.insertedNode = div.firstElementChild || div; + } + this.setAlpha(this.insertedNode, 0); + let images = this.element.querySelectorAll('img'); + let total = 0; + if (images.length > 0) { + let count = 0; + for (let image of images) { + if (!image.complete && !image.src.startsWith('data:')) { + total += 1; + console.log("image not complete", image.src); + image.onload = e => { + count += 1; + if (count == total) { + this.loaded = true; + if (this.onload != null) { + this.onload(); + } + } + }; + } + } + } + if (total == 0) { + this.loaded = true; + } + break + case "node": + this.loaded = true; + Elements$1.addClass(content.node, "PopupContent"); + this.element.appendChild(content.node); + this.insertedNode = content.node; + this.setAlpha(this.insertedNode, 0); + break + default: + alert("Unexpected content type: " + key); + break + } + } + this.insertIntoDOM(); + this.layoutAfterInsert(); + this.setupEventHandler(); + return this + } + + handleClose(e) { + let closing = this.closingEvent(e); + if (closing) { + this.close(); + } + else { + this.setupCloseHandler(); + } + } + + setupCloseHandler() { + let close = this.handleClose; + if (this.hideOnUp) { + if (window.PointerEvent) + this.parent.addEventListener("pointerup", close.bind(this), { capture: true, once: true }); + else if (window.TouchEvent) + this.parent.addEventListener("touchend", close.bind(this), { capture: true, once: true }); + else + this.parent.addEventListener("mouseup", close.bind(this), { capture: true, once: true }); + } else { + if (window.PointerEvent) + this.parent.addEventListener("pointerdown", close.bind(this), { capture: true, once: true }); + else if (window.TouchEvent) + this.parent.addEventListener("touchstart", close.bind(this), { capture: true, once: true }); + else + this.parent.addEventListener("mousedown", close.bind(this), { capture: true, once: true }); + } + } + + setupEventHandler() { + if (this.autoClose) { + this.setupCloseHandler(); + } + } + + closingEvent(e) { + if (this.interactive) { + let node = e.target.closest(".PopupContent"); + return node == null + } + return true + } + + iframeChanged(iframe) { + let body = iframe.contentWindow.document.body; + let w = Math.max(body.scrollWidth, body.offsetWidth); + let h = Math.max(body.scrollHeight, body.offsetHeight); + iframe.style.width = w + "px"; + iframe.style.height = h + "px"; + } + + setupDraggable() { + if (this.draggable) { + let target = this.element; + target.setAttribute("draggable", true); + target.ondragstart = e => { + this.currentPos = { x: e.clientX, y: e.clientY }; + var img = document.createElement('img'); + img.src = 'data:image/gifbase64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; + e.dataTransfer.setDragImage(img, 0, 0); + }; + target.ondrag = e => { + e.preventDefault(); + let delta = { + x: e.clientX - this.currentPos.x, + y: e.clientY - this.currentPos.y + }; + this.currentPos = { x: e.clientX, y: e.clientY }; + let left = parseFloat(target.style.left); + let top = parseFloat(target.style.top); + + target.style.left = left + delta.x + 'px'; + target.style.top = top + delta.y + 'px'; + + //console.log("Popup.ondrag", target, event.target) + if (this.onMove) { + this.onMove({ target, delta }); + } + + this.lastDrag = { left, top }; + }; + target.ondragend = e => { + target.style.left = this.lastDrag.left + 'px'; + target.style.top = this.lastDrag.top + 'px'; + this.currentPos = null; + }; + } + } + + moveDragged(target) { + + } + + insertIntoDOM(layout = true) { + this.setAlpha(this.insertedNode, 0); + this.element.appendChild(this.notch); + this.parent.appendChild(this.element); + } + + layoutAfterInsert() { + Elements$1.setStyle(this.element, this.defaultStyle()); + this.layout(); + //this.element.style.opacity = 1 + } + + /** Layout the menu items. Needed only in the subclass. + */ + layout() { } + + + remove() { + if (this.parent.contains(this.element)) + this.parent.removeChild(this.element); + this.unregister(this.context); + } + + /** Close and remove the Popup from the DOM tree. + */ + close() { + //console.log("Popup.close", this.closeCommand) + this.unregister(this.context); + if (this.closeCommand) { + this.closeCommand(this, () => this.remove()); + } + else { + this.remove(); + } + } + + /** + * Set the alpha value to show or hide the popup. Uses CSS transitions. + * (A former dependency on TweenLite has beeen removed.) + * + * @param {*} targets + * @param {*} value + * @memberof Popup + */ + setAlpha(targets, value) { + let objs = (targets instanceof Array) ? targets : [targets]; + for (let obj of objs) { + if (value) { + obj.style.transition = "opacity 0.2s ease-in"; + } + obj.style.opacity = value; + } + // if (value) { + // TweenLite.to(targets, 0.2, { autoAlpha: value }) + // } + // else { + // TweenLite.set(targets, { autoAlpha: 0 }) + // } + } + + /** + * Starts a fade in animation. + * + * @memberof Popup + */ + fadeIn() { + this.setAlpha([this.element, this.insertedNode], 1); + } + + /** Shows the Popup with the given commands at the specified point. + * @param {Object} content - A dict object with type strings (text, img, html) as keys + * and corresponding values. + * @param {Point} point - The position as x, y coordinates {px}. + * @return {Popup} this + */ + showAt(content, point) { + this.setup(content); + console.log("showAt", this.loaded); + if (this.loaded) { + this.placeAt(point); + this.fadeIn(); + } + else { + this.setAlpha([this.element, this.insertedNode], 0); + this.onload = () => { + this.layoutAfterInsert(); + this.placeAt(point); + this.fadeIn(); + }; + } + return this + } + + /** + * Place the origin, i.e. the upper left corner at the given position using CSS styles. + * + * @param {any} x + * @param {any} y + * @memberof Popup + */ + placeOrigin(x, y) { + Elements$1.setStyle(this.element, { left: x + "px", top: y + "px" }); + } + + /** + * Calculate the local coordinates within the keepWithin container. + * + * @param {any} x + * @param {any} y + * @returns + * @memberof Popup + */ + localPointWithin(x, y, width, height) { + let pt = { x, y }; + return pt + } + + withinDimensions() { + return { + width: this.keepWithin.offsetWidth, + height: this.keepWithin.offsetHeight + } + } + + localDimensions() { + return { + width: this.element.offsetWidth, + height: this.element.offsetHeight + } + } + + /** + * Returns the notch position depending on the local coordinates within the keepWithin container + * Divides the space vertically into top, center, bottom and horizontally into left, center, right + * + * @param {any} x + * @param {any} y + * @returns + * @memberof Popup + */ + notchPositionWithin(x, y) { + let horizontal = "Center"; + let vertical = "center"; + let { width, height } = this.withinDimensions(); + let local = this.localPointWithin(x, y, width, height); + if (local.y < height * 0.33) { + vertical = "top"; + } + if (local.y > height * 0.66) { + vertical = "bottom"; + } + if (local.x < width * 0.33) { + horizontal = "Left"; + } + if (local.x > width * 0.66) { + horizontal = "Right"; + } + let result = vertical + horizontal; + if (result == "centerCenter") + return this.notchPosition + return result + } + + placeAt(point) { + let x = point.x; + let y = point.y; + let notchPosition = this.notchPosition; + if (this.keepWithin != null) { + notchPosition = this.notchPositionWithin(x, y); + } + Elements$1.setStyle(this.notch, this.notchStyle(notchPosition)); + this.notch.className = "notch " + notchPosition; + let { width, height } = this.localDimensions(); + + //if targetBoundingBox is set, popup is placed next to the rectangle + if (this.targetBoundingBox) { + let bbTop = this.targetBoundingBox.y; + let bbBottom = this.targetBoundingBox.y + this.targetBoundingBox.height; + let bbLeft = this.targetBoundingBox.x; + let bbRight = this.targetBoundingBox.x + this.targetBoundingBox.width; + //console.log("place popup with bb set:", x, y, bbTop, bbBottom, bbLeft, bbRight) + switch (notchPosition) { + case "bottomLeft": + case "bottomRight": + case "bottomCenter": + y = bbTop; + if (!this.useEventPosWithBoundingBox) + x = (bbLeft + bbRight) / 2; + break + case "topLeft": + case "topRight": + case "topCenter": + y = bbBottom; + if (!this.useEventPosWithBoundingBox) + x = (bbLeft + bbRight) / 2; + break + case "centerRight": + x = bbLeft; + if (!this.useEventPosWithBoundingBox) + y = (bbTop + bbBottom) / 2; + break + case "centerLeft": + x = bbRight; + if (!this.useEventPosWithBoundingBox) + y = (bbTop + bbBottom) / 2; + break + default: + break + } + } + + //calculate position depending on several (optional) parameters + switch (notchPosition) { + case "bottomLeft": + x -= this.padding; + x -= this.notchSize; + y -= height; + y -= this.notchSize * 2; + y -= this.posOffset; + break + case "bottomRight": + x -= width; + x += this.padding; + x += this.notchSize; + y -= height; + y -= this.notchSize * 2; + y -= this.posOffset; + break + case "bottomCenter": + x -= width / 2; + y -= height; + y -= this.notchSize * 2; + y -= this.posOffset; + break + case "topLeft": + x -= this.padding; + x -= this.notchSize; + y += this.notchSize * 2; + y += this.posOffset; + break + case "topRight": + x -= width; + x += this.padding; + x += this.notchSize; + y += this.notchSize * 2; + y += this.posOffset; + break + case "topCenter": + x -= width / 2; + y += this.notchSize * 2; + y += this.posOffset; + break + case "centerRight": + x -= width + this.notchSize * 2; + x -= this.posOffset; + y -= height / 2; + break + case "centerLeft": + //console.log("height", height) + y -= height / 2; + x += this.notchSize * 2; + x += this.posOffset; + break + default: + break + } + this.placeOrigin(x, y); + } + + /** Shows the Popup with the given commands at the current position. + * @param {Object} content - A dict object with type strings (text, img, html) as keys + * and corresponding values. + * @return {Popup} this + */ + show(content) { + this.setup(content); + this.fadeIn(); + return this + } + + /** Configuration object. Return default styles as CSS values. + */ + defaultStyle() { + let padding = this.padding; + let style = { + maxWidth: this.maxWidth + "px", + zIndex: this.zIndex, + position: "absolute", + }; + if (this.minWidth) { + style.minWidth = this.minWidth + "px"; + } + if (!this.noStyle) { + Object.assign(style, { + borderRadius: Math.round(this.padding / 2) + "px", + backgroundColor: this.backgroundColor, + padding: this.padding + "px", + boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)", + fontFamily: this.fontFamily, + fontSize: this.fontSize, + stroke: "black", + fill: "white" + }); + } + + return style + } + + /** Configuration object. Return notch styles as CSS values. + */ + notchStyle(notchPosition = null) { + if (notchPosition == null) { + notchPosition = this.notchPosition; + } + let width = 0; + let height = 0; + let left = this.padding; + let size = this.localDimensions(); + if (notchPosition.endsWith("Right")) { + left = size.width - this.padding - this.notchSize * 2; + } + if (notchPosition.endsWith("Center")) { + left = size.width / 2 - this.notchSize; + } + left = Math.round(left) + 'px'; + + if (notchPosition.startsWith("bottom")) { + if (this.noStyle) { + return { + width, + height, + left, + bottom: -this.notchSize + "px", + position: "absolute", + borderStyle: "solid", + borderTopWidth: this.notchSize + "px", + borderRight: this.notchSize + "px solid transparent", + borderLeft: this.notchSize + "px solid transparent", + borderBottom: 0 + } + + } else { + return { + width, + height, + left, + boxShadow: "0 12px 15px rgba(0, 0, 0, 0.1)", + bottom: -this.notchSize + "px", + position: "absolute", + borderTop: this.notchSize + "px solid " + this.backgroundColor, + borderRight: this.notchSize + "px solid transparent", + borderLeft: this.notchSize + "px solid transparent", + borderBottom: 0 + } + } + } + if (notchPosition.startsWith("top")) { + if (this.noStyle) { + return { + width, + height, + left, + top: -this.notchSize + "px", + position: "absolute", + borderStyle: "solid", + borderBottomWidth: this.notchSize + "px", + borderRight: this.notchSize + "px solid transparent", + borderLeft: this.notchSize + "px solid transparent", + borderTop: 0 + } + } else { + return { + width, + height, + left, + top: -this.notchSize + "px", + position: "absolute", + borderBottom: this.notchSize + "px solid " + this.backgroundColor, + borderRight: this.notchSize + "px solid transparent", + borderLeft: this.notchSize + "px solid transparent", + borderTop: 0 + } + } + } + + if (this.noStyle) { + + if (notchPosition.endsWith("Left")) { + left = -this.notchSize * 2 + "px"; + } + + if (notchPosition.endsWith("Right")) { + left = size.width + "px"; + } + + + let top = size.height / 2 - this.notchSize; + top = Math.round(top) + 'px'; + + + return { + width, + height, + left, + top, + borderRightWidth: this.notchSize, + borderLeftWidth: this.notchSize, + position: "absolute", + borderTop: this.notchSize + "px solid transparent", + borderBottom: this.notchSize + "px solid transparent" + } + + } else { + let borderRight = this.notchSize + "px solid transparent"; + let borderLeft = this.notchSize + "px solid transparent"; + let top = size.height / 2 - this.notchSize; + if (notchPosition.endsWith("Left")) { + left = -this.notchSize * 2 + "px"; + borderRight = this.notchSize + "px solid " + this.backgroundColor; + this.element.style.boxShadow = "15px 10px 15px rgba(0, 0, 0, 0.3)"; + } + if (notchPosition.endsWith("Right")) { + left = size.width + "px"; + borderLeft = this.notchSize + "px solid " + this.backgroundColor; + this.element.style.boxShadow = "15px 5px 15px rgba(0, 0, 0, 0.3)"; + } + + top = Math.round(top) + 'px'; + + + return { + width, + height, + left, + top, + borderRight, + borderLeft, + // boxShadow, + position: "absolute", + borderTop: this.notchSize + "px solid transparent", + borderBottom: this.notchSize + "px solid transparent" + } + } + } + + /** Convenient static methods to show and reuse a Popup implemented + * as a class variable. + * @param {Object} content - A dict object with type strings (text, img, html) as keys + * and corresponding values. + * @param {Point} point - The position as x, y coordinates {px}. + * @param {boolean} autoClose - Autoclose the menu after selecting an item. + */ + static open( + content, + point, + { + parent = null, + context = window, + fontSize = "1em", + fontFamily = "Arial", + padding = 16, + notchSize = 10, + switchPos = false, + minWidth = null, + maxWidth = 800, + backgroundColor = "#EEE", + zIndex = 0, + normalColor = "#444", + closeIcon = null, + resizeIcon = null, + closeCommand = null, + autoClose = true, + keepWithin = null, + draggable = false, + posOffset = 0, + targetBoundingBox = null, + useEventPosWithBoundingBox = false, + interactive = false, + onResize = null, + onMove = null + } = {} + ) { + + + let notchPosition = (switchPos && point.y < 50) ? "topCenter" : "bottomCenter"; + let popup = new Popup({ + parent, + context, + fontFamily, + fontSize, + padding, + notchSize, + switchPos, + minWidth, + maxWidth, + backgroundColor, + normalColor, + notchPosition, + zIndex, + autoClose, + keepWithin, + closeCommand, + closeIcon, + resizeIcon, + draggable, + posOffset, + targetBoundingBox, + useEventPosWithBoundingBox, + interactive, + onResize, + onMove + }); + popup.register(context); + popup.showAt(content, point); + return popup + } + + /** Convenient static method to close the Popup implemented + * as a class variable. Calls the close command. + */ + static closePopup(context = window) { + let popup = Poppable.registrations.get(context); + if (popup != null) { + popup.close(); + } + } + + /** Convenient static methods to remove the Popup implemented + * as a class variable. Removes the popup without performing the close command. + */ + static remove(context = window) { + let popup = Poppable.registrations.get(context); + if (popup != null) { + popup.remove(); + } + } + + /** + * Convenient static method to compute the clicked rect of objects that have multiple clients rects. + * Needed to position popups correctly above objects with line breaks, e.g. spans + * + * @static + * @param {*} event + * @returns {*} DOMRect + * @memberof Popup + */ + static targetRect(event) { + let target = event.target; + let x = event.pageX; + let y = event.pageY; + for (let rect of target.getClientRects()) { + let withinX = x >= rect.left && x <= rect.left + rect.width; + let withinY = y >= rect.top && y <= rect.top + rect.height; + if (withinX && withinY) { + return rect + } + } + return null + } + + /** + * Convenient static method to compute the center of objects that have multiple clients rects. + * Needed to position popups correctly above objects with line breaks, e.g. spans + * + * @static + * @param {*} event + * @returns {*} Point + * @memberof Popup + */ + static targetCenter(event) { + let target = event.target; + let x = event.pageX; + let y = event.pageY; + let rect = Popup.targetRect(event); + if (rect != null) { + x = rect.left + rect.width / 2; + y = rect.top + rect.height / 2; + } + return { x, y } + } + } + + /** A Popup Menu that shows text labels in a vertical row. + */ + class PopupMenu$1 extends Popup { + /** + * The constructor. + * @constructor + * @param {DOM Element} parent - The DOM parent element. + * @param {Object} commands - A dict object with command label strings as keys + * and command functions as values. + * @param {string} fontSize - Describes the font size as CSS value + * @param {number || string} padding - Describes the padding as CSS value + * @param {number || string} notchSize - Describes the size of the notch (callout) as CSS value + * @param {string} highlightColor - The color of highlighted menu items as CSS value + * @param {string} backgroundColor - The color of the background as CSS value + * @param {string} normalColor - The color of normal menu items as CSS value + * @param {DOM Element} keepWithin - The container to stay within + * @param {boolean} autoClose - Autoclose the menu after selecting an item + */ + constructor({ parent = null, + commands = null, + fontSize = '1em', + fontFamily = 'Arial', + padding = 16, + zIndex = 1, + spacing = '0px', + switchPos = false, + notchSize = 10, + maxWidth = 800, + backgroundColor = '#EEE', + normalColor = '#444', + highlightColor = 'black', + notchPosition = 'bottomLeft', + keepWithin = null, + autoClose = true } = {}) { + super({ parent, fontSize, fontFamily, padding, notchSize, notchPosition, backgroundColor, keepWithin, normalColor, autoClose }); + this.commands = commands; + this.zIndex = zIndex; + this.switchPos = switchPos; + this.spacing = spacing; + this.highlightColor = highlightColor; + } + + /** Setup menu with a dictionary of command labels and command functions. + * @param {Object} commands - A dict object with command label strings as keys + * and command functions as values. + * @return {PopupMenu} this + */ + setup(commands) { + + this.commands = commands; + this.items = {}; + this.element = document.createElement('div'); + this.element.style.zIndex = this.zIndex; + Elements$1.addClass(this.element, 'unselectable'); + this.notch = document.createElement('div'); + Elements$1.setStyle(this.notch, this.notchStyle()); + for (let key in commands) { + let item = document.createElement('div'); + this.element.appendChild(item); + item.innerHTML = key; + item.style.paddingBottom = item.style.paddingTop = this.spacing; + Elements$1.setStyle(item, { color: this.normalColor, cursor: 'default' }); + Elements$1.addClass(item, 'unselectable'); + Elements$1.addClass(item, 'popupMenuItem'); + this.items[key] = item; + item.onclick = (event) => { this.perform(key); }; + item.ontap = (event) => { this.perform(key); }; + item.onmouseover = (event) => { this.over(event, key); }; + item.onmouseout = (event) => { this.out(event, key); }; + } + + this.element.appendChild(this.notch); + this.parent.appendChild(this.element); + this.insertedNode = this.element; + Elements$1.setStyle(this.element, this.defaultStyle()); + this.layout(); + return this + } + + /** Execute a menu command. + * @param {string} key - The selected key. + */ + perform(key) { + let func = this.commands[key]; + if (this.autoClose) { + this.close(); + } + setTimeout(() => { + func.call(); + }, 20); + } + + /** Update the menu item denoted by key. + * @param {string} key - The selected key. + * @param {boolean} highlight - Show the item highlighted. + */ + update(key, highlight = false) { + let text = this.items[key]; + text.style.color = (highlight) ? this.highlightColor : this.normalColor; + } + + /** Mouse over handöer. + * @param {Event} event - The mouse event. + * @param {boolean} key - The selected key. + */ + over(event, key) { + for (let k in this.items) { + this.update(k, k == key); + } + } + + /** Mouse out handöer. + * @param {Event} event - The mouse event. + * @param {boolean} key - The selected key. + */ + out(event, key) { + this.update(key); + } + + /** Shows the PopupMenu with the given commands at the specified point. + * @param {Object} commands - A dict object with command label strings as keys + * and command functions as values. + * @param {Point} point - The position as x, y coordinates {px}. + * @return {PopupMenu} this + */ + showAt(commands, point) { + this.show(commands); + this.placeAt(point); + return this + } + + /** Convenient static methods to show and reuse a PopupMenu implemented + * as a class variable. + * @param {Object} commands - A dict object with command label strings as keys + * and command functions as values. + * @param {Point} point - The position as x, y coordinates {px}. + * @param {string} fontSize - Describes the font size as CSS value + * @param {number || string} padding - Describes the padding as CSS value + * @param {number || string} notchSize - Describes the size of the notch (callout) as CSS value + * @param {string} highlightColor - The color of highlighted menu items as CSS value + * @param {string} backgroundColor - The color of the background as CSS value + * @param {string} normalColor - The color of normal menu items as CSS value + * @param {boolean} autoClose - Autoclose the menu after selecting an item + */ + static open(commands, point, { parent = null, + context = window, + fontSize = '1em', + fontFamily = 'Arial', + padding = 16, + zIndex = 1, + spacing = '0px', + switchPos = false, + notchSize = 10, + maxWidth = 800, + keepWithin = null, + backgroundColor = '#EEE', + normalColor = '#444', + autoClose = true } = {}) { + + let registered = Poppable.get(context); + if (registered) { + this.closePopup(); + return + } + console.log("open", point); + let notchPosition = (point.y < 50 && switchPos) ? 'topCenter' : 'bottomCenter'; + let popup = new PopupMenu$1({ + parent, fontSize, padding, zIndex, spacing, switchPos, notchSize, + notchPosition, + maxWidth, backgroundColor, normalColor, + notchPosition, keepWithin, autoClose + }); + popup.showAt(commands, point); + popup.register(context); + popup.closeEventListener = (e) => { + if (this.eventOutside(e)) + this.closePopup(context); + }; + if (autoClose) { + context.addEventListener('mousedown', popup.closeEventListener, true); + context.addEventListener('touchstart', popup.closeEventListener, true); + context.addEventListener('pointerdown', popup.closeEventListener, true); + } + } + + static eventOutside(e) { + return !Elements$1.hasClass(e.target, 'popupMenuItem') + } + + /** Convenient static methods to close the PopupMenu implemented + * as a class variable. + */ + static closePopup(context=window) { + let registered = Poppable.get(context); + if (registered) { + registered.close(); + context.removeEventListener('mousedown', registered.closeEventListener); + context.removeEventListener('touchstart', registered.closeEventListener); + context.removeEventListener('pointerdown', registered.closeEventListener); + } + } + } + + class FrameContainer { + + constructor(element) { + this.element = element; + this.delegate = new InteractionMapper(element, this, + { mouseWheelElement: window}); + } + + capture(event) { + return true + } + + findTarget(event, local, global) { + let found = document.elementFromPoint(global.x, global.y); + let iframe = found.querySelector('iframe'); + if (iframe) { + let p = Points.fromPageToNode(found, global); + let doc = iframe.contentWindow.document; + let target = doc.elementFromPoint(p.x, p.y); + if (target != null) { + console.log('iframe element', target); + return new FrameTarget(iframe, target) + } + } + return null + } + } + + class FrameTarget { + + constructor(frame, target, debug=false) { + this.frame = frame; + this.target = target; + this.debug = debug; + } + + capture(event) { + return true + } + + simulateMouseEvent(type, point) { + let p = Points.fromPageToNode(this.frame, point); + let event = new MouseEvent(type, { + view: this.frame.contentWindow, + bubbles: true, + cancelable: true, + clientX: p.x, + clientY: p.y}); + this.target.dispatchEvent(event); + } + + createTouchList(pointMap) { + let touches = []; + let doc = this.frame.contentWindow.document; + for(let key of pointMap.keys()) { + let point = pointMap.get(key); + let p = Points.fromPageToNode(this.frame, point); + let touchTarget = doc.elementFromPoint(p.x, p.y); + let touch = new Touch(undefined, touchTarget, key, + p.x, p.y, p.x, p.y); + touches.push(touch); + } + return new TouchList(...touches) + } + + simulateTouchEventChrome(type, point, pointMap) { + let doc = this.frame.contentWindow.document; + let p = Points.fromPageToNode(this.frame, point); + let touchTarget = doc.elementFromPoint(p.x, p.y); + const touchObj = new Touch({ + identifier: Date.now(), + target: touchTarget, + clientX: p.x, + clientY: p.y, + pageX: p.x, + pageY: p.y, + radiusX: 2.5, + radiusY: 2.5, + rotationAngle: 10, + force: 0.5, + }); + + const touchEvent = new TouchEvent(type, { + cancelable: true, + bubbles: true, + touches: [touchObj], + targetTouches: [touchObj], + changedTouches: [touchObj], + shiftKey: false, + }); + if (this.debug) console.log("simulateTouchEventChrome", touchEvent); + this.target.dispatchEvent(touchEvent); + } + + simulateTouchEventSafari(type, point, pointMap, touchEventKey='targetTouches') { + let p = Points.fromPageToNode(this.frame, point); + let data = { view: this.frame.contentWindow, + bubbles: true, + cancelable: true, + clientX: p.x, + clientY: p.y}; + data[touchEventKey] = this.createTouchList(pointMap); + let event = new TouchEvent(type, data); + if (this.debug) console.log("simulateTouchEventChrome", touchEvent); + this.target.dispatchEvent(event); + } + + simulateTouchEvent(type, point, pointMap, touchEventKey='targetTouches') { + if (Capabilities.isSafari) { + this.simulateTouchEventSafari(type, point, pointMap, touchEventKey); + } + else { + this.simulateTouchEventChrome(type, point, pointMap); + } + } + + isMouseLikeEvent(event) { + return event.type.startsWith('mouse') || event.type.startsWith('pointer') + } + + onStart(event, interaction) { + if (this.debug) console.log('onStart', this.frame.parentNode); + for(let [key, point] of interaction.current.entries()) { + if (this.isMouseLikeEvent(event)) { + this.simulateMouseEvent('mousedown', point); + } + else { + this.simulateTouchEvent('touchstart', point, + interaction.current); + return + } + } + } + + onMove(event, interaction) { + if (this.debug) console.log('onMove'); + for(let [key, point] of interaction.current.entries()) { + if (this.isMouseLikeEvent(event)) { + this.simulateMouseEvent('mousemove', point); + } + else { + this.simulateTouchEvent('touchmove', point, + interaction.current); + return + } + } + } + + onEnd(event, interaction) { + if (this.debug) console.log('onEnd'); + for(let [key, point] of interaction.current.entries()) { + if (this.isMouseLikeEvent(event)) { + this.simulateMouseEvent('mouseend', point); + } + else { + this.simulateTouchEvent('touchend', point, + interaction.ended, 'changedTouches'); + return + } + } + } + } + + class Inspect { + // Code inspection functions + + static allScriptSources() + { + let sources = []; + let scripts = document.getElementsByTagName('script'); + for (let i = 0; i < scripts.length; i++) { + console.dir(scripts[i]); + sources.push(scripts[i]); + } + return sources + } + } + + /* globals Power0 */ + /* eslint no-console: ["error", { allow: ["log", "info", "warn", "error"] }] */ + + /** + * Imports + */ + + /** + * A class that can be used to perform automated user interface tests. + * + * @example + * // Create the UITest object + * const test = new UITest({ + * timeScale: 2 + * }) + * + * // Add an action to the test case + * test.tap(button, {eventType: 'click'}) + * + * // Start the test case + * test.start() + * + * @class + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/uitest.html|DocTest} + */ + class UITest { + + /** + * Creates an instance of an UITest. + * + * In the background, the class UITest uses the Greensock TimelineMax class. The opts object is passed directly to the TimelineMax class, so it can use any key that uses the TimelineMax class. + * + * @constructor + * @param {object} [opts] - An options object to specify the behaviour of the test case. + * @param {number} [opts.timeScale=1] - The speed at which the test should run, see https://greensock.com/docs/TimelineMax/timeScale(). + * @param {string} [opts.eventType=auto] - The type of events which should be used. Possible values: pointer, touch, mouse, auto. If set to auto, the eventType is set depending on the support of the browser used. + * @param {boolean} [opts.debug=false] - If set to true, multiple informations will be print to the console. + * @param {number} [opts.defaultInterval] - The interval used when no action is specified for an action. + */ + constructor(opts = {}) { + + this.opts = Object.assign({}, { + timeScale: 1, + eventType: 'auto', + debug: false, + defaultInterval: null + }, opts); + + // timeline + //-------------------- + this._timeline = new TimelineMax(Object.assign({}, { + paused: true + }, this.opts)); + this._timeline.timeScale(this.opts.timeScale); + + // eventType + //-------------------- + if (this.opts.eventType === 'auto') { + if (window.PointerEvent) { + this.opts.eventType = 'pointer'; + } else if ('ontouchstart' in window) { + this.opts.eventType = 'touch'; + } else { + this.opts.eventType = 'mouse'; + } + } + + if (this.opts.debug) { + console.log(`Event type: ${this.opts.eventType}`); + } + + this._timelinePositions = [0]; + this._actions = 0; + + // setup + //----------------- + this.setup(); + } + + /** + * Generates the required structure. + * + * @private + * @return {UITest} A reference to the UITest for chaining. + */ + setup() { + return this + } + + /** + * Gets the Greensock TimelineMax object, used in the background of UITest. + * + * @member {TimelineMax} + */ + get timeline() { + return this._timeline + } + + /** + * Starts the test case and executes the corresponding statements in the specified order. + * + * @return {UITest} A reference to the UITest for chaining. + */ + start() { + this._timeline.play(); + return this + } + + /** + * Stops the test case and stops executing any further instructions. + * + * @return {UITest} A reference to the UITest for chaining. + */ + stop() { + this._timeline.pause(); + return this + } + + /** + * Clears all instructions of the test case. + * + * @return {UITest} A reference to the UITest for chaining. + */ + clear() { + this._timeline.clear(); + return this + } + + /** + * Restarts the test case. + * + * @return {UITest} A reference to the UITest for chaining. + */ + restart() { + this._timeline.restart(); + return this + } + + /** + * Executes a tap event (pointerdown, pointerup) on a specific element. + * + * @param {HTMLElement|string} element - The HTML element on which the event is to be executed, e.g. button, document, h2, canvas, etc. or an selector string. If a selector has been specified, it is evaluated immediately before the event is called using the querySelector method. + * @param {number[]|object|PIXI.DisplayObject} [position=The center of the element.] - The local position of the event in the context of the specified HTML element. If no position is specified, the center of the HTML element is used. The position can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. + * @param {number} [timelinePosition=One second after the last action.] - The position in seconds when the event should be triggered, see shttps://greensock.com/docs/TimelineMax/addCallback(). + * @param {object} [opts] - An options object to specify the behaviour of the action. + * @param {function} [opts.onStart] - A function that runs after the first event is fired. Will not be fired if only one event is running (for example, a click event). Receives the fired event object as the first parameter. The test case (UITest) is bound to this. + * @param {function} [opts.onComplete] - A function that runs after the second event is fired. Always fired, even if only one event is running (for example, a click event). Receives the fired event object as the first parameter. The test case (UITest) is bound to this. + * @param {string[]} [opts.eventTypes=['pointerdown', 'pointerup']] - The event types to use. If no types are specified, the event types specified in the UITest constructor are used (or auto if not specified). + * @param {string} [opts.eventType] - If you want the tap method to fire only one event (for example, a click event), you can specify the opts.eventType parameter. If eventType is not null, the parameter opts.eventTypes is ignored. + * @param {Window|Frame} [opts.context=window] - The context within which the optionally specified element selector should be executed. + * @param {boolean} [opts.bubbles=true] - The Event property bubbles indicates whether the event bubbles up through the DOM or not. + * @param {boolean} [opts.cancelable=true] - Events' cancelable property indicates if the event can be canceled, and therefore prevented as if the event never happened. If the event is not cancelable, then its cancelable property will be false and the event listener cannot stop the event from occurring. + */ + tap(element, position, timelinePosition, opts = {}) { + + // arguments + //-------------------- + [position, timelinePosition, opts] = this.reorderArguments(arguments); + this._timelinePositions.push(timelinePosition); + + // debug + //-------------------- + if (this.opts.debug) console.log('tap params', {element, position, timelinePosition, opts}); + + // opts + //-------------------- + opts = Object.assign({}, { + onStart: null, + onComplete: null, + eventTypes: this.resolveEvents(['down', 'up']), + eventType: null, + context: window, + bubbles: true, + cancelable: true + }, opts); + + if (opts.eventType) { + opts.eventTypes = opts.eventType; + } + opts.eventTypes = Array.isArray(opts.eventTypes) ? opts.eventTypes : [opts.eventTypes]; + + // timeline + //-------------------- + this._timeline.addCallback(position => { + + // element + //-------------------- + const elem = Util.extractElement(opts.context, element); + + // position + //-------------------- + if (position === null) { + const rect = elem.getBoundingClientRect(); + position = [rect.width / 2, rect.height / 2]; + } + + // coords + //-------------------- + const coords = Util.extractPosition(position); + if (this.opts.debug) console.log('local coords', coords); + + // eventTypes + //-------------------- + if (opts.eventTypes.length === 1) { + opts.eventTypes.unshift(null); + } + + // event opts + //-------------------- + const eventOpts = {bubbles: opts.bubbles, cancelable: opts.cancelable}; + + if (opts.eventTypes[0]) { + + // create and dispatch event + //-------------------- + const eventStart = Event.create(elem, coords, opts.eventTypes[0], eventOpts); + if (this.opts.debug) console.log('dispatch event', eventStart); + elem.dispatchEvent(eventStart); + + // onStart + //-------------------- + if (opts.onStart) { + opts.onStart.call(this, eventStart); + } + } + + // create and dispatch event + //-------------------- + const eventComplete = Event.create(elem, coords, opts.eventTypes[1], eventOpts); + if (this.opts.debug) console.log('dispatch event', eventComplete); + elem.dispatchEvent(eventComplete); + + // onComplete + //-------------------- + if (opts.onComplete) { + opts.onComplete.call(this, eventComplete); + } + + }, timelinePosition, [position]); + + this._actions++; + + return this + } + + /** + * Executes a pan event (pointerdown, pointermove, pointerup) on a specific element. + * + * @param {HTMLElement|string} element - The HTML element on which the event is to be executed, e.g. button, document, h2, canvas, etc. or an selector string. If a selector has been specified, it is evaluated immediately before the event is called using the querySelector method. + * @param {number[]|object|PIXI.DisplayObject} [position=The center of the element.] - The local position of the event in the context of the specified HTML element. If no position is specified, the center of the HTML element is used. The position can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. + * @param {number} [timelinePosition=One second after the last action.] - The position in seconds when the event should be triggered, see shttps://greensock.com/docs/TimelineMax/addCallback(). + * @param {object} [opts] - An options object to specify the behaviour of the action. + * @param {function} [opts.onStart] - A function that runs after the first event is fired. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. + * @param {function} [opts.onUpdate] - A function that runs after each execution of the second event. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. + * @param {function} [opts.onComplete] - A function that runs after the third event is fired. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. + * @param {number[]|object|PIXI.DisplayObject} [opts.to={x: 0, y: 0}] - The target of the pan process. The position can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. + * @param {number} [opts.duration=1] - The duration of the pan animation in seconds, see https://greensock.com/docs/TweenLite/duration(). + * @param {Ease} [opts.ease=Power0.easeNone] - The easing of the pan animation, see https://greensock.com/docs/Easing. + * @param {string[]} [opts.eventTypes=['pointerdown', 'pointermove', 'pointerup']] - The event types to use. If no types are specified, the event types specified in the UITest constructor are used (or auto if not specified). + * @param {Window|Frame} [opts.context=window] - The context within which the optionally specified element selector should be executed. + * @param {boolean} [opts.bubbles=true] - The Event property bubbles indicates whether the event bubbles up through the DOM or not. + * @param {boolean} [opts.cancelable=true] - Events' cancelable property indicates if the event can be canceled, and therefore prevented as if the event never happened. If the event is not cancelable, then its cancelable property will be false and the event listener cannot stop the event from occurring. + */ + pan(element, position, timelinePosition, opts = {}) { + + // arguments + //-------------------- + [position, timelinePosition, opts] = this.reorderArguments(arguments); + this._timelinePositions.push(timelinePosition); + + // debug + //-------------------- + if (this.opts.debug) console.log('tap params', {element, position, timelinePosition, opts}); + + // opts + //-------------------- + opts = Object.assign({}, { + onStart: null, + onUpdate: null, + onComplete: null, + to: {x: 0, y: 0}, + duration: 1, + ease: Power0.easeNone, + eventTypes: this.resolveEvents(['down', 'move', 'up']), + context: window, + bubbles: true, + cancelable: true + }, opts); + + // timeline + //-------------------- + this._timeline.addCallback(position => { + + // element + //-------------------- + const elem = Util.extractElement(opts.context, element); + + // coords + //-------------------- + const from = Util.extractPosition(position); + + // event opts + //-------------------- + const eventOpts = {bubbles: opts.bubbles, cancelable: opts.cancelable}; + + const gsOpts = { + ease: opts.ease, + onStart: () => { + + // create and dispatch event + //-------------------- + const event = Event.create(elem, from, opts.eventTypes[0], eventOpts); + if (this.opts.debug) console.log('dispatch event', event); + elem.dispatchEvent(event); + + // onStart + //-------------------- + if (opts.onStart) { + opts.onStart.call(this, event); + } + }, + onUpdate: () => { + + // create and dispatch event + //-------------------- + const event = Event.create(elem, from, opts.eventTypes[1], eventOpts); + if (this.opts.debug) console.log('dispatch event', event); + elem.dispatchEvent(event); + + // onUpdate + //-------------------- + if (opts.onUpdate) { + opts.onUpdate.call(this, event); + } + }, + onComplete: () => { + + // create and dispatch event + //-------------------- + const event = Event.create(elem, from, opts.eventTypes[2], eventOpts); + if (this.opts.debug) console.log('dispatch event', event); + elem.dispatchEvent(event); + + // onComplete + //-------------------- + if (opts.onComplete) { + opts.onComplete.call(this, event); + } + } + }; + + // to + //-------------------- + const object = Util.extractTo(opts); + Object.assign(gsOpts, object); + + // drag animation + //-------------------- + TweenMax.to(from, opts.duration, gsOpts); + + }, timelinePosition, [position]); + + this._actions++; + + return this + } + + /** + * Executes a pinch event (pointerdown, pointermove, pointerup) on a specific element with two "fingers" simultaneously. + * + * @param {HTMLElement|string} element - The HTML element on which the event is to be executed, e.g. button, document, h2, canvas, etc. or an selector string. If a selector has been specified, it is evaluated immediately before the event is called using the querySelector method. + * @param {number[]|object|PIXI.DisplayObject} [position=The center of the element.] - The local position of the event in the context of the specified HTML element. If no position is specified, the center of the HTML element is used. The position can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. + * @param {number} [timelinePosition=One second after the last action.] - The position in seconds when the event should be triggered, see shttps://greensock.com/docs/TimelineMax/addCallback(). + * @param {object} [opts] - An options object to specify the behaviour of the action. + * @param {function} [opts.onStart] - A function that runs after the first events are fired. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. + * @param {function} [opts.onUpdate] - A function that runs after each execution of the second events. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. + * @param {function} [opts.onComplete] - A function that runs after the third events are fired. Receives the fired event object as the first parameter. The test case (UITest) is bound to this. + * @param {boolean} [opts.doubleCallbacks=false] - The callbacks onStart, onUpdate and onComplete will be fired only for one finger. If set to true, the events will be fired for both fingers. + * @param {number} [opts.distance=100] - The distance in pixels, how far the two "fingers" should move apart. If to or bezier specified, distance is ignored. + * @param {number[][]|object[]|PIXI.DisplayObject[]} [opts.to] - The targets of the pinch process. The position must be an array with two entries. An entry can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. If bezier is specified, to is ignored. + * @param {number[][]|object[]|PIXI.DisplayObject[]} [opts.bezier] - The targets of the pinch process. The position must be an array with two entries. An entry may be an array of positions or a bezier object (https://greensock.com/docs/Plugins/BezierPlugin). A position in the array or the values array of the bezier object can be specified as an array of numbers, as an object with the two properties x and y, or as a PIXI.Display object. If bezier is specified, to is ignored. + * @param {number} [opts.duration=1] - The duration of the pan animation in seconds, see https://greensock.com/docs/TweenLite/duration(). + * @param {Ease} [opts.ease=Power0.easeNone] - The easing of the pan animation, see https://greensock.com/docs/Easing. + * @param {string[]} [opts.eventTypes=['pointerdown', 'pointermove', 'pointerup']] - The event types to use. If no types are specified, the event types specified in the UITest constructor are used (or auto if not specified). + * @param {Window|Frame} [opts.context=window] - The context within which the optionally specified element selector should be executed. + * @param {boolean} [opts.bubbles=true] - The Event property bubbles indicates whether the event bubbles up through the DOM or not. + * @param {boolean} [opts.cancelable=true] - Events' cancelable property indicates if the event can be canceled, and therefore prevented as if the event never happened. If the event is not cancelable, then its cancelable property will be false and the event listener cannot stop the event from occurring. + */ + pinch(element, position, timelinePosition, opts = {}) { + + // arguments + //-------------------- + [position, timelinePosition, opts] = this.reorderArguments(arguments); + this._timelinePositions.push(timelinePosition); + + // debug + //-------------------- + if (this.opts.debug) console.log('tap params', {element, position, timelinePosition, opts}); + + // opts + //-------------------- + opts = Object.assign({}, { + onStart: null, + onUpdate: null, + onComplete: null, + doubleCallbacks: false, + duration: 1, + distance: 100, + to: null, + bezier: null, + ease: Power0.easeNone, + eventTypes: this.resolveEvents(['down', 'move', 'up']), + context: window, + bubbles: true, + cancelable: true + }, opts); + + // timeline + //-------------------- + this._timeline.addCallback(position => { + + // element + //-------------------- + const elem = Util.extractElement(opts.context, element); + + // from + //-------------------- + let from1 = null; + let from2 = null; + + if (Array.isArray(position) && !Util.isNumber(position[0])) { + from1 = Util.extractPosition(position[0]); + from2 = Util.extractPosition(position[1]); + } else { + from1 = Util.extractPosition(position); + from2 = {x: from1.x, y: from1.y}; + } + + // to + //-------------------- + let gsOpts1 = {}; + let gsOpts2 = {}; + + if (opts.to || opts.bezier) { + [gsOpts1, gsOpts2] = Util.extractMultiTo(opts); + } else { + const distance = opts.distance != null ? opts.distance : 100; + gsOpts1.x = from1.x - distance / 2; + gsOpts1.y = from1.y; + gsOpts2.x = from2.x + distance / 2; + gsOpts2.y = from2.y; + } + + // pointers + //-------------------- + const pointers = new Map(); + pointers.set(0, {element: from1, gsOpts: gsOpts1}); + pointers.set(1, {element: from2, gsOpts: gsOpts2}); + + // loop + //-------------------- + pointers.forEach((value, key) => { + + // from + //-------------------- + const from = value.element; + + // event opts + //-------------------- + const eventOpts = {bubbles: opts.bubbles, cancelable: opts.cancelable, pointerId: key, isPrimary: key === 0}; + + const gsOpts = { + ease: opts.ease, + onStart: () => { + + // create and dispatch event + //-------------------- + const event = Event.create(elem, from, opts.eventTypes[0], eventOpts); + if (this.opts.debug) console.log('dispatch event', event); + elem.dispatchEvent(event); + + // onStart + //-------------------- + if (opts.onStart && (opts.doubleCallbacks || key === 0)) { + opts.onStart.call(this, event); + } + }, + onUpdate: () => { + + // create and dispatch event + //-------------------- + const event = Event.create(elem, from, opts.eventTypes[1], eventOpts); + if (this.opts.debug) console.log('dispatch event', event); + elem.dispatchEvent(event); + + // onUpdate + //-------------------- + if (opts.onUpdate && (opts.doubleCallbacks || key === 0)) { + opts.onUpdate.call(this, event); + } + }, + onComplete: () => { + + // create and dispatch event + //-------------------- + const event = Event.create(elem, from, opts.eventTypes[2], eventOpts); + if (this.opts.debug) console.log('dispatch event', event); + elem.dispatchEvent(event); + + // onComplete + //-------------------- + if (opts.onComplete && (opts.doubleCallbacks || key === 0)) { + opts.onComplete.call(this, event); + } + } + }; + + // to + //-------------------- + Object.assign(gsOpts, value.gsOpts); + + // drag animation + //-------------------- + TweenMax.to(from, opts.duration, gsOpts); + }); + + }, timelinePosition, [position]); + + this._actions++; + + return this + } + + // /** + // * Adds a tap event to the timeline. + // * + // * @return {UITest} A reference to the uitest for chaining. + // */ + // rotate() { + // return this + // } + + // /** + // * Adds a tap event to the timeline. + // * + // * @return {UITest} A reference to the uitest for chaining. + // */ + // swipe() { + // return this + // } + + // /** + // * Adds a tap event to the timeline. + // * + // * @return {UITest} A reference to the uitest for chaining. + // */ + // press() { + // return this + // } + + // /** + // * Adds a tap event to the timeline. + // * + // * @return {UITest} A reference to the uitest for chaining. + // */ + // event() { + // return this + // } + + /** + * Sorts the parameters so that the second, third, and fourth parameters can be optional (and possibly slip forward). + * + * @private + * @param {arguments} params - The arguments which were passed to the function. + * @returns {array} - Returns an array of the position, the timelinePosition and the opts object. + */ + reorderArguments(params) { + + // first parameter + //-------------------- + const element = params[0]; + + // other parameter + //-------------------- + let position = null; + let timelinePosition = null; + let opts = null; + + // second parameter + //-------------------- + if (Util.isNumber(params[1])) { + timelinePosition = params[1]; + } else if (Util.isObject(params[1]) && !Util.isPixiDisplayObject(params[1]) && (params[1].x == null || params[1].y == null)) { + opts = params[1]; + } else if (params[1] != null) { + position = params[1]; + } + + // third parameter + //-------------------- + if (Util.isNumber(params[2])) { + timelinePosition = params[2]; + } else if (Util.isObject(params[2])) { + opts = params[2]; + } + + // fourth parameter + //-------------------- + if (Util.isObject(params[3])) { + opts = params[3]; + } + + if (timelinePosition === null) { + if (this.opts.defaultInterval === null && this._actions > 1) { + throw new Error('No execution time was specified for this action, and a default interval was not set in the class constructor!') + } + timelinePosition = Math.max(...this._timelinePositions) + (this.opts.defaultInterval || 1); + } + + if (opts === null) { + opts = {}; + } + + return [position, timelinePosition, opts] + } + + /** + * Converts event type shortcuts to real event names. + * + * @private + * @param {string[]} events - An array of event types. + */ + resolveEvents(events) { + + const data = []; + + if (this.opts.eventType === 'pointer') { + events.forEach(it => { + if (it === 'down') { + data.push('pointerdown'); + } else if (it === 'move') { + data.push('pointermove'); + } else if (it === 'up') { + data.push('pointerup'); + } + }); + } else if (this.opts.eventType === 'touch') { + events.forEach(it => { + if (it === 'down') { + data.push('touchstart'); + } else if (it === 'move') { + data.push('touchmove'); + } else if (it === 'up') { + data.push('touchend'); + } + }); + } else { + events.forEach(it => { + if (it === 'down') { + data.push('mousedown'); + } else if (it === 'move') { + data.push('mousemove'); + } else if (it === 'up') { + data.push('mouseup'); + } + }); + } + + return data + } + } + + /** + * Helper class. + * + * @example + * // Checks if a thing is a number. + * const num = Util.isNumber(20) + * + * @private + * @ignore + * @class + */ + class Util { + + /** + * Resolves the element from a specific context. + * + * @static + * @param {Window|Frame} context - The context within which the optionally specified element selector should be executed. + * @return {HTMLElement|string} element - The HTML element on which the event is to be executed, e.g. button, document, h2, canvas, etc. or an selector string. If a selector has been specified, it is evaluated immediately before the event is called using the querySelector method. + */ + static extractElement(context, element) { + + const cont = Util.isFrame(context) ? context.contentDocument : context.document; + const elem = Util.isString(element) ? cont.querySelector(element) : element; + + return elem + } + + /** + * Extracts the position of the second parameter. + * + * @static + * @param {object} object - Something were the coords should be extracted. + * @return {object} - Returns an object with the keys x and y. + */ + static extractPosition(object) { + + // event coords + //-------------------- + const position = {x: 0, y: 0}; + + // get the position + //-------------------- + if (!object) { + position.x = 0; + position.y = 0; + } else if (typeof object.getBounds === 'function') { + const bounds = object.getBounds(); + position.x = bounds.x + bounds.width / 2; + position.y = bounds.y + bounds.height / 2; + } else if (Array.isArray(object)) { + position.x = object[0]; + position.y = object[1]; + } else if (object.x != null && object.y != null) { + position.x = object.x; + position.y = object.y; + } + + return position + } + + /** + * Extracts the to or bezier key. + * + * @static + * @param {object} opts - An options object where to or bezier should be extracted. + * @return {object} - Returns an object with the to or bezier keys. + */ + static extractTo(opts) { + + const object = {}; + + if (opts.bezier) { + + let bezier = null; + + if (Array.isArray(opts.bezier)) { + bezier = { + values: opts.bezier.map(it => Util.extractPosition(it)), + type: 'thru' + }; + } else { + opts.bezier.values = opts.bezier.values.map(it => Util.extractPosition(it)); + bezier = opts.bezier; + } + + object.bezier = bezier; + } else { + const to = Util.extractPosition(opts.to); + object.x = to.x; + object.y = to.y; + } + + return object + } + + /** + * Extracts multiple to or bezier keys. + * + * @static + * @param {object} opts - An options object where to or bezier should be extracted. + * @return {object[]} - Returns an array of objects with the keys x and y. + */ + static extractMultiTo(opts) { + + const objects = []; + + if (opts.bezier) { + + opts.bezier.forEach(it => { + + let bezier = null; + + if (Array.isArray(it)) { + bezier = { + values: it.map(it => Util.extractPosition(it)), + type: 'thru' + }; + } else { + it.values = it.values.map(it => Util.extractPosition(it)); + bezier = it; + } + + objects.push({ + bezier + }); + }); + + } else { + + opts.to.forEach(it => { + const to = Util.extractPosition(it); + objects.push({ + x: to.x, + y: to.y + }); + }); + } + + return objects + } + + /** + * Checks if a thing is a string. + * + * @static + * @param {object} object - The object to test for. + * @return {boolean} - true if the thing is a string, otherwise false. + */ + static isString(object) { + return typeof object === 'string' + } + + /** + * Checks if a thing is a number. + * + * @static + * @param {object} object - The object to test for. + * @return {boolean} - true if the thing is a number, otherwise false. + */ + static isNumber(object) { + return typeof object === 'number' + } + + /** + * Checks if a thing is an object. + * + * @static + * @param {object} object - The object to test for. + * @return {boolean} - true if the thing is an object, otherwise false. + */ + static isObject(object) { + return typeof object === 'object' && !Array.isArray(object) + } + + /** + * Checks if a thing is an PIXI.DisplayObject. + * + * @static + * @param {object} object - The object to test for. + * @return {boolean} - true if the thing is a PIXI.DisplayObject, otherwise false. + */ + static isPixiDisplayObject(object) { + return typeof object.getBounds === 'function' && typeof object.renderWebGL === 'function' && typeof object.setTransform === 'function' + } + + /** + * Checks if a thing is a frame. + * + * @static + * @param {object} object - The object to test for. + * @return {boolean} - true if the thing is a frame, otherwise false. + */ + static isFrame(object) { + return object.contentDocument != null + } + } + + /** + * Event helper class. + * + * @example + * // Creates an event object. + * const event = Event.create(h2, {x: 5, y: 10}, 'touchstart') + * + * @private + * @ignore + * @class + */ + class Event { + + /** + * Creates an event object. + * + * @static + * @param {HTMLElement} target - The element on which the event should be executed. + * @param {object} position - The local position of the event in relation to the target. The object must have the keys x and y. + * @param {string} type - The type of the event, see https://developer.mozilla.org/de/docs/Web/Events + * @param {object} opts - An options object. Every paramter of the event object can be overridden, see e.g. https://developer.mozilla.org/de/docs/Web/API/MouseEvent for all the properties. + */ + static create(target, position = {x: 0, y: 0}, type = 'pointerup', opts = {}) { + + const rect = typeof target.getBoundingClientRect === 'function' ? target.getBoundingClientRect() : {x: 0, y: 0}; + + // EventInit + const eventOpts = { + bubbles: true, + cancelable: true, + composed: false + }; + + // UIEventInit + const uiEventOpts = { + detail: 0, + view: window + }; + + // MouseEvent + const mouseEventOpts = { + screenX: window.screenX + target.offsetLeft + position.x, + screenY: window.screenY + target.offsetTop + position.y, + clientX: rect.x + position.x, + clientY: rect.y + position.y, + ctrlKey: false, + shiftKey: false, + altKey: false, + metaKey: false, + button: 0, + buttons: 1, + relatedTarget: null, + region: null + }; + + // TouchEvent + const touchEventOpts = { + touches: [], + targetTouches: [], + changedTouches: [], + ctrlKey: false, + shiftKey: false, + altKey: false, + metaKey: false + }; + + // PointerEvent + const pointerEventOpts = { + pointerId: 0, + width: 1, + height: 1, + pressure: 0, + tangentialPressure: 0, + tiltX: 0, + tiltY: 0, + twist: 0, + pointerType: 'touch', + isPrimary: true + }; + + if (type.startsWith('pointer')) { + return new PointerEvent(type, Object.assign({}, eventOpts, uiEventOpts, mouseEventOpts, pointerEventOpts, opts)) + } else if (type.startsWith('touch')) { + return new TouchEvent(type, Object.assign({}, eventOpts, uiEventOpts, touchEventOpts, opts)) + } else { + return new MouseEvent(type, Object.assign({}, eventOpts, uiEventOpts, mouseEventOpts, opts)) + } + } + } + + /* Needed to ensure that rollup.js includes class definitions and the classes + are visible inside doctests. + */ + + window.AbstractScatter = AbstractScatter; + window.Angle = Angle; + window.App = App; + window.BaseEvent = BaseEvent; + window.Capabilities = Capabilities; + window.CapabilitiesTests = CapabilitiesTests; + window.Colors = Colors; + window.Cycle = Cycle; + + window.DOMFlip = DOMFlip; + window.DOMFlippable = DOMFlippable; + window.CardLoader = CardLoader; + window.PDFLoader = PDFLoader; + window.HTMLLoader = HTMLLoader; + window.ImageLoader = ImageLoader; + window.FrameLoader = FrameLoader; + + window.DOMScatter = DOMScatter; + window.DOMScatterContainer = DOMScatterContainer; + window.Dates = Dates; + window.Doctest = Doctest; + window.Elements = Elements$1; + window.Errors = Errors; + window.EventRecorder = EventRecorder; + window.Events = Events; + window.FrameContainer = FrameContainer; + window.FrameTarget = FrameTarget; + window.IApp = IApp; + window.IInteractionMapperTarget = IInteractionMapperTarget; + window.IInteractionTarget = IInteractionTarget; + window.Index = Index; + window.Inspect = Inspect; + window.Interaction = Interaction; + window.InteractionDelegate = InteractionDelegate; + window.InteractionDelta = InteractionDelta; + window.InteractionMapper = InteractionMapper$1; + window.InteractionPoints = InteractionPoints; + window.Interface = Interface; + window.PointMap = PointMap; + window.Rect = Rect; + window.Points = Points; + window.Polygon = Polygon; + window.Poppable = Poppable; + window.Popup = Popup; + window.PopupMenu = PopupMenu$1; + window.ResizeEvent = ResizeEvent; + window.ScatterEvent = ScatterEvent; + window.Sets = Sets; + window.Strings = Strings; + window.UITest = UITest; + window.getId = getId$1; + window.isEmpty = isEmpty; + window.lerp = lerp; + window.debounce = debounce; + window.randomInt = randomInt; + window.randomFloat = randomFloat; + +}()); diff --git a/dist/iwmlib.pixi.js b/dist/iwmlib.pixi.js new file mode 100644 index 0000000..8049f7a --- /dev/null +++ b/dist/iwmlib.pixi.js @@ -0,0 +1,13971 @@ +(function () { + 'use strict'; + + /** + * Class that represents a PixiJS Theme. + * + * @example + * // Create the theme + * const yellow = new Theme({ + * fill: 0xfecd2d, + * fillActive: 0xfe9727, + * strokeActive: 0xfecd2d, + * strokeActiveWidth: 4, + * textStyle: { + * fill: 0x5ec7f8 + * }, + * textStyleActive: { + * fill: 0x5954d3 + * }, + * textStyleLarge: { + * fontSize: 36 + * } + * }) + * + * // Create the app and apply the new theme to it + * const app = new PIXIApp({ + * view: canvas, + * width: 450, + * height: 150, + * theme: yellow + * }).setup().run() + * + * @class + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/theme.html|DocTest} + */ + class Theme { + + /** + * Creates an instance of a Theme. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the theme. + * @param {number} [opts.margin=10] - The outer spacing (distance to other objects) from the border. + * @param {number} [opts.padding=10] - The inner spacing (distance from icon and/or label) to the border. + * @param {number} [opts.radius=4] - The radius used when drawing a rounded rectangle. + * @param {number} [opts.fast=0.25] - The duration of time when it has to be fast. + * @param {number} [opts.normal=0.5] - The duration of time when it has to be normal. + * @param {number} [opts.slow=1] - The duration of time when it has to be slow. + * @param {number} [opts.primaryColor=0x5ec7f8] - The primary color of the theme. + * @param {number} [opts.color1=0x282828] - The first color of the theme. For example used for the background. + * @param {number} [opts.color2=0xf6f6f6] - The second color of the theme. For example used for the border. + * @param {number} [opts.fill=color1] - The color of the background as a hex value. + * @param {number} [opts.fillAlpha=1] - The alpha value of the background. + * @param {number} [opts.fillActive=color1] - The color of the background when activated. + * @param {number} [opts.fillActiveAlpha=1] - The alpha value of the background when activated. + * @param {number} [opts.stroke=color2] - The color of the border as a hex value. + * @param {number} [opts.strokeWidth=0.6] - The width of the border in pixel. + * @param {number} [opts.strokeAlpha=1] - The alpha value of the border. + * @param {number} [opts.strokeActive=color2] - The color of the border when activated. + * @param {number} [opts.strokeActiveWidth=0.6] - The width of the border in pixel when activated. + * @param {number} [opts.strokeActiveAlpha=1] - The alpha value of the border when activated. + * @param {number} [opts.iconColor=color2] - The color of the icon (set by the tint property) as a hex value. + * @param {number} [opts.iconColorActive=colorPrimary] - The color of the icon when activated. + * @param {number} [opts.background=color1] - The color of a background for a component (e.g. at the Modal class). + * @param {object} [opts.textStyle={}] - A textstyle object for the styling of text. See PIXI.TextStyle + * for possible options. Default object: + * @param {string} [opts.textStyle.fontFamily="Avenir Next", "Open Sans", "Segoe UI", ...] - The font family. + * @param {string} [opts.textStyle.fontWeight=400] - The font weight. + * @param {number} [opts.textStyle.fontSize=16] - The font size. + * @param {number} [opts.textStyle.fill=color2] - The fill color. + * @param {number} [opts.textStyle.stroke=color1] - The stroke color. + * @param {number} [opts.textStyle.strokeThickness=0] - The thickness of the stroke. + * @param {number} [opts.textStyle.miterLimit=1] - The meter limit. + * @param {string} [opts.textStyle.lineJoin=round] - The line join. + * @param {object} [opts.textStyleActive=textStyle + {fill: primaryColor}] - A textstyle object which is used + * for actived text. + * @param {object} [opts.textStyleSmall=textStyle + {fontSize: -= 3}] - A textstyle object which is used for + * small text. + * @param {object} [opts.textStyleSmallActive=textStyleSmall + {fill: primaryColor}] - A textstyle object which + * is used for small actived text. + * @param {object} [opts.textStyleLarge=textStyle + {fontSize: += 3}] - A textstyle object which is used for + * large text. + * @param {object} [opts.textStyleLargeActive=textStyleLarge + {fill: primaryColor}] - A textstyle object which + * is used for large actived text. + */ + constructor(opts = {}) { + + const colorPrimary = opts.primaryColor != null ? opts.primaryColor : 0x5ec7f8; // blue + const color1 = opts.color1 != null ? opts.color1 : 0x282828; // black + const color2 = opts.color2 != null ? opts.color2 : 0xf6f6f6; // white + + this.opts = Object.assign({}, { + margin: 12, + padding: 12, + radius: 4, + fast: .25, + normal: .5, + slow: 1, + primaryColor: colorPrimary, + color1: color1, + color2: color2, + fill: color1, + fillAlpha: 1, + fillActive: color1, + fillActiveAlpha: 1, + stroke: color2, + strokeWidth: .6, + strokeAlpha: 1, + strokeActive: color2, + strokeActiveWidth: .6, + strokeActiveAlpha: 1, + iconColor: color2, + iconColorActive: colorPrimary, + background: color1 + }, opts); + + // Set textStyle and variants + this.opts.textStyle = Object.assign({}, { + fontFamily: '"Avenir Next", "Open Sans", "Segoe UI", "Roboto", "Helvetica Neue", -apple-system, system-ui, BlinkMacSystemFont, Arial, sans-serif !default', + fontWeight: '500', + fontSize: 18, + fill: color2, + stroke: color1, + strokeThickness: 0, + miterLimit: 1, + lineJoin: 'round' + }, this.opts.textStyle); + this.opts.textStyleSmall = Object.assign({}, this.opts.textStyle, {fontSize: this.opts.textStyle.fontSize - 3}, this.opts.textStyleSmall); + this.opts.textStyleLarge = Object.assign({}, this.opts.textStyle, {fontSize: this.opts.textStyle.fontSize + 3}, this.opts.textStyleLarge); + this.opts.textStyleActive = Object.assign({}, this.opts.textStyle, {fill: this.opts.primaryColor}, this.opts.textStyleActive); + this.opts.textStyleSmallActive = Object.assign({}, this.opts.textStyleSmall, {fill: this.opts.primaryColor}, this.opts.textStyleSmallActive); + this.opts.textStyleLargeActive = Object.assign({}, this.opts.textStyleLarge, {fill: this.opts.primaryColor}, this.opts.textStyleLargeActive); + + Object.assign(this, this.opts); + } + + /** + * Factory function + * + * @static + * @param {string} theme=dark - The name of the theme to load. + * @return {Theme} Returns a newly created Theme object. + */ + static fromString(theme) { + + if (theme && typeof theme === 'object') { + return theme + } + + switch (theme) { + case 'light': + return new ThemeLight() + case 'red': + return new ThemeRed() + default: + return new ThemeDark() + } + } + } + + /** + * Class that represents a PixiJS ThemeDark. + * + * @example + * // Create the app with a new dark theme + * const app = new PIXIApp({ + * view: canvas, + * width: 450, + * height: 150, + * theme: new ThemeDark() + * }).setup().run() + * + * @class + * @extends Theme + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/theme.html|DocTest} + */ + class ThemeDark extends Theme { + + } + + /** + * Class that represents a PixiJS ThemeLight. + * The color1 is set to 0xf6f6f6, color2 to 0x282828. + * + * @example + * // Create the app with a new light theme + * const app = new PIXIApp({ + * view: canvas, + * width: 450, + * height: 150, + * theme: new ThemeLight() + * }).setup().run() + * + * @class + * @extends Theme + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/theme.html|DocTest} + */ + class ThemeLight extends Theme { + + /** + * Creates an instance of a ThemeLight. + * + * @constructor + */ + constructor() { + + super({color1: 0xf6f6f6, color2: 0x282828}); + } + } + + /** + * Class that represents a PixiJS ThemeRed. + * The primaryColor is set to 0xd92f31. + * + * @example + * // Create the app with a new red theme + * const app = new PIXIApp({ + * view: canvas, + * width: 450, + * height: 150, + * theme: new ThemeRed() + * }).setup().run() + * + * @class + * @extends Theme + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/theme.html|DocTest} + */ + class ThemeRed extends Theme { + + /** + * Creates an instance of a ThemeRed. + * + * @constructor + */ + constructor() { + + super({primaryColor: 0xd92f31}); + } + } + + /** + * Class that represents a PixiJS Progress. + * + * @example + * // Create the progress + * const progress = new Progress({ + * app: app + * }) + * + * // Add the progress to a DisplayObject + * app.scene.addChild(progress) + * + * @class + * @extends PIXI.Container + * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/progress.html|DocTest} + */ + class Progress extends PIXI.Container { + + /** + * Creates an instance of a Progress. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the progress. + * @param {number} [opts.id=auto generated] - The id of the progress. + * @param {PIXIApp} [opts.app=window.app] - The app where the progress belongs to. + * @param {number} [opts.width] - The width of the progress bar. When not set, the width is the size of the app + * minus 2 * opts.margin. + * @param {number} [opts.height=2] - The height of the progress bar. + * @param {string|Theme} [opts.theme=dark] - The theme to use for this progress. Possible values are dark, light, red + * or a Theme object. + * @param {number} [opts.margin=100] - The outer spacing to the edges of the app. + * @param {number} [opts.padding=0] - The inner spacing (distance from icon and/or label) to the border. + * @param {number} [opts.fill=Theme.fill] - The color of the progress background as a hex value. + * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. + * @param {number} [opts.fillActive=Theme.primaryColor] - The color of the progress background when activated. + * @param {number} [opts.fillActiveAlpha=Theme.fillActiveAlpha] - The alpha value of the background when activated. + * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. + * @param {number} [opts.strokeWidth=0] - The width of the border in pixel. + * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. + * @param {number} [opts.strokeActive=Theme.strokeActive] - The color of the border when activated. + * @param {number} [opts.strokeActiveWidth=0] - The width of the border in pixel when activated. + * @param {number} [opts.strokeActiveAlpha=Theme.strokeActiveAlpha] - The alpha value of the border when activated. + * @param {boolean} [opts.background=false] - The alpha value of the border when activated. + * @param {number} [opts.backgroundFill=Theme.background] - A textstyle object for the styling of the label. See PIXI.TextStyle + * for possible options. + * @param {number} [opts.backgroundFillAlpha=1] - A textstyle object for the styling of the label when the + * progress is activated. See PIXI.TextStyle for possible options. + * @param {number} [opts.radius=Theme.radius] - The radius of the four corners of the progress (which is a rounded rectangle). + * @param {boolean} [opts.destroyOnComplete=true] - Should the progress bar destroy itself after reaching 100 %? + * @param {boolean} [opts.visible=true] - Is the progress initially visible (property visible)? + */ + constructor(opts = {}) { + + super(); + + const theme = Theme.fromString(opts.theme); + this.theme = theme; + + this.opts = Object.assign({}, { + id: PIXI.utils.uid(), + app: window.app, + width: null, + height: 2, + margin: 100, + padding: 0, + fill: theme.fill, + fillAlpha: theme.fillAlpha, + fillActive: theme.primaryColor, + fillActiveAlpha: theme.fillActiveAlpha, + stroke: theme.stroke, + strokeWidth: 0, + strokeAlpha: theme.strokeAlpha, + strokeActive: theme.strokeActive, + strokeActiveWidth: 0, + strokeActiveAlpha: theme.strokeActiveAlpha, + background: false, + backgroundFill: theme.background, + backgroundFillAlpha: 1, + radius: theme.radius, + destroyOnComplete: true, + visible: true + }, opts); + + this.id = this.opts.id; + + this.background = null; + this.bar = null; + this.barActive = null; + + this.alpha = 0; + + this.visible = this.opts.visible; + + this._progress = 0; + + // setup + //----------------- + this.setup(); + + // layout + //----------------- + this.layout(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {Progress} A reference to the progress for chaining. + */ + setup() { + + // interaction + //----------------- + this.on('added', e => { + this.show(); + }); + + // background + //----------------- + if (this.opts.background) { + const background = new PIXI.Graphics(); + this.background = background; + this.addChild(background); + } + + // bar + //----------------- + const bar = new PIXI.Graphics(); + this.bar = bar; + this.addChild(bar); + + const barActive = new PIXI.Graphics(); + this.barActive = barActive; + this.bar.addChild(barActive); + + return this + } + + /** + * Should be called to refresh the layout of the progress. Can be used after resizing. + * + * @return {Progress} A reference to the progress for chaining. + */ + layout() { + + const width = this.opts.app.size.width; + const height = this.opts.app.size.height; + + // background + //----------------- + if (this.opts.background) { + this.background.clear(); + this.background.beginFill(this.opts.backgroundFill, this.opts.backgroundFillAlpha); + this.background.drawRect(0, 0, width, height); + this.background.endFill(); + } + + this.draw(); + + return this + } + + /** + * Draws the canvas. + * + * @private + * @return {Progress} A reference to the progress for chaining. + */ + draw() { + + this.bar.clear(); + this.barActive.clear(); + + this.drawBar(); + this.drawBarActive(); + + return this + } + + /** + * Draws the bar. + * + * @private + * @return {Progress} A reference to the progress for chaining. + */ + drawBar() { + + const width = this.opts.app.size.width; + const height = this.opts.app.size.height; + + this.radius = this.opts.radius; + if ((this.radius * 2) > this.opts.height) { + this.radius = this.opts.height / 2; + } + + const wantedWidth = this.opts.width || (width - (2 * this.opts.margin)); + const wantedHeight = this.opts.height; + + this.bar.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); + this.bar.beginFill(this.opts.fill, this.opts.fillAlpha); + if (this.radius > 1) { + this.bar.drawRoundedRect(0, 0, wantedWidth, wantedHeight, this.radius); + } else { + this.bar.drawRect(0, 0, wantedWidth, wantedHeight); + } + this.bar.endFill(); + + this.bar.x = width / 2 - this.bar.width / 2; + this.bar.y = height / 2 - this.bar.height / 2; + + return this + } + + /** + * Draws the active bar. + * + * @private + * @return {Progress} A reference to the progress for chaining. + */ + drawBarActive() { + + const wantedWidth = this.bar.width - (2 * this.opts.padding); + const wantedHeight = this.bar.height - (2 * this.opts.padding); + + const barActiveWidth = wantedWidth * this._progress / 100; + + this.barActive.lineStyle(this.opts.strokeActiveWidth, this.opts.strokeActive, this.opts.strokeActiveAlpha); + this.barActive.beginFill(this.opts.fillActive, this.opts.fillActiveAlpha); + if (barActiveWidth > 0) { + if (this.radius > 1) { + this.barActive.drawRoundedRect(0, 0, barActiveWidth, wantedHeight, this.radius); + } else { + this.barActive.drawRect(0, 0, barActiveWidth, wantedHeight); + } + } + this.barActive.endFill(); + + this.barActive.x = this.opts.padding; + this.barActive.y = this.opts.padding; + + return this + } + + /** + * Shows the progress (sets his alpha values to 1). + * + * @return {Progress} A reference to the progress for chaining. + */ + show() { + TweenLite.to(this, this.theme.fast, {alpha: 1}); + + return this + } + + /** + * Hides the progress (sets his alpha values to 1). + * + * @return {Progress} A reference to the progress for chaining. + */ + hide() { + TweenLite.to(this, this.theme.fast, {alpha: 0, onComplete: () => this.visible = false}); + + return this + } + + /** + * Gets or sets the progress. Has to be a number between 0 and 100. + * + * @member {number} + */ + get progress() { + return this._progress + } + set progress(value) { + + value = Math.round(value); + + if (value < 0) { + value = 0; + } + + if (value > 100) { + value = 100; + } + + TweenLite.to(this, this.theme.normal, { + _progress: value, + onUpdate: () => this.draw(), + onComplete: () => { + if (value === 100 && this.opts.destroyOnComplete) { + TweenLite.to(this, this.theme.fast, { + alpha: 0, + onComplete: () => this.destroy({children: true}) + }); + } + } + }); + } + } + + /** + * Class that represents a PixiJS AbstractPopup. + * The class is used for various other Popup-like classes + * like Popup, Message, Tooltip... + * + * @class + * @abstract + * @extends PIXI.Graphics + * @see {@link http://pixijs.download/dev/docs/PIXI.Graphics.html|PIXI.Graphics} + */ + class AbstractPopup extends PIXI.Graphics { + + /** + * Creates an instance of an AbstractPopup (only for internal use). + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the popup. + * @param {number} [opts.id=auto generated] - The id of the popup. + * @param {number} [opts.x=0] - The x position of the popup. Can be also set after creation with popup.x = 0. + * @param {number} [opts.y=0] - The y position of the popup. Can be also set after creation with popup.y = 0. + * @param {string|Theme} [opts.theme=dark] - The theme to use for this popup. Possible values are dark, light, red + * or a Theme object. + * @param {string|number|PIXI.Text} [opts.header] - The heading inside the popup as a string, a number (will be + * converted to a text) or as a PIXI.Text object. + * @param {string|number|PIXI.DisplayObject} [opts.content] - A text, a number (will be converted to a text) or + * an PIXI.DisplayObject as the content of the popup. + * @param {number} [opts.minWidth=320] - The minimum width of the popup. + * @param {number} [opts.minHeight=130] - The minimum height of the popup. + * @param {number} [opts.padding=Theme.padding] - The inner spacing (distance from header and content) the the border. + * @param {number} [opts.fill=Theme.fill] - The color of the button background as a hex value. + * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. + * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. + * @param {number} [opts.strokeWidth=Theme.strokeWidth] - The width of the border in pixel. + * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. + * @param {object} [opts.headerStyle=Theme.textStyleLarge] - A textstyle object for the styling of the header. See PIXI.TextStyle + * for possible options. + * @param {object} [opts.textStyle=Theme.textStyleSmall] - A textstyle object for the styling of the text. See PIXI.TextStyle + * for possible options. + * @param {number} [opts.radius=Theme.radius] - The radius of the four corners of the popup (which is a rounded rectangle). + * @param {hiddenCallback} [opts.onHidden] - Executed when the popup gets hidden. + * @param {boolean} [opts.visible=true] - Is the popup initially visible (property visible)? + * @param {string} [opts.orientation] - When set to portrait, the popup cannot be displayed in landscape mode. When set + * to landscape, the popup cannot be displayed in portrait mode. + */ + constructor(opts = {}) { + + super(); + + const theme = Theme.fromString(opts.theme); + this.theme = theme; + + this.opts = Object.assign({}, { + id: PIXI.utils.uid(), + x: 0, + y: 0, + header: null, // null or null + content: null, // null or String or PIXI.DisplayObject + minWidth: 320, + minHeight: 130, + maxWidth: null, + padding: theme.padding, + fill: theme.fill, + fillAlpha: theme.fillAlpha, + stroke: theme.stroke, + strokeWidth: theme.strokeWidth, + strokeAlpha: theme.strokeAlpha, + headerStyle: theme.textStyleLarge, + textStyle: theme.textStyleSmall, + radius: theme.radius, + onHidden: null, + visible: true, + orientation: null + }, opts); + + this.id = this.opts.id; + + this.headerStyle = new PIXI.TextStyle(this.opts.headerStyle); + this.textStyle = new PIXI.TextStyle(this.opts.textStyle); + + if (this.opts.maxWidth) { + this.headerStyle.wordWrap = true; + this.headerStyle.wordWrapWidth = this.opts.maxWidth - (2 * this.opts.padding); + + this.textStyle.wordWrap = true; + this.textStyle.wordWrapWidth = this.opts.maxWidth - (2 * this.opts.padding); + } + + this.alpha = 0; + this.visible = this.opts.visible; + + this._header = null; + this._content = null; + + // position + this.x = this.opts.x; + this.y = this.opts.y; + + // padding + this.innerPadding = this.opts.padding * 1.5; + + // interaction + //----------------- + this.interactive = true; + this.on('added', e => { + this.show(); + }); + } + + /** + * Creates the framework and instantiates everything. + * + * @private + * @return {AbstractPopup} A reference to the popup for chaining. + */ + setup() { + + // position + //----------------- + this.sy = this.opts.padding; + + // header + //----------------- + if (this.opts.header != null) { + + let header = null; + + if (this.opts.header instanceof PIXI.Text) { + header = this.opts.header; + } else if (typeof this.opts.header === 'number') { + header = new PIXI.Text(this.opts.header.toString(), this.headerStyle); + } else { + header = new PIXI.Text(this.opts.header, this.headerStyle); + } + + header.x = this.opts.padding; + header.y = this.sy; + + this.addChild(header); + + this.sy += header.height; + + this._header = header; + } + + if (this.opts.header && this.opts.content) { + this.sy += this.innerPadding; + } + + // content + //----------------- + if (this.opts.content != null) { + + let content = null; + + if (typeof this.opts.content === 'string') { + content = new PIXI.Text(this.opts.content, this.textStyle); + } else if (typeof this.opts.content === 'number') { + content = new PIXI.Text(this.opts.content.toString(), this.textStyle); + } else { + content = this.opts.content; + } + + content.x = this.opts.padding; + content.y = this.sy; + + this.sy += content.height; + + this.addChild(content); + + this._content = content; + } + + return this + } + + /** + * Should be called to refresh the layout of the popup. Can be used after resizing. + * + * @return {AbstractPopup} A reference to the popup for chaining. + */ + layout() { + + // wanted width & wanted height + //----------------- + const padding = this.opts.padding; + const size = this.getInnerSize(); + const width = size.width + (2 * padding); + const height = size.height + (2 * padding); + + this.wantedWidth = Math.max(width, this.opts.minWidth); + this.wantedHeight = Math.max(height, this.opts.minHeight); + + if (this.opts.maxWidth) { + this.wantedWidth = Math.min(this.wantedWidth, this.opts.maxWidth); + } + + if (this.opts.radius * 2 > this.wantedWidth) { + this.wantedWidth = this.opts.radius * 2; + } + + if (this.opts.radius * 2 > this.wantedHeight) { + this.wantedHeight = this.opts.radius * 2; + } + + switch (this.opts.orientation) { + case 'portrait': + if (this.wantedWidth > this.wantedHeight) { + this.wantedHeight = this.wantedWidth; + } + break + case 'landscape': + if (this.wantedHeight > this.wantedWidth) { + this.wantedWidth = this.wantedHeight; + } + break + } + + this.draw(); + + return this + } + + /** + * Draws the canvas. + * + * @private + * @return {AbstractPopup} A reference to the popup for chaining. + */ + draw() { + + const square = Math.round(this.wantedWidth) === Math.round(this.wantedHeight); + const diameter = Math.round(this.opts.radius * 2); + + this.clear(); + this.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); + this.beginFill(this.opts.fill, this.opts.fillAlpha); + if (square && diameter === this.wantedWidth) { + this.drawCircle(this.wantedWidth / 2, this.wantedHeight / 2, this.opts.radius); + } else { + this.drawRoundedRect(0, 0, this.wantedWidth, this.wantedHeight, this.opts.radius); + } + this.endFill(); + + return this + } + + /** + * Calculates the size of the children of the AbstractPopup. + * Cannot use getBounds() because it is not updated when children + * are removed. + * + * @private + * @returns {object} An JavaScript object width the keys width and height. + */ + getInnerSize() { + + let width = 0; + let height = 0; + + if (this._header) { + width = this._header.width; + height = this._header.height; + } + + if (this._header && this._content) { + height += this.innerPadding; + } + + if (this._content) { + width = Math.max(width, this._content.width); + height += this._content.height; + } + + return {width, height} + } + + /** + * Shows the popup (sets his alpha values to 1). + * + * @param {callback} [cb] - Executed when show animation was completed. + * @return {AbstractPopup} A reference to the popup for chaining. + */ + show(cb) { + + TweenLite.to(this, this.theme.fast, { + alpha: 1, + onComplete: () => { + if (cb) { + cb.call(this); + } + } + }); + + return this + } + + /** + * Hides the popup (sets his alpha values to 0). + * + * @param {callback} [cb] - Executed when hide animation was completed. + * @return {AbstractPopup} A reference to the popup for chaining. + */ + hide(cb) { + + TweenLite.to(this, this.theme.fast, { + alpha: 0, + onComplete: () => { + this.visible = false; + if (cb) { + cb.call(this); + } + } + }); + + if (this.opts.onHidden) { + this.opts.onHidden.call(this, this); + } + + return this + } + + /** + * Sets or gets the header. The getter always returns a PIXI.Text object. The setter can receive + * a string, a number or a PIXI.Text object. + * + * @member {string|number|PIXI.Text} + */ + get header() { + return this._header + } + set header(value) { + if (this._header) { + this._header.destroy(); + } + this.opts.header = value; + this.setup().layout(); + } + + /** + * Sets or gets the content. The getter always returns an PIXI.DisplayObject. The setter can receive + * a string, a number or a PIXI.DisplayObject. + * + * @member {string|number|PIXI.DisplayObject} + */ + get content() { + return this._content + } + set content(value) { + if (this._content) { + this._content.destroy(); + } + this.opts.content = value; + this.setup().layout(); + } + } + + /** + * Class that represents a PixiJS Tooltip. + * + * @example + * // Create the app + * const app = new PIXIApp({ + * view: canvas, + * width: 900, + * height: 250 + * }).setup().run() + * + * // Add an DisplayObject to the app + * const circle = new PIXI.Graphics() + * circle.beginFill(0x5251a3) + * circle.drawCircle(50, 50, 40) + * app.scene.addChild(circle) + * + * const tooltip = new Tooltip({ + * object: circle, + * container: app.scene, + * content: 'Das Gesetz ist der Freund des Schwachen.' + * }) + * + * @class + * @extends AbstractPopup + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/tooltip.html|DocTest} + */ + class Tooltip extends AbstractPopup { + + /** + * Creates an instance of a Tooltip. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the tooltip. + * @param {number} [opts.minWidth=0] - The minimum width of the tooltip. + * @param {number} [opts.minHeight=0] - The minimum height of the tooltip. + * @param {number} [opts.padding=Theme.padding / 2] - The inner spacing of the tooltip. + * @param {PIXI.DisplayObject} opts.object - The object, where the tooltip should be displayed. + * @param {PIXI.DisplayObject} [opts.container=object] - The container where the tooltip should be attached to. + * @param {number} [opts.offsetLeft=8] - The horizontal shift of the tooltip. + * @param {number} [opts.offsetTop=-8] - The vertical shift of the tooltip. + * @param {number} [opts.delay=0] - A delay, after which the tooltip should be opened. + */ + constructor(opts = {}) { + + const theme = Theme.fromString(opts.theme); + + opts = Object.assign({}, { + minWidth: 0, + minHeight: 0, + padding: theme.padding / 2, + object: null, + container: null, + offsetLeft: 8, + offsetTop: -8, + delay: 0 + }, opts); + + opts.container = opts.container || opts.object; + + super(opts); + + // setup + //----------------- + this.setup(); + + // layout + //----------------- + this.layout(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {Tooltip} A reference to the tooltip for chaining. + */ + setup() { + + super.setup(); + + // bind events this + //----------------- + this.interactive = true; + + let mouseoverTooltip = false; + + this.on('mouseover', e => { + mouseoverTooltip = true; + }); + + this.on('mouseout', e => { + mouseoverTooltip = false; + if (!mouseoverObject) { + this.hide(() => { + this.opts.container.removeChild(this); + }); + } + }); + + // bind events object + //----------------- + const object = this.opts.object; + object.interactive = true; + + let mouseoverObject = false; + + object.on('mouseover', e => { + + this.timeout = window.setTimeout(() => { + mouseoverObject = true; + this.visible = true; + this.opts.container.addChild(this); + this.setPosition(e); + }, this.opts.delay * 1000); + }); + + object.on('mousemove', e => { + if (mouseoverObject) { + this.setPosition(e); + } + }); + + object.on('mouseout', e => { + mouseoverObject = false; + window.clearTimeout(this.timeout); + if (!mouseoverTooltip) { + this.hide(() => { + this.opts.container.removeChild(this); + }); + } + }); + + return this + } + + /** + * Calculates and sets the position of the tooltip. + * + * @private + * @return {Tooltip} A reference to the tooltip for chaining. + */ + setPosition(e) { + + const position = e.data.getLocalPosition(this.opts.container); + + this.x = position.x + this.opts.offsetLeft; + this.y = position.y + this.opts.offsetTop - this.height; + + return this + } + } + + /** + * Class that represents a PixiJS Badge. + * + * @example + * // Create the app + * const app = new PIXIApp({ + * view: canvas, + * width: 900, + * height: 250 + * }).setup().run() + * + * // Add an DisplayObject to the app + * const circle = new PIXI.Graphics() + * circle.beginFill(0x5251a3) + * circle.drawCircle(50, 50, 40) + * app.scene.addChild(circle) + * + * const badge1 = new Badge({ + * object: circle, + * container: app.scene, + * content: 'Das Gesetz ist der Freund des Schwachen.' + * }) + * + * @class + * @extends AbstractPopup + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/badge.html|DocTest} + */ + class Badge extends AbstractPopup { + + /** + * Creates an instance of a Badge. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the badge. + * @param {number} [opts.minWidth=0] - The minimum width of the badge. + * @param {number} [opts.minHeight=0] - The minimum height of the badge. + * @param {number} [opts.padding=Theme.padding / 2] - The inner spacing of the badge. + * @param {string|object} [opts.tooltip] - A string for the label of the tooltip or an object to configure the tooltip + * to display. + */ + constructor(opts = {}) { + + const theme = Theme.fromString(opts.theme); + + opts = Object.assign({}, { + minWidth: 0, + minHeight: 0, + padding: theme.padding / 2, + tooltip: null + }, opts); + + super(opts); + + this.tooltip = null; + + // setup + //----------------- + this.setup(); + + // layout + //----------------- + this.layout(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @override + * @return {Badge} A reference to the badge for chaining. + */ + setup() { + + super.setup(); + + // tooltip + //----------------- + if (this.opts.tooltip) { + if (typeof this.opts.tooltip === 'string') { + this.tooltip = new Tooltip({object: this, content: this.opts.tooltip}); + } else { + this.opts.tooltip = Object.assign({}, {object: this}, this.opts.tooltip); + this.tooltip = new Tooltip(this.opts.tooltip); + } + } + + return this + } + + /** + * Should be called to refresh the layout of the badge. Can be used after resizing. + * + * @override + * @return {Badge} A reference to the badge for chaining. + */ + layout() { + + super.layout(); + + this.content.x = this.width / 2 - this.content.width / 2 - this.opts.strokeWidth / 2; + this.content.y = this.height / 2 - this.content.height / 2 - this.opts.strokeWidth / 2; + + return this + } + } + + class Events$1 { + + static stop(event) { + event.preventDefault(); + event.stopPropagation(); + } + + static extractPoint(event) { + switch (event.constructor.name) { + case 'TouchEvent': + for (let i = 0; i < event.targetTouches.length; i++) { + let t = event.targetTouches[i]; + return { x: t.clientX, y: t.clientY } + } + break + default: + return { x: event.clientX, y: event.clientY } + } + } + + static isCaptured(event) { + if (event.__capturedBy) + return true + return false + } + + static capturedBy(event, obj) { + event.__capturedBy = obj; + } + + static isPointerDown(event) { + // According to + // https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events + // pointer events use the buttons feature to represent pressed buttons + return event.buttons + } + + static isMouseDown(event) { + // Attempts to clone the which attribute of events failed in WebKit. May + // be this is a bug or a security feature. Workaround: we introduce + // a mouseDownSubstitute attribute that can be assigned to cloned + // events after instantiation. + if (Reflect.has(event, 'mouseDownSubstitute')) + return event.mouseDownSubstitute + return event.buttons || event.which + } + + static isSimulatedEvent(event) { + return Reflect.has(event, 'mouseDownSubstitute') + } + + static isMouseRightClick(event) { + return event.buttons || event.which + } + + static extractTouches(targets) { + let touches = []; + for (let i = 0; i < targets.length; i++) { + let t = targets[i]; + touches.push({ + targetSelector: this.selector(t.target), + identifier: t.identifier, + screenX: t.screenX, + screenY: t.screenY, + clientX: t.clientX, + clientY: t.clientY, + pageX: t.pageX, + pageY: t.pageY + }); + } + return touches + } + + static createTouchList(targets) { + let touches = []; + for (let i = 0; i < targets.length; i++) { + let t = targets[i]; + let touchTarget = document.elementFromPoint(t.pageX, t.pageY); + let touch = new Touch(undefined, touchTarget, t.identifier, + t.pageX, t.pageY, t.screenX, t.screenY); + touches.push(touch); + } + return new TouchList(...touches) + } + + static extractEvent(timestamp, event) { + let targetSelector = this.selector(event.target); + let infos = { + type: event.type, + time: timestamp, + constructor: event.constructor, + data: { + targetSelector: targetSelector, + view: event.view, + mouseDownSubstitute: event.buttons || event.which, // which cannot be cloned directly + bubbles: event.bubbles, + cancelable: event.cancelable, + screenX: event.screenX, + screenY: event.screenY, + clientX: event.clientX, + clientY: event.clientY, + layerX: event.layerX, + layerY: event.layerY, + pageX: event.pageX, + pageY: event.pageY, + ctrlKey: event.ctrlKey, + altKey: event.altKey, + shiftKey: event.shiftKey, + metaKey: event.metaKey + } + }; + if (event.type.startsWith('touch')) { + // On Safari-WebKit the TouchEvent has layerX, layerY coordinates + let data = infos.data; + data.targetTouches = this.extractTouches(event.targetTouches); + data.changedTouches = this.extractTouches(event.changedTouches); + data.touches = this.extractTouches(event.touches); + } + if (event.type.startsWith('pointer')) { + let data = infos.data; + data.pointerId = event.pointerId; + data.pointerType = event.pointerType; + } + if (Events$1.debug) { + Events$1.extracted.push(this.toLine(event)); + } + return infos + } + + static cloneEvent(type, constructor, data) { + if (type.startsWith('touch')) { + // We need to find target from layerX, layerY + //var target = document.querySelector(data.targetSelector) + // elementFromPoint(data.layerX, data.layerY) + //data.target = target + data.targetTouches = this.createTouchList(data.targetTouches); + data.changedTouches = this.createTouchList(data.changedTouches); + data.touches = this.createTouchList(data.touches); + } + // We need to find target from pageX, pageY which are only + // available after construction. They seem to getter items. + + let clone = Reflect.construct(constructor, [type, data]); + clone.mouseDownSubstitute = data.mouseDownSubstitute; + return clone + } + + static simulateEvent(type, constructor, data) { + data.target = document.querySelector(data.targetSelector); + let clone = this.cloneEvent(type, constructor, data); + if (data.target != null) { + data.target.dispatchEvent(clone); + } + if (Events$1.debug) { + Events$1.simulated.push(this.toLine(clone)); + } + } + + static toLine(event) { + return `${event.type} #${event.target.id} ${event.clientX} ${event.clientY}` + let result = event.type; + let selector = this.selector(event.target); + result += ' selector: ' + selector; + if (event.target != document.querySelector(selector)) + console.log('Cannot resolve', selector); + let keys = ['layerX', 'layerY', 'pageX', 'pageY', 'clientX', 'clientY']; + for (let key of keys) { + try { + result += ' ' + key + ':' + event[key]; + } + catch (e) { + console.log('Invalid key: ' + key); + } + } + return result + } + + static compareExtractedWithSimulated() { + if (this.extracted.length != this.simulated.length) { + alert('Unequal length of extracted [' + this.extracted.length + + '] and simulated events [' + this.simulated.length + '].'); + } + else { + for (let i = 0; i < this.extracted.length; i++) { + var extracted = this.extracted[i]; + var simulated = this.simulated[i]; + if (extracted != simulated) { + console.log('Events differ:' + extracted + '|' + simulated); + } + } + } + } + + static selector(context) { + return OptimalSelect.select(context) + } + + static reset() { + this.extracted = []; + this.simulated = []; + } + + static resetSimulated() { + this.simulated = []; + } + + static showExtractedEvents(event) { + if (!event.shiftKey) { + return + } + if (this.popup == null) { + let element = document.createElement('div'); + Elements.setStyle(element, { + position: 'absolute', + width: '480px', + height: '640px', + overflow: 'auto', + backgroundColor: 'lightgray' + }); + document.body.appendChild(element); + this.popup = element; + } + this.popup.innerHTML = ''; + for (let line of this.extracted) { + let div = document.createElement('div'); + div.innerHTML = line; + this.popup.appendChild(div); + } + let div = document.createElement('div'); + div.innerHTML = '------------ Simulated -----------'; + this.popup.appendChild(div); + for (let line of this.simulated) { + let div = document.createElement('div'); + div.innerHTML = line; + this.popup.appendChild(div); + } + Elements.setStyle(this.popup, + { left: event.clientX + 'px', top: event.clientY + 'px' }); + } + } + + Events$1.popup = null; + Events$1.debug = true; + Events$1.extracted = []; + Events$1.simulated = []; + Events$1.simulationRunning = false; + + /** + * Callback for the button action. + * + * @callback actionCallback + * @param {object} event - The event object. + * @param {Button} button - A reference to the button (also this refers to the button). + */ + + /** + * Callback for the button beforeAction. + * + * @callback beforeActionCallback + * @param {object} event - The event object. + * @param {Button} button - A reference to the button (also this refers to the button). + */ + + /** + * Callback for the button afterAction. + * + * @callback afterActionCallback + * @param {object} event - The event object. + * @param {Button} button - A reference to the button (also this refers to the button). + */ + + /** + * Class that represents a PixiJS Button. + * + * @example + * // Create the button + * const button = new Button({ + * label: 'My Button', + * action: () => console.log('Button was clicked') + * }) + * + * // Add the button to a DisplayObject + * app.scene.addChild(button) + * + * @class + * @extends PIXI.Container + * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/button.html|DocTest} + */ + class Button extends PIXI.Container { + + /** + * Creates an instance of a Button. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the button. + * @param {number} [opts.id=auto generated] - The id of the button. + * @param {string} [opts.label] - The label of the button. + * @param {number} [opts.x=0] - The x position of the button. Can be also set after creation with button.x = 0. + * @param {number} [opts.y=0] - The y position of the button. Can be also set after creation with button.y = 0. + * @param {string|Theme} [opts.theme=dark] - The theme to use for this button. Possible values are dark, light, red + * or a Theme object. + * @param {number} [opts.minWidth=44] - The minimum width of the button. + * @param {number} [opts.minHeight=44] - The minimum height of the button. + * @param {number} [opts.padding=Theme.padding] - The inner spacing (distance from icon and/or label) to the border. + * @param {string|PIXI.DisplayObject} [opts.icon] - The icon of the button. Can be a predefined one, an URL or an PIXI.DisplayObject. + * @param {string|PIXI.DisplayObject} [opts.iconActive=icon] - The icon of the button when activated. Can be a predefined one, an URL or an PIXI.DisplayObject. + * @param {string} [opts.iconPosition=left] - The position of the icon in relation to the label. Can be left or right. + * @param {number} [opts.iconColor=Theme.iconColor] - The color of the icon (set by the tint property) as a hex value. + * @param {number} [opts.iconColorActive=Theme.iconColorActive] - The color of the icon when activated. + * @param {number} [opts.fill=Theme.fill] - The color of the button background as a hex value. + * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. + * @param {number} [opts.fillActive=Theme.fillActive] - The color of the button background when activated. + * @param {number} [opts.fillActiveAlpha=Theme.fillActiveAlpha] - The alpha value of the background when activated. + * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. + * @param {number} [opts.strokeWidth=Theme.strokeWidth] - The width of the border in pixel. + * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. + * @param {number} [opts.strokeActive=Theme.strokeActive] - The color of the border when activated. + * @param {number} [opts.strokeActiveWidth=Theme.strokeActiveWidth] - The width of the border in pixel when activated. + * @param {number} [opts.strokeActiveAlpha=Theme.strokeActiveAlpha] - The alpha value of the border when activated. + * @param {object} [opts.textStyle=Theme.textStyle] - A textstyle object for the styling of the label. See PIXI.TextStyle + * for possible options. + * @param {number} [opts.textStyleActive=Theme.textStyleActive] - A textstyle object for the styling of the label when the + * button is activated. See PIXI.TextStyle for possible options. + * @param {string} [opts.style=default] - A shortcut for styling options. Possible values are default, link. + * @param {number} [opts.radius=Theme.radius] - The radius of the four corners of the button (which is a rounded rectangle). + * @param {boolean} [opts.disabled=false] - Is the button disabled? When disabled, the button has a lower alpha value + * and cannot be clicked (interactive is set to false). + * @param {boolean} [opts.active=false] - Is the button initially active? + * @param {actionCallback} [opts.action] - Executed when the button was triggered (by pointerup). + * @param {beforeActionCallback} [opts.beforeAction] - Executed before the main action is triggered. + * @param {afterActionCallback} [opts.afterAction] - Executed after the main action was triggered. + * @param {string} [opts.type=default] - The type of the button. Can be default or checkbox. When the type is + * checkbox, the active state is toggled automatically. + * @param {string} [opts.align=center] - The horizontal position of the label and the icon. Possible values are + * left, center and right. Only affects the style when the minWidth is bigger than the width of the icon and label. + * @param {string} [opts.verticalAlign=middle] - The vertical position of the label and the icon. Possible values are + * top, middle and button. Only affects the style when the minHeight is bigger than the height of the icon and label. + * @param {string|object} [opts.tooltip] - A string for the label of the tooltip or an object to configure the tooltip + * to display. + * @param {string|object} [opts.badge] - A string for the label of the badge or an object to configure the badge to display. + * If the parameter is an object, all badge options can be set plus the following: + * @param {string} [opts.badge.align=right] - The horizontal alignment of the badge. Possible values: left, center, right + * @param {string} [opts.badge.verticalAlign=top] - The vertical alignment of the badge. Possible values: top, middle, bottom + * @param {number} [opts.badge.offsetLeft=0] - The horizontal shift of the badge. + * @param {number} [opts.badge.offsetTop=0] - The vertical shift of the badge. + * @param {boolean} [opts.visible=true] - Is the button initially visible (property visible)? + */ + constructor(opts = {}) { + + super(); + + const theme = Theme.fromString(opts.theme); + this.theme = theme; + + this.opts = Object.assign({}, { + id: PIXI.utils.uid(), + label: null, + x: 0, + y: 0, + minWidth: 44, + minHeight: 44, + padding: theme.padding, + icon: undefined, + iconActive: undefined, + iconPosition: 'left', + iconColor: theme.iconColor, + iconColorActive: theme.iconColorActive, + fill: theme.fill, + fillAlpha: theme.fillAlpha, + fillActive: theme.fillActive, + fillActiveAlpha: theme.fillActiveAlpha, + stroke: theme.stroke, + strokeWidth: theme.strokeWidth, + strokeAlpha: theme.strokeAlpha, + strokeActive: theme.strokeActive, + strokeActiveWidth: theme.strokeActiveWidth, + strokeActiveAlpha: theme.strokeActiveAlpha, + textStyle: theme.textStyle, + textStyleActive: theme.textStyleActive, + style: 'default', + radius: theme.radius, + disabled: false, + active: false, + action: null, + beforeAction: null, + afterAction: null, + type: 'default', + align: 'center', + verticalAlign: 'middle', + tooltip: null, + badge: null, + visible: true + }, opts); + + this.id = this.opts.id; + + if (typeof this.opts.icon === 'undefined' && typeof this.opts.iconActive !== 'undefined') { + this.opts.icon = this.opts.iconActive; + } else if (typeof this.opts.icon !== 'undefined' && typeof this.opts.iconActive === 'undefined') { + this.opts.iconActive = this.opts.icon; + } + + if (this.opts.style === 'link') { + Object.assign(this.opts, {strokeAlpha: 0, strokeActiveAlpha: 0, fillAlpha: 0, fillActiveAlpha: 0}); + } + + this._active = null; + this._disabled = null; + + this.iconInactive = null; + this.iconActive = null; + this.text = null; + + this.button = null; + this.content = null; + + this.tooltip = null; + this.badge = null; + + this.visible = this.opts.visible; + + // setup + //----------------- + this.setup(); + } + + /** + * Captures an event to inform InteractionMapper about processed events. + * + * @param {event|PIXI.InteractionEvent} event - The PIXI event to capture. + */ + capture(event) { + Events$1.capturedBy(event.data.originalEvent, this); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {Button} A reference to the button for chaining. + */ + setup() { + + // Button + //----------------- + let button = new PIXI.Graphics(); + this.button = button; + this.addChild(button); + + // Content + //----------------- + let content = new PIXI.Container(); + this.content = content; + this.addChild(content); + + // Text + //----------------- + if (this.opts.label) { + this.text = new PIXI.Text(this.opts.label, this.opts.textStyle); + } + + // Icon + //----------------- + if (this.opts.icon) { + this.iconInactive = this.loadIcon(this.opts.icon, this.opts.iconColor); + } + + if (this.opts.iconActive) { + this.iconActive = this.loadIcon(this.opts.iconActive, this.opts.iconColorActive); + } + + // interaction + //----------------- + this.button.on('pointerover', e => { + this.capture(e); + TweenLite.to([this.button, this.content], this.theme.fast, {alpha: .83, overwrite: 'none'}); + }); + + this.button.on('pointermove', e => { + this.capture(e); + }); + + this.button.on('pointerout', e => { + this.capture(e); + TweenLite.to([this.button, this.content], this.theme.fast, {alpha: 1, overwrite: 'none'}); + }); + + this.button.on('pointerdown', e => { + //this.capture(e) + TweenLite.to([this.button, this.content], this.theme.fast, {alpha: .7, overwrite: 'none'}); + }); + + this.button.on('pointerup', e => { + this.capture(e); + if (this.opts.beforeAction) { + this.opts.beforeAction.call(this, e, this); + } + + if (this.opts.action) { + this.opts.action.call(this, e, this); + } + + TweenLite.to([this.button, this.content], this.theme.fast, {alpha: .83, overwrite: 'none'}); + + if (this.opts.type === 'checkbox') { + this.active = !this.active; + } + + if (this.opts.afterAction) { + this.opts.afterAction.call(this, e, this); + } + }); + + // disabled + //----------------- + this.disabled = this.opts.disabled; + + // active + //----------------- + this.active = this.opts.active; // calls .layout() + + // tooltip + //----------------- + if (this.opts.tooltip) { + if (typeof this.opts.tooltip === 'string') { + this.tooltip = new Tooltip({object: this, content: this.opts.tooltip}); + } else { + this.opts.tooltip = Object.assign({}, {object: this}, this.opts.tooltip); + this.tooltip = new Tooltip(this.opts.tooltip); + } + } + + // badge + //----------------- + if (this.opts.badge) { + let opts = Object.assign({}, { + align: 'right', + verticalAlign: 'top', + offsetLeft: 0, + offsetTop: 0 + }); + if (typeof this.opts.badge === 'string') { + opts = Object.assign(opts, {content: this.opts.badge}); + } else { + opts = Object.assign(opts, this.opts.badge); + } + + const badge = new Badge(opts); + + switch (opts.align) { + case 'left': + badge.x = this.x - badge.width / 2 + opts.offsetLeft; + break + case 'center': + badge.x = this.x + this.width / 2 - badge.width / 2 + opts.offsetLeft; + break + case 'right': + badge.x = this.x + this.width - badge.width / 2 + opts.offsetLeft; + } + + switch (opts.verticalAlign) { + case 'top': + badge.y = this.y - badge.height / 2 + opts.offsetTop; + break + case 'middle': + badge.y = this.y + this.height / 2 - badge.height / 2 + opts.offsetTop; + break + case 'bottom': + badge.y = this.y + this.height - badge.height / 2 + opts.offsetTop; + } + + this.addChild(badge); + + this.badge = badge; + } + + // set position + //----------------- + this.position.set(this.opts.x, this.opts.y); + + return this + } + + /** + * Should be called to refresh the layout of the button. Can be used after resizing. + * + * @return {Button} A reference to the button for chaining. + */ + layout() { + + // Clear content + //----------------- + this.removeChild(this.content); + this.content = new PIXI.Container(); + this.addChild(this.content); + + // Set the icon + //----------------- + let icon = null; + + if (!this.active && this.iconInactive) { + icon = this.iconInactive; + } else if (this.active && this.iconActive) { + icon = this.iconActive; + } + + // Set the text + //----------------- + if (this.text) { + this.text.position.set(0, 0); + } + + // Width and Height + //----------------- + let width = 0; + if (icon && this.text) { + width = icon.width + this.text.width + 3 * this.opts.padding; + } else if (icon) { + width = icon.width + 2 * this.opts.padding; + } else if (this.text) { + width = this.text.width + 2 * this.opts.padding; + } + + if (width < this.opts.minWidth) { + width = this.opts.minWidth; + } + + let height = 0; + if (icon) { + height = icon.height + 2 * this.opts.padding; + } else if (this.text) { + height = this.text.height + 2 * this.opts.padding; + } + + if (height < this.opts.minHeight) { + height = this.opts.minHeight; + } + + this._width = width; + this._height = height; + + // Position icon and text + //----------------- + if (icon && this.text) { + if (this.opts.iconPosition === 'right') { + icon.x = this.text.width + this.opts.padding; + } else { + this.text.x = icon.width + this.opts.padding; + } + this.content.addChild(icon, this.text); + } else if (icon) { + this.content.addChild(icon); + } else if (this.text) { + this.content.addChild(this.text); + } + + this.layoutInnerContent(); + this.layoutContent(); + + this.icon = icon; + + // draw + //----------------- + this.draw(); + + return this + } + + /** + * Calculates the positions of the content children (icon and/or text). + * + * @private + * @return {Button} A reference to the button for chaining. + */ + layoutInnerContent() { + + for (let child of this.content.children) { + switch (this.opts.verticalAlign) { + case 'top': + child.y = 0; + break + case 'middle': + child.y = this.content.height / 2 - child.height / 2; + break + case 'bottom': + child.y = this.content.height - child.height; + break + } + } + + return this + } + + /** + * Sets the horizontal and vertical position of the content. + * Uses the option keys "align" and "verticalAlign". + * + * @private + * @return {Button} A reference to the button for chaining. + */ + layoutContent() { + + switch (this.opts.align) { + case 'left': + this.content.x = this.opts.padding; + break + case 'center': + this.content.x = ((this._width - this.content.width) / 2); + break + case 'right': + this.content.x = this._width - this.opts.padding - this.content.width; + break + } + + switch (this.opts.verticalAlign) { + case 'top': + this.content.y = this.opts.padding; + break + case 'middle': + this.content.y = (this._height - this.content.height) / 2; + break + case 'bottom': + this.content.y = this._height - this.opts.padding - this.content.height; + break + } + + return this + } + + /** + * Draws the canvas. + * + * @private + * @return {Button} A reference to the button for chaining. + */ + draw() { + + this.button.clear(); + if (this.active) { + this.button.lineStyle(this.opts.strokeActiveWidth, this.opts.strokeActive, this.opts.strokeActiveAlpha); + this.button.beginFill(this.opts.fillActive, this.opts.fillActiveAlpha); + } else { + this.button.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); + this.button.beginFill(this.opts.fill, this.opts.fillAlpha); + } + this.button.drawRoundedRect(0, 0, this._width, this._height, this.opts.radius); + this.button.endFill(); + + return this + } + + /** + * Gets or sets the active state. + * + * @member {boolean} + */ + get active() { + return this._active + } + set active(value) { + + this._active = value; + + if (this._active) { + if (this.text) { + this.text.style = this.opts.textStyleActive; + } + } else { + if (this.text) { + this.text.style = this.opts.textStyle; + } + } + + this.layout(); + } + + /** + * Gets or sets the disabled state. When disabled, the button cannot be clicked. + * + * @member {boolean} + */ + get disabled() { + return this._disabled + } + set disabled(value) { + + this._disabled = value; + + if (this._disabled) { + this.button.interactive = false; + this.button.buttonMode = false; + this.button.alpha = .5; + if (this.icon) { + this.icon.alpha = .5; + } + if (this.text) { + this.text.alpha = .5; + } + } else { + this.button.interactive = true; + this.button.buttonMode = true; + this.button.alpha = 1; + if (this.icon) { + this.icon.alpha = 1; + } + if (this.text) { + this.text.alpha = 1; + } + } + } + + /** + * Shows the button (sets his alpha values to 1). + * + * @return {Button} A reference to the button for chaining. + */ + show() { + + this.opts.strokeAlpha = 1; + this.opts.strokeActiveAlpha = 1; + this.opts.fillAlpha = 1; + this.opts.fillActiveAlpha = 1; + + this.layout(); + + return this + } + + /** + * Hides the button (sets his alpha values to 0). + * + * @return {Button} A reference to the button for chaining. + */ + hide() { + + this.opts.strokeAlpha = 0; + this.opts.strokeActiveAlpha = 0; + this.opts.fillAlpha = 0; + this.opts.fillActiveAlpha = 0; + + this.layout(); + + return this + } + + /** + * Loads an icon + * + * @private + * @param {string|PIXI.DisplayObject} icon - The icon to load. + * @param {number} color - The color of the icon (if not an PIXI.DisplayObject). + * @return {PIXI.DisplayObject} Return the icon as an PIXI.DisplayObject. + */ + loadIcon(icon, color) { + + let displayObject = null; + + if (icon instanceof PIXI.DisplayObject) { + displayObject = icon; + } else { + let size = 17; + if (this.text) { + size = this.text.height; + } else if (this.opts.minHeight) { + size = this.opts.minHeight - (2 * this.opts.padding); + } + + const url = Button.iconIsUrl(icon) ? icon : `../../assets/icons/png/flat/${icon}.png`; + const iconTexture = PIXI.Texture.fromImage(url, true); + + const sprite = new PIXI.Sprite(iconTexture); + sprite.tint = color; + sprite.width = size; + sprite.height = size; + + displayObject = sprite; + } + + return displayObject + } + + /** + * Tests if an icon string is an url. + * + * @private + * @static + * @param {string} url - The url to test. + * @return {boolean} true if the url is an url to an image. + */ + static iconIsUrl(url) { + return /\.(png|svg|gif|jpg|jpeg|tif|tiff)$/i.test(url) + } + + /** + * Gets or sets the color of the current icon (no matter how the status is). Changing the color, changes + * the tint property of the icon sprite. + * + * @member {number} + */ + get iconColor() { + return this.icon ? this.icon.tint : null + } + set iconColor(value) { + if (this.icon) { + this.icon.tint = value; + } + } + } + + /** + * Class that represents a PixiJS ButtonGroup. + * + * @example + * // Create the button group + * const buttonGroup = new ButtonGroup({ + * buttons: [ + * {label: 'Button 1', action: event => console.log(event)}, + * {label: 'Button 2', action: event => console.log(event)}, + * {label: 'Button 3', action: event => console.log(event)} + * ], + * minWidth: 100 + * }) + * + * // Add the button group to a DisplayObject + * app.scene.addChild(buttonGroup) + * + * @class + * @extends PIXI.Graphics + * @see {@link http://pixijs.download/dev/docs/PIXI.Graphics.html|PIXI.Graphics} + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/buttongroup.html|DocTest} + */ + class ButtonGroup extends PIXI.Graphics { + + /** + * Creates an instance of a ButtonGroup. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the button group. + * @param {number} [opts.id=auto generated] - The id of the button group. + * @param {number} [opts.x=0] - The x position of the button group. Can be also set after creation with buttonGroup.x = 0. + * @param {number} [opts.y=0] - The y position of the button group. Can be also set after creation with buttonGroup.y = 0. + * @param {object[]} [opts.buttons=[]] - An array of the buttons of the button group. One item of the array (one object) + * can have exactly the same properties as an Button object when instantiating a Button. If a property of the button group + * conflicts with a property of a button object, the value from the button object will be used. + * @param {string|Theme=} [opts.theme=dark] - The theme to use for this button group. Possible values are dark, light, red + * or a Theme object. + * @param {number} [opts.minWidth=44] - Button: The minimum width of one button. + * @param {number} [opts.minHeight=44] - Button: The minimum height of one button. + * @param {number} [opts.padding=Theme.padding] - Button: The inner spacing (distance from icon and/or label) the the border. + * @param {number} [opts.margin=Theme.margin] - The outer spacing (distance from one button to the previous/next button). + * @param {string} [opts.iconPosition=left] - Button: The position of the icon in relation to the label. Can be left or right. + * @param {number} [opts.iconColor=Theme.iconColor] - Button: The color of the icon (set by the tint property) as a hex value. + * @param {number} [opts.iconColorActive=Theme.iconColorActive] - Button: The color of the icon when activated. + * @param {number} [opts.fill=Theme.fill] - Button: The color of the button background as a hex value. + * @param {number} [opts.fillAlpha=Theme.fillAlpha] - Button: The alpha value of the background. + * @param {number} [opts.fillActive=Theme.fillActive] - Button: The color of the button background when activated. + * @param {number} [opts.fillActiveAlpha=Theme.fillActiveAlpha] - Button: The alpha value of the background when activated. + * @param {number} [opts.stroke=Theme.stroke] - Button: The color of the border as a hex value. + * @param {number} [opts.strokeWidth=Theme.strokeWidth] - Button: The width of the border in pixel. + * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - Button: The alpha value of the border. + * @param {number} [opts.strokeActive=Theme.strokeActive] - Button: The color of the border when activated. + * @param {number} [opts.strokeActiveWidth=Theme.strokeActiveWidth] - Button: The width of the border in pixel when activated. + * @param {number} [opts.strokeActiveAlpha=Theme.strokeActiveAlpha] - Button: The alpha value of the border when activated. + * @param {object} [opts.textStyle=Theme.textStyle] - Button: A textstyle object for the styling of the label. See PIXI.TextStyle + * for possible options. + * @param {number} [opts.textStyleActive=Theme.textStyleActive] - Button: A textstyle object for the styling of the label when the + * button is activated. See PIXI.TextStyle for possible options. + * @param {string} [opts.style=default] - A shortcut for styling options. Possible values are default, link. + * @param {number} [opts.radius=Theme.radius] - Button: The radius of the four corners of the button (which is a rounded rectangle). + * @param {boolean} [opts.disabled=false] - Is the button group disabled? When disabled, the button group has a lower alpha value + * and cannot be clicked (interactive of every button is set to false). + * @param {string} [opts.type=default] - The type of the button group. Can be default, checkbox or radio. When the type is + * checkbox, the active state is toggled for each button automatically. When the type is radio, only one button can + * be activated at the same time. + * @param {string} [opts.orientation=horizontal] - The orientation of the button group. Can be horizontal or vertical. + * @param {string} [opts.align=center] - Button: The horizontal position of the label and the icon. Possible values are + * left, center and right. Only affects the style when the minWidth is bigger than the width of the icon and label. + * @param {string} [opts.verticalAlign=middle] - Button: The vertical position of the label and the icon. Possible values are + * top, middle and bottom. Only affects the style when the minHeight is bigger than the height of the icon and label. + * @param {boolean} [opts.visible=true] - Is the button group initially visible (property visible)? + */ + constructor(opts = {}) { + + super(); + + const theme = Theme.fromString(opts.theme); + this.theme = theme; + + this.opts = Object.assign({}, { + id: PIXI.utils.uid(), + x: 0, + y: 0, + buttons: [], + minWidth: 44, + minHeight: 44, + padding: theme.padding, + margin: theme.margin, + iconPosition: 'left', // left, right + iconColor: theme.iconColor, + iconColorActive: theme.iconColorActive, + fill: theme.fill, + fillAlpha: theme.fillAlpha, + fillActive: theme.fillActive, + fillActiveAlpha: theme.fillActiveAlpha, + stroke: theme.stroke, + strokeWidth: theme.strokeWidth, + strokeAlpha: theme.strokeAlpha, + strokeActive: theme.strokeActive, + strokeActiveWidth: theme.strokeActiveWidth, + strokeActiveAlpha: theme.strokeActiveAlpha, + textStyle: theme.textStyle, + textStyleActive: theme.textStyleActive, + style: 'default', + radius: theme.radius, + disabled: null, + type: 'default', // default, checkbox, radio + orientation: 'horizontal', + align: 'center', // left, center, right + verticalAlign: 'middle', // top, middle, bottom + visible: true + }, opts); + + this.buttons = []; + + this._disabled = null; + + this.visible = this.opts.visible; + + // setup + //----------------- + this.setup(); + + // layout + //----------------- + this.layout(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {ButtonGroup} A reference to the button group for chaining. + */ + setup() { + + // Buttons + //----------------- + let position = 0; + + for (let it of this.opts.buttons) { + + delete it.x; + delete it.y; + + if (this.opts.orientation === 'horizontal') { + it.x = position; + } else { + it.y = position; + } + + it.theme = it.theme || this.opts.theme; + it.minWidth = it.minWidth || this.opts.minWidth; + it.minHeight = it.minHeight || this.opts.minHeight; + it.padding = it.padding || this.opts.padding; + it.iconPosition = it.iconPosition || this.opts.iconPosition; + it.iconColor = it.iconColor || this.opts.iconColor; + it.iconColorActive = it.iconColorActive || this.opts.iconColorActive; + it.fill = it.fill || this.opts.fill; + it.fillAlpha = it.fillAlpha || this.opts.fillAlpha; + it.fillActive = it.fillActive || this.opts.fillActive; + it.fillActiveAlpha = it.fillActiveAlpha || this.opts.fillActiveAlpha; + it.stroke = it.stroke || this.opts.stroke; + it.strokeWidth = it.strokeWidth != null ? it.strokeWidth : this.opts.strokeWidth; + it.strokeAlpha = it.strokeAlpha != null ? it.strokeAlpha : this.opts.strokeAlpha; + it.strokeActive = it.strokeActive || this.opts.strokeActive; + it.strokeActiveWidth = it.strokeActiveWidth != null ? it.strokeActiveWidth : this.opts.strokeActiveWidth; + it.strokeActiveAlpha = it.strokeActiveAlpha != null ? it.strokeActiveAlpha : this.opts.strokeActiveAlpha; + it.textStyle = it.textStyle || this.opts.textStyle; + it.textStyleActive = it.textStyleActive || this.opts.textStyleActive; + it.style = it.style || this.opts.style; + it.radius = it.radius != null ? it.radius : this.opts.radius; + if (!it.type) { + switch (this.opts.type) { + case 'checkbox': + it.type = this.opts.type; + break + default: + it.type = 'default'; + break + } + } + //it.type = it.type || this.opts.type || 'default' + it.align = it.align || this.opts.align; + it.verticalAlign = it.verticalAlign || this.opts.verticalAlign; + it.afterAction = (event, button) => { + if (this.opts.type === 'radio' && button.opts.type === 'default') { + this.buttons.forEach(it => { + if (it.opts.type === 'default') { + it.active = false; + } + }); + + if (button.opts.type === 'default') { + button.active = true; + } + } + }; + + if (it.tooltip) { + if (typeof it.tooltip === 'string') { + it.tooltip = {content: it.tooltip, container: this}; + } else { + it.tooltip = Object.assign({}, {container: this}, it.tooltip); + } + } + + let button = new Button(it); + + this.addChild(button); + this.buttons.push(button); + + position += (this.opts.orientation === 'horizontal' ? button.width : button.height) + this.opts.margin; + } + + if (this.opts.orientation === 'vertical') { + const maxWidth = this.getMaxButtonWidth(); + + this.buttons.forEach(it => { + it.opts.minWidth = maxWidth; + it.layout(); + }); + } + + // disabled + //----------------- + if (this.opts.disabled != null) { + this.disabled = this.opts.disabled; + } + + return this + } + + /** + * Should be called to refresh the layout of the button group. Can be used after resizing. + * + * @return {ButtonGroup} A reference to the button group for chaining. + */ + layout() { + + // set position + //----------------- + this.position.set(this.opts.x, this.opts.y); + + // draw + //----------------- + this.draw(); + + return this + } + + /** + * Draws the canvas. + * + * @private + * @return {ButtonGroup} A reference to the button group for chaining. + */ + draw() { + + if (this.opts.margin === 0) { + + this.buttons.forEach(it => it.hide()); + + this.clear(); + this.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); + this.beginFill(this.opts.fill, this.opts.fillAlpha); + this.drawRoundedRect(0, 0, this.width, this.height, this.opts.radius); + + // Draw borders + this.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha / 2); + + this.buttons.forEach((it, i) => { + if (i > 0) { + this.moveTo(it.x, it.y); + + if (this.opts.orientation === 'horizontal') { + this.lineTo(it.x, it.height); + } else { + this.lineTo(it.width, it.y); + } + + } + }); + + this.endFill(); + } + + return this + } + + /** + * Gets or sets the disabled state. When disabled, no button of the button group can be clicked. + * + * @member {boolean} + */ + get disabled() { + return this._disabled + } + + set disabled(value) { + + this._disabled = value; + + this.buttons.forEach(it => it.disabled = value); + } + + /** + * Searches all buttons of the button group and returns the maximum width of one button. + * + * @private + * @return {number} The maximum with of a button of the button group. + */ + getMaxButtonWidth() { + + let widths = this.buttons.map(it => it.width); + + return Math.max(...widths) + } + + /** + * Shows the button group (sets his alpha value to 1). + * + * @return {ButtonGroup} A reference to the button group for chaining. + */ + show() { + + this.alpha = 1; + + return this + } + + /** + * Hides the button group (sets his alpha value to 0). + * + * @return {ButtonGroup} A reference to the button group for chaining. + */ + hide() { + + this.alpha = 0; + + return this + } + } + + /** + * Class that represents a PixiJS InteractivePopup. + * The class is used for various other Popup-like classes + * like Popup, Message... + * + * @class + * @abstract + * @extends AbstractPopup + */ + class InteractivePopup extends AbstractPopup { + + /** + * Creates an instance of an InteractivePopup (only for internal use). + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the popup. + * @param {boolean} [opts.closeOnPopup=false] - Should the popup be closed when the user clicks on the popup? + * @param {boolean} [opts.closeButton=true] - Should a close button be displayed on the upper right corner? + * @param {object} [opts.button] - A Button object to be display on the lower right corner. + * @param {object} [opts.buttonGroup] - A ButtonGroup object to be displayed on the lower right corner. + */ + constructor(opts = {}) { + + opts = Object.assign({}, { + closeOnPopup: false, + closeButton: true, + button: null, + buttonGroup: null + }, opts); + + super(opts); + + this._closeButton = null; + this._buttons = null; + + // padding + this.smallPadding = this.opts.padding / 2; + + // setup + //----------------- + this.setup(); + + // layout + //----------------- + this.layout(); + } + + /** + * Creates the framework and instantiates everything. + * + * @private + * @return {AbstractPopup} A reference to the popup for chaining. + */ + setup() { + + super.setup(); + + // interaction + //----------------- + this.on('pointerup', e => { + if (this.opts.closeOnPopup) { + this.hide(); + } else { + e.stopPropagation(); + } + }); + + // closeButton + //----------------- + if (this.opts.closeButton) { + let closeButton = PIXI.Sprite.fromImage('../../assets/icons/png/flat/close.png', true); + closeButton.width = this.headerStyle.fontSize; + closeButton.height = closeButton.width; + closeButton.tint = this.theme.color2; + // This is needed, because the closeButton belongs to the content. The popup must resize with the closeButton. + if (this._header) { + closeButton.x = this._header.width + this.innerPadding; + } else if (this._content) { + closeButton.x = this._content.width + this.innerPadding; + } + + closeButton.interactive = true; + closeButton.buttonMode = true; + closeButton.on('pointerdown', e => { + this.hide(); + }); + + this._closeButton = closeButton; + this.addChild(closeButton); + + // maxWidth is set and a closeButton should be displayed + //----------------- + if (this.opts.maxWidth) { + const wordWrapWidth = this.opts.maxWidth - (2 * this.opts.padding) - this.smallPadding - this._closeButton.width; + if (this._header) { + this.headerStyle.wordWrapWidth = wordWrapWidth; + } else if (this._content) { + this.textStyle.wordWrapWidth = wordWrapWidth; + } + } + } + + // buttons + //----------------- + if (this.opts.button || this.opts.buttonGroup) { + if (this.opts.button) { + this._buttons = new Button(Object.assign({textStyle: this.theme.textStyleSmall}, this.opts.button)); + } else { + this._buttons = new ButtonGroup(Object.assign({textStyle: this.theme.textStyleSmall}, this.opts.buttonGroup)); + } + this.addChild(this._buttons); + + this._buttons.y = this.innerPadding + this.sy; + } + + return this + } + + /** + * Should be called to refresh the layout of the popup. Can be used after resizing. + * + * @return {AbstractPopup} A reference to the popup for chaining. + */ + layout() { + + super.layout(); + + // closeButton + //----------------- + if (this.opts.closeButton) { + this._closeButton.x = this.wantedWidth - this.smallPadding - this._closeButton.width; + this._closeButton.y = this.smallPadding; + } + + // buttons + //----------------- + if (this._buttons) { + this._buttons.x = this.wantedWidth - this.opts.padding - this._buttons.width; + this._buttons.y = this.wantedHeight - this.opts.padding - this._buttons.height; + } + + return this + } + + /** + * Calculates the size of the children of the AbstractPopup. + * Cannot use getBounds() because it is not updated when children + * are removed. + * + * @private + * @override + * @returns {object} An JavaScript object width the keys width and height. + */ + getInnerSize() { + + let size = super.getInnerSize(); + + if (this._closeButton) { + size.width += this.smallPadding + this._closeButton.width; + } + + if (this._buttons) { + size.width = Math.max(size.width, this._buttons.x + this._buttons.width); + size.height += this.innerPadding + this._buttons.height; + } + + return size + } + } + + /** + * Class that represents a PixiJS Popup. + * + * @example + * // Create the popup + * const popup = new Popup({ + * header: 'Goethe', + * content: 'Man kann die Erfahrung nicht früh genug machen, wie entbehrlich man in der Welt ist.' + * }) + * + * // Add the popup to a DisplayObject + * app.scene.addChild(popup) + * + * @class + * @extends InteractivePopup + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/popup.html|DocTest} + */ + class Popup extends InteractivePopup { + + /** + * Creates an instance of a Popup. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the popup. + * @param {boolean} [opts.closeButton=false] - Should a close button be displayed on the upper right corner? + * @param {number} [opts.minWidth=0] - The minimum width of the popup. + * @param {number} [opts.minHeight=0] - The minimum height of the popup. + */ + constructor(opts = {}) { + + opts = Object.assign({}, { + closeButton: false, + minWidth: 0, + minHeight: 0 + }, opts); + + super(opts); + } + } + + /** + * Class that represents a PixiJS Modal. + * + * @example + * // Create the button and the modal when clicked + * const button = new Button({ + * label: 'Show Modal', + * action: e => { + * const modal = new Modal({ + * app: app, + * header: 'This is the header', + * content: 'This is the text.' + * }) + * app.scene.addChild(modal) + * } + * }) + * + * // Add the button to a DisplayObject + * app.scene.addChild(button) + * + * @class + * @extends PIXI.Container + * @extends InteractivePopup + * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/modal.html|DocTest} + */ + class Modal extends PIXI.Container { + + /** + * Creates an instance of a Modal. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the modal. + * @param {number} [opts.id=auto generated] - The id of the modal. + * @param {PIXIApp} [opts.app=window.app] - The app where the modal belongs to. + * @param {number} [opts.backgroundFill=Theme.background] - The color of the background. + * @param {number} [opts.backgroundFillAlpha=0.6] - The opacity of the background. + * @param {boolean} [opts.closeOnBackground=true] - Should the modal be closed when the user clicks the + * background? + * @param {boolean} [opts.visible=true] - Is the modal initially visible (property visible)? + */ + constructor(opts = {}) { + + super(); + + const theme = Theme.fromString(opts.theme); + this.theme = theme; + + this.opts = Object.assign({}, { + id: PIXI.utils.uid(), + app: window.app, + backgroundFill: theme.background, + backgroundFillAlpha: .6, + closeOnBackground: true, + visible: true + }, opts); + + this.id = this.opts.id; + + this.background = null; + this.popup = null; + + this.alpha = 0; + this.visible = this.opts.visible; + + // setup + //----------------- + this.setup(); + + // layout + //----------------- + this.layout(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {Modal} A reference to the modal for chaining. + */ + setup() { + + // interaction + //----------------- + this.interactive = true; + this.on('added', e => { + if (this.visible) { + this.show(); + } + }); + + // background + //----------------- + let background = new PIXI.Graphics(); + this.background = background; + this.addChild(this.background); + + if (this.opts.closeOnBackground) { + background.interactive = true; + background.on('pointerup', e => { + this.hide(); + }); + } + + // popup + //----------------- + const popupOpts = Object.assign({}, this.opts, { + visible: true, + onHidden: () => { + this.hide(); + } + }); + let popup = new InteractivePopup(popupOpts); + this.popup = popup; + this.addChild(popup); + popup.show(); + + return this + } + + /** + * Should be called to refresh the layout of the modal. Can be used after resizing. + * + * @return {Modal} A reference to the modal for chaining. + */ + layout() { + + const width = this.opts.app.size.width; + const height = this.opts.app.size.height; + + // background + //----------------- + this.background.clear(); + this.background.beginFill(this.opts.backgroundFill, this.opts.backgroundFillAlpha); + this.background.drawRect(0, 0, width, height); + this.background.endFill(); + + // position + this.popup.x = width / 2 - this.popup.width / 2; + this.popup.y = height / 2 - this.popup.height / 2; + + return this + } + + /** + * Shows the modal (sets his alpha values to 1). + * + * @return {Modal} A reference to the modal for chaining. + */ + show() { + TweenLite.to(this, this.theme.fast, {alpha: 1, onStart: () => this.visible = true}); + + return this + } + + /** + * Hides the modal (sets his alpha values to 0). + * + * @return {Modal} A reference to the modal for chaining. + */ + hide() { + TweenLite.to(this, this.theme.fast, {alpha: 0, onComplete: () => this.visible = false}); + + return this + } + + /** + * Sets or gets the header. The getter always returns a PIXI.Text object. The setter can receive + * a string or a PIXI.Text object. + * + * @member {string|PIXI.Text} + */ + get header() { + return this.popup.header + } + set header(value) { + this.opts.header = value; + this.background.destroy(); + this.popup.destroy(); + this.setup().layout(); + } + + /** + * Sets or gets the content. The getter always returns an PIXI.DisplayObject. The setter can receive + * a string or a PIXI.DisplayObject. + * + * @member {string|PIXI.DisplayObject} + */ + get content() { + return this.popup.content + } + set content(value) { + this.opts.content = value; + this.background.destroy(); + this.popup.destroy(); + this.setup().layout(); + } + } + + /** + * Class that represents a Message. A message pops up and disappears after a specific amount of time. + * + * @example + * // Create the PixiJS App + * const app = new PIXIApp({ + * view: canvas, + * width: 900, + * height: 250 + * }).setup().run() + * + * // Create a button + * let button = new Button({ + * label: 'Click me', + * action: e => { + * const message = new Message({ + * app: app, + * header: 'Header', + * content: 'Text.' + * }) + * app.scene.addChild(message) + * } + * }) + * + * // Add the button to the scene + * app.scene.addChild(button) + * + * @class + * @extends InteractivePopup + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/message.html|DocTest} + */ + class Message extends InteractivePopup { + + /** + * Creates an instance of a Message. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the message. + * @param {PIXIApp} [opts.app=window.app] - The PIXIApp where this message belongs to. + * @param {boolean} [opts.closeButton=false] - Should a close button be displayed in the upper right corner? + * @param {number} [opts.minWidth=280] - The minimum width of the message box. Automatically expands with the content. + * @param {number} [opts.minHeight=100] - The minimum height of the message box. Automatically expands with the content. + * @param {number} [opts.margin=Theme.margin] - The outer spacing of the message box. + * @param {string} [opts.align=right] - The horizontal position of the message box relative to the app. Possible + * values are left, center, right. + * @param {string} [opts.verticalAlign=top] - The vertical position of the message box relative to the app. Possible + * values are top, middle, bottom. + * @param {number} [opts.duration=5] - The duration in seconds when the message box should disappear. + * @param {boolean} [opts.autoClose=true] - Should the message box be closed automatically? + * @param {number} [opts.closeDuration=Theme.fast] - The duration in seconds of the closing of the message box. + */ + constructor(opts = {}) { + + const theme = Theme.fromString(opts.theme); + + opts = Object.assign({}, { + app: window.app, + closeButton: false, + minWidth: 280, + minHeight: 100, + margin: theme.margin, + align: 'right', // left, center, right + verticalAlign: 'top', // top, middle, bottom + duration: 5, + autoClose: true, + closeDuration: theme.fast + }, opts); + + super(opts); + } + + /** + * Relayouts the position of the message box. + * + * @return {Message} Returns the message box for chaining. + */ + layout() { + + super.layout(); + + // horizontal + switch (this.opts.align) { + case 'left': + this.x = this.opts.margin; + break + case 'center': + this.x = (this.opts.app.size.width / 2) - (this.width / 2); + break + case 'right': + this.x = this.opts.app.size.width - this.opts.margin - this.width; + break + } + + // vertical + switch (this.opts.verticalAlign) { + case 'top': + this.y = this.opts.margin; + break + case 'middle': + this.y = (this.opts.app.size.height / 2) - (this.height / 2); + break + case 'bottom': + this.y = this.opts.app.size.height - this.opts.margin - this.height; + break + } + } + + /** + * Shows the message box. + * + * @private + */ + show() { + + super.show(); + + if (this.opts.autoClose) { + window.setTimeout(() => { + this.hide(); + }, this.opts.duration * 1000); + } + } + } + + /* globals WebKitPoint */ + + /** Tests whether an object is empty + * @param {Object} obj - the object to be tested + * @return {boolean} + */ + function isEmpty(obj) { + // > isEmpty({}) + // true + for (let i in obj) { + return false + } + return true + } + + function lerp(start, stop, amt) { + return amt * (stop - start) + start + } + + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + // Taken from: https://davidwalsh.name/essential-javascript-functions + function debounce(func, wait, immediate) { + let timeout; + return function () { + let context = this, + args = arguments; + let later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + let callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + } + } + + /** Returns an id that is guaranteed to be unique within the livetime of the + * application + * @return {string} + */ + let _idGenerator = 0; + function getId() { + return 'id' + _idGenerator++ + } + + class Dates { + + static create(fullYear, month, day) { + return new Date(Date.UTC(fullYear, month, day)) + } + + static daysInMonth(date) { + return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate() + } + + static startYearRange(date) { + return new Date(Date.UTC(date.getFullYear() - 1, 11, 31, 23, 59, 59, 999)) + } + + static endYearRange(date) { + return new Date(Date.UTC(date.getFullYear() + 1, 0, 1)) + } + + static prevYear(date, offset = 1) { + return this.create(date.getFullYear() - offset, 0, 1) + } + + static nextYear(date, offset = 1) { + return this.create(date.getFullYear() + offset, 0, 1) + } + + static nextMonth(date) { + return this.create(date.getFullYear(), date.getMonth() + 1, 1) + } + + static nextDay(date) { + return this.create( + date.getFullYear(), + date.getMonth(), + date.getDate() + 1 + ) + } + + static nextHour(date) { + // See http://stackoverflow.com/questions/1050720/adding-hours-to-javascript-date-object + return new Date(date.getTime() + 60 * 60 * 1000) + } + + static nextMinute(date) { + // See above + return new Date(date.getTime() + 60 * 1000) + } + + static nextSecond(date) { + // See above + return new Date(date.getTime() + 1000) + } + + static nextMillisecond(date) { + // See above + return new Date(date.getTime() + 1) + } + + static *iterYears(start, end) { + let date = this.create(start.getFullYear(), 0, 1); + while (date <= end) { + yield date; + date = this.nextYear(date); + } + yield date; + } + + static *iterMonths(year, limit = 12) { + let month = 0; + while (month < limit) { + let date = this.create(year.getFullYear(), month, 1); + yield date; + month += 1; + } + } + + static *iterMonthsOfYears(years) { + for (let year of years) { + for (let month of this.iterMonths(year)) { + yield month; + } + } + } + + static *iterDays(month) { + let day = 1; + let limit = Dates.daysInMonth(month); + while (day <= limit) { + let date = this.create(month.getFullYear(), month.getMonth(), day); + yield date; + day += 1; + } + } + + static *iterDaysOfMonths(months) { + for (let month of months) { + for (let day of this.iterDays(month)) { + yield day; + } + } + } + } + /* Color conversion functions */ + + class Colors { + // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + + static rgb2num(red, green, blue) { + let rgb = blue | (green << 8) | (red << 16); + return 0x000000 + rgb + } + + static rgb2hex(red, green, blue) { + let rgb = blue | (green << 8) | (red << 16); + return '#' + (0x1000000 + rgb).toString(16).slice(1) + } + + static hex2rgb(hex) { + // long version + let r = hex.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i); + if (r) { + return r.slice(1, 4).map(x => { + return parseInt(x, 16) + }) + } + // short version + r = hex.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i); + if (r) { + return r.slice(1, 4).map(x => { + return 0x11 * parseInt(x, 16) + }) + } + return null + } + + static rgb(r, g, b) { + return { r, g, b } + } + + static string2hex(str) { + return parseInt('0x' + str.slice(1)) + } + + static lerp(rgb1, rgb2, amount) { + return { + r: Math.round(lerp(rgb1.r, rgb2.r, amount)), + g: Math.round(lerp(rgb1.g, rgb2.g, amount)), + b: Math.round(lerp(rgb1.b, rgb2.b, amount)) + } + } + + static get violet() { + return Colors.rgb2num(89, 34, 131) + } + + static get steelblue() { + return Colors.rgb2num(0, 130, 164) + } + + static get ochre() { + return Colors.rgb2num(181, 157, 0) + } + + static get turquoise() { + return Colors.rgb2num(34, 164, 131) + } + + static get eminence() { + return Colors.rgb2num(150, 60, 134) + } + + static random() { + let r = Math.round(Math.random() * 255); + let g = Math.round(Math.random() * 255); + let b = Math.round(Math.random() * 255); + return Colors.rgb2num(r, g, b) + } + } + + class Cycle extends Array { + constructor(...items) { + super(); + for (let item of items) { + this.push(item); + } + this.index = 0; + } + + next() { + if (this.index == this.length) { + this.index = 0; + } + return this[this.index++] + } + + current() { + if (this.index === this.length) { + this.index = 0; + } + return this[this.index] + } + } + + /** Static methods to compute 2D points with x and y coordinates. + */ + class Points { + static length(a) { + return Math.sqrt(a.x * a.x + a.y * a.y) + } + + static normalize(p) { + let len = this.length(p); + return this.multiplyScalar(p, 1 / len) + } + + static mean(a, b) { + return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 } + } + + static subtract(a, b) { + return { x: a.x - b.x, y: a.y - b.y } + } + + static multiply(a, b) { + return { x: a.x * b.x, y: a.y * b.y } + } + + static divide(a, b) { + return { x: a.x / b.x, y: a.y / b.y } + } + + static multiplyScalar(a, b) { + return { x: a.x * b, y: a.y * b } + } + + static add(a, b) { + return { x: a.x + b.x, y: a.y + b.y } + } + + static negate(p) { + return { x: -p.x, y: -p.y } + } + + static angle(p1, p2) { + return Math.atan2(p1.y - p2.y, p1.x - p2.x) + } + + static normalizedAngle(p1, p2) { + return Angle.normalize(this.angle(p1, p2)) + } + + static normalized2Angle(p1, p2) { + return Angle.normalize2(this.angle(p1, p2)) + } + + static arc(p, alpha, radius) { + return { + x: p.x + radius * Math.cos(alpha), + y: p.y + radius * Math.sin(alpha) + } + } + + static distance(a, b) { + let dx = a.x - b.x; + let dy = a.y - b.y; + return Math.sqrt(dx * dx + dy * dy) + } + + static fromPageToNode(element, p) { + // if (window.webkitConvertPointFromPageToNode) { + // return window.webkitConvertPointFromPageToNode(element, + // new WebKitPoint(p.x, p.y)) + // } + return window.convertPointFromPageToNode(element, p.x, p.y) + } + + static fromNodeToPage(element, p) { + // if (window.webkitConvertPointFromNodeToPage) { + // return window.webkitConvertPointFromNodeToPage(element, + // new WebKitPoint(p.x, p.y)) + // } + return window.convertPointFromNodeToPage(element, p.x, p.y) + } + } + + /** Static methods to compute angles. + */ + class Angle { + + static normalize(angle) { + let TAU = Math.PI * 2.0; + while (angle > Math.PI) { + angle -= TAU; + } + while (angle < -Math.PI) { + angle += TAU; + } + return angle + } + + static normalize2(angle) { + let TAU = Math.PI * 2.0; + while (angle > TAU) { + angle -= TAU; + } + while (angle < 0) { + angle += TAU; + } + return angle + } + + static normalizeDegree(angle) { + let full = 360.0; + while (angle > 180.0) { + angle -= full; + } + while (angle < -180.0) { + angle += full; + } + return angle + } + + static normalizedDiff(a, b) { + return this.normalize(this.diff(a, b)) + } + + static normalized2Diff(a, b) { + return this.normalize2(this.diff(a, b)) + } + + static diff(a, b) { + return Math.atan2(Math.sin(a - b), Math.cos(a - b)) + } + + static degree2radian(degree) { + return Math.PI * degree / 180.0 + } + + static radian2degree(rad) { + return 180.0 / Math.PI * rad + } + } + + class Elements$1 { + static setStyle(element, styles) { + for (let key in styles) { + element.style[key] = styles[key]; + } + } + + static addClass(element, cssClass) { + element.classList.add(cssClass); + } + + static removeClass(element, cssClass) { + element.classList.remove(cssClass); + } + + static toggleClass(element, cssClass) { + element.classList.toggle(cssClass); + } + + static hasClass(element, cssClass) { + return element.classList.contains(cssClass) + } + } + + class MapProxy { + /* This class is needed if we want to use the interaction classes + in Firefox 45.8 and modern Browsers. + + A workaround for https://github.com/babel/babel/issues/2334 + */ + constructor() { + this.map = new Map(); + } + + get size() { + return this.map.size + } + + get(key) { + return this.map.get(key) + } + + set(key, value) { + return this.map.set(key, value) + } + + delete(key) { + return this.map.delete(key) + } + + clear() { + return this.map.clear() + } + + has(key) { + return this.map.has(key) + } + + keys() { + return this.map.keys() + } + + values() { + return this.map.values() + } + + entries() { + return this.map.entries() + } + + forEach(func) { + this.map.forEach(func); + } + } + + /* Based om https://gist.github.com/cwleonard/e124d63238bda7a3cbfa */ + class Polygon { + /* + * This is the Polygon constructor. All points are center-relative. + */ + constructor(center) { + this.points = new Array(); + this.center = center; + } + + /* + * Point x and y values should be relative to the center. + */ + addPoint(p) { + this.points.push(p); + } + + /* + * Point x and y values should be absolute coordinates. + */ + addAbsolutePoint(p) { + this.points.push({ x: p.x - this.center.x, y: p.y - this.center.y }); + } + + /* + * Returns the number of sides. Equal to the number of vertices. + */ + getNumberOfSides() { + return this.points.length + } + + /* + * rotate the polygon by a number of radians + */ + rotate(rads) { + for (let i = 0; i < this.points.length; i++) { + let x = this.points[i].x; + let y = this.points[i].y; + this.points[i].x = Math.cos(rads) * x - Math.sin(rads) * y; + this.points[i].y = Math.sin(rads) * x + Math.cos(rads) * y; + } + } + + /* + * The draw function takes as a parameter a Context object from + * a Canvas element and draws the polygon on it. + */ + draw(context, { lineWidth = 2, stroke = '#000000', fill = null } = {}) { + context.beginPath(); + context.moveTo( + this.points[0].x + this.center.x, + this.points[0].y + this.center.y + ); + for (let i = 1; i < this.points.length; i++) { + context.lineTo( + this.points[i].x + this.center.x, + this.points[i].y + this.center.y + ); + } + context.closePath(); + context.lineWidth = lineWidth; + if (stroke) { + context.strokeStyle = stroke; + context.stroke(); + } + if (fill) { + context.fillStyle = fill; + context.fill(); + } + } + + absolutePoints() { + let result = new Array(); + for (let p of this.points) { + result.push(Points.add(p, this.center)); + } + return result + } + + flatAbsolutePoints() { + let result = new Array(); + for (let p of this.points) { + let a = Points.add(p, this.center); + result.push(a.x); + result.push(a.y); + } + return result + } + + /* + * This function returns true if the given point is inside the polygon, + * and false otherwise. + */ + containsPoint(pnt) { + let nvert = this.points.length; + let testx = pnt.x; + let testy = pnt.y; + + let vertx = new Array(); + for (let q = 0; q < this.points.length; q++) { + vertx.push(this.points[q].x + this.center.x); + } + + let verty = new Array(); + for (let w = 0; w < this.points.length; w++) { + verty.push(this.points[w].y + this.center.y); + } + + let i, + j = 0; + let c = false; + for (i = 0, j = nvert - 1; i < nvert; j = i++) { + if ( + verty[i] > testy != verty[j] > testy && + testx < + (vertx[j] - vertx[i]) * + (testy - verty[i]) / + (verty[j] - verty[i]) + + vertx[i] + ) + c = !c; + } + return c + } + + multiplyScalar(scale) { + let center = Points.multiplyScalar(this.center, scale); + let clone = new Polygon(center); + for (let p of this.points) { + clone.addPoint(Points.multiplyScalar(p, scale)); + } + return clone + } + + /* + * To detect intersection with another Polygon object, this + * function uses the Separating Axis Theorem. It returns false + * if there is no intersection, or an object if there is. The object + * contains 2 fields, overlap and axis. Moving the polygon by overlap + * on axis will get the polygons out of intersection. + */ + intersectsWith(other) { + let axis = { x: 0, y: 0 }; + let tmp, minA, maxA, minB, maxB; + let side, i; + let smallest = null; + let overlap = 99999999; + + /* test polygon A's sides */ + for (side = 0; side < this.getNumberOfSides(); side++) { + /* get the axis that we will project onto */ + if (side == 0) { + axis.x = + this.points[this.getNumberOfSides() - 1].y - + this.points[0].y; + axis.y = + this.points[0].x - + this.points[this.getNumberOfSides() - 1].x; + } else { + axis.x = this.points[side - 1].y - this.points[side].y; + axis.y = this.points[side].x - this.points[side - 1].x; + } + + /* normalize the axis */ + tmp = Math.sqrt(axis.x * axis.x + axis.y * axis.y); + axis.x /= tmp; + axis.y /= tmp; + + /* project polygon A onto axis to determine the min/max */ + minA = maxA = this.points[0].x * axis.x + this.points[0].y * axis.y; + for (i = 1; i < this.getNumberOfSides(); i++) { + tmp = this.points[i].x * axis.x + this.points[i].y * axis.y; + if (tmp > maxA) maxA = tmp; + else if (tmp < minA) minA = tmp; + } + /* correct for offset */ + tmp = this.center.x * axis.x + this.center.y * axis.y; + minA += tmp; + maxA += tmp; + + /* project polygon B onto axis to determine the min/max */ + minB = maxB = + other.points[0].x * axis.x + other.points[0].y * axis.y; + for (i = 1; i < other.getNumberOfSides(); i++) { + tmp = other.points[i].x * axis.x + other.points[i].y * axis.y; + if (tmp > maxB) maxB = tmp; + else if (tmp < minB) minB = tmp; + } + /* correct for offset */ + tmp = other.center.x * axis.x + other.center.y * axis.y; + minB += tmp; + maxB += tmp; + + /* test if lines intersect, if not, return false */ + if (maxA < minB || minA > maxB) { + return false + } else { + let o = maxA > maxB ? maxB - minA : maxA - minB; + if (o < overlap) { + overlap = o; + smallest = { x: axis.x, y: axis.y }; + } + } + } + + /* test polygon B's sides */ + for (side = 0; side < other.getNumberOfSides(); side++) { + /* get the axis that we will project onto */ + if (side == 0) { + axis.x = + other.points[other.getNumberOfSides() - 1].y - + other.points[0].y; + axis.y = + other.points[0].x - + other.points[other.getNumberOfSides() - 1].x; + } else { + axis.x = other.points[side - 1].y - other.points[side].y; + axis.y = other.points[side].x - other.points[side - 1].x; + } + + /* normalize the axis */ + tmp = Math.sqrt(axis.x * axis.x + axis.y * axis.y); + axis.x /= tmp; + axis.y /= tmp; + + /* project polygon A onto axis to determine the min/max */ + minA = maxA = this.points[0].x * axis.x + this.points[0].y * axis.y; + for (i = 1; i < this.getNumberOfSides(); i++) { + tmp = this.points[i].x * axis.x + this.points[i].y * axis.y; + if (tmp > maxA) maxA = tmp; + else if (tmp < minA) minA = tmp; + } + /* correct for offset */ + tmp = this.center.x * axis.x + this.center.y * axis.y; + minA += tmp; + maxA += tmp; + + /* project polygon B onto axis to determine the min/max */ + minB = maxB = + other.points[0].x * axis.x + other.points[0].y * axis.y; + for (i = 1; i < other.getNumberOfSides(); i++) { + tmp = other.points[i].x * axis.x + other.points[i].y * axis.y; + if (tmp > maxB) maxB = tmp; + else if (tmp < minB) minB = tmp; + } + /* correct for offset */ + tmp = other.center.x * axis.x + other.center.y * axis.y; + minB += tmp; + maxB += tmp; + + /* test if lines intersect, if not, return false */ + if (maxA < minB || minA > maxB) { + return false + } else { + let o = maxA > maxB ? maxB - minA : maxA - minB; + if (o < overlap) { + overlap = o; + smallest = { x: axis.x, y: axis.y }; + } + } + } + return { overlap: overlap + 0.001, axis: smallest } + } + + static fromPoints(points) { + let min = { x: Number.MAX_VALUE, y: Number.MAX_VALUE }; + let max = { x: Number.MIN_VALUE, y: Number.MIN_VALUE }; + for (let p of points) { + min.x = Math.min(p.x, min.x); + max.x = Math.max(p.x, max.x); + min.y = Math.min(p.y, min.y); + max.y = Math.max(p.y, max.y); + } + let center = Points.mean(min, max); + let polygon = new Polygon(center); + for (let p of points) { + polygon.addAbsolutePoint(p); + } + return polygon + } + } + + /* global apollo, subscriptions, gql */ + + /** + * A special InteractionManager for fullscreen apps, which may + * go beyond the limits of WebGL drawing buffers. On Safari and Chrome + * the drawing buffers are limited to 4096 in width (Safari) or 4096x4096 + * in total buffer size (Chrome). The original InteractionManager.mapPositionToPoint + * does not work with these extreme sizes which mainly occur if large + * retina displays (>= 4K) are used with devicePixelRatio > 1. + * + * @private + * @class + * @extends PIXI.interaction.InteractionManager + * @see {@link http://pixijs.download/dev/docs/PIXI.interaction.InteractionManager.html|PIXI.interaction.InteractionManager} + * @see {@link https://stackoverflow.com/questions/29710696/webgl-drawing-buffer-size-does-not-equal-canvas-size} + */ + class FullscreenInteractionManager extends PIXI.interaction.InteractionManager { + + mapPositionToPoint(point, x, y) { + let resolution = this.renderer.resolution; + let extendWidth = 1.0; + let extendHeight = 1.0; + let dy = 0; + let canvas = this.renderer.view; + let context = canvas.getContext('webgl'); + if (context.drawingBufferWidth < canvas.width || + context.drawingBufferHeight < canvas.height) { + extendWidth = context.drawingBufferWidth / canvas.width; + extendHeight = context.drawingBufferHeight / canvas.height; + //dx = wantedWidth - context.drawingBufferWidth + dy = (canvas.height - context.drawingBufferHeight) / resolution; + } + x *= extendWidth; + y *= extendHeight; + + super.mapPositionToPoint(point, x, y + dy); + } + } + + /** + * The class PixiApp extends the class PIXI.Application + * by several functions and makes meaningful pre-assumptions. + * + * @example + * // Create the app + * const app = new PIXIApp({ + * view: canvas, + * width: 450, + * height: 150, + * fpsLogging: true, + * theme: 'light', + * transparent: false + * }).setup().run() + * + * @class + * @extends PIXI.Application + * @see {@link http://pixijs.download/dev/docs/PIXI.Application.html|PIXI.Application} + */ + class PIXIApp extends PIXI.Application { + + /** + * Creates an instance of a PixiApp. + * + * @constructor + * @param {object} [opts={}] - An options object. The following options can be set: + * @param {number} [opts.width] - The width of the renderer. If no set, the application will run in fullscreen. + * @param {number} [opts.height] - The height of the renderer. If no set, the application will run in fullscreen. + * @param {HTMLElement} [opts.view] - The canvas HTML element. If not set, a render-element is added inside the body. + * @param {boolean} [opts.transparent=true] - Should the render view be transparent? + * @param {boolean} [opts.antialias=true] - Sets antialias (only applicable in chrome at the moment). + * @param {number} [opts.resolution=window.devicePixelRatio | 1] - The resolution / device pixel ratio of the renderer, retina would be 2. + * @param {boolean} [opts.autoResize=true] - Should the canvas-element be resized automatically if the resolution was set? + * @param {number} [opts.backgroundColor=0x282828] - The color of the background. + * @param {string|Theme} [opts.theme=dark] - The name of the theme (dark, light, red) or a Theme object to use for styling. + * @param {boolean} [opts.fpsLogging=false] - If set to true, the current frames per second are displayed in the upper left corner. + * @param {object} [opts.progress={}] - Can be used to add options to the progress bar. See class Progress for more informations. + * @param {boolean} [opts.forceCanvas=false] - Prevents selection of WebGL renderer, even if such is present. + * @param {boolean} [opts.roundPixels=true] - Align PIXI.DisplayObject coordinates to screen resolution. + * @param {boolean} [opts.monkeyPatchMapping=true] - Monkey patch for canvas fullscreen support on large displays. + * @param {boolean} [opts.adaptive=true] - Adds Graphics adaptive calculation of quadratic curve and arc subdivision. + */ + constructor({ + width = null, height = null, view = null, + transparent = true, backgroundColor = 0x282828, theme = 'dark', + antialias = true, resolution = window.devicePixelRatio || 1, autoResize = true, + fpsLogging = false, progress = {}, forceCanvas = false, roundPixels = true, monkeyPatchMapping = true, adaptive = true, + graphql = false }) { + + const fullScreen = !width || !height; + + if (fullScreen) { + width = window.innerWidth; + height = window.innerHeight; + } + + super({ + view: view, + width: width, + height: height, + transparent: transparent, + antialias: antialias, + resolution: resolution, + autoResize: autoResize, + backgroundColor: backgroundColor, + roundPixels: roundPixels, + forceCanvas: forceCanvas + }); + + this.width = width; + this.height = height; + this.theme = Theme.fromString(theme); + this.fpsLogging = fpsLogging; + this.progressOpts = progress; + this.fullScreen = fullScreen; + this.orient = null; + this.originalMapPositionToPoint = null; + this.monkeyPatchMapping = monkeyPatchMapping; + PIXI.Graphics.CURVES.adaptive = adaptive; + this.graphql = graphql; + if (fullScreen || autoResize) { + console.log('App is in fullScreen mode or autoResize mode'); + const resizeDebounced = debounce(event => this.resize(event), 50); + window.addEventListener('resize', resizeDebounced); + document.body.addEventListener('orientationchange', this.checkOrientation.bind(this)); + } + if (monkeyPatchMapping) { + console.log('Using monkey patched coordinate mapping'); + // Pluggin the specializtion does not work. Monkey patching does + // this.renderer.plugins.interaction = new FullscreenInteractionManager(this.renderer) + this.monkeyPatchPixiMapping(); + } + } + + /** + * Extra setup method to construct complex scenes, etc... + * Overwrite this method if you need additonal views and components. + * + * @return {PIXIApp} A reference to the PIXIApp for chaining. + */ + setup() { + this.scene = this.sceneFactory(); + this.stage.addChild(this.scene); + + // fpsLogging + if (this.fpsLogging) { + this.addFpsDisplay(); + } + + // GraphQL + if (this.graphql && typeof apollo !== 'undefined') { + + const networkInterface = apollo.createNetworkInterface({ + uri: '/graphql' + }); + + const wsClient = new subscriptions.SubscriptionClient(`wss://${location.hostname}/subscriptions`, { + reconnect: true, + connectionParams: {} + }); + + const networkInterfaceWithSubscriptions = subscriptions.addGraphQLSubscriptions( + networkInterface, + wsClient + ); + + this.apolloClient = new apollo.ApolloClient({ + networkInterface: networkInterfaceWithSubscriptions + }); + } + + // progress + this._progress = new Progress(Object.assign({ theme: this.theme }, this.progressOpts, { app: this })); + this._progress.visible = false; + this.stage.addChild(this._progress); + + return this + } + + /** + * Tests whether the width is larger than the height of the application. + * + * @return {boolean} Returns true if app is in landscape mode. + */ + orientation() { + return this.width > this.height + } + + /** + * Checks orientation and adapts view size if necessary. Implements a + * handler for the orientationchange event. + * + * @param {event=} - orientationchange event + */ + checkOrientation(event) { + var value = this.orientation(); + if (value != this.orient) { + setTimeout(100, function () { + this.orientationChanged(true); + }.bind(this)); + this.orient = value; + } + } + + /** + * Called if checkOrientation detects an orientation change event. + * + * @param {boolean=} [force=false] - Called if checkOrientation detects an orientation change event. + */ + orientationChanged(force = false) { + if (this.expandRenderer() || force) { + this.layout(); + } + } + + /** + * Called after a resize. Empty method but can be overwritten to + * adapt their layout to the new app size. + * + * @param {number} [width] - The width of the app. + * @param {number} [height] - The height of the app. + */ + layout(width, height) { + + } + + /** + * Draws the display tree of the app. Typically this can be delegated + * to the layout method. + * + */ + draw() { + this.layout(this.width, this.height); + } + + /* + * Run the application. Override this method with everything + * that is needed to maintain your App, e.g. setup calls, main loops, etc. + * + */ + run() { + return this + } + + /* + * Overwrite this factory method if your application needs a special + * scene object. + * + * @returns {PIXI.Container} - A new PIXI Container for use as a scene. + */ + sceneFactory() { + return new PIXI.Container() + } + + /** + * Adds the display of the frames per second to the renderer in the upper left corner. + * + * @return {PIXIApp} - Returns the PIXIApp for chaining. + */ + addFpsDisplay() { + const fpsDisplay = new FpsDisplay(this); + this.stage.addChild(fpsDisplay); + + return this + } + + /** + * Returns the size of the renderer as an object with the keys width and height. + * + * @readonly + * @member {object} + */ + get size() { + return { width: this.width, height: this.height } + } + + /** + * Returns the center of the renderer as an object with the keys x and y. + * + * @readonly + * @member {object} + */ + get center() { + return { x: this.width / 2, y: this.height / 2 } + } + + /** + * Resizes the renderer to fit into the window or given width and height. + * + * @param {object} [event] - The event. + * @param {object=} [opts={}] - The event. + * @param {number} [opts.width=window.innerWidth] - The width of the app to resize to. + * @param {number} [opts.height=window.innerHeight] - The height of the app to resize to. + * @return {PIXIApp} - Returns the PIXIApp for chaining. + */ + resize(event, { width = window.innerWidth, height = window.innerHeight } = {}) { + this.width = width; + this.height = height; + this.expandRenderer(); + this.layout(width, height); + //console.log("App.resize", width, height, window.innerWidth, window.innerHeight ) + // if (this.scene) { + // console.log("gl.drawingBufferWidth", this.renderer.view.getContext('webgl').drawingBufferWidth) + // console.log("scene", this.scene.scale, this.renderer, this.renderer.autoResize, this.renderer.resolution) + // } + return this + } + + /** + * @todo Write the documentation. + * + * @private + */ + monkeyPatchPixiMapping() { + if (this.originalMapPositionToPoint === null) { + let interactionManager = this.renderer.plugins.interaction; + this.originalMapPositionToPoint = interactionManager.mapPositionToPoint; + interactionManager.mapPositionToPoint = (point, x, y) => { + return this.fixedMapPositionToPoint(point, x, y) + }; + } + } + + /** + * In some browsers the canvas is distorted if the screen resolution and + * overall size of the canvas exceeds the internal limits (e.g. 4096 x 4096 pixels). + * To compensate these distortions we need to fix the mapping to the actual + * drawing buffer coordinates. + * @private + * @param {any} local + * @param {number} x + * @param {number} y + * @return {} interactionManager.mapPositionToPoint + */ + fixedMapPositionToPoint(local, x, y) { + let resolution = this.renderer.resolution; + let interactionManager = this.renderer.plugins.interaction; + let extendWidth = 1.0; + let extendHeight = 1.0; + let dy = 0; + let canvas = this.renderer.view; + let context = canvas.getContext('webgl'); + + if (context !== null && (context.drawingBufferWidth < canvas.width || + context.drawingBufferHeight < canvas.height)) { + extendWidth = context.drawingBufferWidth / canvas.width; + extendHeight = context.drawingBufferHeight / canvas.height; + //dx = wantedWidth - context.drawingBufferWidth + dy = (canvas.height - context.drawingBufferHeight) / resolution; + } + x *= extendWidth; + y *= extendHeight; + return this.originalMapPositionToPoint.call(interactionManager, local, x, y + dy) + } + + /** + * Expand the renderer step-wise on resize. + * + * @param {number} [expand] - The amount of additional space for the renderer [px]. + * @return {boolean} true if the renderer was resized. + */ + expandRenderer(expand = 256) { + let renderer = this.renderer; + let resolution = this.renderer.resolution; + let ww = this.width; + let hh = this.height; + let sw = this.screen.width; + let sh = this.screen.height; + if (ww > sw || hh > sh) { + //console.log('App.expandRenderer') + renderer.resize(ww + expand, hh + expand); + return true + } + + renderer.resize(ww, hh); + return false + } + + /** + * Set the loading progress of the application. If called for the first time, display the progress bar. + * + * @param {number} [value] - Should be a value between 0 and 100. If 100, the progress bar will disappear. + * @return {PIXIApp|Progress} The PixiApp object for chaining or the Progress object when the method was + * called without a parameter. + */ + progress(value) { + + if (typeof value === 'undefined') { + return this._progress + } + + this._progress.visible = true; + this._progress.progress = value; + + return this + } + + /** + * Opens a new Modal object binded to this app. + * + * @param {object} [opts] - An options object for the Modal object. + * @return {Modal} Returns the Modal object. + */ + modal(opts = {}) { + + let modal = new Modal(Object.assign({ theme: this.theme }, opts, { app: this })); + this.scene.addChild(modal); + + return modal + } + + /** + * Opens a new Message object binded to this app. + * + * @param {object} [opts] - An options object for the Message object. + * @return {Message} Returns the Message object. + */ + message(opts = {}) { + + let message = new Message(Object.assign({ theme: this.theme }, opts, { app: this })); + this.scene.addChild(message); + + return message + } + + /** + * Loads sprites, e.g. images into the PIXI TextureCache. + * + * @param {string|string[]} resources - A String or an Array of urls to the images to load. + * @param {function} [loaded] - A callback which is executed after all resources has been loaded. + * Receives one paramter, a Map of sprites where the key is the path of the image which was + * loaded and the value is the PIXI.Sprite object. + * @param {object} [opts] - An options object for more specific parameters. + * @param {boolean} [opts.resolutionDependent=true] - Should the sprites be loaded dependent of the + * renderer resolution? + * @param {boolean} [opts.progress=false] - Should a progress bar display the loading status? + * @return {PIXIApp} The PIXIApp object for chaining. + */ + loadSprites(resources, loaded = null, { resolutionDependent = true, progress = false } = {}) { + + this.loadTextures(resources, textures => { + + let sprites = new Map(); + + for (let [key, texture] of textures) { + sprites.set(key, new PIXI.Sprite(texture)); + } + + if (loaded) { + loaded.call(this, sprites); + } + + }, { resolutionDependent, progress }); + + return this + } + + /** + * Loads textures, e.g. images into the PIXI TextureCache. + * + * @param {string|string[]} resources - A String or an Array of urls to the images to load. + * @param {function} [loaded] - A callback which is executed after all resources has been loaded. + * Receives one paramter, a Map of textures where the key is the path of the image which was + * loaded and the value is the PIXI.Texture object. + * @param {object} [opts] - An options object for more specific parameters. + * @param {boolean} [opts.resolutionDependent=true] - Should the textures be loaded dependent of the + * renderer resolution? + * @param {boolean} [opts.progress=false] - Should a progress bar display the loading status? + * @return {PIXIApp} The PIXIApp object for chaining. + */ + loadTextures(resources, loaded = null, { resolutionDependent = true, progress = false } = {}) { + + if (!Array.isArray(resources)) { + resources = [resources]; + } + + const loader = this.loader; + + for (let resource of resources) { + + if (!loader.resources[resource]) { + + if (resolutionDependent) { + let resolution = Math.round(this.renderer.resolution); + switch (resolution) { + case 2: + loader.add(resource, resource.replace(/\.([^.]*)$/, '@2x.$1')); + break + case 3: + loader.add(resource, resource.replace(/\.([^.]*)$/, '@3x.$1')); + break + default: + loader.add(resource); + break + } + } else { + loader.add(resource); + } + } + } + + if (progress) { + loader.on('progress', e => { + this.progress(e.progress); + }); + } + + loader.load((loader, resources) => { + const textures = new Map(); + + for (let key in resources) { + textures.set(key, resources[key].texture); + } + + if (loaded) { + loaded.call(this, textures); + } + }); + + return this + } + + /** + * Queries the GraphQL endpoint. + * + * @param {string} [query] - The GraphQL query string. + * @param {object} [opts={}] - An options object. The following options can be set: + * http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient.query + * @return {Promise} Returns a Promise which is either resolved with the resulting data or + * rejected with an error. + */ + query(query, opts = {}) { + + if (typeof query === 'string') { + opts = Object.assign({}, opts, { query }); + } else { + opts = Object.assign({}, query); + } + + opts.query = opts.query.trim(); + + if (!opts.query.startsWith('query')) { + if (opts.query.startsWith('{')) { + opts.query = `query ${opts.query}`; + } else { + opts.query = `query {${opts.query}}`; + } + } + + opts.query = gql(opts.query); + + return this.apolloClient.query(opts) + } + + /** + * Mutate the GraphQL endpoint. + * + * @param {string} [mutation] - The GraphQL mutation string. + * @param {object} [opts={}] - An options object. The following options can be set: + * http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient.mutate + * @return {Promise} Returns a Promise which is either resolved with the resulting data or + * rejected with an error. + */ + mutate(mutation, opts = {}) { + + if (typeof mutation === 'string') { + opts = Object.assign({}, opts, { mutation }); + } else { + opts = Object.assign({}, mutation); + } + + opts.mutation = opts.mutation.trim(); + + if (!opts.mutation.startsWith('mutation')) { + if (opts.mutation.startsWith('{')) { + opts.mutation = `mutation ${opts.mutation}`; + } else { + opts.mutation = `mutation {${opts.mutation}}`; + } + } + + opts.mutation = gql(opts.mutation); + + return this.apolloClient.mutate(opts) + } + + /** + * Subscribe the GraphQL endpoint. + * + * @param {string} [subscription] - The GraphQL subscription. + * @param {object} [opts={}] - An options object. The following options can be set: + * http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient.query + * @return {Promise} Returns a Promise which is either resolved with the resulting data or + * rejected with an error. + */ + subscribe(subscription, opts = {}) { + + if (typeof subscription === 'string') { + opts = Object.assign({}, opts, { subscription }); + } else { + opts = Object.assign({}, subscription); + } + + opts.subscription = opts.subscription.trim(); + + if (!opts.subscription.startsWith('subscription')) { + if (opts.subscription.startsWith('{')) { + opts.subscription = `subscription ${opts.subscription}`; + } else { + opts.subscription = `subscription {${opts.subscription}}`; + } + } + + opts.query = gql(opts.subscription); + + delete opts.subscription; + + return this.apolloClient.subscribe(opts) + } + + /** + * Supports the page as a global coordinate system and converts browser page coordinates + * to local DisplayObject coordinates. + * + * @param {DisplayObject} displayObject - The PIXI displayObject. + * @param {number} x - The x coordinate. + * @param {number} y - The y coordinate. + * + * @return {PIXI.Point} Returns a PIXI.Point. + */ + + convertPointFromPageToNode(displayObject, x, y) { + let resolution = this.renderer.resolution; + console.log("resolution", resolution); + let pixiGlobal = window.convertPointFromPageToNode(app.view, x, y); + pixiGlobal.x /= resolution; + pixiGlobal.y /= resolution; + return displayObject.toLocal(new PIXI.Point(pixiGlobal.x, pixiGlobal.y)) + } + + /** + * Supports the page as a global coordinate system and converts local DisplayObject coordinates + * to browser page coordinates. + * + * @param {DisplayObject} displayObject - The PIXI displayObject. + * @param {number} x - The x coordinate. + * @param {number} y - The y coordinate. + * + * @return {Point} Returns a DOM Point. + */ + + convertPointFromNodeToPage(displayObject, x, y) { + let resolution = this.renderer.resolution; + let pixiGlobal = displayObject.toGlobal(new PIXI.Point(x, y)); + pixiGlobal.x *= resolution; + pixiGlobal.y *= resolution; + // console.log("app.convertPointFromNodeToPage", pixiGlobal) + return window.convertPointFromNodeToPage(app.view, pixiGlobal.x, pixiGlobal.y) + } + } + + /** + * The class fpsdisplay shows in the upper left corner + * of the renderer the current image refresh rate. + * + * @private + * @class + * @extends PIXI.Graphics + * @see {@link http://pixijs.download/dev/docs/PIXI.Graphics.html|PIXI.Graphics} + */ + class FpsDisplay extends PIXI.Graphics { + + /** + * Creates an instance of a FpsDisplay. + * + * @constructor + * @param {PIXIApp} app - The PIXIApp where the frames per second should be displayed. + */ + constructor(app) { + + super(); + + this.app = app; + + this.lineStyle(3, 0x434f4f, 1) + .beginFill(0x434f4f, .6) + .drawRoundedRect(0, 0, 68, 32, 5) + .endFill() + .position.set(20, 20); + + this.text = new PIXI.Text(this.fps, new PIXI.TextStyle({ + fontFamily: 'Arial', + fontSize: 14, + fontWeight: 'bold', + fill: '#f6f6f6', + stroke: '#434f4f', + strokeThickness: 3 + })); + this.text.position.set(6, 6); + + this.addChild(this.text); + + this.refreshFps(); + + window.setInterval(this.refreshFps.bind(this), 1000); + } + + /** + * Refreshes fps numer. + * + * @return {PIXIApp} Returns the PIXIApp object for chaining. + */ + refreshFps() { + this.text.text = `${(this.app.ticker.FPS).toFixed(1)} fps`; + + return this + } + } + + /** + * A Gaussian blur filter. With this filter, you can blur an area of a PIXI.DisplayObject. This cannot + * be done with the PIXI.filters.BlurFilter (when you use the PIXI.filters.BlurFilter with + * an filter area, all pixels outside of the area are not displayed). Attention: The area of + * the filter is always in global scope, NOT relative to the PIXI.DisplayObject the filter + * is assigned to! + * + * @example + * // Create the app + * const app = new PIXIApp({ + * view: canvas, + * width: 480, + * height: 270, + * transparent: false + * }).setup().run() + * + * // Add a video sprite + * const sprite = new PIXI.Sprite(PIXI.Texture.fromVideo("assets/blurfilter.mp4")) + * sprite.width = app.size.width + * sprite.height = app.size.height + * app.scene.addChild(sprite) + * + * // Create the filter and assign it to the scene + * const blurFilter = new BlurFilter(new PIXI.Rectangle(20, 20, 80, 60)) + * app.scene.filters = [blurFilter] + * + * @class + * @extends PIXI.Filter + * @param {PIXI.Rectangle|PIXI.Circle|PIXI.DisplayObject} shape The area where the blur effect should be applied to. Relative to the + * canvas, NOT relative to the PIXI.DisplayObject where the blur effect is assigned to! + * @param {number} [blur=50] The strength of the blur. + */ + class BlurFilter extends PIXI.Filter { + + constructor(shape, blur = 50) { + super(); + + const normalized = this.normalize(shape); + + this.tiltShiftXFilter = new TiltShiftXFilter(normalized, blur); + this.tiltShiftYFilter = new TiltShiftYFilter(normalized, blur); + } + + apply(filterManager, input, output) { + let renderTarget = filterManager.getRenderTarget(true); + this.tiltShiftXFilter.apply(filterManager, input, renderTarget); + this.tiltShiftYFilter.apply(filterManager, renderTarget, output); + filterManager.returnRenderTarget(renderTarget); + } + + /** + * The strength of the blur. + * + * @member {number} + */ + get blur() { + return this.tiltShiftXFilter.blur + } + set blur(value) { + this.tiltShiftXFilter.blur = this.tiltShiftYFilter.blur = value; + } + + /** + * The blur shape. + * + * @member {PIXI.Rectangle|PIXI.Circle|PIXI.DisplayObject} + */ + get shape() { + return this.tiltShiftXFilter.shape + } + set shape(value) { + this.tiltShiftXFilter.shape = this.tiltShiftYFilter.shape = this.normalize(value); + } + + /** + * + * @private + * @param {PIXI.Rectangle|PIXI.Circle|PIXI.DisplayObject} value + * @returns {Object} + */ + normalize(value) { + + let shape = null; + + if (value instanceof PIXI.Circle) { + shape = {type: 'circle', x: value.x, y: value.y, r: value.radius}; + } else if (value instanceof PIXI.Rectangle) { + shape = {type: 'rectangle', x: value.x, y: value.y, width: value.width, height: value.height}; + } else { + const bounds = value.getBounds(); + shape = {type: 'rectangle', x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height}; + } + + return shape + } + } + + /** + * A TiltShiftAxisFilter. + * + * @class + * @extends PIXI.Filter + * @abstract + * @private + */ + class TiltShiftAxisFilter extends PIXI.Filter { + + constructor(shape, blur){ + + const vertex = ` + attribute vec2 aVertexPosition; + attribute vec2 aTextureCoord; + + uniform mat3 projectionMatrix; + + varying vec2 vTextureCoord; + + void main(void) { + gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + vTextureCoord = aTextureCoord; + } + `; + + const fragment = ` + varying vec2 vTextureCoord; + + uniform vec4 filterArea; + uniform sampler2D uSampler; + uniform int shape; + uniform vec4 rectangle; + uniform vec3 circle; + uniform float blur; + uniform vec2 delta; + uniform vec2 texSize; + + float random(vec3 scale, float seed) { + return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); + } + + void main(void) { + // textureCoord to pixelCoord + vec2 pixelCoord = vTextureCoord * filterArea.xy - vec2(4.0, 4.0); // FIXME: There's a shift of 4 * 4 pixels, don't know why... + + bool inside = false; + + if (shape == 1) { + inside = distance(pixelCoord, circle.xy) <= circle.z; + } else if (shape == 2) { + inside = pixelCoord.x >= rectangle.x && pixelCoord.x <= rectangle.z && pixelCoord.y >= rectangle.y && pixelCoord.y <= rectangle.w; + } + + if (inside) { + vec4 color = vec4(0.0); + float total = 0.0; + + float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); + + for (float t = -30.0; t <= 30.0; t++) { + float percent = (t + offset - 0.5) / 30.0; + float weight = 1.0 - abs(percent); + vec4 sample = texture2D(uSampler, vTextureCoord + delta / texSize * percent * blur); + sample.rgb *= sample.a; + color += sample * weight; + total += weight; + } + + gl_FragColor = color / total; + gl_FragColor.rgb /= gl_FragColor.a + 0.00001; + } else { + gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)); + } + } + `; + + super(vertex, fragment); + + if (shape.type === 'circle') { + this.uniforms.shape = 1; + this.uniforms.circle = [shape.x, shape.y, shape.r]; + } else { + this.uniforms.shape = 2; + this.uniforms.rectangle = [shape.x, shape.y, shape.x + shape.width, shape.y + shape.height]; + } + this.uniforms.blur = blur; + this.uniforms.delta = new PIXI.Point(0, 0); + this.uniforms.texSize = new PIXI.Point(480, 270); + + this.updateDelta(); + } + + /** + * The strength of the blur. + * + * @member {number} + * @memberof PIXI.filters.TiltShiftAxisFilter# + */ + get blur() { + return this.uniforms.blur + } + set blur(value) { + this.uniforms.blur = value; + } + + /** + * The blur shape. + * + * @member {PIXI.Rectangle} + * @memberof PIXI.filters.TiltShiftAxisFilter# + */ + get shape() { + if (this.uniforms.shape === 1) { + const circle = this.uniforms.circle; + return new PIXI.Circle(circle[0], circle[1], circle[2]) + } else { + const rectangle = this.uniforms.rectangle; + return new PIXI.Rectangle(rectangle[0], rectangle[1], rectangle[2], rectangle[3]) + } + } + set shape(value) { + if (value.type === 'circle') { + this.uniforms.shape = 1; + this.uniforms.circle = [value.x, value.y, value.r]; + } else { + this.uniforms.shape = 2; + this.uniforms.rectangle = [value.x, value.y, value.x + value.width, value.y + value.height]; + } + } + } + + /** + * A TiltShiftXFilter. + * + * @class + * @extends PIXI.TiltShiftAxisFilter + * @private + */ + class TiltShiftXFilter extends TiltShiftAxisFilter { + /** + * Updates the filter delta values. + */ + updateDelta() { + this.uniforms.delta.x = 0.1; + this.uniforms.delta.y = 0; + } + } + + /** + * A TiltShiftYFilter. + * + * @class + * @extends PIXI.TiltShiftAxisFilter + * @private + */ + class TiltShiftYFilter extends TiltShiftAxisFilter { + /** + * Updates the filter delta values. + */ + updateDelta() { + this.uniforms.delta.x = 0; + this.uniforms.delta.y = 0.1; + } + } + + // In order to test this interface implementation run jsc interface.js + + class Interface { + // Abstract interface that should be extended in interface subclasses. + // By convention all interfaces should start with an upper 'I' + + static implementationError(klass) { + let interfaceKeys = Reflect.ownKeys(this.prototype); + let classKeys = Reflect.ownKeys(klass.prototype); + for(let key of interfaceKeys) { + let interfaceDesc = this.prototype[key]; + let classDesc = klass.prototype[key]; + if (typeof(classDesc) == 'undefined') + return 'Missing ' + key + } + return null + } + + static implementedBy(klass) { + // In the first step only checks whether the methods of this + // interface are all implemented by the given class + let error = this.implementationError(klass); + return error == null + } + + // TODO: Specify optional methods + // static optionalMethods() { + // return [this.onMouseWheel] + // } + } + + /* globals Hammer, propagating */ + + /** Interaction patterns + + See interaction.html for explanation + */ + + class IInteractionTarget extends Interface { + capture(event) { + return typeof true + } + + onStart(event, interaction) { } + onMove(event, interaction) { } + onEnd(event, interaction) { } + + onMouseWheel(event) { } + } + + class IInteractionMapperTarget extends Interface { + capture(event) { + return typeof true + } + + findTarget(event, local, global) { + return IInteractionTarget + } + } + + class PointMap extends MapProxy { + // Collects touch points, mouse coordinates, etc. as key value pairs. + // Keys are pointer and touch ids, the special "mouse" key. + // Values are points, i.e. all objects with numeric x and y properties. + constructor(points = {}) { + super(); + for (let key in points) { + this.set(key, points[key]); + } + } + + toString() { + let points = []; + for (let key of this.keys()) { + let value = this.get(key); + points.push(`${key}:{x:${value.x}, y:${value.y}}`); + } + let attrs = points.join(', '); + return `[PointMap ${attrs}]` + } + + clone() { + let result = new PointMap(); + for (let key of this.keys()) { + let value = this.get(key); + result.set(key, { x: value.x, y: value.y }); + } + return result + } + + keyOf(value) { + for (let key of this.keys()) { + let p = this.get(key); + if (p.x == value.x && p.y == value.y) { + return key + } + } + return null + } + + firstKey() { + for (let key of this.keys()) { + return key + } + return null + } + + first() { + for (let key of this.keys()) { + return this.get(key) + } + return null + } + + farthests() { + if (this.size == 0) { + return null + } + let pairs = []; + for (let key of this.keys()) { + let p = this.get(key); + p.key = key; + for (let k of this.keys()) { + let q = this.get(k); + q.key = k; + pairs.push([p, q]); + } + } + let sorted = pairs.sort((a, b) => { + return Points.distance(b[0], b[1]) - Points.distance(a[0], a[1]) + }); + return sorted[0] + } + + mean() { + if (this.size == 0) { + return null + } + let x = 0.0, + y = 0.0; + for (let p of this.values()) { + x += p.x; + y += p.y; + } + return { x: x / this.size, y: y / this.size } + } + } + + class InteractionDelta { + constructor(x, y, zoom, rotate, about) { + this.x = x; + this.y = y; + this.zoom = zoom; + this.rotate = rotate; + this.about = about; + } + + toString() { + let values = []; + for (let key of Object.keys(this)) { + let value = this[key]; + if (key == 'about') { + values.push(`${key}:{x:${value.x}, y:${value.y}}`); + } else { + values.push(`${key}:${value}`); + } + } + let attrs = values.join(', '); + return `[InteractionDelta ${attrs}]` + } + } + + class InteractionPoints { + constructor(parent = null) { + this.parent = parent; + this.current = new PointMap(); + this.previous = new PointMap(); + this.start = new PointMap(); + this.ended = new PointMap(); + this.timestamps = new Map(); + } + + moved(key) { + let current = this.current.get(key); + let previous = this.previous.get(key); + return Points.subtract(current, previous) + } + + move() { + let current = this.current.mean(); + let previous = this.previous.mean(); + return Points.subtract(current, previous) + } + + /** + * Computes the delta between previous and current angles. Corrects + * value that are larger than 45° + * @param {*} a + * @param {*} b + * @returns delta + */ + diffAngle(a, b) { + let alpha = Math.atan2(Math.sin(a - b), Math.cos(a - b)); + if (Math.abs(alpha) > Math.PI / 4) { + alpha -= Math.PI; + } + return alpha + } + + /** + * Computes the delta between interaction points at t and t+1. + * + * @returns InteractionDelta + * @memberof InteractionPoints + */ + delta() { + let csize = this.current.size; + let psize = this.previous.size; + if (csize >= 2 && csize == psize) { + // Reduce to the two farthests points + let current = this.current.farthests(); + + let c1 = current[0]; + let c2 = current[1]; + + let p1 = this.previous.get(c1.key); + let p2 = this.previous.get(c2.key); + + //let p1 = previous[0] + //let p2 = previous[1] + + let d1 = Points.subtract(c1, p1); + let d2 = Points.subtract(c2, p2); + let cm = Points.mean(c1, c2); + //let pm = Points.mean(p1, p2) + // UO: Using the mean lead to jumps between time slices with 3 and 2 fingers + // We use the mean of deltas instead + let delta = Points.mean(d1, d2); //Points.subtract(cm, pm) + let zoom = 1.0; + let distance1 = Points.distance(p1, p2); + let distance2 = Points.distance(c1, c2); + if (distance1 != 0 && distance2 != 0) { + zoom = distance2 / distance1; + } + let currentAngle = Points.angle(c1, c2); + let previousAngle = Points.angle(p1, p2); + let alpha = this.diffAngle(currentAngle, previousAngle); + return new InteractionDelta(delta.x, delta.y, zoom, alpha, cm) + } else if (csize == 1 && psize == 1 && this.current.firstKey() == this.previous.firstKey()) { + // We need to ensure that the keys are the same + let current = this.current.first(); + let previous = this.previous.first(); + let delta = Points.subtract(current, previous); + return new InteractionDelta(delta.x, delta.y, 1.0, 0.0, current) + } + return null + } + + started(key, point) { + this.current.set(key, point); + this.start.set(key, point); + this.previous.set(key, point); + this.timestamps.set(key, performance.now()); + } + + update(key, point) { + // Returns true iff the key is new + this.current.set(key, point); + if (!this.start.has(key)) { + this.start.set(key, point); + this.previous.set(key, point); + this.timestamps.set(key, performance.now()); + return true + } + return false + } + + updatePrevious() { + for (let key of this.current.keys()) { + this.previous.set(key, this.current.get(key)); + } + } + + stop(key, point) { + if (this.current.has(key)) { + this.current.delete(key); + this.previous.delete(key); + this.ended.set(key, point); + } + } + + finish(key, point) { + this.current.delete(key); + this.previous.delete(key); + this.start.delete(key); + this.timestamps.delete(key); + this.ended.delete(key); + } + + isFinished() { + return this.current.size == 0 + } + + isNoLongerTwoFinger() { + return this.previous.size > 1 && this.current.size < 2 + } + + isTap(key) { + return this.parent.isTap(key) + } + + isDoubleTap(key) { + return this.parent.isDoubleTap(key) + } + + isLongPress(key) { + return this.parent.isLongPress(key) + } + } + + class Interaction extends InteractionPoints { + constructor(tapDistance = 10, tapDuration = 250.0, longPressTime = 500.0) { + super(); + this.tapDistance = tapDistance; + this.tapCounts = new Map(); + this.tapPositions = new Map(); + this.tapTimestamps = new Map(); + this.tapDuration = tapDuration; + this.longPressTime = longPressTime; + this.targets = new Map(); + this.subInteractions = new Map(); // target:Object : InteractionPoints + } + + stop(key, point) { + super.stop(key, point); + for (let points of this.subInteractions.values()) { + points.stop(key, point); + } + } + + addTarget(key, target) { + this.targets.set(key, target); + this.subInteractions.set(target, new InteractionPoints(this)); + } + + removeTarget(key) { + let target = this.targets.get(key); + this.targets.delete(key); + // Only remove target if no keys are refering to the target + let remove = true; + for (let t of this.targets.values()) { + if (target === t) { + remove = false; + } + } + if (remove) { + this.subInteractions.delete(target); + } + } + + finish(key, point) { + super.finish(key, point); + this.removeTarget(key); + } + + mapInteraction(points, aspects, mappingFunc) { + // Map centrally registered points to target interactions + // Returns an array of [target, updated subInteraction] pairs + let result = new Map(); + for (let key in points) { + if (this.targets.has(key)) { + let target = this.targets.get(key); + if (this.subInteractions.has(target)) { + let interaction = this.subInteractions.get(target); + for (let aspect of aspects) { + let pointMap = this[aspect]; + let point = pointMap.get(key); + let mapped = mappingFunc(point, target); + interaction[aspect].set(key, mapped); + } + result.set(target, interaction); + } + } + } + return result + } + + registerTap(key, point) { + if (this.tapCounts.has(key)) { + let count = this.tapCounts.get(key); + this.tapCounts.set(key, count+1); + } + else { + this.tapCounts.set(key, 1); + } + this.tapPositions.set(key, point); + this.tapTimestamps.set(key, performance.now()); + } + + unregisterTap(key) { + this.tapCounts.delete(key); + this.tapPositions.delete(key); + this.tapTimestamps.delete(key); + } + + isTap(key) { + let ended = this.ended.get(key); + let start = this.start.get(key); + if ( + start && + ended && + Points.distance(ended, start) < this.tapDistance + ) { + let t1 = this.timestamps.get(key); + let tookLong = performance.now() > t1 + this.longPressTime; + if (tookLong) { + return false + } + return true + } + return false + } + + isDoubleTap(key) { + let ended = this.ended.get(key); + if (this.tapCounts.has(key) && this.tapCounts.get(key) > 2) { + this.unregisterTap(key); + } + if (this.tapPositions.has(key)) { + let pos = this.tapPositions.get(key); + if (Points.distance(ended, pos) > this.tapDistance) { + this.unregisterTap(key); + } + } + if (this.tapTimestamps.has(key) && performance.now() > this.tapTimestamps.get(key) + this.tapDuration) { + //console.log("tap too long") + this.unregisterTap(key); + } + let result = false; + if (this.isTap(key)) { + + this.registerTap(key, ended); + result = this.tapCounts.get(key) == 2; + } + else { + this.unregisterTap(key); + } + //console.log("isDoubleTap", this.tapCounts.get(key), result) + return result + } + + isAnyTap() { + for (let key of this.ended.keys()) { + if (this.isTap(key)) return true + } + return false + } + + isLongPress(key) { + let ended = this.ended.get(key); + let start = this.start.get(key); + if ( + start && + ended && + Points.distance(ended, start) < this.tapDistance + ) { + let t1 = this.timestamps.get(key); + let tookLong = performance.now() > t1 + this.longPressTime; + if (tookLong) { + return true + } + return false + } + return false + } + + isAnyLongPress() { + for (let key of this.ended.keys()) { + if (this.isLongPress(key)) return true + } + return false + } + + isStylus(key) { + return key === 'stylus' + } + } + + /** + * This class implements the main delegate functionality: All necessary event handlers are registered for the + * given element. Uses PointerEvents if available or TouchEvents on iOS. The fallback is on mouse events. + * Collects the events if the interaction target captures the start event (i.e. declares that + * the target wants the start event as well as all following move and end evcents.) + * + * @export + * @class InteractionDelegate + */ + class InteractionDelegate { + // Long press: http://stackoverflow.com/questions/1930895/how-long-is-the-event-onlongpress-in-the-android + // Stylus support: https://w3c.github.io/touch-events/ + + /** + * Creates an instance of InteractionDelegate. + * @param {any} element + * @param {any} target + * @param {any} [{ mouseWheelElement = null, useCapture = true, capturePointerEvents = true, debug = false }={}] + * @memberof InteractionDelegate + */ + constructor( + element, + target, + { mouseWheelElement = null, useCapture = true, capturePointerEvents = true, cancelOnWindowOut = true, debug = false } = {} + ) { + this.debug = debug; + this.interaction = new Interaction(); + this.element = element; + this.mouseWheelElement = mouseWheelElement || element; + this.target = target; + this.useCapture = useCapture; + this.capturePointerEvents = capturePointerEvents; + this.cancelOnWindowOut = cancelOnWindowOut; + this.setupInteraction(); + } + + setupInteraction() { + if (this.debug) { + let error = this.targetInterface.implementationError( + this.target.constructor + ); + if (error != null) { + throw new Error('Expected IInteractionTarget: ' + error) + } + } + this.setupTouchInteraction(); + this.setupMouseWheelInteraction(); + } + + get targetInterface() { + return IInteractionTarget + } + + setupTouchInteraction() { + let element = this.element; + let useCapture = this.useCapture; + if (window.PointerEvent) { + if (this.debug) console.log('Pointer API' + window.PointerEvent); + element.addEventListener( + 'pointerdown', + e => { + if (this.debug) console.log('pointerdown', e.pointerId); + if (this.capture(e)) { + if (this.capturePointerEvents) { + try { + element.setPointerCapture(e.pointerId); + } catch (e) { } + } + this.onStart(e); + } + }, + useCapture + ); + element.addEventListener( + 'pointermove', + e => { + if (this.debug) console.log('pointermove', e.pointerId, e.pointerType); + + if ( + e.pointerType == 'touch' || + (e.pointerType == 'mouse' && Events$1.isPointerDown(e)) + ) { + // this.capture(e) && + if (this.debug) + console.log('pointermove captured', e.pointerId); + this.onMove(e); + } + }, + useCapture + ); + element.addEventListener( + 'pointerup', + e => { + if (this.debug) console.log('pointerup'); + this.onEnd(e); + if (this.capturePointerEvents) { + try { + element.releasePointerCapture(e.pointerId); + } catch (e) { } + } + }, + useCapture + ); + element.addEventListener( + 'pointercancel', + e => { + if (this.debug) console.log('pointercancel'); + this.onEnd(e); + if (this.capturePointerEvents) + element.releasePointerCapture(e.pointerId); + }, + useCapture + ); + + if (!this.capturePointerEvents) { + element.addEventListener( + 'pointerleave', + e => { + if (this.debug) console.log('pointerleave'); + if (e.target == element) this.onEnd(e); + }, + useCapture + ); + } + + if (!this.capturePointerEvents) { + element.addEventListener( + 'pointerout', + e => { + if (this.debug) console.log('pointerout'); + if (e.target == element) this.onEnd(e); + }, + useCapture + ); + } + + if (this.cancelOnWindowOut) { + window.addEventListener( + 'pointerout', + e => { + if (e.target == element) { + this.onEnd(e); + } + }, + useCapture); + } + + } else if (window.TouchEvent) { + if (this.debug) console.log('Touch API'); + element.addEventListener( + 'touchstart', + e => { + if (this.debug) + console.log('touchstart', this.touchPoints(e)); + if (this.capture(e)) { + for (let touch of e.changedTouches) { + this.onStart(touch); + } + } + }, + useCapture + ); + element.addEventListener( + 'touchmove', + e => { + if (this.debug) + console.log('touchmove', this.touchPoints(e), e); + for (let touch of e.changedTouches) { + this.onMove(touch); + } + for (let touch of e.targetTouches) { + this.onMove(touch); + } + }, + useCapture + ); + element.addEventListener( + 'touchend', + e => { + if (this.debug) console.log('touchend', this.touchPoints(e)); + for (let touch of e.changedTouches) { + this.onEnd(touch); + } + }, + useCapture + ); + element.addEventListener( + 'touchcancel', + e => { + if (this.debug) + console.log( + 'touchcancel', + e.targetTouches.length, + e.changedTouches.length + ); + for (let touch of e.changedTouches) { + this.onEnd(touch); + } + }, + useCapture + ); + } else { + if (this.debug) console.log('Mouse API'); + + element.addEventListener( + 'mousedown', + e => { + if (this.debug) console.log('mousedown', e); + if (this.capture(e)) { + this.onStart(e); + } + }, + useCapture + ); + element.addEventListener( + 'mousemove', + e => { + // Dow we only use move events if the mouse is down? + // HOver effects have to be implemented by other means + // && Events.isMouseDown(e)) + + if (Events$1.isMouseDown(e)) { + if (this.debug) + console.log('mousemove', e); + this.onMove(e); + } + }, + useCapture + ); + element.addEventListener( + 'mouseup', + e => { + if (this.debug) console.log('mouseup', e); + this.onEnd(e); + }, + true + ); + + if (!this.capturePointerEvents) { + element.addEventListener( + 'mouseout', + e => { + if (e.target == element) { + this.onEnd(e); + console.warn("Shouldn't happen: mouseout ends interaction"); + } + + }, + useCapture + ); + } + if (this.cancelOnWindowOut) { + window.addEventListener( + 'mouseout', + e => { + if (e.target == element) { + this.onEnd(e); + } + }, + useCapture); + } + } + } + + isDescendant(parent, child) { + if (parent == child) return true + let node = child.parentNode; + while (node != null) { + if (node == parent) { + return true + } + node = node.parentNode; + } + return false + } + + touchPoints(event) { + let result = []; + for (let touch of event.changedTouches) { + result.push(this.extractPoint(touch)); + } + return result + } + + setupMouseWheelInteraction() { + this.mouseWheelElement.addEventListener( + 'mousewheel', + this.onMouseWheel.bind(this), + true + ); + this.mouseWheelElement.addEventListener( + 'DOMMouseScroll', + this.onMouseWheel.bind(this), + true + ); + } + + onMouseWheel(event) { + if (this.capture(event) && this.target.onMouseWheel) { + this.target.onMouseWheel(event); + } + } + + onStart(event) { + let extracted = this.extractPoint(event); + this.startInteraction(event, extracted); + this.target.onStart(event, this.interaction); + } + + onMove(event) { + let extracted = this.extractPoint(event, 'all'); + this.updateInteraction(event, extracted); + this.target.onMove(event, this.interaction); + this.interaction.updatePrevious(); + } + + onEnd(event) { + let extracted = this.extractPoint(event, 'changedTouches'); + this.endInteraction(event, extracted); + this.target.onEnd(event, this.interaction); + this.finishInteraction(event, extracted); + } + + /** + * Asks the target whether the event should be captured + * + * @param {any} event + * @returns {bool} + * @memberof InteractionDelegate + */ + capture(event) { + if (Events$1.isCaptured(event)) { + return false + } + let captured = this.target.capture(event); + return captured + } + + getPosition(event) { + return { x: event.clientX, y: event.clientY } + } + + extractPoint(event, touchEventKey = 'all') { + // 'targetTouches' + let result = {}; + switch (event.constructor.name) { + case 'MouseEvent': + let buttons = event.buttons || event.which; + if (buttons) result['mouse'] = this.getPosition(event); + break + case 'PointerEvent': + result[event.pointerId.toString()] = this.getPosition(event); + break + case 'Touch': + let id = + event.touchType === 'stylus' + ? 'stylus' + : event.identifier.toString(); + result[id] = this.getPosition(event); + break + // case 'TouchEvent': + // // Needs to be observed: Perhaps changedTouches are all we need. If so + // // we can remove the touchEventKey default parameter + // if (touchEventKey == 'all') { + // for(let t of event.targetTouches) { + // result[t.identifier.toString()] = this.getPosition(t) + // } + // for(let t of event.changedTouches) { + // result[t.identifier.toString()] = this.getPosition(t) + // } + // } + // else { + // for(let t of event.changedTouches) { + // result[t.identifier.toString()] = this.getPosition(t) + // } + // } + // break + default: + break + } + return result + } + + interactionStarted(event, key, point) { + // Callback: can be overwritten + } + + interactionEnded(event, key, point) { + // Callback: can be overwritten + } + + interactionFinished(event, key, point) { } + + startInteraction(event, extracted) { + for (let key in extracted) { + let point = extracted[key]; + this.interaction.started(key, point); + this.interactionStarted(event, key, point); + } + } + + updateInteraction(event, extracted) { + for (let key in extracted) { + let point = extracted[key]; + let updated = this.interaction.update(key, point); + if (updated) { + console.warn("new pointer in updateInteraction shouldn't happen", key); + this.interactionStarted(event, key, point); + } + } + } + + endInteraction(event, ended) { + for (let key in ended) { + let point = ended[key]; + this.interaction.stop(key, point); + this.interactionEnded(event, key, point); + } + } + + finishInteraction(event, ended) { + for (let key in ended) { + let point = ended[key]; + this.interaction.finish(key, point); + this.interactionFinished(event, key, point); + } + } + } + /** + * A special InteractionDelegate that maps events to specific parts of + * the interaction target. The InteractionTarget must implement a findTarget + * method that returns an object implementing the IInteractionTarget interface. + * + * If the InteractionTarget also implements a mapPositionToPoint method this + * is used to map the points to the local coordinate space of the the target. + * + * This makes it easier to lookup elements and relate events to local + * positions. + * + * @export + * @class InteractionMapper + * @extends {InteractionDelegate} + */ + class InteractionMapper$1 extends InteractionDelegate { + + constructor( + element, + target, + { tapDistance = 10, longPressTime = 500.0, useCapture = true, mouseWheelElement = null } = {} + ) { + super(element, target, { tapDistance, useCapture, longPressTime, mouseWheelElement }); + } + + get targetInterface() { + return IInteractionMapperTarget + } + + mapPositionToPoint(point, element = null) { + if (this.target.mapPositionToPoint) { + return this.target.mapPositionToPoint(point, element) + } + return point + } + + interactionStarted(event, key, point) { + if (this.target.findTarget) { + let local = this.mapPositionToPoint(point); + let found = this.target.findTarget(event, local, point); + if (found != null) { + this.interaction.addTarget(key, found); + } + } + } + + onMouseWheel(event) { + if (this.capture(event)) { + if (this.target.findTarget) { + let point = this.getPosition(event); + let local = this.mapPositionToPoint(point); + let found = this.target.findTarget(event, local, point); + if (found != null && found.onMouseWheel) { + found.onMouseWheel(event); + return + } + } + if (this.target.onMouseWheel) { + this.target.onMouseWheel(event); + } + } + } + + onStart(event) { + let extracted = this.extractPoint(event); + this.startInteraction(event, extracted); + let mapped = this.interaction.mapInteraction( + extracted, + ['current', 'start'], + this.mapPositionToPoint.bind(this) + ); + for (let [target, interaction] of mapped.entries()) { + target.onStart(event, interaction); + } + } + + onMove(event) { + let extracted = this.extractPoint(event, 'all'); + this.updateInteraction(event, extracted); + let mapped = this.interaction.mapInteraction( + extracted, + ['current', 'previous'], + this.mapPositionToPoint.bind(this) + ); + for (let [target, interaction] of mapped.entries()) { + target.onMove(event, interaction); + interaction.updatePrevious(); + } + this.interaction.updatePrevious(); + } + + onEnd(event) { + let extracted = this.extractPoint(event, 'changedTouches'); + this.endInteraction(event, extracted); + let mapped = this.interaction.mapInteraction( + extracted, + ['ended'], + this.mapPositionToPoint.bind(this) + ); + for (let [target, interaction] of mapped.entries()) { + target.onEnd(event, interaction); + } + this.finishInteraction(event, extracted); + } + + /** + * + * + * @static + * @param {string|array} types - An event type, an array of event types or event types seperated by a space sign. The following + * events are possible: + * pan, panstart, panmove, panend, pancancel, panleft, panright, panup, pandown + * pinch, pinchstart, pinchmove, pinchend, pinchcancel, pinchin, pinchout + * press, pressup + * rotate, rotatestart, rotatemove, rotateend, rotatecancel + * swipe, swipeleft, swiperight, swipeup, swipedown + * tap + * @param {HTMLElement|HTMLElement[]} elements - An HTML element or an array of HTML elements. + * @param {function} [cb] - The callback. A function which is executed after the event occurs. Receives the event object as the + * first paramter + * @param {object} [opts] - An options object. See the hammer documentation for more details. + */ + static on(types, elements, cb, opts = {}) { + opts = Object.assign({}, { + + }, opts); + + if (typeof Hammer === 'undefined') { + console.error('Hammer.js not found!'); + return this + } + + // convert to array + types = Array.isArray(types) ? types : types.split(/\s/); + if (elements instanceof NodeList || elements instanceof HTMLCollection) { + elements = Array.from(elements); + } + elements = Array.isArray(elements) ? elements : [elements]; + + for (let i = 0; i < types.length; i++) { + + const type = types[i].toLowerCase(); + + // list of hammer events + const useHammer = /^(tap|doubletap|press|pan|swipe|pinch|rotate).*$/.test(type); + + // if it is a hammer event + if (useHammer) { + + for (let j = 0; j < elements.length; j++) { + + // if(elements[j].tagName == "svg") return false; + + let hammer = new Hammer(elements[j], opts); + + if (window.propagating !== 'undefined') { + hammer = propagating(hammer); + } + + // recognizers + if (type.startsWith('pan')) { + hammer.get('pan').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts)); + } else if (type.startsWith('pinch')) { + hammer.get('pinch').set(Object.assign({ enable: true }, opts)); + } else if (type.startsWith('press')) { + hammer.get('press').set(opts); + } else if (type.startsWith('rotate')) { + hammer.get('rotate').set(Object.assign({ enable: true }, opts)); + } else if (type.startsWith('swipe')) { + hammer.get('swipe').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts)); + } else if (type.startsWith('tap')) { + hammer.get('tap').set(opts); + } + + hammer.on(type, event => { + cb(event); + }); + } + + } else { + + for (let j = 0; j < elements.length; j++) { + Hammer.on(elements[j], type, event => { + cb(event); + }); + } + } + } + + return this + } + } + + window.InteractionMapper = InteractionMapper$1; + + /** Report capabilities with guaranteed values. + */ + class Capabilities { + + /** Returns the browser userAgent. + @return {string} + */ + static get userAgent() { + return navigator.userAgent || 'Unknown Agent' + } + + /** Tests whether the app is running on a mobile device. + Implemented as a readonly attribute. + @return {boolean} + */ + static get isMobile() { + return (/Mobi/.test(navigator.userAgent)) + } + + /** Tests whether the app is running on a iOS device. + Implemented as a readonly attribute. + @return {boolean} + */ + static get isIOS() { + return (/iPad|iPhone|iPod/.test(navigator.userAgent)) && !window.MSStream + } + + /** Tests whether the app is running in a Safari environment. + See https://stackoverflow.com/questions/7944460/detect-safari-browser + Implemented as a readonly attribute. + @return {boolean} + */ + static get isSafari() { + return navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && navigator.userAgent && !navigator.userAgent.match('CriOS') + } + + /** + * Distincts if the app is running inside electron or not. + * + * source: https://discuss.atom.io/t/detect-electron-or-web-page-running/33180/3 + */ + static get isElectron() { + return typeof process != 'undefined' && process.versions && process.versions.electron !== undefined + } + + /** Returns the display resolution. Necessary for retina displays. + @return {number} + */ + static get devicePixelRatio() { + return window.devicePixelRatio || 1 + } + + /** Returns true if the device is a multi-touch table. This method is currently not universal usable and not sure! + @return {boolean} + */ + static get isMultiTouchTable() { + return Capabilities.devicePixelRatio > 2 && Capabilities.isMobile === false && /Windows/i.test(Capabilities.userAgent) + } + + /** Returns true if mouse events are supported + @return {boolean} + */ + static supportsMouseEvents() { + return typeof(window.MouseEvent) != 'undefined' + } + + /** Returns true if touch events are supported + @return {boolean} + */ + static supportsTouchEvents() { + return typeof(window.TouchEvent) != 'undefined' + } + + /** Returns true if pointer events are supported + @return {boolean} + */ + static supportsPointerEvents() { + return typeof(window.PointerEvent) != 'undefined' + } + + /** Returns true if DOM templates are supported + @return {boolean} + */ + static supportsTemplate() { + return 'content' in document.createElement('template'); + } + } + + /** Basic tests for Capabilities. + */ + class CapabilitiesTests { + + static testConfirm() { + let bool = confirm('Please confirm'); + document.getElementById('demo').innerHTML = (bool) ? 'Confirmed' : 'Not confirmed'; + } + + static testPrompt() { + let person = prompt('Please enter your name', 'Harry Potter'); + if (person != null) { + demo.innerHTML = + 'Hello ' + person + '! How are you today?'; + } + } + + static testUserAgent() { + let agent = 'User-agent: ' + Capabilities.userAgent; + user_agent.innerHTML = agent; + } + + static testDevicePixelRatio() { + let value = 'Device Pixel Ratio: ' + Capabilities.devicePixelRatio; + device_pixel_ratio.innerHTML = value; + } + + static testMultiTouchTable() { + let value = 'Is the device a multi-touch table? ' + Capabilities.isMultiTouchTable; + multi_touch_table.innerHTML = value; + } + + static testSupportedEvents() { + let events = []; + if (Capabilities.supportsMouseEvents()) { + events.push('MouseEvents'); + } + if (Capabilities.supportsTouchEvents()) { + events.push('TouchEvents'); + } + if (Capabilities.supportsPointerEvents()) { + events.push('PointerEvents'); + } + supported_events.innerHTML = 'Supported Events: ' + events.join(', '); + } + + static testAll() { + this.testUserAgent(); + this.testDevicePixelRatio(); + this.testMultiTouchTable(); + this.testSupportedEvents(); + } + } + + /* Optional global variables, needed in DocTests. */ + window.Capabilities = Capabilities; + window.CapabilitiesTests = CapabilitiesTests; + + /** + * A base class for scatter specific events. + * + * @constructor + * @param {name} String - The name of the event + * @param {target} Object - The target of the event + */ + class BaseEvent { + constructor(name, target) { + this.name = name; + this.target = target; + } + } + + // Event types + const START = 'onStart'; + const UPDATE = 'onUpdate'; + const END = 'onEnd'; + + /** + * A scatter event that describes how the scatter has changed. + * + * @constructor + * @param {target} Object - The target scatter of the event + * @param {optional} Object - Optional parameter + */ + class ScatterEvent extends BaseEvent { + constructor( + target, + { + translate = { x: 0, y: 0 }, + scale = null, + rotate = 0, + about = null, + fast = false, + type = null + } = {} + ) { + super('scatterTransformed', { target: target }); + this.translate = translate; + this.scale = scale; + this.rotate = rotate; + this.about = about; + this.fast = fast; + this.type = type; + } + + toString() { + return ( + "Event('scatterTransformed', scale: " + + this.scale + + ' about: ' + + this.about.x + + ', ' + + this.about.y + + ')' + ) + } + } + + /** + * A scatter resize event that describes how the scatter has changed. + * + * @constructor + * @param {target} Object - The target scatter of the event + * @param {optional} Object - Optional parameter + */ + class ResizeEvent extends BaseEvent { + constructor(target, { width = 0, height = 0 } = {}) { + super('scatterResized', { width: width, height: height }); + this.width = width; + this.height = height; + } + + toString() { + return ( + 'Event(scatterResized width: ' + + this.width + + 'height: ' + + this.height + + ')' + ) + } + } + + /** + * A abstract base class that implements the throwable behavior of a scatter + * object. + * + * @constructor + */ + class Throwable { + constructor({ + movableX = true, + movableY = true, + throwVisibility = 44, + throwDamping = 0.95, + autoThrow = true, + onThrowFinished = null + } = {}) { + this.movableX = movableX; + this.movableY = movableY; + this.throwVisibility = throwVisibility; + this.throwDamping = throwDamping; + this.autoThrow = autoThrow; + this.velocities = []; + this.velocity = null; + this.timestamp = null; + this.onThrowFinished = onThrowFinished; + //console.log("onThrowFinished", onThrowFinished) + } + + observeVelocity() { + this.lastframe = performance.now(); + } + + addVelocity(delta, buffer = 5) { + let t = performance.now(); + let dt = t - this.lastframe; + this.lastframe = t; + if (dt > 0) { + // Avoid division by zero errors later on + let velocity = { t: t, dt: dt, dx: delta.x, dy: delta.y }; + this.velocities.push(velocity); + while (this.velocities.length > buffer) { + this.velocities.shift(); + } + } + } + + meanVelocity(milliseconds = 30) { + this.addVelocity({ x: 0, y: 0 }); + let sum = { x: 0, y: 0 }; + let count = 0; + let t = 0; + for (let i = this.velocities.length - 1; i > 0; i--) { + let v = this.velocities[i]; + t += v.dt; + let nv = { x: v.dx / v.dt, y: v.dy / v.dt }; + sum = Points.add(sum, nv); + count += 1; + if (t > milliseconds) { + break + } + } + if (count === 0) return sum // empty vector + return Points.multiplyScalar(sum, 1 / count) + } + + killAnimation() { + this.velocity = null; + this.velocities = []; + } + + startThrow() { + this.velocity = this.meanVelocity(); + if (this.velocity != null) { + // Call next velocity to ansure that specializations + // that use keepOnStage are called + this.velocity = this.nextVelocity(this.velocity); + if (this.autoThrow) this.animateThrow(performance.now()); + } else { + this.onDragComplete(); + } + } + + animateThrow(time) { + if (this.velocity != null) { + let t = performance.now(); + let dt = t - this.lastframe; + this.lastframe = t; + // console.log("animateThrow", dt) + let next = this.nextVelocity(this.velocity); + let prevLength = Points.length(this.velocity); + let nextLength = Points.length(next); + if (nextLength > prevLength) { + let factor = nextLength / prevLength; + next = Points.multiplyScalar(next, 1 / factor); + console.log('Prevent acceleration', factor, this.velocity, next); + } + this.velocity = next; + let d = Points.multiplyScalar(this.velocity, dt); + this._move(d); + + this.onDragUpdate(d); + if (dt == 0 || this.needsAnimation()) { + requestAnimationFrame(this.animateThrow.bind(this)); + return + } else { + if (this.isOutside()) { + requestAnimationFrame(this.animateThrow.bind(this)); + return + } + } + } + this.onDragComplete(); + if (this.onThrowFinished != null) { + this.onThrowFinished(); + } + } + + needsAnimation() { + if (this.velocity == null) { + return false + } + return Points.length(this.velocity) > 0.01 + } + + nextVelocity(velocity) { + // Must be overwritten: computes the changed velocity. Implement + // damping, collison detection, etc. here + let next = Points.multiplyScalar(velocity, this.throwDamping); + return { + x: (this.movableX) ? next.x : 0, + y: (this.movableY) ? next.y : 0 + } + } + + _move(delta) { + // Overwrite if necessary + } + + onDragComplete() { + // Overwrite if necessary + } + + onDragUpdate(delta) { + // Overwrite if necessary + } + } + + class AbstractScatter extends Throwable { + constructor({ + minScale = 0.1, + maxScale = 1.0, + startScale = 1.0, + autoBringToFront = true, + autoThrow = true, + translatable = true, + scalable = true, + rotatable = true, + resizable = false, + movableX = true, + movableY = true, + throwVisibility = 44, + throwDamping = 0.95, + overdoScaling = 1, + mouseZoomFactor = 1.1, + rotationDegrees = null, + rotation = null, + onTransform = null, + interactive = true, + onClose = null, + onThrowFinished = null, + scaleAutoClose = false, + scaleCloseThreshold = 0.10, + scaleCloseBuffer = 0.05 + } = {}) { + if (rotationDegrees != null && rotation != null) { + throw new Error('Use rotationDegrees or rotation but not both') + } else if (rotation != null) { + rotationDegrees = Angle.radian2degree(rotation); + } else if (rotationDegrees == null) { + rotationDegrees = 0; + } + super({ + movableX, + movableY, + throwVisibility, + throwDamping, + autoThrow, + onThrowFinished + }); + + /** + * Closes the card when the minScale is reached and the + * card is released. Card can be saved by scaling it up again. + */ + this.scaleAutoClose = scaleAutoClose; + this.scaleCloseThreshold = scaleCloseThreshold; + this.scaleCloseBuffer = scaleCloseBuffer; + this.scaleAutoCloseTimeout = null; + + this.interactive = interactive; + this.startRotationDegrees = rotationDegrees; + this.startScale = startScale; // Needed to reset object + this.minScale = minScale; + this.maxScale = maxScale; + this.overdoScaling = overdoScaling; + this.translatable = translatable; + if (!translatable) { + this.movableX = false; + this.movableY = false; + } + this.scalable = scalable; + this.rotatable = rotatable; + this.resizable = resizable; + this.mouseZoomFactor = mouseZoomFactor; + this.autoBringToFront = autoBringToFront; + this.dragging = false; + this.onTransform = onTransform != null ? [onTransform] : null; + this.onClose = onClose != null ? [onClose] : null; + } + + addCloseEventCallback(callback) { + if (this.onClose == null) { + this.onClose = []; + } + this.onClose.push(callback); + } + + addTransformEventCallback(callback) { + if (this.onTransform == null) { + this.onTransform = []; + } + this.onTransform.push(callback); + } + + startGesture(interaction) { + this.bringToFront(); + this.killAnimation(); + this.observeVelocity(); + return true + } + + close() { + if (this.onClose) { + this.onClose.forEach(callback => callback(this)); + } + } + + gesture(interaction) { + let delta = interaction.delta(); + //console.log("gesture", delta) + if (delta != null) { + this.addVelocity(delta); + this.transform(delta, delta.zoom, delta.rotate, delta.about); + if (delta.zoom != 1) this.interactionAnchor = delta.about; + } + } + + get polygon() { + let w2 = this.width * this.scale / 2; + let h2 = this.height * this.scale / 2; + let center = this.center; + let polygon = new Polygon(center); + polygon.addPoint({ x: -w2, y: -h2 }); + polygon.addPoint({ x: w2, y: -h2 }); + polygon.addPoint({ x: w2, y: h2 }); + polygon.addPoint({ x: -w2, y: h2 }); + polygon.rotate(this.rotation); + return polygon + } + + isOutside() { + let stagePolygon = this.containerPolygon; + if (stagePolygon == null) + return false + let polygon = this.polygon; + if (polygon == null) + return false + let result = stagePolygon.intersectsWith(polygon); + return result === false || result.overlap < this.throwVisibility + } + + recenter() { + // Return a small vector that guarantees that the scatter is moving + // towards the center of the stage + let center = this.center; + let target = this.container.center; + let delta = Points.subtract(target, center); + return Points.normalize(delta) + } + + nextVelocity(velocity) { + return this.keepOnStage(velocity) + } + + bouncing() { + // Implements the bouncing behavior of the scatter. Moves the scatter + // to the center of the stage if the scatter is outside the stage or + // not within the limits of the throwVisibility. + + let stagePolygon = this.containerPolygon; + let polygon = this.polygon; + let result = stagePolygon.intersectsWith(polygon); + if (result === false || result.overlap < this.throwVisibility) { + let cv = this.recenter(); + let recentered = false; + while (result === false || result.overlap < this.throwVisibility) { + polygon.center.x += cv.x; + polygon.center.y += cv.y; + this._move(cv); + result = stagePolygon.intersectsWith(polygon); + recentered = true; + } + return recentered + } + return false + } + + keepOnStage(velocity, collision = 0.5) { + let stagePolygon = this.containerPolygon; + if (!stagePolygon) return + let polygon = this.polygon; + let bounced = this.bouncing(); + if (bounced) { + let stage = this.containerBounds; + let x = this.center.x; + let y = this.center.y; + let dx = this.movableX ? velocity.x : 0; + let dy = this.movableY ? velocity.y : 0; + let factor = this.throwDamping; + // if (recentered) { + if (x < 0) { + dx = -dx; + factor = collision; + } + if (x > stage.width) { + dx = -dx; + factor = collision; + } + if (y < 0) { + dy = -dy; + factor = collision; + } + if (y > stage.height) { + dy = -dy; + factor = collision; + } + // } + return Points.multiplyScalar({ x: dx, y: dy }, factor) + } + return super.nextVelocity(velocity) + } + + endGesture(interaction) { + this.startThrow(); + this._checkAutoClose(); + } + + _checkAutoClose() { + if (this.scaleAutoClose) + if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) { + this.zoom(this.minScale, { animate: 0.2, onComplete: this.close.bind(this) }); + } else if (this.scale < this.minScale + this.scaleCloseThreshold) { + this.zoom(this.minScale + this.scaleCloseThreshold, { animate: 0.4 }); + } + } + + rotateDegrees(degrees, anchor) { + let rad = Angle.degree2radian(degrees); + this.rotate(rad, anchor); + } + + rotate(rad, anchor) { + this.transform({ x: 0, y: 0 }, 1.0, rad, anchor); + } + + move(d, { animate = 0 } = {}) { + if (this.translatable) { + if (animate > 0) { + let startPos = this.position; + TweenLite.to(this, animate, { + x: '+=' + d.x, + y: '+=' + d.y, + /* scale: scale, uo: not defined, why was this here? */ + onUpdate: e => { + let p = this.position; + let dx = p.x - startPos.x; + let dy = p.x - startPos.y; + this.onMoved(dx, dy); + } + }); + } else { + this._move(d); + this.onMoved(d.x, d.y); + } + } + } + + moveTo(p, { animate = 0 } = {}) { + let c = this.origin; + let delta = Points.subtract(p, c); + this.move(delta, { animate: animate }); + } + + centerAt(p, { animate = 0 } = {}) { + let c = this.center; + let delta = Points.subtract(p, c); + this.move(delta, { animate: animate }); + } + + zoom( + scale, + { + animate = 0, + about = null, + delay = 0, + x = null, + y = null, + onComplete = null + } = {} + ) { + let anchor = about || this.center; + if (scale != this.scale) { + if (animate > 0) { + TweenLite.to(this, animate, { + scale: scale, + delay: delay, + onComplete: onComplete, + onUpdate: this.onZoomed.bind(this) + }); + } else { + this.scale = scale; + this.onZoomed(anchor); + } + } + } + + _move(delta) { + this.x += this.movableX ? delta.x : 0; + this.y += this.movableX ? delta.y : 0; + } + + transform(translate, zoom, rotate, anchor) { + let delta = { + x: this.movableX ? translate.x : 0, + y: this.movableY ? translate.y : 0 + }; + if (this.resizable) var vzoom = zoom; + if (!this.translatable) delta = { x: 0, y: 0 }; + if (!this.rotatable) rotate = 0; + if (!this.scalable) zoom = 1.0; + if (zoom == 1.0 && rotate == 0) { + this._move(delta); + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: delta, + scale: this.scale, + rotate: 0, + about: anchor, + fast: false, + type: UPDATE + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + return + } + let origin = this.rotationOrigin; + let beta = Points.angle(origin, anchor); + let distance = Points.distance(origin, anchor); + let { scale: newScale, zoom: thresholdedZoom } = this.calculateScale(zoom); + + let newOrigin = Points.arc(anchor, beta + rotate, distance * thresholdedZoom); + let extra = Points.subtract(newOrigin, origin); + let offset = Points.subtract(anchor, origin); + this._move(offset); + this.scale = newScale; + this.rotation += rotate; + offset = Points.negate(offset); + offset = Points.add(offset, extra); + offset = Points.add(offset, translate); + this._move(offset); + + delta.x += extra.x; + delta.y += extra.y; + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: delta, + scale: newScale, + rotate: rotate, + about: anchor + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + if (this.resizable) { + this.resizeAfterTransform(vzoom); + } + } + + /** + * For a given zoom, a new scale is calculated, taking + * min and max scale into account. + * + * @param {number} zoom - The zoom factor, to scale the object with. + * @returns {object} - Returns an object containing the a value for a valid scale and the corrected zoom factor. + */ + calculateScale(zoom) { + let scale = this.scale * zoom; + + let minScale = this.minScale / this.overdoScaling; + let maxScale = this.maxScale * this.overdoScaling; + if (scale < minScale) { + scale = minScale; + zoom = scale / this.scale; + } + if (scale > maxScale) { + scale = maxScale; + zoom = scale / this.scale; + } + + if (this.scaleAutoClose) + this._updateTransparency(); + + return { zoom, scale } + } + + _updateTransparency() { + if (this.scale < this.minScale + this.scaleCloseThreshold) { + let transparency = this.calculateScaleTransparency(); + this.element.style.opacity = transparency; + } else this.element.style.opacity = 1; + } + + calculateScaleTransparency() { + let transparency = (this.scale - this.minScale) / this.scaleCloseThreshold; + transparency = (transparency > 1) ? 1 : (transparency < 0) ? 0 : transparency; + return transparency + } + + resizeAfterTransform(zoom) { + // Overwrite this in subclasses. + } + + validScale(scale) { + scale = Math.max(scale, this.minScale); + scale = Math.min(scale, this.maxScale); + return scale + } + + animateZoomBounce(dt = 1) { + if (this.zoomAnchor != null) { + let zoom = 1; + let amount = Math.min(0.01, 0.3 * dt / 100000.0); + if (this.scale < this.minScale) zoom = 1 + amount; + if (this.scale > this.maxScale) zoom = 1 - amount; + if (zoom != 1) { + this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor); + requestAnimationFrame(dt => { + this.animateZoomBounce(dt); + }); + return + } + this.zoomAnchor = null; + } + } + + checkScaling(about, delay = 0) { + this.zoomAnchor = about; + clearTimeout(this.animateZoomBounce.bind(this)); + setTimeout(this.animateZoomBounce.bind(this), delay); + } + + onMouseWheel(event) { + if (event.claimedByScatter) { + if (event.claimedByScatter != this) return + } + this.killAnimation(); + this.targetScale = null; + let direction = event.detail < 0 || event.wheelDelta > 0; + let globalPoint = { x: event.clientX, y: event.clientY }; + let centerPoint = this.mapPositionToContainerPoint(globalPoint); + if (event.shiftKey) { + let degrees = direction ? 5 : -5; + let rad = Angle.degree2radian(degrees); + return this.transform({ x: 0, y: 0 }, 1.0, rad, centerPoint) + } + const zoomFactor = this.mouseZoomFactor; + let zoom = direction ? zoomFactor : 1 / zoomFactor; + this.transform({ x: 0, y: 0 }, zoom, 0, centerPoint); + this.checkScaling(centerPoint, 200); + + if (this.scaleAutoClose) { + if (this.scale <= this.minScale + this.scaleCloseThreshold) { + + if (this.scaleAutoCloseTimeout) clearTimeout(this.scaleAutoCloseTimeout); + this.scaleAutoCloseTimeout = setTimeout(() => { + this._checkAutoClose(); + }, 600); + } + this._updateTransparency(); + } + // + // if (this.onTransform != null) { + // let event = new ScatterEvent(this, { + // translate: {x: 0, y: 0}, + // scale: this.scale, + // rotate: 0, + // about: null, + // fast: false, + // type: ZOOM + // }) + // this.onTransform.forEach(function(f) { + // f(event) + // }) + // } + } + + onStart(event, interaction) { + + if (this.startGesture(interaction)) { + this.dragging = true; + this.interactionAnchor = null; + } + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: { x: 0, y: 0 }, + scale: this.scale, + rotate: 0, + about: null, + fast: false, + type: START + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onMove(event, interaction) { + /** As long as mouseout && mouseleave interrupt we cannot be sure that + * dragging remains correct. + */ + if (this.dragging) { + this.gesture(interaction); + } + } + + onEnd(event, interaction) { + //console.log("Scatter.onEnd", this.dragging) + if (interaction.isFinished()) { + this.endGesture(interaction); + this.dragging = false; + for (let key of interaction.ended.keys()) { + if (interaction.isTap(key)) { + let point = interaction.ended.get(key); + this.onTap(event, interaction, point); + } + } + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: { x: 0, y: 0 }, + scale: this.scale, + rotate: 0, + about: null, + fast: false, + type: END + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + let about = this.interactionAnchor; + if (about != null) { + this.checkScaling(about, 100); + } + } + + onTap(event, interaction, point) { } + + onDragUpdate(delta) { + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + fast: true, + translate: delta, + scale: this.scale, + about: this.currentAbout, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onDragComplete() { + if (this.onTransform) { + let event = new ScatterEvent(this, { + scale: this.scale, + about: this.currentAbout, + fast: false, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onMoved(dx, dy, about) { + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + translate: { x: dx, y: dy }, + about: about, + fast: true, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onResizing() { + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + scale: this.scale, + fast: false, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + + onZoomed(about) { + + if (this.scaleAutoClose) + this._updateTransparency(); + + if (this.onTransform != null) { + let event = new ScatterEvent(this, { + scale: this.scale, + about: about, + fast: false, + type: null + }); + this.onTransform.forEach(function (f) { + f(event); + }); + } + } + } + + + class DOMScatter extends AbstractScatter { + constructor( + element, + container, + { + startScale = 1.0, + minScale = 0.1, + maxScale = 1.0, + overdoScaling = 1.5, + autoBringToFront = true, + translatable = true, + scalable = true, + rotatable = true, + movableX = true, + movableY = true, + rotationDegrees = null, + rotation = null, + onTransform = null, + transformOrigin = 'center center', + // extras which are in part needed + x = 0, + y = 0, + width = null, // required + height = null, // required + resizable = false, + simulateClick = false, + verbose = true, + onResize = null, + touchAction = 'none', + throwVisibility = 44, + throwDamping = 0.95, + autoThrow = true, + scaleAutoClose = false, + onClose = null, + scaleCloseThreshold = 0.10, + scaleCloseBuffer = 0.05 + } = {} + ) { + super({ + minScale, + maxScale, + startScale, + overdoScaling, + autoBringToFront, + translatable, + scalable, + rotatable, + movableX, + movableY, + resizable, + rotationDegrees, + rotation, + onTransform, + throwVisibility, + throwDamping, + autoThrow, + scaleAutoClose, + scaleCloseThreshold, + scaleCloseBuffer, + onClose + }); + if (container == null || width == null || height == null) { + throw new Error('Invalid value: null') + } + element.scatter = this; + this.element = element; + this.x = x; + this.y = y; + this.oldX = 0; + this.oldY = 0; + this.meanX = x; + this.meanY = y; + this.width = width; + this.height = height; + this.throwVisibility = Math.min(width, height, throwVisibility); + this.container = container; + this.simulateClick = simulateClick; + this.scale = startScale; + this.rotationDegrees = this.startRotationDegrees; + this.transformOrigin = transformOrigin; + this.initialValues = { + x: x, + y: y, + width: width, + height: height, + scale: startScale, + rotation: this.startRotationDegrees, + transformOrigin: transformOrigin + }; + + + // For tweenlite we need initial values in _gsTransform + TweenLite.set(element, this.initialValues); + this.onResize = onResize; + this.verbose = verbose; + if (touchAction !== null) { + Elements$1.setStyle(element, { touchAction }); + } + this.resizeButton = null; + if (resizable) { + let button = document.createElement("div"); + button.style.position = "absolute"; + button.style.right = "0px"; + button.style.bottom = "0px"; + button.style.width = "50px"; + button.style.height = "50px"; + // button.style.borderRadius = "100% 0px 0px 0px"; + // button.style.background = this.element.style.backgroundColor + button.className = "interactiveElement"; + this.element.appendChild(button); + + button.addEventListener('pointerdown', (e) => { + this.startResize(e); + }); + + button.addEventListener('pointermove', (e) => { + this.resize(e); + }); + + button.addEventListener('pointerup', (e) => { + this.stopResize(e); + }); + this.resizeButton = button; + } + container.add(this); + } + + /** Returns geometry data as object. **/ + getState() { + return { + scale: this.scale, + x: this.x, + y: this.y, + rotation: this.rotation + } + } + + close() { + super.close(); + let parent = this.element.parentNode; + if (parent) parent.removeChild(this.element); + } + + get rotationOrigin() { + return this.center + } + + get x() { + return this._x + } + + get y() { + return this._y + } + + set x(value) { + this._x = value; + TweenLite.set(this.element, { x: value }); + } + + set y(value) { + this._y = value; + TweenLite.set(this.element, { y: value }); + } + + get position() { + let transform = this.element._gsTransform; + let x = transform.x; + let y = transform.y; + return { x, y } + } + + get origin() { + let p = this.fromNodeToPage(0, 0); + return Points.fromPageToNode(this.container.element, p) + } + + get bounds() { + let stage = this.container.element.getBoundingClientRect(); + let rect = this.element.getBoundingClientRect(); + return { + top: rect.top - stage.top, + left: rect.left - stage.left, + width: rect.width, + height: rect.height + } + } + + get center() { + let r = this.bounds; + let w2 = r.width / 2; + let h2 = r.height / 2; + // if (this.resizable) { + // w2 *= this.scale + // h2 *= this.scale + // } + var x = r.left + w2; + var y = r.top + h2; + return { x, y } + } + + set rotation(radians) { + let rad = radians; // Angle.normalize(radians) + let degrees = Angle.radian2degree(rad); + TweenLite.set(this.element, { rotation: degrees }); + this._rotation = rad; + } + + set rotationDegrees(degrees) { + let deg = degrees; // Angle.normalizeDegree(degrees) + TweenLite.set(this.element, { rotation: deg }); + this._rotation = Angle.degree2radian(deg); + } + + get rotation() { + return this._rotation + } + + get rotationDegrees() { + return this._rotation + } + + set scale(scale) { + TweenLite.set(this.element, { + scale: scale, + transformOrigin: this.transformOrigin + }); + this._scale = scale; + } + + get scale() { + return this._scale + } + + get containerBounds() { + return this.container.bounds + } + + get containerPolygon() { + return this.container.polygon + } + + mapPositionToContainerPoint(point) { + return this.container.mapPositionToPoint(point) + } + + capture(event) { + return true + } + + reset() { + TweenLite.set(this.element, this.initialValues); + } + + hide() { + TweenLite.to(this.element, 0.1, { + display: 'none', + onComplete: e => { + this.element.parentNode.removeChild(this.element); + } + }); + } + + show() { + TweenLite.set(this.element, { display: 'block' }); + } + + showAt(p, rotationDegrees) { + TweenLite.set(this.element, { + display: 'block', + x: p.x, + y: p.y, + rotation: rotationDegrees, + transformOrigin: this.transformOrigin + }); + } + + bringToFront() { + // this.element.parentNode.appendChild(this.element) + // uo: On Chome and Electon appendChild leads to flicker + TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ }); + } + + toggleVideo(element) { + if (element.paused) { + element.play(); + } else { + element.pause(); + } + } + + onTap(event, interaction, point) { + if (this.simulateClick) { + let p = Points.fromPageToNode(this.element, point); + let iframe = this.element.querySelector('iframe'); + if (iframe) { + let doc = iframe.contentWindow.document; + let element = doc.elementFromPoint(p.x, p.y); + if (element == null) { + return + } + switch (element.tagName) { + case 'VIDEO': + console.log(element.currentSrc); + if (PopupMenu) { + PopupMenu.open( + { + Fullscreen: () => + window.open(element.currentSrc), + Play: () => element.play() + }, + { x, y } + ); + } else { + this.toggleVideo(element); + } + break + default: + element.click(); + } + } + } + } + + isDescendant(parent, child) { + let node = child.parentNode; + while (node != null) { + if (node == parent) { + return true + } + node = node.parentNode; + } + return false + } + + fromPageToNode(x, y) { + return Points.fromPageToNode(this.element, { x, y }) + } + + fromNodeToPage(x, y) { + return Points.fromNodeToPage(this.element, { x, y }) + } + + _move(delta) { + // UO: We need to keep TweenLite's _gsTransform and the private + // _x and _y attributes aligned + let x = this.element._gsTransform.x; + let y = this.element._gsTransform.y; + if (this.movableX) { + x += delta.x; + } + if (this.movableY) { + y += delta.y; + } + this._x = x; + this._y = y; + TweenLite.set(this.element, { x: x, y: y }); + } + + resizeAfterTransform(zoom) { + // let w = this.width * this.scale + // let h = this.height * this.scale + // TweenLite.set(this.element, { width: w, height: h }) + if (this.onResize) { + let event = new ResizeEvent(this, { width: w, height: h }); + this.onResize(event); + } + if (this.resizeButton != null) ; + } + + startResize(e) { + e.preventDefault(); + let event = new CustomEvent('resizeStarted'); + + let oldPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; + this.bringToFront(); + + this.element.style.transformOrigin = "0% 0%"; + + let newPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; + + let offset = Points.subtract(oldPostition, newPostition); + + this.oldX = e.clientX; + this.oldY = e.clientY; + + e.target.setAttribute('resizing', "true"); + e.target.setPointerCapture(e.pointerId); + + TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } }); + TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } }); + + this.element.dispatchEvent(event); + } + + resize(e) { + e.preventDefault(); + + let rotation = Angle.radian2degree(this.rotation); + rotation = (rotation + 360) % 360; + let event = new CustomEvent('resized'); + if (e.target.getAttribute('resizing') == "true") { + + let deltaX = (e.clientX - this.oldX); + let deltaY = (e.clientY - this.oldY); + + let r = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); + let phi = Angle.radian2degree(Math.atan2(deltaX, deltaY)); + + phi = ((phi) + 630) % 360; + let rot = ((rotation + 90) + 630) % 360; + + let diffAngle = ((0 + rot) + 360) % 360; + let phiCorrected = (phi + diffAngle + 360) % 360; + + let resizeW = r * Math.cos(Angle.degree2radian(phiCorrected)); + let resizeH = -r * Math.sin(Angle.degree2radian(phiCorrected)); + + if (this.element.offsetWidth + resizeW / this.scale > this.width * 0.3 && this.element.offsetHeight + resizeH / this.scale > this.height * 0.3) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, height: this.element.offsetHeight + resizeH / this.scale }); + + this.oldX = e.clientX; + this.oldY = e.clientY; + this.onResizing(); + + this.element.dispatchEvent(event); + } + } + + stopResize(e) { + e.preventDefault(); + + let event = new CustomEvent('resizeEnded'); + let oldPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; + this.element.style.transformOrigin = "50% 50%"; + let newPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; + let offset = Points.subtract(oldPostition, newPostition); + + TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } }); + TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } }); + + e.target.setAttribute('resizing', "false"); + + this.element.dispatchEvent(event); + } + } + + DOMScatter.zIndex = 1000; + + class CardLoader { + constructor( + src, + { + x = 0, + y = 0, + width = 1000, + height = 800, + maxWidth = null, + maxHeight = null, + scale = 1, + minScale = 0.5, + maxScale = 1.5, + rotation = 0 + } = {} + ) { + this.src = src; + this.x = x; + this.y = y; + this.scale = scale; + this.rotation = 0; + this.maxScale = maxScale; + this.minScale = minScale; + this.wantedWidth = width; + this.wantedHeight = height; + this.maxWidth = maxWidth != null ? maxWidth : window.innerWidth; + this.maxHeight = maxHeight != null ? maxHeight : window.innerHeight; + this.addedNode = null; + console.log({ + + width, + height, + maxWidth, + maxHeight, + + }); + } + + unload() { + if (this.addedNode) { + this.addedNode.remove(); + this.addedNode = null; + } + } + } + + class DOMFlip { + constructor( + domScatterContainer, + flipTemplate, + frontLoader, + backLoader, + { + closeOnMinScale = false, + flipDuration = 1, + fadeDuration = 0.2, + overdoScaling = 1, + autoLoad = false, + center = null, + preloadBack = false, + translatable = true, + scalable = true, + rotatable = true, + onFront = null, + onBack = null, + onClose = null, + onUpdate = null, + onRemoved = null, + onLoaded = null + } = {} + ) { + this.domScatterContainer = domScatterContainer; + this.id = getId(); + this.flipDuration = flipDuration; + this.fadeDuration = fadeDuration; + this.closeOnMinScale = closeOnMinScale; + this.flipTemplate = flipTemplate; + this.frontLoader = frontLoader; + this.backLoader = backLoader; + this.translatable = translatable; + this.scalable = scalable; + this.rotatable = rotatable; + this.onFrontFlipped = onFront; + this.onBackFlipped = onBack; + this.onClose = onClose; + this.onRemoved = onRemoved; + this.onUpdate = onUpdate; + this.onLoaded = onLoaded; + this.center = center; + this.preloadBack = preloadBack; + this.overdoScaling = overdoScaling; + if (autoLoad) { + this.load(); + } + } + + load() { + return new Promise((resolve, reject) => { + let t = this.flipTemplate; + let dom = this.domScatterContainer.element; + let wrapper = t.content.querySelector('.flipWrapper'); + wrapper.id = this.id; + let clone = document.importNode(t.content, true); + dom.appendChild(clone); + // We cannot use the document fragment itself because it + // is not part of the main dom tree. After the appendChild + // call we can access the new dom element by id + this.cardWrapper = dom.querySelector('#' + this.id); + let front = this.cardWrapper.querySelector('.front'); + this.frontLoader.load(front).then(loader => { + this.frontLoaded(loader).then((obj) => { + if (this.onLoaded) this.onLoaded(); + resolve(this); + }); + }); + }) + } + + frontLoaded(loader) { + return new Promise((resolve, reject) => { + let scatter = new DOMScatter( + this.cardWrapper, + this.domScatterContainer, + { + x: loader.x, + y: loader.y, + startScale: loader.scale, + scale: loader.scale, + maxScale: loader.maxScale, + minScale: loader.minScale, + width: loader.wantedWidth, + height: loader.wantedHeight, + rotation: loader.rotation, + translatable: this.translatable, + scalable: this.scalable, + rotatable: this.rotatable, + overdoScaling: this.overdoScaling + } + ); + + if (this.center) { + scatter.centerAt(this.center); + } + + if (this.closeOnMinScale) { + + const removeOnMinScale = function () { + if (scatter.scale <= scatter.minScale) { + this.flippable.close(); + + // 'Disable' overdoscaling to avoid weird jumps on close. + scatter.minScale /= scatter.overdoScaling; + scatter.overdoScaling = 1; + + //Remove callback + if (scatter.onTransform) { + let callbackIdx = scatter.onTransform.indexOf(removeOnMinScale); + scatter.onTransform.splice(callbackIdx, 1); + } + } + + }.bind(this); + + + + scatter.addTransformEventCallback(removeOnMinScale); + } + + let flippable = new DOMFlippable(this.cardWrapper, scatter, this); + let back = this.cardWrapper.querySelector('.back'); + + if (this.preloadBack) { + this.backLoader.load(back).then(loader => { + this.setupFlippable(flippable, loader); + }); + } + this.flippable = flippable; + resolve(this); + }) + } + + centerAt(p) { + this.center = p; + this.flippable.centerAt(p); + } + + zoom(scale) { + this.flippable.zoom(scale); + } + + setupFlippable(flippable, loader) { + flippable.wantedWidth = loader.wantedWidth; + flippable.wantedHeight = loader.wantedHeight; + flippable.wantedScale = loader.scale; + flippable.minScale = loader.minScale; + flippable.maxScale = loader.maxScale; + flippable.scaleButtons(); + } + + start({ targetCenter = null } = {}) { + console.log('DOMFlip.start', targetCenter); + if (this.preloadBack) this.flippable.start({ duration: this.flipDuration, targetCenter }); + else { + let back = this.cardWrapper.querySelector('.back'); + let flippable = this.flippable; + this.backLoader.load(back).then(loader => { + this.setupFlippable(flippable, loader); + flippable.start({ duration: this.flipDuration, targetCenter }); + }); + } + } + + fadeOutAndRemove() { + TweenLite.to(this.cardWrapper, this.fadeDuration, { + opacity: 0, + onComplete: () => { + this.cardWrapper.remove(); + } + }); + } + + closed() { + this.unload(); + } + + unload() { + if (!this.preloadBack) { + this.backLoader.unload(); + } + } + } + + class DOMFlippable { + constructor(element, scatter, flip) { + // Set log to console.log or a custom log function + // define data structures to store our touchpoints in + + this.element = element; + this.flip = flip; + this.card = element.querySelector('.flipCard'); + this.front = element.querySelector('.front'); + this.back = element.querySelector('.back'); + this.flipped = false; + this.scatter = scatter; + this.onFrontFlipped = flip.onFrontFlipped; + this.onBackFlipped = flip.onBackFlipped; + this.onClose = flip.onClose; + this.onRemoved = flip.onRemoved; + this.onUpdate = flip.onUpdate; + + this.flipDuration = flip.flipDuration; + this.fadeDuration = flip.fadeDuration; + scatter.addTransformEventCallback(this.scatterTransformed.bind(this)); + console.log('lib.DOMFlippable', 5000); + TweenLite.set(this.element, { perspective: 5000 }); + TweenLite.set(this.card, { transformStyle: 'preserve-3d' }); + TweenLite.set(this.back, { rotationY: -180 }); + TweenLite.set([this.back, this.front], { + backfaceVisibility: 'hidden', + perspective: 5000 + }); + TweenLite.set(this.front, { visibility: 'visible' }); + this.infoBtn = element.querySelector('.infoBtn'); + this.backBtn = element.querySelector('.backBtn'); + this.closeBtn = element.querySelector('.closeBtn'); + /* Buttons are not guaranteed to exist. */ + if (this.infoBtn) { + InteractionMapper$1.on('tap', this.infoBtn, event => this.flip.start()); + + this.enable(this.infoBtn); + } + if (this.backBtn) { + InteractionMapper$1.on('tap', this.backBtn, event => this.start()); + } + if (this.closeBtn) { + InteractionMapper$1.on('tap', this.closeBtn, event => this.close()); + this.enable(this.closeBtn); + } + this.scaleButtons(); + this.bringToFront(); + } + + close() { + this.disable(this.infoBtn); + this.disable(this.closeBtn); + if (this.onClose) { + this.onClose(this); + this.flip.closed(); + } else { + this.scatter.zoom(0.1, { + animate: this.fadeDuration, + onComplete: () => { + this.element.remove(); + this.flip.closed(); + if (this.onRemoved) { + this.onRemoved.call(this); + } + } + }); + } + } + + showFront() { + TweenLite.set(this.front, { visibility: 'visible' }); + } + + centerAt(p) { + this.scatter.centerAt(p); + } + + zoom(scale) { + this.scatter.zoom(scale); + } + + get buttonScale() { + let iscale = 1.0; + + if (this.scatter != null) { + let scale = this.scatter.scale || 1; + iscale = 1.0 / scale; + } + return iscale + } + + scaleButtons() { + //This also works for svgs. + // if (this.infoBtn) + // this.infoBtn.style.transform = "scale(" + this.buttonScale + ")" + + // if (this.backBtn) + // this.backBtn.style.transform = "scale(" + this.buttonScale + ")" + + // if (this.closeBtn) + // this.closeBtn.style.transform = "scale(" + this.buttonScale + ")" + + console.log(this.buttonScale); + //// This did not work with svgs! + TweenLite.set([this.infoBtn, this.backBtn, this.closeBtn], { + scale: this.buttonScale + }); + } + + bringToFront() { + this.scatter.bringToFront(); + TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ }); + } + + clickInfo() { + this.bringToFront(); + this.infoBtn.click(); + } + + scatterTransformed(event) { + this.scaleButtons(); + } + + targetRotation(alpha) { + let ortho = 90; + let rest = alpha % ortho; + let delta = 0.0; + if (rest > ortho / 2.0) { + delta = ortho - rest; + } else { + delta = -rest; + } + return delta + } + + infoValues(info) { + let startX = this.element._gsTransform.x; + let startY = this.element._gsTransform.y; + let startAngle = this.element._gsTransform.rotation; + let startScale = this.element._gsTransform.scaleX; + let w = this.element.style.width; + let h = this.element.style.height; + console.log(info, startX, startY, startAngle, startScale, w, h); + } + + show(element, duration = 0, alpha = 1) { + if (element) { + TweenLite.to(element, duration, { autoAlpha: alpha }); // visibility: 'visible', display: 'initial'}) + } + } + + hide(element, duration = 0, alpha = 0) { + if (element) { + TweenLite.to(element, duration, { autoAlpha: alpha }); // {visibility: 'hidden', display: 'none'}) + } + } + + + + enable(button) { + this.show(button, this.fadeDuration); + if (button) { + TweenLite.set(button, { pointerEvents: 'auto' }); + } + } + + disable(button) { + this.hide(button, this.fadeDuration); + if (button) { + TweenLite.set(button, { pointerEvents: 'none' }); + } + } + + start({ targetCenter = null } = {}) { + this.bringToFront(); + if (!this.flipped) { + this.startX = this.element._gsTransform.x; + this.startY = this.element._gsTransform.y; + this.startAngle = this.element._gsTransform.rotation; + this.startScale = this.element._gsTransform.scaleX; + this.startWidth = this.element.style.width; + this.startHeight = this.element.style.height; + this.scatterStartWidth = this.scatter.width; + this.scatterStartHeight = this.scatter.height; + this.show(this.back); + this.disable(this.infoBtn); + this.disable(this.closeBtn); + } else { + this.show(this.front, this.fadeDuration); + this.disable(this.backBtn); + } + let { scalable, translatable, rotatable } = this.scatter; + this.saved = { scalable, translatable, rotatable }; + this.scatter.scalable = false; + this.scatter.translatable = false; + this.scatter.rotatable = false; + this.scatter.killAnimation(); + + this.flipped = !this.flipped; + let targetY = this.flipped ? 180 : 0; + let targetZ = this.flipped + ? this.startAngle + this.targetRotation(this.startAngle) + : this.startAngle; + let targetScale = this.flipped ? this.wantedScale : this.startScale; + let w = this.flipped ? this.wantedWidth : this.startWidth; + let h = this.flipped ? this.wantedHeight : this.startHeight; + let dw = this.wantedWidth - this.scatter.width; + let dh = this.wantedHeight - this.scatter.height; + let tc = targetCenter; + let xx = tc != null ? tc.x - w / 2 : this.startX - dw / 2; + let yy = tc != null ? tc.y - h / 2 : this.startY - dh / 2; + let x = this.flipped ? xx : this.startX; + let y = this.flipped ? yy : this.startY; + + console.log("DOMFlippable.start", this.flipped, targetCenter, x, y, this.saved); + let onUpdate = this.onUpdate !== null ? () => this.onUpdate(this) : null; + console.log(this.flipDuration); + TweenLite.to(this.card, this.flipDuration, { + rotationY: targetY, + ease: Power1.easeOut, + transformOrigin: '50% 50%', + onUpdate, + onComplete: e => { + if (this.flipped) { + //this.hide(this.front) + this.enable(this.backBtn); + this.show(this.backBtn); + + if (this.onFrontFlipped) { + this.onFrontFlipped(this); + } + } else { + + if (this.onBackFlipped == null) { + this.enable(this.infoBtn, this.fadeDuration); + this.enable(this.closeBtn, this.fadeDuration); + } else { + this.onBackFlipped(this); + } + this.flip.unload(); + } + this.scatter.scale = targetScale; + this.scaleButtons(); + this.scatter.rotationDegrees = targetZ; + this.scatter.width = this.flipped ? w : this.scatterStartWidth; + this.scatter.height = this.flipped ? h : this.scatterStartHeight; + + let { scalable, translatable, rotatable } = this.saved; + this.scatter.scalable = scalable; + this.scatter.translatable = translatable; + this.scatter.rotatable = rotatable; + }, + force3D: true + }); + + // See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/ + TweenLite.to(this.element, this.flipDuration / 2, { + scale: targetScale, + ease: Power1.easeOut, + rotationZ: targetZ + '_short', + transformOrigin: '50% 50%', + width: w, + height: h, + x: x, + y: y, + onComplete: e => { + if (this.flipped) { + this.hide(this.front); + // this.hide(this.infoBtn) + } else { + this.hide(this.back); + // this.show(this.infoBtn) + } + } + }); + } + } + + const deepZoomTileCache = new Map(); + + + /** The current Tile implementation simply uses PIXI.Sprites. + * + * BTW: PIXI.extras.TilingSprite is not appropriate. It should be used for + * repeating patterns. + **/ + class Tile extends PIXI.Sprite { + constructor(texture, url) { + super(texture); + this.url = url; + this.register(url); + } + + static fromImage(imageId, crossorigin, scaleMode) { + return new Tile(PIXI.Texture.fromImage(imageId, crossorigin, scaleMode), imageId) + } + + /** + * Registers the tile in the global reference counter for textures + * + * @param {*} url + * @param {boolean} [debug=false] + * @memberof Tile + */ + register(url, debug = false) { + if (deepZoomTileCache.has(url)) { + let tiles = deepZoomTileCache.get(url); + tiles.add(this); + if (debug) console.log("Tile.register", url, tiles.size); + } + else { + deepZoomTileCache.set(url, new Set([this])); + if (debug) console.log("Tile.register", url, 1); + } + } + + /** + * Unregisters the rile in the global reference counter for textures + * + * @returns {number} The number of how often a texture is used. + * @memberof Tile + */ + unregister() { + let tiles = deepZoomTileCache.get(this.url); + tiles.delete(this); + if (tiles.size == 0) { + deepZoomTileCache.delete(this.url); + } + return tiles.size + } + + /** + * Destroys this sprite and optionally its texture and children + * + * @param {*} options Part of the PIXI API, but ignored in the implementation + * @memberof Tile + */ + destroy(options, debug = false) { + if (this.parent != null) ; + let count = this.unregister(); + if (count <= 0) { + let opts = { children: true, texture: true, baseTexture: true }; + super.destroy(opts); + if (debug) console.log("Tile.destroy", deepZoomTileCache.size, opts); + } + else { + let opts = { children: true, texture: false, baseTexture: false }; + if (debug) console.log("Tile.destroy", deepZoomTileCache.size, opts); + super.destroy(opts); + } + } + } + + /** + * A Tile Loader component that can be plugged into a Tiles Layer. + */ + class TileLoader { + constructor(tiles) { + this.debug = false; + this.tiles = tiles; + this.setup(); + } + + /** Setup collections and instance vars. */ + setup() { + this.map = new Map(); // Map {url : [ col, row]} + this.loading = new Set(); // Set url + this.loaded = new Map(); // Map {url : sprite } + this.loadQueue = []; + } + + /** Schedules a tile url for loading. The loading itself must be triggered + by a call to loadOneTile or loadAll + + * @param {String} url - the url of the texture / tile + * @param {Number} col - the tile col + * @param {Number} row - the tile row + **/ + schedule(url, col, row) { + if (this.loaded.has(url)) return false + if (this.loading.has(url)) return false + this.map.set(url, [col, row]); + this.loading.add(url); + this.loadQueue.push(url); + return true + } + + unschedule(url) { + if (this.loaded.has(url)) this.loaded.delete(url); + if (this.loading.has(url)) this.loading.delete(url); + this.loadQueue = this.loadQueue.filter(item => item != url); + } + + /** Cancels loading by clearing the load queue **/ + cancel() { + this.loadQueue = []; + this.loading.clear(); + } + + /** Destroys alls collections. **/ + destroy() { + this.setup(); + } + + /** Private method. Informs the tile layer about a texture for a given url. + * Creates the sprite for the loaded texture and informs the tile layer. + * @param {String} url - the url + * @param {Object} texture - the loaded resource + **/ + _textureAvailable(url, col, row, texture) { + let tile = this.loaded.get(url); + if (tile != null) { + console.warn("Tile already loaded"); + tile.unregister(); + } + tile = new Tile(texture, url); + this.loaded.set(url, tile); + this.tiles.tileAvailable(tile, col, row, url); + } + } + + /** + * Uses the PIXI Loader but can be replaced with othe loaders implementing + * the public methods without underscore. + * Calls the Tiles.tileAvailable method if the texture is available. + **/ + class PIXITileLoader extends TileLoader { + + constructor(tiles, compression) { + super(tiles); + this.destroyed = false; + this.loader = new PIXI.loaders.Loader(); + this.loader.on('load', this._onLoaded.bind(this)); + this.loader.on('error', this._onError.bind(this)); + if (compression) { + this.loader.pre(PIXI.compressedTextures.imageParser()); + } + } + + schedule(url, col, row) { + // Overwritten schedule to avoid BaseTexture and Texture already loaded errors. + if (this.loaded.has(url)) return false + if (this.loading.has(url)) return false + + if (deepZoomTileCache.has(url)) { + let tiles = deepZoomTileCache.get(url); + for (let tile of tiles.values()) { + //console.log("Reusing cached texture", tile.parent) + let texture = tile.texture; + this._textureAvailable(url, col, row, texture); + return false + } + } + + let texture = PIXI.utils.TextureCache[url]; + if (texture) { + if (this.debug) console.log('Texture already loaded', texture); + this._textureAvailable(url, col, row, texture); + return false + } + let base = PIXI.utils.BaseTextureCache[url]; + if (base) { + if (this.debug) console.log('BaseTexture already loaded', base); + let texture = new PIXI.Texture(base); + this._textureAvailable(url, col, row, texture); + return false + } + return super.schedule(url, col, row) + } + + /** Load one and only one of the scheduled tiles **/ + loadOneTile() { + if (this.destroyed) + return + this._loadOneTile(); + } + + /** Load all scheduled tiles **/ + loadAll() { + if (this.destroyed) + return + this._loadAllTiles(); + } + + /** Destroys the loader completly **/ + destroy() { + this.destroyed = true; + super.destroy(); + try { + this.loader.reset(); + } catch (error) { + console.warn("Error while resetting loader", error); + } + } + + _onError(loader, error) { + console.warn('Cannot load', error); + } + + /** Private method. Called by the PIXI loader after each successfull + * loading of a single tile. + * Creates the sprite for the loaded texture and informs the tile layer. + * @param {Object} loader - the loader instance + * @param {Object} resource - the loaded resource with url and texture attr + **/ + _onLoaded(loader, resource) { + if (this.destroyed) { + let texture = resource.texture; + let destroyBase = !deepZoomTileCache.has(resource.url); + texture.destroy(destroyBase); + console.warn("Received resource after destroy", texture); + return + } + try { + let [col, row] = this.map.get(resource.url); + this._textureAvailable(resource.url, col, row, resource.texture); + } + catch (err) { + console.warn("Texture unavailable: " + err.message); + } + } + + /** Private method: loads one tile from the queue. **/ + _loadOneTile(retry = 1) { + //console.log("_loadOneTile") + if (this.destroyed) { + //console.warn("_loadOneTile after destroy") + return + } + if (this.loader.loading) { + setTimeout(() => { + this._loadOneTile(); + }, retry); + return + } + if (this.loadQueue.length > 0) { + let url = this.loadQueue.pop(); + this.loader.add(url, url); + this.loader.load(); + } + } + + /** Private method: loads all tiles from the queue in batches. Batches are + helpfull to avoid loading tiles that are no longer needed because the + user has already zoomed to a different level.**/ + _loadAllTiles(batchSize = 8, retry = 16) { + if (this.destroyed) { + return + } + if (this.loadQueue.length > 0) { + if (this.loader.loading) { + //console.log("Loader busy", this.loadQueue.length) + setTimeout(() => { + this._loadAllTiles(); + }, retry); + return + } + let i = 0; + let urls = []; + while (i < batchSize && this.loadQueue.length > 0) { + let url = this.loadQueue.pop(); + if (!this.loaded.has(url)) { + let resource = this.loader.resources[url]; + if (resource) { + console.log("Resource already added", url); + } + else { + urls.push(url); + i += 1; + } + } + } + this.loader.add(urls).load(() => { + this._loadAllTiles(); + }); + } + } + } + + + /** + * Uses Workers but can be replaced with other loaders implementing + * the public methods without underscore. + * Calls the Tiles.tileAvailable method if the texture is available. + **/ + class WorkerTileLoader extends TileLoader { + + constructor(tiles) { + super(tiles); + let worker = this.worker = new Worker("../../lib/pixi/deepzoom/tileloader.js"); + worker.onmessage = (event) => { + if (event.data.success) { + let { url, col, row, buffer } = event.data; + //console.log("WorkerTileLoader.loaded", url, buffer) + let CompressedImage = PIXI.compressedTextures.CompressedImage; + let compressed = CompressedImage.loadFromArrayBuffer(buffer, url); + let base = new PIXI.BaseTexture(compressed); + let texture = new PIXI.Texture(base); + this._textureAvailable(url, col, row, texture); + } + }; + } + + loadOne() { + if (this.loadQueue.length > 0) { + let url = this.loadQueue.pop(); + let [col, row] = this.map.get(url); + let tile = [col, row, url]; + this.worker.postMessage({ command: "load", tiles: [tile] }); + } + } + + loadAll() { + let tiles = []; + while (this.loadQueue.length > 0) { + let url = this.loadQueue.pop(); + let [col, row] = this.map.get(url); + tiles.push([col, row, url]); + } + this.worker.postMessage({ command: "load", tiles }); + } + + cancel() { + super.cancel(); + this.worker.postMessage({ command: "abort" }); + } + + destroy() { + this.worker.postMessage({ command: "abort" }); + this.worker.terminate(); + this.worker = null; + super.destroy(); + } + } + + /** + * A layer of tiles that represents a zoom level of a DeepZoomImage as a grid + * of sprites. + * @constructor + * @param {number} level - the zoom level of the tile layer + * @param {DeepZoomImage} view - the zoomable image the layer belongs to + * @param {number} scale - the scale of the tile layer + * @param {number} cols - the number of columns of the layer + * @param {number} rows - the number of rows of the layer + * @param {number} width - the width of the layer in pixel + * @param {number} height - the height of the layer in pixel + * @param {number} tileSize - the size of a single tile in pixel + * @param {number} overlap - the overlap of the tiles in pixel + * @param {number} fadeInTime - time needed to fade in tiles if TweenLite is set + **/ + class Tiles extends PIXI.Container { + constructor( + level, + view, + scale, + cols, + rows, + width, + height, + tileSize, + overlap, + fadeInTime = 0.33 + ) { + super(); + this.debug = false; + this.showGrid = false; + this.view = view; + this.level = level; + this.cols = cols; + this.rows = rows; + this.pixelWidth = width; + this.pixelHeight = height; + this.tileSize = tileSize; + this.overlap = overlap; + this.needed = new Map(); // url as key, [col, row] as value + this.requested = new Set(); + this.available = new Map(); + this.scale.set(scale, scale); + this.tileScale = scale; + this.fadeInTime = fadeInTime; + this.keep = false; + if (this.view.preferWorker && view.info.compression.length > 0) + this.loader = new WorkerTileLoader(this); + else + this.loader = new PIXITileLoader(this, view.info.compression); + this.interactive = false; + this._highlight = null; + + this._info = null; + + this._centerPoint = null; + this._boundsRect = null; + + this.infoColor = Colors.random(); + this.pprint(); + this.destroyed = false; + } + + + + /** Tests whether all tiles are loaded. **/ + isComplete() { + return this.cols * this.rows === this.children.length + } + + /** Returns the highligh graphics layer for debugging purposes. + **/ + get highlight() { + if (this._highlight == null) { + let graphics = new PIXI.Graphics(); + graphics.beginFill(0xffff00, 0.1); + graphics.lineStyle(2, 0xffff00); + graphics.drawRect(1, 1, this.tileSize - 2, this.tileSize - 2); + graphics.endFill(); + graphics.interactive = false; + this._highlight = graphics; + } + return this._highlight + } + + /** Returns the highligh graphics layer for debugging purposes. + **/ + get info() { + if (this._info == null) { + let graphics = new PIXI.Graphics(); + graphics.lineStyle(4, 0xff0000); + graphics.interactive = false; + this._info = graphics; + this.addChild(this._info); + } + return this._info + } + + /** Helper method pretty printing debug information. **/ + pprint() { + if (this.debug) + console.log( + 'Tiles level: ' + + this.level + + ' scale: ' + + this.scale.x + + ' cols: ' + + this.cols + + ' rows: ' + + this.rows + + ' w: ' + + this.pixelWidth + + ' h: ' + + this.pixelHeight + + ' tsize:' + + this.tileSize + ); + } + + /** Computes the tile position and obeys the overlap. + * @param {number} col - The column of the tile + * @param {number} row - The row of the tile + * @returns {PIXI.Point} obj + **/ + tilePosition(col, row) { + let x = col * this.tileSize; + let y = row * this.tileSize; + let overlap = this.overlap; + if (col != 0) { + x -= overlap; + } + if (row != 0) { + y -= overlap; + } + return new PIXI.Point(x, y) + } + + /** Computes the tile size without overlap + * @param {number} col - The column of the tile + * @param {number} row - The row of the tile + * @returns {PIXI.Point} obj + **/ + tileDimensions(col, row) { + let w = this.tileSize; + let h = this.tileSize; + let pos = this.tilePosition(col, row); + if (col == this.cols - 1) { + w = this.pixelWidth - pos.x; + } + if (row == this.rows - 1) { + h = this.pixelHeight - pos.y; + } + return new PIXI.Point(w, h) + } + + /** Method to support debugging. Highlights the specified tile at col, row **/ + highlightTile(col, row) { + if (col > -1 && row > -1 && col < this.cols && row < this.rows) { + let graphics = this.highlight; + let dim = this.tileDimensions(col, row); + graphics.position = this.tilePosition(col, row); + graphics.clear(); + graphics.beginFill(0xff00ff, 0.1); + graphics.lineStyle(2, 0xffff00); + graphics.drawRect(1, 1, dim.x - 2, dim.y - 2); + graphics.endFill(); + this.addChild(this.highlight); + } else { + this.removeChild(this.highlight); + } + } + + /** Loads the tiles for the given urls and adds the tiles as sprites. + * @param {array} urlpos - An array of URL, pos pairs + * @param {boolean} onlyone - Loads only on tile at a time if true + **/ + loadTiles(urlpos, onlyone, refCol, refRow) { + if (this.showGrid) { + this.highlightTile(refCol, refRow); + } + urlpos.forEach(d => { + let [url, col, row] = d; + if (this.loader.schedule(url, col, row)) { + if (onlyone) { + return this.loader.loadOneTile() + } + } + }); + this.loader.loadAll(); + } + + /** Private method: add a red border to a tile for debugging purposes. **/ + _addTileBorder(tile, col, row) { + let dim = this.tileDimensions(col, row); + let graphics = new PIXI.Graphics(); + graphics.beginFill(0, 0); + graphics.lineStyle(2, 0xff0000); + graphics.drawRect(1, 1, dim.x - 2, dim.y - 2); + graphics.endFill(); + tile.addChild(graphics); + } + + /** Adds a tile. **/ + addTile(tile, col, row, url) { + if (this.available.has(url)) { + console.warn('Trying to add available tile'); + return + } + this.addChildAt(tile, 0); + this.available.set(url, tile); + if (this.destroyed) { + console.warn('Adding to destroyed tiles layer'); + } + // this._addTileBorder(tile, col, row) + } + + /** Called by the loader after each successfull loading of a single tile. + * Adds the sprite to the tile layer. + * @param {Object} tile - the loaded tile sprite + * @param {Number} col - the col position + * @param {Number} row - the rowposition + **/ + tileAvailable(tile, col, row, url) { + let pos = this.tilePosition(col, row); + if (this.showGrid) { + this._addTileBorder(tile, col, row); + } + tile.position = pos; + tile.interactive = false; + if (TweenLite) { + tile.alpha = 0; + TweenLite.to(tile, this.fadeInTime, { alpha: this.alpha }); + } + this.addTile(tile, col, row, url); + } + + /** Destroys the tiles layer and destroys the loader. Async load calls are + * cancelled. + **/ + destroy() { + this.destroyed = true; + this.loader.destroy(); + super.destroy({ children: true }); // Calls destroyChildren + this.available.clear(); + this.requested.clear(); + this.needed.clear(); + } + + destroyTile(url, tile) { + this.loader.unschedule(url); + this.removeChild(tile); + tile.destroy(); + this.available.delete(url); + } + + destroyTileByUrl(url) { + if (this.available.has(url)) { + let tile = this.available.get(url); + this.destroyTile(url, tile); + } + } + + /* Destroys the tiles which are not with the bounds of the app to free + * memory. + **/ + destroyTiles(quadTrees) { + let count = 0; + for (let [url, tile] of this.available.entries()) { + if (!quadTrees.has(url)) { + this.destroyTile(url, tile); + count += 1; + } + } + if (count && this.debug) + console.log('destroyTiles', this.level, count); + } + + destroyUnneededTiles() { + let count = 0; + for (let [url, tile] of this.available.entries()) { + if (!this.needed.has(url)) { + this.destroyTile(url, tile); + count += 1; + } + } + if (count && this.debug) + console.log('destroyUnneededTiles', this.level, count); + } + + highlightInfos() { + let graphics = this.info; + let color = this.infoColor; + graphics.clear(); + graphics.lineStyle(2, color); + for (let [col, row] of this.needed.values()) { + let dim = this.tileDimensions(col, row); + let pos = this.tilePosition(col, row); + graphics.beginFill(color, 0.2); + graphics.drawRect(pos.x + 1, pos.y + 1, dim.x - 2, dim.y - 2); + graphics.endFill(); + } + let r = this._boundsRect; + if (r != null) { + graphics.lineStyle(20, color); + graphics.drawRect(r.x, r.y, r.width, r.height); + graphics.moveTo(r.x, r.y); + graphics.lineTo(r.x + r.width, r.y + r.height); + + graphics.moveTo(r.x, r.y + r.height); + graphics.lineTo(r.x + r.width, r.y); + } + + let p = this._centerPoint; + if (p != null) { + graphics.drawCircle(p.x, p.y, 20); + } + } + + tintTiles(quadTrees) { + for (let [url, tile] of this.available.entries()) { + if (!quadTrees.has(url)) tile.tint = 0xff0000; + } + } + + untintTiles() { + for (let [url, tile] of this.available.entries()) { + tile.tint = 0xffffff; + } + } + } + + function isEven(n) { + return n % 2 == 0 + } + + + function printTileCacheInfos() { + let references = new Map(); + let multiples = 0; + for (let [url, tiles] of deepZoomTileCache.entries()) { + let count = tiles.size; + references.set(url, count); + if (count > 1) { + multiples += 1; + } + } + console.log({ multiples, references }); + } + /** + * A utility class that holds information typically provided by DZI files, i.e. + * height and width of the overall image, overlap, and image type. + * + * @constructor + * @param {obj} attrs - A JSON-Object holding the listed keys and values + * @example + * { + * "tileSize": 1024, + * "format": "jpeg", + * "overlap": 1, + * "height": 4794, + * "width": 4095, + * "clip": { "minLevel": 12, "maxLevel": 20, "startCol": 301436, "startRow": 354060 }, + * // optional: minLevel and maxLevel define the level bounds + * // startCol: first col at maxLevel + * // startRow: first row at maxLevel + * "path": "var/Vermeer/Vermeer_files", + * "type": "dzi", // optional: dzi (default) or map + * "urlTileTemplate": "{path}/{level}/{column}/{row}.{format}" + * // optional: {path}/{level}/{column}_{row}.{format} (default) or + * // a template String with the format of the URL + * } + */ + class DeepZoomInfo { + constructor(attrs) { + for (let key in attrs) { + this[key] = attrs[key]; + } + this.maxLevel = 0; // The highest level number, typically corresponds to the + // number in the file system for the folder with tiles + this.clip = this.clip || null; // e.g. { level: 12, col: 301436, row: 354060 } + this.type = this.type || 'dzi'; + this.urlTileTemplate = + this.urlTileTemplate || '{path}/{level}/{column}_{row}.{format}'; + this.setupDimensions(); + } + + /* Computes the needed number of layers from the width and height + * of the image. Note that this includes the level 0, i.e. 0 ... 4 + * means that 5 levels exist. + **/ + numLevels() { + let maxDimension = Math.max(this.width, this.height); + let boundary = this.type === 'dzi' ? 1 : this.tileSize; + let numLevels = 0; + while (maxDimension >= boundary) { + maxDimension /= 2; + numLevels++; + } + return numLevels + } + + /** Computes the scale at the given level. + * @param {number} level - The level of the wanted layer + * @returns {number} scale + **/ + getScale(level) { + let scale = 1; + if (this.type === 'dzi') { + scale = Math.pow(0.5, this.maxLevel - level + 1); + } else { + scale = Math.pow(0.5, this.maxLevel - level); + } + return scale + } + + /** Computes the scaled width and height of the given level. + * @param {number} level - The level of the wanted layer + * @returns {array} size - The width and height + **/ + getDimensions(level) { + let scale = this.getScale(level); + let w = Math.ceil(this.width * scale); + let h = Math.ceil(this.height * scale); + return [w, h] + } + + /** Computes the number of cols and rows of tiles. + * @param {number} level - The level of the wanted layer + * @returns {array} size - The cols and rows + **/ + getNumTiles(level) { + let dim = this.getDimensions(level); + let cols = Math.ceil(dim[0] / this.tileSize); + let rows = Math.ceil(dim[1] / this.tileSize); + if (this.clip) { + let rest = this.rests[level]; + if (rest) { + if (rest.restCol) { + cols += 1; + } + if (rest.restRows) { + rows += 1; + } + } + } + return [cols, rows] + } + + setupDimensions(loadBaseImage = false) { + /** Setup instance variables and load the base image, i.e. the largest + image that can be represented as a single tile. + @private + **/ + let ww = this.width; + let hh = this.height; + let scale = 1.0; + let level = 0; + let single = 0; + const tsize = this.tileSize; + + if (this.clip) { + this.baseLevel = this.clip.minLevel; + this.maxLevel = this.clip.maxLevel; + this.baseImage = null; + this.size = this.getDimensions(this.baseLevel); + this.offsets = {}; + this.rests = {}; + let startCol = this.clip.startCol; + let startRow = this.clip.startRow; + let floatStartCol = startCol; + let floatStartRow = startRow; + for (let i = this.maxLevel; i >= this.baseLevel; i--) { + this.offsets[i] = { startCol, startRow }; + let restCol = floatStartCol % 1; + let restRow = floatStartRow % 1; + this.rests[i] = { restCol, restRow }; + startCol = Math.floor(startCol / 2); + startRow = Math.floor(startRow / 2); + floatStartCol /= 2; + floatStartRow /= 2; + } + } else { + const boundary = this.type === 'dzi' ? 1.0 : tsize; + while (ww > boundary && hh > boundary) { + if (ww >= tsize && hh >= tsize) { + single += 1; + } + scale = scale / 2.0; + ww = Math.ceil(this.width * scale); + hh = Math.ceil(this.height * scale); + level += 1; + } + this.baseLevel = level - single; + this.maxLevel = this.numLevels() - 1; + this.baseURL = this.urlForTile(this.baseLevel, 0, 0, false); + + if (loadBaseImage) { + this.imageForURL(this.baseURL, e => { + this.size = [e.target.naturalWidth, e.target.naturalHeight]; + this.baseImage = e.target; + }); + } else { + this.baseImage = null; + this.size = this.getDimensions(this.baseLevel); + } + } + } + + get maxLoadableLevel() { + if (this.clip) { + return this.maxLevel + } + return this.type === 'dzi' ? this.maxLevel : this.maxLevel + } + + /** Computes the url for the given level, column and and row. + * @param {number} level - The level of the wanted layer + * @param {number} column - The column of the tile + * @param {number} row - The row of the tile + * @returns {string} url + **/ + urlForTile(level, column, row, compressed = true) { + let format = this.format; + if (compressed && this.compression) { + let supported = Capabilities.isIOS ? 'pvr' : 'dds'; + if (this.compression.indexOf(supported) >= 0) { + format = supported; + } + } + if (this.clip) { + let offset = this.offsets[level]; + if (offset) { + let { startCol, startRow } = offset; + column += startCol; + row += startRow; + } + } + let url = this.urlTileTemplate + .replace(/\{path\}/g, this.path) + .replace(/\{level\}/g, level) + .replace(/\{row\}/g, row) + .replace(/\{column\}/g, column) + .replace(/\{format\}/g, format); + return url + } + + /** Loads the image for the given url and executes a callback function + on completion. + * @param {string} url - The url of the tile + * @param {function} complete - The callback function + * @returns {Image} obj + **/ + imageForURL(url, complete) { + let img = new Image(); + img.onload = complete.bind(this); + img.src = url; + return img + } + + /** Computes the columns and rows as well as scaled width and height. + * @param {number} level - The level of the wanted layer + * @returns {array} [cols, rows, width, height] + **/ + dimensions(level) { + let dim = this.getDimensions(level); + let tiles = this.getNumTiles(level); + return [tiles[0], tiles[1], dim[0], dim[1]] + } + + test() { + //console.log("w=" + this.width + " h=" + this.height + " maxlevel=" + this.maxLevel + " base=" + this.baseLevel) + for (let i = 0; i <= this.maxLevel; i++) { + console.log( + ' ' + + i + + ' -> ' + + this.getScale(i) + + ' [' + + this.dimensions(i) + + ']' + ); + } + console.log(this.urlForTile(this.baseLevel, 0, 0)); + } + } + + /** + * A utility class that describes a quad tree of tiles. Each tile on a given + * level has up to four corresponding tiles on the next level. A TileQuadNode + * uses the attributes nw (i.e. northwest), ne, sw, se to link to the + * quad nodes on the next level. The previous attributes links to the quad + * one level below that holds the given quad as nw, ne, sw, or se. + * We use this node class because we need a representation of tiles that are + * needed but not loaded yet to compute tiles which can be abandoned to reduce + * the memory pressure. + * + * @constructor + * @param {level} Number - The level the quad node belongs to + * @param {col} Number - The col of the quad + * @param {row} Number - The level the quad node belongs to + * @param {url} String - The level the quad node belongs to + */ + class TileQuadNode { + constructor(level, col, row, url) { + this.level = level; + this.col = col; + this.row = row; + this.url = url; + this.nw = null; + this.ne = null; + this.sw = null; + this.se = null; + this.previous = null; + } + + /** Return True if this node has no successors and can be used as + an indicator of tiles to free. + **/ + noQuads() { + if (this.previous === null) return false + return ( + this.nw === null && + this.ne === null && + this.sw === null && + this.se === null + ) + } + + /** Unlink the given quad node + + * @param {node} TileQuadNode - The TileQuadNode to remove + **/ + unlink(node) { + if (this.nw === node) this.nw = null; + if (this.ne === node) this.ne = null; + if (this.sw === node) this.sw = null; + if (this.se === node) this.se = null; + } + + /** Link this quad node to the given previous node. Use the north + * and west flags to address nw, ne, sw, and se. + + * @param {node} TileQuadNode - The TileQuadNode to remove + * @param {north} Boolean - Link to north (true) or south (false) + * @param {west} Boolean - Link to west (true) or east (false) + **/ + link(north, west, previous) { + this.previous = previous; + if (north) { + if (west) { + previous.nw = this; + } else { + previous.ne = this; + } + } else { + if (west) { + previous.sw = this; + } else { + previous.se = this; + } + } + } + } + + + /** + * The main class of a deeply zoomable image that is represented by a hierarchy + * of tile layers for each zoom level. This gives the user the impression that + * even huge pictures (up to gigapixel-images) can be zoomed instantaneously, + * since the tiles at smaller levels are scaled immediately and overloaded by + * more detailed tiles at the larger level as fast as possible. + + * @constructor + * @param {DeepZoomInfo} deepZoomInfo - Information extracted from a JSON-Object + */ + class DeepZoomImage extends PIXI.Container { + constructor( + deepZoomInfo, + { + debug = false, + shadow = false, + center = false, + world = null, // Defines the world bounds the images lives in + highResolution = true, + autoLoadTiles = true, + preferWorker = false, + minimumLevel = 0, + alpha = 1, + app = window.app + } = {} + ) { + super(); + this.app = app; + this.debug = debug; + this.shadow = shadow; + this.world = world; + this.preferWorker = preferWorker; + this.resolution = highResolution + ? Math.round(window.devicePixelRatio) + : 1; + this.alpha = alpha; + this.fastLoads = 0; + this.autoLoadTiles = autoLoadTiles; + this.minimumLevel = minimumLevel; + this.quadTrees = new Map(); // url as keys, TileQuadNodes as values + this.setup(deepZoomInfo, center); + } + + get point() { + if (this._point == null) { + let graphics = new PIXI.Graphics(); + graphics.lineStyle(2, 0x00ff00); + graphics.drawCircle(0, 0, 2); + graphics.interactive = false; + this._point = graphics; + } + return this._point + } + + /** Reads the DeepZoomInfo object and initializes all tile layers. + * Called by the constructor. + * Creates the sprite for the loaded texture and add the sprite to the tile + * layer. + * @param {Object} deepZoomInfo - the DeepZoomInfo instance + * @param {boolean} center - If true ensures that the pivot is set to the center + **/ + setup(deepZoomInfo, center) { + this.info = deepZoomInfo; + this.interactive = true; + this.tileLayers = {}; + + this._foreground = null; + this.tileContainer = new PIXI.Container(); + this.tileContainer.interactive = false; + + let [w, h] = this.baseSize; + if (this.shadow) { + this.filters = [new PIXI.filters.DropShadowFilter(45, 3)]; + } + this.addChild(this.tileContainer); + + if (deepZoomInfo.clip) { + let mask = new PIXI.Graphics(); + mask.beginFill(1, 1); + mask.drawRect(0, 0, w, h); + mask.endFill(); + this.mask = mask; + mask.alpha = 0; + this.addChild(mask); + this.minimumLevel = deepZoomInfo.baseLevel; + } + this.currentLevel = Math.max(this.minimumLevel, deepZoomInfo.baseLevel); + console.log("autoLoadTiles", this.autoLoadTiles); + if (this.autoLoadTiles) { + this.setupTiles(center); + } + } + + /** Default setup method for tiles. Loads all tiles of the current level. + Can be overwritten in subclasses. + @param {boolean} center - If true ensures that the pivot is set to the center + **/ + setupTiles(center = false) { + // First load background tile + let tiles = this.ensureAllTiles(this.currentLevel); + if (center) { + this.pivot.set(w / 2, h / 2); + } + let scaleLevel = this.levelForScale(1); + this.ensureAllTiles(scaleLevel); + } + + removeTileQuadNode(level, col, row, url) { + if (this.quadTrees.has(url)) { + let quad = this.quadTrees.get(url); + this.tileQuadRemoved(quad); + this.quadTrees.delete(url); + } + } + + addTileQuadNode(level, col, row, url) { + if (this.quadTrees.has(url)) return this.quadTrees.get(url) + let quad = new TileQuadNode(level, col, row, url); + this.quadTrees.set(url, quad); + this.tileQuadAdded(quad); + return quad + } + + tileQuadRemoved(quad) { + let below = quad.previous; + // if (this.debug) console.log("tileQuadRemoved", quad) + if (below) { + below.unlink(quad); + if (below.noQuads()) { + if (this.debug) console.log('Removed tile below'); + let levelBelow = quad.level - 1; + if (levelBelow < this.minimumLevel) return + let c = Math.floor(quad.col / 2); + let r = Math.floor(quad.row / 2); + let urlBelow = this.info.urlForTile(levelBelow, c, r); + if (this.quadTrees.has(urlBelow)) { + this.removeTileQuadNode(levelBelow, c, r, urlBelow); + } + } + } + } + + tileQuadAdded(quad) { + let levelBelow = quad.level - 1; + if (levelBelow < this.minimumLevel) return + //if (this.debug) console.log("tileQuadAdded", quad) + let c = Math.floor(quad.col / 2); + let r = Math.floor(quad.row / 2); + let urlBelow = this.info.urlForTile(levelBelow, c, r); + let below = null; + if (!this.quadTrees.has(urlBelow)) { + below = this.addTileQuadNode(levelBelow, c, r, urlBelow); + quad.link(isEven(quad.row), isEven(quad.col), below); + } + } + + /** Returns the tile layer level that corresponds to the given scale. + * @param {number} scale - the scale factor + **/ + levelForScale(scale) { + let level = Math.round(Math.log2(scale * this.resolution)); // Math.floor(Math.log2(event.scale))+1 + let newLevel = this.info.baseLevel + Math.max(level, 0); + return Math.min(newLevel, this.info.maxLoadableLevel) + } + + /** Returns the tile layer level that corresponds to the given scale. + * @param {number} scale - the scale factor + **/ + levelAndAlphaForScale(scale) { + let value = Math.log2(scale * this.resolution); + let level = Math.round(value); + let newLevel = this.info.baseLevel + Math.max(level, 0); + + return { level: Math.min(newLevel, this.info.maxLoadableLevel), alpha: value - level } + } + + /** Adds a tile layer to the DeepZoomImage. + * @param {string} key - the access key + * @param {Tiles} tiles - the tile layer object + **/ + addTiles(key, tiles) { + if (key in this.tileLayers) { + console.warn('Tiles already availabl', key); + } + this.tileContainer.addChild(tiles); + this.tileLayers[key] = tiles; + } + + destroyTiles(key) { + let tiles = this.tileLayers[key]; + this.tileContainer.removeChild(tiles); + tiles.destroy(); + delete this.tileLayers[key]; + } + + /** Getter for PIXI.Container foreground layer. + * Adds a PIXI.Container if necessary. + **/ + get foreground() { + if (this._foreground == null) { + this._foreground = new PIXI.Container(); + this.addChild(this._foreground); + } + return this._foreground + } + + /** Getter for the DeepZoomInfo base level size. + **/ + get baseSize() { + return this.info.getDimensions(this.info.baseLevel) + } + + /** Getter for the current scaled size in pixels. + **/ + get pixelSize() { + let [w, h] = this.baseSize; + return [w * this.scale.x, h * this.scale.y] + } + + /** Getter for the max scale factor. + **/ + get maxScale() { + let delta = this.info.maxLevel - this.info.baseLevel; + return Math.pow(2, delta) / this.resolution * 2 + } + + /** Getter for the current width. + **/ + get width() { + return this.pixelSize[0] + } + + /** Getter for the current height. + **/ + get height() { + return this.pixelSize[1] + } + + + /* Overrides PIXI.Container.hitArea() + * Allows to optimize the hit testing. Container with hit areas are directly + * hit tested without consideration of children. + */ + get hitArea() { + // Defining the hitArea resulted hitting the scatter in masked area + // when a mask was used (@Tüsch[submaps]). Removing the hitArea() altogether + // broke the interaction in other projects (@googleart). + // Fix: When masked, the hitArea is ignored by returning null. + // TODO: test if childs are hittested, without setting interactiveChildren. + // Opel, 03-05-2018 + if (this.mask) { + return null + } + return this + } + + /* Overrides PIXI.Container.contains() + * Allows to optimize the hit testing. + */ + contains(x, y) { + let [w, h] = this.baseSize; + return x >= 0 && x <= w && y >= 0 && y <= h + } + + /** Overrides PIXI.Container._calculateBounds() + * Only considers the base size and reduces the calculation to a single + * rect. + */ + _calculateBounds() { + let [w, h] = this.baseSize; + this._bounds.addFrame(this.transform, 0, 0, w, h); + } + + /** Overrides PIXI.Container.calculateBounds() + * Skips the children and only considers the deep zoom base size. Calls + * the also overwritten _calculateBounds method. + */ + calculateBounds() { + this._bounds.clear(); + this._calculateBounds(); + this._lastBoundsID = this._boundsID; + } + + /** Returns a single sprite that can be used a thumbnail representation of + * large images. + * @return {Sprite} sprite - A sprite with a single tile texture + */ + thumbnail() { + return new PIXI.Sprite.fromImage(this.info.baseURL) + } + + /** Returns a list of all tiles of a given level. + * @param {Tiles} tiles - the grid of tiles + * @param {number} level - The zoom level of the grid + * @return {Array[]} - An array of [url, col, row] arrays + **/ + allTiles(tiles, level) { + let result = []; + for (let col = 0; col < tiles.cols; col++) { + for (let row = 0; row < tiles.rows; row++) { + let url = this.info.urlForTile(level, col, row); + result.push([url, col, row]); + } + } + return result + } + + worldBounds() { + let viewBounds = this.app.scene.bounds; + // Using getBounds extends visible scope after loading tiles and leads + // to excessive loading + if (this.world != null) { + let bounds = this.world.bounds; + let x = Math.max(-bounds.width, bounds.x); + let y = Math.max(-bounds.height, bounds.y); + let width = Math.min(viewBounds.width, bounds.width); + let height = Math.min(viewBounds.height, bounds.height); + //console.log("worldBounds new", { x, y, width, height }) + return { x, y, width, height } + } + //console.log("worldBounds old", viewBounds) + return viewBounds + } + + /** Loads all tiles that are needed to fill the app bounds. + * @param {Tiles} tiles - the grid of tiles + * @param {number} level - The zoom level of the grid + * @param {boolean} debug + * @return {Array[]} - An array of [url, col, row] arrays + */ + neededTiles(tiles, level, debug = false) { + let needed = []; + let tsize = tiles.tileSize; + let worldBounds = this.worldBounds(); + let maxWidth = worldBounds.width; + let maxHeight = worldBounds.height; + + let pointInWindow = new PIXI.Point(); + let worldTopLeft = new PIXI.Point(worldBounds.x, worldBounds.y); + let worldBottomRight = new PIXI.Point(worldBounds.x + maxWidth, worldBounds.y + maxHeight); + let worldCenter = new PIXI.Point(worldBounds.x + maxWidth / 2, worldBounds.y + maxHeight / 2); + let tilesCenter = tiles.toLocal(worldCenter); + + let topLeft = tiles.toLocal(worldTopLeft); + let bottomRight = tiles.toLocal(worldBottomRight); + tiles._centerPoint = tilesCenter; + let bounds = new PIXI.Rectangle( + topLeft.x, + topLeft.y, + bottomRight.x - topLeft.x, + bottomRight.y - topLeft.y); + + tiles._boundsRect = bounds; + + /* UO: we need a toLocal call here since the transform may need an update + which is guaranteed by the toLocal method. */ + let centerCol = Math.floor(tilesCenter.x / tsize); + let centerRow = Math.floor(tilesCenter.y / tsize); + + // Expand because we want to test for included centers + bounds.x -= tsize / 2; + bounds.y -= tsize / 2; + bounds.width += tsize; + bounds.height += tsize; + + try { + let maxTilesWidth = Math.ceil(maxWidth / tsize); + let maxTilesHeight = Math.ceil(maxHeight / tsize); + + maxTilesWidth += 2; + maxTilesHeight += 2; + + let startCol = Math.max(0, centerCol - maxTilesWidth); + let endCol = Math.min(tiles.cols, centerCol + maxTilesWidth); + + let startRow = Math.max(0, centerRow - maxTilesHeight); + let endRow = Math.min(tiles.rows, centerRow + maxTilesHeight); + + for (let col = startCol; col < endCol; col++) { + let cx = (col + 0.5) * tsize; + for (let row = startRow; row < endRow; row++) { + let cy = (row + 0.5) * tsize; + let tileCenter = new PIXI.Point(cx, cy); + if (bounds.contains(tileCenter.x, tileCenter.y)) { + let url = this.info.urlForTile(level, col, row); + needed.push([url, col, row]); + } + } + } + } catch (error) { + console.warn(error.message); + } + return { centerCol, centerRow, needed } + } + + + + + /** Returns all changed tiles for a given level. + * @param {Tiles} tiles - the grid of tiles + * @param {number} level - The zoom level of the grid + * @return {object} - An object with the keys added and removed which values are [url, col, row] arrays + */ + changedTiles(tiles, level) { + if (this.debug) console.time('changedTiles'); + let changed = { added: [], removed: [] }; + let newNeeded = new Map(); + let { centerCol, centerRow, needed } = this.neededTiles(tiles, level); + needed.forEach(d => { + let [url, col, row] = d; + newNeeded.set(url, [col, row]); + if (!tiles.requested.has(url)) { + changed.added.push(d); + } + }); + for (let url of tiles.needed.keys()) { + if (!newNeeded.has(url)) { + let [col, row] = tiles.needed.get(url); + changed.removed.push([url, col, row]); + } + } + tiles.needed = newNeeded; + if (this.debug) console.log(newNeeded); + if (this.debug) console.timeEnd('changedTiles'); + return { centerCol, centerRow, changed } + } + + /** Populates all tiles for a given level. + * @param {Tiles} tiles - the grid of tiles + * @param {number} level - The zoom level of the grid + */ + populateAllTiles(tiles, level) { + let all = this.allTiles(tiles, level); + for (let [url, col, row] of all) { + this.addTileQuadNode(level, col, row, url); + } + tiles.loadTiles(all, false, 0, 0); + } + + /** Loads all tiles that are needed to fill the browser window. + * If the optional about parameter is provided (as a point with col as x, + * and row as y attr) the tiles are sorted by the distance to this point. + * + * @param {Tiles} tiles - the grid of tiles + * @param {number} level - The zoom level of the grid + * Optional parameter: + * @param {boolean} onlyone - if true only one tile is loaded + * @param {PIXI.Point} about - point of interaction + */ + populateTiles(tiles, level, { onlyone = false, about = null } = {}) { + if (tiles.isComplete()) + return + let referenceCol = -1; + let referenceRow = -1; + let { centerCol, centerRow, changed } = this.changedTiles(tiles, level); + if (about != null) { + // We want to load tiles in the focus of the user first, therefore + // we sort according to the distance of the focus of interaction + let refPoint = this.toLocal(about); + let scaledTileSize = tiles.tileSize * tiles.tileScale; + referenceCol = Math.floor(refPoint.x / scaledTileSize); + referenceRow = Math.floor(refPoint.y / scaledTileSize); + } + else { + referenceCol = centerCol; + referenceRow = centerRow; + } + referenceCol = centerCol; + referenceRow = centerRow; + + let removed = changed.removed; + for (let [url, col, row] of removed) { + this.removeTileQuadNode(level, col, row, url); + } + let added = changed.added; + if (added.length == 0) return + for (let [url, col, row] of added) { + this.addTileQuadNode(level, col, row, url); + } + let ref = new PIXI.Point(referenceCol, referenceRow); + // Note: The array must be sorted in a way that the nearest tiles + // are at the end of the array since the load queue uses Array.push + // Array.pop + added.sort((a, b) => { + let aa = new PIXI.Point(a[1], a[2]); + let bb = new PIXI.Point(b[1], b[2]); + let da = Points.distance(aa, ref); + let db = Points.distance(bb, ref); + return db - da + }); + tiles.loadTiles(added, onlyone, referenceCol, referenceRow); + } + + /** Private method: creates all tiles for a given level. + * @param {number} level - The zoom level of the grid + * @return {Tiles} - tiles + */ + _createTiles(key, level) { + let [cols, rows, w, h] = this.info.dimensions(level); + let increasedLevels = level - this.info.baseLevel; + let invScale = Math.pow(0.5, increasedLevels); + let tiles = new Tiles( + level, + this, + invScale, + cols, + rows, + w, + h, + this.info.tileSize, + this.info.overlap + ); + this.addTiles(key, tiles); + if (this.info.clip) { + let rest = this.info.rests[level]; + if (rest) { + let x = rest.restCol * this.info.tileSize * invScale; + let y = rest.restRow * this.info.tileSize * invScale; + tiles.x = -x; + tiles.y = -y; + } + } + return tiles + } + + /** Ensures that all needed tiles of a given level are loaded. Creates + * a new Tiles layer if necessary + * @param {number} level - The zoom level of the grid + * @return {Tiles} tiles + */ + ensureTiles(level, about) { + let key = level.toString(); + if (key in this.tileLayers) { + let tiles = this.tileLayers[key]; + this.populateTiles(tiles, level, { about: about }); + return tiles + } + let tiles = this._createTiles(key, level); + this.populateTiles(tiles, level, { about: about }); + //console.log("ensureTiles", level) + return tiles + } + + untintTiles(level) { + let key = level.toString(); + if (key in this.tileLayers) { + let tiles = this.tileLayers[key]; + } + } + + /** Ensures that all tiles of a given level are loaded. + * @param {number} level - The zoom level of the grid + */ + ensureAllTiles(level) { + let key = level.toString(); + if (key in this.tileLayers) { + let tiles = this.tileLayers[key]; + this.populateAllTiles(tiles, level); + tiles.keep = true; + return + } + let tiles = this._createTiles(key, level); + this.populateAllTiles(tiles, level); + tiles.keep = true; + return tiles + } + + hideTilesAboveLevel(level) { + Object.keys(this.tileLayers).forEach(key => { + let tiles = this.tileLayers[key]; + if (tiles.level > level) { + tiles.visible = false; + } + }); + } + + /** Destroys all tiles above a given level to ensure that the memory can + * be reused. + * @param {number} level - The zoom level of the grid + */ + destroyTilesAboveLevel(level) { + Object.keys(this.tileLayers).forEach(key => { + let tiles = this.tileLayers[key]; + if (tiles.level > level && !tiles.keep) { + for (let url of tiles.available.keys()) { + let quad = this.quadTrees.get(url); + if (quad) this.removeTileQuadNode(quad); + } + this.destroyTiles(key); + } + }); + } + + destroyAllTiles() { + Object.keys(this.tileLayers).forEach(key => { + this.destroyTiles(key); + }); + } + + /** + * Tint tiles in all layers that are no longer needed + * + * @memberof DeepZoomImage + */ + tintObsoleteTiles() { + Object.keys(this.tileLayers).forEach(key => { + let tiles = this.tileLayers[key]; + tiles.untintTiles(); + if (!tiles.keep) { + tiles.tintObsoleteTiles(); + } + }); + } + + + /** + * Destroy tiles in all layers that are no longer needed + * + * @memberof DeepZoomImage + */ + destroyUnneededTiles() { + Object.keys(this.tileLayers).forEach(key => { + let tiles = this.tileLayers[key]; + if (!tiles.keep) { + tiles.destroyUnneededTiles(); + } + }); + } + + /** + * Destroy tiles in all layers that are no longer needed + * + * @memberof DeepZoomImage + */ + destroyObsoleteTiles() { + Object.keys(this.tileLayers).forEach(key => { + let tiles = this.tileLayers[key]; + if (!tiles.keep) { + tiles.destroyObsoleteTiles(); + } + }); + } + + /** + * Destroy tiles in all layers that are not part of the + * visible quadTrees + * + * @memberof DeepZoomImage + */ + destroyTiles() { + Object.keys(this.tileLayers).forEach(key => { + let tiles = this.tileLayers[key]; + if (!tiles.keep) { + tiles.destroyTiles(this.quadTrees); + } + }); + } + + /* Tint all tiles + * @param {number} level - The zoom level of the grid + */ + tintTilesBelowLevel(level) { + Object.keys(this.tileLayers).forEach(key => { + let tiles = this.tileLayers[key]; + if (tiles.level < level) { + tiles.tintTiles(this.quadTrees); + } + }); + } + + /** + * Ensure that the given tiles layer is the topmost one and visible. + * @param {*} tiles + */ + bringTilesToFront(tiles) { + this.tileContainer.addChild(tiles); + tiles.visible = true; + } + + /** A callback function that can be used by a Scatter view to inform + * the zoomable image that it has been moved, rotated or scaled, and should + * load tiles accordingly. + * @param {PIXI.Point} translated - the movement of the scatter + * @param {number} scale - the zoom factor + * @param {PIXI.Point} about - the anchor point of the zoom + * @param {boolean} fast - informs the callback to return as fast as possible, + * i.e. after loading a single tile + * @param {boolean} debug - log debug infos + */ + transformed(event) { + let key = this.currentLevel.toString(); + let currentTiles = this.tileLayers[key]; + if (typeof currentTiles == 'undefined') { + return + } + if (event.fast) { + this.fastLoads += 1; + this.populateTiles(currentTiles, this.currentLevel, { + onlyone: false, + about: event.about + }); + if (this.fastLoads == 3) { + this.fastLoads = 0; + } + else { + return + } + } + if (event.scale == null) { + this.ensureTiles(this.currentLevel, event.about); + return + } + + let level = this.levelForScale(event.scale); + let newLevel = Math.max(level, this.minimumLevel); + if (newLevel != this.currentLevel) { + if (!currentTiles.keep) { + currentTiles.loader.cancel(); + } + this.hideTilesAboveLevel(newLevel); + currentTiles = this.ensureTiles(newLevel, event.about); + this.currentLevel = newLevel; + } else { + this.ensureTiles(this.currentLevel, event.about); + } + this.bringTilesToFront(currentTiles); + if (this._foreground) { + this.addChild(this._foreground); + } + } + + /** + *Activates the textures on the DeepZoomImage. + * + * @memberof DeepZoomImage + */ + activate() { + this.destroyTilesAboveLevel(this.currentLevel); + this.ensureTiles(this.currentLevel, null); + //console.log("Activate Textures!", this.currentLevel) + } + + /** + *Dectivates the textures on the DeepZoomImage. + * + * @memberof DeepZoomImage + */ + deactivate() { + this.destroyAllTiles(); + Object.keys(this.tileLayers).forEach(key => { + this.destroyTiles(key); + }); + this.tileContainer.destroy({ children: true }); + printTileCacheInfos(); + } + + throwFinished() { + console.log("throwFinished"); + let key = this.currentLevel.toString(); + let currentTiles = this.tileLayers[key]; + if (typeof currentTiles == 'undefined') { + return + } + + this.ensureTiles(this.currentLevel); + // let all = new Set() + // for (let tile of currentTiles.children) { + // all.add(tile.url) + // } + // let { centerCol, centerRow, needed } = this.neededTiles(currentTiles, this.currentLevel) + // for (let [url, col, row] of needed) { + // all.delete(url) + // } + // for (let url of all) { + // currentTiles.destroyTileByUrl(url) + // } + // currentTiles.loader.loader.reset() + } + } + + let globalScatterLoaderCanvas = null; + + class ScatterLoader extends CardLoader { + + get scatter() { + return this.src + } + + unscaledSize() { + let displayObject = this.scatter.displayObject; + let w = displayObject.width; + let h = displayObject.height; + return [w / displayObject.scale.x, h / displayObject.scale.y] + } + + scaledSize() { + let displayObject = this.scatter.displayObject; + let w = displayObject.width; + let h = displayObject.height; + return [w, h] + } + + cloneScatterImage() { + let w = this.scatter.width; + let h = this.scatter.height; + let isSprite = this.scatter.displayObject instanceof PIXI.Sprite; + let isDeepZoom = this.scatter.displayObject instanceof DeepZoomImage; + let resolution = app.renderer.resolution; + if (isSprite) { + w = this.scatter.displayObject.texture.width; + h = this.scatter.displayObject.texture.height; + } + else if (isDeepZoom) { + let [ww, hh] = this.scatter.displayObject.baseSize; + w = ww; + h = hh; + } + if (globalScatterLoaderCanvas === null) { + globalScatterLoaderCanvas = document.createElement('canvas'); + } + let canvas = globalScatterLoaderCanvas; + canvas.width = w; + canvas.height = h; + let renderer = new PIXI.WebGLRenderer(w, h, { + view: canvas, + resolution: resolution}); + + let displayObject = this.scatter.displayObject; + let x = displayObject.x; + let y = displayObject.y; + let rot = displayObject.rotation; + let sx = displayObject.scale.x; + let sy = displayObject.scale.y; + displayObject.rotation = 0; + // The Safari WebGLRenderer wants everything flipped + // See https://github.com/pixijs/pixi.js/issues/2283 + displayObject.x = 0; + if (Capabilities.isSafari) { + displayObject.y = h; + displayObject.scale.set(1, -1); // sx, -sy) + } + else { + displayObject.y = 0; + displayObject.scale.set(1, 1); + } + if (isSprite) { + displayObject.width = w; + displayObject.height = h; + } + renderer.render(displayObject); + displayObject.rotation = rot; + displayObject.x = x; + displayObject.y = y; + displayObject.scale.set(sx, sy); + + let url = canvas.toDataURL(); + return [x, y, w, h, url] + } + + load(domNode) { + return new Promise((resolve, reject) => { + let isImage = domNode instanceof HTMLImageElement; + let isSprite = this.scatter.displayObject instanceof PIXI.Sprite; + let image = (isImage) ? domNode : document.createElement("img"); + let [x, y, w, h, cloneURL] = this.cloneScatterImage(); + let [ww, hh] = this.unscaledSize(); + image.onload = (e) => { + if (!isImage) + domNode.appendChild(image); + this.x = x; + this.y = y; + this.wantedWidth = ww; + this.wantedHeight = hh; + this.scale = 1; + this.rotation = this.scatter.rotation; + resolve(this); + }; + image.onerror = (e) => { + reject(this); + }; + image.src = cloneURL; + }) + } + + } + + class FlipEffect { + + constructor(scatter, domScatterContainer, flipTemplate, backLoader) { + this.flipped = false; + this.scatter = scatter; + this.backLoader = backLoader; + this.scatterLoader = new ScatterLoader(scatter); + this.domFlip = new DOMFlip(domScatterContainer, flipTemplate, + this.scatterLoader, + backLoader, { + onBack: this.backCardClosed.bind(this) + }); + this.setupInfoButton(); + } + + startFlip() { + let center = this.flipCenter(); + let loader = this.backLoader; + this.domFlip.load().then((domFlip) => { + this.scatter.displayObject.visible = false; + domFlip.centerAt(center); + domFlip.zoom(this.scatter.scale); + let target = this.constraintFlipCenter(center, loader); + console.log("FlipEffect.startFlip", target, loader); + domFlip.start({targetCenter: target}); + }); + } + + unscaledSize() { + return this.scatterLoader.unscaledSize() + } + + flipCenter() { + let isSprite = this.scatter.displayObject instanceof PIXI.Sprite; + let resolution = (isSprite) ? app.renderer.resolution : 1; + let center = this.scatter.center; + let canvas = app.renderer.view; + let domNode = this.domFlip.domScatterContainer.element; + let page = window.convertPointFromNodeToPage(canvas, + center.x*resolution, + center.y*resolution); + let local = window.convertPointFromPageToNode(domNode, page.x, page.y); + return local + } + + constraintFlipCenter(center, loader) { + let w = loader.wantedWidth; + let h = loader.wantedHeight; + console.log("constraintFlipCenter", w, h); + let canvas = app.renderer.view; + let x = center.x; + let y = center.y; + if (x < w/2) + x = w/2; + if (y < h/2) + y = h/2; + if (x > canvas.width) + x = canvas.width - w/2; + if (y > canvas.height) + y = canvas.height - h/2; + return { x, y } + } + + setupInfoButton() { + let iscale = 1.0 / this.scatter.scale; + this.infoBtn = new PIXI.Graphics(); + this.infoBtn.beginFill(0x333333); + this.infoBtn.lineStyle(4, 0xFFFFFF); + this.infoBtn.drawCircle(0, 0, 22); + this.infoBtn.endFill(); + + this.infoBtn.beginFill(0xFFFFFF); + this.infoBtn.lineStyle(0); + this.infoBtn.drawCircle(0, -8, 4); + this.infoBtn.endFill(); + + this.infoBtn.lineStyle(6, 0xFFFFFF); + this.infoBtn.moveTo(0, -2); + this.infoBtn.lineTo(0, 14); + this.infoBtn.endFill(); + + this.infoBtn.on('click', (e) => { this.infoSelected(); }); + this.infoBtn.on('tap', (e) => { this.infoSelected(); }); + + this.infoBtn.interactive = true; + this.infoBtn.width = 44; + this.infoBtn.height = 44; + this.infoBtn.pivot.x = 30; + this.infoBtn.pivot.y = 30; + + let displayObject = this.scatter.displayObject; + let [w, h] = this.unscaledSize(); + this.infoBtn.position = new PIXI.Point(w, h); + if (displayObject.foreground) { + this.infoBtn.scale.x = iscale; + this.infoBtn.scale.y = iscale; + displayObject.foreground.addChild(this.infoBtn); + } + else { + displayObject.addChild(this.infoBtn); + } + + this.scatter.addTransformEventCallback(e => { + let displayObject = this.scatter.displayObject; + if (displayObject.foreground) { + if (e.scale) { + let iscale = 1.0 / e.scale; + this.infoBtn.scale.x = iscale; + this.infoBtn.scale.y = iscale; + } + } + }); + } + + setupButton(url) { + let svgImage = new Image(); + let canvas = document.createElement('canvas'); + canvas.width = 88 * 4; + canvas.height = 44 * 4; + svgImage.onload = e => { + let displayObject = this.scatter.displayObject; + canvas.getContext ('2d').drawImage(svgImage, 0, 0, + canvas.width, canvas.height); + let texure = new PIXI.Texture(new PIXI.BaseTexture(canvas)); + this.infoBtn = new PIXI.Sprite(texure); + this.infoBtn.anchor.set(0.5, 0.5); + if (displayObject.foreground) { + displayObject.foreground.addChild(this.infoBtn); + } + else { + displayObject.addChild(this.infoBtn); + } + this.infoBtn.scale.set(0.5, 0.5); + + let [w, h] = this.unscaledSize(); + this.infoBtn.position = new PIXI.Point(w, h); + this.infoBtn.interactive = true; + this.infoBtn.updateTransform(); + this.infoBtn.on('click', (e) => { this.infoSelected(); }); + this.infoBtn.on('tap', (e) => { this.infoSelected(); }); + }; + svgImage.src = url; + } + + infoSelected() { + this.startFlip(); + } + + backSelected() { + this.domFlip.start(); + } + + backCardClosed() { + /*** The flip effect should now be in it's initial state again. All + memory should be freed. ***/ + let displayObject = this.scatter.displayObject; + displayObject.visible = true; + this.domFlip.fadeOutAndRemove(); + this.flipped = false; + } + + targetRotation(alpha) { + let ortho = 90; + let rest = alpha % ortho; + let delta = 0.0; + if (rest > (ortho / 2.0)) { + delta = ortho - rest; + } + else { + delta = -rest; + } + return delta + } + } + + /* globals Power2, Sine */ + /*eslint no-console: ["error", { allow: ["log", "info", "error"] }]*/ + + /** + * Callback for the flippable onStart function. + * + * @callback onStartCallback + * @param {Flippable} flippable - A reference to the flippable (also this refers to the flippable). + */ + + /** + * Callback for the flippable onUpdate function. + * + * @callback onUpdateCallback + * @param {Flippable} flippable - A reference to the flippable (also this refers to the flippable). + */ + + /** + * Callback for the flippable onComplete function. + * + * @callback onCompleteCallback + * @param {Flippable} flippable - A reference to the flippable (also this refers to the flippable). + */ + + /** + * Class that represents a PixiJS Flippable. + * + * @example + * const front = PIXI.Sprite.fromImage('./assets/front.jpg') + * const back = PIXI.Sprite.fromImage('./assets/back.jpg') + * app.scene.addChild(front) + * + * // Create the flippable + * const flippable = new Flippable(front, back, app.renderer) + * + * front.interactive = true + * front.on('click', event => flippable.toggle()) + * + * @class + * @extends PIXI.projection.Camera3d + * @see {@link https://github.com/pixijs/pixi-projection|PixiJS Projection} + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/flippable.html|DocTest} + */ + class Flippable extends PIXI.projection.Camera3d { + + /** + * Creates an instance of a Flippable. + * + * @constructor + * @param {PIXI.DisplayObject} front - The object to show initially. Should have been added to the scene. + * @param {PIXI.DisplayObject} back - The object to show on the backside. Should have not been added to the scene. + * @param {PIXI.WebGLRenderer|PIXI.CanvasRenderer} renderer - The renderer of the application. + * @param {object} [opts] - An options object which can contain the following properties. + * @param {number} [opts.duration=1] - The duration of the flip animation in seconds. + * @param {GSAP.Ease} [opts.ease=Power2.easeOut] - The ease of the flip animation. + * @param {boolean} [opts.shadow=false] - Should be a shadow been display during the animation? + * @param {numer} [opts.eulerX=0] - The shift of the x-axis during the animation. + * @param {numer} [opts.eulerY=0] - The shift of the y-axis during the animation. + * @param {GSAP.Ease} [opts.eulerEase=Sine.easeOut] - The ease of the shift. + * @param {boolean} [opts.useBackTransforms=false] - When set to true, the flip animation also animates to the transform parameters of the back-object. + * @param {GSAP.Ease} [opts.transformEase=Power2.easeOut] - The ease of the transform. + * @param {numer} [opts.focus=800] - The value of the focus of the 3D camera (see pixi-projection). + * @param {numer} [opts.near=10] - The near value of the 3D camera (see pixi-projection). + * @param {numer} [opts.far=10000] - The far value of the 3D camera (see pixi-projection). + * @param {boolean} [opts.orthographic=false] - Should the flip animation be an orthographic animation? + * @param {function} [opts.onStart=null] - A callback executed on start of the flip animation. + * @param {function} [opts.onUpdate=null] - A callback executed on each step of the flip animation. + * @param {function} [opts.onComplete=null] - A callback executed when the flip animation is finished. + */ + constructor(front, back, renderer, opts = {}) { + + super(); + + this.opts = Object.assign({}, { + front, + back, + renderer, + duration: 1, + ease: Power2.easeOut, + shadow: false, + eulerX: 0, + eulerY: 0, + eulerEase: Sine.easeOut, + useBackTransforms: false, + transformEase: Power2.easeOut, + focus: 800, + near: 10, + far: 10000, + orthographic: false + }, opts); + + // planes + //-------------------- + this.setPlanes(this.opts.focus, this.opts.near, this.opts.far, this.opts.orthographic); + + // flipped + //-------------------- + this._flipped = false; + + // objects + //-------------------- + this.objects = {}; + + // setup + //-------------------- + this.setup(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {Flippable} A reference to the flippable for chaining. + */ + setup() { + + const scale = .5; + + // filters + //-------------------- + const blurFilter = new PIXI.filters.BlurFilter(); + blurFilter.blur = 0.2; + this.objects.blurFilter = blurFilter; + + // outer + //-------------------- + const outer = new PIXI.projection.Container3d(); + outer.scale3d.set(scale); + this.addChild(outer); + this.objects.outer = outer; + + // shadow + //-------------------- + const shadow = new PIXI.projection.Sprite3d(PIXI.Texture.fromImage('../../assets/images/shadow.png')); + shadow.renderable = false; + shadow.anchor.set(0.5); + shadow.scale3d.set(.98); + shadow.alpha = 0.7; + shadow.filters = [blurFilter]; + shadow.visible = this.opts.shadow; + outer.addChild(shadow); + this.objects.shadow = shadow; + + // inner + //-------------------- + const inner = new PIXI.projection.Container3d(); + inner.euler.y = Math.PI; + outer.addChild(inner); + this.objects.inner = inner; + + // front + //-------------------- + const front = new PIXI.projection.Sprite3d(PIXI.Texture.EMPTY); + front.scale.set(-1 / scale, 1 / scale); + front.renderable = true; + front.anchor.set(.5); + inner.addChild(front); + this.objects.front = front; + + // back + //-------------------- + const back = new PIXI.projection.Sprite3d(PIXI.Texture.EMPTY); + back.scale.set(1 / scale, 1 / scale); + back.renderable = false; + back.anchor.set(.5); + inner.addChild(back); + this.objects.back = back; + + return this + } + + /** + * Gets or sets the flipped state. + * + * @member {boolean} + */ + get flipped() { + return this._flipped + } + set flipped(toBack) { + + this._flipped = toBack; + + // references + //-------------------- + const front = this.objects.front; + const back = this.objects.back; + const inner = this.objects.inner; + const shadow = this.objects.shadow; + const blurFilter = this.objects.blurFilter; + + const half = this.opts.duration / 2; + const ease = this.opts.eulerEase; + + const fromObject = toBack ? this.opts.front : this.opts.back; + const toObject = toBack ? this.opts.back : this.opts.front; + + // set textures for virtual front and virtual back + //-------------------- + front.texture = this.generateTexture(this.opts.front); + back.texture = this.generateTexture(this.opts.back); + + // switch objects and set params for virtual objects + //-------------------- + const fromCenter = this.anchorToCenter(fromObject); + const toCenter = this.anchorToCenter(toObject); + + // from values + //-------------------- + this.x = fromCenter.x; + this.y = fromCenter.y; + front.width = fromObject.width * 2; + front.height = fromObject.height * 2; + back.width = fromObject.width * 2; + back.height = fromObject.height * 2; + this.rotation = fromObject.rotation; + this.skew.x = fromObject.skew.x; + this.skew.y = fromObject.skew.y; + + // calculate to values + //-------------------- + const to = { + x: this.opts.useBackTransforms ? toCenter.x : fromCenter.x, + y: this.opts.useBackTransforms ? toCenter.y : fromCenter.y, + anchorX: this.opts.useBackTransforms ? toObject.x : fromObject.x, + anchorY: this.opts.useBackTransforms ? toObject.y : fromObject.y, + width: this.opts.useBackTransforms ? toObject.width * 2 : fromObject.width * 2, + height: this.opts.useBackTransforms ? toObject.height * 2 : fromObject.height * 2, + rotation: this.opts.useBackTransforms ? toObject.rotation : fromObject.rotation, + skewX: this.opts.useBackTransforms ? toObject.skew.x : fromObject.skew.x, + skewY: this.opts.useBackTransforms ? toObject.skew.y : fromObject.skew.y + }; + + // set toObject end values + //-------------------- + toObject.x = to.anchorX; + toObject.y = to.anchorY; + toObject.width = to.width / 2; + toObject.height = to.height / 2; + toObject.rotation = to.rotation; + toObject.skew.x = to.skewX; + toObject.skew.y = to.skewY; + + // flip + //-------------------- + TweenLite.to(inner.euler, this.opts.duration, { + y: toBack ? 0 : Math.PI, + ease: this.opts.ease, + onStart: () => { + this.switchDisplayObject(fromObject, this); + shadow.renderable = true; + if (this.opts.onStart) { + this.opts.onStart(this, this); + } + }, + onUpdate: () => { + this.layout(); + if (this.opts.onUpdate) { + this.opts.onUpdate(this, this); + } + }, + onComplete: () => { + this.switchDisplayObject(this, toObject); + shadow.renderable = false; + if (this.opts.onComplete) { + this.opts.onComplete(this, this); + } + } + }); + + // x & y + //-------------------- + TweenLite.to(this, this.opts.duration, { + x: to.x, + y: to.y, + ease: this.opts.transformEase + }); + + // width & height + //-------------------- + TweenLite.to([front, back], this.opts.duration, { + width: to.width, + height: to.height, + ease: this.opts.transformEase + }); + + // rotation + //-------------------- + TweenLite.to(this, this.opts.duration, { + directionalRotation: { + rotation: `${to.rotation}_short`, + useRadians: true + }, + ease: this.opts.transformEase + }); + + // skewX & skewY + //-------------------- + TweenLite.to(this.skew, this.opts.duration, { + x: to.skewX, + y: to.skewY, + ease: this.opts.transformEase + }); + + // camera + //-------------------- + new TimelineMax() + .to(this.euler, half, {x: this.opts.eulerX, y: this.opts.eulerY, ease}) + .to(this.euler, half, {x: 0, y: 0, ease}); + + // shadow + //-------------------- + new TimelineMax() + .to(shadow, half, {alpha: .3, ease}) + .to(shadow, half, {alpha: .7, ease}); + + // blurfilter + //-------------------- + new TimelineMax() + .to(blurFilter, half, {blur: 6, ease}) + .to(blurFilter, half, {blur: .2, ease}); + } + + /** + * Should be called to refresh the layout of the camera. + * + * @return {Flippable} A reference to the flippable for chaining. + */ + layout() { + + const front = this.objects.front; + const back = this.objects.back; + const shadow = this.objects.shadow; + const inner = this.objects.inner; + + inner.position3d.z = -Math.sin(inner.euler.y) * front.texture.baseTexture.width * 2; + + //this.objects.shadow.euler = this.objects.inner.euler + shadow.euler.x = -inner.euler.x; + shadow.euler.y = -inner.euler.y; + + if (this.frontSideInFront) { + front.renderable = true; + back.renderable = false; + shadow.width = front.width; + shadow.height = front.height; + } else { + front.renderable = false; + back.renderable = true; + shadow.width = back.width; + shadow.height = back.height; + } + + return this + } + + /** + * Toggles the flippable. Switches the sides. + * + * @private + * @return {Flippable} A reference to the flippable for chaining. + */ + toggle() { + this.flipped = !this.flipped; + + return this + } + + /** + * Gets the alignment state. true if the front side is in front, false otherwise. + * + * @member {boolean} + */ + get frontSideInFront() { + return !this.objects.inner.isFrontFace() + } + + /** + * Calculates the center point of an PIXI.DisplayObject. + * + * @private + * @param {PIXI.DisplayObject} displayObject - The DisplayObject from which to calculate the center. + * @return {object} Return an object with x and y. + */ + anchorToCenter(displayObject) { + const bounds = displayObject.getBounds(); + return { + x: bounds.x + bounds.width / 2, + y: bounds.y + bounds.height / 2 + } + } + + /** + * Creates children and instantiates everything. + * + * @private + * @param {PIXI.DisplayObject} displayObject - The DisplayObject from which to generate the texture. + * @return {PIXI.Texture} The generated PIXI.Texture. + */ + generateTexture(displayObject) { + + // renderTexture + //-------------------- + const renderTexture = PIXI.RenderTexture.create(displayObject.width, displayObject.height); + + // save position + const transform = [displayObject.x, displayObject.y, displayObject.scale.x, displayObject.scale.y, displayObject.rotation, displayObject.skew.x, displayObject.skew.y, displayObject.pivot.x, displayObject.pivot.y]; + + displayObject.position.set(0, 0); + displayObject.skew.set(0, 0); + displayObject.rotation = 0; + + // render + //-------------------- + this.opts.renderer.render(displayObject, renderTexture); + + // restore position + displayObject.setTransform(...transform); + + return renderTexture + } + + /** + * Removed the first DisplayObject and adds the second one at the exactly same position. + * + * @private + * @param {PIXI.DisplayObject} first - The old DisplayObject. + * @param {PIXI.DisplayObject} second - The new DisplayObject. + * @return {Flippable} A reference to the flippable for chaining. + */ + switchDisplayObject(first, second) { + if (first && second && first.parent) { + const parent = first.parent; + const index = parent.getChildIndex(first); + parent.addChildAt(second, index); + parent.removeChild(first); + } + + return this + } + } + + /** + * + */ + class Popover extends PIXI.Graphics { + + constructor({title = null, text = null, x = 0, y = 0, placement = 'top', width = 250, titleStyle = {}, textStyle = {fontSize: '1.6em'}} = {}) { + super(); + + this.opts = {title, text, x, y, placement, width, titleStyle, textStyle}; + + this.padding = 12; + + let style = { + fontFamily: 'Arial', + fontSize: '2em', + stroke: '#f6f6f6', + strokeThickness: 3, + wordWrap: true, + wordWrapWidth: width - (this.padding * 2) + }; + + this.titleTextStyle = new PIXI.TextStyle(Object.assign({}, style, titleStyle)); + this.textTextStyle = new PIXI.TextStyle(Object.assign({}, style, textStyle)); + + if (title || text) { + this.setup(); + this.draw(); + this.positioning(); + } + } + + setup() { + this.removeChildren(); + + if (this.opts.title) { + this.titleText = new PIXI.Text(this.opts.title, this.titleTextStyle); + this.titleText.position.set(this.padding, this.padding); + this.addChild(this.titleText); + } + + this.titleY = this.titleText ? this.titleText.y : 0; + this.titleHeight = this.titleText ? this.titleText.height : 0; + + if (this.opts.text) { + this.textText = new PIXI.Text(this.opts.text, this.textTextStyle); + this.textText.position.set(this.padding, this.titleY + this.titleHeight + this.padding); + this.addChild(this.textText); + } + + this.textY = this.textText ? this.textText.y : 0; + this.textHeight = this.textText ? this.textText.height : 0; + } + + close() { + this.parent.removeChild(this); + } + + draw() { + this.clear(); + this.beginFill(0xffffff, 1); + this.lineStyle(1, 0x282828, .5); + + // Draw rounded rectangle + const height = this.height + this.padding; + this.drawRoundedRect(0, 0, this.opts.width, height, 8); + + // Draw anchor + this.drawAnchor(this.opts.placement); + + // Draw title background + if (this.opts.title) { + this.lineStyle(0); + this.beginFill(0xf7f7f7, 1); + let x = 1; + let y = this.titleText.x + this.titleText.height + (this.padding / 2); + this.moveTo(x, y); + y = 9; + this.lineTo(x, y); + this.quadraticCurveTo(x, y - 8, x + 8, y - 8); + x += this.opts.width - 7; + y -= 8; + this.lineTo(x, y); + this.quadraticCurveTo(x + 5, y, x + 5, y + 8); + x += 5; + y += this.titleText.x + this.titleText.height + (this.padding / 2); + this.lineTo(x, y); + if (this.opts.text) { + x = 1; + this.lineTo(x, y); + } else { + this.quadraticCurveTo(x, y, x - 5, y + 4); + x = 6; + y += 4; + this.lineTo(x, y); + this.quadraticCurveTo(x, y, x - 5, y - 4); + } + } + + this.endFill(); + } + + drawAnchor(placement) { + + let x = 0; + let y = 0; + + switch (placement) { + case 'bottom': + if (this.opts.title) { + this.beginFill(0xf7f7f7, 1); + } + x = (this.width / 2) - 10; + y = 1; + this.moveTo(x, y); + x += 10; + y -= 10; + this.lineTo(x, y); + x += 10; + y += 10; + this.lineTo(x, y); + break + case 'right': + x = 1; + y = (this.height / 2) - 10; + if (this.titleY + this.titleHeight > y) { + this.beginFill(0xf7f7f7, 1); + } + this.moveTo(x, y); + x -= 10; + y += 10; + this.lineTo(x, y); + x += 10; + y += 10; + this.lineTo(x, y); + break + case 'left': + x = this.width - 2; + y = (this.height / 2) - 10; + if (this.titleY + this.titleHeight > y) { + this.beginFill(0xf7f7f7, 1); + } + this.moveTo(x, y); + x += 10; + y += 10; + this.lineTo(x, y); + x -= 10; + y += 10; + this.lineTo(x, y); + break + default: + x = (this.width / 2) - 10; + y = this.height - 2; + this.moveTo(x, y); + x += 10; + y += 10; + this.lineTo(x, y); + x += 10; + y -= 10; + this.lineTo(x, y); + break + } + } + + positioning() { + + const x = this.opts.x; + const y = this.opts.y; + + switch (this.opts.placement) { + case 'bottom': + this.position.set(x - (this.width / 2), y + 10); + break + case 'right': + this.position.set(x, y - (this.height / 2)); + break + case 'left': + this.position.set(x - this.width, y - (this.height / 2)); + break + default: + this.position.set(x - (this.width / 2), y - this.height); + break + } + } + } + + /** A container for scatter objects, which uses a single InteractionMapper + * for all children. This reduces the number of registered event handlers + * and covers the common use case that multiple objects are scattered + * on the same level. + */ + class ScatterContainer extends PIXI.Graphics { + + /** + * @constructor + * @param {PIXI.Renderer} renderer - PIXI renderer, needed for hit testing + * @param {Bool} stopEvents - Whether events should be stopped or propagated + * @param {Bool} claimEvents - Whether events should be marked as claimed + * if findTarget return as non-null value. + * @param {PIXI.Container} container - A container for the scatter + * @param {Bool} showBounds - Show bounds for debugging purposes. + * @param {Bool} showTouches - Show touches and pointer for debugging purposes. + * @param {Color} backgroundColor - Set background color if specified. + * @param {PIXIApp} app - Needed if showBounds is true to register + * update handler. + */ + constructor(renderer, { + stopEvents = true, + claimEvents = true, + container = null, + showBounds = false, + showPolygon = false, + showTouches = false, + backgroundColor = null, + app = window.app + } = {}) { + super(); + this.container = container; + if (this.container) + this.containerDimensions = { + x: this.container.width, + y: this.container.height + }; + this.backgroundWidth = null; + this.backgroundHeight = null; + this.app = app; + this.renderer = renderer; + this.stopEvents = stopEvents; + this.claimEvents = claimEvents; + this.delegate = new InteractionMapper$1(this.eventReceiver, this); + this.showBounds = showBounds; + this.showTouches = showTouches; + this.showPolygon = showPolygon; + this.backgroundColor = backgroundColor; + if (showBounds || showTouches || showPolygon) { + //console.log("Show TOUCHES!!!") + this.app.ticker.add((delta) => this.update(delta), this); + } + if (backgroundColor) { + this.updateBackground(); + } + } + + updateBackground() { + this.clear(); + let rect = this.bounds; + this.beginFill(this.backgroundColor, 1); + this.drawRect(0, 0, rect.width, rect.height); + this.endFill(); + } + + get eventReceiver() { + return this.renderer.plugins.interaction.interactionDOMElement + } + + get bounds() { + let x = 0; + let y = 0; + // @container: We need to call the constant values, as the container + // gets resized, when a child moves outside the original boundaries. + let w = (this.container) ? this.containerDimensions.x : (this.backgroundWidth || this.app.width); + let h = (this.container) ? this.containerDimensions.y : (this.backgroundHeight || this.app.height); + + if (this.app.fullscreen && this.app.monkeyPatchMapping) { + let fixed = this.mapPositionToPoint({ x: w, y: 0 }); + if (fixed.x < w) { + w = fixed.x; + } + if (fixed.y > 0) { + y += fixed.y; + h -= fixed.y; + } + } + return new PIXI.Rectangle(x, y, w, h) + } + + get center() { + let r = this.bounds; + return { x: r.width / 2, y: r.height / 2 } + } + + get polygon() { + let r = this.bounds; + let w2 = r.width / 2; + let h2 = r.height / 2; + let center = { x: w2, y: h2 }; + let polygon = new Polygon(center); + polygon.addPoint({ x: -w2, y: -h2 }); + polygon.addPoint({ x: w2, y: -h2 }); + polygon.addPoint({ x: w2, y: h2 }); + polygon.addPoint({ x: -w2, y: h2 }); + return polygon + } + + update(dt) { + this.clear(); + this.lineStyle(1, 0x0000FF); + if (this.showBounds) { + for (let child of this.children) { + if (child.scatter) { + //let {x, y, width, height} = child.scatter.throwBounds() + // new PIXI.Rectangle(x, y, width, height) + this.drawShape(child.scatter.bounds); + let center = child.scatter.center; + this.drawCircle(center.x, center.y, 4); + this.drawCircle(child.scatter.x, child.scatter.y, 4); + } + } + this.lineStyle(2, 0x0000FF); + this.drawShape(this.bounds); + } + if (this.showPolygon) { + this.lineStyle(2, 0xFF0000); + for (let child of this.children) { + if (child.scatter) { + let polygon = child.scatter.polygon; + let shape = new PIXI.Polygon(polygon.flatAbsolutePoints()); + shape.close(); + this.drawShape(shape); + } + } + } + if (this.showTouches) { + let current = this.delegate.interaction.current; + for (let [key, point] of current.entries()) { + let local = this.mapPositionToPoint(point); + this.drawCircle(local.x, local.y, 12); + } + } + } + + capture(event) { + if (this.stopEvents) + Events$1.stop(event); + return true + } + + fakeInteractionEvent(point, key) { + return { data: { global: point, key: key } } + } + + findHitScatter(data, displayObject, hit) { + // if (hit) { + // console.log("findHitScatter", displayObject) + // } + if (hit && this.hitScatter === null && typeof (displayObject) != undefined) { + this.hitScatter = (displayObject.scatter) ? displayObject.scatter : null; + } + } + + mapPositionToPoint(point, element = null) { + // In case of nested scatters we get an additional parameter that + // contains the found scatter + let local = new PIXI.Point(); + let interactionManager = this.renderer.plugins.interaction; + interactionManager.mapPositionToPoint(local, point.x, point.y); + if (element instanceof DisplayObjectScatter && element.displayObject.parent != null) { + return element.displayObject.parent.toLocal(local) + } + return local + } + + /** + * New method hitTest implemented (in InteractionManager, since 4.5.0). + * See https://github.com/pixijs/pixi.js/pull/3878 + */ + findTarget(event, local, global) { + if (event.claimedByScatter) { + return null + } + this.hitScatter = null; + let interactionManager = this.renderer.plugins.interaction; + let fakeEvent = this.fakeInteractionEvent(local); + interactionManager.processInteractive(fakeEvent, + this, + this.findHitScatter.bind(this), true); + if (this.claimEvents) + event.claimedByScatter = this.hitScatter; + return this.hitScatter + } + + findTargetNew(event, local, global) { + // UO: still problematic. Does not find non interactive elements + // which are needed for some stylus applications + if (event.claimedByScatter) { + return null + } + this.hitScatter = null; + let interactionManager = this.renderer.plugins.interaction; + let displayObject = interactionManager.hitTest(local, this); + if (displayObject != null && displayObject.scatter != null) + this.hitScatter = displayObject.scatter; + if (this.claimEvents) + event.claimedByScatter = this.hitScatter; + return this.hitScatter + } + + + onStart(event, interaction) { + + } + + onMove(event, interaction) { + + } + + onEnd(event, interaction) { + for (let key of interaction.ended.keys()) { + let point = interaction.ended.get(key); + if (interaction.isLongPress(key)) { + this.onLongPress(key, point); + } + if (interaction.isTap(key)) { + this.onTap(key, point); + } + } + } + + onTap(key, point) { + console.info('ScatterContainer.onTap'); + } + + onLongPress(key, point) { + console.info('ScatterContainer.onLongPress'); + } + + bringToFront(displayObject) { + this.addChild(displayObject); + } + + layout(width, height) { + this.backgroundWidth = width; + this.backgroundHeight = height; + if (this.backgroundColor) { + this.updateBackground(); + } + + } + } + + /** A wrapper for child elements of a ScatterContainer. Can be used + * to combine scattered objects with non-scattered objects. Any + * PIXI.DisplayObject can be wrapped. + */ + class DisplayObjectScatter extends AbstractScatter { + + constructor(displayObject, renderer, + { x = null, y = null, + minScale = 0.1, + maxScale = 1.0, + startScale = 1.0, + autoBringToFront = true, + translatable = true, scalable = true, rotatable = true, resizable = false, + movableX = true, + movableY = true, + throwVisibility = 44, + throwDamping = 0.95, + autoThrow = true, + rotationDegrees = null, + rotation = null, + overdoScaling = 1.5, + onTransform = null, + onThrowFinished = null } = {}) { + // For the simulation of named parameters, + // see: http://exploringjs.com/es6/ch_parameter-handling.html + super({ + overdoScaling, + minScale, maxScale, + startScale, + autoBringToFront, + translatable, scalable, rotatable, resizable, + movableX, movableY, throwVisibility, throwDamping, + autoThrow, + onThrowFinished, + rotationDegrees, rotation, + onTransform + }); + this.displayObject = displayObject; + this.displayObject.scatter = this; + this.renderer = renderer; + this.scale = startScale; + this.rotationDegrees = this.startRotationDegrees; + + // Only set x and y if they are specified. + // Otherwise the displayobject gets corrupted. + if (x != null) this.x = x; + if (y != null) this.y = y; + } + + getWorldScatter() { + return this + } + + /** Returns geometry data as object. **/ + getState() { + return { + scale: this.scale, + x: this.x, + y: this.y, + rotation: this.rotation + } + } + + setup() { + this.setupMouseWheelInteraction(); + } + + roundPixel(value) { + // UO: Should be obsolete because Renderer supports roundPixels by default + return value + let res = this.renderer.resolution; + return Math.round(value * res) / res + } + + get container() { + // return this.displayObject.parent + let obj = this.displayObject; + while (obj.parent != null && !(obj.parent instanceof ScatterContainer)) + obj = obj.parent; + return obj.parent + } + + get x() { + return this.position.x + } + + set x(value) { + this.position.x = value; + } + + get y() { + return this.position.y + } + + set y(value) { + this.position.y = value; + } + + get polygon() { + let polygon = new Polygon(this.center); + let w2 = this.width / 2; + let h2 = this.height / 2; + polygon.addPoint({ x: -w2, y: -h2 }); + polygon.addPoint({ x: w2, y: -h2 }); + polygon.addPoint({ x: w2, y: h2 }); + polygon.addPoint({ x: -w2, y: h2 }); + polygon.rotate(this.rotation); + return polygon + } + + get containerBounds() { + return this.container.bounds + } + + get containerPolygon() { + let container = this.container; + if (container == null) return null + return container.polygon + } + + get position() { + return this.displayObject.position + } + + set position(value) { + this.displayObject.position = value; + } + + get scale() { + return this.displayObject.scale.x + } + + set scale(value) { + this.displayObject.scale.x = value; + this.displayObject.scale.y = value; + } + + get width() { + return this.displayObject.width + } + + get height() { + return this.displayObject.height + } + + get bounds() { + return this.displayObject.getBounds() + } + + get pivot() { + return this.displayObject.pivot + } + + get rotation() { + return this.displayObject.rotation + } + + set rotation(value) { + this.displayObject.rotation = value; + } + + get rotationDegrees() { + return Angle.radian2degree(this.displayObject.rotation) + } + + set rotationDegrees(value) { + this.displayObject.rotation = Angle.degree2radian(value); + } + + get center() { + let w2 = this.width / 2; + let h2 = this.height / 2; + let dist = Math.sqrt(w2 * w2 + h2 * h2); + let angle = Points.angle({ x: w2, y: h2 }, { x: 0, y: 0 }); + let p = this.displayObject.x; + let c = Points.arc(this.position, this.rotation + angle, dist); + return c // Points.subtract(c, this.pivot) + } + + get rotationOrigin() { + // In PIXI the default rotation and scale origin is the position + return this.position // Points.add(this.position, this.pivot) + } + + mapPositionToContainerPoint(point) { + // UO: We need the coordinates related to this scatter in case + // of nested scatters + if (this.container != null) + return this.container.mapPositionToPoint(point, this) + return point + } + + capture(event) { + return true + } + + bringToFront() { + if (this.autoBringToFront) { + if (this.displayObject.parent instanceof ScatterContainer) { + let scatterContainer = this.displayObject.parent; + scatterContainer.bringToFront(this.displayObject); + } else if (this.displayObject.parent != null && this.displayObject.parent.scatter) { + this.displayObject.parent.scatter.toFront(this.displayObject); + } + } + } + + toFront(displayObject) { + this.displayObject.addChild(displayObject); + } + + validScale(scale) { + scale = Math.max(scale, this.minScale); + scale = Math.min(scale, this.maxScale); + return scale + } + } + + /** + * + */ + class Command extends PIXI.Graphics { + /*** Abstract base class for record, play, and stop commands. ***/ + constructor(tools, selectedColor, shape) { + super(); + this.tools = tools; + this.shape = shape; + this.selected = false; + this.disabled = false; + this.selectedColor = selectedColor; + this.draw(); + this.setup(); + } + + setup() { + } + + draw() { + this.clear(); + var color = (this.selected) ? this.selectedColor : 0xFFFFFF; + this.lineStyle(0); + this.beginFill(color, 1); + this.drawShape(this.shape); + this.endFill(); + } + + select() { + this.selected = true; + this.draw(); + } + + deselect() { + this.selected = false; + this.draw(); + } + + toggle() { + this.selected = !this.selected; + this.draw(); + } + + stop() { + this.selected = false; + this.draw(); + } + } + + class RecordCommand extends Command { + /*** Records events for later replay. ***/ + setup() { + this.recorder = new EventRecorder(); + } + + toggle() { + super.toggle(); + if (!this.selected) { + this.recorder.stopRecording(); + } + } + + recordEvent(event) { + this.recorder.record(event); + } + + normalize(value, limit) { + return value / limit + } + + normalizeX(value) { + return this.normalize(value, window.innerWidth) + } + + normalizeY(value) { + return this.normalize(value, window.innerHeight) + } + + whileNotStopped() { + return this.tools.play.selected + } + + startReplay() { + let whileCondition = this.whileNotStopped.bind(this); + this.recorder.startReplay(whileCondition, () => this.tools.play.stop()); + } + } + + class PlayCommand extends Command { + /*** Plays recorded events. ***/ + toggle() { + super.toggle(); + if (this.selected && this.tools.record.recorder.recorded.length > 0) { + this.tools.startReplay(); + } + } + } + + class StopCommand extends Command { + /*** Stops recording and playing. ***/ + toggle() { + super.toggle(); + this.tools.record.stop(); + this.tools.play.stop(); + setTimeout(this.deselect.bind(this), 500); + } + } + + class RecorderTools extends PIXI.Container { + + constructor(renderer) { + super(renderer); + this.renderer = renderer; + this.setupToolbar(); + this.replayRate = 100.0; + this.onReset = null; + this.touchGraphics = new PIXI.Graphics(); + this.showTouches(); + } + + setup(container) { + // Since this delegate might shadow another delegate, we mus avoid + // capturing PointerEvents. + this.delegate = new InteractionMapper(container, this, { capturePointerEvents: false }); + } + + findTarget(event, local, global) { + return this + } + + setupToolbar() { + this.toolbar = new PIXI.Graphics(); + this.record = new RecordCommand(this, 0xCC0000, new PIXI.Circle(0, 0, 16)); + this.play = new PlayCommand(this, 0x0000CC, new PIXI.Polygon(0, 16, + 32, 16 + 16, + 0, 16 + 32, + 0, 16)); + this.stop = new StopCommand(this, 0x0000CC, + new PIXI.Rectangle(0, 0, 32, 32)); + this.toolbar.addChild(this.record).position.set(44, 48); + this.toolbar.addChild(this.play).position.set(44 + 44, 16); + this.toolbar.addChild(this.stop).position.set(44 + 44 + 44 + 16, 32); + this.updateToolbar(); + this.addChild(this.toolbar); + } + + updateToolbar() { + var graphics = this.toolbar; + graphics.clear(); + graphics.beginFill(0x000000, 0.5); + graphics.lineStyle(2, 0xFFFFFF, 1); + graphics.drawRoundedRect(16, 16, 44 * 4 + 8, 64, 8); + graphics.endFill(); + } + + onMouseWheel(event) { + console.log('onMouseWheel missing'); + } + + onPress(point) { + if (this.record.containsPoint(point)) { + this.record.toggle(); + } + if (this.play.containsPoint(point)) { + this.play.toggle(); + } + if (this.stop.containsPoint(point)) { + this.stop.toggle(); + if (this.onReset) { + this.onReset(); + } + } + } + + mapPositionToPoint(point) { + let local = new PIXI.Point(); + this.renderer.plugins.interaction.mapPositionToPoint(local, point.x, point.y); + return local + } + + extractLocal(event) { + return this.mapPositionToPoint(Events.extractPoint(event)) + } + + capture(event) { + if (typeof event.mouseDownSubstitute != 'undefined') + return false + return true + } + + startReplay() { + if (this.onReset) { + this.onReset(); + } + this.record.startReplay(); + } + + showTouches() { + this.addChild(this.touchGraphics); + } + + recordEvent(event) { + if (this.record.selected) { + this.record.recordEvent(event); + } + } + + onStart(event, interaction) { + let local = this.extractLocal(event); + if (!this.toolbar.containsPoint(local)) { + this.recordEvent(event); + this.updateTouchGraphics(interaction); + } + } + + onMove(event, interaction) { + let local = this.extractLocal(event); + if (!this.toolbar.containsPoint(local)) { + this.recordEvent(event); + this.updateTouchGraphics(interaction); + } + } + + onEnd(event, interaction) { + let local = this.extractLocal(event); + if (this.toolbar.containsPoint(local)) { + this.onPress(local); + } + else { + this.recordEvent(event); + this.updateTouchGraphics(interaction); + } + } + + updateTouchGraphics(interaction) { + let current = interaction.current; + let graphics = this.touchGraphics; + if (graphics != null) { + graphics.clear(); + for (let key of current.keys()) { + if (interaction.ended.has(key)) { + continue + } + let p = current.get(key); + if (key == 'mouse') { + graphics.beginFill(0xCC0000, 0.5); + } else { + graphics.beginFill(0xCCCCCC, 0.5); + } + graphics.drawCircle(p.x, p.y, 20); + } + graphics.endFill(); + } + } + } + + class AppTest extends PIXIApp { + + constructor(canvas, container) { + super({ view: canvas, backgroundColor: 0x000000 }); + this.container = container; + } + + sceneFactory() { + return new RecorderTools(this.renderer) + } + + setup() { + super.setup(); + this.scene.setup(this.container); + } + + run(reset = null) { + this.scene.onReset = reset; + console.log('Running AppTest'); + return this + } + } + + /** + * Defines usefull default text styles. + */ + class FontInfo { + + static get small() { + return app.theme.textStyleSmall + } + + static get normal() { + return app.theme.textStyle + } + + static get centered() { + return Object.assign({}, app.theme.textStyle, { align: 'center' }) + } + } + + /** + * Static methods to support hyphenation of lines. + * + * @class Hypenate + */ + class Hypenate { + + static splitPart(part) { + let parts = part.split('-'); + if (parts.length == 1) + return [part] + let result = []; + let last = parts.pop(); + for (let p of parts) { + result.push(p + '-'); + } + result.push(last); + return result.filter(p => p.length > 0) + } + + static splitWord(word) { + if (typeof (language) == 'undefined') { + if (word.indexOf('-') > -1) { + return word.split('-') + } + return [word] + } + let parts = language.hyphenate(word); + let result = []; + for (let part of parts) { + for (let splitted of this.splitPart(part)) { + result.push(splitted); + } + } + return result + } + + static abbreviateLine(label, style, width) { + const pixiStyle = new PIXI.TextStyle(style); + let metrics = PIXI.TextMetrics.measureText(label, pixiStyle); + while(metrics.width > width && label.length > 3) { + label = label.slice(0, label.length-1); + metrics = PIXI.TextMetrics.measureText(label, pixiStyle); + } + label = label.slice(0, label.length-1); + return label + '…' + } + + static splitLine(line, pixiStyle, width, space, minus) { + let x = 0; + let result = ''; + let words = line.split(' '); + for (let word of words) { + let wordMetrics = PIXI.TextMetrics.measureText(word, pixiStyle); + if (x + wordMetrics.width >= width) { + let parts = this.splitWord(word); + let newWord = ''; + if (parts.length == 1) { + newWord += '\n' + word + ' '; + x = wordMetrics.width + space.width; + } + else { + let first = true; + let lastPart = ''; + for (let part of parts) { + let partMetrics = PIXI.TextMetrics.measureText(part, pixiStyle); + if (x + partMetrics.width + space.width > width) { + newWord += ((first || lastPart.endsWith('-')) ? '\n' : '-\n') + part; + x = partMetrics.width; + } + else { + newWord += part; + x += partMetrics.width; + } + lastPart = part; + first = false; + } + x += space.width; + } + result += newWord + ' '; + } + else { + result += word + ' '; + x += wordMetrics.width + space.width; + } + } + return result + } + + /** + * Main method and entry point for text hyphenation + * + * @static + * @param {*} text + * @param {*} style + * @param {*} width + * @returns + * @memberof Hypenate + */ + static splitLines(text, style, width) { + const pixiStyle = new PIXI.TextStyle(style); + const lines = text.split('\n'); + const space = PIXI.TextMetrics.measureText(' ', pixiStyle); + const minus = PIXI.TextMetrics.measureText('-', pixiStyle); + let result = []; + for (let line of lines) { + result.push(this.splitLine(line, pixiStyle, width, space, minus)); + } + return result.join('\n') + } + } + + /** + * A specialization of the PIXI.Graphics class that allows to + * resuse and place labels across different layout variants + * + * @export + * @class LabeledGraphics + * @extends {PIXI.Graphics} + */ + class LabeledGraphics extends PIXI.Graphics { + + /** + * Creates an instance of LabeledGraphics and defines a local label cache. + * + * @memberof LabeledGraphics + */ + constructor() { + super(); + this.labels = new Map(); + } + + _createText(label, fontInfo) { + return new PIXI.Text(label, fontInfo) + } + + + /** + * Main additional method. Ensures that a text object is created that is cached + * under the given key. + * + * @param {*} key - The cache key + * @param {*} label - The label to show + * @param {*} [attrs={}] - Defines attributes of the text object. + * align: 'right', 'left', or 'center' + * justify: 'top', 'bottom', or 'center' + * maxLines: {integer} truncates the text and adds ellipsis + * maxHeight: {number} truncates text that needs more space and adds ellipsis + * maxWidth: {number} word wraps text using hyphenation if possible + * @param {*} [fontInfo=FontInfo.normal] - Defines PIXI.TextStyle attributes + * @returns {PIXI.Text} instance + * @memberof LabeledGraphics + */ + ensureLabel(key, label, attrs = {}, fontInfo = FontInfo.normal) { + + if (attrs.maxWidth && attrs.maxLines == 1) { + label = Hypenate.abbreviateLine(label, fontInfo, attrs.maxWidth); + } + else { + if (attrs.maxWidth) { + label = Hypenate.splitLines(label, fontInfo, attrs.maxWidth); + } + if (attrs.maxLines) { + label = this.truncateLabel(label, fontInfo, attrs.maxLines); + } + if (attrs.maxHeight) { + let styleInfo = new PIXI.TextStyle(fontInfo); + let metrics = PIXI.TextMetrics.measureText(label, styleInfo); + let maxLines = Math.max(attrs.maxHeight / metrics.lineHeight, 1); + label = this.truncateLabel(label, fontInfo, maxLines); + } + } + + if (!this.labels.has(key)) { + let text = this._createText(label, fontInfo); + this.labels.set(key, text); + this.addChild(text); + } + let text = this.labels.get(key); + for (let k in attrs) { + text[k] = attrs[k]; + } + if (label != text.text) + text.text = label; + // We do not follow the flexbox jargon and use align for x and justify for y axis + // This deviation is needed to ensure backward compatability + switch (attrs.justify || null) { + case 'top': + text.anchor.y = 0; + break + case 'bottom': + text.anchor.x = 1; + break + default: + text.anchor.y = 0.5; + break + } + switch (attrs.align) { + case 'right': + text.anchor.x = 1; + break + case 'center': + text.anchor.x = 0.5; + break + default: + text.anchor.x = 0; + break + } + text.visible = true; + return text + } + + /** + * Private method that truncates the text and adds an ellipsis if there are more lines + * than wanted + * + * @param {*} text + * @param {*} style + * @param {*} [maxLines=Infinity] + * @returns + * @memberof LabeledGraphics + */ + truncateLabel(text, style, maxLines = Infinity) { + if (maxLines === Infinity) { + return text + } + const { wordWrapWidth } = style; + const pixiStyle = new PIXI.TextStyle(style); + const { lines } = PIXI.TextMetrics.measureText(text, pixiStyle); + let newText = text; + if (lines.length > maxLines) { + const truncatedLines = lines.slice(0, maxLines); + const lastLine = truncatedLines[truncatedLines.length - 1]; + const words = lastLine.split(' '); + const wordMetrics = PIXI.TextMetrics.measureText(`\u00A0\n...\n${words.join('\n')}`, pixiStyle); + const [spaceLength, dotsLength, ...wordLengths] = wordMetrics.lineWidths; + const { text: newLastLine } = wordLengths.reduce((data, wordLength, i) => { + if (data.length + wordLength + spaceLength >= wordWrapWidth) { + return { ...data, length: wordWrapWidth } + } + return { + text: `${data.text}${i > 0 ? ' ' : ''}${words[i]}`, + length: data.length + wordLength + spaceLength, + }; + }, { text: '', length: dotsLength }); + truncatedLines[truncatedLines.length - 1] = `${newLastLine}...`; + newText = truncatedLines.join('\n'); + } + return newText + } + + /** + * Returns the label for the given key. + * + * @param {*} key + * @returns + * @memberof LabeledGraphics + */ + getLabel(key) { + return this.labels.get(key) + } + + /** + * Hides the label with the given key. + * @param {*} key + * @memberof LabeledGraphics + */ + hideLabel(key) { + let label = this.labels.get(key); + if (label) { + label.visible = false; + } + } + + /** + * Removes the label with the given key. + * @param {*} key + * @memberof LabeledGraphics + */ + removeLabel(key) { + let label = this.labels.get(key); + this.labels.delete(key); + label.destroy(); + } + + + /** + * Ensures that labels are hidden on clear. + * + * @memberof LabeledGraphics + */ + clear() { + super.clear(); + for (let key of this.labels.keys()) { + this.hideLabel(key); + } + } + + /** + * Logs debugging infos + * + * @memberof LabeledGraphics + */ + debugInfos() { + console.log({ size: this.labels.size, labels: this.labels }); + } + } + + + const labelCache = new Map(); + + function getTexture(label, fontInfo = FontInfo.normal) { + let key = label + fontInfo.fontFamily + fontInfo.fontSize; + + if (labelCache.has(key)) { + return labelCache.get(key) + } + let expandedFont = Object.assign({}, fontInfo); + expandedFont.fontSize *= window.devicePixelRatio; + let text = new PIXI.Text(label, expandedFont); + text.updateText(); + labelCache.set(key, text.texture); + return text.texture + } + + class SpriteLabel extends PIXI.Sprite { + + constructor(label, fontInfo) { + let texture = getTexture(label, fontInfo); + super(texture); + this.label = label; + this.fontInfo = fontInfo; + this.scale.set(0.8 / window.devicePixelRatio); + } + + set text(label) { + this.label = label; + this.texture = getTexture(label, this.fontInfo); + } + + get text() { + return this.label + } + } + + class BitmapLabeledGraphics extends LabeledGraphics { + + _createText(label, fontInfo) { + let texture = getTexture(label, fontInfo); + return new SpriteLabel(texture) + } + + } + + class Ticks { + + get reservedPrefixes() { + return ['decade', 'year', 'month', 'day', 'hour', 'minute', 'second'] + } + + get minWidth() { + return 10 + } + + format(available) { + return { year: '2-digit', timeZone: 'UTC' } + } + + get minLabelWidth() { + return 44 + } + + get formatKey() { + return this.key + } + + dateKey(date) { + return this.key + date.getFullYear() + } + + *iter(start, end) { + let date = this.iterStart(start); + while (date <= end) { + yield date; + date = this.next(date); + } + yield date; + } + + *iterRanges(range) { + for (let date of this.iter(range.start, range.end)) { + let next = this.next(date); + yield { start: date, end: next }; + } + } + + selectedRange(timeline, info) { + let first = null; + let last = null; + let visibleFirst = null; + let visibleLast = null; + let units = 0; + for (let { start, end } of this.iterRanges(info)) { + if (timeline.visibleRange(start, end)) { + if (first == null) { + first = start; + } + last = end; + } + if (timeline.visibleDate(start) && timeline.visibleDate(end)) { + units += 1; + if (visibleFirst == null) { + visibleFirst = start; + } + visibleLast = end; + } + } + if (first == null) + return info + return { start: first, end: last, visibleStart: visibleFirst, visibleEnd: visibleLast, units: units } + } + + drawTick(timeline, x, y, date) { + let visible = date > timeline.start && date < timeline.end; + if (!visible) + return false + timeline.drawTick(x); + return true + } + + toLocaleString(date, format) { + return date.toLocaleDateString('de', format) + } + + draw(timeline, range, width, height, available, format, nextFormat, level) { + let first = null; + let last = null; + let keyedFormat = (format) ? format[this.formatKey] : null; + let keyedNextFormat = (nextFormat) ? nextFormat[this.formatKey] : null; + let redundant = (nextFormat) ? this.formatKey in nextFormat : false; + let fullyRedundant = keyedFormat != null && keyedFormat == keyedNextFormat; + let y = timeline.getY(); + + for (let { start, end } of this.iterRanges(range)) { + let x = timeline.toX(start); + let xx = x; + let yy = y + timeline.tickLabelOffset(-1); + if (this.drawTick(timeline, x, y, start) && format) { + let key = this.dateKey(start); + let text = this.toLocaleString(start, format); + let align = 'center'; + let downTick = false; + if (nextFormat) { + yy = y + timeline.tickLabelOffset(-1, 1); + align = 'left'; + timeline.drawTick(x, 4.2); + let nextX = timeline.toX(end) - 100; + if (x < 0 && nextX > -100 && !redundant) { + xx = Math.min(4, nextX); + } + else { + xx -= 2; + } + downTick = true; + } + else if (level > 0) { + xx = x + available / 2; + } + else { + downTick = true; + } + if (!fullyRedundant) { + timeline.ensureLabel(key, text, + { x: xx, y: yy, align }, + FontInfo.small); + } + if (downTick) timeline.drawTick(x, -1); + } + if (timeline.visibleRange(start, end)) { + if (first == null) + first = start; + last = end; + } + } + if (first == null) + return null + return { start: first, end: last } + } + } + + class DecadeTicks extends Ticks { + + get milliseconds() { + return 10 * 365 * 24 * 60 * 60 * 1000 + } + + format(available) { + return { year: 'numeric', timeZone: 'UTC' } + } + + selection(timeline, dates, selected) { + return dates + } + + get key() { + return 'decade' + } + + get formatKey() { + return 'year' + } + + iterStart(start) { + let modulo = start.getFullYear() % 10; + let year = start.getFullYear() - modulo; + return Dates.create(year, 0, 1) + } + + next(decade) { + return Dates.nextYear(decade, 10) + } + } + + class YearTicks extends Ticks { + + get milliseconds() { + return 365 * 24 * 60 * 60 * 1000 + } + + format(available) { + if (available < 44) + return { year: '2-digit', timeZone: 'UTC' } + return { year: 'numeric', timeZone: 'UTC' } + } + + get minLabelWidth() { + return 22 + } + + get key() { + return 'year' + } + + iterStart(start) { + return Dates.create(start.getFullYear(), 0, 1) + } + + next(year) { + return Dates.nextYear(year) + } + } + + class MonthTicks extends Ticks { + + get milliseconds() { + return (365 / 12) * 24 * 60 * 60 * 1000 + } + + format(available) { + let format = { month: 'narrow', timeZone: 'UTC' }; + if (available > 44) + format.month = 'short'; + if (available > 66) + format.year = '2-digit'; + if (available > 100) { + format.month = 'long'; + format.year = 'numeric'; + } + return format + } + + get minLabelWidth() { + return 32 + } + + get key() { + return 'month' + } + + dateKey(date) { + return this.key + date.getFullYear() + date.getMonth() + } + + iterStart(start) { + return Dates.create(start.getFullYear(), start.getMonth(), 1) + } + + next(month) { + return Dates.nextMonth(month) + } + } + + class DayTicks extends Ticks { + + get milliseconds() { + return 24 * 60 * 60 * 1000 + } + + format(available) { + let format = { day: 'numeric', timeZone: 'UTC' }; + if (available > 44) + format.month = 'short'; + if (available > 100) { + format.month = 'long'; + format.year = '2-digit'; + } + if (available > 150) { + format.weekday = 'short'; + } + if (available > 200) { + format.year = 'numeric'; + format.weekday = 'long'; + } + return format + } + + get key() { + return 'day' + } + + dateKey(date) { + return this.key + date.getFullYear() + date.getMonth() + date.getDate() + } + + iterStart(start) { + return Dates.create(start.getFullYear(), start.getMonth(), start.getDate()) + } + + next(date) { + return Dates.nextDay(date) + } + } + + class TimeTicks { + + constructor(...ticks) { + this.ticks = ticks; + } + + selectedRange(timeline) { + let info = { start: timeline.start, end: timeline.end, units: 0 }; + for (let ticks of this.ticks) { + info = ticks.selectedRange(timeline, info); + if (info.units > 1) { + timeline.selection = [info.visibleStart, info.visibleEnd]; + return + } + } + timeline.selection = [info.start, info.end]; + } + + draw(timeline, width, height) { + let range = timeline; + let start = timeline.toX(range.start); + let end = timeline.toX(range.end); + let size = end - start; + let duration = timeline.end - timeline.start; + let formats = new Map(); + let nextFormats = new Map(); + let availables = new Map(); + let previous = null; + let visible = []; + for (let ticks of this.ticks) { + let amount = ticks.milliseconds / duration; + let available = amount * size; + availables.set(ticks, available); + if (available < ticks.minWidth) + break + formats.set(ticks, (available < ticks.minLabelWidth) ? null : ticks.format(available)); + nextFormats.set(previous, formats.get(ticks)); + previous = ticks; + visible.push(ticks); + } + let level = 0; + for (let ticks of visible) { + if (range == null) + return + range = ticks.draw(timeline, range, width, height, + availables.get(ticks), + formats.get(ticks), + nextFormats.get(ticks), level); + level += 1; + } + } + } + + class ColorRanges { + + constructor(label, color, ranges) { + this.label = label; + this.color = color; + this.ranges = ranges; + } + + draw(timeline, width, height, size = 12) { + let minimum = 1 / Capabilities.devicePixelRatio; + let h2 = size; + timeline.lineStyle(size, this.color); + for (let range of this.ranges) { + if (range.to === null) { + range.to = Dates.nextDay(range.from); + } + let start = timeline.toX(range.from); + let end = timeline.toX(range.to); + if (end < start + minimum) { + end = start + minimum; + } + timeline.moveTo(start, h2); + timeline.lineTo(end, h2); + } + } + } + + class Timeline extends BitmapLabeledGraphics { + + constructor(width, height, { ticks = null, + baseLine = 0.5, showRange = true } = {}) { + super(); + this.wantedWidth = width; + this.wantedHeight = height; + this.extraLeft = 0; + this.extraRight = 0; + this.inset = 5; + this.showRange = showRange; + this.baseLine = baseLine; + this.tickHeight = 4; + this.zoom = 1; + this.minZoom = 1; + this.maxZoom = 12000; + this.scroll = 0; + this.deltas = []; + this.labelDates = []; + this.colorRanges = []; + this.rangeColors = new Cycle(Colors.eminence, + Colors.steelblue, + Colors.ochre, + Colors.turquoise); + this.callbacks = []; + this.onTapCallbacks = []; + this.onDoubleTapCallbacks = []; + this.onLongPressCallbacks = []; + this.progress = null; + this.start = null; + this.end = null; + this.selection = null; + this.autoScroll = false; + this.direction = -1; + this.timeticks = ticks || new TimeTicks(new DecadeTicks(), + new YearTicks(), + new MonthTicks(), + new DayTicks()); + this.labelPrefix = '__'; + } + + updateSelection() { + if (this.visibleDate(this.start) && this.visibleDate(this.end)) { + this.selection = [this.start, this.end]; + } + else { + this.timeticks.selectedRange(this); + } + + return this.selection + } + + addCallback(callback) { + this.callbacks.push(callback); + } + + addTabCallback(callback) { + this.onTapCallbacks.push(callback); + } + + addDoubleTapCallback(callback) { + this.onDoubleTapCallbacks.push(callback); + } + + addLongPressCallback(callback) { + this.onLongPressCallbacks.push(callback); + } + + addLabels(labels) { + this.labelDates = labels; + } + + range(start, end) { + this.start = start; + this.end = end; + } + + draw(width, height) { + this.wantedWidth = width; + this.wantedHeight = height; + this.redraw(); + } + + updateColorRanges(w, h) { + let xx = w - this.inset; + let size = FontInfo.small.fontSize; + let yy = h - size; + for (let i = this.colorRanges.length - 1; i >= 0; i--) { + let cr = this.colorRanges[i]; + let label = cr.label; + cr.draw(this, w, h); + let current = this.ensureLabel('colorRange:' + label, label, + { x: xx, y: yy, align: 'right' }, FontInfo.small); + let r = current.getBounds(); + xx -= r.width + 16; + + this.lineStyle(size, cr.color); + this.moveTo(xx, yy); + this.lineTo(xx + size, yy); + xx -= size + 4; + } + } + + drawSelectedRamge(selected) { + this.lineStyle(2, app.theme.primaryColor); + let start = this.toX(selected[0]); + let end = this.toX(selected[1]); + this.moveTo(start, 0); + this.lineTo(end, 0); + this.drawTick(start, -1.5, 0); + this.drawTick(end, -1.5, 0); + } + + redraw() { + this.clear(); + let h = this.wantedHeight; + let w = this.wantedWidth; + let y = this.getY(); + this.prepareLabels(); + this.updateColorRanges(w, h); + + this.lineStyle(2, 0xFFFFFF); + if (this.start != null && this.end != null) { + this.moveTo(this.toX(this.start), y); + this.lineTo(this.toX(this.end), y); + this.updateTicksAndLabels(w, h); + this.updateSelection(); + let selected = this.selection; + if (selected[0] != this.start && selected[1] != this.end) { + if (this.showRange) + this.drawSelectedRamge(selected); + } + for (let callback of this.callbacks) { + callback(this.scroll, this.zoom, this.selection); + } + } + else { + this.moveTo(this.inset, y); + this.lineTo(w - this.inset, y); + } + + if (this.progress != null && this.progress < 1) { + this.lineStyle(2, 0xCCCCFF); + this.moveTo(this.inset, y); + this.lineTo((w - this.inset) * this.progress, y); + } + } + + totalWidth(bounded = false) { + let w = this.wantedWidth - (2 * this.inset); + return w * this.validZoom(this.zoom, bounded) + } + + validZoom(zoom, bounded = true) { + let overshoot = (bounded) ? 1.0 : 2.0; + zoom = Math.max(zoom, this.minZoom / overshoot); + zoom = Math.min(zoom, this.maxZoom * overshoot); + return zoom + } + + getY() { + return this.wantedHeight * this.baseLine + } + + toX(date) { + let total = this.end - this.start; + let offset = this.inset + this.scroll; + let width = this.totalWidth(); + let delta = date - this.start; + let ratio = delta / total; + return offset + ratio * width + } + + fromX(value) { + let total = this.end - this.start; + let offset = this.inset + this.scroll; + let width = this.totalWidth(); + let ratio = (value - offset) / width; + let time = this.start.getTime() + total * ratio; + let date = new Date(time); + return date + } + + drawTick(x, direction = 1, y = null) { + if (y == null) { + y = this.getY(); + } + this.moveTo(x, y); + this.lineTo(x, y - (this.tickHeight * direction * this.direction)); + } + + prepareLabels() { + for (let key of this.labels.keys()) { + if (!key.startsWith(this.labelPrefix)) + this.labels.get(key).visible = false; + } + } + + updateTicksAndLabels(width, height) { + this.drawTick(this.toX(this.start)); + this.drawTick(this.toX(this.end)); + this.timeticks.draw(this, width, height); + this.updateLabels(width, height); + } + + visibleDate(date, offset = 0) { + if (date >= this.start && date <= this.end) { + let x = this.toX(date) + offset; + return (x > 0 && x < this.wantedWidth) + } + return false + } + + visibleRange(start, end) { + let x = this.toX(start); + if (x > this.wantedWidth) + return false + x = this.toX(end); + if (x < 0) + return false + return true + } + + tickLabelOffset(direction = 1, level = 0) { + let fs = FontInfo.small.fontSize; + let dh = fs + (level * (fs + 2)); + return this.direction * direction * dh + } + + updateLabels(width, height) { + let h2 = height / 2; + if (this.labelDates) { + let last = null; + let y = h2 + this.tickLabelOffset(); + for (let i = this.labelDates.length - 1; i >= 0; i--) { + let [label, date] = this.labelDates[i]; + let align = 'center'; // (last == null) ? 'right' : 'left' + let x = this.toX(date); + let current = this.ensureLabel(this.labelPrefix + label, label, + { + x: x, y: y, + align + }, + FontInfo.small); + let r = current.getBounds(); + current.visible = !(last != null && r.x + r.width > last.x); + if (current.visible) { + this.drawTick(x, -1); + last = r; + } + } + } + else { + let start = this.start.toLocaleDateString('de', { year: 'numeric', month: 'numeric', day: 'numeric' }); + let end = this.end.toLocaleDateString('de', { year: 'numeric', month: 'numeric', day: 'numeric' }); + this.ensureLabel(this.labelPrefix + 'start', start, { x: this.toX(this.start), y: h2 }); + this.ensureLabel(this.labelPrefix + 'end', end, { x: this.toX(this.end), y: h2, align: 'right' }); + } + } + + onZoom(zoom, anchor) { + let date = this.fromX(anchor.x); + let newZoom = this.validZoom(this.zoom * zoom, false); + this.zoom = newZoom; + let newX = this.toX(date); + this.scroll += anchor.x - newX; + } + + onStart(event, interaction) { + this.killTweens(); + this.deltas = []; + this.validScroll(); + ThrowPropsPlugin.track(this, 'delta'); + } + + onMove(event, interaction) { + let delta = interaction.delta(); + this.scroll += delta.x; + while (this.deltas.length > 10) { + this.deltas.pop(0); + } + this.deltas.push(delta.x); + if (interaction.current.size > 1) { + this.onZoom(delta.zoom, delta.about); + } + this.redraw(); + } + + onEnd(event, interaction) { + let vel = ThrowPropsPlugin.getVelocity(this, 'delta'); + ThrowPropsPlugin.untrack(this); + + this.killTweens(); + this.redraw(); + let delta = 0; + for (let d of this.deltas) { + delta += d; + } + if (this.deltas.length > 0) { + delta /= this.deltas.length; + } + this.autoScroll = true; + let anchor = interaction.current.mean(); + this.keepInBounds(delta, anchor); + + for(let key of interaction.ended.keys()) { + if (interaction.isDoubleTap(key)) { + this.onDoubleTap(event, interaction, key); + } + else if (interaction.isTap(key)) { + this.onTap(event, interaction, key); + } + else if (interaction.isLongPress(key)) { + this.onLongPress(event, interaction, key); + } + } + } + + onLongPress(event, interaction, key) { + for(let callback of this.onLongPressCallbacks) { + callback(event, interaction, key); + } + } + + onTap(event, interaction, key) { + for(let callback of this.onTapCallbacks) { + callback(event, interaction, key); + } + } + + onDoubleTap(event, interaction, key) { + for(let callback of this.onDoubleTapCallbacks) { + callback(event, interaction, key); + } + } + + _scrollMinimum(bounded) { + let total = this.totalWidth(bounded); + return -total + this.wantedWidth - 2 * this.inset + } + + _scrollMaximum(bounded) { + let total = this.totalWidth(bounded); + let limit = this.wantedWidth; + if (total > limit) + return 0 + let w = limit - 2 * this.inset; + return (w - total) / 2 + } + + scrollMinimum(bounded) { + return this._scrollMinimum(bounded) - this.extraRight + } + + scrollMaximum(bounded) { + return this._scrollMaximum(bounded) + this.extraLeft + } + + killTweens() { + TweenLite.killTweensOf(this); + this.autoScroll = false; + } + + + validScroll(bounded = true) { + let minimum = this.scrollMinimum(bounded); + let maximum = this.scrollMaximum(bounded); + if (this.scroll < minimum) { + this.scroll = minimum; + } + if (this.scroll > maximum) { + this.scroll = maximum; + } + } + + keepInBounds(delta, anchor) { + let bounded = true; + let minimum = this.scrollMinimum(bounded); + let maximum = this.scrollMaximum(bounded); + let tweens = {}; + if (this.zoom > this.maxZoom) { + tweens.zoom = this.maxZoom; + let date = this.fromX(anchor.x); + let oldZoom = this.zoom; + this.zoom = this.maxZoom; + let newX = this.toX(date); + tweens.scroll = this.scroll + anchor.x - newX; + this.zoom = oldZoom; + } + else { + if (this.zoom < this.minZoom) { + tweens.zoom = this.minZoom; + } + if (this.scroll > maximum) { + tweens.scroll = maximum; + } + if (this.scroll < minimum) { + tweens.scroll = minimum; + } + } + if (!isEmpty(tweens)) { + tweens.onUpdate = () => { this.redraw(); }; + TweenLite.to(this, 0.5, tweens).delay(0.1); + return + } + this.scroll += delta; + delta *= 0.985; + this.redraw(); + if (Math.abs(delta) > 1 && this.autoScroll) { + setTimeout(() => this.keepInBounds(delta, anchor), 1000 / 100); + } + } + + onMouseWheel(event) { + this.killTweens(); + let direction = event.detail < 0 || event.wheelDelta > 0; + let anchor = { x: event.clientX, y: event.clientY }; + const zoomFactor = 1.5; + this.onZoom((direction) ? zoomFactor : 1 / zoomFactor, anchor); + this.redraw(); + this.keepInBounds(0, anchor); + } + + showRanges(ranges, label = "Untitled", color = null) { + for (let cr of this.colorRanges) { + if (cr.label == label) + return + } + while (this.colorRanges.length >= this.rangeColors.length) { + this.colorRanges.shift(); + } + this.colorRanges.push(new ColorRanges(label, color, ranges)); + this.redraw(); + } + } + + /** + * Callback for the slider action onStart. + * + * @callback onStartCallback + * @param {object} event - The event object. + * @param {Slider} slider - A reference to the slider (also this refers to the slider). + */ + + /** + * Callback for the slider action onUpdate. + * + * @callback onUpdateCallback + * @param {object} event - The event object. + * @param {Slider} slider - A reference to the slider (also this refers to the slider). + */ + + /** + * Callback for the slider action onComplete. + * + * @callback onCompleteCallback + * @param {object} event - The event object. + * @param {Slider} slider - A reference to the slider (also this refers to the slider). + */ + + /** + * Class that represents a PixiJS Slider. + * + * @example + * // Create the app + * const app = new PIXIApp({ + * view: canvas, + * width: 900, + * height: 250 + * }).setup().run() + * + * // Create the slider + * const slider = new Slider({ + * x: 10, + * y: 20 + * }) + * + * // Add the slider to a DisplayObject + * app.scene.addChild(slider) + * + * @class + * @extends PIXI.Container + * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/slider.html|DocTest} + */ + class Slider extends PIXI.Container { + + /** + * Creates an instance of a Slider. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the slider. + * @param {number} [opts.id=auto generated] - The id of the slider. + * @param {number} [opts.x=0] - The x position of the slider. Can be also set after creation with slider.x = 0. + * @param {number} [opts.y=0] - The y position of the slider. Can be also set after creation with slider.y = 0. + * @param {string|Theme} [opts.theme=dark] - The theme to use for this slider. Possible values are dark, light, red + * or a Theme object. + * @param {number} [opts.width=250] - The width of the slider. + * @param {number} [opts.height=2] - The height of the slider. + * @param {PIXI.DisplayObject} [opts.container=window.app|object] - The container where the slider events should be attached to. + * @param {number} [opts.fill=Theme.fill] - The color of the slider background as a hex value. + * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. + * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. + * @param {number} [opts.strokeWidth=Theme.strokeWidth] - The width of the border in pixel. + * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. + * @param {number} [opts.controlFill=Theme.stroke] - The color of the slider control background as a hex value. + * @param {number} [opts.controlFillAlpha=Theme.strokeAlpha] - The alpha value of the background. + * @param {number} [opts.controlStroke=Theme.stroke] - The color of the border as a hex value. + * @param {number} [opts.controlStrokeWidth=Theme.strokeWidth * 0.8] - The width of the border in pixel. + * @param {number} [opts.controlStrokeAlpha=Theme.strokeAlpha] - The alpha value of the border. + * @param {number} [opts.controlRadius=16] - The radius of the slider control. + * @param {boolean} [opts.disabled=false] - Is the slider disabled? When disabled, the slider has a lower alpha value + * and cannot be clicked (interactive is set to false). + * @param {onStartCallback} [opts.onStart] - Executed when the slider control starts to move. + * @param {onUpdateCallback} [opts.onUpdate] - Executed when the slider control is moved. + * @param {onCompleteCallback} [opts.onComplete] - Executed when the slider control was dropped. + * @param {string|object} [opts.tooltip] - A string for the label of the tooltip or an object to configure the tooltip + * to display. + * @param {boolean} [opts.visible=true] - Is the slider initially visible (property visible)? + */ + constructor(opts = {}) { + + super(); + + const theme = Theme.fromString(opts.theme); + this.theme = theme; + + this.opts = Object.assign({}, { + id: PIXI.utils.uid(), + x: 0, + y: 0, + width: 250, + height: 2, + container: null, + fill: theme.fill, + fillAlpha: theme.fillAlpha, + stroke: theme.stroke, + strokeWidth: theme.strokeWidth, + strokeAlpha: theme.strokeAlpha, + controlFill: theme.fill, + controlFillAlpha: .5, + controlStroke: theme.primaryColor, + controlStrokeWidth: 2, + controlStrokeAlpha: theme.strokeAlpha, + controlRadius: 16, + orientation: 'horizontal', + min: 0, + max: 100, + value: 0, + disabled: false, + onStart: null, + onUpdate: null, + onComplete: null, + tooltip: null, + visible: true + }, opts); + + this.opts.container = this.opts.container || this; + + // Validation + //----------------- + if (this.opts.height > this.opts.width) { + this.opts.height = this.opts.width; + } + + if (this.opts.value < this.opts.min) { + this.opts.value = this.opts.min; + } + + if (this.opts.value > this.opts.max) { + this.opts.value = this.opts.max; + } + + // Properties + //----------------- + this.id = this.opts.id; + this.radius = this.opts.height / 2; + + this._value = this.opts.value; + this._disabled = null; + + this.sliderObj = null; + this.control = null; + this.tooltip = null; + + this.visible = this.opts.visible; + + // setup + //----------------- + this.setup(); + + // layout + //----------------- + this.layout(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {Slider} A reference to the slider for chaining. + */ + setup() { + + // Container events + //----------------- + const container = this.opts.container; + + this.on('pointermove', e => { + if (this.control.dragging) { + const moveX = this.control.event.data.getLocalPosition(this.control.parent).x; + this._value = this.pixelToValue(moveX - this.control.delta - this.opts.controlRadius); + let x = this.valueToPixel(this._value) + this.opts.controlRadius; + this.control.x = x; + + if (this.opts.onUpdate) { + this.opts.onUpdate.call(this, e, this); + } + } + }); + + if (container instanceof Element) { + container.addEventListener('pointerup', e => this.onEnd(e), false); + container.addEventListener('pointercancel', e => this.onEnd(e), false); + container.addEventListener('pointerleave', e => this.onEnd(e), false); + container.addEventListener('pointerout', e => this.onEnd(e), false); + container.addEventListener('mouseup', e => this.onEnd(e), false); + container.addEventListener('mousecancel', e => this.onEnd(e), false); + container.addEventListener('mouseleave', e => this.onEnd(e), false); + container.addEventListener('mouseout', e => this.onEnd(e), false); + } else { + container.interactive = true; + container.on('pointerup', e => this.onEnd(e)); + container.on('pointercancel', e => this.onEnd(e)); + container.on('pointerleave', e => this.onEnd(e)); + container.on('pointerout', e => this.onEnd(e)); + } + + // Slider + //----------------- + let sliderObj = new PIXI.Graphics(); + this.sliderObj = sliderObj; + this.addChild(sliderObj); + + // Control + //----------------- + let control = new PIXI.Graphics(); + control.x = this.opts.controlRadius + this.valueToPixel(this.opts.value); + control.y = this.opts.controlRadius; + + // pointerdown on the control for dragndrop + control.on('pointerdown', e => { + control.event = e; + control.delta = e.data.getLocalPosition(this.control).x; + control.dragging = true; + + if (this.opts.onStart) { + this.opts.onStart.call(this, e, this); + } + }); + + this.control = control; + + this.addChild(this.control); + + // interaction + //----------------- + this.sliderObj.on('pointerover', e => { + TweenLite.to(this.control, this.theme.fast, {alpha: .83}); + }); + + this.sliderObj.on('pointerout', e => { + TweenLite.to(this.control, this.theme.fast, {alpha: 1}); + }); + + this.sliderObj.on('pointerdown', e => { + this.sliderObj.pointerdowned = true; + TweenLite.to(this.control, this.theme.fast, {alpha: .7}); + }); + + // Click on the slider bar + this.sliderObj.on('pointerup', e => { + if (this.sliderObj.pointerdowned) { + this.sliderObj.pointerdowned = false; + const position = e.data.getLocalPosition(this.control.parent); + this.value = this.pixelToValue(position.x - this.opts.controlRadius); + TweenLite.to(this.control, this.theme.fast, {alpha: .83}); + } + }); + + // disabled + //----------------- + this.disabled = this.opts.disabled; + + // tooltip + //----------------- + if (this.opts.tooltip) { + if (typeof this.opts.tooltip === 'string') { + this.tooltip = new Tooltip({ + object: this, + content: this.opts.tooltip + }); + } else { + this.opts.tooltip.object = this; + this.tooltip = new Tooltip(this.opts.tooltip); + } + } + + return this + } + + /** + * Should be called to refresh the layout of the slider. Can be used after resizing. + * + * @return {Slider} A reference to the slider for chaining. + */ + layout() { + + // set position + //----------------- + this.position.set(this.opts.x, this.opts.y); + + // draw + //----------------- + this.draw(); + + return this + } + + /** + * Draws the slider to the canvas. + * + * @private + * @return {Slider} A reference to the slider for chaining. + */ + draw() { + + const r = this.radius; + const cr = this.opts.controlRadius; + const w = this.opts.width; + const h = this.opts.height; + const x = cr + r; + const y = cr + r - h; + + this.sliderObj.clear(); + this.sliderObj.beginFill(0xffffff, 0); + this.sliderObj.drawRect(0, 0, x + w + cr, cr * 2); + this.sliderObj.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); + this.sliderObj.beginFill(this.opts.fill, this.opts.fillAlpha); + this.sliderObj.moveTo(x, y); + this.sliderObj.lineTo(x + w, y); + this.sliderObj.arcTo(x + w + r, y, x + w + r, y + r, r); + this.sliderObj.lineTo(x + w + r, y + r + 1); // BUGFIX: If not specified, there is a small area without a stroke. + this.sliderObj.arcTo(x + w + r, y + h, x + w, y + h, r); + this.sliderObj.lineTo(x, y + h); + this.sliderObj.arcTo(x - r, y + h, x - r, y + r, r); + this.sliderObj.arcTo(x - r, y, x, y, r); + this.sliderObj.endFill(); + + // Draw control + this.control.clear(); + this.control.lineStyle(this.opts.controlStrokeWidth, this.opts.controlStroke, this.opts.controlStrokeAlpha); + this.control.beginFill(this.opts.controlFill, this.opts.controlFillAlpha); + this.control.drawCircle(0, 0, cr - 1); + this.control.beginFill(this.opts.controlStroke, this.opts.controlStrokeAlpha); + this.control.drawCircle(0, 0, cr / 6); + this.control.endFill(); + + return this + } + + /** + * Executed, when the slider control movement ended. + * + * @private + * @return {Slider} A reference to the slider for chaining. + */ + onEnd(e) { + + if (this.control.dragging) { + this.control.event = null; + this.control.dragging = false; + if (this.opts.onComplete) { + this.opts.onComplete.call(this, e, this); + } + } + + return this + } + + /** + * Calculates the value for a given pixel. + * + * @private + * @param {number} value + * @returns {number} The calucalted pixel. + */ + valueToPixel(value) { + if (value < this.opts.min) { + value = this.opts.min; + } else if (value > this.opts.max) { + value = this.opts.max; + } + return this.opts.width * (value - this.opts.min) / (this.opts.max - this.opts.min) + } + + /** + * Calculates the pixel for a given value. + * + * @private + * @param {number} pixel + * @returns {number} The calucalted value. + */ + pixelToValue(pixel) { + if (pixel < 0) { + pixel = 0; + } else if (pixel > this.opts.width) { + pixel = this.opts.width; + } + return this.opts.min + ((this.opts.max - this.opts.min) * pixel / this.opts.width) + } + + /** + * Gets or sets the value. + * + * @member {number} + */ + get value() { + return Math.round(this._value) + } + set value(value) { + if (value < this.opts.min) { + value = this.opts.min; + } else if (value > this.opts.max) { + value = this.opts.max; + } + this._value = value; + + const x = this.valueToPixel(value) + this.opts.controlRadius; + + TweenLite.to(this.control, this.theme.fast, {x}); + } + + /** + * Gets or sets the disabled state. When disabled, the slider cannot be clicked. + * + * @member {boolean} + */ + get disabled() { + return this._disabled + } + set disabled(value) { + + this._disabled = value; + + if (this._disabled) { + this.interactive = false; + this.sliderObj.interactive = false; + this.control.interactive = false; + this.control.buttonMode = false; + this.alpha = .5; + } else { + this.interactive = true; + this.sliderObj.interactive = true; + this.control.interactive = true; + this.control.buttonMode = true; + this.alpha = 1; + } + } + + /** + * Shows the slider (sets his alpha values to 1). + * + * @return {Slider} A reference to the slider for chaining. + */ + show() { + + this.opts.strokeAlpha = 1; + this.opts.fillAlpha = 1; + this.opts.controlStrokeAlpha = 1; + this.opts.controlFillAlpha = 1; + + this.layout(); + + return this + } + + /** + * Hides the slider (sets his alpha values to 1). + * + * @return {Slider} A reference to the slider for chaining. + */ + hide() { + + this.opts.strokeAlpha = 0; + this.opts.fillAlpha = 0; + this.opts.controlStrokeAlpha = 0; + this.opts.controlFillAlpha = 0; + + this.layout(); + + return this + } + } + + /** + * Callback for the switch action. + * + * @callback actionCallback + * @param {object} event - The event object. + * @param {Switch} switch - A reference to the switch (also this refers to the switch). + */ + + /** + * Callback for the switch action. + * + * @callback actionActiveCallback + * @param {object} event - The event object. + * @param {Switch} switch - A reference to the switch (also this refers to the switch). + */ + + /** + * Callback for the switch beforeAction. + * + * @callback beforeActionCallback + * @param {object} event - The event object. + * @param {Switch} switch - A reference to the switch (also this refers to the switch). + */ + + /** + * Callback for the switch afterAction. + * + * @callback afterActionCallback + * @param {object} event - The event object. + * @param {Switch} switch - A reference to the switch (also this refers to the switch). + */ + + /** + * Class that represents a PixiJS Switch. + * + * @example + * // Create the app + * const app = new PIXIApp({ + * view: canvas, + * width: 900, + * height: 250 + * }).setup().run() + * + * // Create the switch + * const switch1 = new Switch({ + * x: 10, + * y: 20 + * }) + * + * // Add the switch to a DisplayObject + * app.scene.addChild(switch1) + * + * @class + * @extends PIXI.Container + * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/switch.html|DocTest} + */ + class Switch extends PIXI.Container { + + /** + * Creates an instance of a Switch. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the switch. + * @param {number} [opts.id=auto generated] - The id of the switch. + * @param {number} [opts.x=0] - The x position of the switch. Can be also set after creation with switch.x = 0. + * @param {number} [opts.y=0] - The y position of the switch. Can be also set after creation with switch.y = 0. + * @param {string|Theme} [opts.theme=dark] - The theme to use for this switch. Possible values are dark, light, red + * or a Theme object. + * @param {number} [opts.width=44] - The width of the switch. + * @param {number} [opts.height=28] - The height of the switch. + * @param {number} [opts.fill=Theme.fill] - The color of the switch background as a hex value. + * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. + * @param {number} [opts.fillActive=Theme.fillActive] - The color of the switch background when activated. + * @param {number} [opts.fillActiveAlpha=Theme.fillActiveAlpha] - The alpha value of the background when activated. + * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. + * @param {number} [opts.strokeWidth=Theme.strokeWidth] - The width of the border in pixel. + * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. + * @param {number} [opts.strokeActive=Theme.strokeActive] - The color of the border when activated. + * @param {number} [opts.strokeActiveWidth=Theme.strokeActiveWidth] - The width of the border in pixel when activated. + * @param {number} [opts.strokeActiveAlpha=Theme.strokeActiveAlpha] - The alpha value of the border when activated. + * @param {number} [opts.controlFill=Theme.stroke] - The color of the switch control background as a hex value. + * @param {number} [opts.controlFillAlpha=Theme.strokeAlpha] - The alpha value of the background. + * @param {number} [opts.controlFillActive=Theme.stroke] - The color of the switch control background when activated. + * @param {number} [opts.controlFillActiveAlpha=Theme.strokeAlpha] - The alpha value of the background when activated. + * @param {number} [opts.controlStroke=Theme.stroke] - The color of the border as a hex value. + * @param {number} [opts.controlStrokeWidth=Theme.strokeWidth * 0.8] - The width of the border in pixel. + * @param {number} [opts.controlStrokeAlpha=Theme.strokeAlpha] - The alpha value of the border. + * @param {number} [opts.controlStrokeActive=Theme.stroke] - The color of the border when activated. + * @param {number} [opts.controlStrokeActiveWidth=Theme.strokeActiveWidth * 0.8] - The width of the border in pixel when activated. + * @param {number} [opts.controlStrokeActiveAlpha=Theme.strokeActiveAlpha] - The alpha value of the border when activated. + * @param {number} [opts.duration=Theme.fast] - The duration of the animation when the switch gets activated in seconds. + * @param {number} [opts.durationActive=Theme.fast] - The duration of the animation when the switch gets deactivated in seconds. + * @param {boolean} [opts.disabled=false] - Is the switch disabled? When disabled, the switch has a lower alpha value + * and cannot be clicked (interactive is set to false). + * @param {boolean} [opts.active=false] - Is the button initially active? + * @param {actionCallback} [opts.action] - Executed when the switch was triggered in inactive state (by pointerup). + * @param {actionActiveCallback} [opts.actionActive] - Executed when the button was triggered in active state (by pointerup). + * @param {beforeActionCallback} [opts.beforeAction] - Executed before an action is triggered. + * @param {afterActionCallback} [opts.afterAction] - Executed after an action was triggered. + * @param {string|object} [opts.tooltip] - A string for the label of the tooltip or an object to configure the tooltip + * to display. + * @param {boolean} [opts.visible=true] - Is the switch initially visible (property visible)? + */ + constructor(opts = {}) { + + super(); + + const theme = Theme.fromString(opts.theme); + this.theme = theme; + + this.opts = Object.assign({}, { + id: PIXI.utils.uid(), + x: 0, + y: 0, + width: 44, + height: 28, + fill: theme.fill, + fillAlpha: theme.fillAlpha, + fillActive: theme.primaryColor, + fillActiveAlpha: theme.fillActiveAlpha, + stroke: theme.stroke, + strokeWidth: theme.strokeWidth, + strokeAlpha: theme.strokeAlpha, + strokeActive: theme.primaryColor, + strokeActiveWidth: theme.strokeActiveWidth, + strokeActiveAlpha: theme.strokeActiveAlpha, + controlFill: theme.stroke, + controlFillAlpha: theme.strokeAlpha, + controlFillActive: theme.stroke, + controlFillActiveAlpha: theme.strokeAlpha, + controlStroke: theme.stroke, + controlStrokeWidth: theme.strokeWidth * .8, + controlStrokeAlpha: theme.strokeAlpha, + controlStrokeActive: theme.stroke, + controlStrokeActiveWidth: theme.strokeActiveWidth * .8, + controlStrokeActiveAlpha: theme.strokeActiveAlpha, + duration: theme.fast, + durationActive: theme.fast, + disabled: false, + active: false, + action: null, + actionActive: null, + beforeAction: null, + afterAction: null, + tooltip: null, + visible: true + }, opts); + + this.opts.controlRadius = this.opts.controlRadius || (this.opts.height / 2); + this.opts.controlRadiusActive = this.opts.controlRadiusActive || this.opts.controlRadius; + + // Validation + //----------------- + if (this.opts.height > this.opts.width) { + this.opts.height = this.opts.width; + } + + // Properties + //----------------- + this.id = this.opts.id; + this.radius = this.opts.height / 2; + + this._active = null; + this._disabled = null; + + this.switchObj = null; + this.control = null; + this.tooltip = null; + + this.visible = this.opts.visible; + + // animated + //----------------- + this.tempAnimated = { + fill: this.opts.fill, + fillAlpha: this.opts.fillAlpha, + stroke: this.opts.stroke, + strokeWidth: this.opts.strokeWidth, + strokeAlpha: this.opts.strokeAlpha, + controlFill: this.opts.controlFill, + controlFillAlpha: this.opts.controlFillAlpha, + controlStroke: this.opts.controlStroke, + controlStrokeWidth: this.opts.controlStrokeWidth, + controlStrokeAlpha: this.opts.controlStrokeAlpha, + controlRadius: this.opts.controlRadius + }; + + // setup + //----------------- + this.setup(); + + // layout + //----------------- + this.layout(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {Switch} A reference to the switch for chaining. + */ + setup() { + + // Switch + //----------------- + let switchObj = new PIXI.Graphics(); + this.switchObj = switchObj; + this.addChild(switchObj); + + // Control + //----------------- + this.xInactive = this.opts.controlRadius; + this.xActive = this.opts.width - this.opts.controlRadiusActive; + + let control = new PIXI.Graphics(); + control.x = this.opts.active ? this.xActive : this.xInactive; + control.y = this.opts.height / 2; + + this.control = control; + + this.addChild(this.control); + + // interaction + //----------------- + this.switchObj.on('pointerover', e => { + TweenLite.to(this.control, this.theme.fast, {alpha: .83}); + }); + + this.switchObj.on('pointerout', e => { + TweenLite.to(this.control, this.theme.fast, {alpha: 1}); + }); + + this.switchObj.on('pointerdown', e => { + TweenLite.to(this.control, this.theme.fast, {alpha: .7}); + }); + + this.switchObj.on('pointerup', e => { + + if (this.opts.beforeAction) { + this.opts.beforeAction.call(this, e, this); + } + + this.active = !this.active; + + if (this.active) { + if (this.opts.action) { + this.opts.action.call(this, e, this); + } + } else { + if (this.opts.actionActive) { + this.opts.actionActive.call(this, e, this); + } + } + + TweenLite.to(this.control, this.theme.fast, {alpha: .83}); + + if (this.opts.afterAction) { + this.opts.afterAction.call(this, e, this); + } + }); + + // disabled + //----------------- + this.disabled = this.opts.disabled; + + // active + //----------------- + this.active = this.opts.active; + + // tooltip + //----------------- + if (this.opts.tooltip) { + if (typeof this.opts.tooltip === 'string') { + this.tooltip = new Tooltip({ + object: this, + content: this.opts.tooltip + }); + } else { + this.opts.tooltip.object = this; + this.tooltip = new Tooltip(this.opts.tooltip); + } + } + + return this + } + + /** + * Should be called to refresh the layout of the switch. Can be used after resizing. + * + * @return {Switch} A reference to the switch for chaining. + */ + layout() { + + // set position + //----------------- + this.position.set(this.opts.x, this.opts.y); + + // draw + //----------------- + this.draw(); + + return this + } + + /** + * Draws the switch to the canvas. + * + * @private + * @return {Switch} A reference to the switch for chaining. + */ + draw() { + + this.switchObj.clear(); + if (this.active) { + this.switchObj.lineStyle(this.opts.strokeActiveWidth, this.opts.strokeActive, this.opts.strokeActiveAlpha); + this.switchObj.beginFill(this.opts.fillActive, this.opts.fillActiveAlpha); + } else { + this.switchObj.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); + this.switchObj.beginFill(this.opts.fill, this.opts.fillAlpha); + } + this.switchObj.moveTo(this.radius, 0); + this.switchObj.lineTo(this.opts.width - this.radius, 0); + this.switchObj.arcTo(this.opts.width, 0, this.opts.width, this.radius, this.radius); + this.switchObj.lineTo(this.opts.width, this.radius + 1); // BUGFIX: If not specified, there is a small area without a stroke. + this.switchObj.arcTo(this.opts.width, this.opts.height, this.opts.width - this.radius, this.opts.height, this.radius); + this.switchObj.lineTo(this.radius, this.opts.height); + this.switchObj.arcTo(0, this.opts.height, 0, this.radius, this.radius); + this.switchObj.arcTo(0, 0, this.radius, 0, this.radius); + this.switchObj.endFill(); + + // Draw control + this.control.clear(); + if (this.active) { + this.control.lineStyle(this.opts.controlStrokeActiveWidth, this.opts.controlStrokeActive, this.opts.controlStrokeActiveAlpha); + this.control.beginFill(this.opts.controlFillActive, this.opts.controlFillActiveAlpha); + this.control.drawCircle(0, 0, this.opts.controlRadiusActive - 1); + } else { + this.control.lineStyle(this.opts.controlStrokeWidth, this.opts.controlStroke, this.opts.controlStrokeAlpha); + this.control.beginFill(this.opts.controlFill, this.opts.controlFillAlpha); + this.control.drawCircle(0, 0, this.opts.controlRadius - 1); + } + this.control.endFill(); + + return this + } + + /** + * Draws the animation. + * + * @private + * @return {Switch} A reference to the switch for chaining. + */ + drawAnimated() { + + this.switchObj.clear(); + this.switchObj.lineStyle(this.tempAnimated.strokeWidth, this.tempAnimated.stroke, this.tempAnimated.strokeAlpha); + this.switchObj.beginFill(this.tempAnimated.fill, this.tempAnimated.fillAlpha); + this.switchObj.moveTo(this.radius, 0); + this.switchObj.lineTo(this.opts.width - this.radius, 0); + this.switchObj.arcTo(this.opts.width, 0, this.opts.width, this.radius, this.radius); + this.switchObj.lineTo(this.opts.width, this.radius + 1); // BUGFIX: If not specified, there is a small area without a stroke. + this.switchObj.arcTo(this.opts.width, this.opts.height, this.opts.width - this.radius, this.opts.height, this.radius); + this.switchObj.lineTo(this.radius, this.opts.height); + this.switchObj.arcTo(0, this.opts.height, 0, this.radius, this.radius); + this.switchObj.arcTo(0, 0, this.radius, 0, this.radius); + this.switchObj.endFill(); + + this.control.clear(); + this.control.lineStyle(this.tempAnimated.controlStrokeWidth, this.tempAnimated.controlStroke, this.tempAnimated.controlStrokeAlpha); + this.control.beginFill(this.tempAnimated.controlFill, this.tempAnimated.controlFillAlpha); + this.control.drawCircle(0, 0, this.tempAnimated.controlRadius - 1); + this.control.endFill(); + + return this + } + + /** + * Gets or sets the active state. + * + * @member {boolean} + */ + get active() { + return this._active + } + + set active(value) { + + this._active = value; + + if (this._active) { + + TweenLite.to(this.control, this.opts.duration, {x: this.xActive}); + TweenLite.to(this.tempAnimated, this.opts.duration, { + colorProps: { + fill: this.opts.fillActive, + stroke: this.opts.strokeActive, + controlFill: this.opts.controlFillActive, + controlStroke: this.opts.controlStrokeActive, + format: 'number' + }, + fillAlpha: this.opts.fillActiveAlpha, + strokeWidth: this.opts.strokeActiveWidth, + strokeAlpha: this.opts.strokeActiveAlpha, + controlFillAlpha: this.opts.controlFillActiveAlpha, + controlStrokeWidth: this.opts.controlStrokeActiveWidth, + controlStrokeAlpha: this.opts.controlStrokeActiveAlpha, + controlRadius: this.opts.controlRadiusActive, + onUpdate: () => this.drawAnimated(), + onComplete: () => this.draw() + }); + + + } else { + TweenLite.to(this.control, this.opts.durationActive, {x: this.xInactive}); + TweenLite.to(this.tempAnimated, this.opts.durationActive, { + colorProps: { + fill: this.opts.fill, + stroke: this.opts.stroke, + controlFill: this.opts.controlFill, + controlStroke: this.opts.controlStroke, + format: 'number' + }, + fillAlpha: this.opts.fillAlpha, + strokeWidth: this.opts.strokeWidth, + strokeAlpha: this.opts.strokeAlpha, + controlFillAlpha: this.opts.controlFillAlpha, + controlStrokeWidth: this.opts.controlStrokeWidth, + controlStrokeAlpha: this.opts.controlStrokeAlpha, + controlRadius: this.opts.controlRadius, + onUpdate: () => this.drawAnimated(), + onComplete: () => this.draw() + }); + } + } + + /** + * Gets or sets the disabled state. When disabled, the switch cannot be clicked. + * + * @member {boolean} + */ + get disabled() { + return this._disabled + } + + set disabled(value) { + + this._disabled = value; + + if (this._disabled) { + this.switchObj.interactive = false; + this.switchObj.buttonMode = false; + this.switchObj.alpha = .5; + this.control.alpha = .5; + } else { + this.switchObj.interactive = true; + this.switchObj.buttonMode = true; + this.switchObj.alpha = 1; + this.control.alpha = 1; + } + } + + /** + * Shows the switch (sets his alpha values to 1). + * + * @return {Switch} A reference to the switch for chaining. + */ + show() { + + this.opts.strokeAlpha = 1; + this.opts.strokeActiveAlpha = 1; + this.opts.fillAlpha = 1; + this.opts.fillActiveAlpha = 1; + this.opts.controlStrokeAlpha = 1; + this.opts.controlStrokeActiveAlpha = 1; + this.opts.controlFillAlpha = 1; + this.opts.controlFillActiveAlpha = 1; + + this.layout(); + + return this + } + + /** + * Hides the switch (sets his alpha values to 1). + * + * @return {Switch} A reference to the switch for chaining. + */ + hide() { + + this.opts.strokeAlpha = 0; + this.opts.strokeActiveAlpha = 0; + this.opts.fillAlpha = 0; + this.opts.fillActiveAlpha = 0; + this.opts.controlStrokeAlpha = 0; + this.opts.controlStrokeActiveAlpha = 0; + this.opts.controlFillAlpha = 0; + this.opts.controlFillActiveAlpha = 0; + + this.layout(); + + return this + } + } + + /** + * Class that represents a PixiJS PopupMenu. + * + * @example + * // Create the button and the modal when clicked + * const button = new Button({ + * label: 'Show PopupMenu', + * action: e => { + * const popupmenu = new PopupMenu({ + * items: [ + * {label: 'Save', action: () => alert('Saved')}, + * {label: 'Edit', action: () => alert('Edited')}, + * {label: 'Delete', action: () => alert('Deleted')} + * ] + * }) + * app.scene.addChild(popupmenu) + * } + * }) + * + * // Add the button to a DisplayObject + * app.scene.addChild(button) + * + * @class + * @extends Popup + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/popupmenu.html|DocTest} + */ + class PopupMenu$1 extends Popup { + + /** + * Creates an instance of a PopupMenu. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the modal. + * @param {object[]} [opts.items=[]] - A list of the menu items. Each item must be of type object. + * If an object has a label property, a PIXI.Text object is created (using the textStyle property). + * If an object hasn't a label property, it must contain a content property which has to be a + * PIXI.DisplayObject. + * @param {number} [opts.margin=Theme.margin / 2] - The app where the modal belongs to. + * @param {object} [opts.textStyle=Theme.textStyle] - The color of the background. + * @param {boolean} [opts.closeOnPopup=true] - The opacity of the background. + */ + constructor(opts = {}) { + + const theme = Theme.fromString(opts.theme); + + opts = Object.assign({}, { + items: [], + margin: theme.margin / 2, + textStyle: theme.textStyle, + closeOnPopup: true + }, opts); + + super(opts); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {PopupMenu} A reference to the popupmenu for chaining. + */ + setup() { + + // content + //----------------- + const content = new PIXI.Container(); + + let y = 0; + for (let item of this.opts.items) { + + let object = null; + + if (item.label) { + object = new PIXI.Text(item.label, item.textStyle || this.opts.textStyle); + } else { + object = item.content; + } + + object.y = y; + + if (item.action) { + if (item.disabled) { + object.alpha = .5; + } else { + object.interactive = true; + object.buttonMode = true; + } + object.on('pointerover', e => { + TweenLite.to(object, this.theme.fast, {alpha: .83, overwrite: 'none'}); + }); + object.on('pointerout', e => { + TweenLite.to(object, this.theme.fast, {alpha: 1, overwrite: 'none'}); + }); + object.on('pointerup', e => { + item.action.call(object, e, object); + if (this.opts.closeOnAction) { + this.hide(); + } + }); + } + + content.addChild(object); + + y += object.height + this.opts.margin; + } + + this.opts.content = content; + + super.setup(); + } + } + + /* global Quad */ + + /** + * Class that represents a PixiJS Volatile. + * + * @example + * const app = new PIXIApp({ + * view: canvas, + * width: 900, + * height: 250 + * }).setup().run() + * + * const button = new Button({ + * label: 'Volatile!', + * action: () => { + * new Volatile({ + * object: button, + * direction: 'right', + * destroyOnComplete: false + * }) + * } + * }) + * + * app.scene.addChild(button) + * + * @class + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/volatile.html|DocTest} + */ + class Volatile { + + /** + * Creates an instance of a Volatile. + * + * @constructor + * @param {object} [opts] - An options object to specify to style and behaviour of the modal. + * @param {number} [opts.id=auto generated] - The id of the tooltip. + * @param {PIXI.DisplayObject|PIXI.DisplayObject[]} opts.object - The object where the volatile should be applied to. + * @param {string} [opts.direction=top] - The animation direction. Possible values: top, right, bottom, left. + * @param {function} [opts.onStart] - A function which is executed when the volatile animation starts. + * @param {function} [opts.onComplete] - A function which is executed when the volatile animation finishes. + * @param {number} [opts.distance=160] - The animation distance. + * @param {number} [opts.duration=1.5] - The duration of the animation in seconds. + * @param {object} [opts.ease=Quad.easeOut] - The easing of the animation, see {@link https://greensock.com/docs/Easing} + * @param {boolean} [opts.destroyOnComplete=true] - Should the object be destroyed after the volatile animation? + */ + constructor(opts = {}) { + + const theme = Theme.fromString(opts.theme); + this.theme = theme; + + this.opts = Object.assign({}, { + id: PIXI.utils.uid(), + object: null, + direction: 'top', // top, right, bottom, left + onStart: null, + onComplete: null, + distance: 160, + duration: 1.5, + ease: Quad.easeOut, + destroyOnComplete: true + }, opts); + + this.id = this.opts.id; + + if (!Array.isArray(this.opts.object)) { + this.opts.object = [this.opts.object]; + } + + this.objects = this.opts.object; + + // setup + //----------------- + this.setup(); + + // layout + //----------------- + this.layout(); + + // run + //----------------- + this.run(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {Volatile} A reference to the volatile for chaining. + */ + setup() { + + return this + } + + /** + * Should be called to refresh the layout of the volatile. Can be used after resizing. + * + * @return {Volatile} A reference to the volatile for chaining. + */ + layout() { + + return this + } + + /** + * Starts the volatile animation. + * + * @private + * @return {Volatile} A reference to the volatile for chaining. + */ + run() { + + for (let object of this.objects) { + + let x = object.x; + let y = object.y; + + switch (this.opts.direction) { + case 'top': + y -= this.opts.distance; + break + case 'right': + x += this.opts.distance; + break + case 'bottom': + y += this.opts.distance; + break + case 'left': + x -= this.opts.distance; + break + } + + TweenLite.to(object, this.opts.duration, { + x, + y, + alpha: 0, + ease: this.opts.ease, + overwrite: 'all', + onStart: () => { + if (this.opts.onStart) { + this.opts.onStart.call(object, object); + } + }, + onComplete: () => { + + if (this.opts.onComplete) { + this.opts.onComplete.call(object, object); + } + + if (this.opts.destroyOnComplete) { + object.destroy({children: true}); + } + } + }); + } + + return this + } + } + + /* globals */ + + /** + * Class that represents a PixiJS List. + * + * @example + * const elephant1 = PIXI.Sprite.fromImage('./assets/elephant-1.jpg') + * const elephant2 = PIXI.Sprite.fromImage('./assets/elephant-2.jpg') + * + * // Create the list + * const list = new List([elephant1, elephant2]) + * + * app.scene.addChild(list) + * + * @class + * @extends PIXI.Container + * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PixiJS Container} + * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/list.html|DocTest} + */ + class List extends PIXI.Container { + + /** + * Creates an instance of a Flippable. + * + * @constructor + * @param {PIXI.DisplayObject[]} items - An array of PIXI.DisplayObjects. + * @param {object} [opts] - An options object which can contain the following properties. + * @param {number} [opts.width] - The width of the list. If the items are larger than this width, the overflow + * will be hidden. + * @param {number} [opts.height] - The height of the list. If the items are larger than this height, the overflow + * will be hidden. + * @param {number} [opts.padding=10] - The inner spacing (distance from one item to the previous/next item). + * @param {number} [opts.margin=10] - The outer spacing (distance from one item to the border). + * @param {string} [opts.orientation=vertical] - The orientation of the button group. Can be horizontal or vertical. + * @param {string} [opts.align=left] - The horizontal position of the items. Possible values are + * left, center and right. + * @param {string} [opts.verticalAlign=middle] - The vertical position of the items. Possible values are + * top, middle and bottom. + */ + constructor(items = [], opts = {}) { + + super(); + + this.opts = Object.assign({}, { + padding: 10, + margin: 10, + orientation: 'vertical', + align: 'left', + verticalAlign: 'middle', + width: null, + height: null + }, opts); + + this.__items = items; + this.__dragging = false; + + // setup + //-------------------- + this.setup(); + } + + /** + * Creates children and instantiates everything. + * + * @private + * @return {List} A reference to the list for chaining. + */ + setup() { + + // inner container + //-------------------- + const container = new PIXI.Container(); + this.addChild(container); + this.container = container; + + // mask + //-------------------- + const mask = new PIXI.Graphics(); + this.addChild(mask); + this.__mask = mask; + + // add items + //-------------------- + for(let item of this.__items) { + container.addChild(item); + } + + // interaction + //-------------------- + this.interactive = this.opts.width || this.opts.height; + this.on('pointerdown', this.onStart.bind(this)); + this.on('pointermove', this.onMove.bind(this)); + this.on('pointerup', this.onEnd.bind(this)); + this.on('pointercancel', this.onEnd.bind(this)); + this.on('pointerout', this.onEnd.bind(this)); + this.on('pointerupoutside', this.onEnd.bind(this)); + + this.layout(); + + return this + } + + /** + * Replaces the existing items and relayouts the list. + * + * @param {PIXI.DisplayObject[]} items - An array of PIXI.DisplayObjects. + * @return {List} A reference to the list for chaining. + */ + setItems(items) { + this.container.removeChildren(); + this.__items = items; + for(let item of this.__items) { + this.container.addChild(item); + } + this.layout(); + } + + /** + * Should be called to refresh the layout of the list (the width or the height). + * + * @return {List} A reference to the list for chaining. + */ + layout() { + + const margin = this.opts.margin; + + let x = margin; + let y = margin; + + for (let item of this.__items) { + + item.x = x; + item.y = y; + + if (this.opts.orientation === 'vertical') { + y += item.height + this.opts.padding; + } else { + x += item.width + this.opts.padding; + } + } + + // vertical + //-------------------- + if (this.opts.orientation === 'vertical') { + switch (this.opts.align) { + case 'center': + this.__items.forEach(it => it.x = margin + this.width / 2 - it.width / 2); + break + case 'right': + this.__items.forEach(it => it.x = margin + this.width - it.width); + break + default: + this.__items.forEach(it => it.x = margin); + break + } + + if (this.opts.height) { + const mask = this.__mask; + mask.clear(); + mask.beginFill(0x000); + mask.drawRect(0, 0, this.width + 2 * margin, this.opts.height); + this.mask = mask; + + this.interactive = this.innerHeight > this.opts.height; + } + } + + // horizontal + //-------------------- + if (this.opts.orientation === 'horizontal') { + switch (this.opts.verticalAlign) { + case 'top': + this.__items.forEach(it => it.y = margin); + break + case 'bottom': + this.__items.forEach(it => it.y = margin + this.height - it.height); + break + default: + this.__items.forEach(it => it.y = margin + this.height / 2 - it.height / 2); + break + } + + if (this.opts.width) { + const mask = this.__mask; + mask.clear(); + mask.beginFill(0x000); + mask.drawRect(0, 0, this.opts.width, this.height + 2 * margin); + this.mask = mask; + + this.interactive = this.innerWidth > this.opts.width; + } + } + + return this + } + + /** + * + */ + get innerWidth() { + + let size = 0; + + this.__items.forEach(it => size += it.width); + size += this.opts.padding * (this.__items.length - 1); + size += 2 * this.opts.margin; + + return size + } + + /** + * + */ + get innerHeight() { + + let size = 0; + + this.__items.forEach(it => size += it.height); + size += this.opts.padding * (this.__items.length - 1); + size += 2 * this.opts.margin; + + return size + } + + /** + * Resizes the list. + * + * @param {number} widthOrHeight - The new width (if orientation is horizontal) or height (if orientation is vertical) of the list. + */ + resize(widthOrHeight) { + + if (this.opts.orientation === 'horizontal') { + this.opts.width = widthOrHeight; + } else { + this.opts.height = widthOrHeight; + } + + this.layout(); + } + + /** + * + * @private + * @param {*} event + */ + onStart(event) { + + this.__dragging = true; + + this.capture(event); + + this.__delta = { + x: this.container.position.x - event.data.global.x, + y: this.container.position.y - event.data.global.y + }; + + TweenLite.killTweensOf(this.container.position, {x: true, y: true}); + ThrowPropsPlugin.track(this.container.position, 'x,y'); + } + + /** + * + * @private + * @param {*} event + */ + onMove(event) { + + if (this.__dragging) { + + this.capture(event); + + if (this.opts.orientation === 'horizontal') { + this.container.position.x = event.data.global.x + this.__delta.x; + } else { + this.container.position.y = event.data.global.y + this.__delta.y; + } + } + } + + /** + * + * @private + * @param {*} event + */ + onEnd(event) { + + if (this.__dragging) { + this.__dragging = false; + + this.capture(event); + + const throwProps = {}; + + if (this.opts.orientation === 'horizontal') { + let min = this.opts.width - this.innerWidth; + min = min > 0 ? 0 : min; + throwProps.x = { + velocity: 'auto', + min, + max: 0 + }; + } else { + let min = this.opts.height - this.innerHeight; + min = min > 0 ? 0 : min; + throwProps.y = { + velocity: 'auto', + min, + max: 0 + }; + } + + ThrowPropsPlugin.to(this.container.position, { + throwProps, + ease: Strong.easeOut, + onComplete: () => ThrowPropsPlugin.untrack(this.container.position) + }, .8, .4); + } + } + + /** + * Captures an event to inform InteractionMapper about processed events. + * + * @param {event|PIXI.InteractionEvent} event - The PIXI event to capture. + */ + capture(event) { + Events$1.capturedBy(event.data.originalEvent, this); + } + } + + /* Needed to ensure that rollup.js includes class definitions and the classes + are visible inside doctests. + */ + window.PIXIApp = PIXIApp; + window.BlurFilter = BlurFilter; + window.FlipEffect = FlipEffect; + window.Flippable = Flippable; + window.DeepZoomInfo = DeepZoomInfo; + window.DeepZoomImage = DeepZoomImage; + window.Popover = Popover; + window.ScatterContainer = ScatterContainer; + window.DisplayObjectScatter = DisplayObjectScatter; + window.Command = Command; + window.RecorderTools = RecorderTools; + window.Timeline = Timeline; + window.AppTest = AppTest; + window.Theme = Theme; + window.Button = Button; + window.ButtonGroup = ButtonGroup; + window.Slider = Slider; + window.Switch = Switch; + window.Popup = Popup; + window.PopupMenu = PopupMenu$1; + window.Modal = Modal; + window.Volatile = Volatile; + window.Message = Message; + window.Tooltip = Tooltip; + window.Badge = Badge; + window.Progress = Progress; + window.List = List; + window.LabeledGraphics = LabeledGraphics; + window.FontInfo = FontInfo; + +}()); diff --git a/3rdparty/3rdparty.js b/lib/3rdparty/3rdparty.js similarity index 100% rename from 3rdparty/3rdparty.js rename to lib/3rdparty/3rdparty.js diff --git a/3rdparty/LICENSES.txt b/lib/3rdparty/LICENSES.txt similarity index 100% rename from 3rdparty/LICENSES.txt rename to lib/3rdparty/LICENSES.txt diff --git a/3rdparty/PATCHES.md b/lib/3rdparty/PATCHES.md similarity index 100% rename from 3rdparty/PATCHES.md rename to lib/3rdparty/PATCHES.md diff --git a/3rdparty/codemirror/addon/scroll/simplescrollbars.css b/lib/3rdparty/codemirror/addon/scroll/simplescrollbars.css similarity index 100% rename from 3rdparty/codemirror/addon/scroll/simplescrollbars.css rename to lib/3rdparty/codemirror/addon/scroll/simplescrollbars.css diff --git a/3rdparty/codemirror/addon/scroll/simplescrollbars.js b/lib/3rdparty/codemirror/addon/scroll/simplescrollbars.js similarity index 100% rename from 3rdparty/codemirror/addon/scroll/simplescrollbars.js rename to lib/3rdparty/codemirror/addon/scroll/simplescrollbars.js diff --git a/3rdparty/codemirror/codemirror.css b/lib/3rdparty/codemirror/codemirror.css similarity index 100% rename from 3rdparty/codemirror/codemirror.css rename to lib/3rdparty/codemirror/codemirror.css diff --git a/3rdparty/codemirror/codemirror.js b/lib/3rdparty/codemirror/codemirror.js similarity index 100% rename from 3rdparty/codemirror/codemirror.js rename to lib/3rdparty/codemirror/codemirror.js diff --git a/3rdparty/codemirror/mode/css/css.js b/lib/3rdparty/codemirror/mode/css/css.js similarity index 100% rename from 3rdparty/codemirror/mode/css/css.js rename to lib/3rdparty/codemirror/mode/css/css.js diff --git a/3rdparty/codemirror/mode/css/gss.html b/lib/3rdparty/codemirror/mode/css/gss.html similarity index 100% rename from 3rdparty/codemirror/mode/css/gss.html rename to lib/3rdparty/codemirror/mode/css/gss.html diff --git a/3rdparty/codemirror/mode/css/gss_test.js b/lib/3rdparty/codemirror/mode/css/gss_test.js similarity index 100% rename from 3rdparty/codemirror/mode/css/gss_test.js rename to lib/3rdparty/codemirror/mode/css/gss_test.js diff --git a/3rdparty/codemirror/mode/css/index.html b/lib/3rdparty/codemirror/mode/css/index.html similarity index 100% rename from 3rdparty/codemirror/mode/css/index.html rename to lib/3rdparty/codemirror/mode/css/index.html diff --git a/3rdparty/codemirror/mode/css/less.html b/lib/3rdparty/codemirror/mode/css/less.html similarity index 100% rename from 3rdparty/codemirror/mode/css/less.html rename to lib/3rdparty/codemirror/mode/css/less.html diff --git a/3rdparty/codemirror/mode/css/less_test.js b/lib/3rdparty/codemirror/mode/css/less_test.js similarity index 100% rename from 3rdparty/codemirror/mode/css/less_test.js rename to lib/3rdparty/codemirror/mode/css/less_test.js diff --git a/3rdparty/codemirror/mode/css/scss.html b/lib/3rdparty/codemirror/mode/css/scss.html similarity index 100% rename from 3rdparty/codemirror/mode/css/scss.html rename to lib/3rdparty/codemirror/mode/css/scss.html diff --git a/3rdparty/codemirror/mode/css/scss_test.js b/lib/3rdparty/codemirror/mode/css/scss_test.js similarity index 100% rename from 3rdparty/codemirror/mode/css/scss_test.js rename to lib/3rdparty/codemirror/mode/css/scss_test.js diff --git a/3rdparty/codemirror/mode/css/test.js b/lib/3rdparty/codemirror/mode/css/test.js similarity index 100% rename from 3rdparty/codemirror/mode/css/test.js rename to lib/3rdparty/codemirror/mode/css/test.js diff --git a/3rdparty/codemirror/mode/htmlmixed/htmlmixed.js b/lib/3rdparty/codemirror/mode/htmlmixed/htmlmixed.js similarity index 100% rename from 3rdparty/codemirror/mode/htmlmixed/htmlmixed.js rename to lib/3rdparty/codemirror/mode/htmlmixed/htmlmixed.js diff --git a/3rdparty/codemirror/mode/htmlmixed/index.html b/lib/3rdparty/codemirror/mode/htmlmixed/index.html similarity index 100% rename from 3rdparty/codemirror/mode/htmlmixed/index.html rename to lib/3rdparty/codemirror/mode/htmlmixed/index.html diff --git a/3rdparty/codemirror/mode/javascript/index.html b/lib/3rdparty/codemirror/mode/javascript/index.html similarity index 100% rename from 3rdparty/codemirror/mode/javascript/index.html rename to lib/3rdparty/codemirror/mode/javascript/index.html diff --git a/3rdparty/codemirror/mode/javascript/javascript.js b/lib/3rdparty/codemirror/mode/javascript/javascript.js similarity index 100% rename from 3rdparty/codemirror/mode/javascript/javascript.js rename to lib/3rdparty/codemirror/mode/javascript/javascript.js diff --git a/3rdparty/codemirror/mode/javascript/json-ld.html b/lib/3rdparty/codemirror/mode/javascript/json-ld.html similarity index 100% rename from 3rdparty/codemirror/mode/javascript/json-ld.html rename to lib/3rdparty/codemirror/mode/javascript/json-ld.html diff --git a/3rdparty/codemirror/mode/javascript/test.js b/lib/3rdparty/codemirror/mode/javascript/test.js similarity index 100% rename from 3rdparty/codemirror/mode/javascript/test.js rename to lib/3rdparty/codemirror/mode/javascript/test.js diff --git a/3rdparty/codemirror/mode/javascript/typescript.html b/lib/3rdparty/codemirror/mode/javascript/typescript.html similarity index 100% rename from 3rdparty/codemirror/mode/javascript/typescript.html rename to lib/3rdparty/codemirror/mode/javascript/typescript.html diff --git a/3rdparty/codemirror/mode/xml/xml.js b/lib/3rdparty/codemirror/mode/xml/xml.js similarity index 100% rename from 3rdparty/codemirror/mode/xml/xml.js rename to lib/3rdparty/codemirror/mode/xml/xml.js diff --git a/3rdparty/convertPointFromPageToNode.js b/lib/3rdparty/convertPointFromPageToNode.js similarity index 100% rename from 3rdparty/convertPointFromPageToNode.js rename to lib/3rdparty/convertPointFromPageToNode.js diff --git a/3rdparty/create_all_js.py b/lib/3rdparty/create_all_js.py similarity index 65% rename from 3rdparty/create_all_js.py rename to lib/3rdparty/create_all_js.py index bb79dc9..706f51a 100755 --- a/3rdparty/create_all_js.py +++ b/lib/3rdparty/create_all_js.py @@ -7,26 +7,24 @@ Module docstring. import sys, os, optparse items = ( - "jquery.js", -# "jspolygon.js", - "optimal-select.js", - "hammer.js", - "hammer.propagating.js", - "d3/d3.js", - "d3/d3-selection-multi.js", - "highlight", - "pixi/pixi.js", - "pixi/lib/crn_decomp.js", - "pixi/pixi-compressed-textures.js", - "pixi/pixi-filters.js", - "pixi/pixi-particles.js", - "pixi/pixi-projection.js", - "greensock/src/uncompressed", - "greensock/src/uncompressed/easing", - "greensock/src/uncompressed/plugins", - "greensock/src/uncompressed/utils", - "convertPointFromPageToNode.js" -# "getRelativeURL.js" + "./lib/3rdparty/jquery.js", + "./lib/3rdparty/optimal-select.js", + "./lib/3rdparty/hammer.js", + "./lib/3rdparty/hammer.propagating.js", + "./lib/3rdparty/d3/d3.js", + "./lib/3rdparty/d3/d3-selection-multi.js", + "./lib/3rdparty/highlight", + "./lib/3rdparty/pixi/pixi.js", + "./lib/3rdparty/pixi/lib/crn_decomp.js", + "./lib/3rdparty/pixi/pixi-compressed-textures.js", + "./lib/3rdparty/pixi/pixi-filters.js", + "./lib/3rdparty/pixi/pixi-particles.js", + "./lib/3rdparty/pixi/pixi-projection.js", + "./lib/3rdparty/greensock/src/uncompressed", + "./lib/3rdparty/greensock/src/uncompressed/easing", + "./lib/3rdparty/greensock/src/uncompressed/plugins", + "./lib/3rdparty/greensock/src/uncompressed/utils", + "./lib/3rdparty/convertPointFromPageToNode.js" ) def process_command_line(argv): @@ -64,7 +62,7 @@ def main(argv=None): return 0 # success def run(settings, args): - with open("all.js", 'w') as outfile: + with open("./dist/iwmlib.3rdparty.js", 'w') as outfile: for item in items: if item.endswith(".js"): appendFile(outfile, item) diff --git a/3rdparty/create_preload_js.py b/lib/3rdparty/create_preload_js.py similarity index 100% rename from 3rdparty/create_preload_js.py rename to lib/3rdparty/create_preload_js.py diff --git a/3rdparty/d3/d3-selection-multi.js b/lib/3rdparty/d3/d3-selection-multi.js similarity index 100% rename from 3rdparty/d3/d3-selection-multi.js rename to lib/3rdparty/d3/d3-selection-multi.js diff --git a/3rdparty/d3/d3-selection-multi.min.js b/lib/3rdparty/d3/d3-selection-multi.min.js similarity index 100% rename from 3rdparty/d3/d3-selection-multi.min.js rename to lib/3rdparty/d3/d3-selection-multi.min.js diff --git a/3rdparty/d3/d3.js b/lib/3rdparty/d3/d3.js similarity index 100% rename from 3rdparty/d3/d3.js rename to lib/3rdparty/d3/d3.js diff --git a/3rdparty/d3/d3.min.js b/lib/3rdparty/d3/d3.min.js similarity index 100% rename from 3rdparty/d3/d3.min.js rename to lib/3rdparty/d3/d3.min.js diff --git a/3rdparty/d3/topojson.v2.min.js b/lib/3rdparty/d3/topojson.v2.min.js similarity index 100% rename from 3rdparty/d3/topojson.v2.min.js rename to lib/3rdparty/d3/topojson.v2.min.js diff --git a/3rdparty/dexie.js b/lib/3rdparty/dexie.js similarity index 100% rename from 3rdparty/dexie.js rename to lib/3rdparty/dexie.js diff --git a/3rdparty/dexie.js.map b/lib/3rdparty/dexie.js.map similarity index 100% rename from 3rdparty/dexie.js.map rename to lib/3rdparty/dexie.js.map diff --git a/3rdparty/greensock/docs.html b/lib/3rdparty/greensock/docs.html similarity index 100% rename from 3rdparty/greensock/docs.html rename to lib/3rdparty/greensock/docs.html diff --git a/3rdparty/greensock/getting_started.html b/lib/3rdparty/greensock/getting_started.html similarity index 100% rename from 3rdparty/greensock/getting_started.html rename to lib/3rdparty/greensock/getting_started.html diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/CustomBounce.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/CustomBounce.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/CustomBounce.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/CustomBounce.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/CustomEase.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/CustomEase.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/CustomEase.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/CustomEase.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/CustomWiggle.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/CustomWiggle.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/CustomWiggle.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/CustomWiggle.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/DrawSVGPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/DrawSVGPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/DrawSVGPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/DrawSVGPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/GSDevTools.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/GSDevTools.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/GSDevTools.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/GSDevTools.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/MorphSVGPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/MorphSVGPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/MorphSVGPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/MorphSVGPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/Physics2DPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/Physics2DPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/Physics2DPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/Physics2DPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/PhysicsPropsPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/PhysicsPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/PhysicsPropsPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/PhysicsPropsPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/ScrambleTextPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/ScrambleTextPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/ScrambleTextPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/ScrambleTextPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/SplitText.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/SplitText.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/SplitText.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/SplitText.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/ThrowPropsPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/ThrowPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/ThrowPropsPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/ThrowPropsPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomBounce.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomBounce.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomBounce.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomBounce.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomEase.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomEase.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomEase.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomEase.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomWiggle.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomWiggle.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomWiggle.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/CustomWiggle.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/DrawSVGPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/DrawSVGPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/DrawSVGPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/DrawSVGPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/GSDevTools.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/GSDevTools.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/GSDevTools.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/GSDevTools.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/MorphSVGPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/MorphSVGPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/MorphSVGPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/MorphSVGPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/Physics2DPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/Physics2DPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/Physics2DPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/Physics2DPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/PhysicsPropsPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/PhysicsPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/PhysicsPropsPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/PhysicsPropsPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/ScrambleTextPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/ScrambleTextPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/ScrambleTextPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/ScrambleTextPlugin.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/SplitText.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/SplitText.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/SplitText.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/SplitText.js diff --git a/3rdparty/greensock/src/bonus-files-for-npm-users/umd/ThrowPropsPlugin.js b/lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/ThrowPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/bonus-files-for-npm-users/umd/ThrowPropsPlugin.js rename to lib/3rdparty/greensock/src/bonus-files-for-npm-users/umd/ThrowPropsPlugin.js diff --git a/3rdparty/greensock/src/esm/AttrPlugin.js b/lib/3rdparty/greensock/src/esm/AttrPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/AttrPlugin.js rename to lib/3rdparty/greensock/src/esm/AttrPlugin.js diff --git a/3rdparty/greensock/src/esm/BezierPlugin.js b/lib/3rdparty/greensock/src/esm/BezierPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/BezierPlugin.js rename to lib/3rdparty/greensock/src/esm/BezierPlugin.js diff --git a/3rdparty/greensock/src/esm/CSSPlugin.js b/lib/3rdparty/greensock/src/esm/CSSPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/CSSPlugin.js rename to lib/3rdparty/greensock/src/esm/CSSPlugin.js diff --git a/3rdparty/greensock/src/esm/CSSRulePlugin.js b/lib/3rdparty/greensock/src/esm/CSSRulePlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/CSSRulePlugin.js rename to lib/3rdparty/greensock/src/esm/CSSRulePlugin.js diff --git a/3rdparty/greensock/src/esm/ColorPropsPlugin.js b/lib/3rdparty/greensock/src/esm/ColorPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/ColorPropsPlugin.js rename to lib/3rdparty/greensock/src/esm/ColorPropsPlugin.js diff --git a/3rdparty/greensock/src/esm/DirectionalRotationPlugin.js b/lib/3rdparty/greensock/src/esm/DirectionalRotationPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/DirectionalRotationPlugin.js rename to lib/3rdparty/greensock/src/esm/DirectionalRotationPlugin.js diff --git a/3rdparty/greensock/src/esm/Draggable.js b/lib/3rdparty/greensock/src/esm/Draggable.js similarity index 100% rename from 3rdparty/greensock/src/esm/Draggable.js rename to lib/3rdparty/greensock/src/esm/Draggable.js diff --git a/3rdparty/greensock/src/esm/EasePack.js b/lib/3rdparty/greensock/src/esm/EasePack.js similarity index 100% rename from 3rdparty/greensock/src/esm/EasePack.js rename to lib/3rdparty/greensock/src/esm/EasePack.js diff --git a/3rdparty/greensock/src/esm/EaselPlugin.js b/lib/3rdparty/greensock/src/esm/EaselPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/EaselPlugin.js rename to lib/3rdparty/greensock/src/esm/EaselPlugin.js diff --git a/3rdparty/greensock/src/esm/EndArrayPlugin.js b/lib/3rdparty/greensock/src/esm/EndArrayPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/EndArrayPlugin.js rename to lib/3rdparty/greensock/src/esm/EndArrayPlugin.js diff --git a/3rdparty/greensock/src/esm/ModifiersPlugin.js b/lib/3rdparty/greensock/src/esm/ModifiersPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/ModifiersPlugin.js rename to lib/3rdparty/greensock/src/esm/ModifiersPlugin.js diff --git a/3rdparty/greensock/src/esm/PixiPlugin.js b/lib/3rdparty/greensock/src/esm/PixiPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/PixiPlugin.js rename to lib/3rdparty/greensock/src/esm/PixiPlugin.js diff --git a/3rdparty/greensock/src/esm/RoundPropsPlugin.js b/lib/3rdparty/greensock/src/esm/RoundPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/RoundPropsPlugin.js rename to lib/3rdparty/greensock/src/esm/RoundPropsPlugin.js diff --git a/3rdparty/greensock/src/esm/ScrollToPlugin.js b/lib/3rdparty/greensock/src/esm/ScrollToPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/ScrollToPlugin.js rename to lib/3rdparty/greensock/src/esm/ScrollToPlugin.js diff --git a/3rdparty/greensock/src/esm/TEMPLATE_Plugin.js b/lib/3rdparty/greensock/src/esm/TEMPLATE_Plugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/TEMPLATE_Plugin.js rename to lib/3rdparty/greensock/src/esm/TEMPLATE_Plugin.js diff --git a/3rdparty/greensock/src/esm/TextPlugin.js b/lib/3rdparty/greensock/src/esm/TextPlugin.js similarity index 100% rename from 3rdparty/greensock/src/esm/TextPlugin.js rename to lib/3rdparty/greensock/src/esm/TextPlugin.js diff --git a/3rdparty/greensock/src/esm/TimelineLite.js b/lib/3rdparty/greensock/src/esm/TimelineLite.js similarity index 100% rename from 3rdparty/greensock/src/esm/TimelineLite.js rename to lib/3rdparty/greensock/src/esm/TimelineLite.js diff --git a/3rdparty/greensock/src/esm/TimelineMax.js b/lib/3rdparty/greensock/src/esm/TimelineMax.js similarity index 100% rename from 3rdparty/greensock/src/esm/TimelineMax.js rename to lib/3rdparty/greensock/src/esm/TimelineMax.js diff --git a/3rdparty/greensock/src/esm/TweenLite.js b/lib/3rdparty/greensock/src/esm/TweenLite.js similarity index 100% rename from 3rdparty/greensock/src/esm/TweenLite.js rename to lib/3rdparty/greensock/src/esm/TweenLite.js diff --git a/3rdparty/greensock/src/esm/TweenMax.js b/lib/3rdparty/greensock/src/esm/TweenMax.js similarity index 100% rename from 3rdparty/greensock/src/esm/TweenMax.js rename to lib/3rdparty/greensock/src/esm/TweenMax.js diff --git a/3rdparty/greensock/src/esm/TweenMaxBase.js b/lib/3rdparty/greensock/src/esm/TweenMaxBase.js similarity index 100% rename from 3rdparty/greensock/src/esm/TweenMaxBase.js rename to lib/3rdparty/greensock/src/esm/TweenMaxBase.js diff --git a/3rdparty/greensock/src/esm/all.js b/lib/3rdparty/greensock/src/esm/all.js similarity index 100% rename from 3rdparty/greensock/src/esm/all.js rename to lib/3rdparty/greensock/src/esm/all.js diff --git a/3rdparty/greensock/src/esm/index.js b/lib/3rdparty/greensock/src/esm/index.js similarity index 100% rename from 3rdparty/greensock/src/esm/index.js rename to lib/3rdparty/greensock/src/esm/index.js diff --git a/3rdparty/greensock/src/esm/package.json b/lib/3rdparty/greensock/src/esm/package.json similarity index 100% rename from 3rdparty/greensock/src/esm/package.json rename to lib/3rdparty/greensock/src/esm/package.json diff --git a/3rdparty/greensock/src/minified/TimelineLite.min.js b/lib/3rdparty/greensock/src/minified/TimelineLite.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/TimelineLite.min.js rename to lib/3rdparty/greensock/src/minified/TimelineLite.min.js diff --git a/3rdparty/greensock/src/minified/TimelineMax.min.js b/lib/3rdparty/greensock/src/minified/TimelineMax.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/TimelineMax.min.js rename to lib/3rdparty/greensock/src/minified/TimelineMax.min.js diff --git a/3rdparty/greensock/src/minified/TweenLite.min.js b/lib/3rdparty/greensock/src/minified/TweenLite.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/TweenLite.min.js rename to lib/3rdparty/greensock/src/minified/TweenLite.min.js diff --git a/3rdparty/greensock/src/minified/TweenMax.min.js b/lib/3rdparty/greensock/src/minified/TweenMax.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/TweenMax.min.js rename to lib/3rdparty/greensock/src/minified/TweenMax.min.js diff --git a/3rdparty/greensock/src/minified/easing/CustomBounce.min.js b/lib/3rdparty/greensock/src/minified/easing/CustomBounce.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/easing/CustomBounce.min.js rename to lib/3rdparty/greensock/src/minified/easing/CustomBounce.min.js diff --git a/3rdparty/greensock/src/minified/easing/CustomEase.min.js b/lib/3rdparty/greensock/src/minified/easing/CustomEase.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/easing/CustomEase.min.js rename to lib/3rdparty/greensock/src/minified/easing/CustomEase.min.js diff --git a/3rdparty/greensock/src/minified/easing/CustomWiggle.min.js b/lib/3rdparty/greensock/src/minified/easing/CustomWiggle.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/easing/CustomWiggle.min.js rename to lib/3rdparty/greensock/src/minified/easing/CustomWiggle.min.js diff --git a/3rdparty/greensock/src/minified/easing/EasePack.min.js b/lib/3rdparty/greensock/src/minified/easing/EasePack.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/easing/EasePack.min.js rename to lib/3rdparty/greensock/src/minified/easing/EasePack.min.js diff --git a/3rdparty/greensock/src/minified/jquery.gsap.min.js b/lib/3rdparty/greensock/src/minified/jquery.gsap.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/jquery.gsap.min.js rename to lib/3rdparty/greensock/src/minified/jquery.gsap.min.js diff --git a/3rdparty/greensock/src/minified/plugins/AttrPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/AttrPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/AttrPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/AttrPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/BezierPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/BezierPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/BezierPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/BezierPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/CSSPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/CSSPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/CSSPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/CSSPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/CSSRulePlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/CSSRulePlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/CSSRulePlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/CSSRulePlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/ColorPropsPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/ColorPropsPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/ColorPropsPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/ColorPropsPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/DirectionalRotationPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/DirectionalRotationPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/DirectionalRotationPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/DirectionalRotationPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/DrawSVGPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/DrawSVGPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/DrawSVGPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/DrawSVGPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/EaselPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/EaselPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/EaselPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/EaselPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/EndArrayPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/EndArrayPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/EndArrayPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/EndArrayPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/ModifiersPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/ModifiersPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/ModifiersPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/ModifiersPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/MorphSVGPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/MorphSVGPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/MorphSVGPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/MorphSVGPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/Physics2DPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/Physics2DPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/Physics2DPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/Physics2DPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/PhysicsPropsPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/PhysicsPropsPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/PhysicsPropsPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/PhysicsPropsPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/PixiPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/PixiPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/PixiPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/PixiPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/RaphaelPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/RaphaelPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/RaphaelPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/RaphaelPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/RoundPropsPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/RoundPropsPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/RoundPropsPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/RoundPropsPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/ScrambleTextPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/ScrambleTextPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/ScrambleTextPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/ScrambleTextPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/ScrollToPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/ScrollToPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/ScrollToPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/ScrollToPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/TextPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/TextPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/TextPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/TextPlugin.min.js diff --git a/3rdparty/greensock/src/minified/plugins/ThrowPropsPlugin.min.js b/lib/3rdparty/greensock/src/minified/plugins/ThrowPropsPlugin.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/plugins/ThrowPropsPlugin.min.js rename to lib/3rdparty/greensock/src/minified/plugins/ThrowPropsPlugin.min.js diff --git a/3rdparty/greensock/src/minified/utils/Draggable.min.js b/lib/3rdparty/greensock/src/minified/utils/Draggable.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/utils/Draggable.min.js rename to lib/3rdparty/greensock/src/minified/utils/Draggable.min.js diff --git a/3rdparty/greensock/src/minified/utils/GSDevTools.min.js b/lib/3rdparty/greensock/src/minified/utils/GSDevTools.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/utils/GSDevTools.min.js rename to lib/3rdparty/greensock/src/minified/utils/GSDevTools.min.js diff --git a/3rdparty/greensock/src/minified/utils/SplitText.min.js b/lib/3rdparty/greensock/src/minified/utils/SplitText.min.js similarity index 100% rename from 3rdparty/greensock/src/minified/utils/SplitText.min.js rename to lib/3rdparty/greensock/src/minified/utils/SplitText.min.js diff --git a/3rdparty/greensock/src/uncompressed/TimelineLite.js b/lib/3rdparty/greensock/src/uncompressed/TimelineLite.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/TimelineLite.js rename to lib/3rdparty/greensock/src/uncompressed/TimelineLite.js diff --git a/3rdparty/greensock/src/uncompressed/TimelineMax.js b/lib/3rdparty/greensock/src/uncompressed/TimelineMax.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/TimelineMax.js rename to lib/3rdparty/greensock/src/uncompressed/TimelineMax.js diff --git a/3rdparty/greensock/src/uncompressed/TweenLite.js b/lib/3rdparty/greensock/src/uncompressed/TweenLite.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/TweenLite.js rename to lib/3rdparty/greensock/src/uncompressed/TweenLite.js diff --git a/3rdparty/greensock/src/uncompressed/TweenMax.js b/lib/3rdparty/greensock/src/uncompressed/TweenMax.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/TweenMax.js rename to lib/3rdparty/greensock/src/uncompressed/TweenMax.js diff --git a/3rdparty/greensock/src/uncompressed/easing/CustomBounce.js b/lib/3rdparty/greensock/src/uncompressed/easing/CustomBounce.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/easing/CustomBounce.js rename to lib/3rdparty/greensock/src/uncompressed/easing/CustomBounce.js diff --git a/3rdparty/greensock/src/uncompressed/easing/CustomEase.js b/lib/3rdparty/greensock/src/uncompressed/easing/CustomEase.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/easing/CustomEase.js rename to lib/3rdparty/greensock/src/uncompressed/easing/CustomEase.js diff --git a/3rdparty/greensock/src/uncompressed/easing/CustomWiggle.js b/lib/3rdparty/greensock/src/uncompressed/easing/CustomWiggle.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/easing/CustomWiggle.js rename to lib/3rdparty/greensock/src/uncompressed/easing/CustomWiggle.js diff --git a/3rdparty/greensock/src/uncompressed/easing/EasePack.js b/lib/3rdparty/greensock/src/uncompressed/easing/EasePack.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/easing/EasePack.js rename to lib/3rdparty/greensock/src/uncompressed/easing/EasePack.js diff --git a/3rdparty/greensock/src/uncompressed/jquery.gsap.js b/lib/3rdparty/greensock/src/uncompressed/jquery.gsap.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/jquery.gsap.js rename to lib/3rdparty/greensock/src/uncompressed/jquery.gsap.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/AttrPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/AttrPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/AttrPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/AttrPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/BezierPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/BezierPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/BezierPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/BezierPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/CSSPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/CSSPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/CSSPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/CSSPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/CSSRulePlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/CSSRulePlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/CSSRulePlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/CSSRulePlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/ColorPropsPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/ColorPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/ColorPropsPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/ColorPropsPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/DirectionalRotationPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/DirectionalRotationPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/DirectionalRotationPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/DirectionalRotationPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/DrawSVGPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/DrawSVGPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/DrawSVGPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/DrawSVGPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/EaselPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/EaselPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/EaselPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/EaselPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/EndArrayPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/EndArrayPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/EndArrayPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/EndArrayPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/ModifiersPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/ModifiersPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/ModifiersPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/ModifiersPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/MorphSVGPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/MorphSVGPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/MorphSVGPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/MorphSVGPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/Physics2DPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/Physics2DPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/Physics2DPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/Physics2DPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/PhysicsPropsPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/PhysicsPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/PhysicsPropsPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/PhysicsPropsPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/PixiPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/PixiPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/PixiPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/PixiPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/RaphaelPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/RaphaelPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/RaphaelPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/RaphaelPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/RoundPropsPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/RoundPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/RoundPropsPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/RoundPropsPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/ScrambleTextPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/ScrambleTextPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/ScrambleTextPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/ScrambleTextPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/ScrollToPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/ScrollToPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/ScrollToPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/ScrollToPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/TEMPLATE_Plugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/TEMPLATE_Plugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/TEMPLATE_Plugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/TEMPLATE_Plugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/TextPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/TextPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/TextPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/TextPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/plugins/ThrowPropsPlugin.js b/lib/3rdparty/greensock/src/uncompressed/plugins/ThrowPropsPlugin.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/plugins/ThrowPropsPlugin.js rename to lib/3rdparty/greensock/src/uncompressed/plugins/ThrowPropsPlugin.js diff --git a/3rdparty/greensock/src/uncompressed/utils/Draggable.js b/lib/3rdparty/greensock/src/uncompressed/utils/Draggable.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/utils/Draggable.js rename to lib/3rdparty/greensock/src/uncompressed/utils/Draggable.js diff --git a/3rdparty/greensock/src/uncompressed/utils/GSDevTools.js b/lib/3rdparty/greensock/src/uncompressed/utils/GSDevTools.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/utils/GSDevTools.js rename to lib/3rdparty/greensock/src/uncompressed/utils/GSDevTools.js diff --git a/3rdparty/greensock/src/uncompressed/utils/SplitText.js b/lib/3rdparty/greensock/src/uncompressed/utils/SplitText.js similarity index 100% rename from 3rdparty/greensock/src/uncompressed/utils/SplitText.js rename to lib/3rdparty/greensock/src/uncompressed/utils/SplitText.js diff --git a/3rdparty/hammer.js b/lib/3rdparty/hammer.js similarity index 100% rename from 3rdparty/hammer.js rename to lib/3rdparty/hammer.js diff --git a/3rdparty/hammer.min.js b/lib/3rdparty/hammer.min.js similarity index 100% rename from 3rdparty/hammer.min.js rename to lib/3rdparty/hammer.min.js diff --git a/3rdparty/hammer.min.js.map b/lib/3rdparty/hammer.min.js.map similarity index 100% rename from 3rdparty/hammer.min.js.map rename to lib/3rdparty/hammer.min.js.map diff --git a/3rdparty/hammer.propagating.js b/lib/3rdparty/hammer.propagating.js similarity index 100% rename from 3rdparty/hammer.propagating.js rename to lib/3rdparty/hammer.propagating.js diff --git a/3rdparty/highlight/CHANGES.md b/lib/3rdparty/highlight/CHANGES.md similarity index 100% rename from 3rdparty/highlight/CHANGES.md rename to lib/3rdparty/highlight/CHANGES.md diff --git a/3rdparty/highlight/LICENSE b/lib/3rdparty/highlight/LICENSE similarity index 100% rename from 3rdparty/highlight/LICENSE rename to lib/3rdparty/highlight/LICENSE diff --git a/3rdparty/highlight/README.md b/lib/3rdparty/highlight/README.md similarity index 100% rename from 3rdparty/highlight/README.md rename to lib/3rdparty/highlight/README.md diff --git a/3rdparty/highlight/README.ru.md b/lib/3rdparty/highlight/README.ru.md similarity index 100% rename from 3rdparty/highlight/README.ru.md rename to lib/3rdparty/highlight/README.ru.md diff --git a/3rdparty/highlight/highlight.pack.js b/lib/3rdparty/highlight/highlight.pack.js similarity index 100% rename from 3rdparty/highlight/highlight.pack.js rename to lib/3rdparty/highlight/highlight.pack.js diff --git a/3rdparty/highlight/styles/agate.css b/lib/3rdparty/highlight/styles/agate.css similarity index 100% rename from 3rdparty/highlight/styles/agate.css rename to lib/3rdparty/highlight/styles/agate.css diff --git a/3rdparty/highlight/styles/androidstudio.css b/lib/3rdparty/highlight/styles/androidstudio.css similarity index 100% rename from 3rdparty/highlight/styles/androidstudio.css rename to lib/3rdparty/highlight/styles/androidstudio.css diff --git a/3rdparty/highlight/styles/arduino-light.css b/lib/3rdparty/highlight/styles/arduino-light.css similarity index 100% rename from 3rdparty/highlight/styles/arduino-light.css rename to lib/3rdparty/highlight/styles/arduino-light.css diff --git a/3rdparty/highlight/styles/arta.css b/lib/3rdparty/highlight/styles/arta.css similarity index 100% rename from 3rdparty/highlight/styles/arta.css rename to lib/3rdparty/highlight/styles/arta.css diff --git a/3rdparty/highlight/styles/ascetic.css b/lib/3rdparty/highlight/styles/ascetic.css similarity index 100% rename from 3rdparty/highlight/styles/ascetic.css rename to lib/3rdparty/highlight/styles/ascetic.css diff --git a/3rdparty/highlight/styles/atelier-cave-dark.css b/lib/3rdparty/highlight/styles/atelier-cave-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-cave-dark.css rename to lib/3rdparty/highlight/styles/atelier-cave-dark.css diff --git a/3rdparty/highlight/styles/atelier-cave-light.css b/lib/3rdparty/highlight/styles/atelier-cave-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-cave-light.css rename to lib/3rdparty/highlight/styles/atelier-cave-light.css diff --git a/3rdparty/highlight/styles/atelier-dune-dark.css b/lib/3rdparty/highlight/styles/atelier-dune-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-dune-dark.css rename to lib/3rdparty/highlight/styles/atelier-dune-dark.css diff --git a/3rdparty/highlight/styles/atelier-dune-light.css b/lib/3rdparty/highlight/styles/atelier-dune-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-dune-light.css rename to lib/3rdparty/highlight/styles/atelier-dune-light.css diff --git a/3rdparty/highlight/styles/atelier-estuary-dark.css b/lib/3rdparty/highlight/styles/atelier-estuary-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-estuary-dark.css rename to lib/3rdparty/highlight/styles/atelier-estuary-dark.css diff --git a/3rdparty/highlight/styles/atelier-estuary-light.css b/lib/3rdparty/highlight/styles/atelier-estuary-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-estuary-light.css rename to lib/3rdparty/highlight/styles/atelier-estuary-light.css diff --git a/3rdparty/highlight/styles/atelier-forest-dark.css b/lib/3rdparty/highlight/styles/atelier-forest-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-forest-dark.css rename to lib/3rdparty/highlight/styles/atelier-forest-dark.css diff --git a/3rdparty/highlight/styles/atelier-forest-light.css b/lib/3rdparty/highlight/styles/atelier-forest-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-forest-light.css rename to lib/3rdparty/highlight/styles/atelier-forest-light.css diff --git a/3rdparty/highlight/styles/atelier-heath-dark.css b/lib/3rdparty/highlight/styles/atelier-heath-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-heath-dark.css rename to lib/3rdparty/highlight/styles/atelier-heath-dark.css diff --git a/3rdparty/highlight/styles/atelier-heath-light.css b/lib/3rdparty/highlight/styles/atelier-heath-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-heath-light.css rename to lib/3rdparty/highlight/styles/atelier-heath-light.css diff --git a/3rdparty/highlight/styles/atelier-lakeside-dark.css b/lib/3rdparty/highlight/styles/atelier-lakeside-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-lakeside-dark.css rename to lib/3rdparty/highlight/styles/atelier-lakeside-dark.css diff --git a/3rdparty/highlight/styles/atelier-lakeside-light.css b/lib/3rdparty/highlight/styles/atelier-lakeside-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-lakeside-light.css rename to lib/3rdparty/highlight/styles/atelier-lakeside-light.css diff --git a/3rdparty/highlight/styles/atelier-plateau-dark.css b/lib/3rdparty/highlight/styles/atelier-plateau-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-plateau-dark.css rename to lib/3rdparty/highlight/styles/atelier-plateau-dark.css diff --git a/3rdparty/highlight/styles/atelier-plateau-light.css b/lib/3rdparty/highlight/styles/atelier-plateau-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-plateau-light.css rename to lib/3rdparty/highlight/styles/atelier-plateau-light.css diff --git a/3rdparty/highlight/styles/atelier-savanna-dark.css b/lib/3rdparty/highlight/styles/atelier-savanna-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-savanna-dark.css rename to lib/3rdparty/highlight/styles/atelier-savanna-dark.css diff --git a/3rdparty/highlight/styles/atelier-savanna-light.css b/lib/3rdparty/highlight/styles/atelier-savanna-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-savanna-light.css rename to lib/3rdparty/highlight/styles/atelier-savanna-light.css diff --git a/3rdparty/highlight/styles/atelier-seaside-dark.css b/lib/3rdparty/highlight/styles/atelier-seaside-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-seaside-dark.css rename to lib/3rdparty/highlight/styles/atelier-seaside-dark.css diff --git a/3rdparty/highlight/styles/atelier-seaside-light.css b/lib/3rdparty/highlight/styles/atelier-seaside-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-seaside-light.css rename to lib/3rdparty/highlight/styles/atelier-seaside-light.css diff --git a/3rdparty/highlight/styles/atelier-sulphurpool-dark.css b/lib/3rdparty/highlight/styles/atelier-sulphurpool-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-sulphurpool-dark.css rename to lib/3rdparty/highlight/styles/atelier-sulphurpool-dark.css diff --git a/3rdparty/highlight/styles/atelier-sulphurpool-light.css b/lib/3rdparty/highlight/styles/atelier-sulphurpool-light.css similarity index 100% rename from 3rdparty/highlight/styles/atelier-sulphurpool-light.css rename to lib/3rdparty/highlight/styles/atelier-sulphurpool-light.css diff --git a/3rdparty/highlight/styles/atom-one-dark.css b/lib/3rdparty/highlight/styles/atom-one-dark.css similarity index 100% rename from 3rdparty/highlight/styles/atom-one-dark.css rename to lib/3rdparty/highlight/styles/atom-one-dark.css diff --git a/3rdparty/highlight/styles/atom-one-light.css b/lib/3rdparty/highlight/styles/atom-one-light.css similarity index 100% rename from 3rdparty/highlight/styles/atom-one-light.css rename to lib/3rdparty/highlight/styles/atom-one-light.css diff --git a/3rdparty/highlight/styles/brown-paper.css b/lib/3rdparty/highlight/styles/brown-paper.css similarity index 100% rename from 3rdparty/highlight/styles/brown-paper.css rename to lib/3rdparty/highlight/styles/brown-paper.css diff --git a/3rdparty/highlight/styles/brown-papersq.png b/lib/3rdparty/highlight/styles/brown-papersq.png similarity index 100% rename from 3rdparty/highlight/styles/brown-papersq.png rename to lib/3rdparty/highlight/styles/brown-papersq.png diff --git a/3rdparty/highlight/styles/codepen-embed.css b/lib/3rdparty/highlight/styles/codepen-embed.css similarity index 100% rename from 3rdparty/highlight/styles/codepen-embed.css rename to lib/3rdparty/highlight/styles/codepen-embed.css diff --git a/3rdparty/highlight/styles/color-brewer.css b/lib/3rdparty/highlight/styles/color-brewer.css similarity index 100% rename from 3rdparty/highlight/styles/color-brewer.css rename to lib/3rdparty/highlight/styles/color-brewer.css diff --git a/3rdparty/highlight/styles/darcula.css b/lib/3rdparty/highlight/styles/darcula.css similarity index 100% rename from 3rdparty/highlight/styles/darcula.css rename to lib/3rdparty/highlight/styles/darcula.css diff --git a/3rdparty/highlight/styles/dark.css b/lib/3rdparty/highlight/styles/dark.css similarity index 100% rename from 3rdparty/highlight/styles/dark.css rename to lib/3rdparty/highlight/styles/dark.css diff --git a/3rdparty/highlight/styles/darkula.css b/lib/3rdparty/highlight/styles/darkula.css similarity index 100% rename from 3rdparty/highlight/styles/darkula.css rename to lib/3rdparty/highlight/styles/darkula.css diff --git a/3rdparty/highlight/styles/default.css b/lib/3rdparty/highlight/styles/default.css similarity index 100% rename from 3rdparty/highlight/styles/default.css rename to lib/3rdparty/highlight/styles/default.css diff --git a/3rdparty/highlight/styles/docco.css b/lib/3rdparty/highlight/styles/docco.css similarity index 100% rename from 3rdparty/highlight/styles/docco.css rename to lib/3rdparty/highlight/styles/docco.css diff --git a/3rdparty/highlight/styles/dracula.css b/lib/3rdparty/highlight/styles/dracula.css similarity index 100% rename from 3rdparty/highlight/styles/dracula.css rename to lib/3rdparty/highlight/styles/dracula.css diff --git a/3rdparty/highlight/styles/far.css b/lib/3rdparty/highlight/styles/far.css similarity index 100% rename from 3rdparty/highlight/styles/far.css rename to lib/3rdparty/highlight/styles/far.css diff --git a/3rdparty/highlight/styles/foundation.css b/lib/3rdparty/highlight/styles/foundation.css similarity index 100% rename from 3rdparty/highlight/styles/foundation.css rename to lib/3rdparty/highlight/styles/foundation.css diff --git a/3rdparty/highlight/styles/github-gist.css b/lib/3rdparty/highlight/styles/github-gist.css similarity index 100% rename from 3rdparty/highlight/styles/github-gist.css rename to lib/3rdparty/highlight/styles/github-gist.css diff --git a/3rdparty/highlight/styles/github.css b/lib/3rdparty/highlight/styles/github.css similarity index 100% rename from 3rdparty/highlight/styles/github.css rename to lib/3rdparty/highlight/styles/github.css diff --git a/3rdparty/highlight/styles/googlecode.css b/lib/3rdparty/highlight/styles/googlecode.css similarity index 100% rename from 3rdparty/highlight/styles/googlecode.css rename to lib/3rdparty/highlight/styles/googlecode.css diff --git a/3rdparty/highlight/styles/grayscale.css b/lib/3rdparty/highlight/styles/grayscale.css similarity index 100% rename from 3rdparty/highlight/styles/grayscale.css rename to lib/3rdparty/highlight/styles/grayscale.css diff --git a/3rdparty/highlight/styles/gruvbox-dark.css b/lib/3rdparty/highlight/styles/gruvbox-dark.css similarity index 100% rename from 3rdparty/highlight/styles/gruvbox-dark.css rename to lib/3rdparty/highlight/styles/gruvbox-dark.css diff --git a/3rdparty/highlight/styles/gruvbox-light.css b/lib/3rdparty/highlight/styles/gruvbox-light.css similarity index 100% rename from 3rdparty/highlight/styles/gruvbox-light.css rename to lib/3rdparty/highlight/styles/gruvbox-light.css diff --git a/3rdparty/highlight/styles/hopscotch.css b/lib/3rdparty/highlight/styles/hopscotch.css similarity index 100% rename from 3rdparty/highlight/styles/hopscotch.css rename to lib/3rdparty/highlight/styles/hopscotch.css diff --git a/3rdparty/highlight/styles/hybrid.css b/lib/3rdparty/highlight/styles/hybrid.css similarity index 100% rename from 3rdparty/highlight/styles/hybrid.css rename to lib/3rdparty/highlight/styles/hybrid.css diff --git a/3rdparty/highlight/styles/idea.css b/lib/3rdparty/highlight/styles/idea.css similarity index 100% rename from 3rdparty/highlight/styles/idea.css rename to lib/3rdparty/highlight/styles/idea.css diff --git a/3rdparty/highlight/styles/ir-black.css b/lib/3rdparty/highlight/styles/ir-black.css similarity index 100% rename from 3rdparty/highlight/styles/ir-black.css rename to lib/3rdparty/highlight/styles/ir-black.css diff --git a/3rdparty/highlight/styles/kimbie.dark.css b/lib/3rdparty/highlight/styles/kimbie.dark.css similarity index 100% rename from 3rdparty/highlight/styles/kimbie.dark.css rename to lib/3rdparty/highlight/styles/kimbie.dark.css diff --git a/3rdparty/highlight/styles/kimbie.light.css b/lib/3rdparty/highlight/styles/kimbie.light.css similarity index 100% rename from 3rdparty/highlight/styles/kimbie.light.css rename to lib/3rdparty/highlight/styles/kimbie.light.css diff --git a/3rdparty/highlight/styles/magula.css b/lib/3rdparty/highlight/styles/magula.css similarity index 100% rename from 3rdparty/highlight/styles/magula.css rename to lib/3rdparty/highlight/styles/magula.css diff --git a/3rdparty/highlight/styles/mono-blue.css b/lib/3rdparty/highlight/styles/mono-blue.css similarity index 100% rename from 3rdparty/highlight/styles/mono-blue.css rename to lib/3rdparty/highlight/styles/mono-blue.css diff --git a/3rdparty/highlight/styles/monokai-sublime.css b/lib/3rdparty/highlight/styles/monokai-sublime.css similarity index 100% rename from 3rdparty/highlight/styles/monokai-sublime.css rename to lib/3rdparty/highlight/styles/monokai-sublime.css diff --git a/3rdparty/highlight/styles/monokai.css b/lib/3rdparty/highlight/styles/monokai.css similarity index 100% rename from 3rdparty/highlight/styles/monokai.css rename to lib/3rdparty/highlight/styles/monokai.css diff --git a/3rdparty/highlight/styles/obsidian.css b/lib/3rdparty/highlight/styles/obsidian.css similarity index 100% rename from 3rdparty/highlight/styles/obsidian.css rename to lib/3rdparty/highlight/styles/obsidian.css diff --git a/3rdparty/highlight/styles/ocean.css b/lib/3rdparty/highlight/styles/ocean.css similarity index 100% rename from 3rdparty/highlight/styles/ocean.css rename to lib/3rdparty/highlight/styles/ocean.css diff --git a/3rdparty/highlight/styles/paraiso-dark.css b/lib/3rdparty/highlight/styles/paraiso-dark.css similarity index 100% rename from 3rdparty/highlight/styles/paraiso-dark.css rename to lib/3rdparty/highlight/styles/paraiso-dark.css diff --git a/3rdparty/highlight/styles/paraiso-light.css b/lib/3rdparty/highlight/styles/paraiso-light.css similarity index 100% rename from 3rdparty/highlight/styles/paraiso-light.css rename to lib/3rdparty/highlight/styles/paraiso-light.css diff --git a/3rdparty/highlight/styles/pojoaque.css b/lib/3rdparty/highlight/styles/pojoaque.css similarity index 100% rename from 3rdparty/highlight/styles/pojoaque.css rename to lib/3rdparty/highlight/styles/pojoaque.css diff --git a/3rdparty/highlight/styles/pojoaque.jpg b/lib/3rdparty/highlight/styles/pojoaque.jpg similarity index 100% rename from 3rdparty/highlight/styles/pojoaque.jpg rename to lib/3rdparty/highlight/styles/pojoaque.jpg diff --git a/3rdparty/highlight/styles/purebasic.css b/lib/3rdparty/highlight/styles/purebasic.css similarity index 100% rename from 3rdparty/highlight/styles/purebasic.css rename to lib/3rdparty/highlight/styles/purebasic.css diff --git a/3rdparty/highlight/styles/qtcreator_dark.css b/lib/3rdparty/highlight/styles/qtcreator_dark.css similarity index 100% rename from 3rdparty/highlight/styles/qtcreator_dark.css rename to lib/3rdparty/highlight/styles/qtcreator_dark.css diff --git a/3rdparty/highlight/styles/qtcreator_light.css b/lib/3rdparty/highlight/styles/qtcreator_light.css similarity index 100% rename from 3rdparty/highlight/styles/qtcreator_light.css rename to lib/3rdparty/highlight/styles/qtcreator_light.css diff --git a/3rdparty/highlight/styles/railscasts.css b/lib/3rdparty/highlight/styles/railscasts.css similarity index 100% rename from 3rdparty/highlight/styles/railscasts.css rename to lib/3rdparty/highlight/styles/railscasts.css diff --git a/3rdparty/highlight/styles/rainbow.css b/lib/3rdparty/highlight/styles/rainbow.css similarity index 100% rename from 3rdparty/highlight/styles/rainbow.css rename to lib/3rdparty/highlight/styles/rainbow.css diff --git a/3rdparty/highlight/styles/school-book.css b/lib/3rdparty/highlight/styles/school-book.css similarity index 100% rename from 3rdparty/highlight/styles/school-book.css rename to lib/3rdparty/highlight/styles/school-book.css diff --git a/3rdparty/highlight/styles/school-book.png b/lib/3rdparty/highlight/styles/school-book.png similarity index 100% rename from 3rdparty/highlight/styles/school-book.png rename to lib/3rdparty/highlight/styles/school-book.png diff --git a/3rdparty/highlight/styles/solarized-dark.css b/lib/3rdparty/highlight/styles/solarized-dark.css similarity index 100% rename from 3rdparty/highlight/styles/solarized-dark.css rename to lib/3rdparty/highlight/styles/solarized-dark.css diff --git a/3rdparty/highlight/styles/solarized-light.css b/lib/3rdparty/highlight/styles/solarized-light.css similarity index 100% rename from 3rdparty/highlight/styles/solarized-light.css rename to lib/3rdparty/highlight/styles/solarized-light.css diff --git a/3rdparty/highlight/styles/sunburst.css b/lib/3rdparty/highlight/styles/sunburst.css similarity index 100% rename from 3rdparty/highlight/styles/sunburst.css rename to lib/3rdparty/highlight/styles/sunburst.css diff --git a/3rdparty/highlight/styles/tomorrow-night-blue.css b/lib/3rdparty/highlight/styles/tomorrow-night-blue.css similarity index 100% rename from 3rdparty/highlight/styles/tomorrow-night-blue.css rename to lib/3rdparty/highlight/styles/tomorrow-night-blue.css diff --git a/3rdparty/highlight/styles/tomorrow-night-bright.css b/lib/3rdparty/highlight/styles/tomorrow-night-bright.css similarity index 100% rename from 3rdparty/highlight/styles/tomorrow-night-bright.css rename to lib/3rdparty/highlight/styles/tomorrow-night-bright.css diff --git a/3rdparty/highlight/styles/tomorrow-night-eighties.css b/lib/3rdparty/highlight/styles/tomorrow-night-eighties.css similarity index 100% rename from 3rdparty/highlight/styles/tomorrow-night-eighties.css rename to lib/3rdparty/highlight/styles/tomorrow-night-eighties.css diff --git a/3rdparty/highlight/styles/tomorrow-night.css b/lib/3rdparty/highlight/styles/tomorrow-night.css similarity index 100% rename from 3rdparty/highlight/styles/tomorrow-night.css rename to lib/3rdparty/highlight/styles/tomorrow-night.css diff --git a/3rdparty/highlight/styles/tomorrow.css b/lib/3rdparty/highlight/styles/tomorrow.css similarity index 100% rename from 3rdparty/highlight/styles/tomorrow.css rename to lib/3rdparty/highlight/styles/tomorrow.css diff --git a/3rdparty/highlight/styles/vs.css b/lib/3rdparty/highlight/styles/vs.css similarity index 100% rename from 3rdparty/highlight/styles/vs.css rename to lib/3rdparty/highlight/styles/vs.css diff --git a/3rdparty/highlight/styles/xcode.css b/lib/3rdparty/highlight/styles/xcode.css similarity index 100% rename from 3rdparty/highlight/styles/xcode.css rename to lib/3rdparty/highlight/styles/xcode.css diff --git a/3rdparty/highlight/styles/xt256.css b/lib/3rdparty/highlight/styles/xt256.css similarity index 100% rename from 3rdparty/highlight/styles/xt256.css rename to lib/3rdparty/highlight/styles/xt256.css diff --git a/3rdparty/highlight/styles/zenburn.css b/lib/3rdparty/highlight/styles/zenburn.css similarity index 100% rename from 3rdparty/highlight/styles/zenburn.css rename to lib/3rdparty/highlight/styles/zenburn.css diff --git a/3rdparty/hyphenation/de.js b/lib/3rdparty/hyphenation/de.js similarity index 100% rename from 3rdparty/hyphenation/de.js rename to lib/3rdparty/hyphenation/de.js diff --git a/3rdparty/hyphenation/en-gb.js b/lib/3rdparty/hyphenation/en-gb.js similarity index 100% rename from 3rdparty/hyphenation/en-gb.js rename to lib/3rdparty/hyphenation/en-gb.js diff --git a/3rdparty/hyphenation/en-us.js b/lib/3rdparty/hyphenation/en-us.js similarity index 100% rename from 3rdparty/hyphenation/en-us.js rename to lib/3rdparty/hyphenation/en-us.js diff --git a/3rdparty/hyphenation/fr.js b/lib/3rdparty/hyphenation/fr.js similarity index 100% rename from 3rdparty/hyphenation/fr.js rename to lib/3rdparty/hyphenation/fr.js diff --git a/3rdparty/inobounce.js b/lib/3rdparty/inobounce.js similarity index 100% rename from 3rdparty/inobounce.js rename to lib/3rdparty/inobounce.js diff --git a/3rdparty/inobounce.min.js b/lib/3rdparty/inobounce.min.js similarity index 100% rename from 3rdparty/inobounce.min.js rename to lib/3rdparty/inobounce.min.js diff --git a/3rdparty/inobounce/inobounce.js b/lib/3rdparty/inobounce/inobounce.js similarity index 100% rename from 3rdparty/inobounce/inobounce.js rename to lib/3rdparty/inobounce/inobounce.js diff --git a/3rdparty/inobounce/inobounce.min.js b/lib/3rdparty/inobounce/inobounce.min.js similarity index 100% rename from 3rdparty/inobounce/inobounce.min.js rename to lib/3rdparty/inobounce/inobounce.min.js diff --git a/3rdparty/jquery.hypher.js b/lib/3rdparty/jquery.hypher.js similarity index 100% rename from 3rdparty/jquery.hypher.js rename to lib/3rdparty/jquery.hypher.js diff --git a/3rdparty/jquery.js b/lib/3rdparty/jquery.js similarity index 100% rename from 3rdparty/jquery.js rename to lib/3rdparty/jquery.js diff --git a/3rdparty/jquery.min.js b/lib/3rdparty/jquery.min.js similarity index 100% rename from 3rdparty/jquery.min.js rename to lib/3rdparty/jquery.min.js diff --git a/3rdparty/jquery.min.map b/lib/3rdparty/jquery.min.map similarity index 100% rename from 3rdparty/jquery.min.map rename to lib/3rdparty/jquery.min.map diff --git a/3rdparty/optimal-select.js b/lib/3rdparty/optimal-select.js similarity index 100% rename from 3rdparty/optimal-select.js rename to lib/3rdparty/optimal-select.js diff --git a/3rdparty/optimal-select.min.js b/lib/3rdparty/optimal-select.min.js similarity index 100% rename from 3rdparty/optimal-select.min.js rename to lib/3rdparty/optimal-select.min.js diff --git a/3rdparty/optimal-select.min.js.map b/lib/3rdparty/optimal-select.min.js.map similarity index 100% rename from 3rdparty/optimal-select.min.js.map rename to lib/3rdparty/optimal-select.min.js.map diff --git a/3rdparty/pdf/LICENSE b/lib/3rdparty/pdf/LICENSE similarity index 100% rename from 3rdparty/pdf/LICENSE rename to lib/3rdparty/pdf/LICENSE diff --git a/3rdparty/pdf/pdf.js b/lib/3rdparty/pdf/pdf.js similarity index 100% rename from 3rdparty/pdf/pdf.js rename to lib/3rdparty/pdf/pdf.js diff --git a/3rdparty/pdf/pdf.js.map b/lib/3rdparty/pdf/pdf.js.map similarity index 100% rename from 3rdparty/pdf/pdf.js.map rename to lib/3rdparty/pdf/pdf.js.map diff --git a/3rdparty/pdf/pdf.worker.js b/lib/3rdparty/pdf/pdf.worker.js similarity index 100% rename from 3rdparty/pdf/pdf.worker.js rename to lib/3rdparty/pdf/pdf.worker.js diff --git a/3rdparty/pdf/pdf.worker.js.map b/lib/3rdparty/pdf/pdf.worker.js.map similarity index 100% rename from 3rdparty/pdf/pdf.worker.js.map rename to lib/3rdparty/pdf/pdf.worker.js.map diff --git a/3rdparty/pixi/lib/crn_decomp.js b/lib/3rdparty/pixi/lib/crn_decomp.js similarity index 100% rename from 3rdparty/pixi/lib/crn_decomp.js rename to lib/3rdparty/pixi/lib/crn_decomp.js diff --git a/3rdparty/pixi/pixi-compressed-textures.js b/lib/3rdparty/pixi/pixi-compressed-textures.js similarity index 100% rename from 3rdparty/pixi/pixi-compressed-textures.js rename to lib/3rdparty/pixi/pixi-compressed-textures.js diff --git a/3rdparty/pixi/pixi-compressed-textures.js.map b/lib/3rdparty/pixi/pixi-compressed-textures.js.map similarity index 100% rename from 3rdparty/pixi/pixi-compressed-textures.js.map rename to lib/3rdparty/pixi/pixi-compressed-textures.js.map diff --git a/3rdparty/pixi/pixi-compressed-textures.min.js b/lib/3rdparty/pixi/pixi-compressed-textures.min.js similarity index 100% rename from 3rdparty/pixi/pixi-compressed-textures.min.js rename to lib/3rdparty/pixi/pixi-compressed-textures.min.js diff --git a/3rdparty/pixi/pixi-compressed-textures.min.js.map b/lib/3rdparty/pixi/pixi-compressed-textures.min.js.map similarity index 100% rename from 3rdparty/pixi/pixi-compressed-textures.min.js.map rename to lib/3rdparty/pixi/pixi-compressed-textures.min.js.map diff --git a/3rdparty/pixi/pixi-filters.js b/lib/3rdparty/pixi/pixi-filters.js similarity index 100% rename from 3rdparty/pixi/pixi-filters.js rename to lib/3rdparty/pixi/pixi-filters.js diff --git a/3rdparty/pixi/pixi-filters.js.map b/lib/3rdparty/pixi/pixi-filters.js.map similarity index 100% rename from 3rdparty/pixi/pixi-filters.js.map rename to lib/3rdparty/pixi/pixi-filters.js.map diff --git a/3rdparty/pixi/pixi-layers.js b/lib/3rdparty/pixi/pixi-layers.js similarity index 100% rename from 3rdparty/pixi/pixi-layers.js rename to lib/3rdparty/pixi/pixi-layers.js diff --git a/3rdparty/pixi/pixi-layers.js.map b/lib/3rdparty/pixi/pixi-layers.js.map similarity index 100% rename from 3rdparty/pixi/pixi-layers.js.map rename to lib/3rdparty/pixi/pixi-layers.js.map diff --git a/3rdparty/pixi/pixi-particles.js b/lib/3rdparty/pixi/pixi-particles.js similarity index 100% rename from 3rdparty/pixi/pixi-particles.js rename to lib/3rdparty/pixi/pixi-particles.js diff --git a/3rdparty/pixi/pixi-particles.js.map b/lib/3rdparty/pixi/pixi-particles.js.map similarity index 100% rename from 3rdparty/pixi/pixi-particles.js.map rename to lib/3rdparty/pixi/pixi-particles.js.map diff --git a/3rdparty/pixi/pixi-particles.min.js b/lib/3rdparty/pixi/pixi-particles.min.js similarity index 100% rename from 3rdparty/pixi/pixi-particles.min.js rename to lib/3rdparty/pixi/pixi-particles.min.js diff --git a/3rdparty/pixi/pixi-particles.min.js.map b/lib/3rdparty/pixi/pixi-particles.min.js.map similarity index 100% rename from 3rdparty/pixi/pixi-particles.min.js.map rename to lib/3rdparty/pixi/pixi-particles.min.js.map diff --git a/3rdparty/pixi/pixi-projection.js b/lib/3rdparty/pixi/pixi-projection.js similarity index 100% rename from 3rdparty/pixi/pixi-projection.js rename to lib/3rdparty/pixi/pixi-projection.js diff --git a/3rdparty/pixi/pixi-projection.js.map b/lib/3rdparty/pixi/pixi-projection.js.map similarity index 100% rename from 3rdparty/pixi/pixi-projection.js.map rename to lib/3rdparty/pixi/pixi-projection.js.map diff --git a/3rdparty/pixi/pixi.js b/lib/3rdparty/pixi/pixi.js similarity index 100% rename from 3rdparty/pixi/pixi.js rename to lib/3rdparty/pixi/pixi.js diff --git a/3rdparty/pixi/pixi.js.map b/lib/3rdparty/pixi/pixi.js.map similarity index 100% rename from 3rdparty/pixi/pixi.js.map rename to lib/3rdparty/pixi/pixi.js.map diff --git a/3rdparty/pixi/pixi.min.js b/lib/3rdparty/pixi/pixi.min.js similarity index 100% rename from 3rdparty/pixi/pixi.min.js rename to lib/3rdparty/pixi/pixi.min.js diff --git a/3rdparty/pixi/pixi.min.js.map b/lib/3rdparty/pixi/pixi.min.js.map similarity index 100% rename from 3rdparty/pixi/pixi.min.js.map rename to lib/3rdparty/pixi/pixi.min.js.map diff --git a/3rdparty/polyfills/babel-polyfill.js b/lib/3rdparty/polyfills/babel-polyfill.js similarity index 100% rename from 3rdparty/polyfills/babel-polyfill.js rename to lib/3rdparty/polyfills/babel-polyfill.js diff --git a/3rdparty/polyfills/es-module-loader.js b/lib/3rdparty/polyfills/es-module-loader.js similarity index 100% rename from 3rdparty/polyfills/es-module-loader.js rename to lib/3rdparty/polyfills/es-module-loader.js diff --git a/3rdparty/preload.js b/lib/3rdparty/preload.js similarity index 100% rename from 3rdparty/preload.js rename to lib/3rdparty/preload.js diff --git a/3rdparty/socket.io.js b/lib/3rdparty/socket.io.js similarity index 100% rename from 3rdparty/socket.io.js rename to lib/3rdparty/socket.io.js diff --git a/3rdparty/socket.io.js.map b/lib/3rdparty/socket.io.js.map similarity index 100% rename from 3rdparty/socket.io.js.map rename to lib/3rdparty/socket.io.js.map diff --git a/3rdparty/systemjs/babel-helpers/asyncGenerator.js b/lib/3rdparty/systemjs/babel-helpers/asyncGenerator.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/asyncGenerator.js rename to lib/3rdparty/systemjs/babel-helpers/asyncGenerator.js diff --git a/3rdparty/systemjs/babel-helpers/asyncGeneratorDelegate.js b/lib/3rdparty/systemjs/babel-helpers/asyncGeneratorDelegate.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/asyncGeneratorDelegate.js rename to lib/3rdparty/systemjs/babel-helpers/asyncGeneratorDelegate.js diff --git a/3rdparty/systemjs/babel-helpers/asyncIterator.js b/lib/3rdparty/systemjs/babel-helpers/asyncIterator.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/asyncIterator.js rename to lib/3rdparty/systemjs/babel-helpers/asyncIterator.js diff --git a/3rdparty/systemjs/babel-helpers/asyncToGenerator.js b/lib/3rdparty/systemjs/babel-helpers/asyncToGenerator.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/asyncToGenerator.js rename to lib/3rdparty/systemjs/babel-helpers/asyncToGenerator.js diff --git a/3rdparty/systemjs/babel-helpers/classCallCheck.js b/lib/3rdparty/systemjs/babel-helpers/classCallCheck.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/classCallCheck.js rename to lib/3rdparty/systemjs/babel-helpers/classCallCheck.js diff --git a/3rdparty/systemjs/babel-helpers/createClass.js b/lib/3rdparty/systemjs/babel-helpers/createClass.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/createClass.js rename to lib/3rdparty/systemjs/babel-helpers/createClass.js diff --git a/3rdparty/systemjs/babel-helpers/defaults.js b/lib/3rdparty/systemjs/babel-helpers/defaults.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/defaults.js rename to lib/3rdparty/systemjs/babel-helpers/defaults.js diff --git a/3rdparty/systemjs/babel-helpers/defineEnumerableProperties.js b/lib/3rdparty/systemjs/babel-helpers/defineEnumerableProperties.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/defineEnumerableProperties.js rename to lib/3rdparty/systemjs/babel-helpers/defineEnumerableProperties.js diff --git a/3rdparty/systemjs/babel-helpers/defineProperty.js b/lib/3rdparty/systemjs/babel-helpers/defineProperty.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/defineProperty.js rename to lib/3rdparty/systemjs/babel-helpers/defineProperty.js diff --git a/3rdparty/systemjs/babel-helpers/extends.js b/lib/3rdparty/systemjs/babel-helpers/extends.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/extends.js rename to lib/3rdparty/systemjs/babel-helpers/extends.js diff --git a/3rdparty/systemjs/babel-helpers/get.js b/lib/3rdparty/systemjs/babel-helpers/get.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/get.js rename to lib/3rdparty/systemjs/babel-helpers/get.js diff --git a/3rdparty/systemjs/babel-helpers/inherits.js b/lib/3rdparty/systemjs/babel-helpers/inherits.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/inherits.js rename to lib/3rdparty/systemjs/babel-helpers/inherits.js diff --git a/3rdparty/systemjs/babel-helpers/instanceof.js b/lib/3rdparty/systemjs/babel-helpers/instanceof.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/instanceof.js rename to lib/3rdparty/systemjs/babel-helpers/instanceof.js diff --git a/3rdparty/systemjs/babel-helpers/interopRequireDefault.js b/lib/3rdparty/systemjs/babel-helpers/interopRequireDefault.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/interopRequireDefault.js rename to lib/3rdparty/systemjs/babel-helpers/interopRequireDefault.js diff --git a/3rdparty/systemjs/babel-helpers/interopRequireWildcard.js b/lib/3rdparty/systemjs/babel-helpers/interopRequireWildcard.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/interopRequireWildcard.js rename to lib/3rdparty/systemjs/babel-helpers/interopRequireWildcard.js diff --git a/3rdparty/systemjs/babel-helpers/jsx.js b/lib/3rdparty/systemjs/babel-helpers/jsx.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/jsx.js rename to lib/3rdparty/systemjs/babel-helpers/jsx.js diff --git a/3rdparty/systemjs/babel-helpers/newArrowCheck.js b/lib/3rdparty/systemjs/babel-helpers/newArrowCheck.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/newArrowCheck.js rename to lib/3rdparty/systemjs/babel-helpers/newArrowCheck.js diff --git a/3rdparty/systemjs/babel-helpers/objectDestructuringEmpty.js b/lib/3rdparty/systemjs/babel-helpers/objectDestructuringEmpty.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/objectDestructuringEmpty.js rename to lib/3rdparty/systemjs/babel-helpers/objectDestructuringEmpty.js diff --git a/3rdparty/systemjs/babel-helpers/objectWithoutProperties.js b/lib/3rdparty/systemjs/babel-helpers/objectWithoutProperties.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/objectWithoutProperties.js rename to lib/3rdparty/systemjs/babel-helpers/objectWithoutProperties.js diff --git a/3rdparty/systemjs/babel-helpers/possibleConstructorReturn.js b/lib/3rdparty/systemjs/babel-helpers/possibleConstructorReturn.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/possibleConstructorReturn.js rename to lib/3rdparty/systemjs/babel-helpers/possibleConstructorReturn.js diff --git a/3rdparty/systemjs/babel-helpers/selfGlobal.js b/lib/3rdparty/systemjs/babel-helpers/selfGlobal.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/selfGlobal.js rename to lib/3rdparty/systemjs/babel-helpers/selfGlobal.js diff --git a/3rdparty/systemjs/babel-helpers/set.js b/lib/3rdparty/systemjs/babel-helpers/set.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/set.js rename to lib/3rdparty/systemjs/babel-helpers/set.js diff --git a/3rdparty/systemjs/babel-helpers/slicedToArray.js b/lib/3rdparty/systemjs/babel-helpers/slicedToArray.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/slicedToArray.js rename to lib/3rdparty/systemjs/babel-helpers/slicedToArray.js diff --git a/3rdparty/systemjs/babel-helpers/slicedToArrayLoose.js b/lib/3rdparty/systemjs/babel-helpers/slicedToArrayLoose.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/slicedToArrayLoose.js rename to lib/3rdparty/systemjs/babel-helpers/slicedToArrayLoose.js diff --git a/3rdparty/systemjs/babel-helpers/taggedTemplateLiteral.js b/lib/3rdparty/systemjs/babel-helpers/taggedTemplateLiteral.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/taggedTemplateLiteral.js rename to lib/3rdparty/systemjs/babel-helpers/taggedTemplateLiteral.js diff --git a/3rdparty/systemjs/babel-helpers/taggedTemplateLiteralLoose.js b/lib/3rdparty/systemjs/babel-helpers/taggedTemplateLiteralLoose.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/taggedTemplateLiteralLoose.js rename to lib/3rdparty/systemjs/babel-helpers/taggedTemplateLiteralLoose.js diff --git a/3rdparty/systemjs/babel-helpers/temporalRef.js b/lib/3rdparty/systemjs/babel-helpers/temporalRef.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/temporalRef.js rename to lib/3rdparty/systemjs/babel-helpers/temporalRef.js diff --git a/3rdparty/systemjs/babel-helpers/temporalUndefined.js b/lib/3rdparty/systemjs/babel-helpers/temporalUndefined.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/temporalUndefined.js rename to lib/3rdparty/systemjs/babel-helpers/temporalUndefined.js diff --git a/3rdparty/systemjs/babel-helpers/toArray.js b/lib/3rdparty/systemjs/babel-helpers/toArray.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/toArray.js rename to lib/3rdparty/systemjs/babel-helpers/toArray.js diff --git a/3rdparty/systemjs/babel-helpers/toConsumableArray.js b/lib/3rdparty/systemjs/babel-helpers/toConsumableArray.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/toConsumableArray.js rename to lib/3rdparty/systemjs/babel-helpers/toConsumableArray.js diff --git a/3rdparty/systemjs/babel-helpers/typeof.js b/lib/3rdparty/systemjs/babel-helpers/typeof.js similarity index 100% rename from 3rdparty/systemjs/babel-helpers/typeof.js rename to lib/3rdparty/systemjs/babel-helpers/typeof.js diff --git a/3rdparty/systemjs/plugin-babel.js b/lib/3rdparty/systemjs/plugin-babel.js similarity index 100% rename from 3rdparty/systemjs/plugin-babel.js rename to lib/3rdparty/systemjs/plugin-babel.js diff --git a/3rdparty/systemjs/system-config-es2015.js b/lib/3rdparty/systemjs/system-config-es2015.js similarity index 100% rename from 3rdparty/systemjs/system-config-es2015.js rename to lib/3rdparty/systemjs/system-config-es2015.js diff --git a/3rdparty/systemjs/system-config.js b/lib/3rdparty/systemjs/system-config.js similarity index 100% rename from 3rdparty/systemjs/system-config.js rename to lib/3rdparty/systemjs/system-config.js diff --git a/3rdparty/systemjs/system.js b/lib/3rdparty/systemjs/system.js similarity index 100% rename from 3rdparty/systemjs/system.js rename to lib/3rdparty/systemjs/system.js diff --git a/3rdparty/systemjs/system.js.map b/lib/3rdparty/systemjs/system.js.map similarity index 100% rename from 3rdparty/systemjs/system.js.map rename to lib/3rdparty/systemjs/system.js.map diff --git a/3rdparty/systemjs/systemjs-babel-browser.js b/lib/3rdparty/systemjs/systemjs-babel-browser.js similarity index 100% rename from 3rdparty/systemjs/systemjs-babel-browser.js rename to lib/3rdparty/systemjs/systemjs-babel-browser.js diff --git a/3rdparty/topojson.min.js b/lib/3rdparty/topojson.min.js similarity index 100% rename from 3rdparty/topojson.min.js rename to lib/3rdparty/topojson.min.js diff --git a/app.html b/lib/app.html similarity index 100% rename from app.html rename to lib/app.html diff --git a/app.js b/lib/app.js similarity index 100% rename from app.js rename to lib/app.js diff --git a/bootstrap.babel.js b/lib/bootstrap.babel.js similarity index 100% rename from bootstrap.babel.js rename to lib/bootstrap.babel.js diff --git a/bootstrap.js b/lib/bootstrap.js similarity index 100% rename from bootstrap.js rename to lib/bootstrap.js diff --git a/bundle.js b/lib/bundle.js similarity index 100% rename from bundle.js rename to lib/bundle.js diff --git a/capabilities.html b/lib/capabilities.html similarity index 100% rename from capabilities.html rename to lib/capabilities.html diff --git a/capabilities.js b/lib/capabilities.js similarity index 100% rename from capabilities.js rename to lib/capabilities.js diff --git a/classes.html b/lib/classes.html similarity index 100% rename from classes.html rename to lib/classes.html diff --git a/classes.json b/lib/classes.json similarity index 100% rename from classes.json rename to lib/classes.json diff --git a/coordinates.html b/lib/coordinates.html similarity index 100% rename from coordinates.html rename to lib/coordinates.html diff --git a/doctest.html b/lib/doctest.html similarity index 100% rename from doctest.html rename to lib/doctest.html diff --git a/doctest.js b/lib/doctest.js similarity index 100% rename from doctest.js rename to lib/doctest.js diff --git a/electron.html b/lib/electron.html similarity index 100% rename from electron.html rename to lib/electron.html diff --git a/errors.js b/lib/errors.js similarity index 100% rename from errors.js rename to lib/errors.js diff --git a/events.html b/lib/events.html similarity index 100% rename from events.html rename to lib/events.html diff --git a/events.js b/lib/events.js similarity index 100% rename from events.js rename to lib/events.js diff --git a/examples/king.jpeg b/lib/examples/king.jpeg similarity index 100% rename from examples/king.jpeg rename to lib/examples/king.jpeg diff --git a/examples/memory/index.html b/lib/examples/memory/index.html similarity index 100% rename from examples/memory/index.html rename to lib/examples/memory/index.html diff --git a/examples/movie.mp4 b/lib/examples/movie.mp4 similarity index 100% rename from examples/movie.mp4 rename to lib/examples/movie.mp4 diff --git a/examples/multitouch.html b/lib/examples/multitouch.html similarity index 100% rename from examples/multitouch.html rename to lib/examples/multitouch.html diff --git a/examples/women.jpeg b/lib/examples/women.jpeg similarity index 100% rename from examples/women.jpeg rename to lib/examples/women.jpeg diff --git a/flippable.html b/lib/flippable.html similarity index 100% rename from flippable.html rename to lib/flippable.html diff --git a/flippable.js b/lib/flippable.js similarity index 100% rename from flippable.js rename to lib/flippable.js diff --git a/frames.html b/lib/frames.html similarity index 100% rename from frames.html rename to lib/frames.html diff --git a/frames.js b/lib/frames.js similarity index 100% rename from frames.js rename to lib/frames.js diff --git a/imageloader.html b/lib/imageloader.html similarity index 100% rename from imageloader.html rename to lib/imageloader.html diff --git a/imageloader.js b/lib/imageloader.js similarity index 100% rename from imageloader.js rename to lib/imageloader.js diff --git a/index.html b/lib/index.html similarity index 100% rename from index.html rename to lib/index.html diff --git a/index.js b/lib/index.js similarity index 100% rename from index.js rename to lib/index.js diff --git a/index.png b/lib/index.png similarity index 100% rename from index.png rename to lib/index.png diff --git a/inspect.html b/lib/inspect.html similarity index 100% rename from inspect.html rename to lib/inspect.html diff --git a/inspect.js b/lib/inspect.js similarity index 100% rename from inspect.js rename to lib/inspect.js diff --git a/interaction.html b/lib/interaction.html similarity index 100% rename from interaction.html rename to lib/interaction.html diff --git a/interaction.js b/lib/interaction.js similarity index 100% rename from interaction.js rename to lib/interaction.js diff --git a/interface.html b/lib/interface.html similarity index 100% rename from interface.html rename to lib/interface.html diff --git a/interface.js b/lib/interface.js similarity index 100% rename from interface.js rename to lib/interface.js diff --git a/pixi/abstractpopup.js b/lib/pixi/abstractpopup.js similarity index 100% rename from pixi/abstractpopup.js rename to lib/pixi/abstractpopup.js diff --git a/pixi/app-resolution.html b/lib/pixi/app-resolution.html similarity index 100% rename from pixi/app-resolution.html rename to lib/pixi/app-resolution.html diff --git a/pixi/app.html b/lib/pixi/app.html similarity index 100% rename from pixi/app.html rename to lib/pixi/app.html diff --git a/pixi/app.js b/lib/pixi/app.js similarity index 100% rename from pixi/app.js rename to lib/pixi/app.js diff --git a/pixi/application.html b/lib/pixi/application.html similarity index 100% rename from pixi/application.html rename to lib/pixi/application.html diff --git a/pixi/assets/Arial.bmglyph b/lib/pixi/assets/Arial.bmglyph similarity index 100% rename from pixi/assets/Arial.bmglyph rename to lib/pixi/assets/Arial.bmglyph diff --git a/pixi/assets/Arial.fnt b/lib/pixi/assets/Arial.fnt similarity index 100% rename from pixi/assets/Arial.fnt rename to lib/pixi/assets/Arial.fnt diff --git a/pixi/assets/Arial.png b/lib/pixi/assets/Arial.png similarity index 100% rename from pixi/assets/Arial.png rename to lib/pixi/assets/Arial.png diff --git a/pixi/assets/app-circle.png b/lib/pixi/assets/app-circle.png similarity index 100% rename from pixi/assets/app-circle.png rename to lib/pixi/assets/app-circle.png diff --git a/pixi/assets/app-circle@2x.png b/lib/pixi/assets/app-circle@2x.png similarity index 100% rename from pixi/assets/app-circle@2x.png rename to lib/pixi/assets/app-circle@2x.png diff --git a/pixi/assets/app-circle@3x.png b/lib/pixi/assets/app-circle@3x.png similarity index 100% rename from pixi/assets/app-circle@3x.png rename to lib/pixi/assets/app-circle@3x.png diff --git a/pixi/assets/back.jpg b/lib/pixi/assets/back.jpg similarity index 100% rename from pixi/assets/back.jpg rename to lib/pixi/assets/back.jpg diff --git a/pixi/assets/badge-1.mp4 b/lib/pixi/assets/badge-1.mp4 similarity index 100% rename from pixi/assets/badge-1.mp4 rename to lib/pixi/assets/badge-1.mp4 diff --git a/pixi/assets/bamboo-1.jpg b/lib/pixi/assets/bamboo-1.jpg similarity index 100% rename from pixi/assets/bamboo-1.jpg rename to lib/pixi/assets/bamboo-1.jpg diff --git a/pixi/assets/bamboo-2.jpg b/lib/pixi/assets/bamboo-2.jpg similarity index 100% rename from pixi/assets/bamboo-2.jpg rename to lib/pixi/assets/bamboo-2.jpg diff --git a/pixi/assets/bamboo-3.jpg b/lib/pixi/assets/bamboo-3.jpg similarity index 100% rename from pixi/assets/bamboo-3.jpg rename to lib/pixi/assets/bamboo-3.jpg diff --git a/pixi/assets/bamboo-4.jpg b/lib/pixi/assets/bamboo-4.jpg similarity index 100% rename from pixi/assets/bamboo-4.jpg rename to lib/pixi/assets/bamboo-4.jpg diff --git a/pixi/assets/bamboo-5.jpg b/lib/pixi/assets/bamboo-5.jpg similarity index 100% rename from pixi/assets/bamboo-5.jpg rename to lib/pixi/assets/bamboo-5.jpg diff --git a/pixi/assets/blurfilter.mp4 b/lib/pixi/assets/blurfilter.mp4 similarity index 100% rename from pixi/assets/blurfilter.mp4 rename to lib/pixi/assets/blurfilter.mp4 diff --git a/pixi/assets/circle.png b/lib/pixi/assets/circle.png similarity index 100% rename from pixi/assets/circle.png rename to lib/pixi/assets/circle.png diff --git a/pixi/assets/circle2.png b/lib/pixi/assets/circle2.png similarity index 100% rename from pixi/assets/circle2.png rename to lib/pixi/assets/circle2.png diff --git a/pixi/assets/elephant-1.jpg b/lib/pixi/assets/elephant-1.jpg similarity index 100% rename from pixi/assets/elephant-1.jpg rename to lib/pixi/assets/elephant-1.jpg diff --git a/pixi/assets/elephant-2.jpg b/lib/pixi/assets/elephant-2.jpg similarity index 100% rename from pixi/assets/elephant-2.jpg rename to lib/pixi/assets/elephant-2.jpg diff --git a/pixi/assets/elephant-3.jpg b/lib/pixi/assets/elephant-3.jpg similarity index 100% rename from pixi/assets/elephant-3.jpg rename to lib/pixi/assets/elephant-3.jpg diff --git a/pixi/assets/elephant-4.jpg b/lib/pixi/assets/elephant-4.jpg similarity index 100% rename from pixi/assets/elephant-4.jpg rename to lib/pixi/assets/elephant-4.jpg diff --git a/pixi/assets/elephant-5.jpg b/lib/pixi/assets/elephant-5.jpg similarity index 100% rename from pixi/assets/elephant-5.jpg rename to lib/pixi/assets/elephant-5.jpg diff --git a/pixi/assets/fruit-1.jpg b/lib/pixi/assets/fruit-1.jpg similarity index 100% rename from pixi/assets/fruit-1.jpg rename to lib/pixi/assets/fruit-1.jpg diff --git a/pixi/assets/fruit-10.jpg b/lib/pixi/assets/fruit-10.jpg similarity index 100% rename from pixi/assets/fruit-10.jpg rename to lib/pixi/assets/fruit-10.jpg diff --git a/pixi/assets/fruit-2.jpg b/lib/pixi/assets/fruit-2.jpg similarity index 100% rename from pixi/assets/fruit-2.jpg rename to lib/pixi/assets/fruit-2.jpg diff --git a/pixi/assets/fruit-3.jpg b/lib/pixi/assets/fruit-3.jpg similarity index 100% rename from pixi/assets/fruit-3.jpg rename to lib/pixi/assets/fruit-3.jpg diff --git a/pixi/assets/fruit-4.jpg b/lib/pixi/assets/fruit-4.jpg similarity index 100% rename from pixi/assets/fruit-4.jpg rename to lib/pixi/assets/fruit-4.jpg diff --git a/pixi/assets/fruit-5.jpg b/lib/pixi/assets/fruit-5.jpg similarity index 100% rename from pixi/assets/fruit-5.jpg rename to lib/pixi/assets/fruit-5.jpg diff --git a/pixi/assets/fruit-6.jpg b/lib/pixi/assets/fruit-6.jpg similarity index 100% rename from pixi/assets/fruit-6.jpg rename to lib/pixi/assets/fruit-6.jpg diff --git a/pixi/assets/fruit-7.jpg b/lib/pixi/assets/fruit-7.jpg similarity index 100% rename from pixi/assets/fruit-7.jpg rename to lib/pixi/assets/fruit-7.jpg diff --git a/pixi/assets/fruit-8.jpg b/lib/pixi/assets/fruit-8.jpg similarity index 100% rename from pixi/assets/fruit-8.jpg rename to lib/pixi/assets/fruit-8.jpg diff --git a/pixi/assets/fruit-9.jpg b/lib/pixi/assets/fruit-9.jpg similarity index 100% rename from pixi/assets/fruit-9.jpg rename to lib/pixi/assets/fruit-9.jpg diff --git a/pixi/assets/jungle.jpg b/lib/pixi/assets/jungle.jpg similarity index 100% rename from pixi/assets/jungle.jpg rename to lib/pixi/assets/jungle.jpg diff --git a/pixi/assets/maps/test/0/0/0.jpg b/lib/pixi/assets/maps/test/0/0/0.jpg similarity index 100% rename from pixi/assets/maps/test/0/0/0.jpg rename to lib/pixi/assets/maps/test/0/0/0.jpg diff --git a/pixi/assets/maps/test/1/0/0.jpg b/lib/pixi/assets/maps/test/1/0/0.jpg similarity index 100% rename from pixi/assets/maps/test/1/0/0.jpg rename to lib/pixi/assets/maps/test/1/0/0.jpg diff --git a/pixi/assets/maps/test/1/0/1.jpg b/lib/pixi/assets/maps/test/1/0/1.jpg similarity index 100% rename from pixi/assets/maps/test/1/0/1.jpg rename to lib/pixi/assets/maps/test/1/0/1.jpg diff --git a/pixi/assets/maps/test/1/1/0.jpg b/lib/pixi/assets/maps/test/1/1/0.jpg similarity index 100% rename from pixi/assets/maps/test/1/1/0.jpg rename to lib/pixi/assets/maps/test/1/1/0.jpg diff --git a/pixi/assets/maps/test/1/1/1.jpg b/lib/pixi/assets/maps/test/1/1/1.jpg similarity index 100% rename from pixi/assets/maps/test/1/1/1.jpg rename to lib/pixi/assets/maps/test/1/1/1.jpg diff --git a/pixi/assets/maps/test/2/0/0.jpg b/lib/pixi/assets/maps/test/2/0/0.jpg similarity index 100% rename from pixi/assets/maps/test/2/0/0.jpg rename to lib/pixi/assets/maps/test/2/0/0.jpg diff --git a/pixi/assets/maps/test/2/0/1.jpg b/lib/pixi/assets/maps/test/2/0/1.jpg similarity index 100% rename from pixi/assets/maps/test/2/0/1.jpg rename to lib/pixi/assets/maps/test/2/0/1.jpg diff --git a/pixi/assets/maps/test/2/0/2.jpg b/lib/pixi/assets/maps/test/2/0/2.jpg similarity index 100% rename from pixi/assets/maps/test/2/0/2.jpg rename to lib/pixi/assets/maps/test/2/0/2.jpg diff --git a/pixi/assets/maps/test/2/0/3.jpg b/lib/pixi/assets/maps/test/2/0/3.jpg similarity index 100% rename from pixi/assets/maps/test/2/0/3.jpg rename to lib/pixi/assets/maps/test/2/0/3.jpg diff --git a/pixi/assets/maps/test/2/1/0.jpg b/lib/pixi/assets/maps/test/2/1/0.jpg similarity index 100% rename from pixi/assets/maps/test/2/1/0.jpg rename to lib/pixi/assets/maps/test/2/1/0.jpg diff --git a/pixi/assets/maps/test/2/1/1.jpg b/lib/pixi/assets/maps/test/2/1/1.jpg similarity index 100% rename from pixi/assets/maps/test/2/1/1.jpg rename to lib/pixi/assets/maps/test/2/1/1.jpg diff --git a/pixi/assets/maps/test/2/1/2.jpg b/lib/pixi/assets/maps/test/2/1/2.jpg similarity index 100% rename from pixi/assets/maps/test/2/1/2.jpg rename to lib/pixi/assets/maps/test/2/1/2.jpg diff --git a/pixi/assets/maps/test/2/1/3.jpg b/lib/pixi/assets/maps/test/2/1/3.jpg similarity index 100% rename from pixi/assets/maps/test/2/1/3.jpg rename to lib/pixi/assets/maps/test/2/1/3.jpg diff --git a/pixi/assets/maps/test/2/2/0.jpg b/lib/pixi/assets/maps/test/2/2/0.jpg similarity index 100% rename from pixi/assets/maps/test/2/2/0.jpg rename to lib/pixi/assets/maps/test/2/2/0.jpg diff --git a/pixi/assets/maps/test/2/2/1.jpg b/lib/pixi/assets/maps/test/2/2/1.jpg similarity index 100% rename from pixi/assets/maps/test/2/2/1.jpg rename to lib/pixi/assets/maps/test/2/2/1.jpg diff --git a/pixi/assets/maps/test/2/2/2.jpg b/lib/pixi/assets/maps/test/2/2/2.jpg similarity index 100% rename from pixi/assets/maps/test/2/2/2.jpg rename to lib/pixi/assets/maps/test/2/2/2.jpg diff --git a/pixi/assets/maps/test/2/2/3.jpg b/lib/pixi/assets/maps/test/2/2/3.jpg similarity index 100% rename from pixi/assets/maps/test/2/2/3.jpg rename to lib/pixi/assets/maps/test/2/2/3.jpg diff --git a/pixi/assets/maps/test/2/3/0.jpg b/lib/pixi/assets/maps/test/2/3/0.jpg similarity index 100% rename from pixi/assets/maps/test/2/3/0.jpg rename to lib/pixi/assets/maps/test/2/3/0.jpg diff --git a/pixi/assets/maps/test/2/3/1.jpg b/lib/pixi/assets/maps/test/2/3/1.jpg similarity index 100% rename from pixi/assets/maps/test/2/3/1.jpg rename to lib/pixi/assets/maps/test/2/3/1.jpg diff --git a/pixi/assets/maps/test/2/3/2.jpg b/lib/pixi/assets/maps/test/2/3/2.jpg similarity index 100% rename from pixi/assets/maps/test/2/3/2.jpg rename to lib/pixi/assets/maps/test/2/3/2.jpg diff --git a/pixi/assets/maps/test/2/3/3.jpg b/lib/pixi/assets/maps/test/2/3/3.jpg similarity index 100% rename from pixi/assets/maps/test/2/3/3.jpg rename to lib/pixi/assets/maps/test/2/3/3.jpg diff --git a/pixi/assets/maps/test/3/0/0.jpg b/lib/pixi/assets/maps/test/3/0/0.jpg similarity index 100% rename from pixi/assets/maps/test/3/0/0.jpg rename to lib/pixi/assets/maps/test/3/0/0.jpg diff --git a/pixi/assets/maps/test/3/0/1.jpg b/lib/pixi/assets/maps/test/3/0/1.jpg similarity index 100% rename from pixi/assets/maps/test/3/0/1.jpg rename to lib/pixi/assets/maps/test/3/0/1.jpg diff --git a/pixi/assets/maps/test/3/0/2.jpg b/lib/pixi/assets/maps/test/3/0/2.jpg similarity index 100% rename from pixi/assets/maps/test/3/0/2.jpg rename to lib/pixi/assets/maps/test/3/0/2.jpg diff --git a/pixi/assets/maps/test/3/0/3.jpg b/lib/pixi/assets/maps/test/3/0/3.jpg similarity index 100% rename from pixi/assets/maps/test/3/0/3.jpg rename to lib/pixi/assets/maps/test/3/0/3.jpg diff --git a/pixi/assets/maps/test/3/0/4.jpg b/lib/pixi/assets/maps/test/3/0/4.jpg similarity index 100% rename from pixi/assets/maps/test/3/0/4.jpg rename to lib/pixi/assets/maps/test/3/0/4.jpg diff --git a/pixi/assets/maps/test/3/0/5.jpg b/lib/pixi/assets/maps/test/3/0/5.jpg similarity index 100% rename from pixi/assets/maps/test/3/0/5.jpg rename to lib/pixi/assets/maps/test/3/0/5.jpg diff --git a/pixi/assets/maps/test/3/0/6.jpg b/lib/pixi/assets/maps/test/3/0/6.jpg similarity index 100% rename from pixi/assets/maps/test/3/0/6.jpg rename to lib/pixi/assets/maps/test/3/0/6.jpg diff --git a/pixi/assets/maps/test/3/0/7.jpg b/lib/pixi/assets/maps/test/3/0/7.jpg similarity index 100% rename from pixi/assets/maps/test/3/0/7.jpg rename to lib/pixi/assets/maps/test/3/0/7.jpg diff --git a/pixi/assets/maps/test/3/1/0.jpg b/lib/pixi/assets/maps/test/3/1/0.jpg similarity index 100% rename from pixi/assets/maps/test/3/1/0.jpg rename to lib/pixi/assets/maps/test/3/1/0.jpg diff --git a/pixi/assets/maps/test/3/1/1.jpg b/lib/pixi/assets/maps/test/3/1/1.jpg similarity index 100% rename from pixi/assets/maps/test/3/1/1.jpg rename to lib/pixi/assets/maps/test/3/1/1.jpg diff --git a/pixi/assets/maps/test/3/1/2.jpg b/lib/pixi/assets/maps/test/3/1/2.jpg similarity index 100% rename from pixi/assets/maps/test/3/1/2.jpg rename to lib/pixi/assets/maps/test/3/1/2.jpg diff --git a/pixi/assets/maps/test/3/1/3.jpg b/lib/pixi/assets/maps/test/3/1/3.jpg similarity index 100% rename from pixi/assets/maps/test/3/1/3.jpg rename to lib/pixi/assets/maps/test/3/1/3.jpg diff --git a/pixi/assets/maps/test/3/1/4.jpg b/lib/pixi/assets/maps/test/3/1/4.jpg similarity index 100% rename from pixi/assets/maps/test/3/1/4.jpg rename to lib/pixi/assets/maps/test/3/1/4.jpg diff --git a/pixi/assets/maps/test/3/1/5.jpg b/lib/pixi/assets/maps/test/3/1/5.jpg similarity index 100% rename from pixi/assets/maps/test/3/1/5.jpg rename to lib/pixi/assets/maps/test/3/1/5.jpg diff --git a/pixi/assets/maps/test/3/1/6.jpg b/lib/pixi/assets/maps/test/3/1/6.jpg similarity index 100% rename from pixi/assets/maps/test/3/1/6.jpg rename to lib/pixi/assets/maps/test/3/1/6.jpg diff --git a/pixi/assets/maps/test/3/1/7.jpg b/lib/pixi/assets/maps/test/3/1/7.jpg similarity index 100% rename from pixi/assets/maps/test/3/1/7.jpg rename to lib/pixi/assets/maps/test/3/1/7.jpg diff --git a/pixi/assets/maps/test/3/2/0.jpg b/lib/pixi/assets/maps/test/3/2/0.jpg similarity index 100% rename from pixi/assets/maps/test/3/2/0.jpg rename to lib/pixi/assets/maps/test/3/2/0.jpg diff --git a/pixi/assets/maps/test/3/2/1.jpg b/lib/pixi/assets/maps/test/3/2/1.jpg similarity index 100% rename from pixi/assets/maps/test/3/2/1.jpg rename to lib/pixi/assets/maps/test/3/2/1.jpg diff --git a/pixi/assets/maps/test/3/2/2.jpg b/lib/pixi/assets/maps/test/3/2/2.jpg similarity index 100% rename from pixi/assets/maps/test/3/2/2.jpg rename to lib/pixi/assets/maps/test/3/2/2.jpg diff --git a/pixi/assets/maps/test/3/2/3.jpg b/lib/pixi/assets/maps/test/3/2/3.jpg similarity index 100% rename from pixi/assets/maps/test/3/2/3.jpg rename to lib/pixi/assets/maps/test/3/2/3.jpg diff --git a/pixi/assets/maps/test/3/2/4.jpg b/lib/pixi/assets/maps/test/3/2/4.jpg similarity index 100% rename from pixi/assets/maps/test/3/2/4.jpg rename to lib/pixi/assets/maps/test/3/2/4.jpg diff --git a/pixi/assets/maps/test/3/2/5.jpg b/lib/pixi/assets/maps/test/3/2/5.jpg similarity index 100% rename from pixi/assets/maps/test/3/2/5.jpg rename to lib/pixi/assets/maps/test/3/2/5.jpg diff --git a/pixi/assets/maps/test/3/2/6.jpg b/lib/pixi/assets/maps/test/3/2/6.jpg similarity index 100% rename from pixi/assets/maps/test/3/2/6.jpg rename to lib/pixi/assets/maps/test/3/2/6.jpg diff --git a/pixi/assets/maps/test/3/2/7.jpg b/lib/pixi/assets/maps/test/3/2/7.jpg similarity index 100% rename from pixi/assets/maps/test/3/2/7.jpg rename to lib/pixi/assets/maps/test/3/2/7.jpg diff --git a/pixi/assets/maps/test/3/3/0.jpg b/lib/pixi/assets/maps/test/3/3/0.jpg similarity index 100% rename from pixi/assets/maps/test/3/3/0.jpg rename to lib/pixi/assets/maps/test/3/3/0.jpg diff --git a/pixi/assets/maps/test/3/3/1.jpg b/lib/pixi/assets/maps/test/3/3/1.jpg similarity index 100% rename from pixi/assets/maps/test/3/3/1.jpg rename to lib/pixi/assets/maps/test/3/3/1.jpg diff --git a/pixi/assets/maps/test/3/3/2.jpg b/lib/pixi/assets/maps/test/3/3/2.jpg similarity index 100% rename from pixi/assets/maps/test/3/3/2.jpg rename to lib/pixi/assets/maps/test/3/3/2.jpg diff --git a/pixi/assets/maps/test/3/3/3.jpg b/lib/pixi/assets/maps/test/3/3/3.jpg similarity index 100% rename from pixi/assets/maps/test/3/3/3.jpg rename to lib/pixi/assets/maps/test/3/3/3.jpg diff --git a/pixi/assets/maps/test/3/3/4.jpg b/lib/pixi/assets/maps/test/3/3/4.jpg similarity index 100% rename from pixi/assets/maps/test/3/3/4.jpg rename to lib/pixi/assets/maps/test/3/3/4.jpg diff --git a/pixi/assets/maps/test/3/3/5.jpg b/lib/pixi/assets/maps/test/3/3/5.jpg similarity index 100% rename from pixi/assets/maps/test/3/3/5.jpg rename to lib/pixi/assets/maps/test/3/3/5.jpg diff --git a/pixi/assets/maps/test/3/3/6.jpg b/lib/pixi/assets/maps/test/3/3/6.jpg similarity index 100% rename from pixi/assets/maps/test/3/3/6.jpg rename to lib/pixi/assets/maps/test/3/3/6.jpg diff --git a/pixi/assets/maps/test/3/3/7.jpg b/lib/pixi/assets/maps/test/3/3/7.jpg similarity index 100% rename from pixi/assets/maps/test/3/3/7.jpg rename to lib/pixi/assets/maps/test/3/3/7.jpg diff --git a/pixi/assets/maps/test/3/4/0.jpg b/lib/pixi/assets/maps/test/3/4/0.jpg similarity index 100% rename from pixi/assets/maps/test/3/4/0.jpg rename to lib/pixi/assets/maps/test/3/4/0.jpg diff --git a/pixi/assets/maps/test/3/4/1.jpg b/lib/pixi/assets/maps/test/3/4/1.jpg similarity index 100% rename from pixi/assets/maps/test/3/4/1.jpg rename to lib/pixi/assets/maps/test/3/4/1.jpg diff --git a/pixi/assets/maps/test/3/4/2.jpg b/lib/pixi/assets/maps/test/3/4/2.jpg similarity index 100% rename from pixi/assets/maps/test/3/4/2.jpg rename to lib/pixi/assets/maps/test/3/4/2.jpg diff --git a/pixi/assets/maps/test/3/4/3.jpg b/lib/pixi/assets/maps/test/3/4/3.jpg similarity index 100% rename from pixi/assets/maps/test/3/4/3.jpg rename to lib/pixi/assets/maps/test/3/4/3.jpg diff --git a/pixi/assets/maps/test/3/4/4.jpg b/lib/pixi/assets/maps/test/3/4/4.jpg similarity index 100% rename from pixi/assets/maps/test/3/4/4.jpg rename to lib/pixi/assets/maps/test/3/4/4.jpg diff --git a/pixi/assets/maps/test/3/4/5.jpg b/lib/pixi/assets/maps/test/3/4/5.jpg similarity index 100% rename from pixi/assets/maps/test/3/4/5.jpg rename to lib/pixi/assets/maps/test/3/4/5.jpg diff --git a/pixi/assets/maps/test/3/4/6.jpg b/lib/pixi/assets/maps/test/3/4/6.jpg similarity index 100% rename from pixi/assets/maps/test/3/4/6.jpg rename to lib/pixi/assets/maps/test/3/4/6.jpg diff --git a/pixi/assets/maps/test/3/4/7.jpg b/lib/pixi/assets/maps/test/3/4/7.jpg similarity index 100% rename from pixi/assets/maps/test/3/4/7.jpg rename to lib/pixi/assets/maps/test/3/4/7.jpg diff --git a/pixi/assets/maps/test/3/5/0.jpg b/lib/pixi/assets/maps/test/3/5/0.jpg similarity index 100% rename from pixi/assets/maps/test/3/5/0.jpg rename to lib/pixi/assets/maps/test/3/5/0.jpg diff --git a/pixi/assets/maps/test/3/5/1.jpg b/lib/pixi/assets/maps/test/3/5/1.jpg similarity index 100% rename from pixi/assets/maps/test/3/5/1.jpg rename to lib/pixi/assets/maps/test/3/5/1.jpg diff --git a/pixi/assets/maps/test/3/5/2.jpg b/lib/pixi/assets/maps/test/3/5/2.jpg similarity index 100% rename from pixi/assets/maps/test/3/5/2.jpg rename to lib/pixi/assets/maps/test/3/5/2.jpg diff --git a/pixi/assets/maps/test/3/5/3.jpg b/lib/pixi/assets/maps/test/3/5/3.jpg similarity index 100% rename from pixi/assets/maps/test/3/5/3.jpg rename to lib/pixi/assets/maps/test/3/5/3.jpg diff --git a/pixi/assets/maps/test/3/5/4.jpg b/lib/pixi/assets/maps/test/3/5/4.jpg similarity index 100% rename from pixi/assets/maps/test/3/5/4.jpg rename to lib/pixi/assets/maps/test/3/5/4.jpg diff --git a/pixi/assets/maps/test/3/5/5.jpg b/lib/pixi/assets/maps/test/3/5/5.jpg similarity index 100% rename from pixi/assets/maps/test/3/5/5.jpg rename to lib/pixi/assets/maps/test/3/5/5.jpg diff --git a/pixi/assets/maps/test/3/5/6.jpg b/lib/pixi/assets/maps/test/3/5/6.jpg similarity index 100% rename from pixi/assets/maps/test/3/5/6.jpg rename to lib/pixi/assets/maps/test/3/5/6.jpg diff --git a/pixi/assets/maps/test/3/5/7.jpg b/lib/pixi/assets/maps/test/3/5/7.jpg similarity index 100% rename from pixi/assets/maps/test/3/5/7.jpg rename to lib/pixi/assets/maps/test/3/5/7.jpg diff --git a/pixi/assets/maps/test/3/6/0.jpg b/lib/pixi/assets/maps/test/3/6/0.jpg similarity index 100% rename from pixi/assets/maps/test/3/6/0.jpg rename to lib/pixi/assets/maps/test/3/6/0.jpg diff --git a/pixi/assets/maps/test/3/6/1.jpg b/lib/pixi/assets/maps/test/3/6/1.jpg similarity index 100% rename from pixi/assets/maps/test/3/6/1.jpg rename to lib/pixi/assets/maps/test/3/6/1.jpg diff --git a/pixi/assets/maps/test/3/6/2.jpg b/lib/pixi/assets/maps/test/3/6/2.jpg similarity index 100% rename from pixi/assets/maps/test/3/6/2.jpg rename to lib/pixi/assets/maps/test/3/6/2.jpg diff --git a/pixi/assets/maps/test/3/6/3.jpg b/lib/pixi/assets/maps/test/3/6/3.jpg similarity index 100% rename from pixi/assets/maps/test/3/6/3.jpg rename to lib/pixi/assets/maps/test/3/6/3.jpg diff --git a/pixi/assets/maps/test/3/6/4.jpg b/lib/pixi/assets/maps/test/3/6/4.jpg similarity index 100% rename from pixi/assets/maps/test/3/6/4.jpg rename to lib/pixi/assets/maps/test/3/6/4.jpg diff --git a/pixi/assets/maps/test/3/6/5.jpg b/lib/pixi/assets/maps/test/3/6/5.jpg similarity index 100% rename from pixi/assets/maps/test/3/6/5.jpg rename to lib/pixi/assets/maps/test/3/6/5.jpg diff --git a/pixi/assets/maps/test/3/6/6.jpg b/lib/pixi/assets/maps/test/3/6/6.jpg similarity index 100% rename from pixi/assets/maps/test/3/6/6.jpg rename to lib/pixi/assets/maps/test/3/6/6.jpg diff --git a/pixi/assets/maps/test/3/6/7.jpg b/lib/pixi/assets/maps/test/3/6/7.jpg similarity index 100% rename from pixi/assets/maps/test/3/6/7.jpg rename to lib/pixi/assets/maps/test/3/6/7.jpg diff --git a/pixi/assets/maps/test/3/7/0.jpg b/lib/pixi/assets/maps/test/3/7/0.jpg similarity index 100% rename from pixi/assets/maps/test/3/7/0.jpg rename to lib/pixi/assets/maps/test/3/7/0.jpg diff --git a/pixi/assets/maps/test/3/7/1.jpg b/lib/pixi/assets/maps/test/3/7/1.jpg similarity index 100% rename from pixi/assets/maps/test/3/7/1.jpg rename to lib/pixi/assets/maps/test/3/7/1.jpg diff --git a/pixi/assets/maps/test/3/7/2.jpg b/lib/pixi/assets/maps/test/3/7/2.jpg similarity index 100% rename from pixi/assets/maps/test/3/7/2.jpg rename to lib/pixi/assets/maps/test/3/7/2.jpg diff --git a/pixi/assets/maps/test/3/7/3.jpg b/lib/pixi/assets/maps/test/3/7/3.jpg similarity index 100% rename from pixi/assets/maps/test/3/7/3.jpg rename to lib/pixi/assets/maps/test/3/7/3.jpg diff --git a/pixi/assets/maps/test/3/7/4.jpg b/lib/pixi/assets/maps/test/3/7/4.jpg similarity index 100% rename from pixi/assets/maps/test/3/7/4.jpg rename to lib/pixi/assets/maps/test/3/7/4.jpg diff --git a/pixi/assets/maps/test/3/7/5.jpg b/lib/pixi/assets/maps/test/3/7/5.jpg similarity index 100% rename from pixi/assets/maps/test/3/7/5.jpg rename to lib/pixi/assets/maps/test/3/7/5.jpg diff --git a/pixi/assets/maps/test/3/7/6.jpg b/lib/pixi/assets/maps/test/3/7/6.jpg similarity index 100% rename from pixi/assets/maps/test/3/7/6.jpg rename to lib/pixi/assets/maps/test/3/7/6.jpg diff --git a/pixi/assets/maps/test/3/7/7.jpg b/lib/pixi/assets/maps/test/3/7/7.jpg similarity index 100% rename from pixi/assets/maps/test/3/7/7.jpg rename to lib/pixi/assets/maps/test/3/7/7.jpg diff --git a/pixi/assets/maps/test/4/0/0.jpg b/lib/pixi/assets/maps/test/4/0/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/0.jpg rename to lib/pixi/assets/maps/test/4/0/0.jpg diff --git a/pixi/assets/maps/test/4/0/1.jpg b/lib/pixi/assets/maps/test/4/0/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/1.jpg rename to lib/pixi/assets/maps/test/4/0/1.jpg diff --git a/pixi/assets/maps/test/4/0/10.jpg b/lib/pixi/assets/maps/test/4/0/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/10.jpg rename to lib/pixi/assets/maps/test/4/0/10.jpg diff --git a/pixi/assets/maps/test/4/0/11.jpg b/lib/pixi/assets/maps/test/4/0/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/11.jpg rename to lib/pixi/assets/maps/test/4/0/11.jpg diff --git a/pixi/assets/maps/test/4/0/12.jpg b/lib/pixi/assets/maps/test/4/0/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/12.jpg rename to lib/pixi/assets/maps/test/4/0/12.jpg diff --git a/pixi/assets/maps/test/4/0/13.jpg b/lib/pixi/assets/maps/test/4/0/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/13.jpg rename to lib/pixi/assets/maps/test/4/0/13.jpg diff --git a/pixi/assets/maps/test/4/0/14.jpg b/lib/pixi/assets/maps/test/4/0/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/14.jpg rename to lib/pixi/assets/maps/test/4/0/14.jpg diff --git a/pixi/assets/maps/test/4/0/15.jpg b/lib/pixi/assets/maps/test/4/0/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/15.jpg rename to lib/pixi/assets/maps/test/4/0/15.jpg diff --git a/pixi/assets/maps/test/4/0/2.jpg b/lib/pixi/assets/maps/test/4/0/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/2.jpg rename to lib/pixi/assets/maps/test/4/0/2.jpg diff --git a/pixi/assets/maps/test/4/0/3.jpg b/lib/pixi/assets/maps/test/4/0/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/3.jpg rename to lib/pixi/assets/maps/test/4/0/3.jpg diff --git a/pixi/assets/maps/test/4/0/4.jpg b/lib/pixi/assets/maps/test/4/0/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/4.jpg rename to lib/pixi/assets/maps/test/4/0/4.jpg diff --git a/pixi/assets/maps/test/4/0/5.jpg b/lib/pixi/assets/maps/test/4/0/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/5.jpg rename to lib/pixi/assets/maps/test/4/0/5.jpg diff --git a/pixi/assets/maps/test/4/0/6.jpg b/lib/pixi/assets/maps/test/4/0/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/6.jpg rename to lib/pixi/assets/maps/test/4/0/6.jpg diff --git a/pixi/assets/maps/test/4/0/7.jpg b/lib/pixi/assets/maps/test/4/0/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/7.jpg rename to lib/pixi/assets/maps/test/4/0/7.jpg diff --git a/pixi/assets/maps/test/4/0/8.jpg b/lib/pixi/assets/maps/test/4/0/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/8.jpg rename to lib/pixi/assets/maps/test/4/0/8.jpg diff --git a/pixi/assets/maps/test/4/0/9.jpg b/lib/pixi/assets/maps/test/4/0/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/0/9.jpg rename to lib/pixi/assets/maps/test/4/0/9.jpg diff --git a/pixi/assets/maps/test/4/1/0.jpg b/lib/pixi/assets/maps/test/4/1/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/0.jpg rename to lib/pixi/assets/maps/test/4/1/0.jpg diff --git a/pixi/assets/maps/test/4/1/1.jpg b/lib/pixi/assets/maps/test/4/1/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/1.jpg rename to lib/pixi/assets/maps/test/4/1/1.jpg diff --git a/pixi/assets/maps/test/4/1/10.jpg b/lib/pixi/assets/maps/test/4/1/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/10.jpg rename to lib/pixi/assets/maps/test/4/1/10.jpg diff --git a/pixi/assets/maps/test/4/1/11.jpg b/lib/pixi/assets/maps/test/4/1/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/11.jpg rename to lib/pixi/assets/maps/test/4/1/11.jpg diff --git a/pixi/assets/maps/test/4/1/12.jpg b/lib/pixi/assets/maps/test/4/1/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/12.jpg rename to lib/pixi/assets/maps/test/4/1/12.jpg diff --git a/pixi/assets/maps/test/4/1/13.jpg b/lib/pixi/assets/maps/test/4/1/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/13.jpg rename to lib/pixi/assets/maps/test/4/1/13.jpg diff --git a/pixi/assets/maps/test/4/1/14.jpg b/lib/pixi/assets/maps/test/4/1/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/14.jpg rename to lib/pixi/assets/maps/test/4/1/14.jpg diff --git a/pixi/assets/maps/test/4/1/15.jpg b/lib/pixi/assets/maps/test/4/1/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/15.jpg rename to lib/pixi/assets/maps/test/4/1/15.jpg diff --git a/pixi/assets/maps/test/4/1/2.jpg b/lib/pixi/assets/maps/test/4/1/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/2.jpg rename to lib/pixi/assets/maps/test/4/1/2.jpg diff --git a/pixi/assets/maps/test/4/1/3.jpg b/lib/pixi/assets/maps/test/4/1/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/3.jpg rename to lib/pixi/assets/maps/test/4/1/3.jpg diff --git a/pixi/assets/maps/test/4/1/4.jpg b/lib/pixi/assets/maps/test/4/1/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/4.jpg rename to lib/pixi/assets/maps/test/4/1/4.jpg diff --git a/pixi/assets/maps/test/4/1/5.jpg b/lib/pixi/assets/maps/test/4/1/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/5.jpg rename to lib/pixi/assets/maps/test/4/1/5.jpg diff --git a/pixi/assets/maps/test/4/1/6.jpg b/lib/pixi/assets/maps/test/4/1/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/6.jpg rename to lib/pixi/assets/maps/test/4/1/6.jpg diff --git a/pixi/assets/maps/test/4/1/7.jpg b/lib/pixi/assets/maps/test/4/1/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/7.jpg rename to lib/pixi/assets/maps/test/4/1/7.jpg diff --git a/pixi/assets/maps/test/4/1/8.jpg b/lib/pixi/assets/maps/test/4/1/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/8.jpg rename to lib/pixi/assets/maps/test/4/1/8.jpg diff --git a/pixi/assets/maps/test/4/1/9.jpg b/lib/pixi/assets/maps/test/4/1/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/1/9.jpg rename to lib/pixi/assets/maps/test/4/1/9.jpg diff --git a/pixi/assets/maps/test/4/10/0.jpg b/lib/pixi/assets/maps/test/4/10/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/0.jpg rename to lib/pixi/assets/maps/test/4/10/0.jpg diff --git a/pixi/assets/maps/test/4/10/1.jpg b/lib/pixi/assets/maps/test/4/10/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/1.jpg rename to lib/pixi/assets/maps/test/4/10/1.jpg diff --git a/pixi/assets/maps/test/4/10/10.jpg b/lib/pixi/assets/maps/test/4/10/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/10.jpg rename to lib/pixi/assets/maps/test/4/10/10.jpg diff --git a/pixi/assets/maps/test/4/10/11.jpg b/lib/pixi/assets/maps/test/4/10/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/11.jpg rename to lib/pixi/assets/maps/test/4/10/11.jpg diff --git a/pixi/assets/maps/test/4/10/12.jpg b/lib/pixi/assets/maps/test/4/10/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/12.jpg rename to lib/pixi/assets/maps/test/4/10/12.jpg diff --git a/pixi/assets/maps/test/4/10/13.jpg b/lib/pixi/assets/maps/test/4/10/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/13.jpg rename to lib/pixi/assets/maps/test/4/10/13.jpg diff --git a/pixi/assets/maps/test/4/10/14.jpg b/lib/pixi/assets/maps/test/4/10/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/14.jpg rename to lib/pixi/assets/maps/test/4/10/14.jpg diff --git a/pixi/assets/maps/test/4/10/15.jpg b/lib/pixi/assets/maps/test/4/10/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/15.jpg rename to lib/pixi/assets/maps/test/4/10/15.jpg diff --git a/pixi/assets/maps/test/4/10/2.jpg b/lib/pixi/assets/maps/test/4/10/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/2.jpg rename to lib/pixi/assets/maps/test/4/10/2.jpg diff --git a/pixi/assets/maps/test/4/10/3.jpg b/lib/pixi/assets/maps/test/4/10/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/3.jpg rename to lib/pixi/assets/maps/test/4/10/3.jpg diff --git a/pixi/assets/maps/test/4/10/4.jpg b/lib/pixi/assets/maps/test/4/10/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/4.jpg rename to lib/pixi/assets/maps/test/4/10/4.jpg diff --git a/pixi/assets/maps/test/4/10/5.jpg b/lib/pixi/assets/maps/test/4/10/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/5.jpg rename to lib/pixi/assets/maps/test/4/10/5.jpg diff --git a/pixi/assets/maps/test/4/10/6.jpg b/lib/pixi/assets/maps/test/4/10/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/6.jpg rename to lib/pixi/assets/maps/test/4/10/6.jpg diff --git a/pixi/assets/maps/test/4/10/7.jpg b/lib/pixi/assets/maps/test/4/10/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/7.jpg rename to lib/pixi/assets/maps/test/4/10/7.jpg diff --git a/pixi/assets/maps/test/4/10/8.jpg b/lib/pixi/assets/maps/test/4/10/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/8.jpg rename to lib/pixi/assets/maps/test/4/10/8.jpg diff --git a/pixi/assets/maps/test/4/10/9.jpg b/lib/pixi/assets/maps/test/4/10/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/10/9.jpg rename to lib/pixi/assets/maps/test/4/10/9.jpg diff --git a/pixi/assets/maps/test/4/11/0.jpg b/lib/pixi/assets/maps/test/4/11/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/0.jpg rename to lib/pixi/assets/maps/test/4/11/0.jpg diff --git a/pixi/assets/maps/test/4/11/1.jpg b/lib/pixi/assets/maps/test/4/11/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/1.jpg rename to lib/pixi/assets/maps/test/4/11/1.jpg diff --git a/pixi/assets/maps/test/4/11/10.jpg b/lib/pixi/assets/maps/test/4/11/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/10.jpg rename to lib/pixi/assets/maps/test/4/11/10.jpg diff --git a/pixi/assets/maps/test/4/11/11.jpg b/lib/pixi/assets/maps/test/4/11/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/11.jpg rename to lib/pixi/assets/maps/test/4/11/11.jpg diff --git a/pixi/assets/maps/test/4/11/12.jpg b/lib/pixi/assets/maps/test/4/11/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/12.jpg rename to lib/pixi/assets/maps/test/4/11/12.jpg diff --git a/pixi/assets/maps/test/4/11/13.jpg b/lib/pixi/assets/maps/test/4/11/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/13.jpg rename to lib/pixi/assets/maps/test/4/11/13.jpg diff --git a/pixi/assets/maps/test/4/11/14.jpg b/lib/pixi/assets/maps/test/4/11/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/14.jpg rename to lib/pixi/assets/maps/test/4/11/14.jpg diff --git a/pixi/assets/maps/test/4/11/15.jpg b/lib/pixi/assets/maps/test/4/11/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/15.jpg rename to lib/pixi/assets/maps/test/4/11/15.jpg diff --git a/pixi/assets/maps/test/4/11/2.jpg b/lib/pixi/assets/maps/test/4/11/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/2.jpg rename to lib/pixi/assets/maps/test/4/11/2.jpg diff --git a/pixi/assets/maps/test/4/11/3.jpg b/lib/pixi/assets/maps/test/4/11/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/3.jpg rename to lib/pixi/assets/maps/test/4/11/3.jpg diff --git a/pixi/assets/maps/test/4/11/4.jpg b/lib/pixi/assets/maps/test/4/11/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/4.jpg rename to lib/pixi/assets/maps/test/4/11/4.jpg diff --git a/pixi/assets/maps/test/4/11/5.jpg b/lib/pixi/assets/maps/test/4/11/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/5.jpg rename to lib/pixi/assets/maps/test/4/11/5.jpg diff --git a/pixi/assets/maps/test/4/11/6.jpg b/lib/pixi/assets/maps/test/4/11/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/6.jpg rename to lib/pixi/assets/maps/test/4/11/6.jpg diff --git a/pixi/assets/maps/test/4/11/7.jpg b/lib/pixi/assets/maps/test/4/11/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/7.jpg rename to lib/pixi/assets/maps/test/4/11/7.jpg diff --git a/pixi/assets/maps/test/4/11/8.jpg b/lib/pixi/assets/maps/test/4/11/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/8.jpg rename to lib/pixi/assets/maps/test/4/11/8.jpg diff --git a/pixi/assets/maps/test/4/11/9.jpg b/lib/pixi/assets/maps/test/4/11/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/11/9.jpg rename to lib/pixi/assets/maps/test/4/11/9.jpg diff --git a/pixi/assets/maps/test/4/12/0.jpg b/lib/pixi/assets/maps/test/4/12/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/0.jpg rename to lib/pixi/assets/maps/test/4/12/0.jpg diff --git a/pixi/assets/maps/test/4/12/1.jpg b/lib/pixi/assets/maps/test/4/12/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/1.jpg rename to lib/pixi/assets/maps/test/4/12/1.jpg diff --git a/pixi/assets/maps/test/4/12/10.jpg b/lib/pixi/assets/maps/test/4/12/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/10.jpg rename to lib/pixi/assets/maps/test/4/12/10.jpg diff --git a/pixi/assets/maps/test/4/12/11.jpg b/lib/pixi/assets/maps/test/4/12/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/11.jpg rename to lib/pixi/assets/maps/test/4/12/11.jpg diff --git a/pixi/assets/maps/test/4/12/12.jpg b/lib/pixi/assets/maps/test/4/12/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/12.jpg rename to lib/pixi/assets/maps/test/4/12/12.jpg diff --git a/pixi/assets/maps/test/4/12/13.jpg b/lib/pixi/assets/maps/test/4/12/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/13.jpg rename to lib/pixi/assets/maps/test/4/12/13.jpg diff --git a/pixi/assets/maps/test/4/12/14.jpg b/lib/pixi/assets/maps/test/4/12/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/14.jpg rename to lib/pixi/assets/maps/test/4/12/14.jpg diff --git a/pixi/assets/maps/test/4/12/15.jpg b/lib/pixi/assets/maps/test/4/12/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/15.jpg rename to lib/pixi/assets/maps/test/4/12/15.jpg diff --git a/pixi/assets/maps/test/4/12/2.jpg b/lib/pixi/assets/maps/test/4/12/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/2.jpg rename to lib/pixi/assets/maps/test/4/12/2.jpg diff --git a/pixi/assets/maps/test/4/12/3.jpg b/lib/pixi/assets/maps/test/4/12/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/3.jpg rename to lib/pixi/assets/maps/test/4/12/3.jpg diff --git a/pixi/assets/maps/test/4/12/4.jpg b/lib/pixi/assets/maps/test/4/12/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/4.jpg rename to lib/pixi/assets/maps/test/4/12/4.jpg diff --git a/pixi/assets/maps/test/4/12/5.jpg b/lib/pixi/assets/maps/test/4/12/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/5.jpg rename to lib/pixi/assets/maps/test/4/12/5.jpg diff --git a/pixi/assets/maps/test/4/12/6.jpg b/lib/pixi/assets/maps/test/4/12/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/6.jpg rename to lib/pixi/assets/maps/test/4/12/6.jpg diff --git a/pixi/assets/maps/test/4/12/7.jpg b/lib/pixi/assets/maps/test/4/12/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/7.jpg rename to lib/pixi/assets/maps/test/4/12/7.jpg diff --git a/pixi/assets/maps/test/4/12/8.jpg b/lib/pixi/assets/maps/test/4/12/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/8.jpg rename to lib/pixi/assets/maps/test/4/12/8.jpg diff --git a/pixi/assets/maps/test/4/12/9.jpg b/lib/pixi/assets/maps/test/4/12/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/12/9.jpg rename to lib/pixi/assets/maps/test/4/12/9.jpg diff --git a/pixi/assets/maps/test/4/13/0.jpg b/lib/pixi/assets/maps/test/4/13/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/0.jpg rename to lib/pixi/assets/maps/test/4/13/0.jpg diff --git a/pixi/assets/maps/test/4/13/1.jpg b/lib/pixi/assets/maps/test/4/13/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/1.jpg rename to lib/pixi/assets/maps/test/4/13/1.jpg diff --git a/pixi/assets/maps/test/4/13/10.jpg b/lib/pixi/assets/maps/test/4/13/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/10.jpg rename to lib/pixi/assets/maps/test/4/13/10.jpg diff --git a/pixi/assets/maps/test/4/13/11.jpg b/lib/pixi/assets/maps/test/4/13/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/11.jpg rename to lib/pixi/assets/maps/test/4/13/11.jpg diff --git a/pixi/assets/maps/test/4/13/12.jpg b/lib/pixi/assets/maps/test/4/13/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/12.jpg rename to lib/pixi/assets/maps/test/4/13/12.jpg diff --git a/pixi/assets/maps/test/4/13/13.jpg b/lib/pixi/assets/maps/test/4/13/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/13.jpg rename to lib/pixi/assets/maps/test/4/13/13.jpg diff --git a/pixi/assets/maps/test/4/13/14.jpg b/lib/pixi/assets/maps/test/4/13/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/14.jpg rename to lib/pixi/assets/maps/test/4/13/14.jpg diff --git a/pixi/assets/maps/test/4/13/15.jpg b/lib/pixi/assets/maps/test/4/13/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/15.jpg rename to lib/pixi/assets/maps/test/4/13/15.jpg diff --git a/pixi/assets/maps/test/4/13/2.jpg b/lib/pixi/assets/maps/test/4/13/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/2.jpg rename to lib/pixi/assets/maps/test/4/13/2.jpg diff --git a/pixi/assets/maps/test/4/13/3.jpg b/lib/pixi/assets/maps/test/4/13/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/3.jpg rename to lib/pixi/assets/maps/test/4/13/3.jpg diff --git a/pixi/assets/maps/test/4/13/4.jpg b/lib/pixi/assets/maps/test/4/13/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/4.jpg rename to lib/pixi/assets/maps/test/4/13/4.jpg diff --git a/pixi/assets/maps/test/4/13/5.jpg b/lib/pixi/assets/maps/test/4/13/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/5.jpg rename to lib/pixi/assets/maps/test/4/13/5.jpg diff --git a/pixi/assets/maps/test/4/13/6.jpg b/lib/pixi/assets/maps/test/4/13/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/6.jpg rename to lib/pixi/assets/maps/test/4/13/6.jpg diff --git a/pixi/assets/maps/test/4/13/7.jpg b/lib/pixi/assets/maps/test/4/13/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/7.jpg rename to lib/pixi/assets/maps/test/4/13/7.jpg diff --git a/pixi/assets/maps/test/4/13/8.jpg b/lib/pixi/assets/maps/test/4/13/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/8.jpg rename to lib/pixi/assets/maps/test/4/13/8.jpg diff --git a/pixi/assets/maps/test/4/13/9.jpg b/lib/pixi/assets/maps/test/4/13/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/13/9.jpg rename to lib/pixi/assets/maps/test/4/13/9.jpg diff --git a/pixi/assets/maps/test/4/14/0.jpg b/lib/pixi/assets/maps/test/4/14/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/0.jpg rename to lib/pixi/assets/maps/test/4/14/0.jpg diff --git a/pixi/assets/maps/test/4/14/1.jpg b/lib/pixi/assets/maps/test/4/14/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/1.jpg rename to lib/pixi/assets/maps/test/4/14/1.jpg diff --git a/pixi/assets/maps/test/4/14/10.jpg b/lib/pixi/assets/maps/test/4/14/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/10.jpg rename to lib/pixi/assets/maps/test/4/14/10.jpg diff --git a/pixi/assets/maps/test/4/14/11.jpg b/lib/pixi/assets/maps/test/4/14/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/11.jpg rename to lib/pixi/assets/maps/test/4/14/11.jpg diff --git a/pixi/assets/maps/test/4/14/12.jpg b/lib/pixi/assets/maps/test/4/14/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/12.jpg rename to lib/pixi/assets/maps/test/4/14/12.jpg diff --git a/pixi/assets/maps/test/4/14/13.jpg b/lib/pixi/assets/maps/test/4/14/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/13.jpg rename to lib/pixi/assets/maps/test/4/14/13.jpg diff --git a/pixi/assets/maps/test/4/14/14.jpg b/lib/pixi/assets/maps/test/4/14/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/14.jpg rename to lib/pixi/assets/maps/test/4/14/14.jpg diff --git a/pixi/assets/maps/test/4/14/15.jpg b/lib/pixi/assets/maps/test/4/14/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/15.jpg rename to lib/pixi/assets/maps/test/4/14/15.jpg diff --git a/pixi/assets/maps/test/4/14/2.jpg b/lib/pixi/assets/maps/test/4/14/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/2.jpg rename to lib/pixi/assets/maps/test/4/14/2.jpg diff --git a/pixi/assets/maps/test/4/14/3.jpg b/lib/pixi/assets/maps/test/4/14/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/3.jpg rename to lib/pixi/assets/maps/test/4/14/3.jpg diff --git a/pixi/assets/maps/test/4/14/4.jpg b/lib/pixi/assets/maps/test/4/14/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/4.jpg rename to lib/pixi/assets/maps/test/4/14/4.jpg diff --git a/pixi/assets/maps/test/4/14/5.jpg b/lib/pixi/assets/maps/test/4/14/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/5.jpg rename to lib/pixi/assets/maps/test/4/14/5.jpg diff --git a/pixi/assets/maps/test/4/14/6.jpg b/lib/pixi/assets/maps/test/4/14/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/6.jpg rename to lib/pixi/assets/maps/test/4/14/6.jpg diff --git a/pixi/assets/maps/test/4/14/7.jpg b/lib/pixi/assets/maps/test/4/14/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/7.jpg rename to lib/pixi/assets/maps/test/4/14/7.jpg diff --git a/pixi/assets/maps/test/4/14/8.jpg b/lib/pixi/assets/maps/test/4/14/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/8.jpg rename to lib/pixi/assets/maps/test/4/14/8.jpg diff --git a/pixi/assets/maps/test/4/14/9.jpg b/lib/pixi/assets/maps/test/4/14/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/14/9.jpg rename to lib/pixi/assets/maps/test/4/14/9.jpg diff --git a/pixi/assets/maps/test/4/15/0.jpg b/lib/pixi/assets/maps/test/4/15/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/0.jpg rename to lib/pixi/assets/maps/test/4/15/0.jpg diff --git a/pixi/assets/maps/test/4/15/1.jpg b/lib/pixi/assets/maps/test/4/15/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/1.jpg rename to lib/pixi/assets/maps/test/4/15/1.jpg diff --git a/pixi/assets/maps/test/4/15/10.jpg b/lib/pixi/assets/maps/test/4/15/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/10.jpg rename to lib/pixi/assets/maps/test/4/15/10.jpg diff --git a/pixi/assets/maps/test/4/15/11.jpg b/lib/pixi/assets/maps/test/4/15/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/11.jpg rename to lib/pixi/assets/maps/test/4/15/11.jpg diff --git a/pixi/assets/maps/test/4/15/12.jpg b/lib/pixi/assets/maps/test/4/15/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/12.jpg rename to lib/pixi/assets/maps/test/4/15/12.jpg diff --git a/pixi/assets/maps/test/4/15/13.jpg b/lib/pixi/assets/maps/test/4/15/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/13.jpg rename to lib/pixi/assets/maps/test/4/15/13.jpg diff --git a/pixi/assets/maps/test/4/15/14.jpg b/lib/pixi/assets/maps/test/4/15/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/14.jpg rename to lib/pixi/assets/maps/test/4/15/14.jpg diff --git a/pixi/assets/maps/test/4/15/15.jpg b/lib/pixi/assets/maps/test/4/15/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/15.jpg rename to lib/pixi/assets/maps/test/4/15/15.jpg diff --git a/pixi/assets/maps/test/4/15/2.jpg b/lib/pixi/assets/maps/test/4/15/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/2.jpg rename to lib/pixi/assets/maps/test/4/15/2.jpg diff --git a/pixi/assets/maps/test/4/15/3.jpg b/lib/pixi/assets/maps/test/4/15/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/3.jpg rename to lib/pixi/assets/maps/test/4/15/3.jpg diff --git a/pixi/assets/maps/test/4/15/4.jpg b/lib/pixi/assets/maps/test/4/15/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/4.jpg rename to lib/pixi/assets/maps/test/4/15/4.jpg diff --git a/pixi/assets/maps/test/4/15/5.jpg b/lib/pixi/assets/maps/test/4/15/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/5.jpg rename to lib/pixi/assets/maps/test/4/15/5.jpg diff --git a/pixi/assets/maps/test/4/15/6.jpg b/lib/pixi/assets/maps/test/4/15/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/6.jpg rename to lib/pixi/assets/maps/test/4/15/6.jpg diff --git a/pixi/assets/maps/test/4/15/7.jpg b/lib/pixi/assets/maps/test/4/15/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/7.jpg rename to lib/pixi/assets/maps/test/4/15/7.jpg diff --git a/pixi/assets/maps/test/4/15/8.jpg b/lib/pixi/assets/maps/test/4/15/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/8.jpg rename to lib/pixi/assets/maps/test/4/15/8.jpg diff --git a/pixi/assets/maps/test/4/15/9.jpg b/lib/pixi/assets/maps/test/4/15/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/15/9.jpg rename to lib/pixi/assets/maps/test/4/15/9.jpg diff --git a/pixi/assets/maps/test/4/2/0.jpg b/lib/pixi/assets/maps/test/4/2/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/0.jpg rename to lib/pixi/assets/maps/test/4/2/0.jpg diff --git a/pixi/assets/maps/test/4/2/1.jpg b/lib/pixi/assets/maps/test/4/2/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/1.jpg rename to lib/pixi/assets/maps/test/4/2/1.jpg diff --git a/pixi/assets/maps/test/4/2/10.jpg b/lib/pixi/assets/maps/test/4/2/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/10.jpg rename to lib/pixi/assets/maps/test/4/2/10.jpg diff --git a/pixi/assets/maps/test/4/2/11.jpg b/lib/pixi/assets/maps/test/4/2/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/11.jpg rename to lib/pixi/assets/maps/test/4/2/11.jpg diff --git a/pixi/assets/maps/test/4/2/12.jpg b/lib/pixi/assets/maps/test/4/2/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/12.jpg rename to lib/pixi/assets/maps/test/4/2/12.jpg diff --git a/pixi/assets/maps/test/4/2/13.jpg b/lib/pixi/assets/maps/test/4/2/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/13.jpg rename to lib/pixi/assets/maps/test/4/2/13.jpg diff --git a/pixi/assets/maps/test/4/2/14.jpg b/lib/pixi/assets/maps/test/4/2/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/14.jpg rename to lib/pixi/assets/maps/test/4/2/14.jpg diff --git a/pixi/assets/maps/test/4/2/15.jpg b/lib/pixi/assets/maps/test/4/2/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/15.jpg rename to lib/pixi/assets/maps/test/4/2/15.jpg diff --git a/pixi/assets/maps/test/4/2/2.jpg b/lib/pixi/assets/maps/test/4/2/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/2.jpg rename to lib/pixi/assets/maps/test/4/2/2.jpg diff --git a/pixi/assets/maps/test/4/2/3.jpg b/lib/pixi/assets/maps/test/4/2/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/3.jpg rename to lib/pixi/assets/maps/test/4/2/3.jpg diff --git a/pixi/assets/maps/test/4/2/4.jpg b/lib/pixi/assets/maps/test/4/2/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/4.jpg rename to lib/pixi/assets/maps/test/4/2/4.jpg diff --git a/pixi/assets/maps/test/4/2/5.jpg b/lib/pixi/assets/maps/test/4/2/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/5.jpg rename to lib/pixi/assets/maps/test/4/2/5.jpg diff --git a/pixi/assets/maps/test/4/2/6.jpg b/lib/pixi/assets/maps/test/4/2/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/6.jpg rename to lib/pixi/assets/maps/test/4/2/6.jpg diff --git a/pixi/assets/maps/test/4/2/7.jpg b/lib/pixi/assets/maps/test/4/2/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/7.jpg rename to lib/pixi/assets/maps/test/4/2/7.jpg diff --git a/pixi/assets/maps/test/4/2/8.jpg b/lib/pixi/assets/maps/test/4/2/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/8.jpg rename to lib/pixi/assets/maps/test/4/2/8.jpg diff --git a/pixi/assets/maps/test/4/2/9.jpg b/lib/pixi/assets/maps/test/4/2/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/2/9.jpg rename to lib/pixi/assets/maps/test/4/2/9.jpg diff --git a/pixi/assets/maps/test/4/3/0.jpg b/lib/pixi/assets/maps/test/4/3/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/0.jpg rename to lib/pixi/assets/maps/test/4/3/0.jpg diff --git a/pixi/assets/maps/test/4/3/1.jpg b/lib/pixi/assets/maps/test/4/3/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/1.jpg rename to lib/pixi/assets/maps/test/4/3/1.jpg diff --git a/pixi/assets/maps/test/4/3/10.jpg b/lib/pixi/assets/maps/test/4/3/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/10.jpg rename to lib/pixi/assets/maps/test/4/3/10.jpg diff --git a/pixi/assets/maps/test/4/3/11.jpg b/lib/pixi/assets/maps/test/4/3/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/11.jpg rename to lib/pixi/assets/maps/test/4/3/11.jpg diff --git a/pixi/assets/maps/test/4/3/12.jpg b/lib/pixi/assets/maps/test/4/3/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/12.jpg rename to lib/pixi/assets/maps/test/4/3/12.jpg diff --git a/pixi/assets/maps/test/4/3/13.jpg b/lib/pixi/assets/maps/test/4/3/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/13.jpg rename to lib/pixi/assets/maps/test/4/3/13.jpg diff --git a/pixi/assets/maps/test/4/3/14.jpg b/lib/pixi/assets/maps/test/4/3/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/14.jpg rename to lib/pixi/assets/maps/test/4/3/14.jpg diff --git a/pixi/assets/maps/test/4/3/15.jpg b/lib/pixi/assets/maps/test/4/3/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/15.jpg rename to lib/pixi/assets/maps/test/4/3/15.jpg diff --git a/pixi/assets/maps/test/4/3/2.jpg b/lib/pixi/assets/maps/test/4/3/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/2.jpg rename to lib/pixi/assets/maps/test/4/3/2.jpg diff --git a/pixi/assets/maps/test/4/3/3.jpg b/lib/pixi/assets/maps/test/4/3/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/3.jpg rename to lib/pixi/assets/maps/test/4/3/3.jpg diff --git a/pixi/assets/maps/test/4/3/4.jpg b/lib/pixi/assets/maps/test/4/3/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/4.jpg rename to lib/pixi/assets/maps/test/4/3/4.jpg diff --git a/pixi/assets/maps/test/4/3/5.jpg b/lib/pixi/assets/maps/test/4/3/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/5.jpg rename to lib/pixi/assets/maps/test/4/3/5.jpg diff --git a/pixi/assets/maps/test/4/3/6.jpg b/lib/pixi/assets/maps/test/4/3/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/6.jpg rename to lib/pixi/assets/maps/test/4/3/6.jpg diff --git a/pixi/assets/maps/test/4/3/7.jpg b/lib/pixi/assets/maps/test/4/3/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/7.jpg rename to lib/pixi/assets/maps/test/4/3/7.jpg diff --git a/pixi/assets/maps/test/4/3/8.jpg b/lib/pixi/assets/maps/test/4/3/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/8.jpg rename to lib/pixi/assets/maps/test/4/3/8.jpg diff --git a/pixi/assets/maps/test/4/3/9.jpg b/lib/pixi/assets/maps/test/4/3/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/3/9.jpg rename to lib/pixi/assets/maps/test/4/3/9.jpg diff --git a/pixi/assets/maps/test/4/4/0.jpg b/lib/pixi/assets/maps/test/4/4/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/0.jpg rename to lib/pixi/assets/maps/test/4/4/0.jpg diff --git a/pixi/assets/maps/test/4/4/1.jpg b/lib/pixi/assets/maps/test/4/4/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/1.jpg rename to lib/pixi/assets/maps/test/4/4/1.jpg diff --git a/pixi/assets/maps/test/4/4/10.jpg b/lib/pixi/assets/maps/test/4/4/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/10.jpg rename to lib/pixi/assets/maps/test/4/4/10.jpg diff --git a/pixi/assets/maps/test/4/4/11.jpg b/lib/pixi/assets/maps/test/4/4/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/11.jpg rename to lib/pixi/assets/maps/test/4/4/11.jpg diff --git a/pixi/assets/maps/test/4/4/12.jpg b/lib/pixi/assets/maps/test/4/4/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/12.jpg rename to lib/pixi/assets/maps/test/4/4/12.jpg diff --git a/pixi/assets/maps/test/4/4/13.jpg b/lib/pixi/assets/maps/test/4/4/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/13.jpg rename to lib/pixi/assets/maps/test/4/4/13.jpg diff --git a/pixi/assets/maps/test/4/4/14.jpg b/lib/pixi/assets/maps/test/4/4/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/14.jpg rename to lib/pixi/assets/maps/test/4/4/14.jpg diff --git a/pixi/assets/maps/test/4/4/15.jpg b/lib/pixi/assets/maps/test/4/4/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/15.jpg rename to lib/pixi/assets/maps/test/4/4/15.jpg diff --git a/pixi/assets/maps/test/4/4/2.jpg b/lib/pixi/assets/maps/test/4/4/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/2.jpg rename to lib/pixi/assets/maps/test/4/4/2.jpg diff --git a/pixi/assets/maps/test/4/4/3.jpg b/lib/pixi/assets/maps/test/4/4/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/3.jpg rename to lib/pixi/assets/maps/test/4/4/3.jpg diff --git a/pixi/assets/maps/test/4/4/4.jpg b/lib/pixi/assets/maps/test/4/4/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/4.jpg rename to lib/pixi/assets/maps/test/4/4/4.jpg diff --git a/pixi/assets/maps/test/4/4/5.jpg b/lib/pixi/assets/maps/test/4/4/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/5.jpg rename to lib/pixi/assets/maps/test/4/4/5.jpg diff --git a/pixi/assets/maps/test/4/4/6.jpg b/lib/pixi/assets/maps/test/4/4/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/6.jpg rename to lib/pixi/assets/maps/test/4/4/6.jpg diff --git a/pixi/assets/maps/test/4/4/7.jpg b/lib/pixi/assets/maps/test/4/4/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/7.jpg rename to lib/pixi/assets/maps/test/4/4/7.jpg diff --git a/pixi/assets/maps/test/4/4/8.jpg b/lib/pixi/assets/maps/test/4/4/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/8.jpg rename to lib/pixi/assets/maps/test/4/4/8.jpg diff --git a/pixi/assets/maps/test/4/4/9.jpg b/lib/pixi/assets/maps/test/4/4/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/4/9.jpg rename to lib/pixi/assets/maps/test/4/4/9.jpg diff --git a/pixi/assets/maps/test/4/5/0.jpg b/lib/pixi/assets/maps/test/4/5/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/0.jpg rename to lib/pixi/assets/maps/test/4/5/0.jpg diff --git a/pixi/assets/maps/test/4/5/1.jpg b/lib/pixi/assets/maps/test/4/5/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/1.jpg rename to lib/pixi/assets/maps/test/4/5/1.jpg diff --git a/pixi/assets/maps/test/4/5/10.jpg b/lib/pixi/assets/maps/test/4/5/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/10.jpg rename to lib/pixi/assets/maps/test/4/5/10.jpg diff --git a/pixi/assets/maps/test/4/5/11.jpg b/lib/pixi/assets/maps/test/4/5/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/11.jpg rename to lib/pixi/assets/maps/test/4/5/11.jpg diff --git a/pixi/assets/maps/test/4/5/12.jpg b/lib/pixi/assets/maps/test/4/5/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/12.jpg rename to lib/pixi/assets/maps/test/4/5/12.jpg diff --git a/pixi/assets/maps/test/4/5/13.jpg b/lib/pixi/assets/maps/test/4/5/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/13.jpg rename to lib/pixi/assets/maps/test/4/5/13.jpg diff --git a/pixi/assets/maps/test/4/5/14.jpg b/lib/pixi/assets/maps/test/4/5/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/14.jpg rename to lib/pixi/assets/maps/test/4/5/14.jpg diff --git a/pixi/assets/maps/test/4/5/15.jpg b/lib/pixi/assets/maps/test/4/5/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/15.jpg rename to lib/pixi/assets/maps/test/4/5/15.jpg diff --git a/pixi/assets/maps/test/4/5/2.jpg b/lib/pixi/assets/maps/test/4/5/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/2.jpg rename to lib/pixi/assets/maps/test/4/5/2.jpg diff --git a/pixi/assets/maps/test/4/5/3.jpg b/lib/pixi/assets/maps/test/4/5/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/3.jpg rename to lib/pixi/assets/maps/test/4/5/3.jpg diff --git a/pixi/assets/maps/test/4/5/4.jpg b/lib/pixi/assets/maps/test/4/5/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/4.jpg rename to lib/pixi/assets/maps/test/4/5/4.jpg diff --git a/pixi/assets/maps/test/4/5/5.jpg b/lib/pixi/assets/maps/test/4/5/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/5.jpg rename to lib/pixi/assets/maps/test/4/5/5.jpg diff --git a/pixi/assets/maps/test/4/5/6.jpg b/lib/pixi/assets/maps/test/4/5/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/6.jpg rename to lib/pixi/assets/maps/test/4/5/6.jpg diff --git a/pixi/assets/maps/test/4/5/7.jpg b/lib/pixi/assets/maps/test/4/5/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/7.jpg rename to lib/pixi/assets/maps/test/4/5/7.jpg diff --git a/pixi/assets/maps/test/4/5/8.jpg b/lib/pixi/assets/maps/test/4/5/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/8.jpg rename to lib/pixi/assets/maps/test/4/5/8.jpg diff --git a/pixi/assets/maps/test/4/5/9.jpg b/lib/pixi/assets/maps/test/4/5/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/5/9.jpg rename to lib/pixi/assets/maps/test/4/5/9.jpg diff --git a/pixi/assets/maps/test/4/6/0.jpg b/lib/pixi/assets/maps/test/4/6/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/0.jpg rename to lib/pixi/assets/maps/test/4/6/0.jpg diff --git a/pixi/assets/maps/test/4/6/1.jpg b/lib/pixi/assets/maps/test/4/6/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/1.jpg rename to lib/pixi/assets/maps/test/4/6/1.jpg diff --git a/pixi/assets/maps/test/4/6/10.jpg b/lib/pixi/assets/maps/test/4/6/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/10.jpg rename to lib/pixi/assets/maps/test/4/6/10.jpg diff --git a/pixi/assets/maps/test/4/6/11.jpg b/lib/pixi/assets/maps/test/4/6/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/11.jpg rename to lib/pixi/assets/maps/test/4/6/11.jpg diff --git a/pixi/assets/maps/test/4/6/12.jpg b/lib/pixi/assets/maps/test/4/6/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/12.jpg rename to lib/pixi/assets/maps/test/4/6/12.jpg diff --git a/pixi/assets/maps/test/4/6/13.jpg b/lib/pixi/assets/maps/test/4/6/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/13.jpg rename to lib/pixi/assets/maps/test/4/6/13.jpg diff --git a/pixi/assets/maps/test/4/6/14.jpg b/lib/pixi/assets/maps/test/4/6/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/14.jpg rename to lib/pixi/assets/maps/test/4/6/14.jpg diff --git a/pixi/assets/maps/test/4/6/15.jpg b/lib/pixi/assets/maps/test/4/6/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/15.jpg rename to lib/pixi/assets/maps/test/4/6/15.jpg diff --git a/pixi/assets/maps/test/4/6/2.jpg b/lib/pixi/assets/maps/test/4/6/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/2.jpg rename to lib/pixi/assets/maps/test/4/6/2.jpg diff --git a/pixi/assets/maps/test/4/6/3.jpg b/lib/pixi/assets/maps/test/4/6/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/3.jpg rename to lib/pixi/assets/maps/test/4/6/3.jpg diff --git a/pixi/assets/maps/test/4/6/4.jpg b/lib/pixi/assets/maps/test/4/6/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/4.jpg rename to lib/pixi/assets/maps/test/4/6/4.jpg diff --git a/pixi/assets/maps/test/4/6/5.jpg b/lib/pixi/assets/maps/test/4/6/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/5.jpg rename to lib/pixi/assets/maps/test/4/6/5.jpg diff --git a/pixi/assets/maps/test/4/6/6.jpg b/lib/pixi/assets/maps/test/4/6/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/6.jpg rename to lib/pixi/assets/maps/test/4/6/6.jpg diff --git a/pixi/assets/maps/test/4/6/7.jpg b/lib/pixi/assets/maps/test/4/6/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/7.jpg rename to lib/pixi/assets/maps/test/4/6/7.jpg diff --git a/pixi/assets/maps/test/4/6/8.jpg b/lib/pixi/assets/maps/test/4/6/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/8.jpg rename to lib/pixi/assets/maps/test/4/6/8.jpg diff --git a/pixi/assets/maps/test/4/6/9.jpg b/lib/pixi/assets/maps/test/4/6/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/6/9.jpg rename to lib/pixi/assets/maps/test/4/6/9.jpg diff --git a/pixi/assets/maps/test/4/7/0.jpg b/lib/pixi/assets/maps/test/4/7/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/0.jpg rename to lib/pixi/assets/maps/test/4/7/0.jpg diff --git a/pixi/assets/maps/test/4/7/1.jpg b/lib/pixi/assets/maps/test/4/7/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/1.jpg rename to lib/pixi/assets/maps/test/4/7/1.jpg diff --git a/pixi/assets/maps/test/4/7/10.jpg b/lib/pixi/assets/maps/test/4/7/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/10.jpg rename to lib/pixi/assets/maps/test/4/7/10.jpg diff --git a/pixi/assets/maps/test/4/7/11.jpg b/lib/pixi/assets/maps/test/4/7/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/11.jpg rename to lib/pixi/assets/maps/test/4/7/11.jpg diff --git a/pixi/assets/maps/test/4/7/12.jpg b/lib/pixi/assets/maps/test/4/7/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/12.jpg rename to lib/pixi/assets/maps/test/4/7/12.jpg diff --git a/pixi/assets/maps/test/4/7/13.jpg b/lib/pixi/assets/maps/test/4/7/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/13.jpg rename to lib/pixi/assets/maps/test/4/7/13.jpg diff --git a/pixi/assets/maps/test/4/7/14.jpg b/lib/pixi/assets/maps/test/4/7/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/14.jpg rename to lib/pixi/assets/maps/test/4/7/14.jpg diff --git a/pixi/assets/maps/test/4/7/15.jpg b/lib/pixi/assets/maps/test/4/7/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/15.jpg rename to lib/pixi/assets/maps/test/4/7/15.jpg diff --git a/pixi/assets/maps/test/4/7/2.jpg b/lib/pixi/assets/maps/test/4/7/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/2.jpg rename to lib/pixi/assets/maps/test/4/7/2.jpg diff --git a/pixi/assets/maps/test/4/7/3.jpg b/lib/pixi/assets/maps/test/4/7/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/3.jpg rename to lib/pixi/assets/maps/test/4/7/3.jpg diff --git a/pixi/assets/maps/test/4/7/4.jpg b/lib/pixi/assets/maps/test/4/7/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/4.jpg rename to lib/pixi/assets/maps/test/4/7/4.jpg diff --git a/pixi/assets/maps/test/4/7/5.jpg b/lib/pixi/assets/maps/test/4/7/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/5.jpg rename to lib/pixi/assets/maps/test/4/7/5.jpg diff --git a/pixi/assets/maps/test/4/7/6.jpg b/lib/pixi/assets/maps/test/4/7/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/6.jpg rename to lib/pixi/assets/maps/test/4/7/6.jpg diff --git a/pixi/assets/maps/test/4/7/7.jpg b/lib/pixi/assets/maps/test/4/7/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/7.jpg rename to lib/pixi/assets/maps/test/4/7/7.jpg diff --git a/pixi/assets/maps/test/4/7/8.jpg b/lib/pixi/assets/maps/test/4/7/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/8.jpg rename to lib/pixi/assets/maps/test/4/7/8.jpg diff --git a/pixi/assets/maps/test/4/7/9.jpg b/lib/pixi/assets/maps/test/4/7/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/7/9.jpg rename to lib/pixi/assets/maps/test/4/7/9.jpg diff --git a/pixi/assets/maps/test/4/8/0.jpg b/lib/pixi/assets/maps/test/4/8/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/0.jpg rename to lib/pixi/assets/maps/test/4/8/0.jpg diff --git a/pixi/assets/maps/test/4/8/1.jpg b/lib/pixi/assets/maps/test/4/8/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/1.jpg rename to lib/pixi/assets/maps/test/4/8/1.jpg diff --git a/pixi/assets/maps/test/4/8/10.jpg b/lib/pixi/assets/maps/test/4/8/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/10.jpg rename to lib/pixi/assets/maps/test/4/8/10.jpg diff --git a/pixi/assets/maps/test/4/8/11.jpg b/lib/pixi/assets/maps/test/4/8/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/11.jpg rename to lib/pixi/assets/maps/test/4/8/11.jpg diff --git a/pixi/assets/maps/test/4/8/12.jpg b/lib/pixi/assets/maps/test/4/8/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/12.jpg rename to lib/pixi/assets/maps/test/4/8/12.jpg diff --git a/pixi/assets/maps/test/4/8/13.jpg b/lib/pixi/assets/maps/test/4/8/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/13.jpg rename to lib/pixi/assets/maps/test/4/8/13.jpg diff --git a/pixi/assets/maps/test/4/8/14.jpg b/lib/pixi/assets/maps/test/4/8/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/14.jpg rename to lib/pixi/assets/maps/test/4/8/14.jpg diff --git a/pixi/assets/maps/test/4/8/15.jpg b/lib/pixi/assets/maps/test/4/8/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/15.jpg rename to lib/pixi/assets/maps/test/4/8/15.jpg diff --git a/pixi/assets/maps/test/4/8/2.jpg b/lib/pixi/assets/maps/test/4/8/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/2.jpg rename to lib/pixi/assets/maps/test/4/8/2.jpg diff --git a/pixi/assets/maps/test/4/8/3.jpg b/lib/pixi/assets/maps/test/4/8/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/3.jpg rename to lib/pixi/assets/maps/test/4/8/3.jpg diff --git a/pixi/assets/maps/test/4/8/4.jpg b/lib/pixi/assets/maps/test/4/8/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/4.jpg rename to lib/pixi/assets/maps/test/4/8/4.jpg diff --git a/pixi/assets/maps/test/4/8/5.jpg b/lib/pixi/assets/maps/test/4/8/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/5.jpg rename to lib/pixi/assets/maps/test/4/8/5.jpg diff --git a/pixi/assets/maps/test/4/8/6.jpg b/lib/pixi/assets/maps/test/4/8/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/6.jpg rename to lib/pixi/assets/maps/test/4/8/6.jpg diff --git a/pixi/assets/maps/test/4/8/7.jpg b/lib/pixi/assets/maps/test/4/8/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/7.jpg rename to lib/pixi/assets/maps/test/4/8/7.jpg diff --git a/pixi/assets/maps/test/4/8/8.jpg b/lib/pixi/assets/maps/test/4/8/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/8.jpg rename to lib/pixi/assets/maps/test/4/8/8.jpg diff --git a/pixi/assets/maps/test/4/8/9.jpg b/lib/pixi/assets/maps/test/4/8/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/8/9.jpg rename to lib/pixi/assets/maps/test/4/8/9.jpg diff --git a/pixi/assets/maps/test/4/9/0.jpg b/lib/pixi/assets/maps/test/4/9/0.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/0.jpg rename to lib/pixi/assets/maps/test/4/9/0.jpg diff --git a/pixi/assets/maps/test/4/9/1.jpg b/lib/pixi/assets/maps/test/4/9/1.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/1.jpg rename to lib/pixi/assets/maps/test/4/9/1.jpg diff --git a/pixi/assets/maps/test/4/9/10.jpg b/lib/pixi/assets/maps/test/4/9/10.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/10.jpg rename to lib/pixi/assets/maps/test/4/9/10.jpg diff --git a/pixi/assets/maps/test/4/9/11.jpg b/lib/pixi/assets/maps/test/4/9/11.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/11.jpg rename to lib/pixi/assets/maps/test/4/9/11.jpg diff --git a/pixi/assets/maps/test/4/9/12.jpg b/lib/pixi/assets/maps/test/4/9/12.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/12.jpg rename to lib/pixi/assets/maps/test/4/9/12.jpg diff --git a/pixi/assets/maps/test/4/9/13.jpg b/lib/pixi/assets/maps/test/4/9/13.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/13.jpg rename to lib/pixi/assets/maps/test/4/9/13.jpg diff --git a/pixi/assets/maps/test/4/9/14.jpg b/lib/pixi/assets/maps/test/4/9/14.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/14.jpg rename to lib/pixi/assets/maps/test/4/9/14.jpg diff --git a/pixi/assets/maps/test/4/9/15.jpg b/lib/pixi/assets/maps/test/4/9/15.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/15.jpg rename to lib/pixi/assets/maps/test/4/9/15.jpg diff --git a/pixi/assets/maps/test/4/9/2.jpg b/lib/pixi/assets/maps/test/4/9/2.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/2.jpg rename to lib/pixi/assets/maps/test/4/9/2.jpg diff --git a/pixi/assets/maps/test/4/9/3.jpg b/lib/pixi/assets/maps/test/4/9/3.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/3.jpg rename to lib/pixi/assets/maps/test/4/9/3.jpg diff --git a/pixi/assets/maps/test/4/9/4.jpg b/lib/pixi/assets/maps/test/4/9/4.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/4.jpg rename to lib/pixi/assets/maps/test/4/9/4.jpg diff --git a/pixi/assets/maps/test/4/9/5.jpg b/lib/pixi/assets/maps/test/4/9/5.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/5.jpg rename to lib/pixi/assets/maps/test/4/9/5.jpg diff --git a/pixi/assets/maps/test/4/9/6.jpg b/lib/pixi/assets/maps/test/4/9/6.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/6.jpg rename to lib/pixi/assets/maps/test/4/9/6.jpg diff --git a/pixi/assets/maps/test/4/9/7.jpg b/lib/pixi/assets/maps/test/4/9/7.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/7.jpg rename to lib/pixi/assets/maps/test/4/9/7.jpg diff --git a/pixi/assets/maps/test/4/9/8.jpg b/lib/pixi/assets/maps/test/4/9/8.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/8.jpg rename to lib/pixi/assets/maps/test/4/9/8.jpg diff --git a/pixi/assets/maps/test/4/9/9.jpg b/lib/pixi/assets/maps/test/4/9/9.jpg similarity index 100% rename from pixi/assets/maps/test/4/9/9.jpg rename to lib/pixi/assets/maps/test/4/9/9.jpg diff --git a/pixi/assets/maps/test/5/0/0.jpg b/lib/pixi/assets/maps/test/5/0/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/0.jpg rename to lib/pixi/assets/maps/test/5/0/0.jpg diff --git a/pixi/assets/maps/test/5/0/1.jpg b/lib/pixi/assets/maps/test/5/0/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/1.jpg rename to lib/pixi/assets/maps/test/5/0/1.jpg diff --git a/pixi/assets/maps/test/5/0/10.jpg b/lib/pixi/assets/maps/test/5/0/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/10.jpg rename to lib/pixi/assets/maps/test/5/0/10.jpg diff --git a/pixi/assets/maps/test/5/0/11.jpg b/lib/pixi/assets/maps/test/5/0/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/11.jpg rename to lib/pixi/assets/maps/test/5/0/11.jpg diff --git a/pixi/assets/maps/test/5/0/12.jpg b/lib/pixi/assets/maps/test/5/0/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/12.jpg rename to lib/pixi/assets/maps/test/5/0/12.jpg diff --git a/pixi/assets/maps/test/5/0/13.jpg b/lib/pixi/assets/maps/test/5/0/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/13.jpg rename to lib/pixi/assets/maps/test/5/0/13.jpg diff --git a/pixi/assets/maps/test/5/0/14.jpg b/lib/pixi/assets/maps/test/5/0/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/14.jpg rename to lib/pixi/assets/maps/test/5/0/14.jpg diff --git a/pixi/assets/maps/test/5/0/15.jpg b/lib/pixi/assets/maps/test/5/0/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/15.jpg rename to lib/pixi/assets/maps/test/5/0/15.jpg diff --git a/pixi/assets/maps/test/5/0/16.jpg b/lib/pixi/assets/maps/test/5/0/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/16.jpg rename to lib/pixi/assets/maps/test/5/0/16.jpg diff --git a/pixi/assets/maps/test/5/0/17.jpg b/lib/pixi/assets/maps/test/5/0/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/17.jpg rename to lib/pixi/assets/maps/test/5/0/17.jpg diff --git a/pixi/assets/maps/test/5/0/18.jpg b/lib/pixi/assets/maps/test/5/0/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/18.jpg rename to lib/pixi/assets/maps/test/5/0/18.jpg diff --git a/pixi/assets/maps/test/5/0/19.jpg b/lib/pixi/assets/maps/test/5/0/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/19.jpg rename to lib/pixi/assets/maps/test/5/0/19.jpg diff --git a/pixi/assets/maps/test/5/0/2.jpg b/lib/pixi/assets/maps/test/5/0/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/2.jpg rename to lib/pixi/assets/maps/test/5/0/2.jpg diff --git a/pixi/assets/maps/test/5/0/20.jpg b/lib/pixi/assets/maps/test/5/0/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/20.jpg rename to lib/pixi/assets/maps/test/5/0/20.jpg diff --git a/pixi/assets/maps/test/5/0/21.jpg b/lib/pixi/assets/maps/test/5/0/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/21.jpg rename to lib/pixi/assets/maps/test/5/0/21.jpg diff --git a/pixi/assets/maps/test/5/0/22.jpg b/lib/pixi/assets/maps/test/5/0/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/22.jpg rename to lib/pixi/assets/maps/test/5/0/22.jpg diff --git a/pixi/assets/maps/test/5/0/23.jpg b/lib/pixi/assets/maps/test/5/0/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/23.jpg rename to lib/pixi/assets/maps/test/5/0/23.jpg diff --git a/pixi/assets/maps/test/5/0/24.jpg b/lib/pixi/assets/maps/test/5/0/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/24.jpg rename to lib/pixi/assets/maps/test/5/0/24.jpg diff --git a/pixi/assets/maps/test/5/0/25.jpg b/lib/pixi/assets/maps/test/5/0/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/25.jpg rename to lib/pixi/assets/maps/test/5/0/25.jpg diff --git a/pixi/assets/maps/test/5/0/26.jpg b/lib/pixi/assets/maps/test/5/0/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/26.jpg rename to lib/pixi/assets/maps/test/5/0/26.jpg diff --git a/pixi/assets/maps/test/5/0/27.jpg b/lib/pixi/assets/maps/test/5/0/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/27.jpg rename to lib/pixi/assets/maps/test/5/0/27.jpg diff --git a/pixi/assets/maps/test/5/0/28.jpg b/lib/pixi/assets/maps/test/5/0/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/28.jpg rename to lib/pixi/assets/maps/test/5/0/28.jpg diff --git a/pixi/assets/maps/test/5/0/29.jpg b/lib/pixi/assets/maps/test/5/0/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/29.jpg rename to lib/pixi/assets/maps/test/5/0/29.jpg diff --git a/pixi/assets/maps/test/5/0/3.jpg b/lib/pixi/assets/maps/test/5/0/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/3.jpg rename to lib/pixi/assets/maps/test/5/0/3.jpg diff --git a/pixi/assets/maps/test/5/0/30.jpg b/lib/pixi/assets/maps/test/5/0/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/30.jpg rename to lib/pixi/assets/maps/test/5/0/30.jpg diff --git a/pixi/assets/maps/test/5/0/31.jpg b/lib/pixi/assets/maps/test/5/0/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/31.jpg rename to lib/pixi/assets/maps/test/5/0/31.jpg diff --git a/pixi/assets/maps/test/5/0/4.jpg b/lib/pixi/assets/maps/test/5/0/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/4.jpg rename to lib/pixi/assets/maps/test/5/0/4.jpg diff --git a/pixi/assets/maps/test/5/0/5.jpg b/lib/pixi/assets/maps/test/5/0/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/5.jpg rename to lib/pixi/assets/maps/test/5/0/5.jpg diff --git a/pixi/assets/maps/test/5/0/6.jpg b/lib/pixi/assets/maps/test/5/0/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/6.jpg rename to lib/pixi/assets/maps/test/5/0/6.jpg diff --git a/pixi/assets/maps/test/5/0/7.jpg b/lib/pixi/assets/maps/test/5/0/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/7.jpg rename to lib/pixi/assets/maps/test/5/0/7.jpg diff --git a/pixi/assets/maps/test/5/0/8.jpg b/lib/pixi/assets/maps/test/5/0/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/8.jpg rename to lib/pixi/assets/maps/test/5/0/8.jpg diff --git a/pixi/assets/maps/test/5/0/9.jpg b/lib/pixi/assets/maps/test/5/0/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/0/9.jpg rename to lib/pixi/assets/maps/test/5/0/9.jpg diff --git a/pixi/assets/maps/test/5/1/0.jpg b/lib/pixi/assets/maps/test/5/1/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/0.jpg rename to lib/pixi/assets/maps/test/5/1/0.jpg diff --git a/pixi/assets/maps/test/5/1/1.jpg b/lib/pixi/assets/maps/test/5/1/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/1.jpg rename to lib/pixi/assets/maps/test/5/1/1.jpg diff --git a/pixi/assets/maps/test/5/1/10.jpg b/lib/pixi/assets/maps/test/5/1/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/10.jpg rename to lib/pixi/assets/maps/test/5/1/10.jpg diff --git a/pixi/assets/maps/test/5/1/11.jpg b/lib/pixi/assets/maps/test/5/1/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/11.jpg rename to lib/pixi/assets/maps/test/5/1/11.jpg diff --git a/pixi/assets/maps/test/5/1/12.jpg b/lib/pixi/assets/maps/test/5/1/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/12.jpg rename to lib/pixi/assets/maps/test/5/1/12.jpg diff --git a/pixi/assets/maps/test/5/1/13.jpg b/lib/pixi/assets/maps/test/5/1/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/13.jpg rename to lib/pixi/assets/maps/test/5/1/13.jpg diff --git a/pixi/assets/maps/test/5/1/14.jpg b/lib/pixi/assets/maps/test/5/1/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/14.jpg rename to lib/pixi/assets/maps/test/5/1/14.jpg diff --git a/pixi/assets/maps/test/5/1/15.jpg b/lib/pixi/assets/maps/test/5/1/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/15.jpg rename to lib/pixi/assets/maps/test/5/1/15.jpg diff --git a/pixi/assets/maps/test/5/1/16.jpg b/lib/pixi/assets/maps/test/5/1/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/16.jpg rename to lib/pixi/assets/maps/test/5/1/16.jpg diff --git a/pixi/assets/maps/test/5/1/17.jpg b/lib/pixi/assets/maps/test/5/1/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/17.jpg rename to lib/pixi/assets/maps/test/5/1/17.jpg diff --git a/pixi/assets/maps/test/5/1/18.jpg b/lib/pixi/assets/maps/test/5/1/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/18.jpg rename to lib/pixi/assets/maps/test/5/1/18.jpg diff --git a/pixi/assets/maps/test/5/1/19.jpg b/lib/pixi/assets/maps/test/5/1/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/19.jpg rename to lib/pixi/assets/maps/test/5/1/19.jpg diff --git a/pixi/assets/maps/test/5/1/2.jpg b/lib/pixi/assets/maps/test/5/1/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/2.jpg rename to lib/pixi/assets/maps/test/5/1/2.jpg diff --git a/pixi/assets/maps/test/5/1/20.jpg b/lib/pixi/assets/maps/test/5/1/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/20.jpg rename to lib/pixi/assets/maps/test/5/1/20.jpg diff --git a/pixi/assets/maps/test/5/1/21.jpg b/lib/pixi/assets/maps/test/5/1/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/21.jpg rename to lib/pixi/assets/maps/test/5/1/21.jpg diff --git a/pixi/assets/maps/test/5/1/22.jpg b/lib/pixi/assets/maps/test/5/1/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/22.jpg rename to lib/pixi/assets/maps/test/5/1/22.jpg diff --git a/pixi/assets/maps/test/5/1/23.jpg b/lib/pixi/assets/maps/test/5/1/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/23.jpg rename to lib/pixi/assets/maps/test/5/1/23.jpg diff --git a/pixi/assets/maps/test/5/1/24.jpg b/lib/pixi/assets/maps/test/5/1/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/24.jpg rename to lib/pixi/assets/maps/test/5/1/24.jpg diff --git a/pixi/assets/maps/test/5/1/25.jpg b/lib/pixi/assets/maps/test/5/1/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/25.jpg rename to lib/pixi/assets/maps/test/5/1/25.jpg diff --git a/pixi/assets/maps/test/5/1/26.jpg b/lib/pixi/assets/maps/test/5/1/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/26.jpg rename to lib/pixi/assets/maps/test/5/1/26.jpg diff --git a/pixi/assets/maps/test/5/1/27.jpg b/lib/pixi/assets/maps/test/5/1/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/27.jpg rename to lib/pixi/assets/maps/test/5/1/27.jpg diff --git a/pixi/assets/maps/test/5/1/28.jpg b/lib/pixi/assets/maps/test/5/1/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/28.jpg rename to lib/pixi/assets/maps/test/5/1/28.jpg diff --git a/pixi/assets/maps/test/5/1/29.jpg b/lib/pixi/assets/maps/test/5/1/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/29.jpg rename to lib/pixi/assets/maps/test/5/1/29.jpg diff --git a/pixi/assets/maps/test/5/1/3.jpg b/lib/pixi/assets/maps/test/5/1/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/3.jpg rename to lib/pixi/assets/maps/test/5/1/3.jpg diff --git a/pixi/assets/maps/test/5/1/30.jpg b/lib/pixi/assets/maps/test/5/1/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/30.jpg rename to lib/pixi/assets/maps/test/5/1/30.jpg diff --git a/pixi/assets/maps/test/5/1/31.jpg b/lib/pixi/assets/maps/test/5/1/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/31.jpg rename to lib/pixi/assets/maps/test/5/1/31.jpg diff --git a/pixi/assets/maps/test/5/1/4.jpg b/lib/pixi/assets/maps/test/5/1/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/4.jpg rename to lib/pixi/assets/maps/test/5/1/4.jpg diff --git a/pixi/assets/maps/test/5/1/5.jpg b/lib/pixi/assets/maps/test/5/1/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/5.jpg rename to lib/pixi/assets/maps/test/5/1/5.jpg diff --git a/pixi/assets/maps/test/5/1/6.jpg b/lib/pixi/assets/maps/test/5/1/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/6.jpg rename to lib/pixi/assets/maps/test/5/1/6.jpg diff --git a/pixi/assets/maps/test/5/1/7.jpg b/lib/pixi/assets/maps/test/5/1/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/7.jpg rename to lib/pixi/assets/maps/test/5/1/7.jpg diff --git a/pixi/assets/maps/test/5/1/8.jpg b/lib/pixi/assets/maps/test/5/1/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/8.jpg rename to lib/pixi/assets/maps/test/5/1/8.jpg diff --git a/pixi/assets/maps/test/5/1/9.jpg b/lib/pixi/assets/maps/test/5/1/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/1/9.jpg rename to lib/pixi/assets/maps/test/5/1/9.jpg diff --git a/pixi/assets/maps/test/5/10/0.jpg b/lib/pixi/assets/maps/test/5/10/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/0.jpg rename to lib/pixi/assets/maps/test/5/10/0.jpg diff --git a/pixi/assets/maps/test/5/10/1.jpg b/lib/pixi/assets/maps/test/5/10/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/1.jpg rename to lib/pixi/assets/maps/test/5/10/1.jpg diff --git a/pixi/assets/maps/test/5/10/10.jpg b/lib/pixi/assets/maps/test/5/10/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/10.jpg rename to lib/pixi/assets/maps/test/5/10/10.jpg diff --git a/pixi/assets/maps/test/5/10/11.jpg b/lib/pixi/assets/maps/test/5/10/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/11.jpg rename to lib/pixi/assets/maps/test/5/10/11.jpg diff --git a/pixi/assets/maps/test/5/10/12.jpg b/lib/pixi/assets/maps/test/5/10/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/12.jpg rename to lib/pixi/assets/maps/test/5/10/12.jpg diff --git a/pixi/assets/maps/test/5/10/13.jpg b/lib/pixi/assets/maps/test/5/10/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/13.jpg rename to lib/pixi/assets/maps/test/5/10/13.jpg diff --git a/pixi/assets/maps/test/5/10/14.jpg b/lib/pixi/assets/maps/test/5/10/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/14.jpg rename to lib/pixi/assets/maps/test/5/10/14.jpg diff --git a/pixi/assets/maps/test/5/10/15.jpg b/lib/pixi/assets/maps/test/5/10/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/15.jpg rename to lib/pixi/assets/maps/test/5/10/15.jpg diff --git a/pixi/assets/maps/test/5/10/16.jpg b/lib/pixi/assets/maps/test/5/10/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/16.jpg rename to lib/pixi/assets/maps/test/5/10/16.jpg diff --git a/pixi/assets/maps/test/5/10/17.jpg b/lib/pixi/assets/maps/test/5/10/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/17.jpg rename to lib/pixi/assets/maps/test/5/10/17.jpg diff --git a/pixi/assets/maps/test/5/10/18.jpg b/lib/pixi/assets/maps/test/5/10/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/18.jpg rename to lib/pixi/assets/maps/test/5/10/18.jpg diff --git a/pixi/assets/maps/test/5/10/19.jpg b/lib/pixi/assets/maps/test/5/10/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/19.jpg rename to lib/pixi/assets/maps/test/5/10/19.jpg diff --git a/pixi/assets/maps/test/5/10/2.jpg b/lib/pixi/assets/maps/test/5/10/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/2.jpg rename to lib/pixi/assets/maps/test/5/10/2.jpg diff --git a/pixi/assets/maps/test/5/10/20.jpg b/lib/pixi/assets/maps/test/5/10/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/20.jpg rename to lib/pixi/assets/maps/test/5/10/20.jpg diff --git a/pixi/assets/maps/test/5/10/21.jpg b/lib/pixi/assets/maps/test/5/10/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/21.jpg rename to lib/pixi/assets/maps/test/5/10/21.jpg diff --git a/pixi/assets/maps/test/5/10/22.jpg b/lib/pixi/assets/maps/test/5/10/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/22.jpg rename to lib/pixi/assets/maps/test/5/10/22.jpg diff --git a/pixi/assets/maps/test/5/10/23.jpg b/lib/pixi/assets/maps/test/5/10/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/23.jpg rename to lib/pixi/assets/maps/test/5/10/23.jpg diff --git a/pixi/assets/maps/test/5/10/24.jpg b/lib/pixi/assets/maps/test/5/10/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/24.jpg rename to lib/pixi/assets/maps/test/5/10/24.jpg diff --git a/pixi/assets/maps/test/5/10/25.jpg b/lib/pixi/assets/maps/test/5/10/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/25.jpg rename to lib/pixi/assets/maps/test/5/10/25.jpg diff --git a/pixi/assets/maps/test/5/10/26.jpg b/lib/pixi/assets/maps/test/5/10/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/26.jpg rename to lib/pixi/assets/maps/test/5/10/26.jpg diff --git a/pixi/assets/maps/test/5/10/27.jpg b/lib/pixi/assets/maps/test/5/10/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/27.jpg rename to lib/pixi/assets/maps/test/5/10/27.jpg diff --git a/pixi/assets/maps/test/5/10/28.jpg b/lib/pixi/assets/maps/test/5/10/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/28.jpg rename to lib/pixi/assets/maps/test/5/10/28.jpg diff --git a/pixi/assets/maps/test/5/10/29.jpg b/lib/pixi/assets/maps/test/5/10/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/29.jpg rename to lib/pixi/assets/maps/test/5/10/29.jpg diff --git a/pixi/assets/maps/test/5/10/3.jpg b/lib/pixi/assets/maps/test/5/10/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/3.jpg rename to lib/pixi/assets/maps/test/5/10/3.jpg diff --git a/pixi/assets/maps/test/5/10/30.jpg b/lib/pixi/assets/maps/test/5/10/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/30.jpg rename to lib/pixi/assets/maps/test/5/10/30.jpg diff --git a/pixi/assets/maps/test/5/10/31.jpg b/lib/pixi/assets/maps/test/5/10/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/31.jpg rename to lib/pixi/assets/maps/test/5/10/31.jpg diff --git a/pixi/assets/maps/test/5/10/4.jpg b/lib/pixi/assets/maps/test/5/10/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/4.jpg rename to lib/pixi/assets/maps/test/5/10/4.jpg diff --git a/pixi/assets/maps/test/5/10/5.jpg b/lib/pixi/assets/maps/test/5/10/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/5.jpg rename to lib/pixi/assets/maps/test/5/10/5.jpg diff --git a/pixi/assets/maps/test/5/10/6.jpg b/lib/pixi/assets/maps/test/5/10/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/6.jpg rename to lib/pixi/assets/maps/test/5/10/6.jpg diff --git a/pixi/assets/maps/test/5/10/7.jpg b/lib/pixi/assets/maps/test/5/10/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/7.jpg rename to lib/pixi/assets/maps/test/5/10/7.jpg diff --git a/pixi/assets/maps/test/5/10/8.jpg b/lib/pixi/assets/maps/test/5/10/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/8.jpg rename to lib/pixi/assets/maps/test/5/10/8.jpg diff --git a/pixi/assets/maps/test/5/10/9.jpg b/lib/pixi/assets/maps/test/5/10/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/10/9.jpg rename to lib/pixi/assets/maps/test/5/10/9.jpg diff --git a/pixi/assets/maps/test/5/11/0.jpg b/lib/pixi/assets/maps/test/5/11/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/0.jpg rename to lib/pixi/assets/maps/test/5/11/0.jpg diff --git a/pixi/assets/maps/test/5/11/1.jpg b/lib/pixi/assets/maps/test/5/11/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/1.jpg rename to lib/pixi/assets/maps/test/5/11/1.jpg diff --git a/pixi/assets/maps/test/5/11/10.jpg b/lib/pixi/assets/maps/test/5/11/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/10.jpg rename to lib/pixi/assets/maps/test/5/11/10.jpg diff --git a/pixi/assets/maps/test/5/11/11.jpg b/lib/pixi/assets/maps/test/5/11/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/11.jpg rename to lib/pixi/assets/maps/test/5/11/11.jpg diff --git a/pixi/assets/maps/test/5/11/12.jpg b/lib/pixi/assets/maps/test/5/11/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/12.jpg rename to lib/pixi/assets/maps/test/5/11/12.jpg diff --git a/pixi/assets/maps/test/5/11/13.jpg b/lib/pixi/assets/maps/test/5/11/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/13.jpg rename to lib/pixi/assets/maps/test/5/11/13.jpg diff --git a/pixi/assets/maps/test/5/11/14.jpg b/lib/pixi/assets/maps/test/5/11/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/14.jpg rename to lib/pixi/assets/maps/test/5/11/14.jpg diff --git a/pixi/assets/maps/test/5/11/15.jpg b/lib/pixi/assets/maps/test/5/11/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/15.jpg rename to lib/pixi/assets/maps/test/5/11/15.jpg diff --git a/pixi/assets/maps/test/5/11/16.jpg b/lib/pixi/assets/maps/test/5/11/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/16.jpg rename to lib/pixi/assets/maps/test/5/11/16.jpg diff --git a/pixi/assets/maps/test/5/11/17.jpg b/lib/pixi/assets/maps/test/5/11/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/17.jpg rename to lib/pixi/assets/maps/test/5/11/17.jpg diff --git a/pixi/assets/maps/test/5/11/18.jpg b/lib/pixi/assets/maps/test/5/11/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/18.jpg rename to lib/pixi/assets/maps/test/5/11/18.jpg diff --git a/pixi/assets/maps/test/5/11/19.jpg b/lib/pixi/assets/maps/test/5/11/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/19.jpg rename to lib/pixi/assets/maps/test/5/11/19.jpg diff --git a/pixi/assets/maps/test/5/11/2.jpg b/lib/pixi/assets/maps/test/5/11/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/2.jpg rename to lib/pixi/assets/maps/test/5/11/2.jpg diff --git a/pixi/assets/maps/test/5/11/20.jpg b/lib/pixi/assets/maps/test/5/11/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/20.jpg rename to lib/pixi/assets/maps/test/5/11/20.jpg diff --git a/pixi/assets/maps/test/5/11/21.jpg b/lib/pixi/assets/maps/test/5/11/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/21.jpg rename to lib/pixi/assets/maps/test/5/11/21.jpg diff --git a/pixi/assets/maps/test/5/11/22.jpg b/lib/pixi/assets/maps/test/5/11/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/22.jpg rename to lib/pixi/assets/maps/test/5/11/22.jpg diff --git a/pixi/assets/maps/test/5/11/23.jpg b/lib/pixi/assets/maps/test/5/11/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/23.jpg rename to lib/pixi/assets/maps/test/5/11/23.jpg diff --git a/pixi/assets/maps/test/5/11/24.jpg b/lib/pixi/assets/maps/test/5/11/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/24.jpg rename to lib/pixi/assets/maps/test/5/11/24.jpg diff --git a/pixi/assets/maps/test/5/11/25.jpg b/lib/pixi/assets/maps/test/5/11/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/25.jpg rename to lib/pixi/assets/maps/test/5/11/25.jpg diff --git a/pixi/assets/maps/test/5/11/26.jpg b/lib/pixi/assets/maps/test/5/11/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/26.jpg rename to lib/pixi/assets/maps/test/5/11/26.jpg diff --git a/pixi/assets/maps/test/5/11/27.jpg b/lib/pixi/assets/maps/test/5/11/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/27.jpg rename to lib/pixi/assets/maps/test/5/11/27.jpg diff --git a/pixi/assets/maps/test/5/11/28.jpg b/lib/pixi/assets/maps/test/5/11/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/28.jpg rename to lib/pixi/assets/maps/test/5/11/28.jpg diff --git a/pixi/assets/maps/test/5/11/29.jpg b/lib/pixi/assets/maps/test/5/11/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/29.jpg rename to lib/pixi/assets/maps/test/5/11/29.jpg diff --git a/pixi/assets/maps/test/5/11/3.jpg b/lib/pixi/assets/maps/test/5/11/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/3.jpg rename to lib/pixi/assets/maps/test/5/11/3.jpg diff --git a/pixi/assets/maps/test/5/11/30.jpg b/lib/pixi/assets/maps/test/5/11/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/30.jpg rename to lib/pixi/assets/maps/test/5/11/30.jpg diff --git a/pixi/assets/maps/test/5/11/31.jpg b/lib/pixi/assets/maps/test/5/11/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/31.jpg rename to lib/pixi/assets/maps/test/5/11/31.jpg diff --git a/pixi/assets/maps/test/5/11/4.jpg b/lib/pixi/assets/maps/test/5/11/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/4.jpg rename to lib/pixi/assets/maps/test/5/11/4.jpg diff --git a/pixi/assets/maps/test/5/11/5.jpg b/lib/pixi/assets/maps/test/5/11/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/5.jpg rename to lib/pixi/assets/maps/test/5/11/5.jpg diff --git a/pixi/assets/maps/test/5/11/6.jpg b/lib/pixi/assets/maps/test/5/11/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/6.jpg rename to lib/pixi/assets/maps/test/5/11/6.jpg diff --git a/pixi/assets/maps/test/5/11/7.jpg b/lib/pixi/assets/maps/test/5/11/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/7.jpg rename to lib/pixi/assets/maps/test/5/11/7.jpg diff --git a/pixi/assets/maps/test/5/11/8.jpg b/lib/pixi/assets/maps/test/5/11/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/8.jpg rename to lib/pixi/assets/maps/test/5/11/8.jpg diff --git a/pixi/assets/maps/test/5/11/9.jpg b/lib/pixi/assets/maps/test/5/11/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/11/9.jpg rename to lib/pixi/assets/maps/test/5/11/9.jpg diff --git a/pixi/assets/maps/test/5/12/0.jpg b/lib/pixi/assets/maps/test/5/12/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/0.jpg rename to lib/pixi/assets/maps/test/5/12/0.jpg diff --git a/pixi/assets/maps/test/5/12/1.jpg b/lib/pixi/assets/maps/test/5/12/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/1.jpg rename to lib/pixi/assets/maps/test/5/12/1.jpg diff --git a/pixi/assets/maps/test/5/12/10.jpg b/lib/pixi/assets/maps/test/5/12/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/10.jpg rename to lib/pixi/assets/maps/test/5/12/10.jpg diff --git a/pixi/assets/maps/test/5/12/11.jpg b/lib/pixi/assets/maps/test/5/12/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/11.jpg rename to lib/pixi/assets/maps/test/5/12/11.jpg diff --git a/pixi/assets/maps/test/5/12/12.jpg b/lib/pixi/assets/maps/test/5/12/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/12.jpg rename to lib/pixi/assets/maps/test/5/12/12.jpg diff --git a/pixi/assets/maps/test/5/12/13.jpg b/lib/pixi/assets/maps/test/5/12/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/13.jpg rename to lib/pixi/assets/maps/test/5/12/13.jpg diff --git a/pixi/assets/maps/test/5/12/14.jpg b/lib/pixi/assets/maps/test/5/12/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/14.jpg rename to lib/pixi/assets/maps/test/5/12/14.jpg diff --git a/pixi/assets/maps/test/5/12/15.jpg b/lib/pixi/assets/maps/test/5/12/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/15.jpg rename to lib/pixi/assets/maps/test/5/12/15.jpg diff --git a/pixi/assets/maps/test/5/12/16.jpg b/lib/pixi/assets/maps/test/5/12/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/16.jpg rename to lib/pixi/assets/maps/test/5/12/16.jpg diff --git a/pixi/assets/maps/test/5/12/17.jpg b/lib/pixi/assets/maps/test/5/12/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/17.jpg rename to lib/pixi/assets/maps/test/5/12/17.jpg diff --git a/pixi/assets/maps/test/5/12/18.jpg b/lib/pixi/assets/maps/test/5/12/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/18.jpg rename to lib/pixi/assets/maps/test/5/12/18.jpg diff --git a/pixi/assets/maps/test/5/12/19.jpg b/lib/pixi/assets/maps/test/5/12/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/19.jpg rename to lib/pixi/assets/maps/test/5/12/19.jpg diff --git a/pixi/assets/maps/test/5/12/2.jpg b/lib/pixi/assets/maps/test/5/12/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/2.jpg rename to lib/pixi/assets/maps/test/5/12/2.jpg diff --git a/pixi/assets/maps/test/5/12/20.jpg b/lib/pixi/assets/maps/test/5/12/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/20.jpg rename to lib/pixi/assets/maps/test/5/12/20.jpg diff --git a/pixi/assets/maps/test/5/12/21.jpg b/lib/pixi/assets/maps/test/5/12/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/21.jpg rename to lib/pixi/assets/maps/test/5/12/21.jpg diff --git a/pixi/assets/maps/test/5/12/22.jpg b/lib/pixi/assets/maps/test/5/12/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/22.jpg rename to lib/pixi/assets/maps/test/5/12/22.jpg diff --git a/pixi/assets/maps/test/5/12/23.jpg b/lib/pixi/assets/maps/test/5/12/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/23.jpg rename to lib/pixi/assets/maps/test/5/12/23.jpg diff --git a/pixi/assets/maps/test/5/12/24.jpg b/lib/pixi/assets/maps/test/5/12/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/24.jpg rename to lib/pixi/assets/maps/test/5/12/24.jpg diff --git a/pixi/assets/maps/test/5/12/25.jpg b/lib/pixi/assets/maps/test/5/12/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/25.jpg rename to lib/pixi/assets/maps/test/5/12/25.jpg diff --git a/pixi/assets/maps/test/5/12/26.jpg b/lib/pixi/assets/maps/test/5/12/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/26.jpg rename to lib/pixi/assets/maps/test/5/12/26.jpg diff --git a/pixi/assets/maps/test/5/12/27.jpg b/lib/pixi/assets/maps/test/5/12/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/27.jpg rename to lib/pixi/assets/maps/test/5/12/27.jpg diff --git a/pixi/assets/maps/test/5/12/28.jpg b/lib/pixi/assets/maps/test/5/12/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/28.jpg rename to lib/pixi/assets/maps/test/5/12/28.jpg diff --git a/pixi/assets/maps/test/5/12/29.jpg b/lib/pixi/assets/maps/test/5/12/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/29.jpg rename to lib/pixi/assets/maps/test/5/12/29.jpg diff --git a/pixi/assets/maps/test/5/12/3.jpg b/lib/pixi/assets/maps/test/5/12/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/3.jpg rename to lib/pixi/assets/maps/test/5/12/3.jpg diff --git a/pixi/assets/maps/test/5/12/30.jpg b/lib/pixi/assets/maps/test/5/12/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/30.jpg rename to lib/pixi/assets/maps/test/5/12/30.jpg diff --git a/pixi/assets/maps/test/5/12/31.jpg b/lib/pixi/assets/maps/test/5/12/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/31.jpg rename to lib/pixi/assets/maps/test/5/12/31.jpg diff --git a/pixi/assets/maps/test/5/12/4.jpg b/lib/pixi/assets/maps/test/5/12/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/4.jpg rename to lib/pixi/assets/maps/test/5/12/4.jpg diff --git a/pixi/assets/maps/test/5/12/5.jpg b/lib/pixi/assets/maps/test/5/12/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/5.jpg rename to lib/pixi/assets/maps/test/5/12/5.jpg diff --git a/pixi/assets/maps/test/5/12/6.jpg b/lib/pixi/assets/maps/test/5/12/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/6.jpg rename to lib/pixi/assets/maps/test/5/12/6.jpg diff --git a/pixi/assets/maps/test/5/12/7.jpg b/lib/pixi/assets/maps/test/5/12/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/7.jpg rename to lib/pixi/assets/maps/test/5/12/7.jpg diff --git a/pixi/assets/maps/test/5/12/8.jpg b/lib/pixi/assets/maps/test/5/12/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/8.jpg rename to lib/pixi/assets/maps/test/5/12/8.jpg diff --git a/pixi/assets/maps/test/5/12/9.jpg b/lib/pixi/assets/maps/test/5/12/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/12/9.jpg rename to lib/pixi/assets/maps/test/5/12/9.jpg diff --git a/pixi/assets/maps/test/5/13/0.jpg b/lib/pixi/assets/maps/test/5/13/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/0.jpg rename to lib/pixi/assets/maps/test/5/13/0.jpg diff --git a/pixi/assets/maps/test/5/13/1.jpg b/lib/pixi/assets/maps/test/5/13/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/1.jpg rename to lib/pixi/assets/maps/test/5/13/1.jpg diff --git a/pixi/assets/maps/test/5/13/10.jpg b/lib/pixi/assets/maps/test/5/13/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/10.jpg rename to lib/pixi/assets/maps/test/5/13/10.jpg diff --git a/pixi/assets/maps/test/5/13/11.jpg b/lib/pixi/assets/maps/test/5/13/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/11.jpg rename to lib/pixi/assets/maps/test/5/13/11.jpg diff --git a/pixi/assets/maps/test/5/13/12.jpg b/lib/pixi/assets/maps/test/5/13/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/12.jpg rename to lib/pixi/assets/maps/test/5/13/12.jpg diff --git a/pixi/assets/maps/test/5/13/13.jpg b/lib/pixi/assets/maps/test/5/13/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/13.jpg rename to lib/pixi/assets/maps/test/5/13/13.jpg diff --git a/pixi/assets/maps/test/5/13/14.jpg b/lib/pixi/assets/maps/test/5/13/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/14.jpg rename to lib/pixi/assets/maps/test/5/13/14.jpg diff --git a/pixi/assets/maps/test/5/13/15.jpg b/lib/pixi/assets/maps/test/5/13/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/15.jpg rename to lib/pixi/assets/maps/test/5/13/15.jpg diff --git a/pixi/assets/maps/test/5/13/16.jpg b/lib/pixi/assets/maps/test/5/13/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/16.jpg rename to lib/pixi/assets/maps/test/5/13/16.jpg diff --git a/pixi/assets/maps/test/5/13/17.jpg b/lib/pixi/assets/maps/test/5/13/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/17.jpg rename to lib/pixi/assets/maps/test/5/13/17.jpg diff --git a/pixi/assets/maps/test/5/13/18.jpg b/lib/pixi/assets/maps/test/5/13/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/18.jpg rename to lib/pixi/assets/maps/test/5/13/18.jpg diff --git a/pixi/assets/maps/test/5/13/19.jpg b/lib/pixi/assets/maps/test/5/13/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/19.jpg rename to lib/pixi/assets/maps/test/5/13/19.jpg diff --git a/pixi/assets/maps/test/5/13/2.jpg b/lib/pixi/assets/maps/test/5/13/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/2.jpg rename to lib/pixi/assets/maps/test/5/13/2.jpg diff --git a/pixi/assets/maps/test/5/13/20.jpg b/lib/pixi/assets/maps/test/5/13/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/20.jpg rename to lib/pixi/assets/maps/test/5/13/20.jpg diff --git a/pixi/assets/maps/test/5/13/21.jpg b/lib/pixi/assets/maps/test/5/13/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/21.jpg rename to lib/pixi/assets/maps/test/5/13/21.jpg diff --git a/pixi/assets/maps/test/5/13/22.jpg b/lib/pixi/assets/maps/test/5/13/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/22.jpg rename to lib/pixi/assets/maps/test/5/13/22.jpg diff --git a/pixi/assets/maps/test/5/13/23.jpg b/lib/pixi/assets/maps/test/5/13/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/23.jpg rename to lib/pixi/assets/maps/test/5/13/23.jpg diff --git a/pixi/assets/maps/test/5/13/24.jpg b/lib/pixi/assets/maps/test/5/13/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/24.jpg rename to lib/pixi/assets/maps/test/5/13/24.jpg diff --git a/pixi/assets/maps/test/5/13/25.jpg b/lib/pixi/assets/maps/test/5/13/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/25.jpg rename to lib/pixi/assets/maps/test/5/13/25.jpg diff --git a/pixi/assets/maps/test/5/13/26.jpg b/lib/pixi/assets/maps/test/5/13/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/26.jpg rename to lib/pixi/assets/maps/test/5/13/26.jpg diff --git a/pixi/assets/maps/test/5/13/27.jpg b/lib/pixi/assets/maps/test/5/13/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/27.jpg rename to lib/pixi/assets/maps/test/5/13/27.jpg diff --git a/pixi/assets/maps/test/5/13/28.jpg b/lib/pixi/assets/maps/test/5/13/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/28.jpg rename to lib/pixi/assets/maps/test/5/13/28.jpg diff --git a/pixi/assets/maps/test/5/13/29.jpg b/lib/pixi/assets/maps/test/5/13/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/29.jpg rename to lib/pixi/assets/maps/test/5/13/29.jpg diff --git a/pixi/assets/maps/test/5/13/3.jpg b/lib/pixi/assets/maps/test/5/13/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/3.jpg rename to lib/pixi/assets/maps/test/5/13/3.jpg diff --git a/pixi/assets/maps/test/5/13/30.jpg b/lib/pixi/assets/maps/test/5/13/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/30.jpg rename to lib/pixi/assets/maps/test/5/13/30.jpg diff --git a/pixi/assets/maps/test/5/13/31.jpg b/lib/pixi/assets/maps/test/5/13/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/31.jpg rename to lib/pixi/assets/maps/test/5/13/31.jpg diff --git a/pixi/assets/maps/test/5/13/4.jpg b/lib/pixi/assets/maps/test/5/13/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/4.jpg rename to lib/pixi/assets/maps/test/5/13/4.jpg diff --git a/pixi/assets/maps/test/5/13/5.jpg b/lib/pixi/assets/maps/test/5/13/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/5.jpg rename to lib/pixi/assets/maps/test/5/13/5.jpg diff --git a/pixi/assets/maps/test/5/13/6.jpg b/lib/pixi/assets/maps/test/5/13/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/6.jpg rename to lib/pixi/assets/maps/test/5/13/6.jpg diff --git a/pixi/assets/maps/test/5/13/7.jpg b/lib/pixi/assets/maps/test/5/13/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/7.jpg rename to lib/pixi/assets/maps/test/5/13/7.jpg diff --git a/pixi/assets/maps/test/5/13/8.jpg b/lib/pixi/assets/maps/test/5/13/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/8.jpg rename to lib/pixi/assets/maps/test/5/13/8.jpg diff --git a/pixi/assets/maps/test/5/13/9.jpg b/lib/pixi/assets/maps/test/5/13/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/13/9.jpg rename to lib/pixi/assets/maps/test/5/13/9.jpg diff --git a/pixi/assets/maps/test/5/14/0.jpg b/lib/pixi/assets/maps/test/5/14/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/0.jpg rename to lib/pixi/assets/maps/test/5/14/0.jpg diff --git a/pixi/assets/maps/test/5/14/1.jpg b/lib/pixi/assets/maps/test/5/14/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/1.jpg rename to lib/pixi/assets/maps/test/5/14/1.jpg diff --git a/pixi/assets/maps/test/5/14/10.jpg b/lib/pixi/assets/maps/test/5/14/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/10.jpg rename to lib/pixi/assets/maps/test/5/14/10.jpg diff --git a/pixi/assets/maps/test/5/14/11.jpg b/lib/pixi/assets/maps/test/5/14/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/11.jpg rename to lib/pixi/assets/maps/test/5/14/11.jpg diff --git a/pixi/assets/maps/test/5/14/12.jpg b/lib/pixi/assets/maps/test/5/14/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/12.jpg rename to lib/pixi/assets/maps/test/5/14/12.jpg diff --git a/pixi/assets/maps/test/5/14/13.jpg b/lib/pixi/assets/maps/test/5/14/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/13.jpg rename to lib/pixi/assets/maps/test/5/14/13.jpg diff --git a/pixi/assets/maps/test/5/14/14.jpg b/lib/pixi/assets/maps/test/5/14/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/14.jpg rename to lib/pixi/assets/maps/test/5/14/14.jpg diff --git a/pixi/assets/maps/test/5/14/15.jpg b/lib/pixi/assets/maps/test/5/14/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/15.jpg rename to lib/pixi/assets/maps/test/5/14/15.jpg diff --git a/pixi/assets/maps/test/5/14/16.jpg b/lib/pixi/assets/maps/test/5/14/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/16.jpg rename to lib/pixi/assets/maps/test/5/14/16.jpg diff --git a/pixi/assets/maps/test/5/14/17.jpg b/lib/pixi/assets/maps/test/5/14/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/17.jpg rename to lib/pixi/assets/maps/test/5/14/17.jpg diff --git a/pixi/assets/maps/test/5/14/18.jpg b/lib/pixi/assets/maps/test/5/14/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/18.jpg rename to lib/pixi/assets/maps/test/5/14/18.jpg diff --git a/pixi/assets/maps/test/5/14/19.jpg b/lib/pixi/assets/maps/test/5/14/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/19.jpg rename to lib/pixi/assets/maps/test/5/14/19.jpg diff --git a/pixi/assets/maps/test/5/14/2.jpg b/lib/pixi/assets/maps/test/5/14/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/2.jpg rename to lib/pixi/assets/maps/test/5/14/2.jpg diff --git a/pixi/assets/maps/test/5/14/20.jpg b/lib/pixi/assets/maps/test/5/14/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/20.jpg rename to lib/pixi/assets/maps/test/5/14/20.jpg diff --git a/pixi/assets/maps/test/5/14/21.jpg b/lib/pixi/assets/maps/test/5/14/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/21.jpg rename to lib/pixi/assets/maps/test/5/14/21.jpg diff --git a/pixi/assets/maps/test/5/14/22.jpg b/lib/pixi/assets/maps/test/5/14/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/22.jpg rename to lib/pixi/assets/maps/test/5/14/22.jpg diff --git a/pixi/assets/maps/test/5/14/23.jpg b/lib/pixi/assets/maps/test/5/14/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/23.jpg rename to lib/pixi/assets/maps/test/5/14/23.jpg diff --git a/pixi/assets/maps/test/5/14/24.jpg b/lib/pixi/assets/maps/test/5/14/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/24.jpg rename to lib/pixi/assets/maps/test/5/14/24.jpg diff --git a/pixi/assets/maps/test/5/14/25.jpg b/lib/pixi/assets/maps/test/5/14/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/25.jpg rename to lib/pixi/assets/maps/test/5/14/25.jpg diff --git a/pixi/assets/maps/test/5/14/26.jpg b/lib/pixi/assets/maps/test/5/14/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/26.jpg rename to lib/pixi/assets/maps/test/5/14/26.jpg diff --git a/pixi/assets/maps/test/5/14/27.jpg b/lib/pixi/assets/maps/test/5/14/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/27.jpg rename to lib/pixi/assets/maps/test/5/14/27.jpg diff --git a/pixi/assets/maps/test/5/14/28.jpg b/lib/pixi/assets/maps/test/5/14/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/28.jpg rename to lib/pixi/assets/maps/test/5/14/28.jpg diff --git a/pixi/assets/maps/test/5/14/29.jpg b/lib/pixi/assets/maps/test/5/14/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/29.jpg rename to lib/pixi/assets/maps/test/5/14/29.jpg diff --git a/pixi/assets/maps/test/5/14/3.jpg b/lib/pixi/assets/maps/test/5/14/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/3.jpg rename to lib/pixi/assets/maps/test/5/14/3.jpg diff --git a/pixi/assets/maps/test/5/14/30.jpg b/lib/pixi/assets/maps/test/5/14/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/30.jpg rename to lib/pixi/assets/maps/test/5/14/30.jpg diff --git a/pixi/assets/maps/test/5/14/31.jpg b/lib/pixi/assets/maps/test/5/14/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/31.jpg rename to lib/pixi/assets/maps/test/5/14/31.jpg diff --git a/pixi/assets/maps/test/5/14/4.jpg b/lib/pixi/assets/maps/test/5/14/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/4.jpg rename to lib/pixi/assets/maps/test/5/14/4.jpg diff --git a/pixi/assets/maps/test/5/14/5.jpg b/lib/pixi/assets/maps/test/5/14/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/5.jpg rename to lib/pixi/assets/maps/test/5/14/5.jpg diff --git a/pixi/assets/maps/test/5/14/6.jpg b/lib/pixi/assets/maps/test/5/14/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/6.jpg rename to lib/pixi/assets/maps/test/5/14/6.jpg diff --git a/pixi/assets/maps/test/5/14/7.jpg b/lib/pixi/assets/maps/test/5/14/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/7.jpg rename to lib/pixi/assets/maps/test/5/14/7.jpg diff --git a/pixi/assets/maps/test/5/14/8.jpg b/lib/pixi/assets/maps/test/5/14/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/8.jpg rename to lib/pixi/assets/maps/test/5/14/8.jpg diff --git a/pixi/assets/maps/test/5/14/9.jpg b/lib/pixi/assets/maps/test/5/14/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/14/9.jpg rename to lib/pixi/assets/maps/test/5/14/9.jpg diff --git a/pixi/assets/maps/test/5/15/0.jpg b/lib/pixi/assets/maps/test/5/15/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/0.jpg rename to lib/pixi/assets/maps/test/5/15/0.jpg diff --git a/pixi/assets/maps/test/5/15/1.jpg b/lib/pixi/assets/maps/test/5/15/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/1.jpg rename to lib/pixi/assets/maps/test/5/15/1.jpg diff --git a/pixi/assets/maps/test/5/15/10.jpg b/lib/pixi/assets/maps/test/5/15/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/10.jpg rename to lib/pixi/assets/maps/test/5/15/10.jpg diff --git a/pixi/assets/maps/test/5/15/11.jpg b/lib/pixi/assets/maps/test/5/15/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/11.jpg rename to lib/pixi/assets/maps/test/5/15/11.jpg diff --git a/pixi/assets/maps/test/5/15/12.jpg b/lib/pixi/assets/maps/test/5/15/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/12.jpg rename to lib/pixi/assets/maps/test/5/15/12.jpg diff --git a/pixi/assets/maps/test/5/15/13.jpg b/lib/pixi/assets/maps/test/5/15/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/13.jpg rename to lib/pixi/assets/maps/test/5/15/13.jpg diff --git a/pixi/assets/maps/test/5/15/14.jpg b/lib/pixi/assets/maps/test/5/15/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/14.jpg rename to lib/pixi/assets/maps/test/5/15/14.jpg diff --git a/pixi/assets/maps/test/5/15/15.jpg b/lib/pixi/assets/maps/test/5/15/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/15.jpg rename to lib/pixi/assets/maps/test/5/15/15.jpg diff --git a/pixi/assets/maps/test/5/15/16.jpg b/lib/pixi/assets/maps/test/5/15/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/16.jpg rename to lib/pixi/assets/maps/test/5/15/16.jpg diff --git a/pixi/assets/maps/test/5/15/17.jpg b/lib/pixi/assets/maps/test/5/15/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/17.jpg rename to lib/pixi/assets/maps/test/5/15/17.jpg diff --git a/pixi/assets/maps/test/5/15/18.jpg b/lib/pixi/assets/maps/test/5/15/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/18.jpg rename to lib/pixi/assets/maps/test/5/15/18.jpg diff --git a/pixi/assets/maps/test/5/15/19.jpg b/lib/pixi/assets/maps/test/5/15/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/19.jpg rename to lib/pixi/assets/maps/test/5/15/19.jpg diff --git a/pixi/assets/maps/test/5/15/2.jpg b/lib/pixi/assets/maps/test/5/15/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/2.jpg rename to lib/pixi/assets/maps/test/5/15/2.jpg diff --git a/pixi/assets/maps/test/5/15/20.jpg b/lib/pixi/assets/maps/test/5/15/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/20.jpg rename to lib/pixi/assets/maps/test/5/15/20.jpg diff --git a/pixi/assets/maps/test/5/15/21.jpg b/lib/pixi/assets/maps/test/5/15/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/21.jpg rename to lib/pixi/assets/maps/test/5/15/21.jpg diff --git a/pixi/assets/maps/test/5/15/22.jpg b/lib/pixi/assets/maps/test/5/15/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/22.jpg rename to lib/pixi/assets/maps/test/5/15/22.jpg diff --git a/pixi/assets/maps/test/5/15/23.jpg b/lib/pixi/assets/maps/test/5/15/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/23.jpg rename to lib/pixi/assets/maps/test/5/15/23.jpg diff --git a/pixi/assets/maps/test/5/15/24.jpg b/lib/pixi/assets/maps/test/5/15/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/24.jpg rename to lib/pixi/assets/maps/test/5/15/24.jpg diff --git a/pixi/assets/maps/test/5/15/25.jpg b/lib/pixi/assets/maps/test/5/15/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/25.jpg rename to lib/pixi/assets/maps/test/5/15/25.jpg diff --git a/pixi/assets/maps/test/5/15/26.jpg b/lib/pixi/assets/maps/test/5/15/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/26.jpg rename to lib/pixi/assets/maps/test/5/15/26.jpg diff --git a/pixi/assets/maps/test/5/15/27.jpg b/lib/pixi/assets/maps/test/5/15/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/27.jpg rename to lib/pixi/assets/maps/test/5/15/27.jpg diff --git a/pixi/assets/maps/test/5/15/28.jpg b/lib/pixi/assets/maps/test/5/15/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/28.jpg rename to lib/pixi/assets/maps/test/5/15/28.jpg diff --git a/pixi/assets/maps/test/5/15/29.jpg b/lib/pixi/assets/maps/test/5/15/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/29.jpg rename to lib/pixi/assets/maps/test/5/15/29.jpg diff --git a/pixi/assets/maps/test/5/15/3.jpg b/lib/pixi/assets/maps/test/5/15/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/3.jpg rename to lib/pixi/assets/maps/test/5/15/3.jpg diff --git a/pixi/assets/maps/test/5/15/30.jpg b/lib/pixi/assets/maps/test/5/15/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/30.jpg rename to lib/pixi/assets/maps/test/5/15/30.jpg diff --git a/pixi/assets/maps/test/5/15/31.jpg b/lib/pixi/assets/maps/test/5/15/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/31.jpg rename to lib/pixi/assets/maps/test/5/15/31.jpg diff --git a/pixi/assets/maps/test/5/15/4.jpg b/lib/pixi/assets/maps/test/5/15/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/4.jpg rename to lib/pixi/assets/maps/test/5/15/4.jpg diff --git a/pixi/assets/maps/test/5/15/5.jpg b/lib/pixi/assets/maps/test/5/15/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/5.jpg rename to lib/pixi/assets/maps/test/5/15/5.jpg diff --git a/pixi/assets/maps/test/5/15/6.jpg b/lib/pixi/assets/maps/test/5/15/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/6.jpg rename to lib/pixi/assets/maps/test/5/15/6.jpg diff --git a/pixi/assets/maps/test/5/15/7.jpg b/lib/pixi/assets/maps/test/5/15/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/7.jpg rename to lib/pixi/assets/maps/test/5/15/7.jpg diff --git a/pixi/assets/maps/test/5/15/8.jpg b/lib/pixi/assets/maps/test/5/15/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/8.jpg rename to lib/pixi/assets/maps/test/5/15/8.jpg diff --git a/pixi/assets/maps/test/5/15/9.jpg b/lib/pixi/assets/maps/test/5/15/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/15/9.jpg rename to lib/pixi/assets/maps/test/5/15/9.jpg diff --git a/pixi/assets/maps/test/5/16/0.jpg b/lib/pixi/assets/maps/test/5/16/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/0.jpg rename to lib/pixi/assets/maps/test/5/16/0.jpg diff --git a/pixi/assets/maps/test/5/16/1.jpg b/lib/pixi/assets/maps/test/5/16/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/1.jpg rename to lib/pixi/assets/maps/test/5/16/1.jpg diff --git a/pixi/assets/maps/test/5/16/10.jpg b/lib/pixi/assets/maps/test/5/16/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/10.jpg rename to lib/pixi/assets/maps/test/5/16/10.jpg diff --git a/pixi/assets/maps/test/5/16/11.jpg b/lib/pixi/assets/maps/test/5/16/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/11.jpg rename to lib/pixi/assets/maps/test/5/16/11.jpg diff --git a/pixi/assets/maps/test/5/16/12.jpg b/lib/pixi/assets/maps/test/5/16/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/12.jpg rename to lib/pixi/assets/maps/test/5/16/12.jpg diff --git a/pixi/assets/maps/test/5/16/13.jpg b/lib/pixi/assets/maps/test/5/16/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/13.jpg rename to lib/pixi/assets/maps/test/5/16/13.jpg diff --git a/pixi/assets/maps/test/5/16/14.jpg b/lib/pixi/assets/maps/test/5/16/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/14.jpg rename to lib/pixi/assets/maps/test/5/16/14.jpg diff --git a/pixi/assets/maps/test/5/16/15.jpg b/lib/pixi/assets/maps/test/5/16/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/15.jpg rename to lib/pixi/assets/maps/test/5/16/15.jpg diff --git a/pixi/assets/maps/test/5/16/16.jpg b/lib/pixi/assets/maps/test/5/16/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/16.jpg rename to lib/pixi/assets/maps/test/5/16/16.jpg diff --git a/pixi/assets/maps/test/5/16/17.jpg b/lib/pixi/assets/maps/test/5/16/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/17.jpg rename to lib/pixi/assets/maps/test/5/16/17.jpg diff --git a/pixi/assets/maps/test/5/16/18.jpg b/lib/pixi/assets/maps/test/5/16/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/18.jpg rename to lib/pixi/assets/maps/test/5/16/18.jpg diff --git a/pixi/assets/maps/test/5/16/19.jpg b/lib/pixi/assets/maps/test/5/16/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/19.jpg rename to lib/pixi/assets/maps/test/5/16/19.jpg diff --git a/pixi/assets/maps/test/5/16/2.jpg b/lib/pixi/assets/maps/test/5/16/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/2.jpg rename to lib/pixi/assets/maps/test/5/16/2.jpg diff --git a/pixi/assets/maps/test/5/16/20.jpg b/lib/pixi/assets/maps/test/5/16/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/20.jpg rename to lib/pixi/assets/maps/test/5/16/20.jpg diff --git a/pixi/assets/maps/test/5/16/21.jpg b/lib/pixi/assets/maps/test/5/16/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/21.jpg rename to lib/pixi/assets/maps/test/5/16/21.jpg diff --git a/pixi/assets/maps/test/5/16/22.jpg b/lib/pixi/assets/maps/test/5/16/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/22.jpg rename to lib/pixi/assets/maps/test/5/16/22.jpg diff --git a/pixi/assets/maps/test/5/16/23.jpg b/lib/pixi/assets/maps/test/5/16/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/23.jpg rename to lib/pixi/assets/maps/test/5/16/23.jpg diff --git a/pixi/assets/maps/test/5/16/24.jpg b/lib/pixi/assets/maps/test/5/16/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/24.jpg rename to lib/pixi/assets/maps/test/5/16/24.jpg diff --git a/pixi/assets/maps/test/5/16/25.jpg b/lib/pixi/assets/maps/test/5/16/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/25.jpg rename to lib/pixi/assets/maps/test/5/16/25.jpg diff --git a/pixi/assets/maps/test/5/16/26.jpg b/lib/pixi/assets/maps/test/5/16/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/26.jpg rename to lib/pixi/assets/maps/test/5/16/26.jpg diff --git a/pixi/assets/maps/test/5/16/27.jpg b/lib/pixi/assets/maps/test/5/16/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/27.jpg rename to lib/pixi/assets/maps/test/5/16/27.jpg diff --git a/pixi/assets/maps/test/5/16/28.jpg b/lib/pixi/assets/maps/test/5/16/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/28.jpg rename to lib/pixi/assets/maps/test/5/16/28.jpg diff --git a/pixi/assets/maps/test/5/16/29.jpg b/lib/pixi/assets/maps/test/5/16/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/29.jpg rename to lib/pixi/assets/maps/test/5/16/29.jpg diff --git a/pixi/assets/maps/test/5/16/3.jpg b/lib/pixi/assets/maps/test/5/16/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/3.jpg rename to lib/pixi/assets/maps/test/5/16/3.jpg diff --git a/pixi/assets/maps/test/5/16/30.jpg b/lib/pixi/assets/maps/test/5/16/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/30.jpg rename to lib/pixi/assets/maps/test/5/16/30.jpg diff --git a/pixi/assets/maps/test/5/16/31.jpg b/lib/pixi/assets/maps/test/5/16/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/31.jpg rename to lib/pixi/assets/maps/test/5/16/31.jpg diff --git a/pixi/assets/maps/test/5/16/4.jpg b/lib/pixi/assets/maps/test/5/16/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/4.jpg rename to lib/pixi/assets/maps/test/5/16/4.jpg diff --git a/pixi/assets/maps/test/5/16/5.jpg b/lib/pixi/assets/maps/test/5/16/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/5.jpg rename to lib/pixi/assets/maps/test/5/16/5.jpg diff --git a/pixi/assets/maps/test/5/16/6.jpg b/lib/pixi/assets/maps/test/5/16/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/6.jpg rename to lib/pixi/assets/maps/test/5/16/6.jpg diff --git a/pixi/assets/maps/test/5/16/7.jpg b/lib/pixi/assets/maps/test/5/16/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/7.jpg rename to lib/pixi/assets/maps/test/5/16/7.jpg diff --git a/pixi/assets/maps/test/5/16/8.jpg b/lib/pixi/assets/maps/test/5/16/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/8.jpg rename to lib/pixi/assets/maps/test/5/16/8.jpg diff --git a/pixi/assets/maps/test/5/16/9.jpg b/lib/pixi/assets/maps/test/5/16/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/16/9.jpg rename to lib/pixi/assets/maps/test/5/16/9.jpg diff --git a/pixi/assets/maps/test/5/17/0.jpg b/lib/pixi/assets/maps/test/5/17/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/0.jpg rename to lib/pixi/assets/maps/test/5/17/0.jpg diff --git a/pixi/assets/maps/test/5/17/1.jpg b/lib/pixi/assets/maps/test/5/17/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/1.jpg rename to lib/pixi/assets/maps/test/5/17/1.jpg diff --git a/pixi/assets/maps/test/5/17/10.jpg b/lib/pixi/assets/maps/test/5/17/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/10.jpg rename to lib/pixi/assets/maps/test/5/17/10.jpg diff --git a/pixi/assets/maps/test/5/17/11.jpg b/lib/pixi/assets/maps/test/5/17/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/11.jpg rename to lib/pixi/assets/maps/test/5/17/11.jpg diff --git a/pixi/assets/maps/test/5/17/12.jpg b/lib/pixi/assets/maps/test/5/17/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/12.jpg rename to lib/pixi/assets/maps/test/5/17/12.jpg diff --git a/pixi/assets/maps/test/5/17/13.jpg b/lib/pixi/assets/maps/test/5/17/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/13.jpg rename to lib/pixi/assets/maps/test/5/17/13.jpg diff --git a/pixi/assets/maps/test/5/17/14.jpg b/lib/pixi/assets/maps/test/5/17/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/14.jpg rename to lib/pixi/assets/maps/test/5/17/14.jpg diff --git a/pixi/assets/maps/test/5/17/15.jpg b/lib/pixi/assets/maps/test/5/17/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/15.jpg rename to lib/pixi/assets/maps/test/5/17/15.jpg diff --git a/pixi/assets/maps/test/5/17/16.jpg b/lib/pixi/assets/maps/test/5/17/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/16.jpg rename to lib/pixi/assets/maps/test/5/17/16.jpg diff --git a/pixi/assets/maps/test/5/17/17.jpg b/lib/pixi/assets/maps/test/5/17/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/17.jpg rename to lib/pixi/assets/maps/test/5/17/17.jpg diff --git a/pixi/assets/maps/test/5/17/18.jpg b/lib/pixi/assets/maps/test/5/17/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/18.jpg rename to lib/pixi/assets/maps/test/5/17/18.jpg diff --git a/pixi/assets/maps/test/5/17/19.jpg b/lib/pixi/assets/maps/test/5/17/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/19.jpg rename to lib/pixi/assets/maps/test/5/17/19.jpg diff --git a/pixi/assets/maps/test/5/17/2.jpg b/lib/pixi/assets/maps/test/5/17/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/2.jpg rename to lib/pixi/assets/maps/test/5/17/2.jpg diff --git a/pixi/assets/maps/test/5/17/20.jpg b/lib/pixi/assets/maps/test/5/17/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/20.jpg rename to lib/pixi/assets/maps/test/5/17/20.jpg diff --git a/pixi/assets/maps/test/5/17/21.jpg b/lib/pixi/assets/maps/test/5/17/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/21.jpg rename to lib/pixi/assets/maps/test/5/17/21.jpg diff --git a/pixi/assets/maps/test/5/17/22.jpg b/lib/pixi/assets/maps/test/5/17/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/22.jpg rename to lib/pixi/assets/maps/test/5/17/22.jpg diff --git a/pixi/assets/maps/test/5/17/23.jpg b/lib/pixi/assets/maps/test/5/17/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/23.jpg rename to lib/pixi/assets/maps/test/5/17/23.jpg diff --git a/pixi/assets/maps/test/5/17/24.jpg b/lib/pixi/assets/maps/test/5/17/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/24.jpg rename to lib/pixi/assets/maps/test/5/17/24.jpg diff --git a/pixi/assets/maps/test/5/17/25.jpg b/lib/pixi/assets/maps/test/5/17/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/25.jpg rename to lib/pixi/assets/maps/test/5/17/25.jpg diff --git a/pixi/assets/maps/test/5/17/26.jpg b/lib/pixi/assets/maps/test/5/17/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/26.jpg rename to lib/pixi/assets/maps/test/5/17/26.jpg diff --git a/pixi/assets/maps/test/5/17/27.jpg b/lib/pixi/assets/maps/test/5/17/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/27.jpg rename to lib/pixi/assets/maps/test/5/17/27.jpg diff --git a/pixi/assets/maps/test/5/17/28.jpg b/lib/pixi/assets/maps/test/5/17/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/28.jpg rename to lib/pixi/assets/maps/test/5/17/28.jpg diff --git a/pixi/assets/maps/test/5/17/29.jpg b/lib/pixi/assets/maps/test/5/17/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/29.jpg rename to lib/pixi/assets/maps/test/5/17/29.jpg diff --git a/pixi/assets/maps/test/5/17/3.jpg b/lib/pixi/assets/maps/test/5/17/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/3.jpg rename to lib/pixi/assets/maps/test/5/17/3.jpg diff --git a/pixi/assets/maps/test/5/17/30.jpg b/lib/pixi/assets/maps/test/5/17/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/30.jpg rename to lib/pixi/assets/maps/test/5/17/30.jpg diff --git a/pixi/assets/maps/test/5/17/31.jpg b/lib/pixi/assets/maps/test/5/17/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/31.jpg rename to lib/pixi/assets/maps/test/5/17/31.jpg diff --git a/pixi/assets/maps/test/5/17/4.jpg b/lib/pixi/assets/maps/test/5/17/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/4.jpg rename to lib/pixi/assets/maps/test/5/17/4.jpg diff --git a/pixi/assets/maps/test/5/17/5.jpg b/lib/pixi/assets/maps/test/5/17/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/5.jpg rename to lib/pixi/assets/maps/test/5/17/5.jpg diff --git a/pixi/assets/maps/test/5/17/6.jpg b/lib/pixi/assets/maps/test/5/17/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/6.jpg rename to lib/pixi/assets/maps/test/5/17/6.jpg diff --git a/pixi/assets/maps/test/5/17/7.jpg b/lib/pixi/assets/maps/test/5/17/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/7.jpg rename to lib/pixi/assets/maps/test/5/17/7.jpg diff --git a/pixi/assets/maps/test/5/17/8.jpg b/lib/pixi/assets/maps/test/5/17/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/8.jpg rename to lib/pixi/assets/maps/test/5/17/8.jpg diff --git a/pixi/assets/maps/test/5/17/9.jpg b/lib/pixi/assets/maps/test/5/17/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/17/9.jpg rename to lib/pixi/assets/maps/test/5/17/9.jpg diff --git a/pixi/assets/maps/test/5/18/0.jpg b/lib/pixi/assets/maps/test/5/18/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/0.jpg rename to lib/pixi/assets/maps/test/5/18/0.jpg diff --git a/pixi/assets/maps/test/5/18/1.jpg b/lib/pixi/assets/maps/test/5/18/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/1.jpg rename to lib/pixi/assets/maps/test/5/18/1.jpg diff --git a/pixi/assets/maps/test/5/18/10.jpg b/lib/pixi/assets/maps/test/5/18/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/10.jpg rename to lib/pixi/assets/maps/test/5/18/10.jpg diff --git a/pixi/assets/maps/test/5/18/11.jpg b/lib/pixi/assets/maps/test/5/18/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/11.jpg rename to lib/pixi/assets/maps/test/5/18/11.jpg diff --git a/pixi/assets/maps/test/5/18/12.jpg b/lib/pixi/assets/maps/test/5/18/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/12.jpg rename to lib/pixi/assets/maps/test/5/18/12.jpg diff --git a/pixi/assets/maps/test/5/18/13.jpg b/lib/pixi/assets/maps/test/5/18/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/13.jpg rename to lib/pixi/assets/maps/test/5/18/13.jpg diff --git a/pixi/assets/maps/test/5/18/14.jpg b/lib/pixi/assets/maps/test/5/18/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/14.jpg rename to lib/pixi/assets/maps/test/5/18/14.jpg diff --git a/pixi/assets/maps/test/5/18/15.jpg b/lib/pixi/assets/maps/test/5/18/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/15.jpg rename to lib/pixi/assets/maps/test/5/18/15.jpg diff --git a/pixi/assets/maps/test/5/18/16.jpg b/lib/pixi/assets/maps/test/5/18/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/16.jpg rename to lib/pixi/assets/maps/test/5/18/16.jpg diff --git a/pixi/assets/maps/test/5/18/17.jpg b/lib/pixi/assets/maps/test/5/18/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/17.jpg rename to lib/pixi/assets/maps/test/5/18/17.jpg diff --git a/pixi/assets/maps/test/5/18/18.jpg b/lib/pixi/assets/maps/test/5/18/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/18.jpg rename to lib/pixi/assets/maps/test/5/18/18.jpg diff --git a/pixi/assets/maps/test/5/18/19.jpg b/lib/pixi/assets/maps/test/5/18/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/19.jpg rename to lib/pixi/assets/maps/test/5/18/19.jpg diff --git a/pixi/assets/maps/test/5/18/2.jpg b/lib/pixi/assets/maps/test/5/18/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/2.jpg rename to lib/pixi/assets/maps/test/5/18/2.jpg diff --git a/pixi/assets/maps/test/5/18/20.jpg b/lib/pixi/assets/maps/test/5/18/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/20.jpg rename to lib/pixi/assets/maps/test/5/18/20.jpg diff --git a/pixi/assets/maps/test/5/18/21.jpg b/lib/pixi/assets/maps/test/5/18/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/21.jpg rename to lib/pixi/assets/maps/test/5/18/21.jpg diff --git a/pixi/assets/maps/test/5/18/22.jpg b/lib/pixi/assets/maps/test/5/18/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/22.jpg rename to lib/pixi/assets/maps/test/5/18/22.jpg diff --git a/pixi/assets/maps/test/5/18/23.jpg b/lib/pixi/assets/maps/test/5/18/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/23.jpg rename to lib/pixi/assets/maps/test/5/18/23.jpg diff --git a/pixi/assets/maps/test/5/18/24.jpg b/lib/pixi/assets/maps/test/5/18/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/24.jpg rename to lib/pixi/assets/maps/test/5/18/24.jpg diff --git a/pixi/assets/maps/test/5/18/25.jpg b/lib/pixi/assets/maps/test/5/18/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/25.jpg rename to lib/pixi/assets/maps/test/5/18/25.jpg diff --git a/pixi/assets/maps/test/5/18/26.jpg b/lib/pixi/assets/maps/test/5/18/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/26.jpg rename to lib/pixi/assets/maps/test/5/18/26.jpg diff --git a/pixi/assets/maps/test/5/18/27.jpg b/lib/pixi/assets/maps/test/5/18/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/27.jpg rename to lib/pixi/assets/maps/test/5/18/27.jpg diff --git a/pixi/assets/maps/test/5/18/28.jpg b/lib/pixi/assets/maps/test/5/18/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/28.jpg rename to lib/pixi/assets/maps/test/5/18/28.jpg diff --git a/pixi/assets/maps/test/5/18/29.jpg b/lib/pixi/assets/maps/test/5/18/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/29.jpg rename to lib/pixi/assets/maps/test/5/18/29.jpg diff --git a/pixi/assets/maps/test/5/18/3.jpg b/lib/pixi/assets/maps/test/5/18/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/3.jpg rename to lib/pixi/assets/maps/test/5/18/3.jpg diff --git a/pixi/assets/maps/test/5/18/30.jpg b/lib/pixi/assets/maps/test/5/18/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/30.jpg rename to lib/pixi/assets/maps/test/5/18/30.jpg diff --git a/pixi/assets/maps/test/5/18/31.jpg b/lib/pixi/assets/maps/test/5/18/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/31.jpg rename to lib/pixi/assets/maps/test/5/18/31.jpg diff --git a/pixi/assets/maps/test/5/18/4.jpg b/lib/pixi/assets/maps/test/5/18/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/4.jpg rename to lib/pixi/assets/maps/test/5/18/4.jpg diff --git a/pixi/assets/maps/test/5/18/5.jpg b/lib/pixi/assets/maps/test/5/18/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/5.jpg rename to lib/pixi/assets/maps/test/5/18/5.jpg diff --git a/pixi/assets/maps/test/5/18/6.jpg b/lib/pixi/assets/maps/test/5/18/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/6.jpg rename to lib/pixi/assets/maps/test/5/18/6.jpg diff --git a/pixi/assets/maps/test/5/18/7.jpg b/lib/pixi/assets/maps/test/5/18/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/7.jpg rename to lib/pixi/assets/maps/test/5/18/7.jpg diff --git a/pixi/assets/maps/test/5/18/8.jpg b/lib/pixi/assets/maps/test/5/18/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/8.jpg rename to lib/pixi/assets/maps/test/5/18/8.jpg diff --git a/pixi/assets/maps/test/5/18/9.jpg b/lib/pixi/assets/maps/test/5/18/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/18/9.jpg rename to lib/pixi/assets/maps/test/5/18/9.jpg diff --git a/pixi/assets/maps/test/5/19/0.jpg b/lib/pixi/assets/maps/test/5/19/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/0.jpg rename to lib/pixi/assets/maps/test/5/19/0.jpg diff --git a/pixi/assets/maps/test/5/19/1.jpg b/lib/pixi/assets/maps/test/5/19/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/1.jpg rename to lib/pixi/assets/maps/test/5/19/1.jpg diff --git a/pixi/assets/maps/test/5/19/10.jpg b/lib/pixi/assets/maps/test/5/19/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/10.jpg rename to lib/pixi/assets/maps/test/5/19/10.jpg diff --git a/pixi/assets/maps/test/5/19/11.jpg b/lib/pixi/assets/maps/test/5/19/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/11.jpg rename to lib/pixi/assets/maps/test/5/19/11.jpg diff --git a/pixi/assets/maps/test/5/19/12.jpg b/lib/pixi/assets/maps/test/5/19/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/12.jpg rename to lib/pixi/assets/maps/test/5/19/12.jpg diff --git a/pixi/assets/maps/test/5/19/13.jpg b/lib/pixi/assets/maps/test/5/19/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/13.jpg rename to lib/pixi/assets/maps/test/5/19/13.jpg diff --git a/pixi/assets/maps/test/5/19/14.jpg b/lib/pixi/assets/maps/test/5/19/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/14.jpg rename to lib/pixi/assets/maps/test/5/19/14.jpg diff --git a/pixi/assets/maps/test/5/19/15.jpg b/lib/pixi/assets/maps/test/5/19/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/15.jpg rename to lib/pixi/assets/maps/test/5/19/15.jpg diff --git a/pixi/assets/maps/test/5/19/16.jpg b/lib/pixi/assets/maps/test/5/19/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/16.jpg rename to lib/pixi/assets/maps/test/5/19/16.jpg diff --git a/pixi/assets/maps/test/5/19/17.jpg b/lib/pixi/assets/maps/test/5/19/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/17.jpg rename to lib/pixi/assets/maps/test/5/19/17.jpg diff --git a/pixi/assets/maps/test/5/19/18.jpg b/lib/pixi/assets/maps/test/5/19/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/18.jpg rename to lib/pixi/assets/maps/test/5/19/18.jpg diff --git a/pixi/assets/maps/test/5/19/19.jpg b/lib/pixi/assets/maps/test/5/19/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/19.jpg rename to lib/pixi/assets/maps/test/5/19/19.jpg diff --git a/pixi/assets/maps/test/5/19/2.jpg b/lib/pixi/assets/maps/test/5/19/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/2.jpg rename to lib/pixi/assets/maps/test/5/19/2.jpg diff --git a/pixi/assets/maps/test/5/19/20.jpg b/lib/pixi/assets/maps/test/5/19/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/20.jpg rename to lib/pixi/assets/maps/test/5/19/20.jpg diff --git a/pixi/assets/maps/test/5/19/21.jpg b/lib/pixi/assets/maps/test/5/19/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/21.jpg rename to lib/pixi/assets/maps/test/5/19/21.jpg diff --git a/pixi/assets/maps/test/5/19/22.jpg b/lib/pixi/assets/maps/test/5/19/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/22.jpg rename to lib/pixi/assets/maps/test/5/19/22.jpg diff --git a/pixi/assets/maps/test/5/19/23.jpg b/lib/pixi/assets/maps/test/5/19/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/23.jpg rename to lib/pixi/assets/maps/test/5/19/23.jpg diff --git a/pixi/assets/maps/test/5/19/24.jpg b/lib/pixi/assets/maps/test/5/19/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/24.jpg rename to lib/pixi/assets/maps/test/5/19/24.jpg diff --git a/pixi/assets/maps/test/5/19/25.jpg b/lib/pixi/assets/maps/test/5/19/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/25.jpg rename to lib/pixi/assets/maps/test/5/19/25.jpg diff --git a/pixi/assets/maps/test/5/19/26.jpg b/lib/pixi/assets/maps/test/5/19/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/26.jpg rename to lib/pixi/assets/maps/test/5/19/26.jpg diff --git a/pixi/assets/maps/test/5/19/27.jpg b/lib/pixi/assets/maps/test/5/19/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/27.jpg rename to lib/pixi/assets/maps/test/5/19/27.jpg diff --git a/pixi/assets/maps/test/5/19/28.jpg b/lib/pixi/assets/maps/test/5/19/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/28.jpg rename to lib/pixi/assets/maps/test/5/19/28.jpg diff --git a/pixi/assets/maps/test/5/19/29.jpg b/lib/pixi/assets/maps/test/5/19/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/29.jpg rename to lib/pixi/assets/maps/test/5/19/29.jpg diff --git a/pixi/assets/maps/test/5/19/3.jpg b/lib/pixi/assets/maps/test/5/19/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/3.jpg rename to lib/pixi/assets/maps/test/5/19/3.jpg diff --git a/pixi/assets/maps/test/5/19/30.jpg b/lib/pixi/assets/maps/test/5/19/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/30.jpg rename to lib/pixi/assets/maps/test/5/19/30.jpg diff --git a/pixi/assets/maps/test/5/19/31.jpg b/lib/pixi/assets/maps/test/5/19/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/31.jpg rename to lib/pixi/assets/maps/test/5/19/31.jpg diff --git a/pixi/assets/maps/test/5/19/4.jpg b/lib/pixi/assets/maps/test/5/19/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/4.jpg rename to lib/pixi/assets/maps/test/5/19/4.jpg diff --git a/pixi/assets/maps/test/5/19/5.jpg b/lib/pixi/assets/maps/test/5/19/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/5.jpg rename to lib/pixi/assets/maps/test/5/19/5.jpg diff --git a/pixi/assets/maps/test/5/19/6.jpg b/lib/pixi/assets/maps/test/5/19/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/6.jpg rename to lib/pixi/assets/maps/test/5/19/6.jpg diff --git a/pixi/assets/maps/test/5/19/7.jpg b/lib/pixi/assets/maps/test/5/19/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/7.jpg rename to lib/pixi/assets/maps/test/5/19/7.jpg diff --git a/pixi/assets/maps/test/5/19/8.jpg b/lib/pixi/assets/maps/test/5/19/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/8.jpg rename to lib/pixi/assets/maps/test/5/19/8.jpg diff --git a/pixi/assets/maps/test/5/19/9.jpg b/lib/pixi/assets/maps/test/5/19/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/19/9.jpg rename to lib/pixi/assets/maps/test/5/19/9.jpg diff --git a/pixi/assets/maps/test/5/2/0.jpg b/lib/pixi/assets/maps/test/5/2/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/0.jpg rename to lib/pixi/assets/maps/test/5/2/0.jpg diff --git a/pixi/assets/maps/test/5/2/1.jpg b/lib/pixi/assets/maps/test/5/2/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/1.jpg rename to lib/pixi/assets/maps/test/5/2/1.jpg diff --git a/pixi/assets/maps/test/5/2/10.jpg b/lib/pixi/assets/maps/test/5/2/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/10.jpg rename to lib/pixi/assets/maps/test/5/2/10.jpg diff --git a/pixi/assets/maps/test/5/2/11.jpg b/lib/pixi/assets/maps/test/5/2/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/11.jpg rename to lib/pixi/assets/maps/test/5/2/11.jpg diff --git a/pixi/assets/maps/test/5/2/12.jpg b/lib/pixi/assets/maps/test/5/2/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/12.jpg rename to lib/pixi/assets/maps/test/5/2/12.jpg diff --git a/pixi/assets/maps/test/5/2/13.jpg b/lib/pixi/assets/maps/test/5/2/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/13.jpg rename to lib/pixi/assets/maps/test/5/2/13.jpg diff --git a/pixi/assets/maps/test/5/2/14.jpg b/lib/pixi/assets/maps/test/5/2/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/14.jpg rename to lib/pixi/assets/maps/test/5/2/14.jpg diff --git a/pixi/assets/maps/test/5/2/15.jpg b/lib/pixi/assets/maps/test/5/2/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/15.jpg rename to lib/pixi/assets/maps/test/5/2/15.jpg diff --git a/pixi/assets/maps/test/5/2/16.jpg b/lib/pixi/assets/maps/test/5/2/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/16.jpg rename to lib/pixi/assets/maps/test/5/2/16.jpg diff --git a/pixi/assets/maps/test/5/2/17.jpg b/lib/pixi/assets/maps/test/5/2/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/17.jpg rename to lib/pixi/assets/maps/test/5/2/17.jpg diff --git a/pixi/assets/maps/test/5/2/18.jpg b/lib/pixi/assets/maps/test/5/2/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/18.jpg rename to lib/pixi/assets/maps/test/5/2/18.jpg diff --git a/pixi/assets/maps/test/5/2/19.jpg b/lib/pixi/assets/maps/test/5/2/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/19.jpg rename to lib/pixi/assets/maps/test/5/2/19.jpg diff --git a/pixi/assets/maps/test/5/2/2.jpg b/lib/pixi/assets/maps/test/5/2/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/2.jpg rename to lib/pixi/assets/maps/test/5/2/2.jpg diff --git a/pixi/assets/maps/test/5/2/20.jpg b/lib/pixi/assets/maps/test/5/2/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/20.jpg rename to lib/pixi/assets/maps/test/5/2/20.jpg diff --git a/pixi/assets/maps/test/5/2/21.jpg b/lib/pixi/assets/maps/test/5/2/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/21.jpg rename to lib/pixi/assets/maps/test/5/2/21.jpg diff --git a/pixi/assets/maps/test/5/2/22.jpg b/lib/pixi/assets/maps/test/5/2/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/22.jpg rename to lib/pixi/assets/maps/test/5/2/22.jpg diff --git a/pixi/assets/maps/test/5/2/23.jpg b/lib/pixi/assets/maps/test/5/2/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/23.jpg rename to lib/pixi/assets/maps/test/5/2/23.jpg diff --git a/pixi/assets/maps/test/5/2/24.jpg b/lib/pixi/assets/maps/test/5/2/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/24.jpg rename to lib/pixi/assets/maps/test/5/2/24.jpg diff --git a/pixi/assets/maps/test/5/2/25.jpg b/lib/pixi/assets/maps/test/5/2/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/25.jpg rename to lib/pixi/assets/maps/test/5/2/25.jpg diff --git a/pixi/assets/maps/test/5/2/26.jpg b/lib/pixi/assets/maps/test/5/2/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/26.jpg rename to lib/pixi/assets/maps/test/5/2/26.jpg diff --git a/pixi/assets/maps/test/5/2/27.jpg b/lib/pixi/assets/maps/test/5/2/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/27.jpg rename to lib/pixi/assets/maps/test/5/2/27.jpg diff --git a/pixi/assets/maps/test/5/2/28.jpg b/lib/pixi/assets/maps/test/5/2/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/28.jpg rename to lib/pixi/assets/maps/test/5/2/28.jpg diff --git a/pixi/assets/maps/test/5/2/29.jpg b/lib/pixi/assets/maps/test/5/2/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/29.jpg rename to lib/pixi/assets/maps/test/5/2/29.jpg diff --git a/pixi/assets/maps/test/5/2/3.jpg b/lib/pixi/assets/maps/test/5/2/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/3.jpg rename to lib/pixi/assets/maps/test/5/2/3.jpg diff --git a/pixi/assets/maps/test/5/2/30.jpg b/lib/pixi/assets/maps/test/5/2/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/30.jpg rename to lib/pixi/assets/maps/test/5/2/30.jpg diff --git a/pixi/assets/maps/test/5/2/31.jpg b/lib/pixi/assets/maps/test/5/2/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/31.jpg rename to lib/pixi/assets/maps/test/5/2/31.jpg diff --git a/pixi/assets/maps/test/5/2/4.jpg b/lib/pixi/assets/maps/test/5/2/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/4.jpg rename to lib/pixi/assets/maps/test/5/2/4.jpg diff --git a/pixi/assets/maps/test/5/2/5.jpg b/lib/pixi/assets/maps/test/5/2/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/5.jpg rename to lib/pixi/assets/maps/test/5/2/5.jpg diff --git a/pixi/assets/maps/test/5/2/6.jpg b/lib/pixi/assets/maps/test/5/2/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/6.jpg rename to lib/pixi/assets/maps/test/5/2/6.jpg diff --git a/pixi/assets/maps/test/5/2/7.jpg b/lib/pixi/assets/maps/test/5/2/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/7.jpg rename to lib/pixi/assets/maps/test/5/2/7.jpg diff --git a/pixi/assets/maps/test/5/2/8.jpg b/lib/pixi/assets/maps/test/5/2/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/8.jpg rename to lib/pixi/assets/maps/test/5/2/8.jpg diff --git a/pixi/assets/maps/test/5/2/9.jpg b/lib/pixi/assets/maps/test/5/2/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/2/9.jpg rename to lib/pixi/assets/maps/test/5/2/9.jpg diff --git a/pixi/assets/maps/test/5/20/0.jpg b/lib/pixi/assets/maps/test/5/20/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/0.jpg rename to lib/pixi/assets/maps/test/5/20/0.jpg diff --git a/pixi/assets/maps/test/5/20/1.jpg b/lib/pixi/assets/maps/test/5/20/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/1.jpg rename to lib/pixi/assets/maps/test/5/20/1.jpg diff --git a/pixi/assets/maps/test/5/20/10.jpg b/lib/pixi/assets/maps/test/5/20/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/10.jpg rename to lib/pixi/assets/maps/test/5/20/10.jpg diff --git a/pixi/assets/maps/test/5/20/11.jpg b/lib/pixi/assets/maps/test/5/20/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/11.jpg rename to lib/pixi/assets/maps/test/5/20/11.jpg diff --git a/pixi/assets/maps/test/5/20/12.jpg b/lib/pixi/assets/maps/test/5/20/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/12.jpg rename to lib/pixi/assets/maps/test/5/20/12.jpg diff --git a/pixi/assets/maps/test/5/20/13.jpg b/lib/pixi/assets/maps/test/5/20/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/13.jpg rename to lib/pixi/assets/maps/test/5/20/13.jpg diff --git a/pixi/assets/maps/test/5/20/14.jpg b/lib/pixi/assets/maps/test/5/20/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/14.jpg rename to lib/pixi/assets/maps/test/5/20/14.jpg diff --git a/pixi/assets/maps/test/5/20/15.jpg b/lib/pixi/assets/maps/test/5/20/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/15.jpg rename to lib/pixi/assets/maps/test/5/20/15.jpg diff --git a/pixi/assets/maps/test/5/20/16.jpg b/lib/pixi/assets/maps/test/5/20/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/16.jpg rename to lib/pixi/assets/maps/test/5/20/16.jpg diff --git a/pixi/assets/maps/test/5/20/17.jpg b/lib/pixi/assets/maps/test/5/20/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/17.jpg rename to lib/pixi/assets/maps/test/5/20/17.jpg diff --git a/pixi/assets/maps/test/5/20/18.jpg b/lib/pixi/assets/maps/test/5/20/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/18.jpg rename to lib/pixi/assets/maps/test/5/20/18.jpg diff --git a/pixi/assets/maps/test/5/20/19.jpg b/lib/pixi/assets/maps/test/5/20/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/19.jpg rename to lib/pixi/assets/maps/test/5/20/19.jpg diff --git a/pixi/assets/maps/test/5/20/2.jpg b/lib/pixi/assets/maps/test/5/20/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/2.jpg rename to lib/pixi/assets/maps/test/5/20/2.jpg diff --git a/pixi/assets/maps/test/5/20/20.jpg b/lib/pixi/assets/maps/test/5/20/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/20.jpg rename to lib/pixi/assets/maps/test/5/20/20.jpg diff --git a/pixi/assets/maps/test/5/20/21.jpg b/lib/pixi/assets/maps/test/5/20/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/21.jpg rename to lib/pixi/assets/maps/test/5/20/21.jpg diff --git a/pixi/assets/maps/test/5/20/22.jpg b/lib/pixi/assets/maps/test/5/20/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/22.jpg rename to lib/pixi/assets/maps/test/5/20/22.jpg diff --git a/pixi/assets/maps/test/5/20/23.jpg b/lib/pixi/assets/maps/test/5/20/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/23.jpg rename to lib/pixi/assets/maps/test/5/20/23.jpg diff --git a/pixi/assets/maps/test/5/20/24.jpg b/lib/pixi/assets/maps/test/5/20/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/24.jpg rename to lib/pixi/assets/maps/test/5/20/24.jpg diff --git a/pixi/assets/maps/test/5/20/25.jpg b/lib/pixi/assets/maps/test/5/20/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/25.jpg rename to lib/pixi/assets/maps/test/5/20/25.jpg diff --git a/pixi/assets/maps/test/5/20/26.jpg b/lib/pixi/assets/maps/test/5/20/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/26.jpg rename to lib/pixi/assets/maps/test/5/20/26.jpg diff --git a/pixi/assets/maps/test/5/20/27.jpg b/lib/pixi/assets/maps/test/5/20/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/27.jpg rename to lib/pixi/assets/maps/test/5/20/27.jpg diff --git a/pixi/assets/maps/test/5/20/28.jpg b/lib/pixi/assets/maps/test/5/20/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/28.jpg rename to lib/pixi/assets/maps/test/5/20/28.jpg diff --git a/pixi/assets/maps/test/5/20/29.jpg b/lib/pixi/assets/maps/test/5/20/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/29.jpg rename to lib/pixi/assets/maps/test/5/20/29.jpg diff --git a/pixi/assets/maps/test/5/20/3.jpg b/lib/pixi/assets/maps/test/5/20/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/3.jpg rename to lib/pixi/assets/maps/test/5/20/3.jpg diff --git a/pixi/assets/maps/test/5/20/30.jpg b/lib/pixi/assets/maps/test/5/20/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/30.jpg rename to lib/pixi/assets/maps/test/5/20/30.jpg diff --git a/pixi/assets/maps/test/5/20/31.jpg b/lib/pixi/assets/maps/test/5/20/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/31.jpg rename to lib/pixi/assets/maps/test/5/20/31.jpg diff --git a/pixi/assets/maps/test/5/20/4.jpg b/lib/pixi/assets/maps/test/5/20/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/4.jpg rename to lib/pixi/assets/maps/test/5/20/4.jpg diff --git a/pixi/assets/maps/test/5/20/5.jpg b/lib/pixi/assets/maps/test/5/20/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/5.jpg rename to lib/pixi/assets/maps/test/5/20/5.jpg diff --git a/pixi/assets/maps/test/5/20/6.jpg b/lib/pixi/assets/maps/test/5/20/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/6.jpg rename to lib/pixi/assets/maps/test/5/20/6.jpg diff --git a/pixi/assets/maps/test/5/20/7.jpg b/lib/pixi/assets/maps/test/5/20/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/7.jpg rename to lib/pixi/assets/maps/test/5/20/7.jpg diff --git a/pixi/assets/maps/test/5/20/8.jpg b/lib/pixi/assets/maps/test/5/20/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/8.jpg rename to lib/pixi/assets/maps/test/5/20/8.jpg diff --git a/pixi/assets/maps/test/5/20/9.jpg b/lib/pixi/assets/maps/test/5/20/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/20/9.jpg rename to lib/pixi/assets/maps/test/5/20/9.jpg diff --git a/pixi/assets/maps/test/5/21/0.jpg b/lib/pixi/assets/maps/test/5/21/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/0.jpg rename to lib/pixi/assets/maps/test/5/21/0.jpg diff --git a/pixi/assets/maps/test/5/21/1.jpg b/lib/pixi/assets/maps/test/5/21/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/1.jpg rename to lib/pixi/assets/maps/test/5/21/1.jpg diff --git a/pixi/assets/maps/test/5/21/10.jpg b/lib/pixi/assets/maps/test/5/21/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/10.jpg rename to lib/pixi/assets/maps/test/5/21/10.jpg diff --git a/pixi/assets/maps/test/5/21/11.jpg b/lib/pixi/assets/maps/test/5/21/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/11.jpg rename to lib/pixi/assets/maps/test/5/21/11.jpg diff --git a/pixi/assets/maps/test/5/21/12.jpg b/lib/pixi/assets/maps/test/5/21/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/12.jpg rename to lib/pixi/assets/maps/test/5/21/12.jpg diff --git a/pixi/assets/maps/test/5/21/13.jpg b/lib/pixi/assets/maps/test/5/21/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/13.jpg rename to lib/pixi/assets/maps/test/5/21/13.jpg diff --git a/pixi/assets/maps/test/5/21/14.jpg b/lib/pixi/assets/maps/test/5/21/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/14.jpg rename to lib/pixi/assets/maps/test/5/21/14.jpg diff --git a/pixi/assets/maps/test/5/21/15.jpg b/lib/pixi/assets/maps/test/5/21/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/15.jpg rename to lib/pixi/assets/maps/test/5/21/15.jpg diff --git a/pixi/assets/maps/test/5/21/16.jpg b/lib/pixi/assets/maps/test/5/21/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/16.jpg rename to lib/pixi/assets/maps/test/5/21/16.jpg diff --git a/pixi/assets/maps/test/5/21/17.jpg b/lib/pixi/assets/maps/test/5/21/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/17.jpg rename to lib/pixi/assets/maps/test/5/21/17.jpg diff --git a/pixi/assets/maps/test/5/21/18.jpg b/lib/pixi/assets/maps/test/5/21/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/18.jpg rename to lib/pixi/assets/maps/test/5/21/18.jpg diff --git a/pixi/assets/maps/test/5/21/19.jpg b/lib/pixi/assets/maps/test/5/21/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/19.jpg rename to lib/pixi/assets/maps/test/5/21/19.jpg diff --git a/pixi/assets/maps/test/5/21/2.jpg b/lib/pixi/assets/maps/test/5/21/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/2.jpg rename to lib/pixi/assets/maps/test/5/21/2.jpg diff --git a/pixi/assets/maps/test/5/21/20.jpg b/lib/pixi/assets/maps/test/5/21/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/20.jpg rename to lib/pixi/assets/maps/test/5/21/20.jpg diff --git a/pixi/assets/maps/test/5/21/21.jpg b/lib/pixi/assets/maps/test/5/21/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/21.jpg rename to lib/pixi/assets/maps/test/5/21/21.jpg diff --git a/pixi/assets/maps/test/5/21/22.jpg b/lib/pixi/assets/maps/test/5/21/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/22.jpg rename to lib/pixi/assets/maps/test/5/21/22.jpg diff --git a/pixi/assets/maps/test/5/21/23.jpg b/lib/pixi/assets/maps/test/5/21/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/23.jpg rename to lib/pixi/assets/maps/test/5/21/23.jpg diff --git a/pixi/assets/maps/test/5/21/24.jpg b/lib/pixi/assets/maps/test/5/21/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/24.jpg rename to lib/pixi/assets/maps/test/5/21/24.jpg diff --git a/pixi/assets/maps/test/5/21/25.jpg b/lib/pixi/assets/maps/test/5/21/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/25.jpg rename to lib/pixi/assets/maps/test/5/21/25.jpg diff --git a/pixi/assets/maps/test/5/21/26.jpg b/lib/pixi/assets/maps/test/5/21/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/26.jpg rename to lib/pixi/assets/maps/test/5/21/26.jpg diff --git a/pixi/assets/maps/test/5/21/27.jpg b/lib/pixi/assets/maps/test/5/21/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/27.jpg rename to lib/pixi/assets/maps/test/5/21/27.jpg diff --git a/pixi/assets/maps/test/5/21/28.jpg b/lib/pixi/assets/maps/test/5/21/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/28.jpg rename to lib/pixi/assets/maps/test/5/21/28.jpg diff --git a/pixi/assets/maps/test/5/21/29.jpg b/lib/pixi/assets/maps/test/5/21/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/29.jpg rename to lib/pixi/assets/maps/test/5/21/29.jpg diff --git a/pixi/assets/maps/test/5/21/3.jpg b/lib/pixi/assets/maps/test/5/21/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/3.jpg rename to lib/pixi/assets/maps/test/5/21/3.jpg diff --git a/pixi/assets/maps/test/5/21/30.jpg b/lib/pixi/assets/maps/test/5/21/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/30.jpg rename to lib/pixi/assets/maps/test/5/21/30.jpg diff --git a/pixi/assets/maps/test/5/21/31.jpg b/lib/pixi/assets/maps/test/5/21/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/31.jpg rename to lib/pixi/assets/maps/test/5/21/31.jpg diff --git a/pixi/assets/maps/test/5/21/4.jpg b/lib/pixi/assets/maps/test/5/21/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/4.jpg rename to lib/pixi/assets/maps/test/5/21/4.jpg diff --git a/pixi/assets/maps/test/5/21/5.jpg b/lib/pixi/assets/maps/test/5/21/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/5.jpg rename to lib/pixi/assets/maps/test/5/21/5.jpg diff --git a/pixi/assets/maps/test/5/21/6.jpg b/lib/pixi/assets/maps/test/5/21/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/6.jpg rename to lib/pixi/assets/maps/test/5/21/6.jpg diff --git a/pixi/assets/maps/test/5/21/7.jpg b/lib/pixi/assets/maps/test/5/21/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/7.jpg rename to lib/pixi/assets/maps/test/5/21/7.jpg diff --git a/pixi/assets/maps/test/5/21/8.jpg b/lib/pixi/assets/maps/test/5/21/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/8.jpg rename to lib/pixi/assets/maps/test/5/21/8.jpg diff --git a/pixi/assets/maps/test/5/21/9.jpg b/lib/pixi/assets/maps/test/5/21/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/21/9.jpg rename to lib/pixi/assets/maps/test/5/21/9.jpg diff --git a/pixi/assets/maps/test/5/22/0.jpg b/lib/pixi/assets/maps/test/5/22/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/0.jpg rename to lib/pixi/assets/maps/test/5/22/0.jpg diff --git a/pixi/assets/maps/test/5/22/1.jpg b/lib/pixi/assets/maps/test/5/22/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/1.jpg rename to lib/pixi/assets/maps/test/5/22/1.jpg diff --git a/pixi/assets/maps/test/5/22/10.jpg b/lib/pixi/assets/maps/test/5/22/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/10.jpg rename to lib/pixi/assets/maps/test/5/22/10.jpg diff --git a/pixi/assets/maps/test/5/22/11.jpg b/lib/pixi/assets/maps/test/5/22/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/11.jpg rename to lib/pixi/assets/maps/test/5/22/11.jpg diff --git a/pixi/assets/maps/test/5/22/12.jpg b/lib/pixi/assets/maps/test/5/22/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/12.jpg rename to lib/pixi/assets/maps/test/5/22/12.jpg diff --git a/pixi/assets/maps/test/5/22/13.jpg b/lib/pixi/assets/maps/test/5/22/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/13.jpg rename to lib/pixi/assets/maps/test/5/22/13.jpg diff --git a/pixi/assets/maps/test/5/22/14.jpg b/lib/pixi/assets/maps/test/5/22/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/14.jpg rename to lib/pixi/assets/maps/test/5/22/14.jpg diff --git a/pixi/assets/maps/test/5/22/15.jpg b/lib/pixi/assets/maps/test/5/22/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/15.jpg rename to lib/pixi/assets/maps/test/5/22/15.jpg diff --git a/pixi/assets/maps/test/5/22/16.jpg b/lib/pixi/assets/maps/test/5/22/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/16.jpg rename to lib/pixi/assets/maps/test/5/22/16.jpg diff --git a/pixi/assets/maps/test/5/22/17.jpg b/lib/pixi/assets/maps/test/5/22/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/17.jpg rename to lib/pixi/assets/maps/test/5/22/17.jpg diff --git a/pixi/assets/maps/test/5/22/18.jpg b/lib/pixi/assets/maps/test/5/22/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/18.jpg rename to lib/pixi/assets/maps/test/5/22/18.jpg diff --git a/pixi/assets/maps/test/5/22/19.jpg b/lib/pixi/assets/maps/test/5/22/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/19.jpg rename to lib/pixi/assets/maps/test/5/22/19.jpg diff --git a/pixi/assets/maps/test/5/22/2.jpg b/lib/pixi/assets/maps/test/5/22/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/2.jpg rename to lib/pixi/assets/maps/test/5/22/2.jpg diff --git a/pixi/assets/maps/test/5/22/20.jpg b/lib/pixi/assets/maps/test/5/22/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/20.jpg rename to lib/pixi/assets/maps/test/5/22/20.jpg diff --git a/pixi/assets/maps/test/5/22/21.jpg b/lib/pixi/assets/maps/test/5/22/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/21.jpg rename to lib/pixi/assets/maps/test/5/22/21.jpg diff --git a/pixi/assets/maps/test/5/22/22.jpg b/lib/pixi/assets/maps/test/5/22/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/22.jpg rename to lib/pixi/assets/maps/test/5/22/22.jpg diff --git a/pixi/assets/maps/test/5/22/23.jpg b/lib/pixi/assets/maps/test/5/22/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/23.jpg rename to lib/pixi/assets/maps/test/5/22/23.jpg diff --git a/pixi/assets/maps/test/5/22/24.jpg b/lib/pixi/assets/maps/test/5/22/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/24.jpg rename to lib/pixi/assets/maps/test/5/22/24.jpg diff --git a/pixi/assets/maps/test/5/22/25.jpg b/lib/pixi/assets/maps/test/5/22/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/25.jpg rename to lib/pixi/assets/maps/test/5/22/25.jpg diff --git a/pixi/assets/maps/test/5/22/26.jpg b/lib/pixi/assets/maps/test/5/22/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/26.jpg rename to lib/pixi/assets/maps/test/5/22/26.jpg diff --git a/pixi/assets/maps/test/5/22/27.jpg b/lib/pixi/assets/maps/test/5/22/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/27.jpg rename to lib/pixi/assets/maps/test/5/22/27.jpg diff --git a/pixi/assets/maps/test/5/22/28.jpg b/lib/pixi/assets/maps/test/5/22/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/28.jpg rename to lib/pixi/assets/maps/test/5/22/28.jpg diff --git a/pixi/assets/maps/test/5/22/29.jpg b/lib/pixi/assets/maps/test/5/22/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/29.jpg rename to lib/pixi/assets/maps/test/5/22/29.jpg diff --git a/pixi/assets/maps/test/5/22/3.jpg b/lib/pixi/assets/maps/test/5/22/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/3.jpg rename to lib/pixi/assets/maps/test/5/22/3.jpg diff --git a/pixi/assets/maps/test/5/22/30.jpg b/lib/pixi/assets/maps/test/5/22/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/30.jpg rename to lib/pixi/assets/maps/test/5/22/30.jpg diff --git a/pixi/assets/maps/test/5/22/31.jpg b/lib/pixi/assets/maps/test/5/22/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/31.jpg rename to lib/pixi/assets/maps/test/5/22/31.jpg diff --git a/pixi/assets/maps/test/5/22/4.jpg b/lib/pixi/assets/maps/test/5/22/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/4.jpg rename to lib/pixi/assets/maps/test/5/22/4.jpg diff --git a/pixi/assets/maps/test/5/22/5.jpg b/lib/pixi/assets/maps/test/5/22/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/5.jpg rename to lib/pixi/assets/maps/test/5/22/5.jpg diff --git a/pixi/assets/maps/test/5/22/6.jpg b/lib/pixi/assets/maps/test/5/22/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/6.jpg rename to lib/pixi/assets/maps/test/5/22/6.jpg diff --git a/pixi/assets/maps/test/5/22/7.jpg b/lib/pixi/assets/maps/test/5/22/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/7.jpg rename to lib/pixi/assets/maps/test/5/22/7.jpg diff --git a/pixi/assets/maps/test/5/22/8.jpg b/lib/pixi/assets/maps/test/5/22/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/8.jpg rename to lib/pixi/assets/maps/test/5/22/8.jpg diff --git a/pixi/assets/maps/test/5/22/9.jpg b/lib/pixi/assets/maps/test/5/22/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/22/9.jpg rename to lib/pixi/assets/maps/test/5/22/9.jpg diff --git a/pixi/assets/maps/test/5/23/0.jpg b/lib/pixi/assets/maps/test/5/23/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/0.jpg rename to lib/pixi/assets/maps/test/5/23/0.jpg diff --git a/pixi/assets/maps/test/5/23/1.jpg b/lib/pixi/assets/maps/test/5/23/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/1.jpg rename to lib/pixi/assets/maps/test/5/23/1.jpg diff --git a/pixi/assets/maps/test/5/23/10.jpg b/lib/pixi/assets/maps/test/5/23/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/10.jpg rename to lib/pixi/assets/maps/test/5/23/10.jpg diff --git a/pixi/assets/maps/test/5/23/11.jpg b/lib/pixi/assets/maps/test/5/23/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/11.jpg rename to lib/pixi/assets/maps/test/5/23/11.jpg diff --git a/pixi/assets/maps/test/5/23/12.jpg b/lib/pixi/assets/maps/test/5/23/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/12.jpg rename to lib/pixi/assets/maps/test/5/23/12.jpg diff --git a/pixi/assets/maps/test/5/23/13.jpg b/lib/pixi/assets/maps/test/5/23/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/13.jpg rename to lib/pixi/assets/maps/test/5/23/13.jpg diff --git a/pixi/assets/maps/test/5/23/14.jpg b/lib/pixi/assets/maps/test/5/23/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/14.jpg rename to lib/pixi/assets/maps/test/5/23/14.jpg diff --git a/pixi/assets/maps/test/5/23/15.jpg b/lib/pixi/assets/maps/test/5/23/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/15.jpg rename to lib/pixi/assets/maps/test/5/23/15.jpg diff --git a/pixi/assets/maps/test/5/23/16.jpg b/lib/pixi/assets/maps/test/5/23/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/16.jpg rename to lib/pixi/assets/maps/test/5/23/16.jpg diff --git a/pixi/assets/maps/test/5/23/17.jpg b/lib/pixi/assets/maps/test/5/23/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/17.jpg rename to lib/pixi/assets/maps/test/5/23/17.jpg diff --git a/pixi/assets/maps/test/5/23/18.jpg b/lib/pixi/assets/maps/test/5/23/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/18.jpg rename to lib/pixi/assets/maps/test/5/23/18.jpg diff --git a/pixi/assets/maps/test/5/23/19.jpg b/lib/pixi/assets/maps/test/5/23/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/19.jpg rename to lib/pixi/assets/maps/test/5/23/19.jpg diff --git a/pixi/assets/maps/test/5/23/2.jpg b/lib/pixi/assets/maps/test/5/23/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/2.jpg rename to lib/pixi/assets/maps/test/5/23/2.jpg diff --git a/pixi/assets/maps/test/5/23/20.jpg b/lib/pixi/assets/maps/test/5/23/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/20.jpg rename to lib/pixi/assets/maps/test/5/23/20.jpg diff --git a/pixi/assets/maps/test/5/23/21.jpg b/lib/pixi/assets/maps/test/5/23/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/21.jpg rename to lib/pixi/assets/maps/test/5/23/21.jpg diff --git a/pixi/assets/maps/test/5/23/22.jpg b/lib/pixi/assets/maps/test/5/23/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/22.jpg rename to lib/pixi/assets/maps/test/5/23/22.jpg diff --git a/pixi/assets/maps/test/5/23/23.jpg b/lib/pixi/assets/maps/test/5/23/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/23.jpg rename to lib/pixi/assets/maps/test/5/23/23.jpg diff --git a/pixi/assets/maps/test/5/23/24.jpg b/lib/pixi/assets/maps/test/5/23/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/24.jpg rename to lib/pixi/assets/maps/test/5/23/24.jpg diff --git a/pixi/assets/maps/test/5/23/25.jpg b/lib/pixi/assets/maps/test/5/23/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/25.jpg rename to lib/pixi/assets/maps/test/5/23/25.jpg diff --git a/pixi/assets/maps/test/5/23/26.jpg b/lib/pixi/assets/maps/test/5/23/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/26.jpg rename to lib/pixi/assets/maps/test/5/23/26.jpg diff --git a/pixi/assets/maps/test/5/23/27.jpg b/lib/pixi/assets/maps/test/5/23/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/27.jpg rename to lib/pixi/assets/maps/test/5/23/27.jpg diff --git a/pixi/assets/maps/test/5/23/28.jpg b/lib/pixi/assets/maps/test/5/23/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/28.jpg rename to lib/pixi/assets/maps/test/5/23/28.jpg diff --git a/pixi/assets/maps/test/5/23/29.jpg b/lib/pixi/assets/maps/test/5/23/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/29.jpg rename to lib/pixi/assets/maps/test/5/23/29.jpg diff --git a/pixi/assets/maps/test/5/23/3.jpg b/lib/pixi/assets/maps/test/5/23/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/3.jpg rename to lib/pixi/assets/maps/test/5/23/3.jpg diff --git a/pixi/assets/maps/test/5/23/30.jpg b/lib/pixi/assets/maps/test/5/23/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/30.jpg rename to lib/pixi/assets/maps/test/5/23/30.jpg diff --git a/pixi/assets/maps/test/5/23/31.jpg b/lib/pixi/assets/maps/test/5/23/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/31.jpg rename to lib/pixi/assets/maps/test/5/23/31.jpg diff --git a/pixi/assets/maps/test/5/23/4.jpg b/lib/pixi/assets/maps/test/5/23/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/4.jpg rename to lib/pixi/assets/maps/test/5/23/4.jpg diff --git a/pixi/assets/maps/test/5/23/5.jpg b/lib/pixi/assets/maps/test/5/23/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/5.jpg rename to lib/pixi/assets/maps/test/5/23/5.jpg diff --git a/pixi/assets/maps/test/5/23/6.jpg b/lib/pixi/assets/maps/test/5/23/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/6.jpg rename to lib/pixi/assets/maps/test/5/23/6.jpg diff --git a/pixi/assets/maps/test/5/23/7.jpg b/lib/pixi/assets/maps/test/5/23/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/7.jpg rename to lib/pixi/assets/maps/test/5/23/7.jpg diff --git a/pixi/assets/maps/test/5/23/8.jpg b/lib/pixi/assets/maps/test/5/23/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/8.jpg rename to lib/pixi/assets/maps/test/5/23/8.jpg diff --git a/pixi/assets/maps/test/5/23/9.jpg b/lib/pixi/assets/maps/test/5/23/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/23/9.jpg rename to lib/pixi/assets/maps/test/5/23/9.jpg diff --git a/pixi/assets/maps/test/5/24/0.jpg b/lib/pixi/assets/maps/test/5/24/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/0.jpg rename to lib/pixi/assets/maps/test/5/24/0.jpg diff --git a/pixi/assets/maps/test/5/24/1.jpg b/lib/pixi/assets/maps/test/5/24/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/1.jpg rename to lib/pixi/assets/maps/test/5/24/1.jpg diff --git a/pixi/assets/maps/test/5/24/10.jpg b/lib/pixi/assets/maps/test/5/24/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/10.jpg rename to lib/pixi/assets/maps/test/5/24/10.jpg diff --git a/pixi/assets/maps/test/5/24/11.jpg b/lib/pixi/assets/maps/test/5/24/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/11.jpg rename to lib/pixi/assets/maps/test/5/24/11.jpg diff --git a/pixi/assets/maps/test/5/24/12.jpg b/lib/pixi/assets/maps/test/5/24/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/12.jpg rename to lib/pixi/assets/maps/test/5/24/12.jpg diff --git a/pixi/assets/maps/test/5/24/13.jpg b/lib/pixi/assets/maps/test/5/24/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/13.jpg rename to lib/pixi/assets/maps/test/5/24/13.jpg diff --git a/pixi/assets/maps/test/5/24/14.jpg b/lib/pixi/assets/maps/test/5/24/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/14.jpg rename to lib/pixi/assets/maps/test/5/24/14.jpg diff --git a/pixi/assets/maps/test/5/24/15.jpg b/lib/pixi/assets/maps/test/5/24/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/15.jpg rename to lib/pixi/assets/maps/test/5/24/15.jpg diff --git a/pixi/assets/maps/test/5/24/16.jpg b/lib/pixi/assets/maps/test/5/24/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/16.jpg rename to lib/pixi/assets/maps/test/5/24/16.jpg diff --git a/pixi/assets/maps/test/5/24/17.jpg b/lib/pixi/assets/maps/test/5/24/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/17.jpg rename to lib/pixi/assets/maps/test/5/24/17.jpg diff --git a/pixi/assets/maps/test/5/24/18.jpg b/lib/pixi/assets/maps/test/5/24/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/18.jpg rename to lib/pixi/assets/maps/test/5/24/18.jpg diff --git a/pixi/assets/maps/test/5/24/19.jpg b/lib/pixi/assets/maps/test/5/24/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/19.jpg rename to lib/pixi/assets/maps/test/5/24/19.jpg diff --git a/pixi/assets/maps/test/5/24/2.jpg b/lib/pixi/assets/maps/test/5/24/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/2.jpg rename to lib/pixi/assets/maps/test/5/24/2.jpg diff --git a/pixi/assets/maps/test/5/24/20.jpg b/lib/pixi/assets/maps/test/5/24/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/20.jpg rename to lib/pixi/assets/maps/test/5/24/20.jpg diff --git a/pixi/assets/maps/test/5/24/21.jpg b/lib/pixi/assets/maps/test/5/24/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/21.jpg rename to lib/pixi/assets/maps/test/5/24/21.jpg diff --git a/pixi/assets/maps/test/5/24/22.jpg b/lib/pixi/assets/maps/test/5/24/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/22.jpg rename to lib/pixi/assets/maps/test/5/24/22.jpg diff --git a/pixi/assets/maps/test/5/24/23.jpg b/lib/pixi/assets/maps/test/5/24/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/23.jpg rename to lib/pixi/assets/maps/test/5/24/23.jpg diff --git a/pixi/assets/maps/test/5/24/24.jpg b/lib/pixi/assets/maps/test/5/24/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/24.jpg rename to lib/pixi/assets/maps/test/5/24/24.jpg diff --git a/pixi/assets/maps/test/5/24/25.jpg b/lib/pixi/assets/maps/test/5/24/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/25.jpg rename to lib/pixi/assets/maps/test/5/24/25.jpg diff --git a/pixi/assets/maps/test/5/24/26.jpg b/lib/pixi/assets/maps/test/5/24/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/26.jpg rename to lib/pixi/assets/maps/test/5/24/26.jpg diff --git a/pixi/assets/maps/test/5/24/27.jpg b/lib/pixi/assets/maps/test/5/24/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/27.jpg rename to lib/pixi/assets/maps/test/5/24/27.jpg diff --git a/pixi/assets/maps/test/5/24/28.jpg b/lib/pixi/assets/maps/test/5/24/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/28.jpg rename to lib/pixi/assets/maps/test/5/24/28.jpg diff --git a/pixi/assets/maps/test/5/24/29.jpg b/lib/pixi/assets/maps/test/5/24/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/29.jpg rename to lib/pixi/assets/maps/test/5/24/29.jpg diff --git a/pixi/assets/maps/test/5/24/3.jpg b/lib/pixi/assets/maps/test/5/24/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/3.jpg rename to lib/pixi/assets/maps/test/5/24/3.jpg diff --git a/pixi/assets/maps/test/5/24/30.jpg b/lib/pixi/assets/maps/test/5/24/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/30.jpg rename to lib/pixi/assets/maps/test/5/24/30.jpg diff --git a/pixi/assets/maps/test/5/24/31.jpg b/lib/pixi/assets/maps/test/5/24/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/31.jpg rename to lib/pixi/assets/maps/test/5/24/31.jpg diff --git a/pixi/assets/maps/test/5/24/4.jpg b/lib/pixi/assets/maps/test/5/24/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/4.jpg rename to lib/pixi/assets/maps/test/5/24/4.jpg diff --git a/pixi/assets/maps/test/5/24/5.jpg b/lib/pixi/assets/maps/test/5/24/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/5.jpg rename to lib/pixi/assets/maps/test/5/24/5.jpg diff --git a/pixi/assets/maps/test/5/24/6.jpg b/lib/pixi/assets/maps/test/5/24/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/6.jpg rename to lib/pixi/assets/maps/test/5/24/6.jpg diff --git a/pixi/assets/maps/test/5/24/7.jpg b/lib/pixi/assets/maps/test/5/24/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/7.jpg rename to lib/pixi/assets/maps/test/5/24/7.jpg diff --git a/pixi/assets/maps/test/5/24/8.jpg b/lib/pixi/assets/maps/test/5/24/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/8.jpg rename to lib/pixi/assets/maps/test/5/24/8.jpg diff --git a/pixi/assets/maps/test/5/24/9.jpg b/lib/pixi/assets/maps/test/5/24/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/24/9.jpg rename to lib/pixi/assets/maps/test/5/24/9.jpg diff --git a/pixi/assets/maps/test/5/25/0.jpg b/lib/pixi/assets/maps/test/5/25/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/0.jpg rename to lib/pixi/assets/maps/test/5/25/0.jpg diff --git a/pixi/assets/maps/test/5/25/1.jpg b/lib/pixi/assets/maps/test/5/25/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/1.jpg rename to lib/pixi/assets/maps/test/5/25/1.jpg diff --git a/pixi/assets/maps/test/5/25/10.jpg b/lib/pixi/assets/maps/test/5/25/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/10.jpg rename to lib/pixi/assets/maps/test/5/25/10.jpg diff --git a/pixi/assets/maps/test/5/25/11.jpg b/lib/pixi/assets/maps/test/5/25/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/11.jpg rename to lib/pixi/assets/maps/test/5/25/11.jpg diff --git a/pixi/assets/maps/test/5/25/12.jpg b/lib/pixi/assets/maps/test/5/25/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/12.jpg rename to lib/pixi/assets/maps/test/5/25/12.jpg diff --git a/pixi/assets/maps/test/5/25/13.jpg b/lib/pixi/assets/maps/test/5/25/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/13.jpg rename to lib/pixi/assets/maps/test/5/25/13.jpg diff --git a/pixi/assets/maps/test/5/25/14.jpg b/lib/pixi/assets/maps/test/5/25/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/14.jpg rename to lib/pixi/assets/maps/test/5/25/14.jpg diff --git a/pixi/assets/maps/test/5/25/15.jpg b/lib/pixi/assets/maps/test/5/25/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/15.jpg rename to lib/pixi/assets/maps/test/5/25/15.jpg diff --git a/pixi/assets/maps/test/5/25/16.jpg b/lib/pixi/assets/maps/test/5/25/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/16.jpg rename to lib/pixi/assets/maps/test/5/25/16.jpg diff --git a/pixi/assets/maps/test/5/25/17.jpg b/lib/pixi/assets/maps/test/5/25/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/17.jpg rename to lib/pixi/assets/maps/test/5/25/17.jpg diff --git a/pixi/assets/maps/test/5/25/18.jpg b/lib/pixi/assets/maps/test/5/25/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/18.jpg rename to lib/pixi/assets/maps/test/5/25/18.jpg diff --git a/pixi/assets/maps/test/5/25/19.jpg b/lib/pixi/assets/maps/test/5/25/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/19.jpg rename to lib/pixi/assets/maps/test/5/25/19.jpg diff --git a/pixi/assets/maps/test/5/25/2.jpg b/lib/pixi/assets/maps/test/5/25/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/2.jpg rename to lib/pixi/assets/maps/test/5/25/2.jpg diff --git a/pixi/assets/maps/test/5/25/20.jpg b/lib/pixi/assets/maps/test/5/25/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/20.jpg rename to lib/pixi/assets/maps/test/5/25/20.jpg diff --git a/pixi/assets/maps/test/5/25/21.jpg b/lib/pixi/assets/maps/test/5/25/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/21.jpg rename to lib/pixi/assets/maps/test/5/25/21.jpg diff --git a/pixi/assets/maps/test/5/25/22.jpg b/lib/pixi/assets/maps/test/5/25/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/22.jpg rename to lib/pixi/assets/maps/test/5/25/22.jpg diff --git a/pixi/assets/maps/test/5/25/23.jpg b/lib/pixi/assets/maps/test/5/25/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/23.jpg rename to lib/pixi/assets/maps/test/5/25/23.jpg diff --git a/pixi/assets/maps/test/5/25/24.jpg b/lib/pixi/assets/maps/test/5/25/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/24.jpg rename to lib/pixi/assets/maps/test/5/25/24.jpg diff --git a/pixi/assets/maps/test/5/25/25.jpg b/lib/pixi/assets/maps/test/5/25/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/25.jpg rename to lib/pixi/assets/maps/test/5/25/25.jpg diff --git a/pixi/assets/maps/test/5/25/26.jpg b/lib/pixi/assets/maps/test/5/25/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/26.jpg rename to lib/pixi/assets/maps/test/5/25/26.jpg diff --git a/pixi/assets/maps/test/5/25/27.jpg b/lib/pixi/assets/maps/test/5/25/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/27.jpg rename to lib/pixi/assets/maps/test/5/25/27.jpg diff --git a/pixi/assets/maps/test/5/25/28.jpg b/lib/pixi/assets/maps/test/5/25/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/28.jpg rename to lib/pixi/assets/maps/test/5/25/28.jpg diff --git a/pixi/assets/maps/test/5/25/29.jpg b/lib/pixi/assets/maps/test/5/25/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/29.jpg rename to lib/pixi/assets/maps/test/5/25/29.jpg diff --git a/pixi/assets/maps/test/5/25/3.jpg b/lib/pixi/assets/maps/test/5/25/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/3.jpg rename to lib/pixi/assets/maps/test/5/25/3.jpg diff --git a/pixi/assets/maps/test/5/25/30.jpg b/lib/pixi/assets/maps/test/5/25/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/30.jpg rename to lib/pixi/assets/maps/test/5/25/30.jpg diff --git a/pixi/assets/maps/test/5/25/31.jpg b/lib/pixi/assets/maps/test/5/25/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/31.jpg rename to lib/pixi/assets/maps/test/5/25/31.jpg diff --git a/pixi/assets/maps/test/5/25/4.jpg b/lib/pixi/assets/maps/test/5/25/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/4.jpg rename to lib/pixi/assets/maps/test/5/25/4.jpg diff --git a/pixi/assets/maps/test/5/25/5.jpg b/lib/pixi/assets/maps/test/5/25/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/5.jpg rename to lib/pixi/assets/maps/test/5/25/5.jpg diff --git a/pixi/assets/maps/test/5/25/6.jpg b/lib/pixi/assets/maps/test/5/25/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/6.jpg rename to lib/pixi/assets/maps/test/5/25/6.jpg diff --git a/pixi/assets/maps/test/5/25/7.jpg b/lib/pixi/assets/maps/test/5/25/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/7.jpg rename to lib/pixi/assets/maps/test/5/25/7.jpg diff --git a/pixi/assets/maps/test/5/25/8.jpg b/lib/pixi/assets/maps/test/5/25/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/8.jpg rename to lib/pixi/assets/maps/test/5/25/8.jpg diff --git a/pixi/assets/maps/test/5/25/9.jpg b/lib/pixi/assets/maps/test/5/25/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/25/9.jpg rename to lib/pixi/assets/maps/test/5/25/9.jpg diff --git a/pixi/assets/maps/test/5/26/0.jpg b/lib/pixi/assets/maps/test/5/26/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/0.jpg rename to lib/pixi/assets/maps/test/5/26/0.jpg diff --git a/pixi/assets/maps/test/5/26/1.jpg b/lib/pixi/assets/maps/test/5/26/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/1.jpg rename to lib/pixi/assets/maps/test/5/26/1.jpg diff --git a/pixi/assets/maps/test/5/26/10.jpg b/lib/pixi/assets/maps/test/5/26/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/10.jpg rename to lib/pixi/assets/maps/test/5/26/10.jpg diff --git a/pixi/assets/maps/test/5/26/11.jpg b/lib/pixi/assets/maps/test/5/26/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/11.jpg rename to lib/pixi/assets/maps/test/5/26/11.jpg diff --git a/pixi/assets/maps/test/5/26/12.jpg b/lib/pixi/assets/maps/test/5/26/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/12.jpg rename to lib/pixi/assets/maps/test/5/26/12.jpg diff --git a/pixi/assets/maps/test/5/26/13.jpg b/lib/pixi/assets/maps/test/5/26/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/13.jpg rename to lib/pixi/assets/maps/test/5/26/13.jpg diff --git a/pixi/assets/maps/test/5/26/14.jpg b/lib/pixi/assets/maps/test/5/26/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/14.jpg rename to lib/pixi/assets/maps/test/5/26/14.jpg diff --git a/pixi/assets/maps/test/5/26/15.jpg b/lib/pixi/assets/maps/test/5/26/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/15.jpg rename to lib/pixi/assets/maps/test/5/26/15.jpg diff --git a/pixi/assets/maps/test/5/26/16.jpg b/lib/pixi/assets/maps/test/5/26/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/16.jpg rename to lib/pixi/assets/maps/test/5/26/16.jpg diff --git a/pixi/assets/maps/test/5/26/17.jpg b/lib/pixi/assets/maps/test/5/26/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/17.jpg rename to lib/pixi/assets/maps/test/5/26/17.jpg diff --git a/pixi/assets/maps/test/5/26/18.jpg b/lib/pixi/assets/maps/test/5/26/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/18.jpg rename to lib/pixi/assets/maps/test/5/26/18.jpg diff --git a/pixi/assets/maps/test/5/26/19.jpg b/lib/pixi/assets/maps/test/5/26/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/19.jpg rename to lib/pixi/assets/maps/test/5/26/19.jpg diff --git a/pixi/assets/maps/test/5/26/2.jpg b/lib/pixi/assets/maps/test/5/26/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/2.jpg rename to lib/pixi/assets/maps/test/5/26/2.jpg diff --git a/pixi/assets/maps/test/5/26/20.jpg b/lib/pixi/assets/maps/test/5/26/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/20.jpg rename to lib/pixi/assets/maps/test/5/26/20.jpg diff --git a/pixi/assets/maps/test/5/26/21.jpg b/lib/pixi/assets/maps/test/5/26/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/21.jpg rename to lib/pixi/assets/maps/test/5/26/21.jpg diff --git a/pixi/assets/maps/test/5/26/22.jpg b/lib/pixi/assets/maps/test/5/26/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/22.jpg rename to lib/pixi/assets/maps/test/5/26/22.jpg diff --git a/pixi/assets/maps/test/5/26/23.jpg b/lib/pixi/assets/maps/test/5/26/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/23.jpg rename to lib/pixi/assets/maps/test/5/26/23.jpg diff --git a/pixi/assets/maps/test/5/26/24.jpg b/lib/pixi/assets/maps/test/5/26/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/24.jpg rename to lib/pixi/assets/maps/test/5/26/24.jpg diff --git a/pixi/assets/maps/test/5/26/25.jpg b/lib/pixi/assets/maps/test/5/26/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/25.jpg rename to lib/pixi/assets/maps/test/5/26/25.jpg diff --git a/pixi/assets/maps/test/5/26/26.jpg b/lib/pixi/assets/maps/test/5/26/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/26.jpg rename to lib/pixi/assets/maps/test/5/26/26.jpg diff --git a/pixi/assets/maps/test/5/26/27.jpg b/lib/pixi/assets/maps/test/5/26/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/27.jpg rename to lib/pixi/assets/maps/test/5/26/27.jpg diff --git a/pixi/assets/maps/test/5/26/28.jpg b/lib/pixi/assets/maps/test/5/26/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/28.jpg rename to lib/pixi/assets/maps/test/5/26/28.jpg diff --git a/pixi/assets/maps/test/5/26/29.jpg b/lib/pixi/assets/maps/test/5/26/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/29.jpg rename to lib/pixi/assets/maps/test/5/26/29.jpg diff --git a/pixi/assets/maps/test/5/26/3.jpg b/lib/pixi/assets/maps/test/5/26/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/3.jpg rename to lib/pixi/assets/maps/test/5/26/3.jpg diff --git a/pixi/assets/maps/test/5/26/30.jpg b/lib/pixi/assets/maps/test/5/26/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/30.jpg rename to lib/pixi/assets/maps/test/5/26/30.jpg diff --git a/pixi/assets/maps/test/5/26/31.jpg b/lib/pixi/assets/maps/test/5/26/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/31.jpg rename to lib/pixi/assets/maps/test/5/26/31.jpg diff --git a/pixi/assets/maps/test/5/26/4.jpg b/lib/pixi/assets/maps/test/5/26/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/4.jpg rename to lib/pixi/assets/maps/test/5/26/4.jpg diff --git a/pixi/assets/maps/test/5/26/5.jpg b/lib/pixi/assets/maps/test/5/26/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/5.jpg rename to lib/pixi/assets/maps/test/5/26/5.jpg diff --git a/pixi/assets/maps/test/5/26/6.jpg b/lib/pixi/assets/maps/test/5/26/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/6.jpg rename to lib/pixi/assets/maps/test/5/26/6.jpg diff --git a/pixi/assets/maps/test/5/26/7.jpg b/lib/pixi/assets/maps/test/5/26/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/7.jpg rename to lib/pixi/assets/maps/test/5/26/7.jpg diff --git a/pixi/assets/maps/test/5/26/8.jpg b/lib/pixi/assets/maps/test/5/26/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/8.jpg rename to lib/pixi/assets/maps/test/5/26/8.jpg diff --git a/pixi/assets/maps/test/5/26/9.jpg b/lib/pixi/assets/maps/test/5/26/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/26/9.jpg rename to lib/pixi/assets/maps/test/5/26/9.jpg diff --git a/pixi/assets/maps/test/5/27/0.jpg b/lib/pixi/assets/maps/test/5/27/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/0.jpg rename to lib/pixi/assets/maps/test/5/27/0.jpg diff --git a/pixi/assets/maps/test/5/27/1.jpg b/lib/pixi/assets/maps/test/5/27/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/1.jpg rename to lib/pixi/assets/maps/test/5/27/1.jpg diff --git a/pixi/assets/maps/test/5/27/10.jpg b/lib/pixi/assets/maps/test/5/27/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/10.jpg rename to lib/pixi/assets/maps/test/5/27/10.jpg diff --git a/pixi/assets/maps/test/5/27/11.jpg b/lib/pixi/assets/maps/test/5/27/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/11.jpg rename to lib/pixi/assets/maps/test/5/27/11.jpg diff --git a/pixi/assets/maps/test/5/27/12.jpg b/lib/pixi/assets/maps/test/5/27/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/12.jpg rename to lib/pixi/assets/maps/test/5/27/12.jpg diff --git a/pixi/assets/maps/test/5/27/13.jpg b/lib/pixi/assets/maps/test/5/27/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/13.jpg rename to lib/pixi/assets/maps/test/5/27/13.jpg diff --git a/pixi/assets/maps/test/5/27/14.jpg b/lib/pixi/assets/maps/test/5/27/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/14.jpg rename to lib/pixi/assets/maps/test/5/27/14.jpg diff --git a/pixi/assets/maps/test/5/27/15.jpg b/lib/pixi/assets/maps/test/5/27/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/15.jpg rename to lib/pixi/assets/maps/test/5/27/15.jpg diff --git a/pixi/assets/maps/test/5/27/16.jpg b/lib/pixi/assets/maps/test/5/27/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/16.jpg rename to lib/pixi/assets/maps/test/5/27/16.jpg diff --git a/pixi/assets/maps/test/5/27/17.jpg b/lib/pixi/assets/maps/test/5/27/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/17.jpg rename to lib/pixi/assets/maps/test/5/27/17.jpg diff --git a/pixi/assets/maps/test/5/27/18.jpg b/lib/pixi/assets/maps/test/5/27/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/18.jpg rename to lib/pixi/assets/maps/test/5/27/18.jpg diff --git a/pixi/assets/maps/test/5/27/19.jpg b/lib/pixi/assets/maps/test/5/27/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/19.jpg rename to lib/pixi/assets/maps/test/5/27/19.jpg diff --git a/pixi/assets/maps/test/5/27/2.jpg b/lib/pixi/assets/maps/test/5/27/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/2.jpg rename to lib/pixi/assets/maps/test/5/27/2.jpg diff --git a/pixi/assets/maps/test/5/27/20.jpg b/lib/pixi/assets/maps/test/5/27/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/20.jpg rename to lib/pixi/assets/maps/test/5/27/20.jpg diff --git a/pixi/assets/maps/test/5/27/21.jpg b/lib/pixi/assets/maps/test/5/27/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/21.jpg rename to lib/pixi/assets/maps/test/5/27/21.jpg diff --git a/pixi/assets/maps/test/5/27/22.jpg b/lib/pixi/assets/maps/test/5/27/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/22.jpg rename to lib/pixi/assets/maps/test/5/27/22.jpg diff --git a/pixi/assets/maps/test/5/27/23.jpg b/lib/pixi/assets/maps/test/5/27/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/23.jpg rename to lib/pixi/assets/maps/test/5/27/23.jpg diff --git a/pixi/assets/maps/test/5/27/24.jpg b/lib/pixi/assets/maps/test/5/27/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/24.jpg rename to lib/pixi/assets/maps/test/5/27/24.jpg diff --git a/pixi/assets/maps/test/5/27/25.jpg b/lib/pixi/assets/maps/test/5/27/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/25.jpg rename to lib/pixi/assets/maps/test/5/27/25.jpg diff --git a/pixi/assets/maps/test/5/27/26.jpg b/lib/pixi/assets/maps/test/5/27/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/26.jpg rename to lib/pixi/assets/maps/test/5/27/26.jpg diff --git a/pixi/assets/maps/test/5/27/27.jpg b/lib/pixi/assets/maps/test/5/27/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/27.jpg rename to lib/pixi/assets/maps/test/5/27/27.jpg diff --git a/pixi/assets/maps/test/5/27/28.jpg b/lib/pixi/assets/maps/test/5/27/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/28.jpg rename to lib/pixi/assets/maps/test/5/27/28.jpg diff --git a/pixi/assets/maps/test/5/27/29.jpg b/lib/pixi/assets/maps/test/5/27/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/29.jpg rename to lib/pixi/assets/maps/test/5/27/29.jpg diff --git a/pixi/assets/maps/test/5/27/3.jpg b/lib/pixi/assets/maps/test/5/27/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/3.jpg rename to lib/pixi/assets/maps/test/5/27/3.jpg diff --git a/pixi/assets/maps/test/5/27/30.jpg b/lib/pixi/assets/maps/test/5/27/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/30.jpg rename to lib/pixi/assets/maps/test/5/27/30.jpg diff --git a/pixi/assets/maps/test/5/27/31.jpg b/lib/pixi/assets/maps/test/5/27/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/31.jpg rename to lib/pixi/assets/maps/test/5/27/31.jpg diff --git a/pixi/assets/maps/test/5/27/4.jpg b/lib/pixi/assets/maps/test/5/27/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/4.jpg rename to lib/pixi/assets/maps/test/5/27/4.jpg diff --git a/pixi/assets/maps/test/5/27/5.jpg b/lib/pixi/assets/maps/test/5/27/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/5.jpg rename to lib/pixi/assets/maps/test/5/27/5.jpg diff --git a/pixi/assets/maps/test/5/27/6.jpg b/lib/pixi/assets/maps/test/5/27/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/6.jpg rename to lib/pixi/assets/maps/test/5/27/6.jpg diff --git a/pixi/assets/maps/test/5/27/7.jpg b/lib/pixi/assets/maps/test/5/27/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/7.jpg rename to lib/pixi/assets/maps/test/5/27/7.jpg diff --git a/pixi/assets/maps/test/5/27/8.jpg b/lib/pixi/assets/maps/test/5/27/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/8.jpg rename to lib/pixi/assets/maps/test/5/27/8.jpg diff --git a/pixi/assets/maps/test/5/27/9.jpg b/lib/pixi/assets/maps/test/5/27/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/27/9.jpg rename to lib/pixi/assets/maps/test/5/27/9.jpg diff --git a/pixi/assets/maps/test/5/28/0.jpg b/lib/pixi/assets/maps/test/5/28/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/0.jpg rename to lib/pixi/assets/maps/test/5/28/0.jpg diff --git a/pixi/assets/maps/test/5/28/1.jpg b/lib/pixi/assets/maps/test/5/28/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/1.jpg rename to lib/pixi/assets/maps/test/5/28/1.jpg diff --git a/pixi/assets/maps/test/5/28/10.jpg b/lib/pixi/assets/maps/test/5/28/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/10.jpg rename to lib/pixi/assets/maps/test/5/28/10.jpg diff --git a/pixi/assets/maps/test/5/28/11.jpg b/lib/pixi/assets/maps/test/5/28/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/11.jpg rename to lib/pixi/assets/maps/test/5/28/11.jpg diff --git a/pixi/assets/maps/test/5/28/12.jpg b/lib/pixi/assets/maps/test/5/28/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/12.jpg rename to lib/pixi/assets/maps/test/5/28/12.jpg diff --git a/pixi/assets/maps/test/5/28/13.jpg b/lib/pixi/assets/maps/test/5/28/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/13.jpg rename to lib/pixi/assets/maps/test/5/28/13.jpg diff --git a/pixi/assets/maps/test/5/28/14.jpg b/lib/pixi/assets/maps/test/5/28/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/14.jpg rename to lib/pixi/assets/maps/test/5/28/14.jpg diff --git a/pixi/assets/maps/test/5/28/15.jpg b/lib/pixi/assets/maps/test/5/28/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/15.jpg rename to lib/pixi/assets/maps/test/5/28/15.jpg diff --git a/pixi/assets/maps/test/5/28/16.jpg b/lib/pixi/assets/maps/test/5/28/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/16.jpg rename to lib/pixi/assets/maps/test/5/28/16.jpg diff --git a/pixi/assets/maps/test/5/28/17.jpg b/lib/pixi/assets/maps/test/5/28/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/17.jpg rename to lib/pixi/assets/maps/test/5/28/17.jpg diff --git a/pixi/assets/maps/test/5/28/18.jpg b/lib/pixi/assets/maps/test/5/28/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/18.jpg rename to lib/pixi/assets/maps/test/5/28/18.jpg diff --git a/pixi/assets/maps/test/5/28/19.jpg b/lib/pixi/assets/maps/test/5/28/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/19.jpg rename to lib/pixi/assets/maps/test/5/28/19.jpg diff --git a/pixi/assets/maps/test/5/28/2.jpg b/lib/pixi/assets/maps/test/5/28/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/2.jpg rename to lib/pixi/assets/maps/test/5/28/2.jpg diff --git a/pixi/assets/maps/test/5/28/20.jpg b/lib/pixi/assets/maps/test/5/28/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/20.jpg rename to lib/pixi/assets/maps/test/5/28/20.jpg diff --git a/pixi/assets/maps/test/5/28/21.jpg b/lib/pixi/assets/maps/test/5/28/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/21.jpg rename to lib/pixi/assets/maps/test/5/28/21.jpg diff --git a/pixi/assets/maps/test/5/28/22.jpg b/lib/pixi/assets/maps/test/5/28/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/22.jpg rename to lib/pixi/assets/maps/test/5/28/22.jpg diff --git a/pixi/assets/maps/test/5/28/23.jpg b/lib/pixi/assets/maps/test/5/28/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/23.jpg rename to lib/pixi/assets/maps/test/5/28/23.jpg diff --git a/pixi/assets/maps/test/5/28/24.jpg b/lib/pixi/assets/maps/test/5/28/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/24.jpg rename to lib/pixi/assets/maps/test/5/28/24.jpg diff --git a/pixi/assets/maps/test/5/28/25.jpg b/lib/pixi/assets/maps/test/5/28/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/25.jpg rename to lib/pixi/assets/maps/test/5/28/25.jpg diff --git a/pixi/assets/maps/test/5/28/26.jpg b/lib/pixi/assets/maps/test/5/28/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/26.jpg rename to lib/pixi/assets/maps/test/5/28/26.jpg diff --git a/pixi/assets/maps/test/5/28/27.jpg b/lib/pixi/assets/maps/test/5/28/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/27.jpg rename to lib/pixi/assets/maps/test/5/28/27.jpg diff --git a/pixi/assets/maps/test/5/28/28.jpg b/lib/pixi/assets/maps/test/5/28/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/28.jpg rename to lib/pixi/assets/maps/test/5/28/28.jpg diff --git a/pixi/assets/maps/test/5/28/29.jpg b/lib/pixi/assets/maps/test/5/28/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/29.jpg rename to lib/pixi/assets/maps/test/5/28/29.jpg diff --git a/pixi/assets/maps/test/5/28/3.jpg b/lib/pixi/assets/maps/test/5/28/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/3.jpg rename to lib/pixi/assets/maps/test/5/28/3.jpg diff --git a/pixi/assets/maps/test/5/28/30.jpg b/lib/pixi/assets/maps/test/5/28/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/30.jpg rename to lib/pixi/assets/maps/test/5/28/30.jpg diff --git a/pixi/assets/maps/test/5/28/31.jpg b/lib/pixi/assets/maps/test/5/28/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/31.jpg rename to lib/pixi/assets/maps/test/5/28/31.jpg diff --git a/pixi/assets/maps/test/5/28/4.jpg b/lib/pixi/assets/maps/test/5/28/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/4.jpg rename to lib/pixi/assets/maps/test/5/28/4.jpg diff --git a/pixi/assets/maps/test/5/28/5.jpg b/lib/pixi/assets/maps/test/5/28/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/5.jpg rename to lib/pixi/assets/maps/test/5/28/5.jpg diff --git a/pixi/assets/maps/test/5/28/6.jpg b/lib/pixi/assets/maps/test/5/28/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/6.jpg rename to lib/pixi/assets/maps/test/5/28/6.jpg diff --git a/pixi/assets/maps/test/5/28/7.jpg b/lib/pixi/assets/maps/test/5/28/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/7.jpg rename to lib/pixi/assets/maps/test/5/28/7.jpg diff --git a/pixi/assets/maps/test/5/28/8.jpg b/lib/pixi/assets/maps/test/5/28/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/8.jpg rename to lib/pixi/assets/maps/test/5/28/8.jpg diff --git a/pixi/assets/maps/test/5/28/9.jpg b/lib/pixi/assets/maps/test/5/28/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/28/9.jpg rename to lib/pixi/assets/maps/test/5/28/9.jpg diff --git a/pixi/assets/maps/test/5/29/0.jpg b/lib/pixi/assets/maps/test/5/29/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/0.jpg rename to lib/pixi/assets/maps/test/5/29/0.jpg diff --git a/pixi/assets/maps/test/5/29/1.jpg b/lib/pixi/assets/maps/test/5/29/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/1.jpg rename to lib/pixi/assets/maps/test/5/29/1.jpg diff --git a/pixi/assets/maps/test/5/29/10.jpg b/lib/pixi/assets/maps/test/5/29/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/10.jpg rename to lib/pixi/assets/maps/test/5/29/10.jpg diff --git a/pixi/assets/maps/test/5/29/11.jpg b/lib/pixi/assets/maps/test/5/29/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/11.jpg rename to lib/pixi/assets/maps/test/5/29/11.jpg diff --git a/pixi/assets/maps/test/5/29/12.jpg b/lib/pixi/assets/maps/test/5/29/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/12.jpg rename to lib/pixi/assets/maps/test/5/29/12.jpg diff --git a/pixi/assets/maps/test/5/29/13.jpg b/lib/pixi/assets/maps/test/5/29/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/13.jpg rename to lib/pixi/assets/maps/test/5/29/13.jpg diff --git a/pixi/assets/maps/test/5/29/14.jpg b/lib/pixi/assets/maps/test/5/29/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/14.jpg rename to lib/pixi/assets/maps/test/5/29/14.jpg diff --git a/pixi/assets/maps/test/5/29/15.jpg b/lib/pixi/assets/maps/test/5/29/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/15.jpg rename to lib/pixi/assets/maps/test/5/29/15.jpg diff --git a/pixi/assets/maps/test/5/29/16.jpg b/lib/pixi/assets/maps/test/5/29/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/16.jpg rename to lib/pixi/assets/maps/test/5/29/16.jpg diff --git a/pixi/assets/maps/test/5/29/17.jpg b/lib/pixi/assets/maps/test/5/29/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/17.jpg rename to lib/pixi/assets/maps/test/5/29/17.jpg diff --git a/pixi/assets/maps/test/5/29/18.jpg b/lib/pixi/assets/maps/test/5/29/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/18.jpg rename to lib/pixi/assets/maps/test/5/29/18.jpg diff --git a/pixi/assets/maps/test/5/29/19.jpg b/lib/pixi/assets/maps/test/5/29/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/19.jpg rename to lib/pixi/assets/maps/test/5/29/19.jpg diff --git a/pixi/assets/maps/test/5/29/2.jpg b/lib/pixi/assets/maps/test/5/29/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/2.jpg rename to lib/pixi/assets/maps/test/5/29/2.jpg diff --git a/pixi/assets/maps/test/5/29/20.jpg b/lib/pixi/assets/maps/test/5/29/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/20.jpg rename to lib/pixi/assets/maps/test/5/29/20.jpg diff --git a/pixi/assets/maps/test/5/29/21.jpg b/lib/pixi/assets/maps/test/5/29/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/21.jpg rename to lib/pixi/assets/maps/test/5/29/21.jpg diff --git a/pixi/assets/maps/test/5/29/22.jpg b/lib/pixi/assets/maps/test/5/29/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/22.jpg rename to lib/pixi/assets/maps/test/5/29/22.jpg diff --git a/pixi/assets/maps/test/5/29/23.jpg b/lib/pixi/assets/maps/test/5/29/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/23.jpg rename to lib/pixi/assets/maps/test/5/29/23.jpg diff --git a/pixi/assets/maps/test/5/29/24.jpg b/lib/pixi/assets/maps/test/5/29/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/24.jpg rename to lib/pixi/assets/maps/test/5/29/24.jpg diff --git a/pixi/assets/maps/test/5/29/25.jpg b/lib/pixi/assets/maps/test/5/29/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/25.jpg rename to lib/pixi/assets/maps/test/5/29/25.jpg diff --git a/pixi/assets/maps/test/5/29/26.jpg b/lib/pixi/assets/maps/test/5/29/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/26.jpg rename to lib/pixi/assets/maps/test/5/29/26.jpg diff --git a/pixi/assets/maps/test/5/29/27.jpg b/lib/pixi/assets/maps/test/5/29/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/27.jpg rename to lib/pixi/assets/maps/test/5/29/27.jpg diff --git a/pixi/assets/maps/test/5/29/28.jpg b/lib/pixi/assets/maps/test/5/29/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/28.jpg rename to lib/pixi/assets/maps/test/5/29/28.jpg diff --git a/pixi/assets/maps/test/5/29/29.jpg b/lib/pixi/assets/maps/test/5/29/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/29.jpg rename to lib/pixi/assets/maps/test/5/29/29.jpg diff --git a/pixi/assets/maps/test/5/29/3.jpg b/lib/pixi/assets/maps/test/5/29/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/3.jpg rename to lib/pixi/assets/maps/test/5/29/3.jpg diff --git a/pixi/assets/maps/test/5/29/30.jpg b/lib/pixi/assets/maps/test/5/29/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/30.jpg rename to lib/pixi/assets/maps/test/5/29/30.jpg diff --git a/pixi/assets/maps/test/5/29/31.jpg b/lib/pixi/assets/maps/test/5/29/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/31.jpg rename to lib/pixi/assets/maps/test/5/29/31.jpg diff --git a/pixi/assets/maps/test/5/29/4.jpg b/lib/pixi/assets/maps/test/5/29/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/4.jpg rename to lib/pixi/assets/maps/test/5/29/4.jpg diff --git a/pixi/assets/maps/test/5/29/5.jpg b/lib/pixi/assets/maps/test/5/29/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/5.jpg rename to lib/pixi/assets/maps/test/5/29/5.jpg diff --git a/pixi/assets/maps/test/5/29/6.jpg b/lib/pixi/assets/maps/test/5/29/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/6.jpg rename to lib/pixi/assets/maps/test/5/29/6.jpg diff --git a/pixi/assets/maps/test/5/29/7.jpg b/lib/pixi/assets/maps/test/5/29/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/7.jpg rename to lib/pixi/assets/maps/test/5/29/7.jpg diff --git a/pixi/assets/maps/test/5/29/8.jpg b/lib/pixi/assets/maps/test/5/29/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/8.jpg rename to lib/pixi/assets/maps/test/5/29/8.jpg diff --git a/pixi/assets/maps/test/5/29/9.jpg b/lib/pixi/assets/maps/test/5/29/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/29/9.jpg rename to lib/pixi/assets/maps/test/5/29/9.jpg diff --git a/pixi/assets/maps/test/5/3/0.jpg b/lib/pixi/assets/maps/test/5/3/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/0.jpg rename to lib/pixi/assets/maps/test/5/3/0.jpg diff --git a/pixi/assets/maps/test/5/3/1.jpg b/lib/pixi/assets/maps/test/5/3/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/1.jpg rename to lib/pixi/assets/maps/test/5/3/1.jpg diff --git a/pixi/assets/maps/test/5/3/10.jpg b/lib/pixi/assets/maps/test/5/3/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/10.jpg rename to lib/pixi/assets/maps/test/5/3/10.jpg diff --git a/pixi/assets/maps/test/5/3/11.jpg b/lib/pixi/assets/maps/test/5/3/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/11.jpg rename to lib/pixi/assets/maps/test/5/3/11.jpg diff --git a/pixi/assets/maps/test/5/3/12.jpg b/lib/pixi/assets/maps/test/5/3/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/12.jpg rename to lib/pixi/assets/maps/test/5/3/12.jpg diff --git a/pixi/assets/maps/test/5/3/13.jpg b/lib/pixi/assets/maps/test/5/3/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/13.jpg rename to lib/pixi/assets/maps/test/5/3/13.jpg diff --git a/pixi/assets/maps/test/5/3/14.jpg b/lib/pixi/assets/maps/test/5/3/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/14.jpg rename to lib/pixi/assets/maps/test/5/3/14.jpg diff --git a/pixi/assets/maps/test/5/3/15.jpg b/lib/pixi/assets/maps/test/5/3/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/15.jpg rename to lib/pixi/assets/maps/test/5/3/15.jpg diff --git a/pixi/assets/maps/test/5/3/16.jpg b/lib/pixi/assets/maps/test/5/3/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/16.jpg rename to lib/pixi/assets/maps/test/5/3/16.jpg diff --git a/pixi/assets/maps/test/5/3/17.jpg b/lib/pixi/assets/maps/test/5/3/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/17.jpg rename to lib/pixi/assets/maps/test/5/3/17.jpg diff --git a/pixi/assets/maps/test/5/3/18.jpg b/lib/pixi/assets/maps/test/5/3/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/18.jpg rename to lib/pixi/assets/maps/test/5/3/18.jpg diff --git a/pixi/assets/maps/test/5/3/19.jpg b/lib/pixi/assets/maps/test/5/3/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/19.jpg rename to lib/pixi/assets/maps/test/5/3/19.jpg diff --git a/pixi/assets/maps/test/5/3/2.jpg b/lib/pixi/assets/maps/test/5/3/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/2.jpg rename to lib/pixi/assets/maps/test/5/3/2.jpg diff --git a/pixi/assets/maps/test/5/3/20.jpg b/lib/pixi/assets/maps/test/5/3/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/20.jpg rename to lib/pixi/assets/maps/test/5/3/20.jpg diff --git a/pixi/assets/maps/test/5/3/21.jpg b/lib/pixi/assets/maps/test/5/3/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/21.jpg rename to lib/pixi/assets/maps/test/5/3/21.jpg diff --git a/pixi/assets/maps/test/5/3/22.jpg b/lib/pixi/assets/maps/test/5/3/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/22.jpg rename to lib/pixi/assets/maps/test/5/3/22.jpg diff --git a/pixi/assets/maps/test/5/3/23.jpg b/lib/pixi/assets/maps/test/5/3/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/23.jpg rename to lib/pixi/assets/maps/test/5/3/23.jpg diff --git a/pixi/assets/maps/test/5/3/24.jpg b/lib/pixi/assets/maps/test/5/3/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/24.jpg rename to lib/pixi/assets/maps/test/5/3/24.jpg diff --git a/pixi/assets/maps/test/5/3/25.jpg b/lib/pixi/assets/maps/test/5/3/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/25.jpg rename to lib/pixi/assets/maps/test/5/3/25.jpg diff --git a/pixi/assets/maps/test/5/3/26.jpg b/lib/pixi/assets/maps/test/5/3/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/26.jpg rename to lib/pixi/assets/maps/test/5/3/26.jpg diff --git a/pixi/assets/maps/test/5/3/27.jpg b/lib/pixi/assets/maps/test/5/3/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/27.jpg rename to lib/pixi/assets/maps/test/5/3/27.jpg diff --git a/pixi/assets/maps/test/5/3/28.jpg b/lib/pixi/assets/maps/test/5/3/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/28.jpg rename to lib/pixi/assets/maps/test/5/3/28.jpg diff --git a/pixi/assets/maps/test/5/3/29.jpg b/lib/pixi/assets/maps/test/5/3/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/29.jpg rename to lib/pixi/assets/maps/test/5/3/29.jpg diff --git a/pixi/assets/maps/test/5/3/3.jpg b/lib/pixi/assets/maps/test/5/3/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/3.jpg rename to lib/pixi/assets/maps/test/5/3/3.jpg diff --git a/pixi/assets/maps/test/5/3/30.jpg b/lib/pixi/assets/maps/test/5/3/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/30.jpg rename to lib/pixi/assets/maps/test/5/3/30.jpg diff --git a/pixi/assets/maps/test/5/3/31.jpg b/lib/pixi/assets/maps/test/5/3/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/31.jpg rename to lib/pixi/assets/maps/test/5/3/31.jpg diff --git a/pixi/assets/maps/test/5/3/4.jpg b/lib/pixi/assets/maps/test/5/3/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/4.jpg rename to lib/pixi/assets/maps/test/5/3/4.jpg diff --git a/pixi/assets/maps/test/5/3/5.jpg b/lib/pixi/assets/maps/test/5/3/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/5.jpg rename to lib/pixi/assets/maps/test/5/3/5.jpg diff --git a/pixi/assets/maps/test/5/3/6.jpg b/lib/pixi/assets/maps/test/5/3/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/6.jpg rename to lib/pixi/assets/maps/test/5/3/6.jpg diff --git a/pixi/assets/maps/test/5/3/7.jpg b/lib/pixi/assets/maps/test/5/3/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/7.jpg rename to lib/pixi/assets/maps/test/5/3/7.jpg diff --git a/pixi/assets/maps/test/5/3/8.jpg b/lib/pixi/assets/maps/test/5/3/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/8.jpg rename to lib/pixi/assets/maps/test/5/3/8.jpg diff --git a/pixi/assets/maps/test/5/3/9.jpg b/lib/pixi/assets/maps/test/5/3/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/3/9.jpg rename to lib/pixi/assets/maps/test/5/3/9.jpg diff --git a/pixi/assets/maps/test/5/30/0.jpg b/lib/pixi/assets/maps/test/5/30/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/0.jpg rename to lib/pixi/assets/maps/test/5/30/0.jpg diff --git a/pixi/assets/maps/test/5/30/1.jpg b/lib/pixi/assets/maps/test/5/30/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/1.jpg rename to lib/pixi/assets/maps/test/5/30/1.jpg diff --git a/pixi/assets/maps/test/5/30/10.jpg b/lib/pixi/assets/maps/test/5/30/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/10.jpg rename to lib/pixi/assets/maps/test/5/30/10.jpg diff --git a/pixi/assets/maps/test/5/30/11.jpg b/lib/pixi/assets/maps/test/5/30/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/11.jpg rename to lib/pixi/assets/maps/test/5/30/11.jpg diff --git a/pixi/assets/maps/test/5/30/12.jpg b/lib/pixi/assets/maps/test/5/30/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/12.jpg rename to lib/pixi/assets/maps/test/5/30/12.jpg diff --git a/pixi/assets/maps/test/5/30/13.jpg b/lib/pixi/assets/maps/test/5/30/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/13.jpg rename to lib/pixi/assets/maps/test/5/30/13.jpg diff --git a/pixi/assets/maps/test/5/30/14.jpg b/lib/pixi/assets/maps/test/5/30/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/14.jpg rename to lib/pixi/assets/maps/test/5/30/14.jpg diff --git a/pixi/assets/maps/test/5/30/15.jpg b/lib/pixi/assets/maps/test/5/30/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/15.jpg rename to lib/pixi/assets/maps/test/5/30/15.jpg diff --git a/pixi/assets/maps/test/5/30/16.jpg b/lib/pixi/assets/maps/test/5/30/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/16.jpg rename to lib/pixi/assets/maps/test/5/30/16.jpg diff --git a/pixi/assets/maps/test/5/30/17.jpg b/lib/pixi/assets/maps/test/5/30/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/17.jpg rename to lib/pixi/assets/maps/test/5/30/17.jpg diff --git a/pixi/assets/maps/test/5/30/18.jpg b/lib/pixi/assets/maps/test/5/30/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/18.jpg rename to lib/pixi/assets/maps/test/5/30/18.jpg diff --git a/pixi/assets/maps/test/5/30/19.jpg b/lib/pixi/assets/maps/test/5/30/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/19.jpg rename to lib/pixi/assets/maps/test/5/30/19.jpg diff --git a/pixi/assets/maps/test/5/30/2.jpg b/lib/pixi/assets/maps/test/5/30/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/2.jpg rename to lib/pixi/assets/maps/test/5/30/2.jpg diff --git a/pixi/assets/maps/test/5/30/20.jpg b/lib/pixi/assets/maps/test/5/30/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/20.jpg rename to lib/pixi/assets/maps/test/5/30/20.jpg diff --git a/pixi/assets/maps/test/5/30/21.jpg b/lib/pixi/assets/maps/test/5/30/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/21.jpg rename to lib/pixi/assets/maps/test/5/30/21.jpg diff --git a/pixi/assets/maps/test/5/30/22.jpg b/lib/pixi/assets/maps/test/5/30/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/22.jpg rename to lib/pixi/assets/maps/test/5/30/22.jpg diff --git a/pixi/assets/maps/test/5/30/23.jpg b/lib/pixi/assets/maps/test/5/30/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/23.jpg rename to lib/pixi/assets/maps/test/5/30/23.jpg diff --git a/pixi/assets/maps/test/5/30/24.jpg b/lib/pixi/assets/maps/test/5/30/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/24.jpg rename to lib/pixi/assets/maps/test/5/30/24.jpg diff --git a/pixi/assets/maps/test/5/30/25.jpg b/lib/pixi/assets/maps/test/5/30/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/25.jpg rename to lib/pixi/assets/maps/test/5/30/25.jpg diff --git a/pixi/assets/maps/test/5/30/26.jpg b/lib/pixi/assets/maps/test/5/30/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/26.jpg rename to lib/pixi/assets/maps/test/5/30/26.jpg diff --git a/pixi/assets/maps/test/5/30/27.jpg b/lib/pixi/assets/maps/test/5/30/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/27.jpg rename to lib/pixi/assets/maps/test/5/30/27.jpg diff --git a/pixi/assets/maps/test/5/30/28.jpg b/lib/pixi/assets/maps/test/5/30/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/28.jpg rename to lib/pixi/assets/maps/test/5/30/28.jpg diff --git a/pixi/assets/maps/test/5/30/29.jpg b/lib/pixi/assets/maps/test/5/30/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/29.jpg rename to lib/pixi/assets/maps/test/5/30/29.jpg diff --git a/pixi/assets/maps/test/5/30/3.jpg b/lib/pixi/assets/maps/test/5/30/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/3.jpg rename to lib/pixi/assets/maps/test/5/30/3.jpg diff --git a/pixi/assets/maps/test/5/30/30.jpg b/lib/pixi/assets/maps/test/5/30/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/30.jpg rename to lib/pixi/assets/maps/test/5/30/30.jpg diff --git a/pixi/assets/maps/test/5/30/31.jpg b/lib/pixi/assets/maps/test/5/30/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/31.jpg rename to lib/pixi/assets/maps/test/5/30/31.jpg diff --git a/pixi/assets/maps/test/5/30/4.jpg b/lib/pixi/assets/maps/test/5/30/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/4.jpg rename to lib/pixi/assets/maps/test/5/30/4.jpg diff --git a/pixi/assets/maps/test/5/30/5.jpg b/lib/pixi/assets/maps/test/5/30/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/5.jpg rename to lib/pixi/assets/maps/test/5/30/5.jpg diff --git a/pixi/assets/maps/test/5/30/6.jpg b/lib/pixi/assets/maps/test/5/30/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/6.jpg rename to lib/pixi/assets/maps/test/5/30/6.jpg diff --git a/pixi/assets/maps/test/5/30/7.jpg b/lib/pixi/assets/maps/test/5/30/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/7.jpg rename to lib/pixi/assets/maps/test/5/30/7.jpg diff --git a/pixi/assets/maps/test/5/30/8.jpg b/lib/pixi/assets/maps/test/5/30/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/8.jpg rename to lib/pixi/assets/maps/test/5/30/8.jpg diff --git a/pixi/assets/maps/test/5/30/9.jpg b/lib/pixi/assets/maps/test/5/30/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/30/9.jpg rename to lib/pixi/assets/maps/test/5/30/9.jpg diff --git a/pixi/assets/maps/test/5/31/0.jpg b/lib/pixi/assets/maps/test/5/31/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/0.jpg rename to lib/pixi/assets/maps/test/5/31/0.jpg diff --git a/pixi/assets/maps/test/5/31/1.jpg b/lib/pixi/assets/maps/test/5/31/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/1.jpg rename to lib/pixi/assets/maps/test/5/31/1.jpg diff --git a/pixi/assets/maps/test/5/31/10.jpg b/lib/pixi/assets/maps/test/5/31/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/10.jpg rename to lib/pixi/assets/maps/test/5/31/10.jpg diff --git a/pixi/assets/maps/test/5/31/11.jpg b/lib/pixi/assets/maps/test/5/31/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/11.jpg rename to lib/pixi/assets/maps/test/5/31/11.jpg diff --git a/pixi/assets/maps/test/5/31/12.jpg b/lib/pixi/assets/maps/test/5/31/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/12.jpg rename to lib/pixi/assets/maps/test/5/31/12.jpg diff --git a/pixi/assets/maps/test/5/31/13.jpg b/lib/pixi/assets/maps/test/5/31/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/13.jpg rename to lib/pixi/assets/maps/test/5/31/13.jpg diff --git a/pixi/assets/maps/test/5/31/14.jpg b/lib/pixi/assets/maps/test/5/31/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/14.jpg rename to lib/pixi/assets/maps/test/5/31/14.jpg diff --git a/pixi/assets/maps/test/5/31/15.jpg b/lib/pixi/assets/maps/test/5/31/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/15.jpg rename to lib/pixi/assets/maps/test/5/31/15.jpg diff --git a/pixi/assets/maps/test/5/31/16.jpg b/lib/pixi/assets/maps/test/5/31/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/16.jpg rename to lib/pixi/assets/maps/test/5/31/16.jpg diff --git a/pixi/assets/maps/test/5/31/17.jpg b/lib/pixi/assets/maps/test/5/31/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/17.jpg rename to lib/pixi/assets/maps/test/5/31/17.jpg diff --git a/pixi/assets/maps/test/5/31/18.jpg b/lib/pixi/assets/maps/test/5/31/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/18.jpg rename to lib/pixi/assets/maps/test/5/31/18.jpg diff --git a/pixi/assets/maps/test/5/31/19.jpg b/lib/pixi/assets/maps/test/5/31/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/19.jpg rename to lib/pixi/assets/maps/test/5/31/19.jpg diff --git a/pixi/assets/maps/test/5/31/2.jpg b/lib/pixi/assets/maps/test/5/31/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/2.jpg rename to lib/pixi/assets/maps/test/5/31/2.jpg diff --git a/pixi/assets/maps/test/5/31/20.jpg b/lib/pixi/assets/maps/test/5/31/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/20.jpg rename to lib/pixi/assets/maps/test/5/31/20.jpg diff --git a/pixi/assets/maps/test/5/31/21.jpg b/lib/pixi/assets/maps/test/5/31/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/21.jpg rename to lib/pixi/assets/maps/test/5/31/21.jpg diff --git a/pixi/assets/maps/test/5/31/22.jpg b/lib/pixi/assets/maps/test/5/31/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/22.jpg rename to lib/pixi/assets/maps/test/5/31/22.jpg diff --git a/pixi/assets/maps/test/5/31/23.jpg b/lib/pixi/assets/maps/test/5/31/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/23.jpg rename to lib/pixi/assets/maps/test/5/31/23.jpg diff --git a/pixi/assets/maps/test/5/31/24.jpg b/lib/pixi/assets/maps/test/5/31/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/24.jpg rename to lib/pixi/assets/maps/test/5/31/24.jpg diff --git a/pixi/assets/maps/test/5/31/25.jpg b/lib/pixi/assets/maps/test/5/31/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/25.jpg rename to lib/pixi/assets/maps/test/5/31/25.jpg diff --git a/pixi/assets/maps/test/5/31/26.jpg b/lib/pixi/assets/maps/test/5/31/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/26.jpg rename to lib/pixi/assets/maps/test/5/31/26.jpg diff --git a/pixi/assets/maps/test/5/31/27.jpg b/lib/pixi/assets/maps/test/5/31/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/27.jpg rename to lib/pixi/assets/maps/test/5/31/27.jpg diff --git a/pixi/assets/maps/test/5/31/28.jpg b/lib/pixi/assets/maps/test/5/31/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/28.jpg rename to lib/pixi/assets/maps/test/5/31/28.jpg diff --git a/pixi/assets/maps/test/5/31/29.jpg b/lib/pixi/assets/maps/test/5/31/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/29.jpg rename to lib/pixi/assets/maps/test/5/31/29.jpg diff --git a/pixi/assets/maps/test/5/31/3.jpg b/lib/pixi/assets/maps/test/5/31/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/3.jpg rename to lib/pixi/assets/maps/test/5/31/3.jpg diff --git a/pixi/assets/maps/test/5/31/30.jpg b/lib/pixi/assets/maps/test/5/31/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/30.jpg rename to lib/pixi/assets/maps/test/5/31/30.jpg diff --git a/pixi/assets/maps/test/5/31/31.jpg b/lib/pixi/assets/maps/test/5/31/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/31.jpg rename to lib/pixi/assets/maps/test/5/31/31.jpg diff --git a/pixi/assets/maps/test/5/31/4.jpg b/lib/pixi/assets/maps/test/5/31/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/4.jpg rename to lib/pixi/assets/maps/test/5/31/4.jpg diff --git a/pixi/assets/maps/test/5/31/5.jpg b/lib/pixi/assets/maps/test/5/31/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/5.jpg rename to lib/pixi/assets/maps/test/5/31/5.jpg diff --git a/pixi/assets/maps/test/5/31/6.jpg b/lib/pixi/assets/maps/test/5/31/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/6.jpg rename to lib/pixi/assets/maps/test/5/31/6.jpg diff --git a/pixi/assets/maps/test/5/31/7.jpg b/lib/pixi/assets/maps/test/5/31/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/7.jpg rename to lib/pixi/assets/maps/test/5/31/7.jpg diff --git a/pixi/assets/maps/test/5/31/8.jpg b/lib/pixi/assets/maps/test/5/31/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/8.jpg rename to lib/pixi/assets/maps/test/5/31/8.jpg diff --git a/pixi/assets/maps/test/5/31/9.jpg b/lib/pixi/assets/maps/test/5/31/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/31/9.jpg rename to lib/pixi/assets/maps/test/5/31/9.jpg diff --git a/pixi/assets/maps/test/5/4/0.jpg b/lib/pixi/assets/maps/test/5/4/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/0.jpg rename to lib/pixi/assets/maps/test/5/4/0.jpg diff --git a/pixi/assets/maps/test/5/4/1.jpg b/lib/pixi/assets/maps/test/5/4/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/1.jpg rename to lib/pixi/assets/maps/test/5/4/1.jpg diff --git a/pixi/assets/maps/test/5/4/10.jpg b/lib/pixi/assets/maps/test/5/4/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/10.jpg rename to lib/pixi/assets/maps/test/5/4/10.jpg diff --git a/pixi/assets/maps/test/5/4/11.jpg b/lib/pixi/assets/maps/test/5/4/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/11.jpg rename to lib/pixi/assets/maps/test/5/4/11.jpg diff --git a/pixi/assets/maps/test/5/4/12.jpg b/lib/pixi/assets/maps/test/5/4/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/12.jpg rename to lib/pixi/assets/maps/test/5/4/12.jpg diff --git a/pixi/assets/maps/test/5/4/13.jpg b/lib/pixi/assets/maps/test/5/4/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/13.jpg rename to lib/pixi/assets/maps/test/5/4/13.jpg diff --git a/pixi/assets/maps/test/5/4/14.jpg b/lib/pixi/assets/maps/test/5/4/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/14.jpg rename to lib/pixi/assets/maps/test/5/4/14.jpg diff --git a/pixi/assets/maps/test/5/4/15.jpg b/lib/pixi/assets/maps/test/5/4/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/15.jpg rename to lib/pixi/assets/maps/test/5/4/15.jpg diff --git a/pixi/assets/maps/test/5/4/16.jpg b/lib/pixi/assets/maps/test/5/4/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/16.jpg rename to lib/pixi/assets/maps/test/5/4/16.jpg diff --git a/pixi/assets/maps/test/5/4/17.jpg b/lib/pixi/assets/maps/test/5/4/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/17.jpg rename to lib/pixi/assets/maps/test/5/4/17.jpg diff --git a/pixi/assets/maps/test/5/4/18.jpg b/lib/pixi/assets/maps/test/5/4/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/18.jpg rename to lib/pixi/assets/maps/test/5/4/18.jpg diff --git a/pixi/assets/maps/test/5/4/19.jpg b/lib/pixi/assets/maps/test/5/4/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/19.jpg rename to lib/pixi/assets/maps/test/5/4/19.jpg diff --git a/pixi/assets/maps/test/5/4/2.jpg b/lib/pixi/assets/maps/test/5/4/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/2.jpg rename to lib/pixi/assets/maps/test/5/4/2.jpg diff --git a/pixi/assets/maps/test/5/4/20.jpg b/lib/pixi/assets/maps/test/5/4/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/20.jpg rename to lib/pixi/assets/maps/test/5/4/20.jpg diff --git a/pixi/assets/maps/test/5/4/21.jpg b/lib/pixi/assets/maps/test/5/4/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/21.jpg rename to lib/pixi/assets/maps/test/5/4/21.jpg diff --git a/pixi/assets/maps/test/5/4/22.jpg b/lib/pixi/assets/maps/test/5/4/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/22.jpg rename to lib/pixi/assets/maps/test/5/4/22.jpg diff --git a/pixi/assets/maps/test/5/4/23.jpg b/lib/pixi/assets/maps/test/5/4/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/23.jpg rename to lib/pixi/assets/maps/test/5/4/23.jpg diff --git a/pixi/assets/maps/test/5/4/24.jpg b/lib/pixi/assets/maps/test/5/4/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/24.jpg rename to lib/pixi/assets/maps/test/5/4/24.jpg diff --git a/pixi/assets/maps/test/5/4/25.jpg b/lib/pixi/assets/maps/test/5/4/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/25.jpg rename to lib/pixi/assets/maps/test/5/4/25.jpg diff --git a/pixi/assets/maps/test/5/4/26.jpg b/lib/pixi/assets/maps/test/5/4/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/26.jpg rename to lib/pixi/assets/maps/test/5/4/26.jpg diff --git a/pixi/assets/maps/test/5/4/27.jpg b/lib/pixi/assets/maps/test/5/4/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/27.jpg rename to lib/pixi/assets/maps/test/5/4/27.jpg diff --git a/pixi/assets/maps/test/5/4/28.jpg b/lib/pixi/assets/maps/test/5/4/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/28.jpg rename to lib/pixi/assets/maps/test/5/4/28.jpg diff --git a/pixi/assets/maps/test/5/4/29.jpg b/lib/pixi/assets/maps/test/5/4/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/29.jpg rename to lib/pixi/assets/maps/test/5/4/29.jpg diff --git a/pixi/assets/maps/test/5/4/3.jpg b/lib/pixi/assets/maps/test/5/4/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/3.jpg rename to lib/pixi/assets/maps/test/5/4/3.jpg diff --git a/pixi/assets/maps/test/5/4/30.jpg b/lib/pixi/assets/maps/test/5/4/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/30.jpg rename to lib/pixi/assets/maps/test/5/4/30.jpg diff --git a/pixi/assets/maps/test/5/4/31.jpg b/lib/pixi/assets/maps/test/5/4/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/31.jpg rename to lib/pixi/assets/maps/test/5/4/31.jpg diff --git a/pixi/assets/maps/test/5/4/4.jpg b/lib/pixi/assets/maps/test/5/4/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/4.jpg rename to lib/pixi/assets/maps/test/5/4/4.jpg diff --git a/pixi/assets/maps/test/5/4/5.jpg b/lib/pixi/assets/maps/test/5/4/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/5.jpg rename to lib/pixi/assets/maps/test/5/4/5.jpg diff --git a/pixi/assets/maps/test/5/4/6.jpg b/lib/pixi/assets/maps/test/5/4/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/6.jpg rename to lib/pixi/assets/maps/test/5/4/6.jpg diff --git a/pixi/assets/maps/test/5/4/7.jpg b/lib/pixi/assets/maps/test/5/4/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/7.jpg rename to lib/pixi/assets/maps/test/5/4/7.jpg diff --git a/pixi/assets/maps/test/5/4/8.jpg b/lib/pixi/assets/maps/test/5/4/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/8.jpg rename to lib/pixi/assets/maps/test/5/4/8.jpg diff --git a/pixi/assets/maps/test/5/4/9.jpg b/lib/pixi/assets/maps/test/5/4/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/4/9.jpg rename to lib/pixi/assets/maps/test/5/4/9.jpg diff --git a/pixi/assets/maps/test/5/5/0.jpg b/lib/pixi/assets/maps/test/5/5/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/0.jpg rename to lib/pixi/assets/maps/test/5/5/0.jpg diff --git a/pixi/assets/maps/test/5/5/1.jpg b/lib/pixi/assets/maps/test/5/5/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/1.jpg rename to lib/pixi/assets/maps/test/5/5/1.jpg diff --git a/pixi/assets/maps/test/5/5/10.jpg b/lib/pixi/assets/maps/test/5/5/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/10.jpg rename to lib/pixi/assets/maps/test/5/5/10.jpg diff --git a/pixi/assets/maps/test/5/5/11.jpg b/lib/pixi/assets/maps/test/5/5/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/11.jpg rename to lib/pixi/assets/maps/test/5/5/11.jpg diff --git a/pixi/assets/maps/test/5/5/12.jpg b/lib/pixi/assets/maps/test/5/5/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/12.jpg rename to lib/pixi/assets/maps/test/5/5/12.jpg diff --git a/pixi/assets/maps/test/5/5/13.jpg b/lib/pixi/assets/maps/test/5/5/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/13.jpg rename to lib/pixi/assets/maps/test/5/5/13.jpg diff --git a/pixi/assets/maps/test/5/5/14.jpg b/lib/pixi/assets/maps/test/5/5/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/14.jpg rename to lib/pixi/assets/maps/test/5/5/14.jpg diff --git a/pixi/assets/maps/test/5/5/15.jpg b/lib/pixi/assets/maps/test/5/5/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/15.jpg rename to lib/pixi/assets/maps/test/5/5/15.jpg diff --git a/pixi/assets/maps/test/5/5/16.jpg b/lib/pixi/assets/maps/test/5/5/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/16.jpg rename to lib/pixi/assets/maps/test/5/5/16.jpg diff --git a/pixi/assets/maps/test/5/5/17.jpg b/lib/pixi/assets/maps/test/5/5/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/17.jpg rename to lib/pixi/assets/maps/test/5/5/17.jpg diff --git a/pixi/assets/maps/test/5/5/18.jpg b/lib/pixi/assets/maps/test/5/5/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/18.jpg rename to lib/pixi/assets/maps/test/5/5/18.jpg diff --git a/pixi/assets/maps/test/5/5/19.jpg b/lib/pixi/assets/maps/test/5/5/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/19.jpg rename to lib/pixi/assets/maps/test/5/5/19.jpg diff --git a/pixi/assets/maps/test/5/5/2.jpg b/lib/pixi/assets/maps/test/5/5/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/2.jpg rename to lib/pixi/assets/maps/test/5/5/2.jpg diff --git a/pixi/assets/maps/test/5/5/20.jpg b/lib/pixi/assets/maps/test/5/5/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/20.jpg rename to lib/pixi/assets/maps/test/5/5/20.jpg diff --git a/pixi/assets/maps/test/5/5/21.jpg b/lib/pixi/assets/maps/test/5/5/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/21.jpg rename to lib/pixi/assets/maps/test/5/5/21.jpg diff --git a/pixi/assets/maps/test/5/5/22.jpg b/lib/pixi/assets/maps/test/5/5/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/22.jpg rename to lib/pixi/assets/maps/test/5/5/22.jpg diff --git a/pixi/assets/maps/test/5/5/23.jpg b/lib/pixi/assets/maps/test/5/5/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/23.jpg rename to lib/pixi/assets/maps/test/5/5/23.jpg diff --git a/pixi/assets/maps/test/5/5/24.jpg b/lib/pixi/assets/maps/test/5/5/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/24.jpg rename to lib/pixi/assets/maps/test/5/5/24.jpg diff --git a/pixi/assets/maps/test/5/5/25.jpg b/lib/pixi/assets/maps/test/5/5/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/25.jpg rename to lib/pixi/assets/maps/test/5/5/25.jpg diff --git a/pixi/assets/maps/test/5/5/26.jpg b/lib/pixi/assets/maps/test/5/5/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/26.jpg rename to lib/pixi/assets/maps/test/5/5/26.jpg diff --git a/pixi/assets/maps/test/5/5/27.jpg b/lib/pixi/assets/maps/test/5/5/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/27.jpg rename to lib/pixi/assets/maps/test/5/5/27.jpg diff --git a/pixi/assets/maps/test/5/5/28.jpg b/lib/pixi/assets/maps/test/5/5/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/28.jpg rename to lib/pixi/assets/maps/test/5/5/28.jpg diff --git a/pixi/assets/maps/test/5/5/29.jpg b/lib/pixi/assets/maps/test/5/5/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/29.jpg rename to lib/pixi/assets/maps/test/5/5/29.jpg diff --git a/pixi/assets/maps/test/5/5/3.jpg b/lib/pixi/assets/maps/test/5/5/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/3.jpg rename to lib/pixi/assets/maps/test/5/5/3.jpg diff --git a/pixi/assets/maps/test/5/5/30.jpg b/lib/pixi/assets/maps/test/5/5/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/30.jpg rename to lib/pixi/assets/maps/test/5/5/30.jpg diff --git a/pixi/assets/maps/test/5/5/31.jpg b/lib/pixi/assets/maps/test/5/5/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/31.jpg rename to lib/pixi/assets/maps/test/5/5/31.jpg diff --git a/pixi/assets/maps/test/5/5/4.jpg b/lib/pixi/assets/maps/test/5/5/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/4.jpg rename to lib/pixi/assets/maps/test/5/5/4.jpg diff --git a/pixi/assets/maps/test/5/5/5.jpg b/lib/pixi/assets/maps/test/5/5/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/5.jpg rename to lib/pixi/assets/maps/test/5/5/5.jpg diff --git a/pixi/assets/maps/test/5/5/6.jpg b/lib/pixi/assets/maps/test/5/5/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/6.jpg rename to lib/pixi/assets/maps/test/5/5/6.jpg diff --git a/pixi/assets/maps/test/5/5/7.jpg b/lib/pixi/assets/maps/test/5/5/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/7.jpg rename to lib/pixi/assets/maps/test/5/5/7.jpg diff --git a/pixi/assets/maps/test/5/5/8.jpg b/lib/pixi/assets/maps/test/5/5/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/8.jpg rename to lib/pixi/assets/maps/test/5/5/8.jpg diff --git a/pixi/assets/maps/test/5/5/9.jpg b/lib/pixi/assets/maps/test/5/5/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/5/9.jpg rename to lib/pixi/assets/maps/test/5/5/9.jpg diff --git a/pixi/assets/maps/test/5/6/0.jpg b/lib/pixi/assets/maps/test/5/6/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/0.jpg rename to lib/pixi/assets/maps/test/5/6/0.jpg diff --git a/pixi/assets/maps/test/5/6/1.jpg b/lib/pixi/assets/maps/test/5/6/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/1.jpg rename to lib/pixi/assets/maps/test/5/6/1.jpg diff --git a/pixi/assets/maps/test/5/6/10.jpg b/lib/pixi/assets/maps/test/5/6/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/10.jpg rename to lib/pixi/assets/maps/test/5/6/10.jpg diff --git a/pixi/assets/maps/test/5/6/11.jpg b/lib/pixi/assets/maps/test/5/6/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/11.jpg rename to lib/pixi/assets/maps/test/5/6/11.jpg diff --git a/pixi/assets/maps/test/5/6/12.jpg b/lib/pixi/assets/maps/test/5/6/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/12.jpg rename to lib/pixi/assets/maps/test/5/6/12.jpg diff --git a/pixi/assets/maps/test/5/6/13.jpg b/lib/pixi/assets/maps/test/5/6/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/13.jpg rename to lib/pixi/assets/maps/test/5/6/13.jpg diff --git a/pixi/assets/maps/test/5/6/14.jpg b/lib/pixi/assets/maps/test/5/6/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/14.jpg rename to lib/pixi/assets/maps/test/5/6/14.jpg diff --git a/pixi/assets/maps/test/5/6/15.jpg b/lib/pixi/assets/maps/test/5/6/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/15.jpg rename to lib/pixi/assets/maps/test/5/6/15.jpg diff --git a/pixi/assets/maps/test/5/6/16.jpg b/lib/pixi/assets/maps/test/5/6/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/16.jpg rename to lib/pixi/assets/maps/test/5/6/16.jpg diff --git a/pixi/assets/maps/test/5/6/17.jpg b/lib/pixi/assets/maps/test/5/6/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/17.jpg rename to lib/pixi/assets/maps/test/5/6/17.jpg diff --git a/pixi/assets/maps/test/5/6/18.jpg b/lib/pixi/assets/maps/test/5/6/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/18.jpg rename to lib/pixi/assets/maps/test/5/6/18.jpg diff --git a/pixi/assets/maps/test/5/6/19.jpg b/lib/pixi/assets/maps/test/5/6/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/19.jpg rename to lib/pixi/assets/maps/test/5/6/19.jpg diff --git a/pixi/assets/maps/test/5/6/2.jpg b/lib/pixi/assets/maps/test/5/6/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/2.jpg rename to lib/pixi/assets/maps/test/5/6/2.jpg diff --git a/pixi/assets/maps/test/5/6/20.jpg b/lib/pixi/assets/maps/test/5/6/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/20.jpg rename to lib/pixi/assets/maps/test/5/6/20.jpg diff --git a/pixi/assets/maps/test/5/6/21.jpg b/lib/pixi/assets/maps/test/5/6/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/21.jpg rename to lib/pixi/assets/maps/test/5/6/21.jpg diff --git a/pixi/assets/maps/test/5/6/22.jpg b/lib/pixi/assets/maps/test/5/6/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/22.jpg rename to lib/pixi/assets/maps/test/5/6/22.jpg diff --git a/pixi/assets/maps/test/5/6/23.jpg b/lib/pixi/assets/maps/test/5/6/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/23.jpg rename to lib/pixi/assets/maps/test/5/6/23.jpg diff --git a/pixi/assets/maps/test/5/6/24.jpg b/lib/pixi/assets/maps/test/5/6/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/24.jpg rename to lib/pixi/assets/maps/test/5/6/24.jpg diff --git a/pixi/assets/maps/test/5/6/25.jpg b/lib/pixi/assets/maps/test/5/6/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/25.jpg rename to lib/pixi/assets/maps/test/5/6/25.jpg diff --git a/pixi/assets/maps/test/5/6/26.jpg b/lib/pixi/assets/maps/test/5/6/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/26.jpg rename to lib/pixi/assets/maps/test/5/6/26.jpg diff --git a/pixi/assets/maps/test/5/6/27.jpg b/lib/pixi/assets/maps/test/5/6/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/27.jpg rename to lib/pixi/assets/maps/test/5/6/27.jpg diff --git a/pixi/assets/maps/test/5/6/28.jpg b/lib/pixi/assets/maps/test/5/6/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/28.jpg rename to lib/pixi/assets/maps/test/5/6/28.jpg diff --git a/pixi/assets/maps/test/5/6/29.jpg b/lib/pixi/assets/maps/test/5/6/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/29.jpg rename to lib/pixi/assets/maps/test/5/6/29.jpg diff --git a/pixi/assets/maps/test/5/6/3.jpg b/lib/pixi/assets/maps/test/5/6/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/3.jpg rename to lib/pixi/assets/maps/test/5/6/3.jpg diff --git a/pixi/assets/maps/test/5/6/30.jpg b/lib/pixi/assets/maps/test/5/6/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/30.jpg rename to lib/pixi/assets/maps/test/5/6/30.jpg diff --git a/pixi/assets/maps/test/5/6/31.jpg b/lib/pixi/assets/maps/test/5/6/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/31.jpg rename to lib/pixi/assets/maps/test/5/6/31.jpg diff --git a/pixi/assets/maps/test/5/6/4.jpg b/lib/pixi/assets/maps/test/5/6/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/4.jpg rename to lib/pixi/assets/maps/test/5/6/4.jpg diff --git a/pixi/assets/maps/test/5/6/5.jpg b/lib/pixi/assets/maps/test/5/6/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/5.jpg rename to lib/pixi/assets/maps/test/5/6/5.jpg diff --git a/pixi/assets/maps/test/5/6/6.jpg b/lib/pixi/assets/maps/test/5/6/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/6.jpg rename to lib/pixi/assets/maps/test/5/6/6.jpg diff --git a/pixi/assets/maps/test/5/6/7.jpg b/lib/pixi/assets/maps/test/5/6/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/7.jpg rename to lib/pixi/assets/maps/test/5/6/7.jpg diff --git a/pixi/assets/maps/test/5/6/8.jpg b/lib/pixi/assets/maps/test/5/6/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/8.jpg rename to lib/pixi/assets/maps/test/5/6/8.jpg diff --git a/pixi/assets/maps/test/5/6/9.jpg b/lib/pixi/assets/maps/test/5/6/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/6/9.jpg rename to lib/pixi/assets/maps/test/5/6/9.jpg diff --git a/pixi/assets/maps/test/5/7/0.jpg b/lib/pixi/assets/maps/test/5/7/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/0.jpg rename to lib/pixi/assets/maps/test/5/7/0.jpg diff --git a/pixi/assets/maps/test/5/7/1.jpg b/lib/pixi/assets/maps/test/5/7/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/1.jpg rename to lib/pixi/assets/maps/test/5/7/1.jpg diff --git a/pixi/assets/maps/test/5/7/10.jpg b/lib/pixi/assets/maps/test/5/7/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/10.jpg rename to lib/pixi/assets/maps/test/5/7/10.jpg diff --git a/pixi/assets/maps/test/5/7/11.jpg b/lib/pixi/assets/maps/test/5/7/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/11.jpg rename to lib/pixi/assets/maps/test/5/7/11.jpg diff --git a/pixi/assets/maps/test/5/7/12.jpg b/lib/pixi/assets/maps/test/5/7/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/12.jpg rename to lib/pixi/assets/maps/test/5/7/12.jpg diff --git a/pixi/assets/maps/test/5/7/13.jpg b/lib/pixi/assets/maps/test/5/7/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/13.jpg rename to lib/pixi/assets/maps/test/5/7/13.jpg diff --git a/pixi/assets/maps/test/5/7/14.jpg b/lib/pixi/assets/maps/test/5/7/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/14.jpg rename to lib/pixi/assets/maps/test/5/7/14.jpg diff --git a/pixi/assets/maps/test/5/7/15.jpg b/lib/pixi/assets/maps/test/5/7/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/15.jpg rename to lib/pixi/assets/maps/test/5/7/15.jpg diff --git a/pixi/assets/maps/test/5/7/16.jpg b/lib/pixi/assets/maps/test/5/7/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/16.jpg rename to lib/pixi/assets/maps/test/5/7/16.jpg diff --git a/pixi/assets/maps/test/5/7/17.jpg b/lib/pixi/assets/maps/test/5/7/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/17.jpg rename to lib/pixi/assets/maps/test/5/7/17.jpg diff --git a/pixi/assets/maps/test/5/7/18.jpg b/lib/pixi/assets/maps/test/5/7/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/18.jpg rename to lib/pixi/assets/maps/test/5/7/18.jpg diff --git a/pixi/assets/maps/test/5/7/19.jpg b/lib/pixi/assets/maps/test/5/7/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/19.jpg rename to lib/pixi/assets/maps/test/5/7/19.jpg diff --git a/pixi/assets/maps/test/5/7/2.jpg b/lib/pixi/assets/maps/test/5/7/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/2.jpg rename to lib/pixi/assets/maps/test/5/7/2.jpg diff --git a/pixi/assets/maps/test/5/7/20.jpg b/lib/pixi/assets/maps/test/5/7/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/20.jpg rename to lib/pixi/assets/maps/test/5/7/20.jpg diff --git a/pixi/assets/maps/test/5/7/21.jpg b/lib/pixi/assets/maps/test/5/7/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/21.jpg rename to lib/pixi/assets/maps/test/5/7/21.jpg diff --git a/pixi/assets/maps/test/5/7/22.jpg b/lib/pixi/assets/maps/test/5/7/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/22.jpg rename to lib/pixi/assets/maps/test/5/7/22.jpg diff --git a/pixi/assets/maps/test/5/7/23.jpg b/lib/pixi/assets/maps/test/5/7/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/23.jpg rename to lib/pixi/assets/maps/test/5/7/23.jpg diff --git a/pixi/assets/maps/test/5/7/24.jpg b/lib/pixi/assets/maps/test/5/7/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/24.jpg rename to lib/pixi/assets/maps/test/5/7/24.jpg diff --git a/pixi/assets/maps/test/5/7/25.jpg b/lib/pixi/assets/maps/test/5/7/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/25.jpg rename to lib/pixi/assets/maps/test/5/7/25.jpg diff --git a/pixi/assets/maps/test/5/7/26.jpg b/lib/pixi/assets/maps/test/5/7/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/26.jpg rename to lib/pixi/assets/maps/test/5/7/26.jpg diff --git a/pixi/assets/maps/test/5/7/27.jpg b/lib/pixi/assets/maps/test/5/7/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/27.jpg rename to lib/pixi/assets/maps/test/5/7/27.jpg diff --git a/pixi/assets/maps/test/5/7/28.jpg b/lib/pixi/assets/maps/test/5/7/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/28.jpg rename to lib/pixi/assets/maps/test/5/7/28.jpg diff --git a/pixi/assets/maps/test/5/7/29.jpg b/lib/pixi/assets/maps/test/5/7/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/29.jpg rename to lib/pixi/assets/maps/test/5/7/29.jpg diff --git a/pixi/assets/maps/test/5/7/3.jpg b/lib/pixi/assets/maps/test/5/7/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/3.jpg rename to lib/pixi/assets/maps/test/5/7/3.jpg diff --git a/pixi/assets/maps/test/5/7/30.jpg b/lib/pixi/assets/maps/test/5/7/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/30.jpg rename to lib/pixi/assets/maps/test/5/7/30.jpg diff --git a/pixi/assets/maps/test/5/7/31.jpg b/lib/pixi/assets/maps/test/5/7/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/31.jpg rename to lib/pixi/assets/maps/test/5/7/31.jpg diff --git a/pixi/assets/maps/test/5/7/4.jpg b/lib/pixi/assets/maps/test/5/7/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/4.jpg rename to lib/pixi/assets/maps/test/5/7/4.jpg diff --git a/pixi/assets/maps/test/5/7/5.jpg b/lib/pixi/assets/maps/test/5/7/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/5.jpg rename to lib/pixi/assets/maps/test/5/7/5.jpg diff --git a/pixi/assets/maps/test/5/7/6.jpg b/lib/pixi/assets/maps/test/5/7/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/6.jpg rename to lib/pixi/assets/maps/test/5/7/6.jpg diff --git a/pixi/assets/maps/test/5/7/7.jpg b/lib/pixi/assets/maps/test/5/7/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/7.jpg rename to lib/pixi/assets/maps/test/5/7/7.jpg diff --git a/pixi/assets/maps/test/5/7/8.jpg b/lib/pixi/assets/maps/test/5/7/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/8.jpg rename to lib/pixi/assets/maps/test/5/7/8.jpg diff --git a/pixi/assets/maps/test/5/7/9.jpg b/lib/pixi/assets/maps/test/5/7/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/7/9.jpg rename to lib/pixi/assets/maps/test/5/7/9.jpg diff --git a/pixi/assets/maps/test/5/8/0.jpg b/lib/pixi/assets/maps/test/5/8/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/0.jpg rename to lib/pixi/assets/maps/test/5/8/0.jpg diff --git a/pixi/assets/maps/test/5/8/1.jpg b/lib/pixi/assets/maps/test/5/8/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/1.jpg rename to lib/pixi/assets/maps/test/5/8/1.jpg diff --git a/pixi/assets/maps/test/5/8/10.jpg b/lib/pixi/assets/maps/test/5/8/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/10.jpg rename to lib/pixi/assets/maps/test/5/8/10.jpg diff --git a/pixi/assets/maps/test/5/8/11.jpg b/lib/pixi/assets/maps/test/5/8/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/11.jpg rename to lib/pixi/assets/maps/test/5/8/11.jpg diff --git a/pixi/assets/maps/test/5/8/12.jpg b/lib/pixi/assets/maps/test/5/8/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/12.jpg rename to lib/pixi/assets/maps/test/5/8/12.jpg diff --git a/pixi/assets/maps/test/5/8/13.jpg b/lib/pixi/assets/maps/test/5/8/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/13.jpg rename to lib/pixi/assets/maps/test/5/8/13.jpg diff --git a/pixi/assets/maps/test/5/8/14.jpg b/lib/pixi/assets/maps/test/5/8/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/14.jpg rename to lib/pixi/assets/maps/test/5/8/14.jpg diff --git a/pixi/assets/maps/test/5/8/15.jpg b/lib/pixi/assets/maps/test/5/8/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/15.jpg rename to lib/pixi/assets/maps/test/5/8/15.jpg diff --git a/pixi/assets/maps/test/5/8/16.jpg b/lib/pixi/assets/maps/test/5/8/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/16.jpg rename to lib/pixi/assets/maps/test/5/8/16.jpg diff --git a/pixi/assets/maps/test/5/8/17.jpg b/lib/pixi/assets/maps/test/5/8/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/17.jpg rename to lib/pixi/assets/maps/test/5/8/17.jpg diff --git a/pixi/assets/maps/test/5/8/18.jpg b/lib/pixi/assets/maps/test/5/8/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/18.jpg rename to lib/pixi/assets/maps/test/5/8/18.jpg diff --git a/pixi/assets/maps/test/5/8/19.jpg b/lib/pixi/assets/maps/test/5/8/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/19.jpg rename to lib/pixi/assets/maps/test/5/8/19.jpg diff --git a/pixi/assets/maps/test/5/8/2.jpg b/lib/pixi/assets/maps/test/5/8/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/2.jpg rename to lib/pixi/assets/maps/test/5/8/2.jpg diff --git a/pixi/assets/maps/test/5/8/20.jpg b/lib/pixi/assets/maps/test/5/8/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/20.jpg rename to lib/pixi/assets/maps/test/5/8/20.jpg diff --git a/pixi/assets/maps/test/5/8/21.jpg b/lib/pixi/assets/maps/test/5/8/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/21.jpg rename to lib/pixi/assets/maps/test/5/8/21.jpg diff --git a/pixi/assets/maps/test/5/8/22.jpg b/lib/pixi/assets/maps/test/5/8/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/22.jpg rename to lib/pixi/assets/maps/test/5/8/22.jpg diff --git a/pixi/assets/maps/test/5/8/23.jpg b/lib/pixi/assets/maps/test/5/8/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/23.jpg rename to lib/pixi/assets/maps/test/5/8/23.jpg diff --git a/pixi/assets/maps/test/5/8/24.jpg b/lib/pixi/assets/maps/test/5/8/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/24.jpg rename to lib/pixi/assets/maps/test/5/8/24.jpg diff --git a/pixi/assets/maps/test/5/8/25.jpg b/lib/pixi/assets/maps/test/5/8/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/25.jpg rename to lib/pixi/assets/maps/test/5/8/25.jpg diff --git a/pixi/assets/maps/test/5/8/26.jpg b/lib/pixi/assets/maps/test/5/8/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/26.jpg rename to lib/pixi/assets/maps/test/5/8/26.jpg diff --git a/pixi/assets/maps/test/5/8/27.jpg b/lib/pixi/assets/maps/test/5/8/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/27.jpg rename to lib/pixi/assets/maps/test/5/8/27.jpg diff --git a/pixi/assets/maps/test/5/8/28.jpg b/lib/pixi/assets/maps/test/5/8/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/28.jpg rename to lib/pixi/assets/maps/test/5/8/28.jpg diff --git a/pixi/assets/maps/test/5/8/29.jpg b/lib/pixi/assets/maps/test/5/8/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/29.jpg rename to lib/pixi/assets/maps/test/5/8/29.jpg diff --git a/pixi/assets/maps/test/5/8/3.jpg b/lib/pixi/assets/maps/test/5/8/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/3.jpg rename to lib/pixi/assets/maps/test/5/8/3.jpg diff --git a/pixi/assets/maps/test/5/8/30.jpg b/lib/pixi/assets/maps/test/5/8/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/30.jpg rename to lib/pixi/assets/maps/test/5/8/30.jpg diff --git a/pixi/assets/maps/test/5/8/31.jpg b/lib/pixi/assets/maps/test/5/8/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/31.jpg rename to lib/pixi/assets/maps/test/5/8/31.jpg diff --git a/pixi/assets/maps/test/5/8/4.jpg b/lib/pixi/assets/maps/test/5/8/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/4.jpg rename to lib/pixi/assets/maps/test/5/8/4.jpg diff --git a/pixi/assets/maps/test/5/8/5.jpg b/lib/pixi/assets/maps/test/5/8/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/5.jpg rename to lib/pixi/assets/maps/test/5/8/5.jpg diff --git a/pixi/assets/maps/test/5/8/6.jpg b/lib/pixi/assets/maps/test/5/8/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/6.jpg rename to lib/pixi/assets/maps/test/5/8/6.jpg diff --git a/pixi/assets/maps/test/5/8/7.jpg b/lib/pixi/assets/maps/test/5/8/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/7.jpg rename to lib/pixi/assets/maps/test/5/8/7.jpg diff --git a/pixi/assets/maps/test/5/8/8.jpg b/lib/pixi/assets/maps/test/5/8/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/8.jpg rename to lib/pixi/assets/maps/test/5/8/8.jpg diff --git a/pixi/assets/maps/test/5/8/9.jpg b/lib/pixi/assets/maps/test/5/8/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/8/9.jpg rename to lib/pixi/assets/maps/test/5/8/9.jpg diff --git a/pixi/assets/maps/test/5/9/0.jpg b/lib/pixi/assets/maps/test/5/9/0.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/0.jpg rename to lib/pixi/assets/maps/test/5/9/0.jpg diff --git a/pixi/assets/maps/test/5/9/1.jpg b/lib/pixi/assets/maps/test/5/9/1.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/1.jpg rename to lib/pixi/assets/maps/test/5/9/1.jpg diff --git a/pixi/assets/maps/test/5/9/10.jpg b/lib/pixi/assets/maps/test/5/9/10.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/10.jpg rename to lib/pixi/assets/maps/test/5/9/10.jpg diff --git a/pixi/assets/maps/test/5/9/11.jpg b/lib/pixi/assets/maps/test/5/9/11.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/11.jpg rename to lib/pixi/assets/maps/test/5/9/11.jpg diff --git a/pixi/assets/maps/test/5/9/12.jpg b/lib/pixi/assets/maps/test/5/9/12.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/12.jpg rename to lib/pixi/assets/maps/test/5/9/12.jpg diff --git a/pixi/assets/maps/test/5/9/13.jpg b/lib/pixi/assets/maps/test/5/9/13.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/13.jpg rename to lib/pixi/assets/maps/test/5/9/13.jpg diff --git a/pixi/assets/maps/test/5/9/14.jpg b/lib/pixi/assets/maps/test/5/9/14.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/14.jpg rename to lib/pixi/assets/maps/test/5/9/14.jpg diff --git a/pixi/assets/maps/test/5/9/15.jpg b/lib/pixi/assets/maps/test/5/9/15.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/15.jpg rename to lib/pixi/assets/maps/test/5/9/15.jpg diff --git a/pixi/assets/maps/test/5/9/16.jpg b/lib/pixi/assets/maps/test/5/9/16.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/16.jpg rename to lib/pixi/assets/maps/test/5/9/16.jpg diff --git a/pixi/assets/maps/test/5/9/17.jpg b/lib/pixi/assets/maps/test/5/9/17.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/17.jpg rename to lib/pixi/assets/maps/test/5/9/17.jpg diff --git a/pixi/assets/maps/test/5/9/18.jpg b/lib/pixi/assets/maps/test/5/9/18.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/18.jpg rename to lib/pixi/assets/maps/test/5/9/18.jpg diff --git a/pixi/assets/maps/test/5/9/19.jpg b/lib/pixi/assets/maps/test/5/9/19.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/19.jpg rename to lib/pixi/assets/maps/test/5/9/19.jpg diff --git a/pixi/assets/maps/test/5/9/2.jpg b/lib/pixi/assets/maps/test/5/9/2.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/2.jpg rename to lib/pixi/assets/maps/test/5/9/2.jpg diff --git a/pixi/assets/maps/test/5/9/20.jpg b/lib/pixi/assets/maps/test/5/9/20.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/20.jpg rename to lib/pixi/assets/maps/test/5/9/20.jpg diff --git a/pixi/assets/maps/test/5/9/21.jpg b/lib/pixi/assets/maps/test/5/9/21.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/21.jpg rename to lib/pixi/assets/maps/test/5/9/21.jpg diff --git a/pixi/assets/maps/test/5/9/22.jpg b/lib/pixi/assets/maps/test/5/9/22.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/22.jpg rename to lib/pixi/assets/maps/test/5/9/22.jpg diff --git a/pixi/assets/maps/test/5/9/23.jpg b/lib/pixi/assets/maps/test/5/9/23.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/23.jpg rename to lib/pixi/assets/maps/test/5/9/23.jpg diff --git a/pixi/assets/maps/test/5/9/24.jpg b/lib/pixi/assets/maps/test/5/9/24.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/24.jpg rename to lib/pixi/assets/maps/test/5/9/24.jpg diff --git a/pixi/assets/maps/test/5/9/25.jpg b/lib/pixi/assets/maps/test/5/9/25.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/25.jpg rename to lib/pixi/assets/maps/test/5/9/25.jpg diff --git a/pixi/assets/maps/test/5/9/26.jpg b/lib/pixi/assets/maps/test/5/9/26.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/26.jpg rename to lib/pixi/assets/maps/test/5/9/26.jpg diff --git a/pixi/assets/maps/test/5/9/27.jpg b/lib/pixi/assets/maps/test/5/9/27.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/27.jpg rename to lib/pixi/assets/maps/test/5/9/27.jpg diff --git a/pixi/assets/maps/test/5/9/28.jpg b/lib/pixi/assets/maps/test/5/9/28.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/28.jpg rename to lib/pixi/assets/maps/test/5/9/28.jpg diff --git a/pixi/assets/maps/test/5/9/29.jpg b/lib/pixi/assets/maps/test/5/9/29.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/29.jpg rename to lib/pixi/assets/maps/test/5/9/29.jpg diff --git a/pixi/assets/maps/test/5/9/3.jpg b/lib/pixi/assets/maps/test/5/9/3.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/3.jpg rename to lib/pixi/assets/maps/test/5/9/3.jpg diff --git a/pixi/assets/maps/test/5/9/30.jpg b/lib/pixi/assets/maps/test/5/9/30.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/30.jpg rename to lib/pixi/assets/maps/test/5/9/30.jpg diff --git a/pixi/assets/maps/test/5/9/31.jpg b/lib/pixi/assets/maps/test/5/9/31.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/31.jpg rename to lib/pixi/assets/maps/test/5/9/31.jpg diff --git a/pixi/assets/maps/test/5/9/4.jpg b/lib/pixi/assets/maps/test/5/9/4.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/4.jpg rename to lib/pixi/assets/maps/test/5/9/4.jpg diff --git a/pixi/assets/maps/test/5/9/5.jpg b/lib/pixi/assets/maps/test/5/9/5.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/5.jpg rename to lib/pixi/assets/maps/test/5/9/5.jpg diff --git a/pixi/assets/maps/test/5/9/6.jpg b/lib/pixi/assets/maps/test/5/9/6.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/6.jpg rename to lib/pixi/assets/maps/test/5/9/6.jpg diff --git a/pixi/assets/maps/test/5/9/7.jpg b/lib/pixi/assets/maps/test/5/9/7.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/7.jpg rename to lib/pixi/assets/maps/test/5/9/7.jpg diff --git a/pixi/assets/maps/test/5/9/8.jpg b/lib/pixi/assets/maps/test/5/9/8.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/8.jpg rename to lib/pixi/assets/maps/test/5/9/8.jpg diff --git a/pixi/assets/maps/test/5/9/9.jpg b/lib/pixi/assets/maps/test/5/9/9.jpg similarity index 100% rename from pixi/assets/maps/test/5/9/9.jpg rename to lib/pixi/assets/maps/test/5/9/9.jpg diff --git a/pixi/assets/modal-1.jpg b/lib/pixi/assets/modal-1.jpg similarity index 100% rename from pixi/assets/modal-1.jpg rename to lib/pixi/assets/modal-1.jpg diff --git a/pixi/assets/popupmenu-1.jpg b/lib/pixi/assets/popupmenu-1.jpg similarity index 100% rename from pixi/assets/popupmenu-1.jpg rename to lib/pixi/assets/popupmenu-1.jpg diff --git a/pixi/assets/popupmenu-2.jpg b/lib/pixi/assets/popupmenu-2.jpg similarity index 100% rename from pixi/assets/popupmenu-2.jpg rename to lib/pixi/assets/popupmenu-2.jpg diff --git a/pixi/assets/popupmenu-3.jpg b/lib/pixi/assets/popupmenu-3.jpg similarity index 100% rename from pixi/assets/popupmenu-3.jpg rename to lib/pixi/assets/popupmenu-3.jpg diff --git a/pixi/assets/popupmenu-4.jpg b/lib/pixi/assets/popupmenu-4.jpg similarity index 100% rename from pixi/assets/popupmenu-4.jpg rename to lib/pixi/assets/popupmenu-4.jpg diff --git a/pixi/assets/popupmenu-5.jpg b/lib/pixi/assets/popupmenu-5.jpg similarity index 100% rename from pixi/assets/popupmenu-5.jpg rename to lib/pixi/assets/popupmenu-5.jpg diff --git a/pixi/assets/stuttgart-library.jpg b/lib/pixi/assets/stuttgart-library.jpg similarity index 100% rename from pixi/assets/stuttgart-library.jpg rename to lib/pixi/assets/stuttgart-library.jpg diff --git a/pixi/assets/tooltip-1.jpg b/lib/pixi/assets/tooltip-1.jpg similarity index 100% rename from pixi/assets/tooltip-1.jpg rename to lib/pixi/assets/tooltip-1.jpg diff --git a/pixi/assets/tooltip-2.jpg b/lib/pixi/assets/tooltip-2.jpg similarity index 100% rename from pixi/assets/tooltip-2.jpg rename to lib/pixi/assets/tooltip-2.jpg diff --git a/pixi/assets/tooltip-3.mp4 b/lib/pixi/assets/tooltip-3.mp4 similarity index 100% rename from pixi/assets/tooltip-3.mp4 rename to lib/pixi/assets/tooltip-3.mp4 diff --git a/pixi/badge.html b/lib/pixi/badge.html similarity index 100% rename from pixi/badge.html rename to lib/pixi/badge.html diff --git a/pixi/badge.js b/lib/pixi/badge.js similarity index 100% rename from pixi/badge.js rename to lib/pixi/badge.js diff --git a/pixi/blurfilter.html b/lib/pixi/blurfilter.html similarity index 100% rename from pixi/blurfilter.html rename to lib/pixi/blurfilter.html diff --git a/pixi/blurfilter.js b/lib/pixi/blurfilter.js similarity index 100% rename from pixi/blurfilter.js rename to lib/pixi/blurfilter.js diff --git a/pixi/bundle.js b/lib/pixi/bundle.js similarity index 100% rename from pixi/bundle.js rename to lib/pixi/bundle.js diff --git a/pixi/button.html b/lib/pixi/button.html similarity index 100% rename from pixi/button.html rename to lib/pixi/button.html diff --git a/pixi/button.js b/lib/pixi/button.js similarity index 100% rename from pixi/button.js rename to lib/pixi/button.js diff --git a/pixi/buttongroup.html b/lib/pixi/buttongroup.html similarity index 100% rename from pixi/buttongroup.html rename to lib/pixi/buttongroup.html diff --git a/pixi/buttongroup.js b/lib/pixi/buttongroup.js similarity index 100% rename from pixi/buttongroup.js rename to lib/pixi/buttongroup.js diff --git a/pixi/coordinates.html b/lib/pixi/coordinates.html similarity index 100% rename from pixi/coordinates.html rename to lib/pixi/coordinates.html diff --git a/pixi/deepzoom/image.html b/lib/pixi/deepzoom/image.html similarity index 100% rename from pixi/deepzoom/image.html rename to lib/pixi/deepzoom/image.html diff --git a/pixi/deepzoom/image.js b/lib/pixi/deepzoom/image.js similarity index 100% rename from pixi/deepzoom/image.js rename to lib/pixi/deepzoom/image.js diff --git a/pixi/deepzoom/index.html b/lib/pixi/deepzoom/index.html similarity index 100% rename from pixi/deepzoom/index.html rename to lib/pixi/deepzoom/index.html diff --git a/pixi/deepzoom/loader.js b/lib/pixi/deepzoom/loader.js similarity index 100% rename from pixi/deepzoom/loader.js rename to lib/pixi/deepzoom/loader.js diff --git a/pixi/deepzoom/test.html b/lib/pixi/deepzoom/test.html similarity index 100% rename from pixi/deepzoom/test.html rename to lib/pixi/deepzoom/test.html diff --git a/pixi/deepzoom/thumbnail.test.png b/lib/pixi/deepzoom/thumbnail.test.png similarity index 100% rename from pixi/deepzoom/thumbnail.test.png rename to lib/pixi/deepzoom/thumbnail.test.png diff --git a/pixi/deepzoom/tile.js b/lib/pixi/deepzoom/tile.js similarity index 100% rename from pixi/deepzoom/tile.js rename to lib/pixi/deepzoom/tile.js diff --git a/pixi/deepzoom/tileloader.js b/lib/pixi/deepzoom/tileloader.js similarity index 100% rename from pixi/deepzoom/tileloader.js rename to lib/pixi/deepzoom/tileloader.js diff --git a/pixi/deepzoom/tiles.js b/lib/pixi/deepzoom/tiles.js similarity index 100% rename from pixi/deepzoom/tiles.js rename to lib/pixi/deepzoom/tiles.js diff --git a/pixi/flipeffect.html b/lib/pixi/flipeffect.html similarity index 100% rename from pixi/flipeffect.html rename to lib/pixi/flipeffect.html diff --git a/pixi/flipeffect.js b/lib/pixi/flipeffect.js similarity index 100% rename from pixi/flipeffect.js rename to lib/pixi/flipeffect.js diff --git a/pixi/flippable.html b/lib/pixi/flippable.html similarity index 100% rename from pixi/flippable.html rename to lib/pixi/flippable.html diff --git a/pixi/flippable.js b/lib/pixi/flippable.js similarity index 100% rename from pixi/flippable.js rename to lib/pixi/flippable.js diff --git a/pixi/flippable.test.html b/lib/pixi/flippable.test.html similarity index 100% rename from pixi/flippable.test.html rename to lib/pixi/flippable.test.html diff --git a/pixi/forcelayout.html b/lib/pixi/forcelayout.html similarity index 100% rename from pixi/forcelayout.html rename to lib/pixi/forcelayout.html diff --git a/pixi/index.html b/lib/pixi/index.html similarity index 100% rename from pixi/index.html rename to lib/pixi/index.html diff --git a/pixi/labeledgraphics.html b/lib/pixi/labeledgraphics.html similarity index 100% rename from pixi/labeledgraphics.html rename to lib/pixi/labeledgraphics.html diff --git a/pixi/labeledgraphics.js b/lib/pixi/labeledgraphics.js similarity index 100% rename from pixi/labeledgraphics.js rename to lib/pixi/labeledgraphics.js diff --git a/pixi/lib/graphology-layout-forceatlas2.js b/lib/pixi/lib/graphology-layout-forceatlas2.js similarity index 100% rename from pixi/lib/graphology-layout-forceatlas2.js rename to lib/pixi/lib/graphology-layout-forceatlas2.js diff --git a/pixi/lib/graphology-layout-forceatlas2.min.js b/lib/pixi/lib/graphology-layout-forceatlas2.min.js similarity index 100% rename from pixi/lib/graphology-layout-forceatlas2.min.js rename to lib/pixi/lib/graphology-layout-forceatlas2.min.js diff --git a/pixi/lib/graphology.min.js b/lib/pixi/lib/graphology.min.js similarity index 100% rename from pixi/lib/graphology.min.js rename to lib/pixi/lib/graphology.min.js diff --git a/pixi/list.html b/lib/pixi/list.html similarity index 100% rename from pixi/list.html rename to lib/pixi/list.html diff --git a/pixi/list.js b/lib/pixi/list.js similarity index 100% rename from pixi/list.js rename to lib/pixi/list.js diff --git a/pixi/message.html b/lib/pixi/message.html similarity index 100% rename from pixi/message.html rename to lib/pixi/message.html diff --git a/pixi/message.js b/lib/pixi/message.js similarity index 100% rename from pixi/message.js rename to lib/pixi/message.js diff --git a/pixi/modal.html b/lib/pixi/modal.html similarity index 100% rename from pixi/modal.html rename to lib/pixi/modal.html diff --git a/pixi/modal.js b/lib/pixi/modal.js similarity index 100% rename from pixi/modal.js rename to lib/pixi/modal.js diff --git a/pixi/popover.html b/lib/pixi/popover.html similarity index 100% rename from pixi/popover.html rename to lib/pixi/popover.html diff --git a/pixi/popover.js b/lib/pixi/popover.js similarity index 100% rename from pixi/popover.js rename to lib/pixi/popover.js diff --git a/pixi/popup.html b/lib/pixi/popup.html similarity index 100% rename from pixi/popup.html rename to lib/pixi/popup.html diff --git a/pixi/popup.js b/lib/pixi/popup.js similarity index 100% rename from pixi/popup.js rename to lib/pixi/popup.js diff --git a/pixi/popupmenu.html b/lib/pixi/popupmenu.html similarity index 100% rename from pixi/popupmenu.html rename to lib/pixi/popupmenu.html diff --git a/pixi/popupmenu.js b/lib/pixi/popupmenu.js similarity index 100% rename from pixi/popupmenu.js rename to lib/pixi/popupmenu.js diff --git a/pixi/progress.html b/lib/pixi/progress.html similarity index 100% rename from pixi/progress.html rename to lib/pixi/progress.html diff --git a/pixi/progress.js b/lib/pixi/progress.js similarity index 100% rename from pixi/progress.js rename to lib/pixi/progress.js diff --git a/pixi/scatter.html b/lib/pixi/scatter.html similarity index 100% rename from pixi/scatter.html rename to lib/pixi/scatter.html diff --git a/pixi/scatter.js b/lib/pixi/scatter.js similarity index 100% rename from pixi/scatter.js rename to lib/pixi/scatter.js diff --git a/pixi/slider.html b/lib/pixi/slider.html similarity index 100% rename from pixi/slider.html rename to lib/pixi/slider.html diff --git a/pixi/slider.js b/lib/pixi/slider.js similarity index 100% rename from pixi/slider.js rename to lib/pixi/slider.js diff --git a/pixi/switch.html b/lib/pixi/switch.html similarity index 100% rename from pixi/switch.html rename to lib/pixi/switch.html diff --git a/pixi/switch.js b/lib/pixi/switch.js similarity index 100% rename from pixi/switch.js rename to lib/pixi/switch.js diff --git a/pixi/test.js b/lib/pixi/test.js similarity index 100% rename from pixi/test.js rename to lib/pixi/test.js diff --git a/pixi/test/lib/Chart.bundle.min.js b/lib/pixi/test/lib/Chart.bundle.min.js similarity index 100% rename from pixi/test/lib/Chart.bundle.min.js rename to lib/pixi/test/lib/Chart.bundle.min.js diff --git a/pixi/test/lib/bulma.css b/lib/pixi/test/lib/bulma.css similarity index 100% rename from pixi/test/lib/bulma.css rename to lib/pixi/test/lib/bulma.css diff --git a/pixi/test/lib/bulma.css.map b/lib/pixi/test/lib/bulma.css.map similarity index 100% rename from pixi/test/lib/bulma.css.map rename to lib/pixi/test/lib/bulma.css.map diff --git a/pixi/test/lib/chai.js b/lib/pixi/test/lib/chai.js similarity index 100% rename from pixi/test/lib/chai.js rename to lib/pixi/test/lib/chai.js diff --git a/pixi/test/lib/fontawesome.js b/lib/pixi/test/lib/fontawesome.js similarity index 100% rename from pixi/test/lib/fontawesome.js rename to lib/pixi/test/lib/fontawesome.js diff --git a/pixi/test/lib/mocha.min.css b/lib/pixi/test/lib/mocha.min.css similarity index 100% rename from pixi/test/lib/mocha.min.css rename to lib/pixi/test/lib/mocha.min.css diff --git a/pixi/test/lib/mocha.min.css.map b/lib/pixi/test/lib/mocha.min.css.map similarity index 100% rename from pixi/test/lib/mocha.min.css.map rename to lib/pixi/test/lib/mocha.min.css.map diff --git a/pixi/test/lib/mocha.min.js b/lib/pixi/test/lib/mocha.min.js similarity index 100% rename from pixi/test/lib/mocha.min.js rename to lib/pixi/test/lib/mocha.min.js diff --git a/pixi/test/lib/mocha.min.js.map b/lib/pixi/test/lib/mocha.min.js.map similarity index 100% rename from pixi/test/lib/mocha.min.js.map rename to lib/pixi/test/lib/mocha.min.js.map diff --git a/pixi/test/test.css b/lib/pixi/test/test.css similarity index 100% rename from pixi/test/test.css rename to lib/pixi/test/test.css diff --git a/pixi/test/tests/zoomInZoomOut.js b/lib/pixi/test/tests/zoomInZoomOut.js similarity index 100% rename from pixi/test/tests/zoomInZoomOut.js rename to lib/pixi/test/tests/zoomInZoomOut.js diff --git a/pixi/test/testsuite.js b/lib/pixi/test/testsuite.js similarity index 100% rename from pixi/test/testsuite.js rename to lib/pixi/test/testsuite.js diff --git a/pixi/text.html b/lib/pixi/text.html similarity index 100% rename from pixi/text.html rename to lib/pixi/text.html diff --git a/pixi/theme.html b/lib/pixi/theme.html similarity index 100% rename from pixi/theme.html rename to lib/pixi/theme.html diff --git a/pixi/theme.js b/lib/pixi/theme.js similarity index 100% rename from pixi/theme.js rename to lib/pixi/theme.js diff --git a/pixi/thumbnail.png b/lib/pixi/thumbnail.png similarity index 100% rename from pixi/thumbnail.png rename to lib/pixi/thumbnail.png diff --git a/pixi/thumbnails/app.png b/lib/pixi/thumbnails/app.png similarity index 100% rename from pixi/thumbnails/app.png rename to lib/pixi/thumbnails/app.png diff --git a/pixi/thumbnails/application.png b/lib/pixi/thumbnails/application.png similarity index 100% rename from pixi/thumbnails/application.png rename to lib/pixi/thumbnails/application.png diff --git a/pixi/thumbnails/badge.png b/lib/pixi/thumbnails/badge.png similarity index 100% rename from pixi/thumbnails/badge.png rename to lib/pixi/thumbnails/badge.png diff --git a/pixi/thumbnails/blurfilter.png b/lib/pixi/thumbnails/blurfilter.png similarity index 100% rename from pixi/thumbnails/blurfilter.png rename to lib/pixi/thumbnails/blurfilter.png diff --git a/pixi/thumbnails/button.png b/lib/pixi/thumbnails/button.png similarity index 100% rename from pixi/thumbnails/button.png rename to lib/pixi/thumbnails/button.png diff --git a/pixi/thumbnails/buttongroup.png b/lib/pixi/thumbnails/buttongroup.png similarity index 100% rename from pixi/thumbnails/buttongroup.png rename to lib/pixi/thumbnails/buttongroup.png diff --git a/pixi/thumbnails/coordinates.png b/lib/pixi/thumbnails/coordinates.png similarity index 100% rename from pixi/thumbnails/coordinates.png rename to lib/pixi/thumbnails/coordinates.png diff --git a/pixi/thumbnails/deepzoomimage.png b/lib/pixi/thumbnails/deepzoomimage.png similarity index 100% rename from pixi/thumbnails/deepzoomimage.png rename to lib/pixi/thumbnails/deepzoomimage.png diff --git a/pixi/thumbnails/flipeffect.png b/lib/pixi/thumbnails/flipeffect.png similarity index 100% rename from pixi/thumbnails/flipeffect.png rename to lib/pixi/thumbnails/flipeffect.png diff --git a/pixi/thumbnails/flippable.png b/lib/pixi/thumbnails/flippable.png similarity index 100% rename from pixi/thumbnails/flippable.png rename to lib/pixi/thumbnails/flippable.png diff --git a/pixi/thumbnails/list.png b/lib/pixi/thumbnails/list.png similarity index 100% rename from pixi/thumbnails/list.png rename to lib/pixi/thumbnails/list.png diff --git a/pixi/thumbnails/message.png b/lib/pixi/thumbnails/message.png similarity index 100% rename from pixi/thumbnails/message.png rename to lib/pixi/thumbnails/message.png diff --git a/pixi/thumbnails/modal.png b/lib/pixi/thumbnails/modal.png similarity index 100% rename from pixi/thumbnails/modal.png rename to lib/pixi/thumbnails/modal.png diff --git a/pixi/thumbnails/popover.png b/lib/pixi/thumbnails/popover.png similarity index 100% rename from pixi/thumbnails/popover.png rename to lib/pixi/thumbnails/popover.png diff --git a/pixi/thumbnails/popup.png b/lib/pixi/thumbnails/popup.png similarity index 100% rename from pixi/thumbnails/popup.png rename to lib/pixi/thumbnails/popup.png diff --git a/pixi/thumbnails/popupmenu.png b/lib/pixi/thumbnails/popupmenu.png similarity index 100% rename from pixi/thumbnails/popupmenu.png rename to lib/pixi/thumbnails/popupmenu.png diff --git a/pixi/thumbnails/progress.png b/lib/pixi/thumbnails/progress.png similarity index 100% rename from pixi/thumbnails/progress.png rename to lib/pixi/thumbnails/progress.png diff --git a/pixi/thumbnails/scatter.png b/lib/pixi/thumbnails/scatter.png similarity index 100% rename from pixi/thumbnails/scatter.png rename to lib/pixi/thumbnails/scatter.png diff --git a/pixi/thumbnails/slider.png b/lib/pixi/thumbnails/slider.png similarity index 100% rename from pixi/thumbnails/slider.png rename to lib/pixi/thumbnails/slider.png diff --git a/pixi/thumbnails/switch.png b/lib/pixi/thumbnails/switch.png similarity index 100% rename from pixi/thumbnails/switch.png rename to lib/pixi/thumbnails/switch.png diff --git a/pixi/thumbnails/text.png b/lib/pixi/thumbnails/text.png similarity index 100% rename from pixi/thumbnails/text.png rename to lib/pixi/thumbnails/text.png diff --git a/pixi/thumbnails/tooltip.png b/lib/pixi/thumbnails/tooltip.png similarity index 100% rename from pixi/thumbnails/tooltip.png rename to lib/pixi/thumbnails/tooltip.png diff --git a/pixi/thumbnails/volatile.png b/lib/pixi/thumbnails/volatile.png similarity index 100% rename from pixi/thumbnails/volatile.png rename to lib/pixi/thumbnails/volatile.png diff --git a/pixi/timeline.js b/lib/pixi/timeline.js similarity index 100% rename from pixi/timeline.js rename to lib/pixi/timeline.js diff --git a/pixi/tooltip.html b/lib/pixi/tooltip.html similarity index 100% rename from pixi/tooltip.html rename to lib/pixi/tooltip.html diff --git a/pixi/tooltip.js b/lib/pixi/tooltip.js similarity index 100% rename from pixi/tooltip.js rename to lib/pixi/tooltip.js diff --git a/pixi/volatile.html b/lib/pixi/volatile.html similarity index 100% rename from pixi/volatile.html rename to lib/pixi/volatile.html diff --git a/pixi/volatile.js b/lib/pixi/volatile.js similarity index 100% rename from pixi/volatile.js rename to lib/pixi/volatile.js diff --git a/poppable.html b/lib/poppable.html similarity index 100% rename from poppable.html rename to lib/poppable.html diff --git a/poppable.js b/lib/poppable.js similarity index 100% rename from poppable.js rename to lib/poppable.js diff --git a/popup.html b/lib/popup.html similarity index 100% rename from popup.html rename to lib/popup.html diff --git a/popup.js b/lib/popup.js similarity index 100% rename from popup.js rename to lib/popup.js diff --git a/popupmenu.html b/lib/popupmenu.html similarity index 100% rename from popupmenu.html rename to lib/popupmenu.html diff --git a/popupmenu.js b/lib/popupmenu.js similarity index 100% rename from popupmenu.js rename to lib/popupmenu.js diff --git a/scatter.html b/lib/scatter.html similarity index 100% rename from scatter.html rename to lib/scatter.html diff --git a/scatter.js b/lib/scatter.js similarity index 100% rename from scatter.js rename to lib/scatter.js diff --git a/styleguide.html b/lib/styleguide.html similarity index 100% rename from styleguide.html rename to lib/styleguide.html diff --git a/test.html b/lib/test.html similarity index 100% rename from test.html rename to lib/test.html diff --git a/thumbnail.png b/lib/thumbnail.png similarity index 100% rename from thumbnail.png rename to lib/thumbnail.png diff --git a/thumbnails/app.png b/lib/thumbnails/app.png similarity index 100% rename from thumbnails/app.png rename to lib/thumbnails/app.png diff --git a/thumbnails/capabilities.png b/lib/thumbnails/capabilities.png similarity index 100% rename from thumbnails/capabilities.png rename to lib/thumbnails/capabilities.png diff --git a/thumbnails/classes.png b/lib/thumbnails/classes.png similarity index 100% rename from thumbnails/classes.png rename to lib/thumbnails/classes.png diff --git a/thumbnails/coordinates.png b/lib/thumbnails/coordinates.png similarity index 100% rename from thumbnails/coordinates.png rename to lib/thumbnails/coordinates.png diff --git a/thumbnails/doctest.png b/lib/thumbnails/doctest.png similarity index 100% rename from thumbnails/doctest.png rename to lib/thumbnails/doctest.png diff --git a/thumbnails/electron.png b/lib/thumbnails/electron.png similarity index 100% rename from thumbnails/electron.png rename to lib/thumbnails/electron.png diff --git a/thumbnails/events.png b/lib/thumbnails/events.png similarity index 100% rename from thumbnails/events.png rename to lib/thumbnails/events.png diff --git a/thumbnails/flippable.png b/lib/thumbnails/flippable.png similarity index 100% rename from thumbnails/flippable.png rename to lib/thumbnails/flippable.png diff --git a/thumbnails/frames.png b/lib/thumbnails/frames.png similarity index 100% rename from thumbnails/frames.png rename to lib/thumbnails/frames.png diff --git a/thumbnails/imageloader.png b/lib/thumbnails/imageloader.png similarity index 100% rename from thumbnails/imageloader.png rename to lib/thumbnails/imageloader.png diff --git a/thumbnails/index.png b/lib/thumbnails/index.png similarity index 100% rename from thumbnails/index.png rename to lib/thumbnails/index.png diff --git a/thumbnails/index_ios.png b/lib/thumbnails/index_ios.png similarity index 100% rename from thumbnails/index_ios.png rename to lib/thumbnails/index_ios.png diff --git a/thumbnails/inspect.png b/lib/thumbnails/inspect.png similarity index 100% rename from thumbnails/inspect.png rename to lib/thumbnails/inspect.png diff --git a/thumbnails/interaction.png b/lib/thumbnails/interaction.png similarity index 100% rename from thumbnails/interaction.png rename to lib/thumbnails/interaction.png diff --git a/thumbnails/interface.png b/lib/thumbnails/interface.png similarity index 100% rename from thumbnails/interface.png rename to lib/thumbnails/interface.png diff --git a/thumbnails/notfound.png b/lib/thumbnails/notfound.png similarity index 100% rename from thumbnails/notfound.png rename to lib/thumbnails/notfound.png diff --git a/thumbnails/poppable.png b/lib/thumbnails/poppable.png similarity index 100% rename from thumbnails/poppable.png rename to lib/thumbnails/poppable.png diff --git a/thumbnails/popup.png b/lib/thumbnails/popup.png similarity index 100% rename from thumbnails/popup.png rename to lib/thumbnails/popup.png diff --git a/thumbnails/popupmenu.png b/lib/thumbnails/popupmenu.png similarity index 100% rename from thumbnails/popupmenu.png rename to lib/thumbnails/popupmenu.png diff --git a/thumbnails/scatter.png b/lib/thumbnails/scatter.png similarity index 100% rename from thumbnails/scatter.png rename to lib/thumbnails/scatter.png diff --git a/thumbnails/styleguide.png b/lib/thumbnails/styleguide.png similarity index 100% rename from thumbnails/styleguide.png rename to lib/thumbnails/styleguide.png diff --git a/thumbnails/test.png b/lib/thumbnails/test.png similarity index 100% rename from thumbnails/test.png rename to lib/thumbnails/test.png diff --git a/thumbnails/uitest.png b/lib/thumbnails/uitest.png similarity index 100% rename from thumbnails/uitest.png rename to lib/thumbnails/uitest.png diff --git a/thumbnails/utils.png b/lib/thumbnails/utils.png similarity index 100% rename from thumbnails/utils.png rename to lib/thumbnails/utils.png diff --git a/uitest.html b/lib/uitest.html similarity index 100% rename from uitest.html rename to lib/uitest.html diff --git a/uitest.js b/lib/uitest.js similarity index 100% rename from uitest.js rename to lib/uitest.js diff --git a/utils.html b/lib/utils.html similarity index 100% rename from utils.html rename to lib/utils.html diff --git a/utils.js b/lib/utils.js similarity index 100% rename from utils.js rename to lib/utils.js diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f42df07 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "iwmlib", + "version": "1.0.0", + "lockfileVersion": 1 +} diff --git a/package.json b/package.json index d9ee95d..a0583f2 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,22 @@ { - "name": "iwmlib", - "version": "1.0.0", - "description": "An Open Source library for multi-touch, WebGL powered applications.", - "main": "index.js", - "directories": { - "example": "examples" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://gitea.iwm-tuebingen.de:IWMBrowser/iwmlib.git" - }, - "author": "", - "license": "LGPL-3.0-or-later" + "name": "iwmlib", + "version": "1.0.0", + "description": "An Open Source library for multi-touch, WebGL powered applications.", + "main": "index.js", + "directories": { + "example": "examples" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "rollup --config ./rollup.config.js", + "watch": "rollup --watch --config ./rollup.config.js", + "3rdparty": "python3 ./lib/3rdparty/create_all_js.py" + }, + "repository": { + "type": "git", + "url": "https://gitea.iwm-tuebingen.de:IWMBrowser/iwmlib.git" + }, + "author": "", + "license": "LGPL-3.0-or-later", + "devDependencies": {} } diff --git a/pixi/all.js b/pixi/all.js deleted file mode 100644 index 6bccd97..0000000 --- a/pixi/all.js +++ /dev/null @@ -1,13965 +0,0 @@ -(function () { - 'use strict'; - - /** - * Class that represents a PixiJS Theme. - * - * @example - * // Create the theme - * const yellow = new Theme({ - * fill: 0xfecd2d, - * fillActive: 0xfe9727, - * strokeActive: 0xfecd2d, - * strokeActiveWidth: 4, - * textStyle: { - * fill: 0x5ec7f8 - * }, - * textStyleActive: { - * fill: 0x5954d3 - * }, - * textStyleLarge: { - * fontSize: 36 - * } - * }) - * - * // Create the app and apply the new theme to it - * const app = new PIXIApp({ - * view: canvas, - * width: 450, - * height: 150, - * theme: yellow - * }).setup().run() - * - * @class - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/theme.html|DocTest} - */ - class Theme { - - /** - * Creates an instance of a Theme. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the theme. - * @param {number} [opts.margin=10] - The outer spacing (distance to other objects) from the border. - * @param {number} [opts.padding=10] - The inner spacing (distance from icon and/or label) to the border. - * @param {number} [opts.radius=4] - The radius used when drawing a rounded rectangle. - * @param {number} [opts.fast=0.25] - The duration of time when it has to be fast. - * @param {number} [opts.normal=0.5] - The duration of time when it has to be normal. - * @param {number} [opts.slow=1] - The duration of time when it has to be slow. - * @param {number} [opts.primaryColor=0x5ec7f8] - The primary color of the theme. - * @param {number} [opts.color1=0x282828] - The first color of the theme. For example used for the background. - * @param {number} [opts.color2=0xf6f6f6] - The second color of the theme. For example used for the border. - * @param {number} [opts.fill=color1] - The color of the background as a hex value. - * @param {number} [opts.fillAlpha=1] - The alpha value of the background. - * @param {number} [opts.fillActive=color1] - The color of the background when activated. - * @param {number} [opts.fillActiveAlpha=1] - The alpha value of the background when activated. - * @param {number} [opts.stroke=color2] - The color of the border as a hex value. - * @param {number} [opts.strokeWidth=0.6] - The width of the border in pixel. - * @param {number} [opts.strokeAlpha=1] - The alpha value of the border. - * @param {number} [opts.strokeActive=color2] - The color of the border when activated. - * @param {number} [opts.strokeActiveWidth=0.6] - The width of the border in pixel when activated. - * @param {number} [opts.strokeActiveAlpha=1] - The alpha value of the border when activated. - * @param {number} [opts.iconColor=color2] - The color of the icon (set by the tint property) as a hex value. - * @param {number} [opts.iconColorActive=colorPrimary] - The color of the icon when activated. - * @param {number} [opts.background=color1] - The color of a background for a component (e.g. at the Modal class). - * @param {object} [opts.textStyle={}] - A textstyle object for the styling of text. See PIXI.TextStyle - * for possible options. Default object: - * @param {string} [opts.textStyle.fontFamily="Avenir Next", "Open Sans", "Segoe UI", ...] - The font family. - * @param {string} [opts.textStyle.fontWeight=400] - The font weight. - * @param {number} [opts.textStyle.fontSize=16] - The font size. - * @param {number} [opts.textStyle.fill=color2] - The fill color. - * @param {number} [opts.textStyle.stroke=color1] - The stroke color. - * @param {number} [opts.textStyle.strokeThickness=0] - The thickness of the stroke. - * @param {number} [opts.textStyle.miterLimit=1] - The meter limit. - * @param {string} [opts.textStyle.lineJoin=round] - The line join. - * @param {object} [opts.textStyleActive=textStyle + {fill: primaryColor}] - A textstyle object which is used - * for actived text. - * @param {object} [opts.textStyleSmall=textStyle + {fontSize: -= 3}] - A textstyle object which is used for - * small text. - * @param {object} [opts.textStyleSmallActive=textStyleSmall + {fill: primaryColor}] - A textstyle object which - * is used for small actived text. - * @param {object} [opts.textStyleLarge=textStyle + {fontSize: += 3}] - A textstyle object which is used for - * large text. - * @param {object} [opts.textStyleLargeActive=textStyleLarge + {fill: primaryColor}] - A textstyle object which - * is used for large actived text. - */ - constructor(opts = {}) { - - const colorPrimary = opts.primaryColor != null ? opts.primaryColor : 0x5ec7f8; // blue - const color1 = opts.color1 != null ? opts.color1 : 0x282828; // black - const color2 = opts.color2 != null ? opts.color2 : 0xf6f6f6; // white - - this.opts = Object.assign({}, { - margin: 12, - padding: 12, - radius: 4, - fast: .25, - normal: .5, - slow: 1, - primaryColor: colorPrimary, - color1: color1, - color2: color2, - fill: color1, - fillAlpha: 1, - fillActive: color1, - fillActiveAlpha: 1, - stroke: color2, - strokeWidth: .6, - strokeAlpha: 1, - strokeActive: color2, - strokeActiveWidth: .6, - strokeActiveAlpha: 1, - iconColor: color2, - iconColorActive: colorPrimary, - background: color1 - }, opts); - - // Set textStyle and variants - this.opts.textStyle = Object.assign({}, { - fontFamily: '"Avenir Next", "Open Sans", "Segoe UI", "Roboto", "Helvetica Neue", -apple-system, system-ui, BlinkMacSystemFont, Arial, sans-serif !default', - fontWeight: '500', - fontSize: 18, - fill: color2, - stroke: color1, - strokeThickness: 0, - miterLimit: 1, - lineJoin: 'round' - }, this.opts.textStyle); - this.opts.textStyleSmall = Object.assign({}, this.opts.textStyle, {fontSize: this.opts.textStyle.fontSize - 3}, this.opts.textStyleSmall); - this.opts.textStyleLarge = Object.assign({}, this.opts.textStyle, {fontSize: this.opts.textStyle.fontSize + 3}, this.opts.textStyleLarge); - this.opts.textStyleActive = Object.assign({}, this.opts.textStyle, {fill: this.opts.primaryColor}, this.opts.textStyleActive); - this.opts.textStyleSmallActive = Object.assign({}, this.opts.textStyleSmall, {fill: this.opts.primaryColor}, this.opts.textStyleSmallActive); - this.opts.textStyleLargeActive = Object.assign({}, this.opts.textStyleLarge, {fill: this.opts.primaryColor}, this.opts.textStyleLargeActive); - - Object.assign(this, this.opts); - } - - /** - * Factory function - * - * @static - * @param {string} theme=dark - The name of the theme to load. - * @return {Theme} Returns a newly created Theme object. - */ - static fromString(theme) { - - if (theme && typeof theme === 'object') { - return theme - } - - switch (theme) { - case 'light': - return new ThemeLight() - case 'red': - return new ThemeRed() - default: - return new ThemeDark() - } - } - } - - /** - * Class that represents a PixiJS ThemeDark. - * - * @example - * // Create the app with a new dark theme - * const app = new PIXIApp({ - * view: canvas, - * width: 450, - * height: 150, - * theme: new ThemeDark() - * }).setup().run() - * - * @class - * @extends Theme - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/theme.html|DocTest} - */ - class ThemeDark extends Theme { - - } - - /** - * Class that represents a PixiJS ThemeLight. - * The color1 is set to 0xf6f6f6, color2 to 0x282828. - * - * @example - * // Create the app with a new light theme - * const app = new PIXIApp({ - * view: canvas, - * width: 450, - * height: 150, - * theme: new ThemeLight() - * }).setup().run() - * - * @class - * @extends Theme - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/theme.html|DocTest} - */ - class ThemeLight extends Theme { - - /** - * Creates an instance of a ThemeLight. - * - * @constructor - */ - constructor() { - - super({color1: 0xf6f6f6, color2: 0x282828}); - } - } - - /** - * Class that represents a PixiJS ThemeRed. - * The primaryColor is set to 0xd92f31. - * - * @example - * // Create the app with a new red theme - * const app = new PIXIApp({ - * view: canvas, - * width: 450, - * height: 150, - * theme: new ThemeRed() - * }).setup().run() - * - * @class - * @extends Theme - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/theme.html|DocTest} - */ - class ThemeRed extends Theme { - - /** - * Creates an instance of a ThemeRed. - * - * @constructor - */ - constructor() { - - super({primaryColor: 0xd92f31}); - } - } - - /** - * Class that represents a PixiJS Progress. - * - * @example - * // Create the progress - * const progress = new Progress({ - * app: app - * }) - * - * // Add the progress to a DisplayObject - * app.scene.addChild(progress) - * - * @class - * @extends PIXI.Container - * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/progress.html|DocTest} - */ - class Progress extends PIXI.Container { - - /** - * Creates an instance of a Progress. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the progress. - * @param {number} [opts.id=auto generated] - The id of the progress. - * @param {PIXIApp} [opts.app=window.app] - The app where the progress belongs to. - * @param {number} [opts.width] - The width of the progress bar. When not set, the width is the size of the app - * minus 2 * opts.margin. - * @param {number} [opts.height=2] - The height of the progress bar. - * @param {string|Theme} [opts.theme=dark] - The theme to use for this progress. Possible values are dark, light, red - * or a Theme object. - * @param {number} [opts.margin=100] - The outer spacing to the edges of the app. - * @param {number} [opts.padding=0] - The inner spacing (distance from icon and/or label) to the border. - * @param {number} [opts.fill=Theme.fill] - The color of the progress background as a hex value. - * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. - * @param {number} [opts.fillActive=Theme.primaryColor] - The color of the progress background when activated. - * @param {number} [opts.fillActiveAlpha=Theme.fillActiveAlpha] - The alpha value of the background when activated. - * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. - * @param {number} [opts.strokeWidth=0] - The width of the border in pixel. - * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. - * @param {number} [opts.strokeActive=Theme.strokeActive] - The color of the border when activated. - * @param {number} [opts.strokeActiveWidth=0] - The width of the border in pixel when activated. - * @param {number} [opts.strokeActiveAlpha=Theme.strokeActiveAlpha] - The alpha value of the border when activated. - * @param {boolean} [opts.background=false] - The alpha value of the border when activated. - * @param {number} [opts.backgroundFill=Theme.background] - A textstyle object for the styling of the label. See PIXI.TextStyle - * for possible options. - * @param {number} [opts.backgroundFillAlpha=1] - A textstyle object for the styling of the label when the - * progress is activated. See PIXI.TextStyle for possible options. - * @param {number} [opts.radius=Theme.radius] - The radius of the four corners of the progress (which is a rounded rectangle). - * @param {boolean} [opts.destroyOnComplete=true] - Should the progress bar destroy itself after reaching 100 %? - * @param {boolean} [opts.visible=true] - Is the progress initially visible (property visible)? - */ - constructor(opts = {}) { - - super(); - - const theme = Theme.fromString(opts.theme); - this.theme = theme; - - this.opts = Object.assign({}, { - id: PIXI.utils.uid(), - app: window.app, - width: null, - height: 2, - margin: 100, - padding: 0, - fill: theme.fill, - fillAlpha: theme.fillAlpha, - fillActive: theme.primaryColor, - fillActiveAlpha: theme.fillActiveAlpha, - stroke: theme.stroke, - strokeWidth: 0, - strokeAlpha: theme.strokeAlpha, - strokeActive: theme.strokeActive, - strokeActiveWidth: 0, - strokeActiveAlpha: theme.strokeActiveAlpha, - background: false, - backgroundFill: theme.background, - backgroundFillAlpha: 1, - radius: theme.radius, - destroyOnComplete: true, - visible: true - }, opts); - - this.id = this.opts.id; - - this.background = null; - this.bar = null; - this.barActive = null; - - this.alpha = 0; - - this.visible = this.opts.visible; - - this._progress = 0; - - // setup - //----------------- - this.setup(); - - // layout - //----------------- - this.layout(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {Progress} A reference to the progress for chaining. - */ - setup() { - - // interaction - //----------------- - this.on('added', e => { - this.show(); - }); - - // background - //----------------- - if (this.opts.background) { - const background = new PIXI.Graphics(); - this.background = background; - this.addChild(background); - } - - // bar - //----------------- - const bar = new PIXI.Graphics(); - this.bar = bar; - this.addChild(bar); - - const barActive = new PIXI.Graphics(); - this.barActive = barActive; - this.bar.addChild(barActive); - - return this - } - - /** - * Should be called to refresh the layout of the progress. Can be used after resizing. - * - * @return {Progress} A reference to the progress for chaining. - */ - layout() { - - const width = this.opts.app.size.width; - const height = this.opts.app.size.height; - - // background - //----------------- - if (this.opts.background) { - this.background.clear(); - this.background.beginFill(this.opts.backgroundFill, this.opts.backgroundFillAlpha); - this.background.drawRect(0, 0, width, height); - this.background.endFill(); - } - - this.draw(); - - return this - } - - /** - * Draws the canvas. - * - * @private - * @return {Progress} A reference to the progress for chaining. - */ - draw() { - - this.bar.clear(); - this.barActive.clear(); - - this.drawBar(); - this.drawBarActive(); - - return this - } - - /** - * Draws the bar. - * - * @private - * @return {Progress} A reference to the progress for chaining. - */ - drawBar() { - - const width = this.opts.app.size.width; - const height = this.opts.app.size.height; - - this.radius = this.opts.radius; - if ((this.radius * 2) > this.opts.height) { - this.radius = this.opts.height / 2; - } - - const wantedWidth = this.opts.width || (width - (2 * this.opts.margin)); - const wantedHeight = this.opts.height; - - this.bar.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); - this.bar.beginFill(this.opts.fill, this.opts.fillAlpha); - if (this.radius > 1) { - this.bar.drawRoundedRect(0, 0, wantedWidth, wantedHeight, this.radius); - } else { - this.bar.drawRect(0, 0, wantedWidth, wantedHeight); - } - this.bar.endFill(); - - this.bar.x = width / 2 - this.bar.width / 2; - this.bar.y = height / 2 - this.bar.height / 2; - - return this - } - - /** - * Draws the active bar. - * - * @private - * @return {Progress} A reference to the progress for chaining. - */ - drawBarActive() { - - const wantedWidth = this.bar.width - (2 * this.opts.padding); - const wantedHeight = this.bar.height - (2 * this.opts.padding); - - const barActiveWidth = wantedWidth * this._progress / 100; - - this.barActive.lineStyle(this.opts.strokeActiveWidth, this.opts.strokeActive, this.opts.strokeActiveAlpha); - this.barActive.beginFill(this.opts.fillActive, this.opts.fillActiveAlpha); - if (barActiveWidth > 0) { - if (this.radius > 1) { - this.barActive.drawRoundedRect(0, 0, barActiveWidth, wantedHeight, this.radius); - } else { - this.barActive.drawRect(0, 0, barActiveWidth, wantedHeight); - } - } - this.barActive.endFill(); - - this.barActive.x = this.opts.padding; - this.barActive.y = this.opts.padding; - - return this - } - - /** - * Shows the progress (sets his alpha values to 1). - * - * @return {Progress} A reference to the progress for chaining. - */ - show() { - TweenLite.to(this, this.theme.fast, {alpha: 1}); - - return this - } - - /** - * Hides the progress (sets his alpha values to 1). - * - * @return {Progress} A reference to the progress for chaining. - */ - hide() { - TweenLite.to(this, this.theme.fast, {alpha: 0, onComplete: () => this.visible = false}); - - return this - } - - /** - * Gets or sets the progress. Has to be a number between 0 and 100. - * - * @member {number} - */ - get progress() { - return this._progress - } - set progress(value) { - - value = Math.round(value); - - if (value < 0) { - value = 0; - } - - if (value > 100) { - value = 100; - } - - TweenLite.to(this, this.theme.normal, { - _progress: value, - onUpdate: () => this.draw(), - onComplete: () => { - if (value === 100 && this.opts.destroyOnComplete) { - TweenLite.to(this, this.theme.fast, { - alpha: 0, - onComplete: () => this.destroy({children: true}) - }); - } - } - }); - } - } - - /** - * Class that represents a PixiJS AbstractPopup. - * The class is used for various other Popup-like classes - * like Popup, Message, Tooltip... - * - * @class - * @abstract - * @extends PIXI.Graphics - * @see {@link http://pixijs.download/dev/docs/PIXI.Graphics.html|PIXI.Graphics} - */ - class AbstractPopup extends PIXI.Graphics { - - /** - * Creates an instance of an AbstractPopup (only for internal use). - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the popup. - * @param {number} [opts.id=auto generated] - The id of the popup. - * @param {number} [opts.x=0] - The x position of the popup. Can be also set after creation with popup.x = 0. - * @param {number} [opts.y=0] - The y position of the popup. Can be also set after creation with popup.y = 0. - * @param {string|Theme} [opts.theme=dark] - The theme to use for this popup. Possible values are dark, light, red - * or a Theme object. - * @param {string|number|PIXI.Text} [opts.header] - The heading inside the popup as a string, a number (will be - * converted to a text) or as a PIXI.Text object. - * @param {string|number|PIXI.DisplayObject} [opts.content] - A text, a number (will be converted to a text) or - * an PIXI.DisplayObject as the content of the popup. - * @param {number} [opts.minWidth=320] - The minimum width of the popup. - * @param {number} [opts.minHeight=130] - The minimum height of the popup. - * @param {number} [opts.padding=Theme.padding] - The inner spacing (distance from header and content) the the border. - * @param {number} [opts.fill=Theme.fill] - The color of the button background as a hex value. - * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. - * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. - * @param {number} [opts.strokeWidth=Theme.strokeWidth] - The width of the border in pixel. - * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. - * @param {object} [opts.headerStyle=Theme.textStyleLarge] - A textstyle object for the styling of the header. See PIXI.TextStyle - * for possible options. - * @param {object} [opts.textStyle=Theme.textStyleSmall] - A textstyle object for the styling of the text. See PIXI.TextStyle - * for possible options. - * @param {number} [opts.radius=Theme.radius] - The radius of the four corners of the popup (which is a rounded rectangle). - * @param {hiddenCallback} [opts.onHidden] - Executed when the popup gets hidden. - * @param {boolean} [opts.visible=true] - Is the popup initially visible (property visible)? - * @param {string} [opts.orientation] - When set to portrait, the popup cannot be displayed in landscape mode. When set - * to landscape, the popup cannot be displayed in portrait mode. - */ - constructor(opts = {}) { - - super(); - - const theme = Theme.fromString(opts.theme); - this.theme = theme; - - this.opts = Object.assign({}, { - id: PIXI.utils.uid(), - x: 0, - y: 0, - header: null, // null or null - content: null, // null or String or PIXI.DisplayObject - minWidth: 320, - minHeight: 130, - maxWidth: null, - padding: theme.padding, - fill: theme.fill, - fillAlpha: theme.fillAlpha, - stroke: theme.stroke, - strokeWidth: theme.strokeWidth, - strokeAlpha: theme.strokeAlpha, - headerStyle: theme.textStyleLarge, - textStyle: theme.textStyleSmall, - radius: theme.radius, - onHidden: null, - visible: true, - orientation: null - }, opts); - - this.id = this.opts.id; - - this.headerStyle = new PIXI.TextStyle(this.opts.headerStyle); - this.textStyle = new PIXI.TextStyle(this.opts.textStyle); - - if (this.opts.maxWidth) { - this.headerStyle.wordWrap = true; - this.headerStyle.wordWrapWidth = this.opts.maxWidth - (2 * this.opts.padding); - - this.textStyle.wordWrap = true; - this.textStyle.wordWrapWidth = this.opts.maxWidth - (2 * this.opts.padding); - } - - this.alpha = 0; - this.visible = this.opts.visible; - - this._header = null; - this._content = null; - - // position - this.x = this.opts.x; - this.y = this.opts.y; - - // padding - this.innerPadding = this.opts.padding * 1.5; - - // interaction - //----------------- - this.interactive = true; - this.on('added', e => { - this.show(); - }); - } - - /** - * Creates the framework and instantiates everything. - * - * @private - * @return {AbstractPopup} A reference to the popup for chaining. - */ - setup() { - - // position - //----------------- - this.sy = this.opts.padding; - - // header - //----------------- - if (this.opts.header != null) { - - let header = null; - - if (this.opts.header instanceof PIXI.Text) { - header = this.opts.header; - } else if (typeof this.opts.header === 'number') { - header = new PIXI.Text(this.opts.header.toString(), this.headerStyle); - } else { - header = new PIXI.Text(this.opts.header, this.headerStyle); - } - - header.x = this.opts.padding; - header.y = this.sy; - - this.addChild(header); - - this.sy += header.height; - - this._header = header; - } - - if (this.opts.header && this.opts.content) { - this.sy += this.innerPadding; - } - - // content - //----------------- - if (this.opts.content != null) { - - let content = null; - - if (typeof this.opts.content === 'string') { - content = new PIXI.Text(this.opts.content, this.textStyle); - } else if (typeof this.opts.content === 'number') { - content = new PIXI.Text(this.opts.content.toString(), this.textStyle); - } else { - content = this.opts.content; - } - - content.x = this.opts.padding; - content.y = this.sy; - - this.sy += content.height; - - this.addChild(content); - - this._content = content; - } - - return this - } - - /** - * Should be called to refresh the layout of the popup. Can be used after resizing. - * - * @return {AbstractPopup} A reference to the popup for chaining. - */ - layout() { - - // wanted width & wanted height - //----------------- - const padding = this.opts.padding; - const size = this.getInnerSize(); - const width = size.width + (2 * padding); - const height = size.height + (2 * padding); - - this.wantedWidth = Math.max(width, this.opts.minWidth); - this.wantedHeight = Math.max(height, this.opts.minHeight); - - if (this.opts.maxWidth) { - this.wantedWidth = Math.min(this.wantedWidth, this.opts.maxWidth); - } - - if (this.opts.radius * 2 > this.wantedWidth) { - this.wantedWidth = this.opts.radius * 2; - } - - if (this.opts.radius * 2 > this.wantedHeight) { - this.wantedHeight = this.opts.radius * 2; - } - - switch (this.opts.orientation) { - case 'portrait': - if (this.wantedWidth > this.wantedHeight) { - this.wantedHeight = this.wantedWidth; - } - break - case 'landscape': - if (this.wantedHeight > this.wantedWidth) { - this.wantedWidth = this.wantedHeight; - } - break - } - - this.draw(); - - return this - } - - /** - * Draws the canvas. - * - * @private - * @return {AbstractPopup} A reference to the popup for chaining. - */ - draw() { - - const square = Math.round(this.wantedWidth) === Math.round(this.wantedHeight); - const diameter = Math.round(this.opts.radius * 2); - - this.clear(); - this.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); - this.beginFill(this.opts.fill, this.opts.fillAlpha); - if (square && diameter === this.wantedWidth) { - this.drawCircle(this.wantedWidth / 2, this.wantedHeight / 2, this.opts.radius); - } else { - this.drawRoundedRect(0, 0, this.wantedWidth, this.wantedHeight, this.opts.radius); - } - this.endFill(); - - return this - } - - /** - * Calculates the size of the children of the AbstractPopup. - * Cannot use getBounds() because it is not updated when children - * are removed. - * - * @private - * @returns {object} An JavaScript object width the keys width and height. - */ - getInnerSize() { - - let width = 0; - let height = 0; - - if (this._header) { - width = this._header.width; - height = this._header.height; - } - - if (this._header && this._content) { - height += this.innerPadding; - } - - if (this._content) { - width = Math.max(width, this._content.width); - height += this._content.height; - } - - return {width, height} - } - - /** - * Shows the popup (sets his alpha values to 1). - * - * @param {callback} [cb] - Executed when show animation was completed. - * @return {AbstractPopup} A reference to the popup for chaining. - */ - show(cb) { - - TweenLite.to(this, this.theme.fast, { - alpha: 1, - onComplete: () => { - if (cb) { - cb.call(this); - } - } - }); - - return this - } - - /** - * Hides the popup (sets his alpha values to 0). - * - * @param {callback} [cb] - Executed when hide animation was completed. - * @return {AbstractPopup} A reference to the popup for chaining. - */ - hide(cb) { - - TweenLite.to(this, this.theme.fast, { - alpha: 0, - onComplete: () => { - this.visible = false; - if (cb) { - cb.call(this); - } - } - }); - - if (this.opts.onHidden) { - this.opts.onHidden.call(this, this); - } - - return this - } - - /** - * Sets or gets the header. The getter always returns a PIXI.Text object. The setter can receive - * a string, a number or a PIXI.Text object. - * - * @member {string|number|PIXI.Text} - */ - get header() { - return this._header - } - set header(value) { - if (this._header) { - this._header.destroy(); - } - this.opts.header = value; - this.setup().layout(); - } - - /** - * Sets or gets the content. The getter always returns an PIXI.DisplayObject. The setter can receive - * a string, a number or a PIXI.DisplayObject. - * - * @member {string|number|PIXI.DisplayObject} - */ - get content() { - return this._content - } - set content(value) { - if (this._content) { - this._content.destroy(); - } - this.opts.content = value; - this.setup().layout(); - } - } - - /** - * Class that represents a PixiJS Tooltip. - * - * @example - * // Create the app - * const app = new PIXIApp({ - * view: canvas, - * width: 900, - * height: 250 - * }).setup().run() - * - * // Add an DisplayObject to the app - * const circle = new PIXI.Graphics() - * circle.beginFill(0x5251a3) - * circle.drawCircle(50, 50, 40) - * app.scene.addChild(circle) - * - * const tooltip = new Tooltip({ - * object: circle, - * container: app.scene, - * content: 'Das Gesetz ist der Freund des Schwachen.' - * }) - * - * @class - * @extends AbstractPopup - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/tooltip.html|DocTest} - */ - class Tooltip extends AbstractPopup { - - /** - * Creates an instance of a Tooltip. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the tooltip. - * @param {number} [opts.minWidth=0] - The minimum width of the tooltip. - * @param {number} [opts.minHeight=0] - The minimum height of the tooltip. - * @param {number} [opts.padding=Theme.padding / 2] - The inner spacing of the tooltip. - * @param {PIXI.DisplayObject} opts.object - The object, where the tooltip should be displayed. - * @param {PIXI.DisplayObject} [opts.container=object] - The container where the tooltip should be attached to. - * @param {number} [opts.offsetLeft=8] - The horizontal shift of the tooltip. - * @param {number} [opts.offsetTop=-8] - The vertical shift of the tooltip. - * @param {number} [opts.delay=0] - A delay, after which the tooltip should be opened. - */ - constructor(opts = {}) { - - const theme = Theme.fromString(opts.theme); - - opts = Object.assign({}, { - minWidth: 0, - minHeight: 0, - padding: theme.padding / 2, - object: null, - container: null, - offsetLeft: 8, - offsetTop: -8, - delay: 0 - }, opts); - - opts.container = opts.container || opts.object; - - super(opts); - - // setup - //----------------- - this.setup(); - - // layout - //----------------- - this.layout(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {Tooltip} A reference to the tooltip for chaining. - */ - setup() { - - super.setup(); - - // bind events this - //----------------- - this.interactive = true; - - let mouseoverTooltip = false; - - this.on('mouseover', e => { - mouseoverTooltip = true; - }); - - this.on('mouseout', e => { - mouseoverTooltip = false; - if (!mouseoverObject) { - this.hide(() => { - this.opts.container.removeChild(this); - }); - } - }); - - // bind events object - //----------------- - const object = this.opts.object; - object.interactive = true; - - let mouseoverObject = false; - - object.on('mouseover', e => { - - this.timeout = window.setTimeout(() => { - mouseoverObject = true; - this.visible = true; - this.opts.container.addChild(this); - this.setPosition(e); - }, this.opts.delay * 1000); - }); - - object.on('mousemove', e => { - if (mouseoverObject) { - this.setPosition(e); - } - }); - - object.on('mouseout', e => { - mouseoverObject = false; - window.clearTimeout(this.timeout); - if (!mouseoverTooltip) { - this.hide(() => { - this.opts.container.removeChild(this); - }); - } - }); - - return this - } - - /** - * Calculates and sets the position of the tooltip. - * - * @private - * @return {Tooltip} A reference to the tooltip for chaining. - */ - setPosition(e) { - - const position = e.data.getLocalPosition(this.opts.container); - - this.x = position.x + this.opts.offsetLeft; - this.y = position.y + this.opts.offsetTop - this.height; - - return this - } - } - - /** - * Class that represents a PixiJS Badge. - * - * @example - * // Create the app - * const app = new PIXIApp({ - * view: canvas, - * width: 900, - * height: 250 - * }).setup().run() - * - * // Add an DisplayObject to the app - * const circle = new PIXI.Graphics() - * circle.beginFill(0x5251a3) - * circle.drawCircle(50, 50, 40) - * app.scene.addChild(circle) - * - * const badge1 = new Badge({ - * object: circle, - * container: app.scene, - * content: 'Das Gesetz ist der Freund des Schwachen.' - * }) - * - * @class - * @extends AbstractPopup - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/badge.html|DocTest} - */ - class Badge extends AbstractPopup { - - /** - * Creates an instance of a Badge. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the badge. - * @param {number} [opts.minWidth=0] - The minimum width of the badge. - * @param {number} [opts.minHeight=0] - The minimum height of the badge. - * @param {number} [opts.padding=Theme.padding / 2] - The inner spacing of the badge. - * @param {string|object} [opts.tooltip] - A string for the label of the tooltip or an object to configure the tooltip - * to display. - */ - constructor(opts = {}) { - - const theme = Theme.fromString(opts.theme); - - opts = Object.assign({}, { - minWidth: 0, - minHeight: 0, - padding: theme.padding / 2, - tooltip: null - }, opts); - - super(opts); - - this.tooltip = null; - - // setup - //----------------- - this.setup(); - - // layout - //----------------- - this.layout(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @override - * @return {Badge} A reference to the badge for chaining. - */ - setup() { - - super.setup(); - - // tooltip - //----------------- - if (this.opts.tooltip) { - if (typeof this.opts.tooltip === 'string') { - this.tooltip = new Tooltip({object: this, content: this.opts.tooltip}); - } else { - this.opts.tooltip = Object.assign({}, {object: this}, this.opts.tooltip); - this.tooltip = new Tooltip(this.opts.tooltip); - } - } - - return this - } - - /** - * Should be called to refresh the layout of the badge. Can be used after resizing. - * - * @override - * @return {Badge} A reference to the badge for chaining. - */ - layout() { - - super.layout(); - - this.content.x = this.width / 2 - this.content.width / 2 - this.opts.strokeWidth / 2; - this.content.y = this.height / 2 - this.content.height / 2 - this.opts.strokeWidth / 2; - - return this - } - } - - class Events$1 { - - static stop(event) { - event.preventDefault(); - event.stopPropagation(); - } - - static extractPoint(event) { - switch (event.constructor.name) { - case 'TouchEvent': - for (let i = 0; i < event.targetTouches.length; i++) { - let t = event.targetTouches[i]; - return { x: t.clientX, y: t.clientY } - } - break - default: - return { x: event.clientX, y: event.clientY } - } - } - - static isCaptured(event) { - if (event.__capturedBy) - return true - return false - } - - static capturedBy(event, obj) { - event.__capturedBy = obj; - } - - static isMouseDown(event) { - // Attempts to clone the which attribute of events failed in WebKit. May - // be this is a bug or a security feature. Workaround: we introduce - // a mouseDownSubstitute attribute that can be assigned to cloned - // events after instantiation. - if (Reflect.has(event, 'mouseDownSubstitute')) - return event.mouseDownSubstitute - return event.buttons || event.which - } - - static isSimulatedEvent(event) { - return Reflect.has(event, 'mouseDownSubstitute') - } - - static isMouseRightClick(event) { - return event.buttons || event.which - } - - static extractTouches(targets) { - let touches = []; - for (let i = 0; i < targets.length; i++) { - let t = targets[i]; - touches.push({ - targetSelector: this.selector(t.target), - identifier: t.identifier, - screenX: t.screenX, - screenY: t.screenY, - clientX: t.clientX, - clientY: t.clientY, - pageX: t.pageX, - pageY: t.pageY - }); - } - return touches - } - - static createTouchList(targets) { - let touches = []; - for (let i = 0; i < targets.length; i++) { - let t = targets[i]; - let touchTarget = document.elementFromPoint(t.pageX, t.pageY); - let touch = new Touch(undefined, touchTarget, t.identifier, - t.pageX, t.pageY, t.screenX, t.screenY); - touches.push(touch); - } - return new TouchList(...touches) - } - - static extractEvent(timestamp, event) { - let targetSelector = this.selector(event.target); - let infos = { - type: event.type, - time: timestamp, - constructor: event.constructor, - data: { - targetSelector: targetSelector, - view: event.view, - mouseDownSubstitute: event.buttons || event.which, // which cannot be cloned directly - bubbles: event.bubbles, - cancelable: event.cancelable, - screenX: event.screenX, - screenY: event.screenY, - clientX: event.clientX, - clientY: event.clientY, - layerX: event.layerX, - layerY: event.layerY, - pageX: event.pageX, - pageY: event.pageY, - ctrlKey: event.ctrlKey, - altKey: event.altKey, - shiftKey: event.shiftKey, - metaKey: event.metaKey - } - }; - if (event.type.startsWith('touch')) { - // On Safari-WebKit the TouchEvent has layerX, layerY coordinates - let data = infos.data; - data.targetTouches = this.extractTouches(event.targetTouches); - data.changedTouches = this.extractTouches(event.changedTouches); - data.touches = this.extractTouches(event.touches); - } - if (event.type.startsWith('pointer')) { - let data = infos.data; - data.pointerId = event.pointerId; - data.pointerType = event.pointerType; - } - if (Events$1.debug) { - Events$1.extracted.push(this.toLine(event)); - } - return infos - } - - static cloneEvent(type, constructor, data) { - if (type.startsWith('touch')) { - // We need to find target from layerX, layerY - //var target = document.querySelector(data.targetSelector) - // elementFromPoint(data.layerX, data.layerY) - //data.target = target - data.targetTouches = this.createTouchList(data.targetTouches); - data.changedTouches = this.createTouchList(data.changedTouches); - data.touches = this.createTouchList(data.touches); - } - // We need to find target from pageX, pageY which are only - // available after construction. They seem to getter items. - - let clone = Reflect.construct(constructor, [type, data]); - clone.mouseDownSubstitute = data.mouseDownSubstitute; - return clone - } - - static simulateEvent(type, constructor, data) { - data.target = document.querySelector(data.targetSelector); - let clone = this.cloneEvent(type, constructor, data); - if (data.target != null) { - data.target.dispatchEvent(clone); - } - if (Events$1.debug) { - Events$1.simulated.push(this.toLine(clone)); - } - } - - static toLine(event) { - return `${event.type} #${event.target.id} ${event.clientX} ${event.clientY}` - let result = event.type; - let selector = this.selector(event.target); - result += ' selector: ' + selector; - if (event.target != document.querySelector(selector)) - console.log('Cannot resolve', selector); - let keys = ['layerX', 'layerY', 'pageX', 'pageY', 'clientX', 'clientY']; - for (let key of keys) { - try { - result += ' ' + key + ':' + event[key]; - } - catch (e) { - console.log('Invalid key: ' + key); - } - } - return result - } - - static compareExtractedWithSimulated() { - if (this.extracted.length != this.simulated.length) { - alert('Unequal length of extracted [' + this.extracted.length + - '] and simulated events [' + this.simulated.length + '].'); - } - else { - for (let i = 0; i < this.extracted.length; i++) { - var extracted = this.extracted[i]; - var simulated = this.simulated[i]; - if (extracted != simulated) { - console.log('Events differ:' + extracted + '|' + simulated); - } - } - } - } - - static selector(context) { - return OptimalSelect.select(context) - } - - static reset() { - this.extracted = []; - this.simulated = []; - } - - static resetSimulated() { - this.simulated = []; - } - - static showExtractedEvents(event) { - if (!event.shiftKey) { - return - } - if (this.popup == null) { - let element = document.createElement('div'); - Elements.setStyle(element, { - position: 'absolute', - width: '480px', - height: '640px', - overflow: 'auto', - backgroundColor: 'lightgray' - }); - document.body.appendChild(element); - this.popup = element; - } - this.popup.innerHTML = ''; - for (let line of this.extracted) { - let div = document.createElement('div'); - div.innerHTML = line; - this.popup.appendChild(div); - } - let div = document.createElement('div'); - div.innerHTML = '------------ Simulated -----------'; - this.popup.appendChild(div); - for (let line of this.simulated) { - let div = document.createElement('div'); - div.innerHTML = line; - this.popup.appendChild(div); - } - Elements.setStyle(this.popup, - { left: event.clientX + 'px', top: event.clientY + 'px' }); - } - } - - Events$1.popup = null; - Events$1.debug = true; - Events$1.extracted = []; - Events$1.simulated = []; - Events$1.simulationRunning = false; - - /** - * Callback for the button action. - * - * @callback actionCallback - * @param {object} event - The event object. - * @param {Button} button - A reference to the button (also this refers to the button). - */ - - /** - * Callback for the button beforeAction. - * - * @callback beforeActionCallback - * @param {object} event - The event object. - * @param {Button} button - A reference to the button (also this refers to the button). - */ - - /** - * Callback for the button afterAction. - * - * @callback afterActionCallback - * @param {object} event - The event object. - * @param {Button} button - A reference to the button (also this refers to the button). - */ - - /** - * Class that represents a PixiJS Button. - * - * @example - * // Create the button - * const button = new Button({ - * label: 'My Button', - * action: () => console.log('Button was clicked') - * }) - * - * // Add the button to a DisplayObject - * app.scene.addChild(button) - * - * @class - * @extends PIXI.Container - * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/button.html|DocTest} - */ - class Button extends PIXI.Container { - - /** - * Creates an instance of a Button. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the button. - * @param {number} [opts.id=auto generated] - The id of the button. - * @param {string} [opts.label] - The label of the button. - * @param {number} [opts.x=0] - The x position of the button. Can be also set after creation with button.x = 0. - * @param {number} [opts.y=0] - The y position of the button. Can be also set after creation with button.y = 0. - * @param {string|Theme} [opts.theme=dark] - The theme to use for this button. Possible values are dark, light, red - * or a Theme object. - * @param {number} [opts.minWidth=44] - The minimum width of the button. - * @param {number} [opts.minHeight=44] - The minimum height of the button. - * @param {number} [opts.padding=Theme.padding] - The inner spacing (distance from icon and/or label) to the border. - * @param {string|PIXI.DisplayObject} [opts.icon] - The icon of the button. Can be a predefined one, an URL or an PIXI.DisplayObject. - * @param {string|PIXI.DisplayObject} [opts.iconActive=icon] - The icon of the button when activated. Can be a predefined one, an URL or an PIXI.DisplayObject. - * @param {string} [opts.iconPosition=left] - The position of the icon in relation to the label. Can be left or right. - * @param {number} [opts.iconColor=Theme.iconColor] - The color of the icon (set by the tint property) as a hex value. - * @param {number} [opts.iconColorActive=Theme.iconColorActive] - The color of the icon when activated. - * @param {number} [opts.fill=Theme.fill] - The color of the button background as a hex value. - * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. - * @param {number} [opts.fillActive=Theme.fillActive] - The color of the button background when activated. - * @param {number} [opts.fillActiveAlpha=Theme.fillActiveAlpha] - The alpha value of the background when activated. - * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. - * @param {number} [opts.strokeWidth=Theme.strokeWidth] - The width of the border in pixel. - * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. - * @param {number} [opts.strokeActive=Theme.strokeActive] - The color of the border when activated. - * @param {number} [opts.strokeActiveWidth=Theme.strokeActiveWidth] - The width of the border in pixel when activated. - * @param {number} [opts.strokeActiveAlpha=Theme.strokeActiveAlpha] - The alpha value of the border when activated. - * @param {object} [opts.textStyle=Theme.textStyle] - A textstyle object for the styling of the label. See PIXI.TextStyle - * for possible options. - * @param {number} [opts.textStyleActive=Theme.textStyleActive] - A textstyle object for the styling of the label when the - * button is activated. See PIXI.TextStyle for possible options. - * @param {string} [opts.style=default] - A shortcut for styling options. Possible values are default, link. - * @param {number} [opts.radius=Theme.radius] - The radius of the four corners of the button (which is a rounded rectangle). - * @param {boolean} [opts.disabled=false] - Is the button disabled? When disabled, the button has a lower alpha value - * and cannot be clicked (interactive is set to false). - * @param {boolean} [opts.active=false] - Is the button initially active? - * @param {actionCallback} [opts.action] - Executed when the button was triggered (by pointerup). - * @param {beforeActionCallback} [opts.beforeAction] - Executed before the main action is triggered. - * @param {afterActionCallback} [opts.afterAction] - Executed after the main action was triggered. - * @param {string} [opts.type=default] - The type of the button. Can be default or checkbox. When the type is - * checkbox, the active state is toggled automatically. - * @param {string} [opts.align=center] - The horizontal position of the label and the icon. Possible values are - * left, center and right. Only affects the style when the minWidth is bigger than the width of the icon and label. - * @param {string} [opts.verticalAlign=middle] - The vertical position of the label and the icon. Possible values are - * top, middle and button. Only affects the style when the minHeight is bigger than the height of the icon and label. - * @param {string|object} [opts.tooltip] - A string for the label of the tooltip or an object to configure the tooltip - * to display. - * @param {string|object} [opts.badge] - A string for the label of the badge or an object to configure the badge to display. - * If the parameter is an object, all badge options can be set plus the following: - * @param {string} [opts.badge.align=right] - The horizontal alignment of the badge. Possible values: left, center, right - * @param {string} [opts.badge.verticalAlign=top] - The vertical alignment of the badge. Possible values: top, middle, bottom - * @param {number} [opts.badge.offsetLeft=0] - The horizontal shift of the badge. - * @param {number} [opts.badge.offsetTop=0] - The vertical shift of the badge. - * @param {boolean} [opts.visible=true] - Is the button initially visible (property visible)? - */ - constructor(opts = {}) { - - super(); - - const theme = Theme.fromString(opts.theme); - this.theme = theme; - - this.opts = Object.assign({}, { - id: PIXI.utils.uid(), - label: null, - x: 0, - y: 0, - minWidth: 44, - minHeight: 44, - padding: theme.padding, - icon: undefined, - iconActive: undefined, - iconPosition: 'left', - iconColor: theme.iconColor, - iconColorActive: theme.iconColorActive, - fill: theme.fill, - fillAlpha: theme.fillAlpha, - fillActive: theme.fillActive, - fillActiveAlpha: theme.fillActiveAlpha, - stroke: theme.stroke, - strokeWidth: theme.strokeWidth, - strokeAlpha: theme.strokeAlpha, - strokeActive: theme.strokeActive, - strokeActiveWidth: theme.strokeActiveWidth, - strokeActiveAlpha: theme.strokeActiveAlpha, - textStyle: theme.textStyle, - textStyleActive: theme.textStyleActive, - style: 'default', - radius: theme.radius, - disabled: false, - active: false, - action: null, - beforeAction: null, - afterAction: null, - type: 'default', - align: 'center', - verticalAlign: 'middle', - tooltip: null, - badge: null, - visible: true - }, opts); - - this.id = this.opts.id; - - if (typeof this.opts.icon === 'undefined' && typeof this.opts.iconActive !== 'undefined') { - this.opts.icon = this.opts.iconActive; - } else if (typeof this.opts.icon !== 'undefined' && typeof this.opts.iconActive === 'undefined') { - this.opts.iconActive = this.opts.icon; - } - - if (this.opts.style === 'link') { - Object.assign(this.opts, {strokeAlpha: 0, strokeActiveAlpha: 0, fillAlpha: 0, fillActiveAlpha: 0}); - } - - this._active = null; - this._disabled = null; - - this.iconInactive = null; - this.iconActive = null; - this.text = null; - - this.button = null; - this.content = null; - - this.tooltip = null; - this.badge = null; - - this.visible = this.opts.visible; - - // setup - //----------------- - this.setup(); - } - - /** - * Captures an event to inform InteractionMapper about processed events. - * - * @param {event|PIXI.InteractionEvent} event - The PIXI event to capture. - */ - capture(event) { - Events$1.capturedBy(event.data.originalEvent, this); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {Button} A reference to the button for chaining. - */ - setup() { - - // Button - //----------------- - let button = new PIXI.Graphics(); - this.button = button; - this.addChild(button); - - // Content - //----------------- - let content = new PIXI.Container(); - this.content = content; - this.addChild(content); - - // Text - //----------------- - if (this.opts.label) { - this.text = new PIXI.Text(this.opts.label, this.opts.textStyle); - } - - // Icon - //----------------- - if (this.opts.icon) { - this.iconInactive = this.loadIcon(this.opts.icon, this.opts.iconColor); - } - - if (this.opts.iconActive) { - this.iconActive = this.loadIcon(this.opts.iconActive, this.opts.iconColorActive); - } - - // interaction - //----------------- - this.button.on('pointerover', e => { - this.capture(e); - TweenLite.to([this.button, this.content], this.theme.fast, {alpha: .83, overwrite: 'none'}); - }); - - this.button.on('pointermove', e => { - this.capture(e); - }); - - this.button.on('pointerout', e => { - this.capture(e); - TweenLite.to([this.button, this.content], this.theme.fast, {alpha: 1, overwrite: 'none'}); - }); - - this.button.on('pointerdown', e => { - //this.capture(e) - TweenLite.to([this.button, this.content], this.theme.fast, {alpha: .7, overwrite: 'none'}); - }); - - this.button.on('pointerup', e => { - this.capture(e); - if (this.opts.beforeAction) { - this.opts.beforeAction.call(this, e, this); - } - - if (this.opts.action) { - this.opts.action.call(this, e, this); - } - - TweenLite.to([this.button, this.content], this.theme.fast, {alpha: .83, overwrite: 'none'}); - - if (this.opts.type === 'checkbox') { - this.active = !this.active; - } - - if (this.opts.afterAction) { - this.opts.afterAction.call(this, e, this); - } - }); - - // disabled - //----------------- - this.disabled = this.opts.disabled; - - // active - //----------------- - this.active = this.opts.active; // calls .layout() - - // tooltip - //----------------- - if (this.opts.tooltip) { - if (typeof this.opts.tooltip === 'string') { - this.tooltip = new Tooltip({object: this, content: this.opts.tooltip}); - } else { - this.opts.tooltip = Object.assign({}, {object: this}, this.opts.tooltip); - this.tooltip = new Tooltip(this.opts.tooltip); - } - } - - // badge - //----------------- - if (this.opts.badge) { - let opts = Object.assign({}, { - align: 'right', - verticalAlign: 'top', - offsetLeft: 0, - offsetTop: 0 - }); - if (typeof this.opts.badge === 'string') { - opts = Object.assign(opts, {content: this.opts.badge}); - } else { - opts = Object.assign(opts, this.opts.badge); - } - - const badge = new Badge(opts); - - switch (opts.align) { - case 'left': - badge.x = this.x - badge.width / 2 + opts.offsetLeft; - break - case 'center': - badge.x = this.x + this.width / 2 - badge.width / 2 + opts.offsetLeft; - break - case 'right': - badge.x = this.x + this.width - badge.width / 2 + opts.offsetLeft; - } - - switch (opts.verticalAlign) { - case 'top': - badge.y = this.y - badge.height / 2 + opts.offsetTop; - break - case 'middle': - badge.y = this.y + this.height / 2 - badge.height / 2 + opts.offsetTop; - break - case 'bottom': - badge.y = this.y + this.height - badge.height / 2 + opts.offsetTop; - } - - this.addChild(badge); - - this.badge = badge; - } - - // set position - //----------------- - this.position.set(this.opts.x, this.opts.y); - - return this - } - - /** - * Should be called to refresh the layout of the button. Can be used after resizing. - * - * @return {Button} A reference to the button for chaining. - */ - layout() { - - // Clear content - //----------------- - this.removeChild(this.content); - this.content = new PIXI.Container(); - this.addChild(this.content); - - // Set the icon - //----------------- - let icon = null; - - if (!this.active && this.iconInactive) { - icon = this.iconInactive; - } else if (this.active && this.iconActive) { - icon = this.iconActive; - } - - // Set the text - //----------------- - if (this.text) { - this.text.position.set(0, 0); - } - - // Width and Height - //----------------- - let width = 0; - if (icon && this.text) { - width = icon.width + this.text.width + 3 * this.opts.padding; - } else if (icon) { - width = icon.width + 2 * this.opts.padding; - } else if (this.text) { - width = this.text.width + 2 * this.opts.padding; - } - - if (width < this.opts.minWidth) { - width = this.opts.minWidth; - } - - let height = 0; - if (icon) { - height = icon.height + 2 * this.opts.padding; - } else if (this.text) { - height = this.text.height + 2 * this.opts.padding; - } - - if (height < this.opts.minHeight) { - height = this.opts.minHeight; - } - - this._width = width; - this._height = height; - - // Position icon and text - //----------------- - if (icon && this.text) { - if (this.opts.iconPosition === 'right') { - icon.x = this.text.width + this.opts.padding; - } else { - this.text.x = icon.width + this.opts.padding; - } - this.content.addChild(icon, this.text); - } else if (icon) { - this.content.addChild(icon); - } else if (this.text) { - this.content.addChild(this.text); - } - - this.layoutInnerContent(); - this.layoutContent(); - - this.icon = icon; - - // draw - //----------------- - this.draw(); - - return this - } - - /** - * Calculates the positions of the content children (icon and/or text). - * - * @private - * @return {Button} A reference to the button for chaining. - */ - layoutInnerContent() { - - for (let child of this.content.children) { - switch (this.opts.verticalAlign) { - case 'top': - child.y = 0; - break - case 'middle': - child.y = this.content.height / 2 - child.height / 2; - break - case 'bottom': - child.y = this.content.height - child.height; - break - } - } - - return this - } - - /** - * Sets the horizontal and vertical position of the content. - * Uses the option keys "align" and "verticalAlign". - * - * @private - * @return {Button} A reference to the button for chaining. - */ - layoutContent() { - - switch (this.opts.align) { - case 'left': - this.content.x = this.opts.padding; - break - case 'center': - this.content.x = ((this._width - this.content.width) / 2); - break - case 'right': - this.content.x = this._width - this.opts.padding - this.content.width; - break - } - - switch (this.opts.verticalAlign) { - case 'top': - this.content.y = this.opts.padding; - break - case 'middle': - this.content.y = (this._height - this.content.height) / 2; - break - case 'bottom': - this.content.y = this._height - this.opts.padding - this.content.height; - break - } - - return this - } - - /** - * Draws the canvas. - * - * @private - * @return {Button} A reference to the button for chaining. - */ - draw() { - - this.button.clear(); - if (this.active) { - this.button.lineStyle(this.opts.strokeActiveWidth, this.opts.strokeActive, this.opts.strokeActiveAlpha); - this.button.beginFill(this.opts.fillActive, this.opts.fillActiveAlpha); - } else { - this.button.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); - this.button.beginFill(this.opts.fill, this.opts.fillAlpha); - } - this.button.drawRoundedRect(0, 0, this._width, this._height, this.opts.radius); - this.button.endFill(); - - return this - } - - /** - * Gets or sets the active state. - * - * @member {boolean} - */ - get active() { - return this._active - } - set active(value) { - - this._active = value; - - if (this._active) { - if (this.text) { - this.text.style = this.opts.textStyleActive; - } - } else { - if (this.text) { - this.text.style = this.opts.textStyle; - } - } - - this.layout(); - } - - /** - * Gets or sets the disabled state. When disabled, the button cannot be clicked. - * - * @member {boolean} - */ - get disabled() { - return this._disabled - } - set disabled(value) { - - this._disabled = value; - - if (this._disabled) { - this.button.interactive = false; - this.button.buttonMode = false; - this.button.alpha = .5; - if (this.icon) { - this.icon.alpha = .5; - } - if (this.text) { - this.text.alpha = .5; - } - } else { - this.button.interactive = true; - this.button.buttonMode = true; - this.button.alpha = 1; - if (this.icon) { - this.icon.alpha = 1; - } - if (this.text) { - this.text.alpha = 1; - } - } - } - - /** - * Shows the button (sets his alpha values to 1). - * - * @return {Button} A reference to the button for chaining. - */ - show() { - - this.opts.strokeAlpha = 1; - this.opts.strokeActiveAlpha = 1; - this.opts.fillAlpha = 1; - this.opts.fillActiveAlpha = 1; - - this.layout(); - - return this - } - - /** - * Hides the button (sets his alpha values to 0). - * - * @return {Button} A reference to the button for chaining. - */ - hide() { - - this.opts.strokeAlpha = 0; - this.opts.strokeActiveAlpha = 0; - this.opts.fillAlpha = 0; - this.opts.fillActiveAlpha = 0; - - this.layout(); - - return this - } - - /** - * Loads an icon - * - * @private - * @param {string|PIXI.DisplayObject} icon - The icon to load. - * @param {number} color - The color of the icon (if not an PIXI.DisplayObject). - * @return {PIXI.DisplayObject} Return the icon as an PIXI.DisplayObject. - */ - loadIcon(icon, color) { - - let displayObject = null; - - if (icon instanceof PIXI.DisplayObject) { - displayObject = icon; - } else { - let size = 17; - if (this.text) { - size = this.text.height; - } else if (this.opts.minHeight) { - size = this.opts.minHeight - (2 * this.opts.padding); - } - - const url = Button.iconIsUrl(icon) ? icon : `../../assets/icons/png/flat/${icon}.png`; - const iconTexture = PIXI.Texture.fromImage(url, true); - - const sprite = new PIXI.Sprite(iconTexture); - sprite.tint = color; - sprite.width = size; - sprite.height = size; - - displayObject = sprite; - } - - return displayObject - } - - /** - * Tests if an icon string is an url. - * - * @private - * @static - * @param {string} url - The url to test. - * @return {boolean} true if the url is an url to an image. - */ - static iconIsUrl(url) { - return /\.(png|svg|gif|jpg|jpeg|tif|tiff)$/i.test(url) - } - - /** - * Gets or sets the color of the current icon (no matter how the status is). Changing the color, changes - * the tint property of the icon sprite. - * - * @member {number} - */ - get iconColor() { - return this.icon ? this.icon.tint : null - } - set iconColor(value) { - if (this.icon) { - this.icon.tint = value; - } - } - } - - /** - * Class that represents a PixiJS ButtonGroup. - * - * @example - * // Create the button group - * const buttonGroup = new ButtonGroup({ - * buttons: [ - * {label: 'Button 1', action: event => console.log(event)}, - * {label: 'Button 2', action: event => console.log(event)}, - * {label: 'Button 3', action: event => console.log(event)} - * ], - * minWidth: 100 - * }) - * - * // Add the button group to a DisplayObject - * app.scene.addChild(buttonGroup) - * - * @class - * @extends PIXI.Graphics - * @see {@link http://pixijs.download/dev/docs/PIXI.Graphics.html|PIXI.Graphics} - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/buttongroup.html|DocTest} - */ - class ButtonGroup extends PIXI.Graphics { - - /** - * Creates an instance of a ButtonGroup. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the button group. - * @param {number} [opts.id=auto generated] - The id of the button group. - * @param {number} [opts.x=0] - The x position of the button group. Can be also set after creation with buttonGroup.x = 0. - * @param {number} [opts.y=0] - The y position of the button group. Can be also set after creation with buttonGroup.y = 0. - * @param {object[]} [opts.buttons=[]] - An array of the buttons of the button group. One item of the array (one object) - * can have exactly the same properties as an Button object when instantiating a Button. If a property of the button group - * conflicts with a property of a button object, the value from the button object will be used. - * @param {string|Theme=} [opts.theme=dark] - The theme to use for this button group. Possible values are dark, light, red - * or a Theme object. - * @param {number} [opts.minWidth=44] - Button: The minimum width of one button. - * @param {number} [opts.minHeight=44] - Button: The minimum height of one button. - * @param {number} [opts.padding=Theme.padding] - Button: The inner spacing (distance from icon and/or label) the the border. - * @param {number} [opts.margin=Theme.margin] - The outer spacing (distance from one button to the previous/next button). - * @param {string} [opts.iconPosition=left] - Button: The position of the icon in relation to the label. Can be left or right. - * @param {number} [opts.iconColor=Theme.iconColor] - Button: The color of the icon (set by the tint property) as a hex value. - * @param {number} [opts.iconColorActive=Theme.iconColorActive] - Button: The color of the icon when activated. - * @param {number} [opts.fill=Theme.fill] - Button: The color of the button background as a hex value. - * @param {number} [opts.fillAlpha=Theme.fillAlpha] - Button: The alpha value of the background. - * @param {number} [opts.fillActive=Theme.fillActive] - Button: The color of the button background when activated. - * @param {number} [opts.fillActiveAlpha=Theme.fillActiveAlpha] - Button: The alpha value of the background when activated. - * @param {number} [opts.stroke=Theme.stroke] - Button: The color of the border as a hex value. - * @param {number} [opts.strokeWidth=Theme.strokeWidth] - Button: The width of the border in pixel. - * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - Button: The alpha value of the border. - * @param {number} [opts.strokeActive=Theme.strokeActive] - Button: The color of the border when activated. - * @param {number} [opts.strokeActiveWidth=Theme.strokeActiveWidth] - Button: The width of the border in pixel when activated. - * @param {number} [opts.strokeActiveAlpha=Theme.strokeActiveAlpha] - Button: The alpha value of the border when activated. - * @param {object} [opts.textStyle=Theme.textStyle] - Button: A textstyle object for the styling of the label. See PIXI.TextStyle - * for possible options. - * @param {number} [opts.textStyleActive=Theme.textStyleActive] - Button: A textstyle object for the styling of the label when the - * button is activated. See PIXI.TextStyle for possible options. - * @param {string} [opts.style=default] - A shortcut for styling options. Possible values are default, link. - * @param {number} [opts.radius=Theme.radius] - Button: The radius of the four corners of the button (which is a rounded rectangle). - * @param {boolean} [opts.disabled=false] - Is the button group disabled? When disabled, the button group has a lower alpha value - * and cannot be clicked (interactive of every button is set to false). - * @param {string} [opts.type=default] - The type of the button group. Can be default, checkbox or radio. When the type is - * checkbox, the active state is toggled for each button automatically. When the type is radio, only one button can - * be activated at the same time. - * @param {string} [opts.orientation=horizontal] - The orientation of the button group. Can be horizontal or vertical. - * @param {string} [opts.align=center] - Button: The horizontal position of the label and the icon. Possible values are - * left, center and right. Only affects the style when the minWidth is bigger than the width of the icon and label. - * @param {string} [opts.verticalAlign=middle] - Button: The vertical position of the label and the icon. Possible values are - * top, middle and bottom. Only affects the style when the minHeight is bigger than the height of the icon and label. - * @param {boolean} [opts.visible=true] - Is the button group initially visible (property visible)? - */ - constructor(opts = {}) { - - super(); - - const theme = Theme.fromString(opts.theme); - this.theme = theme; - - this.opts = Object.assign({}, { - id: PIXI.utils.uid(), - x: 0, - y: 0, - buttons: [], - minWidth: 44, - minHeight: 44, - padding: theme.padding, - margin: theme.margin, - iconPosition: 'left', // left, right - iconColor: theme.iconColor, - iconColorActive: theme.iconColorActive, - fill: theme.fill, - fillAlpha: theme.fillAlpha, - fillActive: theme.fillActive, - fillActiveAlpha: theme.fillActiveAlpha, - stroke: theme.stroke, - strokeWidth: theme.strokeWidth, - strokeAlpha: theme.strokeAlpha, - strokeActive: theme.strokeActive, - strokeActiveWidth: theme.strokeActiveWidth, - strokeActiveAlpha: theme.strokeActiveAlpha, - textStyle: theme.textStyle, - textStyleActive: theme.textStyleActive, - style: 'default', - radius: theme.radius, - disabled: null, - type: 'default', // default, checkbox, radio - orientation: 'horizontal', - align: 'center', // left, center, right - verticalAlign: 'middle', // top, middle, bottom - visible: true - }, opts); - - this.buttons = []; - - this._disabled = null; - - this.visible = this.opts.visible; - - // setup - //----------------- - this.setup(); - - // layout - //----------------- - this.layout(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {ButtonGroup} A reference to the button group for chaining. - */ - setup() { - - // Buttons - //----------------- - let position = 0; - - for (let it of this.opts.buttons) { - - delete it.x; - delete it.y; - - if (this.opts.orientation === 'horizontal') { - it.x = position; - } else { - it.y = position; - } - - it.theme = it.theme || this.opts.theme; - it.minWidth = it.minWidth || this.opts.minWidth; - it.minHeight = it.minHeight || this.opts.minHeight; - it.padding = it.padding || this.opts.padding; - it.iconPosition = it.iconPosition || this.opts.iconPosition; - it.iconColor = it.iconColor || this.opts.iconColor; - it.iconColorActive = it.iconColorActive || this.opts.iconColorActive; - it.fill = it.fill || this.opts.fill; - it.fillAlpha = it.fillAlpha || this.opts.fillAlpha; - it.fillActive = it.fillActive || this.opts.fillActive; - it.fillActiveAlpha = it.fillActiveAlpha || this.opts.fillActiveAlpha; - it.stroke = it.stroke || this.opts.stroke; - it.strokeWidth = it.strokeWidth != null ? it.strokeWidth : this.opts.strokeWidth; - it.strokeAlpha = it.strokeAlpha != null ? it.strokeAlpha : this.opts.strokeAlpha; - it.strokeActive = it.strokeActive || this.opts.strokeActive; - it.strokeActiveWidth = it.strokeActiveWidth != null ? it.strokeActiveWidth : this.opts.strokeActiveWidth; - it.strokeActiveAlpha = it.strokeActiveAlpha != null ? it.strokeActiveAlpha : this.opts.strokeActiveAlpha; - it.textStyle = it.textStyle || this.opts.textStyle; - it.textStyleActive = it.textStyleActive || this.opts.textStyleActive; - it.style = it.style || this.opts.style; - it.radius = it.radius != null ? it.radius : this.opts.radius; - if (!it.type) { - switch (this.opts.type) { - case 'checkbox': - it.type = this.opts.type; - break - default: - it.type = 'default'; - break - } - } - //it.type = it.type || this.opts.type || 'default' - it.align = it.align || this.opts.align; - it.verticalAlign = it.verticalAlign || this.opts.verticalAlign; - it.afterAction = (event, button) => { - if (this.opts.type === 'radio' && button.opts.type === 'default') { - this.buttons.forEach(it => { - if (it.opts.type === 'default') { - it.active = false; - } - }); - - if (button.opts.type === 'default') { - button.active = true; - } - } - }; - - if (it.tooltip) { - if (typeof it.tooltip === 'string') { - it.tooltip = {content: it.tooltip, container: this}; - } else { - it.tooltip = Object.assign({}, {container: this}, it.tooltip); - } - } - - let button = new Button(it); - - this.addChild(button); - this.buttons.push(button); - - position += (this.opts.orientation === 'horizontal' ? button.width : button.height) + this.opts.margin; - } - - if (this.opts.orientation === 'vertical') { - const maxWidth = this.getMaxButtonWidth(); - - this.buttons.forEach(it => { - it.opts.minWidth = maxWidth; - it.layout(); - }); - } - - // disabled - //----------------- - if (this.opts.disabled != null) { - this.disabled = this.opts.disabled; - } - - return this - } - - /** - * Should be called to refresh the layout of the button group. Can be used after resizing. - * - * @return {ButtonGroup} A reference to the button group for chaining. - */ - layout() { - - // set position - //----------------- - this.position.set(this.opts.x, this.opts.y); - - // draw - //----------------- - this.draw(); - - return this - } - - /** - * Draws the canvas. - * - * @private - * @return {ButtonGroup} A reference to the button group for chaining. - */ - draw() { - - if (this.opts.margin === 0) { - - this.buttons.forEach(it => it.hide()); - - this.clear(); - this.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); - this.beginFill(this.opts.fill, this.opts.fillAlpha); - this.drawRoundedRect(0, 0, this.width, this.height, this.opts.radius); - - // Draw borders - this.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha / 2); - - this.buttons.forEach((it, i) => { - if (i > 0) { - this.moveTo(it.x, it.y); - - if (this.opts.orientation === 'horizontal') { - this.lineTo(it.x, it.height); - } else { - this.lineTo(it.width, it.y); - } - - } - }); - - this.endFill(); - } - - return this - } - - /** - * Gets or sets the disabled state. When disabled, no button of the button group can be clicked. - * - * @member {boolean} - */ - get disabled() { - return this._disabled - } - - set disabled(value) { - - this._disabled = value; - - this.buttons.forEach(it => it.disabled = value); - } - - /** - * Searches all buttons of the button group and returns the maximum width of one button. - * - * @private - * @return {number} The maximum with of a button of the button group. - */ - getMaxButtonWidth() { - - let widths = this.buttons.map(it => it.width); - - return Math.max(...widths) - } - - /** - * Shows the button group (sets his alpha value to 1). - * - * @return {ButtonGroup} A reference to the button group for chaining. - */ - show() { - - this.alpha = 1; - - return this - } - - /** - * Hides the button group (sets his alpha value to 0). - * - * @return {ButtonGroup} A reference to the button group for chaining. - */ - hide() { - - this.alpha = 0; - - return this - } - } - - /** - * Class that represents a PixiJS InteractivePopup. - * The class is used for various other Popup-like classes - * like Popup, Message... - * - * @class - * @abstract - * @extends AbstractPopup - */ - class InteractivePopup extends AbstractPopup { - - /** - * Creates an instance of an InteractivePopup (only for internal use). - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the popup. - * @param {boolean} [opts.closeOnPopup=false] - Should the popup be closed when the user clicks on the popup? - * @param {boolean} [opts.closeButton=true] - Should a close button be displayed on the upper right corner? - * @param {object} [opts.button] - A Button object to be display on the lower right corner. - * @param {object} [opts.buttonGroup] - A ButtonGroup object to be displayed on the lower right corner. - */ - constructor(opts = {}) { - - opts = Object.assign({}, { - closeOnPopup: false, - closeButton: true, - button: null, - buttonGroup: null - }, opts); - - super(opts); - - this._closeButton = null; - this._buttons = null; - - // padding - this.smallPadding = this.opts.padding / 2; - - // setup - //----------------- - this.setup(); - - // layout - //----------------- - this.layout(); - } - - /** - * Creates the framework and instantiates everything. - * - * @private - * @return {AbstractPopup} A reference to the popup for chaining. - */ - setup() { - - super.setup(); - - // interaction - //----------------- - this.on('pointerup', e => { - if (this.opts.closeOnPopup) { - this.hide(); - } else { - e.stopPropagation(); - } - }); - - // closeButton - //----------------- - if (this.opts.closeButton) { - let closeButton = PIXI.Sprite.fromImage('../../assets/icons/png/flat/close.png', true); - closeButton.width = this.headerStyle.fontSize; - closeButton.height = closeButton.width; - closeButton.tint = this.theme.color2; - // This is needed, because the closeButton belongs to the content. The popup must resize with the closeButton. - if (this._header) { - closeButton.x = this._header.width + this.innerPadding; - } else if (this._content) { - closeButton.x = this._content.width + this.innerPadding; - } - - closeButton.interactive = true; - closeButton.buttonMode = true; - closeButton.on('pointerdown', e => { - this.hide(); - }); - - this._closeButton = closeButton; - this.addChild(closeButton); - - // maxWidth is set and a closeButton should be displayed - //----------------- - if (this.opts.maxWidth) { - const wordWrapWidth = this.opts.maxWidth - (2 * this.opts.padding) - this.smallPadding - this._closeButton.width; - if (this._header) { - this.headerStyle.wordWrapWidth = wordWrapWidth; - } else if (this._content) { - this.textStyle.wordWrapWidth = wordWrapWidth; - } - } - } - - // buttons - //----------------- - if (this.opts.button || this.opts.buttonGroup) { - if (this.opts.button) { - this._buttons = new Button(Object.assign({textStyle: this.theme.textStyleSmall}, this.opts.button)); - } else { - this._buttons = new ButtonGroup(Object.assign({textStyle: this.theme.textStyleSmall}, this.opts.buttonGroup)); - } - this.addChild(this._buttons); - - this._buttons.y = this.innerPadding + this.sy; - } - - return this - } - - /** - * Should be called to refresh the layout of the popup. Can be used after resizing. - * - * @return {AbstractPopup} A reference to the popup for chaining. - */ - layout() { - - super.layout(); - - // closeButton - //----------------- - if (this.opts.closeButton) { - this._closeButton.x = this.wantedWidth - this.smallPadding - this._closeButton.width; - this._closeButton.y = this.smallPadding; - } - - // buttons - //----------------- - if (this._buttons) { - this._buttons.x = this.wantedWidth - this.opts.padding - this._buttons.width; - this._buttons.y = this.wantedHeight - this.opts.padding - this._buttons.height; - } - - return this - } - - /** - * Calculates the size of the children of the AbstractPopup. - * Cannot use getBounds() because it is not updated when children - * are removed. - * - * @private - * @override - * @returns {object} An JavaScript object width the keys width and height. - */ - getInnerSize() { - - let size = super.getInnerSize(); - - if (this._closeButton) { - size.width += this.smallPadding + this._closeButton.width; - } - - if (this._buttons) { - size.width = Math.max(size.width, this._buttons.x + this._buttons.width); - size.height += this.innerPadding + this._buttons.height; - } - - return size - } - } - - /** - * Class that represents a PixiJS Popup. - * - * @example - * // Create the popup - * const popup = new Popup({ - * header: 'Goethe', - * content: 'Man kann die Erfahrung nicht früh genug machen, wie entbehrlich man in der Welt ist.' - * }) - * - * // Add the popup to a DisplayObject - * app.scene.addChild(popup) - * - * @class - * @extends InteractivePopup - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/popup.html|DocTest} - */ - class Popup extends InteractivePopup { - - /** - * Creates an instance of a Popup. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the popup. - * @param {boolean} [opts.closeButton=false] - Should a close button be displayed on the upper right corner? - * @param {number} [opts.minWidth=0] - The minimum width of the popup. - * @param {number} [opts.minHeight=0] - The minimum height of the popup. - */ - constructor(opts = {}) { - - opts = Object.assign({}, { - closeButton: false, - minWidth: 0, - minHeight: 0 - }, opts); - - super(opts); - } - } - - /** - * Class that represents a PixiJS Modal. - * - * @example - * // Create the button and the modal when clicked - * const button = new Button({ - * label: 'Show Modal', - * action: e => { - * const modal = new Modal({ - * app: app, - * header: 'This is the header', - * content: 'This is the text.' - * }) - * app.scene.addChild(modal) - * } - * }) - * - * // Add the button to a DisplayObject - * app.scene.addChild(button) - * - * @class - * @extends PIXI.Container - * @extends InteractivePopup - * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/modal.html|DocTest} - */ - class Modal extends PIXI.Container { - - /** - * Creates an instance of a Modal. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the modal. - * @param {number} [opts.id=auto generated] - The id of the modal. - * @param {PIXIApp} [opts.app=window.app] - The app where the modal belongs to. - * @param {number} [opts.backgroundFill=Theme.background] - The color of the background. - * @param {number} [opts.backgroundFillAlpha=0.6] - The opacity of the background. - * @param {boolean} [opts.closeOnBackground=true] - Should the modal be closed when the user clicks the - * background? - * @param {boolean} [opts.visible=true] - Is the modal initially visible (property visible)? - */ - constructor(opts = {}) { - - super(); - - const theme = Theme.fromString(opts.theme); - this.theme = theme; - - this.opts = Object.assign({}, { - id: PIXI.utils.uid(), - app: window.app, - backgroundFill: theme.background, - backgroundFillAlpha: .6, - closeOnBackground: true, - visible: true - }, opts); - - this.id = this.opts.id; - - this.background = null; - this.popup = null; - - this.alpha = 0; - this.visible = this.opts.visible; - - // setup - //----------------- - this.setup(); - - // layout - //----------------- - this.layout(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {Modal} A reference to the modal for chaining. - */ - setup() { - - // interaction - //----------------- - this.interactive = true; - this.on('added', e => { - if (this.visible) { - this.show(); - } - }); - - // background - //----------------- - let background = new PIXI.Graphics(); - this.background = background; - this.addChild(this.background); - - if (this.opts.closeOnBackground) { - background.interactive = true; - background.on('pointerup', e => { - this.hide(); - }); - } - - // popup - //----------------- - const popupOpts = Object.assign({}, this.opts, { - visible: true, - onHidden: () => { - this.hide(); - } - }); - let popup = new InteractivePopup(popupOpts); - this.popup = popup; - this.addChild(popup); - popup.show(); - - return this - } - - /** - * Should be called to refresh the layout of the modal. Can be used after resizing. - * - * @return {Modal} A reference to the modal for chaining. - */ - layout() { - - const width = this.opts.app.size.width; - const height = this.opts.app.size.height; - - // background - //----------------- - this.background.clear(); - this.background.beginFill(this.opts.backgroundFill, this.opts.backgroundFillAlpha); - this.background.drawRect(0, 0, width, height); - this.background.endFill(); - - // position - this.popup.x = width / 2 - this.popup.width / 2; - this.popup.y = height / 2 - this.popup.height / 2; - - return this - } - - /** - * Shows the modal (sets his alpha values to 1). - * - * @return {Modal} A reference to the modal for chaining. - */ - show() { - TweenLite.to(this, this.theme.fast, {alpha: 1, onStart: () => this.visible = true}); - - return this - } - - /** - * Hides the modal (sets his alpha values to 0). - * - * @return {Modal} A reference to the modal for chaining. - */ - hide() { - TweenLite.to(this, this.theme.fast, {alpha: 0, onComplete: () => this.visible = false}); - - return this - } - - /** - * Sets or gets the header. The getter always returns a PIXI.Text object. The setter can receive - * a string or a PIXI.Text object. - * - * @member {string|PIXI.Text} - */ - get header() { - return this.popup.header - } - set header(value) { - this.opts.header = value; - this.background.destroy(); - this.popup.destroy(); - this.setup().layout(); - } - - /** - * Sets or gets the content. The getter always returns an PIXI.DisplayObject. The setter can receive - * a string or a PIXI.DisplayObject. - * - * @member {string|PIXI.DisplayObject} - */ - get content() { - return this.popup.content - } - set content(value) { - this.opts.content = value; - this.background.destroy(); - this.popup.destroy(); - this.setup().layout(); - } - } - - /** - * Class that represents a Message. A message pops up and disappears after a specific amount of time. - * - * @example - * // Create the PixiJS App - * const app = new PIXIApp({ - * view: canvas, - * width: 900, - * height: 250 - * }).setup().run() - * - * // Create a button - * let button = new Button({ - * label: 'Click me', - * action: e => { - * const message = new Message({ - * app: app, - * header: 'Header', - * content: 'Text.' - * }) - * app.scene.addChild(message) - * } - * }) - * - * // Add the button to the scene - * app.scene.addChild(button) - * - * @class - * @extends InteractivePopup - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/message.html|DocTest} - */ - class Message extends InteractivePopup { - - /** - * Creates an instance of a Message. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the message. - * @param {PIXIApp} [opts.app=window.app] - The PIXIApp where this message belongs to. - * @param {boolean} [opts.closeButton=false] - Should a close button be displayed in the upper right corner? - * @param {number} [opts.minWidth=280] - The minimum width of the message box. Automatically expands with the content. - * @param {number} [opts.minHeight=100] - The minimum height of the message box. Automatically expands with the content. - * @param {number} [opts.margin=Theme.margin] - The outer spacing of the message box. - * @param {string} [opts.align=right] - The horizontal position of the message box relative to the app. Possible - * values are left, center, right. - * @param {string} [opts.verticalAlign=top] - The vertical position of the message box relative to the app. Possible - * values are top, middle, bottom. - * @param {number} [opts.duration=5] - The duration in seconds when the message box should disappear. - * @param {boolean} [opts.autoClose=true] - Should the message box be closed automatically? - * @param {number} [opts.closeDuration=Theme.fast] - The duration in seconds of the closing of the message box. - */ - constructor(opts = {}) { - - const theme = Theme.fromString(opts.theme); - - opts = Object.assign({}, { - app: window.app, - closeButton: false, - minWidth: 280, - minHeight: 100, - margin: theme.margin, - align: 'right', // left, center, right - verticalAlign: 'top', // top, middle, bottom - duration: 5, - autoClose: true, - closeDuration: theme.fast - }, opts); - - super(opts); - } - - /** - * Relayouts the position of the message box. - * - * @return {Message} Returns the message box for chaining. - */ - layout() { - - super.layout(); - - // horizontal - switch (this.opts.align) { - case 'left': - this.x = this.opts.margin; - break - case 'center': - this.x = (this.opts.app.size.width / 2) - (this.width / 2); - break - case 'right': - this.x = this.opts.app.size.width - this.opts.margin - this.width; - break - } - - // vertical - switch (this.opts.verticalAlign) { - case 'top': - this.y = this.opts.margin; - break - case 'middle': - this.y = (this.opts.app.size.height / 2) - (this.height / 2); - break - case 'bottom': - this.y = this.opts.app.size.height - this.opts.margin - this.height; - break - } - } - - /** - * Shows the message box. - * - * @private - */ - show() { - - super.show(); - - if (this.opts.autoClose) { - window.setTimeout(() => { - this.hide(); - }, this.opts.duration * 1000); - } - } - } - - /* globals WebKitPoint */ - - /** Tests whether an object is empty - * @param {Object} obj - the object to be tested - * @return {boolean} - */ - function isEmpty(obj) { - // > isEmpty({}) - // true - for (let i in obj) { - return false - } - return true - } - - function lerp(start, stop, amt) { - return amt * (stop - start) + start - } - - - // Returns a function, that, as long as it continues to be invoked, will not - // be triggered. The function will be called after it stops being called for - // N milliseconds. If `immediate` is passed, trigger the function on the - // leading edge, instead of the trailing. - // Taken from: https://davidwalsh.name/essential-javascript-functions - function debounce(func, wait, immediate) { - let timeout; - return function () { - let context = this, - args = arguments; - let later = function () { - timeout = null; - if (!immediate) func.apply(context, args); - }; - let callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - } - } - - /** Returns an id that is guaranteed to be unique within the livetime of the - * application - * @return {string} - */ - let _idGenerator = 0; - function getId() { - return 'id' + _idGenerator++ - } - - class Dates { - - static create(fullYear, month, day) { - return new Date(Date.UTC(fullYear, month, day)) - } - - static daysInMonth(date) { - return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate() - } - - static startYearRange(date) { - return new Date(Date.UTC(date.getFullYear() - 1, 11, 31, 23, 59, 59, 999)) - } - - static endYearRange(date) { - return new Date(Date.UTC(date.getFullYear() + 1, 0, 1)) - } - - static prevYear(date, offset = 1) { - return this.create(date.getFullYear() - offset, 0, 1) - } - - static nextYear(date, offset = 1) { - return this.create(date.getFullYear() + offset, 0, 1) - } - - static nextMonth(date) { - return this.create(date.getFullYear(), date.getMonth() + 1, 1) - } - - static nextDay(date) { - return this.create( - date.getFullYear(), - date.getMonth(), - date.getDate() + 1 - ) - } - - static nextHour(date) { - // See http://stackoverflow.com/questions/1050720/adding-hours-to-javascript-date-object - return new Date(date.getTime() + 60 * 60 * 1000) - } - - static nextMinute(date) { - // See above - return new Date(date.getTime() + 60 * 1000) - } - - static nextSecond(date) { - // See above - return new Date(date.getTime() + 1000) - } - - static nextMillisecond(date) { - // See above - return new Date(date.getTime() + 1) - } - - static *iterYears(start, end) { - let date = this.create(start.getFullYear(), 0, 1); - while (date <= end) { - yield date; - date = this.nextYear(date); - } - yield date; - } - - static *iterMonths(year, limit = 12) { - let month = 0; - while (month < limit) { - let date = this.create(year.getFullYear(), month, 1); - yield date; - month += 1; - } - } - - static *iterMonthsOfYears(years) { - for (let year of years) { - for (let month of this.iterMonths(year)) { - yield month; - } - } - } - - static *iterDays(month) { - let day = 1; - let limit = Dates.daysInMonth(month); - while (day <= limit) { - let date = this.create(month.getFullYear(), month.getMonth(), day); - yield date; - day += 1; - } - } - - static *iterDaysOfMonths(months) { - for (let month of months) { - for (let day of this.iterDays(month)) { - yield day; - } - } - } - } - /* Color conversion functions */ - - class Colors { - // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb - - static rgb2num(red, green, blue) { - let rgb = blue | (green << 8) | (red << 16); - return 0x000000 + rgb - } - - static rgb2hex(red, green, blue) { - let rgb = blue | (green << 8) | (red << 16); - return '#' + (0x1000000 + rgb).toString(16).slice(1) - } - - static hex2rgb(hex) { - // long version - let r = hex.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i); - if (r) { - return r.slice(1, 4).map(x => { - return parseInt(x, 16) - }) - } - // short version - r = hex.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i); - if (r) { - return r.slice(1, 4).map(x => { - return 0x11 * parseInt(x, 16) - }) - } - return null - } - - static rgb(r, g, b) { - return { r, g, b } - } - - static string2hex(str) { - return parseInt('0x' + str.slice(1)) - } - - static lerp(rgb1, rgb2, amount) { - return { - r: Math.round(lerp(rgb1.r, rgb2.r, amount)), - g: Math.round(lerp(rgb1.g, rgb2.g, amount)), - b: Math.round(lerp(rgb1.b, rgb2.b, amount)) - } - } - - static get violet() { - return Colors.rgb2num(89, 34, 131) - } - - static get steelblue() { - return Colors.rgb2num(0, 130, 164) - } - - static get ochre() { - return Colors.rgb2num(181, 157, 0) - } - - static get turquoise() { - return Colors.rgb2num(34, 164, 131) - } - - static get eminence() { - return Colors.rgb2num(150, 60, 134) - } - - static random() { - let r = Math.round(Math.random() * 255); - let g = Math.round(Math.random() * 255); - let b = Math.round(Math.random() * 255); - return Colors.rgb2num(r, g, b) - } - } - - class Cycle extends Array { - constructor(...items) { - super(); - for (let item of items) { - this.push(item); - } - this.index = 0; - } - - next() { - if (this.index == this.length) { - this.index = 0; - } - return this[this.index++] - } - - current() { - if (this.index === this.length) { - this.index = 0; - } - return this[this.index] - } - } - - /** Static methods to compute 2D points with x and y coordinates. - */ - class Points { - static length(a) { - return Math.sqrt(a.x * a.x + a.y * a.y) - } - - static normalize(p) { - let len = this.length(p); - return this.multiplyScalar(p, 1 / len) - } - - static mean(a, b) { - return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 } - } - - static subtract(a, b) { - return { x: a.x - b.x, y: a.y - b.y } - } - - static multiply(a, b) { - return { x: a.x * b.x, y: a.y * b.y } - } - - static divide(a, b) { - return { x: a.x / b.x, y: a.y / b.y } - } - - static multiplyScalar(a, b) { - return { x: a.x * b, y: a.y * b } - } - - static add(a, b) { - return { x: a.x + b.x, y: a.y + b.y } - } - - static negate(p) { - return { x: -p.x, y: -p.y } - } - - static angle(p1, p2) { - return Math.atan2(p1.y - p2.y, p1.x - p2.x) - } - - static normalizedAngle(p1, p2) { - return Angle.normalize(this.angle(p1, p2)) - } - - static normalized2Angle(p1, p2) { - return Angle.normalize2(this.angle(p1, p2)) - } - - static arc(p, alpha, radius) { - return { - x: p.x + radius * Math.cos(alpha), - y: p.y + radius * Math.sin(alpha) - } - } - - static distance(a, b) { - let dx = a.x - b.x; - let dy = a.y - b.y; - return Math.sqrt(dx * dx + dy * dy) - } - - static fromPageToNode(element, p) { - // if (window.webkitConvertPointFromPageToNode) { - // return window.webkitConvertPointFromPageToNode(element, - // new WebKitPoint(p.x, p.y)) - // } - return window.convertPointFromPageToNode(element, p.x, p.y) - } - - static fromNodeToPage(element, p) { - // if (window.webkitConvertPointFromNodeToPage) { - // return window.webkitConvertPointFromNodeToPage(element, - // new WebKitPoint(p.x, p.y)) - // } - return window.convertPointFromNodeToPage(element, p.x, p.y) - } - } - - /** Static methods to compute angles. - */ - class Angle { - - static normalize(angle) { - let TAU = Math.PI * 2.0; - while (angle > Math.PI) { - angle -= TAU; - } - while (angle < -Math.PI) { - angle += TAU; - } - return angle - } - - static normalize2(angle) { - let TAU = Math.PI * 2.0; - while (angle > TAU) { - angle -= TAU; - } - while (angle < 0) { - angle += TAU; - } - return angle - } - - static normalizeDegree(angle) { - let full = 360.0; - while (angle > 180.0) { - angle -= full; - } - while (angle < -180.0) { - angle += full; - } - return angle - } - - static normalizedDiff(a, b) { - return this.normalize(this.diff(a, b)) - } - - static normalized2Diff(a, b) { - return this.normalize2(this.diff(a, b)) - } - - static diff(a, b) { - return Math.atan2(Math.sin(a - b), Math.cos(a - b)) - } - - static degree2radian(degree) { - return Math.PI * degree / 180.0 - } - - static radian2degree(rad) { - return 180.0 / Math.PI * rad - } - } - - class Elements$1 { - static setStyle(element, styles) { - for (let key in styles) { - element.style[key] = styles[key]; - } - } - - static addClass(element, cssClass) { - element.classList.add(cssClass); - } - - static removeClass(element, cssClass) { - element.classList.remove(cssClass); - } - - static toggleClass(element, cssClass) { - element.classList.toggle(cssClass); - } - - static hasClass(element, cssClass) { - return element.classList.contains(cssClass) - } - } - - class MapProxy { - /* This class is needed if we want to use the interaction classes - in Firefox 45.8 and modern Browsers. - - A workaround for https://github.com/babel/babel/issues/2334 - */ - constructor() { - this.map = new Map(); - } - - get size() { - return this.map.size - } - - get(key) { - return this.map.get(key) - } - - set(key, value) { - return this.map.set(key, value) - } - - delete(key) { - return this.map.delete(key) - } - - clear() { - return this.map.clear() - } - - has(key) { - return this.map.has(key) - } - - keys() { - return this.map.keys() - } - - values() { - return this.map.values() - } - - entries() { - return this.map.entries() - } - - forEach(func) { - this.map.forEach(func); - } - } - - /* Based om https://gist.github.com/cwleonard/e124d63238bda7a3cbfa */ - class Polygon { - /* - * This is the Polygon constructor. All points are center-relative. - */ - constructor(center) { - this.points = new Array(); - this.center = center; - } - - /* - * Point x and y values should be relative to the center. - */ - addPoint(p) { - this.points.push(p); - } - - /* - * Point x and y values should be absolute coordinates. - */ - addAbsolutePoint(p) { - this.points.push({ x: p.x - this.center.x, y: p.y - this.center.y }); - } - - /* - * Returns the number of sides. Equal to the number of vertices. - */ - getNumberOfSides() { - return this.points.length - } - - /* - * rotate the polygon by a number of radians - */ - rotate(rads) { - for (let i = 0; i < this.points.length; i++) { - let x = this.points[i].x; - let y = this.points[i].y; - this.points[i].x = Math.cos(rads) * x - Math.sin(rads) * y; - this.points[i].y = Math.sin(rads) * x + Math.cos(rads) * y; - } - } - - /* - * The draw function takes as a parameter a Context object from - * a Canvas element and draws the polygon on it. - */ - draw(context, { lineWidth = 2, stroke = '#000000', fill = null } = {}) { - context.beginPath(); - context.moveTo( - this.points[0].x + this.center.x, - this.points[0].y + this.center.y - ); - for (let i = 1; i < this.points.length; i++) { - context.lineTo( - this.points[i].x + this.center.x, - this.points[i].y + this.center.y - ); - } - context.closePath(); - context.lineWidth = lineWidth; - if (stroke) { - context.strokeStyle = stroke; - context.stroke(); - } - if (fill) { - context.fillStyle = fill; - context.fill(); - } - } - - absolutePoints() { - let result = new Array(); - for (let p of this.points) { - result.push(Points.add(p, this.center)); - } - return result - } - - flatAbsolutePoints() { - let result = new Array(); - for (let p of this.points) { - let a = Points.add(p, this.center); - result.push(a.x); - result.push(a.y); - } - return result - } - - /* - * This function returns true if the given point is inside the polygon, - * and false otherwise. - */ - containsPoint(pnt) { - let nvert = this.points.length; - let testx = pnt.x; - let testy = pnt.y; - - let vertx = new Array(); - for (let q = 0; q < this.points.length; q++) { - vertx.push(this.points[q].x + this.center.x); - } - - let verty = new Array(); - for (let w = 0; w < this.points.length; w++) { - verty.push(this.points[w].y + this.center.y); - } - - let i, - j = 0; - let c = false; - for (i = 0, j = nvert - 1; i < nvert; j = i++) { - if ( - verty[i] > testy != verty[j] > testy && - testx < - (vertx[j] - vertx[i]) * - (testy - verty[i]) / - (verty[j] - verty[i]) + - vertx[i] - ) - c = !c; - } - return c - } - - multiplyScalar(scale) { - let center = Points.multiplyScalar(this.center, scale); - let clone = new Polygon(center); - for (let p of this.points) { - clone.addPoint(Points.multiplyScalar(p, scale)); - } - return clone - } - - /* - * To detect intersection with another Polygon object, this - * function uses the Separating Axis Theorem. It returns false - * if there is no intersection, or an object if there is. The object - * contains 2 fields, overlap and axis. Moving the polygon by overlap - * on axis will get the polygons out of intersection. - */ - intersectsWith(other) { - let axis = { x: 0, y: 0 }; - let tmp, minA, maxA, minB, maxB; - let side, i; - let smallest = null; - let overlap = 99999999; - - /* test polygon A's sides */ - for (side = 0; side < this.getNumberOfSides(); side++) { - /* get the axis that we will project onto */ - if (side == 0) { - axis.x = - this.points[this.getNumberOfSides() - 1].y - - this.points[0].y; - axis.y = - this.points[0].x - - this.points[this.getNumberOfSides() - 1].x; - } else { - axis.x = this.points[side - 1].y - this.points[side].y; - axis.y = this.points[side].x - this.points[side - 1].x; - } - - /* normalize the axis */ - tmp = Math.sqrt(axis.x * axis.x + axis.y * axis.y); - axis.x /= tmp; - axis.y /= tmp; - - /* project polygon A onto axis to determine the min/max */ - minA = maxA = this.points[0].x * axis.x + this.points[0].y * axis.y; - for (i = 1; i < this.getNumberOfSides(); i++) { - tmp = this.points[i].x * axis.x + this.points[i].y * axis.y; - if (tmp > maxA) maxA = tmp; - else if (tmp < minA) minA = tmp; - } - /* correct for offset */ - tmp = this.center.x * axis.x + this.center.y * axis.y; - minA += tmp; - maxA += tmp; - - /* project polygon B onto axis to determine the min/max */ - minB = maxB = - other.points[0].x * axis.x + other.points[0].y * axis.y; - for (i = 1; i < other.getNumberOfSides(); i++) { - tmp = other.points[i].x * axis.x + other.points[i].y * axis.y; - if (tmp > maxB) maxB = tmp; - else if (tmp < minB) minB = tmp; - } - /* correct for offset */ - tmp = other.center.x * axis.x + other.center.y * axis.y; - minB += tmp; - maxB += tmp; - - /* test if lines intersect, if not, return false */ - if (maxA < minB || minA > maxB) { - return false - } else { - let o = maxA > maxB ? maxB - minA : maxA - minB; - if (o < overlap) { - overlap = o; - smallest = { x: axis.x, y: axis.y }; - } - } - } - - /* test polygon B's sides */ - for (side = 0; side < other.getNumberOfSides(); side++) { - /* get the axis that we will project onto */ - if (side == 0) { - axis.x = - other.points[other.getNumberOfSides() - 1].y - - other.points[0].y; - axis.y = - other.points[0].x - - other.points[other.getNumberOfSides() - 1].x; - } else { - axis.x = other.points[side - 1].y - other.points[side].y; - axis.y = other.points[side].x - other.points[side - 1].x; - } - - /* normalize the axis */ - tmp = Math.sqrt(axis.x * axis.x + axis.y * axis.y); - axis.x /= tmp; - axis.y /= tmp; - - /* project polygon A onto axis to determine the min/max */ - minA = maxA = this.points[0].x * axis.x + this.points[0].y * axis.y; - for (i = 1; i < this.getNumberOfSides(); i++) { - tmp = this.points[i].x * axis.x + this.points[i].y * axis.y; - if (tmp > maxA) maxA = tmp; - else if (tmp < minA) minA = tmp; - } - /* correct for offset */ - tmp = this.center.x * axis.x + this.center.y * axis.y; - minA += tmp; - maxA += tmp; - - /* project polygon B onto axis to determine the min/max */ - minB = maxB = - other.points[0].x * axis.x + other.points[0].y * axis.y; - for (i = 1; i < other.getNumberOfSides(); i++) { - tmp = other.points[i].x * axis.x + other.points[i].y * axis.y; - if (tmp > maxB) maxB = tmp; - else if (tmp < minB) minB = tmp; - } - /* correct for offset */ - tmp = other.center.x * axis.x + other.center.y * axis.y; - minB += tmp; - maxB += tmp; - - /* test if lines intersect, if not, return false */ - if (maxA < minB || minA > maxB) { - return false - } else { - let o = maxA > maxB ? maxB - minA : maxA - minB; - if (o < overlap) { - overlap = o; - smallest = { x: axis.x, y: axis.y }; - } - } - } - return { overlap: overlap + 0.001, axis: smallest } - } - - static fromPoints(points) { - let min = { x: Number.MAX_VALUE, y: Number.MAX_VALUE }; - let max = { x: Number.MIN_VALUE, y: Number.MIN_VALUE }; - for (let p of points) { - min.x = Math.min(p.x, min.x); - max.x = Math.max(p.x, max.x); - min.y = Math.min(p.y, min.y); - max.y = Math.max(p.y, max.y); - } - let center = Points.mean(min, max); - let polygon = new Polygon(center); - for (let p of points) { - polygon.addAbsolutePoint(p); - } - return polygon - } - } - - /* global apollo, subscriptions, gql */ - - /** - * A special InteractionManager for fullscreen apps, which may - * go beyond the limits of WebGL drawing buffers. On Safari and Chrome - * the drawing buffers are limited to 4096 in width (Safari) or 4096x4096 - * in total buffer size (Chrome). The original InteractionManager.mapPositionToPoint - * does not work with these extreme sizes which mainly occur if large - * retina displays (>= 4K) are used with devicePixelRatio > 1. - * - * @private - * @class - * @extends PIXI.interaction.InteractionManager - * @see {@link http://pixijs.download/dev/docs/PIXI.interaction.InteractionManager.html|PIXI.interaction.InteractionManager} - * @see {@link https://stackoverflow.com/questions/29710696/webgl-drawing-buffer-size-does-not-equal-canvas-size} - */ - class FullscreenInteractionManager extends PIXI.interaction.InteractionManager { - - mapPositionToPoint(point, x, y) { - let resolution = this.renderer.resolution; - let extendWidth = 1.0; - let extendHeight = 1.0; - let dy = 0; - let canvas = this.renderer.view; - let context = canvas.getContext('webgl'); - if (context.drawingBufferWidth < canvas.width || - context.drawingBufferHeight < canvas.height) { - extendWidth = context.drawingBufferWidth / canvas.width; - extendHeight = context.drawingBufferHeight / canvas.height; - //dx = wantedWidth - context.drawingBufferWidth - dy = (canvas.height - context.drawingBufferHeight) / resolution; - } - x *= extendWidth; - y *= extendHeight; - - super.mapPositionToPoint(point, x, y + dy); - } - } - - /** - * The class PixiApp extends the class PIXI.Application - * by several functions and makes meaningful pre-assumptions. - * - * @example - * // Create the app - * const app = new PIXIApp({ - * view: canvas, - * width: 450, - * height: 150, - * fpsLogging: true, - * theme: 'light', - * transparent: false - * }).setup().run() - * - * @class - * @extends PIXI.Application - * @see {@link http://pixijs.download/dev/docs/PIXI.Application.html|PIXI.Application} - */ - class PIXIApp extends PIXI.Application { - - /** - * Creates an instance of a PixiApp. - * - * @constructor - * @param {object} [opts={}] - An options object. The following options can be set: - * @param {number} [opts.width] - The width of the renderer. If no set, the application will run in fullscreen. - * @param {number} [opts.height] - The height of the renderer. If no set, the application will run in fullscreen. - * @param {HTMLElement} [opts.view] - The canvas HTML element. If not set, a render-element is added inside the body. - * @param {boolean} [opts.transparent=true] - Should the render view be transparent? - * @param {boolean} [opts.antialias=true] - Sets antialias (only applicable in chrome at the moment). - * @param {number} [opts.resolution=window.devicePixelRatio | 1] - The resolution / device pixel ratio of the renderer, retina would be 2. - * @param {boolean} [opts.autoResize=true] - Should the canvas-element be resized automatically if the resolution was set? - * @param {number} [opts.backgroundColor=0x282828] - The color of the background. - * @param {string|Theme} [opts.theme=dark] - The name of the theme (dark, light, red) or a Theme object to use for styling. - * @param {boolean} [opts.fpsLogging=false] - If set to true, the current frames per second are displayed in the upper left corner. - * @param {object} [opts.progress={}] - Can be used to add options to the progress bar. See class Progress for more informations. - * @param {boolean} [opts.forceCanvas=false] - Prevents selection of WebGL renderer, even if such is present. - * @param {boolean} [opts.roundPixels=true] - Align PIXI.DisplayObject coordinates to screen resolution. - * @param {boolean} [opts.monkeyPatchMapping=true] - Monkey patch for canvas fullscreen support on large displays. - * @param {boolean} [opts.adaptive=true] - Adds Graphics adaptive calculation of quadratic curve and arc subdivision. - */ - constructor({ - width = null, height = null, view = null, - transparent = true, backgroundColor = 0x282828, theme = 'dark', - antialias = true, resolution = window.devicePixelRatio || 1, autoResize = true, - fpsLogging = false, progress = {}, forceCanvas = false, roundPixels = true, monkeyPatchMapping = true, adaptive = true, - graphql = false }) { - - const fullScreen = !width || !height; - - if (fullScreen) { - width = window.innerWidth; - height = window.innerHeight; - } - - super({ - view: view, - width: width, - height: height, - transparent: transparent, - antialias: antialias, - resolution: resolution, - autoResize: autoResize, - backgroundColor: backgroundColor, - roundPixels: roundPixels, - forceCanvas: forceCanvas - }); - - this.width = width; - this.height = height; - this.theme = Theme.fromString(theme); - this.fpsLogging = fpsLogging; - this.progressOpts = progress; - this.fullScreen = fullScreen; - this.orient = null; - this.originalMapPositionToPoint = null; - this.monkeyPatchMapping = monkeyPatchMapping; - PIXI.Graphics.CURVES.adaptive = adaptive; - this.graphql = graphql; - if (fullScreen || autoResize) { - console.log('App is in fullScreen mode or autoResize mode'); - const resizeDebounced = debounce(event => this.resize(event), 50); - window.addEventListener('resize', resizeDebounced); - document.body.addEventListener('orientationchange', this.checkOrientation.bind(this)); - } - if (monkeyPatchMapping) { - console.log('Using monkey patched coordinate mapping'); - // Pluggin the specializtion does not work. Monkey patching does - // this.renderer.plugins.interaction = new FullscreenInteractionManager(this.renderer) - this.monkeyPatchPixiMapping(); - } - } - - /** - * Extra setup method to construct complex scenes, etc... - * Overwrite this method if you need additonal views and components. - * - * @return {PIXIApp} A reference to the PIXIApp for chaining. - */ - setup() { - this.scene = this.sceneFactory(); - this.stage.addChild(this.scene); - - // fpsLogging - if (this.fpsLogging) { - this.addFpsDisplay(); - } - - // GraphQL - if (this.graphql && typeof apollo !== 'undefined') { - - const networkInterface = apollo.createNetworkInterface({ - uri: '/graphql' - }); - - const wsClient = new subscriptions.SubscriptionClient(`wss://${location.hostname}/subscriptions`, { - reconnect: true, - connectionParams: {} - }); - - const networkInterfaceWithSubscriptions = subscriptions.addGraphQLSubscriptions( - networkInterface, - wsClient - ); - - this.apolloClient = new apollo.ApolloClient({ - networkInterface: networkInterfaceWithSubscriptions - }); - } - - // progress - this._progress = new Progress(Object.assign({ theme: this.theme }, this.progressOpts, { app: this })); - this._progress.visible = false; - this.stage.addChild(this._progress); - - return this - } - - /** - * Tests whether the width is larger than the height of the application. - * - * @return {boolean} Returns true if app is in landscape mode. - */ - orientation() { - return this.width > this.height - } - - /** - * Checks orientation and adapts view size if necessary. Implements a - * handler for the orientationchange event. - * - * @param {event=} - orientationchange event - */ - checkOrientation(event) { - var value = this.orientation(); - if (value != this.orient) { - setTimeout(100, function () { - this.orientationChanged(true); - }.bind(this)); - this.orient = value; - } - } - - /** - * Called if checkOrientation detects an orientation change event. - * - * @param {boolean=} [force=false] - Called if checkOrientation detects an orientation change event. - */ - orientationChanged(force = false) { - if (this.expandRenderer() || force) { - this.layout(); - } - } - - /** - * Called after a resize. Empty method but can be overwritten to - * adapt their layout to the new app size. - * - * @param {number} [width] - The width of the app. - * @param {number} [height] - The height of the app. - */ - layout(width, height) { - - } - - /** - * Draws the display tree of the app. Typically this can be delegated - * to the layout method. - * - */ - draw() { - this.layout(this.width, this.height); - } - - /* - * Run the application. Override this method with everything - * that is needed to maintain your App, e.g. setup calls, main loops, etc. - * - */ - run() { - return this - } - - /* - * Overwrite this factory method if your application needs a special - * scene object. - * - * @returns {PIXI.Container} - A new PIXI Container for use as a scene. - */ - sceneFactory() { - return new PIXI.Container() - } - - /** - * Adds the display of the frames per second to the renderer in the upper left corner. - * - * @return {PIXIApp} - Returns the PIXIApp for chaining. - */ - addFpsDisplay() { - const fpsDisplay = new FpsDisplay(this); - this.stage.addChild(fpsDisplay); - - return this - } - - /** - * Returns the size of the renderer as an object with the keys width and height. - * - * @readonly - * @member {object} - */ - get size() { - return { width: this.width, height: this.height } - } - - /** - * Returns the center of the renderer as an object with the keys x and y. - * - * @readonly - * @member {object} - */ - get center() { - return { x: this.width / 2, y: this.height / 2 } - } - - /** - * Resizes the renderer to fit into the window or given width and height. - * - * @param {object} [event] - The event. - * @param {object=} [opts={}] - The event. - * @param {number} [opts.width=window.innerWidth] - The width of the app to resize to. - * @param {number} [opts.height=window.innerHeight] - The height of the app to resize to. - * @return {PIXIApp} - Returns the PIXIApp for chaining. - */ - resize(event, { width = window.innerWidth, height = window.innerHeight } = {}) { - this.width = width; - this.height = height; - this.expandRenderer(); - this.layout(width, height); - //console.log("App.resize", width, height, window.innerWidth, window.innerHeight ) - // if (this.scene) { - // console.log("gl.drawingBufferWidth", this.renderer.view.getContext('webgl').drawingBufferWidth) - // console.log("scene", this.scene.scale, this.renderer, this.renderer.autoResize, this.renderer.resolution) - // } - return this - } - - /** - * @todo Write the documentation. - * - * @private - */ - monkeyPatchPixiMapping() { - if (this.originalMapPositionToPoint === null) { - let interactionManager = this.renderer.plugins.interaction; - this.originalMapPositionToPoint = interactionManager.mapPositionToPoint; - interactionManager.mapPositionToPoint = (point, x, y) => { - return this.fixedMapPositionToPoint(point, x, y) - }; - } - } - - /** - * In some browsers the canvas is distorted if the screen resolution and - * overall size of the canvas exceeds the internal limits (e.g. 4096 x 4096 pixels). - * To compensate these distortions we need to fix the mapping to the actual - * drawing buffer coordinates. - * @private - * @param {any} local - * @param {number} x - * @param {number} y - * @return {} interactionManager.mapPositionToPoint - */ - fixedMapPositionToPoint(local, x, y) { - let resolution = this.renderer.resolution; - let interactionManager = this.renderer.plugins.interaction; - let extendWidth = 1.0; - let extendHeight = 1.0; - let dy = 0; - let canvas = this.renderer.view; - let context = canvas.getContext('webgl'); - - if (context !== null && (context.drawingBufferWidth < canvas.width || - context.drawingBufferHeight < canvas.height)) { - extendWidth = context.drawingBufferWidth / canvas.width; - extendHeight = context.drawingBufferHeight / canvas.height; - //dx = wantedWidth - context.drawingBufferWidth - dy = (canvas.height - context.drawingBufferHeight) / resolution; - } - x *= extendWidth; - y *= extendHeight; - return this.originalMapPositionToPoint.call(interactionManager, local, x, y + dy) - } - - /** - * Expand the renderer step-wise on resize. - * - * @param {number} [expand] - The amount of additional space for the renderer [px]. - * @return {boolean} true if the renderer was resized. - */ - expandRenderer(expand = 256) { - let renderer = this.renderer; - let resolution = this.renderer.resolution; - let ww = this.width; - let hh = this.height; - let sw = this.screen.width; - let sh = this.screen.height; - if (ww > sw || hh > sh) { - //console.log('App.expandRenderer') - renderer.resize(ww + expand, hh + expand); - return true - } - - renderer.resize(ww, hh); - return false - } - - /** - * Set the loading progress of the application. If called for the first time, display the progress bar. - * - * @param {number} [value] - Should be a value between 0 and 100. If 100, the progress bar will disappear. - * @return {PIXIApp|Progress} The PixiApp object for chaining or the Progress object when the method was - * called without a parameter. - */ - progress(value) { - - if (typeof value === 'undefined') { - return this._progress - } - - this._progress.visible = true; - this._progress.progress = value; - - return this - } - - /** - * Opens a new Modal object binded to this app. - * - * @param {object} [opts] - An options object for the Modal object. - * @return {Modal} Returns the Modal object. - */ - modal(opts = {}) { - - let modal = new Modal(Object.assign({ theme: this.theme }, opts, { app: this })); - this.scene.addChild(modal); - - return modal - } - - /** - * Opens a new Message object binded to this app. - * - * @param {object} [opts] - An options object for the Message object. - * @return {Message} Returns the Message object. - */ - message(opts = {}) { - - let message = new Message(Object.assign({ theme: this.theme }, opts, { app: this })); - this.scene.addChild(message); - - return message - } - - /** - * Loads sprites, e.g. images into the PIXI TextureCache. - * - * @param {string|string[]} resources - A String or an Array of urls to the images to load. - * @param {function} [loaded] - A callback which is executed after all resources has been loaded. - * Receives one paramter, a Map of sprites where the key is the path of the image which was - * loaded and the value is the PIXI.Sprite object. - * @param {object} [opts] - An options object for more specific parameters. - * @param {boolean} [opts.resolutionDependent=true] - Should the sprites be loaded dependent of the - * renderer resolution? - * @param {boolean} [opts.progress=false] - Should a progress bar display the loading status? - * @return {PIXIApp} The PIXIApp object for chaining. - */ - loadSprites(resources, loaded = null, { resolutionDependent = true, progress = false } = {}) { - - this.loadTextures(resources, textures => { - - let sprites = new Map(); - - for (let [key, texture] of textures) { - sprites.set(key, new PIXI.Sprite(texture)); - } - - if (loaded) { - loaded.call(this, sprites); - } - - }, { resolutionDependent, progress }); - - return this - } - - /** - * Loads textures, e.g. images into the PIXI TextureCache. - * - * @param {string|string[]} resources - A String or an Array of urls to the images to load. - * @param {function} [loaded] - A callback which is executed after all resources has been loaded. - * Receives one paramter, a Map of textures where the key is the path of the image which was - * loaded and the value is the PIXI.Texture object. - * @param {object} [opts] - An options object for more specific parameters. - * @param {boolean} [opts.resolutionDependent=true] - Should the textures be loaded dependent of the - * renderer resolution? - * @param {boolean} [opts.progress=false] - Should a progress bar display the loading status? - * @return {PIXIApp} The PIXIApp object for chaining. - */ - loadTextures(resources, loaded = null, { resolutionDependent = true, progress = false } = {}) { - - if (!Array.isArray(resources)) { - resources = [resources]; - } - - const loader = this.loader; - - for (let resource of resources) { - - if (!loader.resources[resource]) { - - if (resolutionDependent) { - let resolution = Math.round(this.renderer.resolution); - switch (resolution) { - case 2: - loader.add(resource, resource.replace(/\.([^.]*)$/, '@2x.$1')); - break - case 3: - loader.add(resource, resource.replace(/\.([^.]*)$/, '@3x.$1')); - break - default: - loader.add(resource); - break - } - } else { - loader.add(resource); - } - } - } - - if (progress) { - loader.on('progress', e => { - this.progress(e.progress); - }); - } - - loader.load((loader, resources) => { - const textures = new Map(); - - for (let key in resources) { - textures.set(key, resources[key].texture); - } - - if (loaded) { - loaded.call(this, textures); - } - }); - - return this - } - - /** - * Queries the GraphQL endpoint. - * - * @param {string} [query] - The GraphQL query string. - * @param {object} [opts={}] - An options object. The following options can be set: - * http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient.query - * @return {Promise} Returns a Promise which is either resolved with the resulting data or - * rejected with an error. - */ - query(query, opts = {}) { - - if (typeof query === 'string') { - opts = Object.assign({}, opts, { query }); - } else { - opts = Object.assign({}, query); - } - - opts.query = opts.query.trim(); - - if (!opts.query.startsWith('query')) { - if (opts.query.startsWith('{')) { - opts.query = `query ${opts.query}`; - } else { - opts.query = `query {${opts.query}}`; - } - } - - opts.query = gql(opts.query); - - return this.apolloClient.query(opts) - } - - /** - * Mutate the GraphQL endpoint. - * - * @param {string} [mutation] - The GraphQL mutation string. - * @param {object} [opts={}] - An options object. The following options can be set: - * http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient.mutate - * @return {Promise} Returns a Promise which is either resolved with the resulting data or - * rejected with an error. - */ - mutate(mutation, opts = {}) { - - if (typeof mutation === 'string') { - opts = Object.assign({}, opts, { mutation }); - } else { - opts = Object.assign({}, mutation); - } - - opts.mutation = opts.mutation.trim(); - - if (!opts.mutation.startsWith('mutation')) { - if (opts.mutation.startsWith('{')) { - opts.mutation = `mutation ${opts.mutation}`; - } else { - opts.mutation = `mutation {${opts.mutation}}`; - } - } - - opts.mutation = gql(opts.mutation); - - return this.apolloClient.mutate(opts) - } - - /** - * Subscribe the GraphQL endpoint. - * - * @param {string} [subscription] - The GraphQL subscription. - * @param {object} [opts={}] - An options object. The following options can be set: - * http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient.query - * @return {Promise} Returns a Promise which is either resolved with the resulting data or - * rejected with an error. - */ - subscribe(subscription, opts = {}) { - - if (typeof subscription === 'string') { - opts = Object.assign({}, opts, { subscription }); - } else { - opts = Object.assign({}, subscription); - } - - opts.subscription = opts.subscription.trim(); - - if (!opts.subscription.startsWith('subscription')) { - if (opts.subscription.startsWith('{')) { - opts.subscription = `subscription ${opts.subscription}`; - } else { - opts.subscription = `subscription {${opts.subscription}}`; - } - } - - opts.query = gql(opts.subscription); - - delete opts.subscription; - - return this.apolloClient.subscribe(opts) - } - - /** - * Supports the page as a global coordinate system and converts browser page coordinates - * to local DisplayObject coordinates. - * - * @param {DisplayObject} displayObject - The PIXI displayObject. - * @param {number} x - The x coordinate. - * @param {number} y - The y coordinate. - * - * @return {PIXI.Point} Returns a PIXI.Point. - */ - - convertPointFromPageToNode(displayObject, x, y) { - let resolution = this.renderer.resolution; - console.log("resolution", resolution); - let pixiGlobal = window.convertPointFromPageToNode(app.view, x, y); - pixiGlobal.x /= resolution; - pixiGlobal.y /= resolution; - return displayObject.toLocal(new PIXI.Point(pixiGlobal.x, pixiGlobal.y)) - } - - /** - * Supports the page as a global coordinate system and converts local DisplayObject coordinates - * to browser page coordinates. - * - * @param {DisplayObject} displayObject - The PIXI displayObject. - * @param {number} x - The x coordinate. - * @param {number} y - The y coordinate. - * - * @return {Point} Returns a DOM Point. - */ - - convertPointFromNodeToPage(displayObject, x, y) { - let resolution = this.renderer.resolution; - let pixiGlobal = displayObject.toGlobal(new PIXI.Point(x, y)); - pixiGlobal.x *= resolution; - pixiGlobal.y *= resolution; - // console.log("app.convertPointFromNodeToPage", pixiGlobal) - return window.convertPointFromNodeToPage(app.view, pixiGlobal.x, pixiGlobal.y) - } - } - - /** - * The class fpsdisplay shows in the upper left corner - * of the renderer the current image refresh rate. - * - * @private - * @class - * @extends PIXI.Graphics - * @see {@link http://pixijs.download/dev/docs/PIXI.Graphics.html|PIXI.Graphics} - */ - class FpsDisplay extends PIXI.Graphics { - - /** - * Creates an instance of a FpsDisplay. - * - * @constructor - * @param {PIXIApp} app - The PIXIApp where the frames per second should be displayed. - */ - constructor(app) { - - super(); - - this.app = app; - - this.lineStyle(3, 0x434f4f, 1) - .beginFill(0x434f4f, .6) - .drawRoundedRect(0, 0, 68, 32, 5) - .endFill() - .position.set(20, 20); - - this.text = new PIXI.Text(this.fps, new PIXI.TextStyle({ - fontFamily: 'Arial', - fontSize: 14, - fontWeight: 'bold', - fill: '#f6f6f6', - stroke: '#434f4f', - strokeThickness: 3 - })); - this.text.position.set(6, 6); - - this.addChild(this.text); - - this.refreshFps(); - - window.setInterval(this.refreshFps.bind(this), 1000); - } - - /** - * Refreshes fps numer. - * - * @return {PIXIApp} Returns the PIXIApp object for chaining. - */ - refreshFps() { - this.text.text = `${(this.app.ticker.FPS).toFixed(1)} fps`; - - return this - } - } - - /** - * A Gaussian blur filter. With this filter, you can blur an area of a PIXI.DisplayObject. This cannot - * be done with the PIXI.filters.BlurFilter (when you use the PIXI.filters.BlurFilter with - * an filter area, all pixels outside of the area are not displayed). Attention: The area of - * the filter is always in global scope, NOT relative to the PIXI.DisplayObject the filter - * is assigned to! - * - * @example - * // Create the app - * const app = new PIXIApp({ - * view: canvas, - * width: 480, - * height: 270, - * transparent: false - * }).setup().run() - * - * // Add a video sprite - * const sprite = new PIXI.Sprite(PIXI.Texture.fromVideo("assets/blurfilter.mp4")) - * sprite.width = app.size.width - * sprite.height = app.size.height - * app.scene.addChild(sprite) - * - * // Create the filter and assign it to the scene - * const blurFilter = new BlurFilter(new PIXI.Rectangle(20, 20, 80, 60)) - * app.scene.filters = [blurFilter] - * - * @class - * @extends PIXI.Filter - * @param {PIXI.Rectangle|PIXI.Circle|PIXI.DisplayObject} shape The area where the blur effect should be applied to. Relative to the - * canvas, NOT relative to the PIXI.DisplayObject where the blur effect is assigned to! - * @param {number} [blur=50] The strength of the blur. - */ - class BlurFilter extends PIXI.Filter { - - constructor(shape, blur = 50) { - super(); - - const normalized = this.normalize(shape); - - this.tiltShiftXFilter = new TiltShiftXFilter(normalized, blur); - this.tiltShiftYFilter = new TiltShiftYFilter(normalized, blur); - } - - apply(filterManager, input, output) { - let renderTarget = filterManager.getRenderTarget(true); - this.tiltShiftXFilter.apply(filterManager, input, renderTarget); - this.tiltShiftYFilter.apply(filterManager, renderTarget, output); - filterManager.returnRenderTarget(renderTarget); - } - - /** - * The strength of the blur. - * - * @member {number} - */ - get blur() { - return this.tiltShiftXFilter.blur - } - set blur(value) { - this.tiltShiftXFilter.blur = this.tiltShiftYFilter.blur = value; - } - - /** - * The blur shape. - * - * @member {PIXI.Rectangle|PIXI.Circle|PIXI.DisplayObject} - */ - get shape() { - return this.tiltShiftXFilter.shape - } - set shape(value) { - this.tiltShiftXFilter.shape = this.tiltShiftYFilter.shape = this.normalize(value); - } - - /** - * - * @private - * @param {PIXI.Rectangle|PIXI.Circle|PIXI.DisplayObject} value - * @returns {Object} - */ - normalize(value) { - - let shape = null; - - if (value instanceof PIXI.Circle) { - shape = {type: 'circle', x: value.x, y: value.y, r: value.radius}; - } else if (value instanceof PIXI.Rectangle) { - shape = {type: 'rectangle', x: value.x, y: value.y, width: value.width, height: value.height}; - } else { - const bounds = value.getBounds(); - shape = {type: 'rectangle', x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height}; - } - - return shape - } - } - - /** - * A TiltShiftAxisFilter. - * - * @class - * @extends PIXI.Filter - * @abstract - * @private - */ - class TiltShiftAxisFilter extends PIXI.Filter { - - constructor(shape, blur){ - - const vertex = ` - attribute vec2 aVertexPosition; - attribute vec2 aTextureCoord; - - uniform mat3 projectionMatrix; - - varying vec2 vTextureCoord; - - void main(void) { - gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); - vTextureCoord = aTextureCoord; - } - `; - - const fragment = ` - varying vec2 vTextureCoord; - - uniform vec4 filterArea; - uniform sampler2D uSampler; - uniform int shape; - uniform vec4 rectangle; - uniform vec3 circle; - uniform float blur; - uniform vec2 delta; - uniform vec2 texSize; - - float random(vec3 scale, float seed) { - return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); - } - - void main(void) { - // textureCoord to pixelCoord - vec2 pixelCoord = vTextureCoord * filterArea.xy - vec2(4.0, 4.0); // FIXME: There's a shift of 4 * 4 pixels, don't know why... - - bool inside = false; - - if (shape == 1) { - inside = distance(pixelCoord, circle.xy) <= circle.z; - } else if (shape == 2) { - inside = pixelCoord.x >= rectangle.x && pixelCoord.x <= rectangle.z && pixelCoord.y >= rectangle.y && pixelCoord.y <= rectangle.w; - } - - if (inside) { - vec4 color = vec4(0.0); - float total = 0.0; - - float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); - - for (float t = -30.0; t <= 30.0; t++) { - float percent = (t + offset - 0.5) / 30.0; - float weight = 1.0 - abs(percent); - vec4 sample = texture2D(uSampler, vTextureCoord + delta / texSize * percent * blur); - sample.rgb *= sample.a; - color += sample * weight; - total += weight; - } - - gl_FragColor = color / total; - gl_FragColor.rgb /= gl_FragColor.a + 0.00001; - } else { - gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)); - } - } - `; - - super(vertex, fragment); - - if (shape.type === 'circle') { - this.uniforms.shape = 1; - this.uniforms.circle = [shape.x, shape.y, shape.r]; - } else { - this.uniforms.shape = 2; - this.uniforms.rectangle = [shape.x, shape.y, shape.x + shape.width, shape.y + shape.height]; - } - this.uniforms.blur = blur; - this.uniforms.delta = new PIXI.Point(0, 0); - this.uniforms.texSize = new PIXI.Point(480, 270); - - this.updateDelta(); - } - - /** - * The strength of the blur. - * - * @member {number} - * @memberof PIXI.filters.TiltShiftAxisFilter# - */ - get blur() { - return this.uniforms.blur - } - set blur(value) { - this.uniforms.blur = value; - } - - /** - * The blur shape. - * - * @member {PIXI.Rectangle} - * @memberof PIXI.filters.TiltShiftAxisFilter# - */ - get shape() { - if (this.uniforms.shape === 1) { - const circle = this.uniforms.circle; - return new PIXI.Circle(circle[0], circle[1], circle[2]) - } else { - const rectangle = this.uniforms.rectangle; - return new PIXI.Rectangle(rectangle[0], rectangle[1], rectangle[2], rectangle[3]) - } - } - set shape(value) { - if (value.type === 'circle') { - this.uniforms.shape = 1; - this.uniforms.circle = [value.x, value.y, value.r]; - } else { - this.uniforms.shape = 2; - this.uniforms.rectangle = [value.x, value.y, value.x + value.width, value.y + value.height]; - } - } - } - - /** - * A TiltShiftXFilter. - * - * @class - * @extends PIXI.TiltShiftAxisFilter - * @private - */ - class TiltShiftXFilter extends TiltShiftAxisFilter { - /** - * Updates the filter delta values. - */ - updateDelta() { - this.uniforms.delta.x = 0.1; - this.uniforms.delta.y = 0; - } - } - - /** - * A TiltShiftYFilter. - * - * @class - * @extends PIXI.TiltShiftAxisFilter - * @private - */ - class TiltShiftYFilter extends TiltShiftAxisFilter { - /** - * Updates the filter delta values. - */ - updateDelta() { - this.uniforms.delta.x = 0; - this.uniforms.delta.y = 0.1; - } - } - - // In order to test this interface implementation run jsc interface.js - - class Interface { - // Abstract interface that should be extended in interface subclasses. - // By convention all interfaces should start with an upper 'I' - - static implementationError(klass) { - let interfaceKeys = Reflect.ownKeys(this.prototype); - let classKeys = Reflect.ownKeys(klass.prototype); - for(let key of interfaceKeys) { - let interfaceDesc = this.prototype[key]; - let classDesc = klass.prototype[key]; - if (typeof(classDesc) == 'undefined') - return 'Missing ' + key - } - return null - } - - static implementedBy(klass) { - // In the first step only checks whether the methods of this - // interface are all implemented by the given class - let error = this.implementationError(klass); - return error == null - } - - // TODO: Specify optional methods - // static optionalMethods() { - // return [this.onMouseWheel] - // } - } - - /* globals Hammer, propagating */ - - /** Interaction patterns - - See interaction.html for explanation - */ - - class IInteractionTarget extends Interface { - capture(event) { - return typeof true - } - - onStart(event, interaction) { } - onMove(event, interaction) { } - onEnd(event, interaction) { } - - onMouseWheel(event) { } - } - - class IInteractionMapperTarget extends Interface { - capture(event) { - return typeof true - } - - findTarget(event, local, global) { - return IInteractionTarget - } - } - - class PointMap extends MapProxy { - // Collects touch points, mouse coordinates, etc. as key value pairs. - // Keys are pointer and touch ids, the special "mouse" key. - // Values are points, i.e. all objects with numeric x and y properties. - constructor(points = {}) { - super(); - for (let key in points) { - this.set(key, points[key]); - } - } - - toString() { - let points = []; - for (let key of this.keys()) { - let value = this.get(key); - points.push(`${key}:{x:${value.x}, y:${value.y}}`); - } - let attrs = points.join(', '); - return `[PointMap ${attrs}]` - } - - clone() { - let result = new PointMap(); - for (let key of this.keys()) { - let value = this.get(key); - result.set(key, { x: value.x, y: value.y }); - } - return result - } - - keyOf(value) { - for (let key of this.keys()) { - let p = this.get(key); - if (p.x == value.x && p.y == value.y) { - return key - } - } - return null - } - - firstKey() { - for (let key of this.keys()) { - return key - } - return null - } - - first() { - for (let key of this.keys()) { - return this.get(key) - } - return null - } - - farthests() { - if (this.size == 0) { - return null - } - let pairs = []; - for (let key of this.keys()) { - let p = this.get(key); - p.key = key; - for (let k of this.keys()) { - let q = this.get(k); - q.key = k; - pairs.push([p, q]); - } - } - let sorted = pairs.sort((a, b) => { - return Points.distance(b[0], b[1]) - Points.distance(a[0], a[1]) - }); - return sorted[0] - } - - mean() { - if (this.size == 0) { - return null - } - let x = 0.0, - y = 0.0; - for (let p of this.values()) { - x += p.x; - y += p.y; - } - return { x: x / this.size, y: y / this.size } - } - } - - class InteractionDelta { - constructor(x, y, zoom, rotate, about) { - this.x = x; - this.y = y; - this.zoom = zoom; - this.rotate = rotate; - this.about = about; - } - - toString() { - let values = []; - for (let key of Object.keys(this)) { - let value = this[key]; - if (key == 'about') { - values.push(`${key}:{x:${value.x}, y:${value.y}}`); - } else { - values.push(`${key}:${value}`); - } - } - let attrs = values.join(', '); - return `[InteractionDelta ${attrs}]` - } - } - - class InteractionPoints { - constructor(parent = null) { - this.parent = parent; - this.current = new PointMap(); - this.previous = new PointMap(); - this.start = new PointMap(); - this.ended = new PointMap(); - this.timestamps = new Map(); - } - - moved(key) { - let current = this.current.get(key); - let previous = this.previous.get(key); - return Points.subtract(current, previous) - } - - move() { - let current = this.current.mean(); - let previous = this.previous.mean(); - return Points.subtract(current, previous) - } - - /** - * Computes the delta between previous and current angles. Corrects - * value that are larger than 45° - * @param {*} a - * @param {*} b - * @returns delta - */ - diffAngle(a, b) { - let alpha = Math.atan2(Math.sin(a - b), Math.cos(a - b)); - if (Math.abs(alpha) > Math.PI / 4) { - alpha -= Math.PI; - } - return alpha - } - - /** - * Computes the delta between interaction points at t and t+1. - * - * @returns InteractionDelta - * @memberof InteractionPoints - */ - delta() { - let csize = this.current.size; - let psize = this.previous.size; - if (csize >= 2 && csize == psize) { - // Reduce to the two farthests points - let current = this.current.farthests(); - - let c1 = current[0]; - let c2 = current[1]; - - let p1 = this.previous.get(c1.key); - let p2 = this.previous.get(c2.key); - - //let p1 = previous[0] - //let p2 = previous[1] - - let d1 = Points.subtract(c1, p1); - let d2 = Points.subtract(c2, p2); - let cm = Points.mean(c1, c2); - //let pm = Points.mean(p1, p2) - // UO: Using the mean lead to jumps between time slices with 3 and 2 fingers - // We use the mean of deltas instead - let delta = Points.mean(d1, d2); //Points.subtract(cm, pm) - let zoom = 1.0; - let distance1 = Points.distance(p1, p2); - let distance2 = Points.distance(c1, c2); - if (distance1 != 0 && distance2 != 0) { - zoom = distance2 / distance1; - } - let currentAngle = Points.angle(c1, c2); - let previousAngle = Points.angle(p1, p2); - let alpha = this.diffAngle(currentAngle, previousAngle); - return new InteractionDelta(delta.x, delta.y, zoom, alpha, cm) - } else if (csize == 1 && psize == 1 && this.current.firstKey() == this.previous.firstKey()) { - // We need to ensure that the keys are the same - let current = this.current.first(); - let previous = this.previous.first(); - let delta = Points.subtract(current, previous); - return new InteractionDelta(delta.x, delta.y, 1.0, 0.0, current) - } - return null - } - - started(key, point) { - this.current.set(key, point); - this.start.set(key, point); - this.previous.set(key, point); - this.timestamps.set(key, performance.now()); - } - - update(key, point) { - // Returns true iff the key is new - this.current.set(key, point); - if (!this.start.has(key)) { - this.start.set(key, point); - this.previous.set(key, point); - this.timestamps.set(key, performance.now()); - return true - } - return false - } - - updatePrevious() { - for (let key of this.current.keys()) { - this.previous.set(key, this.current.get(key)); - } - } - - stop(key, point) { - if (this.current.has(key)) { - this.current.delete(key); - this.previous.delete(key); - this.ended.set(key, point); - } - } - - finish(key, point) { - this.current.delete(key); - this.previous.delete(key); - this.start.delete(key); - this.timestamps.delete(key); - this.ended.delete(key); - } - - isFinished() { - return this.current.size == 0 - } - - isNoLongerTwoFinger() { - return this.previous.size > 1 && this.current.size < 2 - } - - isTap(key) { - return this.parent.isTap(key) - } - - isDoubleTap(key) { - return this.parent.isDoubleTap(key) - } - - isLongPress(key) { - return this.parent.isLongPress(key) - } - } - - class Interaction extends InteractionPoints { - constructor(tapDistance = 10, tapDuration = 250.0, longPressTime = 500.0) { - super(); - this.tapDistance = tapDistance; - this.tapCounts = new Map(); - this.tapPositions = new Map(); - this.tapTimestamps = new Map(); - this.tapDuration = tapDuration; - this.longPressTime = longPressTime; - this.targets = new Map(); - this.subInteractions = new Map(); // target:Object : InteractionPoints - } - - stop(key, point) { - super.stop(key, point); - for (let points of this.subInteractions.values()) { - points.stop(key, point); - } - } - - addTarget(key, target) { - this.targets.set(key, target); - this.subInteractions.set(target, new InteractionPoints(this)); - } - - removeTarget(key) { - let target = this.targets.get(key); - this.targets.delete(key); - // Only remove target if no keys are refering to the target - let remove = true; - for (let t of this.targets.values()) { - if (target === t) { - remove = false; - } - } - if (remove) { - this.subInteractions.delete(target); - } - } - - finish(key, point) { - super.finish(key, point); - this.removeTarget(key); - } - - mapInteraction(points, aspects, mappingFunc) { - // Map centrally registered points to target interactions - // Returns an array of [target, updated subInteraction] pairs - let result = new Map(); - for (let key in points) { - if (this.targets.has(key)) { - let target = this.targets.get(key); - if (this.subInteractions.has(target)) { - let interaction = this.subInteractions.get(target); - for (let aspect of aspects) { - let pointMap = this[aspect]; - let point = pointMap.get(key); - let mapped = mappingFunc(point, target); - interaction[aspect].set(key, mapped); - } - result.set(target, interaction); - } - } - } - return result - } - - registerTap(key, point) { - if (this.tapCounts.has(key)) { - let count = this.tapCounts.get(key); - this.tapCounts.set(key, count+1); - } - else { - this.tapCounts.set(key, 1); - } - this.tapPositions.set(key, point); - this.tapTimestamps.set(key, performance.now()); - } - - unregisterTap(key) { - this.tapCounts.delete(key); - this.tapPositions.delete(key); - this.tapTimestamps.delete(key); - } - - isTap(key) { - let ended = this.ended.get(key); - let start = this.start.get(key); - if ( - start && - ended && - Points.distance(ended, start) < this.tapDistance - ) { - let t1 = this.timestamps.get(key); - let tookLong = performance.now() > t1 + this.longPressTime; - if (tookLong) { - return false - } - return true - } - return false - } - - isDoubleTap(key) { - let ended = this.ended.get(key); - if (this.tapCounts.has(key) && this.tapCounts.get(key) > 2) { - this.unregisterTap(key); - } - if (this.tapPositions.has(key)) { - let pos = this.tapPositions.get(key); - if (Points.distance(ended, pos) > this.tapDistance) { - this.unregisterTap(key); - } - } - if (this.tapTimestamps.has(key) && performance.now() > this.tapTimestamps.get(key) + this.tapDuration) { - //console.log("tap too long") - this.unregisterTap(key); - } - let result = false; - if (this.isTap(key)) { - - this.registerTap(key, ended); - result = this.tapCounts.get(key) == 2; - } - else { - this.unregisterTap(key); - } - //console.log("isDoubleTap", this.tapCounts.get(key), result) - return result - } - - isAnyTap() { - for (let key of this.ended.keys()) { - if (this.isTap(key)) return true - } - return false - } - - isLongPress(key) { - let ended = this.ended.get(key); - let start = this.start.get(key); - if ( - start && - ended && - Points.distance(ended, start) < this.tapDistance - ) { - let t1 = this.timestamps.get(key); - let tookLong = performance.now() > t1 + this.longPressTime; - if (tookLong) { - return true - } - return false - } - return false - } - - isAnyLongPress() { - for (let key of this.ended.keys()) { - if (this.isLongPress(key)) return true - } - return false - } - - isStylus(key) { - return key === 'stylus' - } - } - - /** - * This class implements the main delegate functionality: All necessary event handlers are registered for the - * given element. Uses PointerEvents if available or TouchEvents on iOS. The fallback is on mouse events. - * Collects the events if the interaction target captures the start event (i.e. declares that - * the target wants the start event as well as all following move and end evcents.) - * - * @export - * @class InteractionDelegate - */ - class InteractionDelegate { - // Long press: http://stackoverflow.com/questions/1930895/how-long-is-the-event-onlongpress-in-the-android - // Stylus support: https://w3c.github.io/touch-events/ - - /** - * Creates an instance of InteractionDelegate. - * @param {any} element - * @param {any} target - * @param {any} [{ mouseWheelElement = null, useCapture = true, capturePointerEvents = true, debug = false }={}] - * @memberof InteractionDelegate - */ - constructor( - element, - target, - { mouseWheelElement = null, useCapture = true, capturePointerEvents = true, cancelOnWindowOut = true, debug = false } = {} - ) { - this.debug = debug; - this.interaction = new Interaction(); - this.element = element; - this.mouseWheelElement = mouseWheelElement || element; - this.target = target; - this.useCapture = useCapture; - this.capturePointerEvents = capturePointerEvents; - this.cancelOnWindowOut = cancelOnWindowOut; - this.setupInteraction(); - } - - setupInteraction() { - if (this.debug) { - let error = this.targetInterface.implementationError( - this.target.constructor - ); - if (error != null) { - throw new Error('Expected IInteractionTarget: ' + error) - } - } - this.setupTouchInteraction(); - this.setupMouseWheelInteraction(); - } - - get targetInterface() { - return IInteractionTarget - } - - setupTouchInteraction() { - let element = this.element; - let useCapture = this.useCapture; - if (window.PointerEvent) { - if (this.debug) console.log('Pointer API' + window.PointerEvent); - element.addEventListener( - 'pointerdown', - e => { - if (this.debug) console.log('pointerdown', e.pointerId); - if (this.capture(e)) { - if (this.capturePointerEvents) { - try { - element.setPointerCapture(e.pointerId); - } catch (e) { } - } - this.onStart(e); - } - }, - useCapture - ); - element.addEventListener( - 'pointermove', - e => { - if (this.debug) console.log('pointermove', e.pointerId); - - if ( - e.pointerType == 'touch' || - (e.pointerType == 'mouse' && Events$1.isMouseDown(e)) - ) { - // this.capture(e) && - if (this.debug) - console.log('pointermove captured', e.pointerId); - this.onMove(e); - } - }, - useCapture - ); - element.addEventListener( - 'pointerup', - e => { - if (this.debug) console.log('pointerup'); - this.onEnd(e); - if (this.capturePointerEvents) { - try { - element.releasePointerCapture(e.pointerId); - } catch (e) { } - } - }, - useCapture - ); - element.addEventListener( - 'pointercancel', - e => { - if (this.debug) console.log('pointercancel'); - this.onEnd(e); - if (this.capturePointerEvents) - element.releasePointerCapture(e.pointerId); - }, - useCapture - ); - - if (!this.capturePointerEvents) { - element.addEventListener( - 'pointerleave', - e => { - if (this.debug) console.log('pointerleave'); - if (e.target == element) this.onEnd(e); - }, - useCapture - ); - } - - if (!this.capturePointerEvents) { - element.addEventListener( - 'pointerout', - e => { - if (this.debug) console.log('pointerout'); - if (e.target == element) this.onEnd(e); - }, - useCapture - ); - } - - if (this.cancelOnWindowOut) { - window.addEventListener( - 'pointerout', - e => { - if (e.target == element) { - this.onEnd(e); - } - }, - useCapture); - } - - } else if (window.TouchEvent) { - if (this.debug) console.log('Touch API'); - element.addEventListener( - 'touchstart', - e => { - if (this.debug) - console.log('touchstart', this.touchPoints(e)); - if (this.capture(e)) { - for (let touch of e.changedTouches) { - this.onStart(touch); - } - } - }, - useCapture - ); - element.addEventListener( - 'touchmove', - e => { - if (this.debug) - console.log('touchmove', this.touchPoints(e), e); - for (let touch of e.changedTouches) { - this.onMove(touch); - } - for (let touch of e.targetTouches) { - this.onMove(touch); - } - }, - useCapture - ); - element.addEventListener( - 'touchend', - e => { - if (this.debug) console.log('touchend', this.touchPoints(e)); - for (let touch of e.changedTouches) { - this.onEnd(touch); - } - }, - useCapture - ); - element.addEventListener( - 'touchcancel', - e => { - if (this.debug) - console.log( - 'touchcancel', - e.targetTouches.length, - e.changedTouches.length - ); - for (let touch of e.changedTouches) { - this.onEnd(touch); - } - }, - useCapture - ); - } else { - if (this.debug) console.log('Mouse API'); - - element.addEventListener( - 'mousedown', - e => { - if (this.debug) console.log('mousedown', e); - if (this.capture(e)) { - this.onStart(e); - } - }, - useCapture - ); - element.addEventListener( - 'mousemove', - e => { - // Dow we only use move events if the mouse is down? - // HOver effects have to be implemented by other means - // && Events.isMouseDown(e)) - - if (Events$1.isMouseDown(e)) { - if (this.debug) - console.log('mousemove', e); - this.onMove(e); - } - }, - useCapture - ); - element.addEventListener( - 'mouseup', - e => { - if (this.debug) console.log('mouseup', e); - this.onEnd(e); - }, - true - ); - - if (!this.capturePointerEvents) { - element.addEventListener( - 'mouseout', - e => { - if (e.target == element) { - this.onEnd(e); - console.warn("Shouldn't happen: mouseout ends interaction"); - } - - }, - useCapture - ); - } - if (this.cancelOnWindowOut) { - window.addEventListener( - 'mouseout', - e => { - if (e.target == element) { - this.onEnd(e); - } - }, - useCapture); - } - } - } - - isDescendant(parent, child) { - if (parent == child) return true - let node = child.parentNode; - while (node != null) { - if (node == parent) { - return true - } - node = node.parentNode; - } - return false - } - - touchPoints(event) { - let result = []; - for (let touch of event.changedTouches) { - result.push(this.extractPoint(touch)); - } - return result - } - - setupMouseWheelInteraction() { - this.mouseWheelElement.addEventListener( - 'mousewheel', - this.onMouseWheel.bind(this), - true - ); - this.mouseWheelElement.addEventListener( - 'DOMMouseScroll', - this.onMouseWheel.bind(this), - true - ); - } - - onMouseWheel(event) { - if (this.capture(event) && this.target.onMouseWheel) { - this.target.onMouseWheel(event); - } - } - - onStart(event) { - let extracted = this.extractPoint(event); - this.startInteraction(event, extracted); - this.target.onStart(event, this.interaction); - } - - onMove(event) { - let extracted = this.extractPoint(event, 'all'); - this.updateInteraction(event, extracted); - this.target.onMove(event, this.interaction); - this.interaction.updatePrevious(); - } - - onEnd(event) { - let extracted = this.extractPoint(event, 'changedTouches'); - this.endInteraction(event, extracted); - this.target.onEnd(event, this.interaction); - this.finishInteraction(event, extracted); - } - - /** - * Asks the target whether the event should be captured - * - * @param {any} event - * @returns {bool} - * @memberof InteractionDelegate - */ - capture(event) { - if (Events$1.isCaptured(event)) { - return false - } - let captured = this.target.capture(event); - return captured - } - - getPosition(event) { - return { x: event.clientX, y: event.clientY } - } - - extractPoint(event, touchEventKey = 'all') { - // 'targetTouches' - let result = {}; - switch (event.constructor.name) { - case 'MouseEvent': - let buttons = event.buttons || event.which; - if (buttons) result['mouse'] = this.getPosition(event); - break - case 'PointerEvent': - result[event.pointerId.toString()] = this.getPosition(event); - break - case 'Touch': - let id = - event.touchType === 'stylus' - ? 'stylus' - : event.identifier.toString(); - result[id] = this.getPosition(event); - break - // case 'TouchEvent': - // // Needs to be observed: Perhaps changedTouches are all we need. If so - // // we can remove the touchEventKey default parameter - // if (touchEventKey == 'all') { - // for(let t of event.targetTouches) { - // result[t.identifier.toString()] = this.getPosition(t) - // } - // for(let t of event.changedTouches) { - // result[t.identifier.toString()] = this.getPosition(t) - // } - // } - // else { - // for(let t of event.changedTouches) { - // result[t.identifier.toString()] = this.getPosition(t) - // } - // } - // break - default: - break - } - return result - } - - interactionStarted(event, key, point) { - // Callback: can be overwritten - } - - interactionEnded(event, key, point) { - // Callback: can be overwritten - } - - interactionFinished(event, key, point) { } - - startInteraction(event, extracted) { - for (let key in extracted) { - let point = extracted[key]; - this.interaction.started(key, point); - this.interactionStarted(event, key, point); - } - } - - updateInteraction(event, extracted) { - for (let key in extracted) { - let point = extracted[key]; - let updated = this.interaction.update(key, point); - if (updated) { - console.warn("new pointer in updateInteraction shouldn't happen"); - this.interactionStarted(event, key, point); - } - } - } - - endInteraction(event, ended) { - for (let key in ended) { - let point = ended[key]; - this.interaction.stop(key, point); - this.interactionEnded(event, key, point); - } - } - - finishInteraction(event, ended) { - for (let key in ended) { - let point = ended[key]; - this.interaction.finish(key, point); - this.interactionFinished(event, key, point); - } - } - } - /** - * A special InteractionDelegate that maps events to specific parts of - * the interaction target. The InteractionTarget must implement a findTarget - * method that returns an object implementing the IInteractionTarget interface. - * - * If the InteractionTarget also implements a mapPositionToPoint method this - * is used to map the points to the local coordinate space of the the target. - * - * This makes it easier to lookup elements and relate events to local - * positions. - * - * @export - * @class InteractionMapper - * @extends {InteractionDelegate} - */ - class InteractionMapper$1 extends InteractionDelegate { - - constructor( - element, - target, - { tapDistance = 10, longPressTime = 500.0, useCapture = true, mouseWheelElement = null } = {} - ) { - super(element, target, { tapDistance, useCapture, longPressTime, mouseWheelElement }); - } - - get targetInterface() { - return IInteractionMapperTarget - } - - mapPositionToPoint(point, element = null) { - if (this.target.mapPositionToPoint) { - return this.target.mapPositionToPoint(point, element) - } - return point - } - - interactionStarted(event, key, point) { - if (this.target.findTarget) { - let local = this.mapPositionToPoint(point); - let found = this.target.findTarget(event, local, point); - if (found != null) { - this.interaction.addTarget(key, found); - } - } - } - - onMouseWheel(event) { - if (this.capture(event)) { - if (this.target.findTarget) { - let point = this.getPosition(event); - let local = this.mapPositionToPoint(point); - let found = this.target.findTarget(event, local, point); - if (found != null && found.onMouseWheel) { - found.onMouseWheel(event); - return - } - } - if (this.target.onMouseWheel) { - this.target.onMouseWheel(event); - } - } - } - - onStart(event) { - let extracted = this.extractPoint(event); - this.startInteraction(event, extracted); - let mapped = this.interaction.mapInteraction( - extracted, - ['current', 'start'], - this.mapPositionToPoint.bind(this) - ); - for (let [target, interaction] of mapped.entries()) { - target.onStart(event, interaction); - } - } - - onMove(event) { - let extracted = this.extractPoint(event, 'all'); - this.updateInteraction(event, extracted); - let mapped = this.interaction.mapInteraction( - extracted, - ['current', 'previous'], - this.mapPositionToPoint.bind(this) - ); - for (let [target, interaction] of mapped.entries()) { - target.onMove(event, interaction); - interaction.updatePrevious(); - } - this.interaction.updatePrevious(); - } - - onEnd(event) { - let extracted = this.extractPoint(event, 'changedTouches'); - this.endInteraction(event, extracted); - let mapped = this.interaction.mapInteraction( - extracted, - ['ended'], - this.mapPositionToPoint.bind(this) - ); - for (let [target, interaction] of mapped.entries()) { - target.onEnd(event, interaction); - } - this.finishInteraction(event, extracted); - } - - /** - * - * - * @static - * @param {string|array} types - An event type, an array of event types or event types seperated by a space sign. The following - * events are possible: - * pan, panstart, panmove, panend, pancancel, panleft, panright, panup, pandown - * pinch, pinchstart, pinchmove, pinchend, pinchcancel, pinchin, pinchout - * press, pressup - * rotate, rotatestart, rotatemove, rotateend, rotatecancel - * swipe, swipeleft, swiperight, swipeup, swipedown - * tap - * @param {HTMLElement|HTMLElement[]} elements - An HTML element or an array of HTML elements. - * @param {function} [cb] - The callback. A function which is executed after the event occurs. Receives the event object as the - * first paramter - * @param {object} [opts] - An options object. See the hammer documentation for more details. - */ - static on(types, elements, cb, opts = {}) { - opts = Object.assign({}, { - - }, opts); - - if (typeof Hammer === 'undefined') { - console.error('Hammer.js not found!'); - return this - } - - // convert to array - types = Array.isArray(types) ? types : types.split(/\s/); - if (elements instanceof NodeList || elements instanceof HTMLCollection) { - elements = Array.from(elements); - } - elements = Array.isArray(elements) ? elements : [elements]; - - for (let i = 0; i < types.length; i++) { - - const type = types[i].toLowerCase(); - - // list of hammer events - const useHammer = /^(tap|doubletap|press|pan|swipe|pinch|rotate).*$/.test(type); - - // if it is a hammer event - if (useHammer) { - - for (let j = 0; j < elements.length; j++) { - - // if(elements[j].tagName == "svg") return false; - - let hammer = new Hammer(elements[j], opts); - - if (window.propagating !== 'undefined') { - hammer = propagating(hammer); - } - - // recognizers - if (type.startsWith('pan')) { - hammer.get('pan').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts)); - } else if (type.startsWith('pinch')) { - hammer.get('pinch').set(Object.assign({ enable: true }, opts)); - } else if (type.startsWith('press')) { - hammer.get('press').set(opts); - } else if (type.startsWith('rotate')) { - hammer.get('rotate').set(Object.assign({ enable: true }, opts)); - } else if (type.startsWith('swipe')) { - hammer.get('swipe').set(Object.assign({ direction: Hammer.DIRECTION_ALL }, opts)); - } else if (type.startsWith('tap')) { - hammer.get('tap').set(opts); - } - - hammer.on(type, event => { - cb(event); - }); - } - - } else { - - for (let j = 0; j < elements.length; j++) { - Hammer.on(elements[j], type, event => { - cb(event); - }); - } - } - } - - return this - } - } - - window.InteractionMapper = InteractionMapper$1; - - /** Report capabilities with guaranteed values. - */ - class Capabilities { - - /** Returns the browser userAgent. - @return {string} - */ - static get userAgent() { - return navigator.userAgent || 'Unknown Agent' - } - - /** Tests whether the app is running on a mobile device. - Implemented as a readonly attribute. - @return {boolean} - */ - static get isMobile() { - return (/Mobi/.test(navigator.userAgent)) - } - - /** Tests whether the app is running on a iOS device. - Implemented as a readonly attribute. - @return {boolean} - */ - static get isIOS() { - return (/iPad|iPhone|iPod/.test(navigator.userAgent)) && !window.MSStream - } - - /** Tests whether the app is running in a Safari environment. - See https://stackoverflow.com/questions/7944460/detect-safari-browser - Implemented as a readonly attribute. - @return {boolean} - */ - static get isSafari() { - return navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && navigator.userAgent && !navigator.userAgent.match('CriOS') - } - - /** - * Distincts if the app is running inside electron or not. - * - * source: https://discuss.atom.io/t/detect-electron-or-web-page-running/33180/3 - */ - static get isElectron() { - return typeof process != 'undefined' && process.versions && process.versions.electron !== undefined - } - - /** Returns the display resolution. Necessary for retina displays. - @return {number} - */ - static get devicePixelRatio() { - return window.devicePixelRatio || 1 - } - - /** Returns true if the device is a multi-touch table. This method is currently not universal usable and not sure! - @return {boolean} - */ - static get isMultiTouchTable() { - return Capabilities.devicePixelRatio > 2 && Capabilities.isMobile === false && /Windows/i.test(Capabilities.userAgent) - } - - /** Returns true if mouse events are supported - @return {boolean} - */ - static supportsMouseEvents() { - return typeof(window.MouseEvent) != 'undefined' - } - - /** Returns true if touch events are supported - @return {boolean} - */ - static supportsTouchEvents() { - return typeof(window.TouchEvent) != 'undefined' - } - - /** Returns true if pointer events are supported - @return {boolean} - */ - static supportsPointerEvents() { - return typeof(window.PointerEvent) != 'undefined' - } - - /** Returns true if DOM templates are supported - @return {boolean} - */ - static supportsTemplate() { - return 'content' in document.createElement('template'); - } - } - - /** Basic tests for Capabilities. - */ - class CapabilitiesTests { - - static testConfirm() { - let bool = confirm('Please confirm'); - document.getElementById('demo').innerHTML = (bool) ? 'Confirmed' : 'Not confirmed'; - } - - static testPrompt() { - let person = prompt('Please enter your name', 'Harry Potter'); - if (person != null) { - demo.innerHTML = - 'Hello ' + person + '! How are you today?'; - } - } - - static testUserAgent() { - let agent = 'User-agent: ' + Capabilities.userAgent; - user_agent.innerHTML = agent; - } - - static testDevicePixelRatio() { - let value = 'Device Pixel Ratio: ' + Capabilities.devicePixelRatio; - device_pixel_ratio.innerHTML = value; - } - - static testMultiTouchTable() { - let value = 'Is the device a multi-touch table? ' + Capabilities.isMultiTouchTable; - multi_touch_table.innerHTML = value; - } - - static testSupportedEvents() { - let events = []; - if (Capabilities.supportsMouseEvents()) { - events.push('MouseEvents'); - } - if (Capabilities.supportsTouchEvents()) { - events.push('TouchEvents'); - } - if (Capabilities.supportsPointerEvents()) { - events.push('PointerEvents'); - } - supported_events.innerHTML = 'Supported Events: ' + events.join(', '); - } - - static testAll() { - this.testUserAgent(); - this.testDevicePixelRatio(); - this.testMultiTouchTable(); - this.testSupportedEvents(); - } - } - - /* Optional global variables, needed in DocTests. */ - window.Capabilities = Capabilities; - window.CapabilitiesTests = CapabilitiesTests; - - /** - * A base class for scatter specific events. - * - * @constructor - * @param {name} String - The name of the event - * @param {target} Object - The target of the event - */ - class BaseEvent { - constructor(name, target) { - this.name = name; - this.target = target; - } - } - - // Event types - const START = 'onStart'; - const UPDATE = 'onUpdate'; - const END = 'onEnd'; - - /** - * A scatter event that describes how the scatter has changed. - * - * @constructor - * @param {target} Object - The target scatter of the event - * @param {optional} Object - Optional parameter - */ - class ScatterEvent extends BaseEvent { - constructor( - target, - { - translate = { x: 0, y: 0 }, - scale = null, - rotate = 0, - about = null, - fast = false, - type = null - } = {} - ) { - super('scatterTransformed', { target: target }); - this.translate = translate; - this.scale = scale; - this.rotate = rotate; - this.about = about; - this.fast = fast; - this.type = type; - } - - toString() { - return ( - "Event('scatterTransformed', scale: " + - this.scale + - ' about: ' + - this.about.x + - ', ' + - this.about.y + - ')' - ) - } - } - - /** - * A scatter resize event that describes how the scatter has changed. - * - * @constructor - * @param {target} Object - The target scatter of the event - * @param {optional} Object - Optional parameter - */ - class ResizeEvent extends BaseEvent { - constructor(target, { width = 0, height = 0 } = {}) { - super('scatterResized', { width: width, height: height }); - this.width = width; - this.height = height; - } - - toString() { - return ( - 'Event(scatterResized width: ' + - this.width + - 'height: ' + - this.height + - ')' - ) - } - } - - /** - * A abstract base class that implements the throwable behavior of a scatter - * object. - * - * @constructor - */ - class Throwable { - constructor({ - movableX = true, - movableY = true, - throwVisibility = 44, - throwDamping = 0.95, - autoThrow = true, - onThrowFinished = null - } = {}) { - this.movableX = movableX; - this.movableY = movableY; - this.throwVisibility = throwVisibility; - this.throwDamping = throwDamping; - this.autoThrow = autoThrow; - this.velocities = []; - this.velocity = null; - this.timestamp = null; - this.onThrowFinished = onThrowFinished; - //console.log("onThrowFinished", onThrowFinished) - } - - observeVelocity() { - this.lastframe = performance.now(); - } - - addVelocity(delta, buffer = 5) { - let t = performance.now(); - let dt = t - this.lastframe; - this.lastframe = t; - if (dt > 0) { - // Avoid division by zero errors later on - let velocity = { t: t, dt: dt, dx: delta.x, dy: delta.y }; - this.velocities.push(velocity); - while (this.velocities.length > buffer) { - this.velocities.shift(); - } - } - } - - meanVelocity(milliseconds = 30) { - this.addVelocity({ x: 0, y: 0 }); - let sum = { x: 0, y: 0 }; - let count = 0; - let t = 0; - for (let i = this.velocities.length - 1; i > 0; i--) { - let v = this.velocities[i]; - t += v.dt; - let nv = { x: v.dx / v.dt, y: v.dy / v.dt }; - sum = Points.add(sum, nv); - count += 1; - if (t > milliseconds) { - break - } - } - if (count === 0) return sum // empty vector - return Points.multiplyScalar(sum, 1 / count) - } - - killAnimation() { - this.velocity = null; - this.velocities = []; - } - - startThrow() { - this.velocity = this.meanVelocity(); - if (this.velocity != null) { - // Call next velocity to ansure that specializations - // that use keepOnStage are called - this.velocity = this.nextVelocity(this.velocity); - if (this.autoThrow) this.animateThrow(performance.now()); - } else { - this.onDragComplete(); - } - } - - animateThrow(time) { - if (this.velocity != null) { - let t = performance.now(); - let dt = t - this.lastframe; - this.lastframe = t; - // console.log("animateThrow", dt) - let next = this.nextVelocity(this.velocity); - let prevLength = Points.length(this.velocity); - let nextLength = Points.length(next); - if (nextLength > prevLength) { - let factor = nextLength / prevLength; - next = Points.multiplyScalar(next, 1 / factor); - console.log('Prevent acceleration', factor, this.velocity, next); - } - this.velocity = next; - let d = Points.multiplyScalar(this.velocity, dt); - this._move(d); - - this.onDragUpdate(d); - if (dt == 0 || this.needsAnimation()) { - requestAnimationFrame(this.animateThrow.bind(this)); - return - } else { - if (this.isOutside()) { - requestAnimationFrame(this.animateThrow.bind(this)); - return - } - } - } - this.onDragComplete(); - if (this.onThrowFinished != null) { - this.onThrowFinished(); - } - } - - needsAnimation() { - if (this.velocity == null) { - return false - } - return Points.length(this.velocity) > 0.01 - } - - nextVelocity(velocity) { - // Must be overwritten: computes the changed velocity. Implement - // damping, collison detection, etc. here - let next = Points.multiplyScalar(velocity, this.throwDamping); - return { - x: (this.movableX) ? next.x : 0, - y: (this.movableY) ? next.y : 0 - } - } - - _move(delta) { - // Overwrite if necessary - } - - onDragComplete() { - // Overwrite if necessary - } - - onDragUpdate(delta) { - // Overwrite if necessary - } - } - - class AbstractScatter extends Throwable { - constructor({ - minScale = 0.1, - maxScale = 1.0, - startScale = 1.0, - autoBringToFront = true, - autoThrow = true, - translatable = true, - scalable = true, - rotatable = true, - resizable = false, - movableX = true, - movableY = true, - throwVisibility = 44, - throwDamping = 0.95, - overdoScaling = 1, - mouseZoomFactor = 1.1, - rotationDegrees = null, - rotation = null, - onTransform = null, - interactive = true, - onClose = null, - onThrowFinished = null, - scaleAutoClose = false, - scaleCloseThreshold = 0.10, - scaleCloseBuffer = 0.05 - } = {}) { - if (rotationDegrees != null && rotation != null) { - throw new Error('Use rotationDegrees or rotation but not both') - } else if (rotation != null) { - rotationDegrees = Angle.radian2degree(rotation); - } else if (rotationDegrees == null) { - rotationDegrees = 0; - } - super({ - movableX, - movableY, - throwVisibility, - throwDamping, - autoThrow, - onThrowFinished - }); - - /** - * Closes the card when the minScale is reached and the - * card is released. Card can be saved by scaling it up again. - */ - this.scaleAutoClose = scaleAutoClose; - this.scaleCloseThreshold = scaleCloseThreshold; - this.scaleCloseBuffer = scaleCloseBuffer; - this.scaleAutoCloseTimeout = null; - - this.interactive = interactive; - this.startRotationDegrees = rotationDegrees; - this.startScale = startScale; // Needed to reset object - this.minScale = minScale; - this.maxScale = maxScale; - this.overdoScaling = overdoScaling; - this.translatable = translatable; - if (!translatable) { - this.movableX = false; - this.movableY = false; - } - this.scalable = scalable; - this.rotatable = rotatable; - this.resizable = resizable; - this.mouseZoomFactor = mouseZoomFactor; - this.autoBringToFront = autoBringToFront; - this.dragging = false; - this.onTransform = onTransform != null ? [onTransform] : null; - this.onClose = onClose != null ? [onClose] : null; - } - - addCloseEventCallback(callback) { - if (this.onClose == null) { - this.onClose = []; - } - this.onClose.push(callback); - } - - addTransformEventCallback(callback) { - if (this.onTransform == null) { - this.onTransform = []; - } - this.onTransform.push(callback); - } - - startGesture(interaction) { - this.bringToFront(); - this.killAnimation(); - this.observeVelocity(); - return true - } - - close() { - if (this.onClose) { - this.onClose.forEach(callback => callback(this)); - } - } - - gesture(interaction) { - let delta = interaction.delta(); - //console.log("gesture", delta) - if (delta != null) { - this.addVelocity(delta); - this.transform(delta, delta.zoom, delta.rotate, delta.about); - if (delta.zoom != 1) this.interactionAnchor = delta.about; - } - } - - get polygon() { - let w2 = this.width * this.scale / 2; - let h2 = this.height * this.scale / 2; - let center = this.center; - let polygon = new Polygon(center); - polygon.addPoint({ x: -w2, y: -h2 }); - polygon.addPoint({ x: w2, y: -h2 }); - polygon.addPoint({ x: w2, y: h2 }); - polygon.addPoint({ x: -w2, y: h2 }); - polygon.rotate(this.rotation); - return polygon - } - - isOutside() { - let stagePolygon = this.containerPolygon; - if (stagePolygon == null) - return false - let polygon = this.polygon; - if (polygon == null) - return false - let result = stagePolygon.intersectsWith(polygon); - return result === false || result.overlap < this.throwVisibility - } - - recenter() { - // Return a small vector that guarantees that the scatter is moving - // towards the center of the stage - let center = this.center; - let target = this.container.center; - let delta = Points.subtract(target, center); - return Points.normalize(delta) - } - - nextVelocity(velocity) { - return this.keepOnStage(velocity) - } - - bouncing() { - // Implements the bouncing behavior of the scatter. Moves the scatter - // to the center of the stage if the scatter is outside the stage or - // not within the limits of the throwVisibility. - - let stagePolygon = this.containerPolygon; - let polygon = this.polygon; - let result = stagePolygon.intersectsWith(polygon); - if (result === false || result.overlap < this.throwVisibility) { - let cv = this.recenter(); - let recentered = false; - while (result === false || result.overlap < this.throwVisibility) { - polygon.center.x += cv.x; - polygon.center.y += cv.y; - this._move(cv); - result = stagePolygon.intersectsWith(polygon); - recentered = true; - } - return recentered - } - return false - } - - keepOnStage(velocity, collision = 0.5) { - let stagePolygon = this.containerPolygon; - if (!stagePolygon) return - let polygon = this.polygon; - let bounced = this.bouncing(); - if (bounced) { - let stage = this.containerBounds; - let x = this.center.x; - let y = this.center.y; - let dx = this.movableX ? velocity.x : 0; - let dy = this.movableY ? velocity.y : 0; - let factor = this.throwDamping; - // if (recentered) { - if (x < 0) { - dx = -dx; - factor = collision; - } - if (x > stage.width) { - dx = -dx; - factor = collision; - } - if (y < 0) { - dy = -dy; - factor = collision; - } - if (y > stage.height) { - dy = -dy; - factor = collision; - } - // } - return Points.multiplyScalar({ x: dx, y: dy }, factor) - } - return super.nextVelocity(velocity) - } - - endGesture(interaction) { - this.startThrow(); - this._checkAutoClose(); - } - - _checkAutoClose() { - if (this.scaleAutoClose) - if (this.scale < this.minScale + this.scaleCloseThreshold - this.scaleCloseBuffer) { - this.zoom(this.minScale, { animate: 0.2, onComplete: this.close.bind(this) }); - } else if (this.scale < this.minScale + this.scaleCloseThreshold) { - this.zoom(this.minScale + this.scaleCloseThreshold, { animate: 0.4 }); - } - } - - rotateDegrees(degrees, anchor) { - let rad = Angle.degree2radian(degrees); - this.rotate(rad, anchor); - } - - rotate(rad, anchor) { - this.transform({ x: 0, y: 0 }, 1.0, rad, anchor); - } - - move(d, { animate = 0 } = {}) { - if (this.translatable) { - if (animate > 0) { - let startPos = this.position; - TweenLite.to(this, animate, { - x: '+=' + d.x, - y: '+=' + d.y, - /* scale: scale, uo: not defined, why was this here? */ - onUpdate: e => { - let p = this.position; - let dx = p.x - startPos.x; - let dy = p.x - startPos.y; - this.onMoved(dx, dy); - } - }); - } else { - this._move(d); - this.onMoved(d.x, d.y); - } - } - } - - moveTo(p, { animate = 0 } = {}) { - let c = this.origin; - let delta = Points.subtract(p, c); - this.move(delta, { animate: animate }); - } - - centerAt(p, { animate = 0 } = {}) { - let c = this.center; - let delta = Points.subtract(p, c); - this.move(delta, { animate: animate }); - } - - zoom( - scale, - { - animate = 0, - about = null, - delay = 0, - x = null, - y = null, - onComplete = null - } = {} - ) { - let anchor = about || this.center; - if (scale != this.scale) { - if (animate > 0) { - TweenLite.to(this, animate, { - scale: scale, - delay: delay, - onComplete: onComplete, - onUpdate: this.onZoomed.bind(this) - }); - } else { - this.scale = scale; - this.onZoomed(anchor); - } - } - } - - _move(delta) { - this.x += this.movableX ? delta.x : 0; - this.y += this.movableX ? delta.y : 0; - } - - transform(translate, zoom, rotate, anchor) { - let delta = { - x: this.movableX ? translate.x : 0, - y: this.movableY ? translate.y : 0 - }; - if (this.resizable) var vzoom = zoom; - if (!this.translatable) delta = { x: 0, y: 0 }; - if (!this.rotatable) rotate = 0; - if (!this.scalable) zoom = 1.0; - if (zoom == 1.0 && rotate == 0) { - this._move(delta); - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: delta, - scale: this.scale, - rotate: 0, - about: anchor, - fast: false, - type: UPDATE - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - return - } - let origin = this.rotationOrigin; - let beta = Points.angle(origin, anchor); - let distance = Points.distance(origin, anchor); - let { scale: newScale, zoom: thresholdedZoom } = this.calculateScale(zoom); - - let newOrigin = Points.arc(anchor, beta + rotate, distance * thresholdedZoom); - let extra = Points.subtract(newOrigin, origin); - let offset = Points.subtract(anchor, origin); - this._move(offset); - this.scale = newScale; - this.rotation += rotate; - offset = Points.negate(offset); - offset = Points.add(offset, extra); - offset = Points.add(offset, translate); - this._move(offset); - - delta.x += extra.x; - delta.y += extra.y; - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: delta, - scale: newScale, - rotate: rotate, - about: anchor - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - if (this.resizable) { - this.resizeAfterTransform(vzoom); - } - } - - /** - * For a given zoom, a new scale is calculated, taking - * min and max scale into account. - * - * @param {number} zoom - The zoom factor, to scale the object with. - * @returns {object} - Returns an object containing the a value for a valid scale and the corrected zoom factor. - */ - calculateScale(zoom) { - let scale = this.scale * zoom; - - let minScale = this.minScale / this.overdoScaling; - let maxScale = this.maxScale * this.overdoScaling; - if (scale < minScale) { - scale = minScale; - zoom = scale / this.scale; - } - if (scale > maxScale) { - scale = maxScale; - zoom = scale / this.scale; - } - - if (this.scaleAutoClose) - this._updateTransparency(); - - return { zoom, scale } - } - - _updateTransparency() { - if (this.scale < this.minScale + this.scaleCloseThreshold) { - let transparency = this.calculateScaleTransparency(); - this.element.style.opacity = transparency; - } else this.element.style.opacity = 1; - } - - calculateScaleTransparency() { - let transparency = (this.scale - this.minScale) / this.scaleCloseThreshold; - transparency = (transparency > 1) ? 1 : (transparency < 0) ? 0 : transparency; - return transparency - } - - resizeAfterTransform(zoom) { - // Overwrite this in subclasses. - } - - validScale(scale) { - scale = Math.max(scale, this.minScale); - scale = Math.min(scale, this.maxScale); - return scale - } - - animateZoomBounce(dt = 1) { - if (this.zoomAnchor != null) { - let zoom = 1; - let amount = Math.min(0.01, 0.3 * dt / 100000.0); - if (this.scale < this.minScale) zoom = 1 + amount; - if (this.scale > this.maxScale) zoom = 1 - amount; - if (zoom != 1) { - this.transform({ x: 0, y: 0 }, zoom, 0, this.zoomAnchor); - requestAnimationFrame(dt => { - this.animateZoomBounce(dt); - }); - return - } - this.zoomAnchor = null; - } - } - - checkScaling(about, delay = 0) { - this.zoomAnchor = about; - clearTimeout(this.animateZoomBounce.bind(this)); - setTimeout(this.animateZoomBounce.bind(this), delay); - } - - onMouseWheel(event) { - if (event.claimedByScatter) { - if (event.claimedByScatter != this) return - } - this.killAnimation(); - this.targetScale = null; - let direction = event.detail < 0 || event.wheelDelta > 0; - let globalPoint = { x: event.clientX, y: event.clientY }; - let centerPoint = this.mapPositionToContainerPoint(globalPoint); - if (event.shiftKey) { - let degrees = direction ? 5 : -5; - let rad = Angle.degree2radian(degrees); - return this.transform({ x: 0, y: 0 }, 1.0, rad, centerPoint) - } - const zoomFactor = this.mouseZoomFactor; - let zoom = direction ? zoomFactor : 1 / zoomFactor; - this.transform({ x: 0, y: 0 }, zoom, 0, centerPoint); - this.checkScaling(centerPoint, 200); - - if (this.scaleAutoClose) { - if (this.scale <= this.minScale + this.scaleCloseThreshold) { - - if (this.scaleAutoCloseTimeout) clearTimeout(this.scaleAutoCloseTimeout); - this.scaleAutoCloseTimeout = setTimeout(() => { - this._checkAutoClose(); - }, 600); - } - this._updateTransparency(); - } - // - // if (this.onTransform != null) { - // let event = new ScatterEvent(this, { - // translate: {x: 0, y: 0}, - // scale: this.scale, - // rotate: 0, - // about: null, - // fast: false, - // type: ZOOM - // }) - // this.onTransform.forEach(function(f) { - // f(event) - // }) - // } - } - - onStart(event, interaction) { - - if (this.startGesture(interaction)) { - this.dragging = true; - this.interactionAnchor = null; - } - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: { x: 0, y: 0 }, - scale: this.scale, - rotate: 0, - about: null, - fast: false, - type: START - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onMove(event, interaction) { - /** As long as mouseout && mouseleave interrupt we cannot be sure that - * dragging remains correct. - */ - if (this.dragging) { - this.gesture(interaction); - } - } - - onEnd(event, interaction) { - //console.log("Scatter.onEnd", this.dragging) - if (interaction.isFinished()) { - this.endGesture(interaction); - this.dragging = false; - for (let key of interaction.ended.keys()) { - if (interaction.isTap(key)) { - let point = interaction.ended.get(key); - this.onTap(event, interaction, point); - } - } - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: { x: 0, y: 0 }, - scale: this.scale, - rotate: 0, - about: null, - fast: false, - type: END - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - let about = this.interactionAnchor; - if (about != null) { - this.checkScaling(about, 100); - } - } - - onTap(event, interaction, point) { } - - onDragUpdate(delta) { - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - fast: true, - translate: delta, - scale: this.scale, - about: this.currentAbout, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onDragComplete() { - if (this.onTransform) { - let event = new ScatterEvent(this, { - scale: this.scale, - about: this.currentAbout, - fast: false, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onMoved(dx, dy, about) { - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - translate: { x: dx, y: dy }, - about: about, - fast: true, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onResizing() { - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - scale: this.scale, - fast: false, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - - onZoomed(about) { - - if (this.scaleAutoClose) - this._updateTransparency(); - - if (this.onTransform != null) { - let event = new ScatterEvent(this, { - scale: this.scale, - about: about, - fast: false, - type: null - }); - this.onTransform.forEach(function (f) { - f(event); - }); - } - } - } - - - class DOMScatter extends AbstractScatter { - constructor( - element, - container, - { - startScale = 1.0, - minScale = 0.1, - maxScale = 1.0, - overdoScaling = 1.5, - autoBringToFront = true, - translatable = true, - scalable = true, - rotatable = true, - movableX = true, - movableY = true, - rotationDegrees = null, - rotation = null, - onTransform = null, - transformOrigin = 'center center', - // extras which are in part needed - x = 0, - y = 0, - width = null, // required - height = null, // required - resizable = false, - simulateClick = false, - verbose = true, - onResize = null, - touchAction = 'none', - throwVisibility = 44, - throwDamping = 0.95, - autoThrow = true, - scaleAutoClose = false, - onClose = null, - scaleCloseThreshold = 0.10, - scaleCloseBuffer = 0.05 - } = {} - ) { - super({ - minScale, - maxScale, - startScale, - overdoScaling, - autoBringToFront, - translatable, - scalable, - rotatable, - movableX, - movableY, - resizable, - rotationDegrees, - rotation, - onTransform, - throwVisibility, - throwDamping, - autoThrow, - scaleAutoClose, - scaleCloseThreshold, - scaleCloseBuffer, - onClose - }); - if (container == null || width == null || height == null) { - throw new Error('Invalid value: null') - } - element.scatter = this; - this.element = element; - this.x = x; - this.y = y; - this.oldX = 0; - this.oldY = 0; - this.meanX = x; - this.meanY = y; - this.width = width; - this.height = height; - this.throwVisibility = Math.min(width, height, throwVisibility); - this.container = container; - this.simulateClick = simulateClick; - this.scale = startScale; - this.rotationDegrees = this.startRotationDegrees; - this.transformOrigin = transformOrigin; - this.initialValues = { - x: x, - y: y, - width: width, - height: height, - scale: startScale, - rotation: this.startRotationDegrees, - transformOrigin: transformOrigin - }; - - - // For tweenlite we need initial values in _gsTransform - TweenLite.set(element, this.initialValues); - this.onResize = onResize; - this.verbose = verbose; - if (touchAction !== null) { - Elements$1.setStyle(element, { touchAction }); - } - this.resizeButton = null; - if (resizable) { - let button = document.createElement("div"); - button.style.position = "absolute"; - button.style.right = "0px"; - button.style.bottom = "0px"; - button.style.width = "50px"; - button.style.height = "50px"; - // button.style.borderRadius = "100% 0px 0px 0px"; - // button.style.background = this.element.style.backgroundColor - button.className = "interactiveElement"; - this.element.appendChild(button); - - button.addEventListener('pointerdown', (e) => { - this.startResize(e); - }); - - button.addEventListener('pointermove', (e) => { - this.resize(e); - }); - - button.addEventListener('pointerup', (e) => { - this.stopResize(e); - }); - this.resizeButton = button; - } - container.add(this); - } - - /** Returns geometry data as object. **/ - getState() { - return { - scale: this.scale, - x: this.x, - y: this.y, - rotation: this.rotation - } - } - - close() { - super.close(); - let parent = this.element.parentNode; - if (parent) parent.removeChild(this.element); - } - - get rotationOrigin() { - return this.center - } - - get x() { - return this._x - } - - get y() { - return this._y - } - - set x(value) { - this._x = value; - TweenLite.set(this.element, { x: value }); - } - - set y(value) { - this._y = value; - TweenLite.set(this.element, { y: value }); - } - - get position() { - let transform = this.element._gsTransform; - let x = transform.x; - let y = transform.y; - return { x, y } - } - - get origin() { - let p = this.fromNodeToPage(0, 0); - return Points.fromPageToNode(this.container.element, p) - } - - get bounds() { - let stage = this.container.element.getBoundingClientRect(); - let rect = this.element.getBoundingClientRect(); - return { - top: rect.top - stage.top, - left: rect.left - stage.left, - width: rect.width, - height: rect.height - } - } - - get center() { - let r = this.bounds; - let w2 = r.width / 2; - let h2 = r.height / 2; - // if (this.resizable) { - // w2 *= this.scale - // h2 *= this.scale - // } - var x = r.left + w2; - var y = r.top + h2; - return { x, y } - } - - set rotation(radians) { - let rad = radians; // Angle.normalize(radians) - let degrees = Angle.radian2degree(rad); - TweenLite.set(this.element, { rotation: degrees }); - this._rotation = rad; - } - - set rotationDegrees(degrees) { - let deg = degrees; // Angle.normalizeDegree(degrees) - TweenLite.set(this.element, { rotation: deg }); - this._rotation = Angle.degree2radian(deg); - } - - get rotation() { - return this._rotation - } - - get rotationDegrees() { - return this._rotation - } - - set scale(scale) { - TweenLite.set(this.element, { - scale: scale, - transformOrigin: this.transformOrigin - }); - this._scale = scale; - } - - get scale() { - return this._scale - } - - get containerBounds() { - return this.container.bounds - } - - get containerPolygon() { - return this.container.polygon - } - - mapPositionToContainerPoint(point) { - return this.container.mapPositionToPoint(point) - } - - capture(event) { - return true - } - - reset() { - TweenLite.set(this.element, this.initialValues); - } - - hide() { - TweenLite.to(this.element, 0.1, { - display: 'none', - onComplete: e => { - this.element.parentNode.removeChild(this.element); - } - }); - } - - show() { - TweenLite.set(this.element, { display: 'block' }); - } - - showAt(p, rotationDegrees) { - TweenLite.set(this.element, { - display: 'block', - x: p.x, - y: p.y, - rotation: rotationDegrees, - transformOrigin: this.transformOrigin - }); - } - - bringToFront() { - // this.element.parentNode.appendChild(this.element) - // uo: On Chome and Electon appendChild leads to flicker - TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ }); - } - - toggleVideo(element) { - if (element.paused) { - element.play(); - } else { - element.pause(); - } - } - - onTap(event, interaction, point) { - if (this.simulateClick) { - let p = Points.fromPageToNode(this.element, point); - let iframe = this.element.querySelector('iframe'); - if (iframe) { - let doc = iframe.contentWindow.document; - let element = doc.elementFromPoint(p.x, p.y); - if (element == null) { - return - } - switch (element.tagName) { - case 'VIDEO': - console.log(element.currentSrc); - if (PopupMenu) { - PopupMenu.open( - { - Fullscreen: () => - window.open(element.currentSrc), - Play: () => element.play() - }, - { x, y } - ); - } else { - this.toggleVideo(element); - } - break - default: - element.click(); - } - } - } - } - - isDescendant(parent, child) { - let node = child.parentNode; - while (node != null) { - if (node == parent) { - return true - } - node = node.parentNode; - } - return false - } - - fromPageToNode(x, y) { - return Points.fromPageToNode(this.element, { x, y }) - } - - fromNodeToPage(x, y) { - return Points.fromNodeToPage(this.element, { x, y }) - } - - _move(delta) { - // UO: We need to keep TweenLite's _gsTransform and the private - // _x and _y attributes aligned - let x = this.element._gsTransform.x; - let y = this.element._gsTransform.y; - if (this.movableX) { - x += delta.x; - } - if (this.movableY) { - y += delta.y; - } - this._x = x; - this._y = y; - TweenLite.set(this.element, { x: x, y: y }); - } - - resizeAfterTransform(zoom) { - // let w = this.width * this.scale - // let h = this.height * this.scale - // TweenLite.set(this.element, { width: w, height: h }) - if (this.onResize) { - let event = new ResizeEvent(this, { width: w, height: h }); - this.onResize(event); - } - if (this.resizeButton != null) ; - } - - startResize(e) { - e.preventDefault(); - let event = new CustomEvent('resizeStarted'); - - let oldPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; - this.bringToFront(); - - this.element.style.transformOrigin = "0% 0%"; - - let newPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; - - let offset = Points.subtract(oldPostition, newPostition); - - this.oldX = e.clientX; - this.oldY = e.clientY; - - e.target.setAttribute('resizing', "true"); - e.target.setPointerCapture(e.pointerId); - - TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } }); - TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } }); - - this.element.dispatchEvent(event); - } - - resize(e) { - e.preventDefault(); - - let rotation = Angle.radian2degree(this.rotation); - rotation = (rotation + 360) % 360; - let event = new CustomEvent('resized'); - if (e.target.getAttribute('resizing') == "true") { - - let deltaX = (e.clientX - this.oldX); - let deltaY = (e.clientY - this.oldY); - - let r = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); - let phi = Angle.radian2degree(Math.atan2(deltaX, deltaY)); - - phi = ((phi) + 630) % 360; - let rot = ((rotation + 90) + 630) % 360; - - let diffAngle = ((0 + rot) + 360) % 360; - let phiCorrected = (phi + diffAngle + 360) % 360; - - let resizeW = r * Math.cos(Angle.degree2radian(phiCorrected)); - let resizeH = -r * Math.sin(Angle.degree2radian(phiCorrected)); - - if (this.element.offsetWidth + resizeW / this.scale > this.width * 0.3 && this.element.offsetHeight + resizeH / this.scale > this.height * 0.3) TweenLite.to(this.element, 0, { width: this.element.offsetWidth + resizeW / this.scale, height: this.element.offsetHeight + resizeH / this.scale }); - - this.oldX = e.clientX; - this.oldY = e.clientY; - this.onResizing(); - - this.element.dispatchEvent(event); - } - } - - stopResize(e) { - e.preventDefault(); - - let event = new CustomEvent('resizeEnded'); - let oldPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; - this.element.style.transformOrigin = "50% 50%"; - let newPostition = { x: $(this.element).position().left, y: $(this.element).position().top }; - let offset = Points.subtract(oldPostition, newPostition); - - TweenLite.to(this.element, 0, { css: { left: "+=" + offset.x + "px" } }); - TweenLite.to(this.element, 0, { css: { top: "+=" + offset.y + "px" } }); - - e.target.setAttribute('resizing', "false"); - - this.element.dispatchEvent(event); - } - } - - DOMScatter.zIndex = 1000; - - class CardLoader { - constructor( - src, - { - x = 0, - y = 0, - width = 1000, - height = 800, - maxWidth = null, - maxHeight = null, - scale = 1, - minScale = 0.5, - maxScale = 1.5, - rotation = 0 - } = {} - ) { - this.src = src; - this.x = x; - this.y = y; - this.scale = scale; - this.rotation = 0; - this.maxScale = maxScale; - this.minScale = minScale; - this.wantedWidth = width; - this.wantedHeight = height; - this.maxWidth = maxWidth != null ? maxWidth : window.innerWidth; - this.maxHeight = maxHeight != null ? maxHeight : window.innerHeight; - this.addedNode = null; - console.log({ - - width, - height, - maxWidth, - maxHeight, - - }); - } - - unload() { - if (this.addedNode) { - this.addedNode.remove(); - this.addedNode = null; - } - } - } - - class DOMFlip { - constructor( - domScatterContainer, - flipTemplate, - frontLoader, - backLoader, - { - closeOnMinScale = false, - flipDuration = 1, - fadeDuration = 0.2, - overdoScaling = 1, - autoLoad = false, - center = null, - preloadBack = false, - translatable = true, - scalable = true, - rotatable = true, - onFront = null, - onBack = null, - onClose = null, - onUpdate = null, - onRemoved = null, - onLoaded = null - } = {} - ) { - this.domScatterContainer = domScatterContainer; - this.id = getId(); - this.flipDuration = flipDuration; - this.fadeDuration = fadeDuration; - this.closeOnMinScale = closeOnMinScale; - this.flipTemplate = flipTemplate; - this.frontLoader = frontLoader; - this.backLoader = backLoader; - this.translatable = translatable; - this.scalable = scalable; - this.rotatable = rotatable; - this.onFrontFlipped = onFront; - this.onBackFlipped = onBack; - this.onClose = onClose; - this.onRemoved = onRemoved; - this.onUpdate = onUpdate; - this.onLoaded = onLoaded; - this.center = center; - this.preloadBack = preloadBack; - this.overdoScaling = overdoScaling; - if (autoLoad) { - this.load(); - } - } - - load() { - return new Promise((resolve, reject) => { - let t = this.flipTemplate; - let dom = this.domScatterContainer.element; - let wrapper = t.content.querySelector('.flipWrapper'); - wrapper.id = this.id; - let clone = document.importNode(t.content, true); - dom.appendChild(clone); - // We cannot use the document fragment itself because it - // is not part of the main dom tree. After the appendChild - // call we can access the new dom element by id - this.cardWrapper = dom.querySelector('#' + this.id); - let front = this.cardWrapper.querySelector('.front'); - this.frontLoader.load(front).then(loader => { - this.frontLoaded(loader).then((obj) => { - if (this.onLoaded) this.onLoaded(); - resolve(this); - }); - }); - }) - } - - frontLoaded(loader) { - return new Promise((resolve, reject) => { - let scatter = new DOMScatter( - this.cardWrapper, - this.domScatterContainer, - { - x: loader.x, - y: loader.y, - startScale: loader.scale, - scale: loader.scale, - maxScale: loader.maxScale, - minScale: loader.minScale, - width: loader.wantedWidth, - height: loader.wantedHeight, - rotation: loader.rotation, - translatable: this.translatable, - scalable: this.scalable, - rotatable: this.rotatable, - overdoScaling: this.overdoScaling - } - ); - - if (this.center) { - scatter.centerAt(this.center); - } - - if (this.closeOnMinScale) { - - const removeOnMinScale = function () { - if (scatter.scale <= scatter.minScale) { - this.flippable.close(); - - // 'Disable' overdoscaling to avoid weird jumps on close. - scatter.minScale /= scatter.overdoScaling; - scatter.overdoScaling = 1; - - //Remove callback - if (scatter.onTransform) { - let callbackIdx = scatter.onTransform.indexOf(removeOnMinScale); - scatter.onTransform.splice(callbackIdx, 1); - } - } - - }.bind(this); - - - - scatter.addTransformEventCallback(removeOnMinScale); - } - - let flippable = new DOMFlippable(this.cardWrapper, scatter, this); - let back = this.cardWrapper.querySelector('.back'); - - if (this.preloadBack) { - this.backLoader.load(back).then(loader => { - this.setupFlippable(flippable, loader); - }); - } - this.flippable = flippable; - resolve(this); - }) - } - - centerAt(p) { - this.center = p; - this.flippable.centerAt(p); - } - - zoom(scale) { - this.flippable.zoom(scale); - } - - setupFlippable(flippable, loader) { - flippable.wantedWidth = loader.wantedWidth; - flippable.wantedHeight = loader.wantedHeight; - flippable.wantedScale = loader.scale; - flippable.minScale = loader.minScale; - flippable.maxScale = loader.maxScale; - flippable.scaleButtons(); - } - - start({ targetCenter = null } = {}) { - console.log('DOMFlip.start', targetCenter); - if (this.preloadBack) this.flippable.start({ duration: this.flipDuration, targetCenter }); - else { - let back = this.cardWrapper.querySelector('.back'); - let flippable = this.flippable; - this.backLoader.load(back).then(loader => { - this.setupFlippable(flippable, loader); - flippable.start({ duration: this.flipDuration, targetCenter }); - }); - } - } - - fadeOutAndRemove() { - TweenLite.to(this.cardWrapper, this.fadeDuration, { - opacity: 0, - onComplete: () => { - this.cardWrapper.remove(); - } - }); - } - - closed() { - this.unload(); - } - - unload() { - if (!this.preloadBack) { - this.backLoader.unload(); - } - } - } - - class DOMFlippable { - constructor(element, scatter, flip) { - // Set log to console.log or a custom log function - // define data structures to store our touchpoints in - - this.element = element; - this.flip = flip; - this.card = element.querySelector('.flipCard'); - this.front = element.querySelector('.front'); - this.back = element.querySelector('.back'); - this.flipped = false; - this.scatter = scatter; - this.onFrontFlipped = flip.onFrontFlipped; - this.onBackFlipped = flip.onBackFlipped; - this.onClose = flip.onClose; - this.onRemoved = flip.onRemoved; - this.onUpdate = flip.onUpdate; - - this.flipDuration = flip.flipDuration; - this.fadeDuration = flip.fadeDuration; - scatter.addTransformEventCallback(this.scatterTransformed.bind(this)); - console.log('lib.DOMFlippable', 5000); - TweenLite.set(this.element, { perspective: 5000 }); - TweenLite.set(this.card, { transformStyle: 'preserve-3d' }); - TweenLite.set(this.back, { rotationY: -180 }); - TweenLite.set([this.back, this.front], { - backfaceVisibility: 'hidden', - perspective: 5000 - }); - TweenLite.set(this.front, { visibility: 'visible' }); - this.infoBtn = element.querySelector('.infoBtn'); - this.backBtn = element.querySelector('.backBtn'); - this.closeBtn = element.querySelector('.closeBtn'); - /* Buttons are not guaranteed to exist. */ - if (this.infoBtn) { - InteractionMapper$1.on('tap', this.infoBtn, event => this.flip.start()); - - this.enable(this.infoBtn); - } - if (this.backBtn) { - InteractionMapper$1.on('tap', this.backBtn, event => this.start()); - } - if (this.closeBtn) { - InteractionMapper$1.on('tap', this.closeBtn, event => this.close()); - this.enable(this.closeBtn); - } - this.scaleButtons(); - this.bringToFront(); - } - - close() { - this.disable(this.infoBtn); - this.disable(this.closeBtn); - if (this.onClose) { - this.onClose(this); - this.flip.closed(); - } else { - this.scatter.zoom(0.1, { - animate: this.fadeDuration, - onComplete: () => { - this.element.remove(); - this.flip.closed(); - if (this.onRemoved) { - this.onRemoved.call(this); - } - } - }); - } - } - - showFront() { - TweenLite.set(this.front, { visibility: 'visible' }); - } - - centerAt(p) { - this.scatter.centerAt(p); - } - - zoom(scale) { - this.scatter.zoom(scale); - } - - get buttonScale() { - let iscale = 1.0; - - if (this.scatter != null) { - let scale = this.scatter.scale || 1; - iscale = 1.0 / scale; - } - return iscale - } - - scaleButtons() { - //This also works for svgs. - // if (this.infoBtn) - // this.infoBtn.style.transform = "scale(" + this.buttonScale + ")" - - // if (this.backBtn) - // this.backBtn.style.transform = "scale(" + this.buttonScale + ")" - - // if (this.closeBtn) - // this.closeBtn.style.transform = "scale(" + this.buttonScale + ")" - - console.log(this.buttonScale); - //// This did not work with svgs! - TweenLite.set([this.infoBtn, this.backBtn, this.closeBtn], { - scale: this.buttonScale - }); - } - - bringToFront() { - this.scatter.bringToFront(); - TweenLite.set(this.element, { zIndex: DOMScatter.zIndex++ }); - } - - clickInfo() { - this.bringToFront(); - this.infoBtn.click(); - } - - scatterTransformed(event) { - this.scaleButtons(); - } - - targetRotation(alpha) { - let ortho = 90; - let rest = alpha % ortho; - let delta = 0.0; - if (rest > ortho / 2.0) { - delta = ortho - rest; - } else { - delta = -rest; - } - return delta - } - - infoValues(info) { - let startX = this.element._gsTransform.x; - let startY = this.element._gsTransform.y; - let startAngle = this.element._gsTransform.rotation; - let startScale = this.element._gsTransform.scaleX; - let w = this.element.style.width; - let h = this.element.style.height; - console.log(info, startX, startY, startAngle, startScale, w, h); - } - - show(element, duration = 0, alpha = 1) { - if (element) { - TweenLite.to(element, duration, { autoAlpha: alpha }); // visibility: 'visible', display: 'initial'}) - } - } - - hide(element, duration = 0, alpha = 0) { - if (element) { - TweenLite.to(element, duration, { autoAlpha: alpha }); // {visibility: 'hidden', display: 'none'}) - } - } - - - - enable(button) { - this.show(button, this.fadeDuration); - if (button) { - TweenLite.set(button, { pointerEvents: 'auto' }); - } - } - - disable(button) { - this.hide(button, this.fadeDuration); - if (button) { - TweenLite.set(button, { pointerEvents: 'none' }); - } - } - - start({ targetCenter = null } = {}) { - this.bringToFront(); - if (!this.flipped) { - this.startX = this.element._gsTransform.x; - this.startY = this.element._gsTransform.y; - this.startAngle = this.element._gsTransform.rotation; - this.startScale = this.element._gsTransform.scaleX; - this.startWidth = this.element.style.width; - this.startHeight = this.element.style.height; - this.scatterStartWidth = this.scatter.width; - this.scatterStartHeight = this.scatter.height; - this.show(this.back); - this.disable(this.infoBtn); - this.disable(this.closeBtn); - } else { - this.show(this.front, this.fadeDuration); - this.disable(this.backBtn); - } - let { scalable, translatable, rotatable } = this.scatter; - this.saved = { scalable, translatable, rotatable }; - this.scatter.scalable = false; - this.scatter.translatable = false; - this.scatter.rotatable = false; - this.scatter.killAnimation(); - - this.flipped = !this.flipped; - let targetY = this.flipped ? 180 : 0; - let targetZ = this.flipped - ? this.startAngle + this.targetRotation(this.startAngle) - : this.startAngle; - let targetScale = this.flipped ? this.wantedScale : this.startScale; - let w = this.flipped ? this.wantedWidth : this.startWidth; - let h = this.flipped ? this.wantedHeight : this.startHeight; - let dw = this.wantedWidth - this.scatter.width; - let dh = this.wantedHeight - this.scatter.height; - let tc = targetCenter; - let xx = tc != null ? tc.x - w / 2 : this.startX - dw / 2; - let yy = tc != null ? tc.y - h / 2 : this.startY - dh / 2; - let x = this.flipped ? xx : this.startX; - let y = this.flipped ? yy : this.startY; - - console.log("DOMFlippable.start", this.flipped, targetCenter, x, y, this.saved); - let onUpdate = this.onUpdate !== null ? () => this.onUpdate(this) : null; - console.log(this.flipDuration); - TweenLite.to(this.card, this.flipDuration, { - rotationY: targetY, - ease: Power1.easeOut, - transformOrigin: '50% 50%', - onUpdate, - onComplete: e => { - if (this.flipped) { - //this.hide(this.front) - this.enable(this.backBtn); - this.show(this.backBtn); - - if (this.onFrontFlipped) { - this.onFrontFlipped(this); - } - } else { - - if (this.onBackFlipped == null) { - this.enable(this.infoBtn, this.fadeDuration); - this.enable(this.closeBtn, this.fadeDuration); - } else { - this.onBackFlipped(this); - } - this.flip.unload(); - } - this.scatter.scale = targetScale; - this.scaleButtons(); - this.scatter.rotationDegrees = targetZ; - this.scatter.width = this.flipped ? w : this.scatterStartWidth; - this.scatter.height = this.flipped ? h : this.scatterStartHeight; - - let { scalable, translatable, rotatable } = this.saved; - this.scatter.scalable = scalable; - this.scatter.translatable = translatable; - this.scatter.rotatable = rotatable; - }, - force3D: true - }); - - // See https://greensock.com/forums/topic/7997-rotate-the-shortest-way/ - TweenLite.to(this.element, this.flipDuration / 2, { - scale: targetScale, - ease: Power1.easeOut, - rotationZ: targetZ + '_short', - transformOrigin: '50% 50%', - width: w, - height: h, - x: x, - y: y, - onComplete: e => { - if (this.flipped) { - this.hide(this.front); - // this.hide(this.infoBtn) - } else { - this.hide(this.back); - // this.show(this.infoBtn) - } - } - }); - } - } - - const deepZoomTileCache = new Map(); - - - /** The current Tile implementation simply uses PIXI.Sprites. - * - * BTW: PIXI.extras.TilingSprite is not appropriate. It should be used for - * repeating patterns. - **/ - class Tile extends PIXI.Sprite { - constructor(texture, url) { - super(texture); - this.url = url; - this.register(url); - } - - static fromImage(imageId, crossorigin, scaleMode) { - return new Tile(PIXI.Texture.fromImage(imageId, crossorigin, scaleMode), imageId) - } - - /** - * Registers the tile in the global reference counter for textures - * - * @param {*} url - * @param {boolean} [debug=false] - * @memberof Tile - */ - register(url, debug = false) { - if (deepZoomTileCache.has(url)) { - let tiles = deepZoomTileCache.get(url); - tiles.add(this); - if (debug) console.log("Tile.register", url, tiles.size); - } - else { - deepZoomTileCache.set(url, new Set([this])); - if (debug) console.log("Tile.register", url, 1); - } - } - - /** - * Unregisters the rile in the global reference counter for textures - * - * @returns {number} The number of how often a texture is used. - * @memberof Tile - */ - unregister() { - let tiles = deepZoomTileCache.get(this.url); - tiles.delete(this); - if (tiles.size == 0) { - deepZoomTileCache.delete(this.url); - } - return tiles.size - } - - /** - * Destroys this sprite and optionally its texture and children - * - * @param {*} options Part of the PIXI API, but ignored in the implementation - * @memberof Tile - */ - destroy(options, debug = false) { - if (this.parent != null) ; - let count = this.unregister(); - if (count <= 0) { - let opts = { children: true, texture: true, baseTexture: true }; - super.destroy(opts); - if (debug) console.log("Tile.destroy", deepZoomTileCache.size, opts); - } - else { - let opts = { children: true, texture: false, baseTexture: false }; - if (debug) console.log("Tile.destroy", deepZoomTileCache.size, opts); - super.destroy(opts); - } - } - } - - /** - * A Tile Loader component that can be plugged into a Tiles Layer. - */ - class TileLoader { - constructor(tiles) { - this.debug = false; - this.tiles = tiles; - this.setup(); - } - - /** Setup collections and instance vars. */ - setup() { - this.map = new Map(); // Map {url : [ col, row]} - this.loading = new Set(); // Set url - this.loaded = new Map(); // Map {url : sprite } - this.loadQueue = []; - } - - /** Schedules a tile url for loading. The loading itself must be triggered - by a call to loadOneTile or loadAll - - * @param {String} url - the url of the texture / tile - * @param {Number} col - the tile col - * @param {Number} row - the tile row - **/ - schedule(url, col, row) { - if (this.loaded.has(url)) return false - if (this.loading.has(url)) return false - this.map.set(url, [col, row]); - this.loading.add(url); - this.loadQueue.push(url); - return true - } - - unschedule(url) { - if (this.loaded.has(url)) this.loaded.delete(url); - if (this.loading.has(url)) this.loading.delete(url); - this.loadQueue = this.loadQueue.filter(item => item != url); - } - - /** Cancels loading by clearing the load queue **/ - cancel() { - this.loadQueue = []; - this.loading.clear(); - } - - /** Destroys alls collections. **/ - destroy() { - this.setup(); - } - - /** Private method. Informs the tile layer about a texture for a given url. - * Creates the sprite for the loaded texture and informs the tile layer. - * @param {String} url - the url - * @param {Object} texture - the loaded resource - **/ - _textureAvailable(url, col, row, texture) { - let tile = this.loaded.get(url); - if (tile != null) { - console.warn("Tile already loaded"); - tile.unregister(); - } - tile = new Tile(texture, url); - this.loaded.set(url, tile); - this.tiles.tileAvailable(tile, col, row, url); - } - } - - /** - * Uses the PIXI Loader but can be replaced with othe loaders implementing - * the public methods without underscore. - * Calls the Tiles.tileAvailable method if the texture is available. - **/ - class PIXITileLoader extends TileLoader { - - constructor(tiles, compression) { - super(tiles); - this.destroyed = false; - this.loader = new PIXI.loaders.Loader(); - this.loader.on('load', this._onLoaded.bind(this)); - this.loader.on('error', this._onError.bind(this)); - if (compression) { - this.loader.pre(PIXI.compressedTextures.imageParser()); - } - } - - schedule(url, col, row) { - // Overwritten schedule to avoid BaseTexture and Texture already loaded errors. - if (this.loaded.has(url)) return false - if (this.loading.has(url)) return false - - if (deepZoomTileCache.has(url)) { - let tiles = deepZoomTileCache.get(url); - for (let tile of tiles.values()) { - //console.log("Reusing cached texture", tile.parent) - let texture = tile.texture; - this._textureAvailable(url, col, row, texture); - return false - } - } - - let texture = PIXI.utils.TextureCache[url]; - if (texture) { - if (this.debug) console.log('Texture already loaded', texture); - this._textureAvailable(url, col, row, texture); - return false - } - let base = PIXI.utils.BaseTextureCache[url]; - if (base) { - if (this.debug) console.log('BaseTexture already loaded', base); - let texture = new PIXI.Texture(base); - this._textureAvailable(url, col, row, texture); - return false - } - return super.schedule(url, col, row) - } - - /** Load one and only one of the scheduled tiles **/ - loadOneTile() { - if (this.destroyed) - return - this._loadOneTile(); - } - - /** Load all scheduled tiles **/ - loadAll() { - if (this.destroyed) - return - this._loadAllTiles(); - } - - /** Destroys the loader completly **/ - destroy() { - this.destroyed = true; - super.destroy(); - try { - this.loader.reset(); - } catch (error) { - console.warn("Error while resetting loader", error); - } - } - - _onError(loader, error) { - console.warn('Cannot load', error); - } - - /** Private method. Called by the PIXI loader after each successfull - * loading of a single tile. - * Creates the sprite for the loaded texture and informs the tile layer. - * @param {Object} loader - the loader instance - * @param {Object} resource - the loaded resource with url and texture attr - **/ - _onLoaded(loader, resource) { - if (this.destroyed) { - let texture = resource.texture; - let destroyBase = !deepZoomTileCache.has(resource.url); - texture.destroy(destroyBase); - console.warn("Received resource after destroy", texture); - return - } - try { - let [col, row] = this.map.get(resource.url); - this._textureAvailable(resource.url, col, row, resource.texture); - } - catch (err) { - console.warn("Texture unavailable: " + err.message); - } - } - - /** Private method: loads one tile from the queue. **/ - _loadOneTile(retry = 1) { - //console.log("_loadOneTile") - if (this.destroyed) { - //console.warn("_loadOneTile after destroy") - return - } - if (this.loader.loading) { - setTimeout(() => { - this._loadOneTile(); - }, retry); - return - } - if (this.loadQueue.length > 0) { - let url = this.loadQueue.pop(); - this.loader.add(url, url); - this.loader.load(); - } - } - - /** Private method: loads all tiles from the queue in batches. Batches are - helpfull to avoid loading tiles that are no longer needed because the - user has already zoomed to a different level.**/ - _loadAllTiles(batchSize = 8, retry = 16) { - if (this.destroyed) { - return - } - if (this.loadQueue.length > 0) { - if (this.loader.loading) { - //console.log("Loader busy", this.loadQueue.length) - setTimeout(() => { - this._loadAllTiles(); - }, retry); - return - } - let i = 0; - let urls = []; - while (i < batchSize && this.loadQueue.length > 0) { - let url = this.loadQueue.pop(); - if (!this.loaded.has(url)) { - let resource = this.loader.resources[url]; - if (resource) { - console.log("Resource already added", url); - } - else { - urls.push(url); - i += 1; - } - } - } - this.loader.add(urls).load(() => { - this._loadAllTiles(); - }); - } - } - } - - - /** - * Uses Workers but can be replaced with other loaders implementing - * the public methods without underscore. - * Calls the Tiles.tileAvailable method if the texture is available. - **/ - class WorkerTileLoader extends TileLoader { - - constructor(tiles) { - super(tiles); - let worker = this.worker = new Worker("../../lib/pixi/deepzoom/tileloader.js"); - worker.onmessage = (event) => { - if (event.data.success) { - let { url, col, row, buffer } = event.data; - //console.log("WorkerTileLoader.loaded", url, buffer) - let CompressedImage = PIXI.compressedTextures.CompressedImage; - let compressed = CompressedImage.loadFromArrayBuffer(buffer, url); - let base = new PIXI.BaseTexture(compressed); - let texture = new PIXI.Texture(base); - this._textureAvailable(url, col, row, texture); - } - }; - } - - loadOne() { - if (this.loadQueue.length > 0) { - let url = this.loadQueue.pop(); - let [col, row] = this.map.get(url); - let tile = [col, row, url]; - this.worker.postMessage({ command: "load", tiles: [tile] }); - } - } - - loadAll() { - let tiles = []; - while (this.loadQueue.length > 0) { - let url = this.loadQueue.pop(); - let [col, row] = this.map.get(url); - tiles.push([col, row, url]); - } - this.worker.postMessage({ command: "load", tiles }); - } - - cancel() { - super.cancel(); - this.worker.postMessage({ command: "abort" }); - } - - destroy() { - this.worker.postMessage({ command: "abort" }); - this.worker.terminate(); - this.worker = null; - super.destroy(); - } - } - - /** - * A layer of tiles that represents a zoom level of a DeepZoomImage as a grid - * of sprites. - * @constructor - * @param {number} level - the zoom level of the tile layer - * @param {DeepZoomImage} view - the zoomable image the layer belongs to - * @param {number} scale - the scale of the tile layer - * @param {number} cols - the number of columns of the layer - * @param {number} rows - the number of rows of the layer - * @param {number} width - the width of the layer in pixel - * @param {number} height - the height of the layer in pixel - * @param {number} tileSize - the size of a single tile in pixel - * @param {number} overlap - the overlap of the tiles in pixel - * @param {number} fadeInTime - time needed to fade in tiles if TweenLite is set - **/ - class Tiles extends PIXI.Container { - constructor( - level, - view, - scale, - cols, - rows, - width, - height, - tileSize, - overlap, - fadeInTime = 0.33 - ) { - super(); - this.debug = false; - this.showGrid = false; - this.view = view; - this.level = level; - this.cols = cols; - this.rows = rows; - this.pixelWidth = width; - this.pixelHeight = height; - this.tileSize = tileSize; - this.overlap = overlap; - this.needed = new Map(); // url as key, [col, row] as value - this.requested = new Set(); - this.available = new Map(); - this.scale.set(scale, scale); - this.tileScale = scale; - this.fadeInTime = fadeInTime; - this.keep = false; - if (this.view.preferWorker && view.info.compression.length > 0) - this.loader = new WorkerTileLoader(this); - else - this.loader = new PIXITileLoader(this, view.info.compression); - this.interactive = false; - this._highlight = null; - - this._info = null; - - this._centerPoint = null; - this._boundsRect = null; - - this.infoColor = Colors.random(); - this.pprint(); - this.destroyed = false; - } - - - - /** Tests whether all tiles are loaded. **/ - isComplete() { - return this.cols * this.rows === this.children.length - } - - /** Returns the highligh graphics layer for debugging purposes. - **/ - get highlight() { - if (this._highlight == null) { - let graphics = new PIXI.Graphics(); - graphics.beginFill(0xffff00, 0.1); - graphics.lineStyle(2, 0xffff00); - graphics.drawRect(1, 1, this.tileSize - 2, this.tileSize - 2); - graphics.endFill(); - graphics.interactive = false; - this._highlight = graphics; - } - return this._highlight - } - - /** Returns the highligh graphics layer for debugging purposes. - **/ - get info() { - if (this._info == null) { - let graphics = new PIXI.Graphics(); - graphics.lineStyle(4, 0xff0000); - graphics.interactive = false; - this._info = graphics; - this.addChild(this._info); - } - return this._info - } - - /** Helper method pretty printing debug information. **/ - pprint() { - if (this.debug) - console.log( - 'Tiles level: ' + - this.level + - ' scale: ' + - this.scale.x + - ' cols: ' + - this.cols + - ' rows: ' + - this.rows + - ' w: ' + - this.pixelWidth + - ' h: ' + - this.pixelHeight + - ' tsize:' + - this.tileSize - ); - } - - /** Computes the tile position and obeys the overlap. - * @param {number} col - The column of the tile - * @param {number} row - The row of the tile - * @returns {PIXI.Point} obj - **/ - tilePosition(col, row) { - let x = col * this.tileSize; - let y = row * this.tileSize; - let overlap = this.overlap; - if (col != 0) { - x -= overlap; - } - if (row != 0) { - y -= overlap; - } - return new PIXI.Point(x, y) - } - - /** Computes the tile size without overlap - * @param {number} col - The column of the tile - * @param {number} row - The row of the tile - * @returns {PIXI.Point} obj - **/ - tileDimensions(col, row) { - let w = this.tileSize; - let h = this.tileSize; - let pos = this.tilePosition(col, row); - if (col == this.cols - 1) { - w = this.pixelWidth - pos.x; - } - if (row == this.rows - 1) { - h = this.pixelHeight - pos.y; - } - return new PIXI.Point(w, h) - } - - /** Method to support debugging. Highlights the specified tile at col, row **/ - highlightTile(col, row) { - if (col > -1 && row > -1 && col < this.cols && row < this.rows) { - let graphics = this.highlight; - let dim = this.tileDimensions(col, row); - graphics.position = this.tilePosition(col, row); - graphics.clear(); - graphics.beginFill(0xff00ff, 0.1); - graphics.lineStyle(2, 0xffff00); - graphics.drawRect(1, 1, dim.x - 2, dim.y - 2); - graphics.endFill(); - this.addChild(this.highlight); - } else { - this.removeChild(this.highlight); - } - } - - /** Loads the tiles for the given urls and adds the tiles as sprites. - * @param {array} urlpos - An array of URL, pos pairs - * @param {boolean} onlyone - Loads only on tile at a time if true - **/ - loadTiles(urlpos, onlyone, refCol, refRow) { - if (this.showGrid) { - this.highlightTile(refCol, refRow); - } - urlpos.forEach(d => { - let [url, col, row] = d; - if (this.loader.schedule(url, col, row)) { - if (onlyone) { - return this.loader.loadOneTile() - } - } - }); - this.loader.loadAll(); - } - - /** Private method: add a red border to a tile for debugging purposes. **/ - _addTileBorder(tile, col, row) { - let dim = this.tileDimensions(col, row); - let graphics = new PIXI.Graphics(); - graphics.beginFill(0, 0); - graphics.lineStyle(2, 0xff0000); - graphics.drawRect(1, 1, dim.x - 2, dim.y - 2); - graphics.endFill(); - tile.addChild(graphics); - } - - /** Adds a tile. **/ - addTile(tile, col, row, url) { - if (this.available.has(url)) { - console.warn('Trying to add available tile'); - return - } - this.addChildAt(tile, 0); - this.available.set(url, tile); - if (this.destroyed) { - console.warn('Adding to destroyed tiles layer'); - } - // this._addTileBorder(tile, col, row) - } - - /** Called by the loader after each successfull loading of a single tile. - * Adds the sprite to the tile layer. - * @param {Object} tile - the loaded tile sprite - * @param {Number} col - the col position - * @param {Number} row - the rowposition - **/ - tileAvailable(tile, col, row, url) { - let pos = this.tilePosition(col, row); - if (this.showGrid) { - this._addTileBorder(tile, col, row); - } - tile.position = pos; - tile.interactive = false; - if (TweenLite) { - tile.alpha = 0; - TweenLite.to(tile, this.fadeInTime, { alpha: this.alpha }); - } - this.addTile(tile, col, row, url); - } - - /** Destroys the tiles layer and destroys the loader. Async load calls are - * cancelled. - **/ - destroy() { - this.destroyed = true; - this.loader.destroy(); - super.destroy({ children: true }); // Calls destroyChildren - this.available.clear(); - this.requested.clear(); - this.needed.clear(); - } - - destroyTile(url, tile) { - this.loader.unschedule(url); - this.removeChild(tile); - tile.destroy(); - this.available.delete(url); - } - - destroyTileByUrl(url) { - if (this.available.has(url)) { - let tile = this.available.get(url); - this.destroyTile(url, tile); - } - } - - /* Destroys the tiles which are not with the bounds of the app to free - * memory. - **/ - destroyTiles(quadTrees) { - let count = 0; - for (let [url, tile] of this.available.entries()) { - if (!quadTrees.has(url)) { - this.destroyTile(url, tile); - count += 1; - } - } - if (count && this.debug) - console.log('destroyTiles', this.level, count); - } - - destroyUnneededTiles() { - let count = 0; - for (let [url, tile] of this.available.entries()) { - if (!this.needed.has(url)) { - this.destroyTile(url, tile); - count += 1; - } - } - if (count && this.debug) - console.log('destroyUnneededTiles', this.level, count); - } - - highlightInfos() { - let graphics = this.info; - let color = this.infoColor; - graphics.clear(); - graphics.lineStyle(2, color); - for (let [col, row] of this.needed.values()) { - let dim = this.tileDimensions(col, row); - let pos = this.tilePosition(col, row); - graphics.beginFill(color, 0.2); - graphics.drawRect(pos.x + 1, pos.y + 1, dim.x - 2, dim.y - 2); - graphics.endFill(); - } - let r = this._boundsRect; - if (r != null) { - graphics.lineStyle(20, color); - graphics.drawRect(r.x, r.y, r.width, r.height); - graphics.moveTo(r.x, r.y); - graphics.lineTo(r.x + r.width, r.y + r.height); - - graphics.moveTo(r.x, r.y + r.height); - graphics.lineTo(r.x + r.width, r.y); - } - - let p = this._centerPoint; - if (p != null) { - graphics.drawCircle(p.x, p.y, 20); - } - } - - tintTiles(quadTrees) { - for (let [url, tile] of this.available.entries()) { - if (!quadTrees.has(url)) tile.tint = 0xff0000; - } - } - - untintTiles() { - for (let [url, tile] of this.available.entries()) { - tile.tint = 0xffffff; - } - } - } - - function isEven(n) { - return n % 2 == 0 - } - - - function printTileCacheInfos() { - let references = new Map(); - let multiples = 0; - for (let [url, tiles] of deepZoomTileCache.entries()) { - let count = tiles.size; - references.set(url, count); - if (count > 1) { - multiples += 1; - } - } - console.log({ multiples, references }); - } - /** - * A utility class that holds information typically provided by DZI files, i.e. - * height and width of the overall image, overlap, and image type. - * - * @constructor - * @param {obj} attrs - A JSON-Object holding the listed keys and values - * @example - * { - * "tileSize": 1024, - * "format": "jpeg", - * "overlap": 1, - * "height": 4794, - * "width": 4095, - * "clip": { "minLevel": 12, "maxLevel": 20, "startCol": 301436, "startRow": 354060 }, - * // optional: minLevel and maxLevel define the level bounds - * // startCol: first col at maxLevel - * // startRow: first row at maxLevel - * "path": "var/Vermeer/Vermeer_files", - * "type": "dzi", // optional: dzi (default) or map - * "urlTileTemplate": "{path}/{level}/{column}/{row}.{format}" - * // optional: {path}/{level}/{column}_{row}.{format} (default) or - * // a template String with the format of the URL - * } - */ - class DeepZoomInfo { - constructor(attrs) { - for (let key in attrs) { - this[key] = attrs[key]; - } - this.maxLevel = 0; // The highest level number, typically corresponds to the - // number in the file system for the folder with tiles - this.clip = this.clip || null; // e.g. { level: 12, col: 301436, row: 354060 } - this.type = this.type || 'dzi'; - this.urlTileTemplate = - this.urlTileTemplate || '{path}/{level}/{column}_{row}.{format}'; - this.setupDimensions(); - } - - /* Computes the needed number of layers from the width and height - * of the image. Note that this includes the level 0, i.e. 0 ... 4 - * means that 5 levels exist. - **/ - numLevels() { - let maxDimension = Math.max(this.width, this.height); - let boundary = this.type === 'dzi' ? 1 : this.tileSize; - let numLevels = 0; - while (maxDimension >= boundary) { - maxDimension /= 2; - numLevels++; - } - return numLevels - } - - /** Computes the scale at the given level. - * @param {number} level - The level of the wanted layer - * @returns {number} scale - **/ - getScale(level) { - let scale = 1; - if (this.type === 'dzi') { - scale = Math.pow(0.5, this.maxLevel - level + 1); - } else { - scale = Math.pow(0.5, this.maxLevel - level); - } - return scale - } - - /** Computes the scaled width and height of the given level. - * @param {number} level - The level of the wanted layer - * @returns {array} size - The width and height - **/ - getDimensions(level) { - let scale = this.getScale(level); - let w = Math.ceil(this.width * scale); - let h = Math.ceil(this.height * scale); - return [w, h] - } - - /** Computes the number of cols and rows of tiles. - * @param {number} level - The level of the wanted layer - * @returns {array} size - The cols and rows - **/ - getNumTiles(level) { - let dim = this.getDimensions(level); - let cols = Math.ceil(dim[0] / this.tileSize); - let rows = Math.ceil(dim[1] / this.tileSize); - if (this.clip) { - let rest = this.rests[level]; - if (rest) { - if (rest.restCol) { - cols += 1; - } - if (rest.restRows) { - rows += 1; - } - } - } - return [cols, rows] - } - - setupDimensions(loadBaseImage = false) { - /** Setup instance variables and load the base image, i.e. the largest - image that can be represented as a single tile. - @private - **/ - let ww = this.width; - let hh = this.height; - let scale = 1.0; - let level = 0; - let single = 0; - const tsize = this.tileSize; - - if (this.clip) { - this.baseLevel = this.clip.minLevel; - this.maxLevel = this.clip.maxLevel; - this.baseImage = null; - this.size = this.getDimensions(this.baseLevel); - this.offsets = {}; - this.rests = {}; - let startCol = this.clip.startCol; - let startRow = this.clip.startRow; - let floatStartCol = startCol; - let floatStartRow = startRow; - for (let i = this.maxLevel; i >= this.baseLevel; i--) { - this.offsets[i] = { startCol, startRow }; - let restCol = floatStartCol % 1; - let restRow = floatStartRow % 1; - this.rests[i] = { restCol, restRow }; - startCol = Math.floor(startCol / 2); - startRow = Math.floor(startRow / 2); - floatStartCol /= 2; - floatStartRow /= 2; - } - } else { - const boundary = this.type === 'dzi' ? 1.0 : tsize; - while (ww > boundary && hh > boundary) { - if (ww >= tsize && hh >= tsize) { - single += 1; - } - scale = scale / 2.0; - ww = Math.ceil(this.width * scale); - hh = Math.ceil(this.height * scale); - level += 1; - } - this.baseLevel = level - single; - this.maxLevel = this.numLevels() - 1; - this.baseURL = this.urlForTile(this.baseLevel, 0, 0, false); - - if (loadBaseImage) { - this.imageForURL(this.baseURL, e => { - this.size = [e.target.naturalWidth, e.target.naturalHeight]; - this.baseImage = e.target; - }); - } else { - this.baseImage = null; - this.size = this.getDimensions(this.baseLevel); - } - } - } - - get maxLoadableLevel() { - if (this.clip) { - return this.maxLevel - } - return this.type === 'dzi' ? this.maxLevel : this.maxLevel - } - - /** Computes the url for the given level, column and and row. - * @param {number} level - The level of the wanted layer - * @param {number} column - The column of the tile - * @param {number} row - The row of the tile - * @returns {string} url - **/ - urlForTile(level, column, row, compressed = true) { - let format = this.format; - if (compressed && this.compression) { - let supported = Capabilities.isIOS ? 'pvr' : 'dds'; - if (this.compression.indexOf(supported) >= 0) { - format = supported; - } - } - if (this.clip) { - let offset = this.offsets[level]; - if (offset) { - let { startCol, startRow } = offset; - column += startCol; - row += startRow; - } - } - let url = this.urlTileTemplate - .replace(/\{path\}/g, this.path) - .replace(/\{level\}/g, level) - .replace(/\{row\}/g, row) - .replace(/\{column\}/g, column) - .replace(/\{format\}/g, format); - return url - } - - /** Loads the image for the given url and executes a callback function - on completion. - * @param {string} url - The url of the tile - * @param {function} complete - The callback function - * @returns {Image} obj - **/ - imageForURL(url, complete) { - let img = new Image(); - img.onload = complete.bind(this); - img.src = url; - return img - } - - /** Computes the columns and rows as well as scaled width and height. - * @param {number} level - The level of the wanted layer - * @returns {array} [cols, rows, width, height] - **/ - dimensions(level) { - let dim = this.getDimensions(level); - let tiles = this.getNumTiles(level); - return [tiles[0], tiles[1], dim[0], dim[1]] - } - - test() { - //console.log("w=" + this.width + " h=" + this.height + " maxlevel=" + this.maxLevel + " base=" + this.baseLevel) - for (let i = 0; i <= this.maxLevel; i++) { - console.log( - ' ' + - i + - ' -> ' + - this.getScale(i) + - ' [' + - this.dimensions(i) + - ']' - ); - } - console.log(this.urlForTile(this.baseLevel, 0, 0)); - } - } - - /** - * A utility class that describes a quad tree of tiles. Each tile on a given - * level has up to four corresponding tiles on the next level. A TileQuadNode - * uses the attributes nw (i.e. northwest), ne, sw, se to link to the - * quad nodes on the next level. The previous attributes links to the quad - * one level below that holds the given quad as nw, ne, sw, or se. - * We use this node class because we need a representation of tiles that are - * needed but not loaded yet to compute tiles which can be abandoned to reduce - * the memory pressure. - * - * @constructor - * @param {level} Number - The level the quad node belongs to - * @param {col} Number - The col of the quad - * @param {row} Number - The level the quad node belongs to - * @param {url} String - The level the quad node belongs to - */ - class TileQuadNode { - constructor(level, col, row, url) { - this.level = level; - this.col = col; - this.row = row; - this.url = url; - this.nw = null; - this.ne = null; - this.sw = null; - this.se = null; - this.previous = null; - } - - /** Return True if this node has no successors and can be used as - an indicator of tiles to free. - **/ - noQuads() { - if (this.previous === null) return false - return ( - this.nw === null && - this.ne === null && - this.sw === null && - this.se === null - ) - } - - /** Unlink the given quad node - - * @param {node} TileQuadNode - The TileQuadNode to remove - **/ - unlink(node) { - if (this.nw === node) this.nw = null; - if (this.ne === node) this.ne = null; - if (this.sw === node) this.sw = null; - if (this.se === node) this.se = null; - } - - /** Link this quad node to the given previous node. Use the north - * and west flags to address nw, ne, sw, and se. - - * @param {node} TileQuadNode - The TileQuadNode to remove - * @param {north} Boolean - Link to north (true) or south (false) - * @param {west} Boolean - Link to west (true) or east (false) - **/ - link(north, west, previous) { - this.previous = previous; - if (north) { - if (west) { - previous.nw = this; - } else { - previous.ne = this; - } - } else { - if (west) { - previous.sw = this; - } else { - previous.se = this; - } - } - } - } - - - /** - * The main class of a deeply zoomable image that is represented by a hierarchy - * of tile layers for each zoom level. This gives the user the impression that - * even huge pictures (up to gigapixel-images) can be zoomed instantaneously, - * since the tiles at smaller levels are scaled immediately and overloaded by - * more detailed tiles at the larger level as fast as possible. - - * @constructor - * @param {DeepZoomInfo} deepZoomInfo - Information extracted from a JSON-Object - */ - class DeepZoomImage extends PIXI.Container { - constructor( - deepZoomInfo, - { - debug = false, - shadow = false, - center = false, - world = null, // Defines the world bounds the images lives in - highResolution = true, - autoLoadTiles = true, - preferWorker = false, - minimumLevel = 0, - alpha = 1, - app = window.app - } = {} - ) { - super(); - this.app = app; - this.debug = debug; - this.shadow = shadow; - this.world = world; - this.preferWorker = preferWorker; - this.resolution = highResolution - ? Math.round(window.devicePixelRatio) - : 1; - this.alpha = alpha; - this.fastLoads = 0; - this.autoLoadTiles = autoLoadTiles; - this.minimumLevel = minimumLevel; - this.quadTrees = new Map(); // url as keys, TileQuadNodes as values - this.setup(deepZoomInfo, center); - } - - get point() { - if (this._point == null) { - let graphics = new PIXI.Graphics(); - graphics.lineStyle(2, 0x00ff00); - graphics.drawCircle(0, 0, 2); - graphics.interactive = false; - this._point = graphics; - } - return this._point - } - - /** Reads the DeepZoomInfo object and initializes all tile layers. - * Called by the constructor. - * Creates the sprite for the loaded texture and add the sprite to the tile - * layer. - * @param {Object} deepZoomInfo - the DeepZoomInfo instance - * @param {boolean} center - If true ensures that the pivot is set to the center - **/ - setup(deepZoomInfo, center) { - this.info = deepZoomInfo; - this.interactive = true; - this.tileLayers = {}; - - this._foreground = null; - this.tileContainer = new PIXI.Container(); - this.tileContainer.interactive = false; - - let [w, h] = this.baseSize; - if (this.shadow) { - this.filters = [new PIXI.filters.DropShadowFilter(45, 3)]; - } - this.addChild(this.tileContainer); - - if (deepZoomInfo.clip) { - let mask = new PIXI.Graphics(); - mask.beginFill(1, 1); - mask.drawRect(0, 0, w, h); - mask.endFill(); - this.mask = mask; - mask.alpha = 0; - this.addChild(mask); - this.minimumLevel = deepZoomInfo.baseLevel; - } - this.currentLevel = Math.max(this.minimumLevel, deepZoomInfo.baseLevel); - console.log("autoLoadTiles", this.autoLoadTiles); - if (this.autoLoadTiles) { - this.setupTiles(center); - } - } - - /** Default setup method for tiles. Loads all tiles of the current level. - Can be overwritten in subclasses. - @param {boolean} center - If true ensures that the pivot is set to the center - **/ - setupTiles(center = false) { - // First load background tile - let tiles = this.ensureAllTiles(this.currentLevel); - if (center) { - this.pivot.set(w / 2, h / 2); - } - let scaleLevel = this.levelForScale(1); - this.ensureAllTiles(scaleLevel); - } - - removeTileQuadNode(level, col, row, url) { - if (this.quadTrees.has(url)) { - let quad = this.quadTrees.get(url); - this.tileQuadRemoved(quad); - this.quadTrees.delete(url); - } - } - - addTileQuadNode(level, col, row, url) { - if (this.quadTrees.has(url)) return this.quadTrees.get(url) - let quad = new TileQuadNode(level, col, row, url); - this.quadTrees.set(url, quad); - this.tileQuadAdded(quad); - return quad - } - - tileQuadRemoved(quad) { - let below = quad.previous; - // if (this.debug) console.log("tileQuadRemoved", quad) - if (below) { - below.unlink(quad); - if (below.noQuads()) { - if (this.debug) console.log('Removed tile below'); - let levelBelow = quad.level - 1; - if (levelBelow < this.minimumLevel) return - let c = Math.floor(quad.col / 2); - let r = Math.floor(quad.row / 2); - let urlBelow = this.info.urlForTile(levelBelow, c, r); - if (this.quadTrees.has(urlBelow)) { - this.removeTileQuadNode(levelBelow, c, r, urlBelow); - } - } - } - } - - tileQuadAdded(quad) { - let levelBelow = quad.level - 1; - if (levelBelow < this.minimumLevel) return - //if (this.debug) console.log("tileQuadAdded", quad) - let c = Math.floor(quad.col / 2); - let r = Math.floor(quad.row / 2); - let urlBelow = this.info.urlForTile(levelBelow, c, r); - let below = null; - if (!this.quadTrees.has(urlBelow)) { - below = this.addTileQuadNode(levelBelow, c, r, urlBelow); - quad.link(isEven(quad.row), isEven(quad.col), below); - } - } - - /** Returns the tile layer level that corresponds to the given scale. - * @param {number} scale - the scale factor - **/ - levelForScale(scale) { - let level = Math.round(Math.log2(scale * this.resolution)); // Math.floor(Math.log2(event.scale))+1 - let newLevel = this.info.baseLevel + Math.max(level, 0); - return Math.min(newLevel, this.info.maxLoadableLevel) - } - - /** Returns the tile layer level that corresponds to the given scale. - * @param {number} scale - the scale factor - **/ - levelAndAlphaForScale(scale) { - let value = Math.log2(scale * this.resolution); - let level = Math.round(value); - let newLevel = this.info.baseLevel + Math.max(level, 0); - - return { level: Math.min(newLevel, this.info.maxLoadableLevel), alpha: value - level } - } - - /** Adds a tile layer to the DeepZoomImage. - * @param {string} key - the access key - * @param {Tiles} tiles - the tile layer object - **/ - addTiles(key, tiles) { - if (key in this.tileLayers) { - console.warn('Tiles already availabl', key); - } - this.tileContainer.addChild(tiles); - this.tileLayers[key] = tiles; - } - - destroyTiles(key) { - let tiles = this.tileLayers[key]; - this.tileContainer.removeChild(tiles); - tiles.destroy(); - delete this.tileLayers[key]; - } - - /** Getter for PIXI.Container foreground layer. - * Adds a PIXI.Container if necessary. - **/ - get foreground() { - if (this._foreground == null) { - this._foreground = new PIXI.Container(); - this.addChild(this._foreground); - } - return this._foreground - } - - /** Getter for the DeepZoomInfo base level size. - **/ - get baseSize() { - return this.info.getDimensions(this.info.baseLevel) - } - - /** Getter for the current scaled size in pixels. - **/ - get pixelSize() { - let [w, h] = this.baseSize; - return [w * this.scale.x, h * this.scale.y] - } - - /** Getter for the max scale factor. - **/ - get maxScale() { - let delta = this.info.maxLevel - this.info.baseLevel; - return Math.pow(2, delta) / this.resolution * 2 - } - - /** Getter for the current width. - **/ - get width() { - return this.pixelSize[0] - } - - /** Getter for the current height. - **/ - get height() { - return this.pixelSize[1] - } - - - /* Overrides PIXI.Container.hitArea() - * Allows to optimize the hit testing. Container with hit areas are directly - * hit tested without consideration of children. - */ - get hitArea() { - // Defining the hitArea resulted hitting the scatter in masked area - // when a mask was used (@Tüsch[submaps]). Removing the hitArea() altogether - // broke the interaction in other projects (@googleart). - // Fix: When masked, the hitArea is ignored by returning null. - // TODO: test if childs are hittested, without setting interactiveChildren. - // Opel, 03-05-2018 - if (this.mask) { - return null - } - return this - } - - /* Overrides PIXI.Container.contains() - * Allows to optimize the hit testing. - */ - contains(x, y) { - let [w, h] = this.baseSize; - return x >= 0 && x <= w && y >= 0 && y <= h - } - - /** Overrides PIXI.Container._calculateBounds() - * Only considers the base size and reduces the calculation to a single - * rect. - */ - _calculateBounds() { - let [w, h] = this.baseSize; - this._bounds.addFrame(this.transform, 0, 0, w, h); - } - - /** Overrides PIXI.Container.calculateBounds() - * Skips the children and only considers the deep zoom base size. Calls - * the also overwritten _calculateBounds method. - */ - calculateBounds() { - this._bounds.clear(); - this._calculateBounds(); - this._lastBoundsID = this._boundsID; - } - - /** Returns a single sprite that can be used a thumbnail representation of - * large images. - * @return {Sprite} sprite - A sprite with a single tile texture - */ - thumbnail() { - return new PIXI.Sprite.fromImage(this.info.baseURL) - } - - /** Returns a list of all tiles of a given level. - * @param {Tiles} tiles - the grid of tiles - * @param {number} level - The zoom level of the grid - * @return {Array[]} - An array of [url, col, row] arrays - **/ - allTiles(tiles, level) { - let result = []; - for (let col = 0; col < tiles.cols; col++) { - for (let row = 0; row < tiles.rows; row++) { - let url = this.info.urlForTile(level, col, row); - result.push([url, col, row]); - } - } - return result - } - - worldBounds() { - let viewBounds = this.app.scene.bounds; - // Using getBounds extends visible scope after loading tiles and leads - // to excessive loading - if (this.world != null) { - let bounds = this.world.bounds; - let x = Math.max(-bounds.width, bounds.x); - let y = Math.max(-bounds.height, bounds.y); - let width = Math.min(viewBounds.width, bounds.width); - let height = Math.min(viewBounds.height, bounds.height); - //console.log("worldBounds new", { x, y, width, height }) - return { x, y, width, height } - } - //console.log("worldBounds old", viewBounds) - return viewBounds - } - - /** Loads all tiles that are needed to fill the app bounds. - * @param {Tiles} tiles - the grid of tiles - * @param {number} level - The zoom level of the grid - * @param {boolean} debug - * @return {Array[]} - An array of [url, col, row] arrays - */ - neededTiles(tiles, level, debug = false) { - let needed = []; - let tsize = tiles.tileSize; - let worldBounds = this.worldBounds(); - let maxWidth = worldBounds.width; - let maxHeight = worldBounds.height; - - let pointInWindow = new PIXI.Point(); - let worldTopLeft = new PIXI.Point(worldBounds.x, worldBounds.y); - let worldBottomRight = new PIXI.Point(worldBounds.x + maxWidth, worldBounds.y + maxHeight); - let worldCenter = new PIXI.Point(worldBounds.x + maxWidth / 2, worldBounds.y + maxHeight / 2); - let tilesCenter = tiles.toLocal(worldCenter); - - let topLeft = tiles.toLocal(worldTopLeft); - let bottomRight = tiles.toLocal(worldBottomRight); - tiles._centerPoint = tilesCenter; - let bounds = new PIXI.Rectangle( - topLeft.x, - topLeft.y, - bottomRight.x - topLeft.x, - bottomRight.y - topLeft.y); - - tiles._boundsRect = bounds; - - /* UO: we need a toLocal call here since the transform may need an update - which is guaranteed by the toLocal method. */ - let centerCol = Math.floor(tilesCenter.x / tsize); - let centerRow = Math.floor(tilesCenter.y / tsize); - - // Expand because we want to test for included centers - bounds.x -= tsize / 2; - bounds.y -= tsize / 2; - bounds.width += tsize; - bounds.height += tsize; - - try { - let maxTilesWidth = Math.ceil(maxWidth / tsize); - let maxTilesHeight = Math.ceil(maxHeight / tsize); - - maxTilesWidth += 2; - maxTilesHeight += 2; - - let startCol = Math.max(0, centerCol - maxTilesWidth); - let endCol = Math.min(tiles.cols, centerCol + maxTilesWidth); - - let startRow = Math.max(0, centerRow - maxTilesHeight); - let endRow = Math.min(tiles.rows, centerRow + maxTilesHeight); - - for (let col = startCol; col < endCol; col++) { - let cx = (col + 0.5) * tsize; - for (let row = startRow; row < endRow; row++) { - let cy = (row + 0.5) * tsize; - let tileCenter = new PIXI.Point(cx, cy); - if (bounds.contains(tileCenter.x, tileCenter.y)) { - let url = this.info.urlForTile(level, col, row); - needed.push([url, col, row]); - } - } - } - } catch (error) { - console.warn(error.message); - } - return { centerCol, centerRow, needed } - } - - - - - /** Returns all changed tiles for a given level. - * @param {Tiles} tiles - the grid of tiles - * @param {number} level - The zoom level of the grid - * @return {object} - An object with the keys added and removed which values are [url, col, row] arrays - */ - changedTiles(tiles, level) { - if (this.debug) console.time('changedTiles'); - let changed = { added: [], removed: [] }; - let newNeeded = new Map(); - let { centerCol, centerRow, needed } = this.neededTiles(tiles, level); - needed.forEach(d => { - let [url, col, row] = d; - newNeeded.set(url, [col, row]); - if (!tiles.requested.has(url)) { - changed.added.push(d); - } - }); - for (let url of tiles.needed.keys()) { - if (!newNeeded.has(url)) { - let [col, row] = tiles.needed.get(url); - changed.removed.push([url, col, row]); - } - } - tiles.needed = newNeeded; - if (this.debug) console.log(newNeeded); - if (this.debug) console.timeEnd('changedTiles'); - return { centerCol, centerRow, changed } - } - - /** Populates all tiles for a given level. - * @param {Tiles} tiles - the grid of tiles - * @param {number} level - The zoom level of the grid - */ - populateAllTiles(tiles, level) { - let all = this.allTiles(tiles, level); - for (let [url, col, row] of all) { - this.addTileQuadNode(level, col, row, url); - } - tiles.loadTiles(all, false, 0, 0); - } - - /** Loads all tiles that are needed to fill the browser window. - * If the optional about parameter is provided (as a point with col as x, - * and row as y attr) the tiles are sorted by the distance to this point. - * - * @param {Tiles} tiles - the grid of tiles - * @param {number} level - The zoom level of the grid - * Optional parameter: - * @param {boolean} onlyone - if true only one tile is loaded - * @param {PIXI.Point} about - point of interaction - */ - populateTiles(tiles, level, { onlyone = false, about = null } = {}) { - if (tiles.isComplete()) - return - let referenceCol = -1; - let referenceRow = -1; - let { centerCol, centerRow, changed } = this.changedTiles(tiles, level); - if (about != null) { - // We want to load tiles in the focus of the user first, therefore - // we sort according to the distance of the focus of interaction - let refPoint = this.toLocal(about); - let scaledTileSize = tiles.tileSize * tiles.tileScale; - referenceCol = Math.floor(refPoint.x / scaledTileSize); - referenceRow = Math.floor(refPoint.y / scaledTileSize); - } - else { - referenceCol = centerCol; - referenceRow = centerRow; - } - referenceCol = centerCol; - referenceRow = centerRow; - - let removed = changed.removed; - for (let [url, col, row] of removed) { - this.removeTileQuadNode(level, col, row, url); - } - let added = changed.added; - if (added.length == 0) return - for (let [url, col, row] of added) { - this.addTileQuadNode(level, col, row, url); - } - let ref = new PIXI.Point(referenceCol, referenceRow); - // Note: The array must be sorted in a way that the nearest tiles - // are at the end of the array since the load queue uses Array.push - // Array.pop - added.sort((a, b) => { - let aa = new PIXI.Point(a[1], a[2]); - let bb = new PIXI.Point(b[1], b[2]); - let da = Points.distance(aa, ref); - let db = Points.distance(bb, ref); - return db - da - }); - tiles.loadTiles(added, onlyone, referenceCol, referenceRow); - } - - /** Private method: creates all tiles for a given level. - * @param {number} level - The zoom level of the grid - * @return {Tiles} - tiles - */ - _createTiles(key, level) { - let [cols, rows, w, h] = this.info.dimensions(level); - let increasedLevels = level - this.info.baseLevel; - let invScale = Math.pow(0.5, increasedLevels); - let tiles = new Tiles( - level, - this, - invScale, - cols, - rows, - w, - h, - this.info.tileSize, - this.info.overlap - ); - this.addTiles(key, tiles); - if (this.info.clip) { - let rest = this.info.rests[level]; - if (rest) { - let x = rest.restCol * this.info.tileSize * invScale; - let y = rest.restRow * this.info.tileSize * invScale; - tiles.x = -x; - tiles.y = -y; - } - } - return tiles - } - - /** Ensures that all needed tiles of a given level are loaded. Creates - * a new Tiles layer if necessary - * @param {number} level - The zoom level of the grid - * @return {Tiles} tiles - */ - ensureTiles(level, about) { - let key = level.toString(); - if (key in this.tileLayers) { - let tiles = this.tileLayers[key]; - this.populateTiles(tiles, level, { about: about }); - return tiles - } - let tiles = this._createTiles(key, level); - this.populateTiles(tiles, level, { about: about }); - //console.log("ensureTiles", level) - return tiles - } - - untintTiles(level) { - let key = level.toString(); - if (key in this.tileLayers) { - let tiles = this.tileLayers[key]; - } - } - - /** Ensures that all tiles of a given level are loaded. - * @param {number} level - The zoom level of the grid - */ - ensureAllTiles(level) { - let key = level.toString(); - if (key in this.tileLayers) { - let tiles = this.tileLayers[key]; - this.populateAllTiles(tiles, level); - tiles.keep = true; - return - } - let tiles = this._createTiles(key, level); - this.populateAllTiles(tiles, level); - tiles.keep = true; - return tiles - } - - hideTilesAboveLevel(level) { - Object.keys(this.tileLayers).forEach(key => { - let tiles = this.tileLayers[key]; - if (tiles.level > level) { - tiles.visible = false; - } - }); - } - - /** Destroys all tiles above a given level to ensure that the memory can - * be reused. - * @param {number} level - The zoom level of the grid - */ - destroyTilesAboveLevel(level) { - Object.keys(this.tileLayers).forEach(key => { - let tiles = this.tileLayers[key]; - if (tiles.level > level && !tiles.keep) { - for (let url of tiles.available.keys()) { - let quad = this.quadTrees.get(url); - if (quad) this.removeTileQuadNode(quad); - } - this.destroyTiles(key); - } - }); - } - - destroyAllTiles() { - Object.keys(this.tileLayers).forEach(key => { - this.destroyTiles(key); - }); - } - - /** - * Tint tiles in all layers that are no longer needed - * - * @memberof DeepZoomImage - */ - tintObsoleteTiles() { - Object.keys(this.tileLayers).forEach(key => { - let tiles = this.tileLayers[key]; - tiles.untintTiles(); - if (!tiles.keep) { - tiles.tintObsoleteTiles(); - } - }); - } - - - /** - * Destroy tiles in all layers that are no longer needed - * - * @memberof DeepZoomImage - */ - destroyUnneededTiles() { - Object.keys(this.tileLayers).forEach(key => { - let tiles = this.tileLayers[key]; - if (!tiles.keep) { - tiles.destroyUnneededTiles(); - } - }); - } - - /** - * Destroy tiles in all layers that are no longer needed - * - * @memberof DeepZoomImage - */ - destroyObsoleteTiles() { - Object.keys(this.tileLayers).forEach(key => { - let tiles = this.tileLayers[key]; - if (!tiles.keep) { - tiles.destroyObsoleteTiles(); - } - }); - } - - /** - * Destroy tiles in all layers that are not part of the - * visible quadTrees - * - * @memberof DeepZoomImage - */ - destroyTiles() { - Object.keys(this.tileLayers).forEach(key => { - let tiles = this.tileLayers[key]; - if (!tiles.keep) { - tiles.destroyTiles(this.quadTrees); - } - }); - } - - /* Tint all tiles - * @param {number} level - The zoom level of the grid - */ - tintTilesBelowLevel(level) { - Object.keys(this.tileLayers).forEach(key => { - let tiles = this.tileLayers[key]; - if (tiles.level < level) { - tiles.tintTiles(this.quadTrees); - } - }); - } - - /** - * Ensure that the given tiles layer is the topmost one and visible. - * @param {*} tiles - */ - bringTilesToFront(tiles) { - this.tileContainer.addChild(tiles); - tiles.visible = true; - } - - /** A callback function that can be used by a Scatter view to inform - * the zoomable image that it has been moved, rotated or scaled, and should - * load tiles accordingly. - * @param {PIXI.Point} translated - the movement of the scatter - * @param {number} scale - the zoom factor - * @param {PIXI.Point} about - the anchor point of the zoom - * @param {boolean} fast - informs the callback to return as fast as possible, - * i.e. after loading a single tile - * @param {boolean} debug - log debug infos - */ - transformed(event) { - let key = this.currentLevel.toString(); - let currentTiles = this.tileLayers[key]; - if (typeof currentTiles == 'undefined') { - return - } - if (event.fast) { - this.fastLoads += 1; - this.populateTiles(currentTiles, this.currentLevel, { - onlyone: false, - about: event.about - }); - if (this.fastLoads == 3) { - this.fastLoads = 0; - } - else { - return - } - } - if (event.scale == null) { - this.ensureTiles(this.currentLevel, event.about); - return - } - - let level = this.levelForScale(event.scale); - let newLevel = Math.max(level, this.minimumLevel); - if (newLevel != this.currentLevel) { - if (!currentTiles.keep) { - currentTiles.loader.cancel(); - } - this.hideTilesAboveLevel(newLevel); - currentTiles = this.ensureTiles(newLevel, event.about); - this.currentLevel = newLevel; - } else { - this.ensureTiles(this.currentLevel, event.about); - } - this.bringTilesToFront(currentTiles); - if (this._foreground) { - this.addChild(this._foreground); - } - } - - /** - *Activates the textures on the DeepZoomImage. - * - * @memberof DeepZoomImage - */ - activate() { - this.destroyTilesAboveLevel(this.currentLevel); - this.ensureTiles(this.currentLevel, null); - //console.log("Activate Textures!", this.currentLevel) - } - - /** - *Dectivates the textures on the DeepZoomImage. - * - * @memberof DeepZoomImage - */ - deactivate() { - this.destroyAllTiles(); - Object.keys(this.tileLayers).forEach(key => { - this.destroyTiles(key); - }); - this.tileContainer.destroy({ children: true }); - printTileCacheInfos(); - } - - throwFinished() { - console.log("throwFinished"); - let key = this.currentLevel.toString(); - let currentTiles = this.tileLayers[key]; - if (typeof currentTiles == 'undefined') { - return - } - - this.ensureTiles(this.currentLevel); - // let all = new Set() - // for (let tile of currentTiles.children) { - // all.add(tile.url) - // } - // let { centerCol, centerRow, needed } = this.neededTiles(currentTiles, this.currentLevel) - // for (let [url, col, row] of needed) { - // all.delete(url) - // } - // for (let url of all) { - // currentTiles.destroyTileByUrl(url) - // } - // currentTiles.loader.loader.reset() - } - } - - let globalScatterLoaderCanvas = null; - - class ScatterLoader extends CardLoader { - - get scatter() { - return this.src - } - - unscaledSize() { - let displayObject = this.scatter.displayObject; - let w = displayObject.width; - let h = displayObject.height; - return [w / displayObject.scale.x, h / displayObject.scale.y] - } - - scaledSize() { - let displayObject = this.scatter.displayObject; - let w = displayObject.width; - let h = displayObject.height; - return [w, h] - } - - cloneScatterImage() { - let w = this.scatter.width; - let h = this.scatter.height; - let isSprite = this.scatter.displayObject instanceof PIXI.Sprite; - let isDeepZoom = this.scatter.displayObject instanceof DeepZoomImage; - let resolution = app.renderer.resolution; - if (isSprite) { - w = this.scatter.displayObject.texture.width; - h = this.scatter.displayObject.texture.height; - } - else if (isDeepZoom) { - let [ww, hh] = this.scatter.displayObject.baseSize; - w = ww; - h = hh; - } - if (globalScatterLoaderCanvas === null) { - globalScatterLoaderCanvas = document.createElement('canvas'); - } - let canvas = globalScatterLoaderCanvas; - canvas.width = w; - canvas.height = h; - let renderer = new PIXI.WebGLRenderer(w, h, { - view: canvas, - resolution: resolution}); - - let displayObject = this.scatter.displayObject; - let x = displayObject.x; - let y = displayObject.y; - let rot = displayObject.rotation; - let sx = displayObject.scale.x; - let sy = displayObject.scale.y; - displayObject.rotation = 0; - // The Safari WebGLRenderer wants everything flipped - // See https://github.com/pixijs/pixi.js/issues/2283 - displayObject.x = 0; - if (Capabilities.isSafari) { - displayObject.y = h; - displayObject.scale.set(1, -1); // sx, -sy) - } - else { - displayObject.y = 0; - displayObject.scale.set(1, 1); - } - if (isSprite) { - displayObject.width = w; - displayObject.height = h; - } - renderer.render(displayObject); - displayObject.rotation = rot; - displayObject.x = x; - displayObject.y = y; - displayObject.scale.set(sx, sy); - - let url = canvas.toDataURL(); - return [x, y, w, h, url] - } - - load(domNode) { - return new Promise((resolve, reject) => { - let isImage = domNode instanceof HTMLImageElement; - let isSprite = this.scatter.displayObject instanceof PIXI.Sprite; - let image = (isImage) ? domNode : document.createElement("img"); - let [x, y, w, h, cloneURL] = this.cloneScatterImage(); - let [ww, hh] = this.unscaledSize(); - image.onload = (e) => { - if (!isImage) - domNode.appendChild(image); - this.x = x; - this.y = y; - this.wantedWidth = ww; - this.wantedHeight = hh; - this.scale = 1; - this.rotation = this.scatter.rotation; - resolve(this); - }; - image.onerror = (e) => { - reject(this); - }; - image.src = cloneURL; - }) - } - - } - - class FlipEffect { - - constructor(scatter, domScatterContainer, flipTemplate, backLoader) { - this.flipped = false; - this.scatter = scatter; - this.backLoader = backLoader; - this.scatterLoader = new ScatterLoader(scatter); - this.domFlip = new DOMFlip(domScatterContainer, flipTemplate, - this.scatterLoader, - backLoader, { - onBack: this.backCardClosed.bind(this) - }); - this.setupInfoButton(); - } - - startFlip() { - let center = this.flipCenter(); - let loader = this.backLoader; - this.domFlip.load().then((domFlip) => { - this.scatter.displayObject.visible = false; - domFlip.centerAt(center); - domFlip.zoom(this.scatter.scale); - let target = this.constraintFlipCenter(center, loader); - console.log("FlipEffect.startFlip", target, loader); - domFlip.start({targetCenter: target}); - }); - } - - unscaledSize() { - return this.scatterLoader.unscaledSize() - } - - flipCenter() { - let isSprite = this.scatter.displayObject instanceof PIXI.Sprite; - let resolution = (isSprite) ? app.renderer.resolution : 1; - let center = this.scatter.center; - let canvas = app.renderer.view; - let domNode = this.domFlip.domScatterContainer.element; - let page = window.convertPointFromNodeToPage(canvas, - center.x*resolution, - center.y*resolution); - let local = window.convertPointFromPageToNode(domNode, page.x, page.y); - return local - } - - constraintFlipCenter(center, loader) { - let w = loader.wantedWidth; - let h = loader.wantedHeight; - console.log("constraintFlipCenter", w, h); - let canvas = app.renderer.view; - let x = center.x; - let y = center.y; - if (x < w/2) - x = w/2; - if (y < h/2) - y = h/2; - if (x > canvas.width) - x = canvas.width - w/2; - if (y > canvas.height) - y = canvas.height - h/2; - return { x, y } - } - - setupInfoButton() { - let iscale = 1.0 / this.scatter.scale; - this.infoBtn = new PIXI.Graphics(); - this.infoBtn.beginFill(0x333333); - this.infoBtn.lineStyle(4, 0xFFFFFF); - this.infoBtn.drawCircle(0, 0, 22); - this.infoBtn.endFill(); - - this.infoBtn.beginFill(0xFFFFFF); - this.infoBtn.lineStyle(0); - this.infoBtn.drawCircle(0, -8, 4); - this.infoBtn.endFill(); - - this.infoBtn.lineStyle(6, 0xFFFFFF); - this.infoBtn.moveTo(0, -2); - this.infoBtn.lineTo(0, 14); - this.infoBtn.endFill(); - - this.infoBtn.on('click', (e) => { this.infoSelected(); }); - this.infoBtn.on('tap', (e) => { this.infoSelected(); }); - - this.infoBtn.interactive = true; - this.infoBtn.width = 44; - this.infoBtn.height = 44; - this.infoBtn.pivot.x = 30; - this.infoBtn.pivot.y = 30; - - let displayObject = this.scatter.displayObject; - let [w, h] = this.unscaledSize(); - this.infoBtn.position = new PIXI.Point(w, h); - if (displayObject.foreground) { - this.infoBtn.scale.x = iscale; - this.infoBtn.scale.y = iscale; - displayObject.foreground.addChild(this.infoBtn); - } - else { - displayObject.addChild(this.infoBtn); - } - - this.scatter.addTransformEventCallback(e => { - let displayObject = this.scatter.displayObject; - if (displayObject.foreground) { - if (e.scale) { - let iscale = 1.0 / e.scale; - this.infoBtn.scale.x = iscale; - this.infoBtn.scale.y = iscale; - } - } - }); - } - - setupButton(url) { - let svgImage = new Image(); - let canvas = document.createElement('canvas'); - canvas.width = 88 * 4; - canvas.height = 44 * 4; - svgImage.onload = e => { - let displayObject = this.scatter.displayObject; - canvas.getContext ('2d').drawImage(svgImage, 0, 0, - canvas.width, canvas.height); - let texure = new PIXI.Texture(new PIXI.BaseTexture(canvas)); - this.infoBtn = new PIXI.Sprite(texure); - this.infoBtn.anchor.set(0.5, 0.5); - if (displayObject.foreground) { - displayObject.foreground.addChild(this.infoBtn); - } - else { - displayObject.addChild(this.infoBtn); - } - this.infoBtn.scale.set(0.5, 0.5); - - let [w, h] = this.unscaledSize(); - this.infoBtn.position = new PIXI.Point(w, h); - this.infoBtn.interactive = true; - this.infoBtn.updateTransform(); - this.infoBtn.on('click', (e) => { this.infoSelected(); }); - this.infoBtn.on('tap', (e) => { this.infoSelected(); }); - }; - svgImage.src = url; - } - - infoSelected() { - this.startFlip(); - } - - backSelected() { - this.domFlip.start(); - } - - backCardClosed() { - /*** The flip effect should now be in it's initial state again. All - memory should be freed. ***/ - let displayObject = this.scatter.displayObject; - displayObject.visible = true; - this.domFlip.fadeOutAndRemove(); - this.flipped = false; - } - - targetRotation(alpha) { - let ortho = 90; - let rest = alpha % ortho; - let delta = 0.0; - if (rest > (ortho / 2.0)) { - delta = ortho - rest; - } - else { - delta = -rest; - } - return delta - } - } - - /* globals Power2, Sine */ - /*eslint no-console: ["error", { allow: ["log", "info", "error"] }]*/ - - /** - * Callback for the flippable onStart function. - * - * @callback onStartCallback - * @param {Flippable} flippable - A reference to the flippable (also this refers to the flippable). - */ - - /** - * Callback for the flippable onUpdate function. - * - * @callback onUpdateCallback - * @param {Flippable} flippable - A reference to the flippable (also this refers to the flippable). - */ - - /** - * Callback for the flippable onComplete function. - * - * @callback onCompleteCallback - * @param {Flippable} flippable - A reference to the flippable (also this refers to the flippable). - */ - - /** - * Class that represents a PixiJS Flippable. - * - * @example - * const front = PIXI.Sprite.fromImage('./assets/front.jpg') - * const back = PIXI.Sprite.fromImage('./assets/back.jpg') - * app.scene.addChild(front) - * - * // Create the flippable - * const flippable = new Flippable(front, back, app.renderer) - * - * front.interactive = true - * front.on('click', event => flippable.toggle()) - * - * @class - * @extends PIXI.projection.Camera3d - * @see {@link https://github.com/pixijs/pixi-projection|PixiJS Projection} - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/flippable.html|DocTest} - */ - class Flippable extends PIXI.projection.Camera3d { - - /** - * Creates an instance of a Flippable. - * - * @constructor - * @param {PIXI.DisplayObject} front - The object to show initially. Should have been added to the scene. - * @param {PIXI.DisplayObject} back - The object to show on the backside. Should have not been added to the scene. - * @param {PIXI.WebGLRenderer|PIXI.CanvasRenderer} renderer - The renderer of the application. - * @param {object} [opts] - An options object which can contain the following properties. - * @param {number} [opts.duration=1] - The duration of the flip animation in seconds. - * @param {GSAP.Ease} [opts.ease=Power2.easeOut] - The ease of the flip animation. - * @param {boolean} [opts.shadow=false] - Should be a shadow been display during the animation? - * @param {numer} [opts.eulerX=0] - The shift of the x-axis during the animation. - * @param {numer} [opts.eulerY=0] - The shift of the y-axis during the animation. - * @param {GSAP.Ease} [opts.eulerEase=Sine.easeOut] - The ease of the shift. - * @param {boolean} [opts.useBackTransforms=false] - When set to true, the flip animation also animates to the transform parameters of the back-object. - * @param {GSAP.Ease} [opts.transformEase=Power2.easeOut] - The ease of the transform. - * @param {numer} [opts.focus=800] - The value of the focus of the 3D camera (see pixi-projection). - * @param {numer} [opts.near=10] - The near value of the 3D camera (see pixi-projection). - * @param {numer} [opts.far=10000] - The far value of the 3D camera (see pixi-projection). - * @param {boolean} [opts.orthographic=false] - Should the flip animation be an orthographic animation? - * @param {function} [opts.onStart=null] - A callback executed on start of the flip animation. - * @param {function} [opts.onUpdate=null] - A callback executed on each step of the flip animation. - * @param {function} [opts.onComplete=null] - A callback executed when the flip animation is finished. - */ - constructor(front, back, renderer, opts = {}) { - - super(); - - this.opts = Object.assign({}, { - front, - back, - renderer, - duration: 1, - ease: Power2.easeOut, - shadow: false, - eulerX: 0, - eulerY: 0, - eulerEase: Sine.easeOut, - useBackTransforms: false, - transformEase: Power2.easeOut, - focus: 800, - near: 10, - far: 10000, - orthographic: false - }, opts); - - // planes - //-------------------- - this.setPlanes(this.opts.focus, this.opts.near, this.opts.far, this.opts.orthographic); - - // flipped - //-------------------- - this._flipped = false; - - // objects - //-------------------- - this.objects = {}; - - // setup - //-------------------- - this.setup(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {Flippable} A reference to the flippable for chaining. - */ - setup() { - - const scale = .5; - - // filters - //-------------------- - const blurFilter = new PIXI.filters.BlurFilter(); - blurFilter.blur = 0.2; - this.objects.blurFilter = blurFilter; - - // outer - //-------------------- - const outer = new PIXI.projection.Container3d(); - outer.scale3d.set(scale); - this.addChild(outer); - this.objects.outer = outer; - - // shadow - //-------------------- - const shadow = new PIXI.projection.Sprite3d(PIXI.Texture.fromImage('../../assets/images/shadow.png')); - shadow.renderable = false; - shadow.anchor.set(0.5); - shadow.scale3d.set(.98); - shadow.alpha = 0.7; - shadow.filters = [blurFilter]; - shadow.visible = this.opts.shadow; - outer.addChild(shadow); - this.objects.shadow = shadow; - - // inner - //-------------------- - const inner = new PIXI.projection.Container3d(); - inner.euler.y = Math.PI; - outer.addChild(inner); - this.objects.inner = inner; - - // front - //-------------------- - const front = new PIXI.projection.Sprite3d(PIXI.Texture.EMPTY); - front.scale.set(-1 / scale, 1 / scale); - front.renderable = true; - front.anchor.set(.5); - inner.addChild(front); - this.objects.front = front; - - // back - //-------------------- - const back = new PIXI.projection.Sprite3d(PIXI.Texture.EMPTY); - back.scale.set(1 / scale, 1 / scale); - back.renderable = false; - back.anchor.set(.5); - inner.addChild(back); - this.objects.back = back; - - return this - } - - /** - * Gets or sets the flipped state. - * - * @member {boolean} - */ - get flipped() { - return this._flipped - } - set flipped(toBack) { - - this._flipped = toBack; - - // references - //-------------------- - const front = this.objects.front; - const back = this.objects.back; - const inner = this.objects.inner; - const shadow = this.objects.shadow; - const blurFilter = this.objects.blurFilter; - - const half = this.opts.duration / 2; - const ease = this.opts.eulerEase; - - const fromObject = toBack ? this.opts.front : this.opts.back; - const toObject = toBack ? this.opts.back : this.opts.front; - - // set textures for virtual front and virtual back - //-------------------- - front.texture = this.generateTexture(this.opts.front); - back.texture = this.generateTexture(this.opts.back); - - // switch objects and set params for virtual objects - //-------------------- - const fromCenter = this.anchorToCenter(fromObject); - const toCenter = this.anchorToCenter(toObject); - - // from values - //-------------------- - this.x = fromCenter.x; - this.y = fromCenter.y; - front.width = fromObject.width * 2; - front.height = fromObject.height * 2; - back.width = fromObject.width * 2; - back.height = fromObject.height * 2; - this.rotation = fromObject.rotation; - this.skew.x = fromObject.skew.x; - this.skew.y = fromObject.skew.y; - - // calculate to values - //-------------------- - const to = { - x: this.opts.useBackTransforms ? toCenter.x : fromCenter.x, - y: this.opts.useBackTransforms ? toCenter.y : fromCenter.y, - anchorX: this.opts.useBackTransforms ? toObject.x : fromObject.x, - anchorY: this.opts.useBackTransforms ? toObject.y : fromObject.y, - width: this.opts.useBackTransforms ? toObject.width * 2 : fromObject.width * 2, - height: this.opts.useBackTransforms ? toObject.height * 2 : fromObject.height * 2, - rotation: this.opts.useBackTransforms ? toObject.rotation : fromObject.rotation, - skewX: this.opts.useBackTransforms ? toObject.skew.x : fromObject.skew.x, - skewY: this.opts.useBackTransforms ? toObject.skew.y : fromObject.skew.y - }; - - // set toObject end values - //-------------------- - toObject.x = to.anchorX; - toObject.y = to.anchorY; - toObject.width = to.width / 2; - toObject.height = to.height / 2; - toObject.rotation = to.rotation; - toObject.skew.x = to.skewX; - toObject.skew.y = to.skewY; - - // flip - //-------------------- - TweenLite.to(inner.euler, this.opts.duration, { - y: toBack ? 0 : Math.PI, - ease: this.opts.ease, - onStart: () => { - this.switchDisplayObject(fromObject, this); - shadow.renderable = true; - if (this.opts.onStart) { - this.opts.onStart(this, this); - } - }, - onUpdate: () => { - this.layout(); - if (this.opts.onUpdate) { - this.opts.onUpdate(this, this); - } - }, - onComplete: () => { - this.switchDisplayObject(this, toObject); - shadow.renderable = false; - if (this.opts.onComplete) { - this.opts.onComplete(this, this); - } - } - }); - - // x & y - //-------------------- - TweenLite.to(this, this.opts.duration, { - x: to.x, - y: to.y, - ease: this.opts.transformEase - }); - - // width & height - //-------------------- - TweenLite.to([front, back], this.opts.duration, { - width: to.width, - height: to.height, - ease: this.opts.transformEase - }); - - // rotation - //-------------------- - TweenLite.to(this, this.opts.duration, { - directionalRotation: { - rotation: `${to.rotation}_short`, - useRadians: true - }, - ease: this.opts.transformEase - }); - - // skewX & skewY - //-------------------- - TweenLite.to(this.skew, this.opts.duration, { - x: to.skewX, - y: to.skewY, - ease: this.opts.transformEase - }); - - // camera - //-------------------- - new TimelineMax() - .to(this.euler, half, {x: this.opts.eulerX, y: this.opts.eulerY, ease}) - .to(this.euler, half, {x: 0, y: 0, ease}); - - // shadow - //-------------------- - new TimelineMax() - .to(shadow, half, {alpha: .3, ease}) - .to(shadow, half, {alpha: .7, ease}); - - // blurfilter - //-------------------- - new TimelineMax() - .to(blurFilter, half, {blur: 6, ease}) - .to(blurFilter, half, {blur: .2, ease}); - } - - /** - * Should be called to refresh the layout of the camera. - * - * @return {Flippable} A reference to the flippable for chaining. - */ - layout() { - - const front = this.objects.front; - const back = this.objects.back; - const shadow = this.objects.shadow; - const inner = this.objects.inner; - - inner.position3d.z = -Math.sin(inner.euler.y) * front.texture.baseTexture.width * 2; - - //this.objects.shadow.euler = this.objects.inner.euler - shadow.euler.x = -inner.euler.x; - shadow.euler.y = -inner.euler.y; - - if (this.frontSideInFront) { - front.renderable = true; - back.renderable = false; - shadow.width = front.width; - shadow.height = front.height; - } else { - front.renderable = false; - back.renderable = true; - shadow.width = back.width; - shadow.height = back.height; - } - - return this - } - - /** - * Toggles the flippable. Switches the sides. - * - * @private - * @return {Flippable} A reference to the flippable for chaining. - */ - toggle() { - this.flipped = !this.flipped; - - return this - } - - /** - * Gets the alignment state. true if the front side is in front, false otherwise. - * - * @member {boolean} - */ - get frontSideInFront() { - return !this.objects.inner.isFrontFace() - } - - /** - * Calculates the center point of an PIXI.DisplayObject. - * - * @private - * @param {PIXI.DisplayObject} displayObject - The DisplayObject from which to calculate the center. - * @return {object} Return an object with x and y. - */ - anchorToCenter(displayObject) { - const bounds = displayObject.getBounds(); - return { - x: bounds.x + bounds.width / 2, - y: bounds.y + bounds.height / 2 - } - } - - /** - * Creates children and instantiates everything. - * - * @private - * @param {PIXI.DisplayObject} displayObject - The DisplayObject from which to generate the texture. - * @return {PIXI.Texture} The generated PIXI.Texture. - */ - generateTexture(displayObject) { - - // renderTexture - //-------------------- - const renderTexture = PIXI.RenderTexture.create(displayObject.width, displayObject.height); - - // save position - const transform = [displayObject.x, displayObject.y, displayObject.scale.x, displayObject.scale.y, displayObject.rotation, displayObject.skew.x, displayObject.skew.y, displayObject.pivot.x, displayObject.pivot.y]; - - displayObject.position.set(0, 0); - displayObject.skew.set(0, 0); - displayObject.rotation = 0; - - // render - //-------------------- - this.opts.renderer.render(displayObject, renderTexture); - - // restore position - displayObject.setTransform(...transform); - - return renderTexture - } - - /** - * Removed the first DisplayObject and adds the second one at the exactly same position. - * - * @private - * @param {PIXI.DisplayObject} first - The old DisplayObject. - * @param {PIXI.DisplayObject} second - The new DisplayObject. - * @return {Flippable} A reference to the flippable for chaining. - */ - switchDisplayObject(first, second) { - if (first && second && first.parent) { - const parent = first.parent; - const index = parent.getChildIndex(first); - parent.addChildAt(second, index); - parent.removeChild(first); - } - - return this - } - } - - /** - * - */ - class Popover extends PIXI.Graphics { - - constructor({title = null, text = null, x = 0, y = 0, placement = 'top', width = 250, titleStyle = {}, textStyle = {fontSize: '1.6em'}} = {}) { - super(); - - this.opts = {title, text, x, y, placement, width, titleStyle, textStyle}; - - this.padding = 12; - - let style = { - fontFamily: 'Arial', - fontSize: '2em', - stroke: '#f6f6f6', - strokeThickness: 3, - wordWrap: true, - wordWrapWidth: width - (this.padding * 2) - }; - - this.titleTextStyle = new PIXI.TextStyle(Object.assign({}, style, titleStyle)); - this.textTextStyle = new PIXI.TextStyle(Object.assign({}, style, textStyle)); - - if (title || text) { - this.setup(); - this.draw(); - this.positioning(); - } - } - - setup() { - this.removeChildren(); - - if (this.opts.title) { - this.titleText = new PIXI.Text(this.opts.title, this.titleTextStyle); - this.titleText.position.set(this.padding, this.padding); - this.addChild(this.titleText); - } - - this.titleY = this.titleText ? this.titleText.y : 0; - this.titleHeight = this.titleText ? this.titleText.height : 0; - - if (this.opts.text) { - this.textText = new PIXI.Text(this.opts.text, this.textTextStyle); - this.textText.position.set(this.padding, this.titleY + this.titleHeight + this.padding); - this.addChild(this.textText); - } - - this.textY = this.textText ? this.textText.y : 0; - this.textHeight = this.textText ? this.textText.height : 0; - } - - close() { - this.parent.removeChild(this); - } - - draw() { - this.clear(); - this.beginFill(0xffffff, 1); - this.lineStyle(1, 0x282828, .5); - - // Draw rounded rectangle - const height = this.height + this.padding; - this.drawRoundedRect(0, 0, this.opts.width, height, 8); - - // Draw anchor - this.drawAnchor(this.opts.placement); - - // Draw title background - if (this.opts.title) { - this.lineStyle(0); - this.beginFill(0xf7f7f7, 1); - let x = 1; - let y = this.titleText.x + this.titleText.height + (this.padding / 2); - this.moveTo(x, y); - y = 9; - this.lineTo(x, y); - this.quadraticCurveTo(x, y - 8, x + 8, y - 8); - x += this.opts.width - 7; - y -= 8; - this.lineTo(x, y); - this.quadraticCurveTo(x + 5, y, x + 5, y + 8); - x += 5; - y += this.titleText.x + this.titleText.height + (this.padding / 2); - this.lineTo(x, y); - if (this.opts.text) { - x = 1; - this.lineTo(x, y); - } else { - this.quadraticCurveTo(x, y, x - 5, y + 4); - x = 6; - y += 4; - this.lineTo(x, y); - this.quadraticCurveTo(x, y, x - 5, y - 4); - } - } - - this.endFill(); - } - - drawAnchor(placement) { - - let x = 0; - let y = 0; - - switch (placement) { - case 'bottom': - if (this.opts.title) { - this.beginFill(0xf7f7f7, 1); - } - x = (this.width / 2) - 10; - y = 1; - this.moveTo(x, y); - x += 10; - y -= 10; - this.lineTo(x, y); - x += 10; - y += 10; - this.lineTo(x, y); - break - case 'right': - x = 1; - y = (this.height / 2) - 10; - if (this.titleY + this.titleHeight > y) { - this.beginFill(0xf7f7f7, 1); - } - this.moveTo(x, y); - x -= 10; - y += 10; - this.lineTo(x, y); - x += 10; - y += 10; - this.lineTo(x, y); - break - case 'left': - x = this.width - 2; - y = (this.height / 2) - 10; - if (this.titleY + this.titleHeight > y) { - this.beginFill(0xf7f7f7, 1); - } - this.moveTo(x, y); - x += 10; - y += 10; - this.lineTo(x, y); - x -= 10; - y += 10; - this.lineTo(x, y); - break - default: - x = (this.width / 2) - 10; - y = this.height - 2; - this.moveTo(x, y); - x += 10; - y += 10; - this.lineTo(x, y); - x += 10; - y -= 10; - this.lineTo(x, y); - break - } - } - - positioning() { - - const x = this.opts.x; - const y = this.opts.y; - - switch (this.opts.placement) { - case 'bottom': - this.position.set(x - (this.width / 2), y + 10); - break - case 'right': - this.position.set(x, y - (this.height / 2)); - break - case 'left': - this.position.set(x - this.width, y - (this.height / 2)); - break - default: - this.position.set(x - (this.width / 2), y - this.height); - break - } - } - } - - /** A container for scatter objects, which uses a single InteractionMapper - * for all children. This reduces the number of registered event handlers - * and covers the common use case that multiple objects are scattered - * on the same level. - */ - class ScatterContainer extends PIXI.Graphics { - - /** - * @constructor - * @param {PIXI.Renderer} renderer - PIXI renderer, needed for hit testing - * @param {Bool} stopEvents - Whether events should be stopped or propagated - * @param {Bool} claimEvents - Whether events should be marked as claimed - * if findTarget return as non-null value. - * @param {PIXI.Container} container - A container for the scatter - * @param {Bool} showBounds - Show bounds for debugging purposes. - * @param {Bool} showTouches - Show touches and pointer for debugging purposes. - * @param {Color} backgroundColor - Set background color if specified. - * @param {PIXIApp} app - Needed if showBounds is true to register - * update handler. - */ - constructor(renderer, { - stopEvents = true, - claimEvents = true, - container = null, - showBounds = false, - showPolygon = false, - showTouches = false, - backgroundColor = null, - app = window.app - } = {}) { - super(); - this.container = container; - if (this.container) - this.containerDimensions = { - x: this.container.width, - y: this.container.height - }; - this.backgroundWidth = null; - this.backgroundHeight = null; - this.app = app; - this.renderer = renderer; - this.stopEvents = stopEvents; - this.claimEvents = claimEvents; - this.delegate = new InteractionMapper$1(this.eventReceiver, this); - this.showBounds = showBounds; - this.showTouches = showTouches; - this.showPolygon = showPolygon; - this.backgroundColor = backgroundColor; - if (showBounds || showTouches || showPolygon) { - //console.log("Show TOUCHES!!!") - this.app.ticker.add((delta) => this.update(delta), this); - } - if (backgroundColor) { - this.updateBackground(); - } - } - - updateBackground() { - this.clear(); - let rect = this.bounds; - this.beginFill(this.backgroundColor, 1); - this.drawRect(0, 0, rect.width, rect.height); - this.endFill(); - } - - get eventReceiver() { - return this.renderer.plugins.interaction.interactionDOMElement - } - - get bounds() { - let x = 0; - let y = 0; - // @container: We need to call the constant values, as the container - // gets resized, when a child moves outside the original boundaries. - let w = (this.container) ? this.containerDimensions.x : (this.backgroundWidth || this.app.width); - let h = (this.container) ? this.containerDimensions.y : (this.backgroundHeight || this.app.height); - - if (this.app.fullscreen && this.app.monkeyPatchMapping) { - let fixed = this.mapPositionToPoint({ x: w, y: 0 }); - if (fixed.x < w) { - w = fixed.x; - } - if (fixed.y > 0) { - y += fixed.y; - h -= fixed.y; - } - } - return new PIXI.Rectangle(x, y, w, h) - } - - get center() { - let r = this.bounds; - return { x: r.width / 2, y: r.height / 2 } - } - - get polygon() { - let r = this.bounds; - let w2 = r.width / 2; - let h2 = r.height / 2; - let center = { x: w2, y: h2 }; - let polygon = new Polygon(center); - polygon.addPoint({ x: -w2, y: -h2 }); - polygon.addPoint({ x: w2, y: -h2 }); - polygon.addPoint({ x: w2, y: h2 }); - polygon.addPoint({ x: -w2, y: h2 }); - return polygon - } - - update(dt) { - this.clear(); - this.lineStyle(1, 0x0000FF); - if (this.showBounds) { - for (let child of this.children) { - if (child.scatter) { - //let {x, y, width, height} = child.scatter.throwBounds() - // new PIXI.Rectangle(x, y, width, height) - this.drawShape(child.scatter.bounds); - let center = child.scatter.center; - this.drawCircle(center.x, center.y, 4); - this.drawCircle(child.scatter.x, child.scatter.y, 4); - } - } - this.lineStyle(2, 0x0000FF); - this.drawShape(this.bounds); - } - if (this.showPolygon) { - this.lineStyle(2, 0xFF0000); - for (let child of this.children) { - if (child.scatter) { - let polygon = child.scatter.polygon; - let shape = new PIXI.Polygon(polygon.flatAbsolutePoints()); - shape.close(); - this.drawShape(shape); - } - } - } - if (this.showTouches) { - let current = this.delegate.interaction.current; - for (let [key, point] of current.entries()) { - let local = this.mapPositionToPoint(point); - this.drawCircle(local.x, local.y, 12); - } - } - } - - capture(event) { - if (this.stopEvents) - Events$1.stop(event); - return true - } - - fakeInteractionEvent(point, key) { - return { data: { global: point, key: key } } - } - - findHitScatter(data, displayObject, hit) { - // if (hit) { - // console.log("findHitScatter", displayObject) - // } - if (hit && this.hitScatter === null && typeof (displayObject) != undefined) { - this.hitScatter = (displayObject.scatter) ? displayObject.scatter : null; - } - } - - mapPositionToPoint(point, element = null) { - // In case of nested scatters we get an additional parameter that - // contains the found scatter - let local = new PIXI.Point(); - let interactionManager = this.renderer.plugins.interaction; - interactionManager.mapPositionToPoint(local, point.x, point.y); - if (element instanceof DisplayObjectScatter && element.displayObject.parent != null) { - return element.displayObject.parent.toLocal(local) - } - return local - } - - /** - * New method hitTest implemented (in InteractionManager, since 4.5.0). - * See https://github.com/pixijs/pixi.js/pull/3878 - */ - findTarget(event, local, global) { - if (event.claimedByScatter) { - return null - } - this.hitScatter = null; - let interactionManager = this.renderer.plugins.interaction; - let fakeEvent = this.fakeInteractionEvent(local); - interactionManager.processInteractive(fakeEvent, - this, - this.findHitScatter.bind(this), true); - if (this.claimEvents) - event.claimedByScatter = this.hitScatter; - return this.hitScatter - } - - findTargetNew(event, local, global) { - // UO: still problematic. Does not find non interactive elements - // which are needed for some stylus applications - if (event.claimedByScatter) { - return null - } - this.hitScatter = null; - let interactionManager = this.renderer.plugins.interaction; - let displayObject = interactionManager.hitTest(local, this); - if (displayObject != null && displayObject.scatter != null) - this.hitScatter = displayObject.scatter; - if (this.claimEvents) - event.claimedByScatter = this.hitScatter; - return this.hitScatter - } - - - onStart(event, interaction) { - - } - - onMove(event, interaction) { - - } - - onEnd(event, interaction) { - for (let key of interaction.ended.keys()) { - let point = interaction.ended.get(key); - if (interaction.isLongPress(key)) { - this.onLongPress(key, point); - } - if (interaction.isTap(key)) { - this.onTap(key, point); - } - } - } - - onTap(key, point) { - console.info('ScatterContainer.onTap'); - } - - onLongPress(key, point) { - console.info('ScatterContainer.onLongPress'); - } - - bringToFront(displayObject) { - this.addChild(displayObject); - } - - layout(width, height) { - this.backgroundWidth = width; - this.backgroundHeight = height; - if (this.backgroundColor) { - this.updateBackground(); - } - - } - } - - /** A wrapper for child elements of a ScatterContainer. Can be used - * to combine scattered objects with non-scattered objects. Any - * PIXI.DisplayObject can be wrapped. - */ - class DisplayObjectScatter extends AbstractScatter { - - constructor(displayObject, renderer, - { x = null, y = null, - minScale = 0.1, - maxScale = 1.0, - startScale = 1.0, - autoBringToFront = true, - translatable = true, scalable = true, rotatable = true, resizable = false, - movableX = true, - movableY = true, - throwVisibility = 44, - throwDamping = 0.95, - autoThrow = true, - rotationDegrees = null, - rotation = null, - overdoScaling = 1.5, - onTransform = null, - onThrowFinished = null } = {}) { - // For the simulation of named parameters, - // see: http://exploringjs.com/es6/ch_parameter-handling.html - super({ - overdoScaling, - minScale, maxScale, - startScale, - autoBringToFront, - translatable, scalable, rotatable, resizable, - movableX, movableY, throwVisibility, throwDamping, - autoThrow, - onThrowFinished, - rotationDegrees, rotation, - onTransform - }); - this.displayObject = displayObject; - this.displayObject.scatter = this; - this.renderer = renderer; - this.scale = startScale; - this.rotationDegrees = this.startRotationDegrees; - - // Only set x and y if they are specified. - // Otherwise the displayobject gets corrupted. - if (x != null) this.x = x; - if (y != null) this.y = y; - } - - getWorldScatter() { - return this - } - - /** Returns geometry data as object. **/ - getState() { - return { - scale: this.scale, - x: this.x, - y: this.y, - rotation: this.rotation - } - } - - setup() { - this.setupMouseWheelInteraction(); - } - - roundPixel(value) { - // UO: Should be obsolete because Renderer supports roundPixels by default - return value - let res = this.renderer.resolution; - return Math.round(value * res) / res - } - - get container() { - // return this.displayObject.parent - let obj = this.displayObject; - while (obj.parent != null && !(obj.parent instanceof ScatterContainer)) - obj = obj.parent; - return obj.parent - } - - get x() { - return this.position.x - } - - set x(value) { - this.position.x = value; - } - - get y() { - return this.position.y - } - - set y(value) { - this.position.y = value; - } - - get polygon() { - let polygon = new Polygon(this.center); - let w2 = this.width / 2; - let h2 = this.height / 2; - polygon.addPoint({ x: -w2, y: -h2 }); - polygon.addPoint({ x: w2, y: -h2 }); - polygon.addPoint({ x: w2, y: h2 }); - polygon.addPoint({ x: -w2, y: h2 }); - polygon.rotate(this.rotation); - return polygon - } - - get containerBounds() { - return this.container.bounds - } - - get containerPolygon() { - let container = this.container; - if (container == null) return null - return container.polygon - } - - get position() { - return this.displayObject.position - } - - set position(value) { - this.displayObject.position = value; - } - - get scale() { - return this.displayObject.scale.x - } - - set scale(value) { - this.displayObject.scale.x = value; - this.displayObject.scale.y = value; - } - - get width() { - return this.displayObject.width - } - - get height() { - return this.displayObject.height - } - - get bounds() { - return this.displayObject.getBounds() - } - - get pivot() { - return this.displayObject.pivot - } - - get rotation() { - return this.displayObject.rotation - } - - set rotation(value) { - this.displayObject.rotation = value; - } - - get rotationDegrees() { - return Angle.radian2degree(this.displayObject.rotation) - } - - set rotationDegrees(value) { - this.displayObject.rotation = Angle.degree2radian(value); - } - - get center() { - let w2 = this.width / 2; - let h2 = this.height / 2; - let dist = Math.sqrt(w2 * w2 + h2 * h2); - let angle = Points.angle({ x: w2, y: h2 }, { x: 0, y: 0 }); - let p = this.displayObject.x; - let c = Points.arc(this.position, this.rotation + angle, dist); - return c // Points.subtract(c, this.pivot) - } - - get rotationOrigin() { - // In PIXI the default rotation and scale origin is the position - return this.position // Points.add(this.position, this.pivot) - } - - mapPositionToContainerPoint(point) { - // UO: We need the coordinates related to this scatter in case - // of nested scatters - if (this.container != null) - return this.container.mapPositionToPoint(point, this) - return point - } - - capture(event) { - return true - } - - bringToFront() { - if (this.autoBringToFront) { - if (this.displayObject.parent instanceof ScatterContainer) { - let scatterContainer = this.displayObject.parent; - scatterContainer.bringToFront(this.displayObject); - } else if (this.displayObject.parent != null && this.displayObject.parent.scatter) { - this.displayObject.parent.scatter.toFront(this.displayObject); - } - } - } - - toFront(displayObject) { - this.displayObject.addChild(displayObject); - } - - validScale(scale) { - scale = Math.max(scale, this.minScale); - scale = Math.min(scale, this.maxScale); - return scale - } - } - - /** - * - */ - class Command extends PIXI.Graphics { - /*** Abstract base class for record, play, and stop commands. ***/ - constructor(tools, selectedColor, shape) { - super(); - this.tools = tools; - this.shape = shape; - this.selected = false; - this.disabled = false; - this.selectedColor = selectedColor; - this.draw(); - this.setup(); - } - - setup() { - } - - draw() { - this.clear(); - var color = (this.selected) ? this.selectedColor : 0xFFFFFF; - this.lineStyle(0); - this.beginFill(color, 1); - this.drawShape(this.shape); - this.endFill(); - } - - select() { - this.selected = true; - this.draw(); - } - - deselect() { - this.selected = false; - this.draw(); - } - - toggle() { - this.selected = !this.selected; - this.draw(); - } - - stop() { - this.selected = false; - this.draw(); - } - } - - class RecordCommand extends Command { - /*** Records events for later replay. ***/ - setup() { - this.recorder = new EventRecorder(); - } - - toggle() { - super.toggle(); - if (!this.selected) { - this.recorder.stopRecording(); - } - } - - recordEvent(event) { - this.recorder.record(event); - } - - normalize(value, limit) { - return value / limit - } - - normalizeX(value) { - return this.normalize(value, window.innerWidth) - } - - normalizeY(value) { - return this.normalize(value, window.innerHeight) - } - - whileNotStopped() { - return this.tools.play.selected - } - - startReplay() { - let whileCondition = this.whileNotStopped.bind(this); - this.recorder.startReplay(whileCondition, () => this.tools.play.stop()); - } - } - - class PlayCommand extends Command { - /*** Plays recorded events. ***/ - toggle() { - super.toggle(); - if (this.selected && this.tools.record.recorder.recorded.length > 0) { - this.tools.startReplay(); - } - } - } - - class StopCommand extends Command { - /*** Stops recording and playing. ***/ - toggle() { - super.toggle(); - this.tools.record.stop(); - this.tools.play.stop(); - setTimeout(this.deselect.bind(this), 500); - } - } - - class RecorderTools extends PIXI.Container { - - constructor(renderer) { - super(renderer); - this.renderer = renderer; - this.setupToolbar(); - this.replayRate = 100.0; - this.onReset = null; - this.touchGraphics = new PIXI.Graphics(); - this.showTouches(); - } - - setup(container) { - // Since this delegate might shadow another delegate, we mus avoid - // capturing PointerEvents. - this.delegate = new InteractionMapper(container, this, { capturePointerEvents: false }); - } - - findTarget(event, local, global) { - return this - } - - setupToolbar() { - this.toolbar = new PIXI.Graphics(); - this.record = new RecordCommand(this, 0xCC0000, new PIXI.Circle(0, 0, 16)); - this.play = new PlayCommand(this, 0x0000CC, new PIXI.Polygon(0, 16, - 32, 16 + 16, - 0, 16 + 32, - 0, 16)); - this.stop = new StopCommand(this, 0x0000CC, - new PIXI.Rectangle(0, 0, 32, 32)); - this.toolbar.addChild(this.record).position.set(44, 48); - this.toolbar.addChild(this.play).position.set(44 + 44, 16); - this.toolbar.addChild(this.stop).position.set(44 + 44 + 44 + 16, 32); - this.updateToolbar(); - this.addChild(this.toolbar); - } - - updateToolbar() { - var graphics = this.toolbar; - graphics.clear(); - graphics.beginFill(0x000000, 0.5); - graphics.lineStyle(2, 0xFFFFFF, 1); - graphics.drawRoundedRect(16, 16, 44 * 4 + 8, 64, 8); - graphics.endFill(); - } - - onMouseWheel(event) { - console.log('onMouseWheel missing'); - } - - onPress(point) { - if (this.record.containsPoint(point)) { - this.record.toggle(); - } - if (this.play.containsPoint(point)) { - this.play.toggle(); - } - if (this.stop.containsPoint(point)) { - this.stop.toggle(); - if (this.onReset) { - this.onReset(); - } - } - } - - mapPositionToPoint(point) { - let local = new PIXI.Point(); - this.renderer.plugins.interaction.mapPositionToPoint(local, point.x, point.y); - return local - } - - extractLocal(event) { - return this.mapPositionToPoint(Events.extractPoint(event)) - } - - capture(event) { - if (typeof event.mouseDownSubstitute != 'undefined') - return false - return true - } - - startReplay() { - if (this.onReset) { - this.onReset(); - } - this.record.startReplay(); - } - - showTouches() { - this.addChild(this.touchGraphics); - } - - recordEvent(event) { - if (this.record.selected) { - this.record.recordEvent(event); - } - } - - onStart(event, interaction) { - let local = this.extractLocal(event); - if (!this.toolbar.containsPoint(local)) { - this.recordEvent(event); - this.updateTouchGraphics(interaction); - } - } - - onMove(event, interaction) { - let local = this.extractLocal(event); - if (!this.toolbar.containsPoint(local)) { - this.recordEvent(event); - this.updateTouchGraphics(interaction); - } - } - - onEnd(event, interaction) { - let local = this.extractLocal(event); - if (this.toolbar.containsPoint(local)) { - this.onPress(local); - } - else { - this.recordEvent(event); - this.updateTouchGraphics(interaction); - } - } - - updateTouchGraphics(interaction) { - let current = interaction.current; - let graphics = this.touchGraphics; - if (graphics != null) { - graphics.clear(); - for (let key of current.keys()) { - if (interaction.ended.has(key)) { - continue - } - let p = current.get(key); - if (key == 'mouse') { - graphics.beginFill(0xCC0000, 0.5); - } else { - graphics.beginFill(0xCCCCCC, 0.5); - } - graphics.drawCircle(p.x, p.y, 20); - } - graphics.endFill(); - } - } - } - - class AppTest extends PIXIApp { - - constructor(canvas, container) { - super({ view: canvas, backgroundColor: 0x000000 }); - this.container = container; - } - - sceneFactory() { - return new RecorderTools(this.renderer) - } - - setup() { - super.setup(); - this.scene.setup(this.container); - } - - run(reset = null) { - this.scene.onReset = reset; - console.log('Running AppTest'); - return this - } - } - - /** - * Defines usefull default text styles. - */ - class FontInfo { - - static get small() { - return app.theme.textStyleSmall - } - - static get normal() { - return app.theme.textStyle - } - - static get centered() { - return Object.assign({}, app.theme.textStyle, { align: 'center' }) - } - } - - /** - * Static methods to support hyphenation of lines. - * - * @class Hypenate - */ - class Hypenate { - - static splitPart(part) { - let parts = part.split('-'); - if (parts.length == 1) - return [part] - let result = []; - let last = parts.pop(); - for (let p of parts) { - result.push(p + '-'); - } - result.push(last); - return result.filter(p => p.length > 0) - } - - static splitWord(word) { - if (typeof (language) == 'undefined') { - if (word.indexOf('-') > -1) { - return word.split('-') - } - return [word] - } - let parts = language.hyphenate(word); - let result = []; - for (let part of parts) { - for (let splitted of this.splitPart(part)) { - result.push(splitted); - } - } - return result - } - - static abbreviateLine(label, style, width) { - const pixiStyle = new PIXI.TextStyle(style); - let metrics = PIXI.TextMetrics.measureText(label, pixiStyle); - while(metrics.width > width && label.length > 3) { - label = label.slice(0, label.length-1); - metrics = PIXI.TextMetrics.measureText(label, pixiStyle); - } - label = label.slice(0, label.length-1); - return label + '…' - } - - static splitLine(line, pixiStyle, width, space, minus) { - let x = 0; - let result = ''; - let words = line.split(' '); - for (let word of words) { - let wordMetrics = PIXI.TextMetrics.measureText(word, pixiStyle); - if (x + wordMetrics.width >= width) { - let parts = this.splitWord(word); - let newWord = ''; - if (parts.length == 1) { - newWord += '\n' + word + ' '; - x = wordMetrics.width + space.width; - } - else { - let first = true; - let lastPart = ''; - for (let part of parts) { - let partMetrics = PIXI.TextMetrics.measureText(part, pixiStyle); - if (x + partMetrics.width + space.width > width) { - newWord += ((first || lastPart.endsWith('-')) ? '\n' : '-\n') + part; - x = partMetrics.width; - } - else { - newWord += part; - x += partMetrics.width; - } - lastPart = part; - first = false; - } - x += space.width; - } - result += newWord + ' '; - } - else { - result += word + ' '; - x += wordMetrics.width + space.width; - } - } - return result - } - - /** - * Main method and entry point for text hyphenation - * - * @static - * @param {*} text - * @param {*} style - * @param {*} width - * @returns - * @memberof Hypenate - */ - static splitLines(text, style, width) { - const pixiStyle = new PIXI.TextStyle(style); - const lines = text.split('\n'); - const space = PIXI.TextMetrics.measureText(' ', pixiStyle); - const minus = PIXI.TextMetrics.measureText('-', pixiStyle); - let result = []; - for (let line of lines) { - result.push(this.splitLine(line, pixiStyle, width, space, minus)); - } - return result.join('\n') - } - } - - /** - * A specialization of the PIXI.Graphics class that allows to - * resuse and place labels across different layout variants - * - * @export - * @class LabeledGraphics - * @extends {PIXI.Graphics} - */ - class LabeledGraphics extends PIXI.Graphics { - - /** - * Creates an instance of LabeledGraphics and defines a local label cache. - * - * @memberof LabeledGraphics - */ - constructor() { - super(); - this.labels = new Map(); - } - - _createText(label, fontInfo) { - return new PIXI.Text(label, fontInfo) - } - - - /** - * Main additional method. Ensures that a text object is created that is cached - * under the given key. - * - * @param {*} key - The cache key - * @param {*} label - The label to show - * @param {*} [attrs={}] - Defines attributes of the text object. - * align: 'right', 'left', or 'center' - * justify: 'top', 'bottom', or 'center' - * maxLines: {integer} truncates the text and adds ellipsis - * maxHeight: {number} truncates text that needs more space and adds ellipsis - * maxWidth: {number} word wraps text using hyphenation if possible - * @param {*} [fontInfo=FontInfo.normal] - Defines PIXI.TextStyle attributes - * @returns {PIXI.Text} instance - * @memberof LabeledGraphics - */ - ensureLabel(key, label, attrs = {}, fontInfo = FontInfo.normal) { - - if (attrs.maxWidth && attrs.maxLines == 1) { - label = Hypenate.abbreviateLine(label, fontInfo, attrs.maxWidth); - } - else { - if (attrs.maxWidth) { - label = Hypenate.splitLines(label, fontInfo, attrs.maxWidth); - } - if (attrs.maxLines) { - label = this.truncateLabel(label, fontInfo, attrs.maxLines); - } - if (attrs.maxHeight) { - let styleInfo = new PIXI.TextStyle(fontInfo); - let metrics = PIXI.TextMetrics.measureText(label, styleInfo); - let maxLines = Math.max(attrs.maxHeight / metrics.lineHeight, 1); - label = this.truncateLabel(label, fontInfo, maxLines); - } - } - - if (!this.labels.has(key)) { - let text = this._createText(label, fontInfo); - this.labels.set(key, text); - this.addChild(text); - } - let text = this.labels.get(key); - for (let k in attrs) { - text[k] = attrs[k]; - } - if (label != text.text) - text.text = label; - // We do not follow the flexbox jargon and use align for x and justify for y axis - // This deviation is needed to ensure backward compatability - switch (attrs.justify || null) { - case 'top': - text.anchor.y = 0; - break - case 'bottom': - text.anchor.x = 1; - break - default: - text.anchor.y = 0.5; - break - } - switch (attrs.align) { - case 'right': - text.anchor.x = 1; - break - case 'center': - text.anchor.x = 0.5; - break - default: - text.anchor.x = 0; - break - } - text.visible = true; - return text - } - - /** - * Private method that truncates the text and adds an ellipsis if there are more lines - * than wanted - * - * @param {*} text - * @param {*} style - * @param {*} [maxLines=Infinity] - * @returns - * @memberof LabeledGraphics - */ - truncateLabel(text, style, maxLines = Infinity) { - if (maxLines === Infinity) { - return text - } - const { wordWrapWidth } = style; - const pixiStyle = new PIXI.TextStyle(style); - const { lines } = PIXI.TextMetrics.measureText(text, pixiStyle); - let newText = text; - if (lines.length > maxLines) { - const truncatedLines = lines.slice(0, maxLines); - const lastLine = truncatedLines[truncatedLines.length - 1]; - const words = lastLine.split(' '); - const wordMetrics = PIXI.TextMetrics.measureText(`\u00A0\n...\n${words.join('\n')}`, pixiStyle); - const [spaceLength, dotsLength, ...wordLengths] = wordMetrics.lineWidths; - const { text: newLastLine } = wordLengths.reduce((data, wordLength, i) => { - if (data.length + wordLength + spaceLength >= wordWrapWidth) { - return { ...data, length: wordWrapWidth } - } - return { - text: `${data.text}${i > 0 ? ' ' : ''}${words[i]}`, - length: data.length + wordLength + spaceLength, - }; - }, { text: '', length: dotsLength }); - truncatedLines[truncatedLines.length - 1] = `${newLastLine}...`; - newText = truncatedLines.join('\n'); - } - return newText - } - - /** - * Returns the label for the given key. - * - * @param {*} key - * @returns - * @memberof LabeledGraphics - */ - getLabel(key) { - return this.labels.get(key) - } - - /** - * Hides the label with the given key. - * @param {*} key - * @memberof LabeledGraphics - */ - hideLabel(key) { - let label = this.labels.get(key); - if (label) { - label.visible = false; - } - } - - /** - * Removes the label with the given key. - * @param {*} key - * @memberof LabeledGraphics - */ - removeLabel(key) { - let label = this.labels.get(key); - this.labels.delete(key); - label.destroy(); - } - - - /** - * Ensures that labels are hidden on clear. - * - * @memberof LabeledGraphics - */ - clear() { - super.clear(); - for (let key of this.labels.keys()) { - this.hideLabel(key); - } - } - - /** - * Logs debugging infos - * - * @memberof LabeledGraphics - */ - debugInfos() { - console.log({ size: this.labels.size, labels: this.labels }); - } - } - - - const labelCache = new Map(); - - function getTexture(label, fontInfo = FontInfo.normal) { - let key = label + fontInfo.fontFamily + fontInfo.fontSize; - - if (labelCache.has(key)) { - return labelCache.get(key) - } - let expandedFont = Object.assign({}, fontInfo); - expandedFont.fontSize *= window.devicePixelRatio; - let text = new PIXI.Text(label, expandedFont); - text.updateText(); - labelCache.set(key, text.texture); - return text.texture - } - - class SpriteLabel extends PIXI.Sprite { - - constructor(label, fontInfo) { - let texture = getTexture(label, fontInfo); - super(texture); - this.label = label; - this.fontInfo = fontInfo; - this.scale.set(0.8 / window.devicePixelRatio); - } - - set text(label) { - this.label = label; - this.texture = getTexture(label, this.fontInfo); - } - - get text() { - return this.label - } - } - - class BitmapLabeledGraphics extends LabeledGraphics { - - _createText(label, fontInfo) { - let texture = getTexture(label, fontInfo); - return new SpriteLabel(texture) - } - - } - - class Ticks { - - get reservedPrefixes() { - return ['decade', 'year', 'month', 'day', 'hour', 'minute', 'second'] - } - - get minWidth() { - return 10 - } - - format(available) { - return { year: '2-digit', timeZone: 'UTC' } - } - - get minLabelWidth() { - return 44 - } - - get formatKey() { - return this.key - } - - dateKey(date) { - return this.key + date.getFullYear() - } - - *iter(start, end) { - let date = this.iterStart(start); - while (date <= end) { - yield date; - date = this.next(date); - } - yield date; - } - - *iterRanges(range) { - for (let date of this.iter(range.start, range.end)) { - let next = this.next(date); - yield { start: date, end: next }; - } - } - - selectedRange(timeline, info) { - let first = null; - let last = null; - let visibleFirst = null; - let visibleLast = null; - let units = 0; - for (let { start, end } of this.iterRanges(info)) { - if (timeline.visibleRange(start, end)) { - if (first == null) { - first = start; - } - last = end; - } - if (timeline.visibleDate(start) && timeline.visibleDate(end)) { - units += 1; - if (visibleFirst == null) { - visibleFirst = start; - } - visibleLast = end; - } - } - if (first == null) - return info - return { start: first, end: last, visibleStart: visibleFirst, visibleEnd: visibleLast, units: units } - } - - drawTick(timeline, x, y, date) { - let visible = date > timeline.start && date < timeline.end; - if (!visible) - return false - timeline.drawTick(x); - return true - } - - toLocaleString(date, format) { - return date.toLocaleDateString('de', format) - } - - draw(timeline, range, width, height, available, format, nextFormat, level) { - let first = null; - let last = null; - let keyedFormat = (format) ? format[this.formatKey] : null; - let keyedNextFormat = (nextFormat) ? nextFormat[this.formatKey] : null; - let redundant = (nextFormat) ? this.formatKey in nextFormat : false; - let fullyRedundant = keyedFormat != null && keyedFormat == keyedNextFormat; - let y = timeline.getY(); - - for (let { start, end } of this.iterRanges(range)) { - let x = timeline.toX(start); - let xx = x; - let yy = y + timeline.tickLabelOffset(-1); - if (this.drawTick(timeline, x, y, start) && format) { - let key = this.dateKey(start); - let text = this.toLocaleString(start, format); - let align = 'center'; - let downTick = false; - if (nextFormat) { - yy = y + timeline.tickLabelOffset(-1, 1); - align = 'left'; - timeline.drawTick(x, 4.2); - let nextX = timeline.toX(end) - 100; - if (x < 0 && nextX > -100 && !redundant) { - xx = Math.min(4, nextX); - } - else { - xx -= 2; - } - downTick = true; - } - else if (level > 0) { - xx = x + available / 2; - } - else { - downTick = true; - } - if (!fullyRedundant) { - timeline.ensureLabel(key, text, - { x: xx, y: yy, align }, - FontInfo.small); - } - if (downTick) timeline.drawTick(x, -1); - } - if (timeline.visibleRange(start, end)) { - if (first == null) - first = start; - last = end; - } - } - if (first == null) - return null - return { start: first, end: last } - } - } - - class DecadeTicks extends Ticks { - - get milliseconds() { - return 10 * 365 * 24 * 60 * 60 * 1000 - } - - format(available) { - return { year: 'numeric', timeZone: 'UTC' } - } - - selection(timeline, dates, selected) { - return dates - } - - get key() { - return 'decade' - } - - get formatKey() { - return 'year' - } - - iterStart(start) { - let modulo = start.getFullYear() % 10; - let year = start.getFullYear() - modulo; - return Dates.create(year, 0, 1) - } - - next(decade) { - return Dates.nextYear(decade, 10) - } - } - - class YearTicks extends Ticks { - - get milliseconds() { - return 365 * 24 * 60 * 60 * 1000 - } - - format(available) { - if (available < 44) - return { year: '2-digit', timeZone: 'UTC' } - return { year: 'numeric', timeZone: 'UTC' } - } - - get minLabelWidth() { - return 22 - } - - get key() { - return 'year' - } - - iterStart(start) { - return Dates.create(start.getFullYear(), 0, 1) - } - - next(year) { - return Dates.nextYear(year) - } - } - - class MonthTicks extends Ticks { - - get milliseconds() { - return (365 / 12) * 24 * 60 * 60 * 1000 - } - - format(available) { - let format = { month: 'narrow', timeZone: 'UTC' }; - if (available > 44) - format.month = 'short'; - if (available > 66) - format.year = '2-digit'; - if (available > 100) { - format.month = 'long'; - format.year = 'numeric'; - } - return format - } - - get minLabelWidth() { - return 32 - } - - get key() { - return 'month' - } - - dateKey(date) { - return this.key + date.getFullYear() + date.getMonth() - } - - iterStart(start) { - return Dates.create(start.getFullYear(), start.getMonth(), 1) - } - - next(month) { - return Dates.nextMonth(month) - } - } - - class DayTicks extends Ticks { - - get milliseconds() { - return 24 * 60 * 60 * 1000 - } - - format(available) { - let format = { day: 'numeric', timeZone: 'UTC' }; - if (available > 44) - format.month = 'short'; - if (available > 100) { - format.month = 'long'; - format.year = '2-digit'; - } - if (available > 150) { - format.weekday = 'short'; - } - if (available > 200) { - format.year = 'numeric'; - format.weekday = 'long'; - } - return format - } - - get key() { - return 'day' - } - - dateKey(date) { - return this.key + date.getFullYear() + date.getMonth() + date.getDate() - } - - iterStart(start) { - return Dates.create(start.getFullYear(), start.getMonth(), start.getDate()) - } - - next(date) { - return Dates.nextDay(date) - } - } - - class TimeTicks { - - constructor(...ticks) { - this.ticks = ticks; - } - - selectedRange(timeline) { - let info = { start: timeline.start, end: timeline.end, units: 0 }; - for (let ticks of this.ticks) { - info = ticks.selectedRange(timeline, info); - if (info.units > 1) { - timeline.selection = [info.visibleStart, info.visibleEnd]; - return - } - } - timeline.selection = [info.start, info.end]; - } - - draw(timeline, width, height) { - let range = timeline; - let start = timeline.toX(range.start); - let end = timeline.toX(range.end); - let size = end - start; - let duration = timeline.end - timeline.start; - let formats = new Map(); - let nextFormats = new Map(); - let availables = new Map(); - let previous = null; - let visible = []; - for (let ticks of this.ticks) { - let amount = ticks.milliseconds / duration; - let available = amount * size; - availables.set(ticks, available); - if (available < ticks.minWidth) - break - formats.set(ticks, (available < ticks.minLabelWidth) ? null : ticks.format(available)); - nextFormats.set(previous, formats.get(ticks)); - previous = ticks; - visible.push(ticks); - } - let level = 0; - for (let ticks of visible) { - if (range == null) - return - range = ticks.draw(timeline, range, width, height, - availables.get(ticks), - formats.get(ticks), - nextFormats.get(ticks), level); - level += 1; - } - } - } - - class ColorRanges { - - constructor(label, color, ranges) { - this.label = label; - this.color = color; - this.ranges = ranges; - } - - draw(timeline, width, height, size = 12) { - let minimum = 1 / Capabilities.devicePixelRatio; - let h2 = size; - timeline.lineStyle(size, this.color); - for (let range of this.ranges) { - if (range.to === null) { - range.to = Dates.nextDay(range.from); - } - let start = timeline.toX(range.from); - let end = timeline.toX(range.to); - if (end < start + minimum) { - end = start + minimum; - } - timeline.moveTo(start, h2); - timeline.lineTo(end, h2); - } - } - } - - class Timeline extends BitmapLabeledGraphics { - - constructor(width, height, { ticks = null, - baseLine = 0.5, showRange = true } = {}) { - super(); - this.wantedWidth = width; - this.wantedHeight = height; - this.extraLeft = 0; - this.extraRight = 0; - this.inset = 5; - this.showRange = showRange; - this.baseLine = baseLine; - this.tickHeight = 4; - this.zoom = 1; - this.minZoom = 1; - this.maxZoom = 12000; - this.scroll = 0; - this.deltas = []; - this.labelDates = []; - this.colorRanges = []; - this.rangeColors = new Cycle(Colors.eminence, - Colors.steelblue, - Colors.ochre, - Colors.turquoise); - this.callbacks = []; - this.onTapCallbacks = []; - this.onDoubleTapCallbacks = []; - this.onLongPressCallbacks = []; - this.progress = null; - this.start = null; - this.end = null; - this.selection = null; - this.autoScroll = false; - this.direction = -1; - this.timeticks = ticks || new TimeTicks(new DecadeTicks(), - new YearTicks(), - new MonthTicks(), - new DayTicks()); - this.labelPrefix = '__'; - } - - updateSelection() { - if (this.visibleDate(this.start) && this.visibleDate(this.end)) { - this.selection = [this.start, this.end]; - } - else { - this.timeticks.selectedRange(this); - } - - return this.selection - } - - addCallback(callback) { - this.callbacks.push(callback); - } - - addTabCallback(callback) { - this.onTapCallbacks.push(callback); - } - - addDoubleTapCallback(callback) { - this.onDoubleTapCallbacks.push(callback); - } - - addLongPressCallback(callback) { - this.onLongPressCallbacks.push(callback); - } - - addLabels(labels) { - this.labelDates = labels; - } - - range(start, end) { - this.start = start; - this.end = end; - } - - draw(width, height) { - this.wantedWidth = width; - this.wantedHeight = height; - this.redraw(); - } - - updateColorRanges(w, h) { - let xx = w - this.inset; - let size = FontInfo.small.fontSize; - let yy = h - size; - for (let i = this.colorRanges.length - 1; i >= 0; i--) { - let cr = this.colorRanges[i]; - let label = cr.label; - cr.draw(this, w, h); - let current = this.ensureLabel('colorRange:' + label, label, - { x: xx, y: yy, align: 'right' }, FontInfo.small); - let r = current.getBounds(); - xx -= r.width + 16; - - this.lineStyle(size, cr.color); - this.moveTo(xx, yy); - this.lineTo(xx + size, yy); - xx -= size + 4; - } - } - - drawSelectedRamge(selected) { - this.lineStyle(2, app.theme.primaryColor); - let start = this.toX(selected[0]); - let end = this.toX(selected[1]); - this.moveTo(start, 0); - this.lineTo(end, 0); - this.drawTick(start, -1.5, 0); - this.drawTick(end, -1.5, 0); - } - - redraw() { - this.clear(); - let h = this.wantedHeight; - let w = this.wantedWidth; - let y = this.getY(); - this.prepareLabels(); - this.updateColorRanges(w, h); - - this.lineStyle(2, 0xFFFFFF); - if (this.start != null && this.end != null) { - this.moveTo(this.toX(this.start), y); - this.lineTo(this.toX(this.end), y); - this.updateTicksAndLabels(w, h); - this.updateSelection(); - let selected = this.selection; - if (selected[0] != this.start && selected[1] != this.end) { - if (this.showRange) - this.drawSelectedRamge(selected); - } - for (let callback of this.callbacks) { - callback(this.scroll, this.zoom, this.selection); - } - } - else { - this.moveTo(this.inset, y); - this.lineTo(w - this.inset, y); - } - - if (this.progress != null && this.progress < 1) { - this.lineStyle(2, 0xCCCCFF); - this.moveTo(this.inset, y); - this.lineTo((w - this.inset) * this.progress, y); - } - } - - totalWidth(bounded = false) { - let w = this.wantedWidth - (2 * this.inset); - return w * this.validZoom(this.zoom, bounded) - } - - validZoom(zoom, bounded = true) { - let overshoot = (bounded) ? 1.0 : 2.0; - zoom = Math.max(zoom, this.minZoom / overshoot); - zoom = Math.min(zoom, this.maxZoom * overshoot); - return zoom - } - - getY() { - return this.wantedHeight * this.baseLine - } - - toX(date) { - let total = this.end - this.start; - let offset = this.inset + this.scroll; - let width = this.totalWidth(); - let delta = date - this.start; - let ratio = delta / total; - return offset + ratio * width - } - - fromX(value) { - let total = this.end - this.start; - let offset = this.inset + this.scroll; - let width = this.totalWidth(); - let ratio = (value - offset) / width; - let time = this.start.getTime() + total * ratio; - let date = new Date(time); - return date - } - - drawTick(x, direction = 1, y = null) { - if (y == null) { - y = this.getY(); - } - this.moveTo(x, y); - this.lineTo(x, y - (this.tickHeight * direction * this.direction)); - } - - prepareLabels() { - for (let key of this.labels.keys()) { - if (!key.startsWith(this.labelPrefix)) - this.labels.get(key).visible = false; - } - } - - updateTicksAndLabels(width, height) { - this.drawTick(this.toX(this.start)); - this.drawTick(this.toX(this.end)); - this.timeticks.draw(this, width, height); - this.updateLabels(width, height); - } - - visibleDate(date, offset = 0) { - if (date >= this.start && date <= this.end) { - let x = this.toX(date) + offset; - return (x > 0 && x < this.wantedWidth) - } - return false - } - - visibleRange(start, end) { - let x = this.toX(start); - if (x > this.wantedWidth) - return false - x = this.toX(end); - if (x < 0) - return false - return true - } - - tickLabelOffset(direction = 1, level = 0) { - let fs = FontInfo.small.fontSize; - let dh = fs + (level * (fs + 2)); - return this.direction * direction * dh - } - - updateLabels(width, height) { - let h2 = height / 2; - if (this.labelDates) { - let last = null; - let y = h2 + this.tickLabelOffset(); - for (let i = this.labelDates.length - 1; i >= 0; i--) { - let [label, date] = this.labelDates[i]; - let align = 'center'; // (last == null) ? 'right' : 'left' - let x = this.toX(date); - let current = this.ensureLabel(this.labelPrefix + label, label, - { - x: x, y: y, - align - }, - FontInfo.small); - let r = current.getBounds(); - current.visible = !(last != null && r.x + r.width > last.x); - if (current.visible) { - this.drawTick(x, -1); - last = r; - } - } - } - else { - let start = this.start.toLocaleDateString('de', { year: 'numeric', month: 'numeric', day: 'numeric' }); - let end = this.end.toLocaleDateString('de', { year: 'numeric', month: 'numeric', day: 'numeric' }); - this.ensureLabel(this.labelPrefix + 'start', start, { x: this.toX(this.start), y: h2 }); - this.ensureLabel(this.labelPrefix + 'end', end, { x: this.toX(this.end), y: h2, align: 'right' }); - } - } - - onZoom(zoom, anchor) { - let date = this.fromX(anchor.x); - let newZoom = this.validZoom(this.zoom * zoom, false); - this.zoom = newZoom; - let newX = this.toX(date); - this.scroll += anchor.x - newX; - } - - onStart(event, interaction) { - this.killTweens(); - this.deltas = []; - this.validScroll(); - ThrowPropsPlugin.track(this, 'delta'); - } - - onMove(event, interaction) { - let delta = interaction.delta(); - this.scroll += delta.x; - while (this.deltas.length > 10) { - this.deltas.pop(0); - } - this.deltas.push(delta.x); - if (interaction.current.size > 1) { - this.onZoom(delta.zoom, delta.about); - } - this.redraw(); - } - - onEnd(event, interaction) { - let vel = ThrowPropsPlugin.getVelocity(this, 'delta'); - ThrowPropsPlugin.untrack(this); - - this.killTweens(); - this.redraw(); - let delta = 0; - for (let d of this.deltas) { - delta += d; - } - if (this.deltas.length > 0) { - delta /= this.deltas.length; - } - this.autoScroll = true; - let anchor = interaction.current.mean(); - this.keepInBounds(delta, anchor); - - for(let key of interaction.ended.keys()) { - if (interaction.isDoubleTap(key)) { - this.onDoubleTap(event, interaction, key); - } - else if (interaction.isTap(key)) { - this.onTap(event, interaction, key); - } - else if (interaction.isLongPress(key)) { - this.onLongPress(event, interaction, key); - } - } - } - - onLongPress(event, interaction, key) { - for(let callback of this.onLongPressCallbacks) { - callback(event, interaction, key); - } - } - - onTap(event, interaction, key) { - for(let callback of this.onTapCallbacks) { - callback(event, interaction, key); - } - } - - onDoubleTap(event, interaction, key) { - for(let callback of this.onDoubleTapCallbacks) { - callback(event, interaction, key); - } - } - - _scrollMinimum(bounded) { - let total = this.totalWidth(bounded); - return -total + this.wantedWidth - 2 * this.inset - } - - _scrollMaximum(bounded) { - let total = this.totalWidth(bounded); - let limit = this.wantedWidth; - if (total > limit) - return 0 - let w = limit - 2 * this.inset; - return (w - total) / 2 - } - - scrollMinimum(bounded) { - return this._scrollMinimum(bounded) - this.extraRight - } - - scrollMaximum(bounded) { - return this._scrollMaximum(bounded) + this.extraLeft - } - - killTweens() { - TweenLite.killTweensOf(this); - this.autoScroll = false; - } - - - validScroll(bounded = true) { - let minimum = this.scrollMinimum(bounded); - let maximum = this.scrollMaximum(bounded); - if (this.scroll < minimum) { - this.scroll = minimum; - } - if (this.scroll > maximum) { - this.scroll = maximum; - } - } - - keepInBounds(delta, anchor) { - let bounded = true; - let minimum = this.scrollMinimum(bounded); - let maximum = this.scrollMaximum(bounded); - let tweens = {}; - if (this.zoom > this.maxZoom) { - tweens.zoom = this.maxZoom; - let date = this.fromX(anchor.x); - let oldZoom = this.zoom; - this.zoom = this.maxZoom; - let newX = this.toX(date); - tweens.scroll = this.scroll + anchor.x - newX; - this.zoom = oldZoom; - } - else { - if (this.zoom < this.minZoom) { - tweens.zoom = this.minZoom; - } - if (this.scroll > maximum) { - tweens.scroll = maximum; - } - if (this.scroll < minimum) { - tweens.scroll = minimum; - } - } - if (!isEmpty(tweens)) { - tweens.onUpdate = () => { this.redraw(); }; - TweenLite.to(this, 0.5, tweens).delay(0.1); - return - } - this.scroll += delta; - delta *= 0.985; - this.redraw(); - if (Math.abs(delta) > 1 && this.autoScroll) { - setTimeout(() => this.keepInBounds(delta, anchor), 1000 / 100); - } - } - - onMouseWheel(event) { - this.killTweens(); - let direction = event.detail < 0 || event.wheelDelta > 0; - let anchor = { x: event.clientX, y: event.clientY }; - const zoomFactor = 1.5; - this.onZoom((direction) ? zoomFactor : 1 / zoomFactor, anchor); - this.redraw(); - this.keepInBounds(0, anchor); - } - - showRanges(ranges, label = "Untitled", color = null) { - for (let cr of this.colorRanges) { - if (cr.label == label) - return - } - while (this.colorRanges.length >= this.rangeColors.length) { - this.colorRanges.shift(); - } - this.colorRanges.push(new ColorRanges(label, color, ranges)); - this.redraw(); - } - } - - /** - * Callback for the slider action onStart. - * - * @callback onStartCallback - * @param {object} event - The event object. - * @param {Slider} slider - A reference to the slider (also this refers to the slider). - */ - - /** - * Callback for the slider action onUpdate. - * - * @callback onUpdateCallback - * @param {object} event - The event object. - * @param {Slider} slider - A reference to the slider (also this refers to the slider). - */ - - /** - * Callback for the slider action onComplete. - * - * @callback onCompleteCallback - * @param {object} event - The event object. - * @param {Slider} slider - A reference to the slider (also this refers to the slider). - */ - - /** - * Class that represents a PixiJS Slider. - * - * @example - * // Create the app - * const app = new PIXIApp({ - * view: canvas, - * width: 900, - * height: 250 - * }).setup().run() - * - * // Create the slider - * const slider = new Slider({ - * x: 10, - * y: 20 - * }) - * - * // Add the slider to a DisplayObject - * app.scene.addChild(slider) - * - * @class - * @extends PIXI.Container - * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/slider.html|DocTest} - */ - class Slider extends PIXI.Container { - - /** - * Creates an instance of a Slider. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the slider. - * @param {number} [opts.id=auto generated] - The id of the slider. - * @param {number} [opts.x=0] - The x position of the slider. Can be also set after creation with slider.x = 0. - * @param {number} [opts.y=0] - The y position of the slider. Can be also set after creation with slider.y = 0. - * @param {string|Theme} [opts.theme=dark] - The theme to use for this slider. Possible values are dark, light, red - * or a Theme object. - * @param {number} [opts.width=250] - The width of the slider. - * @param {number} [opts.height=2] - The height of the slider. - * @param {PIXI.DisplayObject} [opts.container=window.app|object] - The container where the slider events should be attached to. - * @param {number} [opts.fill=Theme.fill] - The color of the slider background as a hex value. - * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. - * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. - * @param {number} [opts.strokeWidth=Theme.strokeWidth] - The width of the border in pixel. - * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. - * @param {number} [opts.controlFill=Theme.stroke] - The color of the slider control background as a hex value. - * @param {number} [opts.controlFillAlpha=Theme.strokeAlpha] - The alpha value of the background. - * @param {number} [opts.controlStroke=Theme.stroke] - The color of the border as a hex value. - * @param {number} [opts.controlStrokeWidth=Theme.strokeWidth * 0.8] - The width of the border in pixel. - * @param {number} [opts.controlStrokeAlpha=Theme.strokeAlpha] - The alpha value of the border. - * @param {number} [opts.controlRadius=16] - The radius of the slider control. - * @param {boolean} [opts.disabled=false] - Is the slider disabled? When disabled, the slider has a lower alpha value - * and cannot be clicked (interactive is set to false). - * @param {onStartCallback} [opts.onStart] - Executed when the slider control starts to move. - * @param {onUpdateCallback} [opts.onUpdate] - Executed when the slider control is moved. - * @param {onCompleteCallback} [opts.onComplete] - Executed when the slider control was dropped. - * @param {string|object} [opts.tooltip] - A string for the label of the tooltip or an object to configure the tooltip - * to display. - * @param {boolean} [opts.visible=true] - Is the slider initially visible (property visible)? - */ - constructor(opts = {}) { - - super(); - - const theme = Theme.fromString(opts.theme); - this.theme = theme; - - this.opts = Object.assign({}, { - id: PIXI.utils.uid(), - x: 0, - y: 0, - width: 250, - height: 2, - container: null, - fill: theme.fill, - fillAlpha: theme.fillAlpha, - stroke: theme.stroke, - strokeWidth: theme.strokeWidth, - strokeAlpha: theme.strokeAlpha, - controlFill: theme.fill, - controlFillAlpha: .5, - controlStroke: theme.primaryColor, - controlStrokeWidth: 2, - controlStrokeAlpha: theme.strokeAlpha, - controlRadius: 16, - orientation: 'horizontal', - min: 0, - max: 100, - value: 0, - disabled: false, - onStart: null, - onUpdate: null, - onComplete: null, - tooltip: null, - visible: true - }, opts); - - this.opts.container = this.opts.container || this; - - // Validation - //----------------- - if (this.opts.height > this.opts.width) { - this.opts.height = this.opts.width; - } - - if (this.opts.value < this.opts.min) { - this.opts.value = this.opts.min; - } - - if (this.opts.value > this.opts.max) { - this.opts.value = this.opts.max; - } - - // Properties - //----------------- - this.id = this.opts.id; - this.radius = this.opts.height / 2; - - this._value = this.opts.value; - this._disabled = null; - - this.sliderObj = null; - this.control = null; - this.tooltip = null; - - this.visible = this.opts.visible; - - // setup - //----------------- - this.setup(); - - // layout - //----------------- - this.layout(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {Slider} A reference to the slider for chaining. - */ - setup() { - - // Container events - //----------------- - const container = this.opts.container; - - this.on('pointermove', e => { - if (this.control.dragging) { - const moveX = this.control.event.data.getLocalPosition(this.control.parent).x; - this._value = this.pixelToValue(moveX - this.control.delta - this.opts.controlRadius); - let x = this.valueToPixel(this._value) + this.opts.controlRadius; - this.control.x = x; - - if (this.opts.onUpdate) { - this.opts.onUpdate.call(this, e, this); - } - } - }); - - if (container instanceof Element) { - container.addEventListener('pointerup', e => this.onEnd(e), false); - container.addEventListener('pointercancel', e => this.onEnd(e), false); - container.addEventListener('pointerleave', e => this.onEnd(e), false); - container.addEventListener('pointerout', e => this.onEnd(e), false); - container.addEventListener('mouseup', e => this.onEnd(e), false); - container.addEventListener('mousecancel', e => this.onEnd(e), false); - container.addEventListener('mouseleave', e => this.onEnd(e), false); - container.addEventListener('mouseout', e => this.onEnd(e), false); - } else { - container.interactive = true; - container.on('pointerup', e => this.onEnd(e)); - container.on('pointercancel', e => this.onEnd(e)); - container.on('pointerleave', e => this.onEnd(e)); - container.on('pointerout', e => this.onEnd(e)); - } - - // Slider - //----------------- - let sliderObj = new PIXI.Graphics(); - this.sliderObj = sliderObj; - this.addChild(sliderObj); - - // Control - //----------------- - let control = new PIXI.Graphics(); - control.x = this.opts.controlRadius + this.valueToPixel(this.opts.value); - control.y = this.opts.controlRadius; - - // pointerdown on the control for dragndrop - control.on('pointerdown', e => { - control.event = e; - control.delta = e.data.getLocalPosition(this.control).x; - control.dragging = true; - - if (this.opts.onStart) { - this.opts.onStart.call(this, e, this); - } - }); - - this.control = control; - - this.addChild(this.control); - - // interaction - //----------------- - this.sliderObj.on('pointerover', e => { - TweenLite.to(this.control, this.theme.fast, {alpha: .83}); - }); - - this.sliderObj.on('pointerout', e => { - TweenLite.to(this.control, this.theme.fast, {alpha: 1}); - }); - - this.sliderObj.on('pointerdown', e => { - this.sliderObj.pointerdowned = true; - TweenLite.to(this.control, this.theme.fast, {alpha: .7}); - }); - - // Click on the slider bar - this.sliderObj.on('pointerup', e => { - if (this.sliderObj.pointerdowned) { - this.sliderObj.pointerdowned = false; - const position = e.data.getLocalPosition(this.control.parent); - this.value = this.pixelToValue(position.x - this.opts.controlRadius); - TweenLite.to(this.control, this.theme.fast, {alpha: .83}); - } - }); - - // disabled - //----------------- - this.disabled = this.opts.disabled; - - // tooltip - //----------------- - if (this.opts.tooltip) { - if (typeof this.opts.tooltip === 'string') { - this.tooltip = new Tooltip({ - object: this, - content: this.opts.tooltip - }); - } else { - this.opts.tooltip.object = this; - this.tooltip = new Tooltip(this.opts.tooltip); - } - } - - return this - } - - /** - * Should be called to refresh the layout of the slider. Can be used after resizing. - * - * @return {Slider} A reference to the slider for chaining. - */ - layout() { - - // set position - //----------------- - this.position.set(this.opts.x, this.opts.y); - - // draw - //----------------- - this.draw(); - - return this - } - - /** - * Draws the slider to the canvas. - * - * @private - * @return {Slider} A reference to the slider for chaining. - */ - draw() { - - const r = this.radius; - const cr = this.opts.controlRadius; - const w = this.opts.width; - const h = this.opts.height; - const x = cr + r; - const y = cr + r - h; - - this.sliderObj.clear(); - this.sliderObj.beginFill(0xffffff, 0); - this.sliderObj.drawRect(0, 0, x + w + cr, cr * 2); - this.sliderObj.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); - this.sliderObj.beginFill(this.opts.fill, this.opts.fillAlpha); - this.sliderObj.moveTo(x, y); - this.sliderObj.lineTo(x + w, y); - this.sliderObj.arcTo(x + w + r, y, x + w + r, y + r, r); - this.sliderObj.lineTo(x + w + r, y + r + 1); // BUGFIX: If not specified, there is a small area without a stroke. - this.sliderObj.arcTo(x + w + r, y + h, x + w, y + h, r); - this.sliderObj.lineTo(x, y + h); - this.sliderObj.arcTo(x - r, y + h, x - r, y + r, r); - this.sliderObj.arcTo(x - r, y, x, y, r); - this.sliderObj.endFill(); - - // Draw control - this.control.clear(); - this.control.lineStyle(this.opts.controlStrokeWidth, this.opts.controlStroke, this.opts.controlStrokeAlpha); - this.control.beginFill(this.opts.controlFill, this.opts.controlFillAlpha); - this.control.drawCircle(0, 0, cr - 1); - this.control.beginFill(this.opts.controlStroke, this.opts.controlStrokeAlpha); - this.control.drawCircle(0, 0, cr / 6); - this.control.endFill(); - - return this - } - - /** - * Executed, when the slider control movement ended. - * - * @private - * @return {Slider} A reference to the slider for chaining. - */ - onEnd(e) { - - if (this.control.dragging) { - this.control.event = null; - this.control.dragging = false; - if (this.opts.onComplete) { - this.opts.onComplete.call(this, e, this); - } - } - - return this - } - - /** - * Calculates the value for a given pixel. - * - * @private - * @param {number} value - * @returns {number} The calucalted pixel. - */ - valueToPixel(value) { - if (value < this.opts.min) { - value = this.opts.min; - } else if (value > this.opts.max) { - value = this.opts.max; - } - return this.opts.width * (value - this.opts.min) / (this.opts.max - this.opts.min) - } - - /** - * Calculates the pixel for a given value. - * - * @private - * @param {number} pixel - * @returns {number} The calucalted value. - */ - pixelToValue(pixel) { - if (pixel < 0) { - pixel = 0; - } else if (pixel > this.opts.width) { - pixel = this.opts.width; - } - return this.opts.min + ((this.opts.max - this.opts.min) * pixel / this.opts.width) - } - - /** - * Gets or sets the value. - * - * @member {number} - */ - get value() { - return Math.round(this._value) - } - set value(value) { - if (value < this.opts.min) { - value = this.opts.min; - } else if (value > this.opts.max) { - value = this.opts.max; - } - this._value = value; - - const x = this.valueToPixel(value) + this.opts.controlRadius; - - TweenLite.to(this.control, this.theme.fast, {x}); - } - - /** - * Gets or sets the disabled state. When disabled, the slider cannot be clicked. - * - * @member {boolean} - */ - get disabled() { - return this._disabled - } - set disabled(value) { - - this._disabled = value; - - if (this._disabled) { - this.interactive = false; - this.sliderObj.interactive = false; - this.control.interactive = false; - this.control.buttonMode = false; - this.alpha = .5; - } else { - this.interactive = true; - this.sliderObj.interactive = true; - this.control.interactive = true; - this.control.buttonMode = true; - this.alpha = 1; - } - } - - /** - * Shows the slider (sets his alpha values to 1). - * - * @return {Slider} A reference to the slider for chaining. - */ - show() { - - this.opts.strokeAlpha = 1; - this.opts.fillAlpha = 1; - this.opts.controlStrokeAlpha = 1; - this.opts.controlFillAlpha = 1; - - this.layout(); - - return this - } - - /** - * Hides the slider (sets his alpha values to 1). - * - * @return {Slider} A reference to the slider for chaining. - */ - hide() { - - this.opts.strokeAlpha = 0; - this.opts.fillAlpha = 0; - this.opts.controlStrokeAlpha = 0; - this.opts.controlFillAlpha = 0; - - this.layout(); - - return this - } - } - - /** - * Callback for the switch action. - * - * @callback actionCallback - * @param {object} event - The event object. - * @param {Switch} switch - A reference to the switch (also this refers to the switch). - */ - - /** - * Callback for the switch action. - * - * @callback actionActiveCallback - * @param {object} event - The event object. - * @param {Switch} switch - A reference to the switch (also this refers to the switch). - */ - - /** - * Callback for the switch beforeAction. - * - * @callback beforeActionCallback - * @param {object} event - The event object. - * @param {Switch} switch - A reference to the switch (also this refers to the switch). - */ - - /** - * Callback for the switch afterAction. - * - * @callback afterActionCallback - * @param {object} event - The event object. - * @param {Switch} switch - A reference to the switch (also this refers to the switch). - */ - - /** - * Class that represents a PixiJS Switch. - * - * @example - * // Create the app - * const app = new PIXIApp({ - * view: canvas, - * width: 900, - * height: 250 - * }).setup().run() - * - * // Create the switch - * const switch1 = new Switch({ - * x: 10, - * y: 20 - * }) - * - * // Add the switch to a DisplayObject - * app.scene.addChild(switch1) - * - * @class - * @extends PIXI.Container - * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PIXI.Container} - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/switch.html|DocTest} - */ - class Switch extends PIXI.Container { - - /** - * Creates an instance of a Switch. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the switch. - * @param {number} [opts.id=auto generated] - The id of the switch. - * @param {number} [opts.x=0] - The x position of the switch. Can be also set after creation with switch.x = 0. - * @param {number} [opts.y=0] - The y position of the switch. Can be also set after creation with switch.y = 0. - * @param {string|Theme} [opts.theme=dark] - The theme to use for this switch. Possible values are dark, light, red - * or a Theme object. - * @param {number} [opts.width=44] - The width of the switch. - * @param {number} [opts.height=28] - The height of the switch. - * @param {number} [opts.fill=Theme.fill] - The color of the switch background as a hex value. - * @param {number} [opts.fillAlpha=Theme.fillAlpha] - The alpha value of the background. - * @param {number} [opts.fillActive=Theme.fillActive] - The color of the switch background when activated. - * @param {number} [opts.fillActiveAlpha=Theme.fillActiveAlpha] - The alpha value of the background when activated. - * @param {number} [opts.stroke=Theme.stroke] - The color of the border as a hex value. - * @param {number} [opts.strokeWidth=Theme.strokeWidth] - The width of the border in pixel. - * @param {number} [opts.strokeAlpha=Theme.strokeAlpha] - The alpha value of the border. - * @param {number} [opts.strokeActive=Theme.strokeActive] - The color of the border when activated. - * @param {number} [opts.strokeActiveWidth=Theme.strokeActiveWidth] - The width of the border in pixel when activated. - * @param {number} [opts.strokeActiveAlpha=Theme.strokeActiveAlpha] - The alpha value of the border when activated. - * @param {number} [opts.controlFill=Theme.stroke] - The color of the switch control background as a hex value. - * @param {number} [opts.controlFillAlpha=Theme.strokeAlpha] - The alpha value of the background. - * @param {number} [opts.controlFillActive=Theme.stroke] - The color of the switch control background when activated. - * @param {number} [opts.controlFillActiveAlpha=Theme.strokeAlpha] - The alpha value of the background when activated. - * @param {number} [opts.controlStroke=Theme.stroke] - The color of the border as a hex value. - * @param {number} [opts.controlStrokeWidth=Theme.strokeWidth * 0.8] - The width of the border in pixel. - * @param {number} [opts.controlStrokeAlpha=Theme.strokeAlpha] - The alpha value of the border. - * @param {number} [opts.controlStrokeActive=Theme.stroke] - The color of the border when activated. - * @param {number} [opts.controlStrokeActiveWidth=Theme.strokeActiveWidth * 0.8] - The width of the border in pixel when activated. - * @param {number} [opts.controlStrokeActiveAlpha=Theme.strokeActiveAlpha] - The alpha value of the border when activated. - * @param {number} [opts.duration=Theme.fast] - The duration of the animation when the switch gets activated in seconds. - * @param {number} [opts.durationActive=Theme.fast] - The duration of the animation when the switch gets deactivated in seconds. - * @param {boolean} [opts.disabled=false] - Is the switch disabled? When disabled, the switch has a lower alpha value - * and cannot be clicked (interactive is set to false). - * @param {boolean} [opts.active=false] - Is the button initially active? - * @param {actionCallback} [opts.action] - Executed when the switch was triggered in inactive state (by pointerup). - * @param {actionActiveCallback} [opts.actionActive] - Executed when the button was triggered in active state (by pointerup). - * @param {beforeActionCallback} [opts.beforeAction] - Executed before an action is triggered. - * @param {afterActionCallback} [opts.afterAction] - Executed after an action was triggered. - * @param {string|object} [opts.tooltip] - A string for the label of the tooltip or an object to configure the tooltip - * to display. - * @param {boolean} [opts.visible=true] - Is the switch initially visible (property visible)? - */ - constructor(opts = {}) { - - super(); - - const theme = Theme.fromString(opts.theme); - this.theme = theme; - - this.opts = Object.assign({}, { - id: PIXI.utils.uid(), - x: 0, - y: 0, - width: 44, - height: 28, - fill: theme.fill, - fillAlpha: theme.fillAlpha, - fillActive: theme.primaryColor, - fillActiveAlpha: theme.fillActiveAlpha, - stroke: theme.stroke, - strokeWidth: theme.strokeWidth, - strokeAlpha: theme.strokeAlpha, - strokeActive: theme.primaryColor, - strokeActiveWidth: theme.strokeActiveWidth, - strokeActiveAlpha: theme.strokeActiveAlpha, - controlFill: theme.stroke, - controlFillAlpha: theme.strokeAlpha, - controlFillActive: theme.stroke, - controlFillActiveAlpha: theme.strokeAlpha, - controlStroke: theme.stroke, - controlStrokeWidth: theme.strokeWidth * .8, - controlStrokeAlpha: theme.strokeAlpha, - controlStrokeActive: theme.stroke, - controlStrokeActiveWidth: theme.strokeActiveWidth * .8, - controlStrokeActiveAlpha: theme.strokeActiveAlpha, - duration: theme.fast, - durationActive: theme.fast, - disabled: false, - active: false, - action: null, - actionActive: null, - beforeAction: null, - afterAction: null, - tooltip: null, - visible: true - }, opts); - - this.opts.controlRadius = this.opts.controlRadius || (this.opts.height / 2); - this.opts.controlRadiusActive = this.opts.controlRadiusActive || this.opts.controlRadius; - - // Validation - //----------------- - if (this.opts.height > this.opts.width) { - this.opts.height = this.opts.width; - } - - // Properties - //----------------- - this.id = this.opts.id; - this.radius = this.opts.height / 2; - - this._active = null; - this._disabled = null; - - this.switchObj = null; - this.control = null; - this.tooltip = null; - - this.visible = this.opts.visible; - - // animated - //----------------- - this.tempAnimated = { - fill: this.opts.fill, - fillAlpha: this.opts.fillAlpha, - stroke: this.opts.stroke, - strokeWidth: this.opts.strokeWidth, - strokeAlpha: this.opts.strokeAlpha, - controlFill: this.opts.controlFill, - controlFillAlpha: this.opts.controlFillAlpha, - controlStroke: this.opts.controlStroke, - controlStrokeWidth: this.opts.controlStrokeWidth, - controlStrokeAlpha: this.opts.controlStrokeAlpha, - controlRadius: this.opts.controlRadius - }; - - // setup - //----------------- - this.setup(); - - // layout - //----------------- - this.layout(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {Switch} A reference to the switch for chaining. - */ - setup() { - - // Switch - //----------------- - let switchObj = new PIXI.Graphics(); - this.switchObj = switchObj; - this.addChild(switchObj); - - // Control - //----------------- - this.xInactive = this.opts.controlRadius; - this.xActive = this.opts.width - this.opts.controlRadiusActive; - - let control = new PIXI.Graphics(); - control.x = this.opts.active ? this.xActive : this.xInactive; - control.y = this.opts.height / 2; - - this.control = control; - - this.addChild(this.control); - - // interaction - //----------------- - this.switchObj.on('pointerover', e => { - TweenLite.to(this.control, this.theme.fast, {alpha: .83}); - }); - - this.switchObj.on('pointerout', e => { - TweenLite.to(this.control, this.theme.fast, {alpha: 1}); - }); - - this.switchObj.on('pointerdown', e => { - TweenLite.to(this.control, this.theme.fast, {alpha: .7}); - }); - - this.switchObj.on('pointerup', e => { - - if (this.opts.beforeAction) { - this.opts.beforeAction.call(this, e, this); - } - - this.active = !this.active; - - if (this.active) { - if (this.opts.action) { - this.opts.action.call(this, e, this); - } - } else { - if (this.opts.actionActive) { - this.opts.actionActive.call(this, e, this); - } - } - - TweenLite.to(this.control, this.theme.fast, {alpha: .83}); - - if (this.opts.afterAction) { - this.opts.afterAction.call(this, e, this); - } - }); - - // disabled - //----------------- - this.disabled = this.opts.disabled; - - // active - //----------------- - this.active = this.opts.active; - - // tooltip - //----------------- - if (this.opts.tooltip) { - if (typeof this.opts.tooltip === 'string') { - this.tooltip = new Tooltip({ - object: this, - content: this.opts.tooltip - }); - } else { - this.opts.tooltip.object = this; - this.tooltip = new Tooltip(this.opts.tooltip); - } - } - - return this - } - - /** - * Should be called to refresh the layout of the switch. Can be used after resizing. - * - * @return {Switch} A reference to the switch for chaining. - */ - layout() { - - // set position - //----------------- - this.position.set(this.opts.x, this.opts.y); - - // draw - //----------------- - this.draw(); - - return this - } - - /** - * Draws the switch to the canvas. - * - * @private - * @return {Switch} A reference to the switch for chaining. - */ - draw() { - - this.switchObj.clear(); - if (this.active) { - this.switchObj.lineStyle(this.opts.strokeActiveWidth, this.opts.strokeActive, this.opts.strokeActiveAlpha); - this.switchObj.beginFill(this.opts.fillActive, this.opts.fillActiveAlpha); - } else { - this.switchObj.lineStyle(this.opts.strokeWidth, this.opts.stroke, this.opts.strokeAlpha); - this.switchObj.beginFill(this.opts.fill, this.opts.fillAlpha); - } - this.switchObj.moveTo(this.radius, 0); - this.switchObj.lineTo(this.opts.width - this.radius, 0); - this.switchObj.arcTo(this.opts.width, 0, this.opts.width, this.radius, this.radius); - this.switchObj.lineTo(this.opts.width, this.radius + 1); // BUGFIX: If not specified, there is a small area without a stroke. - this.switchObj.arcTo(this.opts.width, this.opts.height, this.opts.width - this.radius, this.opts.height, this.radius); - this.switchObj.lineTo(this.radius, this.opts.height); - this.switchObj.arcTo(0, this.opts.height, 0, this.radius, this.radius); - this.switchObj.arcTo(0, 0, this.radius, 0, this.radius); - this.switchObj.endFill(); - - // Draw control - this.control.clear(); - if (this.active) { - this.control.lineStyle(this.opts.controlStrokeActiveWidth, this.opts.controlStrokeActive, this.opts.controlStrokeActiveAlpha); - this.control.beginFill(this.opts.controlFillActive, this.opts.controlFillActiveAlpha); - this.control.drawCircle(0, 0, this.opts.controlRadiusActive - 1); - } else { - this.control.lineStyle(this.opts.controlStrokeWidth, this.opts.controlStroke, this.opts.controlStrokeAlpha); - this.control.beginFill(this.opts.controlFill, this.opts.controlFillAlpha); - this.control.drawCircle(0, 0, this.opts.controlRadius - 1); - } - this.control.endFill(); - - return this - } - - /** - * Draws the animation. - * - * @private - * @return {Switch} A reference to the switch for chaining. - */ - drawAnimated() { - - this.switchObj.clear(); - this.switchObj.lineStyle(this.tempAnimated.strokeWidth, this.tempAnimated.stroke, this.tempAnimated.strokeAlpha); - this.switchObj.beginFill(this.tempAnimated.fill, this.tempAnimated.fillAlpha); - this.switchObj.moveTo(this.radius, 0); - this.switchObj.lineTo(this.opts.width - this.radius, 0); - this.switchObj.arcTo(this.opts.width, 0, this.opts.width, this.radius, this.radius); - this.switchObj.lineTo(this.opts.width, this.radius + 1); // BUGFIX: If not specified, there is a small area without a stroke. - this.switchObj.arcTo(this.opts.width, this.opts.height, this.opts.width - this.radius, this.opts.height, this.radius); - this.switchObj.lineTo(this.radius, this.opts.height); - this.switchObj.arcTo(0, this.opts.height, 0, this.radius, this.radius); - this.switchObj.arcTo(0, 0, this.radius, 0, this.radius); - this.switchObj.endFill(); - - this.control.clear(); - this.control.lineStyle(this.tempAnimated.controlStrokeWidth, this.tempAnimated.controlStroke, this.tempAnimated.controlStrokeAlpha); - this.control.beginFill(this.tempAnimated.controlFill, this.tempAnimated.controlFillAlpha); - this.control.drawCircle(0, 0, this.tempAnimated.controlRadius - 1); - this.control.endFill(); - - return this - } - - /** - * Gets or sets the active state. - * - * @member {boolean} - */ - get active() { - return this._active - } - - set active(value) { - - this._active = value; - - if (this._active) { - - TweenLite.to(this.control, this.opts.duration, {x: this.xActive}); - TweenLite.to(this.tempAnimated, this.opts.duration, { - colorProps: { - fill: this.opts.fillActive, - stroke: this.opts.strokeActive, - controlFill: this.opts.controlFillActive, - controlStroke: this.opts.controlStrokeActive, - format: 'number' - }, - fillAlpha: this.opts.fillActiveAlpha, - strokeWidth: this.opts.strokeActiveWidth, - strokeAlpha: this.opts.strokeActiveAlpha, - controlFillAlpha: this.opts.controlFillActiveAlpha, - controlStrokeWidth: this.opts.controlStrokeActiveWidth, - controlStrokeAlpha: this.opts.controlStrokeActiveAlpha, - controlRadius: this.opts.controlRadiusActive, - onUpdate: () => this.drawAnimated(), - onComplete: () => this.draw() - }); - - - } else { - TweenLite.to(this.control, this.opts.durationActive, {x: this.xInactive}); - TweenLite.to(this.tempAnimated, this.opts.durationActive, { - colorProps: { - fill: this.opts.fill, - stroke: this.opts.stroke, - controlFill: this.opts.controlFill, - controlStroke: this.opts.controlStroke, - format: 'number' - }, - fillAlpha: this.opts.fillAlpha, - strokeWidth: this.opts.strokeWidth, - strokeAlpha: this.opts.strokeAlpha, - controlFillAlpha: this.opts.controlFillAlpha, - controlStrokeWidth: this.opts.controlStrokeWidth, - controlStrokeAlpha: this.opts.controlStrokeAlpha, - controlRadius: this.opts.controlRadius, - onUpdate: () => this.drawAnimated(), - onComplete: () => this.draw() - }); - } - } - - /** - * Gets or sets the disabled state. When disabled, the switch cannot be clicked. - * - * @member {boolean} - */ - get disabled() { - return this._disabled - } - - set disabled(value) { - - this._disabled = value; - - if (this._disabled) { - this.switchObj.interactive = false; - this.switchObj.buttonMode = false; - this.switchObj.alpha = .5; - this.control.alpha = .5; - } else { - this.switchObj.interactive = true; - this.switchObj.buttonMode = true; - this.switchObj.alpha = 1; - this.control.alpha = 1; - } - } - - /** - * Shows the switch (sets his alpha values to 1). - * - * @return {Switch} A reference to the switch for chaining. - */ - show() { - - this.opts.strokeAlpha = 1; - this.opts.strokeActiveAlpha = 1; - this.opts.fillAlpha = 1; - this.opts.fillActiveAlpha = 1; - this.opts.controlStrokeAlpha = 1; - this.opts.controlStrokeActiveAlpha = 1; - this.opts.controlFillAlpha = 1; - this.opts.controlFillActiveAlpha = 1; - - this.layout(); - - return this - } - - /** - * Hides the switch (sets his alpha values to 1). - * - * @return {Switch} A reference to the switch for chaining. - */ - hide() { - - this.opts.strokeAlpha = 0; - this.opts.strokeActiveAlpha = 0; - this.opts.fillAlpha = 0; - this.opts.fillActiveAlpha = 0; - this.opts.controlStrokeAlpha = 0; - this.opts.controlStrokeActiveAlpha = 0; - this.opts.controlFillAlpha = 0; - this.opts.controlFillActiveAlpha = 0; - - this.layout(); - - return this - } - } - - /** - * Class that represents a PixiJS PopupMenu. - * - * @example - * // Create the button and the modal when clicked - * const button = new Button({ - * label: 'Show PopupMenu', - * action: e => { - * const popupmenu = new PopupMenu({ - * items: [ - * {label: 'Save', action: () => alert('Saved')}, - * {label: 'Edit', action: () => alert('Edited')}, - * {label: 'Delete', action: () => alert('Deleted')} - * ] - * }) - * app.scene.addChild(popupmenu) - * } - * }) - * - * // Add the button to a DisplayObject - * app.scene.addChild(button) - * - * @class - * @extends Popup - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/popupmenu.html|DocTest} - */ - class PopupMenu$1 extends Popup { - - /** - * Creates an instance of a PopupMenu. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the modal. - * @param {object[]} [opts.items=[]] - A list of the menu items. Each item must be of type object. - * If an object has a label property, a PIXI.Text object is created (using the textStyle property). - * If an object hasn't a label property, it must contain a content property which has to be a - * PIXI.DisplayObject. - * @param {number} [opts.margin=Theme.margin / 2] - The app where the modal belongs to. - * @param {object} [opts.textStyle=Theme.textStyle] - The color of the background. - * @param {boolean} [opts.closeOnPopup=true] - The opacity of the background. - */ - constructor(opts = {}) { - - const theme = Theme.fromString(opts.theme); - - opts = Object.assign({}, { - items: [], - margin: theme.margin / 2, - textStyle: theme.textStyle, - closeOnPopup: true - }, opts); - - super(opts); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {PopupMenu} A reference to the popupmenu for chaining. - */ - setup() { - - // content - //----------------- - const content = new PIXI.Container(); - - let y = 0; - for (let item of this.opts.items) { - - let object = null; - - if (item.label) { - object = new PIXI.Text(item.label, item.textStyle || this.opts.textStyle); - } else { - object = item.content; - } - - object.y = y; - - if (item.action) { - if (item.disabled) { - object.alpha = .5; - } else { - object.interactive = true; - object.buttonMode = true; - } - object.on('pointerover', e => { - TweenLite.to(object, this.theme.fast, {alpha: .83, overwrite: 'none'}); - }); - object.on('pointerout', e => { - TweenLite.to(object, this.theme.fast, {alpha: 1, overwrite: 'none'}); - }); - object.on('pointerup', e => { - item.action.call(object, e, object); - if (this.opts.closeOnAction) { - this.hide(); - } - }); - } - - content.addChild(object); - - y += object.height + this.opts.margin; - } - - this.opts.content = content; - - super.setup(); - } - } - - /* global Quad */ - - /** - * Class that represents a PixiJS Volatile. - * - * @example - * const app = new PIXIApp({ - * view: canvas, - * width: 900, - * height: 250 - * }).setup().run() - * - * const button = new Button({ - * label: 'Volatile!', - * action: () => { - * new Volatile({ - * object: button, - * direction: 'right', - * destroyOnComplete: false - * }) - * } - * }) - * - * app.scene.addChild(button) - * - * @class - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/volatile.html|DocTest} - */ - class Volatile { - - /** - * Creates an instance of a Volatile. - * - * @constructor - * @param {object} [opts] - An options object to specify to style and behaviour of the modal. - * @param {number} [opts.id=auto generated] - The id of the tooltip. - * @param {PIXI.DisplayObject|PIXI.DisplayObject[]} opts.object - The object where the volatile should be applied to. - * @param {string} [opts.direction=top] - The animation direction. Possible values: top, right, bottom, left. - * @param {function} [opts.onStart] - A function which is executed when the volatile animation starts. - * @param {function} [opts.onComplete] - A function which is executed when the volatile animation finishes. - * @param {number} [opts.distance=160] - The animation distance. - * @param {number} [opts.duration=1.5] - The duration of the animation in seconds. - * @param {object} [opts.ease=Quad.easeOut] - The easing of the animation, see {@link https://greensock.com/docs/Easing} - * @param {boolean} [opts.destroyOnComplete=true] - Should the object be destroyed after the volatile animation? - */ - constructor(opts = {}) { - - const theme = Theme.fromString(opts.theme); - this.theme = theme; - - this.opts = Object.assign({}, { - id: PIXI.utils.uid(), - object: null, - direction: 'top', // top, right, bottom, left - onStart: null, - onComplete: null, - distance: 160, - duration: 1.5, - ease: Quad.easeOut, - destroyOnComplete: true - }, opts); - - this.id = this.opts.id; - - if (!Array.isArray(this.opts.object)) { - this.opts.object = [this.opts.object]; - } - - this.objects = this.opts.object; - - // setup - //----------------- - this.setup(); - - // layout - //----------------- - this.layout(); - - // run - //----------------- - this.run(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {Volatile} A reference to the volatile for chaining. - */ - setup() { - - return this - } - - /** - * Should be called to refresh the layout of the volatile. Can be used after resizing. - * - * @return {Volatile} A reference to the volatile for chaining. - */ - layout() { - - return this - } - - /** - * Starts the volatile animation. - * - * @private - * @return {Volatile} A reference to the volatile for chaining. - */ - run() { - - for (let object of this.objects) { - - let x = object.x; - let y = object.y; - - switch (this.opts.direction) { - case 'top': - y -= this.opts.distance; - break - case 'right': - x += this.opts.distance; - break - case 'bottom': - y += this.opts.distance; - break - case 'left': - x -= this.opts.distance; - break - } - - TweenLite.to(object, this.opts.duration, { - x, - y, - alpha: 0, - ease: this.opts.ease, - overwrite: 'all', - onStart: () => { - if (this.opts.onStart) { - this.opts.onStart.call(object, object); - } - }, - onComplete: () => { - - if (this.opts.onComplete) { - this.opts.onComplete.call(object, object); - } - - if (this.opts.destroyOnComplete) { - object.destroy({children: true}); - } - } - }); - } - - return this - } - } - - /* globals */ - - /** - * Class that represents a PixiJS List. - * - * @example - * const elephant1 = PIXI.Sprite.fromImage('./assets/elephant-1.jpg') - * const elephant2 = PIXI.Sprite.fromImage('./assets/elephant-2.jpg') - * - * // Create the list - * const list = new List([elephant1, elephant2]) - * - * app.scene.addChild(list) - * - * @class - * @extends PIXI.Container - * @see {@link http://pixijs.download/dev/docs/PIXI.Container.html|PixiJS Container} - * @see {@link https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/list.html|DocTest} - */ - class List extends PIXI.Container { - - /** - * Creates an instance of a Flippable. - * - * @constructor - * @param {PIXI.DisplayObject[]} items - An array of PIXI.DisplayObjects. - * @param {object} [opts] - An options object which can contain the following properties. - * @param {number} [opts.width] - The width of the list. If the items are larger than this width, the overflow - * will be hidden. - * @param {number} [opts.height] - The height of the list. If the items are larger than this height, the overflow - * will be hidden. - * @param {number} [opts.padding=10] - The inner spacing (distance from one item to the previous/next item). - * @param {number} [opts.margin=10] - The outer spacing (distance from one item to the border). - * @param {string} [opts.orientation=vertical] - The orientation of the button group. Can be horizontal or vertical. - * @param {string} [opts.align=left] - The horizontal position of the items. Possible values are - * left, center and right. - * @param {string} [opts.verticalAlign=middle] - The vertical position of the items. Possible values are - * top, middle and bottom. - */ - constructor(items = [], opts = {}) { - - super(); - - this.opts = Object.assign({}, { - padding: 10, - margin: 10, - orientation: 'vertical', - align: 'left', - verticalAlign: 'middle', - width: null, - height: null - }, opts); - - this.__items = items; - this.__dragging = false; - - // setup - //-------------------- - this.setup(); - } - - /** - * Creates children and instantiates everything. - * - * @private - * @return {List} A reference to the list for chaining. - */ - setup() { - - // inner container - //-------------------- - const container = new PIXI.Container(); - this.addChild(container); - this.container = container; - - // mask - //-------------------- - const mask = new PIXI.Graphics(); - this.addChild(mask); - this.__mask = mask; - - // add items - //-------------------- - for(let item of this.__items) { - container.addChild(item); - } - - // interaction - //-------------------- - this.interactive = this.opts.width || this.opts.height; - this.on('pointerdown', this.onStart.bind(this)); - this.on('pointermove', this.onMove.bind(this)); - this.on('pointerup', this.onEnd.bind(this)); - this.on('pointercancel', this.onEnd.bind(this)); - this.on('pointerout', this.onEnd.bind(this)); - this.on('pointerupoutside', this.onEnd.bind(this)); - - this.layout(); - - return this - } - - /** - * Replaces the existing items and relayouts the list. - * - * @param {PIXI.DisplayObject[]} items - An array of PIXI.DisplayObjects. - * @return {List} A reference to the list for chaining. - */ - setItems(items) { - this.container.removeChildren(); - this.__items = items; - for(let item of this.__items) { - this.container.addChild(item); - } - this.layout(); - } - - /** - * Should be called to refresh the layout of the list (the width or the height). - * - * @return {List} A reference to the list for chaining. - */ - layout() { - - const margin = this.opts.margin; - - let x = margin; - let y = margin; - - for (let item of this.__items) { - - item.x = x; - item.y = y; - - if (this.opts.orientation === 'vertical') { - y += item.height + this.opts.padding; - } else { - x += item.width + this.opts.padding; - } - } - - // vertical - //-------------------- - if (this.opts.orientation === 'vertical') { - switch (this.opts.align) { - case 'center': - this.__items.forEach(it => it.x = margin + this.width / 2 - it.width / 2); - break - case 'right': - this.__items.forEach(it => it.x = margin + this.width - it.width); - break - default: - this.__items.forEach(it => it.x = margin); - break - } - - if (this.opts.height) { - const mask = this.__mask; - mask.clear(); - mask.beginFill(0x000); - mask.drawRect(0, 0, this.width + 2 * margin, this.opts.height); - this.mask = mask; - - this.interactive = this.innerHeight > this.opts.height; - } - } - - // horizontal - //-------------------- - if (this.opts.orientation === 'horizontal') { - switch (this.opts.verticalAlign) { - case 'top': - this.__items.forEach(it => it.y = margin); - break - case 'bottom': - this.__items.forEach(it => it.y = margin + this.height - it.height); - break - default: - this.__items.forEach(it => it.y = margin + this.height / 2 - it.height / 2); - break - } - - if (this.opts.width) { - const mask = this.__mask; - mask.clear(); - mask.beginFill(0x000); - mask.drawRect(0, 0, this.opts.width, this.height + 2 * margin); - this.mask = mask; - - this.interactive = this.innerWidth > this.opts.width; - } - } - - return this - } - - /** - * - */ - get innerWidth() { - - let size = 0; - - this.__items.forEach(it => size += it.width); - size += this.opts.padding * (this.__items.length - 1); - size += 2 * this.opts.margin; - - return size - } - - /** - * - */ - get innerHeight() { - - let size = 0; - - this.__items.forEach(it => size += it.height); - size += this.opts.padding * (this.__items.length - 1); - size += 2 * this.opts.margin; - - return size - } - - /** - * Resizes the list. - * - * @param {number} widthOrHeight - The new width (if orientation is horizontal) or height (if orientation is vertical) of the list. - */ - resize(widthOrHeight) { - - if (this.opts.orientation === 'horizontal') { - this.opts.width = widthOrHeight; - } else { - this.opts.height = widthOrHeight; - } - - this.layout(); - } - - /** - * - * @private - * @param {*} event - */ - onStart(event) { - - this.__dragging = true; - - this.capture(event); - - this.__delta = { - x: this.container.position.x - event.data.global.x, - y: this.container.position.y - event.data.global.y - }; - - TweenLite.killTweensOf(this.container.position, {x: true, y: true}); - ThrowPropsPlugin.track(this.container.position, 'x,y'); - } - - /** - * - * @private - * @param {*} event - */ - onMove(event) { - - if (this.__dragging) { - - this.capture(event); - - if (this.opts.orientation === 'horizontal') { - this.container.position.x = event.data.global.x + this.__delta.x; - } else { - this.container.position.y = event.data.global.y + this.__delta.y; - } - } - } - - /** - * - * @private - * @param {*} event - */ - onEnd(event) { - - if (this.__dragging) { - this.__dragging = false; - - this.capture(event); - - const throwProps = {}; - - if (this.opts.orientation === 'horizontal') { - let min = this.opts.width - this.innerWidth; - min = min > 0 ? 0 : min; - throwProps.x = { - velocity: 'auto', - min, - max: 0 - }; - } else { - let min = this.opts.height - this.innerHeight; - min = min > 0 ? 0 : min; - throwProps.y = { - velocity: 'auto', - min, - max: 0 - }; - } - - ThrowPropsPlugin.to(this.container.position, { - throwProps, - ease: Strong.easeOut, - onComplete: () => ThrowPropsPlugin.untrack(this.container.position) - }, .8, .4); - } - } - - /** - * Captures an event to inform InteractionMapper about processed events. - * - * @param {event|PIXI.InteractionEvent} event - The PIXI event to capture. - */ - capture(event) { - Events$1.capturedBy(event.data.originalEvent, this); - } - } - - /* Needed to ensure that rollup.js includes class definitions and the classes - are visible inside doctests. - */ - window.PIXIApp = PIXIApp; - window.BlurFilter = BlurFilter; - window.FlipEffect = FlipEffect; - window.Flippable = Flippable; - window.DeepZoomInfo = DeepZoomInfo; - window.DeepZoomImage = DeepZoomImage; - window.Popover = Popover; - window.ScatterContainer = ScatterContainer; - window.DisplayObjectScatter = DisplayObjectScatter; - window.Command = Command; - window.RecorderTools = RecorderTools; - window.Timeline = Timeline; - window.AppTest = AppTest; - window.Theme = Theme; - window.Button = Button; - window.ButtonGroup = ButtonGroup; - window.Slider = Slider; - window.Switch = Switch; - window.Popup = Popup; - window.PopupMenu = PopupMenu$1; - window.Modal = Modal; - window.Volatile = Volatile; - window.Message = Message; - window.Tooltip = Tooltip; - window.Badge = Badge; - window.Progress = Progress; - window.List = List; - window.LabeledGraphics = LabeledGraphics; - window.FontInfo = FontInfo; - -}()); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxsLmpzIiwic291cmNlcyI6WyJ0aGVtZS5qcyIsInByb2dyZXNzLmpzIiwiYWJzdHJhY3Rwb3B1cC5qcyIsInRvb2x0aXAuanMiLCJiYWRnZS5qcyIsIi4uL2V2ZW50cy5qcyIsImJ1dHRvbi5qcyIsImJ1dHRvbmdyb3VwLmpzIiwicG9wdXAuanMiLCJtb2RhbC5qcyIsIm1lc3NhZ2UuanMiLCIuLi91dGlscy5qcyIsImFwcC5qcyIsImJsdXJmaWx0ZXIuanMiLCIuLi9pbnRlcmZhY2UuanMiLCIuLi9pbnRlcmFjdGlvbi5qcyIsIi4uL2NhcGFiaWxpdGllcy5qcyIsIi4uL3NjYXR0ZXIuanMiLCIuLi9mbGlwcGFibGUuanMiLCJkZWVwem9vbS90aWxlLmpzIiwiZGVlcHpvb20vbG9hZGVyLmpzIiwiZGVlcHpvb20vdGlsZXMuanMiLCJkZWVwem9vbS9pbWFnZS5qcyIsImZsaXBlZmZlY3QuanMiLCJmbGlwcGFibGUuanMiLCJwb3BvdmVyLmpzIiwic2NhdHRlci5qcyIsInRlc3QuanMiLCJsYWJlbGVkZ3JhcGhpY3MuanMiLCJ0aW1lbGluZS5qcyIsInNsaWRlci5qcyIsInN3aXRjaC5qcyIsInBvcHVwbWVudS5qcyIsInZvbGF0aWxlLmpzIiwibGlzdC5qcyIsImJ1bmRsZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENsYXNzIHRoYXQgcmVwcmVzZW50cyBhIFBpeGlKUyBUaGVtZS5cbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gQ3JlYXRlIHRoZSB0aGVtZVxuICogY29uc3QgeWVsbG93ID0gbmV3IFRoZW1lKHtcbiAqICAgICBmaWxsOiAweGZlY2QyZCxcbiAqICAgICBmaWxsQWN0aXZlOiAweGZlOTcyNyxcbiAqICAgICBzdHJva2VBY3RpdmU6IDB4ZmVjZDJkLFxuICogICAgIHN0cm9rZUFjdGl2ZVdpZHRoOiA0LFxuICogICAgIHRleHRTdHlsZToge1xuICogICAgICAgICBmaWxsOiAweDVlYzdmOFxuICogICAgIH0sXG4gKiAgICAgdGV4dFN0eWxlQWN0aXZlOiB7XG4gKiAgICAgICAgIGZpbGw6IDB4NTk1NGQzXG4gKiAgICAgfSxcbiAqICAgICB0ZXh0U3R5bGVMYXJnZToge1xuICogICAgICAgICBmb250U2l6ZTogMzZcbiAqICAgICB9XG4gKiB9KVxuICpcbiAqIC8vIENyZWF0ZSB0aGUgYXBwIGFuZCBhcHBseSB0aGUgbmV3IHRoZW1lIHRvIGl0XG4gKiBjb25zdCBhcHAgPSBuZXcgUElYSUFwcCh7XG4gKiAgICAgdmlldzogY2FudmFzLFxuICogICAgIHdpZHRoOiA0NTAsXG4gKiAgICAgaGVpZ2h0OiAxNTAsXG4gKiAgICAgdGhlbWU6IHllbGxvd1xuICogfSkuc2V0dXAoKS5ydW4oKVxuICpcbiAqIEBjbGFzc1xuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly93d3cuaXdtLXR1ZWJpbmdlbi5kZS9pd21icm93c2VyL2xpYi9waXhpL3RoZW1lLmh0bWx8RG9jVGVzdH1cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgVGhlbWUge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhIFRoZW1lLlxuICAgICAqXG4gICAgICogQGNvbnN0cnVjdG9yXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzXSAtIEFuIG9wdGlvbnMgb2JqZWN0IHRvIHNwZWNpZnkgdG8gc3R5bGUgYW5kIGJlaGF2aW91ciBvZiB0aGUgdGhlbWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLm1hcmdpbj0xMF0gLSBUaGUgb3V0ZXIgc3BhY2luZyAoZGlzdGFuY2UgdG8gb3RoZXIgb2JqZWN0cykgZnJvbSB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5wYWRkaW5nPTEwXSAtIFRoZSBpbm5lciBzcGFjaW5nIChkaXN0YW5jZSBmcm9tIGljb24gYW5kL29yIGxhYmVsKSB0byB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5yYWRpdXM9NF0gLSBUaGUgcmFkaXVzIHVzZWQgd2hlbiBkcmF3aW5nIGEgcm91bmRlZCByZWN0YW5nbGUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmZhc3Q9MC4yNV0gLSBUaGUgZHVyYXRpb24gb2YgdGltZSB3aGVuIGl0IGhhcyB0byBiZSBmYXN0LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5ub3JtYWw9MC41XSAtIFRoZSBkdXJhdGlvbiBvZiB0aW1lIHdoZW4gaXQgaGFzIHRvIGJlIG5vcm1hbC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc2xvdz0xXSAtIFRoZSBkdXJhdGlvbiBvZiB0aW1lIHdoZW4gaXQgaGFzIHRvIGJlIHNsb3cuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnByaW1hcnlDb2xvcj0weDVlYzdmOF0gLSBUaGUgcHJpbWFyeSBjb2xvciBvZiB0aGUgdGhlbWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmNvbG9yMT0weDI4MjgyOF0gLSBUaGUgZmlyc3QgY29sb3Igb2YgdGhlIHRoZW1lLiBGb3IgZXhhbXBsZSB1c2VkIGZvciB0aGUgYmFja2dyb3VuZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuY29sb3IyPTB4ZjZmNmY2XSAtIFRoZSBzZWNvbmQgY29sb3Igb2YgdGhlIHRoZW1lLiBGb3IgZXhhbXBsZSB1c2VkIGZvciB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5maWxsPWNvbG9yMV0gLSBUaGUgY29sb3Igb2YgdGhlIGJhY2tncm91bmQgYXMgYSBoZXggdmFsdWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmZpbGxBbHBoYT0xXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYmFja2dyb3VuZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZmlsbEFjdGl2ZT1jb2xvcjFdIC0gVGhlIGNvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5maWxsQWN0aXZlQWxwaGE9MV0gLSBUaGUgYWxwaGEgdmFsdWUgb2YgdGhlIGJhY2tncm91bmQgd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnN0cm9rZT1jb2xvcjJdIC0gVGhlIGNvbG9yIG9mIHRoZSBib3JkZXIgYXMgYSBoZXggdmFsdWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnN0cm9rZVdpZHRoPTAuNl0gLSBUaGUgd2lkdGggb2YgdGhlIGJvcmRlciBpbiBwaXhlbC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlQWxwaGE9MV0gLSBUaGUgYWxwaGEgdmFsdWUgb2YgdGhlIGJvcmRlci5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlQWN0aXZlPWNvbG9yMl0gLSBUaGUgY29sb3Igb2YgdGhlIGJvcmRlciB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlQWN0aXZlV2lkdGg9MC42XSAtIFRoZSB3aWR0aCBvZiB0aGUgYm9yZGVyIGluIHBpeGVsIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VBY3RpdmVBbHBoYT0xXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYm9yZGVyIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5pY29uQ29sb3I9Y29sb3IyXSAtIFRoZSBjb2xvciBvZiB0aGUgaWNvbiAoc2V0IGJ5IHRoZSB0aW50IHByb3BlcnR5KSBhcyBhIGhleCB2YWx1ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuaWNvbkNvbG9yQWN0aXZlPWNvbG9yUHJpbWFyeV0gLSBUaGUgY29sb3Igb2YgdGhlIGljb24gd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmJhY2tncm91bmQ9Y29sb3IxXSAtIFRoZSBjb2xvciBvZiBhIGJhY2tncm91bmQgZm9yIGEgY29tcG9uZW50IChlLmcuIGF0IHRoZSBNb2RhbCBjbGFzcykuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzLnRleHRTdHlsZT17fV0gLSBBIHRleHRzdHlsZSBvYmplY3QgZm9yIHRoZSBzdHlsaW5nIG9mIHRleHQuIFNlZSBQSVhJLlRleHRTdHlsZVxuICAgICAqICAgICBmb3IgcG9zc2libGUgb3B0aW9ucy4gRGVmYXVsdCBvYmplY3Q6XG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLnRleHRTdHlsZS5mb250RmFtaWx5PVwiQXZlbmlyIE5leHRcIiwgXCJPcGVuIFNhbnNcIiwgXCJTZWdvZSBVSVwiLCAuLi5dIC0gVGhlIGZvbnQgZmFtaWx5LlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy50ZXh0U3R5bGUuZm9udFdlaWdodD00MDBdIC0gVGhlIGZvbnQgd2VpZ2h0LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy50ZXh0U3R5bGUuZm9udFNpemU9MTZdIC0gVGhlIGZvbnQgc2l6ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMudGV4dFN0eWxlLmZpbGw9Y29sb3IyXSAtIFRoZSBmaWxsIGNvbG9yLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy50ZXh0U3R5bGUuc3Ryb2tlPWNvbG9yMV0gLSBUaGUgc3Ryb2tlIGNvbG9yLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy50ZXh0U3R5bGUuc3Ryb2tlVGhpY2tuZXNzPTBdIC0gVGhlIHRoaWNrbmVzcyBvZiB0aGUgc3Ryb2tlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy50ZXh0U3R5bGUubWl0ZXJMaW1pdD0xXSAtIFRoZSBtZXRlciBsaW1pdC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMudGV4dFN0eWxlLmxpbmVKb2luPXJvdW5kXSAtIFRoZSBsaW5lIGpvaW4uXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzLnRleHRTdHlsZUFjdGl2ZT10ZXh0U3R5bGUgKyB7ZmlsbDogcHJpbWFyeUNvbG9yfV0gLSBBIHRleHRzdHlsZSBvYmplY3Qgd2hpY2ggaXMgdXNlZFxuICAgICAqICAgICBmb3IgYWN0aXZlZCB0ZXh0LlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0cy50ZXh0U3R5bGVTbWFsbD10ZXh0U3R5bGUgKyB7Zm9udFNpemU6IC09IDN9XSAtIEEgdGV4dHN0eWxlIG9iamVjdCB3aGljaCBpcyB1c2VkIGZvclxuICAgICAqICAgICBzbWFsbCB0ZXh0LlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0cy50ZXh0U3R5bGVTbWFsbEFjdGl2ZT10ZXh0U3R5bGVTbWFsbCArIHtmaWxsOiBwcmltYXJ5Q29sb3J9XSAtIEEgdGV4dHN0eWxlIG9iamVjdCB3aGljaFxuICAgICAqICAgICBpcyB1c2VkIGZvciBzbWFsbCBhY3RpdmVkIHRleHQuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzLnRleHRTdHlsZUxhcmdlPXRleHRTdHlsZSArIHtmb250U2l6ZTogKz0gM31dIC0gQSB0ZXh0c3R5bGUgb2JqZWN0IHdoaWNoIGlzIHVzZWQgZm9yXG4gICAgICogICAgIGxhcmdlIHRleHQuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzLnRleHRTdHlsZUxhcmdlQWN0aXZlPXRleHRTdHlsZUxhcmdlICsge2ZpbGw6IHByaW1hcnlDb2xvcn1dIC0gQSB0ZXh0c3R5bGUgb2JqZWN0IHdoaWNoXG4gICAgICogICAgIGlzIHVzZWQgZm9yIGxhcmdlIGFjdGl2ZWQgdGV4dC5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihvcHRzID0ge30pIHtcblxuICAgICAgICBjb25zdCBjb2xvclByaW1hcnkgPSBvcHRzLnByaW1hcnlDb2xvciAhPSBudWxsID8gb3B0cy5wcmltYXJ5Q29sb3IgOiAweDVlYzdmOCAgICAgICAgICAgICAgIC8vIGJsdWVcbiAgICAgICAgY29uc3QgY29sb3IxID0gb3B0cy5jb2xvcjEgIT0gbnVsbCA/IG9wdHMuY29sb3IxIDogMHgyODI4MjggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBibGFja1xuICAgICAgICBjb25zdCBjb2xvcjIgPSBvcHRzLmNvbG9yMiAhPSBudWxsID8gb3B0cy5jb2xvcjIgOiAweGY2ZjZmNiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHdoaXRlXG5cbiAgICAgICAgdGhpcy5vcHRzID0gT2JqZWN0LmFzc2lnbih7fSwge1xuICAgICAgICAgICAgbWFyZ2luOiAxMixcbiAgICAgICAgICAgIHBhZGRpbmc6IDEyLFxuICAgICAgICAgICAgcmFkaXVzOiA0LFxuICAgICAgICAgICAgZmFzdDogLjI1LFxuICAgICAgICAgICAgbm9ybWFsOiAuNSxcbiAgICAgICAgICAgIHNsb3c6IDEsXG4gICAgICAgICAgICBwcmltYXJ5Q29sb3I6IGNvbG9yUHJpbWFyeSxcbiAgICAgICAgICAgIGNvbG9yMTogY29sb3IxLFxuICAgICAgICAgICAgY29sb3IyOiBjb2xvcjIsXG4gICAgICAgICAgICBmaWxsOiBjb2xvcjEsXG4gICAgICAgICAgICBmaWxsQWxwaGE6IDEsXG4gICAgICAgICAgICBmaWxsQWN0aXZlOiBjb2xvcjEsXG4gICAgICAgICAgICBmaWxsQWN0aXZlQWxwaGE6IDEsXG4gICAgICAgICAgICBzdHJva2U6IGNvbG9yMixcbiAgICAgICAgICAgIHN0cm9rZVdpZHRoOiAuNixcbiAgICAgICAgICAgIHN0cm9rZUFscGhhOiAxLFxuICAgICAgICAgICAgc3Ryb2tlQWN0aXZlOiBjb2xvcjIsXG4gICAgICAgICAgICBzdHJva2VBY3RpdmVXaWR0aDogLjYsXG4gICAgICAgICAgICBzdHJva2VBY3RpdmVBbHBoYTogMSxcbiAgICAgICAgICAgIGljb25Db2xvcjogY29sb3IyLFxuICAgICAgICAgICAgaWNvbkNvbG9yQWN0aXZlOiBjb2xvclByaW1hcnksXG4gICAgICAgICAgICBiYWNrZ3JvdW5kOiBjb2xvcjFcbiAgICAgICAgfSwgb3B0cylcblxuICAgICAgICAvLyBTZXQgdGV4dFN0eWxlIGFuZCB2YXJpYW50c1xuICAgICAgICB0aGlzLm9wdHMudGV4dFN0eWxlID0gT2JqZWN0LmFzc2lnbih7fSwge1xuICAgICAgICAgICAgZm9udEZhbWlseTogJ1wiQXZlbmlyIE5leHRcIiwgXCJPcGVuIFNhbnNcIiwgXCJTZWdvZSBVSVwiLCBcIlJvYm90b1wiLCBcIkhlbHZldGljYSBOZXVlXCIsIC1hcHBsZS1zeXN0ZW0sIHN5c3RlbS11aSwgQmxpbmtNYWNTeXN0ZW1Gb250LCBBcmlhbCwgc2Fucy1zZXJpZiAhZGVmYXVsdCcsXG4gICAgICAgICAgICBmb250V2VpZ2h0OiAnNTAwJyxcbiAgICAgICAgICAgIGZvbnRTaXplOiAxOCxcbiAgICAgICAgICAgIGZpbGw6IGNvbG9yMixcbiAgICAgICAgICAgIHN0cm9rZTogY29sb3IxLFxuICAgICAgICAgICAgc3Ryb2tlVGhpY2tuZXNzOiAwLFxuICAgICAgICAgICAgbWl0ZXJMaW1pdDogMSxcbiAgICAgICAgICAgIGxpbmVKb2luOiAncm91bmQnXG4gICAgICAgIH0sIHRoaXMub3B0cy50ZXh0U3R5bGUpXG4gICAgICAgIHRoaXMub3B0cy50ZXh0U3R5bGVTbWFsbCA9IE9iamVjdC5hc3NpZ24oe30sIHRoaXMub3B0cy50ZXh0U3R5bGUsIHtmb250U2l6ZTogdGhpcy5vcHRzLnRleHRTdHlsZS5mb250U2l6ZSAtIDN9LCB0aGlzLm9wdHMudGV4dFN0eWxlU21hbGwpXG4gICAgICAgIHRoaXMub3B0cy50ZXh0U3R5bGVMYXJnZSA9IE9iamVjdC5hc3NpZ24oe30sIHRoaXMub3B0cy50ZXh0U3R5bGUsIHtmb250U2l6ZTogdGhpcy5vcHRzLnRleHRTdHlsZS5mb250U2l6ZSArIDN9LCB0aGlzLm9wdHMudGV4dFN0eWxlTGFyZ2UpXG4gICAgICAgIHRoaXMub3B0cy50ZXh0U3R5bGVBY3RpdmUgPSBPYmplY3QuYXNzaWduKHt9LCB0aGlzLm9wdHMudGV4dFN0eWxlLCB7ZmlsbDogdGhpcy5vcHRzLnByaW1hcnlDb2xvcn0sIHRoaXMub3B0cy50ZXh0U3R5bGVBY3RpdmUpXG4gICAgICAgIHRoaXMub3B0cy50ZXh0U3R5bGVTbWFsbEFjdGl2ZSA9IE9iamVjdC5hc3NpZ24oe30sIHRoaXMub3B0cy50ZXh0U3R5bGVTbWFsbCwge2ZpbGw6IHRoaXMub3B0cy5wcmltYXJ5Q29sb3J9LCB0aGlzLm9wdHMudGV4dFN0eWxlU21hbGxBY3RpdmUpXG4gICAgICAgIHRoaXMub3B0cy50ZXh0U3R5bGVMYXJnZUFjdGl2ZSA9IE9iamVjdC5hc3NpZ24oe30sIHRoaXMub3B0cy50ZXh0U3R5bGVMYXJnZSwge2ZpbGw6IHRoaXMub3B0cy5wcmltYXJ5Q29sb3J9LCB0aGlzLm9wdHMudGV4dFN0eWxlTGFyZ2VBY3RpdmUpXG5cbiAgICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLCB0aGlzLm9wdHMpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRmFjdG9yeSBmdW5jdGlvblxuICAgICAqXG4gICAgICogQHN0YXRpY1xuICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0aGVtZT1kYXJrIC0gVGhlIG5hbWUgb2YgdGhlIHRoZW1lIHRvIGxvYWQuXG4gICAgICogQHJldHVybiB7VGhlbWV9IFJldHVybnMgYSBuZXdseSBjcmVhdGVkIFRoZW1lIG9iamVjdC5cbiAgICAgKi9cbiAgICBzdGF0aWMgZnJvbVN0cmluZyh0aGVtZSkge1xuXG4gICAgICAgIGlmICh0aGVtZSAmJiB0eXBlb2YgdGhlbWUgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhlbWVcbiAgICAgICAgfVxuXG4gICAgICAgIHN3aXRjaCAodGhlbWUpIHtcbiAgICAgICAgICAgIGNhc2UgJ2xpZ2h0JzpcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IFRoZW1lTGlnaHQoKVxuICAgICAgICAgICAgY2FzZSAncmVkJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IFRoZW1lUmVkKClcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBUaGVtZURhcmsoKVxuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIENsYXNzIHRoYXQgcmVwcmVzZW50cyBhIFBpeGlKUyBUaGVtZURhcmsuXG4gKlxuICogQGV4YW1wbGVcbiAqIC8vIENyZWF0ZSB0aGUgYXBwIHdpdGggYSBuZXcgZGFyayB0aGVtZVxuICogY29uc3QgYXBwID0gbmV3IFBJWElBcHAoe1xuICogICAgIHZpZXc6IGNhbnZhcyxcbiAqICAgICB3aWR0aDogNDUwLFxuICogICAgIGhlaWdodDogMTUwLFxuICogICAgIHRoZW1lOiBuZXcgVGhlbWVEYXJrKClcbiAqIH0pLnNldHVwKCkucnVuKClcbiAqXG4gKiBAY2xhc3NcbiAqIEBleHRlbmRzIFRoZW1lXG4gKiBAc2VlIHtAbGluayBodHRwczovL3d3dy5pd20tdHVlYmluZ2VuLmRlL2l3bWJyb3dzZXIvbGliL3BpeGkvdGhlbWUuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgY2xhc3MgVGhlbWVEYXJrIGV4dGVuZHMgVGhlbWUge1xuXG59XG5cbi8qKlxuICogQ2xhc3MgdGhhdCByZXByZXNlbnRzIGEgUGl4aUpTIFRoZW1lTGlnaHQuXG4gKiBUaGUgY29sb3IxIGlzIHNldCB0byAweGY2ZjZmNiwgY29sb3IyIHRvIDB4MjgyODI4LlxuICpcbiAqIEBleGFtcGxlXG4gKiAvLyBDcmVhdGUgdGhlIGFwcCB3aXRoIGEgbmV3IGxpZ2h0IHRoZW1lXG4gKiBjb25zdCBhcHAgPSBuZXcgUElYSUFwcCh7XG4gKiAgICAgdmlldzogY2FudmFzLFxuICogICAgIHdpZHRoOiA0NTAsXG4gKiAgICAgaGVpZ2h0OiAxNTAsXG4gKiAgICAgdGhlbWU6IG5ldyBUaGVtZUxpZ2h0KClcbiAqIH0pLnNldHVwKCkucnVuKClcbiAqXG4gKiBAY2xhc3NcbiAqIEBleHRlbmRzIFRoZW1lXG4gKiBAc2VlIHtAbGluayBodHRwczovL3d3dy5pd20tdHVlYmluZ2VuLmRlL2l3bWJyb3dzZXIvbGliL3BpeGkvdGhlbWUuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgY2xhc3MgVGhlbWVMaWdodCBleHRlbmRzIFRoZW1lIHtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYSBUaGVtZUxpZ2h0LlxuICAgICAqXG4gICAgICogQGNvbnN0cnVjdG9yXG4gICAgICovXG4gICAgY29uc3RydWN0b3IoKSB7XG5cbiAgICAgICAgc3VwZXIoe2NvbG9yMTogMHhmNmY2ZjYsIGNvbG9yMjogMHgyODI4Mjh9KVxuICAgIH1cbn1cblxuLyoqXG4gKiBDbGFzcyB0aGF0IHJlcHJlc2VudHMgYSBQaXhpSlMgVGhlbWVSZWQuXG4gKiBUaGUgcHJpbWFyeUNvbG9yIGlzIHNldCB0byAweGQ5MmYzMS5cbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gQ3JlYXRlIHRoZSBhcHAgd2l0aCBhIG5ldyByZWQgdGhlbWVcbiAqIGNvbnN0IGFwcCA9IG5ldyBQSVhJQXBwKHtcbiAqICAgICB2aWV3OiBjYW52YXMsXG4gKiAgICAgd2lkdGg6IDQ1MCxcbiAqICAgICBoZWlnaHQ6IDE1MCxcbiAqICAgICB0aGVtZTogbmV3IFRoZW1lUmVkKClcbiAqIH0pLnNldHVwKCkucnVuKClcbiAqXG4gKiBAY2xhc3NcbiAqIEBleHRlbmRzIFRoZW1lXG4gKiBAc2VlIHtAbGluayBodHRwczovL3d3dy5pd20tdHVlYmluZ2VuLmRlL2l3bWJyb3dzZXIvbGliL3BpeGkvdGhlbWUuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgY2xhc3MgVGhlbWVSZWQgZXh0ZW5kcyBUaGVtZSB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIGEgVGhlbWVSZWQuXG4gICAgICpcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcigpIHtcblxuICAgICAgICBzdXBlcih7cHJpbWFyeUNvbG9yOiAweGQ5MmYzMX0pXG4gICAgfVxufVxuIiwiaW1wb3J0IFRoZW1lIGZyb20gJy4vdGhlbWUuanMnXG5cbi8qKlxuICogQ2xhc3MgdGhhdCByZXByZXNlbnRzIGEgUGl4aUpTIFByb2dyZXNzLlxuICogXG4gKiBAZXhhbXBsZVxuICogLy8gQ3JlYXRlIHRoZSBwcm9ncmVzc1xuICogY29uc3QgcHJvZ3Jlc3MgPSBuZXcgUHJvZ3Jlc3Moe1xuICogICAgIGFwcDogYXBwXG4gKiB9KVxuICpcbiAqIC8vIEFkZCB0aGUgcHJvZ3Jlc3MgdG8gYSBEaXNwbGF5T2JqZWN0XG4gKiBhcHAuc2NlbmUuYWRkQ2hpbGQocHJvZ3Jlc3MpXG4gKlxuICogQGNsYXNzXG4gKiBAZXh0ZW5kcyBQSVhJLkNvbnRhaW5lclxuICogQHNlZSB7QGxpbmsgaHR0cDovL3BpeGlqcy5kb3dubG9hZC9kZXYvZG9jcy9QSVhJLkNvbnRhaW5lci5odG1sfFBJWEkuQ29udGFpbmVyfVxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly93d3cuaXdtLXR1ZWJpbmdlbi5kZS9pd21icm93c2VyL2xpYi9waXhpL3Byb2dyZXNzLmh0bWx8RG9jVGVzdH1cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgUHJvZ3Jlc3MgZXh0ZW5kcyBQSVhJLkNvbnRhaW5lciB7XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhIFByb2dyZXNzLlxuICAgICAqIFxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0c10gLSBBbiBvcHRpb25zIG9iamVjdCB0byBzcGVjaWZ5IHRvIHN0eWxlIGFuZCBiZWhhdmlvdXIgb2YgdGhlIHByb2dyZXNzLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5pZD1hdXRvIGdlbmVyYXRlZF0gLSBUaGUgaWQgb2YgdGhlIHByb2dyZXNzLlxuICAgICAqIEBwYXJhbSB7UElYSUFwcH0gW29wdHMuYXBwPXdpbmRvdy5hcHBdIC0gVGhlIGFwcCB3aGVyZSB0aGUgcHJvZ3Jlc3MgYmVsb25ncyB0by5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMud2lkdGhdIC0gVGhlIHdpZHRoIG9mIHRoZSBwcm9ncmVzcyBiYXIuIFdoZW4gbm90IHNldCwgdGhlIHdpZHRoIGlzIHRoZSBzaXplIG9mIHRoZSBhcHBcbiAgICAgKiAgICAgbWludXMgMiAqIG9wdHMubWFyZ2luLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5oZWlnaHQ9Ml0gLSBUaGUgaGVpZ2h0IG9mIHRoZSBwcm9ncmVzcyBiYXIuXG4gICAgICogQHBhcmFtIHtzdHJpbmd8VGhlbWV9IFtvcHRzLnRoZW1lPWRhcmtdIC0gVGhlIHRoZW1lIHRvIHVzZSBmb3IgdGhpcyBwcm9ncmVzcy4gUG9zc2libGUgdmFsdWVzIGFyZSBkYXJrLCBsaWdodCwgcmVkXG4gICAgICogICAgIG9yIGEgVGhlbWUgb2JqZWN0LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5tYXJnaW49MTAwXSAtIFRoZSBvdXRlciBzcGFjaW5nIHRvIHRoZSBlZGdlcyBvZiB0aGUgYXBwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5wYWRkaW5nPTBdIC0gVGhlIGlubmVyIHNwYWNpbmcgKGRpc3RhbmNlIGZyb20gaWNvbiBhbmQvb3IgbGFiZWwpIHRvIHRoZSBib3JkZXIuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmZpbGw9VGhlbWUuZmlsbF0gLSBUaGUgY29sb3Igb2YgdGhlIHByb2dyZXNzIGJhY2tncm91bmQgYXMgYSBoZXggdmFsdWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmZpbGxBbHBoYT1UaGVtZS5maWxsQWxwaGFdIC0gVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBiYWNrZ3JvdW5kLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5maWxsQWN0aXZlPVRoZW1lLnByaW1hcnlDb2xvcl0gLSBUaGUgY29sb3Igb2YgdGhlIHByb2dyZXNzIGJhY2tncm91bmQgd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmZpbGxBY3RpdmVBbHBoYT1UaGVtZS5maWxsQWN0aXZlQWxwaGFdIC0gVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBiYWNrZ3JvdW5kIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2U9VGhlbWUuc3Ryb2tlXSAtIFRoZSBjb2xvciBvZiB0aGUgYm9yZGVyIGFzIGEgaGV4IHZhbHVlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VXaWR0aD0wXSAtIFRoZSB3aWR0aCBvZiB0aGUgYm9yZGVyIGluIHBpeGVsLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VBbHBoYT1UaGVtZS5zdHJva2VBbHBoYV0gLSBUaGUgYWxwaGEgdmFsdWUgb2YgdGhlIGJvcmRlci5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlQWN0aXZlPVRoZW1lLnN0cm9rZUFjdGl2ZV0gLSBUaGUgY29sb3Igb2YgdGhlIGJvcmRlciB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlQWN0aXZlV2lkdGg9MF0gLSBUaGUgd2lkdGggb2YgdGhlIGJvcmRlciBpbiBwaXhlbCB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlQWN0aXZlQWxwaGE9VGhlbWUuc3Ryb2tlQWN0aXZlQWxwaGFdIC0gVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBib3JkZXIgd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5iYWNrZ3JvdW5kPWZhbHNlXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYm9yZGVyIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5iYWNrZ3JvdW5kRmlsbD1UaGVtZS5iYWNrZ3JvdW5kXSAtIEEgdGV4dHN0eWxlIG9iamVjdCBmb3IgdGhlIHN0eWxpbmcgb2YgdGhlIGxhYmVsLiBTZWUgUElYSS5UZXh0U3R5bGVcbiAgICAgKiAgICAgZm9yIHBvc3NpYmxlIG9wdGlvbnMuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmJhY2tncm91bmRGaWxsQWxwaGE9MV0gLSBBIHRleHRzdHlsZSBvYmplY3QgZm9yIHRoZSBzdHlsaW5nIG9mIHRoZSBsYWJlbCB3aGVuIHRoZVxuICAgICAqICAgICBwcm9ncmVzcyBpcyBhY3RpdmF0ZWQuIFNlZSBQSVhJLlRleHRTdHlsZSBmb3IgcG9zc2libGUgb3B0aW9ucy5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMucmFkaXVzPVRoZW1lLnJhZGl1c10gLSBUaGUgcmFkaXVzIG9mIHRoZSBmb3VyIGNvcm5lcnMgb2YgdGhlIHByb2dyZXNzICh3aGljaCBpcyBhIHJvdW5kZWQgcmVjdGFuZ2xlKS5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmRlc3Ryb3lPbkNvbXBsZXRlPXRydWVdIC0gU2hvdWxkIHRoZSBwcm9ncmVzcyBiYXIgZGVzdHJveSBpdHNlbGYgYWZ0ZXIgcmVhY2hpbmcgMTAwICU/XG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy52aXNpYmxlPXRydWVdIC0gSXMgdGhlIHByb2dyZXNzIGluaXRpYWxseSB2aXNpYmxlIChwcm9wZXJ0eSB2aXNpYmxlKT9cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihvcHRzID0ge30pIHtcblxuICAgICAgICBzdXBlcigpXG4gICAgICAgIFxuICAgICAgICBjb25zdCB0aGVtZSA9IFRoZW1lLmZyb21TdHJpbmcob3B0cy50aGVtZSlcbiAgICAgICAgdGhpcy50aGVtZSA9IHRoZW1lXG5cbiAgICAgICAgdGhpcy5vcHRzID0gT2JqZWN0LmFzc2lnbih7fSwge1xuICAgICAgICAgICAgaWQ6IFBJWEkudXRpbHMudWlkKCksXG4gICAgICAgICAgICBhcHA6IHdpbmRvdy5hcHAsXG4gICAgICAgICAgICB3aWR0aDogbnVsbCxcbiAgICAgICAgICAgIGhlaWdodDogMixcbiAgICAgICAgICAgIG1hcmdpbjogMTAwLFxuICAgICAgICAgICAgcGFkZGluZzogMCxcbiAgICAgICAgICAgIGZpbGw6IHRoZW1lLmZpbGwsXG4gICAgICAgICAgICBmaWxsQWxwaGE6IHRoZW1lLmZpbGxBbHBoYSxcbiAgICAgICAgICAgIGZpbGxBY3RpdmU6IHRoZW1lLnByaW1hcnlDb2xvcixcbiAgICAgICAgICAgIGZpbGxBY3RpdmVBbHBoYTogdGhlbWUuZmlsbEFjdGl2ZUFscGhhLFxuICAgICAgICAgICAgc3Ryb2tlOiB0aGVtZS5zdHJva2UsXG4gICAgICAgICAgICBzdHJva2VXaWR0aDogMCxcbiAgICAgICAgICAgIHN0cm9rZUFscGhhOiB0aGVtZS5zdHJva2VBbHBoYSxcbiAgICAgICAgICAgIHN0cm9rZUFjdGl2ZTogdGhlbWUuc3Ryb2tlQWN0aXZlLFxuICAgICAgICAgICAgc3Ryb2tlQWN0aXZlV2lkdGg6IDAsXG4gICAgICAgICAgICBzdHJva2VBY3RpdmVBbHBoYTogdGhlbWUuc3Ryb2tlQWN0aXZlQWxwaGEsXG4gICAgICAgICAgICBiYWNrZ3JvdW5kOiBmYWxzZSxcbiAgICAgICAgICAgIGJhY2tncm91bmRGaWxsOiB0aGVtZS5iYWNrZ3JvdW5kLFxuICAgICAgICAgICAgYmFja2dyb3VuZEZpbGxBbHBoYTogMSxcbiAgICAgICAgICAgIHJhZGl1czogdGhlbWUucmFkaXVzLFxuICAgICAgICAgICAgZGVzdHJveU9uQ29tcGxldGU6IHRydWUsXG4gICAgICAgICAgICB2aXNpYmxlOiB0cnVlXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgdGhpcy5pZCA9IHRoaXMub3B0cy5pZFxuXG4gICAgICAgIHRoaXMuYmFja2dyb3VuZCA9IG51bGxcbiAgICAgICAgdGhpcy5iYXIgPSBudWxsXG4gICAgICAgIHRoaXMuYmFyQWN0aXZlID0gbnVsbFxuXG4gICAgICAgIHRoaXMuYWxwaGEgPSAwXG4gICAgICAgIFxuICAgICAgICB0aGlzLnZpc2libGUgPSB0aGlzLm9wdHMudmlzaWJsZVxuXG4gICAgICAgIHRoaXMuX3Byb2dyZXNzID0gMFxuXG4gICAgICAgIC8vIHNldHVwXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5zZXR1cCgpXG5cbiAgICAgICAgLy8gbGF5b3V0XG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5sYXlvdXQoKVxuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGNoaWxkcmVuIGFuZCBpbnN0YW50aWF0ZXMgZXZlcnl0aGluZy5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge1Byb2dyZXNzfSBBIHJlZmVyZW5jZSB0byB0aGUgcHJvZ3Jlc3MgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHNldHVwKCkge1xuXG4gICAgICAgIC8vIGludGVyYWN0aW9uXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5vbignYWRkZWQnLCBlID0+IHtcbiAgICAgICAgICAgIHRoaXMuc2hvdygpXG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gYmFja2dyb3VuZFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGlmICh0aGlzLm9wdHMuYmFja2dyb3VuZCkge1xuICAgICAgICAgICAgY29uc3QgYmFja2dyb3VuZCA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAgICAgICAgICAgIHRoaXMuYmFja2dyb3VuZCA9IGJhY2tncm91bmRcbiAgICAgICAgICAgIHRoaXMuYWRkQ2hpbGQoYmFja2dyb3VuZClcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGJhclxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGNvbnN0IGJhciA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAgICAgICAgdGhpcy5iYXIgPSBiYXJcbiAgICAgICAgdGhpcy5hZGRDaGlsZChiYXIpXG5cbiAgICAgICAgY29uc3QgYmFyQWN0aXZlID0gbmV3IFBJWEkuR3JhcGhpY3MoKVxuICAgICAgICB0aGlzLmJhckFjdGl2ZSA9IGJhckFjdGl2ZVxuICAgICAgICB0aGlzLmJhci5hZGRDaGlsZChiYXJBY3RpdmUpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogU2hvdWxkIGJlIGNhbGxlZCB0byByZWZyZXNoIHRoZSBsYXlvdXQgb2YgdGhlIHByb2dyZXNzLiBDYW4gYmUgdXNlZCBhZnRlciByZXNpemluZy5cbiAgICAgKiBcbiAgICAgKiBAcmV0dXJuIHtQcm9ncmVzc30gQSByZWZlcmVuY2UgdG8gdGhlIHByb2dyZXNzIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBsYXlvdXQoKSB7XG5cbiAgICAgICAgY29uc3Qgd2lkdGggPSB0aGlzLm9wdHMuYXBwLnNpemUud2lkdGhcbiAgICAgICAgY29uc3QgaGVpZ2h0ID0gdGhpcy5vcHRzLmFwcC5zaXplLmhlaWdodFxuXG4gICAgICAgIC8vIGJhY2tncm91bmRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLmJhY2tncm91bmQpIHtcbiAgICAgICAgICAgIHRoaXMuYmFja2dyb3VuZC5jbGVhcigpXG4gICAgICAgICAgICB0aGlzLmJhY2tncm91bmQuYmVnaW5GaWxsKHRoaXMub3B0cy5iYWNrZ3JvdW5kRmlsbCwgdGhpcy5vcHRzLmJhY2tncm91bmRGaWxsQWxwaGEpXG4gICAgICAgICAgICB0aGlzLmJhY2tncm91bmQuZHJhd1JlY3QoMCwgMCwgd2lkdGgsIGhlaWdodClcbiAgICAgICAgICAgIHRoaXMuYmFja2dyb3VuZC5lbmRGaWxsKClcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZHJhdygpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogRHJhd3MgdGhlIGNhbnZhcy5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge1Byb2dyZXNzfSBBIHJlZmVyZW5jZSB0byB0aGUgcHJvZ3Jlc3MgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGRyYXcoKSB7XG5cbiAgICAgICAgdGhpcy5iYXIuY2xlYXIoKVxuICAgICAgICB0aGlzLmJhckFjdGl2ZS5jbGVhcigpXG5cbiAgICAgICAgdGhpcy5kcmF3QmFyKClcbiAgICAgICAgdGhpcy5kcmF3QmFyQWN0aXZlKClcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBEcmF3cyB0aGUgYmFyLlxuICAgICAqIFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybiB7UHJvZ3Jlc3N9IEEgcmVmZXJlbmNlIHRvIHRoZSBwcm9ncmVzcyBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgZHJhd0JhcigpIHtcblxuICAgICAgICBjb25zdCB3aWR0aCA9IHRoaXMub3B0cy5hcHAuc2l6ZS53aWR0aFxuICAgICAgICBjb25zdCBoZWlnaHQgPSB0aGlzLm9wdHMuYXBwLnNpemUuaGVpZ2h0XG5cbiAgICAgICAgdGhpcy5yYWRpdXMgPSB0aGlzLm9wdHMucmFkaXVzXG4gICAgICAgIGlmICgodGhpcy5yYWRpdXMgKiAyKSA+IHRoaXMub3B0cy5oZWlnaHQpIHtcbiAgICAgICAgICAgIHRoaXMucmFkaXVzID0gdGhpcy5vcHRzLmhlaWdodCAvIDJcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHdhbnRlZFdpZHRoID0gdGhpcy5vcHRzLndpZHRoIHx8ICh3aWR0aCAtICgyICogdGhpcy5vcHRzLm1hcmdpbikpXG4gICAgICAgIGNvbnN0IHdhbnRlZEhlaWdodCA9IHRoaXMub3B0cy5oZWlnaHRcblxuICAgICAgICB0aGlzLmJhci5saW5lU3R5bGUodGhpcy5vcHRzLnN0cm9rZVdpZHRoLCB0aGlzLm9wdHMuc3Ryb2tlLCB0aGlzLm9wdHMuc3Ryb2tlQWxwaGEpXG4gICAgICAgIHRoaXMuYmFyLmJlZ2luRmlsbCh0aGlzLm9wdHMuZmlsbCwgdGhpcy5vcHRzLmZpbGxBbHBoYSlcbiAgICAgICAgaWYgKHRoaXMucmFkaXVzID4gMSkge1xuICAgICAgICAgICAgdGhpcy5iYXIuZHJhd1JvdW5kZWRSZWN0KDAsIDAsIHdhbnRlZFdpZHRoLCB3YW50ZWRIZWlnaHQsIHRoaXMucmFkaXVzKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5iYXIuZHJhd1JlY3QoMCwgMCwgd2FudGVkV2lkdGgsIHdhbnRlZEhlaWdodClcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmJhci5lbmRGaWxsKClcbiAgICAgICAgXG4gICAgICAgIHRoaXMuYmFyLnggPSB3aWR0aCAvIDIgLSB0aGlzLmJhci53aWR0aCAvIDJcbiAgICAgICAgdGhpcy5iYXIueSA9IGhlaWdodCAvIDIgLSB0aGlzLmJhci5oZWlnaHQgLyAyXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogRHJhd3MgdGhlIGFjdGl2ZSBiYXIuXG4gICAgICogXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcmV0dXJuIHtQcm9ncmVzc30gQSByZWZlcmVuY2UgdG8gdGhlIHByb2dyZXNzIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBkcmF3QmFyQWN0aXZlKCkge1xuXG4gICAgICAgIGNvbnN0IHdhbnRlZFdpZHRoID0gdGhpcy5iYXIud2lkdGggLSAoMiAqIHRoaXMub3B0cy5wYWRkaW5nKVxuICAgICAgICBjb25zdCB3YW50ZWRIZWlnaHQgPSB0aGlzLmJhci5oZWlnaHQgLSAoMiAqIHRoaXMub3B0cy5wYWRkaW5nKVxuICAgICAgICBcbiAgICAgICAgY29uc3QgYmFyQWN0aXZlV2lkdGggPSB3YW50ZWRXaWR0aCAqIHRoaXMuX3Byb2dyZXNzIC8gMTAwXG5cbiAgICAgICAgdGhpcy5iYXJBY3RpdmUubGluZVN0eWxlKHRoaXMub3B0cy5zdHJva2VBY3RpdmVXaWR0aCwgdGhpcy5vcHRzLnN0cm9rZUFjdGl2ZSwgdGhpcy5vcHRzLnN0cm9rZUFjdGl2ZUFscGhhKVxuICAgICAgICB0aGlzLmJhckFjdGl2ZS5iZWdpbkZpbGwodGhpcy5vcHRzLmZpbGxBY3RpdmUsIHRoaXMub3B0cy5maWxsQWN0aXZlQWxwaGEpXG4gICAgICAgIGlmIChiYXJBY3RpdmVXaWR0aCA+IDApIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnJhZGl1cyA+IDEpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmJhckFjdGl2ZS5kcmF3Um91bmRlZFJlY3QoMCwgMCwgYmFyQWN0aXZlV2lkdGgsIHdhbnRlZEhlaWdodCwgdGhpcy5yYWRpdXMpXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMuYmFyQWN0aXZlLmRyYXdSZWN0KDAsIDAsIGJhckFjdGl2ZVdpZHRoLCB3YW50ZWRIZWlnaHQpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5iYXJBY3RpdmUuZW5kRmlsbCgpXG5cbiAgICAgICAgdGhpcy5iYXJBY3RpdmUueCA9IHRoaXMub3B0cy5wYWRkaW5nXG4gICAgICAgIHRoaXMuYmFyQWN0aXZlLnkgPSB0aGlzLm9wdHMucGFkZGluZ1xuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFNob3dzIHRoZSBwcm9ncmVzcyAoc2V0cyBoaXMgYWxwaGEgdmFsdWVzIHRvIDEpLlxuICAgICAqIFxuICAgICAqIEByZXR1cm4ge1Byb2dyZXNzfSBBIHJlZmVyZW5jZSB0byB0aGUgcHJvZ3Jlc3MgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHNob3coKSB7XG4gICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLCB0aGlzLnRoZW1lLmZhc3QsIHthbHBoYTogMX0pXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogSGlkZXMgdGhlIHByb2dyZXNzIChzZXRzIGhpcyBhbHBoYSB2YWx1ZXMgdG8gMSkuXG4gICAgICogXG4gICAgICogQHJldHVybiB7UHJvZ3Jlc3N9IEEgcmVmZXJlbmNlIHRvIHRoZSBwcm9ncmVzcyBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgaGlkZSgpIHtcbiAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMsIHRoaXMudGhlbWUuZmFzdCwge2FscGhhOiAwLCBvbkNvbXBsZXRlOiAoKSA9PiB0aGlzLnZpc2libGUgPSBmYWxzZX0pXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogR2V0cyBvciBzZXRzIHRoZSBwcm9ncmVzcy4gSGFzIHRvIGJlIGEgbnVtYmVyIGJldHdlZW4gMCBhbmQgMTAwLlxuICAgICAqIFxuICAgICAqIEBtZW1iZXIge251bWJlcn1cbiAgICAgKi9cbiAgICBnZXQgcHJvZ3Jlc3MoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9wcm9ncmVzc1xuICAgIH1cbiAgICBzZXQgcHJvZ3Jlc3ModmFsdWUpIHtcblxuICAgICAgICB2YWx1ZSA9IE1hdGgucm91bmQodmFsdWUpXG5cbiAgICAgICAgaWYgKHZhbHVlIDwgMCkge1xuICAgICAgICAgICAgdmFsdWUgPSAwXG4gICAgICAgIH1cblxuICAgICAgICBpZiAodmFsdWUgPiAxMDApIHtcbiAgICAgICAgICAgIHZhbHVlID0gMTAwXG4gICAgICAgIH1cblxuICAgICAgICBUd2VlbkxpdGUudG8odGhpcywgdGhpcy50aGVtZS5ub3JtYWwsIHtcbiAgICAgICAgICAgIF9wcm9ncmVzczogdmFsdWUsXG4gICAgICAgICAgICBvblVwZGF0ZTogKCkgPT4gdGhpcy5kcmF3KCksXG4gICAgICAgICAgICBvbkNvbXBsZXRlOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHZhbHVlID09PSAxMDAgJiYgdGhpcy5vcHRzLmRlc3Ryb3lPbkNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLCB0aGlzLnRoZW1lLmZhc3QsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhOiAwLFxuICAgICAgICAgICAgICAgICAgICAgICAgb25Db21wbGV0ZTogKCkgPT4gdGhpcy5kZXN0cm95KHtjaGlsZHJlbjogdHJ1ZX0pXG4gICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgIH1cbn1cbiIsImltcG9ydCBUaGVtZSBmcm9tICcuL3RoZW1lLmpzJ1xuXG4vKipcbiAqIENsYXNzIHRoYXQgcmVwcmVzZW50cyBhIFBpeGlKUyBBYnN0cmFjdFBvcHVwLlxuICogVGhlIGNsYXNzIGlzIHVzZWQgZm9yIHZhcmlvdXMgb3RoZXIgUG9wdXAtbGlrZSBjbGFzc2VzXG4gKiBsaWtlIFBvcHVwLCBNZXNzYWdlLCBUb29sdGlwLi4uXG4gKlxuICogQGNsYXNzXG4gKiBAYWJzdHJhY3RcbiAqIEBleHRlbmRzIFBJWEkuR3JhcGhpY3NcbiAqIEBzZWUge0BsaW5rIGh0dHA6Ly9waXhpanMuZG93bmxvYWQvZGV2L2RvY3MvUElYSS5HcmFwaGljcy5odG1sfFBJWEkuR3JhcGhpY3N9XG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEFic3RyYWN0UG9wdXAgZXh0ZW5kcyBQSVhJLkdyYXBoaWNzIHtcbiAgICBcbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIGFuIEFic3RyYWN0UG9wdXAgKG9ubHkgZm9yIGludGVybmFsIHVzZSkuXG4gICAgICogXG4gICAgICogQGNvbnN0cnVjdG9yXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzXSAtIEFuIG9wdGlvbnMgb2JqZWN0IHRvIHNwZWNpZnkgdG8gc3R5bGUgYW5kIGJlaGF2aW91ciBvZiB0aGUgcG9wdXAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmlkPWF1dG8gZ2VuZXJhdGVkXSAtIFRoZSBpZCBvZiB0aGUgcG9wdXAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLng9MF0gLSBUaGUgeCBwb3NpdGlvbiBvZiB0aGUgcG9wdXAuIENhbiBiZSBhbHNvIHNldCBhZnRlciBjcmVhdGlvbiB3aXRoIHBvcHVwLnggPSAwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy55PTBdIC0gVGhlIHkgcG9zaXRpb24gb2YgdGhlIHBvcHVwLiBDYW4gYmUgYWxzbyBzZXQgYWZ0ZXIgY3JlYXRpb24gd2l0aCBwb3B1cC55ID0gMC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ3xUaGVtZX0gW29wdHMudGhlbWU9ZGFya10gLSBUaGUgdGhlbWUgdG8gdXNlIGZvciB0aGlzIHBvcHVwLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIGRhcmssIGxpZ2h0LCByZWRcbiAgICAgKiAgICAgb3IgYSBUaGVtZSBvYmplY3QuXG4gICAgICogQHBhcmFtIHtzdHJpbmd8bnVtYmVyfFBJWEkuVGV4dH0gW29wdHMuaGVhZGVyXSAtIFRoZSBoZWFkaW5nIGluc2lkZSB0aGUgcG9wdXAgYXMgYSBzdHJpbmcsIGEgbnVtYmVyICh3aWxsIGJlXG4gICAgICogICAgIGNvbnZlcnRlZCB0byBhIHRleHQpIG9yIGFzIGEgUElYSS5UZXh0IG9iamVjdC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ3xudW1iZXJ8UElYSS5EaXNwbGF5T2JqZWN0fSBbb3B0cy5jb250ZW50XSAtIEEgdGV4dCwgYSBudW1iZXIgKHdpbGwgYmUgY29udmVydGVkIHRvIGEgdGV4dCkgb3JcbiAgICAgKiAgICAgYW4gUElYSS5EaXNwbGF5T2JqZWN0IGFzIHRoZSBjb250ZW50IG9mIHRoZSBwb3B1cC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMubWluV2lkdGg9MzIwXSAtIFRoZSBtaW5pbXVtIHdpZHRoIG9mIHRoZSBwb3B1cC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMubWluSGVpZ2h0PTEzMF0gLSBUaGUgbWluaW11bSBoZWlnaHQgb2YgdGhlIHBvcHVwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5wYWRkaW5nPVRoZW1lLnBhZGRpbmddIC0gVGhlIGlubmVyIHNwYWNpbmcgKGRpc3RhbmNlIGZyb20gaGVhZGVyIGFuZCBjb250ZW50KSB0aGUgdGhlIGJvcmRlci5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZmlsbD1UaGVtZS5maWxsXSAtIFRoZSBjb2xvciBvZiB0aGUgYnV0dG9uIGJhY2tncm91bmQgYXMgYSBoZXggdmFsdWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmZpbGxBbHBoYT1UaGVtZS5maWxsQWxwaGFdIC0gVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBiYWNrZ3JvdW5kLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2U9VGhlbWUuc3Ryb2tlXSAtIFRoZSBjb2xvciBvZiB0aGUgYm9yZGVyIGFzIGEgaGV4IHZhbHVlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VXaWR0aD1UaGVtZS5zdHJva2VXaWR0aF0gLSBUaGUgd2lkdGggb2YgdGhlIGJvcmRlciBpbiBwaXhlbC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlQWxwaGE9VGhlbWUuc3Ryb2tlQWxwaGFdIC0gVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBib3JkZXIuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzLmhlYWRlclN0eWxlPVRoZW1lLnRleHRTdHlsZUxhcmdlXSAtIEEgdGV4dHN0eWxlIG9iamVjdCBmb3IgdGhlIHN0eWxpbmcgb2YgdGhlIGhlYWRlci4gU2VlIFBJWEkuVGV4dFN0eWxlXG4gICAgICogICAgIGZvciBwb3NzaWJsZSBvcHRpb25zLlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0cy50ZXh0U3R5bGU9VGhlbWUudGV4dFN0eWxlU21hbGxdIC0gQSB0ZXh0c3R5bGUgb2JqZWN0IGZvciB0aGUgc3R5bGluZyBvZiB0aGUgdGV4dC4gU2VlIFBJWEkuVGV4dFN0eWxlXG4gICAgICogICAgIGZvciBwb3NzaWJsZSBvcHRpb25zLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5yYWRpdXM9VGhlbWUucmFkaXVzXSAtIFRoZSByYWRpdXMgb2YgdGhlIGZvdXIgY29ybmVycyBvZiB0aGUgcG9wdXAgKHdoaWNoIGlzIGEgcm91bmRlZCByZWN0YW5nbGUpLlxuICAgICAqIEBwYXJhbSB7aGlkZGVuQ2FsbGJhY2t9IFtvcHRzLm9uSGlkZGVuXSAtIEV4ZWN1dGVkIHdoZW4gdGhlIHBvcHVwIGdldHMgaGlkZGVuLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMudmlzaWJsZT10cnVlXSAtIElzIHRoZSBwb3B1cCBpbml0aWFsbHkgdmlzaWJsZSAocHJvcGVydHkgdmlzaWJsZSk/XG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLm9yaWVudGF0aW9uXSAtIFdoZW4gc2V0IHRvIHBvcnRyYWl0LCB0aGUgcG9wdXAgY2Fubm90IGJlIGRpc3BsYXllZCBpbiBsYW5kc2NhcGUgbW9kZS4gV2hlbiBzZXRcbiAgICAgKiAgICAgdG8gbGFuZHNjYXBlLCB0aGUgcG9wdXAgY2Fubm90IGJlIGRpc3BsYXllZCBpbiBwb3J0cmFpdCBtb2RlLlxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKG9wdHMgPSB7fSkge1xuXG4gICAgICAgIHN1cGVyKClcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IHRoZW1lID0gVGhlbWUuZnJvbVN0cmluZyhvcHRzLnRoZW1lKVxuICAgICAgICB0aGlzLnRoZW1lID0gdGhlbWVcblxuICAgICAgICB0aGlzLm9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBpZDogUElYSS51dGlscy51aWQoKSxcbiAgICAgICAgICAgIHg6IDAsXG4gICAgICAgICAgICB5OiAwLFxuICAgICAgICAgICAgaGVhZGVyOiBudWxsLCAgICAgICAgICAgICAgICAgICAgICAgLy8gbnVsbCBvciBudWxsXG4gICAgICAgICAgICBjb250ZW50OiBudWxsLCAgICAgICAgICAgICAgICAgICAgICAvLyBudWxsIG9yIFN0cmluZyBvciBQSVhJLkRpc3BsYXlPYmplY3RcbiAgICAgICAgICAgIG1pbldpZHRoOiAzMjAsXG4gICAgICAgICAgICBtaW5IZWlnaHQ6IDEzMCxcbiAgICAgICAgICAgIG1heFdpZHRoOiBudWxsLFxuICAgICAgICAgICAgcGFkZGluZzogdGhlbWUucGFkZGluZyxcbiAgICAgICAgICAgIGZpbGw6IHRoZW1lLmZpbGwsXG4gICAgICAgICAgICBmaWxsQWxwaGE6IHRoZW1lLmZpbGxBbHBoYSxcbiAgICAgICAgICAgIHN0cm9rZTogdGhlbWUuc3Ryb2tlLFxuICAgICAgICAgICAgc3Ryb2tlV2lkdGg6IHRoZW1lLnN0cm9rZVdpZHRoLFxuICAgICAgICAgICAgc3Ryb2tlQWxwaGE6IHRoZW1lLnN0cm9rZUFscGhhLFxuICAgICAgICAgICAgaGVhZGVyU3R5bGU6IHRoZW1lLnRleHRTdHlsZUxhcmdlLFxuICAgICAgICAgICAgdGV4dFN0eWxlOiB0aGVtZS50ZXh0U3R5bGVTbWFsbCxcbiAgICAgICAgICAgIHJhZGl1czogdGhlbWUucmFkaXVzLFxuICAgICAgICAgICAgb25IaWRkZW46IG51bGwsXG4gICAgICAgICAgICB2aXNpYmxlOiB0cnVlLFxuICAgICAgICAgICAgb3JpZW50YXRpb246IG51bGxcbiAgICAgICAgfSwgb3B0cylcblxuICAgICAgICB0aGlzLmlkID0gdGhpcy5vcHRzLmlkXG5cbiAgICAgICAgdGhpcy5oZWFkZXJTdHlsZSA9IG5ldyBQSVhJLlRleHRTdHlsZSh0aGlzLm9wdHMuaGVhZGVyU3R5bGUpXG4gICAgICAgIHRoaXMudGV4dFN0eWxlID0gbmV3IFBJWEkuVGV4dFN0eWxlKHRoaXMub3B0cy50ZXh0U3R5bGUpXG5cbiAgICAgICAgaWYgKHRoaXMub3B0cy5tYXhXaWR0aCkge1xuICAgICAgICAgICAgdGhpcy5oZWFkZXJTdHlsZS53b3JkV3JhcCA9IHRydWVcbiAgICAgICAgICAgIHRoaXMuaGVhZGVyU3R5bGUud29yZFdyYXBXaWR0aCA9IHRoaXMub3B0cy5tYXhXaWR0aCAtICgyICogdGhpcy5vcHRzLnBhZGRpbmcpXG5cbiAgICAgICAgICAgIHRoaXMudGV4dFN0eWxlLndvcmRXcmFwID0gdHJ1ZVxuICAgICAgICAgICAgdGhpcy50ZXh0U3R5bGUud29yZFdyYXBXaWR0aCA9IHRoaXMub3B0cy5tYXhXaWR0aCAtICgyICogdGhpcy5vcHRzLnBhZGRpbmcpXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmFscGhhID0gMFxuICAgICAgICB0aGlzLnZpc2libGUgPSB0aGlzLm9wdHMudmlzaWJsZVxuXG4gICAgICAgIHRoaXMuX2hlYWRlciA9IG51bGxcbiAgICAgICAgdGhpcy5fY29udGVudCA9IG51bGxcblxuICAgICAgICAvLyBwb3NpdGlvblxuICAgICAgICB0aGlzLnggPSB0aGlzLm9wdHMueFxuICAgICAgICB0aGlzLnkgPSB0aGlzLm9wdHMueVxuXG4gICAgICAgIC8vIHBhZGRpbmdcbiAgICAgICAgdGhpcy5pbm5lclBhZGRpbmcgPSB0aGlzLm9wdHMucGFkZGluZyAqIDEuNVxuICAgICAgICBcbiAgICAgICAgLy8gaW50ZXJhY3Rpb25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmludGVyYWN0aXZlID0gdHJ1ZVxuICAgICAgICB0aGlzLm9uKCdhZGRlZCcsIGUgPT4ge1xuICAgICAgICAgICAgdGhpcy5zaG93KClcbiAgICAgICAgfSlcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyB0aGUgZnJhbWV3b3JrIGFuZCBpbnN0YW50aWF0ZXMgZXZlcnl0aGluZy5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge0Fic3RyYWN0UG9wdXB9IEEgcmVmZXJlbmNlIHRvIHRoZSBwb3B1cCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgc2V0dXAoKSB7XG5cbiAgICAgICAgLy8gcG9zaXRpb25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnN5ID0gdGhpcy5vcHRzLnBhZGRpbmdcblxuICAgICAgICAvLyBoZWFkZXJcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLmhlYWRlciAhPSBudWxsKSB7XG5cbiAgICAgICAgICAgIGxldCBoZWFkZXIgPSBudWxsXG5cbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuaGVhZGVyIGluc3RhbmNlb2YgUElYSS5UZXh0KSB7XG4gICAgICAgICAgICAgICAgaGVhZGVyID0gdGhpcy5vcHRzLmhlYWRlclxuICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgdGhpcy5vcHRzLmhlYWRlciA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICAgICAgICBoZWFkZXIgPSAgbmV3IFBJWEkuVGV4dCh0aGlzLm9wdHMuaGVhZGVyLnRvU3RyaW5nKCksIHRoaXMuaGVhZGVyU3R5bGUpXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGhlYWRlciA9ICBuZXcgUElYSS5UZXh0KHRoaXMub3B0cy5oZWFkZXIsIHRoaXMuaGVhZGVyU3R5bGUpXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGhlYWRlci54ID0gdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgICAgIGhlYWRlci55ID0gdGhpcy5zeVxuXG4gICAgICAgICAgICB0aGlzLmFkZENoaWxkKGhlYWRlcilcblxuICAgICAgICAgICAgdGhpcy5zeSArPSBoZWFkZXIuaGVpZ2h0XG5cbiAgICAgICAgICAgIHRoaXMuX2hlYWRlciA9IGhlYWRlclxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0cy5oZWFkZXIgJiYgdGhpcy5vcHRzLmNvbnRlbnQpIHtcbiAgICAgICAgICAgIHRoaXMuc3kgKz0gdGhpcy5pbm5lclBhZGRpbmdcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGNvbnRlbnRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLmNvbnRlbnQgIT0gbnVsbCkge1xuXG4gICAgICAgICAgICBsZXQgY29udGVudCA9IG51bGxcblxuICAgICAgICAgICAgaWYgKHR5cGVvZiB0aGlzLm9wdHMuY29udGVudCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICBjb250ZW50ID0gbmV3IFBJWEkuVGV4dCh0aGlzLm9wdHMuY29udGVudCwgdGhpcy50ZXh0U3R5bGUpXG4gICAgICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiB0aGlzLm9wdHMuY29udGVudCA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICAgICAgICBjb250ZW50ID0gbmV3IFBJWEkuVGV4dCh0aGlzLm9wdHMuY29udGVudC50b1N0cmluZygpLCB0aGlzLnRleHRTdHlsZSlcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgY29udGVudCA9IHRoaXMub3B0cy5jb250ZW50XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnRlbnQueCA9IHRoaXMub3B0cy5wYWRkaW5nXG4gICAgICAgICAgICBjb250ZW50LnkgPSB0aGlzLnN5XG5cbiAgICAgICAgICAgIHRoaXMuc3kgKz0gY29udGVudC5oZWlnaHRcblxuICAgICAgICAgICAgdGhpcy5hZGRDaGlsZChjb250ZW50KVxuXG4gICAgICAgICAgICB0aGlzLl9jb250ZW50ID0gY29udGVudFxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogU2hvdWxkIGJlIGNhbGxlZCB0byByZWZyZXNoIHRoZSBsYXlvdXQgb2YgdGhlIHBvcHVwLiBDYW4gYmUgdXNlZCBhZnRlciByZXNpemluZy5cbiAgICAgKiBcbiAgICAgKiBAcmV0dXJuIHtBYnN0cmFjdFBvcHVwfSBBIHJlZmVyZW5jZSB0byB0aGUgcG9wdXAgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGxheW91dCgpIHtcbiAgICAgICAgXG4gICAgICAgIC8vIHdhbnRlZCB3aWR0aCAmIHdhbnRlZCBoZWlnaHRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBjb25zdCBwYWRkaW5nID0gdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgY29uc3Qgc2l6ZSA9IHRoaXMuZ2V0SW5uZXJTaXplKClcbiAgICAgICAgY29uc3Qgd2lkdGggPSBzaXplLndpZHRoICsgKDIgKiBwYWRkaW5nKVxuICAgICAgICBjb25zdCBoZWlnaHQgPSBzaXplLmhlaWdodCArICgyICogcGFkZGluZylcblxuICAgICAgICB0aGlzLndhbnRlZFdpZHRoID0gTWF0aC5tYXgod2lkdGgsIHRoaXMub3B0cy5taW5XaWR0aClcbiAgICAgICAgdGhpcy53YW50ZWRIZWlnaHQgPSBNYXRoLm1heChoZWlnaHQsIHRoaXMub3B0cy5taW5IZWlnaHQpXG4gICAgICAgIFxuICAgICAgICBpZiAodGhpcy5vcHRzLm1heFdpZHRoKSB7XG4gICAgICAgICAgICB0aGlzLndhbnRlZFdpZHRoID0gTWF0aC5taW4odGhpcy53YW50ZWRXaWR0aCwgdGhpcy5vcHRzLm1heFdpZHRoKVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0cy5yYWRpdXMgKiAyID4gdGhpcy53YW50ZWRXaWR0aCkge1xuICAgICAgICAgICAgdGhpcy53YW50ZWRXaWR0aCA9IHRoaXMub3B0cy5yYWRpdXMgKiAyXG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5vcHRzLnJhZGl1cyAqIDIgPiB0aGlzLndhbnRlZEhlaWdodCkge1xuICAgICAgICAgICAgdGhpcy53YW50ZWRIZWlnaHQgPSB0aGlzLm9wdHMucmFkaXVzICogMlxuICAgICAgICB9XG5cbiAgICAgICAgc3dpdGNoICh0aGlzLm9wdHMub3JpZW50YXRpb24pIHtcbiAgICAgICAgICAgIGNhc2UgJ3BvcnRyYWl0JzpcbiAgICAgICAgICAgICAgICBpZiAodGhpcy53YW50ZWRXaWR0aCA+IHRoaXMud2FudGVkSGVpZ2h0KSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMud2FudGVkSGVpZ2h0ID0gdGhpcy53YW50ZWRXaWR0aFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgY2FzZSAnbGFuZHNjYXBlJzpcbiAgICAgICAgICAgICAgICBpZiAodGhpcy53YW50ZWRIZWlnaHQgPiB0aGlzLndhbnRlZFdpZHRoKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMud2FudGVkV2lkdGggPSB0aGlzLndhbnRlZEhlaWdodFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5kcmF3KClcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBEcmF3cyB0aGUgY2FudmFzLlxuICAgICAqIFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybiB7QWJzdHJhY3RQb3B1cH0gQSByZWZlcmVuY2UgdG8gdGhlIHBvcHVwIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBkcmF3KCkge1xuXG4gICAgICAgIGNvbnN0IHNxdWFyZSA9IE1hdGgucm91bmQodGhpcy53YW50ZWRXaWR0aCkgPT09IE1hdGgucm91bmQodGhpcy53YW50ZWRIZWlnaHQpXG4gICAgICAgIGNvbnN0IGRpYW1ldGVyID0gTWF0aC5yb3VuZCh0aGlzLm9wdHMucmFkaXVzICogMilcblxuICAgICAgICB0aGlzLmNsZWFyKClcbiAgICAgICAgdGhpcy5saW5lU3R5bGUodGhpcy5vcHRzLnN0cm9rZVdpZHRoLCB0aGlzLm9wdHMuc3Ryb2tlLCB0aGlzLm9wdHMuc3Ryb2tlQWxwaGEpXG4gICAgICAgIHRoaXMuYmVnaW5GaWxsKHRoaXMub3B0cy5maWxsLCB0aGlzLm9wdHMuZmlsbEFscGhhKVxuICAgICAgICBpZiAoc3F1YXJlICYmIGRpYW1ldGVyID09PSB0aGlzLndhbnRlZFdpZHRoKSB7XG4gICAgICAgICAgICB0aGlzLmRyYXdDaXJjbGUodGhpcy53YW50ZWRXaWR0aCAvIDIsIHRoaXMud2FudGVkSGVpZ2h0IC8gMiwgdGhpcy5vcHRzLnJhZGl1cylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuZHJhd1JvdW5kZWRSZWN0KDAsIDAsIHRoaXMud2FudGVkV2lkdGgsIHRoaXMud2FudGVkSGVpZ2h0LCB0aGlzLm9wdHMucmFkaXVzKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuZW5kRmlsbCgpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogQ2FsY3VsYXRlcyB0aGUgc2l6ZSBvZiB0aGUgY2hpbGRyZW4gb2YgdGhlIEFic3RyYWN0UG9wdXAuXG4gICAgICogQ2Fubm90IHVzZSBnZXRCb3VuZHMoKSBiZWNhdXNlIGl0IGlzIG5vdCB1cGRhdGVkIHdoZW4gY2hpbGRyZW5cbiAgICAgKiBhcmUgcmVtb3ZlZC5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm5zIHtvYmplY3R9IEFuIEphdmFTY3JpcHQgb2JqZWN0IHdpZHRoIHRoZSBrZXlzIHdpZHRoIGFuZCBoZWlnaHQuXG4gICAgICovXG4gICAgZ2V0SW5uZXJTaXplKCkge1xuXG4gICAgICAgIGxldCB3aWR0aCA9IDBcbiAgICAgICAgbGV0IGhlaWdodCA9IDBcblxuICAgICAgICBpZiAodGhpcy5faGVhZGVyKSB7XG4gICAgICAgICAgICB3aWR0aCA9IHRoaXMuX2hlYWRlci53aWR0aFxuICAgICAgICAgICAgaGVpZ2h0ID0gdGhpcy5faGVhZGVyLmhlaWdodFxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuX2hlYWRlciAmJiB0aGlzLl9jb250ZW50KSB7XG4gICAgICAgICAgICBoZWlnaHQgKz0gdGhpcy5pbm5lclBhZGRpbmdcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLl9jb250ZW50KSB7XG4gICAgICAgICAgICB3aWR0aCA9IE1hdGgubWF4KHdpZHRoLCB0aGlzLl9jb250ZW50LndpZHRoKVxuICAgICAgICAgICAgaGVpZ2h0ICs9IHRoaXMuX2NvbnRlbnQuaGVpZ2h0XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4ge3dpZHRoLCBoZWlnaHR9XG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFNob3dzIHRoZSBwb3B1cCAoc2V0cyBoaXMgYWxwaGEgdmFsdWVzIHRvIDEpLlxuICAgICAqIFxuICAgICAqIEBwYXJhbSB7Y2FsbGJhY2t9IFtjYl0gLSBFeGVjdXRlZCB3aGVuIHNob3cgYW5pbWF0aW9uIHdhcyBjb21wbGV0ZWQuXG4gICAgICogQHJldHVybiB7QWJzdHJhY3RQb3B1cH0gQSByZWZlcmVuY2UgdG8gdGhlIHBvcHVwIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzaG93KGNiKSB7XG5cbiAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMsIHRoaXMudGhlbWUuZmFzdCwge1xuICAgICAgICAgICAgYWxwaGE6IDEsXG4gICAgICAgICAgICBvbkNvbXBsZXRlOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKGNiKSB7XG4gICAgICAgICAgICAgICAgICAgIGNiLmNhbGwodGhpcylcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogSGlkZXMgdGhlIHBvcHVwIChzZXRzIGhpcyBhbHBoYSB2YWx1ZXMgdG8gMCkuXG4gICAgICogXG4gICAgICogQHBhcmFtIHtjYWxsYmFja30gW2NiXSAtIEV4ZWN1dGVkIHdoZW4gaGlkZSBhbmltYXRpb24gd2FzIGNvbXBsZXRlZC5cbiAgICAgKiBAcmV0dXJuIHtBYnN0cmFjdFBvcHVwfSBBIHJlZmVyZW5jZSB0byB0aGUgcG9wdXAgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGhpZGUoY2IpIHtcblxuICAgICAgICBUd2VlbkxpdGUudG8odGhpcywgdGhpcy50aGVtZS5mYXN0LCB7XG4gICAgICAgICAgICBhbHBoYTogMCxcbiAgICAgICAgICAgIG9uQ29tcGxldGU6ICgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnZpc2libGUgPSBmYWxzZVxuICAgICAgICAgICAgICAgIGlmIChjYikge1xuICAgICAgICAgICAgICAgICAgICBjYi5jYWxsKHRoaXMpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuXG4gICAgICAgIGlmICh0aGlzLm9wdHMub25IaWRkZW4pIHtcbiAgICAgICAgICAgIHRoaXMub3B0cy5vbkhpZGRlbi5jYWxsKHRoaXMsIHRoaXMpXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldHMgb3IgZ2V0cyB0aGUgaGVhZGVyLiBUaGUgZ2V0dGVyIGFsd2F5cyByZXR1cm5zIGEgUElYSS5UZXh0IG9iamVjdC4gVGhlIHNldHRlciBjYW4gcmVjZWl2ZVxuICAgICAqIGEgc3RyaW5nLCBhIG51bWJlciBvciBhIFBJWEkuVGV4dCBvYmplY3QuXG4gICAgICogXG4gICAgICogQG1lbWJlciB7c3RyaW5nfG51bWJlcnxQSVhJLlRleHR9XG4gICAgICovXG4gICAgZ2V0IGhlYWRlcigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2hlYWRlclxuICAgIH1cbiAgICBzZXQgaGVhZGVyKHZhbHVlKSB7XG4gICAgICAgIGlmICh0aGlzLl9oZWFkZXIpIHtcbiAgICAgICAgICAgIHRoaXMuX2hlYWRlci5kZXN0cm95KClcbiAgICAgICAgfVxuICAgICAgICB0aGlzLm9wdHMuaGVhZGVyID0gdmFsdWVcbiAgICAgICAgdGhpcy5zZXR1cCgpLmxheW91dCgpXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFNldHMgb3IgZ2V0cyB0aGUgY29udGVudC4gVGhlIGdldHRlciBhbHdheXMgcmV0dXJucyBhbiBQSVhJLkRpc3BsYXlPYmplY3QuIFRoZSBzZXR0ZXIgY2FuIHJlY2VpdmVcbiAgICAgKiBhIHN0cmluZywgYSBudW1iZXIgb3IgYSBQSVhJLkRpc3BsYXlPYmplY3QuXG4gICAgICogXG4gICAgICogQG1lbWJlciB7c3RyaW5nfG51bWJlcnxQSVhJLkRpc3BsYXlPYmplY3R9XG4gICAgICovXG4gICAgZ2V0IGNvbnRlbnQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jb250ZW50XG4gICAgfVxuICAgIHNldCBjb250ZW50KHZhbHVlKSB7XG4gICAgICAgIGlmICh0aGlzLl9jb250ZW50KSB7XG4gICAgICAgICAgICB0aGlzLl9jb250ZW50LmRlc3Ryb3koKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMub3B0cy5jb250ZW50ID0gdmFsdWVcbiAgICAgICAgdGhpcy5zZXR1cCgpLmxheW91dCgpXG4gICAgfVxufVxuIiwiaW1wb3J0IFRoZW1lIGZyb20gJy4vdGhlbWUuanMnXG5pbXBvcnQgQWJzdHJhY3RQb3B1cCBmcm9tICcuL2Fic3RyYWN0cG9wdXAuanMnXG5cbi8qKlxuICogQ2xhc3MgdGhhdCByZXByZXNlbnRzIGEgUGl4aUpTIFRvb2x0aXAuXG4gKiBcbiAqIEBleGFtcGxlXG4gKiAvLyBDcmVhdGUgdGhlIGFwcFxuICogY29uc3QgYXBwID0gbmV3IFBJWElBcHAoe1xuICogICAgIHZpZXc6IGNhbnZhcyxcbiAqICAgICB3aWR0aDogOTAwLFxuICogICAgIGhlaWdodDogMjUwXG4gKiB9KS5zZXR1cCgpLnJ1bigpXG4gKiBcbiAqIC8vIEFkZCBhbiBEaXNwbGF5T2JqZWN0IHRvIHRoZSBhcHBcbiAqIGNvbnN0IGNpcmNsZSA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAqIGNpcmNsZS5iZWdpbkZpbGwoMHg1MjUxYTMpXG4gKiBjaXJjbGUuZHJhd0NpcmNsZSg1MCwgNTAsIDQwKVxuICogYXBwLnNjZW5lLmFkZENoaWxkKGNpcmNsZSlcbiAqIFxuICogY29uc3QgdG9vbHRpcCA9IG5ldyBUb29sdGlwKHtcbiAqICAgICBvYmplY3Q6IGNpcmNsZSxcbiAqICAgICBjb250YWluZXI6IGFwcC5zY2VuZSxcbiAqICAgICBjb250ZW50OiAnRGFzIEdlc2V0eiBpc3QgZGVyIEZyZXVuZCBkZXMgU2Nod2FjaGVuLidcbiAqIH0pXG4gKlxuICogQGNsYXNzXG4gKiBAZXh0ZW5kcyBBYnN0cmFjdFBvcHVwXG4gKiBAc2VlIHtAbGluayBodHRwczovL3d3dy5pd20tdHVlYmluZ2VuLmRlL2l3bWJyb3dzZXIvbGliL3BpeGkvdG9vbHRpcC5odG1sfERvY1Rlc3R9XG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRvb2x0aXAgZXh0ZW5kcyBBYnN0cmFjdFBvcHVwIHtcbiAgICBcbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIGEgVG9vbHRpcC5cbiAgICAgKiBcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0byBzdHlsZSBhbmQgYmVoYXZpb3VyIG9mIHRoZSB0b29sdGlwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5taW5XaWR0aD0wXSAtIFRoZSBtaW5pbXVtIHdpZHRoIG9mIHRoZSB0b29sdGlwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5taW5IZWlnaHQ9MF0gLSBUaGUgbWluaW11bSBoZWlnaHQgb2YgdGhlIHRvb2x0aXAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnBhZGRpbmc9VGhlbWUucGFkZGluZyAvIDJdIC0gVGhlIGlubmVyIHNwYWNpbmcgb2YgdGhlIHRvb2x0aXAuXG4gICAgICogQHBhcmFtIHtQSVhJLkRpc3BsYXlPYmplY3R9IG9wdHMub2JqZWN0IC0gVGhlIG9iamVjdCwgd2hlcmUgdGhlIHRvb2x0aXAgc2hvdWxkIGJlIGRpc3BsYXllZC5cbiAgICAgKiBAcGFyYW0ge1BJWEkuRGlzcGxheU9iamVjdH0gW29wdHMuY29udGFpbmVyPW9iamVjdF0gLSBUaGUgY29udGFpbmVyIHdoZXJlIHRoZSB0b29sdGlwIHNob3VsZCBiZSBhdHRhY2hlZCB0by5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMub2Zmc2V0TGVmdD04XSAtIFRoZSBob3Jpem9udGFsIHNoaWZ0IG9mIHRoZSB0b29sdGlwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5vZmZzZXRUb3A9LThdIC0gVGhlIHZlcnRpY2FsIHNoaWZ0IG9mIHRoZSB0b29sdGlwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5kZWxheT0wXSAtIEEgZGVsYXksIGFmdGVyIHdoaWNoIHRoZSB0b29sdGlwIHNob3VsZCBiZSBvcGVuZWQuXG4gICAgICovXG4gICAgY29uc3RydWN0b3Iob3B0cyA9IHt9KSB7XG4gICAgICAgIFxuICAgICAgICBjb25zdCB0aGVtZSA9IFRoZW1lLmZyb21TdHJpbmcob3B0cy50aGVtZSlcbiAgICAgICAgXG4gICAgICAgIG9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBtaW5XaWR0aDogMCxcbiAgICAgICAgICAgIG1pbkhlaWdodDogMCxcbiAgICAgICAgICAgIHBhZGRpbmc6IHRoZW1lLnBhZGRpbmcgLyAyLFxuICAgICAgICAgICAgb2JqZWN0OiBudWxsLFxuICAgICAgICAgICAgY29udGFpbmVyOiBudWxsLFxuICAgICAgICAgICAgb2Zmc2V0TGVmdDogOCxcbiAgICAgICAgICAgIG9mZnNldFRvcDogLTgsXG4gICAgICAgICAgICBkZWxheTogMFxuICAgICAgICB9LCBvcHRzKVxuXG4gICAgICAgIG9wdHMuY29udGFpbmVyID0gb3B0cy5jb250YWluZXIgfHwgb3B0cy5vYmplY3RcblxuICAgICAgICBzdXBlcihvcHRzKVxuXG4gICAgICAgIC8vIHNldHVwXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5zZXR1cCgpXG5cbiAgICAgICAgLy8gbGF5b3V0XG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5sYXlvdXQoKVxuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGNoaWxkcmVuIGFuZCBpbnN0YW50aWF0ZXMgZXZlcnl0aGluZy5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge1Rvb2x0aXB9IEEgcmVmZXJlbmNlIHRvIHRoZSB0b29sdGlwIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzZXR1cCgpIHtcblxuICAgICAgICBzdXBlci5zZXR1cCgpXG5cbiAgICAgICAgLy8gYmluZCBldmVudHMgdGhpc1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuaW50ZXJhY3RpdmUgPSB0cnVlXG5cbiAgICAgICAgbGV0IG1vdXNlb3ZlclRvb2x0aXAgPSBmYWxzZVxuICAgICAgICBcbiAgICAgICAgdGhpcy5vbignbW91c2VvdmVyJywgZSA9PiB7XG4gICAgICAgICAgICBtb3VzZW92ZXJUb29sdGlwID0gdHJ1ZVxuICAgICAgICB9KVxuXG4gICAgICAgIHRoaXMub24oJ21vdXNlb3V0JywgZSA9PiB7XG4gICAgICAgICAgICBtb3VzZW92ZXJUb29sdGlwID0gZmFsc2VcbiAgICAgICAgICAgIGlmICghbW91c2VvdmVyT2JqZWN0KSB7XG4gICAgICAgICAgICAgICAgdGhpcy5oaWRlKCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vcHRzLmNvbnRhaW5lci5yZW1vdmVDaGlsZCh0aGlzKVxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgICAgIFxuICAgICAgICAvLyBiaW5kIGV2ZW50cyBvYmplY3RcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBjb25zdCBvYmplY3QgPSB0aGlzLm9wdHMub2JqZWN0XG4gICAgICAgIG9iamVjdC5pbnRlcmFjdGl2ZSA9IHRydWVcblxuICAgICAgICBsZXQgbW91c2VvdmVyT2JqZWN0ID0gZmFsc2VcblxuICAgICAgICBvYmplY3Qub24oJ21vdXNlb3ZlcicsIGUgPT4ge1xuXG4gICAgICAgICAgICB0aGlzLnRpbWVvdXQgPSB3aW5kb3cuc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgbW91c2VvdmVyT2JqZWN0ID0gdHJ1ZVxuICAgICAgICAgICAgICAgIHRoaXMudmlzaWJsZSA9IHRydWVcbiAgICAgICAgICAgICAgICB0aGlzLm9wdHMuY29udGFpbmVyLmFkZENoaWxkKHRoaXMpXG4gICAgICAgICAgICAgICAgdGhpcy5zZXRQb3NpdGlvbihlKVxuICAgICAgICAgICAgfSwgdGhpcy5vcHRzLmRlbGF5ICogMTAwMClcbiAgICAgICAgfSlcblxuICAgICAgICBvYmplY3Qub24oJ21vdXNlbW92ZScsIGUgPT4ge1xuICAgICAgICAgICAgaWYgKG1vdXNlb3Zlck9iamVjdCkge1xuICAgICAgICAgICAgICAgIHRoaXMuc2V0UG9zaXRpb24oZSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcblxuICAgICAgICBvYmplY3Qub24oJ21vdXNlb3V0JywgZSA9PiB7XG4gICAgICAgICAgICBtb3VzZW92ZXJPYmplY3QgPSBmYWxzZVxuICAgICAgICAgICAgd2luZG93LmNsZWFyVGltZW91dCh0aGlzLnRpbWVvdXQpXG4gICAgICAgICAgICBpZiAoIW1vdXNlb3ZlclRvb2x0aXApIHtcbiAgICAgICAgICAgICAgICB0aGlzLmhpZGUoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLm9wdHMuY29udGFpbmVyLnJlbW92ZUNoaWxkKHRoaXMpXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGVzIGFuZCBzZXRzIHRoZSBwb3NpdGlvbiBvZiB0aGUgdG9vbHRpcC5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge1Rvb2x0aXB9IEEgcmVmZXJlbmNlIHRvIHRoZSB0b29sdGlwIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzZXRQb3NpdGlvbihlKSB7XG5cbiAgICAgICAgY29uc3QgcG9zaXRpb24gPSBlLmRhdGEuZ2V0TG9jYWxQb3NpdGlvbih0aGlzLm9wdHMuY29udGFpbmVyKVxuXG4gICAgICAgIHRoaXMueCA9IHBvc2l0aW9uLnggKyB0aGlzLm9wdHMub2Zmc2V0TGVmdFxuICAgICAgICB0aGlzLnkgPSBwb3NpdGlvbi55ICsgdGhpcy5vcHRzLm9mZnNldFRvcCAtIHRoaXMuaGVpZ2h0XG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG59XG4iLCJpbXBvcnQgVGhlbWUgZnJvbSAnLi90aGVtZS5qcydcbmltcG9ydCBBYnN0cmFjdFBvcHVwIGZyb20gJy4vYWJzdHJhY3Rwb3B1cC5qcydcbmltcG9ydCBUb29sdGlwIGZyb20gJy4vdG9vbHRpcC5qcydcblxuLyoqXG4gKiBDbGFzcyB0aGF0IHJlcHJlc2VudHMgYSBQaXhpSlMgQmFkZ2UuXG4gKiBcbiAqIEBleGFtcGxlXG4gKiAvLyBDcmVhdGUgdGhlIGFwcFxuICogY29uc3QgYXBwID0gbmV3IFBJWElBcHAoe1xuICogICAgIHZpZXc6IGNhbnZhcyxcbiAqICAgICB3aWR0aDogOTAwLFxuICogICAgIGhlaWdodDogMjUwXG4gKiB9KS5zZXR1cCgpLnJ1bigpXG4gKiBcbiAqIC8vIEFkZCBhbiBEaXNwbGF5T2JqZWN0IHRvIHRoZSBhcHBcbiAqIGNvbnN0IGNpcmNsZSA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAqIGNpcmNsZS5iZWdpbkZpbGwoMHg1MjUxYTMpXG4gKiBjaXJjbGUuZHJhd0NpcmNsZSg1MCwgNTAsIDQwKVxuICogYXBwLnNjZW5lLmFkZENoaWxkKGNpcmNsZSlcbiAqIFxuICogY29uc3QgYmFkZ2UxID0gbmV3IEJhZGdlKHtcbiAqICAgICBvYmplY3Q6IGNpcmNsZSxcbiAqICAgICBjb250YWluZXI6IGFwcC5zY2VuZSxcbiAqICAgICBjb250ZW50OiAnRGFzIEdlc2V0eiBpc3QgZGVyIEZyZXVuZCBkZXMgU2Nod2FjaGVuLidcbiAqIH0pXG4gKlxuICogQGNsYXNzXG4gKiBAZXh0ZW5kcyBBYnN0cmFjdFBvcHVwXG4gKiBAc2VlIHtAbGluayBodHRwczovL3d3dy5pd20tdHVlYmluZ2VuLmRlL2l3bWJyb3dzZXIvbGliL3BpeGkvYmFkZ2UuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBCYWRnZSBleHRlbmRzIEFic3RyYWN0UG9wdXAge1xuICAgIFxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYSBCYWRnZS5cbiAgICAgKiBcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0byBzdHlsZSBhbmQgYmVoYXZpb3VyIG9mIHRoZSBiYWRnZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMubWluV2lkdGg9MF0gLSBUaGUgbWluaW11bSB3aWR0aCBvZiB0aGUgYmFkZ2UuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLm1pbkhlaWdodD0wXSAtIFRoZSBtaW5pbXVtIGhlaWdodCBvZiB0aGUgYmFkZ2UuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnBhZGRpbmc9VGhlbWUucGFkZGluZyAvIDJdIC0gVGhlIGlubmVyIHNwYWNpbmcgb2YgdGhlIGJhZGdlLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfG9iamVjdH0gW29wdHMudG9vbHRpcF0gLSBBIHN0cmluZyBmb3IgdGhlIGxhYmVsIG9mIHRoZSB0b29sdGlwIG9yIGFuIG9iamVjdCB0byBjb25maWd1cmUgdGhlIHRvb2x0aXBcbiAgICAgKiAgICAgdG8gZGlzcGxheS5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihvcHRzID0ge30pIHtcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IHRoZW1lID0gVGhlbWUuZnJvbVN0cmluZyhvcHRzLnRoZW1lKVxuICAgICAgICBcbiAgICAgICAgb3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIHtcbiAgICAgICAgICAgIG1pbldpZHRoOiAwLFxuICAgICAgICAgICAgbWluSGVpZ2h0OiAwLFxuICAgICAgICAgICAgcGFkZGluZzogdGhlbWUucGFkZGluZyAvIDIsXG4gICAgICAgICAgICB0b29sdGlwOiBudWxsXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgc3VwZXIob3B0cylcblxuICAgICAgICB0aGlzLnRvb2x0aXAgPSBudWxsXG5cbiAgICAgICAgLy8gc2V0dXBcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnNldHVwKClcblxuICAgICAgICAvLyBsYXlvdXRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmxheW91dCgpXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgY2hpbGRyZW4gYW5kIGluc3RhbnRpYXRlcyBldmVyeXRoaW5nLlxuICAgICAqXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAb3ZlcnJpZGVcbiAgICAgKiBAcmV0dXJuIHtCYWRnZX0gQSByZWZlcmVuY2UgdG8gdGhlIGJhZGdlIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzZXR1cCgpIHtcblxuICAgICAgICBzdXBlci5zZXR1cCgpXG5cbiAgICAgICAgLy8gdG9vbHRpcFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGlmICh0aGlzLm9wdHMudG9vbHRpcCkge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiB0aGlzLm9wdHMudG9vbHRpcCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnRvb2x0aXAgPSBuZXcgVG9vbHRpcCh7b2JqZWN0OiB0aGlzLCBjb250ZW50OiB0aGlzLm9wdHMudG9vbHRpcH0pXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMub3B0cy50b29sdGlwID0gT2JqZWN0LmFzc2lnbih7fSwge29iamVjdDogdGhpc30sIHRoaXMub3B0cy50b29sdGlwKVxuICAgICAgICAgICAgICAgIHRoaXMudG9vbHRpcCA9IG5ldyBUb29sdGlwKHRoaXMub3B0cy50b29sdGlwKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogU2hvdWxkIGJlIGNhbGxlZCB0byByZWZyZXNoIHRoZSBsYXlvdXQgb2YgdGhlIGJhZGdlLiBDYW4gYmUgdXNlZCBhZnRlciByZXNpemluZy5cbiAgICAgKiBcbiAgICAgKiBAb3ZlcnJpZGVcbiAgICAgKiBAcmV0dXJuIHtCYWRnZX0gQSByZWZlcmVuY2UgdG8gdGhlIGJhZGdlIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBsYXlvdXQoKSB7XG5cbiAgICAgICAgc3VwZXIubGF5b3V0KClcblxuICAgICAgICB0aGlzLmNvbnRlbnQueCA9IHRoaXMud2lkdGggLyAyIC0gdGhpcy5jb250ZW50LndpZHRoIC8gMiAtIHRoaXMub3B0cy5zdHJva2VXaWR0aCAvIDJcbiAgICAgICAgdGhpcy5jb250ZW50LnkgPSB0aGlzLmhlaWdodCAvIDIgLSB0aGlzLmNvbnRlbnQuaGVpZ2h0IC8gMiAtIHRoaXMub3B0cy5zdHJva2VXaWR0aCAvIDJcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbn1cbiIsIlxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRXZlbnRzIHtcblxuICAgIHN0YXRpYyBzdG9wKGV2ZW50KSB7XG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcbiAgICB9XG5cbiAgICBzdGF0aWMgZXh0cmFjdFBvaW50KGV2ZW50KSB7XG4gICAgICAgIHN3aXRjaCAoZXZlbnQuY29uc3RydWN0b3IubmFtZSkge1xuICAgICAgICAgICAgY2FzZSAnVG91Y2hFdmVudCc6XG4gICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBldmVudC50YXJnZXRUb3VjaGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIGxldCB0ID0gZXZlbnQudGFyZ2V0VG91Y2hlc1tpXVxuICAgICAgICAgICAgICAgICAgICByZXR1cm4geyB4OiB0LmNsaWVudFgsIHk6IHQuY2xpZW50WSB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHJldHVybiB7IHg6IGV2ZW50LmNsaWVudFgsIHk6IGV2ZW50LmNsaWVudFkgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIGlzQ2FwdHVyZWQoZXZlbnQpIHtcbiAgICAgICAgaWYgKGV2ZW50Ll9fY2FwdHVyZWRCeSlcbiAgICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIHN0YXRpYyBjYXB0dXJlZEJ5KGV2ZW50LCBvYmopIHtcbiAgICAgICAgZXZlbnQuX19jYXB0dXJlZEJ5ID0gb2JqXG4gICAgfVxuXG4gICAgc3RhdGljIGlzTW91c2VEb3duKGV2ZW50KSB7XG4gICAgICAgIC8vIEF0dGVtcHRzIHRvIGNsb25lIHRoZSB3aGljaCBhdHRyaWJ1dGUgb2YgZXZlbnRzIGZhaWxlZCBpbiBXZWJLaXQuIE1heVxuICAgICAgICAvLyBiZSB0aGlzIGlzIGEgYnVnIG9yIGEgc2VjdXJpdHkgZmVhdHVyZS4gV29ya2Fyb3VuZDogd2UgaW50cm9kdWNlXG4gICAgICAgIC8vIGEgbW91c2VEb3duU3Vic3RpdHV0ZSBhdHRyaWJ1dGUgdGhhdCBjYW4gYmUgYXNzaWduZWQgdG8gY2xvbmVkXG4gICAgICAgIC8vIGV2ZW50cyBhZnRlciBpbnN0YW50aWF0aW9uLlxuICAgICAgICBpZiAoUmVmbGVjdC5oYXMoZXZlbnQsICdtb3VzZURvd25TdWJzdGl0dXRlJykpXG4gICAgICAgICAgICByZXR1cm4gZXZlbnQubW91c2VEb3duU3Vic3RpdHV0ZVxuICAgICAgICByZXR1cm4gZXZlbnQuYnV0dG9ucyB8fCBldmVudC53aGljaFxuICAgIH1cblxuICAgIHN0YXRpYyBpc1NpbXVsYXRlZEV2ZW50KGV2ZW50KSB7XG4gICAgICAgIHJldHVybiBSZWZsZWN0LmhhcyhldmVudCwgJ21vdXNlRG93blN1YnN0aXR1dGUnKVxuICAgIH1cblxuICAgIHN0YXRpYyBpc01vdXNlUmlnaHRDbGljayhldmVudCkge1xuICAgICAgICByZXR1cm4gZXZlbnQuYnV0dG9ucyB8fCBldmVudC53aGljaFxuICAgIH1cblxuICAgIHN0YXRpYyBleHRyYWN0VG91Y2hlcyh0YXJnZXRzKSB7XG4gICAgICAgIGxldCB0b3VjaGVzID0gW11cbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0YXJnZXRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBsZXQgdCA9IHRhcmdldHNbaV1cbiAgICAgICAgICAgIHRvdWNoZXMucHVzaCh7XG4gICAgICAgICAgICAgICAgdGFyZ2V0U2VsZWN0b3I6IHRoaXMuc2VsZWN0b3IodC50YXJnZXQpLFxuICAgICAgICAgICAgICAgIGlkZW50aWZpZXI6IHQuaWRlbnRpZmllcixcbiAgICAgICAgICAgICAgICBzY3JlZW5YOiB0LnNjcmVlblgsXG4gICAgICAgICAgICAgICAgc2NyZWVuWTogdC5zY3JlZW5ZLFxuICAgICAgICAgICAgICAgIGNsaWVudFg6IHQuY2xpZW50WCxcbiAgICAgICAgICAgICAgICBjbGllbnRZOiB0LmNsaWVudFksXG4gICAgICAgICAgICAgICAgcGFnZVg6IHQucGFnZVgsXG4gICAgICAgICAgICAgICAgcGFnZVk6IHQucGFnZVlcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRvdWNoZXNcbiAgICB9XG5cbiAgICBzdGF0aWMgY3JlYXRlVG91Y2hMaXN0KHRhcmdldHMpIHtcbiAgICAgICAgbGV0IHRvdWNoZXMgPSBbXVxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRhcmdldHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGxldCB0ID0gdGFyZ2V0c1tpXVxuICAgICAgICAgICAgbGV0IHRvdWNoVGFyZ2V0ID0gZG9jdW1lbnQuZWxlbWVudEZyb21Qb2ludCh0LnBhZ2VYLCB0LnBhZ2VZKVxuICAgICAgICAgICAgbGV0IHRvdWNoID0gbmV3IFRvdWNoKHVuZGVmaW5lZCwgdG91Y2hUYXJnZXQsIHQuaWRlbnRpZmllcixcbiAgICAgICAgICAgICAgICB0LnBhZ2VYLCB0LnBhZ2VZLCB0LnNjcmVlblgsIHQuc2NyZWVuWSlcbiAgICAgICAgICAgIHRvdWNoZXMucHVzaCh0b3VjaClcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbmV3IFRvdWNoTGlzdCguLi50b3VjaGVzKVxuICAgIH1cblxuICAgIHN0YXRpYyBleHRyYWN0RXZlbnQodGltZXN0YW1wLCBldmVudCkge1xuICAgICAgICBsZXQgdGFyZ2V0U2VsZWN0b3IgPSB0aGlzLnNlbGVjdG9yKGV2ZW50LnRhcmdldClcbiAgICAgICAgbGV0IGluZm9zID0ge1xuICAgICAgICAgICAgdHlwZTogZXZlbnQudHlwZSxcbiAgICAgICAgICAgIHRpbWU6IHRpbWVzdGFtcCxcbiAgICAgICAgICAgIGNvbnN0cnVjdG9yOiBldmVudC5jb25zdHJ1Y3RvcixcbiAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICB0YXJnZXRTZWxlY3RvcjogdGFyZ2V0U2VsZWN0b3IsXG4gICAgICAgICAgICAgICAgdmlldzogZXZlbnQudmlldyxcbiAgICAgICAgICAgICAgICBtb3VzZURvd25TdWJzdGl0dXRlOiBldmVudC5idXR0b25zIHx8IGV2ZW50LndoaWNoLCAvLyB3aGljaCBjYW5ub3QgYmUgY2xvbmVkIGRpcmVjdGx5XG4gICAgICAgICAgICAgICAgYnViYmxlczogZXZlbnQuYnViYmxlcyxcbiAgICAgICAgICAgICAgICBjYW5jZWxhYmxlOiBldmVudC5jYW5jZWxhYmxlLFxuICAgICAgICAgICAgICAgIHNjcmVlblg6IGV2ZW50LnNjcmVlblgsXG4gICAgICAgICAgICAgICAgc2NyZWVuWTogZXZlbnQuc2NyZWVuWSxcbiAgICAgICAgICAgICAgICBjbGllbnRYOiBldmVudC5jbGllbnRYLFxuICAgICAgICAgICAgICAgIGNsaWVudFk6IGV2ZW50LmNsaWVudFksXG4gICAgICAgICAgICAgICAgbGF5ZXJYOiBldmVudC5sYXllclgsXG4gICAgICAgICAgICAgICAgbGF5ZXJZOiBldmVudC5sYXllclksXG4gICAgICAgICAgICAgICAgcGFnZVg6IGV2ZW50LnBhZ2VYLFxuICAgICAgICAgICAgICAgIHBhZ2VZOiBldmVudC5wYWdlWSxcbiAgICAgICAgICAgICAgICBjdHJsS2V5OiBldmVudC5jdHJsS2V5LFxuICAgICAgICAgICAgICAgIGFsdEtleTogZXZlbnQuYWx0S2V5LFxuICAgICAgICAgICAgICAgIHNoaWZ0S2V5OiBldmVudC5zaGlmdEtleSxcbiAgICAgICAgICAgICAgICBtZXRhS2V5OiBldmVudC5tZXRhS2V5XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGV2ZW50LnR5cGUuc3RhcnRzV2l0aCgndG91Y2gnKSkge1xuICAgICAgICAgICAgLy8gT24gU2FmYXJpLVdlYktpdCB0aGUgVG91Y2hFdmVudCBoYXMgbGF5ZXJYLCBsYXllclkgY29vcmRpbmF0ZXNcbiAgICAgICAgICAgIGxldCBkYXRhID0gaW5mb3MuZGF0YVxuICAgICAgICAgICAgZGF0YS50YXJnZXRUb3VjaGVzID0gdGhpcy5leHRyYWN0VG91Y2hlcyhldmVudC50YXJnZXRUb3VjaGVzKVxuICAgICAgICAgICAgZGF0YS5jaGFuZ2VkVG91Y2hlcyA9IHRoaXMuZXh0cmFjdFRvdWNoZXMoZXZlbnQuY2hhbmdlZFRvdWNoZXMpXG4gICAgICAgICAgICBkYXRhLnRvdWNoZXMgPSB0aGlzLmV4dHJhY3RUb3VjaGVzKGV2ZW50LnRvdWNoZXMpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGV2ZW50LnR5cGUuc3RhcnRzV2l0aCgncG9pbnRlcicpKSB7XG4gICAgICAgICAgICBsZXQgZGF0YSA9IGluZm9zLmRhdGFcbiAgICAgICAgICAgIGRhdGEucG9pbnRlcklkID0gZXZlbnQucG9pbnRlcklkXG4gICAgICAgICAgICBkYXRhLnBvaW50ZXJUeXBlID0gZXZlbnQucG9pbnRlclR5cGVcbiAgICAgICAgfVxuICAgICAgICBpZiAoRXZlbnRzLmRlYnVnKSB7XG4gICAgICAgICAgICBFdmVudHMuZXh0cmFjdGVkLnB1c2godGhpcy50b0xpbmUoZXZlbnQpKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBpbmZvc1xuICAgIH1cblxuICAgIHN0YXRpYyBjbG9uZUV2ZW50KHR5cGUsIGNvbnN0cnVjdG9yLCBkYXRhKSB7XG4gICAgICAgIGlmICh0eXBlLnN0YXJ0c1dpdGgoJ3RvdWNoJykpIHtcbiAgICAgICAgICAgIC8vIFdlIG5lZWQgdG8gZmluZCB0YXJnZXQgZnJvbSBsYXllclgsIGxheWVyWVxuICAgICAgICAgICAgLy92YXIgdGFyZ2V0ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihkYXRhLnRhcmdldFNlbGVjdG9yKVxuICAgICAgICAgICAgLy8gZWxlbWVudEZyb21Qb2ludChkYXRhLmxheWVyWCwgZGF0YS5sYXllclkpXG4gICAgICAgICAgICAvL2RhdGEudGFyZ2V0ID0gdGFyZ2V0XG4gICAgICAgICAgICBkYXRhLnRhcmdldFRvdWNoZXMgPSB0aGlzLmNyZWF0ZVRvdWNoTGlzdChkYXRhLnRhcmdldFRvdWNoZXMpXG4gICAgICAgICAgICBkYXRhLmNoYW5nZWRUb3VjaGVzID0gdGhpcy5jcmVhdGVUb3VjaExpc3QoZGF0YS5jaGFuZ2VkVG91Y2hlcylcbiAgICAgICAgICAgIGRhdGEudG91Y2hlcyA9IHRoaXMuY3JlYXRlVG91Y2hMaXN0KGRhdGEudG91Y2hlcylcbiAgICAgICAgfVxuICAgICAgICAvLyBXZSBuZWVkIHRvIGZpbmQgdGFyZ2V0IGZyb20gcGFnZVgsIHBhZ2VZIHdoaWNoIGFyZSBvbmx5XG4gICAgICAgIC8vIGF2YWlsYWJsZSBhZnRlciBjb25zdHJ1Y3Rpb24uIFRoZXkgc2VlbSB0byBnZXR0ZXIgaXRlbXMuXG5cbiAgICAgICAgbGV0IGNsb25lID0gUmVmbGVjdC5jb25zdHJ1Y3QoY29uc3RydWN0b3IsIFt0eXBlLCBkYXRhXSlcbiAgICAgICAgY2xvbmUubW91c2VEb3duU3Vic3RpdHV0ZSA9IGRhdGEubW91c2VEb3duU3Vic3RpdHV0ZVxuICAgICAgICByZXR1cm4gY2xvbmVcbiAgICB9XG5cbiAgICBzdGF0aWMgc2ltdWxhdGVFdmVudCh0eXBlLCBjb25zdHJ1Y3RvciwgZGF0YSkge1xuICAgICAgICBkYXRhLnRhcmdldCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoZGF0YS50YXJnZXRTZWxlY3RvcilcbiAgICAgICAgbGV0IGNsb25lID0gdGhpcy5jbG9uZUV2ZW50KHR5cGUsIGNvbnN0cnVjdG9yLCBkYXRhKVxuICAgICAgICBpZiAoZGF0YS50YXJnZXQgIT0gbnVsbCkge1xuICAgICAgICAgICAgZGF0YS50YXJnZXQuZGlzcGF0Y2hFdmVudChjbG9uZSlcbiAgICAgICAgfVxuICAgICAgICBpZiAoRXZlbnRzLmRlYnVnKSB7XG4gICAgICAgICAgICBFdmVudHMuc2ltdWxhdGVkLnB1c2godGhpcy50b0xpbmUoY2xvbmUpKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIHRvTGluZShldmVudCkge1xuICAgICAgICByZXR1cm4gYCR7ZXZlbnQudHlwZX0gIyR7ZXZlbnQudGFyZ2V0LmlkfSAke2V2ZW50LmNsaWVudFh9ICR7ZXZlbnQuY2xpZW50WX1gXG4gICAgICAgIGxldCByZXN1bHQgPSBldmVudC50eXBlXG4gICAgICAgIGxldCBzZWxlY3RvciA9IHRoaXMuc2VsZWN0b3IoZXZlbnQudGFyZ2V0KVxuICAgICAgICByZXN1bHQgKz0gJyBzZWxlY3RvcjogJyArIHNlbGVjdG9yXG4gICAgICAgIGlmIChldmVudC50YXJnZXQgIT0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihzZWxlY3RvcikpXG4gICAgICAgICAgICBjb25zb2xlLmxvZygnQ2Fubm90IHJlc29sdmUnLCBzZWxlY3RvcilcbiAgICAgICAgbGV0IGtleXMgPSBbJ2xheWVyWCcsICdsYXllclknLCAncGFnZVgnLCAncGFnZVknLCAnY2xpZW50WCcsICdjbGllbnRZJ11cbiAgICAgICAgZm9yIChsZXQga2V5IG9mIGtleXMpIHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0ICs9ICcgJyArIGtleSArICc6JyArIGV2ZW50W2tleV1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ0ludmFsaWQga2V5OiAnICsga2V5KVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG5cbiAgICBzdGF0aWMgY29tcGFyZUV4dHJhY3RlZFdpdGhTaW11bGF0ZWQoKSB7XG4gICAgICAgIHZhciBkaWZmcyA9IDBcbiAgICAgICAgaWYgKHRoaXMuZXh0cmFjdGVkLmxlbmd0aCAhPSB0aGlzLnNpbXVsYXRlZC5sZW5ndGgpIHtcbiAgICAgICAgICAgIGFsZXJ0KCdVbmVxdWFsIGxlbmd0aCBvZiBleHRyYWN0ZWQgWycgKyB0aGlzLmV4dHJhY3RlZC5sZW5ndGggK1xuICAgICAgICAgICAgICAgICddIGFuZCBzaW11bGF0ZWQgZXZlbnRzIFsnICsgdGhpcy5zaW11bGF0ZWQubGVuZ3RoICsgJ10uJylcbiAgICAgICAgICAgIGRpZmZzICs9IDFcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5leHRyYWN0ZWQubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB2YXIgZXh0cmFjdGVkID0gdGhpcy5leHRyYWN0ZWRbaV1cbiAgICAgICAgICAgICAgICB2YXIgc2ltdWxhdGVkID0gdGhpcy5zaW11bGF0ZWRbaV1cbiAgICAgICAgICAgICAgICBpZiAoZXh0cmFjdGVkICE9IHNpbXVsYXRlZCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnRXZlbnRzIGRpZmZlcjonICsgZXh0cmFjdGVkICsgJ3wnICsgc2ltdWxhdGVkKVxuICAgICAgICAgICAgICAgICAgICBkaWZmcyArPSAxXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIHNlbGVjdG9yKGNvbnRleHQpIHtcbiAgICAgICAgcmV0dXJuIE9wdGltYWxTZWxlY3Quc2VsZWN0KGNvbnRleHQpXG4gICAgfVxuXG4gICAgc3RhdGljIHJlc2V0KCkge1xuICAgICAgICB0aGlzLmV4dHJhY3RlZCA9IFtdXG4gICAgICAgIHRoaXMuc2ltdWxhdGVkID0gW11cbiAgICB9XG5cbiAgICBzdGF0aWMgcmVzZXRTaW11bGF0ZWQoKSB7XG4gICAgICAgIHRoaXMuc2ltdWxhdGVkID0gW11cbiAgICB9XG5cbiAgICBzdGF0aWMgc2hvd0V4dHJhY3RlZEV2ZW50cyhldmVudCkge1xuICAgICAgICBpZiAoIWV2ZW50LnNoaWZ0S2V5KSB7XG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5wb3B1cCA9PSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgZWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpXG4gICAgICAgICAgICBFbGVtZW50cy5zZXRTdHlsZShlbGVtZW50LCB7XG4gICAgICAgICAgICAgICAgcG9zaXRpb246ICdhYnNvbHV0ZScsXG4gICAgICAgICAgICAgICAgd2lkdGg6ICc0ODBweCcsXG4gICAgICAgICAgICAgICAgaGVpZ2h0OiAnNjQwcHgnLFxuICAgICAgICAgICAgICAgIG92ZXJmbG93OiAnYXV0bycsXG4gICAgICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yOiAnbGlnaHRncmF5J1xuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoZWxlbWVudClcbiAgICAgICAgICAgIHRoaXMucG9wdXAgPSBlbGVtZW50XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5wb3B1cC5pbm5lckhUTUwgPSAnJ1xuICAgICAgICBmb3IgKGxldCBsaW5lIG9mIHRoaXMuZXh0cmFjdGVkKSB7XG4gICAgICAgICAgICBsZXQgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICAgICAgICAgIGRpdi5pbm5lckhUTUwgPSBsaW5lXG4gICAgICAgICAgICB0aGlzLnBvcHVwLmFwcGVuZENoaWxkKGRpdilcbiAgICAgICAgfVxuICAgICAgICBsZXQgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JylcbiAgICAgICAgZGl2LmlubmVySFRNTCA9ICctLS0tLS0tLS0tLS0gU2ltdWxhdGVkIC0tLS0tLS0tLS0tJ1xuICAgICAgICB0aGlzLnBvcHVwLmFwcGVuZENoaWxkKGRpdilcbiAgICAgICAgZm9yIChsZXQgbGluZSBvZiB0aGlzLnNpbXVsYXRlZCkge1xuICAgICAgICAgICAgbGV0IGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpXG4gICAgICAgICAgICBkaXYuaW5uZXJIVE1MID0gbGluZVxuICAgICAgICAgICAgdGhpcy5wb3B1cC5hcHBlbmRDaGlsZChkaXYpXG4gICAgICAgIH1cbiAgICAgICAgRWxlbWVudHMuc2V0U3R5bGUodGhpcy5wb3B1cCxcbiAgICAgICAgICAgIHsgbGVmdDogZXZlbnQuY2xpZW50WCArICdweCcsIHRvcDogZXZlbnQuY2xpZW50WSArICdweCcgfSlcbiAgICB9XG59XG5cbkV2ZW50cy5wb3B1cCA9IG51bGxcbkV2ZW50cy5kZWJ1ZyA9IHRydWVcbkV2ZW50cy5leHRyYWN0ZWQgPSBbXVxuRXZlbnRzLnNpbXVsYXRlZCA9IFtdXG5FdmVudHMuc2ltdWxhdGlvblJ1bm5pbmcgPSBmYWxzZVxuXG5leHBvcnQgY2xhc3MgRXZlbnRSZWNvcmRlciB7XG5cbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5yZWNvcmRpbmcgPSBbXVxuICAgICAgICB0aGlzLnJlY29yZGVkID0gW11cbiAgICAgICAgdGhpcy5zdGVwID0gMFxuICAgIH1cblxuICAgIHJlY29yZChldmVudCkge1xuICAgICAgICBsZXQgbGVuZ3RoID0gdGhpcy5yZWNvcmRpbmcubGVuZ3RoXG4gICAgICAgIGlmIChsZW5ndGggPT0gMCkge1xuICAgICAgICAgICAgdGhpcy5zdGFydFRpbWUgPSBldmVudC50aW1lU3RhbXBcbiAgICAgICAgICAgIEV2ZW50cy5yZXNldCgpXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBsZXQgbGFzdCA9IHRoaXMucmVjb3JkaW5nW2xlbmd0aCAtIDFdXG4gICAgICAgICAgICBpZiAoZXZlbnQudGltZVN0YW1wIDwgbGFzdC50aW1lKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3dhcm5pbmc6IHdyb25nIHRlbXBvcmFsIG9yZGVyJylcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBsZXQgdCA9IGV2ZW50LnRpbWVTdGFtcCAtIHRoaXMuc3RhcnRUaW1lXG4gICAgICAgIHRoaXMucmVjb3JkaW5nLnB1c2goRXZlbnRzLmV4dHJhY3RFdmVudCh0LCBldmVudCkpXG4gICAgfVxuXG4gICAgc3RvcFJlY29yZGluZygpIHtcbiAgICAgICAgdGhpcy5yZWNvcmRlZCA9IHRoaXMucmVjb3JkaW5nXG4gICAgICAgIHRoaXMucmVjb3JkaW5nID0gW11cbiAgICAgICAgY29uc29sZS5sb2coJ1JlY29yZGVkICcgKyB0aGlzLnJlY29yZGVkLmxlbmd0aCArICcgZXZlbnRzJylcbiAgICB9XG5cbiAgICBzdGFydFJlcGxheSh3aGlsZUNvbmRpdGlvbiA9IG51bGwsIG9uQ29tcGxldGUgPSBudWxsKSB7XG4gICAgICAgIHRoaXMuc3RlcCA9IDBcbiAgICAgICAgRXZlbnRzLnJlc2V0U2ltdWxhdGVkKClcbiAgICAgICAgY29uc29sZS5sb2coJ1N0YXJ0IHJlcGxheScpXG4gICAgICAgIEV2ZW50cy5zaW11bGF0aW9uUnVubmluZyA9IHRydWVcbiAgICAgICAgdGhpcy5yZXBsYXkod2hpbGVDb25kaXRpb24sIG9uQ29tcGxldGUpXG4gICAgfVxuXG4gICAgcmVwbGF5KHdoaWxlQ29uZGl0aW9uID0gbnVsbCwgb25Db21wbGV0ZSA9IG51bGwpIHtcbiAgICAgICAgaWYgKHRoaXMuc3RlcCA8IHRoaXMucmVjb3JkZWQubGVuZ3RoKSB7XG4gICAgICAgICAgICBsZXQgeyB0eXBlLCB0aW1lLCBjb25zdHJ1Y3RvciwgZGF0YSB9ID0gdGhpcy5yZWNvcmRlZFt0aGlzLnN0ZXBdXG4gICAgICAgICAgICBFdmVudHMuc2ltdWxhdGVFdmVudCh0eXBlLCBjb25zdHJ1Y3RvciwgZGF0YSlcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgdGhpcy5zdGVwICs9IDFcbiAgICAgICAgICAgIGxldCBkdCA9IDBcbiAgICAgICAgICAgIGlmICh0aGlzLnN0ZXAgPCB0aGlzLnJlY29yZGVkLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHZhciBuZXh0ID0gdGhpcy5yZWNvcmRlZFt0aGlzLnN0ZXBdXG4gICAgICAgICAgICAgICAgZHQgPSBuZXh0LnRpbWUgLSB0aW1lXG4gICAgICAgICAgICAgICAgaWYgKGR0IDwgMCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnd2FybmluZzogd3JvbmcgdGVtcG9yYWwgb3JkZXInKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh3aGlsZUNvbmRpdGlvbiA9PSBudWxsIHx8IHdoaWxlQ29uZGl0aW9uKCkpIHtcbiAgICAgICAgICAgICAgICBsZXQgZGVsdGEgPSBNYXRoLnJvdW5kKGR0KVxuICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gdGhpcy5yZXBsYXkod2hpbGVDb25kaXRpb24sIG9uQ29tcGxldGUpLCBkZWx0YSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdQbGF5ZWQgJyArIHRoaXMuc3RlcCArICcgZXZlbnRzJyArIG9uQ29tcGxldGUpXG4gICAgICAgICAgICBFdmVudHMuc2ltdWxhdGlvblJ1bm5pbmcgPSBmYWxzZVxuICAgICAgICAgICAgaWYgKG9uQ29tcGxldGUgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIG9uQ29tcGxldGUoKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy9FdmVudHMuY29tcGFyZUV4dHJhY3RlZFdpdGhTaW11bGF0ZWQoKVxuICAgICAgICB9XG4gICAgfVxufVxuXG4iLCJpbXBvcnQgVGhlbWUgZnJvbSAnLi90aGVtZS5qcydcbmltcG9ydCBUb29sdGlwIGZyb20gJy4vdG9vbHRpcC5qcydcbmltcG9ydCBCYWRnZSBmcm9tICcuL2JhZGdlLmpzJ1xuaW1wb3J0IEV2ZW50cyBmcm9tICcuLi9ldmVudHMuanMnXG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHRoZSBidXR0b24gYWN0aW9uLlxuICpcbiAqIEBjYWxsYmFjayBhY3Rpb25DYWxsYmFja1xuICogQHBhcmFtIHtvYmplY3R9IGV2ZW50IC0gVGhlIGV2ZW50IG9iamVjdC5cbiAqIEBwYXJhbSB7QnV0dG9ufSBidXR0b24gLSBBIHJlZmVyZW5jZSB0byB0aGUgYnV0dG9uIChhbHNvIHRoaXMgcmVmZXJzIHRvIHRoZSBidXR0b24pLlxuICovXG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHRoZSBidXR0b24gYmVmb3JlQWN0aW9uLlxuICpcbiAqIEBjYWxsYmFjayBiZWZvcmVBY3Rpb25DYWxsYmFja1xuICogQHBhcmFtIHtvYmplY3R9IGV2ZW50IC0gVGhlIGV2ZW50IG9iamVjdC5cbiAqIEBwYXJhbSB7QnV0dG9ufSBidXR0b24gLSBBIHJlZmVyZW5jZSB0byB0aGUgYnV0dG9uIChhbHNvIHRoaXMgcmVmZXJzIHRvIHRoZSBidXR0b24pLlxuICovXG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHRoZSBidXR0b24gYWZ0ZXJBY3Rpb24uXG4gKlxuICogQGNhbGxiYWNrIGFmdGVyQWN0aW9uQ2FsbGJhY2tcbiAqIEBwYXJhbSB7b2JqZWN0fSBldmVudCAtIFRoZSBldmVudCBvYmplY3QuXG4gKiBAcGFyYW0ge0J1dHRvbn0gYnV0dG9uIC0gQSByZWZlcmVuY2UgdG8gdGhlIGJ1dHRvbiAoYWxzbyB0aGlzIHJlZmVycyB0byB0aGUgYnV0dG9uKS5cbiAqL1xuXG4vKipcbiAqIENsYXNzIHRoYXQgcmVwcmVzZW50cyBhIFBpeGlKUyBCdXR0b24uXG4gKlxuICogQGV4YW1wbGVcbiAqIC8vIENyZWF0ZSB0aGUgYnV0dG9uXG4gKiBjb25zdCBidXR0b24gPSBuZXcgQnV0dG9uKHtcbiAqICAgICBsYWJlbDogJ015IEJ1dHRvbicsXG4gKiAgICAgYWN0aW9uOiAoKSA9PiBjb25zb2xlLmxvZygnQnV0dG9uIHdhcyBjbGlja2VkJylcbiAqIH0pXG4gKlxuICogLy8gQWRkIHRoZSBidXR0b24gdG8gYSBEaXNwbGF5T2JqZWN0XG4gKiBhcHAuc2NlbmUuYWRkQ2hpbGQoYnV0dG9uKVxuICpcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgUElYSS5Db250YWluZXJcbiAqIEBzZWUge0BsaW5rIGh0dHA6Ly9waXhpanMuZG93bmxvYWQvZGV2L2RvY3MvUElYSS5Db250YWluZXIuaHRtbHxQSVhJLkNvbnRhaW5lcn1cbiAqIEBzZWUge0BsaW5rIGh0dHBzOi8vd3d3Lml3bS10dWViaW5nZW4uZGUvaXdtYnJvd3Nlci9saWIvcGl4aS9idXR0b24uaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBCdXR0b24gZXh0ZW5kcyBQSVhJLkNvbnRhaW5lciB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIGEgQnV0dG9uLlxuICAgICAqXG4gICAgICogQGNvbnN0cnVjdG9yXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzXSAtIEFuIG9wdGlvbnMgb2JqZWN0IHRvIHNwZWNpZnkgdG8gc3R5bGUgYW5kIGJlaGF2aW91ciBvZiB0aGUgYnV0dG9uLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5pZD1hdXRvIGdlbmVyYXRlZF0gLSBUaGUgaWQgb2YgdGhlIGJ1dHRvbi5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubGFiZWxdIC0gVGhlIGxhYmVsIG9mIHRoZSBidXR0b24uXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLng9MF0gLSBUaGUgeCBwb3NpdGlvbiBvZiB0aGUgYnV0dG9uLiBDYW4gYmUgYWxzbyBzZXQgYWZ0ZXIgY3JlYXRpb24gd2l0aCBidXR0b24ueCA9IDAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnk9MF0gLSBUaGUgeSBwb3NpdGlvbiBvZiB0aGUgYnV0dG9uLiBDYW4gYmUgYWxzbyBzZXQgYWZ0ZXIgY3JlYXRpb24gd2l0aCBidXR0b24ueSA9IDAuXG4gICAgICogQHBhcmFtIHtzdHJpbmd8VGhlbWV9IFtvcHRzLnRoZW1lPWRhcmtdIC0gVGhlIHRoZW1lIHRvIHVzZSBmb3IgdGhpcyBidXR0b24uIFBvc3NpYmxlIHZhbHVlcyBhcmUgZGFyaywgbGlnaHQsIHJlZFxuICAgICAqICAgICBvciBhIFRoZW1lIG9iamVjdC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMubWluV2lkdGg9NDRdIC0gVGhlIG1pbmltdW0gd2lkdGggb2YgdGhlIGJ1dHRvbi5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMubWluSGVpZ2h0PTQ0XSAtIFRoZSBtaW5pbXVtIGhlaWdodCBvZiB0aGUgYnV0dG9uLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5wYWRkaW5nPVRoZW1lLnBhZGRpbmddIC0gVGhlIGlubmVyIHNwYWNpbmcgKGRpc3RhbmNlIGZyb20gaWNvbiBhbmQvb3IgbGFiZWwpIHRvIHRoZSBib3JkZXIuXG4gICAgICogQHBhcmFtIHtzdHJpbmd8UElYSS5EaXNwbGF5T2JqZWN0fSBbb3B0cy5pY29uXSAtIFRoZSBpY29uIG9mIHRoZSBidXR0b24uIENhbiBiZSBhIHByZWRlZmluZWQgb25lLCBhbiBVUkwgb3IgYW4gUElYSS5EaXNwbGF5T2JqZWN0LlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfFBJWEkuRGlzcGxheU9iamVjdH0gW29wdHMuaWNvbkFjdGl2ZT1pY29uXSAtIFRoZSBpY29uIG9mIHRoZSBidXR0b24gd2hlbiBhY3RpdmF0ZWQuIENhbiBiZSBhIHByZWRlZmluZWQgb25lLCBhbiBVUkwgb3IgYW4gUElYSS5EaXNwbGF5T2JqZWN0LlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5pY29uUG9zaXRpb249bGVmdF0gLSBUaGUgcG9zaXRpb24gb2YgdGhlIGljb24gaW4gcmVsYXRpb24gdG8gdGhlIGxhYmVsLiBDYW4gYmUgbGVmdCBvciByaWdodC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuaWNvbkNvbG9yPVRoZW1lLmljb25Db2xvcl0gLSBUaGUgY29sb3Igb2YgdGhlIGljb24gKHNldCBieSB0aGUgdGludCBwcm9wZXJ0eSkgYXMgYSBoZXggdmFsdWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmljb25Db2xvckFjdGl2ZT1UaGVtZS5pY29uQ29sb3JBY3RpdmVdIC0gVGhlIGNvbG9yIG9mIHRoZSBpY29uIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5maWxsPVRoZW1lLmZpbGxdIC0gVGhlIGNvbG9yIG9mIHRoZSBidXR0b24gYmFja2dyb3VuZCBhcyBhIGhleCB2YWx1ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZmlsbEFscGhhPVRoZW1lLmZpbGxBbHBoYV0gLSBUaGUgYWxwaGEgdmFsdWUgb2YgdGhlIGJhY2tncm91bmQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmZpbGxBY3RpdmU9VGhlbWUuZmlsbEFjdGl2ZV0gLSBUaGUgY29sb3Igb2YgdGhlIGJ1dHRvbiBiYWNrZ3JvdW5kIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5maWxsQWN0aXZlQWxwaGE9VGhlbWUuZmlsbEFjdGl2ZUFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYmFja2dyb3VuZCB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlPVRoZW1lLnN0cm9rZV0gLSBUaGUgY29sb3Igb2YgdGhlIGJvcmRlciBhcyBhIGhleCB2YWx1ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlV2lkdGg9VGhlbWUuc3Ryb2tlV2lkdGhdIC0gVGhlIHdpZHRoIG9mIHRoZSBib3JkZXIgaW4gcGl4ZWwuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnN0cm9rZUFscGhhPVRoZW1lLnN0cm9rZUFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VBY3RpdmU9VGhlbWUuc3Ryb2tlQWN0aXZlXSAtIFRoZSBjb2xvciBvZiB0aGUgYm9yZGVyIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VBY3RpdmVXaWR0aD1UaGVtZS5zdHJva2VBY3RpdmVXaWR0aF0gLSBUaGUgd2lkdGggb2YgdGhlIGJvcmRlciBpbiBwaXhlbCB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlQWN0aXZlQWxwaGE9VGhlbWUuc3Ryb2tlQWN0aXZlQWxwaGFdIC0gVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBib3JkZXIgd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzLnRleHRTdHlsZT1UaGVtZS50ZXh0U3R5bGVdIC0gQSB0ZXh0c3R5bGUgb2JqZWN0IGZvciB0aGUgc3R5bGluZyBvZiB0aGUgbGFiZWwuIFNlZSBQSVhJLlRleHRTdHlsZVxuICAgICAqICAgICBmb3IgcG9zc2libGUgb3B0aW9ucy5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMudGV4dFN0eWxlQWN0aXZlPVRoZW1lLnRleHRTdHlsZUFjdGl2ZV0gLSBBIHRleHRzdHlsZSBvYmplY3QgZm9yIHRoZSBzdHlsaW5nIG9mIHRoZSBsYWJlbCB3aGVuIHRoZVxuICAgICAqICAgICBidXR0b24gaXMgYWN0aXZhdGVkLiBTZWUgUElYSS5UZXh0U3R5bGUgZm9yIHBvc3NpYmxlIG9wdGlvbnMuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLnN0eWxlPWRlZmF1bHRdIC0gQSBzaG9ydGN1dCBmb3Igc3R5bGluZyBvcHRpb25zLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIGRlZmF1bHQsIGxpbmsuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnJhZGl1cz1UaGVtZS5yYWRpdXNdIC0gVGhlIHJhZGl1cyBvZiB0aGUgZm91ciBjb3JuZXJzIG9mIHRoZSBidXR0b24gKHdoaWNoIGlzIGEgcm91bmRlZCByZWN0YW5nbGUpLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuZGlzYWJsZWQ9ZmFsc2VdIC0gSXMgdGhlIGJ1dHRvbiBkaXNhYmxlZD8gV2hlbiBkaXNhYmxlZCwgdGhlIGJ1dHRvbiBoYXMgYSBsb3dlciBhbHBoYSB2YWx1ZVxuICAgICAqICAgICBhbmQgY2Fubm90IGJlIGNsaWNrZWQgKGludGVyYWN0aXZlIGlzIHNldCB0byBmYWxzZSkuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5hY3RpdmU9ZmFsc2VdIC0gSXMgdGhlIGJ1dHRvbiBpbml0aWFsbHkgYWN0aXZlP1xuICAgICAqIEBwYXJhbSB7YWN0aW9uQ2FsbGJhY2t9IFtvcHRzLmFjdGlvbl0gLSBFeGVjdXRlZCB3aGVuIHRoZSBidXR0b24gd2FzIHRyaWdnZXJlZCAoYnkgcG9pbnRlcnVwKS5cbiAgICAgKiBAcGFyYW0ge2JlZm9yZUFjdGlvbkNhbGxiYWNrfSBbb3B0cy5iZWZvcmVBY3Rpb25dIC0gRXhlY3V0ZWQgYmVmb3JlIHRoZSBtYWluIGFjdGlvbiBpcyB0cmlnZ2VyZWQuXG4gICAgICogQHBhcmFtIHthZnRlckFjdGlvbkNhbGxiYWNrfSBbb3B0cy5hZnRlckFjdGlvbl0gLSBFeGVjdXRlZCBhZnRlciB0aGUgbWFpbiBhY3Rpb24gd2FzIHRyaWdnZXJlZC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMudHlwZT1kZWZhdWx0XSAtIFRoZSB0eXBlIG9mIHRoZSBidXR0b24uIENhbiBiZSBkZWZhdWx0IG9yIGNoZWNrYm94LiBXaGVuIHRoZSB0eXBlIGlzXG4gICAgICogICAgIGNoZWNrYm94LCB0aGUgYWN0aXZlIHN0YXRlIGlzIHRvZ2dsZWQgYXV0b21hdGljYWxseS5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMuYWxpZ249Y2VudGVyXSAtIFRoZSBob3Jpem9udGFsIHBvc2l0aW9uIG9mIHRoZSBsYWJlbCBhbmQgdGhlIGljb24uIFBvc3NpYmxlIHZhbHVlcyBhcmVcbiAgICAgKiAgICAgbGVmdCwgY2VudGVyIGFuZCByaWdodC4gT25seSBhZmZlY3RzIHRoZSBzdHlsZSB3aGVuIHRoZSBtaW5XaWR0aCBpcyBiaWdnZXIgdGhhbiB0aGUgd2lkdGggb2YgdGhlIGljb24gYW5kIGxhYmVsLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy52ZXJ0aWNhbEFsaWduPW1pZGRsZV0gLSBUaGUgdmVydGljYWwgcG9zaXRpb24gb2YgdGhlIGxhYmVsIGFuZCB0aGUgaWNvbi4gUG9zc2libGUgdmFsdWVzIGFyZVxuICAgICAqICAgICB0b3AsIG1pZGRsZSBhbmQgYnV0dG9uLiBPbmx5IGFmZmVjdHMgdGhlIHN0eWxlIHdoZW4gdGhlIG1pbkhlaWdodCBpcyBiaWdnZXIgdGhhbiB0aGUgaGVpZ2h0IG9mIHRoZSBpY29uIGFuZCBsYWJlbC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ3xvYmplY3R9IFtvcHRzLnRvb2x0aXBdIC0gQSBzdHJpbmcgZm9yIHRoZSBsYWJlbCBvZiB0aGUgdG9vbHRpcCBvciBhbiBvYmplY3QgdG8gY29uZmlndXJlIHRoZSB0b29sdGlwXG4gICAgICogICAgIHRvIGRpc3BsYXkuXG4gICAgICogQHBhcmFtIHtzdHJpbmd8b2JqZWN0fSBbb3B0cy5iYWRnZV0gLSBBIHN0cmluZyBmb3IgdGhlIGxhYmVsIG9mIHRoZSBiYWRnZSBvciBhbiBvYmplY3QgdG8gY29uZmlndXJlIHRoZSBiYWRnZSB0byBkaXNwbGF5LlxuICAgICAqICAgICBJZiB0aGUgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCwgYWxsIGJhZGdlIG9wdGlvbnMgY2FuIGJlIHNldCBwbHVzIHRoZSBmb2xsb3dpbmc6XG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLmJhZGdlLmFsaWduPXJpZ2h0XSAtIFRoZSBob3Jpem9udGFsIGFsaWdubWVudCBvZiB0aGUgYmFkZ2UuIFBvc3NpYmxlIHZhbHVlczogbGVmdCwgY2VudGVyLCByaWdodFxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5iYWRnZS52ZXJ0aWNhbEFsaWduPXRvcF0gLSBUaGUgdmVydGljYWwgYWxpZ25tZW50IG9mIHRoZSBiYWRnZS4gUG9zc2libGUgdmFsdWVzOiB0b3AsIG1pZGRsZSwgYm90dG9tXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmJhZGdlLm9mZnNldExlZnQ9MF0gLSBUaGUgaG9yaXpvbnRhbCBzaGlmdCBvZiB0aGUgYmFkZ2UuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmJhZGdlLm9mZnNldFRvcD0wXSAtIFRoZSB2ZXJ0aWNhbCBzaGlmdCBvZiB0aGUgYmFkZ2UuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy52aXNpYmxlPXRydWVdIC0gSXMgdGhlIGJ1dHRvbiBpbml0aWFsbHkgdmlzaWJsZSAocHJvcGVydHkgdmlzaWJsZSk/XG4gICAgICovXG4gICAgY29uc3RydWN0b3Iob3B0cyA9IHt9KSB7XG5cbiAgICAgICAgc3VwZXIoKVxuXG4gICAgICAgIGNvbnN0IHRoZW1lID0gVGhlbWUuZnJvbVN0cmluZyhvcHRzLnRoZW1lKVxuICAgICAgICB0aGlzLnRoZW1lID0gdGhlbWVcblxuICAgICAgICB0aGlzLm9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBpZDogUElYSS51dGlscy51aWQoKSxcbiAgICAgICAgICAgIGxhYmVsOiBudWxsLFxuICAgICAgICAgICAgeDogMCxcbiAgICAgICAgICAgIHk6IDAsXG4gICAgICAgICAgICBtaW5XaWR0aDogNDQsXG4gICAgICAgICAgICBtaW5IZWlnaHQ6IDQ0LFxuICAgICAgICAgICAgcGFkZGluZzogdGhlbWUucGFkZGluZyxcbiAgICAgICAgICAgIGljb246IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGljb25BY3RpdmU6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGljb25Qb3NpdGlvbjogJ2xlZnQnLFxuICAgICAgICAgICAgaWNvbkNvbG9yOiB0aGVtZS5pY29uQ29sb3IsXG4gICAgICAgICAgICBpY29uQ29sb3JBY3RpdmU6IHRoZW1lLmljb25Db2xvckFjdGl2ZSxcbiAgICAgICAgICAgIGZpbGw6IHRoZW1lLmZpbGwsXG4gICAgICAgICAgICBmaWxsQWxwaGE6IHRoZW1lLmZpbGxBbHBoYSxcbiAgICAgICAgICAgIGZpbGxBY3RpdmU6IHRoZW1lLmZpbGxBY3RpdmUsXG4gICAgICAgICAgICBmaWxsQWN0aXZlQWxwaGE6IHRoZW1lLmZpbGxBY3RpdmVBbHBoYSxcbiAgICAgICAgICAgIHN0cm9rZTogdGhlbWUuc3Ryb2tlLFxuICAgICAgICAgICAgc3Ryb2tlV2lkdGg6IHRoZW1lLnN0cm9rZVdpZHRoLFxuICAgICAgICAgICAgc3Ryb2tlQWxwaGE6IHRoZW1lLnN0cm9rZUFscGhhLFxuICAgICAgICAgICAgc3Ryb2tlQWN0aXZlOiB0aGVtZS5zdHJva2VBY3RpdmUsXG4gICAgICAgICAgICBzdHJva2VBY3RpdmVXaWR0aDogdGhlbWUuc3Ryb2tlQWN0aXZlV2lkdGgsXG4gICAgICAgICAgICBzdHJva2VBY3RpdmVBbHBoYTogdGhlbWUuc3Ryb2tlQWN0aXZlQWxwaGEsXG4gICAgICAgICAgICB0ZXh0U3R5bGU6IHRoZW1lLnRleHRTdHlsZSxcbiAgICAgICAgICAgIHRleHRTdHlsZUFjdGl2ZTogdGhlbWUudGV4dFN0eWxlQWN0aXZlLFxuICAgICAgICAgICAgc3R5bGU6ICdkZWZhdWx0JyxcbiAgICAgICAgICAgIHJhZGl1czogdGhlbWUucmFkaXVzLFxuICAgICAgICAgICAgZGlzYWJsZWQ6IGZhbHNlLFxuICAgICAgICAgICAgYWN0aXZlOiBmYWxzZSxcbiAgICAgICAgICAgIGFjdGlvbjogbnVsbCxcbiAgICAgICAgICAgIGJlZm9yZUFjdGlvbjogbnVsbCxcbiAgICAgICAgICAgIGFmdGVyQWN0aW9uOiBudWxsLFxuICAgICAgICAgICAgdHlwZTogJ2RlZmF1bHQnLFxuICAgICAgICAgICAgYWxpZ246ICdjZW50ZXInLFxuICAgICAgICAgICAgdmVydGljYWxBbGlnbjogJ21pZGRsZScsXG4gICAgICAgICAgICB0b29sdGlwOiBudWxsLFxuICAgICAgICAgICAgYmFkZ2U6IG51bGwsXG4gICAgICAgICAgICB2aXNpYmxlOiB0cnVlXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgdGhpcy5pZCA9IHRoaXMub3B0cy5pZFxuXG4gICAgICAgIGlmICh0eXBlb2YgdGhpcy5vcHRzLmljb24gPT09ICd1bmRlZmluZWQnICYmIHR5cGVvZiB0aGlzLm9wdHMuaWNvbkFjdGl2ZSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIHRoaXMub3B0cy5pY29uID0gdGhpcy5vcHRzLmljb25BY3RpdmVcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgdGhpcy5vcHRzLmljb24gIT09ICd1bmRlZmluZWQnICYmIHR5cGVvZiB0aGlzLm9wdHMuaWNvbkFjdGl2ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIHRoaXMub3B0cy5pY29uQWN0aXZlID0gdGhpcy5vcHRzLmljb25cbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLm9wdHMuc3R5bGUgPT09ICdsaW5rJykge1xuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLm9wdHMsIHtzdHJva2VBbHBoYTogMCwgc3Ryb2tlQWN0aXZlQWxwaGE6IDAsIGZpbGxBbHBoYTogMCwgZmlsbEFjdGl2ZUFscGhhOiAwfSlcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX2FjdGl2ZSA9IG51bGxcbiAgICAgICAgdGhpcy5fZGlzYWJsZWQgPSBudWxsXG5cbiAgICAgICAgdGhpcy5pY29uSW5hY3RpdmUgPSBudWxsXG4gICAgICAgIHRoaXMuaWNvbkFjdGl2ZSA9IG51bGxcbiAgICAgICAgdGhpcy50ZXh0ID0gbnVsbFxuXG4gICAgICAgIHRoaXMuYnV0dG9uID0gbnVsbFxuICAgICAgICB0aGlzLmNvbnRlbnQgPSBudWxsXG5cbiAgICAgICAgdGhpcy50b29sdGlwID0gbnVsbFxuICAgICAgICB0aGlzLmJhZGdlID0gbnVsbFxuXG4gICAgICAgIHRoaXMudmlzaWJsZSA9IHRoaXMub3B0cy52aXNpYmxlXG5cbiAgICAgICAgLy8gc2V0dXBcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnNldHVwKClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDYXB0dXJlcyBhbiBldmVudCB0byBpbmZvcm0gSW50ZXJhY3Rpb25NYXBwZXIgYWJvdXQgcHJvY2Vzc2VkIGV2ZW50cy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7ZXZlbnR8UElYSS5JbnRlcmFjdGlvbkV2ZW50fSBldmVudCAtIFRoZSBQSVhJIGV2ZW50IHRvIGNhcHR1cmUuXG4gICAgICovXG4gICAgY2FwdHVyZShldmVudCkge1xuICAgICAgICBFdmVudHMuY2FwdHVyZWRCeShldmVudC5kYXRhLm9yaWdpbmFsRXZlbnQsIHRoaXMpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBjaGlsZHJlbiBhbmQgaW5zdGFudGlhdGVzIGV2ZXJ5dGhpbmcuXG4gICAgICpcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge0J1dHRvbn0gQSByZWZlcmVuY2UgdG8gdGhlIGJ1dHRvbiBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgc2V0dXAoKSB7XG5cbiAgICAgICAgLy8gQnV0dG9uXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgbGV0IGJ1dHRvbiA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAgICAgICAgdGhpcy5idXR0b24gPSBidXR0b25cbiAgICAgICAgdGhpcy5hZGRDaGlsZChidXR0b24pXG5cbiAgICAgICAgLy8gQ29udGVudFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGxldCBjb250ZW50ID0gbmV3IFBJWEkuQ29udGFpbmVyKClcbiAgICAgICAgdGhpcy5jb250ZW50ID0gY29udGVudFxuICAgICAgICB0aGlzLmFkZENoaWxkKGNvbnRlbnQpXG5cbiAgICAgICAgLy8gVGV4dFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGlmICh0aGlzLm9wdHMubGFiZWwpIHtcbiAgICAgICAgICAgIHRoaXMudGV4dCA9IG5ldyBQSVhJLlRleHQodGhpcy5vcHRzLmxhYmVsLCB0aGlzLm9wdHMudGV4dFN0eWxlKVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWNvblxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGlmICh0aGlzLm9wdHMuaWNvbikge1xuICAgICAgICAgICAgdGhpcy5pY29uSW5hY3RpdmUgPSB0aGlzLmxvYWRJY29uKHRoaXMub3B0cy5pY29uLCB0aGlzLm9wdHMuaWNvbkNvbG9yKVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0cy5pY29uQWN0aXZlKSB7XG4gICAgICAgICAgICB0aGlzLmljb25BY3RpdmUgPSB0aGlzLmxvYWRJY29uKHRoaXMub3B0cy5pY29uQWN0aXZlLCB0aGlzLm9wdHMuaWNvbkNvbG9yQWN0aXZlKVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gaW50ZXJhY3Rpb25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmJ1dHRvbi5vbigncG9pbnRlcm92ZXInLCBlID0+IHtcbiAgICAgICAgICAgIHRoaXMuY2FwdHVyZShlKVxuICAgICAgICAgICAgVHdlZW5MaXRlLnRvKFt0aGlzLmJ1dHRvbiwgdGhpcy5jb250ZW50XSwgdGhpcy50aGVtZS5mYXN0LCB7YWxwaGE6IC44Mywgb3ZlcndyaXRlOiAnbm9uZSd9KVxuICAgICAgICB9KVxuXG4gICAgICAgIHRoaXMuYnV0dG9uLm9uKCdwb2ludGVybW92ZScsIGUgPT4ge1xuICAgICAgICAgICAgdGhpcy5jYXB0dXJlKGUpXG4gICAgICAgIH0pXG5cbiAgICAgICAgdGhpcy5idXR0b24ub24oJ3BvaW50ZXJvdXQnLCBlID0+IHtcbiAgICAgICAgICAgIHRoaXMuY2FwdHVyZShlKVxuICAgICAgICAgICAgVHdlZW5MaXRlLnRvKFt0aGlzLmJ1dHRvbiwgdGhpcy5jb250ZW50XSwgdGhpcy50aGVtZS5mYXN0LCB7YWxwaGE6IDEsIG92ZXJ3cml0ZTogJ25vbmUnfSlcbiAgICAgICAgfSlcblxuICAgICAgICB0aGlzLmJ1dHRvbi5vbigncG9pbnRlcmRvd24nLCBlID0+IHtcbiAgICAgICAgICAgIC8vdGhpcy5jYXB0dXJlKGUpXG4gICAgICAgICAgICBUd2VlbkxpdGUudG8oW3RoaXMuYnV0dG9uLCB0aGlzLmNvbnRlbnRdLCB0aGlzLnRoZW1lLmZhc3QsIHthbHBoYTogLjcsIG92ZXJ3cml0ZTogJ25vbmUnfSlcbiAgICAgICAgfSlcblxuICAgICAgICB0aGlzLmJ1dHRvbi5vbigncG9pbnRlcnVwJywgZSA9PiB7XG4gICAgICAgICAgICB0aGlzLmNhcHR1cmUoZSlcbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuYmVmb3JlQWN0aW9uKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vcHRzLmJlZm9yZUFjdGlvbi5jYWxsKHRoaXMsIGUsIHRoaXMpXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuYWN0aW9uKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vcHRzLmFjdGlvbi5jYWxsKHRoaXMsIGUsIHRoaXMpXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIFR3ZWVuTGl0ZS50byhbdGhpcy5idXR0b24sIHRoaXMuY29udGVudF0sIHRoaXMudGhlbWUuZmFzdCwge2FscGhhOiAuODMsIG92ZXJ3cml0ZTogJ25vbmUnfSlcblxuICAgICAgICAgICAgaWYgKHRoaXMub3B0cy50eXBlID09PSAnY2hlY2tib3gnKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5hY3RpdmUgPSAhdGhpcy5hY3RpdmVcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5hZnRlckFjdGlvbikge1xuICAgICAgICAgICAgICAgIHRoaXMub3B0cy5hZnRlckFjdGlvbi5jYWxsKHRoaXMsIGUsIHRoaXMpXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gZGlzYWJsZWRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmRpc2FibGVkID0gdGhpcy5vcHRzLmRpc2FibGVkXG5cbiAgICAgICAgLy8gYWN0aXZlXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5hY3RpdmUgPSB0aGlzLm9wdHMuYWN0aXZlICAgICAgLy8gY2FsbHMgLmxheW91dCgpXG5cbiAgICAgICAgLy8gdG9vbHRpcFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGlmICh0aGlzLm9wdHMudG9vbHRpcCkge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiB0aGlzLm9wdHMudG9vbHRpcCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnRvb2x0aXAgPSBuZXcgVG9vbHRpcCh7b2JqZWN0OiB0aGlzLCBjb250ZW50OiB0aGlzLm9wdHMudG9vbHRpcH0pXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMub3B0cy50b29sdGlwID0gT2JqZWN0LmFzc2lnbih7fSwge29iamVjdDogdGhpc30sIHRoaXMub3B0cy50b29sdGlwKVxuICAgICAgICAgICAgICAgIHRoaXMudG9vbHRpcCA9IG5ldyBUb29sdGlwKHRoaXMub3B0cy50b29sdGlwKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gYmFkZ2VcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLmJhZGdlKSB7XG4gICAgICAgICAgICBsZXQgb3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIHtcbiAgICAgICAgICAgICAgICBhbGlnbjogJ3JpZ2h0JyxcbiAgICAgICAgICAgICAgICB2ZXJ0aWNhbEFsaWduOiAndG9wJyxcbiAgICAgICAgICAgICAgICBvZmZzZXRMZWZ0OiAwLFxuICAgICAgICAgICAgICAgIG9mZnNldFRvcDogMFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIGlmICh0eXBlb2YgdGhpcy5vcHRzLmJhZGdlID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgIG9wdHMgPSBPYmplY3QuYXNzaWduKG9wdHMsIHtjb250ZW50OiB0aGlzLm9wdHMuYmFkZ2V9KVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBvcHRzID0gT2JqZWN0LmFzc2lnbihvcHRzLCB0aGlzLm9wdHMuYmFkZ2UpXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IGJhZGdlID0gbmV3IEJhZGdlKG9wdHMpXG5cbiAgICAgICAgICAgIHN3aXRjaCAob3B0cy5hbGlnbikge1xuICAgICAgICAgICAgICAgIGNhc2UgJ2xlZnQnOlxuICAgICAgICAgICAgICAgICAgICBiYWRnZS54ID0gdGhpcy54IC0gYmFkZ2Uud2lkdGggLyAyICsgb3B0cy5vZmZzZXRMZWZ0XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgY2FzZSAnY2VudGVyJzpcbiAgICAgICAgICAgICAgICAgICAgYmFkZ2UueCA9IHRoaXMueCArIHRoaXMud2lkdGggLyAyIC0gYmFkZ2Uud2lkdGggLyAyICsgb3B0cy5vZmZzZXRMZWZ0XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgY2FzZSAncmlnaHQnOlxuICAgICAgICAgICAgICAgICAgICBiYWRnZS54ID0gdGhpcy54ICsgdGhpcy53aWR0aCAtIGJhZGdlLndpZHRoIC8gMiArIG9wdHMub2Zmc2V0TGVmdFxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzd2l0Y2ggKG9wdHMudmVydGljYWxBbGlnbikge1xuICAgICAgICAgICAgICAgIGNhc2UgJ3RvcCc6XG4gICAgICAgICAgICAgICAgICAgIGJhZGdlLnkgPSB0aGlzLnkgLSBiYWRnZS5oZWlnaHQgLyAyICsgb3B0cy5vZmZzZXRUb3BcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICBjYXNlICdtaWRkbGUnOlxuICAgICAgICAgICAgICAgICAgICBiYWRnZS55ID0gdGhpcy55ICsgdGhpcy5oZWlnaHQgLyAyIC0gYmFkZ2UuaGVpZ2h0IC8gMiArIG9wdHMub2Zmc2V0VG9wXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgY2FzZSAnYm90dG9tJzpcbiAgICAgICAgICAgICAgICAgICAgYmFkZ2UueSA9IHRoaXMueSArIHRoaXMuaGVpZ2h0IC0gYmFkZ2UuaGVpZ2h0IC8gMiArIG9wdHMub2Zmc2V0VG9wXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRoaXMuYWRkQ2hpbGQoYmFkZ2UpXG5cbiAgICAgICAgICAgIHRoaXMuYmFkZ2UgPSBiYWRnZVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gc2V0IHBvc2l0aW9uXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5wb3NpdGlvbi5zZXQodGhpcy5vcHRzLngsIHRoaXMub3B0cy55KVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2hvdWxkIGJlIGNhbGxlZCB0byByZWZyZXNoIHRoZSBsYXlvdXQgb2YgdGhlIGJ1dHRvbi4gQ2FuIGJlIHVzZWQgYWZ0ZXIgcmVzaXppbmcuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtCdXR0b259IEEgcmVmZXJlbmNlIHRvIHRoZSBidXR0b24gZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGxheW91dCgpIHtcblxuICAgICAgICAvLyBDbGVhciBjb250ZW50XG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5yZW1vdmVDaGlsZCh0aGlzLmNvbnRlbnQpXG4gICAgICAgIHRoaXMuY29udGVudCA9IG5ldyBQSVhJLkNvbnRhaW5lcigpXG4gICAgICAgIHRoaXMuYWRkQ2hpbGQodGhpcy5jb250ZW50KVxuXG4gICAgICAgIC8vIFNldCB0aGUgaWNvblxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGxldCBpY29uID0gbnVsbFxuXG4gICAgICAgIGlmICghdGhpcy5hY3RpdmUgJiYgdGhpcy5pY29uSW5hY3RpdmUpIHtcbiAgICAgICAgICAgIGljb24gPSB0aGlzLmljb25JbmFjdGl2ZVxuICAgICAgICB9IGVsc2UgaWYgKHRoaXMuYWN0aXZlICYmIHRoaXMuaWNvbkFjdGl2ZSkge1xuICAgICAgICAgICAgaWNvbiA9IHRoaXMuaWNvbkFjdGl2ZVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gU2V0IHRoZSB0ZXh0XG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgaWYgKHRoaXMudGV4dCkge1xuICAgICAgICAgICAgdGhpcy50ZXh0LnBvc2l0aW9uLnNldCgwLCAwKVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gV2lkdGggYW5kIEhlaWdodFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGxldCB3aWR0aCA9IDBcbiAgICAgICAgaWYgKGljb24gJiYgdGhpcy50ZXh0KSB7XG4gICAgICAgICAgICB3aWR0aCA9IGljb24ud2lkdGggKyB0aGlzLnRleHQud2lkdGggKyAzICogdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgfSBlbHNlIGlmIChpY29uKSB7XG4gICAgICAgICAgICB3aWR0aCA9IGljb24ud2lkdGggKyAyICogdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgfSBlbHNlIGlmICh0aGlzLnRleHQpIHtcbiAgICAgICAgICAgIHdpZHRoID0gdGhpcy50ZXh0LndpZHRoICsgMiAqIHRoaXMub3B0cy5wYWRkaW5nXG4gICAgICAgIH1cblxuICAgICAgICBpZiAod2lkdGggPCB0aGlzLm9wdHMubWluV2lkdGgpIHtcbiAgICAgICAgICAgIHdpZHRoID0gdGhpcy5vcHRzLm1pbldpZHRoXG4gICAgICAgIH1cblxuICAgICAgICBsZXQgaGVpZ2h0ID0gMFxuICAgICAgICBpZiAoaWNvbikge1xuICAgICAgICAgICAgaGVpZ2h0ID0gaWNvbi5oZWlnaHQgKyAyICogdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgfSBlbHNlIGlmICh0aGlzLnRleHQpIHtcbiAgICAgICAgICAgIGhlaWdodCA9IHRoaXMudGV4dC5oZWlnaHQgKyAyICogdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChoZWlnaHQgPCB0aGlzLm9wdHMubWluSGVpZ2h0KSB7XG4gICAgICAgICAgICBoZWlnaHQgPSB0aGlzLm9wdHMubWluSGVpZ2h0XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl93aWR0aCA9IHdpZHRoXG4gICAgICAgIHRoaXMuX2hlaWdodCA9IGhlaWdodFxuXG4gICAgICAgIC8vIFBvc2l0aW9uIGljb24gYW5kIHRleHRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAoaWNvbiAmJiB0aGlzLnRleHQpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuaWNvblBvc2l0aW9uID09PSAncmlnaHQnKSB7XG4gICAgICAgICAgICAgICAgaWNvbi54ID0gdGhpcy50ZXh0LndpZHRoICsgdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy50ZXh0LnggPSBpY29uLndpZHRoICsgdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuY29udGVudC5hZGRDaGlsZChpY29uLCB0aGlzLnRleHQpXG4gICAgICAgIH0gZWxzZSBpZiAoaWNvbikge1xuICAgICAgICAgICAgdGhpcy5jb250ZW50LmFkZENoaWxkKGljb24pXG4gICAgICAgIH0gZWxzZSBpZiAodGhpcy50ZXh0KSB7XG4gICAgICAgICAgICB0aGlzLmNvbnRlbnQuYWRkQ2hpbGQodGhpcy50ZXh0KVxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5sYXlvdXRJbm5lckNvbnRlbnQoKVxuICAgICAgICB0aGlzLmxheW91dENvbnRlbnQoKVxuXG4gICAgICAgIHRoaXMuaWNvbiA9IGljb25cblxuICAgICAgICAvLyBkcmF3XG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5kcmF3KClcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZXMgdGhlIHBvc2l0aW9ucyBvZiB0aGUgY29udGVudCBjaGlsZHJlbiAoaWNvbiBhbmQvb3IgdGV4dCkuXG4gICAgICpcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge0J1dHRvbn0gQSByZWZlcmVuY2UgdG8gdGhlIGJ1dHRvbiBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgbGF5b3V0SW5uZXJDb250ZW50KCkge1xuXG4gICAgICAgIGZvciAobGV0IGNoaWxkIG9mIHRoaXMuY29udGVudC5jaGlsZHJlbikge1xuICAgICAgICAgICAgc3dpdGNoICh0aGlzLm9wdHMudmVydGljYWxBbGlnbikge1xuICAgICAgICAgICAgICAgIGNhc2UgJ3RvcCc6XG4gICAgICAgICAgICAgICAgICAgIGNoaWxkLnkgPSAwXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgY2FzZSAnbWlkZGxlJzpcbiAgICAgICAgICAgICAgICAgICAgY2hpbGQueSA9IHRoaXMuY29udGVudC5oZWlnaHQgLyAyIC0gY2hpbGQuaGVpZ2h0IC8gMlxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgIGNhc2UgJ2JvdHRvbSc6XG4gICAgICAgICAgICAgICAgICAgIGNoaWxkLnkgPSB0aGlzLmNvbnRlbnQuaGVpZ2h0IC0gY2hpbGQuaGVpZ2h0XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldHMgdGhlIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIHBvc2l0aW9uIG9mIHRoZSBjb250ZW50LlxuICAgICAqIFVzZXMgdGhlIG9wdGlvbiBrZXlzIFwiYWxpZ25cIiBhbmQgXCJ2ZXJ0aWNhbEFsaWduXCIuXG4gICAgICpcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge0J1dHRvbn0gQSByZWZlcmVuY2UgdG8gdGhlIGJ1dHRvbiBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgbGF5b3V0Q29udGVudCgpIHtcblxuICAgICAgICBzd2l0Y2ggKHRoaXMub3B0cy5hbGlnbikge1xuICAgICAgICAgICAgY2FzZSAnbGVmdCc6XG4gICAgICAgICAgICAgICAgdGhpcy5jb250ZW50LnggPSB0aGlzLm9wdHMucGFkZGluZ1xuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdjZW50ZXInOlxuICAgICAgICAgICAgICAgIHRoaXMuY29udGVudC54ID0gKCh0aGlzLl93aWR0aCAtIHRoaXMuY29udGVudC53aWR0aCkgLyAyKVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdyaWdodCc6XG4gICAgICAgICAgICAgICAgdGhpcy5jb250ZW50LnggPSB0aGlzLl93aWR0aCAtIHRoaXMub3B0cy5wYWRkaW5nIC0gdGhpcy5jb250ZW50LndpZHRoXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuXG4gICAgICAgIHN3aXRjaCAodGhpcy5vcHRzLnZlcnRpY2FsQWxpZ24pIHtcbiAgICAgICAgICAgIGNhc2UgJ3RvcCc6XG4gICAgICAgICAgICAgICAgdGhpcy5jb250ZW50LnkgPSB0aGlzLm9wdHMucGFkZGluZ1xuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdtaWRkbGUnOlxuICAgICAgICAgICAgICAgIHRoaXMuY29udGVudC55ID0gKHRoaXMuX2hlaWdodCAtIHRoaXMuY29udGVudC5oZWlnaHQpIC8gMlxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdib3R0b20nOlxuICAgICAgICAgICAgICAgIHRoaXMuY29udGVudC55ID0gdGhpcy5faGVpZ2h0IC0gdGhpcy5vcHRzLnBhZGRpbmcgLSB0aGlzLmNvbnRlbnQuaGVpZ2h0XG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRHJhd3MgdGhlIGNhbnZhcy5cbiAgICAgKlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybiB7QnV0dG9ufSBBIHJlZmVyZW5jZSB0byB0aGUgYnV0dG9uIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBkcmF3KCkge1xuXG4gICAgICAgIHRoaXMuYnV0dG9uLmNsZWFyKClcbiAgICAgICAgaWYgKHRoaXMuYWN0aXZlKSB7XG4gICAgICAgICAgICB0aGlzLmJ1dHRvbi5saW5lU3R5bGUodGhpcy5vcHRzLnN0cm9rZUFjdGl2ZVdpZHRoLCB0aGlzLm9wdHMuc3Ryb2tlQWN0aXZlLCB0aGlzLm9wdHMuc3Ryb2tlQWN0aXZlQWxwaGEpXG4gICAgICAgICAgICB0aGlzLmJ1dHRvbi5iZWdpbkZpbGwodGhpcy5vcHRzLmZpbGxBY3RpdmUsIHRoaXMub3B0cy5maWxsQWN0aXZlQWxwaGEpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmJ1dHRvbi5saW5lU3R5bGUodGhpcy5vcHRzLnN0cm9rZVdpZHRoLCB0aGlzLm9wdHMuc3Ryb2tlLCB0aGlzLm9wdHMuc3Ryb2tlQWxwaGEpXG4gICAgICAgICAgICB0aGlzLmJ1dHRvbi5iZWdpbkZpbGwodGhpcy5vcHRzLmZpbGwsIHRoaXMub3B0cy5maWxsQWxwaGEpXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5idXR0b24uZHJhd1JvdW5kZWRSZWN0KDAsIDAsIHRoaXMuX3dpZHRoLCB0aGlzLl9oZWlnaHQsIHRoaXMub3B0cy5yYWRpdXMpXG4gICAgICAgIHRoaXMuYnV0dG9uLmVuZEZpbGwoKVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0cyBvciBzZXRzIHRoZSBhY3RpdmUgc3RhdGUuXG4gICAgICpcbiAgICAgKiBAbWVtYmVyIHtib29sZWFufVxuICAgICAqL1xuICAgIGdldCBhY3RpdmUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9hY3RpdmVcbiAgICB9XG4gICAgc2V0IGFjdGl2ZSh2YWx1ZSkge1xuXG4gICAgICAgIHRoaXMuX2FjdGl2ZSA9IHZhbHVlXG5cbiAgICAgICAgaWYgKHRoaXMuX2FjdGl2ZSkge1xuICAgICAgICAgICAgaWYgKHRoaXMudGV4dCkge1xuICAgICAgICAgICAgICAgIHRoaXMudGV4dC5zdHlsZSA9IHRoaXMub3B0cy50ZXh0U3R5bGVBY3RpdmVcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnRleHQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnRleHQuc3R5bGUgPSB0aGlzLm9wdHMudGV4dFN0eWxlXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmxheW91dCgpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0cyBvciBzZXRzIHRoZSBkaXNhYmxlZCBzdGF0ZS4gV2hlbiBkaXNhYmxlZCwgdGhlIGJ1dHRvbiBjYW5ub3QgYmUgY2xpY2tlZC5cbiAgICAgKlxuICAgICAqIEBtZW1iZXIge2Jvb2xlYW59XG4gICAgICovXG4gICAgZ2V0IGRpc2FibGVkKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fZGlzYWJsZWRcbiAgICB9XG4gICAgc2V0IGRpc2FibGVkKHZhbHVlKSB7XG5cbiAgICAgICAgdGhpcy5fZGlzYWJsZWQgPSB2YWx1ZVxuXG4gICAgICAgIGlmICh0aGlzLl9kaXNhYmxlZCkge1xuICAgICAgICAgICAgdGhpcy5idXR0b24uaW50ZXJhY3RpdmUgPSBmYWxzZVxuICAgICAgICAgICAgdGhpcy5idXR0b24uYnV0dG9uTW9kZSA9IGZhbHNlXG4gICAgICAgICAgICB0aGlzLmJ1dHRvbi5hbHBoYSA9IC41XG4gICAgICAgICAgICBpZiAodGhpcy5pY29uKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5pY29uLmFscGhhID0gLjVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0aGlzLnRleHQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnRleHQuYWxwaGEgPSAuNVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5idXR0b24uaW50ZXJhY3RpdmUgPSB0cnVlXG4gICAgICAgICAgICB0aGlzLmJ1dHRvbi5idXR0b25Nb2RlID0gdHJ1ZVxuICAgICAgICAgICAgdGhpcy5idXR0b24uYWxwaGEgPSAxXG4gICAgICAgICAgICBpZiAodGhpcy5pY29uKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5pY29uLmFscGhhID0gMVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHRoaXMudGV4dCkge1xuICAgICAgICAgICAgICAgIHRoaXMudGV4dC5hbHBoYSA9IDFcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNob3dzIHRoZSBidXR0b24gKHNldHMgaGlzIGFscGhhIHZhbHVlcyB0byAxKS5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge0J1dHRvbn0gQSByZWZlcmVuY2UgdG8gdGhlIGJ1dHRvbiBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgc2hvdygpIHtcblxuICAgICAgICB0aGlzLm9wdHMuc3Ryb2tlQWxwaGEgPSAxXG4gICAgICAgIHRoaXMub3B0cy5zdHJva2VBY3RpdmVBbHBoYSA9IDFcbiAgICAgICAgdGhpcy5vcHRzLmZpbGxBbHBoYSA9IDFcbiAgICAgICAgdGhpcy5vcHRzLmZpbGxBY3RpdmVBbHBoYSA9IDFcblxuICAgICAgICB0aGlzLmxheW91dCgpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBIaWRlcyB0aGUgYnV0dG9uIChzZXRzIGhpcyBhbHBoYSB2YWx1ZXMgdG8gMCkuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtCdXR0b259IEEgcmVmZXJlbmNlIHRvIHRoZSBidXR0b24gZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGhpZGUoKSB7XG5cbiAgICAgICAgdGhpcy5vcHRzLnN0cm9rZUFscGhhID0gMFxuICAgICAgICB0aGlzLm9wdHMuc3Ryb2tlQWN0aXZlQWxwaGEgPSAwXG4gICAgICAgIHRoaXMub3B0cy5maWxsQWxwaGEgPSAwXG4gICAgICAgIHRoaXMub3B0cy5maWxsQWN0aXZlQWxwaGEgPSAwXG5cbiAgICAgICAgdGhpcy5sYXlvdXQoKVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTG9hZHMgYW4gaWNvblxuICAgICAqXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcGFyYW0ge3N0cmluZ3xQSVhJLkRpc3BsYXlPYmplY3R9IGljb24gLSBUaGUgaWNvbiB0byBsb2FkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBjb2xvciAtIFRoZSBjb2xvciBvZiB0aGUgaWNvbiAoaWYgbm90IGFuIFBJWEkuRGlzcGxheU9iamVjdCkuXG4gICAgICogQHJldHVybiB7UElYSS5EaXNwbGF5T2JqZWN0fSBSZXR1cm4gdGhlIGljb24gYXMgYW4gUElYSS5EaXNwbGF5T2JqZWN0LlxuICAgICAqL1xuICAgIGxvYWRJY29uKGljb24sIGNvbG9yKSB7XG5cbiAgICAgICAgbGV0IGRpc3BsYXlPYmplY3QgPSBudWxsXG5cbiAgICAgICAgaWYgKGljb24gaW5zdGFuY2VvZiBQSVhJLkRpc3BsYXlPYmplY3QpIHtcbiAgICAgICAgICAgIGRpc3BsYXlPYmplY3QgPSBpY29uXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsZXQgc2l6ZSA9IDE3XG4gICAgICAgICAgICBpZiAodGhpcy50ZXh0KSB7XG4gICAgICAgICAgICAgICAgc2l6ZSA9IHRoaXMudGV4dC5oZWlnaHRcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5vcHRzLm1pbkhlaWdodCkge1xuICAgICAgICAgICAgICAgIHNpemUgPSB0aGlzLm9wdHMubWluSGVpZ2h0IC0gKDIgKiB0aGlzLm9wdHMucGFkZGluZylcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgdXJsID0gQnV0dG9uLmljb25Jc1VybChpY29uKSA/IGljb24gOiBgLi4vLi4vYXNzZXRzL2ljb25zL3BuZy9mbGF0LyR7aWNvbn0ucG5nYFxuICAgICAgICAgICAgY29uc3QgaWNvblRleHR1cmUgPSBQSVhJLlRleHR1cmUuZnJvbUltYWdlKHVybCwgdHJ1ZSlcblxuICAgICAgICAgICAgY29uc3Qgc3ByaXRlID0gbmV3IFBJWEkuU3ByaXRlKGljb25UZXh0dXJlKVxuICAgICAgICAgICAgc3ByaXRlLnRpbnQgPSBjb2xvclxuICAgICAgICAgICAgc3ByaXRlLndpZHRoID0gc2l6ZVxuICAgICAgICAgICAgc3ByaXRlLmhlaWdodCA9IHNpemVcblxuICAgICAgICAgICAgZGlzcGxheU9iamVjdCA9IHNwcml0ZVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGRpc3BsYXlPYmplY3RcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBUZXN0cyBpZiBhbiBpY29uIHN0cmluZyBpcyBhbiB1cmwuXG4gICAgICpcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIHVybCB0byB0ZXN0LlxuICAgICAqIEByZXR1cm4ge2Jvb2xlYW59IHRydWUgaWYgdGhlIHVybCBpcyBhbiB1cmwgdG8gYW4gaW1hZ2UuXG4gICAgICovXG4gICAgc3RhdGljIGljb25Jc1VybCh1cmwpIHtcbiAgICAgICAgcmV0dXJuIC9cXC4ocG5nfHN2Z3xnaWZ8anBnfGpwZWd8dGlmfHRpZmYpJC9pLnRlc3QodXJsKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldHMgb3Igc2V0cyB0aGUgY29sb3Igb2YgdGhlIGN1cnJlbnQgaWNvbiAobm8gbWF0dGVyIGhvdyB0aGUgc3RhdHVzIGlzKS4gQ2hhbmdpbmcgdGhlIGNvbG9yLCBjaGFuZ2VzXG4gICAgICogdGhlIHRpbnQgcHJvcGVydHkgb2YgdGhlIGljb24gc3ByaXRlLlxuICAgICAqXG4gICAgICogQG1lbWJlciB7bnVtYmVyfVxuICAgICAqL1xuICAgIGdldCBpY29uQ29sb3IoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmljb24gPyB0aGlzLmljb24udGludCA6IG51bGxcbiAgICB9XG4gICAgc2V0IGljb25Db2xvcih2YWx1ZSkge1xuICAgICAgICBpZiAodGhpcy5pY29uKSB7XG4gICAgICAgICAgICB0aGlzLmljb24udGludCA9IHZhbHVlXG4gICAgICAgIH1cbiAgICB9XG59XG4iLCJpbXBvcnQgVGhlbWUgZnJvbSAnLi90aGVtZS5qcydcbmltcG9ydCBCdXR0b24gZnJvbSAnLi9idXR0b24uanMnXG5cbi8qKlxuICogQ2xhc3MgdGhhdCByZXByZXNlbnRzIGEgUGl4aUpTIEJ1dHRvbkdyb3VwLlxuICogXG4gKiBAZXhhbXBsZVxuICogLy8gQ3JlYXRlIHRoZSBidXR0b24gZ3JvdXBcbiAqIGNvbnN0IGJ1dHRvbkdyb3VwID0gbmV3IEJ1dHRvbkdyb3VwKHtcbiAqICAgICBidXR0b25zOiBbXG4gKiAgICAgICAgIHtsYWJlbDogJ0J1dHRvbiAxJywgYWN0aW9uOiBldmVudCA9PiBjb25zb2xlLmxvZyhldmVudCl9LFxuICogICAgICAgICB7bGFiZWw6ICdCdXR0b24gMicsIGFjdGlvbjogZXZlbnQgPT4gY29uc29sZS5sb2coZXZlbnQpfSxcbiAqICAgICAgICAge2xhYmVsOiAnQnV0dG9uIDMnLCBhY3Rpb246IGV2ZW50ID0+IGNvbnNvbGUubG9nKGV2ZW50KX1cbiAqICAgICBdLFxuICogICAgIG1pbldpZHRoOiAxMDBcbiAqIH0pXG4gKlxuICogLy8gQWRkIHRoZSBidXR0b24gZ3JvdXAgdG8gYSBEaXNwbGF5T2JqZWN0XG4gKiBhcHAuc2NlbmUuYWRkQ2hpbGQoYnV0dG9uR3JvdXApXG4gKlxuICogQGNsYXNzXG4gKiBAZXh0ZW5kcyBQSVhJLkdyYXBoaWNzXG4gKiBAc2VlIHtAbGluayBodHRwOi8vcGl4aWpzLmRvd25sb2FkL2Rldi9kb2NzL1BJWEkuR3JhcGhpY3MuaHRtbHxQSVhJLkdyYXBoaWNzfVxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly93d3cuaXdtLXR1ZWJpbmdlbi5kZS9pd21icm93c2VyL2xpYi9waXhpL2J1dHRvbmdyb3VwLmh0bWx8RG9jVGVzdH1cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQnV0dG9uR3JvdXAgZXh0ZW5kcyBQSVhJLkdyYXBoaWNzIHtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYSBCdXR0b25Hcm91cC5cbiAgICAgKiBcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0byBzdHlsZSBhbmQgYmVoYXZpb3VyIG9mIHRoZSBidXR0b24gZ3JvdXAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmlkPWF1dG8gZ2VuZXJhdGVkXSAtIFRoZSBpZCBvZiB0aGUgYnV0dG9uIGdyb3VwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy54PTBdIC0gVGhlIHggcG9zaXRpb24gb2YgdGhlIGJ1dHRvbiBncm91cC4gQ2FuIGJlIGFsc28gc2V0IGFmdGVyIGNyZWF0aW9uIHdpdGggYnV0dG9uR3JvdXAueCA9IDAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnk9MF0gLSBUaGUgeSBwb3NpdGlvbiBvZiB0aGUgYnV0dG9uIGdyb3VwLiBDYW4gYmUgYWxzbyBzZXQgYWZ0ZXIgY3JlYXRpb24gd2l0aCBidXR0b25Hcm91cC55ID0gMC5cbiAgICAgKiBAcGFyYW0ge29iamVjdFtdfSBbb3B0cy5idXR0b25zPVtdXSAtIEFuIGFycmF5IG9mIHRoZSBidXR0b25zIG9mIHRoZSBidXR0b24gZ3JvdXAuIE9uZSBpdGVtIG9mIHRoZSBhcnJheSAob25lIG9iamVjdClcbiAgICAgKiAgICAgY2FuIGhhdmUgZXhhY3RseSB0aGUgc2FtZSBwcm9wZXJ0aWVzIGFzIGFuIEJ1dHRvbiBvYmplY3Qgd2hlbiBpbnN0YW50aWF0aW5nIGEgQnV0dG9uLiBJZiBhIHByb3BlcnR5IG9mIHRoZSBidXR0b24gZ3JvdXBcbiAgICAgKiAgICAgY29uZmxpY3RzIHdpdGggYSBwcm9wZXJ0eSBvZiBhIGJ1dHRvbiBvYmplY3QsIHRoZSB2YWx1ZSBmcm9tIHRoZSBidXR0b24gb2JqZWN0IHdpbGwgYmUgdXNlZC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ3xUaGVtZT19IFtvcHRzLnRoZW1lPWRhcmtdIC0gVGhlIHRoZW1lIHRvIHVzZSBmb3IgdGhpcyBidXR0b24gZ3JvdXAuIFBvc3NpYmxlIHZhbHVlcyBhcmUgZGFyaywgbGlnaHQsIHJlZFxuICAgICAqICAgICBvciBhIFRoZW1lIG9iamVjdC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMubWluV2lkdGg9NDRdIC0gQnV0dG9uOiBUaGUgbWluaW11bSB3aWR0aCBvZiBvbmUgYnV0dG9uLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5taW5IZWlnaHQ9NDRdIC0gQnV0dG9uOiBUaGUgbWluaW11bSBoZWlnaHQgb2Ygb25lIGJ1dHRvbi5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMucGFkZGluZz1UaGVtZS5wYWRkaW5nXSAtIEJ1dHRvbjogVGhlIGlubmVyIHNwYWNpbmcgKGRpc3RhbmNlIGZyb20gaWNvbiBhbmQvb3IgbGFiZWwpIHRoZSB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5tYXJnaW49VGhlbWUubWFyZ2luXSAtIFRoZSBvdXRlciBzcGFjaW5nIChkaXN0YW5jZSBmcm9tIG9uZSBidXR0b24gdG8gdGhlIHByZXZpb3VzL25leHQgYnV0dG9uKS5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMuaWNvblBvc2l0aW9uPWxlZnRdIC0gQnV0dG9uOiBUaGUgcG9zaXRpb24gb2YgdGhlIGljb24gaW4gcmVsYXRpb24gdG8gdGhlIGxhYmVsLiBDYW4gYmUgbGVmdCBvciByaWdodC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuaWNvbkNvbG9yPVRoZW1lLmljb25Db2xvcl0gLSBCdXR0b246IFRoZSBjb2xvciBvZiB0aGUgaWNvbiAoc2V0IGJ5IHRoZSB0aW50IHByb3BlcnR5KSBhcyBhIGhleCB2YWx1ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuaWNvbkNvbG9yQWN0aXZlPVRoZW1lLmljb25Db2xvckFjdGl2ZV0gLSBCdXR0b246IFRoZSBjb2xvciBvZiB0aGUgaWNvbiB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZmlsbD1UaGVtZS5maWxsXSAtIEJ1dHRvbjogVGhlIGNvbG9yIG9mIHRoZSBidXR0b24gYmFja2dyb3VuZCBhcyBhIGhleCB2YWx1ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZmlsbEFscGhhPVRoZW1lLmZpbGxBbHBoYV0gLSBCdXR0b246IFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYmFja2dyb3VuZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZmlsbEFjdGl2ZT1UaGVtZS5maWxsQWN0aXZlXSAtIEJ1dHRvbjogVGhlIGNvbG9yIG9mIHRoZSBidXR0b24gYmFja2dyb3VuZCB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZmlsbEFjdGl2ZUFscGhhPVRoZW1lLmZpbGxBY3RpdmVBbHBoYV0gLSBCdXR0b246IFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYmFja2dyb3VuZCB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlPVRoZW1lLnN0cm9rZV0gLSBCdXR0b246IFRoZSBjb2xvciBvZiB0aGUgYm9yZGVyIGFzIGEgaGV4IHZhbHVlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VXaWR0aD1UaGVtZS5zdHJva2VXaWR0aF0gLSBCdXR0b246IFRoZSB3aWR0aCBvZiB0aGUgYm9yZGVyIGluIHBpeGVsLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VBbHBoYT1UaGVtZS5zdHJva2VBbHBoYV0gLSBCdXR0b246IFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VBY3RpdmU9VGhlbWUuc3Ryb2tlQWN0aXZlXSAtIEJ1dHRvbjogVGhlIGNvbG9yIG9mIHRoZSBib3JkZXIgd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnN0cm9rZUFjdGl2ZVdpZHRoPVRoZW1lLnN0cm9rZUFjdGl2ZVdpZHRoXSAtIEJ1dHRvbjogVGhlIHdpZHRoIG9mIHRoZSBib3JkZXIgaW4gcGl4ZWwgd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnN0cm9rZUFjdGl2ZUFscGhhPVRoZW1lLnN0cm9rZUFjdGl2ZUFscGhhXSAtIEJ1dHRvbjogVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBib3JkZXIgd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzLnRleHRTdHlsZT1UaGVtZS50ZXh0U3R5bGVdIC0gQnV0dG9uOiBBIHRleHRzdHlsZSBvYmplY3QgZm9yIHRoZSBzdHlsaW5nIG9mIHRoZSBsYWJlbC4gU2VlIFBJWEkuVGV4dFN0eWxlXG4gICAgICogICAgIGZvciBwb3NzaWJsZSBvcHRpb25zLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy50ZXh0U3R5bGVBY3RpdmU9VGhlbWUudGV4dFN0eWxlQWN0aXZlXSAtIEJ1dHRvbjogQSB0ZXh0c3R5bGUgb2JqZWN0IGZvciB0aGUgc3R5bGluZyBvZiB0aGUgbGFiZWwgd2hlbiB0aGVcbiAgICAgKiAgICAgYnV0dG9uIGlzIGFjdGl2YXRlZC4gU2VlIFBJWEkuVGV4dFN0eWxlIGZvciBwb3NzaWJsZSBvcHRpb25zLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5zdHlsZT1kZWZhdWx0XSAtIEEgc2hvcnRjdXQgZm9yIHN0eWxpbmcgb3B0aW9ucy4gUG9zc2libGUgdmFsdWVzIGFyZSBkZWZhdWx0LCBsaW5rLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5yYWRpdXM9VGhlbWUucmFkaXVzXSAtIEJ1dHRvbjogVGhlIHJhZGl1cyBvZiB0aGUgZm91ciBjb3JuZXJzIG9mIHRoZSBidXR0b24gKHdoaWNoIGlzIGEgcm91bmRlZCByZWN0YW5nbGUpLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuZGlzYWJsZWQ9ZmFsc2VdIC0gSXMgdGhlIGJ1dHRvbiBncm91cCBkaXNhYmxlZD8gV2hlbiBkaXNhYmxlZCwgdGhlIGJ1dHRvbiBncm91cCBoYXMgYSBsb3dlciBhbHBoYSB2YWx1ZVxuICAgICAqICAgICBhbmQgY2Fubm90IGJlIGNsaWNrZWQgKGludGVyYWN0aXZlIG9mIGV2ZXJ5IGJ1dHRvbiBpcyBzZXQgdG8gZmFsc2UpLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy50eXBlPWRlZmF1bHRdIC0gVGhlIHR5cGUgb2YgdGhlIGJ1dHRvbiBncm91cC4gQ2FuIGJlIGRlZmF1bHQsIGNoZWNrYm94IG9yIHJhZGlvLiBXaGVuIHRoZSB0eXBlIGlzXG4gICAgICogICAgIGNoZWNrYm94LCB0aGUgYWN0aXZlIHN0YXRlIGlzIHRvZ2dsZWQgZm9yIGVhY2ggYnV0dG9uIGF1dG9tYXRpY2FsbHkuIFdoZW4gdGhlIHR5cGUgaXMgcmFkaW8sIG9ubHkgb25lIGJ1dHRvbiBjYW5cbiAgICAgKiAgICAgYmUgYWN0aXZhdGVkIGF0IHRoZSBzYW1lIHRpbWUuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLm9yaWVudGF0aW9uPWhvcml6b250YWxdIC0gVGhlIG9yaWVudGF0aW9uIG9mIHRoZSBidXR0b24gZ3JvdXAuIENhbiBiZSBob3Jpem9udGFsIG9yIHZlcnRpY2FsLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5hbGlnbj1jZW50ZXJdIC0gQnV0dG9uOiBUaGUgaG9yaXpvbnRhbCBwb3NpdGlvbiBvZiB0aGUgbGFiZWwgYW5kIHRoZSBpY29uLiBQb3NzaWJsZSB2YWx1ZXMgYXJlXG4gICAgICogICAgIGxlZnQsIGNlbnRlciBhbmQgcmlnaHQuIE9ubHkgYWZmZWN0cyB0aGUgc3R5bGUgd2hlbiB0aGUgbWluV2lkdGggaXMgYmlnZ2VyIHRoYW4gdGhlIHdpZHRoIG9mIHRoZSBpY29uIGFuZCBsYWJlbC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMudmVydGljYWxBbGlnbj1taWRkbGVdIC0gQnV0dG9uOiBUaGUgdmVydGljYWwgcG9zaXRpb24gb2YgdGhlIGxhYmVsIGFuZCB0aGUgaWNvbi4gUG9zc2libGUgdmFsdWVzIGFyZVxuICAgICAqICAgICB0b3AsIG1pZGRsZSBhbmQgYm90dG9tLiBPbmx5IGFmZmVjdHMgdGhlIHN0eWxlIHdoZW4gdGhlIG1pbkhlaWdodCBpcyBiaWdnZXIgdGhhbiB0aGUgaGVpZ2h0IG9mIHRoZSBpY29uIGFuZCBsYWJlbC5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLnZpc2libGU9dHJ1ZV0gLSBJcyB0aGUgYnV0dG9uIGdyb3VwIGluaXRpYWxseSB2aXNpYmxlIChwcm9wZXJ0eSB2aXNpYmxlKT9cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihvcHRzID0ge30pIHtcblxuICAgICAgICBzdXBlcigpXG4gICAgICAgIFxuICAgICAgICBjb25zdCB0aGVtZSA9IFRoZW1lLmZyb21TdHJpbmcob3B0cy50aGVtZSlcbiAgICAgICAgdGhpcy50aGVtZSA9IHRoZW1lXG5cbiAgICAgICAgdGhpcy5vcHRzID0gT2JqZWN0LmFzc2lnbih7fSwge1xuICAgICAgICAgICAgaWQ6IFBJWEkudXRpbHMudWlkKCksXG4gICAgICAgICAgICB4OiAwLFxuICAgICAgICAgICAgeTogMCxcbiAgICAgICAgICAgIGJ1dHRvbnM6IFtdLFxuICAgICAgICAgICAgbWluV2lkdGg6IDQ0LFxuICAgICAgICAgICAgbWluSGVpZ2h0OiA0NCxcbiAgICAgICAgICAgIHBhZGRpbmc6IHRoZW1lLnBhZGRpbmcsXG4gICAgICAgICAgICBtYXJnaW46IHRoZW1lLm1hcmdpbixcbiAgICAgICAgICAgIGljb25Qb3NpdGlvbjogJ2xlZnQnLCAgICAgICAgICAgICAvLyBsZWZ0LCByaWdodFxuICAgICAgICAgICAgaWNvbkNvbG9yOiB0aGVtZS5pY29uQ29sb3IsXG4gICAgICAgICAgICBpY29uQ29sb3JBY3RpdmU6IHRoZW1lLmljb25Db2xvckFjdGl2ZSxcbiAgICAgICAgICAgIGZpbGw6IHRoZW1lLmZpbGwsXG4gICAgICAgICAgICBmaWxsQWxwaGE6IHRoZW1lLmZpbGxBbHBoYSxcbiAgICAgICAgICAgIGZpbGxBY3RpdmU6IHRoZW1lLmZpbGxBY3RpdmUsXG4gICAgICAgICAgICBmaWxsQWN0aXZlQWxwaGE6IHRoZW1lLmZpbGxBY3RpdmVBbHBoYSxcbiAgICAgICAgICAgIHN0cm9rZTogdGhlbWUuc3Ryb2tlLFxuICAgICAgICAgICAgc3Ryb2tlV2lkdGg6IHRoZW1lLnN0cm9rZVdpZHRoLFxuICAgICAgICAgICAgc3Ryb2tlQWxwaGE6IHRoZW1lLnN0cm9rZUFscGhhLFxuICAgICAgICAgICAgc3Ryb2tlQWN0aXZlOiB0aGVtZS5zdHJva2VBY3RpdmUsXG4gICAgICAgICAgICBzdHJva2VBY3RpdmVXaWR0aDogdGhlbWUuc3Ryb2tlQWN0aXZlV2lkdGgsXG4gICAgICAgICAgICBzdHJva2VBY3RpdmVBbHBoYTogdGhlbWUuc3Ryb2tlQWN0aXZlQWxwaGEsXG4gICAgICAgICAgICB0ZXh0U3R5bGU6IHRoZW1lLnRleHRTdHlsZSxcbiAgICAgICAgICAgIHRleHRTdHlsZUFjdGl2ZTogdGhlbWUudGV4dFN0eWxlQWN0aXZlLFxuICAgICAgICAgICAgc3R5bGU6ICdkZWZhdWx0JyxcbiAgICAgICAgICAgIHJhZGl1czogdGhlbWUucmFkaXVzLFxuICAgICAgICAgICAgZGlzYWJsZWQ6IG51bGwsXG4gICAgICAgICAgICB0eXBlOiAnZGVmYXVsdCcsICAgICAgICAgICAgICAgICAgIC8vIGRlZmF1bHQsIGNoZWNrYm94LCByYWRpb1xuICAgICAgICAgICAgb3JpZW50YXRpb246ICdob3Jpem9udGFsJyxcbiAgICAgICAgICAgIGFsaWduOiAnY2VudGVyJywgICAgICAgICAgICAgICAgICAgLy8gbGVmdCwgY2VudGVyLCByaWdodFxuICAgICAgICAgICAgdmVydGljYWxBbGlnbjogJ21pZGRsZScsICAgICAgICAgICAvLyB0b3AsIG1pZGRsZSwgYm90dG9tXG4gICAgICAgICAgICB2aXNpYmxlOiB0cnVlXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgdGhpcy5idXR0b25zID0gW11cblxuICAgICAgICB0aGlzLl9kaXNhYmxlZCA9IG51bGxcbiAgICAgICAgXG4gICAgICAgIHRoaXMudmlzaWJsZSA9IHRoaXMub3B0cy52aXNpYmxlXG5cbiAgICAgICAgLy8gc2V0dXBcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnNldHVwKClcblxuICAgICAgICAvLyBsYXlvdXRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmxheW91dCgpXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgY2hpbGRyZW4gYW5kIGluc3RhbnRpYXRlcyBldmVyeXRoaW5nLlxuICAgICAqIFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybiB7QnV0dG9uR3JvdXB9IEEgcmVmZXJlbmNlIHRvIHRoZSBidXR0b24gZ3JvdXAgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHNldHVwKCkge1xuXG4gICAgICAgIC8vIEJ1dHRvbnNcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBsZXQgcG9zaXRpb24gPSAwXG5cbiAgICAgICAgZm9yIChsZXQgaXQgb2YgdGhpcy5vcHRzLmJ1dHRvbnMpIHtcblxuICAgICAgICAgICAgZGVsZXRlIGl0LnhcbiAgICAgICAgICAgIGRlbGV0ZSBpdC55XG5cbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMub3JpZW50YXRpb24gPT09ICdob3Jpem9udGFsJykge1xuICAgICAgICAgICAgICAgIGl0LnggPSBwb3NpdGlvblxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpdC55ID0gcG9zaXRpb25cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaXQudGhlbWUgPSBpdC50aGVtZSB8fCB0aGlzLm9wdHMudGhlbWVcbiAgICAgICAgICAgIGl0Lm1pbldpZHRoID0gaXQubWluV2lkdGggfHwgdGhpcy5vcHRzLm1pbldpZHRoXG4gICAgICAgICAgICBpdC5taW5IZWlnaHQgPSBpdC5taW5IZWlnaHQgfHwgdGhpcy5vcHRzLm1pbkhlaWdodFxuICAgICAgICAgICAgaXQucGFkZGluZyA9IGl0LnBhZGRpbmcgfHwgdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgICAgIGl0Lmljb25Qb3NpdGlvbiA9IGl0Lmljb25Qb3NpdGlvbiB8fCB0aGlzLm9wdHMuaWNvblBvc2l0aW9uXG4gICAgICAgICAgICBpdC5pY29uQ29sb3IgPSBpdC5pY29uQ29sb3IgfHwgdGhpcy5vcHRzLmljb25Db2xvclxuICAgICAgICAgICAgaXQuaWNvbkNvbG9yQWN0aXZlID0gaXQuaWNvbkNvbG9yQWN0aXZlIHx8IHRoaXMub3B0cy5pY29uQ29sb3JBY3RpdmVcbiAgICAgICAgICAgIGl0LmZpbGwgPSBpdC5maWxsIHx8IHRoaXMub3B0cy5maWxsXG4gICAgICAgICAgICBpdC5maWxsQWxwaGEgPSBpdC5maWxsQWxwaGEgfHwgdGhpcy5vcHRzLmZpbGxBbHBoYVxuICAgICAgICAgICAgaXQuZmlsbEFjdGl2ZSA9IGl0LmZpbGxBY3RpdmUgfHwgdGhpcy5vcHRzLmZpbGxBY3RpdmVcbiAgICAgICAgICAgIGl0LmZpbGxBY3RpdmVBbHBoYSA9IGl0LmZpbGxBY3RpdmVBbHBoYSB8fCB0aGlzLm9wdHMuZmlsbEFjdGl2ZUFscGhhXG4gICAgICAgICAgICBpdC5zdHJva2UgPSBpdC5zdHJva2UgfHwgdGhpcy5vcHRzLnN0cm9rZVxuICAgICAgICAgICAgaXQuc3Ryb2tlV2lkdGggPSBpdC5zdHJva2VXaWR0aCAhPSBudWxsID8gaXQuc3Ryb2tlV2lkdGggOiB0aGlzLm9wdHMuc3Ryb2tlV2lkdGhcbiAgICAgICAgICAgIGl0LnN0cm9rZUFscGhhID0gaXQuc3Ryb2tlQWxwaGEgIT0gbnVsbCA/IGl0LnN0cm9rZUFscGhhIDogdGhpcy5vcHRzLnN0cm9rZUFscGhhXG4gICAgICAgICAgICBpdC5zdHJva2VBY3RpdmUgPSBpdC5zdHJva2VBY3RpdmUgfHwgdGhpcy5vcHRzLnN0cm9rZUFjdGl2ZVxuICAgICAgICAgICAgaXQuc3Ryb2tlQWN0aXZlV2lkdGggPSBpdC5zdHJva2VBY3RpdmVXaWR0aCAhPSBudWxsID8gaXQuc3Ryb2tlQWN0aXZlV2lkdGggOiB0aGlzLm9wdHMuc3Ryb2tlQWN0aXZlV2lkdGhcbiAgICAgICAgICAgIGl0LnN0cm9rZUFjdGl2ZUFscGhhID0gaXQuc3Ryb2tlQWN0aXZlQWxwaGEgIT0gbnVsbCA/IGl0LnN0cm9rZUFjdGl2ZUFscGhhIDogdGhpcy5vcHRzLnN0cm9rZUFjdGl2ZUFscGhhXG4gICAgICAgICAgICBpdC50ZXh0U3R5bGUgPSBpdC50ZXh0U3R5bGUgfHwgdGhpcy5vcHRzLnRleHRTdHlsZVxuICAgICAgICAgICAgaXQudGV4dFN0eWxlQWN0aXZlID0gaXQudGV4dFN0eWxlQWN0aXZlIHx8IHRoaXMub3B0cy50ZXh0U3R5bGVBY3RpdmVcbiAgICAgICAgICAgIGl0LnN0eWxlID0gaXQuc3R5bGUgfHwgdGhpcy5vcHRzLnN0eWxlXG4gICAgICAgICAgICBpdC5yYWRpdXMgPSBpdC5yYWRpdXMgIT0gbnVsbCA/IGl0LnJhZGl1cyA6IHRoaXMub3B0cy5yYWRpdXNcbiAgICAgICAgICAgIGlmICghaXQudHlwZSkge1xuICAgICAgICAgICAgICAgIHN3aXRjaCAodGhpcy5vcHRzLnR5cGUpIHtcbiAgICAgICAgICAgICAgICAgICAgY2FzZSAnY2hlY2tib3gnOlxuICAgICAgICAgICAgICAgICAgICAgICAgaXQudHlwZSA9IHRoaXMub3B0cy50eXBlXG4gICAgICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICAgICAgaXQudHlwZSA9ICdkZWZhdWx0J1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvL2l0LnR5cGUgPSBpdC50eXBlIHx8IHRoaXMub3B0cy50eXBlIHx8ICdkZWZhdWx0J1xuICAgICAgICAgICAgaXQuYWxpZ24gPSBpdC5hbGlnbiB8fCB0aGlzLm9wdHMuYWxpZ25cbiAgICAgICAgICAgIGl0LnZlcnRpY2FsQWxpZ24gPSBpdC52ZXJ0aWNhbEFsaWduIHx8IHRoaXMub3B0cy52ZXJ0aWNhbEFsaWduXG4gICAgICAgICAgICBpdC5hZnRlckFjdGlvbiA9IChldmVudCwgYnV0dG9uKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMub3B0cy50eXBlID09PSAncmFkaW8nICYmIGJ1dHRvbi5vcHRzLnR5cGUgPT09ICdkZWZhdWx0Jykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmJ1dHRvbnMuZm9yRWFjaChpdCA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXQub3B0cy50eXBlID09PSAnZGVmYXVsdCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpdC5hY3RpdmUgPSBmYWxzZVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KVxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChidXR0b24ub3B0cy50eXBlID09PSAnZGVmYXVsdCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJ1dHRvbi5hY3RpdmUgPSB0cnVlXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChpdC50b29sdGlwKSB7XG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBpdC50b29sdGlwID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgICAgICAgICBpdC50b29sdGlwID0ge2NvbnRlbnQ6IGl0LnRvb2x0aXAsIGNvbnRhaW5lcjogdGhpc31cbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBpdC50b29sdGlwID0gT2JqZWN0LmFzc2lnbih7fSwge2NvbnRhaW5lcjogdGhpc30sIGl0LnRvb2x0aXApXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICBsZXQgYnV0dG9uID0gbmV3IEJ1dHRvbihpdClcblxuICAgICAgICAgICAgdGhpcy5hZGRDaGlsZChidXR0b24pXG4gICAgICAgICAgICB0aGlzLmJ1dHRvbnMucHVzaChidXR0b24pXG5cbiAgICAgICAgICAgIHBvc2l0aW9uICs9ICh0aGlzLm9wdHMub3JpZW50YXRpb24gPT09ICdob3Jpem9udGFsJyA/IGJ1dHRvbi53aWR0aCA6IGJ1dHRvbi5oZWlnaHQpICsgdGhpcy5vcHRzLm1hcmdpblxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0cy5vcmllbnRhdGlvbiA9PT0gJ3ZlcnRpY2FsJykge1xuICAgICAgICAgICAgY29uc3QgbWF4V2lkdGggPSB0aGlzLmdldE1heEJ1dHRvbldpZHRoKClcblxuICAgICAgICAgICAgdGhpcy5idXR0b25zLmZvckVhY2goaXQgPT4ge1xuICAgICAgICAgICAgICAgIGl0Lm9wdHMubWluV2lkdGggPSBtYXhXaWR0aFxuICAgICAgICAgICAgICAgIGl0LmxheW91dCgpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gZGlzYWJsZWRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLmRpc2FibGVkICE9IG51bGwpIHtcbiAgICAgICAgICAgIHRoaXMuZGlzYWJsZWQgPSB0aGlzLm9wdHMuZGlzYWJsZWRcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFNob3VsZCBiZSBjYWxsZWQgdG8gcmVmcmVzaCB0aGUgbGF5b3V0IG9mIHRoZSBidXR0b24gZ3JvdXAuIENhbiBiZSB1c2VkIGFmdGVyIHJlc2l6aW5nLlxuICAgICAqIFxuICAgICAqIEByZXR1cm4ge0J1dHRvbkdyb3VwfSBBIHJlZmVyZW5jZSB0byB0aGUgYnV0dG9uIGdyb3VwIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBsYXlvdXQoKSB7XG4gICAgICAgIFxuICAgICAgICAvLyBzZXQgcG9zaXRpb25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnBvc2l0aW9uLnNldCh0aGlzLm9wdHMueCwgdGhpcy5vcHRzLnkpXG5cbiAgICAgICAgLy8gZHJhd1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuZHJhdygpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogRHJhd3MgdGhlIGNhbnZhcy5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge0J1dHRvbkdyb3VwfSBBIHJlZmVyZW5jZSB0byB0aGUgYnV0dG9uIGdyb3VwIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBkcmF3KCkge1xuXG4gICAgICAgIGlmICh0aGlzLm9wdHMubWFyZ2luID09PSAwKSB7XG5cbiAgICAgICAgICAgIHRoaXMuYnV0dG9ucy5mb3JFYWNoKGl0ID0+IGl0LmhpZGUoKSlcblxuICAgICAgICAgICAgdGhpcy5jbGVhcigpXG4gICAgICAgICAgICB0aGlzLmxpbmVTdHlsZSh0aGlzLm9wdHMuc3Ryb2tlV2lkdGgsIHRoaXMub3B0cy5zdHJva2UsIHRoaXMub3B0cy5zdHJva2VBbHBoYSlcbiAgICAgICAgICAgIHRoaXMuYmVnaW5GaWxsKHRoaXMub3B0cy5maWxsLCB0aGlzLm9wdHMuZmlsbEFscGhhKVxuICAgICAgICAgICAgdGhpcy5kcmF3Um91bmRlZFJlY3QoMCwgMCwgdGhpcy53aWR0aCwgdGhpcy5oZWlnaHQsIHRoaXMub3B0cy5yYWRpdXMpXG5cbiAgICAgICAgICAgIC8vIERyYXcgYm9yZGVyc1xuICAgICAgICAgICAgdGhpcy5saW5lU3R5bGUodGhpcy5vcHRzLnN0cm9rZVdpZHRoLCB0aGlzLm9wdHMuc3Ryb2tlLCB0aGlzLm9wdHMuc3Ryb2tlQWxwaGEgLyAyKVxuXG4gICAgICAgICAgICB0aGlzLmJ1dHRvbnMuZm9yRWFjaCgoaXQsIGkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoaSA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5tb3ZlVG8oaXQueCwgaXQueSlcblxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5vcHRzLm9yaWVudGF0aW9uID09PSAnaG9yaXpvbnRhbCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubGluZVRvKGl0LngsIGl0LmhlaWdodClcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubGluZVRvKGl0LndpZHRoLCBpdC55KVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgIHRoaXMuZW5kRmlsbCgpXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBHZXRzIG9yIHNldHMgdGhlIGRpc2FibGVkIHN0YXRlLiBXaGVuIGRpc2FibGVkLCBubyBidXR0b24gb2YgdGhlIGJ1dHRvbiBncm91cCBjYW4gYmUgY2xpY2tlZC5cbiAgICAgKiBcbiAgICAgKiBAbWVtYmVyIHtib29sZWFufVxuICAgICAqL1xuICAgIGdldCBkaXNhYmxlZCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2Rpc2FibGVkXG4gICAgfVxuXG4gICAgc2V0IGRpc2FibGVkKHZhbHVlKSB7XG5cbiAgICAgICAgdGhpcy5fZGlzYWJsZWQgPSB2YWx1ZVxuXG4gICAgICAgIHRoaXMuYnV0dG9ucy5mb3JFYWNoKGl0ID0+IGl0LmRpc2FibGVkID0gdmFsdWUpXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFNlYXJjaGVzIGFsbCBidXR0b25zIG9mIHRoZSBidXR0b24gZ3JvdXAgYW5kIHJldHVybnMgdGhlIG1heGltdW0gd2lkdGggb2Ygb25lIGJ1dHRvbi5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge251bWJlcn0gVGhlIG1heGltdW0gd2l0aCBvZiBhIGJ1dHRvbiBvZiB0aGUgYnV0dG9uIGdyb3VwLlxuICAgICAqL1xuICAgIGdldE1heEJ1dHRvbldpZHRoKCkge1xuXG4gICAgICAgIGxldCB3aWR0aHMgPSB0aGlzLmJ1dHRvbnMubWFwKGl0ID0+IGl0LndpZHRoKVxuXG4gICAgICAgIHJldHVybiBNYXRoLm1heCguLi53aWR0aHMpXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFNob3dzIHRoZSBidXR0b24gZ3JvdXAgKHNldHMgaGlzIGFscGhhIHZhbHVlIHRvIDEpLlxuICAgICAqIFxuICAgICAqIEByZXR1cm4ge0J1dHRvbkdyb3VwfSBBIHJlZmVyZW5jZSB0byB0aGUgYnV0dG9uIGdyb3VwIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzaG93KCkge1xuXG4gICAgICAgIHRoaXMuYWxwaGEgPSAxXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBIaWRlcyB0aGUgYnV0dG9uIGdyb3VwIChzZXRzIGhpcyBhbHBoYSB2YWx1ZSB0byAwKS5cbiAgICAgKiBcbiAgICAgKiBAcmV0dXJuIHtCdXR0b25Hcm91cH0gQSByZWZlcmVuY2UgdG8gdGhlIGJ1dHRvbiBncm91cCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgaGlkZSgpIHtcblxuICAgICAgICB0aGlzLmFscGhhID0gMFxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxufVxuIiwiaW1wb3J0IEFic3RyYWN0UG9wdXAgZnJvbSAnLi9hYnN0cmFjdHBvcHVwLmpzJ1xuaW1wb3J0IEJ1dHRvbiBmcm9tICcuL2J1dHRvbi5qcydcbmltcG9ydCBCdXR0b25Hcm91cCBmcm9tICcuL2J1dHRvbmdyb3VwLmpzJ1xuXG4vKipcbiAqIENsYXNzIHRoYXQgcmVwcmVzZW50cyBhIFBpeGlKUyBJbnRlcmFjdGl2ZVBvcHVwLlxuICogVGhlIGNsYXNzIGlzIHVzZWQgZm9yIHZhcmlvdXMgb3RoZXIgUG9wdXAtbGlrZSBjbGFzc2VzXG4gKiBsaWtlIFBvcHVwLCBNZXNzYWdlLi4uXG4gKlxuICogQGNsYXNzXG4gKiBAYWJzdHJhY3RcbiAqIEBleHRlbmRzIEFic3RyYWN0UG9wdXBcbiAqL1xuZXhwb3J0IGNsYXNzIEludGVyYWN0aXZlUG9wdXAgZXh0ZW5kcyBBYnN0cmFjdFBvcHVwIHtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYW4gSW50ZXJhY3RpdmVQb3B1cCAob25seSBmb3IgaW50ZXJuYWwgdXNlKS5cbiAgICAgKlxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0c10gLSBBbiBvcHRpb25zIG9iamVjdCB0byBzcGVjaWZ5IHRvIHN0eWxlIGFuZCBiZWhhdmlvdXIgb2YgdGhlIHBvcHVwLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuY2xvc2VPblBvcHVwPWZhbHNlXSAtIFNob3VsZCB0aGUgcG9wdXAgYmUgY2xvc2VkIHdoZW4gdGhlIHVzZXIgY2xpY2tzIG9uIHRoZSBwb3B1cD9cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmNsb3NlQnV0dG9uPXRydWVdIC0gU2hvdWxkIGEgY2xvc2UgYnV0dG9uIGJlIGRpc3BsYXllZCBvbiB0aGUgdXBwZXIgcmlnaHQgY29ybmVyP1xuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0cy5idXR0b25dIC0gQSBCdXR0b24gb2JqZWN0IHRvIGJlIGRpc3BsYXkgb24gdGhlIGxvd2VyIHJpZ2h0IGNvcm5lci5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHMuYnV0dG9uR3JvdXBdIC0gQSBCdXR0b25Hcm91cCBvYmplY3QgdG8gYmUgZGlzcGxheWVkIG9uIHRoZSBsb3dlciByaWdodCBjb3JuZXIuXG4gICAgICovXG4gICAgY29uc3RydWN0b3Iob3B0cyA9IHt9KSB7XG5cbiAgICAgICAgb3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIHtcbiAgICAgICAgICAgIGNsb3NlT25Qb3B1cDogZmFsc2UsXG4gICAgICAgICAgICBjbG9zZUJ1dHRvbjogdHJ1ZSxcbiAgICAgICAgICAgIGJ1dHRvbjogbnVsbCxcbiAgICAgICAgICAgIGJ1dHRvbkdyb3VwOiBudWxsXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgc3VwZXIob3B0cylcblxuICAgICAgICB0aGlzLl9jbG9zZUJ1dHRvbiA9IG51bGxcbiAgICAgICAgdGhpcy5fYnV0dG9ucyA9IG51bGxcblxuICAgICAgICAvLyBwYWRkaW5nXG4gICAgICAgIHRoaXMuc21hbGxQYWRkaW5nID0gdGhpcy5vcHRzLnBhZGRpbmcgLyAyXG5cbiAgICAgICAgLy8gc2V0dXBcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnNldHVwKClcblxuICAgICAgICAvLyBsYXlvdXRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmxheW91dCgpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyB0aGUgZnJhbWV3b3JrIGFuZCBpbnN0YW50aWF0ZXMgZXZlcnl0aGluZy5cbiAgICAgKlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybiB7QWJzdHJhY3RQb3B1cH0gQSByZWZlcmVuY2UgdG8gdGhlIHBvcHVwIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzZXR1cCgpIHtcblxuICAgICAgICBzdXBlci5zZXR1cCgpXG5cbiAgICAgICAgLy8gaW50ZXJhY3Rpb25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLm9uKCdwb2ludGVydXAnLCBlID0+IHtcbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuY2xvc2VPblBvcHVwKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5oaWRlKClcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuXG4gICAgICAgIC8vIGNsb3NlQnV0dG9uXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgaWYgKHRoaXMub3B0cy5jbG9zZUJ1dHRvbikge1xuICAgICAgICAgICAgbGV0IGNsb3NlQnV0dG9uID0gUElYSS5TcHJpdGUuZnJvbUltYWdlKCcuLi8uLi9hc3NldHMvaWNvbnMvcG5nL2ZsYXQvY2xvc2UucG5nJywgdHJ1ZSlcbiAgICAgICAgICAgIGNsb3NlQnV0dG9uLndpZHRoID0gdGhpcy5oZWFkZXJTdHlsZS5mb250U2l6ZVxuICAgICAgICAgICAgY2xvc2VCdXR0b24uaGVpZ2h0ID0gY2xvc2VCdXR0b24ud2lkdGhcbiAgICAgICAgICAgIGNsb3NlQnV0dG9uLnRpbnQgPSB0aGlzLnRoZW1lLmNvbG9yMlxuICAgICAgICAgICAgLy8gVGhpcyBpcyBuZWVkZWQsIGJlY2F1c2UgdGhlIGNsb3NlQnV0dG9uIGJlbG9uZ3MgdG8gdGhlIGNvbnRlbnQuIFRoZSBwb3B1cCBtdXN0IHJlc2l6ZSB3aXRoIHRoZSBjbG9zZUJ1dHRvbi5cbiAgICAgICAgICAgIGlmICh0aGlzLl9oZWFkZXIpIHtcbiAgICAgICAgICAgICAgICBjbG9zZUJ1dHRvbi54ID0gdGhpcy5faGVhZGVyLndpZHRoICsgdGhpcy5pbm5lclBhZGRpbmdcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5fY29udGVudCkge1xuICAgICAgICAgICAgICAgIGNsb3NlQnV0dG9uLnggPSB0aGlzLl9jb250ZW50LndpZHRoICsgdGhpcy5pbm5lclBhZGRpbmdcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY2xvc2VCdXR0b24uaW50ZXJhY3RpdmUgPSB0cnVlXG4gICAgICAgICAgICBjbG9zZUJ1dHRvbi5idXR0b25Nb2RlID0gdHJ1ZVxuICAgICAgICAgICAgY2xvc2VCdXR0b24ub24oJ3BvaW50ZXJkb3duJywgZSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5oaWRlKClcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgIHRoaXMuX2Nsb3NlQnV0dG9uID0gY2xvc2VCdXR0b25cbiAgICAgICAgICAgIHRoaXMuYWRkQ2hpbGQoY2xvc2VCdXR0b24pXG5cbiAgICAgICAgICAgIC8vIG1heFdpZHRoIGlzIHNldCBhbmQgYSBjbG9zZUJ1dHRvbiBzaG91bGQgYmUgZGlzcGxheWVkXG4gICAgICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgICAgICBpZiAodGhpcy5vcHRzLm1heFdpZHRoKSB7XG4gICAgICAgICAgICAgICAgY29uc3Qgd29yZFdyYXBXaWR0aCA9IHRoaXMub3B0cy5tYXhXaWR0aCAtICgyICogdGhpcy5vcHRzLnBhZGRpbmcpIC0gdGhpcy5zbWFsbFBhZGRpbmcgLSB0aGlzLl9jbG9zZUJ1dHRvbi53aWR0aFxuICAgICAgICAgICAgICAgIGlmICh0aGlzLl9oZWFkZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5oZWFkZXJTdHlsZS53b3JkV3JhcFdpZHRoID0gd29yZFdyYXBXaWR0aFxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5fY29udGVudCkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnRleHRTdHlsZS53b3JkV3JhcFdpZHRoID0gd29yZFdyYXBXaWR0aFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGJ1dHRvbnNcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLmJ1dHRvbiB8fCB0aGlzLm9wdHMuYnV0dG9uR3JvdXApIHtcbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuYnV0dG9uKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fYnV0dG9ucyA9IG5ldyBCdXR0b24oT2JqZWN0LmFzc2lnbih7dGV4dFN0eWxlOiB0aGlzLnRoZW1lLnRleHRTdHlsZVNtYWxsfSwgdGhpcy5vcHRzLmJ1dHRvbikpXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMuX2J1dHRvbnMgPSBuZXcgQnV0dG9uR3JvdXAoT2JqZWN0LmFzc2lnbih7dGV4dFN0eWxlOiB0aGlzLnRoZW1lLnRleHRTdHlsZVNtYWxsfSwgdGhpcy5vcHRzLmJ1dHRvbkdyb3VwKSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuYWRkQ2hpbGQodGhpcy5fYnV0dG9ucylcblxuICAgICAgICAgICAgdGhpcy5fYnV0dG9ucy55ID0gdGhpcy5pbm5lclBhZGRpbmcgKyB0aGlzLnN5XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNob3VsZCBiZSBjYWxsZWQgdG8gcmVmcmVzaCB0aGUgbGF5b3V0IG9mIHRoZSBwb3B1cC4gQ2FuIGJlIHVzZWQgYWZ0ZXIgcmVzaXppbmcuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtBYnN0cmFjdFBvcHVwfSBBIHJlZmVyZW5jZSB0byB0aGUgcG9wdXAgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGxheW91dCgpIHtcblxuICAgICAgICBzdXBlci5sYXlvdXQoKVxuICAgICAgICBcbiAgICAgICAgLy8gY2xvc2VCdXR0b25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLmNsb3NlQnV0dG9uKSB7XG4gICAgICAgICAgICB0aGlzLl9jbG9zZUJ1dHRvbi54ID0gdGhpcy53YW50ZWRXaWR0aCAtIHRoaXMuc21hbGxQYWRkaW5nIC0gdGhpcy5fY2xvc2VCdXR0b24ud2lkdGhcbiAgICAgICAgICAgIHRoaXMuX2Nsb3NlQnV0dG9uLnkgPSB0aGlzLnNtYWxsUGFkZGluZ1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gYnV0dG9uc1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGlmICh0aGlzLl9idXR0b25zKSB7XG4gICAgICAgICAgICB0aGlzLl9idXR0b25zLnggPSB0aGlzLndhbnRlZFdpZHRoIC0gdGhpcy5vcHRzLnBhZGRpbmcgLSB0aGlzLl9idXR0b25zLndpZHRoXG4gICAgICAgICAgICB0aGlzLl9idXR0b25zLnkgPSB0aGlzLndhbnRlZEhlaWdodCAtIHRoaXMub3B0cy5wYWRkaW5nIC0gdGhpcy5fYnV0dG9ucy5oZWlnaHRcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2FsY3VsYXRlcyB0aGUgc2l6ZSBvZiB0aGUgY2hpbGRyZW4gb2YgdGhlIEFic3RyYWN0UG9wdXAuXG4gICAgICogQ2Fubm90IHVzZSBnZXRCb3VuZHMoKSBiZWNhdXNlIGl0IGlzIG5vdCB1cGRhdGVkIHdoZW4gY2hpbGRyZW5cbiAgICAgKiBhcmUgcmVtb3ZlZC5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBvdmVycmlkZVxuICAgICAqIEByZXR1cm5zIHtvYmplY3R9IEFuIEphdmFTY3JpcHQgb2JqZWN0IHdpZHRoIHRoZSBrZXlzIHdpZHRoIGFuZCBoZWlnaHQuXG4gICAgICovXG4gICAgZ2V0SW5uZXJTaXplKCkge1xuXG4gICAgICAgIGxldCBzaXplID0gc3VwZXIuZ2V0SW5uZXJTaXplKClcblxuICAgICAgICBpZiAodGhpcy5fY2xvc2VCdXR0b24pIHtcbiAgICAgICAgICAgIHNpemUud2lkdGggKz0gdGhpcy5zbWFsbFBhZGRpbmcgKyB0aGlzLl9jbG9zZUJ1dHRvbi53aWR0aFxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuX2J1dHRvbnMpIHtcbiAgICAgICAgICAgIHNpemUud2lkdGggPSBNYXRoLm1heChzaXplLndpZHRoLCB0aGlzLl9idXR0b25zLnggKyB0aGlzLl9idXR0b25zLndpZHRoKVxuICAgICAgICAgICAgc2l6ZS5oZWlnaHQgKz0gdGhpcy5pbm5lclBhZGRpbmcgKyB0aGlzLl9idXR0b25zLmhlaWdodFxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHNpemVcbiAgICB9XG59XG5cbi8qKlxuICogQ2xhc3MgdGhhdCByZXByZXNlbnRzIGEgUGl4aUpTIFBvcHVwLlxuICpcbiAqIEBleGFtcGxlXG4gKiAvLyBDcmVhdGUgdGhlIHBvcHVwXG4gKiBjb25zdCBwb3B1cCA9IG5ldyBQb3B1cCh7XG4gKiAgICAgaGVhZGVyOiAnR29ldGhlJyxcbiAqICAgICBjb250ZW50OiAnTWFuIGthbm4gZGllIEVyZmFocnVuZyBuaWNodCBmcsO8aCBnZW51ZyBtYWNoZW4sIHdpZSBlbnRiZWhybGljaCBtYW4gaW4gZGVyIFdlbHQgaXN0LidcbiAqIH0pXG4gKlxuICogLy8gQWRkIHRoZSBwb3B1cCB0byBhIERpc3BsYXlPYmplY3RcbiAqIGFwcC5zY2VuZS5hZGRDaGlsZChwb3B1cClcbiAqXG4gKiBAY2xhc3NcbiAqIEBleHRlbmRzIEludGVyYWN0aXZlUG9wdXBcbiAqIEBzZWUge0BsaW5rIGh0dHBzOi8vd3d3Lml3bS10dWViaW5nZW4uZGUvaXdtYnJvd3Nlci9saWIvcGl4aS9wb3B1cC5odG1sfERvY1Rlc3R9XG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFBvcHVwIGV4dGVuZHMgSW50ZXJhY3RpdmVQb3B1cCB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIGEgUG9wdXAuXG4gICAgICpcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0byBzdHlsZSBhbmQgYmVoYXZpb3VyIG9mIHRoZSBwb3B1cC5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmNsb3NlQnV0dG9uPWZhbHNlXSAtIFNob3VsZCBhIGNsb3NlIGJ1dHRvbiBiZSBkaXNwbGF5ZWQgb24gdGhlIHVwcGVyIHJpZ2h0IGNvcm5lcj9cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMubWluV2lkdGg9MF0gLSBUaGUgbWluaW11bSB3aWR0aCBvZiB0aGUgcG9wdXAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLm1pbkhlaWdodD0wXSAtIFRoZSBtaW5pbXVtIGhlaWdodCBvZiB0aGUgcG9wdXAuXG4gICAgICovXG4gICAgY29uc3RydWN0b3Iob3B0cyA9IHt9KSB7XG5cbiAgICAgICAgb3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIHtcbiAgICAgICAgICAgIGNsb3NlQnV0dG9uOiBmYWxzZSxcbiAgICAgICAgICAgIG1pbldpZHRoOiAwLFxuICAgICAgICAgICAgbWluSGVpZ2h0OiAwXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgc3VwZXIob3B0cylcbiAgICB9XG59XG4iLCJpbXBvcnQgVGhlbWUgZnJvbSAnLi90aGVtZS5qcydcbmltcG9ydCB7SW50ZXJhY3RpdmVQb3B1cH0gZnJvbSAnLi9wb3B1cC5qcydcblxuLyoqXG4gKiBDbGFzcyB0aGF0IHJlcHJlc2VudHMgYSBQaXhpSlMgTW9kYWwuXG4gKiBcbiAqIEBleGFtcGxlXG4gKiAvLyBDcmVhdGUgdGhlIGJ1dHRvbiBhbmQgdGhlIG1vZGFsIHdoZW4gY2xpY2tlZFxuICogY29uc3QgYnV0dG9uID0gbmV3IEJ1dHRvbih7XG4gKiAgICAgbGFiZWw6ICdTaG93IE1vZGFsJyxcbiAqICAgICBhY3Rpb246IGUgPT4ge1xuICogICAgICAgICBjb25zdCBtb2RhbCA9IG5ldyBNb2RhbCh7XG4gKiAgICAgICAgICAgICBhcHA6IGFwcCxcbiAqICAgICAgICAgICAgIGhlYWRlcjogJ1RoaXMgaXMgdGhlIGhlYWRlcicsXG4gKiAgICAgICAgICAgICBjb250ZW50OiAnVGhpcyBpcyB0aGUgdGV4dC4nXG4gKiAgICAgICAgIH0pXG4gKiAgICAgICAgIGFwcC5zY2VuZS5hZGRDaGlsZChtb2RhbClcbiAqICAgICB9XG4gKiB9KVxuICpcbiAqIC8vIEFkZCB0aGUgYnV0dG9uIHRvIGEgRGlzcGxheU9iamVjdFxuICogYXBwLnNjZW5lLmFkZENoaWxkKGJ1dHRvbilcbiAqXG4gKiBAY2xhc3NcbiAqIEBleHRlbmRzIFBJWEkuQ29udGFpbmVyXG4gKiBAZXh0ZW5kcyBJbnRlcmFjdGl2ZVBvcHVwXG4gKiBAc2VlIHtAbGluayBodHRwOi8vcGl4aWpzLmRvd25sb2FkL2Rldi9kb2NzL1BJWEkuQ29udGFpbmVyLmh0bWx8UElYSS5Db250YWluZXJ9XG4gKiBAc2VlIHtAbGluayBodHRwczovL3d3dy5pd20tdHVlYmluZ2VuLmRlL2l3bWJyb3dzZXIvbGliL3BpeGkvbW9kYWwuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNb2RhbCBleHRlbmRzIFBJWEkuQ29udGFpbmVyIHtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYSBNb2RhbC5cbiAgICAgKiBcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0byBzdHlsZSBhbmQgYmVoYXZpb3VyIG9mIHRoZSBtb2RhbC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuaWQ9YXV0byBnZW5lcmF0ZWRdIC0gVGhlIGlkIG9mIHRoZSBtb2RhbC5cbiAgICAgKiBAcGFyYW0ge1BJWElBcHB9IFtvcHRzLmFwcD13aW5kb3cuYXBwXSAtIFRoZSBhcHAgd2hlcmUgdGhlIG1vZGFsIGJlbG9uZ3MgdG8uXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmJhY2tncm91bmRGaWxsPVRoZW1lLmJhY2tncm91bmRdIC0gVGhlIGNvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5iYWNrZ3JvdW5kRmlsbEFscGhhPTAuNl0gLSBUaGUgb3BhY2l0eSBvZiB0aGUgYmFja2dyb3VuZC5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmNsb3NlT25CYWNrZ3JvdW5kPXRydWVdIC0gU2hvdWxkIHRoZSBtb2RhbCBiZSBjbG9zZWQgd2hlbiB0aGUgdXNlciBjbGlja3MgdGhlXG4gICAgICogICAgIGJhY2tncm91bmQ/XG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy52aXNpYmxlPXRydWVdIC0gSXMgdGhlIG1vZGFsIGluaXRpYWxseSB2aXNpYmxlIChwcm9wZXJ0eSB2aXNpYmxlKT9cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihvcHRzID0ge30pIHtcblxuICAgICAgICBzdXBlcigpXG4gICAgICAgIFxuICAgICAgICBjb25zdCB0aGVtZSA9IFRoZW1lLmZyb21TdHJpbmcob3B0cy50aGVtZSlcbiAgICAgICAgdGhpcy50aGVtZSA9IHRoZW1lXG5cbiAgICAgICAgdGhpcy5vcHRzID0gT2JqZWN0LmFzc2lnbih7fSwge1xuICAgICAgICAgICAgaWQ6IFBJWEkudXRpbHMudWlkKCksXG4gICAgICAgICAgICBhcHA6IHdpbmRvdy5hcHAsXG4gICAgICAgICAgICBiYWNrZ3JvdW5kRmlsbDogdGhlbWUuYmFja2dyb3VuZCxcbiAgICAgICAgICAgIGJhY2tncm91bmRGaWxsQWxwaGE6IC42LFxuICAgICAgICAgICAgY2xvc2VPbkJhY2tncm91bmQ6IHRydWUsXG4gICAgICAgICAgICB2aXNpYmxlOiB0cnVlXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgdGhpcy5pZCA9IHRoaXMub3B0cy5pZFxuXG4gICAgICAgIHRoaXMuYmFja2dyb3VuZCA9IG51bGxcbiAgICAgICAgdGhpcy5wb3B1cCA9IG51bGxcblxuICAgICAgICB0aGlzLmFscGhhID0gMFxuICAgICAgICB0aGlzLnZpc2libGUgPSB0aGlzLm9wdHMudmlzaWJsZVxuXG4gICAgICAgIC8vIHNldHVwXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5zZXR1cCgpXG5cbiAgICAgICAgLy8gbGF5b3V0XG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5sYXlvdXQoKVxuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGNoaWxkcmVuIGFuZCBpbnN0YW50aWF0ZXMgZXZlcnl0aGluZy5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge01vZGFsfSBBIHJlZmVyZW5jZSB0byB0aGUgbW9kYWwgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHNldHVwKCkge1xuXG4gICAgICAgIC8vIGludGVyYWN0aW9uXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5pbnRlcmFjdGl2ZSA9IHRydWVcbiAgICAgICAgdGhpcy5vbignYWRkZWQnLCBlID0+IHtcbiAgICAgICAgICAgIGlmICh0aGlzLnZpc2libGUpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnNob3coKVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuXG4gICAgICAgIC8vIGJhY2tncm91bmRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBsZXQgYmFja2dyb3VuZCA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAgICAgICAgdGhpcy5iYWNrZ3JvdW5kID0gYmFja2dyb3VuZFxuICAgICAgICB0aGlzLmFkZENoaWxkKHRoaXMuYmFja2dyb3VuZClcblxuICAgICAgICBpZiAodGhpcy5vcHRzLmNsb3NlT25CYWNrZ3JvdW5kKSB7XG4gICAgICAgICAgICBiYWNrZ3JvdW5kLmludGVyYWN0aXZlID0gdHJ1ZVxuICAgICAgICAgICAgYmFja2dyb3VuZC5vbigncG9pbnRlcnVwJywgZSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5oaWRlKClcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICAvLyBwb3B1cFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGNvbnN0IHBvcHVwT3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIHRoaXMub3B0cywge1xuICAgICAgICAgICAgdmlzaWJsZTogdHJ1ZSxcbiAgICAgICAgICAgIG9uSGlkZGVuOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5oaWRlKClcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgbGV0IHBvcHVwID0gbmV3IEludGVyYWN0aXZlUG9wdXAocG9wdXBPcHRzKVxuICAgICAgICB0aGlzLnBvcHVwID0gcG9wdXBcbiAgICAgICAgdGhpcy5hZGRDaGlsZChwb3B1cClcbiAgICAgICAgcG9wdXAuc2hvdygpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogU2hvdWxkIGJlIGNhbGxlZCB0byByZWZyZXNoIHRoZSBsYXlvdXQgb2YgdGhlIG1vZGFsLiBDYW4gYmUgdXNlZCBhZnRlciByZXNpemluZy5cbiAgICAgKiBcbiAgICAgKiBAcmV0dXJuIHtNb2RhbH0gQSByZWZlcmVuY2UgdG8gdGhlIG1vZGFsIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBsYXlvdXQoKSB7XG5cbiAgICAgICAgY29uc3Qgd2lkdGggPSB0aGlzLm9wdHMuYXBwLnNpemUud2lkdGhcbiAgICAgICAgY29uc3QgaGVpZ2h0ID0gdGhpcy5vcHRzLmFwcC5zaXplLmhlaWdodFxuXG4gICAgICAgIC8vIGJhY2tncm91bmRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmJhY2tncm91bmQuY2xlYXIoKVxuICAgICAgICB0aGlzLmJhY2tncm91bmQuYmVnaW5GaWxsKHRoaXMub3B0cy5iYWNrZ3JvdW5kRmlsbCwgdGhpcy5vcHRzLmJhY2tncm91bmRGaWxsQWxwaGEpXG4gICAgICAgIHRoaXMuYmFja2dyb3VuZC5kcmF3UmVjdCgwLCAwLCB3aWR0aCwgaGVpZ2h0KVxuICAgICAgICB0aGlzLmJhY2tncm91bmQuZW5kRmlsbCgpXG5cbiAgICAgICAgLy8gcG9zaXRpb25cbiAgICAgICAgdGhpcy5wb3B1cC54ID0gd2lkdGggLyAyIC0gdGhpcy5wb3B1cC53aWR0aCAvIDJcbiAgICAgICAgdGhpcy5wb3B1cC55ID0gaGVpZ2h0IC8gMiAtIHRoaXMucG9wdXAuaGVpZ2h0IC8gMlxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFNob3dzIHRoZSBtb2RhbCAoc2V0cyBoaXMgYWxwaGEgdmFsdWVzIHRvIDEpLlxuICAgICAqIFxuICAgICAqIEByZXR1cm4ge01vZGFsfSBBIHJlZmVyZW5jZSB0byB0aGUgbW9kYWwgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHNob3coKSB7XG4gICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLCB0aGlzLnRoZW1lLmZhc3QsIHthbHBoYTogMSwgb25TdGFydDogKCkgPT4gdGhpcy52aXNpYmxlID0gdHJ1ZX0pXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogSGlkZXMgdGhlIG1vZGFsIChzZXRzIGhpcyBhbHBoYSB2YWx1ZXMgdG8gMCkuXG4gICAgICogXG4gICAgICogQHJldHVybiB7TW9kYWx9IEEgcmVmZXJlbmNlIHRvIHRoZSBtb2RhbCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgaGlkZSgpIHtcbiAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMsIHRoaXMudGhlbWUuZmFzdCwge2FscGhhOiAwLCBvbkNvbXBsZXRlOiAoKSA9PiB0aGlzLnZpc2libGUgPSBmYWxzZX0pXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogU2V0cyBvciBnZXRzIHRoZSBoZWFkZXIuIFRoZSBnZXR0ZXIgYWx3YXlzIHJldHVybnMgYSBQSVhJLlRleHQgb2JqZWN0LiBUaGUgc2V0dGVyIGNhbiByZWNlaXZlXG4gICAgICogYSBzdHJpbmcgb3IgYSBQSVhJLlRleHQgb2JqZWN0LlxuICAgICAqIFxuICAgICAqIEBtZW1iZXIge3N0cmluZ3xQSVhJLlRleHR9XG4gICAgICovXG4gICAgZ2V0IGhlYWRlcigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucG9wdXAuaGVhZGVyXG4gICAgfVxuICAgIHNldCBoZWFkZXIodmFsdWUpIHtcbiAgICAgICAgdGhpcy5vcHRzLmhlYWRlciA9IHZhbHVlXG4gICAgICAgIHRoaXMuYmFja2dyb3VuZC5kZXN0cm95KClcbiAgICAgICAgdGhpcy5wb3B1cC5kZXN0cm95KClcbiAgICAgICAgdGhpcy5zZXR1cCgpLmxheW91dCgpXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFNldHMgb3IgZ2V0cyB0aGUgY29udGVudC4gVGhlIGdldHRlciBhbHdheXMgcmV0dXJucyBhbiBQSVhJLkRpc3BsYXlPYmplY3QuIFRoZSBzZXR0ZXIgY2FuIHJlY2VpdmVcbiAgICAgKiBhIHN0cmluZyBvciBhIFBJWEkuRGlzcGxheU9iamVjdC5cbiAgICAgKiBcbiAgICAgKiBAbWVtYmVyIHtzdHJpbmd8UElYSS5EaXNwbGF5T2JqZWN0fVxuICAgICAqL1xuICAgIGdldCBjb250ZW50KCkge1xuICAgICAgICByZXR1cm4gdGhpcy5wb3B1cC5jb250ZW50XG4gICAgfVxuICAgIHNldCBjb250ZW50KHZhbHVlKSB7XG4gICAgICAgIHRoaXMub3B0cy5jb250ZW50ID0gdmFsdWVcbiAgICAgICAgdGhpcy5iYWNrZ3JvdW5kLmRlc3Ryb3koKVxuICAgICAgICB0aGlzLnBvcHVwLmRlc3Ryb3koKVxuICAgICAgICB0aGlzLnNldHVwKCkubGF5b3V0KClcbiAgICB9XG59XG4iLCJpbXBvcnQgVGhlbWUgZnJvbSAnLi90aGVtZS5qcydcbmltcG9ydCB7SW50ZXJhY3RpdmVQb3B1cH0gZnJvbSAnLi9wb3B1cC5qcydcblxuLyoqXG4gKiBDbGFzcyB0aGF0IHJlcHJlc2VudHMgYSBNZXNzYWdlLiBBIG1lc3NhZ2UgcG9wcyB1cCBhbmQgZGlzYXBwZWFycyBhZnRlciBhIHNwZWNpZmljIGFtb3VudCBvZiB0aW1lLlxuICogXG4gKiBAZXhhbXBsZVxuICogLy8gQ3JlYXRlIHRoZSBQaXhpSlMgQXBwXG4gKiBjb25zdCBhcHAgPSBuZXcgUElYSUFwcCh7XG4gKiAgICAgdmlldzogY2FudmFzLFxuICogICAgIHdpZHRoOiA5MDAsXG4gKiAgICAgaGVpZ2h0OiAyNTBcbiAqIH0pLnNldHVwKCkucnVuKClcbiAqIFxuICogLy8gQ3JlYXRlIGEgYnV0dG9uXG4gKiBsZXQgYnV0dG9uID0gbmV3IEJ1dHRvbih7XG4gKiAgICAgbGFiZWw6ICdDbGljayBtZScsXG4gKiAgICAgYWN0aW9uOiBlID0+IHtcbiAqICAgICAgICAgY29uc3QgbWVzc2FnZSA9IG5ldyBNZXNzYWdlKHtcbiAqICAgICAgICAgICAgIGFwcDogYXBwLFxuICogICAgICAgICAgICAgaGVhZGVyOiAnSGVhZGVyJyxcbiAqICAgICAgICAgICAgIGNvbnRlbnQ6ICdUZXh0LidcbiAqICAgICAgICAgfSlcbiAqICAgICAgICAgYXBwLnNjZW5lLmFkZENoaWxkKG1lc3NhZ2UpXG4gKiAgICAgfVxuICogfSlcbiAqIFxuICogLy8gQWRkIHRoZSBidXR0b24gdG8gdGhlIHNjZW5lXG4gKiBhcHAuc2NlbmUuYWRkQ2hpbGQoYnV0dG9uKVxuICpcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgSW50ZXJhY3RpdmVQb3B1cFxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly93d3cuaXdtLXR1ZWJpbmdlbi5kZS9pd21icm93c2VyL2xpYi9waXhpL21lc3NhZ2UuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZXNzYWdlIGV4dGVuZHMgSW50ZXJhY3RpdmVQb3B1cCB7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIGEgTWVzc2FnZS5cbiAgICAgKiBcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0byBzdHlsZSBhbmQgYmVoYXZpb3VyIG9mIHRoZSBtZXNzYWdlLlxuICAgICAqIEBwYXJhbSB7UElYSUFwcH0gW29wdHMuYXBwPXdpbmRvdy5hcHBdIC0gVGhlIFBJWElBcHAgd2hlcmUgdGhpcyBtZXNzYWdlIGJlbG9uZ3MgdG8uXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5jbG9zZUJ1dHRvbj1mYWxzZV0gLSBTaG91bGQgYSBjbG9zZSBidXR0b24gYmUgZGlzcGxheWVkIGluIHRoZSB1cHBlciByaWdodCBjb3JuZXI/XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLm1pbldpZHRoPTI4MF0gLSBUaGUgbWluaW11bSB3aWR0aCBvZiB0aGUgbWVzc2FnZSBib3guIEF1dG9tYXRpY2FsbHkgZXhwYW5kcyB3aXRoIHRoZSBjb250ZW50LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5taW5IZWlnaHQ9MTAwXSAtIFRoZSBtaW5pbXVtIGhlaWdodCBvZiB0aGUgbWVzc2FnZSBib3guIEF1dG9tYXRpY2FsbHkgZXhwYW5kcyB3aXRoIHRoZSBjb250ZW50LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5tYXJnaW49VGhlbWUubWFyZ2luXSAtIFRoZSBvdXRlciBzcGFjaW5nIG9mIHRoZSBtZXNzYWdlIGJveC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMuYWxpZ249cmlnaHRdIC0gVGhlIGhvcml6b250YWwgcG9zaXRpb24gb2YgdGhlIG1lc3NhZ2UgYm94IHJlbGF0aXZlIHRvIHRoZSBhcHAuIFBvc3NpYmxlXG4gICAgICogICAgIHZhbHVlcyBhcmUgbGVmdCwgY2VudGVyLCByaWdodC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMudmVydGljYWxBbGlnbj10b3BdIC0gVGhlIHZlcnRpY2FsIHBvc2l0aW9uIG9mIHRoZSBtZXNzYWdlIGJveCByZWxhdGl2ZSB0byB0aGUgYXBwLiBQb3NzaWJsZVxuICAgICAqICAgICB2YWx1ZXMgYXJlIHRvcCwgbWlkZGxlLCBib3R0b20uXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmR1cmF0aW9uPTVdIC0gVGhlIGR1cmF0aW9uIGluIHNlY29uZHMgd2hlbiB0aGUgbWVzc2FnZSBib3ggc2hvdWxkIGRpc2FwcGVhci5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmF1dG9DbG9zZT10cnVlXSAtIFNob3VsZCB0aGUgbWVzc2FnZSBib3ggYmUgY2xvc2VkIGF1dG9tYXRpY2FsbHk/XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmNsb3NlRHVyYXRpb249VGhlbWUuZmFzdF0gLSBUaGUgZHVyYXRpb24gaW4gc2Vjb25kcyBvZiB0aGUgY2xvc2luZyBvZiB0aGUgbWVzc2FnZSBib3guXG4gICAgICovXG4gICAgY29uc3RydWN0b3Iob3B0cyA9IHt9KSB7XG4gICAgICAgIFxuICAgICAgICBjb25zdCB0aGVtZSA9IFRoZW1lLmZyb21TdHJpbmcob3B0cy50aGVtZSlcblxuICAgICAgICBvcHRzID0gT2JqZWN0LmFzc2lnbih7fSwge1xuICAgICAgICAgICAgYXBwOiB3aW5kb3cuYXBwLFxuICAgICAgICAgICAgY2xvc2VCdXR0b246IGZhbHNlLFxuICAgICAgICAgICAgbWluV2lkdGg6IDI4MCxcbiAgICAgICAgICAgIG1pbkhlaWdodDogMTAwLFxuICAgICAgICAgICAgbWFyZ2luOiB0aGVtZS5tYXJnaW4sXG4gICAgICAgICAgICBhbGlnbjogJ3JpZ2h0JywgICAgICAgICAgICAgICAgICAgICAvLyBsZWZ0LCBjZW50ZXIsIHJpZ2h0XG4gICAgICAgICAgICB2ZXJ0aWNhbEFsaWduOiAndG9wJywgICAgICAgICAgICAgICAvLyB0b3AsIG1pZGRsZSwgYm90dG9tXG4gICAgICAgICAgICBkdXJhdGlvbjogNSxcbiAgICAgICAgICAgIGF1dG9DbG9zZTogdHJ1ZSxcbiAgICAgICAgICAgIGNsb3NlRHVyYXRpb246IHRoZW1lLmZhc3RcbiAgICAgICAgfSwgb3B0cylcblxuICAgICAgICBzdXBlcihvcHRzKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbGF5b3V0cyB0aGUgcG9zaXRpb24gb2YgdGhlIG1lc3NhZ2UgYm94LlxuICAgICAqIFxuICAgICAqIEByZXR1cm4ge01lc3NhZ2V9IFJldHVybnMgdGhlIG1lc3NhZ2UgYm94IGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBsYXlvdXQoKSB7XG5cbiAgICAgICAgc3VwZXIubGF5b3V0KClcblxuICAgICAgICAvLyBob3Jpem9udGFsXG4gICAgICAgIHN3aXRjaCAodGhpcy5vcHRzLmFsaWduKSB7XG4gICAgICAgICAgICBjYXNlICdsZWZ0JzpcbiAgICAgICAgICAgICAgICB0aGlzLnggPSB0aGlzLm9wdHMubWFyZ2luXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGNhc2UgJ2NlbnRlcic6XG4gICAgICAgICAgICAgICAgdGhpcy54ID0gKHRoaXMub3B0cy5hcHAuc2l6ZS53aWR0aCAvIDIpIC0gKHRoaXMud2lkdGggLyAyKVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdyaWdodCc6XG4gICAgICAgICAgICAgICAgdGhpcy54ID0gdGhpcy5vcHRzLmFwcC5zaXplLndpZHRoIC0gdGhpcy5vcHRzLm1hcmdpbiAtIHRoaXMud2lkdGhcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gdmVydGljYWxcbiAgICAgICAgc3dpdGNoICh0aGlzLm9wdHMudmVydGljYWxBbGlnbikge1xuICAgICAgICAgICAgY2FzZSAndG9wJzpcbiAgICAgICAgICAgICAgICB0aGlzLnkgPSB0aGlzLm9wdHMubWFyZ2luXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGNhc2UgJ21pZGRsZSc6XG4gICAgICAgICAgICAgICAgdGhpcy55ID0gKHRoaXMub3B0cy5hcHAuc2l6ZS5oZWlnaHQgLyAyKSAtICh0aGlzLmhlaWdodCAvIDIpXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGNhc2UgJ2JvdHRvbSc6XG4gICAgICAgICAgICAgICAgdGhpcy55ID0gdGhpcy5vcHRzLmFwcC5zaXplLmhlaWdodCAtIHRoaXMub3B0cy5tYXJnaW4gLSB0aGlzLmhlaWdodFxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyB0aGUgbWVzc2FnZSBib3guXG4gICAgICogXG4gICAgICogQHByaXZhdGVcbiAgICAgKi9cbiAgICBzaG93KCkge1xuXG4gICAgICAgIHN1cGVyLnNob3coKVxuXG4gICAgICAgIGlmICh0aGlzLm9wdHMuYXV0b0Nsb3NlKSB7XG4gICAgICAgICAgICB3aW5kb3cuc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5oaWRlKClcbiAgICAgICAgICAgIH0sIHRoaXMub3B0cy5kdXJhdGlvbiAqIDEwMDApXG4gICAgICAgIH1cbiAgICB9XG59XG4iLCIvKiBnbG9iYWxzIFdlYktpdFBvaW50ICovXG5cbi8qKiBUZXN0cyB3aGV0aGVyIGFuIG9iamVjdCBpcyBlbXB0eVxuICogQHBhcmFtIHtPYmplY3R9IG9iaiAtIHRoZSBvYmplY3QgdG8gYmUgdGVzdGVkXG4gKiBAcmV0dXJuIHtib29sZWFufVxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNFbXB0eShvYmopIHtcbiAgICAvLyA+IGlzRW1wdHkoe30pXG4gICAgLy8gdHJ1ZVxuICAgIGZvciAobGV0IGkgaW4gb2JqKSB7XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cbiAgICByZXR1cm4gdHJ1ZVxufVxuXG4vKiogUmV0dXJucyBhIHVuaXZlcnNhbCB1bmlxdWUgaWRcbiAqIEByZXR1cm4ge3N0cmluZ31cbiAqIFNlZSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMDUwMzQvY3JlYXRlLWd1aWQtdXVpZC1pbi1qYXZhc2NyaXB0LzIxOTYzMTM2IzIxOTYzMTM2XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1dWlkKCkge1xuICAgIHJldHVybiAneHh4eHh4eHgteHh4eC00eHh4LXl4eHgteHh4eHh4eHh4eHh4Jy5yZXBsYWNlKC9beHldL2csIGMgPT4ge1xuICAgICAgICBsZXQgciA9IChNYXRoLnJhbmRvbSgpICogMTYpIHwgMCxcbiAgICAgICAgICAgIHYgPSBjID09ICd4JyA/IHIgOiAociAmIDB4MykgfCAweDhcbiAgICAgICAgcmV0dXJuIHYudG9TdHJpbmcoMTYpXG4gICAgfSlcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxlcnAoc3RhcnQsIHN0b3AsIGFtdCkge1xuICAgIHJldHVybiBhbXQgKiAoc3RvcCAtIHN0YXJ0KSArIHN0YXJ0XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzYW1wbGUocG9wdWxhdGlvbiwgaykge1xuICAgIC8qXG4gICAgICAgIEZyb20gaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTkyNjk1NDUvaG93LXRvLWdldC1uLW5vLWVsZW1lbnRzLXJhbmRvbWx5LWZyb20tYW4tYXJyYXkvMzg1NzExMzJcbiAgICAgICAgXG4gICAgICAgIENob29zZXMgayB1bmlxdWUgcmFuZG9tIGVsZW1lbnRzIGZyb20gYSBwb3B1bGF0aW9uIHNlcXVlbmNlIG9yIHNldC5cblxuICAgICAgICBSZXR1cm5zIGEgbmV3IGxpc3QgY29udGFpbmluZyBlbGVtZW50cyBmcm9tIHRoZSBwb3B1bGF0aW9uIHdoaWxlXG4gICAgICAgIGxlYXZpbmcgdGhlIG9yaWdpbmFsIHBvcHVsYXRpb24gdW5jaGFuZ2VkLiAgVGhlIHJlc3VsdGluZyBsaXN0IGlzXG4gICAgICAgIGluIHNlbGVjdGlvbiBvcmRlciBzbyB0aGF0IGFsbCBzdWItc2xpY2VzIHdpbGwgYWxzbyBiZSB2YWxpZCByYW5kb21cbiAgICAgICAgc2FtcGxlcy4gIFRoaXMgYWxsb3dzIHJhZmZsZSB3aW5uZXJzICh0aGUgc2FtcGxlKSB0byBiZSBwYXJ0aXRpb25lZFxuICAgICAgICBpbnRvIGdyYW5kIHByaXplIGFuZCBzZWNvbmQgcGxhY2Ugd2lubmVycyAodGhlIHN1YnNsaWNlcykuXG5cbiAgICAgICAgTWVtYmVycyBvZiB0aGUgcG9wdWxhdGlvbiBuZWVkIG5vdCBiZSBoYXNoYWJsZSBvciB1bmlxdWUuICBJZiB0aGVcbiAgICAgICAgcG9wdWxhdGlvbiBjb250YWlucyByZXBlYXRzLCB0aGVuIGVhY2ggb2NjdXJyZW5jZSBpcyBhIHBvc3NpYmxlXG4gICAgICAgIHNlbGVjdGlvbiBpbiB0aGUgc2FtcGxlLlxuXG4gICAgICAgIFRvIGNob29zZSBhIHNhbXBsZSBpbiBhIHJhbmdlIG9mIGludGVnZXJzLCB1c2UgcmFuZ2UgYXMgYW4gYXJndW1lbnQuXG4gICAgICAgIFRoaXMgaXMgZXNwZWNpYWxseSBmYXN0IGFuZCBzcGFjZSBlZmZpY2llbnQgZm9yIHNhbXBsaW5nIGZyb20gYVxuICAgICAgICBsYXJnZSBwb3B1bGF0aW9uOiAgIHNhbXBsZShyYW5nZSgxMDAwMDAwMCksIDYwKVxuXG4gICAgICAgIFNhbXBsaW5nIHdpdGhvdXQgcmVwbGFjZW1lbnQgZW50YWlscyB0cmFja2luZyBlaXRoZXIgcG90ZW50aWFsXG4gICAgICAgIHNlbGVjdGlvbnMgKHRoZSBwb29sKSBpbiBhIGxpc3Qgb3IgcHJldmlvdXMgc2VsZWN0aW9ucyBpbiBhIHNldC5cblxuICAgICAgICBXaGVuIHRoZSBudW1iZXIgb2Ygc2VsZWN0aW9ucyBpcyBzbWFsbCBjb21wYXJlZCB0byB0aGVcbiAgICAgICAgcG9wdWxhdGlvbiwgdGhlbiB0cmFja2luZyBzZWxlY3Rpb25zIGlzIGVmZmljaWVudCwgcmVxdWlyaW5nXG4gICAgICAgIG9ubHkgYSBzbWFsbCBzZXQgYW5kIGFuIG9jY2FzaW9uYWwgcmVzZWxlY3Rpb24uICBGb3JcbiAgICAgICAgYSBsYXJnZXIgbnVtYmVyIG9mIHNlbGVjdGlvbnMsIHRoZSBwb29sIHRyYWNraW5nIG1ldGhvZCBpc1xuICAgICAgICBwcmVmZXJyZWQgc2luY2UgdGhlIGxpc3QgdGFrZXMgbGVzcyBzcGFjZSB0aGFuIHRoZVxuICAgICAgICBzZXQgYW5kIGl0IGRvZXNuJ3Qgc3VmZmVyIGZyb20gZnJlcXVlbnQgcmVzZWxlY3Rpb25zLlxuICAgICovXG5cbiAgICBpZiAoIUFycmF5LmlzQXJyYXkocG9wdWxhdGlvbikpXG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJQb3B1bGF0aW9uIG11c3QgYmUgYW4gYXJyYXkuXCIpXG4gICAgbGV0IG4gPSBwb3B1bGF0aW9uLmxlbmd0aFxuICAgIGlmIChrIDwgMCB8fCBrID4gbilcbiAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoXCJTYW1wbGUgbGFyZ2VyIHRoYW4gcG9wdWxhdGlvbiBvciBpcyBuZWdhdGl2ZVwiKVxuXG4gICAgbGV0IHJlc3VsdCA9IG5ldyBBcnJheShrKVxuICAgIGxldCBzZXRzaXplID0gMjEgICAvLyBzaXplIG9mIGEgc21hbGwgc2V0IG1pbnVzIHNpemUgb2YgYW4gZW1wdHkgbGlzdFxuXG4gICAgaWYgKGsgPiA1KVxuICAgICAgICBzZXRzaXplICs9IE1hdGgucG93KDQsIE1hdGguY2VpbChNYXRoLmxvZyhrICogMywgNCkpKVxuXG4gICAgaWYgKG4gPD0gc2V0c2l6ZSkge1xuICAgICAgICAvLyBBbiBuLWxlbmd0aCBsaXN0IGlzIHNtYWxsZXIgdGhhbiBhIGstbGVuZ3RoIHNldFxuICAgICAgICBsZXQgcG9vbCA9IHBvcHVsYXRpb24uc2xpY2UoKVxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGs7IGkrKykgeyAgICAgICAgICAvLyBpbnZhcmlhbnQ6ICBub24tc2VsZWN0ZWQgYXQgWzAsbi1pKVxuICAgICAgICAgICAgbGV0IGogPSBNYXRoLnJhbmRvbSgpICogKG4gLSBpKSB8IDBcbiAgICAgICAgICAgIHJlc3VsdFtpXSA9IHBvb2xbal1cbiAgICAgICAgICAgIHBvb2xbal0gPSBwb29sW24gLSBpIC0gMV0gICAgICAgLy8gbW92ZSBub24tc2VsZWN0ZWQgaXRlbSBpbnRvIHZhY2FuY3lcbiAgICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAgIGxldCBzZWxlY3RlZCA9IG5ldyBTZXQoKVxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGs7IGkrKykge1xuICAgICAgICAgICAgbGV0IGogPSBNYXRoLnJhbmRvbSgpICogKG4gLSBpKSB8IDBcbiAgICAgICAgICAgIHdoaWxlIChzZWxlY3RlZC5oYXMoaikpIHtcbiAgICAgICAgICAgICAgICBqID0gTWF0aC5yYW5kb20oKSAqIChuIC0gaSkgfCAwXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzZWxlY3RlZC5hZGQoailcbiAgICAgICAgICAgIHJlc3VsdFtpXSA9IHBvcHVsYXRpb25bal1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiByZXN1bHRcbn1cblxuXG4vLyBSZXR1cm5zIGEgZnVuY3Rpb24sIHRoYXQsIGFzIGxvbmcgYXMgaXQgY29udGludWVzIHRvIGJlIGludm9rZWQsIHdpbGwgbm90XG4vLyBiZSB0cmlnZ2VyZWQuIFRoZSBmdW5jdGlvbiB3aWxsIGJlIGNhbGxlZCBhZnRlciBpdCBzdG9wcyBiZWluZyBjYWxsZWQgZm9yXG4vLyBOIG1pbGxpc2Vjb25kcy4gSWYgYGltbWVkaWF0ZWAgaXMgcGFzc2VkLCB0cmlnZ2VyIHRoZSBmdW5jdGlvbiBvbiB0aGVcbi8vIGxlYWRpbmcgZWRnZSwgaW5zdGVhZCBvZiB0aGUgdHJhaWxpbmcuXG4vLyBUYWtlbiBmcm9tOiBodHRwczovL2Rhdmlkd2Fsc2gubmFtZS9lc3NlbnRpYWwtamF2YXNjcmlwdC1mdW5jdGlvbnNcbmV4cG9ydCBmdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBpbW1lZGlhdGUpIHtcbiAgICBsZXQgdGltZW91dFxuICAgIHJldHVybiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGxldCBjb250ZXh0ID0gdGhpcyxcbiAgICAgICAgICAgIGFyZ3MgPSBhcmd1bWVudHNcbiAgICAgICAgbGV0IGxhdGVyID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdGltZW91dCA9IG51bGxcbiAgICAgICAgICAgIGlmICghaW1tZWRpYXRlKSBmdW5jLmFwcGx5KGNvbnRleHQsIGFyZ3MpXG4gICAgICAgIH1cbiAgICAgICAgbGV0IGNhbGxOb3cgPSBpbW1lZGlhdGUgJiYgIXRpbWVvdXRcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpXG4gICAgICAgIHRpbWVvdXQgPSBzZXRUaW1lb3V0KGxhdGVyLCB3YWl0KVxuICAgICAgICBpZiAoY2FsbE5vdykgZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKVxuICAgIH1cbn1cblxuLyoqIFJldHVybnMgYW4gaWQgdGhhdCBpcyBndWFyYW50ZWVkIHRvIGJlIHVuaXF1ZSB3aXRoaW4gdGhlIGxpdmV0aW1lIG9mIHRoZVxuICogYXBwbGljYXRpb25cbiAqIEByZXR1cm4ge3N0cmluZ31cbiAqL1xubGV0IF9pZEdlbmVyYXRvciA9IDBcbmV4cG9ydCBmdW5jdGlvbiBnZXRJZCgpIHtcbiAgICByZXR1cm4gJ2lkJyArIF9pZEdlbmVyYXRvcisrXG59XG5cbmV4cG9ydCBmdW5jdGlvbiByYW5kb21JbnQobWluID0gMCwgbWF4ID0gMTAwKSB7XG4gICAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIChtYXggLSBtaW4gKyAxKSArIG1pbilcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJhbmRvbUZsb2F0KG1pbiA9IDAuMCwgbWF4ID0gMS4wKSB7XG4gICAgcmV0dXJuIE1hdGgucmFuZG9tKCkgKiAobWF4IC0gbWluKSArIG1pblxufVxuXG5leHBvcnQgY2xhc3MgRGF0ZXMge1xuXG4gICAgc3RhdGljIGNyZWF0ZShmdWxsWWVhciwgbW9udGgsIGRheSkge1xuICAgICAgICByZXR1cm4gbmV3IERhdGUoRGF0ZS5VVEMoZnVsbFllYXIsIG1vbnRoLCBkYXkpKVxuICAgIH1cblxuICAgIHN0YXRpYyBkYXlzSW5Nb250aChkYXRlKSB7XG4gICAgICAgIHJldHVybiBuZXcgRGF0ZShkYXRlLmdldEZ1bGxZZWFyKCksIGRhdGUuZ2V0TW9udGgoKSArIDEsIDApLmdldERhdGUoKVxuICAgIH1cblxuICAgIHN0YXRpYyBzdGFydFllYXJSYW5nZShkYXRlKSB7XG4gICAgICAgIHJldHVybiBuZXcgRGF0ZShEYXRlLlVUQyhkYXRlLmdldEZ1bGxZZWFyKCkgLSAxLCAxMSwgMzEsIDIzLCA1OSwgNTksIDk5OSkpXG4gICAgfVxuXG4gICAgc3RhdGljIGVuZFllYXJSYW5nZShkYXRlKSB7XG4gICAgICAgIHJldHVybiBuZXcgRGF0ZShEYXRlLlVUQyhkYXRlLmdldEZ1bGxZZWFyKCkgKyAxLCAwLCAxKSlcbiAgICB9XG5cbiAgICBzdGF0aWMgcHJldlllYXIoZGF0ZSwgb2Zmc2V0ID0gMSkge1xuICAgICAgICByZXR1cm4gdGhpcy5jcmVhdGUoZGF0ZS5nZXRGdWxsWWVhcigpIC0gb2Zmc2V0LCAwLCAxKVxuICAgIH1cblxuICAgIHN0YXRpYyBuZXh0WWVhcihkYXRlLCBvZmZzZXQgPSAxKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNyZWF0ZShkYXRlLmdldEZ1bGxZZWFyKCkgKyBvZmZzZXQsIDAsIDEpXG4gICAgfVxuXG4gICAgc3RhdGljIG5leHRNb250aChkYXRlKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNyZWF0ZShkYXRlLmdldEZ1bGxZZWFyKCksIGRhdGUuZ2V0TW9udGgoKSArIDEsIDEpXG4gICAgfVxuXG4gICAgc3RhdGljIG5leHREYXkoZGF0ZSkge1xuICAgICAgICByZXR1cm4gdGhpcy5jcmVhdGUoXG4gICAgICAgICAgICBkYXRlLmdldEZ1bGxZZWFyKCksXG4gICAgICAgICAgICBkYXRlLmdldE1vbnRoKCksXG4gICAgICAgICAgICBkYXRlLmdldERhdGUoKSArIDFcbiAgICAgICAgKVxuICAgIH1cblxuICAgIHN0YXRpYyBuZXh0SG91cihkYXRlKSB7XG4gICAgICAgIC8vIFNlZSBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzEwNTA3MjAvYWRkaW5nLWhvdXJzLXRvLWphdmFzY3JpcHQtZGF0ZS1vYmplY3RcbiAgICAgICAgcmV0dXJuIG5ldyBEYXRlKGRhdGUuZ2V0VGltZSgpICsgNjAgKiA2MCAqIDEwMDApXG4gICAgfVxuXG4gICAgc3RhdGljIG5leHRNaW51dGUoZGF0ZSkge1xuICAgICAgICAvLyBTZWUgYWJvdmVcbiAgICAgICAgcmV0dXJuIG5ldyBEYXRlKGRhdGUuZ2V0VGltZSgpICsgNjAgKiAxMDAwKVxuICAgIH1cblxuICAgIHN0YXRpYyBuZXh0U2Vjb25kKGRhdGUpIHtcbiAgICAgICAgLy8gU2VlIGFib3ZlXG4gICAgICAgIHJldHVybiBuZXcgRGF0ZShkYXRlLmdldFRpbWUoKSArIDEwMDApXG4gICAgfVxuXG4gICAgc3RhdGljIG5leHRNaWxsaXNlY29uZChkYXRlKSB7XG4gICAgICAgIC8vIFNlZSBhYm92ZVxuICAgICAgICByZXR1cm4gbmV3IERhdGUoZGF0ZS5nZXRUaW1lKCkgKyAxKVxuICAgIH1cblxuICAgIHN0YXRpYyAqaXRlclllYXJzKHN0YXJ0LCBlbmQpIHtcbiAgICAgICAgbGV0IGRhdGUgPSB0aGlzLmNyZWF0ZShzdGFydC5nZXRGdWxsWWVhcigpLCAwLCAxKVxuICAgICAgICB3aGlsZSAoZGF0ZSA8PSBlbmQpIHtcbiAgICAgICAgICAgIHlpZWxkIGRhdGVcbiAgICAgICAgICAgIGRhdGUgPSB0aGlzLm5leHRZZWFyKGRhdGUpXG4gICAgICAgIH1cbiAgICAgICAgeWllbGQgZGF0ZVxuICAgIH1cblxuICAgIHN0YXRpYyAqaXRlck1vbnRocyh5ZWFyLCBsaW1pdCA9IDEyKSB7XG4gICAgICAgIGxldCBtb250aCA9IDBcbiAgICAgICAgd2hpbGUgKG1vbnRoIDwgbGltaXQpIHtcbiAgICAgICAgICAgIGxldCBkYXRlID0gdGhpcy5jcmVhdGUoeWVhci5nZXRGdWxsWWVhcigpLCBtb250aCwgMSlcbiAgICAgICAgICAgIHlpZWxkIGRhdGVcbiAgICAgICAgICAgIG1vbnRoICs9IDFcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyAqaXRlck1vbnRoc09mWWVhcnMoeWVhcnMpIHtcbiAgICAgICAgZm9yIChsZXQgeWVhciBvZiB5ZWFycykge1xuICAgICAgICAgICAgZm9yIChsZXQgbW9udGggb2YgdGhpcy5pdGVyTW9udGhzKHllYXIpKSB7XG4gICAgICAgICAgICAgICAgeWllbGQgbW9udGhcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyAqaXRlckRheXMobW9udGgpIHtcbiAgICAgICAgbGV0IGRheSA9IDFcbiAgICAgICAgbGV0IGxpbWl0ID0gRGF0ZXMuZGF5c0luTW9udGgobW9udGgpXG4gICAgICAgIHdoaWxlIChkYXkgPD0gbGltaXQpIHtcbiAgICAgICAgICAgIGxldCBkYXRlID0gdGhpcy5jcmVhdGUobW9udGguZ2V0RnVsbFllYXIoKSwgbW9udGguZ2V0TW9udGgoKSwgZGF5KVxuICAgICAgICAgICAgeWllbGQgZGF0ZVxuICAgICAgICAgICAgZGF5ICs9IDFcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyAqaXRlckRheXNPZk1vbnRocyhtb250aHMpIHtcbiAgICAgICAgZm9yIChsZXQgbW9udGggb2YgbW9udGhzKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBkYXkgb2YgdGhpcy5pdGVyRGF5cyhtb250aCkpIHtcbiAgICAgICAgICAgICAgICB5aWVsZCBkYXlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbn1cbi8qIENvbG9yIGNvbnZlcnNpb24gZnVuY3Rpb25zICovXG5cbmV4cG9ydCBjbGFzcyBDb2xvcnMge1xuICAgIC8vIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNTYyMzgzOC9yZ2ItdG8taGV4LWFuZC1oZXgtdG8tcmdiXG5cbiAgICBzdGF0aWMgcmdiMm51bShyZWQsIGdyZWVuLCBibHVlKSB7XG4gICAgICAgIGxldCByZ2IgPSBibHVlIHwgKGdyZWVuIDw8IDgpIHwgKHJlZCA8PCAxNilcbiAgICAgICAgcmV0dXJuIDB4MDAwMDAwICsgcmdiXG4gICAgfVxuXG4gICAgc3RhdGljIHJnYjJoZXgocmVkLCBncmVlbiwgYmx1ZSkge1xuICAgICAgICBsZXQgcmdiID0gYmx1ZSB8IChncmVlbiA8PCA4KSB8IChyZWQgPDwgMTYpXG4gICAgICAgIHJldHVybiAnIycgKyAoMHgxMDAwMDAwICsgcmdiKS50b1N0cmluZygxNikuc2xpY2UoMSlcbiAgICB9XG5cbiAgICBzdGF0aWMgaGV4MnJnYihoZXgpIHtcbiAgICAgICAgLy8gbG9uZyB2ZXJzaW9uXG4gICAgICAgIGxldCByID0gaGV4Lm1hdGNoKC9eIyhbMC05YS1mXXsyfSkoWzAtOWEtZl17Mn0pKFswLTlhLWZdezJ9KSQvaSlcbiAgICAgICAgaWYgKHIpIHtcbiAgICAgICAgICAgIHJldHVybiByLnNsaWNlKDEsIDQpLm1hcCh4ID0+IHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcGFyc2VJbnQoeCwgMTYpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICAgIC8vIHNob3J0IHZlcnNpb25cbiAgICAgICAgciA9IGhleC5tYXRjaCgvXiMoWzAtOWEtZl0pKFswLTlhLWZdKShbMC05YS1mXSkkL2kpXG4gICAgICAgIGlmIChyKSB7XG4gICAgICAgICAgICByZXR1cm4gci5zbGljZSgxLCA0KS5tYXAoeCA9PiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIDB4MTEgKiBwYXJzZUludCh4LCAxNilcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG5cbiAgICBzdGF0aWMgcmdiKHIsIGcsIGIpIHtcbiAgICAgICAgcmV0dXJuIHsgciwgZywgYiB9XG4gICAgfVxuXG4gICAgc3RhdGljIHN0cmluZzJoZXgoc3RyKSB7XG4gICAgICAgIHJldHVybiBwYXJzZUludCgnMHgnICsgc3RyLnNsaWNlKDEpKVxuICAgIH1cblxuICAgIHN0YXRpYyBsZXJwKHJnYjEsIHJnYjIsIGFtb3VudCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgcjogTWF0aC5yb3VuZChsZXJwKHJnYjEuciwgcmdiMi5yLCBhbW91bnQpKSxcbiAgICAgICAgICAgIGc6IE1hdGgucm91bmQobGVycChyZ2IxLmcsIHJnYjIuZywgYW1vdW50KSksXG4gICAgICAgICAgICBiOiBNYXRoLnJvdW5kKGxlcnAocmdiMS5iLCByZ2IyLmIsIGFtb3VudCkpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgZ2V0IHZpb2xldCgpIHtcbiAgICAgICAgcmV0dXJuIENvbG9ycy5yZ2IybnVtKDg5LCAzNCwgMTMxKVxuICAgIH1cblxuICAgIHN0YXRpYyBnZXQgc3RlZWxibHVlKCkge1xuICAgICAgICByZXR1cm4gQ29sb3JzLnJnYjJudW0oMCwgMTMwLCAxNjQpXG4gICAgfVxuXG4gICAgc3RhdGljIGdldCBvY2hyZSgpIHtcbiAgICAgICAgcmV0dXJuIENvbG9ycy5yZ2IybnVtKDE4MSwgMTU3LCAwKVxuICAgIH1cblxuICAgIHN0YXRpYyBnZXQgdHVycXVvaXNlKCkge1xuICAgICAgICByZXR1cm4gQ29sb3JzLnJnYjJudW0oMzQsIDE2NCwgMTMxKVxuICAgIH1cblxuICAgIHN0YXRpYyBnZXQgZW1pbmVuY2UoKSB7XG4gICAgICAgIHJldHVybiBDb2xvcnMucmdiMm51bSgxNTAsIDYwLCAxMzQpXG4gICAgfVxuXG4gICAgc3RhdGljIHJhbmRvbSgpIHtcbiAgICAgICAgbGV0IHIgPSBNYXRoLnJvdW5kKE1hdGgucmFuZG9tKCkgKiAyNTUpXG4gICAgICAgIGxldCBnID0gTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogMjU1KVxuICAgICAgICBsZXQgYiA9IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIDI1NSlcbiAgICAgICAgcmV0dXJuIENvbG9ycy5yZ2IybnVtKHIsIGcsIGIpXG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgQ3ljbGUgZXh0ZW5kcyBBcnJheSB7XG4gICAgY29uc3RydWN0b3IoLi4uaXRlbXMpIHtcbiAgICAgICAgc3VwZXIoKVxuICAgICAgICBmb3IgKGxldCBpdGVtIG9mIGl0ZW1zKSB7XG4gICAgICAgICAgICB0aGlzLnB1c2goaXRlbSlcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmluZGV4ID0gMFxuICAgIH1cblxuICAgIG5leHQoKSB7XG4gICAgICAgIGlmICh0aGlzLmluZGV4ID09IHRoaXMubGVuZ3RoKSB7XG4gICAgICAgICAgICB0aGlzLmluZGV4ID0gMFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzW3RoaXMuaW5kZXgrK11cbiAgICB9XG5cbiAgICBjdXJyZW50KCkge1xuICAgICAgICBpZiAodGhpcy5pbmRleCA9PT0gdGhpcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHRoaXMuaW5kZXggPSAwXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXNbdGhpcy5pbmRleF1cbiAgICB9XG59XG5cbi8qKiBTdGF0aWMgbWV0aG9kcyB0byBjb21wdXRlIDJEIHBvaW50cyB3aXRoIHggYW5kIHkgY29vcmRpbmF0ZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBQb2ludHMge1xuICAgIHN0YXRpYyBsZW5ndGgoYSkge1xuICAgICAgICByZXR1cm4gTWF0aC5zcXJ0KGEueCAqIGEueCArIGEueSAqIGEueSlcbiAgICB9XG5cbiAgICBzdGF0aWMgbm9ybWFsaXplKHApIHtcbiAgICAgICAgbGV0IGxlbiA9IHRoaXMubGVuZ3RoKHApXG4gICAgICAgIHJldHVybiB0aGlzLm11bHRpcGx5U2NhbGFyKHAsIDEgLyBsZW4pXG4gICAgfVxuXG4gICAgc3RhdGljIG1lYW4oYSwgYikge1xuICAgICAgICByZXR1cm4geyB4OiAoYS54ICsgYi54KSAvIDIsIHk6IChhLnkgKyBiLnkpIC8gMiB9XG4gICAgfVxuXG4gICAgc3RhdGljIHN1YnRyYWN0KGEsIGIpIHtcbiAgICAgICAgcmV0dXJuIHsgeDogYS54IC0gYi54LCB5OiBhLnkgLSBiLnkgfVxuICAgIH1cblxuICAgIHN0YXRpYyBtdWx0aXBseShhLCBiKSB7XG4gICAgICAgIHJldHVybiB7IHg6IGEueCAqIGIueCwgeTogYS55ICogYi55IH1cbiAgICB9XG5cbiAgICBzdGF0aWMgZGl2aWRlKGEsIGIpIHtcbiAgICAgICAgcmV0dXJuIHsgeDogYS54IC8gYi54LCB5OiBhLnkgLyBiLnkgfVxuICAgIH1cblxuICAgIHN0YXRpYyBtdWx0aXBseVNjYWxhcihhLCBiKSB7XG4gICAgICAgIHJldHVybiB7IHg6IGEueCAqIGIsIHk6IGEueSAqIGIgfVxuICAgIH1cblxuICAgIHN0YXRpYyBhZGQoYSwgYikge1xuICAgICAgICByZXR1cm4geyB4OiBhLnggKyBiLngsIHk6IGEueSArIGIueSB9XG4gICAgfVxuXG4gICAgc3RhdGljIG5lZ2F0ZShwKSB7XG4gICAgICAgIHJldHVybiB7IHg6IC1wLngsIHk6IC1wLnkgfVxuICAgIH1cblxuICAgIHN0YXRpYyBhbmdsZShwMSwgcDIpIHtcbiAgICAgICAgcmV0dXJuIE1hdGguYXRhbjIocDEueSAtIHAyLnksIHAxLnggLSBwMi54KVxuICAgIH1cblxuICAgIHN0YXRpYyBub3JtYWxpemVkQW5nbGUocDEsIHAyKSB7XG4gICAgICAgIHJldHVybiBBbmdsZS5ub3JtYWxpemUodGhpcy5hbmdsZShwMSwgcDIpKVxuICAgIH1cblxuICAgIHN0YXRpYyBub3JtYWxpemVkMkFuZ2xlKHAxLCBwMikge1xuICAgICAgICByZXR1cm4gQW5nbGUubm9ybWFsaXplMih0aGlzLmFuZ2xlKHAxLCBwMikpXG4gICAgfVxuXG4gICAgc3RhdGljIGFyYyhwLCBhbHBoYSwgcmFkaXVzKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB4OiBwLnggKyByYWRpdXMgKiBNYXRoLmNvcyhhbHBoYSksXG4gICAgICAgICAgICB5OiBwLnkgKyByYWRpdXMgKiBNYXRoLnNpbihhbHBoYSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyBkaXN0YW5jZShhLCBiKSB7XG4gICAgICAgIGxldCBkeCA9IGEueCAtIGIueFxuICAgICAgICBsZXQgZHkgPSBhLnkgLSBiLnlcbiAgICAgICAgcmV0dXJuIE1hdGguc3FydChkeCAqIGR4ICsgZHkgKiBkeSlcbiAgICB9XG5cbiAgICBzdGF0aWMgZnJvbVBhZ2VUb05vZGUoZWxlbWVudCwgcCkge1xuICAgICAgICAvLyAgICBpZiAod2luZG93LndlYmtpdENvbnZlcnRQb2ludEZyb21QYWdlVG9Ob2RlKSB7XG4gICAgICAgIC8vICAgICAgICAgICAgIHJldHVybiB3aW5kb3cud2Via2l0Q29udmVydFBvaW50RnJvbVBhZ2VUb05vZGUoZWxlbWVudCxcbiAgICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBXZWJLaXRQb2ludChwLngsIHAueSkpXG4gICAgICAgIC8vICAgICAgICAgfVxuICAgICAgICByZXR1cm4gd2luZG93LmNvbnZlcnRQb2ludEZyb21QYWdlVG9Ob2RlKGVsZW1lbnQsIHAueCwgcC55KVxuICAgIH1cblxuICAgIHN0YXRpYyBmcm9tTm9kZVRvUGFnZShlbGVtZW50LCBwKSB7XG4gICAgICAgIC8vICBpZiAod2luZG93LndlYmtpdENvbnZlcnRQb2ludEZyb21Ob2RlVG9QYWdlKSB7XG4gICAgICAgIC8vICAgICAgICAgICAgIHJldHVybiB3aW5kb3cud2Via2l0Q29udmVydFBvaW50RnJvbU5vZGVUb1BhZ2UoZWxlbWVudCxcbiAgICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBXZWJLaXRQb2ludChwLngsIHAueSkpXG4gICAgICAgIC8vICAgICAgICAgfVxuICAgICAgICByZXR1cm4gd2luZG93LmNvbnZlcnRQb2ludEZyb21Ob2RlVG9QYWdlKGVsZW1lbnQsIHAueCwgcC55KVxuICAgIH1cbn1cblxuLyoqXG4gKiBBIGhlbHBlciBjbGFzcyBmb3IgY29tbW9uIHNldCBvcGVyYXRpb25zLlxuICpcbiAqIEBleHBvcnRcbiAqIEBjbGFzcyBTZXRzXG4gKi9cbmV4cG9ydCBjbGFzcyBTZXRzIHtcblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGludGVyc2VjdGlvbiBvZiBhbGwgc2V0c1xuICAgICAqIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzMxOTMwODk0L2phdmFzY3JpcHQtc2V0LWRhdGEtc3RydWN0dXJlLWludGVyc2VjdFxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0geyp9IHNldHNcbiAgICAgKiBAcmV0dXJuc1xuICAgICAqIEBtZW1iZXJvZiBTZXRzXG4gICAgICovXG4gICAgc3RhdGljIGludGVyc2VjdCguLi5zZXRzKSB7XG4gICAgICAgIGlmICghc2V0cy5sZW5ndGgpIHJldHVybiBuZXcgU2V0KClcbiAgICAgICAgY29uc3QgaSA9IHNldHMucmVkdWNlKChtLCBzLCBpKSA9PiBzLnNpemUgPCBzZXRzW21dLnNpemUgPyBpIDogbSwgMClcbiAgICAgICAgY29uc3QgW3NtYWxsZXN0XSA9IHNldHMuc3BsaWNlKGksIDEpXG4gICAgICAgIGNvbnN0IHJlcyA9IG5ldyBTZXQoKVxuICAgICAgICBmb3IgKGxldCB2YWwgb2Ygc21hbGxlc3QpXG4gICAgICAgICAgICBpZiAoc2V0cy5ldmVyeShzID0+IHMuaGFzKHZhbCkpKVxuICAgICAgICAgICAgICAgIHJlcy5hZGQodmFsKVxuICAgICAgICByZXR1cm4gcmVzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgdW5pb24gb2YgYWxsIHNldHNcbiAgICAgKlxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0geyp9IHNldHNcbiAgICAgKiBAcmV0dXJuc1xuICAgICAqIEBtZW1iZXJvZiBTZXRzXG4gICAgICovXG4gICAgc3RhdGljIHVuaW9uKC4uLnNldHMpIHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IG5ldyBTZXQoKVxuICAgICAgICBmb3IgKGxldCBzZXQgb2Ygc2V0cykge1xuICAgICAgICAgICAgZm9yIChsZXQgbSBvZiBzZXQpIHtcbiAgICAgICAgICAgICAgICByZXN1bHQuYWRkKG0pXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGRpZmZlcmVuY2Ugb2YgdGhlIGdpdmVuIHNldHMuIFN0YXJ0cyB3aXRoIHRoZSBmaXJzdCBzZXQgYW5kIHJlbW92aW5nIGFsbCBlbGVtZW50cyBvZiB0aGUgZm9sbG93aW5nIHNldHMuXG4gICAgICpcbiAgICAgKiBAc3RhdGljXG4gICAgICogQHBhcmFtIHsqfSBzZXRcbiAgICAgKiBAcGFyYW0geyp9IHNldHNcbiAgICAgKiBAcmV0dXJuc1xuICAgICAqIEBtZW1iZXJvZiBTZXRzXG4gICAgICovXG4gICAgc3RhdGljIGRpZmZlcmVuY2Uoc2V0LCAuLi5zZXRzKSB7XG4gICAgICAgIGxldCByZXN1bHQgPSBuZXcgU2V0KClcbiAgICAgICAgZm9yIChsZXQgbSBvZiBzZXQpIHtcbiAgICAgICAgICAgIHJlc3VsdC5hZGQobSlcbiAgICAgICAgfVxuICAgICAgICBmb3IgKGxldCBzIG9mIHNldHMpIHtcbiAgICAgICAgICAgIGZvciAobGV0IG0gb2Ygcykge1xuICAgICAgICAgICAgICAgIHJlc3VsdC5kZWxldGUobSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgfVxufVxuXG4vKiogU3RhdGljIG1ldGhvZHMgdG8gY29tcHV0ZSBhbmdsZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBBbmdsZSB7XG5cbiAgICBzdGF0aWMgbm9ybWFsaXplKGFuZ2xlKSB7XG4gICAgICAgIGxldCBUQVUgPSBNYXRoLlBJICogMi4wXG4gICAgICAgIHdoaWxlIChhbmdsZSA+IE1hdGguUEkpIHtcbiAgICAgICAgICAgIGFuZ2xlIC09IFRBVVxuICAgICAgICB9XG4gICAgICAgIHdoaWxlIChhbmdsZSA8IC1NYXRoLlBJKSB7XG4gICAgICAgICAgICBhbmdsZSArPSBUQVVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYW5nbGVcbiAgICB9XG5cbiAgICBzdGF0aWMgbm9ybWFsaXplMihhbmdsZSkge1xuICAgICAgICBsZXQgVEFVID0gTWF0aC5QSSAqIDIuMFxuICAgICAgICB3aGlsZSAoYW5nbGUgPiBUQVUpIHtcbiAgICAgICAgICAgIGFuZ2xlIC09IFRBVVxuICAgICAgICB9XG4gICAgICAgIHdoaWxlIChhbmdsZSA8IDApIHtcbiAgICAgICAgICAgIGFuZ2xlICs9IFRBVVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBhbmdsZVxuICAgIH1cblxuICAgIHN0YXRpYyBub3JtYWxpemVEZWdyZWUoYW5nbGUpIHtcbiAgICAgICAgbGV0IGZ1bGwgPSAzNjAuMFxuICAgICAgICB3aGlsZSAoYW5nbGUgPiAxODAuMCkge1xuICAgICAgICAgICAgYW5nbGUgLT0gZnVsbFxuICAgICAgICB9XG4gICAgICAgIHdoaWxlIChhbmdsZSA8IC0xODAuMCkge1xuICAgICAgICAgICAgYW5nbGUgKz0gZnVsbFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBhbmdsZVxuICAgIH1cblxuICAgIHN0YXRpYyBub3JtYWxpemVkRGlmZihhLCBiKSB7XG4gICAgICAgIHJldHVybiB0aGlzLm5vcm1hbGl6ZSh0aGlzLmRpZmYoYSwgYikpXG4gICAgfVxuXG4gICAgc3RhdGljIG5vcm1hbGl6ZWQyRGlmZihhLCBiKSB7XG4gICAgICAgIHJldHVybiB0aGlzLm5vcm1hbGl6ZTIodGhpcy5kaWZmKGEsIGIpKVxuICAgIH1cblxuICAgIHN0YXRpYyBkaWZmKGEsIGIpIHtcbiAgICAgICAgcmV0dXJuIE1hdGguYXRhbjIoTWF0aC5zaW4oYSAtIGIpLCBNYXRoLmNvcyhhIC0gYikpXG4gICAgfVxuXG4gICAgc3RhdGljIGRlZ3JlZTJyYWRpYW4oZGVncmVlKSB7XG4gICAgICAgIHJldHVybiBNYXRoLlBJICogZGVncmVlIC8gMTgwLjBcbiAgICB9XG5cbiAgICBzdGF0aWMgcmFkaWFuMmRlZ3JlZShyYWQpIHtcbiAgICAgICAgcmV0dXJuIDE4MC4wIC8gTWF0aC5QSSAqIHJhZFxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIEVsZW1lbnRzIHtcbiAgICBzdGF0aWMgc2V0U3R5bGUoZWxlbWVudCwgc3R5bGVzKSB7XG4gICAgICAgIGZvciAobGV0IGtleSBpbiBzdHlsZXMpIHtcbiAgICAgICAgICAgIGVsZW1lbnQuc3R5bGVba2V5XSA9IHN0eWxlc1trZXldXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdGF0aWMgYWRkQ2xhc3MoZWxlbWVudCwgY3NzQ2xhc3MpIHtcbiAgICAgICAgZWxlbWVudC5jbGFzc0xpc3QuYWRkKGNzc0NsYXNzKVxuICAgIH1cblxuICAgIHN0YXRpYyByZW1vdmVDbGFzcyhlbGVtZW50LCBjc3NDbGFzcykge1xuICAgICAgICBlbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoY3NzQ2xhc3MpXG4gICAgfVxuXG4gICAgc3RhdGljIHRvZ2dsZUNsYXNzKGVsZW1lbnQsIGNzc0NsYXNzKSB7XG4gICAgICAgIGVsZW1lbnQuY2xhc3NMaXN0LnRvZ2dsZShjc3NDbGFzcylcbiAgICB9XG5cbiAgICBzdGF0aWMgaGFzQ2xhc3MoZWxlbWVudCwgY3NzQ2xhc3MpIHtcbiAgICAgICAgcmV0dXJuIGVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKGNzc0NsYXNzKVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIE1hcFByb3h5IHtcbiAgICAvKiBUaGlzIGNsYXNzIGlzIG5lZWRlZCBpZiB3ZSB3YW50IHRvIHVzZSB0aGUgaW50ZXJhY3Rpb24gY2xhc3Nlc1xuICAgIGluIEZpcmVmb3ggNDUuOCBhbmQgbW9kZXJuIEJyb3dzZXJzLlxuXG4gICAgQSB3b3JrYXJvdW5kIGZvciBodHRwczovL2dpdGh1Yi5jb20vYmFiZWwvYmFiZWwvaXNzdWVzLzIzMzRcbiAgKi9cbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5tYXAgPSBuZXcgTWFwKClcbiAgICB9XG5cbiAgICBnZXQgc2l6ZSgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFwLnNpemVcbiAgICB9XG5cbiAgICBnZXQoa2V5KSB7XG4gICAgICAgIHJldHVybiB0aGlzLm1hcC5nZXQoa2V5KVxuICAgIH1cblxuICAgIHNldChrZXksIHZhbHVlKSB7XG4gICAgICAgIHJldHVybiB0aGlzLm1hcC5zZXQoa2V5LCB2YWx1ZSlcbiAgICB9XG5cbiAgICBkZWxldGUoa2V5KSB7XG4gICAgICAgIHJldHVybiB0aGlzLm1hcC5kZWxldGUoa2V5KVxuICAgIH1cblxuICAgIGNsZWFyKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5tYXAuY2xlYXIoKVxuICAgIH1cblxuICAgIGhhcyhrZXkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFwLmhhcyhrZXkpXG4gICAgfVxuXG4gICAga2V5cygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFwLmtleXMoKVxuICAgIH1cblxuICAgIHZhbHVlcygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFwLnZhbHVlcygpXG4gICAgfVxuXG4gICAgZW50cmllcygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFwLmVudHJpZXMoKVxuICAgIH1cblxuICAgIGZvckVhY2goZnVuYykge1xuICAgICAgICB0aGlzLm1hcC5mb3JFYWNoKGZ1bmMpXG4gICAgfVxufVxuXG4vKiBCYXNlZCBvbSBodHRwczovL2dpc3QuZ2l0aHViLmNvbS9jd2xlb25hcmQvZTEyNGQ2MzIzOGJkYTdhM2NiZmEgKi9cbmV4cG9ydCBjbGFzcyBQb2x5Z29uIHtcbiAgICAvKlxuICAgICAqICBUaGlzIGlzIHRoZSBQb2x5Z29uIGNvbnN0cnVjdG9yLiBBbGwgcG9pbnRzIGFyZSBjZW50ZXItcmVsYXRpdmUuXG4gICAgICovXG4gICAgY29uc3RydWN0b3IoY2VudGVyKSB7XG4gICAgICAgIHRoaXMucG9pbnRzID0gbmV3IEFycmF5KClcbiAgICAgICAgdGhpcy5jZW50ZXIgPSBjZW50ZXJcbiAgICB9XG5cbiAgICAvKlxuICAgICAqICBQb2ludCB4IGFuZCB5IHZhbHVlcyBzaG91bGQgYmUgcmVsYXRpdmUgdG8gdGhlIGNlbnRlci5cbiAgICAgKi9cbiAgICBhZGRQb2ludChwKSB7XG4gICAgICAgIHRoaXMucG9pbnRzLnB1c2gocClcbiAgICB9XG5cbiAgICAvKlxuICAgICAqICBQb2ludCB4IGFuZCB5IHZhbHVlcyBzaG91bGQgYmUgYWJzb2x1dGUgY29vcmRpbmF0ZXMuXG4gICAgICovXG4gICAgYWRkQWJzb2x1dGVQb2ludChwKSB7XG4gICAgICAgIHRoaXMucG9pbnRzLnB1c2goeyB4OiBwLnggLSB0aGlzLmNlbnRlci54LCB5OiBwLnkgLSB0aGlzLmNlbnRlci55IH0pXG4gICAgfVxuXG4gICAgLypcbiAgICAgKiBSZXR1cm5zIHRoZSBudW1iZXIgb2Ygc2lkZXMuIEVxdWFsIHRvIHRoZSBudW1iZXIgb2YgdmVydGljZXMuXG4gICAgICovXG4gICAgZ2V0TnVtYmVyT2ZTaWRlcygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucG9pbnRzLmxlbmd0aFxuICAgIH1cblxuICAgIC8qXG4gICAgICogcm90YXRlIHRoZSBwb2x5Z29uIGJ5IGEgbnVtYmVyIG9mIHJhZGlhbnNcbiAgICAgKi9cbiAgICByb3RhdGUocmFkcykge1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMucG9pbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBsZXQgeCA9IHRoaXMucG9pbnRzW2ldLnhcbiAgICAgICAgICAgIGxldCB5ID0gdGhpcy5wb2ludHNbaV0ueVxuICAgICAgICAgICAgdGhpcy5wb2ludHNbaV0ueCA9IE1hdGguY29zKHJhZHMpICogeCAtIE1hdGguc2luKHJhZHMpICogeVxuICAgICAgICAgICAgdGhpcy5wb2ludHNbaV0ueSA9IE1hdGguc2luKHJhZHMpICogeCArIE1hdGguY29zKHJhZHMpICogeVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLypcbiAgICAgKiAgVGhlIGRyYXcgZnVuY3Rpb24gdGFrZXMgYXMgYSBwYXJhbWV0ZXIgYSBDb250ZXh0IG9iamVjdCBmcm9tXG4gICAgICogIGEgQ2FudmFzIGVsZW1lbnQgYW5kIGRyYXdzIHRoZSBwb2x5Z29uIG9uIGl0LlxuICAgICAqL1xuICAgIGRyYXcoY29udGV4dCwgeyBsaW5lV2lkdGggPSAyLCBzdHJva2UgPSAnIzAwMDAwMCcsIGZpbGwgPSBudWxsIH0gPSB7fSkge1xuICAgICAgICBjb250ZXh0LmJlZ2luUGF0aCgpXG4gICAgICAgIGNvbnRleHQubW92ZVRvKFxuICAgICAgICAgICAgdGhpcy5wb2ludHNbMF0ueCArIHRoaXMuY2VudGVyLngsXG4gICAgICAgICAgICB0aGlzLnBvaW50c1swXS55ICsgdGhpcy5jZW50ZXIueVxuICAgICAgICApXG4gICAgICAgIGZvciAobGV0IGkgPSAxOyBpIDwgdGhpcy5wb2ludHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGNvbnRleHQubGluZVRvKFxuICAgICAgICAgICAgICAgIHRoaXMucG9pbnRzW2ldLnggKyB0aGlzLmNlbnRlci54LFxuICAgICAgICAgICAgICAgIHRoaXMucG9pbnRzW2ldLnkgKyB0aGlzLmNlbnRlci55XG4gICAgICAgICAgICApXG4gICAgICAgIH1cbiAgICAgICAgY29udGV4dC5jbG9zZVBhdGgoKVxuICAgICAgICBjb250ZXh0LmxpbmVXaWR0aCA9IGxpbmVXaWR0aFxuICAgICAgICBpZiAoc3Ryb2tlKSB7XG4gICAgICAgICAgICBjb250ZXh0LnN0cm9rZVN0eWxlID0gc3Ryb2tlXG4gICAgICAgICAgICBjb250ZXh0LnN0cm9rZSgpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGZpbGwpIHtcbiAgICAgICAgICAgIGNvbnRleHQuZmlsbFN0eWxlID0gZmlsbFxuICAgICAgICAgICAgY29udGV4dC5maWxsKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGFic29sdXRlUG9pbnRzKCkge1xuICAgICAgICBsZXQgcmVzdWx0ID0gbmV3IEFycmF5KClcbiAgICAgICAgZm9yIChsZXQgcCBvZiB0aGlzLnBvaW50cykge1xuICAgICAgICAgICAgcmVzdWx0LnB1c2goUG9pbnRzLmFkZChwLCB0aGlzLmNlbnRlcikpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgIH1cblxuICAgIGZsYXRBYnNvbHV0ZVBvaW50cygpIHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IG5ldyBBcnJheSgpXG4gICAgICAgIGZvciAobGV0IHAgb2YgdGhpcy5wb2ludHMpIHtcbiAgICAgICAgICAgIGxldCBhID0gUG9pbnRzLmFkZChwLCB0aGlzLmNlbnRlcilcbiAgICAgICAgICAgIHJlc3VsdC5wdXNoKGEueClcbiAgICAgICAgICAgIHJlc3VsdC5wdXNoKGEueSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgfVxuXG4gICAgLypcbiAgICAgKiAgVGhpcyBmdW5jdGlvbiByZXR1cm5zIHRydWUgaWYgdGhlIGdpdmVuIHBvaW50IGlzIGluc2lkZSB0aGUgcG9seWdvbixcbiAgICAgKiAgYW5kIGZhbHNlIG90aGVyd2lzZS5cbiAgICAgKi9cbiAgICBjb250YWluc1BvaW50KHBudCkge1xuICAgICAgICBsZXQgbnZlcnQgPSB0aGlzLnBvaW50cy5sZW5ndGhcbiAgICAgICAgbGV0IHRlc3R4ID0gcG50LnhcbiAgICAgICAgbGV0IHRlc3R5ID0gcG50LnlcblxuICAgICAgICBsZXQgdmVydHggPSBuZXcgQXJyYXkoKVxuICAgICAgICBmb3IgKGxldCBxID0gMDsgcSA8IHRoaXMucG9pbnRzLmxlbmd0aDsgcSsrKSB7XG4gICAgICAgICAgICB2ZXJ0eC5wdXNoKHRoaXMucG9pbnRzW3FdLnggKyB0aGlzLmNlbnRlci54KVxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IHZlcnR5ID0gbmV3IEFycmF5KClcbiAgICAgICAgZm9yIChsZXQgdyA9IDA7IHcgPCB0aGlzLnBvaW50cy5sZW5ndGg7IHcrKykge1xuICAgICAgICAgICAgdmVydHkucHVzaCh0aGlzLnBvaW50c1t3XS55ICsgdGhpcy5jZW50ZXIueSlcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBpLFxuICAgICAgICAgICAgaiA9IDBcbiAgICAgICAgbGV0IGMgPSBmYWxzZVxuICAgICAgICBmb3IgKGkgPSAwLCBqID0gbnZlcnQgLSAxOyBpIDwgbnZlcnQ7IGogPSBpKyspIHtcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICB2ZXJ0eVtpXSA+IHRlc3R5ICE9IHZlcnR5W2pdID4gdGVzdHkgJiZcbiAgICAgICAgICAgICAgICB0ZXN0eCA8XG4gICAgICAgICAgICAgICAgKHZlcnR4W2pdIC0gdmVydHhbaV0pICpcbiAgICAgICAgICAgICAgICAodGVzdHkgLSB2ZXJ0eVtpXSkgL1xuICAgICAgICAgICAgICAgICh2ZXJ0eVtqXSAtIHZlcnR5W2ldKSArXG4gICAgICAgICAgICAgICAgdmVydHhbaV1cbiAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICBjID0gIWNcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY1xuICAgIH1cblxuICAgIG11bHRpcGx5U2NhbGFyKHNjYWxlKSB7XG4gICAgICAgIGxldCBjZW50ZXIgPSBQb2ludHMubXVsdGlwbHlTY2FsYXIodGhpcy5jZW50ZXIsIHNjYWxlKVxuICAgICAgICBsZXQgY2xvbmUgPSBuZXcgUG9seWdvbihjZW50ZXIpXG4gICAgICAgIGZvciAobGV0IHAgb2YgdGhpcy5wb2ludHMpIHtcbiAgICAgICAgICAgIGNsb25lLmFkZFBvaW50KFBvaW50cy5tdWx0aXBseVNjYWxhcihwLCBzY2FsZSkpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNsb25lXG4gICAgfVxuXG4gICAgLypcbiAgICAgKiAgVG8gZGV0ZWN0IGludGVyc2VjdGlvbiB3aXRoIGFub3RoZXIgUG9seWdvbiBvYmplY3QsIHRoaXNcbiAgICAgKiAgZnVuY3Rpb24gdXNlcyB0aGUgU2VwYXJhdGluZyBBeGlzIFRoZW9yZW0uIEl0IHJldHVybnMgZmFsc2VcbiAgICAgKiAgaWYgdGhlcmUgaXMgbm8gaW50ZXJzZWN0aW9uLCBvciBhbiBvYmplY3QgaWYgdGhlcmUgaXMuIFRoZSBvYmplY3RcbiAgICAgKiAgY29udGFpbnMgMiBmaWVsZHMsIG92ZXJsYXAgYW5kIGF4aXMuIE1vdmluZyB0aGUgcG9seWdvbiBieSBvdmVybGFwXG4gICAgICogIG9uIGF4aXMgd2lsbCBnZXQgdGhlIHBvbHlnb25zIG91dCBvZiBpbnRlcnNlY3Rpb24uXG4gICAgICovXG4gICAgaW50ZXJzZWN0c1dpdGgob3RoZXIpIHtcbiAgICAgICAgbGV0IGF4aXMgPSB7IHg6IDAsIHk6IDAgfVxuICAgICAgICBsZXQgdG1wLCBtaW5BLCBtYXhBLCBtaW5CLCBtYXhCXG4gICAgICAgIGxldCBzaWRlLCBpXG4gICAgICAgIGxldCBzbWFsbGVzdCA9IG51bGxcbiAgICAgICAgbGV0IG92ZXJsYXAgPSA5OTk5OTk5OVxuXG4gICAgICAgIC8qIHRlc3QgcG9seWdvbiBBJ3Mgc2lkZXMgKi9cbiAgICAgICAgZm9yIChzaWRlID0gMDsgc2lkZSA8IHRoaXMuZ2V0TnVtYmVyT2ZTaWRlcygpOyBzaWRlKyspIHtcbiAgICAgICAgICAgIC8qIGdldCB0aGUgYXhpcyB0aGF0IHdlIHdpbGwgcHJvamVjdCBvbnRvICovXG4gICAgICAgICAgICBpZiAoc2lkZSA9PSAwKSB7XG4gICAgICAgICAgICAgICAgYXhpcy54ID1cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wb2ludHNbdGhpcy5nZXROdW1iZXJPZlNpZGVzKCkgLSAxXS55IC1cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wb2ludHNbMF0ueVxuICAgICAgICAgICAgICAgIGF4aXMueSA9XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucG9pbnRzWzBdLnggLVxuICAgICAgICAgICAgICAgICAgICB0aGlzLnBvaW50c1t0aGlzLmdldE51bWJlck9mU2lkZXMoKSAtIDFdLnhcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgYXhpcy54ID0gdGhpcy5wb2ludHNbc2lkZSAtIDFdLnkgLSB0aGlzLnBvaW50c1tzaWRlXS55XG4gICAgICAgICAgICAgICAgYXhpcy55ID0gdGhpcy5wb2ludHNbc2lkZV0ueCAtIHRoaXMucG9pbnRzW3NpZGUgLSAxXS54XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8qIG5vcm1hbGl6ZSB0aGUgYXhpcyAqL1xuICAgICAgICAgICAgdG1wID0gTWF0aC5zcXJ0KGF4aXMueCAqIGF4aXMueCArIGF4aXMueSAqIGF4aXMueSlcbiAgICAgICAgICAgIGF4aXMueCAvPSB0bXBcbiAgICAgICAgICAgIGF4aXMueSAvPSB0bXBcblxuICAgICAgICAgICAgLyogcHJvamVjdCBwb2x5Z29uIEEgb250byBheGlzIHRvIGRldGVybWluZSB0aGUgbWluL21heCAqL1xuICAgICAgICAgICAgbWluQSA9IG1heEEgPSB0aGlzLnBvaW50c1swXS54ICogYXhpcy54ICsgdGhpcy5wb2ludHNbMF0ueSAqIGF4aXMueVxuICAgICAgICAgICAgZm9yIChpID0gMTsgaSA8IHRoaXMuZ2V0TnVtYmVyT2ZTaWRlcygpOyBpKyspIHtcbiAgICAgICAgICAgICAgICB0bXAgPSB0aGlzLnBvaW50c1tpXS54ICogYXhpcy54ICsgdGhpcy5wb2ludHNbaV0ueSAqIGF4aXMueVxuICAgICAgICAgICAgICAgIGlmICh0bXAgPiBtYXhBKSBtYXhBID0gdG1wXG4gICAgICAgICAgICAgICAgZWxzZSBpZiAodG1wIDwgbWluQSkgbWluQSA9IHRtcFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLyogY29ycmVjdCBmb3Igb2Zmc2V0ICovXG4gICAgICAgICAgICB0bXAgPSB0aGlzLmNlbnRlci54ICogYXhpcy54ICsgdGhpcy5jZW50ZXIueSAqIGF4aXMueVxuICAgICAgICAgICAgbWluQSArPSB0bXBcbiAgICAgICAgICAgIG1heEEgKz0gdG1wXG5cbiAgICAgICAgICAgIC8qIHByb2plY3QgcG9seWdvbiBCIG9udG8gYXhpcyB0byBkZXRlcm1pbmUgdGhlIG1pbi9tYXggKi9cbiAgICAgICAgICAgIG1pbkIgPSBtYXhCID1cbiAgICAgICAgICAgICAgICBvdGhlci5wb2ludHNbMF0ueCAqIGF4aXMueCArIG90aGVyLnBvaW50c1swXS55ICogYXhpcy55XG4gICAgICAgICAgICBmb3IgKGkgPSAxOyBpIDwgb3RoZXIuZ2V0TnVtYmVyT2ZTaWRlcygpOyBpKyspIHtcbiAgICAgICAgICAgICAgICB0bXAgPSBvdGhlci5wb2ludHNbaV0ueCAqIGF4aXMueCArIG90aGVyLnBvaW50c1tpXS55ICogYXhpcy55XG4gICAgICAgICAgICAgICAgaWYgKHRtcCA+IG1heEIpIG1heEIgPSB0bXBcbiAgICAgICAgICAgICAgICBlbHNlIGlmICh0bXAgPCBtaW5CKSBtaW5CID0gdG1wXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvKiBjb3JyZWN0IGZvciBvZmZzZXQgKi9cbiAgICAgICAgICAgIHRtcCA9IG90aGVyLmNlbnRlci54ICogYXhpcy54ICsgb3RoZXIuY2VudGVyLnkgKiBheGlzLnlcbiAgICAgICAgICAgIG1pbkIgKz0gdG1wXG4gICAgICAgICAgICBtYXhCICs9IHRtcFxuXG4gICAgICAgICAgICAvKiB0ZXN0IGlmIGxpbmVzIGludGVyc2VjdCwgaWYgbm90LCByZXR1cm4gZmFsc2UgKi9cbiAgICAgICAgICAgIGlmIChtYXhBIDwgbWluQiB8fCBtaW5BID4gbWF4Qikge1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBsZXQgbyA9IG1heEEgPiBtYXhCID8gbWF4QiAtIG1pbkEgOiBtYXhBIC0gbWluQlxuICAgICAgICAgICAgICAgIGlmIChvIDwgb3ZlcmxhcCkge1xuICAgICAgICAgICAgICAgICAgICBvdmVybGFwID0gb1xuICAgICAgICAgICAgICAgICAgICBzbWFsbGVzdCA9IHsgeDogYXhpcy54LCB5OiBheGlzLnkgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8qIHRlc3QgcG9seWdvbiBCJ3Mgc2lkZXMgKi9cbiAgICAgICAgZm9yIChzaWRlID0gMDsgc2lkZSA8IG90aGVyLmdldE51bWJlck9mU2lkZXMoKTsgc2lkZSsrKSB7XG4gICAgICAgICAgICAvKiBnZXQgdGhlIGF4aXMgdGhhdCB3ZSB3aWxsIHByb2plY3Qgb250byAqL1xuICAgICAgICAgICAgaWYgKHNpZGUgPT0gMCkge1xuICAgICAgICAgICAgICAgIGF4aXMueCA9XG4gICAgICAgICAgICAgICAgICAgIG90aGVyLnBvaW50c1tvdGhlci5nZXROdW1iZXJPZlNpZGVzKCkgLSAxXS55IC1cbiAgICAgICAgICAgICAgICAgICAgb3RoZXIucG9pbnRzWzBdLnlcbiAgICAgICAgICAgICAgICBheGlzLnkgPVxuICAgICAgICAgICAgICAgICAgICBvdGhlci5wb2ludHNbMF0ueCAtXG4gICAgICAgICAgICAgICAgICAgIG90aGVyLnBvaW50c1tvdGhlci5nZXROdW1iZXJPZlNpZGVzKCkgLSAxXS54XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGF4aXMueCA9IG90aGVyLnBvaW50c1tzaWRlIC0gMV0ueSAtIG90aGVyLnBvaW50c1tzaWRlXS55XG4gICAgICAgICAgICAgICAgYXhpcy55ID0gb3RoZXIucG9pbnRzW3NpZGVdLnggLSBvdGhlci5wb2ludHNbc2lkZSAtIDFdLnhcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLyogbm9ybWFsaXplIHRoZSBheGlzICovXG4gICAgICAgICAgICB0bXAgPSBNYXRoLnNxcnQoYXhpcy54ICogYXhpcy54ICsgYXhpcy55ICogYXhpcy55KVxuICAgICAgICAgICAgYXhpcy54IC89IHRtcFxuICAgICAgICAgICAgYXhpcy55IC89IHRtcFxuXG4gICAgICAgICAgICAvKiBwcm9qZWN0IHBvbHlnb24gQSBvbnRvIGF4aXMgdG8gZGV0ZXJtaW5lIHRoZSBtaW4vbWF4ICovXG4gICAgICAgICAgICBtaW5BID0gbWF4QSA9IHRoaXMucG9pbnRzWzBdLnggKiBheGlzLnggKyB0aGlzLnBvaW50c1swXS55ICogYXhpcy55XG4gICAgICAgICAgICBmb3IgKGkgPSAxOyBpIDwgdGhpcy5nZXROdW1iZXJPZlNpZGVzKCk7IGkrKykge1xuICAgICAgICAgICAgICAgIHRtcCA9IHRoaXMucG9pbnRzW2ldLnggKiBheGlzLnggKyB0aGlzLnBvaW50c1tpXS55ICogYXhpcy55XG4gICAgICAgICAgICAgICAgaWYgKHRtcCA+IG1heEEpIG1heEEgPSB0bXBcbiAgICAgICAgICAgICAgICBlbHNlIGlmICh0bXAgPCBtaW5BKSBtaW5BID0gdG1wXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvKiBjb3JyZWN0IGZvciBvZmZzZXQgKi9cbiAgICAgICAgICAgIHRtcCA9IHRoaXMuY2VudGVyLnggKiBheGlzLnggKyB0aGlzLmNlbnRlci55ICogYXhpcy55XG4gICAgICAgICAgICBtaW5BICs9IHRtcFxuICAgICAgICAgICAgbWF4QSArPSB0bXBcblxuICAgICAgICAgICAgLyogcHJvamVjdCBwb2x5Z29uIEIgb250byBheGlzIHRvIGRldGVybWluZSB0aGUgbWluL21heCAqL1xuICAgICAgICAgICAgbWluQiA9IG1heEIgPVxuICAgICAgICAgICAgICAgIG90aGVyLnBvaW50c1swXS54ICogYXhpcy54ICsgb3RoZXIucG9pbnRzWzBdLnkgKiBheGlzLnlcbiAgICAgICAgICAgIGZvciAoaSA9IDE7IGkgPCBvdGhlci5nZXROdW1iZXJPZlNpZGVzKCk7IGkrKykge1xuICAgICAgICAgICAgICAgIHRtcCA9IG90aGVyLnBvaW50c1tpXS54ICogYXhpcy54ICsgb3RoZXIucG9pbnRzW2ldLnkgKiBheGlzLnlcbiAgICAgICAgICAgICAgICBpZiAodG1wID4gbWF4QikgbWF4QiA9IHRtcFxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKHRtcCA8IG1pbkIpIG1pbkIgPSB0bXBcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8qIGNvcnJlY3QgZm9yIG9mZnNldCAqL1xuICAgICAgICAgICAgdG1wID0gb3RoZXIuY2VudGVyLnggKiBheGlzLnggKyBvdGhlci5jZW50ZXIueSAqIGF4aXMueVxuICAgICAgICAgICAgbWluQiArPSB0bXBcbiAgICAgICAgICAgIG1heEIgKz0gdG1wXG5cbiAgICAgICAgICAgIC8qIHRlc3QgaWYgbGluZXMgaW50ZXJzZWN0LCBpZiBub3QsIHJldHVybiBmYWxzZSAqL1xuICAgICAgICAgICAgaWYgKG1heEEgPCBtaW5CIHx8IG1pbkEgPiBtYXhCKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGxldCBvID0gbWF4QSA+IG1heEIgPyBtYXhCIC0gbWluQSA6IG1heEEgLSBtaW5CXG4gICAgICAgICAgICAgICAgaWYgKG8gPCBvdmVybGFwKSB7XG4gICAgICAgICAgICAgICAgICAgIG92ZXJsYXAgPSBvXG4gICAgICAgICAgICAgICAgICAgIHNtYWxsZXN0ID0geyB4OiBheGlzLngsIHk6IGF4aXMueSB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7IG92ZXJsYXA6IG92ZXJsYXAgKyAwLjAwMSwgYXhpczogc21hbGxlc3QgfVxuICAgIH1cblxuICAgIHN0YXRpYyBmcm9tUG9pbnRzKHBvaW50cykge1xuICAgICAgICBsZXQgbWluID0geyB4OiBOdW1iZXIuTUFYX1ZBTFVFLCB5OiBOdW1iZXIuTUFYX1ZBTFVFIH1cbiAgICAgICAgbGV0IG1heCA9IHsgeDogTnVtYmVyLk1JTl9WQUxVRSwgeTogTnVtYmVyLk1JTl9WQUxVRSB9XG4gICAgICAgIGZvciAobGV0IHAgb2YgcG9pbnRzKSB7XG4gICAgICAgICAgICBtaW4ueCA9IE1hdGgubWluKHAueCwgbWluLngpXG4gICAgICAgICAgICBtYXgueCA9IE1hdGgubWF4KHAueCwgbWF4LngpXG4gICAgICAgICAgICBtaW4ueSA9IE1hdGgubWluKHAueSwgbWluLnkpXG4gICAgICAgICAgICBtYXgueSA9IE1hdGgubWF4KHAueSwgbWF4LnkpXG4gICAgICAgIH1cbiAgICAgICAgbGV0IGNlbnRlciA9IFBvaW50cy5tZWFuKG1pbiwgbWF4KVxuICAgICAgICBsZXQgcG9seWdvbiA9IG5ldyBQb2x5Z29uKGNlbnRlcilcbiAgICAgICAgZm9yIChsZXQgcCBvZiBwb2ludHMpIHtcbiAgICAgICAgICAgIHBvbHlnb24uYWRkQWJzb2x1dGVQb2ludChwKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBwb2x5Z29uXG4gICAgfVxufVxuXG5cbi8qKlxuICogVXRpbCBmdW5jdGlvbnMgdG8gZGVhbCB3aXRoIERPTVJlY3RzLlxuICovXG5leHBvcnQgY2xhc3MgUmVjdCB7XG5cblxuICAgIC8qKlxuICAgICAqIFRlc3QgaWYgYSBnaXZlbiBwb2ludCBpcyBjb250YWluZWQgYnkgdGhlIHByb3ZpZGVkIFJlY3QuXG4gICAgICpcbiAgICAgKiBAc3RhdGljXG4gICAgICogQHBhcmFtIHtET01SZWN0fSByZWN0IC0gUmVjdGFuZ2xlIHRvIGNoZWNrIHRoZSBjb2xsaXNpb24gd2l0aC5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBwb2ludCAtIFBvaW50IHRoYXQgc2hvdWxkIGJlIHRlc3RlZC5cbiAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHBvaW50IGlzIGluc2lkZSBvZiByZWN0LCBvdGhlcndpc2UgZmFsc2UuXG4gICAgICogQG1lbWJlcm9mIFJlY3RcbiAgICAgKi9cbiAgICBzdGF0aWMgY29udGFpbnMocmVjdCwgcG9pbnQpIHtcbiAgICAgICAgcmV0dXJuIChwb2ludC54ID4gcmVjdC5sZWZ0ICYmXG4gICAgICAgICAgICBwb2ludC54IDwgcmVjdC54ICsgcmVjdC5yaWdodFxuICAgICAgICAgICAgJiYgcG9pbnQueSA+IHJlY3QudG9wICYmIHBvaW50LnkgPCByZWN0LmJvdHRvbSlcbiAgICB9XG5cblxuICAgIC8qKlxuICAgICAqUmV0dXJucyB0aGUgcG9zaXRpb24gb2YgYW4gcmVjdCBhcyBwb2ludCBvYmplY3QuXG4gICAgICpcbiAgICAgKiBAc3RhdGljXG4gICAgICogQHBhcmFtIHtSZWN0fSByZWN0IC0gVGhlIHJlY3RhbmdsZSB3ZSB3YW50IHRvIGdldCB0aGUgcG9zaXRpb24gZnJvbS5cbiAgICAgKiBAcmV0dXJucyB7UG9pbnR9IC0gUmV0dXJucyB0aGUgcG9zaXRpb24gYXMgUG9pbnQuXG4gICAgICogQG1lbWJlcm9mIFJlY3RcbiAgICAgKi9cbiAgICBzdGF0aWMgZ2V0UG9zaXRpb24ocmVjdCkge1xuICAgICAgICByZXR1cm4geyB4OiByZWN0LngsIHk6IHJlY3QueSB9XG4gICAgfVxufVxuXG4vKiogU3RyaW5nIHV0aWxpdHkgZnVuY3Rpb25zICovXG5cbmV4cG9ydCBjbGFzcyBTdHJpbmdzIHtcblxuICAgIHN0YXRpYyB0b1VwcGVyQ2FzZUZpcnN0Q2hhcihzdHIpIHtcbiAgICAgICAgcmV0dXJuIHN0ci5zdWJzdHIoMCwgMSkudG9VcHBlckNhc2UoKSArIHN0ci5zdWJzdHIoMSlcbiAgICB9XG5cbiAgICBzdGF0aWMgdG9Mb3dlckNhc2VGaXJzdENoYXIoc3RyKSB7XG4gICAgICAgIHJldHVybiBzdHIuc3Vic3RyKDAsIDEpLnRvTG93ZXJDYXNlKCkgKyBzdHIuc3Vic3RyKDEpXG4gICAgfVxuXG4gICAgc3RhdGljIHRvVXBwZXJDYXNlRWFjaFdvcmQoc3RyLCBkZWxpbSA9ICcgJykge1xuICAgICAgICByZXR1cm4gc3RyLnNwbGl0KGRlbGltKS5tYXAoKHYpID0+IHYudG9VcHBlckNhc2VGaXJzdENoYXIoKSkuam9pbihkZWxpbSlcbiAgICB9XG5cbiAgICBzdGF0aWMgdG9Mb3dlckNhc2VFYWNoV29yZChzdHIsIGRlbGltID0gJyAnKSB7XG4gICAgICAgIHJldHVybiBzdHIuc3BsaXQoZGVsaW0pLm1hcCgodikgPT4gdi50b0xvd2VyQ2FzZUZpcnN0Q2hhcigpKS5qb2luKGRlbGltKVxuICAgIH1cblxufVxuXG5cblxuIiwiLyogZ2xvYmFsIGFwb2xsbywgc3Vic2NyaXB0aW9ucywgZ3FsICovXG5cbmltcG9ydCBUaGVtZSBmcm9tICcuL3RoZW1lLmpzJ1xuaW1wb3J0IFByb2dyZXNzIGZyb20gJy4vcHJvZ3Jlc3MuanMnXG5pbXBvcnQgTW9kYWwgZnJvbSAnLi9tb2RhbC5qcydcbmltcG9ydCBNZXNzYWdlIGZyb20gJy4vbWVzc2FnZS5qcydcbmltcG9ydCB7ZGVib3VuY2V9IGZyb20gJy4uL3V0aWxzLmpzJ1xuXG4vKipcbiAqIEEgc3BlY2lhbCBJbnRlcmFjdGlvbk1hbmFnZXIgZm9yIGZ1bGxzY3JlZW4gYXBwcywgd2hpY2ggbWF5XG4gKiBnbyBiZXlvbmQgdGhlIGxpbWl0cyBvZiBXZWJHTCBkcmF3aW5nIGJ1ZmZlcnMuIE9uIFNhZmFyaSBhbmQgQ2hyb21lXG4gKiB0aGUgZHJhd2luZyBidWZmZXJzIGFyZSBsaW1pdGVkIHRvIDQwOTYgaW4gd2lkdGggKFNhZmFyaSkgb3IgNDA5Nng0MDk2XG4gKiBpbiB0b3RhbCBidWZmZXIgc2l6ZSAoQ2hyb21lKS4gVGhlIG9yaWdpbmFsIEludGVyYWN0aW9uTWFuYWdlci5tYXBQb3NpdGlvblRvUG9pbnRcbiAqIGRvZXMgbm90IHdvcmsgd2l0aCB0aGVzZSBleHRyZW1lIHNpemVzIHdoaWNoIG1haW5seSBvY2N1ciBpZiBsYXJnZVxuICogcmV0aW5hIGRpc3BsYXlzICg+PSA0SykgYXJlIHVzZWQgd2l0aCBkZXZpY2VQaXhlbFJhdGlvID4gMS5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQGNsYXNzXG4gKiBAZXh0ZW5kcyBQSVhJLmludGVyYWN0aW9uLkludGVyYWN0aW9uTWFuYWdlclxuICogQHNlZSB7QGxpbmsgaHR0cDovL3BpeGlqcy5kb3dubG9hZC9kZXYvZG9jcy9QSVhJLmludGVyYWN0aW9uLkludGVyYWN0aW9uTWFuYWdlci5odG1sfFBJWEkuaW50ZXJhY3Rpb24uSW50ZXJhY3Rpb25NYW5hZ2VyfVxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjk3MTA2OTYvd2ViZ2wtZHJhd2luZy1idWZmZXItc2l6ZS1kb2VzLW5vdC1lcXVhbC1jYW52YXMtc2l6ZX1cbiAqL1xuY2xhc3MgRnVsbHNjcmVlbkludGVyYWN0aW9uTWFuYWdlciBleHRlbmRzIFBJWEkuaW50ZXJhY3Rpb24uSW50ZXJhY3Rpb25NYW5hZ2VyIHtcblxuICAgIG1hcFBvc2l0aW9uVG9Qb2ludChwb2ludCwgeCwgeSkge1xuICAgICAgICBsZXQgcmVzb2x1dGlvbiA9IHRoaXMucmVuZGVyZXIucmVzb2x1dGlvblxuICAgICAgICBsZXQgZXh0ZW5kV2lkdGggPSAxLjBcbiAgICAgICAgbGV0IGV4dGVuZEhlaWdodCA9IDEuMFxuICAgICAgICBsZXQgZHkgPSAwXG4gICAgICAgIGxldCBjYW52YXMgPSB0aGlzLnJlbmRlcmVyLnZpZXdcbiAgICAgICAgbGV0IGNvbnRleHQgPSBjYW52YXMuZ2V0Q29udGV4dCgnd2ViZ2wnKVxuICAgICAgICBpZiAoY29udGV4dC5kcmF3aW5nQnVmZmVyV2lkdGggPCBjYW52YXMud2lkdGggfHxcbiAgICAgICAgICAgIGNvbnRleHQuZHJhd2luZ0J1ZmZlckhlaWdodCA8IGNhbnZhcy5oZWlnaHQpIHtcbiAgICAgICAgICAgIGV4dGVuZFdpZHRoID0gY29udGV4dC5kcmF3aW5nQnVmZmVyV2lkdGggLyBjYW52YXMud2lkdGhcbiAgICAgICAgICAgIGV4dGVuZEhlaWdodCA9IGNvbnRleHQuZHJhd2luZ0J1ZmZlckhlaWdodCAvIGNhbnZhcy5oZWlnaHRcbiAgICAgICAgICAgIC8vZHggPSB3YW50ZWRXaWR0aCAtIGNvbnRleHQuZHJhd2luZ0J1ZmZlcldpZHRoXG4gICAgICAgICAgICBkeSA9IChjYW52YXMuaGVpZ2h0IC0gY29udGV4dC5kcmF3aW5nQnVmZmVySGVpZ2h0KSAvIHJlc29sdXRpb25cbiAgICAgICAgfVxuICAgICAgICB4ICo9IGV4dGVuZFdpZHRoXG4gICAgICAgIHkgKj0gZXh0ZW5kSGVpZ2h0XG5cbiAgICAgICAgc3VwZXIubWFwUG9zaXRpb25Ub1BvaW50KHBvaW50LCB4LCB5ICsgZHkpXG4gICAgfVxufVxuXG4vKipcbiAqIFRoZSBjbGFzcyBQaXhpQXBwIGV4dGVuZHMgdGhlIGNsYXNzIFBJWEkuQXBwbGljYXRpb25cbiAqIGJ5IHNldmVyYWwgZnVuY3Rpb25zIGFuZCBtYWtlcyBtZWFuaW5nZnVsIHByZS1hc3N1bXB0aW9ucy5cbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gQ3JlYXRlIHRoZSBhcHBcbiAqIGNvbnN0IGFwcCA9IG5ldyBQSVhJQXBwKHtcbiAqICAgICB2aWV3OiBjYW52YXMsXG4gKiAgICAgd2lkdGg6IDQ1MCxcbiAqICAgICBoZWlnaHQ6IDE1MCxcbiAqICAgICBmcHNMb2dnaW5nOiB0cnVlLFxuICogICAgIHRoZW1lOiAnbGlnaHQnLFxuICogICAgIHRyYW5zcGFyZW50OiBmYWxzZVxuICogfSkuc2V0dXAoKS5ydW4oKVxuICpcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgUElYSS5BcHBsaWNhdGlvblxuICogQHNlZSB7QGxpbmsgaHR0cDovL3BpeGlqcy5kb3dubG9hZC9kZXYvZG9jcy9QSVhJLkFwcGxpY2F0aW9uLmh0bWx8UElYSS5BcHBsaWNhdGlvbn1cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgUElYSUFwcCBleHRlbmRzIFBJWEkuQXBwbGljYXRpb24ge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhIFBpeGlBcHAuXG4gICAgICpcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHM9e31dIC0gQW4gb3B0aW9ucyBvYmplY3QuIFRoZSBmb2xsb3dpbmcgb3B0aW9ucyBjYW4gYmUgc2V0OlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy53aWR0aF0gLSBUaGUgd2lkdGggb2YgdGhlIHJlbmRlcmVyLiBJZiBubyBzZXQsIHRoZSBhcHBsaWNhdGlvbiB3aWxsIHJ1biBpbiBmdWxsc2NyZWVuLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5oZWlnaHRdIC0gVGhlIGhlaWdodCBvZiB0aGUgcmVuZGVyZXIuIElmIG5vIHNldCwgdGhlIGFwcGxpY2F0aW9uIHdpbGwgcnVuIGluIGZ1bGxzY3JlZW4uXG4gICAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gW29wdHMudmlld10gLSBUaGUgY2FudmFzIEhUTUwgZWxlbWVudC4gSWYgbm90IHNldCwgYSByZW5kZXItZWxlbWVudCBpcyBhZGRlZCBpbnNpZGUgdGhlIGJvZHkuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy50cmFuc3BhcmVudD10cnVlXSAtIFNob3VsZCB0aGUgcmVuZGVyIHZpZXcgYmUgdHJhbnNwYXJlbnQ/XG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5hbnRpYWxpYXM9dHJ1ZV0gLSBTZXRzIGFudGlhbGlhcyAob25seSBhcHBsaWNhYmxlIGluIGNocm9tZSBhdCB0aGUgbW9tZW50KS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMucmVzb2x1dGlvbj13aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyB8IDFdIC0gVGhlIHJlc29sdXRpb24gLyBkZXZpY2UgcGl4ZWwgcmF0aW8gb2YgdGhlIHJlbmRlcmVyLCByZXRpbmEgd291bGQgYmUgMi5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmF1dG9SZXNpemU9dHJ1ZV0gLSBTaG91bGQgdGhlIGNhbnZhcy1lbGVtZW50IGJlIHJlc2l6ZWQgYXV0b21hdGljYWxseSBpZiB0aGUgcmVzb2x1dGlvbiB3YXMgc2V0P1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5iYWNrZ3JvdW5kQ29sb3I9MHgyODI4MjhdIC0gVGhlIGNvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfFRoZW1lfSBbb3B0cy50aGVtZT1kYXJrXSAtIFRoZSBuYW1lIG9mIHRoZSB0aGVtZSAoZGFyaywgbGlnaHQsIHJlZCkgb3IgYSBUaGVtZSBvYmplY3QgdG8gdXNlIGZvciBzdHlsaW5nLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuZnBzTG9nZ2luZz1mYWxzZV0gLSBJZiBzZXQgdG8gdHJ1ZSwgdGhlIGN1cnJlbnQgZnJhbWVzIHBlciBzZWNvbmQgYXJlIGRpc3BsYXllZCBpbiB0aGUgdXBwZXIgbGVmdCBjb3JuZXIuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzLnByb2dyZXNzPXt9XSAtIENhbiBiZSB1c2VkIHRvIGFkZCBvcHRpb25zIHRvIHRoZSBwcm9ncmVzcyBiYXIuIFNlZSBjbGFzcyBQcm9ncmVzcyBmb3IgbW9yZSBpbmZvcm1hdGlvbnMuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5mb3JjZUNhbnZhcz1mYWxzZV0gLSBQcmV2ZW50cyBzZWxlY3Rpb24gb2YgV2ViR0wgcmVuZGVyZXIsIGV2ZW4gaWYgc3VjaCBpcyBwcmVzZW50LlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMucm91bmRQaXhlbHM9dHJ1ZV0gLSBBbGlnbiBQSVhJLkRpc3BsYXlPYmplY3QgY29vcmRpbmF0ZXMgdG8gc2NyZWVuIHJlc29sdXRpb24uXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5tb25rZXlQYXRjaE1hcHBpbmc9dHJ1ZV0gLSBNb25rZXkgcGF0Y2ggZm9yIGNhbnZhcyBmdWxsc2NyZWVuIHN1cHBvcnQgb24gbGFyZ2UgZGlzcGxheXMuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5hZGFwdGl2ZT10cnVlXSAtIEFkZHMgR3JhcGhpY3MgYWRhcHRpdmUgY2FsY3VsYXRpb24gb2YgcXVhZHJhdGljIGN1cnZlIGFuZCBhcmMgc3ViZGl2aXNpb24uXG4gICAgICovXG4gICAgY29uc3RydWN0b3Ioe1xuICAgICAgICB3aWR0aCA9IG51bGwsIGhlaWdodCA9IG51bGwsIHZpZXcgPSBudWxsLFxuICAgICAgICB0cmFuc3BhcmVudCA9IHRydWUsIGJhY2tncm91bmRDb2xvciA9IDB4MjgyODI4LCB0aGVtZSA9ICdkYXJrJyxcbiAgICAgICAgYW50aWFsaWFzID0gdHJ1ZSwgcmVzb2x1dGlvbiA9IHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvIHx8IDEsIGF1dG9SZXNpemUgPSB0cnVlLFxuICAgICAgICBmcHNMb2dnaW5nID0gZmFsc2UsIHByb2dyZXNzID0ge30sIGZvcmNlQ2FudmFzID0gZmFsc2UsIHJvdW5kUGl4ZWxzID0gdHJ1ZSwgbW9ua2V5UGF0Y2hNYXBwaW5nID0gdHJ1ZSwgYWRhcHRpdmUgPSB0cnVlLFxuICAgICAgICBncmFwaHFsID0gZmFsc2UgfSkge1xuXG4gICAgICAgIGNvbnN0IGZ1bGxTY3JlZW4gPSAhd2lkdGggfHwgIWhlaWdodFxuXG4gICAgICAgIGlmIChmdWxsU2NyZWVuKSB7XG4gICAgICAgICAgICB3aWR0aCA9IHdpbmRvdy5pbm5lcldpZHRoXG4gICAgICAgICAgICBoZWlnaHQgPSB3aW5kb3cuaW5uZXJIZWlnaHRcbiAgICAgICAgfVxuXG4gICAgICAgIHN1cGVyKHtcbiAgICAgICAgICAgIHZpZXc6IHZpZXcsXG4gICAgICAgICAgICB3aWR0aDogd2lkdGgsXG4gICAgICAgICAgICBoZWlnaHQ6IGhlaWdodCxcbiAgICAgICAgICAgIHRyYW5zcGFyZW50OiB0cmFuc3BhcmVudCxcbiAgICAgICAgICAgIGFudGlhbGlhczogYW50aWFsaWFzLFxuICAgICAgICAgICAgcmVzb2x1dGlvbjogcmVzb2x1dGlvbixcbiAgICAgICAgICAgIGF1dG9SZXNpemU6IGF1dG9SZXNpemUsXG4gICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3I6IGJhY2tncm91bmRDb2xvcixcbiAgICAgICAgICAgIHJvdW5kUGl4ZWxzOiByb3VuZFBpeGVscyxcbiAgICAgICAgICAgIGZvcmNlQ2FudmFzOiBmb3JjZUNhbnZhc1xuICAgICAgICB9KVxuXG4gICAgICAgIHRoaXMud2lkdGggPSB3aWR0aFxuICAgICAgICB0aGlzLmhlaWdodCA9IGhlaWdodFxuICAgICAgICB0aGlzLnRoZW1lID0gVGhlbWUuZnJvbVN0cmluZyh0aGVtZSlcbiAgICAgICAgdGhpcy5mcHNMb2dnaW5nID0gZnBzTG9nZ2luZ1xuICAgICAgICB0aGlzLnByb2dyZXNzT3B0cyA9IHByb2dyZXNzXG4gICAgICAgIHRoaXMuZnVsbFNjcmVlbiA9IGZ1bGxTY3JlZW5cbiAgICAgICAgdGhpcy5vcmllbnQgPSBudWxsXG4gICAgICAgIHRoaXMub3JpZ2luYWxNYXBQb3NpdGlvblRvUG9pbnQgPSBudWxsXG4gICAgICAgIHRoaXMubW9ua2V5UGF0Y2hNYXBwaW5nID0gbW9ua2V5UGF0Y2hNYXBwaW5nXG4gICAgICAgIFBJWEkuR3JhcGhpY3MuQ1VSVkVTLmFkYXB0aXZlID0gYWRhcHRpdmVcbiAgICAgICAgdGhpcy5ncmFwaHFsID0gZ3JhcGhxbFxuICAgICAgICBpZiAoZnVsbFNjcmVlbiB8fCBhdXRvUmVzaXplKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnQXBwIGlzIGluIGZ1bGxTY3JlZW4gbW9kZSBvciBhdXRvUmVzaXplIG1vZGUnKVxuICAgICAgICAgICAgY29uc3QgcmVzaXplRGVib3VuY2VkID0gZGVib3VuY2UoZXZlbnQgPT4gdGhpcy5yZXNpemUoZXZlbnQpLCA1MClcbiAgICAgICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCByZXNpemVEZWJvdW5jZWQpXG4gICAgICAgICAgICBkb2N1bWVudC5ib2R5LmFkZEV2ZW50TGlzdGVuZXIoJ29yaWVudGF0aW9uY2hhbmdlJywgdGhpcy5jaGVja09yaWVudGF0aW9uLmJpbmQodGhpcykpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1vbmtleVBhdGNoTWFwcGluZykge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1VzaW5nIG1vbmtleSBwYXRjaGVkIGNvb3JkaW5hdGUgbWFwcGluZycpXG4gICAgICAgICAgICAvLyBQbHVnZ2luIHRoZSBzcGVjaWFsaXp0aW9uIGRvZXMgbm90IHdvcmsuIE1vbmtleSBwYXRjaGluZyBkb2VzXG4gICAgICAgICAgICAvLyB0aGlzLnJlbmRlcmVyLnBsdWdpbnMuaW50ZXJhY3Rpb24gPSBuZXcgRnVsbHNjcmVlbkludGVyYWN0aW9uTWFuYWdlcih0aGlzLnJlbmRlcmVyKVxuICAgICAgICAgICAgdGhpcy5tb25rZXlQYXRjaFBpeGlNYXBwaW5nKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEV4dHJhIHNldHVwIG1ldGhvZCB0byBjb25zdHJ1Y3QgY29tcGxleCBzY2VuZXMsIGV0Yy4uLlxuICAgICAqIE92ZXJ3cml0ZSB0aGlzIG1ldGhvZCBpZiB5b3UgbmVlZCBhZGRpdG9uYWwgdmlld3MgYW5kIGNvbXBvbmVudHMuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtQSVhJQXBwfSBBIHJlZmVyZW5jZSB0byB0aGUgUElYSUFwcCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgc2V0dXAoKSB7XG4gICAgICAgIHRoaXMuc2NlbmUgPSB0aGlzLnNjZW5lRmFjdG9yeSgpXG4gICAgICAgIHRoaXMuc3RhZ2UuYWRkQ2hpbGQodGhpcy5zY2VuZSlcblxuICAgICAgICAvLyBmcHNMb2dnaW5nXG4gICAgICAgIGlmICh0aGlzLmZwc0xvZ2dpbmcpIHtcbiAgICAgICAgICAgIHRoaXMuYWRkRnBzRGlzcGxheSgpXG4gICAgICAgIH1cblxuICAgICAgICAvLyBHcmFwaFFMXG4gICAgICAgIGlmICh0aGlzLmdyYXBocWwgJiYgdHlwZW9mIGFwb2xsbyAhPT0gJ3VuZGVmaW5lZCcpIHtcblxuICAgICAgICAgICAgY29uc3QgbmV0d29ya0ludGVyZmFjZSA9IGFwb2xsby5jcmVhdGVOZXR3b3JrSW50ZXJmYWNlKHtcbiAgICAgICAgICAgICAgICB1cmk6ICcvZ3JhcGhxbCdcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgIGNvbnN0IHdzQ2xpZW50ID0gbmV3IHN1YnNjcmlwdGlvbnMuU3Vic2NyaXB0aW9uQ2xpZW50KGB3c3M6Ly8ke2xvY2F0aW9uLmhvc3RuYW1lfS9zdWJzY3JpcHRpb25zYCwge1xuICAgICAgICAgICAgICAgIHJlY29ubmVjdDogdHJ1ZSxcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uUGFyYW1zOiB7fVxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgY29uc3QgbmV0d29ya0ludGVyZmFjZVdpdGhTdWJzY3JpcHRpb25zID0gc3Vic2NyaXB0aW9ucy5hZGRHcmFwaFFMU3Vic2NyaXB0aW9ucyhcbiAgICAgICAgICAgICAgICBuZXR3b3JrSW50ZXJmYWNlLFxuICAgICAgICAgICAgICAgIHdzQ2xpZW50XG4gICAgICAgICAgICApXG5cbiAgICAgICAgICAgIHRoaXMuYXBvbGxvQ2xpZW50ID0gbmV3IGFwb2xsby5BcG9sbG9DbGllbnQoe1xuICAgICAgICAgICAgICAgIG5ldHdvcmtJbnRlcmZhY2U6IG5ldHdvcmtJbnRlcmZhY2VXaXRoU3Vic2NyaXB0aW9uc1xuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHByb2dyZXNzXG4gICAgICAgIHRoaXMuX3Byb2dyZXNzID0gbmV3IFByb2dyZXNzKE9iamVjdC5hc3NpZ24oeyB0aGVtZTogdGhpcy50aGVtZSB9LCB0aGlzLnByb2dyZXNzT3B0cywgeyBhcHA6IHRoaXMgfSkpXG4gICAgICAgIHRoaXMuX3Byb2dyZXNzLnZpc2libGUgPSBmYWxzZVxuICAgICAgICB0aGlzLnN0YWdlLmFkZENoaWxkKHRoaXMuX3Byb2dyZXNzKVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVGVzdHMgd2hldGhlciB0aGUgd2lkdGggaXMgbGFyZ2VyIHRoYW4gdGhlIGhlaWdodCBvZiB0aGUgYXBwbGljYXRpb24uXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSBSZXR1cm5zIHRydWUgaWYgYXBwIGlzIGluIGxhbmRzY2FwZSBtb2RlLlxuICAgICAqL1xuICAgIG9yaWVudGF0aW9uKCkge1xuICAgICAgICByZXR1cm4gdGhpcy53aWR0aCA+IHRoaXMuaGVpZ2h0XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2hlY2tzIG9yaWVudGF0aW9uIGFuZCBhZGFwdHMgdmlldyBzaXplIGlmIG5lY2Vzc2FyeS4gSW1wbGVtZW50cyBhXG4gICAgICogaGFuZGxlciBmb3IgdGhlIG9yaWVudGF0aW9uY2hhbmdlIGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIHtldmVudD19IC0gb3JpZW50YXRpb25jaGFuZ2UgZXZlbnRcbiAgICAgKi9cbiAgICBjaGVja09yaWVudGF0aW9uKGV2ZW50KSB7XG4gICAgICAgIHZhciB2YWx1ZSA9IHRoaXMub3JpZW50YXRpb24oKVxuICAgICAgICBpZiAodmFsdWUgIT0gdGhpcy5vcmllbnQpIHtcbiAgICAgICAgICAgIHNldFRpbWVvdXQoMTAwLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vcmllbnRhdGlvbkNoYW5nZWQodHJ1ZSlcbiAgICAgICAgICAgIH0uYmluZCh0aGlzKSlcbiAgICAgICAgICAgIHRoaXMub3JpZW50ID0gdmFsdWVcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENhbGxlZCBpZiBjaGVja09yaWVudGF0aW9uIGRldGVjdHMgYW4gb3JpZW50YXRpb24gY2hhbmdlIGV2ZW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIHtib29sZWFuPX0gW2ZvcmNlPWZhbHNlXSAtIENhbGxlZCBpZiBjaGVja09yaWVudGF0aW9uIGRldGVjdHMgYW4gb3JpZW50YXRpb24gY2hhbmdlIGV2ZW50LlxuICAgICAqL1xuICAgIG9yaWVudGF0aW9uQ2hhbmdlZChmb3JjZSA9IGZhbHNlKSB7XG4gICAgICAgIGlmICh0aGlzLmV4cGFuZFJlbmRlcmVyKCkgfHwgZm9yY2UpIHtcbiAgICAgICAgICAgIHRoaXMubGF5b3V0KClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENhbGxlZCBhZnRlciBhIHJlc2l6ZS4gRW1wdHkgbWV0aG9kIGJ1dCBjYW4gYmUgb3ZlcndyaXR0ZW4gdG9cbiAgICAgKiBhZGFwdCB0aGVpciBsYXlvdXQgdG8gdGhlIG5ldyBhcHAgc2l6ZS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbd2lkdGhdIC0gVGhlIHdpZHRoIG9mIHRoZSBhcHAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtoZWlnaHRdIC0gVGhlIGhlaWdodCBvZiB0aGUgYXBwLlxuICAgICAqL1xuICAgIGxheW91dCh3aWR0aCwgaGVpZ2h0KSB7XG5cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEcmF3cyB0aGUgZGlzcGxheSB0cmVlIG9mIHRoZSBhcHAuIFR5cGljYWxseSB0aGlzIGNhbiBiZSBkZWxlZ2F0ZWRcbiAgICAgKiB0byB0aGUgbGF5b3V0IG1ldGhvZC5cbiAgICAgKlxuICAgICAqL1xuICAgIGRyYXcoKSB7XG4gICAgICAgIHRoaXMubGF5b3V0KHRoaXMud2lkdGgsIHRoaXMuaGVpZ2h0KVxuICAgIH1cblxuICAgIC8qXG4gICAgICogUnVuIHRoZSBhcHBsaWNhdGlvbi4gT3ZlcnJpZGUgdGhpcyBtZXRob2Qgd2l0aCBldmVyeXRoaW5nXG4gICAgICogdGhhdCBpcyBuZWVkZWQgdG8gbWFpbnRhaW4geW91ciBBcHAsIGUuZy4gc2V0dXAgY2FsbHMsIG1haW4gbG9vcHMsIGV0Yy5cbiAgICAgKlxuICAgICAqL1xuICAgIHJ1bigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKlxuICAgICAqIE92ZXJ3cml0ZSB0aGlzIGZhY3RvcnkgbWV0aG9kIGlmIHlvdXIgYXBwbGljYXRpb24gbmVlZHMgYSBzcGVjaWFsXG4gICAgICogc2NlbmUgb2JqZWN0LlxuICAgICAqXG4gICAgICogQHJldHVybnMge1BJWEkuQ29udGFpbmVyfSAtIEEgbmV3IFBJWEkgQ29udGFpbmVyIGZvciB1c2UgYXMgYSBzY2VuZS5cbiAgICAgKi9cbiAgICBzY2VuZUZhY3RvcnkoKSB7XG4gICAgICAgIHJldHVybiBuZXcgUElYSS5Db250YWluZXIoKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFkZHMgdGhlIGRpc3BsYXkgb2YgdGhlIGZyYW1lcyBwZXIgc2Vjb25kIHRvIHRoZSByZW5kZXJlciBpbiB0aGUgdXBwZXIgbGVmdCBjb3JuZXIuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtQSVhJQXBwfSAtIFJldHVybnMgdGhlIFBJWElBcHAgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGFkZEZwc0Rpc3BsYXkoKSB7XG4gICAgICAgIGNvbnN0IGZwc0Rpc3BsYXkgPSBuZXcgRnBzRGlzcGxheSh0aGlzKVxuICAgICAgICB0aGlzLnN0YWdlLmFkZENoaWxkKGZwc0Rpc3BsYXkpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBzaXplIG9mIHRoZSByZW5kZXJlciBhcyBhbiBvYmplY3Qgd2l0aCB0aGUga2V5cyB3aWR0aCBhbmQgaGVpZ2h0LlxuICAgICAqXG4gICAgICogQHJlYWRvbmx5XG4gICAgICogQG1lbWJlciB7b2JqZWN0fVxuICAgICAqL1xuICAgIGdldCBzaXplKCkge1xuICAgICAgICByZXR1cm4geyB3aWR0aDogdGhpcy53aWR0aCwgaGVpZ2h0OiB0aGlzLmhlaWdodCB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY2VudGVyIG9mIHRoZSByZW5kZXJlciBhcyBhbiBvYmplY3Qgd2l0aCB0aGUga2V5cyB4IGFuZCB5LlxuICAgICAqXG4gICAgICogQHJlYWRvbmx5XG4gICAgICogQG1lbWJlciB7b2JqZWN0fVxuICAgICAqL1xuICAgIGdldCBjZW50ZXIoKSB7XG4gICAgICAgIHJldHVybiB7IHg6IHRoaXMud2lkdGggLyAyLCB5OiB0aGlzLmhlaWdodCAvIDIgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgdGhlIHJlbmRlcmVyIHRvIGZpdCBpbnRvIHRoZSB3aW5kb3cgb3IgZ2l2ZW4gd2lkdGggYW5kIGhlaWdodC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbZXZlbnRdIC0gVGhlIGV2ZW50LlxuICAgICAqIEBwYXJhbSB7b2JqZWN0PX0gW29wdHM9e31dIC0gVGhlIGV2ZW50LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy53aWR0aD13aW5kb3cuaW5uZXJXaWR0aF0gLSBUaGUgd2lkdGggb2YgdGhlIGFwcCB0byByZXNpemUgdG8uXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmhlaWdodD13aW5kb3cuaW5uZXJIZWlnaHRdIC0gVGhlIGhlaWdodCBvZiB0aGUgYXBwIHRvIHJlc2l6ZSB0by5cbiAgICAgKiBAcmV0dXJuIHtQSVhJQXBwfSAtIFJldHVybnMgdGhlIFBJWElBcHAgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHJlc2l6ZShldmVudCwgeyB3aWR0aCA9IHdpbmRvdy5pbm5lcldpZHRoLCBoZWlnaHQgPSB3aW5kb3cuaW5uZXJIZWlnaHQgfSA9IHt9KSB7XG4gICAgICAgIHRoaXMud2lkdGggPSB3aWR0aFxuICAgICAgICB0aGlzLmhlaWdodCA9IGhlaWdodFxuICAgICAgICB0aGlzLmV4cGFuZFJlbmRlcmVyKClcbiAgICAgICAgdGhpcy5sYXlvdXQod2lkdGgsIGhlaWdodClcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcIkFwcC5yZXNpemVcIiwgd2lkdGgsIGhlaWdodCwgd2luZG93LmlubmVyV2lkdGgsIHdpbmRvdy5pbm5lckhlaWdodCApXG4gICAgICAgIC8vIGlmICh0aGlzLnNjZW5lKSB7XG4gICAgICAgIC8vIGNvbnNvbGUubG9nKFwiZ2wuZHJhd2luZ0J1ZmZlcldpZHRoXCIsIHRoaXMucmVuZGVyZXIudmlldy5nZXRDb250ZXh0KCd3ZWJnbCcpLmRyYXdpbmdCdWZmZXJXaWR0aClcbiAgICAgICAgLy8gY29uc29sZS5sb2coXCJzY2VuZVwiLCB0aGlzLnNjZW5lLnNjYWxlLCB0aGlzLnJlbmRlcmVyLCB0aGlzLnJlbmRlcmVyLmF1dG9SZXNpemUsIHRoaXMucmVuZGVyZXIucmVzb2x1dGlvbilcbiAgICAgICAgLy8gfVxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEB0b2RvIFdyaXRlIHRoZSBkb2N1bWVudGF0aW9uLlxuICAgICAqXG4gICAgICogQHByaXZhdGVcbiAgICAgKi9cbiAgICBtb25rZXlQYXRjaFBpeGlNYXBwaW5nKCkge1xuICAgICAgICBpZiAodGhpcy5vcmlnaW5hbE1hcFBvc2l0aW9uVG9Qb2ludCA9PT0gbnVsbCkge1xuICAgICAgICAgICAgbGV0IGludGVyYWN0aW9uTWFuYWdlciA9IHRoaXMucmVuZGVyZXIucGx1Z2lucy5pbnRlcmFjdGlvblxuICAgICAgICAgICAgdGhpcy5vcmlnaW5hbE1hcFBvc2l0aW9uVG9Qb2ludCA9IGludGVyYWN0aW9uTWFuYWdlci5tYXBQb3NpdGlvblRvUG9pbnRcbiAgICAgICAgICAgIGludGVyYWN0aW9uTWFuYWdlci5tYXBQb3NpdGlvblRvUG9pbnQgPSAocG9pbnQsIHgsIHkpID0+IHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5maXhlZE1hcFBvc2l0aW9uVG9Qb2ludChwb2ludCwgeCwgeSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEluIHNvbWUgYnJvd3NlcnMgdGhlIGNhbnZhcyBpcyBkaXN0b3J0ZWQgaWYgdGhlIHNjcmVlbiByZXNvbHV0aW9uIGFuZCBcbiAgICAgKiBvdmVyYWxsIHNpemUgb2YgdGhlIGNhbnZhcyBleGNlZWRzIHRoZSBpbnRlcm5hbCBsaW1pdHMgKGUuZy4gNDA5NiB4IDQwOTYgcGl4ZWxzKS5cbiAgICAgKiBUbyBjb21wZW5zYXRlIHRoZXNlIGRpc3RvcnRpb25zIHdlIG5lZWQgdG8gZml4IHRoZSBtYXBwaW5nIHRvIHRoZSBhY3R1YWxcbiAgICAgKiBkcmF3aW5nIGJ1ZmZlciBjb29yZGluYXRlcy5cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7YW55fSBsb2NhbFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHlcbiAgICAgKiBAcmV0dXJuIHt9IGludGVyYWN0aW9uTWFuYWdlci5tYXBQb3NpdGlvblRvUG9pbnRcbiAgICAgKi9cbiAgICBmaXhlZE1hcFBvc2l0aW9uVG9Qb2ludChsb2NhbCwgeCwgeSkge1xuICAgICAgICBsZXQgcmVzb2x1dGlvbiA9IHRoaXMucmVuZGVyZXIucmVzb2x1dGlvblxuICAgICAgICBsZXQgaW50ZXJhY3Rpb25NYW5hZ2VyID0gdGhpcy5yZW5kZXJlci5wbHVnaW5zLmludGVyYWN0aW9uXG4gICAgICAgIGxldCBleHRlbmRXaWR0aCA9IDEuMFxuICAgICAgICBsZXQgZXh0ZW5kSGVpZ2h0ID0gMS4wXG4gICAgICAgIGxldCBkeSA9IDBcbiAgICAgICAgbGV0IGNhbnZhcyA9IHRoaXMucmVuZGVyZXIudmlld1xuICAgICAgICBsZXQgY29udGV4dCA9IGNhbnZhcy5nZXRDb250ZXh0KCd3ZWJnbCcpXG5cbiAgICAgICAgaWYgKGNvbnRleHQgIT09IG51bGwgJiYgKGNvbnRleHQuZHJhd2luZ0J1ZmZlcldpZHRoIDwgY2FudmFzLndpZHRoIHx8XG4gICAgICAgICAgICBjb250ZXh0LmRyYXdpbmdCdWZmZXJIZWlnaHQgPCBjYW52YXMuaGVpZ2h0KSkge1xuICAgICAgICAgICAgZXh0ZW5kV2lkdGggPSBjb250ZXh0LmRyYXdpbmdCdWZmZXJXaWR0aCAvIGNhbnZhcy53aWR0aFxuICAgICAgICAgICAgZXh0ZW5kSGVpZ2h0ID0gY29udGV4dC5kcmF3aW5nQnVmZmVySGVpZ2h0IC8gY2FudmFzLmhlaWdodFxuICAgICAgICAgICAgLy9keCA9IHdhbnRlZFdpZHRoIC0gY29udGV4dC5kcmF3aW5nQnVmZmVyV2lkdGhcbiAgICAgICAgICAgIGR5ID0gKGNhbnZhcy5oZWlnaHQgLSBjb250ZXh0LmRyYXdpbmdCdWZmZXJIZWlnaHQpIC8gcmVzb2x1dGlvblxuICAgICAgICB9XG4gICAgICAgIHggKj0gZXh0ZW5kV2lkdGhcbiAgICAgICAgeSAqPSBleHRlbmRIZWlnaHRcbiAgICAgICAgcmV0dXJuIHRoaXMub3JpZ2luYWxNYXBQb3NpdGlvblRvUG9pbnQuY2FsbChpbnRlcmFjdGlvbk1hbmFnZXIsIGxvY2FsLCB4LCB5ICsgZHkpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRXhwYW5kIHRoZSByZW5kZXJlciBzdGVwLXdpc2Ugb24gcmVzaXplLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtleHBhbmRdIC0gVGhlIGFtb3VudCBvZiBhZGRpdGlvbmFsIHNwYWNlIGZvciB0aGUgcmVuZGVyZXIgW3B4XS5cbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSB0cnVlIGlmIHRoZSByZW5kZXJlciB3YXMgcmVzaXplZC5cbiAgICAgKi9cbiAgICBleHBhbmRSZW5kZXJlcihleHBhbmQgPSAyNTYpIHtcbiAgICAgICAgbGV0IHJlbmRlcmVyID0gdGhpcy5yZW5kZXJlclxuICAgICAgICBsZXQgcmVzb2x1dGlvbiA9IHRoaXMucmVuZGVyZXIucmVzb2x1dGlvblxuICAgICAgICBsZXQgd3cgPSB0aGlzLndpZHRoXG4gICAgICAgIGxldCBoaCA9IHRoaXMuaGVpZ2h0XG4gICAgICAgIGxldCBzdyA9IHRoaXMuc2NyZWVuLndpZHRoXG4gICAgICAgIGxldCBzaCA9IHRoaXMuc2NyZWVuLmhlaWdodFxuICAgICAgICBpZiAod3cgPiBzdyB8fCBoaCA+IHNoKSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKCdBcHAuZXhwYW5kUmVuZGVyZXInKVxuICAgICAgICAgICAgcmVuZGVyZXIucmVzaXplKHd3ICsgZXhwYW5kLCBoaCArIGV4cGFuZClcbiAgICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgIH1cblxuICAgICAgICByZW5kZXJlci5yZXNpemUod3csIGhoKVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXQgdGhlIGxvYWRpbmcgcHJvZ3Jlc3Mgb2YgdGhlIGFwcGxpY2F0aW9uLiBJZiBjYWxsZWQgZm9yIHRoZSBmaXJzdCB0aW1lLCBkaXNwbGF5IHRoZSBwcm9ncmVzcyBiYXIuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW3ZhbHVlXSAtIFNob3VsZCBiZSBhIHZhbHVlIGJldHdlZW4gMCBhbmQgMTAwLiBJZiAxMDAsIHRoZSBwcm9ncmVzcyBiYXIgd2lsbCBkaXNhcHBlYXIuXG4gICAgICogQHJldHVybiB7UElYSUFwcHxQcm9ncmVzc30gVGhlIFBpeGlBcHAgb2JqZWN0IGZvciBjaGFpbmluZyBvciB0aGUgUHJvZ3Jlc3Mgb2JqZWN0IHdoZW4gdGhlIG1ldGhvZCB3YXNcbiAgICAgKiAgICAgY2FsbGVkIHdpdGhvdXQgYSBwYXJhbWV0ZXIuXG4gICAgICovXG4gICAgcHJvZ3Jlc3ModmFsdWUpIHtcblxuICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3Byb2dyZXNzXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9wcm9ncmVzcy52aXNpYmxlID0gdHJ1ZVxuICAgICAgICB0aGlzLl9wcm9ncmVzcy5wcm9ncmVzcyA9IHZhbHVlXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyBhIG5ldyBNb2RhbCBvYmplY3QgYmluZGVkIHRvIHRoaXMgYXBwLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzXSAtIEFuIG9wdGlvbnMgb2JqZWN0IGZvciB0aGUgTW9kYWwgb2JqZWN0LlxuICAgICAqIEByZXR1cm4ge01vZGFsfSBSZXR1cm5zIHRoZSBNb2RhbCBvYmplY3QuXG4gICAgICovXG4gICAgbW9kYWwob3B0cyA9IHt9KSB7XG5cbiAgICAgICAgbGV0IG1vZGFsID0gbmV3IE1vZGFsKE9iamVjdC5hc3NpZ24oeyB0aGVtZTogdGhpcy50aGVtZSB9LCBvcHRzLCB7IGFwcDogdGhpcyB9KSlcbiAgICAgICAgdGhpcy5zY2VuZS5hZGRDaGlsZChtb2RhbClcblxuICAgICAgICByZXR1cm4gbW9kYWxcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBPcGVucyBhIG5ldyBNZXNzYWdlIG9iamVjdCBiaW5kZWQgdG8gdGhpcyBhcHAuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgZm9yIHRoZSBNZXNzYWdlIG9iamVjdC5cbiAgICAgKiBAcmV0dXJuIHtNZXNzYWdlfSBSZXR1cm5zIHRoZSBNZXNzYWdlIG9iamVjdC5cbiAgICAgKi9cbiAgICBtZXNzYWdlKG9wdHMgPSB7fSkge1xuXG4gICAgICAgIGxldCBtZXNzYWdlID0gbmV3IE1lc3NhZ2UoT2JqZWN0LmFzc2lnbih7IHRoZW1lOiB0aGlzLnRoZW1lIH0sIG9wdHMsIHsgYXBwOiB0aGlzIH0pKVxuICAgICAgICB0aGlzLnNjZW5lLmFkZENoaWxkKG1lc3NhZ2UpXG5cbiAgICAgICAgcmV0dXJuIG1lc3NhZ2VcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBMb2FkcyBzcHJpdGVzLCBlLmcuIGltYWdlcyBpbnRvIHRoZSBQSVhJIFRleHR1cmVDYWNoZS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfHN0cmluZ1tdfSByZXNvdXJjZXMgLSBBIFN0cmluZyBvciBhbiBBcnJheSBvZiB1cmxzIHRvIHRoZSBpbWFnZXMgdG8gbG9hZC5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbbG9hZGVkXSAtIEEgY2FsbGJhY2sgd2hpY2ggaXMgZXhlY3V0ZWQgYWZ0ZXIgYWxsIHJlc291cmNlcyBoYXMgYmVlbiBsb2FkZWQuXG4gICAgICogICAgIFJlY2VpdmVzIG9uZSBwYXJhbXRlciwgYSBNYXAgb2Ygc3ByaXRlcyB3aGVyZSB0aGUga2V5IGlzIHRoZSBwYXRoIG9mIHRoZSBpbWFnZSB3aGljaCB3YXNcbiAgICAgKiAgICAgbG9hZGVkIGFuZCB0aGUgdmFsdWUgaXMgdGhlIFBJWEkuU3ByaXRlIG9iamVjdC5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgZm9yIG1vcmUgc3BlY2lmaWMgcGFyYW1ldGVycy5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLnJlc29sdXRpb25EZXBlbmRlbnQ9dHJ1ZV0gLSBTaG91bGQgdGhlIHNwcml0ZXMgYmUgbG9hZGVkIGRlcGVuZGVudCBvZiB0aGVcbiAgICAgKiAgICAgcmVuZGVyZXIgcmVzb2x1dGlvbj9cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLnByb2dyZXNzPWZhbHNlXSAtIFNob3VsZCBhIHByb2dyZXNzIGJhciBkaXNwbGF5IHRoZSBsb2FkaW5nIHN0YXR1cz9cbiAgICAgKiBAcmV0dXJuIHtQSVhJQXBwfSBUaGUgUElYSUFwcCBvYmplY3QgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGxvYWRTcHJpdGVzKHJlc291cmNlcywgbG9hZGVkID0gbnVsbCwgeyByZXNvbHV0aW9uRGVwZW5kZW50ID0gdHJ1ZSwgcHJvZ3Jlc3MgPSBmYWxzZSB9ID0ge30pIHtcblxuICAgICAgICB0aGlzLmxvYWRUZXh0dXJlcyhyZXNvdXJjZXMsIHRleHR1cmVzID0+IHtcblxuICAgICAgICAgICAgbGV0IHNwcml0ZXMgPSBuZXcgTWFwKClcblxuICAgICAgICAgICAgZm9yIChsZXQgW2tleSwgdGV4dHVyZV0gb2YgdGV4dHVyZXMpIHtcbiAgICAgICAgICAgICAgICBzcHJpdGVzLnNldChrZXksIG5ldyBQSVhJLlNwcml0ZSh0ZXh0dXJlKSlcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGxvYWRlZCkge1xuICAgICAgICAgICAgICAgIGxvYWRlZC5jYWxsKHRoaXMsIHNwcml0ZXMpXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgfSwgeyByZXNvbHV0aW9uRGVwZW5kZW50LCBwcm9ncmVzcyB9KVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTG9hZHMgdGV4dHVyZXMsIGUuZy4gaW1hZ2VzIGludG8gdGhlIFBJWEkgVGV4dHVyZUNhY2hlLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd8c3RyaW5nW119IHJlc291cmNlcyAtIEEgU3RyaW5nIG9yIGFuIEFycmF5IG9mIHVybHMgdG8gdGhlIGltYWdlcyB0byBsb2FkLlxuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtsb2FkZWRdIC0gQSBjYWxsYmFjayB3aGljaCBpcyBleGVjdXRlZCBhZnRlciBhbGwgcmVzb3VyY2VzIGhhcyBiZWVuIGxvYWRlZC5cbiAgICAgKiAgICAgUmVjZWl2ZXMgb25lIHBhcmFtdGVyLCBhIE1hcCBvZiB0ZXh0dXJlcyB3aGVyZSB0aGUga2V5IGlzIHRoZSBwYXRoIG9mIHRoZSBpbWFnZSB3aGljaCB3YXNcbiAgICAgKiAgICAgbG9hZGVkIGFuZCB0aGUgdmFsdWUgaXMgdGhlIFBJWEkuVGV4dHVyZSBvYmplY3QuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzXSAtIEFuIG9wdGlvbnMgb2JqZWN0IGZvciBtb3JlIHNwZWNpZmljIHBhcmFtZXRlcnMuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5yZXNvbHV0aW9uRGVwZW5kZW50PXRydWVdIC0gU2hvdWxkIHRoZSB0ZXh0dXJlcyBiZSBsb2FkZWQgZGVwZW5kZW50IG9mIHRoZVxuICAgICAqICAgICByZW5kZXJlciByZXNvbHV0aW9uP1xuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMucHJvZ3Jlc3M9ZmFsc2VdIC0gU2hvdWxkIGEgcHJvZ3Jlc3MgYmFyIGRpc3BsYXkgdGhlIGxvYWRpbmcgc3RhdHVzP1xuICAgICAqIEByZXR1cm4ge1BJWElBcHB9IFRoZSBQSVhJQXBwIG9iamVjdCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgbG9hZFRleHR1cmVzKHJlc291cmNlcywgbG9hZGVkID0gbnVsbCwgeyByZXNvbHV0aW9uRGVwZW5kZW50ID0gdHJ1ZSwgcHJvZ3Jlc3MgPSBmYWxzZSB9ID0ge30pIHtcblxuICAgICAgICBpZiAoIUFycmF5LmlzQXJyYXkocmVzb3VyY2VzKSkge1xuICAgICAgICAgICAgcmVzb3VyY2VzID0gW3Jlc291cmNlc11cbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGxvYWRlciA9IHRoaXMubG9hZGVyXG5cbiAgICAgICAgZm9yIChsZXQgcmVzb3VyY2Ugb2YgcmVzb3VyY2VzKSB7XG5cbiAgICAgICAgICAgIGlmICghbG9hZGVyLnJlc291cmNlc1tyZXNvdXJjZV0pIHtcblxuICAgICAgICAgICAgICAgIGlmIChyZXNvbHV0aW9uRGVwZW5kZW50KSB7XG4gICAgICAgICAgICAgICAgICAgIGxldCByZXNvbHV0aW9uID0gTWF0aC5yb3VuZCh0aGlzLnJlbmRlcmVyLnJlc29sdXRpb24pXG4gICAgICAgICAgICAgICAgICAgIHN3aXRjaCAocmVzb2x1dGlvbikge1xuICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSAyOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRlci5hZGQocmVzb3VyY2UsIHJlc291cmNlLnJlcGxhY2UoL1xcLihbXi5dKikkLywgJ0AyeC4kMScpKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlIDM6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9hZGVyLmFkZChyZXNvdXJjZSwgcmVzb3VyY2UucmVwbGFjZSgvXFwuKFteLl0qKSQvLCAnQDN4LiQxJykpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9hZGVyLmFkZChyZXNvdXJjZSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgbG9hZGVyLmFkZChyZXNvdXJjZSlcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAocHJvZ3Jlc3MpIHtcbiAgICAgICAgICAgIGxvYWRlci5vbigncHJvZ3Jlc3MnLCBlID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnByb2dyZXNzKGUucHJvZ3Jlc3MpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG5cbiAgICAgICAgbG9hZGVyLmxvYWQoKGxvYWRlciwgcmVzb3VyY2VzKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0ZXh0dXJlcyA9IG5ldyBNYXAoKVxuXG4gICAgICAgICAgICBmb3IgKGxldCBrZXkgaW4gcmVzb3VyY2VzKSB7XG4gICAgICAgICAgICAgICAgdGV4dHVyZXMuc2V0KGtleSwgcmVzb3VyY2VzW2tleV0udGV4dHVyZSlcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKGxvYWRlZCkge1xuICAgICAgICAgICAgICAgIGxvYWRlZC5jYWxsKHRoaXMsIHRleHR1cmVzKVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUXVlcmllcyB0aGUgR3JhcGhRTCBlbmRwb2ludC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbcXVlcnldIC0gVGhlIEdyYXBoUUwgcXVlcnkgc3RyaW5nLlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0cz17fV0gLSBBbiBvcHRpb25zIG9iamVjdC4gVGhlIGZvbGxvd2luZyBvcHRpb25zIGNhbiBiZSBzZXQ6XG4gICAgICogICAgIGh0dHA6Ly9kZXYuYXBvbGxvZGF0YS5jb20vY29yZS9hcG9sbG8tY2xpZW50LWFwaS5odG1sI0Fwb2xsb0NsaWVudC5xdWVyeVxuICAgICAqIEByZXR1cm4ge1Byb21pc2V9IFJldHVybnMgYSBQcm9taXNlIHdoaWNoIGlzIGVpdGhlciByZXNvbHZlZCB3aXRoIHRoZSByZXN1bHRpbmcgZGF0YSBvclxuICAgICAqICAgICByZWplY3RlZCB3aXRoIGFuIGVycm9yLlxuICAgICAqL1xuICAgIHF1ZXJ5KHF1ZXJ5LCBvcHRzID0ge30pIHtcblxuICAgICAgICBpZiAodHlwZW9mIHF1ZXJ5ID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgb3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIG9wdHMsIHsgcXVlcnkgfSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIG9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCBxdWVyeSlcbiAgICAgICAgfVxuXG4gICAgICAgIG9wdHMucXVlcnkgPSBvcHRzLnF1ZXJ5LnRyaW0oKVxuXG4gICAgICAgIGlmICghb3B0cy5xdWVyeS5zdGFydHNXaXRoKCdxdWVyeScpKSB7XG4gICAgICAgICAgICBpZiAob3B0cy5xdWVyeS5zdGFydHNXaXRoKCd7JykpIHtcbiAgICAgICAgICAgICAgICBvcHRzLnF1ZXJ5ID0gYHF1ZXJ5ICR7b3B0cy5xdWVyeX1gXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG9wdHMucXVlcnkgPSBgcXVlcnkgeyR7b3B0cy5xdWVyeX19YFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgb3B0cy5xdWVyeSA9IGdxbChvcHRzLnF1ZXJ5KVxuXG4gICAgICAgIHJldHVybiB0aGlzLmFwb2xsb0NsaWVudC5xdWVyeShvcHRzKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE11dGF0ZSB0aGUgR3JhcGhRTCBlbmRwb2ludC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbbXV0YXRpb25dIC0gVGhlIEdyYXBoUUwgbXV0YXRpb24gc3RyaW5nLlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0cz17fV0gLSBBbiBvcHRpb25zIG9iamVjdC4gVGhlIGZvbGxvd2luZyBvcHRpb25zIGNhbiBiZSBzZXQ6XG4gICAgICogICAgIGh0dHA6Ly9kZXYuYXBvbGxvZGF0YS5jb20vY29yZS9hcG9sbG8tY2xpZW50LWFwaS5odG1sI0Fwb2xsb0NsaWVudC5tdXRhdGVcbiAgICAgKiBAcmV0dXJuIHtQcm9taXNlfSBSZXR1cm5zIGEgUHJvbWlzZSB3aGljaCBpcyBlaXRoZXIgcmVzb2x2ZWQgd2l0aCB0aGUgcmVzdWx0aW5nIGRhdGEgb3JcbiAgICAgKiAgICAgcmVqZWN0ZWQgd2l0aCBhbiBlcnJvci5cbiAgICAgKi9cbiAgICBtdXRhdGUobXV0YXRpb24sIG9wdHMgPSB7fSkge1xuXG4gICAgICAgIGlmICh0eXBlb2YgbXV0YXRpb24gPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICBvcHRzID0gT2JqZWN0LmFzc2lnbih7fSwgb3B0cywgeyBtdXRhdGlvbiB9KVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgb3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIG11dGF0aW9uKVxuICAgICAgICB9XG5cbiAgICAgICAgb3B0cy5tdXRhdGlvbiA9IG9wdHMubXV0YXRpb24udHJpbSgpXG5cbiAgICAgICAgaWYgKCFvcHRzLm11dGF0aW9uLnN0YXJ0c1dpdGgoJ211dGF0aW9uJykpIHtcbiAgICAgICAgICAgIGlmIChvcHRzLm11dGF0aW9uLnN0YXJ0c1dpdGgoJ3snKSkge1xuICAgICAgICAgICAgICAgIG9wdHMubXV0YXRpb24gPSBgbXV0YXRpb24gJHtvcHRzLm11dGF0aW9ufWBcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgb3B0cy5tdXRhdGlvbiA9IGBtdXRhdGlvbiB7JHtvcHRzLm11dGF0aW9ufX1gXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBvcHRzLm11dGF0aW9uID0gZ3FsKG9wdHMubXV0YXRpb24pXG5cbiAgICAgICAgcmV0dXJuIHRoaXMuYXBvbGxvQ2xpZW50Lm11dGF0ZShvcHRzKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFN1YnNjcmliZSB0aGUgR3JhcGhRTCBlbmRwb2ludC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbc3Vic2NyaXB0aW9uXSAtIFRoZSBHcmFwaFFMIHN1YnNjcmlwdGlvbi5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHM9e31dIC0gQW4gb3B0aW9ucyBvYmplY3QuIFRoZSBmb2xsb3dpbmcgb3B0aW9ucyBjYW4gYmUgc2V0OlxuICAgICAqICAgICBodHRwOi8vZGV2LmFwb2xsb2RhdGEuY29tL2NvcmUvYXBvbGxvLWNsaWVudC1hcGkuaHRtbCNBcG9sbG9DbGllbnQucXVlcnlcbiAgICAgKiBAcmV0dXJuIHtQcm9taXNlfSBSZXR1cm5zIGEgUHJvbWlzZSB3aGljaCBpcyBlaXRoZXIgcmVzb2x2ZWQgd2l0aCB0aGUgcmVzdWx0aW5nIGRhdGEgb3JcbiAgICAgKiAgICAgcmVqZWN0ZWQgd2l0aCBhbiBlcnJvci5cbiAgICAgKi9cbiAgICBzdWJzY3JpYmUoc3Vic2NyaXB0aW9uLCBvcHRzID0ge30pIHtcblxuICAgICAgICBpZiAodHlwZW9mIHN1YnNjcmlwdGlvbiA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIG9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCBvcHRzLCB7IHN1YnNjcmlwdGlvbiB9KVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgb3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIHN1YnNjcmlwdGlvbilcbiAgICAgICAgfVxuXG4gICAgICAgIG9wdHMuc3Vic2NyaXB0aW9uID0gb3B0cy5zdWJzY3JpcHRpb24udHJpbSgpXG5cbiAgICAgICAgaWYgKCFvcHRzLnN1YnNjcmlwdGlvbi5zdGFydHNXaXRoKCdzdWJzY3JpcHRpb24nKSkge1xuICAgICAgICAgICAgaWYgKG9wdHMuc3Vic2NyaXB0aW9uLnN0YXJ0c1dpdGgoJ3snKSkge1xuICAgICAgICAgICAgICAgIG9wdHMuc3Vic2NyaXB0aW9uID0gYHN1YnNjcmlwdGlvbiAke29wdHMuc3Vic2NyaXB0aW9ufWBcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgb3B0cy5zdWJzY3JpcHRpb24gPSBgc3Vic2NyaXB0aW9uIHske29wdHMuc3Vic2NyaXB0aW9ufX1gXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBvcHRzLnF1ZXJ5ID0gZ3FsKG9wdHMuc3Vic2NyaXB0aW9uKVxuXG4gICAgICAgIGRlbGV0ZSBvcHRzLnN1YnNjcmlwdGlvblxuXG4gICAgICAgIHJldHVybiB0aGlzLmFwb2xsb0NsaWVudC5zdWJzY3JpYmUob3B0cylcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTdXBwb3J0cyB0aGUgcGFnZSBhcyBhIGdsb2JhbCBjb29yZGluYXRlIHN5c3RlbSBhbmQgY29udmVydHMgYnJvd3NlciBwYWdlIGNvb3JkaW5hdGVzXG4gICAgICogdG8gbG9jYWwgRGlzcGxheU9iamVjdCBjb29yZGluYXRlcy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7RGlzcGxheU9iamVjdH0gZGlzcGxheU9iamVjdCAtIFRoZSBQSVhJIGRpc3BsYXlPYmplY3QuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHggLSBUaGUgeCBjb29yZGluYXRlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB5IC0gVGhlIHkgY29vcmRpbmF0ZS5cbiAgICAgKiAgXG4gICAgICogQHJldHVybiB7UElYSS5Qb2ludH0gUmV0dXJucyBhIFBJWEkuUG9pbnQuXG4gICAgICovXG5cbiAgICBjb252ZXJ0UG9pbnRGcm9tUGFnZVRvTm9kZShkaXNwbGF5T2JqZWN0LCB4LCB5KSB7XG4gICAgICAgIGxldCByZXNvbHV0aW9uID0gdGhpcy5yZW5kZXJlci5yZXNvbHV0aW9uXG4gICAgICAgIGNvbnNvbGUubG9nKFwicmVzb2x1dGlvblwiLCByZXNvbHV0aW9uKVxuICAgICAgICBsZXQgcGl4aUdsb2JhbCA9IHdpbmRvdy5jb252ZXJ0UG9pbnRGcm9tUGFnZVRvTm9kZShhcHAudmlldywgeCwgeSlcbiAgICAgICAgcGl4aUdsb2JhbC54IC89IHJlc29sdXRpb25cbiAgICAgICAgcGl4aUdsb2JhbC55IC89IHJlc29sdXRpb25cbiAgICAgICAgcmV0dXJuIGRpc3BsYXlPYmplY3QudG9Mb2NhbChuZXcgUElYSS5Qb2ludChwaXhpR2xvYmFsLngsIHBpeGlHbG9iYWwueSkpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU3VwcG9ydHMgdGhlIHBhZ2UgYXMgYSBnbG9iYWwgY29vcmRpbmF0ZSBzeXN0ZW0gYW5kIGNvbnZlcnRzIGxvY2FsIERpc3BsYXlPYmplY3QgY29vcmRpbmF0ZXNcbiAgICAgKiB0byBicm93c2VyIHBhZ2UgY29vcmRpbmF0ZXMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0Rpc3BsYXlPYmplY3R9IGRpc3BsYXlPYmplY3QgLSBUaGUgUElYSSBkaXNwbGF5T2JqZWN0LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4IC0gVGhlIHggY29vcmRpbmF0ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0geSAtIFRoZSB5IGNvb3JkaW5hdGUuXG4gICAgICogIFxuICAgICAqIEByZXR1cm4ge1BvaW50fSBSZXR1cm5zIGEgRE9NIFBvaW50LlxuICAgICAqL1xuXG4gICAgY29udmVydFBvaW50RnJvbU5vZGVUb1BhZ2UoZGlzcGxheU9iamVjdCwgeCwgeSkge1xuICAgICAgICBsZXQgcmVzb2x1dGlvbiA9IHRoaXMucmVuZGVyZXIucmVzb2x1dGlvblxuICAgICAgICBsZXQgcGl4aUdsb2JhbCA9IGRpc3BsYXlPYmplY3QudG9HbG9iYWwobmV3IFBJWEkuUG9pbnQoeCwgeSkpXG4gICAgICAgIHBpeGlHbG9iYWwueCAqPSByZXNvbHV0aW9uXG4gICAgICAgIHBpeGlHbG9iYWwueSAqPSByZXNvbHV0aW9uXG4gICAgICAgIC8vIGNvbnNvbGUubG9nKFwiYXBwLmNvbnZlcnRQb2ludEZyb21Ob2RlVG9QYWdlXCIsIHBpeGlHbG9iYWwpXG4gICAgICAgIHJldHVybiB3aW5kb3cuY29udmVydFBvaW50RnJvbU5vZGVUb1BhZ2UoYXBwLnZpZXcsIHBpeGlHbG9iYWwueCwgcGl4aUdsb2JhbC55KVxuICAgIH1cbn1cblxuLyoqXG4gKiBUaGUgY2xhc3MgZnBzZGlzcGxheSBzaG93cyBpbiB0aGUgdXBwZXIgbGVmdCBjb3JuZXJcbiAqIG9mIHRoZSByZW5kZXJlciB0aGUgY3VycmVudCBpbWFnZSByZWZyZXNoIHJhdGUuXG4gKlxuICogQHByaXZhdGVcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgUElYSS5HcmFwaGljc1xuICogQHNlZSB7QGxpbmsgaHR0cDovL3BpeGlqcy5kb3dubG9hZC9kZXYvZG9jcy9QSVhJLkdyYXBoaWNzLmh0bWx8UElYSS5HcmFwaGljc31cbiAqL1xuY2xhc3MgRnBzRGlzcGxheSBleHRlbmRzIFBJWEkuR3JhcGhpY3Mge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhIEZwc0Rpc3BsYXkuXG4gICAgICpcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge1BJWElBcHB9IGFwcCAtIFRoZSBQSVhJQXBwIHdoZXJlIHRoZSBmcmFtZXMgcGVyIHNlY29uZCBzaG91bGQgYmUgZGlzcGxheWVkLlxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKGFwcCkge1xuXG4gICAgICAgIHN1cGVyKClcblxuICAgICAgICB0aGlzLmFwcCA9IGFwcFxuXG4gICAgICAgIHRoaXMubGluZVN0eWxlKDMsIDB4NDM0ZjRmLCAxKVxuICAgICAgICAgICAgLmJlZ2luRmlsbCgweDQzNGY0ZiwgLjYpXG4gICAgICAgICAgICAuZHJhd1JvdW5kZWRSZWN0KDAsIDAsIDY4LCAzMiwgNSlcbiAgICAgICAgICAgIC5lbmRGaWxsKClcbiAgICAgICAgICAgIC5wb3NpdGlvbi5zZXQoMjAsIDIwKVxuXG4gICAgICAgIHRoaXMudGV4dCA9IG5ldyBQSVhJLlRleHQodGhpcy5mcHMsIG5ldyBQSVhJLlRleHRTdHlsZSh7XG4gICAgICAgICAgICBmb250RmFtaWx5OiAnQXJpYWwnLFxuICAgICAgICAgICAgZm9udFNpemU6IDE0LFxuICAgICAgICAgICAgZm9udFdlaWdodDogJ2JvbGQnLFxuICAgICAgICAgICAgZmlsbDogJyNmNmY2ZjYnLFxuICAgICAgICAgICAgc3Ryb2tlOiAnIzQzNGY0ZicsXG4gICAgICAgICAgICBzdHJva2VUaGlja25lc3M6IDNcbiAgICAgICAgfSkpXG4gICAgICAgIHRoaXMudGV4dC5wb3NpdGlvbi5zZXQoNiwgNilcblxuICAgICAgICB0aGlzLmFkZENoaWxkKHRoaXMudGV4dClcblxuICAgICAgICB0aGlzLnJlZnJlc2hGcHMoKVxuXG4gICAgICAgIHdpbmRvdy5zZXRJbnRlcnZhbCh0aGlzLnJlZnJlc2hGcHMuYmluZCh0aGlzKSwgMTAwMClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWZyZXNoZXMgZnBzIG51bWVyLlxuICAgICAqXG4gICAgICogQHJldHVybiB7UElYSUFwcH0gUmV0dXJucyB0aGUgUElYSUFwcCBvYmplY3QgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHJlZnJlc2hGcHMoKSB7XG4gICAgICAgIHRoaXMudGV4dC50ZXh0ID0gYCR7KHRoaXMuYXBwLnRpY2tlci5GUFMpLnRvRml4ZWQoMSl9IGZwc2BcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbn1cbiIsIi8qKlxuICogQSBHYXVzc2lhbiBibHVyIGZpbHRlci4gV2l0aCB0aGlzIGZpbHRlciwgeW91IGNhbiBibHVyIGFuIGFyZWEgb2YgYSBQSVhJLkRpc3BsYXlPYmplY3QuIFRoaXMgY2Fubm90XG4gKiBiZSBkb25lIHdpdGggdGhlIFBJWEkuZmlsdGVycy5CbHVyRmlsdGVyICh3aGVuIHlvdSB1c2UgdGhlIFBJWEkuZmlsdGVycy5CbHVyRmlsdGVyIHdpdGhcbiAqIGFuIGZpbHRlciBhcmVhLCBhbGwgcGl4ZWxzIG91dHNpZGUgb2YgdGhlIGFyZWEgYXJlIG5vdCBkaXNwbGF5ZWQpLiBBdHRlbnRpb246IFRoZSBhcmVhIG9mXG4gKiB0aGUgZmlsdGVyIGlzIGFsd2F5cyBpbiBnbG9iYWwgc2NvcGUsIE5PVCByZWxhdGl2ZSB0byB0aGUgUElYSS5EaXNwbGF5T2JqZWN0IHRoZSBmaWx0ZXJcbiAqIGlzIGFzc2lnbmVkIHRvIVxuICpcbiAqIEBleGFtcGxlXG4gKiAvLyBDcmVhdGUgdGhlIGFwcFxuICogY29uc3QgYXBwID0gbmV3IFBJWElBcHAoe1xuICogICAgIHZpZXc6IGNhbnZhcyxcbiAqICAgICB3aWR0aDogNDgwLFxuICogICAgIGhlaWdodDogMjcwLFxuICogICAgIHRyYW5zcGFyZW50OiBmYWxzZVxuICogfSkuc2V0dXAoKS5ydW4oKVxuICogXG4gKiAvLyBBZGQgYSB2aWRlbyBzcHJpdGVcbiAqIGNvbnN0IHNwcml0ZSA9IG5ldyBQSVhJLlNwcml0ZShQSVhJLlRleHR1cmUuZnJvbVZpZGVvKFwiYXNzZXRzL2JsdXJmaWx0ZXIubXA0XCIpKVxuICogc3ByaXRlLndpZHRoID0gYXBwLnNpemUud2lkdGhcbiAqIHNwcml0ZS5oZWlnaHQgPSBhcHAuc2l6ZS5oZWlnaHRcbiAqIGFwcC5zY2VuZS5hZGRDaGlsZChzcHJpdGUpXG4gKiBcbiAqIC8vIENyZWF0ZSB0aGUgZmlsdGVyIGFuZCBhc3NpZ24gaXQgdG8gdGhlIHNjZW5lXG4gKiBjb25zdCBibHVyRmlsdGVyID0gbmV3IEJsdXJGaWx0ZXIobmV3IFBJWEkuUmVjdGFuZ2xlKDIwLCAyMCwgODAsIDYwKSlcbiAqIGFwcC5zY2VuZS5maWx0ZXJzID0gW2JsdXJGaWx0ZXJdXG4gKiBcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgUElYSS5GaWx0ZXJcbiAqIEBwYXJhbSB7UElYSS5SZWN0YW5nbGV8UElYSS5DaXJjbGV8UElYSS5EaXNwbGF5T2JqZWN0fSBzaGFwZSBUaGUgYXJlYSB3aGVyZSB0aGUgYmx1ciBlZmZlY3Qgc2hvdWxkIGJlIGFwcGxpZWQgdG8uIFJlbGF0aXZlIHRvIHRoZVxuICogICAgIGNhbnZhcywgTk9UIHJlbGF0aXZlIHRvIHRoZSBQSVhJLkRpc3BsYXlPYmplY3Qgd2hlcmUgdGhlIGJsdXIgZWZmZWN0IGlzIGFzc2lnbmVkIHRvIVxuICogQHBhcmFtIHtudW1iZXJ9IFtibHVyPTUwXSBUaGUgc3RyZW5ndGggb2YgdGhlIGJsdXIuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEJsdXJGaWx0ZXIgZXh0ZW5kcyBQSVhJLkZpbHRlciB7XG4gICAgXG4gICAgY29uc3RydWN0b3Ioc2hhcGUsIGJsdXIgPSA1MCkge1xuICAgICAgICBzdXBlcigpXG5cbiAgICAgICAgY29uc3Qgbm9ybWFsaXplZCA9IHRoaXMubm9ybWFsaXplKHNoYXBlKVxuXG4gICAgICAgIHRoaXMudGlsdFNoaWZ0WEZpbHRlciA9IG5ldyBUaWx0U2hpZnRYRmlsdGVyKG5vcm1hbGl6ZWQsIGJsdXIpXG4gICAgICAgIHRoaXMudGlsdFNoaWZ0WUZpbHRlciA9IG5ldyBUaWx0U2hpZnRZRmlsdGVyKG5vcm1hbGl6ZWQsIGJsdXIpXG4gICAgfVxuXG4gICAgYXBwbHkoZmlsdGVyTWFuYWdlciwgaW5wdXQsIG91dHB1dCkge1xuICAgICAgICBsZXQgcmVuZGVyVGFyZ2V0ID0gZmlsdGVyTWFuYWdlci5nZXRSZW5kZXJUYXJnZXQodHJ1ZSlcbiAgICAgICAgdGhpcy50aWx0U2hpZnRYRmlsdGVyLmFwcGx5KGZpbHRlck1hbmFnZXIsIGlucHV0LCByZW5kZXJUYXJnZXQpXG4gICAgICAgIHRoaXMudGlsdFNoaWZ0WUZpbHRlci5hcHBseShmaWx0ZXJNYW5hZ2VyLCByZW5kZXJUYXJnZXQsIG91dHB1dClcbiAgICAgICAgZmlsdGVyTWFuYWdlci5yZXR1cm5SZW5kZXJUYXJnZXQocmVuZGVyVGFyZ2V0KVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRoZSBzdHJlbmd0aCBvZiB0aGUgYmx1ci5cbiAgICAgKlxuICAgICAqIEBtZW1iZXIge251bWJlcn1cbiAgICAgKi9cbiAgICBnZXQgYmx1cigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudGlsdFNoaWZ0WEZpbHRlci5ibHVyXG4gICAgfVxuICAgIHNldCBibHVyKHZhbHVlKSB7XG4gICAgICAgIHRoaXMudGlsdFNoaWZ0WEZpbHRlci5ibHVyID0gdGhpcy50aWx0U2hpZnRZRmlsdGVyLmJsdXIgPSB2YWx1ZVxuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBUaGUgYmx1ciBzaGFwZS5cbiAgICAgKlxuICAgICAqIEBtZW1iZXIge1BJWEkuUmVjdGFuZ2xlfFBJWEkuQ2lyY2xlfFBJWEkuRGlzcGxheU9iamVjdH1cbiAgICAgKi9cbiAgICBnZXQgc2hhcGUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnRpbHRTaGlmdFhGaWx0ZXIuc2hhcGVcbiAgICB9XG4gICAgc2V0IHNoYXBlKHZhbHVlKSB7XG4gICAgICAgIHRoaXMudGlsdFNoaWZ0WEZpbHRlci5zaGFwZSA9IHRoaXMudGlsdFNoaWZ0WUZpbHRlci5zaGFwZSA9IHRoaXMubm9ybWFsaXplKHZhbHVlKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHtQSVhJLlJlY3RhbmdsZXxQSVhJLkNpcmNsZXxQSVhJLkRpc3BsYXlPYmplY3R9IHZhbHVlXG4gICAgICogQHJldHVybnMge09iamVjdH1cbiAgICAgKi9cbiAgICBub3JtYWxpemUodmFsdWUpIHtcblxuICAgICAgICBsZXQgc2hhcGUgPSBudWxsXG5cbiAgICAgICAgaWYgKHZhbHVlIGluc3RhbmNlb2YgUElYSS5DaXJjbGUpIHtcbiAgICAgICAgICAgIHNoYXBlID0ge3R5cGU6ICdjaXJjbGUnLCB4OiB2YWx1ZS54LCB5OiB2YWx1ZS55LCByOiB2YWx1ZS5yYWRpdXN9XG4gICAgICAgIH0gZWxzZSBpZiAodmFsdWUgaW5zdGFuY2VvZiBQSVhJLlJlY3RhbmdsZSkge1xuICAgICAgICAgICAgc2hhcGUgPSB7dHlwZTogJ3JlY3RhbmdsZScsIHg6IHZhbHVlLngsIHk6IHZhbHVlLnksIHdpZHRoOiB2YWx1ZS53aWR0aCwgaGVpZ2h0OiB2YWx1ZS5oZWlnaHR9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCBib3VuZHMgPSB2YWx1ZS5nZXRCb3VuZHMoKVxuICAgICAgICAgICAgc2hhcGUgPSB7dHlwZTogJ3JlY3RhbmdsZScsIHg6IGJvdW5kcy54LCB5OiBib3VuZHMueSwgd2lkdGg6IGJvdW5kcy53aWR0aCwgaGVpZ2h0OiBib3VuZHMuaGVpZ2h0fVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHNoYXBlXG4gICAgfVxufVxuXG4vKipcbiAqIEEgVGlsdFNoaWZ0QXhpc0ZpbHRlci5cbiAqXG4gKiBAY2xhc3NcbiAqIEBleHRlbmRzIFBJWEkuRmlsdGVyXG4gKiBAYWJzdHJhY3RcbiAqIEBwcml2YXRlXG4gKi9cbmNsYXNzIFRpbHRTaGlmdEF4aXNGaWx0ZXIgZXh0ZW5kcyBQSVhJLkZpbHRlciB7XG5cbiAgICBjb25zdHJ1Y3RvcihzaGFwZSwgYmx1cil7XG5cbiAgICAgICAgY29uc3QgdmVydGV4ID0gYFxuICAgICAgICAgICAgYXR0cmlidXRlIHZlYzIgYVZlcnRleFBvc2l0aW9uO1xuICAgICAgICAgICAgYXR0cmlidXRlIHZlYzIgYVRleHR1cmVDb29yZDtcblxuICAgICAgICAgICAgdW5pZm9ybSBtYXQzIHByb2plY3Rpb25NYXRyaXg7XG5cbiAgICAgICAgICAgIHZhcnlpbmcgdmVjMiB2VGV4dHVyZUNvb3JkO1xuXG4gICAgICAgICAgICB2b2lkIG1haW4odm9pZCkge1xuICAgICAgICAgICAgICAgIGdsX1Bvc2l0aW9uID0gdmVjNCgocHJvamVjdGlvbk1hdHJpeCAqIHZlYzMoYVZlcnRleFBvc2l0aW9uLCAxLjApKS54eSwgMC4wLCAxLjApO1xuICAgICAgICAgICAgICAgIHZUZXh0dXJlQ29vcmQgPSBhVGV4dHVyZUNvb3JkO1xuICAgICAgICAgICAgfVxuICAgICAgICBgXG5cbiAgICAgICAgY29uc3QgZnJhZ21lbnQgPSBgXG4gICAgICAgICAgICB2YXJ5aW5nIHZlYzIgdlRleHR1cmVDb29yZDtcblxuICAgICAgICAgICAgdW5pZm9ybSB2ZWM0IGZpbHRlckFyZWE7XG4gICAgICAgICAgICB1bmlmb3JtIHNhbXBsZXIyRCB1U2FtcGxlcjtcbiAgICAgICAgICAgIHVuaWZvcm0gaW50IHNoYXBlO1xuICAgICAgICAgICAgdW5pZm9ybSB2ZWM0IHJlY3RhbmdsZTtcbiAgICAgICAgICAgIHVuaWZvcm0gdmVjMyBjaXJjbGU7XG4gICAgICAgICAgICB1bmlmb3JtIGZsb2F0IGJsdXI7XG4gICAgICAgICAgICB1bmlmb3JtIHZlYzIgZGVsdGE7XG4gICAgICAgICAgICB1bmlmb3JtIHZlYzIgdGV4U2l6ZTtcblxuICAgICAgICAgICAgZmxvYXQgcmFuZG9tKHZlYzMgc2NhbGUsIGZsb2F0IHNlZWQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZnJhY3Qoc2luKGRvdChnbF9GcmFnQ29vcmQueHl6ICsgc2VlZCwgc2NhbGUpKSAqIDQzNzU4LjU0NTMgKyBzZWVkKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdm9pZCBtYWluKHZvaWQpIHtcbiAgICAgICAgICAgICAgICAvLyB0ZXh0dXJlQ29vcmQgdG8gcGl4ZWxDb29yZFxuICAgICAgICAgICAgICAgIHZlYzIgcGl4ZWxDb29yZCA9IHZUZXh0dXJlQ29vcmQgKiBmaWx0ZXJBcmVhLnh5IC0gdmVjMig0LjAsIDQuMCk7ICAgLy8gRklYTUU6IFRoZXJlJ3MgYSBzaGlmdCBvZiA0ICogNCBwaXhlbHMsIGRvbid0IGtub3cgd2h5Li4uXG5cbiAgICAgICAgICAgICAgICBib29sIGluc2lkZSA9IGZhbHNlO1xuXG4gICAgICAgICAgICAgICAgaWYgKHNoYXBlID09IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgaW5zaWRlID0gZGlzdGFuY2UocGl4ZWxDb29yZCwgY2lyY2xlLnh5KSA8PSBjaXJjbGUuejtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHNoYXBlID09IDIpIHtcbiAgICAgICAgICAgICAgICAgICAgaW5zaWRlID0gcGl4ZWxDb29yZC54ID49IHJlY3RhbmdsZS54ICYmIHBpeGVsQ29vcmQueCA8PSByZWN0YW5nbGUueiAmJiBwaXhlbENvb3JkLnkgPj0gcmVjdGFuZ2xlLnkgJiYgcGl4ZWxDb29yZC55IDw9IHJlY3RhbmdsZS53O1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChpbnNpZGUpIHtcbiAgICAgICAgICAgICAgICAgICAgdmVjNCBjb2xvciA9IHZlYzQoMC4wKTtcbiAgICAgICAgICAgICAgICAgICAgZmxvYXQgdG90YWwgPSAwLjA7XG5cbiAgICAgICAgICAgICAgICAgICAgZmxvYXQgb2Zmc2V0ID0gcmFuZG9tKHZlYzMoMTIuOTg5OCwgNzguMjMzLCAxNTEuNzE4MiksIDAuMCk7XG5cbiAgICAgICAgICAgICAgICAgICAgZm9yIChmbG9hdCB0ID0gLTMwLjA7IHQgPD0gMzAuMDsgdCsrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBmbG9hdCBwZXJjZW50ID0gKHQgKyBvZmZzZXQgLSAwLjUpIC8gMzAuMDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZsb2F0IHdlaWdodCA9IDEuMCAtIGFicyhwZXJjZW50KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZlYzQgc2FtcGxlID0gdGV4dHVyZTJEKHVTYW1wbGVyLCB2VGV4dHVyZUNvb3JkICsgZGVsdGEgLyB0ZXhTaXplICogcGVyY2VudCAqIGJsdXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlLnJnYiAqPSBzYW1wbGUuYTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yICs9IHNhbXBsZSAqIHdlaWdodDtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsICs9IHdlaWdodDtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIGdsX0ZyYWdDb2xvciA9IGNvbG9yIC8gdG90YWw7XG4gICAgICAgICAgICAgICAgICAgIGdsX0ZyYWdDb2xvci5yZ2IgLz0gZ2xfRnJhZ0NvbG9yLmEgKyAwLjAwMDAxO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGdsX0ZyYWdDb2xvciA9IHRleHR1cmUyRCh1U2FtcGxlciwgdmVjMih2VGV4dHVyZUNvb3JkLnMsIHZUZXh0dXJlQ29vcmQudCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgYFxuXG4gICAgICAgIHN1cGVyKHZlcnRleCwgZnJhZ21lbnQpXG4gICAgICAgIFxuICAgICAgICBpZiAoc2hhcGUudHlwZSA9PT0gJ2NpcmNsZScpIHtcbiAgICAgICAgICAgIHRoaXMudW5pZm9ybXMuc2hhcGUgPSAxXG4gICAgICAgICAgICB0aGlzLnVuaWZvcm1zLmNpcmNsZSA9IFtzaGFwZS54LCBzaGFwZS55LCBzaGFwZS5yXVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy51bmlmb3Jtcy5zaGFwZSA9IDJcbiAgICAgICAgICAgIHRoaXMudW5pZm9ybXMucmVjdGFuZ2xlID0gW3NoYXBlLngsIHNoYXBlLnksIHNoYXBlLnggKyBzaGFwZS53aWR0aCwgc2hhcGUueSArIHNoYXBlLmhlaWdodF1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLnVuaWZvcm1zLmJsdXIgPSBibHVyXG4gICAgICAgIHRoaXMudW5pZm9ybXMuZGVsdGEgPSBuZXcgUElYSS5Qb2ludCgwLCAwKVxuICAgICAgICB0aGlzLnVuaWZvcm1zLnRleFNpemUgPSBuZXcgUElYSS5Qb2ludCg0ODAsIDI3MClcblxuICAgICAgICB0aGlzLnVwZGF0ZURlbHRhKClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBUaGUgc3RyZW5ndGggb2YgdGhlIGJsdXIuXG4gICAgICpcbiAgICAgKiBAbWVtYmVyIHtudW1iZXJ9XG4gICAgICogQG1lbWJlcm9mIFBJWEkuZmlsdGVycy5UaWx0U2hpZnRBeGlzRmlsdGVyI1xuICAgICAqL1xuICAgIGdldCBibHVyKCkge1xuICAgICAgICByZXR1cm4gdGhpcy51bmlmb3Jtcy5ibHVyXG4gICAgfVxuICAgIHNldCBibHVyKHZhbHVlKSB7XG4gICAgICAgIHRoaXMudW5pZm9ybXMuYmx1ciA9IHZhbHVlXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFRoZSBibHVyIHNoYXBlLlxuICAgICAqXG4gICAgICogQG1lbWJlciB7UElYSS5SZWN0YW5nbGV9XG4gICAgICogQG1lbWJlcm9mIFBJWEkuZmlsdGVycy5UaWx0U2hpZnRBeGlzRmlsdGVyI1xuICAgICAqL1xuICAgIGdldCBzaGFwZSgpIHtcbiAgICAgICAgaWYgKHRoaXMudW5pZm9ybXMuc2hhcGUgPT09IDEpIHtcbiAgICAgICAgICAgIGNvbnN0IGNpcmNsZSA9IHRoaXMudW5pZm9ybXMuY2lyY2xlXG4gICAgICAgICAgICByZXR1cm4gbmV3IFBJWEkuQ2lyY2xlKGNpcmNsZVswXSwgY2lyY2xlWzFdLCBjaXJjbGVbMl0pXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCByZWN0YW5nbGUgPSB0aGlzLnVuaWZvcm1zLnJlY3RhbmdsZVxuICAgICAgICAgICAgcmV0dXJuIG5ldyBQSVhJLlJlY3RhbmdsZShyZWN0YW5nbGVbMF0sIHJlY3RhbmdsZVsxXSwgcmVjdGFuZ2xlWzJdLCByZWN0YW5nbGVbM10pXG4gICAgICAgIH1cbiAgICB9XG4gICAgc2V0IHNoYXBlKHZhbHVlKSB7XG4gICAgICAgIGlmICh2YWx1ZS50eXBlID09PSAnY2lyY2xlJykge1xuICAgICAgICAgICAgdGhpcy51bmlmb3Jtcy5zaGFwZSA9IDFcbiAgICAgICAgICAgIHRoaXMudW5pZm9ybXMuY2lyY2xlID0gW3ZhbHVlLngsIHZhbHVlLnksIHZhbHVlLnJdXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnVuaWZvcm1zLnNoYXBlID0gMlxuICAgICAgICAgICAgdGhpcy51bmlmb3Jtcy5yZWN0YW5nbGUgPSBbdmFsdWUueCwgdmFsdWUueSwgdmFsdWUueCArIHZhbHVlLndpZHRoLCB2YWx1ZS55ICsgdmFsdWUuaGVpZ2h0XVxuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIEEgVGlsdFNoaWZ0WEZpbHRlci5cbiAqXG4gKiBAY2xhc3NcbiAqIEBleHRlbmRzIFBJWEkuVGlsdFNoaWZ0QXhpc0ZpbHRlclxuICogQHByaXZhdGVcbiAqL1xuY2xhc3MgVGlsdFNoaWZ0WEZpbHRlciBleHRlbmRzIFRpbHRTaGlmdEF4aXNGaWx0ZXIge1xuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGZpbHRlciBkZWx0YSB2YWx1ZXMuXG4gICAgICovXG4gICAgdXBkYXRlRGVsdGEoKSB7XG4gICAgICAgIHRoaXMudW5pZm9ybXMuZGVsdGEueCA9IDAuMVxuICAgICAgICB0aGlzLnVuaWZvcm1zLmRlbHRhLnkgPSAwXG4gICAgfVxufVxuXG4vKipcbiAqIEEgVGlsdFNoaWZ0WUZpbHRlci5cbiAqXG4gKiBAY2xhc3NcbiAqIEBleHRlbmRzIFBJWEkuVGlsdFNoaWZ0QXhpc0ZpbHRlclxuICogQHByaXZhdGVcbiAqL1xuY2xhc3MgVGlsdFNoaWZ0WUZpbHRlciBleHRlbmRzIFRpbHRTaGlmdEF4aXNGaWx0ZXIge1xuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGZpbHRlciBkZWx0YSB2YWx1ZXMuXG4gICAgICovXG4gICAgdXBkYXRlRGVsdGEoKSB7XG4gICAgICAgIHRoaXMudW5pZm9ybXMuZGVsdGEueCA9IDBcbiAgICAgICAgdGhpcy51bmlmb3Jtcy5kZWx0YS55ID0gMC4xXG4gICAgfVxufVxuXG4iLCIvLyBJbiBvcmRlciB0byB0ZXN0IHRoaXMgaW50ZXJmYWNlIGltcGxlbWVudGF0aW9uIHJ1biBqc2MgaW50ZXJmYWNlLmpzXG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEludGVyZmFjZSB7XG4gICAgLy8gQWJzdHJhY3QgaW50ZXJmYWNlIHRoYXQgc2hvdWxkIGJlIGV4dGVuZGVkIGluIGludGVyZmFjZSBzdWJjbGFzc2VzLlxuICAgIC8vIEJ5IGNvbnZlbnRpb24gYWxsIGludGVyZmFjZXMgc2hvdWxkIHN0YXJ0IHdpdGggYW4gdXBwZXIgJ0knXG5cbiAgICBzdGF0aWMgaW1wbGVtZW50YXRpb25FcnJvcihrbGFzcykge1xuICAgICAgICBsZXQgaW50ZXJmYWNlS2V5cyA9IFJlZmxlY3Qub3duS2V5cyh0aGlzLnByb3RvdHlwZSlcbiAgICAgICAgbGV0IGNsYXNzS2V5cyA9IFJlZmxlY3Qub3duS2V5cyhrbGFzcy5wcm90b3R5cGUpXG4gICAgICAgIGZvcihsZXQga2V5IG9mIGludGVyZmFjZUtleXMpIHtcbiAgICAgICAgICAgIGxldCBpbnRlcmZhY2VEZXNjID0gdGhpcy5wcm90b3R5cGVba2V5XVxuICAgICAgICAgICAgbGV0IGNsYXNzRGVzYyA9IGtsYXNzLnByb3RvdHlwZVtrZXldXG4gICAgICAgICAgICBpZiAodHlwZW9mKGNsYXNzRGVzYykgPT0gJ3VuZGVmaW5lZCcpXG4gICAgICAgICAgICAgICAgcmV0dXJuICdNaXNzaW5nICcgKyBrZXlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIHN0YXRpYyBpbXBsZW1lbnRlZEJ5KGtsYXNzKSB7XG4gICAgICAgIC8vIEluIHRoZSBmaXJzdCBzdGVwIG9ubHkgY2hlY2tzIHdoZXRoZXIgdGhlIG1ldGhvZHMgb2YgdGhpc1xuICAgICAgICAvLyBpbnRlcmZhY2UgYXJlIGFsbCBpbXBsZW1lbnRlZCBieSB0aGUgZ2l2ZW4gY2xhc3NcbiAgICAgICAgbGV0IGVycm9yID0gdGhpcy5pbXBsZW1lbnRhdGlvbkVycm9yKGtsYXNzKVxuICAgICAgICByZXR1cm4gZXJyb3IgPT0gbnVsbFxuICAgIH1cblxuICAgICAgICAvLyBUT0RPOiBTcGVjaWZ5IG9wdGlvbmFsIG1ldGhvZHNcbi8vICAgICBzdGF0aWMgb3B0aW9uYWxNZXRob2RzKCkge1xuLy8gICAgICAgICByZXR1cm4gW3RoaXMub25Nb3VzZVdoZWVsXVxuLy8gICAgIH1cbn1cbiIsIi8qIGdsb2JhbHMgSGFtbWVyLCBwcm9wYWdhdGluZyAqL1xuLyplc2xpbnQgbm8tY29uc29sZTogW1wiZXJyb3JcIiwgeyBhbGxvdzogW1wibG9nXCIsIFwid2FyblwiLCBcImluZm9cIiwgXCJlcnJvclwiXSB9XSovXG5cbmltcG9ydCBJbnRlcmZhY2UgZnJvbSAnLi9pbnRlcmZhY2UuanMnXG5pbXBvcnQgeyBQb2ludHMsIEFuZ2xlLCBNYXBQcm94eSB9IGZyb20gJy4vdXRpbHMuanMnXG5pbXBvcnQgRXZlbnRzIGZyb20gJy4vZXZlbnRzLmpzJ1xuXG4vKiogSW50ZXJhY3Rpb24gcGF0dGVybnNcblxuICAgIFNlZSBpbnRlcmFjdGlvbi5odG1sIGZvciBleHBsYW5hdGlvblxuKi9cblxuZXhwb3J0IGNsYXNzIElJbnRlcmFjdGlvblRhcmdldCBleHRlbmRzIEludGVyZmFjZSB7XG4gICAgY2FwdHVyZShldmVudCkge1xuICAgICAgICByZXR1cm4gdHlwZW9mIHRydWVcbiAgICB9XG5cbiAgICBvblN0YXJ0KGV2ZW50LCBpbnRlcmFjdGlvbikgeyB9XG4gICAgb25Nb3ZlKGV2ZW50LCBpbnRlcmFjdGlvbikgeyB9XG4gICAgb25FbmQoZXZlbnQsIGludGVyYWN0aW9uKSB7IH1cblxuICAgIG9uTW91c2VXaGVlbChldmVudCkgeyB9XG59XG5cbmV4cG9ydCBjbGFzcyBJSW50ZXJhY3Rpb25NYXBwZXJUYXJnZXQgZXh0ZW5kcyBJbnRlcmZhY2Uge1xuICAgIGNhcHR1cmUoZXZlbnQpIHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZiB0cnVlXG4gICAgfVxuXG4gICAgZmluZFRhcmdldChldmVudCwgbG9jYWwsIGdsb2JhbCkge1xuICAgICAgICByZXR1cm4gSUludGVyYWN0aW9uVGFyZ2V0XG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgUG9pbnRNYXAgZXh0ZW5kcyBNYXBQcm94eSB7XG4gICAgLy8gQ29sbGVjdHMgdG91Y2ggcG9pbnRzLCBtb3VzZSBjb29yZGluYXRlcywgZXRjLiBhcyBrZXkgdmFsdWUgcGFpcnMuXG4gICAgLy8gS2V5cyBhcmUgcG9pbnRlciBhbmQgdG91Y2ggaWRzLCB0aGUgc3BlY2lhbCBcIm1vdXNlXCIga2V5LlxuICAgIC8vIFZhbHVlcyBhcmUgcG9pbnRzLCBpLmUuIGFsbCBvYmplY3RzIHdpdGggbnVtZXJpYyB4IGFuZCB5IHByb3BlcnRpZXMuXG4gICAgY29uc3RydWN0b3IocG9pbnRzID0ge30pIHtcbiAgICAgICAgc3VwZXIoKVxuICAgICAgICBmb3IgKGxldCBrZXkgaW4gcG9pbnRzKSB7XG4gICAgICAgICAgICB0aGlzLnNldChrZXksIHBvaW50c1trZXldKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgdG9TdHJpbmcoKSB7XG4gICAgICAgIGxldCBwb2ludHMgPSBbXVxuICAgICAgICBmb3IgKGxldCBrZXkgb2YgdGhpcy5rZXlzKCkpIHtcbiAgICAgICAgICAgIGxldCB2YWx1ZSA9IHRoaXMuZ2V0KGtleSlcbiAgICAgICAgICAgIHBvaW50cy5wdXNoKGAke2tleX06e3g6JHt2YWx1ZS54fSwgeToke3ZhbHVlLnl9fWApXG4gICAgICAgIH1cbiAgICAgICAgbGV0IGF0dHJzID0gcG9pbnRzLmpvaW4oJywgJylcbiAgICAgICAgcmV0dXJuIGBbUG9pbnRNYXAgJHthdHRyc31dYFxuICAgIH1cblxuICAgIGNsb25lKCkge1xuICAgICAgICBsZXQgcmVzdWx0ID0gbmV3IFBvaW50TWFwKClcbiAgICAgICAgZm9yIChsZXQga2V5IG9mIHRoaXMua2V5cygpKSB7XG4gICAgICAgICAgICBsZXQgdmFsdWUgPSB0aGlzLmdldChrZXkpXG4gICAgICAgICAgICByZXN1bHQuc2V0KGtleSwgeyB4OiB2YWx1ZS54LCB5OiB2YWx1ZS55IH0pXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgIH1cblxuICAgIGtleU9mKHZhbHVlKSB7XG4gICAgICAgIGZvciAobGV0IGtleSBvZiB0aGlzLmtleXMoKSkge1xuICAgICAgICAgICAgbGV0IHAgPSB0aGlzLmdldChrZXkpXG4gICAgICAgICAgICBpZiAocC54ID09IHZhbHVlLnggJiYgcC55ID09IHZhbHVlLnkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4ga2V5XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG5cbiAgICBmaXJzdEtleSgpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IG9mIHRoaXMua2V5cygpKSB7XG4gICAgICAgICAgICByZXR1cm4ga2V5XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG5cbiAgICBmaXJzdCgpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IG9mIHRoaXMua2V5cygpKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5nZXQoa2V5KVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgfVxuXG4gICAgZmFydGhlc3RzKCkge1xuICAgICAgICBpZiAodGhpcy5zaXplID09IDApIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsXG4gICAgICAgIH1cbiAgICAgICAgbGV0IHBhaXJzID0gW11cbiAgICAgICAgZm9yIChsZXQga2V5IG9mIHRoaXMua2V5cygpKSB7XG4gICAgICAgICAgICBsZXQgcCA9IHRoaXMuZ2V0KGtleSlcbiAgICAgICAgICAgIHAua2V5ID0ga2V5XG4gICAgICAgICAgICBmb3IgKGxldCBrIG9mIHRoaXMua2V5cygpKSB7XG4gICAgICAgICAgICAgICAgbGV0IHEgPSB0aGlzLmdldChrKVxuICAgICAgICAgICAgICAgIHEua2V5ID0ga1xuICAgICAgICAgICAgICAgIHBhaXJzLnB1c2goW3AsIHFdKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGxldCBzb3J0ZWQgPSBwYWlycy5zb3J0KChhLCBiKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gUG9pbnRzLmRpc3RhbmNlKGJbMF0sIGJbMV0pIC0gUG9pbnRzLmRpc3RhbmNlKGFbMF0sIGFbMV0pXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVybiBzb3J0ZWRbMF1cbiAgICB9XG5cbiAgICBtZWFuKCkge1xuICAgICAgICBpZiAodGhpcy5zaXplID09IDApIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsXG4gICAgICAgIH1cbiAgICAgICAgbGV0IHggPSAwLjAsXG4gICAgICAgICAgICB5ID0gMC4wXG4gICAgICAgIGZvciAobGV0IHAgb2YgdGhpcy52YWx1ZXMoKSkge1xuICAgICAgICAgICAgeCArPSBwLnhcbiAgICAgICAgICAgIHkgKz0gcC55XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHsgeDogeCAvIHRoaXMuc2l6ZSwgeTogeSAvIHRoaXMuc2l6ZSB9XG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgSW50ZXJhY3Rpb25EZWx0YSB7XG4gICAgY29uc3RydWN0b3IoeCwgeSwgem9vbSwgcm90YXRlLCBhYm91dCkge1xuICAgICAgICB0aGlzLnggPSB4XG4gICAgICAgIHRoaXMueSA9IHlcbiAgICAgICAgdGhpcy56b29tID0gem9vbVxuICAgICAgICB0aGlzLnJvdGF0ZSA9IHJvdGF0ZVxuICAgICAgICB0aGlzLmFib3V0ID0gYWJvdXRcbiAgICB9XG5cbiAgICB0b1N0cmluZygpIHtcbiAgICAgICAgbGV0IHZhbHVlcyA9IFtdXG4gICAgICAgIGZvciAobGV0IGtleSBvZiBPYmplY3Qua2V5cyh0aGlzKSkge1xuICAgICAgICAgICAgbGV0IHZhbHVlID0gdGhpc1trZXldXG4gICAgICAgICAgICBpZiAoa2V5ID09ICdhYm91dCcpIHtcbiAgICAgICAgICAgICAgICB2YWx1ZXMucHVzaChgJHtrZXl9Ont4OiR7dmFsdWUueH0sIHk6JHt2YWx1ZS55fX1gKVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB2YWx1ZXMucHVzaChgJHtrZXl9OiR7dmFsdWV9YClcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBsZXQgYXR0cnMgPSB2YWx1ZXMuam9pbignLCAnKVxuICAgICAgICByZXR1cm4gYFtJbnRlcmFjdGlvbkRlbHRhICR7YXR0cnN9XWBcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBJbnRlcmFjdGlvblBvaW50cyB7XG4gICAgY29uc3RydWN0b3IocGFyZW50ID0gbnVsbCkge1xuICAgICAgICB0aGlzLnBhcmVudCA9IHBhcmVudFxuICAgICAgICB0aGlzLmN1cnJlbnQgPSBuZXcgUG9pbnRNYXAoKVxuICAgICAgICB0aGlzLnByZXZpb3VzID0gbmV3IFBvaW50TWFwKClcbiAgICAgICAgdGhpcy5zdGFydCA9IG5ldyBQb2ludE1hcCgpXG4gICAgICAgIHRoaXMuZW5kZWQgPSBuZXcgUG9pbnRNYXAoKVxuICAgICAgICB0aGlzLnRpbWVzdGFtcHMgPSBuZXcgTWFwKClcbiAgICB9XG5cbiAgICBtb3ZlZChrZXkpIHtcbiAgICAgICAgbGV0IGN1cnJlbnQgPSB0aGlzLmN1cnJlbnQuZ2V0KGtleSlcbiAgICAgICAgbGV0IHByZXZpb3VzID0gdGhpcy5wcmV2aW91cy5nZXQoa2V5KVxuICAgICAgICByZXR1cm4gUG9pbnRzLnN1YnRyYWN0KGN1cnJlbnQsIHByZXZpb3VzKVxuICAgIH1cblxuICAgIG1vdmUoKSB7XG4gICAgICAgIGxldCBjdXJyZW50ID0gdGhpcy5jdXJyZW50Lm1lYW4oKVxuICAgICAgICBsZXQgcHJldmlvdXMgPSB0aGlzLnByZXZpb3VzLm1lYW4oKVxuICAgICAgICByZXR1cm4gUG9pbnRzLnN1YnRyYWN0KGN1cnJlbnQsIHByZXZpb3VzKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENvbXB1dGVzIHRoZSBkZWx0YSBiZXR3ZWVuIHByZXZpb3VzIGFuZCBjdXJyZW50IGFuZ2xlcy4gQ29ycmVjdHNcbiAgICAgKiB2YWx1ZSB0aGF0IGFyZSBsYXJnZXIgdGhhbiA0NcKwXG4gICAgICogQHBhcmFtIHsqfSBhXG4gICAgICogQHBhcmFtIHsqfSBiXG4gICAgICogQHJldHVybnMgZGVsdGFcbiAgICAgKi9cbiAgICBkaWZmQW5nbGUoYSwgYikge1xuICAgICAgICBsZXQgYWxwaGEgPSBNYXRoLmF0YW4yKE1hdGguc2luKGEgLSBiKSwgTWF0aC5jb3MoYSAtIGIpKVxuICAgICAgICBpZiAoTWF0aC5hYnMoYWxwaGEpID4gTWF0aC5QSSAvIDQpIHtcbiAgICAgICAgICAgIGFscGhhIC09IE1hdGguUElcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYWxwaGFcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDb21wdXRlcyB0aGUgZGVsdGEgYmV0d2VlbiBpbnRlcmFjdGlvbiBwb2ludHMgYXQgdCBhbmQgdCsxLlxuICAgICAqXG4gICAgICogQHJldHVybnMgSW50ZXJhY3Rpb25EZWx0YVxuICAgICAqIEBtZW1iZXJvZiBJbnRlcmFjdGlvblBvaW50c1xuICAgICAqL1xuICAgIGRlbHRhKCkge1xuICAgICAgICBsZXQgY3NpemUgPSB0aGlzLmN1cnJlbnQuc2l6ZVxuICAgICAgICBsZXQgcHNpemUgPSB0aGlzLnByZXZpb3VzLnNpemVcbiAgICAgICAgaWYgKGNzaXplID49IDIgJiYgY3NpemUgPT0gcHNpemUpIHtcbiAgICAgICAgICAgIC8vIFJlZHVjZSB0byB0aGUgdHdvIGZhcnRoZXN0cyBwb2ludHNcbiAgICAgICAgICAgIGxldCBjdXJyZW50ID0gdGhpcy5jdXJyZW50LmZhcnRoZXN0cygpXG5cbiAgICAgICAgICAgIGxldCBjMSA9IGN1cnJlbnRbMF1cbiAgICAgICAgICAgIGxldCBjMiA9IGN1cnJlbnRbMV1cblxuICAgICAgICAgICAgbGV0IHAxID0gdGhpcy5wcmV2aW91cy5nZXQoYzEua2V5KVxuICAgICAgICAgICAgbGV0IHAyID0gdGhpcy5wcmV2aW91cy5nZXQoYzIua2V5KVxuXG4gICAgICAgICAgICAvL2xldCBwMSA9IHByZXZpb3VzWzBdXG4gICAgICAgICAgICAvL2xldCBwMiA9IHByZXZpb3VzWzFdXG5cbiAgICAgICAgICAgIGxldCBkMSA9IFBvaW50cy5zdWJ0cmFjdChjMSwgcDEpXG4gICAgICAgICAgICBsZXQgZDIgPSBQb2ludHMuc3VidHJhY3QoYzIsIHAyKVxuICAgICAgICAgICAgbGV0IGNtID0gUG9pbnRzLm1lYW4oYzEsIGMyKVxuICAgICAgICAgICAgLy9sZXQgcG0gPSBQb2ludHMubWVhbihwMSwgcDIpXG4gICAgICAgICAgICAvLyBVTzogVXNpbmcgdGhlIG1lYW4gbGVhZCB0byBqdW1wcyBiZXR3ZWVuIHRpbWUgc2xpY2VzIHdpdGggMyBhbmQgMiBmaW5nZXJzXG4gICAgICAgICAgICAvLyBXZSB1c2UgdGhlIG1lYW4gb2YgZGVsdGFzIGluc3RlYWRcbiAgICAgICAgICAgIGxldCBkZWx0YSA9IFBvaW50cy5tZWFuKGQxLCBkMikgLy9Qb2ludHMuc3VidHJhY3QoY20sIHBtKVxuICAgICAgICAgICAgbGV0IHpvb20gPSAxLjBcbiAgICAgICAgICAgIGxldCBkaXN0YW5jZTEgPSBQb2ludHMuZGlzdGFuY2UocDEsIHAyKVxuICAgICAgICAgICAgbGV0IGRpc3RhbmNlMiA9IFBvaW50cy5kaXN0YW5jZShjMSwgYzIpXG4gICAgICAgICAgICBpZiAoZGlzdGFuY2UxICE9IDAgJiYgZGlzdGFuY2UyICE9IDApIHtcbiAgICAgICAgICAgICAgICB6b29tID0gZGlzdGFuY2UyIC8gZGlzdGFuY2UxXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBsZXQgY3VycmVudEFuZ2xlID0gUG9pbnRzLmFuZ2xlKGMxLCBjMilcbiAgICAgICAgICAgIGxldCBwcmV2aW91c0FuZ2xlID0gUG9pbnRzLmFuZ2xlKHAxLCBwMilcbiAgICAgICAgICAgIGxldCBhbHBoYSA9IHRoaXMuZGlmZkFuZ2xlKGN1cnJlbnRBbmdsZSwgcHJldmlvdXNBbmdsZSlcbiAgICAgICAgICAgIHJldHVybiBuZXcgSW50ZXJhY3Rpb25EZWx0YShkZWx0YS54LCBkZWx0YS55LCB6b29tLCBhbHBoYSwgY20pXG4gICAgICAgIH0gZWxzZSBpZiAoY3NpemUgPT0gMSAmJiBwc2l6ZSA9PSAxICYmIHRoaXMuY3VycmVudC5maXJzdEtleSgpID09IHRoaXMucHJldmlvdXMuZmlyc3RLZXkoKSkge1xuICAgICAgICAgICAgLy8gV2UgbmVlZCB0byBlbnN1cmUgdGhhdCB0aGUga2V5cyBhcmUgdGhlIHNhbWVcbiAgICAgICAgICAgIGxldCBjdXJyZW50ID0gdGhpcy5jdXJyZW50LmZpcnN0KClcbiAgICAgICAgICAgIGxldCBwcmV2aW91cyA9IHRoaXMucHJldmlvdXMuZmlyc3QoKVxuICAgICAgICAgICAgbGV0IGRlbHRhID0gUG9pbnRzLnN1YnRyYWN0KGN1cnJlbnQsIHByZXZpb3VzKVxuICAgICAgICAgICAgcmV0dXJuIG5ldyBJbnRlcmFjdGlvbkRlbHRhKGRlbHRhLngsIGRlbHRhLnksIDEuMCwgMC4wLCBjdXJyZW50KVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgfVxuXG4gICAgc3RhcnRlZChrZXksIHBvaW50KSB7XG4gICAgICAgIHRoaXMuY3VycmVudC5zZXQoa2V5LCBwb2ludClcbiAgICAgICAgdGhpcy5zdGFydC5zZXQoa2V5LCBwb2ludClcbiAgICAgICAgdGhpcy5wcmV2aW91cy5zZXQoa2V5LCBwb2ludClcbiAgICAgICAgdGhpcy50aW1lc3RhbXBzLnNldChrZXksIHBlcmZvcm1hbmNlLm5vdygpKVxuICAgIH1cblxuICAgIHVwZGF0ZShrZXksIHBvaW50KSB7XG4gICAgICAgIC8vIFJldHVybnMgdHJ1ZSBpZmYgdGhlIGtleSBpcyBuZXdcbiAgICAgICAgdGhpcy5jdXJyZW50LnNldChrZXksIHBvaW50KVxuICAgICAgICBpZiAoIXRoaXMuc3RhcnQuaGFzKGtleSkpIHtcbiAgICAgICAgICAgIHRoaXMuc3RhcnQuc2V0KGtleSwgcG9pbnQpXG4gICAgICAgICAgICB0aGlzLnByZXZpb3VzLnNldChrZXksIHBvaW50KVxuICAgICAgICAgICAgdGhpcy50aW1lc3RhbXBzLnNldChrZXksIHBlcmZvcm1hbmNlLm5vdygpKVxuICAgICAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICB1cGRhdGVQcmV2aW91cygpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IG9mIHRoaXMuY3VycmVudC5rZXlzKCkpIHtcbiAgICAgICAgICAgIHRoaXMucHJldmlvdXMuc2V0KGtleSwgdGhpcy5jdXJyZW50LmdldChrZXkpKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RvcChrZXksIHBvaW50KSB7XG4gICAgICAgIGlmICh0aGlzLmN1cnJlbnQuaGFzKGtleSkpIHtcbiAgICAgICAgICAgIHRoaXMuY3VycmVudC5kZWxldGUoa2V5KVxuICAgICAgICAgICAgdGhpcy5wcmV2aW91cy5kZWxldGUoa2V5KVxuICAgICAgICAgICAgdGhpcy5lbmRlZC5zZXQoa2V5LCBwb2ludClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZpbmlzaChrZXksIHBvaW50KSB7XG4gICAgICAgIHRoaXMuY3VycmVudC5kZWxldGUoa2V5KVxuICAgICAgICB0aGlzLnByZXZpb3VzLmRlbGV0ZShrZXkpXG4gICAgICAgIHRoaXMuc3RhcnQuZGVsZXRlKGtleSlcbiAgICAgICAgdGhpcy50aW1lc3RhbXBzLmRlbGV0ZShrZXkpXG4gICAgICAgIHRoaXMuZW5kZWQuZGVsZXRlKGtleSlcbiAgICB9XG5cbiAgICBpc0ZpbmlzaGVkKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jdXJyZW50LnNpemUgPT0gMFxuICAgIH1cblxuICAgIGlzTm9Mb25nZXJUd29GaW5nZXIoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnByZXZpb3VzLnNpemUgPiAxICYmIHRoaXMuY3VycmVudC5zaXplIDwgMlxuICAgIH1cblxuICAgIGlzVGFwKGtleSkge1xuICAgICAgICByZXR1cm4gdGhpcy5wYXJlbnQuaXNUYXAoa2V5KVxuICAgIH1cblxuICAgIGlzRG91YmxlVGFwKGtleSkge1xuICAgICAgICByZXR1cm4gdGhpcy5wYXJlbnQuaXNEb3VibGVUYXAoa2V5KVxuICAgIH1cblxuICAgIGlzTG9uZ1ByZXNzKGtleSkge1xuICAgICAgICByZXR1cm4gdGhpcy5wYXJlbnQuaXNMb25nUHJlc3Moa2V5KVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIEludGVyYWN0aW9uIGV4dGVuZHMgSW50ZXJhY3Rpb25Qb2ludHMge1xuICAgIGNvbnN0cnVjdG9yKHRhcERpc3RhbmNlID0gMTAsIHRhcER1cmF0aW9uID0gMjUwLjAsIGxvbmdQcmVzc1RpbWUgPSA1MDAuMCkge1xuICAgICAgICBzdXBlcigpXG4gICAgICAgIHRoaXMudGFwRGlzdGFuY2UgPSB0YXBEaXN0YW5jZVxuICAgICAgICB0aGlzLnRhcENvdW50cyA9IG5ldyBNYXAoKVxuICAgICAgICB0aGlzLnRhcFBvc2l0aW9ucyA9IG5ldyBNYXAoKVxuICAgICAgICB0aGlzLnRhcFRpbWVzdGFtcHMgPSBuZXcgTWFwKClcbiAgICAgICAgdGhpcy50YXBEdXJhdGlvbiA9IHRhcER1cmF0aW9uXG4gICAgICAgIHRoaXMubG9uZ1ByZXNzVGltZSA9IGxvbmdQcmVzc1RpbWVcbiAgICAgICAgdGhpcy50YXJnZXRzID0gbmV3IE1hcCgpXG4gICAgICAgIHRoaXMuc3ViSW50ZXJhY3Rpb25zID0gbmV3IE1hcCgpIC8vIHRhcmdldDpPYmplY3QgOiBJbnRlcmFjdGlvblBvaW50c1xuICAgIH1cblxuICAgIHN0b3Aoa2V5LCBwb2ludCkge1xuICAgICAgICBzdXBlci5zdG9wKGtleSwgcG9pbnQpXG4gICAgICAgIGZvciAobGV0IHBvaW50cyBvZiB0aGlzLnN1YkludGVyYWN0aW9ucy52YWx1ZXMoKSkge1xuICAgICAgICAgICAgcG9pbnRzLnN0b3Aoa2V5LCBwb2ludClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGFkZFRhcmdldChrZXksIHRhcmdldCkge1xuICAgICAgICB0aGlzLnRhcmdldHMuc2V0KGtleSwgdGFyZ2V0KVxuICAgICAgICB0aGlzLnN1YkludGVyYWN0aW9ucy5zZXQodGFyZ2V0LCBuZXcgSW50ZXJhY3Rpb25Qb2ludHModGhpcykpXG4gICAgfVxuXG4gICAgcmVtb3ZlVGFyZ2V0KGtleSkge1xuICAgICAgICBsZXQgdGFyZ2V0ID0gdGhpcy50YXJnZXRzLmdldChrZXkpXG4gICAgICAgIHRoaXMudGFyZ2V0cy5kZWxldGUoa2V5KVxuICAgICAgICAvLyBPbmx5IHJlbW92ZSB0YXJnZXQgaWYgbm8ga2V5cyBhcmUgcmVmZXJpbmcgdG8gdGhlIHRhcmdldFxuICAgICAgICBsZXQgcmVtb3ZlID0gdHJ1ZVxuICAgICAgICBmb3IgKGxldCB0IG9mIHRoaXMudGFyZ2V0cy52YWx1ZXMoKSkge1xuICAgICAgICAgICAgaWYgKHRhcmdldCA9PT0gdCkge1xuICAgICAgICAgICAgICAgIHJlbW92ZSA9IGZhbHNlXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHJlbW92ZSkge1xuICAgICAgICAgICAgdGhpcy5zdWJJbnRlcmFjdGlvbnMuZGVsZXRlKHRhcmdldClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZpbmlzaChrZXksIHBvaW50KSB7XG4gICAgICAgIHN1cGVyLmZpbmlzaChrZXksIHBvaW50KVxuICAgICAgICB0aGlzLnJlbW92ZVRhcmdldChrZXkpXG4gICAgfVxuXG4gICAgbWFwSW50ZXJhY3Rpb24ocG9pbnRzLCBhc3BlY3RzLCBtYXBwaW5nRnVuYykge1xuICAgICAgICAvLyBNYXAgY2VudHJhbGx5IHJlZ2lzdGVyZWQgcG9pbnRzIHRvIHRhcmdldCBpbnRlcmFjdGlvbnNcbiAgICAgICAgLy8gUmV0dXJucyBhbiBhcnJheSBvZiBbdGFyZ2V0LCB1cGRhdGVkIHN1YkludGVyYWN0aW9uXSBwYWlyc1xuICAgICAgICBsZXQgcmVzdWx0ID0gbmV3IE1hcCgpXG4gICAgICAgIGZvciAobGV0IGtleSBpbiBwb2ludHMpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnRhcmdldHMuaGFzKGtleSkpIHtcbiAgICAgICAgICAgICAgICBsZXQgdGFyZ2V0ID0gdGhpcy50YXJnZXRzLmdldChrZXkpXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuc3ViSW50ZXJhY3Rpb25zLmhhcyh0YXJnZXQpKSB7XG4gICAgICAgICAgICAgICAgICAgIGxldCBpbnRlcmFjdGlvbiA9IHRoaXMuc3ViSW50ZXJhY3Rpb25zLmdldCh0YXJnZXQpXG4gICAgICAgICAgICAgICAgICAgIGZvciAobGV0IGFzcGVjdCBvZiBhc3BlY3RzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgcG9pbnRNYXAgPSB0aGlzW2FzcGVjdF1cbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBwb2ludCA9IHBvaW50TWFwLmdldChrZXkpXG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgbWFwcGVkID0gbWFwcGluZ0Z1bmMocG9pbnQsIHRhcmdldClcbiAgICAgICAgICAgICAgICAgICAgICAgIGludGVyYWN0aW9uW2FzcGVjdF0uc2V0KGtleSwgbWFwcGVkKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdC5zZXQodGFyZ2V0LCBpbnRlcmFjdGlvbilcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgIH1cblxuICAgIHJlZ2lzdGVyVGFwKGtleSwgcG9pbnQpIHtcbiAgICAgICAgaWYgKHRoaXMudGFwQ291bnRzLmhhcyhrZXkpKSB7XG4gICAgICAgICAgICBsZXQgY291bnQgPSB0aGlzLnRhcENvdW50cy5nZXQoa2V5KVxuICAgICAgICAgICAgdGhpcy50YXBDb3VudHMuc2V0KGtleSwgY291bnQrMSlcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMudGFwQ291bnRzLnNldChrZXksIDEpXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy50YXBQb3NpdGlvbnMuc2V0KGtleSwgcG9pbnQpXG4gICAgICAgIHRoaXMudGFwVGltZXN0YW1wcy5zZXQoa2V5LCBwZXJmb3JtYW5jZS5ub3coKSlcbiAgICB9XG5cbiAgICB1bnJlZ2lzdGVyVGFwKGtleSkge1xuICAgICAgICB0aGlzLnRhcENvdW50cy5kZWxldGUoa2V5KVxuICAgICAgICB0aGlzLnRhcFBvc2l0aW9ucy5kZWxldGUoa2V5KVxuICAgICAgICB0aGlzLnRhcFRpbWVzdGFtcHMuZGVsZXRlKGtleSlcbiAgICB9XG5cbiAgICBpc1RhcChrZXkpIHtcbiAgICAgICAgbGV0IGVuZGVkID0gdGhpcy5lbmRlZC5nZXQoa2V5KVxuICAgICAgICBsZXQgc3RhcnQgPSB0aGlzLnN0YXJ0LmdldChrZXkpXG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIHN0YXJ0ICYmXG4gICAgICAgICAgICBlbmRlZCAmJlxuICAgICAgICAgICAgUG9pbnRzLmRpc3RhbmNlKGVuZGVkLCBzdGFydCkgPCB0aGlzLnRhcERpc3RhbmNlXG4gICAgICAgICkge1xuICAgICAgICAgICAgbGV0IHQxID0gdGhpcy50aW1lc3RhbXBzLmdldChrZXkpXG4gICAgICAgICAgICBsZXQgdG9va0xvbmcgPSBwZXJmb3JtYW5jZS5ub3coKSA+IHQxICsgdGhpcy5sb25nUHJlc3NUaW1lXG4gICAgICAgICAgICBpZiAodG9va0xvbmcpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgaXNEb3VibGVUYXAoa2V5KSB7XG4gICAgICAgIGxldCBlbmRlZCA9IHRoaXMuZW5kZWQuZ2V0KGtleSlcbiAgICAgICAgaWYgKHRoaXMudGFwQ291bnRzLmhhcyhrZXkpICYmIHRoaXMudGFwQ291bnRzLmdldChrZXkpID4gMikge1xuICAgICAgICAgICAgdGhpcy51bnJlZ2lzdGVyVGFwKGtleSlcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy50YXBQb3NpdGlvbnMuaGFzKGtleSkpIHtcbiAgICAgICAgICAgIGxldCBwb3MgPSB0aGlzLnRhcFBvc2l0aW9ucy5nZXQoa2V5KVxuICAgICAgICAgICAgaWYgKFBvaW50cy5kaXN0YW5jZShlbmRlZCwgcG9zKSA+IHRoaXMudGFwRGlzdGFuY2UpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnVucmVnaXN0ZXJUYXAoa2V5KVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnRhcFRpbWVzdGFtcHMuaGFzKGtleSkgJiYgcGVyZm9ybWFuY2Uubm93KCkgPiB0aGlzLnRhcFRpbWVzdGFtcHMuZ2V0KGtleSkgKyB0aGlzLnRhcER1cmF0aW9uKSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUubG9nKFwidGFwIHRvbyBsb25nXCIpXG4gICAgICAgICAgICB0aGlzLnVucmVnaXN0ZXJUYXAoa2V5KVxuICAgICAgICB9XG4gICAgICAgIGxldCByZXN1bHQgPSBmYWxzZVxuICAgICAgICBpZiAodGhpcy5pc1RhcChrZXkpKSB7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHRoaXMucmVnaXN0ZXJUYXAoa2V5LCBlbmRlZClcbiAgICAgICAgICAgIHJlc3VsdCA9IHRoaXMudGFwQ291bnRzLmdldChrZXkpID09IDJcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMudW5yZWdpc3RlclRhcChrZXkpXG4gICAgICAgIH1cbiAgICAgICAgLy9jb25zb2xlLmxvZyhcImlzRG91YmxlVGFwXCIsIHRoaXMudGFwQ291bnRzLmdldChrZXkpLCByZXN1bHQpXG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG5cbiAgICBpc0FueVRhcCgpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IG9mIHRoaXMuZW5kZWQua2V5cygpKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5pc1RhcChrZXkpKSByZXR1cm4gdHJ1ZVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIGlzTG9uZ1ByZXNzKGtleSkge1xuICAgICAgICBsZXQgZW5kZWQgPSB0aGlzLmVuZGVkLmdldChrZXkpXG4gICAgICAgIGxldCBzdGFydCA9IHRoaXMuc3RhcnQuZ2V0KGtleSlcbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgc3RhcnQgJiZcbiAgICAgICAgICAgIGVuZGVkICYmXG4gICAgICAgICAgICBQb2ludHMuZGlzdGFuY2UoZW5kZWQsIHN0YXJ0KSA8IHRoaXMudGFwRGlzdGFuY2VcbiAgICAgICAgKSB7XG4gICAgICAgICAgICBsZXQgdDEgPSB0aGlzLnRpbWVzdGFtcHMuZ2V0KGtleSlcbiAgICAgICAgICAgIGxldCB0b29rTG9uZyA9IHBlcmZvcm1hbmNlLm5vdygpID4gdDEgKyB0aGlzLmxvbmdQcmVzc1RpbWVcbiAgICAgICAgICAgIGlmICh0b29rTG9uZykge1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICBpc0FueUxvbmdQcmVzcygpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IG9mIHRoaXMuZW5kZWQua2V5cygpKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5pc0xvbmdQcmVzcyhrZXkpKSByZXR1cm4gdHJ1ZVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIGlzU3R5bHVzKGtleSkge1xuICAgICAgICByZXR1cm4ga2V5ID09PSAnc3R5bHVzJ1xuICAgIH1cbn1cblxuLyoqXG4gKiBUaGlzIGNsYXNzIGltcGxlbWVudHMgdGhlIG1haW4gZGVsZWdhdGUgZnVuY3Rpb25hbGl0eTogQWxsIG5lY2Vzc2FyeSBldmVudCBoYW5kbGVycyBhcmUgcmVnaXN0ZXJlZCBmb3IgdGhlXG4gKiBnaXZlbiBlbGVtZW50LiBVc2VzIFBvaW50ZXJFdmVudHMgaWYgYXZhaWxhYmxlIG9yIFRvdWNoRXZlbnRzIG9uIGlPUy4gVGhlIGZhbGxiYWNrIGlzIG9uIG1vdXNlIGV2ZW50cy5cbiAqIENvbGxlY3RzIHRoZSBldmVudHMgaWYgdGhlIGludGVyYWN0aW9uIHRhcmdldCBjYXB0dXJlcyB0aGUgc3RhcnQgZXZlbnQgKGkuZS4gZGVjbGFyZXMgdGhhdFxuICogdGhlIHRhcmdldCB3YW50cyB0aGUgc3RhcnQgZXZlbnQgYXMgd2VsbCBhcyBhbGwgZm9sbG93aW5nIG1vdmUgYW5kIGVuZCBldmNlbnRzLilcbiAqXG4gKiBAZXhwb3J0XG4gKiBAY2xhc3MgSW50ZXJhY3Rpb25EZWxlZ2F0ZVxuICovXG5leHBvcnQgY2xhc3MgSW50ZXJhY3Rpb25EZWxlZ2F0ZSB7XG4gICAgLy8gTG9uZyBwcmVzczogaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xOTMwODk1L2hvdy1sb25nLWlzLXRoZS1ldmVudC1vbmxvbmdwcmVzcy1pbi10aGUtYW5kcm9pZFxuICAgIC8vIFN0eWx1cyBzdXBwb3J0OiBodHRwczovL3czYy5naXRodWIuaW8vdG91Y2gtZXZlbnRzL1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBJbnRlcmFjdGlvbkRlbGVnYXRlLlxuICAgICAqIEBwYXJhbSB7YW55fSBlbGVtZW50XG4gICAgICogQHBhcmFtIHthbnl9IHRhcmdldFxuICAgICAqIEBwYXJhbSB7YW55fSBbeyBtb3VzZVdoZWVsRWxlbWVudCA9IG51bGwsIHVzZUNhcHR1cmUgPSB0cnVlLCBjYXB0dXJlUG9pbnRlckV2ZW50cyA9IHRydWUsIGRlYnVnID0gZmFsc2UgfT17fV1cbiAgICAgKiBAbWVtYmVyb2YgSW50ZXJhY3Rpb25EZWxlZ2F0ZVxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBlbGVtZW50LFxuICAgICAgICB0YXJnZXQsXG4gICAgICAgIHsgbW91c2VXaGVlbEVsZW1lbnQgPSBudWxsLCB1c2VDYXB0dXJlID0gdHJ1ZSwgY2FwdHVyZVBvaW50ZXJFdmVudHMgPSB0cnVlLCBjYW5jZWxPbldpbmRvd091dCA9IHRydWUsIGRlYnVnID0gZmFsc2UgfSA9IHt9XG4gICAgKSB7XG4gICAgICAgIHRoaXMuZGVidWcgPSBkZWJ1Z1xuICAgICAgICB0aGlzLmludGVyYWN0aW9uID0gbmV3IEludGVyYWN0aW9uKClcbiAgICAgICAgdGhpcy5lbGVtZW50ID0gZWxlbWVudFxuICAgICAgICB0aGlzLm1vdXNlV2hlZWxFbGVtZW50ID0gbW91c2VXaGVlbEVsZW1lbnQgfHwgZWxlbWVudFxuICAgICAgICB0aGlzLnRhcmdldCA9IHRhcmdldFxuICAgICAgICB0aGlzLnVzZUNhcHR1cmUgPSB1c2VDYXB0dXJlXG4gICAgICAgIHRoaXMuY2FwdHVyZVBvaW50ZXJFdmVudHMgPSBjYXB0dXJlUG9pbnRlckV2ZW50c1xuICAgICAgICB0aGlzLmNhbmNlbE9uV2luZG93T3V0ID0gY2FuY2VsT25XaW5kb3dPdXRcbiAgICAgICAgdGhpcy5zZXR1cEludGVyYWN0aW9uKClcbiAgICB9XG5cbiAgICBzZXR1cEludGVyYWN0aW9uKCkge1xuICAgICAgICBpZiAodGhpcy5kZWJ1Zykge1xuICAgICAgICAgICAgbGV0IGVycm9yID0gdGhpcy50YXJnZXRJbnRlcmZhY2UuaW1wbGVtZW50YXRpb25FcnJvcihcbiAgICAgICAgICAgICAgICB0aGlzLnRhcmdldC5jb25zdHJ1Y3RvclxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgaWYgKGVycm9yICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0V4cGVjdGVkIElJbnRlcmFjdGlvblRhcmdldDogJyArIGVycm9yKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuc2V0dXBUb3VjaEludGVyYWN0aW9uKClcbiAgICAgICAgdGhpcy5zZXR1cE1vdXNlV2hlZWxJbnRlcmFjdGlvbigpXG4gICAgfVxuXG4gICAgZ2V0IHRhcmdldEludGVyZmFjZSgpIHtcbiAgICAgICAgcmV0dXJuIElJbnRlcmFjdGlvblRhcmdldFxuICAgIH1cblxuICAgIHNldHVwVG91Y2hJbnRlcmFjdGlvbigpIHtcbiAgICAgICAgbGV0IGVsZW1lbnQgPSB0aGlzLmVsZW1lbnRcbiAgICAgICAgbGV0IHVzZUNhcHR1cmUgPSB0aGlzLnVzZUNhcHR1cmVcbiAgICAgICAgaWYgKHdpbmRvdy5Qb2ludGVyRXZlbnQpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygnUG9pbnRlciBBUEknICsgd2luZG93LlBvaW50ZXJFdmVudClcbiAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAncG9pbnRlcmRvd24nLFxuICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ3BvaW50ZXJkb3duJywgZS5wb2ludGVySWQpXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmNhcHR1cmUoZSkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmNhcHR1cmVQb2ludGVyRXZlbnRzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxlbWVudC5zZXRQb2ludGVyQ2FwdHVyZShlLnBvaW50ZXJJZClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7IH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25TdGFydChlKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgJ3BvaW50ZXJtb3ZlJyxcbiAgICAgICAgICAgICAgICBlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKCdwb2ludGVybW92ZScsIGUucG9pbnRlcklkKVxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgICAgIGUucG9pbnRlclR5cGUgPT0gJ3RvdWNoJyB8fFxuICAgICAgICAgICAgICAgICAgICAgICAgKGUucG9pbnRlclR5cGUgPT0gJ21vdXNlJyAmJiBFdmVudHMuaXNNb3VzZURvd24oZSkpXG4gICAgICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gdGhpcy5jYXB0dXJlKGUpICYmXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZylcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygncG9pbnRlcm1vdmUgY2FwdHVyZWQnLCBlLnBvaW50ZXJJZClcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25Nb3ZlKGUpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHVzZUNhcHR1cmVcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAncG9pbnRlcnVwJyxcbiAgICAgICAgICAgICAgICBlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKCdwb2ludGVydXAnKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLm9uRW5kKGUpXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmNhcHR1cmVQb2ludGVyRXZlbnRzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsZW1lbnQucmVsZWFzZVBvaW50ZXJDYXB0dXJlKGUucG9pbnRlcklkKVxuICAgICAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkgeyB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHVzZUNhcHR1cmVcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAncG9pbnRlcmNhbmNlbCcsXG4gICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygncG9pbnRlcmNhbmNlbCcpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMub25FbmQoZSlcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuY2FwdHVyZVBvaW50ZXJFdmVudHMpXG4gICAgICAgICAgICAgICAgICAgICAgICBlbGVtZW50LnJlbGVhc2VQb2ludGVyQ2FwdHVyZShlLnBvaW50ZXJJZClcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHVzZUNhcHR1cmVcbiAgICAgICAgICAgIClcblxuICAgICAgICAgICAgaWYgKCF0aGlzLmNhcHR1cmVQb2ludGVyRXZlbnRzKSB7XG4gICAgICAgICAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgICAgICAgICAncG9pbnRlcmxlYXZlJyxcbiAgICAgICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ3BvaW50ZXJsZWF2ZScpXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoZS50YXJnZXQgPT0gZWxlbWVudCkgdGhpcy5vbkVuZChlKVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIXRoaXMuY2FwdHVyZVBvaW50ZXJFdmVudHMpIHtcbiAgICAgICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgICAgICdwb2ludGVyb3V0JyxcbiAgICAgICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ3BvaW50ZXJvdXQnKVxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGUudGFyZ2V0ID09IGVsZW1lbnQpIHRoaXMub25FbmQoZSlcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgdXNlQ2FwdHVyZVxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMuY2FuY2VsT25XaW5kb3dPdXQpIHtcbiAgICAgICAgICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAgICAgJ3BvaW50ZXJvdXQnLFxuICAgICAgICAgICAgICAgICAgICBlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlLnRhcmdldCA9PSBlbGVtZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vbkVuZChlKVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICB1c2VDYXB0dXJlKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgIH0gZWxzZSBpZiAod2luZG93LlRvdWNoRXZlbnQpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygnVG91Y2ggQVBJJylcbiAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAndG91Y2hzdGFydCcsXG4gICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKVxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3RvdWNoc3RhcnQnLCB0aGlzLnRvdWNoUG9pbnRzKGUpKVxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5jYXB0dXJlKGUpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCB0b3VjaCBvZiBlLmNoYW5nZWRUb3VjaGVzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vblN0YXJ0KHRvdWNoKVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgJ3RvdWNobW92ZScsXG4gICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKVxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ3RvdWNobW92ZScsIHRoaXMudG91Y2hQb2ludHMoZSksIGUpXG4gICAgICAgICAgICAgICAgICAgIGZvciAobGV0IHRvdWNoIG9mIGUuY2hhbmdlZFRvdWNoZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25Nb3ZlKHRvdWNoKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGZvciAobGV0IHRvdWNoIG9mIGUudGFyZ2V0VG91Y2hlcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vbk1vdmUodG91Y2gpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHVzZUNhcHR1cmVcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAndG91Y2hlbmQnLFxuICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ3RvdWNoZW5kJywgdGhpcy50b3VjaFBvaW50cyhlKSlcbiAgICAgICAgICAgICAgICAgICAgZm9yIChsZXQgdG91Y2ggb2YgZS5jaGFuZ2VkVG91Y2hlcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vbkVuZCh0b3VjaClcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgdXNlQ2FwdHVyZVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgICAgICd0b3VjaGNhbmNlbCcsXG4gICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKVxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3RvdWNoY2FuY2VsJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlLnRhcmdldFRvdWNoZXMubGVuZ3RoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGUuY2hhbmdlZFRvdWNoZXMubGVuZ3RoXG4gICAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgIGZvciAobGV0IHRvdWNoIG9mIGUuY2hhbmdlZFRvdWNoZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25FbmQodG91Y2gpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHVzZUNhcHR1cmVcbiAgICAgICAgICAgIClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygnTW91c2UgQVBJJylcblxuICAgICAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgICAgICdtb3VzZWRvd24nLFxuICAgICAgICAgICAgICAgIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ21vdXNlZG93bicsIGUpXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmNhcHR1cmUoZSkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25TdGFydChlKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgJ21vdXNlbW92ZScsXG4gICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIC8vIERvdyB3ZSBvbmx5IHVzZSBtb3ZlIGV2ZW50cyBpZiB0aGUgbW91c2UgaXMgZG93bj9cbiAgICAgICAgICAgICAgICAgICAgLy8gSE92ZXIgZWZmZWN0cyBoYXZlIHRvIGJlIGltcGxlbWVudGVkIGJ5IG90aGVyIG1lYW5zXG4gICAgICAgICAgICAgICAgICAgIC8vICYmIEV2ZW50cy5pc01vdXNlRG93bihlKSlcblxuICAgICAgICAgICAgICAgICAgICBpZiAoRXZlbnRzLmlzTW91c2VEb3duKGUpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZylcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnbW91c2Vtb3ZlJywgZSlcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25Nb3ZlKGUpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHVzZUNhcHR1cmVcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAnbW91c2V1cCcsXG4gICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygnbW91c2V1cCcsIGUpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMub25FbmQoZSlcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHRydWVcbiAgICAgICAgICAgIClcblxuICAgICAgICAgICAgaWYgKCF0aGlzLmNhcHR1cmVQb2ludGVyRXZlbnRzKSB7XG4gICAgICAgICAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgICAgICAgICAnbW91c2VvdXQnLFxuICAgICAgICAgICAgICAgICAgICBlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlLnRhcmdldCA9PSBlbGVtZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vbkVuZChlKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihcIlNob3VsZG4ndCBoYXBwZW46IG1vdXNlb3V0IGVuZHMgaW50ZXJhY3Rpb25cIilcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICB1c2VDYXB0dXJlXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHRoaXMuY2FuY2VsT25XaW5kb3dPdXQpIHtcbiAgICAgICAgICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICAgICAgJ21vdXNlb3V0JyxcbiAgICAgICAgICAgICAgICAgICAgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoZS50YXJnZXQgPT0gZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25FbmQoZSlcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgdXNlQ2FwdHVyZSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGlzRGVzY2VuZGFudChwYXJlbnQsIGNoaWxkKSB7XG4gICAgICAgIGlmIChwYXJlbnQgPT0gY2hpbGQpIHJldHVybiB0cnVlXG4gICAgICAgIGxldCBub2RlID0gY2hpbGQucGFyZW50Tm9kZVxuICAgICAgICB3aGlsZSAobm9kZSAhPSBudWxsKSB7XG4gICAgICAgICAgICBpZiAobm9kZSA9PSBwYXJlbnQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbm9kZSA9IG5vZGUucGFyZW50Tm9kZVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIHRvdWNoUG9pbnRzKGV2ZW50KSB7XG4gICAgICAgIGxldCByZXN1bHQgPSBbXVxuICAgICAgICBmb3IgKGxldCB0b3VjaCBvZiBldmVudC5jaGFuZ2VkVG91Y2hlcykge1xuICAgICAgICAgICAgcmVzdWx0LnB1c2godGhpcy5leHRyYWN0UG9pbnQodG91Y2gpKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG5cbiAgICBzZXR1cE1vdXNlV2hlZWxJbnRlcmFjdGlvbigpIHtcbiAgICAgICAgdGhpcy5tb3VzZVdoZWVsRWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAgICAgJ21vdXNld2hlZWwnLFxuICAgICAgICAgICAgdGhpcy5vbk1vdXNlV2hlZWwuYmluZCh0aGlzKSxcbiAgICAgICAgICAgIHRydWVcbiAgICAgICAgKVxuICAgICAgICB0aGlzLm1vdXNlV2hlZWxFbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAnRE9NTW91c2VTY3JvbGwnLFxuICAgICAgICAgICAgdGhpcy5vbk1vdXNlV2hlZWwuYmluZCh0aGlzKSxcbiAgICAgICAgICAgIHRydWVcbiAgICAgICAgKVxuICAgIH1cblxuICAgIG9uTW91c2VXaGVlbChldmVudCkge1xuICAgICAgICBpZiAodGhpcy5jYXB0dXJlKGV2ZW50KSAmJiB0aGlzLnRhcmdldC5vbk1vdXNlV2hlZWwpIHtcbiAgICAgICAgICAgIHRoaXMudGFyZ2V0Lm9uTW91c2VXaGVlbChldmVudClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vY29uc29sZS53YXJuKCdUYXJnZXQgaGFzIG5vIG9uTW91c2VXaGVlbCBjYWxsYmFjaycpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvblN0YXJ0KGV2ZW50KSB7XG4gICAgICAgIGxldCBleHRyYWN0ZWQgPSB0aGlzLmV4dHJhY3RQb2ludChldmVudClcbiAgICAgICAgdGhpcy5zdGFydEludGVyYWN0aW9uKGV2ZW50LCBleHRyYWN0ZWQpXG4gICAgICAgIHRoaXMudGFyZ2V0Lm9uU3RhcnQoZXZlbnQsIHRoaXMuaW50ZXJhY3Rpb24pXG4gICAgfVxuXG4gICAgb25Nb3ZlKGV2ZW50KSB7XG4gICAgICAgIGxldCBleHRyYWN0ZWQgPSB0aGlzLmV4dHJhY3RQb2ludChldmVudCwgJ2FsbCcpXG4gICAgICAgIHRoaXMudXBkYXRlSW50ZXJhY3Rpb24oZXZlbnQsIGV4dHJhY3RlZClcbiAgICAgICAgdGhpcy50YXJnZXQub25Nb3ZlKGV2ZW50LCB0aGlzLmludGVyYWN0aW9uKVxuICAgICAgICB0aGlzLmludGVyYWN0aW9uLnVwZGF0ZVByZXZpb3VzKClcbiAgICB9XG5cbiAgICBvbkVuZChldmVudCkge1xuICAgICAgICBsZXQgZXh0cmFjdGVkID0gdGhpcy5leHRyYWN0UG9pbnQoZXZlbnQsICdjaGFuZ2VkVG91Y2hlcycpXG4gICAgICAgIHRoaXMuZW5kSW50ZXJhY3Rpb24oZXZlbnQsIGV4dHJhY3RlZClcbiAgICAgICAgdGhpcy50YXJnZXQub25FbmQoZXZlbnQsIHRoaXMuaW50ZXJhY3Rpb24pXG4gICAgICAgIHRoaXMuZmluaXNoSW50ZXJhY3Rpb24oZXZlbnQsIGV4dHJhY3RlZClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBc2tzIHRoZSB0YXJnZXQgd2hldGhlciB0aGUgZXZlbnQgc2hvdWxkIGJlIGNhcHR1cmVkXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge2FueX0gZXZlbnRcbiAgICAgKiBAcmV0dXJucyB7Ym9vbH1cbiAgICAgKiBAbWVtYmVyb2YgSW50ZXJhY3Rpb25EZWxlZ2F0ZVxuICAgICAqL1xuICAgIGNhcHR1cmUoZXZlbnQpIHtcbiAgICAgICAgaWYgKEV2ZW50cy5pc0NhcHR1cmVkKGV2ZW50KSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgIH1cbiAgICAgICAgbGV0IGNhcHR1cmVkID0gdGhpcy50YXJnZXQuY2FwdHVyZShldmVudClcbiAgICAgICAgcmV0dXJuIGNhcHR1cmVkXG4gICAgfVxuXG4gICAgZ2V0UG9zaXRpb24oZXZlbnQpIHtcbiAgICAgICAgcmV0dXJuIHsgeDogZXZlbnQuY2xpZW50WCwgeTogZXZlbnQuY2xpZW50WSB9XG4gICAgfVxuXG4gICAgZXh0cmFjdFBvaW50KGV2ZW50LCB0b3VjaEV2ZW50S2V5ID0gJ2FsbCcpIHtcbiAgICAgICAgLy8gJ3RhcmdldFRvdWNoZXMnXG4gICAgICAgIGxldCByZXN1bHQgPSB7fVxuICAgICAgICBzd2l0Y2ggKGV2ZW50LmNvbnN0cnVjdG9yLm5hbWUpIHtcbiAgICAgICAgICAgIGNhc2UgJ01vdXNlRXZlbnQnOlxuICAgICAgICAgICAgICAgIGxldCBidXR0b25zID0gZXZlbnQuYnV0dG9ucyB8fCBldmVudC53aGljaFxuICAgICAgICAgICAgICAgIGlmIChidXR0b25zKSByZXN1bHRbJ21vdXNlJ10gPSB0aGlzLmdldFBvc2l0aW9uKGV2ZW50KVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdQb2ludGVyRXZlbnQnOlxuICAgICAgICAgICAgICAgIHJlc3VsdFtldmVudC5wb2ludGVySWQudG9TdHJpbmcoKV0gPSB0aGlzLmdldFBvc2l0aW9uKGV2ZW50KVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdUb3VjaCc6XG4gICAgICAgICAgICAgICAgbGV0IGlkID1cbiAgICAgICAgICAgICAgICAgICAgZXZlbnQudG91Y2hUeXBlID09PSAnc3R5bHVzJ1xuICAgICAgICAgICAgICAgICAgICAgICAgPyAnc3R5bHVzJ1xuICAgICAgICAgICAgICAgICAgICAgICAgOiBldmVudC5pZGVudGlmaWVyLnRvU3RyaW5nKClcbiAgICAgICAgICAgICAgICByZXN1bHRbaWRdID0gdGhpcy5nZXRQb3NpdGlvbihldmVudClcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgLy8gICAgICAgICAgICAgY2FzZSAnVG91Y2hFdmVudCc6XG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgLy8gTmVlZHMgdG8gYmUgb2JzZXJ2ZWQ6IFBlcmhhcHMgY2hhbmdlZFRvdWNoZXMgYXJlIGFsbCB3ZSBuZWVkLiBJZiBzb1xuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgIC8vIHdlIGNhbiByZW1vdmUgdGhlIHRvdWNoRXZlbnRLZXkgZGVmYXVsdCBwYXJhbWV0ZXJcbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICBpZiAodG91Y2hFdmVudEtleSA9PSAnYWxsJykge1xuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgICAgICBmb3IobGV0IHQgb2YgZXZlbnQudGFyZ2V0VG91Y2hlcykge1xuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0W3QuaWRlbnRpZmllci50b1N0cmluZygpXSA9IHRoaXMuZ2V0UG9zaXRpb24odClcbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgICAgICBmb3IobGV0IHQgb2YgZXZlbnQuY2hhbmdlZFRvdWNoZXMpIHtcbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdFt0LmlkZW50aWZpZXIudG9TdHJpbmcoKV0gPSB0aGlzLmdldFBvc2l0aW9uKHQpXG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyAgICAgICAgICAgICAgICAgICAgIGZvcihsZXQgdCBvZiBldmVudC5jaGFuZ2VkVG91Y2hlcykge1xuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0W3QuaWRlbnRpZmllci50b1N0cmluZygpXSA9IHRoaXMuZ2V0UG9zaXRpb24odClcbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG5cbiAgICBpbnRlcmFjdGlvblN0YXJ0ZWQoZXZlbnQsIGtleSwgcG9pbnQpIHtcbiAgICAgICAgLy8gQ2FsbGJhY2s6IGNhbiBiZSBvdmVyd3JpdHRlblxuICAgIH1cblxuICAgIGludGVyYWN0aW9uRW5kZWQoZXZlbnQsIGtleSwgcG9pbnQpIHtcbiAgICAgICAgLy8gQ2FsbGJhY2s6IGNhbiBiZSBvdmVyd3JpdHRlblxuICAgIH1cblxuICAgIGludGVyYWN0aW9uRmluaXNoZWQoZXZlbnQsIGtleSwgcG9pbnQpIHsgfVxuXG4gICAgc3RhcnRJbnRlcmFjdGlvbihldmVudCwgZXh0cmFjdGVkKSB7XG4gICAgICAgIGZvciAobGV0IGtleSBpbiBleHRyYWN0ZWQpIHtcbiAgICAgICAgICAgIGxldCBwb2ludCA9IGV4dHJhY3RlZFtrZXldXG4gICAgICAgICAgICB0aGlzLmludGVyYWN0aW9uLnN0YXJ0ZWQoa2V5LCBwb2ludClcbiAgICAgICAgICAgIHRoaXMuaW50ZXJhY3Rpb25TdGFydGVkKGV2ZW50LCBrZXksIHBvaW50KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgdXBkYXRlSW50ZXJhY3Rpb24oZXZlbnQsIGV4dHJhY3RlZCkge1xuICAgICAgICBmb3IgKGxldCBrZXkgaW4gZXh0cmFjdGVkKSB7XG4gICAgICAgICAgICBsZXQgcG9pbnQgPSBleHRyYWN0ZWRba2V5XVxuICAgICAgICAgICAgbGV0IHVwZGF0ZWQgPSB0aGlzLmludGVyYWN0aW9uLnVwZGF0ZShrZXksIHBvaW50KVxuICAgICAgICAgICAgaWYgKHVwZGF0ZWQpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJuZXcgcG9pbnRlciBpbiB1cGRhdGVJbnRlcmFjdGlvbiBzaG91bGRuJ3QgaGFwcGVuXCIpXG4gICAgICAgICAgICAgICAgdGhpcy5pbnRlcmFjdGlvblN0YXJ0ZWQoZXZlbnQsIGtleSwgcG9pbnQpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBlbmRJbnRlcmFjdGlvbihldmVudCwgZW5kZWQpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IGluIGVuZGVkKSB7XG4gICAgICAgICAgICBsZXQgcG9pbnQgPSBlbmRlZFtrZXldXG4gICAgICAgICAgICB0aGlzLmludGVyYWN0aW9uLnN0b3Aoa2V5LCBwb2ludClcbiAgICAgICAgICAgIHRoaXMuaW50ZXJhY3Rpb25FbmRlZChldmVudCwga2V5LCBwb2ludClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZpbmlzaEludGVyYWN0aW9uKGV2ZW50LCBlbmRlZCkge1xuICAgICAgICBmb3IgKGxldCBrZXkgaW4gZW5kZWQpIHtcbiAgICAgICAgICAgIGxldCBwb2ludCA9IGVuZGVkW2tleV1cbiAgICAgICAgICAgIHRoaXMuaW50ZXJhY3Rpb24uZmluaXNoKGtleSwgcG9pbnQpXG4gICAgICAgICAgICB0aGlzLmludGVyYWN0aW9uRmluaXNoZWQoZXZlbnQsIGtleSwgcG9pbnQpXG4gICAgICAgIH1cbiAgICB9XG59XG4vKipcbiAqIEEgc3BlY2lhbCBJbnRlcmFjdGlvbkRlbGVnYXRlIHRoYXQgbWFwcyBldmVudHMgdG8gc3BlY2lmaWMgcGFydHMgb2ZcbiAqIHRoZSBpbnRlcmFjdGlvbiB0YXJnZXQuIFRoZSBJbnRlcmFjdGlvblRhcmdldCBtdXN0IGltcGxlbWVudCBhIGZpbmRUYXJnZXRcbiAqIG1ldGhvZCB0aGF0IHJldHVybnMgYW4gb2JqZWN0IGltcGxlbWVudGluZyB0aGUgSUludGVyYWN0aW9uVGFyZ2V0IGludGVyZmFjZS5cbiAqXG4gKiBJZiB0aGUgSW50ZXJhY3Rpb25UYXJnZXQgYWxzbyBpbXBsZW1lbnRzIGEgbWFwUG9zaXRpb25Ub1BvaW50IG1ldGhvZCB0aGlzXG4gKiBpcyB1c2VkIHRvIG1hcCB0aGUgcG9pbnRzIHRvIHRoZSBsb2NhbCBjb29yZGluYXRlIHNwYWNlIG9mIHRoZSB0aGUgdGFyZ2V0LlxuICpcbiAqIFRoaXMgbWFrZXMgaXQgZWFzaWVyIHRvIGxvb2t1cCBlbGVtZW50cyBhbmQgcmVsYXRlIGV2ZW50cyB0byBsb2NhbFxuICogcG9zaXRpb25zLlxuICpcbiAqIEBleHBvcnRcbiAqIEBjbGFzcyBJbnRlcmFjdGlvbk1hcHBlclxuICogQGV4dGVuZHMge0ludGVyYWN0aW9uRGVsZWdhdGV9XG4gKi9cbmV4cG9ydCBjbGFzcyBJbnRlcmFjdGlvbk1hcHBlciBleHRlbmRzIEludGVyYWN0aW9uRGVsZWdhdGUge1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIGVsZW1lbnQsXG4gICAgICAgIHRhcmdldCxcbiAgICAgICAgeyB0YXBEaXN0YW5jZSA9IDEwLCBsb25nUHJlc3NUaW1lID0gNTAwLjAsIHVzZUNhcHR1cmUgPSB0cnVlLCBtb3VzZVdoZWVsRWxlbWVudCA9IG51bGwgfSA9IHt9XG4gICAgKSB7XG4gICAgICAgIHN1cGVyKGVsZW1lbnQsIHRhcmdldCwgeyB0YXBEaXN0YW5jZSwgdXNlQ2FwdHVyZSwgbG9uZ1ByZXNzVGltZSwgbW91c2VXaGVlbEVsZW1lbnQgfSlcbiAgICB9XG5cbiAgICBnZXQgdGFyZ2V0SW50ZXJmYWNlKCkge1xuICAgICAgICByZXR1cm4gSUludGVyYWN0aW9uTWFwcGVyVGFyZ2V0XG4gICAgfVxuXG4gICAgbWFwUG9zaXRpb25Ub1BvaW50KHBvaW50LCBlbGVtZW50ID0gbnVsbCkge1xuICAgICAgICBpZiAodGhpcy50YXJnZXQubWFwUG9zaXRpb25Ub1BvaW50KSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy50YXJnZXQubWFwUG9zaXRpb25Ub1BvaW50KHBvaW50LCBlbGVtZW50KVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBwb2ludFxuICAgIH1cblxuICAgIGludGVyYWN0aW9uU3RhcnRlZChldmVudCwga2V5LCBwb2ludCkge1xuICAgICAgICBpZiAodGhpcy50YXJnZXQuZmluZFRhcmdldCkge1xuICAgICAgICAgICAgbGV0IGxvY2FsID0gdGhpcy5tYXBQb3NpdGlvblRvUG9pbnQocG9pbnQpXG4gICAgICAgICAgICBsZXQgZm91bmQgPSB0aGlzLnRhcmdldC5maW5kVGFyZ2V0KGV2ZW50LCBsb2NhbCwgcG9pbnQpXG4gICAgICAgICAgICBpZiAoZm91bmQgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRoaXMuaW50ZXJhY3Rpb24uYWRkVGFyZ2V0KGtleSwgZm91bmQpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvbk1vdXNlV2hlZWwoZXZlbnQpIHtcbiAgICAgICAgaWYgKHRoaXMuY2FwdHVyZShldmVudCkpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnRhcmdldC5maW5kVGFyZ2V0KSB7XG4gICAgICAgICAgICAgICAgbGV0IHBvaW50ID0gdGhpcy5nZXRQb3NpdGlvbihldmVudClcbiAgICAgICAgICAgICAgICBsZXQgbG9jYWwgPSB0aGlzLm1hcFBvc2l0aW9uVG9Qb2ludChwb2ludClcbiAgICAgICAgICAgICAgICBsZXQgZm91bmQgPSB0aGlzLnRhcmdldC5maW5kVGFyZ2V0KGV2ZW50LCBsb2NhbCwgcG9pbnQpXG4gICAgICAgICAgICAgICAgaWYgKGZvdW5kICE9IG51bGwgJiYgZm91bmQub25Nb3VzZVdoZWVsKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvdW5kLm9uTW91c2VXaGVlbChldmVudClcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHRoaXMudGFyZ2V0Lm9uTW91c2VXaGVlbCkge1xuICAgICAgICAgICAgICAgIHRoaXMudGFyZ2V0Lm9uTW91c2VXaGVlbChldmVudClcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy9jb25zb2xlLndhcm4oJ1RhcmdldCBoYXMgbm8gb25Nb3VzZVdoZWVsIGNhbGxiYWNrJywgdGhpcy50YXJnZXQpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvblN0YXJ0KGV2ZW50KSB7XG4gICAgICAgIGxldCBleHRyYWN0ZWQgPSB0aGlzLmV4dHJhY3RQb2ludChldmVudClcbiAgICAgICAgdGhpcy5zdGFydEludGVyYWN0aW9uKGV2ZW50LCBleHRyYWN0ZWQpXG4gICAgICAgIGxldCBtYXBwZWQgPSB0aGlzLmludGVyYWN0aW9uLm1hcEludGVyYWN0aW9uKFxuICAgICAgICAgICAgZXh0cmFjdGVkLFxuICAgICAgICAgICAgWydjdXJyZW50JywgJ3N0YXJ0J10sXG4gICAgICAgICAgICB0aGlzLm1hcFBvc2l0aW9uVG9Qb2ludC5iaW5kKHRoaXMpXG4gICAgICAgIClcbiAgICAgICAgZm9yIChsZXQgW3RhcmdldCwgaW50ZXJhY3Rpb25dIG9mIG1hcHBlZC5lbnRyaWVzKCkpIHtcbiAgICAgICAgICAgIHRhcmdldC5vblN0YXJ0KGV2ZW50LCBpbnRlcmFjdGlvbilcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uTW92ZShldmVudCkge1xuICAgICAgICBsZXQgZXh0cmFjdGVkID0gdGhpcy5leHRyYWN0UG9pbnQoZXZlbnQsICdhbGwnKVxuICAgICAgICB0aGlzLnVwZGF0ZUludGVyYWN0aW9uKGV2ZW50LCBleHRyYWN0ZWQpXG4gICAgICAgIGxldCBtYXBwZWQgPSB0aGlzLmludGVyYWN0aW9uLm1hcEludGVyYWN0aW9uKFxuICAgICAgICAgICAgZXh0cmFjdGVkLFxuICAgICAgICAgICAgWydjdXJyZW50JywgJ3ByZXZpb3VzJ10sXG4gICAgICAgICAgICB0aGlzLm1hcFBvc2l0aW9uVG9Qb2ludC5iaW5kKHRoaXMpXG4gICAgICAgIClcbiAgICAgICAgZm9yIChsZXQgW3RhcmdldCwgaW50ZXJhY3Rpb25dIG9mIG1hcHBlZC5lbnRyaWVzKCkpIHtcbiAgICAgICAgICAgIHRhcmdldC5vbk1vdmUoZXZlbnQsIGludGVyYWN0aW9uKVxuICAgICAgICAgICAgaW50ZXJhY3Rpb24udXBkYXRlUHJldmlvdXMoKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuaW50ZXJhY3Rpb24udXBkYXRlUHJldmlvdXMoKVxuICAgIH1cblxuICAgIG9uRW5kKGV2ZW50KSB7XG4gICAgICAgIGxldCBleHRyYWN0ZWQgPSB0aGlzLmV4dHJhY3RQb2ludChldmVudCwgJ2NoYW5nZWRUb3VjaGVzJylcbiAgICAgICAgdGhpcy5lbmRJbnRlcmFjdGlvbihldmVudCwgZXh0cmFjdGVkKVxuICAgICAgICBsZXQgbWFwcGVkID0gdGhpcy5pbnRlcmFjdGlvbi5tYXBJbnRlcmFjdGlvbihcbiAgICAgICAgICAgIGV4dHJhY3RlZCxcbiAgICAgICAgICAgIFsnZW5kZWQnXSxcbiAgICAgICAgICAgIHRoaXMubWFwUG9zaXRpb25Ub1BvaW50LmJpbmQodGhpcylcbiAgICAgICAgKVxuICAgICAgICBmb3IgKGxldCBbdGFyZ2V0LCBpbnRlcmFjdGlvbl0gb2YgbWFwcGVkLmVudHJpZXMoKSkge1xuICAgICAgICAgICAgdGFyZ2V0Lm9uRW5kKGV2ZW50LCBpbnRlcmFjdGlvbilcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmZpbmlzaEludGVyYWN0aW9uKGV2ZW50LCBleHRyYWN0ZWQpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICpcbiAgICAgKlxuICAgICAqIEBzdGF0aWNcbiAgICAgKiBAcGFyYW0ge3N0cmluZ3xhcnJheX0gdHlwZXMgLSBBbiBldmVudCB0eXBlLCBhbiBhcnJheSBvZiBldmVudCB0eXBlcyBvciBldmVudCB0eXBlcyBzZXBlcmF0ZWQgYnkgYSBzcGFjZSBzaWduLiBUaGUgZm9sbG93aW5nXG4gICAgICogICAgIGV2ZW50cyBhcmUgcG9zc2libGU6XG4gICAgICogICAgICAgICBwYW4sIHBhbnN0YXJ0LCBwYW5tb3ZlLCBwYW5lbmQsIHBhbmNhbmNlbCwgcGFubGVmdCwgcGFucmlnaHQsIHBhbnVwLCBwYW5kb3duXG4gICAgICogICAgICAgICBwaW5jaCwgcGluY2hzdGFydCwgcGluY2htb3ZlLCBwaW5jaGVuZCwgcGluY2hjYW5jZWwsIHBpbmNoaW4sIHBpbmNob3V0XG4gICAgICogICAgICAgICBwcmVzcywgcHJlc3N1cFxuICAgICAqICAgICAgICAgcm90YXRlLCByb3RhdGVzdGFydCwgcm90YXRlbW92ZSwgcm90YXRlZW5kLCByb3RhdGVjYW5jZWxcbiAgICAgKiAgICAgICAgIHN3aXBlLCBzd2lwZWxlZnQsIHN3aXBlcmlnaHQsIHN3aXBldXAsIHN3aXBlZG93blxuICAgICAqICAgICAgICAgdGFwXG4gICAgICogQHBhcmFtIHtIVE1MRWxlbWVudHxIVE1MRWxlbWVudFtdfSBlbGVtZW50cyAtIEFuIEhUTUwgZWxlbWVudCBvciBhbiBhcnJheSBvZiBIVE1MIGVsZW1lbnRzLlxuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtjYl0gLSBUaGUgY2FsbGJhY2suIEEgZnVuY3Rpb24gd2hpY2ggaXMgZXhlY3V0ZWQgYWZ0ZXIgdGhlIGV2ZW50IG9jY3Vycy4gUmVjZWl2ZXMgdGhlIGV2ZW50IG9iamVjdCBhcyB0aGVcbiAgICAgKiAgICAgZmlyc3QgcGFyYW10ZXJcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QuIFNlZSB0aGUgaGFtbWVyIGRvY3VtZW50YXRpb24gZm9yIG1vcmUgZGV0YWlscy5cbiAgICAgKi9cbiAgICBzdGF0aWMgb24odHlwZXMsIGVsZW1lbnRzLCBjYiwgb3B0cyA9IHt9KSB7XG4gICAgICAgIG9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG5cbiAgICAgICAgfSwgb3B0cylcblxuICAgICAgICBpZiAodHlwZW9mIEhhbW1lciA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0hhbW1lci5qcyBub3QgZm91bmQhJylcbiAgICAgICAgICAgIHJldHVybiB0aGlzXG4gICAgICAgIH1cblxuICAgICAgICAvLyBjb252ZXJ0IHRvIGFycmF5XG4gICAgICAgIHR5cGVzID0gQXJyYXkuaXNBcnJheSh0eXBlcykgPyB0eXBlcyA6IHR5cGVzLnNwbGl0KC9cXHMvKVxuICAgICAgICBpZiAoZWxlbWVudHMgaW5zdGFuY2VvZiBOb2RlTGlzdCB8fCBlbGVtZW50cyBpbnN0YW5jZW9mIEhUTUxDb2xsZWN0aW9uKSB7XG4gICAgICAgICAgICBlbGVtZW50cyA9IEFycmF5LmZyb20oZWxlbWVudHMpXG4gICAgICAgIH1cbiAgICAgICAgZWxlbWVudHMgPSBBcnJheS5pc0FycmF5KGVsZW1lbnRzKSA/IGVsZW1lbnRzIDogW2VsZW1lbnRzXVxuXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdHlwZXMubGVuZ3RoOyBpKyspIHtcblxuICAgICAgICAgICAgY29uc3QgdHlwZSA9IHR5cGVzW2ldLnRvTG93ZXJDYXNlKClcblxuICAgICAgICAgICAgLy8gbGlzdCBvZiBoYW1tZXIgZXZlbnRzXG4gICAgICAgICAgICBjb25zdCB1c2VIYW1tZXIgPSAvXih0YXB8ZG91YmxldGFwfHByZXNzfHBhbnxzd2lwZXxwaW5jaHxyb3RhdGUpLiokLy50ZXN0KHR5cGUpXG5cbiAgICAgICAgICAgIC8vIGlmIGl0IGlzIGEgaGFtbWVyIGV2ZW50XG4gICAgICAgICAgICBpZiAodXNlSGFtbWVyKSB7XG5cbiAgICAgICAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IGVsZW1lbnRzLmxlbmd0aDsgaisrKSB7XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gaWYoZWxlbWVudHNbal0udGFnTmFtZSA9PSBcInN2Z1wiKSByZXR1cm4gZmFsc2U7XG5cbiAgICAgICAgICAgICAgICAgICAgbGV0IGhhbW1lciA9IG5ldyBIYW1tZXIoZWxlbWVudHNbal0sIG9wdHMpXG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHdpbmRvdy5wcm9wYWdhdGluZyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGhhbW1lciA9IHByb3BhZ2F0aW5nKGhhbW1lcilcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIHJlY29nbml6ZXJzXG4gICAgICAgICAgICAgICAgICAgIGlmICh0eXBlLnN0YXJ0c1dpdGgoJ3BhbicpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBoYW1tZXIuZ2V0KCdwYW4nKS5zZXQoT2JqZWN0LmFzc2lnbih7IGRpcmVjdGlvbjogSGFtbWVyLkRJUkVDVElPTl9BTEwgfSwgb3B0cykpXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodHlwZS5zdGFydHNXaXRoKCdwaW5jaCcpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBoYW1tZXIuZ2V0KCdwaW5jaCcpLnNldChPYmplY3QuYXNzaWduKHsgZW5hYmxlOiB0cnVlIH0sIG9wdHMpKVxuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHR5cGUuc3RhcnRzV2l0aCgncHJlc3MnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaGFtbWVyLmdldCgncHJlc3MnKS5zZXQob3B0cylcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlLnN0YXJ0c1dpdGgoJ3JvdGF0ZScpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBoYW1tZXIuZ2V0KCdyb3RhdGUnKS5zZXQoT2JqZWN0LmFzc2lnbih7IGVuYWJsZTogdHJ1ZSB9LCBvcHRzKSlcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlLnN0YXJ0c1dpdGgoJ3N3aXBlJykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGhhbW1lci5nZXQoJ3N3aXBlJykuc2V0KE9iamVjdC5hc3NpZ24oeyBkaXJlY3Rpb246IEhhbW1lci5ESVJFQ1RJT05fQUxMIH0sIG9wdHMpKVxuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHR5cGUuc3RhcnRzV2l0aCgndGFwJykpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGhhbW1lci5nZXQoJ3RhcCcpLnNldChvcHRzKVxuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgaGFtbWVyLm9uKHR5cGUsIGV2ZW50ID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNiKGV2ZW50KVxuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgZWxlbWVudHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgSGFtbWVyLm9uKGVsZW1lbnRzW2pdLCB0eXBlLCBldmVudCA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYihldmVudClcbiAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbn1cblxud2luZG93LkludGVyYWN0aW9uTWFwcGVyID0gSW50ZXJhY3Rpb25NYXBwZXJcbiIsIi8qKiBSZXBvcnQgY2FwYWJpbGl0aWVzIHdpdGggZ3VhcmFudGVlZCB2YWx1ZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBDYXBhYmlsaXRpZXMge1xuXG4gICAgLyoqIFJldHVybnMgdGhlIGJyb3dzZXIgdXNlckFnZW50LlxuICAgIEByZXR1cm4ge3N0cmluZ31cbiAgICAqL1xuICAgIHN0YXRpYyBnZXQgdXNlckFnZW50KCkge1xuICAgICAgICByZXR1cm4gbmF2aWdhdG9yLnVzZXJBZ2VudCB8fCAnVW5rbm93biBBZ2VudCdcbiAgICB9XG5cbiAgICAvKiogVGVzdHMgd2hldGhlciB0aGUgYXBwIGlzIHJ1bm5pbmcgb24gYSBtb2JpbGUgZGV2aWNlLlxuICAgIEltcGxlbWVudGVkIGFzIGEgcmVhZG9ubHkgYXR0cmlidXRlLlxuICAgIEByZXR1cm4ge2Jvb2xlYW59XG4gICAgKi9cbiAgICBzdGF0aWMgZ2V0IGlzTW9iaWxlKCkge1xuICAgICAgICByZXR1cm4gKC9Nb2JpLy50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpKVxuICAgIH1cblxuICAgIC8qKiBUZXN0cyB3aGV0aGVyIHRoZSBhcHAgaXMgcnVubmluZyBvbiBhIGlPUyBkZXZpY2UuXG4gICAgSW1wbGVtZW50ZWQgYXMgYSByZWFkb25seSBhdHRyaWJ1dGUuXG4gICAgQHJldHVybiB7Ym9vbGVhbn1cbiAgICAqL1xuICAgIHN0YXRpYyBnZXQgaXNJT1MoKSB7XG4gICAgICAgIHJldHVybiAoL2lQYWR8aVBob25lfGlQb2QvLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCkpICYmICF3aW5kb3cuTVNTdHJlYW1cbiAgICB9XG5cbiAgICAvKiogVGVzdHMgd2hldGhlciB0aGUgYXBwIGlzIHJ1bm5pbmcgaW4gYSBTYWZhcmkgZW52aXJvbm1lbnQuXG4gICAgU2VlIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzc5NDQ0NjAvZGV0ZWN0LXNhZmFyaS1icm93c2VyXG4gICAgSW1wbGVtZW50ZWQgYXMgYSByZWFkb25seSBhdHRyaWJ1dGUuXG4gICAgQHJldHVybiB7Ym9vbGVhbn1cbiAgICAqL1xuICAgIHN0YXRpYyBnZXQgaXNTYWZhcmkoKSB7XG4gICAgICAgIHJldHVybiBuYXZpZ2F0b3IudmVuZG9yICYmIG5hdmlnYXRvci52ZW5kb3IuaW5kZXhPZignQXBwbGUnKSA+IC0xICYmIG5hdmlnYXRvci51c2VyQWdlbnQgJiYgIW5hdmlnYXRvci51c2VyQWdlbnQubWF0Y2goJ0NyaU9TJylcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEaXN0aW5jdHMgaWYgdGhlIGFwcCBpcyBydW5uaW5nIGluc2lkZSBlbGVjdHJvbiBvciBub3QuXG4gICAgICogXG4gICAgICogc291cmNlOiBodHRwczovL2Rpc2N1c3MuYXRvbS5pby90L2RldGVjdC1lbGVjdHJvbi1vci13ZWItcGFnZS1ydW5uaW5nLzMzMTgwLzNcbiAgICAgKi9cbiAgICBzdGF0aWMgZ2V0IGlzRWxlY3Ryb24oKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2YgcHJvY2VzcyAhPSAndW5kZWZpbmVkJyAmJiBwcm9jZXNzLnZlcnNpb25zICYmIHByb2Nlc3MudmVyc2lvbnMuZWxlY3Ryb24gIT09IHVuZGVmaW5lZFxuICAgIH1cblxuICAgIC8qKiBSZXR1cm5zIHRoZSBkaXNwbGF5IHJlc29sdXRpb24uIE5lY2Vzc2FyeSBmb3IgcmV0aW5hIGRpc3BsYXlzLlxuICAgIEByZXR1cm4ge251bWJlcn1cbiAgICAqL1xuICAgIHN0YXRpYyBnZXQgZGV2aWNlUGl4ZWxSYXRpbygpIHtcbiAgICAgICAgcmV0dXJuIHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvIHx8IDFcbiAgICB9XG5cbiAgICAvKiogUmV0dXJucyB0cnVlIGlmIHRoZSBkZXZpY2UgaXMgYSBtdWx0aS10b3VjaCB0YWJsZS4gVGhpcyBtZXRob2QgaXMgY3VycmVudGx5IG5vdCB1bml2ZXJzYWwgdXNhYmxlIGFuZCBub3Qgc3VyZSFcbiAgICBAcmV0dXJuIHtib29sZWFufVxuICAgICovXG4gICAgc3RhdGljIGdldCBpc011bHRpVG91Y2hUYWJsZSgpIHtcbiAgICAgICAgcmV0dXJuIENhcGFiaWxpdGllcy5kZXZpY2VQaXhlbFJhdGlvID4gMiAmJiBDYXBhYmlsaXRpZXMuaXNNb2JpbGUgPT09IGZhbHNlICYmIC9XaW5kb3dzL2kudGVzdChDYXBhYmlsaXRpZXMudXNlckFnZW50KVxuICAgIH1cblxuICAgIC8qKiBSZXR1cm5zIHRydWUgaWYgbW91c2UgZXZlbnRzIGFyZSBzdXBwb3J0ZWRcbiAgICBAcmV0dXJuIHtib29sZWFufVxuICAgICovXG4gICAgc3RhdGljIHN1cHBvcnRzTW91c2VFdmVudHMoKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2Yod2luZG93Lk1vdXNlRXZlbnQpICE9ICd1bmRlZmluZWQnXG4gICAgfVxuXG4gICAgLyoqIFJldHVybnMgdHJ1ZSBpZiB0b3VjaCBldmVudHMgYXJlIHN1cHBvcnRlZFxuICAgIEByZXR1cm4ge2Jvb2xlYW59XG4gICAgKi9cbiAgICBzdGF0aWMgc3VwcG9ydHNUb3VjaEV2ZW50cygpIHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZih3aW5kb3cuVG91Y2hFdmVudCkgIT0gJ3VuZGVmaW5lZCdcbiAgICB9XG5cbiAgICAvKiogUmV0dXJucyB0cnVlIGlmIHBvaW50ZXIgZXZlbnRzIGFyZSBzdXBwb3J0ZWRcbiAgICBAcmV0dXJuIHtib29sZWFufVxuICAgICovXG4gICAgc3RhdGljIHN1cHBvcnRzUG9pbnRlckV2ZW50cygpIHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZih3aW5kb3cuUG9pbnRlckV2ZW50KSAhPSAndW5kZWZpbmVkJ1xuICAgIH1cblxuICAgIC8qKiBSZXR1cm5zIHRydWUgaWYgRE9NIHRlbXBsYXRlcyBhcmUgc3VwcG9ydGVkXG4gICAgQHJldHVybiB7Ym9vbGVhbn1cbiAgICAqL1xuICAgIHN0YXRpYyBzdXBwb3J0c1RlbXBsYXRlKCkge1xuICAgICAgICByZXR1cm4gJ2NvbnRlbnQnIGluIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3RlbXBsYXRlJyk7XG4gICAgfVxufVxuXG4vKiogQmFzaWMgdGVzdHMgZm9yIENhcGFiaWxpdGllcy5cbiAqL1xuZXhwb3J0IGNsYXNzIENhcGFiaWxpdGllc1Rlc3RzIHtcblxuICAgIHN0YXRpYyB0ZXN0Q29uZmlybSgpIHtcbiAgICAgICAgbGV0IGJvb2wgPSBjb25maXJtKCdQbGVhc2UgY29uZmlybScpXG4gICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdkZW1vJykuaW5uZXJIVE1MID0gKGJvb2wpID8gJ0NvbmZpcm1lZCcgOiAnTm90IGNvbmZpcm1lZCdcbiAgICB9XG5cbiAgICBzdGF0aWMgdGVzdFByb21wdCgpIHtcbiAgICAgICAgbGV0IHBlcnNvbiA9IHByb21wdCgnUGxlYXNlIGVudGVyIHlvdXIgbmFtZScsICdIYXJyeSBQb3R0ZXInKVxuICAgICAgICBpZiAocGVyc29uICE9IG51bGwpIHtcbiAgICAgICAgICAgIGRlbW8uaW5uZXJIVE1MID1cbiAgICAgICAgICAgICdIZWxsbyAnICsgcGVyc29uICsgJyEgSG93IGFyZSB5b3UgdG9kYXk/J1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhdGljIHRlc3RVc2VyQWdlbnQoKSB7XG4gICAgICAgIGxldCBhZ2VudCA9ICdVc2VyLWFnZW50OiAnICsgQ2FwYWJpbGl0aWVzLnVzZXJBZ2VudFxuICAgICAgICB1c2VyX2FnZW50LmlubmVySFRNTCA9IGFnZW50XG4gICAgfVxuXG4gICAgc3RhdGljIHRlc3REZXZpY2VQaXhlbFJhdGlvKCkge1xuICAgICAgICBsZXQgdmFsdWUgPSAnRGV2aWNlIFBpeGVsIFJhdGlvOiAnICsgQ2FwYWJpbGl0aWVzLmRldmljZVBpeGVsUmF0aW9cbiAgICAgICAgZGV2aWNlX3BpeGVsX3JhdGlvLmlubmVySFRNTCA9IHZhbHVlXG4gICAgfVxuXG4gICAgc3RhdGljIHRlc3RNdWx0aVRvdWNoVGFibGUoKSB7XG4gICAgICAgIGxldCB2YWx1ZSA9ICdJcyB0aGUgZGV2aWNlIGEgbXVsdGktdG91Y2ggdGFibGU/ICcgKyBDYXBhYmlsaXRpZXMuaXNNdWx0aVRvdWNoVGFibGVcbiAgICAgICAgbXVsdGlfdG91Y2hfdGFibGUuaW5uZXJIVE1MID0gdmFsdWVcbiAgICB9XG5cbiAgICBzdGF0aWMgdGVzdFN1cHBvcnRlZEV2ZW50cygpIHtcbiAgICAgICAgbGV0IGV2ZW50cyA9IFtdXG4gICAgICAgIGlmIChDYXBhYmlsaXRpZXMuc3VwcG9ydHNNb3VzZUV2ZW50cygpKSB7XG4gICAgICAgICAgICBldmVudHMucHVzaCgnTW91c2VFdmVudHMnKVxuICAgICAgICB9XG4gICAgICAgIGlmIChDYXBhYmlsaXRpZXMuc3VwcG9ydHNUb3VjaEV2ZW50cygpKSB7XG4gICAgICAgICAgICBldmVudHMucHVzaCgnVG91Y2hFdmVudHMnKVxuICAgICAgICB9XG4gICAgICAgIGlmIChDYXBhYmlsaXRpZXMuc3VwcG9ydHNQb2ludGVyRXZlbnRzKCkpIHtcbiAgICAgICAgICAgIGV2ZW50cy5wdXNoKCdQb2ludGVyRXZlbnRzJylcbiAgICAgICAgfVxuICAgICAgICBzdXBwb3J0ZWRfZXZlbnRzLmlubmVySFRNTCA9ICdTdXBwb3J0ZWQgRXZlbnRzOiAnICsgZXZlbnRzLmpvaW4oJywgJylcbiAgICB9XG5cbiAgICBzdGF0aWMgdGVzdEFsbCgpIHtcbiAgICAgICAgdGhpcy50ZXN0VXNlckFnZW50KClcbiAgICAgICAgdGhpcy50ZXN0RGV2aWNlUGl4ZWxSYXRpbygpXG4gICAgICAgIHRoaXMudGVzdE11bHRpVG91Y2hUYWJsZSgpXG4gICAgICAgIHRoaXMudGVzdFN1cHBvcnRlZEV2ZW50cygpXG4gICAgfVxufVxuXG4vKiBPcHRpb25hbCBnbG9iYWwgdmFyaWFibGVzLCBuZWVkZWQgaW4gRG9jVGVzdHMuICovXG53aW5kb3cuQ2FwYWJpbGl0aWVzID0gQ2FwYWJpbGl0aWVzXG53aW5kb3cuQ2FwYWJpbGl0aWVzVGVzdHMgPSBDYXBhYmlsaXRpZXNUZXN0c1xuIiwiaW1wb3J0IHsgUG9pbnRzLCBQb2x5Z29uLCBBbmdsZSwgRWxlbWVudHMgfSBmcm9tICcuL3V0aWxzLmpzJ1xuaW1wb3J0IEV2ZW50cyBmcm9tICcuL2V2ZW50cy5qcydcbmltcG9ydCB7IEludGVyYWN0aW9uTWFwcGVyIH0gZnJvbSAnLi9pbnRlcmFjdGlvbi5qcydcbmltcG9ydCB7IENhcGFiaWxpdGllcyB9IGZyb20gJy4vY2FwYWJpbGl0aWVzLmpzJ1xuXG4vKipcbiAqIEEgYmFzZSBjbGFzcyBmb3Igc2NhdHRlciBzcGVjaWZpYyBldmVudHMuXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKiBAcGFyYW0ge25hbWV9IFN0cmluZyAtIFRoZSBuYW1lIG9mIHRoZSBldmVudFxuICogQHBhcmFtIHt0YXJnZXR9IE9iamVjdCAtIFRoZSB0YXJnZXQgb2YgdGhlIGV2ZW50XG4gKi9cbmV4cG9ydCBjbGFzcyBCYXNlRXZlbnQge1xuICAgIGNvbnN0cnVjdG9yKG5hbWUsIHRhcmdldCkge1xuICAgICAgICB0aGlzLm5hbWUgPSBuYW1lXG4gICAgICAgIHRoaXMudGFyZ2V0ID0gdGFyZ2V0XG4gICAgfVxufVxuXG4vLyBFdmVudCB0eXBlc1xuY29uc3QgU1RBUlQgPSAnb25TdGFydCdcbmNvbnN0IFVQREFURSA9ICdvblVwZGF0ZSdcbmNvbnN0IEVORCA9ICdvbkVuZCdcbmNvbnN0IFpPT00gPSAnb25ab29tJ1xuY29uc3QgTU9WRSA9ICdvbk1vdmUnXG5cbi8qKlxuICogQSBzY2F0dGVyIGV2ZW50IHRoYXQgZGVzY3JpYmVzIGhvdyB0aGUgc2NhdHRlciBoYXMgY2hhbmdlZC5cbiAqXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwYXJhbSB7dGFyZ2V0fSBPYmplY3QgLSBUaGUgdGFyZ2V0IHNjYXR0ZXIgb2YgdGhlIGV2ZW50XG4gKiBAcGFyYW0ge29wdGlvbmFsfSBPYmplY3QgLSBPcHRpb25hbCBwYXJhbWV0ZXJcbiAqL1xuZXhwb3J0IGNsYXNzIFNjYXR0ZXJFdmVudCBleHRlbmRzIEJhc2VFdmVudCB7XG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHRhcmdldCxcbiAgICAgICAge1xuICAgICAgICAgICAgdHJhbnNsYXRlID0geyB4OiAwLCB5OiAwIH0sXG4gICAgICAgICAgICBzY2FsZSA9IG51bGwsXG4gICAgICAgICAgICByb3RhdGUgPSAwLFxuICAgICAgICAgICAgYWJvdXQgPSBudWxsLFxuICAgICAgICAgICAgZmFzdCA9IGZhbHNlLFxuICAgICAgICAgICAgdHlwZSA9IG51bGxcbiAgICAgICAgfSA9IHt9XG4gICAgKSB7XG4gICAgICAgIHN1cGVyKCdzY2F0dGVyVHJhbnNmb3JtZWQnLCB7IHRhcmdldDogdGFyZ2V0IH0pXG4gICAgICAgIHRoaXMudHJhbnNsYXRlID0gdHJhbnNsYXRlXG4gICAgICAgIHRoaXMuc2NhbGUgPSBzY2FsZVxuICAgICAgICB0aGlzLnJvdGF0ZSA9IHJvdGF0ZVxuICAgICAgICB0aGlzLmFib3V0ID0gYWJvdXRcbiAgICAgICAgdGhpcy5mYXN0ID0gZmFzdFxuICAgICAgICB0aGlzLnR5cGUgPSB0eXBlXG4gICAgfVxuXG4gICAgdG9TdHJpbmcoKSB7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICBcIkV2ZW50KCdzY2F0dGVyVHJhbnNmb3JtZWQnLCBzY2FsZTogXCIgK1xuICAgICAgICAgICAgdGhpcy5zY2FsZSArXG4gICAgICAgICAgICAnIGFib3V0OiAnICtcbiAgICAgICAgICAgIHRoaXMuYWJvdXQueCArXG4gICAgICAgICAgICAnLCAnICtcbiAgICAgICAgICAgIHRoaXMuYWJvdXQueSArXG4gICAgICAgICAgICAnKSdcbiAgICAgICAgKVxuICAgIH1cbn1cblxuLyoqXG4gKiBBIHNjYXR0ZXIgcmVzaXplIGV2ZW50IHRoYXQgZGVzY3JpYmVzIGhvdyB0aGUgc2NhdHRlciBoYXMgY2hhbmdlZC5cbiAqXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwYXJhbSB7dGFyZ2V0fSBPYmplY3QgLSBUaGUgdGFyZ2V0IHNjYXR0ZXIgb2YgdGhlIGV2ZW50XG4gKiBAcGFyYW0ge29wdGlvbmFsfSBPYmplY3QgLSBPcHRpb25hbCBwYXJhbWV0ZXJcbiAqL1xuZXhwb3J0IGNsYXNzIFJlc2l6ZUV2ZW50IGV4dGVuZHMgQmFzZUV2ZW50IHtcbiAgICBjb25zdHJ1Y3Rvcih0YXJnZXQsIHsgd2lkdGggPSAwLCBoZWlnaHQgPSAwIH0gPSB7fSkge1xuICAgICAgICBzdXBlcignc2NhdHRlclJlc2l6ZWQnLCB7IHdpZHRoOiB3aWR0aCwgaGVpZ2h0OiBoZWlnaHQgfSlcbiAgICAgICAgdGhpcy53aWR0aCA9IHdpZHRoXG4gICAgICAgIHRoaXMuaGVpZ2h0ID0gaGVpZ2h0XG4gICAgfVxuXG4gICAgdG9TdHJpbmcoKSB7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAnRXZlbnQoc2NhdHRlclJlc2l6ZWQgd2lkdGg6ICcgK1xuICAgICAgICAgICAgdGhpcy53aWR0aCArXG4gICAgICAgICAgICAnaGVpZ2h0OiAnICtcbiAgICAgICAgICAgIHRoaXMuaGVpZ2h0ICtcbiAgICAgICAgICAgICcpJ1xuICAgICAgICApXG4gICAgfVxufVxuXG4vKipcbiAqIEEgYWJzdHJhY3QgYmFzZSBjbGFzcyB0aGF0IGltcGxlbWVudHMgdGhlIHRocm93YWJsZSBiZWhhdmlvciBvZiBhIHNjYXR0ZXJcbiAqIG9iamVjdC5cbiAqXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuY2xhc3MgVGhyb3dhYmxlIHtcbiAgICBjb25zdHJ1Y3Rvcih7XG4gICAgICAgIG1vdmFibGVYID0gdHJ1ZSxcbiAgICAgICAgbW92YWJsZVkgPSB0cnVlLFxuICAgICAgICB0aHJvd1Zpc2liaWxpdHkgPSA0NCxcbiAgICAgICAgdGhyb3dEYW1waW5nID0gMC45NSxcbiAgICAgICAgYXV0b1Rocm93ID0gdHJ1ZSxcbiAgICAgICAgb25UaHJvd0ZpbmlzaGVkID0gbnVsbFxuICAgIH0gPSB7fSkge1xuICAgICAgICB0aGlzLm1vdmFibGVYID0gbW92YWJsZVhcbiAgICAgICAgdGhpcy5tb3ZhYmxlWSA9IG1vdmFibGVZXG4gICAgICAgIHRoaXMudGhyb3dWaXNpYmlsaXR5ID0gdGhyb3dWaXNpYmlsaXR5XG4gICAgICAgIHRoaXMudGhyb3dEYW1waW5nID0gdGhyb3dEYW1waW5nXG4gICAgICAgIHRoaXMuYXV0b1Rocm93ID0gYXV0b1Rocm93XG4gICAgICAgIHRoaXMudmVsb2NpdGllcyA9IFtdXG4gICAgICAgIHRoaXMudmVsb2NpdHkgPSBudWxsXG4gICAgICAgIHRoaXMudGltZXN0YW1wID0gbnVsbFxuICAgICAgICB0aGlzLm9uVGhyb3dGaW5pc2hlZCA9IG9uVGhyb3dGaW5pc2hlZFxuICAgICAgICAvL2NvbnNvbGUubG9nKFwib25UaHJvd0ZpbmlzaGVkXCIsIG9uVGhyb3dGaW5pc2hlZClcbiAgICB9XG5cbiAgICBvYnNlcnZlVmVsb2NpdHkoKSB7XG4gICAgICAgIHRoaXMubGFzdGZyYW1lID0gcGVyZm9ybWFuY2Uubm93KClcbiAgICB9XG5cbiAgICBhZGRWZWxvY2l0eShkZWx0YSwgYnVmZmVyID0gNSkge1xuICAgICAgICBsZXQgdCA9IHBlcmZvcm1hbmNlLm5vdygpXG4gICAgICAgIGxldCBkdCA9IHQgLSB0aGlzLmxhc3RmcmFtZVxuICAgICAgICB0aGlzLmxhc3RmcmFtZSA9IHRcbiAgICAgICAgaWYgKGR0ID4gMCkge1xuICAgICAgICAgICAgLy8gQXZvaWQgZGl2aXNpb24gYnkgemVybyBlcnJvcnMgbGF0ZXIgb25cbiAgICAgICAgICAgIGxldCB2ZWxvY2l0eSA9IHsgdDogdCwgZHQ6IGR0LCBkeDogZGVsdGEueCwgZHk6IGRlbHRhLnkgfVxuICAgICAgICAgICAgdGhpcy52ZWxvY2l0aWVzLnB1c2godmVsb2NpdHkpXG4gICAgICAgICAgICB3aGlsZSAodGhpcy52ZWxvY2l0aWVzLmxlbmd0aCA+IGJ1ZmZlcikge1xuICAgICAgICAgICAgICAgIHRoaXMudmVsb2NpdGllcy5zaGlmdCgpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBtZWFuVmVsb2NpdHkobWlsbGlzZWNvbmRzID0gMzApIHtcbiAgICAgICAgdGhpcy5hZGRWZWxvY2l0eSh7IHg6IDAsIHk6IDAgfSlcbiAgICAgICAgbGV0IHN1bSA9IHsgeDogMCwgeTogMCB9XG4gICAgICAgIGxldCBjb3VudCA9IDBcbiAgICAgICAgbGV0IHQgPSAwXG4gICAgICAgIGZvciAobGV0IGkgPSB0aGlzLnZlbG9jaXRpZXMubGVuZ3RoIC0gMTsgaSA+IDA7IGktLSkge1xuICAgICAgICAgICAgbGV0IHYgPSB0aGlzLnZlbG9jaXRpZXNbaV1cbiAgICAgICAgICAgIHQgKz0gdi5kdFxuICAgICAgICAgICAgbGV0IG52ID0geyB4OiB2LmR4IC8gdi5kdCwgeTogdi5keSAvIHYuZHQgfVxuICAgICAgICAgICAgc3VtID0gUG9pbnRzLmFkZChzdW0sIG52KVxuICAgICAgICAgICAgY291bnQgKz0gMVxuICAgICAgICAgICAgaWYgKHQgPiBtaWxsaXNlY29uZHMpIHtcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChjb3VudCA9PT0gMCkgcmV0dXJuIHN1bSAvLyBlbXB0eSB2ZWN0b3JcbiAgICAgICAgcmV0dXJuIFBvaW50cy5tdWx0aXBseVNjYWxhcihzdW0sIDEgLyBjb3VudClcbiAgICB9XG5cbiAgICBraWxsQW5pbWF0aW9uKCkge1xuICAgICAgICB0aGlzLnZlbG9jaXR5ID0gbnVsbFxuICAgICAgICB0aGlzLnZlbG9jaXRpZXMgPSBbXVxuICAgIH1cblxuICAgIHN0YXJ0VGhyb3coKSB7XG4gICAgICAgIHRoaXMudmVsb2NpdHkgPSB0aGlzLm1lYW5WZWxvY2l0eSgpXG4gICAgICAgIGlmICh0aGlzLnZlbG9jaXR5ICE9IG51bGwpIHtcbiAgICAgICAgICAgIC8vIENhbGwgbmV4dCB2ZWxvY2l0eSB0byBhbnN1cmUgdGhhdCBzcGVjaWFsaXphdGlvbnNcbiAgICAgICAgICAgIC8vIHRoYXQgdXNlIGtlZXBPblN0YWdlIGFyZSBjYWxsZWRcbiAgICAgICAgICAgIHRoaXMudmVsb2NpdHkgPSB0aGlzLm5leHRWZWxvY2l0eSh0aGlzLnZlbG9jaXR5KVxuICAgICAgICAgICAgaWYgKHRoaXMuYXV0b1Rocm93KSB0aGlzLmFuaW1hdGVUaHJvdyhwZXJmb3JtYW5jZS5ub3coKSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMub25EcmFnQ29tcGxldGUoKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgYW5pbWF0ZVRocm93KHRpbWUpIHtcbiAgICAgICAgaWYgKHRoaXMudmVsb2NpdHkgIT0gbnVsbCkge1xuICAgICAgICAgICAgbGV0IHQgPSBwZXJmb3JtYW5jZS5ub3coKVxuICAgICAgICAgICAgbGV0IGR0ID0gdCAtIHRoaXMubGFzdGZyYW1lXG4gICAgICAgICAgICB0aGlzLmxhc3RmcmFtZSA9IHRcbiAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKFwiYW5pbWF0ZVRocm93XCIsIGR0KVxuICAgICAgICAgICAgbGV0IG5leHQgPSB0aGlzLm5leHRWZWxvY2l0eSh0aGlzLnZlbG9jaXR5KVxuICAgICAgICAgICAgbGV0IHByZXZMZW5ndGggPSBQb2ludHMubGVuZ3RoKHRoaXMudmVsb2NpdHkpXG4gICAgICAgICAgICBsZXQgbmV4dExlbmd0aCA9IFBvaW50cy5sZW5ndGgobmV4dClcbiAgICAgICAgICAgIGlmIChuZXh0TGVuZ3RoID4gcHJldkxlbmd0aCkge1xuICAgICAgICAgICAgICAgIGxldCBmYWN0b3IgPSBuZXh0TGVuZ3RoIC8gcHJldkxlbmd0aFxuICAgICAgICAgICAgICAgIG5leHQgPSBQb2ludHMubXVsdGlwbHlTY2FsYXIobmV4dCwgMSAvIGZhY3RvcilcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnUHJldmVudCBhY2NlbGVyYXRpb24nLCBmYWN0b3IsIHRoaXMudmVsb2NpdHksIG5leHQpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLnZlbG9jaXR5ID0gbmV4dFxuICAgICAgICAgICAgbGV0IGQgPSBQb2ludHMubXVsdGlwbHlTY2FsYXIodGhpcy52ZWxvY2l0eSwgZHQpXG4gICAgICAgICAgICB0aGlzLl9tb3ZlKGQpXG5cbiAgICAgICAgICAgIHRoaXMub25EcmFnVXBkYXRlKGQpXG4gICAgICAgICAgICBpZiAoZHQgPT0gMCB8fCB0aGlzLm5lZWRzQW5pbWF0aW9uKCkpIHtcbiAgICAgICAgICAgICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUodGhpcy5hbmltYXRlVGhyb3cuYmluZCh0aGlzKSlcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuaXNPdXRzaWRlKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKHRoaXMuYW5pbWF0ZVRocm93LmJpbmQodGhpcykpXG4gICAgICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLm9uRHJhZ0NvbXBsZXRlKClcbiAgICAgICAgaWYgKHRoaXMub25UaHJvd0ZpbmlzaGVkICE9IG51bGwpIHtcbiAgICAgICAgICAgIHRoaXMub25UaHJvd0ZpbmlzaGVkKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5lZWRzQW5pbWF0aW9uKCkge1xuICAgICAgICBpZiAodGhpcy52ZWxvY2l0eSA9PSBudWxsKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gUG9pbnRzLmxlbmd0aCh0aGlzLnZlbG9jaXR5KSA+IDAuMDFcbiAgICB9XG5cbiAgICBuZXh0VmVsb2NpdHkodmVsb2NpdHkpIHtcbiAgICAgICAgLy8gTXVzdCBiZSBvdmVyd3JpdHRlbjogY29tcHV0ZXMgdGhlIGNoYW5nZWQgdmVsb2NpdHkuIEltcGxlbWVudFxuICAgICAgICAvLyBkYW1waW5nLCBjb2xsaXNvbiBkZXRlY3Rpb24sIGV0Yy4gaGVyZVxuICAgICAgICBsZXQgbmV4dCA9IFBvaW50cy5tdWx0aXBseVNjYWxhcih2ZWxvY2l0eSwgdGhpcy50aHJvd0RhbXBpbmcpXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB4OiAodGhpcy5tb3ZhYmxlWCkgPyBuZXh0LnggOiAwLFxuICAgICAgICAgICAgeTogKHRoaXMubW92YWJsZVkpID8gbmV4dC55IDogMFxuICAgICAgICB9XG4gICAgfVxuXG4gICAgX21vdmUoZGVsdGEpIHtcbiAgICAgICAgLy8gT3ZlcndyaXRlIGlmIG5lY2Vzc2FyeVxuICAgIH1cblxuICAgIG9uRHJhZ0NvbXBsZXRlKCkge1xuICAgICAgICAvLyBPdmVyd3JpdGUgaWYgbmVjZXNzYXJ5XG4gICAgfVxuXG4gICAgb25EcmFnVXBkYXRlKGRlbHRhKSB7XG4gICAgICAgIC8vIE92ZXJ3cml0ZSBpZiBuZWNlc3NhcnlcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBYnN0cmFjdFNjYXR0ZXIgZXh0ZW5kcyBUaHJvd2FibGUge1xuICAgIGNvbnN0cnVjdG9yKHtcbiAgICAgICAgbWluU2NhbGUgPSAwLjEsXG4gICAgICAgIG1heFNjYWxlID0gMS4wLFxuICAgICAgICBzdGFydFNjYWxlID0gMS4wLFxuICAgICAgICBhdXRvQnJpbmdUb0Zyb250ID0gdHJ1ZSxcbiAgICAgICAgYXV0b1Rocm93ID0gdHJ1ZSxcbiAgICAgICAgdHJhbnNsYXRhYmxlID0gdHJ1ZSxcbiAgICAgICAgc2NhbGFibGUgPSB0cnVlLFxuICAgICAgICByb3RhdGFibGUgPSB0cnVlLFxuICAgICAgICByZXNpemFibGUgPSBmYWxzZSxcbiAgICAgICAgbW92YWJsZVggPSB0cnVlLFxuICAgICAgICBtb3ZhYmxlWSA9IHRydWUsXG4gICAgICAgIHRocm93VmlzaWJpbGl0eSA9IDQ0LFxuICAgICAgICB0aHJvd0RhbXBpbmcgPSAwLjk1LFxuICAgICAgICBvdmVyZG9TY2FsaW5nID0gMSxcbiAgICAgICAgbW91c2Vab29tRmFjdG9yID0gMS4xLFxuICAgICAgICByb3RhdGlvbkRlZ3JlZXMgPSBudWxsLFxuICAgICAgICByb3RhdGlvbiA9IG51bGwsXG4gICAgICAgIG9uVHJhbnNmb3JtID0gbnVsbCxcbiAgICAgICAgaW50ZXJhY3RpdmUgPSB0cnVlLFxuICAgICAgICBvbkNsb3NlID0gbnVsbCxcbiAgICAgICAgb25UaHJvd0ZpbmlzaGVkID0gbnVsbCxcbiAgICAgICAgc2NhbGVBdXRvQ2xvc2UgPSBmYWxzZSxcbiAgICAgICAgc2NhbGVDbG9zZVRocmVzaG9sZCA9IDAuMTAsXG4gICAgICAgIHNjYWxlQ2xvc2VCdWZmZXIgPSAwLjA1XG4gICAgfSA9IHt9KSB7XG4gICAgICAgIGlmIChyb3RhdGlvbkRlZ3JlZXMgIT0gbnVsbCAmJiByb3RhdGlvbiAhPSBudWxsKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VzZSByb3RhdGlvbkRlZ3JlZXMgb3Igcm90YXRpb24gYnV0IG5vdCBib3RoJylcbiAgICAgICAgfSBlbHNlIGlmIChyb3RhdGlvbiAhPSBudWxsKSB7XG4gICAgICAgICAgICByb3RhdGlvbkRlZ3JlZXMgPSBBbmdsZS5yYWRpYW4yZGVncmVlKHJvdGF0aW9uKVxuICAgICAgICB9IGVsc2UgaWYgKHJvdGF0aW9uRGVncmVlcyA9PSBudWxsKSB7XG4gICAgICAgICAgICByb3RhdGlvbkRlZ3JlZXMgPSAwXG4gICAgICAgIH1cbiAgICAgICAgc3VwZXIoe1xuICAgICAgICAgICAgbW92YWJsZVgsXG4gICAgICAgICAgICBtb3ZhYmxlWSxcbiAgICAgICAgICAgIHRocm93VmlzaWJpbGl0eSxcbiAgICAgICAgICAgIHRocm93RGFtcGluZyxcbiAgICAgICAgICAgIGF1dG9UaHJvdyxcbiAgICAgICAgICAgIG9uVGhyb3dGaW5pc2hlZFxuICAgICAgICB9KVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBDbG9zZXMgdGhlIGNhcmQgd2hlbiB0aGUgbWluU2NhbGUgaXMgcmVhY2hlZCBhbmQgdGhlIFxuICAgICAgICAgKiBjYXJkIGlzIHJlbGVhc2VkLiBDYXJkIGNhbiBiZSBzYXZlZCBieSBzY2FsaW5nIGl0IHVwIGFnYWluLlxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5zY2FsZUF1dG9DbG9zZSA9IHNjYWxlQXV0b0Nsb3NlXG4gICAgICAgIHRoaXMuc2NhbGVDbG9zZVRocmVzaG9sZCA9IHNjYWxlQ2xvc2VUaHJlc2hvbGRcbiAgICAgICAgdGhpcy5zY2FsZUNsb3NlQnVmZmVyID0gc2NhbGVDbG9zZUJ1ZmZlclxuICAgICAgICB0aGlzLnNjYWxlQXV0b0Nsb3NlVGltZW91dCA9IG51bGxcblxuICAgICAgICB0aGlzLmludGVyYWN0aXZlID0gaW50ZXJhY3RpdmVcbiAgICAgICAgdGhpcy5zdGFydFJvdGF0aW9uRGVncmVlcyA9IHJvdGF0aW9uRGVncmVlc1xuICAgICAgICB0aGlzLnN0YXJ0U2NhbGUgPSBzdGFydFNjYWxlIC8vIE5lZWRlZCB0byByZXNldCBvYmplY3RcbiAgICAgICAgdGhpcy5taW5TY2FsZSA9IG1pblNjYWxlXG4gICAgICAgIHRoaXMubWF4U2NhbGUgPSBtYXhTY2FsZVxuICAgICAgICB0aGlzLm92ZXJkb1NjYWxpbmcgPSBvdmVyZG9TY2FsaW5nXG4gICAgICAgIHRoaXMudHJhbnNsYXRhYmxlID0gdHJhbnNsYXRhYmxlXG4gICAgICAgIGlmICghdHJhbnNsYXRhYmxlKSB7XG4gICAgICAgICAgICB0aGlzLm1vdmFibGVYID0gZmFsc2VcbiAgICAgICAgICAgIHRoaXMubW92YWJsZVkgPSBmYWxzZVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuc2NhbGFibGUgPSBzY2FsYWJsZVxuICAgICAgICB0aGlzLnJvdGF0YWJsZSA9IHJvdGF0YWJsZVxuICAgICAgICB0aGlzLnJlc2l6YWJsZSA9IHJlc2l6YWJsZVxuICAgICAgICB0aGlzLm1vdXNlWm9vbUZhY3RvciA9IG1vdXNlWm9vbUZhY3RvclxuICAgICAgICB0aGlzLmF1dG9CcmluZ1RvRnJvbnQgPSBhdXRvQnJpbmdUb0Zyb250XG4gICAgICAgIHRoaXMuZHJhZ2dpbmcgPSBmYWxzZVxuICAgICAgICB0aGlzLm9uVHJhbnNmb3JtID0gb25UcmFuc2Zvcm0gIT0gbnVsbCA/IFtvblRyYW5zZm9ybV0gOiBudWxsXG4gICAgICAgIHRoaXMub25DbG9zZSA9IG9uQ2xvc2UgIT0gbnVsbCA/IFtvbkNsb3NlXSA6IG51bGxcbiAgICB9XG5cbiAgICBhZGRDbG9zZUV2ZW50Q2FsbGJhY2soY2FsbGJhY2spIHtcbiAgICAgICAgaWYgKHRoaXMub25DbG9zZSA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm9uQ2xvc2UgPSBbXVxuICAgICAgICB9XG4gICAgICAgIHRoaXMub25DbG9zZS5wdXNoKGNhbGxiYWNrKVxuICAgIH1cblxuICAgIGFkZFRyYW5zZm9ybUV2ZW50Q2FsbGJhY2soY2FsbGJhY2spIHtcbiAgICAgICAgaWYgKHRoaXMub25UcmFuc2Zvcm0gPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5vblRyYW5zZm9ybSA9IFtdXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5vblRyYW5zZm9ybS5wdXNoKGNhbGxiYWNrKVxuICAgIH1cblxuICAgIHN0YXJ0R2VzdHVyZShpbnRlcmFjdGlvbikge1xuICAgICAgICB0aGlzLmJyaW5nVG9Gcm9udCgpXG4gICAgICAgIHRoaXMua2lsbEFuaW1hdGlvbigpXG4gICAgICAgIHRoaXMub2JzZXJ2ZVZlbG9jaXR5KClcbiAgICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG5cbiAgICBjbG9zZSgpIHtcbiAgICAgICAgaWYgKHRoaXMub25DbG9zZSkge1xuICAgICAgICAgICAgdGhpcy5vbkNsb3NlLmZvckVhY2goY2FsbGJhY2sgPT4gY2FsbGJhY2sodGhpcykpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBnZXN0dXJlKGludGVyYWN0aW9uKSB7XG4gICAgICAgIGxldCBkZWx0YSA9IGludGVyYWN0aW9uLmRlbHRhKClcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcImdlc3R1cmVcIiwgZGVsdGEpXG4gICAgICAgIGlmIChkZWx0YSAhPSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLmFkZFZlbG9jaXR5KGRlbHRhKVxuICAgICAgICAgICAgdGhpcy50cmFuc2Zvcm0oZGVsdGEsIGRlbHRhLnpvb20sIGRlbHRhLnJvdGF0ZSwgZGVsdGEuYWJvdXQpXG4gICAgICAgICAgICBpZiAoZGVsdGEuem9vbSAhPSAxKSB0aGlzLmludGVyYWN0aW9uQW5jaG9yID0gZGVsdGEuYWJvdXRcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGdldCBwb2x5Z29uKCkge1xuICAgICAgICBsZXQgdzIgPSB0aGlzLndpZHRoICogdGhpcy5zY2FsZSAvIDJcbiAgICAgICAgbGV0IGgyID0gdGhpcy5oZWlnaHQgKiB0aGlzLnNjYWxlIC8gMlxuICAgICAgICBsZXQgY2VudGVyID0gdGhpcy5jZW50ZXJcbiAgICAgICAgbGV0IHBvbHlnb24gPSBuZXcgUG9seWdvbihjZW50ZXIpXG4gICAgICAgIHBvbHlnb24uYWRkUG9pbnQoeyB4OiAtdzIsIHk6IC1oMiB9KVxuICAgICAgICBwb2x5Z29uLmFkZFBvaW50KHsgeDogdzIsIHk6IC1oMiB9KVxuICAgICAgICBwb2x5Z29uLmFkZFBvaW50KHsgeDogdzIsIHk6IGgyIH0pXG4gICAgICAgIHBvbHlnb24uYWRkUG9pbnQoeyB4OiAtdzIsIHk6IGgyIH0pXG4gICAgICAgIHBvbHlnb24ucm90YXRlKHRoaXMucm90YXRpb24pXG4gICAgICAgIHJldHVybiBwb2x5Z29uXG4gICAgfVxuXG4gICAgaXNPdXRzaWRlKCkge1xuICAgICAgICBsZXQgc3RhZ2VQb2x5Z29uID0gdGhpcy5jb250YWluZXJQb2x5Z29uXG4gICAgICAgIGlmIChzdGFnZVBvbHlnb24gPT0gbnVsbClcbiAgICAgICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgICBsZXQgcG9seWdvbiA9IHRoaXMucG9seWdvblxuICAgICAgICBpZiAocG9seWdvbiA9PSBudWxsKVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgIGxldCByZXN1bHQgPSBzdGFnZVBvbHlnb24uaW50ZXJzZWN0c1dpdGgocG9seWdvbilcbiAgICAgICAgcmV0dXJuIHJlc3VsdCA9PT0gZmFsc2UgfHwgcmVzdWx0Lm92ZXJsYXAgPCB0aGlzLnRocm93VmlzaWJpbGl0eVxuICAgIH1cblxuICAgIHJlY2VudGVyKCkge1xuICAgICAgICAvLyBSZXR1cm4gYSBzbWFsbCB2ZWN0b3IgdGhhdCBndWFyYW50ZWVzIHRoYXQgdGhlIHNjYXR0ZXIgaXMgbW92aW5nXG4gICAgICAgIC8vIHRvd2FyZHMgdGhlIGNlbnRlciBvZiB0aGUgc3RhZ2VcbiAgICAgICAgbGV0IGNlbnRlciA9IHRoaXMuY2VudGVyXG4gICAgICAgIGxldCB0YXJnZXQgPSB0aGlzLmNvbnRhaW5lci5jZW50ZXJcbiAgICAgICAgbGV0IGRlbHRhID0gUG9pbnRzLnN1YnRyYWN0KHRhcmdldCwgY2VudGVyKVxuICAgICAgICByZXR1cm4gUG9pbnRzLm5vcm1hbGl6ZShkZWx0YSlcbiAgICB9XG5cbiAgICBuZXh0VmVsb2NpdHkodmVsb2NpdHkpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMua2VlcE9uU3RhZ2UodmVsb2NpdHkpXG4gICAgfVxuXG4gICAgYm91bmNpbmcoKSB7XG4gICAgICAgIC8vIEltcGxlbWVudHMgdGhlIGJvdW5jaW5nIGJlaGF2aW9yIG9mIHRoZSBzY2F0dGVyLiBNb3ZlcyB0aGUgc2NhdHRlclxuICAgICAgICAvLyB0byB0aGUgY2VudGVyIG9mIHRoZSBzdGFnZSBpZiB0aGUgc2NhdHRlciBpcyBvdXRzaWRlIHRoZSBzdGFnZSBvclxuICAgICAgICAvLyBub3Qgd2l0aGluIHRoZSBsaW1pdHMgb2YgdGhlIHRocm93VmlzaWJpbGl0eS5cblxuICAgICAgICBsZXQgc3RhZ2VQb2x5Z29uID0gdGhpcy5jb250YWluZXJQb2x5Z29uXG4gICAgICAgIGxldCBwb2x5Z29uID0gdGhpcy5wb2x5Z29uXG4gICAgICAgIGxldCByZXN1bHQgPSBzdGFnZVBvbHlnb24uaW50ZXJzZWN0c1dpdGgocG9seWdvbilcbiAgICAgICAgaWYgKHJlc3VsdCA9PT0gZmFsc2UgfHwgcmVzdWx0Lm92ZXJsYXAgPCB0aGlzLnRocm93VmlzaWJpbGl0eSkge1xuICAgICAgICAgICAgbGV0IGN2ID0gdGhpcy5yZWNlbnRlcigpXG4gICAgICAgICAgICBsZXQgcmVjZW50ZXJlZCA9IGZhbHNlXG4gICAgICAgICAgICB3aGlsZSAocmVzdWx0ID09PSBmYWxzZSB8fCByZXN1bHQub3ZlcmxhcCA8IHRoaXMudGhyb3dWaXNpYmlsaXR5KSB7XG4gICAgICAgICAgICAgICAgcG9seWdvbi5jZW50ZXIueCArPSBjdi54XG4gICAgICAgICAgICAgICAgcG9seWdvbi5jZW50ZXIueSArPSBjdi55XG4gICAgICAgICAgICAgICAgdGhpcy5fbW92ZShjdilcbiAgICAgICAgICAgICAgICByZXN1bHQgPSBzdGFnZVBvbHlnb24uaW50ZXJzZWN0c1dpdGgocG9seWdvbilcbiAgICAgICAgICAgICAgICByZWNlbnRlcmVkID0gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHJlY2VudGVyZWRcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICBrZWVwT25TdGFnZSh2ZWxvY2l0eSwgY29sbGlzaW9uID0gMC41KSB7XG4gICAgICAgIGxldCBzdGFnZVBvbHlnb24gPSB0aGlzLmNvbnRhaW5lclBvbHlnb25cbiAgICAgICAgaWYgKCFzdGFnZVBvbHlnb24pIHJldHVyblxuICAgICAgICBsZXQgcG9seWdvbiA9IHRoaXMucG9seWdvblxuICAgICAgICBsZXQgYm91bmNlZCA9IHRoaXMuYm91bmNpbmcoKVxuICAgICAgICBpZiAoYm91bmNlZCkge1xuICAgICAgICAgICAgbGV0IHN0YWdlID0gdGhpcy5jb250YWluZXJCb3VuZHNcbiAgICAgICAgICAgIGxldCB4ID0gdGhpcy5jZW50ZXIueFxuICAgICAgICAgICAgbGV0IHkgPSB0aGlzLmNlbnRlci55XG4gICAgICAgICAgICBsZXQgZHggPSB0aGlzLm1vdmFibGVYID8gdmVsb2NpdHkueCA6IDBcbiAgICAgICAgICAgIGxldCBkeSA9IHRoaXMubW92YWJsZVkgPyB2ZWxvY2l0eS55IDogMFxuICAgICAgICAgICAgbGV0IGZhY3RvciA9IHRoaXMudGhyb3dEYW1waW5nXG4gICAgICAgICAgICAvLyBpZiAocmVjZW50ZXJlZCkge1xuICAgICAgICAgICAgaWYgKHggPCAwKSB7XG4gICAgICAgICAgICAgICAgZHggPSAtZHhcbiAgICAgICAgICAgICAgICBmYWN0b3IgPSBjb2xsaXNpb25cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh4ID4gc3RhZ2Uud2lkdGgpIHtcbiAgICAgICAgICAgICAgICBkeCA9IC1keFxuICAgICAgICAgICAgICAgIGZhY3RvciA9IGNvbGxpc2lvblxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHkgPCAwKSB7XG4gICAgICAgICAgICAgICAgZHkgPSAtZHlcbiAgICAgICAgICAgICAgICBmYWN0b3IgPSBjb2xsaXNpb25cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh5ID4gc3RhZ2UuaGVpZ2h0KSB7XG4gICAgICAgICAgICAgICAgZHkgPSAtZHlcbiAgICAgICAgICAgICAgICBmYWN0b3IgPSBjb2xsaXNpb25cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIH1cbiAgICAgICAgICAgIHJldHVybiBQb2ludHMubXVsdGlwbHlTY2FsYXIoeyB4OiBkeCwgeTogZHkgfSwgZmFjdG9yKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBzdXBlci5uZXh0VmVsb2NpdHkodmVsb2NpdHkpXG4gICAgfVxuXG4gICAgZW5kR2VzdHVyZShpbnRlcmFjdGlvbikge1xuICAgICAgICB0aGlzLnN0YXJ0VGhyb3coKVxuICAgICAgICB0aGlzLl9jaGVja0F1dG9DbG9zZSgpXG4gICAgfVxuXG4gICAgX2NoZWNrQXV0b0Nsb3NlKCkge1xuICAgICAgICBpZiAodGhpcy5zY2FsZUF1dG9DbG9zZSlcbiAgICAgICAgICAgIGlmICh0aGlzLnNjYWxlIDwgdGhpcy5taW5TY2FsZSArIHRoaXMuc2NhbGVDbG9zZVRocmVzaG9sZCAtIHRoaXMuc2NhbGVDbG9zZUJ1ZmZlcikge1xuICAgICAgICAgICAgICAgIHRoaXMuem9vbSh0aGlzLm1pblNjYWxlLCB7IGFuaW1hdGU6IDAuMiwgb25Db21wbGV0ZTogdGhpcy5jbG9zZS5iaW5kKHRoaXMpIH0pXG4gICAgICAgICAgICB9IGVsc2UgaWYgKHRoaXMuc2NhbGUgPCB0aGlzLm1pblNjYWxlICsgdGhpcy5zY2FsZUNsb3NlVGhyZXNob2xkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy56b29tKHRoaXMubWluU2NhbGUgKyB0aGlzLnNjYWxlQ2xvc2VUaHJlc2hvbGQsIHsgYW5pbWF0ZTogMC40IH0pXG4gICAgICAgICAgICB9XG4gICAgfVxuXG4gICAgcm90YXRlRGVncmVlcyhkZWdyZWVzLCBhbmNob3IpIHtcbiAgICAgICAgbGV0IHJhZCA9IEFuZ2xlLmRlZ3JlZTJyYWRpYW4oZGVncmVlcylcbiAgICAgICAgdGhpcy5yb3RhdGUocmFkLCBhbmNob3IpXG4gICAgfVxuXG4gICAgcm90YXRlKHJhZCwgYW5jaG9yKSB7XG4gICAgICAgIHRoaXMudHJhbnNmb3JtKHsgeDogMCwgeTogMCB9LCAxLjAsIHJhZCwgYW5jaG9yKVxuICAgIH1cblxuICAgIG1vdmUoZCwgeyBhbmltYXRlID0gMCB9ID0ge30pIHtcbiAgICAgICAgaWYgKHRoaXMudHJhbnNsYXRhYmxlKSB7XG4gICAgICAgICAgICBpZiAoYW5pbWF0ZSA+IDApIHtcbiAgICAgICAgICAgICAgICBsZXQgc3RhcnRQb3MgPSB0aGlzLnBvc2l0aW9uXG4gICAgICAgICAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMsIGFuaW1hdGUsIHtcbiAgICAgICAgICAgICAgICAgICAgeDogJys9JyArIGQueCxcbiAgICAgICAgICAgICAgICAgICAgeTogJys9JyArIGQueSxcbiAgICAgICAgICAgICAgICAgICAgLyogc2NhbGU6IHNjYWxlLCB1bzogbm90IGRlZmluZWQsIHdoeSB3YXMgdGhpcyBoZXJlPyAqL1xuICAgICAgICAgICAgICAgICAgICBvblVwZGF0ZTogZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgcCA9IHRoaXMucG9zaXRpb25cbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBkeCA9IHAueCAtIHN0YXJ0UG9zLnhcbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBkeSA9IHAueCAtIHN0YXJ0UG9zLnlcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25Nb3ZlZChkeCwgZHkpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9tb3ZlKGQpXG4gICAgICAgICAgICAgICAgdGhpcy5vbk1vdmVkKGQueCwgZC55KVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgbW92ZVRvKHAsIHsgYW5pbWF0ZSA9IDAgfSA9IHt9KSB7XG4gICAgICAgIGxldCBjID0gdGhpcy5vcmlnaW5cbiAgICAgICAgbGV0IGRlbHRhID0gUG9pbnRzLnN1YnRyYWN0KHAsIGMpXG4gICAgICAgIHRoaXMubW92ZShkZWx0YSwgeyBhbmltYXRlOiBhbmltYXRlIH0pXG4gICAgfVxuXG4gICAgY2VudGVyQXQocCwgeyBhbmltYXRlID0gMCB9ID0ge30pIHtcbiAgICAgICAgbGV0IGMgPSB0aGlzLmNlbnRlclxuICAgICAgICBsZXQgZGVsdGEgPSBQb2ludHMuc3VidHJhY3QocCwgYylcbiAgICAgICAgdGhpcy5tb3ZlKGRlbHRhLCB7IGFuaW1hdGU6IGFuaW1hdGUgfSlcbiAgICB9XG5cbiAgICB6b29tKFxuICAgICAgICBzY2FsZSxcbiAgICAgICAge1xuICAgICAgICAgICAgYW5pbWF0ZSA9IDAsXG4gICAgICAgICAgICBhYm91dCA9IG51bGwsXG4gICAgICAgICAgICBkZWxheSA9IDAsXG4gICAgICAgICAgICB4ID0gbnVsbCxcbiAgICAgICAgICAgIHkgPSBudWxsLFxuICAgICAgICAgICAgb25Db21wbGV0ZSA9IG51bGxcbiAgICAgICAgfSA9IHt9XG4gICAgKSB7XG4gICAgICAgIGxldCBhbmNob3IgPSBhYm91dCB8fCB0aGlzLmNlbnRlclxuICAgICAgICBpZiAoc2NhbGUgIT0gdGhpcy5zY2FsZSkge1xuICAgICAgICAgICAgaWYgKGFuaW1hdGUgPiAwKSB7XG4gICAgICAgICAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMsIGFuaW1hdGUsIHtcbiAgICAgICAgICAgICAgICAgICAgc2NhbGU6IHNjYWxlLFxuICAgICAgICAgICAgICAgICAgICBkZWxheTogZGVsYXksXG4gICAgICAgICAgICAgICAgICAgIG9uQ29tcGxldGU6IG9uQ29tcGxldGUsXG4gICAgICAgICAgICAgICAgICAgIG9uVXBkYXRlOiB0aGlzLm9uWm9vbWVkLmJpbmQodGhpcylcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLnNjYWxlID0gc2NhbGVcbiAgICAgICAgICAgICAgICB0aGlzLm9uWm9vbWVkKGFuY2hvcilcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIF9tb3ZlKGRlbHRhKSB7XG4gICAgICAgIHRoaXMueCArPSB0aGlzLm1vdmFibGVYID8gZGVsdGEueCA6IDBcbiAgICAgICAgdGhpcy55ICs9IHRoaXMubW92YWJsZVggPyBkZWx0YS55IDogMFxuICAgIH1cblxuICAgIHRyYW5zZm9ybSh0cmFuc2xhdGUsIHpvb20sIHJvdGF0ZSwgYW5jaG9yKSB7XG4gICAgICAgIGxldCBkZWx0YSA9IHtcbiAgICAgICAgICAgIHg6IHRoaXMubW92YWJsZVggPyB0cmFuc2xhdGUueCA6IDAsXG4gICAgICAgICAgICB5OiB0aGlzLm1vdmFibGVZID8gdHJhbnNsYXRlLnkgOiAwXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMucmVzaXphYmxlKSB2YXIgdnpvb20gPSB6b29tXG4gICAgICAgIGlmICghdGhpcy50cmFuc2xhdGFibGUpIGRlbHRhID0geyB4OiAwLCB5OiAwIH1cbiAgICAgICAgaWYgKCF0aGlzLnJvdGF0YWJsZSkgcm90YXRlID0gMFxuICAgICAgICBpZiAoIXRoaXMuc2NhbGFibGUpIHpvb20gPSAxLjBcbiAgICAgICAgaWYgKHpvb20gPT0gMS4wICYmIHJvdGF0ZSA9PSAwKSB7XG4gICAgICAgICAgICB0aGlzLl9tb3ZlKGRlbHRhKVxuICAgICAgICAgICAgaWYgKHRoaXMub25UcmFuc2Zvcm0gIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIGxldCBldmVudCA9IG5ldyBTY2F0dGVyRXZlbnQodGhpcywge1xuICAgICAgICAgICAgICAgICAgICB0cmFuc2xhdGU6IGRlbHRhLFxuICAgICAgICAgICAgICAgICAgICBzY2FsZTogdGhpcy5zY2FsZSxcbiAgICAgICAgICAgICAgICAgICAgcm90YXRlOiAwLFxuICAgICAgICAgICAgICAgICAgICBhYm91dDogYW5jaG9yLFxuICAgICAgICAgICAgICAgICAgICBmYXN0OiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogVVBEQVRFXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICB0aGlzLm9uVHJhbnNmb3JtLmZvckVhY2goZnVuY3Rpb24gKGYpIHtcbiAgICAgICAgICAgICAgICAgICAgZihldmVudClcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgbGV0IG9yaWdpbiA9IHRoaXMucm90YXRpb25PcmlnaW5cbiAgICAgICAgbGV0IGJldGEgPSBQb2ludHMuYW5nbGUob3JpZ2luLCBhbmNob3IpXG4gICAgICAgIGxldCBkaXN0YW5jZSA9IFBvaW50cy5kaXN0YW5jZShvcmlnaW4sIGFuY2hvcilcbiAgICAgICAgbGV0IHsgc2NhbGU6IG5ld1NjYWxlLCB6b29tOiB0aHJlc2hvbGRlZFpvb20gfSA9IHRoaXMuY2FsY3VsYXRlU2NhbGUoem9vbSlcblxuICAgICAgICBsZXQgbmV3T3JpZ2luID0gUG9pbnRzLmFyYyhhbmNob3IsIGJldGEgKyByb3RhdGUsIGRpc3RhbmNlICogdGhyZXNob2xkZWRab29tKVxuICAgICAgICBsZXQgZXh0cmEgPSBQb2ludHMuc3VidHJhY3QobmV3T3JpZ2luLCBvcmlnaW4pXG4gICAgICAgIGxldCBvZmZzZXQgPSBQb2ludHMuc3VidHJhY3QoYW5jaG9yLCBvcmlnaW4pXG4gICAgICAgIHRoaXMuX21vdmUob2Zmc2V0KVxuICAgICAgICB0aGlzLnNjYWxlID0gbmV3U2NhbGVcbiAgICAgICAgdGhpcy5yb3RhdGlvbiArPSByb3RhdGVcbiAgICAgICAgb2Zmc2V0ID0gUG9pbnRzLm5lZ2F0ZShvZmZzZXQpXG4gICAgICAgIG9mZnNldCA9IFBvaW50cy5hZGQob2Zmc2V0LCBleHRyYSlcbiAgICAgICAgb2Zmc2V0ID0gUG9pbnRzLmFkZChvZmZzZXQsIHRyYW5zbGF0ZSlcbiAgICAgICAgdGhpcy5fbW92ZShvZmZzZXQpXG5cbiAgICAgICAgZGVsdGEueCArPSBleHRyYS54XG4gICAgICAgIGRlbHRhLnkgKz0gZXh0cmEueVxuICAgICAgICBpZiAodGhpcy5vblRyYW5zZm9ybSAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGU6IGRlbHRhLFxuICAgICAgICAgICAgICAgIHNjYWxlOiBuZXdTY2FsZSxcbiAgICAgICAgICAgICAgICByb3RhdGU6IHJvdGF0ZSxcbiAgICAgICAgICAgICAgICBhYm91dDogYW5jaG9yXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgdGhpcy5vblRyYW5zZm9ybS5mb3JFYWNoKGZ1bmN0aW9uIChmKSB7XG4gICAgICAgICAgICAgICAgZihldmVudClcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMucmVzaXphYmxlKSB7XG4gICAgICAgICAgICB0aGlzLnJlc2l6ZUFmdGVyVHJhbnNmb3JtKHZ6b29tKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRm9yIGEgZ2l2ZW4gem9vbSwgYSBuZXcgc2NhbGUgaXMgY2FsY3VsYXRlZCwgdGFraW5nXG4gICAgICogbWluIGFuZCBtYXggc2NhbGUgaW50byBhY2NvdW50LlxuICAgICAqIFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB6b29tIC0gVGhlIHpvb20gZmFjdG9yLCB0byBzY2FsZSB0aGUgb2JqZWN0IHdpdGguXG4gICAgICogQHJldHVybnMge29iamVjdH0gLSBSZXR1cm5zIGFuIG9iamVjdCBjb250YWluaW5nIHRoZSBhIHZhbHVlIGZvciBhIHZhbGlkIHNjYWxlIGFuZCB0aGUgY29ycmVjdGVkIHpvb20gZmFjdG9yLlxuICAgICAqL1xuICAgIGNhbGN1bGF0ZVNjYWxlKHpvb20pIHtcbiAgICAgICAgbGV0IHNjYWxlID0gdGhpcy5zY2FsZSAqIHpvb21cblxuICAgICAgICBsZXQgbWluU2NhbGUgPSB0aGlzLm1pblNjYWxlIC8gdGhpcy5vdmVyZG9TY2FsaW5nXG4gICAgICAgIGxldCBtYXhTY2FsZSA9IHRoaXMubWF4U2NhbGUgKiB0aGlzLm92ZXJkb1NjYWxpbmdcbiAgICAgICAgaWYgKHNjYWxlIDwgbWluU2NhbGUpIHtcbiAgICAgICAgICAgIHNjYWxlID0gbWluU2NhbGVcbiAgICAgICAgICAgIHpvb20gPSBzY2FsZSAvIHRoaXMuc2NhbGVcbiAgICAgICAgfVxuICAgICAgICBpZiAoc2NhbGUgPiBtYXhTY2FsZSkge1xuICAgICAgICAgICAgc2NhbGUgPSBtYXhTY2FsZVxuICAgICAgICAgICAgem9vbSA9IHNjYWxlIC8gdGhpcy5zY2FsZVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuc2NhbGVBdXRvQ2xvc2UpXG4gICAgICAgICAgICB0aGlzLl91cGRhdGVUcmFuc3BhcmVuY3koKVxuXG4gICAgICAgIHJldHVybiB7IHpvb20sIHNjYWxlIH1cbiAgICB9XG5cbiAgICBfdXBkYXRlVHJhbnNwYXJlbmN5KCkge1xuICAgICAgICBpZiAodGhpcy5zY2FsZSA8IHRoaXMubWluU2NhbGUgKyB0aGlzLnNjYWxlQ2xvc2VUaHJlc2hvbGQpIHtcbiAgICAgICAgICAgIGxldCB0cmFuc3BhcmVuY3kgPSB0aGlzLmNhbGN1bGF0ZVNjYWxlVHJhbnNwYXJlbmN5KClcbiAgICAgICAgICAgIHRoaXMuZWxlbWVudC5zdHlsZS5vcGFjaXR5ID0gdHJhbnNwYXJlbmN5XG4gICAgICAgIH0gZWxzZSB0aGlzLmVsZW1lbnQuc3R5bGUub3BhY2l0eSA9IDFcbiAgICB9XG5cbiAgICBjYWxjdWxhdGVTY2FsZVRyYW5zcGFyZW5jeSgpIHtcbiAgICAgICAgbGV0IHRyYW5zcGFyZW5jeSA9ICh0aGlzLnNjYWxlIC0gdGhpcy5taW5TY2FsZSkgLyB0aGlzLnNjYWxlQ2xvc2VUaHJlc2hvbGRcbiAgICAgICAgdHJhbnNwYXJlbmN5ID0gKHRyYW5zcGFyZW5jeSA+IDEpID8gMSA6ICh0cmFuc3BhcmVuY3kgPCAwKSA/IDAgOiB0cmFuc3BhcmVuY3lcbiAgICAgICAgcmV0dXJuIHRyYW5zcGFyZW5jeVxuICAgIH1cblxuICAgIHJlc2l6ZUFmdGVyVHJhbnNmb3JtKHpvb20pIHtcbiAgICAgICAgLy8gT3ZlcndyaXRlIHRoaXMgaW4gc3ViY2xhc3Nlcy5cbiAgICB9XG5cbiAgICB2YWxpZFNjYWxlKHNjYWxlKSB7XG4gICAgICAgIHNjYWxlID0gTWF0aC5tYXgoc2NhbGUsIHRoaXMubWluU2NhbGUpXG4gICAgICAgIHNjYWxlID0gTWF0aC5taW4oc2NhbGUsIHRoaXMubWF4U2NhbGUpXG4gICAgICAgIHJldHVybiBzY2FsZVxuICAgIH1cblxuICAgIGFuaW1hdGVab29tQm91bmNlKGR0ID0gMSkge1xuICAgICAgICBpZiAodGhpcy56b29tQW5jaG9yICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxldCB6b29tID0gMVxuICAgICAgICAgICAgbGV0IGFtb3VudCA9IE1hdGgubWluKDAuMDEsIDAuMyAqIGR0IC8gMTAwMDAwLjApXG4gICAgICAgICAgICBpZiAodGhpcy5zY2FsZSA8IHRoaXMubWluU2NhbGUpIHpvb20gPSAxICsgYW1vdW50XG4gICAgICAgICAgICBpZiAodGhpcy5zY2FsZSA+IHRoaXMubWF4U2NhbGUpIHpvb20gPSAxIC0gYW1vdW50XG4gICAgICAgICAgICBpZiAoem9vbSAhPSAxKSB7XG4gICAgICAgICAgICAgICAgdGhpcy50cmFuc2Zvcm0oeyB4OiAwLCB5OiAwIH0sIHpvb20sIDAsIHRoaXMuem9vbUFuY2hvcilcbiAgICAgICAgICAgICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoZHQgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmFuaW1hdGVab29tQm91bmNlKGR0KVxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLnpvb21BbmNob3IgPSBudWxsXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBjaGVja1NjYWxpbmcoYWJvdXQsIGRlbGF5ID0gMCkge1xuICAgICAgICB0aGlzLnpvb21BbmNob3IgPSBhYm91dFxuICAgICAgICBjbGVhclRpbWVvdXQodGhpcy5hbmltYXRlWm9vbUJvdW5jZS5iaW5kKHRoaXMpKVxuICAgICAgICBzZXRUaW1lb3V0KHRoaXMuYW5pbWF0ZVpvb21Cb3VuY2UuYmluZCh0aGlzKSwgZGVsYXkpXG4gICAgfVxuXG4gICAgb25Nb3VzZVdoZWVsKGV2ZW50KSB7XG4gICAgICAgIGlmIChldmVudC5jbGFpbWVkQnlTY2F0dGVyKSB7XG4gICAgICAgICAgICBpZiAoZXZlbnQuY2xhaW1lZEJ5U2NhdHRlciAhPSB0aGlzKSByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICB0aGlzLmtpbGxBbmltYXRpb24oKVxuICAgICAgICB0aGlzLnRhcmdldFNjYWxlID0gbnVsbFxuICAgICAgICBsZXQgZGlyZWN0aW9uID0gZXZlbnQuZGV0YWlsIDwgMCB8fCBldmVudC53aGVlbERlbHRhID4gMFxuICAgICAgICBsZXQgZ2xvYmFsUG9pbnQgPSB7IHg6IGV2ZW50LmNsaWVudFgsIHk6IGV2ZW50LmNsaWVudFkgfVxuICAgICAgICBsZXQgY2VudGVyUG9pbnQgPSB0aGlzLm1hcFBvc2l0aW9uVG9Db250YWluZXJQb2ludChnbG9iYWxQb2ludClcbiAgICAgICAgaWYgKGV2ZW50LnNoaWZ0S2V5KSB7XG4gICAgICAgICAgICBsZXQgZGVncmVlcyA9IGRpcmVjdGlvbiA/IDUgOiAtNVxuICAgICAgICAgICAgbGV0IHJhZCA9IEFuZ2xlLmRlZ3JlZTJyYWRpYW4oZGVncmVlcylcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnRyYW5zZm9ybSh7IHg6IDAsIHk6IDAgfSwgMS4wLCByYWQsIGNlbnRlclBvaW50KVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHpvb21GYWN0b3IgPSB0aGlzLm1vdXNlWm9vbUZhY3RvclxuICAgICAgICBsZXQgem9vbSA9IGRpcmVjdGlvbiA/IHpvb21GYWN0b3IgOiAxIC8gem9vbUZhY3RvclxuICAgICAgICB0aGlzLnRyYW5zZm9ybSh7IHg6IDAsIHk6IDAgfSwgem9vbSwgMCwgY2VudGVyUG9pbnQpXG4gICAgICAgIHRoaXMuY2hlY2tTY2FsaW5nKGNlbnRlclBvaW50LCAyMDApXG5cbiAgICAgICAgaWYgKHRoaXMuc2NhbGVBdXRvQ2xvc2UpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnNjYWxlIDw9IHRoaXMubWluU2NhbGUgKyB0aGlzLnNjYWxlQ2xvc2VUaHJlc2hvbGQpIHtcblxuICAgICAgICAgICAgICAgIGlmICh0aGlzLnNjYWxlQXV0b0Nsb3NlVGltZW91dCkgY2xlYXJUaW1lb3V0KHRoaXMuc2NhbGVBdXRvQ2xvc2VUaW1lb3V0KVxuICAgICAgICAgICAgICAgIHRoaXMuc2NhbGVBdXRvQ2xvc2VUaW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2NoZWNrQXV0b0Nsb3NlKClcbiAgICAgICAgICAgICAgICB9LCA2MDApXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLl91cGRhdGVUcmFuc3BhcmVuY3koKVxuICAgICAgICB9XG4gICAgICAgIC8vXG4gICAgICAgIC8vICAgICAgICAgaWYgKHRoaXMub25UcmFuc2Zvcm0gIT0gbnVsbCkge1xuICAgICAgICAvLyAgICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgLy8gICAgICAgICAgICAgICAgIHRyYW5zbGF0ZToge3g6IDAsIHk6IDB9LFxuICAgICAgICAvLyAgICAgICAgICAgICAgICAgc2NhbGU6IHRoaXMuc2NhbGUsXG4gICAgICAgIC8vICAgICAgICAgICAgICAgICByb3RhdGU6IDAsXG4gICAgICAgIC8vICAgICAgICAgICAgICAgICBhYm91dDogbnVsbCxcbiAgICAgICAgLy8gICAgICAgICAgICAgICAgIGZhc3Q6IGZhbHNlLFxuICAgICAgICAvLyAgICAgICAgICAgICAgICAgdHlwZTogWk9PTVxuICAgICAgICAvLyAgICAgICAgICAgICB9KVxuICAgICAgICAvLyAgICAgICAgICAgICB0aGlzLm9uVHJhbnNmb3JtLmZvckVhY2goZnVuY3Rpb24oZikge1xuICAgICAgICAvLyAgICAgICAgICAgICAgICAgZihldmVudClcbiAgICAgICAgLy8gICAgICAgICAgICAgfSlcbiAgICAgICAgLy8gICAgICAgICB9XG4gICAgfVxuXG4gICAgb25TdGFydChldmVudCwgaW50ZXJhY3Rpb24pIHtcblxuICAgICAgICBpZiAodGhpcy5zdGFydEdlc3R1cmUoaW50ZXJhY3Rpb24pKSB7XG4gICAgICAgICAgICB0aGlzLmRyYWdnaW5nID0gdHJ1ZVxuICAgICAgICAgICAgdGhpcy5pbnRlcmFjdGlvbkFuY2hvciA9IG51bGxcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5vblRyYW5zZm9ybSAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGU6IHsgeDogMCwgeTogMCB9LFxuICAgICAgICAgICAgICAgIHNjYWxlOiB0aGlzLnNjYWxlLFxuICAgICAgICAgICAgICAgIHJvdGF0ZTogMCxcbiAgICAgICAgICAgICAgICBhYm91dDogbnVsbCxcbiAgICAgICAgICAgICAgICBmYXN0OiBmYWxzZSxcbiAgICAgICAgICAgICAgICB0eXBlOiBTVEFSVFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHRoaXMub25UcmFuc2Zvcm0uZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICAgICAgICAgIGYoZXZlbnQpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25Nb3ZlKGV2ZW50LCBpbnRlcmFjdGlvbikge1xuICAgICAgICAvKiogQXMgbG9uZyBhcyBtb3VzZW91dCAmJiBtb3VzZWxlYXZlIGludGVycnVwdCB3ZSBjYW5ub3QgYmUgc3VyZSB0aGF0XG4gICAgICAgICAqIGRyYWdnaW5nIHJlbWFpbnMgY29ycmVjdC5cbiAgICAgICAgICovXG4gICAgICAgIGlmICh0aGlzLmRyYWdnaW5nKSB7XG4gICAgICAgICAgICB0aGlzLmdlc3R1cmUoaW50ZXJhY3Rpb24pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvbkVuZChldmVudCwgaW50ZXJhY3Rpb24pIHtcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcIlNjYXR0ZXIub25FbmRcIiwgdGhpcy5kcmFnZ2luZylcbiAgICAgICAgaWYgKGludGVyYWN0aW9uLmlzRmluaXNoZWQoKSkge1xuICAgICAgICAgICAgdGhpcy5lbmRHZXN0dXJlKGludGVyYWN0aW9uKVxuICAgICAgICAgICAgdGhpcy5kcmFnZ2luZyA9IGZhbHNlXG4gICAgICAgICAgICBmb3IgKGxldCBrZXkgb2YgaW50ZXJhY3Rpb24uZW5kZWQua2V5cygpKSB7XG4gICAgICAgICAgICAgICAgaWYgKGludGVyYWN0aW9uLmlzVGFwKGtleSkpIHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IHBvaW50ID0gaW50ZXJhY3Rpb24uZW5kZWQuZ2V0KGtleSlcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vblRhcChldmVudCwgaW50ZXJhY3Rpb24sIHBvaW50KVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0aGlzLm9uVHJhbnNmb3JtICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgICAgICAgICAgICAgdHJhbnNsYXRlOiB7IHg6IDAsIHk6IDAgfSxcbiAgICAgICAgICAgICAgICAgICAgc2NhbGU6IHRoaXMuc2NhbGUsXG4gICAgICAgICAgICAgICAgICAgIHJvdGF0ZTogMCxcbiAgICAgICAgICAgICAgICAgICAgYWJvdXQ6IG51bGwsXG4gICAgICAgICAgICAgICAgICAgIGZhc3Q6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICB0eXBlOiBFTkRcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIHRoaXMub25UcmFuc2Zvcm0uZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICAgICAgICAgICAgICBmKGV2ZW50KVxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGV0IGFib3V0ID0gdGhpcy5pbnRlcmFjdGlvbkFuY2hvclxuICAgICAgICBpZiAoYWJvdXQgIT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5jaGVja1NjYWxpbmcoYWJvdXQsIDEwMClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uVGFwKGV2ZW50LCBpbnRlcmFjdGlvbiwgcG9pbnQpIHsgfVxuXG4gICAgb25EcmFnVXBkYXRlKGRlbHRhKSB7XG4gICAgICAgIGlmICh0aGlzLm9uVHJhbnNmb3JtICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxldCBldmVudCA9IG5ldyBTY2F0dGVyRXZlbnQodGhpcywge1xuICAgICAgICAgICAgICAgIGZhc3Q6IHRydWUsXG4gICAgICAgICAgICAgICAgdHJhbnNsYXRlOiBkZWx0YSxcbiAgICAgICAgICAgICAgICBzY2FsZTogdGhpcy5zY2FsZSxcbiAgICAgICAgICAgICAgICBhYm91dDogdGhpcy5jdXJyZW50QWJvdXQsXG4gICAgICAgICAgICAgICAgdHlwZTogbnVsbFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHRoaXMub25UcmFuc2Zvcm0uZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICAgICAgICAgIGYoZXZlbnQpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25EcmFnQ29tcGxldGUoKSB7XG4gICAgICAgIGlmICh0aGlzLm9uVHJhbnNmb3JtKSB7XG4gICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgICAgICAgICBzY2FsZTogdGhpcy5zY2FsZSxcbiAgICAgICAgICAgICAgICBhYm91dDogdGhpcy5jdXJyZW50QWJvdXQsXG4gICAgICAgICAgICAgICAgZmFzdDogZmFsc2UsXG4gICAgICAgICAgICAgICAgdHlwZTogbnVsbFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHRoaXMub25UcmFuc2Zvcm0uZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICAgICAgICAgIGYoZXZlbnQpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25Nb3ZlZChkeCwgZHksIGFib3V0KSB7XG4gICAgICAgIGlmICh0aGlzLm9uVHJhbnNmb3JtICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxldCBldmVudCA9IG5ldyBTY2F0dGVyRXZlbnQodGhpcywge1xuICAgICAgICAgICAgICAgIHRyYW5zbGF0ZTogeyB4OiBkeCwgeTogZHkgfSxcbiAgICAgICAgICAgICAgICBhYm91dDogYWJvdXQsXG4gICAgICAgICAgICAgICAgZmFzdDogdHJ1ZSxcbiAgICAgICAgICAgICAgICB0eXBlOiBudWxsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgdGhpcy5vblRyYW5zZm9ybS5mb3JFYWNoKGZ1bmN0aW9uIChmKSB7XG4gICAgICAgICAgICAgICAgZihldmVudClcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvblJlc2l6aW5nKCkge1xuICAgICAgICBpZiAodGhpcy5vblRyYW5zZm9ybSAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgICAgICAgICBzY2FsZTogdGhpcy5zY2FsZSxcbiAgICAgICAgICAgICAgICBmYXN0OiBmYWxzZSxcbiAgICAgICAgICAgICAgICB0eXBlOiBudWxsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMub25UcmFuc2Zvcm0uZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICAgICAgICAgIGYoZXZlbnQpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvblpvb21lZChhYm91dCkge1xuXG4gICAgICAgIGlmICh0aGlzLnNjYWxlQXV0b0Nsb3NlKVxuICAgICAgICAgICAgdGhpcy5fdXBkYXRlVHJhbnNwYXJlbmN5KClcblxuICAgICAgICBpZiAodGhpcy5vblRyYW5zZm9ybSAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgZXZlbnQgPSBuZXcgU2NhdHRlckV2ZW50KHRoaXMsIHtcbiAgICAgICAgICAgICAgICBzY2FsZTogdGhpcy5zY2FsZSxcbiAgICAgICAgICAgICAgICBhYm91dDogYWJvdXQsXG4gICAgICAgICAgICAgICAgZmFzdDogZmFsc2UsXG4gICAgICAgICAgICAgICAgdHlwZTogbnVsbFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHRoaXMub25UcmFuc2Zvcm0uZm9yRWFjaChmdW5jdGlvbiAoZikge1xuICAgICAgICAgICAgICAgIGYoZXZlbnQpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgfVxufVxuXG4vKiogQSBjb250YWluZXIgZm9yIHNjYXR0ZXIgb2JqZWN0cywgd2hpY2ggdXNlcyBhIHNpbmdsZSBJbnRlcmFjdGlvbk1hcHBlclxuICogZm9yIGFsbCBjaGlsZHJlbi4gVGhpcyByZWR1Y2VzIHRoZSBudW1iZXIgb2YgcmVnaXN0ZXJlZCBldmVudCBoYW5kbGVyc1xuICogYW5kIGNvdmVycyB0aGUgY29tbW9uIHVzZSBjYXNlIHRoYXQgbXVsdGlwbGUgb2JqZWN0cyBhcmUgc2NhdHRlcmVkXG4gKiBvbiB0aGUgc2FtZSBsZXZlbC5cbiAqL1xuZXhwb3J0IGNsYXNzIERPTVNjYXR0ZXJDb250YWluZXIge1xuICAgIC8qKlxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqIEBwYXJhbSB7RE9NIG5vZGV9IGVsZW1lbnQgLSBET00gZWxlbWVudCB0aGF0IHJlY2VpdmVzIGV2ZW50c1xuICAgICAqIEBwYXJhbSB7Qm9vbH0gc3RvcEV2ZW50cyAtICBXaGV0aGVyIGV2ZW50cyBzaG91bGQgYmUgc3RvcHBlZCBvciBwcm9wYWdhdGVkXG4gICAgICogQHBhcmFtIHtCb29sfSBjbGFpbUV2ZW50cyAtIFdoZXRoZXIgZXZlbnRzIHNob3VsZCBiZSBtYXJrZWQgYXMgY2xhaW1lZFxuICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiBmaW5kVGFyZ2V0IHJldHVybiBhcyBub24tbnVsbCB2YWx1ZS5cbiAgICAgKiBAcGFyYW0ge1N0cmluZ30gW3RvdWNoQWN0aW9uPW5vbmVdIC0gQ1NTIHRvIHNldCB0b3VjaCBhY3Rpb24gc3R5bGUsIG5lZWRlZCB0byBwcmV2ZW50XG4gICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb2ludGVyIGNhbmNlbCBldmVudHMuIFVzZSBudWxsIGlmIHRoZVxuICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlIHRvdWNoIGFjdGlvbiBzaG91bGQgbm90IGJlIHNldC5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgZWxlbWVudCxcbiAgICAgICAgeyBzdG9wRXZlbnRzID0gJ2F1dG8nLCBjbGFpbUV2ZW50cyA9IHRydWUsIHVzZUNhcHR1cmUgPSB0cnVlLCB0b3VjaEFjdGlvbiA9ICdub25lJyB9ID0ge31cbiAgICApIHtcbiAgICAgICAgdGhpcy5vbkNhcHR1cmUgPSBudWxsXG4gICAgICAgIHRoaXMuZWxlbWVudCA9IGVsZW1lbnRcbiAgICAgICAgaWYgKHN0b3BFdmVudHMgPT09ICdhdXRvJykge1xuICAgICAgICAgICAgaWYgKENhcGFiaWxpdGllcy5pc1NhZmFyaSkge1xuICAgICAgICAgICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgICAgICAgICAgICAgICd0b3VjaG1vdmUnLFxuICAgICAgICAgICAgICAgICAgICBldmVudCA9PiB0aGlzLnByZXZlbnRQaW5jaChldmVudCksXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIHN0b3BFdmVudHMgPSBmYWxzZVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzdG9wRXZlbnRzID0gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuc3RvcEV2ZW50cyA9IHN0b3BFdmVudHNcbiAgICAgICAgdGhpcy5jbGFpbUV2ZW50cyA9IGNsYWltRXZlbnRzXG4gICAgICAgIGlmICh0b3VjaEFjdGlvbiAhPT0gbnVsbCkge1xuICAgICAgICAgICAgRWxlbWVudHMuc2V0U3R5bGUoZWxlbWVudCwgeyB0b3VjaEFjdGlvbiB9KVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuc2NhdHRlciA9IG5ldyBNYXAoKVxuICAgICAgICB0aGlzLmRlbGVnYXRlID0gbmV3IEludGVyYWN0aW9uTWFwcGVyKGVsZW1lbnQsIHRoaXMsIHtcbiAgICAgICAgICAgIHVzZUNhcHR1cmUsXG4gICAgICAgICAgICBtb3VzZVdoZWVsRWxlbWVudDogd2luZG93XG4gICAgICAgIH0pXG5cbiAgICAgICAgaWYgKHR5cGVvZiBkZWJ1Z0NhbnZhcyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZShkdCA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5zaG93VG91Y2hlcyhkdClcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzaG93VG91Y2hlcyhkdCkge1xuICAgICAgICBsZXQgcmVzb2x1dGlvbiA9IHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvXG4gICAgICAgIGxldCBjYW52YXMgPSBkZWJ1Z0NhbnZhc1xuICAgICAgICBsZXQgY3VycmVudCA9IHRoaXMuZGVsZWdhdGUuaW50ZXJhY3Rpb24uY3VycmVudFxuICAgICAgICBsZXQgY29udGV4dCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpXG4gICAgICAgIGxldCByYWRpdXMgPSAyMCAqIHJlc29sdXRpb25cbiAgICAgICAgY29udGV4dC5jbGVhclJlY3QoMCwgMCwgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0KVxuICAgICAgICBjb250ZXh0LmZpbGxTdHlsZSA9ICdyZ2JhKDAsIDAsIDAsIDAuMyknXG4gICAgICAgIGNvbnRleHQubGluZVdpZHRoID0gMlxuICAgICAgICBjb250ZXh0LnN0cm9rZVN0eWxlID0gJyMwMDMzMDAnXG4gICAgICAgIGZvciAobGV0IFtrZXksIHBvaW50XSBvZiBjdXJyZW50LmVudHJpZXMoKSkge1xuICAgICAgICAgICAgbGV0IGxvY2FsID0gcG9pbnRcbiAgICAgICAgICAgIGNvbnRleHQuYmVnaW5QYXRoKClcbiAgICAgICAgICAgIGNvbnRleHQuYXJjKFxuICAgICAgICAgICAgICAgIGxvY2FsLnggKiByZXNvbHV0aW9uLFxuICAgICAgICAgICAgICAgIGxvY2FsLnkgKiByZXNvbHV0aW9uLFxuICAgICAgICAgICAgICAgIHJhZGl1cyxcbiAgICAgICAgICAgICAgICAwLFxuICAgICAgICAgICAgICAgIDIgKiBNYXRoLlBJLFxuICAgICAgICAgICAgICAgIGZhbHNlXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBjb250ZXh0LmZpbGwoKVxuICAgICAgICAgICAgY29udGV4dC5zdHJva2UoKVxuICAgICAgICB9XG4gICAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZShkdCA9PiB7XG4gICAgICAgICAgICB0aGlzLnNob3dUb3VjaGVzKGR0KVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIHByZXZlbnRQaW5jaChldmVudCkge1xuICAgICAgICBldmVudCA9IGV2ZW50Lm9yaWdpbmFsRXZlbnQgfHwgZXZlbnRcbiAgICAgICAgaWYgKGV2ZW50LnNjYWxlICE9PSAxKSB7XG4gICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBhZGQoc2NhdHRlcikge1xuICAgICAgICB0aGlzLnNjYXR0ZXIuc2V0KHNjYXR0ZXIuZWxlbWVudCwgc2NhdHRlcilcbiAgICB9XG5cbiAgICBjYXB0dXJlKGV2ZW50KSB7XG4gICAgICAgIGlmICh0aGlzLm9uQ2FwdHVyZSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMub25DYXB0dXJlKGV2ZW50KVxuICAgICAgICB9XG4gICAgICAgIGlmIChldmVudC50YXJnZXQgPT0gdGhpcy5lbGVtZW50ICYmIHRoaXMuc3RvcEV2ZW50cykge1xuICAgICAgICAgICAgRXZlbnRzLnN0b3AoZXZlbnQpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG5cbiAgICBtYXBQb3NpdGlvblRvUG9pbnQocG9pbnQpIHtcbiAgICAgICAgcmV0dXJuIFBvaW50cy5mcm9tUGFnZVRvTm9kZSh0aGlzLmVsZW1lbnQsIHBvaW50KVxuICAgIH1cblxuICAgIGlzRGVzY2VuZGFudChwYXJlbnQsIGNoaWxkLCBjbGlja2FibGUgPSBmYWxzZSkge1xuICAgICAgICBpZiAocGFyZW50ID09IGNoaWxkKSByZXR1cm4gdHJ1ZVxuICAgICAgICBsZXQgbm9kZSA9IGNoaWxkLnBhcmVudE5vZGVcbiAgICAgICAgd2hpbGUgKG5vZGUgIT0gbnVsbCkge1xuICAgICAgICAgICAgaWYgKCFjbGlja2FibGUgJiYgbm9kZS5vbmNsaWNrKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAobm9kZSA9PSBwYXJlbnQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbm9kZSA9IG5vZGUucGFyZW50Tm9kZVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIGZpbmRUYXJnZXQoZXZlbnQsIGxvY2FsLCBnbG9iYWwpIHtcbiAgICAgICAgLyoqKiBOb3RlIHRoYXQgZWxlbWVudEZyb21Qb2ludCB3b3JrcyB3aXRoIGNsaWVudFgsIGNsaWVudFksIG5vdCBwYWdlWCwgcGFnZVlcbiAgICAgICAgVGhlIGltcG9ydGFudCBwb2ludCBpcyB0aGF0IGV2ZW50IHNob3VsZCBub3QgYmUgdXNlZCwgc2luY2UgdGhlIFRvdWNoRXZlbnRcbiAgICAgICAgcG9pbnRzIGFyZSBoaWRkZW4gaW4gc3ViIG9iamVjdHMuXG4gICAgICAgICoqKi9cbiAgICAgICAgbGV0IGZvdW5kID0gZG9jdW1lbnQuZWxlbWVudEZyb21Qb2ludChnbG9iYWwueCwgZ2xvYmFsLnkpXG4gICAgICAgIGZvciAobGV0IHRhcmdldCBvZiB0aGlzLnNjYXR0ZXIudmFsdWVzKCkpIHtcbiAgICAgICAgICAgIGlmICh0YXJnZXQuaW50ZXJhY3RpdmUgJiYgdGhpcy5pc0Rlc2NlbmRhbnQodGFyZ2V0LmVsZW1lbnQsIGZvdW5kKSkge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLnN0b3BFdmVudHMpIEV2ZW50cy5zdG9wKGV2ZW50KVxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmNsYWltRXZlbnRzKSBldmVudC5jbGFpbWVkQnlTY2F0dGVyID0gdGFyZ2V0XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRhcmdldFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgfVxuXG4gICAgZ2V0IGNlbnRlcigpIHtcbiAgICAgICAgbGV0IHIgPSB0aGlzLmJvdW5kc1xuICAgICAgICBsZXQgdzIgPSByLndpZHRoIC8gMlxuICAgICAgICBsZXQgaDIgPSByLmhlaWdodCAvIDJcbiAgICAgICAgcmV0dXJuIHsgeDogdzIsIHk6IGgyIH1cbiAgICB9XG5cbiAgICBnZXQgYm91bmRzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpXG4gICAgfVxuXG4gICAgZ2V0IHBvbHlnb24oKSB7XG4gICAgICAgIGxldCByID0gdGhpcy5ib3VuZHNcbiAgICAgICAgbGV0IHcyID0gci53aWR0aCAvIDJcbiAgICAgICAgbGV0IGgyID0gci5oZWlnaHQgLyAyXG4gICAgICAgIGxldCBjZW50ZXIgPSB7IHg6IHcyLCB5OiBoMiB9XG4gICAgICAgIGxldCBwb2x5Z29uID0gbmV3IFBvbHlnb24oY2VudGVyKVxuICAgICAgICBwb2x5Z29uLmFkZFBvaW50KHsgeDogLXcyLCB5OiAtaDIgfSlcbiAgICAgICAgcG9seWdvbi5hZGRQb2ludCh7IHg6IHcyLCB5OiAtaDIgfSlcbiAgICAgICAgcG9seWdvbi5hZGRQb2ludCh7IHg6IHcyLCB5OiBoMiB9KVxuICAgICAgICBwb2x5Z29uLmFkZFBvaW50KHsgeDogLXcyLCB5OiBoMiB9KVxuICAgICAgICByZXR1cm4gcG9seWdvblxuICAgIH1cbn1cblxuXG5leHBvcnQgY2xhc3MgRE9NU2NhdHRlciBleHRlbmRzIEFic3RyYWN0U2NhdHRlciB7XG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIGVsZW1lbnQsXG4gICAgICAgIGNvbnRhaW5lcixcbiAgICAgICAge1xuICAgICAgICAgICAgc3RhcnRTY2FsZSA9IDEuMCxcbiAgICAgICAgICAgIG1pblNjYWxlID0gMC4xLFxuICAgICAgICAgICAgbWF4U2NhbGUgPSAxLjAsXG4gICAgICAgICAgICBvdmVyZG9TY2FsaW5nID0gMS41LFxuICAgICAgICAgICAgYXV0b0JyaW5nVG9Gcm9udCA9IHRydWUsXG4gICAgICAgICAgICB0cmFuc2xhdGFibGUgPSB0cnVlLFxuICAgICAgICAgICAgc2NhbGFibGUgPSB0cnVlLFxuICAgICAgICAgICAgcm90YXRhYmxlID0gdHJ1ZSxcbiAgICAgICAgICAgIG1vdmFibGVYID0gdHJ1ZSxcbiAgICAgICAgICAgIG1vdmFibGVZID0gdHJ1ZSxcbiAgICAgICAgICAgIHJvdGF0aW9uRGVncmVlcyA9IG51bGwsXG4gICAgICAgICAgICByb3RhdGlvbiA9IG51bGwsXG4gICAgICAgICAgICBvblRyYW5zZm9ybSA9IG51bGwsXG4gICAgICAgICAgICB0cmFuc2Zvcm1PcmlnaW4gPSAnY2VudGVyIGNlbnRlcicsXG4gICAgICAgICAgICAvLyBleHRyYXMgd2hpY2ggYXJlIGluIHBhcnQgbmVlZGVkXG4gICAgICAgICAgICB4ID0gMCxcbiAgICAgICAgICAgIHkgPSAwLFxuICAgICAgICAgICAgd2lkdGggPSBudWxsLCAvLyByZXF1aXJlZFxuICAgICAgICAgICAgaGVpZ2h0ID0gbnVsbCwgIC8vIHJlcXVpcmVkXG4gICAgICAgICAgICByZXNpemFibGUgPSBmYWxzZSxcbiAgICAgICAgICAgIHNpbXVsYXRlQ2xpY2sgPSBmYWxzZSxcbiAgICAgICAgICAgIHZlcmJvc2UgPSB0cnVlLFxuICAgICAgICAgICAgb25SZXNpemUgPSBudWxsLFxuICAgICAgICAgICAgdG91Y2hBY3Rpb24gPSAnbm9uZScsXG4gICAgICAgICAgICB0aHJvd1Zpc2liaWxpdHkgPSA0NCxcbiAgICAgICAgICAgIHRocm93RGFtcGluZyA9IDAuOTUsXG4gICAgICAgICAgICBhdXRvVGhyb3cgPSB0cnVlLFxuICAgICAgICAgICAgc2NhbGVBdXRvQ2xvc2UgPSBmYWxzZSxcbiAgICAgICAgICAgIG9uQ2xvc2UgPSBudWxsLFxuICAgICAgICAgICAgc2NhbGVDbG9zZVRocmVzaG9sZCA9IDAuMTAsXG4gICAgICAgICAgICBzY2FsZUNsb3NlQnVmZmVyID0gMC4wNVxuICAgICAgICB9ID0ge31cbiAgICApIHtcbiAgICAgICAgc3VwZXIoe1xuICAgICAgICAgICAgbWluU2NhbGUsXG4gICAgICAgICAgICBtYXhTY2FsZSxcbiAgICAgICAgICAgIHN0YXJ0U2NhbGUsXG4gICAgICAgICAgICBvdmVyZG9TY2FsaW5nLFxuICAgICAgICAgICAgYXV0b0JyaW5nVG9Gcm9udCxcbiAgICAgICAgICAgIHRyYW5zbGF0YWJsZSxcbiAgICAgICAgICAgIHNjYWxhYmxlLFxuICAgICAgICAgICAgcm90YXRhYmxlLFxuICAgICAgICAgICAgbW92YWJsZVgsXG4gICAgICAgICAgICBtb3ZhYmxlWSxcbiAgICAgICAgICAgIHJlc2l6YWJsZSxcbiAgICAgICAgICAgIHJvdGF0aW9uRGVncmVlcyxcbiAgICAgICAgICAgIHJvdGF0aW9uLFxuICAgICAgICAgICAgb25UcmFuc2Zvcm0sXG4gICAgICAgICAgICB0aHJvd1Zpc2liaWxpdHksXG4gICAgICAgICAgICB0aHJvd0RhbXBpbmcsXG4gICAgICAgICAgICBhdXRvVGhyb3csXG4gICAgICAgICAgICBzY2FsZUF1dG9DbG9zZSxcbiAgICAgICAgICAgIHNjYWxlQ2xvc2VUaHJlc2hvbGQsXG4gICAgICAgICAgICBzY2FsZUNsb3NlQnVmZmVyLFxuICAgICAgICAgICAgb25DbG9zZVxuICAgICAgICB9KVxuICAgICAgICBpZiAoY29udGFpbmVyID09IG51bGwgfHwgd2lkdGggPT0gbnVsbCB8fCBoZWlnaHQgPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHZhbHVlOiBudWxsJylcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LnNjYXR0ZXIgPSB0aGlzXG4gICAgICAgIHRoaXMuZWxlbWVudCA9IGVsZW1lbnRcbiAgICAgICAgdGhpcy54ID0geFxuICAgICAgICB0aGlzLnkgPSB5XG4gICAgICAgIHRoaXMub2xkWCA9IDBcbiAgICAgICAgdGhpcy5vbGRZID0gMFxuICAgICAgICB0aGlzLm1lYW5YID0geFxuICAgICAgICB0aGlzLm1lYW5ZID0geVxuICAgICAgICB0aGlzLndpZHRoID0gd2lkdGhcbiAgICAgICAgdGhpcy5oZWlnaHQgPSBoZWlnaHRcbiAgICAgICAgdGhpcy50aHJvd1Zpc2liaWxpdHkgPSBNYXRoLm1pbih3aWR0aCwgaGVpZ2h0LCB0aHJvd1Zpc2liaWxpdHkpXG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gY29udGFpbmVyXG4gICAgICAgIHRoaXMuc2ltdWxhdGVDbGljayA9IHNpbXVsYXRlQ2xpY2tcbiAgICAgICAgdGhpcy5zY2FsZSA9IHN0YXJ0U2NhbGVcbiAgICAgICAgdGhpcy5yb3RhdGlvbkRlZ3JlZXMgPSB0aGlzLnN0YXJ0Um90YXRpb25EZWdyZWVzXG4gICAgICAgIHRoaXMudHJhbnNmb3JtT3JpZ2luID0gdHJhbnNmb3JtT3JpZ2luXG4gICAgICAgIHRoaXMuaW5pdGlhbFZhbHVlcyA9IHtcbiAgICAgICAgICAgIHg6IHgsXG4gICAgICAgICAgICB5OiB5LFxuICAgICAgICAgICAgd2lkdGg6IHdpZHRoLFxuICAgICAgICAgICAgaGVpZ2h0OiBoZWlnaHQsXG4gICAgICAgICAgICBzY2FsZTogc3RhcnRTY2FsZSxcbiAgICAgICAgICAgIHJvdGF0aW9uOiB0aGlzLnN0YXJ0Um90YXRpb25EZWdyZWVzLFxuICAgICAgICAgICAgdHJhbnNmb3JtT3JpZ2luOiB0cmFuc2Zvcm1PcmlnaW5cbiAgICAgICAgfVxuXG5cbiAgICAgICAgLy8gRm9yIHR3ZWVubGl0ZSB3ZSBuZWVkIGluaXRpYWwgdmFsdWVzIGluIF9nc1RyYW5zZm9ybVxuICAgICAgICBUd2VlbkxpdGUuc2V0KGVsZW1lbnQsIHRoaXMuaW5pdGlhbFZhbHVlcylcbiAgICAgICAgdGhpcy5vblJlc2l6ZSA9IG9uUmVzaXplXG4gICAgICAgIHRoaXMudmVyYm9zZSA9IHZlcmJvc2VcbiAgICAgICAgaWYgKHRvdWNoQWN0aW9uICE9PSBudWxsKSB7XG4gICAgICAgICAgICBFbGVtZW50cy5zZXRTdHlsZShlbGVtZW50LCB7IHRvdWNoQWN0aW9uIH0pXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5yZXNpemVCdXR0b24gPSBudWxsXG4gICAgICAgIGlmIChyZXNpemFibGUpIHtcbiAgICAgICAgICAgIGxldCBidXR0b24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpXG4gICAgICAgICAgICBidXR0b24uc3R5bGUucG9zaXRpb24gPSBcImFic29sdXRlXCJcbiAgICAgICAgICAgIGJ1dHRvbi5zdHlsZS5yaWdodCA9IFwiMHB4XCJcbiAgICAgICAgICAgIGJ1dHRvbi5zdHlsZS5ib3R0b20gPSBcIjBweFwiXG4gICAgICAgICAgICBidXR0b24uc3R5bGUud2lkdGggPSBcIjUwcHhcIjtcbiAgICAgICAgICAgIGJ1dHRvbi5zdHlsZS5oZWlnaHQgPSBcIjUwcHhcIjtcbiAgICAgICAgICAgIC8vIGJ1dHRvbi5zdHlsZS5ib3JkZXJSYWRpdXMgPSBcIjEwMCUgMHB4IDBweCAwcHhcIjtcbiAgICAgICAgICAgIC8vIGJ1dHRvbi5zdHlsZS5iYWNrZ3JvdW5kID0gdGhpcy5lbGVtZW50LnN0eWxlLmJhY2tncm91bmRDb2xvclxuICAgICAgICAgICAgYnV0dG9uLmNsYXNzTmFtZSA9IFwiaW50ZXJhY3RpdmVFbGVtZW50XCJcbiAgICAgICAgICAgIHRoaXMuZWxlbWVudC5hcHBlbmRDaGlsZChidXR0b24pXG5cbiAgICAgICAgICAgIGJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVyZG93bicsIChlKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5zdGFydFJlc2l6ZShlKVxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgYnV0dG9uLmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJtb3ZlJywgKGUpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnJlc2l6ZShlKVxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgYnV0dG9uLmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJ1cCcsIChlKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5zdG9wUmVzaXplKGUpXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgdGhpcy5yZXNpemVCdXR0b24gPSBidXR0b25cbiAgICAgICAgfVxuICAgICAgICBjb250YWluZXIuYWRkKHRoaXMpXG4gICAgfVxuXG4gICAgLyoqIFJldHVybnMgZ2VvbWV0cnkgZGF0YSBhcyBvYmplY3QuICoqL1xuICAgIGdldFN0YXRlKCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc2NhbGU6IHRoaXMuc2NhbGUsXG4gICAgICAgICAgICB4OiB0aGlzLngsXG4gICAgICAgICAgICB5OiB0aGlzLnksXG4gICAgICAgICAgICByb3RhdGlvbjogdGhpcy5yb3RhdGlvblxuICAgICAgICB9XG4gICAgfVxuXG4gICAgY2xvc2UoKSB7XG4gICAgICAgIHN1cGVyLmNsb3NlKClcbiAgICAgICAgbGV0IHBhcmVudCA9IHRoaXMuZWxlbWVudC5wYXJlbnROb2RlXG4gICAgICAgIGlmIChwYXJlbnQpIHBhcmVudC5yZW1vdmVDaGlsZCh0aGlzLmVsZW1lbnQpXG4gICAgfVxuXG4gICAgZ2V0IHJvdGF0aW9uT3JpZ2luKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jZW50ZXJcbiAgICB9XG5cbiAgICBnZXQgeCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3hcbiAgICB9XG5cbiAgICBnZXQgeSgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3lcbiAgICB9XG5cbiAgICBzZXQgeCh2YWx1ZSkge1xuICAgICAgICB0aGlzLl94ID0gdmFsdWVcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmVsZW1lbnQsIHsgeDogdmFsdWUgfSlcbiAgICB9XG5cbiAgICBzZXQgeSh2YWx1ZSkge1xuICAgICAgICB0aGlzLl95ID0gdmFsdWVcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmVsZW1lbnQsIHsgeTogdmFsdWUgfSlcbiAgICB9XG5cbiAgICBnZXQgcG9zaXRpb24oKSB7XG4gICAgICAgIGxldCB0cmFuc2Zvcm0gPSB0aGlzLmVsZW1lbnQuX2dzVHJhbnNmb3JtXG4gICAgICAgIGxldCB4ID0gdHJhbnNmb3JtLnhcbiAgICAgICAgbGV0IHkgPSB0cmFuc2Zvcm0ueVxuICAgICAgICByZXR1cm4geyB4LCB5IH1cbiAgICB9XG5cbiAgICBnZXQgb3JpZ2luKCkge1xuICAgICAgICBsZXQgcCA9IHRoaXMuZnJvbU5vZGVUb1BhZ2UoMCwgMClcbiAgICAgICAgcmV0dXJuIFBvaW50cy5mcm9tUGFnZVRvTm9kZSh0aGlzLmNvbnRhaW5lci5lbGVtZW50LCBwKVxuICAgIH1cblxuICAgIGdldCBib3VuZHMoKSB7XG4gICAgICAgIGxldCBzdGFnZSA9IHRoaXMuY29udGFpbmVyLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICAgICAgbGV0IHJlY3QgPSB0aGlzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHRvcDogcmVjdC50b3AgLSBzdGFnZS50b3AsXG4gICAgICAgICAgICBsZWZ0OiByZWN0LmxlZnQgLSBzdGFnZS5sZWZ0LFxuICAgICAgICAgICAgd2lkdGg6IHJlY3Qud2lkdGgsXG4gICAgICAgICAgICBoZWlnaHQ6IHJlY3QuaGVpZ2h0XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBnZXQgY2VudGVyKCkge1xuICAgICAgICBsZXQgciA9IHRoaXMuYm91bmRzXG4gICAgICAgIGxldCB3MiA9IHIud2lkdGggLyAyXG4gICAgICAgIGxldCBoMiA9IHIuaGVpZ2h0IC8gMlxuICAgICAgICAvLyAgIGlmICh0aGlzLnJlc2l6YWJsZSkge1xuICAgICAgICAvLyAgICAgICAgICAgICB3MiAqPSB0aGlzLnNjYWxlXG4gICAgICAgIC8vICAgICAgICAgICAgIGgyICo9IHRoaXMuc2NhbGVcbiAgICAgICAgLy8gICAgICAgICB9XG4gICAgICAgIHZhciB4ID0gci5sZWZ0ICsgdzJcbiAgICAgICAgdmFyIHkgPSByLnRvcCArIGgyXG4gICAgICAgIHJldHVybiB7IHgsIHkgfVxuICAgIH1cblxuICAgIHNldCByb3RhdGlvbihyYWRpYW5zKSB7XG4gICAgICAgIGxldCByYWQgPSByYWRpYW5zIC8vIEFuZ2xlLm5vcm1hbGl6ZShyYWRpYW5zKVxuICAgICAgICBsZXQgZGVncmVlcyA9IEFuZ2xlLnJhZGlhbjJkZWdyZWUocmFkKVxuICAgICAgICBUd2VlbkxpdGUuc2V0KHRoaXMuZWxlbWVudCwgeyByb3RhdGlvbjogZGVncmVlcyB9KVxuICAgICAgICB0aGlzLl9yb3RhdGlvbiA9IHJhZFxuICAgIH1cblxuICAgIHNldCByb3RhdGlvbkRlZ3JlZXMoZGVncmVlcykge1xuICAgICAgICBsZXQgZGVnID0gZGVncmVlcyAvLyBBbmdsZS5ub3JtYWxpemVEZWdyZWUoZGVncmVlcylcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmVsZW1lbnQsIHsgcm90YXRpb246IGRlZyB9KVxuICAgICAgICB0aGlzLl9yb3RhdGlvbiA9IEFuZ2xlLmRlZ3JlZTJyYWRpYW4oZGVnKVxuICAgIH1cblxuICAgIGdldCByb3RhdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3JvdGF0aW9uXG4gICAgfVxuXG4gICAgZ2V0IHJvdGF0aW9uRGVncmVlcygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3JvdGF0aW9uXG4gICAgfVxuXG4gICAgc2V0IHNjYWxlKHNjYWxlKSB7XG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7XG4gICAgICAgICAgICBzY2FsZTogc2NhbGUsXG4gICAgICAgICAgICB0cmFuc2Zvcm1PcmlnaW46IHRoaXMudHJhbnNmb3JtT3JpZ2luXG4gICAgICAgIH0pXG4gICAgICAgIHRoaXMuX3NjYWxlID0gc2NhbGVcbiAgICB9XG5cbiAgICBnZXQgc2NhbGUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9zY2FsZVxuICAgIH1cblxuICAgIGdldCBjb250YWluZXJCb3VuZHMoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnRhaW5lci5ib3VuZHNcbiAgICB9XG5cbiAgICBnZXQgY29udGFpbmVyUG9seWdvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29udGFpbmVyLnBvbHlnb25cbiAgICB9XG5cbiAgICBtYXBQb3NpdGlvblRvQ29udGFpbmVyUG9pbnQocG9pbnQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29udGFpbmVyLm1hcFBvc2l0aW9uVG9Qb2ludChwb2ludClcbiAgICB9XG5cbiAgICBjYXB0dXJlKGV2ZW50KSB7XG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgfVxuXG4gICAgcmVzZXQoKSB7XG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB0aGlzLmluaXRpYWxWYWx1ZXMpXG4gICAgfVxuXG4gICAgaGlkZSgpIHtcbiAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMuZWxlbWVudCwgMC4xLCB7XG4gICAgICAgICAgICBkaXNwbGF5OiAnbm9uZScsXG4gICAgICAgICAgICBvbkNvbXBsZXRlOiBlID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmVsZW1lbnQucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCh0aGlzLmVsZW1lbnQpXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgc2hvdygpIHtcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmVsZW1lbnQsIHsgZGlzcGxheTogJ2Jsb2NrJyB9KVxuICAgIH1cblxuICAgIHNob3dBdChwLCByb3RhdGlvbkRlZ3JlZXMpIHtcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmVsZW1lbnQsIHtcbiAgICAgICAgICAgIGRpc3BsYXk6ICdibG9jaycsXG4gICAgICAgICAgICB4OiBwLngsXG4gICAgICAgICAgICB5OiBwLnksXG4gICAgICAgICAgICByb3RhdGlvbjogcm90YXRpb25EZWdyZWVzLFxuICAgICAgICAgICAgdHJhbnNmb3JtT3JpZ2luOiB0aGlzLnRyYW5zZm9ybU9yaWdpblxuICAgICAgICB9KVxuICAgIH1cblxuICAgIGJyaW5nVG9Gcm9udCgpIHtcbiAgICAgICAgLy8gdGhpcy5lbGVtZW50LnBhcmVudE5vZGUuYXBwZW5kQ2hpbGQodGhpcy5lbGVtZW50KVxuICAgICAgICAvLyB1bzogT24gQ2hvbWUgYW5kIEVsZWN0b24gYXBwZW5kQ2hpbGQgbGVhZHMgdG8gZmxpY2tlclxuICAgICAgICBUd2VlbkxpdGUuc2V0KHRoaXMuZWxlbWVudCwgeyB6SW5kZXg6IERPTVNjYXR0ZXIuekluZGV4KysgfSlcbiAgICB9XG5cbiAgICB0b2dnbGVWaWRlbyhlbGVtZW50KSB7XG4gICAgICAgIGlmIChlbGVtZW50LnBhdXNlZCkge1xuICAgICAgICAgICAgZWxlbWVudC5wbGF5KClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGVsZW1lbnQucGF1c2UoKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25UYXAoZXZlbnQsIGludGVyYWN0aW9uLCBwb2ludCkge1xuICAgICAgICBpZiAodGhpcy5zaW11bGF0ZUNsaWNrKSB7XG4gICAgICAgICAgICBsZXQgcCA9IFBvaW50cy5mcm9tUGFnZVRvTm9kZSh0aGlzLmVsZW1lbnQsIHBvaW50KVxuICAgICAgICAgICAgbGV0IGlmcmFtZSA9IHRoaXMuZWxlbWVudC5xdWVyeVNlbGVjdG9yKCdpZnJhbWUnKVxuICAgICAgICAgICAgaWYgKGlmcmFtZSkge1xuICAgICAgICAgICAgICAgIGxldCBkb2MgPSBpZnJhbWUuY29udGVudFdpbmRvdy5kb2N1bWVudFxuICAgICAgICAgICAgICAgIGxldCBlbGVtZW50ID0gZG9jLmVsZW1lbnRGcm9tUG9pbnQocC54LCBwLnkpXG4gICAgICAgICAgICAgICAgaWYgKGVsZW1lbnQgPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgc3dpdGNoIChlbGVtZW50LnRhZ05hbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgY2FzZSAnVklERU8nOlxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coZWxlbWVudC5jdXJyZW50U3JjKVxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKFBvcHVwTWVudSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBvcHVwTWVudS5vcGVuKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGdWxsc2NyZWVuOiAoKSA9PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpbmRvdy5vcGVuKGVsZW1lbnQuY3VycmVudFNyYyksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQbGF5OiAoKSA9PiBlbGVtZW50LnBsYXkoKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7IHgsIHkgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy50b2dnbGVWaWRlbyhlbGVtZW50KVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgICAgIGVsZW1lbnQuY2xpY2soKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGlzRGVzY2VuZGFudChwYXJlbnQsIGNoaWxkKSB7XG4gICAgICAgIGxldCBub2RlID0gY2hpbGQucGFyZW50Tm9kZVxuICAgICAgICB3aGlsZSAobm9kZSAhPSBudWxsKSB7XG4gICAgICAgICAgICBpZiAobm9kZSA9PSBwYXJlbnQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbm9kZSA9IG5vZGUucGFyZW50Tm9kZVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIGZyb21QYWdlVG9Ob2RlKHgsIHkpIHtcbiAgICAgICAgcmV0dXJuIFBvaW50cy5mcm9tUGFnZVRvTm9kZSh0aGlzLmVsZW1lbnQsIHsgeCwgeSB9KVxuICAgIH1cblxuICAgIGZyb21Ob2RlVG9QYWdlKHgsIHkpIHtcbiAgICAgICAgcmV0dXJuIFBvaW50cy5mcm9tTm9kZVRvUGFnZSh0aGlzLmVsZW1lbnQsIHsgeCwgeSB9KVxuICAgIH1cblxuICAgIF9tb3ZlKGRlbHRhKSB7XG4gICAgICAgIC8vIFVPOiBXZSBuZWVkIHRvIGtlZXAgVHdlZW5MaXRlJ3MgX2dzVHJhbnNmb3JtIGFuZCB0aGUgcHJpdmF0ZVxuICAgICAgICAvLyBfeCBhbmQgX3kgYXR0cmlidXRlcyBhbGlnbmVkXG4gICAgICAgIGxldCB4ID0gdGhpcy5lbGVtZW50Ll9nc1RyYW5zZm9ybS54XG4gICAgICAgIGxldCB5ID0gdGhpcy5lbGVtZW50Ll9nc1RyYW5zZm9ybS55XG4gICAgICAgIGlmICh0aGlzLm1vdmFibGVYKSB7XG4gICAgICAgICAgICB4ICs9IGRlbHRhLnhcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5tb3ZhYmxlWSkge1xuICAgICAgICAgICAgeSArPSBkZWx0YS55XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5feCA9IHhcbiAgICAgICAgdGhpcy5feSA9IHlcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmVsZW1lbnQsIHsgeDogeCwgeTogeSB9KVxuICAgIH1cblxuICAgIHJlc2l6ZUFmdGVyVHJhbnNmb3JtKHpvb20pIHtcbiAgICAgICAgLy8gIGxldCB3ID0gdGhpcy53aWR0aCAqIHRoaXMuc2NhbGVcbiAgICAgICAgLy8gICAgICAgICBsZXQgaCA9IHRoaXMuaGVpZ2h0ICogdGhpcy5zY2FsZVxuICAgICAgICAvLyAgICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7IHdpZHRoOiB3LCBoZWlnaHQ6IGggfSlcbiAgICAgICAgaWYgKHRoaXMub25SZXNpemUpIHtcbiAgICAgICAgICAgIGxldCBldmVudCA9IG5ldyBSZXNpemVFdmVudCh0aGlzLCB7IHdpZHRoOiB3LCBoZWlnaHQ6IGggfSlcbiAgICAgICAgICAgIHRoaXMub25SZXNpemUoZXZlbnQpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMucmVzaXplQnV0dG9uICE9IG51bGwpIHtcbiAgICAgICAgICAgIC8vIHRoaXMucmVzaXplQnV0dG9uLnN0eWxlLndpZHRoID0gNTAvdGhpcy5zY2FsZStcInB4XCJcbiAgICAgICAgICAgIC8vIHRoaXMucmVzaXplQnV0dG9uLnN0eWxlLmhlaWdodCA9IDUwL3RoaXMuc2NhbGUrXCJweFwiXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzdGFydFJlc2l6ZShlKSB7XG4gICAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgICBsZXQgZXZlbnQgPSBuZXcgQ3VzdG9tRXZlbnQoJ3Jlc2l6ZVN0YXJ0ZWQnKVxuXG4gICAgICAgIGxldCBvbGRQb3N0aXRpb24gPSB7IHg6ICQodGhpcy5lbGVtZW50KS5wb3NpdGlvbigpLmxlZnQsIHk6ICQodGhpcy5lbGVtZW50KS5wb3NpdGlvbigpLnRvcCB9XG4gICAgICAgIHRoaXMuYnJpbmdUb0Zyb250KClcblxuICAgICAgICB0aGlzLmVsZW1lbnQuc3R5bGUudHJhbnNmb3JtT3JpZ2luID0gXCIwJSAwJVwiXG5cbiAgICAgICAgbGV0IG5ld1Bvc3RpdGlvbiA9IHsgeDogJCh0aGlzLmVsZW1lbnQpLnBvc2l0aW9uKCkubGVmdCwgeTogJCh0aGlzLmVsZW1lbnQpLnBvc2l0aW9uKCkudG9wIH1cblxuICAgICAgICBsZXQgb2Zmc2V0ID0gUG9pbnRzLnN1YnRyYWN0KG9sZFBvc3RpdGlvbiwgbmV3UG9zdGl0aW9uKVxuXG4gICAgICAgIHRoaXMub2xkWCA9IGUuY2xpZW50WFxuICAgICAgICB0aGlzLm9sZFkgPSBlLmNsaWVudFlcblxuICAgICAgICBlLnRhcmdldC5zZXRBdHRyaWJ1dGUoJ3Jlc2l6aW5nJywgXCJ0cnVlXCIpXG4gICAgICAgIGUudGFyZ2V0LnNldFBvaW50ZXJDYXB0dXJlKGUucG9pbnRlcklkKVxuXG4gICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmVsZW1lbnQsIDAsIHsgY3NzOiB7IGxlZnQ6IFwiKz1cIiArIG9mZnNldC54ICsgXCJweFwiIH0gfSlcbiAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMuZWxlbWVudCwgMCwgeyBjc3M6IHsgdG9wOiBcIis9XCIgKyBvZmZzZXQueSArIFwicHhcIiB9IH0pXG5cbiAgICAgICAgdGhpcy5lbGVtZW50LmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICAgIH1cblxuICAgIHJlc2l6ZShlKSB7XG4gICAgICAgIGUucHJldmVudERlZmF1bHQoKVxuXG4gICAgICAgIGxldCByb3RhdGlvbiA9IEFuZ2xlLnJhZGlhbjJkZWdyZWUodGhpcy5yb3RhdGlvbilcbiAgICAgICAgcm90YXRpb24gPSAocm90YXRpb24gKyAzNjApICUgMzYwXG4gICAgICAgIGxldCBldmVudCA9IG5ldyBDdXN0b21FdmVudCgncmVzaXplZCcpXG4gICAgICAgIGlmIChlLnRhcmdldC5nZXRBdHRyaWJ1dGUoJ3Jlc2l6aW5nJykgPT0gXCJ0cnVlXCIpIHtcblxuICAgICAgICAgICAgbGV0IGRlbHRhWCA9IChlLmNsaWVudFggLSB0aGlzLm9sZFgpXG4gICAgICAgICAgICBsZXQgZGVsdGFZID0gKGUuY2xpZW50WSAtIHRoaXMub2xkWSlcblxuICAgICAgICAgICAgbGV0IHIgPSBNYXRoLnNxcnQoTWF0aC5wb3coZGVsdGFYLCAyKSArIE1hdGgucG93KGRlbHRhWSwgMikpXG4gICAgICAgICAgICBsZXQgcGhpID0gQW5nbGUucmFkaWFuMmRlZ3JlZShNYXRoLmF0YW4yKGRlbHRhWCwgZGVsdGFZKSlcblxuICAgICAgICAgICAgcGhpID0gKChwaGkpICsgNjMwKSAlIDM2MFxuICAgICAgICAgICAgbGV0IHJvdCA9ICgocm90YXRpb24gKyA5MCkgKyA2MzApICUgMzYwXG5cbiAgICAgICAgICAgIGxldCBkaWZmQW5nbGUgPSAoKDAgKyByb3QpICsgMzYwKSAlIDM2MFxuICAgICAgICAgICAgbGV0IHBoaUNvcnJlY3RlZCA9IChwaGkgKyBkaWZmQW5nbGUgKyAzNjApICUgMzYwXG5cbiAgICAgICAgICAgIGxldCByZXNpemVXID0gciAqIE1hdGguY29zKEFuZ2xlLmRlZ3JlZTJyYWRpYW4ocGhpQ29ycmVjdGVkKSlcbiAgICAgICAgICAgIGxldCByZXNpemVIID0gLXIgKiBNYXRoLnNpbihBbmdsZS5kZWdyZWUycmFkaWFuKHBoaUNvcnJlY3RlZCkpXG5cbiAgICAgICAgICAgIGlmICh0aGlzLmVsZW1lbnQub2Zmc2V0V2lkdGggKyByZXNpemVXIC8gdGhpcy5zY2FsZSA+IHRoaXMud2lkdGggKiAwLjMgJiYgdGhpcy5lbGVtZW50Lm9mZnNldEhlaWdodCArIHJlc2l6ZUggLyB0aGlzLnNjYWxlID4gdGhpcy5oZWlnaHQgKiAwLjMpIFR3ZWVuTGl0ZS50byh0aGlzLmVsZW1lbnQsIDAsIHsgd2lkdGg6IHRoaXMuZWxlbWVudC5vZmZzZXRXaWR0aCArIHJlc2l6ZVcgLyB0aGlzLnNjYWxlLCBoZWlnaHQ6IHRoaXMuZWxlbWVudC5vZmZzZXRIZWlnaHQgKyByZXNpemVIIC8gdGhpcy5zY2FsZSB9KTtcblxuICAgICAgICAgICAgdGhpcy5vbGRYID0gZS5jbGllbnRYXG4gICAgICAgICAgICB0aGlzLm9sZFkgPSBlLmNsaWVudFlcbiAgICAgICAgICAgIHRoaXMub25SZXNpemluZygpXG5cbiAgICAgICAgICAgIHRoaXMuZWxlbWVudC5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0b3BSZXNpemUoZSkge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcblxuICAgICAgICBsZXQgZXZlbnQgPSBuZXcgQ3VzdG9tRXZlbnQoJ3Jlc2l6ZUVuZGVkJylcbiAgICAgICAgbGV0IG9sZFBvc3RpdGlvbiA9IHsgeDogJCh0aGlzLmVsZW1lbnQpLnBvc2l0aW9uKCkubGVmdCwgeTogJCh0aGlzLmVsZW1lbnQpLnBvc2l0aW9uKCkudG9wIH1cbiAgICAgICAgdGhpcy5lbGVtZW50LnN0eWxlLnRyYW5zZm9ybU9yaWdpbiA9IFwiNTAlIDUwJVwiXG4gICAgICAgIGxldCBuZXdQb3N0aXRpb24gPSB7IHg6ICQodGhpcy5lbGVtZW50KS5wb3NpdGlvbigpLmxlZnQsIHk6ICQodGhpcy5lbGVtZW50KS5wb3NpdGlvbigpLnRvcCB9XG4gICAgICAgIGxldCBvZmZzZXQgPSBQb2ludHMuc3VidHJhY3Qob2xkUG9zdGl0aW9uLCBuZXdQb3N0aXRpb24pXG5cbiAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMuZWxlbWVudCwgMCwgeyBjc3M6IHsgbGVmdDogXCIrPVwiICsgb2Zmc2V0LnggKyBcInB4XCIgfSB9KVxuICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5lbGVtZW50LCAwLCB7IGNzczogeyB0b3A6IFwiKz1cIiArIG9mZnNldC55ICsgXCJweFwiIH0gfSlcblxuICAgICAgICBlLnRhcmdldC5zZXRBdHRyaWJ1dGUoJ3Jlc2l6aW5nJywgXCJmYWxzZVwiKVxuXG4gICAgICAgIHRoaXMuZWxlbWVudC5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICB9XG59XG5cbkRPTVNjYXR0ZXIuekluZGV4ID0gMTAwMFxuIiwiaW1wb3J0IHsgZ2V0SWQgfSBmcm9tICcuL3V0aWxzLmpzJ1xuaW1wb3J0IHsgRE9NU2NhdHRlciB9IGZyb20gJy4vc2NhdHRlci5qcydcbmltcG9ydCB7IEludGVyYWN0aW9uTWFwcGVyIH0gZnJvbSAnLi9pbnRlcmFjdGlvbi5qcydcblxuZXhwb3J0IGNsYXNzIENhcmRMb2FkZXIge1xuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBzcmMsXG4gICAgICAgIHtcbiAgICAgICAgICAgIHggPSAwLFxuICAgICAgICAgICAgeSA9IDAsXG4gICAgICAgICAgICB3aWR0aCA9IDEwMDAsXG4gICAgICAgICAgICBoZWlnaHQgPSA4MDAsXG4gICAgICAgICAgICBtYXhXaWR0aCA9IG51bGwsXG4gICAgICAgICAgICBtYXhIZWlnaHQgPSBudWxsLFxuICAgICAgICAgICAgc2NhbGUgPSAxLFxuICAgICAgICAgICAgbWluU2NhbGUgPSAwLjUsXG4gICAgICAgICAgICBtYXhTY2FsZSA9IDEuNSxcbiAgICAgICAgICAgIHJvdGF0aW9uID0gMFxuICAgICAgICB9ID0ge31cbiAgICApIHtcbiAgICAgICAgdGhpcy5zcmMgPSBzcmNcbiAgICAgICAgdGhpcy54ID0geFxuICAgICAgICB0aGlzLnkgPSB5XG4gICAgICAgIHRoaXMuc2NhbGUgPSBzY2FsZVxuICAgICAgICB0aGlzLnJvdGF0aW9uID0gMFxuICAgICAgICB0aGlzLm1heFNjYWxlID0gbWF4U2NhbGVcbiAgICAgICAgdGhpcy5taW5TY2FsZSA9IG1pblNjYWxlXG4gICAgICAgIHRoaXMud2FudGVkV2lkdGggPSB3aWR0aFxuICAgICAgICB0aGlzLndhbnRlZEhlaWdodCA9IGhlaWdodFxuICAgICAgICB0aGlzLm1heFdpZHRoID0gbWF4V2lkdGggIT0gbnVsbCA/IG1heFdpZHRoIDogd2luZG93LmlubmVyV2lkdGhcbiAgICAgICAgdGhpcy5tYXhIZWlnaHQgPSBtYXhIZWlnaHQgIT0gbnVsbCA/IG1heEhlaWdodCA6IHdpbmRvdy5pbm5lckhlaWdodFxuICAgICAgICB0aGlzLmFkZGVkTm9kZSA9IG51bGxcbiAgICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICAgXG4gICAgICAgICAgICB3aWR0aCxcbiAgICAgICAgICAgIGhlaWdodCxcbiAgICAgICAgICAgIG1heFdpZHRoLFxuICAgICAgICAgICAgbWF4SGVpZ2h0LFxuICAgICAgICAgICBcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICB1bmxvYWQoKSB7XG4gICAgICAgIGlmICh0aGlzLmFkZGVkTm9kZSkge1xuICAgICAgICAgICAgdGhpcy5hZGRlZE5vZGUucmVtb3ZlKClcbiAgICAgICAgICAgIHRoaXMuYWRkZWROb2RlID0gbnVsbFxuICAgICAgICB9XG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgUERGTG9hZGVyIGV4dGVuZHMgQ2FyZExvYWRlciB7XG4gICAgY29uc3RydWN0b3Ioc3JjLCB7IHdpZHRoID0gMTY0MCwgaGVpZ2h0ID0gODAwLCBzY2FsZSA9IDEgfSA9IHt9KSB7XG4gICAgICAgIHN1cGVyKHNyYywgeyB3aWR0aCwgaGVpZ2h0LCBzY2FsZSB9KVxuICAgICAgICBpZiAodHlwZW9mIFBERkpTID09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICBhbGVydCgnUERGLmpzIG5lZWRlZCcpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBsb2FkKGRvbU5vZGUpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIFBERkpTLmdldERvY3VtZW50KHRoaXMuc3JjKS50aGVuKHBkZiA9PiB7XG4gICAgICAgICAgICAgICAgcGRmLmdldFBhZ2UoMSkudGhlbihwYWdlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IHNjYWxlID0gdGhpcy5zY2FsZSAqIGFwcC5yZW5kZXJlci5yZXNvbHV0aW9uXG4gICAgICAgICAgICAgICAgICAgIGxldCBpbnZTY2FsZSA9IDEgLyBzY2FsZVxuICAgICAgICAgICAgICAgICAgICBsZXQgdmlld3BvcnQgPSBwYWdlLmdldFZpZXdwb3J0KHNjYWxlKVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIFByZXBhcmUgY2FudmFzIHVzaW5nIFBERiBwYWdlIGRpbWVuc2lvbnMuXG4gICAgICAgICAgICAgICAgICAgIGxldCBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKVxuICAgICAgICAgICAgICAgICAgICBsZXQgY29udGV4dCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpXG4gICAgICAgICAgICAgICAgICAgIGNhbnZhcy5oZWlnaHQgPSB2aWV3cG9ydC5oZWlnaHRcbiAgICAgICAgICAgICAgICAgICAgY2FudmFzLndpZHRoID0gdmlld3BvcnQud2lkdGhcblxuICAgICAgICAgICAgICAgICAgICAvLyBSZW5kZXIgUERGIHBhZ2UgaW50byBjYW52YXMgY29udGV4dC5cbiAgICAgICAgICAgICAgICAgICAgbGV0IHJlbmRlckNvbnRleHQgPSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYW52YXNDb250ZXh0OiBjb250ZXh0LFxuICAgICAgICAgICAgICAgICAgICAgICAgdmlld3BvcnQ6IHZpZXdwb3J0XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgcGFnZS5yZW5kZXIocmVuZGVyQ29udGV4dClcbiAgICAgICAgICAgICAgICAgICAgZG9tTm9kZS5hcHBlbmRDaGlsZChjYW52YXMpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMud2FudGVkV2lkdGggPSBjYW52YXMud2lkdGhcbiAgICAgICAgICAgICAgICAgICAgdGhpcy53YW50ZWRIZWlnaHQgPSBjYW52YXMuaGVpZ2h0XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2NhbGUgPSBpbnZTY2FsZVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmFkZGVkTm9kZSA9IGNhbnZhc1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHRoaXMpXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH0pXG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgSW1hZ2VMb2FkZXIgZXh0ZW5kcyBDYXJkTG9hZGVyIHtcbiAgICBsb2FkKGRvbU5vZGUpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGxldCBpc0ltYWdlID0gZG9tTm9kZSBpbnN0YW5jZW9mIEhUTUxJbWFnZUVsZW1lbnRcbiAgICAgICAgICAgIGxldCBpbWFnZSA9IGlzSW1hZ2UgPyBkb21Ob2RlIDogZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJylcbiAgICAgICAgICAgIGltYWdlLm9ubG9hZCA9IGUgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghaXNJbWFnZSkge1xuICAgICAgICAgICAgICAgICAgICBkb21Ob2RlLmFwcGVuZENoaWxkKGltYWdlKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmFkZGVkTm9kZSA9IGltYWdlXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMud2FudGVkV2lkdGggPSBpbWFnZS5uYXR1cmFsV2lkdGhcbiAgICAgICAgICAgICAgICB0aGlzLndhbnRlZEhlaWdodCA9IGltYWdlLm5hdHVyYWxIZWlnaHRcblxuICAgICAgICAgICAgICAgIGxldCBzY2FsZVcgPSB0aGlzLm1heFdpZHRoIC8gaW1hZ2UubmF0dXJhbFdpZHRoXG4gICAgICAgICAgICAgICAgbGV0IHNjYWxlSCA9IHRoaXMubWF4SGVpZ2h0IC8gaW1hZ2UubmF0dXJhbEhlaWdodFxuICAgICAgICAgICAgICAgIHRoaXMuc2NhbGUgPSBNYXRoLm1pbih0aGlzLm1heFNjYWxlLCBNYXRoLm1pbihzY2FsZVcsIHNjYWxlSCkpXG4gICAgICAgICAgICAgICAgaW1hZ2Uuc2V0QXR0cmlidXRlKCdkcmFnZ2FibGUnLCBmYWxzZSlcbiAgICAgICAgICAgICAgICBpbWFnZS53aWR0aCA9IGltYWdlLm5hdHVyYWxXaWR0aFxuICAgICAgICAgICAgICAgIGltYWdlLmhlaWdodCA9IGltYWdlLm5hdHVyYWxIZWlnaHRcbiAgICAgICAgICAgICAgICByZXNvbHZlKHRoaXMpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpbWFnZS5vbmVycm9yID0gZSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KHRoaXMpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpbWFnZS5zcmMgPSB0aGlzLnNyY1xuICAgICAgICB9KVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIEZyYW1lTG9hZGVyIGV4dGVuZHMgQ2FyZExvYWRlciB7XG4gICAgbG9hZChkb21Ob2RlKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBsZXQgaXNGcmFtZSA9IGRvbU5vZGUgaW5zdGFuY2VvZiBIVE1MSUZyYW1lRWxlbWVudFxuICAgICAgICAgICAgbGV0IGlmcmFtZSA9IGlzRnJhbWUgPyBkb21Ob2RlIDogZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaWZyYW1lJylcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdGcmFtZUxvYWRlci5sb2FkJywgaXNGcmFtZSwgaWZyYW1lLCB0aGlzLnNyYylcbiAgICAgICAgICAgIGlmcmFtZS5mcmFtZUJvcmRlciA9IDBcbiAgICAgICAgICAgIGlmcmFtZS5zdHlsZS5zY3JvbGxpbmcgPSBmYWxzZVxuICAgICAgICAgICAgaWZyYW1lLndpZHRoID0gdGhpcy53YW50ZWRXaWR0aFxuICAgICAgICAgICAgaWZyYW1lLmhlaWdodCA9IHRoaXMud2FudGVkSGVpZ2h0XG4gICAgICAgICAgICBpZiAoIWlzRnJhbWUpIHtcbiAgICAgICAgICAgICAgICAvLyBVbmxpa2UgaW1nIG9ubG9hZCBpcyBvbmx5IHRyaWdnZXJlZCBpZiB0aGUgaWZyYW1lIGlzIHBhcnQgb2YgdGhlIERPTSB0cmVlXG4gICAgICAgICAgICAgICAgZG9tTm9kZS5hcHBlbmRDaGlsZChpZnJhbWUpXG4gICAgICAgICAgICAgICAgdGhpcy5hZGRlZE5vZGUgPSBpZnJhbWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmcmFtZS5vbmxvYWQgPSBlID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHRoaXMpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZnJhbWUub25lcnJvciA9IGUgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdCh0aGlzKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWZyYW1lLnNyYyA9IHRoaXMuc3JjXG4gICAgICAgIH0pXG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgSFRNTExvYWRlciBleHRlbmRzIENhcmRMb2FkZXIge1xuICAgIGxvYWQoZG9tTm9kZSkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgbGV0IHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpXG4gICAgICAgICAgICB4aHIub3BlbignR0VUJywgdGhpcy5zcmMsIGZhbHNlKVxuICAgICAgICAgICAgeGhyLm9ubG9hZCA9IGUgPT4ge1xuICAgICAgICAgICAgICAgIGRvbU5vZGUuaW5uZXJIVE1MID0geGhyLnJlc3BvbnNlXG4gICAgICAgICAgICAgICAgdGhpcy5hZGRlZE5vZGUgPSBkb21Ob2RlLmZpcnN0RWxlbWVudENoaWxkXG4gICAgICAgICAgICAgICAgbGV0IHsgd2lkdGgsIGhlaWdodCB9ID0gdGhpcy5zaXplKHRoaXMuYWRkZWROb2RlKVxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiSFRNTExvYWRlci5sb2FkXCIsIHsgYWRkZWQ6IHRoaXMuYWRkZWROb2RlLCB3aWR0aCwgaGVpZ2h0IH0pXG4gICAgICAgICAgICAgICAgaWYgKHdpZHRoKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLndhbnRlZFdpZHRoID0gd2lkdGggfHwgdGhpcy53YW50ZWRXaWR0aFxuICAgICAgICAgICAgICAgIGlmIChoZWlnaHQpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMud2FudGVkSGVpZ2h0ID0gaGVpZ2h0IHx8IHRoaXMud2FudGVkSGVpZ2h0XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh0aGlzKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgeGhyLm9uZXJyb3IgPSBlID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QodGhpcylcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHhoci5zZW5kKClcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBUcmllcyB0byBkZXRlcm1pbmUgdGhlIHNpemUgb2YgdGhlIGFkZGVkTm9kZS5cbiAgICAgKiBDaGVja3MgZm9yIGV4cGxpY2l0IHdpZHRoIGFuZCBoZWlnaHQgc3R5bGUgYXR0cmlidXRlcy5cbiAgICAgKiBcbiAgICAgKiBPdmVyd3JpdGUgdGhpcyBtZXRob2QgaWYgeW91IHdhbnQgdG8gZXh0cmFjdCB2YWx1ZXMgZnJvbSBvdGhlciBpbmZvcy5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHsgd2lkdGg6IGludCwgaGVpZ2h0OiBpbnQgfVxuICAgICAqIEBtZW1iZXJvZiBIVE1MTG9hZGVyXG4gICAgICovXG4gICAgc2l6ZShub2RlKSB7XG4gICAgICAgIGxldCB3aWR0aCA9IHBhcnNlSW50KG5vZGUuc3R5bGUud2lkdGgpIHx8IG51bGxcbiAgICAgICAgbGV0IGhlaWdodCA9IHBhcnNlSW50KG5vZGUuc3R5bGUuaGVpZ2h0KSB8fCBudWxsXG4gICAgICAgIHJldHVybiB7IHdpZHRoLCBoZWlnaHQgfVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIERPTUZsaXAge1xuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBkb21TY2F0dGVyQ29udGFpbmVyLFxuICAgICAgICBmbGlwVGVtcGxhdGUsXG4gICAgICAgIGZyb250TG9hZGVyLFxuICAgICAgICBiYWNrTG9hZGVyLFxuICAgICAgICB7XG4gICAgICAgICAgICBjbG9zZU9uTWluU2NhbGUgPSBmYWxzZSxcbiAgICAgICAgICAgIGZsaXBEdXJhdGlvbiA9IDEsXG4gICAgICAgICAgICBmYWRlRHVyYXRpb24gPSAwLjIsXG4gICAgICAgICAgICBvdmVyZG9TY2FsaW5nID0gMSxcbiAgICAgICAgICAgIGF1dG9Mb2FkID0gZmFsc2UsXG4gICAgICAgICAgICBjZW50ZXIgPSBudWxsLFxuICAgICAgICAgICAgcHJlbG9hZEJhY2sgPSBmYWxzZSxcbiAgICAgICAgICAgIHRyYW5zbGF0YWJsZSA9IHRydWUsXG4gICAgICAgICAgICBzY2FsYWJsZSA9IHRydWUsXG4gICAgICAgICAgICByb3RhdGFibGUgPSB0cnVlLFxuICAgICAgICAgICAgb25Gcm9udCA9IG51bGwsXG4gICAgICAgICAgICBvbkJhY2sgPSBudWxsLFxuICAgICAgICAgICAgb25DbG9zZSA9IG51bGwsXG4gICAgICAgICAgICBvblVwZGF0ZSA9IG51bGwsXG4gICAgICAgICAgICBvblJlbW92ZWQgPSBudWxsLFxuICAgICAgICAgICAgb25Mb2FkZWQgPSBudWxsXG4gICAgICAgIH0gPSB7fVxuICAgICkge1xuICAgICAgICB0aGlzLmRvbVNjYXR0ZXJDb250YWluZXIgPSBkb21TY2F0dGVyQ29udGFpbmVyXG4gICAgICAgIHRoaXMuaWQgPSBnZXRJZCgpXG4gICAgICAgIHRoaXMuZmxpcER1cmF0aW9uID0gZmxpcER1cmF0aW9uXG4gICAgICAgIHRoaXMuZmFkZUR1cmF0aW9uID0gZmFkZUR1cmF0aW9uXG4gICAgICAgIHRoaXMuY2xvc2VPbk1pblNjYWxlID0gY2xvc2VPbk1pblNjYWxlXG4gICAgICAgIHRoaXMuZmxpcFRlbXBsYXRlID0gZmxpcFRlbXBsYXRlXG4gICAgICAgIHRoaXMuZnJvbnRMb2FkZXIgPSBmcm9udExvYWRlclxuICAgICAgICB0aGlzLmJhY2tMb2FkZXIgPSBiYWNrTG9hZGVyXG4gICAgICAgIHRoaXMudHJhbnNsYXRhYmxlID0gdHJhbnNsYXRhYmxlXG4gICAgICAgIHRoaXMuc2NhbGFibGUgPSBzY2FsYWJsZVxuICAgICAgICB0aGlzLnJvdGF0YWJsZSA9IHJvdGF0YWJsZVxuICAgICAgICB0aGlzLm9uRnJvbnRGbGlwcGVkID0gb25Gcm9udFxuICAgICAgICB0aGlzLm9uQmFja0ZsaXBwZWQgPSBvbkJhY2tcbiAgICAgICAgdGhpcy5vbkNsb3NlID0gb25DbG9zZVxuICAgICAgICB0aGlzLm9uUmVtb3ZlZCA9IG9uUmVtb3ZlZFxuICAgICAgICB0aGlzLm9uVXBkYXRlID0gb25VcGRhdGVcbiAgICAgICAgdGhpcy5vbkxvYWRlZCA9IG9uTG9hZGVkXG4gICAgICAgIHRoaXMuY2VudGVyID0gY2VudGVyXG4gICAgICAgIHRoaXMucHJlbG9hZEJhY2sgPSBwcmVsb2FkQmFja1xuICAgICAgICB0aGlzLm92ZXJkb1NjYWxpbmcgPSBvdmVyZG9TY2FsaW5nXG4gICAgICAgIGlmIChhdXRvTG9hZCkge1xuICAgICAgICAgICAgdGhpcy5sb2FkKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGxvYWQoKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBsZXQgdCA9IHRoaXMuZmxpcFRlbXBsYXRlXG4gICAgICAgICAgICBsZXQgZG9tID0gdGhpcy5kb21TY2F0dGVyQ29udGFpbmVyLmVsZW1lbnRcbiAgICAgICAgICAgIGxldCB3cmFwcGVyID0gdC5jb250ZW50LnF1ZXJ5U2VsZWN0b3IoJy5mbGlwV3JhcHBlcicpXG4gICAgICAgICAgICB3cmFwcGVyLmlkID0gdGhpcy5pZFxuICAgICAgICAgICAgbGV0IGNsb25lID0gZG9jdW1lbnQuaW1wb3J0Tm9kZSh0LmNvbnRlbnQsIHRydWUpXG4gICAgICAgICAgICBkb20uYXBwZW5kQ2hpbGQoY2xvbmUpXG4gICAgICAgICAgICAvLyBXZSBjYW5ub3QgdXNlIHRoZSBkb2N1bWVudCBmcmFnbWVudCBpdHNlbGYgYmVjYXVzZSBpdFxuICAgICAgICAgICAgLy8gaXMgbm90IHBhcnQgb2YgdGhlIG1haW4gZG9tIHRyZWUuIEFmdGVyIHRoZSBhcHBlbmRDaGlsZFxuICAgICAgICAgICAgLy8gY2FsbCB3ZSBjYW4gYWNjZXNzIHRoZSBuZXcgZG9tIGVsZW1lbnQgYnkgaWRcbiAgICAgICAgICAgIHRoaXMuY2FyZFdyYXBwZXIgPSBkb20ucXVlcnlTZWxlY3RvcignIycgKyB0aGlzLmlkKVxuICAgICAgICAgICAgbGV0IGZyb250ID0gdGhpcy5jYXJkV3JhcHBlci5xdWVyeVNlbGVjdG9yKCcuZnJvbnQnKVxuICAgICAgICAgICAgdGhpcy5mcm9udExvYWRlci5sb2FkKGZyb250KS50aGVuKGxvYWRlciA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5mcm9udExvYWRlZChsb2FkZXIpLnRoZW4oKG9iaikgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5vbkxvYWRlZCkgdGhpcy5vbkxvYWRlZCgpXG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUodGhpcylcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICBmcm9udExvYWRlZChsb2FkZXIpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGxldCBzY2F0dGVyID0gbmV3IERPTVNjYXR0ZXIoXG4gICAgICAgICAgICAgICAgdGhpcy5jYXJkV3JhcHBlcixcbiAgICAgICAgICAgICAgICB0aGlzLmRvbVNjYXR0ZXJDb250YWluZXIsXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICB4OiBsb2FkZXIueCxcbiAgICAgICAgICAgICAgICAgICAgeTogbG9hZGVyLnksXG4gICAgICAgICAgICAgICAgICAgIHN0YXJ0U2NhbGU6IGxvYWRlci5zY2FsZSxcbiAgICAgICAgICAgICAgICAgICAgc2NhbGU6IGxvYWRlci5zY2FsZSxcbiAgICAgICAgICAgICAgICAgICAgbWF4U2NhbGU6IGxvYWRlci5tYXhTY2FsZSxcbiAgICAgICAgICAgICAgICAgICAgbWluU2NhbGU6IGxvYWRlci5taW5TY2FsZSxcbiAgICAgICAgICAgICAgICAgICAgd2lkdGg6IGxvYWRlci53YW50ZWRXaWR0aCxcbiAgICAgICAgICAgICAgICAgICAgaGVpZ2h0OiBsb2FkZXIud2FudGVkSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICByb3RhdGlvbjogbG9hZGVyLnJvdGF0aW9uLFxuICAgICAgICAgICAgICAgICAgICB0cmFuc2xhdGFibGU6IHRoaXMudHJhbnNsYXRhYmxlLFxuICAgICAgICAgICAgICAgICAgICBzY2FsYWJsZTogdGhpcy5zY2FsYWJsZSxcbiAgICAgICAgICAgICAgICAgICAgcm90YXRhYmxlOiB0aGlzLnJvdGF0YWJsZSxcbiAgICAgICAgICAgICAgICAgICAgb3ZlcmRvU2NhbGluZzogdGhpcy5vdmVyZG9TY2FsaW5nXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKVxuXG4gICAgICAgICAgICBpZiAodGhpcy5jZW50ZXIpIHtcbiAgICAgICAgICAgICAgICBzY2F0dGVyLmNlbnRlckF0KHRoaXMuY2VudGVyKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5jbG9zZU9uTWluU2NhbGUpIHtcblxuICAgICAgICAgICAgICAgIGNvbnN0IHJlbW92ZU9uTWluU2NhbGUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChzY2F0dGVyLnNjYWxlIDw9IHNjYXR0ZXIubWluU2NhbGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZmxpcHBhYmxlLmNsb3NlKClcblxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gJ0Rpc2FibGUnIG92ZXJkb3NjYWxpbmcgdG8gYXZvaWQgd2VpcmQganVtcHMgb24gY2xvc2UuXG4gICAgICAgICAgICAgICAgICAgICAgICBzY2F0dGVyLm1pblNjYWxlIC89IHNjYXR0ZXIub3ZlcmRvU2NhbGluZ1xuICAgICAgICAgICAgICAgICAgICAgICAgc2NhdHRlci5vdmVyZG9TY2FsaW5nID0gMVxuXG4gICAgICAgICAgICAgICAgICAgICAgICAvL1JlbW92ZSBjYWxsYmFja1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHNjYXR0ZXIub25UcmFuc2Zvcm0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgY2FsbGJhY2tJZHggPSBzY2F0dGVyLm9uVHJhbnNmb3JtLmluZGV4T2YocmVtb3ZlT25NaW5TY2FsZSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2F0dGVyLm9uVHJhbnNmb3JtLnNwbGljZShjYWxsYmFja0lkeCwgMSlcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgfS5iaW5kKHRoaXMpXG5cblxuXG4gICAgICAgICAgICAgICAgc2NhdHRlci5hZGRUcmFuc2Zvcm1FdmVudENhbGxiYWNrKHJlbW92ZU9uTWluU2NhbGUpXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGxldCBmbGlwcGFibGUgPSBuZXcgRE9NRmxpcHBhYmxlKHRoaXMuY2FyZFdyYXBwZXIsIHNjYXR0ZXIsIHRoaXMpXG4gICAgICAgICAgICBsZXQgYmFjayA9IHRoaXMuY2FyZFdyYXBwZXIucXVlcnlTZWxlY3RvcignLmJhY2snKVxuXG4gICAgICAgICAgICBpZiAodGhpcy5wcmVsb2FkQmFjaykge1xuICAgICAgICAgICAgICAgIHRoaXMuYmFja0xvYWRlci5sb2FkKGJhY2spLnRoZW4obG9hZGVyID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXR1cEZsaXBwYWJsZShmbGlwcGFibGUsIGxvYWRlcilcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5mbGlwcGFibGUgPSBmbGlwcGFibGVcbiAgICAgICAgICAgIHJlc29sdmUodGhpcylcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICBjZW50ZXJBdChwKSB7XG4gICAgICAgIHRoaXMuY2VudGVyID0gcFxuICAgICAgICB0aGlzLmZsaXBwYWJsZS5jZW50ZXJBdChwKVxuICAgIH1cblxuICAgIHpvb20oc2NhbGUpIHtcbiAgICAgICAgdGhpcy5mbGlwcGFibGUuem9vbShzY2FsZSlcbiAgICB9XG5cbiAgICBzZXR1cEZsaXBwYWJsZShmbGlwcGFibGUsIGxvYWRlcikge1xuICAgICAgICBmbGlwcGFibGUud2FudGVkV2lkdGggPSBsb2FkZXIud2FudGVkV2lkdGhcbiAgICAgICAgZmxpcHBhYmxlLndhbnRlZEhlaWdodCA9IGxvYWRlci53YW50ZWRIZWlnaHRcbiAgICAgICAgZmxpcHBhYmxlLndhbnRlZFNjYWxlID0gbG9hZGVyLnNjYWxlXG4gICAgICAgIGZsaXBwYWJsZS5taW5TY2FsZSA9IGxvYWRlci5taW5TY2FsZVxuICAgICAgICBmbGlwcGFibGUubWF4U2NhbGUgPSBsb2FkZXIubWF4U2NhbGVcbiAgICAgICAgZmxpcHBhYmxlLnNjYWxlQnV0dG9ucygpXG4gICAgfVxuXG4gICAgc3RhcnQoeyB0YXJnZXRDZW50ZXIgPSBudWxsIH0gPSB7fSkge1xuICAgICAgICBjb25zb2xlLmxvZygnRE9NRmxpcC5zdGFydCcsIHRhcmdldENlbnRlcilcbiAgICAgICAgaWYgKHRoaXMucHJlbG9hZEJhY2spIHRoaXMuZmxpcHBhYmxlLnN0YXJ0KHsgZHVyYXRpb246IHRoaXMuZmxpcER1cmF0aW9uLCB0YXJnZXRDZW50ZXIgfSlcbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBsZXQgYmFjayA9IHRoaXMuY2FyZFdyYXBwZXIucXVlcnlTZWxlY3RvcignLmJhY2snKVxuICAgICAgICAgICAgbGV0IGZsaXBwYWJsZSA9IHRoaXMuZmxpcHBhYmxlXG4gICAgICAgICAgICB0aGlzLmJhY2tMb2FkZXIubG9hZChiYWNrKS50aGVuKGxvYWRlciA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5zZXR1cEZsaXBwYWJsZShmbGlwcGFibGUsIGxvYWRlcilcbiAgICAgICAgICAgICAgICBmbGlwcGFibGUuc3RhcnQoeyBkdXJhdGlvbjogdGhpcy5mbGlwRHVyYXRpb24sIHRhcmdldENlbnRlciB9KVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGZhZGVPdXRBbmRSZW1vdmUoKSB7XG4gICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmNhcmRXcmFwcGVyLCB0aGlzLmZhZGVEdXJhdGlvbiwge1xuICAgICAgICAgICAgb3BhY2l0eTogMCxcbiAgICAgICAgICAgIG9uQ29tcGxldGU6ICgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmNhcmRXcmFwcGVyLnJlbW92ZSgpXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgY2xvc2VkKCkge1xuICAgICAgICB0aGlzLnVubG9hZCgpXG4gICAgfVxuXG4gICAgdW5sb2FkKCkge1xuICAgICAgICBpZiAoIXRoaXMucHJlbG9hZEJhY2spIHtcbiAgICAgICAgICAgIHRoaXMuYmFja0xvYWRlci51bmxvYWQoKVxuICAgICAgICB9XG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgRE9NRmxpcHBhYmxlIHtcbiAgICBjb25zdHJ1Y3RvcihlbGVtZW50LCBzY2F0dGVyLCBmbGlwKSB7XG4gICAgICAgIC8vIFNldCBsb2cgdG8gY29uc29sZS5sb2cgb3IgYSBjdXN0b20gbG9nIGZ1bmN0aW9uXG4gICAgICAgIC8vIGRlZmluZSBkYXRhIHN0cnVjdHVyZXMgdG8gc3RvcmUgb3VyIHRvdWNocG9pbnRzIGluXG5cbiAgICAgICAgdGhpcy5lbGVtZW50ID0gZWxlbWVudFxuICAgICAgICB0aGlzLmZsaXAgPSBmbGlwXG4gICAgICAgIHRoaXMuY2FyZCA9IGVsZW1lbnQucXVlcnlTZWxlY3RvcignLmZsaXBDYXJkJylcbiAgICAgICAgdGhpcy5mcm9udCA9IGVsZW1lbnQucXVlcnlTZWxlY3RvcignLmZyb250JylcbiAgICAgICAgdGhpcy5iYWNrID0gZWxlbWVudC5xdWVyeVNlbGVjdG9yKCcuYmFjaycpXG4gICAgICAgIHRoaXMuZmxpcHBlZCA9IGZhbHNlXG4gICAgICAgIHRoaXMuc2NhdHRlciA9IHNjYXR0ZXJcbiAgICAgICAgdGhpcy5vbkZyb250RmxpcHBlZCA9IGZsaXAub25Gcm9udEZsaXBwZWRcbiAgICAgICAgdGhpcy5vbkJhY2tGbGlwcGVkID0gZmxpcC5vbkJhY2tGbGlwcGVkXG4gICAgICAgIHRoaXMub25DbG9zZSA9IGZsaXAub25DbG9zZVxuICAgICAgICB0aGlzLm9uUmVtb3ZlZCA9IGZsaXAub25SZW1vdmVkXG4gICAgICAgIHRoaXMub25VcGRhdGUgPSBmbGlwLm9uVXBkYXRlXG5cbiAgICAgICAgdGhpcy5mbGlwRHVyYXRpb24gPSBmbGlwLmZsaXBEdXJhdGlvblxuICAgICAgICB0aGlzLmZhZGVEdXJhdGlvbiA9IGZsaXAuZmFkZUR1cmF0aW9uXG4gICAgICAgIHNjYXR0ZXIuYWRkVHJhbnNmb3JtRXZlbnRDYWxsYmFjayh0aGlzLnNjYXR0ZXJUcmFuc2Zvcm1lZC5iaW5kKHRoaXMpKVxuICAgICAgICBjb25zb2xlLmxvZygnbGliLkRPTUZsaXBwYWJsZScsIDUwMDApXG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5lbGVtZW50LCB7IHBlcnNwZWN0aXZlOiA1MDAwIH0pXG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5jYXJkLCB7IHRyYW5zZm9ybVN0eWxlOiAncHJlc2VydmUtM2QnIH0pXG4gICAgICAgIFR3ZWVuTGl0ZS5zZXQodGhpcy5iYWNrLCB7IHJvdGF0aW9uWTogLTE4MCB9KVxuICAgICAgICBUd2VlbkxpdGUuc2V0KFt0aGlzLmJhY2ssIHRoaXMuZnJvbnRdLCB7XG4gICAgICAgICAgICBiYWNrZmFjZVZpc2liaWxpdHk6ICdoaWRkZW4nLFxuICAgICAgICAgICAgcGVyc3BlY3RpdmU6IDUwMDBcbiAgICAgICAgfSlcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmZyb250LCB7IHZpc2liaWxpdHk6ICd2aXNpYmxlJyB9KVxuICAgICAgICB0aGlzLmluZm9CdG4gPSBlbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbmZvQnRuJylcbiAgICAgICAgdGhpcy5iYWNrQnRuID0gZWxlbWVudC5xdWVyeVNlbGVjdG9yKCcuYmFja0J0bicpXG4gICAgICAgIHRoaXMuY2xvc2VCdG4gPSBlbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJy5jbG9zZUJ0bicpXG4gICAgICAgIC8qIEJ1dHRvbnMgYXJlIG5vdCBndWFyYW50ZWVkIHRvIGV4aXN0LiAqL1xuICAgICAgICBpZiAodGhpcy5pbmZvQnRuKSB7XG4gICAgICAgICAgICBJbnRlcmFjdGlvbk1hcHBlci5vbigndGFwJywgdGhpcy5pbmZvQnRuLCBldmVudCA9PiB0aGlzLmZsaXAuc3RhcnQoKSlcblxuICAgICAgICAgICAgdGhpcy5lbmFibGUodGhpcy5pbmZvQnRuKVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLmJhY2tCdG4pIHtcbiAgICAgICAgICAgIEludGVyYWN0aW9uTWFwcGVyLm9uKCd0YXAnLCB0aGlzLmJhY2tCdG4sIGV2ZW50ID0+IHRoaXMuc3RhcnQoKSlcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5jbG9zZUJ0bikge1xuICAgICAgICAgICAgSW50ZXJhY3Rpb25NYXBwZXIub24oJ3RhcCcsIHRoaXMuY2xvc2VCdG4sIGV2ZW50ID0+IHRoaXMuY2xvc2UoKSlcbiAgICAgICAgICAgIHRoaXMuZW5hYmxlKHRoaXMuY2xvc2VCdG4pXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5zY2FsZUJ1dHRvbnMoKVxuICAgICAgICB0aGlzLmJyaW5nVG9Gcm9udCgpXG4gICAgfVxuXG4gICAgY2xvc2UoKSB7XG4gICAgICAgIHRoaXMuZGlzYWJsZSh0aGlzLmluZm9CdG4pXG4gICAgICAgIHRoaXMuZGlzYWJsZSh0aGlzLmNsb3NlQnRuKVxuICAgICAgICBpZiAodGhpcy5vbkNsb3NlKSB7XG4gICAgICAgICAgICB0aGlzLm9uQ2xvc2UodGhpcylcbiAgICAgICAgICAgIHRoaXMuZmxpcC5jbG9zZWQoKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5zY2F0dGVyLnpvb20oMC4xLCB7XG4gICAgICAgICAgICAgICAgYW5pbWF0ZTogdGhpcy5mYWRlRHVyYXRpb24sXG4gICAgICAgICAgICAgICAgb25Db21wbGV0ZTogKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmVsZW1lbnQucmVtb3ZlKClcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5mbGlwLmNsb3NlZCgpXG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm9uUmVtb3ZlZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vblJlbW92ZWQuY2FsbCh0aGlzKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHNob3dGcm9udCgpIHtcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmZyb250LCB7IHZpc2liaWxpdHk6ICd2aXNpYmxlJyB9KVxuICAgIH1cblxuICAgIGNlbnRlckF0KHApIHtcbiAgICAgICAgdGhpcy5zY2F0dGVyLmNlbnRlckF0KHApXG4gICAgfVxuXG4gICAgem9vbShzY2FsZSkge1xuICAgICAgICB0aGlzLnNjYXR0ZXIuem9vbShzY2FsZSlcbiAgICB9XG5cbiAgICBnZXQgYnV0dG9uU2NhbGUoKSB7XG4gICAgICAgIGxldCBpc2NhbGUgPSAxLjBcblxuICAgICAgICBpZiAodGhpcy5zY2F0dGVyICE9IG51bGwpIHtcbiAgICAgICAgICAgIGxldCBzY2FsZSA9IHRoaXMuc2NhdHRlci5zY2FsZSB8fCAxXG4gICAgICAgICAgICBpc2NhbGUgPSAxLjAgLyBzY2FsZVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBpc2NhbGVcbiAgICB9XG5cbiAgICBzY2FsZUJ1dHRvbnMoKSB7XG4gICAgICAgIC8vVGhpcyBhbHNvIHdvcmtzIGZvciBzdmdzLlxuICAgICAgICAvLyBpZiAodGhpcy5pbmZvQnRuKVxuICAgICAgICAvLyAgICAgdGhpcy5pbmZvQnRuLnN0eWxlLnRyYW5zZm9ybSA9IFwic2NhbGUoXCIgKyB0aGlzLmJ1dHRvblNjYWxlICsgXCIpXCJcblxuICAgICAgICAvLyBpZiAodGhpcy5iYWNrQnRuKVxuICAgICAgICAvLyAgICAgdGhpcy5iYWNrQnRuLnN0eWxlLnRyYW5zZm9ybSA9IFwic2NhbGUoXCIgKyB0aGlzLmJ1dHRvblNjYWxlICsgXCIpXCJcblxuICAgICAgICAvLyBpZiAodGhpcy5jbG9zZUJ0bilcbiAgICAgICAgLy8gICAgIHRoaXMuY2xvc2VCdG4uc3R5bGUudHJhbnNmb3JtID0gXCJzY2FsZShcIiArIHRoaXMuYnV0dG9uU2NhbGUgKyBcIilcIlxuXG4gICAgICAgIGNvbnNvbGUubG9nKHRoaXMuYnV0dG9uU2NhbGUpXG4gICAgICAgIC8vLy8gVGhpcyBkaWQgbm90IHdvcmsgd2l0aCBzdmdzIVxuICAgICAgICBUd2VlbkxpdGUuc2V0KFt0aGlzLmluZm9CdG4sIHRoaXMuYmFja0J0biwgdGhpcy5jbG9zZUJ0bl0sIHtcbiAgICAgICAgICAgIHNjYWxlOiB0aGlzLmJ1dHRvblNjYWxlXG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgYnJpbmdUb0Zyb250KCkge1xuICAgICAgICB0aGlzLnNjYXR0ZXIuYnJpbmdUb0Zyb250KClcbiAgICAgICAgVHdlZW5MaXRlLnNldCh0aGlzLmVsZW1lbnQsIHsgekluZGV4OiBET01TY2F0dGVyLnpJbmRleCsrIH0pXG4gICAgfVxuXG4gICAgY2xpY2tJbmZvKCkge1xuICAgICAgICB0aGlzLmJyaW5nVG9Gcm9udCgpXG4gICAgICAgIHRoaXMuaW5mb0J0bi5jbGljaygpXG4gICAgfVxuXG4gICAgc2NhdHRlclRyYW5zZm9ybWVkKGV2ZW50KSB7XG4gICAgICAgIHRoaXMuc2NhbGVCdXR0b25zKClcbiAgICB9XG5cbiAgICB0YXJnZXRSb3RhdGlvbihhbHBoYSkge1xuICAgICAgICBsZXQgb3J0aG8gPSA5MFxuICAgICAgICBsZXQgcmVzdCA9IGFscGhhICUgb3J0aG9cbiAgICAgICAgbGV0IGRlbHRhID0gMC4wXG4gICAgICAgIGlmIChyZXN0ID4gb3J0aG8gLyAyLjApIHtcbiAgICAgICAgICAgIGRlbHRhID0gb3J0aG8gLSByZXN0XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBkZWx0YSA9IC1yZXN0XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRlbHRhXG4gICAgfVxuXG4gICAgaW5mb1ZhbHVlcyhpbmZvKSB7XG4gICAgICAgIGxldCBzdGFydFggPSB0aGlzLmVsZW1lbnQuX2dzVHJhbnNmb3JtLnhcbiAgICAgICAgbGV0IHN0YXJ0WSA9IHRoaXMuZWxlbWVudC5fZ3NUcmFuc2Zvcm0ueVxuICAgICAgICBsZXQgc3RhcnRBbmdsZSA9IHRoaXMuZWxlbWVudC5fZ3NUcmFuc2Zvcm0ucm90YXRpb25cbiAgICAgICAgbGV0IHN0YXJ0U2NhbGUgPSB0aGlzLmVsZW1lbnQuX2dzVHJhbnNmb3JtLnNjYWxlWFxuICAgICAgICBsZXQgdyA9IHRoaXMuZWxlbWVudC5zdHlsZS53aWR0aFxuICAgICAgICBsZXQgaCA9IHRoaXMuZWxlbWVudC5zdHlsZS5oZWlnaHRcbiAgICAgICAgY29uc29sZS5sb2coaW5mbywgc3RhcnRYLCBzdGFydFksIHN0YXJ0QW5nbGUsIHN0YXJ0U2NhbGUsIHcsIGgpXG4gICAgfVxuXG4gICAgc2hvdyhlbGVtZW50LCBkdXJhdGlvbiA9IDAsIGFscGhhID0gMSkge1xuICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgVHdlZW5MaXRlLnRvKGVsZW1lbnQsIGR1cmF0aW9uLCB7IGF1dG9BbHBoYTogYWxwaGEgfSkgLy8gdmlzaWJpbGl0eTogJ3Zpc2libGUnLCBkaXNwbGF5OiAnaW5pdGlhbCd9KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgaGlkZShlbGVtZW50LCBkdXJhdGlvbiA9IDAsIGFscGhhID0gMCkge1xuICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgVHdlZW5MaXRlLnRvKGVsZW1lbnQsIGR1cmF0aW9uLCB7IGF1dG9BbHBoYTogYWxwaGEgfSkgLy8ge3Zpc2liaWxpdHk6ICdoaWRkZW4nLCBkaXNwbGF5OiAnbm9uZSd9KVxuICAgICAgICB9XG4gICAgfVxuXG5cblxuICAgIGVuYWJsZShidXR0b24pIHtcbiAgICAgICAgdGhpcy5zaG93KGJ1dHRvbiwgdGhpcy5mYWRlRHVyYXRpb24pXG4gICAgICAgIGlmIChidXR0b24pIHtcbiAgICAgICAgICAgIFR3ZWVuTGl0ZS5zZXQoYnV0dG9uLCB7IHBvaW50ZXJFdmVudHM6ICdhdXRvJyB9KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZGlzYWJsZShidXR0b24pIHtcbiAgICAgICAgdGhpcy5oaWRlKGJ1dHRvbiwgdGhpcy5mYWRlRHVyYXRpb24pXG4gICAgICAgIGlmIChidXR0b24pIHtcbiAgICAgICAgICAgIFR3ZWVuTGl0ZS5zZXQoYnV0dG9uLCB7IHBvaW50ZXJFdmVudHM6ICdub25lJyB9KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3RhcnQoeyB0YXJnZXRDZW50ZXIgPSBudWxsIH0gPSB7fSkge1xuICAgICAgICB0aGlzLmJyaW5nVG9Gcm9udCgpXG4gICAgICAgIGlmICghdGhpcy5mbGlwcGVkKSB7XG4gICAgICAgICAgICB0aGlzLnN0YXJ0WCA9IHRoaXMuZWxlbWVudC5fZ3NUcmFuc2Zvcm0ueFxuICAgICAgICAgICAgdGhpcy5zdGFydFkgPSB0aGlzLmVsZW1lbnQuX2dzVHJhbnNmb3JtLnlcbiAgICAgICAgICAgIHRoaXMuc3RhcnRBbmdsZSA9IHRoaXMuZWxlbWVudC5fZ3NUcmFuc2Zvcm0ucm90YXRpb25cbiAgICAgICAgICAgIHRoaXMuc3RhcnRTY2FsZSA9IHRoaXMuZWxlbWVudC5fZ3NUcmFuc2Zvcm0uc2NhbGVYXG4gICAgICAgICAgICB0aGlzLnN0YXJ0V2lkdGggPSB0aGlzLmVsZW1lbnQuc3R5bGUud2lkdGhcbiAgICAgICAgICAgIHRoaXMuc3RhcnRIZWlnaHQgPSB0aGlzLmVsZW1lbnQuc3R5bGUuaGVpZ2h0XG4gICAgICAgICAgICB0aGlzLnNjYXR0ZXJTdGFydFdpZHRoID0gdGhpcy5zY2F0dGVyLndpZHRoXG4gICAgICAgICAgICB0aGlzLnNjYXR0ZXJTdGFydEhlaWdodCA9IHRoaXMuc2NhdHRlci5oZWlnaHRcbiAgICAgICAgICAgIHRoaXMuc2hvdyh0aGlzLmJhY2spXG4gICAgICAgICAgICB0aGlzLmRpc2FibGUodGhpcy5pbmZvQnRuKVxuICAgICAgICAgICAgdGhpcy5kaXNhYmxlKHRoaXMuY2xvc2VCdG4pXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnNob3codGhpcy5mcm9udCwgdGhpcy5mYWRlRHVyYXRpb24pXG4gICAgICAgICAgICB0aGlzLmRpc2FibGUodGhpcy5iYWNrQnRuKVxuICAgICAgICB9XG4gICAgICAgIGxldCB7IHNjYWxhYmxlLCB0cmFuc2xhdGFibGUsIHJvdGF0YWJsZSB9ID0gdGhpcy5zY2F0dGVyXG4gICAgICAgIHRoaXMuc2F2ZWQgPSB7IHNjYWxhYmxlLCB0cmFuc2xhdGFibGUsIHJvdGF0YWJsZSB9XG4gICAgICAgIHRoaXMuc2NhdHRlci5zY2FsYWJsZSA9IGZhbHNlXG4gICAgICAgIHRoaXMuc2NhdHRlci50cmFuc2xhdGFibGUgPSBmYWxzZVxuICAgICAgICB0aGlzLnNjYXR0ZXIucm90YXRhYmxlID0gZmFsc2VcbiAgICAgICAgdGhpcy5zY2F0dGVyLmtpbGxBbmltYXRpb24oKVxuXG4gICAgICAgIHRoaXMuZmxpcHBlZCA9ICF0aGlzLmZsaXBwZWRcbiAgICAgICAgbGV0IHRhcmdldFkgPSB0aGlzLmZsaXBwZWQgPyAxODAgOiAwXG4gICAgICAgIGxldCB0YXJnZXRaID0gdGhpcy5mbGlwcGVkXG4gICAgICAgICAgICA/IHRoaXMuc3RhcnRBbmdsZSArIHRoaXMudGFyZ2V0Um90YXRpb24odGhpcy5zdGFydEFuZ2xlKVxuICAgICAgICAgICAgOiB0aGlzLnN0YXJ0QW5nbGVcbiAgICAgICAgbGV0IHRhcmdldFNjYWxlID0gdGhpcy5mbGlwcGVkID8gdGhpcy53YW50ZWRTY2FsZSA6IHRoaXMuc3RhcnRTY2FsZVxuICAgICAgICBsZXQgdyA9IHRoaXMuZmxpcHBlZCA/IHRoaXMud2FudGVkV2lkdGggOiB0aGlzLnN0YXJ0V2lkdGhcbiAgICAgICAgbGV0IGggPSB0aGlzLmZsaXBwZWQgPyB0aGlzLndhbnRlZEhlaWdodCA6IHRoaXMuc3RhcnRIZWlnaHRcbiAgICAgICAgbGV0IGR3ID0gdGhpcy53YW50ZWRXaWR0aCAtIHRoaXMuc2NhdHRlci53aWR0aFxuICAgICAgICBsZXQgZGggPSB0aGlzLndhbnRlZEhlaWdodCAtIHRoaXMuc2NhdHRlci5oZWlnaHRcbiAgICAgICAgbGV0IHRjID0gdGFyZ2V0Q2VudGVyXG4gICAgICAgIGxldCB4eCA9IHRjICE9IG51bGwgPyB0Yy54IC0gdyAvIDIgOiB0aGlzLnN0YXJ0WCAtIGR3IC8gMlxuICAgICAgICBsZXQgeXkgPSB0YyAhPSBudWxsID8gdGMueSAtIGggLyAyIDogdGhpcy5zdGFydFkgLSBkaCAvIDJcbiAgICAgICAgbGV0IHggPSB0aGlzLmZsaXBwZWQgPyB4eCA6IHRoaXMuc3RhcnRYXG4gICAgICAgIGxldCB5ID0gdGhpcy5mbGlwcGVkID8geXkgOiB0aGlzLnN0YXJ0WVxuXG4gICAgICAgIGNvbnNvbGUubG9nKFwiRE9NRmxpcHBhYmxlLnN0YXJ0XCIsIHRoaXMuZmxpcHBlZCwgdGFyZ2V0Q2VudGVyLCB4LCB5LCB0aGlzLnNhdmVkKVxuICAgICAgICBsZXQgb25VcGRhdGUgPSB0aGlzLm9uVXBkYXRlICE9PSBudWxsID8gKCkgPT4gdGhpcy5vblVwZGF0ZSh0aGlzKSA6IG51bGxcbiAgICAgICAgY29uc29sZS5sb2codGhpcy5mbGlwRHVyYXRpb24pXG4gICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmNhcmQsIHRoaXMuZmxpcER1cmF0aW9uLCB7XG4gICAgICAgICAgICByb3RhdGlvblk6IHRhcmdldFksXG4gICAgICAgICAgICBlYXNlOiBQb3dlcjEuZWFzZU91dCxcbiAgICAgICAgICAgIHRyYW5zZm9ybU9yaWdpbjogJzUwJSA1MCUnLFxuICAgICAgICAgICAgb25VcGRhdGUsXG4gICAgICAgICAgICBvbkNvbXBsZXRlOiBlID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5mbGlwcGVkKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vdGhpcy5oaWRlKHRoaXMuZnJvbnQpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZW5hYmxlKHRoaXMuYmFja0J0bilcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zaG93KHRoaXMuYmFja0J0bilcblxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5vbkZyb250RmxpcHBlZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vbkZyb250RmxpcHBlZCh0aGlzKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcblxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5vbkJhY2tGbGlwcGVkID09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZW5hYmxlKHRoaXMuaW5mb0J0biwgdGhpcy5mYWRlRHVyYXRpb24pXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmVuYWJsZSh0aGlzLmNsb3NlQnRuLCB0aGlzLmZhZGVEdXJhdGlvbilcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMub25CYWNrRmxpcHBlZCh0aGlzKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZmxpcC51bmxvYWQoKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aGlzLnNjYXR0ZXIuc2NhbGUgPSB0YXJnZXRTY2FsZVxuICAgICAgICAgICAgICAgIHRoaXMuc2NhbGVCdXR0b25zKClcbiAgICAgICAgICAgICAgICB0aGlzLnNjYXR0ZXIucm90YXRpb25EZWdyZWVzID0gdGFyZ2V0WlxuICAgICAgICAgICAgICAgIHRoaXMuc2NhdHRlci53aWR0aCA9IHRoaXMuZmxpcHBlZCA/IHcgOiB0aGlzLnNjYXR0ZXJTdGFydFdpZHRoXG4gICAgICAgICAgICAgICAgdGhpcy5zY2F0dGVyLmhlaWdodCA9IHRoaXMuZmxpcHBlZCA/IGggOiB0aGlzLnNjYXR0ZXJTdGFydEhlaWdodFxuXG4gICAgICAgICAgICAgICAgbGV0IHsgc2NhbGFibGUsIHRyYW5zbGF0YWJsZSwgcm90YXRhYmxlIH0gPSB0aGlzLnNhdmVkXG4gICAgICAgICAgICAgICAgdGhpcy5zY2F0dGVyLnNjYWxhYmxlID0gc2NhbGFibGVcbiAgICAgICAgICAgICAgICB0aGlzLnNjYXR0ZXIudHJhbnNsYXRhYmxlID0gdHJhbnNsYXRhYmxlXG4gICAgICAgICAgICAgICAgdGhpcy5zY2F0dGVyLnJvdGF0YWJsZSA9IHJvdGF0YWJsZVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZvcmNlM0Q6IHRydWVcbiAgICAgICAgfSlcblxuICAgICAgICAvLyBTZWUgaHR0cHM6Ly9ncmVlbnNvY2suY29tL2ZvcnVtcy90b3BpYy83OTk3LXJvdGF0ZS10aGUtc2hvcnRlc3Qtd2F5L1xuICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5lbGVtZW50LCB0aGlzLmZsaXBEdXJhdGlvbiAvIDIsIHtcbiAgICAgICAgICAgIHNjYWxlOiB0YXJnZXRTY2FsZSxcbiAgICAgICAgICAgIGVhc2U6IFBvd2VyMS5lYXNlT3V0LFxuICAgICAgICAgICAgcm90YXRpb25aOiB0YXJnZXRaICsgJ19zaG9ydCcsXG4gICAgICAgICAgICB0cmFuc2Zvcm1PcmlnaW46ICc1MCUgNTAlJyxcbiAgICAgICAgICAgIHdpZHRoOiB3LFxuICAgICAgICAgICAgaGVpZ2h0OiBoLFxuICAgICAgICAgICAgeDogeCxcbiAgICAgICAgICAgIHk6IHksXG4gICAgICAgICAgICBvbkNvbXBsZXRlOiBlID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5mbGlwcGVkKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaGlkZSh0aGlzLmZyb250KVxuICAgICAgICAgICAgICAgICAgICAvLyB0aGlzLmhpZGUodGhpcy5pbmZvQnRuKVxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaGlkZSh0aGlzLmJhY2spXG4gICAgICAgICAgICAgICAgICAgIC8vIHRoaXMuc2hvdyh0aGlzLmluZm9CdG4pXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgIH1cbn1cbiIsIlxuZXhwb3J0IGNvbnN0IGRlZXBab29tVGlsZUNhY2hlID0gbmV3IE1hcCgpXG5cblxuLyoqIFRoZSBjdXJyZW50IFRpbGUgaW1wbGVtZW50YXRpb24gc2ltcGx5IHVzZXMgUElYSS5TcHJpdGVzLlxuICpcbiAqIEJUVzogUElYSS5leHRyYXMuVGlsaW5nU3ByaXRlIGlzIG5vdCBhcHByb3ByaWF0ZS4gSXQgc2hvdWxkIGJlIHVzZWQgZm9yXG4gKiByZXBlYXRpbmcgcGF0dGVybnMuXG4gKiovXG5leHBvcnQgY2xhc3MgVGlsZSBleHRlbmRzIFBJWEkuU3ByaXRlIHtcbiAgICBjb25zdHJ1Y3Rvcih0ZXh0dXJlLCB1cmwpIHtcbiAgICAgICAgc3VwZXIodGV4dHVyZSlcbiAgICAgICAgdGhpcy51cmwgPSB1cmxcbiAgICAgICAgdGhpcy5yZWdpc3Rlcih1cmwpXG4gICAgfVxuXG4gICAgc3RhdGljIGZyb21JbWFnZShpbWFnZUlkLCBjcm9zc29yaWdpbiwgc2NhbGVNb2RlKSB7XG4gICAgICAgIHJldHVybiBuZXcgVGlsZShQSVhJLlRleHR1cmUuZnJvbUltYWdlKGltYWdlSWQsIGNyb3Nzb3JpZ2luLCBzY2FsZU1vZGUpLCBpbWFnZUlkKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlZ2lzdGVycyB0aGUgdGlsZSBpbiB0aGUgZ2xvYmFsIHJlZmVyZW5jZSBjb3VudGVyIGZvciB0ZXh0dXJlc1xuICAgICAqXG4gICAgICogQHBhcmFtIHsqfSB1cmxcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtkZWJ1Zz1mYWxzZV1cbiAgICAgKiBAbWVtYmVyb2YgVGlsZVxuICAgICAqL1xuICAgIHJlZ2lzdGVyKHVybCwgZGVidWcgPSBmYWxzZSkge1xuICAgICAgICBpZiAoZGVlcFpvb21UaWxlQ2FjaGUuaGFzKHVybCkpIHtcbiAgICAgICAgICAgIGxldCB0aWxlcyA9IGRlZXBab29tVGlsZUNhY2hlLmdldCh1cmwpXG4gICAgICAgICAgICB0aWxlcy5hZGQodGhpcylcbiAgICAgICAgICAgIGlmIChkZWJ1ZykgY29uc29sZS5sb2coXCJUaWxlLnJlZ2lzdGVyXCIsIHVybCwgdGlsZXMuc2l6ZSlcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGRlZXBab29tVGlsZUNhY2hlLnNldCh1cmwsIG5ldyBTZXQoW3RoaXNdKSlcbiAgICAgICAgICAgIGlmIChkZWJ1ZykgY29uc29sZS5sb2coXCJUaWxlLnJlZ2lzdGVyXCIsIHVybCwgMSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFVucmVnaXN0ZXJzIHRoZSByaWxlIGluIHRoZSBnbG9iYWwgcmVmZXJlbmNlIGNvdW50ZXIgZm9yIHRleHR1cmVzXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgbnVtYmVyIG9mIGhvdyBvZnRlbiBhIHRleHR1cmUgaXMgdXNlZC5cbiAgICAgKiBAbWVtYmVyb2YgVGlsZVxuICAgICAqL1xuICAgIHVucmVnaXN0ZXIoKSB7XG4gICAgICAgIGxldCB0aWxlcyA9IGRlZXBab29tVGlsZUNhY2hlLmdldCh0aGlzLnVybClcbiAgICAgICAgdGlsZXMuZGVsZXRlKHRoaXMpXG4gICAgICAgIGlmICh0aWxlcy5zaXplID09IDApIHtcbiAgICAgICAgICAgIGRlZXBab29tVGlsZUNhY2hlLmRlbGV0ZSh0aGlzLnVybClcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGlsZXMuc2l6ZVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIERlc3Ryb3lzIHRoaXMgc3ByaXRlIGFuZCBvcHRpb25hbGx5IGl0cyB0ZXh0dXJlIGFuZCBjaGlsZHJlblxuICAgICAqXG4gICAgICogQHBhcmFtIHsqfSBvcHRpb25zICBQYXJ0IG9mIHRoZSBQSVhJIEFQSSwgYnV0IGlnbm9yZWQgaW4gdGhlIGltcGxlbWVudGF0aW9uXG4gICAgICogQG1lbWJlcm9mIFRpbGVcbiAgICAgKi9cbiAgICBkZXN0cm95KG9wdGlvbnMsIGRlYnVnID0gZmFsc2UpIHtcbiAgICAgICAgaWYgKHRoaXMucGFyZW50ICE9IG51bGwpIHtcblxuICAgICAgICB9XG4gICAgICAgIGxldCBjb3VudCA9IHRoaXMudW5yZWdpc3RlcigpXG4gICAgICAgIGlmIChjb3VudCA8PSAwKSB7XG4gICAgICAgICAgICBsZXQgb3B0cyA9IHsgY2hpbGRyZW46IHRydWUsIHRleHR1cmU6IHRydWUsIGJhc2VUZXh0dXJlOiB0cnVlIH1cbiAgICAgICAgICAgIHN1cGVyLmRlc3Ryb3kob3B0cylcbiAgICAgICAgICAgIGlmIChkZWJ1ZykgY29uc29sZS5sb2coXCJUaWxlLmRlc3Ryb3lcIiwgZGVlcFpvb21UaWxlQ2FjaGUuc2l6ZSwgb3B0cylcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGxldCBvcHRzID0geyBjaGlsZHJlbjogdHJ1ZSwgdGV4dHVyZTogZmFsc2UsIGJhc2VUZXh0dXJlOiBmYWxzZSB9XG4gICAgICAgICAgICBpZiAoZGVidWcpIGNvbnNvbGUubG9nKFwiVGlsZS5kZXN0cm95XCIsIGRlZXBab29tVGlsZUNhY2hlLnNpemUsIG9wdHMpXG4gICAgICAgICAgICBzdXBlci5kZXN0cm95KG9wdHMpXG4gICAgICAgIH1cbiAgICB9XG59XG4iLCJpbXBvcnQgeyBkZWVwWm9vbVRpbGVDYWNoZSwgVGlsZSB9IGZyb20gJy4vdGlsZS5qcydcblxuLyoqXG4gKiBBIFRpbGUgTG9hZGVyIGNvbXBvbmVudCB0aGF0IGNhbiBiZSBwbHVnZ2VkIGludG8gYSBUaWxlcyBMYXllci5cbiAqL1xuZXhwb3J0IGNsYXNzIFRpbGVMb2FkZXIge1xuICAgIGNvbnN0cnVjdG9yKHRpbGVzKSB7XG4gICAgICAgIHRoaXMuZGVidWcgPSBmYWxzZVxuICAgICAgICB0aGlzLnRpbGVzID0gdGlsZXNcbiAgICAgICAgdGhpcy5zZXR1cCgpXG4gICAgfVxuXG4gICAgLyoqIFNldHVwIGNvbGxlY3Rpb25zIGFuZCBpbnN0YW5jZSB2YXJzLiAqL1xuICAgIHNldHVwKCkge1xuICAgICAgICB0aGlzLm1hcCA9IG5ldyBNYXAoKSAvLyBNYXAge3VybCA6IFsgY29sLCByb3ddfVxuICAgICAgICB0aGlzLmxvYWRpbmcgPSBuZXcgU2V0KCkgLy8gU2V0IHVybFxuICAgICAgICB0aGlzLmxvYWRlZCA9IG5ldyBNYXAoKSAvLyBNYXAge3VybCA6IHNwcml0ZSB9XG4gICAgICAgIHRoaXMubG9hZFF1ZXVlID0gW11cbiAgICB9XG5cbiAgICAvKiogU2NoZWR1bGVzIGEgdGlsZSB1cmwgZm9yIGxvYWRpbmcuIFRoZSBsb2FkaW5nIGl0c2VsZiBtdXN0IGJlIHRyaWdnZXJlZFxuICAgIGJ5IGEgY2FsbCB0byBsb2FkT25lVGlsZSBvciBsb2FkQWxsXG5cbiAgICAqIEBwYXJhbSB7U3RyaW5nfSB1cmwgLSB0aGUgdXJsIG9mIHRoZSB0ZXh0dXJlIC8gdGlsZVxuICAgICogQHBhcmFtIHtOdW1iZXJ9IGNvbCAtIHRoZSB0aWxlIGNvbFxuICAgICogQHBhcmFtIHtOdW1iZXJ9IHJvdyAtIHRoZSB0aWxlIHJvd1xuICAgICoqL1xuICAgIHNjaGVkdWxlKHVybCwgY29sLCByb3cpIHtcbiAgICAgICAgaWYgKHRoaXMubG9hZGVkLmhhcyh1cmwpKSByZXR1cm4gZmFsc2VcbiAgICAgICAgaWYgKHRoaXMubG9hZGluZy5oYXModXJsKSkgcmV0dXJuIGZhbHNlXG4gICAgICAgIHRoaXMubWFwLnNldCh1cmwsIFtjb2wsIHJvd10pXG4gICAgICAgIHRoaXMubG9hZGluZy5hZGQodXJsKVxuICAgICAgICB0aGlzLmxvYWRRdWV1ZS5wdXNoKHVybClcbiAgICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG5cbiAgICB1bnNjaGVkdWxlKHVybCkge1xuICAgICAgICBpZiAodGhpcy5sb2FkZWQuaGFzKHVybCkpIHRoaXMubG9hZGVkLmRlbGV0ZSh1cmwpXG4gICAgICAgIGlmICh0aGlzLmxvYWRpbmcuaGFzKHVybCkpIHRoaXMubG9hZGluZy5kZWxldGUodXJsKVxuICAgICAgICB0aGlzLmxvYWRRdWV1ZSA9IHRoaXMubG9hZFF1ZXVlLmZpbHRlcihpdGVtID0+IGl0ZW0gIT0gdXJsKVxuICAgIH1cblxuICAgIC8qKiBDYW5jZWxzIGxvYWRpbmcgYnkgY2xlYXJpbmcgdGhlIGxvYWQgcXVldWUgKiovXG4gICAgY2FuY2VsKCkge1xuICAgICAgICB0aGlzLmxvYWRRdWV1ZSA9IFtdXG4gICAgICAgIHRoaXMubG9hZGluZy5jbGVhcigpXG4gICAgfVxuXG4gICAgLyoqIERlc3Ryb3lzIGFsbHMgY29sbGVjdGlvbnMuICoqL1xuICAgIGRlc3Ryb3koKSB7XG4gICAgICAgIHRoaXMuc2V0dXAoKVxuICAgIH1cblxuICAgIC8qKiBQcml2YXRlIG1ldGhvZC4gSW5mb3JtcyB0aGUgdGlsZSBsYXllciBhYm91dCBhIHRleHR1cmUgZm9yIGEgZ2l2ZW4gdXJsLlxuICAgICAqIENyZWF0ZXMgdGhlIHNwcml0ZSBmb3IgdGhlIGxvYWRlZCB0ZXh0dXJlIGFuZCBpbmZvcm1zIHRoZSB0aWxlIGxheWVyLlxuICAgICAqIEBwYXJhbSB7U3RyaW5nfSB1cmwgLSB0aGUgdXJsXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHRleHR1cmUgLSB0aGUgbG9hZGVkIHJlc291cmNlXG4gICAgICoqL1xuICAgIF90ZXh0dXJlQXZhaWxhYmxlKHVybCwgY29sLCByb3csIHRleHR1cmUpIHtcbiAgICAgICAgbGV0IHRpbGUgPSB0aGlzLmxvYWRlZC5nZXQodXJsKVxuICAgICAgICBpZiAodGlsZSAhPSBudWxsKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJUaWxlIGFscmVhZHkgbG9hZGVkXCIpXG4gICAgICAgICAgICB0aWxlLnVucmVnaXN0ZXIoKVxuICAgICAgICB9XG4gICAgICAgIHRpbGUgPSBuZXcgVGlsZSh0ZXh0dXJlLCB1cmwpXG4gICAgICAgIHRoaXMubG9hZGVkLnNldCh1cmwsIHRpbGUpXG4gICAgICAgIHRoaXMudGlsZXMudGlsZUF2YWlsYWJsZSh0aWxlLCBjb2wsIHJvdywgdXJsKVxuICAgIH1cbn1cblxuLyoqXG4gKiBVc2VzIHRoZSBQSVhJIExvYWRlciBidXQgY2FuIGJlIHJlcGxhY2VkIHdpdGggb3RoZSBsb2FkZXJzIGltcGxlbWVudGluZ1xuICogdGhlIHB1YmxpYyBtZXRob2RzIHdpdGhvdXQgdW5kZXJzY29yZS5cbiAqIENhbGxzIHRoZSBUaWxlcy50aWxlQXZhaWxhYmxlIG1ldGhvZCBpZiB0aGUgdGV4dHVyZSBpcyBhdmFpbGFibGUuXG4gKiovXG5leHBvcnQgY2xhc3MgUElYSVRpbGVMb2FkZXIgZXh0ZW5kcyBUaWxlTG9hZGVyIHtcblxuICAgIGNvbnN0cnVjdG9yKHRpbGVzLCBjb21wcmVzc2lvbikge1xuICAgICAgICBzdXBlcih0aWxlcylcbiAgICAgICAgdGhpcy5kZXN0cm95ZWQgPSBmYWxzZVxuICAgICAgICB0aGlzLmxvYWRlciA9IG5ldyBQSVhJLmxvYWRlcnMuTG9hZGVyKClcbiAgICAgICAgdGhpcy5sb2FkZXIub24oJ2xvYWQnLCB0aGlzLl9vbkxvYWRlZC5iaW5kKHRoaXMpKVxuICAgICAgICB0aGlzLmxvYWRlci5vbignZXJyb3InLCB0aGlzLl9vbkVycm9yLmJpbmQodGhpcykpXG4gICAgICAgIGlmIChjb21wcmVzc2lvbikge1xuICAgICAgICAgICAgdGhpcy5sb2FkZXIucHJlKFBJWEkuY29tcHJlc3NlZFRleHR1cmVzLmltYWdlUGFyc2VyKCkpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBzY2hlZHVsZSh1cmwsIGNvbCwgcm93KSB7XG4gICAgICAgIC8vIE92ZXJ3cml0dGVuIHNjaGVkdWxlIHRvIGF2b2lkIEJhc2VUZXh0dXJlIGFuZCBUZXh0dXJlIGFscmVhZHkgbG9hZGVkIGVycm9ycy5cbiAgICAgICAgaWYgKHRoaXMubG9hZGVkLmhhcyh1cmwpKSByZXR1cm4gZmFsc2VcbiAgICAgICAgaWYgKHRoaXMubG9hZGluZy5oYXModXJsKSkgcmV0dXJuIGZhbHNlXG5cbiAgICAgICAgaWYgKGRlZXBab29tVGlsZUNhY2hlLmhhcyh1cmwpKSB7XG4gICAgICAgICAgICBsZXQgdGlsZXMgPSBkZWVwWm9vbVRpbGVDYWNoZS5nZXQodXJsKVxuICAgICAgICAgICAgZm9yIChsZXQgdGlsZSBvZiB0aWxlcy52YWx1ZXMoKSkge1xuICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCJSZXVzaW5nIGNhY2hlZCB0ZXh0dXJlXCIsIHRpbGUucGFyZW50KVxuICAgICAgICAgICAgICAgIGxldCB0ZXh0dXJlID0gdGlsZS50ZXh0dXJlXG4gICAgICAgICAgICAgICAgdGhpcy5fdGV4dHVyZUF2YWlsYWJsZSh1cmwsIGNvbCwgcm93LCB0ZXh0dXJlKVxuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICBsZXQgdGV4dHVyZSA9IFBJWEkudXRpbHMuVGV4dHVyZUNhY2hlW3VybF1cbiAgICAgICAgaWYgKHRleHR1cmUpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmxvZygnVGV4dHVyZSBhbHJlYWR5IGxvYWRlZCcsIHRleHR1cmUpXG4gICAgICAgICAgICB0aGlzLl90ZXh0dXJlQXZhaWxhYmxlKHVybCwgY29sLCByb3csIHRleHR1cmUpXG4gICAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgfVxuICAgICAgICBsZXQgYmFzZSA9IFBJWEkudXRpbHMuQmFzZVRleHR1cmVDYWNoZVt1cmxdXG4gICAgICAgIGlmIChiYXNlKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coJ0Jhc2VUZXh0dXJlIGFscmVhZHkgbG9hZGVkJywgYmFzZSlcbiAgICAgICAgICAgIGxldCB0ZXh0dXJlID0gbmV3IFBJWEkuVGV4dHVyZShiYXNlKVxuICAgICAgICAgICAgdGhpcy5fdGV4dHVyZUF2YWlsYWJsZSh1cmwsIGNvbCwgcm93LCB0ZXh0dXJlKVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHN1cGVyLnNjaGVkdWxlKHVybCwgY29sLCByb3cpXG4gICAgfVxuXG4gICAgLyoqIExvYWQgb25lIGFuZCBvbmx5IG9uZSBvZiB0aGUgc2NoZWR1bGVkIHRpbGVzICoqL1xuICAgIGxvYWRPbmVUaWxlKCkge1xuICAgICAgICBpZiAodGhpcy5kZXN0cm95ZWQpXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgdGhpcy5fbG9hZE9uZVRpbGUoKVxuICAgIH1cblxuICAgIC8qKiBMb2FkIGFsbCBzY2hlZHVsZWQgdGlsZXMgKiovXG4gICAgbG9hZEFsbCgpIHtcbiAgICAgICAgaWYgKHRoaXMuZGVzdHJveWVkKVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIHRoaXMuX2xvYWRBbGxUaWxlcygpXG4gICAgfVxuXG4gICAgLyoqIERlc3Ryb3lzIHRoZSBsb2FkZXIgY29tcGxldGx5ICoqL1xuICAgIGRlc3Ryb3koKSB7XG4gICAgICAgIHRoaXMuZGVzdHJveWVkID0gdHJ1ZVxuICAgICAgICBzdXBlci5kZXN0cm95KClcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHRoaXMubG9hZGVyLnJlc2V0KClcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIkVycm9yIHdoaWxlIHJlc2V0dGluZyBsb2FkZXJcIiwgZXJyb3IpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBfb25FcnJvcihsb2FkZXIsIGVycm9yKSB7XG4gICAgICAgIGNvbnNvbGUud2FybignQ2Fubm90IGxvYWQnLCBlcnJvcilcbiAgICB9XG5cbiAgICAvKiogUHJpdmF0ZSBtZXRob2QuIENhbGxlZCBieSB0aGUgUElYSSBsb2FkZXIgYWZ0ZXIgZWFjaCBzdWNjZXNzZnVsbFxuICAgICAqIGxvYWRpbmcgb2YgYSBzaW5nbGUgdGlsZS5cbiAgICAgKiBDcmVhdGVzIHRoZSBzcHJpdGUgZm9yIHRoZSBsb2FkZWQgdGV4dHVyZSBhbmQgaW5mb3JtcyB0aGUgdGlsZSBsYXllci5cbiAgICAgKiBAcGFyYW0ge09iamVjdH0gbG9hZGVyIC0gdGhlIGxvYWRlciBpbnN0YW5jZVxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSByZXNvdXJjZSAtIHRoZSBsb2FkZWQgcmVzb3VyY2Ugd2l0aCB1cmwgYW5kIHRleHR1cmUgYXR0clxuICAgICAqKi9cbiAgICBfb25Mb2FkZWQobG9hZGVyLCByZXNvdXJjZSkge1xuICAgICAgICBpZiAodGhpcy5kZXN0cm95ZWQpIHtcbiAgICAgICAgICAgIGxldCB0ZXh0dXJlID0gcmVzb3VyY2UudGV4dHVyZVxuICAgICAgICAgICAgbGV0IGRlc3Ryb3lCYXNlID0gIWRlZXBab29tVGlsZUNhY2hlLmhhcyhyZXNvdXJjZS51cmwpXG4gICAgICAgICAgICB0ZXh0dXJlLmRlc3Ryb3koZGVzdHJveUJhc2UpXG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJSZWNlaXZlZCByZXNvdXJjZSBhZnRlciBkZXN0cm95XCIsIHRleHR1cmUpXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgbGV0IFtjb2wsIHJvd10gPSB0aGlzLm1hcC5nZXQocmVzb3VyY2UudXJsKVxuICAgICAgICAgICAgdGhpcy5fdGV4dHVyZUF2YWlsYWJsZShyZXNvdXJjZS51cmwsIGNvbCwgcm93LCByZXNvdXJjZS50ZXh0dXJlKVxuICAgICAgICB9XG4gICAgICAgIGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIlRleHR1cmUgdW5hdmFpbGFibGU6IFwiICsgZXJyLm1lc3NhZ2UpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogUHJpdmF0ZSBtZXRob2Q6IGxvYWRzIG9uZSB0aWxlIGZyb20gdGhlIHF1ZXVlLiAqKi9cbiAgICBfbG9hZE9uZVRpbGUocmV0cnkgPSAxKSB7XG4gICAgICAgIC8vY29uc29sZS5sb2coXCJfbG9hZE9uZVRpbGVcIilcbiAgICAgICAgaWYgKHRoaXMuZGVzdHJveWVkKSB7XG4gICAgICAgICAgICAvL2NvbnNvbGUud2FybihcIl9sb2FkT25lVGlsZSBhZnRlciBkZXN0cm95XCIpXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5sb2FkZXIubG9hZGluZykge1xuICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5fbG9hZE9uZVRpbGUoKVxuICAgICAgICAgICAgfSwgcmV0cnkpXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5sb2FkUXVldWUubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgbGV0IHVybCA9IHRoaXMubG9hZFF1ZXVlLnBvcCgpXG4gICAgICAgICAgICB0aGlzLmxvYWRlci5hZGQodXJsLCB1cmwpXG4gICAgICAgICAgICB0aGlzLmxvYWRlci5sb2FkKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKiBQcml2YXRlIG1ldGhvZDogbG9hZHMgYWxsIHRpbGVzIGZyb20gdGhlIHF1ZXVlIGluIGJhdGNoZXMuIEJhdGNoZXMgYXJlXG4gICAgaGVscGZ1bGwgdG8gYXZvaWQgbG9hZGluZyB0aWxlcyB0aGF0IGFyZSBubyBsb25nZXIgbmVlZGVkIGJlY2F1c2UgdGhlXG4gICAgdXNlciBoYXMgYWxyZWFkeSB6b29tZWQgdG8gYSBkaWZmZXJlbnQgbGV2ZWwuKiovXG4gICAgX2xvYWRBbGxUaWxlcyhiYXRjaFNpemUgPSA4LCByZXRyeSA9IDE2KSB7XG4gICAgICAgIGlmICh0aGlzLmRlc3Ryb3llZCkge1xuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMubG9hZFF1ZXVlLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmxvYWRlci5sb2FkaW5nKSB7XG4gICAgICAgICAgICAgICAgLy9jb25zb2xlLmxvZyhcIkxvYWRlciBidXN5XCIsIHRoaXMubG9hZFF1ZXVlLmxlbmd0aClcbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fbG9hZEFsbFRpbGVzKClcbiAgICAgICAgICAgICAgICB9LCByZXRyeSlcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGxldCBpID0gMFxuICAgICAgICAgICAgbGV0IHVybHMgPSBbXVxuICAgICAgICAgICAgd2hpbGUgKGkgPCBiYXRjaFNpemUgJiYgdGhpcy5sb2FkUXVldWUubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIGxldCB1cmwgPSB0aGlzLmxvYWRRdWV1ZS5wb3AoKVxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5sb2FkZWQuaGFzKHVybCkpIHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IHJlc291cmNlID0gdGhpcy5sb2FkZXIucmVzb3VyY2VzW3VybF1cbiAgICAgICAgICAgICAgICAgICAgaWYgKHJlc291cmNlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIlJlc291cmNlIGFscmVhZHkgYWRkZWRcIiwgdXJsKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXJscy5wdXNoKHVybClcbiAgICAgICAgICAgICAgICAgICAgICAgIGkgKz0gMVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5sb2FkZXIuYWRkKHVybHMpLmxvYWQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuX2xvYWRBbGxUaWxlcygpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIFVzZXMgWE1MSHR0cFJlcXVlc3RzIGJ1dCBjYW4gYmUgcmVwbGFjZWQgd2l0aCBvdGhlciBsb2FkZXJzIGltcGxlbWVudGluZ1xuICogdGhlIHB1YmxpYyBtZXRob2RzIHdpdGhvdXQgdW5kZXJzY29yZS5cbiAqIENhbGxzIHRoZSBUaWxlcy50aWxlQXZhaWxhYmxlIG1ldGhvZCBpZiB0aGUgdGV4dHVyZSBpcyBhdmFpbGFibGUuXG4gKiovXG5leHBvcnQgY2xhc3MgUmVxdWVzdFRpbGVMb2FkZXIgZXh0ZW5kcyBUaWxlTG9hZGVyIHtcblxuICAgIGNvbnN0cnVjdG9yKHRpbGVzLCBjb21wcmVzc2lvbikge1xuICAgICAgICBzdXBlcih0aWxlcylcbiAgICAgICAgdGhpcy5jb21wcmVzc2lvbiA9IGNvbXByZXNzaW9uXG4gICAgfVxuXG4gICAgc2NoZWR1bGUodXJsLCBjb2wsIHJvdykge1xuICAgICAgICB0aGlzLl9sb2FkKHVybCwgY29sLCByb3cpXG4gICAgICAgIHJldHVybiBzdXBlci5zY2hlZHVsZSh1cmwsIGNvbCwgcm93KVxuICAgIH1cblxuICAgIF9sb2FkKHVybCwgY29sLCByb3csIGNhbGxiYWNrID0gbnVsbCkge1xuICAgICAgICBpZiAodGhpcy5jb21wcmVzc2lvbikge1xuICAgICAgICAgICAgbGV0IHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpXG4gICAgICAgICAgICB4aHIub3BlbignR0VUJywgdXJsLCBmYWxzZSlcbiAgICAgICAgICAgIHhoci5yZXNwb25zZVR5cGUgPSAnYXJyYXlidWZmZXInXG4gICAgICAgICAgICB4aHIub25sb2FkID0gZSA9PiB7XG4gICAgICAgICAgICAgICAgbGV0IENvbXByZXNzZWRJbWFnZSA9IFBJWEkuY29tcHJlc3NlZFRleHR1cmVzLkNvbXByZXNzZWRJbWFnZVxuICAgICAgICAgICAgICAgIGxldCBjb21wcmVzc2VkID0gQ29tcHJlc3NlZEltYWdlLmxvYWRGcm9tQXJyYXlCdWZmZXIoXG4gICAgICAgICAgICAgICAgICAgIHhoci5yZXNwb25zZSxcbiAgICAgICAgICAgICAgICAgICAgdXJsXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIGxldCBiYXNlID0gbmV3IFBJWEkuQmFzZVRleHR1cmUoY29tcHJlc3NlZClcbiAgICAgICAgICAgICAgICBsZXQgdGV4dHVyZSA9IG5ldyBQSVhJLlRleHR1cmUoYmFzZSlcbiAgICAgICAgICAgICAgICB0aGlzLl90ZXh0dXJlQXZhaWxhYmxlKHVybCwgY29sLCByb3csIHRleHR1cmUpXG4gICAgICAgICAgICAgICAgaWYgKGNhbGxiYWNrKSBjYWxsYmFjaygpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB4aHIuc2VuZCgpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsZXQgdGV4dHVyZSA9IFBJWEkuVGV4dHVyZS5mcm9tSW1hZ2UoJ2Fzc2V0cy9pbWFnZS5wbmcnKVxuICAgICAgICAgICAgdGhpcy5fdGV4dHVyZUF2YWlsYWJsZSh1cmwsIGNvbCwgcm93LCB0ZXh0dXJlKVxuICAgICAgICAgICAgaWYgKGNhbGxiYWNrKSBjYWxsYmFjaygpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogTG9hZCBvbmUgYW5kIG9ubHkgb25lIG9mIHRoZSBzY2hlZHVsZWQgdGlsZXMgKiovXG4gICAgbG9hZE9uZVRpbGUoKSB7XG4gICAgICAgIHRoaXMuX2xvYWRPbmVUaWxlKClcbiAgICB9XG5cbiAgICAvKiogTG9hZCBhbGwgc2NoZWR1bGVkIHRpbGVzICoqL1xuICAgIGxvYWRBbGwoKSB7XG4gICAgICAgIHRoaXMuX2xvYWRBbGxUaWxlcygpXG4gICAgfVxuXG4gICAgLyoqIFByaXZhdGUgbWV0aG9kOiBsb2FkcyBvbmUgdGlsZSBmcm9tIHRoZSBxdWV1ZS4gKiovXG4gICAgX2xvYWRPbmVUaWxlKHJldHJ5ID0gMSkge1xuICAgICAgICBpZiAodGhpcy5sb2FkUXVldWUubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgbGV0IHVybCA9IHRoaXMubG9hZFF1ZXVlLnBvcCgpXG4gICAgICAgICAgICBsZXQgW2NvbCwgcm93XSA9IHRoaXMubWFwLmdldCh1cmwpXG4gICAgICAgICAgICB0aGlzLl9sb2FkKHVybCwgY29sLCByb3cpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogUHJpdmF0ZSBtZXRob2Q6IGxvYWRzIGFsbCB0aWxlcyBmcm9tIHRoZSBxdWV1ZSBpbiBiYXRjaGVzLiBCYXRjaGVzIGFyZVxuICAgIGhlbHBmdWxsIHRvIGF2b2lkIGxvYWRpbmcgdGlsZXMgdGhhdCBhcmUgbm8gbG9uZ2VyIG5lZWRlZCBiZWNhdXNlIHRoZVxuICAgIHVzZXIgaGFzIGFscmVhZHkgem9vbWVkIHRvIGEgZGlmZmVyZW50IGxldmVsLioqL1xuICAgIF9sb2FkQWxsVGlsZXMoYmF0Y2hTaXplID0gOCwgcmV0cnkgPSAxNikge1xuICAgICAgICBpZiAodGhpcy5sb2FkUXVldWUubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgbGV0IGkgPSAwXG4gICAgICAgICAgICBsZXQgdXJscyA9IFtdXG4gICAgICAgICAgICB3aGlsZSAoaSA8IGJhdGNoU2l6ZSAmJiB0aGlzLmxvYWRRdWV1ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgbGV0IHVybCA9IHRoaXMubG9hZFF1ZXVlLnBvcCgpXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUudGltZSh1cmwpXG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLmxvYWRlZC5oYXModXJsKSkge1xuICAgICAgICAgICAgICAgICAgICB1cmxzLnB1c2godXJsKVxuICAgICAgICAgICAgICAgICAgICBpICs9IDFcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBsZXQgdG90YWwgPSB1cmxzLmxlbmd0aFxuICAgICAgICAgICAgbGV0IGNvdW50ID0gMFxuICAgICAgICAgICAgZm9yIChsZXQgdXJsIG9mIHVybHMpIHtcbiAgICAgICAgICAgICAgICBsZXQgW2NvbCwgcm93XSA9IHRoaXMubWFwLmdldCh1cmwpXG4gICAgICAgICAgICAgICAgdGhpcy5fbG9hZCh1cmwsIGNvbCwgcm93LCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvdW50KytcbiAgICAgICAgICAgICAgICAgICAgaWYgKGNvdW50ID09IHRvdGFsKSB0aGlzLl9sb2FkQWxsVGlsZXMoKVxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG59XG5cblxuLyoqXG4gKiBVc2VzIFdvcmtlcnMgYnV0IGNhbiBiZSByZXBsYWNlZCB3aXRoIG90aGVyIGxvYWRlcnMgaW1wbGVtZW50aW5nXG4gKiB0aGUgcHVibGljIG1ldGhvZHMgd2l0aG91dCB1bmRlcnNjb3JlLlxuICogQ2FsbHMgdGhlIFRpbGVzLnRpbGVBdmFpbGFibGUgbWV0aG9kIGlmIHRoZSB0ZXh0dXJlIGlzIGF2YWlsYWJsZS5cbiAqKi9cbmV4cG9ydCBjbGFzcyBXb3JrZXJUaWxlTG9hZGVyIGV4dGVuZHMgVGlsZUxvYWRlciB7XG5cbiAgICBjb25zdHJ1Y3Rvcih0aWxlcykge1xuICAgICAgICBzdXBlcih0aWxlcylcbiAgICAgICAgbGV0IHdvcmtlciA9IHRoaXMud29ya2VyID0gbmV3IFdvcmtlcihcIi4uLy4uL2xpYi9waXhpL2RlZXB6b29tL3RpbGVsb2FkZXIuanNcIilcbiAgICAgICAgd29ya2VyLm9ubWVzc2FnZSA9IChldmVudCkgPT4ge1xuICAgICAgICAgICAgaWYgKGV2ZW50LmRhdGEuc3VjY2Vzcykge1xuICAgICAgICAgICAgICAgIGxldCB7IHVybCwgY29sLCByb3csIGJ1ZmZlciB9ID0gZXZlbnQuZGF0YVxuICAgICAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCJXb3JrZXJUaWxlTG9hZGVyLmxvYWRlZFwiLCB1cmwsIGJ1ZmZlcilcbiAgICAgICAgICAgICAgICBsZXQgQ29tcHJlc3NlZEltYWdlID0gUElYSS5jb21wcmVzc2VkVGV4dHVyZXMuQ29tcHJlc3NlZEltYWdlXG4gICAgICAgICAgICAgICAgbGV0IGNvbXByZXNzZWQgPSBDb21wcmVzc2VkSW1hZ2UubG9hZEZyb21BcnJheUJ1ZmZlcihidWZmZXIsIHVybClcbiAgICAgICAgICAgICAgICBsZXQgYmFzZSA9IG5ldyBQSVhJLkJhc2VUZXh0dXJlKGNvbXByZXNzZWQpXG4gICAgICAgICAgICAgICAgbGV0IHRleHR1cmUgPSBuZXcgUElYSS5UZXh0dXJlKGJhc2UpXG4gICAgICAgICAgICAgICAgdGhpcy5fdGV4dHVyZUF2YWlsYWJsZSh1cmwsIGNvbCwgcm93LCB0ZXh0dXJlKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgbG9hZE9uZSgpIHtcbiAgICAgICAgaWYgKHRoaXMubG9hZFF1ZXVlLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGxldCB1cmwgPSB0aGlzLmxvYWRRdWV1ZS5wb3AoKVxuICAgICAgICAgICAgbGV0IFtjb2wsIHJvd10gPSB0aGlzLm1hcC5nZXQodXJsKVxuICAgICAgICAgICAgbGV0IHRpbGUgPSBbY29sLCByb3csIHVybF1cbiAgICAgICAgICAgIHRoaXMud29ya2VyLnBvc3RNZXNzYWdlKHsgY29tbWFuZDogXCJsb2FkXCIsIHRpbGVzOiBbdGlsZV0gfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGxvYWRBbGwoKSB7XG4gICAgICAgIGxldCB0aWxlcyA9IFtdXG4gICAgICAgIHdoaWxlICh0aGlzLmxvYWRRdWV1ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBsZXQgdXJsID0gdGhpcy5sb2FkUXVldWUucG9wKClcbiAgICAgICAgICAgIGxldCBbY29sLCByb3ddID0gdGhpcy5tYXAuZ2V0KHVybClcbiAgICAgICAgICAgIHRpbGVzLnB1c2goW2NvbCwgcm93LCB1cmxdKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMud29ya2VyLnBvc3RNZXNzYWdlKHsgY29tbWFuZDogXCJsb2FkXCIsIHRpbGVzIH0pXG4gICAgfVxuXG4gICAgY2FuY2VsKCkge1xuICAgICAgICBzdXBlci5jYW5jZWwoKVxuICAgICAgICB0aGlzLndvcmtlci5wb3N0TWVzc2FnZSh7IGNvbW1hbmQ6IFwiYWJvcnRcIiB9KVxuICAgIH1cblxuICAgIGRlc3Ryb3koKSB7XG4gICAgICAgIHRoaXMud29ya2VyLnBvc3RNZXNzYWdlKHsgY29tbWFuZDogXCJhYm9ydFwiIH0pXG4gICAgICAgIHRoaXMud29ya2VyLnRlcm1pbmF0ZSgpXG4gICAgICAgIHRoaXMud29ya2VyID0gbnVsbFxuICAgICAgICBzdXBlci5kZXN0cm95KClcbiAgICB9XG59XG4iLCJpbXBvcnQgeyBDb2xvcnMgfSBmcm9tICcuLi8uLi91dGlscy5qcydcbmltcG9ydCB7IFdvcmtlclRpbGVMb2FkZXIsIFBJWElUaWxlTG9hZGVyIH0gZnJvbSBcIi4vbG9hZGVyLmpzXCJcblxuXG4vKipcbiAqIEEgbGF5ZXIgb2YgdGlsZXMgdGhhdCByZXByZXNlbnRzIGEgem9vbSBsZXZlbCBvZiBhIERlZXBab29tSW1hZ2UgYXMgYSBncmlkXG4gKiBvZiBzcHJpdGVzLlxuICogQGNvbnN0cnVjdG9yXG4gKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSB0aGUgem9vbSBsZXZlbCBvZiB0aGUgdGlsZSBsYXllclxuICogQHBhcmFtIHtEZWVwWm9vbUltYWdlfSB2aWV3IC0gdGhlIHpvb21hYmxlIGltYWdlIHRoZSBsYXllciBiZWxvbmdzIHRvXG4gKiBAcGFyYW0ge251bWJlcn0gc2NhbGUgLSB0aGUgc2NhbGUgb2YgdGhlIHRpbGUgbGF5ZXJcbiAqIEBwYXJhbSB7bnVtYmVyfSBjb2xzIC0gdGhlIG51bWJlciBvZiBjb2x1bW5zIG9mIHRoZSBsYXllclxuICogQHBhcmFtIHtudW1iZXJ9IHJvd3MgLSB0aGUgbnVtYmVyIG9mIHJvd3Mgb2YgdGhlIGxheWVyXG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGggLSB0aGUgd2lkdGggb2YgdGhlIGxheWVyIGluIHBpeGVsXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0IC0gdGhlIGhlaWdodCBvZiB0aGUgbGF5ZXIgaW4gcGl4ZWxcbiAqIEBwYXJhbSB7bnVtYmVyfSB0aWxlU2l6ZSAtIHRoZSBzaXplIG9mIGEgc2luZ2xlIHRpbGUgaW4gcGl4ZWxcbiAqIEBwYXJhbSB7bnVtYmVyfSBvdmVybGFwIC0gdGhlIG92ZXJsYXAgb2YgdGhlIHRpbGVzIGluIHBpeGVsXG4gKiBAcGFyYW0ge251bWJlcn0gZmFkZUluVGltZSAtIHRpbWUgbmVlZGVkIHRvIGZhZGUgaW4gdGlsZXMgaWYgVHdlZW5MaXRlIGlzIHNldFxuICoqL1xuZXhwb3J0IGNsYXNzIFRpbGVzIGV4dGVuZHMgUElYSS5Db250YWluZXIge1xuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBsZXZlbCxcbiAgICAgICAgdmlldyxcbiAgICAgICAgc2NhbGUsXG4gICAgICAgIGNvbHMsXG4gICAgICAgIHJvd3MsXG4gICAgICAgIHdpZHRoLFxuICAgICAgICBoZWlnaHQsXG4gICAgICAgIHRpbGVTaXplLFxuICAgICAgICBvdmVybGFwLFxuICAgICAgICBmYWRlSW5UaW1lID0gMC4zM1xuICAgICkge1xuICAgICAgICBzdXBlcigpXG4gICAgICAgIHRoaXMuZGVidWcgPSBmYWxzZVxuICAgICAgICB0aGlzLnNob3dHcmlkID0gZmFsc2VcbiAgICAgICAgdGhpcy52aWV3ID0gdmlld1xuICAgICAgICB0aGlzLmxldmVsID0gbGV2ZWxcbiAgICAgICAgdGhpcy5jb2xzID0gY29sc1xuICAgICAgICB0aGlzLnJvd3MgPSByb3dzXG4gICAgICAgIHRoaXMucGl4ZWxXaWR0aCA9IHdpZHRoXG4gICAgICAgIHRoaXMucGl4ZWxIZWlnaHQgPSBoZWlnaHRcbiAgICAgICAgdGhpcy50aWxlU2l6ZSA9IHRpbGVTaXplXG4gICAgICAgIHRoaXMub3ZlcmxhcCA9IG92ZXJsYXBcbiAgICAgICAgdGhpcy5uZWVkZWQgPSBuZXcgTWFwKCkgLy8gdXJsIGFzIGtleSwgW2NvbCwgcm93XSBhcyB2YWx1ZVxuICAgICAgICB0aGlzLnJlcXVlc3RlZCA9IG5ldyBTZXQoKVxuICAgICAgICB0aGlzLmF2YWlsYWJsZSA9IG5ldyBNYXAoKVxuICAgICAgICB0aGlzLnNjYWxlLnNldChzY2FsZSwgc2NhbGUpXG4gICAgICAgIHRoaXMudGlsZVNjYWxlID0gc2NhbGVcbiAgICAgICAgdGhpcy5mYWRlSW5UaW1lID0gZmFkZUluVGltZVxuICAgICAgICB0aGlzLmtlZXAgPSBmYWxzZVxuICAgICAgICBpZiAodGhpcy52aWV3LnByZWZlcldvcmtlciAmJiB2aWV3LmluZm8uY29tcHJlc3Npb24ubGVuZ3RoID4gMClcbiAgICAgICAgICAgIHRoaXMubG9hZGVyID0gbmV3IFdvcmtlclRpbGVMb2FkZXIodGhpcylcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgdGhpcy5sb2FkZXIgPSBuZXcgUElYSVRpbGVMb2FkZXIodGhpcywgdmlldy5pbmZvLmNvbXByZXNzaW9uKVxuICAgICAgICB0aGlzLmludGVyYWN0aXZlID0gZmFsc2VcbiAgICAgICAgdGhpcy5faGlnaGxpZ2h0ID0gbnVsbFxuXG4gICAgICAgIHRoaXMuX2luZm8gPSBudWxsXG5cbiAgICAgICAgdGhpcy5fY2VudGVyUG9pbnQgPSBudWxsXG4gICAgICAgIHRoaXMuX2JvdW5kc1JlY3QgPSBudWxsXG5cbiAgICAgICAgdGhpcy5pbmZvQ29sb3IgPSBDb2xvcnMucmFuZG9tKClcbiAgICAgICAgdGhpcy5wcHJpbnQoKVxuICAgICAgICB0aGlzLmRlc3Ryb3llZCA9IGZhbHNlXG4gICAgfVxuXG5cblxuICAgIC8qKiBUZXN0cyB3aGV0aGVyIGFsbCB0aWxlcyBhcmUgbG9hZGVkLiAqKi9cbiAgICBpc0NvbXBsZXRlKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jb2xzICogdGhpcy5yb3dzID09PSB0aGlzLmNoaWxkcmVuLmxlbmd0aFxuICAgIH1cblxuICAgIC8qKiBSZXR1cm5zIHRoZSBoaWdobGlnaCBncmFwaGljcyBsYXllciBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLlxuICAgICAqKi9cbiAgICBnZXQgaGlnaGxpZ2h0KCkge1xuICAgICAgICBpZiAodGhpcy5faGlnaGxpZ2h0ID09IG51bGwpIHtcbiAgICAgICAgICAgIGxldCBncmFwaGljcyA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAgICAgICAgICAgIGdyYXBoaWNzLmJlZ2luRmlsbCgweGZmZmYwMCwgMC4xKVxuICAgICAgICAgICAgZ3JhcGhpY3MubGluZVN0eWxlKDIsIDB4ZmZmZjAwKVxuICAgICAgICAgICAgZ3JhcGhpY3MuZHJhd1JlY3QoMSwgMSwgdGhpcy50aWxlU2l6ZSAtIDIsIHRoaXMudGlsZVNpemUgLSAyKVxuICAgICAgICAgICAgZ3JhcGhpY3MuZW5kRmlsbCgpXG4gICAgICAgICAgICBncmFwaGljcy5pbnRlcmFjdGl2ZSA9IGZhbHNlXG4gICAgICAgICAgICB0aGlzLl9oaWdobGlnaHQgPSBncmFwaGljc1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9oaWdobGlnaHRcbiAgICB9XG5cbiAgICAvKiogUmV0dXJucyB0aGUgaGlnaGxpZ2ggZ3JhcGhpY3MgbGF5ZXIgZm9yIGRlYnVnZ2luZyBwdXJwb3Nlcy5cbiAgICAgKiovXG4gICAgZ2V0IGluZm8oKSB7XG4gICAgICAgIGlmICh0aGlzLl9pbmZvID09IG51bGwpIHtcbiAgICAgICAgICAgIGxldCBncmFwaGljcyA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAgICAgICAgICAgIGdyYXBoaWNzLmxpbmVTdHlsZSg0LCAweGZmMDAwMClcbiAgICAgICAgICAgIGdyYXBoaWNzLmludGVyYWN0aXZlID0gZmFsc2VcbiAgICAgICAgICAgIHRoaXMuX2luZm8gPSBncmFwaGljc1xuICAgICAgICAgICAgdGhpcy5hZGRDaGlsZCh0aGlzLl9pbmZvKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9pbmZvXG4gICAgfVxuXG4gICAgLyoqIEhlbHBlciBtZXRob2QgcHJldHR5IHByaW50aW5nIGRlYnVnIGluZm9ybWF0aW9uLiAqKi9cbiAgICBwcHJpbnQoKSB7XG4gICAgICAgIGlmICh0aGlzLmRlYnVnKVxuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgJ1RpbGVzIGxldmVsOiAnICtcbiAgICAgICAgICAgICAgICB0aGlzLmxldmVsICtcbiAgICAgICAgICAgICAgICAnIHNjYWxlOiAnICtcbiAgICAgICAgICAgICAgICB0aGlzLnNjYWxlLnggK1xuICAgICAgICAgICAgICAgICcgY29sczogJyArXG4gICAgICAgICAgICAgICAgdGhpcy5jb2xzICtcbiAgICAgICAgICAgICAgICAnIHJvd3M6ICcgK1xuICAgICAgICAgICAgICAgIHRoaXMucm93cyArXG4gICAgICAgICAgICAgICAgJyB3OiAnICtcbiAgICAgICAgICAgICAgICB0aGlzLnBpeGVsV2lkdGggK1xuICAgICAgICAgICAgICAgICcgaDogJyArXG4gICAgICAgICAgICAgICAgdGhpcy5waXhlbEhlaWdodCArXG4gICAgICAgICAgICAgICAgJyB0c2l6ZTonICtcbiAgICAgICAgICAgICAgICB0aGlzLnRpbGVTaXplXG4gICAgICAgICAgICApXG4gICAgfVxuXG4gICAgLyoqIENvbXB1dGVzIHRoZSB0aWxlIHBvc2l0aW9uIGFuZCBvYmV5cyB0aGUgb3ZlcmxhcC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gY29sIC0gVGhlIGNvbHVtbiBvZiB0aGUgdGlsZVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSByb3cgLSBUaGUgcm93IG9mIHRoZSB0aWxlXG4gICAgICogQHJldHVybnMge1BJWEkuUG9pbnR9IG9ialxuICAgICAqKi9cbiAgICB0aWxlUG9zaXRpb24oY29sLCByb3cpIHtcbiAgICAgICAgbGV0IHggPSBjb2wgKiB0aGlzLnRpbGVTaXplXG4gICAgICAgIGxldCB5ID0gcm93ICogdGhpcy50aWxlU2l6ZVxuICAgICAgICBsZXQgb3ZlcmxhcCA9IHRoaXMub3ZlcmxhcFxuICAgICAgICBpZiAoY29sICE9IDApIHtcbiAgICAgICAgICAgIHggLT0gb3ZlcmxhcFxuICAgICAgICB9XG4gICAgICAgIGlmIChyb3cgIT0gMCkge1xuICAgICAgICAgICAgeSAtPSBvdmVybGFwXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ldyBQSVhJLlBvaW50KHgsIHkpXG4gICAgfVxuXG4gICAgLyoqIENvbXB1dGVzIHRoZSB0aWxlIHNpemUgd2l0aG91dCBvdmVybGFwXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGNvbCAtIFRoZSBjb2x1bW4gb2YgdGhlIHRpbGVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcm93IC0gVGhlIHJvdyBvZiB0aGUgdGlsZVxuICAgICAqIEByZXR1cm5zIHtQSVhJLlBvaW50fSBvYmpcbiAgICAgKiovXG4gICAgdGlsZURpbWVuc2lvbnMoY29sLCByb3cpIHtcbiAgICAgICAgbGV0IHcgPSB0aGlzLnRpbGVTaXplXG4gICAgICAgIGxldCBoID0gdGhpcy50aWxlU2l6ZVxuICAgICAgICBsZXQgcG9zID0gdGhpcy50aWxlUG9zaXRpb24oY29sLCByb3cpXG4gICAgICAgIGlmIChjb2wgPT0gdGhpcy5jb2xzIC0gMSkge1xuICAgICAgICAgICAgdyA9IHRoaXMucGl4ZWxXaWR0aCAtIHBvcy54XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHJvdyA9PSB0aGlzLnJvd3MgLSAxKSB7XG4gICAgICAgICAgICBoID0gdGhpcy5waXhlbEhlaWdodCAtIHBvcy55XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ldyBQSVhJLlBvaW50KHcsIGgpXG4gICAgfVxuXG4gICAgLyoqIE1ldGhvZCB0byBzdXBwb3J0IGRlYnVnZ2luZy4gSGlnaGxpZ2h0cyB0aGUgc3BlY2lmaWVkIHRpbGUgYXQgY29sLCByb3cgKiovXG4gICAgaGlnaGxpZ2h0VGlsZShjb2wsIHJvdykge1xuICAgICAgICBpZiAoY29sID4gLTEgJiYgcm93ID4gLTEgJiYgY29sIDwgdGhpcy5jb2xzICYmIHJvdyA8IHRoaXMucm93cykge1xuICAgICAgICAgICAgbGV0IGdyYXBoaWNzID0gdGhpcy5oaWdobGlnaHRcbiAgICAgICAgICAgIGxldCBkaW0gPSB0aGlzLnRpbGVEaW1lbnNpb25zKGNvbCwgcm93KVxuICAgICAgICAgICAgZ3JhcGhpY3MucG9zaXRpb24gPSB0aGlzLnRpbGVQb3NpdGlvbihjb2wsIHJvdylcbiAgICAgICAgICAgIGdyYXBoaWNzLmNsZWFyKClcbiAgICAgICAgICAgIGdyYXBoaWNzLmJlZ2luRmlsbCgweGZmMDBmZiwgMC4xKVxuICAgICAgICAgICAgZ3JhcGhpY3MubGluZVN0eWxlKDIsIDB4ZmZmZjAwKVxuICAgICAgICAgICAgZ3JhcGhpY3MuZHJhd1JlY3QoMSwgMSwgZGltLnggLSAyLCBkaW0ueSAtIDIpXG4gICAgICAgICAgICBncmFwaGljcy5lbmRGaWxsKClcbiAgICAgICAgICAgIHRoaXMuYWRkQ2hpbGQodGhpcy5oaWdobGlnaHQpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnJlbW92ZUNoaWxkKHRoaXMuaGlnaGxpZ2h0KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqIExvYWRzIHRoZSB0aWxlcyBmb3IgdGhlIGdpdmVuIHVybHMgYW5kIGFkZHMgdGhlIHRpbGVzIGFzIHNwcml0ZXMuXG4gICAgICogQHBhcmFtIHthcnJheX0gdXJscG9zIC0gQW4gYXJyYXkgb2YgVVJMLCBwb3MgcGFpcnNcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IG9ubHlvbmUgLSBMb2FkcyBvbmx5IG9uIHRpbGUgYXQgYSB0aW1lIGlmIHRydWVcbiAgICAgKiovXG4gICAgbG9hZFRpbGVzKHVybHBvcywgb25seW9uZSwgcmVmQ29sLCByZWZSb3cpIHtcbiAgICAgICAgaWYgKHRoaXMuc2hvd0dyaWQpIHtcbiAgICAgICAgICAgIHRoaXMuaGlnaGxpZ2h0VGlsZShyZWZDb2wsIHJlZlJvdylcbiAgICAgICAgfVxuICAgICAgICB1cmxwb3MuZm9yRWFjaChkID0+IHtcbiAgICAgICAgICAgIGxldCBbdXJsLCBjb2wsIHJvd10gPSBkXG4gICAgICAgICAgICBpZiAodGhpcy5sb2FkZXIuc2NoZWR1bGUodXJsLCBjb2wsIHJvdykpIHtcbiAgICAgICAgICAgICAgICBpZiAob25seW9uZSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5sb2FkZXIubG9hZE9uZVRpbGUoKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgdGhpcy5sb2FkZXIubG9hZEFsbCgpXG4gICAgfVxuXG4gICAgLyoqIFByaXZhdGUgbWV0aG9kOiBhZGQgYSByZWQgYm9yZGVyIHRvIGEgdGlsZSBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLiAqKi9cbiAgICBfYWRkVGlsZUJvcmRlcih0aWxlLCBjb2wsIHJvdykge1xuICAgICAgICBsZXQgZGltID0gdGhpcy50aWxlRGltZW5zaW9ucyhjb2wsIHJvdylcbiAgICAgICAgbGV0IGdyYXBoaWNzID0gbmV3IFBJWEkuR3JhcGhpY3MoKVxuICAgICAgICBncmFwaGljcy5iZWdpbkZpbGwoMCwgMClcbiAgICAgICAgZ3JhcGhpY3MubGluZVN0eWxlKDIsIDB4ZmYwMDAwKVxuICAgICAgICBncmFwaGljcy5kcmF3UmVjdCgxLCAxLCBkaW0ueCAtIDIsIGRpbS55IC0gMilcbiAgICAgICAgZ3JhcGhpY3MuZW5kRmlsbCgpXG4gICAgICAgIHRpbGUuYWRkQ2hpbGQoZ3JhcGhpY3MpXG4gICAgfVxuXG4gICAgLyoqIEFkZHMgYSB0aWxlLiAqKi9cbiAgICBhZGRUaWxlKHRpbGUsIGNvbCwgcm93LCB1cmwpIHtcbiAgICAgICAgaWYgKHRoaXMuYXZhaWxhYmxlLmhhcyh1cmwpKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oJ1RyeWluZyB0byBhZGQgYXZhaWxhYmxlIHRpbGUnKVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5hZGRDaGlsZEF0KHRpbGUsIDApXG4gICAgICAgIHRoaXMuYXZhaWxhYmxlLnNldCh1cmwsIHRpbGUpXG4gICAgICAgIGlmICh0aGlzLmRlc3Ryb3llZCkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKCdBZGRpbmcgdG8gZGVzdHJveWVkIHRpbGVzIGxheWVyJylcbiAgICAgICAgfVxuICAgICAgICAvLyB0aGlzLl9hZGRUaWxlQm9yZGVyKHRpbGUsIGNvbCwgcm93KVxuICAgIH1cblxuICAgIC8qKiBDYWxsZWQgYnkgdGhlIGxvYWRlciBhZnRlciBlYWNoIHN1Y2Nlc3NmdWxsIGxvYWRpbmcgb2YgYSBzaW5nbGUgdGlsZS5cbiAgICAgKiBBZGRzIHRoZSBzcHJpdGUgdG8gdGhlIHRpbGUgbGF5ZXIuXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHRpbGUgLSB0aGUgbG9hZGVkIHRpbGUgc3ByaXRlXG4gICAgICogQHBhcmFtIHtOdW1iZXJ9IGNvbCAtIHRoZSBjb2wgcG9zaXRpb25cbiAgICAgKiBAcGFyYW0ge051bWJlcn0gcm93IC0gdGhlIHJvd3Bvc2l0aW9uXG4gICAgICoqL1xuICAgIHRpbGVBdmFpbGFibGUodGlsZSwgY29sLCByb3csIHVybCkge1xuICAgICAgICBsZXQgcG9zID0gdGhpcy50aWxlUG9zaXRpb24oY29sLCByb3cpXG4gICAgICAgIGlmICh0aGlzLnNob3dHcmlkKSB7XG4gICAgICAgICAgICB0aGlzLl9hZGRUaWxlQm9yZGVyKHRpbGUsIGNvbCwgcm93KVxuICAgICAgICB9XG4gICAgICAgIHRpbGUucG9zaXRpb24gPSBwb3NcbiAgICAgICAgdGlsZS5pbnRlcmFjdGl2ZSA9IGZhbHNlXG4gICAgICAgIGlmIChUd2VlbkxpdGUpIHtcbiAgICAgICAgICAgIHRpbGUuYWxwaGEgPSAwXG4gICAgICAgICAgICBUd2VlbkxpdGUudG8odGlsZSwgdGhpcy5mYWRlSW5UaW1lLCB7IGFscGhhOiB0aGlzLmFscGhhIH0pXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5hZGRUaWxlKHRpbGUsIGNvbCwgcm93LCB1cmwpXG4gICAgfVxuXG4gICAgLyoqIERlc3Ryb3lzIHRoZSB0aWxlcyBsYXllciBhbmQgZGVzdHJveXMgdGhlIGxvYWRlci4gQXN5bmMgbG9hZCBjYWxscyBhcmVcbiAgICAgKiBjYW5jZWxsZWQuXG4gICAgICoqL1xuICAgIGRlc3Ryb3koKSB7XG4gICAgICAgIHRoaXMuZGVzdHJveWVkID0gdHJ1ZVxuICAgICAgICB0aGlzLmxvYWRlci5kZXN0cm95KClcbiAgICAgICAgc3VwZXIuZGVzdHJveSh7IGNoaWxkcmVuOiB0cnVlIH0pIC8vIENhbGxzIGRlc3Ryb3lDaGlsZHJlblxuICAgICAgICB0aGlzLmF2YWlsYWJsZS5jbGVhcigpXG4gICAgICAgIHRoaXMucmVxdWVzdGVkLmNsZWFyKClcbiAgICAgICAgdGhpcy5uZWVkZWQuY2xlYXIoKVxuICAgIH1cblxuICAgIGRlc3Ryb3lUaWxlKHVybCwgdGlsZSkge1xuICAgICAgICB0aGlzLmxvYWRlci51bnNjaGVkdWxlKHVybClcbiAgICAgICAgdGhpcy5yZW1vdmVDaGlsZCh0aWxlKVxuICAgICAgICB0aWxlLmRlc3Ryb3koKVxuICAgICAgICB0aGlzLmF2YWlsYWJsZS5kZWxldGUodXJsKVxuICAgIH1cblxuICAgIGRlc3Ryb3lUaWxlQnlVcmwodXJsKSB7XG4gICAgICAgIGlmICh0aGlzLmF2YWlsYWJsZS5oYXModXJsKSkge1xuICAgICAgICAgICAgbGV0IHRpbGUgPSB0aGlzLmF2YWlsYWJsZS5nZXQodXJsKVxuICAgICAgICAgICAgdGhpcy5kZXN0cm95VGlsZSh1cmwsIHRpbGUpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiBEZXN0cm95cyB0aGUgdGlsZXMgd2hpY2ggYXJlIG5vdCB3aXRoIHRoZSBib3VuZHMgb2YgdGhlIGFwcCB0byBmcmVlXG4gICAgKiBtZW1vcnkuXG4gICAgKiovXG4gICAgZGVzdHJveVRpbGVzKHF1YWRUcmVlcykge1xuICAgICAgICBsZXQgY291bnQgPSAwXG4gICAgICAgIGZvciAobGV0IFt1cmwsIHRpbGVdIG9mIHRoaXMuYXZhaWxhYmxlLmVudHJpZXMoKSkge1xuICAgICAgICAgICAgaWYgKCFxdWFkVHJlZXMuaGFzKHVybCkpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmRlc3Ryb3lUaWxlKHVybCwgdGlsZSlcbiAgICAgICAgICAgICAgICBjb3VudCArPSAxXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvdW50ICYmIHRoaXMuZGVidWcpXG4gICAgICAgICAgICBjb25zb2xlLmxvZygnZGVzdHJveVRpbGVzJywgdGhpcy5sZXZlbCwgY291bnQpXG4gICAgfVxuXG4gICAgZGVzdHJveVVubmVlZGVkVGlsZXMoKSB7XG4gICAgICAgIGxldCBjb3VudCA9IDBcbiAgICAgICAgZm9yIChsZXQgW3VybCwgdGlsZV0gb2YgdGhpcy5hdmFpbGFibGUuZW50cmllcygpKSB7XG4gICAgICAgICAgICBpZiAoIXRoaXMubmVlZGVkLmhhcyh1cmwpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5kZXN0cm95VGlsZSh1cmwsIHRpbGUpXG4gICAgICAgICAgICAgICAgY291bnQgKz0gMVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChjb3VudCAmJiB0aGlzLmRlYnVnKVxuICAgICAgICAgICAgY29uc29sZS5sb2coJ2Rlc3Ryb3lVbm5lZWRlZFRpbGVzJywgdGhpcy5sZXZlbCwgY291bnQpXG4gICAgfVxuXG4gICAgaGlnaGxpZ2h0SW5mb3MoKSB7XG4gICAgICAgIGxldCBncmFwaGljcyA9IHRoaXMuaW5mb1xuICAgICAgICBsZXQgY29sb3IgPSB0aGlzLmluZm9Db2xvclxuICAgICAgICBncmFwaGljcy5jbGVhcigpXG4gICAgICAgIGdyYXBoaWNzLmxpbmVTdHlsZSgyLCBjb2xvcilcbiAgICAgICAgZm9yIChsZXQgW2NvbCwgcm93XSBvZiB0aGlzLm5lZWRlZC52YWx1ZXMoKSkge1xuICAgICAgICAgICAgbGV0IGRpbSA9IHRoaXMudGlsZURpbWVuc2lvbnMoY29sLCByb3cpXG4gICAgICAgICAgICBsZXQgcG9zID0gdGhpcy50aWxlUG9zaXRpb24oY29sLCByb3cpXG4gICAgICAgICAgICBncmFwaGljcy5iZWdpbkZpbGwoY29sb3IsIDAuMilcbiAgICAgICAgICAgIGdyYXBoaWNzLmRyYXdSZWN0KHBvcy54ICsgMSwgcG9zLnkgKyAxLCBkaW0ueCAtIDIsIGRpbS55IC0gMilcbiAgICAgICAgICAgIGdyYXBoaWNzLmVuZEZpbGwoKVxuICAgICAgICB9XG4gICAgICAgIGxldCByID0gdGhpcy5fYm91bmRzUmVjdFxuICAgICAgICBpZiAociAhPSBudWxsKSB7XG4gICAgICAgICAgICBncmFwaGljcy5saW5lU3R5bGUoMjAsIGNvbG9yKVxuICAgICAgICAgICAgZ3JhcGhpY3MuZHJhd1JlY3Qoci54LCByLnksIHIud2lkdGgsIHIuaGVpZ2h0KVxuICAgICAgICAgICAgZ3JhcGhpY3MubW92ZVRvKHIueCwgci55KVxuICAgICAgICAgICAgZ3JhcGhpY3MubGluZVRvKHIueCArIHIud2lkdGgsIHIueSArIHIuaGVpZ2h0KVxuXG4gICAgICAgICAgICBncmFwaGljcy5tb3ZlVG8oci54LCByLnkgKyByLmhlaWdodClcbiAgICAgICAgICAgIGdyYXBoaWNzLmxpbmVUbyhyLnggKyByLndpZHRoLCByLnkpXG4gICAgICAgIH1cblxuICAgICAgICBsZXQgcCA9IHRoaXMuX2NlbnRlclBvaW50XG4gICAgICAgIGlmIChwICE9IG51bGwpIHtcbiAgICAgICAgICAgIGdyYXBoaWNzLmRyYXdDaXJjbGUocC54LCBwLnksIDIwKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgdGludFRpbGVzKHF1YWRUcmVlcykge1xuICAgICAgICBmb3IgKGxldCBbdXJsLCB0aWxlXSBvZiB0aGlzLmF2YWlsYWJsZS5lbnRyaWVzKCkpIHtcbiAgICAgICAgICAgIGlmICghcXVhZFRyZWVzLmhhcyh1cmwpKSB0aWxlLnRpbnQgPSAweGZmMDAwMFxuICAgICAgICB9XG4gICAgfVxuXG4gICAgdW50aW50VGlsZXMoKSB7XG4gICAgICAgIGZvciAobGV0IFt1cmwsIHRpbGVdIG9mIHRoaXMuYXZhaWxhYmxlLmVudHJpZXMoKSkge1xuICAgICAgICAgICAgdGlsZS50aW50ID0gMHhmZmZmZmZcbiAgICAgICAgfVxuICAgIH1cbn1cbiIsImltcG9ydCB7IENhcGFiaWxpdGllcyB9IGZyb20gJy4uLy4uL2NhcGFiaWxpdGllcy5qcydcbmltcG9ydCB7IFBvaW50cyB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJ1xuaW1wb3J0IHsgZGVlcFpvb21UaWxlQ2FjaGUgfSBmcm9tICcuL3RpbGUuanMnXG5pbXBvcnQgeyBUaWxlcyB9IGZyb20gJy4vdGlsZXMuanMnXG5cbmZ1bmN0aW9uIGlzRXZlbihuKSB7XG4gICAgcmV0dXJuIG4gJSAyID09IDBcbn1cblxuXG5mdW5jdGlvbiBwcmludFRpbGVDYWNoZUluZm9zKCkge1xuICAgIGxldCByZWZlcmVuY2VzID0gbmV3IE1hcCgpXG4gICAgbGV0IG11bHRpcGxlcyA9IDBcbiAgICBmb3IgKGxldCBbdXJsLCB0aWxlc10gb2YgZGVlcFpvb21UaWxlQ2FjaGUuZW50cmllcygpKSB7XG4gICAgICAgIGxldCBjb3VudCA9IHRpbGVzLnNpemVcbiAgICAgICAgcmVmZXJlbmNlcy5zZXQodXJsLCBjb3VudClcbiAgICAgICAgaWYgKGNvdW50ID4gMSkge1xuICAgICAgICAgICAgbXVsdGlwbGVzICs9IDFcbiAgICAgICAgfVxuICAgIH1cbiAgICBjb25zb2xlLmxvZyh7IG11bHRpcGxlcywgcmVmZXJlbmNlcyB9KVxufVxuLyoqXG4gKiBBIHV0aWxpdHkgY2xhc3MgdGhhdCBob2xkcyBpbmZvcm1hdGlvbiB0eXBpY2FsbHkgcHJvdmlkZWQgYnkgRFpJIGZpbGVzLCBpLmUuXG4gKiBoZWlnaHQgYW5kIHdpZHRoIG9mIHRoZSBvdmVyYWxsIGltYWdlLCBvdmVybGFwLCBhbmQgaW1hZ2UgdHlwZS5cbiAqXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwYXJhbSB7b2JqfSBhdHRycyAtIEEgSlNPTi1PYmplY3QgaG9sZGluZyB0aGUgbGlzdGVkIGtleXMgYW5kIHZhbHVlc1xuICogQGV4YW1wbGVcbiAqICAgICB7XG4gKiAgICAgICAgIFwidGlsZVNpemVcIjogMTAyNCxcbiAqICAgICAgICAgXCJmb3JtYXRcIjogXCJqcGVnXCIsXG4gKiAgICAgICAgIFwib3ZlcmxhcFwiOiAxLFxuICogICAgICAgICBcImhlaWdodFwiOiA0Nzk0LFxuICogICAgICAgICBcIndpZHRoXCI6IDQwOTUsXG4gKiAgICAgICAgIFwiY2xpcFwiOiB7IFwibWluTGV2ZWxcIjogMTIsIFwibWF4TGV2ZWxcIjogMjAsIFwic3RhcnRDb2xcIjogMzAxNDM2LCBcInN0YXJ0Um93XCI6IDM1NDA2MCB9LFxuICogICAgICAgICAgICAgICAgICAgLy8gb3B0aW9uYWw6IG1pbkxldmVsIGFuZCBtYXhMZXZlbCBkZWZpbmUgdGhlIGxldmVsIGJvdW5kc1xuICogICAgICAgICAgICAgICAgICAgLy8gc3RhcnRDb2w6IGZpcnN0IGNvbCBhdCBtYXhMZXZlbFxuICogICAgICAgICAgICAgICAgICAgLy8gc3RhcnRSb3c6IGZpcnN0IHJvdyBhdCBtYXhMZXZlbFxuICogICAgICAgICBcInBhdGhcIjogXCJ2YXIvVmVybWVlci9WZXJtZWVyX2ZpbGVzXCIsXG4gKiAgICAgICAgIFwidHlwZVwiOiBcImR6aVwiLCAgLy8gb3B0aW9uYWw6IGR6aSAoZGVmYXVsdCkgb3IgbWFwXG4gKiAgICAgICAgIFwidXJsVGlsZVRlbXBsYXRlXCI6IFwie3BhdGh9L3tsZXZlbH0ve2NvbHVtbn0ve3Jvd30ue2Zvcm1hdH1cIlxuICogICAgICAgICAgIC8vIG9wdGlvbmFsOiB7cGF0aH0ve2xldmVsfS97Y29sdW1ufV97cm93fS57Zm9ybWF0fSAoZGVmYXVsdCkgb3JcbiAqICAgICAgICAgICAvLyBhIHRlbXBsYXRlIFN0cmluZyB3aXRoIHRoZSBmb3JtYXQgb2YgdGhlIFVSTFxuICogICAgIH1cbiAqL1xuZXhwb3J0IGNsYXNzIERlZXBab29tSW5mbyB7XG4gICAgY29uc3RydWN0b3IoYXR0cnMpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IGluIGF0dHJzKSB7XG4gICAgICAgICAgICB0aGlzW2tleV0gPSBhdHRyc1trZXldXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5tYXhMZXZlbCA9IDAgLy8gVGhlIGhpZ2hlc3QgbGV2ZWwgbnVtYmVyLCB0eXBpY2FsbHkgY29ycmVzcG9uZHMgdG8gdGhlXG4gICAgICAgIC8vIG51bWJlciBpbiB0aGUgZmlsZSBzeXN0ZW0gZm9yIHRoZSBmb2xkZXIgd2l0aCB0aWxlc1xuICAgICAgICB0aGlzLmNsaXAgPSB0aGlzLmNsaXAgfHwgbnVsbCAvLyBlLmcuIHsgbGV2ZWw6IDEyLCBjb2w6IDMwMTQzNiwgcm93OiAzNTQwNjAgfVxuICAgICAgICB0aGlzLnR5cGUgPSB0aGlzLnR5cGUgfHwgJ2R6aSdcbiAgICAgICAgdGhpcy51cmxUaWxlVGVtcGxhdGUgPVxuICAgICAgICAgICAgdGhpcy51cmxUaWxlVGVtcGxhdGUgfHwgJ3twYXRofS97bGV2ZWx9L3tjb2x1bW59X3tyb3d9Lntmb3JtYXR9J1xuICAgICAgICB0aGlzLnNldHVwRGltZW5zaW9ucygpXG4gICAgfVxuXG4gICAgLyogQ29tcHV0ZXMgdGhlIG5lZWRlZCBudW1iZXIgb2YgbGF5ZXJzIGZyb20gdGhlIHdpZHRoIGFuZCBoZWlnaHRcbiAgICAqICBvZiB0aGUgaW1hZ2UuIE5vdGUgdGhhdCB0aGlzIGluY2x1ZGVzIHRoZSBsZXZlbCAwLCBpLmUuIDAgLi4uIDRcbiAgICAqIG1lYW5zIHRoYXQgNSBsZXZlbHMgZXhpc3QuXG4gICAgKiovXG4gICAgbnVtTGV2ZWxzKCkge1xuICAgICAgICBsZXQgbWF4RGltZW5zaW9uID0gTWF0aC5tYXgodGhpcy53aWR0aCwgdGhpcy5oZWlnaHQpXG4gICAgICAgIGxldCBib3VuZGFyeSA9IHRoaXMudHlwZSA9PT0gJ2R6aScgPyAxIDogdGhpcy50aWxlU2l6ZVxuICAgICAgICBsZXQgbnVtTGV2ZWxzID0gMFxuICAgICAgICB3aGlsZSAobWF4RGltZW5zaW9uID49IGJvdW5kYXJ5KSB7XG4gICAgICAgICAgICBtYXhEaW1lbnNpb24gLz0gMlxuICAgICAgICAgICAgbnVtTGV2ZWxzKytcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVtTGV2ZWxzXG4gICAgfVxuXG4gICAgLyoqIENvbXB1dGVzIHRoZSBzY2FsZSBhdCB0aGUgZ2l2ZW4gbGV2ZWwuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxldmVsIG9mIHRoZSB3YW50ZWQgbGF5ZXJcbiAgICAgKiBAcmV0dXJucyB7bnVtYmVyfSBzY2FsZVxuICAgICAqKi9cbiAgICBnZXRTY2FsZShsZXZlbCkge1xuICAgICAgICBsZXQgc2NhbGUgPSAxXG4gICAgICAgIGlmICh0aGlzLnR5cGUgPT09ICdkemknKSB7XG4gICAgICAgICAgICBzY2FsZSA9IE1hdGgucG93KDAuNSwgdGhpcy5tYXhMZXZlbCAtIGxldmVsICsgMSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHNjYWxlID0gTWF0aC5wb3coMC41LCB0aGlzLm1heExldmVsIC0gbGV2ZWwpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHNjYWxlXG4gICAgfVxuXG4gICAgLyoqIENvbXB1dGVzIHRoZSBzY2FsZWQgd2lkdGggYW5kIGhlaWdodCBvZiB0aGUgZ2l2ZW4gbGV2ZWwuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxldmVsIG9mIHRoZSB3YW50ZWQgbGF5ZXJcbiAgICAgKiBAcmV0dXJucyB7YXJyYXl9IHNpemUgLSBUaGUgd2lkdGggYW5kIGhlaWdodFxuICAgICAqKi9cbiAgICBnZXREaW1lbnNpb25zKGxldmVsKSB7XG4gICAgICAgIGxldCBzY2FsZSA9IHRoaXMuZ2V0U2NhbGUobGV2ZWwpXG4gICAgICAgIGxldCB3ID0gTWF0aC5jZWlsKHRoaXMud2lkdGggKiBzY2FsZSlcbiAgICAgICAgbGV0IGggPSBNYXRoLmNlaWwodGhpcy5oZWlnaHQgKiBzY2FsZSlcbiAgICAgICAgcmV0dXJuIFt3LCBoXVxuICAgIH1cblxuICAgIC8qKiBDb21wdXRlcyB0aGUgbnVtYmVyIG9mIGNvbHMgYW5kIHJvd3Mgb2YgdGlsZXMuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxldmVsIG9mIHRoZSB3YW50ZWQgbGF5ZXJcbiAgICAgKiBAcmV0dXJucyB7YXJyYXl9IHNpemUgLSBUaGUgY29scyBhbmQgcm93c1xuICAgICAqKi9cbiAgICBnZXROdW1UaWxlcyhsZXZlbCkge1xuICAgICAgICBsZXQgZGltID0gdGhpcy5nZXREaW1lbnNpb25zKGxldmVsKVxuICAgICAgICBsZXQgY29scyA9IE1hdGguY2VpbChkaW1bMF0gLyB0aGlzLnRpbGVTaXplKVxuICAgICAgICBsZXQgcm93cyA9IE1hdGguY2VpbChkaW1bMV0gLyB0aGlzLnRpbGVTaXplKVxuICAgICAgICBpZiAodGhpcy5jbGlwKSB7XG4gICAgICAgICAgICBsZXQgcmVzdCA9IHRoaXMucmVzdHNbbGV2ZWxdXG4gICAgICAgICAgICBpZiAocmVzdCkge1xuICAgICAgICAgICAgICAgIGlmIChyZXN0LnJlc3RDb2wpIHtcbiAgICAgICAgICAgICAgICAgICAgY29scyArPSAxXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChyZXN0LnJlc3RSb3dzKSB7XG4gICAgICAgICAgICAgICAgICAgIHJvd3MgKz0gMVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gW2NvbHMsIHJvd3NdXG4gICAgfVxuXG4gICAgc2V0dXBEaW1lbnNpb25zKGxvYWRCYXNlSW1hZ2UgPSBmYWxzZSkge1xuICAgICAgICAvKiogU2V0dXAgaW5zdGFuY2UgdmFyaWFibGVzIGFuZCBsb2FkIHRoZSBiYXNlIGltYWdlLCBpLmUuIHRoZSBsYXJnZXN0XG4gICAgICAgIGltYWdlIHRoYXQgY2FuIGJlIHJlcHJlc2VudGVkIGFzIGEgc2luZ2xlIHRpbGUuXG4gICAgICAgIEBwcml2YXRlXG4gICAgICAgICoqL1xuICAgICAgICBsZXQgd3cgPSB0aGlzLndpZHRoXG4gICAgICAgIGxldCBoaCA9IHRoaXMuaGVpZ2h0XG4gICAgICAgIGxldCBzY2FsZSA9IDEuMFxuICAgICAgICBsZXQgbGV2ZWwgPSAwXG4gICAgICAgIGxldCBzaW5nbGUgPSAwXG4gICAgICAgIGNvbnN0IHRzaXplID0gdGhpcy50aWxlU2l6ZVxuXG4gICAgICAgIGlmICh0aGlzLmNsaXApIHtcbiAgICAgICAgICAgIHRoaXMuYmFzZUxldmVsID0gdGhpcy5jbGlwLm1pbkxldmVsXG4gICAgICAgICAgICB0aGlzLm1heExldmVsID0gdGhpcy5jbGlwLm1heExldmVsXG4gICAgICAgICAgICB0aGlzLmJhc2VJbWFnZSA9IG51bGxcbiAgICAgICAgICAgIHRoaXMuc2l6ZSA9IHRoaXMuZ2V0RGltZW5zaW9ucyh0aGlzLmJhc2VMZXZlbClcbiAgICAgICAgICAgIHRoaXMub2Zmc2V0cyA9IHt9XG4gICAgICAgICAgICB0aGlzLnJlc3RzID0ge31cbiAgICAgICAgICAgIGxldCBzdGFydENvbCA9IHRoaXMuY2xpcC5zdGFydENvbFxuICAgICAgICAgICAgbGV0IHN0YXJ0Um93ID0gdGhpcy5jbGlwLnN0YXJ0Um93XG4gICAgICAgICAgICBsZXQgZmxvYXRTdGFydENvbCA9IHN0YXJ0Q29sXG4gICAgICAgICAgICBsZXQgZmxvYXRTdGFydFJvdyA9IHN0YXJ0Um93XG4gICAgICAgICAgICBmb3IgKGxldCBpID0gdGhpcy5tYXhMZXZlbDsgaSA+PSB0aGlzLmJhc2VMZXZlbDsgaS0tKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vZmZzZXRzW2ldID0geyBzdGFydENvbCwgc3RhcnRSb3cgfVxuICAgICAgICAgICAgICAgIGxldCByZXN0Q29sID0gZmxvYXRTdGFydENvbCAlIDFcbiAgICAgICAgICAgICAgICBsZXQgcmVzdFJvdyA9IGZsb2F0U3RhcnRSb3cgJSAxXG4gICAgICAgICAgICAgICAgdGhpcy5yZXN0c1tpXSA9IHsgcmVzdENvbCwgcmVzdFJvdyB9XG4gICAgICAgICAgICAgICAgc3RhcnRDb2wgPSBNYXRoLmZsb29yKHN0YXJ0Q29sIC8gMilcbiAgICAgICAgICAgICAgICBzdGFydFJvdyA9IE1hdGguZmxvb3Ioc3RhcnRSb3cgLyAyKVxuICAgICAgICAgICAgICAgIGZsb2F0U3RhcnRDb2wgLz0gMlxuICAgICAgICAgICAgICAgIGZsb2F0U3RhcnRSb3cgLz0gMlxuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgYm91bmRhcnkgPSB0aGlzLnR5cGUgPT09ICdkemknID8gMS4wIDogdHNpemVcbiAgICAgICAgICAgIHdoaWxlICh3dyA+IGJvdW5kYXJ5ICYmIGhoID4gYm91bmRhcnkpIHtcbiAgICAgICAgICAgICAgICBpZiAod3cgPj0gdHNpemUgJiYgaGggPj0gdHNpemUpIHtcbiAgICAgICAgICAgICAgICAgICAgc2luZ2xlICs9IDFcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgc2NhbGUgPSBzY2FsZSAvIDIuMFxuICAgICAgICAgICAgICAgIHd3ID0gTWF0aC5jZWlsKHRoaXMud2lkdGggKiBzY2FsZSlcbiAgICAgICAgICAgICAgICBoaCA9IE1hdGguY2VpbCh0aGlzLmhlaWdodCAqIHNjYWxlKVxuICAgICAgICAgICAgICAgIGxldmVsICs9IDFcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuYmFzZUxldmVsID0gbGV2ZWwgLSBzaW5nbGVcbiAgICAgICAgICAgIHRoaXMubWF4TGV2ZWwgPSB0aGlzLm51bUxldmVscygpIC0gMVxuICAgICAgICAgICAgdGhpcy5iYXNlVVJMID0gdGhpcy51cmxGb3JUaWxlKHRoaXMuYmFzZUxldmVsLCAwLCAwLCBmYWxzZSlcblxuICAgICAgICAgICAgaWYgKGxvYWRCYXNlSW1hZ2UpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmltYWdlRm9yVVJMKHRoaXMuYmFzZVVSTCwgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2l6ZSA9IFtlLnRhcmdldC5uYXR1cmFsV2lkdGgsIGUudGFyZ2V0Lm5hdHVyYWxIZWlnaHRdXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuYmFzZUltYWdlID0gZS50YXJnZXRcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmJhc2VJbWFnZSA9IG51bGxcbiAgICAgICAgICAgICAgICB0aGlzLnNpemUgPSB0aGlzLmdldERpbWVuc2lvbnModGhpcy5iYXNlTGV2ZWwpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBnZXQgbWF4TG9hZGFibGVMZXZlbCgpIHtcbiAgICAgICAgaWYgKHRoaXMuY2xpcCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMubWF4TGV2ZWxcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy50eXBlID09PSAnZHppJyA/IHRoaXMubWF4TGV2ZWwgOiB0aGlzLm1heExldmVsXG4gICAgfVxuXG4gICAgLyoqIENvbXB1dGVzIHRoZSB1cmwgZm9yIHRoZSBnaXZlbiBsZXZlbCwgY29sdW1uIGFuZCBhbmQgcm93LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsZXZlbCBvZiB0aGUgd2FudGVkIGxheWVyXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGNvbHVtbiAtIFRoZSBjb2x1bW4gb2YgdGhlIHRpbGVcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcm93IC0gVGhlIHJvdyBvZiB0aGUgdGlsZVxuICAgICAqIEByZXR1cm5zIHtzdHJpbmd9IHVybFxuICAgICAqKi9cbiAgICB1cmxGb3JUaWxlKGxldmVsLCBjb2x1bW4sIHJvdywgY29tcHJlc3NlZCA9IHRydWUpIHtcbiAgICAgICAgbGV0IGZvcm1hdCA9IHRoaXMuZm9ybWF0XG4gICAgICAgIGlmIChjb21wcmVzc2VkICYmIHRoaXMuY29tcHJlc3Npb24pIHtcbiAgICAgICAgICAgIGxldCBzdXBwb3J0ZWQgPSBDYXBhYmlsaXRpZXMuaXNJT1MgPyAncHZyJyA6ICdkZHMnXG4gICAgICAgICAgICBpZiAodGhpcy5jb21wcmVzc2lvbi5pbmRleE9mKHN1cHBvcnRlZCkgPj0gMCkge1xuICAgICAgICAgICAgICAgIGZvcm1hdCA9IHN1cHBvcnRlZFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLmNsaXApIHtcbiAgICAgICAgICAgIGxldCBvZmZzZXQgPSB0aGlzLm9mZnNldHNbbGV2ZWxdXG4gICAgICAgICAgICBpZiAob2Zmc2V0KSB7XG4gICAgICAgICAgICAgICAgbGV0IHsgc3RhcnRDb2wsIHN0YXJ0Um93IH0gPSBvZmZzZXRcbiAgICAgICAgICAgICAgICBjb2x1bW4gKz0gc3RhcnRDb2xcbiAgICAgICAgICAgICAgICByb3cgKz0gc3RhcnRSb3dcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBsZXQgdXJsID0gdGhpcy51cmxUaWxlVGVtcGxhdGVcbiAgICAgICAgICAgIC5yZXBsYWNlKC9cXHtwYXRoXFx9L2csIHRoaXMucGF0aClcbiAgICAgICAgICAgIC5yZXBsYWNlKC9cXHtsZXZlbFxcfS9nLCBsZXZlbClcbiAgICAgICAgICAgIC5yZXBsYWNlKC9cXHtyb3dcXH0vZywgcm93KVxuICAgICAgICAgICAgLnJlcGxhY2UoL1xce2NvbHVtblxcfS9nLCBjb2x1bW4pXG4gICAgICAgICAgICAucmVwbGFjZSgvXFx7Zm9ybWF0XFx9L2csIGZvcm1hdClcbiAgICAgICAgcmV0dXJuIHVybFxuICAgIH1cblxuICAgIC8qKiBMb2FkcyB0aGUgaW1hZ2UgZm9yIHRoZSBnaXZlbiB1cmwgYW5kIGV4ZWN1dGVzIGEgY2FsbGJhY2sgZnVuY3Rpb25cbiAgICBvbiBjb21wbGV0aW9uLlxuICAgICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSB1cmwgb2YgdGhlIHRpbGVcbiAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IGNvbXBsZXRlIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uXG4gICAgKiBAcmV0dXJucyB7SW1hZ2V9IG9ialxuICAgICoqL1xuICAgIGltYWdlRm9yVVJMKHVybCwgY29tcGxldGUpIHtcbiAgICAgICAgbGV0IGltZyA9IG5ldyBJbWFnZSgpXG4gICAgICAgIGltZy5vbmxvYWQgPSBjb21wbGV0ZS5iaW5kKHRoaXMpXG4gICAgICAgIGltZy5zcmMgPSB1cmxcbiAgICAgICAgcmV0dXJuIGltZ1xuICAgIH1cblxuICAgIC8qKiBDb21wdXRlcyB0aGUgY29sdW1ucyBhbmQgcm93cyBhcyB3ZWxsIGFzIHNjYWxlZCB3aWR0aCBhbmQgaGVpZ2h0LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsZXZlbCBvZiB0aGUgd2FudGVkIGxheWVyXG4gICAgICogQHJldHVybnMge2FycmF5fSBbY29scywgcm93cywgd2lkdGgsIGhlaWdodF1cbiAgICAgKiovXG4gICAgZGltZW5zaW9ucyhsZXZlbCkge1xuICAgICAgICBsZXQgZGltID0gdGhpcy5nZXREaW1lbnNpb25zKGxldmVsKVxuICAgICAgICBsZXQgdGlsZXMgPSB0aGlzLmdldE51bVRpbGVzKGxldmVsKVxuICAgICAgICByZXR1cm4gW3RpbGVzWzBdLCB0aWxlc1sxXSwgZGltWzBdLCBkaW1bMV1dXG4gICAgfVxuXG4gICAgdGVzdCgpIHtcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcInc9XCIgKyB0aGlzLndpZHRoICsgXCIgaD1cIiArIHRoaXMuaGVpZ2h0ICsgXCIgbWF4bGV2ZWw9XCIgKyB0aGlzLm1heExldmVsICsgXCIgYmFzZT1cIiArIHRoaXMuYmFzZUxldmVsKVxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8PSB0aGlzLm1heExldmVsOyBpKyspIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICcgJyArXG4gICAgICAgICAgICAgICAgaSArXG4gICAgICAgICAgICAgICAgJyAtPiAnICtcbiAgICAgICAgICAgICAgICB0aGlzLmdldFNjYWxlKGkpICtcbiAgICAgICAgICAgICAgICAnIFsnICtcbiAgICAgICAgICAgICAgICB0aGlzLmRpbWVuc2lvbnMoaSkgK1xuICAgICAgICAgICAgICAgICddJ1xuICAgICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUubG9nKHRoaXMudXJsRm9yVGlsZSh0aGlzLmJhc2VMZXZlbCwgMCwgMCkpXG4gICAgfVxufVxuXG4vKipcbiAqIEEgdXRpbGl0eSBjbGFzcyB0aGF0IGRlc2NyaWJlcyBhIHF1YWQgdHJlZSBvZiB0aWxlcy4gRWFjaCB0aWxlIG9uIGEgZ2l2ZW5cbiAqIGxldmVsIGhhcyB1cCB0byBmb3VyIGNvcnJlc3BvbmRpbmcgdGlsZXMgb24gdGhlIG5leHQgbGV2ZWwuIEEgVGlsZVF1YWROb2RlXG4gKiB1c2VzIHRoZSBhdHRyaWJ1dGVzIG53IChpLmUuIG5vcnRod2VzdCksIG5lLCBzdywgc2UgdG8gbGluayB0byB0aGVcbiAqIHF1YWQgbm9kZXMgb24gdGhlIG5leHQgbGV2ZWwuIFRoZSBwcmV2aW91cyBhdHRyaWJ1dGVzIGxpbmtzIHRvIHRoZSBxdWFkXG4gKiBvbmUgbGV2ZWwgYmVsb3cgdGhhdCBob2xkcyB0aGUgZ2l2ZW4gcXVhZCBhcyBudywgbmUsIHN3LCBvciBzZS5cbiAqIFdlIHVzZSB0aGlzIG5vZGUgY2xhc3MgYmVjYXVzZSB3ZSBuZWVkIGEgcmVwcmVzZW50YXRpb24gb2YgdGlsZXMgdGhhdCBhcmVcbiAqIG5lZWRlZCBidXQgbm90IGxvYWRlZCB5ZXQgdG8gY29tcHV0ZSB0aWxlcyB3aGljaCBjYW4gYmUgYWJhbmRvbmVkIHRvIHJlZHVjZVxuICogdGhlIG1lbW9yeSBwcmVzc3VyZS5cbiAqXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwYXJhbSB7bGV2ZWx9IE51bWJlciAtIFRoZSBsZXZlbCB0aGUgcXVhZCBub2RlIGJlbG9uZ3MgdG9cbiAqIEBwYXJhbSB7Y29sfSBOdW1iZXIgLSBUaGUgY29sIG9mIHRoZSBxdWFkXG4gKiBAcGFyYW0ge3Jvd30gTnVtYmVyIC0gVGhlIGxldmVsIHRoZSBxdWFkIG5vZGUgYmVsb25ncyB0b1xuICogQHBhcmFtIHt1cmx9IFN0cmluZyAtIFRoZSBsZXZlbCB0aGUgcXVhZCBub2RlIGJlbG9uZ3MgdG9cbiAqL1xuY2xhc3MgVGlsZVF1YWROb2RlIHtcbiAgICBjb25zdHJ1Y3RvcihsZXZlbCwgY29sLCByb3csIHVybCkge1xuICAgICAgICB0aGlzLmxldmVsID0gbGV2ZWxcbiAgICAgICAgdGhpcy5jb2wgPSBjb2xcbiAgICAgICAgdGhpcy5yb3cgPSByb3dcbiAgICAgICAgdGhpcy51cmwgPSB1cmxcbiAgICAgICAgdGhpcy5udyA9IG51bGxcbiAgICAgICAgdGhpcy5uZSA9IG51bGxcbiAgICAgICAgdGhpcy5zdyA9IG51bGxcbiAgICAgICAgdGhpcy5zZSA9IG51bGxcbiAgICAgICAgdGhpcy5wcmV2aW91cyA9IG51bGxcbiAgICB9XG5cbiAgICAvKiogUmV0dXJuIFRydWUgaWYgdGhpcyBub2RlIGhhcyBubyBzdWNjZXNzb3JzIGFuZCBjYW4gYmUgdXNlZCBhc1xuICAgIGFuIGluZGljYXRvciBvZiB0aWxlcyB0byBmcmVlLlxuICAgICoqL1xuICAgIG5vUXVhZHMoKSB7XG4gICAgICAgIGlmICh0aGlzLnByZXZpb3VzID09PSBudWxsKSByZXR1cm4gZmFsc2VcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIHRoaXMubncgPT09IG51bGwgJiZcbiAgICAgICAgICAgIHRoaXMubmUgPT09IG51bGwgJiZcbiAgICAgICAgICAgIHRoaXMuc3cgPT09IG51bGwgJiZcbiAgICAgICAgICAgIHRoaXMuc2UgPT09IG51bGxcbiAgICAgICAgKVxuICAgIH1cblxuICAgIC8qKiBVbmxpbmsgdGhlIGdpdmVuIHF1YWQgbm9kZVxuXG4gICAgKiBAcGFyYW0ge25vZGV9IFRpbGVRdWFkTm9kZSAtIFRoZSBUaWxlUXVhZE5vZGUgdG8gcmVtb3ZlXG4gICAgKiovXG4gICAgdW5saW5rKG5vZGUpIHtcbiAgICAgICAgaWYgKHRoaXMubncgPT09IG5vZGUpIHRoaXMubncgPSBudWxsXG4gICAgICAgIGlmICh0aGlzLm5lID09PSBub2RlKSB0aGlzLm5lID0gbnVsbFxuICAgICAgICBpZiAodGhpcy5zdyA9PT0gbm9kZSkgdGhpcy5zdyA9IG51bGxcbiAgICAgICAgaWYgKHRoaXMuc2UgPT09IG5vZGUpIHRoaXMuc2UgPSBudWxsXG4gICAgfVxuXG4gICAgLyoqIExpbmsgdGhpcyBxdWFkIG5vZGUgdG8gdGhlIGdpdmVuIHByZXZpb3VzIG5vZGUuIFVzZSB0aGUgbm9ydGhcbiAgICAqIGFuZCB3ZXN0IGZsYWdzIHRvIGFkZHJlc3MgbncsIG5lLCBzdywgYW5kIHNlLlxuXG4gICAgKiBAcGFyYW0ge25vZGV9IFRpbGVRdWFkTm9kZSAtIFRoZSBUaWxlUXVhZE5vZGUgdG8gcmVtb3ZlXG4gICAgKiBAcGFyYW0ge25vcnRofSBCb29sZWFuIC0gTGluayB0byBub3J0aCAodHJ1ZSkgb3Igc291dGggKGZhbHNlKVxuICAgICogQHBhcmFtIHt3ZXN0fSBCb29sZWFuIC0gTGluayB0byB3ZXN0ICh0cnVlKSBvciBlYXN0IChmYWxzZSlcbiAgICAqKi9cbiAgICBsaW5rKG5vcnRoLCB3ZXN0LCBwcmV2aW91cykge1xuICAgICAgICB0aGlzLnByZXZpb3VzID0gcHJldmlvdXNcbiAgICAgICAgaWYgKG5vcnRoKSB7XG4gICAgICAgICAgICBpZiAod2VzdCkge1xuICAgICAgICAgICAgICAgIHByZXZpb3VzLm53ID0gdGhpc1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBwcmV2aW91cy5uZSA9IHRoaXNcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICh3ZXN0KSB7XG4gICAgICAgICAgICAgICAgcHJldmlvdXMuc3cgPSB0aGlzXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHByZXZpb3VzLnNlID0gdGhpc1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxufVxuXG5cbi8qKlxuICogVGhlIG1haW4gY2xhc3Mgb2YgYSBkZWVwbHkgem9vbWFibGUgaW1hZ2UgdGhhdCBpcyByZXByZXNlbnRlZCBieSBhIGhpZXJhcmNoeVxuICogb2YgdGlsZSBsYXllcnMgZm9yIGVhY2ggem9vbSBsZXZlbC4gVGhpcyBnaXZlcyB0aGUgdXNlciB0aGUgaW1wcmVzc2lvbiB0aGF0XG4gKiBldmVuIGh1Z2UgcGljdHVyZXMgKHVwIHRvIGdpZ2FwaXhlbC1pbWFnZXMpIGNhbiBiZSB6b29tZWQgaW5zdGFudGFuZW91c2x5LFxuICogc2luY2UgdGhlIHRpbGVzIGF0IHNtYWxsZXIgbGV2ZWxzIGFyZSBzY2FsZWQgaW1tZWRpYXRlbHkgYW5kIG92ZXJsb2FkZWQgYnlcbiAqIG1vcmUgZGV0YWlsZWQgdGlsZXMgYXQgdGhlIGxhcmdlciBsZXZlbCBhcyBmYXN0IGFzIHBvc3NpYmxlLlxuXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwYXJhbSB7RGVlcFpvb21JbmZvfSBkZWVwWm9vbUluZm8gLSBJbmZvcm1hdGlvbiBleHRyYWN0ZWQgZnJvbSBhIEpTT04tT2JqZWN0XG4gKi9cbmV4cG9ydCBjbGFzcyBEZWVwWm9vbUltYWdlIGV4dGVuZHMgUElYSS5Db250YWluZXIge1xuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBkZWVwWm9vbUluZm8sXG4gICAgICAgIHtcbiAgICAgICAgICAgIGRlYnVnID0gZmFsc2UsXG4gICAgICAgICAgICBzaGFkb3cgPSBmYWxzZSxcbiAgICAgICAgICAgIGNlbnRlciA9IGZhbHNlLFxuICAgICAgICAgICAgd29ybGQgPSBudWxsLCAgICAgICAgICAgICAgIC8vIERlZmluZXMgdGhlIHdvcmxkIGJvdW5kcyB0aGUgaW1hZ2VzIGxpdmVzIGluXG4gICAgICAgICAgICBoaWdoUmVzb2x1dGlvbiA9IHRydWUsXG4gICAgICAgICAgICBhdXRvTG9hZFRpbGVzID0gdHJ1ZSxcbiAgICAgICAgICAgIHByZWZlcldvcmtlciA9IGZhbHNlLFxuICAgICAgICAgICAgbWluaW11bUxldmVsID0gMCxcbiAgICAgICAgICAgIGFscGhhID0gMSxcbiAgICAgICAgICAgIGFwcCA9IHdpbmRvdy5hcHBcbiAgICAgICAgfSA9IHt9XG4gICAgKSB7XG4gICAgICAgIHN1cGVyKClcbiAgICAgICAgdGhpcy5hcHAgPSBhcHBcbiAgICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnXG4gICAgICAgIHRoaXMuc2hhZG93ID0gc2hhZG93XG4gICAgICAgIHRoaXMud29ybGQgPSB3b3JsZFxuICAgICAgICB0aGlzLnByZWZlcldvcmtlciA9IHByZWZlcldvcmtlclxuICAgICAgICB0aGlzLnJlc29sdXRpb24gPSBoaWdoUmVzb2x1dGlvblxuICAgICAgICAgICAgPyBNYXRoLnJvdW5kKHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKVxuICAgICAgICAgICAgOiAxXG4gICAgICAgIHRoaXMuYWxwaGEgPSBhbHBoYVxuICAgICAgICB0aGlzLmZhc3RMb2FkcyA9IDBcbiAgICAgICAgdGhpcy5hdXRvTG9hZFRpbGVzID0gYXV0b0xvYWRUaWxlc1xuICAgICAgICB0aGlzLm1pbmltdW1MZXZlbCA9IG1pbmltdW1MZXZlbFxuICAgICAgICB0aGlzLnF1YWRUcmVlcyA9IG5ldyBNYXAoKSAvLyB1cmwgYXMga2V5cywgVGlsZVF1YWROb2RlcyBhcyB2YWx1ZXNcbiAgICAgICAgdGhpcy5zZXR1cChkZWVwWm9vbUluZm8sIGNlbnRlcilcbiAgICB9XG5cbiAgICBnZXQgcG9pbnQoKSB7XG4gICAgICAgIGlmICh0aGlzLl9wb2ludCA9PSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgZ3JhcGhpY3MgPSBuZXcgUElYSS5HcmFwaGljcygpXG4gICAgICAgICAgICBncmFwaGljcy5saW5lU3R5bGUoMiwgMHgwMGZmMDApXG4gICAgICAgICAgICBncmFwaGljcy5kcmF3Q2lyY2xlKDAsIDAsIDIpXG4gICAgICAgICAgICBncmFwaGljcy5pbnRlcmFjdGl2ZSA9IGZhbHNlXG4gICAgICAgICAgICB0aGlzLl9wb2ludCA9IGdyYXBoaWNzXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuX3BvaW50XG4gICAgfVxuXG4gICAgLyoqIFJlYWRzIHRoZSBEZWVwWm9vbUluZm8gb2JqZWN0IGFuZCBpbml0aWFsaXplcyBhbGwgdGlsZSBsYXllcnMuXG4gICAgICogQ2FsbGVkIGJ5IHRoZSBjb25zdHJ1Y3Rvci5cbiAgICAgKiBDcmVhdGVzIHRoZSBzcHJpdGUgZm9yIHRoZSBsb2FkZWQgdGV4dHVyZSBhbmQgYWRkIHRoZSBzcHJpdGUgdG8gdGhlIHRpbGVcbiAgICAgKiBsYXllci5cbiAgICAgKiBAcGFyYW0ge09iamVjdH0gZGVlcFpvb21JbmZvIC0gdGhlIERlZXBab29tSW5mbyBpbnN0YW5jZVxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gY2VudGVyIC0gSWYgdHJ1ZSBlbnN1cmVzIHRoYXQgdGhlIHBpdm90IGlzIHNldCB0byB0aGUgY2VudGVyXG4gICAgICoqL1xuICAgIHNldHVwKGRlZXBab29tSW5mbywgY2VudGVyKSB7XG4gICAgICAgIHRoaXMuaW5mbyA9IGRlZXBab29tSW5mb1xuICAgICAgICB0aGlzLmludGVyYWN0aXZlID0gdHJ1ZVxuICAgICAgICB0aGlzLnRpbGVMYXllcnMgPSB7fVxuXG4gICAgICAgIHRoaXMuX2ZvcmVncm91bmQgPSBudWxsXG4gICAgICAgIHRoaXMudGlsZUNvbnRhaW5lciA9IG5ldyBQSVhJLkNvbnRhaW5lcigpXG4gICAgICAgIHRoaXMudGlsZUNvbnRhaW5lci5pbnRlcmFjdGl2ZSA9IGZhbHNlXG5cbiAgICAgICAgbGV0IFt3LCBoXSA9IHRoaXMuYmFzZVNpemVcbiAgICAgICAgaWYgKHRoaXMuc2hhZG93KSB7XG4gICAgICAgICAgICB0aGlzLmZpbHRlcnMgPSBbbmV3IFBJWEkuZmlsdGVycy5Ecm9wU2hhZG93RmlsdGVyKDQ1LCAzKV1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLmFkZENoaWxkKHRoaXMudGlsZUNvbnRhaW5lcilcblxuICAgICAgICBpZiAoZGVlcFpvb21JbmZvLmNsaXApIHtcbiAgICAgICAgICAgIGxldCBtYXNrID0gbmV3IFBJWEkuR3JhcGhpY3MoKVxuICAgICAgICAgICAgbWFzay5iZWdpbkZpbGwoMSwgMSlcbiAgICAgICAgICAgIG1hc2suZHJhd1JlY3QoMCwgMCwgdywgaClcbiAgICAgICAgICAgIG1hc2suZW5kRmlsbCgpXG4gICAgICAgICAgICB0aGlzLm1hc2sgPSBtYXNrXG4gICAgICAgICAgICBtYXNrLmFscGhhID0gMFxuICAgICAgICAgICAgdGhpcy5hZGRDaGlsZChtYXNrKVxuICAgICAgICAgICAgdGhpcy5taW5pbXVtTGV2ZWwgPSBkZWVwWm9vbUluZm8uYmFzZUxldmVsXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5jdXJyZW50TGV2ZWwgPSBNYXRoLm1heCh0aGlzLm1pbmltdW1MZXZlbCwgZGVlcFpvb21JbmZvLmJhc2VMZXZlbClcbiAgICAgICAgY29uc29sZS5sb2coXCJhdXRvTG9hZFRpbGVzXCIsIHRoaXMuYXV0b0xvYWRUaWxlcylcbiAgICAgICAgaWYgKHRoaXMuYXV0b0xvYWRUaWxlcykge1xuICAgICAgICAgICAgdGhpcy5zZXR1cFRpbGVzKGNlbnRlcilcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKiBEZWZhdWx0IHNldHVwIG1ldGhvZCBmb3IgdGlsZXMuIExvYWRzIGFsbCB0aWxlcyBvZiB0aGUgY3VycmVudCBsZXZlbC5cbiAgICBDYW4gYmUgb3ZlcndyaXR0ZW4gaW4gc3ViY2xhc3Nlcy5cbiAgICBAcGFyYW0ge2Jvb2xlYW59IGNlbnRlciAtIElmIHRydWUgZW5zdXJlcyB0aGF0IHRoZSBwaXZvdCBpcyBzZXQgdG8gdGhlIGNlbnRlclxuICAgICoqL1xuICAgIHNldHVwVGlsZXMoY2VudGVyID0gZmFsc2UpIHtcbiAgICAgICAgLy8gRmlyc3QgbG9hZCBiYWNrZ3JvdW5kIHRpbGVcbiAgICAgICAgbGV0IHRpbGVzID0gdGhpcy5lbnN1cmVBbGxUaWxlcyh0aGlzLmN1cnJlbnRMZXZlbClcbiAgICAgICAgaWYgKGNlbnRlcikge1xuICAgICAgICAgICAgdGhpcy5waXZvdC5zZXQodyAvIDIsIGggLyAyKVxuICAgICAgICB9XG4gICAgICAgIGxldCBzY2FsZUxldmVsID0gdGhpcy5sZXZlbEZvclNjYWxlKDEpXG4gICAgICAgIHRoaXMuZW5zdXJlQWxsVGlsZXMoc2NhbGVMZXZlbClcbiAgICB9XG5cbiAgICByZW1vdmVUaWxlUXVhZE5vZGUobGV2ZWwsIGNvbCwgcm93LCB1cmwpIHtcbiAgICAgICAgaWYgKHRoaXMucXVhZFRyZWVzLmhhcyh1cmwpKSB7XG4gICAgICAgICAgICBsZXQgcXVhZCA9IHRoaXMucXVhZFRyZWVzLmdldCh1cmwpXG4gICAgICAgICAgICB0aGlzLnRpbGVRdWFkUmVtb3ZlZChxdWFkKVxuICAgICAgICAgICAgdGhpcy5xdWFkVHJlZXMuZGVsZXRlKHVybClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGFkZFRpbGVRdWFkTm9kZShsZXZlbCwgY29sLCByb3csIHVybCkge1xuICAgICAgICBpZiAodGhpcy5xdWFkVHJlZXMuaGFzKHVybCkpIHJldHVybiB0aGlzLnF1YWRUcmVlcy5nZXQodXJsKVxuICAgICAgICBsZXQgcXVhZCA9IG5ldyBUaWxlUXVhZE5vZGUobGV2ZWwsIGNvbCwgcm93LCB1cmwpXG4gICAgICAgIHRoaXMucXVhZFRyZWVzLnNldCh1cmwsIHF1YWQpXG4gICAgICAgIHRoaXMudGlsZVF1YWRBZGRlZChxdWFkKVxuICAgICAgICByZXR1cm4gcXVhZFxuICAgIH1cblxuICAgIHRpbGVRdWFkUmVtb3ZlZChxdWFkKSB7XG4gICAgICAgIGxldCBiZWxvdyA9IHF1YWQucHJldmlvdXNcbiAgICAgICAgLy8gaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKFwidGlsZVF1YWRSZW1vdmVkXCIsIHF1YWQpXG4gICAgICAgIGlmIChiZWxvdykge1xuICAgICAgICAgICAgYmVsb3cudW5saW5rKHF1YWQpXG4gICAgICAgICAgICBpZiAoYmVsb3cubm9RdWFkcygpKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUubG9nKCdSZW1vdmVkIHRpbGUgYmVsb3cnKVxuICAgICAgICAgICAgICAgIGxldCBsZXZlbEJlbG93ID0gcXVhZC5sZXZlbCAtIDFcbiAgICAgICAgICAgICAgICBpZiAobGV2ZWxCZWxvdyA8IHRoaXMubWluaW11bUxldmVsKSByZXR1cm5cbiAgICAgICAgICAgICAgICBsZXQgYyA9IE1hdGguZmxvb3IocXVhZC5jb2wgLyAyKVxuICAgICAgICAgICAgICAgIGxldCByID0gTWF0aC5mbG9vcihxdWFkLnJvdyAvIDIpXG4gICAgICAgICAgICAgICAgbGV0IHVybEJlbG93ID0gdGhpcy5pbmZvLnVybEZvclRpbGUobGV2ZWxCZWxvdywgYywgcilcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5xdWFkVHJlZXMuaGFzKHVybEJlbG93KSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnJlbW92ZVRpbGVRdWFkTm9kZShsZXZlbEJlbG93LCBjLCByLCB1cmxCZWxvdylcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB0aWxlUXVhZEFkZGVkKHF1YWQpIHtcbiAgICAgICAgbGV0IGxldmVsQmVsb3cgPSBxdWFkLmxldmVsIC0gMVxuICAgICAgICBpZiAobGV2ZWxCZWxvdyA8IHRoaXMubWluaW11bUxldmVsKSByZXR1cm5cbiAgICAgICAgLy9pZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2coXCJ0aWxlUXVhZEFkZGVkXCIsIHF1YWQpXG4gICAgICAgIGxldCBjID0gTWF0aC5mbG9vcihxdWFkLmNvbCAvIDIpXG4gICAgICAgIGxldCByID0gTWF0aC5mbG9vcihxdWFkLnJvdyAvIDIpXG4gICAgICAgIGxldCB1cmxCZWxvdyA9IHRoaXMuaW5mby51cmxGb3JUaWxlKGxldmVsQmVsb3csIGMsIHIpXG4gICAgICAgIGxldCBiZWxvdyA9IG51bGxcbiAgICAgICAgaWYgKCF0aGlzLnF1YWRUcmVlcy5oYXModXJsQmVsb3cpKSB7XG4gICAgICAgICAgICBiZWxvdyA9IHRoaXMuYWRkVGlsZVF1YWROb2RlKGxldmVsQmVsb3csIGMsIHIsIHVybEJlbG93KVxuICAgICAgICAgICAgcXVhZC5saW5rKGlzRXZlbihxdWFkLnJvdyksIGlzRXZlbihxdWFkLmNvbCksIGJlbG93KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqIFJldHVybnMgdGhlIHRpbGUgbGF5ZXIgbGV2ZWwgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgZ2l2ZW4gc2NhbGUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHNjYWxlIC0gdGhlIHNjYWxlIGZhY3RvclxuICAgICAqKi9cbiAgICBsZXZlbEZvclNjYWxlKHNjYWxlKSB7XG4gICAgICAgIGxldCBsZXZlbCA9IE1hdGgucm91bmQoTWF0aC5sb2cyKHNjYWxlICogdGhpcy5yZXNvbHV0aW9uKSkgLy8gTWF0aC5mbG9vcihNYXRoLmxvZzIoZXZlbnQuc2NhbGUpKSsxXG4gICAgICAgIGxldCBuZXdMZXZlbCA9IHRoaXMuaW5mby5iYXNlTGV2ZWwgKyBNYXRoLm1heChsZXZlbCwgMClcbiAgICAgICAgcmV0dXJuIE1hdGgubWluKG5ld0xldmVsLCB0aGlzLmluZm8ubWF4TG9hZGFibGVMZXZlbClcbiAgICB9XG5cbiAgICAvKiogUmV0dXJucyB0aGUgdGlsZSBsYXllciBsZXZlbCB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBnaXZlbiBzY2FsZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gc2NhbGUgLSB0aGUgc2NhbGUgZmFjdG9yXG4gICAgICoqL1xuICAgIGxldmVsQW5kQWxwaGFGb3JTY2FsZShzY2FsZSkge1xuICAgICAgICBsZXQgdmFsdWUgPSBNYXRoLmxvZzIoc2NhbGUgKiB0aGlzLnJlc29sdXRpb24pXG4gICAgICAgIGxldCBsZXZlbCA9IE1hdGgucm91bmQodmFsdWUpXG4gICAgICAgIGxldCBuZXdMZXZlbCA9IHRoaXMuaW5mby5iYXNlTGV2ZWwgKyBNYXRoLm1heChsZXZlbCwgMClcblxuICAgICAgICByZXR1cm4geyBsZXZlbDogTWF0aC5taW4obmV3TGV2ZWwsIHRoaXMuaW5mby5tYXhMb2FkYWJsZUxldmVsKSwgYWxwaGE6IHZhbHVlIC0gbGV2ZWwgfVxuICAgIH1cblxuICAgIC8qKiBBZGRzIGEgdGlsZSBsYXllciB0byB0aGUgRGVlcFpvb21JbWFnZS5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30ga2V5IC0gdGhlIGFjY2VzcyBrZXlcbiAgICAgKiBAcGFyYW0ge1RpbGVzfSB0aWxlcyAtIHRoZSB0aWxlIGxheWVyIG9iamVjdFxuICAgICAqKi9cbiAgICBhZGRUaWxlcyhrZXksIHRpbGVzKSB7XG4gICAgICAgIGlmIChrZXkgaW4gdGhpcy50aWxlTGF5ZXJzKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oJ1RpbGVzIGFscmVhZHkgYXZhaWxhYmwnLCBrZXkpXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy50aWxlQ29udGFpbmVyLmFkZENoaWxkKHRpbGVzKVxuICAgICAgICB0aGlzLnRpbGVMYXllcnNba2V5XSA9IHRpbGVzXG4gICAgfVxuXG4gICAgZGVzdHJveVRpbGVzKGtleSkge1xuICAgICAgICBsZXQgdGlsZXMgPSB0aGlzLnRpbGVMYXllcnNba2V5XVxuICAgICAgICB0aGlzLnRpbGVDb250YWluZXIucmVtb3ZlQ2hpbGQodGlsZXMpXG4gICAgICAgIHRpbGVzLmRlc3Ryb3koKVxuICAgICAgICBkZWxldGUgdGhpcy50aWxlTGF5ZXJzW2tleV1cbiAgICB9XG5cbiAgICAvKiogR2V0dGVyIGZvciBQSVhJLkNvbnRhaW5lciBmb3JlZ3JvdW5kIGxheWVyLlxuICAgICAqIEFkZHMgYSBQSVhJLkNvbnRhaW5lciBpZiBuZWNlc3NhcnkuXG4gICAgICoqL1xuICAgIGdldCBmb3JlZ3JvdW5kKCkge1xuICAgICAgICBpZiAodGhpcy5fZm9yZWdyb3VuZCA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLl9mb3JlZ3JvdW5kID0gbmV3IFBJWEkuQ29udGFpbmVyKClcbiAgICAgICAgICAgIHRoaXMuYWRkQ2hpbGQodGhpcy5fZm9yZWdyb3VuZClcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5fZm9yZWdyb3VuZFxuICAgIH1cblxuICAgIC8qKiBHZXR0ZXIgZm9yIHRoZSBEZWVwWm9vbUluZm8gYmFzZSBsZXZlbCBzaXplLlxuICAgICAqKi9cbiAgICBnZXQgYmFzZVNpemUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmluZm8uZ2V0RGltZW5zaW9ucyh0aGlzLmluZm8uYmFzZUxldmVsKVxuICAgIH1cblxuICAgIC8qKiBHZXR0ZXIgZm9yIHRoZSBjdXJyZW50IHNjYWxlZCBzaXplIGluIHBpeGVscy5cbiAgICAgKiovXG4gICAgZ2V0IHBpeGVsU2l6ZSgpIHtcbiAgICAgICAgbGV0IFt3LCBoXSA9IHRoaXMuYmFzZVNpemVcbiAgICAgICAgcmV0dXJuIFt3ICogdGhpcy5zY2FsZS54LCBoICogdGhpcy5zY2FsZS55XVxuICAgIH1cblxuICAgIC8qKiBHZXR0ZXIgZm9yIHRoZSBtYXggc2NhbGUgZmFjdG9yLlxuICAgICAqKi9cbiAgICBnZXQgbWF4U2NhbGUoKSB7XG4gICAgICAgIGxldCBkZWx0YSA9IHRoaXMuaW5mby5tYXhMZXZlbCAtIHRoaXMuaW5mby5iYXNlTGV2ZWxcbiAgICAgICAgcmV0dXJuIE1hdGgucG93KDIsIGRlbHRhKSAvIHRoaXMucmVzb2x1dGlvbiAqIDJcbiAgICB9XG5cbiAgICAvKiogR2V0dGVyIGZvciB0aGUgY3VycmVudCB3aWR0aC5cbiAgICAgKiovXG4gICAgZ2V0IHdpZHRoKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5waXhlbFNpemVbMF1cbiAgICB9XG5cbiAgICAvKiogR2V0dGVyIGZvciB0aGUgY3VycmVudCBoZWlnaHQuXG4gICAgICoqL1xuICAgIGdldCBoZWlnaHQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBpeGVsU2l6ZVsxXVxuICAgIH1cblxuXG4gICAgLyogT3ZlcnJpZGVzIFBJWEkuQ29udGFpbmVyLmhpdEFyZWEoKVxuICAgICAqIEFsbG93cyB0byBvcHRpbWl6ZSB0aGUgaGl0IHRlc3RpbmcuIENvbnRhaW5lciB3aXRoIGhpdCBhcmVhcyBhcmUgZGlyZWN0bHlcbiAgICAgKiBoaXQgdGVzdGVkIHdpdGhvdXQgY29uc2lkZXJhdGlvbiBvZiBjaGlsZHJlbi5cbiAgICAgKi9cbiAgICBnZXQgaGl0QXJlYSgpIHtcbiAgICAgICAgLy8gRGVmaW5pbmcgdGhlIGhpdEFyZWEgcmVzdWx0ZWQgaGl0dGluZyB0aGUgc2NhdHRlciBpbiBtYXNrZWQgYXJlYVxuICAgICAgICAvLyB3aGVuIGEgbWFzayB3YXMgdXNlZCAoQFTDvHNjaFtzdWJtYXBzXSkuIFJlbW92aW5nIHRoZSBoaXRBcmVhKCkgYWx0b2dldGhlclxuICAgICAgICAvLyBicm9rZSB0aGUgaW50ZXJhY3Rpb24gaW4gb3RoZXIgcHJvamVjdHMgKEBnb29nbGVhcnQpLlxuICAgICAgICAvLyBGaXg6IFdoZW4gbWFza2VkLCB0aGUgaGl0QXJlYSBpcyBpZ25vcmVkIGJ5IHJldHVybmluZyBudWxsLlxuICAgICAgICAvLyBUT0RPOiB0ZXN0IGlmIGNoaWxkcyBhcmUgaGl0dGVzdGVkLCB3aXRob3V0IHNldHRpbmcgaW50ZXJhY3RpdmVDaGlsZHJlbi5cbiAgICAgICAgLy8gT3BlbCwgMDMtMDUtMjAxOFxuICAgICAgICBpZiAodGhpcy5tYXNrKSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyogT3ZlcnJpZGVzIFBJWEkuQ29udGFpbmVyLmNvbnRhaW5zKClcbiAgICAgKiBBbGxvd3MgdG8gb3B0aW1pemUgdGhlIGhpdCB0ZXN0aW5nLlxuICAgICAqL1xuICAgIGNvbnRhaW5zKHgsIHkpIHtcbiAgICAgICAgbGV0IFt3LCBoXSA9IHRoaXMuYmFzZVNpemVcbiAgICAgICAgcmV0dXJuIHggPj0gMCAmJiB4IDw9IHcgJiYgeSA+PSAwICYmIHkgPD0gaFxuICAgIH1cblxuICAgIC8qKiBPdmVycmlkZXMgUElYSS5Db250YWluZXIuX2NhbGN1bGF0ZUJvdW5kcygpXG4gICAgICogT25seSBjb25zaWRlcnMgdGhlIGJhc2Ugc2l6ZSBhbmQgcmVkdWNlcyB0aGUgY2FsY3VsYXRpb24gdG8gYSBzaW5nbGVcbiAgICAgKiByZWN0LlxuICAgICAqL1xuICAgIF9jYWxjdWxhdGVCb3VuZHMoKSB7XG4gICAgICAgIGxldCBbdywgaF0gPSB0aGlzLmJhc2VTaXplXG4gICAgICAgIHRoaXMuX2JvdW5kcy5hZGRGcmFtZSh0aGlzLnRyYW5zZm9ybSwgMCwgMCwgdywgaClcbiAgICB9XG5cbiAgICAvKiogT3ZlcnJpZGVzIFBJWEkuQ29udGFpbmVyLmNhbGN1bGF0ZUJvdW5kcygpXG4gICAgICogU2tpcHMgdGhlIGNoaWxkcmVuIGFuZCBvbmx5IGNvbnNpZGVycyB0aGUgZGVlcCB6b29tIGJhc2Ugc2l6ZS4gQ2FsbHNcbiAgICAgKiB0aGUgYWxzbyBvdmVyd3JpdHRlbiBfY2FsY3VsYXRlQm91bmRzIG1ldGhvZC5cbiAgICAgKi9cbiAgICBjYWxjdWxhdGVCb3VuZHMoKSB7XG4gICAgICAgIHRoaXMuX2JvdW5kcy5jbGVhcigpXG4gICAgICAgIHRoaXMuX2NhbGN1bGF0ZUJvdW5kcygpXG4gICAgICAgIHRoaXMuX2xhc3RCb3VuZHNJRCA9IHRoaXMuX2JvdW5kc0lEXG4gICAgfVxuXG4gICAgLyoqIFJldHVybnMgYSBzaW5nbGUgc3ByaXRlIHRoYXQgY2FuIGJlIHVzZWQgYSB0aHVtYm5haWwgcmVwcmVzZW50YXRpb24gb2ZcbiAgICAgKiBsYXJnZSBpbWFnZXMuXG4gICAgICogQHJldHVybiB7U3ByaXRlfSBzcHJpdGUgLSBBIHNwcml0ZSB3aXRoIGEgc2luZ2xlIHRpbGUgdGV4dHVyZVxuICAgICAqL1xuICAgIHRodW1ibmFpbCgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQSVhJLlNwcml0ZS5mcm9tSW1hZ2UodGhpcy5pbmZvLmJhc2VVUkwpXG4gICAgfVxuXG4gICAgLyoqIFJldHVybnMgYSBsaXN0IG9mIGFsbCB0aWxlcyBvZiBhIGdpdmVuIGxldmVsLlxuICAgICAqIEBwYXJhbSB7VGlsZXN9IHRpbGVzIC0gdGhlIGdyaWQgb2YgdGlsZXNcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgem9vbSBsZXZlbCBvZiB0aGUgZ3JpZFxuICAgICAqIEByZXR1cm4ge0FycmF5W119IC0gQW4gYXJyYXkgb2YgW3VybCwgY29sLCByb3ddIGFycmF5c1xuICAgICAqKi9cbiAgICBhbGxUaWxlcyh0aWxlcywgbGV2ZWwpIHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IFtdXG4gICAgICAgIGZvciAobGV0IGNvbCA9IDA7IGNvbCA8IHRpbGVzLmNvbHM7IGNvbCsrKSB7XG4gICAgICAgICAgICBmb3IgKGxldCByb3cgPSAwOyByb3cgPCB0aWxlcy5yb3dzOyByb3crKykge1xuICAgICAgICAgICAgICAgIGxldCB1cmwgPSB0aGlzLmluZm8udXJsRm9yVGlsZShsZXZlbCwgY29sLCByb3cpXG4gICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goW3VybCwgY29sLCByb3ddKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG5cbiAgICB3b3JsZEJvdW5kcygpIHtcbiAgICAgICAgbGV0IHZpZXdCb3VuZHMgPSB0aGlzLmFwcC5zY2VuZS5ib3VuZHNcbiAgICAgICAgLy8gVXNpbmcgZ2V0Qm91bmRzIGV4dGVuZHMgdmlzaWJsZSBzY29wZSBhZnRlciBsb2FkaW5nIHRpbGVzIGFuZCBsZWFkc1xuICAgICAgICAvLyB0byBleGNlc3NpdmUgbG9hZGluZ1xuICAgICAgICBpZiAodGhpcy53b3JsZCAhPSBudWxsKSB7XG4gICAgICAgICAgICBsZXQgYm91bmRzID0gdGhpcy53b3JsZC5ib3VuZHNcbiAgICAgICAgICAgIGxldCB4ID0gTWF0aC5tYXgoLWJvdW5kcy53aWR0aCwgYm91bmRzLngpXG4gICAgICAgICAgICBsZXQgeSA9IE1hdGgubWF4KC1ib3VuZHMuaGVpZ2h0LCBib3VuZHMueSlcbiAgICAgICAgICAgIGxldCB3aWR0aCA9IE1hdGgubWluKHZpZXdCb3VuZHMud2lkdGgsIGJvdW5kcy53aWR0aClcbiAgICAgICAgICAgIGxldCBoZWlnaHQgPSBNYXRoLm1pbih2aWV3Qm91bmRzLmhlaWdodCwgYm91bmRzLmhlaWdodClcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCJ3b3JsZEJvdW5kcyBuZXdcIiwgeyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH0pXG4gICAgICAgICAgICByZXR1cm4geyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH1cbiAgICAgICAgfVxuICAgICAgICAvL2NvbnNvbGUubG9nKFwid29ybGRCb3VuZHMgb2xkXCIsIHZpZXdCb3VuZHMpXG4gICAgICAgIHJldHVybiB2aWV3Qm91bmRzXG4gICAgfVxuXG4gICAgLyoqIExvYWRzIGFsbCB0aWxlcyB0aGF0IGFyZSBuZWVkZWQgdG8gZmlsbCB0aGUgYXBwIGJvdW5kcy5cbiAgICAgKiBAcGFyYW0ge1RpbGVzfSB0aWxlcyAtIHRoZSBncmlkIG9mIHRpbGVzXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIHpvb20gbGV2ZWwgb2YgdGhlIGdyaWRcbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IGRlYnVnXG4gICAgICogQHJldHVybiB7QXJyYXlbXX0gLSBBbiBhcnJheSBvZiBbdXJsLCBjb2wsIHJvd10gYXJyYXlzXG4gICAgICovXG4gICAgbmVlZGVkVGlsZXModGlsZXMsIGxldmVsLCBkZWJ1ZyA9IGZhbHNlKSB7XG4gICAgICAgIGxldCBuZWVkZWQgPSBbXVxuICAgICAgICBsZXQgdHNpemUgPSB0aWxlcy50aWxlU2l6ZVxuICAgICAgICBsZXQgd29ybGRCb3VuZHMgPSB0aGlzLndvcmxkQm91bmRzKClcbiAgICAgICAgbGV0IG1heFdpZHRoID0gd29ybGRCb3VuZHMud2lkdGhcbiAgICAgICAgbGV0IG1heEhlaWdodCA9IHdvcmxkQm91bmRzLmhlaWdodFxuXG4gICAgICAgIGxldCBwb2ludEluV2luZG93ID0gbmV3IFBJWEkuUG9pbnQoKVxuICAgICAgICBsZXQgd29ybGRUb3BMZWZ0ID0gbmV3IFBJWEkuUG9pbnQod29ybGRCb3VuZHMueCwgd29ybGRCb3VuZHMueSlcbiAgICAgICAgbGV0IHdvcmxkQm90dG9tUmlnaHQgPSBuZXcgUElYSS5Qb2ludCh3b3JsZEJvdW5kcy54ICsgbWF4V2lkdGgsIHdvcmxkQm91bmRzLnkgKyBtYXhIZWlnaHQpXG4gICAgICAgIGxldCB3b3JsZENlbnRlciA9IG5ldyBQSVhJLlBvaW50KHdvcmxkQm91bmRzLnggKyBtYXhXaWR0aCAvIDIsIHdvcmxkQm91bmRzLnkgKyBtYXhIZWlnaHQgLyAyKVxuICAgICAgICBsZXQgdGlsZXNDZW50ZXIgPSB0aWxlcy50b0xvY2FsKHdvcmxkQ2VudGVyKVxuXG4gICAgICAgIGxldCB0b3BMZWZ0ID0gdGlsZXMudG9Mb2NhbCh3b3JsZFRvcExlZnQpXG4gICAgICAgIGxldCBib3R0b21SaWdodCA9IHRpbGVzLnRvTG9jYWwod29ybGRCb3R0b21SaWdodClcbiAgICAgICAgdGlsZXMuX2NlbnRlclBvaW50ID0gdGlsZXNDZW50ZXJcbiAgICAgICAgbGV0IGJvdW5kcyA9IG5ldyBQSVhJLlJlY3RhbmdsZShcbiAgICAgICAgICAgIHRvcExlZnQueCxcbiAgICAgICAgICAgIHRvcExlZnQueSxcbiAgICAgICAgICAgIGJvdHRvbVJpZ2h0LnggLSB0b3BMZWZ0LngsXG4gICAgICAgICAgICBib3R0b21SaWdodC55IC0gdG9wTGVmdC55KVxuXG4gICAgICAgIHRpbGVzLl9ib3VuZHNSZWN0ID0gYm91bmRzXG5cbiAgICAgICAgLyogVU86IHdlIG5lZWQgYSB0b0xvY2FsIGNhbGwgaGVyZSBzaW5jZSB0aGUgdHJhbnNmb3JtIG1heSBuZWVkIGFuIHVwZGF0ZVxuICAgICAgICB3aGljaCBpcyBndWFyYW50ZWVkIGJ5IHRoZSB0b0xvY2FsIG1ldGhvZC4gKi9cbiAgICAgICAgbGV0IGNlbnRlckNvbCA9IE1hdGguZmxvb3IodGlsZXNDZW50ZXIueCAvIHRzaXplKVxuICAgICAgICBsZXQgY2VudGVyUm93ID0gTWF0aC5mbG9vcih0aWxlc0NlbnRlci55IC8gdHNpemUpXG5cbiAgICAgICAgLy8gRXhwYW5kIGJlY2F1c2Ugd2Ugd2FudCB0byB0ZXN0IGZvciBpbmNsdWRlZCBjZW50ZXJzXG4gICAgICAgIGJvdW5kcy54IC09IHRzaXplIC8gMlxuICAgICAgICBib3VuZHMueSAtPSB0c2l6ZSAvIDJcbiAgICAgICAgYm91bmRzLndpZHRoICs9IHRzaXplXG4gICAgICAgIGJvdW5kcy5oZWlnaHQgKz0gdHNpemVcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgbGV0IG1heFRpbGVzV2lkdGggPSBNYXRoLmNlaWwobWF4V2lkdGggLyB0c2l6ZSlcbiAgICAgICAgICAgIGxldCBtYXhUaWxlc0hlaWdodCA9IE1hdGguY2VpbChtYXhIZWlnaHQgLyB0c2l6ZSlcblxuICAgICAgICAgICAgbWF4VGlsZXNXaWR0aCArPSAyXG4gICAgICAgICAgICBtYXhUaWxlc0hlaWdodCArPSAyXG5cbiAgICAgICAgICAgIGxldCBzdGFydENvbCA9IE1hdGgubWF4KDAsIGNlbnRlckNvbCAtIG1heFRpbGVzV2lkdGgpXG4gICAgICAgICAgICBsZXQgZW5kQ29sID0gTWF0aC5taW4odGlsZXMuY29scywgY2VudGVyQ29sICsgbWF4VGlsZXNXaWR0aClcblxuICAgICAgICAgICAgbGV0IHN0YXJ0Um93ID0gTWF0aC5tYXgoMCwgY2VudGVyUm93IC0gbWF4VGlsZXNIZWlnaHQpXG4gICAgICAgICAgICBsZXQgZW5kUm93ID0gTWF0aC5taW4odGlsZXMucm93cywgY2VudGVyUm93ICsgbWF4VGlsZXNIZWlnaHQpXG5cbiAgICAgICAgICAgIGZvciAobGV0IGNvbCA9IHN0YXJ0Q29sOyBjb2wgPCBlbmRDb2w7IGNvbCsrKSB7XG4gICAgICAgICAgICAgICAgbGV0IGN4ID0gKGNvbCArIDAuNSkgKiB0c2l6ZVxuICAgICAgICAgICAgICAgIGZvciAobGV0IHJvdyA9IHN0YXJ0Um93OyByb3cgPCBlbmRSb3c7IHJvdysrKSB7XG4gICAgICAgICAgICAgICAgICAgIGxldCBjeSA9IChyb3cgKyAwLjUpICogdHNpemVcbiAgICAgICAgICAgICAgICAgICAgbGV0IHRpbGVDZW50ZXIgPSBuZXcgUElYSS5Qb2ludChjeCwgY3kpXG4gICAgICAgICAgICAgICAgICAgIGlmIChib3VuZHMuY29udGFpbnModGlsZUNlbnRlci54LCB0aWxlQ2VudGVyLnkpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgdXJsID0gdGhpcy5pbmZvLnVybEZvclRpbGUobGV2ZWwsIGNvbCwgcm93KVxuICAgICAgICAgICAgICAgICAgICAgICAgbmVlZGVkLnB1c2goW3VybCwgY29sLCByb3ddKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGVycm9yLm1lc3NhZ2UpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHsgY2VudGVyQ29sLCBjZW50ZXJSb3csIG5lZWRlZCB9XG4gICAgfVxuXG5cblxuXG4gICAgLyoqIFJldHVybnMgYWxsIGNoYW5nZWQgdGlsZXMgZm9yIGEgZ2l2ZW4gbGV2ZWwuXG4gICAgICogQHBhcmFtIHtUaWxlc30gdGlsZXMgLSB0aGUgZ3JpZCBvZiB0aWxlc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSB6b29tIGxldmVsIG9mIHRoZSBncmlkXG4gICAgICogQHJldHVybiB7b2JqZWN0fSAtIEFuIG9iamVjdCB3aXRoIHRoZSBrZXlzIGFkZGVkIGFuZCByZW1vdmVkIHdoaWNoIHZhbHVlcyBhcmUgW3VybCwgY29sLCByb3ddIGFycmF5c1xuICAgICAqL1xuICAgIGNoYW5nZWRUaWxlcyh0aWxlcywgbGV2ZWwpIHtcbiAgICAgICAgaWYgKHRoaXMuZGVidWcpIGNvbnNvbGUudGltZSgnY2hhbmdlZFRpbGVzJylcbiAgICAgICAgbGV0IGNoYW5nZWQgPSB7IGFkZGVkOiBbXSwgcmVtb3ZlZDogW10gfVxuICAgICAgICBsZXQgbmV3TmVlZGVkID0gbmV3IE1hcCgpXG4gICAgICAgIGxldCB7IGNlbnRlckNvbCwgY2VudGVyUm93LCBuZWVkZWQgfSA9IHRoaXMubmVlZGVkVGlsZXModGlsZXMsIGxldmVsKVxuICAgICAgICBuZWVkZWQuZm9yRWFjaChkID0+IHtcbiAgICAgICAgICAgIGxldCBbdXJsLCBjb2wsIHJvd10gPSBkXG4gICAgICAgICAgICBuZXdOZWVkZWQuc2V0KHVybCwgW2NvbCwgcm93XSlcbiAgICAgICAgICAgIGlmICghdGlsZXMucmVxdWVzdGVkLmhhcyh1cmwpKSB7XG4gICAgICAgICAgICAgICAgY2hhbmdlZC5hZGRlZC5wdXNoKGQpXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgICAgIGZvciAobGV0IHVybCBvZiB0aWxlcy5uZWVkZWQua2V5cygpKSB7XG4gICAgICAgICAgICBpZiAoIW5ld05lZWRlZC5oYXModXJsKSkge1xuICAgICAgICAgICAgICAgIGxldCBbY29sLCByb3ddID0gdGlsZXMubmVlZGVkLmdldCh1cmwpXG4gICAgICAgICAgICAgICAgY2hhbmdlZC5yZW1vdmVkLnB1c2goW3VybCwgY29sLCByb3ddKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRpbGVzLm5lZWRlZCA9IG5ld05lZWRlZFxuICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5sb2cobmV3TmVlZGVkKVxuICAgICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS50aW1lRW5kKCdjaGFuZ2VkVGlsZXMnKVxuICAgICAgICByZXR1cm4geyBjZW50ZXJDb2wsIGNlbnRlclJvdywgY2hhbmdlZCB9XG4gICAgfVxuXG4gICAgLyoqIFBvcHVsYXRlcyBhbGwgdGlsZXMgZm9yIGEgZ2l2ZW4gbGV2ZWwuXG4gICAgICogQHBhcmFtIHtUaWxlc30gdGlsZXMgLSB0aGUgZ3JpZCBvZiB0aWxlc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSB6b29tIGxldmVsIG9mIHRoZSBncmlkXG4gICAgICovXG4gICAgcG9wdWxhdGVBbGxUaWxlcyh0aWxlcywgbGV2ZWwpIHtcbiAgICAgICAgbGV0IGFsbCA9IHRoaXMuYWxsVGlsZXModGlsZXMsIGxldmVsKVxuICAgICAgICBmb3IgKGxldCBbdXJsLCBjb2wsIHJvd10gb2YgYWxsKSB7XG4gICAgICAgICAgICB0aGlzLmFkZFRpbGVRdWFkTm9kZShsZXZlbCwgY29sLCByb3csIHVybClcbiAgICAgICAgfVxuICAgICAgICB0aWxlcy5sb2FkVGlsZXMoYWxsLCBmYWxzZSwgMCwgMClcbiAgICB9XG5cbiAgICAvKiogTG9hZHMgYWxsIHRpbGVzIHRoYXQgYXJlIG5lZWRlZCB0byBmaWxsIHRoZSBicm93c2VyIHdpbmRvdy5cbiAgICAgKiBJZiB0aGUgb3B0aW9uYWwgYWJvdXQgcGFyYW1ldGVyIGlzIHByb3ZpZGVkIChhcyBhIHBvaW50IHdpdGggY29sIGFzIHgsXG4gICAgICogYW5kIHJvdyBhcyB5IGF0dHIpIHRoZSB0aWxlcyBhcmUgc29ydGVkIGJ5IHRoZSBkaXN0YW5jZSB0byB0aGlzIHBvaW50LlxuICAgICAqXG4gICAgICogQHBhcmFtIHtUaWxlc30gdGlsZXMgLSB0aGUgZ3JpZCBvZiB0aWxlc1xuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSB6b29tIGxldmVsIG9mIHRoZSBncmlkXG4gICAgICogT3B0aW9uYWwgcGFyYW1ldGVyOlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gb25seW9uZSAtIGlmIHRydWUgb25seSBvbmUgdGlsZSBpcyBsb2FkZWRcbiAgICAgKiBAcGFyYW0ge1BJWEkuUG9pbnR9IGFib3V0IC0gcG9pbnQgb2YgaW50ZXJhY3Rpb25cbiAgICAgKi9cbiAgICBwb3B1bGF0ZVRpbGVzKHRpbGVzLCBsZXZlbCwgeyBvbmx5b25lID0gZmFsc2UsIGFib3V0ID0gbnVsbCB9ID0ge30pIHtcbiAgICAgICAgaWYgKHRpbGVzLmlzQ29tcGxldGUoKSlcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICBsZXQgcmVmZXJlbmNlQ29sID0gLTFcbiAgICAgICAgbGV0IHJlZmVyZW5jZVJvdyA9IC0xXG4gICAgICAgIGxldCB7IGNlbnRlckNvbCwgY2VudGVyUm93LCBjaGFuZ2VkIH0gPSB0aGlzLmNoYW5nZWRUaWxlcyh0aWxlcywgbGV2ZWwpXG4gICAgICAgIGlmIChhYm91dCAhPSBudWxsKSB7XG4gICAgICAgICAgICAvLyBXZSB3YW50IHRvIGxvYWQgdGlsZXMgaW4gdGhlIGZvY3VzIG9mIHRoZSB1c2VyIGZpcnN0LCB0aGVyZWZvcmVcbiAgICAgICAgICAgIC8vIHdlIHNvcnQgYWNjb3JkaW5nIHRvIHRoZSBkaXN0YW5jZSBvZiB0aGUgZm9jdXMgb2YgaW50ZXJhY3Rpb25cbiAgICAgICAgICAgIGxldCByZWZQb2ludCA9IHRoaXMudG9Mb2NhbChhYm91dClcbiAgICAgICAgICAgIGxldCBzY2FsZWRUaWxlU2l6ZSA9IHRpbGVzLnRpbGVTaXplICogdGlsZXMudGlsZVNjYWxlXG4gICAgICAgICAgICByZWZlcmVuY2VDb2wgPSBNYXRoLmZsb29yKHJlZlBvaW50LnggLyBzY2FsZWRUaWxlU2l6ZSlcbiAgICAgICAgICAgIHJlZmVyZW5jZVJvdyA9IE1hdGguZmxvb3IocmVmUG9pbnQueSAvIHNjYWxlZFRpbGVTaXplKVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgcmVmZXJlbmNlQ29sID0gY2VudGVyQ29sXG4gICAgICAgICAgICByZWZlcmVuY2VSb3cgPSBjZW50ZXJSb3dcbiAgICAgICAgfVxuICAgICAgICByZWZlcmVuY2VDb2wgPSBjZW50ZXJDb2xcbiAgICAgICAgcmVmZXJlbmNlUm93ID0gY2VudGVyUm93XG5cbiAgICAgICAgbGV0IHJlbW92ZWQgPSBjaGFuZ2VkLnJlbW92ZWRcbiAgICAgICAgZm9yIChsZXQgW3VybCwgY29sLCByb3ddIG9mIHJlbW92ZWQpIHtcbiAgICAgICAgICAgIHRoaXMucmVtb3ZlVGlsZVF1YWROb2RlKGxldmVsLCBjb2wsIHJvdywgdXJsKVxuICAgICAgICB9XG4gICAgICAgIGxldCBhZGRlZCA9IGNoYW5nZWQuYWRkZWRcbiAgICAgICAgaWYgKGFkZGVkLmxlbmd0aCA9PSAwKSByZXR1cm5cbiAgICAgICAgZm9yIChsZXQgW3VybCwgY29sLCByb3ddIG9mIGFkZGVkKSB7XG4gICAgICAgICAgICB0aGlzLmFkZFRpbGVRdWFkTm9kZShsZXZlbCwgY29sLCByb3csIHVybClcbiAgICAgICAgfVxuICAgICAgICBsZXQgcmVmID0gbmV3IFBJWEkuUG9pbnQocmVmZXJlbmNlQ29sLCByZWZlcmVuY2VSb3cpXG4gICAgICAgIC8vIE5vdGU6IFRoZSBhcnJheSBtdXN0IGJlIHNvcnRlZCBpbiBhIHdheSB0aGF0IHRoZSBuZWFyZXN0IHRpbGVzXG4gICAgICAgIC8vIGFyZSBhdCB0aGUgZW5kIG9mIHRoZSBhcnJheSBzaW5jZSB0aGUgbG9hZCBxdWV1ZSB1c2VzIEFycmF5LnB1c2hcbiAgICAgICAgLy8gQXJyYXkucG9wXG4gICAgICAgIGFkZGVkLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgICAgICAgIGxldCBhYSA9IG5ldyBQSVhJLlBvaW50KGFbMV0sIGFbMl0pXG4gICAgICAgICAgICBsZXQgYmIgPSBuZXcgUElYSS5Qb2ludChiWzFdLCBiWzJdKVxuICAgICAgICAgICAgbGV0IGRhID0gUG9pbnRzLmRpc3RhbmNlKGFhLCByZWYpXG4gICAgICAgICAgICBsZXQgZGIgPSBQb2ludHMuZGlzdGFuY2UoYmIsIHJlZilcbiAgICAgICAgICAgIHJldHVybiBkYiAtIGRhXG4gICAgICAgIH0pXG4gICAgICAgIHRpbGVzLmxvYWRUaWxlcyhhZGRlZCwgb25seW9uZSwgcmVmZXJlbmNlQ29sLCByZWZlcmVuY2VSb3cpXG4gICAgfVxuXG4gICAgLyoqIFByaXZhdGUgbWV0aG9kOiBjcmVhdGVzIGFsbCB0aWxlcyBmb3IgYSBnaXZlbiBsZXZlbC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgem9vbSBsZXZlbCBvZiB0aGUgZ3JpZFxuICAgICAqIEByZXR1cm4ge1RpbGVzfSAtIHRpbGVzXG4gICAgICovXG4gICAgX2NyZWF0ZVRpbGVzKGtleSwgbGV2ZWwpIHtcbiAgICAgICAgbGV0IFtjb2xzLCByb3dzLCB3LCBoXSA9IHRoaXMuaW5mby5kaW1lbnNpb25zKGxldmVsKVxuICAgICAgICBsZXQgaW5jcmVhc2VkTGV2ZWxzID0gbGV2ZWwgLSB0aGlzLmluZm8uYmFzZUxldmVsXG4gICAgICAgIGxldCBpbnZTY2FsZSA9IE1hdGgucG93KDAuNSwgaW5jcmVhc2VkTGV2ZWxzKVxuICAgICAgICBsZXQgdGlsZXMgPSBuZXcgVGlsZXMoXG4gICAgICAgICAgICBsZXZlbCxcbiAgICAgICAgICAgIHRoaXMsXG4gICAgICAgICAgICBpbnZTY2FsZSxcbiAgICAgICAgICAgIGNvbHMsXG4gICAgICAgICAgICByb3dzLFxuICAgICAgICAgICAgdyxcbiAgICAgICAgICAgIGgsXG4gICAgICAgICAgICB0aGlzLmluZm8udGlsZVNpemUsXG4gICAgICAgICAgICB0aGlzLmluZm8ub3ZlcmxhcFxuICAgICAgICApXG4gICAgICAgIHRoaXMuYWRkVGlsZXMoa2V5LCB0aWxlcylcbiAgICAgICAgaWYgKHRoaXMuaW5mby5jbGlwKSB7XG4gICAgICAgICAgICBsZXQgcmVzdCA9IHRoaXMuaW5mby5yZXN0c1tsZXZlbF1cbiAgICAgICAgICAgIGlmIChyZXN0KSB7XG4gICAgICAgICAgICAgICAgbGV0IHggPSByZXN0LnJlc3RDb2wgKiB0aGlzLmluZm8udGlsZVNpemUgKiBpbnZTY2FsZVxuICAgICAgICAgICAgICAgIGxldCB5ID0gcmVzdC5yZXN0Um93ICogdGhpcy5pbmZvLnRpbGVTaXplICogaW52U2NhbGVcbiAgICAgICAgICAgICAgICB0aWxlcy54ID0gLXhcbiAgICAgICAgICAgICAgICB0aWxlcy55ID0gLXlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGlsZXNcbiAgICB9XG5cbiAgICAvKiogRW5zdXJlcyB0aGF0IGFsbCBuZWVkZWQgdGlsZXMgb2YgYSBnaXZlbiBsZXZlbCBhcmUgbG9hZGVkLiBDcmVhdGVzXG4gICAgICogYSBuZXcgVGlsZXMgbGF5ZXIgaWYgbmVjZXNzYXJ5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIHpvb20gbGV2ZWwgb2YgdGhlIGdyaWRcbiAgICAgKiBAcmV0dXJuIHtUaWxlc30gdGlsZXNcbiAgICAgKi9cbiAgICBlbnN1cmVUaWxlcyhsZXZlbCwgYWJvdXQpIHtcbiAgICAgICAgbGV0IGtleSA9IGxldmVsLnRvU3RyaW5nKClcbiAgICAgICAgaWYgKGtleSBpbiB0aGlzLnRpbGVMYXllcnMpIHtcbiAgICAgICAgICAgIGxldCB0aWxlcyA9IHRoaXMudGlsZUxheWVyc1trZXldXG4gICAgICAgICAgICB0aGlzLnBvcHVsYXRlVGlsZXModGlsZXMsIGxldmVsLCB7IGFib3V0OiBhYm91dCB9KVxuICAgICAgICAgICAgcmV0dXJuIHRpbGVzXG4gICAgICAgIH1cbiAgICAgICAgbGV0IHRpbGVzID0gdGhpcy5fY3JlYXRlVGlsZXMoa2V5LCBsZXZlbClcbiAgICAgICAgdGhpcy5wb3B1bGF0ZVRpbGVzKHRpbGVzLCBsZXZlbCwgeyBhYm91dDogYWJvdXQgfSlcbiAgICAgICAgLy9jb25zb2xlLmxvZyhcImVuc3VyZVRpbGVzXCIsIGxldmVsKVxuICAgICAgICByZXR1cm4gdGlsZXNcbiAgICB9XG5cbiAgICB1bnRpbnRUaWxlcyhsZXZlbCkge1xuICAgICAgICBsZXQga2V5ID0gbGV2ZWwudG9TdHJpbmcoKVxuICAgICAgICBpZiAoa2V5IGluIHRoaXMudGlsZUxheWVycykge1xuICAgICAgICAgICAgbGV0IHRpbGVzID0gdGhpcy50aWxlTGF5ZXJzW2tleV1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKiBFbnN1cmVzIHRoYXQgYWxsIHRpbGVzIG9mIGEgZ2l2ZW4gbGV2ZWwgYXJlIGxvYWRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgem9vbSBsZXZlbCBvZiB0aGUgZ3JpZFxuICAgICAqL1xuICAgIGVuc3VyZUFsbFRpbGVzKGxldmVsKSB7XG4gICAgICAgIGxldCBrZXkgPSBsZXZlbC50b1N0cmluZygpXG4gICAgICAgIGlmIChrZXkgaW4gdGhpcy50aWxlTGF5ZXJzKSB7XG4gICAgICAgICAgICBsZXQgdGlsZXMgPSB0aGlzLnRpbGVMYXllcnNba2V5XVxuICAgICAgICAgICAgdGhpcy5wb3B1bGF0ZUFsbFRpbGVzKHRpbGVzLCBsZXZlbClcbiAgICAgICAgICAgIHRpbGVzLmtlZXAgPSB0cnVlXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBsZXQgdGlsZXMgPSB0aGlzLl9jcmVhdGVUaWxlcyhrZXksIGxldmVsKVxuICAgICAgICB0aGlzLnBvcHVsYXRlQWxsVGlsZXModGlsZXMsIGxldmVsKVxuICAgICAgICB0aWxlcy5rZWVwID0gdHJ1ZVxuICAgICAgICByZXR1cm4gdGlsZXNcbiAgICB9XG5cbiAgICBoaWRlVGlsZXNBYm92ZUxldmVsKGxldmVsKSB7XG4gICAgICAgIE9iamVjdC5rZXlzKHRoaXMudGlsZUxheWVycykuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgICAgICAgbGV0IHRpbGVzID0gdGhpcy50aWxlTGF5ZXJzW2tleV1cbiAgICAgICAgICAgIGlmICh0aWxlcy5sZXZlbCA+IGxldmVsKSB7XG4gICAgICAgICAgICAgICAgdGlsZXMudmlzaWJsZSA9IGZhbHNlXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgLyoqIERlc3Ryb3lzIGFsbCB0aWxlcyBhYm92ZSBhIGdpdmVuIGxldmVsIHRvIGVuc3VyZSB0aGF0IHRoZSBtZW1vcnkgY2FuXG4gICAgICogYmUgcmV1c2VkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSB6b29tIGxldmVsIG9mIHRoZSBncmlkXG4gICAgICovXG4gICAgZGVzdHJveVRpbGVzQWJvdmVMZXZlbChsZXZlbCkge1xuICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnRpbGVMYXllcnMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgICAgIGxldCB0aWxlcyA9IHRoaXMudGlsZUxheWVyc1trZXldXG4gICAgICAgICAgICBpZiAodGlsZXMubGV2ZWwgPiBsZXZlbCAmJiAhdGlsZXMua2VlcCkge1xuICAgICAgICAgICAgICAgIGZvciAobGV0IHVybCBvZiB0aWxlcy5hdmFpbGFibGUua2V5cygpKSB7XG4gICAgICAgICAgICAgICAgICAgIGxldCBxdWFkID0gdGhpcy5xdWFkVHJlZXMuZ2V0KHVybClcbiAgICAgICAgICAgICAgICAgICAgaWYgKHF1YWQpIHRoaXMucmVtb3ZlVGlsZVF1YWROb2RlKHF1YWQpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMuZGVzdHJveVRpbGVzKGtleSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICBkZXN0cm95QWxsVGlsZXMoKSB7XG4gICAgICAgIE9iamVjdC5rZXlzKHRoaXMudGlsZUxheWVycykuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgICAgICAgdGhpcy5kZXN0cm95VGlsZXMoa2V5KVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRpbnQgdGlsZXMgaW4gYWxsIGxheWVycyB0aGF0IGFyZSBubyBsb25nZXIgbmVlZGVkXG4gICAgICpcbiAgICAgKiBAbWVtYmVyb2YgRGVlcFpvb21JbWFnZVxuICAgICAqL1xuICAgIHRpbnRPYnNvbGV0ZVRpbGVzKCkge1xuICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnRpbGVMYXllcnMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgICAgIGxldCB0aWxlcyA9IHRoaXMudGlsZUxheWVyc1trZXldXG4gICAgICAgICAgICB0aWxlcy51bnRpbnRUaWxlcygpXG4gICAgICAgICAgICBpZiAoIXRpbGVzLmtlZXApIHtcbiAgICAgICAgICAgICAgICB0aWxlcy50aW50T2Jzb2xldGVUaWxlcygpXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgfVxuXG5cbiAgICAvKipcbiAgICAgKiBEZXN0cm95IHRpbGVzIGluIGFsbCBsYXllcnMgdGhhdCBhcmUgbm8gbG9uZ2VyIG5lZWRlZFxuICAgICAqXG4gICAgICogQG1lbWJlcm9mIERlZXBab29tSW1hZ2VcbiAgICAgKi9cbiAgICBkZXN0cm95VW5uZWVkZWRUaWxlcygpIHtcbiAgICAgICAgT2JqZWN0LmtleXModGhpcy50aWxlTGF5ZXJzKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICAgICAgICBsZXQgdGlsZXMgPSB0aGlzLnRpbGVMYXllcnNba2V5XVxuICAgICAgICAgICAgaWYgKCF0aWxlcy5rZWVwKSB7XG4gICAgICAgICAgICAgICAgdGlsZXMuZGVzdHJveVVubmVlZGVkVGlsZXMoKVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIERlc3Ryb3kgdGlsZXMgaW4gYWxsIGxheWVycyB0aGF0IGFyZSBubyBsb25nZXIgbmVlZGVkXG4gICAgICpcbiAgICAgKiBAbWVtYmVyb2YgRGVlcFpvb21JbWFnZVxuICAgICAqL1xuICAgIGRlc3Ryb3lPYnNvbGV0ZVRpbGVzKCkge1xuICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnRpbGVMYXllcnMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgICAgIGxldCB0aWxlcyA9IHRoaXMudGlsZUxheWVyc1trZXldXG4gICAgICAgICAgICBpZiAoIXRpbGVzLmtlZXApIHtcbiAgICAgICAgICAgICAgICB0aWxlcy5kZXN0cm95T2Jzb2xldGVUaWxlcygpXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRGVzdHJveSB0aWxlcyBpbiBhbGwgbGF5ZXJzIHRoYXQgYXJlIG5vdCBwYXJ0IG9mIHRoZVxuICAgICAqIHZpc2libGUgcXVhZFRyZWVzXG4gICAgICpcbiAgICAgKiBAbWVtYmVyb2YgRGVlcFpvb21JbWFnZVxuICAgICAqL1xuICAgIGRlc3Ryb3lUaWxlcygpIHtcbiAgICAgICAgT2JqZWN0LmtleXModGhpcy50aWxlTGF5ZXJzKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICAgICAgICBsZXQgdGlsZXMgPSB0aGlzLnRpbGVMYXllcnNba2V5XVxuICAgICAgICAgICAgaWYgKCF0aWxlcy5rZWVwKSB7XG4gICAgICAgICAgICAgICAgdGlsZXMuZGVzdHJveVRpbGVzKHRoaXMucXVhZFRyZWVzKVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIC8qIFRpbnQgYWxsIHRpbGVzXG4gICAgKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgem9vbSBsZXZlbCBvZiB0aGUgZ3JpZFxuICAgICovXG4gICAgdGludFRpbGVzQmVsb3dMZXZlbChsZXZlbCkge1xuICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnRpbGVMYXllcnMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgICAgIGxldCB0aWxlcyA9IHRoaXMudGlsZUxheWVyc1trZXldXG4gICAgICAgICAgICBpZiAodGlsZXMubGV2ZWwgPCBsZXZlbCkge1xuICAgICAgICAgICAgICAgIHRpbGVzLnRpbnRUaWxlcyh0aGlzLnF1YWRUcmVlcylcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBFbnN1cmUgdGhhdCB0aGUgZ2l2ZW4gdGlsZXMgbGF5ZXIgaXMgdGhlIHRvcG1vc3Qgb25lIGFuZCB2aXNpYmxlLlxuICAgICAqIEBwYXJhbSB7Kn0gdGlsZXMgXG4gICAgICovXG4gICAgYnJpbmdUaWxlc1RvRnJvbnQodGlsZXMpIHtcbiAgICAgICAgdGhpcy50aWxlQ29udGFpbmVyLmFkZENoaWxkKHRpbGVzKVxuICAgICAgICB0aWxlcy52aXNpYmxlID0gdHJ1ZVxuICAgIH1cblxuICAgIC8qKiBBIGNhbGxiYWNrIGZ1bmN0aW9uIHRoYXQgY2FuIGJlIHVzZWQgYnkgYSBTY2F0dGVyIHZpZXcgdG8gaW5mb3JtXG4gICAgICogdGhlIHpvb21hYmxlIGltYWdlIHRoYXQgaXQgaGFzIGJlZW4gbW92ZWQsIHJvdGF0ZWQgb3Igc2NhbGVkLCBhbmQgc2hvdWxkXG4gICAgICogbG9hZCB0aWxlcyBhY2NvcmRpbmdseS5cbiAgICAgKiBAcGFyYW0ge1BJWEkuUG9pbnR9IHRyYW5zbGF0ZWQgLSB0aGUgbW92ZW1lbnQgb2YgdGhlIHNjYXR0ZXJcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gc2NhbGUgLSB0aGUgem9vbSBmYWN0b3JcbiAgICAgKiBAcGFyYW0ge1BJWEkuUG9pbnR9IGFib3V0IC0gdGhlIGFuY2hvciBwb2ludCBvZiB0aGUgem9vbVxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gZmFzdCAtIGluZm9ybXMgdGhlIGNhbGxiYWNrIHRvIHJldHVybiBhcyBmYXN0IGFzIHBvc3NpYmxlLFxuICAgICAqICBpLmUuIGFmdGVyIGxvYWRpbmcgYSBzaW5nbGUgdGlsZVxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gZGVidWcgLSBsb2cgZGVidWcgaW5mb3NcbiAgICAgKi9cbiAgICB0cmFuc2Zvcm1lZChldmVudCkge1xuICAgICAgICBsZXQga2V5ID0gdGhpcy5jdXJyZW50TGV2ZWwudG9TdHJpbmcoKVxuICAgICAgICBsZXQgY3VycmVudFRpbGVzID0gdGhpcy50aWxlTGF5ZXJzW2tleV1cbiAgICAgICAgaWYgKHR5cGVvZiBjdXJyZW50VGlsZXMgPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIGlmIChldmVudC5mYXN0KSB7XG4gICAgICAgICAgICB0aGlzLmZhc3RMb2FkcyArPSAxXG4gICAgICAgICAgICB0aGlzLnBvcHVsYXRlVGlsZXMoY3VycmVudFRpbGVzLCB0aGlzLmN1cnJlbnRMZXZlbCwge1xuICAgICAgICAgICAgICAgIG9ubHlvbmU6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGFib3V0OiBldmVudC5hYm91dFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIGlmICh0aGlzLmZhc3RMb2FkcyA9PSAzKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5mYXN0TG9hZHMgPSAwXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoZXZlbnQuc2NhbGUgPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5lbnN1cmVUaWxlcyh0aGlzLmN1cnJlbnRMZXZlbCwgZXZlbnQuYWJvdXQpXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgbGV0IGxldmVsID0gdGhpcy5sZXZlbEZvclNjYWxlKGV2ZW50LnNjYWxlKVxuICAgICAgICBsZXQgbmV3TGV2ZWwgPSBNYXRoLm1heChsZXZlbCwgdGhpcy5taW5pbXVtTGV2ZWwpXG4gICAgICAgIGlmIChuZXdMZXZlbCAhPSB0aGlzLmN1cnJlbnRMZXZlbCkge1xuICAgICAgICAgICAgaWYgKCFjdXJyZW50VGlsZXMua2VlcCkge1xuICAgICAgICAgICAgICAgIGN1cnJlbnRUaWxlcy5sb2FkZXIuY2FuY2VsKClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuaGlkZVRpbGVzQWJvdmVMZXZlbChuZXdMZXZlbClcbiAgICAgICAgICAgIGN1cnJlbnRUaWxlcyA9IHRoaXMuZW5zdXJlVGlsZXMobmV3TGV2ZWwsIGV2ZW50LmFib3V0KVxuICAgICAgICAgICAgdGhpcy5jdXJyZW50TGV2ZWwgPSBuZXdMZXZlbFxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5lbnN1cmVUaWxlcyh0aGlzLmN1cnJlbnRMZXZlbCwgZXZlbnQuYWJvdXQpXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5icmluZ1RpbGVzVG9Gcm9udChjdXJyZW50VGlsZXMpXG4gICAgICAgIGlmICh0aGlzLl9mb3JlZ3JvdW5kKSB7XG4gICAgICAgICAgICB0aGlzLmFkZENoaWxkKHRoaXMuX2ZvcmVncm91bmQpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICpBY3RpdmF0ZXMgdGhlIHRleHR1cmVzIG9uIHRoZSBEZWVwWm9vbUltYWdlLlxuICAgKlxuICAgKiBAbWVtYmVyb2YgRGVlcFpvb21JbWFnZVxuICAgKi9cbiAgICBhY3RpdmF0ZSgpIHtcbiAgICAgICAgdGhpcy5kZXN0cm95VGlsZXNBYm92ZUxldmVsKHRoaXMuY3VycmVudExldmVsKVxuICAgICAgICB0aGlzLmVuc3VyZVRpbGVzKHRoaXMuY3VycmVudExldmVsLCBudWxsKVxuICAgICAgICAvL2NvbnNvbGUubG9nKFwiQWN0aXZhdGUgVGV4dHVyZXMhXCIsIHRoaXMuY3VycmVudExldmVsKVxuICAgIH1cblxuICAgIC8qKlxuICAgKkRlY3RpdmF0ZXMgdGhlIHRleHR1cmVzIG9uIHRoZSBEZWVwWm9vbUltYWdlLlxuICAgKlxuICAgKiBAbWVtYmVyb2YgRGVlcFpvb21JbWFnZVxuICAgKi9cbiAgICBkZWFjdGl2YXRlKCkge1xuICAgICAgICB0aGlzLmRlc3Ryb3lBbGxUaWxlcygpXG4gICAgICAgIE9iamVjdC5rZXlzKHRoaXMudGlsZUxheWVycykuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgICAgICAgdGhpcy5kZXN0cm95VGlsZXMoa2V5KVxuICAgICAgICB9KVxuICAgICAgICB0aGlzLnRpbGVDb250YWluZXIuZGVzdHJveSh7IGNoaWxkcmVuOiB0cnVlIH0pXG4gICAgICAgIHByaW50VGlsZUNhY2hlSW5mb3MoKVxuICAgIH1cblxuICAgIHRocm93RmluaXNoZWQoKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKFwidGhyb3dGaW5pc2hlZFwiKVxuICAgICAgICBsZXQga2V5ID0gdGhpcy5jdXJyZW50TGV2ZWwudG9TdHJpbmcoKVxuICAgICAgICBsZXQgY3VycmVudFRpbGVzID0gdGhpcy50aWxlTGF5ZXJzW2tleV1cbiAgICAgICAgaWYgKHR5cGVvZiBjdXJyZW50VGlsZXMgPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5lbnN1cmVUaWxlcyh0aGlzLmN1cnJlbnRMZXZlbClcbiAgICAgICAgLy8gbGV0IGFsbCA9IG5ldyBTZXQoKVxuICAgICAgICAvLyBmb3IgKGxldCB0aWxlIG9mIGN1cnJlbnRUaWxlcy5jaGlsZHJlbikge1xuICAgICAgICAvLyAgICAgYWxsLmFkZCh0aWxlLnVybClcbiAgICAgICAgLy8gfVxuICAgICAgICAvLyBsZXQgeyBjZW50ZXJDb2wsIGNlbnRlclJvdywgbmVlZGVkIH0gPSB0aGlzLm5lZWRlZFRpbGVzKGN1cnJlbnRUaWxlcywgdGhpcy5jdXJyZW50TGV2ZWwpXG4gICAgICAgIC8vIGZvciAobGV0IFt1cmwsIGNvbCwgcm93XSBvZiBuZWVkZWQpIHtcbiAgICAgICAgLy8gICAgIGFsbC5kZWxldGUodXJsKVxuICAgICAgICAvLyB9XG4gICAgICAgIC8vIGZvciAobGV0IHVybCBvZiBhbGwpIHtcbiAgICAgICAgLy8gICAgIGN1cnJlbnRUaWxlcy5kZXN0cm95VGlsZUJ5VXJsKHVybClcbiAgICAgICAgLy8gfVxuICAgICAgICAvLyBjdXJyZW50VGlsZXMubG9hZGVyLmxvYWRlci5yZXNldCgpXG4gICAgfVxufVxuIiwiaW1wb3J0IHtnZXRJZCwgQW5nbGV9IGZyb20gJy4uL3V0aWxzLmpzJ1xuaW1wb3J0IHtET01TY2F0dGVyfSBmcm9tICcuLi9zY2F0dGVyLmpzJ1xuaW1wb3J0IHtDYXJkTG9hZGVyLCBET01GbGlwLCBET01GbGlwcGFibGV9IGZyb20gJy4uL2ZsaXBwYWJsZS5qcydcbmltcG9ydCB7Q2FwYWJpbGl0aWVzfSBmcm9tICcuLi9jYXBhYmlsaXRpZXMuanMnXG5pbXBvcnQge0RlZXBab29tSW1hZ2V9IGZyb20gJy4vZGVlcHpvb20vaW1hZ2UuanMnXG5cbmxldCBnbG9iYWxTY2F0dGVyTG9hZGVyQ2FudmFzID0gbnVsbFxuXG5leHBvcnQgY2xhc3MgU2NhdHRlckxvYWRlciBleHRlbmRzIENhcmRMb2FkZXIge1xuXG4gICAgZ2V0IHNjYXR0ZXIoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnNyY1xuICAgIH1cblxuICAgIHVuc2NhbGVkU2l6ZSgpIHtcbiAgICAgICAgbGV0IGRpc3BsYXlPYmplY3QgPSB0aGlzLnNjYXR0ZXIuZGlzcGxheU9iamVjdFxuICAgICAgICBsZXQgdyA9IGRpc3BsYXlPYmplY3Qud2lkdGhcbiAgICAgICAgbGV0IGggPSBkaXNwbGF5T2JqZWN0LmhlaWdodFxuICAgICAgICByZXR1cm4gW3cgLyBkaXNwbGF5T2JqZWN0LnNjYWxlLngsIGggLyBkaXNwbGF5T2JqZWN0LnNjYWxlLnldXG4gICAgfVxuXG4gICAgc2NhbGVkU2l6ZSgpIHtcbiAgICAgICAgbGV0IGRpc3BsYXlPYmplY3QgPSB0aGlzLnNjYXR0ZXIuZGlzcGxheU9iamVjdFxuICAgICAgICBsZXQgdyA9IGRpc3BsYXlPYmplY3Qud2lkdGhcbiAgICAgICAgbGV0IGggPSBkaXNwbGF5T2JqZWN0LmhlaWdodFxuICAgICAgICByZXR1cm4gW3csIGhdXG4gICAgfVxuXG4gICAgY2xvbmVTY2F0dGVySW1hZ2UoKSB7XG4gICAgICAgIGxldCB3ID0gdGhpcy5zY2F0dGVyLndpZHRoXG4gICAgICAgIGxldCBoID0gdGhpcy5zY2F0dGVyLmhlaWdodFxuICAgICAgICBsZXQgaXNTcHJpdGUgPSB0aGlzLnNjYXR0ZXIuZGlzcGxheU9iamVjdCBpbnN0YW5jZW9mIFBJWEkuU3ByaXRlXG4gICAgICAgIGxldCBpc0RlZXBab29tID0gdGhpcy5zY2F0dGVyLmRpc3BsYXlPYmplY3QgaW5zdGFuY2VvZiBEZWVwWm9vbUltYWdlXG4gICAgICAgIGxldCByZXNvbHV0aW9uID0gYXBwLnJlbmRlcmVyLnJlc29sdXRpb25cbiAgICAgICAgaWYgKGlzU3ByaXRlKSB7XG4gICAgICAgICAgICB3ID0gdGhpcy5zY2F0dGVyLmRpc3BsYXlPYmplY3QudGV4dHVyZS53aWR0aFxuICAgICAgICAgICAgaCA9IHRoaXMuc2NhdHRlci5kaXNwbGF5T2JqZWN0LnRleHR1cmUuaGVpZ2h0XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAoaXNEZWVwWm9vbSkge1xuICAgICAgICAgICAgbGV0IFt3dywgaGhdID0gdGhpcy5zY2F0dGVyLmRpc3BsYXlPYmplY3QuYmFzZVNpemVcbiAgICAgICAgICAgIHcgPSB3d1xuICAgICAgICAgICAgaCA9IGhoXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGdsb2JhbFNjYXR0ZXJMb2FkZXJDYW52YXMgPT09IG51bGwpIHtcbiAgICAgICAgICAgIGdsb2JhbFNjYXR0ZXJMb2FkZXJDYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKVxuICAgICAgICB9XG4gICAgICAgIGxldCBjYW52YXMgPSBnbG9iYWxTY2F0dGVyTG9hZGVyQ2FudmFzXG4gICAgICAgIGNhbnZhcy53aWR0aCA9IHdcbiAgICAgICAgY2FudmFzLmhlaWdodCA9IGhcbiAgICAgICAgbGV0IHJlbmRlcmVyID0gbmV3IFBJWEkuV2ViR0xSZW5kZXJlcih3LCBoLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWV3OiBjYW52YXMsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9uOiByZXNvbHV0aW9ufSlcblxuICAgICAgICBsZXQgZGlzcGxheU9iamVjdCA9IHRoaXMuc2NhdHRlci5kaXNwbGF5T2JqZWN0XG4gICAgICAgIGxldCB4ID0gZGlzcGxheU9iamVjdC54XG4gICAgICAgIGxldCB5ID0gZGlzcGxheU9iamVjdC55XG4gICAgICAgIGxldCByb3QgPSBkaXNwbGF5T2JqZWN0LnJvdGF0aW9uXG4gICAgICAgIGxldCBzeCA9IGRpc3BsYXlPYmplY3Quc2NhbGUueFxuICAgICAgICBsZXQgc3kgPSBkaXNwbGF5T2JqZWN0LnNjYWxlLnlcbiAgICAgICAgZGlzcGxheU9iamVjdC5yb3RhdGlvbiA9IDBcbiAgICAgICAgLy8gVGhlIFNhZmFyaSBXZWJHTFJlbmRlcmVyIHdhbnRzIGV2ZXJ5dGhpbmcgZmxpcHBlZFxuICAgICAgICAvLyBTZWUgaHR0cHM6Ly9naXRodWIuY29tL3BpeGlqcy9waXhpLmpzL2lzc3Vlcy8yMjgzXG4gICAgICAgIGRpc3BsYXlPYmplY3QueCA9IDBcbiAgICAgICAgaWYgKENhcGFiaWxpdGllcy5pc1NhZmFyaSkge1xuICAgICAgICAgICAgZGlzcGxheU9iamVjdC55ID0gaFxuICAgICAgICAgICAgZGlzcGxheU9iamVjdC5zY2FsZS5zZXQoMSwgLTEpIC8vIHN4LCAtc3kpXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBkaXNwbGF5T2JqZWN0LnkgPSAwXG4gICAgICAgICAgICBkaXNwbGF5T2JqZWN0LnNjYWxlLnNldCgxLCAxKVxuICAgICAgICB9XG4gICAgICAgIGlmIChpc1Nwcml0ZSkge1xuICAgICAgICAgICAgZGlzcGxheU9iamVjdC53aWR0aCA9IHdcbiAgICAgICAgICAgIGRpc3BsYXlPYmplY3QuaGVpZ2h0ID0gaFxuICAgICAgICB9XG4gICAgICAgIHJlbmRlcmVyLnJlbmRlcihkaXNwbGF5T2JqZWN0KVxuICAgICAgICBkaXNwbGF5T2JqZWN0LnJvdGF0aW9uID0gcm90XG4gICAgICAgIGRpc3BsYXlPYmplY3QueCA9IHhcbiAgICAgICAgZGlzcGxheU9iamVjdC55ID0geVxuICAgICAgICBkaXNwbGF5T2JqZWN0LnNjYWxlLnNldChzeCwgc3kpXG5cbiAgICAgICAgbGV0IHVybCA9IGNhbnZhcy50b0RhdGFVUkwoKVxuICAgICAgICByZXR1cm4gW3gsIHksIHcsIGgsIHVybF1cbiAgICB9XG5cbiAgICBsb2FkKGRvbU5vZGUpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGxldCBpc0ltYWdlID0gZG9tTm9kZSBpbnN0YW5jZW9mIEhUTUxJbWFnZUVsZW1lbnRcbiAgICAgICAgICAgIGxldCBpc1Nwcml0ZSA9IHRoaXMuc2NhdHRlci5kaXNwbGF5T2JqZWN0IGluc3RhbmNlb2YgUElYSS5TcHJpdGVcbiAgICAgICAgICAgIGxldCBpbWFnZSA9IChpc0ltYWdlKSA/IGRvbU5vZGUgOiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiaW1nXCIpXG4gICAgICAgICAgICBsZXQgW3gsIHksIHcsIGgsIGNsb25lVVJMXSA9IHRoaXMuY2xvbmVTY2F0dGVySW1hZ2UoKVxuICAgICAgICAgICAgbGV0IFt3dywgaGhdID0gdGhpcy51bnNjYWxlZFNpemUoKVxuICAgICAgICAgICAgaW1hZ2Uub25sb2FkID0gKGUpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoIWlzSW1hZ2UpXG4gICAgICAgICAgICAgICAgICAgIGRvbU5vZGUuYXBwZW5kQ2hpbGQoaW1hZ2UpXG4gICAgICAgICAgICAgICAgdGhpcy54ID0geFxuICAgICAgICAgICAgICAgIHRoaXMueSA9IHlcbiAgICAgICAgICAgICAgICB0aGlzLndhbnRlZFdpZHRoID0gd3dcbiAgICAgICAgICAgICAgICB0aGlzLndhbnRlZEhlaWdodCA9IGhoXG4gICAgICAgICAgICAgICAgdGhpcy5zY2FsZSA9IDFcbiAgICAgICAgICAgICAgICB0aGlzLnJvdGF0aW9uID0gdGhpcy5zY2F0dGVyLnJvdGF0aW9uXG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh0aGlzKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaW1hZ2Uub25lcnJvciA9IChlKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KHRoaXMpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpbWFnZS5zcmMgPSBjbG9uZVVSTFxuICAgICAgICAgICAgfSlcbiAgICB9XG5cbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRmxpcEVmZmVjdCB7XG5cbiAgICBjb25zdHJ1Y3RvcihzY2F0dGVyLCBkb21TY2F0dGVyQ29udGFpbmVyLCBmbGlwVGVtcGxhdGUsIGJhY2tMb2FkZXIpIHtcbiAgICAgICAgdGhpcy5mbGlwcGVkID0gZmFsc2VcbiAgICAgICAgdGhpcy5zY2F0dGVyID0gc2NhdHRlclxuICAgICAgICB0aGlzLmJhY2tMb2FkZXIgPSBiYWNrTG9hZGVyXG4gICAgICAgIHRoaXMuc2NhdHRlckxvYWRlciA9IG5ldyBTY2F0dGVyTG9hZGVyKHNjYXR0ZXIpXG4gICAgICAgIHRoaXMuZG9tRmxpcCA9IG5ldyBET01GbGlwKGRvbVNjYXR0ZXJDb250YWluZXIsIGZsaXBUZW1wbGF0ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc2NhdHRlckxvYWRlcixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhY2tMb2FkZXIsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbkJhY2s6IHRoaXMuYmFja0NhcmRDbG9zZWQuYmluZCh0aGlzKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgdGhpcy5zZXR1cEluZm9CdXR0b24oKVxuICAgIH1cblxuICAgIHN0YXJ0RmxpcCgpIHtcbiAgICAgICAgbGV0IGNlbnRlciA9IHRoaXMuZmxpcENlbnRlcigpXG4gICAgICAgIGxldCBsb2FkZXIgPSB0aGlzLmJhY2tMb2FkZXJcbiAgICAgICAgdGhpcy5kb21GbGlwLmxvYWQoKS50aGVuKChkb21GbGlwKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNjYXR0ZXIuZGlzcGxheU9iamVjdC52aXNpYmxlID0gZmFsc2VcbiAgICAgICAgICAgIGRvbUZsaXAuY2VudGVyQXQoY2VudGVyKVxuICAgICAgICAgICAgZG9tRmxpcC56b29tKHRoaXMuc2NhdHRlci5zY2FsZSlcbiAgICAgICAgICAgIGxldCB0YXJnZXQgPSB0aGlzLmNvbnN0cmFpbnRGbGlwQ2VudGVyKGNlbnRlciwgbG9hZGVyKVxuICAgICAgICAgICAgY29uc29sZS5sb2coXCJGbGlwRWZmZWN0LnN0YXJ0RmxpcFwiLCB0YXJnZXQsIGxvYWRlcilcbiAgICAgICAgICAgIGRvbUZsaXAuc3RhcnQoe3RhcmdldENlbnRlcjogdGFyZ2V0fSlcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICB1bnNjYWxlZFNpemUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnNjYXR0ZXJMb2FkZXIudW5zY2FsZWRTaXplKClcbiAgICB9XG5cbiAgICBmbGlwQ2VudGVyKCkge1xuICAgICAgICBsZXQgaXNTcHJpdGUgPSB0aGlzLnNjYXR0ZXIuZGlzcGxheU9iamVjdCBpbnN0YW5jZW9mIFBJWEkuU3ByaXRlXG4gICAgICAgIGxldCByZXNvbHV0aW9uID0gKGlzU3ByaXRlKSA/IGFwcC5yZW5kZXJlci5yZXNvbHV0aW9uIDogMVxuICAgICAgICBsZXQgY2VudGVyID0gdGhpcy5zY2F0dGVyLmNlbnRlclxuICAgICAgICBsZXQgY2FudmFzID0gYXBwLnJlbmRlcmVyLnZpZXdcbiAgICAgICAgbGV0IGRvbU5vZGUgPSB0aGlzLmRvbUZsaXAuZG9tU2NhdHRlckNvbnRhaW5lci5lbGVtZW50XG4gICAgICAgIGxldCBwYWdlID0gd2luZG93LmNvbnZlcnRQb2ludEZyb21Ob2RlVG9QYWdlKGNhbnZhcyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVyLngqcmVzb2x1dGlvbixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVyLnkqcmVzb2x1dGlvbilcbiAgICAgICAgbGV0IGxvY2FsID0gd2luZG93LmNvbnZlcnRQb2ludEZyb21QYWdlVG9Ob2RlKGRvbU5vZGUsIHBhZ2UueCwgcGFnZS55KVxuICAgICAgICByZXR1cm4gbG9jYWxcbiAgICB9XG5cbiAgICBjb25zdHJhaW50RmxpcENlbnRlcihjZW50ZXIsIGxvYWRlcikge1xuICAgICAgICBsZXQgdyA9IGxvYWRlci53YW50ZWRXaWR0aFxuICAgICAgICBsZXQgaCA9IGxvYWRlci53YW50ZWRIZWlnaHRcbiAgICAgICAgY29uc29sZS5sb2coXCJjb25zdHJhaW50RmxpcENlbnRlclwiLCB3LCBoKVxuICAgICAgICBsZXQgY2FudmFzID0gYXBwLnJlbmRlcmVyLnZpZXdcbiAgICAgICAgbGV0IHggPSBjZW50ZXIueFxuICAgICAgICBsZXQgeSA9IGNlbnRlci55XG4gICAgICAgIGlmICh4IDwgdy8yKVxuICAgICAgICAgICAgeCA9IHcvMlxuICAgICAgICBpZiAoeSA8IGgvMilcbiAgICAgICAgICAgeSA9IGgvMlxuICAgICAgICBpZiAoeCA+IGNhbnZhcy53aWR0aClcbiAgICAgICAgICAgIHggPSBjYW52YXMud2lkdGggLSB3LzJcbiAgICAgICAgaWYgKHkgPiBjYW52YXMuaGVpZ2h0KVxuICAgICAgICAgICAgeSA9IGNhbnZhcy5oZWlnaHQgLSBoLzJcbiAgICAgICAgcmV0dXJuIHsgeCwgeSB9XG4gICAgfVxuXG4gICAgc2V0dXBJbmZvQnV0dG9uKCkge1xuICAgICAgICBsZXQgaXNjYWxlID0gMS4wIC8gdGhpcy5zY2F0dGVyLnNjYWxlXG4gICAgICAgIHRoaXMuaW5mb0J0biA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAgICAgICAgdGhpcy5pbmZvQnRuLmJlZ2luRmlsbCgweDMzMzMzMylcbiAgICAgICAgdGhpcy5pbmZvQnRuLmxpbmVTdHlsZSg0LCAweEZGRkZGRilcbiAgICAgICAgdGhpcy5pbmZvQnRuLmRyYXdDaXJjbGUoMCwgMCwgMjIpXG4gICAgICAgIHRoaXMuaW5mb0J0bi5lbmRGaWxsKClcblxuICAgICAgICB0aGlzLmluZm9CdG4uYmVnaW5GaWxsKDB4RkZGRkZGKVxuICAgICAgICB0aGlzLmluZm9CdG4ubGluZVN0eWxlKDApXG4gICAgICAgIHRoaXMuaW5mb0J0bi5kcmF3Q2lyY2xlKDAsIC04LCA0KVxuICAgICAgICB0aGlzLmluZm9CdG4uZW5kRmlsbCgpXG5cbiAgICAgICAgdGhpcy5pbmZvQnRuLmxpbmVTdHlsZSg2LCAweEZGRkZGRilcbiAgICAgICAgdGhpcy5pbmZvQnRuLm1vdmVUbygwLCAtMilcbiAgICAgICAgdGhpcy5pbmZvQnRuLmxpbmVUbygwLCAxNClcbiAgICAgICAgdGhpcy5pbmZvQnRuLmVuZEZpbGwoKVxuXG4gICAgICAgIHRoaXMuaW5mb0J0bi5vbignY2xpY2snLCAoZSkgPT4geyB0aGlzLmluZm9TZWxlY3RlZCgpIH0pXG4gICAgICAgIHRoaXMuaW5mb0J0bi5vbigndGFwJywgKGUpID0+IHsgdGhpcy5pbmZvU2VsZWN0ZWQoKSB9KVxuXG4gICAgICAgIHRoaXMuaW5mb0J0bi5pbnRlcmFjdGl2ZSA9IHRydWVcbiAgICAgICAgdGhpcy5pbmZvQnRuLndpZHRoID0gNDRcbiAgICAgICAgdGhpcy5pbmZvQnRuLmhlaWdodCA9IDQ0XG4gICAgICAgIHRoaXMuaW5mb0J0bi5waXZvdC54ID0gMzBcbiAgICAgICAgdGhpcy5pbmZvQnRuLnBpdm90LnkgPSAzMFxuXG4gICAgICAgIGxldCBkaXNwbGF5T2JqZWN0ID0gdGhpcy5zY2F0dGVyLmRpc3BsYXlPYmplY3RcbiAgICAgICAgbGV0IFt3LCBoXSA9IHRoaXMudW5zY2FsZWRTaXplKClcbiAgICAgICAgdGhpcy5pbmZvQnRuLnBvc2l0aW9uID0gbmV3IFBJWEkuUG9pbnQodywgaClcbiAgICAgICAgaWYgKGRpc3BsYXlPYmplY3QuZm9yZWdyb3VuZCkge1xuICAgICAgICAgICAgdGhpcy5pbmZvQnRuLnNjYWxlLnggPSBpc2NhbGVcbiAgICAgICAgICAgIHRoaXMuaW5mb0J0bi5zY2FsZS55ID0gaXNjYWxlXG4gICAgICAgICAgICBkaXNwbGF5T2JqZWN0LmZvcmVncm91bmQuYWRkQ2hpbGQodGhpcy5pbmZvQnRuKVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgZGlzcGxheU9iamVjdC5hZGRDaGlsZCh0aGlzLmluZm9CdG4pXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnNjYXR0ZXIuYWRkVHJhbnNmb3JtRXZlbnRDYWxsYmFjayhlID0+IHtcbiAgICAgICAgICAgIGxldCBkaXNwbGF5T2JqZWN0ID0gdGhpcy5zY2F0dGVyLmRpc3BsYXlPYmplY3RcbiAgICAgICAgICAgIGlmIChkaXNwbGF5T2JqZWN0LmZvcmVncm91bmQpIHtcbiAgICAgICAgICAgICAgICBpZiAoZS5zY2FsZSkge1xuICAgICAgICAgICAgICAgICAgICBsZXQgaXNjYWxlID0gMS4wIC8gZS5zY2FsZVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmluZm9CdG4uc2NhbGUueCA9IGlzY2FsZVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmluZm9CdG4uc2NhbGUueSA9IGlzY2FsZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICBzZXR1cEJ1dHRvbih1cmwpIHtcbiAgICAgICAgbGV0IHN2Z0ltYWdlID0gbmV3IEltYWdlKClcbiAgICAgICAgbGV0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpXG4gICAgICAgIGNhbnZhcy53aWR0aCA9IDg4ICogNFxuICAgICAgICBjYW52YXMuaGVpZ2h0ID0gNDQgKiA0XG4gICAgICAgIHN2Z0ltYWdlLm9ubG9hZCA9IGUgPT4ge1xuICAgICAgICAgICAgbGV0IGRpc3BsYXlPYmplY3QgPSB0aGlzLnNjYXR0ZXIuZGlzcGxheU9iamVjdFxuICAgICAgICAgICAgY2FudmFzLmdldENvbnRleHQgKCcyZCcpLmRyYXdJbWFnZShzdmdJbWFnZSwgMCwgMCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0KVxuICAgICAgICAgICAgbGV0IHRleHVyZSA9IG5ldyBQSVhJLlRleHR1cmUobmV3IFBJWEkuQmFzZVRleHR1cmUoY2FudmFzKSlcbiAgICAgICAgICAgIHRoaXMuaW5mb0J0biA9IG5ldyBQSVhJLlNwcml0ZSh0ZXh1cmUpXG4gICAgICAgICAgICB0aGlzLmluZm9CdG4uYW5jaG9yLnNldCgwLjUsIDAuNSlcbiAgICAgICAgICAgIGlmIChkaXNwbGF5T2JqZWN0LmZvcmVncm91bmQpIHtcbiAgICAgICAgICAgICAgICBkaXNwbGF5T2JqZWN0LmZvcmVncm91bmQuYWRkQ2hpbGQodGhpcy5pbmZvQnRuKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgZGlzcGxheU9iamVjdC5hZGRDaGlsZCh0aGlzLmluZm9CdG4pXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmluZm9CdG4uc2NhbGUuc2V0KDAuNSwgMC41KVxuXG4gICAgICAgICAgICBsZXQgW3csIGhdID0gdGhpcy51bnNjYWxlZFNpemUoKVxuICAgICAgICAgICAgdGhpcy5pbmZvQnRuLnBvc2l0aW9uID0gbmV3IFBJWEkuUG9pbnQodywgaClcbiAgICAgICAgICAgIHRoaXMuaW5mb0J0bi5pbnRlcmFjdGl2ZSA9IHRydWVcbiAgICAgICAgICAgIHRoaXMuaW5mb0J0bi51cGRhdGVUcmFuc2Zvcm0oKVxuICAgICAgICAgICAgdGhpcy5pbmZvQnRuLm9uKCdjbGljaycsIChlKSA9PiB7IHRoaXMuaW5mb1NlbGVjdGVkKCkgfSlcbiAgICAgICAgICAgIHRoaXMuaW5mb0J0bi5vbigndGFwJywgKGUpID0+IHsgdGhpcy5pbmZvU2VsZWN0ZWQoKSB9KVxuICAgICAgICB9XG4gICAgICAgIHN2Z0ltYWdlLnNyYyA9IHVybFxuICAgIH1cblxuICAgIGluZm9TZWxlY3RlZCgpIHtcbiAgICAgICAgdGhpcy5zdGFydEZsaXAoKVxuICAgIH1cblxuICAgIGJhY2tTZWxlY3RlZCgpIHtcbiAgICAgICAgdGhpcy5kb21GbGlwLnN0YXJ0KClcbiAgICB9XG5cbiAgICBiYWNrQ2FyZENsb3NlZCgpIHtcbiAgICAgICAgLyoqKiBUaGUgZmxpcCBlZmZlY3Qgc2hvdWxkIG5vdyBiZSBpbiBpdCdzIGluaXRpYWwgc3RhdGUgYWdhaW4uIEFsbFxuICAgICAgICBtZW1vcnkgc2hvdWxkIGJlIGZyZWVkLiAqKiovXG4gICAgICAgIGxldCBkaXNwbGF5T2JqZWN0ID0gdGhpcy5zY2F0dGVyLmRpc3BsYXlPYmplY3RcbiAgICAgICAgZGlzcGxheU9iamVjdC52aXNpYmxlID0gdHJ1ZVxuICAgICAgICB0aGlzLmRvbUZsaXAuZmFkZU91dEFuZFJlbW92ZSgpXG4gICAgICAgIHRoaXMuZmxpcHBlZCA9IGZhbHNlXG4gICAgfVxuXG4gICAgdGFyZ2V0Um90YXRpb24oYWxwaGEpIHtcbiAgICAgICAgbGV0IG9ydGhvID0gOTBcbiAgICAgICAgbGV0IHJlc3QgPSBhbHBoYSAlIG9ydGhvXG4gICAgICAgIGxldCBkZWx0YSA9IDAuMFxuICAgICAgICBpZiAocmVzdCA+IChvcnRobyAvIDIuMCkpIHtcbiAgICAgICAgICAgIGRlbHRhID0gb3J0aG8gLSByZXN0XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBkZWx0YSA9IC1yZXN0XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGRlbHRhXG4gICAgfVxufVxuXG4iLCIvKiBnbG9iYWxzIFBvd2VyMiwgU2luZSAqL1xuLyplc2xpbnQgbm8tY29uc29sZTogW1wiZXJyb3JcIiwgeyBhbGxvdzogW1wibG9nXCIsIFwiaW5mb1wiLCBcImVycm9yXCJdIH1dKi9cblxuLyoqXG4gKiBDYWxsYmFjayBmb3IgdGhlIGZsaXBwYWJsZSBvblN0YXJ0IGZ1bmN0aW9uLlxuICpcbiAqIEBjYWxsYmFjayBvblN0YXJ0Q2FsbGJhY2tcbiAqIEBwYXJhbSB7RmxpcHBhYmxlfSBmbGlwcGFibGUgLSBBIHJlZmVyZW5jZSB0byB0aGUgZmxpcHBhYmxlIChhbHNvIHRoaXMgcmVmZXJzIHRvIHRoZSBmbGlwcGFibGUpLlxuICovXG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHRoZSBmbGlwcGFibGUgb25VcGRhdGUgZnVuY3Rpb24uXG4gKlxuICogQGNhbGxiYWNrIG9uVXBkYXRlQ2FsbGJhY2tcbiAqIEBwYXJhbSB7RmxpcHBhYmxlfSBmbGlwcGFibGUgLSBBIHJlZmVyZW5jZSB0byB0aGUgZmxpcHBhYmxlIChhbHNvIHRoaXMgcmVmZXJzIHRvIHRoZSBmbGlwcGFibGUpLlxuICovXG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHRoZSBmbGlwcGFibGUgb25Db21wbGV0ZSBmdW5jdGlvbi5cbiAqXG4gKiBAY2FsbGJhY2sgb25Db21wbGV0ZUNhbGxiYWNrXG4gKiBAcGFyYW0ge0ZsaXBwYWJsZX0gZmxpcHBhYmxlIC0gQSByZWZlcmVuY2UgdG8gdGhlIGZsaXBwYWJsZSAoYWxzbyB0aGlzIHJlZmVycyB0byB0aGUgZmxpcHBhYmxlKS5cbiAqL1xuXG4vKipcbiAqIENsYXNzIHRoYXQgcmVwcmVzZW50cyBhIFBpeGlKUyBGbGlwcGFibGUuXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IGZyb250ID0gUElYSS5TcHJpdGUuZnJvbUltYWdlKCcuL2Fzc2V0cy9mcm9udC5qcGcnKVxuICogY29uc3QgYmFjayA9IFBJWEkuU3ByaXRlLmZyb21JbWFnZSgnLi9hc3NldHMvYmFjay5qcGcnKVxuICogYXBwLnNjZW5lLmFkZENoaWxkKGZyb250KVxuICogXG4gKiAvLyBDcmVhdGUgdGhlIGZsaXBwYWJsZVxuICogY29uc3QgZmxpcHBhYmxlID0gbmV3IEZsaXBwYWJsZShmcm9udCwgYmFjaywgYXBwLnJlbmRlcmVyKVxuICogXG4gKiBmcm9udC5pbnRlcmFjdGl2ZSA9IHRydWVcbiAqIGZyb250Lm9uKCdjbGljaycsIGV2ZW50ID0+IGZsaXBwYWJsZS50b2dnbGUoKSlcbiAqXG4gKiBAY2xhc3NcbiAqIEBleHRlbmRzIFBJWEkucHJvamVjdGlvbi5DYW1lcmEzZFxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly9naXRodWIuY29tL3BpeGlqcy9waXhpLXByb2plY3Rpb258UGl4aUpTIFByb2plY3Rpb259XG4gKiBAc2VlIHtAbGluayBodHRwczovL3d3dy5pd20tdHVlYmluZ2VuLmRlL2l3bWJyb3dzZXIvbGliL3BpeGkvZmxpcHBhYmxlLmh0bWx8RG9jVGVzdH1cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRmxpcHBhYmxlIGV4dGVuZHMgUElYSS5wcm9qZWN0aW9uLkNhbWVyYTNkIHtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYSBGbGlwcGFibGUuXG4gICAgICpcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge1BJWEkuRGlzcGxheU9iamVjdH0gZnJvbnQgLSBUaGUgb2JqZWN0IHRvIHNob3cgaW5pdGlhbGx5LiBTaG91bGQgaGF2ZSBiZWVuIGFkZGVkIHRvIHRoZSBzY2VuZS5cbiAgICAgKiBAcGFyYW0ge1BJWEkuRGlzcGxheU9iamVjdH0gYmFjayAtIFRoZSBvYmplY3QgdG8gc2hvdyBvbiB0aGUgYmFja3NpZGUuIFNob3VsZCBoYXZlIG5vdCBiZWVuIGFkZGVkIHRvIHRoZSBzY2VuZS5cbiAgICAgKiBAcGFyYW0ge1BJWEkuV2ViR0xSZW5kZXJlcnxQSVhJLkNhbnZhc1JlbmRlcmVyfSByZW5kZXJlciAtIFRoZSByZW5kZXJlciBvZiB0aGUgYXBwbGljYXRpb24uXG4gICAgICogQHBhcmFtIHtvYmplY3R9IFtvcHRzXSAtIEFuIG9wdGlvbnMgb2JqZWN0IHdoaWNoIGNhbiBjb250YWluIHRoZSBmb2xsb3dpbmcgcHJvcGVydGllcy5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZHVyYXRpb249MV0gLSBUaGUgZHVyYXRpb24gb2YgdGhlIGZsaXAgYW5pbWF0aW9uIGluIHNlY29uZHMuXG4gICAgICogQHBhcmFtIHtHU0FQLkVhc2V9IFtvcHRzLmVhc2U9UG93ZXIyLmVhc2VPdXRdIC0gVGhlIGVhc2Ugb2YgdGhlIGZsaXAgYW5pbWF0aW9uLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuc2hhZG93PWZhbHNlXSAtIFNob3VsZCBiZSBhIHNoYWRvdyBiZWVuIGRpc3BsYXkgZHVyaW5nIHRoZSBhbmltYXRpb24/XG4gICAgICogQHBhcmFtIHtudW1lcn0gW29wdHMuZXVsZXJYPTBdIC0gVGhlIHNoaWZ0IG9mIHRoZSB4LWF4aXMgZHVyaW5nIHRoZSBhbmltYXRpb24uXG4gICAgICogQHBhcmFtIHtudW1lcn0gW29wdHMuZXVsZXJZPTBdIC0gVGhlIHNoaWZ0IG9mIHRoZSB5LWF4aXMgZHVyaW5nIHRoZSBhbmltYXRpb24uXG4gICAgICogQHBhcmFtIHtHU0FQLkVhc2V9IFtvcHRzLmV1bGVyRWFzZT1TaW5lLmVhc2VPdXRdIC0gVGhlIGVhc2Ugb2YgdGhlIHNoaWZ0LlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMudXNlQmFja1RyYW5zZm9ybXM9ZmFsc2VdIC0gV2hlbiBzZXQgdG8gdHJ1ZSwgdGhlIGZsaXAgYW5pbWF0aW9uIGFsc28gYW5pbWF0ZXMgdG8gdGhlIHRyYW5zZm9ybSBwYXJhbWV0ZXJzIG9mIHRoZSBiYWNrLW9iamVjdC5cbiAgICAgKiBAcGFyYW0ge0dTQVAuRWFzZX0gW29wdHMudHJhbnNmb3JtRWFzZT1Qb3dlcjIuZWFzZU91dF0gLSBUaGUgZWFzZSBvZiB0aGUgdHJhbnNmb3JtLlxuICAgICAqIEBwYXJhbSB7bnVtZXJ9IFtvcHRzLmZvY3VzPTgwMF0gLSBUaGUgdmFsdWUgb2YgdGhlIGZvY3VzIG9mIHRoZSAzRCBjYW1lcmEgKHNlZSBwaXhpLXByb2plY3Rpb24pLlxuICAgICAqIEBwYXJhbSB7bnVtZXJ9IFtvcHRzLm5lYXI9MTBdIC0gVGhlIG5lYXIgdmFsdWUgb2YgdGhlIDNEIGNhbWVyYSAoc2VlIHBpeGktcHJvamVjdGlvbikuXG4gICAgICogQHBhcmFtIHtudW1lcn0gW29wdHMuZmFyPTEwMDAwXSAtIFRoZSBmYXIgdmFsdWUgb2YgdGhlIDNEIGNhbWVyYSAoc2VlIHBpeGktcHJvamVjdGlvbikuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5vcnRob2dyYXBoaWM9ZmFsc2VdIC0gU2hvdWxkIHRoZSBmbGlwIGFuaW1hdGlvbiBiZSBhbiBvcnRob2dyYXBoaWMgYW5pbWF0aW9uP1xuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLm9uU3RhcnQ9bnVsbF0gLSBBIGNhbGxiYWNrIGV4ZWN1dGVkIG9uIHN0YXJ0IG9mIHRoZSBmbGlwIGFuaW1hdGlvbi5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy5vblVwZGF0ZT1udWxsXSAtIEEgY2FsbGJhY2sgZXhlY3V0ZWQgb24gZWFjaCBzdGVwIG9mIHRoZSBmbGlwIGFuaW1hdGlvbi5cbiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbb3B0cy5vbkNvbXBsZXRlPW51bGxdIC0gQSBjYWxsYmFjayBleGVjdXRlZCB3aGVuIHRoZSBmbGlwIGFuaW1hdGlvbiBpcyBmaW5pc2hlZC5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3Rvcihmcm9udCwgYmFjaywgcmVuZGVyZXIsIG9wdHMgPSB7fSkge1xuXG4gICAgICAgIHN1cGVyKClcblxuICAgICAgICB0aGlzLm9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBmcm9udCxcbiAgICAgICAgICAgIGJhY2ssXG4gICAgICAgICAgICByZW5kZXJlcixcbiAgICAgICAgICAgIGR1cmF0aW9uOiAxLFxuICAgICAgICAgICAgZWFzZTogUG93ZXIyLmVhc2VPdXQsXG4gICAgICAgICAgICBzaGFkb3c6IGZhbHNlLFxuICAgICAgICAgICAgZXVsZXJYOiAwLFxuICAgICAgICAgICAgZXVsZXJZOiAwLFxuICAgICAgICAgICAgZXVsZXJFYXNlOiBTaW5lLmVhc2VPdXQsXG4gICAgICAgICAgICB1c2VCYWNrVHJhbnNmb3JtczogZmFsc2UsXG4gICAgICAgICAgICB0cmFuc2Zvcm1FYXNlOiBQb3dlcjIuZWFzZU91dCxcbiAgICAgICAgICAgIGZvY3VzOiA4MDAsXG4gICAgICAgICAgICBuZWFyOiAxMCxcbiAgICAgICAgICAgIGZhcjogMTAwMDAsXG4gICAgICAgICAgICBvcnRob2dyYXBoaWM6IGZhbHNlXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgLy8gcGxhbmVzXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5zZXRQbGFuZXModGhpcy5vcHRzLmZvY3VzLCB0aGlzLm9wdHMubmVhciwgdGhpcy5vcHRzLmZhciwgdGhpcy5vcHRzLm9ydGhvZ3JhcGhpYylcblxuICAgICAgICAvLyBmbGlwcGVkXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5fZmxpcHBlZCA9IGZhbHNlXG5cbiAgICAgICAgLy8gb2JqZWN0c1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMub2JqZWN0cyA9IHt9XG5cbiAgICAgICAgLy8gc2V0dXBcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnNldHVwKClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGNoaWxkcmVuIGFuZCBpbnN0YW50aWF0ZXMgZXZlcnl0aGluZy5cbiAgICAgKlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybiB7RmxpcHBhYmxlfSBBIHJlZmVyZW5jZSB0byB0aGUgZmxpcHBhYmxlIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzZXR1cCgpIHtcblxuICAgICAgICBjb25zdCBzY2FsZSA9IC41XG5cbiAgICAgICAgLy8gZmlsdGVyc1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGNvbnN0IGJsdXJGaWx0ZXIgPSBuZXcgUElYSS5maWx0ZXJzLkJsdXJGaWx0ZXIoKVxuICAgICAgICBibHVyRmlsdGVyLmJsdXIgPSAwLjJcbiAgICAgICAgdGhpcy5vYmplY3RzLmJsdXJGaWx0ZXIgPSBibHVyRmlsdGVyXG5cbiAgICAgICAgLy8gb3V0ZXJcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBjb25zdCBvdXRlciA9IG5ldyBQSVhJLnByb2plY3Rpb24uQ29udGFpbmVyM2QoKVxuICAgICAgICBvdXRlci5zY2FsZTNkLnNldChzY2FsZSlcbiAgICAgICAgdGhpcy5hZGRDaGlsZChvdXRlcilcbiAgICAgICAgdGhpcy5vYmplY3RzLm91dGVyID0gb3V0ZXJcblxuICAgICAgICAvLyBzaGFkb3dcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBjb25zdCBzaGFkb3cgPSBuZXcgUElYSS5wcm9qZWN0aW9uLlNwcml0ZTNkKFBJWEkuVGV4dHVyZS5mcm9tSW1hZ2UoJy4uLy4uL2Fzc2V0cy9pbWFnZXMvc2hhZG93LnBuZycpKVxuICAgICAgICBzaGFkb3cucmVuZGVyYWJsZSA9IGZhbHNlXG4gICAgICAgIHNoYWRvdy5hbmNob3Iuc2V0KDAuNSlcbiAgICAgICAgc2hhZG93LnNjYWxlM2Quc2V0KC45OClcbiAgICAgICAgc2hhZG93LmFscGhhID0gMC43XG4gICAgICAgIHNoYWRvdy5maWx0ZXJzID0gW2JsdXJGaWx0ZXJdXG4gICAgICAgIHNoYWRvdy52aXNpYmxlID0gdGhpcy5vcHRzLnNoYWRvd1xuICAgICAgICBvdXRlci5hZGRDaGlsZChzaGFkb3cpXG4gICAgICAgIHRoaXMub2JqZWN0cy5zaGFkb3cgPSBzaGFkb3dcbiAgICAgICAgXG4gICAgICAgIC8vIGlubmVyXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgY29uc3QgaW5uZXIgPSBuZXcgUElYSS5wcm9qZWN0aW9uLkNvbnRhaW5lcjNkKClcbiAgICAgICAgaW5uZXIuZXVsZXIueSA9IE1hdGguUElcbiAgICAgICAgb3V0ZXIuYWRkQ2hpbGQoaW5uZXIpXG4gICAgICAgIHRoaXMub2JqZWN0cy5pbm5lciA9IGlubmVyXG5cbiAgICAgICAgLy8gZnJvbnRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBjb25zdCBmcm9udCA9IG5ldyBQSVhJLnByb2plY3Rpb24uU3ByaXRlM2QoUElYSS5UZXh0dXJlLkVNUFRZKVxuICAgICAgICBmcm9udC5zY2FsZS5zZXQoLTEgLyBzY2FsZSwgMSAvIHNjYWxlKVxuICAgICAgICBmcm9udC5yZW5kZXJhYmxlID0gdHJ1ZVxuICAgICAgICBmcm9udC5hbmNob3Iuc2V0KC41KVxuICAgICAgICBpbm5lci5hZGRDaGlsZChmcm9udClcbiAgICAgICAgdGhpcy5vYmplY3RzLmZyb250ID0gZnJvbnRcblxuICAgICAgICAvLyBiYWNrXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgY29uc3QgYmFjayA9IG5ldyBQSVhJLnByb2plY3Rpb24uU3ByaXRlM2QoUElYSS5UZXh0dXJlLkVNUFRZKVxuICAgICAgICBiYWNrLnNjYWxlLnNldCgxIC8gc2NhbGUsIDEgLyBzY2FsZSlcbiAgICAgICAgYmFjay5yZW5kZXJhYmxlID0gZmFsc2VcbiAgICAgICAgYmFjay5hbmNob3Iuc2V0KC41KVxuICAgICAgICBpbm5lci5hZGRDaGlsZChiYWNrKVxuICAgICAgICB0aGlzLm9iamVjdHMuYmFjayA9IGJhY2tcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldHMgb3Igc2V0cyB0aGUgZmxpcHBlZCBzdGF0ZS5cbiAgICAgKlxuICAgICAqIEBtZW1iZXIge2Jvb2xlYW59XG4gICAgICovXG4gICAgZ2V0IGZsaXBwZWQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9mbGlwcGVkXG4gICAgfVxuICAgIHNldCBmbGlwcGVkKHRvQmFjaykge1xuXG4gICAgICAgIHRoaXMuX2ZsaXBwZWQgPSB0b0JhY2tcblxuICAgICAgICAvLyByZWZlcmVuY2VzXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgY29uc3QgZnJvbnQgPSB0aGlzLm9iamVjdHMuZnJvbnRcbiAgICAgICAgY29uc3QgYmFjayA9IHRoaXMub2JqZWN0cy5iYWNrXG4gICAgICAgIGNvbnN0IGlubmVyID0gdGhpcy5vYmplY3RzLmlubmVyXG4gICAgICAgIGNvbnN0IHNoYWRvdyA9IHRoaXMub2JqZWN0cy5zaGFkb3dcbiAgICAgICAgY29uc3QgYmx1ckZpbHRlciA9IHRoaXMub2JqZWN0cy5ibHVyRmlsdGVyXG5cbiAgICAgICAgY29uc3QgaGFsZiA9IHRoaXMub3B0cy5kdXJhdGlvbiAvIDJcbiAgICAgICAgY29uc3QgZWFzZSA9IHRoaXMub3B0cy5ldWxlckVhc2VcblxuICAgICAgICBjb25zdCBmcm9tT2JqZWN0ID0gdG9CYWNrID8gdGhpcy5vcHRzLmZyb250IDogdGhpcy5vcHRzLmJhY2tcbiAgICAgICAgY29uc3QgdG9PYmplY3QgPSB0b0JhY2sgPyB0aGlzLm9wdHMuYmFjayA6IHRoaXMub3B0cy5mcm9udFxuXG4gICAgICAgIC8vIHNldCB0ZXh0dXJlcyBmb3IgdmlydHVhbCBmcm9udCBhbmQgdmlydHVhbCBiYWNrXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgZnJvbnQudGV4dHVyZSA9IHRoaXMuZ2VuZXJhdGVUZXh0dXJlKHRoaXMub3B0cy5mcm9udClcbiAgICAgICAgYmFjay50ZXh0dXJlID0gdGhpcy5nZW5lcmF0ZVRleHR1cmUodGhpcy5vcHRzLmJhY2spXG4gICAgICAgIFxuICAgICAgICAvLyBzd2l0Y2ggb2JqZWN0cyBhbmQgc2V0IHBhcmFtcyBmb3IgdmlydHVhbCBvYmplY3RzXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgY29uc3QgZnJvbUNlbnRlciA9IHRoaXMuYW5jaG9yVG9DZW50ZXIoZnJvbU9iamVjdClcbiAgICAgICAgY29uc3QgdG9DZW50ZXIgPSB0aGlzLmFuY2hvclRvQ2VudGVyKHRvT2JqZWN0KVxuXG4gICAgICAgIC8vIGZyb20gdmFsdWVzXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy54ID0gZnJvbUNlbnRlci54XG4gICAgICAgIHRoaXMueSA9IGZyb21DZW50ZXIueVxuICAgICAgICBmcm9udC53aWR0aCA9IGZyb21PYmplY3Qud2lkdGggKiAyXG4gICAgICAgIGZyb250LmhlaWdodCA9IGZyb21PYmplY3QuaGVpZ2h0ICogMlxuICAgICAgICBiYWNrLndpZHRoID0gZnJvbU9iamVjdC53aWR0aCAqIDJcbiAgICAgICAgYmFjay5oZWlnaHQgPSBmcm9tT2JqZWN0LmhlaWdodCAqIDJcbiAgICAgICAgdGhpcy5yb3RhdGlvbiA9IGZyb21PYmplY3Qucm90YXRpb25cbiAgICAgICAgdGhpcy5za2V3LnggPSBmcm9tT2JqZWN0LnNrZXcueFxuICAgICAgICB0aGlzLnNrZXcueSA9IGZyb21PYmplY3Quc2tldy55XG5cbiAgICAgICAgLy8gY2FsY3VsYXRlIHRvIHZhbHVlc1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGNvbnN0IHRvID0ge1xuICAgICAgICAgICAgeDogdGhpcy5vcHRzLnVzZUJhY2tUcmFuc2Zvcm1zID8gdG9DZW50ZXIueCA6IGZyb21DZW50ZXIueCxcbiAgICAgICAgICAgIHk6IHRoaXMub3B0cy51c2VCYWNrVHJhbnNmb3JtcyA/IHRvQ2VudGVyLnkgOiBmcm9tQ2VudGVyLnksXG4gICAgICAgICAgICBhbmNob3JYOiB0aGlzLm9wdHMudXNlQmFja1RyYW5zZm9ybXMgPyB0b09iamVjdC54IDogZnJvbU9iamVjdC54LFxuICAgICAgICAgICAgYW5jaG9yWTogdGhpcy5vcHRzLnVzZUJhY2tUcmFuc2Zvcm1zID8gdG9PYmplY3QueSA6IGZyb21PYmplY3QueSxcbiAgICAgICAgICAgIHdpZHRoOiB0aGlzLm9wdHMudXNlQmFja1RyYW5zZm9ybXMgPyB0b09iamVjdC53aWR0aCAqIDIgOiBmcm9tT2JqZWN0LndpZHRoICogMixcbiAgICAgICAgICAgIGhlaWdodDogdGhpcy5vcHRzLnVzZUJhY2tUcmFuc2Zvcm1zID8gdG9PYmplY3QuaGVpZ2h0ICogMiA6IGZyb21PYmplY3QuaGVpZ2h0ICogMixcbiAgICAgICAgICAgIHJvdGF0aW9uOiB0aGlzLm9wdHMudXNlQmFja1RyYW5zZm9ybXMgPyB0b09iamVjdC5yb3RhdGlvbiA6IGZyb21PYmplY3Qucm90YXRpb24sXG4gICAgICAgICAgICBza2V3WDogdGhpcy5vcHRzLnVzZUJhY2tUcmFuc2Zvcm1zID8gdG9PYmplY3Quc2tldy54IDogZnJvbU9iamVjdC5za2V3LngsXG4gICAgICAgICAgICBza2V3WTogdGhpcy5vcHRzLnVzZUJhY2tUcmFuc2Zvcm1zID8gdG9PYmplY3Quc2tldy55IDogZnJvbU9iamVjdC5za2V3LnlcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHNldCB0b09iamVjdCBlbmQgdmFsdWVzXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdG9PYmplY3QueCA9IHRvLmFuY2hvclhcbiAgICAgICAgdG9PYmplY3QueSA9IHRvLmFuY2hvcllcbiAgICAgICAgdG9PYmplY3Qud2lkdGggPSB0by53aWR0aCAvIDJcbiAgICAgICAgdG9PYmplY3QuaGVpZ2h0ID0gdG8uaGVpZ2h0IC8gMlxuICAgICAgICB0b09iamVjdC5yb3RhdGlvbiA9IHRvLnJvdGF0aW9uXG4gICAgICAgIHRvT2JqZWN0LnNrZXcueCA9IHRvLnNrZXdYXG4gICAgICAgIHRvT2JqZWN0LnNrZXcueSA9IHRvLnNrZXdZXG5cbiAgICAgICAgLy8gZmxpcFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIFR3ZWVuTGl0ZS50byhpbm5lci5ldWxlciwgdGhpcy5vcHRzLmR1cmF0aW9uLCB7XG4gICAgICAgICAgICB5OiB0b0JhY2sgPyAwIDogTWF0aC5QSSxcbiAgICAgICAgICAgIGVhc2U6IHRoaXMub3B0cy5lYXNlLFxuICAgICAgICAgICAgb25TdGFydDogKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc3dpdGNoRGlzcGxheU9iamVjdChmcm9tT2JqZWN0LCB0aGlzKVxuICAgICAgICAgICAgICAgIHNoYWRvdy5yZW5kZXJhYmxlID0gdHJ1ZVxuICAgICAgICAgICAgICAgIGlmICh0aGlzLm9wdHMub25TdGFydCkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLm9wdHMub25TdGFydCh0aGlzLCB0aGlzKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBvblVwZGF0ZTogKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMubGF5b3V0KClcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5vcHRzLm9uVXBkYXRlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMub3B0cy5vblVwZGF0ZSh0aGlzLCB0aGlzKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBvbkNvbXBsZXRlOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5zd2l0Y2hEaXNwbGF5T2JqZWN0KHRoaXMsIHRvT2JqZWN0KVxuICAgICAgICAgICAgICAgIHNoYWRvdy5yZW5kZXJhYmxlID0gZmFsc2VcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5vcHRzLm9uQ29tcGxldGUpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vcHRzLm9uQ29tcGxldGUodGhpcywgdGhpcylcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8geCAmIHlcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBUd2VlbkxpdGUudG8odGhpcywgdGhpcy5vcHRzLmR1cmF0aW9uLCB7XG4gICAgICAgICAgICB4OiB0by54LFxuICAgICAgICAgICAgeTogdG8ueSxcbiAgICAgICAgICAgIGVhc2U6IHRoaXMub3B0cy50cmFuc2Zvcm1FYXNlXG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gd2lkdGggJiBoZWlnaHRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBUd2VlbkxpdGUudG8oW2Zyb250LCBiYWNrXSwgdGhpcy5vcHRzLmR1cmF0aW9uLCB7XG4gICAgICAgICAgICB3aWR0aDogdG8ud2lkdGgsXG4gICAgICAgICAgICBoZWlnaHQ6IHRvLmhlaWdodCxcbiAgICAgICAgICAgIGVhc2U6IHRoaXMub3B0cy50cmFuc2Zvcm1FYXNlXG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gcm90YXRpb25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBUd2VlbkxpdGUudG8odGhpcywgdGhpcy5vcHRzLmR1cmF0aW9uLCB7XG4gICAgICAgICAgICBkaXJlY3Rpb25hbFJvdGF0aW9uOiB7XG4gICAgICAgICAgICAgICAgcm90YXRpb246IGAke3RvLnJvdGF0aW9ufV9zaG9ydGAsXG4gICAgICAgICAgICAgICAgdXNlUmFkaWFuczogdHJ1ZVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGVhc2U6IHRoaXMub3B0cy50cmFuc2Zvcm1FYXNlXG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gc2tld1ggJiBza2V3WVxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLnNrZXcsIHRoaXMub3B0cy5kdXJhdGlvbiwge1xuICAgICAgICAgICAgeDogdG8uc2tld1gsXG4gICAgICAgICAgICB5OiB0by5za2V3WSxcbiAgICAgICAgICAgIGVhc2U6IHRoaXMub3B0cy50cmFuc2Zvcm1FYXNlXG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gY2FtZXJhXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgbmV3IFRpbWVsaW5lTWF4KClcbiAgICAgICAgICAgIC50byh0aGlzLmV1bGVyLCBoYWxmLCB7eDogdGhpcy5vcHRzLmV1bGVyWCwgeTogdGhpcy5vcHRzLmV1bGVyWSwgZWFzZX0pXG4gICAgICAgICAgICAudG8odGhpcy5ldWxlciwgaGFsZiwge3g6IDAsIHk6IDAsIGVhc2V9KVxuXG4gICAgICAgIC8vIHNoYWRvd1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIG5ldyBUaW1lbGluZU1heCgpXG4gICAgICAgICAgICAudG8oc2hhZG93LCBoYWxmLCB7YWxwaGE6IC4zLCBlYXNlfSlcbiAgICAgICAgICAgIC50byhzaGFkb3csIGhhbGYsIHthbHBoYTogLjcsIGVhc2V9KVxuICAgICAgICBcbiAgICAgICAgLy8gYmx1cmZpbHRlclxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIG5ldyBUaW1lbGluZU1heCgpXG4gICAgICAgICAgICAudG8oYmx1ckZpbHRlciwgaGFsZiwge2JsdXI6IDYsIGVhc2V9KVxuICAgICAgICAgICAgLnRvKGJsdXJGaWx0ZXIsIGhhbGYsIHtibHVyOiAuMiwgZWFzZX0pXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2hvdWxkIGJlIGNhbGxlZCB0byByZWZyZXNoIHRoZSBsYXlvdXQgb2YgdGhlIGNhbWVyYS5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge0ZsaXBwYWJsZX0gQSByZWZlcmVuY2UgdG8gdGhlIGZsaXBwYWJsZSBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgbGF5b3V0KCkge1xuXG4gICAgICAgIGNvbnN0IGZyb250ID0gdGhpcy5vYmplY3RzLmZyb250XG4gICAgICAgIGNvbnN0IGJhY2sgPSB0aGlzLm9iamVjdHMuYmFja1xuICAgICAgICBjb25zdCBzaGFkb3cgPSB0aGlzLm9iamVjdHMuc2hhZG93XG4gICAgICAgIGNvbnN0IGlubmVyID0gdGhpcy5vYmplY3RzLmlubmVyXG4gICAgICAgIFxuICAgICAgICBpbm5lci5wb3NpdGlvbjNkLnogPSAtTWF0aC5zaW4oaW5uZXIuZXVsZXIueSkgKiBmcm9udC50ZXh0dXJlLmJhc2VUZXh0dXJlLndpZHRoICogMlxuXG4gICAgICAgIC8vdGhpcy5vYmplY3RzLnNoYWRvdy5ldWxlciA9IHRoaXMub2JqZWN0cy5pbm5lci5ldWxlclxuICAgICAgICBzaGFkb3cuZXVsZXIueCA9IC1pbm5lci5ldWxlci54XG4gICAgICAgIHNoYWRvdy5ldWxlci55ID0gLWlubmVyLmV1bGVyLnlcbiAgICAgICAgXG4gICAgICAgIGlmICh0aGlzLmZyb250U2lkZUluRnJvbnQpIHtcbiAgICAgICAgICAgIGZyb250LnJlbmRlcmFibGUgPSB0cnVlXG4gICAgICAgICAgICBiYWNrLnJlbmRlcmFibGUgPSBmYWxzZVxuICAgICAgICAgICAgc2hhZG93LndpZHRoID0gZnJvbnQud2lkdGhcbiAgICAgICAgICAgIHNoYWRvdy5oZWlnaHQgPSBmcm9udC5oZWlnaHRcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGZyb250LnJlbmRlcmFibGUgPSBmYWxzZVxuICAgICAgICAgICAgYmFjay5yZW5kZXJhYmxlID0gdHJ1ZVxuICAgICAgICAgICAgc2hhZG93LndpZHRoID0gYmFjay53aWR0aFxuICAgICAgICAgICAgc2hhZG93LmhlaWdodCA9IGJhY2suaGVpZ2h0XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRvZ2dsZXMgdGhlIGZsaXBwYWJsZS4gU3dpdGNoZXMgdGhlIHNpZGVzLlxuICAgICAqXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcmV0dXJuIHtGbGlwcGFibGV9IEEgcmVmZXJlbmNlIHRvIHRoZSBmbGlwcGFibGUgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHRvZ2dsZSgpIHtcbiAgICAgICAgdGhpcy5mbGlwcGVkID0gIXRoaXMuZmxpcHBlZFxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0cyB0aGUgYWxpZ25tZW50IHN0YXRlLiB0cnVlIGlmIHRoZSBmcm9udCBzaWRlIGlzIGluIGZyb250LCBmYWxzZSBvdGhlcndpc2UuXG4gICAgICpcbiAgICAgKiBAbWVtYmVyIHtib29sZWFufVxuICAgICAqL1xuICAgIGdldCBmcm9udFNpZGVJbkZyb250KCkge1xuICAgICAgICByZXR1cm4gIXRoaXMub2JqZWN0cy5pbm5lci5pc0Zyb250RmFjZSgpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2FsY3VsYXRlcyB0aGUgY2VudGVyIHBvaW50IG9mIGFuIFBJWEkuRGlzcGxheU9iamVjdC5cbiAgICAgKlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHtQSVhJLkRpc3BsYXlPYmplY3R9IGRpc3BsYXlPYmplY3QgLSBUaGUgRGlzcGxheU9iamVjdCBmcm9tIHdoaWNoIHRvIGNhbGN1bGF0ZSB0aGUgY2VudGVyLlxuICAgICAqIEByZXR1cm4ge29iamVjdH0gUmV0dXJuIGFuIG9iamVjdCB3aXRoIHggYW5kIHkuXG4gICAgICovXG4gICAgYW5jaG9yVG9DZW50ZXIoZGlzcGxheU9iamVjdCkge1xuICAgICAgICBjb25zdCBib3VuZHMgPSBkaXNwbGF5T2JqZWN0LmdldEJvdW5kcygpXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB4OiBib3VuZHMueCArIGJvdW5kcy53aWR0aCAvIDIsXG4gICAgICAgICAgICB5OiBib3VuZHMueSArIGJvdW5kcy5oZWlnaHQgLyAyXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGNoaWxkcmVuIGFuZCBpbnN0YW50aWF0ZXMgZXZlcnl0aGluZy5cbiAgICAgKlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHtQSVhJLkRpc3BsYXlPYmplY3R9IGRpc3BsYXlPYmplY3QgLSBUaGUgRGlzcGxheU9iamVjdCBmcm9tIHdoaWNoIHRvIGdlbmVyYXRlIHRoZSB0ZXh0dXJlLlxuICAgICAqIEByZXR1cm4ge1BJWEkuVGV4dHVyZX0gVGhlIGdlbmVyYXRlZCBQSVhJLlRleHR1cmUuXG4gICAgICovXG4gICAgZ2VuZXJhdGVUZXh0dXJlKGRpc3BsYXlPYmplY3QpIHtcblxuICAgICAgICAvLyByZW5kZXJUZXh0dXJlXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgY29uc3QgcmVuZGVyVGV4dHVyZSA9IFBJWEkuUmVuZGVyVGV4dHVyZS5jcmVhdGUoZGlzcGxheU9iamVjdC53aWR0aCwgZGlzcGxheU9iamVjdC5oZWlnaHQpXG5cbiAgICAgICAgLy8gc2F2ZSBwb3NpdGlvblxuICAgICAgICBjb25zdCB0cmFuc2Zvcm0gPSBbZGlzcGxheU9iamVjdC54LCBkaXNwbGF5T2JqZWN0LnksIGRpc3BsYXlPYmplY3Quc2NhbGUueCwgZGlzcGxheU9iamVjdC5zY2FsZS55LCBkaXNwbGF5T2JqZWN0LnJvdGF0aW9uLCBkaXNwbGF5T2JqZWN0LnNrZXcueCwgZGlzcGxheU9iamVjdC5za2V3LnksIGRpc3BsYXlPYmplY3QucGl2b3QueCwgZGlzcGxheU9iamVjdC5waXZvdC55XVxuXG4gICAgICAgIGRpc3BsYXlPYmplY3QucG9zaXRpb24uc2V0KDAsIDApXG4gICAgICAgIGRpc3BsYXlPYmplY3Quc2tldy5zZXQoMCwgMClcbiAgICAgICAgZGlzcGxheU9iamVjdC5yb3RhdGlvbiA9IDBcblxuICAgICAgICAvLyByZW5kZXJcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLm9wdHMucmVuZGVyZXIucmVuZGVyKGRpc3BsYXlPYmplY3QsIHJlbmRlclRleHR1cmUpXG4gICAgICAgIFxuICAgICAgICAvLyByZXN0b3JlIHBvc2l0aW9uXG4gICAgICAgIGRpc3BsYXlPYmplY3Quc2V0VHJhbnNmb3JtKC4uLnRyYW5zZm9ybSlcblxuICAgICAgICByZXR1cm4gcmVuZGVyVGV4dHVyZVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbW92ZWQgdGhlIGZpcnN0IERpc3BsYXlPYmplY3QgYW5kIGFkZHMgdGhlIHNlY29uZCBvbmUgYXQgdGhlIGV4YWN0bHkgc2FtZSBwb3NpdGlvbi5cbiAgICAgKlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHtQSVhJLkRpc3BsYXlPYmplY3R9IGZpcnN0IC0gVGhlIG9sZCBEaXNwbGF5T2JqZWN0LlxuICAgICAqIEBwYXJhbSB7UElYSS5EaXNwbGF5T2JqZWN0fSBzZWNvbmQgLSBUaGUgbmV3IERpc3BsYXlPYmplY3QuXG4gICAgICogQHJldHVybiB7RmxpcHBhYmxlfSBBIHJlZmVyZW5jZSB0byB0aGUgZmxpcHBhYmxlIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzd2l0Y2hEaXNwbGF5T2JqZWN0KGZpcnN0LCBzZWNvbmQpIHtcbiAgICAgICAgaWYgKGZpcnN0ICYmIHNlY29uZCAmJiBmaXJzdC5wYXJlbnQpIHtcbiAgICAgICAgICAgIGNvbnN0IHBhcmVudCA9IGZpcnN0LnBhcmVudFxuICAgICAgICAgICAgY29uc3QgaW5kZXggPSBwYXJlbnQuZ2V0Q2hpbGRJbmRleChmaXJzdClcbiAgICAgICAgICAgIHBhcmVudC5hZGRDaGlsZEF0KHNlY29uZCwgaW5kZXgpXG4gICAgICAgICAgICBwYXJlbnQucmVtb3ZlQ2hpbGQoZmlyc3QpXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbn1cbiIsIi8qKlxuICogXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFBvcG92ZXIgZXh0ZW5kcyBQSVhJLkdyYXBoaWNzIHtcblxuICAgIGNvbnN0cnVjdG9yKHt0aXRsZSA9IG51bGwsIHRleHQgPSBudWxsLCB4ID0gMCwgeSA9IDAsIHBsYWNlbWVudCA9ICd0b3AnLCB3aWR0aCA9IDI1MCwgdGl0bGVTdHlsZSA9IHt9LCB0ZXh0U3R5bGUgPSB7Zm9udFNpemU6ICcxLjZlbSd9fSA9IHt9KSB7XG4gICAgICAgIHN1cGVyKClcblxuICAgICAgICB0aGlzLm9wdHMgPSB7dGl0bGUsIHRleHQsIHgsIHksIHBsYWNlbWVudCwgd2lkdGgsIHRpdGxlU3R5bGUsIHRleHRTdHlsZX1cbiAgICAgICAgXG4gICAgICAgIHRoaXMucGFkZGluZyA9IDEyXG5cbiAgICAgICAgbGV0IHN0eWxlID0ge1xuICAgICAgICAgICAgZm9udEZhbWlseTogJ0FyaWFsJyxcbiAgICAgICAgICAgIGZvbnRTaXplOiAnMmVtJyxcbiAgICAgICAgICAgIHN0cm9rZTogJyNmNmY2ZjYnLFxuICAgICAgICAgICAgc3Ryb2tlVGhpY2tuZXNzOiAzLFxuICAgICAgICAgICAgd29yZFdyYXA6IHRydWUsXG4gICAgICAgICAgICB3b3JkV3JhcFdpZHRoOiB3aWR0aCAtICh0aGlzLnBhZGRpbmcgKiAyKVxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy50aXRsZVRleHRTdHlsZSA9IG5ldyBQSVhJLlRleHRTdHlsZShPYmplY3QuYXNzaWduKHt9LCBzdHlsZSwgdGl0bGVTdHlsZSkpXG4gICAgICAgIHRoaXMudGV4dFRleHRTdHlsZSA9IG5ldyBQSVhJLlRleHRTdHlsZShPYmplY3QuYXNzaWduKHt9LCBzdHlsZSwgdGV4dFN0eWxlKSlcbiAgICAgICAgXG4gICAgICAgIGlmICh0aXRsZSB8fCB0ZXh0KSB7XG4gICAgICAgICAgICB0aGlzLnNldHVwKClcbiAgICAgICAgICAgIHRoaXMuZHJhdygpXG4gICAgICAgICAgICB0aGlzLnBvc2l0aW9uaW5nKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHNldHVwKCkge1xuICAgICAgICB0aGlzLnJlbW92ZUNoaWxkcmVuKClcblxuICAgICAgICBpZiAodGhpcy5vcHRzLnRpdGxlKSB7XG4gICAgICAgICAgICB0aGlzLnRpdGxlVGV4dCA9IG5ldyBQSVhJLlRleHQodGhpcy5vcHRzLnRpdGxlLCB0aGlzLnRpdGxlVGV4dFN0eWxlKVxuICAgICAgICAgICAgdGhpcy50aXRsZVRleHQucG9zaXRpb24uc2V0KHRoaXMucGFkZGluZywgdGhpcy5wYWRkaW5nKVxuICAgICAgICAgICAgdGhpcy5hZGRDaGlsZCh0aGlzLnRpdGxlVGV4dClcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMudGl0bGVZID0gdGhpcy50aXRsZVRleHQgPyB0aGlzLnRpdGxlVGV4dC55IDogMFxuICAgICAgICB0aGlzLnRpdGxlSGVpZ2h0ID0gdGhpcy50aXRsZVRleHQgPyB0aGlzLnRpdGxlVGV4dC5oZWlnaHQgOiAwXG5cbiAgICAgICAgaWYgKHRoaXMub3B0cy50ZXh0KSB7XG4gICAgICAgICAgICB0aGlzLnRleHRUZXh0ID0gbmV3IFBJWEkuVGV4dCh0aGlzLm9wdHMudGV4dCwgdGhpcy50ZXh0VGV4dFN0eWxlKVxuICAgICAgICAgICAgdGhpcy50ZXh0VGV4dC5wb3NpdGlvbi5zZXQodGhpcy5wYWRkaW5nLCB0aGlzLnRpdGxlWSArIHRoaXMudGl0bGVIZWlnaHQgKyB0aGlzLnBhZGRpbmcpXG4gICAgICAgICAgICB0aGlzLmFkZENoaWxkKHRoaXMudGV4dFRleHQpXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnRleHRZID0gdGhpcy50ZXh0VGV4dCA/IHRoaXMudGV4dFRleHQueSA6IDBcbiAgICAgICAgdGhpcy50ZXh0SGVpZ2h0ID0gdGhpcy50ZXh0VGV4dCA/IHRoaXMudGV4dFRleHQuaGVpZ2h0IDogMFxuICAgIH1cblxuICAgIGNsb3NlKCkge1xuICAgICAgICB0aGlzLnBhcmVudC5yZW1vdmVDaGlsZCh0aGlzKVxuICAgIH1cblxuICAgIGRyYXcoKSB7XG4gICAgICAgIHRoaXMuY2xlYXIoKVxuICAgICAgICB0aGlzLmJlZ2luRmlsbCgweGZmZmZmZiwgMSlcbiAgICAgICAgdGhpcy5saW5lU3R5bGUoMSwgMHgyODI4MjgsIC41KVxuXG4gICAgICAgIC8vIERyYXcgcm91bmRlZCByZWN0YW5nbGVcbiAgICAgICAgY29uc3QgaGVpZ2h0ID0gdGhpcy5oZWlnaHQgKyB0aGlzLnBhZGRpbmdcbiAgICAgICAgdGhpcy5kcmF3Um91bmRlZFJlY3QoMCwgMCwgdGhpcy5vcHRzLndpZHRoLCBoZWlnaHQsIDgpXG5cbiAgICAgICAgLy8gRHJhdyBhbmNob3JcbiAgICAgICAgdGhpcy5kcmF3QW5jaG9yKHRoaXMub3B0cy5wbGFjZW1lbnQpXG5cbiAgICAgICAgLy8gRHJhdyB0aXRsZSBiYWNrZ3JvdW5kXG4gICAgICAgIGlmICh0aGlzLm9wdHMudGl0bGUpIHtcbiAgICAgICAgICAgIHRoaXMubGluZVN0eWxlKDApXG4gICAgICAgICAgICB0aGlzLmJlZ2luRmlsbCgweGY3ZjdmNywgMSlcbiAgICAgICAgICAgIGxldCB4ID0gMVxuICAgICAgICAgICAgbGV0IHkgPSB0aGlzLnRpdGxlVGV4dC54ICsgdGhpcy50aXRsZVRleHQuaGVpZ2h0ICsgKHRoaXMucGFkZGluZyAvIDIpXG4gICAgICAgICAgICB0aGlzLm1vdmVUbyh4LCB5KVxuICAgICAgICAgICAgeSA9IDlcbiAgICAgICAgICAgIHRoaXMubGluZVRvKHgsIHkpXG4gICAgICAgICAgICB0aGlzLnF1YWRyYXRpY0N1cnZlVG8oeCwgeSAtIDgsIHggKyA4LCB5IC0gOClcbiAgICAgICAgICAgIHggKz0gdGhpcy5vcHRzLndpZHRoIC0gN1xuICAgICAgICAgICAgeSAtPSA4XG4gICAgICAgICAgICB0aGlzLmxpbmVUbyh4LCB5KVxuICAgICAgICAgICAgdGhpcy5xdWFkcmF0aWNDdXJ2ZVRvKHggKyA1LCB5LCB4ICsgNSwgeSArIDgpXG4gICAgICAgICAgICB4ICs9IDVcbiAgICAgICAgICAgIHkgKz0gdGhpcy50aXRsZVRleHQueCArIHRoaXMudGl0bGVUZXh0LmhlaWdodCArICh0aGlzLnBhZGRpbmcgLyAyKVxuICAgICAgICAgICAgdGhpcy5saW5lVG8oeCwgeSlcbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMudGV4dCkge1xuICAgICAgICAgICAgICAgIHggPSAxXG4gICAgICAgICAgICAgICAgdGhpcy5saW5lVG8oeCwgeSlcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5xdWFkcmF0aWNDdXJ2ZVRvKHgsIHksIHggLSA1LCB5ICsgNClcbiAgICAgICAgICAgICAgICB4ID0gNlxuICAgICAgICAgICAgICAgIHkgKz0gNFxuICAgICAgICAgICAgICAgIHRoaXMubGluZVRvKHgsIHkpXG4gICAgICAgICAgICAgICAgdGhpcy5xdWFkcmF0aWNDdXJ2ZVRvKHgsIHksIHggLSA1LCB5IC0gNClcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZW5kRmlsbCgpXG4gICAgfVxuXG4gICAgZHJhd0FuY2hvcihwbGFjZW1lbnQpIHtcblxuICAgICAgICBsZXQgeCA9IDBcbiAgICAgICAgbGV0IHkgPSAwXG5cbiAgICAgICAgc3dpdGNoIChwbGFjZW1lbnQpIHtcbiAgICAgICAgICAgIGNhc2UgJ2JvdHRvbSc6XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMub3B0cy50aXRsZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmJlZ2luRmlsbCgweGY3ZjdmNywgMSlcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgeCA9ICh0aGlzLndpZHRoIC8gMikgLSAxMFxuICAgICAgICAgICAgICAgIHkgPSAxXG4gICAgICAgICAgICAgICAgdGhpcy5tb3ZlVG8oeCwgeSlcbiAgICAgICAgICAgICAgICB4ICs9IDEwXG4gICAgICAgICAgICAgICAgeSAtPSAxMFxuICAgICAgICAgICAgICAgIHRoaXMubGluZVRvKHgsIHkpXG4gICAgICAgICAgICAgICAgeCArPSAxMFxuICAgICAgICAgICAgICAgIHkgKz0gMTBcbiAgICAgICAgICAgICAgICB0aGlzLmxpbmVUbyh4LCB5KVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdyaWdodCc6XG4gICAgICAgICAgICAgICAgeCA9IDFcbiAgICAgICAgICAgICAgICB5ID0gKHRoaXMuaGVpZ2h0IC8gMikgLSAxMFxuICAgICAgICAgICAgICAgIGlmICh0aGlzLnRpdGxlWSArIHRoaXMudGl0bGVIZWlnaHQgPiB5KSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuYmVnaW5GaWxsKDB4ZjdmN2Y3LCAxKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aGlzLm1vdmVUbyh4LCB5KVxuICAgICAgICAgICAgICAgIHggLT0gMTBcbiAgICAgICAgICAgICAgICB5ICs9IDEwXG4gICAgICAgICAgICAgICAgdGhpcy5saW5lVG8oeCwgeSlcbiAgICAgICAgICAgICAgICB4ICs9IDEwXG4gICAgICAgICAgICAgICAgeSArPSAxMFxuICAgICAgICAgICAgICAgIHRoaXMubGluZVRvKHgsIHkpXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGNhc2UgJ2xlZnQnOlxuICAgICAgICAgICAgICAgIHggPSB0aGlzLndpZHRoIC0gMlxuICAgICAgICAgICAgICAgIHkgPSAodGhpcy5oZWlnaHQgLyAyKSAtIDEwXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMudGl0bGVZICsgdGhpcy50aXRsZUhlaWdodCA+IHkpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5iZWdpbkZpbGwoMHhmN2Y3ZjcsIDEpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMubW92ZVRvKHgsIHkpXG4gICAgICAgICAgICAgICAgeCArPSAxMFxuICAgICAgICAgICAgICAgIHkgKz0gMTBcbiAgICAgICAgICAgICAgICB0aGlzLmxpbmVUbyh4LCB5KVxuICAgICAgICAgICAgICAgIHggLT0gMTBcbiAgICAgICAgICAgICAgICB5ICs9IDEwXG4gICAgICAgICAgICAgICAgdGhpcy5saW5lVG8oeCwgeSlcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICB4ID0gKHRoaXMud2lkdGggLyAyKSAtIDEwXG4gICAgICAgICAgICAgICAgeSA9IHRoaXMuaGVpZ2h0IC0gMlxuICAgICAgICAgICAgICAgIHRoaXMubW92ZVRvKHgsIHkpXG4gICAgICAgICAgICAgICAgeCArPSAxMFxuICAgICAgICAgICAgICAgIHkgKz0gMTBcbiAgICAgICAgICAgICAgICB0aGlzLmxpbmVUbyh4LCB5KVxuICAgICAgICAgICAgICAgIHggKz0gMTBcbiAgICAgICAgICAgICAgICB5IC09IDEwXG4gICAgICAgICAgICAgICAgdGhpcy5saW5lVG8oeCwgeSlcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcG9zaXRpb25pbmcoKSB7XG5cbiAgICAgICAgY29uc3QgeCA9IHRoaXMub3B0cy54XG4gICAgICAgIGNvbnN0IHkgPSB0aGlzLm9wdHMueVxuXG4gICAgICAgIHN3aXRjaCAodGhpcy5vcHRzLnBsYWNlbWVudCkge1xuICAgICAgICAgICAgY2FzZSAnYm90dG9tJzpcbiAgICAgICAgICAgICAgICB0aGlzLnBvc2l0aW9uLnNldCh4IC0gKHRoaXMud2lkdGggLyAyKSwgeSArIDEwKVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdyaWdodCc6XG4gICAgICAgICAgICAgICAgdGhpcy5wb3NpdGlvbi5zZXQoeCwgeSAtICh0aGlzLmhlaWdodCAvIDIpKVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdsZWZ0JzpcbiAgICAgICAgICAgICAgICB0aGlzLnBvc2l0aW9uLnNldCh4IC0gdGhpcy53aWR0aCwgeSAtICh0aGlzLmhlaWdodCAvIDIpKVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHRoaXMucG9zaXRpb24uc2V0KHggLSAodGhpcy53aWR0aCAvIDIpLCB5IC0gdGhpcy5oZWlnaHQpXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgIH1cbn1cbiIsImltcG9ydCBFdmVudHMgZnJvbSAnLi4vZXZlbnRzLmpzJ1xuaW1wb3J0IHsgQWJzdHJhY3RTY2F0dGVyIH0gZnJvbSAnLi4vc2NhdHRlci5qcydcbmltcG9ydCB7IEFuZ2xlLCBQb2ludHMsIFBvbHlnb24gfSBmcm9tICcuLi91dGlscy5qcydcbmltcG9ydCB7IEludGVyYWN0aW9uTWFwcGVyIH0gZnJvbSAnLi4vaW50ZXJhY3Rpb24uanMnXG5cbi8qKiBBIGNvbnRhaW5lciBmb3Igc2NhdHRlciBvYmplY3RzLCB3aGljaCB1c2VzIGEgc2luZ2xlIEludGVyYWN0aW9uTWFwcGVyXG4gKiBmb3IgYWxsIGNoaWxkcmVuLiBUaGlzIHJlZHVjZXMgdGhlIG51bWJlciBvZiByZWdpc3RlcmVkIGV2ZW50IGhhbmRsZXJzXG4gKiBhbmQgY292ZXJzIHRoZSBjb21tb24gdXNlIGNhc2UgdGhhdCBtdWx0aXBsZSBvYmplY3RzIGFyZSBzY2F0dGVyZWRcbiAqIG9uIHRoZSBzYW1lIGxldmVsLlxuICovXG5leHBvcnQgY2xhc3MgU2NhdHRlckNvbnRhaW5lciBleHRlbmRzIFBJWEkuR3JhcGhpY3Mge1xuXG4gICAgLyoqXG4gICAgKiBAY29uc3RydWN0b3JcbiAgICAqIEBwYXJhbSB7UElYSS5SZW5kZXJlcn0gcmVuZGVyZXIgLSBQSVhJIHJlbmRlcmVyLCBuZWVkZWQgZm9yIGhpdCB0ZXN0aW5nXG4gICAgKiBAcGFyYW0ge0Jvb2x9IHN0b3BFdmVudHMgLSBXaGV0aGVyIGV2ZW50cyBzaG91bGQgYmUgc3RvcHBlZCBvciBwcm9wYWdhdGVkXG4gICAgKiBAcGFyYW0ge0Jvb2x9IGNsYWltRXZlbnRzIC0gV2hldGhlciBldmVudHMgc2hvdWxkIGJlIG1hcmtlZCBhcyBjbGFpbWVkXG4gICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgZmluZFRhcmdldCByZXR1cm4gYXMgbm9uLW51bGwgdmFsdWUuXG4gICAgKiBAcGFyYW0ge1BJWEkuQ29udGFpbmVyfSBjb250YWluZXIgLSBBIGNvbnRhaW5lciBmb3IgdGhlIHNjYXR0ZXJcbiAgICAqIEBwYXJhbSB7Qm9vbH0gc2hvd0JvdW5kcyAtIFNob3cgYm91bmRzIGZvciBkZWJ1Z2dpbmcgcHVycG9zZXMuXG4gICAgKiBAcGFyYW0ge0Jvb2x9IHNob3dUb3VjaGVzIC0gU2hvdyB0b3VjaGVzIGFuZCBwb2ludGVyIGZvciBkZWJ1Z2dpbmcgcHVycG9zZXMuXG4gICAgKiBAcGFyYW0ge0NvbG9yfSBiYWNrZ3JvdW5kQ29sb3IgLSBTZXQgYmFja2dyb3VuZCBjb2xvciBpZiBzcGVjaWZpZWQuXG4gICAgKiBAcGFyYW0ge1BJWElBcHB9IGFwcCAtIE5lZWRlZCBpZiBzaG93Qm91bmRzIGlzIHRydWUgdG8gcmVnaXN0ZXJcbiAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cGRhdGUgaGFuZGxlci5cbiAgICAqL1xuICAgIGNvbnN0cnVjdG9yKHJlbmRlcmVyLCB7XG4gICAgICAgIHN0b3BFdmVudHMgPSB0cnVlLFxuICAgICAgICBjbGFpbUV2ZW50cyA9IHRydWUsXG4gICAgICAgIGNvbnRhaW5lciA9IG51bGwsXG4gICAgICAgIHNob3dCb3VuZHMgPSBmYWxzZSxcbiAgICAgICAgc2hvd1BvbHlnb24gPSBmYWxzZSxcbiAgICAgICAgc2hvd1RvdWNoZXMgPSBmYWxzZSxcbiAgICAgICAgYmFja2dyb3VuZENvbG9yID0gbnVsbCxcbiAgICAgICAgYXBwID0gd2luZG93LmFwcFxuICAgIH0gPSB7fSkge1xuICAgICAgICBzdXBlcigpXG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gY29udGFpbmVyXG4gICAgICAgIGlmICh0aGlzLmNvbnRhaW5lcilcbiAgICAgICAgICAgIHRoaXMuY29udGFpbmVyRGltZW5zaW9ucyA9IHtcbiAgICAgICAgICAgICAgICB4OiB0aGlzLmNvbnRhaW5lci53aWR0aCxcbiAgICAgICAgICAgICAgICB5OiB0aGlzLmNvbnRhaW5lci5oZWlnaHRcbiAgICAgICAgICAgIH1cbiAgICAgICAgdGhpcy5iYWNrZ3JvdW5kV2lkdGggPSBudWxsXG4gICAgICAgIHRoaXMuYmFja2dyb3VuZEhlaWdodCA9IG51bGxcbiAgICAgICAgdGhpcy5hcHAgPSBhcHBcbiAgICAgICAgdGhpcy5yZW5kZXJlciA9IHJlbmRlcmVyXG4gICAgICAgIHRoaXMuc3RvcEV2ZW50cyA9IHN0b3BFdmVudHNcbiAgICAgICAgdGhpcy5jbGFpbUV2ZW50cyA9IGNsYWltRXZlbnRzXG4gICAgICAgIHRoaXMuZGVsZWdhdGUgPSBuZXcgSW50ZXJhY3Rpb25NYXBwZXIodGhpcy5ldmVudFJlY2VpdmVyLCB0aGlzKVxuICAgICAgICB0aGlzLnNob3dCb3VuZHMgPSBzaG93Qm91bmRzXG4gICAgICAgIHRoaXMuc2hvd1RvdWNoZXMgPSBzaG93VG91Y2hlc1xuICAgICAgICB0aGlzLnNob3dQb2x5Z29uID0gc2hvd1BvbHlnb25cbiAgICAgICAgdGhpcy5iYWNrZ3JvdW5kQ29sb3IgPSBiYWNrZ3JvdW5kQ29sb3JcbiAgICAgICAgaWYgKHNob3dCb3VuZHMgfHwgc2hvd1RvdWNoZXMgfHwgc2hvd1BvbHlnb24pIHtcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCJTaG93IFRPVUNIRVMhISFcIilcbiAgICAgICAgICAgIHRoaXMuYXBwLnRpY2tlci5hZGQoKGRlbHRhKSA9PiB0aGlzLnVwZGF0ZShkZWx0YSksIHRoaXMpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGJhY2tncm91bmRDb2xvcikge1xuICAgICAgICAgICAgdGhpcy51cGRhdGVCYWNrZ3JvdW5kKClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHVwZGF0ZUJhY2tncm91bmQoKSB7XG4gICAgICAgIHRoaXMuY2xlYXIoKVxuICAgICAgICBsZXQgcmVjdCA9IHRoaXMuYm91bmRzXG4gICAgICAgIHRoaXMuYmVnaW5GaWxsKHRoaXMuYmFja2dyb3VuZENvbG9yLCAxKVxuICAgICAgICB0aGlzLmRyYXdSZWN0KDAsIDAsIHJlY3Qud2lkdGgsIHJlY3QuaGVpZ2h0KVxuICAgICAgICB0aGlzLmVuZEZpbGwoKVxuICAgIH1cblxuICAgIGdldCBldmVudFJlY2VpdmVyKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5yZW5kZXJlci5wbHVnaW5zLmludGVyYWN0aW9uLmludGVyYWN0aW9uRE9NRWxlbWVudFxuICAgIH1cblxuICAgIGdldCBib3VuZHMoKSB7XG4gICAgICAgIGxldCB4ID0gMFxuICAgICAgICBsZXQgeSA9IDBcbiAgICAgICAgLy8gQGNvbnRhaW5lcjogV2UgbmVlZCB0byBjYWxsIHRoZSBjb25zdGFudCB2YWx1ZXMsIGFzIHRoZSBjb250YWluZXJcbiAgICAgICAgLy8gZ2V0cyByZXNpemVkLCB3aGVuIGEgY2hpbGQgbW92ZXMgb3V0c2lkZSB0aGUgb3JpZ2luYWwgYm91bmRhcmllcy5cbiAgICAgICAgbGV0IHcgPSAodGhpcy5jb250YWluZXIpID8gdGhpcy5jb250YWluZXJEaW1lbnNpb25zLnggOiAodGhpcy5iYWNrZ3JvdW5kV2lkdGggfHwgdGhpcy5hcHAud2lkdGgpXG4gICAgICAgIGxldCBoID0gKHRoaXMuY29udGFpbmVyKSA/IHRoaXMuY29udGFpbmVyRGltZW5zaW9ucy55IDogKHRoaXMuYmFja2dyb3VuZEhlaWdodCB8fCB0aGlzLmFwcC5oZWlnaHQpXG5cbiAgICAgICAgaWYgKHRoaXMuYXBwLmZ1bGxzY3JlZW4gJiYgdGhpcy5hcHAubW9ua2V5UGF0Y2hNYXBwaW5nKSB7XG4gICAgICAgICAgICBsZXQgZml4ZWQgPSB0aGlzLm1hcFBvc2l0aW9uVG9Qb2ludCh7IHg6IHcsIHk6IDAgfSlcbiAgICAgICAgICAgIGlmIChmaXhlZC54IDwgdykge1xuICAgICAgICAgICAgICAgIHcgPSBmaXhlZC54XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoZml4ZWQueSA+IDApIHtcbiAgICAgICAgICAgICAgICB5ICs9IGZpeGVkLnlcbiAgICAgICAgICAgICAgICBoIC09IGZpeGVkLnlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbmV3IFBJWEkuUmVjdGFuZ2xlKHgsIHksIHcsIGgpXG4gICAgfVxuXG4gICAgZ2V0IGNlbnRlcigpIHtcbiAgICAgICAgbGV0IHIgPSB0aGlzLmJvdW5kc1xuICAgICAgICByZXR1cm4geyB4OiByLndpZHRoIC8gMiwgeTogci5oZWlnaHQgLyAyIH1cbiAgICB9XG5cbiAgICBnZXQgcG9seWdvbigpIHtcbiAgICAgICAgbGV0IHIgPSB0aGlzLmJvdW5kc1xuICAgICAgICBsZXQgdzIgPSByLndpZHRoIC8gMlxuICAgICAgICBsZXQgaDIgPSByLmhlaWdodCAvIDJcbiAgICAgICAgbGV0IGNlbnRlciA9IHsgeDogdzIsIHk6IGgyIH1cbiAgICAgICAgbGV0IHBvbHlnb24gPSBuZXcgUG9seWdvbihjZW50ZXIpXG4gICAgICAgIHBvbHlnb24uYWRkUG9pbnQoeyB4OiAtdzIsIHk6IC1oMiB9KVxuICAgICAgICBwb2x5Z29uLmFkZFBvaW50KHsgeDogdzIsIHk6IC1oMiB9KVxuICAgICAgICBwb2x5Z29uLmFkZFBvaW50KHsgeDogdzIsIHk6IGgyIH0pXG4gICAgICAgIHBvbHlnb24uYWRkUG9pbnQoeyB4OiAtdzIsIHk6IGgyIH0pXG4gICAgICAgIHJldHVybiBwb2x5Z29uXG4gICAgfVxuXG4gICAgdXBkYXRlKGR0KSB7XG4gICAgICAgIHRoaXMuY2xlYXIoKVxuICAgICAgICB0aGlzLmxpbmVTdHlsZSgxLCAweDAwMDBGRilcbiAgICAgICAgaWYgKHRoaXMuc2hvd0JvdW5kcykge1xuICAgICAgICAgICAgZm9yIChsZXQgY2hpbGQgb2YgdGhpcy5jaGlsZHJlbikge1xuICAgICAgICAgICAgICAgIGlmIChjaGlsZC5zY2F0dGVyKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vbGV0IHt4LCB5LCB3aWR0aCwgaGVpZ2h0fSA9IGNoaWxkLnNjYXR0ZXIudGhyb3dCb3VuZHMoKVxuICAgICAgICAgICAgICAgICAgICAvLyBuZXcgUElYSS5SZWN0YW5nbGUoeCwgeSwgd2lkdGgsIGhlaWdodClcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5kcmF3U2hhcGUoY2hpbGQuc2NhdHRlci5ib3VuZHMpXG4gICAgICAgICAgICAgICAgICAgIGxldCBjZW50ZXIgPSBjaGlsZC5zY2F0dGVyLmNlbnRlclxuICAgICAgICAgICAgICAgICAgICB0aGlzLmRyYXdDaXJjbGUoY2VudGVyLngsIGNlbnRlci55LCA0KVxuICAgICAgICAgICAgICAgICAgICB0aGlzLmRyYXdDaXJjbGUoY2hpbGQuc2NhdHRlci54LCBjaGlsZC5zY2F0dGVyLnksIDQpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5saW5lU3R5bGUoMiwgMHgwMDAwRkYpXG4gICAgICAgICAgICB0aGlzLmRyYXdTaGFwZSh0aGlzLmJvdW5kcylcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5zaG93UG9seWdvbikge1xuICAgICAgICAgICAgdGhpcy5saW5lU3R5bGUoMiwgMHhGRjAwMDApXG4gICAgICAgICAgICBmb3IgKGxldCBjaGlsZCBvZiB0aGlzLmNoaWxkcmVuKSB7XG4gICAgICAgICAgICAgICAgaWYgKGNoaWxkLnNjYXR0ZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IHBvbHlnb24gPSBjaGlsZC5zY2F0dGVyLnBvbHlnb25cbiAgICAgICAgICAgICAgICAgICAgbGV0IHNoYXBlID0gbmV3IFBJWEkuUG9seWdvbihwb2x5Z29uLmZsYXRBYnNvbHV0ZVBvaW50cygpKVxuICAgICAgICAgICAgICAgICAgICBzaGFwZS5jbG9zZSgpXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZHJhd1NoYXBlKHNoYXBlKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5zaG93VG91Y2hlcykge1xuICAgICAgICAgICAgbGV0IGN1cnJlbnQgPSB0aGlzLmRlbGVnYXRlLmludGVyYWN0aW9uLmN1cnJlbnRcbiAgICAgICAgICAgIGZvciAobGV0IFtrZXksIHBvaW50XSBvZiBjdXJyZW50LmVudHJpZXMoKSkge1xuICAgICAgICAgICAgICAgIGxldCBsb2NhbCA9IHRoaXMubWFwUG9zaXRpb25Ub1BvaW50KHBvaW50KVxuICAgICAgICAgICAgICAgIHRoaXMuZHJhd0NpcmNsZShsb2NhbC54LCBsb2NhbC55LCAxMilcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGNhcHR1cmUoZXZlbnQpIHtcbiAgICAgICAgaWYgKHRoaXMuc3RvcEV2ZW50cylcbiAgICAgICAgICAgIEV2ZW50cy5zdG9wKGV2ZW50KVxuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgIH1cblxuICAgIGZha2VJbnRlcmFjdGlvbkV2ZW50KHBvaW50LCBrZXkpIHtcbiAgICAgICAgcmV0dXJuIHsgZGF0YTogeyBnbG9iYWw6IHBvaW50LCBrZXk6IGtleSB9IH1cbiAgICB9XG5cbiAgICBmaW5kSGl0U2NhdHRlcihkYXRhLCBkaXNwbGF5T2JqZWN0LCBoaXQpIHtcbiAgICAgICAgLy8gICAgIGlmIChoaXQpIHtcbiAgICAgICAgLy8gICAgICAgICAgICAgY29uc29sZS5sb2coXCJmaW5kSGl0U2NhdHRlclwiLCBkaXNwbGF5T2JqZWN0KVxuICAgICAgICAvLyAgICAgICAgIH1cbiAgICAgICAgaWYgKGhpdCAmJiB0aGlzLmhpdFNjYXR0ZXIgPT09IG51bGwgJiYgdHlwZW9mIChkaXNwbGF5T2JqZWN0KSAhPSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHRoaXMuaGl0U2NhdHRlciA9IChkaXNwbGF5T2JqZWN0LnNjYXR0ZXIpID8gZGlzcGxheU9iamVjdC5zY2F0dGVyIDogbnVsbFxuICAgICAgICB9XG4gICAgfVxuXG4gICAgbWFwUG9zaXRpb25Ub1BvaW50KHBvaW50LCBlbGVtZW50ID0gbnVsbCkge1xuICAgICAgICAvLyBJbiBjYXNlIG9mIG5lc3RlZCBzY2F0dGVycyB3ZSBnZXQgYW4gYWRkaXRpb25hbCBwYXJhbWV0ZXIgdGhhdFxuICAgICAgICAvLyBjb250YWlucyB0aGUgZm91bmQgc2NhdHRlclxuICAgICAgICBsZXQgbG9jYWwgPSBuZXcgUElYSS5Qb2ludCgpXG4gICAgICAgIGxldCBpbnRlcmFjdGlvbk1hbmFnZXIgPSB0aGlzLnJlbmRlcmVyLnBsdWdpbnMuaW50ZXJhY3Rpb25cbiAgICAgICAgaW50ZXJhY3Rpb25NYW5hZ2VyLm1hcFBvc2l0aW9uVG9Qb2ludChsb2NhbCwgcG9pbnQueCwgcG9pbnQueSlcbiAgICAgICAgaWYgKGVsZW1lbnQgaW5zdGFuY2VvZiBEaXNwbGF5T2JqZWN0U2NhdHRlciAmJiBlbGVtZW50LmRpc3BsYXlPYmplY3QucGFyZW50ICE9IG51bGwpIHtcbiAgICAgICAgICAgIHJldHVybiBlbGVtZW50LmRpc3BsYXlPYmplY3QucGFyZW50LnRvTG9jYWwobG9jYWwpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGxvY2FsXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTmV3IG1ldGhvZCBoaXRUZXN0IGltcGxlbWVudGVkIChpbiBJbnRlcmFjdGlvbk1hbmFnZXIsIHNpbmNlIDQuNS4wKS5cbiAgICAgKiBTZWUgaHR0cHM6Ly9naXRodWIuY29tL3BpeGlqcy9waXhpLmpzL3B1bGwvMzg3OFxuICAgICAqL1xuICAgIGZpbmRUYXJnZXQoZXZlbnQsIGxvY2FsLCBnbG9iYWwpIHtcbiAgICAgICAgaWYgKGV2ZW50LmNsYWltZWRCeVNjYXR0ZXIpIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5oaXRTY2F0dGVyID0gbnVsbFxuICAgICAgICBsZXQgaW50ZXJhY3Rpb25NYW5hZ2VyID0gdGhpcy5yZW5kZXJlci5wbHVnaW5zLmludGVyYWN0aW9uXG4gICAgICAgIGxldCBmYWtlRXZlbnQgPSB0aGlzLmZha2VJbnRlcmFjdGlvbkV2ZW50KGxvY2FsKVxuICAgICAgICBpbnRlcmFjdGlvbk1hbmFnZXIucHJvY2Vzc0ludGVyYWN0aXZlKGZha2VFdmVudCxcbiAgICAgICAgICAgIHRoaXMsXG4gICAgICAgICAgICB0aGlzLmZpbmRIaXRTY2F0dGVyLmJpbmQodGhpcyksIHRydWUpXG4gICAgICAgIGlmICh0aGlzLmNsYWltRXZlbnRzKVxuICAgICAgICAgICAgZXZlbnQuY2xhaW1lZEJ5U2NhdHRlciA9IHRoaXMuaGl0U2NhdHRlclxuICAgICAgICByZXR1cm4gdGhpcy5oaXRTY2F0dGVyXG4gICAgfVxuXG4gICAgZmluZFRhcmdldE5ldyhldmVudCwgbG9jYWwsIGdsb2JhbCkge1xuICAgICAgICAvLyBVTzogc3RpbGwgcHJvYmxlbWF0aWMuIERvZXMgbm90IGZpbmQgbm9uIGludGVyYWN0aXZlIGVsZW1lbnRzXG4gICAgICAgIC8vIHdoaWNoIGFyZSBuZWVkZWQgZm9yIHNvbWUgc3R5bHVzIGFwcGxpY2F0aW9uc1xuICAgICAgICBpZiAoZXZlbnQuY2xhaW1lZEJ5U2NhdHRlcikge1xuICAgICAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmhpdFNjYXR0ZXIgPSBudWxsXG4gICAgICAgIGxldCBpbnRlcmFjdGlvbk1hbmFnZXIgPSB0aGlzLnJlbmRlcmVyLnBsdWdpbnMuaW50ZXJhY3Rpb25cbiAgICAgICAgbGV0IGRpc3BsYXlPYmplY3QgPSBpbnRlcmFjdGlvbk1hbmFnZXIuaGl0VGVzdChsb2NhbCwgdGhpcylcbiAgICAgICAgaWYgKGRpc3BsYXlPYmplY3QgIT0gbnVsbCAmJiBkaXNwbGF5T2JqZWN0LnNjYXR0ZXIgIT0gbnVsbClcbiAgICAgICAgICAgIHRoaXMuaGl0U2NhdHRlciA9IGRpc3BsYXlPYmplY3Quc2NhdHRlclxuICAgICAgICBpZiAodGhpcy5jbGFpbUV2ZW50cylcbiAgICAgICAgICAgIGV2ZW50LmNsYWltZWRCeVNjYXR0ZXIgPSB0aGlzLmhpdFNjYXR0ZXJcbiAgICAgICAgcmV0dXJuIHRoaXMuaGl0U2NhdHRlclxuICAgIH1cblxuXG4gICAgb25TdGFydChldmVudCwgaW50ZXJhY3Rpb24pIHtcblxuICAgIH1cblxuICAgIG9uTW92ZShldmVudCwgaW50ZXJhY3Rpb24pIHtcblxuICAgIH1cblxuICAgIG9uRW5kKGV2ZW50LCBpbnRlcmFjdGlvbikge1xuICAgICAgICBmb3IgKGxldCBrZXkgb2YgaW50ZXJhY3Rpb24uZW5kZWQua2V5cygpKSB7XG4gICAgICAgICAgICBsZXQgcG9pbnQgPSBpbnRlcmFjdGlvbi5lbmRlZC5nZXQoa2V5KVxuICAgICAgICAgICAgaWYgKGludGVyYWN0aW9uLmlzTG9uZ1ByZXNzKGtleSkpIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9uTG9uZ1ByZXNzKGtleSwgcG9pbnQpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoaW50ZXJhY3Rpb24uaXNUYXAoa2V5KSkge1xuICAgICAgICAgICAgICAgIHRoaXMub25UYXAoa2V5LCBwb2ludClcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uVGFwKGtleSwgcG9pbnQpIHtcbiAgICAgICAgY29uc29sZS5pbmZvKCdTY2F0dGVyQ29udGFpbmVyLm9uVGFwJylcbiAgICB9XG5cbiAgICBvbkxvbmdQcmVzcyhrZXksIHBvaW50KSB7XG4gICAgICAgIGNvbnNvbGUuaW5mbygnU2NhdHRlckNvbnRhaW5lci5vbkxvbmdQcmVzcycpXG4gICAgfVxuXG4gICAgYnJpbmdUb0Zyb250KGRpc3BsYXlPYmplY3QpIHtcbiAgICAgICAgdGhpcy5hZGRDaGlsZChkaXNwbGF5T2JqZWN0KVxuICAgIH1cblxuICAgIGxheW91dCh3aWR0aCwgaGVpZ2h0KSB7XG4gICAgICAgIHRoaXMuYmFja2dyb3VuZFdpZHRoID0gd2lkdGhcbiAgICAgICAgdGhpcy5iYWNrZ3JvdW5kSGVpZ2h0ID0gaGVpZ2h0XG4gICAgICAgIGlmICh0aGlzLmJhY2tncm91bmRDb2xvcikge1xuICAgICAgICAgICAgdGhpcy51cGRhdGVCYWNrZ3JvdW5kKClcbiAgICAgICAgfVxuICAgICAgICBcbiAgICB9XG59XG5cbi8qKiBBIHdyYXBwZXIgZm9yIGNoaWxkIGVsZW1lbnRzIG9mIGEgU2NhdHRlckNvbnRhaW5lci4gQ2FuIGJlIHVzZWRcbiAqICB0byBjb21iaW5lIHNjYXR0ZXJlZCBvYmplY3RzIHdpdGggbm9uLXNjYXR0ZXJlZCBvYmplY3RzLiBBbnlcbiAqICBQSVhJLkRpc3BsYXlPYmplY3QgY2FuIGJlIHdyYXBwZWQuXG4gKi9cbmV4cG9ydCBjbGFzcyBEaXNwbGF5T2JqZWN0U2NhdHRlciBleHRlbmRzIEFic3RyYWN0U2NhdHRlciB7XG5cbiAgICBjb25zdHJ1Y3RvcihkaXNwbGF5T2JqZWN0LCByZW5kZXJlcixcbiAgICAgICAgeyB4ID0gbnVsbCwgeSA9IG51bGwsXG4gICAgICAgICAgICBtaW5TY2FsZSA9IDAuMSxcbiAgICAgICAgICAgIG1heFNjYWxlID0gMS4wLFxuICAgICAgICAgICAgc3RhcnRTY2FsZSA9IDEuMCxcbiAgICAgICAgICAgIGF1dG9CcmluZ1RvRnJvbnQgPSB0cnVlLFxuICAgICAgICAgICAgdHJhbnNsYXRhYmxlID0gdHJ1ZSwgc2NhbGFibGUgPSB0cnVlLCByb3RhdGFibGUgPSB0cnVlLCByZXNpemFibGUgPSBmYWxzZSxcbiAgICAgICAgICAgIG1vdmFibGVYID0gdHJ1ZSxcbiAgICAgICAgICAgIG1vdmFibGVZID0gdHJ1ZSxcbiAgICAgICAgICAgIHRocm93VmlzaWJpbGl0eSA9IDQ0LFxuICAgICAgICAgICAgdGhyb3dEYW1waW5nID0gMC45NSxcbiAgICAgICAgICAgIGF1dG9UaHJvdyA9IHRydWUsXG4gICAgICAgICAgICByb3RhdGlvbkRlZ3JlZXMgPSBudWxsLFxuICAgICAgICAgICAgcm90YXRpb24gPSBudWxsLFxuICAgICAgICAgICAgb3ZlcmRvU2NhbGluZyA9IDEuNSxcbiAgICAgICAgICAgIG9uVHJhbnNmb3JtID0gbnVsbCxcbiAgICAgICAgICAgIG9uVGhyb3dGaW5pc2hlZCA9IG51bGwgfSA9IHt9KSB7XG4gICAgICAgIC8vIEZvciB0aGUgc2ltdWxhdGlvbiBvZiBuYW1lZCBwYXJhbWV0ZXJzLFxuICAgICAgICAvLyBzZWU6IGh0dHA6Ly9leHBsb3Jpbmdqcy5jb20vZXM2L2NoX3BhcmFtZXRlci1oYW5kbGluZy5odG1sXG4gICAgICAgIHN1cGVyKHtcbiAgICAgICAgICAgIG92ZXJkb1NjYWxpbmcsXG4gICAgICAgICAgICBtaW5TY2FsZSwgbWF4U2NhbGUsXG4gICAgICAgICAgICBzdGFydFNjYWxlLFxuICAgICAgICAgICAgYXV0b0JyaW5nVG9Gcm9udCxcbiAgICAgICAgICAgIHRyYW5zbGF0YWJsZSwgc2NhbGFibGUsIHJvdGF0YWJsZSwgcmVzaXphYmxlLFxuICAgICAgICAgICAgbW92YWJsZVgsIG1vdmFibGVZLCB0aHJvd1Zpc2liaWxpdHksIHRocm93RGFtcGluZyxcbiAgICAgICAgICAgIGF1dG9UaHJvdyxcbiAgICAgICAgICAgIG9uVGhyb3dGaW5pc2hlZCxcbiAgICAgICAgICAgIHJvdGF0aW9uRGVncmVlcywgcm90YXRpb24sXG4gICAgICAgICAgICBvblRyYW5zZm9ybVxuICAgICAgICB9KVxuICAgICAgICB0aGlzLmRpc3BsYXlPYmplY3QgPSBkaXNwbGF5T2JqZWN0XG4gICAgICAgIHRoaXMuZGlzcGxheU9iamVjdC5zY2F0dGVyID0gdGhpc1xuICAgICAgICB0aGlzLnJlbmRlcmVyID0gcmVuZGVyZXJcbiAgICAgICAgdGhpcy5zY2FsZSA9IHN0YXJ0U2NhbGVcbiAgICAgICAgdGhpcy5yb3RhdGlvbkRlZ3JlZXMgPSB0aGlzLnN0YXJ0Um90YXRpb25EZWdyZWVzXG5cbiAgICAgICAgLy8gT25seSBzZXQgeCBhbmQgeSBpZiB0aGV5IGFyZSBzcGVjaWZpZWQuXG4gICAgICAgIC8vIE90aGVyd2lzZSB0aGUgZGlzcGxheW9iamVjdCBnZXRzIGNvcnJ1cHRlZC5cbiAgICAgICAgaWYgKHggIT0gbnVsbCkgdGhpcy54ID0geFxuICAgICAgICBpZiAoeSAhPSBudWxsKSB0aGlzLnkgPSB5XG4gICAgfVxuXG4gICAgZ2V0V29ybGRTY2F0dGVyKCkge1xuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIC8qKiBSZXR1cm5zIGdlb21ldHJ5IGRhdGEgYXMgb2JqZWN0LiAqKi9cbiAgICBnZXRTdGF0ZSgpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHNjYWxlOiB0aGlzLnNjYWxlLFxuICAgICAgICAgICAgeDogdGhpcy54LFxuICAgICAgICAgICAgeTogdGhpcy55LFxuICAgICAgICAgICAgcm90YXRpb246IHRoaXMucm90YXRpb25cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHNldHVwKCkge1xuICAgICAgICB0aGlzLnNldHVwTW91c2VXaGVlbEludGVyYWN0aW9uKClcbiAgICB9XG5cbiAgICByb3VuZFBpeGVsKHZhbHVlKSB7XG4gICAgICAgIC8vIFVPOiBTaG91bGQgYmUgb2Jzb2xldGUgYmVjYXVzZSBSZW5kZXJlciBzdXBwb3J0cyByb3VuZFBpeGVscyBieSBkZWZhdWx0XG4gICAgICAgIHJldHVybiB2YWx1ZVxuICAgICAgICBsZXQgcmVzID0gdGhpcy5yZW5kZXJlci5yZXNvbHV0aW9uXG4gICAgICAgIHJldHVybiBNYXRoLnJvdW5kKHZhbHVlICogcmVzKSAvIHJlc1xuICAgIH1cblxuICAgIGdldCBjb250YWluZXIoKSB7XG4gICAgICAgIC8vIHJldHVybiB0aGlzLmRpc3BsYXlPYmplY3QucGFyZW50XG4gICAgICAgIGxldCBvYmogPSB0aGlzLmRpc3BsYXlPYmplY3RcbiAgICAgICAgd2hpbGUgKG9iai5wYXJlbnQgIT0gbnVsbCAmJiAhKG9iai5wYXJlbnQgaW5zdGFuY2VvZiBTY2F0dGVyQ29udGFpbmVyKSlcbiAgICAgICAgICAgIG9iaiA9IG9iai5wYXJlbnRcbiAgICAgICAgcmV0dXJuIG9iai5wYXJlbnRcbiAgICB9XG5cbiAgICBnZXQgeCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucG9zaXRpb24ueFxuICAgIH1cblxuICAgIHNldCB4KHZhbHVlKSB7XG4gICAgICAgIHRoaXMucG9zaXRpb24ueCA9IHZhbHVlXG4gICAgfVxuXG4gICAgZ2V0IHkoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBvc2l0aW9uLnlcbiAgICB9XG5cbiAgICBzZXQgeSh2YWx1ZSkge1xuICAgICAgICB0aGlzLnBvc2l0aW9uLnkgPSB2YWx1ZVxuICAgIH1cblxuICAgIGdldCBwb2x5Z29uKCkge1xuICAgICAgICBsZXQgcG9seWdvbiA9IG5ldyBQb2x5Z29uKHRoaXMuY2VudGVyKVxuICAgICAgICBsZXQgdzIgPSB0aGlzLndpZHRoIC8gMlxuICAgICAgICBsZXQgaDIgPSB0aGlzLmhlaWdodCAvIDJcbiAgICAgICAgcG9seWdvbi5hZGRQb2ludCh7IHg6IC13MiwgeTogLWgyIH0pXG4gICAgICAgIHBvbHlnb24uYWRkUG9pbnQoeyB4OiB3MiwgeTogLWgyIH0pXG4gICAgICAgIHBvbHlnb24uYWRkUG9pbnQoeyB4OiB3MiwgeTogaDIgfSlcbiAgICAgICAgcG9seWdvbi5hZGRQb2ludCh7IHg6IC13MiwgeTogaDIgfSlcbiAgICAgICAgcG9seWdvbi5yb3RhdGUodGhpcy5yb3RhdGlvbilcbiAgICAgICAgcmV0dXJuIHBvbHlnb25cbiAgICB9XG5cbiAgICBnZXQgY29udGFpbmVyQm91bmRzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jb250YWluZXIuYm91bmRzXG4gICAgfVxuXG4gICAgZ2V0IGNvbnRhaW5lclBvbHlnb24oKSB7XG4gICAgICAgIGxldCBjb250YWluZXIgPSB0aGlzLmNvbnRhaW5lclxuICAgICAgICBpZiAoY29udGFpbmVyID09IG51bGwpIHJldHVybiBudWxsXG4gICAgICAgIHJldHVybiBjb250YWluZXIucG9seWdvblxuICAgIH1cblxuICAgIGdldCBwb3NpdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGlzcGxheU9iamVjdC5wb3NpdGlvblxuICAgIH1cblxuICAgIHNldCBwb3NpdGlvbih2YWx1ZSkge1xuICAgICAgICB0aGlzLmRpc3BsYXlPYmplY3QucG9zaXRpb24gPSB2YWx1ZVxuICAgIH1cblxuICAgIGdldCBzY2FsZSgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGlzcGxheU9iamVjdC5zY2FsZS54XG4gICAgfVxuXG4gICAgc2V0IHNjYWxlKHZhbHVlKSB7XG4gICAgICAgIHRoaXMuZGlzcGxheU9iamVjdC5zY2FsZS54ID0gdmFsdWVcbiAgICAgICAgdGhpcy5kaXNwbGF5T2JqZWN0LnNjYWxlLnkgPSB2YWx1ZVxuICAgIH1cblxuICAgIGdldCB3aWR0aCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGlzcGxheU9iamVjdC53aWR0aFxuICAgIH1cblxuICAgIGdldCBoZWlnaHQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRpc3BsYXlPYmplY3QuaGVpZ2h0XG4gICAgfVxuXG4gICAgZ2V0IGJvdW5kcygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGlzcGxheU9iamVjdC5nZXRCb3VuZHMoKVxuICAgIH1cblxuICAgIGdldCBwaXZvdCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGlzcGxheU9iamVjdC5waXZvdFxuICAgIH1cblxuICAgIGdldCByb3RhdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGlzcGxheU9iamVjdC5yb3RhdGlvblxuICAgIH1cblxuICAgIHNldCByb3RhdGlvbih2YWx1ZSkge1xuICAgICAgICB0aGlzLmRpc3BsYXlPYmplY3Qucm90YXRpb24gPSB2YWx1ZVxuICAgIH1cblxuICAgIGdldCByb3RhdGlvbkRlZ3JlZXMoKSB7XG4gICAgICAgIHJldHVybiBBbmdsZS5yYWRpYW4yZGVncmVlKHRoaXMuZGlzcGxheU9iamVjdC5yb3RhdGlvbilcbiAgICB9XG5cbiAgICBzZXQgcm90YXRpb25EZWdyZWVzKHZhbHVlKSB7XG4gICAgICAgIHRoaXMuZGlzcGxheU9iamVjdC5yb3RhdGlvbiA9IEFuZ2xlLmRlZ3JlZTJyYWRpYW4odmFsdWUpXG4gICAgfVxuXG4gICAgZ2V0IGNlbnRlcigpIHtcbiAgICAgICAgbGV0IHcyID0gdGhpcy53aWR0aCAvIDJcbiAgICAgICAgbGV0IGgyID0gdGhpcy5oZWlnaHQgLyAyXG4gICAgICAgIGxldCBkaXN0ID0gTWF0aC5zcXJ0KHcyICogdzIgKyBoMiAqIGgyKVxuICAgICAgICBsZXQgYW5nbGUgPSBQb2ludHMuYW5nbGUoeyB4OiB3MiwgeTogaDIgfSwgeyB4OiAwLCB5OiAwIH0pXG4gICAgICAgIGxldCBwID0gdGhpcy5kaXNwbGF5T2JqZWN0LnhcbiAgICAgICAgbGV0IGMgPSBQb2ludHMuYXJjKHRoaXMucG9zaXRpb24sIHRoaXMucm90YXRpb24gKyBhbmdsZSwgZGlzdClcbiAgICAgICAgcmV0dXJuIGMgLy8gUG9pbnRzLnN1YnRyYWN0KGMsIHRoaXMucGl2b3QpXG4gICAgfVxuXG4gICAgZ2V0IHJvdGF0aW9uT3JpZ2luKCkge1xuICAgICAgICAvLyBJbiBQSVhJIHRoZSBkZWZhdWx0IHJvdGF0aW9uIGFuZCBzY2FsZSBvcmlnaW4gaXMgdGhlIHBvc2l0aW9uXG4gICAgICAgIHJldHVybiB0aGlzLnBvc2l0aW9uIC8vIFBvaW50cy5hZGQodGhpcy5wb3NpdGlvbiwgdGhpcy5waXZvdClcbiAgICB9XG5cbiAgICBtYXBQb3NpdGlvblRvQ29udGFpbmVyUG9pbnQocG9pbnQpIHtcbiAgICAgICAgLy8gVU86IFdlIG5lZWQgdGhlIGNvb3JkaW5hdGVzIHJlbGF0ZWQgdG8gdGhpcyBzY2F0dGVyIGluIGNhc2VcbiAgICAgICAgLy8gb2YgbmVzdGVkIHNjYXR0ZXJzXG4gICAgICAgIGlmICh0aGlzLmNvbnRhaW5lciAhPSBudWxsKVxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuY29udGFpbmVyLm1hcFBvc2l0aW9uVG9Qb2ludChwb2ludCwgdGhpcylcbiAgICAgICAgcmV0dXJuIHBvaW50XG4gICAgfVxuXG4gICAgY2FwdHVyZShldmVudCkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgIH1cblxuICAgIGJyaW5nVG9Gcm9udCgpIHtcbiAgICAgICAgaWYgKHRoaXMuYXV0b0JyaW5nVG9Gcm9udCkge1xuICAgICAgICAgICAgaWYgKHRoaXMuZGlzcGxheU9iamVjdC5wYXJlbnQgaW5zdGFuY2VvZiBTY2F0dGVyQ29udGFpbmVyKSB7XG4gICAgICAgICAgICAgICAgbGV0IHNjYXR0ZXJDb250YWluZXIgPSB0aGlzLmRpc3BsYXlPYmplY3QucGFyZW50XG4gICAgICAgICAgICAgICAgc2NhdHRlckNvbnRhaW5lci5icmluZ1RvRnJvbnQodGhpcy5kaXNwbGF5T2JqZWN0KVxuICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLmRpc3BsYXlPYmplY3QucGFyZW50ICE9IG51bGwgJiYgdGhpcy5kaXNwbGF5T2JqZWN0LnBhcmVudC5zY2F0dGVyKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5kaXNwbGF5T2JqZWN0LnBhcmVudC5zY2F0dGVyLnRvRnJvbnQodGhpcy5kaXNwbGF5T2JqZWN0KVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgdG9Gcm9udChkaXNwbGF5T2JqZWN0KSB7XG4gICAgICAgIHRoaXMuZGlzcGxheU9iamVjdC5hZGRDaGlsZChkaXNwbGF5T2JqZWN0KVxuICAgIH1cblxuICAgIHZhbGlkU2NhbGUoc2NhbGUpIHtcbiAgICAgICAgc2NhbGUgPSBNYXRoLm1heChzY2FsZSwgdGhpcy5taW5TY2FsZSlcbiAgICAgICAgc2NhbGUgPSBNYXRoLm1pbihzY2FsZSwgdGhpcy5tYXhTY2FsZSlcbiAgICAgICAgcmV0dXJuIHNjYWxlXG4gICAgfVxufVxuIiwiaW1wb3J0IFBJWElBcHAgZnJvbSAnLi9hcHAuanMnXG4vKipcbiAqXG4gKi9cbmV4cG9ydCBjbGFzcyBDb21tYW5kIGV4dGVuZHMgUElYSS5HcmFwaGljcyB7XG4gICAgLyoqKiBBYnN0cmFjdCBiYXNlIGNsYXNzIGZvciByZWNvcmQsIHBsYXksIGFuZCBzdG9wIGNvbW1hbmRzLiAqKiovXG4gICAgY29uc3RydWN0b3IodG9vbHMsIHNlbGVjdGVkQ29sb3IsIHNoYXBlKSB7XG4gICAgICAgIHN1cGVyKClcbiAgICAgICAgdGhpcy50b29scyA9IHRvb2xzXG4gICAgICAgIHRoaXMuc2hhcGUgPSBzaGFwZVxuICAgICAgICB0aGlzLnNlbGVjdGVkID0gZmFsc2VcbiAgICAgICAgdGhpcy5kaXNhYmxlZCA9IGZhbHNlXG4gICAgICAgIHRoaXMuc2VsZWN0ZWRDb2xvciA9IHNlbGVjdGVkQ29sb3JcbiAgICAgICAgdGhpcy5kcmF3KClcbiAgICAgICAgdGhpcy5zZXR1cCgpXG4gICAgfVxuXG4gICAgc2V0dXAoKSB7XG4gICAgfVxuXG4gICAgZHJhdygpIHtcbiAgICAgICAgdGhpcy5jbGVhcigpXG4gICAgICAgIHZhciBjb2xvciA9ICh0aGlzLnNlbGVjdGVkKSA/IHRoaXMuc2VsZWN0ZWRDb2xvciA6IDB4RkZGRkZGXG4gICAgICAgIHRoaXMubGluZVN0eWxlKDApXG4gICAgICAgIHRoaXMuYmVnaW5GaWxsKGNvbG9yLCAxKVxuICAgICAgICB0aGlzLmRyYXdTaGFwZSh0aGlzLnNoYXBlKVxuICAgICAgICB0aGlzLmVuZEZpbGwoKVxuICAgIH1cblxuICAgIHNlbGVjdCgpIHtcbiAgICAgICAgdGhpcy5zZWxlY3RlZCA9IHRydWVcbiAgICAgICAgdGhpcy5kcmF3KClcbiAgICB9XG5cbiAgICBkZXNlbGVjdCgpIHtcbiAgICAgICAgdGhpcy5zZWxlY3RlZCA9IGZhbHNlXG4gICAgICAgIHRoaXMuZHJhdygpXG4gICAgfVxuXG4gICAgdG9nZ2xlKCkge1xuICAgICAgICB0aGlzLnNlbGVjdGVkID0gIXRoaXMuc2VsZWN0ZWRcbiAgICAgICAgdGhpcy5kcmF3KClcbiAgICB9XG5cbiAgICBzdG9wKCkge1xuICAgICAgICB0aGlzLnNlbGVjdGVkID0gZmFsc2VcbiAgICAgICAgdGhpcy5kcmF3KClcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSZWNvcmRDb21tYW5kIGV4dGVuZHMgQ29tbWFuZCB7XG4gICAgLyoqKiBSZWNvcmRzIGV2ZW50cyBmb3IgbGF0ZXIgcmVwbGF5LiAqKiovXG4gICAgc2V0dXAoKSB7XG4gICAgICAgIHRoaXMucmVjb3JkZXIgPSBuZXcgRXZlbnRSZWNvcmRlcigpXG4gICAgfVxuXG4gICAgdG9nZ2xlKCkge1xuICAgICAgICBzdXBlci50b2dnbGUoKVxuICAgICAgICBpZiAoIXRoaXMuc2VsZWN0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMucmVjb3JkZXIuc3RvcFJlY29yZGluZygpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZWNvcmRFdmVudChldmVudCkge1xuICAgICAgICB0aGlzLnJlY29yZGVyLnJlY29yZChldmVudClcbiAgICB9XG5cbiAgICBub3JtYWxpemUodmFsdWUsIGxpbWl0KSB7XG4gICAgICAgIHJldHVybiB2YWx1ZSAvIGxpbWl0XG4gICAgfVxuXG4gICAgbm9ybWFsaXplWCh2YWx1ZSkge1xuICAgICAgICByZXR1cm4gdGhpcy5ub3JtYWxpemUodmFsdWUsIHdpbmRvdy5pbm5lcldpZHRoKVxuICAgIH1cblxuICAgIG5vcm1hbGl6ZVkodmFsdWUpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9ybWFsaXplKHZhbHVlLCB3aW5kb3cuaW5uZXJIZWlnaHQpXG4gICAgfVxuXG4gICAgd2hpbGVOb3RTdG9wcGVkKCkge1xuICAgICAgICByZXR1cm4gdGhpcy50b29scy5wbGF5LnNlbGVjdGVkXG4gICAgfVxuXG4gICAgc3RhcnRSZXBsYXkoKSB7XG4gICAgICAgIGxldCB3aGlsZUNvbmRpdGlvbiA9IHRoaXMud2hpbGVOb3RTdG9wcGVkLmJpbmQodGhpcylcbiAgICAgICAgdGhpcy5yZWNvcmRlci5zdGFydFJlcGxheSh3aGlsZUNvbmRpdGlvbiwgKCkgPT4gdGhpcy50b29scy5wbGF5LnN0b3AoKSlcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBQbGF5Q29tbWFuZCBleHRlbmRzIENvbW1hbmQge1xuICAgIC8qKiogUGxheXMgcmVjb3JkZWQgZXZlbnRzLiAqKiovXG4gICAgdG9nZ2xlKCkge1xuICAgICAgICBzdXBlci50b2dnbGUoKVxuICAgICAgICBpZiAodGhpcy5zZWxlY3RlZCAmJiB0aGlzLnRvb2xzLnJlY29yZC5yZWNvcmRlci5yZWNvcmRlZC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICB0aGlzLnRvb2xzLnN0YXJ0UmVwbGF5KClcbiAgICAgICAgfVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIFN0b3BDb21tYW5kIGV4dGVuZHMgQ29tbWFuZCB7XG4gICAgLyoqKiBTdG9wcyByZWNvcmRpbmcgYW5kIHBsYXlpbmcuICoqKi9cbiAgICB0b2dnbGUoKSB7XG4gICAgICAgIHN1cGVyLnRvZ2dsZSgpXG4gICAgICAgIHRoaXMudG9vbHMucmVjb3JkLnN0b3AoKVxuICAgICAgICB0aGlzLnRvb2xzLnBsYXkuc3RvcCgpXG4gICAgICAgIHNldFRpbWVvdXQodGhpcy5kZXNlbGVjdC5iaW5kKHRoaXMpLCA1MDApXG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgUmVjb3JkZXJUb29scyBleHRlbmRzIFBJWEkuQ29udGFpbmVyIHtcblxuICAgIGNvbnN0cnVjdG9yKHJlbmRlcmVyKSB7XG4gICAgICAgIHN1cGVyKHJlbmRlcmVyKVxuICAgICAgICB0aGlzLnJlbmRlcmVyID0gcmVuZGVyZXJcbiAgICAgICAgdGhpcy5zZXR1cFRvb2xiYXIoKVxuICAgICAgICB0aGlzLnJlcGxheVJhdGUgPSAxMDAuMFxuICAgICAgICB0aGlzLm9uUmVzZXQgPSBudWxsXG4gICAgICAgIHRoaXMudG91Y2hHcmFwaGljcyA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAgICAgICAgdGhpcy5zaG93VG91Y2hlcygpXG4gICAgfVxuXG4gICAgc2V0dXAoY29udGFpbmVyKSB7XG4gICAgICAgIC8vIFNpbmNlIHRoaXMgZGVsZWdhdGUgbWlnaHQgc2hhZG93IGFub3RoZXIgZGVsZWdhdGUsIHdlIG11cyBhdm9pZFxuICAgICAgICAvLyBjYXB0dXJpbmcgUG9pbnRlckV2ZW50cy5cbiAgICAgICAgdGhpcy5kZWxlZ2F0ZSA9IG5ldyBJbnRlcmFjdGlvbk1hcHBlcihjb250YWluZXIsIHRoaXMsIHsgY2FwdHVyZVBvaW50ZXJFdmVudHM6IGZhbHNlIH0pXG4gICAgfVxuXG4gICAgZmluZFRhcmdldChldmVudCwgbG9jYWwsIGdsb2JhbCkge1xuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cblxuICAgIHNldHVwVG9vbGJhcigpIHtcbiAgICAgICAgdGhpcy50b29sYmFyID0gbmV3IFBJWEkuR3JhcGhpY3MoKVxuICAgICAgICB0aGlzLnJlY29yZCA9IG5ldyBSZWNvcmRDb21tYW5kKHRoaXMsIDB4Q0MwMDAwLCBuZXcgUElYSS5DaXJjbGUoMCwgMCwgMTYpKVxuICAgICAgICB0aGlzLnBsYXkgPSBuZXcgUGxheUNvbW1hbmQodGhpcywgMHgwMDAwQ0MsIG5ldyBQSVhJLlBvbHlnb24oMCwgMTYsXG4gICAgICAgICAgICAzMiwgMTYgKyAxNixcbiAgICAgICAgICAgIDAsIDE2ICsgMzIsXG4gICAgICAgICAgICAwLCAxNikpXG4gICAgICAgIHRoaXMuc3RvcCA9IG5ldyBTdG9wQ29tbWFuZCh0aGlzLCAweDAwMDBDQyxcbiAgICAgICAgICAgIG5ldyBQSVhJLlJlY3RhbmdsZSgwLCAwLCAzMiwgMzIpKVxuICAgICAgICB0aGlzLnRvb2xiYXIuYWRkQ2hpbGQodGhpcy5yZWNvcmQpLnBvc2l0aW9uLnNldCg0NCwgNDgpXG4gICAgICAgIHRoaXMudG9vbGJhci5hZGRDaGlsZCh0aGlzLnBsYXkpLnBvc2l0aW9uLnNldCg0NCArIDQ0LCAxNilcbiAgICAgICAgdGhpcy50b29sYmFyLmFkZENoaWxkKHRoaXMuc3RvcCkucG9zaXRpb24uc2V0KDQ0ICsgNDQgKyA0NCArIDE2LCAzMilcbiAgICAgICAgdGhpcy51cGRhdGVUb29sYmFyKClcbiAgICAgICAgdGhpcy5hZGRDaGlsZCh0aGlzLnRvb2xiYXIpXG4gICAgfVxuXG4gICAgdXBkYXRlVG9vbGJhcigpIHtcbiAgICAgICAgdmFyIGdyYXBoaWNzID0gdGhpcy50b29sYmFyXG4gICAgICAgIGdyYXBoaWNzLmNsZWFyKClcbiAgICAgICAgZ3JhcGhpY3MuYmVnaW5GaWxsKDB4MDAwMDAwLCAwLjUpXG4gICAgICAgIGdyYXBoaWNzLmxpbmVTdHlsZSgyLCAweEZGRkZGRiwgMSlcbiAgICAgICAgZ3JhcGhpY3MuZHJhd1JvdW5kZWRSZWN0KDE2LCAxNiwgNDQgKiA0ICsgOCwgNjQsIDgpXG4gICAgICAgIGdyYXBoaWNzLmVuZEZpbGwoKVxuICAgIH1cblxuICAgIG9uTW91c2VXaGVlbChldmVudCkge1xuICAgICAgICBjb25zb2xlLmxvZygnb25Nb3VzZVdoZWVsIG1pc3NpbmcnKVxuICAgIH1cblxuICAgIG9uUHJlc3MocG9pbnQpIHtcbiAgICAgICAgaWYgKHRoaXMucmVjb3JkLmNvbnRhaW5zUG9pbnQocG9pbnQpKSB7XG4gICAgICAgICAgICB0aGlzLnJlY29yZC50b2dnbGUoKVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnBsYXkuY29udGFpbnNQb2ludChwb2ludCkpIHtcbiAgICAgICAgICAgIHRoaXMucGxheS50b2dnbGUoKVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnN0b3AuY29udGFpbnNQb2ludChwb2ludCkpIHtcbiAgICAgICAgICAgIHRoaXMuc3RvcC50b2dnbGUoKVxuICAgICAgICAgICAgaWYgKHRoaXMub25SZXNldCkge1xuICAgICAgICAgICAgICAgIHRoaXMub25SZXNldCgpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBtYXBQb3NpdGlvblRvUG9pbnQocG9pbnQpIHtcbiAgICAgICAgbGV0IGxvY2FsID0gbmV3IFBJWEkuUG9pbnQoKVxuICAgICAgICB0aGlzLnJlbmRlcmVyLnBsdWdpbnMuaW50ZXJhY3Rpb24ubWFwUG9zaXRpb25Ub1BvaW50KGxvY2FsLCBwb2ludC54LCBwb2ludC55KVxuICAgICAgICByZXR1cm4gbG9jYWxcbiAgICB9XG5cbiAgICBleHRyYWN0TG9jYWwoZXZlbnQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFwUG9zaXRpb25Ub1BvaW50KEV2ZW50cy5leHRyYWN0UG9pbnQoZXZlbnQpKVxuICAgIH1cblxuICAgIGNhcHR1cmUoZXZlbnQpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBldmVudC5tb3VzZURvd25TdWJzdGl0dXRlICE9ICd1bmRlZmluZWQnKVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgfVxuXG4gICAgc3RhcnRSZXBsYXkoKSB7XG4gICAgICAgIGlmICh0aGlzLm9uUmVzZXQpIHtcbiAgICAgICAgICAgIHRoaXMub25SZXNldCgpXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5yZWNvcmQuc3RhcnRSZXBsYXkoKVxuICAgIH1cblxuICAgIHNob3dUb3VjaGVzKCkge1xuICAgICAgICB0aGlzLmFkZENoaWxkKHRoaXMudG91Y2hHcmFwaGljcylcbiAgICB9XG5cbiAgICByZWNvcmRFdmVudChldmVudCkge1xuICAgICAgICBpZiAodGhpcy5yZWNvcmQuc2VsZWN0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMucmVjb3JkLnJlY29yZEV2ZW50KGV2ZW50KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25TdGFydChldmVudCwgaW50ZXJhY3Rpb24pIHtcbiAgICAgICAgbGV0IGxvY2FsID0gdGhpcy5leHRyYWN0TG9jYWwoZXZlbnQpXG4gICAgICAgIGlmICghdGhpcy50b29sYmFyLmNvbnRhaW5zUG9pbnQobG9jYWwpKSB7XG4gICAgICAgICAgICB0aGlzLnJlY29yZEV2ZW50KGV2ZW50KVxuICAgICAgICAgICAgdGhpcy51cGRhdGVUb3VjaEdyYXBoaWNzKGludGVyYWN0aW9uKVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25Nb3ZlKGV2ZW50LCBpbnRlcmFjdGlvbikge1xuICAgICAgICBsZXQgbG9jYWwgPSB0aGlzLmV4dHJhY3RMb2NhbChldmVudClcbiAgICAgICAgaWYgKCF0aGlzLnRvb2xiYXIuY29udGFpbnNQb2ludChsb2NhbCkpIHtcbiAgICAgICAgICAgIHRoaXMucmVjb3JkRXZlbnQoZXZlbnQpXG4gICAgICAgICAgICB0aGlzLnVwZGF0ZVRvdWNoR3JhcGhpY3MoaW50ZXJhY3Rpb24pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvbkVuZChldmVudCwgaW50ZXJhY3Rpb24pIHtcbiAgICAgICAgbGV0IGxvY2FsID0gdGhpcy5leHRyYWN0TG9jYWwoZXZlbnQpXG4gICAgICAgIGlmICh0aGlzLnRvb2xiYXIuY29udGFpbnNQb2ludChsb2NhbCkpIHtcbiAgICAgICAgICAgIHRoaXMub25QcmVzcyhsb2NhbClcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMucmVjb3JkRXZlbnQoZXZlbnQpXG4gICAgICAgICAgICB0aGlzLnVwZGF0ZVRvdWNoR3JhcGhpY3MoaW50ZXJhY3Rpb24pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICB1cGRhdGVUb3VjaEdyYXBoaWNzKGludGVyYWN0aW9uKSB7XG4gICAgICAgIGxldCBjdXJyZW50ID0gaW50ZXJhY3Rpb24uY3VycmVudFxuICAgICAgICBsZXQgZ3JhcGhpY3MgPSB0aGlzLnRvdWNoR3JhcGhpY3NcbiAgICAgICAgaWYgKGdyYXBoaWNzICE9IG51bGwpIHtcbiAgICAgICAgICAgIGdyYXBoaWNzLmNsZWFyKClcbiAgICAgICAgICAgIGZvciAobGV0IGtleSBvZiBjdXJyZW50LmtleXMoKSkge1xuICAgICAgICAgICAgICAgIGlmIChpbnRlcmFjdGlvbi5lbmRlZC5oYXMoa2V5KSkge1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBsZXQgcCA9IGN1cnJlbnQuZ2V0KGtleSlcbiAgICAgICAgICAgICAgICBpZiAoa2V5ID09ICdtb3VzZScpIHtcbiAgICAgICAgICAgICAgICAgICAgZ3JhcGhpY3MuYmVnaW5GaWxsKDB4Q0MwMDAwLCAwLjUpXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZ3JhcGhpY3MuYmVnaW5GaWxsKDB4Q0NDQ0NDLCAwLjUpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGdyYXBoaWNzLmRyYXdDaXJjbGUocC54LCBwLnksIDIwKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZ3JhcGhpY3MuZW5kRmlsbCgpXG4gICAgICAgIH1cbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcHBUZXN0IGV4dGVuZHMgUElYSUFwcCB7XG5cbiAgICBjb25zdHJ1Y3RvcihjYW52YXMsIGNvbnRhaW5lcikge1xuICAgICAgICBzdXBlcih7IHZpZXc6IGNhbnZhcywgYmFja2dyb3VuZENvbG9yOiAweDAwMDAwMCB9KVxuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IGNvbnRhaW5lclxuICAgIH1cblxuICAgIHNjZW5lRmFjdG9yeSgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBSZWNvcmRlclRvb2xzKHRoaXMucmVuZGVyZXIpXG4gICAgfVxuXG4gICAgc2V0dXAoKSB7XG4gICAgICAgIHN1cGVyLnNldHVwKClcbiAgICAgICAgdGhpcy5zY2VuZS5zZXR1cCh0aGlzLmNvbnRhaW5lcilcbiAgICB9XG5cbiAgICBydW4ocmVzZXQgPSBudWxsKSB7XG4gICAgICAgIHRoaXMuc2NlbmUub25SZXNldCA9IHJlc2V0XG4gICAgICAgIGNvbnNvbGUubG9nKCdSdW5uaW5nIEFwcFRlc3QnKVxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbn1cblxuXG5cblxuIiwiXG5cbi8qKlxuICogRGVmaW5lcyB1c2VmdWxsIGRlZmF1bHQgdGV4dCBzdHlsZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBGb250SW5mbyB7XG5cbiAgICBzdGF0aWMgZ2V0IHNtYWxsKCkge1xuICAgICAgICByZXR1cm4gYXBwLnRoZW1lLnRleHRTdHlsZVNtYWxsXG4gICAgfVxuXG4gICAgc3RhdGljIGdldCBub3JtYWwoKSB7XG4gICAgICAgIHJldHVybiBhcHAudGhlbWUudGV4dFN0eWxlXG4gICAgfVxuXG4gICAgc3RhdGljIGdldCBjZW50ZXJlZCgpIHtcbiAgICAgICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oe30sIGFwcC50aGVtZS50ZXh0U3R5bGUsIHsgYWxpZ246ICdjZW50ZXInIH0pXG4gICAgfVxufVxuXG4vKipcbiAqIFN0YXRpYyBtZXRob2RzIHRvIHN1cHBvcnQgaHlwaGVuYXRpb24gb2YgbGluZXMuXG4gKiBcbiAqIEBjbGFzcyBIeXBlbmF0ZVxuICovXG5leHBvcnQgY2xhc3MgSHlwZW5hdGUge1xuXG4gICAgc3RhdGljIHNwbGl0UGFydChwYXJ0KSB7XG4gICAgICAgIGxldCBwYXJ0cyA9IHBhcnQuc3BsaXQoJy0nKVxuICAgICAgICBpZiAocGFydHMubGVuZ3RoID09IDEpXG4gICAgICAgICAgICByZXR1cm4gW3BhcnRdXG4gICAgICAgIGxldCByZXN1bHQgPSBbXVxuICAgICAgICBsZXQgbGFzdCA9IHBhcnRzLnBvcCgpXG4gICAgICAgIGZvciAobGV0IHAgb2YgcGFydHMpIHtcbiAgICAgICAgICAgIHJlc3VsdC5wdXNoKHAgKyAnLScpXG4gICAgICAgIH1cbiAgICAgICAgcmVzdWx0LnB1c2gobGFzdClcbiAgICAgICAgcmV0dXJuIHJlc3VsdC5maWx0ZXIocCA9PiBwLmxlbmd0aCA+IDApXG4gICAgfVxuXG4gICAgc3RhdGljIHNwbGl0V29yZCh3b3JkKSB7XG4gICAgICAgIGlmICh0eXBlb2YgKGxhbmd1YWdlKSA9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgaWYgKHdvcmQuaW5kZXhPZignLScpID4gLTEpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gd29yZC5zcGxpdCgnLScpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gW3dvcmRdXG4gICAgICAgIH1cbiAgICAgICAgbGV0IHBhcnRzID0gbGFuZ3VhZ2UuaHlwaGVuYXRlKHdvcmQpXG4gICAgICAgIGxldCByZXN1bHQgPSBbXVxuICAgICAgICBmb3IgKGxldCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBzcGxpdHRlZCBvZiB0aGlzLnNwbGl0UGFydChwYXJ0KSkge1xuICAgICAgICAgICAgICAgIHJlc3VsdC5wdXNoKHNwbGl0dGVkKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICB9XG5cbiAgICBzdGF0aWMgYWJicmV2aWF0ZUxpbmUobGFiZWwsIHN0eWxlLCB3aWR0aCkge1xuICAgICAgICBjb25zdCBwaXhpU3R5bGUgPSBuZXcgUElYSS5UZXh0U3R5bGUoc3R5bGUpICAgXG4gICAgICAgIGxldCBtZXRyaWNzID0gUElYSS5UZXh0TWV0cmljcy5tZWFzdXJlVGV4dChsYWJlbCwgcGl4aVN0eWxlKVxuICAgICAgICB3aGlsZShtZXRyaWNzLndpZHRoID4gd2lkdGggJiYgbGFiZWwubGVuZ3RoID4gMykge1xuICAgICAgICAgICAgbGFiZWwgPSBsYWJlbC5zbGljZSgwLCBsYWJlbC5sZW5ndGgtMSlcbiAgICAgICAgICAgIG1ldHJpY3MgPSBQSVhJLlRleHRNZXRyaWNzLm1lYXN1cmVUZXh0KGxhYmVsLCBwaXhpU3R5bGUpXG4gICAgICAgIH1cbiAgICAgICAgbGFiZWwgPSBsYWJlbC5zbGljZSgwLCBsYWJlbC5sZW5ndGgtMSlcbiAgICAgICAgcmV0dXJuIGxhYmVsICsgJ+KApidcbiAgICB9XG5cbiAgICBzdGF0aWMgc3BsaXRMaW5lKGxpbmUsIHBpeGlTdHlsZSwgd2lkdGgsIHNwYWNlLCBtaW51cykge1xuICAgICAgICBsZXQgeCA9IDBcbiAgICAgICAgbGV0IHJlc3VsdCA9ICcnXG4gICAgICAgIGxldCB3b3JkcyA9IGxpbmUuc3BsaXQoJyAnKVxuICAgICAgICBmb3IgKGxldCB3b3JkIG9mIHdvcmRzKSB7XG4gICAgICAgICAgICBsZXQgd29yZE1ldHJpY3MgPSBQSVhJLlRleHRNZXRyaWNzLm1lYXN1cmVUZXh0KHdvcmQsIHBpeGlTdHlsZSlcbiAgICAgICAgICAgIGlmICh4ICsgd29yZE1ldHJpY3Mud2lkdGggPj0gd2lkdGgpIHtcbiAgICAgICAgICAgICAgICBsZXQgcGFydHMgPSB0aGlzLnNwbGl0V29yZCh3b3JkKVxuICAgICAgICAgICAgICAgIGxldCBuZXdXb3JkID0gJydcbiAgICAgICAgICAgICAgICBpZiAocGFydHMubGVuZ3RoID09IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgbmV3V29yZCArPSAnXFxuJyArIHdvcmQgKyAnICdcbiAgICAgICAgICAgICAgICAgICAgeCA9IHdvcmRNZXRyaWNzLndpZHRoICsgc3BhY2Uud2lkdGhcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGxldCBmaXJzdCA9IHRydWVcbiAgICAgICAgICAgICAgICAgICAgbGV0IGxhc3RQYXJ0ID0gJydcbiAgICAgICAgICAgICAgICAgICAgZm9yIChsZXQgcGFydCBvZiBwYXJ0cykge1xuICAgICAgICAgICAgICAgICAgICAgICAgbGV0IHBhcnRNZXRyaWNzID0gUElYSS5UZXh0TWV0cmljcy5tZWFzdXJlVGV4dChwYXJ0LCBwaXhpU3R5bGUpXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoeCArIHBhcnRNZXRyaWNzLndpZHRoICsgc3BhY2Uud2lkdGggPiB3aWR0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld1dvcmQgKz0gKChmaXJzdCB8fCBsYXN0UGFydC5lbmRzV2l0aCgnLScpKSA/ICdcXG4nIDogJy1cXG4nKSArIHBhcnRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcGFydE1ldHJpY3Mud2lkdGhcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld1dvcmQgKz0gcGFydFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggKz0gcGFydE1ldHJpY3Mud2lkdGhcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RQYXJ0ID0gcGFydFxuICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgPSBmYWxzZVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHggKz0gc3BhY2Uud2lkdGhcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmVzdWx0ICs9IG5ld1dvcmQgKyAnICdcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIHJlc3VsdCArPSB3b3JkICsgJyAnXG4gICAgICAgICAgICAgICAgeCArPSB3b3JkTWV0cmljcy53aWR0aCArIHNwYWNlLndpZHRoXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgIH1cblxuICAgIC8qKlxuICAgICAqICBNYWluIG1ldGhvZCBhbmQgZW50cnkgcG9pbnQgZm9yIHRleHQgaHlwaGVuYXRpb24gXG4gICAgICpcbiAgICAgKiBAc3RhdGljXG4gICAgICogQHBhcmFtIHsqfSB0ZXh0XG4gICAgICogQHBhcmFtIHsqfSBzdHlsZVxuICAgICAqIEBwYXJhbSB7Kn0gd2lkdGhcbiAgICAgKiBAcmV0dXJuc1xuICAgICAqIEBtZW1iZXJvZiBIeXBlbmF0ZVxuICAgICAqL1xuICAgIHN0YXRpYyBzcGxpdExpbmVzKHRleHQsIHN0eWxlLCB3aWR0aCkge1xuICAgICAgICBjb25zdCBwaXhpU3R5bGUgPSBuZXcgUElYSS5UZXh0U3R5bGUoc3R5bGUpXG4gICAgICAgIGNvbnN0IGxpbmVzID0gdGV4dC5zcGxpdCgnXFxuJylcbiAgICAgICAgY29uc3Qgc3BhY2UgPSBQSVhJLlRleHRNZXRyaWNzLm1lYXN1cmVUZXh0KCcgJywgcGl4aVN0eWxlKVxuICAgICAgICBjb25zdCBtaW51cyA9IFBJWEkuVGV4dE1ldHJpY3MubWVhc3VyZVRleHQoJy0nLCBwaXhpU3R5bGUpXG4gICAgICAgIGxldCByZXN1bHQgPSBbXVxuICAgICAgICBmb3IgKGxldCBsaW5lIG9mIGxpbmVzKSB7XG4gICAgICAgICAgICByZXN1bHQucHVzaCh0aGlzLnNwbGl0TGluZShsaW5lLCBwaXhpU3R5bGUsIHdpZHRoLCBzcGFjZSwgbWludXMpKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHQuam9pbignXFxuJylcbiAgICB9XG59XG5cbi8qKlxuICogQSBzcGVjaWFsaXphdGlvbiBvZiB0aGUgUElYSS5HcmFwaGljcyBjbGFzcyB0aGF0IGFsbG93cyB0b1xuICogcmVzdXNlIGFuZCBwbGFjZSBsYWJlbHMgYWNyb3NzIGRpZmZlcmVudCBsYXlvdXQgdmFyaWFudHNcbiAqXG4gKiBAZXhwb3J0XG4gKiBAY2xhc3MgTGFiZWxlZEdyYXBoaWNzXG4gKiBAZXh0ZW5kcyB7UElYSS5HcmFwaGljc31cbiAqL1xuZXhwb3J0IGNsYXNzIExhYmVsZWRHcmFwaGljcyBleHRlbmRzIFBJWEkuR3JhcGhpY3Mge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBMYWJlbGVkR3JhcGhpY3MgYW5kIGRlZmluZXMgYSBsb2NhbCBsYWJlbCBjYWNoZS5cbiAgICAgKiBcbiAgICAgKiBAbWVtYmVyb2YgTGFiZWxlZEdyYXBoaWNzXG4gICAgICovXG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHN1cGVyKClcbiAgICAgICAgdGhpcy5sYWJlbHMgPSBuZXcgTWFwKClcbiAgICB9XG5cbiAgICBfY3JlYXRlVGV4dChsYWJlbCwgZm9udEluZm8pIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQSVhJLlRleHQobGFiZWwsIGZvbnRJbmZvKVxuICAgIH1cblxuICAgIFxuICAgIC8qKlxuICAgICAqIE1haW4gYWRkaXRpb25hbCBtZXRob2QuIEVuc3VyZXMgdGhhdCBhIHRleHQgb2JqZWN0IGlzIGNyZWF0ZWQgdGhhdCBpcyBjYWNoZWRcbiAgICAgKiB1bmRlciB0aGUgZ2l2ZW4ga2V5LlxuICAgICAqXG4gICAgICogQHBhcmFtIHsqfSBrZXkgLSBUaGUgY2FjaGUga2V5XG4gICAgICogQHBhcmFtIHsqfSBsYWJlbCAtIFRoZSBsYWJlbCB0byBzaG93XG4gICAgICogQHBhcmFtIHsqfSBbYXR0cnM9e31dIC0gRGVmaW5lcyBhdHRyaWJ1dGVzIG9mIHRoZSB0ZXh0IG9iamVjdC4gXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ246ICdyaWdodCcsICdsZWZ0Jywgb3IgJ2NlbnRlcidcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdXN0aWZ5OiAndG9wJywgJ2JvdHRvbScsIG9yICdjZW50ZXInXG4gICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4TGluZXM6IHtpbnRlZ2VyfSB0cnVuY2F0ZXMgdGhlIHRleHQgYW5kIGFkZHMgZWxsaXBzaXNcbiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhIZWlnaHQ6IHtudW1iZXJ9IHRydW5jYXRlcyB0ZXh0IHRoYXQgbmVlZHMgbW9yZSBzcGFjZSBhbmQgYWRkcyBlbGxpcHNpc1xuICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heFdpZHRoOiB7bnVtYmVyfSB3b3JkIHdyYXBzIHRleHQgdXNpbmcgaHlwaGVuYXRpb24gaWYgcG9zc2libGVcbiAgICAgKiBAcGFyYW0geyp9IFtmb250SW5mbz1Gb250SW5mby5ub3JtYWxdIC0gRGVmaW5lcyBQSVhJLlRleHRTdHlsZSBhdHRyaWJ1dGVzXG4gICAgICogQHJldHVybnMge1BJWEkuVGV4dH0gaW5zdGFuY2VcbiAgICAgKiBAbWVtYmVyb2YgTGFiZWxlZEdyYXBoaWNzXG4gICAgICovXG4gICAgZW5zdXJlTGFiZWwoa2V5LCBsYWJlbCwgYXR0cnMgPSB7fSwgZm9udEluZm8gPSBGb250SW5mby5ub3JtYWwpIHtcblxuICAgICAgICBpZiAoYXR0cnMubWF4V2lkdGggJiYgYXR0cnMubWF4TGluZXMgPT0gMSkge1xuICAgICAgICAgICAgbGFiZWwgPSBIeXBlbmF0ZS5hYmJyZXZpYXRlTGluZShsYWJlbCwgZm9udEluZm8sIGF0dHJzLm1heFdpZHRoKVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgaWYgKGF0dHJzLm1heFdpZHRoKSB7XG4gICAgICAgICAgICAgICAgbGFiZWwgPSBIeXBlbmF0ZS5zcGxpdExpbmVzKGxhYmVsLCBmb250SW5mbywgYXR0cnMubWF4V2lkdGgpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoYXR0cnMubWF4TGluZXMpIHtcbiAgICAgICAgICAgICAgICBsYWJlbCA9IHRoaXMudHJ1bmNhdGVMYWJlbChsYWJlbCwgZm9udEluZm8sIGF0dHJzLm1heExpbmVzKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGF0dHJzLm1heEhlaWdodCkge1xuICAgICAgICAgICAgICAgIGxldCBzdHlsZUluZm8gPSBuZXcgUElYSS5UZXh0U3R5bGUoZm9udEluZm8pXG4gICAgICAgICAgICAgICAgbGV0IG1ldHJpY3MgPSBQSVhJLlRleHRNZXRyaWNzLm1lYXN1cmVUZXh0KGxhYmVsLCBzdHlsZUluZm8pXG4gICAgICAgICAgICAgICAgbGV0IG1heExpbmVzID0gTWF0aC5tYXgoYXR0cnMubWF4SGVpZ2h0IC8gbWV0cmljcy5saW5lSGVpZ2h0LCAxKVxuICAgICAgICAgICAgICAgIGxhYmVsID0gdGhpcy50cnVuY2F0ZUxhYmVsKGxhYmVsLCBmb250SW5mbywgbWF4TGluZXMpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICBcbiAgICAgICAgaWYgKCF0aGlzLmxhYmVscy5oYXMoa2V5KSkge1xuICAgICAgICAgICAgbGV0IHRleHQgPSB0aGlzLl9jcmVhdGVUZXh0KGxhYmVsLCBmb250SW5mbylcbiAgICAgICAgICAgIHRoaXMubGFiZWxzLnNldChrZXksIHRleHQpXG4gICAgICAgICAgICB0aGlzLmFkZENoaWxkKHRleHQpXG4gICAgICAgIH1cbiAgICAgICAgbGV0IHRleHQgPSB0aGlzLmxhYmVscy5nZXQoa2V5KVxuICAgICAgICBmb3IgKGxldCBrIGluIGF0dHJzKSB7XG4gICAgICAgICAgICB0ZXh0W2tdID0gYXR0cnNba11cbiAgICAgICAgfVxuICAgICAgICBpZiAobGFiZWwgIT0gdGV4dC50ZXh0KVxuICAgICAgICAgICAgdGV4dC50ZXh0ID0gbGFiZWxcbiAgICAgICAgLy8gV2UgZG8gbm90IGZvbGxvdyB0aGUgZmxleGJveCBqYXJnb24gYW5kIHVzZSBhbGlnbiBmb3IgeCBhbmQganVzdGlmeSBmb3IgeSBheGlzXG4gICAgICAgIC8vIFRoaXMgZGV2aWF0aW9uIGlzIG5lZWRlZCB0byBlbnN1cmUgYmFja3dhcmQgY29tcGF0YWJpbGl0eVxuICAgICAgICBzd2l0Y2ggKGF0dHJzLmp1c3RpZnkgfHwgbnVsbCkge1xuICAgICAgICAgICAgY2FzZSAndG9wJzpcbiAgICAgICAgICAgICAgICB0ZXh0LmFuY2hvci55ID0gMFxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBjYXNlICdib3R0b20nOlxuICAgICAgICAgICAgICAgIHRleHQuYW5jaG9yLnggPSAxXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgdGV4dC5hbmNob3IueSA9IDAuNVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgIH1cbiAgICAgICAgc3dpdGNoIChhdHRycy5hbGlnbikge1xuICAgICAgICAgICAgY2FzZSAncmlnaHQnOlxuICAgICAgICAgICAgICAgIHRleHQuYW5jaG9yLnggPSAxXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIGNhc2UgJ2NlbnRlcic6XG4gICAgICAgICAgICAgICAgdGV4dC5hbmNob3IueCA9IDAuNVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHRleHQuYW5jaG9yLnggPSAwXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgICB0ZXh0LnZpc2libGUgPSB0cnVlXG4gICAgICAgIHJldHVybiB0ZXh0XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUHJpdmF0ZSBtZXRob2QgdGhhdCB0cnVuY2F0ZXMgdGhlIHRleHQgYW5kIGFkZHMgYW4gZWxsaXBzaXMgaWYgdGhlcmUgYXJlIG1vcmUgbGluZXNcbiAgICAgKiB0aGFuIHdhbnRlZFxuICAgICAqXG4gICAgICogQHBhcmFtIHsqfSB0ZXh0XG4gICAgICogQHBhcmFtIHsqfSBzdHlsZVxuICAgICAqIEBwYXJhbSB7Kn0gW21heExpbmVzPUluZmluaXR5XVxuICAgICAqIEByZXR1cm5zXG4gICAgICogQG1lbWJlcm9mIExhYmVsZWRHcmFwaGljc1xuICAgICAqL1xuICAgIHRydW5jYXRlTGFiZWwodGV4dCwgc3R5bGUsIG1heExpbmVzID0gSW5maW5pdHkpIHtcbiAgICAgICAgaWYgKG1heExpbmVzID09PSBJbmZpbml0eSkge1xuICAgICAgICAgICAgcmV0dXJuIHRleHRcbiAgICAgICAgfVxuICAgICAgICBjb25zdCB7IHdvcmRXcmFwV2lkdGggfSA9IHN0eWxlXG4gICAgICAgIGNvbnN0IHBpeGlTdHlsZSA9IG5ldyBQSVhJLlRleHRTdHlsZShzdHlsZSlcbiAgICAgICAgY29uc3QgeyBsaW5lcyB9ID0gUElYSS5UZXh0TWV0cmljcy5tZWFzdXJlVGV4dCh0ZXh0LCBwaXhpU3R5bGUpXG4gICAgICAgIGxldCBuZXdUZXh0ID0gdGV4dFxuICAgICAgICBpZiAobGluZXMubGVuZ3RoID4gbWF4TGluZXMpIHtcbiAgICAgICAgICAgIGNvbnN0IHRydW5jYXRlZExpbmVzID0gbGluZXMuc2xpY2UoMCwgbWF4TGluZXMpXG4gICAgICAgICAgICBjb25zdCBsYXN0TGluZSA9IHRydW5jYXRlZExpbmVzW3RydW5jYXRlZExpbmVzLmxlbmd0aCAtIDFdXG4gICAgICAgICAgICBjb25zdCB3b3JkcyA9IGxhc3RMaW5lLnNwbGl0KCcgJylcbiAgICAgICAgICAgIGNvbnN0IHdvcmRNZXRyaWNzID0gUElYSS5UZXh0TWV0cmljcy5tZWFzdXJlVGV4dChgXFx1MDBBMFxcbi4uLlxcbiR7d29yZHMuam9pbignXFxuJyl9YCwgcGl4aVN0eWxlKVxuICAgICAgICAgICAgY29uc3QgW3NwYWNlTGVuZ3RoLCBkb3RzTGVuZ3RoLCAuLi53b3JkTGVuZ3Roc10gPSB3b3JkTWV0cmljcy5saW5lV2lkdGhzXG4gICAgICAgICAgICBjb25zdCB7IHRleHQ6IG5ld0xhc3RMaW5lIH0gPSB3b3JkTGVuZ3Rocy5yZWR1Y2UoKGRhdGEsIHdvcmRMZW5ndGgsIGkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoZGF0YS5sZW5ndGggKyB3b3JkTGVuZ3RoICsgc3BhY2VMZW5ndGggPj0gd29yZFdyYXBXaWR0aCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4geyAuLi5kYXRhLCBsZW5ndGg6IHdvcmRXcmFwV2lkdGggfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICB0ZXh0OiBgJHtkYXRhLnRleHR9JHtpID4gMCA/ICcgJyA6ICcnfSR7d29yZHNbaV19YCxcbiAgICAgICAgICAgICAgICAgICAgbGVuZ3RoOiBkYXRhLmxlbmd0aCArIHdvcmRMZW5ndGggKyBzcGFjZUxlbmd0aCxcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfSwgeyB0ZXh0OiAnJywgbGVuZ3RoOiBkb3RzTGVuZ3RoIH0pXG4gICAgICAgICAgICB0cnVuY2F0ZWRMaW5lc1t0cnVuY2F0ZWRMaW5lcy5sZW5ndGggLSAxXSA9IGAke25ld0xhc3RMaW5lfS4uLmBcbiAgICAgICAgICAgIG5ld1RleHQgPSB0cnVuY2F0ZWRMaW5lcy5qb2luKCdcXG4nKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBuZXdUZXh0XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbGFiZWwgZm9yIHRoZSBnaXZlbiBrZXkuXG4gICAgICpcbiAgICAgKiBAcGFyYW0geyp9IGtleVxuICAgICAqIEByZXR1cm5zXG4gICAgICogQG1lbWJlcm9mIExhYmVsZWRHcmFwaGljc1xuICAgICAqL1xuICAgIGdldExhYmVsKGtleSkge1xuICAgICAgICByZXR1cm4gdGhpcy5sYWJlbHMuZ2V0KGtleSlcbiAgICB9XG5cbiAgICAvKiogXG4gICAgICogSGlkZXMgdGhlIGxhYmVsIHdpdGggdGhlIGdpdmVuIGtleS5cbiAgICAgKiBAcGFyYW0geyp9IGtleVxuICAgICAqIEBtZW1iZXJvZiBMYWJlbGVkR3JhcGhpY3NcbiAgICAgKi9cbiAgICBoaWRlTGFiZWwoa2V5KSB7XG4gICAgICAgIGxldCBsYWJlbCA9IHRoaXMubGFiZWxzLmdldChrZXkpXG4gICAgICAgIGlmIChsYWJlbCkge1xuICAgICAgICAgICAgbGFiZWwudmlzaWJsZSA9IGZhbHNlXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogXG4gICAgICogUmVtb3ZlcyB0aGUgbGFiZWwgd2l0aCB0aGUgZ2l2ZW4ga2V5LlxuICAgICAqIEBwYXJhbSB7Kn0ga2V5XG4gICAgICogQG1lbWJlcm9mIExhYmVsZWRHcmFwaGljc1xuICAgICAqL1xuICAgIHJlbW92ZUxhYmVsKGtleSkge1xuICAgICAgICBsZXQgbGFiZWwgPSB0aGlzLmxhYmVscy5nZXQoa2V5KVxuICAgICAgICB0aGlzLmxhYmVscy5kZWxldGUoa2V5KVxuICAgICAgICBsYWJlbC5kZXN0cm95KClcbiAgICB9XG5cbiAgIFxuICAgIC8qKlxuICAgICAqIEVuc3VyZXMgdGhhdCBsYWJlbHMgYXJlIGhpZGRlbiBvbiBjbGVhci5cbiAgICAgKlxuICAgICAqIEBtZW1iZXJvZiBMYWJlbGVkR3JhcGhpY3NcbiAgICAgKi9cbiAgICBjbGVhcigpIHtcbiAgICAgICAgc3VwZXIuY2xlYXIoKVxuICAgICAgICBmb3IgKGxldCBrZXkgb2YgdGhpcy5sYWJlbHMua2V5cygpKSB7XG4gICAgICAgICAgICB0aGlzLmhpZGVMYWJlbChrZXkpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBMb2dzIGRlYnVnZ2luZyBpbmZvc1xuICAgICAqXG4gICAgICogQG1lbWJlcm9mIExhYmVsZWRHcmFwaGljc1xuICAgICAqL1xuICAgIGRlYnVnSW5mb3MoKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKHsgc2l6ZTogdGhpcy5sYWJlbHMuc2l6ZSwgbGFiZWxzOiB0aGlzLmxhYmVscyB9KVxuICAgIH1cbn1cblxuXG5jb25zdCBsYWJlbENhY2hlID0gbmV3IE1hcCgpXG5cbmZ1bmN0aW9uIGdldFRleHR1cmUobGFiZWwsIGZvbnRJbmZvID0gRm9udEluZm8ubm9ybWFsKSB7XG4gICAgbGV0IGtleSA9IGxhYmVsICsgZm9udEluZm8uZm9udEZhbWlseSArIGZvbnRJbmZvLmZvbnRTaXplXG5cbiAgICBpZiAobGFiZWxDYWNoZS5oYXMoa2V5KSkge1xuICAgICAgICByZXR1cm4gbGFiZWxDYWNoZS5nZXQoa2V5KVxuICAgIH1cbiAgICBsZXQgZXhwYW5kZWRGb250ID0gT2JqZWN0LmFzc2lnbih7fSwgZm9udEluZm8pXG4gICAgZXhwYW5kZWRGb250LmZvbnRTaXplICo9IHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvXG4gICAgbGV0IHRleHQgPSBuZXcgUElYSS5UZXh0KGxhYmVsLCBleHBhbmRlZEZvbnQpXG4gICAgdGV4dC51cGRhdGVUZXh0KClcbiAgICBsYWJlbENhY2hlLnNldChrZXksIHRleHQudGV4dHVyZSlcbiAgICByZXR1cm4gdGV4dC50ZXh0dXJlXG59XG5cbmNsYXNzIFNwcml0ZUxhYmVsIGV4dGVuZHMgUElYSS5TcHJpdGUge1xuXG4gICAgY29uc3RydWN0b3IobGFiZWwsIGZvbnRJbmZvKSB7XG4gICAgICAgIGxldCB0ZXh0dXJlID0gZ2V0VGV4dHVyZShsYWJlbCwgZm9udEluZm8pXG4gICAgICAgIHN1cGVyKHRleHR1cmUpXG4gICAgICAgIHRoaXMubGFiZWwgPSBsYWJlbFxuICAgICAgICB0aGlzLmZvbnRJbmZvID0gZm9udEluZm9cbiAgICAgICAgdGhpcy5zY2FsZS5zZXQoMC44IC8gd2luZG93LmRldmljZVBpeGVsUmF0aW8pXG4gICAgfVxuXG4gICAgc2V0IHRleHQobGFiZWwpIHtcbiAgICAgICAgdGhpcy5sYWJlbCA9IGxhYmVsXG4gICAgICAgIHRoaXMudGV4dHVyZSA9IGdldFRleHR1cmUobGFiZWwsIHRoaXMuZm9udEluZm8pXG4gICAgfVxuXG4gICAgZ2V0IHRleHQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmxhYmVsXG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgQml0bWFwTGFiZWxlZEdyYXBoaWNzIGV4dGVuZHMgTGFiZWxlZEdyYXBoaWNzIHtcblxuICAgIF9jcmVhdGVUZXh0KGxhYmVsLCBmb250SW5mbykge1xuICAgICAgICBsZXQgdGV4dHVyZSA9IGdldFRleHR1cmUobGFiZWwsIGZvbnRJbmZvKVxuICAgICAgICByZXR1cm4gbmV3IFNwcml0ZUxhYmVsKHRleHR1cmUpXG4gICAgfVxuXG59IiwiaW1wb3J0IHsgQ3ljbGUsIENvbG9ycywgRGF0ZXMsIGlzRW1wdHkgfSBmcm9tICcuLi91dGlscy5qcydcbmltcG9ydCB7IENhcGFiaWxpdGllcyB9IGZyb20gJy4uL2NhcGFiaWxpdGllcy5qcydcbmltcG9ydCB7IEJpdG1hcExhYmVsZWRHcmFwaGljcywgTGFiZWxlZEdyYXBoaWNzLCBGb250SW5mbyB9IGZyb20gJy4vbGFiZWxlZGdyYXBoaWNzLmpzJ1xuXG5cbmV4cG9ydCBjbGFzcyBUaWNrcyB7XG5cbiAgICBnZXQgcmVzZXJ2ZWRQcmVmaXhlcygpIHtcbiAgICAgICAgcmV0dXJuIFsnZGVjYWRlJywgJ3llYXInLCAnbW9udGgnLCAnZGF5JywgJ2hvdXInLCAnbWludXRlJywgJ3NlY29uZCddXG4gICAgfVxuXG4gICAgZ2V0IG1pbldpZHRoKCkge1xuICAgICAgICByZXR1cm4gMTBcbiAgICB9XG5cbiAgICBmb3JtYXQoYXZhaWxhYmxlKSB7XG4gICAgICAgIHJldHVybiB7IHllYXI6ICcyLWRpZ2l0JywgdGltZVpvbmU6ICdVVEMnIH1cbiAgICB9XG5cbiAgICBnZXQgbWluTGFiZWxXaWR0aCgpIHtcbiAgICAgICAgcmV0dXJuIDQ0XG4gICAgfVxuXG4gICAgZ2V0IGZvcm1hdEtleSgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMua2V5XG4gICAgfVxuXG4gICAgZGF0ZUtleShkYXRlKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmtleSArIGRhdGUuZ2V0RnVsbFllYXIoKVxuICAgIH1cblxuICAgICppdGVyKHN0YXJ0LCBlbmQpIHtcbiAgICAgICAgbGV0IGRhdGUgPSB0aGlzLml0ZXJTdGFydChzdGFydClcbiAgICAgICAgd2hpbGUgKGRhdGUgPD0gZW5kKSB7XG4gICAgICAgICAgICB5aWVsZCBkYXRlXG4gICAgICAgICAgICBkYXRlID0gdGhpcy5uZXh0KGRhdGUpXG4gICAgICAgIH1cbiAgICAgICAgeWllbGQgZGF0ZVxuICAgIH1cblxuICAgICppdGVyUmFuZ2VzKHJhbmdlKSB7XG4gICAgICAgIGZvciAobGV0IGRhdGUgb2YgdGhpcy5pdGVyKHJhbmdlLnN0YXJ0LCByYW5nZS5lbmQpKSB7XG4gICAgICAgICAgICBsZXQgbmV4dCA9IHRoaXMubmV4dChkYXRlKVxuICAgICAgICAgICAgeWllbGQgeyBzdGFydDogZGF0ZSwgZW5kOiBuZXh0IH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHNlbGVjdGVkUmFuZ2UodGltZWxpbmUsIGluZm8pIHtcbiAgICAgICAgbGV0IGZpcnN0ID0gbnVsbFxuICAgICAgICBsZXQgbGFzdCA9IG51bGxcbiAgICAgICAgbGV0IHZpc2libGVGaXJzdCA9IG51bGxcbiAgICAgICAgbGV0IHZpc2libGVMYXN0ID0gbnVsbFxuICAgICAgICBsZXQgdW5pdHMgPSAwXG4gICAgICAgIGZvciAobGV0IHsgc3RhcnQsIGVuZCB9IG9mIHRoaXMuaXRlclJhbmdlcyhpbmZvKSkge1xuICAgICAgICAgICAgaWYgKHRpbWVsaW5lLnZpc2libGVSYW5nZShzdGFydCwgZW5kKSkge1xuICAgICAgICAgICAgICAgIGlmIChmaXJzdCA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIGZpcnN0ID0gc3RhcnRcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgbGFzdCA9IGVuZFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHRpbWVsaW5lLnZpc2libGVEYXRlKHN0YXJ0KSAmJiB0aW1lbGluZS52aXNpYmxlRGF0ZShlbmQpKSB7XG4gICAgICAgICAgICAgICAgdW5pdHMgKz0gMVxuICAgICAgICAgICAgICAgIGlmICh2aXNpYmxlRmlyc3QgPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICB2aXNpYmxlRmlyc3QgPSBzdGFydFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB2aXNpYmxlTGFzdCA9IGVuZFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChmaXJzdCA9PSBudWxsKVxuICAgICAgICAgICAgcmV0dXJuIGluZm9cbiAgICAgICAgcmV0dXJuIHsgc3RhcnQ6IGZpcnN0LCBlbmQ6IGxhc3QsIHZpc2libGVTdGFydDogdmlzaWJsZUZpcnN0LCB2aXNpYmxlRW5kOiB2aXNpYmxlTGFzdCwgdW5pdHM6IHVuaXRzIH1cbiAgICB9XG5cbiAgICBkcmF3VGljayh0aW1lbGluZSwgeCwgeSwgZGF0ZSkge1xuICAgICAgICBsZXQgdmlzaWJsZSA9IGRhdGUgPiB0aW1lbGluZS5zdGFydCAmJiBkYXRlIDwgdGltZWxpbmUuZW5kXG4gICAgICAgIGlmICghdmlzaWJsZSlcbiAgICAgICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgICB0aW1lbGluZS5kcmF3VGljayh4KVxuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgIH1cblxuICAgIHRvTG9jYWxlU3RyaW5nKGRhdGUsIGZvcm1hdCkge1xuICAgICAgICByZXR1cm4gZGF0ZS50b0xvY2FsZURhdGVTdHJpbmcoJ2RlJywgZm9ybWF0KVxuICAgIH1cblxuICAgIGRyYXcodGltZWxpbmUsIHJhbmdlLCB3aWR0aCwgaGVpZ2h0LCBhdmFpbGFibGUsIGZvcm1hdCwgbmV4dEZvcm1hdCwgbGV2ZWwpIHtcbiAgICAgICAgbGV0IGZpcnN0ID0gbnVsbFxuICAgICAgICBsZXQgbGFzdCA9IG51bGxcbiAgICAgICAgbGV0IGtleWVkRm9ybWF0ID0gKGZvcm1hdCkgPyBmb3JtYXRbdGhpcy5mb3JtYXRLZXldIDogbnVsbFxuICAgICAgICBsZXQga2V5ZWROZXh0Rm9ybWF0ID0gKG5leHRGb3JtYXQpID8gbmV4dEZvcm1hdFt0aGlzLmZvcm1hdEtleV0gOiBudWxsXG4gICAgICAgIGxldCByZWR1bmRhbnQgPSAobmV4dEZvcm1hdCkgPyB0aGlzLmZvcm1hdEtleSBpbiBuZXh0Rm9ybWF0IDogZmFsc2VcbiAgICAgICAgbGV0IGZ1bGx5UmVkdW5kYW50ID0ga2V5ZWRGb3JtYXQgIT0gbnVsbCAmJiBrZXllZEZvcm1hdCA9PSBrZXllZE5leHRGb3JtYXRcbiAgICAgICAgbGV0IHkgPSB0aW1lbGluZS5nZXRZKClcblxuICAgICAgICBmb3IgKGxldCB7IHN0YXJ0LCBlbmQgfSBvZiB0aGlzLml0ZXJSYW5nZXMocmFuZ2UpKSB7XG4gICAgICAgICAgICBsZXQgeCA9IHRpbWVsaW5lLnRvWChzdGFydClcbiAgICAgICAgICAgIGxldCB4eCA9IHhcbiAgICAgICAgICAgIGxldCB5eSA9IHkgKyB0aW1lbGluZS50aWNrTGFiZWxPZmZzZXQoLTEpXG4gICAgICAgICAgICBpZiAodGhpcy5kcmF3VGljayh0aW1lbGluZSwgeCwgeSwgc3RhcnQpICYmIGZvcm1hdCkge1xuICAgICAgICAgICAgICAgIGxldCBrZXkgPSB0aGlzLmRhdGVLZXkoc3RhcnQpXG4gICAgICAgICAgICAgICAgbGV0IHRleHQgPSB0aGlzLnRvTG9jYWxlU3RyaW5nKHN0YXJ0LCBmb3JtYXQpXG4gICAgICAgICAgICAgICAgbGV0IGFsaWduID0gJ2NlbnRlcidcbiAgICAgICAgICAgICAgICBsZXQgZG93blRpY2sgPSBmYWxzZVxuICAgICAgICAgICAgICAgIGlmIChuZXh0Rm9ybWF0KSB7XG4gICAgICAgICAgICAgICAgICAgIHl5ID0geSArIHRpbWVsaW5lLnRpY2tMYWJlbE9mZnNldCgtMSwgMSlcbiAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAnbGVmdCdcbiAgICAgICAgICAgICAgICAgICAgdGltZWxpbmUuZHJhd1RpY2soeCwgNC4yKVxuICAgICAgICAgICAgICAgICAgICBsZXQgbmV4dFggPSB0aW1lbGluZS50b1goZW5kKSAtIDEwMFxuICAgICAgICAgICAgICAgICAgICBpZiAoeCA8IDAgJiYgbmV4dFggPiAtMTAwICYmICFyZWR1bmRhbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHh4ID0gTWF0aC5taW4oNCwgbmV4dFgpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB4eCAtPSAyXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZG93blRpY2sgPSB0cnVlXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKGxldmVsID4gMCkge1xuICAgICAgICAgICAgICAgICAgICB4eCA9IHggKyBhdmFpbGFibGUgLyAyXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBkb3duVGljayA9IHRydWVcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKCFmdWxseVJlZHVuZGFudCkge1xuICAgICAgICAgICAgICAgICAgICB0aW1lbGluZS5lbnN1cmVMYWJlbChrZXksIHRleHQsXG4gICAgICAgICAgICAgICAgICAgICAgICB7IHg6IHh4LCB5OiB5eSwgYWxpZ24gfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIEZvbnRJbmZvLnNtYWxsKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoZG93blRpY2spIHRpbWVsaW5lLmRyYXdUaWNrKHgsIC0xKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHRpbWVsaW5lLnZpc2libGVSYW5nZShzdGFydCwgZW5kKSkge1xuICAgICAgICAgICAgICAgIGlmIChmaXJzdCA9PSBudWxsKVxuICAgICAgICAgICAgICAgICAgICBmaXJzdCA9IHN0YXJ0XG4gICAgICAgICAgICAgICAgbGFzdCA9IGVuZFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChmaXJzdCA9PSBudWxsKVxuICAgICAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgICAgcmV0dXJuIHsgc3RhcnQ6IGZpcnN0LCBlbmQ6IGxhc3QgfVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIERlY2FkZVRpY2tzIGV4dGVuZHMgVGlja3Mge1xuXG4gICAgZ2V0IG1pbGxpc2Vjb25kcygpIHtcbiAgICAgICAgcmV0dXJuIDEwICogMzY1ICogMjQgKiA2MCAqIDYwICogMTAwMFxuICAgIH1cblxuICAgIGZvcm1hdChhdmFpbGFibGUpIHtcbiAgICAgICAgcmV0dXJuIHsgeWVhcjogJ251bWVyaWMnLCB0aW1lWm9uZTogJ1VUQycgfVxuICAgIH1cblxuICAgIHNlbGVjdGlvbih0aW1lbGluZSwgZGF0ZXMsIHNlbGVjdGVkKSB7XG4gICAgICAgIHJldHVybiBkYXRlc1xuICAgIH1cblxuICAgIGdldCBrZXkoKSB7XG4gICAgICAgIHJldHVybiAnZGVjYWRlJ1xuICAgIH1cblxuICAgIGdldCBmb3JtYXRLZXkoKSB7XG4gICAgICAgIHJldHVybiAneWVhcidcbiAgICB9XG5cbiAgICBpdGVyU3RhcnQoc3RhcnQpIHtcbiAgICAgICAgbGV0IG1vZHVsbyA9IHN0YXJ0LmdldEZ1bGxZZWFyKCkgJSAxMFxuICAgICAgICBsZXQgeWVhciA9IHN0YXJ0LmdldEZ1bGxZZWFyKCkgLSBtb2R1bG9cbiAgICAgICAgcmV0dXJuIERhdGVzLmNyZWF0ZSh5ZWFyLCAwLCAxKVxuICAgIH1cblxuICAgIG5leHQoZGVjYWRlKSB7XG4gICAgICAgIHJldHVybiBEYXRlcy5uZXh0WWVhcihkZWNhZGUsIDEwKVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIFllYXJUaWNrcyBleHRlbmRzIFRpY2tzIHtcblxuICAgIGdldCBtaWxsaXNlY29uZHMoKSB7XG4gICAgICAgIHJldHVybiAzNjUgKiAyNCAqIDYwICogNjAgKiAxMDAwXG4gICAgfVxuXG4gICAgZm9ybWF0KGF2YWlsYWJsZSkge1xuICAgICAgICBpZiAoYXZhaWxhYmxlIDwgNDQpXG4gICAgICAgICAgICByZXR1cm4geyB5ZWFyOiAnMi1kaWdpdCcsIHRpbWVab25lOiAnVVRDJyB9XG4gICAgICAgIHJldHVybiB7IHllYXI6ICdudW1lcmljJywgdGltZVpvbmU6ICdVVEMnIH1cbiAgICB9XG5cbiAgICBnZXQgbWluTGFiZWxXaWR0aCgpIHtcbiAgICAgICAgcmV0dXJuIDIyXG4gICAgfVxuXG4gICAgZ2V0IGtleSgpIHtcbiAgICAgICAgcmV0dXJuICd5ZWFyJ1xuICAgIH1cblxuICAgIGl0ZXJTdGFydChzdGFydCkge1xuICAgICAgICByZXR1cm4gRGF0ZXMuY3JlYXRlKHN0YXJ0LmdldEZ1bGxZZWFyKCksIDAsIDEpXG4gICAgfVxuXG4gICAgbmV4dCh5ZWFyKSB7XG4gICAgICAgIHJldHVybiBEYXRlcy5uZXh0WWVhcih5ZWFyKVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIE1vbnRoVGlja3MgZXh0ZW5kcyBUaWNrcyB7XG5cbiAgICBnZXQgbWlsbGlzZWNvbmRzKCkge1xuICAgICAgICByZXR1cm4gKDM2NSAvIDEyKSAqIDI0ICogNjAgKiA2MCAqIDEwMDBcbiAgICB9XG5cbiAgICBmb3JtYXQoYXZhaWxhYmxlKSB7XG4gICAgICAgIGxldCBmb3JtYXQgPSB7IG1vbnRoOiAnbmFycm93JywgdGltZVpvbmU6ICdVVEMnIH1cbiAgICAgICAgaWYgKGF2YWlsYWJsZSA+IDQ0KVxuICAgICAgICAgICAgZm9ybWF0Lm1vbnRoID0gJ3Nob3J0J1xuICAgICAgICBpZiAoYXZhaWxhYmxlID4gNjYpXG4gICAgICAgICAgICBmb3JtYXQueWVhciA9ICcyLWRpZ2l0J1xuICAgICAgICBpZiAoYXZhaWxhYmxlID4gMTAwKSB7XG4gICAgICAgICAgICBmb3JtYXQubW9udGggPSAnbG9uZydcbiAgICAgICAgICAgIGZvcm1hdC55ZWFyID0gJ251bWVyaWMnXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZvcm1hdFxuICAgIH1cblxuICAgIGdldCBtaW5MYWJlbFdpZHRoKCkge1xuICAgICAgICByZXR1cm4gMzJcbiAgICB9XG5cbiAgICBnZXQga2V5KCkge1xuICAgICAgICByZXR1cm4gJ21vbnRoJ1xuICAgIH1cblxuICAgIGRhdGVLZXkoZGF0ZSkge1xuICAgICAgICByZXR1cm4gdGhpcy5rZXkgKyBkYXRlLmdldEZ1bGxZZWFyKCkgKyBkYXRlLmdldE1vbnRoKClcbiAgICB9XG5cbiAgICBpdGVyU3RhcnQoc3RhcnQpIHtcbiAgICAgICAgcmV0dXJuIERhdGVzLmNyZWF0ZShzdGFydC5nZXRGdWxsWWVhcigpLCBzdGFydC5nZXRNb250aCgpLCAxKVxuICAgIH1cblxuICAgIG5leHQobW9udGgpIHtcbiAgICAgICAgcmV0dXJuIERhdGVzLm5leHRNb250aChtb250aClcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBEYXlUaWNrcyBleHRlbmRzIFRpY2tzIHtcblxuICAgIGdldCBtaWxsaXNlY29uZHMoKSB7XG4gICAgICAgIHJldHVybiAyNCAqIDYwICogNjAgKiAxMDAwXG4gICAgfVxuXG4gICAgZm9ybWF0KGF2YWlsYWJsZSkge1xuICAgICAgICBsZXQgZm9ybWF0ID0geyBkYXk6ICdudW1lcmljJywgdGltZVpvbmU6ICdVVEMnIH1cbiAgICAgICAgaWYgKGF2YWlsYWJsZSA+IDQ0KVxuICAgICAgICAgICAgZm9ybWF0Lm1vbnRoID0gJ3Nob3J0J1xuICAgICAgICBpZiAoYXZhaWxhYmxlID4gMTAwKSB7XG4gICAgICAgICAgICBmb3JtYXQubW9udGggPSAnbG9uZydcbiAgICAgICAgICAgIGZvcm1hdC55ZWFyID0gJzItZGlnaXQnXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGF2YWlsYWJsZSA+IDE1MCkge1xuICAgICAgICAgICAgZm9ybWF0LndlZWtkYXkgPSAnc2hvcnQnXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGF2YWlsYWJsZSA+IDIwMCkge1xuICAgICAgICAgICAgZm9ybWF0LnllYXIgPSAnbnVtZXJpYydcbiAgICAgICAgICAgIGZvcm1hdC53ZWVrZGF5ID0gJ2xvbmcnXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZvcm1hdFxuICAgIH1cblxuICAgIGdldCBrZXkoKSB7XG4gICAgICAgIHJldHVybiAnZGF5J1xuICAgIH1cblxuICAgIGRhdGVLZXkoZGF0ZSkge1xuICAgICAgICByZXR1cm4gdGhpcy5rZXkgKyBkYXRlLmdldEZ1bGxZZWFyKCkgKyBkYXRlLmdldE1vbnRoKCkgKyBkYXRlLmdldERhdGUoKVxuICAgIH1cblxuICAgIGl0ZXJTdGFydChzdGFydCkge1xuICAgICAgICByZXR1cm4gRGF0ZXMuY3JlYXRlKHN0YXJ0LmdldEZ1bGxZZWFyKCksIHN0YXJ0LmdldE1vbnRoKCksIHN0YXJ0LmdldERhdGUoKSlcbiAgICB9XG5cbiAgICBuZXh0KGRhdGUpIHtcbiAgICAgICAgcmV0dXJuIERhdGVzLm5leHREYXkoZGF0ZSlcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBIb3VyVGlja3MgZXh0ZW5kcyBUaWNrcyB7XG5cbiAgICBnZXQgbWlsbGlzZWNvbmRzKCkge1xuICAgICAgICByZXR1cm4gNjAgKiA2MCAqIDEwMDBcbiAgICB9XG5cbiAgICBmb3JtYXQoYXZhaWxhYmxlKSB7XG4gICAgICAgIGxldCBmb3JtYXQgPSB7fVxuICAgICAgICBpZiAoYXZhaWxhYmxlID4gNDQpIHtcbiAgICAgICAgICAgIGZvcm1hdC5ob3VyID0gJzItZGlnaXQnXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGF2YWlsYWJsZSA+IDEwMCkge1xuICAgICAgICAgICAgZm9ybWF0LmRheSA9ICcyLWRpZ2l0J1xuICAgICAgICAgICAgZm9ybWF0Lm1vbnRoID0gJzItZGlnaXQnXG4gICAgICAgICAgICBmb3JtYXQueWVhciA9ICcyLWRpZ2l0J1xuICAgICAgICB9XG4gICAgICAgIGlmIChhdmFpbGFibGUgPiAxNTApIHtcbiAgICAgICAgICAgIGZvcm1hdC53ZWVrZGF5ID0gJ3Nob3J0J1xuICAgICAgICAgICAgZm9ybWF0Lm1vbnRoID0gJ3Nob3J0J1xuICAgICAgICB9XG4gICAgICAgIGlmIChhdmFpbGFibGUgPiAyMDApIHtcbiAgICAgICAgICAgIGZvcm1hdC5kYXkgPSAnbnVtZXJpYydcbiAgICAgICAgICAgIGZvcm1hdC55ZWFyID0gJ251bWVyaWMnXG4gICAgICAgICAgICBmb3JtYXQubW9udGggPSAnbG9uZydcbiAgICAgICAgICAgIGZvcm1hdC53ZWVrZGF5ID0gJ2xvbmcnXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZvcm1hdFxuICAgIH1cblxuICAgIGdldCBrZXkoKSB7XG4gICAgICAgIHJldHVybiAnaG91cidcbiAgICB9XG5cbiAgICBkYXRlS2V5KGRhdGUpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMua2V5ICsgZGF0ZS5nZXRGdWxsWWVhcigpXG4gICAgICAgICAgICArIGRhdGUuZ2V0TW9udGgoKVxuICAgICAgICAgICAgKyBkYXRlLmdldERhdGUoKVxuICAgICAgICAgICAgKyBkYXRlLmdldEhvdXJzKClcbiAgICB9XG5cbiAgICBpdGVyU3RhcnQoc3RhcnQpIHtcbiAgICAgICAgcmV0dXJuIERhdGVzLmNyZWF0ZShzdGFydC5nZXRGdWxsWWVhcigpLFxuICAgICAgICAgICAgc3RhcnQuZ2V0TW9udGgoKSxcbiAgICAgICAgICAgIHN0YXJ0LmdldERhdGUoKSxcbiAgICAgICAgICAgIHN0YXJ0LmdldEhvdXJzKCkpXG4gICAgfVxuXG4gICAgbmV4dChkYXRlKSB7XG4gICAgICAgIHJldHVybiBEYXRlcy5uZXh0SG91cihkYXRlKVxuICAgIH1cblxuICAgIHRvTG9jYWxlU3RyaW5nKGRhdGUsIGZvcm1hdCkge1xuICAgICAgICByZXR1cm4gZGF0ZS50b0xvY2FsZVRpbWVTdHJpbmcoJ2RlJywgZm9ybWF0KVxuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIE1pbnV0ZVRpY2tzIGV4dGVuZHMgVGlja3Mge1xuXG4gICAgZ2V0IG1pbGxpc2Vjb25kcygpIHtcbiAgICAgICAgcmV0dXJuIDYwICogMTAwMFxuICAgIH1cblxuICAgIGZvcm1hdChhdmFpbGFibGUpIHtcbiAgICAgICAgbGV0IGZvcm1hdCA9IHsgbWludXRlOiAnbnVtZXJpYycsIHRpbWVab25lOiAnVVRDJyB9XG4gICAgICAgIGlmIChhdmFpbGFibGUgPiA0NCkge1xuICAgICAgICAgICAgZm9ybWF0LmhvdXIgPSAnbnVtZXJpYydcbiAgICAgICAgICAgIGZvcm1hdC5taW51dGUgPSAnbnVtZXJpYydcbiAgICAgICAgfVxuICAgICAgICBpZiAoYXZhaWxhYmxlID4gMTAwKSB7XG4gICAgICAgICAgICBmb3JtYXQubW9udGggPSAnc2hvcnQnXG4gICAgICAgICAgICBmb3JtYXQueWVhciA9ICcyLWRpZ2l0J1xuICAgICAgICB9XG4gICAgICAgIGlmIChhdmFpbGFibGUgPiAxNTApIHtcbiAgICAgICAgICAgIGZvcm1hdC53ZWVrZGF5ID0gJ3Nob3J0J1xuICAgICAgICB9XG4gICAgICAgIGlmIChhdmFpbGFibGUgPiAyMDApIHtcbiAgICAgICAgICAgIGZvcm1hdC55ZWFyID0gJ251bWVyaWMnXG4gICAgICAgICAgICBmb3JtYXQud2Vla2RheSA9ICdsb25nJ1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmb3JtYXRcbiAgICB9XG5cbiAgICBnZXQga2V5KCkge1xuICAgICAgICByZXR1cm4gJ21pbnV0ZSdcbiAgICB9XG5cbiAgICBkYXRlS2V5KGRhdGUpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMua2V5ICsgZGF0ZS5nZXRGdWxsWWVhcigpXG4gICAgICAgICAgICArIGRhdGUuZ2V0TW9udGgoKVxuICAgICAgICAgICAgKyBkYXRlLmdldERhdGUoKVxuICAgICAgICAgICAgKyBkYXRlLmdldEhvdXJzKClcbiAgICAgICAgICAgICsgZGF0ZS5nZXRNaW51dGVzKClcbiAgICB9XG5cbiAgICBpdGVyU3RhcnQoc3RhcnQpIHtcbiAgICAgICAgcmV0dXJuIERhdGVzLmNyZWF0ZShzdGFydC5nZXRGdWxsWWVhcigpLFxuICAgICAgICAgICAgc3RhcnQuZ2V0TW9udGgoKSxcbiAgICAgICAgICAgIHN0YXJ0LmdldERhdGUoKSxcbiAgICAgICAgICAgIHN0YXJ0LmdldEhvdXJzKCksXG4gICAgICAgICAgICBzdGFydC5nZXRNaW51dGVzKCkpXG4gICAgfVxuXG4gICAgbmV4dChkYXRlKSB7XG4gICAgICAgIHJldHVybiBEYXRlcy5uZXh0TWludXRlKGRhdGUpXG4gICAgfVxuXG4gICAgdG9Mb2NhbGVTdHJpbmcoZGF0ZSwgZm9ybWF0KSB7XG4gICAgICAgIHJldHVybiBkYXRlLnRvTG9jYWxlVGltZVN0cmluZygnZGUnLCBmb3JtYXQpXG4gICAgfVxufVxuXG5leHBvcnQgY2xhc3MgVGltZVRpY2tzIHtcblxuICAgIGNvbnN0cnVjdG9yKC4uLnRpY2tzKSB7XG4gICAgICAgIHRoaXMudGlja3MgPSB0aWNrc1xuICAgIH1cblxuICAgIHNlbGVjdGVkUmFuZ2UodGltZWxpbmUpIHtcbiAgICAgICAgbGV0IGluZm8gPSB7IHN0YXJ0OiB0aW1lbGluZS5zdGFydCwgZW5kOiB0aW1lbGluZS5lbmQsIHVuaXRzOiAwIH1cbiAgICAgICAgZm9yIChsZXQgdGlja3Mgb2YgdGhpcy50aWNrcykge1xuICAgICAgICAgICAgaW5mbyA9IHRpY2tzLnNlbGVjdGVkUmFuZ2UodGltZWxpbmUsIGluZm8pXG4gICAgICAgICAgICBpZiAoaW5mby51bml0cyA+IDEpIHtcbiAgICAgICAgICAgICAgICB0aW1lbGluZS5zZWxlY3Rpb24gPSBbaW5mby52aXNpYmxlU3RhcnQsIGluZm8udmlzaWJsZUVuZF1cbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aW1lbGluZS5zZWxlY3Rpb24gPSBbaW5mby5zdGFydCwgaW5mby5lbmRdXG4gICAgfVxuXG4gICAgZHJhdyh0aW1lbGluZSwgd2lkdGgsIGhlaWdodCkge1xuICAgICAgICBsZXQgcmFuZ2UgPSB0aW1lbGluZVxuICAgICAgICBsZXQgc3RhcnQgPSB0aW1lbGluZS50b1gocmFuZ2Uuc3RhcnQpXG4gICAgICAgIGxldCBlbmQgPSB0aW1lbGluZS50b1gocmFuZ2UuZW5kKVxuICAgICAgICBsZXQgc2l6ZSA9IGVuZCAtIHN0YXJ0XG4gICAgICAgIGxldCBkdXJhdGlvbiA9IHRpbWVsaW5lLmVuZCAtIHRpbWVsaW5lLnN0YXJ0XG4gICAgICAgIGxldCBmb3JtYXRzID0gbmV3IE1hcCgpXG4gICAgICAgIGxldCBuZXh0Rm9ybWF0cyA9IG5ldyBNYXAoKVxuICAgICAgICBsZXQgYXZhaWxhYmxlcyA9IG5ldyBNYXAoKVxuICAgICAgICBsZXQgcHJldmlvdXMgPSBudWxsXG4gICAgICAgIGxldCB2aXNpYmxlID0gW11cbiAgICAgICAgZm9yIChsZXQgdGlja3Mgb2YgdGhpcy50aWNrcykge1xuICAgICAgICAgICAgbGV0IGFtb3VudCA9IHRpY2tzLm1pbGxpc2Vjb25kcyAvIGR1cmF0aW9uXG4gICAgICAgICAgICBsZXQgYXZhaWxhYmxlID0gYW1vdW50ICogc2l6ZVxuICAgICAgICAgICAgYXZhaWxhYmxlcy5zZXQodGlja3MsIGF2YWlsYWJsZSlcbiAgICAgICAgICAgIGlmIChhdmFpbGFibGUgPCB0aWNrcy5taW5XaWR0aClcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgZm9ybWF0cy5zZXQodGlja3MsIChhdmFpbGFibGUgPCB0aWNrcy5taW5MYWJlbFdpZHRoKSA/IG51bGwgOiB0aWNrcy5mb3JtYXQoYXZhaWxhYmxlKSlcbiAgICAgICAgICAgIG5leHRGb3JtYXRzLnNldChwcmV2aW91cywgZm9ybWF0cy5nZXQodGlja3MpKVxuICAgICAgICAgICAgcHJldmlvdXMgPSB0aWNrc1xuICAgICAgICAgICAgdmlzaWJsZS5wdXNoKHRpY2tzKVxuICAgICAgICB9XG4gICAgICAgIGxldCBsZXZlbCA9IDBcbiAgICAgICAgZm9yIChsZXQgdGlja3Mgb2YgdmlzaWJsZSkge1xuICAgICAgICAgICAgaWYgKHJhbmdlID09IG51bGwpXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICByYW5nZSA9IHRpY2tzLmRyYXcodGltZWxpbmUsIHJhbmdlLCB3aWR0aCwgaGVpZ2h0LFxuICAgICAgICAgICAgICAgIGF2YWlsYWJsZXMuZ2V0KHRpY2tzKSxcbiAgICAgICAgICAgICAgICBmb3JtYXRzLmdldCh0aWNrcyksXG4gICAgICAgICAgICAgICAgbmV4dEZvcm1hdHMuZ2V0KHRpY2tzKSwgbGV2ZWwpXG4gICAgICAgICAgICBsZXZlbCArPSAxXG4gICAgICAgIH1cbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBDb2xvclJhbmdlcyB7XG5cbiAgICBjb25zdHJ1Y3RvcihsYWJlbCwgY29sb3IsIHJhbmdlcykge1xuICAgICAgICB0aGlzLmxhYmVsID0gbGFiZWxcbiAgICAgICAgdGhpcy5jb2xvciA9IGNvbG9yXG4gICAgICAgIHRoaXMucmFuZ2VzID0gcmFuZ2VzXG4gICAgfVxuXG4gICAgZHJhdyh0aW1lbGluZSwgd2lkdGgsIGhlaWdodCwgc2l6ZSA9IDEyKSB7XG4gICAgICAgIGxldCBtaW5pbXVtID0gMSAvIENhcGFiaWxpdGllcy5kZXZpY2VQaXhlbFJhdGlvXG4gICAgICAgIGxldCBoMiA9IHNpemVcbiAgICAgICAgdGltZWxpbmUubGluZVN0eWxlKHNpemUsIHRoaXMuY29sb3IpXG4gICAgICAgIGZvciAobGV0IHJhbmdlIG9mIHRoaXMucmFuZ2VzKSB7XG4gICAgICAgICAgICBpZiAocmFuZ2UudG8gPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICByYW5nZS50byA9IERhdGVzLm5leHREYXkocmFuZ2UuZnJvbSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGxldCBzdGFydCA9IHRpbWVsaW5lLnRvWChyYW5nZS5mcm9tKVxuICAgICAgICAgICAgbGV0IGVuZCA9IHRpbWVsaW5lLnRvWChyYW5nZS50bylcbiAgICAgICAgICAgIGlmIChlbmQgPCBzdGFydCArIG1pbmltdW0pIHtcbiAgICAgICAgICAgICAgICBlbmQgPSBzdGFydCArIG1pbmltdW1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRpbWVsaW5lLm1vdmVUbyhzdGFydCwgaDIpXG4gICAgICAgICAgICB0aW1lbGluZS5saW5lVG8oZW5kLCBoMilcbiAgICAgICAgfVxuICAgIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgVGltZWxpbmUgZXh0ZW5kcyBCaXRtYXBMYWJlbGVkR3JhcGhpY3Mge1xuXG4gICAgY29uc3RydWN0b3Iod2lkdGgsIGhlaWdodCwgeyB0aWNrcyA9IG51bGwsXG4gICAgICAgIGJhc2VMaW5lID0gMC41LCBzaG93UmFuZ2UgPSB0cnVlIH0gPSB7fSkge1xuICAgICAgICBzdXBlcigpXG4gICAgICAgIHRoaXMud2FudGVkV2lkdGggPSB3aWR0aFxuICAgICAgICB0aGlzLndhbnRlZEhlaWdodCA9IGhlaWdodFxuICAgICAgICB0aGlzLmV4dHJhTGVmdCA9IDBcbiAgICAgICAgdGhpcy5leHRyYVJpZ2h0ID0gMFxuICAgICAgICB0aGlzLmluc2V0ID0gNVxuICAgICAgICB0aGlzLnNob3dSYW5nZSA9IHNob3dSYW5nZVxuICAgICAgICB0aGlzLmJhc2VMaW5lID0gYmFzZUxpbmVcbiAgICAgICAgdGhpcy50aWNrSGVpZ2h0ID0gNFxuICAgICAgICB0aGlzLnpvb20gPSAxXG4gICAgICAgIHRoaXMubWluWm9vbSA9IDFcbiAgICAgICAgdGhpcy5tYXhab29tID0gMTIwMDBcbiAgICAgICAgdGhpcy5zY3JvbGwgPSAwXG4gICAgICAgIHRoaXMuZGVsdGFzID0gW11cbiAgICAgICAgdGhpcy5sYWJlbERhdGVzID0gW11cbiAgICAgICAgdGhpcy5jb2xvclJhbmdlcyA9IFtdXG4gICAgICAgIHRoaXMucmFuZ2VDb2xvcnMgPSBuZXcgQ3ljbGUoQ29sb3JzLmVtaW5lbmNlLFxuICAgICAgICAgICAgQ29sb3JzLnN0ZWVsYmx1ZSxcbiAgICAgICAgICAgIENvbG9ycy5vY2hyZSxcbiAgICAgICAgICAgIENvbG9ycy50dXJxdW9pc2UpXG4gICAgICAgIHRoaXMuY2FsbGJhY2tzID0gW11cbiAgICAgICAgdGhpcy5vblRhcENhbGxiYWNrcyA9IFtdXG4gICAgICAgIHRoaXMub25Eb3VibGVUYXBDYWxsYmFja3MgPSBbXVxuICAgICAgICB0aGlzLm9uTG9uZ1ByZXNzQ2FsbGJhY2tzID0gW11cbiAgICAgICAgdGhpcy5wcm9ncmVzcyA9IG51bGxcbiAgICAgICAgdGhpcy5zdGFydCA9IG51bGxcbiAgICAgICAgdGhpcy5lbmQgPSBudWxsXG4gICAgICAgIHRoaXMuc2VsZWN0aW9uID0gbnVsbFxuICAgICAgICB0aGlzLmF1dG9TY3JvbGwgPSBmYWxzZVxuICAgICAgICB0aGlzLmRpcmVjdGlvbiA9IC0xXG4gICAgICAgIHRoaXMudGltZXRpY2tzID0gdGlja3MgfHwgbmV3IFRpbWVUaWNrcyhuZXcgRGVjYWRlVGlja3MoKSxcbiAgICAgICAgICAgIG5ldyBZZWFyVGlja3MoKSxcbiAgICAgICAgICAgIG5ldyBNb250aFRpY2tzKCksXG4gICAgICAgICAgICBuZXcgRGF5VGlja3MoKSlcbiAgICAgICAgdGhpcy5sYWJlbFByZWZpeCA9ICdfXydcbiAgICB9XG5cbiAgICB1cGRhdGVTZWxlY3Rpb24oKSB7XG4gICAgICAgIGlmICh0aGlzLnZpc2libGVEYXRlKHRoaXMuc3RhcnQpICYmIHRoaXMudmlzaWJsZURhdGUodGhpcy5lbmQpKSB7XG4gICAgICAgICAgICB0aGlzLnNlbGVjdGlvbiA9IFt0aGlzLnN0YXJ0LCB0aGlzLmVuZF1cbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMudGltZXRpY2tzLnNlbGVjdGVkUmFuZ2UodGhpcylcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB0aGlzLnNlbGVjdGlvblxuICAgIH1cblxuICAgIGFkZENhbGxiYWNrKGNhbGxiYWNrKSB7XG4gICAgICAgIHRoaXMuY2FsbGJhY2tzLnB1c2goY2FsbGJhY2spXG4gICAgfVxuXG4gICAgYWRkVGFiQ2FsbGJhY2soY2FsbGJhY2spIHtcbiAgICAgICAgdGhpcy5vblRhcENhbGxiYWNrcy5wdXNoKGNhbGxiYWNrKVxuICAgIH1cblxuICAgIGFkZERvdWJsZVRhcENhbGxiYWNrKGNhbGxiYWNrKSB7XG4gICAgICAgIHRoaXMub25Eb3VibGVUYXBDYWxsYmFja3MucHVzaChjYWxsYmFjaylcbiAgICB9XG5cbiAgICBhZGRMb25nUHJlc3NDYWxsYmFjayhjYWxsYmFjaykge1xuICAgICAgICB0aGlzLm9uTG9uZ1ByZXNzQ2FsbGJhY2tzLnB1c2goY2FsbGJhY2spXG4gICAgfVxuXG4gICAgYWRkTGFiZWxzKGxhYmVscykge1xuICAgICAgICB0aGlzLmxhYmVsRGF0ZXMgPSBsYWJlbHNcbiAgICB9XG5cbiAgICByYW5nZShzdGFydCwgZW5kKSB7XG4gICAgICAgIHRoaXMuc3RhcnQgPSBzdGFydFxuICAgICAgICB0aGlzLmVuZCA9IGVuZFxuICAgIH1cblxuICAgIGRyYXcod2lkdGgsIGhlaWdodCkge1xuICAgICAgICB0aGlzLndhbnRlZFdpZHRoID0gd2lkdGhcbiAgICAgICAgdGhpcy53YW50ZWRIZWlnaHQgPSBoZWlnaHRcbiAgICAgICAgdGhpcy5yZWRyYXcoKVxuICAgIH1cblxuICAgIHVwZGF0ZUNvbG9yUmFuZ2VzKHcsIGgpIHtcbiAgICAgICAgbGV0IHh4ID0gdyAtIHRoaXMuaW5zZXRcbiAgICAgICAgbGV0IHNpemUgPSBGb250SW5mby5zbWFsbC5mb250U2l6ZVxuICAgICAgICBsZXQgeXkgPSBoIC0gc2l6ZVxuICAgICAgICBmb3IgKGxldCBpID0gdGhpcy5jb2xvclJhbmdlcy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgICAgICAgICAgbGV0IGNyID0gdGhpcy5jb2xvclJhbmdlc1tpXVxuICAgICAgICAgICAgbGV0IGxhYmVsID0gY3IubGFiZWxcbiAgICAgICAgICAgIGNyLmRyYXcodGhpcywgdywgaClcbiAgICAgICAgICAgIGxldCBjdXJyZW50ID0gdGhpcy5lbnN1cmVMYWJlbCgnY29sb3JSYW5nZTonICsgbGFiZWwsIGxhYmVsLFxuICAgICAgICAgICAgICAgIHsgeDogeHgsIHk6IHl5LCBhbGlnbjogJ3JpZ2h0JyB9LCBGb250SW5mby5zbWFsbClcbiAgICAgICAgICAgIGxldCByID0gY3VycmVudC5nZXRCb3VuZHMoKVxuICAgICAgICAgICAgeHggLT0gci53aWR0aCArIDE2XG5cbiAgICAgICAgICAgIHRoaXMubGluZVN0eWxlKHNpemUsIGNyLmNvbG9yKVxuICAgICAgICAgICAgdGhpcy5tb3ZlVG8oeHgsIHl5KVxuICAgICAgICAgICAgdGhpcy5saW5lVG8oeHggKyBzaXplLCB5eSlcbiAgICAgICAgICAgIHh4IC09IHNpemUgKyA0XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBkcmF3U2VsZWN0ZWRSYW1nZShzZWxlY3RlZCkge1xuICAgICAgICB0aGlzLmxpbmVTdHlsZSgyLCBhcHAudGhlbWUucHJpbWFyeUNvbG9yKVxuICAgICAgICBsZXQgc3RhcnQgPSB0aGlzLnRvWChzZWxlY3RlZFswXSlcbiAgICAgICAgbGV0IGVuZCA9IHRoaXMudG9YKHNlbGVjdGVkWzFdKVxuICAgICAgICB0aGlzLm1vdmVUbyhzdGFydCwgMClcbiAgICAgICAgdGhpcy5saW5lVG8oZW5kLCAwKVxuICAgICAgICB0aGlzLmRyYXdUaWNrKHN0YXJ0LCAtMS41LCAwKVxuICAgICAgICB0aGlzLmRyYXdUaWNrKGVuZCwgLTEuNSwgMClcbiAgICB9XG5cbiAgICByZWRyYXcoKSB7XG4gICAgICAgIHRoaXMuY2xlYXIoKVxuICAgICAgICBsZXQgaCA9IHRoaXMud2FudGVkSGVpZ2h0XG4gICAgICAgIGxldCB3ID0gdGhpcy53YW50ZWRXaWR0aFxuICAgICAgICBsZXQgeSA9IHRoaXMuZ2V0WSgpXG4gICAgICAgIHRoaXMucHJlcGFyZUxhYmVscygpXG4gICAgICAgIHRoaXMudXBkYXRlQ29sb3JSYW5nZXModywgaClcblxuICAgICAgICB0aGlzLmxpbmVTdHlsZSgyLCAweEZGRkZGRilcbiAgICAgICAgaWYgKHRoaXMuc3RhcnQgIT0gbnVsbCAmJiB0aGlzLmVuZCAhPSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLm1vdmVUbyh0aGlzLnRvWCh0aGlzLnN0YXJ0KSwgeSlcbiAgICAgICAgICAgIHRoaXMubGluZVRvKHRoaXMudG9YKHRoaXMuZW5kKSwgeSlcbiAgICAgICAgICAgIHRoaXMudXBkYXRlVGlja3NBbmRMYWJlbHModywgaClcbiAgICAgICAgICAgIHRoaXMudXBkYXRlU2VsZWN0aW9uKClcbiAgICAgICAgICAgIGxldCBzZWxlY3RlZCA9IHRoaXMuc2VsZWN0aW9uXG4gICAgICAgICAgICBpZiAoc2VsZWN0ZWRbMF0gIT0gdGhpcy5zdGFydCAmJiBzZWxlY3RlZFsxXSAhPSB0aGlzLmVuZCkge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLnNob3dSYW5nZSlcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5kcmF3U2VsZWN0ZWRSYW1nZShzZWxlY3RlZClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGZvciAobGV0IGNhbGxiYWNrIG9mIHRoaXMuY2FsbGJhY2tzKSB7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2sodGhpcy5zY3JvbGwsIHRoaXMuem9vbSwgdGhpcy5zZWxlY3Rpb24pXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0aGlzLm1vdmVUbyh0aGlzLmluc2V0LCB5KVxuICAgICAgICAgICAgdGhpcy5saW5lVG8odyAtIHRoaXMuaW5zZXQsIHkpXG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5wcm9ncmVzcyAhPSBudWxsICYmIHRoaXMucHJvZ3Jlc3MgPCAxKSB7XG4gICAgICAgICAgICB0aGlzLmxpbmVTdHlsZSgyLCAweENDQ0NGRilcbiAgICAgICAgICAgIHRoaXMubW92ZVRvKHRoaXMuaW5zZXQsIHkpXG4gICAgICAgICAgICB0aGlzLmxpbmVUbygodyAtIHRoaXMuaW5zZXQpICogdGhpcy5wcm9ncmVzcywgeSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHRvdGFsV2lkdGgoYm91bmRlZCA9IGZhbHNlKSB7XG4gICAgICAgIGxldCB3ID0gdGhpcy53YW50ZWRXaWR0aCAtICgyICogdGhpcy5pbnNldClcbiAgICAgICAgcmV0dXJuIHcgKiB0aGlzLnZhbGlkWm9vbSh0aGlzLnpvb20sIGJvdW5kZWQpXG4gICAgfVxuXG4gICAgdmFsaWRab29tKHpvb20sIGJvdW5kZWQgPSB0cnVlKSB7XG4gICAgICAgIGxldCBvdmVyc2hvb3QgPSAoYm91bmRlZCkgPyAxLjAgOiAyLjBcbiAgICAgICAgem9vbSA9IE1hdGgubWF4KHpvb20sIHRoaXMubWluWm9vbSAvIG92ZXJzaG9vdClcbiAgICAgICAgem9vbSA9IE1hdGgubWluKHpvb20sIHRoaXMubWF4Wm9vbSAqIG92ZXJzaG9vdClcbiAgICAgICAgcmV0dXJuIHpvb21cbiAgICB9XG5cbiAgICBnZXRZKCkge1xuICAgICAgICByZXR1cm4gdGhpcy53YW50ZWRIZWlnaHQgKiB0aGlzLmJhc2VMaW5lXG4gICAgfVxuXG4gICAgdG9YKGRhdGUpIHtcbiAgICAgICAgbGV0IHRvdGFsID0gdGhpcy5lbmQgLSB0aGlzLnN0YXJ0XG4gICAgICAgIGxldCBvZmZzZXQgPSB0aGlzLmluc2V0ICsgdGhpcy5zY3JvbGxcbiAgICAgICAgbGV0IHdpZHRoID0gdGhpcy50b3RhbFdpZHRoKClcbiAgICAgICAgbGV0IGRlbHRhID0gZGF0ZSAtIHRoaXMuc3RhcnRcbiAgICAgICAgbGV0IHJhdGlvID0gZGVsdGEgLyB0b3RhbFxuICAgICAgICByZXR1cm4gb2Zmc2V0ICsgcmF0aW8gKiB3aWR0aFxuICAgIH1cblxuICAgIGZyb21YKHZhbHVlKSB7XG4gICAgICAgIGxldCB0b3RhbCA9IHRoaXMuZW5kIC0gdGhpcy5zdGFydFxuICAgICAgICBsZXQgb2Zmc2V0ID0gdGhpcy5pbnNldCArIHRoaXMuc2Nyb2xsXG4gICAgICAgIGxldCB3aWR0aCA9IHRoaXMudG90YWxXaWR0aCgpXG4gICAgICAgIGxldCByYXRpbyA9ICh2YWx1ZSAtIG9mZnNldCkgLyB3aWR0aFxuICAgICAgICBsZXQgdGltZSA9IHRoaXMuc3RhcnQuZ2V0VGltZSgpICsgdG90YWwgKiByYXRpb1xuICAgICAgICBsZXQgZGF0ZSA9IG5ldyBEYXRlKHRpbWUpXG4gICAgICAgIHJldHVybiBkYXRlXG4gICAgfVxuXG4gICAgZHJhd1RpY2soeCwgZGlyZWN0aW9uID0gMSwgeSA9IG51bGwpIHtcbiAgICAgICAgaWYgKHkgPT0gbnVsbCkge1xuICAgICAgICAgICAgeSA9IHRoaXMuZ2V0WSgpXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5tb3ZlVG8oeCwgeSlcbiAgICAgICAgdGhpcy5saW5lVG8oeCwgeSAtICh0aGlzLnRpY2tIZWlnaHQgKiBkaXJlY3Rpb24gKiB0aGlzLmRpcmVjdGlvbikpXG4gICAgfVxuXG4gICAgcHJlcGFyZUxhYmVscygpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IG9mIHRoaXMubGFiZWxzLmtleXMoKSkge1xuICAgICAgICAgICAgaWYgKCFrZXkuc3RhcnRzV2l0aCh0aGlzLmxhYmVsUHJlZml4KSlcbiAgICAgICAgICAgICAgICB0aGlzLmxhYmVscy5nZXQoa2V5KS52aXNpYmxlID0gZmFsc2VcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHVwZGF0ZVRpY2tzQW5kTGFiZWxzKHdpZHRoLCBoZWlnaHQpIHtcbiAgICAgICAgdGhpcy5kcmF3VGljayh0aGlzLnRvWCh0aGlzLnN0YXJ0KSlcbiAgICAgICAgdGhpcy5kcmF3VGljayh0aGlzLnRvWCh0aGlzLmVuZCkpXG4gICAgICAgIHRoaXMudGltZXRpY2tzLmRyYXcodGhpcywgd2lkdGgsIGhlaWdodClcbiAgICAgICAgdGhpcy51cGRhdGVMYWJlbHMod2lkdGgsIGhlaWdodClcbiAgICB9XG5cbiAgICB2aXNpYmxlRGF0ZShkYXRlLCBvZmZzZXQgPSAwKSB7XG4gICAgICAgIGlmIChkYXRlID49IHRoaXMuc3RhcnQgJiYgZGF0ZSA8PSB0aGlzLmVuZCkge1xuICAgICAgICAgICAgbGV0IHggPSB0aGlzLnRvWChkYXRlKSArIG9mZnNldFxuICAgICAgICAgICAgcmV0dXJuICh4ID4gMCAmJiB4IDwgdGhpcy53YW50ZWRXaWR0aClcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICB2aXNpYmxlUmFuZ2Uoc3RhcnQsIGVuZCkge1xuICAgICAgICBsZXQgeCA9IHRoaXMudG9YKHN0YXJ0KVxuICAgICAgICBpZiAoeCA+IHRoaXMud2FudGVkV2lkdGgpXG4gICAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgeCA9IHRoaXMudG9YKGVuZClcbiAgICAgICAgaWYgKHggPCAwKVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgfVxuXG4gICAgdGlja0xhYmVsT2Zmc2V0KGRpcmVjdGlvbiA9IDEsIGxldmVsID0gMCkge1xuICAgICAgICBsZXQgZnMgPSBGb250SW5mby5zbWFsbC5mb250U2l6ZVxuICAgICAgICBsZXQgZGggPSBmcyArIChsZXZlbCAqIChmcyArIDIpKVxuICAgICAgICByZXR1cm4gdGhpcy5kaXJlY3Rpb24gKiBkaXJlY3Rpb24gKiBkaFxuICAgIH1cblxuICAgIHVwZGF0ZUxhYmVscyh3aWR0aCwgaGVpZ2h0KSB7XG4gICAgICAgIGxldCBoMiA9IGhlaWdodCAvIDJcbiAgICAgICAgaWYgKHRoaXMubGFiZWxEYXRlcykge1xuICAgICAgICAgICAgbGV0IGxhc3QgPSBudWxsXG4gICAgICAgICAgICBsZXQgeSA9IGgyICsgdGhpcy50aWNrTGFiZWxPZmZzZXQoKVxuICAgICAgICAgICAgZm9yIChsZXQgaSA9IHRoaXMubGFiZWxEYXRlcy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgICAgICAgICAgICAgIGxldCBbbGFiZWwsIGRhdGVdID0gdGhpcy5sYWJlbERhdGVzW2ldXG4gICAgICAgICAgICAgICAgbGV0IGFsaWduID0gJ2NlbnRlcicgLy8gKGxhc3QgPT0gbnVsbCkgPyAncmlnaHQnIDogJ2xlZnQnXG4gICAgICAgICAgICAgICAgbGV0IHggPSB0aGlzLnRvWChkYXRlKVxuICAgICAgICAgICAgICAgIGxldCBjdXJyZW50ID0gdGhpcy5lbnN1cmVMYWJlbCh0aGlzLmxhYmVsUHJlZml4ICsgbGFiZWwsIGxhYmVsLFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICB4OiB4LCB5OiB5LFxuICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ25cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgRm9udEluZm8uc21hbGwpXG4gICAgICAgICAgICAgICAgbGV0IHIgPSBjdXJyZW50LmdldEJvdW5kcygpXG4gICAgICAgICAgICAgICAgY3VycmVudC52aXNpYmxlID0gIShsYXN0ICE9IG51bGwgJiYgci54ICsgci53aWR0aCA+IGxhc3QueClcbiAgICAgICAgICAgICAgICBpZiAoY3VycmVudC52aXNpYmxlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZHJhd1RpY2soeCwgLTEpXG4gICAgICAgICAgICAgICAgICAgIGxhc3QgPSByXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgbGV0IHN0YXJ0ID0gdGhpcy5zdGFydC50b0xvY2FsZURhdGVTdHJpbmcoJ2RlJywgeyB5ZWFyOiAnbnVtZXJpYycsIG1vbnRoOiAnbnVtZXJpYycsIGRheTogJ251bWVyaWMnIH0pXG4gICAgICAgICAgICBsZXQgZW5kID0gdGhpcy5lbmQudG9Mb2NhbGVEYXRlU3RyaW5nKCdkZScsIHsgeWVhcjogJ251bWVyaWMnLCBtb250aDogJ251bWVyaWMnLCBkYXk6ICdudW1lcmljJyB9KVxuICAgICAgICAgICAgdGhpcy5lbnN1cmVMYWJlbCh0aGlzLmxhYmVsUHJlZml4ICsgJ3N0YXJ0Jywgc3RhcnQsIHsgeDogdGhpcy50b1godGhpcy5zdGFydCksIHk6IGgyIH0pXG4gICAgICAgICAgICB0aGlzLmVuc3VyZUxhYmVsKHRoaXMubGFiZWxQcmVmaXggKyAnZW5kJywgZW5kLCB7IHg6IHRoaXMudG9YKHRoaXMuZW5kKSwgeTogaDIsIGFsaWduOiAncmlnaHQnIH0pXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvblpvb20oem9vbSwgYW5jaG9yKSB7XG4gICAgICAgIGxldCBkYXRlID0gdGhpcy5mcm9tWChhbmNob3IueClcbiAgICAgICAgbGV0IG5ld1pvb20gPSB0aGlzLnZhbGlkWm9vbSh0aGlzLnpvb20gKiB6b29tLCBmYWxzZSlcbiAgICAgICAgdGhpcy56b29tID0gbmV3Wm9vbVxuICAgICAgICBsZXQgbmV3WCA9IHRoaXMudG9YKGRhdGUpXG4gICAgICAgIHRoaXMuc2Nyb2xsICs9IGFuY2hvci54IC0gbmV3WFxuICAgIH1cblxuICAgIG9uU3RhcnQoZXZlbnQsIGludGVyYWN0aW9uKSB7XG4gICAgICAgIHRoaXMua2lsbFR3ZWVucygpXG4gICAgICAgIHRoaXMuZGVsdGFzID0gW11cbiAgICAgICAgdGhpcy52YWxpZFNjcm9sbCgpXG4gICAgICAgIFRocm93UHJvcHNQbHVnaW4udHJhY2sodGhpcywgJ2RlbHRhJylcbiAgICB9XG5cbiAgICBvbk1vdmUoZXZlbnQsIGludGVyYWN0aW9uKSB7XG4gICAgICAgIGxldCBkZWx0YSA9IGludGVyYWN0aW9uLmRlbHRhKClcbiAgICAgICAgdGhpcy5zY3JvbGwgKz0gZGVsdGEueFxuICAgICAgICB3aGlsZSAodGhpcy5kZWx0YXMubGVuZ3RoID4gMTApIHtcbiAgICAgICAgICAgIHRoaXMuZGVsdGFzLnBvcCgwKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuZGVsdGFzLnB1c2goZGVsdGEueClcbiAgICAgICAgaWYgKGludGVyYWN0aW9uLmN1cnJlbnQuc2l6ZSA+IDEpIHtcbiAgICAgICAgICAgIHRoaXMub25ab29tKGRlbHRhLnpvb20sIGRlbHRhLmFib3V0KVxuICAgICAgICB9XG4gICAgICAgIHRoaXMucmVkcmF3KClcbiAgICB9XG5cbiAgICBvbkVuZChldmVudCwgaW50ZXJhY3Rpb24pIHtcbiAgICAgICAgbGV0IHZlbCA9IFRocm93UHJvcHNQbHVnaW4uZ2V0VmVsb2NpdHkodGhpcywgJ2RlbHRhJylcbiAgICAgICAgVGhyb3dQcm9wc1BsdWdpbi51bnRyYWNrKHRoaXMpXG5cbiAgICAgICAgdGhpcy5raWxsVHdlZW5zKClcbiAgICAgICAgdGhpcy5yZWRyYXcoKVxuICAgICAgICBsZXQgZGVsdGEgPSAwXG4gICAgICAgIGZvciAobGV0IGQgb2YgdGhpcy5kZWx0YXMpIHtcbiAgICAgICAgICAgIGRlbHRhICs9IGRcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5kZWx0YXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgZGVsdGEgLz0gdGhpcy5kZWx0YXMubGVuZ3RoXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5hdXRvU2Nyb2xsID0gdHJ1ZVxuICAgICAgICBsZXQgYW5jaG9yID0gaW50ZXJhY3Rpb24uY3VycmVudC5tZWFuKClcbiAgICAgICAgdGhpcy5rZWVwSW5Cb3VuZHMoZGVsdGEsIGFuY2hvcilcblxuICAgICAgICBmb3IobGV0IGtleSBvZiBpbnRlcmFjdGlvbi5lbmRlZC5rZXlzKCkpIHtcbiAgICAgICAgICAgIGlmIChpbnRlcmFjdGlvbi5pc0RvdWJsZVRhcChrZXkpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vbkRvdWJsZVRhcChldmVudCwgaW50ZXJhY3Rpb24sIGtleSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2UgaWYgKGludGVyYWN0aW9uLmlzVGFwKGtleSkpIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9uVGFwKGV2ZW50LCBpbnRlcmFjdGlvbiwga2V5KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSBpZiAoaW50ZXJhY3Rpb24uaXNMb25nUHJlc3Moa2V5KSkge1xuICAgICAgICAgICAgICAgIHRoaXMub25Mb25nUHJlc3MoZXZlbnQsIGludGVyYWN0aW9uLCBrZXkpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvbkxvbmdQcmVzcyhldmVudCwgaW50ZXJhY3Rpb24sIGtleSkge1xuICAgICAgICBmb3IobGV0IGNhbGxiYWNrIG9mIHRoaXMub25Mb25nUHJlc3NDYWxsYmFja3MpIHtcbiAgICAgICAgICAgIGNhbGxiYWNrKGV2ZW50LCBpbnRlcmFjdGlvbiwga2V5KVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgb25UYXAoZXZlbnQsIGludGVyYWN0aW9uLCBrZXkpIHtcbiAgICAgICAgZm9yKGxldCBjYWxsYmFjayBvZiB0aGlzLm9uVGFwQ2FsbGJhY2tzKSB7XG4gICAgICAgICAgICBjYWxsYmFjayhldmVudCwgaW50ZXJhY3Rpb24sIGtleSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uRG91YmxlVGFwKGV2ZW50LCBpbnRlcmFjdGlvbiwga2V5KSB7XG4gICAgICAgIGZvcihsZXQgY2FsbGJhY2sgb2YgdGhpcy5vbkRvdWJsZVRhcENhbGxiYWNrcykge1xuICAgICAgICAgICAgY2FsbGJhY2soZXZlbnQsIGludGVyYWN0aW9uLCBrZXkpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBfc2Nyb2xsTWluaW11bShib3VuZGVkKSB7XG4gICAgICAgIGxldCB0b3RhbCA9IHRoaXMudG90YWxXaWR0aChib3VuZGVkKVxuICAgICAgICByZXR1cm4gLXRvdGFsICsgdGhpcy53YW50ZWRXaWR0aCAtIDIgKiB0aGlzLmluc2V0XG4gICAgfVxuXG4gICAgX3Njcm9sbE1heGltdW0oYm91bmRlZCkge1xuICAgICAgICBsZXQgdG90YWwgPSB0aGlzLnRvdGFsV2lkdGgoYm91bmRlZClcbiAgICAgICAgbGV0IGxpbWl0ID0gdGhpcy53YW50ZWRXaWR0aFxuICAgICAgICBpZiAodG90YWwgPiBsaW1pdClcbiAgICAgICAgICAgIHJldHVybiAwXG4gICAgICAgIGxldCB3ID0gbGltaXQgLSAyICogdGhpcy5pbnNldFxuICAgICAgICByZXR1cm4gKHcgLSB0b3RhbCkgLyAyXG4gICAgfVxuXG4gICAgc2Nyb2xsTWluaW11bShib3VuZGVkKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9zY3JvbGxNaW5pbXVtKGJvdW5kZWQpIC0gdGhpcy5leHRyYVJpZ2h0XG4gICAgfVxuXG4gICAgc2Nyb2xsTWF4aW11bShib3VuZGVkKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9zY3JvbGxNYXhpbXVtKGJvdW5kZWQpICsgdGhpcy5leHRyYUxlZnRcbiAgICB9XG5cbiAgICBraWxsVHdlZW5zKCkge1xuICAgICAgICBUd2VlbkxpdGUua2lsbFR3ZWVuc09mKHRoaXMpXG4gICAgICAgIHRoaXMuYXV0b1Njcm9sbCA9IGZhbHNlXG4gICAgfVxuXG5cbiAgICB2YWxpZFNjcm9sbChib3VuZGVkID0gdHJ1ZSkge1xuICAgICAgICBsZXQgbWluaW11bSA9IHRoaXMuc2Nyb2xsTWluaW11bShib3VuZGVkKVxuICAgICAgICBsZXQgbWF4aW11bSA9IHRoaXMuc2Nyb2xsTWF4aW11bShib3VuZGVkKVxuICAgICAgICBpZiAodGhpcy5zY3JvbGwgPCBtaW5pbXVtKSB7XG4gICAgICAgICAgICB0aGlzLnNjcm9sbCA9IG1pbmltdW1cbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5zY3JvbGwgPiBtYXhpbXVtKSB7XG4gICAgICAgICAgICB0aGlzLnNjcm9sbCA9IG1heGltdW1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGtlZXBJbkJvdW5kcyhkZWx0YSwgYW5jaG9yKSB7XG4gICAgICAgIGxldCBib3VuZGVkID0gdHJ1ZVxuICAgICAgICBsZXQgbWluaW11bSA9IHRoaXMuc2Nyb2xsTWluaW11bShib3VuZGVkKVxuICAgICAgICBsZXQgbWF4aW11bSA9IHRoaXMuc2Nyb2xsTWF4aW11bShib3VuZGVkKVxuICAgICAgICBsZXQgdHdlZW5zID0ge31cbiAgICAgICAgaWYgKHRoaXMuem9vbSA+IHRoaXMubWF4Wm9vbSkge1xuICAgICAgICAgICAgdHdlZW5zLnpvb20gPSB0aGlzLm1heFpvb21cbiAgICAgICAgICAgIGxldCBkYXRlID0gdGhpcy5mcm9tWChhbmNob3IueClcbiAgICAgICAgICAgIGxldCBvbGRab29tID0gdGhpcy56b29tXG4gICAgICAgICAgICB0aGlzLnpvb20gPSB0aGlzLm1heFpvb21cbiAgICAgICAgICAgIGxldCBuZXdYID0gdGhpcy50b1goZGF0ZSlcbiAgICAgICAgICAgIHR3ZWVucy5zY3JvbGwgPSB0aGlzLnNjcm9sbCArIGFuY2hvci54IC0gbmV3WFxuICAgICAgICAgICAgdGhpcy56b29tID0gb2xkWm9vbVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgaWYgKHRoaXMuem9vbSA8IHRoaXMubWluWm9vbSkge1xuICAgICAgICAgICAgICAgIHR3ZWVucy56b29tID0gdGhpcy5taW5ab29tXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAodGhpcy5zY3JvbGwgPiBtYXhpbXVtKSB7XG4gICAgICAgICAgICAgICAgdHdlZW5zLnNjcm9sbCA9IG1heGltdW1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0aGlzLnNjcm9sbCA8IG1pbmltdW0pIHtcbiAgICAgICAgICAgICAgICB0d2VlbnMuc2Nyb2xsID0gbWluaW11bVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICghaXNFbXB0eSh0d2VlbnMpKSB7XG4gICAgICAgICAgICB0d2VlbnMub25VcGRhdGUgPSAoKSA9PiB7IHRoaXMucmVkcmF3KCkgfVxuICAgICAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMsIDAuNSwgdHdlZW5zKS5kZWxheSgwLjEpXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICB0aGlzLnNjcm9sbCArPSBkZWx0YVxuICAgICAgICBkZWx0YSAqPSAwLjk4NVxuICAgICAgICB0aGlzLnJlZHJhdygpXG4gICAgICAgIGlmIChNYXRoLmFicyhkZWx0YSkgPiAxICYmIHRoaXMuYXV0b1Njcm9sbCkge1xuICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLmtlZXBJbkJvdW5kcyhkZWx0YSwgYW5jaG9yKSwgMTAwMCAvIDEwMClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uTW91c2VXaGVlbChldmVudCkge1xuICAgICAgICB0aGlzLmtpbGxUd2VlbnMoKVxuICAgICAgICBsZXQgZGlyZWN0aW9uID0gZXZlbnQuZGV0YWlsIDwgMCB8fCBldmVudC53aGVlbERlbHRhID4gMFxuICAgICAgICBsZXQgYW5jaG9yID0geyB4OiBldmVudC5jbGllbnRYLCB5OiBldmVudC5jbGllbnRZIH1cbiAgICAgICAgY29uc3Qgem9vbUZhY3RvciA9IDEuNVxuICAgICAgICB0aGlzLm9uWm9vbSgoZGlyZWN0aW9uKSA/IHpvb21GYWN0b3IgOiAxIC8gem9vbUZhY3RvciwgYW5jaG9yKVxuICAgICAgICB0aGlzLnJlZHJhdygpXG4gICAgICAgIHRoaXMua2VlcEluQm91bmRzKDAsIGFuY2hvcilcbiAgICB9XG5cbiAgICBzaG93UmFuZ2VzKHJhbmdlcywgbGFiZWwgPSBcIlVudGl0bGVkXCIsIGNvbG9yID0gbnVsbCkge1xuICAgICAgICBmb3IgKGxldCBjciBvZiB0aGlzLmNvbG9yUmFuZ2VzKSB7XG4gICAgICAgICAgICBpZiAoY3IubGFiZWwgPT0gbGFiZWwpXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgd2hpbGUgKHRoaXMuY29sb3JSYW5nZXMubGVuZ3RoID49IHRoaXMucmFuZ2VDb2xvcnMubGVuZ3RoKSB7XG4gICAgICAgICAgICB0aGlzLmNvbG9yUmFuZ2VzLnNoaWZ0KClcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmNvbG9yUmFuZ2VzLnB1c2gobmV3IENvbG9yUmFuZ2VzKGxhYmVsLCBjb2xvciwgcmFuZ2VzKSlcbiAgICAgICAgdGhpcy5yZWRyYXcoKVxuICAgIH1cbn1cblxuIiwiaW1wb3J0IFRoZW1lIGZyb20gJy4vdGhlbWUuanMnXG5pbXBvcnQgVG9vbHRpcCBmcm9tICcuL3Rvb2x0aXAuanMnXG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHRoZSBzbGlkZXIgYWN0aW9uIG9uU3RhcnQuXG4gKlxuICogQGNhbGxiYWNrIG9uU3RhcnRDYWxsYmFja1xuICogQHBhcmFtIHtvYmplY3R9IGV2ZW50IC0gVGhlIGV2ZW50IG9iamVjdC5cbiAqIEBwYXJhbSB7U2xpZGVyfSBzbGlkZXIgLSBBIHJlZmVyZW5jZSB0byB0aGUgc2xpZGVyIChhbHNvIHRoaXMgcmVmZXJzIHRvIHRoZSBzbGlkZXIpLlxuICovXG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHRoZSBzbGlkZXIgYWN0aW9uIG9uVXBkYXRlLlxuICpcbiAqIEBjYWxsYmFjayBvblVwZGF0ZUNhbGxiYWNrXG4gKiBAcGFyYW0ge29iamVjdH0gZXZlbnQgLSBUaGUgZXZlbnQgb2JqZWN0LlxuICogQHBhcmFtIHtTbGlkZXJ9IHNsaWRlciAtIEEgcmVmZXJlbmNlIHRvIHRoZSBzbGlkZXIgKGFsc28gdGhpcyByZWZlcnMgdG8gdGhlIHNsaWRlcikuXG4gKi9cblxuLyoqXG4gKiBDYWxsYmFjayBmb3IgdGhlIHNsaWRlciBhY3Rpb24gb25Db21wbGV0ZS5cbiAqXG4gKiBAY2FsbGJhY2sgb25Db21wbGV0ZUNhbGxiYWNrXG4gKiBAcGFyYW0ge29iamVjdH0gZXZlbnQgLSBUaGUgZXZlbnQgb2JqZWN0LlxuICogQHBhcmFtIHtTbGlkZXJ9IHNsaWRlciAtIEEgcmVmZXJlbmNlIHRvIHRoZSBzbGlkZXIgKGFsc28gdGhpcyByZWZlcnMgdG8gdGhlIHNsaWRlcikuXG4gKi9cblxuLyoqXG4gKiBDbGFzcyB0aGF0IHJlcHJlc2VudHMgYSBQaXhpSlMgU2xpZGVyLlxuICogXG4gKiBAZXhhbXBsZVxuICogLy8gQ3JlYXRlIHRoZSBhcHBcbiAqIGNvbnN0IGFwcCA9IG5ldyBQSVhJQXBwKHtcbiAqICAgICB2aWV3OiBjYW52YXMsXG4gKiAgICAgd2lkdGg6IDkwMCxcbiAqICAgICBoZWlnaHQ6IDI1MFxuICogfSkuc2V0dXAoKS5ydW4oKVxuICogXG4gKiAvLyBDcmVhdGUgdGhlIHNsaWRlclxuICogY29uc3Qgc2xpZGVyID0gbmV3IFNsaWRlcih7XG4gKiAgICAgeDogMTAsXG4gKiAgICAgeTogMjBcbiAqIH0pXG4gKlxuICogLy8gQWRkIHRoZSBzbGlkZXIgdG8gYSBEaXNwbGF5T2JqZWN0XG4gKiBhcHAuc2NlbmUuYWRkQ2hpbGQoc2xpZGVyKVxuICpcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgUElYSS5Db250YWluZXJcbiAqIEBzZWUge0BsaW5rIGh0dHA6Ly9waXhpanMuZG93bmxvYWQvZGV2L2RvY3MvUElYSS5Db250YWluZXIuaHRtbHxQSVhJLkNvbnRhaW5lcn1cbiAqIEBzZWUge0BsaW5rIGh0dHBzOi8vd3d3Lml3bS10dWViaW5nZW4uZGUvaXdtYnJvd3Nlci9saWIvcGl4aS9zbGlkZXIuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTbGlkZXIgZXh0ZW5kcyBQSVhJLkNvbnRhaW5lciB7XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhIFNsaWRlci5cbiAgICAgKiBcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0byBzdHlsZSBhbmQgYmVoYXZpb3VyIG9mIHRoZSBzbGlkZXIuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmlkPWF1dG8gZ2VuZXJhdGVkXSAtIFRoZSBpZCBvZiB0aGUgc2xpZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy54PTBdIC0gVGhlIHggcG9zaXRpb24gb2YgdGhlIHNsaWRlci4gQ2FuIGJlIGFsc28gc2V0IGFmdGVyIGNyZWF0aW9uIHdpdGggc2xpZGVyLnggPSAwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy55PTBdIC0gVGhlIHkgcG9zaXRpb24gb2YgdGhlIHNsaWRlci4gQ2FuIGJlIGFsc28gc2V0IGFmdGVyIGNyZWF0aW9uIHdpdGggc2xpZGVyLnkgPSAwLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfFRoZW1lfSBbb3B0cy50aGVtZT1kYXJrXSAtIFRoZSB0aGVtZSB0byB1c2UgZm9yIHRoaXMgc2xpZGVyLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIGRhcmssIGxpZ2h0LCByZWRcbiAgICAgKiAgICAgb3IgYSBUaGVtZSBvYmplY3QuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLndpZHRoPTI1MF0gLSBUaGUgd2lkdGggb2YgdGhlIHNsaWRlci5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuaGVpZ2h0PTJdIC0gVGhlIGhlaWdodCBvZiB0aGUgc2xpZGVyLlxuICAgICAqIEBwYXJhbSB7UElYSS5EaXNwbGF5T2JqZWN0fSBbb3B0cy5jb250YWluZXI9d2luZG93LmFwcHxvYmplY3RdIC0gVGhlIGNvbnRhaW5lciB3aGVyZSB0aGUgc2xpZGVyIGV2ZW50cyBzaG91bGQgYmUgYXR0YWNoZWQgdG8uXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmZpbGw9VGhlbWUuZmlsbF0gLSBUaGUgY29sb3Igb2YgdGhlIHNsaWRlciBiYWNrZ3JvdW5kIGFzIGEgaGV4IHZhbHVlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5maWxsQWxwaGE9VGhlbWUuZmlsbEFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYmFja2dyb3VuZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlPVRoZW1lLnN0cm9rZV0gLSBUaGUgY29sb3Igb2YgdGhlIGJvcmRlciBhcyBhIGhleCB2YWx1ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlV2lkdGg9VGhlbWUuc3Ryb2tlV2lkdGhdIC0gVGhlIHdpZHRoIG9mIHRoZSBib3JkZXIgaW4gcGl4ZWwuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnN0cm9rZUFscGhhPVRoZW1lLnN0cm9rZUFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5jb250cm9sRmlsbD1UaGVtZS5zdHJva2VdIC0gVGhlIGNvbG9yIG9mIHRoZSBzbGlkZXIgY29udHJvbCBiYWNrZ3JvdW5kIGFzIGEgaGV4IHZhbHVlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5jb250cm9sRmlsbEFscGhhPVRoZW1lLnN0cm9rZUFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYmFja2dyb3VuZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuY29udHJvbFN0cm9rZT1UaGVtZS5zdHJva2VdIC0gVGhlIGNvbG9yIG9mIHRoZSBib3JkZXIgYXMgYSBoZXggdmFsdWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmNvbnRyb2xTdHJva2VXaWR0aD1UaGVtZS5zdHJva2VXaWR0aCAqIDAuOF0gLSBUaGUgd2lkdGggb2YgdGhlIGJvcmRlciBpbiBwaXhlbC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuY29udHJvbFN0cm9rZUFscGhhPVRoZW1lLnN0cm9rZUFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5jb250cm9sUmFkaXVzPTE2XSAtIFRoZSByYWRpdXMgb2YgdGhlIHNsaWRlciBjb250cm9sLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuZGlzYWJsZWQ9ZmFsc2VdIC0gSXMgdGhlIHNsaWRlciBkaXNhYmxlZD8gV2hlbiBkaXNhYmxlZCwgdGhlIHNsaWRlciBoYXMgYSBsb3dlciBhbHBoYSB2YWx1ZVxuICAgICAqICAgICBhbmQgY2Fubm90IGJlIGNsaWNrZWQgKGludGVyYWN0aXZlIGlzIHNldCB0byBmYWxzZSkuXG4gICAgICogQHBhcmFtIHtvblN0YXJ0Q2FsbGJhY2t9IFtvcHRzLm9uU3RhcnRdIC0gRXhlY3V0ZWQgd2hlbiB0aGUgc2xpZGVyIGNvbnRyb2wgc3RhcnRzIHRvIG1vdmUuXG4gICAgICogQHBhcmFtIHtvblVwZGF0ZUNhbGxiYWNrfSBbb3B0cy5vblVwZGF0ZV0gLSBFeGVjdXRlZCB3aGVuIHRoZSBzbGlkZXIgY29udHJvbCBpcyBtb3ZlZC5cbiAgICAgKiBAcGFyYW0ge29uQ29tcGxldGVDYWxsYmFja30gW29wdHMub25Db21wbGV0ZV0gLSBFeGVjdXRlZCB3aGVuIHRoZSBzbGlkZXIgY29udHJvbCB3YXMgZHJvcHBlZC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ3xvYmplY3R9IFtvcHRzLnRvb2x0aXBdIC0gQSBzdHJpbmcgZm9yIHRoZSBsYWJlbCBvZiB0aGUgdG9vbHRpcCBvciBhbiBvYmplY3QgdG8gY29uZmlndXJlIHRoZSB0b29sdGlwXG4gICAgICogICAgIHRvIGRpc3BsYXkuIFxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMudmlzaWJsZT10cnVlXSAtIElzIHRoZSBzbGlkZXIgaW5pdGlhbGx5IHZpc2libGUgKHByb3BlcnR5IHZpc2libGUpP1xuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKG9wdHMgPSB7fSkge1xuXG4gICAgICAgIHN1cGVyKClcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IHRoZW1lID0gVGhlbWUuZnJvbVN0cmluZyhvcHRzLnRoZW1lKVxuICAgICAgICB0aGlzLnRoZW1lID0gdGhlbWVcblxuICAgICAgICB0aGlzLm9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBpZDogUElYSS51dGlscy51aWQoKSxcbiAgICAgICAgICAgIHg6IDAsXG4gICAgICAgICAgICB5OiAwLFxuICAgICAgICAgICAgd2lkdGg6IDI1MCxcbiAgICAgICAgICAgIGhlaWdodDogMixcbiAgICAgICAgICAgIGNvbnRhaW5lcjogbnVsbCxcbiAgICAgICAgICAgIGZpbGw6IHRoZW1lLmZpbGwsXG4gICAgICAgICAgICBmaWxsQWxwaGE6IHRoZW1lLmZpbGxBbHBoYSxcbiAgICAgICAgICAgIHN0cm9rZTogdGhlbWUuc3Ryb2tlLFxuICAgICAgICAgICAgc3Ryb2tlV2lkdGg6IHRoZW1lLnN0cm9rZVdpZHRoLFxuICAgICAgICAgICAgc3Ryb2tlQWxwaGE6IHRoZW1lLnN0cm9rZUFscGhhLFxuICAgICAgICAgICAgY29udHJvbEZpbGw6IHRoZW1lLmZpbGwsXG4gICAgICAgICAgICBjb250cm9sRmlsbEFscGhhOiAuNSxcbiAgICAgICAgICAgIGNvbnRyb2xTdHJva2U6IHRoZW1lLnByaW1hcnlDb2xvcixcbiAgICAgICAgICAgIGNvbnRyb2xTdHJva2VXaWR0aDogMixcbiAgICAgICAgICAgIGNvbnRyb2xTdHJva2VBbHBoYTogdGhlbWUuc3Ryb2tlQWxwaGEsXG4gICAgICAgICAgICBjb250cm9sUmFkaXVzOiAxNixcbiAgICAgICAgICAgIG9yaWVudGF0aW9uOiAnaG9yaXpvbnRhbCcsXG4gICAgICAgICAgICBtaW46IDAsXG4gICAgICAgICAgICBtYXg6IDEwMCxcbiAgICAgICAgICAgIHZhbHVlOiAwLFxuICAgICAgICAgICAgZGlzYWJsZWQ6IGZhbHNlLFxuICAgICAgICAgICAgb25TdGFydDogbnVsbCxcbiAgICAgICAgICAgIG9uVXBkYXRlOiBudWxsLFxuICAgICAgICAgICAgb25Db21wbGV0ZTogbnVsbCxcbiAgICAgICAgICAgIHRvb2x0aXA6IG51bGwsXG4gICAgICAgICAgICB2aXNpYmxlOiB0cnVlXG4gICAgICAgIH0sIG9wdHMpXG4gICAgICAgIFxuICAgICAgICB0aGlzLm9wdHMuY29udGFpbmVyID0gdGhpcy5vcHRzLmNvbnRhaW5lciB8fCB0aGlzXG5cbiAgICAgICAgLy8gVmFsaWRhdGlvblxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGlmICh0aGlzLm9wdHMuaGVpZ2h0ID4gdGhpcy5vcHRzLndpZHRoKSB7XG4gICAgICAgICAgICB0aGlzLm9wdHMuaGVpZ2h0ID0gdGhpcy5vcHRzLndpZHRoXG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5vcHRzLnZhbHVlIDwgdGhpcy5vcHRzLm1pbikge1xuICAgICAgICAgICAgdGhpcy5vcHRzLnZhbHVlID0gdGhpcy5vcHRzLm1pblxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0cy52YWx1ZSA+IHRoaXMub3B0cy5tYXgpIHtcbiAgICAgICAgICAgIHRoaXMub3B0cy52YWx1ZSA9IHRoaXMub3B0cy5tYXhcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFByb3BlcnRpZXNcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmlkID0gdGhpcy5vcHRzLmlkXG4gICAgICAgIHRoaXMucmFkaXVzID0gdGhpcy5vcHRzLmhlaWdodCAvIDJcblxuICAgICAgICB0aGlzLl92YWx1ZSA9IHRoaXMub3B0cy52YWx1ZVxuICAgICAgICB0aGlzLl9kaXNhYmxlZCA9IG51bGxcblxuICAgICAgICB0aGlzLnNsaWRlck9iaiA9IG51bGxcbiAgICAgICAgdGhpcy5jb250cm9sID0gbnVsbFxuICAgICAgICB0aGlzLnRvb2x0aXAgPSBudWxsXG4gICAgICAgIFxuICAgICAgICB0aGlzLnZpc2libGUgPSB0aGlzLm9wdHMudmlzaWJsZVxuXG4gICAgICAgIC8vIHNldHVwXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5zZXR1cCgpXG5cbiAgICAgICAgLy8gbGF5b3V0XG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5sYXlvdXQoKVxuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGNoaWxkcmVuIGFuZCBpbnN0YW50aWF0ZXMgZXZlcnl0aGluZy5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge1NsaWRlcn0gQSByZWZlcmVuY2UgdG8gdGhlIHNsaWRlciBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgc2V0dXAoKSB7XG5cbiAgICAgICAgLy8gQ29udGFpbmVyIGV2ZW50c1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGNvbnN0IGNvbnRhaW5lciA9IHRoaXMub3B0cy5jb250YWluZXJcblxuICAgICAgICB0aGlzLm9uKCdwb2ludGVybW92ZScsIGUgPT4ge1xuICAgICAgICAgICAgaWYgKHRoaXMuY29udHJvbC5kcmFnZ2luZykge1xuICAgICAgICAgICAgICAgIGNvbnN0IG1vdmVYID0gdGhpcy5jb250cm9sLmV2ZW50LmRhdGEuZ2V0TG9jYWxQb3NpdGlvbih0aGlzLmNvbnRyb2wucGFyZW50KS54XG4gICAgICAgICAgICAgICAgdGhpcy5fdmFsdWUgPSB0aGlzLnBpeGVsVG9WYWx1ZShtb3ZlWCAtIHRoaXMuY29udHJvbC5kZWx0YSAtIHRoaXMub3B0cy5jb250cm9sUmFkaXVzKVxuICAgICAgICAgICAgICAgIGxldCB4ID0gdGhpcy52YWx1ZVRvUGl4ZWwodGhpcy5fdmFsdWUpICsgdGhpcy5vcHRzLmNvbnRyb2xSYWRpdXNcbiAgICAgICAgICAgICAgICB0aGlzLmNvbnRyb2wueCA9IHhcblxuICAgICAgICAgICAgICAgIGlmICh0aGlzLm9wdHMub25VcGRhdGUpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vcHRzLm9uVXBkYXRlLmNhbGwodGhpcywgZSwgdGhpcylcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG5cbiAgICAgICAgaWYgKGNvbnRhaW5lciBpbnN0YW5jZW9mIEVsZW1lbnQpIHtcbiAgICAgICAgICAgIGNvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVydXAnLCBlID0+IHRoaXMub25FbmQoZSksIGZhbHNlKVxuICAgICAgICAgICAgY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJjYW5jZWwnLCBlID0+IHRoaXMub25FbmQoZSksIGZhbHNlKVxuICAgICAgICAgICAgY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJsZWF2ZScsIGUgPT4gdGhpcy5vbkVuZChlKSwgZmFsc2UpXG4gICAgICAgICAgICBjb250YWluZXIuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcm91dCcsIGUgPT4gdGhpcy5vbkVuZChlKSwgZmFsc2UpXG4gICAgICAgICAgICBjb250YWluZXIuYWRkRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsIGUgPT4gdGhpcy5vbkVuZChlKSwgZmFsc2UpXG4gICAgICAgICAgICBjb250YWluZXIuYWRkRXZlbnRMaXN0ZW5lcignbW91c2VjYW5jZWwnLCBlID0+IHRoaXMub25FbmQoZSksIGZhbHNlKVxuICAgICAgICAgICAgY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlbGVhdmUnLCBlID0+IHRoaXMub25FbmQoZSksIGZhbHNlKVxuICAgICAgICAgICAgY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlb3V0JywgZSA9PiB0aGlzLm9uRW5kKGUpLCBmYWxzZSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnRhaW5lci5pbnRlcmFjdGl2ZSA9IHRydWVcbiAgICAgICAgICAgIGNvbnRhaW5lci5vbigncG9pbnRlcnVwJywgZSA9PiB0aGlzLm9uRW5kKGUpKVxuICAgICAgICAgICAgY29udGFpbmVyLm9uKCdwb2ludGVyY2FuY2VsJywgZSA9PiB0aGlzLm9uRW5kKGUpKVxuICAgICAgICAgICAgY29udGFpbmVyLm9uKCdwb2ludGVybGVhdmUnLCBlID0+IHRoaXMub25FbmQoZSkpXG4gICAgICAgICAgICBjb250YWluZXIub24oJ3BvaW50ZXJvdXQnLCBlID0+IHRoaXMub25FbmQoZSkpXG4gICAgICAgIH1cblxuICAgICAgICAvLyBTbGlkZXJcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBsZXQgc2xpZGVyT2JqID0gbmV3IFBJWEkuR3JhcGhpY3MoKVxuICAgICAgICB0aGlzLnNsaWRlck9iaiA9IHNsaWRlck9ialxuICAgICAgICB0aGlzLmFkZENoaWxkKHNsaWRlck9iailcblxuICAgICAgICAvLyBDb250cm9sXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgbGV0IGNvbnRyb2wgPSBuZXcgUElYSS5HcmFwaGljcygpXG4gICAgICAgIGNvbnRyb2wueCA9IHRoaXMub3B0cy5jb250cm9sUmFkaXVzICsgdGhpcy52YWx1ZVRvUGl4ZWwodGhpcy5vcHRzLnZhbHVlKVxuICAgICAgICBjb250cm9sLnkgPSB0aGlzLm9wdHMuY29udHJvbFJhZGl1c1xuXG4gICAgICAgIC8vIHBvaW50ZXJkb3duIG9uIHRoZSBjb250cm9sIGZvciBkcmFnbmRyb3BcbiAgICAgICAgY29udHJvbC5vbigncG9pbnRlcmRvd24nLCBlID0+IHtcbiAgICAgICAgICAgIGNvbnRyb2wuZXZlbnQgPSBlXG4gICAgICAgICAgICBjb250cm9sLmRlbHRhID0gZS5kYXRhLmdldExvY2FsUG9zaXRpb24odGhpcy5jb250cm9sKS54XG4gICAgICAgICAgICBjb250cm9sLmRyYWdnaW5nID0gdHJ1ZVxuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAodGhpcy5vcHRzLm9uU3RhcnQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9wdHMub25TdGFydC5jYWxsKHRoaXMsIGUsIHRoaXMpXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pXG5cbiAgICAgICAgdGhpcy5jb250cm9sID0gY29udHJvbFxuXG4gICAgICAgIHRoaXMuYWRkQ2hpbGQodGhpcy5jb250cm9sKVxuICAgICAgICBcbiAgICAgICAgLy8gaW50ZXJhY3Rpb25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnNsaWRlck9iai5vbigncG9pbnRlcm92ZXInLCBlID0+IHtcbiAgICAgICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmNvbnRyb2wsIHRoaXMudGhlbWUuZmFzdCwge2FscGhhOiAuODN9KVxuICAgICAgICB9KVxuXG4gICAgICAgIHRoaXMuc2xpZGVyT2JqLm9uKCdwb2ludGVyb3V0JywgZSA9PiB7XG4gICAgICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5jb250cm9sLCB0aGlzLnRoZW1lLmZhc3QsIHthbHBoYTogMX0pXG4gICAgICAgIH0pXG5cbiAgICAgICAgdGhpcy5zbGlkZXJPYmoub24oJ3BvaW50ZXJkb3duJywgZSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNsaWRlck9iai5wb2ludGVyZG93bmVkID0gdHJ1ZVxuICAgICAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMuY29udHJvbCwgdGhpcy50aGVtZS5mYXN0LCB7YWxwaGE6IC43fSlcbiAgICAgICAgfSlcblxuICAgICAgICAvLyBDbGljayBvbiB0aGUgc2xpZGVyIGJhclxuICAgICAgICB0aGlzLnNsaWRlck9iai5vbigncG9pbnRlcnVwJywgZSA9PiB7XG4gICAgICAgICAgICBpZiAodGhpcy5zbGlkZXJPYmoucG9pbnRlcmRvd25lZCkge1xuICAgICAgICAgICAgICAgIHRoaXMuc2xpZGVyT2JqLnBvaW50ZXJkb3duZWQgPSBmYWxzZVxuICAgICAgICAgICAgICAgIGNvbnN0IHBvc2l0aW9uID0gZS5kYXRhLmdldExvY2FsUG9zaXRpb24odGhpcy5jb250cm9sLnBhcmVudClcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlID0gdGhpcy5waXhlbFRvVmFsdWUocG9zaXRpb24ueCAtIHRoaXMub3B0cy5jb250cm9sUmFkaXVzKVxuICAgICAgICAgICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmNvbnRyb2wsIHRoaXMudGhlbWUuZmFzdCwge2FscGhhOiAuODN9KVxuICAgICAgICAgICAgfVxuICAgICAgICB9KVxuXG4gICAgICAgIC8vIGRpc2FibGVkXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5kaXNhYmxlZCA9IHRoaXMub3B0cy5kaXNhYmxlZFxuICAgICAgICBcbiAgICAgICAgLy8gdG9vbHRpcFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGlmICh0aGlzLm9wdHMudG9vbHRpcCkge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiB0aGlzLm9wdHMudG9vbHRpcCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnRvb2x0aXAgPSBuZXcgVG9vbHRpcCh7XG4gICAgICAgICAgICAgICAgICAgIG9iamVjdDogdGhpcyxcbiAgICAgICAgICAgICAgICAgICAgY29udGVudDogdGhpcy5vcHRzLnRvb2x0aXBcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9wdHMudG9vbHRpcC5vYmplY3QgPSB0aGlzXG4gICAgICAgICAgICAgICAgdGhpcy50b29sdGlwID0gbmV3IFRvb2x0aXAodGhpcy5vcHRzLnRvb2x0aXApXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBTaG91bGQgYmUgY2FsbGVkIHRvIHJlZnJlc2ggdGhlIGxheW91dCBvZiB0aGUgc2xpZGVyLiBDYW4gYmUgdXNlZCBhZnRlciByZXNpemluZy5cbiAgICAgKiBcbiAgICAgKiBAcmV0dXJuIHtTbGlkZXJ9IEEgcmVmZXJlbmNlIHRvIHRoZSBzbGlkZXIgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGxheW91dCgpIHtcbiAgICAgICAgXG4gICAgICAgIC8vIHNldCBwb3NpdGlvblxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMucG9zaXRpb24uc2V0KHRoaXMub3B0cy54LCB0aGlzLm9wdHMueSlcblxuICAgICAgICAvLyBkcmF3XG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5kcmF3KClcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBEcmF3cyB0aGUgc2xpZGVyIHRvIHRoZSBjYW52YXMuXG4gICAgICogXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcmV0dXJuIHtTbGlkZXJ9IEEgcmVmZXJlbmNlIHRvIHRoZSBzbGlkZXIgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGRyYXcoKSB7XG5cbiAgICAgICAgY29uc3QgciA9IHRoaXMucmFkaXVzXG4gICAgICAgIGNvbnN0IGNyID0gdGhpcy5vcHRzLmNvbnRyb2xSYWRpdXNcbiAgICAgICAgY29uc3QgdyA9IHRoaXMub3B0cy53aWR0aFxuICAgICAgICBjb25zdCBoID0gdGhpcy5vcHRzLmhlaWdodFxuICAgICAgICBjb25zdCB4ID0gY3IgKyByXG4gICAgICAgIGNvbnN0IHkgPSBjciArIHIgLSBoXG5cbiAgICAgICAgdGhpcy5zbGlkZXJPYmouY2xlYXIoKVxuICAgICAgICB0aGlzLnNsaWRlck9iai5iZWdpbkZpbGwoMHhmZmZmZmYsIDApXG4gICAgICAgIHRoaXMuc2xpZGVyT2JqLmRyYXdSZWN0KDAsIDAsIHggKyB3ICsgY3IsIGNyICogMilcbiAgICAgICAgdGhpcy5zbGlkZXJPYmoubGluZVN0eWxlKHRoaXMub3B0cy5zdHJva2VXaWR0aCwgdGhpcy5vcHRzLnN0cm9rZSwgdGhpcy5vcHRzLnN0cm9rZUFscGhhKVxuICAgICAgICB0aGlzLnNsaWRlck9iai5iZWdpbkZpbGwodGhpcy5vcHRzLmZpbGwsIHRoaXMub3B0cy5maWxsQWxwaGEpXG4gICAgICAgIHRoaXMuc2xpZGVyT2JqLm1vdmVUbyh4LCB5KVxuICAgICAgICB0aGlzLnNsaWRlck9iai5saW5lVG8oeCArIHcsIHkpXG4gICAgICAgIHRoaXMuc2xpZGVyT2JqLmFyY1RvKHggKyB3ICsgciwgeSwgeCArIHcgKyByLCB5ICsgciwgcilcbiAgICAgICAgdGhpcy5zbGlkZXJPYmoubGluZVRvKHggKyB3ICsgciwgeSArIHIgKyAxKSAgICAgICAgICAgICAgICAgICAgIC8vIEJVR0ZJWDogSWYgbm90IHNwZWNpZmllZCwgdGhlcmUgaXMgYSBzbWFsbCBhcmVhIHdpdGhvdXQgYSBzdHJva2UuXG4gICAgICAgIHRoaXMuc2xpZGVyT2JqLmFyY1RvKHggKyB3ICsgciwgeSArIGgsIHggKyB3LCB5ICsgaCwgcilcbiAgICAgICAgdGhpcy5zbGlkZXJPYmoubGluZVRvKHgsIHkgKyBoKVxuICAgICAgICB0aGlzLnNsaWRlck9iai5hcmNUbyh4IC0gciwgeSArIGgsIHggLSByLCB5ICsgciwgcilcbiAgICAgICAgdGhpcy5zbGlkZXJPYmouYXJjVG8oeCAtIHIsIHksIHgsIHksIHIpXG4gICAgICAgIHRoaXMuc2xpZGVyT2JqLmVuZEZpbGwoKVxuXG4gICAgICAgIC8vIERyYXcgY29udHJvbFxuICAgICAgICB0aGlzLmNvbnRyb2wuY2xlYXIoKVxuICAgICAgICB0aGlzLmNvbnRyb2wubGluZVN0eWxlKHRoaXMub3B0cy5jb250cm9sU3Ryb2tlV2lkdGgsIHRoaXMub3B0cy5jb250cm9sU3Ryb2tlLCB0aGlzLm9wdHMuY29udHJvbFN0cm9rZUFscGhhKVxuICAgICAgICB0aGlzLmNvbnRyb2wuYmVnaW5GaWxsKHRoaXMub3B0cy5jb250cm9sRmlsbCwgdGhpcy5vcHRzLmNvbnRyb2xGaWxsQWxwaGEpXG4gICAgICAgIHRoaXMuY29udHJvbC5kcmF3Q2lyY2xlKDAsIDAsIGNyIC0gMSlcbiAgICAgICAgdGhpcy5jb250cm9sLmJlZ2luRmlsbCh0aGlzLm9wdHMuY29udHJvbFN0cm9rZSwgdGhpcy5vcHRzLmNvbnRyb2xTdHJva2VBbHBoYSlcbiAgICAgICAgdGhpcy5jb250cm9sLmRyYXdDaXJjbGUoMCwgMCwgY3IgLyA2KVxuICAgICAgICB0aGlzLmNvbnRyb2wuZW5kRmlsbCgpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBFeGVjdXRlZCwgd2hlbiB0aGUgc2xpZGVyIGNvbnRyb2wgbW92ZW1lbnQgZW5kZWQuXG4gICAgICogXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcmV0dXJuIHtTbGlkZXJ9IEEgcmVmZXJlbmNlIHRvIHRoZSBzbGlkZXIgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIG9uRW5kKGUpIHtcblxuICAgICAgICBpZiAodGhpcy5jb250cm9sLmRyYWdnaW5nKSB7XG4gICAgICAgICAgICB0aGlzLmNvbnRyb2wuZXZlbnQgPSBudWxsXG4gICAgICAgICAgICB0aGlzLmNvbnRyb2wuZHJhZ2dpbmcgPSBmYWxzZVxuICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5vbkNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vcHRzLm9uQ29tcGxldGUuY2FsbCh0aGlzLCBlLCB0aGlzKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGVzIHRoZSB2YWx1ZSBmb3IgYSBnaXZlbiBwaXhlbC5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB2YWx1ZSBcbiAgICAgKiBAcmV0dXJucyAge251bWJlcn0gVGhlIGNhbHVjYWx0ZWQgcGl4ZWwuXG4gICAgICovXG4gICAgdmFsdWVUb1BpeGVsKHZhbHVlKSB7XG4gICAgICAgIGlmICh2YWx1ZSA8IHRoaXMub3B0cy5taW4pIHtcbiAgICAgICAgICAgIHZhbHVlID0gdGhpcy5vcHRzLm1pblxuICAgICAgICB9IGVsc2UgaWYgKHZhbHVlID4gdGhpcy5vcHRzLm1heCkge1xuICAgICAgICAgICAgdmFsdWUgPSB0aGlzLm9wdHMubWF4XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMub3B0cy53aWR0aCAqICh2YWx1ZSAtIHRoaXMub3B0cy5taW4pIC8gKHRoaXMub3B0cy5tYXggLSB0aGlzLm9wdHMubWluKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZXMgdGhlIHBpeGVsIGZvciBhIGdpdmVuIHZhbHVlLlxuICAgICAqIFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHBpeGVsIFxuICAgICAqIEByZXR1cm5zIHtudW1iZXJ9IFRoZSBjYWx1Y2FsdGVkIHZhbHVlLlxuICAgICAqL1xuICAgIHBpeGVsVG9WYWx1ZShwaXhlbCkge1xuICAgICAgICBpZiAocGl4ZWwgPCAwKSB7XG4gICAgICAgICAgICBwaXhlbCA9IDBcbiAgICAgICAgfSBlbHNlIGlmIChwaXhlbCA+IHRoaXMub3B0cy53aWR0aCkge1xuICAgICAgICAgICAgcGl4ZWwgPSB0aGlzLm9wdHMud2lkdGhcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5vcHRzLm1pbiArICgodGhpcy5vcHRzLm1heCAtIHRoaXMub3B0cy5taW4pICogcGl4ZWwgLyB0aGlzLm9wdHMud2lkdGgpXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIEdldHMgb3Igc2V0cyB0aGUgdmFsdWUuXG4gICAgICogXG4gICAgICogQG1lbWJlciB7bnVtYmVyfVxuICAgICAqL1xuICAgIGdldCB2YWx1ZSgpIHtcbiAgICAgICAgcmV0dXJuIE1hdGgucm91bmQodGhpcy5fdmFsdWUpXG4gICAgfVxuICAgIHNldCB2YWx1ZSh2YWx1ZSkge1xuICAgICAgICBpZiAodmFsdWUgPCB0aGlzLm9wdHMubWluKSB7XG4gICAgICAgICAgICB2YWx1ZSA9IHRoaXMub3B0cy5taW5cbiAgICAgICAgfSBlbHNlIGlmICh2YWx1ZSA+IHRoaXMub3B0cy5tYXgpIHtcbiAgICAgICAgICAgIHZhbHVlID0gdGhpcy5vcHRzLm1heFxuICAgICAgICB9XG4gICAgICAgIHRoaXMuX3ZhbHVlID0gdmFsdWVcblxuICAgICAgICBjb25zdCB4ID0gdGhpcy52YWx1ZVRvUGl4ZWwodmFsdWUpICsgdGhpcy5vcHRzLmNvbnRyb2xSYWRpdXNcblxuICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5jb250cm9sLCB0aGlzLnRoZW1lLmZhc3QsIHt4fSlcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogR2V0cyBvciBzZXRzIHRoZSBkaXNhYmxlZCBzdGF0ZS4gV2hlbiBkaXNhYmxlZCwgdGhlIHNsaWRlciBjYW5ub3QgYmUgY2xpY2tlZC5cbiAgICAgKiBcbiAgICAgKiBAbWVtYmVyIHtib29sZWFufVxuICAgICAqL1xuICAgIGdldCBkaXNhYmxlZCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2Rpc2FibGVkXG4gICAgfVxuICAgIHNldCBkaXNhYmxlZCh2YWx1ZSkge1xuXG4gICAgICAgIHRoaXMuX2Rpc2FibGVkID0gdmFsdWVcbiAgICAgICAgXG4gICAgICAgIGlmICh0aGlzLl9kaXNhYmxlZCkge1xuICAgICAgICAgICAgdGhpcy5pbnRlcmFjdGl2ZSA9IGZhbHNlXG4gICAgICAgICAgICB0aGlzLnNsaWRlck9iai5pbnRlcmFjdGl2ZSA9IGZhbHNlXG4gICAgICAgICAgICB0aGlzLmNvbnRyb2wuaW50ZXJhY3RpdmUgPSBmYWxzZVxuICAgICAgICAgICAgdGhpcy5jb250cm9sLmJ1dHRvbk1vZGUgPSBmYWxzZVxuICAgICAgICAgICAgdGhpcy5hbHBoYSA9IC41XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmludGVyYWN0aXZlID0gdHJ1ZVxuICAgICAgICAgICAgdGhpcy5zbGlkZXJPYmouaW50ZXJhY3RpdmUgPSB0cnVlXG4gICAgICAgICAgICB0aGlzLmNvbnRyb2wuaW50ZXJhY3RpdmUgPSB0cnVlXG4gICAgICAgICAgICB0aGlzLmNvbnRyb2wuYnV0dG9uTW9kZSA9IHRydWVcbiAgICAgICAgICAgIHRoaXMuYWxwaGEgPSAxXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTaG93cyB0aGUgc2xpZGVyIChzZXRzIGhpcyBhbHBoYSB2YWx1ZXMgdG8gMSkuXG4gICAgICogXG4gICAgICogQHJldHVybiB7U2xpZGVyfSBBIHJlZmVyZW5jZSB0byB0aGUgc2xpZGVyIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzaG93KCkge1xuXG4gICAgICAgIHRoaXMub3B0cy5zdHJva2VBbHBoYSA9IDFcbiAgICAgICAgdGhpcy5vcHRzLmZpbGxBbHBoYSA9IDFcbiAgICAgICAgdGhpcy5vcHRzLmNvbnRyb2xTdHJva2VBbHBoYSA9IDFcbiAgICAgICAgdGhpcy5vcHRzLmNvbnRyb2xGaWxsQWxwaGEgPSAxXG5cbiAgICAgICAgdGhpcy5sYXlvdXQoKVxuXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIEhpZGVzIHRoZSBzbGlkZXIgKHNldHMgaGlzIGFscGhhIHZhbHVlcyB0byAxKS5cbiAgICAgKiBcbiAgICAgKiBAcmV0dXJuIHtTbGlkZXJ9IEEgcmVmZXJlbmNlIHRvIHRoZSBzbGlkZXIgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGhpZGUoKSB7XG5cbiAgICAgICAgdGhpcy5vcHRzLnN0cm9rZUFscGhhID0gMFxuICAgICAgICB0aGlzLm9wdHMuZmlsbEFscGhhID0gMFxuICAgICAgICB0aGlzLm9wdHMuY29udHJvbFN0cm9rZUFscGhhID0gMFxuICAgICAgICB0aGlzLm9wdHMuY29udHJvbEZpbGxBbHBoYSA9IDBcblxuICAgICAgICB0aGlzLmxheW91dCgpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG59XG4iLCJpbXBvcnQgVGhlbWUgZnJvbSAnLi90aGVtZS5qcydcbmltcG9ydCBUb29sdGlwIGZyb20gJy4vdG9vbHRpcC5qcydcblxuLyoqXG4gKiBDYWxsYmFjayBmb3IgdGhlIHN3aXRjaCBhY3Rpb24uXG4gKlxuICogQGNhbGxiYWNrIGFjdGlvbkNhbGxiYWNrXG4gKiBAcGFyYW0ge29iamVjdH0gZXZlbnQgLSBUaGUgZXZlbnQgb2JqZWN0LlxuICogQHBhcmFtIHtTd2l0Y2h9IHN3aXRjaCAtIEEgcmVmZXJlbmNlIHRvIHRoZSBzd2l0Y2ggKGFsc28gdGhpcyByZWZlcnMgdG8gdGhlIHN3aXRjaCkuXG4gKi9cblxuLyoqXG4gKiBDYWxsYmFjayBmb3IgdGhlIHN3aXRjaCBhY3Rpb24uXG4gKlxuICogQGNhbGxiYWNrIGFjdGlvbkFjdGl2ZUNhbGxiYWNrXG4gKiBAcGFyYW0ge29iamVjdH0gZXZlbnQgLSBUaGUgZXZlbnQgb2JqZWN0LlxuICogQHBhcmFtIHtTd2l0Y2h9IHN3aXRjaCAtIEEgcmVmZXJlbmNlIHRvIHRoZSBzd2l0Y2ggKGFsc28gdGhpcyByZWZlcnMgdG8gdGhlIHN3aXRjaCkuXG4gKi9cblxuLyoqXG4gKiBDYWxsYmFjayBmb3IgdGhlIHN3aXRjaCBiZWZvcmVBY3Rpb24uXG4gKlxuICogQGNhbGxiYWNrIGJlZm9yZUFjdGlvbkNhbGxiYWNrXG4gKiBAcGFyYW0ge29iamVjdH0gZXZlbnQgLSBUaGUgZXZlbnQgb2JqZWN0LlxuICogQHBhcmFtIHtTd2l0Y2h9IHN3aXRjaCAtIEEgcmVmZXJlbmNlIHRvIHRoZSBzd2l0Y2ggKGFsc28gdGhpcyByZWZlcnMgdG8gdGhlIHN3aXRjaCkuXG4gKi9cblxuLyoqXG4gKiBDYWxsYmFjayBmb3IgdGhlIHN3aXRjaCBhZnRlckFjdGlvbi5cbiAqXG4gKiBAY2FsbGJhY2sgYWZ0ZXJBY3Rpb25DYWxsYmFja1xuICogQHBhcmFtIHtvYmplY3R9IGV2ZW50IC0gVGhlIGV2ZW50IG9iamVjdC5cbiAqIEBwYXJhbSB7U3dpdGNofSBzd2l0Y2ggLSBBIHJlZmVyZW5jZSB0byB0aGUgc3dpdGNoIChhbHNvIHRoaXMgcmVmZXJzIHRvIHRoZSBzd2l0Y2gpLlxuICovXG5cbi8qKlxuICogQ2xhc3MgdGhhdCByZXByZXNlbnRzIGEgUGl4aUpTIFN3aXRjaC5cbiAqIFxuICogQGV4YW1wbGVcbiAqIC8vIENyZWF0ZSB0aGUgYXBwXG4gKiBjb25zdCBhcHAgPSBuZXcgUElYSUFwcCh7XG4gKiAgICAgdmlldzogY2FudmFzLFxuICogICAgIHdpZHRoOiA5MDAsXG4gKiAgICAgaGVpZ2h0OiAyNTBcbiAqIH0pLnNldHVwKCkucnVuKClcbiAqIFxuICogLy8gQ3JlYXRlIHRoZSBzd2l0Y2hcbiAqIGNvbnN0IHN3aXRjaDEgPSBuZXcgU3dpdGNoKHtcbiAqICAgICB4OiAxMCxcbiAqICAgICB5OiAyMFxuICogfSlcbiAqXG4gKiAvLyBBZGQgdGhlIHN3aXRjaCB0byBhIERpc3BsYXlPYmplY3RcbiAqIGFwcC5zY2VuZS5hZGRDaGlsZChzd2l0Y2gxKVxuICpcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgUElYSS5Db250YWluZXJcbiAqIEBzZWUge0BsaW5rIGh0dHA6Ly9waXhpanMuZG93bmxvYWQvZGV2L2RvY3MvUElYSS5Db250YWluZXIuaHRtbHxQSVhJLkNvbnRhaW5lcn1cbiAqIEBzZWUge0BsaW5rIGh0dHBzOi8vd3d3Lml3bS10dWViaW5nZW4uZGUvaXdtYnJvd3Nlci9saWIvcGl4aS9zd2l0Y2guaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTd2l0Y2ggZXh0ZW5kcyBQSVhJLkNvbnRhaW5lciB7XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhIFN3aXRjaC5cbiAgICAgKiBcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0byBzdHlsZSBhbmQgYmVoYXZpb3VyIG9mIHRoZSBzd2l0Y2guXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmlkPWF1dG8gZ2VuZXJhdGVkXSAtIFRoZSBpZCBvZiB0aGUgc3dpdGNoLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy54PTBdIC0gVGhlIHggcG9zaXRpb24gb2YgdGhlIHN3aXRjaC4gQ2FuIGJlIGFsc28gc2V0IGFmdGVyIGNyZWF0aW9uIHdpdGggc3dpdGNoLnggPSAwLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy55PTBdIC0gVGhlIHkgcG9zaXRpb24gb2YgdGhlIHN3aXRjaC4gQ2FuIGJlIGFsc28gc2V0IGFmdGVyIGNyZWF0aW9uIHdpdGggc3dpdGNoLnkgPSAwLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfFRoZW1lfSBbb3B0cy50aGVtZT1kYXJrXSAtIFRoZSB0aGVtZSB0byB1c2UgZm9yIHRoaXMgc3dpdGNoLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIGRhcmssIGxpZ2h0LCByZWRcbiAgICAgKiAgICAgb3IgYSBUaGVtZSBvYmplY3QuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLndpZHRoPTQ0XSAtIFRoZSB3aWR0aCBvZiB0aGUgc3dpdGNoLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5oZWlnaHQ9MjhdIC0gVGhlIGhlaWdodCBvZiB0aGUgc3dpdGNoLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5maWxsPVRoZW1lLmZpbGxdIC0gVGhlIGNvbG9yIG9mIHRoZSBzd2l0Y2ggYmFja2dyb3VuZCBhcyBhIGhleCB2YWx1ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuZmlsbEFscGhhPVRoZW1lLmZpbGxBbHBoYV0gLSBUaGUgYWxwaGEgdmFsdWUgb2YgdGhlIGJhY2tncm91bmQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmZpbGxBY3RpdmU9VGhlbWUuZmlsbEFjdGl2ZV0gLSBUaGUgY29sb3Igb2YgdGhlIHN3aXRjaCBiYWNrZ3JvdW5kIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5maWxsQWN0aXZlQWxwaGE9VGhlbWUuZmlsbEFjdGl2ZUFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYmFja2dyb3VuZCB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlPVRoZW1lLnN0cm9rZV0gLSBUaGUgY29sb3Igb2YgdGhlIGJvcmRlciBhcyBhIGhleCB2YWx1ZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlV2lkdGg9VGhlbWUuc3Ryb2tlV2lkdGhdIC0gVGhlIHdpZHRoIG9mIHRoZSBib3JkZXIgaW4gcGl4ZWwuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLnN0cm9rZUFscGhhPVRoZW1lLnN0cm9rZUFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VBY3RpdmU9VGhlbWUuc3Ryb2tlQWN0aXZlXSAtIFRoZSBjb2xvciBvZiB0aGUgYm9yZGVyIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5zdHJva2VBY3RpdmVXaWR0aD1UaGVtZS5zdHJva2VBY3RpdmVXaWR0aF0gLSBUaGUgd2lkdGggb2YgdGhlIGJvcmRlciBpbiBwaXhlbCB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuc3Ryb2tlQWN0aXZlQWxwaGE9VGhlbWUuc3Ryb2tlQWN0aXZlQWxwaGFdIC0gVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBib3JkZXIgd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmNvbnRyb2xGaWxsPVRoZW1lLnN0cm9rZV0gLSBUaGUgY29sb3Igb2YgdGhlIHN3aXRjaCBjb250cm9sIGJhY2tncm91bmQgYXMgYSBoZXggdmFsdWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmNvbnRyb2xGaWxsQWxwaGE9VGhlbWUuc3Ryb2tlQWxwaGFdIC0gVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBiYWNrZ3JvdW5kLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5jb250cm9sRmlsbEFjdGl2ZT1UaGVtZS5zdHJva2VdIC0gVGhlIGNvbG9yIG9mIHRoZSBzd2l0Y2ggY29udHJvbCBiYWNrZ3JvdW5kIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5jb250cm9sRmlsbEFjdGl2ZUFscGhhPVRoZW1lLnN0cm9rZUFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYmFja2dyb3VuZCB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuY29udHJvbFN0cm9rZT1UaGVtZS5zdHJva2VdIC0gVGhlIGNvbG9yIG9mIHRoZSBib3JkZXIgYXMgYSBoZXggdmFsdWUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmNvbnRyb2xTdHJva2VXaWR0aD1UaGVtZS5zdHJva2VXaWR0aCAqIDAuOF0gLSBUaGUgd2lkdGggb2YgdGhlIGJvcmRlciBpbiBwaXhlbC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuY29udHJvbFN0cm9rZUFscGhhPVRoZW1lLnN0cm9rZUFscGhhXSAtIFRoZSBhbHBoYSB2YWx1ZSBvZiB0aGUgYm9yZGVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5jb250cm9sU3Ryb2tlQWN0aXZlPVRoZW1lLnN0cm9rZV0gLSBUaGUgY29sb3Igb2YgdGhlIGJvcmRlciB3aGVuIGFjdGl2YXRlZC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMuY29udHJvbFN0cm9rZUFjdGl2ZVdpZHRoPVRoZW1lLnN0cm9rZUFjdGl2ZVdpZHRoICogMC44XSAtIFRoZSB3aWR0aCBvZiB0aGUgYm9yZGVyIGluIHBpeGVsIHdoZW4gYWN0aXZhdGVkLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5jb250cm9sU3Ryb2tlQWN0aXZlQWxwaGE9VGhlbWUuc3Ryb2tlQWN0aXZlQWxwaGFdIC0gVGhlIGFscGhhIHZhbHVlIG9mIHRoZSBib3JkZXIgd2hlbiBhY3RpdmF0ZWQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmR1cmF0aW9uPVRoZW1lLmZhc3RdIC0gVGhlIGR1cmF0aW9uIG9mIHRoZSBhbmltYXRpb24gd2hlbiB0aGUgc3dpdGNoIGdldHMgYWN0aXZhdGVkIGluIHNlY29uZHMuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmR1cmF0aW9uQWN0aXZlPVRoZW1lLmZhc3RdIC0gVGhlIGR1cmF0aW9uIG9mIHRoZSBhbmltYXRpb24gd2hlbiB0aGUgc3dpdGNoIGdldHMgZGVhY3RpdmF0ZWQgaW4gc2Vjb25kcy5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmRpc2FibGVkPWZhbHNlXSAtIElzIHRoZSBzd2l0Y2ggZGlzYWJsZWQ/IFdoZW4gZGlzYWJsZWQsIHRoZSBzd2l0Y2ggaGFzIGEgbG93ZXIgYWxwaGEgdmFsdWVcbiAgICAgKiAgICAgYW5kIGNhbm5vdCBiZSBjbGlja2VkIChpbnRlcmFjdGl2ZSBpcyBzZXQgdG8gZmFsc2UpLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuYWN0aXZlPWZhbHNlXSAtIElzIHRoZSBidXR0b24gaW5pdGlhbGx5IGFjdGl2ZT9cbiAgICAgKiBAcGFyYW0ge2FjdGlvbkNhbGxiYWNrfSBbb3B0cy5hY3Rpb25dIC0gRXhlY3V0ZWQgd2hlbiB0aGUgc3dpdGNoIHdhcyB0cmlnZ2VyZWQgaW4gaW5hY3RpdmUgc3RhdGUgKGJ5IHBvaW50ZXJ1cCkuXG4gICAgICogQHBhcmFtIHthY3Rpb25BY3RpdmVDYWxsYmFja30gW29wdHMuYWN0aW9uQWN0aXZlXSAtIEV4ZWN1dGVkIHdoZW4gdGhlIGJ1dHRvbiB3YXMgdHJpZ2dlcmVkIGluIGFjdGl2ZSBzdGF0ZSAoYnkgcG9pbnRlcnVwKS5cbiAgICAgKiBAcGFyYW0ge2JlZm9yZUFjdGlvbkNhbGxiYWNrfSBbb3B0cy5iZWZvcmVBY3Rpb25dIC0gRXhlY3V0ZWQgYmVmb3JlIGFuIGFjdGlvbiBpcyB0cmlnZ2VyZWQuXG4gICAgICogQHBhcmFtIHthZnRlckFjdGlvbkNhbGxiYWNrfSBbb3B0cy5hZnRlckFjdGlvbl0gLSBFeGVjdXRlZCBhZnRlciBhbiBhY3Rpb24gd2FzIHRyaWdnZXJlZC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ3xvYmplY3R9IFtvcHRzLnRvb2x0aXBdIC0gQSBzdHJpbmcgZm9yIHRoZSBsYWJlbCBvZiB0aGUgdG9vbHRpcCBvciBhbiBvYmplY3QgdG8gY29uZmlndXJlIHRoZSB0b29sdGlwXG4gICAgICogICAgIHRvIGRpc3BsYXkuIFxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMudmlzaWJsZT10cnVlXSAtIElzIHRoZSBzd2l0Y2ggaW5pdGlhbGx5IHZpc2libGUgKHByb3BlcnR5IHZpc2libGUpP1xuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKG9wdHMgPSB7fSkge1xuXG4gICAgICAgIHN1cGVyKClcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IHRoZW1lID0gVGhlbWUuZnJvbVN0cmluZyhvcHRzLnRoZW1lKVxuICAgICAgICB0aGlzLnRoZW1lID0gdGhlbWVcblxuICAgICAgICB0aGlzLm9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBpZDogUElYSS51dGlscy51aWQoKSxcbiAgICAgICAgICAgIHg6IDAsXG4gICAgICAgICAgICB5OiAwLFxuICAgICAgICAgICAgd2lkdGg6IDQ0LFxuICAgICAgICAgICAgaGVpZ2h0OiAyOCxcbiAgICAgICAgICAgIGZpbGw6IHRoZW1lLmZpbGwsXG4gICAgICAgICAgICBmaWxsQWxwaGE6IHRoZW1lLmZpbGxBbHBoYSxcbiAgICAgICAgICAgIGZpbGxBY3RpdmU6IHRoZW1lLnByaW1hcnlDb2xvcixcbiAgICAgICAgICAgIGZpbGxBY3RpdmVBbHBoYTogdGhlbWUuZmlsbEFjdGl2ZUFscGhhLFxuICAgICAgICAgICAgc3Ryb2tlOiB0aGVtZS5zdHJva2UsXG4gICAgICAgICAgICBzdHJva2VXaWR0aDogdGhlbWUuc3Ryb2tlV2lkdGgsXG4gICAgICAgICAgICBzdHJva2VBbHBoYTogdGhlbWUuc3Ryb2tlQWxwaGEsXG4gICAgICAgICAgICBzdHJva2VBY3RpdmU6IHRoZW1lLnByaW1hcnlDb2xvcixcbiAgICAgICAgICAgIHN0cm9rZUFjdGl2ZVdpZHRoOiB0aGVtZS5zdHJva2VBY3RpdmVXaWR0aCxcbiAgICAgICAgICAgIHN0cm9rZUFjdGl2ZUFscGhhOiB0aGVtZS5zdHJva2VBY3RpdmVBbHBoYSxcbiAgICAgICAgICAgIGNvbnRyb2xGaWxsOiB0aGVtZS5zdHJva2UsXG4gICAgICAgICAgICBjb250cm9sRmlsbEFscGhhOiB0aGVtZS5zdHJva2VBbHBoYSxcbiAgICAgICAgICAgIGNvbnRyb2xGaWxsQWN0aXZlOiB0aGVtZS5zdHJva2UsXG4gICAgICAgICAgICBjb250cm9sRmlsbEFjdGl2ZUFscGhhOiB0aGVtZS5zdHJva2VBbHBoYSxcbiAgICAgICAgICAgIGNvbnRyb2xTdHJva2U6IHRoZW1lLnN0cm9rZSxcbiAgICAgICAgICAgIGNvbnRyb2xTdHJva2VXaWR0aDogdGhlbWUuc3Ryb2tlV2lkdGggKiAuOCxcbiAgICAgICAgICAgIGNvbnRyb2xTdHJva2VBbHBoYTogdGhlbWUuc3Ryb2tlQWxwaGEsXG4gICAgICAgICAgICBjb250cm9sU3Ryb2tlQWN0aXZlOiB0aGVtZS5zdHJva2UsXG4gICAgICAgICAgICBjb250cm9sU3Ryb2tlQWN0aXZlV2lkdGg6IHRoZW1lLnN0cm9rZUFjdGl2ZVdpZHRoICogLjgsXG4gICAgICAgICAgICBjb250cm9sU3Ryb2tlQWN0aXZlQWxwaGE6IHRoZW1lLnN0cm9rZUFjdGl2ZUFscGhhLFxuICAgICAgICAgICAgZHVyYXRpb246IHRoZW1lLmZhc3QsXG4gICAgICAgICAgICBkdXJhdGlvbkFjdGl2ZTogdGhlbWUuZmFzdCxcbiAgICAgICAgICAgIGRpc2FibGVkOiBmYWxzZSxcbiAgICAgICAgICAgIGFjdGl2ZTogZmFsc2UsXG4gICAgICAgICAgICBhY3Rpb246IG51bGwsXG4gICAgICAgICAgICBhY3Rpb25BY3RpdmU6IG51bGwsXG4gICAgICAgICAgICBiZWZvcmVBY3Rpb246IG51bGwsXG4gICAgICAgICAgICBhZnRlckFjdGlvbjogbnVsbCxcbiAgICAgICAgICAgIHRvb2x0aXA6IG51bGwsXG4gICAgICAgICAgICB2aXNpYmxlOiB0cnVlXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgdGhpcy5vcHRzLmNvbnRyb2xSYWRpdXMgPSB0aGlzLm9wdHMuY29udHJvbFJhZGl1cyB8fCAodGhpcy5vcHRzLmhlaWdodCAvIDIpXG4gICAgICAgIHRoaXMub3B0cy5jb250cm9sUmFkaXVzQWN0aXZlID0gdGhpcy5vcHRzLmNvbnRyb2xSYWRpdXNBY3RpdmUgfHwgdGhpcy5vcHRzLmNvbnRyb2xSYWRpdXNcblxuICAgICAgICAvLyBWYWxpZGF0aW9uXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgaWYgKHRoaXMub3B0cy5oZWlnaHQgPiB0aGlzLm9wdHMud2lkdGgpIHtcbiAgICAgICAgICAgIHRoaXMub3B0cy5oZWlnaHQgPSB0aGlzLm9wdHMud2lkdGhcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFByb3BlcnRpZXNcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmlkID0gdGhpcy5vcHRzLmlkXG4gICAgICAgIHRoaXMucmFkaXVzID0gdGhpcy5vcHRzLmhlaWdodCAvIDJcblxuICAgICAgICB0aGlzLl9hY3RpdmUgPSBudWxsXG4gICAgICAgIHRoaXMuX2Rpc2FibGVkID0gbnVsbFxuXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqID0gbnVsbFxuICAgICAgICB0aGlzLmNvbnRyb2wgPSBudWxsXG4gICAgICAgIHRoaXMudG9vbHRpcCA9IG51bGxcbiAgICAgICAgXG4gICAgICAgIHRoaXMudmlzaWJsZSA9IHRoaXMub3B0cy52aXNpYmxlXG5cbiAgICAgICAgLy8gYW5pbWF0ZWRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnRlbXBBbmltYXRlZCA9IHtcbiAgICAgICAgICAgIGZpbGw6IHRoaXMub3B0cy5maWxsLFxuICAgICAgICAgICAgZmlsbEFscGhhOiB0aGlzLm9wdHMuZmlsbEFscGhhLFxuICAgICAgICAgICAgc3Ryb2tlOiB0aGlzLm9wdHMuc3Ryb2tlLFxuICAgICAgICAgICAgc3Ryb2tlV2lkdGg6IHRoaXMub3B0cy5zdHJva2VXaWR0aCxcbiAgICAgICAgICAgIHN0cm9rZUFscGhhOiB0aGlzLm9wdHMuc3Ryb2tlQWxwaGEsXG4gICAgICAgICAgICBjb250cm9sRmlsbDogdGhpcy5vcHRzLmNvbnRyb2xGaWxsLFxuICAgICAgICAgICAgY29udHJvbEZpbGxBbHBoYTogdGhpcy5vcHRzLmNvbnRyb2xGaWxsQWxwaGEsXG4gICAgICAgICAgICBjb250cm9sU3Ryb2tlOiB0aGlzLm9wdHMuY29udHJvbFN0cm9rZSxcbiAgICAgICAgICAgIGNvbnRyb2xTdHJva2VXaWR0aDogdGhpcy5vcHRzLmNvbnRyb2xTdHJva2VXaWR0aCxcbiAgICAgICAgICAgIGNvbnRyb2xTdHJva2VBbHBoYTogdGhpcy5vcHRzLmNvbnRyb2xTdHJva2VBbHBoYSxcbiAgICAgICAgICAgIGNvbnRyb2xSYWRpdXM6IHRoaXMub3B0cy5jb250cm9sUmFkaXVzXG4gICAgICAgIH1cblxuICAgICAgICAvLyBzZXR1cFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuc2V0dXAoKVxuXG4gICAgICAgIC8vIGxheW91dFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMubGF5b3V0KClcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBjaGlsZHJlbiBhbmQgaW5zdGFudGlhdGVzIGV2ZXJ5dGhpbmcuXG4gICAgICogXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcmV0dXJuIHtTd2l0Y2h9IEEgcmVmZXJlbmNlIHRvIHRoZSBzd2l0Y2ggZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHNldHVwKCkge1xuXG4gICAgICAgIC8vIFN3aXRjaFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGxldCBzd2l0Y2hPYmogPSBuZXcgUElYSS5HcmFwaGljcygpXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqID0gc3dpdGNoT2JqXG4gICAgICAgIHRoaXMuYWRkQ2hpbGQoc3dpdGNoT2JqKVxuXG4gICAgICAgIC8vIENvbnRyb2xcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnhJbmFjdGl2ZSA9IHRoaXMub3B0cy5jb250cm9sUmFkaXVzXG4gICAgICAgIHRoaXMueEFjdGl2ZSA9IHRoaXMub3B0cy53aWR0aCAtIHRoaXMub3B0cy5jb250cm9sUmFkaXVzQWN0aXZlXG4gICAgICAgIFxuICAgICAgICBsZXQgY29udHJvbCA9IG5ldyBQSVhJLkdyYXBoaWNzKClcbiAgICAgICAgY29udHJvbC54ID0gdGhpcy5vcHRzLmFjdGl2ZSA/IHRoaXMueEFjdGl2ZSA6IHRoaXMueEluYWN0aXZlXG4gICAgICAgIGNvbnRyb2wueSA9IHRoaXMub3B0cy5oZWlnaHQgLyAyXG5cbiAgICAgICAgdGhpcy5jb250cm9sID0gY29udHJvbFxuXG4gICAgICAgIHRoaXMuYWRkQ2hpbGQodGhpcy5jb250cm9sKVxuICAgICAgICBcbiAgICAgICAgLy8gaW50ZXJhY3Rpb25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnN3aXRjaE9iai5vbigncG9pbnRlcm92ZXInLCBlID0+IHtcbiAgICAgICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmNvbnRyb2wsIHRoaXMudGhlbWUuZmFzdCwge2FscGhhOiAuODN9KVxuICAgICAgICB9KVxuXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLm9uKCdwb2ludGVyb3V0JywgZSA9PiB7XG4gICAgICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5jb250cm9sLCB0aGlzLnRoZW1lLmZhc3QsIHthbHBoYTogMX0pXG4gICAgICAgIH0pXG5cbiAgICAgICAgdGhpcy5zd2l0Y2hPYmoub24oJ3BvaW50ZXJkb3duJywgZSA9PiB7XG4gICAgICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5jb250cm9sLCB0aGlzLnRoZW1lLmZhc3QsIHthbHBoYTogLjd9KVxuICAgICAgICB9KVxuXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLm9uKCdwb2ludGVydXAnLCBlID0+IHtcblxuICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5iZWZvcmVBY3Rpb24pIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9wdHMuYmVmb3JlQWN0aW9uLmNhbGwodGhpcywgZSwgdGhpcylcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhpcy5hY3RpdmUgPSAhdGhpcy5hY3RpdmVcblxuICAgICAgICAgICAgaWYgKHRoaXMuYWN0aXZlKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5hY3Rpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5vcHRzLmFjdGlvbi5jYWxsKHRoaXMsIGUsIHRoaXMpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5vcHRzLmFjdGlvbkFjdGl2ZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLm9wdHMuYWN0aW9uQWN0aXZlLmNhbGwodGhpcywgZSwgdGhpcylcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIFR3ZWVuTGl0ZS50byh0aGlzLmNvbnRyb2wsIHRoaXMudGhlbWUuZmFzdCwge2FscGhhOiAuODN9KVxuXG4gICAgICAgICAgICBpZiAodGhpcy5vcHRzLmFmdGVyQWN0aW9uKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vcHRzLmFmdGVyQWN0aW9uLmNhbGwodGhpcywgZSwgdGhpcylcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcblxuICAgICAgICAvLyBkaXNhYmxlZFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuZGlzYWJsZWQgPSB0aGlzLm9wdHMuZGlzYWJsZWRcblxuICAgICAgICAvLyBhY3RpdmVcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLmFjdGl2ZSA9IHRoaXMub3B0cy5hY3RpdmVcbiAgICAgICAgXG4gICAgICAgIC8vIHRvb2x0aXBcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLnRvb2x0aXApIHtcbiAgICAgICAgICAgIGlmICh0eXBlb2YgdGhpcy5vcHRzLnRvb2x0aXAgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgdGhpcy50b29sdGlwID0gbmV3IFRvb2x0aXAoe1xuICAgICAgICAgICAgICAgICAgICBvYmplY3Q6IHRoaXMsXG4gICAgICAgICAgICAgICAgICAgIGNvbnRlbnQ6IHRoaXMub3B0cy50b29sdGlwXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vcHRzLnRvb2x0aXAub2JqZWN0ID0gdGhpc1xuICAgICAgICAgICAgICAgIHRoaXMudG9vbHRpcCA9IG5ldyBUb29sdGlwKHRoaXMub3B0cy50b29sdGlwKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogU2hvdWxkIGJlIGNhbGxlZCB0byByZWZyZXNoIHRoZSBsYXlvdXQgb2YgdGhlIHN3aXRjaC4gQ2FuIGJlIHVzZWQgYWZ0ZXIgcmVzaXppbmcuXG4gICAgICogXG4gICAgICogQHJldHVybiB7U3dpdGNofSBBIHJlZmVyZW5jZSB0byB0aGUgc3dpdGNoIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBsYXlvdXQoKSB7XG4gICAgICAgIFxuICAgICAgICAvLyBzZXQgcG9zaXRpb25cbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICB0aGlzLnBvc2l0aW9uLnNldCh0aGlzLm9wdHMueCwgdGhpcy5vcHRzLnkpXG5cbiAgICAgICAgLy8gZHJhd1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuZHJhdygpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogRHJhd3MgdGhlIHN3aXRjaCB0byB0aGUgY2FudmFzLlxuICAgICAqIFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybiB7U3dpdGNofSBBIHJlZmVyZW5jZSB0byB0aGUgc3dpdGNoIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBkcmF3KCkge1xuXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLmNsZWFyKClcbiAgICAgICAgaWYgKHRoaXMuYWN0aXZlKSB7XG4gICAgICAgICAgICB0aGlzLnN3aXRjaE9iai5saW5lU3R5bGUodGhpcy5vcHRzLnN0cm9rZUFjdGl2ZVdpZHRoLCB0aGlzLm9wdHMuc3Ryb2tlQWN0aXZlLCB0aGlzLm9wdHMuc3Ryb2tlQWN0aXZlQWxwaGEpXG4gICAgICAgICAgICB0aGlzLnN3aXRjaE9iai5iZWdpbkZpbGwodGhpcy5vcHRzLmZpbGxBY3RpdmUsIHRoaXMub3B0cy5maWxsQWN0aXZlQWxwaGEpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnN3aXRjaE9iai5saW5lU3R5bGUodGhpcy5vcHRzLnN0cm9rZVdpZHRoLCB0aGlzLm9wdHMuc3Ryb2tlLCB0aGlzLm9wdHMuc3Ryb2tlQWxwaGEpXG4gICAgICAgICAgICB0aGlzLnN3aXRjaE9iai5iZWdpbkZpbGwodGhpcy5vcHRzLmZpbGwsIHRoaXMub3B0cy5maWxsQWxwaGEpXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5zd2l0Y2hPYmoubW92ZVRvKHRoaXMucmFkaXVzLCAwKVxuICAgICAgICB0aGlzLnN3aXRjaE9iai5saW5lVG8odGhpcy5vcHRzLndpZHRoIC0gdGhpcy5yYWRpdXMsIDApXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLmFyY1RvKHRoaXMub3B0cy53aWR0aCwgMCwgdGhpcy5vcHRzLndpZHRoLCB0aGlzLnJhZGl1cywgdGhpcy5yYWRpdXMpXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLmxpbmVUbyh0aGlzLm9wdHMud2lkdGgsIHRoaXMucmFkaXVzICsgMSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEJVR0ZJWDogSWYgbm90IHNwZWNpZmllZCwgdGhlcmUgaXMgYSBzbWFsbCBhcmVhIHdpdGhvdXQgYSBzdHJva2UuXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLmFyY1RvKHRoaXMub3B0cy53aWR0aCwgdGhpcy5vcHRzLmhlaWdodCwgdGhpcy5vcHRzLndpZHRoIC0gdGhpcy5yYWRpdXMsIHRoaXMub3B0cy5oZWlnaHQsIHRoaXMucmFkaXVzKVxuICAgICAgICB0aGlzLnN3aXRjaE9iai5saW5lVG8odGhpcy5yYWRpdXMsIHRoaXMub3B0cy5oZWlnaHQpXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLmFyY1RvKDAsIHRoaXMub3B0cy5oZWlnaHQsIDAsIHRoaXMucmFkaXVzLCB0aGlzLnJhZGl1cylcbiAgICAgICAgdGhpcy5zd2l0Y2hPYmouYXJjVG8oMCwgMCwgdGhpcy5yYWRpdXMsIDAsIHRoaXMucmFkaXVzKVxuICAgICAgICB0aGlzLnN3aXRjaE9iai5lbmRGaWxsKClcblxuICAgICAgICAvLyBEcmF3IGNvbnRyb2xcbiAgICAgICAgdGhpcy5jb250cm9sLmNsZWFyKClcbiAgICAgICAgaWYgKHRoaXMuYWN0aXZlKSB7XG4gICAgICAgICAgICB0aGlzLmNvbnRyb2wubGluZVN0eWxlKHRoaXMub3B0cy5jb250cm9sU3Ryb2tlQWN0aXZlV2lkdGgsIHRoaXMub3B0cy5jb250cm9sU3Ryb2tlQWN0aXZlLCB0aGlzLm9wdHMuY29udHJvbFN0cm9rZUFjdGl2ZUFscGhhKVxuICAgICAgICAgICAgdGhpcy5jb250cm9sLmJlZ2luRmlsbCh0aGlzLm9wdHMuY29udHJvbEZpbGxBY3RpdmUsIHRoaXMub3B0cy5jb250cm9sRmlsbEFjdGl2ZUFscGhhKVxuICAgICAgICAgICAgdGhpcy5jb250cm9sLmRyYXdDaXJjbGUoMCwgMCwgdGhpcy5vcHRzLmNvbnRyb2xSYWRpdXNBY3RpdmUgLSAxKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5jb250cm9sLmxpbmVTdHlsZSh0aGlzLm9wdHMuY29udHJvbFN0cm9rZVdpZHRoLCB0aGlzLm9wdHMuY29udHJvbFN0cm9rZSwgdGhpcy5vcHRzLmNvbnRyb2xTdHJva2VBbHBoYSlcbiAgICAgICAgICAgIHRoaXMuY29udHJvbC5iZWdpbkZpbGwodGhpcy5vcHRzLmNvbnRyb2xGaWxsLCB0aGlzLm9wdHMuY29udHJvbEZpbGxBbHBoYSlcbiAgICAgICAgICAgIHRoaXMuY29udHJvbC5kcmF3Q2lyY2xlKDAsIDAsIHRoaXMub3B0cy5jb250cm9sUmFkaXVzIC0gMSlcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmNvbnRyb2wuZW5kRmlsbCgpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogRHJhd3MgdGhlIGFuaW1hdGlvbi5cbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEByZXR1cm4ge1N3aXRjaH0gQSByZWZlcmVuY2UgdG8gdGhlIHN3aXRjaCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgZHJhd0FuaW1hdGVkKCkge1xuXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLmNsZWFyKClcbiAgICAgICAgdGhpcy5zd2l0Y2hPYmoubGluZVN0eWxlKHRoaXMudGVtcEFuaW1hdGVkLnN0cm9rZVdpZHRoLCB0aGlzLnRlbXBBbmltYXRlZC5zdHJva2UsIHRoaXMudGVtcEFuaW1hdGVkLnN0cm9rZUFscGhhKVxuICAgICAgICB0aGlzLnN3aXRjaE9iai5iZWdpbkZpbGwodGhpcy50ZW1wQW5pbWF0ZWQuZmlsbCwgdGhpcy50ZW1wQW5pbWF0ZWQuZmlsbEFscGhhKVxuICAgICAgICB0aGlzLnN3aXRjaE9iai5tb3ZlVG8odGhpcy5yYWRpdXMsIDApXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLmxpbmVUbyh0aGlzLm9wdHMud2lkdGggLSB0aGlzLnJhZGl1cywgMClcbiAgICAgICAgdGhpcy5zd2l0Y2hPYmouYXJjVG8odGhpcy5vcHRzLndpZHRoLCAwLCB0aGlzLm9wdHMud2lkdGgsIHRoaXMucmFkaXVzLCB0aGlzLnJhZGl1cylcbiAgICAgICAgdGhpcy5zd2l0Y2hPYmoubGluZVRvKHRoaXMub3B0cy53aWR0aCwgdGhpcy5yYWRpdXMgKyAxKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQlVHRklYOiBJZiBub3Qgc3BlY2lmaWVkLCB0aGVyZSBpcyBhIHNtYWxsIGFyZWEgd2l0aG91dCBhIHN0cm9rZS5cbiAgICAgICAgdGhpcy5zd2l0Y2hPYmouYXJjVG8odGhpcy5vcHRzLndpZHRoLCB0aGlzLm9wdHMuaGVpZ2h0LCB0aGlzLm9wdHMud2lkdGggLSB0aGlzLnJhZGl1cywgdGhpcy5vcHRzLmhlaWdodCwgdGhpcy5yYWRpdXMpXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLmxpbmVUbyh0aGlzLnJhZGl1cywgdGhpcy5vcHRzLmhlaWdodClcbiAgICAgICAgdGhpcy5zd2l0Y2hPYmouYXJjVG8oMCwgdGhpcy5vcHRzLmhlaWdodCwgMCwgdGhpcy5yYWRpdXMsIHRoaXMucmFkaXVzKVxuICAgICAgICB0aGlzLnN3aXRjaE9iai5hcmNUbygwLCAwLCB0aGlzLnJhZGl1cywgMCwgdGhpcy5yYWRpdXMpXG4gICAgICAgIHRoaXMuc3dpdGNoT2JqLmVuZEZpbGwoKVxuXG4gICAgICAgIHRoaXMuY29udHJvbC5jbGVhcigpXG4gICAgICAgIHRoaXMuY29udHJvbC5saW5lU3R5bGUodGhpcy50ZW1wQW5pbWF0ZWQuY29udHJvbFN0cm9rZVdpZHRoLCB0aGlzLnRlbXBBbmltYXRlZC5jb250cm9sU3Ryb2tlLCB0aGlzLnRlbXBBbmltYXRlZC5jb250cm9sU3Ryb2tlQWxwaGEpXG4gICAgICAgIHRoaXMuY29udHJvbC5iZWdpbkZpbGwodGhpcy50ZW1wQW5pbWF0ZWQuY29udHJvbEZpbGwsIHRoaXMudGVtcEFuaW1hdGVkLmNvbnRyb2xGaWxsQWxwaGEpXG4gICAgICAgIHRoaXMuY29udHJvbC5kcmF3Q2lyY2xlKDAsIDAsIHRoaXMudGVtcEFuaW1hdGVkLmNvbnRyb2xSYWRpdXMgLSAxKVxuICAgICAgICB0aGlzLmNvbnRyb2wuZW5kRmlsbCgpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogR2V0cyBvciBzZXRzIHRoZSBhY3RpdmUgc3RhdGUuXG4gICAgICogXG4gICAgICogQG1lbWJlciB7Ym9vbGVhbn1cbiAgICAgKi9cbiAgICBnZXQgYWN0aXZlKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fYWN0aXZlXG4gICAgfVxuXG4gICAgc2V0IGFjdGl2ZSh2YWx1ZSkge1xuXG4gICAgICAgIHRoaXMuX2FjdGl2ZSA9IHZhbHVlXG5cbiAgICAgICAgaWYgKHRoaXMuX2FjdGl2ZSkge1xuXG4gICAgICAgICAgICBUd2VlbkxpdGUudG8odGhpcy5jb250cm9sLCB0aGlzLm9wdHMuZHVyYXRpb24sIHt4OiB0aGlzLnhBY3RpdmV9KVxuICAgICAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMudGVtcEFuaW1hdGVkLCB0aGlzLm9wdHMuZHVyYXRpb24sIHtcbiAgICAgICAgICAgICAgICBjb2xvclByb3BzOiB7XG4gICAgICAgICAgICAgICAgICAgIGZpbGw6IHRoaXMub3B0cy5maWxsQWN0aXZlLFxuICAgICAgICAgICAgICAgICAgICBzdHJva2U6IHRoaXMub3B0cy5zdHJva2VBY3RpdmUsXG4gICAgICAgICAgICAgICAgICAgIGNvbnRyb2xGaWxsOiB0aGlzLm9wdHMuY29udHJvbEZpbGxBY3RpdmUsXG4gICAgICAgICAgICAgICAgICAgIGNvbnRyb2xTdHJva2U6IHRoaXMub3B0cy5jb250cm9sU3Ryb2tlQWN0aXZlLFxuICAgICAgICAgICAgICAgICAgICBmb3JtYXQ6ICdudW1iZXInXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmaWxsQWxwaGE6IHRoaXMub3B0cy5maWxsQWN0aXZlQWxwaGEsXG4gICAgICAgICAgICAgICAgc3Ryb2tlV2lkdGg6IHRoaXMub3B0cy5zdHJva2VBY3RpdmVXaWR0aCxcbiAgICAgICAgICAgICAgICBzdHJva2VBbHBoYTogdGhpcy5vcHRzLnN0cm9rZUFjdGl2ZUFscGhhLFxuICAgICAgICAgICAgICAgIGNvbnRyb2xGaWxsQWxwaGE6IHRoaXMub3B0cy5jb250cm9sRmlsbEFjdGl2ZUFscGhhLFxuICAgICAgICAgICAgICAgIGNvbnRyb2xTdHJva2VXaWR0aDogdGhpcy5vcHRzLmNvbnRyb2xTdHJva2VBY3RpdmVXaWR0aCxcbiAgICAgICAgICAgICAgICBjb250cm9sU3Ryb2tlQWxwaGE6IHRoaXMub3B0cy5jb250cm9sU3Ryb2tlQWN0aXZlQWxwaGEsXG4gICAgICAgICAgICAgICAgY29udHJvbFJhZGl1czogdGhpcy5vcHRzLmNvbnRyb2xSYWRpdXNBY3RpdmUsXG4gICAgICAgICAgICAgICAgb25VcGRhdGU6ICgpID0+IHRoaXMuZHJhd0FuaW1hdGVkKCksXG4gICAgICAgICAgICAgICAgb25Db21wbGV0ZTogKCkgPT4gdGhpcy5kcmF3KClcbiAgICAgICAgICAgIH0pXG5cblxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMuY29udHJvbCwgdGhpcy5vcHRzLmR1cmF0aW9uQWN0aXZlLCB7eDogdGhpcy54SW5hY3RpdmV9KVxuICAgICAgICAgICAgVHdlZW5MaXRlLnRvKHRoaXMudGVtcEFuaW1hdGVkLCB0aGlzLm9wdHMuZHVyYXRpb25BY3RpdmUsIHtcbiAgICAgICAgICAgICAgICBjb2xvclByb3BzOiB7XG4gICAgICAgICAgICAgICAgICAgIGZpbGw6IHRoaXMub3B0cy5maWxsLFxuICAgICAgICAgICAgICAgICAgICBzdHJva2U6IHRoaXMub3B0cy5zdHJva2UsXG4gICAgICAgICAgICAgICAgICAgIGNvbnRyb2xGaWxsOiB0aGlzLm9wdHMuY29udHJvbEZpbGwsXG4gICAgICAgICAgICAgICAgICAgIGNvbnRyb2xTdHJva2U6IHRoaXMub3B0cy5jb250cm9sU3Ryb2tlLFxuICAgICAgICAgICAgICAgICAgICBmb3JtYXQ6ICdudW1iZXInXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmaWxsQWxwaGE6IHRoaXMub3B0cy5maWxsQWxwaGEsXG4gICAgICAgICAgICAgICAgc3Ryb2tlV2lkdGg6IHRoaXMub3B0cy5zdHJva2VXaWR0aCxcbiAgICAgICAgICAgICAgICBzdHJva2VBbHBoYTogdGhpcy5vcHRzLnN0cm9rZUFscGhhLFxuICAgICAgICAgICAgICAgIGNvbnRyb2xGaWxsQWxwaGE6IHRoaXMub3B0cy5jb250cm9sRmlsbEFscGhhLFxuICAgICAgICAgICAgICAgIGNvbnRyb2xTdHJva2VXaWR0aDogdGhpcy5vcHRzLmNvbnRyb2xTdHJva2VXaWR0aCxcbiAgICAgICAgICAgICAgICBjb250cm9sU3Ryb2tlQWxwaGE6IHRoaXMub3B0cy5jb250cm9sU3Ryb2tlQWxwaGEsXG4gICAgICAgICAgICAgICAgY29udHJvbFJhZGl1czogdGhpcy5vcHRzLmNvbnRyb2xSYWRpdXMsXG4gICAgICAgICAgICAgICAgb25VcGRhdGU6ICgpID0+IHRoaXMuZHJhd0FuaW1hdGVkKCksXG4gICAgICAgICAgICAgICAgb25Db21wbGV0ZTogKCkgPT4gdGhpcy5kcmF3KClcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogR2V0cyBvciBzZXRzIHRoZSBkaXNhYmxlZCBzdGF0ZS4gV2hlbiBkaXNhYmxlZCwgdGhlIHN3aXRjaCBjYW5ub3QgYmUgY2xpY2tlZC5cbiAgICAgKiBcbiAgICAgKiBAbWVtYmVyIHtib29sZWFufVxuICAgICAqL1xuICAgIGdldCBkaXNhYmxlZCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2Rpc2FibGVkXG4gICAgfVxuXG4gICAgc2V0IGRpc2FibGVkKHZhbHVlKSB7XG5cbiAgICAgICAgdGhpcy5fZGlzYWJsZWQgPSB2YWx1ZVxuICAgICAgICBcbiAgICAgICAgaWYgKHRoaXMuX2Rpc2FibGVkKSB7XG4gICAgICAgICAgICB0aGlzLnN3aXRjaE9iai5pbnRlcmFjdGl2ZSA9IGZhbHNlXG4gICAgICAgICAgICB0aGlzLnN3aXRjaE9iai5idXR0b25Nb2RlID0gZmFsc2VcbiAgICAgICAgICAgIHRoaXMuc3dpdGNoT2JqLmFscGhhID0gLjVcbiAgICAgICAgICAgIHRoaXMuY29udHJvbC5hbHBoYSA9IC41XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnN3aXRjaE9iai5pbnRlcmFjdGl2ZSA9IHRydWVcbiAgICAgICAgICAgIHRoaXMuc3dpdGNoT2JqLmJ1dHRvbk1vZGUgPSB0cnVlXG4gICAgICAgICAgICB0aGlzLnN3aXRjaE9iai5hbHBoYSA9IDFcbiAgICAgICAgICAgIHRoaXMuY29udHJvbC5hbHBoYSA9IDFcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNob3dzIHRoZSBzd2l0Y2ggKHNldHMgaGlzIGFscGhhIHZhbHVlcyB0byAxKS5cbiAgICAgKiBcbiAgICAgKiBAcmV0dXJuIHtTd2l0Y2h9IEEgcmVmZXJlbmNlIHRvIHRoZSBzd2l0Y2ggZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHNob3coKSB7XG5cbiAgICAgICAgdGhpcy5vcHRzLnN0cm9rZUFscGhhID0gMVxuICAgICAgICB0aGlzLm9wdHMuc3Ryb2tlQWN0aXZlQWxwaGEgPSAxXG4gICAgICAgIHRoaXMub3B0cy5maWxsQWxwaGEgPSAxXG4gICAgICAgIHRoaXMub3B0cy5maWxsQWN0aXZlQWxwaGEgPSAxXG4gICAgICAgIHRoaXMub3B0cy5jb250cm9sU3Ryb2tlQWxwaGEgPSAxXG4gICAgICAgIHRoaXMub3B0cy5jb250cm9sU3Ryb2tlQWN0aXZlQWxwaGEgPSAxXG4gICAgICAgIHRoaXMub3B0cy5jb250cm9sRmlsbEFscGhhID0gMVxuICAgICAgICB0aGlzLm9wdHMuY29udHJvbEZpbGxBY3RpdmVBbHBoYSA9IDFcblxuICAgICAgICB0aGlzLmxheW91dCgpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogSGlkZXMgdGhlIHN3aXRjaCAoc2V0cyBoaXMgYWxwaGEgdmFsdWVzIHRvIDEpLlxuICAgICAqIFxuICAgICAqIEByZXR1cm4ge1N3aXRjaH0gQSByZWZlcmVuY2UgdG8gdGhlIHN3aXRjaCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgaGlkZSgpIHtcblxuICAgICAgICB0aGlzLm9wdHMuc3Ryb2tlQWxwaGEgPSAwXG4gICAgICAgIHRoaXMub3B0cy5zdHJva2VBY3RpdmVBbHBoYSA9IDBcbiAgICAgICAgdGhpcy5vcHRzLmZpbGxBbHBoYSA9IDBcbiAgICAgICAgdGhpcy5vcHRzLmZpbGxBY3RpdmVBbHBoYSA9IDBcbiAgICAgICAgdGhpcy5vcHRzLmNvbnRyb2xTdHJva2VBbHBoYSA9IDBcbiAgICAgICAgdGhpcy5vcHRzLmNvbnRyb2xTdHJva2VBY3RpdmVBbHBoYSA9IDBcbiAgICAgICAgdGhpcy5vcHRzLmNvbnRyb2xGaWxsQWxwaGEgPSAwXG4gICAgICAgIHRoaXMub3B0cy5jb250cm9sRmlsbEFjdGl2ZUFscGhhID0gMFxuXG4gICAgICAgIHRoaXMubGF5b3V0KClcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbn1cbiIsImltcG9ydCBUaGVtZSBmcm9tICcuL3RoZW1lLmpzJ1xuaW1wb3J0IFBvcHVwIGZyb20gJy4vcG9wdXAuanMnXG5cbi8qKlxuICogQ2xhc3MgdGhhdCByZXByZXNlbnRzIGEgUGl4aUpTIFBvcHVwTWVudS5cbiAqIFxuICogQGV4YW1wbGVcbiAqIC8vIENyZWF0ZSB0aGUgYnV0dG9uIGFuZCB0aGUgbW9kYWwgd2hlbiBjbGlja2VkXG4gKiBjb25zdCBidXR0b24gPSBuZXcgQnV0dG9uKHtcbiAqICAgICBsYWJlbDogJ1Nob3cgUG9wdXBNZW51JyxcbiAqICAgICBhY3Rpb246IGUgPT4ge1xuICogICAgICAgICBjb25zdCBwb3B1cG1lbnUgPSBuZXcgUG9wdXBNZW51KHtcbiAqICAgICAgICAgICAgIGl0ZW1zOiBbXG4gKiAgICAgICAgICAgICAgICAge2xhYmVsOiAnU2F2ZScsIGFjdGlvbjogKCkgPT4gYWxlcnQoJ1NhdmVkJyl9LFxuICogICAgICAgICAgICAgICAgIHtsYWJlbDogJ0VkaXQnLCBhY3Rpb246ICgpID0+IGFsZXJ0KCdFZGl0ZWQnKX0sXG4gKiAgICAgICAgICAgICAgICAge2xhYmVsOiAnRGVsZXRlJywgYWN0aW9uOiAoKSA9PiBhbGVydCgnRGVsZXRlZCcpfVxuICogICAgICAgICAgICAgXVxuICogICAgICAgICB9KVxuICogICAgICAgICBhcHAuc2NlbmUuYWRkQ2hpbGQocG9wdXBtZW51KVxuICogICAgIH1cbiAqIH0pXG4gKlxuICogLy8gQWRkIHRoZSBidXR0b24gdG8gYSBEaXNwbGF5T2JqZWN0XG4gKiBhcHAuc2NlbmUuYWRkQ2hpbGQoYnV0dG9uKVxuICpcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgUG9wdXBcbiAqIEBzZWUge0BsaW5rIGh0dHBzOi8vd3d3Lml3bS10dWViaW5nZW4uZGUvaXdtYnJvd3Nlci9saWIvcGl4aS9wb3B1cG1lbnUuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBQb3B1cE1lbnUgZXh0ZW5kcyBQb3B1cCB7XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhIFBvcHVwTWVudS5cbiAgICAgKiBcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3QgdG8gc3BlY2lmeSB0byBzdHlsZSBhbmQgYmVoYXZpb3VyIG9mIHRoZSBtb2RhbC5cbiAgICAgKiBAcGFyYW0ge29iamVjdFtdfSBbb3B0cy5pdGVtcz1bXV0gLSBBIGxpc3Qgb2YgdGhlIG1lbnUgaXRlbXMuIEVhY2ggaXRlbSBtdXN0IGJlIG9mIHR5cGUgb2JqZWN0LlxuICAgICAqICAgICBJZiBhbiBvYmplY3QgaGFzIGEgbGFiZWwgcHJvcGVydHksIGEgUElYSS5UZXh0IG9iamVjdCBpcyBjcmVhdGVkICh1c2luZyB0aGUgdGV4dFN0eWxlIHByb3BlcnR5KS5cbiAgICAgKiAgICAgSWYgYW4gb2JqZWN0IGhhc24ndCBhIGxhYmVsIHByb3BlcnR5LCBpdCBtdXN0IGNvbnRhaW4gYSBjb250ZW50IHByb3BlcnR5IHdoaWNoIGhhcyB0byBiZSBhXG4gICAgICogICAgIFBJWEkuRGlzcGxheU9iamVjdC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdHMubWFyZ2luPVRoZW1lLm1hcmdpbiAvIDJdIC0gVGhlIGFwcCB3aGVyZSB0aGUgbW9kYWwgYmVsb25ncyB0by5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHMudGV4dFN0eWxlPVRoZW1lLnRleHRTdHlsZV0gLSBUaGUgY29sb3Igb2YgdGhlIGJhY2tncm91bmQuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5jbG9zZU9uUG9wdXA9dHJ1ZV0gLSBUaGUgb3BhY2l0eSBvZiB0aGUgYmFja2dyb3VuZC5cbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihvcHRzID0ge30pIHtcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IHRoZW1lID0gVGhlbWUuZnJvbVN0cmluZyhvcHRzLnRoZW1lKVxuICAgICAgICBcbiAgICAgICAgb3B0cyA9IE9iamVjdC5hc3NpZ24oe30sIHtcbiAgICAgICAgICAgIGl0ZW1zOiBbXSxcbiAgICAgICAgICAgIG1hcmdpbjogdGhlbWUubWFyZ2luIC8gMixcbiAgICAgICAgICAgIHRleHRTdHlsZTogdGhlbWUudGV4dFN0eWxlLFxuICAgICAgICAgICAgY2xvc2VPblBvcHVwOiB0cnVlXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgc3VwZXIob3B0cylcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBjaGlsZHJlbiBhbmQgaW5zdGFudGlhdGVzIGV2ZXJ5dGhpbmcuXG4gICAgICogXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcmV0dXJuIHtQb3B1cE1lbnV9IEEgcmVmZXJlbmNlIHRvIHRoZSBwb3B1cG1lbnUgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIHNldHVwKCkge1xuXG4gICAgICAgIC8vIGNvbnRlbnRcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBjb25zdCBjb250ZW50ID0gbmV3IFBJWEkuQ29udGFpbmVyKClcbiAgICAgICAgXG4gICAgICAgIGxldCB5ID0gMFxuICAgICAgICBmb3IgKGxldCBpdGVtIG9mIHRoaXMub3B0cy5pdGVtcykge1xuXG4gICAgICAgICAgICBsZXQgb2JqZWN0ID0gbnVsbFxuXG4gICAgICAgICAgICBpZiAoaXRlbS5sYWJlbCkge1xuICAgICAgICAgICAgICAgIG9iamVjdCA9IG5ldyBQSVhJLlRleHQoaXRlbS5sYWJlbCwgaXRlbS50ZXh0U3R5bGUgfHwgdGhpcy5vcHRzLnRleHRTdHlsZSlcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgb2JqZWN0ID0gaXRlbS5jb250ZW50XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIG9iamVjdC55ID0geVxuXG4gICAgICAgICAgICBpZiAoaXRlbS5hY3Rpb24pIHtcbiAgICAgICAgICAgICAgICBpZiAoaXRlbS5kaXNhYmxlZCkge1xuICAgICAgICAgICAgICAgICAgICBvYmplY3QuYWxwaGEgPSAuNVxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIG9iamVjdC5pbnRlcmFjdGl2ZSA9IHRydWVcbiAgICAgICAgICAgICAgICAgICAgb2JqZWN0LmJ1dHRvbk1vZGUgPSB0cnVlXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG9iamVjdC5vbigncG9pbnRlcm92ZXInLCBlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgVHdlZW5MaXRlLnRvKG9iamVjdCwgdGhpcy50aGVtZS5mYXN0LCB7YWxwaGE6IC44Mywgb3ZlcndyaXRlOiAnbm9uZSd9KVxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgb2JqZWN0Lm9uKCdwb2ludGVyb3V0JywgZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIFR3ZWVuTGl0ZS50byhvYmplY3QsIHRoaXMudGhlbWUuZmFzdCwge2FscGhhOiAxLCBvdmVyd3JpdGU6ICdub25lJ30pXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICBvYmplY3Qub24oJ3BvaW50ZXJ1cCcsIGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpdGVtLmFjdGlvbi5jYWxsKG9iamVjdCwgZSwgb2JqZWN0KVxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5vcHRzLmNsb3NlT25BY3Rpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaGlkZSgpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb250ZW50LmFkZENoaWxkKG9iamVjdClcblxuICAgICAgICAgICAgeSArPSBvYmplY3QuaGVpZ2h0ICsgdGhpcy5vcHRzLm1hcmdpblxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5vcHRzLmNvbnRlbnQgPSBjb250ZW50XG5cbiAgICAgICAgc3VwZXIuc2V0dXAoKVxuICAgIH1cbn1cbiIsIi8qIGdsb2JhbCBRdWFkICovXG5cbmltcG9ydCBUaGVtZSBmcm9tICcuL3RoZW1lLmpzJ1xuXG4vKipcbiAqIENsYXNzIHRoYXQgcmVwcmVzZW50cyBhIFBpeGlKUyBWb2xhdGlsZS5cbiAqIFxuICogQGV4YW1wbGVcbiAqIGNvbnN0IGFwcCA9IG5ldyBQSVhJQXBwKHtcbiAqICAgICB2aWV3OiBjYW52YXMsXG4gKiAgICAgd2lkdGg6IDkwMCxcbiAqICAgICBoZWlnaHQ6IDI1MFxuICogfSkuc2V0dXAoKS5ydW4oKVxuICogXG4gKiBjb25zdCBidXR0b24gPSBuZXcgQnV0dG9uKHtcbiAqICAgICBsYWJlbDogJ1ZvbGF0aWxlIScsXG4gKiAgICAgYWN0aW9uOiAoKSA9PiB7XG4gKiAgICAgICAgIG5ldyBWb2xhdGlsZSh7XG4gKiAgICAgICAgICAgICBvYmplY3Q6IGJ1dHRvbixcbiAqICAgICAgICAgICAgIGRpcmVjdGlvbjogJ3JpZ2h0JyxcbiAqICAgICAgICAgICAgIGRlc3Ryb3lPbkNvbXBsZXRlOiBmYWxzZVxuICogICAgICAgICB9KVxuICogICAgIH1cbiAqIH0pXG4gKiBcbiAqIGFwcC5zY2VuZS5hZGRDaGlsZChidXR0b24pXG4gKlxuICogQGNsYXNzXG4gKiBAc2VlIHtAbGluayBodHRwczovL3d3dy5pd20tdHVlYmluZ2VuLmRlL2l3bWJyb3dzZXIvbGliL3BpeGkvdm9sYXRpbGUuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBWb2xhdGlsZSB7XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhIFZvbGF0aWxlLlxuICAgICAqIFxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0c10gLSBBbiBvcHRpb25zIG9iamVjdCB0byBzcGVjaWZ5IHRvIHN0eWxlIGFuZCBiZWhhdmlvdXIgb2YgdGhlIG1vZGFsLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5pZD1hdXRvIGdlbmVyYXRlZF0gLSBUaGUgaWQgb2YgdGhlIHRvb2x0aXAuXG4gICAgICogQHBhcmFtIHtQSVhJLkRpc3BsYXlPYmplY3R8UElYSS5EaXNwbGF5T2JqZWN0W119IG9wdHMub2JqZWN0IC0gVGhlIG9iamVjdCB3aGVyZSB0aGUgdm9sYXRpbGUgc2hvdWxkIGJlIGFwcGxpZWQgdG8uXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLmRpcmVjdGlvbj10b3BdIC0gVGhlIGFuaW1hdGlvbiBkaXJlY3Rpb24uIFBvc3NpYmxlIHZhbHVlczogdG9wLCByaWdodCwgYm90dG9tLCBsZWZ0LlxuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLm9uU3RhcnRdIC0gQSBmdW5jdGlvbiB3aGljaCBpcyBleGVjdXRlZCB3aGVuIHRoZSB2b2xhdGlsZSBhbmltYXRpb24gc3RhcnRzLlxuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtvcHRzLm9uQ29tcGxldGVdIC0gQSBmdW5jdGlvbiB3aGljaCBpcyBleGVjdXRlZCB3aGVuIHRoZSB2b2xhdGlsZSBhbmltYXRpb24gZmluaXNoZXMuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRzLmRpc3RhbmNlPTE2MF0gLSBUaGUgYW5pbWF0aW9uIGRpc3RhbmNlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5kdXJhdGlvbj0xLjVdIC0gVGhlIGR1cmF0aW9uIG9mIHRoZSBhbmltYXRpb24gaW4gc2Vjb25kcy5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHMuZWFzZT1RdWFkLmVhc2VPdXRdIC0gVGhlIGVhc2luZyBvZiB0aGUgYW5pbWF0aW9uLCBzZWUge0BsaW5rIGh0dHBzOi8vZ3JlZW5zb2NrLmNvbS9kb2NzL0Vhc2luZ31cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmRlc3Ryb3lPbkNvbXBsZXRlPXRydWVdIC0gU2hvdWxkIHRoZSBvYmplY3QgYmUgZGVzdHJveWVkIGFmdGVyIHRoZSB2b2xhdGlsZSBhbmltYXRpb24/XG4gICAgICovXG4gICAgY29uc3RydWN0b3Iob3B0cyA9IHt9KSB7XG4gICAgICAgIFxuICAgICAgICBjb25zdCB0aGVtZSA9IFRoZW1lLmZyb21TdHJpbmcob3B0cy50aGVtZSlcbiAgICAgICAgdGhpcy50aGVtZSA9IHRoZW1lXG5cbiAgICAgICAgdGhpcy5vcHRzID0gT2JqZWN0LmFzc2lnbih7fSwge1xuICAgICAgICAgICAgaWQ6IFBJWEkudXRpbHMudWlkKCksXG4gICAgICAgICAgICBvYmplY3Q6IG51bGwsXG4gICAgICAgICAgICBkaXJlY3Rpb246ICd0b3AnLCAgICAgICAgICAgICAgIC8vIHRvcCwgcmlnaHQsIGJvdHRvbSwgbGVmdFxuICAgICAgICAgICAgb25TdGFydDogbnVsbCxcbiAgICAgICAgICAgIG9uQ29tcGxldGU6IG51bGwsXG4gICAgICAgICAgICBkaXN0YW5jZTogMTYwLFxuICAgICAgICAgICAgZHVyYXRpb246IDEuNSxcbiAgICAgICAgICAgIGVhc2U6IFF1YWQuZWFzZU91dCxcbiAgICAgICAgICAgIGRlc3Ryb3lPbkNvbXBsZXRlOiB0cnVlXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgdGhpcy5pZCA9IHRoaXMub3B0cy5pZFxuXG4gICAgICAgIGlmICghQXJyYXkuaXNBcnJheSh0aGlzLm9wdHMub2JqZWN0KSkge1xuICAgICAgICAgICAgdGhpcy5vcHRzLm9iamVjdCA9IFt0aGlzLm9wdHMub2JqZWN0XVxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5vYmplY3RzID0gdGhpcy5vcHRzLm9iamVjdFxuXG4gICAgICAgIC8vIHNldHVwXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5zZXR1cCgpXG5cbiAgICAgICAgLy8gbGF5b3V0XG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgdGhpcy5sYXlvdXQoKVxuXG4gICAgICAgIC8vIHJ1blxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMucnVuKClcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBjaGlsZHJlbiBhbmQgaW5zdGFudGlhdGVzIGV2ZXJ5dGhpbmcuXG4gICAgICogXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcmV0dXJuIHtWb2xhdGlsZX0gQSByZWZlcmVuY2UgdG8gdGhlIHZvbGF0aWxlIGZvciBjaGFpbmluZy5cbiAgICAgKi9cbiAgICBzZXR1cCgpIHtcblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiBTaG91bGQgYmUgY2FsbGVkIHRvIHJlZnJlc2ggdGhlIGxheW91dCBvZiB0aGUgdm9sYXRpbGUuIENhbiBiZSB1c2VkIGFmdGVyIHJlc2l6aW5nLlxuICAgICAqIFxuICAgICAqIEByZXR1cm4ge1ZvbGF0aWxlfSBBIHJlZmVyZW5jZSB0byB0aGUgdm9sYXRpbGUgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGxheW91dCgpIHtcbiAgICAgICAgXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFN0YXJ0cyB0aGUgdm9sYXRpbGUgYW5pbWF0aW9uLlxuICAgICAqIFxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybiB7Vm9sYXRpbGV9IEEgcmVmZXJlbmNlIHRvIHRoZSB2b2xhdGlsZSBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgcnVuKCkge1xuXG4gICAgICAgIGZvciAobGV0IG9iamVjdCBvZiB0aGlzLm9iamVjdHMpIHtcblxuICAgICAgICAgICAgbGV0IHggPSBvYmplY3QueFxuICAgICAgICAgICAgbGV0IHkgPSBvYmplY3QueVxuXG4gICAgICAgICAgICBzd2l0Y2ggKHRoaXMub3B0cy5kaXJlY3Rpb24pIHtcbiAgICAgICAgICAgICAgICBjYXNlICd0b3AnOlxuICAgICAgICAgICAgICAgICAgICB5IC09IHRoaXMub3B0cy5kaXN0YW5jZVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgIGNhc2UgJ3JpZ2h0JzpcbiAgICAgICAgICAgICAgICAgICAgeCArPSB0aGlzLm9wdHMuZGlzdGFuY2VcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICBjYXNlICdib3R0b20nOlxuICAgICAgICAgICAgICAgICAgICB5ICs9IHRoaXMub3B0cy5kaXN0YW5jZVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgIGNhc2UgJ2xlZnQnOlxuICAgICAgICAgICAgICAgICAgICB4IC09IHRoaXMub3B0cy5kaXN0YW5jZVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBUd2VlbkxpdGUudG8ob2JqZWN0LCB0aGlzLm9wdHMuZHVyYXRpb24sIHtcbiAgICAgICAgICAgICAgICB4LFxuICAgICAgICAgICAgICAgIHksXG4gICAgICAgICAgICAgICAgYWxwaGE6IDAsXG4gICAgICAgICAgICAgICAgZWFzZTogdGhpcy5vcHRzLmVhc2UsXG4gICAgICAgICAgICAgICAgb3ZlcndyaXRlOiAnYWxsJyxcbiAgICAgICAgICAgICAgICBvblN0YXJ0OiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm9wdHMub25TdGFydCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vcHRzLm9uU3RhcnQuY2FsbChvYmplY3QsIG9iamVjdClcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgb25Db21wbGV0ZTogKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5vbkNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9wdHMub25Db21wbGV0ZS5jYWxsKG9iamVjdCwgb2JqZWN0KVxuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5kZXN0cm95T25Db21wbGV0ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgb2JqZWN0LmRlc3Ryb3koe2NoaWxkcmVuOiB0cnVlfSlcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpc1xuICAgIH1cbn1cbiIsIi8qIGdsb2JhbHMgKi9cblxuLyogSW1wb3J0cyAqL1xuaW1wb3J0IEV2ZW50cyBmcm9tICcuLi9ldmVudHMuanMnXG5cbi8qKlxuICogQ2xhc3MgdGhhdCByZXByZXNlbnRzIGEgUGl4aUpTIExpc3QuXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IGVsZXBoYW50MSA9IFBJWEkuU3ByaXRlLmZyb21JbWFnZSgnLi9hc3NldHMvZWxlcGhhbnQtMS5qcGcnKVxuICogY29uc3QgZWxlcGhhbnQyID0gUElYSS5TcHJpdGUuZnJvbUltYWdlKCcuL2Fzc2V0cy9lbGVwaGFudC0yLmpwZycpXG4gKiBcbiAqIC8vIENyZWF0ZSB0aGUgbGlzdFxuICogY29uc3QgbGlzdCA9IG5ldyBMaXN0KFtlbGVwaGFudDEsIGVsZXBoYW50Ml0pXG4gKiBcbiAqIGFwcC5zY2VuZS5hZGRDaGlsZChsaXN0KVxuICpcbiAqIEBjbGFzc1xuICogQGV4dGVuZHMgUElYSS5Db250YWluZXJcbiAqIEBzZWUge0BsaW5rIGh0dHA6Ly9waXhpanMuZG93bmxvYWQvZGV2L2RvY3MvUElYSS5Db250YWluZXIuaHRtbHxQaXhpSlMgQ29udGFpbmVyfVxuICogQHNlZSB7QGxpbmsgaHR0cHM6Ly93d3cuaXdtLXR1ZWJpbmdlbi5kZS9pd21icm93c2VyL2xpYi9waXhpL2xpc3QuaHRtbHxEb2NUZXN0fVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBMaXN0IGV4dGVuZHMgUElYSS5Db250YWluZXIge1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhIEZsaXBwYWJsZS5cbiAgICAgKlxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqIEBwYXJhbSB7UElYSS5EaXNwbGF5T2JqZWN0W119IGl0ZW1zIC0gQW4gYXJyYXkgb2YgUElYSS5EaXNwbGF5T2JqZWN0cy5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdHNdIC0gQW4gb3B0aW9ucyBvYmplY3Qgd2hpY2ggY2FuIGNvbnRhaW4gdGhlIGZvbGxvd2luZyBwcm9wZXJ0aWVzLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy53aWR0aF0gLSBUaGUgd2lkdGggb2YgdGhlIGxpc3QuIElmIHRoZSBpdGVtcyBhcmUgbGFyZ2VyIHRoYW4gdGhpcyB3aWR0aCwgdGhlIG92ZXJmbG93XG4gICAgICogICAgIHdpbGwgYmUgaGlkZGVuLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5oZWlnaHRdIC0gVGhlIGhlaWdodCBvZiB0aGUgbGlzdC4gSWYgdGhlIGl0ZW1zIGFyZSBsYXJnZXIgdGhhbiB0aGlzIGhlaWdodCwgdGhlIG92ZXJmbG93XG4gICAgICogICAgIHdpbGwgYmUgaGlkZGVuLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5wYWRkaW5nPTEwXSAtIFRoZSBpbm5lciBzcGFjaW5nIChkaXN0YW5jZSBmcm9tIG9uZSBpdGVtIHRvIHRoZSBwcmV2aW91cy9uZXh0IGl0ZW0pLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0cy5tYXJnaW49MTBdIC0gVGhlIG91dGVyIHNwYWNpbmcgKGRpc3RhbmNlIGZyb20gb25lIGl0ZW0gdG8gdGhlIGJvcmRlcikuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLm9yaWVudGF0aW9uPXZlcnRpY2FsXSAtIFRoZSBvcmllbnRhdGlvbiBvZiB0aGUgYnV0dG9uIGdyb3VwLiBDYW4gYmUgaG9yaXpvbnRhbCBvciB2ZXJ0aWNhbC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMuYWxpZ249bGVmdF0gLSBUaGUgaG9yaXpvbnRhbCBwb3NpdGlvbiBvZiB0aGUgaXRlbXMuIFBvc3NpYmxlIHZhbHVlcyBhcmVcbiAgICAgKiAgICAgbGVmdCwgY2VudGVyIGFuZCByaWdodC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMudmVydGljYWxBbGlnbj1taWRkbGVdIC0gVGhlIHZlcnRpY2FsIHBvc2l0aW9uIG9mIHRoZSBpdGVtcy4gUG9zc2libGUgdmFsdWVzIGFyZVxuICAgICAqICAgICB0b3AsIG1pZGRsZSBhbmQgYm90dG9tLlxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKGl0ZW1zID0gW10sIG9wdHMgPSB7fSkge1xuXG4gICAgICAgIHN1cGVyKClcblxuICAgICAgICB0aGlzLm9wdHMgPSBPYmplY3QuYXNzaWduKHt9LCB7XG4gICAgICAgICAgICBwYWRkaW5nOiAxMCxcbiAgICAgICAgICAgIG1hcmdpbjogMTAsXG4gICAgICAgICAgICBvcmllbnRhdGlvbjogJ3ZlcnRpY2FsJyxcbiAgICAgICAgICAgIGFsaWduOiAnbGVmdCcsXG4gICAgICAgICAgICB2ZXJ0aWNhbEFsaWduOiAnbWlkZGxlJyxcbiAgICAgICAgICAgIHdpZHRoOiBudWxsLFxuICAgICAgICAgICAgaGVpZ2h0OiBudWxsXG4gICAgICAgIH0sIG9wdHMpXG5cbiAgICAgICAgdGhpcy5fX2l0ZW1zID0gaXRlbXNcbiAgICAgICAgdGhpcy5fX2RyYWdnaW5nID0gZmFsc2VcblxuICAgICAgICAvLyBzZXR1cFxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuc2V0dXAoKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgY2hpbGRyZW4gYW5kIGluc3RhbnRpYXRlcyBldmVyeXRoaW5nLlxuICAgICAqXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcmV0dXJuIHtMaXN0fSBBIHJlZmVyZW5jZSB0byB0aGUgbGlzdCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgc2V0dXAoKSB7XG5cbiAgICAgICAgLy8gaW5uZXIgY29udGFpbmVyXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgY29uc3QgY29udGFpbmVyID0gbmV3IFBJWEkuQ29udGFpbmVyKClcbiAgICAgICAgdGhpcy5hZGRDaGlsZChjb250YWluZXIpXG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gY29udGFpbmVyXG5cbiAgICAgICAgLy8gbWFza1xuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIGNvbnN0IG1hc2sgPSBuZXcgUElYSS5HcmFwaGljcygpXG4gICAgICAgIHRoaXMuYWRkQ2hpbGQobWFzaylcbiAgICAgICAgdGhpcy5fX21hc2sgPSBtYXNrXG5cbiAgICAgICAgLy8gYWRkIGl0ZW1zXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgZm9yKGxldCBpdGVtIG9mIHRoaXMuX19pdGVtcykge1xuICAgICAgICAgICAgY29udGFpbmVyLmFkZENoaWxkKGl0ZW0pXG4gICAgICAgIH1cblxuICAgICAgICAvLyBpbnRlcmFjdGlvblxuICAgICAgICAvLy0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAgIHRoaXMuaW50ZXJhY3RpdmUgPSB0aGlzLm9wdHMud2lkdGggfHwgdGhpcy5vcHRzLmhlaWdodFxuICAgICAgICB0aGlzLm9uKCdwb2ludGVyZG93bicsIHRoaXMub25TdGFydC5iaW5kKHRoaXMpKVxuICAgICAgICB0aGlzLm9uKCdwb2ludGVybW92ZScsIHRoaXMub25Nb3ZlLmJpbmQodGhpcykpXG4gICAgICAgIHRoaXMub24oJ3BvaW50ZXJ1cCcsIHRoaXMub25FbmQuYmluZCh0aGlzKSlcbiAgICAgICAgdGhpcy5vbigncG9pbnRlcmNhbmNlbCcsIHRoaXMub25FbmQuYmluZCh0aGlzKSlcbiAgICAgICAgdGhpcy5vbigncG9pbnRlcm91dCcsIHRoaXMub25FbmQuYmluZCh0aGlzKSlcbiAgICAgICAgdGhpcy5vbigncG9pbnRlcnVwb3V0c2lkZScsIHRoaXMub25FbmQuYmluZCh0aGlzKSlcblxuICAgICAgICB0aGlzLmxheW91dCgpXG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXBsYWNlcyB0aGUgZXhpc3RpbmcgaXRlbXMgYW5kIHJlbGF5b3V0cyB0aGUgbGlzdC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7UElYSS5EaXNwbGF5T2JqZWN0W119IGl0ZW1zIC0gQW4gYXJyYXkgb2YgUElYSS5EaXNwbGF5T2JqZWN0cy5cbiAgICAgKiBAcmV0dXJuIHtMaXN0fSBBIHJlZmVyZW5jZSB0byB0aGUgbGlzdCBmb3IgY2hhaW5pbmcuXG4gICAgICovXG4gICAgc2V0SXRlbXMoaXRlbXMpIHtcbiAgICAgICAgdGhpcy5jb250YWluZXIucmVtb3ZlQ2hpbGRyZW4oKVxuICAgICAgICB0aGlzLl9faXRlbXMgPSBpdGVtc1xuICAgICAgICBmb3IobGV0IGl0ZW0gb2YgdGhpcy5fX2l0ZW1zKSB7XG4gICAgICAgICAgICB0aGlzLmNvbnRhaW5lci5hZGRDaGlsZChpdGVtKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMubGF5b3V0KClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTaG91bGQgYmUgY2FsbGVkIHRvIHJlZnJlc2ggdGhlIGxheW91dCBvZiB0aGUgbGlzdCAodGhlIHdpZHRoIG9yIHRoZSBoZWlnaHQpLlxuICAgICAqXG4gICAgICogQHJldHVybiB7TGlzdH0gQSByZWZlcmVuY2UgdG8gdGhlIGxpc3QgZm9yIGNoYWluaW5nLlxuICAgICAqL1xuICAgIGxheW91dCgpIHtcblxuICAgICAgICBjb25zdCBtYXJnaW4gPSB0aGlzLm9wdHMubWFyZ2luXG5cbiAgICAgICAgbGV0IHggPSBtYXJnaW5cbiAgICAgICAgbGV0IHkgPSBtYXJnaW5cblxuICAgICAgICBmb3IgKGxldCBpdGVtIG9mIHRoaXMuX19pdGVtcykge1xuXG4gICAgICAgICAgICBpdGVtLnggPSB4XG4gICAgICAgICAgICBpdGVtLnkgPSB5XG5cbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMub3JpZW50YXRpb24gPT09ICd2ZXJ0aWNhbCcpIHtcbiAgICAgICAgICAgICAgICB5ICs9IGl0ZW0uaGVpZ2h0ICsgdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgeCArPSBpdGVtLndpZHRoICsgdGhpcy5vcHRzLnBhZGRpbmdcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHZlcnRpY2FsXG4gICAgICAgIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAgICAgaWYgKHRoaXMub3B0cy5vcmllbnRhdGlvbiA9PT0gJ3ZlcnRpY2FsJykge1xuICAgICAgICAgICAgc3dpdGNoICh0aGlzLm9wdHMuYWxpZ24pIHtcbiAgICAgICAgICAgICAgICBjYXNlICdjZW50ZXInOlxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9faXRlbXMuZm9yRWFjaChpdCA9PiBpdC54ID0gbWFyZ2luICsgdGhpcy53aWR0aCAvIDIgLSBpdC53aWR0aCAvIDIpXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgY2FzZSAncmlnaHQnOlxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9faXRlbXMuZm9yRWFjaChpdCA9PiBpdC54ID0gbWFyZ2luICsgdGhpcy53aWR0aCAtIGl0LndpZHRoKVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX19pdGVtcy5mb3JFYWNoKGl0ID0+IGl0LnggPSBtYXJnaW4pXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh0aGlzLm9wdHMuaGVpZ2h0KSB7XG4gICAgICAgICAgICAgICAgY29uc3QgbWFzayA9IHRoaXMuX19tYXNrXG4gICAgICAgICAgICAgICAgbWFzay5jbGVhcigpXG4gICAgICAgICAgICAgICAgbWFzay5iZWdpbkZpbGwoMHgwMDApXG4gICAgICAgICAgICAgICAgbWFzay5kcmF3UmVjdCgwLCAwLCB0aGlzLndpZHRoICsgMiAqIG1hcmdpbiwgdGhpcy5vcHRzLmhlaWdodClcbiAgICAgICAgICAgICAgICB0aGlzLm1hc2sgPSBtYXNrXG5cbiAgICAgICAgICAgICAgICB0aGlzLmludGVyYWN0aXZlID0gdGhpcy5pbm5lckhlaWdodCA+IHRoaXMub3B0cy5oZWlnaHRcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGhvcml6b250YWxcbiAgICAgICAgLy8tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgICAgICBpZiAodGhpcy5vcHRzLm9yaWVudGF0aW9uID09PSAnaG9yaXpvbnRhbCcpIHtcbiAgICAgICAgICAgIHN3aXRjaCAodGhpcy5vcHRzLnZlcnRpY2FsQWxpZ24pIHtcbiAgICAgICAgICAgICAgICBjYXNlICd0b3AnOlxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9faXRlbXMuZm9yRWFjaChpdCA9PiBpdC55ID0gbWFyZ2luKVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgIGNhc2UgJ2JvdHRvbSc6XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX19pdGVtcy5mb3JFYWNoKGl0ID0+IGl0LnkgPSBtYXJnaW4gKyB0aGlzLmhlaWdodCAtIGl0LmhlaWdodClcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9faXRlbXMuZm9yRWFjaChpdCA9PiBpdC55ID0gbWFyZ2luICsgdGhpcy5oZWlnaHQgLyAyIC0gaXQuaGVpZ2h0IC8gMilcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHRoaXMub3B0cy53aWR0aCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IG1hc2sgPSB0aGlzLl9fbWFza1xuICAgICAgICAgICAgICAgIG1hc2suY2xlYXIoKVxuICAgICAgICAgICAgICAgIG1hc2suYmVnaW5GaWxsKDB4MDAwKVxuICAgICAgICAgICAgICAgIG1hc2suZHJhd1JlY3QoMCwgMCwgdGhpcy5vcHRzLndpZHRoLCB0aGlzLmhlaWdodCArIDIgKiBtYXJnaW4pXG4gICAgICAgICAgICAgICAgdGhpcy5tYXNrID0gbWFza1xuXG4gICAgICAgICAgICAgICAgdGhpcy5pbnRlcmFjdGl2ZSA9IHRoaXMuaW5uZXJXaWR0aCA+IHRoaXMub3B0cy53aWR0aFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXNcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBcbiAgICAgKi9cbiAgICBnZXQgaW5uZXJXaWR0aCgpIHtcblxuICAgICAgICBsZXQgc2l6ZSA9IDBcblxuICAgICAgICB0aGlzLl9faXRlbXMuZm9yRWFjaChpdCA9PiBzaXplICs9IGl0LndpZHRoKVxuICAgICAgICBzaXplICs9IHRoaXMub3B0cy5wYWRkaW5nICogKHRoaXMuX19pdGVtcy5sZW5ndGggLSAxKVxuICAgICAgICBzaXplICs9IDIgKiB0aGlzLm9wdHMubWFyZ2luXG5cbiAgICAgICAgcmV0dXJuIHNpemVcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBcbiAgICAgKi9cbiAgICBnZXQgaW5uZXJIZWlnaHQoKSB7XG5cbiAgICAgICAgbGV0IHNpemUgPSAwXG5cbiAgICAgICAgdGhpcy5fX2l0ZW1zLmZvckVhY2goaXQgPT4gc2l6ZSArPSBpdC5oZWlnaHQpXG4gICAgICAgIHNpemUgKz0gdGhpcy5vcHRzLnBhZGRpbmcgKiAodGhpcy5fX2l0ZW1zLmxlbmd0aCAtIDEpXG4gICAgICAgIHNpemUgKz0gMiAqIHRoaXMub3B0cy5tYXJnaW5cblxuICAgICAgICByZXR1cm4gc2l6ZVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlc2l6ZXMgdGhlIGxpc3QuXG4gICAgICogXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoT3JIZWlnaHQgLSBUaGUgbmV3IHdpZHRoIChpZiBvcmllbnRhdGlvbiBpcyBob3Jpem9udGFsKSBvciBoZWlnaHQgKGlmIG9yaWVudGF0aW9uIGlzIHZlcnRpY2FsKSBvZiB0aGUgbGlzdC5cbiAgICAgKi9cbiAgICByZXNpemUod2lkdGhPckhlaWdodCkge1xuXG4gICAgICAgIGlmICh0aGlzLm9wdHMub3JpZW50YXRpb24gPT09ICdob3Jpem9udGFsJykge1xuICAgICAgICAgICAgdGhpcy5vcHRzLndpZHRoID0gd2lkdGhPckhlaWdodFxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5vcHRzLmhlaWdodCA9IHdpZHRoT3JIZWlnaHRcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMubGF5b3V0KClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7Kn0gZXZlbnQgXG4gICAgICovXG4gICAgb25TdGFydChldmVudCkge1xuXG4gICAgICAgIHRoaXMuX19kcmFnZ2luZyA9IHRydWVcblxuICAgICAgICB0aGlzLmNhcHR1cmUoZXZlbnQpXG5cbiAgICAgICAgdGhpcy5fX2RlbHRhID0ge1xuICAgICAgICAgICAgeDogdGhpcy5jb250YWluZXIucG9zaXRpb24ueCAtIGV2ZW50LmRhdGEuZ2xvYmFsLngsXG4gICAgICAgICAgICB5OiB0aGlzLmNvbnRhaW5lci5wb3NpdGlvbi55IC0gZXZlbnQuZGF0YS5nbG9iYWwueVxuICAgICAgICB9XG5cbiAgICAgICAgVHdlZW5MaXRlLmtpbGxUd2VlbnNPZih0aGlzLmNvbnRhaW5lci5wb3NpdGlvbiwge3g6IHRydWUsIHk6IHRydWV9KVxuICAgICAgICBUaHJvd1Byb3BzUGx1Z2luLnRyYWNrKHRoaXMuY29udGFpbmVyLnBvc2l0aW9uLCAneCx5JylcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7Kn0gZXZlbnQgXG4gICAgICovXG4gICAgb25Nb3ZlKGV2ZW50KSB7XG5cbiAgICAgICAgaWYgKHRoaXMuX19kcmFnZ2luZykge1xuICAgICAgICBcbiAgICAgICAgICAgIHRoaXMuY2FwdHVyZShldmVudClcblxuICAgICAgICAgICAgaWYgKHRoaXMub3B0cy5vcmllbnRhdGlvbiA9PT0gJ2hvcml6b250YWwnKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jb250YWluZXIucG9zaXRpb24ueCA9IGV2ZW50LmRhdGEuZ2xvYmFsLnggKyB0aGlzLl9fZGVsdGEueFxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmNvbnRhaW5lci5wb3NpdGlvbi55ID0gZXZlbnQuZGF0YS5nbG9iYWwueSArIHRoaXMuX19kZWx0YS55XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7Kn0gZXZlbnQgXG4gICAgICovXG4gICAgb25FbmQoZXZlbnQpIHtcblxuICAgICAgICBpZiAodGhpcy5fX2RyYWdnaW5nKSB7XG4gICAgICAgICAgICB0aGlzLl9fZHJhZ2dpbmcgPSBmYWxzZVxuXG4gICAgICAgICAgICB0aGlzLmNhcHR1cmUoZXZlbnQpXG5cbiAgICAgICAgICAgIGNvbnN0IHRocm93UHJvcHMgPSB7fVxuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAodGhpcy5vcHRzLm9yaWVudGF0aW9uID09PSAnaG9yaXpvbnRhbCcpIHtcbiAgICAgICAgICAgICAgICBsZXQgbWluID0gdGhpcy5vcHRzLndpZHRoIC0gdGhpcy5pbm5lcldpZHRoXG4gICAgICAgICAgICAgICAgbWluID0gbWluID4gMCA/IDAgOiBtaW5cbiAgICAgICAgICAgICAgICB0aHJvd1Byb3BzLnggPSB7XG4gICAgICAgICAgICAgICAgICAgIHZlbG9jaXR5OiAnYXV0bycsXG4gICAgICAgICAgICAgICAgICAgIG1pbixcbiAgICAgICAgICAgICAgICAgICAgbWF4OiAwXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBsZXQgbWluID0gdGhpcy5vcHRzLmhlaWdodCAtIHRoaXMuaW5uZXJIZWlnaHRcbiAgICAgICAgICAgICAgICBtaW4gPSBtaW4gPiAwID8gMCA6IG1pblxuICAgICAgICAgICAgICAgIHRocm93UHJvcHMueSA9IHtcbiAgICAgICAgICAgICAgICAgICAgdmVsb2NpdHk6ICdhdXRvJyxcbiAgICAgICAgICAgICAgICAgICAgbWluLFxuICAgICAgICAgICAgICAgICAgICBtYXg6IDBcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIFRocm93UHJvcHNQbHVnaW4udG8odGhpcy5jb250YWluZXIucG9zaXRpb24sIHtcbiAgICAgICAgICAgICAgICB0aHJvd1Byb3BzLFxuICAgICAgICAgICAgICAgIGVhc2U6IFN0cm9uZy5lYXNlT3V0LFxuICAgICAgICAgICAgICAgIG9uQ29tcGxldGU6ICgpID0+IFRocm93UHJvcHNQbHVnaW4udW50cmFjayh0aGlzLmNvbnRhaW5lci5wb3NpdGlvbilcbiAgICAgICAgICAgIH0sIC44LCAuNClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENhcHR1cmVzIGFuIGV2ZW50IHRvIGluZm9ybSBJbnRlcmFjdGlvbk1hcHBlciBhYm91dCBwcm9jZXNzZWQgZXZlbnRzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtldmVudHxQSVhJLkludGVyYWN0aW9uRXZlbnR9IGV2ZW50IC0gVGhlIFBJWEkgZXZlbnQgdG8gY2FwdHVyZS5cbiAgICAgKi9cbiAgICBjYXB0dXJlKGV2ZW50KSB7XG4gICAgICAgIEV2ZW50cy5jYXB0dXJlZEJ5KGV2ZW50LmRhdGEub3JpZ2luYWxFdmVudCwgdGhpcylcbiAgICB9XG59XG4iLCJpbXBvcnQgUElYSUFwcCBmcm9tICcuL2FwcC5qcydcbmltcG9ydCBCbHVyRmlsdGVyIGZyb20gJy4vYmx1cmZpbHRlci5qcydcbmltcG9ydCBGbGlwRWZmZWN0IGZyb20gJy4vZmxpcGVmZmVjdC5qcydcbmltcG9ydCBGbGlwcGFibGUgZnJvbSAnLi9mbGlwcGFibGUuanMnXG5pbXBvcnQge0RlZXBab29tSW5mbywgRGVlcFpvb21JbWFnZX0gZnJvbSAnLi9kZWVwem9vbS9pbWFnZS5qcydcbmltcG9ydCBQb3BvdmVyIGZyb20gJy4vcG9wb3Zlci5qcydcbmltcG9ydCB7U2NhdHRlckNvbnRhaW5lciwgRGlzcGxheU9iamVjdFNjYXR0ZXJ9IGZyb20gJy4vc2NhdHRlci5qcydcbmltcG9ydCB7QXBwVGVzdCwgQ29tbWFuZCwgUmVjb3JkZXJUb29sc30gZnJvbSAnLi90ZXN0LmpzJ1xuaW1wb3J0IFRpbWVsaW5lIGZyb20gJy4vdGltZWxpbmUuanMnXG5pbXBvcnQgVGhlbWUgZnJvbSAnLi90aGVtZS5qcydcbmltcG9ydCBCdXR0b24gZnJvbSAnLi9idXR0b24uanMnXG5pbXBvcnQgQnV0dG9uR3JvdXAgZnJvbSAnLi9idXR0b25ncm91cC5qcydcbmltcG9ydCBTbGlkZXIgZnJvbSAnLi9zbGlkZXIuanMnXG5pbXBvcnQgU3dpdGNoIGZyb20gJy4vc3dpdGNoLmpzJ1xuaW1wb3J0IFBvcHVwIGZyb20gJy4vcG9wdXAuanMnXG5pbXBvcnQgUG9wdXBNZW51IGZyb20gJy4vcG9wdXBtZW51LmpzJ1xuaW1wb3J0IE1vZGFsIGZyb20gJy4vbW9kYWwuanMnXG5pbXBvcnQgVm9sYXRpbGUgZnJvbSAnLi92b2xhdGlsZS5qcydcbmltcG9ydCBNZXNzYWdlIGZyb20gJy4vbWVzc2FnZS5qcydcbmltcG9ydCBUb29sdGlwIGZyb20gJy4vdG9vbHRpcC5qcydcbmltcG9ydCBCYWRnZSBmcm9tICcuL2JhZGdlLmpzJ1xuaW1wb3J0IFByb2dyZXNzIGZyb20gJy4vcHJvZ3Jlc3MuanMnXG5pbXBvcnQgTGlzdCBmcm9tICcuL2xpc3QuanMnXG5pbXBvcnQge0xhYmVsZWRHcmFwaGljcywgRm9udEluZm99IGZyb20gJy4vbGFiZWxlZGdyYXBoaWNzLmpzJ1xuLyogTmVlZGVkIHRvIGVuc3VyZSB0aGF0IHJvbGx1cC5qcyBpbmNsdWRlcyBjbGFzcyBkZWZpbml0aW9ucyBhbmQgdGhlIGNsYXNzZXNcbmFyZSB2aXNpYmxlIGluc2lkZSBkb2N0ZXN0cy5cbiovXG53aW5kb3cuUElYSUFwcCA9IFBJWElBcHBcbndpbmRvdy5CbHVyRmlsdGVyID0gQmx1ckZpbHRlclxud2luZG93LkZsaXBFZmZlY3QgPSBGbGlwRWZmZWN0XG53aW5kb3cuRmxpcHBhYmxlID0gRmxpcHBhYmxlXG53aW5kb3cuRGVlcFpvb21JbmZvID0gRGVlcFpvb21JbmZvXG53aW5kb3cuRGVlcFpvb21JbWFnZSA9IERlZXBab29tSW1hZ2VcbndpbmRvdy5Qb3BvdmVyID0gUG9wb3Zlclxud2luZG93LlNjYXR0ZXJDb250YWluZXIgPSBTY2F0dGVyQ29udGFpbmVyXG53aW5kb3cuRGlzcGxheU9iamVjdFNjYXR0ZXIgPSBEaXNwbGF5T2JqZWN0U2NhdHRlclxud2luZG93LkNvbW1hbmQgPSBDb21tYW5kXG53aW5kb3cuUmVjb3JkZXJUb29scyA9IFJlY29yZGVyVG9vbHNcbndpbmRvdy5UaW1lbGluZSA9IFRpbWVsaW5lXG53aW5kb3cuQXBwVGVzdCA9IEFwcFRlc3RcbndpbmRvdy5UaGVtZSA9IFRoZW1lXG53aW5kb3cuQnV0dG9uID0gQnV0dG9uXG53aW5kb3cuQnV0dG9uR3JvdXAgPSBCdXR0b25Hcm91cFxud2luZG93LlNsaWRlciA9IFNsaWRlclxud2luZG93LlN3aXRjaCA9IFN3aXRjaFxud2luZG93LlBvcHVwID0gUG9wdXBcbndpbmRvdy5Qb3B1cE1lbnUgPSBQb3B1cE1lbnVcbndpbmRvdy5Nb2RhbCA9IE1vZGFsXG53aW5kb3cuVm9sYXRpbGUgPSBWb2xhdGlsZVxud2luZG93Lk1lc3NhZ2UgPSBNZXNzYWdlXG53aW5kb3cuVG9vbHRpcCA9IFRvb2x0aXBcbndpbmRvdy5CYWRnZSA9IEJhZGdlXG53aW5kb3cuUHJvZ3Jlc3MgPSBQcm9ncmVzc1xud2luZG93Lkxpc3QgPSBMaXN0XG53aW5kb3cuTGFiZWxlZEdyYXBoaWNzID0gTGFiZWxlZEdyYXBoaWNzXG53aW5kb3cuRm9udEluZm8gPSBGb250SW5mbyJdLCJuYW1lcyI6WyJFdmVudHMiLCJFbGVtZW50cyIsIkludGVyYWN0aW9uTWFwcGVyIiwiUG9wdXBNZW51Il0sIm1hcHBpbmdzIjoiOzs7SUFBQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0FBQ0EsSUFBZSxNQUFNLEtBQUssQ0FBQzs7SUFFM0I7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0EsSUFBSSxXQUFXLENBQUMsSUFBSSxHQUFHLEVBQUUsRUFBRTs7SUFFM0IsUUFBUSxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsWUFBWSxJQUFJLElBQUksR0FBRyxJQUFJLENBQUMsWUFBWSxHQUFHLFNBQVE7SUFDckYsUUFBUSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxHQUFHLFNBQVE7SUFDbkUsUUFBUSxNQUFNLE1B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SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBLElBQUksV0FBVyxHQUFHOztJQUVsQixRQUFRLEtBQUssQ0FBQyxDQUFDLE1BQU0sRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxFQUFDO0lBQ25ELEtBQUs7SUFDTCxDQUFDOztJQUVEO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7QUFDQSxJQUFPLE1BQU0sUUFBUSxTQUFTLEtBQUssQ0FBQzs7SUFFcEM7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBLElBQUksV0FBVyxHQUFHOztJQUVsQixRQUFRLEtBQUssQ0FBQyxDQUFDLFlBQVksRUFBRSxRQUFRLENBQUMsRUFBQztJQUN2QyxLQUFLO0lBQ0wsQ0FBQzs7SUN6T0Q7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtBQUNBLElBQWUsTUFBTSxRQUFRLFNBQVMsSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUNyRDtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBLElBQUksV0FBVyxDQUFDLElBQUksR0FBRyxFQUFFLEVBQUU7O0lBRTNCLFFBQVEsS0FBSyxHQUFFO0lBQ2Y7SUFDQSxRQUFRLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBQztJQUNsRCxRQUFRLElBQUksQ0FBQyxLQUFLLEdBQUcsTUFBSzs7SUFFMUIsUUFBUSxJQUFJLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFB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QUFHLE9BQU07SUFDNUIsUUFBUSxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBQzs7SUFFN0I7SUFDQTtJQUNBLFFBQVEsSUFBSSxPQUFPLEdBQUcsSUFBSSxJQUFJLENBQUMsU0FBUyxHQUFFO0lBQzFDLFFBQVEsSUFBSSxDQUFDLE9BQU8sR0FBRyxRQUFPO0lBQzlCLFFBQVEsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUM7O0lBRTlCO0lBQ0E7SUFDQSxRQUFRLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUU7SUFDN0IsWUFBWSxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBQztJQUMzRSxTQUFTOztJQUVUO0lBQ0E7SUFDQSxRQUFRLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUU7SUFDNUIsWUFBWSxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUM7SUFDbEYsU0FBUzs7SUFFVCxRQUFRLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUU7SUFDbEMsWUFBWSxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUM7SUFDNUYsU0FBUzs7SUFFVDtJQUNBO0lBQ0EsUUFBUSxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxhQUFhLEVBQUUsQ0FBQyxJQUFJO0lBQzNDLFlBQVksSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLEVBQUM7SUFDM0IsWUFBWSxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsRUFBQztJQUN2RyxTQUFTLEVBQUM7O0lBRVYsUUFBUSxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxhQUFhLEVBQUUsQ0FBQyxJQUFJO0lBQzNDLFlBQVksSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLEVBQUM7SUFDM0IsU0FBUyxFQUFDOztJQUVWLFFBQVEsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsWUFBWSxFQUFFLENBQUMsSUFBSTtJQUMxQyxZQUFZLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFDO0lBQzNCLFlBQVksU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUMsRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLEVBQUM7SUFDckcsU0FBUyxFQUFDOztJQUVWLFFBQVEsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsYUFBYSxFQUFFLENBQUMsSUFBSTtJQUMzQztJQUNBLFlBQVksU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLEVBQUM7SUFDdEcsU0FBUyxFQUFDOztJQUVWLFFBQVEsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsSUFBSTtJQUN6QyxZQUFZLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFDO0lBQzNCLFlBQVksSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRTtJQUN4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LENBQUMsQ0FBQyxFQUFDO0lBQzNCLEtBQUs7O0lBRUw7SUFDQTtJQUNBO0lBQ0EsSUFBSSxnQkFBZ0IsQ0FBQyxDQUFDLEVBQUU7SUFDeEIsUUFBUSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLEVBQUM7SUFDNUUsS0FBSzs7SUFFTDtJQUNBO0lBQ0E7SUFDQSxJQUFJLGdCQUFnQixHQUFHO0lBQ3ZCLFFBQVEsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU07SUFDakMsS0FBSzs7SUFFTDtJQUNBO0lBQ0E7SUFDQSxJQUFJLE1BQU0sQ0FBQyxJQUFJLEVBQUU7SUFDakIsUUFBUSxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7SUFDckQsWUFBWSxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUM7SUFDcEMsWUFBWSxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUM7SUFDcEMsWUFBWSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUM7SUFDdEUsWUFBWSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLEVB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SUFDbEQsUUFBUSxJQUFJLFlBQVksR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxHQUFHLEdBQUU7SUFDcEcsUUFBUSxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxlQUFlLEdBQUcsVUFBUztJQUN0RCxRQUFRLElBQUksWUFBWSxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDLEdBQUcsR0FBRTtJQUNwRyxRQUFRLElBQUksTUFBTSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsWUFBWSxFQUFFLFlBQVksRUFBQzs7SUFFaEUsUUFBUSxTQUFTLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLElBQUksRUFBRSxFQUFFLEVBQUM7SUFDaEYsUUFBUSxTQUFTLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsR0FBRyxFQUFFLElBQUksR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLElBQUksRUFBRSxFQUFFLEVBQUM7O0lBRS9FLFFBQVEsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsVUFBVSxFQUFFLE9BQU8sRUFBQzs7SUFFbEQsUUFBUSxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMxQyxLQUFLO0lBQ0wsQ0FBQzs7SUFFRCxVQUFVLENB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RyxFQUFDO0lBQzFDLFlBQVksUUFBUSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBQztJQUN6RSxZQUFZLFFBQVEsQ0FBQyxPQUFPLEdBQUU7SUFDOUIsU0FBUztJQUNULFFBQVEsSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLFlBQVc7SUFDaEMsUUFBUSxJQUFJLENBQUMsSUFBSSxJQUFJLEVBQUU7SUFDdkIsWUFBWSxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUUsRUFBRSxLQUFLLEVBQUM7SUFDekMsWUFBWSxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUM7SUFDMUQsWUFBWSxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBQztJQUNyQyxZQUFZLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBQzs7SUFFMUQsWUFBWSxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFDO0lBQ2hELFlBQVksUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBQztJQUMvQyxTQUFTOztJQUVULFFBQVEsSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLGFBQVk7SUFDakMsUUFBUSxJQUFJLENBQUMsSUFBSSxJQUFJLEVBQUU7SUFDdkIsWUFB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SSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUM7SUFDM0YsUUFBUSxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBQztJQUMvRCxRQUFRLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBQztJQUM3SCxRQUFRLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUM7SUFDNUQsUUFBUSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBQztJQUM5RSxRQUFRLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBQztJQUMvRCxRQUFRLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxHQUFFOztJQUVoQztJQUNBLFFBQVEsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEdBQUU7SUFDNUIsUUFBUSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUU7SUFDekIsWUFBWSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLHdCQUF3QixFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsbUJBQW1CLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyx3QkFBd0IsRUFBQztJQUN6SSxZQUFZLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsRUFBQztJQUNqRyxZQUFZLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxDQUFDLEVBQUM7SUFDNUUsU0FBUyxNQUFNO0lBQ2YsWUFBWSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsa0JBQWtCLEVBQUM7SUFDdkgsWUFBWSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLGdCQUFnQixFQUFDO0lBQ3JGLFlBQVksSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsR0FBRyxDQUFDLEVBQUM7SUFDdEUsU0FBUztJQUNULFFBQVEsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEdBQUU7O0lBRTlCLFFBQVEsT0FBTyxJQUFJO0lBQ25CLEtBQUs7SUFDTDtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBLElBQUksWUFBWSxHQUFHOztJQUVuQixRQUFRLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxHQUFFO0lBQzlCLFFBQVEsSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUM7SUFDeEgsUUFBUSxJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLFNBQVMsRUFBQztJQUNyRixRQUFRLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFDO0lBQzdDLFFBQVEsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUM7SUFDL0QsUUFBUSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFDO0lBQzNGLFFBQVEsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUM7SUFDL0QsUUFBUSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUM7SUFDN0gsUUFBUSxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFDO0lBQzVELFFBQVEsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLElBQUks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 diff --git a/pixi/pixijs.code-workspace b/pixi/pixijs.code-workspace deleted file mode 100644 index 3a76f00..0000000 --- a/pixi/pixijs.code-workspace +++ /dev/null @@ -1,8 +0,0 @@ -{ - "folders": [ - { - "path": "./" - } - ], - "settings": {} -} \ No newline at end of file diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..eac5f49 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,22 @@ +// rollup.config.js (building more than one bundle) +export default [{ + input: './lib/bundle.js', + output: { + file: './dist/iwmlib.js', + format: 'iife', + sourcemap: false + }, + watch: { + clearScreen: false + } +}, { + input: './lib/pixi/bundle.js', + output: { + file: './dist/iwmlib.pixi.js', + format: 'iife', + sourcemap: false + }, + watch: { + clearScreen: false + } +}];