@jax-data-science/sci-image-visualizer 0.4.0 → 0.6.0
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 +109 -0
- package/esm2022/index.mjs +2 -1
- package/esm2022/lib/contracts/display-types.mjs +1 -1
- package/esm2022/lib/contracts/plot-type-contribution.contract.mjs +66 -0
- package/esm2022/lib/contracts/plot-type.mjs +6 -1
- package/esm2022/lib/contracts/toolbar-tool.contract.mjs +46 -4
- package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +2 -3
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +70 -7
- package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +8 -1
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +2 -3
- package/esm2022/lib/plot-mode/plot-mode-controller.mjs +329 -0
- package/esm2022/lib/routing-visualizer.service.mjs +6 -1
- package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +44 -3
- package/esm2022/lib/toolbar/toolbar.component.mjs +50 -21
- package/esm2022/lib/visualizer.component.mjs +479 -43
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +1092 -75
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/index.d.ts +1 -0
- package/lib/contracts/display-types.d.ts +10 -0
- package/lib/contracts/plot-type-contribution.contract.d.ts +199 -0
- package/lib/contracts/plot-type.d.ts +10 -0
- package/lib/contracts/toolbar-tool.contract.d.ts +84 -5
- package/lib/contracts/visualizer.contract.d.ts +5 -0
- package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +21 -0
- package/lib/implementations/osd/osd-coordinate-transform.d.ts +6 -0
- package/lib/plot-mode/plot-mode-controller.d.ts +130 -0
- package/lib/routing-visualizer.service.d.ts +4 -0
- package/lib/toolbar/brush/brush-tool.service.d.ts +21 -0
- package/lib/toolbar/toolbar.component.d.ts +28 -8
- package/lib/visualizer.component.d.ts +121 -8
- package/package.json +1 -1
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { PlotType, isBuiltinPlotType } from '../contracts/plot-type';
|
|
2
|
+
/** Base types a contributed mode can ride on in this version. */
|
|
3
|
+
export const SUPPORTED_CONTRIBUTION_BASE_TYPES = [PlotType.IMAGE];
|
|
4
|
+
/** How many animation frames to wait for the viewport before giving up. */
|
|
5
|
+
const MAX_READY_FRAMES = 180;
|
|
6
|
+
const TAG = '[visualizer] plot-type contribution';
|
|
7
|
+
/**
|
|
8
|
+
* Validate what arrived on `PLOT_TYPE_CONTRIBUTIONS`. A malformed or clashing
|
|
9
|
+
* contribution is dropped with a warning rather than breaking the selector:
|
|
10
|
+
* - a nested array (`useValue: [a, b], multi: true`) is flattened one level;
|
|
11
|
+
* - `type` must be a non-empty string that is not a built-in `PlotType` value;
|
|
12
|
+
* - the first contribution with a given `type` wins;
|
|
13
|
+
* - `baseType` must be one of {@link SUPPORTED_CONTRIBUTION_BASE_TYPES};
|
|
14
|
+
* - `activate` must be a function.
|
|
15
|
+
*/
|
|
16
|
+
export function normalizeContributions(raw, log = console) {
|
|
17
|
+
const out = [];
|
|
18
|
+
const seen = new Set();
|
|
19
|
+
const flat = (raw ?? []).flatMap((c) => (Array.isArray(c) ? c : [c]));
|
|
20
|
+
for (const c of flat) {
|
|
21
|
+
const d = c?.descriptor;
|
|
22
|
+
const id = d?.type;
|
|
23
|
+
if (!d || typeof id !== 'string' || !id || typeof c.activate !== 'function') {
|
|
24
|
+
log.warn(`${TAG} ignored: it needs a descriptor with a string \`type\` and an \`activate\` function.`, c);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (isBuiltinPlotType(id)) {
|
|
28
|
+
log.warn(`${TAG} '${id}' ignored: the id clashes with a built-in plot type.`);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (seen.has(id)) {
|
|
32
|
+
log.warn(`${TAG} '${id}' ignored: another contribution already uses that id.`);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (!SUPPORTED_CONTRIBUTION_BASE_TYPES.includes(d.baseType)) {
|
|
36
|
+
log.warn(`${TAG} '${id}' ignored: baseType '${String(d.baseType)}' is not supported `
|
|
37
|
+
+ `(supported: ${SUPPORTED_CONTRIBUTION_BASE_TYPES.join(', ')}).`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
seen.add(id);
|
|
41
|
+
out.push(c);
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
export class PlotModeController {
|
|
46
|
+
constructor(raw, hooks, log = console) {
|
|
47
|
+
this.hooks = hooks;
|
|
48
|
+
this.log = log;
|
|
49
|
+
this.active = null;
|
|
50
|
+
this.teardown = null;
|
|
51
|
+
/** Bumped by every activate/deactivate, so a pending async activation can
|
|
52
|
+
* tell it has been superseded. */
|
|
53
|
+
this.generation = 0;
|
|
54
|
+
this.readyFrame = null;
|
|
55
|
+
/** Contributions whose last cleanup (panel teardown / `deactivate()`) threw or
|
|
56
|
+
* rejected. Their next automatic re-activation (base re-render, image switch)
|
|
57
|
+
* falls back to the base type instead; an explicit selection clears the mark. */
|
|
58
|
+
this.failedCleanup = new Map();
|
|
59
|
+
this.readyCb = null;
|
|
60
|
+
this.pendingId = null;
|
|
61
|
+
this.contributions = normalizeContributions(raw, log);
|
|
62
|
+
this.byId = new Map(this.contributions.map((c) => [c.descriptor.type, c]));
|
|
63
|
+
}
|
|
64
|
+
/** Descriptors of the usable contributions, in registration order. */
|
|
65
|
+
descriptors() {
|
|
66
|
+
return this.contributions.map((c) => c.descriptor);
|
|
67
|
+
}
|
|
68
|
+
/** The contribution for a selector id, if it is a contributed mode. */
|
|
69
|
+
find(id) {
|
|
70
|
+
return typeof id === 'string' ? this.byId.get(id) : undefined;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The built-in type that renders `id`: a built-in type is itself, a
|
|
74
|
+
* contributed id is its `baseType`, and an id that is neither (a stale
|
|
75
|
+
* contributed id whose provider is gone) resolves to `PlotType.IMAGE`, the
|
|
76
|
+
* default view.
|
|
77
|
+
*/
|
|
78
|
+
baseTypeOf(id) {
|
|
79
|
+
if (isBuiltinPlotType(id))
|
|
80
|
+
return id;
|
|
81
|
+
return this.find(id)?.descriptor.baseType ?? PlotType.IMAGE;
|
|
82
|
+
}
|
|
83
|
+
/** The live session, if any. */
|
|
84
|
+
get current() {
|
|
85
|
+
return this.active;
|
|
86
|
+
}
|
|
87
|
+
/** Whether an activation is waiting on the contribution or the viewport. */
|
|
88
|
+
get pending() {
|
|
89
|
+
return this.pendingId !== null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Start a session for `contribution`. Ends any live one first. Resolves when
|
|
93
|
+
* the attempt has finished (successfully or not); it never rejects.
|
|
94
|
+
*/
|
|
95
|
+
activate(contribution, ctx) {
|
|
96
|
+
this.deactivate();
|
|
97
|
+
const id = contribution.descriptor.type;
|
|
98
|
+
if (this.failedCleanup.has(id)) {
|
|
99
|
+
// Its previous session did not end cleanly; do not stack a new one on top.
|
|
100
|
+
const err = this.failedCleanup.get(id);
|
|
101
|
+
this.failedCleanup.delete(id);
|
|
102
|
+
this.fail(contribution, err);
|
|
103
|
+
return Promise.resolve();
|
|
104
|
+
}
|
|
105
|
+
const gen = ++this.generation;
|
|
106
|
+
this.pendingId = id;
|
|
107
|
+
return new Promise((resolve) => {
|
|
108
|
+
this.whenReady(ctx, gen, (ready) => {
|
|
109
|
+
if (gen !== this.generation)
|
|
110
|
+
return resolve();
|
|
111
|
+
if (!ready) {
|
|
112
|
+
this.fail(contribution, new Error('the viewport never became ready'));
|
|
113
|
+
return resolve();
|
|
114
|
+
}
|
|
115
|
+
let result;
|
|
116
|
+
try {
|
|
117
|
+
result = contribution.activate(ctx);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
this.fail(contribution, err);
|
|
121
|
+
return resolve();
|
|
122
|
+
}
|
|
123
|
+
if (isThenable(result)) {
|
|
124
|
+
result.then((session) => { this.start(contribution, ctx, session, gen); resolve(); }, (err) => {
|
|
125
|
+
if (gen === this.generation)
|
|
126
|
+
this.fail(contribution, err);
|
|
127
|
+
resolve();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
this.start(contribution, ctx, result, gen);
|
|
132
|
+
resolve();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* End the live session, if any: the component panel is dropped (via
|
|
139
|
+
* `onDeactivating`), a `mount` panel's teardown runs, then
|
|
140
|
+
* `session.deactivate()`, then the mount host is removed. Each step is
|
|
141
|
+
* isolated. A pending activation is cancelled; if it resolves later its
|
|
142
|
+
* session is deactivated on arrival. Idempotent.
|
|
143
|
+
*/
|
|
144
|
+
deactivate() {
|
|
145
|
+
this.generation++;
|
|
146
|
+
this.pendingId = null;
|
|
147
|
+
this.cancelReadyWait();
|
|
148
|
+
const a = this.active;
|
|
149
|
+
if (!a)
|
|
150
|
+
return;
|
|
151
|
+
this.active = null;
|
|
152
|
+
const teardown = this.teardown;
|
|
153
|
+
this.teardown = null;
|
|
154
|
+
const id = a.contribution.descriptor.type;
|
|
155
|
+
this.guard(`'${id}' panel removal`, () => this.hooks.onDeactivating(a));
|
|
156
|
+
if (teardown)
|
|
157
|
+
this.cleanup(a.contribution, 'panel teardown', teardown);
|
|
158
|
+
this.cleanup(a.contribution, 'deactivate()', () => a.session.deactivate());
|
|
159
|
+
a.panelHost?.remove();
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Forget recorded cleanup failures. The host calls this when the USER picks a
|
|
163
|
+
* plot type: an explicit choice gets a fresh attempt, only automatic
|
|
164
|
+
* re-activations fall back.
|
|
165
|
+
*/
|
|
166
|
+
clearCleanupFailures() {
|
|
167
|
+
this.failedCleanup.clear();
|
|
168
|
+
}
|
|
169
|
+
// ── internals ──────────────────────────────────────────────────────────
|
|
170
|
+
start(contribution, ctx, session, gen) {
|
|
171
|
+
const id = contribution.descriptor.type;
|
|
172
|
+
if (!session || typeof session.deactivate !== 'function') {
|
|
173
|
+
if (gen === this.generation) {
|
|
174
|
+
this.fail(contribution, new Error('activate() did not return a PlotModeSession'));
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (gen !== this.generation) {
|
|
179
|
+
// Superseded while activate() was in flight — the user already left. The
|
|
180
|
+
// session still gets its single deactivate(), just late.
|
|
181
|
+
this.guardLogged(`'${id}' deactivate() of a superseded session`, () => session.deactivate());
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.pendingId = null;
|
|
185
|
+
const panel = contribution.panel;
|
|
186
|
+
let panelHost = null;
|
|
187
|
+
let teardown = null;
|
|
188
|
+
if (panel && 'mount' in panel && typeof panel.mount === 'function') {
|
|
189
|
+
panelHost = document.createElement('div');
|
|
190
|
+
panelHost.className = 'plot-mode-panel-host';
|
|
191
|
+
try {
|
|
192
|
+
const t = panel.mount(panelHost, ctx, session);
|
|
193
|
+
teardown = typeof t === 'function' ? t : null;
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
panelHost.remove();
|
|
197
|
+
this.guardLogged(`'${id}' deactivate()`, () => session.deactivate());
|
|
198
|
+
this.fail(contribution, err);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
this.active = { contribution, ctx, session, panelHost };
|
|
203
|
+
this.teardown = teardown;
|
|
204
|
+
try {
|
|
205
|
+
this.hooks.onActivated(this.active);
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
// The host could not show it (e.g. its panel failed to render): not a usable mode.
|
|
209
|
+
this.failActive(err);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* The host could not make the live mode usable (its panel threw while being
|
|
214
|
+
* created or first rendered): end the session once, then fall back to its base.
|
|
215
|
+
* No-op when nothing is live.
|
|
216
|
+
*/
|
|
217
|
+
failActive(err) {
|
|
218
|
+
const a = this.active;
|
|
219
|
+
if (!a)
|
|
220
|
+
return;
|
|
221
|
+
this.deactivate();
|
|
222
|
+
this.fail(a.contribution, err);
|
|
223
|
+
}
|
|
224
|
+
fail(contribution, err) {
|
|
225
|
+
this.pendingId = null;
|
|
226
|
+
this.log.error(`${TAG} '${contribution.descriptor.type}' failed to activate — `
|
|
227
|
+
+ `falling back to '${contribution.descriptor.baseType}'.`, err);
|
|
228
|
+
try {
|
|
229
|
+
this.hooks.onFailed(contribution, err);
|
|
230
|
+
}
|
|
231
|
+
catch (hookErr) {
|
|
232
|
+
this.log.error(`${TAG} fallback failed.`, hookErr);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
guard(what, fn) {
|
|
236
|
+
try {
|
|
237
|
+
fn();
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
this.log.error(`${TAG} ${what} threw.`, err);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Like `guard`, for a contribution callback typed `void` that may still return a
|
|
244
|
+
* promise: a rejection is logged instead of escaping as an unhandled rejection. */
|
|
245
|
+
guardLogged(what, fn) {
|
|
246
|
+
try {
|
|
247
|
+
const r = fn();
|
|
248
|
+
if (isThenable(r))
|
|
249
|
+
r.then(undefined, (err) => this.log.error(`${TAG} ${what} rejected.`, err));
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
this.log.error(`${TAG} ${what} threw.`, err);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/** Run a live session's cleanup; a throw OR a rejection goes to `cleanupFailed`. */
|
|
256
|
+
cleanup(contribution, what, fn) {
|
|
257
|
+
try {
|
|
258
|
+
const r = fn();
|
|
259
|
+
if (isThenable(r))
|
|
260
|
+
r.then(undefined, (err) => this.cleanupFailed(contribution, what, err));
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
this.cleanupFailed(contribution, what, err);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* A contribution's cleanup failed. If that same contribution is live or starting
|
|
268
|
+
* again (a base re-render / image switch re-activated it — or, for an async
|
|
269
|
+
* rejection, already has), end it and fall back to its base type. Otherwise the
|
|
270
|
+
* user has moved on: remember it, so the next automatic re-activation falls back.
|
|
271
|
+
*/
|
|
272
|
+
cleanupFailed(contribution, what, err) {
|
|
273
|
+
const id = contribution.descriptor.type;
|
|
274
|
+
this.log.error(`${TAG} '${id}' ${what} failed.`, err);
|
|
275
|
+
const again = this.active?.contribution === contribution || this.pendingId === id;
|
|
276
|
+
if (again) {
|
|
277
|
+
this.deactivate();
|
|
278
|
+
this.fail(contribution, err);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
this.failedCleanup.set(id, err);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/** Call `cb(true)` once the viewport reports ready (now, or on a later
|
|
285
|
+
* animation frame), or `cb(false)` after {@link MAX_READY_FRAMES}. */
|
|
286
|
+
whenReady(ctx, gen, cb) {
|
|
287
|
+
const isReady = () => {
|
|
288
|
+
try {
|
|
289
|
+
return ctx.viewport.isReady();
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
if (isReady())
|
|
296
|
+
return cb(true);
|
|
297
|
+
let frames = 0;
|
|
298
|
+
const settle = (ready) => {
|
|
299
|
+
this.readyFrame = null;
|
|
300
|
+
this.readyCb = null;
|
|
301
|
+
cb(ready);
|
|
302
|
+
};
|
|
303
|
+
const tick = () => {
|
|
304
|
+
this.readyFrame = null;
|
|
305
|
+
if (gen !== this.generation)
|
|
306
|
+
return settle(false);
|
|
307
|
+
if (isReady())
|
|
308
|
+
return settle(true);
|
|
309
|
+
if (++frames >= MAX_READY_FRAMES)
|
|
310
|
+
return settle(false);
|
|
311
|
+
this.readyFrame = requestAnimationFrame(tick);
|
|
312
|
+
};
|
|
313
|
+
this.readyCb = settle;
|
|
314
|
+
this.readyFrame = requestAnimationFrame(tick);
|
|
315
|
+
}
|
|
316
|
+
/** Stop waiting for the viewport; the waiting activation resolves as superseded. */
|
|
317
|
+
cancelReadyWait() {
|
|
318
|
+
if (this.readyFrame !== null)
|
|
319
|
+
cancelAnimationFrame(this.readyFrame);
|
|
320
|
+
this.readyFrame = null;
|
|
321
|
+
const cb = this.readyCb;
|
|
322
|
+
this.readyCb = null;
|
|
323
|
+
cb?.(false);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function isThenable(v) {
|
|
327
|
+
return !!v && typeof v.then === 'function';
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGxvdC1tb2RlLWNvbnRyb2xsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Bsb3QtbW9kZS9wbG90LW1vZGUtY29udHJvbGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFjLGlCQUFpQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFpRGpGLGlFQUFpRTtBQUNqRSxNQUFNLENBQUMsTUFBTSxpQ0FBaUMsR0FBd0IsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFdkYsMkVBQTJFO0FBQzNFLE1BQU0sZ0JBQWdCLEdBQUcsR0FBRyxDQUFDO0FBRTdCLE1BQU0sR0FBRyxHQUFHLHFDQUFxQyxDQUFDO0FBRWxEOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxVQUFVLHNCQUFzQixDQUNwQyxHQUEwQyxFQUMxQyxNQUFzQixPQUFPO0lBRTdCLE1BQU0sR0FBRyxHQUEyQixFQUFFLENBQUM7SUFDdkMsTUFBTSxJQUFJLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztJQUMvQixNQUFNLElBQUksR0FBRyxDQUFDLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN0RSxLQUFLLE1BQU0sQ0FBQyxJQUFJLElBQThCLEVBQUUsQ0FBQztRQUMvQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsVUFBdUQsQ0FBQztRQUNyRSxNQUFNLEVBQUUsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDO1FBQ25CLElBQUksQ0FBQyxDQUFDLElBQUksT0FBTyxFQUFFLEtBQUssUUFBUSxJQUFJLENBQUMsRUFBRSxJQUFJLE9BQU8sQ0FBQyxDQUFDLFFB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UUFDeEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7UUFDcEIsRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDZCxDQUFDO0NBQ0Y7QUFFRCxTQUFTLFVBQVUsQ0FBSSxDQUEyQjtJQUNoRCxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksT0FBUSxDQUFnQixDQUFDLElBQUksS0FBSyxVQUFVLENBQUM7QUFDN0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFBsb3RUeXBlLCBQbG90VHlwZUlkLCBpc0J1aWx0aW5QbG90VHlwZSB9IGZyb20gJy4uL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuaW1wb3J0IHtcbiAgQ29udHJpYnV0ZWRQbG90VHlwZURlc2NyaXB0b3IsXG4gIFBsb3RNb2RlQ29udGV4dCxcbiAgUGxvdE1vZGVTZXNzaW9uLFxuICBQbG90VHlwZUNvbnRyaWJ1dGlvbixcbn0gZnJvbSAnLi4vY29udHJhY3RzL3Bsb3QtdHlwZS1jb250cmlidXRpb24uY29udHJhY3QnO1xuXG4vKipcbiAqIExpZmVjeWNsZSBvZiB0aGUgY29udHJpYnV0ZWQgcGxvdCBtb2RlcyAoe0BsaW5rIFBsb3RUeXBlQ29udHJpYnV0aW9ufSkgZm9yIG9uZVxuICogdmlzdWFsaXplciBpbnN0YW5jZS4gUGFja2FnZS1pbnRlcm5hbDogdGhlIHZpc3VhbGl6ZXIgY29tcG9uZW50IG93bnMgb25lIGFuZFxuICogZHJpdmVzIGl0IGZyb20gaXRzIHJlbmRlciBwaXBlbGluZS5cbiAqXG4gKiBPd25zIHRocmVlIHRoaW5ncyB0aGUgY29tcG9uZW50IHNob3VsZCBub3QgaGF2ZSB0byByZWFzb24gYWJvdXQ6XG4gKiAgLSB3aGljaCBjb250cmlidXRpb25zIGFyZSB1c2FibGUgKHZhbGlkYXRlZCBhbmQgZGUtZHVwbGljYXRlZCBvbmNlKTtcbiAqICAtIHRoZSBzaW5nbGUgbGl2ZSBzZXNzaW9uLCBzbyBgZGVhY3RpdmF0ZSgpYCBydW5zIGV4YWN0bHkgb25jZSBwZXIgc2Vzc2lvblxuICogICAgaG93ZXZlciBtYW55IGV4aXRzIHJhY2UgZWFjaCBvdGhlciAodHlwZSBjaGFuZ2UsIGltYWdlIGNoYW5nZSwgZGVzdHJveSxcbiAqICAgIGEgc3VwZXJzZWRlZCBhc3luYyBhY3RpdmF0aW9uKTtcbiAqICAtIGlzb2xhdGlvbjogbm90aGluZyBhIGNvbnRyaWJ1dGlvbiB0aHJvd3Mgb3IgcmVqZWN0cyBlc2NhcGVzIHRoaXMgY2xhc3MuXG4gKiAgICBBIGZhaWx1cmUgaXMgbG9nZ2VkIGFuZCByZXBvcnRlZCB0aHJvdWdoIGBvbkZhaWxlZGAsIGFuZCB0aGUgY29tcG9uZW50XG4gKiAgICBmYWxscyBiYWNrIHRvIHRoZSBtb2RlJ3MgYmFzZSB0eXBlLlxuICovXG5cbi8qKiBUaGUgbGl2ZSBhY3RpdmF0aW9uIG9mIGEgY29udHJpYnV0ZWQgbW9kZSwgYXMgdGhlIGNvbXBvbmVudCByZW5kZXJzIGl0LiAqL1xuZXhwb3J0IGludGVyZmFjZSBBY3RpdmVQbG90TW9kZSB7XG4gIHJlYWRvbmx5IGNvbnRyaWJ1dGlvbjogUGxvdFR5cGVDb250cmlidXRpb247XG4gIHJlYWRvbmx5IGN0eDogUGxvdE1vZGVDb250ZXh0O1xuICByZWFkb25seSBzZXNzaW9uOiBQbG90TW9kZVNlc3Npb247XG4gIC8qKiBIb3N0IGVsZW1lbnQgaGFuZGVkIHRvIGEgYG1vdW50YCBwYW5lbCwgb3IgbnVsbCBmb3IgYSBjb21wb25lbnQgLyBubyBwYW5lbC4gKi9cbiAgcmVhZG9ubHkgcGFuZWxIb3N0OiBIVE1MRWxlbWVudCB8IG51bGw7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGxvdE1vZGVDb250cm9sbGVySG9va3Mge1xuICAvKiogQSBzZXNzaW9uIGlzIGxpdmUgKGFuZCBhIGBtb3VudGAgcGFuZWwsIGlmIGFueSwgaGFzIGJlZW4gbW91bnRlZCkuICovXG4gIG9uQWN0aXZhdGVkKGFjdGl2ZTogQWN0aXZlUGxvdE1vZGUpOiB2b2lkO1xuICAvKiogVGhlIGxpdmUgc2Vzc2lvbiBpcyBhYm91dCB0byBlbmQuIENhbGxlZCBCRUZPUkUgYW55IHRlYXJkb3duLCBzbyBhXG4gICAqICBjb21wb25lbnQgcGFuZWwgY2FuIGJlIGRlc3Ryb3llZCB3aGlsZSBpdHMgc2Vzc2lvbiBpcyBzdGlsbCB2YWxpZC4gKi9cbiAgb25EZWFjdGl2YXRpbmcoYWN0aXZlOiBBY3RpdmVQbG90TW9kZSk6IHZvaWQ7XG4gIC8qKiBBY3RpdmF0aW9uIGZhaWxlZCAodGhyZXcsIHJlamVjdGVkLCBuZXZlciBnb3QgYSByZWFkeSB2aWV3cG9ydCwgb3IgaXRzXG4gICAqICBwYW5lbCBmYWlsZWQgdG8gbW91bnQpLiBUaGUgY29tcG9uZW50IGZhbGxzIGJhY2sgdG8gdGhlIGJhc2UgdHlwZS4gKi9cbiAgb25GYWlsZWQoY29udHJpYnV0aW9uOiBQbG90VHlwZUNvbnRyaWJ1dGlvbiwgZXJyb3I6IHVua25vd24pOiB2b2lkO1xufVxuXG4vKiogTWluaW1hbCBsb2dnZXIsIGluamVjdGFibGUgZm9yIHRlc3RzLiAqL1xuZXhwb3J0IGludGVyZmFjZSBQbG90TW9kZUxvZ2dlciB7XG4gIHdhcm4oLi4uYXJnczogdW5rbm93bltdKTogdm9pZDtcbiAgZXJyb3IoLi4uYXJnczogdW5rbm93bltdKTogdm9pZDtcbn1cblxuLyoqIEJhc2UgdHlwZXMgYSBjb250cmlidXRlZCBtb2RlIGNhbiByaWRlIG9uIGluIHRoaXMgdmVyc2lvbi4gKi9cbmV4cG9ydCBjb25zdCBTVVBQT1JURURfQ09OVFJJQlVUSU9OX0JBU0VfVFlQRVM6IHJlYWRvbmx5IFBsb3RUeXBlW10gPSBbUGxvdFR5cGUuSU1BR0VdO1xuXG4vKiogSG93IG1hbnkgYW5pbWF0aW9uIGZyYW1lcyB0byB3YWl0IGZvciB0aGUgdmlld3BvcnQgYmVmb3JlIGdpdmluZyB1cC4gKi9cbmNvbnN0IE1BWF9SRUFEWV9GUkFNRVMgPSAxODA7XG5cbmNvbnN0IFRBRyA9ICdbdmlzdWFsaXplcl0gcGxvdC10eXBlIGNvbnRyaWJ1dGlvbic7XG5cbi8qKlxuICogVmFsaWRhdGUgd2hhdCBhcnJpdmVkIG9uIGBQTE9UX1RZUEVfQ09OVFJJQlVUSU9OU2AuIEEgbWFsZm9ybWVkIG9yIGNsYXNoaW5nXG4gKiBjb250cmlidXRpb24gaXMgZHJvcHBlZCB3aXRoIGEgd2FybmluZyByYXRoZXIgdGhhbiBicmVha2luZyB0aGUgc2VsZWN0b3I6XG4gKiAgLSBhIG5lc3RlZCBhcnJheSAoYHVzZVZhbHVlOiBbYSwgYl0sIG11bHRpOiB0cnVlYCkgaXMgZmxhdHRlbmVkIG9uZSBsZXZlbDtcbiAqICAtIGB0eXBlYCBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZyB0aGF0IGlzIG5vdCBhIGJ1aWx0LWluIGBQbG90VHlwZWAgdmFsdWU7XG4gKiAgLSB0aGUgZmlyc3QgY29udHJpYnV0aW9uIHdpdGggYSBnaXZlbiBgdHlwZWAgd2lucztcbiAqICAtIGBiYXNlVHlwZWAgbXVzdCBiZSBvbmUgb2Yge0BsaW5rIFNVUFBPUlRFRF9DT05UUklCVVRJT05fQkFTRV9UWVBFU307XG4gKiAgLSBgYWN0aXZhdGVgIG11c3QgYmUgYSBmdW5jdGlvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG5vcm1hbGl6ZUNvbnRyaWJ1dGlvbnMoXG4gIHJhdzogcmVhZG9ubHkgdW5rbm93bltdIHwgbnVsbCB8IHVuZGVmaW5lZCxcbiAgbG9nOiBQbG90TW9kZUxvZ2dlciA9IGNvbnNvbGUsXG4pOiBQbG90VHlwZUNvbnRyaWJ1dGlvbltdIHtcbiAgY29uc3Qgb3V0OiBQbG90VHlwZUNvbnRyaWJ1dGlvbltdID0gW107XG4gIGNvbnN0IHNlZW4gPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgY29uc3QgZmxhdCA9IChyYXcgPz8gW10pLmZsYXRNYXAoKGMpID0+IChBcnJheS5pc0FycmF5KGMpID8gYyA6IFtjXSkpO1xuICBmb3IgKGNvbnN0IGMgb2YgZmxhdCBhcyBQbG90VHlwZUNvbnRyaWJ1dGlvbltdKSB7XG4gICAgY29uc3QgZCA9IGM/LmRlc2NyaXB0b3IgYXMgQ29udHJpYnV0ZWRQbG90VHlwZURlc2NyaXB0b3IgfCB1bmRlZmluZWQ7XG4gICAgY29uc3QgaWQgPSBkPy50eXBlO1xuICAgIGlmICghZCB8fCB0eXBlb2YgaWQgIT09ICdzdHJpbmcnIHx8ICFpZCB8fCB0eXBlb2YgYy5hY3RpdmF0ZSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgbG9nLndhcm4oYCR7VEFHfSBpZ25vcmVkOiBpdCBuZWVkcyBhIGRlc2NyaXB0b3Igd2l0aCBhIHN0cmluZyBcXGB0eXBlXFxgIGFuZCBhbiBcXGBhY3RpdmF0ZVxcYCBmdW5jdGlvbi5gLCBjKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBpZiAoaXNCdWlsdGluUGxvdFR5cGUoaWQpKSB7XG4gICAgICBsb2cud2FybihgJHtUQUd9ICcke2lkfScgaWdub3JlZDogdGhlIGlkIGNsYXNoZXMgd2l0aCBhIGJ1aWx0LWluIHBsb3QgdHlwZS5gKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBpZiAoc2Vlbi5oYXMoaWQpKSB7XG4gICAgICBsb2cud2FybihgJHtUQUd9ICcke2lkfScgaWdub3JlZDogYW5vdGhlciBjb250cmlidXRpb24gYWxyZWFkeSB1c2VzIHRoYXQgaWQuYCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgaWYgKCFTVVBQT1JURURfQ09OVFJJQlVUSU9OX0JBU0VfVFlQRVMuaW5jbHVkZXMoZC5iYXNlVHlwZSkpIHtcbiAgICAgIGxvZy53YXJuKGAke1RBR30gJyR7aWR9JyBpZ25vcmVkOiBiYXNlVHlwZSAnJHtTdHJpbmcoZC5iYXNlVHlwZSl9JyBpcyBub3Qgc3VwcG9ydGVkIGBcbiAgICAgICAgKyBgKHN1cHBvcnRlZDogJHtTVVBQT1JURURfQ09OVFJJQlVUSU9OX0JBU0VfVFlQRVMuam9pbignLCAnKX0pLmApO1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIHNlZW4uYWRkKGlkKTtcbiAgICBvdXQucHVzaChjKTtcbiAgfVxuICByZXR1cm4gb3V0O1xufVxuXG5leHBvcnQgY2xhc3MgUGxvdE1vZGVDb250cm9sbGVyIHtcbiAgcmVhZG9ubHkgY29udHJpYnV0aW9uczogcmVhZG9ubHkgUGxvdFR5cGVDb250cmlidXRpb25bXTtcbiAgcHJpdmF0ZSByZWFkb25seSBieUlkOiBSZWFkb25seU1hcDxzdHJpbmcsIFBsb3RUeXBlQ29udHJpYnV0aW9uPjtcblxuICBwcml2YXRlIGFjdGl2ZTogQWN0aXZlUGxvdE1vZGUgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSB0ZWFyZG93bjogKCgpID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBCdW1wZWQgYnkgZXZlcnkgYWN0aXZhdGUvZGVhY3RpdmF0ZSwgc28gYSBwZW5kaW5nIGFzeW5jIGFjdGl2YXRpb24gY2FuXG4gICAqICB0ZWxsIGl0IGhhcyBiZWVuIHN1cGVyc2VkZWQuICovXG4gIHByaXZhdGUgZ2VuZXJhdGlvbiA9IDA7XG4gIHByaXZhdGUgcmVhZHlGcmFtZTogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBDb250cmlidXRpb25zIHdob3NlIGxhc3QgY2xlYW51cCAocGFuZWwgdGVhcmRvd24gLyBgZGVhY3RpdmF0ZSgpYCkgdGhyZXcgb3JcbiAgICogIHJlamVjdGVkLiBUaGVpciBuZXh0IGF1dG9tYXRpYyByZS1hY3RpdmF0aW9uIChiYXNlIHJlLXJlbmRlciwgaW1hZ2Ugc3dpdGNoKVxuICAgKiAgZmFsbHMgYmFjayB0byB0aGUgYmFzZSB0eXBlIGluc3RlYWQ7IGFuIGV4cGxpY2l0IHNlbGVjdGlvbiBjbGVhcnMgdGhlIG1hcmsuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgZmFpbGVkQ2xlYW51cCA9IG5ldyBNYXA8c3RyaW5nLCB1bmtub3duPigpO1xuICBwcml2YXRlIHJlYWR5Q2I6ICgocmVhZHk6IGJvb2xlYW4pID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcmF3OiByZWFkb25seSB1bmtub3duW10gfCBudWxsIHwgdW5kZWZpbmVkLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgaG9va3M6IFBsb3RNb2RlQ29udHJvbGxlckhvb2tzLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgbG9nOiBQbG90TW9kZUxvZ2dlciA9IGNvbnNvbGUsXG4gICkge1xuICAgIHRoaXMuY29udHJpYnV0aW9ucyA9IG5vcm1hbGl6ZUNvbnRyaWJ1dGlvbnMocmF3LCBsb2cpO1xuICAgIHRoaXMuYnlJZCA9IG5ldyBNYXAodGhpcy5jb250cmlidXRpb25zLm1hcCgoYykgPT4gW2MuZGVzY3JpcHRvci50eXBlLCBjXSkpO1xuICB9XG5cbiAgLyoqIERlc2NyaXB0b3JzIG9mIHRoZSB1c2FibGUgY29udHJpYnV0aW9ucywgaW4gcmVnaXN0cmF0aW9uIG9yZGVyLiAqL1xuICBkZXNjcmlwdG9ycygpOiBDb250cmlidXRlZFBsb3RUeXBlRGVzY3JpcHRvcltdIHtcbiAgICByZXR1cm4gdGhpcy5jb250cmlidXRpb25zLm1hcCgoYykgPT4gYy5kZXNjcmlwdG9yKTtcbiAgfVxuXG4gIC8qKiBUaGUgY29udHJpYnV0aW9uIGZvciBhIHNlbGVjdG9yIGlkLCBpZiBpdCBpcyBhIGNvbnRyaWJ1dGVkIG1vZGUuICovXG4gIGZpbmQoaWQ6IFBsb3RUeXBlSWQgfCBudWxsIHwgdW5kZWZpbmVkKTogUGxvdFR5cGVDb250cmlidXRpb24gfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB0eXBlb2YgaWQgPT09ICdzdHJpbmcnID8gdGhpcy5ieUlkLmdldChpZCkgOiB1bmRlZmluZWQ7XG4gIH1cblxuICAvKipcbiAgICogVGhlIGJ1aWx0LWluIHR5cGUgdGhhdCByZW5kZXJzIGBpZGA6IGEgYnVpbHQtaW4gdHlwZSBpcyBpdHNlbGYsIGFcbiAgICogY29udHJpYnV0ZWQgaWQgaXMgaXRzIGBiYXNlVHlwZWAsIGFuZCBhbiBpZCB0aGF0IGlzIG5laXRoZXIgKGEgc3RhbGVcbiAgICogY29udHJpYnV0ZWQgaWQgd2hvc2UgcHJvdmlkZXIgaXMgZ29uZSkgcmVzb2x2ZXMgdG8gYFBsb3RUeXBlLklNQUdFYCwgdGhlXG4gICAqIGRlZmF1bHQgdmlldy5cbiAgICovXG4gIGJhc2VUeXBlT2YoaWQ6IFBsb3RUeXBlSWQgfCBudWxsIHwgdW5kZWZpbmVkKTogUGxvdFR5cGUge1xuICAgIGlmIChpc0J1aWx0aW5QbG90VHlwZShpZCkpIHJldHVybiBpZDtcbiAgICByZXR1cm4gdGhpcy5maW5kKGlkKT8uZGVzY3JpcHRvci5iYXNlVHlwZSA/PyBQbG90VHlwZS5JTUFHRTtcbiAgfVxuXG4gIC8qKiBUaGUgbGl2ZSBzZXNzaW9uLCBpZiBhbnkuICovXG4gIGdldCBjdXJyZW50KCk6IEFjdGl2ZVBsb3RNb2RlIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuYWN0aXZlO1xuICB9XG5cbiAgLyoqIFdoZXRoZXIgYW4gYWN0aXZhdGlvbiBpcyB3YWl0aW5nIG9uIHRoZSBjb250cmlidXRpb24gb3IgdGhlIHZpZXdwb3J0LiAqL1xuICBnZXQgcGVuZGluZygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5wZW5kaW5nSWQgIT09IG51bGw7XG4gIH1cbiAgcHJpdmF0ZSBwZW5kaW5nSWQ6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8qKlxuICAgKiBTdGFydCBhIHNlc3Npb24gZm9yIGBjb250cmlidXRpb25gLiBFbmRzIGFueSBsaXZlIG9uZSBmaXJzdC4gUmVzb2x2ZXMgd2hlblxuICAgKiB0aGUgYXR0ZW1wdCBoYXMgZmluaXNoZWQgKHN1Y2Nlc3NmdWxseSBvciBub3QpOyBpdCBuZXZlciByZWplY3RzLlxuICAgKi9cbiAgYWN0aXZhdGUoY29udHJpYnV0aW9uOiBQbG90VHlwZUNvbnRyaWJ1dGlvbiwgY3R4OiBQbG90TW9kZUNvbnRleHQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmRlYWN0aXZhdGUoKTtcbiAgICBjb25zdCBpZCA9IGNvbnRyaWJ1dGlvbi5kZXNjcmlwdG9yLnR5cGU7XG4gICAgaWYgKHRoaXMuZmFpbGVkQ2xlYW51cC5oYXMoaWQpKSB7XG4gICAgICAvLyBJdHMgcHJldmlvdXMgc2Vzc2lvbiBkaWQgbm90IGVuZCBjbGVhbmx5OyBkbyBub3Qgc3RhY2sgYSBuZXcgb25lIG9uIHRvcC5cbiAgICAgIGNvbnN0IGVyciA9IHRoaXMuZmFpbGVkQ2xlYW51cC5nZXQoaWQpO1xuICAgICAgdGhpcy5mYWlsZWRDbGVhbnVwLmRlbGV0ZShpZCk7XG4gICAgICB0aGlzLmZhaWwoY29udHJpYnV0aW9uLCBlcnIpO1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgIH1cbiAgICBjb25zdCBnZW4gPSArK3RoaXMuZ2VuZXJhdGlvbjtcbiAgICB0aGlzLnBlbmRpbmdJZCA9IGlkO1xuICAgIHJldHVybiBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT4ge1xuICAgICAgdGhpcy53aGVuUmVhZHkoY3R4LCBnZW4sIChyZWFkeSkgPT4ge1xuICAgICAgICBpZiAoZ2VuICE9PSB0aGlzLmdlbmVyYXRpb24pIHJldHVybiByZXNvbHZlKCk7XG4gICAgICAgIGlmICghcmVhZHkpIHtcbiAgICAgICAgICB0aGlzLmZhaWwoY29udHJpYnV0aW9uLCBuZXcgRXJyb3IoJ3RoZSB2aWV3cG9ydCBuZXZlciBiZWNhbWUgcmVhZHknKSk7XG4gICAgICAgICAgcmV0dXJuIHJlc29sdmUoKTtcbiAgICAgICAgfVxuICAgICAgICBsZXQgcmVzdWx0OiBQbG90TW9kZVNlc3Npb24gfCBQcm9taXNlPFBsb3RNb2RlU2Vzc2lvbj47XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmVzdWx0ID0gY29udHJpYnV0aW9uLmFjdGl2YXRlKGN0eCk7XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIHRoaXMuZmFpbChjb250cmlidXRpb24sIGVycik7XG4gICAgICAgICAgcmV0dXJuIHJlc29sdmUoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoaXNUaGVuYWJsZShyZXN1bHQpKSB7XG4gICAgICAgICAgcmVzdWx0LnRoZW4oXG4gICAgICAgICAgICAoc2Vzc2lvbikgPT4geyB0aGlzLnN0YXJ0KGNvbnRyaWJ1dGlvbiwgY3R4LCBzZXNzaW9uLCBnZW4pOyByZXNvbHZlKCk7IH0sXG4gICAgICAgICAgICAoZXJyKSA9PiB7XG4gICAgICAgICAgICAgIGlmIChnZW4gPT09IHRoaXMuZ2VuZXJhdGlvbikgdGhpcy5mYWlsKGNvbnRyaWJ1dGlvbiwgZXJyKTtcbiAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMuc3RhcnQoY29udHJpYnV0aW9uLCBjdHgsIHJlc3VsdCwgZ2VuKTtcbiAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuZCB0aGUgbGl2ZSBzZXNzaW9uLCBpZiBhbnk6IHRoZSBjb21wb25lbnQgcGFuZWwgaXMgZHJvcHBlZCAodmlhXG4gICAqIGBvbkRlYWN0aXZhdGluZ2ApLCBhIGBtb3VudGAgcGFuZWwncyB0ZWFyZG93biBydW5zLCB0aGVuXG4gICAqIGBzZXNzaW9uLmRlYWN0aXZhdGUoKWAsIHRoZW4gdGhlIG1vdW50IGhvc3QgaXMgcmVtb3ZlZC4gRWFjaCBzdGVwIGlzXG4gICAqIGlzb2xhdGVkLiBBIHBlbmRpbmcgYWN0aXZhdGlvbiBpcyBjYW5jZWxsZWQ7IGlmIGl0IHJlc29sdmVzIGxhdGVyIGl0c1xuICAgKiBzZXNzaW9uIGlzIGRlYWN0aXZhdGVkIG9uIGFycml2YWwuIElkZW1wb3RlbnQuXG4gICAqL1xuICBkZWFjdGl2YXRlKCk6IHZvaWQge1xuICAgIHRoaXMuZ2VuZXJhdGlvbisrO1xuICAgIHRoaXMucGVuZGluZ0lkID0gbnVsbDtcbiAgICB0aGlzLmNhbmNlbFJlYWR5V2FpdCgpO1xuICAgIGNvbnN0IGEgPSB0aGlzLmFjdGl2ZTtcbiAgICBpZiAoIWEpIHJldHVybjtcbiAgICB0aGlzLmFjdGl2ZSA9IG51bGw7XG4gICAgY29uc3QgdGVhcmRvd24gPSB0aGlzLnRlYXJkb3duO1xuICAgIHRoaXMudGVhcmRvd24gPSBudWxsO1xuICAgIGNvbnN0IGlkID0gYS5jb250cmlidXRpb24uZGVzY3JpcHRvci50eXBlO1xuICAgIHRoaXMuZ3VhcmQoYCcke2lkfScgcGFuZWwgcmVtb3ZhbGAsICgpID0+IHRoaXMuaG9va3Mub25EZWFjdGl2YXRpbmcoYSkpO1xuICAgIGlmICh0ZWFyZG93bikgdGhpcy5jbGVhbnVwKGEuY29udHJpYnV0aW9uLCAncGFuZWwgdGVhcmRvd24nLCB0ZWFyZG93bik7XG4gICAgdGhpcy5jbGVhbnVwKGEuY29udHJpYnV0aW9uLCAnZGVhY3RpdmF0ZSgpJywgKCkgPT4gYS5zZXNzaW9uLmRlYWN0aXZhdGUoKSk7XG4gICAgYS5wYW5lbEhvc3Q/LnJlbW92ZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZvcmdldCByZWNvcmRlZCBjbGVhbnVwIGZhaWx1cmVzLiBUaGUgaG9zdCBjYWxscyB0aGlzIHdoZW4gdGhlIFVTRVIgcGlja3MgYVxuICAgKiBwbG90IHR5cGU6IGFuIGV4cGxpY2l0IGNob2ljZSBnZXRzIGEgZnJlc2ggYXR0ZW1wdCwgb25seSBhdXRvbWF0aWNcbiAgICogcmUtYWN0aXZhdGlvbnMgZmFsbCBiYWNrLlxuICAgKi9cbiAgY2xlYXJDbGVhbnVwRmFpbHVyZXMoKTogdm9pZCB7XG4gICAgdGhpcy5mYWlsZWRDbGVhbnVwLmNsZWFyKCk7XG4gIH1cblxuICAvLyDilIDilIAgaW50ZXJuYWxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIHByaXZhdGUgc3RhcnQoY29udHJpYnV0aW9uOiBQbG90VHlwZUNvbnRyaWJ1dGlvbiwgY3R4OiBQbG90TW9kZUNvbnRleHQsXG4gICAgICAgICAgICAgICAgc2Vzc2lvbjogUGxvdE1vZGVTZXNzaW9uLCBnZW46IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IGlkID0gY29udHJpYnV0aW9uLmRlc2NyaXB0b3IudHlwZTtcbiAgICBpZiAoIXNlc3Npb24gfHwgdHlwZW9mIHNlc3Npb24uZGVhY3RpdmF0ZSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgaWYgKGdlbiA9PT0gdGhpcy5nZW5lcmF0aW9uKSB7XG4gICAgICAgIHRoaXMuZmFpbChjb250cmlidXRpb24sIG5ldyBFcnJvcignYWN0aXZhdGUoKSBkaWQgbm90IHJldHVybiBhIFBsb3RNb2RlU2Vzc2lvbicpKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGdlbiAhPT0gdGhpcy5nZW5lcmF0aW9uKSB7XG4gICAgICAvLyBTdXBlcnNlZGVkIHdoaWxlIGFjdGl2YXRlKCkgd2FzIGluIGZsaWdodCDigJQgdGhlIHVzZXIgYWxyZWFkeSBsZWZ0LiBUaGVcbiAgICAgIC8vIHNlc3Npb24gc3RpbGwgZ2V0cyBpdHMgc2luZ2xlIGRlYWN0aXZhdGUoKSwganVzdCBsYXRlLlxuICAgICAgdGhpcy5ndWFyZExvZ2dlZChgJyR7aWR9JyBkZWFjdGl2YXRlKCkgb2YgYSBzdXBlcnNlZGVkIHNlc3Npb25gLCAoKSA9PiBzZXNzaW9uLmRlYWN0aXZhdGUoKSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMucGVuZGluZ0lkID0gbnVsbDtcbiAgICBjb25zdCBwYW5lbCA9IGNvbnRyaWJ1dGlvbi5wYW5lbDtcbiAgICBsZXQgcGFuZWxIb3N0OiBIVE1MRWxlbWVudCB8IG51bGwgPSBudWxsO1xuICAgIGxldCB0ZWFyZG93bjogKCgpID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG4gICAgaWYgKHBhbmVsICYmICdtb3VudCcgaW4gcGFuZWwgJiYgdHlwZW9mIHBhbmVsLm1vdW50ID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICBwYW5lbEhvc3QgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgIHBhbmVsSG9zdC5jbGFzc05hbWUgPSAncGxvdC1tb2RlLXBhbmVsLWhvc3QnO1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgdCA9IHBhbmVsLm1vdW50KHBhbmVsSG9zdCwgY3R4LCBzZXNzaW9uKTtcbiAgICAgICAgdGVhcmRvd24gPSB0eXBlb2YgdCA9PT0gJ2Z1bmN0aW9uJyA/IHQgOiBudWxsO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIHBhbmVsSG9zdC5yZW1vdmUoKTtcbiAgICAgICAgdGhpcy5ndWFyZExvZ2dlZChgJyR7aWR9JyBkZWFjdGl2YXRlKClgLCAoKSA9PiBzZXNzaW9uLmRlYWN0aXZhdGUoKSk7XG4gICAgICAgIHRoaXMuZmFpbChjb250cmlidXRpb24sIGVycik7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5hY3RpdmUgPSB7IGNvbnRyaWJ1dGlvbiwgY3R4LCBzZXNzaW9uLCBwYW5lbEhvc3QgfTtcbiAgICB0aGlzLnRlYXJkb3duID0gdGVhcmRvd247XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMuaG9va3Mub25BY3RpdmF0ZWQodGhpcy5hY3RpdmUpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8gVGhlIGhvc3QgY291bGQgbm90IHNob3cgaXQgKGUuZy4gaXRzIHBhbmVsIGZhaWxlZCB0byByZW5kZXIpOiBub3QgYSB1c2FibGUgbW9kZS5cbiAgICAgIHRoaXMuZmFpbEFjdGl2ZShlcnIpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgaG9zdCBjb3VsZCBub3QgbWFrZSB0aGUgbGl2ZSBtb2RlIHVzYWJsZSAoaXRzIHBhbmVsIHRocmV3IHdoaWxlIGJlaW5nXG4gICAqIGNyZWF0ZWQgb3IgZmlyc3QgcmVuZGVyZWQpOiBlbmQgdGhlIHNlc3Npb24gb25jZSwgdGhlbiBmYWxsIGJhY2sgdG8gaXRzIGJhc2UuXG4gICAqIE5vLW9wIHdoZW4gbm90aGluZyBpcyBsaXZlLlxuICAgKi9cbiAgZmFpbEFjdGl2ZShlcnI6IHVua25vd24pOiB2b2lkIHtcbiAgICBjb25zdCBhID0gdGhpcy5hY3RpdmU7XG4gICAgaWYgKCFhKSByZXR1cm47XG4gICAgdGhpcy5kZWFjdGl2YXRlKCk7XG4gICAgdGhpcy5mYWlsKGEuY29udHJpYnV0aW9uLCBlcnIpO1xuICB9XG5cbiAgcHJpdmF0ZSBmYWlsKGNvbnRyaWJ1dGlvbjogUGxvdFR5cGVDb250cmlidXRpb24sIGVycjogdW5rbm93bik6IHZvaWQge1xuICAgIHRoaXMucGVuZGluZ0lkID0gbnVsbDtcbiAgICB0aGlzLmxvZy5lcnJvcihgJHtUQUd9ICcke2NvbnRyaWJ1dGlvbi5kZXNjcmlwdG9yLnR5cGV9JyBmYWlsZWQgdG8gYWN0aXZhdGUg4oCUIGBcbiAgICAgICsgYGZhbGxpbmcgYmFjayB0byAnJHtjb250cmlidXRpb24uZGVzY3JpcHRvci5iYXNlVHlwZX0nLmAsIGVycik7XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMuaG9va3Mub25GYWlsZWQoY29udHJpYnV0aW9uLCBlcnIpO1xuICAgIH0gY2F0Y2ggKGhvb2tFcnIpIHtcbiAgICAgIHRoaXMubG9nLmVycm9yKGAke1RBR30gZmFsbGJhY2sgZmFpbGVkLmAsIGhvb2tFcnIpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZ3VhcmQod2hhdDogc3RyaW5nLCBmbjogKCkgPT4gdm9pZCk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICBmbigpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgdGhpcy5sb2cuZXJyb3IoYCR7VEFHfSAke3doYXR9IHRocmV3LmAsIGVycik7XG4gICAgfVxuICB9XG5cbiAgLyoqIExpa2UgYGd1YXJkYCwgZm9yIGEgY29udHJpYnV0aW9uIGNhbGxiYWNrIHR5cGVkIGB2b2lkYCB0aGF0IG1heSBzdGlsbCByZXR1cm4gYVxuICAgKiAgcHJvbWlzZTogYSByZWplY3Rpb24gaXMgbG9nZ2VkIGluc3RlYWQgb2YgZXNjYXBpbmcgYXMgYW4gdW5oYW5kbGVkIHJlamVjdGlvbi4gKi9cbiAgcHJpdmF0ZSBndWFyZExvZ2dlZCh3aGF0OiBzdHJpbmcsIGZuOiAoKSA9PiB1bmtub3duKTogdm9pZCB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHIgPSBmbigpO1xuICAgICAgaWYgKGlzVGhlbmFibGUocikpIHIudGhlbih1bmRlZmluZWQsIChlcnIpID0+IHRoaXMubG9nLmVycm9yKGAke1RBR30gJHt3aGF0fSByZWplY3RlZC5gLCBlcnIpKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMubG9nLmVycm9yKGAke1RBR30gJHt3aGF0fSB0aHJldy5gLCBlcnIpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBSdW4gYSBsaXZlIHNlc3Npb24ncyBjbGVhbnVwOyBhIHRocm93IE9SIGEgcmVqZWN0aW9uIGdvZXMgdG8gYGNsZWFudXBGYWlsZWRgLiAqL1xuICBwcml2YXRlIGNsZWFudXAoY29udHJpYnV0aW9uOiBQbG90VHlwZUNvbnRyaWJ1dGlvbiwgd2hhdDogc3RyaW5nLCBmbjogKCkgPT4gdW5rbm93bik6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByID0gZm4oKTtcbiAgICAgIGlmIChpc1RoZW5hYmxlKHIpKSByLnRoZW4odW5kZWZpbmVkLCAoZXJyKSA9PiB0aGlzLmNsZWFudXBGYWlsZWQoY29udHJpYnV0aW9uLCB3aGF0LCBlcnIpKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMuY2xlYW51cEZhaWxlZChjb250cmlidXRpb24sIHdoYXQsIGVycik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEEgY29udHJpYnV0aW9uJ3MgY2xlYW51cCBmYWlsZWQuIElmIHRoYXQgc2FtZSBjb250cmlidXRpb24gaXMgbGl2ZSBvciBzdGFydGluZ1xuICAgKiBhZ2FpbiAoYSBiYXNlIHJlLXJlbmRlciAvIGltYWdlIHN3aXRjaCByZS1hY3RpdmF0ZWQgaXQg4oCUIG9yLCBmb3IgYW4gYXN5bmNcbiAgICogcmVqZWN0aW9uLCBhbHJlYWR5IGhhcyksIGVuZCBpdCBhbmQgZmFsbCBiYWNrIHRvIGl0cyBiYXNlIHR5cGUuIE90aGVyd2lzZSB0aGVcbiAgICogdXNlciBoYXMgbW92ZWQgb246IHJlbWVtYmVyIGl0LCBzbyB0aGUgbmV4dCBhdXRvbWF0aWMgcmUtYWN0aXZhdGlvbiBmYWxscyBiYWNrLlxuICAgKi9cbiAgcHJpdmF0ZSBjbGVhbnVwRmFpbGVkKGNvbnRyaWJ1dGlvbjogUGxvdFR5cGVDb250cmlidXRpb24sIHdoYXQ6IHN0cmluZywgZXJyOiB1bmtub3duKTogdm9pZCB7XG4gICAgY29uc3QgaWQgPSBjb250cmlidXRpb24uZGVzY3JpcHRvci50eXBlO1xuICAgIHRoaXMubG9nLmVycm9yKGAke1RBR30gJyR7aWR9JyAke3doYXR9IGZhaWxlZC5gLCBlcnIpO1xuICAgIGNvbnN0IGFnYWluID0gdGhpcy5hY3RpdmU/LmNvbnRyaWJ1dGlvbiA9PT0gY29udHJpYnV0aW9uIHx8IHRoaXMucGVuZGluZ0lkID09PSBpZDtcbiAgICBpZiAoYWdhaW4pIHtcbiAgICAgIHRoaXMuZGVhY3RpdmF0ZSgpO1xuICAgICAgdGhpcy5mYWlsKGNvbnRyaWJ1dGlvbiwgZXJyKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5mYWlsZWRDbGVhbnVwLnNldChpZCwgZXJyKTtcbiAgICB9XG4gIH1cblxuICAvKiogQ2FsbCBgY2IodHJ1ZSlgIG9uY2UgdGhlIHZpZXdwb3J0IHJlcG9ydHMgcmVhZHkgKG5vdywgb3Igb24gYSBsYXRlclxuICAgKiAgYW5pbWF0aW9uIGZyYW1lKSwgb3IgYGNiKGZhbHNlKWAgYWZ0ZXIge0BsaW5rIE1BWF9SRUFEWV9GUkFNRVN9LiAqL1xuICBwcml2YXRlIHdoZW5SZWFkeShjdHg6IFBsb3RNb2RlQ29udGV4dCwgZ2VuOiBudW1iZXIsIGNiOiAocmVhZHk6IGJvb2xlYW4pID0+IHZvaWQpOiB2b2lkIHtcbiAgICBjb25zdCBpc1JlYWR5ID0gKCkgPT4ge1xuICAgICAgdHJ5IHsgcmV0dXJuIGN0eC52aWV3cG9ydC5pc1JlYWR5KCk7IH0gY2F0Y2ggeyByZXR1cm4gZmFsc2U7IH1cbiAgICB9O1xuICAgIGlmIChpc1JlYWR5KCkpIHJldHVybiBjYih0cnVlKTtcbiAgICBsZXQgZnJhbWVzID0gMDtcbiAgICBjb25zdCBzZXR0bGUgPSAocmVhZHk6IGJvb2xlYW4pID0+IHtcbiAgICAgIHRoaXMucmVhZHlGcmFtZSA9IG51bGw7XG4gICAgICB0aGlzLnJlYWR5Q2IgPSBudWxsO1xuICAgICAgY2IocmVhZHkpO1xuICAgIH07XG4gICAgY29uc3QgdGljayA9ICgpID0+IHtcbiAgICAgIHRoaXMucmVhZHlGcmFtZSA9IG51bGw7XG4gICAgICBpZiAoZ2VuICE9PSB0aGlzLmdlbmVyYXRpb24pIHJldHVybiBzZXR0bGUoZmFsc2UpO1xuICAgICAgaWYgKGlzUmVhZHkoKSkgcmV0dXJuIHNldHRsZSh0cnVlKTtcbiAgICAgIGlmICgrK2ZyYW1lcyA+PSBNQVhfUkVBRFlfRlJBTUVTKSByZXR1cm4gc2V0dGxlKGZhbHNlKTtcbiAgICAgIHRoaXMucmVhZHlGcmFtZSA9IHJlcXVlc3RBbmltYXRpb25GcmFtZSh0aWNrKTtcbiAgICB9O1xuICAgIHRoaXMucmVhZHlDYiA9IHNldHRsZTtcbiAgICB0aGlzLnJlYWR5RnJhbWUgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUodGljayk7XG4gIH1cblxuICAvKiogU3RvcCB3YWl0aW5nIGZvciB0aGUgdmlld3BvcnQ7IHRoZSB3YWl0aW5nIGFjdGl2YXRpb24gcmVzb2x2ZXMgYXMgc3VwZXJzZWRlZC4gKi9cbiAgcHJpdmF0ZSBjYW5jZWxSZWFkeVdhaXQoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMucmVhZHlGcmFtZSAhPT0gbnVsbCkgY2FuY2VsQW5pbWF0aW9uRnJhbWUodGhpcy5yZWFkeUZyYW1lKTtcbiAgICB0aGlzLnJlYWR5RnJhbWUgPSBudWxsO1xuICAgIGNvbnN0IGNiID0gdGhpcy5yZWFkeUNiO1xuICAgIHRoaXMucmVhZHlDYiA9IG51bGw7XG4gICAgY2I/LihmYWxzZSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gaXNUaGVuYWJsZTxUPih2OiBUIHwgUHJvbWlzZTxUPiB8IHVua25vd24pOiB2IGlzIFByb21pc2U8VD4ge1xuICByZXR1cm4gISF2ICYmIHR5cGVvZiAodiBhcyBQcm9taXNlPFQ+KS50aGVuID09PSAnZnVuY3Rpb24nO1xufVxuIl19
|