@jax-data-science/sci-image-visualizer 0.6.0 → 0.7.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.
Files changed (29) hide show
  1. package/esm2022/lib/contracts/display-types.mjs +28 -1
  2. package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -1
  3. package/esm2022/lib/contracts/spatial-dataset.contract.mjs +3 -1
  4. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  5. package/esm2022/lib/implementations/napari-js/napari-navigator.mjs +195 -0
  6. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +10 -2
  7. package/esm2022/lib/implementations/napari-js/napari-spatial-tiles.mjs +1098 -0
  8. package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +6 -3
  9. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +185 -8
  10. package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +120 -2
  11. package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +76 -1
  12. package/esm2022/lib/routing-visualizer.service.mjs +10 -1
  13. package/esm2022/lib/spatial/spatial-tiles.mjs +506 -0
  14. package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +659 -19
  15. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +2909 -62
  16. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  17. package/lib/contracts/display-types.d.ts +94 -1
  18. package/lib/contracts/ports/spatial-data.port.d.ts +59 -1
  19. package/lib/contracts/spatial-dataset.contract.d.ts +167 -0
  20. package/lib/contracts/visualizer.contract.d.ts +22 -1
  21. package/lib/implementations/napari-js/napari-navigator.d.ts +72 -0
  22. package/lib/implementations/napari-js/napari-region-overlay.d.ts +3 -0
  23. package/lib/implementations/napari-js/napari-spatial-tiles.d.ts +240 -0
  24. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +42 -2
  25. package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +26 -2
  26. package/lib/implementations/spatial-data-http/spatial-wire.d.ts +34 -1
  27. package/lib/spatial/spatial-tiles.d.ts +222 -0
  28. package/lib/spatial-controls/spatial-controls.component.d.ts +234 -3
  29. package/package.json +1 -1
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Overview navigator (minimap) for the napari-js 2D views, mirroring the OSD backend's
3
+ * built-in navigator: a thumbnail of the whole image in the bottom-right corner with the
4
+ * current viewport drawn on it. Clicking re-centres the main view on that point at the
5
+ * same zoom; dragging pans continuously — exactly OSD's behaviour.
6
+ *
7
+ * napari keeps layers at full-resolution pixel scale, so the camera's world units ARE
8
+ * image pixels and the thumbnail maps onto `[0, width) × [0, height)` directly.
9
+ */
10
+ /** Fraction of the host's width the navigator takes — OSD's `navigatorSizeRatio`. */
11
+ export const NAVIGATOR_SIZE_RATIO = 0.16;
12
+ const NAVIGATOR_MIN_PX = 110;
13
+ const NAVIGATOR_MAX_PX = 300;
14
+ /** Box size and scale for a `worldW × worldH` image in a host `hostW` wide. */
15
+ export function navigatorLayout(hostW, worldW, worldH, ratio = NAVIGATOR_SIZE_RATIO) {
16
+ if (!(worldW > 0) || !(worldH > 0) || !(hostW > 0))
17
+ return null;
18
+ const longest = Math.min(NAVIGATOR_MAX_PX, Math.max(NAVIGATOR_MIN_PX, hostW * ratio));
19
+ const scale = longest / Math.max(worldW, worldH);
20
+ return { width: worldW * scale, height: worldH * scale, scale };
21
+ }
22
+ /** The part of the world the camera shows, as `[x0, y0, x1, y1]`. */
23
+ export function viewportWorldRect(center, zoom, hostW, hostH) {
24
+ const hw = hostW / zoom / 2;
25
+ const hh = hostH / zoom / 2;
26
+ return [center[0] - hw, center[1] - hh, center[0] + hw, center[1] + hh];
27
+ }
28
+ /** A point in the navigator box → world coordinates, clamped to the image. */
29
+ export function navigatorToWorld(px, py, layout, worldW, worldH) {
30
+ const x = Math.min(worldW, Math.max(0, px / layout.scale));
31
+ const y = Math.min(worldH, Math.max(0, py / layout.scale));
32
+ return [x, y];
33
+ }
34
+ export class NapariNavigator {
35
+ constructor(host, camera, worldW, worldH,
36
+ /** Called when the pointer enters the navigator — e.g. to hide a hover tooltip that
37
+ * was describing the canvas underneath. */
38
+ onEnter) {
39
+ this.host = host;
40
+ this.camera = camera;
41
+ this.worldW = worldW;
42
+ this.worldH = worldH;
43
+ this.onEnter = onEnter;
44
+ /** Aborted by destroy() to remove every DOM listener the box holds. */
45
+ this.listeners = new AbortController();
46
+ this.layout = null;
47
+ this.image = null;
48
+ this.dragging = false;
49
+ this.visible = true;
50
+ this.onDown = (e) => {
51
+ stop(e);
52
+ if (e.button !== 0)
53
+ return;
54
+ this.dragging = true;
55
+ this.box.setPointerCapture?.(e.pointerId);
56
+ this.panTo(e);
57
+ };
58
+ this.onMove = (e) => {
59
+ // Always consumed: a hover over the navigator is not a hover over the canvas.
60
+ stop(e);
61
+ if (this.dragging)
62
+ this.panTo(e);
63
+ };
64
+ this.onUp = (e) => {
65
+ if (!this.dragging)
66
+ return;
67
+ stop(e);
68
+ this.dragging = false;
69
+ this.box.releasePointerCapture?.(e.pointerId);
70
+ };
71
+ if (getComputedStyle(host).position === 'static')
72
+ host.style.position = 'relative';
73
+ this.box = document.createElement('div');
74
+ this.box.className = 'napari-navigator';
75
+ Object.assign(this.box.style, {
76
+ position: 'absolute',
77
+ right: '12px',
78
+ bottom: '12px',
79
+ background: 'rgba(0,0,0,0.5)',
80
+ border: '1px solid rgba(255,255,255,0.35)',
81
+ overflow: 'hidden',
82
+ cursor: 'pointer',
83
+ zIndex: '5',
84
+ touchAction: 'none',
85
+ });
86
+ this.canvas = document.createElement('canvas');
87
+ Object.assign(this.canvas.style, { display: 'block', width: '100%', height: '100%' });
88
+ // OSD's default display-region style.
89
+ this.region = document.createElement('div');
90
+ Object.assign(this.region.style, {
91
+ position: 'absolute',
92
+ border: '2px solid #900',
93
+ boxSizing: 'border-box',
94
+ pointerEvents: 'none',
95
+ });
96
+ this.box.append(this.canvas, this.region);
97
+ host.appendChild(this.box);
98
+ // The navigator's own gestures must not also reach the canvas underneath (pan/zoom,
99
+ // region drawing), so every pointer/wheel event is consumed here.
100
+ // One signal for all of them, so destroy() releases every listener at once.
101
+ const signal = this.listeners.signal;
102
+ this.box.addEventListener('pointerdown', this.onDown, { signal });
103
+ this.box.addEventListener('pointermove', this.onMove, { signal });
104
+ this.box.addEventListener('pointerup', this.onUp, { signal });
105
+ this.box.addEventListener('pointercancel', this.onUp, { signal });
106
+ this.box.addEventListener('pointerenter', () => this.onEnter?.(), { signal });
107
+ for (const type of ['wheel', 'click', 'dblclick', 'contextmenu', 'mousedown']) {
108
+ this.box.addEventListener(type, stop, { passive: false, signal });
109
+ }
110
+ this.disconnectCamera = camera.changed.connect(() => this.updateRegion());
111
+ if (typeof ResizeObserver !== 'undefined') {
112
+ this.resizeObserver = new ResizeObserver(() => this.relayout());
113
+ this.resizeObserver.observe(host);
114
+ }
115
+ this.relayout();
116
+ }
117
+ /** The thumbnail to draw: any image of the whole world (a coarse pyramid level). */
118
+ setImage(image) {
119
+ this.image = image;
120
+ this.drawImage();
121
+ }
122
+ /** The image's world size changed (another image was loaded). */
123
+ setWorld(width, height) {
124
+ this.worldW = width;
125
+ this.worldH = height;
126
+ this.relayout();
127
+ }
128
+ setVisible(visible) {
129
+ this.visible = visible;
130
+ this.box.style.display = visible && this.layout ? 'block' : 'none';
131
+ }
132
+ destroy() {
133
+ this.listeners.abort();
134
+ this.disconnectCamera();
135
+ this.resizeObserver?.disconnect();
136
+ this.box.remove();
137
+ }
138
+ relayout() {
139
+ this.layout = navigatorLayout(this.host.clientWidth, this.worldW, this.worldH);
140
+ if (!this.layout) {
141
+ this.box.style.display = 'none';
142
+ return;
143
+ }
144
+ this.box.style.display = this.visible ? 'block' : 'none';
145
+ this.box.style.width = `${this.layout.width}px`;
146
+ this.box.style.height = `${this.layout.height}px`;
147
+ const dpr = typeof devicePixelRatio === 'number' ? devicePixelRatio : 1;
148
+ this.canvas.width = Math.max(1, Math.round(this.layout.width * dpr));
149
+ this.canvas.height = Math.max(1, Math.round(this.layout.height * dpr));
150
+ this.drawImage();
151
+ this.updateRegion();
152
+ }
153
+ drawImage() {
154
+ const ctx = this.canvas.getContext('2d');
155
+ if (!ctx)
156
+ return;
157
+ ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
158
+ if (this.image) {
159
+ ctx.imageSmoothingEnabled = true;
160
+ ctx.drawImage(this.image, 0, 0, this.canvas.width, this.canvas.height);
161
+ }
162
+ }
163
+ /** Draw the viewport rectangle, clipped to the box (as OSD does when zoomed out). */
164
+ updateRegion() {
165
+ const l = this.layout;
166
+ if (!l)
167
+ return;
168
+ const [x0, y0, x1, y1] = viewportWorldRect(this.camera.center, this.camera.zoom, this.host.clientWidth, this.host.clientHeight);
169
+ const left = Math.max(0, x0 * l.scale);
170
+ const top = Math.max(0, y0 * l.scale);
171
+ const right = Math.min(l.width, x1 * l.scale);
172
+ const bottom = Math.min(l.height, y1 * l.scale);
173
+ Object.assign(this.region.style, {
174
+ left: `${left}px`,
175
+ top: `${top}px`,
176
+ width: `${Math.max(2, right - left)}px`,
177
+ height: `${Math.max(2, bottom - top)}px`,
178
+ display: right > left && bottom > top ? 'block' : 'none',
179
+ });
180
+ }
181
+ /** Re-centre the main view on the navigator point under the pointer, keeping the zoom. */
182
+ panTo(e) {
183
+ const l = this.layout;
184
+ if (!l)
185
+ return;
186
+ const rect = this.box.getBoundingClientRect();
187
+ this.camera.center = navigatorToWorld(e.clientX - rect.left, e.clientY - rect.top, l, this.worldW, this.worldH);
188
+ }
189
+ }
190
+ function stop(e) {
191
+ e.stopPropagation();
192
+ if (e.cancelable)
193
+ e.preventDefault();
194
+ }
195
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmFwYXJpLW5hdmlnYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvaW1wbGVtZW50YXRpb25zL25hcGFyaS1qcy9uYXBhcmktbmF2aWdhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7OztHQVFHO0FBa0JILHFGQUFxRjtBQUNyRixNQUFNLENBQUMsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUM7QUFDekMsTUFBTSxnQkFBZ0IsR0FBRyxHQUFHLENBQUM7QUFDN0IsTUFBTSxnQkFBZ0IsR0FBRyxHQUFHLENBQUM7QUFFN0IsK0VBQStFO0FBQy9FLE1BQU0sVUFBVSxlQUFlLENBQzdCLEtBQWEsRUFBRSxNQUFjLEVBQUUsTUFBYyxFQUFFLEtBQUssR0FBRyxvQkFBb0I7SUFFM0UsSUFBSSxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUNoRSxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLGdCQUFnQixFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLEVBQUUsS0FBSyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUM7SUFDdEYsTUFBTSxLQUFLLEdBQUcsT0FBTyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ2pELE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxHQUFHLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxHQUFHLEtBQUssRUFBRSxLQUFLLEVBQUUsQ0FBQztBQUNsRSxDQUFDO0FBRUQscUVBQXFFO0FBQ3JFLE1BQU0sVUFBVSxpQkFBaUIsQ0FDL0IsTUFBaUMsRUFBRSxJQUFZLEVBQUUsS0FBYSxFQUFFLEtBQWE7SUFFN0UsTUFB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Q0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FDekUsQ0FBQztJQUNKLENBQUM7Q0FzQkY7QUFFRCxTQUFTLElBQUksQ0FBQyxDQUFRO0lBQ3BCLENBQUMsQ0FBQyxlQUFlLEVBQUUsQ0FBQztJQUNwQixJQUFJLENBQUMsQ0FBQyxVQUFVO1FBQUUsQ0FBQyxDQUFDLGNBQWMsRUFBRSxDQUFDO0FBQ3ZDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE92ZXJ2aWV3IG5hdmlnYXRvciAobWluaW1hcCkgZm9yIHRoZSBuYXBhcmktanMgMkQgdmlld3MsIG1pcnJvcmluZyB0aGUgT1NEIGJhY2tlbmQnc1xuICogYnVpbHQtaW4gbmF2aWdhdG9yOiBhIHRodW1ibmFpbCBvZiB0aGUgd2hvbGUgaW1hZ2UgaW4gdGhlIGJvdHRvbS1yaWdodCBjb3JuZXIgd2l0aCB0aGVcbiAqIGN1cnJlbnQgdmlld3BvcnQgZHJhd24gb24gaXQuIENsaWNraW5nIHJlLWNlbnRyZXMgdGhlIG1haW4gdmlldyBvbiB0aGF0IHBvaW50IGF0IHRoZVxuICogc2FtZSB6b29tOyBkcmFnZ2luZyBwYW5zIGNvbnRpbnVvdXNseSDigJQgZXhhY3RseSBPU0QncyBiZWhhdmlvdXIuXG4gKlxuICogbmFwYXJpIGtlZXBzIGxheWVycyBhdCBmdWxsLXJlc29sdXRpb24gcGl4ZWwgc2NhbGUsIHNvIHRoZSBjYW1lcmEncyB3b3JsZCB1bml0cyBBUkVcbiAqIGltYWdlIHBpeGVscyBhbmQgdGhlIHRodW1ibmFpbCBtYXBzIG9udG8gYFswLCB3aWR0aCkgw5cgWzAsIGhlaWdodClgIGRpcmVjdGx5LlxuICovXG5cbi8qKiBUaGUgc2xpY2Ugb2YgdGhlIG5hcGFyaSAyRCBjYW1lcmEgdGhlIG5hdmlnYXRvciByZWFkcyBhbmQgZHJpdmVzLiAqL1xuZXhwb3J0IGludGVyZmFjZSBOYXZpZ2F0b3JDYW1lcmEge1xuICBjZW50ZXI6IHJlYWRvbmx5IFtudW1iZXIsIG51bWJlcl07XG4gIHJlYWRvbmx5IHpvb206IG51bWJlcjtcbiAgcmVhZG9ubHkgY2hhbmdlZDogeyBjb25uZWN0KGxpc3RlbmVyOiAoKSA9PiB2b2lkKTogKCkgPT4gdm9pZCB9O1xufVxuXG4vKiogV2hlcmUgdGhlIHRodW1ibmFpbCBzaXRzIGluc2lkZSB0aGUgbmF2aWdhdG9yIGJveCwgaW4gQ1NTIHB4LiAqL1xuZXhwb3J0IGludGVyZmFjZSBOYXZpZ2F0b3JMYXlvdXQge1xuICAvKiogQm94IHNpemUuICovXG4gIHdpZHRoOiBudW1iZXI7XG4gIGhlaWdodDogbnVtYmVyO1xuICAvKiogQ1NTIHB4IHBlciB3b3JsZCB1bml0IChpbWFnZSBwaXhlbCkuICovXG4gIHNjYWxlOiBudW1iZXI7XG59XG5cbi8qKiBGcmFjdGlvbiBvZiB0aGUgaG9zdCdzIHdpZHRoIHRoZSBuYXZpZ2F0b3IgdGFrZXMg4oCUIE9TRCdzIGBuYXZpZ2F0b3JTaXplUmF0aW9gLiAqL1xuZXhwb3J0IGNvbnN0IE5BVklHQVRPUl9TSVpFX1JBVElPID0gMC4xNjtcbmNvbnN0IE5BVklHQVRPUl9NSU5fUFggPSAxMTA7XG5jb25zdCBOQVZJR0FUT1JfTUFYX1BYID0gMzAwO1xuXG4vKiogQm94IHNpemUgYW5kIHNjYWxlIGZvciBhIGB3b3JsZFcgw5cgd29ybGRIYCBpbWFnZSBpbiBhIGhvc3QgYGhvc3RXYCB3aWRlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG5hdmlnYXRvckxheW91dChcbiAgaG9zdFc6IG51bWJlciwgd29ybGRXOiBudW1iZXIsIHdvcmxkSDogbnVtYmVyLCByYXRpbyA9IE5BVklHQVRPUl9TSVpFX1JBVElPLFxuKTogTmF2aWdhdG9yTGF5b3V0IHwgbnVsbCB7XG4gIGlmICghKHdvcmxkVyA+IDApIHx8ICEod29ybGRIID4gMCkgfHwgIShob3N0VyA+IDApKSByZXR1cm4gbnVsbDtcbiAgY29uc3QgbG9uZ2VzdCA9IE1hdGgubWluKE5BVklHQVRPUl9NQVhfUFgsIE1hdGgubWF4KE5BVklHQVRPUl9NSU5fUFgsIGhvc3RXICogcmF0aW8pKTtcbiAgY29uc3Qgc2NhbGUgPSBsb25nZXN0IC8gTWF0aC5tYXgod29ybGRXLCB3b3JsZEgpO1xuICByZXR1cm4geyB3aWR0aDogd29ybGRXICogc2NhbGUsIGhlaWdodDogd29ybGRIICogc2NhbGUsIHNjYWxlIH07XG59XG5cbi8qKiBUaGUgcGFydCBvZiB0aGUgd29ybGQgdGhlIGNhbWVyYSBzaG93cywgYXMgYFt4MCwgeTAsIHgxLCB5MV1gLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZpZXdwb3J0V29ybGRSZWN0KFxuICBjZW50ZXI6IHJlYWRvbmx5IFtudW1iZXIsIG51bWJlcl0sIHpvb206IG51bWJlciwgaG9zdFc6IG51bWJlciwgaG9zdEg6IG51bWJlcixcbik6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgY29uc3QgaHcgPSBob3N0VyAvIHpvb20gLyAyO1xuICBjb25zdCBoaCA9IGhvc3RIIC8gem9vbSAvIDI7XG4gIHJldHVybiBbY2VudGVyWzBdIC0gaHcsIGNlbnRlclsxXSAtIGhoLCBjZW50ZXJbMF0gKyBodywgY2VudGVyWzFdICsgaGhdO1xufVxuXG4vKiogQSBwb2ludCBpbiB0aGUgbmF2aWdhdG9yIGJveCDihpIgd29ybGQgY29vcmRpbmF0ZXMsIGNsYW1wZWQgdG8gdGhlIGltYWdlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG5hdmlnYXRvclRvV29ybGQoXG4gIHB4OiBudW1iZXIsIHB5OiBudW1iZXIsIGxheW91dDogTmF2aWdhdG9yTGF5b3V0LCB3b3JsZFc6IG51bWJlciwgd29ybGRIOiBudW1iZXIsXG4pOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgY29uc3QgeCA9IE1hdGgubWluKHdvcmxkVywgTWF0aC5tYXgoMCwgcHggLyBsYXlvdXQuc2NhbGUpKTtcbiAgY29uc3QgeSA9IE1hdGgubWluKHdvcmxkSCwgTWF0aC5tYXgoMCwgcHkgLyBsYXlvdXQuc2NhbGUpKTtcbiAgcmV0dXJuIFt4LCB5XTtcbn1cblxuZXhwb3J0IGNsYXNzIE5hcGFyaU5hdmlnYXRvciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgYm94OiBIVE1MRGl2RWxlbWVudDtcbiAgcHJpdmF0ZSByZWFkb25seSBjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50O1xuICBwcml2YXRlIHJlYWRvbmx5IHJlZ2lvbjogSFRNTERpdkVsZW1lbnQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgZGlzY29ubmVjdENhbWVyYTogKCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSByZWFkb25seSByZXNpemVPYnNlcnZlcj86IFJlc2l6ZU9ic2VydmVyO1xuICAvKiogQWJvcnRlZCBieSBkZXN0cm95KCkgdG8gcmVtb3ZlIGV2ZXJ5IERPTSBsaXN0ZW5lciB0aGUgYm94IGhvbGRzLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGxpc3RlbmVycyA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgcHJpdmF0ZSBsYXlvdXQ6IE5hdmlnYXRvckxheW91dCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGltYWdlOiBDYW52YXNJbWFnZVNvdXJjZSB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGRyYWdnaW5nID0gZmFsc2U7XG4gIHByaXZhdGUgdmlzaWJsZSA9IHRydWU7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBob3N0OiBIVE1MRWxlbWVudCxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNhbWVyYTogTmF2aWdhdG9yQ2FtZXJhLFxuICAgIHByaXZhdGUgd29ybGRXOiBudW1iZXIsXG4gICAgcHJpdmF0ZSB3b3JsZEg6IG51bWJlcixcbiAgICAvKiogQ2FsbGVkIHdoZW4gdGhlIHBvaW50ZXIgZW50ZXJzIHRoZSBuYXZpZ2F0b3Ig4oCUIGUuZy4gdG8gaGlkZSBhIGhvdmVyIHRvb2x0aXAgdGhhdFxuICAgICAqICB3YXMgZGVzY3JpYmluZyB0aGUgY2FudmFzIHVuZGVybmVhdGguICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBvbkVudGVyPzogKCkgPT4gdm9pZCxcbiAgKSB7XG4gICAgaWYgKGdldENvbXB1dGVkU3R5bGUoaG9zdCkucG9zaXRpb24gPT09ICdzdGF0aWMnKSBob3N0LnN0eWxlLnBvc2l0aW9uID0gJ3JlbGF0aXZlJztcblxuICAgIHRoaXMuYm94ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgdGhpcy5ib3guY2xhc3NOYW1lID0gJ25hcGFyaS1uYXZpZ2F0b3InO1xuICAgIE9iamVjdC5hc3NpZ24odGhpcy5ib3guc3R5bGUsIHtcbiAgICAgIHBvc2l0aW9uOiAnYWJzb2x1dGUnLFxuICAgICAgcmlnaHQ6ICcxMnB4JyxcbiAgICAgIGJvdHRvbTogJzEycHgnLFxuICAgICAgYmFja2dyb3VuZDogJ3JnYmEoMCwwLDAsMC41KScsXG4gICAgICBib3JkZXI6ICcxcHggc29saWQgcmdiYSgyNTUsMjU1LDI1NSwwLjM1KScsXG4gICAgICBvdmVyZmxvdzogJ2hpZGRlbicsXG4gICAgICBjdXJzb3I6ICdwb2ludGVyJyxcbiAgICAgIHpJbmRleDogJzUnLFxuICAgICAgdG91Y2hBY3Rpb246ICdub25lJyxcbiAgICB9IHNhdGlzZmllcyBQYXJ0aWFsPENTU1N0eWxlRGVjbGFyYXRpb24+KTtcblxuICAgIHRoaXMuY2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgT2JqZWN0LmFzc2lnbih0aGlzLmNhbnZhcy5zdHlsZSwgeyBkaXNwbGF5OiAnYmxvY2snLCB3aWR0aDogJzEwMCUnLCBoZWlnaHQ6ICcxMDAlJyB9KTtcblxuICAgIC8vIE9TRCdzIGRlZmF1bHQgZGlzcGxheS1yZWdpb24gc3R5bGUuXG4gICAgdGhpcy5yZWdpb24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBPYmplY3QuYXNzaWduKHRoaXMucmVnaW9uLnN0eWxlLCB7XG4gICAgICBwb3NpdGlvbjogJ2Fic29sdXRlJyxcbiAgICAgIGJvcmRlcjogJzJweCBzb2xpZCAjOTAwJyxcbiAgICAgIGJveFNpemluZzogJ2JvcmRlci1ib3gnLFxuICAgICAgcG9pbnRlckV2ZW50czogJ25vbmUnLFxuICAgIH0gc2F0aXNmaWVzIFBhcnRpYWw8Q1NTU3R5bGVEZWNsYXJhdGlvbj4pO1xuXG4gICAgdGhpcy5ib3guYXBwZW5kKHRoaXMuY2FudmFzLCB0aGlzLnJlZ2lvbik7XG4gICAgaG9zdC5hcHBlbmRDaGlsZCh0aGlzLmJveCk7XG5cbiAgICAvLyBUaGUgbmF2aWdhdG9yJ3Mgb3duIGdlc3R1cmVzIG11c3Qgbm90IGFsc28gcmVhY2ggdGhlIGNhbnZhcyB1bmRlcm5lYXRoIChwYW4vem9vbSxcbiAgICAvLyByZWdpb24gZHJhd2luZyksIHNvIGV2ZXJ5IHBvaW50ZXIvd2hlZWwgZXZlbnQgaXMgY29uc3VtZWQgaGVyZS5cbiAgICAvLyBPbmUgc2lnbmFsIGZvciBhbGwgb2YgdGhlbSwgc28gZGVzdHJveSgpIHJlbGVhc2VzIGV2ZXJ5IGxpc3RlbmVyIGF0IG9uY2UuXG4gICAgY29uc3Qgc2lnbmFsID0gdGhpcy5saXN0ZW5lcnMuc2lnbmFsO1xuICAgIHRoaXMuYm94LmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJkb3duJywgdGhpcy5vbkRvd24sIHsgc2lnbmFsIH0pO1xuICAgIHRoaXMuYm94LmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJtb3ZlJywgdGhpcy5vbk1vdmUsIHsgc2lnbmFsIH0pO1xuICAgIHRoaXMuYm94LmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJ1cCcsIHRoaXMub25VcCwgeyBzaWduYWwgfSk7XG4gICAgdGhpcy5ib3guYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcmNhbmNlbCcsIHRoaXMub25VcCwgeyBzaWduYWwgfSk7XG4gICAgdGhpcy5ib3guYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcmVudGVyJywgKCkgPT4gdGhpcy5vbkVudGVyPy4oKSwgeyBzaWduYWwgfSk7XG4gICAgZm9yIChjb25zdCB0eXBlIG9mIFsnd2hlZWwnLCAnY2xpY2snLCAnZGJsY2xpY2snLCAnY29udGV4dG1lbnUnLCAnbW91c2Vkb3duJ10gYXMgY29uc3QpIHtcbiAgICAgIHRoaXMuYm94LmFkZEV2ZW50TGlzdGVuZXIodHlwZSwgc3RvcCwgeyBwYXNzaXZlOiBmYWxzZSwgc2lnbmFsIH0pO1xuICAgIH1cblxuICAgIHRoaXMuZGlzY29ubmVjdENhbWVyYSA9IGNhbWVyYS5jaGFuZ2VkLmNvbm5lY3QoKCkgPT4gdGhpcy51cGRhdGVSZWdpb24oKSk7XG4gICAgaWYgKHR5cGVvZiBSZXNpemVPYnNlcnZlciAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHRoaXMucmVzaXplT2JzZXJ2ZXIgPSBuZXcgUmVzaXplT2JzZXJ2ZXIoKCkgPT4gdGhpcy5yZWxheW91dCgpKTtcbiAgICAgIHRoaXMucmVzaXplT2JzZXJ2ZXIub2JzZXJ2ZShob3N0KTtcbiAgICB9XG4gICAgdGhpcy5yZWxheW91dCgpO1xuICB9XG5cbiAgLyoqIFRoZSB0aHVtYm5haWwgdG8gZHJhdzogYW55IGltYWdlIG9mIHRoZSB3aG9sZSB3b3JsZCAoYSBjb2Fyc2UgcHlyYW1pZCBsZXZlbCkuICovXG4gIHNldEltYWdlKGltYWdlOiBDYW52YXNJbWFnZVNvdXJjZSB8IG51bGwpOiB2b2lkIHtcbiAgICB0aGlzLmltYWdlID0gaW1hZ2U7XG4gICAgdGhpcy5kcmF3SW1hZ2UoKTtcbiAgfVxuXG4gIC8qKiBUaGUgaW1hZ2UncyB3b3JsZCBzaXplIGNoYW5nZWQgKGFub3RoZXIgaW1hZ2Ugd2FzIGxvYWRlZCkuICovXG4gIHNldFdvcmxkKHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy53b3JsZFcgPSB3aWR0aDtcbiAgICB0aGlzLndvcmxkSCA9IGhlaWdodDtcbiAgICB0aGlzLnJlbGF5b3V0KCk7XG4gIH1cblxuICBzZXRWaXNpYmxlKHZpc2libGU6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLnZpc2libGUgPSB2aXNpYmxlO1xuICAgIHRoaXMuYm94LnN0eWxlLmRpc3BsYXkgPSB2aXNpYmxlICYmIHRoaXMubGF5b3V0ID8gJ2Jsb2NrJyA6ICdub25lJztcbiAgfVxuXG4gIGRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5saXN0ZW5lcnMuYWJvcnQoKTtcbiAgICB0aGlzLmRpc2Nvbm5lY3RDYW1lcmEoKTtcbiAgICB0aGlzLnJlc2l6ZU9ic2VydmVyPy5kaXNjb25uZWN0KCk7XG4gICAgdGhpcy5ib3gucmVtb3ZlKCk7XG4gIH1cblxuICBwcml2YXRlIHJlbGF5b3V0KCk6IHZvaWQge1xuICAgIHRoaXMubGF5b3V0ID0gbmF2aWdhdG9yTGF5b3V0KHRoaXMuaG9zdC5jbGllbnRXaWR0aCwgdGhpcy53b3JsZFcsIHRoaXMud29ybGRIKTtcbiAgICBpZiAoIXRoaXMubGF5b3V0KSB7XG4gICAgICB0aGlzLmJveC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmJveC5zdHlsZS5kaXNwbGF5ID0gdGhpcy52aXNpYmxlID8gJ2Jsb2NrJyA6ICdub25lJztcbiAgICB0aGlzLmJveC5zdHlsZS53aWR0aCA9IGAke3RoaXMubGF5b3V0LndpZHRofXB4YDtcbiAgICB0aGlzLmJveC5zdHlsZS5oZWlnaHQgPSBgJHt0aGlzLmxheW91dC5oZWlnaHR9cHhgO1xuICAgIGNvbnN0IGRwciA9IHR5cGVvZiBkZXZpY2VQaXhlbFJhdGlvID09PSAnbnVtYmVyJyA/IGRldmljZVBpeGVsUmF0aW8gOiAxO1xuICAgIHRoaXMuY2FudmFzLndpZHRoID0gTWF0aC5tYXgoMSwgTWF0aC5yb3VuZCh0aGlzLmxheW91dC53aWR0aCAqIGRwcikpO1xuICAgIHRoaXMuY2FudmFzLmhlaWdodCA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQodGhpcy5sYXlvdXQuaGVpZ2h0ICogZHByKSk7XG4gICAgdGhpcy5kcmF3SW1hZ2UoKTtcbiAgICB0aGlzLnVwZGF0ZVJlZ2lvbigpO1xuICB9XG5cbiAgcHJpdmF0ZSBkcmF3SW1hZ2UoKTogdm9pZCB7XG4gICAgY29uc3QgY3R4ID0gdGhpcy5jYW52YXMuZ2V0Q29udGV4dCgnMmQnKTtcbiAgICBpZiAoIWN0eCkgcmV0dXJuO1xuICAgIGN0eC5jbGVhclJlY3QoMCwgMCwgdGhpcy5jYW52YXMud2lkdGgsIHRoaXMuY2FudmFzLmhlaWdodCk7XG4gICAgaWYgKHRoaXMuaW1hZ2UpIHtcbiAgICAgIGN0eC5pbWFnZVNtb290aGluZ0VuYWJsZWQgPSB0cnVlO1xuICAgICAgY3R4LmRyYXdJbWFnZSh0aGlzLmltYWdlLCAwLCAwLCB0aGlzLmNhbnZhcy53aWR0aCwgdGhpcy5jYW52YXMuaGVpZ2h0KTtcbiAgICB9XG4gIH1cblxuICAvKiogRHJhdyB0aGUgdmlld3BvcnQgcmVjdGFuZ2xlLCBjbGlwcGVkIHRvIHRoZSBib3ggKGFzIE9TRCBkb2VzIHdoZW4gem9vbWVkIG91dCkuICovXG4gIHByaXZhdGUgdXBkYXRlUmVnaW9uKCk6IHZvaWQge1xuICAgIGNvbnN0IGwgPSB0aGlzLmxheW91dDtcbiAgICBpZiAoIWwpIHJldHVybjtcbiAgICBjb25zdCBbeDAsIHkwLCB4MSwgeTFdID0gdmlld3BvcnRXb3JsZFJlY3QoXG4gICAgICB0aGlzLmNhbWVyYS5jZW50ZXIsIHRoaXMuY2FtZXJhLnpvb20sIHRoaXMuaG9zdC5jbGllbnRXaWR0aCwgdGhpcy5ob3N0LmNsaWVudEhlaWdodCxcbiAgICApO1xuICAgIGNvbnN0IGxlZnQgPSBNYXRoLm1heCgwLCB4MCAqIGwuc2NhbGUpO1xuICAgIGNvbnN0IHRvcCA9IE1hdGgubWF4KDAsIHkwICogbC5zY2FsZSk7XG4gICAgY29uc3QgcmlnaHQgPSBNYXRoLm1pbihsLndpZHRoLCB4MSAqIGwuc2NhbGUpO1xuICAgIGNvbnN0IGJvdHRvbSA9IE1hdGgubWluKGwuaGVpZ2h0LCB5MSAqIGwuc2NhbGUpO1xuICAgIE9iamVjdC5hc3NpZ24odGhpcy5yZWdpb24uc3R5bGUsIHtcbiAgICAgIGxlZnQ6IGAke2xlZnR9cHhgLFxuICAgICAgdG9wOiBgJHt0b3B9cHhgLFxuICAgICAgd2lkdGg6IGAke01hdGgubWF4KDIsIHJpZ2h0IC0gbGVmdCl9cHhgLFxuICAgICAgaGVpZ2h0OiBgJHtNYXRoLm1heCgyLCBib3R0b20gLSB0b3ApfXB4YCxcbiAgICAgIGRpc3BsYXk6IHJpZ2h0ID4gbGVmdCAmJiBib3R0b20gPiB0b3AgPyAnYmxvY2snIDogJ25vbmUnLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIFJlLWNlbnRyZSB0aGUgbWFpbiB2aWV3IG9uIHRoZSBuYXZpZ2F0b3IgcG9pbnQgdW5kZXIgdGhlIHBvaW50ZXIsIGtlZXBpbmcgdGhlIHpvb20uICovXG4gIHByaXZhdGUgcGFuVG8oZTogUG9pbnRlckV2ZW50KTogdm9pZCB7XG4gICAgY29uc3QgbCA9IHRoaXMubGF5b3V0O1xuICAgIGlmICghbCkgcmV0dXJuO1xuICAgIGNvbnN0IHJlY3QgPSB0aGlzLmJveC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICB0aGlzLmNhbWVyYS5jZW50ZXIgPSBuYXZpZ2F0b3JUb1dvcmxkKFxuICAgICAgZS5jbGllbnRYIC0gcmVjdC5sZWZ0LCBlLmNsaWVudFkgLSByZWN0LnRvcCwgbCwgdGhpcy53b3JsZFcsIHRoaXMud29ybGRILFxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIHJlYWRvbmx5IG9uRG93biA9IChlOiBQb2ludGVyRXZlbnQpOiB2b2lkID0+IHtcbiAgICBzdG9wKGUpO1xuICAgIGlmIChlLmJ1dHRvbiAhPT0gMCkgcmV0dXJuO1xuICAgIHRoaXMuZHJhZ2dpbmcgPSB0cnVlO1xuICAgIHRoaXMuYm94LnNldFBvaW50ZXJDYXB0dXJlPy4oZS5wb2ludGVySWQpO1xuICAgIHRoaXMucGFuVG8oZSk7XG4gIH07XG5cbiAgcHJpdmF0ZSByZWFkb25seSBvbk1vdmUgPSAoZTogUG9pbnRlckV2ZW50KTogdm9pZCA9PiB7XG4gICAgLy8gQWx3YXlzIGNvbnN1bWVkOiBhIGhvdmVyIG92ZXIgdGhlIG5hdmlnYXRvciBpcyBub3QgYSBob3ZlciBvdmVyIHRoZSBjYW52YXMuXG4gICAgc3RvcChlKTtcbiAgICBpZiAodGhpcy5kcmFnZ2luZykgdGhpcy5wYW5UbyhlKTtcbiAgfTtcblxuICBwcml2YXRlIHJlYWRvbmx5IG9uVXAgPSAoZTogUG9pbnRlckV2ZW50KTogdm9pZCA9PiB7XG4gICAgaWYgKCF0aGlzLmRyYWdnaW5nKSByZXR1cm47XG4gICAgc3RvcChlKTtcbiAgICB0aGlzLmRyYWdnaW5nID0gZmFsc2U7XG4gICAgdGhpcy5ib3gucmVsZWFzZVBvaW50ZXJDYXB0dXJlPy4oZS5wb2ludGVySWQpO1xuICB9O1xufVxuXG5mdW5jdGlvbiBzdG9wKGU6IEV2ZW50KTogdm9pZCB7XG4gIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gIGlmIChlLmNhbmNlbGFibGUpIGUucHJldmVudERlZmF1bHQoKTtcbn1cbiJdfQ==