@smarterplan/ngx-smarterplan-core 1.4.15 → 1.4.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -24
- package/esm2022/lib/components/csv-export/csv-export.component.mjs +3 -3
- package/esm2022/lib/components/loader/loader.component.mjs +3 -3
- package/esm2022/lib/components/menu-bar/avatar/avatar.component.mjs +1 -1
- package/esm2022/lib/components/menu-bar/menu-bar.component.mjs +3 -3
- package/esm2022/lib/components/menu-bar/navigation-bar/navigation-bar.component.mjs +3 -3
- package/esm2022/lib/components/modal-switch-visit/modal-switch-visit.component.mjs +3 -3
- package/esm2022/lib/components/search-bar/search-bar.component.mjs +3 -3
- package/esm2022/lib/components/support-modal/support-modal.component.mjs +3 -3
- package/esm2022/lib/config.mjs +1 -1
- package/esm2022/lib/helpers.service.mjs +46 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasImage.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasRenderer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/NestThermostat.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/PlaneRenderer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/scene-component/SceneComponent.mjs +1 -1
- package/esm2022/lib/matterport-extensions/security-camera/SecurityCamera.mjs +22 -22
- package/esm2022/lib/matterport-extensions/tv-player/TvPlayer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/view-frustum-mesh/ViewFrustumMesh.mjs +22 -22
- package/esm2022/lib/mattertagData.mjs +1 -1
- package/esm2022/lib/ngx-smarterplan-core.module.mjs +1 -1
- package/esm2022/lib/ngx-smarterplan-core.service.mjs +1 -1
- package/esm2022/lib/pipes/duration-to-string.pipe.mjs +1 -1
- package/esm2022/lib/pipes/format-date-number-to-digits.pipe.mjs +1 -1
- package/esm2022/lib/pipes/hashtag-from-id.pipe.mjs +1 -1
- package/esm2022/lib/pipes/safe-url.pipe.mjs +1 -1
- package/esm2022/lib/pipes/time-date-to-local-string.pipe.mjs +1 -1
- package/esm2022/lib/pipes/username-from-id.pipe.mjs +1 -1
- package/esm2022/lib/services/amplify-cache.service.mjs +1 -1
- package/esm2022/lib/services/base-tab.service.mjs +1 -1
- package/esm2022/lib/services/baseVisibility.service.mjs +1 -1
- package/esm2022/lib/services/content.service.mjs +1 -1
- package/esm2022/lib/services/filter.service.mjs +11 -1
- package/esm2022/lib/services/intervention.service.mjs +1 -1
- package/esm2022/lib/services/locale.service.mjs +1 -1
- package/esm2022/lib/services/matterport-import.service.mjs +1 -1
- package/esm2022/lib/services/matterport-measurement.service.mjs +1 -1
- package/esm2022/lib/services/matterport-navigation.service.mjs +1 -1
- package/esm2022/lib/services/matterport-object3d.service.mjs +1 -1
- package/esm2022/lib/services/matterport-pointer.service.mjs +6 -6
- package/esm2022/lib/services/matterport-tag-copy.service.mjs +1 -1
- package/esm2022/lib/services/matterport-tag.service.mjs +1 -1
- package/esm2022/lib/services/matterport.service.mjs +29 -18
- package/esm2022/lib/services/models/affectation.service.mjs +1 -1
- package/esm2022/lib/services/models/base-object.service.mjs +31 -4
- package/esm2022/lib/services/models/capture.service.mjs +1 -1
- package/esm2022/lib/services/models/comment.service.mjs +1 -1
- package/esm2022/lib/services/models/domain.service.mjs +1 -1
- package/esm2022/lib/services/models/equipment.service.mjs +14 -13
- package/esm2022/lib/services/models/event.service.mjs +1 -1
- package/esm2022/lib/services/models/feature.service.mjs +8 -7
- package/esm2022/lib/services/models/hashtag.service.mjs +1 -1
- package/esm2022/lib/services/models/layer.service.mjs +1 -1
- package/esm2022/lib/services/models/measurement.service.mjs +1 -1
- package/esm2022/lib/services/models/mission.service.mjs +1 -1
- package/esm2022/lib/services/models/navigation.service.mjs +1 -1
- package/esm2022/lib/services/models/node.service.mjs +1 -1
- package/esm2022/lib/services/models/object3D.service.mjs +1 -1
- package/esm2022/lib/services/models/operation.service.mjs +1 -1
- package/esm2022/lib/services/models/organisation.service.mjs +1 -1
- package/esm2022/lib/services/models/plan.service.mjs +1 -1
- package/esm2022/lib/services/models/poi.service.mjs +27 -27
- package/esm2022/lib/services/models/profile.service.mjs +1 -1
- package/esm2022/lib/services/models/property.service.mjs +1 -1
- package/esm2022/lib/services/models/space.service.mjs +48 -6
- package/esm2022/lib/services/models/template.service.mjs +1 -1
- package/esm2022/lib/services/models/ticket.service.mjs +27 -8
- package/esm2022/lib/services/models/visit.service.mjs +17 -5
- package/esm2022/lib/services/models/zone.service.mjs +1 -1
- package/esm2022/lib/services/navigator.service.mjs +14 -1
- package/esm2022/lib/services/s3.service.mjs +18 -1
- package/esm2022/lib/services/search.service.mjs +1 -1
- package/esm2022/lib/services/support.service.mjs +1 -1
- package/esm2022/lib/services/tag.service.mjs +16 -16
- package/esm2022/lib/services/threejs-import.service.mjs +151 -0
- package/esm2022/lib/services/threejs.service.mjs +1587 -0
- package/esm2022/lib/services/user.service.mjs +1 -1
- package/esm2022/lib/services/utils/matterport-transform.util.mjs +1 -1
- package/esm2022/lib/services/validators.service.mjs +1 -1
- package/esm2022/lib/services/viewer.service.mjs +402 -5
- package/esm2022/lib/services/zone-drawer.service.mjs +1 -1
- package/esm2022/lib/services/zoneChange.service.mjs +10 -1
- package/esm2022/lib/tokens/matterport-sdk-key.token.mjs +1 -1
- package/esm2022/lib/types.service.mjs +2 -1
- package/esm2022/lib/validators/email.directive.mjs +1 -1
- package/esm2022/lib/validators/no-empty.directive.mjs +1 -1
- package/esm2022/lib/validators/number.directive.mjs +1 -1
- package/esm2022/lib/validators/text.directive.mjs +1 -1
- package/esm2022/public-api.mjs +3 -1
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs +8046 -5724
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
- package/lib/helpers.service.d.ts +43 -0
- package/lib/services/models/base-object.service.d.ts +11 -1
- package/lib/services/models/equipment.service.d.ts +2 -1
- package/lib/services/models/feature.service.d.ts +2 -1
- package/lib/services/models/space.service.d.ts +22 -1
- package/lib/services/models/ticket.service.d.ts +4 -1
- package/lib/services/models/visit.service.d.ts +9 -1
- package/lib/services/s3.service.d.ts +8 -0
- package/lib/services/threejs-import.service.d.ts +31 -0
- package/lib/services/threejs.service.d.ts +521 -0
- package/lib/services/viewer.service.d.ts +152 -1
- package/lib/services/zoneChange.service.d.ts +9 -0
- package/lib/types.service.d.ts +15 -1
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
- package/src/assets/vs-app.json +67 -67
|
@@ -0,0 +1,1587 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
|
|
4
|
+
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
|
|
5
|
+
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
|
|
6
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
7
|
+
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader';
|
|
8
|
+
import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
|
|
9
|
+
import { Subject } from 'rxjs';
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
/**
|
|
12
|
+
* Builds an orthonormal frame from three points, or null when they are (nearly) collinear.
|
|
13
|
+
*
|
|
14
|
+
* Three points define a plane, and a plane plus an origin defines an orientation. Collinear points
|
|
15
|
+
* do not: they leave the rotation about their common line undetermined, which is why the caller has
|
|
16
|
+
* to reject them rather than produce a confident wrong answer.
|
|
17
|
+
*/
|
|
18
|
+
function frameFromPoints(a, b, c) {
|
|
19
|
+
const e1 = b.clone().sub(a);
|
|
20
|
+
if (e1.lengthSq() < 1e-12) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
e1.normalize();
|
|
24
|
+
const toC = c.clone().sub(a);
|
|
25
|
+
const e2 = toC.clone().sub(e1.clone().multiplyScalar(toC.dot(e1)));
|
|
26
|
+
if (e2.lengthSq() < 1e-12) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
e2.normalize();
|
|
30
|
+
return new THREE.Matrix4().makeBasis(e1, e2, e1.clone().cross(e2));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Solves the transform that maps stored tag coordinates onto the loaded model.
|
|
34
|
+
*
|
|
35
|
+
* Takes exactly the correspondences a person can produce by hand: three tags, each pointed at twice
|
|
36
|
+
* - once where it currently sits, once where it belongs. That is the minimum that pins down all
|
|
37
|
+
* three parts of the answer. One pair fixes only translation; two leave the rotation about the line
|
|
38
|
+
* between them free; three resolve it.
|
|
39
|
+
*
|
|
40
|
+
* Returns null when the points are collinear or degenerate, because a wrong alignment applied
|
|
41
|
+
* silently is worse than none.
|
|
42
|
+
*/
|
|
43
|
+
export function solveThreeJsAlignment(pairs) {
|
|
44
|
+
if (!pairs || pairs.length < 3) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const sources = pairs.slice(0, 3).map((pair) => new THREE.Vector3(pair.source.x, pair.source.y, pair.source.z));
|
|
48
|
+
const targets = pairs.slice(0, 3).map((pair) => new THREE.Vector3(pair.target.x, pair.target.y, pair.target.z));
|
|
49
|
+
const sourceFrame = frameFromPoints(sources[0], sources[1], sources[2]);
|
|
50
|
+
const targetFrame = frameFromPoints(targets[0], targets[1], targets[2]);
|
|
51
|
+
if (!sourceFrame || !targetFrame) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
// A rotation's inverse is its transpose, so this is the rotation carrying one frame onto the other.
|
|
55
|
+
const rotation = targetFrame.multiply(sourceFrame.clone().transpose());
|
|
56
|
+
// Scale from how far apart the points are in each space, averaged over the three edges so a single
|
|
57
|
+
// sloppy click does not set it on its own.
|
|
58
|
+
const edges = [[0, 1], [1, 2], [0, 2]];
|
|
59
|
+
let ratioSum = 0;
|
|
60
|
+
let ratioCount = 0;
|
|
61
|
+
for (const [i, j] of edges) {
|
|
62
|
+
const sourceLength = sources[i].distanceTo(sources[j]);
|
|
63
|
+
const targetLength = targets[i].distanceTo(targets[j]);
|
|
64
|
+
if (sourceLength > 1e-9) {
|
|
65
|
+
ratioSum += targetLength / sourceLength;
|
|
66
|
+
ratioCount += 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (ratioCount === 0) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const scale = ratioSum / ratioCount;
|
|
73
|
+
// The group applies position, then rotation, then scale, so a point lands at t + s*R*p. Solving
|
|
74
|
+
// that for t at the centroids gives the translation.
|
|
75
|
+
const sourceCentroid = sources.reduce((sum, v) => sum.add(v), new THREE.Vector3()).multiplyScalar(1 / 3);
|
|
76
|
+
const targetCentroid = targets.reduce((sum, v) => sum.add(v), new THREE.Vector3()).multiplyScalar(1 / 3);
|
|
77
|
+
const translation = targetCentroid
|
|
78
|
+
.clone()
|
|
79
|
+
.sub(sourceCentroid.clone().applyMatrix4(rotation).multiplyScalar(scale));
|
|
80
|
+
const euler = new THREE.Euler().setFromRotationMatrix(rotation);
|
|
81
|
+
return {
|
|
82
|
+
translation: { x: translation.x, y: translation.y, z: translation.z },
|
|
83
|
+
rotation: { x: euler.x, y: euler.y, z: euler.z },
|
|
84
|
+
scale,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export class ThreeJsService {
|
|
88
|
+
ngZone;
|
|
89
|
+
scene;
|
|
90
|
+
camera;
|
|
91
|
+
renderer;
|
|
92
|
+
controls;
|
|
93
|
+
animationFrameId;
|
|
94
|
+
// Raycasting and interaction
|
|
95
|
+
raycaster = new THREE.Raycaster();
|
|
96
|
+
mouse = new THREE.Vector2();
|
|
97
|
+
isDraggingCamera = false;
|
|
98
|
+
isDraggingTimer;
|
|
99
|
+
clickableObjects = [];
|
|
100
|
+
// Observables for the viewer component
|
|
101
|
+
objectHovered$ = new Subject();
|
|
102
|
+
objectClicked$ = new Subject();
|
|
103
|
+
/** Emits when the user clicks a tag, mirroring Matterport's tag click. */
|
|
104
|
+
tagClicked$ = new Subject();
|
|
105
|
+
/** Emits a tag's label while the pointer is over it, and null when it leaves. */
|
|
106
|
+
tagHovered$ = new Subject();
|
|
107
|
+
// Tags live under the model rather than the scene, so the centering and scaling applied when the
|
|
108
|
+
// model is placed carries over and POI coordinates can stay in the model's own space.
|
|
109
|
+
tagGroup = new THREE.Group();
|
|
110
|
+
tagSprites = new Map();
|
|
111
|
+
canvasResizeObserver;
|
|
112
|
+
/** Aborts the in-flight model download. Parsing cannot be interrupted, only not started. */
|
|
113
|
+
loadAbortController;
|
|
114
|
+
// The loaded base model, kept so tag placement can raycast against its geometry.
|
|
115
|
+
modelRoot;
|
|
116
|
+
/** True while the next click on the model places a tag instead of orbiting. */
|
|
117
|
+
placingTag = false;
|
|
118
|
+
/** What the armed placement is for, echoed back when the spot is picked. */
|
|
119
|
+
placementPoiType = null;
|
|
120
|
+
/** Position and normal of the last placed tag, in model space, awaiting commit to a POI. */
|
|
121
|
+
pendingTagPlacement = null;
|
|
122
|
+
/** Pointer position waiting for a preview pick, coalesced to one per frame. */
|
|
123
|
+
pendingPreviewEvent = null;
|
|
124
|
+
/**
|
|
125
|
+
* Layer carrying the model alone, so the depth pick can render it without the tags.
|
|
126
|
+
*
|
|
127
|
+
* Layers are tested per object at render time and children are traversed regardless of whether
|
|
128
|
+
* their parent passed, so marking the model's own meshes is enough - no re-parenting.
|
|
129
|
+
*/
|
|
130
|
+
static PICK_LAYER = 1;
|
|
131
|
+
/** Cache Storage bucket holding downloaded models, and how many to keep. */
|
|
132
|
+
static MODEL_CACHE = 'smarterplan-3d-models';
|
|
133
|
+
static MODEL_CACHE_SIZE = 3;
|
|
134
|
+
pickTarget;
|
|
135
|
+
pickCamera;
|
|
136
|
+
pickDepthMaterial;
|
|
137
|
+
pickPixel = new Uint8Array(4);
|
|
138
|
+
/** Emits once a tag is dropped on the model, so the UI can confirm and persist it. */
|
|
139
|
+
tagPlaced$ = new Subject();
|
|
140
|
+
/** Emits when the calibration walkthrough is abandoned, so the panel driving it can close. */
|
|
141
|
+
alignmentCancelled$ = new Subject();
|
|
142
|
+
/** Translucent marker shown under the cursor while placing. */
|
|
143
|
+
placementPreview;
|
|
144
|
+
aligning = false;
|
|
145
|
+
alignmentPairs = [];
|
|
146
|
+
alignmentSource = null;
|
|
147
|
+
/** Drives the alignment walkthrough's UI: how many pairs are done and what it wants next. */
|
|
148
|
+
alignmentProgress$ = new Subject();
|
|
149
|
+
/** In-flight camera move started by goToTag, advanced by the render loop. */
|
|
150
|
+
cameraTween = null;
|
|
151
|
+
/** Emits true while a heavy OBJ file is being parsed (in a worker, when available), false once done. */
|
|
152
|
+
modelLoading$ = new Subject();
|
|
153
|
+
/**
|
|
154
|
+
* Download progress for GLB models, as a 0-1 fraction, or null when the total size is unknown.
|
|
155
|
+
*
|
|
156
|
+
* A null means the server did not send Content-Length (chunked responses, some proxies) and no
|
|
157
|
+
* percentage can be derived; show an indeterminate indicator rather than guessing at one.
|
|
158
|
+
*/
|
|
159
|
+
modelLoadProgress$ = new Subject();
|
|
160
|
+
/**
|
|
161
|
+
* Emits when a load actually stopped short, which is not the same as asking it to.
|
|
162
|
+
*
|
|
163
|
+
* cancelModelLoad() can arrive after parse() has taken the main thread, and parse cannot be
|
|
164
|
+
* interrupted - the model finishes loading regardless. Only this tells the UI which of the two
|
|
165
|
+
* happened, so it does not offer to reload something that is already on screen.
|
|
166
|
+
*/
|
|
167
|
+
modelLoadCancelled$ = new Subject();
|
|
168
|
+
// Background parsing of heavy .obj files, provided by the hosting app (see setObjParserWorker).
|
|
169
|
+
// Angular libraries built with ng-packagr can't bundle `new Worker(new URL(...))` themselves,
|
|
170
|
+
// so the app instantiates the worker (its build pipeline supports it) and hands it over here.
|
|
171
|
+
objParserWorker;
|
|
172
|
+
objParserPendingRequests = new Map();
|
|
173
|
+
constructor(ngZone) {
|
|
174
|
+
this.ngZone = ngZone;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Registers a Web Worker used to parse heavy .obj files off the main thread.
|
|
178
|
+
* Must be instantiated by the consuming app (e.g. `new Worker(new URL('./obj-parser.worker', import.meta.url))`),
|
|
179
|
+
* since a library packaged with ng-packagr cannot bundle worker files itself.
|
|
180
|
+
* When no worker is provided, loadObjModel falls back to parsing on the main thread.
|
|
181
|
+
*/
|
|
182
|
+
setObjParserWorker(worker) {
|
|
183
|
+
this.objParserWorker = worker;
|
|
184
|
+
this.objParserWorker.onmessage = ({ data }) => {
|
|
185
|
+
const pending = this.objParserPendingRequests.get(data.requestId);
|
|
186
|
+
if (!pending) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this.objParserPendingRequests.delete(data.requestId);
|
|
190
|
+
if (data.success && data.meshes) {
|
|
191
|
+
pending.resolve(data.meshes);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
pending.reject(new Error(data.error || 'Unknown error while parsing OBJ file in worker'));
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
requestObjParseInWorker(objUrl) {
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
201
|
+
this.objParserPendingRequests.set(requestId, { resolve, reject });
|
|
202
|
+
this.objParserWorker.postMessage({ requestId, objUrl });
|
|
203
|
+
// Diagnostic only: large models legitimately take a while, this isn't a timeout/abort,
|
|
204
|
+
// just a signal in case the worker went silent (e.g. it ran out of memory mid-parse).
|
|
205
|
+
setTimeout(() => {
|
|
206
|
+
if (this.objParserPendingRequests.has(requestId)) {
|
|
207
|
+
console.warn(`ThreeJsService: obj-parser worker has not replied after 60s for request ${requestId} (objUrl=${objUrl}). Still waiting - large files can take a while, but this may also mean the worker hung or ran out of memory.`);
|
|
208
|
+
}
|
|
209
|
+
}, 60000);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/** Rebuilds a THREE.Mesh from the raw typed arrays sent back by the parser worker. */
|
|
213
|
+
meshFromParsedData(data, materials) {
|
|
214
|
+
const geometry = new THREE.BufferGeometry();
|
|
215
|
+
geometry.setAttribute('position', new THREE.BufferAttribute(data.position, 3));
|
|
216
|
+
if (data.uv) {
|
|
217
|
+
geometry.setAttribute('uv', new THREE.BufferAttribute(data.uv, 2));
|
|
218
|
+
}
|
|
219
|
+
// Index has to be set before any normal computation, which reads it to average shared faces.
|
|
220
|
+
if (data.index) {
|
|
221
|
+
geometry.setIndex(new THREE.BufferAttribute(data.index, 1));
|
|
222
|
+
}
|
|
223
|
+
if (data.normal) {
|
|
224
|
+
geometry.setAttribute('normal', new THREE.BufferAttribute(data.normal, 3));
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
geometry.computeVertexNormals();
|
|
228
|
+
}
|
|
229
|
+
// Box3.setFromObject() reuses an already-set boundingBox instead of walking every vertex,
|
|
230
|
+
// so adopting the worker's makes framing the camera cheap no matter how dense the mesh is.
|
|
231
|
+
if (data.boundingBox) {
|
|
232
|
+
geometry.boundingBox = new THREE.Box3(new THREE.Vector3(...data.boundingBox.min), new THREE.Vector3(...data.boundingBox.max));
|
|
233
|
+
}
|
|
234
|
+
const resolvedMaterials = data.materialNames.map((name) => {
|
|
235
|
+
const material = materials && name ? materials.create(name) : undefined;
|
|
236
|
+
if (!material) {
|
|
237
|
+
console.warn(`ThreeJsService: no MTL material named "${name}" for mesh "${data.name}", using a plain fallback (no texture).`);
|
|
238
|
+
}
|
|
239
|
+
return material || new THREE.MeshPhongMaterial();
|
|
240
|
+
});
|
|
241
|
+
let mesh;
|
|
242
|
+
if (data.groups && data.groups.length > 0) {
|
|
243
|
+
data.groups.forEach((group) => geometry.addGroup(group.start, group.count, group.materialIndex));
|
|
244
|
+
mesh = new THREE.Mesh(geometry, resolvedMaterials);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
mesh = new THREE.Mesh(geometry, resolvedMaterials[0]);
|
|
248
|
+
}
|
|
249
|
+
mesh.name = data.name;
|
|
250
|
+
return mesh;
|
|
251
|
+
}
|
|
252
|
+
init(canvas) {
|
|
253
|
+
// This service is a singleton but the viewer component is rebuilt on every navigation, so state
|
|
254
|
+
// from the previous scene is still here. Two things outlive it and have to go first:
|
|
255
|
+
//
|
|
256
|
+
// - A download still in flight. Its onLoaded resolves against whatever `this.scene` points at
|
|
257
|
+
// by then, so the model being abandoned would be handed to the scene built below.
|
|
258
|
+
// - The tag group, which is a field and keeps its sprites. They would be re-parented under the
|
|
259
|
+
// new model and show the previous visit's tags on it.
|
|
260
|
+
this.discardCurrentLoad();
|
|
261
|
+
this.clearTags();
|
|
262
|
+
this.cameraTween = null;
|
|
263
|
+
this.modelRoot = undefined;
|
|
264
|
+
// 1. Setup Scene
|
|
265
|
+
this.scene = new THREE.Scene();
|
|
266
|
+
this.scene.background = new THREE.Color(0xe0f7ff); // from POC
|
|
267
|
+
// 2. Setup Camera
|
|
268
|
+
this.camera = new THREE.PerspectiveCamera(75, canvas.clientWidth / canvas.clientHeight, 0.1, 10000);
|
|
269
|
+
this.camera.position.set(0, 5, 10); // will be overridden later if passed
|
|
270
|
+
// 3. Setup WebGL Renderer
|
|
271
|
+
this.renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
|
|
272
|
+
this.renderer.setSize(canvas.clientWidth, canvas.clientHeight);
|
|
273
|
+
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
|
|
274
|
+
this.observeCanvasResize(canvas);
|
|
275
|
+
// Fix for Three.js r147 vs r152+ color management
|
|
276
|
+
if ('outputColorSpace' in this.renderer) {
|
|
277
|
+
this.renderer.outputColorSpace = 'srgb'; // THREE.SRGBColorSpace
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
this.renderer.outputEncoding = 3001; // THREE.sRGBEncoding
|
|
281
|
+
}
|
|
282
|
+
// Without tone mapping anything brighter than 1.0 is simply cut off to pure white. Captured
|
|
283
|
+
// textures carry a small fraction of very bright pixels - sky reflected in windows, sunlit
|
|
284
|
+
// render - and once lighting pushes those past the limit they flatten into white specks with
|
|
285
|
+
// no detail left. Rolling the highlights off instead keeps that detail and keeps bright areas
|
|
286
|
+
// reading as bright rather than blown.
|
|
287
|
+
this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
288
|
+
this.renderer.toneMappingExposure = 1.0;
|
|
289
|
+
// 4. Lighting, kept deliberately restrained and neutral.
|
|
290
|
+
//
|
|
291
|
+
// Captured/photogrammetry models already have lighting baked into their textures (the sun and
|
|
292
|
+
// shadows came with the photos), so lighting them again distorts what they actually look like.
|
|
293
|
+
// The intensities below add up to roughly 1.0 on a surface facing the viewer, which renders the
|
|
294
|
+
// texture at its true value. Anything above 1.0 is clipped to pure white by the renderer, which
|
|
295
|
+
// is what turns bright areas into flat white patches with no detail left in them.
|
|
296
|
+
//
|
|
297
|
+
// Neutral white is used rather than a warm tint for the same reason: a tinted light shifts every
|
|
298
|
+
// colour in the model away from what was captured.
|
|
299
|
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
|
|
300
|
+
this.scene.add(ambientLight);
|
|
301
|
+
// Headlight rather than a fixed sun: parented to the camera so it always points at whatever is
|
|
302
|
+
// being looked at, giving shape to surfaces without ever leaving a face unlit. No shadow casting
|
|
303
|
+
// - shadows would double up with the ones already baked into the textures, and the shadow map
|
|
304
|
+
// was a real cost per frame.
|
|
305
|
+
const headLight = new THREE.DirectionalLight(0xffffff, 0.35);
|
|
306
|
+
headLight.position.set(0, 0, 1);
|
|
307
|
+
this.camera.add(headLight);
|
|
308
|
+
// A light only contributes while it is part of the scene graph, and this one hangs off the camera.
|
|
309
|
+
this.scene.add(this.camera);
|
|
310
|
+
// 5. Setup Controls (from POC)
|
|
311
|
+
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
|
|
312
|
+
this.controls.rotateSpeed = 1.0;
|
|
313
|
+
this.controls.dampingFactor = 0.1;
|
|
314
|
+
this.controls.enableDamping = true;
|
|
315
|
+
this.controls.mouseButtons = {
|
|
316
|
+
LEFT: THREE.MOUSE.ROTATE,
|
|
317
|
+
MIDDLE: THREE.MOUSE.DOLLY,
|
|
318
|
+
RIGHT: THREE.MOUSE.PAN,
|
|
319
|
+
};
|
|
320
|
+
this.controls.target.set(0, 0, 0);
|
|
321
|
+
// Event listeners on controls to differentiate dragging vs clicking
|
|
322
|
+
this.controls.addEventListener('start', () => {
|
|
323
|
+
this.isDraggingTimer = setTimeout(() => {
|
|
324
|
+
this.isDraggingCamera = true;
|
|
325
|
+
}, 150);
|
|
326
|
+
});
|
|
327
|
+
this.controls.addEventListener('end', () => {
|
|
328
|
+
clearTimeout(this.isDraggingTimer);
|
|
329
|
+
if (this.isDraggingCamera) {
|
|
330
|
+
setTimeout(() => {
|
|
331
|
+
this.isDraggingCamera = false;
|
|
332
|
+
}, 250);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
// Interaction Event Listeners on canvas
|
|
336
|
+
canvas.addEventListener('click', this.onMouseClick.bind(this), false);
|
|
337
|
+
canvas.addEventListener('mousemove', this.onMouseMove.bind(this), false);
|
|
338
|
+
// Escape backs out of placement and calibration. On the window rather than the canvas, which
|
|
339
|
+
// would need focus the user has no reason to have given it - they are aiming with the mouse.
|
|
340
|
+
// Removed first because this service is a singleton: init() runs again for every canvas, and
|
|
341
|
+
// the previous registration would otherwise still be attached.
|
|
342
|
+
window.removeEventListener('keydown', this.onKeyDown);
|
|
343
|
+
this.pickTarget?.dispose();
|
|
344
|
+
this.pickTarget = undefined;
|
|
345
|
+
this.pickDepthMaterial?.dispose();
|
|
346
|
+
this.pickDepthMaterial = undefined;
|
|
347
|
+
this.pickCamera = undefined;
|
|
348
|
+
window.addEventListener('keydown', this.onKeyDown);
|
|
349
|
+
// 6. Start the render loop
|
|
350
|
+
this.ngZone.runOutsideAngular(() => {
|
|
351
|
+
this.animate();
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Path (served by the app) holding basis_transcoder.js/.wasm, which KTX2Loader needs to turn
|
|
356
|
+
* Basis Universal textures into whatever compressed format the local GPU actually supports.
|
|
357
|
+
*/
|
|
358
|
+
ktx2TranscoderPath = 'assets/basis/';
|
|
359
|
+
/** Overrides where the Basis transcoder files are served from. */
|
|
360
|
+
setKtx2TranscoderPath(path) {
|
|
361
|
+
this.ktx2TranscoderPath = path;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Loads a base 3D model in GLTF/GLB format - the preferred format for large models.
|
|
365
|
+
* Unlike .obj, geometry arrives as binary typed arrays (no text parsing to offload to a worker)
|
|
366
|
+
* and stays indexed, so far less data reaches the GPU. KTX2 textures are supported too: they
|
|
367
|
+
* stay compressed in VRAM instead of being decoded to raw pixels like JPEG/PNG.
|
|
368
|
+
*/
|
|
369
|
+
loadGLTFModel(url, cameraPosition, rotation, cacheKey) {
|
|
370
|
+
const loader = new GLTFLoader();
|
|
371
|
+
// KTX2 needs the renderer to report which compressed formats this GPU accepts, so this has to
|
|
372
|
+
// happen after init(). Guarded so a model without KTX2 textures still loads if it's missing.
|
|
373
|
+
if (this.renderer) {
|
|
374
|
+
try {
|
|
375
|
+
const ktx2Loader = new KTX2Loader()
|
|
376
|
+
.setTranscoderPath(this.ktx2TranscoderPath)
|
|
377
|
+
.detectSupport(this.renderer);
|
|
378
|
+
loader.setKTX2Loader(ktx2Loader);
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
console.warn('ThreeJsService: could not set up KTX2 texture support, falling back to uncompressed textures.', error);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// Lets gltfpack/gltf-transform output using EXT_meshopt_compression load without extra setup.
|
|
385
|
+
loader.setMeshoptDecoder(MeshoptDecoder);
|
|
386
|
+
this.modelLoading$.next(true);
|
|
387
|
+
this.modelLoadProgress$.next(0);
|
|
388
|
+
// Order matters: normals must exist before anisotropy, and the Phong conversion has to run
|
|
389
|
+
// before side/flatShading are touched so those settings land on the materials we keep.
|
|
390
|
+
const onLoaded = (gltf) => {
|
|
391
|
+
this.convertToPhongMaterials(gltf.scene);
|
|
392
|
+
this.forceDoubleSidedMaterials(gltf.scene);
|
|
393
|
+
this.ensureVertexNormals(gltf.scene);
|
|
394
|
+
this.tuneTextureSampling(gltf.scene);
|
|
395
|
+
this.placeLoadedObjectInScene(gltf.scene, cameraPosition, rotation);
|
|
396
|
+
this.modelLoading$.next(false);
|
|
397
|
+
};
|
|
398
|
+
const onError = (error) => {
|
|
399
|
+
// Cancelling is a decision, not a failure: clear the overlay without an error in the console.
|
|
400
|
+
if (error?.name === 'AbortError') {
|
|
401
|
+
// Only a cancel of the load that is still current came from the user; anything else is this
|
|
402
|
+
// load having been superseded or discarded.
|
|
403
|
+
if (this.loadAbortController?.signal.aborted) {
|
|
404
|
+
this.modelLoadCancelled$.next();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
console.error('Error loading GLTF model:', error);
|
|
409
|
+
}
|
|
410
|
+
this.modelLoading$.next(false);
|
|
411
|
+
this.modelLoadProgress$.next(null);
|
|
412
|
+
};
|
|
413
|
+
// GLTFLoader decodes embedded images with createImageBitmap when the browser offers it, and on
|
|
414
|
+
// large texture atlases that path returns visibly corrupt data - scattered white blocks across
|
|
415
|
+
// the model. The exact same bytes decoded through an HTMLImageElement, which is what the .obj
|
|
416
|
+
// path uses via MTLLoader, come out clean. GLTFLoader already falls back to that decoder for
|
|
417
|
+
// Safari and older Firefox; this simply extends the fallback to cover this case too.
|
|
418
|
+
//
|
|
419
|
+
// The decoder is chosen while GLTFParser is being constructed, which happens synchronously
|
|
420
|
+
// inside parse(), so hiding createImageBitmap only has to hold across that call rather than
|
|
421
|
+
// for the whole download.
|
|
422
|
+
this.fetchWithProgress(url, cacheKey)
|
|
423
|
+
// parse() holds the main thread for seconds on a large model. Yield until the browser has
|
|
424
|
+
// actually painted first, otherwise the final progress update never reaches the screen and
|
|
425
|
+
// the bar appears to stall short of the end.
|
|
426
|
+
.then((buffer) => this.afterNextPaint().then(() => buffer))
|
|
427
|
+
.then((buffer) => {
|
|
428
|
+
// parse() cannot be interrupted, so a cancel that lands during the paint yield has to be
|
|
429
|
+
// caught here - the last moment before the main thread is committed for several seconds.
|
|
430
|
+
if (this.loadAbortController?.signal.aborted) {
|
|
431
|
+
throw new DOMException('Model load cancelled', 'AbortError');
|
|
432
|
+
}
|
|
433
|
+
const globalScope = globalThis;
|
|
434
|
+
const originalCreateImageBitmap = globalScope.createImageBitmap;
|
|
435
|
+
globalScope.createImageBitmap = undefined;
|
|
436
|
+
try {
|
|
437
|
+
loader.parse(buffer, url.substring(0, url.lastIndexOf('/') + 1), onLoaded, onError);
|
|
438
|
+
}
|
|
439
|
+
finally {
|
|
440
|
+
globalScope.createImageBitmap = originalCreateImageBitmap;
|
|
441
|
+
}
|
|
442
|
+
})
|
|
443
|
+
.catch(onError);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Stops the model download in progress, if any.
|
|
447
|
+
*
|
|
448
|
+
* Only the download can be interrupted: parse() runs synchronously and holds the main thread until
|
|
449
|
+
* it finishes, so once it has begun there is nothing to cancel. In practice the download is the
|
|
450
|
+
* long part, which is where someone reaches for this.
|
|
451
|
+
*/
|
|
452
|
+
cancelModelLoad() {
|
|
453
|
+
this.loadAbortController?.abort();
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Abandons the load in progress without reporting it as cancelled.
|
|
457
|
+
*
|
|
458
|
+
* Dropping the controller is what makes the difference: onError treats an abort as user-initiated
|
|
459
|
+
* only while the controller that aborted is still the current one. Tearing down or rebuilding the
|
|
460
|
+
* scene is not something the person asked for, so it must not surface as "loading cancelled".
|
|
461
|
+
*/
|
|
462
|
+
discardCurrentLoad() {
|
|
463
|
+
this.loadAbortController?.abort();
|
|
464
|
+
this.loadAbortController = undefined;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Resolves once the browser has painted at least one frame.
|
|
468
|
+
*
|
|
469
|
+
* A requestAnimationFrame callback runs *before* that frame is painted, so a single one is not
|
|
470
|
+
* enough to guarantee pending DOM changes are on screen; the second fires after the first frame
|
|
471
|
+
* has gone out. Falls back to a timeout where rAF isn't available (SSR, tests).
|
|
472
|
+
*/
|
|
473
|
+
afterNextPaint() {
|
|
474
|
+
if (typeof requestAnimationFrame !== 'function') {
|
|
475
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
476
|
+
}
|
|
477
|
+
return new Promise((resolve) => {
|
|
478
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Downloads a file, reporting progress on modelLoadProgress$ as it arrives.
|
|
483
|
+
*
|
|
484
|
+
* GLTFLoader.load() would report progress through XHR, but this path deliberately uses fetch() +
|
|
485
|
+
* parse() instead (see loadGLTFModel for why), so progress is read off the response stream here.
|
|
486
|
+
* That is also the more useful number: on a model of this size the download dominates, and the
|
|
487
|
+
* stream reports it as it happens rather than in one jump at the end.
|
|
488
|
+
*/
|
|
489
|
+
async fetchWithProgress(url, cacheKey) {
|
|
490
|
+
// A load already running is replaced, not raced: whichever finished last would otherwise win.
|
|
491
|
+
this.loadAbortController?.abort();
|
|
492
|
+
this.loadAbortController = new AbortController();
|
|
493
|
+
const { signal } = this.loadAbortController;
|
|
494
|
+
const cached = await this.readModelFromCache(cacheKey);
|
|
495
|
+
if (cached) {
|
|
496
|
+
this.modelLoadProgress$.next(1);
|
|
497
|
+
return cached;
|
|
498
|
+
}
|
|
499
|
+
const response = await fetch(url, { signal });
|
|
500
|
+
if (!response.ok) {
|
|
501
|
+
throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
|
502
|
+
}
|
|
503
|
+
const contentLength = Number(response.headers.get('Content-Length'));
|
|
504
|
+
const total = Number.isFinite(contentLength) && contentLength > 0 ? contentLength : 0;
|
|
505
|
+
// Without a readable stream (older browsers, some polyfills) there is nothing to measure, so
|
|
506
|
+
// fall back to a plain buffered read and let the UI show an indeterminate indicator.
|
|
507
|
+
if (!response.body?.getReader) {
|
|
508
|
+
this.modelLoadProgress$.next(null);
|
|
509
|
+
return response.arrayBuffer();
|
|
510
|
+
}
|
|
511
|
+
const reader = response.body.getReader();
|
|
512
|
+
// With a known size, write chunks straight into one pre-allocated buffer. Collecting them in an
|
|
513
|
+
// array and concatenating afterwards would hold both copies at once - on a 164 MB model that is
|
|
514
|
+
// an avoidable 300+ MB peak.
|
|
515
|
+
const buffer = total > 0 ? new Uint8Array(total) : null;
|
|
516
|
+
const chunks = [];
|
|
517
|
+
let usingBuffer = buffer !== null;
|
|
518
|
+
let received = 0;
|
|
519
|
+
// Chunks arrive in tens of kilobytes, so a 164 MB model produces thousands of them. Emitting on
|
|
520
|
+
// every one would run change detection just as often to move a bar that is measured in whole
|
|
521
|
+
// percent, so only report when the displayed value would actually change.
|
|
522
|
+
let lastReported = -1;
|
|
523
|
+
if (total === 0) {
|
|
524
|
+
this.modelLoadProgress$.next(null);
|
|
525
|
+
}
|
|
526
|
+
for (;;) {
|
|
527
|
+
const { done, value } = await reader.read();
|
|
528
|
+
if (done) {
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
if (signal.aborted) {
|
|
532
|
+
// Free the connection rather than leaving it draining in the background.
|
|
533
|
+
await reader.cancel();
|
|
534
|
+
throw new DOMException('Model load cancelled', 'AbortError');
|
|
535
|
+
}
|
|
536
|
+
if (usingBuffer && received + value.length > total) {
|
|
537
|
+
// Content-Length understated the real size. Move what we have into the chunk list and stay
|
|
538
|
+
// there for the rest of the response, rather than truncating the tail.
|
|
539
|
+
chunks.push(buffer.slice(0, received));
|
|
540
|
+
usingBuffer = false;
|
|
541
|
+
}
|
|
542
|
+
if (usingBuffer) {
|
|
543
|
+
buffer.set(value, received);
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
chunks.push(value);
|
|
547
|
+
}
|
|
548
|
+
received += value.length;
|
|
549
|
+
if (total > 0) {
|
|
550
|
+
const percent = Math.min(Math.round((received / total) * 100), 100);
|
|
551
|
+
if (percent !== lastReported) {
|
|
552
|
+
lastReported = percent;
|
|
553
|
+
this.modelLoadProgress$.next(percent / 100);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
// The stream can end below the advertised size (or with no size at all). Land on a full bar
|
|
558
|
+
// rather than leaving it stuck at whatever the last chunk happened to reach.
|
|
559
|
+
if (total > 0 && lastReported !== 100) {
|
|
560
|
+
this.modelLoadProgress$.next(1);
|
|
561
|
+
}
|
|
562
|
+
let result;
|
|
563
|
+
if (usingBuffer && received === total) {
|
|
564
|
+
result = buffer.buffer;
|
|
565
|
+
}
|
|
566
|
+
else if (usingBuffer) {
|
|
567
|
+
// Fewer bytes than advertised: hand back exactly what arrived.
|
|
568
|
+
result = buffer.buffer.slice(0, received);
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
const merged = new Uint8Array(received);
|
|
572
|
+
let offset = 0;
|
|
573
|
+
chunks.forEach((chunk) => {
|
|
574
|
+
merged.set(chunk, offset);
|
|
575
|
+
offset += chunk.length;
|
|
576
|
+
});
|
|
577
|
+
result = merged.buffer;
|
|
578
|
+
}
|
|
579
|
+
await this.writeModelToCache(cacheKey, result);
|
|
580
|
+
return result;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* The model store, or null where the browser will not give us one.
|
|
584
|
+
*
|
|
585
|
+
* Cache Storage needs a secure context, so it is absent over plain http and in some embedded
|
|
586
|
+
* webviews. Every caller treats that as a miss - caching is an optimisation, never a requirement.
|
|
587
|
+
*/
|
|
588
|
+
async openModelCache() {
|
|
589
|
+
if (typeof caches === 'undefined') {
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
return await caches.open(ThreeJsService.MODEL_CACHE);
|
|
594
|
+
}
|
|
595
|
+
catch {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* A cached copy of this model, if one was stored.
|
|
601
|
+
*
|
|
602
|
+
* Keyed by the S3 key rather than the URL it is fetched from: that URL is signed, so it carries a
|
|
603
|
+
* fresh signature and expiry every time and would never match twice. The browser's own HTTP cache
|
|
604
|
+
* misses for the same reason, which is why this exists at all.
|
|
605
|
+
*/
|
|
606
|
+
async readModelFromCache(cacheKey) {
|
|
607
|
+
if (!cacheKey) {
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
try {
|
|
611
|
+
const cache = await this.openModelCache();
|
|
612
|
+
const hit = await cache?.match(this.modelCacheUrl(cacheKey));
|
|
613
|
+
return hit ? await hit.arrayBuffer() : null;
|
|
614
|
+
}
|
|
615
|
+
catch {
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
/** Stores a model for next time, evicting the oldest once a few are held. */
|
|
620
|
+
async writeModelToCache(cacheKey, model) {
|
|
621
|
+
if (!cacheKey) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
try {
|
|
625
|
+
const cache = await this.openModelCache();
|
|
626
|
+
if (!cache) {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
await cache.put(this.modelCacheUrl(cacheKey), new Response(model));
|
|
630
|
+
// Models run to hundreds of megabytes, so this cannot grow without limit. Cache Storage keeps
|
|
631
|
+
// insertion order, which makes the front of the list the least recently stored.
|
|
632
|
+
const keys = await cache.keys();
|
|
633
|
+
await Promise.all(keys.slice(0, Math.max(0, keys.length - ThreeJsService.MODEL_CACHE_SIZE))
|
|
634
|
+
.map((key) => cache.delete(key)));
|
|
635
|
+
}
|
|
636
|
+
catch {
|
|
637
|
+
// Out of quota, private browsing, storage disabled: the model is already loaded either way.
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
/** Cache Storage keys on a URL, so the S3 key is expressed as one. It is never requested. */
|
|
641
|
+
modelCacheUrl(cacheKey) {
|
|
642
|
+
return `https://3d-model-cache.smarterplan.local/${encodeURIComponent(cacheKey)}`;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Fits the camera's clipping planes to how big the model actually is.
|
|
646
|
+
*
|
|
647
|
+
* Depth precision depends on the far/near ratio, and the fixed 0.1 / 10000 the camera starts with
|
|
648
|
+
* is a ratio of 100000 for a model only ~10 units across. That wastes most of the depth buffer on
|
|
649
|
+
* empty space and lets nearly coincident surfaces - which captured meshes have everywhere -
|
|
650
|
+
* flicker against each other.
|
|
651
|
+
*/
|
|
652
|
+
adjustClippingPlanes(radius) {
|
|
653
|
+
if (radius < 10) {
|
|
654
|
+
this.camera.near = 0.01;
|
|
655
|
+
this.camera.far = 100;
|
|
656
|
+
}
|
|
657
|
+
else if (radius < 100) {
|
|
658
|
+
this.camera.near = 0.1;
|
|
659
|
+
this.camera.far = 1000;
|
|
660
|
+
}
|
|
661
|
+
else if (radius < 1000) {
|
|
662
|
+
this.camera.near = 10;
|
|
663
|
+
this.camera.far = 10000;
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
this.camera.near = 100;
|
|
667
|
+
this.camera.far = 1000000;
|
|
668
|
+
}
|
|
669
|
+
this.camera.updateProjectionMatrix();
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Swaps the PBR materials GLTFLoader builds for the plain Phong ones the .obj path produces.
|
|
673
|
+
*
|
|
674
|
+
* MeshStandardMaterial keeps a specular reflection even at metalness 0, since every dielectric
|
|
675
|
+
* reflects some light, and Fresnel drives that reflection towards white as a surface turns away
|
|
676
|
+
* from the viewer. On an aerial view of a facade most surfaces sit at exactly those grazing
|
|
677
|
+
* angles, which paints white highlights over window frames and roof edges - the same model
|
|
678
|
+
* loaded as .obj never shows them, because MTLLoader builds Phong materials and OBJLoader zeroes
|
|
679
|
+
* their specular when the .mtl declares Ns 0.
|
|
680
|
+
*
|
|
681
|
+
* Captured models carry their lighting in the texture and want no specular response at all, so
|
|
682
|
+
* matching the .obj path here is both the fix and the more faithful choice.
|
|
683
|
+
*/
|
|
684
|
+
convertToPhongMaterials(root) {
|
|
685
|
+
const converted = new Map();
|
|
686
|
+
root.traverse((child) => {
|
|
687
|
+
const mesh = child;
|
|
688
|
+
if (!mesh.isMesh) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
const convert = (source) => {
|
|
692
|
+
if (!source || !source.isMeshStandardMaterial) {
|
|
693
|
+
return source;
|
|
694
|
+
}
|
|
695
|
+
if (!converted.has(source)) {
|
|
696
|
+
const phong = new THREE.MeshPhongMaterial({
|
|
697
|
+
map: source.map,
|
|
698
|
+
color: source.color,
|
|
699
|
+
transparent: source.transparent,
|
|
700
|
+
opacity: source.opacity,
|
|
701
|
+
alphaTest: source.alphaTest,
|
|
702
|
+
side: source.side,
|
|
703
|
+
specular: new THREE.Color(0x000000),
|
|
704
|
+
shininess: 0,
|
|
705
|
+
});
|
|
706
|
+
phong.name = source.name;
|
|
707
|
+
converted.set(source, phong);
|
|
708
|
+
}
|
|
709
|
+
return converted.get(source);
|
|
710
|
+
};
|
|
711
|
+
mesh.material = Array.isArray(mesh.material)
|
|
712
|
+
? mesh.material.map(convert)
|
|
713
|
+
: convert(mesh.material);
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Renders every material double-sided.
|
|
718
|
+
*
|
|
719
|
+
* Reality capture meshes routinely contain triangles with reversed winding - an artefact of the
|
|
720
|
+
* reconstruction, not of the file. With the default FrontSide those triangles are culled, leaving
|
|
721
|
+
* scattered holes that show the scene background through the model. Against a light background
|
|
722
|
+
* they read as white specks with no discernible pattern.
|
|
723
|
+
*
|
|
724
|
+
* Drawing both sides costs a little fill rate but is what makes captured geometry render as
|
|
725
|
+
* solid; other viewers of this kind of content do the same unconditionally.
|
|
726
|
+
*/
|
|
727
|
+
forceDoubleSidedMaterials(root) {
|
|
728
|
+
const seen = new Set();
|
|
729
|
+
root.traverse((child) => {
|
|
730
|
+
const mesh = child;
|
|
731
|
+
if (!mesh.isMesh) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
735
|
+
materials.forEach((material) => {
|
|
736
|
+
if (material && !seen.has(material)) {
|
|
737
|
+
seen.add(material);
|
|
738
|
+
material.side = THREE.DoubleSide;
|
|
739
|
+
material.needsUpdate = true;
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Computes real vertex normals for any geometry that arrived without them.
|
|
746
|
+
*
|
|
747
|
+
* A glTF without a NORMAL attribute is spec-compliant, and GLTFLoader handles it by switching the
|
|
748
|
+
* material to flatShading, which makes the shader derive the normal per pixel from screen-space
|
|
749
|
+
* derivatives. That derivation collapses on the degenerate, needle-thin triangles that reality
|
|
750
|
+
* capture meshes are full of: the face has almost no area, the derived normal is meaningless, and
|
|
751
|
+
* lighting blows those fragments out to white specks - densest exactly where fine detail is.
|
|
752
|
+
*
|
|
753
|
+
* Computing normals from the geometry instead is robust to those triangles, since a degenerate
|
|
754
|
+
* face contributes almost nothing to the average and its neighbours dominate.
|
|
755
|
+
*/
|
|
756
|
+
ensureVertexNormals(root) {
|
|
757
|
+
root.traverse((child) => {
|
|
758
|
+
const mesh = child;
|
|
759
|
+
if (!mesh.isMesh) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const geometry = mesh.geometry;
|
|
763
|
+
if (geometry.getAttribute('normal')) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
geometry.computeVertexNormals();
|
|
767
|
+
// GLTFLoader already switched these materials to flatShading because normals were missing;
|
|
768
|
+
// now that real ones exist, let them be used instead of the derivative fallback.
|
|
769
|
+
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
770
|
+
materials.forEach((material) => {
|
|
771
|
+
if (material && material.flatShading) {
|
|
772
|
+
material.flatShading = false;
|
|
773
|
+
material.needsUpdate = true;
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Configures how every texture in a model is sampled: maximum anisotropy, and no mipmapping.
|
|
780
|
+
*
|
|
781
|
+
* Photogrammetry atlases pack their UV islands with unused gutter pixels between them. Mipmapping
|
|
782
|
+
* averages neighbouring pixels at each level, so those gutters bleed further into the islands the
|
|
783
|
+
* smaller the level gets - and viewing the whole model at once uses the smallest levels, painting
|
|
784
|
+
* bright gutter pixels over the edges of fine detail. Sampling the full-resolution image avoids
|
|
785
|
+
* that, at the cost of some shimmer on minified surfaces.
|
|
786
|
+
*
|
|
787
|
+
* Note that anisotropy only takes effect while mipmapping is on, so with the settings below it is
|
|
788
|
+
* inert; it is set anyway so the sampling stays correct for anyone who re-enables mipmaps here.
|
|
789
|
+
*/
|
|
790
|
+
tuneTextureSampling(root) {
|
|
791
|
+
if (!this.renderer) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const maxAnisotropy = this.renderer.capabilities.getMaxAnisotropy();
|
|
795
|
+
if (!maxAnisotropy || maxAnisotropy <= 1) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
const seen = new Set();
|
|
799
|
+
root.traverse((child) => {
|
|
800
|
+
const mesh = child;
|
|
801
|
+
if (!mesh.isMesh) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
805
|
+
materials.forEach((material) => {
|
|
806
|
+
if (!material) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
// Same texture is reused across the model's materials; only touch each one once.
|
|
810
|
+
['map', 'normalMap', 'roughnessMap', 'metalnessMap', 'emissiveMap', 'aoMap'].forEach((slot) => {
|
|
811
|
+
const texture = material[slot];
|
|
812
|
+
if (texture && !seen.has(texture)) {
|
|
813
|
+
seen.add(texture);
|
|
814
|
+
texture.anisotropy = maxAnisotropy;
|
|
815
|
+
texture.generateMipmaps = false;
|
|
816
|
+
texture.minFilter = THREE.LinearFilter;
|
|
817
|
+
texture.needsUpdate = true;
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
});
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Loads an OBJ model with MTL.
|
|
825
|
+
* The heavy text-parsing step runs in a Web Worker when one has been registered via
|
|
826
|
+
* setObjParserWorker (see there for why the app, not this library, provides it); this keeps
|
|
827
|
+
* the main thread free while large architecture models (millions of vertices) are parsed.
|
|
828
|
+
* Falls back to parsing on the main thread otherwise.
|
|
829
|
+
*/
|
|
830
|
+
loadObjModel(mtlUrl, objUrl, cameraPosition, rotation) {
|
|
831
|
+
const mtlLoader = new MTLLoader();
|
|
832
|
+
const basePath = mtlUrl.substring(0, mtlUrl.lastIndexOf('/') + 1);
|
|
833
|
+
mtlLoader.setPath(basePath);
|
|
834
|
+
const mtlFileName = mtlUrl.substring(mtlUrl.lastIndexOf('/') + 1);
|
|
835
|
+
this.modelLoading$.next(true);
|
|
836
|
+
mtlLoader.load(mtlFileName, (materials) => {
|
|
837
|
+
materials.preload();
|
|
838
|
+
if (this.objParserWorker) {
|
|
839
|
+
this.requestObjParseInWorker(objUrl)
|
|
840
|
+
.then((meshes) => {
|
|
841
|
+
const group = new THREE.Group();
|
|
842
|
+
meshes.forEach((meshData) => {
|
|
843
|
+
group.add(this.meshFromParsedData(meshData, materials));
|
|
844
|
+
});
|
|
845
|
+
this.placeLoadedObjectInScene(group, cameraPosition, rotation);
|
|
846
|
+
})
|
|
847
|
+
.catch((error) => {
|
|
848
|
+
console.error('Error parsing OBJ model in worker:', error);
|
|
849
|
+
})
|
|
850
|
+
.finally(() => this.modelLoading$.next(false));
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const objLoader = new OBJLoader();
|
|
854
|
+
objLoader.setMaterials(materials);
|
|
855
|
+
objLoader.load(objUrl, (object) => {
|
|
856
|
+
this.placeLoadedObjectInScene(object, cameraPosition, rotation);
|
|
857
|
+
this.modelLoading$.next(false);
|
|
858
|
+
}, undefined, (error) => {
|
|
859
|
+
console.error('Error loading OBJ model:', error);
|
|
860
|
+
this.modelLoading$.next(false);
|
|
861
|
+
});
|
|
862
|
+
}, undefined, (error) => {
|
|
863
|
+
console.error('Error loading MTL file:', error);
|
|
864
|
+
this.modelLoading$.next(false);
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
/** Centers, scales and adds a loaded object to the scene, then frames the camera on it. */
|
|
868
|
+
placeLoadedObjectInScene(object, cameraPosition, rotation) {
|
|
869
|
+
const box = new THREE.Box3().setFromObject(object);
|
|
870
|
+
const center = box.getCenter(new THREE.Vector3());
|
|
871
|
+
const size = box.getSize(new THREE.Vector3());
|
|
872
|
+
const maxDim = Math.max(size.x, size.y, size.z);
|
|
873
|
+
const scale = maxDim > 0 ? 10 / maxDim : 1;
|
|
874
|
+
// Two nested groups so tags can share the model's frame: `centered` carries the offset that
|
|
875
|
+
// moves the model's own origin to the scene origin, `group` carries the scale. A tag positioned
|
|
876
|
+
// with raw POI coordinates then lands in the same place the model's geometry does.
|
|
877
|
+
const group = new THREE.Group();
|
|
878
|
+
const centered = new THREE.Group();
|
|
879
|
+
centered.position.sub(center);
|
|
880
|
+
if (rotation) {
|
|
881
|
+
object.rotation.set(rotation.x, rotation.y, rotation.z);
|
|
882
|
+
}
|
|
883
|
+
this.modelRoot = object;
|
|
884
|
+
// Marks the model for the depth pick, which renders this layer alone - so tags and the ghost
|
|
885
|
+
// itself cannot end up being what the cursor reports as the surface under it.
|
|
886
|
+
object.traverse((child) => child.layers.enable(ThreeJsService.PICK_LAYER));
|
|
887
|
+
centered.add(object);
|
|
888
|
+
centered.add(this.tagGroup);
|
|
889
|
+
group.add(centered);
|
|
890
|
+
group.scale.setScalar(scale);
|
|
891
|
+
this.scene.add(group);
|
|
892
|
+
// Tags registered before the model finished loading were sized against the old scale.
|
|
893
|
+
this.tagSprites.forEach((sprite) => this.scaleTagForModel(sprite));
|
|
894
|
+
this.adjustClippingPlanes((size.length() / 2) * scale);
|
|
895
|
+
if (cameraPosition) {
|
|
896
|
+
this.camera.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
this.camera.position.set(0, 5, 15);
|
|
900
|
+
}
|
|
901
|
+
this.controls.target.set(0, 0, 0);
|
|
902
|
+
this.controls.update();
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Keeps the drawing buffer and camera matched to the canvas as it is resized.
|
|
906
|
+
*
|
|
907
|
+
* renderer.setSize() writes explicit pixel width/height into the canvas's style, overriding the
|
|
908
|
+
* 100%/100% it starts with. Without this, the canvas keeps whatever size it had at startup: hiding
|
|
909
|
+
* the top menu grows the container, the canvas does not follow, and the strip that opens up shows
|
|
910
|
+
* the page behind it instead of the scene.
|
|
911
|
+
*
|
|
912
|
+
* The parent is observed rather than the canvas, because resizing the canvas is what this reacts
|
|
913
|
+
* to - watching the element we resize would retrigger the observer.
|
|
914
|
+
*/
|
|
915
|
+
observeCanvasResize(canvas) {
|
|
916
|
+
const container = canvas.parentElement;
|
|
917
|
+
if (!container || typeof ResizeObserver === 'undefined') {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
this.canvasResizeObserver = new ResizeObserver(() => {
|
|
921
|
+
const width = container.clientWidth;
|
|
922
|
+
const height = container.clientHeight;
|
|
923
|
+
if (width === 0 || height === 0) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
this.camera.aspect = width / height;
|
|
927
|
+
this.camera.updateProjectionMatrix();
|
|
928
|
+
this.renderer.setSize(width, height);
|
|
929
|
+
});
|
|
930
|
+
this.canvasResizeObserver.observe(container);
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Starts the alignment walkthrough: pick a tag, then click where it really is, three times.
|
|
934
|
+
*
|
|
935
|
+
* Existing tags are the correspondences - they already carry the stored coordinate, so the person
|
|
936
|
+
* only has to supply the matching spot on the model. No new records are written; nothing is
|
|
937
|
+
* applied until solveAlignmentFromPairs() succeeds.
|
|
938
|
+
*/
|
|
939
|
+
startTagAlignment() {
|
|
940
|
+
this.aligning = true;
|
|
941
|
+
this.alignmentPairs = [];
|
|
942
|
+
this.alignmentSource = null;
|
|
943
|
+
if (this.renderer) {
|
|
944
|
+
this.renderer.domElement.style.cursor = 'crosshair';
|
|
945
|
+
}
|
|
946
|
+
this.alignmentProgress$.next({ pairs: 0, awaiting: 'tag' });
|
|
947
|
+
}
|
|
948
|
+
cancelTagAlignment() {
|
|
949
|
+
const wasAligning = this.aligning;
|
|
950
|
+
this.aligning = false;
|
|
951
|
+
this.alignmentPairs = [];
|
|
952
|
+
this.alignmentSource = null;
|
|
953
|
+
this.pendingPreviewEvent = null;
|
|
954
|
+
this.removePlacementPreview();
|
|
955
|
+
if (this.renderer) {
|
|
956
|
+
this.renderer.domElement.style.cursor = 'default';
|
|
957
|
+
}
|
|
958
|
+
this.alignmentProgress$.next({ pairs: 0, awaiting: null });
|
|
959
|
+
if (wasAligning) {
|
|
960
|
+
this.alignmentCancelled$.next();
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
isAligningTags() {
|
|
964
|
+
return this.aligning;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Whether the pointer should be showing the placement ghost.
|
|
968
|
+
*
|
|
969
|
+
* True while placing a tag, and while calibrating once a tag has been picked - that second click
|
|
970
|
+
* is the same question ("where on the model?") and deserves the same aiming aid. Not true for the
|
|
971
|
+
* first half of a pair, which is a click on a tag, not on the model.
|
|
972
|
+
*/
|
|
973
|
+
isPreviewingPlacement() {
|
|
974
|
+
return this.placingTag || (this.aligning && !!this.alignmentSource);
|
|
975
|
+
}
|
|
976
|
+
/** Whether a tag is currently registered for this element. */
|
|
977
|
+
hasTag(elementId) {
|
|
978
|
+
return this.tagSprites.has(elementId);
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Solves and applies the alignment collected so far, returning it so the caller can persist it.
|
|
982
|
+
*
|
|
983
|
+
* Returns null when the three tags turn out to be collinear, in which case nothing is applied and
|
|
984
|
+
* the walkthrough stays open for the person to pick a better third point.
|
|
985
|
+
*/
|
|
986
|
+
solveAlignmentFromPairs() {
|
|
987
|
+
const alignment = solveThreeJsAlignment(this.alignmentPairs);
|
|
988
|
+
if (!alignment) {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
this.setTagAlignment(alignment);
|
|
992
|
+
this.aligning = false;
|
|
993
|
+
this.pendingPreviewEvent = null;
|
|
994
|
+
this.removePlacementPreview();
|
|
995
|
+
this.renderer.domElement.style.cursor = 'default';
|
|
996
|
+
this.alignmentProgress$.next({ pairs: this.alignmentPairs.length, awaiting: null });
|
|
997
|
+
return alignment;
|
|
998
|
+
}
|
|
999
|
+
/** Handles a click while the alignment walkthrough is running. */
|
|
1000
|
+
handleAlignmentClick(event) {
|
|
1001
|
+
if (!this.alignmentSource) {
|
|
1002
|
+
// First half of a pair: which tag are we talking about?
|
|
1003
|
+
const hit = this.raycaster.intersectObjects(this.tagGroup.children, false)[0];
|
|
1004
|
+
if (!hit) {
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
// The sprite's own position is the stored coordinate, untouched by any alignment already
|
|
1008
|
+
// applied - that transform lives on the group, not on the sprite.
|
|
1009
|
+
const stored = hit.object.position;
|
|
1010
|
+
this.alignmentSource = { x: stored.x, y: stored.y, z: stored.z };
|
|
1011
|
+
this.alignmentProgress$.next({
|
|
1012
|
+
pairs: this.alignmentPairs.length,
|
|
1013
|
+
awaiting: 'point',
|
|
1014
|
+
tagId: hit.object.name,
|
|
1015
|
+
});
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
// Second half: where that tag actually belongs on the model.
|
|
1019
|
+
const target = this.pickAlignmentTarget(event);
|
|
1020
|
+
if (!target) {
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
this.alignmentPairs.push({ source: this.alignmentSource, target });
|
|
1024
|
+
this.alignmentSource = null;
|
|
1025
|
+
// The ghost belongs to the click that just happened; the next half is a click on a tag.
|
|
1026
|
+
this.pendingPreviewEvent = null;
|
|
1027
|
+
this.removePlacementPreview();
|
|
1028
|
+
this.alignmentProgress$.next({
|
|
1029
|
+
pairs: this.alignmentPairs.length,
|
|
1030
|
+
awaiting: this.alignmentPairs.length >= 3 ? null : 'tag',
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
/** Arms tag placement: the next click on the model drops a tag instead of being ignored. */
|
|
1034
|
+
startTagPlacement(poiType = null) {
|
|
1035
|
+
this.placingTag = true;
|
|
1036
|
+
this.placementPoiType = poiType;
|
|
1037
|
+
this.pendingTagPlacement = null;
|
|
1038
|
+
if (this.renderer) {
|
|
1039
|
+
this.renderer.domElement.style.cursor = 'crosshair';
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
/** Leaves placement mode without placing anything. */
|
|
1043
|
+
cancelTagPlacement() {
|
|
1044
|
+
this.placingTag = false;
|
|
1045
|
+
this.pendingTagPlacement = null;
|
|
1046
|
+
this.pendingPreviewEvent = null;
|
|
1047
|
+
this.removePlacementPreview();
|
|
1048
|
+
if (this.renderer) {
|
|
1049
|
+
this.renderer.domElement.style.cursor = 'default';
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Shows a translucent tag under the cursor while placing, so the spot is visible before commiting.
|
|
1054
|
+
*
|
|
1055
|
+
* Matterport does this with a tag that follows the pointer; the equivalent here is a preview
|
|
1056
|
+
* sprite that gets removed the moment the real tag takes its place.
|
|
1057
|
+
*/
|
|
1058
|
+
updatePlacementPreview(event) {
|
|
1059
|
+
const position = this.pickPointOnGpu(event);
|
|
1060
|
+
if (!position) {
|
|
1061
|
+
this.removePlacementPreview();
|
|
1062
|
+
this.renderer.domElement.style.cursor = 'not-allowed';
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
this.renderer.domElement.style.cursor = 'crosshair';
|
|
1066
|
+
if (!this.placementPreview) {
|
|
1067
|
+
this.placementPreview = new THREE.Sprite(new THREE.SpriteMaterial({
|
|
1068
|
+
map: this.createTagTexture('#9E9E9E'),
|
|
1069
|
+
depthTest: false,
|
|
1070
|
+
transparent: true,
|
|
1071
|
+
opacity: 0.6,
|
|
1072
|
+
}));
|
|
1073
|
+
this.placementPreview.renderOrder = 999;
|
|
1074
|
+
this.tagGroup.add(this.placementPreview);
|
|
1075
|
+
this.scaleTagForModel(this.placementPreview);
|
|
1076
|
+
}
|
|
1077
|
+
this.placementPreview.position.copy(position);
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* The point under the cursor, read from the depth buffer instead of raycast, in tag space.
|
|
1081
|
+
*
|
|
1082
|
+
* A raycast walks every triangle of the mesh it hits: on a captured model that is millions of
|
|
1083
|
+
* them, tens of milliseconds, every time the pointer moves. Coalescing to one per frame did not
|
|
1084
|
+
* help, because the cost is the pick itself - it simply became one slow frame instead of several.
|
|
1085
|
+
*
|
|
1086
|
+
* Rendering the model's depth into a single pixel under the cursor answers the same question on
|
|
1087
|
+
* the GPU in constant time, whatever the model's size. The camera's view offset is what narrows
|
|
1088
|
+
* the whole image down to that one pixel, so the value read back is the depth the cursor is over.
|
|
1089
|
+
*
|
|
1090
|
+
* Used for the ghost only. The click that actually places a tag still raycasts: it is one pick,
|
|
1091
|
+
* its cost does not matter, and it yields the surface normal, which depth alone cannot give.
|
|
1092
|
+
*/
|
|
1093
|
+
pickPointOnGpu(event) {
|
|
1094
|
+
if (!this.modelRoot || !this.renderer || !this.camera) {
|
|
1095
|
+
return null;
|
|
1096
|
+
}
|
|
1097
|
+
if (!this.pickTarget) {
|
|
1098
|
+
this.pickTarget = new THREE.WebGLRenderTarget(1, 1);
|
|
1099
|
+
this.pickCamera = new THREE.PerspectiveCamera();
|
|
1100
|
+
this.pickCamera.layers.set(ThreeJsService.PICK_LAYER);
|
|
1101
|
+
this.pickDepthMaterial = new THREE.MeshDepthMaterial({
|
|
1102
|
+
depthPacking: THREE.RGBADepthPacking,
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
const rect = this.renderer.domElement.getBoundingClientRect();
|
|
1106
|
+
const x = event.clientX - rect.left;
|
|
1107
|
+
const y = event.clientY - rect.top;
|
|
1108
|
+
if (x < 0 || y < 0 || x > rect.width || y > rect.height) {
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
this.pickCamera.copy(this.camera);
|
|
1112
|
+
this.pickCamera.layers.set(ThreeJsService.PICK_LAYER);
|
|
1113
|
+
// Renders the one pixel the cursor is over as if it were the whole image - offset by half a
|
|
1114
|
+
// pixel so the cursor sits at that pixel's centre, which is where NDC (0, 0) lands.
|
|
1115
|
+
this.pickCamera.setViewOffset(rect.width, rect.height, x - 0.5, y - 0.5, 1, 1);
|
|
1116
|
+
// Where the model is not, the pixel must read as "nothing", so it is cleared to white - a value
|
|
1117
|
+
// no real depth packs to. Two things would otherwise paint over that: the default black clear,
|
|
1118
|
+
// which decodes as depth 0 (a hit against the lens), and the scene background, which three.js
|
|
1119
|
+
// draws in place of the clear colour whenever one is set.
|
|
1120
|
+
const previousClearColor = this.renderer.getClearColor(new THREE.Color());
|
|
1121
|
+
const previousClearAlpha = this.renderer.getClearAlpha();
|
|
1122
|
+
const previousTarget = this.renderer.getRenderTarget();
|
|
1123
|
+
const previousBackground = this.scene.background;
|
|
1124
|
+
this.scene.background = null;
|
|
1125
|
+
this.scene.overrideMaterial = this.pickDepthMaterial;
|
|
1126
|
+
this.renderer.setClearColor(0xffffff, 1);
|
|
1127
|
+
this.renderer.setRenderTarget(this.pickTarget);
|
|
1128
|
+
this.renderer.clear();
|
|
1129
|
+
this.renderer.render(this.scene, this.pickCamera);
|
|
1130
|
+
this.renderer.readRenderTargetPixels(this.pickTarget, 0, 0, 1, 1, this.pickPixel);
|
|
1131
|
+
this.renderer.setRenderTarget(previousTarget);
|
|
1132
|
+
this.renderer.setClearColor(previousClearColor, previousClearAlpha);
|
|
1133
|
+
this.scene.overrideMaterial = null;
|
|
1134
|
+
this.scene.background = previousBackground;
|
|
1135
|
+
const [r, g, b, a] = this.pickPixel;
|
|
1136
|
+
if (r === 255 && g === 255 && b === 255 && a === 255) {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
// The inverse of three's packDepthToRGBA, which stores the value itself in alpha and ever finer
|
|
1140
|
+
// fractions of it in blue, green and red - alpha is the most significant byte, not red. Read the
|
|
1141
|
+
// other way round, the result is mostly the low-order noise, often near zero, which unprojects
|
|
1142
|
+
// onto the near plane: a ghost pressed against the lens, filling the whole screen.
|
|
1143
|
+
const downscale = 255 / 256;
|
|
1144
|
+
const depth = (r / 255) * downscale / (256 * 256 * 256) +
|
|
1145
|
+
(g / 255) * downscale / (256 * 256) +
|
|
1146
|
+
(b / 255) * downscale / 256 +
|
|
1147
|
+
(a / 255) * downscale;
|
|
1148
|
+
// NDC x and y are zero because the pixel read is the centre of that one-pixel image - which only
|
|
1149
|
+
// holds while the view offset is in effect. Clearing it first would unproject through the full
|
|
1150
|
+
// view instead, landing the point on the ray through the middle of the screen. The offset is
|
|
1151
|
+
// simply replaced by the next pick, so it is never cleared.
|
|
1152
|
+
const world = new THREE.Vector3(0, 0, depth * 2 - 1).unproject(this.pickCamera);
|
|
1153
|
+
this.tagGroup.updateWorldMatrix(true, false);
|
|
1154
|
+
return this.tagGroup.worldToLocal(world);
|
|
1155
|
+
}
|
|
1156
|
+
removePlacementPreview() {
|
|
1157
|
+
if (!this.placementPreview) {
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
this.tagGroup.remove(this.placementPreview);
|
|
1161
|
+
this.placementPreview.material.map?.dispose();
|
|
1162
|
+
this.placementPreview.material.dispose();
|
|
1163
|
+
this.placementPreview = undefined;
|
|
1164
|
+
}
|
|
1165
|
+
/** Shows or hides one tag, leaving it registered so it can be brought back without reloading. */
|
|
1166
|
+
setTagVisibility(elementId, visible) {
|
|
1167
|
+
const sprite = this.tagSprites.get(elementId);
|
|
1168
|
+
if (sprite) {
|
|
1169
|
+
sprite.visible = visible;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Shows only tags of the given types, hiding the rest.
|
|
1174
|
+
*
|
|
1175
|
+
* Passing null shows everything - the "no filter" state, distinct from an empty array, which is a
|
|
1176
|
+
* filter that happens to match nothing.
|
|
1177
|
+
*/
|
|
1178
|
+
setVisibleTagTypes(types) {
|
|
1179
|
+
this.tagSprites.forEach((sprite) => {
|
|
1180
|
+
sprite.visible = types === null || types.includes(sprite.userData.poiType);
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Moves the camera to look at a tag, mirroring Matterport's goToTag.
|
|
1185
|
+
*
|
|
1186
|
+
* Animated rather than teleported: jumping the camera loses the viewer's sense of where they were
|
|
1187
|
+
* relative to the model. The approach distance follows the model's size so it frames sensibly
|
|
1188
|
+
* whatever the units are.
|
|
1189
|
+
*/
|
|
1190
|
+
goToTag(elementId) {
|
|
1191
|
+
const sprite = this.tagSprites.get(elementId);
|
|
1192
|
+
if (!sprite || !this.camera) {
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
const target = sprite.getWorldPosition(new THREE.Vector3());
|
|
1196
|
+
const distance = this.modelRoot
|
|
1197
|
+
? new THREE.Box3().setFromObject(this.modelRoot).getSize(new THREE.Vector3()).length() * 0.15
|
|
1198
|
+
: 5;
|
|
1199
|
+
// Approach along the current viewing direction, so the camera closes in rather than swinging
|
|
1200
|
+
// around to an arbitrary side.
|
|
1201
|
+
const direction = this.camera.position.clone().sub(this.controls.target).normalize();
|
|
1202
|
+
this.cameraTween = {
|
|
1203
|
+
fromPosition: this.camera.position.clone(),
|
|
1204
|
+
toPosition: target.clone().add(direction.multiplyScalar(distance)),
|
|
1205
|
+
fromTarget: this.controls.target.clone(),
|
|
1206
|
+
toTarget: target,
|
|
1207
|
+
startedAt: performance.now(),
|
|
1208
|
+
durationMs: 600,
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
/** Advances the camera animation started by goToTag, if one is running. */
|
|
1212
|
+
stepCameraTween() {
|
|
1213
|
+
if (!this.cameraTween) {
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
const { fromPosition, toPosition, fromTarget, toTarget, startedAt, durationMs } = this.cameraTween;
|
|
1217
|
+
const linear = Math.min((performance.now() - startedAt) / durationMs, 1);
|
|
1218
|
+
// Ease out, so the move settles instead of stopping dead.
|
|
1219
|
+
const t = 1 - Math.pow(1 - linear, 3);
|
|
1220
|
+
this.camera.position.lerpVectors(fromPosition, toPosition, t);
|
|
1221
|
+
this.controls.target.lerpVectors(fromTarget, toTarget, t);
|
|
1222
|
+
if (linear >= 1) {
|
|
1223
|
+
this.cameraTween = null;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
isPlacingTag() {
|
|
1227
|
+
return this.placingTag;
|
|
1228
|
+
}
|
|
1229
|
+
/** The spot picked by the last placement, for whoever commits it to a POI. */
|
|
1230
|
+
getPendingTagPlacement() {
|
|
1231
|
+
return this.pendingTagPlacement;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Raycasts a click against the model's geometry and returns the hit, in world space.
|
|
1235
|
+
*
|
|
1236
|
+
* Returns null when the click missed, which is a normal outcome - the user clicked the sky - and
|
|
1237
|
+
* leaves whatever mode is armed still armed, so they can simply try again.
|
|
1238
|
+
*/
|
|
1239
|
+
raycastModel(event) {
|
|
1240
|
+
if (!this.modelRoot) {
|
|
1241
|
+
return null;
|
|
1242
|
+
}
|
|
1243
|
+
const rect = this.renderer.domElement.getBoundingClientRect();
|
|
1244
|
+
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
1245
|
+
this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
1246
|
+
this.raycaster.setFromCamera(this.mouse, this.camera);
|
|
1247
|
+
return this.raycaster.intersectObject(this.modelRoot, true)[0] ?? null;
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Turns a click into a point on the model, in the frame tag coordinates are persisted in.
|
|
1251
|
+
*
|
|
1252
|
+
* That frame is the tag group's, not the model's. A stored coordinate is read back as a sprite
|
|
1253
|
+
* position inside that group, so a coordinate captured in any other frame comes back displaced by
|
|
1254
|
+
* exactly however far the alignment moves the group. The two frames agree only while the
|
|
1255
|
+
* alignment is identity, which is why this stayed invisible until a model was calibrated.
|
|
1256
|
+
*/
|
|
1257
|
+
pickPlacementOnModel(event) {
|
|
1258
|
+
const hit = this.raycastModel(event);
|
|
1259
|
+
if (!hit) {
|
|
1260
|
+
return null;
|
|
1261
|
+
}
|
|
1262
|
+
this.tagGroup.updateWorldMatrix(true, false);
|
|
1263
|
+
const position = this.tagGroup.worldToLocal(hit.point.clone());
|
|
1264
|
+
// A face normal is in the geometry's local space already, but the mesh it belongs to may sit
|
|
1265
|
+
// under transforms of its own, so take it out through those and back into the tag frame, so it
|
|
1266
|
+
// describes the same orientation the position does.
|
|
1267
|
+
let normal = new THREE.Vector3(0, 1, 0);
|
|
1268
|
+
if (hit.face) {
|
|
1269
|
+
normal = hit.face.normal
|
|
1270
|
+
.clone()
|
|
1271
|
+
.transformDirection(hit.object.matrixWorld)
|
|
1272
|
+
.transformDirection(new THREE.Matrix4().copy(this.tagGroup.matrixWorld).invert())
|
|
1273
|
+
.normalize();
|
|
1274
|
+
}
|
|
1275
|
+
return {
|
|
1276
|
+
position: { x: position.x, y: position.y, z: position.z },
|
|
1277
|
+
normal: { x: normal.x, y: normal.y, z: normal.z },
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Where the user says a tag really belongs, in the space the solved alignment maps onto.
|
|
1282
|
+
*
|
|
1283
|
+
* setTagAlignment writes the solution to the tag group's own transform, which is expressed
|
|
1284
|
+
* relative to its parent - so the target has to be in the parent's frame. Measuring it in the
|
|
1285
|
+
* group's own frame instead would fold the current alignment into the answer, making each
|
|
1286
|
+
* calibration depend on the one before it.
|
|
1287
|
+
*/
|
|
1288
|
+
pickAlignmentTarget(event) {
|
|
1289
|
+
const hit = this.raycastModel(event);
|
|
1290
|
+
if (!hit) {
|
|
1291
|
+
return null;
|
|
1292
|
+
}
|
|
1293
|
+
const frame = this.tagGroup.parent ?? this.scene;
|
|
1294
|
+
frame.updateWorldMatrix(true, false);
|
|
1295
|
+
const point = frame.worldToLocal(hit.point.clone());
|
|
1296
|
+
return { x: point.x, y: point.y, z: point.z };
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Sets the transform mapping captured tag coordinates onto this model's space.
|
|
1300
|
+
*
|
|
1301
|
+
* Every tag comes from the same source space, so this is one transform on the group rather than
|
|
1302
|
+
* per-tag maths - which also means it applies retroactively to tags already placed, and can be
|
|
1303
|
+
* adjusted live while someone dials the numbers in.
|
|
1304
|
+
*
|
|
1305
|
+
* Passing null resets to identity, i.e. tag coordinates are already in model space.
|
|
1306
|
+
*/
|
|
1307
|
+
setTagAlignment(alignment) {
|
|
1308
|
+
const { translation, rotation, scale } = alignment ?? {};
|
|
1309
|
+
this.tagGroup.position.set(translation?.x ?? 0, translation?.y ?? 0, translation?.z ?? 0);
|
|
1310
|
+
this.tagGroup.rotation.set(rotation?.x ?? 0, rotation?.y ?? 0, rotation?.z ?? 0);
|
|
1311
|
+
this.tagGroup.scale.setScalar(scale ?? 1);
|
|
1312
|
+
// Tag size is derived from the world scale it inherits, which this call just changed.
|
|
1313
|
+
this.tagSprites.forEach((sprite) => this.scaleTagForModel(sprite));
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Adds a tag, or moves/restyles the one already registered for this element.
|
|
1317
|
+
*
|
|
1318
|
+
* Upsert rather than a declarative "here is the full list" call, because tags arrive per type
|
|
1319
|
+
* (equipment, then tickets, then measurements...) from separate subscriptions - replacing the set
|
|
1320
|
+
* on each of those would wipe the types that came before.
|
|
1321
|
+
*/
|
|
1322
|
+
upsertTag(tag) {
|
|
1323
|
+
const existing = this.tagSprites.get(tag.id);
|
|
1324
|
+
if (existing) {
|
|
1325
|
+
existing.position.set(tag.position.x, tag.position.y, tag.position.z);
|
|
1326
|
+
existing.userData.poiType = tag.poiType ?? existing.userData.poiType;
|
|
1327
|
+
existing.userData.label = tag.label ?? existing.userData.label;
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
const sprite = new THREE.Sprite(new THREE.SpriteMaterial({
|
|
1331
|
+
map: this.createTagTexture(tag.color),
|
|
1332
|
+
// Tags read as UI rather than geometry, so they stay visible through the model instead of
|
|
1333
|
+
// being occluded by whatever wall happens to be between them and the camera.
|
|
1334
|
+
depthTest: false,
|
|
1335
|
+
transparent: true,
|
|
1336
|
+
}));
|
|
1337
|
+
sprite.name = tag.id;
|
|
1338
|
+
sprite.userData.poiType = tag.poiType ?? null;
|
|
1339
|
+
sprite.userData.label = tag.label ?? null;
|
|
1340
|
+
sprite.position.set(tag.position.x, tag.position.y, tag.position.z);
|
|
1341
|
+
sprite.renderOrder = 999;
|
|
1342
|
+
this.scaleTagForModel(sprite);
|
|
1343
|
+
this.tagGroup.add(sprite);
|
|
1344
|
+
this.tagSprites.set(tag.id, sprite);
|
|
1345
|
+
if (tag.iconUrl) {
|
|
1346
|
+
this.applyTagIcon(sprite, tag.iconUrl);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
/** Removes the tag registered for an element, if there is one. */
|
|
1350
|
+
removeTag(elementId) {
|
|
1351
|
+
const sprite = this.tagSprites.get(elementId);
|
|
1352
|
+
if (!sprite) {
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
this.tagGroup.remove(sprite);
|
|
1356
|
+
sprite.material.map?.dispose();
|
|
1357
|
+
sprite.material.dispose();
|
|
1358
|
+
this.tagSprites.delete(elementId);
|
|
1359
|
+
}
|
|
1360
|
+
/** Removes every tag from the scene. */
|
|
1361
|
+
clearTags() {
|
|
1362
|
+
[...this.tagSprites.keys()].forEach((id) => this.removeTag(id));
|
|
1363
|
+
}
|
|
1364
|
+
/** Loads an icon into an existing tag, leaving the fallback disc in place if it fails. */
|
|
1365
|
+
applyTagIcon(sprite, iconUrl) {
|
|
1366
|
+
new THREE.TextureLoader().load(iconUrl, (texture) => {
|
|
1367
|
+
sprite.material.map?.dispose();
|
|
1368
|
+
sprite.material.map = texture;
|
|
1369
|
+
sprite.material.needsUpdate = true;
|
|
1370
|
+
}, undefined, () => console.warn(`ThreeJsService: could not load tag icon "${iconUrl}", keeping the default marker.`));
|
|
1371
|
+
}
|
|
1372
|
+
/** Draws the default tag marker: a filled disc with a white ring, so it reads on any background. */
|
|
1373
|
+
createTagTexture(color = '#1E88E5') {
|
|
1374
|
+
const size = 128;
|
|
1375
|
+
const canvas = document.createElement('canvas');
|
|
1376
|
+
canvas.width = size;
|
|
1377
|
+
canvas.height = size;
|
|
1378
|
+
const context = canvas.getContext('2d');
|
|
1379
|
+
context.beginPath();
|
|
1380
|
+
context.arc(size / 2, size / 2, size / 2 - 8, 0, Math.PI * 2);
|
|
1381
|
+
context.fillStyle = color;
|
|
1382
|
+
context.fill();
|
|
1383
|
+
context.lineWidth = 8;
|
|
1384
|
+
context.strokeStyle = '#FFFFFF';
|
|
1385
|
+
context.stroke();
|
|
1386
|
+
return new THREE.CanvasTexture(canvas);
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Sizes a tag relative to the model.
|
|
1390
|
+
*
|
|
1391
|
+
* A sprite inherits the model's scale and the alignment's, so a fixed size would come out tiny on
|
|
1392
|
+
* a large model and huge on a small one. Dividing by the group's world scale keeps tags the same
|
|
1393
|
+
* size on screen whatever the model's units are.
|
|
1394
|
+
*/
|
|
1395
|
+
scaleTagForModel(sprite) {
|
|
1396
|
+
const worldScale = this.tagGroup.getWorldScale(new THREE.Vector3()).x || 1;
|
|
1397
|
+
const size = 0.4 / worldScale;
|
|
1398
|
+
sprite.scale.set(size, size, 1);
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Adds secondary interactive objects (e.g., POIs, sensors) to the scene.
|
|
1402
|
+
*/
|
|
1403
|
+
updateObjsToScene(objsAdded) {
|
|
1404
|
+
const existingIds = this.clickableObjects.map((obj) => obj.name);
|
|
1405
|
+
const objLoader = new OBJLoader();
|
|
1406
|
+
// delete objects that are not in the provided array
|
|
1407
|
+
this.clickableObjects.forEach((obj) => {
|
|
1408
|
+
if (!objsAdded.find((objData) => objData.id === obj.name)) {
|
|
1409
|
+
this.scene.remove(obj);
|
|
1410
|
+
this.clickableObjects = this.clickableObjects.filter((object) => object.name !== obj.name);
|
|
1411
|
+
}
|
|
1412
|
+
});
|
|
1413
|
+
objsAdded.forEach((objectData) => {
|
|
1414
|
+
if (!existingIds.includes(objectData.id)) {
|
|
1415
|
+
objLoader.load(objectData.file, (newObject) => {
|
|
1416
|
+
newObject.name = objectData.id;
|
|
1417
|
+
newObject.traverse((child) => {
|
|
1418
|
+
if (child.isMesh) {
|
|
1419
|
+
const mesh = child;
|
|
1420
|
+
if (mesh.material instanceof THREE.Material) {
|
|
1421
|
+
mesh.material.transparent = true;
|
|
1422
|
+
mesh.material.opacity = 0.8;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
// set object position and rotation
|
|
1427
|
+
newObject.position.set(objectData.position.x, objectData.position.y, objectData.position.z);
|
|
1428
|
+
// The JSON configuration has rotation already in radians (e.g. 3.1415...)
|
|
1429
|
+
newObject.rotation.set(objectData.rotation.x, objectData.rotation.y, objectData.rotation.z);
|
|
1430
|
+
newObject.scale.set(objectData.scale.x, objectData.scale.y, objectData.scale.z);
|
|
1431
|
+
this.scene.add(newObject);
|
|
1432
|
+
this.clickableObjects.push(newObject);
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
onMouseMove(event) {
|
|
1438
|
+
if (!this.isDraggingCamera) {
|
|
1439
|
+
const rect = this.renderer.domElement.getBoundingClientRect();
|
|
1440
|
+
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
1441
|
+
this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
1442
|
+
this.raycaster.setFromCamera(this.mouse, this.camera);
|
|
1443
|
+
// While the ghost is up it follows the pointer and nothing else should react to it. The move
|
|
1444
|
+
// is only recorded here: pointer events fire far more often than frames, and picking a point
|
|
1445
|
+
// costs a raycast against the whole model, so doing it per event is what made the ghost trail
|
|
1446
|
+
// behind the cursor. The render loop picks up the latest position, once, per frame.
|
|
1447
|
+
if (this.isPreviewingPlacement()) {
|
|
1448
|
+
this.pendingPreviewEvent = event;
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
// Not previewing any more (a pair was just completed, say): drop a ghost left over from it.
|
|
1452
|
+
this.removePlacementPreview();
|
|
1453
|
+
// Tags first: they draw on top of the model, so they have to answer the pointer first too.
|
|
1454
|
+
const hoveredTag = this.raycaster
|
|
1455
|
+
.intersectObjects(this.tagGroup.children, false)
|
|
1456
|
+
.find((hit) => hit.object.visible)?.object;
|
|
1457
|
+
if (hoveredTag) {
|
|
1458
|
+
this.renderer.domElement.style.cursor = 'pointer';
|
|
1459
|
+
this.objectHovered$.next(null);
|
|
1460
|
+
const label = hoveredTag.userData.label;
|
|
1461
|
+
this.tagHovered$.next(label ? { label, event } : null);
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
this.tagHovered$.next(null);
|
|
1465
|
+
const intersects = this.raycaster.intersectObjects(this.clickableObjects, true);
|
|
1466
|
+
if (intersects.length > 0) {
|
|
1467
|
+
this.renderer.domElement.style.cursor = 'pointer';
|
|
1468
|
+
const intersectedObjectId = intersects[0].object?.parent?.name;
|
|
1469
|
+
if (intersectedObjectId) {
|
|
1470
|
+
this.objectHovered$.next({ id: intersectedObjectId, event });
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
else {
|
|
1474
|
+
this.renderer.domElement.style.cursor = 'default';
|
|
1475
|
+
this.objectHovered$.next(null);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
this.renderer.domElement.style.cursor = 'grabbing';
|
|
1480
|
+
this.objectHovered$.next(null);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
onMouseClick(event) {
|
|
1484
|
+
if (!this.isDraggingCamera) {
|
|
1485
|
+
const rect = this.renderer.domElement.getBoundingClientRect();
|
|
1486
|
+
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
1487
|
+
this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
1488
|
+
// Alignment and placement both take the click before anything can be opened by it.
|
|
1489
|
+
if (this.aligning) {
|
|
1490
|
+
this.raycaster.setFromCamera(this.mouse, this.camera);
|
|
1491
|
+
this.handleAlignmentClick(event);
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
// Placement wins over everything: while armed, a click means "put it here", not "open that".
|
|
1495
|
+
if (this.placingTag) {
|
|
1496
|
+
const placement = this.pickPlacementOnModel(event);
|
|
1497
|
+
if (placement) {
|
|
1498
|
+
placement.poiType = this.placementPoiType;
|
|
1499
|
+
this.pendingTagPlacement = placement;
|
|
1500
|
+
this.placingTag = false;
|
|
1501
|
+
this.removePlacementPreview();
|
|
1502
|
+
this.renderer.domElement.style.cursor = 'default';
|
|
1503
|
+
this.tagPlaced$.next(placement);
|
|
1504
|
+
}
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
this.raycaster.setFromCamera(this.mouse, this.camera);
|
|
1508
|
+
// Same precedence as hover: a tag drawn over the model claims the click.
|
|
1509
|
+
const tagHits = this.raycaster.intersectObjects(this.tagGroup.children, false)
|
|
1510
|
+
.filter((hit) => hit.object.visible);
|
|
1511
|
+
if (tagHits.length > 0) {
|
|
1512
|
+
const tag = tagHits[0].object;
|
|
1513
|
+
this.tagClicked$.next({ elementId: tag.name, poiType: tag.userData.poiType ?? null });
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
const intersects = this.raycaster.intersectObjects(this.clickableObjects, true);
|
|
1517
|
+
if (intersects.length > 0) {
|
|
1518
|
+
const clickedObject = intersects[0].object;
|
|
1519
|
+
const id = clickedObject?.parent?.name;
|
|
1520
|
+
if (id) {
|
|
1521
|
+
this.objectClicked$.next(id);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
else {
|
|
1525
|
+
this.objectClicked$.next(null);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Escape cancels whatever the pointer is currently committed to.
|
|
1531
|
+
*
|
|
1532
|
+
* Emits through the same subjects the explicit Cancel buttons use, so the UI driving them follows
|
|
1533
|
+
* along instead of being left showing a mode that is no longer armed.
|
|
1534
|
+
*/
|
|
1535
|
+
onKeyDown = (event) => {
|
|
1536
|
+
if (event.key !== 'Escape') {
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
if (this.placingTag) {
|
|
1540
|
+
// Nothing outside the scene tracks placement, so clearing it here is the whole of it.
|
|
1541
|
+
this.cancelTagPlacement();
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
if (this.aligning) {
|
|
1545
|
+
this.cancelTagAlignment();
|
|
1546
|
+
}
|
|
1547
|
+
};
|
|
1548
|
+
animate = () => {
|
|
1549
|
+
this.animationFrameId = requestAnimationFrame(this.animate);
|
|
1550
|
+
this.stepCameraTween();
|
|
1551
|
+
this.controls.update();
|
|
1552
|
+
// One pick per frame, against the pointer's latest position - see onMouseMove.
|
|
1553
|
+
if (this.pendingPreviewEvent) {
|
|
1554
|
+
const event = this.pendingPreviewEvent;
|
|
1555
|
+
this.pendingPreviewEvent = null;
|
|
1556
|
+
this.updatePlacementPreview(event);
|
|
1557
|
+
}
|
|
1558
|
+
this.renderer.render(this.scene, this.camera);
|
|
1559
|
+
};
|
|
1560
|
+
destroy() {
|
|
1561
|
+
if (this.animationFrameId) {
|
|
1562
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
1563
|
+
}
|
|
1564
|
+
window.removeEventListener('keydown', this.onKeyDown);
|
|
1565
|
+
if (this.canvasResizeObserver) {
|
|
1566
|
+
this.canvasResizeObserver.disconnect();
|
|
1567
|
+
this.canvasResizeObserver = undefined;
|
|
1568
|
+
}
|
|
1569
|
+
this.discardCurrentLoad();
|
|
1570
|
+
this.clearTags();
|
|
1571
|
+
if (this.renderer) {
|
|
1572
|
+
this.renderer.dispose();
|
|
1573
|
+
}
|
|
1574
|
+
if (this.controls) {
|
|
1575
|
+
this.controls.dispose();
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ThreeJsService, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1579
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ThreeJsService, providedIn: 'root' });
|
|
1580
|
+
}
|
|
1581
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ThreeJsService, decorators: [{
|
|
1582
|
+
type: Injectable,
|
|
1583
|
+
args: [{
|
|
1584
|
+
providedIn: 'root'
|
|
1585
|
+
}]
|
|
1586
|
+
}], ctorParameters: () => [{ type: i0.NgZone }] });
|
|
1587
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhyZWVqcy5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LXNtYXJ0ZXJwbGFuLWNvcmUvc3JjL2xpYi9zZXJ2aWNlcy90aHJlZWpzLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBVSxNQUFNLGVBQWUsQ0FBQztBQUNuRCxPQUFPLEtBQUssS0FBSyxNQUFNLE9BQU8sQ0FBQztBQUMvQixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFDMUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNqRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFDbkUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQ25FLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtREFBbUQsQ0FBQztBQUNuRixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDOztBQW9EL0I7Ozs7OztHQU1HO0FBQ0gsU0FBUyxlQUFlLENBQUMsQ0FBZ0IsRUFBRSxDQUFnQixFQUFFLENBQWdCO0lBQzNFLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUIsSUFBSSxFQUFFLENBQUMsUUFBUSxFQUFFLEdBQUcsS0FBSyxFQUFFLENBQUM7UUFDMUIsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQ0QsRUFBRSxDQUFDLFNBQVMsRUFBRSxDQUFDO0lBRWYsTUFBTSxHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3QixNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMs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QUFDLFNBQVMsRUFBRSxDQUFDO1FBQ2pCLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ2xCLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDMUIsQ0FBQztRQUNELElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ2xCLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDMUIsQ0FBQztJQUNILENBQUM7d0dBeHBEVSxjQUFjOzRHQUFkLGNBQWMsY0FGYixNQUFNOzs0RkFFUCxjQUFjO2tCQUgxQixVQUFVO21CQUFDO29CQUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIE5nWm9uZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0ICogYXMgVEhSRUUgZnJvbSAndGhyZWUnO1xuaW1wb3J0IHsgT3JiaXRDb250cm9scyB9IGZyb20gJ3RocmVlL2V4YW1wbGVzL2pzbS9jb250cm9scy9PcmJpdENvbnRyb2xzJztcbmltcG9ydCB7IE9CSkxvYWRlciB9IGZyb20gJ3RocmVlL2V4YW1wbGVzL2pzbS9sb2FkZXJzL09CSkxvYWRlcic7XG5pbXBvcnQgeyBNVExMb2FkZXIgfSBmcm9tICd0aHJlZS9leGFtcGxlcy9qc20vbG9hZGVycy9NVExMb2FkZXInO1xuaW1wb3J0IHsgR0xURkxvYWRlciB9IGZyb20gJ3RocmVlL2V4YW1wbGVzL2pzbS9sb2FkZXJzL0dMVEZMb2FkZXInO1xuaW1wb3J0IHsgS1RYMkxvYWRlciB9IGZyb20gJ3RocmVlL2V4YW1wbGVzL2pzbS9sb2FkZXJzL0tUWDJMb2FkZXInO1xuaW1wb3J0IHsgTWVzaG9wdERlY29kZXIgfSBmcm9tICd0aHJlZS9leGFtcGxlcy9qc20vbGlicy9tZXNob3B0X2RlY29kZXIubW9kdWxlLmpzJztcbmltcG9ydCB7IFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IFRocmVlSnNBbGlnbm1lbnQgfSBmcm9tICcuLi9oZWxwZXJzLnNlcnZpY2UnO1xuaW1wb3J0IHsgUG9pVHlwZSB9IGZyb20gJy4uL3R5cGVzLnNlcnZpY2UnO1xuXG4vKipcbiAqIEEgdGFnIHRvIGRyYXcgaW4gdGhlIFRocmVlLmpzIHNjZW5lLCBtaXJyb3JpbmcgYSBNYXR0ZXJ0YWcuXG4gKlxuICogUG9zaXRpb25zIGFyZSBpbiB0aGUgbW9kZWwncyBvd24gY29vcmRpbmF0ZSBzcGFjZSAtIHRoZSBzYW1lIG51bWJlcnMgc3RvcmVkIGluIFBPSS5jb29yZGluYXRlIC1cbiAqIG5vdCBzY2VuZSBzcGFjZS4gVGhlIHNlcnZpY2UgcmVwYXJlbnRzIHRhZ3MgdW5kZXIgdGhlIG1vZGVsIHNvIHRoZXkgaW5oZXJpdCB0aGUgY2VudGVyaW5nIGFuZFxuICogc2NhbGluZyBhcHBsaWVkIGF0IGxvYWQsIHdoaWNoIG1lYW5zIGNhbGxlcnMgbmV2ZXIgZGVhbCB3aXRoIHRoYXQgdHJhbnNmb3JtLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFRocmVlSnNUYWcge1xuICAvKiogVGhlIHRhZ2dlZCBlbGVtZW50J3MgaWQgKFBPSS5lbGVtZW50SUQpLCB1c2VkIHRvIHVwZGF0ZSBvciByZW1vdmUgdGhlIHRhZyBsYXRlci4gKi9cbiAgaWQ6IHN0cmluZztcbiAgcG9zaXRpb246IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9O1xuICAvKiogRHJpdmVzIGJvdGggdGhlIGNsaWNrIHJvdXRlIGFuZCB0eXBlIGZpbHRlcmluZywgc28gaXQgdHJhdmVscyB3aXRoIHRoZSB0YWcuICovXG4gIHBvaVR5cGU/OiBQb2lUeXBlIHwgbnVsbDtcbiAgLyoqIFNob3duIG9uIGhvdmVyLiBBIG1hcmtlciB3aXRoIG5vIGlkZW50aXR5IGlzIGp1c3QgYSBkb3Qgb24gYSB3YWxsLiAqL1xuICBsYWJlbD86IHN0cmluZyB8IG51bGw7XG4gIC8qKiBJY29uIGRyYXduIG9uIHRoZSB0YWcuIEZhbGxzIGJhY2sgdG8gYSBwbGFpbiBkaXNjIHdoZW4gYWJzZW50IG9yIGl0IGZhaWxzIHRvIGxvYWQuICovXG4gIGljb25Vcmw/OiBzdHJpbmcgfCBudWxsO1xuICAvKiogVGludCBmb3IgdGhlIGZhbGxiYWNrIGRpc2MsIGFzIGEgQ1NTIGNvbG91ci4gKi9cbiAgY29sb3I/OiBzdHJpbmc7XG59XG5cbi8qKiBXaGF0IGEgY2xpY2sgb24gYSB0YWcgcmVwb3J0czogZW5vdWdoIHRvIHJvdXRlIHRvIHRoZSBlbGVtZW50J3MgZGV0YWlsIHBhZ2UuICovXG5leHBvcnQgaW50ZXJmYWNlIFRocmVlSnNUYWdDbGljayB7XG4gIGVsZW1lbnRJZDogc3RyaW5nO1xuICBwb2lUeXBlOiBQb2lUeXBlIHwgbnVsbDtcbn1cblxuLyoqXG4gKiBBIHNwb3QgcGlja2VkIG9uIHRoZSBtb2RlbCBmb3IgYSBuZXcgdGFnLlxuICpcbiAqIEJvdGggdmVjdG9ycyBhcmUgaW4gdGhlIG1vZGVsJ3Mgb3duIGNvb3JkaW5hdGUgc3BhY2UsIHdoaWNoIGlzIHdoYXQgUE9JLmNvb3JkaW5hdGUgc3RvcmVzLiBTY2VuZVxuICogY29vcmRpbmF0ZXMgZGVsaWJlcmF0ZWx5IG5ldmVyIGxlYXZlIHRoaXMgc2VydmljZTogdGhlIG1vZGVsIGlzIHNjYWxlZCB0byBhIGZpeGVkIHNpemUgb24gbG9hZCwgc29cbiAqIGEgc2NlbmUtc3BhY2UgcG9zaXRpb24gd291bGQgc2lsZW50bHkgbWVhbiBzb21ld2hlcmUgZWxzZSB0aGUgbW9tZW50IHRoZSAuZ2xiIGlzIHJlLWV4cG9ydGVkLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFRocmVlSnNUYWdQbGFjZW1lbnQge1xuICBwb3NpdGlvbjogeyB4OiBudW1iZXI7IHk6IG51bWJlcjsgejogbnVtYmVyIH07XG4gIC8qKiBTdXJmYWNlIG5vcm1hbCBhdCB0aGUgcGlja2VkIHBvaW50IC0gTWF0dGVycG9ydCBzdG9yZXMgdGhlIHNhbWUgdGhpbmcgaW4gUE9JLm1ldGFkYXRhLiAqL1xuICBub3JtYWw6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9O1xuICAvKiogV2hhdCB0aGUgcGxhY2VtZW50IHdhcyBhcm1lZCBmb3IsIHNvIHRoZSBjYWxsZXIga25vd3Mgd2hpY2ggY3JlYXRpb24gZm9ybSB0byBvcGVuLiAqL1xuICBwb2lUeXBlPzogUG9pVHlwZSB8IG51bGw7XG59XG5cbi8qKiBPbmUgdGFnIHNlZW4gaW4gYm90aCBzcGFjZXM6IHdoZXJlIGl0IGlzIHN0b3JlZCwgYW5kIHdoZXJlIGl0IHJlYWxseSBzaXRzIG9uIHRoZSBtb2RlbC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVGhyZWVKc0FsaWdubWVudFBhaXIge1xuICBzb3VyY2U6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9O1xuICB0YXJnZXQ6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9O1xufVxuXG4vKipcbiAqIEJ1aWxkcyBhbiBvcnRob25vcm1hbCBmcmFtZSBmcm9tIHRocmVlIHBvaW50cywgb3IgbnVsbCB3aGVuIHRoZXkgYXJlIChuZWFybHkpIGNvbGxpbmVhci5cbiAqXG4gKiBUaHJlZSBwb2ludHMgZGVmaW5lIGEgcGxhbmUsIGFuZCBhIHBsYW5lIHBsdXMgYW4gb3JpZ2luIGRlZmluZXMgYW4gb3JpZW50YXRpb24uIENvbGxpbmVhciBwb2ludHNcbiAqIGRvIG5vdDogdGhleSBsZWF2ZSB0aGUgcm90YXRpb24gYWJvdXQgdGhlaXIgY29tbW9uIGxpbmUgdW5kZXRlcm1pbmVkLCB3aGljaCBpcyB3aHkgdGhlIGNhbGxlciBoYXNcbiAqIHRvIHJlamVjdCB0aGVtIHJhdGhlciB0aGFuIHByb2R1Y2UgYSBjb25maWRlbnQgd3JvbmcgYW5zd2VyLlxuICovXG5mdW5jdGlvbiBmcmFtZUZyb21Qb2ludHMoYTogVEhSRUUuVmVjdG9yMywgYjogVEhSRUUuVmVjdG9yMywgYzogVEhSRUUuVmVjdG9yMyk6IFRIUkVFLk1hdHJpeDQgfCBudWxsIHtcbiAgY29uc3QgZTEgPSBiLmNsb25lKCkuc3ViKGEpO1xuICBpZiAoZTEubGVuZ3RoU3EoKSA8IDFlLTEyKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgZTEubm9ybWFsaXplKCk7XG5cbiAgY29uc3QgdG9DID0gYy5jbG9uZSgpLnN1YihhKTtcbiAgY29uc3QgZTIgPSB0b0MuY2xvbmUoKS5zdWIoZTEuY2xvbmUoKS5tdWx0aXBseVNjYWxhcih0b0MuZG90KGUxKSkpO1xuICBpZiAoZTIubGVuZ3RoU3EoKSA8IDFlLTEyKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgZTIubm9ybWFsaXplKCk7XG5cbiAgcmV0dXJuIG5ldyBUSFJFRS5NYXRyaXg0KCkubWFrZUJhc2lzKGUxLCBlMiwgZTEuY2xvbmUoKS5jcm9zcyhlMikpO1xufVxuXG4vKipcbiAqIFNvbHZlcyB0aGUgdHJhbnNmb3JtIHRoYXQgbWFwcyBzdG9yZWQgdGFnIGNvb3JkaW5hdGVzIG9udG8gdGhlIGxvYWRlZCBtb2RlbC5cbiAqXG4gKiBUYWtlcyBleGFjdGx5IHRoZSBjb3JyZXNwb25kZW5jZXMgYSBwZXJzb24gY2FuIHByb2R1Y2UgYnkgaGFuZDogdGhyZWUgdGFncywgZWFjaCBwb2ludGVkIGF0IHR3aWNlXG4gKiAtIG9uY2Ugd2hlcmUgaXQgY3VycmVudGx5IHNpdHMsIG9uY2Ugd2hlcmUgaXQgYmVsb25ncy4gVGhhdCBpcyB0aGUgbWluaW11bSB0aGF0IHBpbnMgZG93biBhbGxcbiAqIHRocmVlIHBhcnRzIG9mIHRoZSBhbnN3ZXIuIE9uZSBwYWlyIGZpeGVzIG9ubHkgdHJhbnNsYXRpb247IHR3byBsZWF2ZSB0aGUgcm90YXRpb24gYWJvdXQgdGhlIGxpbmVcbiAqIGJldHdlZW4gdGhlbSBmcmVlOyB0aHJlZSByZXNvbHZlIGl0LlxuICpcbiAqIFJldHVybnMgbnVsbCB3aGVuIHRoZSBwb2ludHMgYXJlIGNvbGxpbmVhciBvciBkZWdlbmVyYXRlLCBiZWNhdXNlIGEgd3JvbmcgYWxpZ25tZW50IGFwcGxpZWRcbiAqIHNpbGVudGx5IGlzIHdvcnNlIHRoYW4gbm9uZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNvbHZlVGhyZWVKc0FsaWdubWVudChwYWlyczogVGhyZWVKc0FsaWdubWVudFBhaXJbXSk6IFRocmVlSnNBbGlnbm1lbnQgfCBudWxsIHtcbiAgaWYgKCFwYWlycyB8fCBwYWlycy5sZW5ndGggPCAzKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBjb25zdCBzb3VyY2VzID0gcGFpcnMuc2xpY2UoMCwgMykubWFwKChwYWlyKSA9PiBuZXcgVEhSRUUuVmVjdG9yMyhwYWlyLnNvdXJjZS54LCBwYWlyLnNvdXJjZS55LCBwYWlyLnNvdXJjZS56KSk7XG4gIGNvbnN0IHRhcmdldHMgPSBwYWlycy5zbGljZSgwLCAzKS5tYXAoKHBhaXIpID0+IG5ldyBUSFJFRS5WZWN0b3IzKHBhaXIudGFyZ2V0LngsIHBhaXIudGFyZ2V0LnksIHBhaXIudGFyZ2V0LnopKTtcblxuICBjb25zdCBzb3VyY2VGcmFtZSA9IGZyYW1lRnJvbVBvaW50cyhzb3VyY2VzWzBdLCBzb3VyY2VzWzFdLCBzb3VyY2VzWzJdKTtcbiAgY29uc3QgdGFyZ2V0RnJhbWUgPSBmcmFtZUZyb21Qb2ludHModGFyZ2V0c1swXSwgdGFyZ2V0c1sxXSwgdGFyZ2V0c1syXSk7XG4gIGlmICghc291cmNlRnJhbWUgfHwgIXRhcmdldEZyYW1lKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvLyBBIHJvdGF0aW9uJ3MgaW52ZXJzZSBpcyBpdHMgdHJhbnNwb3NlLCBzbyB0aGlzIGlzIHRoZSByb3RhdGlvbiBjYXJyeWluZyBvbmUgZnJhbWUgb250byB0aGUgb3RoZXIuXG4gIGNvbnN0IHJvdGF0aW9uID0gdGFyZ2V0RnJhbWUubXVsdGlwbHkoc291cmNlRnJhbWUuY2xvbmUoKS50cmFuc3Bvc2UoKSk7XG5cbiAgLy8gU2NhbGUgZnJvbSBob3cgZmFyIGFwYXJ0IHRoZSBwb2ludHMgYXJlIGluIGVhY2ggc3BhY2UsIGF2ZXJhZ2VkIG92ZXIgdGhlIHRocmVlIGVkZ2VzIHNvIGEgc2luZ2xlXG4gIC8vIHNsb3BweSBjbGljayBkb2VzIG5vdCBzZXQgaXQgb24gaXRzIG93bi5cbiAgY29uc3QgZWRnZXMgPSBbWzAsIDFdLCBbMSwgMl0sIFswLCAyXV07XG4gIGxldCByYXRpb1N1bSA9IDA7XG4gIGxldCByYXRpb0NvdW50ID0gMDtcbiAgZm9yIChjb25zdCBbaSwgal0gb2YgZWRnZXMpIHtcbiAgICBjb25zdCBzb3VyY2VMZW5ndGggPSBzb3VyY2VzW2ldLmRpc3RhbmNlVG8oc291cmNlc1tqXSk7XG4gICAgY29uc3QgdGFyZ2V0TGVuZ3RoID0gdGFyZ2V0c1tpXS5kaXN0YW5jZVRvKHRhcmdldHNbal0pO1xuICAgIGlmIChzb3VyY2VMZW5ndGggPiAxZS05KSB7XG4gICAgICByYXRpb1N1bSArPSB0YXJnZXRMZW5ndGggLyBzb3VyY2VMZW5ndGg7XG4gICAgICByYXRpb0NvdW50ICs9IDE7XG4gICAgfVxuICB9XG4gIGlmIChyYXRpb0NvdW50ID09PSAwKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3Qgc2NhbGUgPSByYXRpb1N1bSAvIHJhdGlvQ291bnQ7XG5cbiAgLy8gVGhlIGdyb3VwIGFwcGxpZXMgcG9zaXRpb24sIHRoZW4gcm90YXRpb24sIHRoZW4gc2NhbGUsIHNvIGEgcG9pbnQgbGFuZHMgYXQgdCArIHMqUipwLiBTb2x2aW5nXG4gIC8vIHRoYXQgZm9yIHQgYXQgdGhlIGNlbnRyb2lkcyBnaXZlcyB0aGUgdHJhbnNsYXRpb24uXG4gIGNvbnN0IHNvdXJjZUNlbnRyb2lkID0gc291cmNlcy5yZWR1Y2UoKHN1bSwgdikgPT4gc3VtLmFkZCh2KSwgbmV3IFRIUkVFLlZlY3RvcjMoKSkubXVsdGlwbHlTY2FsYXIoMSAvIDMpO1xuICBjb25zdCB0YXJnZXRDZW50cm9pZCA9IHRhcmdldHMucmVkdWNlKChzdW0sIHYpID0+IHN1bS5hZGQodiksIG5ldyBUSFJFRS5WZWN0b3IzKCkpLm11bHRpcGx5U2NhbGFyKDEgLyAzKTtcbiAgY29uc3QgdHJhbnNsYXRpb24gPSB0YXJnZXRDZW50cm9pZFxuICAgIC5jbG9uZSgpXG4gICAgLnN1Yihzb3VyY2VDZW50cm9pZC5jbG9uZSgpLmFwcGx5TWF0cml4NChyb3RhdGlvbikubXVsdGlwbHlTY2FsYXIoc2NhbGUpKTtcblxuICBjb25zdCBldWxlciA9IG5ldyBUSFJFRS5FdWxlcigpLnNldEZyb21Sb3RhdGlvbk1hdHJpeChyb3RhdGlvbik7XG5cbiAgcmV0dXJuIHtcbiAgICB0cmFuc2xhdGlvbjogeyB4OiB0cmFuc2xhdGlvbi54LCB5OiB0cmFuc2xhdGlvbi55LCB6OiB0cmFuc2xhdGlvbi56IH0sXG4gICAgcm90YXRpb246IHsgeDogZXVsZXIueCwgeTogZXVsZXIueSwgejogZXVsZXIueiB9LFxuICAgIHNjYWxlLFxuICB9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE9iakFkZGVkIHtcbiAgaWQ6IHN0cmluZztcbiAgbmFtZTogc3RyaW5nO1xuICBmaWxlOiBzdHJpbmc7IC8vIFRoZSBVUkwgdG8gdGhlIC5vYmogZmlsZVxuICBwb3NpdGlvbjogeyB4OiBudW1iZXI7IHk6IG51bWJlcjsgejogbnVtYmVyIH07XG4gIHJvdGF0aW9uOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB6OiBudW1iZXIgfTtcbiAgc2NhbGU6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9O1xufVxuXG5pbnRlcmZhY2UgUGFyc2VkTWVzaEdyb3VwIHtcbiAgc3RhcnQ6IG51bWJlcjtcbiAgY291bnQ6IG51bWJlcjtcbiAgbWF0ZXJpYWxJbmRleDogbnVtYmVyO1xufVxuXG4vKiogUmF3IGdlb21ldHJ5IGRhdGEgZXh0cmFjdGVkIGZyb20gYSBwYXJzZWQgT0JKIG1lc2gsIHNlbnQgYmFjayBieSB0aGUgcGFyc2VyIHdvcmtlci4gKi9cbmludGVyZmFjZSBQYXJzZWRNZXNoRGF0YSB7XG4gIG5hbWU6IHN0cmluZztcbiAgLy8gT25lIG5hbWUgcGVyIG1hdGVyaWFsIHVzZWQgYnkgdGhpcyBtZXNoLiBBIHNpbmdsZS1tYXRlcmlhbCBtZXNoIGhhcyBvbmUgZW50cnk7XG4gIC8vIGEgbWVzaCB3aXRoIG11bHRpcGxlIHVzZW10bCBzd2l0Y2hlcyAoZ2VvbWV0cnkuZ3JvdXBzKSBoYXMgb25lIHBlciBncm91cC5cbiAgbWF0ZXJpYWxOYW1lczogc3RyaW5nW107XG4gIGdyb3VwczogUGFyc2VkTWVzaEdyb3VwW10gfCBudWxsO1xuICBwb3NpdGlvbjogRmxvYXQzMkFycmF5O1xuICBub3JtYWw6IEZsb2F0MzJBcnJheSB8IG51bGw7XG4gIHV2OiBGbG9hdDMyQXJyYXkgfCBudWxsO1xuICBpbmRleDogVWludDMyQXJyYXkgfCBudWxsO1xuICBib3VuZGluZ0JveD86IHsgbWluOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl07IG1heDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdIH07XG59XG5cbmludGVyZmFjZSBPYmpQYXJzZXJXb3JrZXJSZXNwb25zZSB7XG4gIHJlcXVlc3RJZDogc3RyaW5nO1xuICBzdWNjZXNzOiBib29sZWFuO1xuICBtZXNoZXM/OiBQYXJzZWRNZXNoRGF0YVtdO1xuICBlcnJvcj86IHN0cmluZztcbn1cblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgVGhyZWVKc1NlcnZpY2Uge1xuICBwcml2YXRlIHNjZW5lITogVEhSRUUuU2NlbmU7XG4gIHByaXZhdGUgY2FtZXJhITogVEhSRUUuUGVyc3BlY3RpdmVDYW1lcmE7XG4gIHByaXZhdGUgcmVuZGVyZXIhOiBUSFJFRS5XZWJHTFJlbmRlcmVyO1xuICBwcml2YXRlIGNvbnRyb2xzITogT3JiaXRDb250cm9scztcbiAgcHJpdmF0ZSBhbmltYXRpb25GcmFtZUlkITogbnVtYmVyO1xuXG4gIC8vIFJheWNhc3RpbmcgYW5kIGludGVyYWN0aW9uXG4gIHByaXZhdGUgcmF5Y2FzdGVyID0gbmV3IFRIUkVFLlJheWNhc3RlcigpO1xuICBwcml2YXRlIG1vdXNlID0gbmV3IFRIUkVFLlZlY3RvcjIoKTtcbiAgcHJpdmF0ZSBpc0RyYWdnaW5nQ2FtZXJhOiBib29sZWFuID0gZmFsc2U7XG4gIHByaXZhdGUgaXNEcmFnZ2luZ1RpbWVyOiBhbnk7XG4gIHByaXZhdGUgY2xpY2thYmxlT2JqZWN0czogVEhSRUUuT2JqZWN0M0RbXSA9IFtdO1xuXG4gIC8vIE9ic2VydmFibGVzIGZvciB0aGUgdmlld2VyIGNvbXBvbmVudFxuICBwdWJsaWMgb2JqZWN0SG92ZXJlZCQgPSBuZXcgU3ViamVjdDx7IGlkOiBzdHJpbmc7IGV2ZW50OiBNb3VzZUV2ZW50IH0gfCBudWxsPigpO1xuICBwdWJsaWMgb2JqZWN0Q2xpY2tlZCQgPSBuZXcgU3ViamVjdDxzdHJpbmcgfCBudWxsPigpO1xuXG4gIC8qKiBFbWl0cyB3aGVuIHRoZSB1c2VyIGNsaWNrcyBhIHRhZywgbWlycm9yaW5nIE1hdHRlcnBvcnQncyB0YWcgY2xpY2suICovXG4gIHB1YmxpYyB0YWdDbGlja2VkJCA9IG5ldyBTdWJqZWN0PFRocmVlSnNUYWdDbGljaz4oKTtcblxuICAvKiogRW1pdHMgYSB0YWcncyBsYWJlbCB3aGlsZSB0aGUgcG9pbnRlciBpcyBvdmVyIGl0LCBhbmQgbnVsbCB3aGVuIGl0IGxlYXZlcy4gKi9cbiAgcHVibGljIHRhZ0hvdmVyZWQkID0gbmV3IFN1YmplY3Q8eyBsYWJlbDogc3RyaW5nOyBldmVudDogTW91c2VFdmVudCB9IHwgbnVsbD4oKTtcblxuICAvLyBUYWdzIGxpdmUgdW5kZXIgdGhlIG1vZGVsIHJhdGhlciB0aGFuIHRoZSBzY2VuZSwgc28gdGhlIGNlbnRlcmluZyBhbmQgc2NhbGluZyBhcHBsaWVkIHdoZW4gdGhlXG4gIC8vIG1vZGVsIGlzIHBsYWNlZCBjYXJyaWVzIG92ZXIgYW5kIFBPSSBjb29yZGluYXRlcyBjYW4gc3RheSBpbiB0aGUgbW9kZWwncyBvd24gc3BhY2UuXG4gIHByaXZhdGUgdGFnR3JvdXAgPSBuZXcgVEhSRUUuR3JvdXAoKTtcbiAgcHJpdmF0ZSB0YWdTcHJpdGVzID0gbmV3IE1hcDxzdHJpbmcsIFRIUkVFLlNwcml0ZT4oKTtcblxuICBwcml2YXRlIGNhbnZhc1Jlc2l6ZU9ic2VydmVyPzogUmVzaXplT2JzZXJ2ZXI7XG5cbiAgLyoqIEFib3J0cyB0aGUgaW4tZmxpZ2h0IG1vZGVsIGRvd25sb2FkLiBQYXJzaW5nIGNhbm5vdCBiZSBpbnRlcnJ1cHRlZCwgb25seSBub3Qgc3RhcnRlZC4gKi9cbiAgcHJpdmF0ZSBsb2FkQWJvcnRDb250cm9sbGVyPzogQWJvcnRDb250cm9sbGVyO1xuXG4gIC8vIFRoZSBsb2FkZWQgYmFzZSBtb2RlbCwga2VwdCBzbyB0YWcgcGxhY2VtZW50IGNhbiByYXljYXN0IGFnYWluc3QgaXRzIGdlb21ldHJ5LlxuICBwcml2YXRlIG1vZGVsUm9vdD86IFRIUkVFLk9iamVjdDNEO1xuXG4gIC8qKiBUcnVlIHdoaWxlIHRoZSBuZXh0IGNsaWNrIG9uIHRoZSBtb2RlbCBwbGFjZXMgYSB0YWcgaW5zdGVhZCBvZiBvcmJpdGluZy4gKi9cbiAgcHJpdmF0ZSBwbGFjaW5nVGFnID0gZmFsc2U7XG5cbiAgLyoqIFdoYXQgdGhlIGFybWVkIHBsYWNlbWVudCBpcyBmb3IsIGVjaG9lZCBiYWNrIHdoZW4gdGhlIHNwb3QgaXMgcGlja2VkLiAqL1xuICBwcml2YXRlIHBsYWNlbWVudFBvaVR5cGU6IFBvaVR5cGUgfCBudWxsID0gbnVsbDtcblxuICAvKiogUG9zaXRpb24gYW5kIG5vcm1hbCBvZiB0aGUgbGFzdCBwbGFjZWQgdGFnLCBpbiBtb2RlbCBzcGFjZSwgYXdhaXRpbmcgY29tbWl0IHRvIGEgUE9JLiAqL1xuICBwcml2YXRlIHBlbmRpbmdUYWdQbGFjZW1lbnQ6IFRocmVlSnNUYWdQbGFjZW1lbnQgfCBudWxsID0gbnVsbDtcblxuICAvKiogUG9pbnRlciBwb3NpdGlvbiB3YWl0aW5nIGZvciBhIHByZXZpZXcgcGljaywgY29hbGVzY2VkIHRvIG9uZSBwZXIgZnJhbWUuICovXG4gIHByaXZhdGUgcGVuZGluZ1ByZXZpZXdFdmVudDogTW91c2VFdmVudCB8IG51bGwgPSBudWxsO1xuXG4gIC8qKlxuICAgKiBMYXllciBjYXJyeWluZyB0aGUgbW9kZWwgYWxvbmUsIHNvIHRoZSBkZXB0aCBwaWNrIGNhbiByZW5kZXIgaXQgd2l0aG91dCB0aGUgdGFncy5cbiAgICpcbiAgICogTGF5ZXJzIGFyZSB0ZXN0ZWQgcGVyIG9iamVjdCBhdCByZW5kZXIgdGltZSBhbmQgY2hpbGRyZW4gYXJlIHRyYXZlcnNlZCByZWdhcmRsZXNzIG9mIHdoZXRoZXJcbiAgICogdGhlaXIgcGFyZW50IHBhc3NlZCwgc28gbWFya2luZyB0aGUgbW9kZWwncyBvd24gbWVzaGVzIGlzIGVub3VnaCAtIG5vIHJlLXBhcmVudGluZy5cbiAgICovXG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IFBJQ0tfTEFZRVIgPSAxO1xuXG4gIC8qKiBDYWNoZSBTdG9yYWdlIGJ1Y2tldCBob2xkaW5nIGRvd25sb2FkZWQgbW9kZWxzLCBhbmQgaG93IG1hbnkgdG8ga2VlcC4gKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgTU9ERUxfQ0FDSEUgPSAnc21hcnRlcnBsYW4tM2QtbW9kZWxzJztcblxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBNT0RFTF9DQUNIRV9TSVpFID0gMztcblxuICBwcml2YXRlIHBpY2tUYXJnZXQ/OiBUSFJFRS5XZWJHTFJlbmRlclRhcmdldDtcblxuICBwcml2YXRlIHBpY2tDYW1lcmE/OiBUSFJFRS5QZXJzcGVjdGl2ZUNhbWVyYTtcblxuICBwcml2YXRlIHBpY2tEZXB0aE1hdGVyaWFsPzogVEhSRUUuTWVzaERlcHRoTWF0ZXJpYWw7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBwaWNrUGl4ZWwgPSBuZXcgVWludDhBcnJheSg0KTtcblxuICAvKiogRW1pdHMgb25jZSBhIHRhZyBpcyBkcm9wcGVkIG9uIHRoZSBtb2RlbCwgc28gdGhlIFVJIGNhbiBjb25maXJtIGFuZCBwZXJzaXN0IGl0LiAqL1xuICBwdWJsaWMgdGFnUGxhY2VkJCA9IG5ldyBTdWJqZWN0PFRocmVlSnNUYWdQbGFjZW1lbnQ+KCk7XG5cbiAgLyoqIEVtaXRzIHdoZW4gdGhlIGNhbGlicmF0aW9uIHdhbGt0aHJvdWdoIGlzIGFiYW5kb25lZCwgc28gdGhlIHBhbmVsIGRyaXZpbmcgaXQgY2FuIGNsb3NlLiAqL1xuICBwdWJsaWMgYWxpZ25tZW50Q2FuY2VsbGVkJCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgLyoqIFRyYW5zbHVjZW50IG1hcmtlciBzaG93biB1bmRlciB0aGUgY3Vyc29yIHdoaWxlIHBsYWNpbmcuICovXG4gIHByaXZhdGUgcGxhY2VtZW50UHJldmlldz86IFRIUkVFLlNwcml0ZTtcblxuICBwcml2YXRlIGFsaWduaW5nID0gZmFsc2U7XG4gIHByaXZhdGUgYWxpZ25tZW50UGFpcnM6IFRocmVlSnNBbGlnbm1lbnRQYWlyW10gPSBbXTtcbiAgcHJpdmF0ZSBhbGlnbm1lbnRTb3VyY2U6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9IHwgbnVsbCA9IG51bGw7XG5cbiAgLyoqIERyaXZlcyB0aGUgYWxpZ25tZW50IHdhbGt0aHJvdWdoJ3MgVUk6IGhvdyBtYW55IHBhaXJzIGFyZSBkb25lIGFuZCB3aGF0IGl0IHdhbnRzIG5leHQuICovXG4gIHB1YmxpYyBhbGlnbm1lbnRQcm9ncmVzcyQgPSBuZXcgU3ViamVjdDx7XG4gICAgcGFpcnM6IG51bWJlcjtcbiAgICBhd2FpdGluZzogJ3RhZycgfCAncG9pbnQnIHwgbnVsbDtcbiAgICB0YWdJZD86IHN0cmluZztcbiAgfT4oKTtcblxuICAvKiogSW4tZmxpZ2h0IGNhbWVyYSBtb3ZlIHN0YXJ0ZWQgYnkgZ29Ub1RhZywgYWR2YW5jZWQgYnkgdGhlIHJlbmRlciBsb29wLiAqL1xuICBwcml2YXRlIGNhbWVyYVR3ZWVuOiB7XG4gICAgZnJvbVBvc2l0aW9uOiBUSFJFRS5WZWN0b3IzO1xuICAgIHRvUG9zaXRpb246IFRIUkVFLlZlY3RvcjM7XG4gICAgZnJvbVRhcmdldDogVEhSRUUuVmVjdG9yMztcbiAgICB0b1RhcmdldDogVEhSRUUuVmVjdG9yMztcbiAgICBzdGFydGVkQXQ6IG51bWJlcjtcbiAgICBkdXJhdGlvbk1zOiBudW1iZXI7XG4gIH0gfCBudWxsID0gbnVsbDtcblxuICAvKiogRW1pdHMgdHJ1ZSB3aGlsZSBhIGhlYXZ5IE9CSiBmaWxlIGlzIGJlaW5nIHBhcnNlZCAoaW4gYSB3b3JrZXIsIHdoZW4gYXZhaWxhYmxlKSwgZmFsc2Ugb25jZSBkb25lLiAqL1xuICBwdWJsaWMgbW9kZWxMb2FkaW5nJCA9IG5ldyBTdWJqZWN0PGJvb2xlYW4+KCk7XG5cbiAgLyoqXG4gICAqIERvd25sb2FkIHByb2dyZXNzIGZvciBHTEIgbW9kZWxzLCBhcyBhIDAtMSBmcmFjdGlvbiwgb3IgbnVsbCB3aGVuIHRoZSB0b3RhbCBzaXplIGlzIHVua25vd24uXG4gICAqXG4gICAqIEEgbnVsbCBtZWFucyB0aGUgc2VydmVyIGRpZCBub3Qgc2VuZCBDb250ZW50LUxlbmd0aCAoY2h1bmtlZCByZXNwb25zZXMsIHNvbWUgcHJveGllcykgYW5kIG5vXG4gICAqIHBlcmNlbnRhZ2UgY2FuIGJlIGRlcml2ZWQ7IHNob3cgYW4gaW5kZXRlcm1pbmF0ZSBpbmRpY2F0b3IgcmF0aGVyIHRoYW4gZ3Vlc3NpbmcgYXQgb25lLlxuICAgKi9cbiAgcHVibGljIG1vZGVsTG9hZFByb2dyZXNzJCA9IG5ldyBTdWJqZWN0PG51bWJlciB8IG51bGw+KCk7XG5cbiAgLyoqXG4gICAqIEVtaXRzIHdoZW4gYSBsb2FkIGFjdHVhbGx5IHN0b3BwZWQgc2hvcnQsIHdoaWNoIGlzIG5vdCB0aGUgc2FtZSBhcyBhc2tpbmcgaXQgdG8uXG4gICAqXG4gICAqIGNhbmNlbE1vZGVsTG9hZCgpIGNhbiBhcnJpdmUgYWZ0ZXIgcGFyc2UoKSBoYXMgdGFrZW4gdGhlIG1haW4gdGhyZWFkLCBhbmQgcGFyc2UgY2Fubm90IGJlXG4gICAqIGludGVycnVwdGVkIC0gdGhlIG1vZGVsIGZpbmlzaGVzIGxvYWRpbmcgcmVnYXJkbGVzcy4gT25seSB0aGlzIHRlbGxzIHRoZSBVSSB3aGljaCBvZiB0aGUgdHdvXG4gICAqIGhhcHBlbmVkLCBzbyBpdCBkb2VzIG5vdCBvZmZlciB0byByZWxvYWQgc29tZXRoaW5nIHRoYXQgaXMgYWxyZWFkeSBvbiBzY3JlZW4uXG4gICAqL1xuICBwdWJsaWMgbW9kZWxMb2FkQ2FuY2VsbGVkJCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgLy8gQmFja2dyb3VuZCBwYXJzaW5nIG9mIGhlYXZ5IC5vYmogZmlsZXMsIHByb3ZpZGVkIGJ5IHRoZSBob3N0aW5nIGFwcCAoc2VlIHNldE9ialBhcnNlcldvcmtlcikuXG4gIC8vIEFuZ3VsYXIgbGlicmFyaWVzIGJ1aWx0IHdpdGggbmctcGFja2FnciBjYW4ndCBidW5kbGUgYG5ldyBXb3JrZXIobmV3IFVSTCguLi4pKWAgdGhlbXNlbHZlcyxcbiAgLy8gc28gdGhlIGFwcCBpbnN0YW50aWF0ZXMgdGhlIHdvcmtlciAoaXRzIGJ1aWxkIHBpcGVsaW5lIHN1cHBvcnRzIGl0KSBhbmQgaGFuZHMgaXQgb3ZlciBoZXJlLlxuICBwcml2YXRlIG9ialBhcnNlcldvcmtlcj86IFdvcmtlcjtcbiAgcHJpdmF0ZSBvYmpQYXJzZXJQZW5kaW5nUmVxdWVzdHMgPSBuZXcgTWFwPHN0cmluZywgeyByZXNvbHZlOiAobWVzaGVzOiBQYXJzZWRNZXNoRGF0YVtdKSA9PiB2b2lkOyByZWplY3Q6IChlcnJvcjogRXJyb3IpID0+IHZvaWQgfT4oKTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIG5nWm9uZTogTmdab25lKSB7IH1cblxuICAvKipcbiAgICogUmVnaXN0ZXJzIGEgV2ViIFdvcmtlciB1c2VkIHRvIHBhcnNlIGhlYXZ5IC5vYmogZmlsZXMgb2ZmIHRoZSBtYWluIHRocmVhZC5cbiAgICogTXVzdCBiZSBpbnN0YW50aWF0ZWQgYnkgdGhlIGNvbnN1bWluZyBhcHAgKGUuZy4gYG5ldyBXb3JrZXIobmV3IFVSTCgnLi9vYmotcGFyc2VyLndvcmtlcicsIGltcG9ydC5tZXRhLnVybCkpYCksXG4gICAqIHNpbmNlIGEgbGlicmFyeSBwYWNrYWdlZCB3aXRoIG5nLXBhY2thZ3IgY2Fubm90IGJ1bmRsZSB3b3JrZXIgZmlsZXMgaXRzZWxmLlxuICAgKiBXaGVuIG5vIHdvcmtlciBpcyBwcm92aWRlZCwgbG9hZE9iak1vZGVsIGZhbGxzIGJhY2sgdG8gcGFyc2luZyBvbiB0aGUgbWFpbiB0aHJlYWQuXG4gICAqL1xuICBzZXRPYmpQYXJzZXJXb3JrZXIod29ya2VyOiBXb3JrZXIpOiB2b2lkIHtcbiAgICB0aGlzLm9ialBhcnNlcldvcmtlciA9IHdvcmtlcjtcbiAgICB0aGlzLm9ialBhcnNlcldvcmtlci5vbm1lc3NhZ2UgPSAoeyBkYXRhIH06IE1lc3NhZ2VFdmVudDxPYmpQYXJzZXJXb3JrZXJSZXNwb25zZT4pID0+IHtcbiAgICAgIGNvbnN0IHBlbmRpbmcgPSB0aGlzLm9ialBhcnNlclBlbmRpbmdSZXF1ZXN0cy5nZXQoZGF0YS5yZXF1ZXN0SWQpO1xuICAgICAgaWYgKCFwZW5kaW5nKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMub2JqUGFyc2VyUGVuZGluZ1JlcXVlc3RzLmRlbGV0ZShkYXRhLnJlcXVlc3RJZCk7XG4gICAgICBpZiAoZGF0YS5zdWNjZXNzICYmIGRhdGEubWVzaGVzKSB7XG4gICAgICAgIHBlbmRpbmcucmVzb2x2ZShkYXRhLm1lc2hlcyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBwZW5kaW5nLnJlamVjdChuZXcgRXJyb3IoZGF0YS5lcnJvciB8fCAnVW5rbm93biBlcnJvciB3aGlsZSBwYXJzaW5nIE9CSiBmaWxlIGluIHdvcmtlcicpKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSByZXF1ZXN0T2JqUGFyc2VJbldvcmtlcihvYmpVcmw6IHN0cmluZyk6IFByb21pc2U8UGFyc2VkTWVzaERhdGFbXT4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCByZXF1ZXN0SWQgPSBgJHtEYXRlLm5vdygpfS0ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnNsaWNlKDIpfWA7XG4gICAgICB0aGlzLm9ialBhcnNlclBlbmRpbmdSZXF1ZXN0cy5zZXQocmVxdWVzdElkLCB7IHJlc29sdmUsIHJlamVjdCB9KTtcbiAgICAgIHRoaXMub2JqUGFyc2VyV29ya2VyIS5wb3N0TWVzc2FnZSh7IHJlcXVlc3RJZCwgb2JqVXJsIH0pO1xuICAgICAgLy8gRGlhZ25vc3RpYyBvbmx5OiBsYXJnZSBtb2RlbHMgbGVnaXRpbWF0ZWx5IHRha2UgYSB3aGlsZSwgdGhpcyBpc24ndCBhIHRpbWVvdXQvYWJvcnQsXG4gICAgICAvLyBqdXN0IGEgc2lnbmFsIGluIGNhc2UgdGhlIHdvcmtlciB3ZW50IHNpbGVudCAoZS5nLiBpdCByYW4gb3V0IG9mIG1lbW9yeSBtaWQtcGFyc2UpLlxuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLm9ialBhcnNlclBlbmRpbmdSZXF1ZXN0cy5oYXMocmVxdWVzdElkKSkge1xuICAgICAgICAgIGNvbnNvbGUud2FybihgVGhyZWVKc1NlcnZpY2U6IG9iai1wYXJzZXIgd29ya2VyIGhhcyBub3QgcmVwbGllZCBhZnRlciA2MHMgZm9yIHJlcXVlc3QgJHtyZXF1ZXN0SWR9IChvYmpVcmw9JHtvYmpVcmx9KS4gU3RpbGwgd2FpdGluZyAtIGxhcmdlIGZpbGVzIGNhbiB0YWtlIGEgd2hpbGUsIGJ1dCB0aGlzIG1heSBhbHNvIG1lYW4gdGhlIHdvcmtlciBodW5nIG9yIHJhbiBvdXQgb2YgbWVtb3J5LmApO1xuICAgICAgICB9XG4gICAgICB9LCA2MDAwMCk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogUmVidWlsZHMgYSBUSFJFRS5NZXNoIGZyb20gdGhlIHJhdyB0eXBlZCBhcnJheXMgc2VudCBiYWNrIGJ5IHRoZSBwYXJzZXIgd29ya2VyLiAqL1xuICBwcml2YXRlIG1lc2hGcm9tUGFyc2VkRGF0YShkYXRhOiBQYXJzZWRNZXNoRGF0YSwgbWF0ZXJpYWxzPzogYW55KTogVEhSRUUuTWVzaCB7XG4gICAgY29uc3QgZ2VvbWV0cnkgPSBuZXcgVEhSRUUuQnVmZmVyR2VvbWV0cnkoKTtcbiAgICBnZW9tZXRyeS5zZXRBdHRyaWJ1dGUoJ3Bvc2l0aW9uJywgbmV3IFRIUkVFLkJ1ZmZlckF0dHJpYnV0ZShkYXRhLnBvc2l0aW9uLCAzKSk7XG4gICAgaWYgKGRhdGEudXYpIHtcbiAgICAgIGdlb21ldHJ5LnNldEF0dHJpYnV0ZSgndXYnLCBuZXcgVEhSRUUuQnVmZmVyQXR0cmlidXRlKGRhdGEudXYsIDIpKTtcbiAgICB9XG4gICAgLy8gSW5kZXggaGFzIHRvIGJlIHNldCBiZWZvcmUgYW55IG5vcm1hbCBjb21wdXRhdGlvbiwgd2hpY2ggcmVhZHMgaXQgdG8gYXZlcmFnZSBzaGFyZWQgZmFjZXMuXG4gICAgaWYgKGRhdGEuaW5kZXgpIHtcbiAgICAgIGdlb21ldHJ5LnNldEluZGV4KG5ldyBUSFJFRS5CdWZmZXJBdHRyaWJ1dGUoZGF0YS5pbmRleCwgMSkpO1xuICAgIH1cbiAgICBpZiAoZGF0YS5ub3JtYWwpIHtcbiAgICAgIGdlb21ldHJ5LnNldEF0dHJpYnV0ZSgnbm9ybWFsJywgbmV3IFRIUkVFLkJ1ZmZlckF0dHJpYnV0ZShkYXRhLm5vcm1hbCwgMykpO1xuICAgIH0gZWxzZSB7XG4gICAgICBnZW9tZXRyeS5jb21wdXRlVmVydGV4Tm9ybWFscygpO1xuICAgIH1cbiAgICAvLyBCb3gzLnNldEZyb21PYmplY3QoKSByZXVzZXMgYW4gYWxyZWFkeS1zZXQgYm91bmRpbmdCb3ggaW5zdGVhZCBvZiB3YWxraW5nIGV2ZXJ5IHZlcnRleCxcbiAgICAvLyBzbyBhZG9wdGluZyB0aGUgd29ya2VyJ3MgbWFrZXMgZnJhbWluZyB0aGUgY2FtZXJhIGNoZWFwIG5vIG1hdHRlciBob3cgZGVuc2UgdGhlIG1lc2ggaXMuXG4gICAgaWYgKGRhdGEuYm91bmRpbmdCb3gpIHtcbiAgICAgIGdlb21ldHJ5LmJvdW5kaW5nQm94ID0gbmV3IFRIUkVFLkJveDMoXG4gICAgICAgIG5ldyBUSFJFRS5WZWN0b3IzKC4uLmRhdGEuYm91bmRpbmdCb3gubWluKSxcbiAgICAgICAgbmV3IFRIUkVFLlZlY3RvcjMoLi4uZGF0YS5ib3VuZGluZ0JveC5tYXgpLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBjb25zdCByZXNvbHZlZE1hdGVyaWFscyA9IGRhdGEubWF0ZXJpYWxOYW1lcy5tYXAoKG5hbWUpID0+IHtcbiAgICAgIGNvbnN0IG1hdGVyaWFsID0gbWF0ZXJpYWxzICYmIG5hbWUgPyBtYXRlcmlhbHMuY3JlYXRlKG5hbWUpIDogdW5kZWZpbmVkO1xuICAgICAgaWYgKCFtYXRlcmlhbCkge1xuICAgICAgICBjb25zb2xlLndhcm4oYFRocmVlSnNTZXJ2aWNlOiBubyBNVEwgbWF0ZXJpYWwgbmFtZWQgXCIke25hbWV9XCIgZm9yIG1lc2ggXCIke2RhdGEubmFtZX1cIiwgdXNpbmcgYSBwbGFpbiBmYWxsYmFjayAobm8gdGV4dHVyZSkuYCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gbWF0ZXJpYWwgfHwgbmV3IFRIUkVFLk1lc2hQaG9uZ01hdGVyaWFsKCk7XG4gICAgfSk7XG5cbiAgICBsZXQgbWVzaDogVEhSRUUuTWVzaDtcbiAgICBpZiAoZGF0YS5ncm91cHMgJiYgZGF0YS5ncm91cHMubGVuZ3RoID4gMCkge1xuICAgICAgZGF0YS5ncm91cHMuZm9yRWFjaCgoZ3JvdXApID0+IGdlb21ldHJ5LmFkZEdyb3VwKGdyb3VwLnN0YXJ0LCBncm91cC5jb3VudCwgZ3JvdXAubWF0ZXJpYWxJbmRleCkpO1xuICAgICAgbWVzaCA9IG5ldyBUSFJFRS5NZXNoKGdlb21ldHJ5LCByZXNvbHZlZE1hdGVyaWFscyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIG1lc2ggPSBuZXcgVEhSRUUuTWVzaChnZW9tZXRyeSwgcmVzb2x2ZWRNYXRlcmlhbHNbMF0pO1xuICAgIH1cbiAgICBtZXNoLm5hbWUgPSBkYXRhLm5hbWU7XG4gICAgcmV0dXJuIG1lc2g7XG4gIH1cblxuICBpbml0KGNhbnZhczogSFRNTENhbnZhc0VsZW1lbnQpOiB2b2lkIHtcbiAgICAvLyBUaGlzIHNlcnZpY2UgaXMgYSBzaW5nbGV0b24gYnV0IHRoZSB2aWV3ZXIgY29tcG9uZW50IGlzIHJlYnVpbHQgb24gZXZlcnkgbmF2aWdhdGlvbiwgc28gc3RhdGVcbiAgICAvLyBmcm9tIHRoZSBwcmV2aW91cyBzY2VuZSBpcyBzdGlsbCBoZXJlLiBUd28gdGhpbmdzIG91dGxpdmUgaXQgYW5kIGhhdmUgdG8gZ28gZmlyc3Q6XG4gICAgLy9cbiAgICAvLyAgLSBBIGRvd25sb2FkIHN0aWxsIGluIGZsaWdodC4gSXRzIG9uTG9hZGVkIHJlc29sdmVzIGFnYWluc3Qgd2hhdGV2ZXIgYHRoaXMuc2NlbmVgIHBvaW50cyBhdFxuICAgIC8vICAgIGJ5IHRoZW4sIHNvIHRoZSBtb2RlbCBiZWluZyBhYmFuZG9uZWQgd291bGQgYmUgaGFuZGVkIHRvIHRoZSBzY2VuZSBidWlsdCBiZWxvdy5cbiAgICAvLyAgLSBUaGUgdGFnIGdyb3VwLCB3aGljaCBpcyBhIGZpZWxkIGFuZCBrZWVwcyBpdHMgc3ByaXRlcy4gVGhleSB3b3VsZCBiZSByZS1wYXJlbnRlZCB1bmRlciB0aGVcbiAgICAvLyAgICBuZXcgbW9kZWwgYW5kIHNob3cgdGhlIHByZXZpb3VzIHZpc2l0J3MgdGFncyBvbiBpdC5cbiAgICB0aGlzLmRpc2NhcmRDdXJyZW50TG9hZCgpO1xuICAgIHRoaXMuY2xlYXJUYWdzKCk7XG4gICAgdGhpcy5jYW1lcmFUd2VlbiA9IG51bGw7XG4gICAgdGhpcy5tb2RlbFJvb3QgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyAxLiBTZXR1cCBTY2VuZVxuICAgIHRoaXMuc2NlbmUgPSBuZXcgVEhSRUUuU2NlbmUoKTtcbiAgICB0aGlzLnNjZW5lLmJhY2tncm91bmQgPSBuZXcgVEhSRUUuQ29sb3IoMHhlMGY3ZmYpOyAvLyBmcm9tIFBPQ1xuXG4gICAgLy8gMi4gU2V0dXAgQ2FtZXJhXG4gICAgdGhpcy5jYW1lcmEgPSBuZXcgVEhSRUUuUGVyc3BlY3RpdmVDYW1lcmEoNzUsIGNhbnZhcy5jbGllbnRXaWR0aCAvIGNhbnZhcy5jbGllbnRIZWlnaHQsIDAuMSwgMTAwMDApO1xuICAgIHRoaXMuY2FtZXJhLnBvc2l0aW9uLnNldCgwLCA1LCAxMCk7IC8vIHdpbGwgYmUgb3ZlcnJpZGRlbiBsYXRlciBpZiBwYXNzZWRcblxuICAgIC8vIDMuIFNldHVwIFdlYkdMIFJlbmRlcmVyXG4gICAgdGhpcy5yZW5kZXJlciA9IG5ldyBUSFJFRS5XZWJHTFJlbmRlcmVyKHsgY2FudmFzLCBhbnRpYWxpYXM6IHRydWUgfSk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRTaXplKGNhbnZhcy5jbGllbnRXaWR0aCwgY2FudmFzLmNsaWVudEhlaWdodCk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRQaXhlbFJhdGlvKE1hdGgubWluKHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLCAxLjUpKTtcbiAgICB0aGlzLm9ic2VydmVDYW52YXNSZXNpemUoY2FudmFzKTtcbiAgICBcbiAgICAvLyBGaXggZm9yIFRocmVlLmpzIHIxNDcgdnMgcjE1MisgY29sb3IgbWFuYWdlbWVudFxuICAgIGlmICgnb3V0cHV0Q29sb3JTcGFjZScgaW4gdGhpcy5yZW5kZXJlcikge1xuICAgICAgKHRoaXMucmVuZGVyZXIgYXMgYW55KS5vdXRwdXRDb2xvclNwYWNlID0gJ3NyZ2InOyAvLyBUSFJFRS5TUkdCQ29sb3JTcGFjZVxuICAgIH0gZWxzZSB7XG4gICAgICAodGhpcy5yZW5kZXJlciBhcyBhbnkpLm91dHB1dEVuY29kaW5nID0gMzAwMTsgLy8gVEhSRUUuc1JHQkVuY29kaW5nXG4gICAgfVxuXG4gICAgLy8gV2l0aG91dCB0b25lIG1hcHBpbmcgYW55dGhpbmcgYnJpZ2h0ZXIgdGhhbiAxLjAgaXMgc2ltcGx5IGN1dCBvZmYgdG8gcHVyZSB3aGl0ZS4gQ2FwdHVyZWRcbiAgICAvLyB0ZXh0dXJlcyBjYXJyeSBhIHNtYWxsIGZyYWN0aW9uIG9mIHZlcnkgYnJpZ2h0IHBpeGVscyAtIHNreSByZWZsZWN0ZWQgaW4gd2luZG93cywgc3VubGl0XG4gICAgLy8gcmVuZGVyIC0gYW5kIG9uY2UgbGlnaHRpbmcgcHVzaGVzIHRob3NlIHBhc3QgdGhlIGxpbWl0IHRoZXkgZmxhdHRlbiBpbnRvIHdoaXRlIHNwZWNrcyB3aXRoXG4gICAgLy8gbm8gZGV0YWlsIGxlZnQuIFJvbGxpbmcgdGhlIGhpZ2hsaWdodHMgb2ZmIGluc3RlYWQga2VlcHMgdGhhdCBkZXRhaWwgYW5kIGtlZXBzIGJyaWdodCBhcmVhc1xuICAgIC8vIHJlYWRpbmcgYXMgYnJpZ2h0IHJhdGhlciB0aGFuIGJsb3duLlxuICAgIHRoaXMucmVuZGVyZXIudG9uZU1hcHBpbmcgPSBUSFJFRS5BQ0VTRmlsbWljVG9uZU1hcHBpbmc7XG4gICAgdGhpcy5yZW5kZXJlci50b25lTWFwcGluZ0V4cG9zdXJlID0gMS4wO1xuXG4gICAgLy8gNC4gTGlnaHRpbmcsIGtlcHQgZGVsaWJlcmF0ZWx5IHJlc3RyYWluZWQgYW5kIG5ldXRyYWwuXG4gICAgLy9cbiAgICAvLyBDYXB0dXJlZC9waG90b2dyYW1tZXRyeSBtb2RlbHMgYWxyZWFkeSBoYXZlIGxpZ2h0aW5nIGJha2VkIGludG8gdGhlaXIgdGV4dHVyZXMgKHRoZSBzdW4gYW5kXG4gICAgLy8gc2hhZG93cyBjYW1lIHdpdGggdGhlIHBob3RvcyksIHNvIGxpZ2h0aW5nIHRoZW0gYWdhaW4gZGlzdG9ydHMgd2hhdCB0aGV5IGFjdHVhbGx5IGxvb2sgbGlrZS5cbiAgICAvLyBUaGUgaW50ZW5zaXRpZXMgYmVsb3cgYWRkIHVwIHRvIHJvdWdobHkgMS4wIG9uIGEgc3VyZmFjZSBmYWNpbmcgdGhlIHZpZXdlciwgd2hpY2ggcmVuZGVycyB0aGVcbiAgICAvLyB0ZXh0dXJlIGF0IGl0cyB0cnVlIHZhbHVlLiBBbnl0aGluZyBhYm92ZSAxLjAgaXMgY2xpcHBlZCB0byBwdXJlIHdoaXRlIGJ5IHRoZSByZW5kZXJlciwgd2hpY2hcbiAgICAvLyBpcyB3aGF0IHR1cm5zIGJyaWdodCBhcmVhcyBpbnRvIGZsYXQgd2hpdGUgcGF0Y2hlcyB3aXRoIG5vIGRldGFpbCBsZWZ0IGluIHRoZW0uXG4gICAgLy9cbiAgICAvLyBOZXV0cmFsIHdoaXRlIGlzIHVzZWQgcmF0aGVyIHRoYW4gYSB3YXJtIHRpbnQgZm9yIHRoZSBzYW1lIHJlYXNvbjogYSB0aW50ZWQgbGlnaHQgc2hpZnRzIGV2ZXJ5XG4gICAgLy8gY29sb3VyIGluIHRoZSBtb2RlbCBhd2F5IGZyb20gd2hhdCB3YXMgY2FwdHVyZWQuXG4gICAgY29uc3QgYW1iaWVudExpZ2h0ID0gbmV3IFRIUkVFLkFtYmllbnRMaWdodCgweGZmZmZmZiwgMC43KTtcbiAgICB0aGlzLnNjZW5lLmFkZChhbWJpZW50TGlnaHQpO1xuXG4gICAgLy8gSGVhZGxpZ2h0IHJhdGhlciB0aGFuIGEgZml4ZWQgc3VuOiBwYXJlbnRlZCB0byB0aGUgY2FtZXJhIHNvIGl0IGFsd2F5cyBwb2ludHMgYXQgd2hhdGV2ZXIgaXNcbiAgICAvLyBiZWluZyBsb29rZWQgYXQsIGdpdmluZyBzaGFwZSB0byBzdXJmYWNlcyB3aXRob3V0IGV2ZXIgbGVhdmluZyBhIGZhY2UgdW5saXQuIE5vIHNoYWRvdyBjYXN0aW5nXG4gICAgLy8gLSBzaGFkb3dzIHdvdWxkIGRvdWJsZSB1cCB3aXRoIHRoZSBvbmVzIGFscmVhZHkgYmFrZWQgaW50byB0aGUgdGV4dHVyZXMsIGFuZCB0aGUgc2hhZG93IG1hcFxuICAgIC8vIHdhcyBhIHJlYWwgY29zdCBwZXIgZnJhbWUuXG4gICAgY29uc3QgaGVhZExpZ2h0ID0gbmV3IFRIUkVFLkRpcmVjdGlvbmFsTGlnaHQoMHhmZmZmZmYsIDAuMzUpO1xuICAgIGhlYWRMaWdodC5wb3NpdGlvbi5zZXQoMCwgMCwgMSk7XG4gICAgdGhpcy5jYW1lcmEuYWRkKGhlYWRMaWdodCk7XG4gICAgLy8gQSBsaWdodCBvbmx5IGNvbnRyaWJ1dGVzIHdoaWxlIGl0IGlzIHBhcnQgb2YgdGhlIHNjZW5lIGdyYXBoLCBhbmQgdGhpcyBvbmUgaGFuZ3Mgb2ZmIHRoZSBjYW1lcmEuXG4gICAgdGhpcy5zY2VuZS5hZGQodGhpcy5jYW1lcmEpO1xuXG4gICAgLy8gNS4gU2V0dXAgQ29udHJvbHMgKGZyb20gUE9DKVxuICAgIHRoaXMuY29udHJvbHMgPSBuZXcgT3JiaXRDb250cm9scyh0aGlzLmNhbWVyYSwgdGhpcy5yZW5kZXJlci5kb21FbGVtZW50KTtcbiAgICB0aGlzLmNvbnRyb2xzLnJvdGF0ZVNwZWVkID0gMS4wO1xuICAgIHRoaXMuY29udHJvbHMuZGFtcGluZ0ZhY3RvciA9IDAuMTtcbiAgICB0aGlzLmNvbnRyb2xzLmVuYWJsZURhbXBpbmcgPSB0cnVlO1xuICAgIHRoaXMuY29udHJvbHMubW91c2VCdXR0b25zID0ge1xuICAgICAgTEVGVDogVEhSRUUuTU9VU0UuUk9UQVRFLFxuICAgICAgTUlERExFOiBUSFJFRS5NT1VTRS5ET0xMWSxcbiAgICAgIFJJR0hUOiBUSFJFRS5NT1VTRS5QQU4sXG4gICAgfTtcbiAgICB0aGlzLmNvbnRyb2xzLnRhcmdldC5zZXQoMCwgMCwgMCk7XG5cbiAgICAvLyBFdmVudCBsaXN0ZW5lcnMgb24gY29udHJvbHMgdG8gZGlmZmVyZW50aWF0ZSBkcmFnZ2luZyB2cyBjbGlja2luZ1xuICAgIHRoaXMuY29udHJvbHMuYWRkRXZlbnRMaXN0ZW5lcignc3RhcnQnLCAoKSA9PiB7XG4gICAgICB0aGlzLmlzRHJhZ2dpbmdUaW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICB0aGlzLmlzRHJhZ2dpbmdDYW1lcmEgPSB0cnVlO1xuICAgICAgfSwgMTUwKTtcbiAgICB9KTtcblxuICAgIHRoaXMuY29udHJvbHMuYWRkRXZlbnRMaXN0ZW5lcignZW5kJywgKCkgPT4ge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuaXNEcmFnZ2luZ1RpbWVyKTtcbiAgICAgIGlmICh0aGlzLmlzRHJhZ2dpbmdDYW1lcmEpIHtcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgdGhpcy5pc0RyYWdnaW5nQ2FtZXJhID0gZmFsc2U7XG4gICAgICAgIH0sIDI1MCk7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICAvLyBJbnRlcmFjdGlvbiBFdmVudCBMaXN0ZW5lcnMgb24gY2FudmFzXG4gICAgY2FudmFzLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgdGhpcy5vbk1vdXNlQ2xpY2suYmluZCh0aGlzKSwgZmFsc2UpO1xuICAgIGNhbnZhcy5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCB0aGlzLm9uTW91c2VNb3ZlLmJpbmQodGhpcyksIGZhbHNlKTtcblxuICAgIC8vIEVzY2FwZSBiYWNrcyBvdXQgb2YgcGxhY2VtZW50IGFuZCBjYWxpYnJhdGlvbi4gT24gdGhlIHdpbmRvdyByYXRoZXIgdGhhbiB0aGUgY2FudmFzLCB3aGljaFxuICAgIC8vIHdvdWxkIG5lZWQgZm9jdXMgdGhlIHVzZXIgaGFzIG5vIHJlYXNvbiB0byBoYXZlIGdpdmVuIGl0IC0gdGhleSBhcmUgYWltaW5nIHdpdGggdGhlIG1vdXNlLlxuICAgIC8vIFJlbW92ZWQgZmlyc3QgYmVjYXVzZSB0aGlzIHNlcnZpY2UgaXMgYSBzaW5nbGV0b246IGluaXQoKSBydW5zIGFnYWluIGZvciBldmVyeSBjYW52YXMsIGFuZFxuICAgIC8vIHRoZSBwcmV2aW91cyByZWdpc3RyYXRpb24gd291bGQgb3RoZXJ3aXNlIHN0aWxsIGJlIGF0dGFjaGVkLlxuICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdrZXlkb3duJywgdGhpcy5vbktleURvd24pO1xuICAgIHRoaXMucGlja1RhcmdldD8uZGlzcG9zZSgpO1xuICAgIHRoaXMucGlja1RhcmdldCA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLnBpY2tEZXB0aE1hdGVyaWFsPy5kaXNwb3NlKCk7XG4gICAgdGhpcy5waWNrRGVwdGhNYXRlcmlhbCA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLnBpY2tDYW1lcmEgPSB1bmRlZmluZWQ7XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLCB0aGlzLm9uS2V5RG93bik7XG5cbiAgICAvLyA2LiBTdGFydCB0aGUgcmVuZGVyIGxvb3BcbiAgICB0aGlzLm5nWm9uZS5ydW5PdXRzaWRlQW5ndWxhcigoKSA9PiB7XG4gICAgICB0aGlzLmFuaW1hdGUoKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQYXRoIChzZXJ2ZWQgYnkgdGhlIGFwcCkgaG9sZGluZyBiYXNpc190cmFuc2NvZGVyLmpzLy53YXNtLCB3aGljaCBLVFgyTG9hZGVyIG5lZWRzIHRvIHR1cm5cbiAgICogQmFzaXMgVW5pdmVyc2FsIHRleHR1cmVzIGludG8gd2hhdGV2ZXIgY29tcHJlc3NlZCBmb3JtYXQgdGhlIGxvY2FsIEdQVSBhY3R1YWxseSBzdXBwb3J0cy5cbiAgICovXG4gIHByaXZhdGUga3R4MlRyYW5zY29kZXJQYXRoID0gJ2Fzc2V0cy9iYXNpcy8nO1xuXG4gIC8qKiBPdmVycmlkZXMgd2hlcmUgdGhlIEJhc2lzIHRyYW5zY29kZXIgZmlsZXMgYXJlIHNlcnZlZCBmcm9tLiAqL1xuICBzZXRLdHgyVHJhbnNjb2RlclBhdGgocGF0aDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5rdHgyVHJhbnNjb2RlclBhdGggPSBwYXRoO1xuICB9XG5cbiAgLyoqXG4gICAqIExvYWRzIGEgYmFzZSAzRCBtb2RlbCBpbiBHTFRGL0dMQiBmb3JtYXQgLSB0aGUgcHJlZmVycmVkIGZvcm1hdCBmb3IgbGFyZ2UgbW9kZWxzLlxuICAgKiBVbmxpa2UgLm9iaiwgZ2VvbWV0cnkgYXJyaXZlcyBhcyBiaW5hcnkgdHlwZWQgYXJyYXlzIChubyB0ZXh0IHBhcnNpbmcgdG8gb2ZmbG9hZCB0byBhIHdvcmtlcilcbiAgICogYW5kIHN0YXlzIGluZGV4ZWQsIHNvIGZhciBsZXNzIGRhdGEgcmVhY2hlcyB0aGUgR1BVLiBLVFgyIHRleHR1cmVzIGFyZSBzdXBwb3J0ZWQgdG9vOiB0aGV5XG4gICAqIHN0YXkgY29tcHJlc3NlZCBpbiBWUkFNIGluc3RlYWQgb2YgYmVpbmcgZGVjb2RlZCB0byByYXcgcGl4ZWxzIGxpa2UgSlBFRy9QTkcuXG4gICAqL1xuICBsb2FkR0xURk1vZGVsKHVybDogc3RyaW5nLCBjYW1lcmFQb3NpdGlvbj86IHt4OiBudW1iZXIsIHk6IG51bWJlciwgejogbnVtYmVyfSwgcm90YXRpb24/OiB7eDogbnVtYmVyLCB5OiBudW1iZXIsIHo6IG51bWJlcn0sIGNhY2hlS2V5Pzogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgbG9hZGVyID0gbmV3IEdMVEZMb2FkZXIoKTtcblxuICAgIC8vIEtUWDIgbmVlZHMgdGhlIHJlbmRlcmVyIHRvIHJlcG9ydCB3aGljaCBjb21wcmVzc2VkIGZvcm1hdHMgdGhpcyBHUFUgYWNjZXB0cywgc28gdGhpcyBoYXMgdG9cbiAgICAvLyBoYXBwZW4gYWZ0ZXIgaW5pdCgpLiBHdWFyZGVkIHNvIGEgbW9kZWwgd2l0aG91dCBLVFgyIHRleHR1cmVzIHN0aWxsIGxvYWRzIGlmIGl0J3MgbWlzc2luZy5cbiAgICBpZiAodGhpcy5yZW5kZXJlcikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3Qga3R4MkxvYWRlciA9IG5ldyBLVFgyTG9hZGVyKClcbiAgICAgICAgICAuc2V0VHJhbnNjb2RlclBhdGgodGhpcy5rdHgyVHJhbnNjb2RlclBhdGgpXG4gICAgICAgICAgLmRldGVjdFN1cHBvcnQodGhpcy5yZW5kZXJlcik7XG4gICAgICAgIGxvYWRlci5zZXRLVFgyTG9hZGVyKGt0eDJMb2FkZXIpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdUaHJlZUpzU2VydmljZTogY291bGQgbm90IHNldCB1cCBLVFgyIHRleHR1cmUgc3VwcG9ydCwgZmFsbGluZyBiYWNrIHRvIHVuY29tcHJlc3NlZCB0ZXh0dXJlcy4nLCBlcnJvcik7XG4gICAgICB9XG4gICAgfVxuICAgIC8vIExldHMgZ2x0ZnBhY2svZ2x0Zi10cmFuc2Zvcm0gb3V0cHV0IHVzaW5nIEVYVF9tZXNob3B0X2NvbXByZXNzaW9uIGxvYWQgd2l0aG91dCBleHRyYSBzZXR1cC5cbiAgICBsb2FkZXIuc2V0TWVzaG9wdERlY29kZXIoTWVzaG9wdERlY29kZXIpO1xuXG4gICAgdGhpcy5tb2RlbExvYWRpbmckLm5leHQodHJ1ZSk7XG4gICAgdGhpcy5tb2RlbExvYWRQcm9ncmVzcyQubmV4dCgwKTtcblxuICAgIC8vIE9yZGVyIG1hdHRlcnM6IG5vcm1hbHMgbXVzdCBleGlzdCBiZWZvcmUgYW5pc290cm9weSwgYW5kIHRoZSBQaG9uZyBjb252ZXJzaW9uIGhhcyB0byBydW5cbiAgICAvLyBiZWZvcmUgc2lkZS9mbGF0U2hhZGluZyBhcmUgdG91Y2hlZCBzbyB0aG9zZSBzZXR0aW5ncyBsYW5kIG9uIHRoZSBtYXRlcmlhbHMgd2Uga2VlcC5cbiAgICBjb25zdCBvbkxvYWRlZCA9IChnbHRmOiBhbnkpID0+IHtcbiAgICAgIHRoaXMuY29udmVydFRvUGhvbmdNYXRlcmlhbHMoZ2x0Zi5zY2VuZSk7XG4gICAgICB0aGlzLmZvcmNlRG91YmxlU2lkZWRNYXRlcmlhbHMoZ2x0Zi5zY2VuZSk7XG4gICAgICB0aGlzLmVuc3VyZVZlcnRleE5vcm1hbHMoZ2x0Zi5zY2VuZSk7XG4gICAgICB0aGlzLnR1bmVUZXh0dXJlU2FtcGxpbmcoZ2x0Zi5zY2VuZSk7XG4gICAgICB0aGlzLnBsYWNlTG9hZGVkT2JqZWN0SW5TY2VuZShnbHRmLnNjZW5lLCBjYW1lcmFQb3NpdGlvbiwgcm90YXRpb24pO1xuICAgICAgdGhpcy5tb2RlbExvYWRpbmckLm5leHQoZmFsc2UpO1xuICAgIH07XG4gICAgY29uc3Qgb25FcnJvciA9IChlcnJvcjogYW55KSA9PiB7XG4gICAgICAvLyBDYW5jZWxsaW5nIGlzIGEgZGVjaXNpb24sIG5vdCBhIGZhaWx1cmU6IGNsZWFyIHRoZSBvdmVybGF5IHdpdGhvdXQgYW4gZXJyb3IgaW4gdGhlIGNvbnNvbGUuXG4gICAgICBpZiAoZXJyb3I/Lm5hbWUgPT09ICdBYm9ydEVycm9yJykge1xuICAgICAgICAvLyBPbmx5IGEgY2FuY2VsIG9mIHRoZSBsb2FkIHRoYXQgaXMgc3RpbGwgY3VycmVudCBjYW1lIGZyb20gdGhlIHVzZXI7IGFueXRoaW5nIGVsc2UgaXMgdGhpc1xuICAgICAgICAvLyBsb2FkIGhhdmluZyBiZWVuIHN1cGVyc2VkZWQgb3IgZGlzY2FyZGVkLlxuICAgICAgICBpZiAodGhpcy5sb2FkQWJvcnRDb250cm9sbGVyPy5zaWduYWwuYWJvcnRlZCkge1xuICAgICAgICAgIHRoaXMubW9kZWxMb2FkQ2FuY2VsbGVkJC5uZXh0KCk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGxvYWRpbmcgR0xURiBtb2RlbDonLCBlcnJvcik7XG4gICAgICB9XG4gICAgICB0aGlzLm1vZGVsTG9hZGluZyQubmV4dChmYWxzZSk7XG4gICAgICB0aGlzLm1vZGVsTG9hZFByb2dyZXNzJC5uZXh0KG51bGwpO1xuICAgIH07XG5cbiAgICAvLyBHTFRGTG9hZGVyIGRlY29kZXMgZW1iZWRkZWQgaW1hZ2VzIHdpdGggY3JlYXRlSW1hZ2VCaXRtYXAgd2hlbiB0aGUgYnJvd3NlciBvZmZlcnMgaXQsIGFuZCBvblxuICAgIC8vIGxhcmdlIHRleHR1cmUgYXRsYXNlcyB0aGF0IHBhdGggcmV0dXJucyB2aXNpYmx5IGNvcnJ1cHQgZGF0YSAtIHNjYXR0ZXJlZCB3aGl0ZSBibG9ja3MgYWNyb3NzXG4gICAgLy8gdGhlIG1vZGVsLiBUaGUgZXhhY3Qgc2FtZSBieXRlcyBkZWNvZGVkIHRocm91Z2ggYW4gSFRNTEltYWdlRWxlbWVudCwgd2hpY2ggaXMgd2hhdCB0aGUgLm9ialxuICAgIC8vIHBhdGggdXNlcyB2aWEgTVRMTG9hZGVyLCBjb21lIG91dCBjbGVhbi4gR0xURkxvYWRlciBhbHJlYWR5IGZhbGxzIGJhY2sgdG8gdGhhdCBkZWNvZGVyIGZvclxuICAgIC8vIFNhZmFyaSBhbmQgb2xkZXIgRmlyZWZveDsgdGhpcyBzaW1wbHkgZXh0ZW5kcyB0aGUgZmFsbGJhY2sgdG8gY292ZXIgdGhpcyBjYXNlIHRvby5cbiAgICAvL1xuICAgIC8vIFRoZSBkZWNvZGVyIGlzIGNob3NlbiB3aGlsZSBHTFRGUGFyc2VyIGlzIGJlaW5nIGNvbnN0cnVjdGVkLCB3aGljaCBoYXBwZW5zIHN5bmNocm9ub3VzbHlcbiAgICAvLyBpbnNpZGUgcGFyc2UoKSwgc28gaGlkaW5nIGNyZWF0ZUltYWdlQml0bWFwIG9ubHkgaGFzIHRvIGhvbGQgYWNyb3NzIHRoYXQgY2FsbCByYXRoZXIgdGhhblxuICAgIC8vIGZvciB0aGUgd2hvbGUgZG93bmxvYWQuXG4gICAgdGhpcy5mZXRjaFdpdGhQcm9ncmVzcyh1cmwsIGNhY2hlS2V5KVxuICAgICAgLy8gcGFyc2UoKSBob2xkcyB0aGUgbWFpbiB0aHJlYWQgZm9yIHNlY29uZHMgb24gYSBsYXJnZSBtb2RlbC4gWWllbGQgdW50aWwgdGhlIGJyb3dzZXIgaGFzXG4gICAgICAvLyBhY3R1YWxseSBwYWludGVkIGZpcnN0LCBvdGhlcndpc2UgdGhlIGZpbmFsIHByb2dyZXNzIHVwZGF0ZSBuZXZlciByZWFjaGVzIHRoZSBzY3JlZW4gYW5kXG4gICAgICAvLyB0aGUgYmFyIGFwcGVhcnMgdG8gc3RhbGwgc2hvcnQgb2YgdGhlIGVuZC5cbiAgICAgIC50aGVuKChidWZmZXIpID0+IHRoaXMuYWZ0ZXJOZXh0UGFpbnQoKS50aGVuKCgpID0+IGJ1ZmZlcikpXG4gICAgICAudGhlbigoYnVmZmVyKSA9PiB7XG4gICAgICAgIC8vIHBhcnNlKCkgY2Fubm90IGJlIGludGVycnVwdGVkLCBzbyBhIGNhbmNlbCB0aGF0IGxhbmRzIGR1cmluZyB0aGUgcGFpbnQgeWllbGQgaGFzIHRvIGJlXG4gICAgICAgIC8vIGNhdWdodCBoZXJlIC0gdGhlIGxhc3QgbW9tZW50IGJlZm9yZSB0aGUgbWFpbiB0aHJlYWQgaXMgY29tbWl0dGVkIGZvciBzZXZlcmFsIHNlY29uZHMuXG4gICAgICAgIGlmICh0aGlzLmxvYWRBYm9ydENvbnRyb2xsZXI/LnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IERPTUV4Y2VwdGlvbignTW9kZWwgbG9hZCBjYW5jZWxsZWQnLCAnQWJvcnRFcnJvcicpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGdsb2JhbFNjb3BlID0gZ2xvYmFsVGhpcyBhcyBhbnk7XG4gICAgICAgIGNvbnN0IG9yaWdpbmFsQ3JlYXRlSW1hZ2VCaXRtYXAgPSBnbG9iYWxTY29wZS5jcmVhdGVJbWFnZUJpdG1hcDtcbiAgICAgICAgZ2xvYmFsU2NvcGUuY3JlYXRlSW1hZ2VCaXRtYXAgPSB1bmRlZmluZWQ7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9hZGVyLnBhcnNlKGJ1ZmZlciwgdXJsLnN1YnN0cmluZygwLCB1cmwubGFzdEluZGV4T2YoJy8nKSArIDEpLCBvbkxvYWRlZCwgb25FcnJvcik7XG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgZ2xvYmFsU2NvcGUuY3JlYXRlSW1hZ2VCaXRtYXAgPSBvcmlnaW5hbENyZWF0ZUltYWdlQml0bWFwO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICAgLmNhdGNoKG9uRXJyb3IpO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0b3BzIHRoZSBtb2RlbCBkb3dubG9hZCBpbiBwcm9ncmVzcywgaWYgYW55LlxuICAgKlxuICAgKiBPbmx5IHRoZSBkb3dubG9hZCBjYW4gYmUgaW50ZXJydXB0ZWQ6IHBhcnNlKCkgcnVucyBzeW5jaHJvbm91c2x5IGFuZCBob2xkcyB0aGUgbWFpbiB0aHJlYWQgdW50aWxcbiAgICogaXQgZmluaXNoZXMsIHNvIG9uY2UgaXQgaGFzIGJlZ3VuIHRoZXJlIGlzIG5vdGhpbmcgdG8gY2FuY2VsLiBJbiBwcmFjdGljZSB0aGUgZG93bmxvYWQgaXMgdGhlXG4gICAqIGxvbmcgcGFydCwgd2hpY2ggaXMgd2hlcmUgc29tZW9uZSByZWFjaGVzIGZvciB0aGlzLlxuICAgKi9cbiAgY2FuY2VsTW9kZWxMb2FkKCk6IHZvaWQge1xuICAgIHRoaXMubG9hZEFib3J0Q29udHJvbGxlcj8uYWJvcnQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBYmFuZG9ucyB0aGUgbG9hZCBpbiBwcm9ncmVzcyB3aXRob3V0IHJlcG9ydGluZyBpdCBhcyBjYW5jZWxsZWQuXG4gICAqXG4gICAqIERyb3BwaW5nIHRoZSBjb250cm9sbGVyIGlzIHdoYXQgbWFrZXMgdGhlIGRpZmZlcmVuY2U6IG9uRXJyb3IgdHJlYXRzIGFuIGFib3J0IGFzIHVzZXItaW5pdGlhdGVkXG4gICAqIG9ubHkgd2hpbGUgdGhlIGNvbnRyb2xsZXIgdGhhdCBhYm9ydGVkIGlzIHN0aWxsIHRoZSBjdXJyZW50IG9uZS4gVGVhcmluZyBkb3duIG9yIHJlYnVpbGRpbmcgdGhlXG4gICAqIHNjZW5lIGlzIG5vdCBzb21ldGhpbmcgdGhlIHBlcnNvbiBhc2tlZCBmb3IsIHNvIGl0IG11c3Qgbm90IHN1cmZhY2UgYXMgXCJsb2FkaW5nIGNhbmNlbGxlZFwiLlxuICAgKi9cbiAgcHJpdmF0ZSBkaXNjYXJkQ3VycmVudExvYWQoKTogdm9pZCB7XG4gICAgdGhpcy5sb2FkQWJvcnRDb250cm9sbGVyPy5hYm9ydCgpO1xuICAgIHRoaXMubG9hZEFib3J0Q29udHJvbGxlciA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNvbHZlcyBvbmNlIHRoZSBicm93c2VyIGhhcyBwYWludGVkIGF0IGxlYXN0IG9uZSBmcmFtZS5cbiAgICpcbiAgICogQSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUgY2FsbGJhY2sgcnVucyAqYmVmb3JlKiB0aGF0IGZyYW1lIGlzIHBhaW50ZWQsIHNvIGEgc2luZ2xlIG9uZSBpcyBub3RcbiAgICogZW5vdWdoIHRvIGd1YXJhbnRlZSBwZW5kaW5nIERPTSBjaGFuZ2VzIGFyZSBvbiBzY3JlZW47IHRoZSBzZWNvbmQgZmlyZXMgYWZ0ZXIgdGhlIGZpcnN0IGZyYW1lXG4gICAqIGhhcyBnb25lIG91dC4gRmFsbHMgYmFjayB0byBhIHRpbWVvdXQgd2hlcmUgckFGIGlzbid0IGF2YWlsYWJsZSAoU1NSLCB0ZXN0cykuXG4gICAqL1xuICBwcml2YXRlIGFmdGVyTmV4dFBhaW50KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0eXBlb2YgcmVxdWVzdEFuaW1hdGlvbkZyYW1lICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMCkpO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gcmVzb2x2ZSgpKSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogRG93bmxvYWRzIGEgZmlsZSwgcmVwb3J0aW5nIHByb2dyZXNzIG9uIG1vZGVsTG9hZFByb2dyZXNzJCBhcyBpdCBhcnJpdmVzLlxuICAgKlxuICAgKiBHTFRGTG9hZGVyLmxvYWQoKSB3b3VsZCByZXBvcnQgcHJvZ3Jlc3MgdGhyb3VnaCBYSFIsIGJ1dCB0aGlzIHBhdGggZGVsaWJlcmF0ZWx5IHVzZXMgZmV0Y2goKSArXG4gICAqIHBhcnNlKCkgaW5zdGVhZCAoc2VlIGxvYWRHTFRGTW9kZWwgZm9yIHdoeSksIHNvIHByb2dyZXNzIGlzIHJlYWQgb2ZmIHRoZSByZXNwb25zZSBzdHJlYW0gaGVyZS5cbiAgICogVGhhdCBpcyBhbHNvIHRoZSBtb3JlIHVzZWZ1bCBudW1iZXI6IG9uIGEgbW9kZWwgb2YgdGhpcyBzaXplIHRoZSBkb3dubG9hZCBkb21pbmF0ZXMsIGFuZCB0aGVcbiAgICogc3RyZWFtIHJlcG9ydHMgaXQgYXMgaXQgaGFwcGVucyByYXRoZXIgdGhhbiBpbiBvbmUganVtcCBhdCB0aGUgZW5kLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBmZXRjaFdpdGhQcm9ncmVzcyh1cmw6IHN0cmluZywgY2FjaGVLZXk/OiBzdHJpbmcpOiBQcm9taXNlPEFycmF5QnVmZmVyPiB7XG4gICAgLy8gQSBsb2FkIGFscmVhZHkgcnVubmluZyBpcyByZXBsYWNlZCwgbm90IHJhY2VkOiB3aGljaGV2ZXIgZmluaXNoZWQgbGFzdCB3b3VsZCBvdGhlcndpc2Ugd2luLlxuICAgIHRoaXMubG9hZEFib3J0Q29udHJvbGxlcj8uYWJvcnQoKTtcbiAgICB0aGlzLmxvYWRBYm9ydENvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgY29uc3QgeyBzaWduYWwgfSA9IHRoaXMubG9hZEFib3J0Q29udHJvbGxlcjtcblxuICAgIGNvbnN0IGNhY2hlZCA9IGF3YWl0IHRoaXMucmVhZE1vZGVsRnJvbUNhY2hlKGNhY2hlS2V5KTtcbiAgICBpZiAoY2FjaGVkKSB7XG4gICAgICB0aGlzLm1vZGVsTG9hZFByb2dyZXNzJC5uZXh0KDEpO1xuICAgICAgcmV0dXJuIGNhY2hlZDtcbiAgICB9XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHVybCwgeyBzaWduYWwgfSk7XG4gICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZmV0Y2ggJHt1cmx9OiAke3Jlc3BvbnNlLnN0YXR1c30gJHtyZXNwb25zZS5zdGF0dXNUZXh0fWApO1xuICAgIH1cblxuICAgIGNvbnN0IGNvbnRlbnRMZW5ndGggPSBOdW1iZXIocmVzcG9uc2UuaGVhZGVycy5nZXQoJ0NvbnRlbnQtTGVuZ3RoJykpO1xuICAgIGNvbnN0IHRvdGFsID0gTnVtYmVyLmlzRmluaXRlKGNvbnRlbnRMZW5ndGgpICYmIGNvbnRlbnRMZW5ndGggPiAwID8gY29udGVudExlbmd0aCA6IDA7XG5cbiAgICAvLyBXaXRob3V0IGEgcmVhZGFibGUgc3RyZWFtIChvbGRlciBicm93c2Vycywgc29tZSBwb2x5ZmlsbHMpIHRoZXJlIGlzIG5vdGhpbmcgdG8gbWVhc3VyZSwgc29cbiAgICAvLyBmYWxsIGJhY2sgdG8gYSBwbGFpbiBidWZmZXJlZCByZWFkIGFuZCBsZXQgdGhlIFVJIHNob3cgYW4gaW5kZXRlcm1pbmF0ZSBpbmRpY2F0b3IuXG4gICAgaWYgKCFyZXNwb25zZS5ib2R5Py5nZXRSZWFkZXIpIHtcbiAgICAgIHRoaXMubW9kZWxMb2FkUHJvZ3Jlc3MkLm5leHQobnVsbCk7XG4gICAgICByZXR1cm4gcmVzcG9uc2UuYXJyYXlCdWZmZXIoKTtcbiAgICB9XG5cbiAgICBjb25zdCByZWFkZXIgPSByZXNwb25zZS5ib2R5LmdldFJlYWRlcigpO1xuXG4gICAgLy8gV2l0aCBhIGtub3duIHNpemUsIHdyaXRlIGNodW5rcyBzdHJhaWdodCBpbnRvIG9uZSBwcmUtYWxsb2NhdGVkIGJ1ZmZlci4gQ29sbGVjdGluZyB0aGVtIGluIGFuXG4gICAgLy8gYXJyYXkgYW5kIGNvbmNhdGVuYXRpbmcgYWZ0ZXJ3YXJkcyB3b3VsZCBob2xkIGJvdGggY29waWVzIGF0IG9uY2UgLSBvbiBhIDE2NCBNQiBtb2RlbCB0aGF0IGlzXG4gICAgLy8gYW4gYXZvaWRhYmxlIDMwMCsgTUIgcGVhay5cbiAgICBjb25zdCBidWZmZXIgPSB0b3RhbCA+IDAgPyBuZXcgVWludDhBcnJheSh0b3RhbCkgOiBudWxsO1xuICAgIGNvbnN0IGNodW5rczogVWludDhBcnJheVtdID0gW107XG4gICAgbGV0IHVzaW5nQnVmZmVyID0gYnVmZmVyICE9PSBudWxsO1xuICAgIGxldCByZWNlaXZlZCA9IDA7XG4gICAgLy8gQ2h1bmtzIGFycml2ZSBpbiB0ZW5zIG9mIGtpbG9ieXRlcywgc28gYSAxNjQgTUIgbW9kZWwgcHJvZHVjZXMgdGhvdXNhbmRzIG9mIHRoZW0uIEVtaXR0aW5nIG9uXG4gICAgLy8gZXZlcnkgb25lIHdvdWxkIHJ1biBjaGFuZ2UgZGV0ZWN0aW9uIGp1c3QgYXMgb2Z0ZW4gdG8gbW92ZSBhIGJhciB0aGF0IGlzIG1lYXN1cmVkIGluIHdob2xlXG4gICAgLy8gcGVyY2VudCwgc28gb25seSByZXBvcnQgd2hlbiB0aGUgZGlzcGxheWVkIHZhbHVlIHdvdWxkIGFjdHVhbGx5IGNoYW5nZS5cbiAgICBsZXQgbGFzdFJlcG9ydGVkID0gLTE7XG5cbiAgICBpZiAodG90YWwgPT09IDApIHtcbiAgICAgIHRoaXMubW9kZWxMb2FkUHJvZ3Jlc3MkLm5leHQobnVsbCk7XG4gICAgfVxuXG4gICAgZm9yICg7Oykge1xuICAgICAgY29uc3QgeyBkb25lLCB2YWx1ZSB9ID0gYXdhaXQgcmVhZGVyLnJlYWQoKTtcbiAgICAgIGlmIChkb25lKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgaWYgKHNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICAgIC8vIEZyZWUgdGhlIGNvbm5lY3Rpb24gcmF0aGVyIHRoYW4gbGVhdmluZyBpdCBkcmFpbmluZyBpbiB0aGUgYmFja2dyb3VuZC5cbiAgICAgICAgYXdhaXQgcmVhZGVyLmNhbmNlbCgpO1xuICAgICAgICB0aHJvdyBuZXcgRE9NRXhjZXB0aW9uKCdNb2RlbCBsb2FkIGNhbmNlbGxlZCcsICdBYm9ydEVycm9yJyk7XG4gICAgICB9XG4gICAgICBpZiAodXNpbmdCdWZmZXIgJiYgcmVjZWl2ZWQgKyB2YWx1ZS5sZW5ndGggPiB0b3RhbCkge1xuICAgICAgICAvLyBDb250ZW50LUxlbmd0aCB1bmRlcnN0YXRlZCB0aGUgcmVhbCBzaXplLiBNb3ZlIHdoYXQgd2UgaGF2ZSBpbnRvIHRoZSBjaHVuayBsaXN0IGFuZCBzdGF5XG4gICAgICAgIC8vIHRoZXJlIGZvciB0aGUgcmVzdCBvZiB0aGUgcmVzcG9uc2UsIHJhdGhlciB0aGFuIHRydW5jYXRpbmcgdGhlIHRhaWwuXG4gICAgICAgIGNodW5rcy5wdXNoKGJ1ZmZlci5zbGljZSgwLCByZWNlaXZlZCkpO1xuICAgICAgICB1c2luZ0J1ZmZlciA9IGZhbHNlO1xuICAgICAgfVxuICAgICAgaWYgKHVzaW5nQnVmZmVyKSB7XG4gICAgICAgIGJ1ZmZlci5zZXQodmFsdWUsIHJlY2VpdmVkKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNodW5rcy5wdXNoKHZhbHVlKTtcbiAgICAgIH1cbiAgICAgIHJlY2VpdmVkICs9IHZhbHVlLmxlbmd0aDtcbiAgICAgIGlmICh0b3RhbCA+IDApIHtcbiAgICAgICAgY29uc3QgcGVyY2VudCA9IE1hdGgubWluKE1hdGgucm91bmQoKHJlY2VpdmVkIC8gdG90YWwpICogMTAwKSwgMTAwKTtcbiAgICAgICAgaWYgKHBlcmNlbnQgIT09IGxhc3RSZXBvcnRlZCkge1xuICAgICAgICAgIGxhc3RSZXBvcnRlZCA9IHBlcmNlbnQ7XG4gICAgICAgICAgdGhpcy5tb2RlbExvYWRQcm9ncmVzcyQubmV4dChwZXJjZW50IC8gMTAwKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFRoZSBzdHJlYW0gY2FuIGVuZCBiZWxvdyB0aGUgYWR2ZXJ0aXNlZCBzaXplIChvciB3aXRoIG5vIHNpemUgYXQgYWxsKS4gTGFuZCBvbiBhIGZ1bGwgYmFyXG4gICAgLy8gcmF0aGVyIHRoYW4gbGVhdmluZyBpdCBzdHVjayBhdCB3aGF0ZXZlciB0aGUgbGFzdCBjaHVuayBoYXBwZW5lZCB0byByZWFjaC5cbiAgICBpZiAodG90YWwgPiAwICYmIGxhc3RSZXBvcnRlZCAhPT0gMTAwKSB7XG4gICAgICB0aGlzLm1vZGVsTG9hZFByb2dyZXNzJC5uZXh0KDEpO1xuICAgIH1cblxuICAgIGxldCByZXN1bHQ6IEFycmF5QnVmZmVyO1xuICAgIGlmICh1c2luZ0J1ZmZlciAmJiByZWNlaXZlZCA9PT0gdG90YWwpIHtcbiAgICAgIHJlc3VsdCA9IGJ1ZmZlci5idWZmZXI7XG4gICAgfSBlbHNlIGlmICh1c2luZ0J1ZmZlcikge1xuICAgICAgLy8gRmV3ZXIgYnl0ZXMgdGhhbiBhZHZlcnRpc2VkOiBoYW5kIGJhY2sgZXhhY3RseSB3aGF0IGFycml2ZWQuXG4gICAgICByZXN1bHQgPSBidWZmZXIuYnVmZmVyLnNsaWNlKDAsIHJlY2VpdmVkKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgbWVyZ2VkID0gbmV3IFVpbnQ4QXJyYXkocmVjZWl2ZWQpO1xuICAgICAgbGV0IG9mZnNldCA9IDA7XG4gICAgICBjaHVua3MuZm9yRWFjaCgoY2h1bmspID0+IHtcbiAgICAgICAgbWVyZ2VkLnNldChjaHVuaywgb2Zmc2V0KTtcbiAgICAgICAgb2Zmc2V0ICs9IGNodW5rLmxlbmd0aDtcbiAgICAgIH0pO1xuICAgICAgcmVzdWx0ID0gbWVyZ2VkLmJ1ZmZlcjtcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLndyaXRlTW9kZWxUb0NhY2hlKGNhY2hlS2V5LCByZXN1bHQpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogVGhlIG1vZGVsIHN0b3JlLCBvciBudWxsIHdoZXJlIHRoZSBicm93c2VyIHdpbGwgbm90IGdpdmUgdXMgb25lLlxuICAgKlxuICAgKiBDYWNoZSBTdG9yYWdlIG5lZWRzIGEgc2VjdXJlIGNvbnRleHQsIHNvIGl0IGlzIGFic2VudCBvdmVyIHBsYWluIGh0dHAgYW5kIGluIHNvbWUgZW1iZWRkZWRcbiAgICogd2Vidmlld3MuIEV2ZXJ5IGNhbGxlciB0cmVhdHMgdGhhdCBhcyBhIG1pc3MgLSBjYWNoaW5nIGlzIGFuIG9wdGltaXNhdGlvbiwgbmV2ZXIgYSByZXF1aXJlbWVudC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgb3Blbk1vZGVsQ2FjaGUoKTogUHJvbWlzZTxDYWNoZSB8IG51bGw+IHtcbiAgICBpZiAodHlwZW9mIGNhY2hlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IGNhY2hlcy5vcGVuKFRocmVlSnNTZXJ2aWNlLk1PREVMX0NBQ0hFKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBIGNhY2hlZCBjb3B5IG9mIHRoaXMgbW9kZWwsIGlmIG9uZSB3YXMgc3RvcmVkLlxuICAgKlxuICAgKiBLZXllZCBieSB0aGUgUzMga2V5IHJhdGhlciB0aGFuIHRoZSBVUkwgaXQgaXMgZmV0Y2hlZCBmcm9tOiB0aGF0IFVSTCBpcyBzaWduZWQsIHNvIGl0IGNhcnJpZXMgYVxuICAgKiBmcmVzaCBzaWduYXR1cmUgYW5kIGV4cGlyeSBldmVyeSB0aW1lIGFuZCB3b3VsZCBuZXZlciBtYXRjaCB0d2ljZS4gVGhlIGJyb3dzZXIncyBvd24gSFRUUCBjYWNoZVxuICAgKiBtaXNzZXMgZm9yIHRoZSBzYW1lIHJlYXNvbiwgd2hpY2ggaXMgd2h5IHRoaXMgZXhpc3RzIGF0IGFsbC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVhZE1vZGVsRnJvbUNhY2hlKGNhY2hlS2V5Pzogc3RyaW5nKTogUHJvbWlzZTxBcnJheUJ1ZmZlciB8IG51bGw+IHtcbiAgICBpZiAoIWNhY2hlS2V5KSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNhY2hlID0gYXdhaXQgdGhpcy5vcGVuTW9kZWxDYWNoZSgpO1xuICAgICAgY29uc3QgaGl0ID0gYXdhaXQgY2FjaGU/Lm1hdGNoKHRoaXMubW9kZWxDYWNoZVVybChjYWNoZUtleSkpO1xuICAgICAgcmV0dXJuIGhpdCA/IGF3YWl0IGhpdC5hcnJheUJ1ZmZlcigpIDogbnVsbDtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBTdG9yZXMgYSBtb2RlbCBmb3IgbmV4dCB0aW1lLCBldmljdGluZyB0aGUgb2xkZXN0IG9uY2UgYSBmZXcgYXJlIGhlbGQuICovXG4gIHByaXZhdGUgYXN5bmMgd3JpdGVNb2RlbFRvQ2FjaGUoY2FjaGVLZXk6IHN0cmluZyB8IHVuZGVmaW5lZCwgbW9kZWw6IEFycmF5QnVmZmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCFjYWNoZUtleSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgY29uc3QgY2FjaGUgPSBhd2FpdCB0aGlzLm9wZW5Nb2RlbENhY2hlKCk7XG4gICAgICBpZiAoIWNhY2hlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGF3YWl0IGNhY2hlLnB1dCh0aGlzLm1vZGVsQ2FjaGVVcmwoY2FjaGVLZXkpLCBuZXcgUmVzcG9uc2UobW9kZWwpKTtcblxuICAgICAgLy8gTW9kZWxzIHJ1biB0byBodW5kcmVkcyBvZiBtZWdhYnl0ZXMsIHNvIHRoaXMgY2Fubm90IGdyb3cgd2l0aG91dCBsaW1pdC4gQ2FjaGUgU3RvcmFnZSBrZWVwc1xuICAgICAgLy8gaW5zZXJ0aW9uIG9yZGVyLCB3aGljaCBtYWtlcyB0aGUgZnJvbnQgb2YgdGhlIGxpc3QgdGhlIGxlYXN0IHJlY2VudGx5IHN0b3JlZC5cbiAgICAgIGNvbnN0IGtleXMgPSBhd2FpdCBjYWNoZS5rZXlzKCk7XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAga2V5cy5zbGljZSgwLCBNYXRoLm1heCgwLCBrZXlzLmxlbmd0aCAtIFRocmVlSnNTZXJ2aWNlLk1PREVMX0NBQ0hFX1NJWkUpKVxuICAgICAgICAgIC5tYXAoKGtleSkgPT4gY2FjaGUuZGVsZXRlKGtleSkpXG4gICAgICApO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gT3V0IG9mIHF1b3RhLCBwcml2YXRlIGJyb3dzaW5nLCBzdG9yYWdlIGRpc2FibGVkOiB0aGUgbW9kZWwgaXMgYWxyZWFkeSBsb2FkZWQgZWl0aGVyIHdheS5cbiAgICB9XG4gIH1cblxuICAvKiogQ2FjaGUgU3RvcmFnZSBrZXlzIG9uIGEgVVJMLCBzbyB0aGUgUzMga2V5IGlzIGV4cHJlc3NlZCBhcyBvbmUuIEl0IGlzIG5ldmVyIHJlcXVlc3RlZC4gKi9cbiAgcHJpdmF0ZSBtb2RlbENhY2hlVXJsKGNhY2hlS2V5OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBgaHR0cHM6Ly8zZC1tb2RlbC1jYWNoZS5zbWFydGVycGxhbi5sb2NhbC8ke2VuY29kZVVSSUNvbXBvbmVudChjYWNoZUtleSl9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaXRzIHRoZSBjYW1lcmEncyBjbGlwcGluZyBwbGFuZXMgdG8gaG93IGJpZyB0aGUgbW9kZWwgYWN0dWFsbHkgaXMuXG4gICAqXG4gICAqIERlcHRoIHByZWNpc2lvbiBkZXBlbmRzIG9uIHRoZSBmYXIvbmVhciByYXRpbywgYW5kIHRoZSBmaXhlZCAwLjEgLyAxMDAwMCB0aGUgY2FtZXJhIHN0YXJ0cyB3aXRoXG4gICAqIGlzIGEgcmF0aW8gb2YgMTAwMDAwIGZvciBhIG1vZGVsIG9ubHkgfjEwIHVuaXRzIGFjcm9zcy4gVGhhdCB3YXN0ZXMgbW9zdCBvZiB0aGUgZGVwdGggYnVmZmVyIG9uXG4gICAqIGVtcHR5IHNwYWNlIGFuZCBsZXRzIG5lYXJseSBjb2luY2lkZW50IHN1cmZhY2VzIC0gd2hpY2ggY2FwdHVyZWQgbWVzaGVzIGhhdmUgZXZlcnl3aGVyZSAtXG4gICAqIGZsaWNrZXIgYWdhaW5zdCBlYWNoIG90aGVyLlxuICAgKi9cbiAgcHJpdmF0ZSBhZGp1c3RDbGlwcGluZ1BsYW5lcyhyYWRpdXM6IG51bWJlcik6IHZvaWQge1xuICAgIGlmIChyYWRpdXMgPCAxMCkge1xuICAgICAgdGhpcy5jYW1lcmEubmVhciA9IDAuMDE7XG4gICAgICB0aGlzLmNhbWVyYS5mYXIgPSAxMDA7XG4gICAgfSBlbHNlIGlmIChyYWRpdXMgPCAxMDApIHtcbiAgICAgIHRoaXMuY2FtZXJhLm5lYXIgPSAwLjE7XG4gICAgICB0aGlzLmNhbWVyYS5mYXIgPSAxMDAwO1xuICAgIH0gZWxzZSBpZiAocmFkaXVzIDwgMTAwMCkge1xuICAgICAgdGhpcy5jYW1lcmEubmVhciA9IDEwO1xuICAgICAgdGhpcy5jYW1lcmEuZmFyID0gMTAwMDA7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY2FtZXJhLm5lYXIgPSAxMDA7XG4gICAgICB0aGlzLmNhbWVyYS5mYXIgPSAxMDAwMDAwO1xuICAgIH1cbiAgICB0aGlzLmNhbWVyYS51cGRhdGVQcm9qZWN0aW9uTWF0cml4KCk7XG4gIH1cblxuICAvKipcbiAgICogU3dhcHMgdGhlIFBCUiBtYXRlcmlhbHMgR0xURkxvYWRlciBidWlsZHMgZm9yIHRoZSBwbGFpbiBQaG9uZyBvbmVzIHRoZSAub2JqIHBhdGggcHJvZHVjZXMuXG4gICAqXG4gICAqIE1lc2hTdGFuZGFyZE1hdGVyaWFsIGtlZXBzIGEgc3BlY3VsYXIgcmVmbGVjdGlvbiBldmVuIGF0IG1ldGFsbmVzcyAwLCBzaW5jZSBldmVyeSBkaWVsZWN0cmljXG4gICAqIHJlZmxlY3RzIHNvbWUgbGlnaHQsIGFuZCBGcmVzbmVsIGRyaXZlcyB0aGF0IHJlZmxlY3Rpb24gdG93YXJkcyB3aGl0ZSBhcyBhIHN1cmZhY2UgdHVybnMgYXdheVxuICAgKiBmcm9tIHRoZSB2aWV3ZXIuIE9uIGFuIGFlcmlhbCB2aWV3IG9mIGEgZmFjYWRlIG1vc3Qgc3VyZmFjZXMgc2l0IGF0IGV4YWN0bHkgdGhvc2UgZ3JhemluZ1xuICAgKiBhbmdsZXMsIHdoaWNoIHBhaW50cyB3aGl0ZSBoaWdobGlnaHRzIG92ZXIgd2luZG93IGZyYW1lcyBhbmQgcm9vZiBlZGdlcyAtIHRoZSBzYW1lIG1vZGVsXG4gICAqIGxvYWRlZCBhcyAub2JqIG5ldmVyIHNob3dzIHRoZW0sIGJlY2F1c2UgTVRMTG9hZGVyIGJ1aWxkcyBQaG9uZyBtYXRlcmlhbHMgYW5kIE9CSkxvYWRlciB6ZXJvZXNcbiAgICogdGhlaXIgc3BlY3VsYXIgd2hlbiB0aGUgLm10bCBkZWNsYXJlcyBOcyAwLlxuICAgKlxuICAgKiBDYXB0dXJlZCBtb2RlbHMgY2FycnkgdGhlaXIgbGlnaHRpbmcgaW4gdGhlIHRleHR1cmUgYW5kIHdhbnQgbm8gc3BlY3VsYXIgcmVzcG9uc2UgYXQgYWxsLCBzb1xuICAgKiBtYXRjaGluZyB0aGUgLm9iaiBwYXRoIGhlcmUgaXMgYm90aCB0aGUgZml4IGFuZCB0aGUgbW9yZSBmYWl0aGZ1bCBjaG9pY2UuXG4gICAqL1xuICBwcml2YXRlIGNvbnZlcnRUb1Bob25nTWF0ZXJpYWxzKHJvb3Q6IFRIUkVFLk9iamVjdDNEKTogdm9pZCB7XG4gICAgY29uc3QgY29udmVydGVkID0gbmV3IE1hcDxhbnksIFRIUkVFLk1lc2hQaG9uZ01hdGVyaWFsPigpO1xuXG4gICAgcm9vdC50cmF2ZXJzZSgoY2hpbGQpID0+IHtcbiAgICAgIGNvbnN0IG1lc2ggPSBjaGlsZCBhcyBUSFJFRS5NZXNoO1xuICAgICAgaWYgKCEobWVzaCBhcyBhbnkpLmlzTWVzaCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGNvbnZlcnQgPSAoc291cmNlOiBhbnkpOiBhbnkgPT4ge1xuICAgICAgICBpZiAoIXNvdXJjZSB8fCAhKHNvdXJjZSBhcyBhbnkpLmlzTWVzaFN0YW5kYXJkTWF0ZXJpYWwpIHtcbiAgICAgICAgICByZXR1cm4gc291cmNlO1xuICAgICAgICB9XG4gICAgICAgIGlmICghY29udmVydGVkLmhhcyhzb3VyY2UpKSB7XG4gICAgICAgICAgY29uc3QgcGhvbmcgPSBuZXcgVEhSRUUuTWVzaFBob25nTWF0ZXJpYWwoe1xuICAgICAgICAgICAgbWFwOiBzb3VyY2UubWFwLFxuICAgICAgICAgICAgY29sb3I6IHNvdXJjZS5jb2xvcixcbiAgICAgICAgICAgIHRyYW5zcGFyZW50OiBzb3VyY2UudHJhbnNwYXJlbnQsXG4gICAgICAgICAgICBvcGFjaXR5OiBzb3VyY2Uub3BhY2l0eSxcbiAgICAgICAgICAgIGFscGhhVGVzdDogc291cmNlLmFscGhhVGVzdCxcbiAgICAgICAgICAgIHNpZGU6IHNvdXJjZS5zaWRlLFxuICAgICAgICAgICAgc3BlY3VsYXI6IG5ldyBUSFJFRS5Db2xvcigweDAwMDAwMCksXG4gICAgICAgICAgICBzaGluaW5lc3M6IDAsXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgcGhvbmcubmFtZSA9IHNvdXJjZS5uYW1lO1xuICAgICAgICAgIGNvbnZlcnRlZC5zZXQoc291cmNlLCBwaG9uZyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNvbnZlcnRlZC5nZXQoc291cmNlKTtcbiAgICAgIH07XG5cbiAgICAgIG1lc2gubWF0ZXJpYWwgPSBBcnJheS5pc0FycmF5KG1lc2gubWF0ZXJpYWwpXG4gICAgICAgID8gbWVzaC5tYXRlcmlhbC5tYXAoY29udmVydClcbiAgICAgICAgOiBjb252ZXJ0KG1lc2gubWF0ZXJpYWwpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbmRlcnMgZXZlcnkgbWF0ZXJpYWwgZG91YmxlLXNpZGVkLlxuICAgKlxuICAgKiBSZWFsaXR5IGNhcHR1cmUgbWVzaGVzIHJvdXRpbmVseSBjb250YWluIHRyaWFuZ2xlcyB3aXRoIHJldmVyc2VkIHdpbmRpbmcgLSBhbiBhcnRlZmFjdCBvZiB0aGVcbiAgICogcmVjb25zdHJ1Y3Rpb24sIG5vdCBvZiB0aGUgZmlsZS4gV2l0aCB0aGUgZGVmYXVsdCBGcm9udFNpZGUgdGhvc2UgdHJpYW5nbGVzIGFyZSBjdWxsZWQsIGxlYXZpbmdcbiAgICogc2NhdHRlcmVkIGhvbGVzIHRoYXQgc2hvdyB0aGUgc2NlbmUgYmFja2dyb3VuZCB0aHJvdWdoIHRoZSBtb2RlbC4gQWdhaW5zdCBhIGxpZ2h0IGJhY2tncm91bmRcbiAgICogdGhleSByZWFkIGFzIHdoaXRlIHNwZWNrcyB3aXRoIG5vIGRpc2Nlcm5pYmxlIHBhdHRlcm4uXG4gICAqXG4gICAqIERyYXdpbmcgYm90aCBzaWRlcyBjb3N0cyBhIGxpdHRsZSBmaWxsIHJhdGUgYnV0IGlzIHdoYXQgbWFrZXMgY2FwdHVyZWQgZ2VvbWV0cnkgcmVuZGVyIGFzXG4gICAqIHNvbGlkOyBvdGhlciB2aWV3ZXJzIG9mIHRoaXMga2luZCBvZiBjb250ZW50IGRvIHRoZSBzYW1lIHVuY29uZGl0aW9uYWxseS5cbiAgICovXG4gIHByaXZhdGUgZm9yY2VEb3VibGVTaWRlZE1hdGVyaWFscyhyb290OiBUSFJFRS5PYmplY3QzRCk6IHZvaWQge1xuICAgIGNvbnN0IHNlZW4gPSBuZXcgU2V0PGFueT4oKTtcbiAgICByb290LnRyYXZlcnNlKChjaGlsZCkgPT4ge1xuICAgICAgY29uc3QgbWVzaCA9IGNoaWxkIGFzIFRIUkVFLk1lc2g7XG4gICAgICBpZiAoIShtZXNoIGFzIGFueSkuaXNNZXNoKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IG1hdGVyaWFscyA9IEFycmF5LmlzQXJyYXkobWVzaC5tYXRlcmlhbCkgPyBtZXNoLm1hdGVyaWFsIDogW21lc2gubWF0ZXJpYWxdO1xuICAgICAgbWF0ZXJpYWxzLmZvckVhY2goKG1hdGVyaWFsOiBhbnkpID0+IHtcbiAgICAgICAgaWYgKG1hdGVyaWFsICYmICFzZWVuLmhhcyhtYXRlcmlhbCkpIHtcbiAgICAgICAgICBzZWVuLmFkZChtYXRlcmlhbCk7XG4gICAgICAgICAgbWF0ZXJpYWwuc2lkZSA9IFRIUkVFLkRvdWJsZVNpZGU7XG4gICAgICAgICAgbWF0ZXJpYWwubmVlZHNVcGRhdGUgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyByZWFsIHZlcnRleCBub3JtYWxzIGZvciBhbnkgZ2VvbWV0cnkgdGhhdCBhcnJpdmVkIHdpdGhvdXQgdGhlbS5cbiAgICpcbiAgICogQSBnbFRGIHdpdGhvdXQgYSBOT1JNQUwgYXR0cmlidXRlIGlzIHNwZWMtY29tcGxpYW50LCBhbmQgR0xURkxvYWRlciBoYW5kbGVzIGl0IGJ5IHN3aXRjaGluZyB0aGVcbiAgICogbWF0ZXJpYWwgdG8gZmxhdFNoYWRpbmcsIHdoaWNoIG1ha2VzIHRoZSBzaGFkZXIgZGVyaXZlIHRoZSBub3JtYWwgcGVyIHBpeGVsIGZyb20gc2NyZWVuLXNwYWNlXG4gICAqIGRlcml2YXRpdmVzLiBUaGF0IGRlcml2YXRpb24gY29sbGFwc2VzIG9uIHRoZSBkZWdlbmVyYXRlLCBuZWVkbGUtdGhpbiB0cmlhbmdsZXMgdGhhdCByZWFsaXR5XG4gICAqIGNhcHR1cmUgbWVzaGVzIGFyZSBmdWxsIG9mOiB0aGUgZmFjZSBoYXMgYWxtb3N0IG5vIGFyZWEsIHRoZSBkZXJpdmVkIG5vcm1hbCBpcyBtZWFuaW5nbGVzcywgYW5kXG4gICAqIGxpZ2h0aW5nIGJsb3dzIHRob3NlIGZyYWdtZW50cyBvdXQgdG8gd2hpdGUgc3BlY2tzIC0gZGVuc2VzdCBleGFjdGx5IHdoZXJlIGZpbmUgZGV0YWlsIGlzLlxuICAgKlxuICAgKiBDb21wdXRpbmcgbm9ybWFscyBmcm9tIHRoZSBnZW9tZXRyeSBpbnN0ZWFkIGlzIHJvYnVzdCB0byB0aG9zZSB0cmlhbmdsZXMsIHNpbmNlIGEgZGVnZW5lcmF0ZVxuICAgKiBmYWNlIGNvbnRyaWJ1dGVzIGFsbW9zdCBub3RoaW5nIHRvIHRoZSBhdmVyYWdlIGFuZCBpdHMgbmVpZ2hib3VycyBkb21pbmF0ZS5cbiAgICovXG4gIHByaXZhdGUgZW5zdXJlVmVydGV4Tm9ybWFscyhyb290OiBUSFJFRS5PYmplY3QzRCk6IHZvaWQge1xuICAgIHJvb3QudHJhdmVyc2UoKGNoaWxkKSA9PiB7XG4gICAgICBjb25zdCBtZXNoID0gY2hpbGQgYXMgVEhSRUUuTWVzaDtcbiAgICAgIGlmICghKG1lc2ggYXMgYW55KS5pc01lc2gpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgZ2VvbWV0cnkgPSBtZXNoLmdlb21ldHJ5IGFzIFRIUkVFLkJ1ZmZlckdlb21ldHJ5O1xuICAgICAgaWYgKGdlb21ldHJ5LmdldEF0dHJpYnV0ZSgnbm9ybWFsJykpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBnZW9tZXRyeS5jb21wdXRlVmVydGV4Tm9ybWFscygpO1xuXG4gICAgICAvLyBHTFRGTG9hZGVyIGFscmVhZHkgc3dpdGNoZWQgdGhlc2UgbWF0ZXJpYWxzIHRvIGZsYXRTaGFkaW5nIGJlY2F1c2Ugbm9ybWFscyB3ZXJlIG1pc3Npbmc7XG4gICAgICAvLyBub3cgdGhhdCByZWFsIG9uZXMgZXhpc3QsIGxldCB0aGVtIGJlIHVzZWQgaW5zdGVhZCBvZiB0aGUgZGVyaXZhdGl2ZSBmYWxsYmFjay5cbiAgICAgIGNvbnN0IG1hdGVyaWFscyA9IEFycmF5LmlzQXJyYXkobWVzaC5tYXRlcmlhbCkgPyBtZXNoLm1hdGVyaWFsIDogW21lc2gubWF0ZXJpYWxdO1xuICAgICAgbWF0ZXJpYWxzLmZvckVhY2goKG1hdGVyaWFsOiBhbnkpID0+IHtcbiAgICAgICAgaWYgKG1hdGVyaWFsICYmIG1hdGVyaWFsLmZsYXRTaGFkaW5nKSB7XG4gICAgICAgICAgbWF0ZXJpYWwuZmxhdFNoYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgICBtYXRlcmlhbC5uZWVkc1VwZGF0ZSA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbmZpZ3VyZXMgaG93IGV2ZXJ5IHRleHR1cmUgaW4gYSBtb2RlbCBpcyBzYW1wbGVkOiBtYXhpbXVtIGFuaXNvdHJvcHksIGFuZCBubyBtaXBtYXBwaW5nLlxuICAgKlxuICAgKiBQaG90b2dyYW1tZXRyeSBhdGxhc2VzIHBhY2sgdGhlaXIgVVYgaXNsYW5kcyB3aXRoIHVudXNlZCBndXR0ZXIgcGl4ZWxzIGJldHdlZW4gdGhlbS4gTWlwbWFwcGluZ1xuICAgKiBhdmVyYWdlcyBuZWlnaGJvdXJpbmcgcGl4ZWxzIGF0IGVhY2ggbGV2ZWwsIHNvIHRob3NlIGd1dHRlcnMgYmxlZWQgZnVydGhlciBpbnRvIHRoZSBpc2xhbmRzIHRoZVxuICAgKiBzbWFsbGVyIHRoZSBsZXZlbCBnZXRzIC0gYW5kIHZpZXdpbmcgdGhlIHdob2xlIG1vZGVsIGF0IG9uY2UgdXNlcyB0aGUgc21hbGxlc3QgbGV2ZWxzLCBwYWludGluZ1xuICAgKiBicmlnaHQgZ3V0dGVyIHBpeGVscyBvdmVyIHRoZSBlZGdlcyBvZiBmaW5lIGRldGFpbC4gU2FtcGxpbmcgdGhlIGZ1bGwtcmVzb2x1dGlvbiBpbWFnZSBhdm9pZHNcbiAgICogdGhhdCwgYXQgdGhlIGNvc3Qgb2Ygc29tZSBzaGltbWVyIG9uIG1pbmlmaWVkIHN1cmZhY2VzLlxuICAgKlxuICAgKiBOb3RlIHRoYXQgYW5pc290cm9weSBvbmx5IHRha2VzIGVmZmVjdCB3aGlsZSBtaXBtYXBwaW5nIGlzIG9uLCBzbyB3aXRoIHRoZSBzZXR0aW5ncyBiZWxvdyBpdCBpc1xuICAgKiBpbmVydDsgaXQgaXMgc2V0IGFueXdheSBzbyB0aGUgc2FtcGxpbmcgc3RheXMgY29ycmVjdCBmb3IgYW55b25lIHdobyByZS1lbmFibGVzIG1pcG1hcHMgaGVyZS5cbiAgICovXG4gIHByaXZhdGUgdHVuZVRleHR1cmVTYW1wbGluZyhyb290OiBUSFJFRS5PYmplY3QzRCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5yZW5kZXJlcikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBtYXhBbmlzb3Ryb3B5ID0gdGhpcy5yZW5kZXJlci5jYXBhYmlsaXRpZXMuZ2V0TWF4QW5pc290cm9weSgpO1xuICAgIGlmICghbWF4QW5pc290cm9weSB8fCBtYXhBbmlzb3Ryb3B5IDw9IDEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBzZWVuID0gbmV3IFNldDxhbnk+KCk7XG4gICAgcm9vdC50cmF2ZXJzZSgoY2hpbGQpID0+IHtcbiAgICAgIGNvbnN0IG1lc2ggPSBjaGlsZCBhcyBUSFJFRS5NZXNoO1xuICAgICAgaWYgKCEobWVzaCBhcyBhbnkpLmlzTWVzaCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBtYXRlcmlhbHMgPSBBcnJheS5pc0FycmF5KG1lc2gubWF0ZXJpYWwpID8gbWVzaC5tYXRlcmlhbCA6IFttZXNoLm1hdGVyaWFsXTtcbiAgICAgIG1hdGVyaWFscy5mb3JFYWNoKChtYXRlcmlhbDogYW55KSA9PiB7XG4gICAgICAgIGlmICghbWF0ZXJpYWwpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gU2FtZSB0ZXh0dXJlIGlzIHJldXNlZCBhY3Jvc3MgdGhlIG1vZGVsJ3MgbWF0ZXJpYWxzOyBvbmx5IHRvdWNoIGVhY2ggb25lIG9uY2UuXG4gICAgICAgIFsnbWFwJywgJ25vcm1hbE1hcCcsICdyb3VnaG5lc3NNYXAnLCAnbWV0YWxuZXNzTWFwJywgJ2VtaXNzaXZlTWFwJywgJ2FvTWFwJ10uZm9yRWFjaCgoc2xvdCkgPT4ge1xuICAgICAgICAgIGNvbnN0IHRleHR1cmUgPSBtYXRlcmlhbFtzbG90XTtcbiAgICAgICAgICBpZiAodGV4dHVyZSAmJiAhc2Vlbi5oYXModGV4dHVyZSkpIHtcbiAgICAgICAgICAgIHNlZW4uYWRkKHRleHR1cmUpO1xuICAgICAgICAgICAgdGV4dHVyZS5hbmlzb3Ryb3B5ID0gbWF4QW5pc290cm9weTtcbiAgICAgICAgICAgIHRleHR1cmUuZ2VuZXJhdGVNaXBtYXBzID0gZmFsc2U7XG4gICAgICAgICAgICB0ZXh0dXJlLm1pbkZpbHRlciA9IFRIUkVFLkxpbmVhckZpbHRlcjtcbiAgICAgICAgICAgIHRleHR1cmUubmVlZHNVcGRhdGUgPSB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMb2FkcyBhbiBPQkogbW9kZWwgd2l0aCBNVEwuXG4gICAqIFRoZSBoZWF2eSB0ZXh0LXBhcnNpbmcgc3RlcCBydW5zIGluIGEgV2ViIFdvcmtlciB3aGVuIG9uZSBoYXMgYmVlbiByZWdpc3RlcmVkIHZpYVxuICAgKiBzZXRPYmpQYXJzZXJXb3JrZXIgKHNlZSB0aGVyZSBmb3Igd2h5IHRoZSBhcHAsIG5vdCB0aGlzIGxpYnJhcnksIHByb3ZpZGVzIGl0KTsgdGhpcyBrZWVwc1xuICAgKiB0aGUgbWFpbiB0aHJlYWQgZnJlZSB3aGlsZSBsYXJnZSBhcmNoaXRlY3R1cmUgbW9kZWxzIChtaWxsaW9ucyBvZiB2ZXJ0aWNlcykgYXJlIHBhcnNlZC5cbiAgICogRmFsbHMgYmFjayB0byBwYXJzaW5nIG9uIHRoZSBtYWluIHRocmVhZCBvdGhlcndpc2UuXG4gICAqL1xuICBsb2FkT2JqTW9kZWwobXRsVXJsOiBzdHJpbmcsIG9ialVybDogc3RyaW5nLCBjYW1lcmFQb3NpdGlvbj86IHt4OiBudW1iZXIsIHk6IG51bWJlciwgejogbnVtYmVyfSwgcm90YXRpb24/OiB7eDogbnVtYmVyLCB5OiBudW1iZXIsIHo6IG51bWJlcn0pOiB2b2lkIHtcbiAgICBjb25zdCBtdGxMb2FkZXIgPSBuZXcgTVRMTG9hZGVyKCk7XG4gICAgY29uc3QgYmFzZVBhdGggPSBtdGxVcmwuc3Vic3RyaW5nKDAsIG10bFVybC5sYXN0SW5kZXhPZignLycpICsgMSk7XG4gICAgbXRsTG9hZGVyLnNldFBhdGgoYmFzZVBhdGgpO1xuICAgIGNvbnN0IG10bEZpbGVOYW1lID0gbXRsVXJsLnN1YnN0cmluZyhtdGxVcmwubGFzdEluZGV4T2YoJy8nKSArIDEpO1xuXG4gICAgdGhpcy5tb2RlbExvYWRpbmckLm5leHQodHJ1ZSk7XG5cbiAgICBtdGxMb2FkZXIubG9hZChtdGxGaWxlTmFtZSwgKG1hdGVyaWFscykgPT4ge1xuICAgICAgbWF0ZXJpYWxzLnByZWxvYWQoKTtcblxuICAgICAgaWYgKHRoaXMub2JqUGFyc2VyV29ya2VyKSB7XG4gICAgICAgIHRoaXMucmVxdWVzdE9ialBhcnNlSW5Xb3JrZXIob2JqVXJsKVxuICAgICAgICAgIC50aGVuKChtZXNoZXMpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGdyb3VwID0gbmV3IFRIUkVFLkdyb3VwKCk7XG4gICAgICAgICAgICBtZXNoZXMuZm9yRWFjaCgobWVzaERhdGEpID0+IHtcbiAgICAgICAgICAgICAgZ3JvdXAuYWRkKHRoaXMubWVzaEZyb21QYXJzZWREYXRhKG1lc2hEYXRhLCBtYXRlcmlhbHMpKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5wbGFjZUxvYWRlZE9iamVjdEluU2NlbmUoZ3JvdXAsIGNhbWVyYVBvc2l0aW9uLCByb3RhdGlvbik7XG4gICAgICAgICAgfSlcbiAgICAgICAgICAuY2F0Y2goKGVycm9yKSA9PiB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBwYXJzaW5nIE9CSiBtb2RlbCBpbiB3b3JrZXI6JywgZXJyb3IpO1xuICAgICAgICAgIH0pXG4gICAgICAgICAgLmZpbmFsbHkoKCkgPT4gdGhpcy5tb2RlbExvYWRpbmckLm5leHQoZmFsc2UpKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBvYmpMb2FkZXIgPSBuZXcgT0JKTG9hZGVyKCk7XG4gICAgICBvYmpMb2FkZXIuc2V0TWF0ZXJpYWxzKG1hdGVyaWFscyk7XG5cbiAgICAgIG9iakxvYWRlci5sb2FkKG9ialVybCwgKG9iamVjdCkgPT4ge1xuICAgICAgICB0aGlzLnBsYWNlTG9hZGVkT2JqZWN0SW5TY2VuZShvYmplY3QsIGNhbWVyYVBvc2l0aW9uLCByb3RhdGlvbik7XG4gICAgICAgIHRoaXMubW9kZWxMb2FkaW5nJC5uZXh0KGZhbHNlKTtcbiAgICAgIH0sIHVuZGVmaW5lZCwgKGVycm9yKSA9PiB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGxvYWRpbmcgT0JKIG1vZGVsOicsIGVycm9yKTtcbiAgICAgICAgdGhpcy5tb2RlbExvYWRpbmckLm5leHQoZmFsc2UpO1xuICAgICAgfSk7XG4gICAgfSwgdW5kZWZpbmVkLCAoZXJyb3IpID0+IHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGxvYWRpbmcgTVRMIGZpbGU6JywgZXJyb3IpO1xuICAgICAgdGhpcy5tb2RlbExvYWRpbmckLm5leHQoZmFsc2UpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqIENlbnRlcnMsIHNjYWxlcyBhbmQgYWRkcyBhIGxvYWRlZCBvYmplY3QgdG8gdGhlIHNjZW5lLCB0aGVuIGZyYW1lcyB0aGUgY2FtZXJhIG9uIGl0LiAqL1xuICBwcml2YXRlIHBsYWNlTG9hZGVkT2JqZWN0SW5TY2VuZShvYmplY3Q6IFRIUkVFLk9iamVjdDNELCBjYW1lcmFQb3NpdGlvbj86IHt4OiBudW1iZXIsIHk6IG51bWJlciwgejogbnVtYmVyfSwgcm90YXRpb24/OiB7eDogbnVtYmVyLCB5OiBudW1iZXIsIHo6IG51bWJlcn0pOiB2b2lkIHtcbiAgICBjb25zdCBib3ggPSBuZXcgVEhSRUUuQm94MygpLnNldEZyb21PYmplY3Qob2JqZWN0KTtcbiAgICBjb25zdCBjZW50ZXIgPSBib3guZ2V0Q2VudGVyKG5ldyBUSFJFRS5WZWN0b3IzKCkpO1xuICAgIGNvbnN0IHNpemUgPSBib3guZ2V0U2l6ZShuZXcgVEhSRUUuVmVjdG9yMygpKTtcbiAgICBjb25zdCBtYXhEaW0gPSBNYXRoLm1heChzaXplLngsIHNpemUueSwgc2l6ZS56KTtcbiAgICBjb25zdCBzY2FsZSA9IG1heERpbSA+IDAgPyAxMCAvIG1heERpbSA6IDE7XG5cbiAgICAvLyBUd28gbmVzdGVkIGdyb3VwcyBzbyB0YWdzIGNhbiBzaGFyZSB0aGUgbW9kZWwncyBmcmFtZTogYGNlbnRlcmVkYCBjYXJyaWVzIHRoZSBvZmZzZXQgdGhhdFxuICAgIC8vIG1vdmVzIHRoZSBtb2RlbCdzIG93biBvcmlnaW4gdG8gdGhlIHNjZW5lIG9yaWdpbiwgYGdyb3VwYCBjYXJyaWVzIHRoZSBzY2FsZS4gQSB0YWcgcG9zaXRpb25lZFxuICAgIC8vIHdpdGggcmF3IFBPSSBjb29yZGluYXRlcyB0aGVuIGxhbmRzIGluIHRoZSBzYW1lIHBsYWNlIHRoZSBtb2RlbCdzIGdlb21ldHJ5IGRvZXMuXG4gICAgY29uc3QgZ3JvdXAgPSBuZXcgVEhSRUUuR3JvdXAoKTtcbiAgICBjb25zdCBjZW50ZXJlZCA9IG5ldyBUSFJFRS5Hcm91cCgpO1xuICAgIGNlbnRlcmVkLnBvc2l0aW9uLnN1YihjZW50ZXIpO1xuXG4gICAgaWYgKHJvdGF0aW9uKSB7XG4gICAgICBvYmplY3Qucm90YXRpb24uc2V0KHJvdGF0aW9uLngsIHJvdGF0aW9uLnksIHJvdGF0aW9uLnopO1xuICAgIH1cblxuICAgIHRoaXMubW9kZWxSb290ID0gb2JqZWN0O1xuICAgIC8vIE1hcmtzIHRoZSBtb2RlbCBmb3IgdGhlIGRlcHRoIHBpY2ssIHdoaWNoIHJlbmRlcnMgdGhpcyBsYXllciBhbG9uZSAtIHNvIHRhZ3MgYW5kIHRoZSBnaG9zdFxuICAgIC8vIGl0c2VsZiBjYW5ub3QgZW5kIHVwIGJlaW5nIHdoYXQgdGhlIGN1cnNvciByZXBvcnRzIGFzIHRoZSBzdXJmYWNlIHVuZGVyIGl0LlxuICAgIG9iamVjdC50cmF2ZXJzZSgoY2hpbGQpID0+IGNoaWxkLmxheWVycy5lbmFibGUoVGhyZWVKc1NlcnZpY2UuUElDS19MQVlFUikpO1xuICAgIGNlbnRlcmVkLmFkZChvYmplY3QpO1xuICAgIGNlbnRlcmVkLmFkZCh0aGlzLnRhZ0dyb3VwKTtcbiAgICBncm91cC5hZGQoY2VudGVyZWQpO1xuICAgIGdyb3VwLnNjYWxlLnNldFNjYWxhcihzY2FsZSk7XG5cbiAgICB0aGlzLnNjZW5lLmFkZChncm91cCk7XG5cbiAgICAvLyBUYWdzIHJlZ2lzdGVyZWQgYmVmb3JlIHRoZSBtb2RlbCBmaW5pc2hlZCBsb2FkaW5nIHdlcmUgc2l6ZWQgYWdhaW5zdCB0aGUgb2xkIHNjYWxlLlxuICAgIHRoaXMudGFnU3ByaXRlcy5mb3JFYWNoKChzcHJpdGUpID0+IHRoaXMuc2NhbGVUYWdGb3JNb2RlbChzcHJpdGUpKTtcblxuICAgIHRoaXMuYWRqdXN0Q2xpcHBpbmdQbGFuZXMoKHNpemUubGVuZ3RoKCkgLyAyKSAqIHNjYWxlKTtcblxuICAgIGlmIChjYW1lcmFQb3NpdGlvbikge1xuICAgICAgdGhpcy5jYW1lcmEucG9zaXRpb24uc2V0KGNhbWVyYVBvc2l0aW9uLngsIGNhbWVyYVBvc2l0aW9uLnksIGNhbWVyYVBvc2l0aW9uLnopO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmNhbWVyYS5wb3NpdGlvbi5zZXQoMCwgNSwgMTUpO1xuICAgIH1cbiAgICB0aGlzLmNvbnRyb2xzLnRhcmdldC5zZXQoMCwgMCwgMCk7XG4gICAgdGhpcy5jb250cm9scy51cGRhdGUoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBLZWVwcyB0aGUgZHJhd2luZyBidWZmZXIgYW5kIGNhbWVyYSBtYXRjaGVkIHRvIHRoZSBjYW52YXMgYXMgaXQgaXMgcmVzaXplZC5cbiAgICpcbiAgICogcmVuZGVyZXIuc2V0U2l6ZSgpIHdyaXRlcyBleHBsaWNpdCBwaXhlbCB3aWR0aC9oZWlnaHQgaW50byB0aGUgY2FudmFzJ3Mgc3R5bGUsIG92ZXJyaWRpbmcgdGhlXG4gICAqIDEwMCUvMTAwJSBpdCBzdGFydHMgd2l0aC4gV2l0aG91dCB0aGlzLCB0aGUgY2FudmFzIGtlZXBzIHdoYXRldmVyIHNpemUgaXQgaGFkIGF0IHN0YXJ0dXA6IGhpZGluZ1xuICAgKiB0aGUgdG9wIG1lbnUgZ3Jvd3MgdGhlIGNvbnRhaW5lciwgdGhlIGNhbnZhcyBkb2VzIG5vdCBmb2xsb3csIGFuZCB0aGUgc3RyaXAgdGhhdCBvcGVucyB1cCBzaG93c1xuICAgKiB0aGUgcGFnZSBiZWhpbmQgaXQgaW5zdGVhZCBvZiB0aGUgc2NlbmUuXG4gICAqXG4gICAqIFRoZSBwYXJlbnQgaXMgb2JzZXJ2ZWQgcmF0aGVyIHRoYW4gdGhlIGNhbnZhcywgYmVjYXVzZSByZXNpemluZyB0aGUgY2FudmFzIGlzIHdoYXQgdGhpcyByZWFjdHNcbiAgICogdG8gLSB3YXRjaGluZyB0aGUgZWxlbWVudCB3ZSByZXNpemUgd291bGQgcmV0cmlnZ2VyIHRoZSBvYnNlcnZlci5cbiAgICovXG4gIHByaXZhdGUgb2JzZXJ2ZUNhbnZhc1Jlc2l6ZShjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50KTogdm9pZCB7XG4gICAgY29uc3QgY29udGFpbmVyID0gY2FudmFzLnBhcmVudEVsZW1lbnQ7XG4gICAgaWYgKCFjb250YWluZXIgfHwgdHlwZW9mIFJlc2l6ZU9ic2VydmVyID09PSAndW5kZWZpbmVkJykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuY2FudmFzUmVzaXplT2JzZXJ2ZXIgPSBuZXcgUmVzaXplT2JzZXJ2ZXIoKCkgPT4ge1xuICAgICAgY29uc3Qgd2lkdGggPSBjb250YWluZXIuY2xpZW50V2lkdGg7XG4gICAgICBjb25zdCBoZWlnaHQgPSBjb250YWluZXIuY2xpZW50SGVpZ2h0O1xuICAgICAgaWYgKHdpZHRoID09PSAwIHx8IGhlaWdodCA9PT0gMCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLmNhbWVyYS5hc3BlY3QgPSB3aWR0aCAvIGhlaWdodDtcbiAgICAgIHRoaXMuY2FtZXJhLnVwZGF0ZVByb2plY3Rpb25NYXRyaXgoKTtcbiAgICAgIHRoaXMucmVuZGVyZXIuc2V0U2l6ZSh3aWR0aCwgaGVpZ2h0KTtcbiAgICB9KTtcbiAgICB0aGlzLmNhbnZhc1Jlc2l6ZU9ic2VydmVyLm9ic2VydmUoY29udGFpbmVyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydHMgdGhlIGFsaWdubWVudCB3YWxrdGhyb3VnaDogcGljayBhIHRhZywgdGhlbiBjbGljayB3aGVyZSBpdCByZWFsbHkgaXMsIHRocmVlIHRpbWVzLlxuICAgKlxuICAgKiBFeGlzdGluZyB0YWdzIGFyZSB0aGUgY29ycmVzcG9uZGVuY2VzIC0gdGhleSBhbHJlYWR5IGNhcnJ5IHRoZSBzdG9yZWQgY29vcmRpbmF0ZSwgc28gdGhlIHBlcnNvblxuICAgKiBvbmx5IGhhcyB0byBzdXBwbHkgdGhlIG1hdGNoaW5nIHNwb3Qgb24gdGhlIG1vZGVsLiBObyBuZXcgcmVjb3JkcyBhcmUgd3JpdHRlbjsgbm90aGluZyBpc1xuICAgKiBhcHBsaWVkIHVudGlsIHNvbHZlQWxpZ25tZW50RnJvbVBhaXJzKCkgc3VjY2VlZHMuXG4gICAqL1xuICBzdGFydFRhZ0FsaWdubWVudCgpOiB2b2lkIHtcbiAgICB0aGlzLmFsaWduaW5nID0gdHJ1ZTtcbiAgICB0aGlzLmFsaWdubWVudFBhaXJzID0gW107XG4gICAgdGhpcy5hbGlnbm1lbnRTb3VyY2UgPSBudWxsO1xuICAgIGlmICh0aGlzLnJlbmRlcmVyKSB7XG4gICAgICB0aGlzLnJlbmRlcmVyLmRvbUVsZW1lbnQuc3R5bGUuY3Vyc29yID0gJ2Nyb3NzaGFpcic7XG4gICAgfVxuICAgIHRoaXMuYWxpZ25tZW50UHJvZ3Jlc3MkLm5leHQoeyBwYWlyczogMCwgYXdhaXRpbmc6ICd0YWcnIH0pO1xuICB9XG5cbiAgY2FuY2VsVGFnQWxpZ25tZW50KCk6IHZvaWQge1xuICAgIGNvbnN0IHdhc0FsaWduaW5nID0gdGhpcy5hbGlnbmluZztcbiAgICB0aGlzLmFsaWduaW5nID0gZmFsc2U7XG4gICAgdGhpcy5hbGlnbm1lbnRQYWlycyA9IFtdO1xuICAgIHRoaXMuYWxpZ25tZW50U291cmNlID0gbnVsbDtcbiAgICB0aGlzLnBlbmRpbmdQcmV2aWV3RXZlbnQgPSBudWxsO1xuICAgIHRoaXMucmVtb3ZlUGxhY2VtZW50UHJldmlldygpO1xuICAgIGlmICh0aGlzLnJlbmRlcmVyKSB7XG4gICAgICB0aGlzLnJlbmRlcmVyLmRvbUVsZW1lbnQuc3R5bGUuY3Vyc29yID0gJ2RlZmF1bHQnO1xuICAgIH1cbiAgICB0aGlzLmFsaWdubWVudFByb2dyZXNzJC5uZXh0KHsgcGFpcnM6IDAsIGF3YWl0aW5nOiBudWxsIH0pO1xuICAgIGlmICh3YXNBbGlnbmluZykge1xuICAgICAgdGhpcy5hbGlnbm1lbnRDYW5jZWxsZWQkLm5leHQoKTtcbiAgICB9XG4gIH1cblxuICBpc0FsaWduaW5nVGFncygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5hbGlnbmluZztcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRoZSBwb2ludGVyIHNob3VsZCBiZSBzaG93aW5nIHRoZSBwbGFjZW1lbnQgZ2hvc3QuXG4gICAqXG4gICAqIFRydWUgd2hpbGUgcGxhY2luZyBhIHRhZywgYW5kIHdoaWxlIGNhbGlicmF0aW5nIG9uY2UgYSB0YWcgaGFzIGJlZW4gcGlja2VkIC0gdGhhdCBzZWNvbmQgY2xpY2tcbiAgICogaXMgdGhlIHNhbWUgcXVlc3Rpb24gKFwid2hlcmUgb24gdGhlIG1vZGVsP1wiKSBhbmQgZGVzZXJ2ZXMgdGhlIHNhbWUgYWltaW5nIGFpZC4gTm90IHRydWUgZm9yIHRoZVxuICAgKiBmaXJzdCBoYWxmIG9mIGEgcGFpciwgd2hpY2ggaXMgYSBjbGljayBvbiBhIHRhZywgbm90IG9uIHRoZSBtb2RlbC5cbiAgICovXG4gIHByaXZhdGUgaXNQcmV2aWV3aW5nUGxhY2VtZW50KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnBsYWNpbmdUYWcgfHwgKHRoaXMuYWxpZ25pbmcgJiYgISF0aGlzLmFsaWdubWVudFNvdXJjZSk7XG4gIH1cblxuICAvKiogV2hldGhlciBhIHRhZyBpcyBjdXJyZW50bHkgcmVnaXN0ZXJlZCBmb3IgdGhpcyBlbGVtZW50LiAqL1xuICBoYXNUYWcoZWxlbWVudElkOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy50YWdTcHJpdGVzLmhhcyhlbGVtZW50SWQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNvbHZlcyBhbmQgYXBwbGllcyB0aGUgYWxpZ25tZW50IGNvbGxlY3RlZCBzbyBmYXIsIHJldHVybmluZyBpdCBzbyB0aGUgY2FsbGVyIGNhbiBwZXJzaXN0IGl0LlxuICAgKlxuICAgKiBSZXR1cm5zIG51bGwgd2hlbiB0aGUgdGhyZWUgdGFncyB0dXJuIG91dCB0byBiZSBjb2xsaW5lYXIsIGluIHdoaWNoIGNhc2Ugbm90aGluZyBpcyBhcHBsaWVkIGFuZFxuICAgKiB0aGUgd2Fsa3Rocm91Z2ggc3RheXMgb3BlbiBmb3IgdGhlIHBlcnNvbiB0byBwaWNrIGEgYmV0dGVyIHRoaXJkIHBvaW50LlxuICAgKi9cbiAgc29sdmVBbGlnbm1lbnRGcm9tUGFpcnMoKTogVGhyZWVKc0FsaWdubWVudCB8IG51bGwge1xuICAgIGNvbnN0IGFsaWdubWVudCA9IHNvbHZlVGhyZWVKc0FsaWdubWVudCh0aGlzLmFsaWdubWVudFBhaXJzKTtcbiAgICBpZiAoIWFsaWdubWVudCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHRoaXMuc2V0VGFnQWxpZ25tZW50KGFsaWdubWVudCk7XG4gICAgdGhpcy5hbGlnbmluZyA9IGZhbHNlO1xuICAgIHRoaXMucGVuZGluZ1ByZXZpZXdFdmVudCA9IG51bGw7XG4gICAgdGhpcy5yZW1vdmVQbGFjZW1lbnRQcmV2aWV3KCk7XG4gICAgdGhpcy5yZW5kZXJlci5kb21FbGVtZW50LnN0eWxlLmN1cnNvciA9ICdkZWZhdWx0JztcbiAgICB0aGlzLmFsaWdubWVudFByb2dyZXNzJC5uZXh0KHsgcGFpcnM6IHRoaXMuYWxpZ25tZW50UGFpcnMubGVuZ3RoLCBhd2FpdGluZzogbnVsbCB9KTtcbiAgICByZXR1cm4gYWxpZ25tZW50O1xuICB9XG5cbiAgLyoqIEhhbmRsZXMgYSBjbGljayB3aGlsZSB0aGUgYWxpZ25tZW50IHdhbGt0aHJvdWdoIGlzIHJ1bm5pbmcuICovXG4gIHByaXZhdGUgaGFuZGxlQWxpZ25tZW50Q2xpY2soZXZlbnQ6IE1vdXNlRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuYWxpZ25tZW50U291cmNlKSB7XG4gICAgICAvLyBGaXJzdCBoYWxmIG9mIGEgcGFpcjogd2hpY2ggdGFnIGFyZSB3ZSB0YWxraW5nIGFib3V0P1xuICAgICAgY29uc3QgaGl0ID0gdGhpcy5yYXljYXN0ZXIuaW50ZXJzZWN0T2JqZWN0cyh0aGlzLnRhZ0dyb3VwLmNoaWxkcmVuLCBmYWxzZSlbMF07XG4gICAgICBpZiAoIWhpdCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBUaGUgc3ByaXRlJ3Mgb3duIHBvc2l0aW9uIGlzIHRoZSBzdG9yZWQgY29vcmRpbmF0ZSwgdW50b3VjaGVkIGJ5IGFueSBhbGlnbm1lbnQgYWxyZWFkeVxuICAgICAgLy8gYXBwbGllZCAtIHRoYXQgdHJhbnNmb3JtIGxpdmVzIG9uIHRoZSBncm91cCwgbm90IG9uIHRoZSBzcHJpdGUuXG4gICAgICBjb25zdCBzdG9yZWQgPSBoaXQub2JqZWN0LnBvc2l0aW9uO1xuICAgICAgdGhpcy5hbGlnbm1lbnRTb3VyY2UgPSB7IHg6IHN0b3JlZC54LCB5OiBzdG9yZWQueSwgejogc3RvcmVkLnogfTtcbiAgICAgIHRoaXMuYWxpZ25tZW50UHJvZ3Jlc3MkLm5leHQoe1xuICAgICAgICBwYWlyczogdGhpcy5hbGlnbm1lbnRQYWlycy5sZW5ndGgsXG4gICAgICAgIGF3YWl0aW5nOiAncG9pbnQnLFxuICAgICAgICB0YWdJZDogaGl0Lm9iamVjdC5uYW1lLFxuICAgICAgfSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gU2Vjb25kIGhhbGY6IHdoZXJlIHRoYXQgdGFnIGFjdHVhbGx5IGJlbG9uZ3Mgb24gdGhlIG1vZGVsLlxuICAgIGNvbnN0IHRhcmdldCA9IHRoaXMucGlja0FsaWdubWVudFRhcmdldChldmVudCk7XG4gICAgaWYgKCF0YXJnZXQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5hbGlnbm1lbnRQYWlycy5wdXNoKHsgc291cmNlOiB0aGlzLmFsaWdubWVudFNvdXJjZSwgdGFyZ2V0IH0pO1xuICAgIHRoaXMuYWxpZ25tZW50U291cmNlID0gbnVsbDtcbiAgICAvLyBUaGUgZ2hvc3QgYmVsb25ncyB0byB0aGUgY2xpY2sgdGhhdCBqdXN0IGhhcHBlbmVkOyB0aGUgbmV4dCBoYWxmIGlzIGEgY2xpY2sgb24gYSB0YWcuXG4gICAgdGhpcy5wZW5kaW5nUHJldmlld0V2ZW50ID0gbnVsbDtcbiAgICB0aGlzLnJlbW92ZVBsYWNlbWVudFByZXZpZXcoKTtcbiAgICB0aGlzLmFsaWdubWVudFByb2dyZXNzJC5uZXh0KHtcbiAgICAgIHBhaXJzOiB0aGlzLmFsaWdubWVudFBhaXJzLmxlbmd0aCxcbiAgICAgIGF3YWl0aW5nOiB0aGlzLmFsaWdubWVudFBhaXJzLmxlbmd0aCA+PSAzID8gbnVsbCA6ICd0YWcnLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIEFybXMgdGFnIHBsYWNlbWVudDogdGhlIG5leHQgY2xpY2sgb24gdGhlIG1vZGVsIGRyb3BzIGEgdGFnIGluc3RlYWQgb2YgYmVpbmcgaWdub3JlZC4gKi9cbiAgc3RhcnRUYWdQbGFjZW1lbnQocG9pVHlwZTogUG9pVHlwZSB8IG51bGwgPSBudWxsKTogdm9pZCB7XG4gICAgdGhpcy5wbGFjaW5nVGFnID0gdHJ1ZTtcbiAgICB0aGlzLnBsYWNlbWVudFBvaVR5cGUgPSBwb2lUeXBlO1xuICAgIHRoaXMucGVuZGluZ1RhZ1BsYWNlbWVudCA9IG51bGw7XG4gICAgaWYgKHRoaXMucmVuZGVyZXIpIHtcbiAgICAgIHRoaXMucmVuZGVyZXIuZG9tRWxlbWVudC5zdHlsZS5jdXJzb3IgPSAnY3Jvc3NoYWlyJztcbiAgICB9XG4gIH1cblxuICAvKiogTGVhdmVzIHBsYWNlbWVudCBtb2RlIHdpdGhvdXQgcGxhY2luZyBhbnl0aGluZy4gKi9cbiAgY2FuY2VsVGFnUGxhY2VtZW50KCk6IHZvaWQge1xuICAgIHRoaXMucGxhY2luZ1RhZyA9IGZhbHNlO1xuICAgIHRoaXMucGVuZGluZ1RhZ1BsYWNlbWVudCA9IG51bGw7XG4gICAgdGhpcy5wZW5kaW5nUHJldmlld0V2ZW50ID0gbnVsbDtcbiAgICB0aGlzLnJlbW92ZVBsYWNlbWVudFByZXZpZXcoKTtcbiAgICBpZiAodGhpcy5yZW5kZXJlcikge1xuICAgICAgdGhpcy5yZW5kZXJlci5kb21FbGVtZW50LnN0eWxlLmN1cnNvciA9ICdkZWZhdWx0JztcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2hvd3MgYSB0cmFuc2x1Y2VudCB0YWcgdW5kZXIgdGhlIGN1cnNvciB3aGlsZSBwbGFjaW5nLCBzbyB0aGUgc3BvdCBpcyB2aXNpYmxlIGJlZm9yZSBjb21taXRpbmcuXG4gICAqXG4gICAqIE1hdHRlcnBvcnQgZG9lcyB0aGlzIHdpdGggYSB0YWcgdGhhdCBmb2xsb3dzIHRoZSBwb2ludGVyOyB0aGUgZXF1aXZhbGVudCBoZXJlIGlzIGEgcHJldmlld1xuICAgKiBzcHJpdGUgdGhhdCBnZXRzIHJlbW92ZWQgdGhlIG1vbWVudCB0aGUgcmVhbCB0YWcgdGFrZXMgaXRzIHBsYWNlLlxuICAgKi9cbiAgcHJpdmF0ZSB1cGRhdGVQbGFjZW1lbnRQcmV2aWV3KGV2ZW50OiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgY29uc3QgcG9zaXRpb24gPSB0aGlzLnBpY2tQb2ludE9uR3B1KGV2ZW50KTtcbiAgICBpZiAoIXBvc2l0aW9uKSB7XG4gICAgICB0aGlzLnJlbW92ZVBsYWNlbWVudFByZXZpZXcoKTtcbiAgICAgIHRoaXMucmVuZGVyZXIuZG9tRWxlbWVudC5zdHlsZS5jdXJzb3IgPSAnbm90LWFsbG93ZWQnO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMucmVuZGVyZXIuZG9tRWxlbWVudC5zdHlsZS5jdXJzb3IgPSAnY3Jvc3NoYWlyJztcbiAgICBpZiAoIXRoaXMucGxhY2VtZW50UHJldmlldykge1xuICAgICAgdGhpcy5wbGFjZW1lbnRQcmV2aWV3ID0gbmV3IFRIUkVFLlNwcml0ZShcbiAgICAgICAgbmV3IFRIUkVFLlNwcml0ZU1hdGVyaWFsKHtcbiAgICAgICAgICBtYXA6IHRoaXMuY3JlYXRlVGFnVGV4dHVyZSgnIzlFOUU5RScpLFxuICAgICAgICAgIGRlcHRoVGVzdDogZmFsc2UsXG4gICAgICAgICAgdHJhbnNwYXJlbnQ6IHRydWUsXG4gICAgICAgICAgb3BhY2l0eTogMC42LFxuICAgICAgICB9KVxuICAgICAgKTtcbiAgICAgIHRoaXMucGxhY2VtZW50UHJldmlldy5yZW5kZXJPcmRlciA9IDk5OTtcbiAgICAgIHRoaXMudGFnR3JvdXAuYWRkKHRoaXMucGxhY2VtZW50UHJldmlldyk7XG4gICAgICB0aGlzLnNjYWxlVGFnRm9yTW9kZWwodGhpcy5wbGFjZW1lbnRQcmV2aWV3KTtcbiAgICB9XG4gICAgdGhpcy5wbGFjZW1lbnRQcmV2aWV3LnBvc2l0aW9uLmNvcHkocG9zaXRpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBwb2ludCB1bmRlciB0aGUgY3Vyc29yLCByZWFkIGZyb20gdGhlIGRlcHRoIGJ1ZmZlciBpbnN0ZWFkIG9mIHJheWNhc3QsIGluIHRhZyBzcGFjZS5cbiAgICpcbiAgICogQSByYXljYXN0IHdhbGtzIGV2ZXJ5IHRyaWFuZ2xlIG9mIHRoZSBtZXNoIGl0IGhpdHM6IG9uIGEgY2FwdHVyZWQgbW9kZWwgdGhhdCBpcyBtaWxsaW9ucyBvZlxuICAgKiB0aGVtLCB0ZW5zIG9mIG1pbGxpc2Vjb25kcywgZXZlcnkgdGltZSB0aGUgcG9pbnRlciBtb3Zlcy4gQ29hbGVzY2luZyB0byBvbmUgcGVyIGZyYW1lIGRpZCBub3RcbiAgICogaGVscCwgYmVjYXVzZSB0aGUgY29zdCBpcyB0aGUgcGljayBpdHNlbGYgLSBpdCBzaW1wbHkgYmVjYW1lIG9uZSBzbG93IGZyYW1lIGluc3RlYWQgb2Ygc2V2ZXJhbC5cbiAgICpcbiAgICogUmVuZGVyaW5nIHRoZSBtb2RlbCdzIGRlcHRoIGludG8gYSBzaW5nbGUgcGl4ZWwgdW5kZXIgdGhlIGN1cnNvciBhbnN3ZXJzIHRoZSBzYW1lIHF1ZXN0aW9uIG9uXG4gICAqIHRoZSBHUFUgaW4gY29uc3RhbnQgdGltZSwgd2hhdGV2ZXIgdGhlIG1vZGVsJ3Mgc2l6ZS4gVGhlIGNhbWVyYSdzIHZpZXcgb2Zmc2V0IGlzIHdoYXQgbmFycm93c1xuICAgKiB0aGUgd2hvbGUgaW1hZ2UgZG93biB0byB0aGF0IG9uZSBwaXhlbCwgc28gdGhlIHZhbHVlIHJlYWQgYmFjayBpcyB0aGUgZGVwdGggdGhlIGN1cnNvciBpcyBvdmVyLlxuICAgKlxuICAgKiBVc2VkIGZvciB0aGUgZ2hvc3Qgb25seS4gVGhlIGNsaWNrIHRoYXQgYWN0dWFsbHkgcGxhY2VzIGEgdGFnIHN0aWxsIHJheWNhc3RzOiBpdCBpcyBvbmUgcGljayxcbiAgICogaXRzIGNvc3QgZG9lcyBub3QgbWF0dGVyLCBhbmQgaXQgeWllbGRzIHRoZSBzdXJmYWNlIG5vcm1hbCwgd2hpY2ggZGVwdGggYWxvbmUgY2Fubm90IGdpdmUuXG4gICAqL1xuICBwcml2YXRlIHBpY2tQb2ludE9uR3B1KGV2ZW50OiBNb3VzZUV2ZW50KTogVEhSRUUuVmVjdG9yMyB8IG51bGwge1xuICAgIGlmICghdGhpcy5tb2RlbFJvb3QgfHwgIXRoaXMucmVuZGVyZXIgfHwgIXRoaXMuY2FtZXJhKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMucGlja1RhcmdldCkge1xuICAgICAgdGhpcy5waWNrVGFyZ2V0ID0gbmV3IFRIUkVFLldlYkdMUmVuZGVyVGFyZ2V0KDEsIDEpO1xuICAgICAgdGhpcy5waWNrQ2FtZXJhID0gbmV3IFRIUkVFLlBlcnNwZWN0aXZlQ2FtZXJhKCk7XG4gICAgICB0aGlzLnBpY2tDYW1lcmEubGF5ZXJzLnNldChUaHJlZUpzU2VydmljZS5QSUNLX0xBWUVSKTtcbiAgICAgIHRoaXMucGlja0RlcHRoTWF0ZXJpYWwgPSBuZXcgVEhSRUUuTWVzaERlcHRoTWF0ZXJpYWwoe1xuICAgICAgICBkZXB0aFBhY2tpbmc6IFRIUkVFLlJHQkFEZXB0aFBhY2tpbmcsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBjb25zdCByZWN0ID0gdGhpcy5yZW5kZXJlci5kb21FbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIGNvbnN0IHggPSBldmVudC5jbGllbnRYIC0gcmVjdC5sZWZ0O1xuICAgIGNvbnN0IHkgPSBldmVudC5jbGllbnRZIC0gcmVjdC50b3A7XG4gICAgaWYgKHggPCAwIHx8IHkgPCAwIHx8IHggPiByZWN0LndpZHRoIHx8IHkgPiByZWN0LmhlaWdodCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgdGhpcy5waWNrQ2FtZXJhLmNvcHkodGhpcy5jYW1lcmEpO1xuICAgIHRoaXMucGlja0NhbWVyYS5sYXllcnMuc2V0KFRocmVlSnNTZXJ2aWNlLlBJQ0tfTEFZRVIpO1xuICAgIC8vIFJlbmRlcnMgdGhlIG9uZSBwaXhlbCB0aGUgY3Vyc29yIGlzIG92ZXIgYXMgaWYgaXQgd2VyZSB0aGUgd2hvbGUgaW1hZ2UgLSBvZmZzZXQgYnkgaGFsZiBhXG4gICAgLy8gcGl4ZWwgc28gdGhlIGN1cnNvciBzaXRzIGF0IHRoYXQgcGl4ZWwncyBjZW50cmUsIHdoaWNoIGlzIHdoZXJlIE5EQyAoMCwgMCkgbGFuZHMuXG4gICAgdGhpcy5waWNrQ2FtZXJhLnNldFZpZXdPZmZzZXQocmVjdC53aWR0aCwgcmVjdC5oZWlnaHQsIHggLSAwLjUsIHkgLSAwLjUsIDEsIDEpO1xuXG4gICAgLy8gV2hlcmUgdGhlIG1vZGVsIGlzIG5vdCwgdGhlIHBpeGVsIG11c3QgcmVhZCBhcyBcIm5vdGhpbmdcIiwgc28gaXQgaXMgY2xlYXJlZCB0byB3aGl0ZSAtIGEgdmFsdWVcbiAgICAvLyBubyByZWFsIGRlcHRoIHBhY2tzIHRvLiBUd28gdGhpbmdzIHdvdWxkIG90aGVyd2lzZSBwYWludCBvdmVyIHRoYXQ6IHRoZSBkZWZhdWx0IGJsYWNrIGNsZWFyLFxuICAgIC8vIHdoaWNoIGRlY29kZXMgYXMgZGVwdGggMCAoYSBoaXQgYWdhaW5zdCB0aGUgbGVucyksIGFuZCB0aGUgc2NlbmUgYmFja2dyb3VuZCwgd2hpY2ggdGhyZWUuanNcbiAgICAvLyBkcmF3cyBpbiBwbGFjZSBvZiB0aGUgY2xlYXIgY29sb3VyIHdoZW5ldmVyIG9uZSBpcyBzZXQuXG4gICAgY29uc3QgcHJldmlvdXNDbGVhckNvbG9yID0gdGhpcy5yZW5kZXJlci5nZXRDbGVhckNvbG9yKG5ldyBUSFJFRS5Db2xvcigpKTtcbiAgICBjb25zdCBwcmV2aW91c0NsZWFyQWxwaGEgPSB0aGlzLnJlbmRlcmVyLmdldENsZWFyQWxwaGEoKTtcbiAgICBjb25zdCBwcmV2aW91c1RhcmdldCA9IHRoaXMucmVuZGVyZXIuZ2V0UmVuZGVyVGFyZ2V0KCk7XG4gICAgY29uc3QgcHJldmlvdXNCYWNrZ3JvdW5kID0gdGhpcy5zY2VuZS5iYWNrZ3JvdW5kO1xuXG4gICAgdGhpcy5zY2VuZS5iYWNrZ3JvdW5kID0gbnVsbDtcbiAgICB0aGlzLnNjZW5lLm92ZXJyaWRlTWF0ZXJpYWwgPSB0aGlzLnBpY2tEZXB0aE1hdGVyaWFsO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0Q2xlYXJDb2xvcigweGZmZmZmZiwgMSk7XG4gICAgdGhpcy5yZW5kZXJlci5zZXRSZW5kZXJUYXJnZXQodGhpcy5waWNrVGFyZ2V0KTtcbiAgICB0aGlzLnJlbmRlcmVyLmNsZWFyKCk7XG4gICAgdGhpcy5yZW5kZXJlci5yZW5kZXIodGhpcy5zY2VuZSwgdGhpcy5waWNrQ2FtZXJhKTtcblxuICAgIHRoaXMucmVuZGVyZXIucmVhZFJlbmRlclRhcmdldFBpeGVscyh0aGlzLnBpY2tUYXJnZXQsIDAsIDAsIDEsIDEsIHRoaXMucGlja1BpeGVsKTtcblxuICAgIHRoaXMucmVuZGVyZXIuc2V0UmVuZGVyVGFyZ2V0KHByZXZpb3VzVGFyZ2V0KTtcbiAgICB0aGlzLnJlbmRlcmVyLnNldENsZWFyQ29sb3IocHJldmlvdXNDbGVhckNvbG9yLCBwcmV2aW91c0NsZWFyQWxwaGEpO1xuICAgIHRoaXMuc2NlbmUub3ZlcnJpZGVNYXRlcmlhbCA9IG51bGw7XG4gICAgdGhpcy5zY2VuZS5iYWNrZ3JvdW5kID0gcHJldmlvdXNCYWNrZ3JvdW5kO1xuXG4gICAgY29uc3QgW3IsIGcsIGIsIGFdID0gdGhpcy5waWNrUGl4ZWw7XG4gICAgaWYgKHIgPT09IDI1NSAmJiBnID09PSAyNTUgJiYgYiA9PT0gMjU1ICYmIGEgPT09IDI1NSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgLy8gVGhlIGludmVyc2Ugb2YgdGhyZWUncyBwYWNrRGVwdGhUb1JHQkEsIHdoaWNoIHN0b3JlcyB0aGUgdmFsdWUgaXRzZWxmIGluIGFscGhhIGFuZCBldmVyIGZpbmVyXG4gICAgLy8gZnJhY3Rpb25zIG9mIGl0IGluIGJsdWUsIGdyZWVuIGFuZCByZWQgLSBhbHBoYSBpcyB0aGUgbW9zdCBzaWduaWZpY2FudCBieXRlLCBub3QgcmVkLiBSZWFkIHRoZVxuICAgIC8vIG90aGVyIHdheSByb3VuZCwgdGhlIHJlc3VsdCBpcyBtb3N0bHkgdGhlIGxvdy1vcmRlciBub2lzZSwgb2Z0ZW4gbmVhciB6ZXJvLCB3aGljaCB1bnByb2plY3RzXG4gICAgLy8gb250byB0aGUgbmVhciBwbGFuZTogYSBnaG9zdCBwcmVzc2VkIGFnYWluc3QgdGhlIGxlbnMsIGZpbGxpbmcgdGhlIHdob2xlIHNjcmVlbi5cbiAgICBjb25zdCBkb3duc2NhbGUgPSAyNTUgLyAyNTY7XG4gICAgY29uc3QgZGVwdGggPVxuICAgICAgKHIgLyAyNTUpICogZG93bnNjYWxlIC8gKDI1NiAqIDI1NiAqIDI1NikgK1xuICAgICAgKGcgLyAyNTUpICogZG93bnNjYWxlIC8gKDI1NiAqIDI1NikgK1xuICAgICAgKGIgLyAyNTUpICogZG93bnNjYWxlIC8gMjU2ICtcbiAgICAgIChhIC8gMjU1KSAqIGRvd25zY2FsZTtcblxuICAgIC8vIE5EQyB4IGFuZCB5IGFyZSB6ZXJvIGJlY2F1c2UgdGhlIHBpeGVsIHJlYWQgaXMgdGhlIGNlbnRyZSBvZiB0aGF0IG9uZS1waXhlbCBpbWFnZSAtIHdoaWNoIG9ubHlcbiAgICAvLyBob2xkcyB3aGlsZSB0aGUgdmlldyBvZmZzZXQgaXMgaW4gZWZmZWN0LiBDbGVhcmluZyBpdCBmaXJzdCB3b3VsZCB1bnByb2plY3QgdGhyb3VnaCB0aGUgZnVsbFxuICAgIC8vIHZpZXcgaW5zdGVhZCwgbGFuZGluZyB0aGUgcG9pbnQgb24gdGhlIHJheSB0aHJvdWdoIHRoZSBtaWRkbGUgb2YgdGhlIHNjcmVlbi4gVGhlIG9mZnNldCBpc1xuICAgIC8vIHNpbXBseSByZXBsYWNlZCBieSB0aGUgbmV4dCBwaWNrLCBzbyBpdCBpcyBuZXZlciBjbGVhcmVkLlxuICAgIGNvbnN0IHdvcmxkID0gbmV3IFRIUkVFLlZlY3RvcjMoMCwgMCwgZGVwdGggKiAyIC0gMSkudW5wcm9qZWN0KHRoaXMucGlja0NhbWVyYSk7XG4gICAgdGhpcy50YWdHcm91cC51cGRhdGVXb3JsZE1hdHJpeCh0cnVlLCBmYWxzZSk7XG4gICAgcmV0dXJuIHRoaXMudGFnR3JvdXAud29ybGRUb0xvY2FsKHdvcmxkKTtcbiAgfVxuXG4gIHByaXZhdGUgcmVtb3ZlUGxhY2VtZW50UHJldmlldygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMucGxhY2VtZW50UHJldmlldykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLnRhZ0dyb3VwLnJlbW92ZSh0aGlzLnBsYWNlbWVudFByZXZpZXcpO1xuICAgIHRoaXMucGxhY2VtZW50UHJldmlldy5tYXRlcmlhbC5tYXA/LmRpc3Bvc2UoKTtcbiAgICB0aGlzLnBsYWNlbWVudFByZXZpZXcubWF0ZXJpYWwuZGlzcG9zZSgpO1xuICAgIHRoaXMucGxhY2VtZW50UHJldmlldyA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKiBTaG93cyBvciBoaWRlcyBvbmUgdGFnLCBsZWF2aW5nIGl0IHJlZ2lzdGVyZWQgc28gaXQgY2FuIGJlIGJyb3VnaHQgYmFjayB3aXRob3V0IHJlbG9hZGluZy4gKi9cbiAgc2V0VGFnVmlzaWJpbGl0eShlbGVtZW50SWQ6IHN0cmluZywgdmlzaWJsZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGNvbnN0IHNwcml0ZSA9IHRoaXMudGFnU3ByaXRlcy5nZXQoZWxlbWVudElkKTtcbiAgICBpZiAoc3ByaXRlKSB7XG4gICAgICBzcHJpdGUudmlzaWJsZSA9IHZpc2libGU7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNob3dzIG9ubHkgdGFncyBvZiB0aGUgZ2l2ZW4gdHlwZXMsIGhpZGluZyB0aGUgcmVzdC5cbiAgICpcbiAgICogUGFzc2luZyBudWxsIHNob3dzIGV2ZXJ5dGhpbmcgLSB0aGUgXCJubyBmaWx0ZXJcIiBzdGF0ZSwgZGlzdGluY3QgZnJvbSBhbiBlbXB0eSBhcnJheSwgd2hpY2ggaXMgYVxuICAgKiBmaWx0ZXIgdGhhdCBoYXBwZW5zIHRvIG1hdGNoIG5vdGhpbmcuXG4gICAqL1xuICBzZXRWaXNpYmxlVGFnVHlwZXModHlwZXM6IFBvaVR5cGVbXSB8IG51bGwpOiB2b2lkIHtcbiAgICB0aGlzLnRhZ1Nwcml0ZXMuZm9yRWFjaCgoc3ByaXRlKSA9PiB7XG4gICAgICBzcHJpdGUudmlzaWJsZSA9IHR5cGVzID09PSBudWxsIHx8IHR5cGVzLmluY2x1ZGVzKHNwcml0ZS51c2VyRGF0YS5wb2lUeXBlKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNb3ZlcyB0aGUgY2FtZXJhIHRvIGxvb2sgYXQgYSB0YWcsIG1pcnJvcmluZyBNYXR0ZXJwb3J0J3MgZ29Ub1RhZy5cbiAgICpcbiAgICogQW5pbWF0ZWQgcmF0aGVyIHRoYW4gdGVsZXBvcnRlZDoganVtcGluZyB0aGUgY2FtZXJhIGxvc2VzIHRoZSB2aWV3ZXIncyBzZW5zZSBvZiB3aGVyZSB0aGV5IHdlcmVcbiAgICogcmVsYXRpdmUgdG8gdGhlIG1vZGVsLiBUaGUgYXBwcm9hY2ggZGlzdGFuY2UgZm9sbG93cyB0aGUgbW9kZWwncyBzaXplIHNvIGl0IGZyYW1lcyBzZW5zaWJseVxuICAgKiB3aGF0ZXZlciB0aGUgdW5pdHMgYXJlLlxuICAgKi9cbiAgZ29Ub1RhZyhlbGVtZW50SWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHNwcml0ZSA9IHRoaXMudGFnU3ByaXRlcy5nZXQoZWxlbWVudElkKTtcbiAgICBpZiAoIXNwcml0ZSB8fCAhdGhpcy5jYW1lcmEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCB0YXJnZXQgPSBzcHJpdGUuZ2V0V29ybGRQb3NpdGlvbihuZXcgVEhSRUUuVmVjdG9yMygpKTtcbiAgICBjb25zdCBkaXN0YW5jZSA9IHRoaXMubW9kZWxSb290XG4gICAgICA/IG5ldyBUSFJFRS5Cb3gzKCkuc2V0RnJvbU9iamVjdCh0aGlzLm1vZGVsUm9vdCkuZ2V0U2l6ZShuZXcgVEhSRUUuVmVjdG9yMygpKS5sZW5ndGgoKSAqIDAuMTVcbiAgICAgIDogNTtcblxuICAgIC8vIEFwcHJvYWNoIGFsb25nIHRoZSBjdXJyZW50IHZpZXdpbmcgZGlyZWN0aW9uLCBzbyB0aGUgY2FtZXJhIGNsb3NlcyBpbiByYXRoZXIgdGhhbiBzd2luZ2luZ1xuICAgIC8vIGFyb3VuZCB0byBhbiBhcmJpdHJhcnkgc2lkZS5cbiAgICBjb25zdCBkaXJlY3Rpb24gPSB0aGlzLmNhbWVyYS5wb3NpdGlvbi5jbG9uZSgpLnN1Yih0aGlzLmNvbnRyb2xzLnRhcmdldCkubm9ybWFsaXplKCk7XG4gICAgdGhpcy5jYW1lcmFUd2VlbiA9IHtcbiAgICAgIGZyb21Qb3NpdGlvbjogdGhpcy5jYW1lcmEucG9zaXRpb24uY2xvbmUoKSxcbiAgICAgIHRvUG9zaXRpb246IHRhcmdldC5jbG9uZSgpLmFkZChkaXJlY3Rpb24ubXVsdGlwbHlTY2FsYXIoZGlzdGFuY2UpKSxcbiAgICAgIGZyb21UYXJnZXQ6IHRoaXMuY29udHJvbHMudGFyZ2V0LmNsb25lKCksXG4gICAgICB0b1RhcmdldDogdGFyZ2V0LFxuICAgICAgc3RhcnRlZEF0OiBwZXJmb3JtYW5jZS5ub3coKSxcbiAgICAgIGR1cmF0aW9uTXM6IDYwMCxcbiAgICB9O1xuICB9XG5cbiAgLyoqIEFkdmFuY2VzIHRoZSBjYW1lcmEgYW5pbWF0aW9uIHN0YXJ0ZWQgYnkgZ29Ub1RhZywgaWYgb25lIGlzIHJ1bm5pbmcuICovXG4gIHByaXZhdGUgc3RlcENhbWVyYVR3ZWVuKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5jYW1lcmFUd2Vlbikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB7IGZyb21Qb3NpdGlvbiwgdG9Qb3NpdGlvbiwgZnJvbVRhcmdldCwgdG9UYXJnZXQsIHN0YXJ0ZWRBdCwgZHVyYXRpb25NcyB9ID0gdGhpcy5jYW1lcmFUd2VlbjtcbiAgICBjb25zdCBsaW5lYXIgPSBNYXRoLm1pbigocGVyZm9ybWFuY2Uubm93KCkgLSBzdGFydGVkQXQpIC8gZHVyYXRpb25NcywgMSk7XG4gICAgLy8gRWFzZSBvdXQsIHNvIHRoZSBtb3ZlIHNldHRsZXMgaW5zdGVhZCBvZiBzdG9wcGluZyBkZWFkLlxuICAgIGNvbnN0IHQgPSAxIC0gTWF0aC5wb3coMSAtIGxpbmVhciwgMyk7XG5cbiAgICB0aGlzLmNhbWVyYS5wb3NpdGlvbi5sZXJwVmVjdG9ycyhmcm9tUG9zaXRpb24sIHRvUG9zaXRpb24sIHQpO1xuICAgIHRoaXMuY29udHJvbHMudGFyZ2V0LmxlcnBWZWN0b3JzKGZyb21UYXJnZXQsIHRvVGFyZ2V0LCB0KTtcblxuICAgIGlmIChsaW5lYXIgPj0gMSkge1xuICAgICAgdGhpcy5jYW1lcmFUd2VlbiA9IG51bGw7XG4gICAgfVxuICB9XG5cbiAgaXNQbGFjaW5nVGFnKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnBsYWNpbmdUYWc7XG4gIH1cblxuICAvKiogVGhlIHNwb3QgcGlja2VkIGJ5IHRoZSBsYXN0IHBsYWNlbWVudCwgZm9yIHdob2V2ZXIgY29tbWl0cyBpdCB0byBhIFBPSS4gKi9cbiAgZ2V0UGVuZGluZ1RhZ1BsYWNlbWVudCgpOiBUaHJlZUpzVGFnUGxhY2VtZW50IHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMucGVuZGluZ1RhZ1BsYWNlbWVudDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSYXljYXN0cyBhIGNsaWNrIGFnYWluc3QgdGhlIG1vZGVsJ3MgZ2VvbWV0cnkgYW5kIHJldHVybnMgdGhlIGhpdCwgaW4gd29ybGQgc3BhY2UuXG4gICAqXG4gICAqIFJldHVybnMgbnVsbCB3aGVuIHRoZSBjbGljayBtaXNzZWQsIHdoaWNoIGlzIGEgbm9ybWFsIG91dGNvbWUgLSB0aGUgdXNlciBjbGlja2VkIHRoZSBza3kgLSBhbmRcbiAgICogbGVhdmVzIHdoYXRldmVyIG1vZGUgaXMgYXJtZWQgc3RpbGwgYXJtZWQsIHNvIHRoZXkgY2FuIHNpbXBseSB0cnkgYWdhaW4uXG4gICAqL1xuICBwcml2YXRlIHJheWNhc3RNb2RlbChldmVudDogTW91c2VFdmVudCk6IFRIUkVFLkludGVyc2VjdGlvbiB8IG51bGwge1xuICAgIGlmICghdGhpcy5tb2RlbFJvb3QpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IHJlY3QgPSB0aGlzLnJlbmRlcmVyLmRvbUVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgdGhpcy5tb3VzZS54ID0gKChldmVudC5jbGllbnRYIC0gcmVjdC5sZWZ0KSAvIHJlY3Qud2lkdGgpICogMiAtIDE7XG4gICAgdGhpcy5tb3VzZS55ID0gLSgoZXZlbnQuY2xpZW50WSAtIHJlY3QudG9wKSAvIHJlY3QuaGVpZ2h0KSAqIDIgKyAxO1xuICAgIHRoaXMucmF5Y2FzdGVyLnNldEZyb21DYW1lcmEodGhpcy5tb3VzZSwgdGhpcy5jYW1lcmEpO1xuXG4gICAgcmV0dXJuIHRoaXMucmF5Y2FzdGVyLmludGVyc2VjdE9iamVjdCh0aGlzLm1vZGVsUm9vdCwgdHJ1ZSlbMF0gPz8gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBUdXJucyBhIGNsaWNrIGludG8gYSBwb2ludCBvbiB0aGUgbW9kZWwsIGluIHRoZSBmcmFtZSB0YWcgY29vcmRpbmF0ZXMgYXJlIHBlcnNpc3RlZCBpbi5cbiAgICpcbiAgICogVGhhdCBmcmFtZSBpcyB0aGUgdGFnIGdyb3VwJ3MsIG5vdCB0aGUgbW9kZWwncy4gQSBzdG9yZWQgY29vcmRpbmF0ZSBpcyByZWFkIGJhY2sgYXMgYSBzcHJpdGVcbiAgICogcG9zaXRpb24gaW5zaWRlIHRoYXQgZ3JvdXAsIHNvIGEgY29vcmRpbmF0ZSBjYXB0dXJlZCBpbiBhbnkgb3RoZXIgZnJhbWUgY29tZXMgYmFjayBkaXNwbGFjZWQgYnlcbiAgICogZXhhY3RseSBob3dldmVyIGZhciB0aGUgYWxpZ25tZW50IG1vdmVzIHRoZSBncm91cC4gVGhlIHR3byBmcmFtZXMgYWdyZWUgb25seSB3aGlsZSB0aGVcbiAgICogYWxpZ25tZW50IGlzIGlkZW50aXR5LCB3aGljaCBpcyB3aHkgdGhpcyBzdGF5ZWQgaW52aXNpYmxlIHVudGlsIGEgbW9kZWwgd2FzIGNhbGlicmF0ZWQuXG4gICAqL1xuICBwcml2YXRlIHBpY2tQbGFjZW1lbnRPbk1vZGVsKGV2ZW50OiBNb3VzZUV2ZW50KTogVGhyZWVKc1RhZ1BsYWNlbWVudCB8IG51bGwge1xuICAgIGNvbnN0IGhpdCA9IHRoaXMucmF5Y2FzdE1vZGVsKGV2ZW50KTtcbiAgICBpZiAoIWhpdCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgdGhpcy50YWdHcm91cC51cGRhdGVXb3JsZE1hdHJpeCh0cnVlLCBmYWxzZSk7XG4gICAgY29uc3QgcG9zaXRpb24gPSB0aGlzLnRhZ0dyb3VwLndvcmxkVG9Mb2NhbChoaXQucG9pbnQuY2xvbmUoKSk7XG5cbiAgICAvLyBBIGZhY2Ugbm9ybWFsIGlzIGluIHRoZSBnZW9tZXRyeSdzIGxvY2FsIHNwYWNlIGFscmVhZHksIGJ1dCB0aGUgbWVzaCBpdCBiZWxvbmdzIHRvIG1heSBzaXRcbiAgICAvLyB1bmRlciB0cmFuc2Zvcm1zIG9mIGl0cyBvd24sIHNvIHRha2UgaXQgb3V0IHRocm91Z2ggdGhvc2UgYW5kIGJhY2sgaW50byB0aGUgdGFnIGZyYW1lLCBzbyBpdFxuICAgIC8vIGRlc2NyaWJlcyB0aGUgc2FtZSBvcmllbnRhdGlvbiB0aGUgcG9zaXRpb24gZG9lcy5cbiAgICBsZXQgbm9ybWFsID0gbmV3IFRIUkVFLlZlY3RvcjMoMCwgMSwgMCk7XG4gICAgaWYgKGhpdC5mYWNlKSB7XG4gICAgICBub3JtYWwgPSBoaXQuZmFjZS5ub3JtYWxcbiAgICAgICAgLmNsb25lKClcbiAgICAgICAgLnRyYW5zZm9ybURpcmVjdGlvbihoaXQub2JqZWN0Lm1hdHJpeFdvcmxkKVxuICAgICAgICAudHJhbnNmb3JtRGlyZWN0aW9uKG5ldyBUSFJFRS5NYXRyaXg0KCkuY29weSh0aGlzLnRhZ0dyb3VwLm1hdHJpeFdvcmxkKS5pbnZlcnQoKSlcbiAgICAgICAgLm5vcm1hbGl6ZSgpO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBwb3NpdGlvbjogeyB4OiBwb3NpdGlvbi54LCB5OiBwb3NpdGlvbi55LCB6OiBwb3NpdGlvbi56IH0sXG4gICAgICBub3JtYWw6IHsgeDogbm9ybWFsLngsIHk6IG5vcm1hbC55LCB6OiBub3JtYWwueiB9LFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogV2hlcmUgdGhlIHVzZXIgc2F5cyBhIHRhZyByZWFsbHkgYmVsb25ncywgaW4gdGhlIHNwYWNlIHRoZSBzb2x2ZWQgYWxpZ25tZW50IG1hcHMgb250by5cbiAgICpcbiAgICogc2V0VGFnQWxpZ25tZW50IHdyaXRlcyB0aGUgc29sdXRpb24gdG8gdGhlIHRhZyBncm91cCdzIG93biB0cmFuc2Zvcm0sIHdoaWNoIGlzIGV4cHJlc3NlZFxuICAgKiByZWxhdGl2ZSB0byBpdHMgcGFyZW50IC0gc28gdGhlIHRhcmdldCBoYXMgdG8gYmUgaW4gdGhlIHBhcmVudCdzIGZyYW1lLiBNZWFzdXJpbmcgaXQgaW4gdGhlXG4gICAqIGdyb3VwJ3Mgb3duIGZyYW1lIGluc3RlYWQgd291bGQgZm9sZCB0aGUgY3VycmVudCBhbGlnbm1lbnQgaW50byB0aGUgYW5zd2VyLCBtYWtpbmcgZWFjaFxuICAgKiBjYWxpYnJhdGlvbiBkZXBlbmQgb24gdGhlIG9uZSBiZWZvcmUgaXQuXG4gICAqL1xuICBwcml2YXRlIHBpY2tBbGlnbm1lbnRUYXJnZXQoZXZlbnQ6IE1vdXNlRXZlbnQpOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB6OiBudW1iZXIgfSB8IG51bGwge1xuICAgIGNvbnN0IGhpdCA9IHRoaXMucmF5Y2FzdE1vZGVsKGV2ZW50KTtcbiAgICBpZiAoIWhpdCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGNvbnN0IGZyYW1lID0gdGhpcy50YWdHcm91cC5wYXJlbnQgPz8gdGhpcy5zY2VuZTtcbiAgICBmcmFtZS51cGRhdGVXb3JsZE1hdHJpeCh0cnVlLCBmYWxzZSk7XG4gICAgY29uc3QgcG9pbnQgPSBmcmFtZS53b3JsZFRvTG9jYWwoaGl0LnBvaW50LmNsb25lKCkpO1xuICAgIHJldHVybiB7IHg6IHBvaW50LngsIHk6IHBvaW50LnksIHo6IHBvaW50LnogfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSB0cmFuc2Zvcm0gbWFwcGluZyBjYXB0dXJlZCB0YWcgY29vcmRpbmF0ZXMgb250byB0aGlzIG1vZGVsJ3Mgc3BhY2UuXG4gICAqXG4gICAqIEV2ZXJ5IHRhZyBjb21lcyBmcm9tIHRoZSBzYW1lIHNvdXJjZSBzcGFjZSwgc28gdGhpcyBpcyBvbmUgdHJhbnNmb3JtIG9uIHRoZSBncm91cCByYXRoZXIgdGhhblxuICAgKiBwZXItdGFnIG1hdGhzIC0gd2hpY2ggYWxzbyBtZWFucyBpdCBhcHBsaWVzIHJldHJvYWN0aXZlbHkgdG8gdGFncyBhbHJlYWR5IHBsYWNlZCwgYW5kIGNhbiBiZVxuICAgKiBhZGp1c3RlZCBsaXZlIHdoaWxlIHNvbWVvbmUgZGlhbHMgdGhlIG51bWJlcnMgaW4uXG4gICAqXG4gICAqIFBhc3NpbmcgbnVsbCByZXNldHMgdG8gaWRlbnRpdHksIGkuZS4gdGFnIGNvb3JkaW5hdGVzIGFyZSBhbHJlYWR5IGluIG1vZGVsIHNwYWNlLlxuICAgKi9cbiAgc2V0VGFnQWxpZ25tZW50KGFsaWdubWVudDogVGhyZWVKc0FsaWdubWVudCB8IG51bGwpOiB2b2lkIHtcbiAgICBjb25zdCB7IHRyYW5zbGF0aW9uLCByb3RhdGlvbiwgc2NhbGUgfSA9IGFsaWdubWVudCA/PyB7fTtcbiAgICB0aGlzLnRhZ0dyb3VwLnBvc2l0aW9uLnNldCh0cmFuc2xhdGlvbj8ueCA/PyAwLCB0cmFuc2xhdGlvbj8ueSA/PyAwLCB0cmFuc2xhdGlvbj8ueiA/PyAwKTtcbiAgICB0aGlzLnRhZ0dyb3VwLnJvdGF0aW9uLnNldChyb3RhdGlvbj8ueCA/PyAwLCByb3RhdGlvbj8ueSA/PyAwLCByb3RhdGlvbj8ueiA/PyAwKTtcbiAgICB0aGlzLnRhZ0dyb3VwLnNjYWxlLnNldFNjYWxhcihzY2FsZSA/PyAxKTtcblxuICAgIC8vIFRhZyBzaXplIGlzIGRlcml2ZWQgZnJvbSB0aGUgd29ybGQgc2NhbGUgaXQgaW5oZXJpdHMsIHdoaWNoIHRoaXMgY2FsbCBqdXN0IGNoYW5nZWQuXG4gICAgdGhpcy50YWdTcHJpdGVzLmZvckVhY2goKHNwcml0ZSkgPT4gdGhpcy5zY2FsZVRhZ0Zvck1vZGVsKHNwcml0ZSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSB0YWcsIG9yIG1vdmVzL3Jlc3R5bGVzIHRoZSBvbmUgYWxyZWFkeSByZWdpc3RlcmVkIGZvciB0aGlzIGVsZW1lbnQuXG4gICAqXG4gICAqIFVwc2VydCByYXRoZXIgdGhhbiBhIGRlY2xhcmF0aXZlIFwiaGVyZSBpcyB0aGUgZnVsbCBsaXN0XCIgY2FsbCwgYmVjYXVzZSB0YWdzIGFycml2ZSBwZXIgdHlwZVxuICAgKiAoZXF1aXBtZW50LCB0aGVuIHRpY2tldHMsIHRoZW4gbWVhc3VyZW1lbnRzLi4uKSBmcm9tIHNlcGFyYXRlIHN1YnNjcmlwdGlvbnMgLSByZXBsYWNpbmcgdGhlIHNldFxuICAgKiBvbiBlYWNoIG9mIHRob3NlIHdvdWxkIHdpcGUgdGhlIHR5cGVzIHRoYXQgY2FtZSBiZWZvcmUuXG4gICAqL1xuICB1cHNlcnRUYWcodGFnOiBUaHJlZUpzVGFnKTogdm9pZCB7XG4gICAgY29uc3QgZXhpc3RpbmcgPSB0aGlzLnRhZ1Nwcml0ZXMuZ2V0KHRhZy5pZCk7XG4gICAgaWYgKGV4aXN0aW5nKSB7XG4gICAgICBleGlzdGluZy5wb3NpdGlvbi5zZXQodGFnLnBvc2l0aW9uLngsIHRhZy5wb3NpdGlvbi55LCB0YWcucG9zaXRpb24ueik7XG4gICAgICBleGlzdGluZy51c2VyRGF0YS5wb2lUeXBlID0gdGFnLnBvaVR5cGUgPz8gZXhpc3RpbmcudXNlckRhdGEucG9pVHlwZTtcbiAgICAgIGV4aXN0aW5nLnVzZXJEYXRhLmxhYmVsID0gdGFnLmxhYmVsID8/IGV4aXN0aW5nLnVzZXJEYXRhLmxhYmVsO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHNwcml0ZSA9IG5ldyBUSFJFRS5TcHJpdGUoXG4gICAgICBuZXcgVEhSRUUuU3ByaXRlTWF0ZXJpYWwoe1xuICAgICAgICBtYXA6IHRoaXMuY3JlYXRlVGFnVGV4dHVyZSh0YWcuY29sb3IpLFxuICAgICAgICAvLyBUYWdzIHJlYWQgYXMgVUkgcmF0aGVyIHRoYW4gZ2VvbWV0cnksIHNvIHRoZXkgc3RheSB2aXNpYmxlIHRocm91Z2ggdGhlIG1vZGVsIGluc3RlYWQgb2ZcbiAgICAgICAgLy8gYmVpbmcgb2NjbHVkZWQgYnkgd2hhdGV2ZXIgd2FsbCBoYXBwZW5zIHRvIGJlIGJldHdlZW4gdGhlbSBhbmQgdGhlIGNhbWVyYS5cbiAgICAgICAgZGVwdGhUZXN0OiBmYWxzZSxcbiAgICAgICAgdHJhbnNwYXJlbnQ6IHRydWUsXG4gICAgICB9KVxuICAgICk7XG4gICAgc3ByaXRlLm5hbWUgPSB0YWcuaWQ7XG4gICAgc3ByaXRlLnVzZXJEYXRhLnBvaVR5cGUgPSB0YWcucG9pVHlwZSA/PyBudWxsO1xuICAgIHNwcml0ZS51c2VyRGF0YS5sYWJlbCA9IHRhZy5sYWJlbCA/PyBudWxsO1xuICAgIHNwcml0ZS5wb3NpdGlvbi5zZXQodGFnLnBvc2l0aW9uLngsIHRhZy5wb3NpdGlvbi55LCB0YWcucG9zaXRpb24ueik7XG4gICAgc3ByaXRlLnJlbmRlck9yZGVyID0gOTk5O1xuICAgIHRoaXMuc2NhbGVUYWdGb3JNb2RlbChzcHJpdGUpO1xuXG4gICAgdGhpcy50YWdHcm91cC5hZGQoc3ByaXRlKTtcbiAgICB0aGlzLnRhZ1Nwcml0ZXMuc2V0KHRhZy5pZCwgc3ByaXRlKTtcblxuICAgIGlmICh0YWcuaWNvblVybCkge1xuICAgICAgdGhpcy5hcHBseVRhZ0ljb24oc3ByaXRlLCB0YWcuaWNvblVybCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFJlbW92ZXMgdGhlIHRhZyByZWdpc3RlcmVkIGZvciBhbiBlbGVtZW50LCBpZiB0aGVyZSBpcyBvbmUuICovXG4gIHJlbW92ZVRhZyhlbGVtZW50SWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHNwcml0ZSA9IHRoaXMudGFnU3ByaXRlcy5nZXQoZWxlbWVudElkKTtcbiAgICBpZiAoIXNwcml0ZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLnRhZ0dyb3VwLnJlbW92ZShzcHJpdGUpO1xuICAgIHNwcml0ZS5tYXRlcmlhbC5tYXA/LmRpc3Bvc2UoKTtcbiAgICBzcHJpdGUubWF0ZXJpYWwuZGlzcG9zZSgpO1xuICAgIHRoaXMudGFnU3ByaXRlcy5kZWxldGUoZWxlbWVudElkKTtcbiAgfVxuXG4gIC8qKiBSZW1vdmVzIGV2ZXJ5IHRhZyBmcm9tIHRoZSBzY2VuZS4gKi9cbiAgY2xlYXJUYWdzKCk6IHZvaWQge1xuICAgIFsuLi50aGlzLnRhZ1Nwcml0ZXMua2V5cygpXS5mb3JFYWNoKChpZCkgPT4gdGhpcy5yZW1vdmVUYWcoaWQpKTtcbiAgfVxuXG4gIC8qKiBMb2FkcyBhbiBpY29uIGludG8gYW4gZXhpc3RpbmcgdGFnLCBsZWF2aW5nIHRoZSBmYWxsYmFjayBkaXNjIGluIHBsYWNlIGlmIGl0IGZhaWxzLiAqL1xuICBwcml2YXRlIGFwcGx5VGFnSWNvbihzcHJpdGU6IFRIUkVFLlNwcml0ZSwgaWNvblVybDogc3RyaW5nKTogdm9pZCB7XG4gICAgbmV3IFRIUkVFLlRleHR1cmVMb2FkZXIoKS5sb2FkKFxuICAgICAgaWNvblVybCxcbiAgICAgICh0ZXh0dXJlKSA9PiB7XG4gICAgICAgIHNwcml0ZS5tYXRlcmlhbC5tYXA/LmRpc3Bvc2UoKTtcbiAgICAgICAgc3ByaXRlLm1hdGVyaWFsLm1hcCA9IHRleHR1cmU7XG4gICAgICAgIHNwcml0ZS5tYXRlcmlhbC5uZWVkc1VwZGF0ZSA9IHRydWU7XG4gICAgICB9LFxuICAgICAgdW5kZWZpbmVkLFxuICAgICAgKCkgPT4gY29uc29sZS53YXJuKGBUaHJlZUpzU2VydmljZTogY291bGQgbm90IGxvYWQgdGFnIGljb24gXCIke2ljb25Vcmx9XCIsIGtlZXBpbmcgdGhlIGRlZmF1bHQgbWFya2VyLmApXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBEcmF3cyB0aGUgZGVmYXVsdCB0YWcgbWFya2VyOiBhIGZpbGxlZCBkaXNjIHdpdGggYSB3aGl0ZSByaW5nLCBzbyBpdCByZWFkcyBvbiBhbnkgYmFja2dyb3VuZC4gKi9cbiAgcHJpdmF0ZSBjcmVhdGVUYWdUZXh0dXJlKGNvbG9yID0gJyMxRTg4RTUnKTogVEhSRUUuQ2FudmFzVGV4dHVyZSB7XG4gICAgY29uc3Qgc2l6ZSA9IDEyODtcbiAgICBjb25zdCBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICBjYW52YXMud2lkdGggPSBzaXplO1xuICAgIGNhbnZhcy5oZWlnaHQgPSBzaXplO1xuICAgIGNvbnN0IGNvbnRleHQgPSBjYW52YXMuZ2V0Q29udGV4dCgnMmQnKTtcblxuICAgIGNvbnRleHQuYmVnaW5QYXRoKCk7XG4gICAgY29udGV4dC5hcmMoc2l6ZSAvIDIsIHNpemUgLyAyLCBzaXplIC8gMiAtIDgsIDAsIE1hdGguUEkgKiAyKTtcbiAgICBjb250ZXh0LmZpbGxTdHlsZSA9IGNvbG9yO1xuICAgIGNvbnRleHQuZmlsbCgpO1xuICAgIGNvbnRleHQubGluZVdpZHRoID0gODtcbiAgICBjb250ZXh0LnN0cm9rZVN0eWxlID0gJyNGRkZGRkYnO1xuICAgIGNvbnRleHQuc3Ryb2tlKCk7XG5cbiAgICByZXR1cm4gbmV3IFRIUkVFLkNhbnZhc1RleHR1cmUoY2FudmFzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTaXplcyBhIHRhZyByZWxhdGl2ZSB0byB0aGUgbW9kZWwuXG4gICAqXG4gICAqIEEgc3ByaXRlIGluaGVyaXRzIHRoZSBtb2RlbCdzIHNjYWxlIGFuZCB0aGUgYWxpZ25tZW50J3MsIHNvIGEgZml4ZWQgc2l6ZSB3b3VsZCBjb21lIG91dCB0aW55IG9uXG4gICAqIGEgbGFyZ2UgbW9kZWwgYW5kIGh1Z2Ugb24gYSBzbWFsbCBvbmUuIERpdmlkaW5nIGJ5IHRoZSBncm91cCdzIHdvcmxkIHNjYWxlIGtlZXBzIHRhZ3MgdGhlIHNhbWVcbiAgICogc2l6ZSBvbiBzY3JlZW4gd2hhdGV2ZXIgdGhlIG1vZGVsJ3MgdW5pdHMgYXJlLlxuICAgKi9cbiAgcHJpdmF0ZSBzY2FsZVRhZ0Zvck1vZGVsKHNwcml0ZTogVEhSRUUuU3ByaXRlKTogdm9pZCB7XG4gICAgY29uc3Qgd29ybGRTY2FsZSA9IHRoaXMudGFnR3JvdXAuZ2V0V29ybGRTY2FsZShuZXcgVEhSRUUuVmVjdG9yMygpKS54IHx8IDE7XG4gICAgY29uc3Qgc2l6ZSA9IDAuNCAvIHdvcmxkU2NhbGU7XG4gICAgc3ByaXRlLnNjYWxlLnNldChzaXplLCBzaXplLCAxKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIHNlY29uZGFyeSBpbnRlcmFjdGl2ZSBvYmplY3RzIChlLmcuLCBQT0lzLCBzZW5zb3JzKSB0byB0aGUgc2NlbmUuXG4gICAqL1xuICB1cGRhdGVPYmpzVG9TY2VuZShvYmpzQWRkZWQ6IE9iakFkZGVkW10pOiB2b2lkIHtcbiAgICBjb25zdCBleGlzdGluZ0lkcyA9IHRoaXMuY2xpY2thYmxlT2JqZWN0cy5tYXAoKG9iaikgPT4gb2JqLm5hbWUpO1xuICAgIGNvbnN0IG9iakxvYWRlciA9IG5ldyBPQkpMb2FkZXIoKTtcblxuICAgIC8vIGRlbGV0ZSBvYmplY3RzIHRoYXQgYXJlIG5vdCBpbiB0aGUgcHJvdmlkZWQgYXJyYXlcbiAgICB0aGlzLmNsaWNrYWJsZU9iamVjdHMuZm9yRWFjaCgob2JqKSA9PiB7XG4gICAgICBpZiAoIW9ianNBZGRlZC5maW5kKChvYmpEYXRhKSA9PiBvYmpEYXRhLmlkID09PSBvYmoubmFtZSkpIHtcbiAgICAgICAgdGhpcy5zY2VuZS5yZW1vdmUob2JqKTtcbiAgICAgICAgdGhpcy5jbGlja2FibGVPYmplY3RzID0gdGhpcy5jbGlja2FibGVPYmplY3RzLmZpbHRlcihcbiAgICAgICAgICAob2JqZWN0KSA9PiBvYmplY3QubmFtZSAhPT0gb2JqLm5hbWVcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIG9ianNBZGRlZC5mb3JFYWNoKChvYmplY3REYXRhOiBPYmpBZGRlZCkgPT4ge1xuICAgICAgaWYgKCFleGlzdGluZ0lkcy5pbmNsdWRlcyhvYmplY3REYXRhLmlkKSkge1xuICAgICAgICBvYmpMb2FkZXIubG9hZChvYmplY3REYXRhLmZpbGUsIChuZXdPYmplY3QpID0+IHtcbiAgICAgICAgICBuZXdPYmplY3QubmFtZSA9IG9iamVjdERhdGEuaWQ7XG5cbiAgICAgICAgICBuZXdPYmplY3QudHJhdmVyc2UoKGNoaWxkKSA9PiB7XG4gICAgICAgICAgICBpZiAoKGNoaWxkIGFzIFRIUkVFLk1lc2gpLmlzTWVzaCkge1xuICAgICAgICAgICAgICBjb25zdCBtZXNoID0gY2hpbGQgYXMgVEhSRUUuTWVzaDtcbiAgICAgICAgICAgICAgaWYgKG1lc2gubWF0ZXJpYWwgaW5zdGFuY2VvZiBUSFJFRS5NYXRlcmlhbCkge1xuICAgICAgICAgICAgICAgIG1lc2gubWF0ZXJpYWwudHJhbnNwYXJlbnQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIG1lc2gubWF0ZXJpYWwub3BhY2l0eSA9IDAuODtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgLy8gc2V0IG9iamVjdCBwb3NpdGlvbiBhbmQgcm90YXRpb25cbiAgICAgICAgICBuZXdPYmplY3QucG9zaXRpb24uc2V0KFxuICAgICAgICAgICAgb2JqZWN0RGF0YS5wb3NpdGlvbi54LFxuICAgICAgICAgICAgb2JqZWN0RGF0YS5wb3NpdGlvbi55LFxuICAgICAgICAgICAgb2JqZWN0RGF0YS5wb3NpdGlvbi56XG4gICAgICAgICAgKTtcbiAgICAgICAgICBcbiAgICAgICAgICAvLyBUaGUgSlNPTiBjb25maWd1cmF0aW9uIGhhcyByb3RhdGlvbiBhbHJlYWR5IGluIHJhZGlhbnMgKGUuZy4gMy4xNDE1Li4uKVxuICAgICAgICAgIG5ld09iamVjdC5yb3RhdGlvbi5zZXQoXG4gICAgICAgICAgICBvYmplY3REYXRhLnJvdGF0aW9uLngsXG4gICAgICAgICAgICBvYmplY3REYXRhLnJvdGF0aW9uLnksXG4gICAgICAgICAgICBvYmplY3REYXRhLnJvdGF0aW9uLnpcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgbmV3T2JqZWN0LnNjYWxlLnNldChvYmplY3REYXRhLnNjYWxlLngsIG9iamVjdERhdGEuc2NhbGUueSwgb2JqZWN0RGF0YS5zY2FsZS56KTtcblxuICAgICAgICAgIHRoaXMuc2NlbmUuYWRkKG5ld09iamVjdCk7XG4gICAgICAgICAgdGhpcy5jbGlja2FibGVPYmplY3RzLnB1c2gobmV3T2JqZWN0KTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIG9uTW91c2VNb3ZlKGV2ZW50OiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmlzRHJhZ2dpbmdDYW1lcmEpIHtcbiAgICAgIGNvbnN0IHJlY3QgPSB0aGlzLnJlbmRlcmVyLmRvbUVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICB0aGlzLm1vdXNlLnggPSAoKGV2ZW50LmNsaWVudFggLSByZWN0LmxlZnQpIC8gcmVjdC53aWR0aCkgKiAyIC0gMTtcbiAgICAgIHRoaXMubW91c2UueSA9IC0oKGV2ZW50LmNsaWVudFkgLSByZWN0LnRvcCkgLyByZWN0LmhlaWdodCkgKiAyICsgMTtcblxuICAgICAgdGhpcy5yYXljYXN0ZXIuc2V0RnJvbUNhbWVyYSh0aGlzLm1vdXNlLCB0aGlzLmNhbWVyYSk7XG5cbiAgICAgIC8vIFdoaWxlIHRoZSBnaG9zdCBpcyB1cCBpdCBmb2xsb3dzIHRoZSBwb2ludGVyIGFuZCBub3RoaW5nIGVsc2Ugc2hvdWxkIHJlYWN0IHRvIGl0LiBUaGUgbW92ZVxuICAgICAgLy8gaXMgb25seSByZWNvcmRlZCBoZXJlOiBwb2ludGVyIGV2ZW50cyBmaXJlIGZhciBtb3JlIG9mdGVuIHRoYW4gZnJhbWVzLCBhbmQgcGlja2luZyBhIHBvaW50XG4gICAgICAvLyBjb3N0cyBhIHJheWNhc3QgYWdhaW5zdCB0aGUgd2hvbGUgbW9kZWwsIHNvIGRvaW5nIGl0IHBlciBldmVudCBpcyB3aGF0IG1hZGUgdGhlIGdob3N0IHRyYWlsXG4gICAgICAvLyBiZWhpbmQgdGhlIGN1cnNvci4gVGhlIHJlbmRlciBsb29wIHBpY2tzIHVwIHRoZSBsYXRlc3QgcG9zaXRpb24sIG9uY2UsIHBlciBmcmFtZS5cbiAgICAgIGlmICh0aGlzLmlzUHJldmlld2luZ1BsYWNlbWVudCgpKSB7XG4gICAgICAgIHRoaXMucGVuZGluZ1ByZXZpZXdFdmVudCA9IGV2ZW50O1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIC8vIE5vdCBwcmV2aWV3aW5nIGFueSBtb3JlIChhIHBhaXIgd2FzIGp1c3QgY29tcGxldGVkLCBzYXkpOiBkcm9wIGEgZ2hvc3QgbGVmdCBvdmVyIGZyb20gaXQuXG4gICAgICB0aGlzLnJlbW92ZVBsYWNlbWVudFByZXZpZXcoKTtcblxuICAgICAgLy8gVGFncyBmaXJzdDogdGhleSBkcmF3IG9uIHRvcCBvZiB0aGUgbW9kZWwsIHNvIHRoZXkgaGF2ZSB0byBhbnN3ZXIgdGhlIHBvaW50ZXIgZmlyc3QgdG9vLlxuICAgICAgY29uc3QgaG92ZXJlZFRhZyA9IHRoaXMucmF5Y2FzdGVyXG4gICAgICAgIC5pbnRlcnNlY3RPYmplY3RzKHRoaXMudGFnR3JvdXAuY2hpbGRyZW4sIGZhbHNlKVxuICAgICAgICAuZmluZCgoaGl0KSA9PiBoaXQub2JqZWN0LnZpc2libGUpPy5vYmplY3Q7XG4gICAgICBpZiAoaG92ZXJlZFRhZykge1xuICAgICAgICB0aGlzLnJlbmRlcmVyLmRvbUVsZW1lbnQuc3R5bGUuY3Vyc29yID0gJ3BvaW50ZXInO1xuICAgICAgICB0aGlzLm9iamVjdEhvdmVyZWQkLm5leHQobnVsbCk7XG4gICAgICAgIGNvbnN0IGxhYmVsID0gaG92ZXJlZFRhZy51c2VyRGF0YS5sYWJlbDtcbiAgICAgICAgdGhpcy50YWdIb3ZlcmVkJC5uZXh0KGxhYmVsID8geyBsYWJlbCwgZXZlbnQgfSA6IG51bGwpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLnRhZ0hvdmVyZWQkLm5leHQobnVsbCk7XG5cbiAgICAgIGNvbnN0IGludGVyc2VjdHMgPSB0aGlzLnJheWNhc3Rlci5pbnRlcnNlY3RPYmplY3RzKHRoaXMuY2xpY2thYmxlT2JqZWN0cywgdHJ1ZSk7XG5cbiAgICAgIGlmIChpbnRlcnNlY3RzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgdGhpcy5yZW5kZXJlci5kb21FbGVtZW50LnN0eWxlLmN1cnNvciA9ICdwb2ludGVyJztcbiAgICAgICAgY29uc3QgaW50ZXJzZWN0ZWRPYmplY3RJZCA9IGludGVyc2VjdHNbMF0ub2JqZWN0Py5wYXJlbnQ/Lm5hbWU7XG4gICAgICAgIGlmIChpbnRlcnNlY3RlZE9iamVjdElkKSB7XG4gICAgICAgICAgICB0aGlzLm9iamVjdEhvdmVyZWQkLm5leHQoeyBpZDogaW50ZXJzZWN0ZWRPYmplY3RJZCwgZXZlbnQgfSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMucmVuZGVyZXIuZG9tRWxlbWVudC5zdHlsZS5jdXJzb3IgPSAnZGVmYXVsdCc7XG4gICAgICAgIHRoaXMub2JqZWN0SG92ZXJlZCQubmV4dChudWxsKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yZW5kZXJlci5kb21FbGVtZW50LnN0eWxlLmN1cnNvciA9ICdncmFiYmluZyc7XG4gICAgICB0aGlzLm9iamVjdEhvdmVyZWQkLm5leHQobnVsbCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBvbk1vdXNlQ2xpY2soZXZlbnQ6IE1vdXNlRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNEcmFnZ2luZ0NhbWVyYSkge1xuICAgICAgY29uc3QgcmVjdCA9IHRoaXMucmVuZGVyZXIuZG9tRWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgIHRoaXMubW91c2UueCA9ICgoZXZlbnQuY2xpZW50WCAtIHJlY3QubGVmdCkgLyByZWN0LndpZHRoKSAqIDIgLSAxO1xuICAgICAgdGhpcy5tb3VzZS55ID0gLSgoZXZlbnQuY2xpZW50WSAtIHJlY3QudG9wKSAvIHJlY3QuaGVpZ2h0KSAqIDIgKyAxO1xuXG4gICAgICAvLyBBbGlnbm1lbnQgYW5kIHBsYWNlbWVudCBib3RoIHRha2UgdGhlIGNsaWNrIGJlZm9yZSBhbnl0aGluZyBjYW4gYmUgb3BlbmVkIGJ5IGl0LlxuICAgICAgaWYgKHRoaXMuYWxpZ25pbmcpIHtcbiAgICAgICAgdGhpcy5yYXljYXN0ZXIuc2V0RnJvbUNhbWVyYSh0aGlzLm1vdXNlLCB0aGlzLmNhbWVyYSk7XG4gICAgICAgIHRoaXMuaGFuZGxlQWxpZ25tZW50Q2xpY2soZXZlbnQpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIC8vIFBsYWNlbWVudCB3aW5zIG92ZXIgZXZlcnl0aGluZzogd2hpbGUgYXJtZWQsIGEgY2xpY2sgbWVhbnMgXCJwdXQgaXQgaGVyZVwiLCBub3QgXCJvcGVuIHRoYXRcIi5cbiAgICAgIGlmICh0aGlzLnBsYWNpbmdUYWcpIHtcbiAgICAgICAgY29uc3QgcGxhY2VtZW50ID0gdGhpcy5waWNrUGxhY2VtZW50T25Nb2RlbChldmVudCk7XG4gICAgICAgIGlmIChwbGFjZW1lbnQpIHtcbiAgICAgICAgICBwbGFjZW1lbnQucG9pVHlwZSA9IHRoaXMucGxhY2VtZW50UG9pVHlwZTtcbiAgICAgICAgICB0aGlzLnBlbmRpbmdUYWdQbGFjZW1lbnQgPSBwbGFjZW1lbnQ7XG4gICAgICAgICAgdGhpcy5wbGFjaW5nVGFnID0gZmFsc2U7XG4gICAgICAgICAgdGhpcy5yZW1vdmVQbGFjZW1lbnRQcmV2aWV3KCk7XG4gICAgICAgICAgdGhpcy5yZW5kZXJlci5kb21FbGVtZW50LnN0eWxlLmN1cnNvciA9ICdkZWZhdWx0JztcbiAgICAgICAgICB0aGlzLnRhZ1BsYWNlZCQubmV4dChwbGFjZW1lbnQpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdGhpcy5yYXljYXN0ZXIuc2V0RnJvbUNhbWVyYSh0aGlzLm1vdXNlLCB0aGlzLmNhbWVyYSk7XG5cbiAgICAgIC8vIFNhbWUgcHJlY2VkZW5jZSBhcyBob3ZlcjogYSB0YWcgZHJhd24gb3ZlciB0aGUgbW9kZWwgY2xhaW1zIHRoZSBjbGljay5cbiAgICAgIGNvbnN0IHRhZ0hpdHMgPSB0aGlzLnJheWNhc3Rlci5pbnRlcnNlY3RPYmplY3RzKHRoaXMudGFnR3JvdXAuY2hpbGRyZW4sIGZhbHNlKVxuICAgICAgICAuZmlsdGVyKChoaXQpID0+IGhpdC5vYmplY3QudmlzaWJsZSk7XG4gICAgICBpZiAodGFnSGl0cy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IHRhZyA9IHRhZ0hpdHNbMF0ub2JqZWN0O1xuICAgICAgICB0aGlzLnRhZ0NsaWNrZWQkLm5leHQoeyBlbGVtZW50SWQ6IHRhZy5uYW1lLCBwb2lUeXBlOiB0YWcudXNlckRhdGEucG9pVHlwZSA/PyBudWxsIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGludGVyc2VjdHMgPSB0aGlzLnJheWNhc3Rlci5pbnRlcnNlY3RPYmplY3RzKHRoaXMuY2xpY2thYmxlT2JqZWN0cywgdHJ1ZSk7XG5cbiAgICAgIGlmIChpbnRlcnNlY3RzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3QgY2xpY2tlZE9iamVjdCA9IGludGVyc2VjdHNbMF0ub2JqZWN0O1xuICAgICAgICBjb25zdCBpZCA9IGNsaWNrZWRPYmplY3Q/LnBhcmVudD8ubmFtZTtcbiAgICAgICAgaWYgKGlkKSB7XG4gICAgICAgICAgICB0aGlzLm9iamVjdENsaWNrZWQkLm5leHQoaWQpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLm9iamVjdENsaWNrZWQkLm5leHQobnVsbCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEVzY2FwZSBjYW5jZWxzIHdoYXRldmVyIHRoZSBwb2ludGVyIGlzIGN1cnJlbnRseSBjb21taXR0ZWQgdG8uXG4gICAqXG4gICAqIEVtaXRzIHRocm91Z2ggdGhlIHNhbWUgc3ViamVjdHMgdGhlIGV4cGxpY2l0IENhbmNlbCBidXR0b25zIHVzZSwgc28gdGhlIFVJIGRyaXZpbmcgdGhlbSBmb2xsb3dzXG4gICAqIGFsb25nIGluc3RlYWQgb2YgYmVpbmcgbGVmdCBzaG93aW5nIGEgbW9kZSB0aGF0IGlzIG5vIGxvbmdlciBhcm1lZC5cbiAgICovXG4gIHByaXZhdGUgb25LZXlEb3duID0gKGV2ZW50OiBLZXlib2FyZEV2ZW50KTogdm9pZCA9PiB7XG4gICAgaWYgKGV2ZW50LmtleSAhPT0gJ0VzY2FwZScpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHRoaXMucGxhY2luZ1RhZykge1xuICAgICAgLy8gTm90aGluZyBvdXRzaWRlIHRoZSBzY2VuZSB0cmFja3MgcGxhY2VtZW50LCBzbyBjbGVhcmluZyBpdCBoZXJlIGlzIHRoZSB3aG9sZSBvZiBpdC5cbiAgICAgIHRoaXMuY2FuY2VsVGFnUGxhY2VtZW50KCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICh0aGlzLmFsaWduaW5nKSB7XG4gICAgICB0aGlzLmNhbmNlbFRhZ0FsaWdubWVudCgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYW5pbWF0ZSA9ICgpOiB2b2lkID0+IHtcbiAgICB0aGlzLmFuaW1hdGlvbkZyYW1lSWQgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUodGhpcy5hbmltYXRlKTtcbiAgICB0aGlzLnN0ZXBDYW1lcmFUd2VlbigpO1xuICAgIHRoaXMuY29udHJvbHMudXBkYXRlKCk7XG5cbiAgICAvLyBPbmUgcGljayBwZXIgZnJhbWUsIGFnYWluc3QgdGhlIHBvaW50ZXIncyBsYXRlc3QgcG9zaXRpb24gLSBzZWUgb25Nb3VzZU1vdmUuXG4gICAgaWYgKHRoaXMucGVuZGluZ1ByZXZpZXdFdmVudCkge1xuICAgICAgY29uc3QgZXZlbnQgPSB0aGlzLnBlbmRpbmdQcmV2aWV3RXZlbnQ7XG4gICAgICB0aGlzLnBlbmRpbmdQcmV2aWV3RXZlbnQgPSBudWxsO1xuICAgICAgdGhpcy51cGRhdGVQbGFjZW1lbnRQcmV2aWV3KGV2ZW50KTtcbiAgICB9XG5cbiAgICB0aGlzLnJlbmRlcmVyLnJlbmRlcih0aGlzLnNjZW5lLCB0aGlzLmNhbWVyYSk7XG4gIH1cblxuICBkZXN0cm95KCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmFuaW1hdGlvbkZyYW1lSWQpIHtcbiAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMuYW5pbWF0aW9uRnJhbWVJZCk7XG4gICAgfVxuICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdrZXlkb3duJywgdGhpcy5vbktleURvd24pO1xuICAgIGlmICh0aGlzLmNhbnZhc1Jlc2l6ZU9ic2VydmVyKSB7XG4gICAgICB0aGlzLmNhbnZhc1Jlc2l6ZU9ic2VydmVyLmRpc2Nvbm5lY3QoKTtcbiAgICAgIHRoaXMuY2FudmFzUmVzaXplT2JzZXJ2ZXIgPSB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHRoaXMuZGlzY2FyZEN1cnJlbnRMb2FkKCk7XG4gICAgdGhpcy5jbGVhclRhZ3MoKTtcbiAgICBpZiAodGhpcy5yZW5kZXJlcikge1xuICAgICAgdGhpcy5yZW5kZXJlci5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmNvbnRyb2xzKSB7XG4gICAgICB0aGlzLmNvbnRyb2xzLmRpc3Bvc2UoKTtcbiAgICB9XG4gIH1cbn1cbiJdfQ==
|