diff --git a/webapp2/css/style.css b/webapp2/css/style.css deleted file mode 100644 index e331e13..0000000 --- a/webapp2/css/style.css +++ /dev/null @@ -1,120 +0,0 @@ -/* ========================================================================== - - LET'S GET STARTED! - - ========================================================================== */ - -body, input, select, button { - font-size: 100%; - line-height: 1.5; - font-family: 'Montserrat', 'Proxima Nova', sans-serif; - color: #ccc; -} - -/* ========================================================================== - =BASE - ========================================================================== */ - -.hide { - /*display: none;*/ -} - -input { - padding: 1em; - background: #222; - border: none; - display: block; - width: 100%; - box-sizing: border-box; - transition: all .15s ease; -} - -input:hover { - background: #252525; -} - -input:focus { - border-color: #2962FF; - outline: none; -} - -.label--magic { - position: relative; - margin: 0 0 1.5em; - display: block; -} - -.label--magic > span { - position: absolute; - z-index: 2; - color: #555; - margin: 1em .625em; - padding: 0 .375em; - cursor: pointer; - top: 0; - transform-origin: 0 50%; - transition: all .3s ease; - border-radius: 2px; -} - -.label--magic > input { - position: relative; -} - -.label--magic input:focus { - background: #252525; -} - -.label--magic input:focus + span, -.label--magic input:not(:focus):valid + span { - transform: scale(.8); - background: #252525; - top: -2em; -} - - -button { - display: block; - width: 100%; - padding: 1em; - border: none; - color: white; - background: #2962FF; - cursor: pointer; - transition: all .15s ease; -} - -button:hover { - background: #2979FF; -} - - - -/* ========================================================================== - =LOGIN OVERLAY - ========================================================================== */ - -.login-overlay { - position: fixed; - padding: 0; - margin: 0; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(255, 255, 255, 0.2); - z-index: 9; -} - -.login-panel { - width: 300px; - padding: 2em; - margin: 2em auto; - background: #111; -} - -.login-icon { - max-width: 50%; - margin: 0 auto 1.5em; - display: block; -} \ No newline at end of file diff --git a/webapp2/favicon/android-icon-144x144.png b/webapp2/favicon/android-icon-144x144.png deleted file mode 100644 index 026dda7..0000000 Binary files a/webapp2/favicon/android-icon-144x144.png and /dev/null differ diff --git a/webapp2/favicon/android-icon-192x192.png b/webapp2/favicon/android-icon-192x192.png deleted file mode 100644 index e979adb..0000000 Binary files a/webapp2/favicon/android-icon-192x192.png and /dev/null differ diff --git a/webapp2/favicon/android-icon-36x36.png b/webapp2/favicon/android-icon-36x36.png deleted file mode 100644 index 365d2ce..0000000 Binary files a/webapp2/favicon/android-icon-36x36.png and /dev/null differ diff --git a/webapp2/favicon/android-icon-48x48.png b/webapp2/favicon/android-icon-48x48.png deleted file mode 100644 index 9c8fcda..0000000 Binary files a/webapp2/favicon/android-icon-48x48.png and /dev/null differ diff --git a/webapp2/favicon/android-icon-72x72.png b/webapp2/favicon/android-icon-72x72.png deleted file mode 100644 index 92fea11..0000000 Binary files a/webapp2/favicon/android-icon-72x72.png and /dev/null differ diff --git a/webapp2/favicon/android-icon-96x96.png b/webapp2/favicon/android-icon-96x96.png deleted file mode 100644 index 1083a56..0000000 Binary files a/webapp2/favicon/android-icon-96x96.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-114x114.png b/webapp2/favicon/apple-icon-114x114.png deleted file mode 100644 index 203654f..0000000 Binary files a/webapp2/favicon/apple-icon-114x114.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-120x120.png b/webapp2/favicon/apple-icon-120x120.png deleted file mode 100644 index ed0f48b..0000000 Binary files a/webapp2/favicon/apple-icon-120x120.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-144x144.png b/webapp2/favicon/apple-icon-144x144.png deleted file mode 100644 index 026dda7..0000000 Binary files a/webapp2/favicon/apple-icon-144x144.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-152x152.png b/webapp2/favicon/apple-icon-152x152.png deleted file mode 100644 index c89c29c..0000000 Binary files a/webapp2/favicon/apple-icon-152x152.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-180x180.png b/webapp2/favicon/apple-icon-180x180.png deleted file mode 100644 index 2b36f42..0000000 Binary files a/webapp2/favicon/apple-icon-180x180.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-57x57.png b/webapp2/favicon/apple-icon-57x57.png deleted file mode 100644 index a95a6c9..0000000 Binary files a/webapp2/favicon/apple-icon-57x57.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-60x60.png b/webapp2/favicon/apple-icon-60x60.png deleted file mode 100644 index d780125..0000000 Binary files a/webapp2/favicon/apple-icon-60x60.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-72x72.png b/webapp2/favicon/apple-icon-72x72.png deleted file mode 100644 index 92fea11..0000000 Binary files a/webapp2/favicon/apple-icon-72x72.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-76x76.png b/webapp2/favicon/apple-icon-76x76.png deleted file mode 100644 index 37cc3cd..0000000 Binary files a/webapp2/favicon/apple-icon-76x76.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon-precomposed.png b/webapp2/favicon/apple-icon-precomposed.png deleted file mode 100644 index 82e8da9..0000000 Binary files a/webapp2/favicon/apple-icon-precomposed.png and /dev/null differ diff --git a/webapp2/favicon/apple-icon.png b/webapp2/favicon/apple-icon.png deleted file mode 100644 index 82e8da9..0000000 Binary files a/webapp2/favicon/apple-icon.png and /dev/null differ diff --git a/webapp2/favicon/browserconfig.xml b/webapp2/favicon/browserconfig.xml deleted file mode 100644 index c554148..0000000 --- a/webapp2/favicon/browserconfig.xml +++ /dev/null @@ -1,2 +0,0 @@ - -#ffffff \ No newline at end of file diff --git a/webapp2/favicon/favicon-16x16.png b/webapp2/favicon/favicon-16x16.png deleted file mode 100644 index a00b064..0000000 Binary files a/webapp2/favicon/favicon-16x16.png and /dev/null differ diff --git a/webapp2/favicon/favicon-32x32.png b/webapp2/favicon/favicon-32x32.png deleted file mode 100644 index 07ad58d..0000000 Binary files a/webapp2/favicon/favicon-32x32.png and /dev/null differ diff --git a/webapp2/favicon/favicon-96x96.png b/webapp2/favicon/favicon-96x96.png deleted file mode 100644 index 1083a56..0000000 Binary files a/webapp2/favicon/favicon-96x96.png and /dev/null differ diff --git a/webapp2/favicon/favicon.ico b/webapp2/favicon/favicon.ico deleted file mode 100644 index c625e31..0000000 Binary files a/webapp2/favicon/favicon.ico and /dev/null differ diff --git a/webapp2/favicon/manifest.json b/webapp2/favicon/manifest.json deleted file mode 100644 index 013d4a6..0000000 --- a/webapp2/favicon/manifest.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "App", - "icons": [ - { - "src": "\/android-icon-36x36.png", - "sizes": "36x36", - "type": "image\/png", - "density": "0.75" - }, - { - "src": "\/android-icon-48x48.png", - "sizes": "48x48", - "type": "image\/png", - "density": "1.0" - }, - { - "src": "\/android-icon-72x72.png", - "sizes": "72x72", - "type": "image\/png", - "density": "1.5" - }, - { - "src": "\/android-icon-96x96.png", - "sizes": "96x96", - "type": "image\/png", - "density": "2.0" - }, - { - "src": "\/android-icon-144x144.png", - "sizes": "144x144", - "type": "image\/png", - "density": "3.0" - }, - { - "src": "\/android-icon-192x192.png", - "sizes": "192x192", - "type": "image\/png", - "density": "4.0" - } - ] -} \ No newline at end of file diff --git a/webapp2/favicon/ms-icon-144x144.png b/webapp2/favicon/ms-icon-144x144.png deleted file mode 100644 index 026dda7..0000000 Binary files a/webapp2/favicon/ms-icon-144x144.png and /dev/null differ diff --git a/webapp2/favicon/ms-icon-150x150.png b/webapp2/favicon/ms-icon-150x150.png deleted file mode 100644 index 58a2f87..0000000 Binary files a/webapp2/favicon/ms-icon-150x150.png and /dev/null differ diff --git a/webapp2/favicon/ms-icon-310x310.png b/webapp2/favicon/ms-icon-310x310.png deleted file mode 100644 index e2507b8..0000000 Binary files a/webapp2/favicon/ms-icon-310x310.png and /dev/null differ diff --git a/webapp2/favicon/ms-icon-70x70.png b/webapp2/favicon/ms-icon-70x70.png deleted file mode 100644 index 119c563..0000000 Binary files a/webapp2/favicon/ms-icon-70x70.png and /dev/null differ diff --git a/webapp2/img/mstream-icon.svg b/webapp2/img/mstream-icon.svg deleted file mode 100644 index a3d0190..0000000 --- a/webapp2/img/mstream-icon.svg +++ /dev/null @@ -1,32 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/webapp2/img/mstream-logo.png b/webapp2/img/mstream-logo.png deleted file mode 100644 index 36c2631..0000000 Binary files a/webapp2/img/mstream-logo.png and /dev/null differ diff --git a/webapp2/img/mstream-logo.svg b/webapp2/img/mstream-logo.svg deleted file mode 100644 index 164177d..0000000 --- a/webapp2/img/mstream-logo.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp2/img/mstream-logo.svgz b/webapp2/img/mstream-logo.svgz deleted file mode 100644 index bf19dca..0000000 Binary files a/webapp2/img/mstream-logo.svgz and /dev/null differ diff --git a/webapp2/js/lib/aurora.js b/webapp2/js/lib/aurora.js deleted file mode 100644 index 49bee2c..0000000 --- a/webapp2/js/lib/aurora.js +++ /dev/null @@ -1,4003 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.AV = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o> 3); - return this.bitPosition = pos & 7; - }; - - Bitstream.prototype.rewind = function(bits) { - var pos; - pos = this.bitPosition - bits; - this.stream.rewind(Math.abs(pos >> 3)); - return this.bitPosition = pos & 7; - }; - - Bitstream.prototype.seek = function(offset) { - var curOffset; - curOffset = this.offset(); - if (offset > curOffset) { - return this.advance(offset - curOffset); - } else if (offset < curOffset) { - return this.rewind(curOffset - offset); - } - }; - - Bitstream.prototype.align = function() { - if (this.bitPosition !== 0) { - this.bitPosition = 0; - return this.stream.advance(1); - } - }; - - Bitstream.prototype.read = function(bits, signed) { - var a, a0, a1, a2, a3, a4, mBits; - if (bits === 0) { - return 0; - } - mBits = bits + this.bitPosition; - if (mBits <= 8) { - a = ((this.stream.peekUInt8() << this.bitPosition) & 0xff) >>> (8 - bits); - } else if (mBits <= 16) { - a = ((this.stream.peekUInt16() << this.bitPosition) & 0xffff) >>> (16 - bits); - } else if (mBits <= 24) { - a = ((this.stream.peekUInt24() << this.bitPosition) & 0xffffff) >>> (24 - bits); - } else if (mBits <= 32) { - a = (this.stream.peekUInt32() << this.bitPosition) >>> (32 - bits); - } else if (mBits <= 40) { - a0 = this.stream.peekUInt8(0) * 0x0100000000; - a1 = this.stream.peekUInt8(1) << 24 >>> 0; - a2 = this.stream.peekUInt8(2) << 16; - a3 = this.stream.peekUInt8(3) << 8; - a4 = this.stream.peekUInt8(4); - a = a0 + a1 + a2 + a3 + a4; - a %= Math.pow(2, 40 - this.bitPosition); - a = Math.floor(a / Math.pow(2, 40 - this.bitPosition - bits)); - } else { - throw new Error("Too many bits!"); - } - if (signed) { - if (mBits < 32) { - if (a >>> (bits - 1)) { - a = ((1 << bits >>> 0) - a) * -1; - } - } else { - if (a / Math.pow(2, bits - 1) | 0) { - a = (Math.pow(2, bits) - a) * -1; - } - } - } - this.advance(bits); - return a; - }; - - Bitstream.prototype.peek = function(bits, signed) { - var a, a0, a1, a2, a3, a4, mBits; - if (bits === 0) { - return 0; - } - mBits = bits + this.bitPosition; - if (mBits <= 8) { - a = ((this.stream.peekUInt8() << this.bitPosition) & 0xff) >>> (8 - bits); - } else if (mBits <= 16) { - a = ((this.stream.peekUInt16() << this.bitPosition) & 0xffff) >>> (16 - bits); - } else if (mBits <= 24) { - a = ((this.stream.peekUInt24() << this.bitPosition) & 0xffffff) >>> (24 - bits); - } else if (mBits <= 32) { - a = (this.stream.peekUInt32() << this.bitPosition) >>> (32 - bits); - } else if (mBits <= 40) { - a0 = this.stream.peekUInt8(0) * 0x0100000000; - a1 = this.stream.peekUInt8(1) << 24 >>> 0; - a2 = this.stream.peekUInt8(2) << 16; - a3 = this.stream.peekUInt8(3) << 8; - a4 = this.stream.peekUInt8(4); - a = a0 + a1 + a2 + a3 + a4; - a %= Math.pow(2, 40 - this.bitPosition); - a = Math.floor(a / Math.pow(2, 40 - this.bitPosition - bits)); - } else { - throw new Error("Too many bits!"); - } - if (signed) { - if (mBits < 32) { - if (a >>> (bits - 1)) { - a = ((1 << bits >>> 0) - a) * -1; - } - } else { - if (a / Math.pow(2, bits - 1) | 0) { - a = (Math.pow(2, bits) - a) * -1; - } - } - } - return a; - }; - - Bitstream.prototype.readLSB = function(bits, signed) { - var a, mBits; - if (bits === 0) { - return 0; - } - if (bits > 40) { - throw new Error("Too many bits!"); - } - mBits = bits + this.bitPosition; - a = (this.stream.peekUInt8(0)) >>> this.bitPosition; - if (mBits > 8) { - a |= (this.stream.peekUInt8(1)) << (8 - this.bitPosition); - } - if (mBits > 16) { - a |= (this.stream.peekUInt8(2)) << (16 - this.bitPosition); - } - if (mBits > 24) { - a += (this.stream.peekUInt8(3)) << (24 - this.bitPosition) >>> 0; - } - if (mBits > 32) { - a += (this.stream.peekUInt8(4)) * Math.pow(2, 32 - this.bitPosition); - } - if (mBits >= 32) { - a %= Math.pow(2, bits); - } else { - a &= (1 << bits) - 1; - } - if (signed) { - if (mBits < 32) { - if (a >>> (bits - 1)) { - a = ((1 << bits >>> 0) - a) * -1; - } - } else { - if (a / Math.pow(2, bits - 1) | 0) { - a = (Math.pow(2, bits) - a) * -1; - } - } - } - this.advance(bits); - return a; - }; - - Bitstream.prototype.peekLSB = function(bits, signed) { - var a, mBits; - if (bits === 0) { - return 0; - } - if (bits > 40) { - throw new Error("Too many bits!"); - } - mBits = bits + this.bitPosition; - a = (this.stream.peekUInt8(0)) >>> this.bitPosition; - if (mBits > 8) { - a |= (this.stream.peekUInt8(1)) << (8 - this.bitPosition); - } - if (mBits > 16) { - a |= (this.stream.peekUInt8(2)) << (16 - this.bitPosition); - } - if (mBits > 24) { - a += (this.stream.peekUInt8(3)) << (24 - this.bitPosition) >>> 0; - } - if (mBits > 32) { - a += (this.stream.peekUInt8(4)) * Math.pow(2, 32 - this.bitPosition); - } - if (mBits >= 32) { - a %= Math.pow(2, bits); - } else { - a &= (1 << bits) - 1; - } - if (signed) { - if (mBits < 32) { - if (a >>> (bits - 1)) { - a = ((1 << bits >>> 0) - a) * -1; - } - } else { - if (a / Math.pow(2, bits - 1) | 0) { - a = (Math.pow(2, bits) - a) * -1; - } - } - } - return a; - }; - - return Bitstream; - - })(); - - module.exports = Bitstream; - -}).call(this); - -},{}],6:[function(require,module,exports){ -(function (global){ -// Generated by CoffeeScript 1.7.1 -(function() { - var AVBuffer; - - AVBuffer = (function() { - var BlobBuilder, URL; - - function AVBuffer(input) { - var _ref; - if (input instanceof Uint8Array) { - this.data = input; - } else if (input instanceof ArrayBuffer || Array.isArray(input) || typeof input === 'number' || ((_ref = global.Buffer) != null ? _ref.isBuffer(input) : void 0)) { - this.data = new Uint8Array(input); - } else if (input.buffer instanceof ArrayBuffer) { - this.data = new Uint8Array(input.buffer, input.byteOffset, input.length * input.BYTES_PER_ELEMENT); - } else if (input instanceof AVBuffer) { - this.data = input.data; - } else { - throw new Error("Constructing buffer with unknown type."); - } - this.length = this.data.length; - this.next = null; - this.prev = null; - } - - AVBuffer.allocate = function(size) { - return new AVBuffer(size); - }; - - AVBuffer.prototype.copy = function() { - return new AVBuffer(new Uint8Array(this.data)); - }; - - AVBuffer.prototype.slice = function(position, length) { - if (length == null) { - length = this.length; - } - if (position === 0 && length >= this.length) { - return new AVBuffer(this.data); - } else { - return new AVBuffer(this.data.subarray(position, position + length)); - } - }; - - BlobBuilder = global.BlobBuilder || global.MozBlobBuilder || global.WebKitBlobBuilder; - - URL = global.URL || global.webkitURL || global.mozURL; - - AVBuffer.makeBlob = function(data, type) { - var bb; - if (type == null) { - type = 'application/octet-stream'; - } - try { - return new Blob([data], { - type: type - }); - } catch (_error) {} - if (BlobBuilder != null) { - bb = new BlobBuilder; - bb.append(data); - return bb.getBlob(type); - } - return null; - }; - - AVBuffer.makeBlobURL = function(data, type) { - return URL != null ? URL.createObjectURL(this.makeBlob(data, type)) : void 0; - }; - - AVBuffer.revokeBlobURL = function(url) { - return URL != null ? URL.revokeObjectURL(url) : void 0; - }; - - AVBuffer.prototype.toBlob = function() { - return AVBuffer.makeBlob(this.data.buffer); - }; - - AVBuffer.prototype.toBlobURL = function() { - return AVBuffer.makeBlobURL(this.data.buffer); - }; - - return AVBuffer; - - })(); - - module.exports = AVBuffer; - -}).call(this); - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{}],7:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var BufferList; - - BufferList = (function() { - function BufferList() { - this.first = null; - this.last = null; - this.numBuffers = 0; - this.availableBytes = 0; - this.availableBuffers = 0; - } - - BufferList.prototype.copy = function() { - var result; - result = new BufferList; - result.first = this.first; - result.last = this.last; - result.numBuffers = this.numBuffers; - result.availableBytes = this.availableBytes; - result.availableBuffers = this.availableBuffers; - return result; - }; - - BufferList.prototype.append = function(buffer) { - var _ref; - buffer.prev = this.last; - if ((_ref = this.last) != null) { - _ref.next = buffer; - } - this.last = buffer; - if (this.first == null) { - this.first = buffer; - } - this.availableBytes += buffer.length; - this.availableBuffers++; - return this.numBuffers++; - }; - - BufferList.prototype.advance = function() { - if (this.first) { - this.availableBytes -= this.first.length; - this.availableBuffers--; - this.first = this.first.next; - return this.first != null; - } - return false; - }; - - BufferList.prototype.rewind = function() { - var _ref; - if (this.first && !this.first.prev) { - return false; - } - this.first = ((_ref = this.first) != null ? _ref.prev : void 0) || this.last; - if (this.first) { - this.availableBytes += this.first.length; - this.availableBuffers++; - } - return this.first != null; - }; - - BufferList.prototype.reset = function() { - var _results; - _results = []; - while (this.rewind()) { - continue; - } - return _results; - }; - - return BufferList; - - })(); - - module.exports = BufferList; - -}).call(this); - -},{}],8:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var Base, EventEmitter, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - __slice = [].slice; - - Base = require('./base'); - - EventEmitter = (function(_super) { - __extends(EventEmitter, _super); - - function EventEmitter() { - return EventEmitter.__super__.constructor.apply(this, arguments); - } - - EventEmitter.prototype.on = function(event, fn) { - var _base; - if (this.events == null) { - this.events = {}; - } - if ((_base = this.events)[event] == null) { - _base[event] = []; - } - return this.events[event].push(fn); - }; - - EventEmitter.prototype.off = function(event, fn) { - var events, index, _ref; - if (this.events == null) { - return; - } - if ((_ref = this.events) != null ? _ref[event] : void 0) { - if (fn != null) { - index = this.events[event].indexOf(fn); - if (~index) { - return this.events[event].splice(index, 1); - } - } else { - return this.events[event]; - } - } else if (event == null) { - return events = {}; - } - }; - - EventEmitter.prototype.once = function(event, fn) { - var cb; - return this.on(event, cb = function() { - this.off(event, cb); - return fn.apply(this, arguments); - }); - }; - - EventEmitter.prototype.emit = function() { - var args, event, fn, _i, _len, _ref, _ref1; - event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; - if (!((_ref = this.events) != null ? _ref[event] : void 0)) { - return; - } - _ref1 = this.events[event].slice(); - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - fn = _ref1[_i]; - fn.apply(this, args); - } - }; - - return EventEmitter; - - })(Base); - - module.exports = EventEmitter; - -}).call(this); - -},{"./base":4}],9:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var AVBuffer, BufferList, Stream, UnderflowError; - - BufferList = require('./bufferlist'); - - AVBuffer = require('./buffer'); - - UnderflowError = require('./underflow'); - - Stream = (function() { - var buf, decodeString, float32, float64, float64Fallback, float80, int16, int32, int8, nativeEndian, uint16, uint32, uint8; - - buf = new ArrayBuffer(16); - - uint8 = new Uint8Array(buf); - - int8 = new Int8Array(buf); - - uint16 = new Uint16Array(buf); - - int16 = new Int16Array(buf); - - uint32 = new Uint32Array(buf); - - int32 = new Int32Array(buf); - - float32 = new Float32Array(buf); - - if (typeof Float64Array !== "undefined" && Float64Array !== null) { - float64 = new Float64Array(buf); - } - - nativeEndian = new Uint16Array(new Uint8Array([0x12, 0x34]).buffer)[0] === 0x3412; - - function Stream(list) { - this.list = list; - this.localOffset = 0; - this.offset = 0; - } - - Stream.fromBuffer = function(buffer) { - var list; - list = new BufferList; - list.append(buffer); - return new Stream(list); - }; - - Stream.prototype.copy = function() { - var result; - result = new Stream(this.list.copy()); - result.localOffset = this.localOffset; - result.offset = this.offset; - return result; - }; - - Stream.prototype.available = function(bytes) { - return bytes <= this.list.availableBytes - this.localOffset; - }; - - Stream.prototype.remainingBytes = function() { - return this.list.availableBytes - this.localOffset; - }; - - Stream.prototype.advance = function(bytes) { - if (!this.available(bytes)) { - throw new UnderflowError(); - } - this.localOffset += bytes; - this.offset += bytes; - while (this.list.first && this.localOffset >= this.list.first.length) { - this.localOffset -= this.list.first.length; - this.list.advance(); - } - return this; - }; - - Stream.prototype.rewind = function(bytes) { - if (bytes > this.offset) { - throw new UnderflowError(); - } - if (!this.list.first) { - this.list.rewind(); - this.localOffset = this.list.first.length; - } - this.localOffset -= bytes; - this.offset -= bytes; - while (this.list.first.prev && this.localOffset < 0) { - this.list.rewind(); - this.localOffset += this.list.first.length; - } - return this; - }; - - Stream.prototype.seek = function(position) { - if (position > this.offset) { - return this.advance(position - this.offset); - } else if (position < this.offset) { - return this.rewind(this.offset - position); - } - }; - - Stream.prototype.readUInt8 = function() { - var a; - if (!this.available(1)) { - throw new UnderflowError(); - } - a = this.list.first.data[this.localOffset]; - this.localOffset += 1; - this.offset += 1; - if (this.localOffset === this.list.first.length) { - this.localOffset = 0; - this.list.advance(); - } - return a; - }; - - Stream.prototype.peekUInt8 = function(offset) { - var buffer; - if (offset == null) { - offset = 0; - } - if (!this.available(offset + 1)) { - throw new UnderflowError(); - } - offset = this.localOffset + offset; - buffer = this.list.first; - while (buffer) { - if (buffer.length > offset) { - return buffer.data[offset]; - } - offset -= buffer.length; - buffer = buffer.next; - } - return 0; - }; - - Stream.prototype.read = function(bytes, littleEndian) { - var i, _i, _j, _ref; - if (littleEndian == null) { - littleEndian = false; - } - if (littleEndian === nativeEndian) { - for (i = _i = 0; _i < bytes; i = _i += 1) { - uint8[i] = this.readUInt8(); - } - } else { - for (i = _j = _ref = bytes - 1; _j >= 0; i = _j += -1) { - uint8[i] = this.readUInt8(); - } - } - }; - - Stream.prototype.peek = function(bytes, offset, littleEndian) { - var i, _i, _j; - if (littleEndian == null) { - littleEndian = false; - } - if (littleEndian === nativeEndian) { - for (i = _i = 0; _i < bytes; i = _i += 1) { - uint8[i] = this.peekUInt8(offset + i); - } - } else { - for (i = _j = 0; _j < bytes; i = _j += 1) { - uint8[bytes - i - 1] = this.peekUInt8(offset + i); - } - } - }; - - Stream.prototype.readInt8 = function() { - this.read(1); - return int8[0]; - }; - - Stream.prototype.peekInt8 = function(offset) { - if (offset == null) { - offset = 0; - } - this.peek(1, offset); - return int8[0]; - }; - - Stream.prototype.readUInt16 = function(littleEndian) { - this.read(2, littleEndian); - return uint16[0]; - }; - - Stream.prototype.peekUInt16 = function(offset, littleEndian) { - if (offset == null) { - offset = 0; - } - this.peek(2, offset, littleEndian); - return uint16[0]; - }; - - Stream.prototype.readInt16 = function(littleEndian) { - this.read(2, littleEndian); - return int16[0]; - }; - - Stream.prototype.peekInt16 = function(offset, littleEndian) { - if (offset == null) { - offset = 0; - } - this.peek(2, offset, littleEndian); - return int16[0]; - }; - - Stream.prototype.readUInt24 = function(littleEndian) { - if (littleEndian) { - return this.readUInt16(true) + (this.readUInt8() << 16); - } else { - return (this.readUInt16() << 8) + this.readUInt8(); - } - }; - - Stream.prototype.peekUInt24 = function(offset, littleEndian) { - if (offset == null) { - offset = 0; - } - if (littleEndian) { - return this.peekUInt16(offset, true) + (this.peekUInt8(offset + 2) << 16); - } else { - return (this.peekUInt16(offset) << 8) + this.peekUInt8(offset + 2); - } - }; - - Stream.prototype.readInt24 = function(littleEndian) { - if (littleEndian) { - return this.readUInt16(true) + (this.readInt8() << 16); - } else { - return (this.readInt16() << 8) + this.readUInt8(); - } - }; - - Stream.prototype.peekInt24 = function(offset, littleEndian) { - if (offset == null) { - offset = 0; - } - if (littleEndian) { - return this.peekUInt16(offset, true) + (this.peekInt8(offset + 2) << 16); - } else { - return (this.peekInt16(offset) << 8) + this.peekUInt8(offset + 2); - } - }; - - Stream.prototype.readUInt32 = function(littleEndian) { - this.read(4, littleEndian); - return uint32[0]; - }; - - Stream.prototype.peekUInt32 = function(offset, littleEndian) { - if (offset == null) { - offset = 0; - } - this.peek(4, offset, littleEndian); - return uint32[0]; - }; - - Stream.prototype.readInt32 = function(littleEndian) { - this.read(4, littleEndian); - return int32[0]; - }; - - Stream.prototype.peekInt32 = function(offset, littleEndian) { - if (offset == null) { - offset = 0; - } - this.peek(4, offset, littleEndian); - return int32[0]; - }; - - Stream.prototype.readFloat32 = function(littleEndian) { - this.read(4, littleEndian); - return float32[0]; - }; - - Stream.prototype.peekFloat32 = function(offset, littleEndian) { - if (offset == null) { - offset = 0; - } - this.peek(4, offset, littleEndian); - return float32[0]; - }; - - Stream.prototype.readFloat64 = function(littleEndian) { - this.read(8, littleEndian); - if (float64) { - return float64[0]; - } else { - return float64Fallback(); - } - }; - - float64Fallback = function() { - var exp, frac, high, low, out, sign; - low = uint32[0], high = uint32[1]; - if (!high || high === 0x80000000) { - return 0.0; - } - sign = 1 - (high >>> 31) * 2; - exp = (high >>> 20) & 0x7ff; - frac = high & 0xfffff; - if (exp === 0x7ff) { - if (frac) { - return NaN; - } - return sign * Infinity; - } - exp -= 1023; - out = (frac | 0x100000) * Math.pow(2, exp - 20); - out += low * Math.pow(2, exp - 52); - return sign * out; - }; - - Stream.prototype.peekFloat64 = function(offset, littleEndian) { - if (offset == null) { - offset = 0; - } - this.peek(8, offset, littleEndian); - if (float64) { - return float64[0]; - } else { - return float64Fallback(); - } - }; - - Stream.prototype.readFloat80 = function(littleEndian) { - this.read(10, littleEndian); - return float80(); - }; - - float80 = function() { - var a0, a1, exp, high, low, out, sign; - high = uint32[0], low = uint32[1]; - a0 = uint8[9]; - a1 = uint8[8]; - sign = 1 - (a0 >>> 7) * 2; - exp = ((a0 & 0x7F) << 8) | a1; - if (exp === 0 && low === 0 && high === 0) { - return 0; - } - if (exp === 0x7fff) { - if (low === 0 && high === 0) { - return sign * Infinity; - } - return NaN; - } - exp -= 16383; - out = low * Math.pow(2, exp - 31); - out += high * Math.pow(2, exp - 63); - return sign * out; - }; - - Stream.prototype.peekFloat80 = function(offset, littleEndian) { - if (offset == null) { - offset = 0; - } - this.peek(10, offset, littleEndian); - return float80(); - }; - - Stream.prototype.readBuffer = function(length) { - var i, result, to, _i; - result = AVBuffer.allocate(length); - to = result.data; - for (i = _i = 0; _i < length; i = _i += 1) { - to[i] = this.readUInt8(); - } - return result; - }; - - Stream.prototype.peekBuffer = function(offset, length) { - var i, result, to, _i; - if (offset == null) { - offset = 0; - } - result = AVBuffer.allocate(length); - to = result.data; - for (i = _i = 0; _i < length; i = _i += 1) { - to[i] = this.peekUInt8(offset + i); - } - return result; - }; - - Stream.prototype.readSingleBuffer = function(length) { - var result; - result = this.list.first.slice(this.localOffset, length); - this.advance(result.length); - return result; - }; - - Stream.prototype.peekSingleBuffer = function(offset, length) { - var result; - result = this.list.first.slice(this.localOffset + offset, length); - return result; - }; - - Stream.prototype.readString = function(length, encoding) { - if (encoding == null) { - encoding = 'ascii'; - } - return decodeString.call(this, 0, length, encoding, true); - }; - - Stream.prototype.peekString = function(offset, length, encoding) { - if (offset == null) { - offset = 0; - } - if (encoding == null) { - encoding = 'ascii'; - } - return decodeString.call(this, offset, length, encoding, false); - }; - - decodeString = function(offset, length, encoding, advance) { - var b1, b2, b3, b4, bom, c, end, littleEndian, nullEnd, pt, result, w1, w2; - encoding = encoding.toLowerCase(); - nullEnd = length === null ? 0 : -1; - if (length == null) { - length = Infinity; - } - end = offset + length; - result = ''; - switch (encoding) { - case 'ascii': - case 'latin1': - while (offset < end && (c = this.peekUInt8(offset++)) !== nullEnd) { - result += String.fromCharCode(c); - } - break; - case 'utf8': - case 'utf-8': - while (offset < end && (b1 = this.peekUInt8(offset++)) !== nullEnd) { - if ((b1 & 0x80) === 0) { - result += String.fromCharCode(b1); - } else if ((b1 & 0xe0) === 0xc0) { - b2 = this.peekUInt8(offset++) & 0x3f; - result += String.fromCharCode(((b1 & 0x1f) << 6) | b2); - } else if ((b1 & 0xf0) === 0xe0) { - b2 = this.peekUInt8(offset++) & 0x3f; - b3 = this.peekUInt8(offset++) & 0x3f; - result += String.fromCharCode(((b1 & 0x0f) << 12) | (b2 << 6) | b3); - } else if ((b1 & 0xf8) === 0xf0) { - b2 = this.peekUInt8(offset++) & 0x3f; - b3 = this.peekUInt8(offset++) & 0x3f; - b4 = this.peekUInt8(offset++) & 0x3f; - pt = (((b1 & 0x0f) << 18) | (b2 << 12) | (b3 << 6) | b4) - 0x10000; - result += String.fromCharCode(0xd800 + (pt >> 10), 0xdc00 + (pt & 0x3ff)); - } - } - break; - case 'utf16-be': - case 'utf16be': - case 'utf16le': - case 'utf16-le': - case 'utf16bom': - case 'utf16-bom': - switch (encoding) { - case 'utf16be': - case 'utf16-be': - littleEndian = false; - break; - case 'utf16le': - case 'utf16-le': - littleEndian = true; - break; - case 'utf16bom': - case 'utf16-bom': - if (length < 2 || (bom = this.peekUInt16(offset)) === nullEnd) { - if (advance) { - this.advance(offset += 2); - } - return result; - } - littleEndian = bom === 0xfffe; - offset += 2; - } - while (offset < end && (w1 = this.peekUInt16(offset, littleEndian)) !== nullEnd) { - offset += 2; - if (w1 < 0xd800 || w1 > 0xdfff) { - result += String.fromCharCode(w1); - } else { - if (w1 > 0xdbff) { - throw new Error("Invalid utf16 sequence."); - } - w2 = this.peekUInt16(offset, littleEndian); - if (w2 < 0xdc00 || w2 > 0xdfff) { - throw new Error("Invalid utf16 sequence."); - } - result += String.fromCharCode(w1, w2); - offset += 2; - } - } - if (w1 === nullEnd) { - offset += 2; - } - break; - default: - throw new Error("Unknown encoding: " + encoding); - } - if (advance) { - this.advance(offset); - } - return result; - }; - - return Stream; - - })(); - - module.exports = Stream; - -}).call(this); - -},{"./buffer":6,"./bufferlist":7,"./underflow":10}],10:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var UnderflowError, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - UnderflowError = (function(_super) { - __extends(UnderflowError, _super); - - function UnderflowError() { - UnderflowError.__super__.constructor.apply(this, arguments); - this.name = 'UnderflowError'; - this.stack = new Error().stack; - } - - return UnderflowError; - - })(Error); - - module.exports = UnderflowError; - -}).call(this); - -},{}],11:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var Bitstream, BufferList, Decoder, EventEmitter, Stream, UnderflowError, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - EventEmitter = require('./core/events'); - - BufferList = require('./core/bufferlist'); - - Stream = require('./core/stream'); - - Bitstream = require('./core/bitstream'); - - UnderflowError = require('./core/underflow'); - - Decoder = (function(_super) { - var codecs; - - __extends(Decoder, _super); - - function Decoder(demuxer, format) { - var list; - this.demuxer = demuxer; - this.format = format; - list = new BufferList; - this.stream = new Stream(list); - this.bitstream = new Bitstream(this.stream); - this.receivedFinalBuffer = false; - this.waiting = false; - this.demuxer.on('cookie', (function(_this) { - return function(cookie) { - var error; - try { - return _this.setCookie(cookie); - } catch (_error) { - error = _error; - return _this.emit('error', error); - } - }; - })(this)); - this.demuxer.on('data', (function(_this) { - return function(chunk) { - list.append(chunk); - if (_this.waiting) { - return _this.decode(); - } - }; - })(this)); - this.demuxer.on('end', (function(_this) { - return function() { - _this.receivedFinalBuffer = true; - if (_this.waiting) { - return _this.decode(); - } - }; - })(this)); - this.init(); - } - - Decoder.prototype.init = function() {}; - - Decoder.prototype.setCookie = function(cookie) {}; - - Decoder.prototype.readChunk = function() {}; - - Decoder.prototype.decode = function() { - var error, offset, packet; - this.waiting = !this.receivedFinalBuffer; - offset = this.bitstream.offset(); - try { - packet = this.readChunk(); - } catch (_error) { - error = _error; - if (!(error instanceof UnderflowError)) { - this.emit('error', error); - return false; - } - } - if (packet) { - this.emit('data', packet); - if (this.receivedFinalBuffer) { - this.emit('end'); - } - return true; - } else if (!this.receivedFinalBuffer) { - this.bitstream.seek(offset); - this.waiting = true; - } else { - this.emit('end'); - } - return false; - }; - - Decoder.prototype.seek = function(timestamp) { - var seekPoint; - seekPoint = this.demuxer.seek(timestamp); - this.stream.seek(seekPoint.offset); - return seekPoint.timestamp; - }; - - codecs = {}; - - Decoder.register = function(id, decoder) { - return codecs[id] = decoder; - }; - - Decoder.find = function(id) { - return codecs[id] || null; - }; - - return Decoder; - - })(EventEmitter); - - module.exports = Decoder; - -}).call(this); - -},{"./core/bitstream":5,"./core/bufferlist":7,"./core/events":8,"./core/stream":9,"./core/underflow":10}],12:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var Decoder, LPCMDecoder, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - Decoder = require('../decoder'); - - LPCMDecoder = (function(_super) { - __extends(LPCMDecoder, _super); - - function LPCMDecoder() { - this.readChunk = __bind(this.readChunk, this); - return LPCMDecoder.__super__.constructor.apply(this, arguments); - } - - Decoder.register('lpcm', LPCMDecoder); - - LPCMDecoder.prototype.readChunk = function() { - var chunkSize, i, littleEndian, output, samples, stream, _i, _j, _k, _l, _m, _n; - stream = this.stream; - littleEndian = this.format.littleEndian; - chunkSize = Math.min(4096, stream.remainingBytes()); - samples = chunkSize / (this.format.bitsPerChannel / 8) | 0; - if (chunkSize < this.format.bitsPerChannel / 8) { - return null; - } - if (this.format.floatingPoint) { - switch (this.format.bitsPerChannel) { - case 32: - output = new Float32Array(samples); - for (i = _i = 0; _i < samples; i = _i += 1) { - output[i] = stream.readFloat32(littleEndian); - } - break; - case 64: - output = new Float64Array(samples); - for (i = _j = 0; _j < samples; i = _j += 1) { - output[i] = stream.readFloat64(littleEndian); - } - break; - default: - throw new Error('Unsupported bit depth.'); - } - } else { - switch (this.format.bitsPerChannel) { - case 8: - output = new Int8Array(samples); - for (i = _k = 0; _k < samples; i = _k += 1) { - output[i] = stream.readInt8(); - } - break; - case 16: - output = new Int16Array(samples); - for (i = _l = 0; _l < samples; i = _l += 1) { - output[i] = stream.readInt16(littleEndian); - } - break; - case 24: - output = new Int32Array(samples); - for (i = _m = 0; _m < samples; i = _m += 1) { - output[i] = stream.readInt24(littleEndian); - } - break; - case 32: - output = new Int32Array(samples); - for (i = _n = 0; _n < samples; i = _n += 1) { - output[i] = stream.readInt32(littleEndian); - } - break; - default: - throw new Error('Unsupported bit depth.'); - } - } - return output; - }; - - return LPCMDecoder; - - })(Decoder); - -}).call(this); - -},{"../decoder":11}],13:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var Decoder, XLAWDecoder, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - Decoder = require('../decoder'); - - XLAWDecoder = (function(_super) { - var BIAS, QUANT_MASK, SEG_MASK, SEG_SHIFT, SIGN_BIT; - - __extends(XLAWDecoder, _super); - - function XLAWDecoder() { - this.readChunk = __bind(this.readChunk, this); - return XLAWDecoder.__super__.constructor.apply(this, arguments); - } - - Decoder.register('ulaw', XLAWDecoder); - - Decoder.register('alaw', XLAWDecoder); - - SIGN_BIT = 0x80; - - QUANT_MASK = 0xf; - - SEG_SHIFT = 4; - - SEG_MASK = 0x70; - - BIAS = 0x84; - - XLAWDecoder.prototype.init = function() { - var i, seg, t, table, val, _i, _j; - this.format.bitsPerChannel = 16; - this.table = table = new Int16Array(256); - if (this.format.formatID === 'ulaw') { - for (i = _i = 0; _i < 256; i = ++_i) { - val = ~i; - t = ((val & QUANT_MASK) << 3) + BIAS; - t <<= (val & SEG_MASK) >>> SEG_SHIFT; - table[i] = val & SIGN_BIT ? BIAS - t : t - BIAS; - } - } else { - for (i = _j = 0; _j < 256; i = ++_j) { - val = i ^ 0x55; - t = val & QUANT_MASK; - seg = (val & SEG_MASK) >>> SEG_SHIFT; - if (seg) { - t = (t + t + 1 + 32) << (seg + 2); - } else { - t = (t + t + 1) << 3; - } - table[i] = val & SIGN_BIT ? t : -t; - } - } - }; - - XLAWDecoder.prototype.readChunk = function() { - var i, output, samples, stream, table, _i; - stream = this.stream, table = this.table; - samples = Math.min(4096, this.stream.remainingBytes()); - if (samples === 0) { - return; - } - output = new Int16Array(samples); - for (i = _i = 0; _i < samples; i = _i += 1) { - output[i] = table[stream.readUInt8()]; - } - return output; - }; - - return XLAWDecoder; - - })(Decoder); - -}).call(this); - -},{"../decoder":11}],14:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var BufferList, Demuxer, EventEmitter, Stream, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - EventEmitter = require('./core/events'); - - BufferList = require('./core/bufferlist'); - - Stream = require('./core/stream'); - - Demuxer = (function(_super) { - var formats; - - __extends(Demuxer, _super); - - Demuxer.probe = function(buffer) { - return false; - }; - - function Demuxer(source, chunk) { - var list, received; - list = new BufferList; - list.append(chunk); - this.stream = new Stream(list); - received = false; - source.on('data', (function(_this) { - return function(chunk) { - var e; - received = true; - list.append(chunk); - try { - return _this.readChunk(chunk); - } catch (_error) { - e = _error; - return _this.emit('error', e); - } - }; - })(this)); - source.on('error', (function(_this) { - return function(err) { - return _this.emit('error', err); - }; - })(this)); - source.on('end', (function(_this) { - return function() { - if (!received) { - _this.readChunk(chunk); - } - return _this.emit('end'); - }; - })(this)); - this.seekPoints = []; - this.init(); - } - - Demuxer.prototype.init = function() {}; - - Demuxer.prototype.readChunk = function(chunk) {}; - - Demuxer.prototype.addSeekPoint = function(offset, timestamp) { - var index; - index = this.searchTimestamp(timestamp); - return this.seekPoints.splice(index, 0, { - offset: offset, - timestamp: timestamp - }); - }; - - Demuxer.prototype.searchTimestamp = function(timestamp, backward) { - var high, low, mid, time; - low = 0; - high = this.seekPoints.length; - if (high > 0 && this.seekPoints[high - 1].timestamp < timestamp) { - return high; - } - while (low < high) { - mid = (low + high) >> 1; - time = this.seekPoints[mid].timestamp; - if (time < timestamp) { - low = mid + 1; - } else if (time >= timestamp) { - high = mid; - } - } - if (high > this.seekPoints.length) { - high = this.seekPoints.length; - } - return high; - }; - - Demuxer.prototype.seek = function(timestamp) { - var index, seekPoint; - if (this.format && this.format.framesPerPacket > 0 && this.format.bytesPerPacket > 0) { - seekPoint = { - timestamp: timestamp, - offset: this.format.bytesPerPacket * timestamp / this.format.framesPerPacket - }; - return seekPoint; - } else { - index = this.searchTimestamp(timestamp); - return this.seekPoints[index]; - } - }; - - formats = []; - - Demuxer.register = function(demuxer) { - return formats.push(demuxer); - }; - - Demuxer.find = function(buffer) { - var e, format, offset, stream, _i, _len; - stream = Stream.fromBuffer(buffer); - for (_i = 0, _len = formats.length; _i < _len; _i++) { - format = formats[_i]; - offset = stream.offset; - try { - if (format.probe(stream)) { - return format; - } - } catch (_error) { - e = _error; - } - stream.seek(offset); - } - return null; - }; - - return Demuxer; - - })(EventEmitter); - - module.exports = Demuxer; - -}).call(this); - -},{"./core/bufferlist":7,"./core/events":8,"./core/stream":9}],15:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var AIFFDemuxer, Demuxer, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - Demuxer = require('../demuxer'); - - AIFFDemuxer = (function(_super) { - __extends(AIFFDemuxer, _super); - - function AIFFDemuxer() { - return AIFFDemuxer.__super__.constructor.apply(this, arguments); - } - - Demuxer.register(AIFFDemuxer); - - AIFFDemuxer.probe = function(buffer) { - var _ref; - return buffer.peekString(0, 4) === 'FORM' && ((_ref = buffer.peekString(8, 4)) === 'AIFF' || _ref === 'AIFC'); - }; - - AIFFDemuxer.prototype.readChunk = function() { - var buffer, format, offset, _ref; - if (!this.readStart && this.stream.available(12)) { - if (this.stream.readString(4) !== 'FORM') { - return this.emit('error', 'Invalid AIFF.'); - } - this.fileSize = this.stream.readUInt32(); - this.fileType = this.stream.readString(4); - this.readStart = true; - if ((_ref = this.fileType) !== 'AIFF' && _ref !== 'AIFC') { - return this.emit('error', 'Invalid AIFF.'); - } - } - while (this.stream.available(1)) { - if (!this.readHeaders && this.stream.available(8)) { - this.type = this.stream.readString(4); - this.len = this.stream.readUInt32(); - } - switch (this.type) { - case 'COMM': - if (!this.stream.available(this.len)) { - return; - } - this.format = { - formatID: 'lpcm', - channelsPerFrame: this.stream.readUInt16(), - sampleCount: this.stream.readUInt32(), - bitsPerChannel: this.stream.readUInt16(), - sampleRate: this.stream.readFloat80(), - framesPerPacket: 1, - littleEndian: false, - floatingPoint: false - }; - this.format.bytesPerPacket = (this.format.bitsPerChannel / 8) * this.format.channelsPerFrame; - if (this.fileType === 'AIFC') { - format = this.stream.readString(4); - this.format.littleEndian = format === 'sowt' && this.format.bitsPerChannel > 8; - this.format.floatingPoint = format === 'fl32' || format === 'fl64'; - if (format === 'twos' || format === 'sowt' || format === 'fl32' || format === 'fl64' || format === 'NONE') { - format = 'lpcm'; - } - this.format.formatID = format; - this.len -= 4; - } - this.stream.advance(this.len - 18); - this.emit('format', this.format); - this.emit('duration', this.format.sampleCount / this.format.sampleRate * 1000 | 0); - break; - case 'SSND': - if (!(this.readSSNDHeader && this.stream.available(4))) { - offset = this.stream.readUInt32(); - this.stream.advance(4); - this.stream.advance(offset); - this.readSSNDHeader = true; - } - buffer = this.stream.readSingleBuffer(this.len); - this.len -= buffer.length; - this.readHeaders = this.len > 0; - this.emit('data', buffer); - break; - default: - if (!this.stream.available(this.len)) { - return; - } - this.stream.advance(this.len); - } - if (this.type !== 'SSND') { - this.readHeaders = false; - } - } - }; - - return AIFFDemuxer; - - })(Demuxer); - -}).call(this); - -},{"../demuxer":14}],16:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var AUDemuxer, Demuxer, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - Demuxer = require('../demuxer'); - - AUDemuxer = (function(_super) { - var bps, formats; - - __extends(AUDemuxer, _super); - - function AUDemuxer() { - return AUDemuxer.__super__.constructor.apply(this, arguments); - } - - Demuxer.register(AUDemuxer); - - AUDemuxer.probe = function(buffer) { - return buffer.peekString(0, 4) === '.snd'; - }; - - bps = [8, 8, 16, 24, 32, 32, 64]; - - bps[26] = 8; - - formats = { - 1: 'ulaw', - 27: 'alaw' - }; - - AUDemuxer.prototype.readChunk = function() { - var bytes, dataSize, encoding, size; - if (!this.readHeader && this.stream.available(24)) { - if (this.stream.readString(4) !== '.snd') { - return this.emit('error', 'Invalid AU file.'); - } - size = this.stream.readUInt32(); - dataSize = this.stream.readUInt32(); - encoding = this.stream.readUInt32(); - this.format = { - formatID: formats[encoding] || 'lpcm', - littleEndian: false, - floatingPoint: encoding === 6 || encoding === 7, - bitsPerChannel: bps[encoding - 1], - sampleRate: this.stream.readUInt32(), - channelsPerFrame: this.stream.readUInt32(), - framesPerPacket: 1 - }; - if (this.format.bitsPerChannel == null) { - return this.emit('error', 'Unsupported encoding in AU file.'); - } - this.format.bytesPerPacket = (this.format.bitsPerChannel / 8) * this.format.channelsPerFrame; - if (dataSize !== 0xffffffff) { - bytes = this.format.bitsPerChannel / 8; - this.emit('duration', dataSize / bytes / this.format.channelsPerFrame / this.format.sampleRate * 1000 | 0); - } - this.emit('format', this.format); - this.readHeader = true; - } - if (this.readHeader) { - while (this.stream.available(1)) { - this.emit('data', this.stream.readSingleBuffer(this.stream.remainingBytes())); - } - } - }; - - return AUDemuxer; - - })(Demuxer); - -}).call(this); - -},{"../demuxer":14}],17:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var CAFDemuxer, Demuxer, M4ADemuxer, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - Demuxer = require('../demuxer'); - - M4ADemuxer = require('./m4a'); - - CAFDemuxer = (function(_super) { - __extends(CAFDemuxer, _super); - - function CAFDemuxer() { - return CAFDemuxer.__super__.constructor.apply(this, arguments); - } - - Demuxer.register(CAFDemuxer); - - CAFDemuxer.probe = function(buffer) { - return buffer.peekString(0, 4) === 'caff'; - }; - - CAFDemuxer.prototype.readChunk = function() { - var buffer, byteOffset, cookie, entries, flags, i, key, metadata, offset, sampleOffset, value, _i, _j, _ref; - if (!this.format && this.stream.available(64)) { - if (this.stream.readString(4) !== 'caff') { - return this.emit('error', "Invalid CAF, does not begin with 'caff'"); - } - this.stream.advance(4); - if (this.stream.readString(4) !== 'desc') { - return this.emit('error', "Invalid CAF, 'caff' is not followed by 'desc'"); - } - if (!(this.stream.readUInt32() === 0 && this.stream.readUInt32() === 32)) { - return this.emit('error', "Invalid 'desc' size, should be 32"); - } - this.format = {}; - this.format.sampleRate = this.stream.readFloat64(); - this.format.formatID = this.stream.readString(4); - flags = this.stream.readUInt32(); - if (this.format.formatID === 'lpcm') { - this.format.floatingPoint = Boolean(flags & 1); - this.format.littleEndian = Boolean(flags & 2); - } - this.format.bytesPerPacket = this.stream.readUInt32(); - this.format.framesPerPacket = this.stream.readUInt32(); - this.format.channelsPerFrame = this.stream.readUInt32(); - this.format.bitsPerChannel = this.stream.readUInt32(); - this.emit('format', this.format); - } - while (this.stream.available(1)) { - if (!this.headerCache) { - this.headerCache = { - type: this.stream.readString(4), - oversize: this.stream.readUInt32() !== 0, - size: this.stream.readUInt32() - }; - if (this.headerCache.oversize) { - return this.emit('error', "Holy Shit, an oversized file, not supported in JS"); - } - } - switch (this.headerCache.type) { - case 'kuki': - if (this.stream.available(this.headerCache.size)) { - if (this.format.formatID === 'aac ') { - offset = this.stream.offset + this.headerCache.size; - if (cookie = M4ADemuxer.readEsds(this.stream)) { - this.emit('cookie', cookie); - } - this.stream.seek(offset); - } else { - buffer = this.stream.readBuffer(this.headerCache.size); - this.emit('cookie', buffer); - } - this.headerCache = null; - } - break; - case 'pakt': - if (this.stream.available(this.headerCache.size)) { - if (this.stream.readUInt32() !== 0) { - return this.emit('error', 'Sizes greater than 32 bits are not supported.'); - } - this.numPackets = this.stream.readUInt32(); - if (this.stream.readUInt32() !== 0) { - return this.emit('error', 'Sizes greater than 32 bits are not supported.'); - } - this.numFrames = this.stream.readUInt32(); - this.primingFrames = this.stream.readUInt32(); - this.remainderFrames = this.stream.readUInt32(); - this.emit('duration', this.numFrames / this.format.sampleRate * 1000 | 0); - this.sentDuration = true; - byteOffset = 0; - sampleOffset = 0; - for (i = _i = 0, _ref = this.numPackets; _i < _ref; i = _i += 1) { - this.addSeekPoint(byteOffset, sampleOffset); - byteOffset += this.format.bytesPerPacket || M4ADemuxer.readDescrLen(this.stream); - sampleOffset += this.format.framesPerPacket || M4ADemuxer.readDescrLen(this.stream); - } - this.headerCache = null; - } - break; - case 'info': - entries = this.stream.readUInt32(); - metadata = {}; - for (i = _j = 0; 0 <= entries ? _j < entries : _j > entries; i = 0 <= entries ? ++_j : --_j) { - key = this.stream.readString(null); - value = this.stream.readString(null); - metadata[key] = value; - } - this.emit('metadata', metadata); - this.headerCache = null; - break; - case 'data': - if (!this.sentFirstDataChunk) { - this.stream.advance(4); - this.headerCache.size -= 4; - if (this.format.bytesPerPacket !== 0 && !this.sentDuration) { - this.numFrames = this.headerCache.size / this.format.bytesPerPacket; - this.emit('duration', this.numFrames / this.format.sampleRate * 1000 | 0); - } - this.sentFirstDataChunk = true; - } - buffer = this.stream.readSingleBuffer(this.headerCache.size); - this.headerCache.size -= buffer.length; - this.emit('data', buffer); - if (this.headerCache.size <= 0) { - this.headerCache = null; - } - break; - default: - if (this.stream.available(this.headerCache.size)) { - this.stream.advance(this.headerCache.size); - this.headerCache = null; - } - } - } - }; - - return CAFDemuxer; - - })(Demuxer); - -}).call(this); - -},{"../demuxer":14,"./m4a":18}],18:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var Demuxer, M4ADemuxer, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; - - Demuxer = require('../demuxer'); - - M4ADemuxer = (function(_super) { - var BITS_PER_CHANNEL, TYPES, after, atom, atoms, bool, containers, diskTrack, genres, meta, string; - - __extends(M4ADemuxer, _super); - - function M4ADemuxer() { - return M4ADemuxer.__super__.constructor.apply(this, arguments); - } - - Demuxer.register(M4ADemuxer); - - TYPES = ['M4A ', 'M4P ', 'M4B ', 'M4V ', 'isom', 'mp42', 'qt ']; - - M4ADemuxer.probe = function(buffer) { - var _ref; - return buffer.peekString(4, 4) === 'ftyp' && (_ref = buffer.peekString(8, 4), __indexOf.call(TYPES, _ref) >= 0); - }; - - M4ADemuxer.prototype.init = function() { - this.atoms = []; - this.offsets = []; - this.track = null; - return this.tracks = []; - }; - - atoms = {}; - - containers = {}; - - atom = function(name, fn) { - var c, container, _i, _len, _ref; - c = []; - _ref = name.split('.').slice(0, -1); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - container = _ref[_i]; - c.push(container); - containers[c.join('.')] = true; - } - if (atoms[name] == null) { - atoms[name] = {}; - } - return atoms[name].fn = fn; - }; - - after = function(name, fn) { - if (atoms[name] == null) { - atoms[name] = {}; - } - return atoms[name].after = fn; - }; - - M4ADemuxer.prototype.readChunk = function() { - var handler, path, type; - this["break"] = false; - while (this.stream.available(1) && !this["break"]) { - if (!this.readHeaders) { - if (!this.stream.available(8)) { - return; - } - this.len = this.stream.readUInt32() - 8; - this.type = this.stream.readString(4); - if (this.len === 0) { - continue; - } - this.atoms.push(this.type); - this.offsets.push(this.stream.offset + this.len); - this.readHeaders = true; - } - path = this.atoms.join('.'); - handler = atoms[path]; - if (handler != null ? handler.fn : void 0) { - if (!(this.stream.available(this.len) || path === 'mdat')) { - return; - } - handler.fn.call(this); - if (path in containers) { - this.readHeaders = false; - } - } else if (path in containers) { - this.readHeaders = false; - } else { - if (!this.stream.available(this.len)) { - return; - } - this.stream.advance(this.len); - } - while (this.stream.offset >= this.offsets[this.offsets.length - 1]) { - handler = atoms[this.atoms.join('.')]; - if (handler != null ? handler.after : void 0) { - handler.after.call(this); - } - type = this.atoms.pop(); - this.offsets.pop(); - this.readHeaders = false; - } - } - }; - - atom('ftyp', function() { - var _ref; - if (_ref = this.stream.readString(4), __indexOf.call(TYPES, _ref) < 0) { - return this.emit('error', 'Not a valid M4A file.'); - } - return this.stream.advance(this.len - 4); - }); - - atom('moov.trak', function() { - this.track = {}; - return this.tracks.push(this.track); - }); - - atom('moov.trak.tkhd', function() { - this.stream.advance(4); - this.stream.advance(8); - this.track.id = this.stream.readUInt32(); - return this.stream.advance(this.len - 16); - }); - - atom('moov.trak.mdia.hdlr', function() { - this.stream.advance(4); - this.stream.advance(4); - this.track.type = this.stream.readString(4); - this.stream.advance(12); - return this.stream.advance(this.len - 24); - }); - - atom('moov.trak.mdia.mdhd', function() { - this.stream.advance(4); - this.stream.advance(8); - this.track.timeScale = this.stream.readUInt32(); - this.track.duration = this.stream.readUInt32(); - return this.stream.advance(4); - }); - - BITS_PER_CHANNEL = { - ulaw: 8, - alaw: 8, - in24: 24, - in32: 32, - fl32: 32, - fl64: 64 - }; - - atom('moov.trak.mdia.minf.stbl.stsd', function() { - var format, numEntries, version, _ref, _ref1; - this.stream.advance(4); - numEntries = this.stream.readUInt32(); - if (this.track.type !== 'soun') { - return this.stream.advance(this.len - 8); - } - if (numEntries !== 1) { - return this.emit('error', "Only expecting one entry in sample description atom!"); - } - this.stream.advance(4); - format = this.track.format = {}; - format.formatID = this.stream.readString(4); - this.stream.advance(6); - this.stream.advance(2); - version = this.stream.readUInt16(); - this.stream.advance(6); - format.channelsPerFrame = this.stream.readUInt16(); - format.bitsPerChannel = this.stream.readUInt16(); - this.stream.advance(4); - format.sampleRate = this.stream.readUInt16(); - this.stream.advance(2); - if (version === 1) { - format.framesPerPacket = this.stream.readUInt32(); - this.stream.advance(4); - format.bytesPerFrame = this.stream.readUInt32(); - this.stream.advance(4); - } else if (version !== 0) { - this.emit('error', 'Unknown version in stsd atom'); - } - if (BITS_PER_CHANNEL[format.formatID] != null) { - format.bitsPerChannel = BITS_PER_CHANNEL[format.formatID]; - } - format.floatingPoint = (_ref = format.formatID) === 'fl32' || _ref === 'fl64'; - format.littleEndian = format.formatID === 'sowt' && format.bitsPerChannel > 8; - if ((_ref1 = format.formatID) === 'twos' || _ref1 === 'sowt' || _ref1 === 'in24' || _ref1 === 'in32' || _ref1 === 'fl32' || _ref1 === 'fl64' || _ref1 === 'raw ' || _ref1 === 'NONE') { - return format.formatID = 'lpcm'; - } - }); - - atom('moov.trak.mdia.minf.stbl.stsd.alac', function() { - this.stream.advance(4); - return this.track.cookie = this.stream.readBuffer(this.len - 4); - }); - - atom('moov.trak.mdia.minf.stbl.stsd.esds', function() { - var offset; - offset = this.stream.offset + this.len; - this.track.cookie = M4ADemuxer.readEsds(this.stream); - return this.stream.seek(offset); - }); - - atom('moov.trak.mdia.minf.stbl.stsd.wave.enda', function() { - return this.track.format.littleEndian = !!this.stream.readUInt16(); - }); - - M4ADemuxer.readDescrLen = function(stream) { - var c, count, len; - len = 0; - count = 4; - while (count--) { - c = stream.readUInt8(); - len = (len << 7) | (c & 0x7f); - if (!(c & 0x80)) { - break; - } - } - return len; - }; - - M4ADemuxer.readEsds = function(stream) { - var codec_id, flags, len, tag; - stream.advance(4); - tag = stream.readUInt8(); - len = M4ADemuxer.readDescrLen(stream); - if (tag === 0x03) { - stream.advance(2); - flags = stream.readUInt8(); - if (flags & 0x80) { - stream.advance(2); - } - if (flags & 0x40) { - stream.advance(stream.readUInt8()); - } - if (flags & 0x20) { - stream.advance(2); - } - } else { - stream.advance(2); - } - tag = stream.readUInt8(); - len = M4ADemuxer.readDescrLen(stream); - if (tag === 0x04) { - codec_id = stream.readUInt8(); - stream.advance(1); - stream.advance(3); - stream.advance(4); - stream.advance(4); - tag = stream.readUInt8(); - len = M4ADemuxer.readDescrLen(stream); - if (tag === 0x05) { - return stream.readBuffer(len); - } - } - return null; - }; - - atom('moov.trak.mdia.minf.stbl.stts', function() { - var entries, i, _i; - this.stream.advance(4); - entries = this.stream.readUInt32(); - this.track.stts = []; - for (i = _i = 0; _i < entries; i = _i += 1) { - this.track.stts[i] = { - count: this.stream.readUInt32(), - duration: this.stream.readUInt32() - }; - } - return this.setupSeekPoints(); - }); - - atom('moov.trak.mdia.minf.stbl.stsc', function() { - var entries, i, _i; - this.stream.advance(4); - entries = this.stream.readUInt32(); - this.track.stsc = []; - for (i = _i = 0; _i < entries; i = _i += 1) { - this.track.stsc[i] = { - first: this.stream.readUInt32(), - count: this.stream.readUInt32(), - id: this.stream.readUInt32() - }; - } - return this.setupSeekPoints(); - }); - - atom('moov.trak.mdia.minf.stbl.stsz', function() { - var entries, i, _i; - this.stream.advance(4); - this.track.sampleSize = this.stream.readUInt32(); - entries = this.stream.readUInt32(); - if (this.track.sampleSize === 0 && entries > 0) { - this.track.sampleSizes = []; - for (i = _i = 0; _i < entries; i = _i += 1) { - this.track.sampleSizes[i] = this.stream.readUInt32(); - } - } - return this.setupSeekPoints(); - }); - - atom('moov.trak.mdia.minf.stbl.stco', function() { - var entries, i, _i; - this.stream.advance(4); - entries = this.stream.readUInt32(); - this.track.chunkOffsets = []; - for (i = _i = 0; _i < entries; i = _i += 1) { - this.track.chunkOffsets[i] = this.stream.readUInt32(); - } - return this.setupSeekPoints(); - }); - - atom('moov.trak.tref.chap', function() { - var entries, i, _i; - entries = this.len >> 2; - this.track.chapterTracks = []; - for (i = _i = 0; _i < entries; i = _i += 1) { - this.track.chapterTracks[i] = this.stream.readUInt32(); - } - }); - - M4ADemuxer.prototype.setupSeekPoints = function() { - var i, j, offset, position, sampleIndex, size, stscIndex, sttsIndex, sttsSample, timestamp, _i, _j, _len, _ref, _ref1, _results; - if (!((this.track.chunkOffsets != null) && (this.track.stsc != null) && (this.track.sampleSize != null) && (this.track.stts != null))) { - return; - } - stscIndex = 0; - sttsIndex = 0; - sttsIndex = 0; - sttsSample = 0; - sampleIndex = 0; - offset = 0; - timestamp = 0; - this.track.seekPoints = []; - _ref = this.track.chunkOffsets; - _results = []; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - position = _ref[i]; - for (j = _j = 0, _ref1 = this.track.stsc[stscIndex].count; _j < _ref1; j = _j += 1) { - this.track.seekPoints.push({ - offset: offset, - position: position, - timestamp: timestamp - }); - size = this.track.sampleSize || this.track.sampleSizes[sampleIndex++]; - offset += size; - position += size; - timestamp += this.track.stts[sttsIndex].duration; - if (sttsIndex + 1 < this.track.stts.length && ++sttsSample === this.track.stts[sttsIndex].count) { - sttsSample = 0; - sttsIndex++; - } - } - if (stscIndex + 1 < this.track.stsc.length && i + 1 === this.track.stsc[stscIndex + 1].first) { - _results.push(stscIndex++); - } else { - _results.push(void 0); - } - } - return _results; - }; - - after('moov', function() { - var track, _i, _len, _ref; - if (this.mdatOffset != null) { - this.stream.seek(this.mdatOffset - 8); - } - _ref = this.tracks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - track = _ref[_i]; - if (!(track.type === 'soun')) { - continue; - } - this.track = track; - break; - } - if (this.track.type !== 'soun') { - this.track = null; - return this.emit('error', 'No audio tracks in m4a file.'); - } - this.emit('format', this.track.format); - this.emit('duration', this.track.duration / this.track.timeScale * 1000 | 0); - if (this.track.cookie) { - this.emit('cookie', this.track.cookie); - } - return this.seekPoints = this.track.seekPoints; - }); - - atom('mdat', function() { - var bytes, chunkSize, length, numSamples, offset, sample, size, _i; - if (!this.startedData) { - if (this.mdatOffset == null) { - this.mdatOffset = this.stream.offset; - } - if (this.tracks.length === 0) { - bytes = Math.min(this.stream.remainingBytes(), this.len); - this.stream.advance(bytes); - this.len -= bytes; - return; - } - this.chunkIndex = 0; - this.stscIndex = 0; - this.sampleIndex = 0; - this.tailOffset = 0; - this.tailSamples = 0; - this.startedData = true; - } - if (!this.readChapters) { - this.readChapters = this.parseChapters(); - if (this["break"] = !this.readChapters) { - return; - } - this.stream.seek(this.mdatOffset); - } - offset = this.track.chunkOffsets[this.chunkIndex] + this.tailOffset; - length = 0; - if (!this.stream.available(offset - this.stream.offset)) { - this["break"] = true; - return; - } - this.stream.seek(offset); - while (this.chunkIndex < this.track.chunkOffsets.length) { - numSamples = this.track.stsc[this.stscIndex].count - this.tailSamples; - chunkSize = 0; - for (sample = _i = 0; _i < numSamples; sample = _i += 1) { - size = this.track.sampleSize || this.track.sampleSizes[this.sampleIndex]; - if (!this.stream.available(length + size)) { - break; - } - length += size; - chunkSize += size; - this.sampleIndex++; - } - if (sample < numSamples) { - this.tailOffset += chunkSize; - this.tailSamples += sample; - break; - } else { - this.chunkIndex++; - this.tailOffset = 0; - this.tailSamples = 0; - if (this.stscIndex + 1 < this.track.stsc.length && this.chunkIndex + 1 === this.track.stsc[this.stscIndex + 1].first) { - this.stscIndex++; - } - if (offset + length !== this.track.chunkOffsets[this.chunkIndex]) { - break; - } - } - } - if (length > 0) { - this.emit('data', this.stream.readBuffer(length)); - return this["break"] = this.chunkIndex === this.track.chunkOffsets.length; - } else { - return this["break"] = true; - } - }); - - M4ADemuxer.prototype.parseChapters = function() { - var bom, id, len, nextTimestamp, point, title, track, _i, _len, _ref, _ref1, _ref2, _ref3; - if (!(((_ref = this.track.chapterTracks) != null ? _ref.length : void 0) > 0)) { - return true; - } - id = this.track.chapterTracks[0]; - _ref1 = this.tracks; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - track = _ref1[_i]; - if (track.id === id) { - break; - } - } - if (track.id !== id) { - this.emit('error', 'Chapter track does not exist.'); - } - if (this.chapters == null) { - this.chapters = []; - } - while (this.chapters.length < track.seekPoints.length) { - point = track.seekPoints[this.chapters.length]; - if (!this.stream.available(point.position - this.stream.offset + 32)) { - return false; - } - this.stream.seek(point.position); - len = this.stream.readUInt16(); - title = null; - if (!this.stream.available(len)) { - return false; - } - if (len > 2) { - bom = this.stream.peekUInt16(); - if (bom === 0xfeff || bom === 0xfffe) { - title = this.stream.readString(len, 'utf16-bom'); - } - } - if (title == null) { - title = this.stream.readString(len, 'utf8'); - } - nextTimestamp = (_ref2 = (_ref3 = track.seekPoints[this.chapters.length + 1]) != null ? _ref3.timestamp : void 0) != null ? _ref2 : track.duration; - this.chapters.push({ - title: title, - timestamp: point.timestamp / track.timeScale * 1000 | 0, - duration: (nextTimestamp - point.timestamp) / track.timeScale * 1000 | 0 - }); - } - this.emit('chapters', this.chapters); - return true; - }; - - atom('moov.udta.meta', function() { - this.metadata = {}; - return this.stream.advance(4); - }); - - after('moov.udta.meta', function() { - return this.emit('metadata', this.metadata); - }); - - meta = function(field, name, fn) { - return atom("moov.udta.meta.ilst." + field + ".data", function() { - this.stream.advance(8); - this.len -= 8; - return fn.call(this, name); - }); - }; - - string = function(field) { - return this.metadata[field] = this.stream.readString(this.len, 'utf8'); - }; - - meta('©alb', 'album', string); - - meta('©arg', 'arranger', string); - - meta('©art', 'artist', string); - - meta('©ART', 'artist', string); - - meta('aART', 'albumArtist', string); - - meta('catg', 'category', string); - - meta('©com', 'composer', string); - - meta('©cpy', 'copyright', string); - - meta('cprt', 'copyright', string); - - meta('©cmt', 'comments', string); - - meta('©day', 'releaseDate', string); - - meta('desc', 'description', string); - - meta('©gen', 'genre', string); - - meta('©grp', 'grouping', string); - - meta('©isr', 'ISRC', string); - - meta('keyw', 'keywords', string); - - meta('©lab', 'recordLabel', string); - - meta('ldes', 'longDescription', string); - - meta('©lyr', 'lyrics', string); - - meta('©nam', 'title', string); - - meta('©phg', 'recordingCopyright', string); - - meta('©prd', 'producer', string); - - meta('©prf', 'performers', string); - - meta('purd', 'purchaseDate', string); - - meta('purl', 'podcastURL', string); - - meta('©swf', 'songwriter', string); - - meta('©too', 'encoder', string); - - meta('©wrt', 'composer', string); - - meta('covr', 'coverArt', function(field) { - return this.metadata[field] = this.stream.readBuffer(this.len); - }); - - genres = ["Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A Capella", "Euro-House", "Dance Hall"]; - - meta('gnre', 'genre', function(field) { - return this.metadata[field] = genres[this.stream.readUInt16() - 1]; - }); - - meta('tmpo', 'tempo', function(field) { - return this.metadata[field] = this.stream.readUInt16(); - }); - - meta('rtng', 'rating', function(field) { - var rating; - rating = this.stream.readUInt8(); - return this.metadata[field] = rating === 2 ? 'Clean' : rating !== 0 ? 'Explicit' : 'None'; - }); - - diskTrack = function(field) { - this.stream.advance(2); - this.metadata[field] = this.stream.readUInt16() + ' of ' + this.stream.readUInt16(); - return this.stream.advance(this.len - 6); - }; - - meta('disk', 'diskNumber', diskTrack); - - meta('trkn', 'trackNumber', diskTrack); - - bool = function(field) { - return this.metadata[field] = this.stream.readUInt8() === 1; - }; - - meta('cpil', 'compilation', bool); - - meta('pcst', 'podcast', bool); - - meta('pgap', 'gapless', bool); - - return M4ADemuxer; - - })(Demuxer); - - module.exports = M4ADemuxer; - -}).call(this); - -},{"../demuxer":14}],19:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var Demuxer, WAVEDemuxer, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - Demuxer = require('../demuxer'); - - WAVEDemuxer = (function(_super) { - var formats; - - __extends(WAVEDemuxer, _super); - - function WAVEDemuxer() { - return WAVEDemuxer.__super__.constructor.apply(this, arguments); - } - - Demuxer.register(WAVEDemuxer); - - WAVEDemuxer.probe = function(buffer) { - return buffer.peekString(0, 4) === 'RIFF' && buffer.peekString(8, 4) === 'WAVE'; - }; - - formats = { - 0x0001: 'lpcm', - 0x0003: 'lpcm', - 0x0006: 'alaw', - 0x0007: 'ulaw' - }; - - WAVEDemuxer.prototype.readChunk = function() { - var buffer, bytes, encoding; - if (!this.readStart && this.stream.available(12)) { - if (this.stream.readString(4) !== 'RIFF') { - return this.emit('error', 'Invalid WAV file.'); - } - this.fileSize = this.stream.readUInt32(true); - this.readStart = true; - if (this.stream.readString(4) !== 'WAVE') { - return this.emit('error', 'Invalid WAV file.'); - } - } - while (this.stream.available(1)) { - if (!this.readHeaders && this.stream.available(8)) { - this.type = this.stream.readString(4); - this.len = this.stream.readUInt32(true); - } - switch (this.type) { - case 'fmt ': - encoding = this.stream.readUInt16(true); - if (!(encoding in formats)) { - return this.emit('error', 'Unsupported format in WAV file.'); - } - this.format = { - formatID: formats[encoding], - floatingPoint: encoding === 0x0003, - littleEndian: formats[encoding] === 'lpcm', - channelsPerFrame: this.stream.readUInt16(true), - sampleRate: this.stream.readUInt32(true), - framesPerPacket: 1 - }; - this.stream.advance(4); - this.stream.advance(2); - this.format.bitsPerChannel = this.stream.readUInt16(true); - this.format.bytesPerPacket = (this.format.bitsPerChannel / 8) * this.format.channelsPerFrame; - this.emit('format', this.format); - this.stream.advance(this.len - 16); - break; - case 'data': - if (!this.sentDuration) { - bytes = this.format.bitsPerChannel / 8; - this.emit('duration', this.len / bytes / this.format.channelsPerFrame / this.format.sampleRate * 1000 | 0); - this.sentDuration = true; - } - buffer = this.stream.readSingleBuffer(this.len); - this.len -= buffer.length; - this.readHeaders = this.len > 0; - this.emit('data', buffer); - break; - default: - if (!this.stream.available(this.len)) { - return; - } - this.stream.advance(this.len); - } - if (this.type !== 'data') { - this.readHeaders = false; - } - } - }; - - return WAVEDemuxer; - - })(Demuxer); - -}).call(this); - -},{"../demuxer":14}],20:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var AudioDevice, EventEmitter, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - EventEmitter = require('./core/events'); - - AudioDevice = (function(_super) { - var devices; - - __extends(AudioDevice, _super); - - function AudioDevice(sampleRate, channels) { - this.sampleRate = sampleRate; - this.channels = channels; - this.updateTime = __bind(this.updateTime, this); - this.playing = false; - this.currentTime = 0; - this._lastTime = 0; - } - - AudioDevice.prototype.start = function() { - if (this.playing) { - return; - } - this.playing = true; - if (this.device == null) { - this.device = AudioDevice.create(this.sampleRate, this.channels); - } - if (!this.device) { - throw new Error("No supported audio device found."); - } - this._lastTime = this.device.getDeviceTime(); - this._timer = setInterval(this.updateTime, 200); - return this.device.on('refill', this.refill = (function(_this) { - return function(buffer) { - return _this.emit('refill', buffer); - }; - })(this)); - }; - - AudioDevice.prototype.stop = function() { - if (!this.playing) { - return; - } - this.playing = false; - this.device.off('refill', this.refill); - return clearInterval(this._timer); - }; - - AudioDevice.prototype.destroy = function() { - var _ref; - this.stop(); - return (_ref = this.device) != null ? _ref.destroy() : void 0; - }; - - AudioDevice.prototype.seek = function(currentTime) { - this.currentTime = currentTime; - if (this.playing) { - this._lastTime = this.device.getDeviceTime(); - } - return this.emit('timeUpdate', this.currentTime); - }; - - AudioDevice.prototype.updateTime = function() { - var time; - time = this.device.getDeviceTime(); - this.currentTime += (time - this._lastTime) / this.device.sampleRate * 1000 | 0; - this._lastTime = time; - return this.emit('timeUpdate', this.currentTime); - }; - - devices = []; - - AudioDevice.register = function(device) { - return devices.push(device); - }; - - AudioDevice.create = function(sampleRate, channels) { - var device, _i, _len; - for (_i = 0, _len = devices.length; _i < _len; _i++) { - device = devices[_i]; - if (device.supported) { - return new device(sampleRate, channels); - } - } - return null; - }; - - return AudioDevice; - - })(EventEmitter); - - module.exports = AudioDevice; - -}).call(this); - -},{"./core/events":8}],21:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var AVBuffer, AudioDevice, EventEmitter, MozillaAudioDevice, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - EventEmitter = require('../core/events'); - - AudioDevice = require('../device'); - - AVBuffer = require('../core/buffer'); - - MozillaAudioDevice = (function(_super) { - var createTimer, destroyTimer; - - __extends(MozillaAudioDevice, _super); - - AudioDevice.register(MozillaAudioDevice); - - MozillaAudioDevice.supported = (typeof Audio !== "undefined" && Audio !== null) && 'mozWriteAudio' in new Audio; - - function MozillaAudioDevice(sampleRate, channels) { - this.sampleRate = sampleRate; - this.channels = channels; - this.refill = __bind(this.refill, this); - this.audio = new Audio; - this.audio.mozSetup(this.channels, this.sampleRate); - this.writePosition = 0; - this.prebufferSize = this.sampleRate / 2; - this.tail = null; - this.timer = createTimer(this.refill, 100); - } - - MozillaAudioDevice.prototype.refill = function() { - var available, buffer, currentPosition, written; - if (this.tail) { - written = this.audio.mozWriteAudio(this.tail); - this.writePosition += written; - if (this.writePosition < this.tail.length) { - this.tail = this.tail.subarray(written); - } else { - this.tail = null; - } - } - currentPosition = this.audio.mozCurrentSampleOffset(); - available = currentPosition + this.prebufferSize - this.writePosition; - if (available > 0) { - buffer = new Float32Array(available); - this.emit('refill', buffer); - written = this.audio.mozWriteAudio(buffer); - if (written < buffer.length) { - this.tail = buffer.subarray(written); - } - this.writePosition += written; - } - }; - - MozillaAudioDevice.prototype.destroy = function() { - return destroyTimer(this.timer); - }; - - MozillaAudioDevice.prototype.getDeviceTime = function() { - return this.audio.mozCurrentSampleOffset() / this.channels; - }; - - createTimer = function(fn, interval) { - var url, worker; - url = AVBuffer.makeBlobURL("setInterval(function() { postMessage('ping'); }, " + interval + ");"); - if (url == null) { - return setInterval(fn, interval); - } - worker = new Worker(url); - worker.onmessage = fn; - worker.url = url; - return worker; - }; - - destroyTimer = function(timer) { - if (timer.terminate) { - timer.terminate(); - return URL.revokeObjectURL(timer.url); - } else { - return clearInterval(timer); - } - }; - - return MozillaAudioDevice; - - })(EventEmitter); - -}).call(this); - -},{"../core/buffer":6,"../core/events":8,"../device":20}],22:[function(require,module,exports){ -//JavaScript Audio Resampler -//Copyright (C) 2011-2015 Grant Galitz -//Released to Public Domain -function Resampler(fromSampleRate, toSampleRate, channels, inputBufferLength) { - this.fromSampleRate = +fromSampleRate; - this.toSampleRate = +toSampleRate; - this.channels = channels | 0; - this.inputBufferLength = inputBufferLength; - this.initialize(); -} - -Resampler.prototype.initialize = function () { - //Perform some checks: - if (this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0) { - if (this.fromSampleRate == this.toSampleRate) { - //Setup a resampler bypass: - this.resampler = this.bypassResampler; //Resampler just returns what was passed through. - this.ratioWeight = 1; - } else { - this.ratioWeight = this.fromSampleRate / this.toSampleRate; - if (this.fromSampleRate < this.toSampleRate) { - /* - Use generic linear interpolation if upsampling, - as linear interpolation produces a gradient that we want - and works fine with two input sample points per output in this case. - */ - this.compileLinearInterpolationFunction(); - this.lastWeight = 1; - } else { - /* - Custom resampler I wrote that doesn't skip samples - like standard linear interpolation in high downsampling. - This is more accurate than linear interpolation on downsampling. - */ - this.compileMultiTapFunction(); - this.tailExists = false; - this.lastWeight = 0; - } - - var outputBufferSize = (Math.ceil(this.inputBufferLength * this.toSampleRate / this.fromSampleRate / this.channels * 1.01) * this.channels) + this.channels; - this.outputBuffer = new Float32Array(outputBufferSize); - this.lastOutput = new Float32Array(this.channels); - } - } else { - throw(new Error("Invalid settings specified for the resampler.")); - } -}; - -Resampler.prototype.compileLinearInterpolationFunction = function () { - var toCompile = "var outputOffset = 0;\ - var bufferLength = buffer.length;\ - if (bufferLength > 0) {\ - var weight = this.lastWeight;\ - var firstWeight = 0;\ - var secondWeight = 0;\ - var sourceOffset = 0;\ - var outputOffset = 0;\ - var outputBuffer = this.outputBuffer;\ - for (; weight < 1; weight += " + this.ratioWeight + ") {\ - secondWeight = weight % 1;\ - firstWeight = 1 - secondWeight;"; - for (var channel = 0; channel < this.channels; ++channel) { - toCompile += "outputBuffer[outputOffset++] = (this.lastOutput[" + channel + "] * firstWeight) + (buffer[" + channel + "] * secondWeight);"; - } - toCompile += "}\ - weight -= 1;\ - for (bufferLength -= " + this.channels + ", sourceOffset = Math.floor(weight) * " + this.channels + "; sourceOffset < bufferLength;) {\ - secondWeight = weight % 1;\ - firstWeight = 1 - secondWeight;"; - for (var channel = 0; channel < this.channels; ++channel) { - toCompile += "outputBuffer[outputOffset++] = (buffer[sourceOffset" + ((channel > 0) ? (" + " + channel) : "") + "] * firstWeight) + (buffer[sourceOffset + " + (this.channels + channel) + "] * secondWeight);"; - } - toCompile += "weight += " + this.ratioWeight + ";\ - sourceOffset = Math.floor(weight) * " + this.channels + ";\ - }"; - for (var channel = 0; channel < this.channels; ++channel) { - toCompile += "this.lastOutput[" + channel + "] = buffer[sourceOffset++];"; - } - toCompile += "this.lastWeight = weight % 1;\ - }\ - return this.outputBuffer;"; - - this.resampler = Function("buffer", toCompile); -}; - -Resampler.prototype.compileMultiTapFunction = function () { - var toCompile = "var outputOffset = 0;\ - var bufferLength = buffer.length;\ - if (bufferLength > 0) {\ - var weight = 0;"; - for (var channel = 0; channel < this.channels; ++channel) { - toCompile += "var output" + channel + " = 0;" - } - toCompile += "var actualPosition = 0;\ - var amountToNext = 0;\ - var alreadyProcessedTail = !this.tailExists;\ - this.tailExists = false;\ - var outputBuffer = this.outputBuffer;\ - var currentPosition = 0;\ - do {\ - if (alreadyProcessedTail) {\ - weight = " + this.ratioWeight + ";"; - for (channel = 0; channel < this.channels; ++channel) { - toCompile += "output" + channel + " = 0;" - } - toCompile += "}\ - else {\ - weight = this.lastWeight;"; - for (channel = 0; channel < this.channels; ++channel) { - toCompile += "output" + channel + " = this.lastOutput[" + channel + "];" - } - toCompile += "alreadyProcessedTail = true;\ - }\ - while (weight > 0 && actualPosition < bufferLength) {\ - amountToNext = 1 + actualPosition - currentPosition;\ - if (weight >= amountToNext) {"; - for (channel = 0; channel < this.channels; ++channel) { - toCompile += "output" + channel + " += buffer[actualPosition++] * amountToNext;" - } - toCompile += "currentPosition = actualPosition;\ - weight -= amountToNext;\ - }\ - else {"; - for (channel = 0; channel < this.channels; ++channel) { - toCompile += "output" + channel + " += buffer[actualPosition" + ((channel > 0) ? (" + " + channel) : "") + "] * weight;" - } - toCompile += "currentPosition += weight;\ - weight = 0;\ - break;\ - }\ - }\ - if (weight <= 0) {"; - for (channel = 0; channel < this.channels; ++channel) { - toCompile += "outputBuffer[outputOffset++] = output" + channel + " / " + this.ratioWeight + ";" - } - toCompile += "}\ - else {\ - this.lastWeight = weight;"; - for (channel = 0; channel < this.channels; ++channel) { - toCompile += "this.lastOutput[" + channel + "] = output" + channel + ";" - } - toCompile += "this.tailExists = true;\ - break;\ - }\ - } while (actualPosition < bufferLength);\ - }\ - return this.outputBuffer;"; - - this.resampler = Function("buffer", toCompile); -}; - -Resampler.prototype.bypassResampler = function (inputBuffer) { - return inputBuffer; -}; - -module.exports = Resampler; - -},{}],23:[function(require,module,exports){ -(function (global){ -// Generated by CoffeeScript 1.7.1 -(function() { - var AudioDevice, EventEmitter, Resampler, WebAudioDevice, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - EventEmitter = require('../core/events'); - - AudioDevice = require('../device'); - - Resampler = require('./resampler'); - - WebAudioDevice = (function(_super) { - var AudioContext, createProcessor, sharedContext; - - __extends(WebAudioDevice, _super); - - AudioDevice.register(WebAudioDevice); - - AudioContext = global.AudioContext || global.webkitAudioContext; - - WebAudioDevice.supported = AudioContext && (typeof AudioContext.prototype[createProcessor = 'createScriptProcessor'] === 'function' || typeof AudioContext.prototype[createProcessor = 'createJavaScriptNode'] === 'function'); - - sharedContext = null; - - function WebAudioDevice(sampleRate, channels) { - this.sampleRate = sampleRate; - this.channels = channels; - this.refill = __bind(this.refill, this); - this.context = sharedContext != null ? sharedContext : sharedContext = new AudioContext; - this.deviceSampleRate = this.context.sampleRate; - this.bufferSize = Math.ceil(4096 / (this.deviceSampleRate / this.sampleRate) * this.channels); - this.bufferSize += this.bufferSize % this.channels; - if (this.deviceSampleRate !== this.sampleRate) { - this.resampler = new Resampler(this.sampleRate, this.deviceSampleRate, this.channels, this.bufferSize); - } - this.node = this.context[createProcessor](4096, this.channels, this.channels); - this.node.onaudioprocess = this.refill; - this.node.connect(this.context.destination); - } - - WebAudioDevice.prototype.refill = function(event) { - var channelCount, channels, data, i, n, outputBuffer, _i, _j, _k, _ref; - outputBuffer = event.outputBuffer; - channelCount = outputBuffer.numberOfChannels; - channels = new Array(channelCount); - for (i = _i = 0; _i < channelCount; i = _i += 1) { - channels[i] = outputBuffer.getChannelData(i); - } - data = new Float32Array(this.bufferSize); - this.emit('refill', data); - if (this.resampler) { - data = this.resampler.resampler(data); - } - for (i = _j = 0, _ref = outputBuffer.length; _j < _ref; i = _j += 1) { - for (n = _k = 0; _k < channelCount; n = _k += 1) { - channels[n][i] = data[i * channelCount + n]; - } - } - }; - - WebAudioDevice.prototype.destroy = function() { - return this.node.disconnect(0); - }; - - WebAudioDevice.prototype.getDeviceTime = function() { - return this.context.currentTime * this.sampleRate; - }; - - return WebAudioDevice; - - })(EventEmitter); - -}).call(this); - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../core/events":8,"../device":20,"./resampler":22}],24:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var Filter; - - Filter = (function() { - function Filter(context, key) { - if (context && key) { - Object.defineProperty(this, 'value', { - get: function() { - return context[key]; - } - }); - } - } - - Filter.prototype.process = function(buffer) {}; - - return Filter; - - })(); - - module.exports = Filter; - -}).call(this); - -},{}],25:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var BalanceFilter, Filter, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - Filter = require('../filter'); - - BalanceFilter = (function(_super) { - __extends(BalanceFilter, _super); - - function BalanceFilter() { - return BalanceFilter.__super__.constructor.apply(this, arguments); - } - - BalanceFilter.prototype.process = function(buffer) { - var i, pan, _i, _ref; - if (this.value === 0) { - return; - } - pan = Math.max(-50, Math.min(50, this.value)); - for (i = _i = 0, _ref = buffer.length; _i < _ref; i = _i += 2) { - buffer[i] *= Math.min(1, (50 - pan) / 50); - buffer[i + 1] *= Math.min(1, (50 + pan) / 50); - } - }; - - return BalanceFilter; - - })(Filter); - - module.exports = BalanceFilter; - -}).call(this); - -},{"../filter":24}],26:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var Filter, VolumeFilter, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - Filter = require('../filter'); - - VolumeFilter = (function(_super) { - __extends(VolumeFilter, _super); - - function VolumeFilter() { - return VolumeFilter.__super__.constructor.apply(this, arguments); - } - - VolumeFilter.prototype.process = function(buffer) { - var i, vol, _i, _ref; - if (this.value >= 100) { - return; - } - vol = Math.max(0, Math.min(100, this.value)) / 100; - for (i = _i = 0, _ref = buffer.length; _i < _ref; i = _i += 1) { - buffer[i] *= vol; - } - }; - - return VolumeFilter; - - })(Filter); - - module.exports = VolumeFilter; - -}).call(this); - -},{"../filter":24}],27:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var Asset, AudioDevice, BalanceFilter, EventEmitter, Player, Queue, VolumeFilter, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - EventEmitter = require('./core/events'); - - Asset = require('./asset'); - - VolumeFilter = require('./filters/volume'); - - BalanceFilter = require('./filters/balance'); - - Queue = require('./queue'); - - AudioDevice = require('./device'); - - Player = (function(_super) { - __extends(Player, _super); - - function Player(asset) { - this.asset = asset; - this.startPlaying = __bind(this.startPlaying, this); - this.playing = false; - this.buffered = 0; - this.currentTime = 0; - this.duration = 0; - this.volume = 100; - this.pan = 0; - this.metadata = {}; - this.filters = [new VolumeFilter(this, 'volume'), new BalanceFilter(this, 'pan')]; - this.asset.on('buffer', (function(_this) { - return function(buffered) { - _this.buffered = buffered; - return _this.emit('buffer', _this.buffered); - }; - })(this)); - this.asset.on('decodeStart', (function(_this) { - return function() { - _this.queue = new Queue(_this.asset); - return _this.queue.once('ready', _this.startPlaying); - }; - })(this)); - this.asset.on('format', (function(_this) { - return function(format) { - _this.format = format; - return _this.emit('format', _this.format); - }; - })(this)); - this.asset.on('metadata', (function(_this) { - return function(metadata) { - _this.metadata = metadata; - return _this.emit('metadata', _this.metadata); - }; - })(this)); - this.asset.on('duration', (function(_this) { - return function(duration) { - _this.duration = duration; - return _this.emit('duration', _this.duration); - }; - })(this)); - this.asset.on('error', (function(_this) { - return function(error) { - return _this.emit('error', error); - }; - })(this)); - } - - Player.fromURL = function(url, opts) { - return new Player(Asset.fromURL(url, opts)); - }; - - Player.fromFile = function(file) { - return new Player(Asset.fromFile(file)); - }; - - Player.fromBuffer = function(buffer) { - return new Player(Asset.fromBuffer(buffer)); - }; - - Player.prototype.preload = function() { - if (!this.asset) { - return; - } - this.startedPreloading = true; - return this.asset.start(false); - }; - - Player.prototype.play = function() { - var _ref; - if (this.playing) { - return; - } - if (!this.startedPreloading) { - this.preload(); - } - this.playing = true; - return (_ref = this.device) != null ? _ref.start() : void 0; - }; - - Player.prototype.pause = function() { - var _ref; - if (!this.playing) { - return; - } - this.playing = false; - return (_ref = this.device) != null ? _ref.stop() : void 0; - }; - - Player.prototype.togglePlayback = function() { - if (this.playing) { - return this.pause(); - } else { - return this.play(); - } - }; - - Player.prototype.stop = function() { - var _ref; - this.pause(); - this.asset.stop(); - return (_ref = this.device) != null ? _ref.destroy() : void 0; - }; - - Player.prototype.seek = function(timestamp) { - var _ref; - if ((_ref = this.device) != null) { - _ref.stop(); - } - this.queue.once('ready', (function(_this) { - return function() { - var _ref1, _ref2; - if ((_ref1 = _this.device) != null) { - _ref1.seek(_this.currentTime); - } - if (_this.playing) { - return (_ref2 = _this.device) != null ? _ref2.start() : void 0; - } - }; - })(this)); - timestamp = (timestamp / 1000) * this.format.sampleRate; - timestamp = this.asset.decoder.seek(timestamp); - this.currentTime = timestamp / this.format.sampleRate * 1000 | 0; - this.queue.reset(); - return this.currentTime; - }; - - Player.prototype.startPlaying = function() { - var frame, frameOffset; - frame = this.queue.read(); - frameOffset = 0; - this.device = new AudioDevice(this.format.sampleRate, this.format.channelsPerFrame); - this.device.on('timeUpdate', (function(_this) { - return function(currentTime) { - _this.currentTime = currentTime; - return _this.emit('progress', _this.currentTime); - }; - })(this)); - this.refill = (function(_this) { - return function(buffer) { - var bufferOffset, filter, i, max, _i, _j, _len, _ref; - if (!_this.playing) { - return; - } - if (!frame) { - frame = _this.queue.read(); - frameOffset = 0; - } - bufferOffset = 0; - while (frame && bufferOffset < buffer.length) { - max = Math.min(frame.length - frameOffset, buffer.length - bufferOffset); - for (i = _i = 0; _i < max; i = _i += 1) { - buffer[bufferOffset++] = frame[frameOffset++]; - } - if (frameOffset === frame.length) { - frame = _this.queue.read(); - frameOffset = 0; - } - } - _ref = _this.filters; - for (_j = 0, _len = _ref.length; _j < _len; _j++) { - filter = _ref[_j]; - filter.process(buffer); - } - if (!frame) { - if (_this.queue.ended) { - _this.currentTime = _this.duration; - _this.emit('progress', _this.currentTime); - _this.emit('end'); - _this.stop(); - } else { - _this.device.stop(); - } - } - }; - })(this); - this.device.on('refill', this.refill); - if (this.playing) { - this.device.start(); - } - return this.emit('ready'); - }; - - Player.prototype.destroy = function() { - var _ref, _ref1; - this.stop(); - if ((_ref = this.device) != null) { - _ref.off(); - } - if ((_ref1 = this.asset) != null) { - _ref1.destroy(); - } - return this.off(); - }; - - return Player; - - })(EventEmitter); - - module.exports = Player; - -}).call(this); - -},{"./asset":1,"./core/events":8,"./device":20,"./filters/balance":25,"./filters/volume":26,"./queue":28}],28:[function(require,module,exports){ -// Generated by CoffeeScript 1.7.1 -(function() { - var EventEmitter, Queue, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - EventEmitter = require('./core/events'); - - Queue = (function(_super) { - __extends(Queue, _super); - - function Queue(asset) { - this.asset = asset; - this.write = __bind(this.write, this); - this.readyMark = 64; - this.finished = false; - this.buffering = true; - this.ended = false; - this.buffers = []; - this.asset.on('data', this.write); - this.asset.on('end', (function(_this) { - return function() { - return _this.ended = true; - }; - })(this)); - this.asset.decodePacket(); - } - - Queue.prototype.write = function(buffer) { - if (buffer) { - this.buffers.push(buffer); - } - if (this.buffering) { - if (this.buffers.length >= this.readyMark || this.ended) { - this.buffering = false; - return this.emit('ready'); - } else { - return this.asset.decodePacket(); - } - } - }; - - Queue.prototype.read = function() { - if (this.buffers.length === 0) { - return null; - } - this.asset.decodePacket(); - return this.buffers.shift(); - }; - - Queue.prototype.reset = function() { - this.buffers.length = 0; - this.buffering = true; - return this.asset.decodePacket(); - }; - - return Queue; - - })(EventEmitter); - - module.exports = Queue; - -}).call(this); - -},{"./core/events":8}],29:[function(require,module,exports){ -var AVBuffer, EventEmitter, FileSource, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - -EventEmitter = require('../../core/events'); - -AVBuffer = require('../../core/buffer'); - -FileSource = (function(_super) { - __extends(FileSource, _super); - - function FileSource(file) { - this.file = file; - if (typeof FileReader === "undefined" || FileReader === null) { - return this.emit('error', 'This browser does not have FileReader support.'); - } - this.offset = 0; - this.length = this.file.size; - this.chunkSize = 1 << 20; - this.file[this.slice = 'slice'] || this.file[this.slice = 'webkitSlice'] || this.file[this.slice = 'mozSlice']; - } - - FileSource.prototype.start = function() { - if (this.reader) { - if (!this.active) { - return this.loop(); - } - } - this.reader = new FileReader; - this.active = true; - this.reader.onload = (function(_this) { - return function(e) { - var buf; - buf = new AVBuffer(new Uint8Array(e.target.result)); - _this.offset += buf.length; - _this.emit('data', buf); - _this.active = false; - if (_this.offset < _this.length) { - return _this.loop(); - } - }; - })(this); - this.reader.onloadend = (function(_this) { - return function() { - if (_this.offset === _this.length) { - _this.emit('end'); - return _this.reader = null; - } - }; - })(this); - this.reader.onerror = (function(_this) { - return function(e) { - return _this.emit('error', e); - }; - })(this); - this.reader.onprogress = (function(_this) { - return function(e) { - return _this.emit('progress', (_this.offset + e.loaded) / _this.length * 100); - }; - })(this); - return this.loop(); - }; - - FileSource.prototype.loop = function() { - var blob, endPos; - this.active = true; - endPos = Math.min(this.offset + this.chunkSize, this.length); - blob = this.file[this.slice](this.offset, endPos); - return this.reader.readAsArrayBuffer(blob); - }; - - FileSource.prototype.pause = function() { - var _ref; - this.active = false; - try { - return (_ref = this.reader) != null ? _ref.abort() : void 0; - } catch (_error) {} - }; - - FileSource.prototype.reset = function() { - this.pause(); - return this.offset = 0; - }; - - return FileSource; - -})(EventEmitter); - -module.exports = FileSource; - -},{"../../core/buffer":6,"../../core/events":8}],30:[function(require,module,exports){ -var AVBuffer, EventEmitter, HTTPSource, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - -EventEmitter = require('../../core/events'); - -AVBuffer = require('../../core/buffer'); - -HTTPSource = (function(_super) { - __extends(HTTPSource, _super); - - function HTTPSource(url, opts) { - this.url = url; - this.opts = opts != null ? opts : {}; - this.chunkSize = 1 << 20; - this.inflight = false; - if (this.opts.length) { - this.length = this.opts.length; - } - this.reset(); - } - - HTTPSource.prototype.start = function() { - if (this.length) { - if (!this.inflight) { - return this.loop(); - } - } - this.inflight = true; - this.xhr = new XMLHttpRequest(); - this.xhr.onload = (function(_this) { - return function(event) { - _this.length = parseInt(_this.xhr.getResponseHeader("Content-Length")); - _this.inflight = false; - return _this.loop(); - }; - })(this); - this.xhr.onerror = (function(_this) { - return function(err) { - _this.pause(); - return _this.emit('error', err); - }; - })(this); - this.xhr.onabort = (function(_this) { - return function(event) { - return _this.inflight = false; - }; - })(this); - this.xhr.open("HEAD", this.url, true); - return this.xhr.send(null); - }; - - HTTPSource.prototype.loop = function() { - var endPos; - if (this.inflight || !this.length) { - return this.emit('error', 'Something is wrong in HTTPSource.loop'); - } - this.inflight = true; - this.xhr = new XMLHttpRequest(); - this.xhr.onload = (function(_this) { - return function(event) { - var buf, buffer, i, txt, _i, _ref; - if (_this.xhr.response) { - buf = new Uint8Array(_this.xhr.response); - } else { - txt = _this.xhr.responseText; - buf = new Uint8Array(txt.length); - for (i = _i = 0, _ref = txt.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - buf[i] = txt.charCodeAt(i) & 0xff; - } - } - buffer = new AVBuffer(buf); - _this.offset += buffer.length; - _this.emit('data', buffer); - if (_this.offset >= _this.length) { - _this.emit('end'); - } - _this.inflight = false; - if (!(_this.offset >= _this.length)) { - return _this.loop(); - } - }; - })(this); - this.xhr.onprogress = (function(_this) { - return function(event) { - return _this.emit('progress', (_this.offset + event.loaded) / _this.length * 100); - }; - })(this); - this.xhr.onerror = (function(_this) { - return function(err) { - _this.emit('error', err); - return _this.pause(); - }; - })(this); - this.xhr.onabort = (function(_this) { - return function(event) { - return _this.inflight = false; - }; - })(this); - this.xhr.open("GET", this.url, true); - this.xhr.responseType = "arraybuffer"; - endPos = Math.min(this.offset + this.chunkSize, this.length - 1); - this.xhr.setRequestHeader("If-None-Match", "webkit-no-cache"); - this.xhr.setRequestHeader("Range", "bytes=" + this.offset + "-" + endPos); - this.xhr.overrideMimeType('text/plain; charset=x-user-defined'); - return this.xhr.send(null); - }; - - HTTPSource.prototype.pause = function() { - var _ref; - this.inflight = false; - return (_ref = this.xhr) != null ? _ref.abort() : void 0; - }; - - HTTPSource.prototype.reset = function() { - this.pause(); - return this.offset = 0; - }; - - return HTTPSource; - -})(EventEmitter); - -module.exports = HTTPSource; - -},{"../../core/buffer":6,"../../core/events":8}],31:[function(require,module,exports){ -(function (global){ -// Generated by CoffeeScript 1.7.1 -(function() { - var AVBuffer, BufferList, BufferSource, EventEmitter, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - - EventEmitter = require('../core/events'); - - BufferList = require('../core/bufferlist'); - - AVBuffer = require('../core/buffer'); - - BufferSource = (function(_super) { - var clearImmediate, setImmediate; - - __extends(BufferSource, _super); - - function BufferSource(input) { - this.loop = __bind(this.loop, this); - if (input instanceof BufferList) { - this.list = input; - } else { - this.list = new BufferList; - this.list.append(new AVBuffer(input)); - } - this.paused = true; - } - - setImmediate = global.setImmediate || function(fn) { - return global.setTimeout(fn, 0); - }; - - clearImmediate = global.clearImmediate || function(timer) { - return global.clearTimeout(timer); - }; - - BufferSource.prototype.start = function() { - this.paused = false; - return this._timer = setImmediate(this.loop); - }; - - BufferSource.prototype.loop = function() { - this.emit('progress', (this.list.numBuffers - this.list.availableBuffers + 1) / this.list.numBuffers * 100 | 0); - this.emit('data', this.list.first); - if (this.list.advance()) { - return setImmediate(this.loop); - } else { - return this.emit('end'); - } - }; - - BufferSource.prototype.pause = function() { - clearImmediate(this._timer); - return this.paused = true; - }; - - BufferSource.prototype.reset = function() { - this.pause(); - return this.list.rewind(); - }; - - return BufferSource; - - })(EventEmitter); - - module.exports = BufferSource; - -}).call(this); - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"../core/buffer":6,"../core/bufferlist":7,"../core/events":8}],32:[function(require,module,exports){ -var key, val, _ref; - -_ref = require('./src/aurora'); -for (key in _ref) { - val = _ref[key]; - exports[key] = val; -} - -require('./src/devices/webaudio'); - -require('./src/devices/mozilla'); - -},{"./src/aurora":2,"./src/devices/mozilla":21,"./src/devices/webaudio":23}]},{},[32])(32) -}); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJzcmMvYXNzZXQuanMiLCJzcmMvYXVyb3JhLmpzIiwic3JjL2F1cm9yYV9iYXNlLmpzIiwic3JjL2NvcmUvYmFzZS5qcyIsInNyYy9jb3JlL2JpdHN0cmVhbS5qcyIsInNyYy9jb3JlL2J1ZmZlci5qcyIsInNyYy9jb3JlL2J1ZmZlcmxpc3QuanMiLCJzcmMvY29yZS9ldmVudHMuanMiLCJzcmMvY29yZS9zdHJlYW0uanMiLCJzcmMvY29yZS91bmRlcmZsb3cuanMiLCJzcmMvZGVjb2Rlci5qcyIsInNyYy9kZWNvZGVycy9scGNtLmpzIiwic3JjL2RlY29kZXJzL3hsYXcuanMiLCJzcmMvZGVtdXhlci5qcyIsInNyYy9kZW11eGVycy9haWZmLmpzIiwic3JjL2RlbXV4ZXJzL2F1LmpzIiwic3JjL2RlbXV4ZXJzL2NhZi5qcyIsInNyYy9kZW11eGVycy9tNGEuanMiLCJzcmMvZGVtdXhlcnMvd2F2ZS5qcyIsInNyYy9kZXZpY2UuanMiLCJzcmMvZGV2aWNlcy9tb3ppbGxhLmpzIiwic3JjL2RldmljZXMvcmVzYW1wbGVyLmpzIiwic3JjL2RldmljZXMvd2ViYXVkaW8uanMiLCJzcmMvZmlsdGVyLmpzIiwic3JjL2ZpbHRlcnMvYmFsYW5jZS5qcyIsInNyYy9maWx0ZXJzL3ZvbHVtZS5qcyIsInNyYy9wbGF5ZXIuanMiLCJzcmMvcXVldWUuanMiLCIvVXNlcnMvcHNvcmkvRG9jdW1lbnRzL0NvZGUvYXVyb3JhLmpzL3NyYy9zb3VyY2VzL2Jyb3dzZXIvZmlsZS5jb2ZmZWUiLCIvVXNlcnMvcHNvcmkvRG9jdW1lbnRzL0NvZGUvYXVyb3JhLmpzL3NyYy9zb3VyY2VzL2Jyb3dzZXIvaHR0cC5jb2ZmZWUiLCJzcmMvc291cmNlcy9idWZmZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pQQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDbk9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7OztBQ3pGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xnQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0SEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25GQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9JQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeG5CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUM1SkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUMzRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaE9BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaEVBLElBQUEsa0NBQUE7RUFBQTtpU0FBQTs7QUFBQSxZQUFBLEdBQWUsT0FBQSxDQUFRLG1CQUFSLENBQWYsQ0FBQTs7QUFBQSxRQUNBLEdBQVcsT0FBQSxDQUFRLG1CQUFSLENBRFgsQ0FBQTs7QUFBQTtBQUlJLCtCQUFBLENBQUE7O0FBQWEsRUFBQSxvQkFBRSxJQUFGLEdBQUE7QUFDVCxJQURVLElBQUMsQ0FBQSxPQUFBLElBQ1gsQ0FBQTtBQUFBLElBQUEsSUFBTyx3REFBUDtBQUNJLGFBQU8sSUFBQyxDQUFBLElBQUQsQ0FBTSxPQUFOLEVBQWUsZ0RBQWYsQ0FBUCxDQURKO0tBQUE7QUFBQSxJQUdBLElBQUMsQ0FBQSxNQUFELEdBQVUsQ0FIVixDQUFBO0FBQUEsSUFJQSxJQUFDLENBQUEsTUFBRCxHQUFVLElBQUMsQ0FBQSxJQUFJLENBQUMsSUFKaEIsQ0FBQTtBQUFBLElBS0EsSUFBQyxDQUFBLFNBQUQsR0FBYSxDQUFBLElBQUssRUFMbEIsQ0FBQTtBQUFBLElBTUEsSUFBQyxDQUFBLElBQUssQ0FBQSxJQUFDLENBQUEsS0FBRCxHQUFTLE9BQVQsQ0FBTixJQUEyQixJQUFDLENBQUEsSUFBSyxDQUFBLElBQUMsQ0FBQSxLQUFELEdBQVMsYUFBVCxDQUFqQyxJQUE0RCxJQUFDLENBQUEsSUFBSyxDQUFBLElBQUMsQ0FBQSxLQUFELEdBQVMsVUFBVCxDQU5sRSxDQURTO0VBQUEsQ0FBYjs7QUFBQSx1QkFTQSxLQUFBLEdBQU8sU0FBQSxHQUFBO0FBQ0gsSUFBQSxJQUFHLElBQUMsQ0FBQSxNQUFKO0FBQ0ksTUFBQSxJQUFBLENBQUEsSUFBdUIsQ0FBQSxNQUF2QjtBQUFBLGVBQU8sSUFBQyxDQUFBLElBQUQsQ0FBQSxDQUFQLENBQUE7T0FESjtLQUFBO0FBQUEsSUFHQSxJQUFDLENBQUEsTUFBRCxHQUFVLEdBQUEsQ0FBQSxVQUhWLENBQUE7QUFBQSxJQUlBLElBQUMsQ0FBQSxNQUFELEdBQVUsSUFKVixDQUFBO0FBQUEsSUFNQSxJQUFDLENBQUEsTUFBTSxDQUFDLE1BQVIsR0FBaUIsQ0FBQSxTQUFBLEtBQUEsR0FBQTthQUFBLFNBQUMsQ0FBRCxHQUFBO0FBQ2IsWUFBQSxHQUFBO0FBQUEsUUFBQSxHQUFBLEdBQVUsSUFBQSxRQUFBLENBQWEsSUFBQSxVQUFBLENBQVcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFwQixDQUFiLENBQVYsQ0FBQTtBQUFBLFFBQ0EsS0FBQyxDQUFBLE1BQUQsSUFBVyxHQUFHLENBQUMsTUFEZixDQUFBO0FBQUEsUUFHQSxLQUFDLENBQUEsSUFBRCxDQUFNLE1BQU4sRUFBYyxHQUFkLENBSEEsQ0FBQTtBQUFBLFFBSUEsS0FBQyxDQUFBLE1BQUQsR0FBVSxLQUpWLENBQUE7QUFLQSxRQUFBLElBQVcsS0FBQyxDQUFBLE1BQUQsR0FBVSxLQUFDLENBQUEsTUFBdEI7aUJBQUEsS0FBQyxDQUFBLElBQUQsQ0FBQSxFQUFBO1NBTmE7TUFBQSxFQUFBO0lBQUEsQ0FBQSxDQUFBLENBQUEsSUFBQSxDQU5qQixDQUFBO0FBQUEsSUFjQSxJQUFDLENBQUEsTUFBTSxDQUFDLFNBQVIsR0FBb0IsQ0FBQSxTQUFBLEtBQUEsR0FBQTthQUFBLFNBQUEsR0FBQTtBQUNoQixRQUFBLElBQUcsS0FBQyxDQUFBLE1BQUQsS0FBVyxLQUFDLENBQUEsTUFBZjtBQUNJLFVBQUEsS0FBQyxDQUFBLElBQUQsQ0FBTSxLQUFOLENBQUEsQ0FBQTtpQkFDQSxLQUFDLENBQUEsTUFBRCxHQUFVLEtBRmQ7U0FEZ0I7TUFBQSxFQUFBO0lBQUEsQ0FBQSxDQUFBLENBQUEsSUFBQSxDQWRwQixDQUFBO0FBQUEsSUFtQkEsSUFBQyxDQUFBLE1BQU0sQ0FBQyxPQUFSLEdBQWtCLENBQUEsU0FBQSxLQUFBLEdBQUE7YUFBQSxTQUFDLENBQUQsR0FBQTtlQUNkLEtBQUMsQ0FBQSxJQUFELENBQU0sT0FBTixFQUFlLENBQWYsRUFEYztNQUFBLEVBQUE7SUFBQSxDQUFBLENBQUEsQ0FBQSxJQUFBLENBbkJsQixDQUFBO0FBQUEsSUFzQkEsSUFBQyxDQUFBLE1BQU0sQ0FBQyxVQUFSLEdBQXFCLENBQUEsU0FBQSxLQUFBLEdBQUE7YUFBQSxTQUFDLENBQUQsR0FBQTtlQUNqQixLQUFDLENBQUEsSUFBRCxDQUFNLFVBQU4sRUFBa0IsQ0FBQyxLQUFDLENBQUEsTUFBRCxHQUFVLENBQUMsQ0FBQyxNQUFiLENBQUEsR0FBdUIsS0FBQyxDQUFBLE1BQXhCLEdBQWlDLEdBQW5ELEVBRGlCO01BQUEsRUFBQTtJQUFBLENBQUEsQ0FBQSxDQUFBLElBQUEsQ0F0QnJCLENBQUE7V0F5QkEsSUFBQyxDQUFBLElBQUQsQ0FBQSxFQTFCRztFQUFBLENBVFAsQ0FBQTs7QUFBQSx1QkFxQ0EsSUFBQSxHQUFNLFNBQUEsR0FBQTtBQUNGLFFBQUEsWUFBQTtBQUFBLElBQUEsSUFBQyxDQUFBLE1BQUQsR0FBVSxJQUFWLENBQUE7QUFBQSxJQUNBLE1BQUEsR0FBUyxJQUFJLENBQUMsR0FBTCxDQUFTLElBQUMsQ0FBQSxNQUFELEdBQVUsSUFBQyxDQUFBLFNBQXBCLEVBQStCLElBQUMsQ0FBQSxNQUFoQyxDQURULENBQUE7QUFBQSxJQUdBLElBQUEsR0FBTyxJQUFDLENBQUEsSUFBSyxDQUFBLElBQUMsQ0FBQSxLQUFELENBQU4sQ0FBYyxJQUFDLENBQUEsTUFBZixFQUF1QixNQUF2QixDQUhQLENBQUE7V0FJQSxJQUFDLENBQUEsTUFBTSxDQUFDLGlCQUFSLENBQTBCLElBQTFCLEVBTEU7RUFBQSxDQXJDTixDQUFBOztBQUFBLHVCQTRDQSxLQUFBLEdBQU8sU0FBQSxHQUFBO0FBQ0gsUUFBQSxJQUFBO0FBQUEsSUFBQSxJQUFDLENBQUEsTUFBRCxHQUFVLEtBQVYsQ0FBQTtBQUNBO2dEQUNTLENBQUUsS0FBVCxDQUFBLFdBREY7S0FBQSxrQkFGRztFQUFBLENBNUNQLENBQUE7O0FBQUEsdUJBaURBLEtBQUEsR0FBTyxTQUFBLEdBQUE7QUFDSCxJQUFBLElBQUMsQ0FBQSxLQUFELENBQUEsQ0FBQSxDQUFBO1dBQ0EsSUFBQyxDQUFBLE1BQUQsR0FBVSxFQUZQO0VBQUEsQ0FqRFAsQ0FBQTs7b0JBQUE7O0dBRHFCLGFBSHpCLENBQUE7O0FBQUEsTUF5RE0sQ0FBQyxPQUFQLEdBQWlCLFVBekRqQixDQUFBOzs7QUNBQSxJQUFBLGtDQUFBO0VBQUE7aVNBQUE7O0FBQUEsWUFBQSxHQUFlLE9BQUEsQ0FBUSxtQkFBUixDQUFmLENBQUE7O0FBQUEsUUFDQSxHQUFXLE9BQUEsQ0FBUSxtQkFBUixDQURYLENBQUE7O0FBQUE7QUFJSSwrQkFBQSxDQUFBOztBQUFhLEVBQUEsb0JBQUUsR0FBRixFQUFRLElBQVIsR0FBQTtBQUNULElBRFUsSUFBQyxDQUFBLE1BQUEsR0FDWCxDQUFBO0FBQUEsSUFEZ0IsSUFBQyxDQUFBLHNCQUFBLE9BQU8sRUFDeEIsQ0FBQTtBQUFBLElBQUEsSUFBQyxDQUFBLFNBQUQsR0FBYSxDQUFBLElBQUssRUFBbEIsQ0FBQTtBQUFBLElBQ0EsSUFBQyxDQUFBLFFBQUQsR0FBWSxLQURaLENBQUE7QUFFQSxJQUFBLElBQUcsSUFBQyxDQUFBLElBQUksQ0FBQyxNQUFUO0FBQ0ksTUFBQSxJQUFDLENBQUEsTUFBRCxHQUFVLElBQUMsQ0FBQSxJQUFJLENBQUMsTUFBaEIsQ0FESjtLQUZBO0FBQUEsSUFJQSxJQUFDLENBQUEsS0FBRCxDQUFBLENBSkEsQ0FEUztFQUFBLENBQWI7O0FBQUEsdUJBT0EsS0FBQSxHQUFPLFNBQUEsR0FBQTtBQUNILElBQUEsSUFBRyxJQUFDLENBQUEsTUFBSjtBQUNJLE1BQUEsSUFBQSxDQUFBLElBQXVCLENBQUEsUUFBdkI7QUFBQSxlQUFPLElBQUMsQ0FBQSxJQUFELENBQUEsQ0FBUCxDQUFBO09BREo7S0FBQTtBQUFBLElBR0EsSUFBQyxDQUFBLFFBQUQsR0FBWSxJQUhaLENBQUE7QUFBQSxJQUlBLElBQUMsQ0FBQSxHQUFELEdBQVcsSUFBQSxjQUFBLENBQUEsQ0FKWCxDQUFBO0FBQUEsSUFNQSxJQUFDLENBQUEsR0FBRyxDQUFDLE1BQUwsR0FBYyxDQUFBLFNBQUEsS0FBQSxHQUFBO2FBQUEsU0FBQyxLQUFELEdBQUE7QUFDVixRQUFBLEtBQUMsQ0FBQSxNQUFELEdBQVUsUUFBQSxDQUFTLEtBQUMsQ0FBQSxHQUFHLENBQUMsaUJBQUwsQ0FBdUIsZ0JBQXZCLENBQVQsQ0FBVixDQUFBO0FBQUEsUUFDQSxLQUFDLENBQUEsUUFBRCxHQUFZLEtBRFosQ0FBQTtlQUVBLEtBQUMsQ0FBQSxJQUFELENBQUEsRUFIVTtNQUFBLEVBQUE7SUFBQSxDQUFBLENBQUEsQ0FBQSxJQUFBLENBTmQsQ0FBQTtBQUFBLElBV0EsSUFBQyxDQUFBLEdBQUcsQ0FBQyxPQUFMLEdBQWUsQ0FBQSxTQUFBLEtBQUEsR0FBQTthQUFBLFNBQUMsR0FBRCxHQUFBO0FBQ1gsUUFBQSxLQUFDLENBQUEsS0FBRCxDQUFBLENBQUEsQ0FBQTtlQUNBLEtBQUMsQ0FBQSxJQUFELENBQU0sT0FBTixFQUFlLEdBQWYsRUFGVztNQUFBLEVBQUE7SUFBQSxDQUFBLENBQUEsQ0FBQSxJQUFBLENBWGYsQ0FBQTtBQUFBLElBZUEsSUFBQyxDQUFBLEdBQUcsQ0FBQyxPQUFMLEdBQWUsQ0FBQSxTQUFBLEtBQUEsR0FBQTthQUFBLFNBQUMsS0FBRCxHQUFBO2VBQ1gsS0FBQyxDQUFBLFFBQUQsR0FBWSxNQUREO01BQUEsRUFBQTtJQUFBLENBQUEsQ0FBQSxDQUFBLElBQUEsQ0FmZixDQUFBO0FBQUEsSUFrQkEsSUFBQyxDQUFBLEdBQUcsQ0FBQyxJQUFMLENBQVUsTUFBVixFQUFrQixJQUFDLENBQUEsR0FBbkIsRUFBd0IsSUFBeEIsQ0FsQkEsQ0FBQTtXQW1CQSxJQUFDLENBQUEsR0FBRyxDQUFDLElBQUwsQ0FBVSxJQUFWLEVBcEJHO0VBQUEsQ0FQUCxDQUFBOztBQUFBLHVCQTZCQSxJQUFBLEdBQU0sU0FBQSxHQUFBO0FBQ0YsUUFBQSxNQUFBO0FBQUEsSUFBQSxJQUFHLElBQUMsQ0FBQSxRQUFELElBQWEsQ0FBQSxJQUFLLENBQUEsTUFBckI7QUFDSSxhQUFPLElBQUMsQ0FBQSxJQUFELENBQU0sT0FBTixFQUFlLHVDQUFmLENBQVAsQ0FESjtLQUFBO0FBQUEsSUFHQSxJQUFDLENBQUEsUUFBRCxHQUFZLElBSFosQ0FBQTtBQUFBLElBSUEsSUFBQyxDQUFBLEdBQUQsR0FBVyxJQUFBLGNBQUEsQ0FBQSxDQUpYLENBQUE7QUFBQSxJQU1BLElBQUMsQ0FBQSxHQUFHLENBQUMsTUFBTCxHQUFjLENBQUEsU0FBQSxLQUFBLEdBQUE7YUFBQSxTQUFDLEtBQUQsR0FBQTtBQUNWLFlBQUEsNkJBQUE7QUFBQSxRQUFBLElBQUcsS0FBQyxDQUFBLEdBQUcsQ0FBQyxRQUFSO0FBQ0ksVUFBQSxHQUFBLEdBQVUsSUFBQSxVQUFBLENBQVcsS0FBQyxDQUFBLEdBQUcsQ0FBQyxRQUFoQixDQUFWLENBREo7U0FBQSxNQUFBO0FBR0ksVUFBQSxHQUFBLEdBQU0sS0FBQyxDQUFBLEdBQUcsQ0FBQyxZQUFYLENBQUE7QUFBQSxVQUNBLEdBQUEsR0FBVSxJQUFBLFVBQUEsQ0FBVyxHQUFHLENBQUMsTUFBZixDQURWLENBQUE7QUFFQSxlQUFTLDZGQUFULEdBQUE7QUFDSSxZQUFBLEdBQUksQ0FBQSxDQUFBLENBQUosR0FBUyxHQUFHLENBQUMsVUFBSixDQUFlLENBQWYsQ0FBQSxHQUFvQixJQUE3QixDQURKO0FBQUEsV0FMSjtTQUFBO0FBQUEsUUFRQSxNQUFBLEdBQWEsSUFBQSxRQUFBLENBQVMsR0FBVCxDQVJiLENBQUE7QUFBQSxRQVNBLEtBQUMsQ0FBQSxNQUFELElBQVcsTUFBTSxDQUFDLE1BVGxCLENBQUE7QUFBQSxRQVdBLEtBQUMsQ0FBQSxJQUFELENBQU0sTUFBTixFQUFjLE1BQWQsQ0FYQSxDQUFBO0FBWUEsUUFBQSxJQUFlLEtBQUMsQ0FBQSxNQUFELElBQVcsS0FBQyxDQUFBLE1BQTNCO0FBQUEsVUFBQSxLQUFDLENBQUEsSUFBRCxDQUFNLEtBQU4sQ0FBQSxDQUFBO1NBWkE7QUFBQSxRQWNBLEtBQUMsQ0FBQSxRQUFELEdBQVksS0FkWixDQUFBO0FBZUEsUUFBQSxJQUFBLENBQUEsQ0FBZSxLQUFDLENBQUEsTUFBRCxJQUFXLEtBQUMsQ0FBQSxNQUEzQixDQUFBO2lCQUFBLEtBQUMsQ0FBQSxJQUFELENBQUEsRUFBQTtTQWhCVTtNQUFBLEVBQUE7SUFBQSxDQUFBLENBQUEsQ0FBQSxJQUFBLENBTmQsQ0FBQTtBQUFBLElBd0JBLElBQUMsQ0FBQSxHQUFHLENBQUMsVUFBTCxHQUFrQixDQUFBLFNBQUEsS0FBQSxHQUFBO2FBQUEsU0FBQyxLQUFELEdBQUE7ZUFDZCxLQUFDLENBQUEsSUFBRCxDQUFNLFVBQU4sRUFBa0IsQ0FBQyxLQUFDLENBQUEsTUFBRCxHQUFVLEtBQUssQ0FBQyxNQUFqQixDQUFBLEdBQTJCLEtBQUMsQ0FBQSxNQUE1QixHQUFxQyxHQUF2RCxFQURjO01BQUEsRUFBQTtJQUFBLENBQUEsQ0FBQSxDQUFBLElBQUEsQ0F4QmxCLENBQUE7QUFBQSxJQTJCQSxJQUFDLENBQUEsR0FBRyxDQUFDLE9BQUwsR0FBZSxDQUFBLFNBQUEsS0FBQSxHQUFBO2FBQUEsU0FBQyxHQUFELEdBQUE7QUFDWCxRQUFBLEtBQUMsQ0FBQSxJQUFELENBQU0sT0FBTixFQUFlLEdBQWYsQ0FBQSxDQUFBO2VBQ0EsS0FBQyxDQUFBLEtBQUQsQ0FBQSxFQUZXO01BQUEsRUFBQTtJQUFBLENBQUEsQ0FBQSxDQUFBLElBQUEsQ0EzQmYsQ0FBQTtBQUFBLElBK0JBLElBQUMsQ0FBQSxHQUFHLENBQUMsT0FBTCxHQUFlLENBQUEsU0FBQSxLQUFBLEdBQUE7YUFBQSxTQUFDLEtBQUQsR0FBQTtlQUNYLEtBQUMsQ0FBQSxRQUFELEdBQVksTUFERDtNQUFBLEVBQUE7SUFBQSxDQUFBLENBQUEsQ0FBQSxJQUFBLENBL0JmLENBQUE7QUFBQSxJQWtDQSxJQUFDLENBQUEsR0FBRyxDQUFDLElBQUwsQ0FBVSxLQUFWLEVBQWlCLElBQUMsQ0FBQSxHQUFsQixFQUF1QixJQUF2QixDQWxDQSxDQUFBO0FBQUEsSUFtQ0EsSUFBQyxDQUFBLEdBQUcsQ0FBQyxZQUFMLEdBQW9CLGFBbkNwQixDQUFBO0FBQUEsSUFxQ0EsTUFBQSxHQUFTLElBQUksQ0FBQyxHQUFMLENBQVMsSUFBQyxDQUFBLE1BQUQsR0FBVSxJQUFDLENBQUEsU0FBcEIsRUFBK0IsSUFBQyxDQUFBLE1BQUQsR0FBVSxDQUF6QyxDQXJDVCxDQUFBO0FBQUEsSUFzQ0EsSUFBQyxDQUFBLEdBQUcsQ0FBQyxnQkFBTCxDQUFzQixlQUF0QixFQUF1QyxpQkFBdkMsQ0F0Q0EsQ0FBQTtBQUFBLElBdUNBLElBQUMsQ0FBQSxHQUFHLENBQUMsZ0JBQUwsQ0FBc0IsT0FBdEIsRUFBZ0MsUUFBQSxHQUFPLElBQUMsQ0FBQSxNQUFSLEdBQWdCLEdBQWhCLEdBQWtCLE1BQWxELENBdkNBLENBQUE7QUFBQSxJQXdDQSxJQUFDLENBQUEsR0FBRyxDQUFDLGdCQUFMLENBQXNCLG9DQUF0QixDQXhDQSxDQUFBO1dBeUNBLElBQUMsQ0FBQSxHQUFHLENBQUMsSUFBTCxDQUFVLElBQVYsRUExQ0U7RUFBQSxDQTdCTixDQUFBOztBQUFBLHVCQXlFQSxLQUFBLEdBQU8sU0FBQSxHQUFBO0FBQ0gsUUFBQSxJQUFBO0FBQUEsSUFBQSxJQUFDLENBQUEsUUFBRCxHQUFZLEtBQVosQ0FBQTsyQ0FDSSxDQUFFLEtBQU4sQ0FBQSxXQUZHO0VBQUEsQ0F6RVAsQ0FBQTs7QUFBQSx1QkE2RUEsS0FBQSxHQUFPLFNBQUEsR0FBQTtBQUNILElBQUEsSUFBQyxDQUFBLEtBQUQsQ0FBQSxDQUFBLENBQUE7V0FDQSxJQUFDLENBQUEsTUFBRCxHQUFVLEVBRlA7RUFBQSxDQTdFUCxDQUFBOztvQkFBQTs7R0FEcUIsYUFIekIsQ0FBQTs7QUFBQSxNQXFGTSxDQUFDLE9BQVAsR0FBaUIsVUFyRmpCLENBQUE7Ozs7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBBc3NldCwgQnVmZmVyU291cmNlLCBEZWNvZGVyLCBEZW11eGVyLCBFdmVudEVtaXR0ZXIsIEZpbGVTb3VyY2UsIEhUVFBTb3VyY2UsXG4gICAgX19iaW5kID0gZnVuY3Rpb24oZm4sIG1lKXsgcmV0dXJuIGZ1bmN0aW9uKCl7IHJldHVybiBmbi5hcHBseShtZSwgYXJndW1lbnRzKTsgfTsgfSxcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKCcuL2NvcmUvZXZlbnRzJyk7XG5cbiAgSFRUUFNvdXJjZSA9IHJlcXVpcmUoJy4vc291cmNlcy9ub2RlL2h0dHAnKTtcblxuICBGaWxlU291cmNlID0gcmVxdWlyZSgnLi9zb3VyY2VzL25vZGUvZmlsZScpO1xuXG4gIEJ1ZmZlclNvdXJjZSA9IHJlcXVpcmUoJy4vc291cmNlcy9idWZmZXInKTtcblxuICBEZW11eGVyID0gcmVxdWlyZSgnLi9kZW11eGVyJyk7XG5cbiAgRGVjb2RlciA9IHJlcXVpcmUoJy4vZGVjb2RlcicpO1xuXG4gIEFzc2V0ID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICAgIF9fZXh0ZW5kcyhBc3NldCwgX3N1cGVyKTtcblxuICAgIGZ1bmN0aW9uIEFzc2V0KHNvdXJjZSkge1xuICAgICAgdGhpcy5zb3VyY2UgPSBzb3VyY2U7XG4gICAgICB0aGlzLl9kZWNvZGUgPSBfX2JpbmQodGhpcy5fZGVjb2RlLCB0aGlzKTtcbiAgICAgIHRoaXMuZmluZERlY29kZXIgPSBfX2JpbmQodGhpcy5maW5kRGVjb2RlciwgdGhpcyk7XG4gICAgICB0aGlzLnByb2JlID0gX19iaW5kKHRoaXMucHJvYmUsIHRoaXMpO1xuICAgICAgdGhpcy5idWZmZXJlZCA9IDA7XG4gICAgICB0aGlzLmR1cmF0aW9uID0gbnVsbDtcbiAgICAgIHRoaXMuZm9ybWF0ID0gbnVsbDtcbiAgICAgIHRoaXMubWV0YWRhdGEgPSBudWxsO1xuICAgICAgdGhpcy5hY3RpdmUgPSBmYWxzZTtcbiAgICAgIHRoaXMuZGVtdXhlciA9IG51bGw7XG4gICAgICB0aGlzLmRlY29kZXIgPSBudWxsO1xuICAgICAgdGhpcy5zb3VyY2Uub25jZSgnZGF0YScsIHRoaXMucHJvYmUpO1xuICAgICAgdGhpcy5zb3VyY2Uub24oJ2Vycm9yJywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbihlcnIpIHtcbiAgICAgICAgICBfdGhpcy5lbWl0KCdlcnJvcicsIGVycik7XG4gICAgICAgICAgcmV0dXJuIF90aGlzLnN0b3AoKTtcbiAgICAgICAgfTtcbiAgICAgIH0pKHRoaXMpKTtcbiAgICAgIHRoaXMuc291cmNlLm9uKCdwcm9ncmVzcycsIChmdW5jdGlvbihfdGhpcykge1xuICAgICAgICByZXR1cm4gZnVuY3Rpb24oYnVmZmVyZWQpIHtcbiAgICAgICAgICBfdGhpcy5idWZmZXJlZCA9IGJ1ZmZlcmVkO1xuICAgICAgICAgIHJldHVybiBfdGhpcy5lbWl0KCdidWZmZXInLCBfdGhpcy5idWZmZXJlZCk7XG4gICAgICAgIH07XG4gICAgICB9KSh0aGlzKSk7XG4gICAgfVxuXG4gICAgQXNzZXQuZnJvbVVSTCA9IGZ1bmN0aW9uKHVybCwgb3B0cykge1xuICAgICAgcmV0dXJuIG5ldyBBc3NldChuZXcgSFRUUFNvdXJjZSh1cmwsIG9wdHMpKTtcbiAgICB9O1xuXG4gICAgQXNzZXQuZnJvbUZpbGUgPSBmdW5jdGlvbihmaWxlKSB7XG4gICAgICByZXR1cm4gbmV3IEFzc2V0KG5ldyBGaWxlU291cmNlKGZpbGUpKTtcbiAgICB9O1xuXG4gICAgQXNzZXQuZnJvbUJ1ZmZlciA9IGZ1bmN0aW9uKGJ1ZmZlcikge1xuICAgICAgcmV0dXJuIG5ldyBBc3NldChuZXcgQnVmZmVyU291cmNlKGJ1ZmZlcikpO1xuICAgIH07XG5cbiAgICBBc3NldC5wcm90b3R5cGUuc3RhcnQgPSBmdW5jdGlvbihkZWNvZGUpIHtcbiAgICAgIGlmICh0aGlzLmFjdGl2ZSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAoZGVjb2RlICE9IG51bGwpIHtcbiAgICAgICAgdGhpcy5zaG91bGREZWNvZGUgPSBkZWNvZGU7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5zaG91bGREZWNvZGUgPT0gbnVsbCkge1xuICAgICAgICB0aGlzLnNob3VsZERlY29kZSA9IHRydWU7XG4gICAgICB9XG4gICAgICB0aGlzLmFjdGl2ZSA9IHRydWU7XG4gICAgICB0aGlzLnNvdXJjZS5zdGFydCgpO1xuICAgICAgaWYgKHRoaXMuZGVjb2RlciAmJiB0aGlzLnNob3VsZERlY29kZSkge1xuICAgICAgICByZXR1cm4gdGhpcy5fZGVjb2RlKCk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIEFzc2V0LnByb3RvdHlwZS5zdG9wID0gZnVuY3Rpb24oKSB7XG4gICAgICBpZiAoIXRoaXMuYWN0aXZlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMuYWN0aXZlID0gZmFsc2U7XG4gICAgICByZXR1cm4gdGhpcy5zb3VyY2UucGF1c2UoKTtcbiAgICB9O1xuXG4gICAgQXNzZXQucHJvdG90eXBlLmdldCA9IGZ1bmN0aW9uKGV2ZW50LCBjYWxsYmFjaykge1xuICAgICAgaWYgKGV2ZW50ICE9PSAnZm9ybWF0JyAmJiBldmVudCAhPT0gJ2R1cmF0aW9uJyAmJiBldmVudCAhPT0gJ21ldGFkYXRhJykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAodGhpc1tldmVudF0gIT0gbnVsbCkge1xuICAgICAgICByZXR1cm4gY2FsbGJhY2sodGhpc1tldmVudF0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5vbmNlKGV2ZW50LCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgICByZXR1cm4gZnVuY3Rpb24odmFsdWUpIHtcbiAgICAgICAgICAgIF90aGlzLnN0b3AoKTtcbiAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayh2YWx1ZSk7XG4gICAgICAgICAgfTtcbiAgICAgICAgfSkodGhpcykpO1xuICAgICAgICByZXR1cm4gdGhpcy5zdGFydCgpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICBBc3NldC5wcm90b3R5cGUuZGVjb2RlUGFja2V0ID0gZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy5kZWNvZGVyLmRlY29kZSgpO1xuICAgIH07XG5cbiAgICBBc3NldC5wcm90b3R5cGUuZGVjb2RlVG9CdWZmZXIgPSBmdW5jdGlvbihjYWxsYmFjaykge1xuICAgICAgdmFyIGNodW5rcywgZGF0YUhhbmRsZXIsIGxlbmd0aDtcbiAgICAgIGxlbmd0aCA9IDA7XG4gICAgICBjaHVua3MgPSBbXTtcbiAgICAgIHRoaXMub24oJ2RhdGEnLCBkYXRhSGFuZGxlciA9IGZ1bmN0aW9uKGNodW5rKSB7XG4gICAgICAgIGxlbmd0aCArPSBjaHVuay5sZW5ndGg7XG4gICAgICAgIHJldHVybiBjaHVua3MucHVzaChjaHVuayk7XG4gICAgICB9KTtcbiAgICAgIHRoaXMub25jZSgnZW5kJywgZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBidWYsIGNodW5rLCBvZmZzZXQsIF9pLCBfbGVuO1xuICAgICAgICBidWYgPSBuZXcgRmxvYXQzMkFycmF5KGxlbmd0aCk7XG4gICAgICAgIG9mZnNldCA9IDA7XG4gICAgICAgIGZvciAoX2kgPSAwLCBfbGVuID0gY2h1bmtzLmxlbmd0aDsgX2kgPCBfbGVuOyBfaSsrKSB7XG4gICAgICAgICAgY2h1bmsgPSBjaHVua3NbX2ldO1xuICAgICAgICAgIGJ1Zi5zZXQoY2h1bmssIG9mZnNldCk7XG4gICAgICAgICAgb2Zmc2V0ICs9IGNodW5rLmxlbmd0aDtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLm9mZignZGF0YScsIGRhdGFIYW5kbGVyKTtcbiAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGJ1Zik7XG4gICAgICB9KTtcbiAgICAgIHJldHVybiB0aGlzLnN0YXJ0KCk7XG4gICAgfTtcblxuICAgIEFzc2V0LnByb3RvdHlwZS5wcm9iZSA9IGZ1bmN0aW9uKGNodW5rKSB7XG4gICAgICB2YXIgZGVtdXhlcjtcbiAgICAgIGlmICghdGhpcy5hY3RpdmUpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgZGVtdXhlciA9IERlbXV4ZXIuZmluZChjaHVuayk7XG4gICAgICBpZiAoIWRlbXV4ZXIpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZW1pdCgnZXJyb3InLCAnQSBkZW11eGVyIGZvciB0aGlzIGNvbnRhaW5lciB3YXMgbm90IGZvdW5kLicpO1xuICAgICAgfVxuICAgICAgdGhpcy5kZW11eGVyID0gbmV3IGRlbXV4ZXIodGhpcy5zb3VyY2UsIGNodW5rKTtcbiAgICAgIHRoaXMuZGVtdXhlci5vbignZm9ybWF0JywgdGhpcy5maW5kRGVjb2Rlcik7XG4gICAgICB0aGlzLmRlbXV4ZXIub24oJ2R1cmF0aW9uJywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbihkdXJhdGlvbikge1xuICAgICAgICAgIF90aGlzLmR1cmF0aW9uID0gZHVyYXRpb247XG4gICAgICAgICAgcmV0dXJuIF90aGlzLmVtaXQoJ2R1cmF0aW9uJywgX3RoaXMuZHVyYXRpb24pO1xuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgICAgdGhpcy5kZW11eGVyLm9uKCdtZXRhZGF0YScsIChmdW5jdGlvbihfdGhpcykge1xuICAgICAgICByZXR1cm4gZnVuY3Rpb24obWV0YWRhdGEpIHtcbiAgICAgICAgICBfdGhpcy5tZXRhZGF0YSA9IG1ldGFkYXRhO1xuICAgICAgICAgIHJldHVybiBfdGhpcy5lbWl0KCdtZXRhZGF0YScsIF90aGlzLm1ldGFkYXRhKTtcbiAgICAgICAgfTtcbiAgICAgIH0pKHRoaXMpKTtcbiAgICAgIHJldHVybiB0aGlzLmRlbXV4ZXIub24oJ2Vycm9yJywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbihlcnIpIHtcbiAgICAgICAgICBfdGhpcy5lbWl0KCdlcnJvcicsIGVycik7XG4gICAgICAgICAgcmV0dXJuIF90aGlzLnN0b3AoKTtcbiAgICAgICAgfTtcbiAgICAgIH0pKHRoaXMpKTtcbiAgICB9O1xuXG4gICAgQXNzZXQucHJvdG90eXBlLmZpbmREZWNvZGVyID0gZnVuY3Rpb24oZm9ybWF0KSB7XG4gICAgICB2YXIgZGVjb2RlciwgZGl2O1xuICAgICAgdGhpcy5mb3JtYXQgPSBmb3JtYXQ7XG4gICAgICBpZiAoIXRoaXMuYWN0aXZlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMuZW1pdCgnZm9ybWF0JywgdGhpcy5mb3JtYXQpO1xuICAgICAgZGVjb2RlciA9IERlY29kZXIuZmluZCh0aGlzLmZvcm1hdC5mb3JtYXRJRCk7XG4gICAgICBpZiAoIWRlY29kZXIpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZW1pdCgnZXJyb3InLCBcIkEgZGVjb2RlciBmb3IgXCIgKyB0aGlzLmZvcm1hdC5mb3JtYXRJRCArIFwiIHdhcyBub3QgZm91bmQuXCIpO1xuICAgICAgfVxuICAgICAgdGhpcy5kZWNvZGVyID0gbmV3IGRlY29kZXIodGhpcy5kZW11eGVyLCB0aGlzLmZvcm1hdCk7XG4gICAgICBpZiAodGhpcy5mb3JtYXQuZmxvYXRpbmdQb2ludCkge1xuICAgICAgICB0aGlzLmRlY29kZXIub24oJ2RhdGEnLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgICByZXR1cm4gZnVuY3Rpb24oYnVmZmVyKSB7XG4gICAgICAgICAgICByZXR1cm4gX3RoaXMuZW1pdCgnZGF0YScsIGJ1ZmZlcik7XG4gICAgICAgICAgfTtcbiAgICAgICAgfSkodGhpcykpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZGl2ID0gTWF0aC5wb3coMiwgdGhpcy5mb3JtYXQuYml0c1BlckNoYW5uZWwgLSAxKTtcbiAgICAgICAgdGhpcy5kZWNvZGVyLm9uKCdkYXRhJywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKGJ1ZmZlcikge1xuICAgICAgICAgICAgdmFyIGJ1ZiwgaSwgc2FtcGxlLCBfaSwgX2xlbjtcbiAgICAgICAgICAgIGJ1ZiA9IG5ldyBGbG9hdDMyQXJyYXkoYnVmZmVyLmxlbmd0aCk7XG4gICAgICAgICAgICBmb3IgKGkgPSBfaSA9IDAsIF9sZW4gPSBidWZmZXIubGVuZ3RoOyBfaSA8IF9sZW47IGkgPSArK19pKSB7XG4gICAgICAgICAgICAgIHNhbXBsZSA9IGJ1ZmZlcltpXTtcbiAgICAgICAgICAgICAgYnVmW2ldID0gc2FtcGxlIC8gZGl2O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIF90aGlzLmVtaXQoJ2RhdGEnLCBidWYpO1xuICAgICAgICAgIH07XG4gICAgICAgIH0pKHRoaXMpKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuZGVjb2Rlci5vbignZXJyb3InLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKGVycikge1xuICAgICAgICAgIF90aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTtcbiAgICAgICAgICByZXR1cm4gX3RoaXMuc3RvcCgpO1xuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgICAgdGhpcy5kZWNvZGVyLm9uKCdlbmQnLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICAgIHJldHVybiBfdGhpcy5lbWl0KCdlbmQnKTtcbiAgICAgICAgfTtcbiAgICAgIH0pKHRoaXMpKTtcbiAgICAgIHRoaXMuZW1pdCgnZGVjb2RlU3RhcnQnKTtcbiAgICAgIGlmICh0aGlzLnNob3VsZERlY29kZSkge1xuICAgICAgICByZXR1cm4gdGhpcy5fZGVjb2RlKCk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIEFzc2V0LnByb3RvdHlwZS5fZGVjb2RlID0gZnVuY3Rpb24oKSB7XG4gICAgICB3aGlsZSAodGhpcy5kZWNvZGVyLmRlY29kZSgpICYmIHRoaXMuYWN0aXZlKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuYWN0aXZlKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRlY29kZXIub25jZSgnZGF0YScsIHRoaXMuX2RlY29kZSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIEFzc2V0LnByb3RvdHlwZS5kZXN0cm95ID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgX3JlZiwgX3JlZjEsIF9yZWYyO1xuICAgICAgdGhpcy5zdG9wKCk7XG4gICAgICBpZiAoKF9yZWYgPSB0aGlzLmRlbXV4ZXIpICE9IG51bGwpIHtcbiAgICAgICAgX3JlZi5vZmYoKTtcbiAgICAgIH1cbiAgICAgIGlmICgoX3JlZjEgPSB0aGlzLmRlY29kZXIpICE9IG51bGwpIHtcbiAgICAgICAgX3JlZjEub2ZmKCk7XG4gICAgICB9XG4gICAgICBpZiAoKF9yZWYyID0gdGhpcy5zb3VyY2UpICE9IG51bGwpIHtcbiAgICAgICAgX3JlZjIub2ZmKCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5vZmYoKTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIEFzc2V0O1xuXG4gIH0pKEV2ZW50RW1pdHRlcik7XG5cbiAgbW9kdWxlLmV4cG9ydHMgPSBBc3NldDtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIGtleSwgdmFsLCBfcmVmO1xuXG4gIF9yZWYgPSByZXF1aXJlKCcuL2F1cm9yYV9iYXNlJyk7XG4gIGZvciAoa2V5IGluIF9yZWYpIHtcbiAgICB2YWwgPSBfcmVmW2tleV07XG4gICAgZXhwb3J0c1trZXldID0gdmFsO1xuICB9XG5cbiAgcmVxdWlyZSgnLi9kZW11eGVycy9jYWYnKTtcblxuICByZXF1aXJlKCcuL2RlbXV4ZXJzL200YScpO1xuXG4gIHJlcXVpcmUoJy4vZGVtdXhlcnMvYWlmZicpO1xuXG4gIHJlcXVpcmUoJy4vZGVtdXhlcnMvd2F2ZScpO1xuXG4gIHJlcXVpcmUoJy4vZGVtdXhlcnMvYXUnKTtcblxuICByZXF1aXJlKCcuL2RlY29kZXJzL2xwY20nKTtcblxuICByZXF1aXJlKCcuL2RlY29kZXJzL3hsYXcnKTtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgZXhwb3J0cy5CYXNlID0gcmVxdWlyZSgnLi9jb3JlL2Jhc2UnKTtcblxuICBleHBvcnRzLkJ1ZmZlciA9IHJlcXVpcmUoJy4vY29yZS9idWZmZXInKTtcblxuICBleHBvcnRzLkJ1ZmZlckxpc3QgPSByZXF1aXJlKCcuL2NvcmUvYnVmZmVybGlzdCcpO1xuXG4gIGV4cG9ydHMuU3RyZWFtID0gcmVxdWlyZSgnLi9jb3JlL3N0cmVhbScpO1xuXG4gIGV4cG9ydHMuQml0c3RyZWFtID0gcmVxdWlyZSgnLi9jb3JlL2JpdHN0cmVhbScpO1xuXG4gIGV4cG9ydHMuRXZlbnRFbWl0dGVyID0gcmVxdWlyZSgnLi9jb3JlL2V2ZW50cycpO1xuXG4gIGV4cG9ydHMuVW5kZXJmbG93RXJyb3IgPSByZXF1aXJlKCcuL2NvcmUvdW5kZXJmbG93Jyk7XG5cbiAgZXhwb3J0cy5IVFRQU291cmNlID0gcmVxdWlyZSgnLi9zb3VyY2VzL25vZGUvaHR0cCcpO1xuXG4gIGV4cG9ydHMuRmlsZVNvdXJjZSA9IHJlcXVpcmUoJy4vc291cmNlcy9ub2RlL2ZpbGUnKTtcblxuICBleHBvcnRzLkJ1ZmZlclNvdXJjZSA9IHJlcXVpcmUoJy4vc291cmNlcy9idWZmZXInKTtcblxuICBleHBvcnRzLkRlbXV4ZXIgPSByZXF1aXJlKCcuL2RlbXV4ZXInKTtcblxuICBleHBvcnRzLkRlY29kZXIgPSByZXF1aXJlKCcuL2RlY29kZXInKTtcblxuICBleHBvcnRzLkF1ZGlvRGV2aWNlID0gcmVxdWlyZSgnLi9kZXZpY2UnKTtcblxuICBleHBvcnRzLkFzc2V0ID0gcmVxdWlyZSgnLi9hc3NldCcpO1xuXG4gIGV4cG9ydHMuUGxheWVyID0gcmVxdWlyZSgnLi9wbGF5ZXInKTtcblxuICBleHBvcnRzLkZpbHRlciA9IHJlcXVpcmUoJy4vZmlsdGVyJyk7XG5cbiAgZXhwb3J0cy5Wb2x1bWVGaWx0ZXIgPSByZXF1aXJlKCcuL2ZpbHRlcnMvdm9sdW1lJyk7XG5cbiAgZXhwb3J0cy5CYWxhbmNlRmlsdGVyID0gcmVxdWlyZSgnLi9maWx0ZXJzL2JhbGFuY2UnKTtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIEJhc2UsXG4gICAgX19oYXNQcm9wID0ge30uaGFzT3duUHJvcGVydHksXG4gICAgX19leHRlbmRzID0gZnVuY3Rpb24oY2hpbGQsIHBhcmVudCkgeyBmb3IgKHZhciBrZXkgaW4gcGFyZW50KSB7IGlmIChfX2hhc1Byb3AuY2FsbChwYXJlbnQsIGtleSkpIGNoaWxkW2tleV0gPSBwYXJlbnRba2V5XTsgfSBmdW5jdGlvbiBjdG9yKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gY2hpbGQ7IH0gY3Rvci5wcm90b3R5cGUgPSBwYXJlbnQucHJvdG90eXBlOyBjaGlsZC5wcm90b3R5cGUgPSBuZXcgY3RvcigpOyBjaGlsZC5fX3N1cGVyX18gPSBwYXJlbnQucHJvdG90eXBlOyByZXR1cm4gY2hpbGQ7IH0sXG4gICAgX19pbmRleE9mID0gW10uaW5kZXhPZiB8fCBmdW5jdGlvbihpdGVtKSB7IGZvciAodmFyIGkgPSAwLCBsID0gdGhpcy5sZW5ndGg7IGkgPCBsOyBpKyspIHsgaWYgKGkgaW4gdGhpcyAmJiB0aGlzW2ldID09PSBpdGVtKSByZXR1cm4gaTsgfSByZXR1cm4gLTE7IH07XG5cbiAgQmFzZSA9IChmdW5jdGlvbigpIHtcbiAgICB2YXIgZm5UZXN0O1xuXG4gICAgZnVuY3Rpb24gQmFzZSgpIHt9XG5cbiAgICBmblRlc3QgPSAvXFxiX3N1cGVyXFxiLztcblxuICAgIEJhc2UuZXh0ZW5kID0gZnVuY3Rpb24ocHJvcCkge1xuICAgICAgdmFyIENsYXNzLCBmbiwga2V5LCBrZXlzLCBfcmVmLCBfc3VwZXI7XG4gICAgICBDbGFzcyA9IChmdW5jdGlvbihfc3VwZXIpIHtcbiAgICAgICAgX19leHRlbmRzKENsYXNzLCBfc3VwZXIpO1xuXG4gICAgICAgIGZ1bmN0aW9uIENsYXNzKCkge1xuICAgICAgICAgIHJldHVybiBDbGFzcy5fX3N1cGVyX18uY29uc3RydWN0b3IuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBDbGFzcztcblxuICAgICAgfSkodGhpcyk7XG4gICAgICBpZiAodHlwZW9mIHByb3AgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAga2V5cyA9IE9iamVjdC5rZXlzKENsYXNzLnByb3RvdHlwZSk7XG4gICAgICAgIHByb3AuY2FsbChDbGFzcywgQ2xhc3MpO1xuICAgICAgICBwcm9wID0ge307XG4gICAgICAgIF9yZWYgPSBDbGFzcy5wcm90b3R5cGU7XG4gICAgICAgIGZvciAoa2V5IGluIF9yZWYpIHtcbiAgICAgICAgICBmbiA9IF9yZWZba2V5XTtcbiAgICAgICAgICBpZiAoX19pbmRleE9mLmNhbGwoa2V5cywga2V5KSA8IDApIHtcbiAgICAgICAgICAgIHByb3Bba2V5XSA9IGZuO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgX3N1cGVyID0gQ2xhc3MuX19zdXBlcl9fO1xuICAgICAgZm9yIChrZXkgaW4gcHJvcCkge1xuICAgICAgICBmbiA9IHByb3Bba2V5XTtcbiAgICAgICAgaWYgKHR5cGVvZiBmbiA9PT0gJ2Z1bmN0aW9uJyAmJiBmblRlc3QudGVzdChmbikpIHtcbiAgICAgICAgICAoZnVuY3Rpb24oa2V5LCBmbikge1xuICAgICAgICAgICAgcmV0dXJuIENsYXNzLnByb3RvdHlwZVtrZXldID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgIHZhciByZXQsIHRtcDtcbiAgICAgICAgICAgICAgdG1wID0gdGhpcy5fc3VwZXI7XG4gICAgICAgICAgICAgIHRoaXMuX3N1cGVyID0gX3N1cGVyW2tleV07XG4gICAgICAgICAgICAgIHJldCA9IGZuLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgICAgIHRoaXMuX3N1cGVyID0gdG1wO1xuICAgICAgICAgICAgICByZXR1cm4gcmV0O1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9KShrZXksIGZuKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBDbGFzcy5wcm90b3R5cGVba2V5XSA9IGZuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gQ2xhc3M7XG4gICAgfTtcblxuICAgIHJldHVybiBCYXNlO1xuXG4gIH0pKCk7XG5cbiAgbW9kdWxlLmV4cG9ydHMgPSBCYXNlO1xuXG59KS5jYWxsKHRoaXMpO1xuIiwiLy8gR2VuZXJhdGVkIGJ5IENvZmZlZVNjcmlwdCAxLjcuMVxuKGZ1bmN0aW9uKCkge1xuICB2YXIgQml0c3RyZWFtO1xuXG4gIEJpdHN0cmVhbSA9IChmdW5jdGlvbigpIHtcbiAgICBmdW5jdGlvbiBCaXRzdHJlYW0oc3RyZWFtKSB7XG4gICAgICB0aGlzLnN0cmVhbSA9IHN0cmVhbTtcbiAgICAgIHRoaXMuYml0UG9zaXRpb24gPSAwO1xuICAgIH1cblxuICAgIEJpdHN0cmVhbS5wcm90b3R5cGUuY29weSA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHJlc3VsdDtcbiAgICAgIHJlc3VsdCA9IG5ldyBCaXRzdHJlYW0odGhpcy5zdHJlYW0uY29weSgpKTtcbiAgICAgIHJlc3VsdC5iaXRQb3NpdGlvbiA9IHRoaXMuYml0UG9zaXRpb247XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH07XG5cbiAgICBCaXRzdHJlYW0ucHJvdG90eXBlLm9mZnNldCA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIDggKiB0aGlzLnN0cmVhbS5vZmZzZXQgKyB0aGlzLmJpdFBvc2l0aW9uO1xuICAgIH07XG5cbiAgICBCaXRzdHJlYW0ucHJvdG90eXBlLmF2YWlsYWJsZSA9IGZ1bmN0aW9uKGJpdHMpIHtcbiAgICAgIHJldHVybiB0aGlzLnN0cmVhbS5hdmFpbGFibGUoKGJpdHMgKyA4IC0gdGhpcy5iaXRQb3NpdGlvbikgLyA4KTtcbiAgICB9O1xuXG4gICAgQml0c3RyZWFtLnByb3RvdHlwZS5hZHZhbmNlID0gZnVuY3Rpb24oYml0cykge1xuICAgICAgdmFyIHBvcztcbiAgICAgIHBvcyA9IHRoaXMuYml0UG9zaXRpb24gKyBiaXRzO1xuICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZShwb3MgPj4gMyk7XG4gICAgICByZXR1cm4gdGhpcy5iaXRQb3NpdGlvbiA9IHBvcyAmIDc7XG4gICAgfTtcblxuICAgIEJpdHN0cmVhbS5wcm90b3R5cGUucmV3aW5kID0gZnVuY3Rpb24oYml0cykge1xuICAgICAgdmFyIHBvcztcbiAgICAgIHBvcyA9IHRoaXMuYml0UG9zaXRpb24gLSBiaXRzO1xuICAgICAgdGhpcy5zdHJlYW0ucmV3aW5kKE1hdGguYWJzKHBvcyA+PiAzKSk7XG4gICAgICByZXR1cm4gdGhpcy5iaXRQb3NpdGlvbiA9IHBvcyAmIDc7XG4gICAgfTtcblxuICAgIEJpdHN0cmVhbS5wcm90b3R5cGUuc2VlayA9IGZ1bmN0aW9uKG9mZnNldCkge1xuICAgICAgdmFyIGN1ck9mZnNldDtcbiAgICAgIGN1ck9mZnNldCA9IHRoaXMub2Zmc2V0KCk7XG4gICAgICBpZiAob2Zmc2V0ID4gY3VyT2Zmc2V0KSB7XG4gICAgICAgIHJldHVybiB0aGlzLmFkdmFuY2Uob2Zmc2V0IC0gY3VyT2Zmc2V0KTtcbiAgICAgIH0gZWxzZSBpZiAob2Zmc2V0IDwgY3VyT2Zmc2V0KSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJld2luZChjdXJPZmZzZXQgLSBvZmZzZXQpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICBCaXRzdHJlYW0ucHJvdG90eXBlLmFsaWduID0gZnVuY3Rpb24oKSB7XG4gICAgICBpZiAodGhpcy5iaXRQb3NpdGlvbiAhPT0gMCkge1xuICAgICAgICB0aGlzLmJpdFBvc2l0aW9uID0gMDtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RyZWFtLmFkdmFuY2UoMSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIEJpdHN0cmVhbS5wcm90b3R5cGUucmVhZCA9IGZ1bmN0aW9uKGJpdHMsIHNpZ25lZCkge1xuICAgICAgdmFyIGEsIGEwLCBhMSwgYTIsIGEzLCBhNCwgbUJpdHM7XG4gICAgICBpZiAoYml0cyA9PT0gMCkge1xuICAgICAgICByZXR1cm4gMDtcbiAgICAgIH1cbiAgICAgIG1CaXRzID0gYml0cyArIHRoaXMuYml0UG9zaXRpb247XG4gICAgICBpZiAobUJpdHMgPD0gOCkge1xuICAgICAgICBhID0gKCh0aGlzLnN0cmVhbS5wZWVrVUludDgoKSA8PCB0aGlzLmJpdFBvc2l0aW9uKSAmIDB4ZmYpID4+PiAoOCAtIGJpdHMpO1xuICAgICAgfSBlbHNlIGlmIChtQml0cyA8PSAxNikge1xuICAgICAgICBhID0gKCh0aGlzLnN0cmVhbS5wZWVrVUludDE2KCkgPDwgdGhpcy5iaXRQb3NpdGlvbikgJiAweGZmZmYpID4+PiAoMTYgLSBiaXRzKTtcbiAgICAgIH0gZWxzZSBpZiAobUJpdHMgPD0gMjQpIHtcbiAgICAgICAgYSA9ICgodGhpcy5zdHJlYW0ucGVla1VJbnQyNCgpIDw8IHRoaXMuYml0UG9zaXRpb24pICYgMHhmZmZmZmYpID4+PiAoMjQgLSBiaXRzKTtcbiAgICAgIH0gZWxzZSBpZiAobUJpdHMgPD0gMzIpIHtcbiAgICAgICAgYSA9ICh0aGlzLnN0cmVhbS5wZWVrVUludDMyKCkgPDwgdGhpcy5iaXRQb3NpdGlvbikgPj4+ICgzMiAtIGJpdHMpO1xuICAgICAgfSBlbHNlIGlmIChtQml0cyA8PSA0MCkge1xuICAgICAgICBhMCA9IHRoaXMuc3RyZWFtLnBlZWtVSW50OCgwKSAqIDB4MDEwMDAwMDAwMDtcbiAgICAgICAgYTEgPSB0aGlzLnN0cmVhbS5wZWVrVUludDgoMSkgPDwgMjQgPj4+IDA7XG4gICAgICAgIGEyID0gdGhpcy5zdHJlYW0ucGVla1VJbnQ4KDIpIDw8IDE2O1xuICAgICAgICBhMyA9IHRoaXMuc3RyZWFtLnBlZWtVSW50OCgzKSA8PCA4O1xuICAgICAgICBhNCA9IHRoaXMuc3RyZWFtLnBlZWtVSW50OCg0KTtcbiAgICAgICAgYSA9IGEwICsgYTEgKyBhMiArIGEzICsgYTQ7XG4gICAgICAgIGEgJT0gTWF0aC5wb3coMiwgNDAgLSB0aGlzLmJpdFBvc2l0aW9uKTtcbiAgICAgICAgYSA9IE1hdGguZmxvb3IoYSAvIE1hdGgucG93KDIsIDQwIC0gdGhpcy5iaXRQb3NpdGlvbiAtIGJpdHMpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIlRvbyBtYW55IGJpdHMhXCIpO1xuICAgICAgfVxuICAgICAgaWYgKHNpZ25lZCkge1xuICAgICAgICBpZiAobUJpdHMgPCAzMikge1xuICAgICAgICAgIGlmIChhID4+PiAoYml0cyAtIDEpKSB7XG4gICAgICAgICAgICBhID0gKCgxIDw8IGJpdHMgPj4+IDApIC0gYSkgKiAtMTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWYgKGEgLyBNYXRoLnBvdygyLCBiaXRzIC0gMSkgfCAwKSB7XG4gICAgICAgICAgICBhID0gKE1hdGgucG93KDIsIGJpdHMpIC0gYSkgKiAtMTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHRoaXMuYWR2YW5jZShiaXRzKTtcbiAgICAgIHJldHVybiBhO1xuICAgIH07XG5cbiAgICBCaXRzdHJlYW0ucHJvdG90eXBlLnBlZWsgPSBmdW5jdGlvbihiaXRzLCBzaWduZWQpIHtcbiAgICAgIHZhciBhLCBhMCwgYTEsIGEyLCBhMywgYTQsIG1CaXRzO1xuICAgICAgaWYgKGJpdHMgPT09IDApIHtcbiAgICAgICAgcmV0dXJuIDA7XG4gICAgICB9XG4gICAgICBtQml0cyA9IGJpdHMgKyB0aGlzLmJpdFBvc2l0aW9uO1xuICAgICAgaWYgKG1CaXRzIDw9IDgpIHtcbiAgICAgICAgYSA9ICgodGhpcy5zdHJlYW0ucGVla1VJbnQ4KCkgPDwgdGhpcy5iaXRQb3NpdGlvbikgJiAweGZmKSA+Pj4gKDggLSBiaXRzKTtcbiAgICAgIH0gZWxzZSBpZiAobUJpdHMgPD0gMTYpIHtcbiAgICAgICAgYSA9ICgodGhpcy5zdHJlYW0ucGVla1VJbnQxNigpIDw8IHRoaXMuYml0UG9zaXRpb24pICYgMHhmZmZmKSA+Pj4gKDE2IC0gYml0cyk7XG4gICAgICB9IGVsc2UgaWYgKG1CaXRzIDw9IDI0KSB7XG4gICAgICAgIGEgPSAoKHRoaXMuc3RyZWFtLnBlZWtVSW50MjQoKSA8PCB0aGlzLmJpdFBvc2l0aW9uKSAmIDB4ZmZmZmZmKSA+Pj4gKDI0IC0gYml0cyk7XG4gICAgICB9IGVsc2UgaWYgKG1CaXRzIDw9IDMyKSB7XG4gICAgICAgIGEgPSAodGhpcy5zdHJlYW0ucGVla1VJbnQzMigpIDw8IHRoaXMuYml0UG9zaXRpb24pID4+PiAoMzIgLSBiaXRzKTtcbiAgICAgIH0gZWxzZSBpZiAobUJpdHMgPD0gNDApIHtcbiAgICAgICAgYTAgPSB0aGlzLnN0cmVhbS5wZWVrVUludDgoMCkgKiAweDAxMDAwMDAwMDA7XG4gICAgICAgIGExID0gdGhpcy5zdHJlYW0ucGVla1VJbnQ4KDEpIDw8IDI0ID4+PiAwO1xuICAgICAgICBhMiA9IHRoaXMuc3RyZWFtLnBlZWtVSW50OCgyKSA8PCAxNjtcbiAgICAgICAgYTMgPSB0aGlzLnN0cmVhbS5wZWVrVUludDgoMykgPDwgODtcbiAgICAgICAgYTQgPSB0aGlzLnN0cmVhbS5wZWVrVUludDgoNCk7XG4gICAgICAgIGEgPSBhMCArIGExICsgYTIgKyBhMyArIGE0O1xuICAgICAgICBhICU9IE1hdGgucG93KDIsIDQwIC0gdGhpcy5iaXRQb3NpdGlvbik7XG4gICAgICAgIGEgPSBNYXRoLmZsb29yKGEgLyBNYXRoLnBvdygyLCA0MCAtIHRoaXMuYml0UG9zaXRpb24gLSBiaXRzKSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJUb28gbWFueSBiaXRzIVwiKTtcbiAgICAgIH1cbiAgICAgIGlmIChzaWduZWQpIHtcbiAgICAgICAgaWYgKG1CaXRzIDwgMzIpIHtcbiAgICAgICAgICBpZiAoYSA+Pj4gKGJpdHMgLSAxKSkge1xuICAgICAgICAgICAgYSA9ICgoMSA8PCBiaXRzID4+PiAwKSAtIGEpICogLTE7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmIChhIC8gTWF0aC5wb3coMiwgYml0cyAtIDEpIHwgMCkge1xuICAgICAgICAgICAgYSA9IChNYXRoLnBvdygyLCBiaXRzKSAtIGEpICogLTE7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gYTtcbiAgICB9O1xuXG4gICAgQml0c3RyZWFtLnByb3RvdHlwZS5yZWFkTFNCID0gZnVuY3Rpb24oYml0cywgc2lnbmVkKSB7XG4gICAgICB2YXIgYSwgbUJpdHM7XG4gICAgICBpZiAoYml0cyA9PT0gMCkge1xuICAgICAgICByZXR1cm4gMDtcbiAgICAgIH1cbiAgICAgIGlmIChiaXRzID4gNDApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiVG9vIG1hbnkgYml0cyFcIik7XG4gICAgICB9XG4gICAgICBtQml0cyA9IGJpdHMgKyB0aGlzLmJpdFBvc2l0aW9uO1xuICAgICAgYSA9ICh0aGlzLnN0cmVhbS5wZWVrVUludDgoMCkpID4+PiB0aGlzLmJpdFBvc2l0aW9uO1xuICAgICAgaWYgKG1CaXRzID4gOCkge1xuICAgICAgICBhIHw9ICh0aGlzLnN0cmVhbS5wZWVrVUludDgoMSkpIDw8ICg4IC0gdGhpcy5iaXRQb3NpdGlvbik7XG4gICAgICB9XG4gICAgICBpZiAobUJpdHMgPiAxNikge1xuICAgICAgICBhIHw9ICh0aGlzLnN0cmVhbS5wZWVrVUludDgoMikpIDw8ICgxNiAtIHRoaXMuYml0UG9zaXRpb24pO1xuICAgICAgfVxuICAgICAgaWYgKG1CaXRzID4gMjQpIHtcbiAgICAgICAgYSArPSAodGhpcy5zdHJlYW0ucGVla1VJbnQ4KDMpKSA8PCAoMjQgLSB0aGlzLmJpdFBvc2l0aW9uKSA+Pj4gMDtcbiAgICAgIH1cbiAgICAgIGlmIChtQml0cyA+IDMyKSB7XG4gICAgICAgIGEgKz0gKHRoaXMuc3RyZWFtLnBlZWtVSW50OCg0KSkgKiBNYXRoLnBvdygyLCAzMiAtIHRoaXMuYml0UG9zaXRpb24pO1xuICAgICAgfVxuICAgICAgaWYgKG1CaXRzID49IDMyKSB7XG4gICAgICAgIGEgJT0gTWF0aC5wb3coMiwgYml0cyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhICY9ICgxIDw8IGJpdHMpIC0gMTtcbiAgICAgIH1cbiAgICAgIGlmIChzaWduZWQpIHtcbiAgICAgICAgaWYgKG1CaXRzIDwgMzIpIHtcbiAgICAgICAgICBpZiAoYSA+Pj4gKGJpdHMgLSAxKSkge1xuICAgICAgICAgICAgYSA9ICgoMSA8PCBiaXRzID4+PiAwKSAtIGEpICogLTE7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmIChhIC8gTWF0aC5wb3coMiwgYml0cyAtIDEpIHwgMCkge1xuICAgICAgICAgICAgYSA9IChNYXRoLnBvdygyLCBiaXRzKSAtIGEpICogLTE7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLmFkdmFuY2UoYml0cyk7XG4gICAgICByZXR1cm4gYTtcbiAgICB9O1xuXG4gICAgQml0c3RyZWFtLnByb3RvdHlwZS5wZWVrTFNCID0gZnVuY3Rpb24oYml0cywgc2lnbmVkKSB7XG4gICAgICB2YXIgYSwgbUJpdHM7XG4gICAgICBpZiAoYml0cyA9PT0gMCkge1xuICAgICAgICByZXR1cm4gMDtcbiAgICAgIH1cbiAgICAgIGlmIChiaXRzID4gNDApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiVG9vIG1hbnkgYml0cyFcIik7XG4gICAgICB9XG4gICAgICBtQml0cyA9IGJpdHMgKyB0aGlzLmJpdFBvc2l0aW9uO1xuICAgICAgYSA9ICh0aGlzLnN0cmVhbS5wZWVrVUludDgoMCkpID4+PiB0aGlzLmJpdFBvc2l0aW9uO1xuICAgICAgaWYgKG1CaXRzID4gOCkge1xuICAgICAgICBhIHw9ICh0aGlzLnN0cmVhbS5wZWVrVUludDgoMSkpIDw8ICg4IC0gdGhpcy5iaXRQb3NpdGlvbik7XG4gICAgICB9XG4gICAgICBpZiAobUJpdHMgPiAxNikge1xuICAgICAgICBhIHw9ICh0aGlzLnN0cmVhbS5wZWVrVUludDgoMikpIDw8ICgxNiAtIHRoaXMuYml0UG9zaXRpb24pO1xuICAgICAgfVxuICAgICAgaWYgKG1CaXRzID4gMjQpIHtcbiAgICAgICAgYSArPSAodGhpcy5zdHJlYW0ucGVla1VJbnQ4KDMpKSA8PCAoMjQgLSB0aGlzLmJpdFBvc2l0aW9uKSA+Pj4gMDtcbiAgICAgIH1cbiAgICAgIGlmIChtQml0cyA+IDMyKSB7XG4gICAgICAgIGEgKz0gKHRoaXMuc3RyZWFtLnBlZWtVSW50OCg0KSkgKiBNYXRoLnBvdygyLCAzMiAtIHRoaXMuYml0UG9zaXRpb24pO1xuICAgICAgfVxuICAgICAgaWYgKG1CaXRzID49IDMyKSB7XG4gICAgICAgIGEgJT0gTWF0aC5wb3coMiwgYml0cyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhICY9ICgxIDw8IGJpdHMpIC0gMTtcbiAgICAgIH1cbiAgICAgIGlmIChzaWduZWQpIHtcbiAgICAgICAgaWYgKG1CaXRzIDwgMzIpIHtcbiAgICAgICAgICBpZiAoYSA+Pj4gKGJpdHMgLSAxKSkge1xuICAgICAgICAgICAgYSA9ICgoMSA8PCBiaXRzID4+PiAwKSAtIGEpICogLTE7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmIChhIC8gTWF0aC5wb3coMiwgYml0cyAtIDEpIHwgMCkge1xuICAgICAgICAgICAgYSA9IChNYXRoLnBvdygyLCBiaXRzKSAtIGEpICogLTE7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gYTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIEJpdHN0cmVhbTtcblxuICB9KSgpO1xuXG4gIG1vZHVsZS5leHBvcnRzID0gQml0c3RyZWFtO1xuXG59KS5jYWxsKHRoaXMpO1xuIiwiLy8gR2VuZXJhdGVkIGJ5IENvZmZlZVNjcmlwdCAxLjcuMVxuKGZ1bmN0aW9uKCkge1xuICB2YXIgQVZCdWZmZXI7XG5cbiAgQVZCdWZmZXIgPSAoZnVuY3Rpb24oKSB7XG4gICAgdmFyIEJsb2JCdWlsZGVyLCBVUkw7XG5cbiAgICBmdW5jdGlvbiBBVkJ1ZmZlcihpbnB1dCkge1xuICAgICAgdmFyIF9yZWY7XG4gICAgICBpZiAoaW5wdXQgaW5zdGFuY2VvZiBVaW50OEFycmF5KSB7XG4gICAgICAgIHRoaXMuZGF0YSA9IGlucHV0O1xuICAgICAgfSBlbHNlIGlmIChpbnB1dCBpbnN0YW5jZW9mIEFycmF5QnVmZmVyIHx8IEFycmF5LmlzQXJyYXkoaW5wdXQpIHx8IHR5cGVvZiBpbnB1dCA9PT0gJ251bWJlcicgfHwgKChfcmVmID0gZ2xvYmFsLkJ1ZmZlcikgIT0gbnVsbCA/IF9yZWYuaXNCdWZmZXIoaW5wdXQpIDogdm9pZCAwKSkge1xuICAgICAgICB0aGlzLmRhdGEgPSBuZXcgVWludDhBcnJheShpbnB1dCk7XG4gICAgICB9IGVsc2UgaWYgKGlucHV0LmJ1ZmZlciBpbnN0YW5jZW9mIEFycmF5QnVmZmVyKSB7XG4gICAgICAgIHRoaXMuZGF0YSA9IG5ldyBVaW50OEFycmF5KGlucHV0LmJ1ZmZlciwgaW5wdXQuYnl0ZU9mZnNldCwgaW5wdXQubGVuZ3RoICogaW5wdXQuQllURVNfUEVSX0VMRU1FTlQpO1xuICAgICAgfSBlbHNlIGlmIChpbnB1dCBpbnN0YW5jZW9mIEFWQnVmZmVyKSB7XG4gICAgICAgIHRoaXMuZGF0YSA9IGlucHV0LmRhdGE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDb25zdHJ1Y3RpbmcgYnVmZmVyIHdpdGggdW5rbm93biB0eXBlLlwiKTtcbiAgICAgIH1cbiAgICAgIHRoaXMubGVuZ3RoID0gdGhpcy5kYXRhLmxlbmd0aDtcbiAgICAgIHRoaXMubmV4dCA9IG51bGw7XG4gICAgICB0aGlzLnByZXYgPSBudWxsO1xuICAgIH1cblxuICAgIEFWQnVmZmVyLmFsbG9jYXRlID0gZnVuY3Rpb24oc2l6ZSkge1xuICAgICAgcmV0dXJuIG5ldyBBVkJ1ZmZlcihzaXplKTtcbiAgICB9O1xuXG4gICAgQVZCdWZmZXIucHJvdG90eXBlLmNvcHkgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBuZXcgQVZCdWZmZXIobmV3IFVpbnQ4QXJyYXkodGhpcy5kYXRhKSk7XG4gICAgfTtcblxuICAgIEFWQnVmZmVyLnByb3RvdHlwZS5zbGljZSA9IGZ1bmN0aW9uKHBvc2l0aW9uLCBsZW5ndGgpIHtcbiAgICAgIGlmIChsZW5ndGggPT0gbnVsbCkge1xuICAgICAgICBsZW5ndGggPSB0aGlzLmxlbmd0aDtcbiAgICAgIH1cbiAgICAgIGlmIChwb3NpdGlvbiA9PT0gMCAmJiBsZW5ndGggPj0gdGhpcy5sZW5ndGgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBBVkJ1ZmZlcih0aGlzLmRhdGEpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIG5ldyBBVkJ1ZmZlcih0aGlzLmRhdGEuc3ViYXJyYXkocG9zaXRpb24sIHBvc2l0aW9uICsgbGVuZ3RoKSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIEJsb2JCdWlsZGVyID0gZ2xvYmFsLkJsb2JCdWlsZGVyIHx8IGdsb2JhbC5Nb3pCbG9iQnVpbGRlciB8fCBnbG9iYWwuV2ViS2l0QmxvYkJ1aWxkZXI7XG5cbiAgICBVUkwgPSBnbG9iYWwuVVJMIHx8IGdsb2JhbC53ZWJraXRVUkwgfHwgZ2xvYmFsLm1velVSTDtcblxuICAgIEFWQnVmZmVyLm1ha2VCbG9iID0gZnVuY3Rpb24oZGF0YSwgdHlwZSkge1xuICAgICAgdmFyIGJiO1xuICAgICAgaWYgKHR5cGUgPT0gbnVsbCkge1xuICAgICAgICB0eXBlID0gJ2FwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSc7XG4gICAgICB9XG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gbmV3IEJsb2IoW2RhdGFdLCB7XG4gICAgICAgICAgdHlwZTogdHlwZVxuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKF9lcnJvcikge31cbiAgICAgIGlmIChCbG9iQnVpbGRlciAhPSBudWxsKSB7XG4gICAgICAgIGJiID0gbmV3IEJsb2JCdWlsZGVyO1xuICAgICAgICBiYi5hcHBlbmQoZGF0YSk7XG4gICAgICAgIHJldHVybiBiYi5nZXRCbG9iKHR5cGUpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfTtcblxuICAgIEFWQnVmZmVyLm1ha2VCbG9iVVJMID0gZnVuY3Rpb24oZGF0YSwgdHlwZSkge1xuICAgICAgcmV0dXJuIFVSTCAhPSBudWxsID8gVVJMLmNyZWF0ZU9iamVjdFVSTCh0aGlzLm1ha2VCbG9iKGRhdGEsIHR5cGUpKSA6IHZvaWQgMDtcbiAgICB9O1xuXG4gICAgQVZCdWZmZXIucmV2b2tlQmxvYlVSTCA9IGZ1bmN0aW9uKHVybCkge1xuICAgICAgcmV0dXJuIFVSTCAhPSBudWxsID8gVVJMLnJldm9rZU9iamVjdFVSTCh1cmwpIDogdm9pZCAwO1xuICAgIH07XG5cbiAgICBBVkJ1ZmZlci5wcm90b3R5cGUudG9CbG9iID0gZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gQVZCdWZmZXIubWFrZUJsb2IodGhpcy5kYXRhLmJ1ZmZlcik7XG4gICAgfTtcblxuICAgIEFWQnVmZmVyLnByb3RvdHlwZS50b0Jsb2JVUkwgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBBVkJ1ZmZlci5tYWtlQmxvYlVSTCh0aGlzLmRhdGEuYnVmZmVyKTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIEFWQnVmZmVyO1xuXG4gIH0pKCk7XG5cbiAgbW9kdWxlLmV4cG9ydHMgPSBBVkJ1ZmZlcjtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIEJ1ZmZlckxpc3Q7XG5cbiAgQnVmZmVyTGlzdCA9IChmdW5jdGlvbigpIHtcbiAgICBmdW5jdGlvbiBCdWZmZXJMaXN0KCkge1xuICAgICAgdGhpcy5maXJzdCA9IG51bGw7XG4gICAgICB0aGlzLmxhc3QgPSBudWxsO1xuICAgICAgdGhpcy5udW1CdWZmZXJzID0gMDtcbiAgICAgIHRoaXMuYXZhaWxhYmxlQnl0ZXMgPSAwO1xuICAgICAgdGhpcy5hdmFpbGFibGVCdWZmZXJzID0gMDtcbiAgICB9XG5cbiAgICBCdWZmZXJMaXN0LnByb3RvdHlwZS5jb3B5ID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgcmVzdWx0O1xuICAgICAgcmVzdWx0ID0gbmV3IEJ1ZmZlckxpc3Q7XG4gICAgICByZXN1bHQuZmlyc3QgPSB0aGlzLmZpcnN0O1xuICAgICAgcmVzdWx0Lmxhc3QgPSB0aGlzLmxhc3Q7XG4gICAgICByZXN1bHQubnVtQnVmZmVycyA9IHRoaXMubnVtQnVmZmVycztcbiAgICAgIHJlc3VsdC5hdmFpbGFibGVCeXRlcyA9IHRoaXMuYXZhaWxhYmxlQnl0ZXM7XG4gICAgICByZXN1bHQuYXZhaWxhYmxlQnVmZmVycyA9IHRoaXMuYXZhaWxhYmxlQnVmZmVycztcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfTtcblxuICAgIEJ1ZmZlckxpc3QucHJvdG90eXBlLmFwcGVuZCA9IGZ1bmN0aW9uKGJ1ZmZlcikge1xuICAgICAgdmFyIF9yZWY7XG4gICAgICBidWZmZXIucHJldiA9IHRoaXMubGFzdDtcbiAgICAgIGlmICgoX3JlZiA9IHRoaXMubGFzdCkgIT0gbnVsbCkge1xuICAgICAgICBfcmVmLm5leHQgPSBidWZmZXI7XG4gICAgICB9XG4gICAgICB0aGlzLmxhc3QgPSBidWZmZXI7XG4gICAgICBpZiAodGhpcy5maXJzdCA9PSBudWxsKSB7XG4gICAgICAgIHRoaXMuZmlyc3QgPSBidWZmZXI7XG4gICAgICB9XG4gICAgICB0aGlzLmF2YWlsYWJsZUJ5dGVzICs9IGJ1ZmZlci5sZW5ndGg7XG4gICAgICB0aGlzLmF2YWlsYWJsZUJ1ZmZlcnMrKztcbiAgICAgIHJldHVybiB0aGlzLm51bUJ1ZmZlcnMrKztcbiAgICB9O1xuXG4gICAgQnVmZmVyTGlzdC5wcm90b3R5cGUuYWR2YW5jZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgaWYgKHRoaXMuZmlyc3QpIHtcbiAgICAgICAgdGhpcy5hdmFpbGFibGVCeXRlcyAtPSB0aGlzLmZpcnN0Lmxlbmd0aDtcbiAgICAgICAgdGhpcy5hdmFpbGFibGVCdWZmZXJzLS07XG4gICAgICAgIHRoaXMuZmlyc3QgPSB0aGlzLmZpcnN0Lm5leHQ7XG4gICAgICAgIHJldHVybiB0aGlzLmZpcnN0ICE9IG51bGw7XG4gICAgICB9XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfTtcblxuICAgIEJ1ZmZlckxpc3QucHJvdG90eXBlLnJld2luZCA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIF9yZWY7XG4gICAgICBpZiAodGhpcy5maXJzdCAmJiAhdGhpcy5maXJzdC5wcmV2KSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIHRoaXMuZmlyc3QgPSAoKF9yZWYgPSB0aGlzLmZpcnN0KSAhPSBudWxsID8gX3JlZi5wcmV2IDogdm9pZCAwKSB8fCB0aGlzLmxhc3Q7XG4gICAgICBpZiAodGhpcy5maXJzdCkge1xuICAgICAgICB0aGlzLmF2YWlsYWJsZUJ5dGVzICs9IHRoaXMuZmlyc3QubGVuZ3RoO1xuICAgICAgICB0aGlzLmF2YWlsYWJsZUJ1ZmZlcnMrKztcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLmZpcnN0ICE9IG51bGw7XG4gICAgfTtcblxuICAgIEJ1ZmZlckxpc3QucHJvdG90eXBlLnJlc2V0ID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgX3Jlc3VsdHM7XG4gICAgICBfcmVzdWx0cyA9IFtdO1xuICAgICAgd2hpbGUgKHRoaXMucmV3aW5kKCkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICByZXR1cm4gX3Jlc3VsdHM7XG4gICAgfTtcblxuICAgIHJldHVybiBCdWZmZXJMaXN0O1xuXG4gIH0pKCk7XG5cbiAgbW9kdWxlLmV4cG9ydHMgPSBCdWZmZXJMaXN0O1xuXG59KS5jYWxsKHRoaXMpO1xuIiwiLy8gR2VuZXJhdGVkIGJ5IENvZmZlZVNjcmlwdCAxLjcuMVxuKGZ1bmN0aW9uKCkge1xuICB2YXIgQmFzZSwgRXZlbnRFbWl0dGVyLFxuICAgIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICAgIF9fZXh0ZW5kcyA9IGZ1bmN0aW9uKGNoaWxkLCBwYXJlbnQpIHsgZm9yICh2YXIga2V5IGluIHBhcmVudCkgeyBpZiAoX19oYXNQcm9wLmNhbGwocGFyZW50LCBrZXkpKSBjaGlsZFtrZXldID0gcGFyZW50W2tleV07IH0gZnVuY3Rpb24gY3RvcigpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGNoaWxkOyB9IGN0b3IucHJvdG90eXBlID0gcGFyZW50LnByb3RvdHlwZTsgY2hpbGQucHJvdG90eXBlID0gbmV3IGN0b3IoKTsgY2hpbGQuX19zdXBlcl9fID0gcGFyZW50LnByb3RvdHlwZTsgcmV0dXJuIGNoaWxkOyB9LFxuICAgIF9fc2xpY2UgPSBbXS5zbGljZTtcblxuICBCYXNlID0gcmVxdWlyZSgnLi9iYXNlJyk7XG5cbiAgRXZlbnRFbWl0dGVyID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICAgIF9fZXh0ZW5kcyhFdmVudEVtaXR0ZXIsIF9zdXBlcik7XG5cbiAgICBmdW5jdGlvbiBFdmVudEVtaXR0ZXIoKSB7XG4gICAgICByZXR1cm4gRXZlbnRFbWl0dGVyLl9fc3VwZXJfXy5jb25zdHJ1Y3Rvci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH1cblxuICAgIEV2ZW50RW1pdHRlci5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldmVudCwgZm4pIHtcbiAgICAgIHZhciBfYmFzZTtcbiAgICAgIGlmICh0aGlzLmV2ZW50cyA9PSBudWxsKSB7XG4gICAgICAgIHRoaXMuZXZlbnRzID0ge307XG4gICAgICB9XG4gICAgICBpZiAoKF9iYXNlID0gdGhpcy5ldmVudHMpW2V2ZW50XSA9PSBudWxsKSB7XG4gICAgICAgIF9iYXNlW2V2ZW50XSA9IFtdO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuZXZlbnRzW2V2ZW50XS5wdXNoKGZuKTtcbiAgICB9O1xuXG4gICAgRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5vZmYgPSBmdW5jdGlvbihldmVudCwgZm4pIHtcbiAgICAgIHZhciBldmVudHMsIGluZGV4LCBfcmVmO1xuICAgICAgaWYgKHRoaXMuZXZlbnRzID09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKChfcmVmID0gdGhpcy5ldmVudHMpICE9IG51bGwgPyBfcmVmW2V2ZW50XSA6IHZvaWQgMCkge1xuICAgICAgICBpZiAoZm4gIT0gbnVsbCkge1xuICAgICAgICAgIGluZGV4ID0gdGhpcy5ldmVudHNbZXZlbnRdLmluZGV4T2YoZm4pO1xuICAgICAgICAgIGlmICh+aW5kZXgpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmV2ZW50c1tldmVudF0uc3BsaWNlKGluZGV4LCAxKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuZXZlbnRzW2V2ZW50XTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChldmVudCA9PSBudWxsKSB7XG4gICAgICAgIHJldHVybiBldmVudHMgPSB7fTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5vbmNlID0gZnVuY3Rpb24oZXZlbnQsIGZuKSB7XG4gICAgICB2YXIgY2I7XG4gICAgICByZXR1cm4gdGhpcy5vbihldmVudCwgY2IgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgdGhpcy5vZmYoZXZlbnQsIGNiKTtcbiAgICAgICAgcmV0dXJuIGZuLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICB9KTtcbiAgICB9O1xuXG4gICAgRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5lbWl0ID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgYXJncywgZXZlbnQsIGZuLCBfaSwgX2xlbiwgX3JlZiwgX3JlZjE7XG4gICAgICBldmVudCA9IGFyZ3VtZW50c1swXSwgYXJncyA9IDIgPD0gYXJndW1lbnRzLmxlbmd0aCA/IF9fc2xpY2UuY2FsbChhcmd1bWVudHMsIDEpIDogW107XG4gICAgICBpZiAoISgoX3JlZiA9IHRoaXMuZXZlbnRzKSAhPSBudWxsID8gX3JlZltldmVudF0gOiB2b2lkIDApKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIF9yZWYxID0gdGhpcy5ldmVudHNbZXZlbnRdLnNsaWNlKCk7XG4gICAgICBmb3IgKF9pID0gMCwgX2xlbiA9IF9yZWYxLmxlbmd0aDsgX2kgPCBfbGVuOyBfaSsrKSB7XG4gICAgICAgIGZuID0gX3JlZjFbX2ldO1xuICAgICAgICBmbi5hcHBseSh0aGlzLCBhcmdzKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgcmV0dXJuIEV2ZW50RW1pdHRlcjtcblxuICB9KShCYXNlKTtcblxuICBtb2R1bGUuZXhwb3J0cyA9IEV2ZW50RW1pdHRlcjtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIEFWQnVmZmVyLCBCdWZmZXJMaXN0LCBTdHJlYW0sIFVuZGVyZmxvd0Vycm9yO1xuXG4gIEJ1ZmZlckxpc3QgPSByZXF1aXJlKCcuL2J1ZmZlcmxpc3QnKTtcblxuICBBVkJ1ZmZlciA9IHJlcXVpcmUoJy4vYnVmZmVyJyk7XG5cbiAgVW5kZXJmbG93RXJyb3IgPSByZXF1aXJlKCcuL3VuZGVyZmxvdycpO1xuXG4gIFN0cmVhbSA9IChmdW5jdGlvbigpIHtcbiAgICB2YXIgYnVmLCBkZWNvZGVTdHJpbmcsIGZsb2F0MzIsIGZsb2F0NjQsIGZsb2F0NjRGYWxsYmFjaywgZmxvYXQ4MCwgaW50MTYsIGludDMyLCBpbnQ4LCBuYXRpdmVFbmRpYW4sIHVpbnQxNiwgdWludDMyLCB1aW50ODtcblxuICAgIGJ1ZiA9IG5ldyBBcnJheUJ1ZmZlcigxNik7XG5cbiAgICB1aW50OCA9IG5ldyBVaW50OEFycmF5KGJ1Zik7XG5cbiAgICBpbnQ4ID0gbmV3IEludDhBcnJheShidWYpO1xuXG4gICAgdWludDE2ID0gbmV3IFVpbnQxNkFycmF5KGJ1Zik7XG5cbiAgICBpbnQxNiA9IG5ldyBJbnQxNkFycmF5KGJ1Zik7XG5cbiAgICB1aW50MzIgPSBuZXcgVWludDMyQXJyYXkoYnVmKTtcblxuICAgIGludDMyID0gbmV3IEludDMyQXJyYXkoYnVmKTtcblxuICAgIGZsb2F0MzIgPSBuZXcgRmxvYXQzMkFycmF5KGJ1Zik7XG5cbiAgICBpZiAodHlwZW9mIEZsb2F0NjRBcnJheSAhPT0gXCJ1bmRlZmluZWRcIiAmJiBGbG9hdDY0QXJyYXkgIT09IG51bGwpIHtcbiAgICAgIGZsb2F0NjQgPSBuZXcgRmxvYXQ2NEFycmF5KGJ1Zik7XG4gICAgfVxuXG4gICAgbmF0aXZlRW5kaWFuID0gbmV3IFVpbnQxNkFycmF5KG5ldyBVaW50OEFycmF5KFsweDEyLCAweDM0XSkuYnVmZmVyKVswXSA9PT0gMHgzNDEyO1xuXG4gICAgZnVuY3Rpb24gU3RyZWFtKGxpc3QpIHtcbiAgICAgIHRoaXMubGlzdCA9IGxpc3Q7XG4gICAgICB0aGlzLmxvY2FsT2Zmc2V0ID0gMDtcbiAgICAgIHRoaXMub2Zmc2V0ID0gMDtcbiAgICB9XG5cbiAgICBTdHJlYW0uZnJvbUJ1ZmZlciA9IGZ1bmN0aW9uKGJ1ZmZlcikge1xuICAgICAgdmFyIGxpc3Q7XG4gICAgICBsaXN0ID0gbmV3IEJ1ZmZlckxpc3Q7XG4gICAgICBsaXN0LmFwcGVuZChidWZmZXIpO1xuICAgICAgcmV0dXJuIG5ldyBTdHJlYW0obGlzdCk7XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUuY29weSA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHJlc3VsdDtcbiAgICAgIHJlc3VsdCA9IG5ldyBTdHJlYW0odGhpcy5saXN0LmNvcHkoKSk7XG4gICAgICByZXN1bHQubG9jYWxPZmZzZXQgPSB0aGlzLmxvY2FsT2Zmc2V0O1xuICAgICAgcmVzdWx0Lm9mZnNldCA9IHRoaXMub2Zmc2V0O1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5hdmFpbGFibGUgPSBmdW5jdGlvbihieXRlcykge1xuICAgICAgcmV0dXJuIGJ5dGVzIDw9IHRoaXMubGlzdC5hdmFpbGFibGVCeXRlcyAtIHRoaXMubG9jYWxPZmZzZXQ7XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucmVtYWluaW5nQnl0ZXMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiB0aGlzLmxpc3QuYXZhaWxhYmxlQnl0ZXMgLSB0aGlzLmxvY2FsT2Zmc2V0O1xuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLmFkdmFuY2UgPSBmdW5jdGlvbihieXRlcykge1xuICAgICAgaWYgKCF0aGlzLmF2YWlsYWJsZShieXRlcykpIHtcbiAgICAgICAgdGhyb3cgbmV3IFVuZGVyZmxvd0Vycm9yKCk7XG4gICAgICB9XG4gICAgICB0aGlzLmxvY2FsT2Zmc2V0ICs9IGJ5dGVzO1xuICAgICAgdGhpcy5vZmZzZXQgKz0gYnl0ZXM7XG4gICAgICB3aGlsZSAodGhpcy5saXN0LmZpcnN0ICYmIHRoaXMubG9jYWxPZmZzZXQgPj0gdGhpcy5saXN0LmZpcnN0Lmxlbmd0aCkge1xuICAgICAgICB0aGlzLmxvY2FsT2Zmc2V0IC09IHRoaXMubGlzdC5maXJzdC5sZW5ndGg7XG4gICAgICAgIHRoaXMubGlzdC5hZHZhbmNlKCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5yZXdpbmQgPSBmdW5jdGlvbihieXRlcykge1xuICAgICAgaWYgKGJ5dGVzID4gdGhpcy5vZmZzZXQpIHtcbiAgICAgICAgdGhyb3cgbmV3IFVuZGVyZmxvd0Vycm9yKCk7XG4gICAgICB9XG4gICAgICBpZiAoIXRoaXMubGlzdC5maXJzdCkge1xuICAgICAgICB0aGlzLmxpc3QucmV3aW5kKCk7XG4gICAgICAgIHRoaXMubG9jYWxPZmZzZXQgPSB0aGlzLmxpc3QuZmlyc3QubGVuZ3RoO1xuICAgICAgfVxuICAgICAgdGhpcy5sb2NhbE9mZnNldCAtPSBieXRlcztcbiAgICAgIHRoaXMub2Zmc2V0IC09IGJ5dGVzO1xuICAgICAgd2hpbGUgKHRoaXMubGlzdC5maXJzdC5wcmV2ICYmIHRoaXMubG9jYWxPZmZzZXQgPCAwKSB7XG4gICAgICAgIHRoaXMubGlzdC5yZXdpbmQoKTtcbiAgICAgICAgdGhpcy5sb2NhbE9mZnNldCArPSB0aGlzLmxpc3QuZmlyc3QubGVuZ3RoO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUuc2VlayA9IGZ1bmN0aW9uKHBvc2l0aW9uKSB7XG4gICAgICBpZiAocG9zaXRpb24gPiB0aGlzLm9mZnNldCkge1xuICAgICAgICByZXR1cm4gdGhpcy5hZHZhbmNlKHBvc2l0aW9uIC0gdGhpcy5vZmZzZXQpO1xuICAgICAgfSBlbHNlIGlmIChwb3NpdGlvbiA8IHRoaXMub2Zmc2V0KSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJld2luZCh0aGlzLm9mZnNldCAtIHBvc2l0aW9uKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5yZWFkVUludDggPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBhO1xuICAgICAgaWYgKCF0aGlzLmF2YWlsYWJsZSgxKSkge1xuICAgICAgICB0aHJvdyBuZXcgVW5kZXJmbG93RXJyb3IoKTtcbiAgICAgIH1cbiAgICAgIGEgPSB0aGlzLmxpc3QuZmlyc3QuZGF0YVt0aGlzLmxvY2FsT2Zmc2V0XTtcbiAgICAgIHRoaXMubG9jYWxPZmZzZXQgKz0gMTtcbiAgICAgIHRoaXMub2Zmc2V0ICs9IDE7XG4gICAgICBpZiAodGhpcy5sb2NhbE9mZnNldCA9PT0gdGhpcy5saXN0LmZpcnN0Lmxlbmd0aCkge1xuICAgICAgICB0aGlzLmxvY2FsT2Zmc2V0ID0gMDtcbiAgICAgICAgdGhpcy5saXN0LmFkdmFuY2UoKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBhO1xuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnBlZWtVSW50OCA9IGZ1bmN0aW9uKG9mZnNldCkge1xuICAgICAgdmFyIGJ1ZmZlcjtcbiAgICAgIGlmIChvZmZzZXQgPT0gbnVsbCkge1xuICAgICAgICBvZmZzZXQgPSAwO1xuICAgICAgfVxuICAgICAgaWYgKCF0aGlzLmF2YWlsYWJsZShvZmZzZXQgKyAxKSkge1xuICAgICAgICB0aHJvdyBuZXcgVW5kZXJmbG93RXJyb3IoKTtcbiAgICAgIH1cbiAgICAgIG9mZnNldCA9IHRoaXMubG9jYWxPZmZzZXQgKyBvZmZzZXQ7XG4gICAgICBidWZmZXIgPSB0aGlzLmxpc3QuZmlyc3Q7XG4gICAgICB3aGlsZSAoYnVmZmVyKSB7XG4gICAgICAgIGlmIChidWZmZXIubGVuZ3RoID4gb2Zmc2V0KSB7XG4gICAgICAgICAgcmV0dXJuIGJ1ZmZlci5kYXRhW29mZnNldF07XG4gICAgICAgIH1cbiAgICAgICAgb2Zmc2V0IC09IGJ1ZmZlci5sZW5ndGg7XG4gICAgICAgIGJ1ZmZlciA9IGJ1ZmZlci5uZXh0O1xuICAgICAgfVxuICAgICAgcmV0dXJuIDA7XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucmVhZCA9IGZ1bmN0aW9uKGJ5dGVzLCBsaXR0bGVFbmRpYW4pIHtcbiAgICAgIHZhciBpLCBfaSwgX2osIF9yZWY7XG4gICAgICBpZiAobGl0dGxlRW5kaWFuID09IG51bGwpIHtcbiAgICAgICAgbGl0dGxlRW5kaWFuID0gZmFsc2U7XG4gICAgICB9XG4gICAgICBpZiAobGl0dGxlRW5kaWFuID09PSBuYXRpdmVFbmRpYW4pIHtcbiAgICAgICAgZm9yIChpID0gX2kgPSAwOyBfaSA8IGJ5dGVzOyBpID0gX2kgKz0gMSkge1xuICAgICAgICAgIHVpbnQ4W2ldID0gdGhpcy5yZWFkVUludDgoKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZm9yIChpID0gX2ogPSBfcmVmID0gYnl0ZXMgLSAxOyBfaiA+PSAwOyBpID0gX2ogKz0gLTEpIHtcbiAgICAgICAgICB1aW50OFtpXSA9IHRoaXMucmVhZFVJbnQ4KCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5wZWVrID0gZnVuY3Rpb24oYnl0ZXMsIG9mZnNldCwgbGl0dGxlRW5kaWFuKSB7XG4gICAgICB2YXIgaSwgX2ksIF9qO1xuICAgICAgaWYgKGxpdHRsZUVuZGlhbiA9PSBudWxsKSB7XG4gICAgICAgIGxpdHRsZUVuZGlhbiA9IGZhbHNlO1xuICAgICAgfVxuICAgICAgaWYgKGxpdHRsZUVuZGlhbiA9PT0gbmF0aXZlRW5kaWFuKSB7XG4gICAgICAgIGZvciAoaSA9IF9pID0gMDsgX2kgPCBieXRlczsgaSA9IF9pICs9IDEpIHtcbiAgICAgICAgICB1aW50OFtpXSA9IHRoaXMucGVla1VJbnQ4KG9mZnNldCArIGkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmb3IgKGkgPSBfaiA9IDA7IF9qIDwgYnl0ZXM7IGkgPSBfaiArPSAxKSB7XG4gICAgICAgICAgdWludDhbYnl0ZXMgLSBpIC0gMV0gPSB0aGlzLnBlZWtVSW50OChvZmZzZXQgKyBpKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnJlYWRJbnQ4ID0gZnVuY3Rpb24oKSB7XG4gICAgICB0aGlzLnJlYWQoMSk7XG4gICAgICByZXR1cm4gaW50OFswXTtcbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5wZWVrSW50OCA9IGZ1bmN0aW9uKG9mZnNldCkge1xuICAgICAgaWYgKG9mZnNldCA9PSBudWxsKSB7XG4gICAgICAgIG9mZnNldCA9IDA7XG4gICAgICB9XG4gICAgICB0aGlzLnBlZWsoMSwgb2Zmc2V0KTtcbiAgICAgIHJldHVybiBpbnQ4WzBdO1xuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnJlYWRVSW50MTYgPSBmdW5jdGlvbihsaXR0bGVFbmRpYW4pIHtcbiAgICAgIHRoaXMucmVhZCgyLCBsaXR0bGVFbmRpYW4pO1xuICAgICAgcmV0dXJuIHVpbnQxNlswXTtcbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5wZWVrVUludDE2ID0gZnVuY3Rpb24ob2Zmc2V0LCBsaXR0bGVFbmRpYW4pIHtcbiAgICAgIGlmIChvZmZzZXQgPT0gbnVsbCkge1xuICAgICAgICBvZmZzZXQgPSAwO1xuICAgICAgfVxuICAgICAgdGhpcy5wZWVrKDIsIG9mZnNldCwgbGl0dGxlRW5kaWFuKTtcbiAgICAgIHJldHVybiB1aW50MTZbMF07XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucmVhZEludDE2ID0gZnVuY3Rpb24obGl0dGxlRW5kaWFuKSB7XG4gICAgICB0aGlzLnJlYWQoMiwgbGl0dGxlRW5kaWFuKTtcbiAgICAgIHJldHVybiBpbnQxNlswXTtcbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5wZWVrSW50MTYgPSBmdW5jdGlvbihvZmZzZXQsIGxpdHRsZUVuZGlhbikge1xuICAgICAgaWYgKG9mZnNldCA9PSBudWxsKSB7XG4gICAgICAgIG9mZnNldCA9IDA7XG4gICAgICB9XG4gICAgICB0aGlzLnBlZWsoMiwgb2Zmc2V0LCBsaXR0bGVFbmRpYW4pO1xuICAgICAgcmV0dXJuIGludDE2WzBdO1xuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnJlYWRVSW50MjQgPSBmdW5jdGlvbihsaXR0bGVFbmRpYW4pIHtcbiAgICAgIGlmIChsaXR0bGVFbmRpYW4pIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmVhZFVJbnQxNih0cnVlKSArICh0aGlzLnJlYWRVSW50OCgpIDw8IDE2KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiAodGhpcy5yZWFkVUludDE2KCkgPDwgOCkgKyB0aGlzLnJlYWRVSW50OCgpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnBlZWtVSW50MjQgPSBmdW5jdGlvbihvZmZzZXQsIGxpdHRsZUVuZGlhbikge1xuICAgICAgaWYgKG9mZnNldCA9PSBudWxsKSB7XG4gICAgICAgIG9mZnNldCA9IDA7XG4gICAgICB9XG4gICAgICBpZiAobGl0dGxlRW5kaWFuKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBlZWtVSW50MTYob2Zmc2V0LCB0cnVlKSArICh0aGlzLnBlZWtVSW50OChvZmZzZXQgKyAyKSA8PCAxNik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gKHRoaXMucGVla1VJbnQxNihvZmZzZXQpIDw8IDgpICsgdGhpcy5wZWVrVUludDgob2Zmc2V0ICsgMik7XG4gICAgICB9XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucmVhZEludDI0ID0gZnVuY3Rpb24obGl0dGxlRW5kaWFuKSB7XG4gICAgICBpZiAobGl0dGxlRW5kaWFuKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJlYWRVSW50MTYodHJ1ZSkgKyAodGhpcy5yZWFkSW50OCgpIDw8IDE2KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiAodGhpcy5yZWFkSW50MTYoKSA8PCA4KSArIHRoaXMucmVhZFVJbnQ4KCk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucGVla0ludDI0ID0gZnVuY3Rpb24ob2Zmc2V0LCBsaXR0bGVFbmRpYW4pIHtcbiAgICAgIGlmIChvZmZzZXQgPT0gbnVsbCkge1xuICAgICAgICBvZmZzZXQgPSAwO1xuICAgICAgfVxuICAgICAgaWYgKGxpdHRsZUVuZGlhbikge1xuICAgICAgICByZXR1cm4gdGhpcy5wZWVrVUludDE2KG9mZnNldCwgdHJ1ZSkgKyAodGhpcy5wZWVrSW50OChvZmZzZXQgKyAyKSA8PCAxNik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gKHRoaXMucGVla0ludDE2KG9mZnNldCkgPDwgOCkgKyB0aGlzLnBlZWtVSW50OChvZmZzZXQgKyAyKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5yZWFkVUludDMyID0gZnVuY3Rpb24obGl0dGxlRW5kaWFuKSB7XG4gICAgICB0aGlzLnJlYWQoNCwgbGl0dGxlRW5kaWFuKTtcbiAgICAgIHJldHVybiB1aW50MzJbMF07XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucGVla1VJbnQzMiA9IGZ1bmN0aW9uKG9mZnNldCwgbGl0dGxlRW5kaWFuKSB7XG4gICAgICBpZiAob2Zmc2V0ID09IG51bGwpIHtcbiAgICAgICAgb2Zmc2V0ID0gMDtcbiAgICAgIH1cbiAgICAgIHRoaXMucGVlayg0LCBvZmZzZXQsIGxpdHRsZUVuZGlhbik7XG4gICAgICByZXR1cm4gdWludDMyWzBdO1xuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnJlYWRJbnQzMiA9IGZ1bmN0aW9uKGxpdHRsZUVuZGlhbikge1xuICAgICAgdGhpcy5yZWFkKDQsIGxpdHRsZUVuZGlhbik7XG4gICAgICByZXR1cm4gaW50MzJbMF07XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucGVla0ludDMyID0gZnVuY3Rpb24ob2Zmc2V0LCBsaXR0bGVFbmRpYW4pIHtcbiAgICAgIGlmIChvZmZzZXQgPT0gbnVsbCkge1xuICAgICAgICBvZmZzZXQgPSAwO1xuICAgICAgfVxuICAgICAgdGhpcy5wZWVrKDQsIG9mZnNldCwgbGl0dGxlRW5kaWFuKTtcbiAgICAgIHJldHVybiBpbnQzMlswXTtcbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5yZWFkRmxvYXQzMiA9IGZ1bmN0aW9uKGxpdHRsZUVuZGlhbikge1xuICAgICAgdGhpcy5yZWFkKDQsIGxpdHRsZUVuZGlhbik7XG4gICAgICByZXR1cm4gZmxvYXQzMlswXTtcbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5wZWVrRmxvYXQzMiA9IGZ1bmN0aW9uKG9mZnNldCwgbGl0dGxlRW5kaWFuKSB7XG4gICAgICBpZiAob2Zmc2V0ID09IG51bGwpIHtcbiAgICAgICAgb2Zmc2V0ID0gMDtcbiAgICAgIH1cbiAgICAgIHRoaXMucGVlayg0LCBvZmZzZXQsIGxpdHRsZUVuZGlhbik7XG4gICAgICByZXR1cm4gZmxvYXQzMlswXTtcbiAgICB9O1xuXG4gICAgU3RyZWFtLnByb3RvdHlwZS5yZWFkRmxvYXQ2NCA9IGZ1bmN0aW9uKGxpdHRsZUVuZGlhbikge1xuICAgICAgdGhpcy5yZWFkKDgsIGxpdHRsZUVuZGlhbik7XG4gICAgICBpZiAoZmxvYXQ2NCkge1xuICAgICAgICByZXR1cm4gZmxvYXQ2NFswXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBmbG9hdDY0RmFsbGJhY2soKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgZmxvYXQ2NEZhbGxiYWNrID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgZXhwLCBmcmFjLCBoaWdoLCBsb3csIG91dCwgc2lnbjtcbiAgICAgIGxvdyA9IHVpbnQzMlswXSwgaGlnaCA9IHVpbnQzMlsxXTtcbiAgICAgIGlmICghaGlnaCB8fCBoaWdoID09PSAweDgwMDAwMDAwKSB7XG4gICAgICAgIHJldHVybiAwLjA7XG4gICAgICB9XG4gICAgICBzaWduID0gMSAtIChoaWdoID4+PiAzMSkgKiAyO1xuICAgICAgZXhwID0gKGhpZ2ggPj4+IDIwKSAmIDB4N2ZmO1xuICAgICAgZnJhYyA9IGhpZ2ggJiAweGZmZmZmO1xuICAgICAgaWYgKGV4cCA9PT0gMHg3ZmYpIHtcbiAgICAgICAgaWYgKGZyYWMpIHtcbiAgICAgICAgICByZXR1cm4gTmFOO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBzaWduICogSW5maW5pdHk7XG4gICAgICB9XG4gICAgICBleHAgLT0gMTAyMztcbiAgICAgIG91dCA9IChmcmFjIHwgMHgxMDAwMDApICogTWF0aC5wb3coMiwgZXhwIC0gMjApO1xuICAgICAgb3V0ICs9IGxvdyAqIE1hdGgucG93KDIsIGV4cCAtIDUyKTtcbiAgICAgIHJldHVybiBzaWduICogb3V0O1xuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnBlZWtGbG9hdDY0ID0gZnVuY3Rpb24ob2Zmc2V0LCBsaXR0bGVFbmRpYW4pIHtcbiAgICAgIGlmIChvZmZzZXQgPT0gbnVsbCkge1xuICAgICAgICBvZmZzZXQgPSAwO1xuICAgICAgfVxuICAgICAgdGhpcy5wZWVrKDgsIG9mZnNldCwgbGl0dGxlRW5kaWFuKTtcbiAgICAgIGlmIChmbG9hdDY0KSB7XG4gICAgICAgIHJldHVybiBmbG9hdDY0WzBdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIGZsb2F0NjRGYWxsYmFjaygpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnJlYWRGbG9hdDgwID0gZnVuY3Rpb24obGl0dGxlRW5kaWFuKSB7XG4gICAgICB0aGlzLnJlYWQoMTAsIGxpdHRsZUVuZGlhbik7XG4gICAgICByZXR1cm4gZmxvYXQ4MCgpO1xuICAgIH07XG5cbiAgICBmbG9hdDgwID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgYTAsIGExLCBleHAsIGhpZ2gsIGxvdywgb3V0LCBzaWduO1xuICAgICAgaGlnaCA9IHVpbnQzMlswXSwgbG93ID0gdWludDMyWzFdO1xuICAgICAgYTAgPSB1aW50OFs5XTtcbiAgICAgIGExID0gdWludDhbOF07XG4gICAgICBzaWduID0gMSAtIChhMCA+Pj4gNykgKiAyO1xuICAgICAgZXhwID0gKChhMCAmIDB4N0YpIDw8IDgpIHwgYTE7XG4gICAgICBpZiAoZXhwID09PSAwICYmIGxvdyA9PT0gMCAmJiBoaWdoID09PSAwKSB7XG4gICAgICAgIHJldHVybiAwO1xuICAgICAgfVxuICAgICAgaWYgKGV4cCA9PT0gMHg3ZmZmKSB7XG4gICAgICAgIGlmIChsb3cgPT09IDAgJiYgaGlnaCA9PT0gMCkge1xuICAgICAgICAgIHJldHVybiBzaWduICogSW5maW5pdHk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIE5hTjtcbiAgICAgIH1cbiAgICAgIGV4cCAtPSAxNjM4MztcbiAgICAgIG91dCA9IGxvdyAqIE1hdGgucG93KDIsIGV4cCAtIDMxKTtcbiAgICAgIG91dCArPSBoaWdoICogTWF0aC5wb3coMiwgZXhwIC0gNjMpO1xuICAgICAgcmV0dXJuIHNpZ24gKiBvdXQ7XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucGVla0Zsb2F0ODAgPSBmdW5jdGlvbihvZmZzZXQsIGxpdHRsZUVuZGlhbikge1xuICAgICAgaWYgKG9mZnNldCA9PSBudWxsKSB7XG4gICAgICAgIG9mZnNldCA9IDA7XG4gICAgICB9XG4gICAgICB0aGlzLnBlZWsoMTAsIG9mZnNldCwgbGl0dGxlRW5kaWFuKTtcbiAgICAgIHJldHVybiBmbG9hdDgwKCk7XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucmVhZEJ1ZmZlciA9IGZ1bmN0aW9uKGxlbmd0aCkge1xuICAgICAgdmFyIGksIHJlc3VsdCwgdG8sIF9pO1xuICAgICAgcmVzdWx0ID0gQVZCdWZmZXIuYWxsb2NhdGUobGVuZ3RoKTtcbiAgICAgIHRvID0gcmVzdWx0LmRhdGE7XG4gICAgICBmb3IgKGkgPSBfaSA9IDA7IF9pIDwgbGVuZ3RoOyBpID0gX2kgKz0gMSkge1xuICAgICAgICB0b1tpXSA9IHRoaXMucmVhZFVJbnQ4KCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnBlZWtCdWZmZXIgPSBmdW5jdGlvbihvZmZzZXQsIGxlbmd0aCkge1xuICAgICAgdmFyIGksIHJlc3VsdCwgdG8sIF9pO1xuICAgICAgaWYgKG9mZnNldCA9PSBudWxsKSB7XG4gICAgICAgIG9mZnNldCA9IDA7XG4gICAgICB9XG4gICAgICByZXN1bHQgPSBBVkJ1ZmZlci5hbGxvY2F0ZShsZW5ndGgpO1xuICAgICAgdG8gPSByZXN1bHQuZGF0YTtcbiAgICAgIGZvciAoaSA9IF9pID0gMDsgX2kgPCBsZW5ndGg7IGkgPSBfaSArPSAxKSB7XG4gICAgICAgIHRvW2ldID0gdGhpcy5wZWVrVUludDgob2Zmc2V0ICsgaSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnJlYWRTaW5nbGVCdWZmZXIgPSBmdW5jdGlvbihsZW5ndGgpIHtcbiAgICAgIHZhciByZXN1bHQ7XG4gICAgICByZXN1bHQgPSB0aGlzLmxpc3QuZmlyc3Quc2xpY2UodGhpcy5sb2NhbE9mZnNldCwgbGVuZ3RoKTtcbiAgICAgIHRoaXMuYWR2YW5jZShyZXN1bHQubGVuZ3RoKTtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucGVla1NpbmdsZUJ1ZmZlciA9IGZ1bmN0aW9uKG9mZnNldCwgbGVuZ3RoKSB7XG4gICAgICB2YXIgcmVzdWx0O1xuICAgICAgcmVzdWx0ID0gdGhpcy5saXN0LmZpcnN0LnNsaWNlKHRoaXMubG9jYWxPZmZzZXQgKyBvZmZzZXQsIGxlbmd0aCk7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH07XG5cbiAgICBTdHJlYW0ucHJvdG90eXBlLnJlYWRTdHJpbmcgPSBmdW5jdGlvbihsZW5ndGgsIGVuY29kaW5nKSB7XG4gICAgICBpZiAoZW5jb2RpbmcgPT0gbnVsbCkge1xuICAgICAgICBlbmNvZGluZyA9ICdhc2NpaSc7XG4gICAgICB9XG4gICAgICByZXR1cm4gZGVjb2RlU3RyaW5nLmNhbGwodGhpcywgMCwgbGVuZ3RoLCBlbmNvZGluZywgdHJ1ZSk7XG4gICAgfTtcblxuICAgIFN0cmVhbS5wcm90b3R5cGUucGVla1N0cmluZyA9IGZ1bmN0aW9uKG9mZnNldCwgbGVuZ3RoLCBlbmNvZGluZykge1xuICAgICAgaWYgKG9mZnNldCA9PSBudWxsKSB7XG4gICAgICAgIG9mZnNldCA9IDA7XG4gICAgICB9XG4gICAgICBpZiAoZW5jb2RpbmcgPT0gbnVsbCkge1xuICAgICAgICBlbmNvZGluZyA9ICdhc2NpaSc7XG4gICAgICB9XG4gICAgICByZXR1cm4gZGVjb2RlU3RyaW5nLmNhbGwodGhpcywgb2Zmc2V0LCBsZW5ndGgsIGVuY29kaW5nLCBmYWxzZSk7XG4gICAgfTtcblxuICAgIGRlY29kZVN0cmluZyA9IGZ1bmN0aW9uKG9mZnNldCwgbGVuZ3RoLCBlbmNvZGluZywgYWR2YW5jZSkge1xuICAgICAgdmFyIGIxLCBiMiwgYjMsIGI0LCBib20sIGMsIGVuZCwgbGl0dGxlRW5kaWFuLCBudWxsRW5kLCBwdCwgcmVzdWx0LCB3MSwgdzI7XG4gICAgICBlbmNvZGluZyA9IGVuY29kaW5nLnRvTG93ZXJDYXNlKCk7XG4gICAgICBudWxsRW5kID0gbGVuZ3RoID09PSBudWxsID8gMCA6IC0xO1xuICAgICAgaWYgKGxlbmd0aCA9PSBudWxsKSB7XG4gICAgICAgIGxlbmd0aCA9IEluZmluaXR5O1xuICAgICAgfVxuICAgICAgZW5kID0gb2Zmc2V0ICsgbGVuZ3RoO1xuICAgICAgcmVzdWx0ID0gJyc7XG4gICAgICBzd2l0Y2ggKGVuY29kaW5nKSB7XG4gICAgICAgIGNhc2UgJ2FzY2lpJzpcbiAgICAgICAgY2FzZSAnbGF0aW4xJzpcbiAgICAgICAgICB3aGlsZSAob2Zmc2V0IDwgZW5kICYmIChjID0gdGhpcy5wZWVrVUludDgob2Zmc2V0KyspKSAhPT0gbnVsbEVuZCkge1xuICAgICAgICAgICAgcmVzdWx0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoYyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd1dGY4JzpcbiAgICAgICAgY2FzZSAndXRmLTgnOlxuICAgICAgICAgIHdoaWxlIChvZmZzZXQgPCBlbmQgJiYgKGIxID0gdGhpcy5wZWVrVUludDgob2Zmc2V0KyspKSAhPT0gbnVsbEVuZCkge1xuICAgICAgICAgICAgaWYgKChiMSAmIDB4ODApID09PSAwKSB7XG4gICAgICAgICAgICAgIHJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGIxKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoKGIxICYgMHhlMCkgPT09IDB4YzApIHtcbiAgICAgICAgICAgICAgYjIgPSB0aGlzLnBlZWtVSW50OChvZmZzZXQrKykgJiAweDNmO1xuICAgICAgICAgICAgICByZXN1bHQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZSgoKGIxICYgMHgxZikgPDwgNikgfCBiMik7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKChiMSAmIDB4ZjApID09PSAweGUwKSB7XG4gICAgICAgICAgICAgIGIyID0gdGhpcy5wZWVrVUludDgob2Zmc2V0KyspICYgMHgzZjtcbiAgICAgICAgICAgICAgYjMgPSB0aGlzLnBlZWtVSW50OChvZmZzZXQrKykgJiAweDNmO1xuICAgICAgICAgICAgICByZXN1bHQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZSgoKGIxICYgMHgwZikgPDwgMTIpIHwgKGIyIDw8IDYpIHwgYjMpO1xuICAgICAgICAgICAgfSBlbHNlIGlmICgoYjEgJiAweGY4KSA9PT0gMHhmMCkge1xuICAgICAgICAgICAgICBiMiA9IHRoaXMucGVla1VJbnQ4KG9mZnNldCsrKSAmIDB4M2Y7XG4gICAgICAgICAgICAgIGIzID0gdGhpcy5wZWVrVUludDgob2Zmc2V0KyspICYgMHgzZjtcbiAgICAgICAgICAgICAgYjQgPSB0aGlzLnBlZWtVSW50OChvZmZzZXQrKykgJiAweDNmO1xuICAgICAgICAgICAgICBwdCA9ICgoKGIxICYgMHgwZikgPDwgMTgpIHwgKGIyIDw8IDEyKSB8IChiMyA8PCA2KSB8IGI0KSAtIDB4MTAwMDA7XG4gICAgICAgICAgICAgIHJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKDB4ZDgwMCArIChwdCA+PiAxMCksIDB4ZGMwMCArIChwdCAmIDB4M2ZmKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICd1dGYxNi1iZSc6XG4gICAgICAgIGNhc2UgJ3V0ZjE2YmUnOlxuICAgICAgICBjYXNlICd1dGYxNmxlJzpcbiAgICAgICAgY2FzZSAndXRmMTYtbGUnOlxuICAgICAgICBjYXNlICd1dGYxNmJvbSc6XG4gICAgICAgIGNhc2UgJ3V0ZjE2LWJvbSc6XG4gICAgICAgICAgc3dpdGNoIChlbmNvZGluZykge1xuICAgICAgICAgICAgY2FzZSAndXRmMTZiZSc6XG4gICAgICAgICAgICBjYXNlICd1dGYxNi1iZSc6XG4gICAgICAgICAgICAgIGxpdHRsZUVuZGlhbiA9IGZhbHNlO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ3V0ZjE2bGUnOlxuICAgICAgICAgICAgY2FzZSAndXRmMTYtbGUnOlxuICAgICAgICAgICAgICBsaXR0bGVFbmRpYW4gPSB0cnVlO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ3V0ZjE2Ym9tJzpcbiAgICAgICAgICAgIGNhc2UgJ3V0ZjE2LWJvbSc6XG4gICAgICAgICAgICAgIGlmIChsZW5ndGggPCAyIHx8IChib20gPSB0aGlzLnBlZWtVSW50MTYob2Zmc2V0KSkgPT09IG51bGxFbmQpIHtcbiAgICAgICAgICAgICAgICBpZiAoYWR2YW5jZSkge1xuICAgICAgICAgICAgICAgICAgdGhpcy5hZHZhbmNlKG9mZnNldCArPSAyKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBsaXR0bGVFbmRpYW4gPSBib20gPT09IDB4ZmZmZTtcbiAgICAgICAgICAgICAgb2Zmc2V0ICs9IDI7XG4gICAgICAgICAgfVxuICAgICAgICAgIHdoaWxlIChvZmZzZXQgPCBlbmQgJiYgKHcxID0gdGhpcy5wZWVrVUludDE2KG9mZnNldCwgbGl0dGxlRW5kaWFuKSkgIT09IG51bGxFbmQpIHtcbiAgICAgICAgICAgIG9mZnNldCArPSAyO1xuICAgICAgICAgICAgaWYgKHcxIDwgMHhkODAwIHx8IHcxID4gMHhkZmZmKSB7XG4gICAgICAgICAgICAgIHJlc3VsdCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKHcxKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGlmICh3MSA+IDB4ZGJmZikge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIkludmFsaWQgdXRmMTYgc2VxdWVuY2UuXCIpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHcyID0gdGhpcy5wZWVrVUludDE2KG9mZnNldCwgbGl0dGxlRW5kaWFuKTtcbiAgICAgICAgICAgICAgaWYgKHcyIDwgMHhkYzAwIHx8IHcyID4gMHhkZmZmKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiSW52YWxpZCB1dGYxNiBzZXF1ZW5jZS5cIik7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgcmVzdWx0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUodzEsIHcyKTtcbiAgICAgICAgICAgICAgb2Zmc2V0ICs9IDI7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh3MSA9PT0gbnVsbEVuZCkge1xuICAgICAgICAgICAgb2Zmc2V0ICs9IDI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIlVua25vd24gZW5jb2Rpbmc6IFwiICsgZW5jb2RpbmcpO1xuICAgICAgfVxuICAgICAgaWYgKGFkdmFuY2UpIHtcbiAgICAgICAgdGhpcy5hZHZhbmNlKG9mZnNldCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH07XG5cbiAgICByZXR1cm4gU3RyZWFtO1xuXG4gIH0pKCk7XG5cbiAgbW9kdWxlLmV4cG9ydHMgPSBTdHJlYW07XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBVbmRlcmZsb3dFcnJvcixcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBVbmRlcmZsb3dFcnJvciA9IChmdW5jdGlvbihfc3VwZXIpIHtcbiAgICBfX2V4dGVuZHMoVW5kZXJmbG93RXJyb3IsIF9zdXBlcik7XG5cbiAgICBmdW5jdGlvbiBVbmRlcmZsb3dFcnJvcigpIHtcbiAgICAgIFVuZGVyZmxvd0Vycm9yLl9fc3VwZXJfXy5jb25zdHJ1Y3Rvci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgdGhpcy5uYW1lID0gJ1VuZGVyZmxvd0Vycm9yJztcbiAgICAgIHRoaXMuc3RhY2sgPSBuZXcgRXJyb3IoKS5zdGFjaztcbiAgICB9XG5cbiAgICByZXR1cm4gVW5kZXJmbG93RXJyb3I7XG5cbiAgfSkoRXJyb3IpO1xuXG4gIG1vZHVsZS5leHBvcnRzID0gVW5kZXJmbG93RXJyb3I7XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBCaXRzdHJlYW0sIEJ1ZmZlckxpc3QsIERlY29kZXIsIEV2ZW50RW1pdHRlciwgU3RyZWFtLCBVbmRlcmZsb3dFcnJvcixcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKCcuL2NvcmUvZXZlbnRzJyk7XG5cbiAgQnVmZmVyTGlzdCA9IHJlcXVpcmUoJy4vY29yZS9idWZmZXJsaXN0Jyk7XG5cbiAgU3RyZWFtID0gcmVxdWlyZSgnLi9jb3JlL3N0cmVhbScpO1xuXG4gIEJpdHN0cmVhbSA9IHJlcXVpcmUoJy4vY29yZS9iaXRzdHJlYW0nKTtcblxuICBVbmRlcmZsb3dFcnJvciA9IHJlcXVpcmUoJy4vY29yZS91bmRlcmZsb3cnKTtcblxuICBEZWNvZGVyID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICAgIHZhciBjb2RlY3M7XG5cbiAgICBfX2V4dGVuZHMoRGVjb2RlciwgX3N1cGVyKTtcblxuICAgIGZ1bmN0aW9uIERlY29kZXIoZGVtdXhlciwgZm9ybWF0KSB7XG4gICAgICB2YXIgbGlzdDtcbiAgICAgIHRoaXMuZGVtdXhlciA9IGRlbXV4ZXI7XG4gICAgICB0aGlzLmZvcm1hdCA9IGZvcm1hdDtcbiAgICAgIGxpc3QgPSBuZXcgQnVmZmVyTGlzdDtcbiAgICAgIHRoaXMuc3RyZWFtID0gbmV3IFN0cmVhbShsaXN0KTtcbiAgICAgIHRoaXMuYml0c3RyZWFtID0gbmV3IEJpdHN0cmVhbSh0aGlzLnN0cmVhbSk7XG4gICAgICB0aGlzLnJlY2VpdmVkRmluYWxCdWZmZXIgPSBmYWxzZTtcbiAgICAgIHRoaXMud2FpdGluZyA9IGZhbHNlO1xuICAgICAgdGhpcy5kZW11eGVyLm9uKCdjb29raWUnLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKGNvb2tpZSkge1xuICAgICAgICAgIHZhciBlcnJvcjtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgcmV0dXJuIF90aGlzLnNldENvb2tpZShjb29raWUpO1xuICAgICAgICAgIH0gY2F0Y2ggKF9lcnJvcikge1xuICAgICAgICAgICAgZXJyb3IgPSBfZXJyb3I7XG4gICAgICAgICAgICByZXR1cm4gX3RoaXMuZW1pdCgnZXJyb3InLCBlcnJvcik7XG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgICAgdGhpcy5kZW11eGVyLm9uKCdkYXRhJywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbihjaHVuaykge1xuICAgICAgICAgIGxpc3QuYXBwZW5kKGNodW5rKTtcbiAgICAgICAgICBpZiAoX3RoaXMud2FpdGluZykge1xuICAgICAgICAgICAgcmV0dXJuIF90aGlzLmRlY29kZSgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgIH0pKHRoaXMpKTtcbiAgICAgIHRoaXMuZGVtdXhlci5vbignZW5kJywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICAgICAgICBfdGhpcy5yZWNlaXZlZEZpbmFsQnVmZmVyID0gdHJ1ZTtcbiAgICAgICAgICBpZiAoX3RoaXMud2FpdGluZykge1xuICAgICAgICAgICAgcmV0dXJuIF90aGlzLmRlY29kZSgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgIH0pKHRoaXMpKTtcbiAgICAgIHRoaXMuaW5pdCgpO1xuICAgIH1cblxuICAgIERlY29kZXIucHJvdG90eXBlLmluaXQgPSBmdW5jdGlvbigpIHt9O1xuXG4gICAgRGVjb2Rlci5wcm90b3R5cGUuc2V0Q29va2llID0gZnVuY3Rpb24oY29va2llKSB7fTtcblxuICAgIERlY29kZXIucHJvdG90eXBlLnJlYWRDaHVuayA9IGZ1bmN0aW9uKCkge307XG5cbiAgICBEZWNvZGVyLnByb3RvdHlwZS5kZWNvZGUgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBlcnJvciwgb2Zmc2V0LCBwYWNrZXQ7XG4gICAgICB0aGlzLndhaXRpbmcgPSAhdGhpcy5yZWNlaXZlZEZpbmFsQnVmZmVyO1xuICAgICAgb2Zmc2V0ID0gdGhpcy5iaXRzdHJlYW0ub2Zmc2V0KCk7XG4gICAgICB0cnkge1xuICAgICAgICBwYWNrZXQgPSB0aGlzLnJlYWRDaHVuaygpO1xuICAgICAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgICAgIGVycm9yID0gX2Vycm9yO1xuICAgICAgICBpZiAoIShlcnJvciBpbnN0YW5jZW9mIFVuZGVyZmxvd0Vycm9yKSkge1xuICAgICAgICAgIHRoaXMuZW1pdCgnZXJyb3InLCBlcnJvcik7XG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAocGFja2V0KSB7XG4gICAgICAgIHRoaXMuZW1pdCgnZGF0YScsIHBhY2tldCk7XG4gICAgICAgIGlmICh0aGlzLnJlY2VpdmVkRmluYWxCdWZmZXIpIHtcbiAgICAgICAgICB0aGlzLmVtaXQoJ2VuZCcpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfSBlbHNlIGlmICghdGhpcy5yZWNlaXZlZEZpbmFsQnVmZmVyKSB7XG4gICAgICAgIHRoaXMuYml0c3RyZWFtLnNlZWsob2Zmc2V0KTtcbiAgICAgICAgdGhpcy53YWl0aW5nID0gdHJ1ZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuZW1pdCgnZW5kJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfTtcblxuICAgIERlY29kZXIucHJvdG90eXBlLnNlZWsgPSBmdW5jdGlvbih0aW1lc3RhbXApIHtcbiAgICAgIHZhciBzZWVrUG9pbnQ7XG4gICAgICBzZWVrUG9pbnQgPSB0aGlzLmRlbXV4ZXIuc2Vlayh0aW1lc3RhbXApO1xuICAgICAgdGhpcy5zdHJlYW0uc2VlayhzZWVrUG9pbnQub2Zmc2V0KTtcbiAgICAgIHJldHVybiBzZWVrUG9pbnQudGltZXN0YW1wO1xuICAgIH07XG5cbiAgICBjb2RlY3MgPSB7fTtcblxuICAgIERlY29kZXIucmVnaXN0ZXIgPSBmdW5jdGlvbihpZCwgZGVjb2Rlcikge1xuICAgICAgcmV0dXJuIGNvZGVjc1tpZF0gPSBkZWNvZGVyO1xuICAgIH07XG5cbiAgICBEZWNvZGVyLmZpbmQgPSBmdW5jdGlvbihpZCkge1xuICAgICAgcmV0dXJuIGNvZGVjc1tpZF0gfHwgbnVsbDtcbiAgICB9O1xuXG4gICAgcmV0dXJuIERlY29kZXI7XG5cbiAgfSkoRXZlbnRFbWl0dGVyKTtcblxuICBtb2R1bGUuZXhwb3J0cyA9IERlY29kZXI7XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBEZWNvZGVyLCBMUENNRGVjb2RlcixcbiAgICBfX2JpbmQgPSBmdW5jdGlvbihmbiwgbWUpeyByZXR1cm4gZnVuY3Rpb24oKXsgcmV0dXJuIGZuLmFwcGx5KG1lLCBhcmd1bWVudHMpOyB9OyB9LFxuICAgIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICAgIF9fZXh0ZW5kcyA9IGZ1bmN0aW9uKGNoaWxkLCBwYXJlbnQpIHsgZm9yICh2YXIga2V5IGluIHBhcmVudCkgeyBpZiAoX19oYXNQcm9wLmNhbGwocGFyZW50LCBrZXkpKSBjaGlsZFtrZXldID0gcGFyZW50W2tleV07IH0gZnVuY3Rpb24gY3RvcigpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGNoaWxkOyB9IGN0b3IucHJvdG90eXBlID0gcGFyZW50LnByb3RvdHlwZTsgY2hpbGQucHJvdG90eXBlID0gbmV3IGN0b3IoKTsgY2hpbGQuX19zdXBlcl9fID0gcGFyZW50LnByb3RvdHlwZTsgcmV0dXJuIGNoaWxkOyB9O1xuXG4gIERlY29kZXIgPSByZXF1aXJlKCcuLi9kZWNvZGVyJyk7XG5cbiAgTFBDTURlY29kZXIgPSAoZnVuY3Rpb24oX3N1cGVyKSB7XG4gICAgX19leHRlbmRzKExQQ01EZWNvZGVyLCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gTFBDTURlY29kZXIoKSB7XG4gICAgICB0aGlzLnJlYWRDaHVuayA9IF9fYmluZCh0aGlzLnJlYWRDaHVuaywgdGhpcyk7XG4gICAgICByZXR1cm4gTFBDTURlY29kZXIuX19zdXBlcl9fLmNvbnN0cnVjdG9yLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfVxuXG4gICAgRGVjb2Rlci5yZWdpc3RlcignbHBjbScsIExQQ01EZWNvZGVyKTtcblxuICAgIExQQ01EZWNvZGVyLnByb3RvdHlwZS5yZWFkQ2h1bmsgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBjaHVua1NpemUsIGksIGxpdHRsZUVuZGlhbiwgb3V0cHV0LCBzYW1wbGVzLCBzdHJlYW0sIF9pLCBfaiwgX2ssIF9sLCBfbSwgX247XG4gICAgICBzdHJlYW0gPSB0aGlzLnN0cmVhbTtcbiAgICAgIGxpdHRsZUVuZGlhbiA9IHRoaXMuZm9ybWF0LmxpdHRsZUVuZGlhbjtcbiAgICAgIGNodW5rU2l6ZSA9IE1hdGgubWluKDQwOTYsIHN0cmVhbS5yZW1haW5pbmdCeXRlcygpKTtcbiAgICAgIHNhbXBsZXMgPSBjaHVua1NpemUgLyAodGhpcy5mb3JtYXQuYml0c1BlckNoYW5uZWwgLyA4KSB8IDA7XG4gICAgICBpZiAoY2h1bmtTaXplIDwgdGhpcy5mb3JtYXQuYml0c1BlckNoYW5uZWwgLyA4KSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuZm9ybWF0LmZsb2F0aW5nUG9pbnQpIHtcbiAgICAgICAgc3dpdGNoICh0aGlzLmZvcm1hdC5iaXRzUGVyQ2hhbm5lbCkge1xuICAgICAgICAgIGNhc2UgMzI6XG4gICAgICAgICAgICBvdXRwdXQgPSBuZXcgRmxvYXQzMkFycmF5KHNhbXBsZXMpO1xuICAgICAgICAgICAgZm9yIChpID0gX2kgPSAwOyBfaSA8IHNhbXBsZXM7IGkgPSBfaSArPSAxKSB7XG4gICAgICAgICAgICAgIG91dHB1dFtpXSA9IHN0cmVhbS5yZWFkRmxvYXQzMihsaXR0bGVFbmRpYW4pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSA2NDpcbiAgICAgICAgICAgIG91dHB1dCA9IG5ldyBGbG9hdDY0QXJyYXkoc2FtcGxlcyk7XG4gICAgICAgICAgICBmb3IgKGkgPSBfaiA9IDA7IF9qIDwgc2FtcGxlczsgaSA9IF9qICs9IDEpIHtcbiAgICAgICAgICAgICAgb3V0cHV0W2ldID0gc3RyZWFtLnJlYWRGbG9hdDY0KGxpdHRsZUVuZGlhbik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbnN1cHBvcnRlZCBiaXQgZGVwdGguJyk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHN3aXRjaCAodGhpcy5mb3JtYXQuYml0c1BlckNoYW5uZWwpIHtcbiAgICAgICAgICBjYXNlIDg6XG4gICAgICAgICAgICBvdXRwdXQgPSBuZXcgSW50OEFycmF5KHNhbXBsZXMpO1xuICAgICAgICAgICAgZm9yIChpID0gX2sgPSAwOyBfayA8IHNhbXBsZXM7IGkgPSBfayArPSAxKSB7XG4gICAgICAgICAgICAgIG91dHB1dFtpXSA9IHN0cmVhbS5yZWFkSW50OCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAxNjpcbiAgICAgICAgICAgIG91dHB1dCA9IG5ldyBJbnQxNkFycmF5KHNhbXBsZXMpO1xuICAgICAgICAgICAgZm9yIChpID0gX2wgPSAwOyBfbCA8IHNhbXBsZXM7IGkgPSBfbCArPSAxKSB7XG4gICAgICAgICAgICAgIG91dHB1dFtpXSA9IHN0cmVhbS5yZWFkSW50MTYobGl0dGxlRW5kaWFuKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgMjQ6XG4gICAgICAgICAgICBvdXRwdXQgPSBuZXcgSW50MzJBcnJheShzYW1wbGVzKTtcbiAgICAgICAgICAgIGZvciAoaSA9IF9tID0gMDsgX20gPCBzYW1wbGVzOyBpID0gX20gKz0gMSkge1xuICAgICAgICAgICAgICBvdXRwdXRbaV0gPSBzdHJlYW0ucmVhZEludDI0KGxpdHRsZUVuZGlhbik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIDMyOlxuICAgICAgICAgICAgb3V0cHV0ID0gbmV3IEludDMyQXJyYXkoc2FtcGxlcyk7XG4gICAgICAgICAgICBmb3IgKGkgPSBfbiA9IDA7IF9uIDwgc2FtcGxlczsgaSA9IF9uICs9IDEpIHtcbiAgICAgICAgICAgICAgb3V0cHV0W2ldID0gc3RyZWFtLnJlYWRJbnQzMihsaXR0bGVFbmRpYW4pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignVW5zdXBwb3J0ZWQgYml0IGRlcHRoLicpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gb3V0cHV0O1xuICAgIH07XG5cbiAgICByZXR1cm4gTFBDTURlY29kZXI7XG5cbiAgfSkoRGVjb2Rlcik7XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBEZWNvZGVyLCBYTEFXRGVjb2RlcixcbiAgICBfX2JpbmQgPSBmdW5jdGlvbihmbiwgbWUpeyByZXR1cm4gZnVuY3Rpb24oKXsgcmV0dXJuIGZuLmFwcGx5KG1lLCBhcmd1bWVudHMpOyB9OyB9LFxuICAgIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICAgIF9fZXh0ZW5kcyA9IGZ1bmN0aW9uKGNoaWxkLCBwYXJlbnQpIHsgZm9yICh2YXIga2V5IGluIHBhcmVudCkgeyBpZiAoX19oYXNQcm9wLmNhbGwocGFyZW50LCBrZXkpKSBjaGlsZFtrZXldID0gcGFyZW50W2tleV07IH0gZnVuY3Rpb24gY3RvcigpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGNoaWxkOyB9IGN0b3IucHJvdG90eXBlID0gcGFyZW50LnByb3RvdHlwZTsgY2hpbGQucHJvdG90eXBlID0gbmV3IGN0b3IoKTsgY2hpbGQuX19zdXBlcl9fID0gcGFyZW50LnByb3RvdHlwZTsgcmV0dXJuIGNoaWxkOyB9O1xuXG4gIERlY29kZXIgPSByZXF1aXJlKCcuLi9kZWNvZGVyJyk7XG5cbiAgWExBV0RlY29kZXIgPSAoZnVuY3Rpb24oX3N1cGVyKSB7XG4gICAgdmFyIEJJQVMsIFFVQU5UX01BU0ssIFNFR19NQVNLLCBTRUdfU0hJRlQsIFNJR05fQklUO1xuXG4gICAgX19leHRlbmRzKFhMQVdEZWNvZGVyLCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gWExBV0RlY29kZXIoKSB7XG4gICAgICB0aGlzLnJlYWRDaHVuayA9IF9fYmluZCh0aGlzLnJlYWRDaHVuaywgdGhpcyk7XG4gICAgICByZXR1cm4gWExBV0RlY29kZXIuX19zdXBlcl9fLmNvbnN0cnVjdG9yLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfVxuXG4gICAgRGVjb2Rlci5yZWdpc3RlcigndWxhdycsIFhMQVdEZWNvZGVyKTtcblxuICAgIERlY29kZXIucmVnaXN0ZXIoJ2FsYXcnLCBYTEFXRGVjb2Rlcik7XG5cbiAgICBTSUdOX0JJVCA9IDB4ODA7XG5cbiAgICBRVUFOVF9NQVNLID0gMHhmO1xuXG4gICAgU0VHX1NISUZUID0gNDtcblxuICAgIFNFR19NQVNLID0gMHg3MDtcblxuICAgIEJJQVMgPSAweDg0O1xuXG4gICAgWExBV0RlY29kZXIucHJvdG90eXBlLmluaXQgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBpLCBzZWcsIHQsIHRhYmxlLCB2YWwsIF9pLCBfajtcbiAgICAgIHRoaXMuZm9ybWF0LmJpdHNQZXJDaGFubmVsID0gMTY7XG4gICAgICB0aGlzLnRhYmxlID0gdGFibGUgPSBuZXcgSW50MTZBcnJheSgyNTYpO1xuICAgICAgaWYgKHRoaXMuZm9ybWF0LmZvcm1hdElEID09PSAndWxhdycpIHtcbiAgICAgICAgZm9yIChpID0gX2kgPSAwOyBfaSA8IDI1NjsgaSA9ICsrX2kpIHtcbiAgICAgICAgICB2YWwgPSB+aTtcbiAgICAgICAgICB0ID0gKCh2YWwgJiBRVUFOVF9NQVNLKSA8PCAzKSArIEJJQVM7XG4gICAgICAgICAgdCA8PD0gKHZhbCAmIFNFR19NQVNLKSA+Pj4gU0VHX1NISUZUO1xuICAgICAgICAgIHRhYmxlW2ldID0gdmFsICYgU0lHTl9CSVQgPyBCSUFTIC0gdCA6IHQgLSBCSUFTO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmb3IgKGkgPSBfaiA9IDA7IF9qIDwgMjU2OyBpID0gKytfaikge1xuICAgICAgICAgIHZhbCA9IGkgXiAweDU1O1xuICAgICAgICAgIHQgPSB2YWwgJiBRVUFOVF9NQVNLO1xuICAgICAgICAgIHNlZyA9ICh2YWwgJiBTRUdfTUFTSykgPj4+IFNFR19TSElGVDtcbiAgICAgICAgICBpZiAoc2VnKSB7XG4gICAgICAgICAgICB0ID0gKHQgKyB0ICsgMSArIDMyKSA8PCAoc2VnICsgMik7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHQgPSAodCArIHQgKyAxKSA8PCAzO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0YWJsZVtpXSA9IHZhbCAmIFNJR05fQklUID8gdCA6IC10O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfTtcblxuICAgIFhMQVdEZWNvZGVyLnByb3RvdHlwZS5yZWFkQ2h1bmsgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBpLCBvdXRwdXQsIHNhbXBsZXMsIHN0cmVhbSwgdGFibGUsIF9pO1xuICAgICAgc3RyZWFtID0gdGhpcy5zdHJlYW0sIHRhYmxlID0gdGhpcy50YWJsZTtcbiAgICAgIHNhbXBsZXMgPSBNYXRoLm1pbig0MDk2LCB0aGlzLnN0cmVhbS5yZW1haW5pbmdCeXRlcygpKTtcbiAgICAgIGlmIChzYW1wbGVzID09PSAwKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIG91dHB1dCA9IG5ldyBJbnQxNkFycmF5KHNhbXBsZXMpO1xuICAgICAgZm9yIChpID0gX2kgPSAwOyBfaSA8IHNhbXBsZXM7IGkgPSBfaSArPSAxKSB7XG4gICAgICAgIG91dHB1dFtpXSA9IHRhYmxlW3N0cmVhbS5yZWFkVUludDgoKV07XG4gICAgICB9XG4gICAgICByZXR1cm4gb3V0cHV0O1xuICAgIH07XG5cbiAgICByZXR1cm4gWExBV0RlY29kZXI7XG5cbiAgfSkoRGVjb2Rlcik7XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBCdWZmZXJMaXN0LCBEZW11eGVyLCBFdmVudEVtaXR0ZXIsIFN0cmVhbSxcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKCcuL2NvcmUvZXZlbnRzJyk7XG5cbiAgQnVmZmVyTGlzdCA9IHJlcXVpcmUoJy4vY29yZS9idWZmZXJsaXN0Jyk7XG5cbiAgU3RyZWFtID0gcmVxdWlyZSgnLi9jb3JlL3N0cmVhbScpO1xuXG4gIERlbXV4ZXIgPSAoZnVuY3Rpb24oX3N1cGVyKSB7XG4gICAgdmFyIGZvcm1hdHM7XG5cbiAgICBfX2V4dGVuZHMoRGVtdXhlciwgX3N1cGVyKTtcblxuICAgIERlbXV4ZXIucHJvYmUgPSBmdW5jdGlvbihidWZmZXIpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9O1xuXG4gICAgZnVuY3Rpb24gRGVtdXhlcihzb3VyY2UsIGNodW5rKSB7XG4gICAgICB2YXIgbGlzdCwgcmVjZWl2ZWQ7XG4gICAgICBsaXN0ID0gbmV3IEJ1ZmZlckxpc3Q7XG4gICAgICBsaXN0LmFwcGVuZChjaHVuayk7XG4gICAgICB0aGlzLnN0cmVhbSA9IG5ldyBTdHJlYW0obGlzdCk7XG4gICAgICByZWNlaXZlZCA9IGZhbHNlO1xuICAgICAgc291cmNlLm9uKCdkYXRhJywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbihjaHVuaykge1xuICAgICAgICAgIHZhciBlO1xuICAgICAgICAgIHJlY2VpdmVkID0gdHJ1ZTtcbiAgICAgICAgICBsaXN0LmFwcGVuZChjaHVuayk7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJldHVybiBfdGhpcy5yZWFkQ2h1bmsoY2h1bmspO1xuICAgICAgICAgIH0gY2F0Y2ggKF9lcnJvcikge1xuICAgICAgICAgICAgZSA9IF9lcnJvcjtcbiAgICAgICAgICAgIHJldHVybiBfdGhpcy5lbWl0KCdlcnJvcicsIGUpO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgIH0pKHRoaXMpKTtcbiAgICAgIHNvdXJjZS5vbignZXJyb3InLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKGVycikge1xuICAgICAgICAgIHJldHVybiBfdGhpcy5lbWl0KCdlcnJvcicsIGVycik7XG4gICAgICAgIH07XG4gICAgICB9KSh0aGlzKSk7XG4gICAgICBzb3VyY2Uub24oJ2VuZCcsIChmdW5jdGlvbihfdGhpcykge1xuICAgICAgICByZXR1cm4gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgaWYgKCFyZWNlaXZlZCkge1xuICAgICAgICAgICAgX3RoaXMucmVhZENodW5rKGNodW5rKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIF90aGlzLmVtaXQoJ2VuZCcpO1xuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgICAgdGhpcy5zZWVrUG9pbnRzID0gW107XG4gICAgICB0aGlzLmluaXQoKTtcbiAgICB9XG5cbiAgICBEZW11eGVyLnByb3RvdHlwZS5pbml0ID0gZnVuY3Rpb24oKSB7fTtcblxuICAgIERlbXV4ZXIucHJvdG90eXBlLnJlYWRDaHVuayA9IGZ1bmN0aW9uKGNodW5rKSB7fTtcblxuICAgIERlbXV4ZXIucHJvdG90eXBlLmFkZFNlZWtQb2ludCA9IGZ1bmN0aW9uKG9mZnNldCwgdGltZXN0YW1wKSB7XG4gICAgICB2YXIgaW5kZXg7XG4gICAgICBpbmRleCA9IHRoaXMuc2VhcmNoVGltZXN0YW1wKHRpbWVzdGFtcCk7XG4gICAgICByZXR1cm4gdGhpcy5zZWVrUG9pbnRzLnNwbGljZShpbmRleCwgMCwge1xuICAgICAgICBvZmZzZXQ6IG9mZnNldCxcbiAgICAgICAgdGltZXN0YW1wOiB0aW1lc3RhbXBcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICBEZW11eGVyLnByb3RvdHlwZS5zZWFyY2hUaW1lc3RhbXAgPSBmdW5jdGlvbih0aW1lc3RhbXAsIGJhY2t3YXJkKSB7XG4gICAgICB2YXIgaGlnaCwgbG93LCBtaWQsIHRpbWU7XG4gICAgICBsb3cgPSAwO1xuICAgICAgaGlnaCA9IHRoaXMuc2Vla1BvaW50cy5sZW5ndGg7XG4gICAgICBpZiAoaGlnaCA+IDAgJiYgdGhpcy5zZWVrUG9pbnRzW2hpZ2ggLSAxXS50aW1lc3RhbXAgPCB0aW1lc3RhbXApIHtcbiAgICAgICAgcmV0dXJuIGhpZ2g7XG4gICAgICB9XG4gICAgICB3aGlsZSAobG93IDwgaGlnaCkge1xuICAgICAgICBtaWQgPSAobG93ICsgaGlnaCkgPj4gMTtcbiAgICAgICAgdGltZSA9IHRoaXMuc2Vla1BvaW50c1ttaWRdLnRpbWVzdGFtcDtcbiAgICAgICAgaWYgKHRpbWUgPCB0aW1lc3RhbXApIHtcbiAgICAgICAgICBsb3cgPSBtaWQgKyAxO1xuICAgICAgICB9IGVsc2UgaWYgKHRpbWUgPj0gdGltZXN0YW1wKSB7XG4gICAgICAgICAgaGlnaCA9IG1pZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKGhpZ2ggPiB0aGlzLnNlZWtQb2ludHMubGVuZ3RoKSB7XG4gICAgICAgIGhpZ2ggPSB0aGlzLnNlZWtQb2ludHMubGVuZ3RoO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGhpZ2g7XG4gICAgfTtcblxuICAgIERlbXV4ZXIucHJvdG90eXBlLnNlZWsgPSBmdW5jdGlvbih0aW1lc3RhbXApIHtcbiAgICAgIHZhciBpbmRleCwgc2Vla1BvaW50O1xuICAgICAgaWYgKHRoaXMuZm9ybWF0ICYmIHRoaXMuZm9ybWF0LmZyYW1lc1BlclBhY2tldCA+IDAgJiYgdGhpcy5mb3JtYXQuYnl0ZXNQZXJQYWNrZXQgPiAwKSB7XG4gICAgICAgIHNlZWtQb2ludCA9IHtcbiAgICAgICAgICB0aW1lc3RhbXA6IHRpbWVzdGFtcCxcbiAgICAgICAgICBvZmZzZXQ6IHRoaXMuZm9ybWF0LmJ5dGVzUGVyUGFja2V0ICogdGltZXN0YW1wIC8gdGhpcy5mb3JtYXQuZnJhbWVzUGVyUGFja2V0XG4gICAgICAgIH07XG4gICAgICAgIHJldHVybiBzZWVrUG9pbnQ7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpbmRleCA9IHRoaXMuc2VhcmNoVGltZXN0YW1wKHRpbWVzdGFtcCk7XG4gICAgICAgIHJldHVybiB0aGlzLnNlZWtQb2ludHNbaW5kZXhdO1xuICAgICAgfVxuICAgIH07XG5cbiAgICBmb3JtYXRzID0gW107XG5cbiAgICBEZW11eGVyLnJlZ2lzdGVyID0gZnVuY3Rpb24oZGVtdXhlcikge1xuICAgICAgcmV0dXJuIGZvcm1hdHMucHVzaChkZW11eGVyKTtcbiAgICB9O1xuXG4gICAgRGVtdXhlci5maW5kID0gZnVuY3Rpb24oYnVmZmVyKSB7XG4gICAgICB2YXIgZSwgZm9ybWF0LCBvZmZzZXQsIHN0cmVhbSwgX2ksIF9sZW47XG4gICAgICBzdHJlYW0gPSBTdHJlYW0uZnJvbUJ1ZmZlcihidWZmZXIpO1xuICAgICAgZm9yIChfaSA9IDAsIF9sZW4gPSBmb3JtYXRzLmxlbmd0aDsgX2kgPCBfbGVuOyBfaSsrKSB7XG4gICAgICAgIGZvcm1hdCA9IGZvcm1hdHNbX2ldO1xuICAgICAgICBvZmZzZXQgPSBzdHJlYW0ub2Zmc2V0O1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlmIChmb3JtYXQucHJvYmUoc3RyZWFtKSkge1xuICAgICAgICAgICAgcmV0dXJuIGZvcm1hdDtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKF9lcnJvcikge1xuICAgICAgICAgIGUgPSBfZXJyb3I7XG4gICAgICAgIH1cbiAgICAgICAgc3RyZWFtLnNlZWsob2Zmc2V0KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH07XG5cbiAgICByZXR1cm4gRGVtdXhlcjtcblxuICB9KShFdmVudEVtaXR0ZXIpO1xuXG4gIG1vZHVsZS5leHBvcnRzID0gRGVtdXhlcjtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIEFJRkZEZW11eGVyLCBEZW11eGVyLFxuICAgIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICAgIF9fZXh0ZW5kcyA9IGZ1bmN0aW9uKGNoaWxkLCBwYXJlbnQpIHsgZm9yICh2YXIga2V5IGluIHBhcmVudCkgeyBpZiAoX19oYXNQcm9wLmNhbGwocGFyZW50LCBrZXkpKSBjaGlsZFtrZXldID0gcGFyZW50W2tleV07IH0gZnVuY3Rpb24gY3RvcigpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGNoaWxkOyB9IGN0b3IucHJvdG90eXBlID0gcGFyZW50LnByb3RvdHlwZTsgY2hpbGQucHJvdG90eXBlID0gbmV3IGN0b3IoKTsgY2hpbGQuX19zdXBlcl9fID0gcGFyZW50LnByb3RvdHlwZTsgcmV0dXJuIGNoaWxkOyB9O1xuXG4gIERlbXV4ZXIgPSByZXF1aXJlKCcuLi9kZW11eGVyJyk7XG5cbiAgQUlGRkRlbXV4ZXIgPSAoZnVuY3Rpb24oX3N1cGVyKSB7XG4gICAgX19leHRlbmRzKEFJRkZEZW11eGVyLCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gQUlGRkRlbXV4ZXIoKSB7XG4gICAgICByZXR1cm4gQUlGRkRlbXV4ZXIuX19zdXBlcl9fLmNvbnN0cnVjdG9yLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfVxuXG4gICAgRGVtdXhlci5yZWdpc3RlcihBSUZGRGVtdXhlcik7XG5cbiAgICBBSUZGRGVtdXhlci5wcm9iZSA9IGZ1bmN0aW9uKGJ1ZmZlcikge1xuICAgICAgdmFyIF9yZWY7XG4gICAgICByZXR1cm4gYnVmZmVyLnBlZWtTdHJpbmcoMCwgNCkgPT09ICdGT1JNJyAmJiAoKF9yZWYgPSBidWZmZXIucGVla1N0cmluZyg4LCA0KSkgPT09ICdBSUZGJyB8fCBfcmVmID09PSAnQUlGQycpO1xuICAgIH07XG5cbiAgICBBSUZGRGVtdXhlci5wcm90b3R5cGUucmVhZENodW5rID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgYnVmZmVyLCBmb3JtYXQsIG9mZnNldCwgX3JlZjtcbiAgICAgIGlmICghdGhpcy5yZWFkU3RhcnQgJiYgdGhpcy5zdHJlYW0uYXZhaWxhYmxlKDEyKSkge1xuICAgICAgICBpZiAodGhpcy5zdHJlYW0ucmVhZFN0cmluZyg0KSAhPT0gJ0ZPUk0nKSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuZW1pdCgnZXJyb3InLCAnSW52YWxpZCBBSUZGLicpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZmlsZVNpemUgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICAgIHRoaXMuZmlsZVR5cGUgPSB0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKDQpO1xuICAgICAgICB0aGlzLnJlYWRTdGFydCA9IHRydWU7XG4gICAgICAgIGlmICgoX3JlZiA9IHRoaXMuZmlsZVR5cGUpICE9PSAnQUlGRicgJiYgX3JlZiAhPT0gJ0FJRkMnKSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuZW1pdCgnZXJyb3InLCAnSW52YWxpZCBBSUZGLicpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB3aGlsZSAodGhpcy5zdHJlYW0uYXZhaWxhYmxlKDEpKSB7XG4gICAgICAgIGlmICghdGhpcy5yZWFkSGVhZGVycyAmJiB0aGlzLnN0cmVhbS5hdmFpbGFibGUoOCkpIHtcbiAgICAgICAgICB0aGlzLnR5cGUgPSB0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKDQpO1xuICAgICAgICAgIHRoaXMubGVuID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpO1xuICAgICAgICB9XG4gICAgICAgIHN3aXRjaCAodGhpcy50eXBlKSB7XG4gICAgICAgICAgY2FzZSAnQ09NTSc6XG4gICAgICAgICAgICBpZiAoIXRoaXMuc3RyZWFtLmF2YWlsYWJsZSh0aGlzLmxlbikpIHtcbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5mb3JtYXQgPSB7XG4gICAgICAgICAgICAgIGZvcm1hdElEOiAnbHBjbScsXG4gICAgICAgICAgICAgIGNoYW5uZWxzUGVyRnJhbWU6IHRoaXMuc3RyZWFtLnJlYWRVSW50MTYoKSxcbiAgICAgICAgICAgICAgc2FtcGxlQ291bnQ6IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKSxcbiAgICAgICAgICAgICAgYml0c1BlckNoYW5uZWw6IHRoaXMuc3RyZWFtLnJlYWRVSW50MTYoKSxcbiAgICAgICAgICAgICAgc2FtcGxlUmF0ZTogdGhpcy5zdHJlYW0ucmVhZEZsb2F0ODAoKSxcbiAgICAgICAgICAgICAgZnJhbWVzUGVyUGFja2V0OiAxLFxuICAgICAgICAgICAgICBsaXR0bGVFbmRpYW46IGZhbHNlLFxuICAgICAgICAgICAgICBmbG9hdGluZ1BvaW50OiBmYWxzZVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuZm9ybWF0LmJ5dGVzUGVyUGFja2V0ID0gKHRoaXMuZm9ybWF0LmJpdHNQZXJDaGFubmVsIC8gOCkgKiB0aGlzLmZvcm1hdC5jaGFubmVsc1BlckZyYW1lO1xuICAgICAgICAgICAgaWYgKHRoaXMuZmlsZVR5cGUgPT09ICdBSUZDJykge1xuICAgICAgICAgICAgICBmb3JtYXQgPSB0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKDQpO1xuICAgICAgICAgICAgICB0aGlzLmZvcm1hdC5saXR0bGVFbmRpYW4gPSBmb3JtYXQgPT09ICdzb3d0JyAmJiB0aGlzLmZvcm1hdC5iaXRzUGVyQ2hhbm5lbCA+IDg7XG4gICAgICAgICAgICAgIHRoaXMuZm9ybWF0LmZsb2F0aW5nUG9pbnQgPSBmb3JtYXQgPT09ICdmbDMyJyB8fCBmb3JtYXQgPT09ICdmbDY0JztcbiAgICAgICAgICAgICAgaWYgKGZvcm1hdCA9PT0gJ3R3b3MnIHx8IGZvcm1hdCA9PT0gJ3Nvd3QnIHx8IGZvcm1hdCA9PT0gJ2ZsMzInIHx8IGZvcm1hdCA9PT0gJ2ZsNjQnIHx8IGZvcm1hdCA9PT0gJ05PTkUnKSB7XG4gICAgICAgICAgICAgICAgZm9ybWF0ID0gJ2xwY20nO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHRoaXMuZm9ybWF0LmZvcm1hdElEID0gZm9ybWF0O1xuICAgICAgICAgICAgICB0aGlzLmxlbiAtPSA0O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSh0aGlzLmxlbiAtIDE4KTtcbiAgICAgICAgICAgIHRoaXMuZW1pdCgnZm9ybWF0JywgdGhpcy5mb3JtYXQpO1xuICAgICAgICAgICAgdGhpcy5lbWl0KCdkdXJhdGlvbicsIHRoaXMuZm9ybWF0LnNhbXBsZUNvdW50IC8gdGhpcy5mb3JtYXQuc2FtcGxlUmF0ZSAqIDEwMDAgfCAwKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ1NTTkQnOlxuICAgICAgICAgICAgaWYgKCEodGhpcy5yZWFkU1NOREhlYWRlciAmJiB0aGlzLnN0cmVhbS5hdmFpbGFibGUoNCkpKSB7XG4gICAgICAgICAgICAgIG9mZnNldCA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgICAgICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSg0KTtcbiAgICAgICAgICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZShvZmZzZXQpO1xuICAgICAgICAgICAgICB0aGlzLnJlYWRTU05ESGVhZGVyID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJ1ZmZlciA9IHRoaXMuc3RyZWFtLnJlYWRTaW5nbGVCdWZmZXIodGhpcy5sZW4pO1xuICAgICAgICAgICAgdGhpcy5sZW4gLT0gYnVmZmVyLmxlbmd0aDtcbiAgICAgICAgICAgIHRoaXMucmVhZEhlYWRlcnMgPSB0aGlzLmxlbiA+IDA7XG4gICAgICAgICAgICB0aGlzLmVtaXQoJ2RhdGEnLCBidWZmZXIpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIGlmICghdGhpcy5zdHJlYW0uYXZhaWxhYmxlKHRoaXMubGVuKSkge1xuICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKHRoaXMubGVuKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy50eXBlICE9PSAnU1NORCcpIHtcbiAgICAgICAgICB0aGlzLnJlYWRIZWFkZXJzID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgcmV0dXJuIEFJRkZEZW11eGVyO1xuXG4gIH0pKERlbXV4ZXIpO1xuXG59KS5jYWxsKHRoaXMpO1xuIiwiLy8gR2VuZXJhdGVkIGJ5IENvZmZlZVNjcmlwdCAxLjcuMVxuKGZ1bmN0aW9uKCkge1xuICB2YXIgQVVEZW11eGVyLCBEZW11eGVyLFxuICAgIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICAgIF9fZXh0ZW5kcyA9IGZ1bmN0aW9uKGNoaWxkLCBwYXJlbnQpIHsgZm9yICh2YXIga2V5IGluIHBhcmVudCkgeyBpZiAoX19oYXNQcm9wLmNhbGwocGFyZW50LCBrZXkpKSBjaGlsZFtrZXldID0gcGFyZW50W2tleV07IH0gZnVuY3Rpb24gY3RvcigpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGNoaWxkOyB9IGN0b3IucHJvdG90eXBlID0gcGFyZW50LnByb3RvdHlwZTsgY2hpbGQucHJvdG90eXBlID0gbmV3IGN0b3IoKTsgY2hpbGQuX19zdXBlcl9fID0gcGFyZW50LnByb3RvdHlwZTsgcmV0dXJuIGNoaWxkOyB9O1xuXG4gIERlbXV4ZXIgPSByZXF1aXJlKCcuLi9kZW11eGVyJyk7XG5cbiAgQVVEZW11eGVyID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICAgIHZhciBicHMsIGZvcm1hdHM7XG5cbiAgICBfX2V4dGVuZHMoQVVEZW11eGVyLCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gQVVEZW11eGVyKCkge1xuICAgICAgcmV0dXJuIEFVRGVtdXhlci5fX3N1cGVyX18uY29uc3RydWN0b3IuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICB9XG5cbiAgICBEZW11eGVyLnJlZ2lzdGVyKEFVRGVtdXhlcik7XG5cbiAgICBBVURlbXV4ZXIucHJvYmUgPSBmdW5jdGlvbihidWZmZXIpIHtcbiAgICAgIHJldHVybiBidWZmZXIucGVla1N0cmluZygwLCA0KSA9PT0gJy5zbmQnO1xuICAgIH07XG5cbiAgICBicHMgPSBbOCwgOCwgMTYsIDI0LCAzMiwgMzIsIDY0XTtcblxuICAgIGJwc1syNl0gPSA4O1xuXG4gICAgZm9ybWF0cyA9IHtcbiAgICAgIDE6ICd1bGF3JyxcbiAgICAgIDI3OiAnYWxhdydcbiAgICB9O1xuXG4gICAgQVVEZW11eGVyLnByb3RvdHlwZS5yZWFkQ2h1bmsgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBieXRlcywgZGF0YVNpemUsIGVuY29kaW5nLCBzaXplO1xuICAgICAgaWYgKCF0aGlzLnJlYWRIZWFkZXIgJiYgdGhpcy5zdHJlYW0uYXZhaWxhYmxlKDI0KSkge1xuICAgICAgICBpZiAodGhpcy5zdHJlYW0ucmVhZFN0cmluZyg0KSAhPT0gJy5zbmQnKSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuZW1pdCgnZXJyb3InLCAnSW52YWxpZCBBVSBmaWxlLicpO1xuICAgICAgICB9XG4gICAgICAgIHNpemUgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICAgIGRhdGFTaXplID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpO1xuICAgICAgICBlbmNvZGluZyA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgICAgdGhpcy5mb3JtYXQgPSB7XG4gICAgICAgICAgZm9ybWF0SUQ6IGZvcm1hdHNbZW5jb2RpbmddIHx8ICdscGNtJyxcbiAgICAgICAgICBsaXR0bGVFbmRpYW46IGZhbHNlLFxuICAgICAgICAgIGZsb2F0aW5nUG9pbnQ6IGVuY29kaW5nID09PSA2IHx8IGVuY29kaW5nID09PSA3LFxuICAgICAgICAgIGJpdHNQZXJDaGFubmVsOiBicHNbZW5jb2RpbmcgLSAxXSxcbiAgICAgICAgICBzYW1wbGVSYXRlOiB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCksXG4gICAgICAgICAgY2hhbm5lbHNQZXJGcmFtZTogdGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpLFxuICAgICAgICAgIGZyYW1lc1BlclBhY2tldDogMVxuICAgICAgICB9O1xuICAgICAgICBpZiAodGhpcy5mb3JtYXQuYml0c1BlckNoYW5uZWwgPT0gbnVsbCkge1xuICAgICAgICAgIHJldHVybiB0aGlzLmVtaXQoJ2Vycm9yJywgJ1Vuc3VwcG9ydGVkIGVuY29kaW5nIGluIEFVIGZpbGUuJyk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5mb3JtYXQuYnl0ZXNQZXJQYWNrZXQgPSAodGhpcy5mb3JtYXQuYml0c1BlckNoYW5uZWwgLyA4KSAqIHRoaXMuZm9ybWF0LmNoYW5uZWxzUGVyRnJhbWU7XG4gICAgICAgIGlmIChkYXRhU2l6ZSAhPT0gMHhmZmZmZmZmZikge1xuICAgICAgICAgIGJ5dGVzID0gdGhpcy5mb3JtYXQuYml0c1BlckNoYW5uZWwgLyA4O1xuICAgICAgICAgIHRoaXMuZW1pdCgnZHVyYXRpb24nLCBkYXRhU2l6ZSAvIGJ5dGVzIC8gdGhpcy5mb3JtYXQuY2hhbm5lbHNQZXJGcmFtZSAvIHRoaXMuZm9ybWF0LnNhbXBsZVJhdGUgKiAxMDAwIHwgMCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5lbWl0KCdmb3JtYXQnLCB0aGlzLmZvcm1hdCk7XG4gICAgICAgIHRoaXMucmVhZEhlYWRlciA9IHRydWU7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5yZWFkSGVhZGVyKSB7XG4gICAgICAgIHdoaWxlICh0aGlzLnN0cmVhbS5hdmFpbGFibGUoMSkpIHtcbiAgICAgICAgICB0aGlzLmVtaXQoJ2RhdGEnLCB0aGlzLnN0cmVhbS5yZWFkU2luZ2xlQnVmZmVyKHRoaXMuc3RyZWFtLnJlbWFpbmluZ0J5dGVzKCkpKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH07XG5cbiAgICByZXR1cm4gQVVEZW11eGVyO1xuXG4gIH0pKERlbXV4ZXIpO1xuXG59KS5jYWxsKHRoaXMpO1xuIiwiLy8gR2VuZXJhdGVkIGJ5IENvZmZlZVNjcmlwdCAxLjcuMVxuKGZ1bmN0aW9uKCkge1xuICB2YXIgQ0FGRGVtdXhlciwgRGVtdXhlciwgTTRBRGVtdXhlcixcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBEZW11eGVyID0gcmVxdWlyZSgnLi4vZGVtdXhlcicpO1xuXG4gIE00QURlbXV4ZXIgPSByZXF1aXJlKCcuL200YScpO1xuXG4gIENBRkRlbXV4ZXIgPSAoZnVuY3Rpb24oX3N1cGVyKSB7XG4gICAgX19leHRlbmRzKENBRkRlbXV4ZXIsIF9zdXBlcik7XG5cbiAgICBmdW5jdGlvbiBDQUZEZW11eGVyKCkge1xuICAgICAgcmV0dXJuIENBRkRlbXV4ZXIuX19zdXBlcl9fLmNvbnN0cnVjdG9yLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfVxuXG4gICAgRGVtdXhlci5yZWdpc3RlcihDQUZEZW11eGVyKTtcblxuICAgIENBRkRlbXV4ZXIucHJvYmUgPSBmdW5jdGlvbihidWZmZXIpIHtcbiAgICAgIHJldHVybiBidWZmZXIucGVla1N0cmluZygwLCA0KSA9PT0gJ2NhZmYnO1xuICAgIH07XG5cbiAgICBDQUZEZW11eGVyLnByb3RvdHlwZS5yZWFkQ2h1bmsgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBidWZmZXIsIGJ5dGVPZmZzZXQsIGNvb2tpZSwgZW50cmllcywgZmxhZ3MsIGksIGtleSwgbWV0YWRhdGEsIG9mZnNldCwgc2FtcGxlT2Zmc2V0LCB2YWx1ZSwgX2ksIF9qLCBfcmVmO1xuICAgICAgaWYgKCF0aGlzLmZvcm1hdCAmJiB0aGlzLnN0cmVhbS5hdmFpbGFibGUoNjQpKSB7XG4gICAgICAgIGlmICh0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKDQpICE9PSAnY2FmZicpIHtcbiAgICAgICAgICByZXR1cm4gdGhpcy5lbWl0KCdlcnJvcicsIFwiSW52YWxpZCBDQUYsIGRvZXMgbm90IGJlZ2luIHdpdGggJ2NhZmYnXCIpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuc3RyZWFtLmFkdmFuY2UoNCk7XG4gICAgICAgIGlmICh0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKDQpICE9PSAnZGVzYycpIHtcbiAgICAgICAgICByZXR1cm4gdGhpcy5lbWl0KCdlcnJvcicsIFwiSW52YWxpZCBDQUYsICdjYWZmJyBpcyBub3QgZm9sbG93ZWQgYnkgJ2Rlc2MnXCIpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghKHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKSA9PT0gMCAmJiB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCkgPT09IDMyKSkge1xuICAgICAgICAgIHJldHVybiB0aGlzLmVtaXQoJ2Vycm9yJywgXCJJbnZhbGlkICdkZXNjJyBzaXplLCBzaG91bGQgYmUgMzJcIik7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5mb3JtYXQgPSB7fTtcbiAgICAgICAgdGhpcy5mb3JtYXQuc2FtcGxlUmF0ZSA9IHRoaXMuc3RyZWFtLnJlYWRGbG9hdDY0KCk7XG4gICAgICAgIHRoaXMuZm9ybWF0LmZvcm1hdElEID0gdGhpcy5zdHJlYW0ucmVhZFN0cmluZyg0KTtcbiAgICAgICAgZmxhZ3MgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICAgIGlmICh0aGlzLmZvcm1hdC5mb3JtYXRJRCA9PT0gJ2xwY20nKSB7XG4gICAgICAgICAgdGhpcy5mb3JtYXQuZmxvYXRpbmdQb2ludCA9IEJvb2xlYW4oZmxhZ3MgJiAxKTtcbiAgICAgICAgICB0aGlzLmZvcm1hdC5saXR0bGVFbmRpYW4gPSBCb29sZWFuKGZsYWdzICYgMik7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5mb3JtYXQuYnl0ZXNQZXJQYWNrZXQgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICAgIHRoaXMuZm9ybWF0LmZyYW1lc1BlclBhY2tldCA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgICAgdGhpcy5mb3JtYXQuY2hhbm5lbHNQZXJGcmFtZSA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgICAgdGhpcy5mb3JtYXQuYml0c1BlckNoYW5uZWwgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICAgIHRoaXMuZW1pdCgnZm9ybWF0JywgdGhpcy5mb3JtYXQpO1xuICAgICAgfVxuICAgICAgd2hpbGUgKHRoaXMuc3RyZWFtLmF2YWlsYWJsZSgxKSkge1xuICAgICAgICBpZiAoIXRoaXMuaGVhZGVyQ2FjaGUpIHtcbiAgICAgICAgICB0aGlzLmhlYWRlckNhY2hlID0ge1xuICAgICAgICAgICAgdHlwZTogdGhpcy5zdHJlYW0ucmVhZFN0cmluZyg0KSxcbiAgICAgICAgICAgIG92ZXJzaXplOiB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCkgIT09IDAsXG4gICAgICAgICAgICBzaXplOiB0aGlzLnN0cmVhbS5yZWFkVUludDMyKClcbiAgICAgICAgICB9O1xuICAgICAgICAgIGlmICh0aGlzLmhlYWRlckNhY2hlLm92ZXJzaXplKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5lbWl0KCdlcnJvcicsIFwiSG9seSBTaGl0LCBhbiBvdmVyc2l6ZWQgZmlsZSwgbm90IHN1cHBvcnRlZCBpbiBKU1wiKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgc3dpdGNoICh0aGlzLmhlYWRlckNhY2hlLnR5cGUpIHtcbiAgICAgICAgICBjYXNlICdrdWtpJzpcbiAgICAgICAgICAgIGlmICh0aGlzLnN0cmVhbS5hdmFpbGFibGUodGhpcy5oZWFkZXJDYWNoZS5zaXplKSkge1xuICAgICAgICAgICAgICBpZiAodGhpcy5mb3JtYXQuZm9ybWF0SUQgPT09ICdhYWMgJykge1xuICAgICAgICAgICAgICAgIG9mZnNldCA9IHRoaXMuc3RyZWFtLm9mZnNldCArIHRoaXMuaGVhZGVyQ2FjaGUuc2l6ZTtcbiAgICAgICAgICAgICAgICBpZiAoY29va2llID0gTTRBRGVtdXhlci5yZWFkRXNkcyh0aGlzLnN0cmVhbSkpIHtcbiAgICAgICAgICAgICAgICAgIHRoaXMuZW1pdCgnY29va2llJywgY29va2llKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5zdHJlYW0uc2VlayhvZmZzZXQpO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGJ1ZmZlciA9IHRoaXMuc3RyZWFtLnJlYWRCdWZmZXIodGhpcy5oZWFkZXJDYWNoZS5zaXplKTtcbiAgICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2Nvb2tpZScsIGJ1ZmZlcik7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgdGhpcy5oZWFkZXJDYWNoZSA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdwYWt0JzpcbiAgICAgICAgICAgIGlmICh0aGlzLnN0cmVhbS5hdmFpbGFibGUodGhpcy5oZWFkZXJDYWNoZS5zaXplKSkge1xuICAgICAgICAgICAgICBpZiAodGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpICE9PSAwKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZW1pdCgnZXJyb3InLCAnU2l6ZXMgZ3JlYXRlciB0aGFuIDMyIGJpdHMgYXJlIG5vdCBzdXBwb3J0ZWQuJyk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgdGhpcy5udW1QYWNrZXRzID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpO1xuICAgICAgICAgICAgICBpZiAodGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpICE9PSAwKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZW1pdCgnZXJyb3InLCAnU2l6ZXMgZ3JlYXRlciB0aGFuIDMyIGJpdHMgYXJlIG5vdCBzdXBwb3J0ZWQuJyk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgdGhpcy5udW1GcmFtZXMgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICAgICAgICAgIHRoaXMucHJpbWluZ0ZyYW1lcyA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgICAgICAgICAgdGhpcy5yZW1haW5kZXJGcmFtZXMgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICAgICAgICAgIHRoaXMuZW1pdCgnZHVyYXRpb24nLCB0aGlzLm51bUZyYW1lcyAvIHRoaXMuZm9ybWF0LnNhbXBsZVJhdGUgKiAxMDAwIHwgMCk7XG4gICAgICAgICAgICAgIHRoaXMuc2VudER1cmF0aW9uID0gdHJ1ZTtcbiAgICAgICAgICAgICAgYnl0ZU9mZnNldCA9IDA7XG4gICAgICAgICAgICAgIHNhbXBsZU9mZnNldCA9IDA7XG4gICAgICAgICAgICAgIGZvciAoaSA9IF9pID0gMCwgX3JlZiA9IHRoaXMubnVtUGFja2V0czsgX2kgPCBfcmVmOyBpID0gX2kgKz0gMSkge1xuICAgICAgICAgICAgICAgIHRoaXMuYWRkU2Vla1BvaW50KGJ5dGVPZmZzZXQsIHNhbXBsZU9mZnNldCk7XG4gICAgICAgICAgICAgICAgYnl0ZU9mZnNldCArPSB0aGlzLmZvcm1hdC5ieXRlc1BlclBhY2tldCB8fCBNNEFEZW11eGVyLnJlYWREZXNjckxlbih0aGlzLnN0cmVhbSk7XG4gICAgICAgICAgICAgICAgc2FtcGxlT2Zmc2V0ICs9IHRoaXMuZm9ybWF0LmZyYW1lc1BlclBhY2tldCB8fCBNNEFEZW11eGVyLnJlYWREZXNjckxlbih0aGlzLnN0cmVhbSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgdGhpcy5oZWFkZXJDYWNoZSA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdpbmZvJzpcbiAgICAgICAgICAgIGVudHJpZXMgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICAgICAgICBtZXRhZGF0YSA9IHt9O1xuICAgICAgICAgICAgZm9yIChpID0gX2ogPSAwOyAwIDw9IGVudHJpZXMgPyBfaiA8IGVudHJpZXMgOiBfaiA+IGVudHJpZXM7IGkgPSAwIDw9IGVudHJpZXMgPyArK19qIDogLS1faikge1xuICAgICAgICAgICAgICBrZXkgPSB0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKG51bGwpO1xuICAgICAgICAgICAgICB2YWx1ZSA9IHRoaXMuc3RyZWFtLnJlYWRTdHJpbmcobnVsbCk7XG4gICAgICAgICAgICAgIG1ldGFkYXRhW2tleV0gPSB2YWx1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuZW1pdCgnbWV0YWRhdGEnLCBtZXRhZGF0YSk7XG4gICAgICAgICAgICB0aGlzLmhlYWRlckNhY2hlID0gbnVsbDtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ2RhdGEnOlxuICAgICAgICAgICAgaWYgKCF0aGlzLnNlbnRGaXJzdERhdGFDaHVuaykge1xuICAgICAgICAgICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDQpO1xuICAgICAgICAgICAgICB0aGlzLmhlYWRlckNhY2hlLnNpemUgLT0gNDtcbiAgICAgICAgICAgICAgaWYgKHRoaXMuZm9ybWF0LmJ5dGVzUGVyUGFja2V0ICE9PSAwICYmICF0aGlzLnNlbnREdXJhdGlvbikge1xuICAgICAgICAgICAgICAgIHRoaXMubnVtRnJhbWVzID0gdGhpcy5oZWFkZXJDYWNoZS5zaXplIC8gdGhpcy5mb3JtYXQuYnl0ZXNQZXJQYWNrZXQ7XG4gICAgICAgICAgICAgICAgdGhpcy5lbWl0KCdkdXJhdGlvbicsIHRoaXMubnVtRnJhbWVzIC8gdGhpcy5mb3JtYXQuc2FtcGxlUmF0ZSAqIDEwMDAgfCAwKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB0aGlzLnNlbnRGaXJzdERhdGFDaHVuayA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBidWZmZXIgPSB0aGlzLnN0cmVhbS5yZWFkU2luZ2xlQnVmZmVyKHRoaXMuaGVhZGVyQ2FjaGUuc2l6ZSk7XG4gICAgICAgICAgICB0aGlzLmhlYWRlckNhY2hlLnNpemUgLT0gYnVmZmVyLmxlbmd0aDtcbiAgICAgICAgICAgIHRoaXMuZW1pdCgnZGF0YScsIGJ1ZmZlcik7XG4gICAgICAgICAgICBpZiAodGhpcy5oZWFkZXJDYWNoZS5zaXplIDw9IDApIHtcbiAgICAgICAgICAgICAgdGhpcy5oZWFkZXJDYWNoZSA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgaWYgKHRoaXMuc3RyZWFtLmF2YWlsYWJsZSh0aGlzLmhlYWRlckNhY2hlLnNpemUpKSB7XG4gICAgICAgICAgICAgIHRoaXMuc3RyZWFtLmFkdmFuY2UodGhpcy5oZWFkZXJDYWNoZS5zaXplKTtcbiAgICAgICAgICAgICAgdGhpcy5oZWFkZXJDYWNoZSA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgcmV0dXJuIENBRkRlbXV4ZXI7XG5cbiAgfSkoRGVtdXhlcik7XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBEZW11eGVyLCBNNEFEZW11eGVyLFxuICAgIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICAgIF9fZXh0ZW5kcyA9IGZ1bmN0aW9uKGNoaWxkLCBwYXJlbnQpIHsgZm9yICh2YXIga2V5IGluIHBhcmVudCkgeyBpZiAoX19oYXNQcm9wLmNhbGwocGFyZW50LCBrZXkpKSBjaGlsZFtrZXldID0gcGFyZW50W2tleV07IH0gZnVuY3Rpb24gY3RvcigpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGNoaWxkOyB9IGN0b3IucHJvdG90eXBlID0gcGFyZW50LnByb3RvdHlwZTsgY2hpbGQucHJvdG90eXBlID0gbmV3IGN0b3IoKTsgY2hpbGQuX19zdXBlcl9fID0gcGFyZW50LnByb3RvdHlwZTsgcmV0dXJuIGNoaWxkOyB9LFxuICAgIF9faW5kZXhPZiA9IFtdLmluZGV4T2YgfHwgZnVuY3Rpb24oaXRlbSkgeyBmb3IgKHZhciBpID0gMCwgbCA9IHRoaXMubGVuZ3RoOyBpIDwgbDsgaSsrKSB7IGlmIChpIGluIHRoaXMgJiYgdGhpc1tpXSA9PT0gaXRlbSkgcmV0dXJuIGk7IH0gcmV0dXJuIC0xOyB9O1xuXG4gIERlbXV4ZXIgPSByZXF1aXJlKCcuLi9kZW11eGVyJyk7XG5cbiAgTTRBRGVtdXhlciA9IChmdW5jdGlvbihfc3VwZXIpIHtcbiAgICB2YXIgQklUU19QRVJfQ0hBTk5FTCwgVFlQRVMsIGFmdGVyLCBhdG9tLCBhdG9tcywgYm9vbCwgY29udGFpbmVycywgZGlza1RyYWNrLCBnZW5yZXMsIG1ldGEsIHN0cmluZztcblxuICAgIF9fZXh0ZW5kcyhNNEFEZW11eGVyLCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gTTRBRGVtdXhlcigpIHtcbiAgICAgIHJldHVybiBNNEFEZW11eGVyLl9fc3VwZXJfXy5jb25zdHJ1Y3Rvci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH1cblxuICAgIERlbXV4ZXIucmVnaXN0ZXIoTTRBRGVtdXhlcik7XG5cbiAgICBUWVBFUyA9IFsnTTRBICcsICdNNFAgJywgJ000QiAnLCAnTTRWICcsICdpc29tJywgJ21wNDInLCAncXQgICddO1xuXG4gICAgTTRBRGVtdXhlci5wcm9iZSA9IGZ1bmN0aW9uKGJ1ZmZlcikge1xuICAgICAgdmFyIF9yZWY7XG4gICAgICByZXR1cm4gYnVmZmVyLnBlZWtTdHJpbmcoNCwgNCkgPT09ICdmdHlwJyAmJiAoX3JlZiA9IGJ1ZmZlci5wZWVrU3RyaW5nKDgsIDQpLCBfX2luZGV4T2YuY2FsbChUWVBFUywgX3JlZikgPj0gMCk7XG4gICAgfTtcblxuICAgIE00QURlbXV4ZXIucHJvdG90eXBlLmluaXQgPSBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMuYXRvbXMgPSBbXTtcbiAgICAgIHRoaXMub2Zmc2V0cyA9IFtdO1xuICAgICAgdGhpcy50cmFjayA9IG51bGw7XG4gICAgICByZXR1cm4gdGhpcy50cmFja3MgPSBbXTtcbiAgICB9O1xuXG4gICAgYXRvbXMgPSB7fTtcblxuICAgIGNvbnRhaW5lcnMgPSB7fTtcblxuICAgIGF0b20gPSBmdW5jdGlvbihuYW1lLCBmbikge1xuICAgICAgdmFyIGMsIGNvbnRhaW5lciwgX2ksIF9sZW4sIF9yZWY7XG4gICAgICBjID0gW107XG4gICAgICBfcmVmID0gbmFtZS5zcGxpdCgnLicpLnNsaWNlKDAsIC0xKTtcbiAgICAgIGZvciAoX2kgPSAwLCBfbGVuID0gX3JlZi5sZW5ndGg7IF9pIDwgX2xlbjsgX2krKykge1xuICAgICAgICBjb250YWluZXIgPSBfcmVmW19pXTtcbiAgICAgICAgYy5wdXNoKGNvbnRhaW5lcik7XG4gICAgICAgIGNvbnRhaW5lcnNbYy5qb2luKCcuJyldID0gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIGlmIChhdG9tc1tuYW1lXSA9PSBudWxsKSB7XG4gICAgICAgIGF0b21zW25hbWVdID0ge307XG4gICAgICB9XG4gICAgICByZXR1cm4gYXRvbXNbbmFtZV0uZm4gPSBmbjtcbiAgICB9O1xuXG4gICAgYWZ0ZXIgPSBmdW5jdGlvbihuYW1lLCBmbikge1xuICAgICAgaWYgKGF0b21zW25hbWVdID09IG51bGwpIHtcbiAgICAgICAgYXRvbXNbbmFtZV0gPSB7fTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBhdG9tc1tuYW1lXS5hZnRlciA9IGZuO1xuICAgIH07XG5cbiAgICBNNEFEZW11eGVyLnByb3RvdHlwZS5yZWFkQ2h1bmsgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBoYW5kbGVyLCBwYXRoLCB0eXBlO1xuICAgICAgdGhpc1tcImJyZWFrXCJdID0gZmFsc2U7XG4gICAgICB3aGlsZSAodGhpcy5zdHJlYW0uYXZhaWxhYmxlKDEpICYmICF0aGlzW1wiYnJlYWtcIl0pIHtcbiAgICAgICAgaWYgKCF0aGlzLnJlYWRIZWFkZXJzKSB7XG4gICAgICAgICAgaWYgKCF0aGlzLnN0cmVhbS5hdmFpbGFibGUoOCkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhpcy5sZW4gPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCkgLSA4O1xuICAgICAgICAgIHRoaXMudHlwZSA9IHRoaXMuc3RyZWFtLnJlYWRTdHJpbmcoNCk7XG4gICAgICAgICAgaWYgKHRoaXMubGVuID09PSAwKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhpcy5hdG9tcy5wdXNoKHRoaXMudHlwZSk7XG4gICAgICAgICAgdGhpcy5vZmZzZXRzLnB1c2godGhpcy5zdHJlYW0ub2Zmc2V0ICsgdGhpcy5sZW4pO1xuICAgICAgICAgIHRoaXMucmVhZEhlYWRlcnMgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIHBhdGggPSB0aGlzLmF0b21zLmpvaW4oJy4nKTtcbiAgICAgICAgaGFuZGxlciA9IGF0b21zW3BhdGhdO1xuICAgICAgICBpZiAoaGFuZGxlciAhPSBudWxsID8gaGFuZGxlci5mbiA6IHZvaWQgMCkge1xuICAgICAgICAgIGlmICghKHRoaXMuc3RyZWFtLmF2YWlsYWJsZSh0aGlzLmxlbikgfHwgcGF0aCA9PT0gJ21kYXQnKSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICBoYW5kbGVyLmZuLmNhbGwodGhpcyk7XG4gICAgICAgICAgaWYgKHBhdGggaW4gY29udGFpbmVycykge1xuICAgICAgICAgICAgdGhpcy5yZWFkSGVhZGVycyA9IGZhbHNlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChwYXRoIGluIGNvbnRhaW5lcnMpIHtcbiAgICAgICAgICB0aGlzLnJlYWRIZWFkZXJzID0gZmFsc2U7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWYgKCF0aGlzLnN0cmVhbS5hdmFpbGFibGUodGhpcy5sZW4pKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIHRoaXMuc3RyZWFtLmFkdmFuY2UodGhpcy5sZW4pO1xuICAgICAgICB9XG4gICAgICAgIHdoaWxlICh0aGlzLnN0cmVhbS5vZmZzZXQgPj0gdGhpcy5vZmZzZXRzW3RoaXMub2Zmc2V0cy5sZW5ndGggLSAxXSkge1xuICAgICAgICAgIGhhbmRsZXIgPSBhdG9tc1t0aGlzLmF0b21zLmpvaW4oJy4nKV07XG4gICAgICAgICAgaWYgKGhhbmRsZXIgIT0gbnVsbCA/IGhhbmRsZXIuYWZ0ZXIgOiB2b2lkIDApIHtcbiAgICAgICAgICAgIGhhbmRsZXIuYWZ0ZXIuY2FsbCh0aGlzKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdHlwZSA9IHRoaXMuYXRvbXMucG9wKCk7XG4gICAgICAgICAgdGhpcy5vZmZzZXRzLnBvcCgpO1xuICAgICAgICAgIHRoaXMucmVhZEhlYWRlcnMgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH07XG5cbiAgICBhdG9tKCdmdHlwJywgZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgX3JlZjtcbiAgICAgIGlmIChfcmVmID0gdGhpcy5zdHJlYW0ucmVhZFN0cmluZyg0KSwgX19pbmRleE9mLmNhbGwoVFlQRVMsIF9yZWYpIDwgMCkge1xuICAgICAgICByZXR1cm4gdGhpcy5lbWl0KCdlcnJvcicsICdOb3QgYSB2YWxpZCBNNEEgZmlsZS4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnN0cmVhbS5hZHZhbmNlKHRoaXMubGVuIC0gNCk7XG4gICAgfSk7XG5cbiAgICBhdG9tKCdtb292LnRyYWsnLCBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMudHJhY2sgPSB7fTtcbiAgICAgIHJldHVybiB0aGlzLnRyYWNrcy5wdXNoKHRoaXMudHJhY2spO1xuICAgIH0pO1xuXG4gICAgYXRvbSgnbW9vdi50cmFrLnRraGQnLCBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMuc3RyZWFtLmFkdmFuY2UoNCk7XG4gICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDgpO1xuICAgICAgdGhpcy50cmFjay5pZCA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgIHJldHVybiB0aGlzLnN0cmVhbS5hZHZhbmNlKHRoaXMubGVuIC0gMTYpO1xuICAgIH0pO1xuXG4gICAgYXRvbSgnbW9vdi50cmFrLm1kaWEuaGRscicsIGZ1bmN0aW9uKCkge1xuICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSg0KTtcbiAgICAgIHRoaXMuc3RyZWFtLmFkdmFuY2UoNCk7XG4gICAgICB0aGlzLnRyYWNrLnR5cGUgPSB0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKDQpO1xuICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSgxMik7XG4gICAgICByZXR1cm4gdGhpcy5zdHJlYW0uYWR2YW5jZSh0aGlzLmxlbiAtIDI0KTtcbiAgICB9KTtcblxuICAgIGF0b20oJ21vb3YudHJhay5tZGlhLm1kaGQnLCBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMuc3RyZWFtLmFkdmFuY2UoNCk7XG4gICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDgpO1xuICAgICAgdGhpcy50cmFjay50aW1lU2NhbGUgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICB0aGlzLnRyYWNrLmR1cmF0aW9uID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpO1xuICAgICAgcmV0dXJuIHRoaXMuc3RyZWFtLmFkdmFuY2UoNCk7XG4gICAgfSk7XG5cbiAgICBCSVRTX1BFUl9DSEFOTkVMID0ge1xuICAgICAgdWxhdzogOCxcbiAgICAgIGFsYXc6IDgsXG4gICAgICBpbjI0OiAyNCxcbiAgICAgIGluMzI6IDMyLFxuICAgICAgZmwzMjogMzIsXG4gICAgICBmbDY0OiA2NFxuICAgIH07XG5cbiAgICBhdG9tKCdtb292LnRyYWsubWRpYS5taW5mLnN0Ymwuc3RzZCcsIGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGZvcm1hdCwgbnVtRW50cmllcywgdmVyc2lvbiwgX3JlZiwgX3JlZjE7XG4gICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDQpO1xuICAgICAgbnVtRW50cmllcyA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgIGlmICh0aGlzLnRyYWNrLnR5cGUgIT09ICdzb3VuJykge1xuICAgICAgICByZXR1cm4gdGhpcy5zdHJlYW0uYWR2YW5jZSh0aGlzLmxlbiAtIDgpO1xuICAgICAgfVxuICAgICAgaWYgKG51bUVudHJpZXMgIT09IDEpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZW1pdCgnZXJyb3InLCBcIk9ubHkgZXhwZWN0aW5nIG9uZSBlbnRyeSBpbiBzYW1wbGUgZGVzY3JpcHRpb24gYXRvbSFcIik7XG4gICAgICB9XG4gICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDQpO1xuICAgICAgZm9ybWF0ID0gdGhpcy50cmFjay5mb3JtYXQgPSB7fTtcbiAgICAgIGZvcm1hdC5mb3JtYXRJRCA9IHRoaXMuc3RyZWFtLnJlYWRTdHJpbmcoNCk7XG4gICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDYpO1xuICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSgyKTtcbiAgICAgIHZlcnNpb24gPSB0aGlzLnN0cmVhbS5yZWFkVUludDE2KCk7XG4gICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDYpO1xuICAgICAgZm9ybWF0LmNoYW5uZWxzUGVyRnJhbWUgPSB0aGlzLnN0cmVhbS5yZWFkVUludDE2KCk7XG4gICAgICBmb3JtYXQuYml0c1BlckNoYW5uZWwgPSB0aGlzLnN0cmVhbS5yZWFkVUludDE2KCk7XG4gICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDQpO1xuICAgICAgZm9ybWF0LnNhbXBsZVJhdGUgPSB0aGlzLnN0cmVhbS5yZWFkVUludDE2KCk7XG4gICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDIpO1xuICAgICAgaWYgKHZlcnNpb24gPT09IDEpIHtcbiAgICAgICAgZm9ybWF0LmZyYW1lc1BlclBhY2tldCA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSg0KTtcbiAgICAgICAgZm9ybWF0LmJ5dGVzUGVyRnJhbWUgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICAgIHRoaXMuc3RyZWFtLmFkdmFuY2UoNCk7XG4gICAgICB9IGVsc2UgaWYgKHZlcnNpb24gIT09IDApIHtcbiAgICAgICAgdGhpcy5lbWl0KCdlcnJvcicsICdVbmtub3duIHZlcnNpb24gaW4gc3RzZCBhdG9tJyk7XG4gICAgICB9XG4gICAgICBpZiAoQklUU19QRVJfQ0hBTk5FTFtmb3JtYXQuZm9ybWF0SURdICE9IG51bGwpIHtcbiAgICAgICAgZm9ybWF0LmJpdHNQZXJDaGFubmVsID0gQklUU19QRVJfQ0hBTk5FTFtmb3JtYXQuZm9ybWF0SURdO1xuICAgICAgfVxuICAgICAgZm9ybWF0LmZsb2F0aW5nUG9pbnQgPSAoX3JlZiA9IGZvcm1hdC5mb3JtYXRJRCkgPT09ICdmbDMyJyB8fCBfcmVmID09PSAnZmw2NCc7XG4gICAgICBmb3JtYXQubGl0dGxlRW5kaWFuID0gZm9ybWF0LmZvcm1hdElEID09PSAnc293dCcgJiYgZm9ybWF0LmJpdHNQZXJDaGFubmVsID4gODtcbiAgICAgIGlmICgoX3JlZjEgPSBmb3JtYXQuZm9ybWF0SUQpID09PSAndHdvcycgfHwgX3JlZjEgPT09ICdzb3d0JyB8fCBfcmVmMSA9PT0gJ2luMjQnIHx8IF9yZWYxID09PSAnaW4zMicgfHwgX3JlZjEgPT09ICdmbDMyJyB8fCBfcmVmMSA9PT0gJ2ZsNjQnIHx8IF9yZWYxID09PSAncmF3ICcgfHwgX3JlZjEgPT09ICdOT05FJykge1xuICAgICAgICByZXR1cm4gZm9ybWF0LmZvcm1hdElEID0gJ2xwY20nO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgYXRvbSgnbW9vdi50cmFrLm1kaWEubWluZi5zdGJsLnN0c2QuYWxhYycsIGZ1bmN0aW9uKCkge1xuICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSg0KTtcbiAgICAgIHJldHVybiB0aGlzLnRyYWNrLmNvb2tpZSA9IHRoaXMuc3RyZWFtLnJlYWRCdWZmZXIodGhpcy5sZW4gLSA0KTtcbiAgICB9KTtcblxuICAgIGF0b20oJ21vb3YudHJhay5tZGlhLm1pbmYuc3RibC5zdHNkLmVzZHMnLCBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBvZmZzZXQ7XG4gICAgICBvZmZzZXQgPSB0aGlzLnN0cmVhbS5vZmZzZXQgKyB0aGlzLmxlbjtcbiAgICAgIHRoaXMudHJhY2suY29va2llID0gTTRBRGVtdXhlci5yZWFkRXNkcyh0aGlzLnN0cmVhbSk7XG4gICAgICByZXR1cm4gdGhpcy5zdHJlYW0uc2VlayhvZmZzZXQpO1xuICAgIH0pO1xuXG4gICAgYXRvbSgnbW9vdi50cmFrLm1kaWEubWluZi5zdGJsLnN0c2Qud2F2ZS5lbmRhJywgZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy50cmFjay5mb3JtYXQubGl0dGxlRW5kaWFuID0gISF0aGlzLnN0cmVhbS5yZWFkVUludDE2KCk7XG4gICAgfSk7XG5cbiAgICBNNEFEZW11eGVyLnJlYWREZXNjckxlbiA9IGZ1bmN0aW9uKHN0cmVhbSkge1xuICAgICAgdmFyIGMsIGNvdW50LCBsZW47XG4gICAgICBsZW4gPSAwO1xuICAgICAgY291bnQgPSA0O1xuICAgICAgd2hpbGUgKGNvdW50LS0pIHtcbiAgICAgICAgYyA9IHN0cmVhbS5yZWFkVUludDgoKTtcbiAgICAgICAgbGVuID0gKGxlbiA8PCA3KSB8IChjICYgMHg3Zik7XG4gICAgICAgIGlmICghKGMgJiAweDgwKSkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gbGVuO1xuICAgIH07XG5cbiAgICBNNEFEZW11eGVyLnJlYWRFc2RzID0gZnVuY3Rpb24oc3RyZWFtKSB7XG4gICAgICB2YXIgY29kZWNfaWQsIGZsYWdzLCBsZW4sIHRhZztcbiAgICAgIHN0cmVhbS5hZHZhbmNlKDQpO1xuICAgICAgdGFnID0gc3RyZWFtLnJlYWRVSW50OCgpO1xuICAgICAgbGVuID0gTTRBRGVtdXhlci5yZWFkRGVzY3JMZW4oc3RyZWFtKTtcbiAgICAgIGlmICh0YWcgPT09IDB4MDMpIHtcbiAgICAgICAgc3RyZWFtLmFkdmFuY2UoMik7XG4gICAgICAgIGZsYWdzID0gc3RyZWFtLnJlYWRVSW50OCgpO1xuICAgICAgICBpZiAoZmxhZ3MgJiAweDgwKSB7XG4gICAgICAgICAgc3RyZWFtLmFkdmFuY2UoMik7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGZsYWdzICYgMHg0MCkge1xuICAgICAgICAgIHN0cmVhbS5hZHZhbmNlKHN0cmVhbS5yZWFkVUludDgoKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGZsYWdzICYgMHgyMCkge1xuICAgICAgICAgIHN0cmVhbS5hZHZhbmNlKDIpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzdHJlYW0uYWR2YW5jZSgyKTtcbiAgICAgIH1cbiAgICAgIHRhZyA9IHN0cmVhbS5yZWFkVUludDgoKTtcbiAgICAgIGxlbiA9IE00QURlbXV4ZXIucmVhZERlc2NyTGVuKHN0cmVhbSk7XG4gICAgICBpZiAodGFnID09PSAweDA0KSB7XG4gICAgICAgIGNvZGVjX2lkID0gc3RyZWFtLnJlYWRVSW50OCgpO1xuICAgICAgICBzdHJlYW0uYWR2YW5jZSgxKTtcbiAgICAgICAgc3RyZWFtLmFkdmFuY2UoMyk7XG4gICAgICAgIHN0cmVhbS5hZHZhbmNlKDQpO1xuICAgICAgICBzdHJlYW0uYWR2YW5jZSg0KTtcbiAgICAgICAgdGFnID0gc3RyZWFtLnJlYWRVSW50OCgpO1xuICAgICAgICBsZW4gPSBNNEFEZW11eGVyLnJlYWREZXNjckxlbihzdHJlYW0pO1xuICAgICAgICBpZiAodGFnID09PSAweDA1KSB7XG4gICAgICAgICAgcmV0dXJuIHN0cmVhbS5yZWFkQnVmZmVyKGxlbik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH07XG5cbiAgICBhdG9tKCdtb292LnRyYWsubWRpYS5taW5mLnN0Ymwuc3R0cycsIGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGVudHJpZXMsIGksIF9pO1xuICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSg0KTtcbiAgICAgIGVudHJpZXMgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICB0aGlzLnRyYWNrLnN0dHMgPSBbXTtcbiAgICAgIGZvciAoaSA9IF9pID0gMDsgX2kgPCBlbnRyaWVzOyBpID0gX2kgKz0gMSkge1xuICAgICAgICB0aGlzLnRyYWNrLnN0dHNbaV0gPSB7XG4gICAgICAgICAgY291bnQ6IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKSxcbiAgICAgICAgICBkdXJhdGlvbjogdGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5zZXR1cFNlZWtQb2ludHMoKTtcbiAgICB9KTtcblxuICAgIGF0b20oJ21vb3YudHJhay5tZGlhLm1pbmYuc3RibC5zdHNjJywgZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgZW50cmllcywgaSwgX2k7XG4gICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDQpO1xuICAgICAgZW50cmllcyA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgIHRoaXMudHJhY2suc3RzYyA9IFtdO1xuICAgICAgZm9yIChpID0gX2kgPSAwOyBfaSA8IGVudHJpZXM7IGkgPSBfaSArPSAxKSB7XG4gICAgICAgIHRoaXMudHJhY2suc3RzY1tpXSA9IHtcbiAgICAgICAgICBmaXJzdDogdGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpLFxuICAgICAgICAgIGNvdW50OiB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCksXG4gICAgICAgICAgaWQ6IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKVxuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuc2V0dXBTZWVrUG9pbnRzKCk7XG4gICAgfSk7XG5cbiAgICBhdG9tKCdtb292LnRyYWsubWRpYS5taW5mLnN0Ymwuc3RzeicsIGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGVudHJpZXMsIGksIF9pO1xuICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSg0KTtcbiAgICAgIHRoaXMudHJhY2suc2FtcGxlU2l6ZSA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgIGVudHJpZXMgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICBpZiAodGhpcy50cmFjay5zYW1wbGVTaXplID09PSAwICYmIGVudHJpZXMgPiAwKSB7XG4gICAgICAgIHRoaXMudHJhY2suc2FtcGxlU2l6ZXMgPSBbXTtcbiAgICAgICAgZm9yIChpID0gX2kgPSAwOyBfaSA8IGVudHJpZXM7IGkgPSBfaSArPSAxKSB7XG4gICAgICAgICAgdGhpcy50cmFjay5zYW1wbGVTaXplc1tpXSA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuc2V0dXBTZWVrUG9pbnRzKCk7XG4gICAgfSk7XG5cbiAgICBhdG9tKCdtb292LnRyYWsubWRpYS5taW5mLnN0Ymwuc3RjbycsIGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGVudHJpZXMsIGksIF9pO1xuICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSg0KTtcbiAgICAgIGVudHJpZXMgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKCk7XG4gICAgICB0aGlzLnRyYWNrLmNodW5rT2Zmc2V0cyA9IFtdO1xuICAgICAgZm9yIChpID0gX2kgPSAwOyBfaSA8IGVudHJpZXM7IGkgPSBfaSArPSAxKSB7XG4gICAgICAgIHRoaXMudHJhY2suY2h1bmtPZmZzZXRzW2ldID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQzMigpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuc2V0dXBTZWVrUG9pbnRzKCk7XG4gICAgfSk7XG5cbiAgICBhdG9tKCdtb292LnRyYWsudHJlZi5jaGFwJywgZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgZW50cmllcywgaSwgX2k7XG4gICAgICBlbnRyaWVzID0gdGhpcy5sZW4gPj4gMjtcbiAgICAgIHRoaXMudHJhY2suY2hhcHRlclRyYWNrcyA9IFtdO1xuICAgICAgZm9yIChpID0gX2kgPSAwOyBfaSA8IGVudHJpZXM7IGkgPSBfaSArPSAxKSB7XG4gICAgICAgIHRoaXMudHJhY2suY2hhcHRlclRyYWNrc1tpXSA9IHRoaXMuc3RyZWFtLnJlYWRVSW50MzIoKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIE00QURlbXV4ZXIucHJvdG90eXBlLnNldHVwU2Vla1BvaW50cyA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGksIGosIG9mZnNldCwgcG9zaXRpb24sIHNhbXBsZUluZGV4LCBzaXplLCBzdHNjSW5kZXgsIHN0dHNJbmRleCwgc3R0c1NhbXBsZSwgdGltZXN0YW1wLCBfaSwgX2osIF9sZW4sIF9yZWYsIF9yZWYxLCBfcmVzdWx0cztcbiAgICAgIGlmICghKCh0aGlzLnRyYWNrLmNodW5rT2Zmc2V0cyAhPSBudWxsKSAmJiAodGhpcy50cmFjay5zdHNjICE9IG51bGwpICYmICh0aGlzLnRyYWNrLnNhbXBsZVNpemUgIT0gbnVsbCkgJiYgKHRoaXMudHJhY2suc3R0cyAhPSBudWxsKSkpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgc3RzY0luZGV4ID0gMDtcbiAgICAgIHN0dHNJbmRleCA9IDA7XG4gICAgICBzdHRzSW5kZXggPSAwO1xuICAgICAgc3R0c1NhbXBsZSA9IDA7XG4gICAgICBzYW1wbGVJbmRleCA9IDA7XG4gICAgICBvZmZzZXQgPSAwO1xuICAgICAgdGltZXN0YW1wID0gMDtcbiAgICAgIHRoaXMudHJhY2suc2Vla1BvaW50cyA9IFtdO1xuICAgICAgX3JlZiA9IHRoaXMudHJhY2suY2h1bmtPZmZzZXRzO1xuICAgICAgX3Jlc3VsdHMgPSBbXTtcbiAgICAgIGZvciAoaSA9IF9pID0gMCwgX2xlbiA9IF9yZWYubGVuZ3RoOyBfaSA8IF9sZW47IGkgPSArK19pKSB7XG4gICAgICAgIHBvc2l0aW9uID0gX3JlZltpXTtcbiAgICAgICAgZm9yIChqID0gX2ogPSAwLCBfcmVmMSA9IHRoaXMudHJhY2suc3RzY1tzdHNjSW5kZXhdLmNvdW50OyBfaiA8IF9yZWYxOyBqID0gX2ogKz0gMSkge1xuICAgICAgICAgIHRoaXMudHJhY2suc2Vla1BvaW50cy5wdXNoKHtcbiAgICAgICAgICAgIG9mZnNldDogb2Zmc2V0LFxuICAgICAgICAgICAgcG9zaXRpb246IHBvc2l0aW9uLFxuICAgICAgICAgICAgdGltZXN0YW1wOiB0aW1lc3RhbXBcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBzaXplID0gdGhpcy50cmFjay5zYW1wbGVTaXplIHx8IHRoaXMudHJhY2suc2FtcGxlU2l6ZXNbc2FtcGxlSW5kZXgrK107XG4gICAgICAgICAgb2Zmc2V0ICs9IHNpemU7XG4gICAgICAgICAgcG9zaXRpb24gKz0gc2l6ZTtcbiAgICAgICAgICB0aW1lc3RhbXAgKz0gdGhpcy50cmFjay5zdHRzW3N0dHNJbmRleF0uZHVyYXRpb247XG4gICAgICAgICAgaWYgKHN0dHNJbmRleCArIDEgPCB0aGlzLnRyYWNrLnN0dHMubGVuZ3RoICYmICsrc3R0c1NhbXBsZSA9PT0gdGhpcy50cmFjay5zdHRzW3N0dHNJbmRleF0uY291bnQpIHtcbiAgICAgICAgICAgIHN0dHNTYW1wbGUgPSAwO1xuICAgICAgICAgICAgc3R0c0luZGV4Kys7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChzdHNjSW5kZXggKyAxIDwgdGhpcy50cmFjay5zdHNjLmxlbmd0aCAmJiBpICsgMSA9PT0gdGhpcy50cmFjay5zdHNjW3N0c2NJbmRleCArIDFdLmZpcnN0KSB7XG4gICAgICAgICAgX3Jlc3VsdHMucHVzaChzdHNjSW5kZXgrKyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgX3Jlc3VsdHMucHVzaCh2b2lkIDApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gX3Jlc3VsdHM7XG4gICAgfTtcblxuICAgIGFmdGVyKCdtb292JywgZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgdHJhY2ssIF9pLCBfbGVuLCBfcmVmO1xuICAgICAgaWYgKHRoaXMubWRhdE9mZnNldCAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMuc3RyZWFtLnNlZWsodGhpcy5tZGF0T2Zmc2V0IC0gOCk7XG4gICAgICB9XG4gICAgICBfcmVmID0gdGhpcy50cmFja3M7XG4gICAgICBmb3IgKF9pID0gMCwgX2xlbiA9IF9yZWYubGVuZ3RoOyBfaSA8IF9sZW47IF9pKyspIHtcbiAgICAgICAgdHJhY2sgPSBfcmVmW19pXTtcbiAgICAgICAgaWYgKCEodHJhY2sudHlwZSA9PT0gJ3NvdW4nKSkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMudHJhY2sgPSB0cmFjaztcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy50cmFjay50eXBlICE9PSAnc291bicpIHtcbiAgICAgICAgdGhpcy50cmFjayA9IG51bGw7XG4gICAgICAgIHJldHVybiB0aGlzLmVtaXQoJ2Vycm9yJywgJ05vIGF1ZGlvIHRyYWNrcyBpbiBtNGEgZmlsZS4nKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuZW1pdCgnZm9ybWF0JywgdGhpcy50cmFjay5mb3JtYXQpO1xuICAgICAgdGhpcy5lbWl0KCdkdXJhdGlvbicsIHRoaXMudHJhY2suZHVyYXRpb24gLyB0aGlzLnRyYWNrLnRpbWVTY2FsZSAqIDEwMDAgfCAwKTtcbiAgICAgIGlmICh0aGlzLnRyYWNrLmNvb2tpZSkge1xuICAgICAgICB0aGlzLmVtaXQoJ2Nvb2tpZScsIHRoaXMudHJhY2suY29va2llKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnNlZWtQb2ludHMgPSB0aGlzLnRyYWNrLnNlZWtQb2ludHM7XG4gICAgfSk7XG5cbiAgICBhdG9tKCdtZGF0JywgZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgYnl0ZXMsIGNodW5rU2l6ZSwgbGVuZ3RoLCBudW1TYW1wbGVzLCBvZmZzZXQsIHNhbXBsZSwgc2l6ZSwgX2k7XG4gICAgICBpZiAoIXRoaXMuc3RhcnRlZERhdGEpIHtcbiAgICAgICAgaWYgKHRoaXMubWRhdE9mZnNldCA9PSBudWxsKSB7XG4gICAgICAgICAgdGhpcy5tZGF0T2Zmc2V0ID0gdGhpcy5zdHJlYW0ub2Zmc2V0O1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnRyYWNrcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICBieXRlcyA9IE1hdGgubWluKHRoaXMuc3RyZWFtLnJlbWFpbmluZ0J5dGVzKCksIHRoaXMubGVuKTtcbiAgICAgICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKGJ5dGVzKTtcbiAgICAgICAgICB0aGlzLmxlbiAtPSBieXRlcztcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5jaHVua0luZGV4ID0gMDtcbiAgICAgICAgdGhpcy5zdHNjSW5kZXggPSAwO1xuICAgICAgICB0aGlzLnNhbXBsZUluZGV4ID0gMDtcbiAgICAgICAgdGhpcy50YWlsT2Zmc2V0ID0gMDtcbiAgICAgICAgdGhpcy50YWlsU2FtcGxlcyA9IDA7XG4gICAgICAgIHRoaXMuc3RhcnRlZERhdGEgPSB0cnVlO1xuICAgICAgfVxuICAgICAgaWYgKCF0aGlzLnJlYWRDaGFwdGVycykge1xuICAgICAgICB0aGlzLnJlYWRDaGFwdGVycyA9IHRoaXMucGFyc2VDaGFwdGVycygpO1xuICAgICAgICBpZiAodGhpc1tcImJyZWFrXCJdID0gIXRoaXMucmVhZENoYXB0ZXJzKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuc3RyZWFtLnNlZWsodGhpcy5tZGF0T2Zmc2V0KTtcbiAgICAgIH1cbiAgICAgIG9mZnNldCA9IHRoaXMudHJhY2suY2h1bmtPZmZzZXRzW3RoaXMuY2h1bmtJbmRleF0gKyB0aGlzLnRhaWxPZmZzZXQ7XG4gICAgICBsZW5ndGggPSAwO1xuICAgICAgaWYgKCF0aGlzLnN0cmVhbS5hdmFpbGFibGUob2Zmc2V0IC0gdGhpcy5zdHJlYW0ub2Zmc2V0KSkge1xuICAgICAgICB0aGlzW1wiYnJlYWtcIl0gPSB0cnVlO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLnN0cmVhbS5zZWVrKG9mZnNldCk7XG4gICAgICB3aGlsZSAodGhpcy5jaHVua0luZGV4IDwgdGhpcy50cmFjay5jaHVua09mZnNldHMubGVuZ3RoKSB7XG4gICAgICAgIG51bVNhbXBsZXMgPSB0aGlzLnRyYWNrLnN0c2NbdGhpcy5zdHNjSW5kZXhdLmNvdW50IC0gdGhpcy50YWlsU2FtcGxlcztcbiAgICAgICAgY2h1bmtTaXplID0gMDtcbiAgICAgICAgZm9yIChzYW1wbGUgPSBfaSA9IDA7IF9pIDwgbnVtU2FtcGxlczsgc2FtcGxlID0gX2kgKz0gMSkge1xuICAgICAgICAgIHNpemUgPSB0aGlzLnRyYWNrLnNhbXBsZVNpemUgfHwgdGhpcy50cmFjay5zYW1wbGVTaXplc1t0aGlzLnNhbXBsZUluZGV4XTtcbiAgICAgICAgICBpZiAoIXRoaXMuc3RyZWFtLmF2YWlsYWJsZShsZW5ndGggKyBzaXplKSkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICAgIGxlbmd0aCArPSBzaXplO1xuICAgICAgICAgIGNodW5rU2l6ZSArPSBzaXplO1xuICAgICAgICAgIHRoaXMuc2FtcGxlSW5kZXgrKztcbiAgICAgICAgfVxuICAgICAgICBpZiAoc2FtcGxlIDwgbnVtU2FtcGxlcykge1xuICAgICAgICAgIHRoaXMudGFpbE9mZnNldCArPSBjaHVua1NpemU7XG4gICAgICAgICAgdGhpcy50YWlsU2FtcGxlcyArPSBzYW1wbGU7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5jaHVua0luZGV4Kys7XG4gICAgICAgICAgdGhpcy50YWlsT2Zmc2V0ID0gMDtcbiAgICAgICAgICB0aGlzLnRhaWxTYW1wbGVzID0gMDtcbiAgICAgICAgICBpZiAodGhpcy5zdHNjSW5kZXggKyAxIDwgdGhpcy50cmFjay5zdHNjLmxlbmd0aCAmJiB0aGlzLmNodW5rSW5kZXggKyAxID09PSB0aGlzLnRyYWNrLnN0c2NbdGhpcy5zdHNjSW5kZXggKyAxXS5maXJzdCkge1xuICAgICAgICAgICAgdGhpcy5zdHNjSW5kZXgrKztcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKG9mZnNldCArIGxlbmd0aCAhPT0gdGhpcy50cmFjay5jaHVua09mZnNldHNbdGhpcy5jaHVua0luZGV4XSkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAobGVuZ3RoID4gMCkge1xuICAgICAgICB0aGlzLmVtaXQoJ2RhdGEnLCB0aGlzLnN0cmVhbS5yZWFkQnVmZmVyKGxlbmd0aCkpO1xuICAgICAgICByZXR1cm4gdGhpc1tcImJyZWFrXCJdID0gdGhpcy5jaHVua0luZGV4ID09PSB0aGlzLnRyYWNrLmNodW5rT2Zmc2V0cy5sZW5ndGg7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gdGhpc1tcImJyZWFrXCJdID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIE00QURlbXV4ZXIucHJvdG90eXBlLnBhcnNlQ2hhcHRlcnMgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBib20sIGlkLCBsZW4sIG5leHRUaW1lc3RhbXAsIHBvaW50LCB0aXRsZSwgdHJhY2ssIF9pLCBfbGVuLCBfcmVmLCBfcmVmMSwgX3JlZjIsIF9yZWYzO1xuICAgICAgaWYgKCEoKChfcmVmID0gdGhpcy50cmFjay5jaGFwdGVyVHJhY2tzKSAhPSBudWxsID8gX3JlZi5sZW5ndGggOiB2b2lkIDApID4gMCkpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgICBpZCA9IHRoaXMudHJhY2suY2hhcHRlclRyYWNrc1swXTtcbiAgICAgIF9yZWYxID0gdGhpcy50cmFja3M7XG4gICAgICBmb3IgKF9pID0gMCwgX2xlbiA9IF9yZWYxLmxlbmd0aDsgX2kgPCBfbGVuOyBfaSsrKSB7XG4gICAgICAgIHRyYWNrID0gX3JlZjFbX2ldO1xuICAgICAgICBpZiAodHJhY2suaWQgPT09IGlkKSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICh0cmFjay5pZCAhPT0gaWQpIHtcbiAgICAgICAgdGhpcy5lbWl0KCdlcnJvcicsICdDaGFwdGVyIHRyYWNrIGRvZXMgbm90IGV4aXN0LicpO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuY2hhcHRlcnMgPT0gbnVsbCkge1xuICAgICAgICB0aGlzLmNoYXB0ZXJzID0gW107XG4gICAgICB9XG4gICAgICB3aGlsZSAodGhpcy5jaGFwdGVycy5sZW5ndGggPCB0cmFjay5zZWVrUG9pbnRzLmxlbmd0aCkge1xuICAgICAgICBwb2ludCA9IHRyYWNrLnNlZWtQb2ludHNbdGhpcy5jaGFwdGVycy5sZW5ndGhdO1xuICAgICAgICBpZiAoIXRoaXMuc3RyZWFtLmF2YWlsYWJsZShwb2ludC5wb3NpdGlvbiAtIHRoaXMuc3RyZWFtLm9mZnNldCArIDMyKSkge1xuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnN0cmVhbS5zZWVrKHBvaW50LnBvc2l0aW9uKTtcbiAgICAgICAgbGVuID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQxNigpO1xuICAgICAgICB0aXRsZSA9IG51bGw7XG4gICAgICAgIGlmICghdGhpcy5zdHJlYW0uYXZhaWxhYmxlKGxlbikpIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGxlbiA+IDIpIHtcbiAgICAgICAgICBib20gPSB0aGlzLnN0cmVhbS5wZWVrVUludDE2KCk7XG4gICAgICAgICAgaWYgKGJvbSA9PT0gMHhmZWZmIHx8IGJvbSA9PT0gMHhmZmZlKSB7XG4gICAgICAgICAgICB0aXRsZSA9IHRoaXMuc3RyZWFtLnJlYWRTdHJpbmcobGVuLCAndXRmMTYtYm9tJyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aXRsZSA9PSBudWxsKSB7XG4gICAgICAgICAgdGl0bGUgPSB0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKGxlbiwgJ3V0ZjgnKTtcbiAgICAgICAgfVxuICAgICAgICBuZXh0VGltZXN0YW1wID0gKF9yZWYyID0gKF9yZWYzID0gdHJhY2suc2Vla1BvaW50c1t0aGlzLmNoYXB0ZXJzLmxlbmd0aCArIDFdKSAhPSBudWxsID8gX3JlZjMudGltZXN0YW1wIDogdm9pZCAwKSAhPSBudWxsID8gX3JlZjIgOiB0cmFjay5kdXJhdGlvbjtcbiAgICAgICAgdGhpcy5jaGFwdGVycy5wdXNoKHtcbiAgICAgICAgICB0aXRsZTogdGl0bGUsXG4gICAgICAgICAgdGltZXN0YW1wOiBwb2ludC50aW1lc3RhbXAgLyB0cmFjay50aW1lU2NhbGUgKiAxMDAwIHwgMCxcbiAgICAgICAgICBkdXJhdGlvbjogKG5leHRUaW1lc3RhbXAgLSBwb2ludC50aW1lc3RhbXApIC8gdHJhY2sudGltZVNjYWxlICogMTAwMCB8IDBcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0aGlzLmVtaXQoJ2NoYXB0ZXJzJywgdGhpcy5jaGFwdGVycyk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9O1xuXG4gICAgYXRvbSgnbW9vdi51ZHRhLm1ldGEnLCBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMubWV0YWRhdGEgPSB7fTtcbiAgICAgIHJldHVybiB0aGlzLnN0cmVhbS5hZHZhbmNlKDQpO1xuICAgIH0pO1xuXG4gICAgYWZ0ZXIoJ21vb3YudWR0YS5tZXRhJywgZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy5lbWl0KCdtZXRhZGF0YScsIHRoaXMubWV0YWRhdGEpO1xuICAgIH0pO1xuXG4gICAgbWV0YSA9IGZ1bmN0aW9uKGZpZWxkLCBuYW1lLCBmbikge1xuICAgICAgcmV0dXJuIGF0b20oXCJtb292LnVkdGEubWV0YS5pbHN0LlwiICsgZmllbGQgKyBcIi5kYXRhXCIsIGZ1bmN0aW9uKCkge1xuICAgICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDgpO1xuICAgICAgICB0aGlzLmxlbiAtPSA4O1xuICAgICAgICByZXR1cm4gZm4uY2FsbCh0aGlzLCBuYW1lKTtcbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICBzdHJpbmcgPSBmdW5jdGlvbihmaWVsZCkge1xuICAgICAgcmV0dXJuIHRoaXMubWV0YWRhdGFbZmllbGRdID0gdGhpcy5zdHJlYW0ucmVhZFN0cmluZyh0aGlzLmxlbiwgJ3V0ZjgnKTtcbiAgICB9O1xuXG4gICAgbWV0YSgnwqlhbGInLCAnYWxidW0nLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqlhcmcnLCAnYXJyYW5nZXInLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqlhcnQnLCAnYXJ0aXN0Jywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ8KpQVJUJywgJ2FydGlzdCcsIHN0cmluZyk7XG5cbiAgICBtZXRhKCdhQVJUJywgJ2FsYnVtQXJ0aXN0Jywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ2NhdGcnLCAnY2F0ZWdvcnknLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqljb20nLCAnY29tcG9zZXInLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqljcHknLCAnY29weXJpZ2h0Jywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ2NwcnQnLCAnY29weXJpZ2h0Jywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ8KpY210JywgJ2NvbW1lbnRzJywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ8KpZGF5JywgJ3JlbGVhc2VEYXRlJywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ2Rlc2MnLCAnZGVzY3JpcHRpb24nLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqlnZW4nLCAnZ2VucmUnLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqlncnAnLCAnZ3JvdXBpbmcnLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqlpc3InLCAnSVNSQycsIHN0cmluZyk7XG5cbiAgICBtZXRhKCdrZXl3JywgJ2tleXdvcmRzJywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ8KpbGFiJywgJ3JlY29yZExhYmVsJywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ2xkZXMnLCAnbG9uZ0Rlc2NyaXB0aW9uJywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ8KpbHlyJywgJ2x5cmljcycsIHN0cmluZyk7XG5cbiAgICBtZXRhKCfCqW5hbScsICd0aXRsZScsIHN0cmluZyk7XG5cbiAgICBtZXRhKCfCqXBoZycsICdyZWNvcmRpbmdDb3B5cmlnaHQnLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqlwcmQnLCAncHJvZHVjZXInLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqlwcmYnLCAncGVyZm9ybWVycycsIHN0cmluZyk7XG5cbiAgICBtZXRhKCdwdXJkJywgJ3B1cmNoYXNlRGF0ZScsIHN0cmluZyk7XG5cbiAgICBtZXRhKCdwdXJsJywgJ3BvZGNhc3RVUkwnLCBzdHJpbmcpO1xuXG4gICAgbWV0YSgnwqlzd2YnLCAnc29uZ3dyaXRlcicsIHN0cmluZyk7XG5cbiAgICBtZXRhKCfCqXRvbycsICdlbmNvZGVyJywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ8Kpd3J0JywgJ2NvbXBvc2VyJywgc3RyaW5nKTtcblxuICAgIG1ldGEoJ2NvdnInLCAnY292ZXJBcnQnLCBmdW5jdGlvbihmaWVsZCkge1xuICAgICAgcmV0dXJuIHRoaXMubWV0YWRhdGFbZmllbGRdID0gdGhpcy5zdHJlYW0ucmVhZEJ1ZmZlcih0aGlzLmxlbik7XG4gICAgfSk7XG5cbiAgICBnZW5yZXMgPSBbXCJCbHVlc1wiLCBcIkNsYXNzaWMgUm9ja1wiLCBcIkNvdW50cnlcIiwgXCJEYW5jZVwiLCBcIkRpc2NvXCIsIFwiRnVua1wiLCBcIkdydW5nZVwiLCBcIkhpcC1Ib3BcIiwgXCJKYXp6XCIsIFwiTWV0YWxcIiwgXCJOZXcgQWdlXCIsIFwiT2xkaWVzXCIsIFwiT3RoZXJcIiwgXCJQb3BcIiwgXCJSJkJcIiwgXCJSYXBcIiwgXCJSZWdnYWVcIiwgXCJSb2NrXCIsIFwiVGVjaG5vXCIsIFwiSW5kdXN0cmlhbFwiLCBcIkFsdGVybmF0aXZlXCIsIFwiU2thXCIsIFwiRGVhdGggTWV0YWxcIiwgXCJQcmFua3NcIiwgXCJTb3VuZHRyYWNrXCIsIFwiRXVyby1UZWNobm9cIiwgXCJBbWJpZW50XCIsIFwiVHJpcC1Ib3BcIiwgXCJWb2NhbFwiLCBcIkphenorRnVua1wiLCBcIkZ1c2lvblwiLCBcIlRyYW5jZVwiLCBcIkNsYXNzaWNhbFwiLCBcIkluc3RydW1lbnRhbFwiLCBcIkFjaWRcIiwgXCJIb3VzZVwiLCBcIkdhbWVcIiwgXCJTb3VuZCBDbGlwXCIsIFwiR29zcGVsXCIsIFwiTm9pc2VcIiwgXCJBbHRlcm5Sb2NrXCIsIFwiQmFzc1wiLCBcIlNvdWxcIiwgXCJQdW5rXCIsIFwiU3BhY2VcIiwgXCJNZWRpdGF0aXZlXCIsIFwiSW5zdHJ1bWVudGFsIFBvcFwiLCBcIkluc3RydW1lbnRhbCBSb2NrXCIsIFwiRXRobmljXCIsIFwiR290aGljXCIsIFwiRGFya3dhdmVcIiwgXCJUZWNobm8tSW5kdXN0cmlhbFwiLCBcIkVsZWN0cm9uaWNcIiwgXCJQb3AtRm9sa1wiLCBcIkV1cm9kYW5jZVwiLCBcIkRyZWFtXCIsIFwiU291dGhlcm4gUm9ja1wiLCBcIkNvbWVkeVwiLCBcIkN1bHRcIiwgXCJHYW5nc3RhXCIsIFwiVG9wIDQwXCIsIFwiQ2hyaXN0aWFuIFJhcFwiLCBcIlBvcC9GdW5rXCIsIFwiSnVuZ2xlXCIsIFwiTmF0aXZlIEFtZXJpY2FuXCIsIFwiQ2FiYXJldFwiLCBcIk5ldyBXYXZlXCIsIFwiUHN5Y2hhZGVsaWNcIiwgXCJSYXZlXCIsIFwiU2hvd3R1bmVzXCIsIFwiVHJhaWxlclwiLCBcIkxvLUZpXCIsIFwiVHJpYmFsXCIsIFwiQWNpZCBQdW5rXCIsIFwiQWNpZCBKYXp6XCIsIFwiUG9sa2FcIiwgXCJSZXRyb1wiLCBcIk11c2ljYWxcIiwgXCJSb2NrICYgUm9sbFwiLCBcIkhhcmQgUm9ja1wiLCBcIkZvbGtcIiwgXCJGb2xrL1JvY2tcIiwgXCJOYXRpb25hbCBGb2xrXCIsIFwiU3dpbmdcIiwgXCJGYXN0IEZ1c2lvblwiLCBcIkJlYm9iXCIsIFwiTGF0aW5cIiwgXCJSZXZpdmFsXCIsIFwiQ2VsdGljXCIsIFwiQmx1ZWdyYXNzXCIsIFwiQXZhbnRnYXJkZVwiLCBcIkdvdGhpYyBSb2NrXCIsIFwiUHJvZ3Jlc3NpdmUgUm9ja1wiLCBcIlBzeWNoZWRlbGljIFJvY2tcIiwgXCJTeW1waG9uaWMgUm9ja1wiLCBcIlNsb3cgUm9ja1wiLCBcIkJpZyBCYW5kXCIsIFwiQ2hvcnVzXCIsIFwiRWFzeSBMaXN0ZW5pbmdcIiwgXCJBY291c3RpY1wiLCBcIkh1bW91clwiLCBcIlNwZWVjaFwiLCBcIkNoYW5zb25cIiwgXCJPcGVyYVwiLCBcIkNoYW1iZXIgTXVzaWNcIiwgXCJTb25hdGFcIiwgXCJTeW1waG9ueVwiLCBcIkJvb3R5IEJhc3NcIiwgXCJQcmltdXNcIiwgXCJQb3JuIEdyb292ZVwiLCBcIlNhdGlyZVwiLCBcIlNsb3cgSmFtXCIsIFwiQ2x1YlwiLCBcIlRhbmdvXCIsIFwiU2FtYmFcIiwgXCJGb2xrbG9yZVwiLCBcIkJhbGxhZFwiLCBcIlBvd2VyIEJhbGxhZFwiLCBcIlJoeXRobWljIFNvdWxcIiwgXCJGcmVlc3R5bGVcIiwgXCJEdWV0XCIsIFwiUHVuayBSb2NrXCIsIFwiRHJ1bSBTb2xvXCIsIFwiQSBDYXBlbGxhXCIsIFwiRXVyby1Ib3VzZVwiLCBcIkRhbmNlIEhhbGxcIl07XG5cbiAgICBtZXRhKCdnbnJlJywgJ2dlbnJlJywgZnVuY3Rpb24oZmllbGQpIHtcbiAgICAgIHJldHVybiB0aGlzLm1ldGFkYXRhW2ZpZWxkXSA9IGdlbnJlc1t0aGlzLnN0cmVhbS5yZWFkVUludDE2KCkgLSAxXTtcbiAgICB9KTtcblxuICAgIG1ldGEoJ3RtcG8nLCAndGVtcG8nLCBmdW5jdGlvbihmaWVsZCkge1xuICAgICAgcmV0dXJuIHRoaXMubWV0YWRhdGFbZmllbGRdID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQxNigpO1xuICAgIH0pO1xuXG4gICAgbWV0YSgncnRuZycsICdyYXRpbmcnLCBmdW5jdGlvbihmaWVsZCkge1xuICAgICAgdmFyIHJhdGluZztcbiAgICAgIHJhdGluZyA9IHRoaXMuc3RyZWFtLnJlYWRVSW50OCgpO1xuICAgICAgcmV0dXJuIHRoaXMubWV0YWRhdGFbZmllbGRdID0gcmF0aW5nID09PSAyID8gJ0NsZWFuJyA6IHJhdGluZyAhPT0gMCA/ICdFeHBsaWNpdCcgOiAnTm9uZSc7XG4gICAgfSk7XG5cbiAgICBkaXNrVHJhY2sgPSBmdW5jdGlvbihmaWVsZCkge1xuICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSgyKTtcbiAgICAgIHRoaXMubWV0YWRhdGFbZmllbGRdID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQxNigpICsgJyBvZiAnICsgdGhpcy5zdHJlYW0ucmVhZFVJbnQxNigpO1xuICAgICAgcmV0dXJuIHRoaXMuc3RyZWFtLmFkdmFuY2UodGhpcy5sZW4gLSA2KTtcbiAgICB9O1xuXG4gICAgbWV0YSgnZGlzaycsICdkaXNrTnVtYmVyJywgZGlza1RyYWNrKTtcblxuICAgIG1ldGEoJ3Rya24nLCAndHJhY2tOdW1iZXInLCBkaXNrVHJhY2spO1xuXG4gICAgYm9vbCA9IGZ1bmN0aW9uKGZpZWxkKSB7XG4gICAgICByZXR1cm4gdGhpcy5tZXRhZGF0YVtmaWVsZF0gPSB0aGlzLnN0cmVhbS5yZWFkVUludDgoKSA9PT0gMTtcbiAgICB9O1xuXG4gICAgbWV0YSgnY3BpbCcsICdjb21waWxhdGlvbicsIGJvb2wpO1xuXG4gICAgbWV0YSgncGNzdCcsICdwb2RjYXN0JywgYm9vbCk7XG5cbiAgICBtZXRhKCdwZ2FwJywgJ2dhcGxlc3MnLCBib29sKTtcblxuICAgIHJldHVybiBNNEFEZW11eGVyO1xuXG4gIH0pKERlbXV4ZXIpO1xuXG4gIG1vZHVsZS5leHBvcnRzID0gTTRBRGVtdXhlcjtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIERlbXV4ZXIsIFdBVkVEZW11eGVyLFxuICAgIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICAgIF9fZXh0ZW5kcyA9IGZ1bmN0aW9uKGNoaWxkLCBwYXJlbnQpIHsgZm9yICh2YXIga2V5IGluIHBhcmVudCkgeyBpZiAoX19oYXNQcm9wLmNhbGwocGFyZW50LCBrZXkpKSBjaGlsZFtrZXldID0gcGFyZW50W2tleV07IH0gZnVuY3Rpb24gY3RvcigpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGNoaWxkOyB9IGN0b3IucHJvdG90eXBlID0gcGFyZW50LnByb3RvdHlwZTsgY2hpbGQucHJvdG90eXBlID0gbmV3IGN0b3IoKTsgY2hpbGQuX19zdXBlcl9fID0gcGFyZW50LnByb3RvdHlwZTsgcmV0dXJuIGNoaWxkOyB9O1xuXG4gIERlbXV4ZXIgPSByZXF1aXJlKCcuLi9kZW11eGVyJyk7XG5cbiAgV0FWRURlbXV4ZXIgPSAoZnVuY3Rpb24oX3N1cGVyKSB7XG4gICAgdmFyIGZvcm1hdHM7XG5cbiAgICBfX2V4dGVuZHMoV0FWRURlbXV4ZXIsIF9zdXBlcik7XG5cbiAgICBmdW5jdGlvbiBXQVZFRGVtdXhlcigpIHtcbiAgICAgIHJldHVybiBXQVZFRGVtdXhlci5fX3N1cGVyX18uY29uc3RydWN0b3IuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICB9XG5cbiAgICBEZW11eGVyLnJlZ2lzdGVyKFdBVkVEZW11eGVyKTtcblxuICAgIFdBVkVEZW11eGVyLnByb2JlID0gZnVuY3Rpb24oYnVmZmVyKSB7XG4gICAgICByZXR1cm4gYnVmZmVyLnBlZWtTdHJpbmcoMCwgNCkgPT09ICdSSUZGJyAmJiBidWZmZXIucGVla1N0cmluZyg4LCA0KSA9PT0gJ1dBVkUnO1xuICAgIH07XG5cbiAgICBmb3JtYXRzID0ge1xuICAgICAgMHgwMDAxOiAnbHBjbScsXG4gICAgICAweDAwMDM6ICdscGNtJyxcbiAgICAgIDB4MDAwNjogJ2FsYXcnLFxuICAgICAgMHgwMDA3OiAndWxhdydcbiAgICB9O1xuXG4gICAgV0FWRURlbXV4ZXIucHJvdG90eXBlLnJlYWRDaHVuayA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGJ1ZmZlciwgYnl0ZXMsIGVuY29kaW5nO1xuICAgICAgaWYgKCF0aGlzLnJlYWRTdGFydCAmJiB0aGlzLnN0cmVhbS5hdmFpbGFibGUoMTIpKSB7XG4gICAgICAgIGlmICh0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKDQpICE9PSAnUklGRicpIHtcbiAgICAgICAgICByZXR1cm4gdGhpcy5lbWl0KCdlcnJvcicsICdJbnZhbGlkIFdBViBmaWxlLicpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZmlsZVNpemUgPSB0aGlzLnN0cmVhbS5yZWFkVUludDMyKHRydWUpO1xuICAgICAgICB0aGlzLnJlYWRTdGFydCA9IHRydWU7XG4gICAgICAgIGlmICh0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKDQpICE9PSAnV0FWRScpIHtcbiAgICAgICAgICByZXR1cm4gdGhpcy5lbWl0KCdlcnJvcicsICdJbnZhbGlkIFdBViBmaWxlLicpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB3aGlsZSAodGhpcy5zdHJlYW0uYXZhaWxhYmxlKDEpKSB7XG4gICAgICAgIGlmICghdGhpcy5yZWFkSGVhZGVycyAmJiB0aGlzLnN0cmVhbS5hdmFpbGFibGUoOCkpIHtcbiAgICAgICAgICB0aGlzLnR5cGUgPSB0aGlzLnN0cmVhbS5yZWFkU3RyaW5nKDQpO1xuICAgICAgICAgIHRoaXMubGVuID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQzMih0cnVlKTtcbiAgICAgICAgfVxuICAgICAgICBzd2l0Y2ggKHRoaXMudHlwZSkge1xuICAgICAgICAgIGNhc2UgJ2ZtdCAnOlxuICAgICAgICAgICAgZW5jb2RpbmcgPSB0aGlzLnN0cmVhbS5yZWFkVUludDE2KHRydWUpO1xuICAgICAgICAgICAgaWYgKCEoZW5jb2RpbmcgaW4gZm9ybWF0cykpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZW1pdCgnZXJyb3InLCAnVW5zdXBwb3J0ZWQgZm9ybWF0IGluIFdBViBmaWxlLicpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5mb3JtYXQgPSB7XG4gICAgICAgICAgICAgIGZvcm1hdElEOiBmb3JtYXRzW2VuY29kaW5nXSxcbiAgICAgICAgICAgICAgZmxvYXRpbmdQb2ludDogZW5jb2RpbmcgPT09IDB4MDAwMyxcbiAgICAgICAgICAgICAgbGl0dGxlRW5kaWFuOiBmb3JtYXRzW2VuY29kaW5nXSA9PT0gJ2xwY20nLFxuICAgICAgICAgICAgICBjaGFubmVsc1BlckZyYW1lOiB0aGlzLnN0cmVhbS5yZWFkVUludDE2KHRydWUpLFxuICAgICAgICAgICAgICBzYW1wbGVSYXRlOiB0aGlzLnN0cmVhbS5yZWFkVUludDMyKHRydWUpLFxuICAgICAgICAgICAgICBmcmFtZXNQZXJQYWNrZXQ6IDFcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKDQpO1xuICAgICAgICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSgyKTtcbiAgICAgICAgICAgIHRoaXMuZm9ybWF0LmJpdHNQZXJDaGFubmVsID0gdGhpcy5zdHJlYW0ucmVhZFVJbnQxNih0cnVlKTtcbiAgICAgICAgICAgIHRoaXMuZm9ybWF0LmJ5dGVzUGVyUGFja2V0ID0gKHRoaXMuZm9ybWF0LmJpdHNQZXJDaGFubmVsIC8gOCkgKiB0aGlzLmZvcm1hdC5jaGFubmVsc1BlckZyYW1lO1xuICAgICAgICAgICAgdGhpcy5lbWl0KCdmb3JtYXQnLCB0aGlzLmZvcm1hdCk7XG4gICAgICAgICAgICB0aGlzLnN0cmVhbS5hZHZhbmNlKHRoaXMubGVuIC0gMTYpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnZGF0YSc6XG4gICAgICAgICAgICBpZiAoIXRoaXMuc2VudER1cmF0aW9uKSB7XG4gICAgICAgICAgICAgIGJ5dGVzID0gdGhpcy5mb3JtYXQuYml0c1BlckNoYW5uZWwgLyA4O1xuICAgICAgICAgICAgICB0aGlzLmVtaXQoJ2R1cmF0aW9uJywgdGhpcy5sZW4gLyBieXRlcyAvIHRoaXMuZm9ybWF0LmNoYW5uZWxzUGVyRnJhbWUgLyB0aGlzLmZvcm1hdC5zYW1wbGVSYXRlICogMTAwMCB8IDApO1xuICAgICAgICAgICAgICB0aGlzLnNlbnREdXJhdGlvbiA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBidWZmZXIgPSB0aGlzLnN0cmVhbS5yZWFkU2luZ2xlQnVmZmVyKHRoaXMubGVuKTtcbiAgICAgICAgICAgIHRoaXMubGVuIC09IGJ1ZmZlci5sZW5ndGg7XG4gICAgICAgICAgICB0aGlzLnJlYWRIZWFkZXJzID0gdGhpcy5sZW4gPiAwO1xuICAgICAgICAgICAgdGhpcy5lbWl0KCdkYXRhJywgYnVmZmVyKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICBpZiAoIXRoaXMuc3RyZWFtLmF2YWlsYWJsZSh0aGlzLmxlbikpIHtcbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5zdHJlYW0uYWR2YW5jZSh0aGlzLmxlbik7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMudHlwZSAhPT0gJ2RhdGEnKSB7XG4gICAgICAgICAgdGhpcy5yZWFkSGVhZGVycyA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfTtcblxuICAgIHJldHVybiBXQVZFRGVtdXhlcjtcblxuICB9KShEZW11eGVyKTtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIEF1ZGlvRGV2aWNlLCBFdmVudEVtaXR0ZXIsXG4gICAgX19iaW5kID0gZnVuY3Rpb24oZm4sIG1lKXsgcmV0dXJuIGZ1bmN0aW9uKCl7IHJldHVybiBmbi5hcHBseShtZSwgYXJndW1lbnRzKTsgfTsgfSxcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKCcuL2NvcmUvZXZlbnRzJyk7XG5cbiAgQXVkaW9EZXZpY2UgPSAoZnVuY3Rpb24oX3N1cGVyKSB7XG4gICAgdmFyIGRldmljZXM7XG5cbiAgICBfX2V4dGVuZHMoQXVkaW9EZXZpY2UsIF9zdXBlcik7XG5cbiAgICBmdW5jdGlvbiBBdWRpb0RldmljZShzYW1wbGVSYXRlLCBjaGFubmVscykge1xuICAgICAgdGhpcy5zYW1wbGVSYXRlID0gc2FtcGxlUmF0ZTtcbiAgICAgIHRoaXMuY2hhbm5lbHMgPSBjaGFubmVscztcbiAgICAgIHRoaXMudXBkYXRlVGltZSA9IF9fYmluZCh0aGlzLnVwZGF0ZVRpbWUsIHRoaXMpO1xuICAgICAgdGhpcy5wbGF5aW5nID0gZmFsc2U7XG4gICAgICB0aGlzLmN1cnJlbnRUaW1lID0gMDtcbiAgICAgIHRoaXMuX2xhc3RUaW1lID0gMDtcbiAgICB9XG5cbiAgICBBdWRpb0RldmljZS5wcm90b3R5cGUuc3RhcnQgPSBmdW5jdGlvbigpIHtcbiAgICAgIGlmICh0aGlzLnBsYXlpbmcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5wbGF5aW5nID0gdHJ1ZTtcbiAgICAgIGlmICh0aGlzLmRldmljZSA9PSBudWxsKSB7XG4gICAgICAgIHRoaXMuZGV2aWNlID0gQXVkaW9EZXZpY2UuY3JlYXRlKHRoaXMuc2FtcGxlUmF0ZSwgdGhpcy5jaGFubmVscyk7XG4gICAgICB9XG4gICAgICBpZiAoIXRoaXMuZGV2aWNlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIk5vIHN1cHBvcnRlZCBhdWRpbyBkZXZpY2UgZm91bmQuXCIpO1xuICAgICAgfVxuICAgICAgdGhpcy5fbGFzdFRpbWUgPSB0aGlzLmRldmljZS5nZXREZXZpY2VUaW1lKCk7XG4gICAgICB0aGlzLl90aW1lciA9IHNldEludGVydmFsKHRoaXMudXBkYXRlVGltZSwgMjAwKTtcbiAgICAgIHJldHVybiB0aGlzLmRldmljZS5vbigncmVmaWxsJywgdGhpcy5yZWZpbGwgPSAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKGJ1ZmZlcikge1xuICAgICAgICAgIHJldHVybiBfdGhpcy5lbWl0KCdyZWZpbGwnLCBidWZmZXIpO1xuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgIH07XG5cbiAgICBBdWRpb0RldmljZS5wcm90b3R5cGUuc3RvcCA9IGZ1bmN0aW9uKCkge1xuICAgICAgaWYgKCF0aGlzLnBsYXlpbmcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5wbGF5aW5nID0gZmFsc2U7XG4gICAgICB0aGlzLmRldmljZS5vZmYoJ3JlZmlsbCcsIHRoaXMucmVmaWxsKTtcbiAgICAgIHJldHVybiBjbGVhckludGVydmFsKHRoaXMuX3RpbWVyKTtcbiAgICB9O1xuXG4gICAgQXVkaW9EZXZpY2UucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBfcmVmO1xuICAgICAgdGhpcy5zdG9wKCk7XG4gICAgICByZXR1cm4gKF9yZWYgPSB0aGlzLmRldmljZSkgIT0gbnVsbCA/IF9yZWYuZGVzdHJveSgpIDogdm9pZCAwO1xuICAgIH07XG5cbiAgICBBdWRpb0RldmljZS5wcm90b3R5cGUuc2VlayA9IGZ1bmN0aW9uKGN1cnJlbnRUaW1lKSB7XG4gICAgICB0aGlzLmN1cnJlbnRUaW1lID0gY3VycmVudFRpbWU7XG4gICAgICBpZiAodGhpcy5wbGF5aW5nKSB7XG4gICAgICAgIHRoaXMuX2xhc3RUaW1lID0gdGhpcy5kZXZpY2UuZ2V0RGV2aWNlVGltZSgpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuZW1pdCgndGltZVVwZGF0ZScsIHRoaXMuY3VycmVudFRpbWUpO1xuICAgIH07XG5cbiAgICBBdWRpb0RldmljZS5wcm90b3R5cGUudXBkYXRlVGltZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHRpbWU7XG4gICAgICB0aW1lID0gdGhpcy5kZXZpY2UuZ2V0RGV2aWNlVGltZSgpO1xuICAgICAgdGhpcy5jdXJyZW50VGltZSArPSAodGltZSAtIHRoaXMuX2xhc3RUaW1lKSAvIHRoaXMuZGV2aWNlLnNhbXBsZVJhdGUgKiAxMDAwIHwgMDtcbiAgICAgIHRoaXMuX2xhc3RUaW1lID0gdGltZTtcbiAgICAgIHJldHVybiB0aGlzLmVtaXQoJ3RpbWVVcGRhdGUnLCB0aGlzLmN1cnJlbnRUaW1lKTtcbiAgICB9O1xuXG4gICAgZGV2aWNlcyA9IFtdO1xuXG4gICAgQXVkaW9EZXZpY2UucmVnaXN0ZXIgPSBmdW5jdGlvbihkZXZpY2UpIHtcbiAgICAgIHJldHVybiBkZXZpY2VzLnB1c2goZGV2aWNlKTtcbiAgICB9O1xuXG4gICAgQXVkaW9EZXZpY2UuY3JlYXRlID0gZnVuY3Rpb24oc2FtcGxlUmF0ZSwgY2hhbm5lbHMpIHtcbiAgICAgIHZhciBkZXZpY2UsIF9pLCBfbGVuO1xuICAgICAgZm9yIChfaSA9IDAsIF9sZW4gPSBkZXZpY2VzLmxlbmd0aDsgX2kgPCBfbGVuOyBfaSsrKSB7XG4gICAgICAgIGRldmljZSA9IGRldmljZXNbX2ldO1xuICAgICAgICBpZiAoZGV2aWNlLnN1cHBvcnRlZCkge1xuICAgICAgICAgIHJldHVybiBuZXcgZGV2aWNlKHNhbXBsZVJhdGUsIGNoYW5uZWxzKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfTtcblxuICAgIHJldHVybiBBdWRpb0RldmljZTtcblxuICB9KShFdmVudEVtaXR0ZXIpO1xuXG4gIG1vZHVsZS5leHBvcnRzID0gQXVkaW9EZXZpY2U7XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBBVkJ1ZmZlciwgQXVkaW9EZXZpY2UsIEV2ZW50RW1pdHRlciwgTW96aWxsYUF1ZGlvRGV2aWNlLFxuICAgIF9fYmluZCA9IGZ1bmN0aW9uKGZuLCBtZSl7IHJldHVybiBmdW5jdGlvbigpeyByZXR1cm4gZm4uYXBwbHkobWUsIGFyZ3VtZW50cyk7IH07IH0sXG4gICAgX19oYXNQcm9wID0ge30uaGFzT3duUHJvcGVydHksXG4gICAgX19leHRlbmRzID0gZnVuY3Rpb24oY2hpbGQsIHBhcmVudCkgeyBmb3IgKHZhciBrZXkgaW4gcGFyZW50KSB7IGlmIChfX2hhc1Byb3AuY2FsbChwYXJlbnQsIGtleSkpIGNoaWxkW2tleV0gPSBwYXJlbnRba2V5XTsgfSBmdW5jdGlvbiBjdG9yKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gY2hpbGQ7IH0gY3Rvci5wcm90b3R5cGUgPSBwYXJlbnQucHJvdG90eXBlOyBjaGlsZC5wcm90b3R5cGUgPSBuZXcgY3RvcigpOyBjaGlsZC5fX3N1cGVyX18gPSBwYXJlbnQucHJvdG90eXBlOyByZXR1cm4gY2hpbGQ7IH07XG5cbiAgRXZlbnRFbWl0dGVyID0gcmVxdWlyZSgnLi4vY29yZS9ldmVudHMnKTtcblxuICBBdWRpb0RldmljZSA9IHJlcXVpcmUoJy4uL2RldmljZScpO1xuXG4gIEFWQnVmZmVyID0gcmVxdWlyZSgnLi4vY29yZS9idWZmZXInKTtcblxuICBNb3ppbGxhQXVkaW9EZXZpY2UgPSAoZnVuY3Rpb24oX3N1cGVyKSB7XG4gICAgdmFyIGNyZWF0ZVRpbWVyLCBkZXN0cm95VGltZXI7XG5cbiAgICBfX2V4dGVuZHMoTW96aWxsYUF1ZGlvRGV2aWNlLCBfc3VwZXIpO1xuXG4gICAgQXVkaW9EZXZpY2UucmVnaXN0ZXIoTW96aWxsYUF1ZGlvRGV2aWNlKTtcblxuICAgIE1vemlsbGFBdWRpb0RldmljZS5zdXBwb3J0ZWQgPSAodHlwZW9mIEF1ZGlvICE9PSBcInVuZGVmaW5lZFwiICYmIEF1ZGlvICE9PSBudWxsKSAmJiAnbW96V3JpdGVBdWRpbycgaW4gbmV3IEF1ZGlvO1xuXG4gICAgZnVuY3Rpb24gTW96aWxsYUF1ZGlvRGV2aWNlKHNhbXBsZVJhdGUsIGNoYW5uZWxzKSB7XG4gICAgICB0aGlzLnNhbXBsZVJhdGUgPSBzYW1wbGVSYXRlO1xuICAgICAgdGhpcy5jaGFubmVscyA9IGNoYW5uZWxzO1xuICAgICAgdGhpcy5yZWZpbGwgPSBfX2JpbmQodGhpcy5yZWZpbGwsIHRoaXMpO1xuICAgICAgdGhpcy5hdWRpbyA9IG5ldyBBdWRpbztcbiAgICAgIHRoaXMuYXVkaW8ubW96U2V0dXAodGhpcy5jaGFubmVscywgdGhpcy5zYW1wbGVSYXRlKTtcbiAgICAgIHRoaXMud3JpdGVQb3NpdGlvbiA9IDA7XG4gICAgICB0aGlzLnByZWJ1ZmZlclNpemUgPSB0aGlzLnNhbXBsZVJhdGUgLyAyO1xuICAgICAgdGhpcy50YWlsID0gbnVsbDtcbiAgICAgIHRoaXMudGltZXIgPSBjcmVhdGVUaW1lcih0aGlzLnJlZmlsbCwgMTAwKTtcbiAgICB9XG5cbiAgICBNb3ppbGxhQXVkaW9EZXZpY2UucHJvdG90eXBlLnJlZmlsbCA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGF2YWlsYWJsZSwgYnVmZmVyLCBjdXJyZW50UG9zaXRpb24sIHdyaXR0ZW47XG4gICAgICBpZiAodGhpcy50YWlsKSB7XG4gICAgICAgIHdyaXR0ZW4gPSB0aGlzLmF1ZGlvLm1veldyaXRlQXVkaW8odGhpcy50YWlsKTtcbiAgICAgICAgdGhpcy53cml0ZVBvc2l0aW9uICs9IHdyaXR0ZW47XG4gICAgICAgIGlmICh0aGlzLndyaXRlUG9zaXRpb24gPCB0aGlzLnRhaWwubGVuZ3RoKSB7XG4gICAgICAgICAgdGhpcy50YWlsID0gdGhpcy50YWlsLnN1YmFycmF5KHdyaXR0ZW4pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMudGFpbCA9IG51bGw7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGN1cnJlbnRQb3NpdGlvbiA9IHRoaXMuYXVkaW8ubW96Q3VycmVudFNhbXBsZU9mZnNldCgpO1xuICAgICAgYXZhaWxhYmxlID0gY3VycmVudFBvc2l0aW9uICsgdGhpcy5wcmVidWZmZXJTaXplIC0gdGhpcy53cml0ZVBvc2l0aW9uO1xuICAgICAgaWYgKGF2YWlsYWJsZSA+IDApIHtcbiAgICAgICAgYnVmZmVyID0gbmV3IEZsb2F0MzJBcnJheShhdmFpbGFibGUpO1xuICAgICAgICB0aGlzLmVtaXQoJ3JlZmlsbCcsIGJ1ZmZlcik7XG4gICAgICAgIHdyaXR0ZW4gPSB0aGlzLmF1ZGlvLm1veldyaXRlQXVkaW8oYnVmZmVyKTtcbiAgICAgICAgaWYgKHdyaXR0ZW4gPCBidWZmZXIubGVuZ3RoKSB7XG4gICAgICAgICAgdGhpcy50YWlsID0gYnVmZmVyLnN1YmFycmF5KHdyaXR0ZW4pO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMud3JpdGVQb3NpdGlvbiArPSB3cml0dGVuO1xuICAgICAgfVxuICAgIH07XG5cbiAgICBNb3ppbGxhQXVkaW9EZXZpY2UucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBkZXN0cm95VGltZXIodGhpcy50aW1lcik7XG4gICAgfTtcblxuICAgIE1vemlsbGFBdWRpb0RldmljZS5wcm90b3R5cGUuZ2V0RGV2aWNlVGltZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMuYXVkaW8ubW96Q3VycmVudFNhbXBsZU9mZnNldCgpIC8gdGhpcy5jaGFubmVscztcbiAgICB9O1xuXG4gICAgY3JlYXRlVGltZXIgPSBmdW5jdGlvbihmbiwgaW50ZXJ2YWwpIHtcbiAgICAgIHZhciB1cmwsIHdvcmtlcjtcbiAgICAgIHVybCA9IEFWQnVmZmVyLm1ha2VCbG9iVVJMKFwic2V0SW50ZXJ2YWwoZnVuY3Rpb24oKSB7IHBvc3RNZXNzYWdlKCdwaW5nJyk7IH0sIFwiICsgaW50ZXJ2YWwgKyBcIik7XCIpO1xuICAgICAgaWYgKHVybCA9PSBudWxsKSB7XG4gICAgICAgIHJldHVybiBzZXRJbnRlcnZhbChmbiwgaW50ZXJ2YWwpO1xuICAgICAgfVxuICAgICAgd29ya2VyID0gbmV3IFdvcmtlcih1cmwpO1xuICAgICAgd29ya2VyLm9ubWVzc2FnZSA9IGZuO1xuICAgICAgd29ya2VyLnVybCA9IHVybDtcbiAgICAgIHJldHVybiB3b3JrZXI7XG4gICAgfTtcblxuICAgIGRlc3Ryb3lUaW1lciA9IGZ1bmN0aW9uKHRpbWVyKSB7XG4gICAgICBpZiAodGltZXIudGVybWluYXRlKSB7XG4gICAgICAgIHRpbWVyLnRlcm1pbmF0ZSgpO1xuICAgICAgICByZXR1cm4gVVJMLnJldm9rZU9iamVjdFVSTCh0aW1lci51cmwpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIGNsZWFySW50ZXJ2YWwodGltZXIpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICByZXR1cm4gTW96aWxsYUF1ZGlvRGV2aWNlO1xuXG4gIH0pKEV2ZW50RW1pdHRlcik7XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvL0phdmFTY3JpcHQgQXVkaW8gUmVzYW1wbGVyXG4vL0NvcHlyaWdodCAoQykgMjAxMS0yMDE1IEdyYW50IEdhbGl0elxuLy9SZWxlYXNlZCB0byBQdWJsaWMgRG9tYWluXG5mdW5jdGlvbiBSZXNhbXBsZXIoZnJvbVNhbXBsZVJhdGUsIHRvU2FtcGxlUmF0ZSwgY2hhbm5lbHMsIGlucHV0QnVmZmVyTGVuZ3RoKSB7XG4gIHRoaXMuZnJvbVNhbXBsZVJhdGUgPSArZnJvbVNhbXBsZVJhdGU7XG4gIHRoaXMudG9TYW1wbGVSYXRlID0gK3RvU2FtcGxlUmF0ZTtcbiAgdGhpcy5jaGFubmVscyA9IGNoYW5uZWxzIHwgMDtcbiAgdGhpcy5pbnB1dEJ1ZmZlckxlbmd0aCA9IGlucHV0QnVmZmVyTGVuZ3RoO1xuICB0aGlzLmluaXRpYWxpemUoKTtcbn1cblxuUmVzYW1wbGVyLnByb3RvdHlwZS5pbml0aWFsaXplID0gZnVuY3Rpb24gKCkge1xuICAvL1BlcmZvcm0gc29tZSBjaGVja3M6XG4gIGlmICh0aGlzLmZyb21TYW1wbGVSYXRlID4gMCAmJiB0aGlzLnRvU2FtcGxlUmF0ZSA+IDAgJiYgdGhpcy5jaGFubmVscyA+IDApIHtcbiAgICBpZiAodGhpcy5mcm9tU2FtcGxlUmF0ZSA9PSB0aGlzLnRvU2FtcGxlUmF0ZSkge1xuICAgICAgLy9TZXR1cCBhIHJlc2FtcGxlciBieXBhc3M6XG4gICAgICB0aGlzLnJlc2FtcGxlciA9IHRoaXMuYnlwYXNzUmVzYW1wbGVyOyAgICAvL1Jlc2FtcGxlciBqdXN0IHJldHVybnMgd2hhdCB3YXMgcGFzc2VkIHRocm91Z2guXG4gICAgICB0aGlzLnJhdGlvV2VpZ2h0ID0gMTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yYXRpb1dlaWdodCA9IHRoaXMuZnJvbVNhbXBsZVJhdGUgLyB0aGlzLnRvU2FtcGxlUmF0ZTtcbiAgICAgIGlmICh0aGlzLmZyb21TYW1wbGVSYXRlIDwgdGhpcy50b1NhbXBsZVJhdGUpIHtcbiAgICAgICAgLypcbiAgICAgICAgICBVc2UgZ2VuZXJpYyBsaW5lYXIgaW50ZXJwb2xhdGlvbiBpZiB1cHNhbXBsaW5nLFxuICAgICAgICAgIGFzIGxpbmVhciBpbnRlcnBvbGF0aW9uIHByb2R1Y2VzIGEgZ3JhZGllbnQgdGhhdCB3ZSB3YW50XG4gICAgICAgICAgYW5kIHdvcmtzIGZpbmUgd2l0aCB0d28gaW5wdXQgc2FtcGxlIHBvaW50cyBwZXIgb3V0cHV0IGluIHRoaXMgY2FzZS5cbiAgICAgICAgKi9cbiAgICAgICAgdGhpcy5jb21waWxlTGluZWFySW50ZXJwb2xhdGlvbkZ1bmN0aW9uKCk7XG4gICAgICAgIHRoaXMubGFzdFdlaWdodCA9IDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvKlxuICAgICAgICAgIEN1c3RvbSByZXNhbXBsZXIgSSB3cm90ZSB0aGF0IGRvZXNuJ3Qgc2tpcCBzYW1wbGVzXG4gICAgICAgICAgbGlrZSBzdGFuZGFyZCBsaW5lYXIgaW50ZXJwb2xhdGlvbiBpbiBoaWdoIGRvd25zYW1wbGluZy5cbiAgICAgICAgICBUaGlzIGlzIG1vcmUgYWNjdXJhdGUgdGhhbiBsaW5lYXIgaW50ZXJwb2xhdGlvbiBvbiBkb3duc2FtcGxpbmcuXG4gICAgICAgICovXG4gICAgICAgIHRoaXMuY29tcGlsZU11bHRpVGFwRnVuY3Rpb24oKTtcbiAgICAgICAgdGhpcy50YWlsRXhpc3RzID0gZmFsc2U7XG4gICAgICAgIHRoaXMubGFzdFdlaWdodCA9IDA7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHZhciBvdXRwdXRCdWZmZXJTaXplID0gKE1hdGguY2VpbCh0aGlzLmlucHV0QnVmZmVyTGVuZ3RoICogdGhpcy50b1NhbXBsZVJhdGUgLyB0aGlzLmZyb21TYW1wbGVSYXRlIC8gdGhpcy5jaGFubmVscyAqIDEuMDEpICogdGhpcy5jaGFubmVscykgKyB0aGlzLmNoYW5uZWxzO1xuICAgICAgdGhpcy5vdXRwdXRCdWZmZXIgPSBuZXcgRmxvYXQzMkFycmF5KG91dHB1dEJ1ZmZlclNpemUpO1xuICAgICAgdGhpcy5sYXN0T3V0cHV0ID0gbmV3IEZsb2F0MzJBcnJheSh0aGlzLmNoYW5uZWxzKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgdGhyb3cobmV3IEVycm9yKFwiSW52YWxpZCBzZXR0aW5ncyBzcGVjaWZpZWQgZm9yIHRoZSByZXNhbXBsZXIuXCIpKTtcbiAgfVxufTtcblxuUmVzYW1wbGVyLnByb3RvdHlwZS5jb21waWxlTGluZWFySW50ZXJwb2xhdGlvbkZ1bmN0aW9uID0gZnVuY3Rpb24gKCkge1xuICB2YXIgdG9Db21waWxlID0gXCJ2YXIgb3V0cHV0T2Zmc2V0ID0gMDtcXFxuICAgIHZhciBidWZmZXJMZW5ndGggPSBidWZmZXIubGVuZ3RoO1xcXG4gICAgaWYgKGJ1ZmZlckxlbmd0aCA+IDApIHtcXFxuICAgICAgdmFyIHdlaWdodCA9IHRoaXMubGFzdFdlaWdodDtcXFxuICAgICAgdmFyIGZpcnN0V2VpZ2h0ID0gMDtcXFxuICAgICAgdmFyIHNlY29uZFdlaWdodCA9IDA7XFxcbiAgICAgIHZhciBzb3VyY2VPZmZzZXQgPSAwO1xcXG4gICAgICB2YXIgb3V0cHV0T2Zmc2V0ID0gMDtcXFxuICAgICAgdmFyIG91dHB1dEJ1ZmZlciA9IHRoaXMub3V0cHV0QnVmZmVyO1xcXG4gICAgICBmb3IgKDsgd2VpZ2h0IDwgMTsgd2VpZ2h0ICs9IFwiICsgdGhpcy5yYXRpb1dlaWdodCArIFwiKSB7XFxcbiAgICAgICAgc2Vjb25kV2VpZ2h0ID0gd2VpZ2h0ICUgMTtcXFxuICAgICAgICBmaXJzdFdlaWdodCA9IDEgLSBzZWNvbmRXZWlnaHQ7XCI7XG4gICAgICAgIGZvciAodmFyIGNoYW5uZWwgPSAwOyBjaGFubmVsIDwgdGhpcy5jaGFubmVsczsgKytjaGFubmVsKSB7XG4gICAgICAgICAgdG9Db21waWxlICs9IFwib3V0cHV0QnVmZmVyW291dHB1dE9mZnNldCsrXSA9ICh0aGlzLmxhc3RPdXRwdXRbXCIgKyBjaGFubmVsICsgXCJdICogZmlyc3RXZWlnaHQpICsgKGJ1ZmZlcltcIiArIGNoYW5uZWwgKyBcIl0gKiBzZWNvbmRXZWlnaHQpO1wiO1xuICAgICAgICB9XG4gICAgICB0b0NvbXBpbGUgKz0gXCJ9XFxcbiAgICAgIHdlaWdodCAtPSAxO1xcXG4gICAgICBmb3IgKGJ1ZmZlckxlbmd0aCAtPSBcIiArIHRoaXMuY2hhbm5lbHMgKyBcIiwgc291cmNlT2Zmc2V0ID0gTWF0aC5mbG9vcih3ZWlnaHQpICogXCIgKyB0aGlzLmNoYW5uZWxzICsgXCI7IHNvdXJjZU9mZnNldCA8IGJ1ZmZlckxlbmd0aDspIHtcXFxuICAgICAgICBzZWNvbmRXZWlnaHQgPSB3ZWlnaHQgJSAxO1xcXG4gICAgICAgIGZpcnN0V2VpZ2h0ID0gMSAtIHNlY29uZFdlaWdodDtcIjtcbiAgICAgICAgZm9yICh2YXIgY2hhbm5lbCA9IDA7IGNoYW5uZWwgPCB0aGlzLmNoYW5uZWxzOyArK2NoYW5uZWwpIHtcbiAgICAgICAgICB0b0NvbXBpbGUgKz0gXCJvdXRwdXRCdWZmZXJbb3V0cHV0T2Zmc2V0KytdID0gKGJ1ZmZlcltzb3VyY2VPZmZzZXRcIiArICgoY2hhbm5lbCA+IDApID8gKFwiICsgXCIgKyBjaGFubmVsKSA6IFwiXCIpICsgXCJdICogZmlyc3RXZWlnaHQpICsgKGJ1ZmZlcltzb3VyY2VPZmZzZXQgKyBcIiArICh0aGlzLmNoYW5uZWxzICsgY2hhbm5lbCkgKyBcIl0gKiBzZWNvbmRXZWlnaHQpO1wiO1xuICAgICAgICB9XG4gICAgICAgIHRvQ29tcGlsZSArPSBcIndlaWdodCArPSBcIiArIHRoaXMucmF0aW9XZWlnaHQgKyBcIjtcXFxuICAgICAgICBzb3VyY2VPZmZzZXQgPSBNYXRoLmZsb29yKHdlaWdodCkgKiBcIiArIHRoaXMuY2hhbm5lbHMgKyBcIjtcXFxuICAgICAgfVwiO1xuICAgICAgZm9yICh2YXIgY2hhbm5lbCA9IDA7IGNoYW5uZWwgPCB0aGlzLmNoYW5uZWxzOyArK2NoYW5uZWwpIHtcbiAgICAgICAgdG9Db21waWxlICs9IFwidGhpcy5sYXN0T3V0cHV0W1wiICsgY2hhbm5lbCArIFwiXSA9IGJ1ZmZlcltzb3VyY2VPZmZzZXQrK107XCI7XG4gICAgICB9XG4gICAgICB0b0NvbXBpbGUgKz0gXCJ0aGlzLmxhc3RXZWlnaHQgPSB3ZWlnaHQgJSAxO1xcXG4gICAgfVxcXG4gICAgcmV0dXJuIHRoaXMub3V0cHV0QnVmZmVyO1wiO1xuICAgIFxuICB0aGlzLnJlc2FtcGxlciA9IEZ1bmN0aW9uKFwiYnVmZmVyXCIsIHRvQ29tcGlsZSk7XG59O1xuXG5SZXNhbXBsZXIucHJvdG90eXBlLmNvbXBpbGVNdWx0aVRhcEZ1bmN0aW9uID0gZnVuY3Rpb24gKCkge1xuICB2YXIgdG9Db21waWxlID0gXCJ2YXIgb3V0cHV0T2Zmc2V0ID0gMDtcXFxuICAgIHZhciBidWZmZXJMZW5ndGggPSBidWZmZXIubGVuZ3RoO1xcXG4gICAgaWYgKGJ1ZmZlckxlbmd0aCA+IDApIHtcXFxuICAgICAgdmFyIHdlaWdodCA9IDA7XCI7XG4gICAgICBmb3IgKHZhciBjaGFubmVsID0gMDsgY2hhbm5lbCA8IHRoaXMuY2hhbm5lbHM7ICsrY2hhbm5lbCkge1xuICAgICAgICB0b0NvbXBpbGUgKz0gXCJ2YXIgb3V0cHV0XCIgKyBjaGFubmVsICsgXCIgPSAwO1wiXG4gICAgICB9XG4gICAgICB0b0NvbXBpbGUgKz0gXCJ2YXIgYWN0dWFsUG9zaXRpb24gPSAwO1xcXG4gICAgICB2YXIgYW1vdW50VG9OZXh0ID0gMDtcXFxuICAgICAgdmFyIGFscmVhZHlQcm9jZXNzZWRUYWlsID0gIXRoaXMudGFpbEV4aXN0cztcXFxuICAgICAgdGhpcy50YWlsRXhpc3RzID0gZmFsc2U7XFxcbiAgICAgIHZhciBvdXRwdXRCdWZmZXIgPSB0aGlzLm91dHB1dEJ1ZmZlcjtcXFxuICAgICAgdmFyIGN1cnJlbnRQb3NpdGlvbiA9IDA7XFxcbiAgICAgIGRvIHtcXFxuICAgICAgICBpZiAoYWxyZWFkeVByb2Nlc3NlZFRhaWwpIHtcXFxuICAgICAgICAgIHdlaWdodCA9IFwiICsgdGhpcy5yYXRpb1dlaWdodCArIFwiO1wiO1xuICAgICAgICAgIGZvciAoY2hhbm5lbCA9IDA7IGNoYW5uZWwgPCB0aGlzLmNoYW5uZWxzOyArK2NoYW5uZWwpIHtcbiAgICAgICAgICAgIHRvQ29tcGlsZSArPSBcIm91dHB1dFwiICsgY2hhbm5lbCArIFwiID0gMDtcIlxuICAgICAgICAgIH1cbiAgICAgICAgdG9Db21waWxlICs9IFwifVxcXG4gICAgICAgIGVsc2Uge1xcXG4gICAgICAgICAgd2VpZ2h0ID0gdGhpcy5sYXN0V2VpZ2h0O1wiO1xuICAgICAgICAgIGZvciAoY2hhbm5lbCA9IDA7IGNoYW5uZWwgPCB0aGlzLmNoYW5uZWxzOyArK2NoYW5uZWwpIHtcbiAgICAgICAgICAgIHRvQ29tcGlsZSArPSBcIm91dHB1dFwiICsgY2hhbm5lbCArIFwiID0gdGhpcy5sYXN0T3V0cHV0W1wiICsgY2hhbm5lbCArIFwiXTtcIlxuICAgICAgICAgIH1cbiAgICAgICAgICB0b0NvbXBpbGUgKz0gXCJhbHJlYWR5UHJvY2Vzc2VkVGFpbCA9IHRydWU7XFxcbiAgICAgICAgfVxcXG4gICAgICAgIHdoaWxlICh3ZWlnaHQgPiAwICYmIGFjdHVhbFBvc2l0aW9uIDwgYnVmZmVyTGVuZ3RoKSB7XFxcbiAgICAgICAgICBhbW91bnRUb05leHQgPSAxICsgYWN0dWFsUG9zaXRpb24gLSBjdXJyZW50UG9zaXRpb247XFxcbiAgICAgICAgICBpZiAod2VpZ2h0ID49IGFtb3VudFRvTmV4dCkge1wiO1xuICAgICAgICAgICAgZm9yIChjaGFubmVsID0gMDsgY2hhbm5lbCA8IHRoaXMuY2hhbm5lbHM7ICsrY2hhbm5lbCkge1xuICAgICAgICAgICAgICB0b0NvbXBpbGUgKz0gXCJvdXRwdXRcIiArIGNoYW5uZWwgKyBcIiArPSBidWZmZXJbYWN0dWFsUG9zaXRpb24rK10gKiBhbW91bnRUb05leHQ7XCJcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRvQ29tcGlsZSArPSBcImN1cnJlbnRQb3NpdGlvbiA9IGFjdHVhbFBvc2l0aW9uO1xcXG4gICAgICAgICAgICB3ZWlnaHQgLT0gYW1vdW50VG9OZXh0O1xcXG4gICAgICAgICAgfVxcXG4gICAgICAgICAgZWxzZSB7XCI7XG4gICAgICAgICAgICBmb3IgKGNoYW5uZWwgPSAwOyBjaGFubmVsIDwgdGhpcy5jaGFubmVsczsgKytjaGFubmVsKSB7XG4gICAgICAgICAgICAgIHRvQ29tcGlsZSArPSBcIm91dHB1dFwiICsgY2hhbm5lbCArIFwiICs9IGJ1ZmZlclthY3R1YWxQb3NpdGlvblwiICsgKChjaGFubmVsID4gMCkgPyAoXCIgKyBcIiArIGNoYW5uZWwpIDogXCJcIikgKyBcIl0gKiB3ZWlnaHQ7XCJcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRvQ29tcGlsZSArPSBcImN1cnJlbnRQb3NpdGlvbiArPSB3ZWlnaHQ7XFxcbiAgICAgICAgICAgIHdlaWdodCA9IDA7XFxcbiAgICAgICAgICAgIGJyZWFrO1xcXG4gICAgICAgICAgfVxcXG4gICAgICAgIH1cXFxuICAgICAgICBpZiAod2VpZ2h0IDw9IDApIHtcIjtcbiAgICAgICAgICBmb3IgKGNoYW5uZWwgPSAwOyBjaGFubmVsIDwgdGhpcy5jaGFubmVsczsgKytjaGFubmVsKSB7XG4gICAgICAgICAgICB0b0NvbXBpbGUgKz0gXCJvdXRwdXRCdWZmZXJbb3V0cHV0T2Zmc2V0KytdID0gb3V0cHV0XCIgKyBjaGFubmVsICsgXCIgLyBcIiArIHRoaXMucmF0aW9XZWlnaHQgKyBcIjtcIlxuICAgICAgICAgIH1cbiAgICAgICAgdG9Db21waWxlICs9IFwifVxcXG4gICAgICAgIGVsc2Uge1xcXG4gICAgICAgICAgdGhpcy5sYXN0V2VpZ2h0ID0gd2VpZ2h0O1wiO1xuICAgICAgICAgIGZvciAoY2hhbm5lbCA9IDA7IGNoYW5uZWwgPCB0aGlzLmNoYW5uZWxzOyArK2NoYW5uZWwpIHtcbiAgICAgICAgICAgIHRvQ29tcGlsZSArPSBcInRoaXMubGFzdE91dHB1dFtcIiArIGNoYW5uZWwgKyBcIl0gPSBvdXRwdXRcIiArIGNoYW5uZWwgKyBcIjtcIlxuICAgICAgICAgIH1cbiAgICAgICAgICB0b0NvbXBpbGUgKz0gXCJ0aGlzLnRhaWxFeGlzdHMgPSB0cnVlO1xcXG4gICAgICAgICAgYnJlYWs7XFxcbiAgICAgICAgfVxcXG4gICAgICB9IHdoaWxlIChhY3R1YWxQb3NpdGlvbiA8IGJ1ZmZlckxlbmd0aCk7XFxcbiAgICB9XFxcbiAgICByZXR1cm4gdGhpcy5vdXRwdXRCdWZmZXI7XCI7XG4gIFxuICB0aGlzLnJlc2FtcGxlciA9IEZ1bmN0aW9uKFwiYnVmZmVyXCIsIHRvQ29tcGlsZSk7XG59O1xuXG5SZXNhbXBsZXIucHJvdG90eXBlLmJ5cGFzc1Jlc2FtcGxlciA9IGZ1bmN0aW9uIChpbnB1dEJ1ZmZlcikge1xuICByZXR1cm4gaW5wdXRCdWZmZXI7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFJlc2FtcGxlcjtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIEF1ZGlvRGV2aWNlLCBFdmVudEVtaXR0ZXIsIFJlc2FtcGxlciwgV2ViQXVkaW9EZXZpY2UsXG4gICAgX19iaW5kID0gZnVuY3Rpb24oZm4sIG1lKXsgcmV0dXJuIGZ1bmN0aW9uKCl7IHJldHVybiBmbi5hcHBseShtZSwgYXJndW1lbnRzKTsgfTsgfSxcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKCcuLi9jb3JlL2V2ZW50cycpO1xuXG4gIEF1ZGlvRGV2aWNlID0gcmVxdWlyZSgnLi4vZGV2aWNlJyk7XG5cbiAgUmVzYW1wbGVyID0gcmVxdWlyZSgnLi9yZXNhbXBsZXInKTtcblxuICBXZWJBdWRpb0RldmljZSA9IChmdW5jdGlvbihfc3VwZXIpIHtcbiAgICB2YXIgQXVkaW9Db250ZXh0LCBjcmVhdGVQcm9jZXNzb3IsIHNoYXJlZENvbnRleHQ7XG5cbiAgICBfX2V4dGVuZHMoV2ViQXVkaW9EZXZpY2UsIF9zdXBlcik7XG5cbiAgICBBdWRpb0RldmljZS5yZWdpc3RlcihXZWJBdWRpb0RldmljZSk7XG5cbiAgICBBdWRpb0NvbnRleHQgPSBnbG9iYWwuQXVkaW9Db250ZXh0IHx8IGdsb2JhbC53ZWJraXRBdWRpb0NvbnRleHQ7XG5cbiAgICBXZWJBdWRpb0RldmljZS5zdXBwb3J0ZWQgPSBBdWRpb0NvbnRleHQgJiYgKHR5cGVvZiBBdWRpb0NvbnRleHQucHJvdG90eXBlW2NyZWF0ZVByb2Nlc3NvciA9ICdjcmVhdGVTY3JpcHRQcm9jZXNzb3InXSA9PT0gJ2Z1bmN0aW9uJyB8fCB0eXBlb2YgQXVkaW9Db250ZXh0LnByb3RvdHlwZVtjcmVhdGVQcm9jZXNzb3IgPSAnY3JlYXRlSmF2YVNjcmlwdE5vZGUnXSA9PT0gJ2Z1bmN0aW9uJyk7XG5cbiAgICBzaGFyZWRDb250ZXh0ID0gbnVsbDtcblxuICAgIGZ1bmN0aW9uIFdlYkF1ZGlvRGV2aWNlKHNhbXBsZVJhdGUsIGNoYW5uZWxzKSB7XG4gICAgICB0aGlzLnNhbXBsZVJhdGUgPSBzYW1wbGVSYXRlO1xuICAgICAgdGhpcy5jaGFubmVscyA9IGNoYW5uZWxzO1xuICAgICAgdGhpcy5yZWZpbGwgPSBfX2JpbmQodGhpcy5yZWZpbGwsIHRoaXMpO1xuICAgICAgdGhpcy5jb250ZXh0ID0gc2hhcmVkQ29udGV4dCAhPSBudWxsID8gc2hhcmVkQ29udGV4dCA6IHNoYXJlZENvbnRleHQgPSBuZXcgQXVkaW9Db250ZXh0O1xuICAgICAgdGhpcy5kZXZpY2VTYW1wbGVSYXRlID0gdGhpcy5jb250ZXh0LnNhbXBsZVJhdGU7XG4gICAgICB0aGlzLmJ1ZmZlclNpemUgPSBNYXRoLmNlaWwoNDA5NiAvICh0aGlzLmRldmljZVNhbXBsZVJhdGUgLyB0aGlzLnNhbXBsZVJhdGUpICogdGhpcy5jaGFubmVscyk7XG4gICAgICB0aGlzLmJ1ZmZlclNpemUgKz0gdGhpcy5idWZmZXJTaXplICUgdGhpcy5jaGFubmVscztcbiAgICAgIGlmICh0aGlzLmRldmljZVNhbXBsZVJhdGUgIT09IHRoaXMuc2FtcGxlUmF0ZSkge1xuICAgICAgICB0aGlzLnJlc2FtcGxlciA9IG5ldyBSZXNhbXBsZXIodGhpcy5zYW1wbGVSYXRlLCB0aGlzLmRldmljZVNhbXBsZVJhdGUsIHRoaXMuY2hhbm5lbHMsIHRoaXMuYnVmZmVyU2l6ZSk7XG4gICAgICB9XG4gICAgICB0aGlzLm5vZGUgPSB0aGlzLmNvbnRleHRbY3JlYXRlUHJvY2Vzc29yXSg0MDk2LCB0aGlzLmNoYW5uZWxzLCB0aGlzLmNoYW5uZWxzKTtcbiAgICAgIHRoaXMubm9kZS5vbmF1ZGlvcHJvY2VzcyA9IHRoaXMucmVmaWxsO1xuICAgICAgdGhpcy5ub2RlLmNvbm5lY3QodGhpcy5jb250ZXh0LmRlc3RpbmF0aW9uKTtcbiAgICB9XG5cbiAgICBXZWJBdWRpb0RldmljZS5wcm90b3R5cGUucmVmaWxsID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgIHZhciBjaGFubmVsQ291bnQsIGNoYW5uZWxzLCBkYXRhLCBpLCBuLCBvdXRwdXRCdWZmZXIsIF9pLCBfaiwgX2ssIF9yZWY7XG4gICAgICBvdXRwdXRCdWZmZXIgPSBldmVudC5vdXRwdXRCdWZmZXI7XG4gICAgICBjaGFubmVsQ291bnQgPSBvdXRwdXRCdWZmZXIubnVtYmVyT2ZDaGFubmVscztcbiAgICAgIGNoYW5uZWxzID0gbmV3IEFycmF5KGNoYW5uZWxDb3VudCk7XG4gICAgICBmb3IgKGkgPSBfaSA9IDA7IF9pIDwgY2hhbm5lbENvdW50OyBpID0gX2kgKz0gMSkge1xuICAgICAgICBjaGFubmVsc1tpXSA9IG91dHB1dEJ1ZmZlci5nZXRDaGFubmVsRGF0YShpKTtcbiAgICAgIH1cbiAgICAgIGRhdGEgPSBuZXcgRmxvYXQzMkFycmF5KHRoaXMuYnVmZmVyU2l6ZSk7XG4gICAgICB0aGlzLmVtaXQoJ3JlZmlsbCcsIGRhdGEpO1xuICAgICAgaWYgKHRoaXMucmVzYW1wbGVyKSB7XG4gICAgICAgIGRhdGEgPSB0aGlzLnJlc2FtcGxlci5yZXNhbXBsZXIoZGF0YSk7XG4gICAgICB9XG4gICAgICBmb3IgKGkgPSBfaiA9IDAsIF9yZWYgPSBvdXRwdXRCdWZmZXIubGVuZ3RoOyBfaiA8IF9yZWY7IGkgPSBfaiArPSAxKSB7XG4gICAgICAgIGZvciAobiA9IF9rID0gMDsgX2sgPCBjaGFubmVsQ291bnQ7IG4gPSBfayArPSAxKSB7XG4gICAgICAgICAgY2hhbm5lbHNbbl1baV0gPSBkYXRhW2kgKiBjaGFubmVsQ291bnQgKyBuXTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH07XG5cbiAgICBXZWJBdWRpb0RldmljZS5wcm90b3R5cGUuZGVzdHJveSA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMubm9kZS5kaXNjb25uZWN0KDApO1xuICAgIH07XG5cbiAgICBXZWJBdWRpb0RldmljZS5wcm90b3R5cGUuZ2V0RGV2aWNlVGltZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMuY29udGV4dC5jdXJyZW50VGltZSAqIHRoaXMuc2FtcGxlUmF0ZTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIFdlYkF1ZGlvRGV2aWNlO1xuXG4gIH0pKEV2ZW50RW1pdHRlcik7XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBGaWx0ZXI7XG5cbiAgRmlsdGVyID0gKGZ1bmN0aW9uKCkge1xuICAgIGZ1bmN0aW9uIEZpbHRlcihjb250ZXh0LCBrZXkpIHtcbiAgICAgIGlmIChjb250ZXh0ICYmIGtleSkge1xuICAgICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkodGhpcywgJ3ZhbHVlJywge1xuICAgICAgICAgIGdldDogZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICByZXR1cm4gY29udGV4dFtrZXldO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgRmlsdGVyLnByb3RvdHlwZS5wcm9jZXNzID0gZnVuY3Rpb24oYnVmZmVyKSB7fTtcblxuICAgIHJldHVybiBGaWx0ZXI7XG5cbiAgfSkoKTtcblxuICBtb2R1bGUuZXhwb3J0cyA9IEZpbHRlcjtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIEJhbGFuY2VGaWx0ZXIsIEZpbHRlcixcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBGaWx0ZXIgPSByZXF1aXJlKCcuLi9maWx0ZXInKTtcblxuICBCYWxhbmNlRmlsdGVyID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICAgIF9fZXh0ZW5kcyhCYWxhbmNlRmlsdGVyLCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gQmFsYW5jZUZpbHRlcigpIHtcbiAgICAgIHJldHVybiBCYWxhbmNlRmlsdGVyLl9fc3VwZXJfXy5jb25zdHJ1Y3Rvci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH1cblxuICAgIEJhbGFuY2VGaWx0ZXIucHJvdG90eXBlLnByb2Nlc3MgPSBmdW5jdGlvbihidWZmZXIpIHtcbiAgICAgIHZhciBpLCBwYW4sIF9pLCBfcmVmO1xuICAgICAgaWYgKHRoaXMudmFsdWUgPT09IDApIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgcGFuID0gTWF0aC5tYXgoLTUwLCBNYXRoLm1pbig1MCwgdGhpcy52YWx1ZSkpO1xuICAgICAgZm9yIChpID0gX2kgPSAwLCBfcmVmID0gYnVmZmVyLmxlbmd0aDsgX2kgPCBfcmVmOyBpID0gX2kgKz0gMikge1xuICAgICAgICBidWZmZXJbaV0gKj0gTWF0aC5taW4oMSwgKDUwIC0gcGFuKSAvIDUwKTtcbiAgICAgICAgYnVmZmVyW2kgKyAxXSAqPSBNYXRoLm1pbigxLCAoNTAgKyBwYW4pIC8gNTApO1xuICAgICAgfVxuICAgIH07XG5cbiAgICByZXR1cm4gQmFsYW5jZUZpbHRlcjtcblxuICB9KShGaWx0ZXIpO1xuXG4gIG1vZHVsZS5leHBvcnRzID0gQmFsYW5jZUZpbHRlcjtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIEZpbHRlciwgVm9sdW1lRmlsdGVyLFxuICAgIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICAgIF9fZXh0ZW5kcyA9IGZ1bmN0aW9uKGNoaWxkLCBwYXJlbnQpIHsgZm9yICh2YXIga2V5IGluIHBhcmVudCkgeyBpZiAoX19oYXNQcm9wLmNhbGwocGFyZW50LCBrZXkpKSBjaGlsZFtrZXldID0gcGFyZW50W2tleV07IH0gZnVuY3Rpb24gY3RvcigpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGNoaWxkOyB9IGN0b3IucHJvdG90eXBlID0gcGFyZW50LnByb3RvdHlwZTsgY2hpbGQucHJvdG90eXBlID0gbmV3IGN0b3IoKTsgY2hpbGQuX19zdXBlcl9fID0gcGFyZW50LnByb3RvdHlwZTsgcmV0dXJuIGNoaWxkOyB9O1xuXG4gIEZpbHRlciA9IHJlcXVpcmUoJy4uL2ZpbHRlcicpO1xuXG4gIFZvbHVtZUZpbHRlciA9IChmdW5jdGlvbihfc3VwZXIpIHtcbiAgICBfX2V4dGVuZHMoVm9sdW1lRmlsdGVyLCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gVm9sdW1lRmlsdGVyKCkge1xuICAgICAgcmV0dXJuIFZvbHVtZUZpbHRlci5fX3N1cGVyX18uY29uc3RydWN0b3IuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICB9XG5cbiAgICBWb2x1bWVGaWx0ZXIucHJvdG90eXBlLnByb2Nlc3MgPSBmdW5jdGlvbihidWZmZXIpIHtcbiAgICAgIHZhciBpLCB2b2wsIF9pLCBfcmVmO1xuICAgICAgaWYgKHRoaXMudmFsdWUgPj0gMTAwKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHZvbCA9IE1hdGgubWF4KDAsIE1hdGgubWluKDEwMCwgdGhpcy52YWx1ZSkpIC8gMTAwO1xuICAgICAgZm9yIChpID0gX2kgPSAwLCBfcmVmID0gYnVmZmVyLmxlbmd0aDsgX2kgPCBfcmVmOyBpID0gX2kgKz0gMSkge1xuICAgICAgICBidWZmZXJbaV0gKj0gdm9sO1xuICAgICAgfVxuICAgIH07XG5cbiAgICByZXR1cm4gVm9sdW1lRmlsdGVyO1xuXG4gIH0pKEZpbHRlcik7XG5cbiAgbW9kdWxlLmV4cG9ydHMgPSBWb2x1bWVGaWx0ZXI7XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBBc3NldCwgQXVkaW9EZXZpY2UsIEJhbGFuY2VGaWx0ZXIsIEV2ZW50RW1pdHRlciwgUGxheWVyLCBRdWV1ZSwgVm9sdW1lRmlsdGVyLFxuICAgIF9fYmluZCA9IGZ1bmN0aW9uKGZuLCBtZSl7IHJldHVybiBmdW5jdGlvbigpeyByZXR1cm4gZm4uYXBwbHkobWUsIGFyZ3VtZW50cyk7IH07IH0sXG4gICAgX19oYXNQcm9wID0ge30uaGFzT3duUHJvcGVydHksXG4gICAgX19leHRlbmRzID0gZnVuY3Rpb24oY2hpbGQsIHBhcmVudCkgeyBmb3IgKHZhciBrZXkgaW4gcGFyZW50KSB7IGlmIChfX2hhc1Byb3AuY2FsbChwYXJlbnQsIGtleSkpIGNoaWxkW2tleV0gPSBwYXJlbnRba2V5XTsgfSBmdW5jdGlvbiBjdG9yKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gY2hpbGQ7IH0gY3Rvci5wcm90b3R5cGUgPSBwYXJlbnQucHJvdG90eXBlOyBjaGlsZC5wcm90b3R5cGUgPSBuZXcgY3RvcigpOyBjaGlsZC5fX3N1cGVyX18gPSBwYXJlbnQucHJvdG90eXBlOyByZXR1cm4gY2hpbGQ7IH07XG5cbiAgRXZlbnRFbWl0dGVyID0gcmVxdWlyZSgnLi9jb3JlL2V2ZW50cycpO1xuXG4gIEFzc2V0ID0gcmVxdWlyZSgnLi9hc3NldCcpO1xuXG4gIFZvbHVtZUZpbHRlciA9IHJlcXVpcmUoJy4vZmlsdGVycy92b2x1bWUnKTtcblxuICBCYWxhbmNlRmlsdGVyID0gcmVxdWlyZSgnLi9maWx0ZXJzL2JhbGFuY2UnKTtcblxuICBRdWV1ZSA9IHJlcXVpcmUoJy4vcXVldWUnKTtcblxuICBBdWRpb0RldmljZSA9IHJlcXVpcmUoJy4vZGV2aWNlJyk7XG5cbiAgUGxheWVyID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICAgIF9fZXh0ZW5kcyhQbGF5ZXIsIF9zdXBlcik7XG5cbiAgICBmdW5jdGlvbiBQbGF5ZXIoYXNzZXQpIHtcbiAgICAgIHRoaXMuYXNzZXQgPSBhc3NldDtcbiAgICAgIHRoaXMuc3RhcnRQbGF5aW5nID0gX19iaW5kKHRoaXMuc3RhcnRQbGF5aW5nLCB0aGlzKTtcbiAgICAgIHRoaXMucGxheWluZyA9IGZhbHNlO1xuICAgICAgdGhpcy5idWZmZXJlZCA9IDA7XG4gICAgICB0aGlzLmN1cnJlbnRUaW1lID0gMDtcbiAgICAgIHRoaXMuZHVyYXRpb24gPSAwO1xuICAgICAgdGhpcy52b2x1bWUgPSAxMDA7XG4gICAgICB0aGlzLnBhbiA9IDA7XG4gICAgICB0aGlzLm1ldGFkYXRhID0ge307XG4gICAgICB0aGlzLmZpbHRlcnMgPSBbbmV3IFZvbHVtZUZpbHRlcih0aGlzLCAndm9sdW1lJyksIG5ldyBCYWxhbmNlRmlsdGVyKHRoaXMsICdwYW4nKV07XG4gICAgICB0aGlzLmFzc2V0Lm9uKCdidWZmZXInLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKGJ1ZmZlcmVkKSB7XG4gICAgICAgICAgX3RoaXMuYnVmZmVyZWQgPSBidWZmZXJlZDtcbiAgICAgICAgICByZXR1cm4gX3RoaXMuZW1pdCgnYnVmZmVyJywgX3RoaXMuYnVmZmVyZWQpO1xuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgICAgdGhpcy5hc3NldC5vbignZGVjb2RlU3RhcnQnLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICAgIF90aGlzLnF1ZXVlID0gbmV3IFF1ZXVlKF90aGlzLmFzc2V0KTtcbiAgICAgICAgICByZXR1cm4gX3RoaXMucXVldWUub25jZSgncmVhZHknLCBfdGhpcy5zdGFydFBsYXlpbmcpO1xuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgICAgdGhpcy5hc3NldC5vbignZm9ybWF0JywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbihmb3JtYXQpIHtcbiAgICAgICAgICBfdGhpcy5mb3JtYXQgPSBmb3JtYXQ7XG4gICAgICAgICAgcmV0dXJuIF90aGlzLmVtaXQoJ2Zvcm1hdCcsIF90aGlzLmZvcm1hdCk7XG4gICAgICAgIH07XG4gICAgICB9KSh0aGlzKSk7XG4gICAgICB0aGlzLmFzc2V0Lm9uKCdtZXRhZGF0YScsIChmdW5jdGlvbihfdGhpcykge1xuICAgICAgICByZXR1cm4gZnVuY3Rpb24obWV0YWRhdGEpIHtcbiAgICAgICAgICBfdGhpcy5tZXRhZGF0YSA9IG1ldGFkYXRhO1xuICAgICAgICAgIHJldHVybiBfdGhpcy5lbWl0KCdtZXRhZGF0YScsIF90aGlzLm1ldGFkYXRhKTtcbiAgICAgICAgfTtcbiAgICAgIH0pKHRoaXMpKTtcbiAgICAgIHRoaXMuYXNzZXQub24oJ2R1cmF0aW9uJywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbihkdXJhdGlvbikge1xuICAgICAgICAgIF90aGlzLmR1cmF0aW9uID0gZHVyYXRpb247XG4gICAgICAgICAgcmV0dXJuIF90aGlzLmVtaXQoJ2R1cmF0aW9uJywgX3RoaXMuZHVyYXRpb24pO1xuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgICAgdGhpcy5hc3NldC5vbignZXJyb3InLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKGVycm9yKSB7XG4gICAgICAgICAgcmV0dXJuIF90aGlzLmVtaXQoJ2Vycm9yJywgZXJyb3IpO1xuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgIH1cblxuICAgIFBsYXllci5mcm9tVVJMID0gZnVuY3Rpb24odXJsLCBvcHRzKSB7XG4gICAgICByZXR1cm4gbmV3IFBsYXllcihBc3NldC5mcm9tVVJMKHVybCwgb3B0cykpO1xuICAgIH07XG5cbiAgICBQbGF5ZXIuZnJvbUZpbGUgPSBmdW5jdGlvbihmaWxlKSB7XG4gICAgICByZXR1cm4gbmV3IFBsYXllcihBc3NldC5mcm9tRmlsZShmaWxlKSk7XG4gICAgfTtcblxuICAgIFBsYXllci5mcm9tQnVmZmVyID0gZnVuY3Rpb24oYnVmZmVyKSB7XG4gICAgICByZXR1cm4gbmV3IFBsYXllcihBc3NldC5mcm9tQnVmZmVyKGJ1ZmZlcikpO1xuICAgIH07XG5cbiAgICBQbGF5ZXIucHJvdG90eXBlLnByZWxvYWQgPSBmdW5jdGlvbigpIHtcbiAgICAgIGlmICghdGhpcy5hc3NldCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLnN0YXJ0ZWRQcmVsb2FkaW5nID0gdHJ1ZTtcbiAgICAgIHJldHVybiB0aGlzLmFzc2V0LnN0YXJ0KGZhbHNlKTtcbiAgICB9O1xuXG4gICAgUGxheWVyLnByb3RvdHlwZS5wbGF5ID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgX3JlZjtcbiAgICAgIGlmICh0aGlzLnBsYXlpbmcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKCF0aGlzLnN0YXJ0ZWRQcmVsb2FkaW5nKSB7XG4gICAgICAgIHRoaXMucHJlbG9hZCgpO1xuICAgICAgfVxuICAgICAgdGhpcy5wbGF5aW5nID0gdHJ1ZTtcbiAgICAgIHJldHVybiAoX3JlZiA9IHRoaXMuZGV2aWNlKSAhPSBudWxsID8gX3JlZi5zdGFydCgpIDogdm9pZCAwO1xuICAgIH07XG5cbiAgICBQbGF5ZXIucHJvdG90eXBlLnBhdXNlID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgX3JlZjtcbiAgICAgIGlmICghdGhpcy5wbGF5aW5nKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMucGxheWluZyA9IGZhbHNlO1xuICAgICAgcmV0dXJuIChfcmVmID0gdGhpcy5kZXZpY2UpICE9IG51bGwgPyBfcmVmLnN0b3AoKSA6IHZvaWQgMDtcbiAgICB9O1xuXG4gICAgUGxheWVyLnByb3RvdHlwZS50b2dnbGVQbGF5YmFjayA9IGZ1bmN0aW9uKCkge1xuICAgICAgaWYgKHRoaXMucGxheWluZykge1xuICAgICAgICByZXR1cm4gdGhpcy5wYXVzZSgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGxheSgpO1xuICAgICAgfVxuICAgIH07XG5cbiAgICBQbGF5ZXIucHJvdG90eXBlLnN0b3AgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBfcmVmO1xuICAgICAgdGhpcy5wYXVzZSgpO1xuICAgICAgdGhpcy5hc3NldC5zdG9wKCk7XG4gICAgICByZXR1cm4gKF9yZWYgPSB0aGlzLmRldmljZSkgIT0gbnVsbCA/IF9yZWYuZGVzdHJveSgpIDogdm9pZCAwO1xuICAgIH07XG5cbiAgICBQbGF5ZXIucHJvdG90eXBlLnNlZWsgPSBmdW5jdGlvbih0aW1lc3RhbXApIHtcbiAgICAgIHZhciBfcmVmO1xuICAgICAgaWYgKChfcmVmID0gdGhpcy5kZXZpY2UpICE9IG51bGwpIHtcbiAgICAgICAgX3JlZi5zdG9wKCk7XG4gICAgICB9XG4gICAgICB0aGlzLnF1ZXVlLm9uY2UoJ3JlYWR5JywgKGZ1bmN0aW9uKF90aGlzKSB7XG4gICAgICAgIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICAgICAgICB2YXIgX3JlZjEsIF9yZWYyO1xuICAgICAgICAgIGlmICgoX3JlZjEgPSBfdGhpcy5kZXZpY2UpICE9IG51bGwpIHtcbiAgICAgICAgICAgIF9yZWYxLnNlZWsoX3RoaXMuY3VycmVudFRpbWUpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoX3RoaXMucGxheWluZykge1xuICAgICAgICAgICAgcmV0dXJuIChfcmVmMiA9IF90aGlzLmRldmljZSkgIT0gbnVsbCA/IF9yZWYyLnN0YXJ0KCkgOiB2b2lkIDA7XG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgfSkodGhpcykpO1xuICAgICAgdGltZXN0YW1wID0gKHRpbWVzdGFtcCAvIDEwMDApICogdGhpcy5mb3JtYXQuc2FtcGxlUmF0ZTtcbiAgICAgIHRpbWVzdGFtcCA9IHRoaXMuYXNzZXQuZGVjb2Rlci5zZWVrKHRpbWVzdGFtcCk7XG4gICAgICB0aGlzLmN1cnJlbnRUaW1lID0gdGltZXN0YW1wIC8gdGhpcy5mb3JtYXQuc2FtcGxlUmF0ZSAqIDEwMDAgfCAwO1xuICAgICAgdGhpcy5xdWV1ZS5yZXNldCgpO1xuICAgICAgcmV0dXJuIHRoaXMuY3VycmVudFRpbWU7XG4gICAgfTtcblxuICAgIFBsYXllci5wcm90b3R5cGUuc3RhcnRQbGF5aW5nID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgZnJhbWUsIGZyYW1lT2Zmc2V0O1xuICAgICAgZnJhbWUgPSB0aGlzLnF1ZXVlLnJlYWQoKTtcbiAgICAgIGZyYW1lT2Zmc2V0ID0gMDtcbiAgICAgIHRoaXMuZGV2aWNlID0gbmV3IEF1ZGlvRGV2aWNlKHRoaXMuZm9ybWF0LnNhbXBsZVJhdGUsIHRoaXMuZm9ybWF0LmNoYW5uZWxzUGVyRnJhbWUpO1xuICAgICAgdGhpcy5kZXZpY2Uub24oJ3RpbWVVcGRhdGUnLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKGN1cnJlbnRUaW1lKSB7XG4gICAgICAgICAgX3RoaXMuY3VycmVudFRpbWUgPSBjdXJyZW50VGltZTtcbiAgICAgICAgICByZXR1cm4gX3RoaXMuZW1pdCgncHJvZ3Jlc3MnLCBfdGhpcy5jdXJyZW50VGltZSk7XG4gICAgICAgIH07XG4gICAgICB9KSh0aGlzKSk7XG4gICAgICB0aGlzLnJlZmlsbCA9IChmdW5jdGlvbihfdGhpcykge1xuICAgICAgICByZXR1cm4gZnVuY3Rpb24oYnVmZmVyKSB7XG4gICAgICAgICAgdmFyIGJ1ZmZlck9mZnNldCwgZmlsdGVyLCBpLCBtYXgsIF9pLCBfaiwgX2xlbiwgX3JlZjtcbiAgICAgICAgICBpZiAoIV90aGlzLnBsYXlpbmcpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKCFmcmFtZSkge1xuICAgICAgICAgICAgZnJhbWUgPSBfdGhpcy5xdWV1ZS5yZWFkKCk7XG4gICAgICAgICAgICBmcmFtZU9mZnNldCA9IDA7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJ1ZmZlck9mZnNldCA9IDA7XG4gICAgICAgICAgd2hpbGUgKGZyYW1lICYmIGJ1ZmZlck9mZnNldCA8IGJ1ZmZlci5sZW5ndGgpIHtcbiAgICAgICAgICAgIG1heCA9IE1hdGgubWluKGZyYW1lLmxlbmd0aCAtIGZyYW1lT2Zmc2V0LCBidWZmZXIubGVuZ3RoIC0gYnVmZmVyT2Zmc2V0KTtcbiAgICAgICAgICAgIGZvciAoaSA9IF9pID0gMDsgX2kgPCBtYXg7IGkgPSBfaSArPSAxKSB7XG4gICAgICAgICAgICAgIGJ1ZmZlcltidWZmZXJPZmZzZXQrK10gPSBmcmFtZVtmcmFtZU9mZnNldCsrXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChmcmFtZU9mZnNldCA9PT0gZnJhbWUubGVuZ3RoKSB7XG4gICAgICAgICAgICAgIGZyYW1lID0gX3RoaXMucXVldWUucmVhZCgpO1xuICAgICAgICAgICAgICBmcmFtZU9mZnNldCA9IDA7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIF9yZWYgPSBfdGhpcy5maWx0ZXJzO1xuICAgICAgICAgIGZvciAoX2ogPSAwLCBfbGVuID0gX3JlZi5sZW5ndGg7IF9qIDwgX2xlbjsgX2orKykge1xuICAgICAgICAgICAgZmlsdGVyID0gX3JlZltfal07XG4gICAgICAgICAgICBmaWx0ZXIucHJvY2VzcyhidWZmZXIpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoIWZyYW1lKSB7XG4gICAgICAgICAgICBpZiAoX3RoaXMucXVldWUuZW5kZWQpIHtcbiAgICAgICAgICAgICAgX3RoaXMuY3VycmVudFRpbWUgPSBfdGhpcy5kdXJhdGlvbjtcbiAgICAgICAgICAgICAgX3RoaXMuZW1pdCgncHJvZ3Jlc3MnLCBfdGhpcy5jdXJyZW50VGltZSk7XG4gICAgICAgICAgICAgIF90aGlzLmVtaXQoJ2VuZCcpO1xuICAgICAgICAgICAgICBfdGhpcy5zdG9wKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBfdGhpcy5kZXZpY2Uuc3RvcCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgIH0pKHRoaXMpO1xuICAgICAgdGhpcy5kZXZpY2Uub24oJ3JlZmlsbCcsIHRoaXMucmVmaWxsKTtcbiAgICAgIGlmICh0aGlzLnBsYXlpbmcpIHtcbiAgICAgICAgdGhpcy5kZXZpY2Uuc3RhcnQoKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLmVtaXQoJ3JlYWR5Jyk7XG4gICAgfTtcblxuICAgIFBsYXllci5wcm90b3R5cGUuZGVzdHJveSA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIF9yZWYsIF9yZWYxO1xuICAgICAgdGhpcy5zdG9wKCk7XG4gICAgICBpZiAoKF9yZWYgPSB0aGlzLmRldmljZSkgIT0gbnVsbCkge1xuICAgICAgICBfcmVmLm9mZigpO1xuICAgICAgfVxuICAgICAgaWYgKChfcmVmMSA9IHRoaXMuYXNzZXQpICE9IG51bGwpIHtcbiAgICAgICAgX3JlZjEuZGVzdHJveSgpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMub2ZmKCk7XG4gICAgfTtcblxuICAgIHJldHVybiBQbGF5ZXI7XG5cbiAgfSkoRXZlbnRFbWl0dGVyKTtcblxuICBtb2R1bGUuZXhwb3J0cyA9IFBsYXllcjtcblxufSkuY2FsbCh0aGlzKTtcbiIsIi8vIEdlbmVyYXRlZCBieSBDb2ZmZWVTY3JpcHQgMS43LjFcbihmdW5jdGlvbigpIHtcbiAgdmFyIEV2ZW50RW1pdHRlciwgUXVldWUsXG4gICAgX19iaW5kID0gZnVuY3Rpb24oZm4sIG1lKXsgcmV0dXJuIGZ1bmN0aW9uKCl7IHJldHVybiBmbi5hcHBseShtZSwgYXJndW1lbnRzKTsgfTsgfSxcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKCcuL2NvcmUvZXZlbnRzJyk7XG5cbiAgUXVldWUgPSAoZnVuY3Rpb24oX3N1cGVyKSB7XG4gICAgX19leHRlbmRzKFF1ZXVlLCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gUXVldWUoYXNzZXQpIHtcbiAgICAgIHRoaXMuYXNzZXQgPSBhc3NldDtcbiAgICAgIHRoaXMud3JpdGUgPSBfX2JpbmQodGhpcy53cml0ZSwgdGhpcyk7XG4gICAgICB0aGlzLnJlYWR5TWFyayA9IDY0O1xuICAgICAgdGhpcy5maW5pc2hlZCA9IGZhbHNlO1xuICAgICAgdGhpcy5idWZmZXJpbmcgPSB0cnVlO1xuICAgICAgdGhpcy5lbmRlZCA9IGZhbHNlO1xuICAgICAgdGhpcy5idWZmZXJzID0gW107XG4gICAgICB0aGlzLmFzc2V0Lm9uKCdkYXRhJywgdGhpcy53cml0ZSk7XG4gICAgICB0aGlzLmFzc2V0Lm9uKCdlbmQnLCAoZnVuY3Rpb24oX3RoaXMpIHtcbiAgICAgICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICAgIHJldHVybiBfdGhpcy5lbmRlZCA9IHRydWU7XG4gICAgICAgIH07XG4gICAgICB9KSh0aGlzKSk7XG4gICAgICB0aGlzLmFzc2V0LmRlY29kZVBhY2tldCgpO1xuICAgIH1cblxuICAgIFF1ZXVlLnByb3RvdHlwZS53cml0ZSA9IGZ1bmN0aW9uKGJ1ZmZlcikge1xuICAgICAgaWYgKGJ1ZmZlcikge1xuICAgICAgICB0aGlzLmJ1ZmZlcnMucHVzaChidWZmZXIpO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuYnVmZmVyaW5nKSB7XG4gICAgICAgIGlmICh0aGlzLmJ1ZmZlcnMubGVuZ3RoID49IHRoaXMucmVhZHlNYXJrIHx8IHRoaXMuZW5kZWQpIHtcbiAgICAgICAgICB0aGlzLmJ1ZmZlcmluZyA9IGZhbHNlO1xuICAgICAgICAgIHJldHVybiB0aGlzLmVtaXQoJ3JlYWR5Jyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuYXNzZXQuZGVjb2RlUGFja2V0KCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgUXVldWUucHJvdG90eXBlLnJlYWQgPSBmdW5jdGlvbigpIHtcbiAgICAgIGlmICh0aGlzLmJ1ZmZlcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgdGhpcy5hc3NldC5kZWNvZGVQYWNrZXQoKTtcbiAgICAgIHJldHVybiB0aGlzLmJ1ZmZlcnMuc2hpZnQoKTtcbiAgICB9O1xuXG4gICAgUXVldWUucHJvdG90eXBlLnJlc2V0ID0gZnVuY3Rpb24oKSB7XG4gICAgICB0aGlzLmJ1ZmZlcnMubGVuZ3RoID0gMDtcbiAgICAgIHRoaXMuYnVmZmVyaW5nID0gdHJ1ZTtcbiAgICAgIHJldHVybiB0aGlzLmFzc2V0LmRlY29kZVBhY2tldCgpO1xuICAgIH07XG5cbiAgICByZXR1cm4gUXVldWU7XG5cbiAgfSkoRXZlbnRFbWl0dGVyKTtcblxuICBtb2R1bGUuZXhwb3J0cyA9IFF1ZXVlO1xuXG59KS5jYWxsKHRoaXMpO1xuIiwiRXZlbnRFbWl0dGVyID0gcmVxdWlyZSAnLi4vLi4vY29yZS9ldmVudHMnXG5BVkJ1ZmZlciA9IHJlcXVpcmUgJy4uLy4uL2NvcmUvYnVmZmVyJ1xuXG5jbGFzcyBGaWxlU291cmNlIGV4dGVuZHMgRXZlbnRFbWl0dGVyXG4gICAgY29uc3RydWN0b3I6IChAZmlsZSkgLT5cbiAgICAgICAgaWYgbm90IEZpbGVSZWFkZXI/XG4gICAgICAgICAgICByZXR1cm4gQGVtaXQgJ2Vycm9yJywgJ1RoaXMgYnJvd3NlciBkb2VzIG5vdCBoYXZlIEZpbGVSZWFkZXIgc3VwcG9ydC4nXG4gICAgICAgIFxuICAgICAgICBAb2Zmc2V0ID0gMFxuICAgICAgICBAbGVuZ3RoID0gQGZpbGUuc2l6ZVxuICAgICAgICBAY2h1bmtTaXplID0gMSA8PCAyMFxuICAgICAgICBAZmlsZVtAc2xpY2UgPSAnc2xpY2UnXSBvciBAZmlsZVtAc2xpY2UgPSAnd2Via2l0U2xpY2UnXSBvciBAZmlsZVtAc2xpY2UgPSAnbW96U2xpY2UnXVxuICAgICAgICAgICAgXG4gICAgc3RhcnQ6IC0+XG4gICAgICAgIGlmIEByZWFkZXJcbiAgICAgICAgICAgIHJldHVybiBAbG9vcCgpIHVubGVzcyBAYWN0aXZlXG4gICAgICAgIFxuICAgICAgICBAcmVhZGVyID0gbmV3IEZpbGVSZWFkZXJcbiAgICAgICAgQGFjdGl2ZSA9IHRydWVcbiAgICAgICAgXG4gICAgICAgIEByZWFkZXIub25sb2FkID0gKGUpID0+XG4gICAgICAgICAgICBidWYgPSBuZXcgQVZCdWZmZXIobmV3IFVpbnQ4QXJyYXkoZS50YXJnZXQucmVzdWx0KSlcbiAgICAgICAgICAgIEBvZmZzZXQgKz0gYnVmLmxlbmd0aFxuICAgICAgICBcbiAgICAgICAgICAgIEBlbWl0ICdkYXRhJywgYnVmICAgXG4gICAgICAgICAgICBAYWN0aXZlID0gZmFsc2UgICAgIFxuICAgICAgICAgICAgQGxvb3AoKSBpZiBAb2Zmc2V0IDwgQGxlbmd0aFxuICAgICAgICBcbiAgICAgICAgQHJlYWRlci5vbmxvYWRlbmQgPSA9PlxuICAgICAgICAgICAgaWYgQG9mZnNldCBpcyBAbGVuZ3RoXG4gICAgICAgICAgICAgICAgQGVtaXQgJ2VuZCdcbiAgICAgICAgICAgICAgICBAcmVhZGVyID0gbnVsbFxuICAgICAgICBcbiAgICAgICAgQHJlYWRlci5vbmVycm9yID0gKGUpID0+XG4gICAgICAgICAgICBAZW1pdCAnZXJyb3InLCBlXG4gICAgICAgIFxuICAgICAgICBAcmVhZGVyLm9ucHJvZ3Jlc3MgPSAoZSkgPT5cbiAgICAgICAgICAgIEBlbWl0ICdwcm9ncmVzcycsIChAb2Zmc2V0ICsgZS5sb2FkZWQpIC8gQGxlbmd0aCAqIDEwMFxuICAgICAgICBcbiAgICAgICAgQGxvb3AoKVxuICAgICAgICBcbiAgICBsb29wOiAtPlxuICAgICAgICBAYWN0aXZlID0gdHJ1ZVxuICAgICAgICBlbmRQb3MgPSBNYXRoLm1pbihAb2Zmc2V0ICsgQGNodW5rU2l6ZSwgQGxlbmd0aClcbiAgICAgICAgXG4gICAgICAgIGJsb2IgPSBAZmlsZVtAc2xpY2VdKEBvZmZzZXQsIGVuZFBvcylcbiAgICAgICAgQHJlYWRlci5yZWFkQXNBcnJheUJ1ZmZlcihibG9iKVxuICAgICAgICBcbiAgICBwYXVzZTogLT5cbiAgICAgICAgQGFjdGl2ZSA9IGZhbHNlXG4gICAgICAgIHRyeVxuICAgICAgICAgIEByZWFkZXI/LmFib3J0KClcbiAgICAgICAgXG4gICAgcmVzZXQ6IC0+XG4gICAgICAgIEBwYXVzZSgpXG4gICAgICAgIEBvZmZzZXQgPSAwXG5cbm1vZHVsZS5leHBvcnRzID0gRmlsZVNvdXJjZVxuIiwiRXZlbnRFbWl0dGVyID0gcmVxdWlyZSAnLi4vLi4vY29yZS9ldmVudHMnXG5BVkJ1ZmZlciA9IHJlcXVpcmUgJy4uLy4uL2NvcmUvYnVmZmVyJ1xuXG5jbGFzcyBIVFRQU291cmNlIGV4dGVuZHMgRXZlbnRFbWl0dGVyXG4gICAgY29uc3RydWN0b3I6IChAdXJsLCBAb3B0cyA9IHt9KSAtPlxuICAgICAgICBAY2h1bmtTaXplID0gMSA8PCAyMFxuICAgICAgICBAaW5mbGlnaHQgPSBmYWxzZVxuICAgICAgICBpZiBAb3B0cy5sZW5ndGhcbiAgICAgICAgICAgIEBsZW5ndGggPSBAb3B0cy5sZW5ndGhcbiAgICAgICAgQHJlc2V0KClcbiAgICAgICAgXG4gICAgc3RhcnQ6IC0+XG4gICAgICAgIGlmIEBsZW5ndGhcbiAgICAgICAgICAgIHJldHVybiBAbG9vcCgpIHVubGVzcyBAaW5mbGlnaHRcbiAgICAgICAgXG4gICAgICAgIEBpbmZsaWdodCA9IHRydWVcbiAgICAgICAgQHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpXG4gICAgICAgIFxuICAgICAgICBAeGhyLm9ubG9hZCA9IChldmVudCkgPT5cbiAgICAgICAgICAgIEBsZW5ndGggPSBwYXJzZUludCBAeGhyLmdldFJlc3BvbnNlSGVhZGVyKFwiQ29udGVudC1MZW5ndGhcIikgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICBAaW5mbGlnaHQgPSBmYWxzZVxuICAgICAgICAgICAgQGxvb3AoKVxuICAgICAgICBcbiAgICAgICAgQHhoci5vbmVycm9yID0gKGVycikgPT5cbiAgICAgICAgICAgIEBwYXVzZSgpXG4gICAgICAgICAgICBAZW1pdCAnZXJyb3InLCBlcnJcbiAgICAgICAgICAgIFxuICAgICAgICBAeGhyLm9uYWJvcnQgPSAoZXZlbnQpID0+XG4gICAgICAgICAgICBAaW5mbGlnaHQgPSBmYWxzZVxuICAgICAgICBcbiAgICAgICAgQHhoci5vcGVuKFwiSEVBRFwiLCBAdXJsLCB0cnVlKVxuICAgICAgICBAeGhyLnNlbmQobnVsbClcbiAgICAgICAgXG4gICAgbG9vcDogLT5cbiAgICAgICAgaWYgQGluZmxpZ2h0IG9yIG5vdCBAbGVuZ3RoXG4gICAgICAgICAgICByZXR1cm4gQGVtaXQgJ2Vycm9yJywgJ1NvbWV0aGluZyBpcyB3cm9uZyBpbiBIVFRQU291cmNlLmxvb3AnXG4gICAgICAgICAgICBcbiAgICAgICAgQGluZmxpZ2h0ID0gdHJ1ZVxuICAgICAgICBAeGhyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KClcbiAgICAgICAgXG4gICAgICAgIEB4aHIub25sb2FkID0gKGV2ZW50KSA9PlxuICAgICAgICAgICAgaWYgQHhoci5yZXNwb25zZVxuICAgICAgICAgICAgICAgIGJ1ZiA9IG5ldyBVaW50OEFycmF5KEB4aHIucmVzcG9uc2UpXG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgdHh0ID0gQHhoci5yZXNwb25zZVRleHRcbiAgICAgICAgICAgICAgICBidWYgPSBuZXcgVWludDhBcnJheSh0eHQubGVuZ3RoKVxuICAgICAgICAgICAgICAgIGZvciBpIGluIFswLi4udHh0Lmxlbmd0aF1cbiAgICAgICAgICAgICAgICAgICAgYnVmW2ldID0gdHh0LmNoYXJDb2RlQXQoaSkgJiAweGZmXG5cbiAgICAgICAgICAgIGJ1ZmZlciA9IG5ldyBBVkJ1ZmZlcihidWYpXG4gICAgICAgICAgICBAb2Zmc2V0ICs9IGJ1ZmZlci5sZW5ndGhcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgQGVtaXQgJ2RhdGEnLCBidWZmZXJcbiAgICAgICAgICAgIEBlbWl0ICdlbmQnIGlmIEBvZmZzZXQgPj0gQGxlbmd0aFxuXG4gICAgICAgICAgICBAaW5mbGlnaHQgPSBmYWxzZVxuICAgICAgICAgICAgQGxvb3AoKSB1bmxlc3MgQG9mZnNldCA+PSBAbGVuZ3RoXG4gICAgICAgICAgICBcbiAgICAgICAgQHhoci5vbnByb2dyZXNzID0gKGV2ZW50KSA9PlxuICAgICAgICAgICAgQGVtaXQgJ3Byb2dyZXNzJywgKEBvZmZzZXQgKyBldmVudC5sb2FkZWQpIC8gQGxlbmd0aCAqIDEwMFxuXG4gICAgICAgIEB4aHIub25lcnJvciA9IChlcnIpID0+XG4gICAgICAgICAgICBAZW1pdCAnZXJyb3InLCBlcnJcbiAgICAgICAgICAgIEBwYXVzZSgpXG5cbiAgICAgICAgQHhoci5vbmFib3J0ID0gKGV2ZW50KSA9PlxuICAgICAgICAgICAgQGluZmxpZ2h0ID0gZmFsc2VcblxuICAgICAgICBAeGhyLm9wZW4oXCJHRVRcIiwgQHVybCwgdHJ1ZSlcbiAgICAgICAgQHhoci5yZXNwb25zZVR5cGUgPSBcImFycmF5YnVmZmVyXCJcblxuICAgICAgICBlbmRQb3MgPSBNYXRoLm1pbihAb2Zmc2V0ICsgQGNodW5rU2l6ZSwgQGxlbmd0aCAtIDEpXG4gICAgICAgIEB4aHIuc2V0UmVxdWVzdEhlYWRlcihcIklmLU5vbmUtTWF0Y2hcIiwgXCJ3ZWJraXQtbm8tY2FjaGVcIilcbiAgICAgICAgQHhoci5zZXRSZXF1ZXN0SGVhZGVyKFwiUmFuZ2VcIiwgXCJieXRlcz0je0BvZmZzZXR9LSN7ZW5kUG9zfVwiKVxuICAgICAgICBAeGhyLm92ZXJyaWRlTWltZVR5cGUoJ3RleHQvcGxhaW47IGNoYXJzZXQ9eC11c2VyLWRlZmluZWQnKVxuICAgICAgICBAeGhyLnNlbmQobnVsbClcbiAgICAgICAgXG4gICAgcGF1c2U6IC0+XG4gICAgICAgIEBpbmZsaWdodCA9IGZhbHNlXG4gICAgICAgIEB4aHI/LmFib3J0KClcbiAgICAgICAgXG4gICAgcmVzZXQ6IC0+XG4gICAgICAgIEBwYXVzZSgpXG4gICAgICAgIEBvZmZzZXQgPSAwXG4gICAgICAgIFxubW9kdWxlLmV4cG9ydHMgPSBIVFRQU291cmNlXG4iLCIvLyBHZW5lcmF0ZWQgYnkgQ29mZmVlU2NyaXB0IDEuNy4xXG4oZnVuY3Rpb24oKSB7XG4gIHZhciBBVkJ1ZmZlciwgQnVmZmVyTGlzdCwgQnVmZmVyU291cmNlLCBFdmVudEVtaXR0ZXIsXG4gICAgX19iaW5kID0gZnVuY3Rpb24oZm4sIG1lKXsgcmV0dXJuIGZ1bmN0aW9uKCl7IHJldHVybiBmbi5hcHBseShtZSwgYXJndW1lbnRzKTsgfTsgfSxcbiAgICBfX2hhc1Byb3AgPSB7fS5oYXNPd25Qcm9wZXJ0eSxcbiAgICBfX2V4dGVuZHMgPSBmdW5jdGlvbihjaGlsZCwgcGFyZW50KSB7IGZvciAodmFyIGtleSBpbiBwYXJlbnQpIHsgaWYgKF9faGFzUHJvcC5jYWxsKHBhcmVudCwga2V5KSkgY2hpbGRba2V5XSA9IHBhcmVudFtrZXldOyB9IGZ1bmN0aW9uIGN0b3IoKSB7IHRoaXMuY29uc3RydWN0b3IgPSBjaGlsZDsgfSBjdG9yLnByb3RvdHlwZSA9IHBhcmVudC5wcm90b3R5cGU7IGNoaWxkLnByb3RvdHlwZSA9IG5ldyBjdG9yKCk7IGNoaWxkLl9fc3VwZXJfXyA9IHBhcmVudC5wcm90b3R5cGU7IHJldHVybiBjaGlsZDsgfTtcblxuICBFdmVudEVtaXR0ZXIgPSByZXF1aXJlKCcuLi9jb3JlL2V2ZW50cycpO1xuXG4gIEJ1ZmZlckxpc3QgPSByZXF1aXJlKCcuLi9jb3JlL2J1ZmZlcmxpc3QnKTtcblxuICBBVkJ1ZmZlciA9IHJlcXVpcmUoJy4uL2NvcmUvYnVmZmVyJyk7XG5cbiAgQnVmZmVyU291cmNlID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICAgIHZhciBjbGVhckltbWVkaWF0ZSwgc2V0SW1tZWRpYXRlO1xuXG4gICAgX19leHRlbmRzKEJ1ZmZlclNvdXJjZSwgX3N1cGVyKTtcblxuICAgIGZ1bmN0aW9uIEJ1ZmZlclNvdXJjZShpbnB1dCkge1xuICAgICAgdGhpcy5sb29wID0gX19iaW5kKHRoaXMubG9vcCwgdGhpcyk7XG4gICAgICBpZiAoaW5wdXQgaW5zdGFuY2VvZiBCdWZmZXJMaXN0KSB7XG4gICAgICAgIHRoaXMubGlzdCA9IGlucHV0O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5saXN0ID0gbmV3IEJ1ZmZlckxpc3Q7XG4gICAgICAgIHRoaXMubGlzdC5hcHBlbmQobmV3IEFWQnVmZmVyKGlucHV0KSk7XG4gICAgICB9XG4gICAgICB0aGlzLnBhdXNlZCA9IHRydWU7XG4gICAgfVxuXG4gICAgc2V0SW1tZWRpYXRlID0gZ2xvYmFsLnNldEltbWVkaWF0ZSB8fCBmdW5jdGlvbihmbikge1xuICAgICAgcmV0dXJuIGdsb2JhbC5zZXRUaW1lb3V0KGZuLCAwKTtcbiAgICB9O1xuXG4gICAgY2xlYXJJbW1lZGlhdGUgPSBnbG9iYWwuY2xlYXJJbW1lZGlhdGUgfHwgZnVuY3Rpb24odGltZXIpIHtcbiAgICAgIHJldHVybiBnbG9iYWwuY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICB9O1xuXG4gICAgQnVmZmVyU291cmNlLnByb3RvdHlwZS5zdGFydCA9IGZ1bmN0aW9uKCkge1xuICAgICAgdGhpcy5wYXVzZWQgPSBmYWxzZTtcbiAgICAgIHJldHVybiB0aGlzLl90aW1lciA9IHNldEltbWVkaWF0ZSh0aGlzLmxvb3ApO1xuICAgIH07XG5cbiAgICBCdWZmZXJTb3VyY2UucHJvdG90eXBlLmxvb3AgPSBmdW5jdGlvbigpIHtcbiAgICAgIHRoaXMuZW1pdCgncHJvZ3Jlc3MnLCAodGhpcy5saXN0Lm51bUJ1ZmZlcnMgLSB0aGlzLmxpc3QuYXZhaWxhYmxlQnVmZmVycyArIDEpIC8gdGhpcy5saXN0Lm51bUJ1ZmZlcnMgKiAxMDAgfCAwKTtcbiAgICAgIHRoaXMuZW1pdCgnZGF0YScsIHRoaXMubGlzdC5maXJzdCk7XG4gICAgICBpZiAodGhpcy5saXN0LmFkdmFuY2UoKSkge1xuICAgICAgICByZXR1cm4gc2V0SW1tZWRpYXRlKHRoaXMubG9vcCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gdGhpcy5lbWl0KCdlbmQnKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgQnVmZmVyU291cmNlLnByb3RvdHlwZS5wYXVzZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgY2xlYXJJbW1lZGlhdGUodGhpcy5fdGltZXIpO1xuICAgICAgcmV0dXJuIHRoaXMucGF1c2VkID0gdHJ1ZTtcbiAgICB9O1xuXG4gICAgQnVmZmVyU291cmNlLnByb3RvdHlwZS5yZXNldCA9IGZ1bmN0aW9uKCkge1xuICAgICAgdGhpcy5wYXVzZSgpO1xuICAgICAgcmV0dXJuIHRoaXMubGlzdC5yZXdpbmQoKTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIEJ1ZmZlclNvdXJjZTtcblxuICB9KShFdmVudEVtaXR0ZXIpO1xuXG4gIG1vZHVsZS5leHBvcnRzID0gQnVmZmVyU291cmNlO1xuXG59KS5jYWxsKHRoaXMpO1xuIl19 diff --git a/webapp2/js/lib/cookie.js b/webapp2/js/lib/cookie.js deleted file mode 100644 index 12fa0ee..0000000 --- a/webapp2/js/lib/cookie.js +++ /dev/null @@ -1,156 +0,0 @@ -/*! - * JavaScript Cookie v2.1.3 - * https://github.com/js-cookie/js-cookie - * - * Copyright 2006, 2015 Klaus Hartl & Fagner Brack - * Released under the MIT license - */ -;(function (factory) { - var registeredInModuleLoader = false; - if (typeof define === 'function' && define.amd) { - define(factory); - registeredInModuleLoader = true; - } - if (typeof exports === 'object') { - module.exports = factory(); - registeredInModuleLoader = true; - } - if (!registeredInModuleLoader) { - var OldCookies = window.Cookies; - var api = window.Cookies = factory(); - api.noConflict = function () { - window.Cookies = OldCookies; - return api; - }; - } -}(function () { - function extend () { - var i = 0; - var result = {}; - for (; i < arguments.length; i++) { - var attributes = arguments[ i ]; - for (var key in attributes) { - result[key] = attributes[key]; - } - } - return result; - } - - function init (converter) { - function api (key, value, attributes) { - var result; - if (typeof document === 'undefined') { - return; - } - - // Write - - if (arguments.length > 1) { - attributes = extend({ - path: '/' - }, api.defaults, attributes); - - if (typeof attributes.expires === 'number') { - var expires = new Date(); - expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); - attributes.expires = expires; - } - - try { - result = JSON.stringify(value); - if (/^[\{\[]/.test(result)) { - value = result; - } - } catch (e) {} - - if (!converter.write) { - value = encodeURIComponent(String(value)) - .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); - } else { - value = converter.write(value, key); - } - - key = encodeURIComponent(String(key)); - key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); - key = key.replace(/[\(\)]/g, escape); - - return (document.cookie = [ - key, '=', value, - attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE - attributes.path ? '; path=' + attributes.path : '', - attributes.domain ? '; domain=' + attributes.domain : '', - attributes.secure ? '; secure' : '' - ].join('')); - } - - // Read - - if (!key) { - result = {}; - } - - // To prevent the for loop in the first place assign an empty array - // in case there are no cookies at all. Also prevents odd result when - // calling "get()" - var cookies = document.cookie ? document.cookie.split('; ') : []; - var rdecode = /(%[0-9A-Z]{2})+/g; - var i = 0; - - for (; i < cookies.length; i++) { - var parts = cookies[i].split('='); - var cookie = parts.slice(1).join('='); - - if (cookie.charAt(0) === '"') { - cookie = cookie.slice(1, -1); - } - - try { - var name = parts[0].replace(rdecode, decodeURIComponent); - cookie = converter.read ? - converter.read(cookie, name) : converter(cookie, name) || - cookie.replace(rdecode, decodeURIComponent); - - if (this.json) { - try { - cookie = JSON.parse(cookie); - } catch (e) {} - } - - if (key === name) { - result = cookie; - break; - } - - if (!key) { - result[name] = cookie; - } - } catch (e) {} - } - - return result; - } - - api.set = api; - api.get = function (key) { - return api.call(api, key); - }; - api.getJSON = function () { - return api.apply({ - json: true - }, [].slice.call(arguments)); - }; - api.defaults = {}; - - api.remove = function (key, attributes) { - api(key, '', extend(attributes, { - expires: -1 - })); - }; - - api.withConverter = init; - - return api; - } - - return init(function () {}); -})); diff --git a/webapp2/js/lib/flac.js b/webapp2/js/lib/flac.js deleted file mode 100644 index 6a35ecf..0000000 --- a/webapp2/js/lib/flac.js +++ /dev/null @@ -1,799 +0,0 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1; ones--) { - stream.advance(2); // == 2 - frame_or_sample_num = (frame_or_sample_num << 6) | stream.read(6); - } - - // block size - if (bsCode === 0) - throw new Error('Reserved blocksize code'); - else if (bsCode === 6) - this.blockSize = stream.read(8) + 1; - else if (bsCode === 7) - this.blockSize = stream.read(16) + 1; - else - this.blockSize = BLOCK_SIZES[bsCode]; - - // sample rate - var sampleRate; - if (srCode < 12) - sampleRate = SAMPLE_RATES[srCode]; - else if (srCode === 12) - sampleRate = stream.read(8) * 1000; - else if (srCode === 13) - sampleRate = stream.read(16); - else if (srCode === 14) - sampleRate = stream.read(16) * 10; - else - throw new Error('Invalid sample rate code'); - - stream.advance(8); // skip CRC check - - // subframes - for (var i = 0; i < channels; i++) - this.decodeSubframe(i); - - stream.align(); - stream.advance(16); // skip CRC frame footer - - var is32 = this.bps > 16, - output = new ArrayBuffer(this.blockSize * channels * (is32 ? 4 : 2)), - buf = is32 ? new Int32Array(output) : new Int16Array(output), - blockSize = this.blockSize, - decoded = this.decoded, - j = 0; - - switch (this.chMode) { - case CHMODE_INDEPENDENT: - for (var k = 0; k < blockSize; k++) { - for (var i = 0; i < channels; i++) { - buf[j++] = decoded[i][k]; - } - } - break; - - case CHMODE_LEFT_SIDE: - for (var i = 0; i < blockSize; i++) { - var left = decoded[0][i], - right = decoded[1][i]; - - buf[j++] = left; - buf[j++] = (left - right); - } - break; - - case CHMODE_RIGHT_SIDE: - for (var i = 0; i < blockSize; i++) { - var left = decoded[0][i], - right = decoded[1][i]; - - buf[j++] = (left + right); - buf[j++] = right; - } - break; - - case CHMODE_MID_SIDE: - for (var i = 0; i < blockSize; i++) { - var left = decoded[0][i], - right = decoded[1][i]; - - left -= right >> 1; - buf[j++] = (left + right); - buf[j++] = left; - } - break; - } - - return buf; - }; - - this.prototype.decodeSubframe = function(channel) { - var wasted = 0, - stream = this.bitstream, - blockSize = this.blockSize, - decoded = this.decoded; - - this.curr_bps = this.bps; - if (channel === 0) { - if (this.chMode === CHMODE_RIGHT_SIDE) - this.curr_bps++; - } else { - if (this.chMode === CHMODE_LEFT_SIDE || this.chMode === CHMODE_MID_SIDE) - this.curr_bps++; - } - - if (stream.read(1)) - throw new Error("Invalid subframe padding"); - - var type = stream.read(6); - - if (stream.read(1)) { - wasted = 1; - while (!stream.read(1)) - wasted++; - - this.curr_bps -= wasted; - } - - if (this.curr_bps > 32) - throw new Error("decorrelated bit depth > 32 (" + this.curr_bps + ")"); - - if (type === 0) { - var tmp = stream.read(this.curr_bps, true); - for (var i = 0; i < blockSize; i++) - decoded[channel][i] = tmp; - - } else if (type === 1) { - var bps = this.curr_bps; - for (var i = 0; i < blockSize; i++) - decoded[channel][i] = stream.read(bps, true); - - } else if ((type >= 8) && (type <= 12)) { - this.decode_subframe_fixed(channel, type & ~0x8); - - } else if (type >= 32) { - this.decode_subframe_lpc(channel, (type & ~0x20) + 1); - - } else { - throw new Error("Invalid coding type"); - } - - if (wasted) { - for (var i = 0; i < blockSize; i++) - decoded[channel][i] <<= wasted; - } - }; - - this.prototype.decode_subframe_fixed = function(channel, predictor_order) { - var decoded = this.decoded[channel], - stream = this.bitstream, - bps = this.curr_bps; - - // warm up samples - for (var i = 0; i < predictor_order; i++) - decoded[i] = stream.read(bps, true); - - this.decode_residuals(channel, predictor_order); - - var a = 0, b = 0, c = 0, d = 0; - - if (predictor_order > 0) - a = decoded[predictor_order - 1]; - - if (predictor_order > 1) - b = a - decoded[predictor_order - 2]; - - if (predictor_order > 2) - c = b - decoded[predictor_order - 2] + decoded[predictor_order - 3]; - - if (predictor_order > 3) - d = c - decoded[predictor_order - 2] + 2 * decoded[predictor_order - 3] - decoded[predictor_order - 4]; - - switch (predictor_order) { - case 0: - break; - - case 1: - case 2: - case 3: - case 4: - var abcd = new Int32Array([a, b, c, d]), - blockSize = this.blockSize; - - for (var i = predictor_order; i < blockSize; i++) { - abcd[predictor_order - 1] += decoded[i]; - - for (var j = predictor_order - 2; j >= 0; j--) { - abcd[j] += abcd[j + 1]; - } - - decoded[i] = abcd[0]; - } - - break; - - default: - throw new Error("Invalid Predictor Order " + predictor_order); - } - }; - - this.prototype.decode_subframe_lpc = function(channel, predictor_order) { - var stream = this.bitstream, - decoded = this.decoded[channel], - bps = this.curr_bps, - blockSize = this.blockSize; - - // warm up samples - for (var i = 0; i < predictor_order; i++) { - decoded[i] = stream.read(bps, true); - } - - var coeff_prec = stream.read(4) + 1; - if (coeff_prec === 16) - throw new Error("Invalid coefficient precision"); - - var qlevel = stream.read(5, true); - if (qlevel < 0) - throw new Error("Negative qlevel, maybe buggy stream"); - - var coeffs = new Int32Array(32); - for (var i = 0; i < predictor_order; i++) { - coeffs[i] = stream.read(coeff_prec, true); - } - - this.decode_residuals(channel, predictor_order); - - if (this.bps <= 16) { - for (var i = predictor_order; i < blockSize - 1; i += 2) { - var d = decoded[i - predictor_order], - s0 = 0, s1 = 0, c = 0; - - for (var j = predictor_order - 1; j > 0; j--) { - c = coeffs[j]; - s0 += c * d; - d = decoded[i - j]; - s1 += c * d; - } - - c = coeffs[0]; - s0 += c * d; - d = decoded[i] += (s0 >> qlevel); - s1 += c * d; - decoded[i + 1] += (s1 >> qlevel); - } - - if (i < blockSize) { - var sum = 0; - for (var j = 0; j < predictor_order; j++) - sum += coeffs[j] * decoded[i - j - 1]; - - decoded[i] += (sum >> qlevel); - } - } else { - // simulate 64 bit integer using an array of two 32 bit ints - var total = this.lpc_total; - for (var i = predictor_order; i < blockSize; i++) { - // reset total to 0 - total[0] = 0; - total[1] = 0; - - for (j = 0; j < predictor_order; j++) { - // simulate `total += coeffs[j] * decoded[i - j - 1]` - multiply_add(total, coeffs[j], decoded[i - j - 1]); - } - - // simulate `decoded[i] += total >> qlevel` - // we know that qlevel < 32 since it is a 5 bit field (see above) - decoded[i] += (total[0] >>> qlevel) | (total[1] << (32 - qlevel)); - } - } - }; - - const TWO_PWR_32_DBL = Math.pow(2, 32); - - // performs `total += a * b` on a simulated 64 bit int - // total is an Int32Array(2) - // a and b are JS numbers (32 bit ints) - function multiply_add(total, a, b) { - // multiply a * b (we can use normal JS multiplication for this) - var r = a * b; - var n = r < 0; - if (n) - r = -r; - - var r_low = (r % TWO_PWR_32_DBL) | 0; - var r_high = (r / TWO_PWR_32_DBL) | 0; - if (n) { - r_low = ~r_low + 1; - r_high = ~r_high; - } - - // add result to total - var a48 = total[1] >>> 16; - var a32 = total[1] & 0xFFFF; - var a16 = total[0] >>> 16; - var a00 = total[0] & 0xFFFF; - - var b48 = r_high >>> 16; - var b32 = r_high & 0xFFFF; - var b16 = r_low >>> 16; - var b00 = r_low & 0xFFFF; - - var c48 = 0, c32 = 0, c16 = 0, c00 = 0; - c00 += a00 + b00; - c16 += c00 >>> 16; - c00 &= 0xFFFF; - c16 += a16 + b16; - c32 += c16 >>> 16; - c16 &= 0xFFFF; - c32 += a32 + b32; - c48 += c32 >>> 16; - c32 &= 0xFFFF; - c48 += a48 + b48; - c48 &= 0xFFFF; - - // store result back in total - total[0] = (c16 << 16) | c00; - total[1] = (c48 << 16) | c32; - } - - const INT_MAX = 32767; - - this.prototype.decode_residuals = function(channel, predictor_order) { - var stream = this.bitstream, - method_type = stream.read(2); - - if (method_type > 1) - throw new Error('Illegal residual coding method ' + method_type); - - var rice_order = stream.read(4), - samples = (this.blockSize >>> rice_order); - - if (predictor_order > samples) - throw new Error('Invalid predictor order ' + predictor_order + ' > ' + samples); - - var decoded = this.decoded[channel], - sample = predictor_order, - i = predictor_order; - - for (var partition = 0; partition < (1 << rice_order); partition++) { - var tmp = stream.read(method_type === 0 ? 4 : 5); - - if (tmp === (method_type === 0 ? 15 : 31)) { - tmp = stream.read(5); - for (; i < samples; i++) - decoded[sample++] = stream.read(tmp, true); - - } else { - for (; i < samples; i++) - decoded[sample++] = this.golomb(tmp, INT_MAX, 0); - } - - i = 0; - } - }; - - const MIN_CACHE_BITS = 25; - - this.prototype.golomb = function(k, limit, esc_len) { - var data = this.bitstream, - offset = data.bitPosition, - buf = data.peek(32 - offset) << offset, - v = 0; - - var log = 31 - clz(buf | 1); // log2(buf) - - if (log - k >= 32 - MIN_CACHE_BITS && 32 - log < limit) { - buf >>>= log - k; - buf += (30 - log) << k; - - data.advance(32 + k - log); - v = buf; - - } else { - for (var i = 0; data.read(1) === 0; i++) - buf = data.peek(32 - offset) << offset; - - if (i < limit - 1) { - if (k) - buf = data.read(k); - else - buf = 0; - - v = buf + (i << k); - - } else if (i === limit - 1) { - buf = data.read(esc_len); - v = buf + 1; - - } else { - v = -1; - } - } - - return (v >> 1) ^ -(v & 1); - }; - - // Should be in the damned standard library... - function clz(input) { - var output = 0, - curbyte = 0; - - while(true) { // emulate goto in JS using the break statement :D - curbyte = input >>> 24; - if (curbyte) break; - output += 8; - - curbyte = input >>> 16; - if (curbyte & 0xff) break; - output += 8; - - curbyte = input >>> 8; - if (curbyte & 0xff) break; - output += 8; - - curbyte = input; - if (curbyte & 0xff) break; - output += 8; - - return output; - } - - if (!(curbyte & 0xf0)) - output += 4; - else - curbyte >>>= 4; - - if (curbyte & 0x8) - return output; - - if (curbyte & 0x4) - return output + 1; - - if (curbyte & 0x2) - return output + 2; - - if (curbyte & 0x1) - return output + 3; - - // shouldn't get here - return output + 4; - } -}); - -module.exports = FLACDecoder; - -}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],3:[function(require,module,exports){ -(function (global){ -/* - * FLAC.js - Free Lossless Audio Codec decoder in JavaScript - * By Devon Govett and Jens Nockert of Official.fm Labs - * - * FLAC.js is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FLAC.js is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - */ - -var AV = (typeof window !== "undefined" ? window['AV'] : typeof global !== "undefined" ? global['AV'] : null); - -var FLACDemuxer = AV.Demuxer.extend(function() { - AV.Demuxer.register(this); - - this.probe = function(buffer) { - return buffer.peekString(0, 4) === 'fLaC'; - } - - const STREAMINFO = 0, - PADDING = 1, - APPLICATION = 2, - SEEKTABLE = 3, - VORBIS_COMMENT = 4, - CUESHEET = 5, - PICTURE = 6, - INVALID = 127, - STREAMINFO_SIZE = 34; - - this.prototype.readChunk = function() { - var stream = this.stream; - - if (!this.readHeader && stream.available(4)) { - if (stream.readString(4) !== 'fLaC') - return this.emit('error', 'Invalid FLAC file.'); - - this.readHeader = true; - } - - while (stream.available(1) && !this.last) { - if (!this.readBlockHeaders) { - var tmp = stream.readUInt8(); - this.last = (tmp & 0x80) === 0x80, - this.type = tmp & 0x7F, - this.size = stream.readUInt24(); - } - - if (!this.foundStreamInfo && this.type !== STREAMINFO) - return this.emit('error', 'STREAMINFO must be the first block'); - - if (!stream.available(this.size)) - return; - - switch (this.type) { - case STREAMINFO: - if (this.foundStreamInfo) - return this.emit('error', 'STREAMINFO can only occur once.'); - - if (this.size !== STREAMINFO_SIZE) - return this.emit('error', 'STREAMINFO size is wrong.'); - - this.foundStreamInfo = true; - var bitstream = new AV.Bitstream(stream); - - var cookie = { - minBlockSize: bitstream.read(16), - maxBlockSize: bitstream.read(16), - minFrameSize: bitstream.read(24), - maxFrameSize: bitstream.read(24) - }; - - this.format = { - formatID: 'flac', - sampleRate: bitstream.read(20), - channelsPerFrame: bitstream.read(3) + 1, - bitsPerChannel: bitstream.read(5) + 1 - }; - - this.emit('format', this.format); - this.emit('cookie', cookie); - - var sampleCount = bitstream.read(36); - this.emit('duration', sampleCount / this.format.sampleRate * 1000 | 0); - - stream.advance(16); // skip MD5 hashes - this.readBlockHeaders = false; - break; - - /* - I am only looking at the least significant 32 bits of sample number and offset data - This is more than sufficient for the longest flac file I have (~50 mins 2-channel 16-bit 44.1k which uses about 7.5% of the UInt32 space for the largest offset) - Can certainly be improved by storing sample numbers and offests as doubles, but would require additional overriding of the searchTimestamp and seek functions (possibly more?) - Also the flac faq suggests it would be possible to find frame lengths and thus create seek points on the fly via decoding but I assume this would be slow - I may look into these thigns though as my project progresses - */ - case SEEKTABLE: - for(var s=0; s 0) - { - this.emit('error', 'Seek points with sample number >UInt32 not supported'); - } - var samplenum = stream.readUInt32(); - if(stream.readUInt32() > 0) - { - this.emit('error', 'Seek points with stream offset >UInt32 not supported'); - } - var offset = stream.readUInt32(); - - stream.advance(2); - - this.addSeekPoint(offset, samplenum); - } - } - break; - - case VORBIS_COMMENT: - // see http://www.xiph.org/vorbis/doc/v-comment.html - this.metadata || (this.metadata = {}); - var len = stream.readUInt32(true); - - this.metadata.vendor = stream.readString(len); - var length = stream.readUInt32(true); - - for (var i = 0; i < length; i++) { - len = stream.readUInt32(true); - var str = stream.readString(len, 'utf8'), - idx = str.indexOf('='); - - this.metadata[str.slice(0, idx).toLowerCase()] = str.slice(idx + 1); - } - - // TODO: standardize field names across formats - break; - - case PICTURE: - var type = stream.readUInt32(); - if (type !== 3) { // make sure this is album art (type 3) - stream.advance(this.size - 4); - } else { - var mimeLen = stream.readUInt32(), - mime = stream.readString(mimeLen), - descLen = stream.readUInt32(), - description = stream.readString(descLen), - width = stream.readUInt32(), - height = stream.readUInt32(), - depth = stream.readUInt32(), - colors = stream.readUInt32(), - length = stream.readUInt32(), - picture = stream.readBuffer(length); - - this.metadata || (this.metadata = {}); - this.metadata.coverArt = picture; - } - - // does anyone want the rest of the info? - break; - - default: - stream.advance(this.size); - this.readBlockHeaders = false; - } - - if (this.last && this.metadata) - this.emit('metadata', this.metadata); - } - - while (stream.available(1) && this.last) { - var buffer = stream.readSingleBuffer(stream.remainingBytes()); - this.emit('data', buffer); - } - } - -}); - -module.exports = FLACDemuxer; - -}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],4:[function(require,module,exports){ -(function (global){ -var AV = (typeof window !== "undefined" ? window['AV'] : typeof global !== "undefined" ? global['AV'] : null); - -// if ogg.js exists, register a plugin -try { - var OggDemuxer = (typeof window !== "undefined" ? window['AV']['OggDemuxer'] : typeof global !== "undefined" ? global['AV']['OggDemuxer'] : null); -} catch (e) {}; -if (!OggDemuxer) return; - -OggDemuxer.plugins.push({ - magic: "\177FLAC", - - init: function() { - this.list = new AV.BufferList(); - this.stream = new AV.Stream(this.list); - }, - - readHeaders: function(packet) { - var stream = this.stream; - this.list.append(new AV.Buffer(packet)); - - stream.advance(5); // magic - if (stream.readUInt8() != 1) - throw new Error('Unsupported FLAC version'); - - stream.advance(3); - if (stream.peekString(0, 4) != 'fLaC') - throw new Error('Not flac'); - - this.flac = AV.Demuxer.find(stream.peekSingleBuffer(0, stream.remainingBytes())); - if (!this.flac) - throw new Error('Flac demuxer not found'); - - this.flac.prototype.readChunk.call(this); - return true; - }, - - readPacket: function(packet) { - this.list.append(new AV.Buffer(packet)); - this.flac.prototype.readChunk.call(this); - } -}); - -}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}]},{},[1]) - - -//# sourceMappingURL=flac.js.map \ No newline at end of file diff --git a/webapp2/js/lib/howler.core.js b/webapp2/js/lib/howler.core.js deleted file mode 100644 index 2c811a6..0000000 --- a/webapp2/js/lib/howler.core.js +++ /dev/null @@ -1,2194 +0,0 @@ -/*! - * howler.js v2.0.3 - * howlerjs.com - * - * (c) 2013-2017, James Simpson of GoldFire Studios - * goldfirestudios.com - * - * MIT License - */ - -(function() { - - 'use strict'; - - /** Global Methods **/ - /***************************************************************************/ - - /** - * Create the global controller. All contained methods and properties apply - * to all sounds that are currently playing or will be in the future. - */ - var HowlerGlobal = function() { - this.init(); - }; - HowlerGlobal.prototype = { - /** - * Initialize the global Howler object. - * @return {Howler} - */ - init: function() { - var self = this || Howler; - - // Create a global ID counter. - self._counter = 0; - - // Internal properties. - self._codecs = {}; - self._howls = []; - self._muted = false; - self._volume = 1; - self._canPlayEvent = 'canplaythrough'; - self._navigator = (typeof window !== 'undefined' && window.navigator) ? window.navigator : null; - - // Public properties. - self.masterGain = null; - self.noAudio = false; - self.usingWebAudio = true; - self.autoSuspend = true; - self.ctx = null; - - // Set to false to disable the auto iOS enabler. - self.mobileAutoEnable = true; - - // Setup the various state values for global tracking. - self._setup(); - - return self; - }, - - /** - * Get/set the global volume for all sounds. - * @param {Float} vol Volume from 0.0 to 1.0. - * @return {Howler/Float} Returns self or current volume. - */ - volume: function(vol) { - var self = this || Howler; - vol = parseFloat(vol); - - // If we don't have an AudioContext created yet, run the setup. - if (!self.ctx) { - setupAudioContext(); - } - - if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) { - self._volume = vol; - - // Don't update any of the nodes if we are muted. - if (self._muted) { - return self; - } - - // When using Web Audio, we just need to adjust the master gain. - if (self.usingWebAudio) { - self.masterGain.gain.value = vol; - } - - // Loop through and change volume for all HTML5 audio nodes. - for (var i=0; i=0; i--) { - self._howls[i].unload(); - } - - // Create a new AudioContext to make sure it is fully reset. - if (self.usingWebAudio && self.ctx && typeof self.ctx.close !== 'undefined') { - self.ctx.close(); - self.ctx = null; - setupAudioContext(); - } - - return self; - }, - - /** - * Check for codec support of specific extension. - * @param {String} ext Audio file extention. - * @return {Boolean} - */ - codecs: function(ext) { - return (this || Howler)._codecs[ext.replace(/^x-/, '')]; - }, - - /** - * Setup various state values for global tracking. - * @return {Howler} - */ - _setup: function() { - var self = this || Howler; - - // Keeps track of the suspend/resume state of the AudioContext. - self.state = self.ctx ? self.ctx.state || 'running' : 'running'; - - // Automatically begin the 30-second suspend process - self._autoSuspend(); - - // Check if audio is available. - if (!self.usingWebAudio) { - // No audio is available on this system if noAudio is set to true. - if (typeof Audio !== 'undefined') { - try { - var test = new Audio(); - - // Check if the canplaythrough event is available. - if (typeof test.oncanplaythrough === 'undefined') { - self._canPlayEvent = 'canplay'; - } - } catch(e) { - self.noAudio = true; - } - } else { - self.noAudio = true; - } - } - - // Test to make sure audio isn't disabled in Internet Explorer. - try { - var test = new Audio(); - if (test.muted) { - self.noAudio = true; - } - } catch (e) {} - - // Check for supported codecs. - if (!self.noAudio) { - self._setupCodecs(); - } - - return self; - }, - - /** - * Check for browser support for various codecs and cache the results. - * @return {Howler} - */ - _setupCodecs: function() { - var self = this || Howler; - var audioTest = null; - - // Must wrap in a try/catch because IE11 in server mode throws an error. - try { - audioTest = (typeof Audio !== 'undefined') ? new Audio() : null; - } catch (err) { - return self; - } - - if (!audioTest || typeof audioTest.canPlayType !== 'function') { - return self; - } - - var mpegTest = audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''); - - // Opera version <33 has mixed MP3 support, so we need to check for and block it. - var checkOpera = self._navigator && self._navigator.userAgent.match(/OPR\/([0-6].)/g); - var isOldOpera = (checkOpera && parseInt(checkOpera[0].split('/')[1], 10) < 33); - - self._codecs = { - mp3: !!(!isOldOpera && (mpegTest || audioTest.canPlayType('audio/mp3;').replace(/^no$/, ''))), - mpeg: !!mpegTest, - opus: !!audioTest.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''), - ogg: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''), - oga: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''), - wav: !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''), - aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''), - caf: !!audioTest.canPlayType('audio/x-caf;').replace(/^no$/, ''), - m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''), - mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''), - weba: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''), - webm: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''), - dolby: !!audioTest.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/, ''), - flac: !!(audioTest.canPlayType('audio/x-flac;') || audioTest.canPlayType('audio/flac;')).replace(/^no$/, '') - }; - - return self; - }, - - /** - * Mobile browsers will only allow audio to be played after a user interaction. - * Attempt to automatically unlock audio on the first user interaction. - * Concept from: http://paulbakaus.com/tutorials/html5/web-audio-on-ios/ - * @return {Howler} - */ - _enableMobileAudio: function() { - var self = this || Howler; - - // Only run this on mobile devices if audio isn't already eanbled. - var isMobile = /iPhone|iPad|iPod|Android|BlackBerry|BB10|Silk|Mobi/i.test(self._navigator && self._navigator.userAgent); - var isTouch = !!(('ontouchend' in window) || (self._navigator && self._navigator.maxTouchPoints > 0) || (self._navigator && self._navigator.msMaxTouchPoints > 0)); - if (self._mobileEnabled || !self.ctx || (!isMobile && !isTouch)) { - return; - } - - self._mobileEnabled = false; - - // Some mobile devices/platforms have distortion issues when opening/closing tabs and/or web views. - // Bugs in the browser (especially Mobile Safari) can cause the sampleRate to change from 44100 to 48000. - // By calling Howler.unload(), we create a new AudioContext with the correct sampleRate. - if (!self._mobileUnloaded && self.ctx.sampleRate !== 44100) { - self._mobileUnloaded = true; - self.unload(); - } - - // Scratch buffer for enabling iOS to dispose of web audio buffers correctly, as per: - // http://stackoverflow.com/questions/24119684 - self._scratchBuffer = self.ctx.createBuffer(1, 1, 22050); - - // Call this method on touch start to create and play a buffer, - // then check if the audio actually played to determine if - // audio has now been unlocked on iOS, Android, etc. - var unlock = function() { - // Create an empty buffer. - var source = self.ctx.createBufferSource(); - source.buffer = self._scratchBuffer; - source.connect(self.ctx.destination); - - // Play the empty buffer. - if (typeof source.start === 'undefined') { - source.noteOn(0); - } else { - source.start(0); - } - - // Setup a timeout to check that we are unlocked on the next event loop. - source.onended = function() { - source.disconnect(0); - - // Update the unlocked state and prevent this check from happening again. - self._mobileEnabled = true; - self.mobileAutoEnable = false; - - // Remove the touch start listener. - document.removeEventListener('touchend', unlock, true); - }; - }; - - // Setup a touch start listener to attempt an unlock in. - document.addEventListener('touchend', unlock, true); - - return self; - }, - - /** - * Automatically suspend the Web Audio AudioContext after no sound has played for 30 seconds. - * This saves processing/energy and fixes various browser-specific bugs with audio getting stuck. - * @return {Howler} - */ - _autoSuspend: function() { - var self = this; - - if (!self.autoSuspend || !self.ctx || typeof self.ctx.suspend === 'undefined' || !Howler.usingWebAudio) { - return; - } - - // Check if any sounds are playing. - for (var i=0; i 0 ? sound._seek : self._sprite[sprite][0] / 1000); - var duration = Math.max(0, ((self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000) - seek); - var timeout = (duration * 1000) / Math.abs(sound._rate); - - // Update the parameters of the sound - sound._paused = false; - sound._ended = false; - sound._sprite = sprite; - sound._seek = seek; - sound._start = self._sprite[sprite][0] / 1000; - sound._stop = (self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000; - sound._loop = !!(sound._loop || self._sprite[sprite][2]); - - // Begin the actual playback. - var node = sound._node; - if (self._webAudio) { - // Fire this when the sound is ready to play to begin Web Audio playback. - var playWebAudio = function() { - self._refreshBuffer(sound); - - // Setup the playback params. - var vol = (sound._muted || self._muted) ? 0 : sound._volume; - node.gain.setValueAtTime(vol, Howler.ctx.currentTime); - sound._playStart = Howler.ctx.currentTime; - - // Play the sound using the supported method. - if (typeof node.bufferSource.start === 'undefined') { - sound._loop ? node.bufferSource.noteGrainOn(0, seek, 86400) : node.bufferSource.noteGrainOn(0, seek, duration); - } else { - sound._loop ? node.bufferSource.start(0, seek, 86400) : node.bufferSource.start(0, seek, duration); - } - - // Start a new timer if none is present. - if (timeout !== Infinity) { - self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout); - } - - if (!internal) { - setTimeout(function() { - self._emit('play', sound._id); - }, 0); - } - }; - - var isRunning = (Howler.state === 'running'); - if (self._state === 'loaded' && isRunning) { - playWebAudio(); - } else { - // Wait for the audio to load and then begin playback. - var event = !isRunning && self._state === 'loaded' ? 'resume' : 'load'; - self.once(event, playWebAudio, isRunning ? sound._id : null); - - // Cancel the end timer. - self._clearTimer(sound._id); - } - } else { - // Fire this when the sound is ready to play to begin HTML5 Audio playback. - var playHtml5 = function() { - node.currentTime = seek; - node.muted = sound._muted || self._muted || Howler._muted || node.muted; - node.volume = sound._volume * Howler.volume(); - node.playbackRate = sound._rate; - node.play(); - - // Setup the new end timer. - if (timeout !== Infinity) { - self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout); - } - - if (!internal) { - self._emit('play', sound._id); - } - }; - - // Play immediately if ready, or wait for the 'canplaythrough'e vent. - var loadedNoReadyState = (self._state === 'loaded' && (window && window.ejecta || !node.readyState && Howler._navigator.isCocoonJS)); - if (node.readyState === 4 || loadedNoReadyState) { - playHtml5(); - } else { - var listener = function() { - // Begin playback. - playHtml5(); - - // Clear this listener. - node.removeEventListener(Howler._canPlayEvent, listener, false); - }; - node.addEventListener(Howler._canPlayEvent, listener, false); - - // Cancel the end timer. - self._clearTimer(sound._id); - } - } - - return sound._id; - }, - - /** - * Pause playback and save current position. - * @param {Number} id The sound ID (empty to pause all in group). - * @return {Howl} - */ - pause: function(id) { - var self = this; - - // If the sound hasn't loaded, add it to the load queue to pause when capable. - if (self._state !== 'loaded') { - self._queue.push({ - event: 'pause', - action: function() { - self.pause(id); - } - }); - - return self; - } - - // If no id is passed, get all ID's to be paused. - var ids = self._getSoundIds(id); - - for (var i=0; i Returns the group's volume value. - * volume(id) -> Returns the sound id's current volume. - * volume(vol) -> Sets the volume of all sounds in this Howl group. - * volume(vol, id) -> Sets the volume of passed sound id. - * @return {Howl/Number} Returns self or current volume. - */ - volume: function() { - var self = this; - var args = arguments; - var vol, id; - - // Determine the values based on arguments. - if (args.length === 0) { - // Return the value of the groups' volume. - return self._volume; - } else if (args.length === 1 || args.length === 2 && typeof args[1] === 'undefined') { - // First check if this is an ID, and if not, assume it is a new volume. - var ids = self._getSoundIds(); - var index = ids.indexOf(args[0]); - if (index >= 0) { - id = parseInt(args[0], 10); - } else { - vol = parseFloat(args[0]); - } - } else if (args.length >= 2) { - vol = parseFloat(args[0]); - id = parseInt(args[1], 10); - } - - // Update the volume or return the current volume. - var sound; - if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) { - // If the sound hasn't loaded, add it to the load queue to change volume when capable. - if (self._state !== 'loaded') { - self._queue.push({ - event: 'volume', - action: function() { - self.volume.apply(self, args); - } - }); - - return self; - } - - // Set the group volume. - if (typeof id === 'undefined') { - self._volume = vol; - } - - // Update one or all volumes. - id = self._getSoundIds(id); - for (var i=0; i to ? 'out' : 'in'; - var steps = diff / 0.01; - var stepLen = (steps > 0) ? len / steps : len; - - // Since browsers clamp timeouts to 4ms, we need to clamp our steps to that too. - if (stepLen < 4) { - steps = Math.ceil(steps / (4 / stepLen)); - stepLen = 4; - } - - // If the sound hasn't loaded, add it to the load queue to fade when capable. - if (self._state !== 'loaded') { - self._queue.push({ - event: 'fade', - action: function() { - self.fade(from, to, len, id); - } - }); - - return self; - } - - // Set the volume to the start position. - self.volume(from, id); - - // Fade the volume of one or all sounds. - var ids = self._getSoundIds(id); - for (var i=0; i 0) { - vol += (dir === 'in' ? 0.01 : -0.01); - } - - // Make sure the volume is in the right bounds. - vol = Math.max(0, vol); - vol = Math.min(1, vol); - - // Round to within 2 decimal points. - vol = Math.round(vol * 100) / 100; - - // Change the volume. - if (self._webAudio) { - if (typeof id === 'undefined') { - self._volume = vol; - } - - sound._volume = vol; - } else { - self.volume(vol, soundId, true); - } - - // When the fade is complete, stop it and fire event. - if ((to < from && vol <= to) || (to > from && vol >= to)) { - clearInterval(sound._interval); - sound._interval = null; - self.volume(to, soundId); - self._emit('fade', soundId); - } - }.bind(self, ids[i], sound), stepLen); - } - } - - return self; - }, - - /** - * Internal method that stops the currently playing fade when - * a new fade starts, volume is changed or the sound is stopped. - * @param {Number} id The sound id. - * @return {Howl} - */ - _stopFade: function(id) { - var self = this; - var sound = self._soundById(id); - - if (sound && sound._interval) { - if (self._webAudio) { - sound._node.gain.cancelScheduledValues(Howler.ctx.currentTime); - } - - clearInterval(sound._interval); - sound._interval = null; - self._emit('fade', id); - } - - return self; - }, - - /** - * Get/set the loop parameter on a sound. This method can optionally take 0, 1 or 2 arguments. - * loop() -> Returns the group's loop value. - * loop(id) -> Returns the sound id's loop value. - * loop(loop) -> Sets the loop value for all sounds in this Howl group. - * loop(loop, id) -> Sets the loop value of passed sound id. - * @return {Howl/Boolean} Returns self or current loop value. - */ - loop: function() { - var self = this; - var args = arguments; - var loop, id, sound; - - // Determine the values for loop and id. - if (args.length === 0) { - // Return the grou's loop value. - return self._loop; - } else if (args.length === 1) { - if (typeof args[0] === 'boolean') { - loop = args[0]; - self._loop = loop; - } else { - // Return this sound's loop value. - sound = self._soundById(parseInt(args[0], 10)); - return sound ? sound._loop : false; - } - } else if (args.length === 2) { - loop = args[0]; - id = parseInt(args[1], 10); - } - - // If no id is passed, get all ID's to be looped. - var ids = self._getSoundIds(id); - for (var i=0; i Returns the first sound node's current playback rate. - * rate(id) -> Returns the sound id's current playback rate. - * rate(rate) -> Sets the playback rate of all sounds in this Howl group. - * rate(rate, id) -> Sets the playback rate of passed sound id. - * @return {Howl/Number} Returns self or the current playback rate. - */ - rate: function() { - var self = this; - var args = arguments; - var rate, id; - - // Determine the values based on arguments. - if (args.length === 0) { - // We will simply return the current rate of the first node. - id = self._sounds[0]._id; - } else if (args.length === 1) { - // First check if this is an ID, and if not, assume it is a new rate value. - var ids = self._getSoundIds(); - var index = ids.indexOf(args[0]); - if (index >= 0) { - id = parseInt(args[0], 10); - } else { - rate = parseFloat(args[0]); - } - } else if (args.length === 2) { - rate = parseFloat(args[0]); - id = parseInt(args[1], 10); - } - - // Update the playback rate or return the current value. - var sound; - if (typeof rate === 'number') { - // If the sound hasn't loaded, add it to the load queue to change playback rate when capable. - if (self._state !== 'loaded') { - self._queue.push({ - event: 'rate', - action: function() { - self.rate.apply(self, args); - } - }); - - return self; - } - - // Set the group rate. - if (typeof id === 'undefined') { - self._rate = rate; - } - - // Update one or all volumes. - id = self._getSoundIds(id); - for (var i=0; i Returns the first sound node's current seek position. - * seek(id) -> Returns the sound id's current seek position. - * seek(seek) -> Sets the seek position of the first sound node. - * seek(seek, id) -> Sets the seek position of passed sound id. - * @return {Howl/Number} Returns self or the current seek position. - */ - seek: function() { - var self = this; - var args = arguments; - var seek, id; - - // Determine the values based on arguments. - if (args.length === 0) { - // We will simply return the current position of the first node. - id = self._sounds[0]._id; - } else if (args.length === 1) { - // First check if this is an ID, and if not, assume it is a new seek position. - var ids = self._getSoundIds(); - var index = ids.indexOf(args[0]); - if (index >= 0) { - id = parseInt(args[0], 10); - } else { - id = self._sounds[0]._id; - seek = parseFloat(args[0]); - } - } else if (args.length === 2) { - seek = parseFloat(args[0]); - id = parseInt(args[1], 10); - } - - // If there is no ID, bail out. - if (typeof id === 'undefined') { - return self; - } - - // If the sound hasn't loaded, add it to the load queue to seek when capable. - if (self._state !== 'loaded') { - self._queue.push({ - event: 'seek', - action: function() { - self.seek.apply(self, args); - } - }); - - return self; - } - - // Get the sound. - var sound = self._soundById(id); - - if (sound) { - if (typeof seek === 'number' && seek >= 0) { - // Pause the sound and update position for restarting playback. - var playing = self.playing(id); - if (playing) { - self.pause(id, true); - } - - // Move the position of the track and cancel timer. - sound._seek = seek; - sound._ended = false; - self._clearTimer(id); - - // Restart the playback if the sound was playing. - if (playing) { - self.play(id, true); - } - - // Update the seek position for HTML5 Audio. - if (!self._webAudio && sound._node) { - sound._node.currentTime = seek; - } - - self._emit('seek', id); - } else { - if (self._webAudio) { - var realTime = self.playing(id) ? Howler.ctx.currentTime - sound._playStart : 0; - var rateSeek = sound._rateSeek ? sound._rateSeek - sound._seek : 0; - return sound._seek + (rateSeek + realTime * Math.abs(sound._rate)); - } else { - return sound._node.currentTime; - } - } - } - - return self; - }, - - /** - * Check if a specific sound is currently playing or not (if id is provided), or check if at least one of the sounds in the group is playing or not. - * @param {Number} id The sound id to check. If none is passed, the whole sound group is checked. - * @return {Boolean} True if playing and false if not. - */ - playing: function(id) { - var self = this; - - // Check the passed sound ID (if any). - if (typeof id === 'number') { - var sound = self._soundById(id); - return sound ? !sound._paused : false; - } - - // Otherwise, loop through all sounds and check if any are playing. - for (var i=0; i= 0) { - Howler._howls.splice(index, 1); - } - } - - // Delete this sound from the cache (if no other Howl is using it). - var remCache = true; - for (i=0; i=0; i--) { - if (!events[i].id || events[i].id === id || event === 'load') { - setTimeout(function(fn) { - fn.call(this, id, msg); - }.bind(self, events[i].fn), 0); - - // If this event was setup with `once`, remove it. - if (events[i].once) { - self.off(event, events[i].fn, events[i].id); - } - } - } - - return self; - }, - - /** - * Queue of actions initiated before the sound has loaded. - * These will be called in sequence, with the next only firing - * after the previous has finished executing (even if async like play). - * @return {Howl} - */ - _loadQueue: function() { - var self = this; - - if (self._queue.length > 0) { - var task = self._queue[0]; - - // don't move onto the next task until this one is done - self.once(task.event, function() { - self._queue.shift(); - self._loadQueue(); - }); - - task.action(); - } - - return self; - }, - - /** - * Fired when playback ends at the end of the duration. - * @param {Sound} sound The sound object to work with. - * @return {Howl} - */ - _ended: function(sound) { - var self = this; - var sprite = sound._sprite; - - // Should this sound loop? - var loop = !!(sound._loop || self._sprite[sprite][2]); - - // Fire the ended event. - self._emit('end', sound._id); - - // Restart the playback for HTML5 Audio loop. - if (!self._webAudio && loop) { - self.stop(sound._id, true).play(sound._id); - } - - // Restart this timer if on a Web Audio loop. - if (self._webAudio && loop) { - self._emit('play', sound._id); - sound._seek = sound._start || 0; - sound._rateSeek = 0; - sound._playStart = Howler.ctx.currentTime; - - var timeout = ((sound._stop - sound._start) * 1000) / Math.abs(sound._rate); - self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout); - } - - // Mark the node as paused. - if (self._webAudio && !loop) { - sound._paused = true; - sound._ended = true; - sound._seek = sound._start || 0; - sound._rateSeek = 0; - self._clearTimer(sound._id); - - // Clean up the buffer source. - self._cleanBuffer(sound._node); - - // Attempt to auto-suspend AudioContext if no sounds are still playing. - Howler._autoSuspend(); - } - - // When using a sprite, end the track. - if (!self._webAudio && !loop) { - self.stop(sound._id); - } - - return self; - }, - - /** - * Clear the end timer for a sound playback. - * @param {Number} id The sound ID. - * @return {Howl} - */ - _clearTimer: function(id) { - var self = this; - - if (self._endTimers[id]) { - clearTimeout(self._endTimers[id]); - delete self._endTimers[id]; - } - - return self; - }, - - /** - * Return the sound identified by this ID, or return null. - * @param {Number} id Sound ID - * @return {Object} Sound object or null. - */ - _soundById: function(id) { - var self = this; - - // Loop through all sounds and find the one with this ID. - for (var i=0; i=0; i--) { - if (cnt <= limit) { - return; - } - - if (self._sounds[i]._ended) { - // Disconnect the audio source when using Web Audio. - if (self._webAudio && self._sounds[i]._node) { - self._sounds[i]._node.disconnect(0); - } - - // Remove sounds until we have the pool size. - self._sounds.splice(i, 1); - cnt--; - } - } - }, - - /** - * Get all ID's from the sounds pool. - * @param {Number} id Only return one ID if one is passed. - * @return {Array} Array of IDs. - */ - _getSoundIds: function(id) { - var self = this; - - if (typeof id === 'undefined') { - var ids = []; - for (var i=0; i 0) { - cache[self._src] = buffer; - loadSound(self, buffer); - } - }, function() { - self._emit('loaderror', null, 'Decoding audio data failed.'); - }); - }; - - /** - * Sound is now loaded, so finish setting everything up and fire the loaded event. - * @param {Howl} self - * @param {Object} buffer The decoded buffer sound source. - */ - var loadSound = function(self, buffer) { - // Set the duration. - if (buffer && !self._duration) { - self._duration = buffer.duration; - } - - // Setup a sprite if none is defined. - if (Object.keys(self._sprite).length === 0) { - self._sprite = {__default: [0, self._duration * 1000]}; - } - - // Fire the loaded event. - if (self._state !== 'loaded') { - self._state = 'loaded'; - self._emit('load'); - self._loadQueue(); - } - }; - - /** - * Setup the audio context when available, or switch to HTML5 Audio mode. - */ - var setupAudioContext = function() { - // Check if we are using Web Audio and setup the AudioContext if we are. - try { - if (typeof AudioContext !== 'undefined') { - Howler.ctx = new AudioContext(); - } else if (typeof webkitAudioContext !== 'undefined') { - Howler.ctx = new webkitAudioContext(); - } else { - Howler.usingWebAudio = false; - } - } catch(e) { - Howler.usingWebAudio = false; - } - - // Check if a webview is being used on iOS8 or earlier (rather than the browser). - // If it is, disable Web Audio as it causes crashing. - var iOS = (/iP(hone|od|ad)/.test(Howler._navigator && Howler._navigator.platform)); - var appVersion = Howler._navigator && Howler._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/); - var version = appVersion ? parseInt(appVersion[1], 10) : null; - if (iOS && version && version < 9) { - var safari = /safari/.test(Howler._navigator && Howler._navigator.userAgent.toLowerCase()); - if (Howler._navigator && Howler._navigator.standalone && !safari || Howler._navigator && !Howler._navigator.standalone && !safari) { - Howler.usingWebAudio = false; - } - } - - // Create and expose the master GainNode when using Web Audio (useful for plugins or advanced usage). - if (Howler.usingWebAudio) { - Howler.masterGain = (typeof Howler.ctx.createGain === 'undefined') ? Howler.ctx.createGainNode() : Howler.ctx.createGain(); - Howler.masterGain.gain.value = 1; - Howler.masterGain.connect(Howler.ctx.destination); - } - - // Re-run the setup on Howler. - Howler._setup(); - }; - - // Add support for AMD (Asynchronous Module Definition) libraries such as require.js. - if (typeof define === 'function' && define.amd) { - define([], function() { - return { - Howler: Howler, - Howl: Howl - }; - }); - } - - // Add support for CommonJS libraries such as browserify. - if (typeof exports !== 'undefined') { - exports.Howler = Howler; - exports.Howl = Howl; - } - - // Define globally in case AMD is not available or unused. - if (typeof window !== 'undefined') { - window.HowlerGlobal = HowlerGlobal; - window.Howler = Howler; - window.Howl = Howl; - window.Sound = Sound; - } else if (typeof global !== 'undefined') { // Add to global in Node.js (for testing, etc). - global.HowlerGlobal = HowlerGlobal; - global.Howler = Howler; - global.Howl = Howl; - global.Sound = Sound; - } -})(); diff --git a/webapp2/js/lib/howler.core.min.js b/webapp2/js/lib/howler.core.min.js deleted file mode 100644 index ec68dcf..0000000 --- a/webapp2/js/lib/howler.core.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! howler.js v2.0.2 | (c) 2013-2016, James Simpson of GoldFire Studios | MIT License | howlerjs.com */ -!function(){"use strict";var e=function(){this.init()};e.prototype={init:function(){var e=this||n;return e._codecs={},e._howls=[],e._muted=!1,e._volume=1,e._canPlayEvent="canplaythrough",e._navigator="undefined"!=typeof window&&window.navigator?window.navigator:null,e.masterGain=null,e.noAudio=!1,e.usingWebAudio=!0,e.autoSuspend=!0,e.ctx=null,e.mobileAutoEnable=!0,e._setup(),e},volume:function(e){var t=this||n;if(e=parseFloat(e),t.ctx||_(),"undefined"!=typeof e&&e>=0&&e<=1){if(t._volume=e,t._muted)return t;t.usingWebAudio&&(t.masterGain.gain.value=e);for(var o=0;o=0;t--)e._howls[t].unload();return e.usingWebAudio&&e.ctx&&"undefined"!=typeof e.ctx.close&&(e.ctx.close(),e.ctx=null,_()),e},codecs:function(e){return(this||n)._codecs[e.replace(/^x-/,"")]},_setup:function(){var e=this||n;if(e.state=e.ctx?e.ctx.state||"running":"running",e._autoSuspend(),!e.usingWebAudio)if("undefined"!=typeof Audio)try{var t=new Audio;"undefined"==typeof t.oncanplaythrough&&(e._canPlayEvent="canplay")}catch(n){e.noAudio=!0}else e.noAudio=!0;try{var t=new Audio;t.muted&&(e.noAudio=!0)}catch(e){}return e.noAudio||e._setupCodecs(),e},_setupCodecs:function(){var e=this||n,t=null;try{t="undefined"!=typeof Audio?new Audio:null}catch(n){return e}if(!t||"function"!=typeof t.canPlayType)return e;var o=t.canPlayType("audio/mpeg;").replace(/^no$/,""),r=e._navigator&&e._navigator.userAgent.match(/OPR\/([0-6].)/g),u=r&&parseInt(r[0].split("/")[1],10)<33;return e._codecs={mp3:!(u||!o&&!t.canPlayType("audio/mp3;").replace(/^no$/,"")),mpeg:!!o,opus:!!t.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/,""),ogg:!!t.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),oga:!!t.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),wav:!!t.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),aac:!!t.canPlayType("audio/aac;").replace(/^no$/,""),caf:!!t.canPlayType("audio/x-caf;").replace(/^no$/,""),m4a:!!(t.canPlayType("audio/x-m4a;")||t.canPlayType("audio/m4a;")||t.canPlayType("audio/aac;")).replace(/^no$/,""),mp4:!!(t.canPlayType("audio/x-mp4;")||t.canPlayType("audio/mp4;")||t.canPlayType("audio/aac;")).replace(/^no$/,""),weba:!!t.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),webm:!!t.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),dolby:!!t.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/,""),flac:!!(t.canPlayType("audio/x-flac;")||t.canPlayType("audio/flac;")).replace(/^no$/,"")},e},_enableMobileAudio:function(){var e=this||n,t=/iPhone|iPad|iPod|Android|BlackBerry|BB10|Silk|Mobi/i.test(e._navigator&&e._navigator.userAgent),o=!!("ontouchend"in window||e._navigator&&e._navigator.maxTouchPoints>0||e._navigator&&e._navigator.msMaxTouchPoints>0);if(!e._mobileEnabled&&e.ctx&&(t||o)){e._mobileEnabled=!1,e._mobileUnloaded||44100===e.ctx.sampleRate||(e._mobileUnloaded=!0,e.unload()),e._scratchBuffer=e.ctx.createBuffer(1,1,22050);var r=function(){var n=e.ctx.createBufferSource();n.buffer=e._scratchBuffer,n.connect(e.ctx.destination),"undefined"==typeof n.start?n.noteOn(0):n.start(0),n.onended=function(){n.disconnect(0),e._mobileEnabled=!0,e.mobileAutoEnable=!1,document.removeEventListener("touchend",r,!0)}};return document.addEventListener("touchend",r,!0),e}},_autoSuspend:function(){var e=this;if(e.autoSuspend&&e.ctx&&"undefined"!=typeof e.ctx.suspend&&n.usingWebAudio){for(var t=0;t0?d._seek:o._sprite[e][0]/1e3),_=Math.max(0,(o._sprite[e][0]+o._sprite[e][1])/1e3-i),s=1e3*_/Math.abs(d._rate);d._paused=!1,d._ended=!1,d._sprite=e,d._seek=i,d._start=o._sprite[e][0]/1e3,d._stop=(o._sprite[e][0]+o._sprite[e][1])/1e3,d._loop=!(!d._loop&&!o._sprite[e][2]);var l=d._node;if(o._webAudio){var f=function(){o._refreshBuffer(d);var e=d._muted||o._muted?0:d._volume;l.gain.setValueAtTime(e,n.ctx.currentTime),d._playStart=n.ctx.currentTime,"undefined"==typeof l.bufferSource.start?d._loop?l.bufferSource.noteGrainOn(0,i,86400):l.bufferSource.noteGrainOn(0,i,_):d._loop?l.bufferSource.start(0,i,86400):l.bufferSource.start(0,i,_),s!==1/0&&(o._endTimers[d._id]=setTimeout(o._ended.bind(o,d),s)),t||setTimeout(function(){o._emit("play",d._id)},0)},c="running"===n.state;"loaded"===o._state&&c?f():(o.once(c?"load":"resume",f,c?d._id:null),o._clearTimer(d._id))}else{var p=function(){l.currentTime=i,l.muted=d._muted||o._muted||n._muted||l.muted,l.volume=d._volume*n.volume(),l.playbackRate=d._rate,setTimeout(function(){l.play(),s!==1/0&&(o._endTimers[d._id]=setTimeout(o._ended.bind(o,d),s)),t||o._emit("play",d._id)},0)},m="loaded"===o._state&&(window&&window.ejecta||!l.readyState&&n._navigator.isCocoonJS);if(4===l.readyState||m)p();else{var v=function(){p(),l.removeEventListener(n._canPlayEvent,v,!1)};l.addEventListener(n._canPlayEvent,v,!1),o._clearTimer(d._id)}}return d._id},pause:function(e){var n=this;if("loaded"!==n._state)return n._queue.push({event:"pause",action:function(){n.pause(e)}}),n;for(var t=n._getSoundIds(e),o=0;o=0?t=parseInt(r[0],10):e=parseFloat(r[0])}else r.length>=2&&(e=parseFloat(r[0]),t=parseInt(r[1],10));var d;if(!("undefined"!=typeof e&&e>=0&&e<=1))return d=t?o._soundById(t):o._sounds[0],d?d._volume:0;if("loaded"!==o._state)return o._queue.push({event:"volume",action:function(){o.volume.apply(o,r)}}),o;"undefined"==typeof t&&(o._volume=e),t=o._getSoundIds(t);for(var i=0;it?"out":"in",i=a/.01,_=i>0?o/i:o;if(_<4&&(i=Math.ceil(i/(4/_)),_=4),"loaded"!==u._state)return u._queue.push({event:"fade",action:function(){u.fade(e,t,o,r)}}),u;u.volume(e,r);for(var s=u._getSoundIds(r),l=0;l0&&(m+="in"===d?.01:-.01),m=Math.max(0,m),m=Math.min(1,m),m=Math.round(100*m)/100,u._webAudio?("undefined"==typeof r&&(u._volume=m),n._volume=m):u.volume(m,e,!0),m===t&&(clearInterval(n._interval),n._interval=null,u.volume(m,e),u._emit("fade",e))}.bind(u,s[l],f),_)}}return u},_stopFade:function(e){var t=this,o=t._soundById(e);return o&&o._interval&&(t._webAudio&&o._node.gain.cancelScheduledValues(n.ctx.currentTime),clearInterval(o._interval),o._interval=null,t._emit("fade",e)),t},loop:function(){var e,n,t,o=this,r=arguments;if(0===r.length)return o._loop;if(1===r.length){if("boolean"!=typeof r[0])return t=o._soundById(parseInt(r[0],10)),!!t&&t._loop;e=r[0],o._loop=e}else 2===r.length&&(e=r[0],n=parseInt(r[1],10));for(var u=o._getSoundIds(n),a=0;a=0?t=parseInt(r[0],10):e=parseFloat(r[0])}else 2===r.length&&(e=parseFloat(r[0]),t=parseInt(r[1],10));var d;if("number"!=typeof e)return d=o._soundById(t),d?d._rate:o._rate;if("loaded"!==o._state)return o._queue.push({event:"rate",action:function(){o.rate.apply(o,r)}}),o;"undefined"==typeof t&&(o._rate=e),t=o._getSoundIds(t);for(var i=0;i=0?t=parseInt(r[0],10):(t=o._sounds[0]._id,e=parseFloat(r[0]))}else 2===r.length&&(e=parseFloat(r[0]),t=parseInt(r[1],10));if("undefined"==typeof t)return o;if("loaded"!==o._state)return o._queue.push({event:"seek",action:function(){o.seek.apply(o,r)}}),o;var d=o._soundById(t);if(d){if(!("number"==typeof e&&e>=0)){if(o._webAudio){var i=o.playing(t)?n.ctx.currentTime-d._playStart:0,_=d._rateSeek?d._rateSeek-d._seek:0;return d._seek+(_+i*Math.abs(d._rate))}return d._node.currentTime}var s=o.playing(t);s&&o.pause(t,!0),d._seek=e,d._ended=!1,o._clearTimer(t),s&&o.play(t,!0),!o._webAudio&&d._node&&(d._node.currentTime=e),o._emit("seek",t)}return o},playing:function(e){var n=this;if("number"==typeof e){var t=n._soundById(e);return!!t&&!t._paused}for(var o=0;o=0&&n._howls.splice(u,1)}var a=!0;for(o=0;o=0;u--)r[u].id&&r[u].id!==n&&"load"!==e||(setTimeout(function(e){e.call(this,n,t)}.bind(o,r[u].fn),0),r[u].once&&o.off(e,r[u].fn,r[u].id));return o},_loadQueue:function(){var e=this;if(e._queue.length>0){var n=e._queue[0];e.once(n.event,function(){e._queue.shift(),e._loadQueue()}),n.action()}return e},_ended:function(e){var t=this,o=e._sprite,r=!(!e._loop&&!t._sprite[o][2]);if(t._emit("end",e._id),!t._webAudio&&r&&t.stop(e._id,!0).play(e._id),t._webAudio&&r){t._emit("play",e._id),e._seek=e._start||0,e._rateSeek=0,e._playStart=n.ctx.currentTime;var u=1e3*(e._stop-e._start)/Math.abs(e._rate);t._endTimers[e._id]=setTimeout(t._ended.bind(t,e),u)}return t._webAudio&&!r&&(e._paused=!0,e._ended=!0,e._seek=e._start||0,e._rateSeek=0,t._clearTimer(e._id),t._cleanBuffer(e._node),n._autoSuspend()),t._webAudio||r||t.stop(e._id),t},_clearTimer:function(e){var n=this;return n._endTimers[e]&&(clearTimeout(n._endTimers[e]),delete n._endTimers[e]),n},_soundById:function(e){for(var n=this,t=0;t=0;o--){if(t<=n)return;e._sounds[o]._ended&&(e._webAudio&&e._sounds[o]._node&&e._sounds[o]._node.disconnect(0),e._sounds.splice(o,1),t--)}}},_getSoundIds:function(e){var n=this;if("undefined"==typeof e){for(var t=[],o=0;o0&&(r[t._src]=e,i(t,e))},function(){t._emit("loaderror",null,"Decoding audio data failed.")})},i=function(e,n){n&&!e._duration&&(e._duration=n.duration),0===Object.keys(e._sprite).length&&(e._sprite={__default:[0,1e3*e._duration]}),"loaded"!==e._state&&(e._state="loaded",e._emit("load"),e._loadQueue())},_=function(){try{"undefined"!=typeof AudioContext?n.ctx=new AudioContext:"undefined"!=typeof webkitAudioContext?n.ctx=new webkitAudioContext:n.usingWebAudio=!1}catch(e){n.usingWebAudio=!1}var e=/iP(hone|od|ad)/.test(n._navigator&&n._navigator.platform),t=n._navigator&&n._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/),o=t?parseInt(t[1],10):null;if(e&&o&&o<9){var r=/safari/.test(n._navigator&&n._navigator.userAgent.toLowerCase());(n._navigator&&n._navigator.standalone&&!r||n._navigator&&!n._navigator.standalone&&!r)&&(n.usingWebAudio=!1)}n.usingWebAudio&&(n.masterGain="undefined"==typeof n.ctx.createGain?n.ctx.createGainNode():n.ctx.createGain(),n.masterGain.gain.value=1,n.masterGain.connect(n.ctx.destination)),n._setup()};"function"==typeof define&&define.amd&&define([],function(){return{Howler:n,Howl:t}}),"undefined"!=typeof exports&&(exports.Howler=n,exports.Howl=t),"undefined"!=typeof window?(window.HowlerGlobal=e,window.Howler=n,window.Howl=t,window.Sound=o):"undefined"!=typeof global&&(global.HowlerGlobal=e,global.Howler=n,global.Howl=t,global.Sound=o)}(); \ No newline at end of file diff --git a/webapp2/js/lib/vue-sortable.js b/webapp2/js/lib/vue-sortable.js deleted file mode 100644 index 138fef9..0000000 --- a/webapp2/js/lib/vue-sortable.js +++ /dev/null @@ -1,326 +0,0 @@ -'use strict'; - -var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; - -function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } - -(function () { - "use strict"; - - function buildDraggable(Sortable) { - function removeNode(node) { - node.parentElement.removeChild(node); - } - - function insertNodeAt(fatherNode, node, position) { - if (position < fatherNode.children.length) { - fatherNode.insertBefore(node, fatherNode.children[position]); - } else { - fatherNode.appendChild(node); - } - } - - function computeVmIndex(vnodes, element) { - return vnodes.map(function (elt) { - return elt.elm; - }).indexOf(element); - } - - function _computeIndexes(slots, children) { - if (!slots) { - return []; - } - - var elmFromNodes = slots.map(function (elt) { - return elt.elm; - }); - return [].concat(_toConsumableArray(children)).map(function (elt) { - return elmFromNodes.indexOf(elt); - }); - } - - function emit(evtName, evtData) { - var _this = this; - - this.$nextTick(function () { - return _this.$emit(evtName.toLowerCase(), evtData); - }); - } - - function delegateAndEmit(evtName) { - var _this2 = this; - - return function (evtData) { - if (_this2.realList !== null) { - _this2['onDrag' + evtName](evtData); - } - emit.call(_this2, evtName, evtData); - }; - } - - var eventsListened = ['Start', 'Add', 'Remove', 'Update', 'End']; - var eventsToEmit = ['Choose', 'Sort', 'Filter', 'Clone']; - var readonlyProperties = ['Move'].concat(eventsListened, eventsToEmit).map(function (evt) { - return 'on' + evt; - }); - var draggingElement = null; - - var props = { - options: Object, - list: { - type: Array, - required: false, - default: null - }, - value: { - type: Array, - required: false, - default: null - }, - clone: { - type: Function, - default: function _default(original) { - return original; - } - }, - element: { - type: String, - default: 'div' - }, - move: { - type: Function, - default: null - } - }; - - var draggableComponent = { - props: props, - - data: function data() { - return { - transitionMode: false, - componentMode: false - }; - }, - render: function render(h) { - if (this.$slots.default && this.$slots.default.length === 1) { - var child = this.$slots.default[0]; - if (child.componentOptions && child.componentOptions.tag === "transition-group") { - this.transitionMode = true; - } - } - return h(this.element, null, this.$slots.default); - }, - mounted: function mounted() { - var _this3 = this; - - this.componentMode = this.element.toLowerCase() !== this.$el.nodeName.toLowerCase(); - if (this.componentMode && this.transitionMode) { - throw new Error('Transition-group inside component is not suppported. Please alter element value or remove transition-group. Current element value: ' + this.element); - } - var optionsAdded = {}; - eventsListened.forEach(function (elt) { - optionsAdded['on' + elt] = delegateAndEmit.call(_this3, elt); - }); - - eventsToEmit.forEach(function (elt) { - optionsAdded['on' + elt] = emit.bind(_this3, elt); - }); - - var options = _extends({}, this.options, optionsAdded, { onMove: function onMove(evt) { - return _this3.onDragMove(evt); - } }); - this._sortable = new Sortable(this.rootContainer, options); - this.computeIndexes(); - }, - beforeDestroy: function beforeDestroy() { - this._sortable.destroy(); - }, - - - computed: { - rootContainer: function rootContainer() { - return this.transitionMode ? this.$el.children[0] : this.$el; - }, - isCloning: function isCloning() { - return !!this.options && !!this.options.group && this.options.group.pull === 'clone'; - }, - realList: function realList() { - return !!this.list ? this.list : this.value; - } - }, - - watch: { - options: function options(newOptionValue) { - for (var property in newOptionValue) { - if (readonlyProperties.indexOf(property) == -1) { - this._sortable.option(property, newOptionValue[property]); - } - } - }, - realList: function realList() { - this.computeIndexes(); - } - }, - - methods: { - getChildrenNodes: function getChildrenNodes() { - if (this.componentMode) { - return this.$children[0].$slots.default; - } - var rawNodes = this.$slots.default; - return this.transitionMode ? rawNodes[0].child.$slots.default : rawNodes; - }, - computeIndexes: function computeIndexes() { - var _this4 = this; - - this.$nextTick(function () { - _this4.visibleIndexes = _computeIndexes(_this4.getChildrenNodes(), _this4.rootContainer.children); - }); - }, - getUnderlyingVm: function getUnderlyingVm(htmlElt) { - var index = computeVmIndex(this.getChildrenNodes(), htmlElt); - var element = this.realList[index]; - return { index: index, element: element }; - }, - getUnderlyingPotencialDraggableComponent: function getUnderlyingPotencialDraggableComponent(_ref) { - var __vue__ = _ref.__vue__; - - if (!__vue__ || !__vue__.$options || __vue__.$options._componentTag !== "transition-group") { - return __vue__; - } - return __vue__.$parent; - }, - emitChanges: function emitChanges(evt) { - var _this5 = this; - - this.$nextTick(function () { - _this5.$emit('change', evt); - }); - }, - alterList: function alterList(onList) { - if (!!this.list) { - onList(this.list); - } else { - var newList = [].concat(_toConsumableArray(this.value)); - onList(newList); - this.$emit('input', newList); - } - }, - spliceList: function spliceList() { - var _arguments = arguments; - - var spliceList = function spliceList(list) { - return list.splice.apply(list, _arguments); - }; - this.alterList(spliceList); - }, - updatePosition: function updatePosition(oldIndex, newIndex) { - var updatePosition = function updatePosition(list) { - return list.splice(newIndex, 0, list.splice(oldIndex, 1)[0]); - }; - this.alterList(updatePosition); - }, - getRelatedContextFromMoveEvent: function getRelatedContextFromMoveEvent(_ref2) { - var to = _ref2.to, - related = _ref2.related; - - var component = this.getUnderlyingPotencialDraggableComponent(to); - if (!component) { - return { component: component }; - } - var list = component.realList; - var context = { list: list, component: component }; - if (to !== related && list && component.getUnderlyingVm) { - var destination = component.getUnderlyingVm(related); - return _extends(destination, context); - } - - return context; - }, - getVmIndex: function getVmIndex(domIndex) { - var indexes = this.visibleIndexes; - var numberIndexes = indexes.length; - return domIndex > numberIndexes - 1 ? numberIndexes : indexes[domIndex]; - }, - onDragStart: function onDragStart(evt) { - this.context = this.getUnderlyingVm(evt.item); - evt.item._underlying_vm_ = this.clone(this.context.element); - draggingElement = evt.item; - }, - onDragAdd: function onDragAdd(evt) { - var element = evt.item._underlying_vm_; - if (element === undefined) { - return; - } - removeNode(evt.item); - var newIndex = this.getVmIndex(evt.newIndex); - this.spliceList(newIndex, 0, element); - this.computeIndexes(); - var added = { element: element, newIndex: newIndex }; - this.emitChanges({ added: added }); - }, - onDragRemove: function onDragRemove(evt) { - insertNodeAt(this.rootContainer, evt.item, evt.oldIndex); - if (this.isCloning) { - removeNode(evt.clone); - return; - } - var oldIndex = this.context.index; - this.spliceList(oldIndex, 1); - var removed = { element: this.context.element, oldIndex: oldIndex }; - this.emitChanges({ removed: removed }); - }, - onDragUpdate: function onDragUpdate(evt) { - removeNode(evt.item); - insertNodeAt(evt.from, evt.item, evt.oldIndex); - var oldIndex = this.context.index; - var newIndex = this.getVmIndex(evt.newIndex); - this.updatePosition(oldIndex, newIndex); - var moved = { element: this.context.element, oldIndex: oldIndex, newIndex: newIndex }; - this.emitChanges({ moved: moved }); - }, - computeFutureIndex: function computeFutureIndex(relatedContext, evt) { - if (!relatedContext.element) { - return 0; - } - var domChildren = [].concat(_toConsumableArray(evt.to.children)); - var currentDOMIndex = domChildren.indexOf(evt.related); - var currentIndex = relatedContext.component.getVmIndex(currentDOMIndex); - var draggedInList = domChildren.indexOf(draggingElement) != -1; - return draggedInList ? currentIndex : currentIndex + 1; - }, - onDragMove: function onDragMove(evt) { - var onMove = this.move; - if (!onMove || !this.realList) { - return true; - } - - var relatedContext = this.getRelatedContextFromMoveEvent(evt); - var draggedContext = this.context; - var futureIndex = this.computeFutureIndex(relatedContext, evt); - _extends(draggedContext, { futureIndex: futureIndex }); - _extends(evt, { relatedContext: relatedContext, draggedContext: draggedContext }); - return onMove(evt); - }, - onDragEnd: function onDragEnd(evt) { - this.computeIndexes(); - draggingElement = null; - } - } - }; - return draggableComponent; - } - - if (typeof exports == "object") { - var Sortable = require("sortablejs"); - module.exports = buildDraggable(Sortable); - } else if (typeof define == "function" && define.amd) { - define(['sortablejs'], function (Sortable) { - return buildDraggable(Sortable); - }); - } else if (window && window.Vue && window.Sortable) { - var draggable = buildDraggable(window.Sortable); - Vue.component('draggable', draggable); - } -})(); diff --git a/webapp2/js/mstream.api.js b/webapp2/js/mstream.api.js deleted file mode 100644 index a68379d..0000000 --- a/webapp2/js/mstream.api.js +++ /dev/null @@ -1,180 +0,0 @@ - // TODO: MOVE THIS ALL TO PROMISES - - -var MSTREAMAPI = (function () { - let mstreamModule = {}; - - mstreamModule.listOfServers = []; - mstreamModule.currentServer = { - host:"", - username:"", - token: "", - vPath: "" - } - - $.ajaxPrefilter(function( options ) { - options.beforeSend = function (xhr) { - xhr.setRequestHeader('x-access-token', MSTREAMAPI.currentServer.token); - } - }); - - - // TODO: Special functions for handling multiple servers - // Add Server - // Delete Server - // Select Server - // Edit Server - // Test Sever - // Login server and save credentials - - - - function makeRequest(url, type, dataObject, callback){ - var request = $.ajax({ - url: url, - type: type, - contentType: "application/json", - dataType: "json", - data: JSON.stringify(dataObject) - }); - - request.done(function( response ) { - callback(response, false); - }); - - // TODO: AHandle errors - request.fail(function( jqXHR, textStatus ) { - callback(textStatus, jqXHR); - }); - } - - - function makePOSTRequest(url, dataObject, callback){ - makeRequest(url, "POST", dataObject, callback); - } - - function makeGETRequest(url, dataObject, callback){ - makeRequest(url, "GET", dataObject, callback); - } - - - - mstreamModule.dirparser = function(directory, filetypes, callback){ - makePOSTRequest('/dirparser', {dir: directory}, callback); - } - - mstreamModule.savePlaylist = function(title, songs, callback){ - makePOSTRequest('/playlist/save', { title: title, songs: songs }, callback); - } - - mstreamModule.deletePlaylist = function(playlistname, callback){ - makePOSTRequest('/playlist/delete', {playlistname: playlistname}, callback); - } - - mstreamModule.loadPlaylist = function(playlistname, callback){ - makePOSTRequest('/playlist/load', {playlistname: playlistname}, callback); - } - - mstreamModule.getAllPlaylists = function(callback){ - makeGETRequest('/playlist/getall', false, callback); - } - - mstreamModule.search = function(searchTerm, callback){ - makePOSTRequest('/db/search', {search: searchTerm}, callback); - } - - mstreamModule.artists = function(callback){ - makeGETRequest('/db/artists', false, callback); - } - - mstreamModule.albums = function(callback){ - makeGETRequest('/db/albums', false, callback); - } - - mstreamModule.artistAlbums = function(artist, callback){ - makePOSTRequest("/db/artists-albums", {artist: artist}, callback); - } - - mstreamModule.albumSongs = function(album, callback){ - makePOSTRequest("/db/album-songs", {album: album}, callback); - } - - mstreamModule.dbStatus = function(callback){ - makeGETRequest("/db/status", false, callback); - } - - mstreamModule.dbScan = function(callback){ - makeGETRequest("/db/recursive-scan", false, callback); - } - - mstreamModule.makeShared = function(playlist, shareTimeInDays, callback){ - makePOSTRequest("/shared/make-shared", { time: shareTimeInDays, playlist: playlist}, callback); - } - - - mstreamModule.lookupMetadata = function(filepath, callback){ - makePOSTRequest("/db/metadata", {filepath: filepath}, callback); - } - - - - // LOGIN - mstreamModule.login = function(username, password, callback){ - makePOSTRequest("/login", { username: username, password: password}, callback); - } - mstreamModule.updateCurrentServer = function(username, token, vPath){ - mstreamModule.currentServer.user = username; - mstreamModule.currentServer.token = token; - mstreamModule.currentServer.vPath = vPath; - } - - mstreamModule.ping = function(callback){ - makeGETRequest("/ping", false, callback); - } - - - - - // Special helper function - // TODO: handle metadata - MSTREAMPLAYER.addSongWizard = function(filepath, metadata, lookupMetadata){ - var url = mstreamModule.currentServer.host + filepath; - - if(mstreamModule.currentServer.vPath){ - url = mstreamModule.currentServer.vPath + '/' + url; - } - - if(mstreamModule.currentServer.token){ - url = url + '?token=' + mstreamModule.currentServer.token; - } - - var newSong = { - url: url, - filepath: filepath, - metadata: metadata - }; - - MSTREAMPLAYER.addSong(newSong); - - // perform lookup - if(lookupMetadata === true){ - mstreamModule.lookupMetadata(filepath, function(response, error){ - if(error !== false){ - return; - } - - console.log(response); - - if(response.metadata){ - newSong.metadata = Object.create(response.metadata); - MSTREAMPLAYER.resetCurrentMetadata(); - } - - }); - } - } - - - - return mstreamModule; -}()); diff --git a/webapp2/js/mstream.general.js b/webapp2/js/mstream.general.js deleted file mode 100644 index 63bd69d..0000000 --- a/webapp2/js/mstream.general.js +++ /dev/null @@ -1,293 +0,0 @@ -var MSTREAMGEN = (function () { - let mstreamModule = {}; - - - - - // TODO: Functions to change current server and save servers - // Not needed until express port - - - - - mstreamModule.currentProperties = { - currentList: false - // Can be anything in the title array - } - - var currentListTypes = { - 'filebrowser': {displayName: 'File Browser'}, - 'albums': {displayName: 'Albums'}, - 'artists': {displayName: 'Artists'}, - 'search': {displayName: 'Search'}, - 'playlists': {displayname: 'Playlists'} - }; - - mstreamModule.dataList = []; - // TODO: Modify prototype functions for dataList to verify all items - // dataItem = { - // type: '', - // data:'', - //} - - function clearAndSetDataList(type){ - if(!(type in currentListTypes) || type !== false){ - // TODO: Throw Error - } - - mstreamModule.currentProperties.currentList = type; - // Loop through and pop so Vue doesn't throw a fit - while(mstreamModule.dataList.length > 0){ - mstreamModule.dataList.pop(); - } - } - - mstreamModule.clearDataList = function(){ - clearAndSetDataList(false); - } - - - - - - - // TODO: TURN THIS INTO MAP - mstreamModule.fileExplorerArray = [ - {name:'/', position:0} - ]; - - function getDirectoryContents(){ - // Construct the directory string - var directoryString = ""; - for (var i = 0; i < mstreamModule.fileExplorerArray.length; i++) { - // Ignore root directory - if(mstreamModule.fileExplorerArray[i].name !== '/'){ - directoryString += mstreamModule.fileExplorerArray[i].name + "/"; - } - } - - - MSTREAMAPI.dirparser(directoryString, false, function(response, error){ - if(error !== false){ - boilerplateFailure(response, error); - } - - clearAndSetDataList('filebrowser'); - $.each(response.contents, function() { - mstreamModule.dataList.push( - { - type: (this.type === 'directory' ? "directory" : "file"), - metadata: {}, // TODO: Move all metadata to here - path: response.path + this.name, - name: this.name, - artist: false, // TODO: - title: false // TODO: - } - ); - - }); - }); - } - - - - mstreamModule.getCurrentDirectoryContents = function(){ - getDirectoryContents(); - } - - mstreamModule.goToNextDirectory = function(folder, currentScrollPosition = 0){ - if(currentScrollPosition != 0 ){ - // TODO: Save Scroll Position - } - - mstreamModule.fileExplorerArray.push({name:folder, position:0}); - getDirectoryContents(); - } - - mstreamModule.goBackDirectory = function(){ - // Make sure it's not the root directory - // TODO: TEST THAT THIS ALL WORKS - if(mstreamModule.dataList[mstreamModule.dataList.length-1].name === '/'){ - return false; - } - - mstreamModule.fileExplorerArray.pop(); - getDirectoryContents(); - - // TODO: Return Current Scroll Position - } - - mstreamModule.getCurrentScrollPosition = function(){ - return mstreamModule.dataList[mstreamModule.dataList.length-1].position; - } - - // TODO: - mstreamModule.goToExactDirectory = function(directory){ - // Clear Out fileExplorerArray - // loop and pop - - // Setup new fileExplorerArray - // splice - // loop - - getDirectoryContents(); - } - - - - - // TODO Move this to a secondary module that's initiated when it's assured the MSTREAM module is looded - mstreamModule.savePlaylist = function(saveThis){ - // TODO: Verify all data in saveThis - - if(saveThis.length == 0){ - return; - } - - // Get playlist from MSTREAM - // var playlist = MSTREAMPLAYER.whatever - - // Get user entered title - // var title = ''; - - - - // Check for special characters - if(/^[a-zA-Z0-9-_ ]*$/.test(title) == false) { - // TODO: Warn User - return false; - } - - // loop through array and add each file to the playlist - // $.each( playlistArray, function() { - // // TODO: - // }); - - - - $.ajax({ - type: "POST", - url: "saveplaylist", - contentType: "application/json", - dataType: "json", - data: { - title:title, - stuff:saveThis // TODO: Change this on server end - }, - }) - .done(function( msg ) { - - if(msg == 1){ - // ??? - } - if(msg == 0){ - // .. ??? - } - - }); - - // TODO: error handeling - } - - - - - mstreamModule.getAllPlaylists = function(){ - var request = $.ajax({ - url: "getallplaylists", - type: "GET" - }); - - request.done(function( msg ) { - clearAndSetDataList('playlists'); - var parsedResponse = $.parseJSON(msg); - - //parse through the json array and make an array of corresponding divs - var playlists = []; - $.each(parsedResponse, function() { - mstreamModule.dataList.push( - { - type: 'playlist', - name: this.name - } - ); - }); - }); - - request.fail(function( jqXHR, textStatus ) { - // TODO: - }); - } - - - - - - - - - mstreamModule.deletePlaylist = function(playlistNameString){ - - } - - - - - - - mstreamModule.getPlaylistContents = function(playlistNameString){ - - // Make an AJAX call to get the contents of the playlist - $.ajax({ - type: "GET", - url: "loadplaylist", - data: {playlistname: playlistNameString}, - dataType: 'json', - }) - .done(function( msg ) { - // Add the playlist name to the modal - - // Clear the playlist - - // Append the playlist items to the playlist - $.each( msg, function(i ,item) { - - }); - }); - } - - - - - - -// TODO: -function boilerplateFailure(response, error){ - return; -} - - - - - - - - - - - mstreamModule.getSharedPlaylist = function(){ - // Get the URL parameters - console.log(window.location.pathname); - - - // Call the api with the the short token - - // Add songs to MSTREAM - } - - - - // Return an object that is assigned to Module - return mstreamModule; - -}()); diff --git a/webapp2/js/mstream.js b/webapp2/js/mstream.js deleted file mode 100644 index 68a2a2a..0000000 --- a/webapp2/js/mstream.js +++ /dev/null @@ -1,673 +0,0 @@ -$(document).ready(function(){ - - // Auto Focus - Vue.directive('focus', { - // When the bound element is inserted into the DOM... - inserted: function (el) { - // Focus the element - el.focus() - } - }); - - - var loginPanel = new Vue({ - el: '#login-overlay', - data: { - needToLogin: false, - error: false, - errorMessage: 'Login Failed', - pending: false - }, - methods: { - submitCode: function(e){ - // Get Code - this.pending = true; - var that = this; - MSTREAMAPI.login($('#login-username').val(), $('#login-password').val(), function(response, error){ - if(error !== false){ - // Alert the user - that.pending = false; - that.error = true; - return; - } - - // Eye-candy: change the error color and essage - $('#login-alert').toggleClass('alert'); - $('#login-alert').toggleClass('success'); - that.errorMessage = "Welcome To mStream!"; - - // Add the token to the cookies - Cookies.set('token', response.token); - - // Add the token the URL calls - MSTREAMAPI.updateCurrentServer($('#login-username').val(), response.token, response.vPath) - - loadFileExplorer(); - // MSTREAMGEN.getCurrentDirectoryContents(); - - // Remove the overlay - $('.login-overlay').fadeOut( "slow" ); - that.pending = false; - that.needToLogin = false; - }); - } - } - }); - - function testIt(token){ - if(token){ - MSTREAMAPI.currentServer.token = token; - } - - MSTREAMAPI.ping( function(response, error){ - if(error !== false){ - // NOTE: There needs to be a split here - // For the webapp we simply display the login panel - loginPanel.needToLogin = true; - // TODO: Move this transitionstuff to vue - $('.login-overlay').fadeIn( "slow" ); - console.log(loginPanel); - // For electron we need to alert the user that user it failed and guide them to the login form - - return; - } - // set vPath - MSTREAMAPI.currentServer.vPath = response.vPath; - // Setup the filebrowser - loadFileExplorer(); - }); - } - - // NOTE: There needs to be a split here - // For the normal webap we just get the token - // var token = Cookies.get('token'); - testIt(Cookies.get('token')); - // For electron we need to pull it from wherever electron stores things - - - - ////////////////////////////// Global Variables - // These vars track your position within the file explorer - var fileExplorerArray = []; - var fileExplorerScrollPosition = []; - // Stores an array of searchable ojects - var currentBrowsingList = []; - - //////////////////////////////// Administrative stuff - // when you click an mp3, add it to the now playling playlist - $("#filelist").on('click', 'div.filez', function() { - MSTREAMPLAYER.addSongWizard($(this).data("file_location"), {}, true); - }); - - // Handle panel stuff - function resetPanel(panelName, className){ - $('#filelist').empty(); - $('.directoryTitle').hide(); - $('#filelist').removeClass('scrollBoxHeight1'); - $('#filelist').removeClass('scrollBoxHeight2'); - - $('#filelist').addClass(className); - $('.panel_one_name').html(panelName); - } - - function boilerplateFailure(response, error){ - $('#filelist').empty(); - $('#filelist').html('

Call Failed

'); - } - - // clear the playlist - $("#clear").on('click', function() { - MSTREAMPLAYER.clearPlaylist(); - }); - - - - - /////////////////////////////////////// File Explorer - function loadFileExplorer(){ - resetPanel('File Explorer', 'scrollBoxHeight1'); - $('#directory_bar').show(); - - // Reset file explorer vars - fileExplorerArray = []; - fileExplorerScrollPosition = []; - - //send this directory to be parsed and displayed - senddir(0); - } - - // Load up the file explorer - $('.get_file_explorer').on('click', loadFileExplorer); - - // when you click on a directory, go to that directory - $("#filelist").on('click', 'div.dirz', function() { - //get the id of that class - var nextDir = $(this).attr("id"); - fileExplorerArray.push(nextDir); - - // Save the scroll position - var scrollPosition = $('#filelist').scrollTop(); - fileExplorerScrollPosition.push(scrollPosition); - - // pass this value along - senddir(0); - }); - - // when you click the back directory - $(".backButton").on('click', function() { - if(fileExplorerArray.length != 0){ - // remove the last item in the array - fileExplorerArray.pop(); - // Get the scroll postion - var scrollPosition = fileExplorerScrollPosition.pop(); - - senddir(scrollPosition); - } - }); - - // send a new directory to be parsed. - function senddir(scrollPosition){ - // Construct the directory string - var directoryString = ""; - for (var i = 0; i < fileExplorerArray.length; i++) { - directoryString += fileExplorerArray[i] + "/"; - } - - MSTREAMAPI.dirparser(directoryString, false, function(response, error){ - if(error !== false){ - boilerplateFailure(response, error); - } - // Set any directory views - $('.directoryName').html('/' + directoryString); - // hand this data off to be printed on the page - printdir(response); - // Set scroll postion - $('#filelist').scrollTop(scrollPosition); - }); - } - - - // function that will recieve JSON array of a directory listing. It will then make a list of the directory and tack on classes for functionality - function printdir(response){ - currentBrowsingList = response.contents; - - // clear the list - $('#filelist').empty(); - $('#search_folders').val(''); - - // TODO: create an object of everything that the user can easily sort through - var searchObject = []; - - //parse through the json array and make an array of corresponding divs - var filelist = []; - $.each(currentBrowsingList, function() { - if(this.type=='directory'){ - filelist.push('
'+this.name+'
'); - }else{ - if(this.artist!=null || this.title!=null){ - filelist.push('
'+this.artist+' - '+this.title+'
'); - }else{ - filelist.push('
'+this.name+'
'); - } - } - }); - - // Post the html to the filelist div - $('#filelist').html(filelist); - } - - // when you click 'add directory', add entire directory to the playlist - $("#addall").on('click', function() { - //make an array of all the mp3 files in the curent directory - var elems = document.getElementsByClassName('filez'); - var arr = jQuery.makeArray(elems); - - //loop through array and add each file to the playlist - $.each( arr, function() { - MSTREAMPLAYER.addSongWizard($(this).data("file_location"), {}, true); - }); - }); - - - // Search Files - $('#search_folders').on('keyup', function(){ - if($(this).val().length>1){ - var searchVal = $(this).val(); - - var path = ""; // Construct the directory string - for (var i = 0; i < fileExplorerArray.length; i++) { - path += fileExplorerArray[i] + "/"; - } - - var filelist = []; - $.each(currentBrowsingList, function() { - var lowerCase = this.name.toLowerCase(); - - if (lowerCase.indexOf( searchVal.toLowerCase() ) !== -1) { - if(this.type=='directory'){ - filelist.push('
'+this.name+'
'); - }else{ - if(this.artist!=null || this.title!=null){ - filelist.push('
'+this.artist+' - '+this.title+'
'); - }else{ - filelist.push('
'+this.name+'
'); - } - } - } - }); - } - - // Post the html to the filelist div - $('#filelist').html(filelist); - }); - - $('#search-explorer').on('click', function(){ - // Hide Filepath - $('#search_folders').toggleClass( 'hide' ); - // Show Search Input - $('.directoryName').toggleClass( 'hide' ); - - if(!$('#search_folders').hasClass('hide')){ - $( "#search_folders" ).focus(); - } - }); - - - ////////////////////////////////////// Share playlists - $('#share_playlist_form').on('submit', function(e){ - e.preventDefault(); - - $('#share_it').prop("disabled",true); - var shareTimeInDays = $('#share_time').val(); - - // Check for special characters - if(/^[0-9]*$/.test(shareTimeInDays) == false) { - console.log('don\'t do that'); - $('#share_it').prop("disabled",false); - return false; - } - - //loop through array and add each file to the playlist - var stuff = []; - for (let i = 0; i < MSTREAMPLAYER.playlist.length; i++) { - //Do something - stuff.push(MSTREAMPLAYER.playlist[i].filepath); - } - - if(stuff.length == 0){ - $('#share_it').prop("disabled",false); - return; - } - - MSTREAMAPI.makeShared(stuff, shareTimeInDays, function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - $('#share_it').prop("disabled",false); - var adrs = window.location.protocol + '//' + window.location.host + '/shared/playlist/' + response.id; - $('.share-textarea').val(adrs); - }); - }); - - - ////////////////////////////////////// Save/Load playlists - // Save a new playlist - $('#save_playlist_form').on('submit', function(e){ - e.preventDefault(); - - // Check for special characters - if(/^[a-zA-Z0-9-_ ]*$/.test(title) == false) { - console.log('don\'t do that'); - return false; - } - - if(MSTREAMPLAYER.playlist.length == 0){ - // TODO: Alert user nothing was saved - return; - } - - $('#save_playlist').prop("disabled",true); - var title = $('#playlist_name').val(); - - //loop through array and add each file to the playlist - var songs = []; - for (let i = 0; i < MSTREAMPLAYER.playlist.length; i++) { - songs.push(MSTREAMPLAYER.playlist[i].filepath); - } - - MSTREAMAPI.savePlaylist(title, songs, function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - $('#save_playlist').prop("disabled",false); - $('#close_save_playlist').trigger("click"); - }); - }); - - // Get all playlists - $('.get_all_playlists').on('click', function(){ - resetPanel('Playlists', 'scrollBoxHeight2'); - - MSTREAMAPI.getAllPlaylists( function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - // loop through the json array and make an array of corresponding divs - var playlists = []; - $.each(response, function() { - playlists.push('
'+this.name+'x
'); - }); - - // Add playlists to the left panel - $('#filelist').html(playlists); - }); - }); - - // delete playlist - $("#filelist").on('click', '.deletePlaylist', function(){ - // Get Playlist ID - var playlistname = $(this).data('playlistname'); - var that = this; - - MSTREAMAPI.deletePlaylist(playlistname, function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - $(that).parent().remove(); - }); - }); - - // load up a playlist - $("#filelist").on('click', '.playlistz', function() { - var playlistname = $(this).data('playlistname'); - var name = $(this).html(); - - MSTREAMAPI.loadPlaylist(playlistname, function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - // Add the playlist name to the modal - $('#playlist_name').val(name); - - // Clear the playlist - MSTREAMPLAYER.clearPlaylist(); - - // Append the playlist items to the playlist - $.each( response, function(i ,item) { - MSTREAMPLAYER.addSongWizard(item.filepath , {}, true); - }); - }); - }); - - /////////////// Download Playlist - $('#downloadPlaylist').click(function(){ - // Loop through array and add each file to the playlist - var downloadFiles = []; - for (let i = 0; i < MSTREAMPLAYER.playlist.length; i++) { - downloadFiles.push(MSTREAMPLAYER.playlist[i].filepath); - } - - // Use key if necessary - if( MSTREAMAPI.currentServer.token){ - $("#downform").attr("action", "download?token=" + MSTREAMAPI.currentServer.token); - } - - $('').attr({ - type: 'hidden', - name: 'fileArray', - value: JSON.stringify(downloadFiles), - }).appendTo('#downform'); - - //submit form - $('#downform').submit(); - // clear the form - $('#downform').empty(); - }); - - ///////////////////////////// Database Management - // The Manage DB panel - $('#manage_database').on('click', function(){ - resetPanel('Database Management', 'scrollBoxHeight2'); - - MSTREAMAPI.dbStatus( function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - // If there is an error - if(response.error){ - $('#filelist').html('

The database returned the following error:

' + response.error + '

'); - return; - } - // Add Beets Msg - if(response.dbType == 'beets' || response.dbType == 'beets-default' ){ - $('#filelist').append('

Powered by Beets DB

'); - } - // if the DB is locked - if(response.locked){ - $('#filelist').append('

The database is currently being built. Currently ' + response.totalFileCount + ' files are in the DB

'); - return; - } - // If you got this far the db is made and working - $('#filelist').append('

Your DB has ' + response.totalFileCount + ' files

'); - }); - }); - - // Build the database - $('body').on('click', '#build_database', function(){ - $(this).prop("disabled", true); - - MSTREAMAPI.dbScan( function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - // Append the check db button so the user can start checking right away - $('#filelist').append(''); - }); - }); - - // Check DB build progress - $('body').on('click', '#check_db_progress', function(){ - MSTREAMAPI.dbStatus( function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - $( "#db_progress_report" ).remove(); - - // if file_count is 0, report that the the build script is not done counting files - if(response.file_count == 0){ - $('#filelist').append('

The create database script is still counting the files in the music collection. This operation can take some time. Try again in a bit

'); - return; - } - - // Append new

tag with id of "db_progress_report" - $('#filelist').append('

Progress: '+ response.files_in_db +'/'+ response.file_count +'

'); - }); - }); - - - //////////////////////////////////// Sort by Albums - //Load up album explorer - $('.get_all_albums').on('click', function(){ - resetPanel('Albums', 'scrollBoxHeight2'); - - MSTREAMAPI.albums( function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - //parse through the json array and make an array of corresponding divs - var albums = []; - $.each(response.albums, function(index, value) { - albums.push('
'+value+'
'); - }); - - $('#filelist').html(albums); - }); - }); - - // Load up album-songs - $("#filelist").on('click', '.albumz', function() { - var album = $(this).data('album'); - - MSTREAMAPI.albumSongs(album, function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - //clear the list - $('#filelist').empty(); - - //parse through the json array and make an array of corresponding divs - var filelist = []; - $.each(response, function() { - if(this.metadata.title){ - filelist.push('
'+this.metadata.title+'
'); - } - else{ - filelist.push('
'+this.metadata.filename+'
'); - } - }); - - $('#filelist').html(filelist); - }); - }); - - /////////////////////////////////////// Artists - // Load up album-songs - $('.get_all_artists').on('click', function(){ - resetPanel('Artists', 'scrollBoxHeight2'); - - MSTREAMAPI.artists( function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - //parse through the json array and make an array of corresponding divs - var artists = []; - $.each(response.artists, function(index,value) { - artists.push('
'+value+'
'); - }); - - $('#filelist').html(artists); - }); - }); - - - $("#filelist").on('click', '.artistz', function() { - var artist = $(this).data('artist'); - - MSTREAMAPI.artistAlbums(artist, function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - //clear the list - $('#filelist').empty(); - - var albums = []; - $.each(response.albums, function(index, value) { - albums.push('
'+value+'
'); - }); - - $('#filelist').html(albums); - $('.panel_one_name').html('Artists->Albums'); - }); - }); - - - ///////////////////////////// Search Function - // Setup the search interface - $('#search_database').on('click', function(){ - resetPanel('Search', 'scrollBoxHeight1'); - $('#search_container').show(); - }); - - // Auto Search - $('#search_it').on('keyup', function(){ - // TODO: Put this on some kind of time delay. That way rapid keystrokes won't spam the server - if($(this).val().length>1){ - MSTREAMAPI.search($(this).val(), function(response, error){ - if(error !== false){ - return boilerplateFailure(response, error); - } - var htmlString = ''; - - if(response.artists.length > 0){ - htmlString += '

Artists

'; - $.each(response.artists, function(index, value) { - htmlString += '
'+value+'
'; - }); - } - - if(response.albums.length > 0){ - htmlString += '

Albums

'; - $.each(response.albums, function(index, value) { - htmlString += '
'+value+'
'; - }); - } - - $('#filelist').html(htmlString); - }); - } - }); - - - //////////////////////// Jukebox Mode - function setupJukeboxPanel(){ - // Hide the directory bar - resetPanel('Jukebox Mode', 'scrollBoxHeight2'); - - var newHtml; - if(JUKEBOX.stats.live !== false && JUKEBOX.connection !== false){ - newHtml = createJukeboxPanel(); - }else{ - newHtml = '\ -

\ -

\ -

Jukebox Mode allows you to control this page remotely



\ -
CONNECT IT!
\ -

\ - '; - } - - // Add the content - $('#filelist').html(newHtml); - } - - // The jukebox panel - $('#jukebox_mode').on('click', function(){ - setupJukeboxPanel(); - }); - - // Setup Jukebox - $('body').on('click', '.jukebox_connect', function(){ - $(this).prop("disabled", true); - $(this).hide(); - $('.jukebox-loading').toggleClass('hide'); - - JUKEBOX.createWebsocket( MSTREAMAPI.currentServer.token, function(){ - // Wait a while and display the status - setTimeout(function(){ - // TODO: Check that status has changed - setupJukeboxPanel(); - },1800); - }); - }); - - function createJukeboxPanel(){ - var returnHtml = '

'; - - if(JUKEBOX.stats.error !== false){ - return returnHtml + 'An error occurred. Please refresh the page and try again

'; - } - - if(JUKEBOX.stats.adminCode){ - returnHtml += '

Code: ' + JUKEBOX.stats.adminCode + '

'; - } - if(JUKEBOX.stats.guestCode){ - returnHtml += '

Guest Code: ' + JUKEBOX.stats.guestCode + '

'; - } - - var adrs = window.location.protocol + '//' + window.location.host + '/remote'; - returnHtml += '

Remote Jukebox Controls: ' + adrs + '

'; - - return returnHtml + '

'; - } - -}); diff --git a/webapp2/js/mstream.jukebox.js b/webapp2/js/mstream.jukebox.js deleted file mode 100644 index 9d4a636..0000000 --- a/webapp2/js/mstream.jukebox.js +++ /dev/null @@ -1,102 +0,0 @@ -var JUKEBOX = (function () { - let mstreamModule = {}; - - mstreamModule.connection = false; - - // jukebox global variable - mstreamModule.stats = { - // connection: false, - live: false, - guestCode: false, - adminCode: false, - error: false, - accessAddress: false - }; - - // TODO: Move token to the api library - mstreamModule.createWebsocket = function(accessKey, callback){ - if(mstreamModule.stats.live ===true ){ - return false; - } - mstreamModule.stats.live = true; - // if user is running mozilla then use it's built-in WebSocket - window.WebSocket = window.WebSocket || window.MozWebSocket; - - // if browser doesn't support WebSocket, just show some notification and exit - if (!window.WebSocket) { - // TODO: Make a warning - return; - } - - // TODO: Check if websocket has already been created - - // open connection - var l = window.location; - var wsLink = ((l.protocol === "https:") ? "wss://" : "ws://") + l.host + l.pathname; - mstreamModule.connection = new WebSocket(wsLink + 'jukebox/open-connection?token=' + accessKey); - - - - mstreamModule.connection.onopen = function () { - callback(); - }; - - mstreamModule.connection.onerror = function (error) { - // TODO: Error Code - console.log('CONNECTION ERROR!!!!!!!!!!!!'); - }; - - // most important part - incoming messages - mstreamModule.connection.onmessage = function (message) { - // try to parse JSON message. Because we know that the server always returns - // JSON this should work without any problem but we should make sure that - // the message is not chunked or otherwise damaged. - try { - var json = JSON.parse(message.data); - } catch (e) { - return; - } - - // Handle Code - if(json.code){ - mstreamModule.stats.adminCode = json.code; - } - if(json.guestCode){ - mstreamModule.stats.guestCode = json.guestCode; - } - - - if(!json.command){ - return; - } - - if(json.command === 'next'){ - MSTREAMPLAYER.nextSong(); - return; - } - if( json.command === 'playPause'){ - MSTREAMPLAYER.playPause(); - } - if( json.command === 'previous'){ - MSTREAMPLAYER.previousSong(); - return; - } - if( json.command === 'addSong' && json.file){ - MSTREAMPLAYER.addSongWizard(json.file, {}, true); - } - }; - } - - - // TODO: Finish this at some point - // mstreamModule.commandList = { - // 'next': MSTREAMPLAYER.nextSong(), - // 'playPause': MSTREAMPLAYER.playPause(), - // 'previous': MSTREAMPLAYER.previousSong(), - // } - - - - - return mstreamModule; -}()); diff --git a/webapp2/js/mstream.player.js b/webapp2/js/mstream.player.js deleted file mode 100644 index f55caae..0000000 --- a/webapp2/js/mstream.player.js +++ /dev/null @@ -1,765 +0,0 @@ -var MSTREAMPLAYER = (function () { - let mstreamModule = {}; - - - // Playlist variables - mstreamModule.positionCache = {val:-1}; - // var currentSong; - mstreamModule.playlist = []; - - - // The audioData looks like this - // var song = { - // "url":"vPath/path/to/song.mp3?token=xxx", - // "filepath": "path/to/song.mp3" - // } - - - - mstreamModule.addSong = function(audioData){ - if(!audioData.url || audioData.url == false){ - return false; - } - - // Handle shuffle - if(mstreamModule.playerStats.shuffle === true){ - var pos = Math.floor(Math.random() * (shuffleCache.length + 1)); - shuffleCache.splice(pos, 0, song); - } - - return addSongToPlaylist(audioData); - } - - function addSongToPlaylist(song){ - mstreamModule.playlist.push(song); - - // If this the first song in the list - if(mstreamModule.playlist.length === 1){ - mstreamModule.positionCache.val = 0; - return goToSong(mstreamModule.positionCache.val); - } - - // TODO: Check if we are at the end of the playlist and nothing is playing. - // Start playing if this condition is met - - // Cache song if appropriate - var oPlayer = getOtherPlayer(); - if(oPlayer.playerObject === false && mstreamModule.playlist[mstreamModule.positionCache.val + 1]){ - // setCachedSong(mstreamModule.positionCache.val + 1); - - clearTimeout(cacheTimer); - cacheTimer = setTimeout(function(){ setCachedSong(mstreamModule.positionCache.val + 1) } , 3000); - } - - return true; - } - - - - mstreamModule.clearAndPlay = function(song){ - // Clear playlist - mstreamModule.playlist = []; - - return addSong(song); - } - - mstreamModule.clearPlaylist = function(){ - while(mstreamModule.playlist.length > 0) {mstreamModule.playlist.pop();} - mstreamModule.positionCache.val = -1; - - clearEnd(); - - // Clear shuffle as well - if(mstreamModule.playerStats.shuffle === true){ - // Clear Shuffle Cache - while(shuffleCache.length > 0) {shuffleCache.pop();} - } - - return true; - } - - mstreamModule.nextSong = function(){ - // Stop the current song - return goToNextSong(); - } - mstreamModule.previousSong = function(){ - return goToPreviousSong(); - } - - - mstreamModule.goToSongAtPosition = function(position){ - if(!mstreamModule.playlist[position]){ - return false; - } - - - clearEnd(); - - mstreamModule.positionCache.val = position; - return goToSong(mstreamModule.positionCache.val); - } - - // TODO: Log Failures - mstreamModule.removeSongAtPosition = function(position, sanityCheckUrl){ - // Check that position is filled - if (position > mstreamModule.playlist.length || position < 0) { - return false; - } - // If sanityCheckUrl, check that url are the same - if(sanityCheckUrl && sanityCheckUrl != mstreamModule.playlist[position].url){ - return false; - } - - var removedSong = mstreamModule.playlist[position]; - - // Remove song - mstreamModule.playlist.splice(position, 1); - - - if(mstreamModule.playerStats.shuffle === true){ - // Remove song from shuffle Cache - for(var i=0, len=shuffleCache.length; i < len; i++){ - // Check if this is the current song - if(removedSong === shuffleCache[i]){ - shuffleCache.splice(i, 1); - } - } - for(var i=0, len=shufflePrevious.length; i < len; i++){ - // Check if this is the current song - if(removedSong === shufflePrevious[i]){ - shufflePrevious.splice(i, 1); - } - } - - } - - // Handle case where user removes current song and it's the last song in the playlist - if(position === mstreamModule.positionCache.val && position === mstreamModule.playlist.length ){ - clearEnd(); - // Go to random song if random is set - if(mstreamModule.playerStats.shuffle === true){ - goToNextSong(); - }else if(mstreamModule.playerStats.shouldLoop === true){ // Loop is loop is set - mstreamModule.positionCache.val = 0; - goToSong(mstreamModule.positionCache.val); - }else{ // Reset to start is nothing is set - mstreamModule.positionCache.val = -1; - } - }else if(position === mstreamModule.positionCache.val){ // User removes currently playing song - // Go to next song - clearEnd(); - - // If random is set, go to random song - if(mstreamModule.playerStats.shuffle === true){ - goToNextSong(); - }else{ - goToSong(mstreamModule.positionCache.val); - } - - }else if( position < mstreamModule.positionCache.val){ - // Lower positioncache by 1 if necessary - mstreamModule.positionCache.val--; - }else if( position === (mstreamModule.positionCache.val + 1) ){ - // setCachedSong(mstreamModule.positionCache.val + 1); - clearTimeout(cacheTimer); - cacheTimer = setTimeout(function(){ setCachedSong(mstreamModule.positionCache.val + 1) } , 3000); - } - } - - mstreamModule.getCurrentSong = function(){ - var lPlayer = getCurrentPlayer(); - return lPlayer.songObject; - } - - function goToPreviousSong(){ - // TODO: If random is set, go to previous song from cache - if(mstreamModule.playerStats.shuffle === true){ - // TODO: Check that there is a previous song to go back to - if(shufflePrevious.length <= 1){ - return; - } - - // Pop a song and go to the last song - var nextSong = shufflePrevious.pop(); - shuffleCache.push(nextSong); - - var currentSong = shufflePrevious[shufflePrevious.length-1]; - - // Reset position cache - for(var i=0, len=mstreamModule.playlist.length; i < len; i++){ - // Check if this is the current song - if(currentSong === mstreamModule.playlist[i]){ - mstreamModule.positionCache.val = i; - } - } - clearEnd(); - - goToSong(mstreamModule.positionCache.val ); - return; - } - - // Make sure there is a previous song - if(mstreamModule.positionCache.val === 0 || mstreamModule.positionCache.val === -1){ - return false; - } - - // Set previous song and play - clearEnd(); - mstreamModule.positionCache.val--; - return goToSong(mstreamModule.positionCache.val); - } - - function goToNextSong(){ - // If random is set, go to random song - if(mstreamModule.playerStats.shuffle === true){ - // Chose a random value - var nextSong = shuffleCache.pop(); - - // Prevent same song from playing twice after a re-shuffle - if(nextSong === mstreamModule.getCurrentSong()){ - console.log('DUPEEEEE'); - shuffleCache.unshift(nextSong); - nextSong = shuffleCache.pop(); - } - - if(shuffleCache.length === 0){ - newShuffle(); - } - - - // Reset position cache - for(var i=0, len=mstreamModule.playlist.length; i < len; i++){ - // Check if this is the current song - if(nextSong === mstreamModule.playlist[i]){ - mstreamModule.positionCache.val = i; - } - } - clearEnd(); - - goToSong(mstreamModule.positionCache.val ); - - // Remove duplicates from shuffle previous - for(var i=0, len=shufflePrevious.length; i < len; i++){ - // Check if this is the current song - if(nextSong === shufflePrevious[i]){ - shufflePrevious.splice(i, 1); - } - } - - shufflePrevious.push(nextSong); - - // Load selected song - return; - } - - // Check if the next song exists - if(!mstreamModule.playlist[mstreamModule.positionCache.val + 1]){ - - // If loop is set and no other song, go back to first song - if(mstreamModule.playerStats.shouldLoop === true && mstreamModule.playlist.length > 0){ - mstreamModule.positionCache.val = 0; - clearEnd(); - - return goToSong(mstreamModule.positionCache.val); - } - - return false; - } - - - // Load up next song - mstreamModule.positionCache.val++; - clearEnd(); - return goToSong(mstreamModule.positionCache.val); - } - - - function getCurrentPlayer(){ - if(curP === 'A'){ - return playerA; - }else if(curP === 'B'){ - return playerB; - } - - return false; - } - - function getOtherPlayer(){ - if(curP === 'A'){ - return playerB; - }else if(curP === 'B'){ - return playerA; - } - - return false; - } - - function flipFlop(){ - if(curP === 'A'){ - curP = 'B'; - }else if(curP === 'B'){ - curP = 'A'; - } - - return curP; - } - - - function goToSong(position){ - if(!mstreamModule.playlist[position]){ - return false; - } - - var localPlayerObject = getCurrentPlayer(); - var otherPlayerObject = getOtherPlayer(); - - // Stop the current song - if(localPlayerObject.playerType === 'aurora' ){ - localPlayerObject.playerObject.stop(); - }else if(localPlayerObject.playerType === 'howler'){ - localPlayerObject.playerObject.unload(); - } - - // Reset Duration - mstreamModule.playerStats.duration = 0; - mstreamModule.playerStats.currentTime = 0; - - // TODO: Handle situation where next song is same as current song - - // Song is cached - if(otherPlayerObject.songObject === mstreamModule.playlist[position]){ - // console.log('USING CACHED SONG'); - flipFlop(); - // Play - mstreamModule.playPause(); - - }else{ - // console.log('DID NOT USE CACHE'); - setMedia(mstreamModule.playlist[position], localPlayerObject, true); - } - - var lPlayer = getCurrentPlayer(); - var curSong = lPlayer.songObject; - // TODO: Handle instace where metadata is empty - // mstreamModule.playerStats.metadata = curSong.metadata; - if(curSong.metadata){ - mstreamModule.resetCurrentMetadata(); - } - - - // TODO: This is a mess, figure out a better way - var newOtherPlayerObject = getOtherPlayer(); - newOtherPlayerObject.playerType = false; - newOtherPlayerObject.playerObject = false; - newOtherPlayerObject.songObject= false; - - // Cache next song - // The timer prevents excessive cachign when the user starts button mashing - // setCachedSong(position + 1); - clearTimeout(cacheTimer); - cacheTimer = setTimeout(function(){ setCachedSong(position + 1) } , 3000); - return true; - } - - - mstreamModule.resetCurrentMetadata = function() { - var lPlayer = getCurrentPlayer(); - var curSong = lPlayer.songObject; - // TODO: Handle instace where metadata is empty - // mstreamModule.playerStats.metadata = curSong.metadata; - if(curSong.metadata){ - mstreamModule.playerStats.metadata.artist = curSong.metadata.artist; - mstreamModule.playerStats.metadata.album = curSong.metadata.album; - mstreamModule.playerStats.metadata.track = curSong.metadata.track; - mstreamModule.playerStats.metadata.title = curSong.metadata.title; - mstreamModule.playerStats.metadata.year = curSong.metadata.year; - mstreamModule.playerStats.metadata['album-art'] = curSong.metadata['album-art']; - } - - console.log( mstreamModule.playerStats.metadata) - } - - - mstreamModule.resetPositionCache = function(){ - var len; - - var lPlayer = getCurrentPlayer(); - var curSong = lPlayer.songObject; - - for(var i=0, len=mstreamModule.playlist.length; i < len; i++){ - // Check if this is the current song - if(curSong === mstreamModule.playlist[i]){ - mstreamModule.positionCache.val = i; - return; - } - } - - // No song found, reset - mstreamModule.positionCache.val = -1; - } - - - - // ========================= Aurora Player =============== - // Shell for interacting with Aurora - function AVPlayerPlay(){ - var localPlayer = getCurrentPlayer(); - - if(localPlayer.playerObject.playing){ - return; - } - localPlayer.playerObject.play(); - mstreamModule.playerStats.playing = true; - - } - function AVPlayerPause(){ - var localPlayer = getCurrentPlayer(); - - localPlayer.playerObject.pause(); - mstreamModule.playerStats.playing = false; - - } - function AVPlayerPlayPause(){ - var localPlayer = getCurrentPlayer(); - - // TODO: Check that media is loaded - if(localPlayer.playerObject.playing){ - localPlayer.playerObject.pause(); - mstreamModule.playerStats.playing = false; - }else{ - localPlayer.playerObject.play(); - mstreamModule.playerStats.playing = true; - } - } - // ======================================================== - - - - - - // ========================= Howler Player =============== - function howlPlayerPlay(){ - var localPlayer = getCurrentPlayer(); - mstreamModule.playerStats.playing = true; - - localPlayer.playerObject.play(); - } - function howlPlayerPause(){ - var localPlayer = getCurrentPlayer(); - mstreamModule.playerStats.playing = false; - - localPlayer.playerObject.pause(); - } - function howlPlayerPlayPause(){ - var localPlayer = getCurrentPlayer(); - - // TODO: Check that media is loaded - if(localPlayer.playerObject.playing()){ - mstreamModule.playerStats.playing = false; - localPlayer.playerObject.pause(); - }else{ - localPlayer.playerObject.play(); - mstreamModule.playerStats.playing = true; - - } - } - // ======================================================== - - - - // ========================= Youtube Player =============== - var YTPlayer; - // TODO: - // ======================================================== - - - function clearEnd(){ - var localPlayer = getCurrentPlayer(); - - if(localPlayer.playerType === 'aurora' ){ - localPlayer.playerObject.on("end", function() { - return - }, false); - }else if(localPlayer.playerType === 'howler'){ - localPlayer.playerObject.off('end'); - } - } - - - - // Player - // Event: On Song end - // Set Media - // Play, pause, skip, etc - mstreamModule.playPause = function(){ - var localPlayer = getCurrentPlayer(); - - if(localPlayer.playerType === 'aurora' ){ - return AVPlayerPlayPause(); - }else if(localPlayer.playerType === 'howler'){ - return howlPlayerPlayPause(); - } - } - - - mstreamModule.playerStats = { - duration:0, - currentTime:0, - playing: false, - repeat: false, - shuffle:false, - metadata: { - "artist": false, - "album": false, - "track": false, - "title": false, - "year": false, - "album-art": false, - "filepath": false, - } - } - - var playerA = { - playerType: false, - playerObject: false, - songObject: false - } - var playerB = { - playerType: false, - playerObject: false, - songObject: false - } - - var curP = 'A'; - - function setMedia(song, player, play){ - - - if(song.url.indexOf('.flac') !== -1 && Howler.codecs('flac') === false ){ - // Set via aurora - player.playerType = 'aurora'; - - - player.playerObject = AV.Player.fromURL(song.url); - player.playerObject.on("end", function() { - callMeOnStreamEnd(); - }, false); - // Handle error event - player.playerObject.on("error", function(e) { - // TODO: GO TO NEXT SONG - }, false); - player.playerObject.on("metadata", function() { - // Move this to metadata ??? - if(play == true){ - AVPlayerPlay(); - } - }, false); - - player.playerObject.preload(); - - - - - }else{ - player.playerType = 'howler'; - - player.playerObject = new Howl({ - src: [song.url], - html5: true, // Force to HTML5. Otherwise streaming will suck - // onplay: function() { }, - onload: function() { - - }, - onend: function() { - callMeOnStreamEnd(); - }, - onpause: function() { - }, - onstop: function() { - }, - onplay: function(){ - } - }); - - if(play == true){ - howlPlayerPlay(); - } - } - - player.songObject = song; - } - - - - function callMeOnStreamEnd(){ - mstreamModule.playerStats.playing= false; - - // Go to next song - goToNextSong(); - } - - - - - -// NOTE: Seektime is in seconds -mstreamModule.seek = function(seekTime){ - var lPlayer = getCurrentPlayer(); - if(lPlayer.playerType === 'aurora' ){ - // Do nothing, auroradoesn't support seeking right now - return false; - }else if(lPlayer.playerType === 'howler'){ - // Check that the seek number is less than the duration - if(seekTime < 0 || seekTime > lPlayer.playerObject._duration){ - return false; - } - - lPlayer.playerObject.seek(seektime) - } - -} - -mstreamModule.seekByPercentage = function(percentage){ - if(percentage < 0 || percentage > 99){ - return false; - } - var lPlayer = getCurrentPlayer(); - - if(lPlayer.playerType === 'aurora' ){ - // Do nothing, auroradoesn't support seeking - return false; - }else if(lPlayer.playerType === 'howler'){ - var seektime = (percentage * lPlayer.playerObject._duration)/ 100; - lPlayer.playerObject.seek(seektime) - } - -} - - - - var timers = {}; - - function startTime(interval) { - if (timers.sliderUpdateInterval) { clearInterval(timers.sliderUpdateInterval); } - - - timers.sliderUpdateInterval = setInterval( function(){ - var lPlayer = getCurrentPlayer(); - - if(lPlayer.playerType === 'aurora' ){ - mstreamModule.playerStats.duration = lPlayer.playerObject.duration / 1000; - mstreamModule.playerStats.currentTime = lPlayer.playerObject.currentTime / 1000; - }else if(lPlayer.playerType === 'howler'){ - mstreamModule.playerStats.currentTime = lPlayer.playerObject.seek(); - mstreamModule.playerStats.duration = lPlayer.playerObject._duration; - - }else{ - // NO PLAYER, set default values - mstreamModule.playerStats.currentTime = 0; - mstreamModule.playerStats.duration = 0; - } - - }, interval); - } - startTime(100); - - function clearTimer(){ - clearInterval(timers.sliderUpdateInterval); - } - - - // Timer for caching. Helps prevent excess cahing due to button mashing - var cacheTimer; - function setCachedSong(position){ - - console.log(' ATTEMPTING TO CACHE'); - if(!mstreamModule.playlist[position]){ - console.log(' FAILED TO CACHE'); - return false; - } - - var oPlayer = getOtherPlayer(); - setMedia(mstreamModule.playlist[position], oPlayer, false); - console.log(' IT CACHED!!!!!!'); - console.log(mstreamModule.playlist[position]); - - return true; - } - - - // Loop - mstreamModule.playerStats.shouldLoop = false; - mstreamModule.setRepeat = function(newValue){ - if(typeof(newValue) != "boolean"){ - return false; - } - mstreamModule.playerStats.shouldLoop = newValue; - return newValue; - } - mstreamModule.toggleRepeat = function(){ - mstreamModule.playerStats.shouldLoop = !mstreamModule.playerStats.shouldLoop; - return mstreamModule.playerStats.shouldLoop; - } - - // Random Song - mstreamModule.playerStats.shuffle = false; - shuffleCache = []; // Cache the last 5 songs played to avoid repeats - shufflePrevious = []; - mstreamModule.setShuffle = function(newValue){ - if(typeof(newValue) != "boolean"){ - return false; - } - - if(newValue===true){ - newShuffle(); - }else{ - turnShuffleOff(); - } - - mstreamModule.playerStats.shuffle = newValue; - return true; - } - mstreamModule.toggleShuffle = function(){ - mstreamModule.playerStats.shuffle = !mstreamModule.playerStats.shuffle; - if(mstreamModule.playerStats.shuffle === true){ - newShuffle(); - }else{ - turnShuffleOff(); - } - return mstreamModule.playerStats.shuffle; - } - - function newShuffle(){ - shuffleCache = shuffle(mstreamModule.playlist.slice(0)); - } - - function turnShuffleOff(){ - shufflePrevious = []; - shuffleCache = []; - } - - function shuffle(array) { - var currentIndex = array.length - , temporaryValue - , randomIndex - ; - - // While there remain elements to shuffle... - while (0 !== currentIndex) { - - // Pick a remaining element... - randomIndex = Math.floor(Math.random() * currentIndex); - currentIndex -= 1; - - // And swap it with the current element. - temporaryValue = array[currentIndex]; - array[currentIndex] = array[randomIndex]; - array[randomIndex] = temporaryValue; - } - - return array; - } - - // Return an object that is assigned to Module - return mstreamModule; -}()); diff --git a/webapp2/js/mstream.vue-browser.js b/webapp2/js/mstream.vue-browser.js deleted file mode 100644 index 549d126..0000000 --- a/webapp2/js/mstream.vue-browser.js +++ /dev/null @@ -1,86 +0,0 @@ -var VUEBROWSER = function() { - - // Auto Focus - Vue.directive('focus', { - // When the bound element is inserted into the DOM... - inserted: function (el) { - // Focus the element - el.focus() - } - }); - - - var loginPanel = new Vue({ - el: '#login-overlay', - data: { - needToLogin: false, - error: false, - errorMessage: 'Login Failed', - pending: false - }, - methods: { - submitCode: function(e){ - // Get Code - this.pending = true; - var that = this; - MSTREAMAPI.login($('#login-username').val(), $('#login-password').val(), function(response, error){ - if(error !== false){ - // Alert the user - that.pending = false; - that.error = true; - return; - } - - // Eye-candy: change the error message - that.errorMessage = "Welcome To mStream!"; - - // Add the token to the cookies - Cookies.set('token', response.token); - - // Add the token the URL calls - MSTREAMAPI.updateCurrentServer($('#login-username').val(), response.token, response.vPath) - - // TODO: Add function to load up either the file browser or artist panel - - // Remove the overlay - $('.login-overlay').fadeOut( "slow" ); // TDO: Figure out how to use Vue to fade the modal in and out - that.pending = false; - that.needToLogin = false; - }); - } - } - }); - - - function testIt(token){ - if(token){ - MSTREAMAPI.currentServer.token = token; - } - - MSTREAMAPI.ping( function(response, error){ - if(error !== false){ - // NOTE: There needs to be a split here - // For the webapp we simply display the login panel - loginPanel.needToLogin = true; - // TODO: Move this transitionstuff to vue - $('.login-overlay').fadeIn( "slow" ); - // For electron we need to alert the user that user it failed and guide them to the login form - - return; - } - // set vPath - MSTREAMAPI.currentServer.vPath = response.vPath; - - // TODO: Add function to load up either the file browser or artist panel - - }); - } - - // NOTE: There needs to be a split here - // For the normal webap we just get the token - // var token = Cookies.get('token'); - testIt(Cookies.get('token')); - // For electron we need to pull it from wherever electron stores things - - -}; diff --git a/webapp2/js/mstream.vue-player-controls.js b/webapp2/js/mstream.vue-player-controls.js deleted file mode 100644 index 9ac16aa..0000000 --- a/webapp2/js/mstream.vue-player-controls.js +++ /dev/null @@ -1,235 +0,0 @@ -var VUEPLAYER = function() { - - // Template for playlist items - Vue.component('playlist-item', { - template: '\ -
\ - {{ comtext }} X\ -
\ - ', - - props: [ 'index', 'song'], - - // We need the positionCache to track the currently playing song - data: function(){ - return { - positionCache: MSTREAMPLAYER.positionCache, - } - }, - - // Methods used by playlist item events - methods: { - // Go to a song on item click - goToSong: function(event){ - MSTREAMPLAYER.goToSongAtPosition(this.index); - }, - // Remove song - removeSong: function(event){ - MSTREAMPLAYER.removeSongAtPosition(this.index, false); - } - }, - - computed: { - comtext: function() { - var returnThis = this.song.filepath; - - if(this.song.metadata.title){ - returnThis = this.song.metadata.title; - if(this.song.metadata.artist){ - returnThis = this.song.metadata.artist + ' - ' + returnThis; - } - - } - - return returnThis; - } - } - }); - - // Code to update playlist - var playlistElement = new Vue({ - el: '#playlist', - data: { - playlist: MSTREAMPLAYER.playlist, - }, - methods: { - // checkMove is called when a drag-and-drop action happens - checkMove: function (event) { - MSTREAMPLAYER.resetPositionCache(); - } - } - }); - - - var progressBar = new Vue({ - el: '#mstream-player', - data: { - playerStats: MSTREAMPLAYER.playerStats, - playlist: MSTREAMPLAYER.playlist, - positionCache: MSTREAMPLAYER.positionCache, - met: MSTREAMPLAYER.playerStats.metadata - }, - computed: { - imgsrc: function () { - return "/public/img/"+(this.playerStats.playing ? 'pause' : 'play')+"-white.svg"; - }, - widthcss: function ( ) { - if(this.playerStats.duration === 0){ - return "width:0"; - } - - var percentage = 100 - (( this.playerStats.currentTime / this.playerStats.duration) * 100); - return "width:calc(100% - "+percentage+"%)"; - }, - - showTime: function(){ - if (this.playerStats.duration === 0) { - return ''; - } - - var curr = this.playerStats.duration - this.playerStats.currentTime; - var minutes = Math.floor(curr / 60); - var secondsToCalc = Math.floor(curr % 60) + ''; - var currentText = minutes + ':' + (secondsToCalc.length < 2 ? '0' + secondsToCalc : secondsToCalc); - - return currentText; - }, - - currentSongText: function(){ - // TODO: Handle metadata - - // Call these vars so updates cahnge whenever they do - var posit = this.positionCache.val; - var plist = this.playlist; - var playerStats = this.playerStats; - var titleX = this.met.title; - var metx = this.met; - - - - var currentSong = MSTREAMPLAYER.getCurrentSong(); - - if(currentSong === false){ - return '\u00A0\u00A0\u00A0Welcome To mStream!\u00A0\u00A0\u00A0'; - } - - // Get current song straight from the source - var returnText = ''; - if(playerStats.metadata && titleX){ - returnText = titleX; - if(playerStats.metadata.artist){ - returnText = playerStats.metadata.artist + ' - ' + returnText; - } - }else{ - // Use filepath instead - var filepathArray = currentSong.filepath.split("/"); - returnText = filepathArray[filepathArray.length-1] - } - - console.log(MSTREAMPLAYER.playerStats.metadata); - - - return '\u00A0\u00A0\u00A0' + returnText + '\u00A0\u00A0\u00A0'; - } - }, - methods: { - toggleRepeat: function(){ - MSTREAMPLAYER.toggleRepeat(); - }, - toggleShuffle: function(){ - MSTREAMPLAYER.toggleShuffle(); - } - } - }); - - - var metadataPanel = new Vue({ - el: '#metadata-panel', - data: { - meta: MSTREAMPLAYER.playerStats.metadata - }, - computed: { - albumArtPath: function(){ - if(!this.meta['album-art']){ - return '/public/img/default.png'; - } - return '/album-art/' + this.meta['album-art']; - } - } - - }); - - - - - - // Button Events - document.getElementById( "progress-bar" ).addEventListener("click",function(event) { - var relativeClickPosition = event.clientX - this.getBoundingClientRect().left; - var totalWidth = this.getBoundingClientRect().width; - var percentage = (relativeClickPosition / totalWidth) * 100; - // Set Player time - MSTREAMPLAYER.seekByPercentage(percentage); - }); - - // Button Events - document.getElementById( "next-button" ).addEventListener("click",function() { - MSTREAMPLAYER.nextSong(); - }); - document.getElementById( "play-pause-button" ).addEventListener("click", function() { - MSTREAMPLAYER.playPause(); - }); - document.getElementById("previous-button").addEventListener("click", function(){ - MSTREAMPLAYER.previousSong(); - }); - - // This makes the title text scroll back and forth - var scrollTimer; - var scrollRight = true; //Track Scroll Direction - function startTime(interval) { - if (scrollTimer) { clearInterval(scrollTimer); } - - scrollTimer = setInterval( function(){ - // Get the max scroll distance - var maxScrollLeft = document.getElementById('title-text').scrollWidth - document.getElementById('title-text').clientWidth; - - // Change the scroll direction if necessary - // TODO: Pause for a second when these conditions are hit - if(document.getElementById('title-text').scrollLeft > (maxScrollLeft - 1)){ - scrollRight = false; - } - if(document.getElementById('title-text').scrollLeft === 0){ - scrollRight = true; - } - - // Do the scroll - if(scrollRight === true){ - document.getElementById('title-text').scrollLeft = document.getElementById('title-text').scrollLeft + 2; - }else{ - document.getElementById('title-text').scrollLeft = document.getElementById('title-text').scrollLeft - 2; - } - }, interval); - } - startTime(50); - - - - // Change spacebar behviour to Play/PauseListen to every key press user makes - // Useful for adding media functionality to certain keys - window.addEventListener("keydown", function(event){ - // Use default behavior if user is in a form - var element = event.target.tagName.toLowerCase(); - if(element == 'input' || element == 'textarea'){ - return; - } - - // Check the key - switch (event.keyCode) { - case 32: //SpaceBar - event.preventDefault(); - MSTREAMPLAYER.playPause(); - break; - } - return false; - }, false); -}; diff --git a/webapp2/mstream.html b/webapp2/mstream.html deleted file mode 100644 index e449de9..0000000 --- a/webapp2/mstream.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - mStream Media Player - All your media. Everywhere you go. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- The rest of the webapp goes here -
- diff --git a/webapp2/mstreamdb.lite b/webapp2/mstreamdb.lite deleted file mode 100644 index f3228d4..0000000 Binary files a/webapp2/mstreamdb.lite and /dev/null differ