From 0f72c6fbc5fb3001ea1ab5961dd277681b695f6c Mon Sep 17 00:00:00 2001 From: kelvinabrokwa Date: Tue, 17 Oct 2017 08:09:33 -0400 Subject: [PATCH 1/8] bowerize --- bower.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 bower.json diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..9cb9e81 --- /dev/null +++ b/bower.json @@ -0,0 +1,22 @@ +{ + "name": "leaflet-vectortiles", + "description": "Leaflet vector tile layer", + "main": "dist/leaflet.vector-tiles.js", + "authors": [ + "Kelvin Abrokwa" + ], + "license": "ISC", + "keywords": [ + "leaflet", + "vector", + "tiles" + ], + "homepage": "https://github.com/NBTSolutions/Leaflet.VectorTiles", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} From 6318c7043ac8aa5a4d693a70c2c409599fd55701 Mon Sep 17 00:00:00 2001 From: kelvinabrokwa Date: Tue, 14 Nov 2017 09:42:53 -0500 Subject: [PATCH 2/8] use webworker for fetching and parsing vector tiles --- Leaflet.VectorTiles.js | 256 ++++++++++++++++------------------------- package-lock.json | 69 +++++------ package.json | 3 +- tile.js | 2 + worker.js | 41 +++++++ 5 files changed, 179 insertions(+), 192 deletions(-) create mode 100644 worker.js diff --git a/Leaflet.VectorTiles.js b/Leaflet.VectorTiles.js index 4193561..d99dba2 100644 --- a/Leaflet.VectorTiles.js +++ b/Leaflet.VectorTiles.js @@ -3,6 +3,7 @@ import TileCache from './tile_cache'; import Feature from './feature'; import { VectorTile } from '@mapbox/vector-tile'; import Pbf from 'pbf'; +import work from 'webworkify'; /** @@ -96,18 +97,16 @@ L.VectorTiles = L.GridLayer.extend({ // maps feature ids to booleans this._featureOnMap = {}; - // mark a tile for destruction in case it is unloaded before it loads - this._toDestroy = {}; - // tile cache this._tileCache = new TileCache(50, this._debug); // mark a tile as loaded // this is needed because if a tile is unloaded before its finished loading // we need to wait for it to finish loading before we can clean up - this.on('vt_tileload', (e) => { + this.on('vt_tileload', e => { const tileKey = this._tileCoordsToKey(e.coords); - if (this._toDestroy[tileKey]) { + const tile = this._vectorTiles[tileKey]; + if (tile.destroy) { this.destroyTile(e.coords); } }); @@ -129,12 +128,19 @@ L.VectorTiles = L.GridLayer.extend({ if (this._debug) { console.log('marking tile', e.coords, 'for deletion after loading'); } - this._toDestroy[tileKey] = true; + this._vectorTiles[tileKey].destroy = true; } else { // destroy it immediately this.destroyTile(e.coords); } }); + + // web worker + this.worker = work(require('./worker')); + this.worker.addEventListener('message', e => { + const { coords, features } = e.data; + this._renderVectorTile(features, coords); + }); }, onAdd(map) { @@ -184,174 +190,111 @@ L.VectorTiles = L.GridLayer.extend({ * @returns DOM element * @private */ - createTile(coords, done) { + createTile(coords) { if (this._debug) { console.log("creating tile:", coords); } - const tile = L.DomUtil.create('div', 'leaflet-tile'); - if (this.options.debug) { - // show tile boundaries - tile.style.outline = '1px solid red'; - } - this._createTile(coords); - done(null, tile); - return tile; - }, - _createTile(coords) { const tileKey = this._tileCoordsToKey(coords); - // tile has already been unloaded - if (this._toDestroy[tileKey]) { - return; - } - + // check cache for tile let tile = this._tileCache.get(tileKey); - if (!tile) { tile = new Tile(coords.x, coords.y, coords.z); } this._vectorTiles[tileKey] = tile; - // fetch vector tile data for this tile const url = L.Util.template(this._url, coords); + + // assign tile to a worker + this.worker.postMessage({ url, coords }); + const headers = new Headers(); - if (tile.timeCreated) { - headers.append('If-Modified-Since', tile.timeCreated); - } - fetch(url, { headers }) - .then(res => { - // use cached tile - if (res.status == '304') { - // add tile to FeatureGroup to add to map - tile.addTo(this._featureGroup); - return; + headers.append('If-Modified-Since', tile.timestamp); + + // create an empty div for passing back to GridLayer + const div = L.DomUtil.create('div'); + return div; + }, + + _renderVectorTile(features, coords) { + const tileKey = this._tileCoordsToKey(coords); + const tile = this._vectorTiles[tileKey]; + + for (let geojson of features) { + const id = this.options.getFeatureId(geojson); + const layer = this._geojsonToLayer(geojson); + if (!layer) { + // unsupported geometry type + continue; + } + + // create the Feature + const feature = new Feature(id, geojson, layer); + + // add it to the tile + tile.addFeature(feature); + + // calculate its style and if its visible + const style = {}; + let onMap = true; + let prop; + + // property based styles + for (prop in geojson.properties) { + // apply style from options + if (prop in this.options.style + && geojson.properties[prop] in this.options.style[prop]) { + Object.assign(style, this.options.style[prop][geojson.properties[prop]]); } - // record time that tile was retrieved - tile.timeCreated = new Date().getTime(); - - // parse new vector tile - res.blob() - .then(blob => { - const reader = new FileReader(); - return new Promise((resolve, reject) => { - reader.onloadend = () => { - resolve(new VectorTile(new Pbf(reader.result))); - } - reader.readAsArrayBuffer(blob); - }); - }) - .then(function parseVectorTile(vtTile) { - for (const vtLayerName in vtTile.layers) { - // break out if this tile has already be unloaded - if (this._toDestroy[tileKey]) { - if (this._debug) { - console.log('Tile', coords, 'stopped while loading'); - } - break; - } - - const vtLayer = vtTile.layers[vtLayerName]; - - for (let j = 0; j < vtLayer.length; j++) { - // break out if this tile has already be unloaded - if (this._toDestroy[tileKey]) { - if (this._debug) { - console.log('Tile', coords, 'stopped while loading'); - } - break; - } - - const vtFeature = vtLayer.feature(j); - - const geojson = vtFeature.toGeoJSON(coords.x, coords.y, coords.z); - const id = this.options.getFeatureId(geojson); - const layer = this._geojsonToLayer(geojson); - if (!layer) { - // unsupported geometry type - continue; - } - - // create the Feature - const feature = new Feature(id, geojson, layer); - - // add it to the tile - tile.addFeature(feature); - - // calculate its style and if its visible - const style = {}; - let onMap = true; - let prop; - - // property based styles - for (prop in geojson.properties) { - // apply style from options - if (prop in this.options.style - && geojson.properties[prop] in this.options.style[prop]) { - Object.assign(style, this.options.style[prop][geojson.properties[prop]]); - } - - // apply style modifications - if (prop in this._propertyStyles - && geojson.properties[prop] in this._propertyStyles[prop]) { - Object.assign(style, this._propertyStyles[prop][geojson.properties[prop]]); - } - - // put on map based on property - if (prop in this._propertyOnMap - && geojson.properties[prop] in this._propertyOnMap[prop]) { - onMap = this._propertyOnMap[prop][geojson.properties[prop]]; - } - } - - // feature based styles - if (id in this._featureStyles) { - Object.assign(style, this._featureStyles[id]); - } - - feature.setStyle(style); - - // feature based on map - if (id in this._featureOnMap) { - onMap = this._featureOnMap[id]; - } - - feature.putOnMap(onMap); - } - } + // apply style modifications + if (prop in this._propertyStyles + && geojson.properties[prop] in this._propertyStyles[prop]) { + Object.assign(style, this._propertyStyles[prop][geojson.properties[prop]]); + } - if (!this._toDestroy[tileKey]) { - // called when all features have been added to the tile - tile.init(); + // put on map based on property + if (prop in this._propertyOnMap + && geojson.properties[prop] in this._propertyOnMap[prop]) { + onMap = this._propertyOnMap[prop][geojson.properties[prop]]; + } + } - // add the featureGroup of this tile to the map - tile.addTo(this._featureGroup); - } + // feature based styles + if (id in this._featureStyles) { + Object.assign(style, this._featureStyles[id]); + } + + feature.setStyle(style); - // mark tile as loaded - tile.markAsLoaded(); + // feature based on map + if (id in this._featureOnMap) { + onMap = this._featureOnMap[id]; + } - // cache the tile - this._tileCache.put(tileKey, tile); + feature.putOnMap(onMap); + } - if (this._debug) { - console.log('tile loaded:', coords, tile.featureGroup.getLayers().length, ' features'); - } + if (!tile.destroy) { + // called when all features have been added to the tile + tile.init(); + + // add the featureGroup of this tile to the map + tile.addTo(this._featureGroup); + } - // the tile has ~actually~ loaded - // the `tileload` event doesn't fire when `tileunload` fires first - // but in our case we still need to be finished loading to clean up - this.fire('vt_tileload', { coords }); - }.bind(this)) - .catch(err => { - console.log(err); - }); - }) - .catch(err => { - console.log(err); - }); + // mark tile as loaded + tile.markAsLoaded(); + + // cache the tile + this._tileCache.put(tileKey, tile); + + // the tile has ~actually~ loaded + // the `tileload` event doesn't fire when `tileunload` fires first + // but in our case we still need to be finished loading to clean up + this.fire('vt_tileload', { coords }); }, /** @@ -371,13 +314,8 @@ L.VectorTiles = L.GridLayer.extend({ // remove this tile's FeatureGroup from the map tile.removeFrom(this._featureGroup); - // delete the tile's data + // delete the tile delete this._vectorTiles[tileKey]; - - // remove delete marker - if (this._toDestroy[tileKey]) { - delete this._toDestroy[tileKey]; - } }, /** @@ -583,8 +521,8 @@ L.VectorTiles = L.GridLayer.extend({ case 'LineString': coords = []; - for (let i = 0; i < c.length; i++) { - coords.push([c[i][1], c[i][0]]); + for (let p of c) { + coords.push([p[1], p[0]]); } layer = L.polyline(coords, {}); break; diff --git a/package-lock.json b/package-lock.json index 34acee5..0f632db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,16 @@ "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-4.6.0.tgz", "integrity": "sha1-DT+aIY5Y0cXl3u30Z8MyHdYSA/M=" }, + "JSONStream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", + "dev": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, "acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", @@ -1325,9 +1335,9 @@ "integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=", "dev": true, "requires": { + "JSONStream": "1.3.1", "combine-source-map": "0.7.2", "defined": "1.0.0", - "JSONStream": "1.3.1", "through2": "2.0.3", "umd": "3.0.1" } @@ -1355,6 +1365,7 @@ "integrity": "sha1-CJo0Y69Y0OSNjNQHCz90ZU1avKk=", "dev": true, "requires": { + "JSONStream": "1.3.1", "assert": "1.4.1", "browser-pack": "6.0.2", "browser-resolve": "1.11.2", @@ -1376,7 +1387,6 @@ "https-browserify": "1.0.0", "inherits": "2.0.3", "insert-module-globals": "7.0.1", - "JSONStream": "1.3.1", "labeled-stream-splicer": "2.0.0", "module-deps": "4.1.1", "os-browserify": "0.1.2", @@ -2535,13 +2545,13 @@ "integrity": "sha1-ElGkuixEqS32mJvQKdoSGk8hCbA=", "dev": true, "requires": { + "JSONStream": "1.3.1", "browser-resolve": "1.11.2", "concat-stream": "1.5.2", "defined": "1.0.0", "detective": "4.5.0", "duplexer2": "0.1.4", "inherits": "2.0.3", - "JSONStream": "1.3.1", "parents": "1.0.1", "readable-stream": "2.3.3", "resolve": "1.4.0", @@ -4016,14 +4026,6 @@ } } }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, "string-width": { "version": "1.0.2", "bundled": true, @@ -4034,6 +4036,14 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, "stringstream": { "version": "0.0.5", "bundled": true, @@ -4776,10 +4786,10 @@ "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=", "dev": true, "requires": { + "JSONStream": "1.3.1", "combine-source-map": "0.7.2", "concat-stream": "1.5.2", "is-buffer": "1.1.5", - "JSONStream": "1.3.1", "lexical-scope": "1.2.0", "process": "0.11.10", "through2": "2.0.3", @@ -5241,16 +5251,6 @@ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", "dev": true }, - "JSONStream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", - "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", - "dev": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - } - }, "kebab-case": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.0.tgz", @@ -5659,6 +5659,7 @@ "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", "dev": true, "requires": { + "JSONStream": "1.3.1", "browser-resolve": "1.11.2", "cached-path-relative": "1.0.1", "concat-stream": "1.5.2", @@ -5666,7 +5667,6 @@ "detective": "4.5.0", "duplexer2": "0.1.4", "inherits": "2.0.3", - "JSONStream": "1.3.1", "parents": "1.0.1", "readable-stream": "2.3.3", "resolve": "1.4.0", @@ -7374,15 +7374,6 @@ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", "dev": true }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "string-template": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", @@ -7400,6 +7391,15 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "stringify-entities": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.1.tgz", @@ -8216,6 +8216,11 @@ "integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=", "dev": true }, + "webworkify": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/webworkify/-/webworkify-1.5.0.tgz", + "integrity": "sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g==" + }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", diff --git a/package.json b/package.json index 7077bc3..2302bc9 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@mapbox/vector-tile": "^1.3.0", "@turf/bbox": "^4.3.0", "pbf": "^3.0.5", - "rbush": "^2.0.1" + "rbush": "^2.0.1", + "webworkify": "^1.5.0" } } diff --git a/tile.js b/tile.js index 3b2c477..be9bd34 100644 --- a/tile.js +++ b/tile.js @@ -23,7 +23,9 @@ export default class Tile { this.coords = { x, y, z }; this._features = {}; + this.timestamp = new Date().getTime(); // tracks when the tile was created this.loaded = false; + this.destroy = false; // used in tile mark/sweep this._index = rbush(); this.featureGroup = L.featureGroup(); diff --git a/worker.js b/worker.js new file mode 100644 index 0000000..7685691 --- /dev/null +++ b/worker.js @@ -0,0 +1,41 @@ +/** + * + */ +import Pbf from 'pbf'; +import { VectorTile } from '@mapbox/vector-tile'; + +/** + * Fetches and processes the vector tile for a given tile coordinate + */ +module.exports = function(self) { + self.addEventListener('message', e => { + const { url, coords } = e.data; + fetch(url) + .then(res => res.blob()) + .then(blob => { + const reader = new FileReader(); + reader.onloadend = () => { + const features = getFeatures(new VectorTile(new Pbf(reader.result)), coords); + self.postMessage({ coords, features }); + } + reader.readAsArrayBuffer(blob); + }); + }); +}; + +/** + * Projects vector tile data to lat/lng coordinates and returns a list of + * GeoJSON features + */ +function getFeatures(vectorTile, coords) { + const features = []; + for (const layerName in vectorTile.layers) { + const layer = vectorTile.layers[layerName]; + for (let i = 0; i < layer.length; i++) { + const feature = layer.feature(i); + const geojson = feature.toGeoJSON(coords.x, coords.y, coords.z); + features.push(geojson); + } + } + return features; +} From 94be0ee7e022c75314a4c7d90bc6c1a734d2d2b6 Mon Sep 17 00:00:00 2001 From: kelvinabrokwa Date: Tue, 14 Nov 2017 16:32:57 -0500 Subject: [PATCH 3/8] organize tile data structure by layer name --- Leaflet.VectorTiles.js | 95 +++++++++++++++-------------- feature.js | 7 ++- tile.js | 132 +++++++++++++++++++++++------------------ worker.js | 5 +- 4 files changed, 133 insertions(+), 106 deletions(-) diff --git a/Leaflet.VectorTiles.js b/Leaflet.VectorTiles.js index d99dba2..dc551dc 100644 --- a/Leaflet.VectorTiles.js +++ b/Leaflet.VectorTiles.js @@ -222,59 +222,67 @@ L.VectorTiles = L.GridLayer.extend({ const tileKey = this._tileCoordsToKey(coords); const tile = this._vectorTiles[tileKey]; - for (let geojson of features) { - const id = this.options.getFeatureId(geojson); - const layer = this._geojsonToLayer(geojson); - if (!layer) { - // unsupported geometry type - continue; + for (let layerName in features) { + if (tile.destroy) { + break; } + for (let geojson of features[layerName]) { + if (tile.destroy) { + break; + } + const id = this.options.getFeatureId(geojson); + const leafletLayer = this._geojsonToLeafletLayer(geojson); + if (!leafletLayer) { + // unsupported geometry type + continue; + } - // create the Feature - const feature = new Feature(id, geojson, layer); + // create the Feature + const feature = new Feature(id, layerName, geojson, leafletLayer); - // add it to the tile - tile.addFeature(feature); + // add it to the tile + tile.addFeature(feature); - // calculate its style and if its visible - const style = {}; - let onMap = true; - let prop; + // calculate its style and if its visible + const style = {}; + let onMap = true; + let prop; - // property based styles - for (prop in geojson.properties) { - // apply style from options - if (prop in this.options.style - && geojson.properties[prop] in this.options.style[prop]) { - Object.assign(style, this.options.style[prop][geojson.properties[prop]]); - } + // property based styles + for (prop in geojson.properties) { + // apply style from options + if (prop in this.options.style + && geojson.properties[prop] in this.options.style[prop]) { + Object.assign(style, this.options.style[prop][geojson.properties[prop]]); + } + + // apply style modifications + if (prop in this._propertyStyles + && geojson.properties[prop] in this._propertyStyles[prop]) { + Object.assign(style, this._propertyStyles[prop][geojson.properties[prop]]); + } - // apply style modifications - if (prop in this._propertyStyles - && geojson.properties[prop] in this._propertyStyles[prop]) { - Object.assign(style, this._propertyStyles[prop][geojson.properties[prop]]); + // put on map based on property + if (prop in this._propertyOnMap + && geojson.properties[prop] in this._propertyOnMap[prop]) { + onMap = this._propertyOnMap[prop][geojson.properties[prop]]; + } } - // put on map based on property - if (prop in this._propertyOnMap - && geojson.properties[prop] in this._propertyOnMap[prop]) { - onMap = this._propertyOnMap[prop][geojson.properties[prop]]; + // feature based styles + if (id in this._featureStyles) { + Object.assign(style, this._featureStyles[id]); } - } - // feature based styles - if (id in this._featureStyles) { - Object.assign(style, this._featureStyles[id]); - } + feature.setStyle(style); - feature.setStyle(style); + // feature based on map + if (id in this._featureOnMap) { + onMap = this._featureOnMap[id]; + } - // feature based on map - if (id in this._featureOnMap) { - onMap = this._featureOnMap[id]; + feature.putOnMap(onMap); } - - feature.putOnMap(onMap); } if (!tile.destroy) { @@ -283,13 +291,14 @@ L.VectorTiles = L.GridLayer.extend({ // add the featureGroup of this tile to the map tile.addTo(this._featureGroup); + + // cache the tile + this._tileCache.put(tileKey, tile); } // mark tile as loaded tile.markAsLoaded(); - // cache the tile - this._tileCache.put(tileKey, tile); // the tile has ~actually~ loaded // the `tileload` event doesn't fire when `tileunload` fires first @@ -507,7 +516,7 @@ L.VectorTiles = L.GridLayer.extend({ * @returns {L.Path} * @private */ - _geojsonToLayer(feature) { + _geojsonToLeafletLayer(feature) { let layer; let coords; let ring; diff --git a/feature.js b/feature.js index 95389e8..7efd1a9 100644 --- a/feature.js +++ b/feature.js @@ -5,10 +5,11 @@ * @private */ export default class Feature { - constructor(id, geojson, layer) { + constructor(id, layer, geojson, leafletLayer) { this.id = id; - this.geojson = geojson; this.layer = layer; + this.geojson = geojson; + this.leafletLayer = leafletLayer; this.onMap = true; this.style = {}; @@ -23,7 +24,7 @@ export default class Feature { */ setStyle(style) { Object.assign(this.style, style); - this.layer.setStyle(this.style); + this.leafletLayer.setStyle(this.style); return this; } diff --git a/tile.js b/tile.js index be9bd34..0d6903c 100644 --- a/tile.js +++ b/tile.js @@ -46,39 +46,38 @@ export default class Tile { */ indexFeatures() { const bboxes = []; - for (const id in this._features) { - if (!this._features.hasOwnProperty(id)) { - continue; - } - const feature = this._features[id]; - const geom = feature.geojson.geometry; - const c = geom.coordinates; - - let minX; - let minY; - let maxX; - let maxY; - - if (geom.type === 'Point') { - minX = c[0]; - maxX = c[0]; - minY = c[1]; - maxY = c[1]; - } else { - [minX, minY, maxX, maxY] = bbox(geom); - } + for (const layerName in this._features) { + for (const id in this._features[layerName]) { + const feature = this._features[layerName][id]; + const geom = feature.geojson.geometry; + const c = geom.coordinates; + + let minX; + let minY; + let maxX; + let maxY; + + if (geom.type === 'Point') { + minX = c[0]; + maxX = c[0]; + minY = c[1]; + maxY = c[1]; + } else { + [minX, minY, maxX, maxY] = bbox(geom); + } - const item = { - minX, - minY, - maxX, - maxY, - id: feature.id - }; + const item = { + minX, + minY, + maxX, + maxY, + id: feature.id + }; - feature.indexEntry = item; + feature.indexEntry = item; - bboxes.push(item); + bboxes.push(item); + } } // bulk insert into spatial index @@ -90,7 +89,12 @@ export default class Tile { * @returns {boolean} true if this tile contains a feature with the given id */ contains(id) { - return id in this._features; + for (let layerName of this._features) { + if (id in this._features[layerName]) { + return true; + } + } + return false; } /** @@ -98,8 +102,11 @@ export default class Tile { * @returns {Tile} this */ addFeature(feature) { - this._features[feature.id] = feature; - this.featureGroup.addLayer(feature.layer); + if (!(feature.layer in this._features)) { + this._features[feature.layer] = {}; + } + this._features[feature.layer][feature.id] = feature; + this.featureGroup.addLayer(feature.leafletLayer); return this; } @@ -112,9 +119,9 @@ export default class Tile { return this; } const feature = this.getFeature(id); - this.featureGroup.removeLayer(feature.layer); + this.featureGroup.removeLayer(feature.leafletLayer); this._index.remove(feature.indexEntry); - delete this._features[id]; + delete this._features[feature.layer][id]; return this; } @@ -123,7 +130,12 @@ export default class Tile { * @returns {Feature} */ getFeature(id) { - return this._features[id]; + for (let layerName of this._features) { + if (id in this._features[layerName]) { + return this._features[layerName][id]; + } + } + return null; } /** @@ -181,20 +193,22 @@ export default class Tile { toggleByProperty(property, value, on, toggled) { let feature; let geoj; - for (const id in this._features) { - if (!this._features.hasOwnProperty(id)) { - continue; - } - feature = this.getFeature(id); - geoj = feature.geojson; - if (property in geoj.properties && geoj.properties[property] === value) { - if (toggled) { - if (on) { - this._index.insert(feature.indexEntry); - this.featureGroup.addLayer(feature.layer); - } else { - this._index.remove(feature.indexEntry); - this.featureGroup.removeLayer(feature.layer); + for (const layerName in this._features) { + for (const id in this._features[layerName]) { + if (!this._features[layerName].hasOwnProperty(id)) { + continue; + } + feature = this._features[layerName][id]; + geoj = feature.geojson; + if (property in geoj.properties && geoj.properties[property] === value) { + if (toggled) { + if (on) { + this._index.insert(feature.indexEntry); + this.featureGroup.addLayer(feature.leafletLayer); + } else { + this._index.remove(feature.indexEntry); + this.featureGroup.removeLayer(feature.leafletLayer); + } } } } @@ -210,14 +224,16 @@ export default class Tile { */ restyleByProperty(property, value, style) { let feature; - for (const id in this._features) { - if (!this._features.hasOwnProperty(id)) { - continue; - } - feature = this.getFeature(id); - if (property in feature.geojson.properties - && feature.geojson.properties[property] === value) { - feature.layer.setStyle(style); + for (const layerName in this._features) { + for (const id in this._features[layerName]) { + if (!this._features[layerName].hasOwnProperty(id)) { + continue; + } + feature = this._features[layerName][id]; + if (property in feature.geojson.properties + && feature.geojson.properties[property] === value) { + feature.leafletLayer.setStyle(style); + } } } return this; diff --git a/worker.js b/worker.js index 7685691..cc4bcb6 100644 --- a/worker.js +++ b/worker.js @@ -28,13 +28,14 @@ module.exports = function(self) { * GeoJSON features */ function getFeatures(vectorTile, coords) { - const features = []; + const features = {}; for (const layerName in vectorTile.layers) { + const l = features[layerName] = []; const layer = vectorTile.layers[layerName]; for (let i = 0; i < layer.length; i++) { const feature = layer.feature(i); const geojson = feature.toGeoJSON(coords.x, coords.y, coords.z); - features.push(geojson); + l.push(geojson); } } return features; From ac886d5b8e9e00823904219158ef918c934f8548 Mon Sep 17 00:00:00 2001 From: kelvinabrokwa Date: Fri, 17 Nov 2017 13:49:00 -0500 Subject: [PATCH 4/8] lint and fix cache bug --- Leaflet.VectorTiles.js | 142 ++++++++++++++++++++--------------------- feature.js | 10 +++ tile.js | 53 +++++++-------- tile_cache.js | 37 +++++++---- worker.js | 26 +++++++- 5 files changed, 153 insertions(+), 115 deletions(-) diff --git a/Leaflet.VectorTiles.js b/Leaflet.VectorTiles.js index dc551dc..172f7be 100644 --- a/Leaflet.VectorTiles.js +++ b/Leaflet.VectorTiles.js @@ -1,9 +1,11 @@ +import work from 'webworkify'; import Tile from './tile'; import TileCache from './tile_cache'; import Feature from './feature'; -import { VectorTile } from '@mapbox/vector-tile'; -import Pbf from 'pbf'; -import work from 'webworkify'; +import worker from './worker'; + + +const DEFAULT_TILE_CACHE_SIZE = 25; /** @@ -19,7 +21,7 @@ function tileIsValid(coords) { if (coords.x < 0 || coords.y < 0 || coords.z < 0) { return false; } - const nTiles = Math.pow(4, coords.z); + const nTiles = 4 ** coords.z; const maxIndex = Math.sqrt(nTiles) - 1; if (coords.x > maxIndex || coords.y > maxIndex) { return false; @@ -59,6 +61,7 @@ L.VectorTiles = L.GridLayer.extend({ * @param {Function} [options.getFeatureId] * @param {boolean} [options.debug] * @param {Object} [options.style] + * @param {number} [options.tileCacheSize] */ initialize(url, options) { L.Util.setOptions(options); @@ -70,7 +73,10 @@ L.VectorTiles = L.GridLayer.extend({ this._featureGroup = L.featureGroup(); // show tile boundaries and log tile loading and unloading - this._debug = options.debug; + this._debug = options.debug || false; + + // set the cache size + this._tileCacheSize = options.tileCacheSize || DEFAULT_TILE_CACHE_SIZE; // store Tile objects representing vector tiles // keyed on tileKeys @@ -98,12 +104,12 @@ L.VectorTiles = L.GridLayer.extend({ this._featureOnMap = {}; // tile cache - this._tileCache = new TileCache(50, this._debug); + this._tileCache = new TileCache(this._tileCacheSize, this._debug); // mark a tile as loaded // this is needed because if a tile is unloaded before its finished loading // we need to wait for it to finish loading before we can clean up - this.on('vt_tileload', e => { + this.on('vt_tileload', (e) => { const tileKey = this._tileCoordsToKey(e.coords); const tile = this._vectorTiles[tileKey]; if (tile.destroy) { @@ -126,7 +132,7 @@ L.VectorTiles = L.GridLayer.extend({ if (!(tileKey in this._vectorTiles) || !this._vectorTiles[tileKey].loaded) { // invalidate the tile so that it is deleted when its done loading if (this._debug) { - console.log('marking tile', e.coords, 'for deletion after loading'); + console.log('(Main) marking tile', e.coords, 'for deletion after loading'); } this._vectorTiles[tileKey].destroy = true; } else { @@ -136,8 +142,8 @@ L.VectorTiles = L.GridLayer.extend({ }); // web worker - this.worker = work(require('./worker')); - this.worker.addEventListener('message', e => { + this.worker = work(worker); + this.worker.addEventListener('message', (e) => { const { coords, features } = e.data; this._renderVectorTile(features, coords); }); @@ -167,11 +173,8 @@ L.VectorTiles = L.GridLayer.extend({ const maxX = max.lng; const maxY = max.lat; - for (const tileKey in this._vectorTiles) { - if (!this._vectorTiles.hasOwnProperty(tileKey)) { - continue; - } - for (const result of this._vectorTiles[tileKey].search(minX, minY, maxX, maxY)) { + for (const tile of Object.values(this._vectorTiles)) { + for (const result of tile.search(minX, minY, maxX, maxY)) { results.add(result); } } @@ -185,22 +188,28 @@ L.VectorTiles = L.GridLayer.extend({ * uses the tile from the cache * * @param {Object} coords + * @param {number} coords.x + * @param {number} coords.y + * @param {number} coords.z * @param {Function} done - * @fires vt_tileload * @returns DOM element + * * @private */ createTile(coords) { if (this._debug) { - console.log("creating tile:", coords); + console.log('(Main) creating tile:', coords); } const tileKey = this._tileCoordsToKey(coords); // check cache for tile let tile = this._tileCache.get(tileKey); + let timestamp = null; if (!tile) { tile = new Tile(coords.x, coords.y, coords.z); + } else { + timestamp = tile.timestamp; } this._vectorTiles[tileKey] = tile; @@ -208,25 +217,32 @@ L.VectorTiles = L.GridLayer.extend({ const url = L.Util.template(this._url, coords); // assign tile to a worker - this.worker.postMessage({ url, coords }); - - const headers = new Headers(); - headers.append('If-Modified-Since', tile.timestamp); + this.worker.postMessage({ url, coords, timestamp }); // create an empty div for passing back to GridLayer const div = L.DomUtil.create('div'); return div; }, + /** + * @param {Array} features + * @param {Object} coords + * @param {number} coords.x + * @param {number} coords.y + * @param {number} coords.z + * @fires vt_tileload + * + * @private + */ _renderVectorTile(features, coords) { const tileKey = this._tileCoordsToKey(coords); const tile = this._vectorTiles[tileKey]; - for (let layerName in features) { + for (const [layerName, layer] of Object.entries(features)) { if (tile.destroy) { break; } - for (let geojson of features[layerName]) { + for (const geojson of layer) { if (tile.destroy) { break; } @@ -246,30 +262,27 @@ L.VectorTiles = L.GridLayer.extend({ // calculate its style and if its visible const style = {}; let onMap = true; - let prop; // property based styles - for (prop in geojson.properties) { + // TODO: for nested properties this will be comparing objects. Look into this. + for (const [prop, val] of Object.entries(geojson.properties)) { // apply style from options - if (prop in this.options.style - && geojson.properties[prop] in this.options.style[prop]) { - Object.assign(style, this.options.style[prop][geojson.properties[prop]]); + if (prop in this.options.style && val in this.options.style[prop]) { + Object.assign(style, this.options.style[prop][val]); } // apply style modifications - if (prop in this._propertyStyles - && geojson.properties[prop] in this._propertyStyles[prop]) { - Object.assign(style, this._propertyStyles[prop][geojson.properties[prop]]); + if (prop in this._propertyStyles && val in this._propertyStyles[prop]) { + Object.assign(style, this._propertyStyles[prop][val]); } // put on map based on property - if (prop in this._propertyOnMap - && geojson.properties[prop] in this._propertyOnMap[prop]) { - onMap = this._propertyOnMap[prop][geojson.properties[prop]]; + if (prop in this._propertyOnMap && val in this._propertyOnMap[prop]) { + onMap = this._propertyOnMap[prop][val]; } } - // feature based styles + // apply styles custom to this specific feature if (id in this._featureStyles) { Object.assign(style, this._featureStyles[id]); } @@ -315,7 +328,7 @@ L.VectorTiles = L.GridLayer.extend({ */ destroyTile(coords) { if (this._debug) { - console.log("destroying tile:", coords); + console.log('(Main) destroying tile:', coords); } const tileKey = this._tileCoordsToKey(coords); const tile = this._vectorTiles[tileKey]; @@ -360,6 +373,7 @@ L.VectorTiles = L.GridLayer.extend({ * @param {string} property * @param {string} value * @param {boolean} on + * * @private */ _toggleByProperty(property, value, on) { @@ -372,12 +386,7 @@ L.VectorTiles = L.GridLayer.extend({ this._propertyOnMap[property][value] = on; - let tile; - for (const tileKey in this._vectorTiles) { - if (!this._vectorTiles.hasOwnProperty(tileKey)) { - continue; - } - tile = this._vectorTiles[tileKey]; + for (const tile of Object.values(this._vectorTiles)) { tile.toggleByProperty(property, value, on, toggled); } }, @@ -401,12 +410,7 @@ L.VectorTiles = L.GridLayer.extend({ Object.assign(this._propertyStyles[property][value], style); - let tile; - for (const tileKey in this._vectorTiles) { - if (!this._vectorTiles.hasOwnProperty(tileKey)) { - continue; - } - tile = this._vectorTiles[tileKey]; + for (const tile of Object.values(this._vectorTiles)) { tile.restyleByProperty(property, value, style); } @@ -422,11 +426,7 @@ L.VectorTiles = L.GridLayer.extend({ */ setFeatureStyle(id, style) { this._featureStyles[id] = style; - for (const tileKey in this._vectorTiles) { - if (!this._vectorTiles.hasOwnProperty(tileKey)) { - continue; - } - const tile = this._vectorTiles[tileKey]; + for (const tile of Object.values(this._vectorTiles)) { if (tile.contains(id)) { const feature = tile.getFeature(id); feature.setStyle(style); @@ -442,12 +442,7 @@ L.VectorTiles = L.GridLayer.extend({ * @returns {L.Path} */ getLayer(id) { - let tile; - for (const tileKey in this._vectorTiles) { - if (!this._vectorTiles.hasOwnProperty(tileKey)) { - continue; - } - tile = this._vectorTiles[tileKey]; + for (const tile of Object.values(this._vectorTiles)) { if (tile.contains(id)) { return tile.getFeature(id).layer; } @@ -462,12 +457,7 @@ L.VectorTiles = L.GridLayer.extend({ * @return {Object} */ getGeoJSON(id) { - let tile; - for (const tileKey in this._vectorTiles) { - if (!this._vectorTiles.hasOwnProperty(tileKey)) { - continue; - } - tile = this._vectorTiles[tileKey]; + for (const tile of Object.values(this._vectorTiles)) { if (tile.contains(id)) { return tile.getFeature(id).geojson; } @@ -483,12 +473,7 @@ L.VectorTiles = L.GridLayer.extend({ * @returns {L.VectorTiles} this */ removeFeature(id) { - let tile; - for (const tileKey in this._vectorTiles) { - if (!this._vectorTiles.hasOwnProperty(tileKey)) { - continue; - } - tile = this._vectorTiles[tileKey]; + for (const tile of Object.values(this._vectorTiles)) { tile.removeFeature(id); } return this; @@ -501,10 +486,18 @@ L.VectorTiles = L.GridLayer.extend({ * returns {L.VectorTiles} this */ setTileCacheSize(size) { - this._tileCache.setSize(size); + this._tileCacheSize = size; + this._tileCache.setSize(this._tileCacheSize); return this; }, + /** + * @returns {number} current maximum size of the cache + */ + getTileCacheSize() { + return this._tileCacheSize; + }, + /** * Convert a GeoJSON feature into a Leaflet feature * Point -> L.Circle @@ -514,6 +507,7 @@ L.VectorTiles = L.GridLayer.extend({ * * @param {Object} feature * @returns {L.Path} + * * @private */ _geojsonToLeafletLayer(feature) { @@ -530,7 +524,7 @@ L.VectorTiles = L.GridLayer.extend({ case 'LineString': coords = []; - for (let p of c) { + for (const p of c) { coords.push([p[1], p[0]]); } layer = L.polyline(coords, {}); @@ -565,7 +559,7 @@ L.VectorTiles = L.GridLayer.extend({ break; default: - console.log(`Unsupported feature type: ${feature.geometry.type}`); + console.log(`(Main) Unsupported feature type: ${feature.geometry.type}`); return null; } diff --git a/feature.js b/feature.js index 7efd1a9..b828964 100644 --- a/feature.js +++ b/feature.js @@ -5,6 +5,15 @@ * @private */ export default class Feature { + + /** + * Constructor + * + * @param {string} id + * @param {string} layer + * @param {Object} geojson + * @param {L.Layer} leafletLayer + */ constructor(id, layer, geojson, leafletLayer) { this.id = id; this.layer = layer; @@ -36,4 +45,5 @@ export default class Feature { this.onMap = on; return this; } + } diff --git a/tile.js b/tile.js index 0d6903c..faa69af 100644 --- a/tile.js +++ b/tile.js @@ -15,15 +15,17 @@ export default class Tile { * param {number} x - x coordinate of tile * param {number} y - y coordinate of tile * param {number} z - z coordinate of tile + * + * @private */ - constructor(x, y, z) { + constructor(x, y, z, debug = false) { this.x = x; this.y = y; this.z = z; + this._debug = debug; this.coords = { x, y, z }; this._features = {}; - this.timestamp = new Date().getTime(); // tracks when the tile was created this.loaded = false; this.destroy = false; // used in tile mark/sweep @@ -41,14 +43,23 @@ export default class Tile { return this; } + /** + * + */ + updateTimestamp() { + this.timestamp = new Date().getTime(); + } + /** * This method adds all features in this tile to an rbush index */ indexFeatures() { + if (this._debug) { + console.log('(Tile)', this.coords, 'indexing features'); + } const bboxes = []; - for (const layerName in this._features) { - for (const id in this._features[layerName]) { - const feature = this._features[layerName][id]; + for (const layer of Object.values(this._features)) { + for (const feature of Object.values(layer)) { const geom = feature.geojson.geometry; const c = geom.coordinates; @@ -89,8 +100,8 @@ export default class Tile { * @returns {boolean} true if this tile contains a feature with the given id */ contains(id) { - for (let layerName of this._features) { - if (id in this._features[layerName]) { + for (const layer of Object.values(this._features)) { + if (id in layer) { return true; } } @@ -130,9 +141,9 @@ export default class Tile { * @returns {Feature} */ getFeature(id) { - for (let layerName of this._features) { - if (id in this._features[layerName]) { - return this._features[layerName][id]; + for (const layer of Object.values(this._features)) { + if (id in layer) { + return layer[id]; } } return null; @@ -180,6 +191,7 @@ export default class Tile { */ markAsLoaded() { this.loaded = true; + this.updateTimestamp(); return this; } @@ -191,15 +203,9 @@ export default class Tile { * @returns {Tile} this */ toggleByProperty(property, value, on, toggled) { - let feature; - let geoj; - for (const layerName in this._features) { - for (const id in this._features[layerName]) { - if (!this._features[layerName].hasOwnProperty(id)) { - continue; - } - feature = this._features[layerName][id]; - geoj = feature.geojson; + for (const layer of Object.values(this._features)) { + for (const feature of Object.values(layer)) { + const geoj = feature.geojson; if (property in geoj.properties && geoj.properties[property] === value) { if (toggled) { if (on) { @@ -223,13 +229,8 @@ export default class Tile { * @returns {Tile} this */ restyleByProperty(property, value, style) { - let feature; - for (const layerName in this._features) { - for (const id in this._features[layerName]) { - if (!this._features[layerName].hasOwnProperty(id)) { - continue; - } - feature = this._features[layerName][id]; + for (const layer of Object.values(this._features)) { + for (const feature of Object.values(layer)) { if (property in feature.geojson.properties && feature.geojson.properties[property] === value) { feature.leafletLayer.setStyle(style); diff --git a/tile_cache.js b/tile_cache.js index fb9833f..8808af5 100644 --- a/tile_cache.js +++ b/tile_cache.js @@ -40,11 +40,12 @@ export default class TileCache { this._debug = debug; this._cache = {}; // a hashtable for holding cached items this._head = null; // the head of a doubly linked list that maintains the order of items by age - this._tail = null // tail pointer to the order linked list + this._tail = null; // tail pointer to the order linked list } /** * Retrieve an item from the cache + * Also places that item at the head of the list * * @param {string} tileKey * @returns {Tile|null} @@ -52,13 +53,13 @@ export default class TileCache { get(tileKey) { if (!(tileKey in this._cache)) { if (this._debug) { - console.log('tile cache:', 'miss', tileKey); + console.log('(TileCache)', 'miss', tileKey); } return null; } if (this._debug) { - console.log('tile cache', 'hit', tileKey); + console.log('(TileCache)', 'hit', tileKey); } // move node to front of linked list @@ -75,13 +76,16 @@ export default class TileCache { } /** + * Place an item into the cache + * * @param {string} tileKey * @param {Tile} tile + * @returns {TileCache} this */ put(tileKey, tile) { if (this._debug) { - console.log('tile cache:', 'caching', tileKey); - console.log(this._stringifyList()); + console.log('(TileCache)', 'caching', tileKey); + console.log(this._stringifyList()); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! } if (tileKey in this._cache) { @@ -97,18 +101,22 @@ export default class TileCache { } // place at heaad of order linked list - let node = new Node({ tileKey }); + const node = new Node({ tileKey }); if (this._head) { this._head.prev = node; } node.next = this._head; this._head = node; + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + if (!this._tail) { + this._tail = this._head; + } + this._cache[tileKey] = { tileKey, tile, node }; - if (Object.keys(this._cache) > this._size) { + if (Object.keys(this._cache).length > this._size) { // we need to evict an item - // remove from linked list const tailNode = this._tail; this._tail = tailNode.prev; @@ -117,11 +125,12 @@ export default class TileCache { // remove from cache table const tailtileKey = tailNode.data.tileKey; if (this._debug) { - console.log('tile cache:', 'evicting', tileKey); - console.log(this._stringifyList()); + console.log('(TileCache)', 'evicting', tileKey); + console.log(this._stringifyList()); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! } delete this._cache[tailtileKey]; } + return this; } /** @@ -129,11 +138,11 @@ export default class TileCache { */ setSize(size) { if (size < 0) { - throw "Size cannot be a negative number"; + throw new Error('Cache size cannot be a negative number'); } if (this._debug) { - console.log('tile cache:', 'changing cache size from', this._size, 'to', size); + console.log('(TileCache):', 'changing cache size from', this._size, 'to', size); } if (size >= this._size) { @@ -163,9 +172,9 @@ export default class TileCache { // collect the garbage while (garbage != null) { - let { tileKey } = garbage.data; + const { tileKey } = garbage.data; if (this._debug) { - console.log('tile cache:', 'removing tile', tileKey, 'due to cache resize'); + console.log('(TileCache)', 'removing tile', tileKey, 'due to cache resize'); console.log(this._stringifyList()); } delete this._cache[tileKey]; // delete from cache diff --git a/worker.js b/worker.js index cc4bcb6..53bd932 100644 --- a/worker.js +++ b/worker.js @@ -3,19 +3,28 @@ */ import Pbf from 'pbf'; import { VectorTile } from '@mapbox/vector-tile'; +import geojsonvt from 'geojson-vt'; /** * Fetches and processes the vector tile for a given tile coordinate */ module.exports = function(self) { self.addEventListener('message', e => { - const { url, coords } = e.data; + const { url, coords, timestamp } = e.data; + const headers = new Headers(); + if (timestamp) { + headers.append('If-Modified-Since', timestamp); + } fetch(url) .then(res => res.blob()) .then(blob => { const reader = new FileReader(); reader.onloadend = () => { const features = getFeatures(new VectorTile(new Pbf(reader.result)), coords); + for (let layer in features) { + //features[layer] = simplify(fatures[layer], coords); + //simplify(features[layer], coords); + } self.postMessage({ coords, features }); } reader.readAsArrayBuffer(blob); @@ -40,3 +49,18 @@ function getFeatures(vectorTile, coords) { } return features; } + +/** + * Build a geojson-vt index for this zoom level and have it do simplification + */ +function simplify(features, coords) { + const geojson = { + type: 'FeatureCollection', + features + }; + const tileIndex = geojsonvt(geojson, { indexMaxZoom: coords.z }); + const tile = tileIndex.getTile(coords.z, coords.x, coords.y).features; + console.log(tile); + const vt = new VectorTile(tile); + console.log(vt); +} From 55335ef6ff1a59df35f0246cb392ba798276ee6f Mon Sep 17 00:00:00 2001 From: kelvinabrokwa Date: Tue, 28 Nov 2017 09:56:04 -0500 Subject: [PATCH 5/8] add architecture doc --- ARCHITECTURE.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 ARCHITECTURE.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..a688f81 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,84 @@ +# Architecture + +### Tile Fetching and Parsing + +When a tile comes into view, Leaflet calls `createTile` with the tile's coordinates as an arguemnt in the form `{ x: , y: , z: }`. + + +#### Web Worker + +The webworker fetches the tile, parses it, converts it to GeoJSON, and then returns an object with layer names as keys and arrays of geojson features as values. + +When we request a tile from the url `///.pbf`, the vector tile server returns a protocol buffer encoded binary blob of the tile's vector data. +We read the blob using a [`FileReader`](https://developer.mozilla.org/en-US/docs/Web/API/FileReader), decode the blob using the [pbf library](https://github.com/mapbox/pbf) and then parse the JSON output of that using the [vector-tile-js library](https://github.com/mapbox/vector-tile-js/). +That outputs [`VectorTileFeature`s](https://github.com/mapbox/vector-tile-js#vectortilefeature) for each feature in the tile, grouped by layer. +We can now iterate over each tile and feature and convert to GeoJSON as a method of projecting from pixel coordinates to lat/lons. +Now we create a [geojson-vt](https://github.com/mapbox/geojson-vt) index for the single zoom level of the tile we are processing. +geojson-vt simplifies and drops lines and polygons appropriateley so this gives us tile simplification. +Then, we request the simplified ile from the geojson-vt index. +Then we convert the tile back to GeoJSON using vector-tile-js. +Then we post the GeoJSON to the main thread with the coords. + +*NOT IMPLEMENTED:* + + +*TODO: webworker pool* + + +### Tile Data Structure + +The `Tile` data type encapsulates most of the functionality for storing the data of and rendering a given tile. + +### Tile Caching + +The tile cache is an implementation of a [Least Recently Used (LRU) cache](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29). +Internally, a doubly linked list is used to track the order of access (and insertion) of all items in the cache and a hash table is used to hold the data. Each node in the linked list holds `next` and `previous` pointers as well as a `data` field which holds the tile key used to access the tile in the cache's hash table. +When an item is first placed in the cache, we create an entry for it in the linked list, placing it at the head. +The tile itself is then placed in the hash table. +If the length of the list is greater than the maximum cache size, we pop the tail node of the list and use the tile key therein to delete the tile from the hash table. +Whenever a tile is fetched from (or reinserted into) the cache, its corresponding node is moved to the head of the list. + +### Styling + +Styling is primarily feature property based. +That is, a style applies to a feature when that feature has the corresponding `key: value` pair in its properties. +For example, the following style: + +```js +style: { + treeType: { + oak: { + color: 'green' + } + } +} +``` + +would apply to a feature like: + +```js +{ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [11, 78] + }, + properties: { + treeType: 'oak' + id: 12 + } +} +``` + +The hierarchy of styling is as follows: + +- default Leaflet styles for the layer +- property based styles as applied at library instantiation +- property based style modifications made at run time +- style modifications for the specific feature (by feature id) + +TODO + +- Layer based styling + +### Indexing and Searching From c4de6d931e682f87c3e587e497adb8875717d64d Mon Sep 17 00:00:00 2001 From: kelvinabrokwa Date: Tue, 19 Dec 2017 08:36:54 -0500 Subject: [PATCH 6/8] lotta code --- ARCHITECHTURE.md | 82 ++ Leaflet.VectorTiles.js | 10 +- example/index.html | 28 +- example/index.js | 60 +- example/package-lock.json | 54 +- example/vt_server.js | 15 +- package-lock.json | 1525 +++++++++++++++++++++++++++++++++++-- package.json | 3 + tile_cache.js | 2 +- worker.js | 44 +- 10 files changed, 1650 insertions(+), 173 deletions(-) create mode 100644 ARCHITECHTURE.md diff --git a/ARCHITECHTURE.md b/ARCHITECHTURE.md new file mode 100644 index 0000000..7f1d2f8 --- /dev/null +++ b/ARCHITECHTURE.md @@ -0,0 +1,82 @@ +# Architecture + +### Tile Fetching and Parsing + +When a tile comes into view, Leaflet calls `createTile` with the tile's coordinates as an arguemnt in the form `{ x: , y: , z: }`. +`createTile` posts a message to the webworker to handle fetching, parsing, and simlpifying the tile. + + +#### Web Worker + +The webworker fetches the tile, parses it, converts it to GeoJSON, and then returns an object with layer names as keys and arrays of geojson features as values. + +When we request a tile from the url `///.pbf`, the vector tile server returns a protocol buffer encoded binary blob of the tile's vector data. +We read the blob using a [`FileReader`](https://developer.mozilla.org/en-US/docs/Web/API/FileReader), decode the blob using the [pbf library](https://github.com/mapbox/pbf) and then parse the JSON output of that using the [vector-tile-js library](https://github.com/mapbox/vector-tile-js/). +That outputs [`VectorTileFeature`s](https://github.com/mapbox/vector-tile-js#vectortilefeature) for each feature in the tile, grouped by layer. +We can now iterate over each tile and feature and convert to GeoJSON as a method of projecting from pixel coordinates to lat/lons. +Now we create a [geojson-vt](https://github.com/mapbox/geojson-vt) index for the single zoom level of the tile we are processing. +geojson-vt simplifies and drops lines and polygons appropriateley so this gives us tile simplification. +Then, we request the simplified ile from the geojson-vt index. +Then we convert the tile back to GeoJSON using vector-tile-js. +Then we post the GeoJSON to the main thread with the coords. + +*TODO: webworker pool* + + +### Tile Data Structure + +The `Tile` data type encapsulates most of the functionality for storing the data of and rendering a given tile. + +### Tile Caching + +The tile cache is an implementation of a [Least Recently Used (LRU) cache](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29). +Internally, a doubly linked list is used to track the order of access (and insertion) of all items in the cache and a hash table is used to hold the data. Each node in the linked list holds `next` and `previous` pointers as well as a `data` field which holds the tile key used to access the tile in the cache's hash table. +When an item is first placed in the cache, we create an entry for it in the linked list, placing it at the head. +The tile itself is then placed in the hash table. +If the length of the list is greater than the maximum cache size, we pop the tail node of the list and use the tile key therein to delete the tile from the hash table. +Whenever a tile is fetched from (or reinserted into) the cache, its corresponding node is moved to the head of the list. + +### Styling + +Styling is primarily feature property based. +That is, a style applies to a feature when that feature has the corresponding `key: value` pair in its properties. +For example, the following style: + +```js +style: { + treeType: { + oak: { + color: 'green' + } + } +} +``` + +would apply to a feature like: + +```js +{ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [11, 78] + }, + properties: { + treeType: 'oak' + id: 12 + } +} +``` + +The hierarchy of styling is as follows: + +- default Leaflet styles for the layer +- property based styles as applied at library instantiation +- property based style modifications made at run time +- style modifications for the specific feature (by feature id) + +TODO + +- Layer based styling + +### Indexing and Searching diff --git a/Leaflet.VectorTiles.js b/Leaflet.VectorTiles.js index 172f7be..4db0b0c 100644 --- a/Leaflet.VectorTiles.js +++ b/Leaflet.VectorTiles.js @@ -29,7 +29,6 @@ function tileIsValid(coords) { return true; } - /** * Manages interactive tiles of data * @@ -130,6 +129,7 @@ L.VectorTiles = L.GridLayer.extend({ // if the tile hasn't loaded yet, mark it for deletion for when it // is finished loading if (!(tileKey in this._vectorTiles) || !this._vectorTiles[tileKey].loaded) { + // invalidate the tile so that it is deleted when its done loading if (this._debug) { console.log('(Main) marking tile', e.coords, 'for deletion after loading'); @@ -220,7 +220,13 @@ L.VectorTiles = L.GridLayer.extend({ this.worker.postMessage({ url, coords, timestamp }); // create an empty div for passing back to GridLayer - const div = L.DomUtil.create('div'); + const div = L.DomUtil.create('div', 'tile-boundary'); + + if (self._debug) { + div.innerHTML = [coords.x, coords.y, coords.z].join(', '); + div.style.outline = '1px solid red'; + } + return div; }, diff --git a/example/index.html b/example/index.html index b5f6866..1049f80 100644 --- a/example/index.html +++ b/example/index.html @@ -3,28 +3,34 @@ Leaflet.VectorTiles Test Page + -
- hover highlight -
-
- search: -
-
-
- set cache size: +
+
hover highlight
+
search:
+
set cache size:
+
cache size: {{ cacheSize }}
+
zoom: {{ zoom }}
+
+
+
+
({{ tile.coords.x }}, {{ tile.coords.y }}, {{ tile.coords.z }})
+
timestamp {{ tile.timestamp }}
+
feature count: {{ tile.featureCount }}
+
loaded
+
loading
+
-
cache size: 50
-
diff --git a/example/index.js b/example/index.js index e54dafb..44de870 100644 --- a/example/index.js +++ b/example/index.js @@ -8,34 +8,49 @@ fetch('/geojson/country') function main(geojson) { + var app = new Vue({ + el: '#app', + data: { + zoom: 0, + tiles: [], + cacheSize: 50, + }, + methods: { + setTileCacheSize(e) { + vtLayer.setTileCacheSize(+e.target.value); + } + } + }); + const countries = geojson.features.map(f => f.properties.name.toLowerCase()); const map = L.map('map', { center: { lat: 0, - lng: 0 + lon: 0, }, - zoom: 2, + zoom: 3, preferCanvas: true, }); - //L.tileLayer('http://tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); + L.tileLayer('http://tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); - const url = '/{z}/{x}/{y}?pbf=true'; + const url = 'http://localhost:12345/{z}/{x}/{y}?pbf=true'; const vtLayer = window.vtLayer = new L.VectorTiles(url, { getFeatureId: f => f.properties.name.toLowerCase(), style: {}, - debug: true, + //debug: true, + tileCacheSize: 5, }).addTo(map); - var hoverHighlight = false; + var hoverHighlight = true; + map[hoverHighlight ? 'on' : 'off']('mousemove', highlightOnHover); + document.getElementById('hover-radio').checked = hoverHighlight; function highlightOnHover(e) { // reset all feature styles - for (let i = 0; i < countries.length; i++) { - vtLayer.setFeatureStyle(countries[i], Object.assign(L.Path.prototype.options, { fill: true })); - } + countries.forEach(c => vtLayer.setFeatureStyle(c, Object.assign(L.Path.prototype.options, { fill: true }))); const buf = .00001; const { lat, lng } = e.latlng; vtLayer.search( @@ -51,22 +66,31 @@ function main(geojson) { }; document.getElementById('search').onkeyup = e => { - // reset all feature styles for (let i = 0; i < countries.length; i++) { vtLayer.setFeatureStyle(countries[i], Object.assign(L.Path.prototype.options, { fill: true })); } const q = e.target.value.toLowerCase(); - if (!q) { - return; - } + if (!q) return; countries .filter(c => c.indexOf(q) > -1) .forEach(id => vtLayer.setFeatureStyle(id, { color: 'black' })); }; - document.getElementById('cache-size-input').onchange = e => { - const cacheSize = +e.target.value; - document.getElementById('cache-size').innerHTML = cacheSize; - vtLayer.setTileCacheSize(cacheSize); - }; + app.zoom = map.getZoom(); + map.on('zoomend', () => { + app.zoom = map.getZoom(); + }) + + vtLayer.on('vt_tileload', () => { + app.tiles = Object.entries(vtLayer._vectorTiles).map(t => { + const [tileKey, tile] = t; + return { + coords: tile.coords, + featureCount: Object.values(tile._features).reduce((p, c) => p + Object.keys(c).length, 0), + loaded: tile.loaded, + timestamp: (new Date(tile.timestamp || 0)).toISOString(), + }; + }); + }); } + diff --git a/example/package-lock.json b/example/package-lock.json index c1d73c1..c5bb799 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -22,6 +22,16 @@ "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-4.6.0.tgz", "integrity": "sha1-DT+aIY5Y0cXl3u30Z8MyHdYSA/M=" }, + "JSONStream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", + "dev": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, "abbrev": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", @@ -1214,14 +1224,6 @@ } } }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, "string-width": { "version": "1.0.2", "bundled": true, @@ -1232,6 +1234,14 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, "stringstream": { "version": "0.0.5", "bundled": true, @@ -1599,16 +1609,6 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, - "JSONStream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", - "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", - "dev": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - } - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -2329,15 +2329,6 @@ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "string-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz", @@ -2347,6 +2338,15 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", diff --git a/example/vt_server.js b/example/vt_server.js index 22b79a2..4c35832 100644 --- a/example/vt_server.js +++ b/example/vt_server.js @@ -8,7 +8,6 @@ const bbox = require('@turf/bbox'); const vtpbf = require('vt-pbf'); const express = require('express'); const geojsonvt = require('geojson-vt'); -const SphericalMercator = require('sphericalmercator'); const featureCollection = require('@turf/helpers').featureCollection; const app = express(); @@ -17,8 +16,6 @@ app.use(cors()); const PORT = 12345; -const mercator = new SphericalMercator({ size: 256 }); - // load features from file const countryGeoj = JSON.parse(fs.readFileSync('countries.geojson')); const pointGeoj = JSON.parse(fs.readFileSync('points-10000.geojson')); @@ -40,17 +37,17 @@ const pointTileIndex = geojsonvt(pointGeoj, { debug: 2, }); -// returned when empty tiles are requested -const emptyFeatCollection = featureCollection([]); - app.get('/:z/:x/:y', (req, res) => { if (req.get('If-Modified-Since')) { return res.status(304).send(); } const [x, y, z] = [+req.params.x, +req.params.y, +req.params.z]; - const countries = countryTileIndex.getTile(z, x, y) || emptyFeatCollection; - const points = pointTileIndex.getTile(z, x, y) || emptyFeatCollection; - const buff = vtpbf.fromGeojsonVt({ countries, points }); + const countries = countryTileIndex.getTile(z, x, y); + //const points = pointTileIndex.getTile(z, x, y); + const d = {}; + if (countries) d.countries = countries; + //if (points) d.points = points; + const buff = vtpbf.fromGeojsonVt(d); res.status(200).send(buff); }); diff --git a/package-lock.json b/package-lock.json index 0f632db..316bf5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,11 @@ "through": "2.3.8" } }, + "JSV": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", + "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=" + }, "acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", @@ -63,6 +68,11 @@ } } }, + "agentkeepalive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.0.2.tgz", + "integrity": "sha1-o2YcQG7RILsRykiNZ7MnbGBMz+c=" + }, "ajv": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", @@ -105,14 +115,12 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "anymatch": { "version": "1.3.2", @@ -211,6 +219,11 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asn1": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=" + }, "asn1.js": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", @@ -231,6 +244,11 @@ "util": "0.10.3" } }, + "assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=" + }, "astw": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", @@ -240,6 +258,14 @@ "acorn": "4.0.13" } }, + "async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "requires": { + "lodash": "4.17.4" + } + }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", @@ -252,6 +278,11 @@ "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", "dev": true }, + "aws-sign2": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", + "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=" + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -1273,6 +1304,42 @@ "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", "dev": true }, + "bl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", + "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=", + "requires": { + "readable-stream": "1.0.34" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" + }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", @@ -1302,6 +1369,14 @@ "json-stringify-safe": "5.0.1" } }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } + }, "brace-expansion": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", @@ -1611,8 +1686,7 @@ "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" }, "camelcase-keys": { "version": "2.1.0", @@ -1624,6 +1698,11 @@ "map-obj": "1.0.1" } }, + "caseless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.10.0.tgz", + "integrity": "sha1-7WsnGa3NH9GPWNwIHA8aW0OWOQk=" + }, "ccount": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.2.tgz", @@ -1634,7 +1713,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, "requires": { "ansi-styles": "2.2.1", "escape-string-regexp": "1.0.5", @@ -1766,7 +1844,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, "requires": { "string-width": "1.0.2", "strip-ansi": "3.0.1", @@ -1811,8 +1888,7 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "collapse-white-space": { "version": "1.0.3", @@ -1866,6 +1942,14 @@ } } }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, "comma-separated-tokens": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.4.tgz", @@ -1875,6 +1959,11 @@ "trim": "0.0.1" } }, + "commander": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", + "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==" + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", @@ -1891,7 +1980,6 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, "requires": { "inherits": "2.0.3", "readable-stream": "2.0.6", @@ -1902,7 +1990,6 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -1915,8 +2002,7 @@ "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" } } }, @@ -1974,8 +2060,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "create-ecdh": { "version": "4.0.0", @@ -2013,6 +2098,14 @@ "sha.js": "2.4.8" } }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.10.1" + } + }, "crypto-browserify": { "version": "3.11.1", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", @@ -2031,6 +2124,11 @@ "randombytes": "2.0.5" } }, + "ctype": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=" + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -2065,7 +2163,6 @@ "version": "2.6.8", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true, "requires": { "ms": "2.0.0" } @@ -2073,8 +2170,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "deep-is": { "version": "0.1.3", @@ -2112,6 +2208,11 @@ "rimraf": "2.6.1" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, "depd": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", @@ -2789,8 +2890,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escope": { "version": "3.6.0", @@ -3135,6 +3235,22 @@ "websocket-driver": "0.6.5" } }, + "fg-lodash": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/fg-lodash/-/fg-lodash-0.0.2.tgz", + "integrity": "sha1-mINSU39CfaavIiEpu2OsyknmL6M=", + "requires": { + "lodash": "2.4.2", + "underscore.string": "2.3.3" + }, + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + } + } + }, "figures": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", @@ -3217,6 +3333,31 @@ "for-in": "1.0.2" } }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz", + "integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=", + "requires": { + "async": "2.6.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + }, + "dependencies": { + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + } + } + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -4240,18 +4381,71 @@ "generate-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" }, "generate-object-property": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, "requires": { "is-property": "1.0.2" } }, + "geojson-vt": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-2.4.0.tgz", + "integrity": "sha1-PBz0RJPzXrTSxwyV2mVQ3mYHLAU=" + }, + "geojsonhint": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/geojsonhint/-/geojsonhint-1.2.1.tgz", + "integrity": "sha1-U0gnDsrDxCi0Vc/tqyRdQKWun8c=", + "requires": { + "chalk": "1.1.3", + "concat-stream": "1.4.10", + "jsonlint-lines": "1.7.1", + "minimist": "1.1.1", + "text-table": "0.2.0" + }, + "dependencies": { + "concat-stream": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.10.tgz", + "integrity": "sha1-rMO79WAsuMyYDGrIQPp9hgPj7zY=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "1.1.14", + "typedarray": "0.0.6" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "minimist": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.1.tgz", + "integrity": "sha1-G8K8cWWM3KVxJHVoQ2NhWwtPaVs=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, "get-caller-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", @@ -4518,6 +4712,17 @@ } } }, + "har-validator": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-1.8.0.tgz", + "integrity": "sha1-2DhCsOtMQ1lgrrEIoGejqpTA7rI=", + "requires": { + "bluebird": "2.11.0", + "chalk": "1.1.3", + "commander": "2.12.2", + "is-my-json-valid": "2.16.0" + } + }, "has": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", @@ -4531,11 +4736,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "2.1.1" } }, + "has-color": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=" + }, "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", @@ -4618,6 +4827,17 @@ "integrity": "sha1-vQlpGWJdKTbh/xe8Tff9cn8X7Ok=", "dev": true }, + "hawk": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-2.3.1.tgz", + "integrity": "sha1-HnMc45RH+h0PbXB/e87r7A/R7B8=", + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, "highlight.js": { "version": "9.12.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", @@ -4635,6 +4855,11 @@ "minimalistic-crypto-utils": "1.0.1" } }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -4675,6 +4900,16 @@ "statuses": "1.3.1" } }, + "http-signature": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.11.0.tgz", + "integrity": "sha1-F5bPZ6ABrVzWhJ3KCZFIXwkIn+Y=", + "requires": { + "asn1": "0.1.11", + "assert-plus": "0.1.5", + "ctype": "0.5.3" + } + }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -4732,8 +4967,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.4", @@ -4823,8 +5057,7 @@ "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, "is-absolute": { "version": "0.2.6", @@ -4971,7 +5204,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, "requires": { "number-is-nan": "1.0.1" } @@ -4995,7 +5227,6 @@ "version": "2.16.0", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", - "dev": true, "requires": { "generate-function": "2.0.0", "generate-object-property": "1.2.0", @@ -5094,8 +5325,7 @@ "is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" }, "is-relative": { "version": "0.2.1", @@ -5172,8 +5402,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -5190,6 +5419,11 @@ "isarray": "1.0.0" } }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -5224,8 +5458,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "0.5.1", @@ -5239,6 +5472,15 @@ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", "dev": true }, + "jsonlint-lines": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/jsonlint-lines/-/jsonlint-lines-1.7.1.tgz", + "integrity": "sha1-UH3mgNP7jEvhZBzFfW9nnynxeP8=", + "requires": { + "JSV": "4.0.2", + "nomnom": "1.8.1" + } + }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -5248,8 +5490,7 @@ "jsonpointer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" }, "kebab-case": { "version": "1.0.0", @@ -5307,7 +5548,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, "requires": { "invert-kv": "1.0.0" } @@ -5371,8 +5611,7 @@ "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" }, "lodash.cond": { "version": "4.5.2", @@ -5471,6 +5710,16 @@ "integrity": "sha1-Sz3ToTPRUYuO8NvHCb8qG0gkvIw=", "dev": true }, + "mbtiles": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/mbtiles/-/mbtiles-0.8.2.tgz", + "integrity": "sha1-vhZN+bb0GaX2LHbtMcW/iAIv52w=", + "requires": { + "sphericalmercator": "1.0.5", + "sqlite3": "3.1.13", + "tiletype": "0.1.0" + } + }, "mdast-util-compact": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz", @@ -5607,6 +5856,26 @@ "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", "dev": true }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", + "integrity": "sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY=", + "requires": { + "mime-db": "1.12.0" + }, + "dependencies": { + "mime-db": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz", + "integrity": "sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc=" + } + } + }, "minimalistic-assert": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", @@ -5679,8 +5948,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "mute-stream": { "version": "0.0.5", @@ -5750,6 +6018,42 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" + }, + "nomnom": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", + "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", + "requires": { + "chalk": "0.4.0", + "underscore": "1.6.0" + }, + "dependencies": { + "ansi-styles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=" + }, + "chalk": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", + "requires": { + "ansi-styles": "1.0.0", + "has-color": "0.1.7", + "strip-ansi": "0.1.1" + } + }, + "strip-ansi": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=" + } + } + }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -5780,8 +6084,12 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" }, "object-assign": { "version": "4.1.1", @@ -5830,6 +6138,11 @@ } } }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, "object-visit": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-0.3.4.tgz", @@ -5943,7 +6256,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, "requires": { "lcid": "1.0.0" } @@ -6252,6 +6564,11 @@ "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", "dev": true }, + "point-geometry": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/point-geometry/-/point-geometry-0.0.0.tgz", + "integrity": "sha1-b8vK16gDtkGCR91uScKFPFhNr/c=" + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -6302,8 +6619,7 @@ "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "progress": { "version": "1.1.8", @@ -6311,6 +6627,56 @@ "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, + "progress-stream": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-0.5.0.tgz", + "integrity": "sha1-zEdZFnpv9PBYdheThPC2rg0ddYc=", + "requires": { + "single-line-log": "0.3.1", + "speedometer": "0.1.4", + "through2": "0.2.3" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "through2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", + "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", + "requires": { + "readable-stream": "1.1.14", + "xtend": "2.1.2" + } + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "requires": { + "object-keys": "0.4.0" + } + } + } + }, "property-information": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-3.2.0.tgz", @@ -6344,8 +6710,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "qs": { "version": "6.5.0", @@ -6375,6 +6740,11 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, + "queue-async": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/queue-async/-/queue-async-1.0.7.tgz", + "integrity": "sha1-Iq4KHaxKkvW81GNPmTxoKiqBCUU=" + }, "quickselect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.0.0.tgz", @@ -6757,6 +7127,53 @@ "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", "dev": true }, + "request": { + "version": "2.58.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.58.0.tgz", + "integrity": "sha1-tfScC5Sqt/rTiGEqH7atA7bMFYA=", + "requires": { + "aws-sign2": "0.5.0", + "bl": "0.9.5", + "caseless": "0.10.0", + "combined-stream": "1.0.5", + "extend": "2.0.1", + "forever-agent": "0.6.1", + "form-data": "1.0.1", + "har-validator": "1.8.0", + "hawk": "2.3.1", + "http-signature": "0.11.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.0.14", + "node-uuid": "1.4.8", + "oauth-sign": "0.8.2", + "qs": "3.1.0", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.4.3" + }, + "dependencies": { + "extend": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-2.0.1.tgz", + "integrity": "sha1-HugBBonnOV/5RIJByYZSvHWagmA=" + }, + "qs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-3.1.0.tgz", + "integrity": "sha1-0OmudFIzoS3EP7TzBVu6RGJhFTw=" + } + } + }, + "requestretry": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.4.0.tgz", + "integrity": "sha1-g598rYRYVi7uAWgNJ0Uun4XhZYI=", + "requires": { + "fg-lodash": "0.0.2", + "request": "2.58.0" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7015,6 +7432,11 @@ "from2-string": "1.1.0" } }, + "single-line-log": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.3.1.tgz", + "integrity": "sha1-p61lB/IYzl3+FsS/LWWSRkGeegY=" + }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", @@ -7099,6 +7521,14 @@ "kind-of": "3.2.2" } }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.16.3" + } + }, "source-map": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", @@ -7162,6 +7592,16 @@ "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", "dev": true }, + "speedometer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", + "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=" + }, + "sphericalmercator": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sphericalmercator/-/sphericalmercator-1.0.5.tgz", + "integrity": "sha1-3cWgSeNg4ADQ+tn8IsQHGIJYSYA=" + }, "split-string": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/split-string/-/split-string-2.1.1.tgz", @@ -7222,16 +7662,733 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "stacked": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stacked/-/stacked-1.1.1.tgz", - "integrity": "sha1-LH+jjMfjejQRp3zY55LeRI+faXU=", - "dev": true - }, - "state-toggle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.0.tgz", - "integrity": "sha1-0g+aYWu08MO5i5GSLSW2QKorxCU=", + "sqlite3": { + "version": "3.1.13", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-3.1.13.tgz", + "integrity": "sha512-JxXKPJnkZ6NuHRojq+g2WXWBt3M1G9sjZaYiHEWSTGijDM3cwju/0T2XbWqMXFmPqDgw+iB7zKQvnns4bvzXlw==", + "requires": { + "nan": "2.7.0", + "node-pre-gyp": "0.6.38" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.3" + } + }, + "asn1": { + "version": "0.2.3", + "bundled": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "bundled": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "co": { + "version": "4.6.0", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.4", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "mime-db": { + "version": "1.30.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.17", + "bundled": true, + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true + }, + "nan": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=" + }, + "node-pre-gyp": { + "version": "0.6.38", + "bundled": true, + "requires": { + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.2", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.4.1", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true + }, + "qs": { + "version": "6.4.0", + "bundled": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "readable-stream": { + "version": "2.3.3", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true + }, + "semver": { + "version": "5.4.1", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.1", + "bundled": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "bundled": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "requires": { + "debug": "2.6.9", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.3.3", + "rimraf": "2.6.2", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.3", + "bundled": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "uuid": { + "version": "3.1.0", + "bundled": true + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, + "stacked": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stacked/-/stacked-1.1.1.tgz", + "integrity": "sha1-LH+jjMfjejQRp3zY55LeRI+faXU=", + "dev": true + }, + "state-toggle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.0.tgz", + "integrity": "sha1-0g+aYWu08MO5i5GSLSW2QKorxCU=", "dev": true }, "static-extend": { @@ -7384,7 +8541,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, "requires": { "code-point-at": "1.1.0", "is-fullwidth-code-point": "1.0.0", @@ -7412,11 +8568,15 @@ "is-hexadecimal": "1.0.1" } }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, "requires": { "ansi-regex": "2.1.1" } @@ -7475,8 +8635,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "syntax-error": { "version": "1.3.0", @@ -7561,14 +8720,12 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "2.0.3", @@ -7590,6 +8747,52 @@ "xtend": "4.0.1" } }, + "tile-cover": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tile-cover/-/tile-cover-2.4.1.tgz", + "integrity": "sha1-jKkFscqTNKVGim1oDolmMhZMjl8=", + "requires": { + "tilebelt": "0.7.1" + } + }, + "tilebelt": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/tilebelt/-/tilebelt-0.7.1.tgz", + "integrity": "sha1-2fQY262cpIO0Ql3Q+GuQZXFv1nk=" + }, + "tilejson": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tilejson/-/tilejson-1.0.3.tgz", + "integrity": "sha1-Kqmm8qelhruglsi+49uFIbr6D4g=", + "requires": { + "agentkeepalive": "2.0.2", + "requestretry": "1.4.0", + "tiletype": "0.1.0" + } + }, + "tilelive": { + "version": "5.12.3", + "resolved": "https://registry.npmjs.org/tilelive/-/tilelive-5.12.3.tgz", + "integrity": "sha1-nIx3DhGUqo01PZodAxR0BDB83r0=", + "requires": { + "minimist": "0.2.0", + "progress-stream": "0.5.0", + "queue-async": "1.0.7", + "sphericalmercator": "1.0.5" + }, + "dependencies": { + "minimist": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", + "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=" + } + } + }, + "tiletype": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tiletype/-/tiletype-0.1.0.tgz", + "integrity": "sha1-C5ymVPjAp8zM2SDt9ZTT7pSJwpc=" + }, "timers-browserify": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", @@ -7703,6 +8906,14 @@ } } }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "requires": { + "punycode": "1.4.1" + } + }, "trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", @@ -7751,6 +8962,46 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=" + }, + "turf-bbox-polygon": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/turf-bbox-polygon/-/turf-bbox-polygon-1.0.2.tgz", + "integrity": "sha1-SIsiP7eHdhFh+jMr/6Hdj9W9CdE=", + "requires": { + "turf-polygon": "1.0.3" + } + }, + "turf-envelope": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/turf-envelope/-/turf-envelope-1.0.2.tgz", + "integrity": "sha1-a5ab3PZ+VTyV8wIHHmtLXxEsAAg=", + "requires": { + "turf-bbox-polygon": "1.0.2", + "turf-extent": "1.0.4" + } + }, + "turf-extent": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/turf-extent/-/turf-extent-1.0.4.tgz", + "integrity": "sha1-nyjOEa+RatqIhioGIHRli6P+DwE=", + "requires": { + "turf-meta": "1.0.2" + } + }, + "turf-meta": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/turf-meta/-/turf-meta-1.0.2.tgz", + "integrity": "sha1-dNTElZOOskdypwsxoVfuPU/Wuhs=" + }, + "turf-polygon": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/turf-polygon/-/turf-polygon-1.0.3.tgz", + "integrity": "sha1-Zx3TSEmGRQkoGvGOI29np0SMY2M=" + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -7763,8 +9014,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "ultron": { "version": "1.0.2", @@ -7784,6 +9034,16 @@ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=" + }, "unherit": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", @@ -8021,8 +9281,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "vali-date": { "version": "1.0.0", @@ -8040,6 +9299,14 @@ "spdx-expression-parse": "1.0.4" } }, + "vector-tile": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/vector-tile/-/vector-tile-1.3.0.tgz", + "integrity": "sha1-BtUWqDsGPwTILvU5zxuxrr62lrQ=", + "requires": { + "point-geometry": "0.0.0" + } + }, "vfile": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.2.0.tgz", @@ -8172,6 +9439,106 @@ "indexof": "0.0.1" } }, + "vt-geojson": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vt-geojson/-/vt-geojson-2.1.1.tgz", + "integrity": "sha1-jK5X3yc6cxX38kdsJNjMNQvAy6o=", + "requires": { + "JSONStream": "0.10.0", + "concat-stream": "1.5.2", + "debug": "2.6.8", + "geojsonhint": "1.2.1", + "mbtiles": "0.8.2", + "pbf": "1.3.7", + "through2": "0.6.5", + "tile-cover": "2.4.1", + "tilejson": "1.0.3", + "tilelive": "5.12.3", + "turf-bbox-polygon": "1.0.2", + "turf-envelope": "1.0.2", + "vector-tile": "1.3.0", + "yargs": "3.32.0" + }, + "dependencies": { + "JSONStream": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", + "integrity": "sha1-dDSdDYlSK3HzDwoD/5vSDKbxKsA=", + "requires": { + "jsonparse": "0.0.5", + "through": "2.3.8" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=" + }, + "pbf": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-1.3.7.tgz", + "integrity": "sha1-Hj0Ee6PL6Ahq6FSiVQOrRTfUM10=", + "requires": { + "ieee754": "1.1.8", + "resolve-protobuf-schema": "2.0.0" + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + }, + "yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "requires": { + "camelcase": "2.1.1", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "os-locale": "1.4.0", + "string-width": "1.0.2", + "window-size": "0.1.4", + "y18n": "3.2.1" + } + } + } + }, + "vt-pbf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.0.tgz", + "integrity": "sha512-UUCGPkpT1P/bm3R3/HX0SCnRSto44xXx0WuLFVG6C7KspdfQfU+84etoO6cITAGCdq8V5DjuWfDhvk/pyTyt3Q==", + "requires": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "1.3.0", + "pbf": "3.0.5" + } + }, "watchify": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/watchify/-/watchify-3.9.0.tgz", @@ -8236,6 +9603,11 @@ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -8246,7 +9618,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, "requires": { "string-width": "1.0.2", "strip-ansi": "3.0.1" @@ -8292,14 +9663,12 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" }, "yargs": { "version": "6.6.0", diff --git a/package.json b/package.json index 2302bc9..de31250 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,11 @@ "dependencies": { "@mapbox/vector-tile": "^1.3.0", "@turf/bbox": "^4.3.0", + "geojson-vt": "^2.4.0", "pbf": "^3.0.5", "rbush": "^2.0.1", + "vt-geojson": "^2.1.1", + "vt-pbf": "^3.1.0", "webworkify": "^1.5.0" } } diff --git a/tile_cache.js b/tile_cache.js index 8808af5..57eb1ed 100644 --- a/tile_cache.js +++ b/tile_cache.js @@ -195,7 +195,7 @@ export default class TileCache { let node = this._head; let out = ''; while (node !== null) { - out += `(coords = ${node.data.tileKey}, feature count = ${Object.keys(this._cache[node.data.tileKey].tile._features).length}) -> `; + out += `(coords = ${node.data.tileKey}, feature count = ${Object.keys(this._cache[node.data.tileKey].tile._features).length}) -> \n`; node = node.next; } return out; diff --git a/worker.js b/worker.js index 53bd932..f1fa7aa 100644 --- a/worker.js +++ b/worker.js @@ -4,6 +4,7 @@ import Pbf from 'pbf'; import { VectorTile } from '@mapbox/vector-tile'; import geojsonvt from 'geojson-vt'; +import vtpbf from 'vt-pbf'; /** * Fetches and processes the vector tile for a given tile coordinate @@ -16,19 +17,23 @@ module.exports = function(self) { headers.append('If-Modified-Since', timestamp); } fetch(url) - .then(res => res.blob()) - .then(blob => { - const reader = new FileReader(); - reader.onloadend = () => { - const features = getFeatures(new VectorTile(new Pbf(reader.result)), coords); - for (let layer in features) { - //features[layer] = simplify(fatures[layer], coords); - //simplify(features[layer], coords); - } - self.postMessage({ coords, features }); + .then(res => { + if (res.status === 404) { + return self.postMessage({ coords, features: [] }); } - reader.readAsArrayBuffer(blob); - }); + res.blob() + .then(blob => { + const reader = new FileReader(); + reader.onloadend = () => { + const features = getFeatures(new VectorTile(new Pbf(reader.result)), coords); + self.postMessage({ coords, features }); + }; + reader.readAsArrayBuffer(blob); + }); + }) + .catch(e => { + console.log(e); + }); }); }; @@ -49,18 +54,3 @@ function getFeatures(vectorTile, coords) { } return features; } - -/** - * Build a geojson-vt index for this zoom level and have it do simplification - */ -function simplify(features, coords) { - const geojson = { - type: 'FeatureCollection', - features - }; - const tileIndex = geojsonvt(geojson, { indexMaxZoom: coords.z }); - const tile = tileIndex.getTile(coords.z, coords.x, coords.y).features; - console.log(tile); - const vt = new VectorTile(tile); - console.log(vt); -} From ea2b5a1de0d3975d253679a30783714d89453764 Mon Sep 17 00:00:00 2001 From: kelvinabrokwa Date: Tue, 19 Dec 2017 08:45:27 -0500 Subject: [PATCH 7/8] more --- ARCHITECHTURE.md | 82 -- dist/leaflet.vector-tiles.js | 613 +++++++++++--- package-lock.json | 1500 ++-------------------------------- package.json | 2 - tile_cache.js | 2 - 5 files changed, 552 insertions(+), 1647 deletions(-) delete mode 100644 ARCHITECHTURE.md diff --git a/ARCHITECHTURE.md b/ARCHITECHTURE.md deleted file mode 100644 index 7f1d2f8..0000000 --- a/ARCHITECHTURE.md +++ /dev/null @@ -1,82 +0,0 @@ -# Architecture - -### Tile Fetching and Parsing - -When a tile comes into view, Leaflet calls `createTile` with the tile's coordinates as an arguemnt in the form `{ x: , y: , z: }`. -`createTile` posts a message to the webworker to handle fetching, parsing, and simlpifying the tile. - - -#### Web Worker - -The webworker fetches the tile, parses it, converts it to GeoJSON, and then returns an object with layer names as keys and arrays of geojson features as values. - -When we request a tile from the url `///.pbf`, the vector tile server returns a protocol buffer encoded binary blob of the tile's vector data. -We read the blob using a [`FileReader`](https://developer.mozilla.org/en-US/docs/Web/API/FileReader), decode the blob using the [pbf library](https://github.com/mapbox/pbf) and then parse the JSON output of that using the [vector-tile-js library](https://github.com/mapbox/vector-tile-js/). -That outputs [`VectorTileFeature`s](https://github.com/mapbox/vector-tile-js#vectortilefeature) for each feature in the tile, grouped by layer. -We can now iterate over each tile and feature and convert to GeoJSON as a method of projecting from pixel coordinates to lat/lons. -Now we create a [geojson-vt](https://github.com/mapbox/geojson-vt) index for the single zoom level of the tile we are processing. -geojson-vt simplifies and drops lines and polygons appropriateley so this gives us tile simplification. -Then, we request the simplified ile from the geojson-vt index. -Then we convert the tile back to GeoJSON using vector-tile-js. -Then we post the GeoJSON to the main thread with the coords. - -*TODO: webworker pool* - - -### Tile Data Structure - -The `Tile` data type encapsulates most of the functionality for storing the data of and rendering a given tile. - -### Tile Caching - -The tile cache is an implementation of a [Least Recently Used (LRU) cache](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29). -Internally, a doubly linked list is used to track the order of access (and insertion) of all items in the cache and a hash table is used to hold the data. Each node in the linked list holds `next` and `previous` pointers as well as a `data` field which holds the tile key used to access the tile in the cache's hash table. -When an item is first placed in the cache, we create an entry for it in the linked list, placing it at the head. -The tile itself is then placed in the hash table. -If the length of the list is greater than the maximum cache size, we pop the tail node of the list and use the tile key therein to delete the tile from the hash table. -Whenever a tile is fetched from (or reinserted into) the cache, its corresponding node is moved to the head of the list. - -### Styling - -Styling is primarily feature property based. -That is, a style applies to a feature when that feature has the corresponding `key: value` pair in its properties. -For example, the following style: - -```js -style: { - treeType: { - oak: { - color: 'green' - } - } -} -``` - -would apply to a feature like: - -```js -{ - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [11, 78] - }, - properties: { - treeType: 'oak' - id: 12 - } -} -``` - -The hierarchy of styling is as follows: - -- default Leaflet styles for the layer -- property based styles as applied at library instantiation -- property based style modifications made at run time -- style modifications for the specific feature (by feature id) - -TODO - -- Layer based styling - -### Indexing and Searching diff --git a/dist/leaflet.vector-tiles.js b/dist/leaflet.vector-tiles.js index c60789c..7eb7301 100644 --- a/dist/leaflet.vector-tiles.js +++ b/dist/leaflet.vector-tiles.js @@ -5,6 +5,10 @@ var _tile = require('./tile'); var _tile2 = _interopRequireDefault(_tile); +var _tile_cache = require('./tile_cache'); + +var _tile_cache2 = _interopRequireDefault(_tile_cache); + var _feature = require('./feature'); var _feature2 = _interopRequireDefault(_feature); @@ -17,6 +21,27 @@ var _pbf2 = _interopRequireDefault(_pbf); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +/** + * Checks whether the given tile coordinates is within the normal range + * + * @param {Object} coords + * @param {number} coords.x + * @param {number} coords.y + * @param {number} coords.z + * @returns {boolean} true if the coordinates correspond to a valid tile + */ +function tileIsValid(coords) { + if (coords.x < 0 || coords.y < 0 || coords.z < 0) { + return false; + } + var nTiles = Math.pow(4, coords.z); + var maxIndex = Math.sqrt(nTiles) - 1; + if (coords.x > maxIndex || coords.y > maxIndex) { + return false; + } + return true; +} + /** * Manages interactive tiles of data * @@ -35,7 +60,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * } * }).addTo(map); */ - L.VectorTiles = L.GridLayer.extend({ style: {}, @@ -61,13 +85,15 @@ L.VectorTiles = L.GridLayer.extend({ // the FeatureGroup that holds per tile FeatureGroups this._featureGroup = L.featureGroup(); - // show tile boundaries + // show tile boundaries and log tile loading and unloading this._debug = options.debug; + // store Tile objects representing vector tiles + // keyed on tileKeys this._vectorTiles = {}; - // property based style modifications - // for highlighting and junk + // a lookup table for property value based styling + // maps property names to values to style objects // this._propertyStyles = { // propertyName: { // value1: { L.Path style options } @@ -75,18 +101,24 @@ L.VectorTiles = L.GridLayer.extend({ // } this._propertyStyles = {}; - // property based toggling + // a lookup table for whether or not features with property values are on the map + // maps property names to values to booleans this._propertyOnMap = {}; - // track individual feature style modifications + // a lookup table for individual feature styles + // maps feaure ids to style objects this._featureStyles = {}; - // mark individual features as on or off the map + // a lookup table for whether or not individual features are on the map + // maps feature ids to booleans this._featureOnMap = {}; // mark a tile for destruction in case it is unloaded before it loads this._toDestroy = {}; + // tile cache + this._tileCache = new _tile_cache2.default(50, this._debug); + // mark a tile as loaded // this is needed because if a tile is unloaded before its finished loading // we need to wait for it to finish loading before we can clean up @@ -99,10 +131,9 @@ L.VectorTiles = L.GridLayer.extend({ // listen for tileunload event and clean up old features this.on('tileunload', function (e) { - // Leaflet will not call createTile for tiles with negative - // coordinates but it will fire unload on them so - // ignore those events - if (e.coords.x < 0 || e.coords.y < 0 || e.coords.z < 0) { + // Leaflet will not call createTile on invalid tile coordinates but it will fire + // tileunload on it. Ignore these. + if (!tileIsValid(e.coords)) { return; } @@ -112,6 +143,9 @@ L.VectorTiles = L.GridLayer.extend({ // is finished loading if (!(tileKey in _this._vectorTiles) || !_this._vectorTiles[tileKey].loaded) { // invalidate the tile so that it is deleted when its done loading + if (_this._debug) { + console.log('marking tile', e.coords, 'for deletion after loading'); + } _this._toDestroy[tileKey] = true; } else { // destroy it immediately @@ -179,11 +213,9 @@ L.VectorTiles = L.GridLayer.extend({ /** - * This method: - * - fetches the data for the tile - * - adds all of its features to the map - * - adds its features to the internal data structure - * - inserts its features into the a spatial tree + * This method fetches vector tile data from the network and creates a tile + * in the event that the tile is cached and the HTTP request returns a 304, it + * uses the tile from the cache * * @param {Object} coords * @param {Function} done @@ -192,6 +224,9 @@ L.VectorTiles = L.GridLayer.extend({ * @private */ createTile: function createTile(coords, done) { + if (this._debug) { + console.log("creating tile:", coords); + } var tile = L.DomUtil.create('div', 'leaflet-tile'); if (this.options.debug) { // show tile boundaries @@ -211,100 +246,155 @@ L.VectorTiles = L.GridLayer.extend({ return; } - var tile = new _tile2.default(coords.x, coords.y, coords.z); + var tile = this._tileCache.get(tileKey); + + if (!tile) { + tile = new _tile2.default(coords.x, coords.y, coords.z); + } + this._vectorTiles[tileKey] = tile; + if (this._debug) { + // start timer + tile.fetchStartTime = new Date(); + } + // fetch vector tile data for this tile var url = L.Util.template(this._url, coords); - fetch(url).then(function (res) { - return res.blob(); - }).then(function (blob) { - var reader = new FileReader(); - return new Promise(function (resolve, reject) { - reader.onloadend = function () { - resolve(new _vectorTile.VectorTile(new _pbf2.default(reader.result))); - }; - reader.readAsArrayBuffer(blob); - }); - }).then(function (vtTile) { - for (var vtLayerName in vtTile.layers) { - // break out if this tile has already be unloaded - if (_this2._toDestroy[tileKey]) { - break; - } - var vtLayer = vtTile.layers[vtLayerName]; - for (var j = 0; j < vtLayer.length; j++) { + var headers = new Headers(); + if (tile.timeCreated) { + headers.append('If-Modified-Since', tile.timeCreated); + } + fetch(url, { headers: headers }).then(function (res) { + // use cached tile + if (res.status == '304') { + // add tile to FeatureGroup to add to map + tile.addTo(_this2._featureGroup); + return; + } + + // record time that tile was retrieved + tile.timeCreated = new Date().getTime(); + + // parse new vector tile + res.blob().then(function (blob) { + if (_this2._debug) { + var fetchTimeMs = new Date() - tile.fetchStartTime; + console.log('tile ' + tileKey + ' with size ' + blob.size + ' bytes fetched in ' + fetchTimeMs + ' ms (' + fetchTimeMs / 1000 + ' s)'); + tile.processStartTime = new Date(); + } + + var reader = new FileReader(); + return new Promise(function (resolve, reject) { + reader.onloadend = function () { + resolve(new _vectorTile.VectorTile(new _pbf2.default(reader.result))); + }; + reader.readAsArrayBuffer(blob); + }); + }).then(function parseVectorTile(vtTile) { + for (var vtLayerName in vtTile.layers) { // break out if this tile has already be unloaded - if (_this2._toDestroy[tileKey]) { + if (this._toDestroy[tileKey]) { + if (this._debug) { + console.log('Tile', coords, 'stopped while loading'); + } break; } - var vtFeature = vtLayer.feature(j); - - var geojson = vtFeature.toGeoJSON(coords.x, coords.y, coords.z); - var id = _this2.options.getFeatureId(geojson); - var layer = _this2._geojsonToLayer(geojson); - if (!layer) { - // unsupported geometry type - continue; - } - var feature = new _feature2.default(id, geojson, layer); + var vtLayer = vtTile.layers[vtLayerName]; - tile.addFeature(feature); + for (var j = 0; j < vtLayer.length; j++) { + // break out if this tile has already be unloaded + if (this._toDestroy[tileKey]) { + if (this._debug) { + console.log('Tile', coords, 'stopped while loading'); + } + break; + } - var style = {}; - var onMap = true; - var prop = void 0; + var vtFeature = vtLayer.feature(j); - // property based styles - for (prop in geojson.properties) { - // apply style from options - if (prop in _this2.options.style && geojson.properties[prop] in _this2.options.style[prop]) { - Object.assign(style, _this2.options.style[prop][geojson.properties[prop]]); + var geojson = vtFeature.toGeoJSON(coords.x, coords.y, coords.z); + var id = this.options.getFeatureId(geojson); + var layer = this._geojsonToLayer(geojson); + if (!layer) { + // unsupported geometry type + continue; } - // apply style modifications - if (prop in _this2._propertyStyles && geojson.properties[prop] in _this2._propertyStyles[prop]) { - Object.assign(style, _this2._propertyStyles[prop][geojson.properties[prop]]); + // create the Feature + var feature = new _feature2.default(id, geojson, layer); + + // add it to the tile + tile.addFeature(feature); + + // calculate its style and if its visible + var style = {}; + var onMap = true; + var prop = void 0; + + // property based styles + for (prop in geojson.properties) { + // apply style from options + if (prop in this.options.style && geojson.properties[prop] in this.options.style[prop]) { + Object.assign(style, this.options.style[prop][geojson.properties[prop]]); + } + + // apply style modifications + if (prop in this._propertyStyles && geojson.properties[prop] in this._propertyStyles[prop]) { + Object.assign(style, this._propertyStyles[prop][geojson.properties[prop]]); + } + + // put on map based on property + if (prop in this._propertyOnMap && geojson.properties[prop] in this._propertyOnMap[prop]) { + onMap = this._propertyOnMap[prop][geojson.properties[prop]]; + } } - // put on map based on property - if (prop in _this2._propertyOnMap && geojson.properties[prop] in _this2._propertyOnMap[prop]) { - onMap = _this2._propertyOnMap[prop][geojson.properties[prop]]; + // feature based styles + if (id in this._featureStyles) { + Object.assign(style, this._featureStyles[id]); } - } - // feature based styles - if (id in _this2._featureStyles) { - Object.assign(style, _this2._featureStyles[id]); - } + feature.setStyle(style); - feature.setStyle(style); + // feature based on map + if (id in this._featureOnMap) { + onMap = this._featureOnMap[id]; + } - // feature based on map - if (id in _this2._featureOnMap) { - onMap = _this2._featureOnMap[id]; + feature.putOnMap(onMap); } + } + + if (!this._toDestroy[tileKey]) { + // called when all features have been added to the tile + tile.init(); - feature.putOnMap(onMap); + // add the featureGroup of this tile to the map + tile.addTo(this._featureGroup); } - } - if (!_this2._toDestroy[tileKey]) { - // called when all features have been added to the tile - tile.init(); + // mark tile as loaded + tile.markAsLoaded(); - // add the featureGroup of this tile to the map - tile.featureGroup.addTo(_this2._featureGroup); - } + // cache the tile + this._tileCache.put(tileKey, tile); - // mark tile as loaded - tile.markAsLoaded(); + if (this._debug) { + var processTimeMs = new Date() - tile.processStartTime; + console.log('tile loaded: ' + coords + ' ' + tile.featureGroup.getLayers().length + ' features in ' + processTimeMs + ' ms (' + processTimeMs / 1000 + ' s)'); + } - // the tile has ~actually~ loaded - // the `tileload` event doesn't fire when `tileunload` fires first - // but in our case we still need to be finished loading to clean up - _this2.fire('vt_tileload', { coords: coords }); + // the tile has ~actually~ loaded + // the `tileload` event doesn't fire when `tileunload` fires first + // but in our case we still need to be finished loading to clean up + this.fire('vt_tileload', { coords: coords }); + }.bind(_this2)).catch(function (err) { + console.log(err); + }); + }).catch(function (err) { + console.log(err); }); }, @@ -317,10 +407,14 @@ L.VectorTiles = L.GridLayer.extend({ * @private */ destroyTile: function destroyTile(coords) { + if (this._debug) { + console.log("destroying tile:", coords); + } var tileKey = this._tileCoordsToKey(coords); + var tile = this._vectorTiles[tileKey]; // remove this tile's FeatureGroup from the map - this._featureGroup.removeLayer(this._vectorTiles[tileKey].featureGroup); + tile.removeFrom(this._featureGroup); // delete the tile's data delete this._vectorTiles[tileKey]; @@ -507,6 +601,18 @@ L.VectorTiles = L.GridLayer.extend({ }, + /** + * Set the maximum size of the cache + * + * @param {number} size + * returns {L.VectorTiles} this + */ + setTileCacheSize: function setTileCacheSize(size) { + this._tileCache.setSize(size); + return this; + }, + + /** * Convert a GeoJSON feature into a Leaflet feature * Point -> L.Circle @@ -515,7 +621,6 @@ L.VectorTiles = L.GridLayer.extend({ * Here we must make lon,lat (GeoJSON) into lat,lon (Leaflet) * * @param {Object} feature - * @param {string} id * @returns {L.Path} * @private */ @@ -576,7 +681,7 @@ L.VectorTiles = L.GridLayer.extend({ } }); -},{"./feature":2,"./tile":14,"@mapbox/vector-tile":4,"pbf":11}],2:[function(require,module,exports){ +},{"./feature":2,"./tile":14,"./tile_cache":15,"@mapbox/vector-tile":4,"pbf":11}],2:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -3490,22 +3595,31 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons * @private */ var Tile = function () { + + /** + * Constructor + * + * param {number} x - x coordinate of tile + * param {number} y - y coordinate of tile + * param {number} z - z coordinate of tile + */ function Tile(x, y, z) { _classCallCheck(this, Tile); this.x = x; this.y = y; this.z = z; - this.features = {}; + this.coords = { x: x, y: y, z: z }; + this._features = {}; this.loaded = false; - this.index = (0, _rbush2.default)(); + this._index = (0, _rbush2.default)(); this.featureGroup = L.featureGroup(); } /** - * Call this method when all features have been added to the tile + * We call this method when all features have been added to the tile * * @returns {Tile} this */ @@ -3515,41 +3629,22 @@ var Tile = function () { key: 'init', value: function init() { this.indexFeatures(); - this.render(); return this; } /** - * - */ - - }, { - key: 'render', - value: function render() { - for (var id in this.features) { - if (!this.features.hasOwnProperty(id)) { - continue; - } - var feature = this.features[id]; - if (feature.onMap) { - this.featureGroup.addLayer(feature.layer); - } - } - } - - /** - * + * This method adds all features in this tile to an rbush index */ }, { key: 'indexFeatures', value: function indexFeatures() { var bboxes = []; - for (var id in this.features) { - if (!this.features.hasOwnProperty(id)) { + for (var id in this._features) { + if (!this._features.hasOwnProperty(id)) { continue; } - var feature = this.features[id]; + var feature = this._features[id]; var geom = feature.geojson.geometry; var c = geom.coordinates; @@ -3587,18 +3682,19 @@ var Tile = function () { bboxes.push(item); } - this.index.load(bboxes); + // bulk insert into spatial index + this._index.load(bboxes); } /** * @param {string} id - * @returns {boolean} true is this tile contains a feature with the given id + * @returns {boolean} true if this tile contains a feature with the given id */ }, { key: 'contains', value: function contains(id) { - return id in this.features; + return id in this._features; } /** @@ -3609,7 +3705,8 @@ var Tile = function () { }, { key: 'addFeature', value: function addFeature(feature) { - this.features[feature.id] = feature; + this._features[feature.id] = feature; + this.featureGroup.addLayer(feature.layer); return this; } @@ -3626,8 +3723,8 @@ var Tile = function () { } var feature = this.getFeature(id); this.featureGroup.removeLayer(feature.layer); - this.index.remove(feature.indexEntry); - delete this.features[id]; + this._index.remove(feature.indexEntry); + delete this._features[id]; return this; } @@ -3639,7 +3736,36 @@ var Tile = function () { }, { key: 'getFeature', value: function getFeature(id) { - return this.features[id]; + return this._features[id]; + } + + /** + * This method adds this tile to the given layer by calling `addLayer` with the + * tile's underlying FeatureGroup + * + * @param {L.Map|L.FeatureGroup|L.LayerGroup} + * @returns {Tile} this + */ + + }, { + key: 'addTo', + value: function addTo(layer) { + layer.addLayer(this.featureGroup); + return this; + } + + /** + * This method removes this tile from the given layer by calling `removeLayer` with + * the tile's underlying FeatureGroup + * + * @param {L.Map|L.FeatureGroup|L.LayerGroup} + * @returns {Tile} this + */ + + }, { + key: 'removeFrom', + value: function removeFrom(layer) { + layer.removeLayer(this.featureGroup); } /** @@ -3654,12 +3780,13 @@ var Tile = function () { }, { key: 'search', value: function search(minX, minY, maxX, maxY) { - return this.index.search({ minX: minX, minY: minY, maxX: maxX, maxY: maxY }).map(function (r) { + return this._index.search({ minX: minX, minY: minY, maxX: maxX, maxY: maxY }).map(function (r) { return r.id; }); } /** + * Marks the tile as loaded * * @returns {Tile} this */ @@ -3684,8 +3811,8 @@ var Tile = function () { value: function toggleByProperty(property, value, on, toggled) { var feature = void 0; var geoj = void 0; - for (var id in this.features) { - if (!this.features.hasOwnProperty(id)) { + for (var id in this._features) { + if (!this._features.hasOwnProperty(id)) { continue; } feature = this.getFeature(id); @@ -3693,10 +3820,10 @@ var Tile = function () { if (property in geoj.properties && geoj.properties[property] === value) { if (toggled) { if (on) { - this.index.insert(feature.indexEntry); + this._index.insert(feature.indexEntry); this.featureGroup.addLayer(feature.layer); } else { - this.index.remove(feature.indexEntry); + this._index.remove(feature.indexEntry); this.featureGroup.removeLayer(feature.layer); } } @@ -3716,8 +3843,8 @@ var Tile = function () { key: 'restyleByProperty', value: function restyleByProperty(property, value, style) { var feature = void 0; - for (var id in this.features) { - if (!this.features.hasOwnProperty(id)) { + for (var id in this._features) { + if (!this._features.hasOwnProperty(id)) { continue; } feature = this.getFeature(id); @@ -3734,5 +3861,233 @@ var Tile = function () { exports.default = Tile; -},{"@turf/bbox":8,"rbush":13}]},{},[1]) -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJMZWFmbGV0LlZlY3RvclRpbGVzLmpzIiwiZmVhdHVyZS5qcyIsIm5vZGVfbW9kdWxlcy9AbWFwYm94L3BvaW50LWdlb21ldHJ5L2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL0BtYXBib3gvdmVjdG9yLXRpbGUvaW5kZXguanMiLCJub2RlX21vZHVsZXMvQG1hcGJveC92ZWN0b3ItdGlsZS9saWIvdmVjdG9ydGlsZS5qcyIsIm5vZGVfbW9kdWxlcy9AbWFwYm94L3ZlY3Rvci10aWxlL2xpYi92ZWN0b3J0aWxlZmVhdHVyZS5qcyIsIm5vZGVfbW9kdWxlcy9AbWFwYm94L3ZlY3Rvci10aWxlL2xpYi92ZWN0b3J0aWxlbGF5ZXIuanMiLCJub2RlX21vZHVsZXMvQHR1cmYvYmJveC9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9AdHVyZi9tZXRhL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL2llZWU3NTQvaW5kZXguanMiLCJub2RlX21vZHVsZXMvcGJmL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL3F1aWNrc2VsZWN0L2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL3JidXNoL2luZGV4LmpzIiwidGlsZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0FDQUE7Ozs7QUFDQTs7OztBQUNBOztBQUNBOzs7Ozs7QUFFQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW1CQSxFQUFFLFdBQUYsR0FBZ0IsRUFBRSxTQUFGLENBQVksTUFBWixDQUFtQjs7QUFFakMsU0FBTyxFQUYwQjs7QUFJakM7Ozs7Ozs7Ozs7QUFVQSxZQWRpQyxzQkFjdEIsR0Fkc0IsRUFjakIsT0FkaUIsRUFjUjtBQUFBOztBQUN2QixNQUFFLElBQUYsQ0FBTyxVQUFQLENBQWtCLE9BQWxCO0FBQ0EsTUFBRSxTQUFGLENBQVksU0FBWixDQUFzQixVQUF0QixDQUFpQyxJQUFqQyxDQUFzQyxJQUF0QyxFQUE0QyxPQUE1Qzs7QUFF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QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeFRBO0FBQ0E7QUFDQTtBQUNBOztBQ0hBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pPQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdnpCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMW1CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB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TCxFQUF1QztBQUNyQztBQUNEO0FBQ0Qsa0JBQVUsS0FBSyxVQUFMLENBQWdCLEVBQWhCLENBQVY7QUFDQSxlQUFPLFFBQVEsT0FBZjtBQUNBLFlBQUksWUFBWSxLQUFLLFVBQWpCLElBQStCLEtBQUssVUFBTCxDQUFnQixRQUFoQixNQUE4QixLQUFqRSxFQUF3RTtBQUN0RSxjQUFJLE9BQUosRUFBYTtBQUNYLGdCQUFJLEVBQUosRUFBUTtBQUNOLG1CQUFLLEtBQUwsQ0FBVyxNQUFYLENBQWtCLFFBQVEsVUFBMUI7QUFDQSxtQkFBSyxZQUFMLENBQWtCLFFBQWxCLENBQTJCLFFBQVEsS0FBbkM7QUFDRCxhQUhELE1BR087QUFDTCxtQkFBSyxLQUFMLENBQVcsTUFBWCxDQUFrQixRQUFRLFVBQTFCO0FBQ0EsbUJBQUssWUFBTCxDQUFrQixXQUFsQixDQUE4QixRQUFRLEtBQXRDO0FBQ0Q7QUFDRjtBQUNGO0FBQ0Y7QUFDRCxhQUFPLElBQVA7QUFDRDs7QUFFRDs7Ozs7Ozs7O3NDQU1rQixRLEVBQVUsSyxFQUFPLEssRUFBTztBQUN4QyxVQUFJLGdCQUFKO0FBQ0EsV0FBSyxJQUFNLEVBQVgsSUFBaUIsS0FBSyxRQUF0QixFQUFnQztBQUM5QixZQUFJLENBQUMsS0FBSyxRQUFMLENBQWMsY0FBZCxDQUE2QixFQUE3QixDQUFMLEVBQXVDO0FBQ3JDO0FBQ0Q7QUFDRCxrQkFBVSxLQUFLLFVBQUwsQ0FBZ0IsRUFBaEIsQ0FBVjtBQUNBLFlBQUksWUFBWSxRQUFRLE9BQVIsQ0FBZ0IsVUFBNUIsSUFDRyxRQUFRLE9BQVIsQ0FBZ0IsVUFBaEIsQ0FBMkIsUUFBM0IsTUFBeUMsS0FEaEQsRUFDdUQ7QUFDckQsa0JBQVEsS0FBUixDQUFjLFFBQWQsQ0FBdUIsS0FBdkI7QUFDRDtBQUNGO0FBQ0QsYUFBTyxJQUFQO0FBQ0Q7Ozs7OztrQkFqTWtCLEkiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiaW1wb3J0IFRpbGUgZnJvbSAnLi90aWxlJztcbmltcG9ydCBGZWF0dXJlIGZyb20gJy4vZmVhdHVyZSc7XG5pbXBvcnQgeyBWZWN0b3JUaWxlIH0gZnJvbSAnQG1hcGJveC92ZWN0b3ItdGlsZSc7XG5pbXBvcnQgUGJmIGZyb20gJ3BiZic7XG5cbi8qKlxuICogTWFuYWdlcyBpbnRlcmFjdGl2ZSB0aWxlcyBvZiBkYXRhXG4gKlxuICogQGNsYXNzIFZlY3RvclRpbGVzXG4gKiBAZXh0ZW5kcyBHcmlkTGF5ZXJcbiAqXG4gKiBAZXhhbXBsZVxuICogdmFyIHZ0TGF5ZXIgPSBuZXcgTC5WZWN0b3JUaWxlcygnaHR0cDovL215dGlsZXMuY29tL3t6fS97eH0ve3l9LnBiZicsIHtcbiAqICAgZGVidWc6IHRydWUsXG4gKiAgIHN0eWxlOiB7XG4gKiAgICAgcHJvcGVydHk6IHtcbiAqICAgICAgIHZhbHVlOiB7XG4gKiAgICAgICAgIGNvbG9yOiAncmVkJ1xuICogICAgICAgfVxuICogICAgIH1cbiAqICAgfVxuICogfSkuYWRkVG8obWFwKTtcbiAqL1xuXG5MLlZlY3RvclRpbGVzID0gTC5HcmlkTGF5ZXIuZXh0ZW5kKHtcblxuICBzdHlsZToge30sXG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdG9yXG4gICAqXG4gICAqIEBjb25zdHJ1Y3RzXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgVGhlIHRlbXBsYXRlIHVybCBmb3IgZmVjdGNoaW5nIHZlY3RvciB0aWxlc1xuICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBbb3B0aW9ucy5nZXRGZWF0dXJlSWRdXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMuZGVidWddXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0aW9ucy5zdHlsZV1cbiAgICovXG4gIGluaXRpYWxpemUodXJsLCBvcHRpb25zKSB7XG4gICAgTC5VdGlsLnNldE9wdGlvbnMob3B0aW9ucyk7XG4gICAgTC5HcmlkTGF5ZXIucHJvdG90eXBlLmluaXRpYWxpemUuY2FsbCh0aGlzLCBvcHRpb25zKTtcblxuICAgIHRoaXMuX3VybCA9IHVybDtcblxuICAgIC8vIHRoZSBGZWF0dXJlR3JvdXAgdGhhdCBob2xkcyBwZXIgdGlsZSBGZWF0dXJlR3JvdXBzXG4gICAgdGhpcy5fZmVhdHVyZUdyb3VwID0gTC5mZWF0dXJlR3JvdXAoKTtcblxuICAgIC8vIHNob3cgdGlsZSBib3VuZGFyaWVzXG4gICAgdGhpcy5fZGVidWcgPSBvcHRpb25zLmRlYnVnO1xuXG4gICAgdGhpcy5fdmVjdG9yVGlsZXMgPSB7fTtcblxuICAgIC8vIHByb3BlcnR5IGJhc2VkIHN0eWxlIG1vZGlmaWNhdGlvbnNcbiAgICAvLyBmb3IgaGlnaGxpZ2h0aW5nIGFuZCBqdW5rXG4gICAgLy8gdGhpcy5fcHJvcGVydHlTdHlsZXMgPSB7XG4gICAgLy8gICBwcm9wZXJ0eU5hbWU6IHtcbiAgICAvLyAgICAgdmFsdWUxOiB7IEwuUGF0aCBzdHlsZSBvcHRpb25zIH1cbiAgICAvLyAgIH1cbiAgICAvLyB9XG4gICAgdGhpcy5fcHJvcGVydHlTdHlsZXMgPSB7fTtcblxuICAgIC8vIHByb3BlcnR5IGJhc2VkIHRvZ2dsaW5nXG4gICAgdGhpcy5fcHJvcGVydHlPbk1hcCA9IHt9O1xuXG4gICAgLy8gdHJhY2sgaW5kaXZpZHVhbCBmZWF0dXJlIHN0eWxlIG1vZGlmaWNhdGlvbnNcbiAgICB0aGlzLl9mZWF0dXJlU3R5bGVzID0ge307XG5cbiAgICAvLyBtYXJrIGluZGl2aWR1YWwgZmVhdHVyZXMgYXMgb24gb3Igb2ZmIHRoZSBtYXBcbiAgICB0aGlzLl9mZWF0dXJlT25NYXAgPSB7fTtcblxuICAgIC8vIG1hcmsgYSB0aWxlIGZvciBkZXN0cnVjdGlvbiBpbiBjYXNlIGl0IGlzIHVubG9hZGVkIGJlZm9yZSBpdCBsb2Fkc1xuICAgIHRoaXMuX3RvRGVzdHJveSA9IHt9O1xuXG4gICAgLy8gbWFyayBhIHRpbGUgYXMgbG9hZGVkXG4gICAgLy8gdGhpcyBpcyBuZWVkZWQgYmVjYXVzZSBpZiBhIHRpbGUgaXMgdW5sb2FkZWQgYmVmb3JlIGl0cyBmaW5pc2hlZCBsb2FkaW5nXG4gICAgLy8gd2UgbmVlZCB0byB3YWl0IGZvciBpdCB0byBmaW5pc2ggbG9hZGluZyBiZWZvcmUgd2UgY2FuIGNsZWFuIHVwXG4gICAgdGhpcy5vbigndnRfdGlsZWxvYWQnLCAoZSkgPT4ge1xuICAgICAgY29uc3QgdGlsZUtleSA9IHRoaXMuX3RpbGVDb29yZHNUb0tleShlLmNvb3Jkcyk7XG4gICAgICBpZiAodGhpcy5fdG9EZXN0cm95W3RpbGVLZXldKSB7XG4gICAgICAgIHRoaXMuZGVzdHJveVRpbGUoZS5jb29yZHMpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gbGlzdGVuIGZvciB0aWxldW5sb2FkIGV2ZW50IGFuZCBjbGVhbiB1cCBvbGQgZmVhdHVyZXNcbiAgICB0aGlzLm9uKCd0aWxldW5sb2FkJywgKGUpID0+IHtcbiAgICAgIC8vIExlYWZsZXQgd2lsbCBub3QgY2FsbCBjcmVhdGVUaWxlIGZvciB0aWxlcyB3aXRoIG5lZ2F0aXZlXG4gICAgICAvLyBjb29yZGluYXRlcyBidXQgaXQgd2lsbCBmaXJlIHVubG9hZCBvbiB0aGVtIHNvXG4gICAgICAvLyBpZ25vcmUgdGhvc2UgZXZlbnRzXG4gICAgICBpZiAoZS5jb29yZHMueCA8IDAgfHwgZS5jb29yZHMueSA8IDAgfHwgZS5jb29yZHMueiA8IDApIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBjb25zdCB0aWxlS2V5ID0gdGhpcy5fdGlsZUNvb3Jkc1RvS2V5KGUuY29vcmRzKTtcblxuICAgICAgLy8gaWYgdGhlIHRpbGUgaGFzbid0IGxvYWRlZCB5ZXQsIG1hcmsgaXQgZm9yIGRlbGV0aW9uIGZvciB3aGVuIGl0XG4gICAgICAvLyBpcyBmaW5pc2hlZCBsb2FkaW5nXG4gICAgICBpZiAoISh0aWxlS2V5IGluIHRoaXMuX3ZlY3RvclRpbGVzKSB8fCAhdGhpcy5fdmVjdG9yVGlsZXNbdGlsZUtleV0ubG9hZGVkKSB7XG4gICAgICAgIC8vIGludmFsaWRhdGUgdGhlIHRpbGUgc28gdGhhdCBpdCBpcyBkZWxldGVkIHdoZW4gaXRzIGRvbmUgbG9hZGluZ1xuICAgICAgICB0aGlzLl90b0Rlc3Ryb3lbdGlsZUtleV0gPSB0cnVlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gZGVzdHJveSBpdCBpbW1lZGlhdGVseVxuICAgICAgICB0aGlzLmRlc3Ryb3lUaWxlKGUuY29vcmRzKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSxcblxuICBvbkFkZChtYXApIHtcbiAgICBMLkdyaWRMYXllci5wcm90b3R5cGUub25BZGQuY2FsbCh0aGlzLCBtYXApO1xuICAgIHRoaXMuX21hcCA9IG1hcDtcbiAgICB0aGlzLl9mZWF0dXJlR3JvdXAuYWRkVG8odGhpcy5fbWFwKTtcbiAgfSxcblxuICAvKipcbiAgICogUmV0dXJucyBhbiBhcnJheSBvZiBmZWF0dXJlIGlkcyBuZWFyIGEgZ2l2ZW4gcG9pbnRcbiAgICpcbiAgICogQHBhcmFtIHtMLkxhdExuZ30gbWluXG4gICAqIEBwYXJhbSB7TC5MYXRMbmd9IG1heFxuICAgKiBAcmV0dXJucyB7QXJyYXk8c3RyaW5nPn1cbiAgICovXG4gIHNlYXJjaChtaW4sIG1heCkge1xuICAgIGlmICghdGhpcy5fbWFwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1ZlY3RvciB0aWxlIGxheWVyIG5vdCBhZGRlZCB0byB0aGUgbWFwLicpO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdHMgPSBuZXcgU2V0KCk7XG4gICAgY29uc3QgbWluWCA9IG1pbi5sbmc7XG4gICAgY29uc3QgbWluWSA9IG1pbi5sYXQ7XG4gICAgY29uc3QgbWF4WCA9IG1heC5sbmc7XG4gICAgY29uc3QgbWF4WSA9IG1heC5sYXQ7XG5cbiAgICBmb3IgKGNvbnN0IHRpbGVLZXkgaW4gdGhpcy5fdmVjdG9yVGlsZXMpIHtcbiAgICAgIGlmICghdGhpcy5fdmVjdG9yVGlsZXMuaGFzT3duUHJvcGVydHkodGlsZUtleSkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBmb3IgKGNvbnN0IHJlc3VsdCBvZiB0aGlzLl92ZWN0b3JUaWxlc1t0aWxlS2V5XS5zZWFyY2gobWluWCwgbWluWSwgbWF4WCwgbWF4WSkpIHtcbiAgICAgICAgcmVzdWx0cy5hZGQocmVzdWx0KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gQXJyYXkuZnJvbShyZXN1bHRzKTtcbiAgfSxcblxuICAvKipcbiAgICogVGhpcyBtZXRob2Q6XG4gICAqICAgLSBmZXRjaGVzIHRoZSBkYXRhIGZvciB0aGUgdGlsZVxuICAgKiAgIC0gYWRkcyBhbGwgb2YgaXRzIGZlYXR1cmVzIHRvIHRoZSBtYXBcbiAgICogICAtIGFkZHMgaXRzIGZlYXR1cmVzIHRvIHRoZSBpbnRlcm5hbCBkYXRhIHN0cnVjdHVyZVxuICAgKiAgIC0gaW5zZXJ0cyBpdHMgZmVhdHVyZXMgaW50byB0aGUgYSBzcGF0aWFsIHRyZWVcbiAgICpcbiAgICogQHBhcmFtIHtPYmplY3R9IGNvb3Jkc1xuICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBkb25lXG4gICAqIEBmaXJlcyB2dF90aWxlbG9hZFxuICAgKiBAcmV0dXJucyBET00gZWxlbWVudFxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgY3JlYXRlVGlsZShjb29yZHMsIGRvbmUpIHtcbiAgICBjb25zdCB0aWxlID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgJ2xlYWZsZXQtdGlsZScpO1xuICAgIGlmICh0aGlzLm9wdGlvbnMuZGVidWcpIHtcbiAgICAgIC8vIHNob3cgdGlsZSBib3VuZGFyaWVzXG4gICAgICB0aWxlLnN0eWxlLm91dGxpbmUgPSAnMXB4IHNvbGlkIHJlZCc7XG4gICAgfVxuICAgIHRoaXMuX2NyZWF0ZVRpbGUoY29vcmRzKTtcbiAgICBkb25lKG51bGwsIHRpbGUpO1xuICAgIHJldHVybiB0aWxlO1xuICB9LFxuXG4gIF9jcmVhdGVUaWxlKGNvb3Jkcykge1xuICAgIGNvbnN0IHRpbGVLZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKTtcblxuICAgIC8vIHRpbGUgaGFzIGFscmVhZHkgYmVlbiB1bmxvYWRlZFxuICAgIGlmICh0aGlzLl90b0Rlc3Ryb3lbdGlsZUtleV0pIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCB0aWxlID0gbmV3IFRpbGUoY29vcmRzLngsIGNvb3Jkcy55LCBjb29yZHMueik7XG4gICAgdGhpcy5fdmVjdG9yVGlsZXNbdGlsZUtleV0gPSB0aWxlO1xuXG4gICAgLy8gZmV0Y2ggdmVjdG9yIHRpbGUgZGF0YSBmb3IgdGhpcyB0aWxlXG4gICAgY29uc3QgdXJsID0gTC5VdGlsLnRlbXBsYXRlKHRoaXMuX3VybCwgY29vcmRzKTtcbiAgICBmZXRjaCh1cmwpXG4gICAgICAudGhlbihyZXMgPT4gcmVzLmJsb2IoKSlcbiAgICAgIC50aGVuKGJsb2IgPT4ge1xuICAgICAgICBjb25zdCByZWFkZXIgPSBuZXcgRmlsZVJlYWRlcigpO1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgIHJlYWRlci5vbmxvYWRlbmQgPSAoKSA9PiB7XG4gICAgICAgICAgICByZXNvbHZlKG5ldyBWZWN0b3JUaWxlKG5ldyBQYmYocmVhZGVyLnJlc3VsdCkpKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmVhZGVyLnJlYWRBc0FycmF5QnVmZmVyKGJsb2IpO1xuICAgICAgICB9KTtcbiAgICAgIH0pXG4gICAgICAudGhlbih2dFRpbGUgPT4ge1xuICAgICAgICBmb3IgKGNvbnN0IHZ0TGF5ZXJOYW1lIGluIHZ0VGlsZS5sYXllcnMpIHtcbiAgICAgICAgICAvLyBicmVhayBvdXQgaWYgdGhpcyB0aWxlIGhhcyBhbHJlYWR5IGJlIHVubG9hZGVkXG4gICAgICAgICAgaWYgKHRoaXMuX3RvRGVzdHJveVt0aWxlS2V5XSkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IHZ0TGF5ZXIgPSB2dFRpbGUubGF5ZXJzW3Z0TGF5ZXJOYW1lXTtcbiAgICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHZ0TGF5ZXIubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIC8vIGJyZWFrIG91dCBpZiB0aGlzIHRpbGUgaGFzIGFscmVhZHkgYmUgdW5sb2FkZWRcbiAgICAgICAgICAgIGlmICh0aGlzLl90b0Rlc3Ryb3lbdGlsZUtleV0pIHtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCB2dEZlYXR1cmUgPSB2dExheWVyLmZlYXR1cmUoaik7XG5cbiAgICAgICAgICAgIGNvbnN0IGdlb2pzb24gPSB2dEZlYXR1cmUudG9HZW9KU09OKGNvb3Jkcy54LCBjb29yZHMueSwgY29vcmRzLnopO1xuICAgICAgICAgICAgY29uc3QgaWQgPSB0aGlzLm9wdGlvbnMuZ2V0RmVhdHVyZUlkKGdlb2pzb24pO1xuICAgICAgICAgICAgY29uc3QgbGF5ZXIgPSB0aGlzLl9nZW9qc29uVG9MYXllcihnZW9qc29uKTtcbiAgICAgICAgICAgIGlmICghbGF5ZXIpIHtcbiAgICAgICAgICAgICAgLy8gdW5zdXBwb3J0ZWQgZ2VvbWV0cnkgdHlwZVxuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgZmVhdHVyZSA9IG5ldyBGZWF0dXJlKGlkLCBnZW9qc29uLCBsYXllcik7XG5cbiAgICAgICAgICAgIHRpbGUuYWRkRmVhdHVyZShmZWF0dXJlKTtcblxuICAgICAgICAgICAgY29uc3Qgc3R5bGUgPSB7fTtcbiAgICAgICAgICAgIGxldCBvbk1hcCA9IHRydWU7XG4gICAgICAgICAgICBsZXQgcHJvcDtcblxuICAgICAgICAgICAgLy8gcHJvcGVydHkgYmFzZWQgc3R5bGVzXG4gICAgICAgICAgICBmb3IgKHByb3AgaW4gZ2VvanNvbi5wcm9wZXJ0aWVzKSB7XG4gICAgICAgICAgICAgIC8vIGFwcGx5IHN0eWxlIGZyb20gb3B0aW9uc1xuICAgICAgICAgICAgICBpZiAocHJvcCBpbiB0aGlzLm9wdGlvbnMuc3R5bGVcbiAgICAgICAgICAgICAgICAgICYmIGdlb2pzb24ucHJvcGVydGllc1twcm9wXSBpbiB0aGlzLm9wdGlvbnMuc3R5bGVbcHJvcF0pIHtcbiAgICAgICAgICAgICAgICBPYmplY3QuYXNzaWduKHN0eWxlLCB0aGlzLm9wdGlvbnMuc3R5bGVbcHJvcF1bZ2VvanNvbi5wcm9wZXJ0aWVzW3Byb3BdXSk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAvLyBhcHBseSBzdHlsZSBtb2RpZmljYXRpb25zXG4gICAgICAgICAgICAgIGlmIChwcm9wIGluIHRoaXMuX3Byb3BlcnR5U3R5bGVzXG4gICAgICAgICAgICAgICAgICAmJiBnZW9qc29uLnByb3BlcnRpZXNbcHJvcF0gaW4gdGhpcy5fcHJvcGVydHlTdHlsZXNbcHJvcF0pIHtcbiAgICAgICAgICAgICAgICBPYmplY3QuYXNzaWduKHN0eWxlLCB0aGlzLl9wcm9wZXJ0eVN0eWxlc1twcm9wXVtnZW9qc29uLnByb3BlcnRpZXNbcHJvcF1dKTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIC8vIHB1dCBvbiBtYXAgYmFzZWQgb24gcHJvcGVydHlcbiAgICAgICAgICAgICAgaWYgKHByb3AgaW4gdGhpcy5fcHJvcGVydHlPbk1hcFxuICAgICAgICAgICAgICAgICAgJiYgZ2VvanNvbi5wcm9wZXJ0aWVzW3Byb3BdIGluIHRoaXMuX3Byb3BlcnR5T25NYXBbcHJvcF0pIHtcbiAgICAgICAgICAgICAgICBvbk1hcCA9IHRoaXMuX3Byb3BlcnR5T25NYXBbcHJvcF1bZ2VvanNvbi5wcm9wZXJ0aWVzW3Byb3BdXTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBmZWF0dXJlIGJhc2VkIHN0eWxlc1xuICAgICAgICAgICAgaWYgKGlkIGluIHRoaXMuX2ZlYXR1cmVTdHlsZXMpIHtcbiAgICAgICAgICAgICAgT2JqZWN0LmFzc2lnbihzdHlsZSwgdGhpcy5fZmVhdHVyZVN0eWxlc1tpZF0pO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBmZWF0dXJlLnNldFN0eWxlKHN0eWxlKTtcblxuICAgICAgICAgICAgLy8gZmVhdHVyZSBiYXNlZCBvbiBtYXBcbiAgICAgICAgICAgIGlmIChpZCBpbiB0aGlzLl9mZWF0dXJlT25NYXApIHtcbiAgICAgICAgICAgICAgb25NYXAgPSB0aGlzLl9mZWF0dXJlT25NYXBbaWRdO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBmZWF0dXJlLnB1dE9uTWFwKG9uTWFwKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXRoaXMuX3RvRGVzdHJveVt0aWxlS2V5XSkge1xuICAgICAgICAgIC8vIGNhbGxlZCB3aGVuIGFsbCBmZWF0dXJlcyBoYXZlIGJlZW4gYWRkZWQgdG8gdGhlIHRpbGVcbiAgICAgICAgICB0aWxlLmluaXQoKTtcblxuICAgICAgICAgIC8vIGFkZCB0aGUgZmVhdHVyZUdyb3VwIG9mIHRoaXMgdGlsZSB0byB0aGUgbWFwXG4gICAgICAgICAgdGlsZS5mZWF0dXJlR3JvdXAuYWRkVG8odGhpcy5fZmVhdHVyZUdyb3VwKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIG1hcmsgdGlsZSBhcyBsb2FkZWRcbiAgICAgICAgdGlsZS5tYXJrQXNMb2FkZWQoKTtcblxuICAgICAgICAvLyB0aGUgdGlsZSBoYXMgfmFjdHVhbGx5fiBsb2FkZWRcbiAgICAgICAgLy8gdGhlIGB0aWxlbG9hZGAgZXZlbnQgZG9lc24ndCBmaXJlIHdoZW4gYHRpbGV1bmxvYWRgIGZpcmVzIGZpcnN0XG4gICAgICAgIC8vIGJ1dCBpbiBvdXIgY2FzZSB3ZSBzdGlsbCBuZWVkIHRvIGJlIGZpbmlzaGVkIGxvYWRpbmcgdG8gY2xlYW4gdXBcbiAgICAgICAgdGhpcy5maXJlKCd2dF90aWxlbG9hZCcsIHsgY29vcmRzIH0pO1xuICAgICAgfSk7XG4gIH0sXG5cbiAgLyoqXG4gICAqIFJlbW92ZSB0aGUgZmVhdHVyZXMgb2YgYSB0aWxlIGZyb20gdGhlIG1hcCBhbmQgZGVsZXRlIHRoYXQgdGlsZSdzXG4gICAqIGRhdGEgc3RydWN0dXJlXG4gICAqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBjb29yZHNcbiAgICogQHByaXZhdGVcbiAgICovXG4gIGRlc3Ryb3lUaWxlKGNvb3Jkcykge1xuICAgIGNvbnN0IHRpbGVLZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKTtcblxuICAgIC8vIHJlbW92ZSB0aGlzIHRpbGUncyBGZWF0dXJlR3JvdXAgZnJvbSB0aGUgbWFwXG4gICAgdGhpcy5fZmVhdHVyZUdyb3VwLnJlbW92ZUxheWVyKHRoaXMuX3ZlY3RvclRpbGVzW3RpbGVLZXldLmZlYXR1cmVHcm91cCk7XG5cbiAgICAvLyBkZWxldGUgdGhlIHRpbGUncyBkYXRhXG4gICAgZGVsZXRlIHRoaXMuX3ZlY3RvclRpbGVzW3RpbGVLZXldO1xuXG4gICAgLy8gcmVtb3ZlIGRlbGV0ZSBtYXJrZXJcbiAgICBpZiAodGhpcy5fdG9EZXN0cm95W3RpbGVLZXldKSB7XG4gICAgICBkZWxldGUgdGhpcy5fdG9EZXN0cm95W3RpbGVLZXldO1xuICAgIH1cbiAgfSxcblxuICAvKipcbiAgICogUmVtb3ZlcyBmZWF0dXJlcyBmcm9tIHRoZSBtYXAgYnkgcHJvcGVydHkuXG4gICAqIFdyYXBwZXIgZnVuY3Rpb24gb2YgYF90b2dnbGVCeVByb3BlcnR5YC5cbiAgICogRXF1aXZhbGVudCB0byBgdGhpcy5fdG9nZ2xlQnlQcm9wZXJ0eShwcm9wZXJ0eSwgdmFsdWUsIGZhbHNlKWAuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eVxuICAgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWVcbiAgICovXG4gIGhpZGVCeVByb3BlcnR5KHByb3BlcnR5LCB2YWx1ZSkge1xuICAgIHRoaXMuX3RvZ2dsZUJ5UHJvcGVydHkocHJvcGVydHksIHZhbHVlLCBmYWxzZSk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG5cbiAgLyoqXG4gICAqIEFkZCBmZWF0dXJlcyB0byB0aGUgbWFwIGJ5IHByb3BlcnR5LlxuICAgKiBXcmFwcGVyIGZ1bmN0aW9uIG9mIGBfdG9nZ2xlQnlQcm9wZXJ0eWAuXG4gICAqIEVxdWl2YWxlbnQgdG8gYHRoaXMuX3RvZ2dsZUJ5UHJvcGVydHkocHJvcGVydHksIHZhbHVlLCB0cnVlKWAuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eVxuICAgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWVcbiAgICovXG4gIHNob3dCeVByb3BlcnR5KHByb3BlcnR5LCB2YWx1ZSkge1xuICAgIHRoaXMuX3RvZ2dsZUJ5UHJvcGVydHkocHJvcGVydHksIHZhbHVlLCB0cnVlKTtcbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICAvKipcbiAgICogSXRlcmF0ZXMgb3ZlciBhbGwgZmVhdHVyZXMgYW5kIGFkZCB0aGVtIHRvIG9yIHJlbW92ZXMgdGhlbSBmcm9tXG4gICAqIHRoZSBtYXAgYmFzZWQgb24gYSBwcm9wZXJ0eSB2YWx1ZVxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcGVydHlcbiAgICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gb25cbiAgICogQHByaXZhdGVcbiAgICovXG4gIF90b2dnbGVCeVByb3BlcnR5KHByb3BlcnR5LCB2YWx1ZSwgb24pIHtcbiAgICBpZiAoIShwcm9wZXJ0eSBpbiB0aGlzLl9wcm9wZXJ0eU9uTWFwKSkge1xuICAgICAgdGhpcy5fcHJvcGVydHlPbk1hcFtwcm9wZXJ0eV0gPSB7fTtcbiAgICB9XG5cbiAgICAvLyBkaWQgdGhlIHN0YXRlIGNoYW5nZT9cbiAgICBjb25zdCB0b2dnbGVkID0gdGhpcy5fcHJvcGVydHlPbk1hcFtwcm9wZXJ0eV1bdmFsdWVdICE9PSBvbjtcblxuICAgIHRoaXMuX3Byb3BlcnR5T25NYXBbcHJvcGVydHldW3ZhbHVlXSA9IG9uO1xuXG4gICAgbGV0IHRpbGU7XG4gICAgZm9yIChjb25zdCB0aWxlS2V5IGluIHRoaXMuX3ZlY3RvclRpbGVzKSB7XG4gICAgICBpZiAoIXRoaXMuX3ZlY3RvclRpbGVzLmhhc093blByb3BlcnR5KHRpbGVLZXkpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgdGlsZSA9IHRoaXMuX3ZlY3RvclRpbGVzW3RpbGVLZXldO1xuICAgICAgdGlsZS50b2dnbGVCeVByb3BlcnR5KHByb3BlcnR5LCB2YWx1ZSwgb24sIHRvZ2dsZWQpO1xuICAgIH1cbiAgfSxcblxuICAvKipcbiAgICogQ2hhbmdlIHRoZSBzdHlsZSBvZiBmZWF0dXJlcyBiYXNlZCBvbiBwcm9wZXJ0eSB2YWx1ZXNcbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IHByb3BlcnR5XG4gICAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZVxuICAgKiBAcGFyYW0ge09iamVjdH0gc3R5bGVcbiAgICogQHJldHVybnMge0wuVmVjdG9yVGlsZXN9IHRoaXNcbiAgICovXG4gIHJlc3R5bGVCeVByb3BlcnR5KHByb3BlcnR5LCB2YWx1ZSwgc3R5bGUpIHtcbiAgICBpZiAoIShwcm9wZXJ0eSBpbiB0aGlzLl9wcm9wZXJ0eVN0eWxlcykpIHtcbiAgICAgIHRoaXMuX3Byb3BlcnR5U3R5bGVzW3Byb3BlcnR5XSA9IHt9O1xuICAgIH1cblxuICAgIGlmICghKHZhbHVlIGluIHRoaXMuX3Byb3BlcnR5U3R5bGVzW3Byb3BlcnR5XSkpIHtcbiAgICAgIHRoaXMuX3Byb3BlcnR5U3R5bGVzW3Byb3BlcnR5XVt2YWx1ZV0gPSB7fTtcbiAgICB9XG5cbiAgICBPYmplY3QuYXNzaWduKHRoaXMuX3Byb3BlcnR5U3R5bGVzW3Byb3BlcnR5XVt2YWx1ZV0sIHN0eWxlKTtcblxuICAgIGxldCB0aWxlO1xuICAgIGZvciAoY29uc3QgdGlsZUtleSBpbiB0aGlzLl92ZWN0b3JUaWxlcykge1xuICAgICAgaWYgKCF0aGlzLl92ZWN0b3JUaWxlcy5oYXNPd25Qcm9wZXJ0eSh0aWxlS2V5KSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHRpbGUgPSB0aGlzLl92ZWN0b3JUaWxlc1t0aWxlS2V5XTtcbiAgICAgIHRpbGUucmVzdHlsZUJ5UHJvcGVydHkocHJvcGVydHksIHZhbHVlLCBzdHlsZSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG5cbiAgLyoqXG4gICAqIENoYW5nZSB0aGUgc3R5bGUgb2YgYSBmZWF0dXJlIGJ5IGl0cyBpZFxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWRcbiAgICogQHBhcmFtIHtPYmplY3R9IHN0eWxlXG4gICAqIEByZXR1cm5zIHtMLlZlY3RvclRpbGVzfSB0aGlzXG4gICAqL1xuICBzZXRGZWF0dXJlU3R5bGUoaWQsIHN0eWxlKSB7XG4gICAgdGhpcy5fZmVhdHVyZVN0eWxlc1tpZF0gPSBzdHlsZTtcbiAgICBmb3IgKGNvbnN0IHRpbGVLZXkgaW4gdGhpcy5fdmVjdG9yVGlsZXMpIHtcbiAgICAgIGlmICghdGhpcy5fdmVjdG9yVGlsZXMuaGFzT3duUHJvcGVydHkodGlsZUtleSkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBjb25zdCB0aWxlID0gdGhpcy5fdmVjdG9yVGlsZXNbdGlsZUtleV07XG4gICAgICBpZiAodGlsZS5jb250YWlucyhpZCkpIHtcbiAgICAgICAgY29uc3QgZmVhdHVyZSA9IHRpbGUuZ2V0RmVhdHVyZShpZCk7XG4gICAgICAgIGZlYXR1cmUuc2V0U3R5bGUoc3R5bGUpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICAvKipcbiAgICogUmV0dXJucyBhIHJlZmVyZW5jZSB0byB0aGUgbGF5ZXIgaWRlbnRpZmllZCBieSB0aGUgaWRcbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkXG4gICAqIEByZXR1cm5zIHtMLlBhdGh9XG4gICAqL1xuICBnZXRMYXllcihpZCkge1xuICAgIGxldCB0aWxlO1xuICAgIGZvciAoY29uc3QgdGlsZUtleSBpbiB0aGlzLl92ZWN0b3JUaWxlcykge1xuICAgICAgaWYgKCF0aGlzLl92ZWN0b3JUaWxlcy5oYXNPd25Qcm9wZXJ0eSh0aWxlS2V5KSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHRpbGUgPSB0aGlzLl92ZWN0b3JUaWxlc1t0aWxlS2V5XTtcbiAgICAgIGlmICh0aWxlLmNvbnRhaW5zKGlkKSkge1xuICAgICAgICByZXR1cm4gdGlsZS5nZXRGZWF0dXJlKGlkKS5sYXllcjtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH0sXG5cbiAgLyoqXG4gICAqIFJldHVybnMgYSByZWZlcmVuY2UgdG8gdGhlIEdlb0pTT04gZmVhdHVyZSBpZGVudGlmaWVkIGJ5IHRoZSBpZFxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWRcbiAgICogQHJldHVybiB7T2JqZWN0fVxuICAgKi9cbiAgZ2V0R2VvSlNPTihpZCkge1xuICAgIGxldCB0aWxlO1xuICAgIGZvciAoY29uc3QgdGlsZUtleSBpbiB0aGlzLl92ZWN0b3JUaWxlcykge1xuICAgICAgaWYgKCF0aGlzLl92ZWN0b3JUaWxlcy5oYXNPd25Qcm9wZXJ0eSh0aWxlS2V5KSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHRpbGUgPSB0aGlzLl92ZWN0b3JUaWxlc1t0aWxlS2V5XTtcbiAgICAgIGlmICh0aWxlLmNvbnRhaW5zKGlkKSkge1xuICAgICAgICByZXR1cm4gdGlsZS5nZXRGZWF0dXJlKGlkKS5nZW9qc29uO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfSxcblxuICAvKipcbiAgICogRGVsZXRlcyBhIGZlYXR1cmUgYnkgaXRzIElEXG4gICAqIE5vdGUgdGhhdCB0aGlzIGZlYXR1cmUgd2lsbCBzdGlsbCBiZSBsb2FkZWQgaW4gc3Vic2VxdWVudCB0aWxlc1xuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWRcbiAgICogQHJldHVybnMge0wuVmVjdG9yVGlsZXN9IHRoaXNcbiAgICovXG4gIHJlbW92ZUZlYXR1cmUoaWQpIHtcbiAgICBsZXQgdGlsZTtcbiAgICBmb3IgKGNvbnN0IHRpbGVLZXkgaW4gdGhpcy5fdmVjdG9yVGlsZXMpIHtcbiAgICAgIGlmICghdGhpcy5fdmVjdG9yVGlsZXMuaGFzT3duUHJvcGVydHkodGlsZUtleSkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICB0aWxlID0gdGhpcy5fdmVjdG9yVGlsZXNbdGlsZUtleV07XG4gICAgICB0aWxlLnJlbW92ZUZlYXR1cmUoaWQpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICAvKipcbiAgICogQ29udmVydCBhIEdlb0pTT04gZmVhdHVyZSBpbnRvIGEgTGVhZmxldCBmZWF0dXJlXG4gICAqIFBvaW50IC0+IEwuQ2lyY2xlXG4gICAqIExpbmVTdHJpbmcgLT4gTC5Qb2x5bGluZVxuICAgKiBQb2x5Z29uL011bHRpcG9seWdvbiAtPiBMLlBvbHlnb25cbiAgICogSGVyZSB3ZSBtdXN0IG1ha2UgbG9uLGxhdCAoR2VvSlNPTikgaW50byBsYXQsbG9uIChMZWFmbGV0KVxuICAgKlxuICAgKiBAcGFyYW0ge09iamVjdH0gZmVhdHVyZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWRcbiAgICogQHJldHVybnMge0wuUGF0aH1cbiAgICogQHByaXZhdGVcbiAgICovXG4gIF9nZW9qc29uVG9MYXllcihmZWF0dXJlKSB7XG4gICAgbGV0IGxheWVyO1xuICAgIGxldCBjb29yZHM7XG4gICAgbGV0IHJpbmc7XG4gICAgY29uc3QgYyA9IGZlYXR1cmUuZ2VvbWV0cnkuY29vcmRpbmF0ZXM7XG4gICAgc3dpdGNoIChmZWF0dXJlLmdlb21ldHJ5LnR5cGUpIHtcbiAgICAgIGNhc2UgJ1BvaW50JzpcbiAgICAgICAgbGF5ZXIgPSBMLmNpcmNsZShbY1sxXSwgY1swXV0sIHtcbiAgICAgICAgICByYWRpdXM6IDQwXG4gICAgICAgIH0pO1xuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSAnTGluZVN0cmluZyc6XG4gICAgICAgIGNvb3JkcyA9IFtdO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBjb29yZHMucHVzaChbY1tpXVsxXSwgY1tpXVswXV0pO1xuICAgICAgICB9XG4gICAgICAgIGxheWVyID0gTC5wb2x5bGluZShjb29yZHMsIHt9KTtcbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgJ1BvbHlnb24nOlxuICAgICAgICBjb29yZHMgPSBbXTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgY29vcmRzLnB1c2goW10pO1xuICAgICAgICAgIHJpbmcgPSBjW2ldO1xuICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgcmluZy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgY29vcmRzW2ldLnB1c2goW3Jpbmdbal1bMV0sIHJpbmdbal1bMF1dKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGF5ZXIgPSBMLnBvbHlnb24oY29vcmRzLCB7fSk7XG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBjYXNlICdNdWx0aVBvbHlnb24nOlxuICAgICAgICBjb29yZHMgPSBbXTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgY29vcmRzLnB1c2goW10pO1xuICAgICAgICAgIGNvbnN0IHBvbHlnb24gPSBjW2ldO1xuICAgICAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgcG9seWdvbi5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgY29vcmRzW2ldLnB1c2goW10pO1xuICAgICAgICAgICAgcmluZyA9IHBvbHlnb25bal07XG4gICAgICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IHJpbmcubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgICAgY29vcmRzW2ldW2pdLnB1c2goW3Jpbmdba11bMV0sIHJpbmdba11bMF1dKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGF5ZXIgPSBMLnBvbHlnb24oY29vcmRzLCB7fSk7XG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICBjb25zb2xlLmxvZyhgVW5zdXBwb3J0ZWQgZmVhdHVyZSB0eXBlOiAke2ZlYXR1cmUuZ2VvbWV0cnkudHlwZX1gKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgcmV0dXJuIGxheWVyO1xuICB9XG5cbn0pO1xuXG4iLCIvKipcbiAqIEEgZmVhdHVyZSBvYmplY3RcbiAqXG4gKiBAY2xhc3MgRmVhdHVyZVxuICogQHByaXZhdGVcbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRmVhdHVyZSB7XG4gIGNvbnN0cnVjdG9yKGlkLCBnZW9qc29uLCBsYXllcikge1xuICAgIHRoaXMuaWQgPSBpZDtcbiAgICB0aGlzLmdlb2pzb24gPSBnZW9qc29uO1xuICAgIHRoaXMubGF5ZXIgPSBsYXllcjtcbiAgICB0aGlzLm9uTWFwID0gdHJ1ZTtcbiAgICB0aGlzLnN0eWxlID0ge307XG5cbiAgICAvLyB0aGUgZm9sbG93aW5nIGJlY29tZXMgYSByZWZlcmVuY2UgdG8gdGhpcyBmZWF0dXJlJ3NcbiAgICAvLyBpbmRleCBiYm94IHdoZW4gdGhpcyBmZWF0dXJlIGlzIGluZGV4ZWQgYnkgaXRzIHRpbGVcbiAgICB0aGlzLmluZGV4RW50cnkgPSBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBzdHlsZVxuICAgKiByZXR1cm5zIHtGZWF0dXJlfSB0aGlzXG4gICAqL1xuICBzZXRTdHlsZShzdHlsZSkge1xuICAgIE9iamVjdC5hc3NpZ24odGhpcy5zdHlsZSwgc3R5bGUpO1xuICAgIHRoaXMubGF5ZXIuc2V0U3R5bGUodGhpcy5zdHlsZSk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtib29sZWFufSBvblxuICAgKiBAcmV0dXJucyB7RmVhdHVyZX0gdGhpc1xuICAgKi9cbiAgcHV0T25NYXAob24pIHtcbiAgICB0aGlzLm9uTWFwID0gb247XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cbn1cbiIsIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBQb2ludDtcblxuLyoqXG4gKiBBIHN0YW5kYWxvbmUgcG9pbnQgZ2VvbWV0cnkgd2l0aCB1c2VmdWwgYWNjZXNzb3IsIGNvbXBhcmlzb24sIGFuZFxuICogbW9kaWZpY2F0aW9uIG1ldGhvZHMuXG4gKlxuICogQGNsYXNzIFBvaW50XG4gKiBAcGFyYW0ge051bWJlcn0geCB0aGUgeC1jb29yZGluYXRlLiB0aGlzIGNvdWxkIGJlIGxvbmdpdHVkZSBvciBzY3JlZW5cbiAqIHBpeGVscywgb3IgYW55IG90aGVyIHNvcnQgb2YgdW5pdC5cbiAqIEBwYXJhbSB7TnVtYmVyfSB5IHRoZSB5LWNvb3JkaW5hdGUuIHRoaXMgY291bGQgYmUgbGF0aXR1ZGUgb3Igc2NyZWVuXG4gKiBwaXhlbHMsIG9yIGFueSBvdGhlciBzb3J0IG9mIHVuaXQuXG4gKiBAZXhhbXBsZVxuICogdmFyIHBvaW50ID0gbmV3IFBvaW50KC03NywgMzgpO1xuICovXG5mdW5jdGlvbiBQb2ludCh4LCB5KSB7XG4gICAgdGhpcy54ID0geDtcbiAgICB0aGlzLnkgPSB5O1xufVxuXG5Qb2ludC5wcm90b3R5cGUgPSB7XG5cbiAgICAvKipcbiAgICAgKiBDbG9uZSB0aGlzIHBvaW50LCByZXR1cm5pbmcgYSBuZXcgcG9pbnQgdGhhdCBjYW4gYmUgbW9kaWZpZWRcbiAgICAgKiB3aXRob3V0IGFmZmVjdGluZyB0aGUgb2xkIG9uZS5cbiAgICAgKiBAcmV0dXJuIHtQb2ludH0gdGhlIGNsb25lXG4gICAgICovXG4gICAgY2xvbmU6IGZ1bmN0aW9uKCkgeyByZXR1cm4gbmV3IFBvaW50KHRoaXMueCwgdGhpcy55KTsgfSxcblxuICAgIC8qKlxuICAgICAqIEFkZCB0aGlzIHBvaW50J3MgeCAmIHkgY29vcmRpbmF0ZXMgdG8gYW5vdGhlciBwb2ludCxcbiAgICAgKiB5aWVsZGluZyBhIG5ldyBwb2ludC5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBwIHRoZSBvdGhlciBwb2ludFxuICAgICAqIEByZXR1cm4ge1BvaW50fSBvdXRwdXQgcG9pbnRcbiAgICAgKi9cbiAgICBhZGQ6ICAgICBmdW5jdGlvbihwKSB7IHJldHVybiB0aGlzLmNsb25lKCkuX2FkZChwKTsgfSxcblxuICAgIC8qKlxuICAgICAqIFN1YnRyYWN0IHRoaXMgcG9pbnQncyB4ICYgeSBjb29yZGluYXRlcyB0byBmcm9tIHBvaW50LFxuICAgICAqIHlpZWxkaW5nIGEgbmV3IHBvaW50LlxuICAgICAqIEBwYXJhbSB7UG9pbnR9IHAgdGhlIG90aGVyIHBvaW50XG4gICAgICogQHJldHVybiB7UG9pbnR9IG91dHB1dCBwb2ludFxuICAgICAqL1xuICAgIHN1YjogICAgIGZ1bmN0aW9uKHApIHsgcmV0dXJuIHRoaXMuY2xvbmUoKS5fc3ViKHApOyB9LFxuXG4gICAgLyoqXG4gICAgICogTXVsdGlwbHkgdGhpcyBwb2ludCdzIHggJiB5IGNvb3JkaW5hdGVzIGJ5IHBvaW50LFxuICAgICAqIHlpZWxkaW5nIGEgbmV3IHBvaW50LlxuICAgICAqIEBwYXJhbSB7UG9pbnR9IHAgdGhlIG90aGVyIHBvaW50XG4gICAgICogQHJldHVybiB7UG9pbnR9IG91dHB1dCBwb2ludFxuICAgICAqL1xuICAgIG11bHRCeVBvaW50OiAgICBmdW5jdGlvbihwKSB7IHJldHVybiB0aGlzLmNsb25lKCkuX211bHRCeVBvaW50KHApOyB9LFxuXG4gICAgLyoqXG4gICAgICogRGl2aWRlIHRoaXMgcG9pbnQncyB4ICYgeSBjb29yZGluYXRlcyBieSBwb2ludCxcbiAgICAgKiB5aWVsZGluZyBhIG5ldyBwb2ludC5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBwIHRoZSBvdGhlciBwb2ludFxuICAgICAqIEByZXR1cm4ge1BvaW50fSBvdXRwdXQgcG9pbnRcbiAgICAgKi9cbiAgICBkaXZCeVBvaW50OiAgICAgZnVuY3Rpb24ocCkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl9kaXZCeVBvaW50KHApOyB9LFxuXG4gICAgLyoqXG4gICAgICogTXVsdGlwbHkgdGhpcyBwb2ludCdzIHggJiB5IGNvb3JkaW5hdGVzIGJ5IGEgZmFjdG9yLFxuICAgICAqIHlpZWxkaW5nIGEgbmV3IHBvaW50LlxuICAgICAqIEBwYXJhbSB7UG9pbnR9IGsgZmFjdG9yXG4gICAgICogQHJldHVybiB7UG9pbnR9IG91dHB1dCBwb2ludFxuICAgICAqL1xuICAgIG11bHQ6ICAgIGZ1bmN0aW9uKGspIHsgcmV0dXJuIHRoaXMuY2xvbmUoKS5fbXVsdChrKTsgfSxcblxuICAgIC8qKlxuICAgICAqIERpdmlkZSB0aGlzIHBvaW50J3MgeCAmIHkgY29vcmRpbmF0ZXMgYnkgYSBmYWN0b3IsXG4gICAgICogeWllbGRpbmcgYSBuZXcgcG9pbnQuXG4gICAgICogQHBhcmFtIHtQb2ludH0gayBmYWN0b3JcbiAgICAgKiBAcmV0dXJuIHtQb2ludH0gb3V0cHV0IHBvaW50XG4gICAgICovXG4gICAgZGl2OiAgICAgZnVuY3Rpb24oaykgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl9kaXYoayk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBSb3RhdGUgdGhpcyBwb2ludCBhcm91bmQgdGhlIDAsIDAgb3JpZ2luIGJ5IGFuIGFuZ2xlIGEsXG4gICAgICogZ2l2ZW4gaW4gcmFkaWFuc1xuICAgICAqIEBwYXJhbSB7TnVtYmVyfSBhIGFuZ2xlIHRvIHJvdGF0ZSBhcm91bmQsIGluIHJhZGlhbnNcbiAgICAgKiBAcmV0dXJuIHtQb2ludH0gb3V0cHV0IHBvaW50XG4gICAgICovXG4gICAgcm90YXRlOiAgZnVuY3Rpb24oYSkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl9yb3RhdGUoYSk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBSb3RhdGUgdGhpcyBwb2ludCBhcm91bmQgcCBwb2ludCBieSBhbiBhbmdsZSBhLFxuICAgICAqIGdpdmVuIGluIHJhZGlhbnNcbiAgICAgKiBAcGFyYW0ge051bWJlcn0gYSBhbmdsZSB0byByb3RhdGUgYXJvdW5kLCBpbiByYWRpYW5zXG4gICAgICogQHBhcmFtIHtQb2ludH0gcCBQb2ludCB0byByb3RhdGUgYXJvdW5kXG4gICAgICogQHJldHVybiB7UG9pbnR9IG91dHB1dCBwb2ludFxuICAgICAqL1xuICAgIHJvdGF0ZUFyb3VuZDogIGZ1bmN0aW9uKGEscCkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl9yb3RhdGVBcm91bmQoYSxwKTsgfSxcblxuICAgIC8qKlxuICAgICAqIE11bHRpcGx5IHRoaXMgcG9pbnQgYnkgYSA0eDEgdHJhbnNmb3JtYXRpb24gbWF0cml4XG4gICAgICogQHBhcmFtIHtBcnJheTxOdW1iZXI+fSBtIHRyYW5zZm9ybWF0aW9uIG1hdHJpeFxuICAgICAqIEByZXR1cm4ge1BvaW50fSBvdXRwdXQgcG9pbnRcbiAgICAgKi9cbiAgICBtYXRNdWx0OiBmdW5jdGlvbihtKSB7IHJldHVybiB0aGlzLmNsb25lKCkuX21hdE11bHQobSk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGUgdGhpcyBwb2ludCBidXQgYXMgYSB1bml0IHZlY3RvciBmcm9tIDAsIDAsIG1lYW5pbmdcbiAgICAgKiB0aGF0IHRoZSBkaXN0YW5jZSBmcm9tIHRoZSByZXN1bHRpbmcgcG9pbnQgdG8gdGhlIDAsIDBcbiAgICAgKiBjb29yZGluYXRlIHdpbGwgYmUgZXF1YWwgdG8gMSBhbmQgdGhlIGFuZ2xlIGZyb20gdGhlIHJlc3VsdGluZ1xuICAgICAqIHBvaW50IHRvIHRoZSAwLCAwIGNvb3JkaW5hdGUgd2lsbCBiZSB0aGUgc2FtZSBhcyBiZWZvcmUuXG4gICAgICogQHJldHVybiB7UG9pbnR9IHVuaXQgdmVjdG9yIHBvaW50XG4gICAgICovXG4gICAgdW5pdDogICAgZnVuY3Rpb24oKSB7IHJldHVybiB0aGlzLmNsb25lKCkuX3VuaXQoKTsgfSxcblxuICAgIC8qKlxuICAgICAqIENvbXB1dGUgYSBwZXJwZW5kaWN1bGFyIHBvaW50LCB3aGVyZSB0aGUgbmV3IHkgY29vcmRpbmF0ZVxuICAgICAqIGlzIHRoZSBvbGQgeCBjb29yZGluYXRlIGFuZCB0aGUgbmV3IHggY29vcmRpbmF0ZSBpcyB0aGUgb2xkIHlcbiAgICAgKiBjb29yZGluYXRlIG11bHRpcGxpZWQgYnkgLTFcbiAgICAgKiBAcmV0dXJuIHtQb2ludH0gcGVycGVuZGljdWxhciBwb2ludFxuICAgICAqL1xuICAgIHBlcnA6ICAgIGZ1bmN0aW9uKCkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl9wZXJwKCk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm4gYSB2ZXJzaW9uIG9mIHRoaXMgcG9pbnQgd2l0aCB0aGUgeCAmIHkgY29vcmRpbmF0ZXNcbiAgICAgKiByb3VuZGVkIHRvIGludGVnZXJzLlxuICAgICAqIEByZXR1cm4ge1BvaW50fSByb3VuZGVkIHBvaW50XG4gICAgICovXG4gICAgcm91bmQ6ICAgZnVuY3Rpb24oKSB7IHJldHVybiB0aGlzLmNsb25lKCkuX3JvdW5kKCk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm4gdGhlIG1hZ2l0dWRlIG9mIHRoaXMgcG9pbnQ6IHRoaXMgaXMgdGhlIEV1Y2xpZGVhblxuICAgICAqIGRpc3RhbmNlIGZyb20gdGhlIDAsIDAgY29vcmRpbmF0ZSB0byB0aGlzIHBvaW50J3MgeCBhbmQgeVxuICAgICAqIGNvb3JkaW5hdGVzLlxuICAgICAqIEByZXR1cm4ge051bWJlcn0gbWFnbml0dWRlXG4gICAgICovXG4gICAgbWFnOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIE1hdGguc3FydCh0aGlzLnggKiB0aGlzLnggKyB0aGlzLnkgKiB0aGlzLnkpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBKdWRnZSB3aGV0aGVyIHRoaXMgcG9pbnQgaXMgZXF1YWwgdG8gYW5vdGhlciBwb2ludCwgcmV0dXJuaW5nXG4gICAgICogdHJ1ZSBvciBmYWxzZS5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBvdGhlciB0aGUgb3RoZXIgcG9pbnRcbiAgICAgKiBAcmV0dXJuIHtib29sZWFufSB3aGV0aGVyIHRoZSBwb2ludHMgYXJlIGVxdWFsXG4gICAgICovXG4gICAgZXF1YWxzOiBmdW5jdGlvbihvdGhlcikge1xuICAgICAgICByZXR1cm4gdGhpcy54ID09PSBvdGhlci54ICYmXG4gICAgICAgICAgICAgICB0aGlzLnkgPT09IG90aGVyLnk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgZnJvbSB0aGlzIHBvaW50IHRvIGFub3RoZXIgcG9pbnRcbiAgICAgKiBAcGFyYW0ge1BvaW50fSBwIHRoZSBvdGhlciBwb2ludFxuICAgICAqIEByZXR1cm4ge051bWJlcn0gZGlzdGFuY2VcbiAgICAgKi9cbiAgICBkaXN0OiBmdW5jdGlvbihwKSB7XG4gICAgICAgIHJldHVybiBNYXRoLnNxcnQodGhpcy5kaXN0U3FyKHApKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQ2FsY3VsYXRlIHRoZSBkaXN0YW5jZSBmcm9tIHRoaXMgcG9pbnQgdG8gYW5vdGhlciBwb2ludCxcbiAgICAgKiB3aXRob3V0IHRoZSBzcXVhcmUgcm9vdCBzdGVwLiBVc2VmdWwgaWYgeW91J3JlIGNvbXBhcmluZ1xuICAgICAqIHJlbGF0aXZlIGRpc3RhbmNlcy5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBwIHRoZSBvdGhlciBwb2ludFxuICAgICAqIEByZXR1cm4ge051bWJlcn0gZGlzdGFuY2VcbiAgICAgKi9cbiAgICBkaXN0U3FyOiBmdW5jdGlvbihwKSB7XG4gICAgICAgIHZhciBkeCA9IHAueCAtIHRoaXMueCxcbiAgICAgICAgICAgIGR5ID0gcC55IC0gdGhpcy55O1xuICAgICAgICByZXR1cm4gZHggKiBkeCArIGR5ICogZHk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgYW5nbGUgZnJvbSB0aGUgMCwgMCBjb29yZGluYXRlIHRvIHRoaXMgcG9pbnQsIGluIHJhZGlhbnNcbiAgICAgKiBjb29yZGluYXRlcy5cbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ9IGFuZ2xlXG4gICAgICovXG4gICAgYW5nbGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gTWF0aC5hdGFuMih0aGlzLnksIHRoaXMueCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgYW5nbGUgZnJvbSB0aGlzIHBvaW50IHRvIGFub3RoZXIgcG9pbnQsIGluIHJhZGlhbnNcbiAgICAgKiBAcGFyYW0ge1BvaW50fSBiIHRoZSBvdGhlciBwb2ludFxuICAgICAqIEByZXR1cm4ge051bWJlcn0gYW5nbGVcbiAgICAgKi9cbiAgICBhbmdsZVRvOiBmdW5jdGlvbihiKSB7XG4gICAgICAgIHJldHVybiBNYXRoLmF0YW4yKHRoaXMueSAtIGIueSwgdGhpcy54IC0gYi54KTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogR2V0IHRoZSBhbmdsZSBiZXR3ZWVuIHRoaXMgcG9pbnQgYW5kIGFub3RoZXIgcG9pbnQsIGluIHJhZGlhbnNcbiAgICAgKiBAcGFyYW0ge1BvaW50fSBiIHRoZSBvdGhlciBwb2ludFxuICAgICAqIEByZXR1cm4ge051bWJlcn0gYW5nbGVcbiAgICAgKi9cbiAgICBhbmdsZVdpdGg6IGZ1bmN0aW9uKGIpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYW5nbGVXaXRoU2VwKGIueCwgYi55KTtcbiAgICB9LFxuXG4gICAgLypcbiAgICAgKiBGaW5kIHRoZSBhbmdsZSBvZiB0aGUgdHdvIHZlY3RvcnMsIHNvbHZpbmcgdGhlIGZvcm11bGEgZm9yXG4gICAgICogdGhlIGNyb3NzIHByb2R1Y3QgYSB4IGIgPSB8YXx8YnxzaW4ozrgpIGZvciDOuC5cbiAgICAgKiBAcGFyYW0ge051bWJlcn0geCB0aGUgeC1jb29yZGluYXRlXG4gICAgICogQHBhcmFtIHtOdW1iZXJ9IHkgdGhlIHktY29vcmRpbmF0ZVxuICAgICAqIEByZXR1cm4ge051bWJlcn0gdGhlIGFuZ2xlIGluIHJhZGlhbnNcbiAgICAgKi9cbiAgICBhbmdsZVdpdGhTZXA6IGZ1bmN0aW9uKHgsIHkpIHtcbiAgICAgICAgcmV0dXJuIE1hdGguYXRhbjIoXG4gICAgICAgICAgICB0aGlzLnggKiB5IC0gdGhpcy55ICogeCxcbiAgICAgICAgICAgIHRoaXMueCAqIHggKyB0aGlzLnkgKiB5KTtcbiAgICB9LFxuXG4gICAgX21hdE11bHQ6IGZ1bmN0aW9uKG0pIHtcbiAgICAgICAgdmFyIHggPSBtWzBdICogdGhpcy54ICsgbVsxXSAqIHRoaXMueSxcbiAgICAgICAgICAgIHkgPSBtWzJdICogdGhpcy54ICsgbVszXSAqIHRoaXMueTtcbiAgICAgICAgdGhpcy54ID0geDtcbiAgICAgICAgdGhpcy55ID0geTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9hZGQ6IGZ1bmN0aW9uKHApIHtcbiAgICAgICAgdGhpcy54ICs9IHAueDtcbiAgICAgICAgdGhpcy55ICs9IHAueTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9zdWI6IGZ1bmN0aW9uKHApIHtcbiAgICAgICAgdGhpcy54IC09IHAueDtcbiAgICAgICAgdGhpcy55IC09IHAueTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9tdWx0OiBmdW5jdGlvbihrKSB7XG4gICAgICAgIHRoaXMueCAqPSBrO1xuICAgICAgICB0aGlzLnkgKj0gaztcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9kaXY6IGZ1bmN0aW9uKGspIHtcbiAgICAgICAgdGhpcy54IC89IGs7XG4gICAgICAgIHRoaXMueSAvPSBrO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgX211bHRCeVBvaW50OiBmdW5jdGlvbihwKSB7XG4gICAgICAgIHRoaXMueCAqPSBwLng7XG4gICAgICAgIHRoaXMueSAqPSBwLnk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBfZGl2QnlQb2ludDogZnVuY3Rpb24ocCkge1xuICAgICAgICB0aGlzLnggLz0gcC54O1xuICAgICAgICB0aGlzLnkgLz0gcC55O1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgX3VuaXQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICB0aGlzLl9kaXYodGhpcy5tYWcoKSk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBfcGVycDogZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB5ID0gdGhpcy55O1xuICAgICAgICB0aGlzLnkgPSB0aGlzLng7XG4gICAgICAgIHRoaXMueCA9IC15O1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgX3JvdGF0ZTogZnVuY3Rpb24oYW5nbGUpIHtcbiAgICAgICAgdmFyIGNvcyA9IE1hdGguY29zKGFuZ2xlKSxcbiAgICAgICAgICAgIHNpbiA9IE1hdGguc2luKGFuZ2xlKSxcbiAgICAgICAgICAgIHggPSBjb3MgKiB0aGlzLnggLSBzaW4gKiB0aGlzLnksXG4gICAgICAgICAgICB5ID0gc2luICogdGhpcy54ICsgY29zICogdGhpcy55O1xuICAgICAgICB0aGlzLnggPSB4O1xuICAgICAgICB0aGlzLnkgPSB5O1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgX3JvdGF0ZUFyb3VuZDogZnVuY3Rpb24oYW5nbGUsIHApIHtcbiAgICAgICAgdmFyIGNvcyA9IE1hdGguY29zKGFuZ2xlKSxcbiAgICAgICAgICAgIHNpbiA9IE1hdGguc2luKGFuZ2xlKSxcbiAgICAgICAgICAgIHggPSBwLnggKyBjb3MgKiAodGhpcy54IC0gcC54KSAtIHNpbiAqICh0aGlzLnkgLSBwLnkpLFxuICAgICAgICAgICAgeSA9IHAueSArIHNpbiAqICh0aGlzLnggLSBwLngpICsgY29zICogKHRoaXMueSAtIHAueSk7XG4gICAgICAgIHRoaXMueCA9IHg7XG4gICAgICAgIHRoaXMueSA9IHk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBfcm91bmQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICB0aGlzLnggPSBNYXRoLnJvdW5kKHRoaXMueCk7XG4gICAgICAgIHRoaXMueSA9IE1hdGgucm91bmQodGhpcy55KTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxufTtcblxuLyoqXG4gKiBDb25zdHJ1Y3QgYSBwb2ludCBmcm9tIGFuIGFycmF5IGlmIG5lY2Vzc2FyeSwgb3RoZXJ3aXNlIGlmIHRoZSBpbnB1dFxuICogaXMgYWxyZWFkeSBhIFBvaW50LCBvciBhbiB1bmtub3duIHR5cGUsIHJldHVybiBpdCB1bmNoYW5nZWRcbiAqIEBwYXJhbSB7QXJyYXk8TnVtYmVyPnxQb2ludHwqfSBhIGFueSBraW5kIG9mIGlucHV0IHZhbHVlXG4gKiBAcmV0dXJuIHtQb2ludH0gY29uc3RydWN0ZWQgcG9pbnQsIG9yIHBhc3NlZC10aHJvdWdoIHZhbHVlLlxuICogQGV4YW1wbGVcbiAqIC8vIHRoaXNcbiAqIHZhciBwb2ludCA9IFBvaW50LmNvbnZlcnQoWzAsIDFdKTtcbiAqIC8vIGlzIGVxdWl2YWxlbnQgdG9cbiAqIHZhciBwb2ludCA9IG5ldyBQb2ludCgwLCAxKTtcbiAqL1xuUG9pbnQuY29udmVydCA9IGZ1bmN0aW9uIChhKSB7XG4gICAgaWYgKGEgaW5zdGFuY2VvZiBQb2ludCkge1xuICAgICAgICByZXR1cm4gYTtcbiAgICB9XG4gICAgaWYgKEFycmF5LmlzQXJyYXkoYSkpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQb2ludChhWzBdLCBhWzFdKTtcbiAgICB9XG4gICAgcmV0dXJuIGE7XG59O1xuIiwibW9kdWxlLmV4cG9ydHMuVmVjdG9yVGlsZSA9IHJlcXVpcmUoJy4vbGliL3ZlY3RvcnRpbGUuanMnKTtcbm1vZHVsZS5leHBvcnRzLlZlY3RvclRpbGVGZWF0dXJlID0gcmVxdWlyZSgnLi9saWIvdmVjdG9ydGlsZWZlYXR1cmUuanMnKTtcbm1vZHVsZS5leHBvcnRzLlZlY3RvclRpbGVMYXllciA9IHJlcXVpcmUoJy4vbGliL3ZlY3RvcnRpbGVsYXllci5qcycpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgVmVjdG9yVGlsZUxheWVyID0gcmVxdWlyZSgnLi92ZWN0b3J0aWxlbGF5ZXInKTtcblxubW9kdWxlLmV4cG9ydHMgPSBWZWN0b3JUaWxlO1xuXG5mdW5jdGlvbiBWZWN0b3JUaWxlKHBiZiwgZW5kKSB7XG4gICAgdGhpcy5sYXllcnMgPSBwYmYucmVhZEZpZWxkcyhyZWFkVGlsZSwge30sIGVuZCk7XG59XG5cbmZ1bmN0aW9uIHJlYWRUaWxlKHRhZywgbGF5ZXJzLCBwYmYpIHtcbiAgICBpZiAodGFnID09PSAzKSB7XG4gICAgICAgIHZhciBsYXllciA9IG5ldyBWZWN0b3JUaWxlTGF5ZXIocGJmLCBwYmYucmVhZFZhcmludCgpICsgcGJmLnBvcyk7XG4gICAgICAgIGlmIChsYXllci5sZW5ndGgpIGxheWVyc1tsYXllci5uYW1lXSA9IGxheWVyO1xuICAgIH1cbn1cblxuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgUG9pbnQgPSByZXF1aXJlKCdAbWFwYm94L3BvaW50LWdlb21ldHJ5Jyk7XG5cbm1vZHVsZS5leHBvcnRzID0gVmVjdG9yVGlsZUZlYXR1cmU7XG5cbmZ1bmN0aW9uIFZlY3RvclRpbGVGZWF0dXJlKHBiZiwgZW5kLCBleHRlbnQsIGtleXMsIHZhbHVlcykge1xuICAgIC8vIFB1YmxpY1xuICAgIHRoaXMucHJvcGVydGllcyA9IHt9O1xuICAgIHRoaXMuZXh0ZW50ID0gZXh0ZW50O1xuICAgIHRoaXMudHlwZSA9IDA7XG5cbiAgICAvLyBQcml2YXRlXG4gICAgdGhpcy5fcGJmID0gcGJmO1xuICAgIHRoaXMuX2dlb21ldHJ5ID0gLTE7XG4gICAgdGhpcy5fa2V5cyA9IGtleXM7XG4gICAgdGhpcy5fdmFsdWVzID0gdmFsdWVzO1xuXG4gICAgcGJmLnJlYWRGaWVsZHMocmVhZEZlYXR1cmUsIHRoaXMsIGVuZCk7XG59XG5cbmZ1bmN0aW9uIHJlYWRGZWF0dXJlKHRhZywgZmVhdHVyZSwgcGJmKSB7XG4gICAgaWYgKHRhZyA9PSAxKSBmZWF0dXJlLmlkID0gcGJmLnJlYWRWYXJpbnQoKTtcbiAgICBlbHNlIGlmICh0YWcgPT0gMikgcmVhZFRhZyhwYmYsIGZlYXR1cmUpO1xuICAgIGVsc2UgaWYgKHRhZyA9PSAzKSBmZWF0dXJlLnR5cGUgPSBwYmYucmVhZFZhcmludCgpO1xuICAgIGVsc2UgaWYgKHRhZyA9PSA0KSBmZWF0dXJlLl9nZW9tZXRyeSA9IHBiZi5wb3M7XG59XG5cbmZ1bmN0aW9uIHJlYWRUYWcocGJmLCBmZWF0dXJlKSB7XG4gICAgdmFyIGVuZCA9IHBiZi5yZWFkVmFyaW50KCkgKyBwYmYucG9zO1xuXG4gICAgd2hpbGUgKHBiZi5wb3MgPCBlbmQpIHtcbiAgICAgICAgdmFyIGtleSA9IGZlYXR1cmUuX2tleXNbcGJmLnJlYWRWYXJpbnQoKV0sXG4gICAgICAgICAgICB2YWx1ZSA9IGZlYXR1cmUuX3ZhbHVlc1twYmYucmVhZFZhcmludCgpXTtcbiAgICAgICAgZmVhdHVyZS5wcm9wZXJ0aWVzW2tleV0gPSB2YWx1ZTtcbiAgICB9XG59XG5cblZlY3RvclRpbGVGZWF0dXJlLnR5cGVzID0gWydVbmtub3duJywgJ1BvaW50JywgJ0xpbmVTdHJpbmcnLCAnUG9seWdvbiddO1xuXG5WZWN0b3JUaWxlRmVhdHVyZS5wcm90b3R5cGUubG9hZEdlb21ldHJ5ID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIHBiZiA9IHRoaXMuX3BiZjtcbiAgICBwYmYucG9zID0gdGhpcy5fZ2VvbWV0cnk7XG5cbiAgICB2YXIgZW5kID0gcGJmLnJlYWRWYXJpbnQoKSArIHBiZi5wb3MsXG4gICAgICAgIGNtZCA9IDEsXG4gICAgICAgIGxlbmd0aCA9IDAsXG4gICAgICAgIHggPSAwLFxuICAgICAgICB5ID0gMCxcbiAgICAgICAgbGluZXMgPSBbXSxcbiAgICAgICAgbGluZTtcblxuICAgIHdoaWxlIChwYmYucG9zIDwgZW5kKSB7XG4gICAgICAgIGlmICghbGVuZ3RoKSB7XG4gICAgICAgICAgICB2YXIgY21kTGVuID0gcGJmLnJlYWRWYXJpbnQoKTtcbiAgICAgICAgICAgIGNtZCA9IGNtZExlbiAmIDB4NztcbiAgICAgICAgICAgIGxlbmd0aCA9IGNtZExlbiA+PiAzO1xuICAgICAgICB9XG5cbiAgICAgICAgbGVuZ3RoLS07XG5cbiAgICAgICAgaWYgKGNtZCA9PT0gMSB8fCBjbWQgPT09IDIpIHtcbiAgICAgICAgICAgIHggKz0gcGJmLnJlYWRTVmFyaW50KCk7XG4gICAgICAgICAgICB5ICs9IHBiZi5yZWFkU1ZhcmludCgpO1xuXG4gICAgICAgICAgICBpZiAoY21kID09PSAxKSB7IC8vIG1vdmVUb1xuICAgICAgICAgICAgICAgIGlmIChsaW5lKSBsaW5lcy5wdXNoKGxpbmUpO1xuICAgICAgICAgICAgICAgIGxpbmUgPSBbXTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbGluZS5wdXNoKG5ldyBQb2ludCh4LCB5KSk7XG5cbiAgICAgICAgfSBlbHNlIGlmIChjbWQgPT09IDcpIHtcblxuICAgICAgICAgICAgLy8gV29ya2Fyb3VuZCBmb3IgaHR0cHM6Ly9naXRodWIuY29tL21hcGJveC9tYXBuaWstdmVjdG9yLXRpbGUvaXNzdWVzLzkwXG4gICAgICAgICAgICBpZiAobGluZSkge1xuICAgICAgICAgICAgICAgIGxpbmUucHVzaChsaW5lWzBdLmNsb25lKCkpOyAvLyBjbG9zZVBvbHlnb25cbiAgICAgICAgICAgIH1cblxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCd1bmtub3duIGNvbW1hbmQgJyArIGNtZCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAobGluZSkgbGluZXMucHVzaChsaW5lKTtcblxuICAgIHJldHVybiBsaW5lcztcbn07XG5cblZlY3RvclRpbGVGZWF0dXJlLnByb3RvdHlwZS5iYm94ID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIHBiZiA9IHRoaXMuX3BiZjtcbiAgICBwYmYucG9zID0gdGhpcy5fZ2VvbWV0cnk7XG5cbiAgICB2YXIgZW5kID0gcGJmLnJlYWRWYXJpbnQoKSArIHBiZi5wb3MsXG4gICAgICAgIGNtZCA9IDEsXG4gICAgICAgIGxlbmd0aCA9IDAsXG4gICAgICAgIHggPSAwLFxuICAgICAgICB5ID0gMCxcbiAgICAgICAgeDEgPSBJbmZpbml0eSxcbiAgICAgICAgeDIgPSAtSW5maW5pdHksXG4gICAgICAgIHkxID0gSW5maW5pdHksXG4gICAgICAgIHkyID0gLUluZmluaXR5O1xuXG4gICAgd2hpbGUgKHBiZi5wb3MgPCBlbmQpIHtcbiAgICAgICAgaWYgKCFsZW5ndGgpIHtcbiAgICAgICAgICAgIHZhciBjbWRMZW4gPSBwYmYucmVhZFZhcmludCgpO1xuICAgICAgICAgICAgY21kID0gY21kTGVuICYgMHg3O1xuICAgICAgICAgICAgbGVuZ3RoID0gY21kTGVuID4+IDM7XG4gICAgICAgIH1cblxuICAgICAgICBsZW5ndGgtLTtcblxuICAgICAgICBpZiAoY21kID09PSAxIHx8IGNtZCA9PT0gMikge1xuICAgICAgICAgICAgeCArPSBwYmYucmVhZFNWYXJpbnQoKTtcbiAgICAgICAgICAgIHkgKz0gcGJmLnJlYWRTVmFyaW50KCk7XG4gICAgICAgICAgICBpZiAoeCA8IHgxKSB4MSA9IHg7XG4gICAgICAgICAgICBpZiAoeCA+IHgyKSB4MiA9IHg7XG4gICAgICAgICAgICBpZiAoeSA8IHkxKSB5MSA9IHk7XG4gICAgICAgICAgICBpZiAoeSA+IHkyKSB5MiA9IHk7XG5cbiAgICAgICAgfSBlbHNlIGlmIChjbWQgIT09IDcpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcigndW5rbm93biBjb21tYW5kICcgKyBjbWQpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIFt4MSwgeTEsIHgyLCB5Ml07XG59O1xuXG5WZWN0b3JUaWxlRmVhdHVyZS5wcm90b3R5cGUudG9HZW9KU09OID0gZnVuY3Rpb24oeCwgeSwgeikge1xuICAgIHZhciBzaXplID0gdGhpcy5leHRlbnQgKiBNYXRoLnBvdygyLCB6KSxcbiAgICAgICAgeDAgPSB0aGlzLmV4dGVudCAqIHgsXG4gICAgICAgIHkwID0gdGhpcy5leHRlbnQgKiB5LFxuICAgICAgICBjb29yZHMgPSB0aGlzLmxvYWRHZW9tZXRyeSgpLFxuICAgICAgICB0eXBlID0gVmVjdG9yVGlsZUZlYXR1cmUudHlwZXNbdGhpcy50eXBlXSxcbiAgICAgICAgaSwgajtcblxuICAgIGZ1bmN0aW9uIHByb2plY3QobGluZSkge1xuICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxpbmUubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgIHZhciBwID0gbGluZVtqXSwgeTIgPSAxODAgLSAocC55ICsgeTApICogMzYwIC8gc2l6ZTtcbiAgICAgICAgICAgIGxpbmVbal0gPSBbXG4gICAgICAgICAgICAgICAgKHAueCArIHgwKSAqIDM2MCAvIHNpemUgLSAxODAsXG4gICAgICAgICAgICAgICAgMzYwIC8gTWF0aC5QSSAqIE1hdGguYXRhbihNYXRoLmV4cCh5MiAqIE1hdGguUEkgLyAxODApKSAtIDkwXG4gICAgICAgICAgICBdO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgc3dpdGNoICh0aGlzLnR5cGUpIHtcbiAgICBjYXNlIDE6XG4gICAgICAgIHZhciBwb2ludHMgPSBbXTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGNvb3Jkcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgcG9pbnRzW2ldID0gY29vcmRzW2ldWzBdO1xuICAgICAgICB9XG4gICAgICAgIGNvb3JkcyA9IHBvaW50cztcbiAgICAgICAgcHJvamVjdChjb29yZHMpO1xuICAgICAgICBicmVhaztcblxuICAgIGNhc2UgMjpcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGNvb3Jkcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgcHJvamVjdChjb29yZHNbaV0pO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuXG4gICAgY2FzZSAzOlxuICAgICAgICBjb29yZHMgPSBjbGFzc2lmeVJpbmdzKGNvb3Jkcyk7XG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBjb29yZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCBjb29yZHNbaV0ubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICBwcm9qZWN0KGNvb3Jkc1tpXVtqXSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgaWYgKGNvb3Jkcy5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgY29vcmRzID0gY29vcmRzWzBdO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHR5cGUgPSAnTXVsdGknICsgdHlwZTtcbiAgICB9XG5cbiAgICB2YXIgcmVzdWx0ID0ge1xuICAgICAgICB0eXBlOiBcIkZlYXR1cmVcIixcbiAgICAgICAgZ2VvbWV0cnk6IHtcbiAgICAgICAgICAgIHR5cGU6IHR5cGUsXG4gICAgICAgICAgICBjb29yZGluYXRlczogY29vcmRzXG4gICAgICAgIH0sXG4gICAgICAgIHByb3BlcnRpZXM6IHRoaXMucHJvcGVydGllc1xuICAgIH07XG5cbiAgICBpZiAoJ2lkJyBpbiB0aGlzKSB7XG4gICAgICAgIHJlc3VsdC5pZCA9IHRoaXMuaWQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbi8vIGNsYXNzaWZpZXMgYW4gYXJyYXkgb2YgcmluZ3MgaW50byBwb2x5Z29ucyB3aXRoIG91dGVyIHJpbmdzIGFuZCBob2xlc1xuXG5mdW5jdGlvbiBjbGFzc2lmeVJpbmdzKHJpbmdzKSB7XG4gICAgdmFyIGxlbiA9IHJpbmdzLmxlbmd0aDtcblxuICAgIGlmIChsZW4gPD0gMSkgcmV0dXJuIFtyaW5nc107XG5cbiAgICB2YXIgcG9seWdvbnMgPSBbXSxcbiAgICAgICAgcG9seWdvbixcbiAgICAgICAgY2N3O1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW47IGkrKykge1xuICAgICAgICB2YXIgYXJlYSA9IHNpZ25lZEFyZWEocmluZ3NbaV0pO1xuICAgICAgICBpZiAoYXJlYSA9PT0gMCkgY29udGludWU7XG5cbiAgICAgICAgaWYgKGNjdyA9PT0gdW5kZWZpbmVkKSBjY3cgPSBhcmVhIDwgMDtcblxuICAgICAgICBpZiAoY2N3ID09PSBhcmVhIDwgMCkge1xuICAgICAgICAgICAgaWYgKHBvbHlnb24pIHBvbHlnb25zLnB1c2gocG9seWdvbik7XG4gICAgICAgICAgICBwb2x5Z29uID0gW3JpbmdzW2ldXTtcblxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcG9seWdvbi5wdXNoKHJpbmdzW2ldKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBpZiAocG9seWdvbikgcG9seWdvbnMucHVzaChwb2x5Z29uKTtcblxuICAgIHJldHVybiBwb2x5Z29ucztcbn1cblxuZnVuY3Rpb24gc2lnbmVkQXJlYShyaW5nKSB7XG4gICAgdmFyIHN1bSA9IDA7XG4gICAgZm9yICh2YXIgaSA9IDAsIGxlbiA9IHJpbmcubGVuZ3RoLCBqID0gbGVuIC0gMSwgcDEsIHAyOyBpIDwgbGVuOyBqID0gaSsrKSB7XG4gICAgICAgIHAxID0gcmluZ1tpXTtcbiAgICAgICAgcDIgPSByaW5nW2pdO1xuICAgICAgICBzdW0gKz0gKHAyLnggLSBwMS54KSAqIChwMS55ICsgcDIueSk7XG4gICAgfVxuICAgIHJldHVybiBzdW07XG59XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBWZWN0b3JUaWxlRmVhdHVyZSA9IHJlcXVpcmUoJy4vdmVjdG9ydGlsZWZlYXR1cmUuanMnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBWZWN0b3JUaWxlTGF5ZXI7XG5cbmZ1bmN0aW9uIFZlY3RvclRpbGVMYXllcihwYmYsIGVuZCkge1xuICAgIC8vIFB1YmxpY1xuICAgIHRoaXMudmVyc2lvbiA9IDE7XG4gICAgdGhpcy5uYW1lID0gbnVsbDtcbiAgICB0aGlzLmV4dGVudCA9IDQwOTY7XG4gICAgdGhpcy5sZW5ndGggPSAwO1xuXG4gICAgLy8gUHJpdmF0ZVxuICAgIHRoaXMuX3BiZiA9IHBiZjtcbiAgICB0aGlzLl9rZXlzID0gW107XG4gICAgdGhpcy5fdmFsdWVzID0gW107XG4gICAgdGhpcy5fZmVhdHVyZXMgPSBbXTtcblxuICAgIHBiZi5yZWFkRmllbGRzKHJlYWRMYXllciwgdGhpcywgZW5kKTtcblxuICAgIHRoaXMubGVuZ3RoID0gdGhpcy5fZmVhdHVyZXMubGVuZ3RoO1xufVxuXG5mdW5jdGlvbiByZWFkTGF5ZXIodGFnLCBsYXllciwgcGJmKSB7XG4gICAgaWYgKHRhZyA9PT0gMTUpIGxheWVyLnZlcnNpb24gPSBwYmYucmVhZFZhcmludCgpO1xuICAgIGVsc2UgaWYgKHRhZyA9PT0gMSkgbGF5ZXIubmFtZSA9IHBiZi5yZWFkU3RyaW5nKCk7XG4gICAgZWxzZSBpZiAodGFnID09PSA1KSBsYXllci5leHRlbnQgPSBwYmYucmVhZFZhcmludCgpO1xuICAgIGVsc2UgaWYgKHRhZyA9PT0gMikgbGF5ZXIuX2ZlYXR1cmVzLnB1c2gocGJmLnBvcyk7XG4gICAgZWxzZSBpZiAodGFnID09PSAzKSBsYXllci5fa2V5cy5wdXNoKHBiZi5yZWFkU3RyaW5nKCkpO1xuICAgIGVsc2UgaWYgKHRhZyA9PT0gNCkgbGF5ZXIuX3ZhbHVlcy5wdXNoKHJlYWRWYWx1ZU1lc3NhZ2UocGJmKSk7XG59XG5cbmZ1bmN0aW9uIHJlYWRWYWx1ZU1lc3NhZ2UocGJmKSB7XG4gICAgdmFyIHZhbHVlID0gbnVsbCxcbiAgICAgICAgZW5kID0gcGJmLnJlYWRWYXJpbnQoKSArIHBiZi5wb3M7XG5cbiAgICB3aGlsZSAocGJmLnBvcyA8IGVuZCkge1xuICAgICAgICB2YXIgdGFnID0gcGJmLnJlYWRWYXJpbnQoKSA+PiAzO1xuXG4gICAgICAgIHZhbHVlID0gdGFnID09PSAxID8gcGJmLnJlYWRTdHJpbmcoKSA6XG4gICAgICAgICAgICB0YWcgPT09IDIgPyBwYmYucmVhZEZsb2F0KCkgOlxuICAgICAgICAgICAgdGFnID09PSAzID8gcGJmLnJlYWREb3VibGUoKSA6XG4gICAgICAgICAgICB0YWcgPT09IDQgPyBwYmYucmVhZFZhcmludDY0KCkgOlxuICAgICAgICAgICAgdGFnID09PSA1ID8gcGJmLnJlYWRWYXJpbnQoKSA6XG4gICAgICAgICAgICB0YWcgPT09IDYgPyBwYmYucmVhZFNWYXJpbnQoKSA6XG4gICAgICAgICAgICB0YWcgPT09IDcgPyBwYmYucmVhZEJvb2xlYW4oKSA6IG51bGw7XG4gICAgfVxuXG4gICAgcmV0dXJuIHZhbHVlO1xufVxuXG4vLyByZXR1cm4gZmVhdHVyZSBgaWAgZnJvbSB0aGlzIGxheWVyIGFzIGEgYFZlY3RvclRpbGVGZWF0dXJlYFxuVmVjdG9yVGlsZUxheWVyLnByb3RvdHlwZS5mZWF0dXJlID0gZnVuY3Rpb24oaSkge1xuICAgIGlmIChpIDwgMCB8fCBpID49IHRoaXMuX2ZlYXR1cmVzLmxlbmd0aCkgdGhyb3cgbmV3IEVycm9yKCdmZWF0dXJlIGluZGV4IG91dCBvZiBib3VuZHMnKTtcblxuICAgIHRoaXMuX3BiZi5wb3MgPSB0aGlzLl9mZWF0dXJlc1tpXTtcblxuICAgIHZhciBlbmQgPSB0aGlzLl9wYmYucmVhZFZhcmludCgpICsgdGhpcy5fcGJmLnBvcztcbiAgICByZXR1cm4gbmV3IFZlY3RvclRpbGVGZWF0dXJlKHRoaXMuX3BiZiwgZW5kLCB0aGlzLmV4dGVudCwgdGhpcy5fa2V5cywgdGhpcy5fdmFsdWVzKTtcbn07XG4iLCJ2YXIgY29vcmRFYWNoID0gcmVxdWlyZSgnQHR1cmYvbWV0YScpLmNvb3JkRWFjaDtcblxuLyoqXG4gKiBUYWtlcyBhIHNldCBvZiBmZWF0dXJlcywgY2FsY3VsYXRlcyB0aGUgYmJveCBvZiBhbGwgaW5wdXQgZmVhdHVyZXMsIGFuZCByZXR1cm5zIGEgYm91bmRpbmcgYm94LlxuICpcbiAqIEBuYW1lIGJib3hcbiAqIEBwYXJhbSB7RmVhdHVyZUNvbGxlY3Rpb258RmVhdHVyZTxhbnk+fSBnZW9qc29uIGlucHV0IGZlYXR1cmVzXG4gKiBAcmV0dXJucyB7QXJyYXk8bnVtYmVyPn0gYmJveCBleHRlbnQgaW4gW21pblgsIG1pblksIG1heFgsIG1heFldIG9yZGVyXG4gKiBAZXhhbXBsZVxuICogdmFyIGxpbmUgPSB0dXJmLmxpbmVTdHJpbmcoW1stNzQsIDQwXSwgWy03OCwgNDJdLCBbLTgyLCAzNV1dKTtcbiAqIHZhciBiYm94ID0gdHVyZi5iYm94KGxpbmUpO1xuICogdmFyIGJib3hQb2x5Z29uID0gdHVyZi5iYm94UG9seWdvbihiYm94KTtcbiAqXG4gKiAvL2FkZFRvTWFwXG4gKiB2YXIgYWRkVG9NYXAgPSBbbGluZSwgYmJveFBvbHlnb25dXG4gKi9cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGdlb2pzb24pIHtcbiAgICB2YXIgYmJveCA9IFtJbmZpbml0eSwgSW5maW5pdHksIC1JbmZpbml0eSwgLUluZmluaXR5XTtcbiAgICBjb29yZEVhY2goZ2VvanNvbiwgZnVuY3Rpb24gKGNvb3JkKSB7XG4gICAgICAgIGlmIChiYm94WzBdID4gY29vcmRbMF0pIGJib3hbMF0gPSBjb29yZFswXTtcbiAgICAgICAgaWYgKGJib3hbMV0gPiBjb29yZFsxXSkgYmJveFsxXSA9IGNvb3JkWzFdO1xuICAgICAgICBpZiAoYmJveFsyXSA8IGNvb3JkWzBdKSBiYm94WzJdID0gY29vcmRbMF07XG4gICAgICAgIGlmIChiYm94WzNdIDwgY29vcmRbMV0pIGJib3hbM10gPSBjb29yZFsxXTtcbiAgICB9KTtcbiAgICByZXR1cm4gYmJveDtcbn07XG4iLCIvKipcbiAqIENhbGxiYWNrIGZvciBjb29yZEVhY2hcbiAqXG4gKiBAY2FsbGJhY2sgY29vcmRFYWNoQ2FsbGJhY2tcbiAqIEBwYXJhbSB7QXJyYXk8bnVtYmVyPn0gY3VycmVudENvb3JkIFRoZSBjdXJyZW50IGNvb3JkaW5hdGUgYmVpbmcgcHJvY2Vzc2VkLlxuICogQHBhcmFtIHtudW1iZXJ9IGNvb3JkSW5kZXggVGhlIGN1cnJlbnQgaW5kZXggb2YgdGhlIGNvb3JkaW5hdGUgYmVpbmcgcHJvY2Vzc2VkLlxuICogU3RhcnRzIGF0IGluZGV4IDAuXG4gKiBAcGFyYW0ge251bWJlcn0gZmVhdHVyZUluZGV4IFRoZSBjdXJyZW50IGluZGV4IG9mIHRoZSBmZWF0dXJlIGJlaW5nIHByb2Nlc3NlZC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBmZWF0dXJlU3ViSW5kZXggVGhlIGN1cnJlbnQgc3ViSW5kZXggb2YgdGhlIGZlYXR1cmUgYmVpbmcgcHJvY2Vzc2VkLlxuICovXG5cbi8qKlxuICogSXRlcmF0ZSBvdmVyIGNvb3JkaW5hdGVzIGluIGFueSBHZW9KU09OIG9iamVjdCwgc2ltaWxhciB0byBBcnJheS5mb3JFYWNoKClcbiAqXG4gKiBAbmFtZSBjb29yZEVhY2hcbiAqIEBwYXJhbSB7RmVhdHVyZUNvbGxlY3Rpb258R2VvbWV0cnl8RmVhdHVyZX0gZ2VvanNvbiBhbnkgR2VvSlNPTiBvYmplY3RcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrIGEgbWV0aG9kIHRoYXQgdGFrZXMgKGN1cnJlbnRDb29yZCwgY29vcmRJbmRleCwgZmVhdHVyZUluZGV4LCBmZWF0dXJlU3ViSW5kZXgpXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtleGNsdWRlV3JhcENvb3JkPWZhbHNlXSB3aGV0aGVyIG9yIG5vdCB0byBpbmNsdWRlIHRoZSBmaW5hbCBjb29yZGluYXRlIG9mIExpbmVhclJpbmdzIHRoYXQgd3JhcHMgdGhlIHJpbmcgaW4gaXRzIGl0ZXJhdGlvbi5cbiAqIEBleGFtcGxlXG4gKiB2YXIgZmVhdHVyZXMgPSB0dXJmLmZlYXR1cmVDb2xsZWN0aW9uKFtcbiAqICAgdHVyZi5wb2ludChbMjYsIDM3XSwge1wiZm9vXCI6IFwiYmFyXCJ9KSxcbiAqICAgdHVyZi5wb2ludChbMzYsIDUzXSwge1wiaGVsbG9cIjogXCJ3b3JsZFwifSlcbiAqIF0pO1xuICpcbiAqIHR1cmYuY29vcmRFYWNoKGZlYXR1cmVzLCBmdW5jdGlvbiAoY3VycmVudENvb3JkLCBjb29yZEluZGV4LCBmZWF0dXJlSW5kZXgsIGZlYXR1cmVTdWJJbmRleCkge1xuICogICAvLz1jdXJyZW50Q29vcmRcbiAqICAgLy89Y29vcmRJbmRleFxuICogICAvLz1mZWF0dXJlSW5kZXhcbiAqICAgLy89ZmVhdHVyZVN1YkluZGV4XG4gKiB9KTtcbiAqL1xuZnVuY3Rpb24gY29vcmRFYWNoKGdlb2pzb24sIGNhbGxiYWNrLCBleGNsdWRlV3JhcENvb3JkKSB7XG4gICAgLy8gSGFuZGxlcyBudWxsIEdlb21ldHJ5IC0tIFNraXBzIHRoaXMgR2VvSlNPTlxuICAgIGlmIChnZW9qc29uID09PSBudWxsKSByZXR1cm47XG4gICAgdmFyIGZlYXR1cmVJbmRleCwgZ2VvbWV0cnlJbmRleCwgaiwgaywgbCwgZ2VvbWV0cnksIHN0b3BHLCBjb29yZHMsXG4gICAgICAgIGdlb21ldHJ5TWF5YmVDb2xsZWN0aW9uLFxuICAgICAgICB3cmFwU2hyaW5rID0gMCxcbiAgICAgICAgY29vcmRJbmRleCA9IDAsXG4gICAgICAgIGlzR2VvbWV0cnlDb2xsZWN0aW9uLFxuICAgICAgICB0eXBlID0gZ2VvanNvbi50eXBlLFxuICAgICAgICBpc0ZlYXR1cmVDb2xsZWN0aW9uID0gdHlwZSA9PT0gJ0ZlYXR1cmVDb2xsZWN0aW9uJyxcbiAgICAgICAgaXNGZWF0dXJlID0gdHlwZSA9PT0gJ0ZlYXR1cmUnLFxuICAgICAgICBzdG9wID0gaXNGZWF0dXJlQ29sbGVjdGlvbiA/IGdlb2pzb24uZmVhdHVyZXMubGVuZ3RoIDogMTtcblxuICAgIC8vIFRoaXMgbG9naWMgbWF5IGxvb2sgYSBsaXR0bGUgd2VpcmQuIFRoZSByZWFzb24gd2h5IGl0IGlzIHRoYXQgd2F5XG4gICAgLy8gaXMgYmVjYXVzZSBpdCdzIHRyeWluZyB0byBiZSBmYXN0LiBHZW9KU09OIHN1cHBvcnRzIG11bHRpcGxlIGtpbmRzXG4gICAgLy8gb2Ygb2JqZWN0cyBhdCBpdHMgcm9vdDogRmVhdHVyZUNvbGxlY3Rpb24sIEZlYXR1cmVzLCBHZW9tZXRyaWVzLlxuICAgIC8vIFRoaXMgZnVuY3Rpb24gaGFzIHRoZSByZXNwb25zaWJpbGl0eSBvZiBoYW5kbGluZyBhbGwgb2YgdGhlbSwgYW5kIHRoYXRcbiAgICAvLyBtZWFucyB0aGF0IHNvbWUgb2YgdGhlIGBmb3JgIGxvb3BzIHlvdSBzZWUgYmVsb3cgYWN0dWFsbHkganVzdCBkb24ndCBhcHBseVxuICAgIC8vIHRvIGNlcnRhaW4gaW5wdXRzLiBGb3IgaW5zdGFuY2UsIGlmIHlvdSBnaXZlIHRoaXMganVzdCBhXG4gICAgLy8gUG9pbnQgZ2VvbWV0cnksIHRoZW4gYm90aCBsb29wcyBhcmUgc2hvcnQtY2lyY3VpdGVkIGFuZCBhbGwgd2UgZG9cbiAgICAvLyBpcyBncmFkdWFsbHkgcmVuYW1lIHRoZSBpbnB1dCB1bnRpbCBpdCdzIGNhbGxlZCAnZ2VvbWV0cnknLlxuICAgIC8vXG4gICAgLy8gVGhpcyBhbHNvIGFpbXMgdG8gYWxsb2NhdGUgYXMgZmV3IHJlc291cmNlcyBhcyBwb3NzaWJsZToganVzdCBhXG4gICAgLy8gZmV3IG51bWJlcnMgYW5kIGJvb2xlYW5zLCByYXRoZXIgdGhhbiBhbnkgdGVtcG9yYXJ5IGFycmF5cyBhcyB3b3VsZFxuICAgIC8vIGJlIHJlcXVpcmVkIHdpdGggdGhlIG5vcm1hbGl6YXRpb24gYXBwcm9hY2guXG4gICAgZm9yIChmZWF0dXJlSW5kZXggPSAwOyBmZWF0dXJlSW5kZXggPCBzdG9wOyBmZWF0dXJlSW5kZXgrKykge1xuICAgICAgICB2YXIgZmVhdHVyZVN1YkluZGV4ID0gMDtcblxuICAgICAgICBnZW9tZXRyeU1heWJlQ29sbGVjdGlvbiA9IChpc0ZlYXR1cmVDb2xsZWN0aW9uID8gZ2VvanNvbi5mZWF0dXJlc1tmZWF0dXJlSW5kZXhdLmdlb21ldHJ5IDpcbiAgICAgICAgKGlzRmVhdHVyZSA/IGdlb2pzb24uZ2VvbWV0cnkgOiBnZW9qc29uKSk7XG4gICAgICAgIGlzR2VvbWV0cnlDb2xsZWN0aW9uID0gKGdlb21ldHJ5TWF5YmVDb2xsZWN0aW9uKSA/IGdlb21ldHJ5TWF5YmVDb2xsZWN0aW9uLnR5cGUgPT09ICdHZW9tZXRyeUNvbGxlY3Rpb24nIDogZmFsc2U7XG4gICAgICAgIHN0b3BHID0gaXNHZW9tZXRyeUNvbGxlY3Rpb24gPyBnZW9tZXRyeU1heWJlQ29sbGVjdGlvbi5nZW9tZXRyaWVzLmxlbmd0aCA6IDE7XG5cbiAgICAgICAgZm9yIChnZW9tZXRyeUluZGV4ID0gMDsgZ2VvbWV0cnlJbmRleCA8IHN0b3BHOyBnZW9tZXRyeUluZGV4KyspIHtcbiAgICAgICAgICAgIGdlb21ldHJ5ID0gaXNHZW9tZXRyeUNvbGxlY3Rpb24gP1xuICAgICAgICAgICAgZ2VvbWV0cnlNYXliZUNvbGxlY3Rpb24uZ2VvbWV0cmllc1tnZW9tZXRyeUluZGV4XSA6IGdlb21ldHJ5TWF5YmVDb2xsZWN0aW9uO1xuXG4gICAgICAgICAgICAvLyBIYW5kbGVzIG51bGwgR2VvbWV0cnkgLS0gU2tpcHMgdGhpcyBnZW9tZXRyeVxuICAgICAgICAgICAgaWYgKGdlb21ldHJ5ID09PSBudWxsKSBjb250aW51ZTtcbiAgICAgICAgICAgIGNvb3JkcyA9IGdlb21ldHJ5LmNvb3JkaW5hdGVzO1xuICAgICAgICAgICAgdmFyIGdlb21UeXBlID0gZ2VvbWV0cnkudHlwZTtcblxuICAgICAgICAgICAgd3JhcFNocmluayA9IChleGNsdWRlV3JhcENvb3JkICYmIChnZW9tVHlwZSA9PT0gJ1BvbHlnb24nIHx8IGdlb21UeXBlID09PSAnTXVsdGlQb2x5Z29uJykpID8gMSA6IDA7XG5cbiAgICAgICAgICAgIHN3aXRjaCAoZ2VvbVR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgbnVsbDpcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ1BvaW50JzpcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhjb29yZHMsIGNvb3JkSW5kZXgsIGZlYXR1cmVJbmRleCwgZmVhdHVyZVN1YkluZGV4KTtcbiAgICAgICAgICAgICAgICBjb29yZEluZGV4Kys7XG4gICAgICAgICAgICAgICAgZmVhdHVyZVN1YkluZGV4Kys7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdMaW5lU3RyaW5nJzpcbiAgICAgICAgICAgIGNhc2UgJ011bHRpUG9pbnQnOlxuICAgICAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCBjb29yZHMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2soY29vcmRzW2pdLCBjb29yZEluZGV4LCBmZWF0dXJlSW5kZXgsIGZlYXR1cmVTdWJJbmRleCk7XG4gICAgICAgICAgICAgICAgICAgIGNvb3JkSW5kZXgrKztcbiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZVN1YkluZGV4Kys7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAnUG9seWdvbic6XG4gICAgICAgICAgICBjYXNlICdNdWx0aUxpbmVTdHJpbmcnOlxuICAgICAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCBjb29yZHMubGVuZ3RoOyBqKyspXG4gICAgICAgICAgICAgICAgICAgIGZvciAoayA9IDA7IGsgPCBjb29yZHNbal0ubGVuZ3RoIC0gd3JhcFNocmluazsgaysrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFjayhjb29yZHNbal1ba10sIGNvb3JkSW5kZXgsIGZlYXR1cmVJbmRleCwgZmVhdHVyZVN1YkluZGV4KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvb3JkSW5kZXgrKztcbiAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVTdWJJbmRleCsrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlICdNdWx0aVBvbHlnb24nOlxuICAgICAgICAgICAgICAgIGZvciAoaiA9IDA7IGogPCBjb29yZHMubGVuZ3RoOyBqKyspXG4gICAgICAgICAgICAgICAgICAgIGZvciAoayA9IDA7IGsgPCBjb29yZHNbal0ubGVuZ3RoOyBrKyspXG4gICAgICAgICAgICAgICAgICAgICAgICBmb3IgKGwgPSAwOyBsIDwgY29vcmRzW2pdW2tdLmxlbmd0aCAtIHdyYXBTaHJpbms7IGwrKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKGNvb3Jkc1tqXVtrXVtsXSwgY29vcmRJbmRleCwgZmVhdHVyZUluZGV4LCBmZWF0dXJlU3ViSW5kZXgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvb3JkSW5kZXgrKztcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlU3ViSW5kZXgrKztcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ0dlb21ldHJ5Q29sbGVjdGlvbic6XG4gICAgICAgICAgICAgICAgZm9yIChqID0gMDsgaiA8IGdlb21ldHJ5Lmdlb21ldHJpZXMubGVuZ3RoOyBqKyspXG4gICAgICAgICAgICAgICAgICAgIGNvb3JkRWFjaChnZW9tZXRyeS5nZW9tZXRyaWVzW2pdLCBjYWxsYmFjaywgZXhjbHVkZVdyYXBDb29yZCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBkZWZhdWx0OiB0aHJvdyBuZXcgRXJyb3IoJ1Vua25vd24gR2VvbWV0cnkgVHlwZScpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIENhbGxiYWNrIGZvciBjb29yZFJlZHVjZVxuICpcbiAqIFRoZSBmaXJzdCB0aW1lIHRoZSBjYWxsYmFjayBmdW5jdGlvbiBpcyBjYWxsZWQsIHRoZSB2YWx1ZXMgcHJvdmlkZWQgYXMgYXJndW1lbnRzIGRlcGVuZFxuICogb24gd2hldGhlciB0aGUgcmVkdWNlIG1ldGhvZCBoYXMgYW4gaW5pdGlhbFZhbHVlIGFyZ3VtZW50LlxuICpcbiAqIElmIGFuIGluaXRpYWxWYWx1ZSBpcyBwcm92aWRlZCB0byB0aGUgcmVkdWNlIG1ldGhvZDpcbiAqICAtIFRoZSBwcmV2aW91c1ZhbHVlIGFyZ3VtZW50IGlzIGluaXRpYWxWYWx1ZS5cbiAqICAtIFRoZSBjdXJyZW50VmFsdWUgYXJndW1lbnQgaXMgdGhlIHZhbHVlIG9mIHRoZSBmaXJzdCBlbGVtZW50IHByZXNlbnQgaW4gdGhlIGFycmF5LlxuICpcbiAqIElmIGFuIGluaXRpYWxWYWx1ZSBpcyBub3QgcHJvdmlkZWQ6XG4gKiAgLSBUaGUgcHJldmlvdXNWYWx1ZSBhcmd1bWVudCBpcyB0aGUgdmFsdWUgb2YgdGhlIGZpcnN0IGVsZW1lbnQgcHJlc2VudCBpbiB0aGUgYXJyYXkuXG4gKiAgLSBUaGUgY3VycmVudFZhbHVlIGFyZ3VtZW50IGlzIHRoZSB2YWx1ZSBvZiB0aGUgc2Vjb25kIGVsZW1lbnQgcHJlc2VudCBpbiB0aGUgYXJyYXkuXG4gKlxuICogQGNhbGxiYWNrIGNvb3JkUmVkdWNlQ2FsbGJhY2tcbiAqIEBwYXJhbSB7Kn0gcHJldmlvdXNWYWx1ZSBUaGUgYWNjdW11bGF0ZWQgdmFsdWUgcHJldmlvdXNseSByZXR1cm5lZCBpbiB0aGUgbGFzdCBpbnZvY2F0aW9uXG4gKiBvZiB0aGUgY2FsbGJhY2ssIG9yIGluaXRpYWxWYWx1ZSwgaWYgc3VwcGxpZWQuXG4gKiBAcGFyYW0ge0FycmF5PG51bWJlcj59IGN1cnJlbnRDb29yZCBUaGUgY3VycmVudCBjb29yZGluYXRlIGJlaW5nIHByb2Nlc3NlZC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBjb29yZEluZGV4IFRoZSBjdXJyZW50IGluZGV4IG9mIHRoZSBjb29yZGluYXRlIGJlaW5nIHByb2Nlc3NlZC5cbiAqIFN0YXJ0cyBhdCBpbmRleCAwLCBpZiBhbiBpbml0aWFsVmFsdWUgaXMgcHJvdmlkZWQsIGFuZCBhdCBpbmRleCAxIG90aGVyd2lzZS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBmZWF0dXJlSW5kZXggVGhlIGN1cnJlbnQgaW5kZXggb2YgdGhlIGZlYXR1cmUgYmVpbmcgcHJvY2Vzc2VkLlxuICogQHBhcmFtIHtudW1iZXJ9IGZlYXR1cmVTdWJJbmRleCBUaGUgY3VycmVudCBzdWJJbmRleCBvZiB0aGUgZmVhdHVyZSBiZWluZyBwcm9jZXNzZWQuXG4gKi9cblxuLyoqXG4gKiBSZWR1Y2UgY29vcmRpbmF0ZXMgaW4gYW55IEdlb0pTT04gb2JqZWN0LCBzaW1pbGFyIHRvIEFycmF5LnJlZHVjZSgpXG4gKlxuICogQG5hbWUgY29vcmRSZWR1Y2VcbiAqIEBwYXJhbSB7RmVhdHVyZUNvbGxlY3Rpb258R2VvbWV0cnl8RmVhdHVyZX0gZ2VvanNvbiBhbnkgR2VvSlNPTiBvYmplY3RcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrIGEgbWV0aG9kIHRoYXQgdGFrZXMgKHByZXZpb3VzVmFsdWUsIGN1cnJlbnRDb29yZCwgY29vcmRJbmRleClcbiAqIEBwYXJhbSB7Kn0gW2luaXRpYWxWYWx1ZV0gVmFsdWUgdG8gdXNlIGFzIHRoZSBmaXJzdCBhcmd1bWVudCB0byB0aGUgZmlyc3QgY2FsbCBvZiB0aGUgY2FsbGJhY2suXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtleGNsdWRlV3JhcENvb3JkPWZhbHNlXSB3aGV0aGVyIG9yIG5vdCB0byBpbmNsdWRlIHRoZSBmaW5hbCBjb29yZGluYXRlIG9mIExpbmVhclJpbmdzIHRoYXQgd3JhcHMgdGhlIHJpbmcgaW4gaXRzIGl0ZXJhdGlvbi5cbiAqIEByZXR1cm5zIHsqfSBUaGUgdmFsdWUgdGhhdCByZXN1bHRzIGZyb20gdGhlIHJlZHVjdGlvbi5cbiAqIEBleGFtcGxlXG4gKiB2YXIgZmVhdHVyZXMgPSB0dXJmLmZlYXR1cmVDb2xsZWN0aW9uKFtcbiAqICAgdHVyZi5wb2ludChbMjYsIDM3XSwge1wiZm9vXCI6IFwiYmFyXCJ9KSxcbiAqICAgdHVyZi5wb2ludChbMzYsIDUzXSwge1wiaGVsbG9cIjogXCJ3b3JsZFwifSlcbiAqIF0pO1xuICpcbiAqIHR1cmYuY29vcmRSZWR1Y2UoZmVhdHVyZXMsIGZ1bmN0aW9uIChwcmV2aW91c1ZhbHVlLCBjdXJyZW50Q29vcmQsIGNvb3JkSW5kZXgsIGZlYXR1cmVJbmRleCwgZmVhdHVyZVN1YkluZGV4KSB7XG4gKiAgIC8vPXByZXZpb3VzVmFsdWVcbiAqICAgLy89Y3VycmVudENvb3JkXG4gKiAgIC8vPWNvb3JkSW5kZXhcbiAqICAgLy89ZmVhdHVyZUluZGV4XG4gKiAgIC8vPWZlYXR1cmVTdWJJbmRleFxuICogICByZXR1cm4gY3VycmVudENvb3JkO1xuICogfSk7XG4gKi9cbmZ1bmN0aW9uIGNvb3JkUmVkdWNlKGdlb2pzb24sIGNhbGxiYWNrLCBpbml0aWFsVmFsdWUsIGV4Y2x1ZGVXcmFwQ29vcmQpIHtcbiAgICB2YXIgcHJldmlvdXNWYWx1ZSA9IGluaXRpYWxWYWx1ZTtcbiAgICBjb29yZEVhY2goZ2VvanNvbiwgZnVuY3Rpb24gKGN1cnJlbnRDb29yZCwgY29vcmRJbmRleCwgZmVhdHVyZUluZGV4LCBmZWF0dXJlU3ViSW5kZXgpIHtcbiAgICAgICAgaWYgKGNvb3JkSW5kZXggPT09IDAgJiYgaW5pdGlhbFZhbHVlID09PSB1bmRlZmluZWQpIHByZXZpb3VzVmFsdWUgPSBjdXJyZW50Q29vcmQ7XG4gICAgICAgIGVsc2UgcHJldmlvdXNWYWx1ZSA9IGNhbGxiYWNrKHByZXZpb3VzVmFsdWUsIGN1cnJlbnRDb29yZCwgY29vcmRJbmRleCwgZmVhdHVyZUluZGV4LCBmZWF0dXJlU3ViSW5kZXgpO1xuICAgIH0sIGV4Y2x1ZGVXcmFwQ29vcmQpO1xuICAgIHJldHVybiBwcmV2aW91c1ZhbHVlO1xufVxuXG4vKipcbiAqIENhbGxiYWNrIGZvciBwcm9wRWFjaFxuICpcbiAqIEBjYWxsYmFjayBwcm9wRWFjaENhbGxiYWNrXG4gKiBAcGFyYW0ge09iamVjdH0gY3VycmVudFByb3BlcnRpZXMgVGhlIGN1cnJlbnQgcHJvcGVydGllcyBiZWluZyBwcm9jZXNzZWQuXG4gKiBAcGFyYW0ge251bWJlcn0gZmVhdHVyZUluZGV4IFRoZSBpbmRleCBvZiB0aGUgY3VycmVudCBlbGVtZW50IGJlaW5nIHByb2Nlc3NlZCBpbiB0aGVcbiAqIGFycmF5LlN0YXJ0cyBhdCBpbmRleCAwLCBpZiBhbiBpbml0aWFsVmFsdWUgaXMgcHJvdmlkZWQsIGFuZCBhdCBpbmRleCAxIG90aGVyd2lzZS5cbiAqL1xuXG4vKipcbiAqIEl0ZXJhdGUgb3ZlciBwcm9wZXJ0aWVzIGluIGFueSBHZW9KU09OIG9iamVjdCwgc2ltaWxhciB0byBBcnJheS5mb3JFYWNoKClcbiAqXG4gKiBAbmFtZSBwcm9wRWFjaFxuICogQHBhcmFtIHtGZWF0dXJlQ29sbGVjdGlvbnxGZWF0dXJlfSBnZW9qc29uIGFueSBHZW9KU09OIG9iamVjdFxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2sgYSBtZXRob2QgdGhhdCB0YWtlcyAoY3VycmVudFByb3BlcnRpZXMsIGZlYXR1cmVJbmRleClcbiAqIEBleGFtcGxlXG4gKiB2YXIgZmVhdHVyZXMgPSB0dXJmLmZlYXR1cmVDb2xsZWN0aW9uKFtcbiAqICAgICB0dXJmLnBvaW50KFsyNiwgMzddLCB7Zm9vOiAnYmFyJ30pLFxuICogICAgIHR1cmYucG9pbnQoWzM2LCA1M10sIHtoZWxsbzogJ3dvcmxkJ30pXG4gKiBdKTtcbiAqXG4gKiB0dXJmLnByb3BFYWNoKGZlYXR1cmVzLCBmdW5jdGlvbiAoY3VycmVudFByb3BlcnRpZXMsIGZlYXR1cmVJbmRleCkge1xuICogICAvLz1jdXJyZW50UHJvcGVydGllc1xuICogICAvLz1mZWF0dXJlSW5kZXhcbiAqIH0pO1xuICovXG5mdW5jdGlvbiBwcm9wRWFjaChnZW9qc29uLCBjYWxsYmFjaykge1xuICAgIHZhciBpO1xuICAgIHN3aXRjaCAoZ2VvanNvbi50eXBlKSB7XG4gICAgY2FzZSAnRmVhdHVyZUNvbGxlY3Rpb24nOlxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgZ2VvanNvbi5mZWF0dXJlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgY2FsbGJhY2soZ2VvanNvbi5mZWF0dXJlc1tpXS5wcm9wZXJ0aWVzLCBpKTtcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICBjYXNlICdGZWF0dXJlJzpcbiAgICAgICAgY2FsbGJhY2soZ2VvanNvbi5wcm9wZXJ0aWVzLCAwKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxufVxuXG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHByb3BSZWR1Y2VcbiAqXG4gKiBUaGUgZmlyc3QgdGltZSB0aGUgY2FsbGJhY2sgZnVuY3Rpb24gaXMgY2FsbGVkLCB0aGUgdmFsdWVzIHByb3ZpZGVkIGFzIGFyZ3VtZW50cyBkZXBlbmRcbiAqIG9uIHdoZXRoZXIgdGhlIHJlZHVjZSBtZXRob2QgaGFzIGFuIGluaXRpYWxWYWx1ZSBhcmd1bWVudC5cbiAqXG4gKiBJZiBhbiBpbml0aWFsVmFsdWUgaXMgcHJvdmlkZWQgdG8gdGhlIHJlZHVjZSBtZXRob2Q6XG4gKiAgLSBUaGUgcHJldmlvdXNWYWx1ZSBhcmd1bWVudCBpcyBpbml0aWFsVmFsdWUuXG4gKiAgLSBUaGUgY3VycmVudFZhbHVlIGFyZ3VtZW50IGlzIHRoZSB2YWx1ZSBvZiB0aGUgZmlyc3QgZWxlbWVudCBwcmVzZW50IGluIHRoZSBhcnJheS5cbiAqXG4gKiBJZiBhbiBpbml0aWFsVmFsdWUgaXMgbm90IHByb3ZpZGVkOlxuICogIC0gVGhlIHByZXZpb3VzVmFsdWUgYXJndW1lbnQgaXMgdGhlIHZhbHVlIG9mIHRoZSBmaXJzdCBlbGVtZW50IHByZXNlbnQgaW4gdGhlIGFycmF5LlxuICogIC0gVGhlIGN1cnJlbnRWYWx1ZSBhcmd1bWVudCBpcyB0aGUgdmFsdWUgb2YgdGhlIHNlY29uZCBlbGVtZW50IHByZXNlbnQgaW4gdGhlIGFycmF5LlxuICpcbiAqIEBjYWxsYmFjayBwcm9wUmVkdWNlQ2FsbGJhY2tcbiAqIEBwYXJhbSB7Kn0gcHJldmlvdXNWYWx1ZSBUaGUgYWNjdW11bGF0ZWQgdmFsdWUgcHJldmlvdXNseSByZXR1cm5lZCBpbiB0aGUgbGFzdCBpbnZvY2F0aW9uXG4gKiBvZiB0aGUgY2FsbGJhY2ssIG9yIGluaXRpYWxWYWx1ZSwgaWYgc3VwcGxpZWQuXG4gKiBAcGFyYW0geyp9IGN1cnJlbnRQcm9wZXJ0aWVzIFRoZSBjdXJyZW50IHByb3BlcnRpZXMgYmVpbmcgcHJvY2Vzc2VkLlxuICogQHBhcmFtIHtudW1iZXJ9IGZlYXR1cmVJbmRleCBUaGUgaW5kZXggb2YgdGhlIGN1cnJlbnQgZWxlbWVudCBiZWluZyBwcm9jZXNzZWQgaW4gdGhlXG4gKiBhcnJheS5TdGFydHMgYXQgaW5kZXggMCwgaWYgYW4gaW5pdGlhbFZhbHVlIGlzIHByb3ZpZGVkLCBhbmQgYXQgaW5kZXggMSBvdGhlcndpc2UuXG4gKi9cblxuLyoqXG4gKiBSZWR1Y2UgcHJvcGVydGllcyBpbiBhbnkgR2VvSlNPTiBvYmplY3QgaW50byBhIHNpbmdsZSB2YWx1ZSxcbiAqIHNpbWlsYXIgdG8gaG93IEFycmF5LnJlZHVjZSB3b3Jrcy4gSG93ZXZlciwgaW4gdGhpcyBjYXNlIHdlIGxhemlseSBydW5cbiAqIHRoZSByZWR1Y3Rpb24sIHNvIGFuIGFycmF5IG9mIGFsbCBwcm9wZXJ0aWVzIGlzIHVubmVjZXNzYXJ5LlxuICpcbiAqIEBuYW1lIHByb3BSZWR1Y2VcbiAqIEBwYXJhbSB7RmVhdHVyZUNvbGxlY3Rpb258RmVhdHVyZX0gZ2VvanNvbiBhbnkgR2VvSlNPTiBvYmplY3RcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrIGEgbWV0aG9kIHRoYXQgdGFrZXMgKHByZXZpb3VzVmFsdWUsIGN1cnJlbnRQcm9wZXJ0aWVzLCBmZWF0dXJlSW5kZXgpXG4gKiBAcGFyYW0geyp9IFtpbml0aWFsVmFsdWVdIFZhbHVlIHRvIHVzZSBhcyB0aGUgZmlyc3QgYXJndW1lbnQgdG8gdGhlIGZpcnN0IGNhbGwgb2YgdGhlIGNhbGxiYWNrLlxuICogQHJldHVybnMgeyp9IFRoZSB2YWx1ZSB0aGF0IHJlc3VsdHMgZnJvbSB0aGUgcmVkdWN0aW9uLlxuICogQGV4YW1wbGVcbiAqIHZhciBmZWF0dXJlcyA9IHR1cmYuZmVhdHVyZUNvbGxlY3Rpb24oW1xuICogICAgIHR1cmYucG9pbnQoWzI2LCAzN10sIHtmb286ICdiYXInfSksXG4gKiAgICAgdHVyZi5wb2ludChbMzYsIDUzXSwge2hlbGxvOiAnd29ybGQnfSlcbiAqIF0pO1xuICpcbiAqIHR1cmYucHJvcFJlZHVjZShmZWF0dXJlcywgZnVuY3Rpb24gKHByZXZpb3VzVmFsdWUsIGN1cnJlbnRQcm9wZXJ0aWVzLCBmZWF0dXJlSW5kZXgpIHtcbiAqICAgLy89cHJldmlvdXNWYWx1ZVxuICogICAvLz1jdXJyZW50UHJvcGVydGllc1xuICogICAvLz1mZWF0dXJlSW5kZXhcbiAqICAgcmV0dXJuIGN1cnJlbnRQcm9wZXJ0aWVzXG4gKiB9KTtcbiAqL1xuZnVuY3Rpb24gcHJvcFJlZHVjZShnZW9qc29uLCBjYWxsYmFjaywgaW5pdGlhbFZhbHVlKSB7XG4gICAgdmFyIHByZXZpb3VzVmFsdWUgPSBpbml0aWFsVmFsdWU7XG4gICAgcHJvcEVhY2goZ2VvanNvbiwgZnVuY3Rpb24gKGN1cnJlbnRQcm9wZXJ0aWVzLCBmZWF0dXJlSW5kZXgpIHtcbiAgICAgICAgaWYgKGZlYXR1cmVJbmRleCA9PT0gMCAmJiBpbml0aWFsVmFsdWUgPT09IHVuZGVmaW5lZCkgcHJldmlvdXNWYWx1ZSA9IGN1cnJlbnRQcm9wZXJ0aWVzO1xuICAgICAgICBlbHNlIHByZXZpb3VzVmFsdWUgPSBjYWxsYmFjayhwcmV2aW91c1ZhbHVlLCBjdXJyZW50UHJvcGVydGllcywgZmVhdHVyZUluZGV4KTtcbiAgICB9KTtcbiAgICByZXR1cm4gcHJldmlvdXNWYWx1ZTtcbn1cblxuLyoqXG4gKiBDYWxsYmFjayBmb3IgZmVhdHVyZUVhY2hcbiAqXG4gKiBAY2FsbGJhY2sgZmVhdHVyZUVhY2hDYWxsYmFja1xuICogQHBhcmFtIHtGZWF0dXJlPGFueT59IGN1cnJlbnRGZWF0dXJlIFRoZSBjdXJyZW50IGZlYXR1cmUgYmVpbmcgcHJvY2Vzc2VkLlxuICogQHBhcmFtIHtudW1iZXJ9IGZlYXR1cmVJbmRleCBUaGUgaW5kZXggb2YgdGhlIGN1cnJlbnQgZWxlbWVudCBiZWluZyBwcm9jZXNzZWQgaW4gdGhlXG4gKiBhcnJheS5TdGFydHMgYXQgaW5kZXggMCwgaWYgYW4gaW5pdGlhbFZhbHVlIGlzIHByb3ZpZGVkLCBhbmQgYXQgaW5kZXggMSBvdGhlcndpc2UuXG4gKi9cblxuLyoqXG4gKiBJdGVyYXRlIG92ZXIgZmVhdHVyZXMgaW4gYW55IEdlb0pTT04gb2JqZWN0LCBzaW1pbGFyIHRvXG4gKiBBcnJheS5mb3JFYWNoLlxuICpcbiAqIEBuYW1lIGZlYXR1cmVFYWNoXG4gKiBAcGFyYW0ge0dlb21ldHJ5fEZlYXR1cmVDb2xsZWN0aW9ufEZlYXR1cmV9IGdlb2pzb24gYW55IEdlb0pTT04gb2JqZWN0XG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFjayBhIG1ldGhvZCB0aGF0IHRha2VzIChjdXJyZW50RmVhdHVyZSwgZmVhdHVyZUluZGV4KVxuICogQGV4YW1wbGVcbiAqIHZhciBmZWF0dXJlcyA9IHR1cmYuZmVhdHVyZUNvbGxlY3Rpb24oW1xuICogICB0dXJmLnBvaW50KFsyNiwgMzddLCB7Zm9vOiAnYmFyJ30pLFxuICogICB0dXJmLnBvaW50KFszNiwgNTNdLCB7aGVsbG86ICd3b3JsZCd9KVxuICogXSk7XG4gKlxuICogdHVyZi5mZWF0dXJlRWFjaChmZWF0dXJlcywgZnVuY3Rpb24gKGN1cnJlbnRGZWF0dXJlLCBmZWF0dXJlSW5kZXgpIHtcbiAqICAgLy89Y3VycmVudEZlYXR1cmVcbiAqICAgLy89ZmVhdHVyZUluZGV4XG4gKiB9KTtcbiAqL1xuZnVuY3Rpb24gZmVhdHVyZUVhY2goZ2VvanNvbiwgY2FsbGJhY2spIHtcbiAgICBpZiAoZ2VvanNvbi50eXBlID09PSAnRmVhdHVyZScpIHtcbiAgICAgICAgY2FsbGJhY2soZ2VvanNvbiwgMCk7XG4gICAgfSBlbHNlIGlmIChnZW9qc29uLnR5cGUgPT09ICdGZWF0dXJlQ29sbGVjdGlvbicpIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBnZW9qc29uLmZlYXR1cmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjYWxsYmFjayhnZW9qc29uLmZlYXR1cmVzW2ldLCBpKTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqXG4gKiBDYWxsYmFjayBmb3IgZmVhdHVyZVJlZHVjZVxuICpcbiAqIFRoZSBmaXJzdCB0aW1lIHRoZSBjYWxsYmFjayBmdW5jdGlvbiBpcyBjYWxsZWQsIHRoZSB2YWx1ZXMgcHJvdmlkZWQgYXMgYXJndW1lbnRzIGRlcGVuZFxuICogb24gd2hldGhlciB0aGUgcmVkdWNlIG1ldGhvZCBoYXMgYW4gaW5pdGlhbFZhbHVlIGFyZ3VtZW50LlxuICpcbiAqIElmIGFuIGluaXRpYWxWYWx1ZSBpcyBwcm92aWRlZCB0byB0aGUgcmVkdWNlIG1ldGhvZDpcbiAqICAtIFRoZSBwcmV2aW91c1ZhbHVlIGFyZ3VtZW50IGlzIGluaXRpYWxWYWx1ZS5cbiAqICAtIFRoZSBjdXJyZW50VmFsdWUgYXJndW1lbnQgaXMgdGhlIHZhbHVlIG9mIHRoZSBmaXJzdCBlbGVtZW50IHByZXNlbnQgaW4gdGhlIGFycmF5LlxuICpcbiAqIElmIGFuIGluaXRpYWxWYWx1ZSBpcyBub3QgcHJvdmlkZWQ6XG4gKiAgLSBUaGUgcHJldmlvdXNWYWx1ZSBhcmd1bWVudCBpcyB0aGUgdmFsdWUgb2YgdGhlIGZpcnN0IGVsZW1lbnQgcHJlc2VudCBpbiB0aGUgYXJyYXkuXG4gKiAgLSBUaGUgY3VycmVudFZhbHVlIGFyZ3VtZW50IGlzIHRoZSB2YWx1ZSBvZiB0aGUgc2Vjb25kIGVsZW1lbnQgcHJlc2VudCBpbiB0aGUgYXJyYXkuXG4gKlxuICogQGNhbGxiYWNrIGZlYXR1cmVSZWR1Y2VDYWxsYmFja1xuICogQHBhcmFtIHsqfSBwcmV2aW91c1ZhbHVlIFRoZSBhY2N1bXVsYXRlZCB2YWx1ZSBwcmV2aW91c2x5IHJldHVybmVkIGluIHRoZSBsYXN0IGludm9jYXRpb25cbiAqIG9mIHRoZSBjYWxsYmFjaywgb3IgaW5pdGlhbFZhbHVlLCBpZiBzdXBwbGllZC5cbiAqIEBwYXJhbSB7RmVhdHVyZX0gY3VycmVudEZlYXR1cmUgVGhlIGN1cnJlbnQgRmVhdHVyZSBiZWluZyBwcm9jZXNzZWQuXG4gKiBAcGFyYW0ge251bWJlcn0gZmVhdHVyZUluZGV4IFRoZSBpbmRleCBvZiB0aGUgY3VycmVudCBlbGVtZW50IGJlaW5nIHByb2Nlc3NlZCBpbiB0aGVcbiAqIGFycmF5LlN0YXJ0cyBhdCBpbmRleCAwLCBpZiBhbiBpbml0aWFsVmFsdWUgaXMgcHJvdmlkZWQsIGFuZCBhdCBpbmRleCAxIG90aGVyd2lzZS5cbiAqL1xuXG4vKipcbiAqIFJlZHVjZSBmZWF0dXJlcyBpbiBhbnkgR2VvSlNPTiBvYmplY3QsIHNpbWlsYXIgdG8gQXJyYXkucmVkdWNlKCkuXG4gKlxuICogQG5hbWUgZmVhdHVyZVJlZHVjZVxuICogQHBhcmFtIHtHZW9tZXRyeXxGZWF0dXJlQ29sbGVjdGlvbnxGZWF0dXJlfSBnZW9qc29uIGFueSBHZW9KU09OIG9iamVjdFxuICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2sgYSBtZXRob2QgdGhhdCB0YWtlcyAocHJldmlvdXNWYWx1ZSwgY3VycmVudEZlYXR1cmUsIGZlYXR1cmVJbmRleClcbiAqIEBwYXJhbSB7Kn0gW2luaXRpYWxWYWx1ZV0gVmFsdWUgdG8gdXNlIGFzIHRoZSBmaXJzdCBhcmd1bWVudCB0byB0aGUgZmlyc3QgY2FsbCBvZiB0aGUgY2FsbGJhY2suXG4gKiBAcmV0dXJucyB7Kn0gVGhlIHZhbHVlIHRoYXQgcmVzdWx0cyBmcm9tIHRoZSByZWR1Y3Rpb24uXG4gKiBAZXhhbXBsZVxuICogdmFyIGZlYXR1cmVzID0gdHVyZi5mZWF0dXJlQ29sbGVjdGlvbihbXG4gKiAgIHR1cmYucG9pbnQoWzI2LCAzN10sIHtcImZvb1wiOiBcImJhclwifSksXG4gKiAgIHR1cmYucG9pbnQoWzM2LCA1M10sIHtcImhlbGxvXCI6IFwid29ybGRcIn0pXG4gKiBdKTtcbiAqXG4gKiB0dXJmLmZlYXR1cmVSZWR1Y2UoZmVhdHVyZXMsIGZ1bmN0aW9uIChwcmV2aW91c1ZhbHVlLCBjdXJyZW50RmVhdHVyZSwgZmVhdHVyZUluZGV4KSB7XG4gKiAgIC8vPXByZXZpb3VzVmFsdWVcbiAqICAgLy89Y3VycmVudEZlYXR1cmVcbiAqICAgLy89ZmVhdHVyZUluZGV4XG4gKiAgIHJldHVybiBjdXJyZW50RmVhdHVyZVxuICogfSk7XG4gKi9cbmZ1bmN0aW9uIGZlYXR1cmVSZWR1Y2UoZ2VvanNvbiwgY2FsbGJhY2ssIGluaXRpYWxWYWx1ZSkge1xuICAgIHZhciBwcmV2aW91c1ZhbHVlID0gaW5pdGlhbFZhbHVlO1xuICAgIGZlYXR1cmVFYWNoKGdlb2pzb24sIGZ1bmN0aW9uIChjdXJyZW50RmVhdHVyZSwgZmVhdHVyZUluZGV4KSB7XG4gICAgICAgIGlmIChmZWF0dXJlSW5kZXggPT09IDAgJiYgaW5pdGlhbFZhbHVlID09PSB1bmRlZmluZWQpIHByZXZpb3VzVmFsdWUgPSBjdXJyZW50RmVhdHVyZTtcbiAgICAgICAgZWxzZSBwcmV2aW91c1ZhbHVlID0gY2FsbGJhY2socHJldmlvdXNWYWx1ZSwgY3VycmVudEZlYXR1cmUsIGZlYXR1cmVJbmRleCk7XG4gICAgfSk7XG4gICAgcmV0dXJuIHByZXZpb3VzVmFsdWU7XG59XG5cbi8qKlxuICogR2V0IGFsbCBjb29yZGluYXRlcyBmcm9tIGFueSBHZW9KU09OIG9iamVjdC5cbiAqXG4gKiBAbmFtZSBjb29yZEFsbFxuICogQHBhcmFtIHtHZW9tZXRyeXxGZWF0dXJlQ29sbGVjdGlvbnxGZWF0dXJlfSBnZW9qc29uIGFueSBHZW9KU09OIG9iamVjdFxuICogQHJldHVybnMge0FycmF5PEFycmF5PG51bWJlcj4+fSBjb29yZGluYXRlIHBvc2l0aW9uIGFycmF5XG4gKiBAZXhhbXBsZVxuICogdmFyIGZlYXR1cmVzID0gdHVyZi5mZWF0dXJlQ29sbGVjdGlvbihbXG4gKiAgIHR1cmYucG9pbnQoWzI2LCAzN10sIHtmb286ICdiYXInfSksXG4gKiAgIHR1cmYucG9pbnQoWzM2LCA1M10sIHtoZWxsbzogJ3dvcmxkJ30pXG4gKiBdKTtcbiAqXG4gKiB2YXIgY29vcmRzID0gdHVyZi5jb29yZEFsbChmZWF0dXJlcyk7XG4gKiAvLz0gW1syNiwgMzddLCBbMzYsIDUzXV1cbiAqL1xuZnVuY3Rpb24gY29vcmRBbGwoZ2VvanNvbikge1xuICAgIHZhciBjb29yZHMgPSBbXTtcbiAgICBjb29yZEVhY2goZ2VvanNvbiwgZnVuY3Rpb24gKGNvb3JkKSB7XG4gICAgICAgIGNvb3Jkcy5wdXNoKGNvb3JkKTtcbiAgICB9KTtcbiAgICByZXR1cm4gY29vcmRzO1xufVxuXG4vKipcbiAqIENhbGxiYWNrIGZvciBnZW9tRWFjaFxuICpcbiAqIEBjYWxsYmFjayBnZW9tRWFjaENhbGxiYWNrXG4gKiBAcGFyYW0ge0dlb21ldHJ5fSBjdXJyZW50R2VvbWV0cnkgVGhlIGN1cnJlbnQgZ2VvbWV0cnkgYmVpbmcgcHJvY2Vzc2VkLlxuICogQHBhcmFtIHtudW1iZXJ9IGN1cnJlbnRJbmRleCBUaGUgaW5kZXggb2YgdGhlIGN1cnJlbnQgZWxlbWVudCBiZWluZyBwcm9jZXNzZWQgaW4gdGhlXG4gKiBhcnJheS4gU3RhcnRzIGF0IGluZGV4IDAsIGlmIGFuIGluaXRpYWxWYWx1ZSBpcyBwcm92aWRlZCwgYW5kIGF0IGluZGV4IDEgb3RoZXJ3aXNlLlxuICogQHBhcmFtIHtudW1iZXJ9IGN1cnJlbnRQcm9wZXJ0aWVzIFRoZSBjdXJyZW50IGZlYXR1cmUgcHJvcGVydGllcyBiZWluZyBwcm9jZXNzZWQuXG4gKi9cblxuLyoqXG4gKiBJdGVyYXRlIG92ZXIgZWFjaCBnZW9tZXRyeSBpbiBhbnkgR2VvSlNPTiBvYmplY3QsIHNpbWlsYXIgdG8gQXJyYXkuZm9yRWFjaCgpXG4gKlxuICogQG5hbWUgZ2VvbUVhY2hcbiAqIEBwYXJhbSB7R2VvbWV0cnl8RmVhdHVyZUNvbGxlY3Rpb258RmVhdHVyZX0gZ2VvanNvbiBhbnkgR2VvSlNPTiBvYmplY3RcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrIGEgbWV0aG9kIHRoYXQgdGFrZXMgKGN1cnJlbnRHZW9tZXRyeSwgZmVhdHVyZUluZGV4LCBjdXJyZW50UHJvcGVydGllcylcbiAqIEBleGFtcGxlXG4gKiB2YXIgZmVhdHVyZXMgPSB0dXJmLmZlYXR1cmVDb2xsZWN0aW9uKFtcbiAqICAgICB0dXJmLnBvaW50KFsyNiwgMzddLCB7Zm9vOiAnYmFyJ30pLFxuICogICAgIHR1cmYucG9pbnQoWzM2LCA1M10sIHtoZWxsbzogJ3dvcmxkJ30pXG4gKiBdKTtcbiAqXG4gKiB0dXJmLmdlb21FYWNoKGZlYXR1cmVzLCBmdW5jdGlvbiAoY3VycmVudEdlb21ldHJ5LCBmZWF0dXJlSW5kZXgsIGN1cnJlbnRQcm9wZXJ0aWVzKSB7XG4gKiAgIC8vPWN1cnJlbnRHZW9tZXRyeVxuICogICAvLz1mZWF0dXJlSW5kZXhcbiAqICAgLy89Y3VycmVudFByb3BlcnRpZXNcbiAqIH0pO1xuICovXG5mdW5jdGlvbiBnZW9tRWFjaChnZW9qc29uLCBjYWxsYmFjaykge1xuICAgIHZhciBpLCBqLCBnLCBnZW9tZXRyeSwgc3RvcEcsXG4gICAgICAgIGdlb21ldHJ5TWF5YmVDb2xsZWN0aW9uLFxuICAgICAgICBpc0dlb21ldHJ5Q29sbGVjdGlvbixcbiAgICAgICAgZ2VvbWV0cnlQcm9wZXJ0aWVzLFxuICAgICAgICBmZWF0dXJlSW5kZXggPSAwLFxuICAgICAgICBpc0ZlYXR1cmVDb2xsZWN0aW9uID0gZ2VvanNvbi50eXBlID09PSAnRmVhdHVyZUNvbGxlY3Rpb24nLFxuICAgICAgICBpc0ZlYXR1cmUgPSBnZW9qc29uLnR5cGUgPT09ICdGZWF0dXJlJyxcbiAgICAgICAgc3RvcCA9IGlzRmVhdHVyZUNvbGxlY3Rpb24gPyBnZW9qc29uLmZlYXR1cmVzLmxlbmd0aCA6IDE7XG5cbiAgLy8gVGhpcyBsb2dpYyBtYXkgbG9vayBhIGxpdHRsZSB3ZWlyZC4gVGhlIHJlYXNvbiB3aHkgaXQgaXMgdGhhdCB3YXlcbiAgLy8gaXMgYmVjYXVzZSBpdCdzIHRyeWluZyB0byBiZSBmYXN0LiBHZW9KU09OIHN1cHBvcnRzIG11bHRpcGxlIGtpbmRzXG4gIC8vIG9mIG9iamVjdHMgYXQgaXRzIHJvb3Q6IEZlYXR1cmVDb2xsZWN0aW9uLCBGZWF0dXJlcywgR2VvbWV0cmllcy5cbiAgLy8gVGhpcyBmdW5jdGlvbiBoYXMgdGhlIHJlc3BvbnNpYmlsaXR5IG9mIGhhbmRsaW5nIGFsbCBvZiB0aGVtLCBhbmQgdGhhdFxuICAvLyBtZWFucyB0aGF0IHNvbWUgb2YgdGhlIGBmb3JgIGxvb3BzIHlvdSBzZWUgYmVsb3cgYWN0dWFsbHkganVzdCBkb24ndCBhcHBseVxuICAvLyB0byBjZXJ0YWluIGlucHV0cy4gRm9yIGluc3RhbmNlLCBpZiB5b3UgZ2l2ZSB0aGlzIGp1c3QgYVxuICAvLyBQb2ludCBnZW9tZXRyeSwgdGhlbiBib3RoIGxvb3BzIGFyZSBzaG9ydC1jaXJjdWl0ZWQgYW5kIGFsbCB3ZSBkb1xuICAvLyBpcyBncmFkdWFsbHkgcmVuYW1lIHRoZSBpbnB1dCB1bnRpbCBpdCdzIGNhbGxlZCAnZ2VvbWV0cnknLlxuICAvL1xuICAvLyBUaGlzIGFsc28gYWltcyB0byBhbGxvY2F0ZSBhcyBmZXcgcmVzb3VyY2VzIGFzIHBvc3NpYmxlOiBqdXN0IGFcbiAgLy8gZmV3IG51bWJlcnMgYW5kIGJvb2xlYW5zLCByYXRoZXIgdGhhbiBhbnkgdGVtcG9yYXJ5IGFycmF5cyBhcyB3b3VsZFxuICAvLyBiZSByZXF1aXJlZCB3aXRoIHRoZSBub3JtYWxpemF0aW9uIGFwcHJvYWNoLlxuICAgIGZvciAoaSA9IDA7IGkgPCBzdG9wOyBpKyspIHtcblxuICAgICAgICBnZW9tZXRyeU1heWJlQ29sbGVjdGlvbiA9IChpc0ZlYXR1cmVDb2xsZWN0aW9uID8gZ2VvanNvbi5mZWF0dXJlc1tpXS5nZW9tZXRyeSA6XG4gICAgICAgIChpc0ZlYXR1cmUgPyBnZW9qc29uLmdlb21ldHJ5IDogZ2VvanNvbikpO1xuICAgICAgICBnZW9tZXRyeVByb3BlcnRpZXMgPSAoaXNGZWF0dXJlQ29sbGVjdGlvbiA/IGdlb2pzb24uZmVhdHVyZXNbaV0ucHJvcGVydGllcyA6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoaXNGZWF0dXJlID8gZ2VvanNvbi5wcm9wZXJ0aWVzIDoge30pKTtcbiAgICAgICAgaXNHZW9tZXRyeUNvbGxlY3Rpb24gPSAoZ2VvbWV0cnlNYXliZUNvbGxlY3Rpb24pID8gZ2VvbWV0cnlNYXliZUNvbGxlY3Rpb24udHlwZSA9PT0gJ0dlb21ldHJ5Q29sbGVjdGlvbicgOiBmYWxzZTtcbiAgICAgICAgc3RvcEcgPSBpc0dlb21ldHJ5Q29sbGVjdGlvbiA/IGdlb21ldHJ5TWF5YmVDb2xsZWN0aW9uLmdlb21ldHJpZXMubGVuZ3RoIDogMTtcblxuICAgICAgICBmb3IgKGcgPSAwOyBnIDwgc3RvcEc7IGcrKykge1xuICAgICAgICAgICAgZ2VvbWV0cnkgPSBpc0dlb21ldHJ5Q29sbGVjdGlvbiA/XG4gICAgICAgICAgICBnZW9tZXRyeU1heWJlQ29sbGVjdGlvbi5nZW9tZXRyaWVzW2ddIDogZ2VvbWV0cnlNYXliZUNvbGxlY3Rpb247XG5cbiAgICAgICAgICAgIC8vIEhhbmRsZSBudWxsIEdlb21ldHJ5XG4gICAgICAgICAgICBpZiAoZ2VvbWV0cnkgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICBjYWxsYmFjayhudWxsLCBmZWF0dXJlSW5kZXgsIGdlb21ldHJ5UHJvcGVydGllcyk7XG4gICAgICAgICAgICAgICAgZmVhdHVyZUluZGV4Kys7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzd2l0Y2ggKGdlb21ldHJ5LnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ1BvaW50JzpcbiAgICAgICAgICAgIGNhc2UgJ0xpbmVTdHJpbmcnOlxuICAgICAgICAgICAgY2FzZSAnTXVsdGlQb2ludCc6XG4gICAgICAgICAgICBjYXNlICdQb2x5Z29uJzpcbiAgICAgICAgICAgIGNhc2UgJ011bHRpTGluZVN0cmluZyc6XG4gICAgICAgICAgICBjYXNlICdNdWx0aVBvbHlnb24nOiB7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2soZ2VvbWV0cnksIGZlYXR1cmVJbmRleCwgZ2VvbWV0cnlQcm9wZXJ0aWVzKTtcbiAgICAgICAgICAgICAgICBmZWF0dXJlSW5kZXgrKztcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhc2UgJ0dlb21ldHJ5Q29sbGVjdGlvbic6IHtcbiAgICAgICAgICAgICAgICBmb3IgKGogPSAwOyBqIDwgZ2VvbWV0cnkuZ2VvbWV0cmllcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICBjYWxsYmFjayhnZW9tZXRyeS5nZW9tZXRyaWVzW2pdLCBmZWF0dXJlSW5kZXgsIGdlb21ldHJ5UHJvcGVydGllcyk7XG4gICAgICAgICAgICAgICAgICAgIGZlYXR1cmVJbmRleCsrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGRlZmF1bHQ6IHRocm93IG5ldyBFcnJvcignVW5rbm93biBHZW9tZXRyeSBUeXBlJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG59XG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIGdlb21SZWR1Y2VcbiAqXG4gKiBUaGUgZmlyc3QgdGltZSB0aGUgY2FsbGJhY2sgZnVuY3Rpb24gaXMgY2FsbGVkLCB0aGUgdmFsdWVzIHByb3ZpZGVkIGFzIGFyZ3VtZW50cyBkZXBlbmRcbiAqIG9uIHdoZXRoZXIgdGhlIHJlZHVjZSBtZXRob2QgaGFzIGFuIGluaXRpYWxWYWx1ZSBhcmd1bWVudC5cbiAqXG4gKiBJZiBhbiBpbml0aWFsVmFsdWUgaXMgcHJvdmlkZWQgdG8gdGhlIHJlZHVjZSBtZXRob2Q6XG4gKiAgLSBUaGUgcHJldmlvdXNWYWx1ZSBhcmd1bWVudCBpcyBpbml0aWFsVmFsdWUuXG4gKiAgLSBUaGUgY3VycmVudFZhbHVlIGFyZ3VtZW50IGlzIHRoZSB2YWx1ZSBvZiB0aGUgZmlyc3QgZWxlbWVudCBwcmVzZW50IGluIHRoZSBhcnJheS5cbiAqXG4gKiBJZiBhbiBpbml0aWFsVmFsdWUgaXMgbm90IHByb3ZpZGVkOlxuICogIC0gVGhlIHByZXZpb3VzVmFsdWUgYXJndW1lbnQgaXMgdGhlIHZhbHVlIG9mIHRoZSBmaXJzdCBlbGVtZW50IHByZXNlbnQgaW4gdGhlIGFycmF5LlxuICogIC0gVGhlIGN1cnJlbnRWYWx1ZSBhcmd1bWVudCBpcyB0aGUgdmFsdWUgb2YgdGhlIHNlY29uZCBlbGVtZW50IHByZXNlbnQgaW4gdGhlIGFycmF5LlxuICpcbiAqIEBjYWxsYmFjayBnZW9tUmVkdWNlQ2FsbGJhY2tcbiAqIEBwYXJhbSB7Kn0gcHJldmlvdXNWYWx1ZSBUaGUgYWNjdW11bGF0ZWQgdmFsdWUgcHJldmlvdXNseSByZXR1cm5lZCBpbiB0aGUgbGFzdCBpbnZvY2F0aW9uXG4gKiBvZiB0aGUgY2FsbGJhY2ssIG9yIGluaXRpYWxWYWx1ZSwgaWYgc3VwcGxpZWQuXG4gKiBAcGFyYW0ge0dlb21ldHJ5fSBjdXJyZW50R2VvbWV0cnkgVGhlIGN1cnJlbnQgRmVhdHVyZSBiZWluZyBwcm9jZXNzZWQuXG4gKiBAcGFyYW0ge251bWJlcn0gY3VycmVudEluZGV4IFRoZSBpbmRleCBvZiB0aGUgY3VycmVudCBlbGVtZW50IGJlaW5nIHByb2Nlc3NlZCBpbiB0aGVcbiAqIGFycmF5LlN0YXJ0cyBhdCBpbmRleCAwLCBpZiBhbiBpbml0aWFsVmFsdWUgaXMgcHJvdmlkZWQsIGFuZCBhdCBpbmRleCAxIG90aGVyd2lzZS5cbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXJyZW50UHJvcGVydGllcyBUaGUgY3VycmVudCBmZWF0dXJlIHByb3BlcnRpZXMgYmVpbmcgcHJvY2Vzc2VkLlxuICovXG5cbi8qKlxuICogUmVkdWNlIGdlb21ldHJ5IGluIGFueSBHZW9KU09OIG9iamVjdCwgc2ltaWxhciB0byBBcnJheS5yZWR1Y2UoKS5cbiAqXG4gKiBAbmFtZSBnZW9tUmVkdWNlXG4gKiBAcGFyYW0ge0dlb21ldHJ5fEZlYXR1cmVDb2xsZWN0aW9ufEZlYXR1cmV9IGdlb2pzb24gYW55IEdlb0pTT04gb2JqZWN0XG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFjayBhIG1ldGhvZCB0aGF0IHRha2VzIChwcmV2aW91c1ZhbHVlLCBjdXJyZW50R2VvbWV0cnksIGZlYXR1cmVJbmRleCwgY3VycmVudFByb3BlcnRpZXMpXG4gKiBAcGFyYW0geyp9IFtpbml0aWFsVmFsdWVdIFZhbHVlIHRvIHVzZSBhcyB0aGUgZmlyc3QgYXJndW1lbnQgdG8gdGhlIGZpcnN0IGNhbGwgb2YgdGhlIGNhbGxiYWNrLlxuICogQHJldHVybnMgeyp9IFRoZSB2YWx1ZSB0aGF0IHJlc3VsdHMgZnJvbSB0aGUgcmVkdWN0aW9uLlxuICogQGV4YW1wbGVcbiAqIHZhciBmZWF0dXJlcyA9IHR1cmYuZmVhdHVyZUNvbGxlY3Rpb24oW1xuICogICAgIHR1cmYucG9pbnQoWzI2LCAzN10sIHtmb286ICdiYXInfSksXG4gKiAgICAgdHVyZi5wb2ludChbMzYsIDUzXSwge2hlbGxvOiAnd29ybGQnfSlcbiAqIF0pO1xuICpcbiAqIHR1cmYuZ2VvbVJlZHVjZShmZWF0dXJlcywgZnVuY3Rpb24gKHByZXZpb3VzVmFsdWUsIGN1cnJlbnRHZW9tZXRyeSwgZmVhdHVyZUluZGV4LCBjdXJyZW50UHJvcGVydGllcykge1xuICogICAvLz1wcmV2aW91c1ZhbHVlXG4gKiAgIC8vPWN1cnJlbnRHZW9tZXRyeVxuICogICAvLz1mZWF0dXJlSW5kZXhcbiAqICAgLy89Y3VycmVudFByb3BlcnRpZXNcbiAqICAgcmV0dXJuIGN1cnJlbnRHZW9tZXRyeVxuICogfSk7XG4gKi9cbmZ1bmN0aW9uIGdlb21SZWR1Y2UoZ2VvanNvbiwgY2FsbGJhY2ssIGluaXRpYWxWYWx1ZSkge1xuICAgIHZhciBwcmV2aW91c1ZhbHVlID0gaW5pdGlhbFZhbHVlO1xuICAgIGdlb21FYWNoKGdlb2pzb24sIGZ1bmN0aW9uIChjdXJyZW50R2VvbWV0cnksIGN1cnJlbnRJbmRleCwgY3VycmVudFByb3BlcnRpZXMpIHtcbiAgICAgICAgaWYgKGN1cnJlbnRJbmRleCA9PT0gMCAmJiBpbml0aWFsVmFsdWUgPT09IHVuZGVmaW5lZCkgcHJldmlvdXNWYWx1ZSA9IGN1cnJlbnRHZW9tZXRyeTtcbiAgICAgICAgZWxzZSBwcmV2aW91c1ZhbHVlID0gY2FsbGJhY2socHJldmlvdXNWYWx1ZSwgY3VycmVudEdlb21ldHJ5LCBjdXJyZW50SW5kZXgsIGN1cnJlbnRQcm9wZXJ0aWVzKTtcbiAgICB9KTtcbiAgICByZXR1cm4gcHJldmlvdXNWYWx1ZTtcbn1cblxuLyoqXG4gKiBDYWxsYmFjayBmb3IgZmxhdHRlbkVhY2hcbiAqXG4gKiBAY2FsbGJhY2sgZmxhdHRlbkVhY2hDYWxsYmFja1xuICogQHBhcmFtIHtGZWF0dXJlfSBjdXJyZW50RmVhdHVyZSBUaGUgY3VycmVudCBmbGF0dGVuZWQgZmVhdHVyZSBiZWluZyBwcm9jZXNzZWQuXG4gKiBAcGFyYW0ge251bWJlcn0gZmVhdHVyZUluZGV4IFRoZSBpbmRleCBvZiB0aGUgY3VycmVudCBlbGVtZW50IGJlaW5nIHByb2Nlc3NlZCBpbiB0aGVcbiAqIGFycmF5LiBTdGFydHMgYXQgaW5kZXggMCwgaWYgYW4gaW5pdGlhbFZhbHVlIGlzIHByb3ZpZGVkLCBhbmQgYXQgaW5kZXggMSBvdGhlcndpc2UuXG4gKiBAcGFyYW0ge251bWJlcn0gZmVhdHVyZVN1YkluZGV4IFRoZSBzdWJpbmRleCBvZiB0aGUgY3VycmVudCBlbGVtZW50IGJlaW5nIHByb2Nlc3NlZCBpbiB0aGVcbiAqIGFycmF5LiBTdGFydHMgYXQgaW5kZXggMCBhbmQgaW5jcmVhc2VzIGlmIHRoZSBmbGF0dGVuZWQgZmVhdHVyZSB3YXMgYSBtdWx0aS1nZW9tZXRyeS5cbiAqL1xuXG4vKipcbiAqIEl0ZXJhdGUgb3ZlciBmbGF0dGVuZWQgZmVhdHVyZXMgaW4gYW55IEdlb0pTT04gb2JqZWN0LCBzaW1pbGFyIHRvXG4gKiBBcnJheS5mb3JFYWNoLlxuICpcbiAqIEBuYW1lIGZsYXR0ZW5FYWNoXG4gKiBAcGFyYW0ge0dlb21ldHJ5fEZlYXR1cmVDb2xsZWN0aW9ufEZlYXR1cmV9IGdlb2pzb24gYW55IEdlb0pTT04gb2JqZWN0XG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFjayBhIG1ldGhvZCB0aGF0IHRha2VzIChjdXJyZW50RmVhdHVyZSwgZmVhdHVyZUluZGV4LCBmZWF0dXJlU3ViSW5kZXgpXG4gKiBAZXhhbXBsZVxuICogdmFyIGZlYXR1cmVzID0gdHVyZi5mZWF0dXJlQ29sbGVjdGlvbihbXG4gKiAgICAgdHVyZi5wb2ludChbMjYsIDM3XSwge2ZvbzogJ2Jhcid9KSxcbiAqICAgICB0dXJmLm11bHRpUG9pbnQoW1s0MCwgMzBdLCBbMzYsIDUzXV0sIHtoZWxsbzogJ3dvcmxkJ30pXG4gKiBdKTtcbiAqXG4gKiB0dXJmLmZsYXR0ZW5FYWNoKGZlYXR1cmVzLCBmdW5jdGlvbiAoY3VycmVudEZlYXR1cmUsIGZlYXR1cmVJbmRleCwgZmVhdHVyZVN1YkluZGV4KSB7XG4gKiAgIC8vPWN1cnJlbnRGZWF0dXJlXG4gKiAgIC8vPWZlYXR1cmVJbmRleFxuICogICAvLz1mZWF0dXJlU3ViSW5kZXhcbiAqIH0pO1xuICovXG5mdW5jdGlvbiBmbGF0dGVuRWFjaChnZW9qc29uLCBjYWxsYmFjaykge1xuICAgIGdlb21FYWNoKGdlb2pzb24sIGZ1bmN0aW9uIChnZW9tZXRyeSwgZmVhdHVyZUluZGV4LCBwcm9wZXJ0aWVzKSB7XG4gICAgICAgIC8vIENhbGxiYWNrIGZvciBzaW5nbGUgZ2VvbWV0cnlcbiAgICAgICAgdmFyIHR5cGUgPSAoZ2VvbWV0cnkgPT09IG51bGwpID8gbnVsbCA6IGdlb21ldHJ5LnR5cGU7XG4gICAgICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgICBjYXNlIG51bGw6XG4gICAgICAgIGNhc2UgJ1BvaW50JzpcbiAgICAgICAgY2FzZSAnTGluZVN0cmluZyc6XG4gICAgICAgIGNhc2UgJ1BvbHlnb24nOlxuICAgICAgICAgICAgY2FsbGJhY2soZmVhdHVyZShnZW9tZXRyeSwgcHJvcGVydGllcyksIGZlYXR1cmVJbmRleCwgMCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgZ2VvbVR5cGU7XG5cbiAgICAgICAgLy8gQ2FsbGJhY2sgZm9yIG11bHRpLWdlb21ldHJ5XG4gICAgICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgICBjYXNlICdNdWx0aVBvaW50JzpcbiAgICAgICAgICAgIGdlb21UeXBlID0gJ1BvaW50JztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdNdWx0aUxpbmVTdHJpbmcnOlxuICAgICAgICAgICAgZ2VvbVR5cGUgPSAnTGluZVN0cmluZyc7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnTXVsdGlQb2x5Z29uJzpcbiAgICAgICAgICAgIGdlb21UeXBlID0gJ1BvbHlnb24nO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICBnZW9tZXRyeS5jb29yZGluYXRlcy5mb3JFYWNoKGZ1bmN0aW9uIChjb29yZGluYXRlLCBmZWF0dXJlU3ViSW5kZXgpIHtcbiAgICAgICAgICAgIHZhciBnZW9tID0ge1xuICAgICAgICAgICAgICAgIHR5cGU6IGdlb21UeXBlLFxuICAgICAgICAgICAgICAgIGNvb3JkaW5hdGVzOiBjb29yZGluYXRlXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgY2FsbGJhY2soZmVhdHVyZShnZW9tLCBwcm9wZXJ0aWVzKSwgZmVhdHVyZUluZGV4LCBmZWF0dXJlU3ViSW5kZXgpO1xuICAgICAgICB9KTtcblxuICAgIH0pO1xufVxuXG4vKipcbiAqIENhbGxiYWNrIGZvciBmbGF0dGVuUmVkdWNlXG4gKlxuICogVGhlIGZpcnN0IHRpbWUgdGhlIGNhbGxiYWNrIGZ1bmN0aW9uIGlzIGNhbGxlZCwgdGhlIHZhbHVlcyBwcm92aWRlZCBhcyBhcmd1bWVudHMgZGVwZW5kXG4gKiBvbiB3aGV0aGVyIHRoZSByZWR1Y2UgbWV0aG9kIGhhcyBhbiBpbml0aWFsVmFsdWUgYXJndW1lbnQuXG4gKlxuICogSWYgYW4gaW5pdGlhbFZhbHVlIGlzIHByb3ZpZGVkIHRvIHRoZSByZWR1Y2UgbWV0aG9kOlxuICogIC0gVGhlIHByZXZpb3VzVmFsdWUgYXJndW1lbnQgaXMgaW5pdGlhbFZhbHVlLlxuICogIC0gVGhlIGN1cnJlbnRWYWx1ZSBhcmd1bWVudCBpcyB0aGUgdmFsdWUgb2YgdGhlIGZpcnN0IGVsZW1lbnQgcHJlc2VudCBpbiB0aGUgYXJyYXkuXG4gKlxuICogSWYgYW4gaW5pdGlhbFZhbHVlIGlzIG5vdCBwcm92aWRlZDpcbiAqICAtIFRoZSBwcmV2aW91c1ZhbHVlIGFyZ3VtZW50IGlzIHRoZSB2YWx1ZSBvZiB0aGUgZmlyc3QgZWxlbWVudCBwcmVzZW50IGluIHRoZSBhcnJheS5cbiAqICAtIFRoZSBjdXJyZW50VmFsdWUgYXJndW1lbnQgaXMgdGhlIHZhbHVlIG9mIHRoZSBzZWNvbmQgZWxlbWVudCBwcmVzZW50IGluIHRoZSBhcnJheS5cbiAqXG4gKiBAY2FsbGJhY2sgZmxhdHRlblJlZHVjZUNhbGxiYWNrXG4gKiBAcGFyYW0geyp9IHByZXZpb3VzVmFsdWUgVGhlIGFjY3VtdWxhdGVkIHZhbHVlIHByZXZpb3VzbHkgcmV0dXJuZWQgaW4gdGhlIGxhc3QgaW52b2NhdGlvblxuICogb2YgdGhlIGNhbGxiYWNrLCBvciBpbml0aWFsVmFsdWUsIGlmIHN1cHBsaWVkLlxuICogQHBhcmFtIHtGZWF0dXJlfSBjdXJyZW50RmVhdHVyZSBUaGUgY3VycmVudCBGZWF0dXJlIGJlaW5nIHByb2Nlc3NlZC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBmZWF0dXJlSW5kZXggVGhlIGluZGV4IG9mIHRoZSBjdXJyZW50IGVsZW1lbnQgYmVpbmcgcHJvY2Vzc2VkIGluIHRoZVxuICogYXJyYXkuU3RhcnRzIGF0IGluZGV4IDAsIGlmIGFuIGluaXRpYWxWYWx1ZSBpcyBwcm92aWRlZCwgYW5kIGF0IGluZGV4IDEgb3RoZXJ3aXNlLlxuICogQHBhcmFtIHtudW1iZXJ9IGZlYXR1cmVTdWJJbmRleCBUaGUgc3ViaW5kZXggb2YgdGhlIGN1cnJlbnQgZWxlbWVudCBiZWluZyBwcm9jZXNzZWQgaW4gdGhlXG4gKiBhcnJheS4gU3RhcnRzIGF0IGluZGV4IDAgYW5kIGluY3JlYXNlcyBpZiB0aGUgZmxhdHRlbmVkIGZlYXR1cmUgd2FzIGEgbXVsdGktZ2VvbWV0cnkuXG4gKi9cblxuLyoqXG4gKiBSZWR1Y2UgZmxhdHRlbmVkIGZlYXR1cmVzIGluIGFueSBHZW9KU09OIG9iamVjdCwgc2ltaWxhciB0byBBcnJheS5yZWR1Y2UoKS5cbiAqXG4gKiBAbmFtZSBmbGF0dGVuUmVkdWNlXG4gKiBAcGFyYW0ge0dlb21ldHJ5fEZlYXR1cmVDb2xsZWN0aW9ufEZlYXR1cmV9IGdlb2pzb24gYW55IEdlb0pTT04gb2JqZWN0XG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFjayBhIG1ldGhvZCB0aGF0IHRha2VzIChwcmV2aW91c1ZhbHVlLCBjdXJyZW50RmVhdHVyZSwgZmVhdHVyZUluZGV4LCBmZWF0dXJlU3ViSW5kZXgpXG4gKiBAcGFyYW0geyp9IFtpbml0aWFsVmFsdWVdIFZhbHVlIHRvIHVzZSBhcyB0aGUgZmlyc3QgYXJndW1lbnQgdG8gdGhlIGZpcnN0IGNhbGwgb2YgdGhlIGNhbGxiYWNrLlxuICogQHJldHVybnMgeyp9IFRoZSB2YWx1ZSB0aGF0IHJlc3VsdHMgZnJvbSB0aGUgcmVkdWN0aW9uLlxuICogQGV4YW1wbGVcbiAqIHZhciBmZWF0dXJlcyA9IHR1cmYuZmVhdHVyZUNvbGxlY3Rpb24oW1xuICogICAgIHR1cmYucG9pbnQoWzI2LCAzN10sIHtmb286ICdiYXInfSksXG4gKiAgICAgdHVyZi5tdWx0aVBvaW50KFtbNDAsIDMwXSwgWzM2LCA1M11dLCB7aGVsbG86ICd3b3JsZCd9KVxuICogXSk7XG4gKlxuICogdHVyZi5mbGF0dGVuUmVkdWNlKGZlYXR1cmVzLCBmdW5jdGlvbiAocHJldmlvdXNWYWx1ZSwgY3VycmVudEZlYXR1cmUsIGZlYXR1cmVJbmRleCwgZmVhdHVyZVN1YkluZGV4KSB7XG4gKiAgIC8vPXByZXZpb3VzVmFsdWVcbiAqICAgLy89Y3VycmVudEZlYXR1cmVcbiAqICAgLy89ZmVhdHVyZUluZGV4XG4gKiAgIC8vPWZlYXR1cmVTdWJJbmRleFxuICogICByZXR1cm4gY3VycmVudEZlYXR1cmVcbiAqIH0pO1xuICovXG5mdW5jdGlvbiBmbGF0dGVuUmVkdWNlKGdlb2pzb24sIGNhbGxiYWNrLCBpbml0aWFsVmFsdWUpIHtcbiAgICB2YXIgcHJldmlvdXNWYWx1ZSA9IGluaXRpYWxWYWx1ZTtcbiAgICBmbGF0dGVuRWFjaChnZW9qc29uLCBmdW5jdGlvbiAoY3VycmVudEZlYXR1cmUsIGZlYXR1cmVJbmRleCwgZmVhdHVyZVN1YkluZGV4KSB7XG4gICAgICAgIGlmIChmZWF0dXJlSW5kZXggPT09IDAgJiYgZmVhdHVyZVN1YkluZGV4ID09PSAwICYmIGluaXRpYWxWYWx1ZSA9PT0gdW5kZWZpbmVkKSBwcmV2aW91c1ZhbHVlID0gY3VycmVudEZlYXR1cmU7XG4gICAgICAgIGVsc2UgcHJldmlvdXNWYWx1ZSA9IGNhbGxiYWNrKHByZXZpb3VzVmFsdWUsIGN1cnJlbnRGZWF0dXJlLCBmZWF0dXJlSW5kZXgsIGZlYXR1cmVTdWJJbmRleCk7XG4gICAgfSk7XG4gICAgcmV0dXJuIHByZXZpb3VzVmFsdWU7XG59XG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHNlZ21lbnRFYWNoXG4gKlxuICogQGNhbGxiYWNrIHNlZ21lbnRFYWNoQ2FsbGJhY2tcbiAqIEBwYXJhbSB7RmVhdHVyZTxMaW5lU3RyaW5nPn0gY3VycmVudFNlZ21lbnQgVGhlIGN1cnJlbnQgc2VnbWVudCBiZWluZyBwcm9jZXNzZWQuXG4gKiBAcGFyYW0ge251bWJlcn0gZmVhdHVyZUluZGV4IFRoZSBpbmRleCBvZiB0aGUgY3VycmVudCBlbGVtZW50IGJlaW5nIHByb2Nlc3NlZCBpbiB0aGUgYXJyYXksIHN0YXJ0cyBhdCBpbmRleCAwLlxuICogQHBhcmFtIHtudW1iZXJ9IGZlYXR1cmVTdWJJbmRleCBUaGUgc3ViaW5kZXggb2YgdGhlIGN1cnJlbnQgZWxlbWVudCBiZWluZyBwcm9jZXNzZWQgaW4gdGhlXG4gKiBhcnJheS4gU3RhcnRzIGF0IGluZGV4IDAgYW5kIGluY3JlYXNlcyBmb3IgZWFjaCBpdGVyYXRpbmcgbGluZSBzZWdtZW50LlxuICogQHJldHVybnMge3ZvaWR9XG4gKi9cblxuLyoqXG4gKiBJdGVyYXRlIG92ZXIgMi12ZXJ0ZXggbGluZSBzZWdtZW50IGluIGFueSBHZW9KU09OIG9iamVjdCwgc2ltaWxhciB0byBBcnJheS5mb3JFYWNoKClcbiAqIChNdWx0aSlQb2ludCBnZW9tZXRyaWVzIGRvIG5vdCBjb250YWluIHNlZ21lbnRzIHRoZXJlZm9yZSB0aGV5IGFyZSBpZ25vcmVkIGR1cmluZyB0aGlzIG9wZXJhdGlvbi5cbiAqXG4gKiBAcGFyYW0ge0ZlYXR1cmVDb2xsZWN0aW9ufEZlYXR1cmV8R2VvbWV0cnl9IGdlb2pzb24gYW55IEdlb0pTT05cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGNhbGxiYWNrIGEgbWV0aG9kIHRoYXQgdGFrZXMgKGN1cnJlbnRTZWdtZW50LCBmZWF0dXJlSW5kZXgsIGZlYXR1cmVTdWJJbmRleClcbiAqIEByZXR1cm5zIHt2b2lkfVxuICogQGV4YW1wbGVcbiAqIHZhciBwb2x5Z29uID0gdHVyZi5wb2x5Z29uKFtbWy01MCwgNV0sIFstNDAsIC0xMF0sIFstNTAsIC0xMF0sIFstNDAsIDVdLCBbLTUwLCA1XV1dKTtcbiAqXG4gKiAvLyBJdGVyYXRlIG92ZXIgR2VvSlNPTiBieSAyLXZlcnRleCBzZWdtZW50c1xuICogdHVyZi5zZWdtZW50RWFjaChwb2x5Z29uLCBmdW5jdGlvbiAoY3VycmVudFNlZ21lbnQsIGZlYXR1cmVJbmRleCwgZmVhdHVyZVN1YkluZGV4KSB7XG4gKiAgIC8vPSBjdXJyZW50U2VnbWVudFxuICogICAvLz0gZmVhdHVyZUluZGV4XG4gKiAgIC8vPSBmZWF0dXJlU3ViSW5kZXhcbiAqIH0pO1xuICpcbiAqIC8vIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIHNlZ21lbnRzXG4gKiB2YXIgdG90YWwgPSAwO1xuICogdmFyIGluaXRpYWxWYWx1ZSA9IDA7XG4gKiB0dXJmLnNlZ21lbnRFYWNoKHBvbHlnb24sIGZ1bmN0aW9uICgpIHtcbiAqICAgICB0b3RhbCsrO1xuICogfSwgaW5pdGlhbFZhbHVlKTtcbiAqL1xuZnVuY3Rpb24gc2VnbWVudEVhY2goZ2VvanNvbiwgY2FsbGJhY2spIHtcbiAgICBmbGF0dGVuRWFjaChnZW9qc29uLCBmdW5jdGlvbiAoZmVhdHVyZSwgZmVhdHVyZUluZGV4KSB7XG4gICAgICAgIHZhciBmZWF0dXJlU3ViSW5kZXggPSAwO1xuICAgICAgICAvLyBFeGNsdWRlIG51bGwgR2VvbWV0cmllc1xuICAgICAgICBpZiAoIWZlYXR1cmUuZ2VvbWV0cnkpIHJldHVybjtcbiAgICAgICAgLy8gKE11bHRpKVBvaW50IGdlb21ldHJpZXMgZG8gbm90IGNvbnRhaW4gc2VnbWVudHMgdGhlcmVmb3JlIHRoZXkgYXJlIGlnbm9yZWQgZHVyaW5nIHRoaXMgb3BlcmF0aW9uLlxuICAgICAgICB2YXIgdHlwZSA9IGZlYXR1cmUuZ2VvbWV0cnkudHlwZTtcbiAgICAgICAgaWYgKHR5cGUgPT09ICdQb2ludCcgfHwgdHlwZSA9PT0gJ011bHRpUG9pbnQnKSByZXR1cm47XG5cbiAgICAgICAgLy8gR2VuZXJhdGUgMi12ZXJ0ZXggbGluZSBzZWdtZW50c1xuICAgICAgICBjb29yZFJlZHVjZShmZWF0dXJlLCBmdW5jdGlvbiAocHJldmlvdXNDb29yZHMsIGN1cnJlbnRDb29yZCkge1xuICAgICAgICAgICAgdmFyIGN1cnJlbnRTZWdtZW50ID0gbGluZVN0cmluZyhbcHJldmlvdXNDb29yZHMsIGN1cnJlbnRDb29yZF0sIGZlYXR1cmUucHJvcGVydGllcyk7XG4gICAgICAgICAgICBjYWxsYmFjayhjdXJyZW50U2VnbWVudCwgZmVhdHVyZUluZGV4LCBmZWF0dXJlU3ViSW5kZXgpO1xuICAgICAgICAgICAgZmVhdHVyZVN1YkluZGV4Kys7XG4gICAgICAgICAgICByZXR1cm4gY3VycmVudENvb3JkO1xuICAgICAgICB9KTtcbiAgICB9KTtcbn1cblxuLyoqXG4gKiBDYWxsYmFjayBmb3Igc2VnbWVudFJlZHVjZVxuICpcbiAqIFRoZSBmaXJzdCB0aW1lIHRoZSBjYWxsYmFjayBmdW5jdGlvbiBpcyBjYWxsZWQsIHRoZSB2YWx1ZXMgcHJvdmlkZWQgYXMgYXJndW1lbnRzIGRlcGVuZFxuICogb24gd2hldGhlciB0aGUgcmVkdWNlIG1ldGhvZCBoYXMgYW4gaW5pdGlhbFZhbHVlIGFyZ3VtZW50LlxuICpcbiAqIElmIGFuIGluaXRpYWxWYWx1ZSBpcyBwcm92aWRlZCB0byB0aGUgcmVkdWNlIG1ldGhvZDpcbiAqICAtIFRoZSBwcmV2aW91c1ZhbHVlIGFyZ3VtZW50IGlzIGluaXRpYWxWYWx1ZS5cbiAqICAtIFRoZSBjdXJyZW50VmFsdWUgYXJndW1lbnQgaXMgdGhlIHZhbHVlIG9mIHRoZSBmaXJzdCBlbGVtZW50IHByZXNlbnQgaW4gdGhlIGFycmF5LlxuICpcbiAqIElmIGFuIGluaXRpYWxWYWx1ZSBpcyBub3QgcHJvdmlkZWQ6XG4gKiAgLSBUaGUgcHJldmlvdXNWYWx1ZSBhcmd1bWVudCBpcyB0aGUgdmFsdWUgb2YgdGhlIGZpcnN0IGVsZW1lbnQgcHJlc2VudCBpbiB0aGUgYXJyYXkuXG4gKiAgLSBUaGUgY3VycmVudFZhbHVlIGFyZ3VtZW50IGlzIHRoZSB2YWx1ZSBvZiB0aGUgc2Vjb25kIGVsZW1lbnQgcHJlc2VudCBpbiB0aGUgYXJyYXkuXG4gKlxuICogQGNhbGxiYWNrIHNlZ21lbnRSZWR1Y2VDYWxsYmFja1xuICogQHBhcmFtIHsqfSBbcHJldmlvdXNWYWx1ZV0gVGhlIGFjY3VtdWxhdGVkIHZhbHVlIHByZXZpb3VzbHkgcmV0dXJuZWQgaW4gdGhlIGxhc3QgaW52b2NhdGlvblxuICogb2YgdGhlIGNhbGxiYWNrLCBvciBpbml0aWFsVmFsdWUsIGlmIHN1cHBsaWVkLlxuICogQHBhcmFtIHtGZWF0dXJlPExpbmVTdHJpbmc+fSBbY3VycmVudFNlZ21lbnRdIFRoZSBjdXJyZW50IHNlZ21lbnQgYmVpbmcgcHJvY2Vzc2VkLlxuICogQHBhcmFtIHtudW1iZXJ9IFtjdXJyZW50SW5kZXhdIFRoZSBpbmRleCBvZiB0aGUgY3VycmVudCBlbGVtZW50IGJlaW5nIHByb2Nlc3NlZCBpbiB0aGVcbiAqIGFycmF5LiBTdGFydHMgYXQgaW5kZXggMCwgaWYgYW4gaW5pdGlhbFZhbHVlIGlzIHByb3ZpZGVkLCBhbmQgYXQgaW5kZXggMSBvdGhlcndpc2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW2N1cnJlbnRTdWJJbmRleF0gVGhlIHN1YmluZGV4IG9mIHRoZSBjdXJyZW50IGVsZW1lbnQgYmVpbmcgcHJvY2Vzc2VkIGluIHRoZVxuICogYXJyYXkuIFN0YXJ0cyBhdCBpbmRleCAwIGFuZCBpbmNyZWFzZXMgZm9yIGVhY2ggaXRlcmF0aW5nIGxpbmUgc2VnbWVudC5cbiAqL1xuXG4vKipcbiAqIFJlZHVjZSAyLXZlcnRleCBsaW5lIHNlZ21lbnQgaW4gYW55IEdlb0pTT04gb2JqZWN0LCBzaW1pbGFyIHRvIEFycmF5LnJlZHVjZSgpXG4gKiAoTXVsdGkpUG9pbnQgZ2VvbWV0cmllcyBkbyBub3QgY29udGFpbiBzZWdtZW50cyB0aGVyZWZvcmUgdGhleSBhcmUgaWdub3JlZCBkdXJpbmcgdGhpcyBvcGVyYXRpb24uXG4gKlxuICogQHBhcmFtIHtGZWF0dXJlQ29sbGVjdGlvbnxGZWF0dXJlfEdlb21ldHJ5fSBnZW9qc29uIGFueSBHZW9KU09OXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFjayBhIG1ldGhvZCB0aGF0IHRha2VzIChwcmV2aW91c1ZhbHVlLCBjdXJyZW50U2VnbWVudCwgY3VycmVudEluZGV4KVxuICogQHBhcmFtIHsqfSBbaW5pdGlhbFZhbHVlXSBWYWx1ZSB0byB1c2UgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IHRvIHRoZSBmaXJzdCBjYWxsIG9mIHRoZSBjYWxsYmFjay5cbiAqIEByZXR1cm5zIHt2b2lkfVxuICogQGV4YW1wbGVcbiAqIHZhciBwb2x5Z29uID0gdHVyZi5wb2x5Z29uKFtbWy01MCwgNV0sIFstNDAsIC0xMF0sIFstNTAsIC0xMF0sIFstNDAsIDVdLCBbLTUwLCA1XV1dKTtcbiAqXG4gKiAvLyBJdGVyYXRlIG92ZXIgR2VvSlNPTiBieSAyLXZlcnRleCBzZWdtZW50c1xuICogdHVyZi5zZWdtZW50UmVkdWNlKHBvbHlnb24sIGZ1bmN0aW9uIChwcmV2aW91c1NlZ21lbnQsIGN1cnJlbnRTZWdtZW50LCBjdXJyZW50SW5kZXgsIGN1cnJlbnRTdWJJbmRleCkge1xuICogICAvLz0gcHJldmlvdXNTZWdtZW50XG4gKiAgIC8vPSBjdXJyZW50U2VnbWVudFxuICogICAvLz0gY3VycmVudEluZGV4XG4gKiAgIC8vPSBjdXJyZW50U3ViSW5kZXhcbiAqICAgcmV0dXJuIGN1cnJlbnRTZWdtZW50XG4gKiB9KTtcbiAqXG4gKiAvLyBDYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBzZWdtZW50c1xuICogdmFyIGluaXRpYWxWYWx1ZSA9IDBcbiAqIHZhciB0b3RhbCA9IHR1cmYuc2VnbWVudFJlZHVjZShwb2x5Z29uLCBmdW5jdGlvbiAocHJldmlvdXNWYWx1ZSkge1xuICogICAgIHByZXZpb3VzVmFsdWUrKztcbiAqICAgICByZXR1cm4gcHJldmlvdXNWYWx1ZTtcbiAqIH0sIGluaXRpYWxWYWx1ZSk7XG4gKi9cbmZ1bmN0aW9uIHNlZ21lbnRSZWR1Y2UoZ2VvanNvbiwgY2FsbGJhY2ssIGluaXRpYWxWYWx1ZSkge1xuICAgIHZhciBwcmV2aW91c1ZhbHVlID0gaW5pdGlhbFZhbHVlO1xuICAgIHNlZ21lbnRFYWNoKGdlb2pzb24sIGZ1bmN0aW9uIChjdXJyZW50U2VnbWVudCwgY3VycmVudEluZGV4LCBjdXJyZW50U3ViSW5kZXgpIHtcbiAgICAgICAgaWYgKGN1cnJlbnRJbmRleCA9PT0gMCAmJiBpbml0aWFsVmFsdWUgPT09IHVuZGVmaW5lZCkgcHJldmlvdXNWYWx1ZSA9IGN1cnJlbnRTZWdtZW50O1xuICAgICAgICBlbHNlIHByZXZpb3VzVmFsdWUgPSBjYWxsYmFjayhwcmV2aW91c1ZhbHVlLCBjdXJyZW50U2VnbWVudCwgY3VycmVudEluZGV4LCBjdXJyZW50U3ViSW5kZXgpO1xuICAgIH0pO1xuICAgIHJldHVybiBwcmV2aW91c1ZhbHVlO1xufVxuXG4vKipcbiAqIENyZWF0ZSBGZWF0dXJlXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7R2VvbWV0cnl9IGdlb21ldHJ5IEdlb0pTT04gR2VvbWV0cnlcbiAqIEBwYXJhbSB7T2JqZWN0fSBwcm9wZXJ0aWVzIFByb3BlcnRpZXNcbiAqIEByZXR1cm5zIHtGZWF0dXJlfSBHZW9KU09OIEZlYXR1cmVcbiAqL1xuZnVuY3Rpb24gZmVhdHVyZShnZW9tZXRyeSwgcHJvcGVydGllcykge1xuICAgIGlmIChnZW9tZXRyeSA9PT0gdW5kZWZpbmVkKSB0aHJvdyBuZXcgRXJyb3IoJ05vIGdlb21ldHJ5IHBhc3NlZCcpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgICAgdHlwZTogJ0ZlYXR1cmUnLFxuICAgICAgICBwcm9wZXJ0aWVzOiBwcm9wZXJ0aWVzIHx8IHt9LFxuICAgICAgICBnZW9tZXRyeTogZ2VvbWV0cnlcbiAgICB9O1xufVxuXG4vKipcbiAqIENyZWF0ZSBMaW5lU3RyaW5nXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7QXJyYXk8QXJyYXk8bnVtYmVyPj59IGNvb3JkaW5hdGVzIExpbmUgQ29vcmRpbmF0ZXNcbiAqIEBwYXJhbSB7T2JqZWN0fSBwcm9wZXJ0aWVzIFByb3BlcnRpZXNcbiAqIEByZXR1cm5zIHtGZWF0dXJlPExpbmVTdHJpbmc+fSBHZW9KU09OIExpbmVTdHJpbmcgRmVhdHVyZVxuICovXG5mdW5jdGlvbiBsaW5lU3RyaW5nKGNvb3JkaW5hdGVzLCBwcm9wZXJ0aWVzKSB7XG4gICAgaWYgKCFjb29yZGluYXRlcykgdGhyb3cgbmV3IEVycm9yKCdObyBjb29yZGluYXRlcyBwYXNzZWQnKTtcbiAgICBpZiAoY29vcmRpbmF0ZXMubGVuZ3RoIDwgMikgdGhyb3cgbmV3IEVycm9yKCdDb29yZGluYXRlcyBtdXN0IGJlIGFuIGFycmF5IG9mIHR3byBvciBtb3JlIHBvc2l0aW9ucycpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgICAgdHlwZTogJ0ZlYXR1cmUnLFxuICAgICAgICBwcm9wZXJ0aWVzOiBwcm9wZXJ0aWVzIHx8IHt9LFxuICAgICAgICBnZW9tZXRyeToge1xuICAgICAgICAgICAgdHlwZTogJ0xpbmVTdHJpbmcnLFxuICAgICAgICAgICAgY29vcmRpbmF0ZXM6IGNvb3JkaW5hdGVzXG4gICAgICAgIH1cbiAgICB9O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBjb29yZEVhY2g6IGNvb3JkRWFjaCxcbiAgICBjb29yZFJlZHVjZTogY29vcmRSZWR1Y2UsXG4gICAgcHJvcEVhY2g6IHByb3BFYWNoLFxuICAgIHByb3BSZWR1Y2U6IHByb3BSZWR1Y2UsXG4gICAgZmVhdHVyZUVhY2g6IGZlYXR1cmVFYWNoLFxuICAgIGZlYXR1cmVSZWR1Y2U6IGZlYXR1cmVSZWR1Y2UsXG4gICAgY29vcmRBbGw6IGNvb3JkQWxsLFxuICAgIGdlb21FYWNoOiBnZW9tRWFjaCxcbiAgICBnZW9tUmVkdWNlOiBnZW9tUmVkdWNlLFxuICAgIGZsYXR0ZW5FYWNoOiBmbGF0dGVuRWFjaCxcbiAgICBmbGF0dGVuUmVkdWNlOiBmbGF0dGVuUmVkdWNlLFxuICAgIHNlZ21lbnRFYWNoOiBzZWdtZW50RWFjaCxcbiAgICBzZWdtZW50UmVkdWNlOiBzZWdtZW50UmVkdWNlXG59O1xuIiwiZXhwb3J0cy5yZWFkID0gZnVuY3Rpb24gKGJ1ZmZlciwgb2Zmc2V0LCBpc0xFLCBtTGVuLCBuQnl0ZXMpIHtcbiAgdmFyIGUsIG1cbiAgdmFyIGVMZW4gPSBuQnl0ZXMgKiA4IC0gbUxlbiAtIDFcbiAgdmFyIGVNYXggPSAoMSA8PCBlTGVuKSAtIDFcbiAgdmFyIGVCaWFzID0gZU1heCA+PiAxXG4gIHZhciBuQml0cyA9IC03XG4gIHZhciBpID0gaXNMRSA/IChuQnl0ZXMgLSAxKSA6IDBcbiAgdmFyIGQgPSBpc0xFID8gLTEgOiAxXG4gIHZhciBzID0gYnVmZmVyW29mZnNldCArIGldXG5cbiAgaSArPSBkXG5cbiAgZSA9IHMgJiAoKDEgPDwgKC1uQml0cykpIC0gMSlcbiAgcyA+Pj0gKC1uQml0cylcbiAgbkJpdHMgKz0gZUxlblxuICBmb3IgKDsgbkJpdHMgPiAwOyBlID0gZSAqIDI1NiArIGJ1ZmZlcltvZmZzZXQgKyBpXSwgaSArPSBkLCBuQml0cyAtPSA4KSB7fVxuXG4gIG0gPSBlICYgKCgxIDw8ICgtbkJpdHMpKSAtIDEpXG4gIGUgPj49ICgtbkJpdHMpXG4gIG5CaXRzICs9IG1MZW5cbiAgZm9yICg7IG5CaXRzID4gMDsgbSA9IG0gKiAyNTYgKyBidWZmZXJbb2Zmc2V0ICsgaV0sIGkgKz0gZCwgbkJpdHMgLT0gOCkge31cblxuICBpZiAoZSA9PT0gMCkge1xuICAgIGUgPSAxIC0gZUJpYXNcbiAgfSBlbHNlIGlmIChlID09PSBlTWF4KSB7XG4gICAgcmV0dXJuIG0gPyBOYU4gOiAoKHMgPyAtMSA6IDEpICogSW5maW5pdHkpXG4gIH0gZWxzZSB7XG4gICAgbSA9IG0gKyBNYXRoLnBvdygyLCBtTGVuKVxuICAgIGUgPSBlIC0gZUJpYXNcbiAgfVxuICByZXR1cm4gKHMgPyAtMSA6IDEpICogbSAqIE1hdGgucG93KDIsIGUgLSBtTGVuKVxufVxuXG5leHBvcnRzLndyaXRlID0gZnVuY3Rpb24gKGJ1ZmZlciwgdmFsdWUsIG9mZnNldCwgaXNMRSwgbUxlbiwgbkJ5dGVzKSB7XG4gIHZhciBlLCBtLCBjXG4gIHZhciBlTGVuID0gbkJ5dGVzICogOCAtIG1MZW4gLSAxXG4gIHZhciBlTWF4ID0gKDEgPDwgZUxlbikgLSAxXG4gIHZhciBlQmlhcyA9IGVNYXggPj4gMVxuICB2YXIgcnQgPSAobUxlbiA9PT0gMjMgPyBNYXRoLnBvdygyLCAtMjQpIC0gTWF0aC5wb3coMiwgLTc3KSA6IDApXG4gIHZhciBpID0gaXNMRSA/IDAgOiAobkJ5dGVzIC0gMSlcbiAgdmFyIGQgPSBpc0xFID8gMSA6IC0xXG4gIHZhciBzID0gdmFsdWUgPCAwIHx8ICh2YWx1ZSA9PT0gMCAmJiAxIC8gdmFsdWUgPCAwKSA/IDEgOiAwXG5cbiAgdmFsdWUgPSBNYXRoLmFicyh2YWx1ZSlcblxuICBpZiAoaXNOYU4odmFsdWUpIHx8IHZhbHVlID09PSBJbmZpbml0eSkge1xuICAgIG0gPSBpc05hTih2YWx1ZSkgPyAxIDogMFxuICAgIGUgPSBlTWF4XG4gIH0gZWxzZSB7XG4gICAgZSA9IE1hdGguZmxvb3IoTWF0aC5sb2codmFsdWUpIC8gTWF0aC5MTjIpXG4gICAgaWYgKHZhbHVlICogKGMgPSBNYXRoLnBvdygyLCAtZSkpIDwgMSkge1xuICAgICAgZS0tXG4gICAgICBjICo9IDJcbiAgICB9XG4gICAgaWYgKGUgKyBlQmlhcyA+PSAxKSB7XG4gICAgICB2YWx1ZSArPSBydCAvIGNcbiAgICB9IGVsc2Uge1xuICAgICAgdmFsdWUgKz0gcnQgKiBNYXRoLnBvdygyLCAxIC0gZUJpYXMpXG4gICAgfVxuICAgIGlmICh2YWx1ZSAqIGMgPj0gMikge1xuICAgICAgZSsrXG4gICAgICBjIC89IDJcbiAgICB9XG5cbiAgICBpZiAoZSArIGVCaWFzID49IGVNYXgpIHtcbiAgICAgIG0gPSAwXG4gICAgICBlID0gZU1heFxuICAgIH0gZWxzZSBpZiAoZSArIGVCaWFzID49IDEpIHtcbiAgICAgIG0gPSAodmFsdWUgKiBjIC0gMSkgKiBNYXRoLnBvdygyLCBtTGVuKVxuICAgICAgZSA9IGUgKyBlQmlhc1xuICAgIH0gZWxzZSB7XG4gICAgICBtID0gdmFsdWUgKiBNYXRoLnBvdygyLCBlQmlhcyAtIDEpICogTWF0aC5wb3coMiwgbUxlbilcbiAgICAgIGUgPSAwXG4gICAgfVxuICB9XG5cbiAgZm9yICg7IG1MZW4gPj0gODsgYnVmZmVyW29mZnNldCArIGldID0gbSAmIDB4ZmYsIGkgKz0gZCwgbSAvPSAyNTYsIG1MZW4gLT0gOCkge31cblxuICBlID0gKGUgPDwgbUxlbikgfCBtXG4gIGVMZW4gKz0gbUxlblxuICBmb3IgKDsgZUxlbiA+IDA7IGJ1ZmZlcltvZmZzZXQgKyBpXSA9IGUgJiAweGZmLCBpICs9IGQsIGUgLz0gMjU2LCBlTGVuIC09IDgpIHt9XG5cbiAgYnVmZmVyW29mZnNldCArIGkgLSBkXSB8PSBzICogMTI4XG59XG4iLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0gUGJmO1xuXG52YXIgaWVlZTc1NCA9IHJlcXVpcmUoJ2llZWU3NTQnKTtcblxuZnVuY3Rpb24gUGJmKGJ1Zikge1xuICAgIHRoaXMuYnVmID0gQXJyYXlCdWZmZXIuaXNWaWV3ICYmIEFycmF5QnVmZmVyLmlzVmlldyhidWYpID8gYnVmIDogbmV3IFVpbnQ4QXJyYXkoYnVmIHx8IDApO1xuICAgIHRoaXMucG9zID0gMDtcbiAgICB0aGlzLnR5cGUgPSAwO1xuICAgIHRoaXMubGVuZ3RoID0gdGhpcy5idWYubGVuZ3RoO1xufVxuXG5QYmYuVmFyaW50ICA9IDA7IC8vIHZhcmludDogaW50MzIsIGludDY0LCB1aW50MzIsIHVpbnQ2NCwgc2ludDMyLCBzaW50NjQsIGJvb2wsIGVudW1cblBiZi5GaXhlZDY0ID0gMTsgLy8gNjQtYml0OiBkb3VibGUsIGZpeGVkNjQsIHNmaXhlZDY0XG5QYmYuQnl0ZXMgICA9IDI7IC8vIGxlbmd0aC1kZWxpbWl0ZWQ6IHN0cmluZywgYnl0ZXMsIGVtYmVkZGVkIG1lc3NhZ2VzLCBwYWNrZWQgcmVwZWF0ZWQgZmllbGRzXG5QYmYuRml4ZWQzMiA9IDU7IC8vIDMyLWJpdDogZmxvYXQsIGZpeGVkMzIsIHNmaXhlZDMyXG5cbnZhciBTSElGVF9MRUZUXzMyID0gKDEgPDwgMTYpICogKDEgPDwgMTYpLFxuICAgIFNISUZUX1JJR0hUXzMyID0gMSAvIFNISUZUX0xFRlRfMzI7XG5cblBiZi5wcm90b3R5cGUgPSB7XG5cbiAgICBkZXN0cm95OiBmdW5jdGlvbigpIHtcbiAgICAgICAgdGhpcy5idWYgPSBudWxsO1xuICAgIH0sXG5cbiAgICAvLyA9PT0gUkVBRElORyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gICAgcmVhZEZpZWxkczogZnVuY3Rpb24ocmVhZEZpZWxkLCByZXN1bHQsIGVuZCkge1xuICAgICAgICBlbmQgPSBlbmQgfHwgdGhpcy5sZW5ndGg7XG5cbiAgICAgICAgd2hpbGUgKHRoaXMucG9zIDwgZW5kKSB7XG4gICAgICAgICAgICB2YXIgdmFsID0gdGhpcy5yZWFkVmFyaW50KCksXG4gICAgICAgICAgICAgICAgdGFnID0gdmFsID4+IDMsXG4gICAgICAgICAgICAgICAgc3RhcnRQb3MgPSB0aGlzLnBvcztcblxuICAgICAgICAgICAgdGhpcy50eXBlID0gdmFsICYgMHg3O1xuICAgICAgICAgICAgcmVhZEZpZWxkKHRhZywgcmVzdWx0LCB0aGlzKTtcblxuICAgICAgICAgICAgaWYgKHRoaXMucG9zID09PSBzdGFydFBvcykgdGhpcy5za2lwKHZhbCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9LFxuXG4gICAgcmVhZE1lc3NhZ2U6IGZ1bmN0aW9uKHJlYWRGaWVsZCwgcmVzdWx0KSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJlYWRGaWVsZHMocmVhZEZpZWxkLCByZXN1bHQsIHRoaXMucmVhZFZhcmludCgpICsgdGhpcy5wb3MpO1xuICAgIH0sXG5cbiAgICByZWFkRml4ZWQzMjogZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB2YWwgPSByZWFkVUludDMyKHRoaXMuYnVmLCB0aGlzLnBvcyk7XG4gICAgICAgIHRoaXMucG9zICs9IDQ7XG4gICAgICAgIHJldHVybiB2YWw7XG4gICAgfSxcblxuICAgIHJlYWRTRml4ZWQzMjogZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB2YWwgPSByZWFkSW50MzIodGhpcy5idWYsIHRoaXMucG9zKTtcbiAgICAgICAgdGhpcy5wb3MgKz0gNDtcbiAgICAgICAgcmV0dXJuIHZhbDtcbiAgICB9LFxuXG4gICAgLy8gNjQtYml0IGludCBoYW5kbGluZyBpcyBiYXNlZCBvbiBnaXRodWIuY29tL2Rwdy9ub2RlLWJ1ZmZlci1tb3JlLWludHMgKE1JVC1saWNlbnNlZClcblxuICAgIHJlYWRGaXhlZDY0OiBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHZhbCA9IHJlYWRVSW50MzIodGhpcy5idWYsIHRoaXMucG9zKSArIHJlYWRVSW50MzIodGhpcy5idWYsIHRoaXMucG9zICsgNCkgKiBTSElGVF9MRUZUXzMyO1xuICAgICAgICB0aGlzLnBvcyArPSA4O1xuICAgICAgICByZXR1cm4gdmFsO1xuICAgIH0sXG5cbiAgICByZWFkU0ZpeGVkNjQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgdmFsID0gcmVhZFVJbnQzMih0aGlzLmJ1ZiwgdGhpcy5wb3MpICsgcmVhZEludDMyKHRoaXMuYnVmLCB0aGlzLnBvcyArIDQpICogU0hJRlRfTEVGVF8zMjtcbiAgICAgICAgdGhpcy5wb3MgKz0gODtcbiAgICAgICAgcmV0dXJuIHZhbDtcbiAgICB9LFxuXG4gICAgcmVhZEZsb2F0OiBmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHZhbCA9IGllZWU3NTQucmVhZCh0aGlzLmJ1ZiwgdGhpcy5wb3MsIHRydWUsIDIzLCA0KTtcbiAgICAgICAgdGhpcy5wb3MgKz0gNDtcbiAgICAgICAgcmV0dXJuIHZhbDtcbiAgICB9LFxuXG4gICAgcmVhZERvdWJsZTogZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciB2YWwgPSBpZWVlNzU0LnJlYWQodGhpcy5idWYsIHRoaXMucG9zLCB0cnVlLCA1MiwgOCk7XG4gICAgICAgIHRoaXMucG9zICs9IDg7XG4gICAgICAgIHJldHVybiB2YWw7XG4gICAgfSxcblxuICAgIHJlYWRWYXJpbnQ6IGZ1bmN0aW9uKGlzU2lnbmVkKSB7XG4gICAgICAgIHZhciBidWYgPSB0aGlzLmJ1ZixcbiAgICAgICAgICAgIHZhbCwgYjtcblxuICAgICAgICBiID0gYnVmW3RoaXMucG9zKytdOyB2YWwgID0gIGIgJiAweDdmOyAgICAgICAgaWYgKGIgPCAweDgwKSByZXR1cm4gdmFsO1xuICAgICAgICBiID0gYnVmW3RoaXMucG9zKytdOyB2YWwgfD0gKGIgJiAweDdmKSA8PCA3OyAgaWYgKGIgPCAweDgwKSByZXR1cm4gdmFsO1xuICAgICAgICBiID0gYnVmW3RoaXMucG9zKytdOyB2YWwgfD0gKGIgJiAweDdmKSA8PCAxNDsgaWYgKGIgPCAweDgwKSByZXR1cm4gdmFsO1xuICAgICAgICBiID0gYnVmW3RoaXMucG9zKytdOyB2YWwgfD0gKGIgJiAweDdmKSA8PCAyMTsgaWYgKGIgPCAweDgwKSByZXR1cm4gdmFsO1xuICAgICAgICBiID0gYnVmW3RoaXMucG9zXTsgICB2YWwgfD0gKGIgJiAweDBmKSA8PCAyODtcblxuICAgICAgICByZXR1cm4gcmVhZFZhcmludFJlbWFpbmRlcih2YWwsIGlzU2lnbmVkLCB0aGlzKTtcbiAgICB9LFxuXG4gICAgcmVhZFZhcmludDY0OiBmdW5jdGlvbigpIHsgLy8gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCB2Mi4wLjFcbiAgICAgICAgcmV0dXJuIHRoaXMucmVhZFZhcmludCh0cnVlKTtcbiAgICB9LFxuXG4gICAgcmVhZFNWYXJpbnQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgbnVtID0gdGhpcy5yZWFkVmFyaW50KCk7XG4gICAgICAgIHJldHVybiBudW0gJSAyID09PSAxID8gKG51bSArIDEpIC8gLTIgOiBudW0gLyAyOyAvLyB6aWd6YWcgZW5jb2RpbmdcbiAgICB9LFxuXG4gICAgcmVhZEJvb2xlYW46IGZ1bmN0aW9uKCkge1xuICAgICAgICByZXR1cm4gQm9vbGVhbih0aGlzLnJlYWRWYXJpbnQoKSk7XG4gICAgfSxcblxuICAgIHJlYWRTdHJpbmc6IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgZW5kID0gdGhpcy5yZWFkVmFyaW50KCkgKyB0aGlzLnBvcyxcbiAgICAgICAgICAgIHN0ciA9IHJlYWRVdGY4KHRoaXMuYnVmLCB0aGlzLnBvcywgZW5kKTtcbiAgICAgICAgdGhpcy5wb3MgPSBlbmQ7XG4gICAgICAgIHJldHVybiBzdHI7XG4gICAgfSxcblxuICAgIHJlYWRCeXRlczogZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBlbmQgPSB0aGlzLnJlYWRWYXJpbnQoKSArIHRoaXMucG9zLFxuICAgICAgICAgICAgYnVmZmVyID0gdGhpcy5idWYuc3ViYXJyYXkodGhpcy5wb3MsIGVuZCk7XG4gICAgICAgIHRoaXMucG9zID0gZW5kO1xuICAgICAgICByZXR1cm4gYnVmZmVyO1xuICAgIH0sXG5cbiAgICAvLyB2ZXJib3NlIGZvciBwZXJmb3JtYW5jZSByZWFzb25zOyBkb2Vzbid0IGFmZmVjdCBnemlwcGVkIHNpemVcblxuICAgIHJlYWRQYWNrZWRWYXJpbnQ6IGZ1bmN0aW9uKGFyciwgaXNTaWduZWQpIHtcbiAgICAgICAgdmFyIGVuZCA9IHJlYWRQYWNrZWRFbmQodGhpcyk7XG4gICAgICAgIGFyciA9IGFyciB8fCBbXTtcbiAgICAgICAgd2hpbGUgKHRoaXMucG9zIDwgZW5kKSBhcnIucHVzaCh0aGlzLnJlYWRWYXJpbnQoaXNTaWduZWQpKTtcbiAgICAgICAgcmV0dXJuIGFycjtcbiAgICB9LFxuICAgIHJlYWRQYWNrZWRTVmFyaW50OiBmdW5jdGlvbihhcnIpIHtcbiAgICAgICAgdmFyIGVuZCA9IHJlYWRQYWNrZWRFbmQodGhpcyk7XG4gICAgICAgIGFyciA9IGFyciB8fCBbXTtcbiAgICAgICAgd2hpbGUgKHRoaXMucG9zIDwgZW5kKSBhcnIucHVzaCh0aGlzLnJlYWRTVmFyaW50KCkpO1xuICAgICAgICByZXR1cm4gYXJyO1xuICAgIH0sXG4gICAgcmVhZFBhY2tlZEJvb2xlYW46IGZ1bmN0aW9uKGFycikge1xuICAgICAgICB2YXIgZW5kID0gcmVhZFBhY2tlZEVuZCh0aGlzKTtcbiAgICAgICAgYXJyID0gYXJyIHx8IFtdO1xuICAgICAgICB3aGlsZSAodGhpcy5wb3MgPCBlbmQpIGFyci5wdXNoKHRoaXMucmVhZEJvb2xlYW4oKSk7XG4gICAgICAgIHJldHVybiBhcnI7XG4gICAgfSxcbiAgICByZWFkUGFja2VkRmxvYXQ6IGZ1bmN0aW9uKGFycikge1xuICAgICAgICB2YXIgZW5kID0gcmVhZFBhY2tlZEVuZCh0aGlzKTtcbiAgICAgICAgYXJyID0gYXJyIHx8IFtdO1xuICAgICAgICB3aGlsZSAodGhpcy5wb3MgPCBlbmQpIGFyci5wdXNoKHRoaXMucmVhZEZsb2F0KCkpO1xuICAgICAgICByZXR1cm4gYXJyO1xuICAgIH0sXG4gICAgcmVhZFBhY2tlZERvdWJsZTogZnVuY3Rpb24oYXJyKSB7XG4gICAgICAgIHZhciBlbmQgPSByZWFkUGFja2VkRW5kKHRoaXMpO1xuICAgICAgICBhcnIgPSBhcnIgfHwgW107XG4gICAgICAgIHdoaWxlICh0aGlzLnBvcyA8IGVuZCkgYXJyLnB1c2godGhpcy5yZWFkRG91YmxlKCkpO1xuICAgICAgICByZXR1cm4gYXJyO1xuICAgIH0sXG4gICAgcmVhZFBhY2tlZEZpeGVkMzI6IGZ1bmN0aW9uKGFycikge1xuICAgICAgICB2YXIgZW5kID0gcmVhZFBhY2tlZEVuZCh0aGlzKTtcbiAgICAgICAgYXJyID0gYXJyIHx8IFtdO1xuICAgICAgICB3aGlsZSAodGhpcy5wb3MgPCBlbmQpIGFyci5wdXNoKHRoaXMucmVhZEZpeGVkMzIoKSk7XG4gICAgICAgIHJldHVybiBhcnI7XG4gICAgfSxcbiAgICByZWFkUGFja2VkU0ZpeGVkMzI6IGZ1bmN0aW9uKGFycikge1xuICAgICAgICB2YXIgZW5kID0gcmVhZFBhY2tlZEVuZCh0aGlzKTtcbiAgICAgICAgYXJyID0gYXJyIHx8IFtdO1xuICAgICAgICB3aGlsZSAodGhpcy5wb3MgPCBlbmQpIGFyci5wdXNoKHRoaXMucmVhZFNGaXhlZDMyKCkpO1xuICAgICAgICByZXR1cm4gYXJyO1xuICAgIH0sXG4gICAgcmVhZFBhY2tlZEZpeGVkNjQ6IGZ1bmN0aW9uKGFycikge1xuICAgICAgICB2YXIgZW5kID0gcmVhZFBhY2tlZEVuZCh0aGlzKTtcbiAgICAgICAgYXJyID0gYXJyIHx8IFtdO1xuICAgICAgICB3aGlsZSAodGhpcy5wb3MgPCBlbmQpIGFyci5wdXNoKHRoaXMucmVhZEZpeGVkNjQoKSk7XG4gICAgICAgIHJldHVybiBhcnI7XG4gICAgfSxcbiAgICByZWFkUGFja2VkU0ZpeGVkNjQ6IGZ1bmN0aW9uKGFycikge1xuICAgICAgICB2YXIgZW5kID0gcmVhZFBhY2tlZEVuZCh0aGlzKTtcbiAgICAgICAgYXJyID0gYXJyIHx8IFtdO1xuICAgICAgICB3aGlsZSAodGhpcy5wb3MgPCBlbmQpIGFyci5wdXNoKHRoaXMucmVhZFNGaXhlZDY0KCkpO1xuICAgICAgICByZXR1cm4gYXJyO1xuICAgIH0sXG5cbiAgICBza2lwOiBmdW5jdGlvbih2YWwpIHtcbiAgICAgICAgdmFyIHR5cGUgPSB2YWwgJiAweDc7XG4gICAgICAgIGlmICh0eXBlID09PSBQYmYuVmFyaW50KSB3aGlsZSAodGhpcy5idWZbdGhpcy5wb3MrK10gPiAweDdmKSB7fVxuICAgICAgICBlbHNlIGlmICh0eXBlID09PSBQYmYuQnl0ZXMpIHRoaXMucG9zID0gdGhpcy5yZWFkVmFyaW50KCkgKyB0aGlzLnBvcztcbiAgICAgICAgZWxzZSBpZiAodHlwZSA9PT0gUGJmLkZpeGVkMzIpIHRoaXMucG9zICs9IDQ7XG4gICAgICAgIGVsc2UgaWYgKHR5cGUgPT09IFBiZi5GaXhlZDY0KSB0aGlzLnBvcyArPSA4O1xuICAgICAgICBlbHNlIHRocm93IG5ldyBFcnJvcignVW5pbXBsZW1lbnRlZCB0eXBlOiAnICsgdHlwZSk7XG4gICAgfSxcblxuICAgIC8vID09PSBXUklUSU5HID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgICB3cml0ZVRhZzogZnVuY3Rpb24odGFnLCB0eXBlKSB7XG4gICAgICAgIHRoaXMud3JpdGVWYXJpbnQoKHRhZyA8PCAzKSB8IHR5cGUpO1xuICAgIH0sXG5cbiAgICByZWFsbG9jOiBmdW5jdGlvbihtaW4pIHtcbiAgICAgICAgdmFyIGxlbmd0aCA9IHRoaXMubGVuZ3RoIHx8IDE2O1xuXG4gICAgICAgIHdoaWxlIChsZW5ndGggPCB0aGlzLnBvcyArIG1pbikgbGVuZ3RoICo9IDI7XG5cbiAgICAgICAgaWYgKGxlbmd0aCAhPT0gdGhpcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHZhciBidWYgPSBuZXcgVWludDhBcnJheShsZW5ndGgpO1xuICAgICAgICAgICAgYnVmLnNldCh0aGlzLmJ1Zik7XG4gICAgICAgICAgICB0aGlzLmJ1ZiA9IGJ1ZjtcbiAgICAgICAgICAgIHRoaXMubGVuZ3RoID0gbGVuZ3RoO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIGZpbmlzaDogZnVuY3Rpb24oKSB7XG4gICAgICAgIHRoaXMubGVuZ3RoID0gdGhpcy5wb3M7XG4gICAgICAgIHRoaXMucG9zID0gMDtcbiAgICAgICAgcmV0dXJuIHRoaXMuYnVmLnN1YmFycmF5KDAsIHRoaXMubGVuZ3RoKTtcbiAgICB9LFxuXG4gICAgd3JpdGVGaXhlZDMyOiBmdW5jdGlvbih2YWwpIHtcbiAgICAgICAgdGhpcy5yZWFsbG9jKDQpO1xuICAgICAgICB3cml0ZUludDMyKHRoaXMuYnVmLCB2YWwsIHRoaXMucG9zKTtcbiAgICAgICAgdGhpcy5wb3MgKz0gNDtcbiAgICB9LFxuXG4gICAgd3JpdGVTRml4ZWQzMjogZnVuY3Rpb24odmFsKSB7XG4gICAgICAgIHRoaXMucmVhbGxvYyg0KTtcbiAgICAgICAgd3JpdGVJbnQzMih0aGlzLmJ1ZiwgdmFsLCB0aGlzLnBvcyk7XG4gICAgICAgIHRoaXMucG9zICs9IDQ7XG4gICAgfSxcblxuICAgIHdyaXRlRml4ZWQ2NDogZnVuY3Rpb24odmFsKSB7XG4gICAgICAgIHRoaXMucmVhbGxvYyg4KTtcbiAgICAgICAgd3JpdGVJbnQzMih0aGlzLmJ1ZiwgdmFsICYgLTEsIHRoaXMucG9zKTtcbiAgICAgICAgd3JpdGVJbnQzMih0aGlzLmJ1ZiwgTWF0aC5mbG9vcih2YWwgKiBTSElGVF9SSUdIVF8zMiksIHRoaXMucG9zICsgNCk7XG4gICAgICAgIHRoaXMucG9zICs9IDg7XG4gICAgfSxcblxuICAgIHdyaXRlU0ZpeGVkNjQ6IGZ1bmN0aW9uKHZhbCkge1xuICAgICAgICB0aGlzLnJlYWxsb2MoOCk7XG4gICAgICAgIHdyaXRlSW50MzIodGhpcy5idWYsIHZhbCAmIC0xLCB0aGlzLnBvcyk7XG4gICAgICAgIHdyaXRlSW50MzIodGhpcy5idWYsIE1hdGguZmxvb3IodmFsICogU0hJRlRfUklHSFRfMzIpLCB0aGlzLnBvcyArIDQpO1xuICAgICAgICB0aGlzLnBvcyArPSA4O1xuICAgIH0sXG5cbiAgICB3cml0ZVZhcmludDogZnVuY3Rpb24odmFsKSB7XG4gICAgICAgIHZhbCA9ICt2YWwgfHwgMDtcblxuICAgICAgICBpZiAodmFsID4gMHhmZmZmZmZmIHx8IHZhbCA8IDApIHtcbiAgICAgICAgICAgIHdyaXRlQmlnVmFyaW50KHZhbCwgdGhpcyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnJlYWxsb2MoNCk7XG5cbiAgICAgICAgdGhpcy5idWZbdGhpcy5wb3MrK10gPSAgICAgICAgICAgdmFsICYgMHg3ZiAgfCAodmFsID4gMHg3ZiA/IDB4ODAgOiAwKTsgaWYgKHZhbCA8PSAweDdmKSByZXR1cm47XG4gICAgICAgIHRoaXMuYnVmW3RoaXMucG9zKytdID0gKCh2YWwgPj4+PSA3KSAmIDB4N2YpIHwgKHZhbCA+IDB4N2YgPyAweDgwIDogMCk7IGlmICh2YWwgPD0gMHg3ZikgcmV0dXJuO1xuICAgICAgICB0aGlzLmJ1Zlt0aGlzLnBvcysrXSA9ICgodmFsID4+Pj0gNykgJiAweDdmKSB8ICh2YWwgPiAweDdmID8gMHg4MCA6IDApOyBpZiAodmFsIDw9IDB4N2YpIHJldHVybjtcbiAgICAgICAgdGhpcy5idWZbdGhpcy5wb3MrK10gPSAgICh2YWwgPj4+IDcpICYgMHg3ZjtcbiAgICB9LFxuXG4gICAgd3JpdGVTVmFyaW50OiBmdW5jdGlvbih2YWwpIHtcbiAgICAgICAgdGhpcy53cml0ZVZhcmludCh2YWwgPCAwID8gLXZhbCAqIDIgLSAxIDogdmFsICogMik7XG4gICAgfSxcblxuICAgIHdyaXRlQm9vbGVhbjogZnVuY3Rpb24odmFsKSB7XG4gICAgICAgIHRoaXMud3JpdGVWYXJpbnQoQm9vbGVhbih2YWwpKTtcbiAgICB9LFxuXG4gICAgd3JpdGVTdHJpbmc6IGZ1bmN0aW9uKHN0cikge1xuICAgICAgICBzdHIgPSBTdHJpbmcoc3RyKTtcbiAgICAgICAgdGhpcy5yZWFsbG9jKHN0ci5sZW5ndGggKiA0KTtcblxuICAgICAgICB0aGlzLnBvcysrOyAvLyByZXNlcnZlIDEgYnl0ZSBmb3Igc2hvcnQgc3RyaW5nIGxlbmd0aFxuXG4gICAgICAgIHZhciBzdGFydFBvcyA9IHRoaXMucG9zO1xuICAgICAgICAvLyB3cml0ZSB0aGUgc3RyaW5nIGRpcmVjdGx5IHRvIHRoZSBidWZmZXIgYW5kIHNlZSBob3cgbXVjaCB3YXMgd3JpdHRlblxuICAgICAgICB0aGlzLnBvcyA9IHdyaXRlVXRmOCh0aGlzLmJ1Ziwgc3RyLCB0aGlzLnBvcyk7XG4gICAgICAgIHZhciBsZW4gPSB0aGlzLnBvcyAtIHN0YXJ0UG9zO1xuXG4gICAgICAgIGlmIChsZW4gPj0gMHg4MCkgbWFrZVJvb21Gb3JFeHRyYUxlbmd0aChzdGFydFBvcywgbGVuLCB0aGlzKTtcblxuICAgICAgICAvLyBmaW5hbGx5LCB3cml0ZSB0aGUgbWVzc2FnZSBsZW5ndGggaW4gdGhlIHJlc2VydmVkIHBsYWNlIGFuZCByZXN0b3JlIHRoZSBwb3NpdGlvblxuICAgICAgICB0aGlzLnBvcyA9IHN0YXJ0UG9zIC0gMTtcbiAgICAgICAgdGhpcy53cml0ZVZhcmludChsZW4pO1xuICAgICAgICB0aGlzLnBvcyArPSBsZW47XG4gICAgfSxcblxuICAgIHdyaXRlRmxvYXQ6IGZ1bmN0aW9uKHZhbCkge1xuICAgICAgICB0aGlzLnJlYWxsb2MoNCk7XG4gICAgICAgIGllZWU3NTQud3JpdGUodGhpcy5idWYsIHZhbCwgdGhpcy5wb3MsIHRydWUsIDIzLCA0KTtcbiAgICAgICAgdGhpcy5wb3MgKz0gNDtcbiAgICB9LFxuXG4gICAgd3JpdGVEb3VibGU6IGZ1bmN0aW9uKHZhbCkge1xuICAgICAgICB0aGlzLnJlYWxsb2MoOCk7XG4gICAgICAgIGllZWU3NTQud3JpdGUodGhpcy5idWYsIHZhbCwgdGhpcy5wb3MsIHRydWUsIDUyLCA4KTtcbiAgICAgICAgdGhpcy5wb3MgKz0gODtcbiAgICB9LFxuXG4gICAgd3JpdGVCeXRlczogZnVuY3Rpb24oYnVmZmVyKSB7XG4gICAgICAgIHZhciBsZW4gPSBidWZmZXIubGVuZ3RoO1xuICAgICAgICB0aGlzLndyaXRlVmFyaW50KGxlbik7XG4gICAgICAgIHRoaXMucmVhbGxvYyhsZW4pO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbjsgaSsrKSB0aGlzLmJ1Zlt0aGlzLnBvcysrXSA9IGJ1ZmZlcltpXTtcbiAgICB9LFxuXG4gICAgd3JpdGVSYXdNZXNzYWdlOiBmdW5jdGlvbihmbiwgb2JqKSB7XG4gICAgICAgIHRoaXMucG9zKys7IC8vIHJlc2VydmUgMSBieXRlIGZvciBzaG9ydCBtZXNzYWdlIGxlbmd0aFxuXG4gICAgICAgIC8vIHdyaXRlIHRoZSBtZXNzYWdlIGRpcmVjdGx5IHRvIHRoZSBidWZmZXIgYW5kIHNlZSBob3cgbXVjaCB3YXMgd3JpdHRlblxuICAgICAgICB2YXIgc3RhcnRQb3MgPSB0aGlzLnBvcztcbiAgICAgICAgZm4ob2JqLCB0aGlzKTtcbiAgICAgICAgdmFyIGxlbiA9IHRoaXMucG9zIC0gc3RhcnRQb3M7XG5cbiAgICAgICAgaWYgKGxlbiA+PSAweDgwKSBtYWtlUm9vbUZvckV4dHJhTGVuZ3RoKHN0YXJ0UG9zLCBsZW4sIHRoaXMpO1xuXG4gICAgICAgIC8vIGZpbmFsbHksIHdyaXRlIHRoZSBtZXNzYWdlIGxlbmd0aCBpbiB0aGUgcmVzZXJ2ZWQgcGxhY2UgYW5kIHJlc3RvcmUgdGhlIHBvc2l0aW9uXG4gICAgICAgIHRoaXMucG9zID0gc3RhcnRQb3MgLSAxO1xuICAgICAgICB0aGlzLndyaXRlVmFyaW50KGxlbik7XG4gICAgICAgIHRoaXMucG9zICs9IGxlbjtcbiAgICB9LFxuXG4gICAgd3JpdGVNZXNzYWdlOiBmdW5jdGlvbih0YWcsIGZuLCBvYmopIHtcbiAgICAgICAgdGhpcy53cml0ZVRhZyh0YWcsIFBiZi5CeXRlcyk7XG4gICAgICAgIHRoaXMud3JpdGVSYXdNZXNzYWdlKGZuLCBvYmopO1xuICAgIH0sXG5cbiAgICB3cml0ZVBhY2tlZFZhcmludDogICBmdW5jdGlvbih0YWcsIGFycikgeyB0aGlzLndyaXRlTWVzc2FnZSh0YWcsIHdyaXRlUGFja2VkVmFyaW50LCBhcnIpOyAgIH0sXG4gICAgd3JpdGVQYWNrZWRTVmFyaW50OiAgZnVuY3Rpb24odGFnLCBhcnIpIHsgdGhpcy53cml0ZU1lc3NhZ2UodGFnLCB3cml0ZVBhY2tlZFNWYXJpbnQsIGFycik7ICB9LFxuICAgIHdyaXRlUGFja2VkQm9vbGVhbjogIGZ1bmN0aW9uKHRhZywgYXJyKSB7IHRoaXMud3JpdGVNZXNzYWdlKHRhZywgd3JpdGVQYWNrZWRCb29sZWFuLCBhcnIpOyAgfSxcbiAgICB3cml0ZVBhY2tlZEZsb2F0OiAgICBmdW5jdGlvbih0YWcsIGFycikgeyB0aGlzLndyaXRlTWVzc2FnZSh0YWcsIHdyaXRlUGFja2VkRmxvYXQsIGFycik7ICAgIH0sXG4gICAgd3JpdGVQYWNrZWREb3VibGU6ICAgZnVuY3Rpb24odGFnLCBhcnIpIHsgdGhpcy53cml0ZU1lc3NhZ2UodGFnLCB3cml0ZVBhY2tlZERvdWJsZSwgYXJyKTsgICB9LFxuICAgIHdyaXRlUGFja2VkRml4ZWQzMjogIGZ1bmN0aW9uKHRhZywgYXJyKSB7IHRoaXMud3JpdGVNZXNzYWdlKHRhZywgd3JpdGVQYWNrZWRGaXhlZDMyLCBhcnIpOyAgfSxcbiAgICB3cml0ZVBhY2tlZFNGaXhlZDMyOiBmdW5jdGlvbih0YWcsIGFycikgeyB0aGlzLndyaXRlTWVzc2FnZSh0YWcsIHdyaXRlUGFja2VkU0ZpeGVkMzIsIGFycik7IH0sXG4gICAgd3JpdGVQYWNrZWRGaXhlZDY0OiAgZnVuY3Rpb24odGFnLCBhcnIpIHsgdGhpcy53cml0ZU1lc3NhZ2UodGFnLCB3cml0ZVBhY2tlZEZpeGVkNjQsIGFycik7ICB9LFxuICAgIHdyaXRlUGFja2VkU0ZpeGVkNjQ6IGZ1bmN0aW9uKHRhZywgYXJyKSB7IHRoaXMud3JpdGVNZXNzYWdlKHRhZywgd3JpdGVQYWNrZWRTRml4ZWQ2NCwgYXJyKTsgfSxcblxuICAgIHdyaXRlQnl0ZXNGaWVsZDogZnVuY3Rpb24odGFnLCBidWZmZXIpIHtcbiAgICAgICAgdGhpcy53cml0ZVRhZyh0YWcsIFBiZi5CeXRlcyk7XG4gICAgICAgIHRoaXMud3JpdGVCeXRlcyhidWZmZXIpO1xuICAgIH0sXG4gICAgd3JpdGVGaXhlZDMyRmllbGQ6IGZ1bmN0aW9uKHRhZywgdmFsKSB7XG4gICAgICAgIHRoaXMud3JpdGVUYWcodGFnLCBQYmYuRml4ZWQzMik7XG4gICAgICAgIHRoaXMud3JpdGVGaXhlZDMyKHZhbCk7XG4gICAgfSxcbiAgICB3cml0ZVNGaXhlZDMyRmllbGQ6IGZ1bmN0aW9uKHRhZywgdmFsKSB7XG4gICAgICAgIHRoaXMud3JpdGVUYWcodGFnLCBQYmYuRml4ZWQzMik7XG4gICAgICAgIHRoaXMud3JpdGVTRml4ZWQzMih2YWwpO1xuICAgIH0sXG4gICAgd3JpdGVGaXhlZDY0RmllbGQ6IGZ1bmN0aW9uKHRhZywgdmFsKSB7XG4gICAgICAgIHRoaXMud3JpdGVUYWcodGFnLCBQYmYuRml4ZWQ2NCk7XG4gICAgICAgIHRoaXMud3JpdGVGaXhlZDY0KHZhbCk7XG4gICAgfSxcbiAgICB3cml0ZVNGaXhlZDY0RmllbGQ6IGZ1bmN0aW9uKHRhZywgdmFsKSB7XG4gICAgICAgIHRoaXMud3JpdGVUYWcodGFnLCBQYmYuRml4ZWQ2NCk7XG4gICAgICAgIHRoaXMud3JpdGVTRml4ZWQ2NCh2YWwpO1xuICAgIH0sXG4gICAgd3JpdGVWYXJpbnRGaWVsZDogZnVuY3Rpb24odGFnLCB2YWwpIHtcbiAgICAgICAgdGhpcy53cml0ZVRhZyh0YWcsIFBiZi5WYXJpbnQpO1xuICAgICAgICB0aGlzLndyaXRlVmFyaW50KHZhbCk7XG4gICAgfSxcbiAgICB3cml0ZVNWYXJpbnRGaWVsZDogZnVuY3Rpb24odGFnLCB2YWwpIHtcbiAgICAgICAgdGhpcy53cml0ZVRhZyh0YWcsIFBiZi5WYXJpbnQpO1xuICAgICAgICB0aGlzLndyaXRlU1ZhcmludCh2YWwpO1xuICAgIH0sXG4gICAgd3JpdGVTdHJpbmdGaWVsZDogZnVuY3Rpb24odGFnLCBzdHIpIHtcbiAgICAgICAgdGhpcy53cml0ZVRhZyh0YWcsIFBiZi5CeXRlcyk7XG4gICAgICAgIHRoaXMud3JpdGVTdHJpbmcoc3RyKTtcbiAgICB9LFxuICAgIHdyaXRlRmxvYXRGaWVsZDogZnVuY3Rpb24odGFnLCB2YWwpIHtcbiAgICAgICAgdGhpcy53cml0ZVRhZyh0YWcsIFBiZi5GaXhlZDMyKTtcbiAgICAgICAgdGhpcy53cml0ZUZsb2F0KHZhbCk7XG4gICAgfSxcbiAgICB3cml0ZURvdWJsZUZpZWxkOiBmdW5jdGlvbih0YWcsIHZhbCkge1xuICAgICAgICB0aGlzLndyaXRlVGFnKHRhZywgUGJmLkZpeGVkNjQpO1xuICAgICAgICB0aGlzLndyaXRlRG91YmxlKHZhbCk7XG4gICAgfSxcbiAgICB3cml0ZUJvb2xlYW5GaWVsZDogZnVuY3Rpb24odGFnLCB2YWwpIHtcbiAgICAgICAgdGhpcy53cml0ZVZhcmludEZpZWxkKHRhZywgQm9vbGVhbih2YWwpKTtcbiAgICB9XG59O1xuXG5mdW5jdGlvbiByZWFkVmFyaW50UmVtYWluZGVyKGwsIHMsIHApIHtcbiAgICB2YXIgYnVmID0gcC5idWYsXG4gICAgICAgIGgsIGI7XG5cbiAgICBiID0gYnVmW3AucG9zKytdOyBoICA9IChiICYgMHg3MCkgPj4gNDsgIGlmIChiIDwgMHg4MCkgcmV0dXJuIHRvTnVtKGwsIGgsIHMpO1xuICAgIGIgPSBidWZbcC5wb3MrK107IGggfD0gKGIgJiAweDdmKSA8PCAzOyAgaWYgKGIgPCAweDgwKSByZXR1cm4gdG9OdW0obCwgaCwgcyk7XG4gICAgYiA9IGJ1ZltwLnBvcysrXTsgaCB8PSAoYiAmIDB4N2YpIDw8IDEwOyBpZiAoYiA8IDB4ODApIHJldHVybiB0b051bShsLCBoLCBzKTtcbiAgICBiID0gYnVmW3AucG9zKytdOyBoIHw9IChiICYgMHg3ZikgPDwgMTc7IGlmIChiIDwgMHg4MCkgcmV0dXJuIHRvTnVtKGwsIGgsIHMpO1xuICAgIGIgPSBidWZbcC5wb3MrK107IGggfD0gKGIgJiAweDdmKSA8PCAyNDsgaWYgKGIgPCAweDgwKSByZXR1cm4gdG9OdW0obCwgaCwgcyk7XG4gICAgYiA9IGJ1ZltwLnBvcysrXTsgaCB8PSAoYiAmIDB4MDEpIDw8IDMxOyBpZiAoYiA8IDB4ODApIHJldHVybiB0b051bShsLCBoLCBzKTtcblxuICAgIHRocm93IG5ldyBFcnJvcignRXhwZWN0ZWQgdmFyaW50IG5vdCBtb3JlIHRoYW4gMTAgYnl0ZXMnKTtcbn1cblxuZnVuY3Rpb24gcmVhZFBhY2tlZEVuZChwYmYpIHtcbiAgICByZXR1cm4gcGJmLnR5cGUgPT09IFBiZi5CeXRlcyA/XG4gICAgICAgIHBiZi5yZWFkVmFyaW50KCkgKyBwYmYucG9zIDogcGJmLnBvcyArIDE7XG59XG5cbmZ1bmN0aW9uIHRvTnVtKGxvdywgaGlnaCwgaXNTaWduZWQpIHtcbiAgICBpZiAoaXNTaWduZWQpIHtcbiAgICAgICAgcmV0dXJuIGhpZ2ggKiAweDEwMDAwMDAwMCArIChsb3cgPj4+IDApO1xuICAgIH1cblxuICAgIHJldHVybiAoKGhpZ2ggPj4+IDApICogMHgxMDAwMDAwMDApICsgKGxvdyA+Pj4gMCk7XG59XG5cbmZ1bmN0aW9uIHdyaXRlQmlnVmFyaW50KHZhbCwgcGJmKSB7XG4gICAgdmFyIGxvdywgaGlnaDtcblxuICAgIGlmICh2YWwgPj0gMCkge1xuICAgICAgICBsb3cgID0gKHZhbCAlIDB4MTAwMDAwMDAwKSB8IDA7XG4gICAgICAgIGhpZ2ggPSAodmFsIC8gMHgxMDAwMDAwMDApIHwgMDtcbiAgICB9IGVsc2Uge1xuICAgICAgICBsb3cgID0gfigtdmFsICUgMHgxMDAwMDAwMDApO1xuICAgICAgICBoaWdoID0gfigtdmFsIC8gMHgxMDAwMDAwMDApO1xuXG4gICAgICAgIGlmIChsb3cgXiAweGZmZmZmZmZmKSB7XG4gICAgICAgICAgICBsb3cgPSAobG93ICsgMSkgfCAwO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbG93ID0gMDtcbiAgICAgICAgICAgIGhpZ2ggPSAoaGlnaCArIDEpIHwgMDtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGlmICh2YWwgPj0gMHgxMDAwMDAwMDAwMDAwMDAwMCB8fCB2YWwgPCAtMHgxMDAwMDAwMDAwMDAwMDAwMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0dpdmVuIHZhcmludCBkb2VzblxcJ3QgZml0IGludG8gMTAgYnl0ZXMnKTtcbiAgICB9XG5cbiAgICBwYmYucmVhbGxvYygxMCk7XG5cbiAgICB3cml0ZUJpZ1ZhcmludExvdyhsb3csIGhpZ2gsIHBiZik7XG4gICAgd3JpdGVCaWdWYXJpbnRIaWdoKGhpZ2gsIHBiZik7XG59XG5cbmZ1bmN0aW9uIHdyaXRlQmlnVmFyaW50TG93KGxvdywgaGlnaCwgcGJmKSB7XG4gICAgcGJmLmJ1ZltwYmYucG9zKytdID0gbG93ICYgMHg3ZiB8IDB4ODA7IGxvdyA+Pj49IDc7XG4gICAgcGJmLmJ1ZltwYmYucG9zKytdID0gbG93ICYgMHg3ZiB8IDB4ODA7IGxvdyA+Pj49IDc7XG4gICAgcGJmLmJ1ZltwYmYucG9zKytdID0gbG93ICYgMHg3ZiB8IDB4ODA7IGxvdyA+Pj49IDc7XG4gICAgcGJmLmJ1ZltwYmYucG9zKytdID0gbG93ICYgMHg3ZiB8IDB4ODA7IGxvdyA+Pj49IDc7XG4gICAgcGJmLmJ1ZltwYmYucG9zXSAgID0gbG93ICYgMHg3Zjtcbn1cblxuZnVuY3Rpb24gd3JpdGVCaWdWYXJpbnRIaWdoKGhpZ2gsIHBiZikge1xuICAgIHZhciBsc2IgPSAoaGlnaCAmIDB4MDcpIDw8IDQ7XG5cbiAgICBwYmYuYnVmW3BiZi5wb3MrK10gfD0gbHNiICAgICAgICAgfCAoKGhpZ2ggPj4+PSAzKSA/IDB4ODAgOiAwKTsgaWYgKCFoaWdoKSByZXR1cm47XG4gICAgcGJmLmJ1ZltwYmYucG9zKytdICA9IGhpZ2ggJiAweDdmIHwgKChoaWdoID4+Pj0gNykgPyAweDgwIDogMCk7IGlmICghaGlnaCkgcmV0dXJuO1xuICAgIHBiZi5idWZbcGJmLnBvcysrXSAgPSBoaWdoICYgMHg3ZiB8ICgoaGlnaCA+Pj49IDcpID8gMHg4MCA6IDApOyBpZiAoIWhpZ2gpIHJldHVybjtcbiAgICBwYmYuYnVmW3BiZi5wb3MrK10gID0gaGlnaCAmIDB4N2YgfCAoKGhpZ2ggPj4+PSA3KSA/IDB4ODAgOiAwKTsgaWYgKCFoaWdoKSByZXR1cm47XG4gICAgcGJmLmJ1ZltwYmYucG9zKytdICA9IGhpZ2ggJiAweDdmIHwgKChoaWdoID4+Pj0gNykgPyAweDgwIDogMCk7IGlmICghaGlnaCkgcmV0dXJuO1xuICAgIHBiZi5idWZbcGJmLnBvcysrXSAgPSBoaWdoICYgMHg3Zjtcbn1cblxuZnVuY3Rpb24gbWFrZVJvb21Gb3JFeHRyYUxlbmd0aChzdGFydFBvcywgbGVuLCBwYmYpIHtcbiAgICB2YXIgZXh0cmFMZW4gPVxuICAgICAgICBsZW4gPD0gMHgzZmZmID8gMSA6XG4gICAgICAgIGxlbiA8PSAweDFmZmZmZiA/IDIgOlxuICAgICAgICBsZW4gPD0gMHhmZmZmZmZmID8gMyA6IE1hdGguY2VpbChNYXRoLmxvZyhsZW4pIC8gKE1hdGguTE4yICogNykpO1xuXG4gICAgLy8gaWYgMSBieXRlIGlzbid0IGVub3VnaCBmb3IgZW5jb2RpbmcgbWVzc2FnZSBsZW5ndGgsIHNoaWZ0IHRoZSBkYXRhIHRvIHRoZSByaWdodFxuICAgIHBiZi5yZWFsbG9jKGV4dHJhTGVuKTtcbiAgICBmb3IgKHZhciBpID0gcGJmLnBvcyAtIDE7IGkgPj0gc3RhcnRQb3M7IGktLSkgcGJmLmJ1ZltpICsgZXh0cmFMZW5dID0gcGJmLmJ1ZltpXTtcbn1cblxuZnVuY3Rpb24gd3JpdGVQYWNrZWRWYXJpbnQoYXJyLCBwYmYpICAgeyBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykgcGJmLndyaXRlVmFyaW50KGFycltpXSk7ICAgfVxuZnVuY3Rpb24gd3JpdGVQYWNrZWRTVmFyaW50KGFyciwgcGJmKSAgeyBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykgcGJmLndyaXRlU1ZhcmludChhcnJbaV0pOyAgfVxuZnVuY3Rpb24gd3JpdGVQYWNrZWRGbG9hdChhcnIsIHBiZikgICAgeyBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykgcGJmLndyaXRlRmxvYXQoYXJyW2ldKTsgICAgfVxuZnVuY3Rpb24gd3JpdGVQYWNrZWREb3VibGUoYXJyLCBwYmYpICAgeyBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykgcGJmLndyaXRlRG91YmxlKGFycltpXSk7ICAgfVxuZnVuY3Rpb24gd3JpdGVQYWNrZWRCb29sZWFuKGFyciwgcGJmKSAgeyBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykgcGJmLndyaXRlQm9vbGVhbihhcnJbaV0pOyAgfVxuZnVuY3Rpb24gd3JpdGVQYWNrZWRGaXhlZDMyKGFyciwgcGJmKSAgeyBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykgcGJmLndyaXRlRml4ZWQzMihhcnJbaV0pOyAgfVxuZnVuY3Rpb24gd3JpdGVQYWNrZWRTRml4ZWQzMihhcnIsIHBiZikgeyBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykgcGJmLndyaXRlU0ZpeGVkMzIoYXJyW2ldKTsgfVxuZnVuY3Rpb24gd3JpdGVQYWNrZWRGaXhlZDY0KGFyciwgcGJmKSAgeyBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykgcGJmLndyaXRlRml4ZWQ2NChhcnJbaV0pOyAgfVxuZnVuY3Rpb24gd3JpdGVQYWNrZWRTRml4ZWQ2NChhcnIsIHBiZikgeyBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykgcGJmLndyaXRlU0ZpeGVkNjQoYXJyW2ldKTsgfVxuXG4vLyBCdWZmZXIgY29kZSBiZWxvdyBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9mZXJvc3MvYnVmZmVyLCBNSVQtbGljZW5zZWRcblxuZnVuY3Rpb24gcmVhZFVJbnQzMihidWYsIHBvcykge1xuICAgIHJldHVybiAoKGJ1Zltwb3NdKSB8XG4gICAgICAgIChidWZbcG9zICsgMV0gPDwgOCkgfFxuICAgICAgICAoYnVmW3BvcyArIDJdIDw8IDE2KSkgK1xuICAgICAgICAoYnVmW3BvcyArIDNdICogMHgxMDAwMDAwKTtcbn1cblxuZnVuY3Rpb24gd3JpdGVJbnQzMihidWYsIHZhbCwgcG9zKSB7XG4gICAgYnVmW3Bvc10gPSB2YWw7XG4gICAgYnVmW3BvcyArIDFdID0gKHZhbCA+Pj4gOCk7XG4gICAgYnVmW3BvcyArIDJdID0gKHZhbCA+Pj4gMTYpO1xuICAgIGJ1Zltwb3MgKyAzXSA9ICh2YWwgPj4+IDI0KTtcbn1cblxuZnVuY3Rpb24gcmVhZEludDMyKGJ1ZiwgcG9zKSB7XG4gICAgcmV0dXJuICgoYnVmW3Bvc10pIHxcbiAgICAgICAgKGJ1Zltwb3MgKyAxXSA8PCA4KSB8XG4gICAgICAgIChidWZbcG9zICsgMl0gPDwgMTYpKSArXG4gICAgICAgIChidWZbcG9zICsgM10gPDwgMjQpO1xufVxuXG5mdW5jdGlvbiByZWFkVXRmOChidWYsIHBvcywgZW5kKSB7XG4gICAgdmFyIHN0ciA9ICcnO1xuICAgIHZhciBpID0gcG9zO1xuXG4gICAgd2hpbGUgKGkgPCBlbmQpIHtcbiAgICAgICAgdmFyIGIwID0gYnVmW2ldO1xuICAgICAgICB2YXIgYyA9IG51bGw7IC8vIGNvZGVwb2ludFxuICAgICAgICB2YXIgYnl0ZXNQZXJTZXF1ZW5jZSA9XG4gICAgICAgICAgICBiMCA+IDB4RUYgPyA0IDpcbiAgICAgICAgICAgIGIwID4gMHhERiA/IDMgOlxuICAgICAgICAgICAgYjAgPiAweEJGID8gMiA6IDE7XG5cbiAgICAgICAgaWYgKGkgKyBieXRlc1BlclNlcXVlbmNlID4gZW5kKSBicmVhaztcblxuICAgICAgICB2YXIgYjEsIGIyLCBiMztcblxuICAgICAgICBpZiAoYnl0ZXNQZXJTZXF1ZW5jZSA9PT0gMSkge1xuICAgICAgICAgICAgaWYgKGIwIDwgMHg4MCkge1xuICAgICAgICAgICAgICAgIGMgPSBiMDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChieXRlc1BlclNlcXVlbmNlID09PSAyKSB7XG4gICAgICAgICAgICBiMSA9IGJ1ZltpICsgMV07XG4gICAgICAgICAgICBpZiAoKGIxICYgMHhDMCkgPT09IDB4ODApIHtcbiAgICAgICAgICAgICAgICBjID0gKGIwICYgMHgxRikgPDwgMHg2IHwgKGIxICYgMHgzRik7XG4gICAgICAgICAgICAgICAgaWYgKGMgPD0gMHg3Rikge1xuICAgICAgICAgICAgICAgICAgICBjID0gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoYnl0ZXNQZXJTZXF1ZW5jZSA9PT0gMykge1xuICAgICAgICAgICAgYjEgPSBidWZbaSArIDFdO1xuICAgICAgICAgICAgYjIgPSBidWZbaSArIDJdO1xuICAgICAgICAgICAgaWYgKChiMSAmIDB4QzApID09PSAweDgwICYmIChiMiAmIDB4QzApID09PSAweDgwKSB7XG4gICAgICAgICAgICAgICAgYyA9IChiMCAmIDB4RikgPDwgMHhDIHwgKGIxICYgMHgzRikgPDwgMHg2IHwgKGIyICYgMHgzRik7XG4gICAgICAgICAgICAgICAgaWYgKGMgPD0gMHg3RkYgfHwgKGMgPj0gMHhEODAwICYmIGMgPD0gMHhERkZGKSkge1xuICAgICAgICAgICAgICAgICAgICBjID0gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoYnl0ZXNQZXJTZXF1ZW5jZSA9PT0gNCkge1xuICAgICAgICAgICAgYjEgPSBidWZbaSArIDFdO1xuICAgICAgICAgICAgYjIgPSBidWZbaSArIDJdO1xuICAgICAgICAgICAgYjMgPSBidWZbaSArIDNdO1xuICAgICAgICAgICAgaWYgKChiMSAmIDB4QzApID09PSAweDgwICYmIChiMiAmIDB4QzApID09PSAweDgwICYmIChiMyAmIDB4QzApID09PSAweDgwKSB7XG4gICAgICAgICAgICAgICAgYyA9IChiMCAmIDB4RikgPDwgMHgxMiB8IChiMSAmIDB4M0YpIDw8IDB4QyB8IChiMiAmIDB4M0YpIDw8IDB4NiB8IChiMyAmIDB4M0YpO1xuICAgICAgICAgICAgICAgIGlmIChjIDw9IDB4RkZGRiB8fCBjID49IDB4MTEwMDAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGMgPSBudWxsO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjID09PSBudWxsKSB7XG4gICAgICAgICAgICBjID0gMHhGRkZEO1xuICAgICAgICAgICAgYnl0ZXNQZXJTZXF1ZW5jZSA9IDE7XG5cbiAgICAgICAgfSBlbHNlIGlmIChjID4gMHhGRkZGKSB7XG4gICAgICAgICAgICBjIC09IDB4MTAwMDA7XG4gICAgICAgICAgICBzdHIgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShjID4+PiAxMCAmIDB4M0ZGIHwgMHhEODAwKTtcbiAgICAgICAgICAgIGMgPSAweERDMDAgfCBjICYgMHgzRkY7XG4gICAgICAgIH1cblxuICAgICAgICBzdHIgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShjKTtcbiAgICAgICAgaSArPSBieXRlc1BlclNlcXVlbmNlO1xuICAgIH1cblxuICAgIHJldHVybiBzdHI7XG59XG5cbmZ1bmN0aW9uIHdyaXRlVXRmOChidWYsIHN0ciwgcG9zKSB7XG4gICAgZm9yICh2YXIgaSA9IDAsIGMsIGxlYWQ7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgYyA9IHN0ci5jaGFyQ29kZUF0KGkpOyAvLyBjb2RlIHBvaW50XG5cbiAgICAgICAgaWYgKGMgPiAweEQ3RkYgJiYgYyA8IDB4RTAwMCkge1xuICAgICAgICAgICAgaWYgKGxlYWQpIHtcbiAgICAgICAgICAgICAgICBpZiAoYyA8IDB4REMwMCkge1xuICAgICAgICAgICAgICAgICAgICBidWZbcG9zKytdID0gMHhFRjtcbiAgICAgICAgICAgICAgICAgICAgYnVmW3BvcysrXSA9IDB4QkY7XG4gICAgICAgICAgICAgICAgICAgIGJ1Zltwb3MrK10gPSAweEJEO1xuICAgICAgICAgICAgICAgICAgICBsZWFkID0gYztcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgYyA9IGxlYWQgLSAweEQ4MDAgPDwgMTAgfCBjIC0gMHhEQzAwIHwgMHgxMDAwMDtcbiAgICAgICAgICAgICAgICAgICAgbGVhZCA9IG51bGw7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpZiAoYyA+IDB4REJGRiB8fCAoaSArIDEgPT09IHN0ci5sZW5ndGgpKSB7XG4gICAgICAgICAgICAgICAgICAgIGJ1Zltwb3MrK10gPSAweEVGO1xuICAgICAgICAgICAgICAgICAgICBidWZbcG9zKytdID0gMHhCRjtcbiAgICAgICAgICAgICAgICAgICAgYnVmW3BvcysrXSA9IDB4QkQ7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgbGVhZCA9IGM7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKGxlYWQpIHtcbiAgICAgICAgICAgIGJ1Zltwb3MrK10gPSAweEVGO1xuICAgICAgICAgICAgYnVmW3BvcysrXSA9IDB4QkY7XG4gICAgICAgICAgICBidWZbcG9zKytdID0gMHhCRDtcbiAgICAgICAgICAgIGxlYWQgPSBudWxsO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGMgPCAweDgwKSB7XG4gICAgICAgICAgICBidWZbcG9zKytdID0gYztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmIChjIDwgMHg4MDApIHtcbiAgICAgICAgICAgICAgICBidWZbcG9zKytdID0gYyA+PiAweDYgfCAweEMwO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBpZiAoYyA8IDB4MTAwMDApIHtcbiAgICAgICAgICAgICAgICAgICAgYnVmW3BvcysrXSA9IGMgPj4gMHhDIHwgMHhFMDtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBidWZbcG9zKytdID0gYyA+PiAweDEyIHwgMHhGMDtcbiAgICAgICAgICAgICAgICAgICAgYnVmW3BvcysrXSA9IGMgPj4gMHhDICYgMHgzRiB8IDB4ODA7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJ1Zltwb3MrK10gPSBjID4+IDB4NiAmIDB4M0YgfCAweDgwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnVmW3BvcysrXSA9IGMgJiAweDNGIHwgMHg4MDtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcG9zO1xufVxuIiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHBhcnRpYWxTb3J0O1xuXG4vLyBGbG95ZC1SaXZlc3Qgc2VsZWN0aW9uIGFsZ29yaXRobTpcbi8vIFJlYXJyYW5nZSBpdGVtcyBzbyB0aGF0IGFsbCBpdGVtcyBpbiB0aGUgW2xlZnQsIGtdIHJhbmdlIGFyZSBzbWFsbGVyIHRoYW4gYWxsIGl0ZW1zIGluIChrLCByaWdodF07XG4vLyBUaGUgay10aCBlbGVtZW50IHdpbGwgaGF2ZSB0aGUgKGsgLSBsZWZ0ICsgMSl0aCBzbWFsbGVzdCB2YWx1ZSBpbiBbbGVmdCwgcmlnaHRdXG5cbmZ1bmN0aW9uIHBhcnRpYWxTb3J0KGFyciwgaywgbGVmdCwgcmlnaHQsIGNvbXBhcmUpIHtcbiAgICBsZWZ0ID0gbGVmdCB8fCAwO1xuICAgIHJpZ2h0ID0gcmlnaHQgfHwgKGFyci5sZW5ndGggLSAxKTtcbiAgICBjb21wYXJlID0gY29tcGFyZSB8fCBkZWZhdWx0Q29tcGFyZTtcblxuICAgIHdoaWxlIChyaWdodCA+IGxlZnQpIHtcbiAgICAgICAgaWYgKHJpZ2h0IC0gbGVmdCA+IDYwMCkge1xuICAgICAgICAgICAgdmFyIG4gPSByaWdodCAtIGxlZnQgKyAxO1xuICAgICAgICAgICAgdmFyIG0gPSBrIC0gbGVmdCArIDE7XG4gICAgICAgICAgICB2YXIgeiA9IE1hdGgubG9nKG4pO1xuICAgICAgICAgICAgdmFyIHMgPSAwLjUgKiBNYXRoLmV4cCgyICogeiAvIDMpO1xuICAgICAgICAgICAgdmFyIHNkID0gMC41ICogTWF0aC5zcXJ0KHogKiBzICogKG4gLSBzKSAvIG4pICogKG0gLSBuIC8gMiA8IDAgPyAtMSA6IDEpO1xuICAgICAgICAgICAgdmFyIG5ld0xlZnQgPSBNYXRoLm1heChsZWZ0LCBNYXRoLmZsb29yKGsgLSBtICogcyAvIG4gKyBzZCkpO1xuICAgICAgICAgICAgdmFyIG5ld1JpZ2h0ID0gTWF0aC5taW4ocmlnaHQsIE1hdGguZmxvb3IoayArIChuIC0gbSkgKiBzIC8gbiArIHNkKSk7XG4gICAgICAgICAgICBwYXJ0aWFsU29ydChhcnIsIGssIG5ld0xlZnQsIG5ld1JpZ2h0LCBjb21wYXJlKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciB0ID0gYXJyW2tdO1xuICAgICAgICB2YXIgaSA9IGxlZnQ7XG4gICAgICAgIHZhciBqID0gcmlnaHQ7XG5cbiAgICAgICAgc3dhcChhcnIsIGxlZnQsIGspO1xuICAgICAgICBpZiAoY29tcGFyZShhcnJbcmlnaHRdLCB0KSA+IDApIHN3YXAoYXJyLCBsZWZ0LCByaWdodCk7XG5cbiAgICAgICAgd2hpbGUgKGkgPCBqKSB7XG4gICAgICAgICAgICBzd2FwKGFyciwgaSwgaik7XG4gICAgICAgICAgICBpKys7XG4gICAgICAgICAgICBqLS07XG4gICAgICAgICAgICB3aGlsZSAoY29tcGFyZShhcnJbaV0sIHQpIDwgMCkgaSsrO1xuICAgICAgICAgICAgd2hpbGUgKGNvbXBhcmUoYXJyW2pdLCB0KSA+IDApIGotLTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjb21wYXJlKGFycltsZWZ0XSwgdCkgPT09IDApIHN3YXAoYXJyLCBsZWZ0LCBqKTtcbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBqKys7XG4gICAgICAgICAgICBzd2FwKGFyciwgaiwgcmlnaHQpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGogPD0gaykgbGVmdCA9IGogKyAxO1xuICAgICAgICBpZiAoayA8PSBqKSByaWdodCA9IGogLSAxO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gc3dhcChhcnIsIGksIGopIHtcbiAgICB2YXIgdG1wID0gYXJyW2ldO1xuICAgIGFycltpXSA9IGFycltqXTtcbiAgICBhcnJbal0gPSB0bXA7XG59XG5cbmZ1bmN0aW9uIGRlZmF1bHRDb21wYXJlKGEsIGIpIHtcbiAgICByZXR1cm4gYSA8IGIgPyAtMSA6IGEgPiBiID8gMSA6IDA7XG59XG4iLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0gcmJ1c2g7XG5cbnZhciBxdWlja3NlbGVjdCA9IHJlcXVpcmUoJ3F1aWNrc2VsZWN0Jyk7XG5cbmZ1bmN0aW9uIHJidXNoKG1heEVudHJpZXMsIGZvcm1hdCkge1xuICAgIGlmICghKHRoaXMgaW5zdGFuY2VvZiByYnVzaCkpIHJldHVybiBuZXcgcmJ1c2gobWF4RW50cmllcywgZm9ybWF0KTtcblxuICAgIC8vIG1heCBlbnRyaWVzIGluIGEgbm9kZSBpcyA5IGJ5IGRlZmF1bHQ7IG1pbiBub2RlIGZpbGwgaXMgNDAlIGZvciBiZXN0IHBlcmZvcm1hbmNlXG4gICAgdGhpcy5fbWF4RW50cmllcyA9IE1hdGgubWF4KDQsIG1heEVudHJpZXMgfHwgOSk7XG4gICAgdGhpcy5fbWluRW50cmllcyA9IE1hdGgubWF4KDIsIE1hdGguY2VpbCh0aGlzLl9tYXhFbnRyaWVzICogMC40KSk7XG5cbiAgICBpZiAoZm9ybWF0KSB7XG4gICAgICAgIHRoaXMuX2luaXRGb3JtYXQoZm9ybWF0KTtcbiAgICB9XG5cbiAgICB0aGlzLmNsZWFyKCk7XG59XG5cbnJidXNoLnByb3RvdHlwZSA9IHtcblxuICAgIGFsbDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fYWxsKHRoaXMuZGF0YSwgW10pO1xuICAgIH0sXG5cbiAgICBzZWFyY2g6IGZ1bmN0aW9uIChiYm94KSB7XG5cbiAgICAgICAgdmFyIG5vZGUgPSB0aGlzLmRhdGEsXG4gICAgICAgICAgICByZXN1bHQgPSBbXSxcbiAgICAgICAgICAgIHRvQkJveCA9IHRoaXMudG9CQm94O1xuXG4gICAgICAgIGlmICghaW50ZXJzZWN0cyhiYm94LCBub2RlKSkgcmV0dXJuIHJlc3VsdDtcblxuICAgICAgICB2YXIgbm9kZXNUb1NlYXJjaCA9IFtdLFxuICAgICAgICAgICAgaSwgbGVuLCBjaGlsZCwgY2hpbGRCQm94O1xuXG4gICAgICAgIHdoaWxlIChub2RlKSB7XG4gICAgICAgICAgICBmb3IgKGkgPSAwLCBsZW4gPSBub2RlLmNoaWxkcmVuLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cbiAgICAgICAgICAgICAgICBjaGlsZCA9IG5vZGUuY2hpbGRyZW5baV07XG4gICAgICAgICAgICAgICAgY2hpbGRCQm94ID0gbm9kZS5sZWFmID8gdG9CQm94KGNoaWxkKSA6IGNoaWxkO1xuXG4gICAgICAgICAgICAgICAgaWYgKGludGVyc2VjdHMoYmJveCwgY2hpbGRCQm94KSkge1xuICAgICAgICAgICAgICAgICAgICBpZiAobm9kZS5sZWFmKSByZXN1bHQucHVzaChjaGlsZCk7XG4gICAgICAgICAgICAgICAgICAgIGVsc2UgaWYgKGNvbnRhaW5zKGJib3gsIGNoaWxkQkJveCkpIHRoaXMuX2FsbChjaGlsZCwgcmVzdWx0KTtcbiAgICAgICAgICAgICAgICAgICAgZWxzZSBub2Rlc1RvU2VhcmNoLnB1c2goY2hpbGQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG5vZGUgPSBub2Rlc1RvU2VhcmNoLnBvcCgpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9LFxuXG4gICAgY29sbGlkZXM6IGZ1bmN0aW9uIChiYm94KSB7XG5cbiAgICAgICAgdmFyIG5vZGUgPSB0aGlzLmRhdGEsXG4gICAgICAgICAgICB0b0JCb3ggPSB0aGlzLnRvQkJveDtcblxuICAgICAgICBpZiAoIWludGVyc2VjdHMoYmJveCwgbm9kZSkpIHJldHVybiBmYWxzZTtcblxuICAgICAgICB2YXIgbm9kZXNUb1NlYXJjaCA9IFtdLFxuICAgICAgICAgICAgaSwgbGVuLCBjaGlsZCwgY2hpbGRCQm94O1xuXG4gICAgICAgIHdoaWxlIChub2RlKSB7XG4gICAgICAgICAgICBmb3IgKGkgPSAwLCBsZW4gPSBub2RlLmNoaWxkcmVuLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cbiAgICAgICAgICAgICAgICBjaGlsZCA9IG5vZGUuY2hpbGRyZW5baV07XG4gICAgICAgICAgICAgICAgY2hpbGRCQm94ID0gbm9kZS5sZWFmID8gdG9CQm94KGNoaWxkKSA6IGNoaWxkO1xuXG4gICAgICAgICAgICAgICAgaWYgKGludGVyc2VjdHMoYmJveCwgY2hpbGRCQm94KSkge1xuICAgICAgICAgICAgICAgICAgICBpZiAobm9kZS5sZWFmIHx8IGNvbnRhaW5zKGJib3gsIGNoaWxkQkJveCkpIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgICAgICBub2Rlc1RvU2VhcmNoLnB1c2goY2hpbGQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG5vZGUgPSBub2Rlc1RvU2VhcmNoLnBvcCgpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH0sXG5cbiAgICBsb2FkOiBmdW5jdGlvbiAoZGF0YSkge1xuICAgICAgICBpZiAoIShkYXRhICYmIGRhdGEubGVuZ3RoKSkgcmV0dXJuIHRoaXM7XG5cbiAgICAgICAgaWYgKGRhdGEubGVuZ3RoIDwgdGhpcy5fbWluRW50cmllcykge1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDAsIGxlbiA9IGRhdGEubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgICAgICAgICB0aGlzLmluc2VydChkYXRhW2ldKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gcmVjdXJzaXZlbHkgYnVpbGQgdGhlIHRyZWUgd2l0aCB0aGUgZ2l2ZW4gZGF0YSBmcm9tIHN0cmF0Y2ggdXNpbmcgT01UIGFsZ29yaXRobVxuICAgICAgICB2YXIgbm9kZSA9IHRoaXMuX2J1aWxkKGRhdGEuc2xpY2UoKSwgMCwgZGF0YS5sZW5ndGggLSAxLCAwKTtcblxuICAgICAgICBpZiAoIXRoaXMuZGF0YS5jaGlsZHJlbi5sZW5ndGgpIHtcbiAgICAgICAgICAgIC8vIHNhdmUgYXMgaXMgaWYgdHJlZSBpcyBlbXB0eVxuICAgICAgICAgICAgdGhpcy5kYXRhID0gbm9kZTtcblxuICAgICAgICB9IGVsc2UgaWYgKHRoaXMuZGF0YS5oZWlnaHQgPT09IG5vZGUuaGVpZ2h0KSB7XG4gICAgICAgICAgICAvLyBzcGxpdCByb290IGlmIHRyZWVzIGhhdmUgdGhlIHNhbWUgaGVpZ2h0XG4gICAgICAgICAgICB0aGlzLl9zcGxpdFJvb3QodGhpcy5kYXRhLCBub2RlKTtcblxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKHRoaXMuZGF0YS5oZWlnaHQgPCBub2RlLmhlaWdodCkge1xuICAgICAgICAgICAgICAgIC8vIHN3YXAgdHJlZXMgaWYgaW5zZXJ0ZWQgb25lIGlzIGJpZ2dlclxuICAgICAgICAgICAgICAgIHZhciB0bXBOb2RlID0gdGhpcy5kYXRhO1xuICAgICAgICAgICAgICAgIHRoaXMuZGF0YSA9IG5vZGU7XG4gICAgICAgICAgICAgICAgbm9kZSA9IHRtcE5vZGU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIGluc2VydCB0aGUgc21hbGwgdHJlZSBpbnRvIHRoZSBsYXJnZSB0cmVlIGF0IGFwcHJvcHJpYXRlIGxldmVsXG4gICAgICAgICAgICB0aGlzLl9pbnNlcnQobm9kZSwgdGhpcy5kYXRhLmhlaWdodCAtIG5vZGUuaGVpZ2h0IC0gMSwgdHJ1ZSk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgaW5zZXJ0OiBmdW5jdGlvbiAoaXRlbSkge1xuICAgICAgICBpZiAoaXRlbSkgdGhpcy5faW5zZXJ0KGl0ZW0sIHRoaXMuZGF0YS5oZWlnaHQgLSAxKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIGNsZWFyOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHRoaXMuZGF0YSA9IGNyZWF0ZU5vZGUoW10pO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgcmVtb3ZlOiBmdW5jdGlvbiAoaXRlbSwgZXF1YWxzRm4pIHtcbiAgICAgICAgaWYgKCFpdGVtKSByZXR1cm4gdGhpcztcblxuICAgICAgICB2YXIgbm9kZSA9IHRoaXMuZGF0YSxcbiAgICAgICAgICAgIGJib3ggPSB0aGlzLnRvQkJveChpdGVtKSxcbiAgICAgICAgICAgIHBhdGggPSBbXSxcbiAgICAgICAgICAgIGluZGV4ZXMgPSBbXSxcbiAgICAgICAgICAgIGksIHBhcmVudCwgaW5kZXgsIGdvaW5nVXA7XG5cbiAgICAgICAgLy8gZGVwdGgtZmlyc3QgaXRlcmF0aXZlIHRyZWUgdHJhdmVyc2FsXG4gICAgICAgIHdoaWxlIChub2RlIHx8IHBhdGgubGVuZ3RoKSB7XG5cbiAgICAgICAgICAgIGlmICghbm9kZSkgeyAvLyBnbyB1cFxuICAgICAgICAgICAgICAgIG5vZGUgPSBwYXRoLnBvcCgpO1xuICAgICAgICAgICAgICAgIHBhcmVudCA9IHBhdGhbcGF0aC5sZW5ndGggLSAxXTtcbiAgICAgICAgICAgICAgICBpID0gaW5kZXhlcy5wb3AoKTtcbiAgICAgICAgICAgICAgICBnb2luZ1VwID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKG5vZGUubGVhZikgeyAvLyBjaGVjayBjdXJyZW50IG5vZGVcbiAgICAgICAgICAgICAgICBpbmRleCA9IGZpbmRJdGVtKGl0ZW0sIG5vZGUuY2hpbGRyZW4sIGVxdWFsc0ZuKTtcblxuICAgICAgICAgICAgICAgIGlmIChpbmRleCAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gaXRlbSBmb3VuZCwgcmVtb3ZlIHRoZSBpdGVtIGFuZCBjb25kZW5zZSB0cmVlIHVwd2FyZHNcbiAgICAgICAgICAgICAgICAgICAgbm9kZS5jaGlsZHJlbi5zcGxpY2UoaW5kZXgsIDEpO1xuICAgICAgICAgICAgICAgICAgICBwYXRoLnB1c2gobm9kZSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2NvbmRlbnNlKHBhdGgpO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghZ29pbmdVcCAmJiAhbm9kZS5sZWFmICYmIGNvbnRhaW5zKG5vZGUsIGJib3gpKSB7IC8vIGdvIGRvd25cbiAgICAgICAgICAgICAgICBwYXRoLnB1c2gobm9kZSk7XG4gICAgICAgICAgICAgICAgaW5kZXhlcy5wdXNoKGkpO1xuICAgICAgICAgICAgICAgIGkgPSAwO1xuICAgICAgICAgICAgICAgIHBhcmVudCA9IG5vZGU7XG4gICAgICAgICAgICAgICAgbm9kZSA9IG5vZGUuY2hpbGRyZW5bMF07XG5cbiAgICAgICAgICAgIH0gZWxzZSBpZiAocGFyZW50KSB7IC8vIGdvIHJpZ2h0XG4gICAgICAgICAgICAgICAgaSsrO1xuICAgICAgICAgICAgICAgIG5vZGUgPSBwYXJlbnQuY2hpbGRyZW5baV07XG4gICAgICAgICAgICAgICAgZ29pbmdVcCA9IGZhbHNlO1xuXG4gICAgICAgICAgICB9IGVsc2Ugbm9kZSA9IG51bGw7IC8vIG5vdGhpbmcgZm91bmRcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICB0b0JCb3g6IGZ1bmN0aW9uIChpdGVtKSB7IHJldHVybiBpdGVtOyB9LFxuXG4gICAgY29tcGFyZU1pblg6IGNvbXBhcmVOb2RlTWluWCxcbiAgICBjb21wYXJlTWluWTogY29tcGFyZU5vZGVNaW5ZLFxuXG4gICAgdG9KU09OOiBmdW5jdGlvbiAoKSB7IHJldHVybiB0aGlzLmRhdGE7IH0sXG5cbiAgICBmcm9tSlNPTjogZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgICAgdGhpcy5kYXRhID0gZGF0YTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9hbGw6IGZ1bmN0aW9uIChub2RlLCByZXN1bHQpIHtcbiAgICAgICAgdmFyIG5vZGVzVG9TZWFyY2ggPSBbXTtcbiAgICAgICAgd2hpbGUgKG5vZGUpIHtcbiAgICAgICAgICAgIGlmIChub2RlLmxlYWYpIHJlc3VsdC5wdXNoLmFwcGx5KHJlc3VsdCwgbm9kZS5jaGlsZHJlbik7XG4gICAgICAgICAgICBlbHNlIG5vZGVzVG9TZWFyY2gucHVzaC5hcHBseShub2Rlc1RvU2VhcmNoLCBub2RlLmNoaWxkcmVuKTtcblxuICAgICAgICAgICAgbm9kZSA9IG5vZGVzVG9TZWFyY2gucG9wKCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9LFxuXG4gICAgX2J1aWxkOiBmdW5jdGlvbiAoaXRlbXMsIGxlZnQsIHJpZ2h0LCBoZWlnaHQpIHtcblxuICAgICAgICB2YXIgTiA9IHJpZ2h0IC0gbGVmdCArIDEsXG4gICAgICAgICAgICBNID0gdGhpcy5fbWF4RW50cmllcyxcbiAgICAgICAgICAgIG5vZGU7XG5cbiAgICAgICAgaWYgKE4gPD0gTSkge1xuICAgICAgICAgICAgLy8gcmVhY2hlZCBsZWFmIGxldmVsOyByZXR1cm4gbGVhZlxuICAgICAgICAgICAgbm9kZSA9IGNyZWF0ZU5vZGUoaXRlbXMuc2xpY2UobGVmdCwgcmlnaHQgKyAxKSk7XG4gICAgICAgICAgICBjYWxjQkJveChub2RlLCB0aGlzLnRvQkJveCk7XG4gICAgICAgICAgICByZXR1cm4gbm9kZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghaGVpZ2h0KSB7XG4gICAgICAgICAgICAvLyB0YXJnZXQgaGVpZ2h0IG9mIHRoZSBidWxrLWxvYWRlZCB0cmVlXG4gICAgICAgICAgICBoZWlnaHQgPSBNYXRoLmNlaWwoTWF0aC5sb2coTikgLyBNYXRoLmxvZyhNKSk7XG5cbiAgICAgICAgICAgIC8vIHRhcmdldCBudW1iZXIgb2Ygcm9vdCBlbnRyaWVzIHRvIG1heGltaXplIHN0b3JhZ2UgdXRpbGl6YXRpb25cbiAgICAgICAgICAgIE0gPSBNYXRoLmNlaWwoTiAvIE1hdGgucG93KE0sIGhlaWdodCAtIDEpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIG5vZGUgPSBjcmVhdGVOb2RlKFtdKTtcbiAgICAgICAgbm9kZS5sZWFmID0gZmFsc2U7XG4gICAgICAgIG5vZGUuaGVpZ2h0ID0gaGVpZ2h0O1xuXG4gICAgICAgIC8vIHNwbGl0IHRoZSBpdGVtcyBpbnRvIE0gbW9zdGx5IHNxdWFyZSB0aWxlc1xuXG4gICAgICAgIHZhciBOMiA9IE1hdGguY2VpbChOIC8gTSksXG4gICAgICAgICAgICBOMSA9IE4yICogTWF0aC5jZWlsKE1hdGguc3FydChNKSksXG4gICAgICAgICAgICBpLCBqLCByaWdodDIsIHJpZ2h0MztcblxuICAgICAgICBtdWx0aVNlbGVjdChpdGVtcywgbGVmdCwgcmlnaHQsIE4xLCB0aGlzLmNvbXBhcmVNaW5YKTtcblxuICAgICAgICBmb3IgKGkgPSBsZWZ0OyBpIDw9IHJpZ2h0OyBpICs9IE4xKSB7XG5cbiAgICAgICAgICAgIHJpZ2h0MiA9IE1hdGgubWluKGkgKyBOMSAtIDEsIHJpZ2h0KTtcblxuICAgICAgICAgICAgbXVsdGlTZWxlY3QoaXRlbXMsIGksIHJpZ2h0MiwgTjIsIHRoaXMuY29tcGFyZU1pblkpO1xuXG4gICAgICAgICAgICBmb3IgKGogPSBpOyBqIDw9IHJpZ2h0MjsgaiArPSBOMikge1xuXG4gICAgICAgICAgICAgICAgcmlnaHQzID0gTWF0aC5taW4oaiArIE4yIC0gMSwgcmlnaHQyKTtcblxuICAgICAgICAgICAgICAgIC8vIHBhY2sgZWFjaCBlbnRyeSByZWN1cnNpdmVseVxuICAgICAgICAgICAgICAgIG5vZGUuY2hpbGRyZW4ucHVzaCh0aGlzLl9idWlsZChpdGVtcywgaiwgcmlnaHQzLCBoZWlnaHQgLSAxKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjYWxjQkJveChub2RlLCB0aGlzLnRvQkJveCk7XG5cbiAgICAgICAgcmV0dXJuIG5vZGU7XG4gICAgfSxcblxuICAgIF9jaG9vc2VTdWJ0cmVlOiBmdW5jdGlvbiAoYmJveCwgbm9kZSwgbGV2ZWwsIHBhdGgpIHtcblxuICAgICAgICB2YXIgaSwgbGVuLCBjaGlsZCwgdGFyZ2V0Tm9kZSwgYXJlYSwgZW5sYXJnZW1lbnQsIG1pbkFyZWEsIG1pbkVubGFyZ2VtZW50O1xuXG4gICAgICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAgICAgICBwYXRoLnB1c2gobm9kZSk7XG5cbiAgICAgICAgICAgIGlmIChub2RlLmxlYWYgfHwgcGF0aC5sZW5ndGggLSAxID09PSBsZXZlbCkgYnJlYWs7XG5cbiAgICAgICAgICAgIG1pbkFyZWEgPSBtaW5FbmxhcmdlbWVudCA9IEluZmluaXR5O1xuXG4gICAgICAgICAgICBmb3IgKGkgPSAwLCBsZW4gPSBub2RlLmNoaWxkcmVuLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICAgICAgICAgICAgY2hpbGQgPSBub2RlLmNoaWxkcmVuW2ldO1xuICAgICAgICAgICAgICAgIGFyZWEgPSBiYm94QXJlYShjaGlsZCk7XG4gICAgICAgICAgICAgICAgZW5sYXJnZW1lbnQgPSBlbmxhcmdlZEFyZWEoYmJveCwgY2hpbGQpIC0gYXJlYTtcblxuICAgICAgICAgICAgICAgIC8vIGNob29zZSBlbnRyeSB3aXRoIHRoZSBsZWFzdCBhcmVhIGVubGFyZ2VtZW50XG4gICAgICAgICAgICAgICAgaWYgKGVubGFyZ2VtZW50IDwgbWluRW5sYXJnZW1lbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgbWluRW5sYXJnZW1lbnQgPSBlbmxhcmdlbWVudDtcbiAgICAgICAgICAgICAgICAgICAgbWluQXJlYSA9IGFyZWEgPCBtaW5BcmVhID8gYXJlYSA6IG1pbkFyZWE7XG4gICAgICAgICAgICAgICAgICAgIHRhcmdldE5vZGUgPSBjaGlsZDtcblxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoZW5sYXJnZW1lbnQgPT09IG1pbkVubGFyZ2VtZW50KSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIG90aGVyd2lzZSBjaG9vc2Ugb25lIHdpdGggdGhlIHNtYWxsZXN0IGFyZWFcbiAgICAgICAgICAgICAgICAgICAgaWYgKGFyZWEgPCBtaW5BcmVhKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtaW5BcmVhID0gYXJlYTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldE5vZGUgPSBjaGlsZDtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbm9kZSA9IHRhcmdldE5vZGUgfHwgbm9kZS5jaGlsZHJlblswXTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBub2RlO1xuICAgIH0sXG5cbiAgICBfaW5zZXJ0OiBmdW5jdGlvbiAoaXRlbSwgbGV2ZWwsIGlzTm9kZSkge1xuXG4gICAgICAgIHZhciB0b0JCb3ggPSB0aGlzLnRvQkJveCxcbiAgICAgICAgICAgIGJib3ggPSBpc05vZGUgPyBpdGVtIDogdG9CQm94KGl0ZW0pLFxuICAgICAgICAgICAgaW5zZXJ0UGF0aCA9IFtdO1xuXG4gICAgICAgIC8vIGZpbmQgdGhlIGJlc3Qgbm9kZSBmb3IgYWNjb21tb2RhdGluZyB0aGUgaXRlbSwgc2F2aW5nIGFsbCBub2RlcyBhbG9uZyB0aGUgcGF0aCB0b29cbiAgICAgICAgdmFyIG5vZGUgPSB0aGlzLl9jaG9vc2VTdWJ0cmVlKGJib3gsIHRoaXMuZGF0YSwgbGV2ZWwsIGluc2VydFBhdGgpO1xuXG4gICAgICAgIC8vIHB1dCB0aGUgaXRlbSBpbnRvIHRoZSBub2RlXG4gICAgICAgIG5vZGUuY2hpbGRyZW4ucHVzaChpdGVtKTtcbiAgICAgICAgZXh0ZW5kKG5vZGUsIGJib3gpO1xuXG4gICAgICAgIC8vIHNwbGl0IG9uIG5vZGUgb3ZlcmZsb3c7IHByb3BhZ2F0ZSB1cHdhcmRzIGlmIG5lY2Vzc2FyeVxuICAgICAgICB3aGlsZSAobGV2ZWwgPj0gMCkge1xuICAgICAgICAgICAgaWYgKGluc2VydFBhdGhbbGV2ZWxdLmNoaWxkcmVuLmxlbmd0aCA+IHRoaXMuX21heEVudHJpZXMpIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9zcGxpdChpbnNlcnRQYXRoLCBsZXZlbCk7XG4gICAgICAgICAgICAgICAgbGV2ZWwtLTtcbiAgICAgICAgICAgIH0gZWxzZSBicmVhaztcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGFkanVzdCBiYm94ZXMgYWxvbmcgdGhlIGluc2VydGlvbiBwYXRoXG4gICAgICAgIHRoaXMuX2FkanVzdFBhcmVudEJCb3hlcyhiYm94LCBpbnNlcnRQYXRoLCBsZXZlbCk7XG4gICAgfSxcblxuICAgIC8vIHNwbGl0IG92ZXJmbG93ZWQgbm9kZSBpbnRvIHR3b1xuICAgIF9zcGxpdDogZnVuY3Rpb24gKGluc2VydFBhdGgsIGxldmVsKSB7XG5cbiAgICAgICAgdmFyIG5vZGUgPSBpbnNlcnRQYXRoW2xldmVsXSxcbiAgICAgICAgICAgIE0gPSBub2RlLmNoaWxkcmVuLmxlbmd0aCxcbiAgICAgICAgICAgIG0gPSB0aGlzLl9taW5FbnRyaWVzO1xuXG4gICAgICAgIHRoaXMuX2Nob29zZVNwbGl0QXhpcyhub2RlLCBtLCBNKTtcblxuICAgICAgICB2YXIgc3BsaXRJbmRleCA9IHRoaXMuX2Nob29zZVNwbGl0SW5kZXgobm9kZSwgbSwgTSk7XG5cbiAgICAgICAgdmFyIG5ld05vZGUgPSBjcmVhdGVOb2RlKG5vZGUuY2hpbGRyZW4uc3BsaWNlKHNwbGl0SW5kZXgsIG5vZGUuY2hpbGRyZW4ubGVuZ3RoIC0gc3BsaXRJbmRleCkpO1xuICAgICAgICBuZXdOb2RlLmhlaWdodCA9IG5vZGUuaGVpZ2h0O1xuICAgICAgICBuZXdOb2RlLmxlYWYgPSBub2RlLmxlYWY7XG5cbiAgICAgICAgY2FsY0JCb3gobm9kZSwgdGhpcy50b0JCb3gpO1xuICAgICAgICBjYWxjQkJveChuZXdOb2RlLCB0aGlzLnRvQkJveCk7XG5cbiAgICAgICAgaWYgKGxldmVsKSBpbnNlcnRQYXRoW2xldmVsIC0gMV0uY2hpbGRyZW4ucHVzaChuZXdOb2RlKTtcbiAgICAgICAgZWxzZSB0aGlzLl9zcGxpdFJvb3Qobm9kZSwgbmV3Tm9kZSk7XG4gICAgfSxcblxuICAgIF9zcGxpdFJvb3Q6IGZ1bmN0aW9uIChub2RlLCBuZXdOb2RlKSB7XG4gICAgICAgIC8vIHNwbGl0IHJvb3Qgbm9kZVxuICAgICAgICB0aGlzLmRhdGEgPSBjcmVhdGVOb2RlKFtub2RlLCBuZXdOb2RlXSk7XG4gICAgICAgIHRoaXMuZGF0YS5oZWlnaHQgPSBub2RlLmhlaWdodCArIDE7XG4gICAgICAgIHRoaXMuZGF0YS5sZWFmID0gZmFsc2U7XG4gICAgICAgIGNhbGNCQm94KHRoaXMuZGF0YSwgdGhpcy50b0JCb3gpO1xuICAgIH0sXG5cbiAgICBfY2hvb3NlU3BsaXRJbmRleDogZnVuY3Rpb24gKG5vZGUsIG0sIE0pIHtcblxuICAgICAgICB2YXIgaSwgYmJveDEsIGJib3gyLCBvdmVybGFwLCBhcmVhLCBtaW5PdmVybGFwLCBtaW5BcmVhLCBpbmRleDtcblxuICAgICAgICBtaW5PdmVybGFwID0gbWluQXJlYSA9IEluZmluaXR5O1xuXG4gICAgICAgIGZvciAoaSA9IG07IGkgPD0gTSAtIG07IGkrKykge1xuICAgICAgICAgICAgYmJveDEgPSBkaXN0QkJveChub2RlLCAwLCBpLCB0aGlzLnRvQkJveCk7XG4gICAgICAgICAgICBiYm94MiA9IGRpc3RCQm94KG5vZGUsIGksIE0sIHRoaXMudG9CQm94KTtcblxuICAgICAgICAgICAgb3ZlcmxhcCA9IGludGVyc2VjdGlvbkFyZWEoYmJveDEsIGJib3gyKTtcbiAgICAgICAgICAgIGFyZWEgPSBiYm94QXJlYShiYm94MSkgKyBiYm94QXJlYShiYm94Mik7XG5cbiAgICAgICAgICAgIC8vIGNob29zZSBkaXN0cmlidXRpb24gd2l0aCBtaW5pbXVtIG92ZXJsYXBcbiAgICAgICAgICAgIGlmIChvdmVybGFwIDwgbWluT3ZlcmxhcCkge1xuICAgICAgICAgICAgICAgIG1pbk92ZXJsYXAgPSBvdmVybGFwO1xuICAgICAgICAgICAgICAgIGluZGV4ID0gaTtcblxuICAgICAgICAgICAgICAgIG1pbkFyZWEgPSBhcmVhIDwgbWluQXJlYSA/IGFyZWEgOiBtaW5BcmVhO1xuXG4gICAgICAgICAgICB9IGVsc2UgaWYgKG92ZXJsYXAgPT09IG1pbk92ZXJsYXApIHtcbiAgICAgICAgICAgICAgICAvLyBvdGhlcndpc2UgY2hvb3NlIGRpc3RyaWJ1dGlvbiB3aXRoIG1pbmltdW0gYXJlYVxuICAgICAgICAgICAgICAgIGlmIChhcmVhIDwgbWluQXJlYSkge1xuICAgICAgICAgICAgICAgICAgICBtaW5BcmVhID0gYXJlYTtcbiAgICAgICAgICAgICAgICAgICAgaW5kZXggPSBpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBpbmRleDtcbiAgICB9LFxuXG4gICAgLy8gc29ydHMgbm9kZSBjaGlsZHJlbiBieSB0aGUgYmVzdCBheGlzIGZvciBzcGxpdFxuICAgIF9jaG9vc2VTcGxpdEF4aXM6IGZ1bmN0aW9uIChub2RlLCBtLCBNKSB7XG5cbiAgICAgICAgdmFyIGNvbXBhcmVNaW5YID0gbm9kZS5sZWFmID8gdGhpcy5jb21wYXJlTWluWCA6IGNvbXBhcmVOb2RlTWluWCxcbiAgICAgICAgICAgIGNvbXBhcmVNaW5ZID0gbm9kZS5sZWFmID8gdGhpcy5jb21wYXJlTWluWSA6IGNvbXBhcmVOb2RlTWluWSxcbiAgICAgICAgICAgIHhNYXJnaW4gPSB0aGlzLl9hbGxEaXN0TWFyZ2luKG5vZGUsIG0sIE0sIGNvbXBhcmVNaW5YKSxcbiAgICAgICAgICAgIHlNYXJnaW4gPSB0aGlzLl9hbGxEaXN0TWFyZ2luKG5vZGUsIG0sIE0sIGNvbXBhcmVNaW5ZKTtcblxuICAgICAgICAvLyBpZiB0b3RhbCBkaXN0cmlidXRpb25zIG1hcmdpbiB2YWx1ZSBpcyBtaW5pbWFsIGZvciB4LCBzb3J0IGJ5IG1pblgsXG4gICAgICAgIC8vIG90aGVyd2lzZSBpdCdzIGFscmVhZHkgc29ydGVkIGJ5IG1pbllcbiAgICAgICAgaWYgKHhNYXJnaW4gPCB5TWFyZ2luKSBub2RlLmNoaWxkcmVuLnNvcnQoY29tcGFyZU1pblgpO1xuICAgIH0sXG5cbiAgICAvLyB0b3RhbCBtYXJnaW4gb2YgYWxsIHBvc3NpYmxlIHNwbGl0IGRpc3RyaWJ1dGlvbnMgd2hlcmUgZWFjaCBub2RlIGlzIGF0IGxlYXN0IG0gZnVsbFxuICAgIF9hbGxEaXN0TWFyZ2luOiBmdW5jdGlvbiAobm9kZSwgbSwgTSwgY29tcGFyZSkge1xuXG4gICAgICAgIG5vZGUuY2hpbGRyZW4uc29ydChjb21wYXJlKTtcblxuICAgICAgICB2YXIgdG9CQm94ID0gdGhpcy50b0JCb3gsXG4gICAgICAgICAgICBsZWZ0QkJveCA9IGRpc3RCQm94KG5vZGUsIDAsIG0sIHRvQkJveCksXG4gICAgICAgICAgICByaWdodEJCb3ggPSBkaXN0QkJveChub2RlLCBNIC0gbSwgTSwgdG9CQm94KSxcbiAgICAgICAgICAgIG1hcmdpbiA9IGJib3hNYXJnaW4obGVmdEJCb3gpICsgYmJveE1hcmdpbihyaWdodEJCb3gpLFxuICAgICAgICAgICAgaSwgY2hpbGQ7XG5cbiAgICAgICAgZm9yIChpID0gbTsgaSA8IE0gLSBtOyBpKyspIHtcbiAgICAgICAgICAgIGNoaWxkID0gbm9kZS5jaGlsZHJlbltpXTtcbiAgICAgICAgICAgIGV4dGVuZChsZWZ0QkJveCwgbm9kZS5sZWFmID8gdG9CQm94KGNoaWxkKSA6IGNoaWxkKTtcbiAgICAgICAgICAgIG1hcmdpbiArPSBiYm94TWFyZ2luKGxlZnRCQm94KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvciAoaSA9IE0gLSBtIC0gMTsgaSA+PSBtOyBpLS0pIHtcbiAgICAgICAgICAgIGNoaWxkID0gbm9kZS5jaGlsZHJlbltpXTtcbiAgICAgICAgICAgIGV4dGVuZChyaWdodEJCb3gsIG5vZGUubGVhZiA/IHRvQkJveChjaGlsZCkgOiBjaGlsZCk7XG4gICAgICAgICAgICBtYXJnaW4gKz0gYmJveE1hcmdpbihyaWdodEJCb3gpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG1hcmdpbjtcbiAgICB9LFxuXG4gICAgX2FkanVzdFBhcmVudEJCb3hlczogZnVuY3Rpb24gKGJib3gsIHBhdGgsIGxldmVsKSB7XG4gICAgICAgIC8vIGFkanVzdCBiYm94ZXMgYWxvbmcgdGhlIGdpdmVuIHRyZWUgcGF0aFxuICAgICAgICBmb3IgKHZhciBpID0gbGV2ZWw7IGkgPj0gMDsgaS0tKSB7XG4gICAgICAgICAgICBleHRlbmQocGF0aFtpXSwgYmJveCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgX2NvbmRlbnNlOiBmdW5jdGlvbiAocGF0aCkge1xuICAgICAgICAvLyBnbyB0aHJvdWdoIHRoZSBwYXRoLCByZW1vdmluZyBlbXB0eSBub2RlcyBhbmQgdXBkYXRpbmcgYmJveGVzXG4gICAgICAgIGZvciAodmFyIGkgPSBwYXRoLmxlbmd0aCAtIDEsIHNpYmxpbmdzOyBpID49IDA7IGktLSkge1xuICAgICAgICAgICAgaWYgKHBhdGhbaV0uY2hpbGRyZW4ubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgICAgaWYgKGkgPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHNpYmxpbmdzID0gcGF0aFtpIC0gMV0uY2hpbGRyZW47XG4gICAgICAgICAgICAgICAgICAgIHNpYmxpbmdzLnNwbGljZShzaWJsaW5ncy5pbmRleE9mKHBhdGhbaV0pLCAxKTtcblxuICAgICAgICAgICAgICAgIH0gZWxzZSB0aGlzLmNsZWFyKCk7XG5cbiAgICAgICAgICAgIH0gZWxzZSBjYWxjQkJveChwYXRoW2ldLCB0aGlzLnRvQkJveCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgX2luaXRGb3JtYXQ6IGZ1bmN0aW9uIChmb3JtYXQpIHtcbiAgICAgICAgLy8gZGF0YSBmb3JtYXQgKG1pblgsIG1pblksIG1heFgsIG1heFkgYWNjZXNzb3JzKVxuXG4gICAgICAgIC8vIHVzZXMgZXZhbC10eXBlIGZ1bmN0aW9uIGNvbXBpbGF0aW9uIGluc3RlYWQgb2YganVzdCBhY2NlcHRpbmcgYSB0b0JCb3ggZnVuY3Rpb25cbiAgICAgICAgLy8gYmVjYXVzZSB0aGUgYWxnb3JpdGhtcyBhcmUgdmVyeSBzZW5zaXRpdmUgdG8gc29ydGluZyBmdW5jdGlvbnMgcGVyZm9ybWFuY2UsXG4gICAgICAgIC8vIHNvIHRoZXkgc2hvdWxkIGJlIGRlYWQgc2ltcGxlIGFuZCB3aXRob3V0IGlubmVyIGNhbGxzXG5cbiAgICAgICAgdmFyIGNvbXBhcmVBcnIgPSBbJ3JldHVybiBhJywgJyAtIGInLCAnOyddO1xuXG4gICAgICAgIHRoaXMuY29tcGFyZU1pblggPSBuZXcgRnVuY3Rpb24oJ2EnLCAnYicsIGNvbXBhcmVBcnIuam9pbihmb3JtYXRbMF0pKTtcbiAgICAgICAgdGhpcy5jb21wYXJlTWluWSA9IG5ldyBGdW5jdGlvbignYScsICdiJywgY29tcGFyZUFyci5qb2luKGZvcm1hdFsxXSkpO1xuXG4gICAgICAgIHRoaXMudG9CQm94ID0gbmV3IEZ1bmN0aW9uKCdhJyxcbiAgICAgICAgICAgICdyZXR1cm4ge21pblg6IGEnICsgZm9ybWF0WzBdICtcbiAgICAgICAgICAgICcsIG1pblk6IGEnICsgZm9ybWF0WzFdICtcbiAgICAgICAgICAgICcsIG1heFg6IGEnICsgZm9ybWF0WzJdICtcbiAgICAgICAgICAgICcsIG1heFk6IGEnICsgZm9ybWF0WzNdICsgJ307Jyk7XG4gICAgfVxufTtcblxuZnVuY3Rpb24gZmluZEl0ZW0oaXRlbSwgaXRlbXMsIGVxdWFsc0ZuKSB7XG4gICAgaWYgKCFlcXVhbHNGbikgcmV0dXJuIGl0ZW1zLmluZGV4T2YoaXRlbSk7XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGl0ZW1zLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmIChlcXVhbHNGbihpdGVtLCBpdGVtc1tpXSkpIHJldHVybiBpO1xuICAgIH1cbiAgICByZXR1cm4gLTE7XG59XG5cbi8vIGNhbGN1bGF0ZSBub2RlJ3MgYmJveCBmcm9tIGJib3hlcyBvZiBpdHMgY2hpbGRyZW5cbmZ1bmN0aW9uIGNhbGNCQm94KG5vZGUsIHRvQkJveCkge1xuICAgIGRpc3RCQm94KG5vZGUsIDAsIG5vZGUuY2hpbGRyZW4ubGVuZ3RoLCB0b0JCb3gsIG5vZGUpO1xufVxuXG4vLyBtaW4gYm91bmRpbmcgcmVjdGFuZ2xlIG9mIG5vZGUgY2hpbGRyZW4gZnJvbSBrIHRvIHAtMVxuZnVuY3Rpb24gZGlzdEJCb3gobm9kZSwgaywgcCwgdG9CQm94LCBkZXN0Tm9kZSkge1xuICAgIGlmICghZGVzdE5vZGUpIGRlc3ROb2RlID0gY3JlYXRlTm9kZShudWxsKTtcbiAgICBkZXN0Tm9kZS5taW5YID0gSW5maW5pdHk7XG4gICAgZGVzdE5vZGUubWluWSA9IEluZmluaXR5O1xuICAgIGRlc3ROb2RlLm1heFggPSAtSW5maW5pdHk7XG4gICAgZGVzdE5vZGUubWF4WSA9IC1JbmZpbml0eTtcblxuICAgIGZvciAodmFyIGkgPSBrLCBjaGlsZDsgaSA8IHA7IGkrKykge1xuICAgICAgICBjaGlsZCA9IG5vZGUuY2hpbGRyZW5baV07XG4gICAgICAgIGV4dGVuZChkZXN0Tm9kZSwgbm9kZS5sZWFmID8gdG9CQm94KGNoaWxkKSA6IGNoaWxkKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZGVzdE5vZGU7XG59XG5cbmZ1bmN0aW9uIGV4dGVuZChhLCBiKSB7XG4gICAgYS5taW5YID0gTWF0aC5taW4oYS5taW5YLCBiLm1pblgpO1xuICAgIGEubWluWSA9IE1hdGgubWluKGEubWluWSwgYi5taW5ZKTtcbiAgICBhLm1heFggPSBNYXRoLm1heChhLm1heFgsIGIubWF4WCk7XG4gICAgYS5tYXhZID0gTWF0aC5tYXgoYS5tYXhZLCBiLm1heFkpO1xuICAgIHJldHVybiBhO1xufVxuXG5mdW5jdGlvbiBjb21wYXJlTm9kZU1pblgoYSwgYikgeyByZXR1cm4gYS5taW5YIC0gYi5taW5YOyB9XG5mdW5jdGlvbiBjb21wYXJlTm9kZU1pblkoYSwgYikgeyByZXR1cm4gYS5taW5ZIC0gYi5taW5ZOyB9XG5cbmZ1bmN0aW9uIGJib3hBcmVhKGEpICAgeyByZXR1cm4gKGEubWF4WCAtIGEubWluWCkgKiAoYS5tYXhZIC0gYS5taW5ZKTsgfVxuZnVuY3Rpb24gYmJveE1hcmdpbihhKSB7IHJldHVybiAoYS5tYXhYIC0gYS5taW5YKSArIChhLm1heFkgLSBhLm1pblkpOyB9XG5cbmZ1bmN0aW9uIGVubGFyZ2VkQXJlYShhLCBiKSB7XG4gICAgcmV0dXJuIChNYXRoLm1heChiLm1heFgsIGEubWF4WCkgLSBNYXRoLm1pbihiLm1pblgsIGEubWluWCkpICpcbiAgICAgICAgICAgKE1hdGgubWF4KGIubWF4WSwgYS5tYXhZKSAtIE1hdGgubWluKGIubWluWSwgYS5taW5ZKSk7XG59XG5cbmZ1bmN0aW9uIGludGVyc2VjdGlvbkFyZWEoYSwgYikge1xuICAgIHZhciBtaW5YID0gTWF0aC5tYXgoYS5taW5YLCBiLm1pblgpLFxuICAgICAgICBtaW5ZID0gTWF0aC5tYXgoYS5taW5ZLCBiLm1pblkpLFxuICAgICAgICBtYXhYID0gTWF0aC5taW4oYS5tYXhYLCBiLm1heFgpLFxuICAgICAgICBtYXhZID0gTWF0aC5taW4oYS5tYXhZLCBiLm1heFkpO1xuXG4gICAgcmV0dXJuIE1hdGgubWF4KDAsIG1heFggLSBtaW5YKSAqXG4gICAgICAgICAgIE1hdGgubWF4KDAsIG1heFkgLSBtaW5ZKTtcbn1cblxuZnVuY3Rpb24gY29udGFpbnMoYSwgYikge1xuICAgIHJldHVybiBhLm1pblggPD0gYi5taW5YICYmXG4gICAgICAgICAgIGEubWluWSA8PSBiLm1pblkgJiZcbiAgICAgICAgICAgYi5tYXhYIDw9IGEubWF4WCAmJlxuICAgICAgICAgICBiLm1heFkgPD0gYS5tYXhZO1xufVxuXG5mdW5jdGlvbiBpbnRlcnNlY3RzKGEsIGIpIHtcbiAgICByZXR1cm4gYi5taW5YIDw9IGEubWF4WCAmJlxuICAgICAgICAgICBiLm1pblkgPD0gYS5tYXhZICYmXG4gICAgICAgICAgIGIubWF4WCA+PSBhLm1pblggJiZcbiAgICAgICAgICAgYi5tYXhZID49IGEubWluWTtcbn1cblxuZnVuY3Rpb24gY3JlYXRlTm9kZShjaGlsZHJlbikge1xuICAgIHJldHVybiB7XG4gICAgICAgIGNoaWxkcmVuOiBjaGlsZHJlbixcbiAgICAgICAgaGVpZ2h0OiAxLFxuICAgICAgICBsZWFmOiB0cnVlLFxuICAgICAgICBtaW5YOiBJbmZpbml0eSxcbiAgICAgICAgbWluWTogSW5maW5pdHksXG4gICAgICAgIG1heFg6IC1JbmZpbml0eSxcbiAgICAgICAgbWF4WTogLUluZmluaXR5XG4gICAgfTtcbn1cblxuLy8gc29ydCBhbiBhcnJheSBzbyB0aGF0IGl0ZW1zIGNvbWUgaW4gZ3JvdXBzIG9mIG4gdW5zb3J0ZWQgaXRlbXMsIHdpdGggZ3JvdXBzIHNvcnRlZCBiZXR3ZWVuIGVhY2ggb3RoZXI7XG4vLyBjb21iaW5lcyBzZWxlY3Rpb24gYWxnb3JpdGhtIHdpdGggYmluYXJ5IGRpdmlkZSAmIGNvbnF1ZXIgYXBwcm9hY2hcblxuZnVuY3Rpb24gbXVsdGlTZWxlY3QoYXJyLCBsZWZ0LCByaWdodCwgbiwgY29tcGFyZSkge1xuICAgIHZhciBzdGFjayA9IFtsZWZ0LCByaWdodF0sXG4gICAgICAgIG1pZDtcblxuICAgIHdoaWxlIChzdGFjay5sZW5ndGgpIHtcbiAgICAgICAgcmlnaHQgPSBzdGFjay5wb3AoKTtcbiAgICAgICAgbGVmdCA9IHN0YWNrLnBvcCgpO1xuXG4gICAgICAgIGlmIChyaWdodCAtIGxlZnQgPD0gbikgY29udGludWU7XG5cbiAgICAgICAgbWlkID0gbGVmdCArIE1hdGguY2VpbCgocmlnaHQgLSBsZWZ0KSAvIG4gLyAyKSAqIG47XG4gICAgICAgIHF1aWNrc2VsZWN0KGFyciwgbWlkLCBsZWZ0LCByaWdodCwgY29tcGFyZSk7XG5cbiAgICAgICAgc3RhY2sucHVzaChsZWZ0LCBtaWQsIG1pZCwgcmlnaHQpO1xuICAgIH1cbn1cbiIsImltcG9ydCByYnVzaCBmcm9tICdyYnVzaCc7XG5pbXBvcnQgYmJveCBmcm9tICdAdHVyZi9iYm94JztcblxuLyoqXG4gKiBBIHRpbGUgb2JqZWN0XG4gKlxuICogQGNsYXNzIFRpbGVcbiAqIEBwcml2YXRlXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRpbGUge1xuICBjb25zdHJ1Y3Rvcih4LCB5LCB6KSB7XG4gICAgdGhpcy54ID0geDtcbiAgICB0aGlzLnkgPSB5O1xuICAgIHRoaXMueiA9IHo7XG4gICAgdGhpcy5mZWF0dXJlcyA9IHt9O1xuXG4gICAgdGhpcy5sb2FkZWQgPSBmYWxzZTtcblxuICAgIHRoaXMuaW5kZXggPSByYnVzaCgpO1xuICAgIHRoaXMuZmVhdHVyZUdyb3VwID0gTC5mZWF0dXJlR3JvdXAoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxsIHRoaXMgbWV0aG9kIHdoZW4gYWxsIGZlYXR1cmVzIGhhdmUgYmVlbiBhZGRlZCB0byB0aGUgdGlsZVxuICAgKlxuICAgKiBAcmV0dXJucyB7VGlsZX0gdGhpc1xuICAgKi9cbiAgaW5pdCgpIHtcbiAgICB0aGlzLmluZGV4RmVhdHVyZXMoKTtcbiAgICB0aGlzLnJlbmRlcigpO1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLyoqXG4gICAqXG4gICAqL1xuICByZW5kZXIoKSB7XG4gICAgZm9yIChjb25zdCBpZCBpbiB0aGlzLmZlYXR1cmVzKSB7XG4gICAgICBpZiAoIXRoaXMuZmVhdHVyZXMuaGFzT3duUHJvcGVydHkoaWQpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgY29uc3QgZmVhdHVyZSA9IHRoaXMuZmVhdHVyZXNbaWRdO1xuICAgICAgaWYgKGZlYXR1cmUub25NYXApIHtcbiAgICAgICAgdGhpcy5mZWF0dXJlR3JvdXAuYWRkTGF5ZXIoZmVhdHVyZS5sYXllcik7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqXG4gICAqL1xuICBpbmRleEZlYXR1cmVzKCkge1xuICAgIGNvbnN0IGJib3hlcyA9IFtdO1xuICAgIGZvciAoY29uc3QgaWQgaW4gdGhpcy5mZWF0dXJlcykge1xuICAgICAgaWYgKCF0aGlzLmZlYXR1cmVzLmhhc093blByb3BlcnR5KGlkKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGZlYXR1cmUgPSB0aGlzLmZlYXR1cmVzW2lkXTtcbiAgICAgIGNvbnN0IGdlb20gPSBmZWF0dXJlLmdlb2pzb24uZ2VvbWV0cnk7XG4gICAgICBjb25zdCBjID0gZ2VvbS5jb29yZGluYXRlcztcblxuICAgICAgbGV0IG1pblg7XG4gICAgICBsZXQgbWluWTtcbiAgICAgIGxldCBtYXhYO1xuICAgICAgbGV0IG1heFk7XG5cbiAgICAgIGlmIChnZW9tLnR5cGUgPT09ICdQb2ludCcpIHtcbiAgICAgICAgbWluWCA9IGNbMF07XG4gICAgICAgIG1heFggPSBjWzBdO1xuICAgICAgICBtaW5ZID0gY1sxXTtcbiAgICAgICAgbWF4WSA9IGNbMV07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBbbWluWCwgbWluWSwgbWF4WCwgbWF4WV0gPSBiYm94KGdlb20pO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBpdGVtID0ge1xuICAgICAgICBtaW5YLFxuICAgICAgICBtaW5ZLFxuICAgICAgICBtYXhYLFxuICAgICAgICBtYXhZLFxuICAgICAgICBpZDogZmVhdHVyZS5pZFxuICAgICAgfTtcblxuICAgICAgZmVhdHVyZS5pbmRleEVudHJ5ID0gaXRlbTtcblxuICAgICAgYmJveGVzLnB1c2goaXRlbSk7XG4gICAgfVxuXG4gICAgdGhpcy5pbmRleC5sb2FkKGJib3hlcyk7XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkXG4gICAqIEByZXR1cm5zIHtib29sZWFufSB0cnVlIGlzIHRoaXMgdGlsZSBjb250YWlucyBhIGZlYXR1cmUgd2l0aCB0aGUgZ2l2ZW4gaWRcbiAgICovXG4gIGNvbnRhaW5zKGlkKSB7XG4gICAgcmV0dXJuIGlkIGluIHRoaXMuZmVhdHVyZXM7XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtGZWF0dXJlfSBmZWF0dXJlXG4gICAqIEByZXR1cm5zIHtUaWxlfSB0aGlzXG4gICAqL1xuICBhZGRGZWF0dXJlKGZlYXR1cmUpIHtcbiAgICB0aGlzLmZlYXR1cmVzW2ZlYXR1cmUuaWRdID0gZmVhdHVyZTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWRcbiAgICogQHJldHVybnMge1RpbGV9IHRoaXNcbiAgICovXG4gIHJlbW92ZUZlYXR1cmUoaWQpIHtcbiAgICBpZiAoIXRoaXMuY29udGFpbnMoaWQpKSB7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgY29uc3QgZmVhdHVyZSA9IHRoaXMuZ2V0RmVhdHVyZShpZCk7XG4gICAgdGhpcy5mZWF0dXJlR3JvdXAucmVtb3ZlTGF5ZXIoZmVhdHVyZS5sYXllcik7XG4gICAgdGhpcy5pbmRleC5yZW1vdmUoZmVhdHVyZS5pbmRleEVudHJ5KTtcbiAgICBkZWxldGUgdGhpcy5mZWF0dXJlc1tpZF07XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkXG4gICAqIEByZXR1cm5zIHtGZWF0dXJlfVxuICAgKi9cbiAgZ2V0RmVhdHVyZShpZCkge1xuICAgIHJldHVybiB0aGlzLmZlYXR1cmVzW2lkXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge251bWJlcn0gbWluWFxuICAgKiBAcGFyYW0ge251bWJlcn0gbWluWVxuICAgKiBAcGFyYW0ge251bWJlcn0gbWF4WFxuICAgKiBAcGFyYW0ge251bWJlcn0gbWF4WVxuICAgKiBAcmV0dXJucyB7QXJyYXk8U3RyaW5nPn0gYW4gYXJyYXkgb2YgZmVhdHVyZSBpZHMgb2YgZmVhdHVyZXMgdGhhdCBpbnRlcnNlY3RcbiAgICogdGhlIGJvdW5kaW5nIGJveFxuICAgKi9cbiAgc2VhcmNoKG1pblgsIG1pblksIG1heFgsIG1heFkpIHtcbiAgICByZXR1cm4gdGhpcy5pbmRleC5zZWFyY2goeyBtaW5YLCBtaW5ZLCBtYXhYLCBtYXhZIH0pLm1hcChyID0+IHIuaWQpO1xuICB9XG5cbiAgLyoqXG4gICAqXG4gICAqIEByZXR1cm5zIHtUaWxlfSB0aGlzXG4gICAqL1xuICBtYXJrQXNMb2FkZWQoKSB7XG4gICAgdGhpcy5sb2FkZWQgPSB0cnVlO1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wZXJ0eVxuICAgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWVcbiAgICogQHBhcmFtIHtib29sZWFufSBvblxuICAgKiBAcGFyYW0ge2Jvb2xlYWR9IHRvZ2dsZWRcbiAgICogQHJldHVybnMge1RpbGV9IHRoaXNcbiAgICovXG4gIHRvZ2dsZUJ5UHJvcGVydHkocHJvcGVydHksIHZhbHVlLCBvbiwgdG9nZ2xlZCkge1xuICAgIGxldCBmZWF0dXJlO1xuICAgIGxldCBnZW9qO1xuICAgIGZvciAoY29uc3QgaWQgaW4gdGhpcy5mZWF0dXJlcykge1xuICAgICAgaWYgKCF0aGlzLmZlYXR1cmVzLmhhc093blByb3BlcnR5KGlkKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGZlYXR1cmUgPSB0aGlzLmdldEZlYXR1cmUoaWQpO1xuICAgICAgZ2VvaiA9IGZlYXR1cmUuZ2VvanNvbjtcbiAgICAgIGlmIChwcm9wZXJ0eSBpbiBnZW9qLnByb3BlcnRpZXMgJiYgZ2Vvai5wcm9wZXJ0aWVzW3Byb3BlcnR5XSA9PT0gdmFsdWUpIHtcbiAgICAgICAgaWYgKHRvZ2dsZWQpIHtcbiAgICAgICAgICBpZiAob24pIHtcbiAgICAgICAgICAgIHRoaXMuaW5kZXguaW5zZXJ0KGZlYXR1cmUuaW5kZXhFbnRyeSk7XG4gICAgICAgICAgICB0aGlzLmZlYXR1cmVHcm91cC5hZGRMYXllcihmZWF0dXJlLmxheWVyKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5pbmRleC5yZW1vdmUoZmVhdHVyZS5pbmRleEVudHJ5KTtcbiAgICAgICAgICAgIHRoaXMuZmVhdHVyZUdyb3VwLnJlbW92ZUxheWVyKGZlYXR1cmUubGF5ZXIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcHJvcGVydHlcbiAgICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBzdHlsZVxuICAgKiBAcmV0dXJucyB7VGlsZX0gdGhpc1xuICAgKi9cbiAgcmVzdHlsZUJ5UHJvcGVydHkocHJvcGVydHksIHZhbHVlLCBzdHlsZSkge1xuICAgIGxldCBmZWF0dXJlO1xuICAgIGZvciAoY29uc3QgaWQgaW4gdGhpcy5mZWF0dXJlcykge1xuICAgICAgaWYgKCF0aGlzLmZlYXR1cmVzLmhhc093blByb3BlcnR5KGlkKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGZlYXR1cmUgPSB0aGlzLmdldEZlYXR1cmUoaWQpO1xuICAgICAgaWYgKHByb3BlcnR5IGluIGZlYXR1cmUuZ2VvanNvbi5wcm9wZXJ0aWVzXG4gICAgICAgICAgJiYgZmVhdHVyZS5nZW9qc29uLnByb3BlcnRpZXNbcHJvcGVydHldID09PSB2YWx1ZSkge1xuICAgICAgICBmZWF0dXJlLmxheWVyLnNldFN0eWxlKHN0eWxlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cbn1cblxuIl19 +},{"@turf/bbox":8,"rbush":13}],15:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/** + * LRU cache for Tile objects + */ + +/** + * Node object for tile cache linked list + * + * @class Node + * @private + */ +var Node = function Node(data) { + _classCallCheck(this, Node); + + this.data = data; + this.prev = null; + this.next = null; +}; + +/** + * This class implements an LRU cache + * Objects in the cache are stored by key in a hash table, hashed on `tileKey` + * A separate doubly linked list is used to maintain the order of eviction. By maintaining + * references to corresponding nodes in this._cache, we achieve constant time `get`s and `put`s. + * + * @class TileCache + * @private + */ + + +var TileCache = function () { + + /** + * Constructor + * + * @param {number} size - maximum number of items the cache can hold + * @param {boolean} [debug=false] - enables debug printing + */ + function TileCache(size) { + var debug = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + _classCallCheck(this, TileCache); + + this._size = size; + this._debug = debug; + this._cache = {}; // a hashtable for holding cached items + this._head = null; // the head of a doubly linked list that maintains the order of items by age + this._tail = null; // tail pointer to the order linked list + } + + /** + * Retrieve an item from the cache + * + * @param {string} tileKey + * @returns {Tile|null} + */ + + + _createClass(TileCache, [{ + key: 'get', + value: function get(tileKey) { + if (!(tileKey in this._cache)) { + if (this._debug) { + console.log('tile cache:', 'miss', tileKey); + } + return null; + } + + if (this._debug) { + console.log('tile cache', 'hit', tileKey); + } + + // move node to front of linked list + var node = this._cache[tileKey].node; + if (node.prev) { + node.prev.next = node.next; + } + if (node === this._tail) { + this._tail = node.prev; + } + node.next = this._head; + + return this._cache[tileKey].tile; + } + + /** + * @param {string} tileKey + * @param {Tile} tile + */ + + }, { + key: 'put', + value: function put(tileKey, tile) { + if (this._debug) { + console.log('tile cache:', 'caching', tileKey); + console.log(this._stringifyList()); + } + + if (tileKey in this._cache) { + // move to front of linked list + var _node = this._cache[tileKey].node; + if (_node.prev) { + _node.prev.next = _node.next; + } + _node.next = this._head; + // the data may be new so always replace it + this._cache[tileKey].tile = tile; + return this; + } + + // place at heaad of order linked list + var node = new Node({ tileKey: tileKey }); + if (this._head) { + this._head.prev = node; + } + node.next = this._head; + this._head = node; + + this._cache[tileKey] = { tileKey: tileKey, tile: tile, node: node }; + + if (Object.keys(this._cache) > this._size) { + // we need to evict an item + + // remove from linked list + var tailNode = this._tail; + this._tail = tailNode.prev; + tailNode.prev.next = null; + + // remove from cache table + var tailtileKey = tailNode.data.tileKey; + if (this._debug) { + console.log('tile cache:', 'evicting', tileKey); + console.log(this._stringifyList()); + } + delete this._cache[tailtileKey]; + } + } + + /** + * @param {number} size + */ + + }, { + key: 'setSize', + value: function setSize(size) { + if (size < 0) { + throw "Size cannot be a negative number"; + } + + if (this._debug) { + console.log('tile cache:', 'changing cache size from', this._size, 'to', size); + } + + if (size >= this._size) { + // we are increasing the size, no need to remove items from the cache + this._size = size; + return; + } + + this._size = size; + + if (this._head == null) { + // this cache is empty + return; + } + + var node = this._head; + var garbage = node; + if (size > 0) { + var c = 1; + while (c < size && node.next != null) { + node = node.next; + c++; + } + garbage = node.next; + node.next = null; + } + + // collect the garbage + while (garbage != null) { + var tileKey = garbage.data.tileKey; + + if (this._debug) { + console.log('tile cache:', 'removing tile', tileKey, 'due to cache resize'); + console.log(this._stringifyList()); + } + delete this._cache[tileKey]; // delete from cache + garbage = garbage.next; + } + + // when this function exits, there should be no more references to the garbage + // nodes in the linked list, so they will be garbage collected as usual + } + + /** + * Returns a string reprenting the current order of tiles in the cache + * + * @returns {string} + * @private + */ + + }, { + key: '_stringifyList', + value: function _stringifyList() { + var node = this._head; + var out = ''; + while (node !== null) { + out += '(coords = ' + node.data.tileKey + ', feature count = ' + Object.keys(this._cache[node.data.tileKey].tile._features).length + ') -> '; + node = node.next; + } + return out; + } + }]); + + return TileCache; +}(); + +exports.default = TileCache; + +},{}]},{},[1]); diff --git a/package-lock.json b/package-lock.json index 316bf5c..049a6fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,11 +40,6 @@ "through": "2.3.8" } }, - "JSV": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", - "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=" - }, "acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", @@ -68,11 +63,6 @@ } } }, - "agentkeepalive": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.0.2.tgz", - "integrity": "sha1-o2YcQG7RILsRykiNZ7MnbGBMz+c=" - }, "ajv": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", @@ -115,12 +105,14 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, "anymatch": { "version": "1.3.2", @@ -219,11 +211,6 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "asn1": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", - "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=" - }, "asn1.js": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", @@ -244,11 +231,6 @@ "util": "0.10.3" } }, - "assert-plus": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", - "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=" - }, "astw": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", @@ -258,14 +240,6 @@ "acorn": "4.0.13" } }, - "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", - "requires": { - "lodash": "4.17.4" - } - }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", @@ -278,11 +252,6 @@ "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", "dev": true }, - "aws-sign2": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", - "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=" - }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -1304,42 +1273,6 @@ "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", "dev": true }, - "bl": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", - "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=", - "requires": { - "readable-stream": "1.0.34" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" - }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", @@ -1369,14 +1302,6 @@ "json-stringify-safe": "5.0.1" } }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "requires": { - "hoek": "2.16.3" - } - }, "brace-expansion": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", @@ -1686,7 +1611,8 @@ "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true }, "camelcase-keys": { "version": "2.1.0", @@ -1698,11 +1624,6 @@ "map-obj": "1.0.1" } }, - "caseless": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.10.0.tgz", - "integrity": "sha1-7WsnGa3NH9GPWNwIHA8aW0OWOQk=" - }, "ccount": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.2.tgz", @@ -1713,6 +1634,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, "requires": { "ansi-styles": "2.2.1", "escape-string-regexp": "1.0.5", @@ -1844,6 +1766,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, "requires": { "string-width": "1.0.2", "strip-ansi": "3.0.1", @@ -1888,7 +1811,8 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true }, "collapse-white-space": { "version": "1.0.3", @@ -1942,14 +1866,6 @@ } } }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "requires": { - "delayed-stream": "1.0.0" - } - }, "comma-separated-tokens": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.4.tgz", @@ -1959,11 +1875,6 @@ "trim": "0.0.1" } }, - "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==" - }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", @@ -1980,6 +1891,7 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, "requires": { "inherits": "2.0.3", "readable-stream": "2.0.6", @@ -1990,6 +1902,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -2002,7 +1915,8 @@ "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true } } }, @@ -2060,7 +1974,8 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true }, "create-ecdh": { "version": "4.0.0", @@ -2098,14 +2013,6 @@ "sha.js": "2.4.8" } }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "requires": { - "boom": "2.10.1" - } - }, "crypto-browserify": { "version": "3.11.1", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", @@ -2124,11 +2031,6 @@ "randombytes": "2.0.5" } }, - "ctype": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", - "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=" - }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -2163,6 +2065,7 @@ "version": "2.6.8", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, "requires": { "ms": "2.0.0" } @@ -2170,7 +2073,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "deep-is": { "version": "0.1.3", @@ -2208,11 +2112,6 @@ "rimraf": "2.6.1" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, "depd": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", @@ -2890,7 +2789,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "escope": { "version": "3.6.0", @@ -3235,22 +3135,6 @@ "websocket-driver": "0.6.5" } }, - "fg-lodash": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/fg-lodash/-/fg-lodash-0.0.2.tgz", - "integrity": "sha1-mINSU39CfaavIiEpu2OsyknmL6M=", - "requires": { - "lodash": "2.4.2", - "underscore.string": "2.3.3" - }, - "dependencies": { - "lodash": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" - } - } - }, "figures": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", @@ -3333,31 +3217,6 @@ "for-in": "1.0.2" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz", - "integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=", - "requires": { - "async": "2.6.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - }, - "dependencies": { - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "requires": { - "mime-db": "1.30.0" - } - } - } - }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -4381,12 +4240,14 @@ "generate-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true }, "generate-object-property": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, "requires": { "is-property": "1.0.2" } @@ -4396,56 +4257,6 @@ "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-2.4.0.tgz", "integrity": "sha1-PBz0RJPzXrTSxwyV2mVQ3mYHLAU=" }, - "geojsonhint": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/geojsonhint/-/geojsonhint-1.2.1.tgz", - "integrity": "sha1-U0gnDsrDxCi0Vc/tqyRdQKWun8c=", - "requires": { - "chalk": "1.1.3", - "concat-stream": "1.4.10", - "jsonlint-lines": "1.7.1", - "minimist": "1.1.1", - "text-table": "0.2.0" - }, - "dependencies": { - "concat-stream": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.10.tgz", - "integrity": "sha1-rMO79WAsuMyYDGrIQPp9hgPj7zY=", - "requires": { - "inherits": "2.0.3", - "readable-stream": "1.1.14", - "typedarray": "0.0.6" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "minimist": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.1.tgz", - "integrity": "sha1-G8K8cWWM3KVxJHVoQ2NhWwtPaVs=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, "get-caller-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", @@ -4712,17 +4523,6 @@ } } }, - "har-validator": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-1.8.0.tgz", - "integrity": "sha1-2DhCsOtMQ1lgrrEIoGejqpTA7rI=", - "requires": { - "bluebird": "2.11.0", - "chalk": "1.1.3", - "commander": "2.12.2", - "is-my-json-valid": "2.16.0" - } - }, "has": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", @@ -4736,15 +4536,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, "requires": { "ansi-regex": "2.1.1" } }, - "has-color": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", - "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=" - }, "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", @@ -4827,17 +4623,6 @@ "integrity": "sha1-vQlpGWJdKTbh/xe8Tff9cn8X7Ok=", "dev": true }, - "hawk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-2.3.1.tgz", - "integrity": "sha1-HnMc45RH+h0PbXB/e87r7A/R7B8=", - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, "highlight.js": { "version": "9.12.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", @@ -4855,11 +4640,6 @@ "minimalistic-crypto-utils": "1.0.1" } }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" - }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -4900,16 +4680,6 @@ "statuses": "1.3.1" } }, - "http-signature": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.11.0.tgz", - "integrity": "sha1-F5bPZ6ABrVzWhJ3KCZFIXwkIn+Y=", - "requires": { - "asn1": "0.1.11", - "assert-plus": "0.1.5", - "ctype": "0.5.3" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -4967,7 +4737,8 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true }, "ini": { "version": "1.3.4", @@ -5057,7 +4828,8 @@ "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true }, "is-absolute": { "version": "0.2.6", @@ -5204,6 +4976,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, "requires": { "number-is-nan": "1.0.1" } @@ -5227,6 +5000,7 @@ "version": "2.16.0", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", + "dev": true, "requires": { "generate-function": "2.0.0", "generate-object-property": "1.2.0", @@ -5325,7 +5099,8 @@ "is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true }, "is-relative": { "version": "0.2.1", @@ -5402,7 +5177,8 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "isexe": { "version": "2.0.0", @@ -5419,11 +5195,6 @@ "isarray": "1.0.0" } }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -5458,7 +5229,8 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true }, "json5": { "version": "0.5.1", @@ -5472,15 +5244,6 @@ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", "dev": true }, - "jsonlint-lines": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/jsonlint-lines/-/jsonlint-lines-1.7.1.tgz", - "integrity": "sha1-UH3mgNP7jEvhZBzFfW9nnynxeP8=", - "requires": { - "JSV": "4.0.2", - "nomnom": "1.8.1" - } - }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -5490,7 +5253,8 @@ "jsonpointer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true }, "kebab-case": { "version": "1.0.0", @@ -5548,6 +5312,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, "requires": { "invert-kv": "1.0.0" } @@ -5611,7 +5376,8 @@ "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true }, "lodash.cond": { "version": "4.5.2", @@ -5710,16 +5476,6 @@ "integrity": "sha1-Sz3ToTPRUYuO8NvHCb8qG0gkvIw=", "dev": true }, - "mbtiles": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/mbtiles/-/mbtiles-0.8.2.tgz", - "integrity": "sha1-vhZN+bb0GaX2LHbtMcW/iAIv52w=", - "requires": { - "sphericalmercator": "1.0.5", - "sqlite3": "3.1.13", - "tiletype": "0.1.0" - } - }, "mdast-util-compact": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz", @@ -5856,26 +5612,6 @@ "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", "dev": true }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" - }, - "mime-types": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", - "integrity": "sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY=", - "requires": { - "mime-db": "1.12.0" - }, - "dependencies": { - "mime-db": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz", - "integrity": "sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc=" - } - } - }, "minimalistic-assert": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", @@ -5948,7 +5684,8 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true }, "mute-stream": { "version": "0.0.5", @@ -6018,42 +5755,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" - }, - "nomnom": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", - "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", - "requires": { - "chalk": "0.4.0", - "underscore": "1.6.0" - }, - "dependencies": { - "ansi-styles": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", - "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=" - }, - "chalk": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", - "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", - "requires": { - "ansi-styles": "1.0.0", - "has-color": "0.1.7", - "strip-ansi": "0.1.1" - } - }, - "strip-ansi": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", - "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=" - } - } - }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -6084,12 +5785,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6138,11 +5835,6 @@ } } }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" - }, "object-visit": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-0.3.4.tgz", @@ -6256,6 +5948,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, "requires": { "lcid": "1.0.0" } @@ -6564,11 +6257,6 @@ "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", "dev": true }, - "point-geometry": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/point-geometry/-/point-geometry-0.0.0.tgz", - "integrity": "sha1-b8vK16gDtkGCR91uScKFPFhNr/c=" - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -6619,7 +6307,8 @@ "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true }, "progress": { "version": "1.1.8", @@ -6627,56 +6316,6 @@ "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, - "progress-stream": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-0.5.0.tgz", - "integrity": "sha1-zEdZFnpv9PBYdheThPC2rg0ddYc=", - "requires": { - "single-line-log": "0.3.1", - "speedometer": "0.1.4", - "through2": "0.2.3" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "through2": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", - "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", - "requires": { - "readable-stream": "1.1.14", - "xtend": "2.1.2" - } - }, - "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "requires": { - "object-keys": "0.4.0" - } - } - } - }, "property-information": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-3.2.0.tgz", @@ -6710,7 +6349,8 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true }, "qs": { "version": "6.5.0", @@ -6740,11 +6380,6 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, - "queue-async": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/queue-async/-/queue-async-1.0.7.tgz", - "integrity": "sha1-Iq4KHaxKkvW81GNPmTxoKiqBCUU=" - }, "quickselect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.0.0.tgz", @@ -7127,53 +6762,6 @@ "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", "dev": true }, - "request": { - "version": "2.58.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.58.0.tgz", - "integrity": "sha1-tfScC5Sqt/rTiGEqH7atA7bMFYA=", - "requires": { - "aws-sign2": "0.5.0", - "bl": "0.9.5", - "caseless": "0.10.0", - "combined-stream": "1.0.5", - "extend": "2.0.1", - "forever-agent": "0.6.1", - "form-data": "1.0.1", - "har-validator": "1.8.0", - "hawk": "2.3.1", - "http-signature": "0.11.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.0.14", - "node-uuid": "1.4.8", - "oauth-sign": "0.8.2", - "qs": "3.1.0", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.4.3" - }, - "dependencies": { - "extend": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-2.0.1.tgz", - "integrity": "sha1-HugBBonnOV/5RIJByYZSvHWagmA=" - }, - "qs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-3.1.0.tgz", - "integrity": "sha1-0OmudFIzoS3EP7TzBVu6RGJhFTw=" - } - } - }, - "requestretry": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.4.0.tgz", - "integrity": "sha1-g598rYRYVi7uAWgNJ0Uun4XhZYI=", - "requires": { - "fg-lodash": "0.0.2", - "request": "2.58.0" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7432,11 +7020,6 @@ "from2-string": "1.1.0" } }, - "single-line-log": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.3.1.tgz", - "integrity": "sha1-p61lB/IYzl3+FsS/LWWSRkGeegY=" - }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", @@ -7521,14 +7104,6 @@ "kind-of": "3.2.2" } }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "requires": { - "hoek": "2.16.3" - } - }, "source-map": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", @@ -7592,16 +7167,6 @@ "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", "dev": true }, - "speedometer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", - "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=" - }, - "sphericalmercator": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sphericalmercator/-/sphericalmercator-1.0.5.tgz", - "integrity": "sha1-3cWgSeNg4ADQ+tn8IsQHGIJYSYA=" - }, "split-string": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/split-string/-/split-string-2.1.1.tgz", @@ -7662,723 +7227,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "sqlite3": { - "version": "3.1.13", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-3.1.13.tgz", - "integrity": "sha512-JxXKPJnkZ6NuHRojq+g2WXWBt3M1G9sjZaYiHEWSTGijDM3cwju/0T2XbWqMXFmPqDgw+iB7zKQvnns4bvzXlw==", - "requires": { - "nan": "2.7.0", - "node-pre-gyp": "0.6.38" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.3" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.8", - "bundled": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "caseless": { - "version": "0.12.0", - "bundled": true - }, - "co": { - "version": "4.6.0", - "bundled": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true - }, - "extsprintf": { - "version": "1.3.0", - "bundled": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.4", - "bundled": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true - }, - "jsprim": { - "version": "1.4.1", - "bundled": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "mime-db": { - "version": "1.30.0", - "bundled": true - }, - "mime-types": { - "version": "2.1.17", - "bundled": true, - "requires": { - "mime-db": "1.30.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "1.1.8" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true - }, - "nan": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", - "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=" - }, - "node-pre-gyp": { - "version": "0.6.38", - "bundled": true, - "requires": { - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.2", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.2", - "semver": "5.4.1", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true - }, - "qs": { - "version": "6.4.0", - "bundled": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true - } - } - }, - "readable-stream": { - "version": "2.3.3", - "bundled": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true - }, - "semver": { - "version": "5.4.1", - "bundled": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.1", - "bundled": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.3", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "requires": { - "debug": "2.6.9", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.3.3", - "rimraf": "2.6.2", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.3", - "bundled": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true - }, - "uuid": { - "version": "3.1.0", - "bundled": true - }, - "verror": { - "version": "1.10.0", - "bundled": true, - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - } - } - }, "stacked": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stacked/-/stacked-1.1.1.tgz", @@ -8541,6 +7389,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, "requires": { "code-point-at": "1.1.0", "is-fullwidth-code-point": "1.0.0", @@ -8568,15 +7417,11 @@ "is-hexadecimal": "1.0.1" } }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "2.1.1" } @@ -8635,7 +7480,8 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true }, "syntax-error": { "version": "1.3.0", @@ -8720,12 +7566,14 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true }, "through2": { "version": "2.0.3", @@ -8747,52 +7595,6 @@ "xtend": "4.0.1" } }, - "tile-cover": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tile-cover/-/tile-cover-2.4.1.tgz", - "integrity": "sha1-jKkFscqTNKVGim1oDolmMhZMjl8=", - "requires": { - "tilebelt": "0.7.1" - } - }, - "tilebelt": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/tilebelt/-/tilebelt-0.7.1.tgz", - "integrity": "sha1-2fQY262cpIO0Ql3Q+GuQZXFv1nk=" - }, - "tilejson": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tilejson/-/tilejson-1.0.3.tgz", - "integrity": "sha1-Kqmm8qelhruglsi+49uFIbr6D4g=", - "requires": { - "agentkeepalive": "2.0.2", - "requestretry": "1.4.0", - "tiletype": "0.1.0" - } - }, - "tilelive": { - "version": "5.12.3", - "resolved": "https://registry.npmjs.org/tilelive/-/tilelive-5.12.3.tgz", - "integrity": "sha1-nIx3DhGUqo01PZodAxR0BDB83r0=", - "requires": { - "minimist": "0.2.0", - "progress-stream": "0.5.0", - "queue-async": "1.0.7", - "sphericalmercator": "1.0.5" - }, - "dependencies": { - "minimist": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", - "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=" - } - } - }, - "tiletype": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/tiletype/-/tiletype-0.1.0.tgz", - "integrity": "sha1-C5ymVPjAp8zM2SDt9ZTT7pSJwpc=" - }, "timers-browserify": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", @@ -8906,14 +7708,6 @@ } } }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "requires": { - "punycode": "1.4.1" - } - }, "trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", @@ -8962,46 +7756,6 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=" - }, - "turf-bbox-polygon": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/turf-bbox-polygon/-/turf-bbox-polygon-1.0.2.tgz", - "integrity": "sha1-SIsiP7eHdhFh+jMr/6Hdj9W9CdE=", - "requires": { - "turf-polygon": "1.0.3" - } - }, - "turf-envelope": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/turf-envelope/-/turf-envelope-1.0.2.tgz", - "integrity": "sha1-a5ab3PZ+VTyV8wIHHmtLXxEsAAg=", - "requires": { - "turf-bbox-polygon": "1.0.2", - "turf-extent": "1.0.4" - } - }, - "turf-extent": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/turf-extent/-/turf-extent-1.0.4.tgz", - "integrity": "sha1-nyjOEa+RatqIhioGIHRli6P+DwE=", - "requires": { - "turf-meta": "1.0.2" - } - }, - "turf-meta": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/turf-meta/-/turf-meta-1.0.2.tgz", - "integrity": "sha1-dNTElZOOskdypwsxoVfuPU/Wuhs=" - }, - "turf-polygon": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/turf-polygon/-/turf-polygon-1.0.3.tgz", - "integrity": "sha1-Zx3TSEmGRQkoGvGOI29np0SMY2M=" - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -9014,7 +7768,8 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true }, "ultron": { "version": "1.0.2", @@ -9034,16 +7789,6 @@ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true }, - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" - }, - "underscore.string": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", - "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=" - }, "unherit": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", @@ -9281,7 +8026,8 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, "vali-date": { "version": "1.0.0", @@ -9299,14 +8045,6 @@ "spdx-expression-parse": "1.0.4" } }, - "vector-tile": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/vector-tile/-/vector-tile-1.3.0.tgz", - "integrity": "sha1-BtUWqDsGPwTILvU5zxuxrr62lrQ=", - "requires": { - "point-geometry": "0.0.0" - } - }, "vfile": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.2.0.tgz", @@ -9439,106 +8177,6 @@ "indexof": "0.0.1" } }, - "vt-geojson": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/vt-geojson/-/vt-geojson-2.1.1.tgz", - "integrity": "sha1-jK5X3yc6cxX38kdsJNjMNQvAy6o=", - "requires": { - "JSONStream": "0.10.0", - "concat-stream": "1.5.2", - "debug": "2.6.8", - "geojsonhint": "1.2.1", - "mbtiles": "0.8.2", - "pbf": "1.3.7", - "through2": "0.6.5", - "tile-cover": "2.4.1", - "tilejson": "1.0.3", - "tilelive": "5.12.3", - "turf-bbox-polygon": "1.0.2", - "turf-envelope": "1.0.2", - "vector-tile": "1.3.0", - "yargs": "3.32.0" - }, - "dependencies": { - "JSONStream": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", - "integrity": "sha1-dDSdDYlSK3HzDwoD/5vSDKbxKsA=", - "requires": { - "jsonparse": "0.0.5", - "through": "2.3.8" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "jsonparse": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", - "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=" - }, - "pbf": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-1.3.7.tgz", - "integrity": "sha1-Hj0Ee6PL6Ahq6FSiVQOrRTfUM10=", - "requires": { - "ieee754": "1.1.8", - "resolve-protobuf-schema": "2.0.0" - } - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "requires": { - "readable-stream": "1.0.34", - "xtend": "4.0.1" - } - }, - "yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", - "requires": { - "camelcase": "2.1.1", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "os-locale": "1.4.0", - "string-width": "1.0.2", - "window-size": "0.1.4", - "y18n": "3.2.1" - } - } - } - }, - "vt-pbf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.0.tgz", - "integrity": "sha512-UUCGPkpT1P/bm3R3/HX0SCnRSto44xXx0WuLFVG6C7KspdfQfU+84etoO6cITAGCdq8V5DjuWfDhvk/pyTyt3Q==", - "requires": { - "@mapbox/point-geometry": "0.1.0", - "@mapbox/vector-tile": "1.3.0", - "pbf": "3.0.5" - } - }, "watchify": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/watchify/-/watchify-3.9.0.tgz", @@ -9603,11 +8241,6 @@ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, - "window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -9618,6 +8251,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, "requires": { "string-width": "1.0.2", "strip-ansi": "3.0.1" @@ -9663,12 +8297,14 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true }, "yargs": { "version": "6.6.0", diff --git a/package.json b/package.json index de31250..9c096d6 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,6 @@ "geojson-vt": "^2.4.0", "pbf": "^3.0.5", "rbush": "^2.0.1", - "vt-geojson": "^2.1.1", - "vt-pbf": "^3.1.0", "webworkify": "^1.5.0" } } diff --git a/tile_cache.js b/tile_cache.js index 57eb1ed..2d1c5b1 100644 --- a/tile_cache.js +++ b/tile_cache.js @@ -85,7 +85,6 @@ export default class TileCache { put(tileKey, tile) { if (this._debug) { console.log('(TileCache)', 'caching', tileKey); - console.log(this._stringifyList()); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! } if (tileKey in this._cache) { @@ -126,7 +125,6 @@ export default class TileCache { const tailtileKey = tailNode.data.tileKey; if (this._debug) { console.log('(TileCache)', 'evicting', tileKey); - console.log(this._stringifyList()); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! } delete this._cache[tailtileKey]; } From 0bbe95f056655ebb3baacbaae4f4d6f91a491167 Mon Sep 17 00:00:00 2001 From: kelvinabrokwa Date: Tue, 19 Dec 2017 09:12:49 -0500 Subject: [PATCH 8/8] clean up example code and deps --- example/index.html | 34 +++++++++++++++++++++++++--------- example/index.js | 2 +- example/package-lock.json | 23 ----------------------- example/package.json | 3 --- example/vt_server.js | 2 -- 5 files changed, 26 insertions(+), 38 deletions(-) diff --git a/example/index.html b/example/index.html index 1049f80..66053d9 100644 --- a/example/index.html +++ b/example/index.html @@ -11,6 +11,12 @@ html, body, input { font-family: Helvetica; } #map { height: 600px; } .mb4 { margin-bottom: 40px; } + td, th { + border: 1px solid #dddddd; + text-align: left; + padding: 8px; + } + table { border-collapse: collapse; } @@ -21,15 +27,25 @@
cache size: {{ cacheSize }}
zoom: {{ zoom }}
-
-
-
({{ tile.coords.x }}, {{ tile.coords.y }}, {{ tile.coords.z }})
-
timestamp {{ tile.timestamp }}
-
feature count: {{ tile.featureCount }}
-
loaded
-
loading
-
-
+ + + + + + + + + + + + + + + + + + +
coordstimestampfeature countloaded
({{ tile.coords.x }}, {{ tile.coords.y }}, {{ tile.coords.z }}){{ tile.timestamp }}{{ tile.featureCount }}yesno
diff --git a/example/index.js b/example/index.js index 44de870..d400914 100644 --- a/example/index.js +++ b/example/index.js @@ -40,7 +40,7 @@ function main(geojson) { const vtLayer = window.vtLayer = new L.VectorTiles(url, { getFeatureId: f => f.properties.name.toLowerCase(), style: {}, - //debug: true, + debug: true, tileCacheSize: 5, }).addTo(map); diff --git a/example/package-lock.json b/example/package-lock.json index c5bb799..9efe44c 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -4,24 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@turf/bbox": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-4.6.0.tgz", - "integrity": "sha1-jH9ozdK+6ReN5Nn5v7zuQjXbX8M=", - "requires": { - "@turf/meta": "4.6.0" - } - }, - "@turf/helpers": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-4.6.0.tgz", - "integrity": "sha1-EjmHM7iuKEIN9hZvpEx+6P/WQUw=" - }, - "@turf/meta": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-4.6.0.tgz", - "integrity": "sha1-DT+aIY5Y0cXl3u30Z8MyHdYSA/M=" - }, "JSONStream": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", @@ -2295,11 +2277,6 @@ "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, - "sphericalmercator": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sphericalmercator/-/sphericalmercator-1.0.5.tgz", - "integrity": "sha1-3cWgSeNg4ADQ+tn8IsQHGIJYSYA=" - }, "split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", diff --git a/example/package.json b/example/package.json index 27aff21..fb3c23b 100644 --- a/example/package.json +++ b/example/package.json @@ -10,13 +10,10 @@ "author": "", "license": "ISC", "dependencies": { - "@turf/bbox": "^4.3.0", - "@turf/helpers": "^4.3.0", "cors": "^2.8.3", "express": "^4.15.3", "geojson-vt": "^2.4.0", "rbush": "^2.0.1", - "sphericalmercator": "^1.0.5", "vt-pbf": "^2.1.2" }, "devDependencies": { diff --git a/example/vt_server.js b/example/vt_server.js index 4c35832..2319e38 100644 --- a/example/vt_server.js +++ b/example/vt_server.js @@ -4,11 +4,9 @@ const fs = require('fs'); const cors = require('cors'); const rbush = require('rbush'); -const bbox = require('@turf/bbox'); const vtpbf = require('vt-pbf'); const express = require('express'); const geojsonvt = require('geojson-vt'); -const featureCollection = require('@turf/helpers').featureCollection; const app = express(); app.use(express.static('.'))