@sumaris-net/ngx-components 18.25.5 → 18.25.6
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/doc/changelog.md +8 -0
- package/esm2022/public_api.mjs +3 -1
- package/esm2022/src/app/shared/image/gallery/image-gallery.component.mjs +82 -82
- package/esm2022/src/app/shared/image/gallery/image-gallery.model.mjs +2 -0
- package/esm2022/src/app/shared/image/gallery/image-gallery.module.mjs +8 -3
- package/esm2022/src/app/shared/image/gallery/slideshow/image-gallery-slideshow.component.mjs +685 -0
- package/esm2022/src/app/shared/image/gallery/testing/gallery.testing.mjs +16 -9
- package/fesm2022/sumaris-net.ngx-components.mjs +767 -86
- package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
- package/package.json +1 -1
- package/public_api.d.ts +2 -0
- package/src/app/shared/image/gallery/image-gallery.component.d.ts +42 -21
- package/src/app/shared/image/gallery/image-gallery.model.d.ts +11 -0
- package/src/app/shared/image/gallery/image-gallery.module.d.ts +10 -9
- package/src/app/shared/image/gallery/slideshow/image-gallery-slideshow.component.d.ts +149 -0
- package/src/app/shared/image/gallery/testing/gallery.testing.d.ts +6 -1
- package/src/assets/i18n/en-US.json +6 -0
- package/src/assets/i18n/en.json +6 -0
- package/src/assets/i18n/fr.json +6 -0
- package/src/assets/manifest.json +1 -1
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
import { booleanAttribute, ChangeDetectionStrategy, Component, EventEmitter, HostListener, inject, Input, Output, ViewChild, } from '@angular/core';
|
|
2
|
+
import { IonicSlides } from '@ionic/angular';
|
|
3
|
+
import { isNilOrBlank } from '../../../functions';
|
|
4
|
+
import { createPromiseEventEmitter, emitPromiseEvent } from '../../../events';
|
|
5
|
+
import { Alerts } from '../../../alerts';
|
|
6
|
+
import { APP_SHOW_TOOLTIP } from '../../../constants';
|
|
7
|
+
import * as i0 from "@angular/core";
|
|
8
|
+
import * as i1 from "@ionic/angular";
|
|
9
|
+
import * as i2 from "@ngx-translate/core";
|
|
10
|
+
import * as i3 from "@angular/common";
|
|
11
|
+
import * as i4 from "@angular/material/icon";
|
|
12
|
+
import * as i5 from "@angular/material/tooltip";
|
|
13
|
+
import * as i6 from "../../../pipes/property.pipes";
|
|
14
|
+
import * as i7 from "../../../pipes/arrays.pipe";
|
|
15
|
+
import * as i8 from "../../../pipes/url.pipes";
|
|
16
|
+
/**
|
|
17
|
+
* The zoom/pan/rotate/crop slideshow shown when clicking an image in `AppImageGalleryComponent`.
|
|
18
|
+
* Extracted into its own component so the exact same slideshow can be presented either inside an
|
|
19
|
+
* `ion-modal` (default), or inline as an overlay directly within the gallery, without any duplication.
|
|
20
|
+
*/
|
|
21
|
+
export class AppImageGallerySlideshowComponent {
|
|
22
|
+
alterCtrl;
|
|
23
|
+
translate;
|
|
24
|
+
cd;
|
|
25
|
+
activeSlideIndex = 0;
|
|
26
|
+
swiperModules = [IonicSlides];
|
|
27
|
+
_swiper;
|
|
28
|
+
// Mouse wheel zoom & rotation state
|
|
29
|
+
_zoomState = { scale: 1, tx: 0, ty: 0 };
|
|
30
|
+
_rotation = 0; // degrees: 0, 90, 180, 270
|
|
31
|
+
_dragState = null;
|
|
32
|
+
_wheelListener = null;
|
|
33
|
+
_wheelEl = null;
|
|
34
|
+
// Crop & rotate state. Rotation is always available; the crop grid/handles only show once the
|
|
35
|
+
// crop tool has been explicitly activated (fab button). Validate/cancel show up once something changed.
|
|
36
|
+
_cropToolActive = false;
|
|
37
|
+
_cropRect = { x: 0, y: 0, width: 1, height: 1 };
|
|
38
|
+
_cropOverlayRect = null;
|
|
39
|
+
_cropDragState = null;
|
|
40
|
+
_cropHandles = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
|
|
41
|
+
rows = [];
|
|
42
|
+
selectedIndex = 0;
|
|
43
|
+
mobile = false;
|
|
44
|
+
readOnly = false;
|
|
45
|
+
disabled = false;
|
|
46
|
+
debug = false;
|
|
47
|
+
mouseZoomEnabled = false;
|
|
48
|
+
wheelZoomStep = 0.3;
|
|
49
|
+
maxZoomRatio = 3;
|
|
50
|
+
rotationEnabled = true;
|
|
51
|
+
cropEnabled = true;
|
|
52
|
+
imageExportFormat = 'png';
|
|
53
|
+
imageExportQuality = 0.92;
|
|
54
|
+
imageSizeQueryParam = 'size';
|
|
55
|
+
modalImageSize;
|
|
56
|
+
/** Requests deletion of the given row. The caller (gallery) owns the confirmation/deletion logic and resolves the promise with whether it actually happened. */
|
|
57
|
+
deleteRequested = createPromiseEventEmitter();
|
|
58
|
+
closeRequested = new EventEmitter();
|
|
59
|
+
afterEditImage = new EventEmitter();
|
|
60
|
+
afterEditRow = new EventEmitter();
|
|
61
|
+
swiperRef;
|
|
62
|
+
showTooltip = inject(APP_SHOW_TOOLTIP, { optional: true });
|
|
63
|
+
get hasPendingEdit() {
|
|
64
|
+
const r = this._cropRect;
|
|
65
|
+
return this._rotation !== 0 || r.x !== 0 || r.y !== 0 || r.width !== 1 || r.height !== 1;
|
|
66
|
+
}
|
|
67
|
+
constructor(alterCtrl, translate, cd) {
|
|
68
|
+
this.alterCtrl = alterCtrl;
|
|
69
|
+
this.translate = translate;
|
|
70
|
+
this.cd = cd;
|
|
71
|
+
}
|
|
72
|
+
ngOnDestroy() {
|
|
73
|
+
this._cleanupWheelZoom();
|
|
74
|
+
}
|
|
75
|
+
getSwiper(swiperElement) {
|
|
76
|
+
this._swiper = (swiperElement || this.swiperRef?.nativeElement)?.swiper || this._swiper;
|
|
77
|
+
return this._swiper;
|
|
78
|
+
}
|
|
79
|
+
onSlideChange(swiperElement) {
|
|
80
|
+
const swiper = this.getSwiper(swiperElement);
|
|
81
|
+
this.activeSlideIndex = swiper.activeIndex;
|
|
82
|
+
this._resetZoom(swiperElement);
|
|
83
|
+
this._updateSwiperTouchMove(swiperElement);
|
|
84
|
+
this.markForCheck();
|
|
85
|
+
}
|
|
86
|
+
onSwiperInit(swiperElement) {
|
|
87
|
+
this._resetZoom();
|
|
88
|
+
if (this.selectedIndex > 0) {
|
|
89
|
+
if (this.debug)
|
|
90
|
+
console.debug(`[image-gallery-slideshow] Initializing slideshow, selectedIndex=${this.selectedIndex}`);
|
|
91
|
+
// Use a setTimeout otherwise the swiper cannot slide
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
const swiper = this.getSwiper(swiperElement);
|
|
94
|
+
swiper.slideTo(this.selectedIndex, 0);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
this._setupWheelZoom(swiperElement);
|
|
98
|
+
this._updateSwiperTouchMove(swiperElement);
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
this._updateEditImageSize(swiperElement);
|
|
101
|
+
this._updateCropOverlayPosition(swiperElement);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Swiper's own touch/mouse-drag-to-swipe gesture is captured very early (before our crop overlay's
|
|
106
|
+
* own mousedown handler can stop it), so it must be disabled upfront whenever the crop tool is active
|
|
107
|
+
* -- otherwise dragging inside the crop selection ends up sliding to the next/previous image instead.
|
|
108
|
+
* Prev/next arrow buttons keep working, since they call the Swiper API directly.
|
|
109
|
+
*/
|
|
110
|
+
_updateSwiperTouchMove(swiperElement) {
|
|
111
|
+
const swiper = this.getSwiper(swiperElement);
|
|
112
|
+
if (!swiper)
|
|
113
|
+
return;
|
|
114
|
+
swiper.allowTouchMove = !this._cropToolActive;
|
|
115
|
+
}
|
|
116
|
+
async zoom(swiperElement, zoomIn) {
|
|
117
|
+
if (this.debug)
|
|
118
|
+
console.debug('[image-gallery-slideshow] zoom ' + (zoomIn ? 'in' : 'out'), swiperElement);
|
|
119
|
+
if (this.mouseZoomEnabled) {
|
|
120
|
+
const delta = zoomIn ? this.wheelZoomStep : -this.wheelZoomStep;
|
|
121
|
+
const newScale = Math.max(1, Math.min(this._zoomState.scale + delta, this.maxZoomRatio));
|
|
122
|
+
if (newScale === this._zoomState.scale)
|
|
123
|
+
return;
|
|
124
|
+
if (newScale === 1) {
|
|
125
|
+
this._zoomState = { scale: 1, tx: 0, ty: 0 };
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this._zoomState.scale = newScale;
|
|
129
|
+
this._clampTranslations(swiperElement);
|
|
130
|
+
}
|
|
131
|
+
this._applyZoom(swiperElement);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const swiper = this.getSwiper(swiperElement);
|
|
135
|
+
if (zoomIn)
|
|
136
|
+
swiper.zoom.in();
|
|
137
|
+
else
|
|
138
|
+
swiper.zoom.out();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async requestDelete(swiperElement) {
|
|
142
|
+
const swiper = this.getSwiper(swiperElement);
|
|
143
|
+
const currentRow = this.rows.find((row, index) => index === swiper.activeIndex);
|
|
144
|
+
if (!currentRow)
|
|
145
|
+
return false; // Row not found
|
|
146
|
+
let deleted = false;
|
|
147
|
+
try {
|
|
148
|
+
deleted = await emitPromiseEvent(this.deleteRequested, 'delete', { detail: { row: currentRow } });
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
if (err === 'CANCELLED')
|
|
152
|
+
return false; // User cancel
|
|
153
|
+
throw err;
|
|
154
|
+
}
|
|
155
|
+
if (deleted) {
|
|
156
|
+
if (!this.rows?.length) {
|
|
157
|
+
this.closeRequested.emit();
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
this._resetZoom(swiperElement);
|
|
161
|
+
swiper.update();
|
|
162
|
+
this.markForCheck();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return deleted;
|
|
166
|
+
}
|
|
167
|
+
async slidePrev(swiperElement) {
|
|
168
|
+
const swiper = this.getSwiper(swiperElement);
|
|
169
|
+
const activeIndex = swiper.activeIndex;
|
|
170
|
+
if (activeIndex === 0) {
|
|
171
|
+
// Loop to last
|
|
172
|
+
await swiper.slideTo(this.rows.length - 1, 0 /*no transition, to avoid a "revert" effect*/);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
await swiper.slidePrev();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async slideNext(swiperElement) {
|
|
179
|
+
const swiper = this.getSwiper(swiperElement);
|
|
180
|
+
const activeIndex = swiper.activeIndex;
|
|
181
|
+
if (activeIndex === this.rows.length - 1) {
|
|
182
|
+
// Loop to first
|
|
183
|
+
await swiper.slideTo(0, 0 /*no transition, to avoid a "revert" effect*/);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
await swiper.slideNext();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
getImageSizeQueryParams() {
|
|
190
|
+
if (!this.imageSizeQueryParam || isNilOrBlank(this.modalImageSize))
|
|
191
|
+
return undefined;
|
|
192
|
+
return {
|
|
193
|
+
[this.imageSizeQueryParam]: this.modalImageSize,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// --- Crop & rotate tools: always available (no "enter edit mode" step) ---
|
|
197
|
+
// Validate/Cancel only appear once the user actually changed something (see `hasPendingEdit`).
|
|
198
|
+
discardEdit(swiperElement) {
|
|
199
|
+
this._resetEditState(swiperElement);
|
|
200
|
+
}
|
|
201
|
+
resetCrop(_swiperElement) {
|
|
202
|
+
this._cropRect = { x: 0, y: 0, width: 1, height: 1 };
|
|
203
|
+
this.markForCheck();
|
|
204
|
+
}
|
|
205
|
+
toggleCropTool(swiperElement) {
|
|
206
|
+
if (!this.cropEnabled || this.readOnly)
|
|
207
|
+
return;
|
|
208
|
+
this._cropToolActive = !this._cropToolActive;
|
|
209
|
+
// Discard any pending crop selection when hiding the tool
|
|
210
|
+
if (!this._cropToolActive) {
|
|
211
|
+
this._cropRect = { x: 0, y: 0, width: 1, height: 1 };
|
|
212
|
+
this._cropDragState = null;
|
|
213
|
+
}
|
|
214
|
+
this._updateSwiperTouchMove(swiperElement);
|
|
215
|
+
this.markForCheck();
|
|
216
|
+
if (this._cropToolActive) {
|
|
217
|
+
// Wait for the crop overlay to be rendered, then compute its position
|
|
218
|
+
setTimeout(() => this._updateCropOverlayPosition(swiperElement));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async validateEdit(swiperElement) {
|
|
222
|
+
const swiper = this.getSwiper(swiperElement);
|
|
223
|
+
const currentRow = this.rows.find((row, index) => index === swiper.activeIndex);
|
|
224
|
+
if (!currentRow)
|
|
225
|
+
return;
|
|
226
|
+
const image = currentRow.currentData;
|
|
227
|
+
const format = this.imageExportFormat;
|
|
228
|
+
const mimeType = format === 'jpeg' ? 'image/jpeg' : 'image/png';
|
|
229
|
+
const quality = format === 'jpeg' ? this.imageExportQuality : undefined;
|
|
230
|
+
try {
|
|
231
|
+
const canvas = await this._renderEditedImage(image, this._rotation, this._cropRect);
|
|
232
|
+
const dataUrl = canvas.toDataURL(mimeType, quality);
|
|
233
|
+
const blob = await new Promise((resolve, reject) => {
|
|
234
|
+
canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('canvas.toBlob() failed'))), mimeType, quality);
|
|
235
|
+
});
|
|
236
|
+
// Update the row, so the slideshow and thumbnail reflect the change immediately
|
|
237
|
+
image.dataUrl = dataUrl;
|
|
238
|
+
image.url = null;
|
|
239
|
+
this.afterEditImage.emit({ row: currentRow, image, dataUrl, blob, format });
|
|
240
|
+
this.afterEditRow.emit(currentRow);
|
|
241
|
+
this._resetEditState(swiperElement);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
console.error('[image-gallery-slideshow] Failed to export the edited image', err);
|
|
245
|
+
await Alerts.showError('IMAGE.GALLERY.ERROR_EDIT_IMAGE', this.alterCtrl, this.translate);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
_resetEditState(swiperElement) {
|
|
249
|
+
this._rotation = 0;
|
|
250
|
+
this._cropRect = { x: 0, y: 0, width: 1, height: 1 };
|
|
251
|
+
this._cropDragState = null;
|
|
252
|
+
this._cropToolActive = false;
|
|
253
|
+
this._applyZoom(swiperElement);
|
|
254
|
+
this._updateEditImageSize(swiperElement);
|
|
255
|
+
this._updateSwiperTouchMove(swiperElement);
|
|
256
|
+
this.markForCheck();
|
|
257
|
+
}
|
|
258
|
+
onWindowResize() {
|
|
259
|
+
const swiperElement = this.swiperRef?.nativeElement;
|
|
260
|
+
if (!swiperElement)
|
|
261
|
+
return;
|
|
262
|
+
this._updateEditImageSize(swiperElement);
|
|
263
|
+
this._updateCropOverlayPosition(swiperElement);
|
|
264
|
+
}
|
|
265
|
+
rotate(swiperElement, direction) {
|
|
266
|
+
if (!this.rotationEnabled)
|
|
267
|
+
return;
|
|
268
|
+
const delta = direction === 'right' ? 90 : -90;
|
|
269
|
+
this._rotation = (((this._rotation + delta) % 360) + 360) % 360;
|
|
270
|
+
this._applyZoom(swiperElement);
|
|
271
|
+
this._updateEditImageSize(swiperElement);
|
|
272
|
+
this._updateCropOverlayPosition(swiperElement);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Constrains the displayed image so that, even once rotated 90°/270° via CSS transform,
|
|
276
|
+
* its visual bounding box always fits inside the slide container (no overflow).
|
|
277
|
+
*/
|
|
278
|
+
_updateEditImageSize(swiperElement, retriesLeft = 60) {
|
|
279
|
+
const swiper = this.getSwiper(swiperElement);
|
|
280
|
+
const activeSlide = swiper?.slides?.[swiper.activeIndex];
|
|
281
|
+
if (!activeSlide)
|
|
282
|
+
return;
|
|
283
|
+
const containerEl = activeSlide.querySelector('.swiper-zoom-container');
|
|
284
|
+
const imgEl = activeSlide.querySelector('.swiper-zoom-target, .swiper-zoom-container > img');
|
|
285
|
+
if (!containerEl || !imgEl)
|
|
286
|
+
return;
|
|
287
|
+
if (!this.cropEnabled && !this.rotationEnabled) {
|
|
288
|
+
imgEl.style.width = '';
|
|
289
|
+
imgEl.style.height = '';
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const containerRect = containerEl.getBoundingClientRect();
|
|
293
|
+
const naturalSize = this._getImageNaturalSize(imgEl);
|
|
294
|
+
// The container/image may not be laid out or loaded yet: skip and retry on the next frame
|
|
295
|
+
if (!containerRect.width || !containerRect.height || !naturalSize) {
|
|
296
|
+
if (retriesLeft > 0) {
|
|
297
|
+
requestAnimationFrame(() => this._updateEditImageSize(swiperElement, retriesLeft - 1));
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
// Compute the exact box (preserving the image's aspect ratio) that fits the available space,
|
|
302
|
+
// swapping width/height when rotated 90°/270° so the rotated visual box never overflows.
|
|
303
|
+
const swapped = this._rotation % 180 !== 0;
|
|
304
|
+
const availWidth = swapped ? containerRect.height : containerRect.width;
|
|
305
|
+
const availHeight = swapped ? containerRect.width : containerRect.height;
|
|
306
|
+
const ratio = Math.min(availWidth / naturalSize.width, availHeight / naturalSize.height);
|
|
307
|
+
imgEl.style.width = `${naturalSize.width * ratio}px`;
|
|
308
|
+
imgEl.style.height = `${naturalSize.height * ratio}px`;
|
|
309
|
+
}
|
|
310
|
+
_getImageNaturalSize(imgEl) {
|
|
311
|
+
const inner = (imgEl.tagName === 'ION-IMG' ? imgEl.shadowRoot?.querySelector('img') : imgEl);
|
|
312
|
+
if (!inner?.naturalWidth || !inner?.naturalHeight)
|
|
313
|
+
return null;
|
|
314
|
+
return { width: inner.naturalWidth, height: inner.naturalHeight };
|
|
315
|
+
}
|
|
316
|
+
// --- Crop overlay: drag to move/resize the crop rectangle ---
|
|
317
|
+
onCropPointerDown(event, swiperElement, handle) {
|
|
318
|
+
event.preventDefault();
|
|
319
|
+
event.stopPropagation();
|
|
320
|
+
const pt = this._getEventPoint(event);
|
|
321
|
+
this._cropOverlayRect = this._cropOverlayRect ?? this._updateCropOverlayPosition(swiperElement);
|
|
322
|
+
this._cropDragState = {
|
|
323
|
+
handle,
|
|
324
|
+
startX: pt.x,
|
|
325
|
+
startY: pt.y,
|
|
326
|
+
startRect: { ...this._cropRect },
|
|
327
|
+
};
|
|
328
|
+
// Also disable programmatic slide navigation while dragging (allowTouchMove is already
|
|
329
|
+
// permanently off whenever crop is enabled, see `_updateSwiperTouchMove`)
|
|
330
|
+
const swiper = this.getSwiper(swiperElement);
|
|
331
|
+
if (swiper) {
|
|
332
|
+
swiper.allowSlideNext = false;
|
|
333
|
+
swiper.allowSlidePrev = false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
onCropPointerMove(event, _swiperElement) {
|
|
337
|
+
if (!this._cropDragState)
|
|
338
|
+
return;
|
|
339
|
+
event.preventDefault();
|
|
340
|
+
const rect = this._cropOverlayRect;
|
|
341
|
+
if (!rect?.width || !rect?.height)
|
|
342
|
+
return;
|
|
343
|
+
const pt = this._getEventPoint(event);
|
|
344
|
+
const dx = (pt.x - this._cropDragState.startX) / rect.width;
|
|
345
|
+
const dy = (pt.y - this._cropDragState.startY) / rect.height;
|
|
346
|
+
this._cropRect = this._computeCropRect(this._cropDragState.handle, this._cropDragState.startRect, dx, dy);
|
|
347
|
+
this.markForCheck();
|
|
348
|
+
}
|
|
349
|
+
onCropPointerUp(_event, swiperElement) {
|
|
350
|
+
if (!this._cropDragState)
|
|
351
|
+
return;
|
|
352
|
+
this._cropDragState = null;
|
|
353
|
+
const swiper = swiperElement ? this.getSwiper(swiperElement) : null;
|
|
354
|
+
if (swiper) {
|
|
355
|
+
swiper.allowSlideNext = true;
|
|
356
|
+
swiper.allowSlidePrev = true;
|
|
357
|
+
}
|
|
358
|
+
if (swiperElement)
|
|
359
|
+
this._updateSwiperTouchMove(swiperElement);
|
|
360
|
+
}
|
|
361
|
+
_computeCropRect(handle, start, dx, dy) {
|
|
362
|
+
const MIN_SIZE = 0.08;
|
|
363
|
+
const clamp01 = (v) => Math.max(0, Math.min(1, v));
|
|
364
|
+
if (handle === 'move') {
|
|
365
|
+
let x = clamp01(start.x + dx);
|
|
366
|
+
let y = clamp01(start.y + dy);
|
|
367
|
+
x = Math.min(x, 1 - start.width);
|
|
368
|
+
y = Math.min(y, 1 - start.height);
|
|
369
|
+
return { x, y, width: start.width, height: start.height };
|
|
370
|
+
}
|
|
371
|
+
let left = start.x;
|
|
372
|
+
let top = start.y;
|
|
373
|
+
let right = start.x + start.width;
|
|
374
|
+
let bottom = start.y + start.height;
|
|
375
|
+
if (handle.includes('w'))
|
|
376
|
+
left = clamp01(start.x + dx);
|
|
377
|
+
if (handle.includes('e'))
|
|
378
|
+
right = clamp01(start.x + start.width + dx);
|
|
379
|
+
if (handle.includes('n'))
|
|
380
|
+
top = clamp01(start.y + dy);
|
|
381
|
+
if (handle.includes('s'))
|
|
382
|
+
bottom = clamp01(start.y + start.height + dy);
|
|
383
|
+
if (right - left < MIN_SIZE) {
|
|
384
|
+
if (handle.includes('w'))
|
|
385
|
+
left = right - MIN_SIZE;
|
|
386
|
+
else
|
|
387
|
+
right = left + MIN_SIZE;
|
|
388
|
+
}
|
|
389
|
+
if (bottom - top < MIN_SIZE) {
|
|
390
|
+
if (handle.includes('n'))
|
|
391
|
+
top = bottom - MIN_SIZE;
|
|
392
|
+
else
|
|
393
|
+
bottom = top + MIN_SIZE;
|
|
394
|
+
}
|
|
395
|
+
left = clamp01(left);
|
|
396
|
+
right = clamp01(right);
|
|
397
|
+
top = clamp01(top);
|
|
398
|
+
bottom = clamp01(bottom);
|
|
399
|
+
return { x: left, y: top, width: right - left, height: bottom - top };
|
|
400
|
+
}
|
|
401
|
+
_getEventPoint(event) {
|
|
402
|
+
if ('touches' in event && event.touches.length) {
|
|
403
|
+
return { x: event.touches[0].clientX, y: event.touches[0].clientY };
|
|
404
|
+
}
|
|
405
|
+
if ('changedTouches' in event && event.changedTouches.length) {
|
|
406
|
+
return { x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientY };
|
|
407
|
+
}
|
|
408
|
+
const mouseEvent = event;
|
|
409
|
+
return { x: mouseEvent.clientX, y: mouseEvent.clientY };
|
|
410
|
+
}
|
|
411
|
+
_updateCropOverlayPosition(swiperElement, retriesLeft = 60) {
|
|
412
|
+
const swiper = this.getSwiper(swiperElement);
|
|
413
|
+
const activeSlide = swiper?.slides?.[swiper.activeIndex];
|
|
414
|
+
if (!activeSlide)
|
|
415
|
+
return null;
|
|
416
|
+
const containerEl = activeSlide.querySelector('.swiper-zoom-container');
|
|
417
|
+
const imgEl = activeSlide.querySelector('.swiper-zoom-target, .swiper-zoom-container > img');
|
|
418
|
+
const overlayEl = activeSlide.querySelector('.crop-overlay');
|
|
419
|
+
if (!containerEl || !imgEl || !overlayEl)
|
|
420
|
+
return null;
|
|
421
|
+
const containerRect = containerEl.getBoundingClientRect();
|
|
422
|
+
const imgRect = imgEl.getBoundingClientRect();
|
|
423
|
+
// The image may not be laid out yet: keep any previous (non-zero) value and retry on the next frame
|
|
424
|
+
if (!imgRect.width || !imgRect.height) {
|
|
425
|
+
if (retriesLeft > 0) {
|
|
426
|
+
requestAnimationFrame(() => this._updateCropOverlayPosition(swiperElement, retriesLeft - 1));
|
|
427
|
+
}
|
|
428
|
+
return this._cropOverlayRect;
|
|
429
|
+
}
|
|
430
|
+
overlayEl.style.left = `${imgRect.left - containerRect.left}px`;
|
|
431
|
+
overlayEl.style.top = `${imgRect.top - containerRect.top}px`;
|
|
432
|
+
overlayEl.style.width = `${imgRect.width}px`;
|
|
433
|
+
overlayEl.style.height = `${imgRect.height}px`;
|
|
434
|
+
this._cropOverlayRect = { width: imgRect.width, height: imgRect.height };
|
|
435
|
+
return this._cropOverlayRect;
|
|
436
|
+
}
|
|
437
|
+
// --- Canvas rendering, used to export the cropped/rotated image ---
|
|
438
|
+
async _renderEditedImage(image, rotation, cropRect) {
|
|
439
|
+
const src = image.dataUrl || image.url;
|
|
440
|
+
const img = await this._loadImageElement(src);
|
|
441
|
+
const sw = img.naturalWidth;
|
|
442
|
+
const sh = img.naturalHeight;
|
|
443
|
+
const swapped = rotation % 180 !== 0;
|
|
444
|
+
const rw = swapped ? sh : sw;
|
|
445
|
+
const rh = swapped ? sw : sh;
|
|
446
|
+
// 1) Draw the full (uncropped) image, rotated
|
|
447
|
+
const rotCanvas = document.createElement('canvas');
|
|
448
|
+
rotCanvas.width = rw;
|
|
449
|
+
rotCanvas.height = rh;
|
|
450
|
+
const rctx = rotCanvas.getContext('2d');
|
|
451
|
+
rctx.translate(rw / 2, rh / 2);
|
|
452
|
+
rctx.rotate((rotation * Math.PI) / 180);
|
|
453
|
+
rctx.drawImage(img, -sw / 2, -sh / 2);
|
|
454
|
+
// 2) Crop the rotated image
|
|
455
|
+
const cropX = Math.round(cropRect.x * rw);
|
|
456
|
+
const cropY = Math.round(cropRect.y * rh);
|
|
457
|
+
const cropW = Math.max(1, Math.round(cropRect.width * rw));
|
|
458
|
+
const cropH = Math.max(1, Math.round(cropRect.height * rh));
|
|
459
|
+
const outCanvas = document.createElement('canvas');
|
|
460
|
+
outCanvas.width = cropW;
|
|
461
|
+
outCanvas.height = cropH;
|
|
462
|
+
outCanvas.getContext('2d').drawImage(rotCanvas, cropX, cropY, cropW, cropH, 0, 0, cropW, cropH);
|
|
463
|
+
return outCanvas;
|
|
464
|
+
}
|
|
465
|
+
_loadImageElement(src) {
|
|
466
|
+
return new Promise((resolve, reject) => {
|
|
467
|
+
const img = new Image();
|
|
468
|
+
if (!src.startsWith('data:'))
|
|
469
|
+
img.crossOrigin = 'anonymous';
|
|
470
|
+
img.onload = () => resolve(img);
|
|
471
|
+
img.onerror = () => reject(new Error(`[image-gallery-slideshow] Cannot load image: ${src}`));
|
|
472
|
+
img.src = src;
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
// --- Mouse-wheel zoom & mouse-drag pan ---
|
|
476
|
+
onPointerDown(event, swiperElement) {
|
|
477
|
+
if (!this.mouseZoomEnabled || this._zoomState.scale <= 1)
|
|
478
|
+
return;
|
|
479
|
+
// Don't intercept clicks on buttons/controls
|
|
480
|
+
if (event.target.closest('ion-fab-button, ion-button, button'))
|
|
481
|
+
return;
|
|
482
|
+
event.preventDefault();
|
|
483
|
+
this._dragState = {
|
|
484
|
+
startX: event.clientX,
|
|
485
|
+
startY: event.clientY,
|
|
486
|
+
startTx: this._zoomState.tx,
|
|
487
|
+
startTy: this._zoomState.ty,
|
|
488
|
+
};
|
|
489
|
+
const wrapEl = this._getActiveZoomWrapEl(swiperElement);
|
|
490
|
+
if (wrapEl)
|
|
491
|
+
wrapEl.style.cursor = 'grabbing';
|
|
492
|
+
// Disable Swiper slide-change while dragging
|
|
493
|
+
const swiper = this.getSwiper(swiperElement);
|
|
494
|
+
if (swiper) {
|
|
495
|
+
swiper.allowSlideNext = false;
|
|
496
|
+
swiper.allowSlidePrev = false;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
onPointerMove(event, swiperElement) {
|
|
500
|
+
if (this._cropDragState) {
|
|
501
|
+
this.onCropPointerMove(event, swiperElement);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (!this._dragState)
|
|
505
|
+
return;
|
|
506
|
+
event.preventDefault();
|
|
507
|
+
this._zoomState.tx = this._dragState.startTx + event.clientX - this._dragState.startX;
|
|
508
|
+
this._zoomState.ty = this._dragState.startTy + event.clientY - this._dragState.startY;
|
|
509
|
+
this._clampTranslations(swiperElement);
|
|
510
|
+
this._applyZoom(swiperElement);
|
|
511
|
+
}
|
|
512
|
+
onPointerUp(event, swiperElement) {
|
|
513
|
+
if (this._cropDragState) {
|
|
514
|
+
this.onCropPointerUp(event, swiperElement);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
if (!this._dragState)
|
|
518
|
+
return;
|
|
519
|
+
this._dragState = null;
|
|
520
|
+
const wrapEl = this._getActiveZoomWrapEl(swiperElement);
|
|
521
|
+
if (wrapEl)
|
|
522
|
+
wrapEl.style.cursor = this._zoomState.scale > 1 ? 'grab' : '';
|
|
523
|
+
// Re-enable Swiper navigation
|
|
524
|
+
const swiper = swiperElement ? this.getSwiper(swiperElement) : null;
|
|
525
|
+
if (swiper) {
|
|
526
|
+
swiper.allowSlideNext = true;
|
|
527
|
+
swiper.allowSlidePrev = true;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
_resetZoom(swiperElement) {
|
|
531
|
+
this._zoomState = { scale: 1, tx: 0, ty: 0 };
|
|
532
|
+
this._dragState = null;
|
|
533
|
+
this._rotation = 0;
|
|
534
|
+
this._cropRect = { x: 0, y: 0, width: 1, height: 1 };
|
|
535
|
+
this._cropDragState = null;
|
|
536
|
+
this._cropToolActive = false;
|
|
537
|
+
if (swiperElement) {
|
|
538
|
+
this._applyZoom(swiperElement);
|
|
539
|
+
this._updateEditImageSize(swiperElement);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
_setupWheelZoom(swiperElement) {
|
|
543
|
+
this._cleanupWheelZoom();
|
|
544
|
+
if (!this.mouseZoomEnabled)
|
|
545
|
+
return;
|
|
546
|
+
const el = swiperElement?.nativeElement ?? swiperElement;
|
|
547
|
+
if (!el)
|
|
548
|
+
return;
|
|
549
|
+
this._wheelListener = (event) => {
|
|
550
|
+
event.preventDefault();
|
|
551
|
+
const step = this.wheelZoomStep;
|
|
552
|
+
const oldScale = this._zoomState.scale;
|
|
553
|
+
const newScale = Math.max(1, Math.min(oldScale + (event.deltaY < 0 ? step : -step), this.maxZoomRatio));
|
|
554
|
+
if (newScale === oldScale)
|
|
555
|
+
return;
|
|
556
|
+
if (newScale === 1) {
|
|
557
|
+
this._zoomState = { scale: 1, tx: 0, ty: 0 };
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
// Zoom towards cursor position
|
|
561
|
+
const swiper = this.getSwiper(el);
|
|
562
|
+
const activeSlide = swiper?.slides?.[swiper.activeIndex];
|
|
563
|
+
if (activeSlide) {
|
|
564
|
+
const rect = activeSlide.getBoundingClientRect();
|
|
565
|
+
const cx = event.clientX - rect.left - rect.width / 2;
|
|
566
|
+
const cy = event.clientY - rect.top - rect.height / 2;
|
|
567
|
+
this._zoomState.tx = cx - ((cx - this._zoomState.tx) * newScale) / oldScale;
|
|
568
|
+
this._zoomState.ty = cy - ((cy - this._zoomState.ty) * newScale) / oldScale;
|
|
569
|
+
}
|
|
570
|
+
this._zoomState.scale = newScale;
|
|
571
|
+
this._clampTranslations(el);
|
|
572
|
+
}
|
|
573
|
+
this._applyZoom(el);
|
|
574
|
+
};
|
|
575
|
+
el.addEventListener('wheel', this._wheelListener, { passive: false });
|
|
576
|
+
this._wheelEl = el;
|
|
577
|
+
}
|
|
578
|
+
_cleanupWheelZoom() {
|
|
579
|
+
if (this._wheelEl && this._wheelListener) {
|
|
580
|
+
this._wheelEl.removeEventListener('wheel', this._wheelListener);
|
|
581
|
+
}
|
|
582
|
+
this._wheelEl = null;
|
|
583
|
+
this._wheelListener = null;
|
|
584
|
+
}
|
|
585
|
+
_applyZoom(swiperElement) {
|
|
586
|
+
const swiper = this.getSwiper(swiperElement);
|
|
587
|
+
const activeSlide = swiper?.slides?.[swiper.activeIndex];
|
|
588
|
+
if (!activeSlide)
|
|
589
|
+
return;
|
|
590
|
+
const wrapEl = activeSlide.querySelector('.swiper-zoom-container');
|
|
591
|
+
const imgEl = activeSlide.querySelector('.swiper-zoom-target, .swiper-zoom-container > img');
|
|
592
|
+
if (!wrapEl || !imgEl)
|
|
593
|
+
return;
|
|
594
|
+
const { scale, tx, ty } = this._zoomState;
|
|
595
|
+
wrapEl.style.transitionDuration = '0ms';
|
|
596
|
+
wrapEl.style.transform = `translate3d(${tx}px, ${ty}px, 0)`;
|
|
597
|
+
imgEl.style.transitionDuration = '0ms';
|
|
598
|
+
imgEl.style.transform = `translate3d(0, 0, 0) scale(${scale}) rotate(${this._rotation}deg)`;
|
|
599
|
+
wrapEl.style.cursor = scale > 1 ? 'grab' : '';
|
|
600
|
+
}
|
|
601
|
+
_clampTranslations(swiperElement) {
|
|
602
|
+
const swiper = this.getSwiper(swiperElement);
|
|
603
|
+
const activeSlide = swiper?.slides?.[swiper.activeIndex];
|
|
604
|
+
if (!activeSlide)
|
|
605
|
+
return;
|
|
606
|
+
const imgEl = activeSlide.querySelector('.swiper-zoom-target, .swiper-zoom-container > img');
|
|
607
|
+
if (!imgEl)
|
|
608
|
+
return;
|
|
609
|
+
const imgW = imgEl.offsetWidth || imgEl.clientWidth;
|
|
610
|
+
const imgH = imgEl.offsetHeight || imgEl.clientHeight;
|
|
611
|
+
if (!imgW || !imgH)
|
|
612
|
+
return;
|
|
613
|
+
const { scale } = this._zoomState;
|
|
614
|
+
const maxTx = Math.max(0, (imgW * scale - activeSlide.offsetWidth) / 2);
|
|
615
|
+
const maxTy = Math.max(0, (imgH * scale - activeSlide.offsetHeight) / 2);
|
|
616
|
+
this._zoomState.tx = Math.max(-maxTx, Math.min(maxTx, this._zoomState.tx));
|
|
617
|
+
this._zoomState.ty = Math.max(-maxTy, Math.min(maxTy, this._zoomState.ty));
|
|
618
|
+
}
|
|
619
|
+
_getActiveZoomWrapEl(swiperElement) {
|
|
620
|
+
const swiper = this.getSwiper(swiperElement);
|
|
621
|
+
const activeSlide = swiper?.slides?.[swiper.activeIndex];
|
|
622
|
+
return activeSlide?.querySelector('.swiper-zoom-container') ?? null;
|
|
623
|
+
}
|
|
624
|
+
markForCheck() {
|
|
625
|
+
this.cd.markForCheck();
|
|
626
|
+
}
|
|
627
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AppImageGallerySlideshowComponent, deps: [{ token: i1.AlertController }, { token: i2.TranslateService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
628
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AppImageGallerySlideshowComponent, selector: "app-image-gallery-slideshow", inputs: { rows: "rows", selectedIndex: "selectedIndex", mobile: ["mobile", "mobile", booleanAttribute], readOnly: ["readOnly", "readOnly", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], debug: ["debug", "debug", booleanAttribute], mouseZoomEnabled: ["mouseZoomEnabled", "mouseZoomEnabled", booleanAttribute], wheelZoomStep: "wheelZoomStep", maxZoomRatio: "maxZoomRatio", rotationEnabled: ["rotationEnabled", "rotationEnabled", booleanAttribute], cropEnabled: ["cropEnabled", "cropEnabled", booleanAttribute], imageExportFormat: "imageExportFormat", imageExportQuality: "imageExportQuality", imageSizeQueryParam: "imageSizeQueryParam", modalImageSize: "modalImageSize" }, outputs: { deleteRequested: "deleteRequested", closeRequested: "closeRequested", afterEditImage: "afterEditImage", afterEditRow: "afterEditRow" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "swiperRef", first: true, predicate: ["slideshowSwiper"], descendants: true }], ngImport: i0, template: "<ion-content\n class=\"ion-no-padding ion-no-margin\"\n (mousedown)=\"onPointerDown($event, slideshowSwiper)\"\n (mousemove)=\"onPointerMove($event, slideshowSwiper)\"\n (mouseup)=\"onPointerUp($event, slideshowSwiper)\"\n (mouseleave)=\"onPointerUp($event, slideshowSwiper)\"\n (touchmove)=\"onCropPointerMove($event, slideshowSwiper)\"\n (touchend)=\"onCropPointerUp($event, slideshowSwiper)\"\n (touchcancel)=\"onCropPointerUp($event, slideshowSwiper)\"\n>\n @let count = rows?.length || 0;\n <!-- Counter -->\n <div class=\"top-left\">\n <ion-label color=\"light\">{{ activeSlideIndex + 1 }} / {{ count }}</ion-label>\n </div>\n\n <!-- zoom / rotation / crop buttons: all shown together, no \"enter edit mode\" step needed -->\n <div class=\"top-right\">\n @if (!mobile) {\n <ion-fab-button size=\"small\" (click)=\"zoom(slideshowSwiper, true)\" color=\"light\">\n <ion-icon name=\"search\"></ion-icon>\n <ion-icon class=\"icon-secondary\" name=\"add\"></ion-icon>\n </ion-fab-button>\n <ion-fab-button size=\"small\" (click)=\"zoom(slideshowSwiper, false)\" color=\"light\">\n <ion-icon name=\"search\"></ion-icon>\n <ion-icon class=\"icon-secondary\" name=\"remove\"></ion-icon>\n </ion-fab-button>\n }\n @if (!readOnly && rotationEnabled) {\n <ion-fab-button\n size=\"small\"\n (click)=\"rotate(slideshowSwiper, 'left')\"\n color=\"light\"\n [title]=\"!showTooltip ? ('IMAGE.GALLERY.BTN_ROTATE_LEFT' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('IMAGE.GALLERY.BTN_ROTATE_LEFT' | translate) : ''\"\n >\n <mat-icon>rotate_left</mat-icon>\n </ion-fab-button>\n <ion-fab-button\n size=\"small\"\n (click)=\"rotate(slideshowSwiper, 'right')\"\n color=\"light\"\n [title]=\"!showTooltip ? ('IMAGE.GALLERY.BTN_ROTATE_RIGHT' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('IMAGE.GALLERY.BTN_ROTATE_RIGHT' | translate) : ''\"\n >\n <mat-icon>rotate_right</mat-icon>\n </ion-fab-button>\n }\n @if (!readOnly && cropEnabled) {\n <ion-fab-button\n size=\"small\"\n (click)=\"toggleCropTool(slideshowSwiper)\"\n [color]=\"_cropToolActive ? 'tertiary' : 'light'\"\n [title]=\"\n !showTooltip\n ? ((_cropToolActive ? 'IMAGE.GALLERY.BTN_CROP_DEACTIVATE' : 'IMAGE.GALLERY.BTN_CROP_ACTIVATE') | translate)\n : ''\n \"\n [matTooltip]=\"\n showTooltip\n ? ((_cropToolActive ? 'IMAGE.GALLERY.BTN_CROP_DEACTIVATE' : 'IMAGE.GALLERY.BTN_CROP_ACTIVATE') | translate)\n : ''\n \"\n >\n <mat-icon>crop</mat-icon>\n </ion-fab-button>\n @if (_cropToolActive) {\n <ion-fab-button\n size=\"small\"\n (click)=\"resetCrop(slideshowSwiper)\"\n color=\"light\"\n [title]=\"!showTooltip ? ('IMAGE.GALLERY.BTN_CROP_RESET' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('IMAGE.GALLERY.BTN_CROP_RESET' | translate) : ''\"\n >\n <mat-icon>crop_free</mat-icon>\n </ion-fab-button>\n }\n }\n </div>\n\n <!-- navigation side buttons next/back -->\n <ng-container *ngIf=\"!mobile && (rows | isArrayLength: { greaterThan: 1 })\">\n <div class=\"side-buttons prev\" (click)=\"slidePrev(slideshowSwiper)\">\n <ion-icon name=\"chevron-back\"></ion-icon>\n </div>\n <div class=\"side-buttons next\" (click)=\"slideNext(slideshowSwiper)\">\n <ion-icon name=\"chevron-forward\"></ion-icon>\n </div>\n </ng-container>\n\n <!-- image slides -->\n <swiper-container\n #slideshowSwiper\n [modules]=\"swiperModules\"\n [zoom]=\"!mouseZoomEnabled\"\n (swiperslidechange)=\"onSlideChange(slideshowSwiper)\"\n (swiperinit)=\"onSwiperInit(slideshowSwiper)\"\n >\n @for (row of rows; track row.id; let i = $index) {\n <swiper-slide>\n @let image = row | propertyGet: 'currentData';\n <div class=\"swiper-zoom-container\">\n @if (image.url) {\n <ion-img class=\"swiper-zoom-target\" [src]=\"image.url | appendQueryParams: getImageSizeQueryParams()\" />\n } @else {\n <img [src]=\"image.dataUrl\" [alt]=\"image.title\" />\n }\n\n <!-- crop overlay: only rendered for the active slide -->\n @if (!readOnly && cropEnabled && _cropToolActive && i === activeSlideIndex) {\n <div\n class=\"crop-overlay\"\n (mousedown)=\"onCropPointerDown($event, slideshowSwiper, 'move')\"\n (touchstart)=\"onCropPointerDown($event, slideshowSwiper, 'move')\"\n >\n <!-- darkened mask around the selection: clipped to the image bounds -->\n <div class=\"crop-mask\">\n <div\n class=\"crop-mask-rect\"\n [style.left.%]=\"_cropRect.x * 100\"\n [style.top.%]=\"_cropRect.y * 100\"\n [style.width.%]=\"_cropRect.width * 100\"\n [style.height.%]=\"_cropRect.height * 100\"\n ></div>\n </div>\n\n <!-- interactive selection border, grid & drag handles: never clipped, so corner/edge handles\n remain fully grabbable even when the selection touches the image bounds -->\n <div\n class=\"crop-rect\"\n [style.left.%]=\"_cropRect.x * 100\"\n [style.top.%]=\"_cropRect.y * 100\"\n [style.width.%]=\"_cropRect.width * 100\"\n [style.height.%]=\"_cropRect.height * 100\"\n >\n <div class=\"crop-grid\">\n <span class=\"line v\" style=\"left: 33.333%\"></span>\n <span class=\"line v\" style=\"left: 66.666%\"></span>\n <span class=\"line h\" style=\"top: 33.333%\"></span>\n <span class=\"line h\" style=\"top: 66.666%\"></span>\n </div>\n @for (handle of _cropHandles; track handle) {\n <div\n class=\"crop-handle {{ handle }}\"\n (mousedown)=\"onCropPointerDown($event, slideshowSwiper, handle)\"\n (touchstart)=\"onCropPointerDown($event, slideshowSwiper, handle)\"\n ></div>\n }\n </div>\n </div>\n }\n </div>\n </swiper-slide>\n }\n </swiper-container>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar color=\"light\">\n @if (!hasPendingEdit) {\n <ion-row>\n <ion-col size=\"4\" class=\"ion-text-start\">\n <ion-button\n *ngIf=\"!readOnly\"\n (click)=\"requestDelete(slideshowSwiper)\"\n [disabled]=\"disabled\"\n fill=\"clear\"\n color=\"danger\"\n >\n <mat-icon slot=\"start\">delete</mat-icon>\n {{ 'COMMON.BTN_DELETE' | translate }}\n </ion-button>\n </ion-col>\n <ion-col size=\"4\"></ion-col>\n <ion-col size=\"4\" class=\"ion-text-end\">\n <ion-button (click)=\"closeRequested.emit()\" color=\"tertiary\" fill=\"clear\" *ngIf=\"mobile; else desktop\">\n <ion-icon slot=\"start\" name=\"close\"></ion-icon>\n {{ 'COMMON.BTN_CLOSE' | translate }}\n </ion-button>\n <ng-template #desktop>\n <ion-button (click)=\"closeRequested.emit()\" color=\"tertiary\">\n {{ 'COMMON.BTN_CLOSE' | translate }}\n </ion-button>\n </ng-template>\n </ion-col>\n </ion-row>\n } @else {\n <!-- A crop or rotation change is pending: ask for an explicit validation before exporting the image -->\n <ion-row>\n <ion-col size=\"6\" class=\"ion-text-start\">\n <ion-button (click)=\"discardEdit(slideshowSwiper)\" color=\"medium\" fill=\"clear\">\n <ion-icon slot=\"start\" name=\"close\"></ion-icon>\n {{ 'COMMON.BTN_CANCEL' | translate }}\n </ion-button>\n </ion-col>\n <ion-col size=\"6\" class=\"ion-text-end\">\n <ion-button (click)=\"validateEdit(slideshowSwiper)\" color=\"tertiary\">\n <ion-icon slot=\"start\" name=\"checkmark\"></ion-icon>\n {{ 'COMMON.BTN_VALIDATE' | translate }}\n </ion-button>\n </ion-col>\n </ion-row>\n }\n </ion-toolbar>\n</ion-footer>\n", styles: [":host{display:flex;flex-direction:column;height:100%;-webkit-user-select:none;user-select:none}:host ion-content{flex:1 1 auto}:host .top-left{z-index:14;position:absolute;display:inline-grid;left:var(--ion-padding-start, 8px);top:var(--ion-padding-start, 8px)}:host .top-right{z-index:14;position:absolute;display:inline-grid;right:var(--ion-padding-start, 8px);top:var(--ion-padding-start, 8px)}:host .side-buttons{--side-buttons-width: 120px;--side-buttons-margin: calc(var(--side-buttons-width) / 3);z-index:13;position:absolute;top:0;bottom:0;width:var(--side-buttons-width)}:host .side-buttons ion-icon{top:50%;color:#fff;font-size:2.5rem;position:absolute;opacity:0;transition-duration:.15s;transition-timing-function:ease-in}:host .side-buttons:hover{cursor:pointer}:host .side-buttons:hover ion-icon{opacity:1}:host .side-buttons.prev{left:0}:host .side-buttons.prev ion-icon{left:var(--side-buttons-margin)}:host .side-buttons.next{right:0}:host .side-buttons.next ion-icon{right:var(--side-buttons-margin)}:host swiper-container{height:100%;background:#000}:host .swiper-zoom-container{position:relative}:host ion-fab-button .icon-secondary{top:11px;left:6px;height:14px}:host .crop-overlay{position:absolute;z-index:15;cursor:move;touch-action:none;animation:crop-overlay-fade-in .15s ease-in}:host .crop-overlay .crop-mask{position:absolute;inset:0;overflow:hidden;pointer-events:none}:host .crop-overlay .crop-mask .crop-mask-rect{position:absolute;box-shadow:0 0 0 2000px #0009}:host .crop-overlay .crop-rect{position:absolute;box-sizing:border-box;border:1px solid rgba(255,255,255,.95);cursor:move;touch-action:none}:host .crop-overlay .crop-rect .crop-grid{position:absolute;inset:0;pointer-events:none}:host .crop-overlay .crop-rect .crop-grid .line{position:absolute;background:#ffffff73}:host .crop-overlay .crop-rect .crop-grid .line.v{top:0;bottom:0;width:1px}:host .crop-overlay .crop-rect .crop-grid .line.h{left:0;right:0;height:1px}:host .crop-overlay .crop-rect .crop-handle{position:absolute;width:16px;height:16px;margin:-8px;background:#fff;border:1px solid rgba(0,0,0,.45);border-radius:50%;box-shadow:0 1px 4px #00000080;transition:transform .12s ease;touch-action:none}:host .crop-overlay .crop-rect .crop-handle:hover{transform:scale(1.3)}:host .crop-overlay .crop-rect .crop-handle.nw{left:0;top:0;cursor:nwse-resize}:host .crop-overlay .crop-rect .crop-handle.n{left:50%;top:0;cursor:ns-resize}:host .crop-overlay .crop-rect .crop-handle.ne{left:100%;top:0;cursor:nesw-resize}:host .crop-overlay .crop-rect .crop-handle.e{left:100%;top:50%;cursor:ew-resize}:host .crop-overlay .crop-rect .crop-handle.se{left:100%;top:100%;cursor:nwse-resize}:host .crop-overlay .crop-rect .crop-handle.s{left:50%;top:100%;cursor:ns-resize}:host .crop-overlay .crop-rect .crop-handle.sw{left:0;top:100%;cursor:nesw-resize}:host .crop-overlay .crop-rect .crop-handle.w{left:0;top:50%;cursor:ew-resize}@keyframes crop-overlay-fade-in{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i1.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i1.IonFabButton, selector: "ion-fab-button", inputs: ["activated", "closeIcon", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "show", "size", "target", "translucent", "type"] }, { kind: "component", type: i1.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i1.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i1.IonRow, selector: "ion-row" }, { kind: "component", type: i1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: i6.PropertyGetPipe, name: "propertyGet" }, { kind: "pipe", type: i7.ArrayLengthPipe, name: "isArrayLength" }, { kind: "pipe", type: i8.AppendQueryParamsPipePipe, name: "appendQueryParams" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
629
|
+
}
|
|
630
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AppImageGallerySlideshowComponent, decorators: [{
|
|
631
|
+
type: Component,
|
|
632
|
+
args: [{ selector: 'app-image-gallery-slideshow', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-content\n class=\"ion-no-padding ion-no-margin\"\n (mousedown)=\"onPointerDown($event, slideshowSwiper)\"\n (mousemove)=\"onPointerMove($event, slideshowSwiper)\"\n (mouseup)=\"onPointerUp($event, slideshowSwiper)\"\n (mouseleave)=\"onPointerUp($event, slideshowSwiper)\"\n (touchmove)=\"onCropPointerMove($event, slideshowSwiper)\"\n (touchend)=\"onCropPointerUp($event, slideshowSwiper)\"\n (touchcancel)=\"onCropPointerUp($event, slideshowSwiper)\"\n>\n @let count = rows?.length || 0;\n <!-- Counter -->\n <div class=\"top-left\">\n <ion-label color=\"light\">{{ activeSlideIndex + 1 }} / {{ count }}</ion-label>\n </div>\n\n <!-- zoom / rotation / crop buttons: all shown together, no \"enter edit mode\" step needed -->\n <div class=\"top-right\">\n @if (!mobile) {\n <ion-fab-button size=\"small\" (click)=\"zoom(slideshowSwiper, true)\" color=\"light\">\n <ion-icon name=\"search\"></ion-icon>\n <ion-icon class=\"icon-secondary\" name=\"add\"></ion-icon>\n </ion-fab-button>\n <ion-fab-button size=\"small\" (click)=\"zoom(slideshowSwiper, false)\" color=\"light\">\n <ion-icon name=\"search\"></ion-icon>\n <ion-icon class=\"icon-secondary\" name=\"remove\"></ion-icon>\n </ion-fab-button>\n }\n @if (!readOnly && rotationEnabled) {\n <ion-fab-button\n size=\"small\"\n (click)=\"rotate(slideshowSwiper, 'left')\"\n color=\"light\"\n [title]=\"!showTooltip ? ('IMAGE.GALLERY.BTN_ROTATE_LEFT' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('IMAGE.GALLERY.BTN_ROTATE_LEFT' | translate) : ''\"\n >\n <mat-icon>rotate_left</mat-icon>\n </ion-fab-button>\n <ion-fab-button\n size=\"small\"\n (click)=\"rotate(slideshowSwiper, 'right')\"\n color=\"light\"\n [title]=\"!showTooltip ? ('IMAGE.GALLERY.BTN_ROTATE_RIGHT' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('IMAGE.GALLERY.BTN_ROTATE_RIGHT' | translate) : ''\"\n >\n <mat-icon>rotate_right</mat-icon>\n </ion-fab-button>\n }\n @if (!readOnly && cropEnabled) {\n <ion-fab-button\n size=\"small\"\n (click)=\"toggleCropTool(slideshowSwiper)\"\n [color]=\"_cropToolActive ? 'tertiary' : 'light'\"\n [title]=\"\n !showTooltip\n ? ((_cropToolActive ? 'IMAGE.GALLERY.BTN_CROP_DEACTIVATE' : 'IMAGE.GALLERY.BTN_CROP_ACTIVATE') | translate)\n : ''\n \"\n [matTooltip]=\"\n showTooltip\n ? ((_cropToolActive ? 'IMAGE.GALLERY.BTN_CROP_DEACTIVATE' : 'IMAGE.GALLERY.BTN_CROP_ACTIVATE') | translate)\n : ''\n \"\n >\n <mat-icon>crop</mat-icon>\n </ion-fab-button>\n @if (_cropToolActive) {\n <ion-fab-button\n size=\"small\"\n (click)=\"resetCrop(slideshowSwiper)\"\n color=\"light\"\n [title]=\"!showTooltip ? ('IMAGE.GALLERY.BTN_CROP_RESET' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('IMAGE.GALLERY.BTN_CROP_RESET' | translate) : ''\"\n >\n <mat-icon>crop_free</mat-icon>\n </ion-fab-button>\n }\n }\n </div>\n\n <!-- navigation side buttons next/back -->\n <ng-container *ngIf=\"!mobile && (rows | isArrayLength: { greaterThan: 1 })\">\n <div class=\"side-buttons prev\" (click)=\"slidePrev(slideshowSwiper)\">\n <ion-icon name=\"chevron-back\"></ion-icon>\n </div>\n <div class=\"side-buttons next\" (click)=\"slideNext(slideshowSwiper)\">\n <ion-icon name=\"chevron-forward\"></ion-icon>\n </div>\n </ng-container>\n\n <!-- image slides -->\n <swiper-container\n #slideshowSwiper\n [modules]=\"swiperModules\"\n [zoom]=\"!mouseZoomEnabled\"\n (swiperslidechange)=\"onSlideChange(slideshowSwiper)\"\n (swiperinit)=\"onSwiperInit(slideshowSwiper)\"\n >\n @for (row of rows; track row.id; let i = $index) {\n <swiper-slide>\n @let image = row | propertyGet: 'currentData';\n <div class=\"swiper-zoom-container\">\n @if (image.url) {\n <ion-img class=\"swiper-zoom-target\" [src]=\"image.url | appendQueryParams: getImageSizeQueryParams()\" />\n } @else {\n <img [src]=\"image.dataUrl\" [alt]=\"image.title\" />\n }\n\n <!-- crop overlay: only rendered for the active slide -->\n @if (!readOnly && cropEnabled && _cropToolActive && i === activeSlideIndex) {\n <div\n class=\"crop-overlay\"\n (mousedown)=\"onCropPointerDown($event, slideshowSwiper, 'move')\"\n (touchstart)=\"onCropPointerDown($event, slideshowSwiper, 'move')\"\n >\n <!-- darkened mask around the selection: clipped to the image bounds -->\n <div class=\"crop-mask\">\n <div\n class=\"crop-mask-rect\"\n [style.left.%]=\"_cropRect.x * 100\"\n [style.top.%]=\"_cropRect.y * 100\"\n [style.width.%]=\"_cropRect.width * 100\"\n [style.height.%]=\"_cropRect.height * 100\"\n ></div>\n </div>\n\n <!-- interactive selection border, grid & drag handles: never clipped, so corner/edge handles\n remain fully grabbable even when the selection touches the image bounds -->\n <div\n class=\"crop-rect\"\n [style.left.%]=\"_cropRect.x * 100\"\n [style.top.%]=\"_cropRect.y * 100\"\n [style.width.%]=\"_cropRect.width * 100\"\n [style.height.%]=\"_cropRect.height * 100\"\n >\n <div class=\"crop-grid\">\n <span class=\"line v\" style=\"left: 33.333%\"></span>\n <span class=\"line v\" style=\"left: 66.666%\"></span>\n <span class=\"line h\" style=\"top: 33.333%\"></span>\n <span class=\"line h\" style=\"top: 66.666%\"></span>\n </div>\n @for (handle of _cropHandles; track handle) {\n <div\n class=\"crop-handle {{ handle }}\"\n (mousedown)=\"onCropPointerDown($event, slideshowSwiper, handle)\"\n (touchstart)=\"onCropPointerDown($event, slideshowSwiper, handle)\"\n ></div>\n }\n </div>\n </div>\n }\n </div>\n </swiper-slide>\n }\n </swiper-container>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar color=\"light\">\n @if (!hasPendingEdit) {\n <ion-row>\n <ion-col size=\"4\" class=\"ion-text-start\">\n <ion-button\n *ngIf=\"!readOnly\"\n (click)=\"requestDelete(slideshowSwiper)\"\n [disabled]=\"disabled\"\n fill=\"clear\"\n color=\"danger\"\n >\n <mat-icon slot=\"start\">delete</mat-icon>\n {{ 'COMMON.BTN_DELETE' | translate }}\n </ion-button>\n </ion-col>\n <ion-col size=\"4\"></ion-col>\n <ion-col size=\"4\" class=\"ion-text-end\">\n <ion-button (click)=\"closeRequested.emit()\" color=\"tertiary\" fill=\"clear\" *ngIf=\"mobile; else desktop\">\n <ion-icon slot=\"start\" name=\"close\"></ion-icon>\n {{ 'COMMON.BTN_CLOSE' | translate }}\n </ion-button>\n <ng-template #desktop>\n <ion-button (click)=\"closeRequested.emit()\" color=\"tertiary\">\n {{ 'COMMON.BTN_CLOSE' | translate }}\n </ion-button>\n </ng-template>\n </ion-col>\n </ion-row>\n } @else {\n <!-- A crop or rotation change is pending: ask for an explicit validation before exporting the image -->\n <ion-row>\n <ion-col size=\"6\" class=\"ion-text-start\">\n <ion-button (click)=\"discardEdit(slideshowSwiper)\" color=\"medium\" fill=\"clear\">\n <ion-icon slot=\"start\" name=\"close\"></ion-icon>\n {{ 'COMMON.BTN_CANCEL' | translate }}\n </ion-button>\n </ion-col>\n <ion-col size=\"6\" class=\"ion-text-end\">\n <ion-button (click)=\"validateEdit(slideshowSwiper)\" color=\"tertiary\">\n <ion-icon slot=\"start\" name=\"checkmark\"></ion-icon>\n {{ 'COMMON.BTN_VALIDATE' | translate }}\n </ion-button>\n </ion-col>\n </ion-row>\n }\n </ion-toolbar>\n</ion-footer>\n", styles: [":host{display:flex;flex-direction:column;height:100%;-webkit-user-select:none;user-select:none}:host ion-content{flex:1 1 auto}:host .top-left{z-index:14;position:absolute;display:inline-grid;left:var(--ion-padding-start, 8px);top:var(--ion-padding-start, 8px)}:host .top-right{z-index:14;position:absolute;display:inline-grid;right:var(--ion-padding-start, 8px);top:var(--ion-padding-start, 8px)}:host .side-buttons{--side-buttons-width: 120px;--side-buttons-margin: calc(var(--side-buttons-width) / 3);z-index:13;position:absolute;top:0;bottom:0;width:var(--side-buttons-width)}:host .side-buttons ion-icon{top:50%;color:#fff;font-size:2.5rem;position:absolute;opacity:0;transition-duration:.15s;transition-timing-function:ease-in}:host .side-buttons:hover{cursor:pointer}:host .side-buttons:hover ion-icon{opacity:1}:host .side-buttons.prev{left:0}:host .side-buttons.prev ion-icon{left:var(--side-buttons-margin)}:host .side-buttons.next{right:0}:host .side-buttons.next ion-icon{right:var(--side-buttons-margin)}:host swiper-container{height:100%;background:#000}:host .swiper-zoom-container{position:relative}:host ion-fab-button .icon-secondary{top:11px;left:6px;height:14px}:host .crop-overlay{position:absolute;z-index:15;cursor:move;touch-action:none;animation:crop-overlay-fade-in .15s ease-in}:host .crop-overlay .crop-mask{position:absolute;inset:0;overflow:hidden;pointer-events:none}:host .crop-overlay .crop-mask .crop-mask-rect{position:absolute;box-shadow:0 0 0 2000px #0009}:host .crop-overlay .crop-rect{position:absolute;box-sizing:border-box;border:1px solid rgba(255,255,255,.95);cursor:move;touch-action:none}:host .crop-overlay .crop-rect .crop-grid{position:absolute;inset:0;pointer-events:none}:host .crop-overlay .crop-rect .crop-grid .line{position:absolute;background:#ffffff73}:host .crop-overlay .crop-rect .crop-grid .line.v{top:0;bottom:0;width:1px}:host .crop-overlay .crop-rect .crop-grid .line.h{left:0;right:0;height:1px}:host .crop-overlay .crop-rect .crop-handle{position:absolute;width:16px;height:16px;margin:-8px;background:#fff;border:1px solid rgba(0,0,0,.45);border-radius:50%;box-shadow:0 1px 4px #00000080;transition:transform .12s ease;touch-action:none}:host .crop-overlay .crop-rect .crop-handle:hover{transform:scale(1.3)}:host .crop-overlay .crop-rect .crop-handle.nw{left:0;top:0;cursor:nwse-resize}:host .crop-overlay .crop-rect .crop-handle.n{left:50%;top:0;cursor:ns-resize}:host .crop-overlay .crop-rect .crop-handle.ne{left:100%;top:0;cursor:nesw-resize}:host .crop-overlay .crop-rect .crop-handle.e{left:100%;top:50%;cursor:ew-resize}:host .crop-overlay .crop-rect .crop-handle.se{left:100%;top:100%;cursor:nwse-resize}:host .crop-overlay .crop-rect .crop-handle.s{left:50%;top:100%;cursor:ns-resize}:host .crop-overlay .crop-rect .crop-handle.sw{left:0;top:100%;cursor:nesw-resize}:host .crop-overlay .crop-rect .crop-handle.w{left:0;top:50%;cursor:ew-resize}@keyframes crop-overlay-fade-in{0%{opacity:0}to{opacity:1}}\n"] }]
|
|
633
|
+
}], ctorParameters: () => [{ type: i1.AlertController }, { type: i2.TranslateService }, { type: i0.ChangeDetectorRef }], propDecorators: { rows: [{
|
|
634
|
+
type: Input
|
|
635
|
+
}], selectedIndex: [{
|
|
636
|
+
type: Input
|
|
637
|
+
}], mobile: [{
|
|
638
|
+
type: Input,
|
|
639
|
+
args: [{ transform: booleanAttribute }]
|
|
640
|
+
}], readOnly: [{
|
|
641
|
+
type: Input,
|
|
642
|
+
args: [{ transform: booleanAttribute }]
|
|
643
|
+
}], disabled: [{
|
|
644
|
+
type: Input,
|
|
645
|
+
args: [{ transform: booleanAttribute }]
|
|
646
|
+
}], debug: [{
|
|
647
|
+
type: Input,
|
|
648
|
+
args: [{ transform: booleanAttribute }]
|
|
649
|
+
}], mouseZoomEnabled: [{
|
|
650
|
+
type: Input,
|
|
651
|
+
args: [{ transform: booleanAttribute }]
|
|
652
|
+
}], wheelZoomStep: [{
|
|
653
|
+
type: Input
|
|
654
|
+
}], maxZoomRatio: [{
|
|
655
|
+
type: Input
|
|
656
|
+
}], rotationEnabled: [{
|
|
657
|
+
type: Input,
|
|
658
|
+
args: [{ transform: booleanAttribute }]
|
|
659
|
+
}], cropEnabled: [{
|
|
660
|
+
type: Input,
|
|
661
|
+
args: [{ transform: booleanAttribute }]
|
|
662
|
+
}], imageExportFormat: [{
|
|
663
|
+
type: Input
|
|
664
|
+
}], imageExportQuality: [{
|
|
665
|
+
type: Input
|
|
666
|
+
}], imageSizeQueryParam: [{
|
|
667
|
+
type: Input
|
|
668
|
+
}], modalImageSize: [{
|
|
669
|
+
type: Input
|
|
670
|
+
}], deleteRequested: [{
|
|
671
|
+
type: Output
|
|
672
|
+
}], closeRequested: [{
|
|
673
|
+
type: Output
|
|
674
|
+
}], afterEditImage: [{
|
|
675
|
+
type: Output
|
|
676
|
+
}], afterEditRow: [{
|
|
677
|
+
type: Output
|
|
678
|
+
}], swiperRef: [{
|
|
679
|
+
type: ViewChild,
|
|
680
|
+
args: ['slideshowSwiper']
|
|
681
|
+
}], onWindowResize: [{
|
|
682
|
+
type: HostListener,
|
|
683
|
+
args: ['window:resize']
|
|
684
|
+
}] } });
|
|
685
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2UtZ2FsbGVyeS1zbGlkZXNob3cuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9zaGFyZWQvaW1hZ2UvZ2FsbGVyeS9zbGlkZXNob3cvaW1hZ2UtZ2FsbGVyeS1zbGlkZXNob3cuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9zaGFyZWQvaW1hZ2UvZ2FsbGVyeS9zbGlkZXNob3cvaW1hZ2UtZ2FsbGVyeS1zbGlkZXNob3cuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLGdCQUFnQixFQUNoQix1QkFBdUIsRUFFdkIsU0FBUyxFQUVULFlBQVksRUFDWixZQUFZLEVBQ1osTUFBTSxFQUNOLEtBQUssRUFFTCxNQUFNLEVBQ04sU0FBUyxHQUNWLE1BQU0sZUFBZSxDQUFDO0FBRXZCLE9BQU8sRUFBbUIsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFJOUQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzlFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQzs7Ozs7Ozs7OztBQVl0RDs7OztHQUlHO0FBT0gsTUFBTSxPQUFPLGlDQUFpQztJQW9EaEM7SUFDQTtJQUNBO0lBckRGLGdCQUFnQixHQUFHLENBQUMsQ0FBQztJQUNyQixhQUFhLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUM5QixPQUFPLENBQVM7SUFFMUIsb0NBQW9DO0lBQzFCLFVBQVUsR0FBRyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFLEVBQUUs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QUF2QixNQUFNO2dCQUNHLGNBQWM7c0JBQXZCLE1BQU07Z0JBQ0csWUFBWTtzQkFBckIsTUFBTTtnQkFFdUIsU0FBUztzQkFBdEMsU0FBUzt1QkFBQyxpQkFBaUI7Z0JBK01sQixjQUFjO3NCQUR2QixZQUFZO3VCQUFDLGVBQWUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBib29sZWFuQXR0cmlidXRlLFxuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gIENvbXBvbmVudCxcbiAgRWxlbWVudFJlZixcbiAgRXZlbnRFbWl0dGVyLFxuICBIb3N0TGlzdGVuZXIsXG4gIGluamVjdCxcbiAgSW5wdXQsXG4gIE9uRGVzdHJveSxcbiAgT3V0cHV0LFxuICBWaWV3Q2hpbGQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgVGFibGVFbGVtZW50IH0gZnJvbSAnQGUtaXMvbmd4LW1hdGVyaWFsLXRhYmxlJztcbmltcG9ydCB7IEFsZXJ0Q29udHJvbGxlciwgSW9uaWNTbGlkZXMgfSBmcm9tICdAaW9uaWMvYW5ndWxhcic7XG5pbXBvcnQgeyBUcmFuc2xhdGVTZXJ2aWNlIH0gZnJvbSAnQG5neC10cmFuc2xhdGUvY29yZSc7XG5pbXBvcnQgeyBTd2lwZXIgfSBmcm9tICdzd2lwZXIvdHlwZXMnO1xuaW1wb3J0IHsgSW1hZ2UsIEltYWdlU2l6ZSB9IGZyb20gJy4uLy4uL2ltYWdlLm1vZGVsJztcbmltcG9ydCB7IGlzTmlsT3JCbGFuayB9IGZyb20gJy4uLy4uLy4uL2Z1bmN0aW9ucyc7XG5pbXBvcnQgeyBjcmVhdGVQcm9taXNlRXZlbnRFbWl0dGVyLCBlbWl0UHJvbWlzZUV2ZW50IH0gZnJvbSAnLi4vLi4vLi4vZXZlbnRzJztcbmltcG9ydCB7IEFsZXJ0cyB9IGZyb20gJy4uLy4uLy4uL2FsZXJ0cyc7XG5pbXBvcnQgeyBBUFBfU0hPV19UT09MVElQIH0gZnJvbSAnLi4vLi4vLi4vY29uc3RhbnRzJztcbmltcG9ydCB7SW1hZ2VFZGl0RXZlbnQsIEltYWdlRXhwb3J0Rm9ybWF0fSBmcm9tICcuLi9pbWFnZS1nYWxsZXJ5Lm1vZGVsJztcblxuaW50ZXJmYWNlIENyb3BSZWN0IHtcbiAgeDogbnVtYmVyO1xuICB5OiBudW1iZXI7XG4gIHdpZHRoOiBudW1iZXI7XG4gIGhlaWdodDogbnVtYmVyO1xufVxuXG50eXBlIENyb3BIYW5kbGUgPSAnbW92ZScgfCAnbncnIHwgJ24nIHwgJ25lJyB8ICdlJyB8ICdzZScgfCAncycgfCAnc3cnIHwgJ3cnO1xuXG4vKipcbiAqIFRoZSB6b29tL3Bhbi9yb3RhdGUvY3JvcCBzbGlkZXNob3cgc2hvd24gd2hlbiBjbGlja2luZyBhbiBpbWFnZSBpbiBgQXBwSW1hZ2VHYWxsZXJ5Q29tcG9uZW50YC5cbiAqIEV4dHJhY3RlZCBpbnRvIGl0cyBvd24gY29tcG9uZW50IHNvIHRoZSBleGFjdCBzYW1lIHNsaWRlc2hvdyBjYW4gYmUgcHJlc2VudGVkIGVpdGhlciBpbnNpZGUgYW5cbiAqIGBpb24tbW9kYWxgIChkZWZhdWx0KSwgb3IgaW5saW5lIGFzIGFuIG92ZXJsYXkgZGlyZWN0bHkgd2l0aGluIHRoZSBnYWxsZXJ5LCB3aXRob3V0IGFueSBkdXBsaWNhdGlvbi5cbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYXBwLWltYWdlLWdhbGxlcnktc2xpZGVzaG93JyxcbiAgdGVtcGxhdGVVcmw6ICcuL2ltYWdlLWdhbGxlcnktc2xpZGVzaG93LmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vaW1hZ2UtZ2FsbGVyeS1zbGlkZXNob3cuY29tcG9uZW50LnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIEFwcEltYWdlR2FsbGVyeVNsaWRlc2hvd0NvbXBvbmVudDxUIGV4dGVuZHMgSW1hZ2UgPSBJbWFnZT4gaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuICBwcm90ZWN0ZWQgYWN0aXZlU2xpZGVJbmRleCA9IDA7XG4gIHByb3RlY3RlZCBzd2lwZXJNb2R1bGVzID0gW0lvbmljU2xpZGVzXTtcbiAgcHJvdGVjdGVkIF9zd2lwZXI6IFN3aXBlcjtcblxuICAvLyBNb3VzZSB3aGVlbCB6b29tICYgcm90YXRpb24gc3RhdGVcbiAgcHJvdGVjdGVkIF96b29tU3RhdGUgPSB7IHNjYWxlOiAxLCB0eDogMCwgdHk6IDAgfTtcbiAgcHJvdGVjdGVkIF9yb3RhdGlvbiA9IDA7IC8vIGRlZ3JlZXM6IDAsIDkwLCAxODAsIDI3MFxuICBwcm90ZWN0ZWQgX2RyYWdTdGF0ZTogeyBzdGFydFg6IG51bWJlcjsgc3RhcnRZOiBudW1iZXI7IHN0YXJ0VHg6IG51bWJlcjsgc3RhcnRUeTogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcbiAgcHJvdGVjdGVkIF93aGVlbExpc3RlbmVyOiAoKGU6IFdoZWVsRXZlbnQpID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG4gIHByb3RlY3RlZCBfd2hlZWxFbDogSFRNTEVsZW1lbnQgfCBudWxsID0gbnVsbDtcblxuICAvLyBDcm9wICYgcm90YXRlIHN0YXRlLiBSb3RhdGlvbiBpcyBhbHdheXMgYXZhaWxhYmxlOyB0aGUgY3JvcCBncmlkL2hhbmRsZXMgb25seSBzaG93IG9uY2UgdGhlXG4gIC8vIGNyb3AgdG9vbCBoYXMgYmVlbiBleHBsaWNpdGx5IGFjdGl2YXRlZCAoZmFiIGJ1dHRvbikuIFZhbGlkYXRlL2NhbmNlbCBzaG93IHVwIG9uY2Ugc29tZXRoaW5nIGNoYW5nZWQuXG4gIHByb3RlY3RlZCBfY3JvcFRvb2xBY3RpdmUgPSBmYWxzZTtcbiAgcHJvdGVjdGVkIF9jcm9wUmVjdDogQ3JvcFJlY3QgPSB7IHg6IDAsIHk6IDAsIHdpZHRoOiAxLCBoZWlnaHQ6IDEgfTtcbiAgcHJvdGVjdGVkIF9jcm9wT3ZlcmxheVJlY3Q6IHsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSB8IG51bGwgPSBudWxsO1xuICBwcm90ZWN0ZWQgX2Nyb3BEcmFnU3RhdGU6IHsgaGFuZGxlOiBDcm9wSGFuZGxlOyBzdGFydFg6IG51bWJlcjsgc3RhcnRZOiBudW1iZXI7IHN0YXJ0UmVjdDogQ3JvcFJlY3QgfSB8IG51bGwgPSBudWxsO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgX2Nyb3BIYW5kbGVzOiBDcm9wSGFuZGxlW10gPSBbJ253JywgJ24nLCAnbmUnLCAnZScsICdzZScsICdzJywgJ3N3JywgJ3cnXTtcblxuICBASW5wdXQoKSByb3dzOiBUYWJsZUVsZW1lbnQ8VD5bXSB8IFJlYWRvbmx5QXJyYXk8VGFibGVFbGVtZW50PFQ+PiA9IFtdO1xuICBASW5wdXQoKSBzZWxlY3RlZEluZGV4ID0gMDtcbiAgQElucHV0KHsgdHJhbnNmb3JtOiBib29sZWFuQXR0cmlidXRlIH0pIG1vYmlsZSA9IGZhbHNlO1xuICBASW5wdXQoeyB0cmFuc2Zvcm06IGJvb2xlYW5BdHRyaWJ1dGUgfSkgcmVhZE9ubHkgPSBmYWxzZTtcbiAgQElucHV0KHsgdHJhbnNmb3JtOiBib29sZWFuQXR0cmlidXRlIH0pIGRpc2FibGVkID0gZmFsc2U7XG4gIEBJbnB1dCh7IHRyYW5zZm9ybTogYm9vbGVhbkF0dHJpYnV0ZSB9KSBkZWJ1ZyA9IGZhbHNlO1xuICBASW5wdXQoeyB0cmFuc2Zvcm06IGJvb2xlYW5BdHRyaWJ1dGUgfSkgbW91c2Vab29tRW5hYmxlZCA9IGZhbHNlO1xuICBASW5wdXQoKSB3aGVlbFpvb21TdGVwID0gMC4zO1xuICBASW5wdXQoKSBtYXhab29tUmF0aW8gPSAzO1xuICBASW5wdXQoeyB0cmFuc2Zvcm06IGJvb2xlYW5BdHRyaWJ1dGUgfSkgcm90YXRpb25FbmFibGVkID0gdHJ1ZTtcbiAgQElucHV0KHsgdHJhbnNmb3JtOiBib29sZWFuQXR0cmlidXRlIH0pIGNyb3BFbmFibGVkID0gdHJ1ZTtcbiAgQElucHV0KCkgaW1hZ2VFeHBvcnRGb3JtYXQ6IEltYWdlRXhwb3J0Rm9ybWF0ID0gJ3BuZyc7XG4gIEBJbnB1dCgpIGltYWdlRXhwb3J0UXVhbGl0eSA9IDAuOTI7XG4gIEBJbnB1dCgpIGltYWdlU2l6ZVF1ZXJ5UGFyYW0gPSAnc2l6ZSc7XG4gIEBJbnB1dCgpIG1vZGFsSW1hZ2VTaXplOiBJbWFnZVNpemUgfCBzdHJpbmc7XG5cbiAgLyoqIFJlcXVlc3RzIGRlbGV0aW9uIG9mIHRoZSBnaXZlbiByb3cuIFRoZSBjYWxsZXIgKGdhbGxlcnkpIG93bnMgdGhlIGNvbmZpcm1hdGlvbi9kZWxldGlvbiBsb2dpYyBhbmQgcmVzb2x2ZXMgdGhlIHByb21pc2Ugd2l0aCB3aGV0aGVyIGl0IGFjdHVhbGx5IGhhcHBlbmVkLiAqL1xuICBAT3V0cHV0KCkgZGVsZXRlUmVxdWVzdGVkID0gY3JlYXRlUHJvbWlzZUV2ZW50RW1pdHRlcjxib29sZWFuLCB7IHJvdzogVGFibGVFbGVtZW50PFQ+IH0+KCk7XG4gIEBPdXRwdXQoKSBjbG9zZVJlcXVlc3RlZCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIGFmdGVyRWRpdEltYWdlID0gbmV3IEV2ZW50RW1pdHRlcjxJbWFnZUVkaXRFdmVudDxUPj4oKTtcbiAgQE91dHB1dCgpIGFmdGVyRWRpdFJvdyA9IG5ldyBFdmVudEVtaXR0ZXI8VGFibGVFbGVtZW50PFQ+PigpO1xuXG4gIEBWaWV3Q2hpbGQoJ3NsaWRlc2hvd1N3aXBlcicpIHN3aXBlclJlZjogRWxlbWVudFJlZjtcblxuICBzaG93VG9vbHRpcCA9IGluamVjdChBUFBfU0hPV19UT09MVElQLCB7IG9wdGlvbmFsOiB0cnVlIH0pO1xuXG4gIHByb3RlY3RlZCBnZXQgaGFzUGVuZGluZ0VkaXQoKTogYm9vbGVhbiB7XG4gICAgY29uc3QgciA9IHRoaXMuX2Nyb3BSZWN0O1xuICAgIHJldHVybiB0aGlzLl9yb3RhdGlvbiAhPT0gMCB8fCByLnggIT09IDAgfHwgci55ICE9PSAwIHx8IHIud2lkdGggIT09IDEgfHwgci5oZWlnaHQgIT09IDE7XG4gIH1cblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcm90ZWN0ZWQgYWx0ZXJDdHJsOiBBbGVydENvbnRyb2xsZXIsXG4gICAgcHJvdGVjdGVkIHRyYW5zbGF0ZTogVHJhbnNsYXRlU2VydmljZSxcbiAgICBwcm90ZWN0ZWQgY2Q6IENoYW5nZURldGVjdG9yUmVmXG4gICkge31cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLl9jbGVhbnVwV2hlZWxab29tKCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQ/OiBhbnkpOiBTd2lwZXIge1xuICAgIHRoaXMuX3N3aXBlciA9IChzd2lwZXJFbGVtZW50IHx8IHRoaXMuc3dpcGVyUmVmPy5uYXRpdmVFbGVtZW50KT8uc3dpcGVyIHx8IHRoaXMuX3N3aXBlcjtcbiAgICByZXR1cm4gdGhpcy5fc3dpcGVyO1xuICB9XG5cbiAgcHJvdGVjdGVkIG9uU2xpZGVDaGFuZ2Uoc3dpcGVyRWxlbWVudDogYW55KSB7XG4gICAgY29uc3Qgc3dpcGVyID0gdGhpcy5nZXRTd2lwZXIoc3dpcGVyRWxlbWVudCk7XG4gICAgdGhpcy5hY3RpdmVTbGlkZUluZGV4ID0gc3dpcGVyLmFjdGl2ZUluZGV4O1xuICAgIHRoaXMuX3Jlc2V0Wm9vbShzd2lwZXJFbGVtZW50KTtcbiAgICB0aGlzLl91cGRhdGVTd2lwZXJUb3VjaE1vdmUoc3dpcGVyRWxlbWVudCk7XG4gICAgdGhpcy5tYXJrRm9yQ2hlY2soKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvblN3aXBlckluaXQoc3dpcGVyRWxlbWVudDogYW55KSB7XG4gICAgdGhpcy5fcmVzZXRab29tKCk7XG4gICAgaWYgKHRoaXMuc2VsZWN0ZWRJbmRleCA+IDApIHtcbiAgICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmRlYnVnKGBbaW1hZ2UtZ2FsbGVyeS1zbGlkZXNob3ddIEluaXRpYWxpemluZyBzbGlkZXNob3csIHNlbGVjdGVkSW5kZXg9JHt0aGlzLnNlbGVjdGVkSW5kZXh9YCk7XG4gICAgICAvLyBVc2UgYSBzZXRUaW1lb3V0IG90aGVyd2lzZSB0aGUgc3dpcGVyIGNhbm5vdCBzbGlkZVxuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGNvbnN0IHN3aXBlciA9IHRoaXMuZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQpO1xuICAgICAgICBzd2lwZXIuc2xpZGVUbyh0aGlzLnNlbGVjdGVkSW5kZXgsIDApO1xuICAgICAgfSk7XG4gICAgfVxuICAgIHRoaXMuX3NldHVwV2hlZWxab29tKHN3aXBlckVsZW1lbnQpO1xuICAgIHRoaXMuX3VwZGF0ZVN3aXBlclRvdWNoTW92ZShzd2lwZXJFbGVtZW50KTtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHRoaXMuX3VwZGF0ZUVkaXRJbWFnZVNpemUoc3dpcGVyRWxlbWVudCk7XG4gICAgICB0aGlzLl91cGRhdGVDcm9wT3ZlcmxheVBvc2l0aW9uKHN3aXBlckVsZW1lbnQpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFN3aXBlcidzIG93biB0b3VjaC9tb3VzZS1kcmFnLXRvLXN3aXBlIGdlc3R1cmUgaXMgY2FwdHVyZWQgdmVyeSBlYXJseSAoYmVmb3JlIG91ciBjcm9wIG92ZXJsYXknc1xuICAgKiBvd24gbW91c2Vkb3duIGhhbmRsZXIgY2FuIHN0b3AgaXQpLCBzbyBpdCBtdXN0IGJlIGRpc2FibGVkIHVwZnJvbnQgd2hlbmV2ZXIgdGhlIGNyb3AgdG9vbCBpcyBhY3RpdmVcbiAgICogLS0gb3RoZXJ3aXNlIGRyYWdnaW5nIGluc2lkZSB0aGUgY3JvcCBzZWxlY3Rpb24gZW5kcyB1cCBzbGlkaW5nIHRvIHRoZSBuZXh0L3ByZXZpb3VzIGltYWdlIGluc3RlYWQuXG4gICAqIFByZXYvbmV4dCBhcnJvdyBidXR0b25zIGtlZXAgd29ya2luZywgc2luY2UgdGhleSBjYWxsIHRoZSBTd2lwZXIgQVBJIGRpcmVjdGx5LlxuICAgKi9cbiAgcHJvdGVjdGVkIF91cGRhdGVTd2lwZXJUb3VjaE1vdmUoc3dpcGVyRWxlbWVudDogYW55KSB7XG4gICAgY29uc3Qgc3dpcGVyID0gdGhpcy5nZXRTd2lwZXIoc3dpcGVyRWxlbWVudCk7XG4gICAgaWYgKCFzd2lwZXIpIHJldHVybjtcbiAgICBzd2lwZXIuYWxsb3dUb3VjaE1vdmUgPSAhdGhpcy5fY3JvcFRvb2xBY3RpdmU7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgem9vbShzd2lwZXJFbGVtZW50OiBhbnksIHpvb21JbjogYm9vbGVhbikge1xuICAgIGlmICh0aGlzLmRlYnVnKSBjb25zb2xlLmRlYnVnKCdbaW1hZ2UtZ2FsbGVyeS1zbGlkZXNob3ddIHpvb20gJyArICh6b29tSW4gPyAnaW4nIDogJ291dCcpLCBzd2lwZXJFbGVtZW50KTtcbiAgICBpZiAodGhpcy5tb3VzZVpvb21FbmFibGVkKSB7XG4gICAgICBjb25zdCBkZWx0YSA9IHpvb21JbiA/IHRoaXMud2hlZWxab29tU3RlcCA6IC10aGlzLndoZWVsWm9vbVN0ZXA7XG4gICAgICBjb25zdCBuZXdTY2FsZSA9IE1hdGgubWF4KDEsIE1hdGgubWluKHRoaXMuX3pvb21TdGF0ZS5zY2FsZSArIGRlbHRhLCB0aGlzLm1heFpvb21SYXRpbykpO1xuICAgICAgaWYgKG5ld1NjYWxlID09PSB0aGlzLl96b29tU3RhdGUuc2NhbGUpIHJldHVybjtcbiAgICAgIGlmIChuZXdTY2FsZSA9PT0gMSkge1xuICAgICAgICB0aGlzLl96b29tU3RhdGUgPSB7IHNjYWxlOiAxLCB0eDogMCwgdHk6IDAgfTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuX3pvb21TdGF0ZS5zY2FsZSA9IG5ld1NjYWxlO1xuICAgICAgICB0aGlzLl9jbGFtcFRyYW5zbGF0aW9ucyhzd2lwZXJFbGVtZW50KTtcbiAgICAgIH1cbiAgICAgIHRoaXMuX2FwcGx5Wm9vbShzd2lwZXJFbGVtZW50KTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3Qgc3dpcGVyID0gdGhpcy5nZXRTd2lwZXIoc3dpcGVyRWxlbWVudCk7XG4gICAgICBpZiAoem9vbUluKSBzd2lwZXIuem9vbS5pbigpO1xuICAgICAgZWxzZSBzd2lwZXIuem9vbS5vdXQoKTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgcmVxdWVzdERlbGV0ZShzd2lwZXJFbGVtZW50OiBhbnkpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBzd2lwZXIgPSB0aGlzLmdldFN3aXBlcihzd2lwZXJFbGVtZW50KTtcbiAgICBjb25zdCBjdXJyZW50Um93ID0gdGhpcy5yb3dzLmZpbmQoKHJvdywgaW5kZXgpID0+IGluZGV4ID09PSBzd2lwZXIuYWN0aXZlSW5kZXgpO1xuICAgIGlmICghY3VycmVudFJvdykgcmV0dXJuIGZhbHNlOyAvLyBSb3cgbm90IGZvdW5kXG5cbiAgICBsZXQgZGVsZXRlZCA9IGZhbHNlO1xuICAgIHRyeSB7XG4gICAgICBkZWxldGVkID0gYXdhaXQgZW1pdFByb21pc2VFdmVudCh0aGlzLmRlbGV0ZVJlcXVlc3RlZCwgJ2RlbGV0ZScsIHsgZGV0YWlsOiB7IHJvdzogY3VycmVudFJvdyB9IH0pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgaWYgKGVyciA9PT0gJ0NBTkNFTExFRCcpIHJldHVybiBmYWxzZTsgLy8gVXNlciBjYW5jZWxcbiAgICAgIHRocm93IGVycjtcbiAgICB9XG5cbiAgICBpZiAoZGVsZXRlZCkge1xuICAgICAgaWYgKCF0aGlzLnJvd3M/Lmxlbmd0aCkge1xuICAgICAgICB0aGlzLmNsb3NlUmVxdWVzdGVkLmVtaXQoKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuX3Jlc2V0Wm9vbShzd2lwZXJFbGVtZW50KTtcbiAgICAgICAgc3dpcGVyLnVwZGF0ZSgpO1xuICAgICAgICB0aGlzLm1hcmtGb3JDaGVjaygpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZGVsZXRlZDtcbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyBzbGlkZVByZXYoc3dpcGVyRWxlbWVudDogYW55KSB7XG4gICAgY29uc3Qgc3dpcGVyID0gdGhpcy5nZXRTd2lwZXIoc3dpcGVyRWxlbWVudCk7XG4gICAgY29uc3QgYWN0aXZlSW5kZXggPSBzd2lwZXIuYWN0aXZlSW5kZXg7XG4gICAgaWYgKGFjdGl2ZUluZGV4ID09PSAwKSB7XG4gICAgICAvLyBMb29wIHRvIGxhc3RcbiAgICAgIGF3YWl0IHN3aXBlci5zbGlkZVRvKHRoaXMucm93cy5sZW5ndGggLSAxLCAwIC8qbm8gdHJhbnNpdGlvbiwgdG8gYXZvaWQgYSBcInJldmVydFwiIGVmZmVjdCovKTtcbiAgICB9IGVsc2Uge1xuICAgICAgYXdhaXQgc3dpcGVyLnNsaWRlUHJldigpO1xuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyBzbGlkZU5leHQoc3dpcGVyRWxlbWVudDogYW55KSB7XG4gICAgY29uc3Qgc3dpcGVyID0gdGhpcy5nZXRTd2lwZXIoc3dpcGVyRWxlbWVudCk7XG4gICAgY29uc3QgYWN0aXZlSW5kZXggPSBzd2lwZXIuYWN0aXZlSW5kZXg7XG4gICAgaWYgKGFjdGl2ZUluZGV4ID09PSB0aGlzLnJvd3MubGVuZ3RoIC0gMSkge1xuICAgICAgLy8gTG9vcCB0byBmaXJzdFxuICAgICAgYXdhaXQgc3dpcGVyLnNsaWRlVG8oMCwgMCAvKm5vIHRyYW5zaXRpb24sIHRvIGF2b2lkIGEgXCJyZXZlcnRcIiBlZmZlY3QqLyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3YWl0IHN3aXBlci5zbGlkZU5leHQoKTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0SW1hZ2VTaXplUXVlcnlQYXJhbXMoKTogeyBba2V5OiBzdHJpbmddOiBhbnkgfSB7XG4gICAgaWYgKCF0aGlzLmltYWdlU2l6ZVF1ZXJ5UGFyYW0gfHwgaXNOaWxPckJsYW5rKHRoaXMubW9kYWxJbWFnZVNpemUpKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgIHJldHVybiB7XG4gICAgICBbdGhpcy5pbWFnZVNpemVRdWVyeVBhcmFtXTogdGhpcy5tb2RhbEltYWdlU2l6ZSxcbiAgICB9O1xuICB9XG5cbiAgLy8gLS0tIENyb3AgJiByb3RhdGUgdG9vbHM6IGFsd2F5cyBhdmFpbGFibGUgKG5vIFwiZW50ZXIgZWRpdCBtb2RlXCIgc3RlcCkgLS0tXG4gIC8vIFZhbGlkYXRlL0NhbmNlbCBvbmx5IGFwcGVhciBvbmNlIHRoZSB1c2VyIGFjdHVhbGx5IGNoYW5nZWQgc29tZXRoaW5nIChzZWUgYGhhc1BlbmRpbmdFZGl0YCkuXG5cbiAgcHJvdGVjdGVkIGRpc2NhcmRFZGl0KHN3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIHRoaXMuX3Jlc2V0RWRpdFN0YXRlKHN3aXBlckVsZW1lbnQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlc2V0Q3JvcChfc3dpcGVyRWxlbWVudDogYW55KSB7XG4gICAgdGhpcy5fY3JvcFJlY3QgPSB7IHg6IDAsIHk6IDAsIHdpZHRoOiAxLCBoZWlnaHQ6IDEgfTtcbiAgICB0aGlzLm1hcmtGb3JDaGVjaygpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRvZ2dsZUNyb3BUb29sKHN3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIGlmICghdGhpcy5jcm9wRW5hYmxlZCB8fCB0aGlzLnJlYWRPbmx5KSByZXR1cm47XG4gICAgdGhpcy5fY3JvcFRvb2xBY3RpdmUgPSAhdGhpcy5fY3JvcFRvb2xBY3RpdmU7XG4gICAgLy8gRGlzY2FyZCBhbnkgcGVuZGluZyBjcm9wIHNlbGVjdGlvbiB3aGVuIGhpZGluZyB0aGUgdG9vbFxuICAgIGlmICghdGhpcy5fY3JvcFRvb2xBY3RpdmUpIHtcbiAgICAgIHRoaXMuX2Nyb3BSZWN0ID0geyB4OiAwLCB5OiAwLCB3aWR0aDogMSwgaGVpZ2h0OiAxIH07XG4gICAgICB0aGlzLl9jcm9wRHJhZ1N0YXRlID0gbnVsbDtcbiAgICB9XG4gICAgdGhpcy5fdXBkYXRlU3dpcGVyVG91Y2hNb3ZlKHN3aXBlckVsZW1lbnQpO1xuICAgIHRoaXMubWFya0ZvckNoZWNrKCk7XG4gICAgaWYgKHRoaXMuX2Nyb3BUb29sQWN0aXZlKSB7XG4gICAgICAvLyBXYWl0IGZvciB0aGUgY3JvcCBvdmVybGF5IHRvIGJlIHJlbmRlcmVkLCB0aGVuIGNvbXB1dGUgaXRzIHBvc2l0aW9uXG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuX3VwZGF0ZUNyb3BPdmVybGF5UG9zaXRpb24oc3dpcGVyRWxlbWVudCkpO1xuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyB2YWxpZGF0ZUVkaXQoc3dpcGVyRWxlbWVudDogYW55KSB7XG4gICAgY29uc3Qgc3dpcGVyID0gdGhpcy5nZXRTd2lwZXIoc3dpcGVyRWxlbWVudCk7XG4gICAgY29uc3QgY3VycmVudFJvdyA9IHRoaXMucm93cy5maW5kKChyb3csIGluZGV4KSA9PiBpbmRleCA9PT0gc3dpcGVyLmFjdGl2ZUluZGV4KTtcbiAgICBpZiAoIWN1cnJlbnRSb3cpIHJldHVybjtcblxuICAgIGNvbnN0IGltYWdlID0gY3VycmVudFJvdy5jdXJyZW50RGF0YTtcbiAgICBjb25zdCBmb3JtYXQgPSB0aGlzLmltYWdlRXhwb3J0Rm9ybWF0O1xuICAgIGNvbnN0IG1pbWVUeXBlID0gZm9ybWF0ID09PSAnanBlZycgPyAnaW1hZ2UvanBlZycgOiAnaW1hZ2UvcG5nJztcbiAgICBjb25zdCBxdWFsaXR5ID0gZm9ybWF0ID09PSAnanBlZycgPyB0aGlzLmltYWdlRXhwb3J0UXVhbGl0eSA6IHVuZGVmaW5lZDtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBjYW52YXMgPSBhd2FpdCB0aGlzLl9yZW5kZXJFZGl0ZWRJbWFnZShpbWFnZSwgdGhpcy5fcm90YXRpb24sIHRoaXMuX2Nyb3BSZWN0KTtcbiAgICAgIGNvbnN0IGRhdGFVcmwgPSBjYW52YXMudG9EYXRhVVJMKG1pbWVUeXBlLCBxdWFsaXR5KTtcbiAgICAgIGNvbnN0IGJsb2IgPSBhd2FpdCBuZXcgUHJvbWlzZTxCbG9iPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIGNhbnZhcy50b0Jsb2IoKGIpID0+IChiID8gcmVzb2x2ZShiKSA6IHJlamVjdChuZXcgRXJyb3IoJ2NhbnZhcy50b0Jsb2IoKSBmYWlsZWQnKSkpLCBtaW1lVHlwZSwgcXVhbGl0eSk7XG4gICAgICB9KTtcblxuICAgICAgLy8gVXBkYXRlIHRoZSByb3csIHNvIHRoZSBzbGlkZXNob3cgYW5kIHRodW1ibmFpbCByZWZsZWN0IHRoZSBjaGFuZ2UgaW1tZWRpYXRlbHlcbiAgICAgIGltYWdlLmRhdGFVcmwgPSBkYXRhVXJsO1xuICAgICAgaW1hZ2UudXJsID0gbnVsbDtcblxuICAgICAgdGhpcy5hZnRlckVkaXRJbWFnZS5lbWl0KHsgcm93OiBjdXJyZW50Um93LCBpbWFnZSwgZGF0YVVybCwgYmxvYiwgZm9ybWF0IH0pO1xuICAgICAgdGhpcy5hZnRlckVkaXRSb3cuZW1pdChjdXJyZW50Um93KTtcblxuICAgICAgdGhpcy5fcmVzZXRFZGl0U3RhdGUoc3dpcGVyRWxlbWVudCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdbaW1hZ2UtZ2FsbGVyeS1zbGlkZXNob3ddIEZhaWxlZCB0byBleHBvcnQgdGhlIGVkaXRlZCBpbWFnZScsIGVycik7XG4gICAgICBhd2FpdCBBbGVydHMuc2hvd0Vycm9yKCdJTUFHRS5HQUxMRVJZLkVSUk9SX0VESVRfSU1BR0UnLCB0aGlzLmFsdGVyQ3RybCwgdGhpcy50cmFuc2xhdGUpO1xuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBfcmVzZXRFZGl0U3RhdGUoc3dpcGVyRWxlbWVudDogYW55KSB7XG4gICAgdGhpcy5fcm90YXRpb24gPSAwO1xuICAgIHRoaXMuX2Nyb3BSZWN0ID0geyB4OiAwLCB5OiAwLCB3aWR0aDogMSwgaGVpZ2h0OiAxIH07XG4gICAgdGhpcy5fY3JvcERyYWdTdGF0ZSA9IG51bGw7XG4gICAgdGhpcy5fY3JvcFRvb2xBY3RpdmUgPSBmYWxzZTtcbiAgICB0aGlzLl9hcHBseVpvb20oc3dpcGVyRWxlbWVudCk7XG4gICAgdGhpcy5fdXBkYXRlRWRpdEltYWdlU2l6ZShzd2lwZXJFbGVtZW50KTtcbiAgICB0aGlzLl91cGRhdGVTd2lwZXJUb3VjaE1vdmUoc3dpcGVyRWxlbWVudCk7XG4gICAgdGhpcy5tYXJrRm9yQ2hlY2soKTtcbiAgfVxuXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzpyZXNpemUnKVxuICBwcm90ZWN0ZWQgb25XaW5kb3dSZXNpemUoKSB7XG4gICAgY29uc3Qgc3dpcGVyRWxlbWVudCA9IHRoaXMuc3dpcGVyUmVmPy5uYXRpdmVFbGVtZW50O1xuICAgIGlmICghc3dpcGVyRWxlbWVudCkgcmV0dXJuO1xuICAgIHRoaXMuX3VwZGF0ZUVkaXRJbWFnZVNpemUoc3dpcGVyRWxlbWVudCk7XG4gICAgdGhpcy5fdXBkYXRlQ3JvcE92ZXJsYXlQb3NpdGlvbihzd2lwZXJFbGVtZW50KTtcbiAgfVxuXG4gIHByb3RlY3RlZCByb3RhdGUoc3dpcGVyRWxlbWVudDogYW55LCBkaXJlY3Rpb246ICdsZWZ0JyB8ICdyaWdodCcpIHtcbiAgICBpZiAoIXRoaXMucm90YXRpb25FbmFibGVkKSByZXR1cm47XG4gICAgY29uc3QgZGVsdGEgPSBkaXJlY3Rpb24gPT09ICdyaWdodCcgPyA5MCA6IC05MDtcbiAgICB0aGlzLl9yb3RhdGlvbiA9ICgoKHRoaXMuX3JvdGF0aW9uICsgZGVsdGEpICUgMzYwKSArIDM2MCkgJSAzNjA7XG4gICAgdGhpcy5fYXBwbHlab29tKHN3aXBlckVsZW1lbnQpO1xuICAgIHRoaXMuX3VwZGF0ZUVkaXRJbWFnZVNpemUoc3dpcGVyRWxlbWVudCk7XG4gICAgdGhpcy5fdXBkYXRlQ3JvcE92ZXJsYXlQb3NpdGlvbihzd2lwZXJFbGVtZW50KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb25zdHJhaW5zIHRoZSBkaXNwbGF5ZWQgaW1hZ2Ugc28gdGhhdCwgZXZlbiBvbmNlIHJvdGF0ZWQgOTDCsC8yNzDCsCB2aWEgQ1NTIHRyYW5zZm9ybSxcbiAgICogaXRzIHZpc3VhbCBib3VuZGluZyBib3ggYWx3YXlzIGZpdHMgaW5zaWRlIHRoZSBzbGlkZSBjb250YWluZXIgKG5vIG92ZXJmbG93KS5cbiAgICovXG4gIHByb3RlY3RlZCBfdXBkYXRlRWRpdEltYWdlU2l6ZShzd2lwZXJFbGVtZW50OiBhbnksIHJldHJpZXNMZWZ0ID0gNjApIHtcbiAgICBjb25zdCBzd2lwZXIgPSB0aGlzLmdldFN3aXBlcihzd2lwZXJFbGVtZW50KTtcbiAgICBjb25zdCBhY3RpdmVTbGlkZSA9IHN3aXBlcj8uc2xpZGVzPy5bc3dpcGVyLmFjdGl2ZUluZGV4XSBhcyBIVE1MRWxlbWVudDtcbiAgICBpZiAoIWFjdGl2ZVNsaWRlKSByZXR1cm47XG4gICAgY29uc3QgY29udGFpbmVyRWwgPSBhY3RpdmVTbGlkZS5xdWVyeVNlbGVjdG9yKCcuc3dpcGVyLXpvb20tY29udGFpbmVyJykgYXMgSFRNTEVsZW1lbnQ7XG4gICAgY29uc3QgaW1nRWwgPSBhY3RpdmVTbGlkZS5xdWVyeVNlbGVjdG9yKCcuc3dpcGVyLXpvb20tdGFyZ2V0LCAuc3dpcGVyLXpvb20tY29udGFpbmVyID4gaW1nJykgYXMgSFRNTEVsZW1lbnQ7XG4gICAgaWYgKCFjb250YWluZXJFbCB8fCAhaW1nRWwpIHJldHVybjtcbiAgICBpZiAoIXRoaXMuY3JvcEVuYWJsZWQgJiYgIXRoaXMucm90YXRpb25FbmFibGVkKSB7XG4gICAgICBpbWdFbC5zdHlsZS53aWR0aCA9ICcnO1xuICAgICAgaW1nRWwuc3R5bGUuaGVpZ2h0ID0gJyc7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGNvbnRhaW5lclJlY3QgPSBjb250YWluZXJFbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICBjb25zdCBuYXR1cmFsU2l6ZSA9IHRoaXMuX2dldEltYWdlTmF0dXJhbFNpemUoaW1nRWwpO1xuICAgIC8vIFRoZSBjb250YWluZXIvaW1hZ2UgbWF5IG5vdCBiZSBsYWlkIG91dCBvciBsb2FkZWQgeWV0OiBza2lwIGFuZCByZXRyeSBvbiB0aGUgbmV4dCBmcmFtZVxuICAgIGlmICghY29udGFpbmVyUmVjdC53aWR0aCB8fCAhY29udGFpbmVyUmVjdC5oZWlnaHQgfHwgIW5hdHVyYWxTaXplKSB7XG4gICAgICBpZiAocmV0cmllc0xlZnQgPiAwKSB7XG4gICAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiB0aGlzLl91cGRhdGVFZGl0SW1hZ2VTaXplKHN3aXBlckVsZW1lbnQsIHJldHJpZXNMZWZ0IC0gMSkpO1xuICAgICAgfVxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBDb21wdXRlIHRoZSBleGFjdCBib3ggKHByZXNlcnZpbmcgdGhlIGltYWdlJ3MgYXNwZWN0IHJhdGlvKSB0aGF0IGZpdHMgdGhlIGF2YWlsYWJsZSBzcGFjZSxcbiAgICAvLyBzd2FwcGluZyB3aWR0aC9oZWlnaHQgd2hlbiByb3RhdGVkIDkwwrAvMjcwwrAgc28gdGhlIHJvdGF0ZWQgdmlzdWFsIGJveCBuZXZlciBvdmVyZmxvd3MuXG4gICAgY29uc3Qgc3dhcHBlZCA9IHRoaXMuX3JvdGF0aW9uICUgMTgwICE9PSAwO1xuICAgIGNvbnN0IGF2YWlsV2lkdGggPSBzd2FwcGVkID8gY29udGFpbmVyUmVjdC5oZWlnaHQgOiBjb250YWluZXJSZWN0LndpZHRoO1xuICAgIGNvbnN0IGF2YWlsSGVpZ2h0ID0gc3dhcHBlZCA/IGNvbnRhaW5lclJlY3Qud2lkdGggOiBjb250YWluZXJSZWN0LmhlaWdodDtcbiAgICBjb25zdCByYXRpbyA9IE1hdGgubWluKGF2YWlsV2lkdGggLyBuYXR1cmFsU2l6ZS53aWR0aCwgYXZhaWxIZWlnaHQgLyBuYXR1cmFsU2l6ZS5oZWlnaHQpO1xuICAgIGltZ0VsLnN0eWxlLndpZHRoID0gYCR7bmF0dXJhbFNpemUud2lkdGggKiByYXRpb31weGA7XG4gICAgaW1nRWwuc3R5bGUuaGVpZ2h0ID0gYCR7bmF0dXJhbFNpemUuaGVpZ2h0ICogcmF0aW99cHhgO1xuICB9XG5cbiAgcHJvdGVjdGVkIF9nZXRJbWFnZU5hdHVyYWxTaXplKGltZ0VsOiBIVE1MRWxlbWVudCk6IHsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSB8IG51bGwge1xuICAgIGNvbnN0IGlubmVyID0gKGltZ0VsLnRhZ05hbWUgPT09ICdJT04tSU1HJyA/IGltZ0VsLnNoYWRvd1Jvb3Q/LnF1ZXJ5U2VsZWN0b3IoJ2ltZycpIDogaW1nRWwpIGFzIEhUTUxJbWFnZUVsZW1lbnQ7XG4gICAgaWYgKCFpbm5lcj8ubmF0dXJhbFdpZHRoIHx8ICFpbm5lcj8ubmF0dXJhbEhlaWdodCkgcmV0dXJuIG51bGw7XG4gICAgcmV0dXJuIHsgd2lkdGg6IGlubmVyLm5hdHVyYWxXaWR0aCwgaGVpZ2h0OiBpbm5lci5uYXR1cmFsSGVpZ2h0IH07XG4gIH1cblxuICAvLyAtLS0gQ3JvcCBvdmVybGF5OiBkcmFnIHRvIG1vdmUvcmVzaXplIHRoZSBjcm9wIHJlY3RhbmdsZSAtLS1cblxuICBwcm90ZWN0ZWQgb25Dcm9wUG9pbnRlckRvd24oZXZlbnQ6IE1vdXNlRXZlbnQgfCBUb3VjaEV2ZW50LCBzd2lwZXJFbGVtZW50OiBhbnksIGhhbmRsZTogQ3JvcEhhbmRsZSkge1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgY29uc3QgcHQgPSB0aGlzLl9nZXRFdmVudFBvaW50KGV2ZW50KTtcbiAgICB0aGlzLl9jcm9wT3ZlcmxheVJlY3QgPSB0aGlzLl9jcm9wT3ZlcmxheVJlY3QgPz8gdGhpcy5fdXBkYXRlQ3JvcE92ZXJsYXlQb3NpdGlvbihzd2lwZXJFbGVtZW50KTtcbiAgICB0aGlzLl9jcm9wRHJhZ1N0YXRlID0ge1xuICAgICAgaGFuZGxlLFxuICAgICAgc3RhcnRYOiBwdC54LFxuICAgICAgc3RhcnRZOiBwdC55LFxuICAgICAgc3RhcnRSZWN0OiB7IC4uLnRoaXMuX2Nyb3BSZWN0IH0sXG4gICAgfTtcbiAgICAvLyBBbHNvIGRpc2FibGUgcHJvZ3JhbW1hdGljIHNsaWRlIG5hdmlnYXRpb24gd2hpbGUgZHJhZ2dpbmcgKGFsbG93VG91Y2hNb3ZlIGlzIGFscmVhZHlcbiAgICAvLyBwZXJtYW5lbnRseSBvZmYgd2hlbmV2ZXIgY3JvcCBpcyBlbmFibGVkLCBzZWUgYF91cGRhdGVTd2lwZXJUb3VjaE1vdmVgKVxuICAgIGNvbnN0IHN3aXBlciA9IHRoaXMuZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQpO1xuICAgIGlmIChzd2lwZXIpIHtcbiAgICAgIHN3aXBlci5hbGxvd1NsaWRlTmV4dCA9IGZhbHNlO1xuICAgICAgc3dpcGVyLmFsbG93U2xpZGVQcmV2ID0gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIG9uQ3JvcFBvaW50ZXJNb3ZlKGV2ZW50OiBNb3VzZUV2ZW50IHwgVG91Y2hFdmVudCwgX3N3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIGlmICghdGhpcy5fY3JvcERyYWdTdGF0ZSkgcmV0dXJuO1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgY29uc3QgcmVjdCA9IHRoaXMuX2Nyb3BPdmVybGF5UmVjdDtcbiAgICBpZiAoIXJlY3Q/LndpZHRoIHx8ICFyZWN0Py5oZWlnaHQpIHJldHVybjtcbiAgICBjb25zdCBwdCA9IHRoaXMuX2dldEV2ZW50UG9pbnQoZXZlbnQpO1xuICAgIGNvbnN0IGR4ID0gKHB0LnggLSB0aGlzLl9jcm9wRHJhZ1N0YXRlLnN0YXJ0WCkgLyByZWN0LndpZHRoO1xuICAgIGNvbnN0IGR5ID0gKHB0LnkgLSB0aGlzLl9jcm9wRHJhZ1N0YXRlLnN0YXJ0WSkgLyByZWN0LmhlaWdodDtcbiAgICB0aGlzLl9jcm9wUmVjdCA9IHRoaXMuX2NvbXB1dGVDcm9wUmVjdCh0aGlzLl9jcm9wRHJhZ1N0YXRlLmhhbmRsZSwgdGhpcy5fY3JvcERyYWdTdGF0ZS5zdGFydFJlY3QsIGR4LCBkeSk7XG4gICAgdGhpcy5tYXJrRm9yQ2hlY2soKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvbkNyb3BQb2ludGVyVXAoX2V2ZW50OiBNb3VzZUV2ZW50IHwgVG91Y2hFdmVudCwgc3dpcGVyRWxlbWVudD86IGFueSkge1xuICAgIGlmICghdGhpcy5fY3JvcERyYWdTdGF0ZSkgcmV0dXJuO1xuICAgIHRoaXMuX2Nyb3BEcmFnU3RhdGUgPSBudWxsO1xuICAgIGNvbnN0IHN3aXBlciA9IHN3aXBlckVsZW1lbnQgPyB0aGlzLmdldFN3aXBlcihzd2lwZXJFbGVtZW50KSA6IG51bGw7XG4gICAgaWYgKHN3aXBlcikge1xuICAgICAgc3dpcGVyLmFsbG93U2xpZGVOZXh0ID0gdHJ1ZTtcbiAgICAgIHN3aXBlci5hbGxvd1NsaWRlUHJldiA9IHRydWU7XG4gICAgfVxuICAgIGlmIChzd2lwZXJFbGVtZW50KSB0aGlzLl91cGRhdGVTd2lwZXJUb3VjaE1vdmUoc3dpcGVyRWxlbWVudCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgX2NvbXB1dGVDcm9wUmVjdChoYW5kbGU6IENyb3BIYW5kbGUsIHN0YXJ0OiBDcm9wUmVjdCwgZHg6IG51bWJlciwgZHk6IG51bWJlcik6IENyb3BSZWN0IHtcbiAgICBjb25zdCBNSU5fU0laRSA9IDAuMDg7XG4gICAgY29uc3QgY2xhbXAwMSA9ICh2OiBudW1iZXIpID0+IE1hdGgubWF4KDAsIE1hdGgubWluKDEsIHYpKTtcblxuICAgIGlmIChoYW5kbGUgPT09ICdtb3ZlJykge1xuICAgICAgbGV0IHggPSBjbGFtcDAxKHN0YXJ0LnggKyBkeCk7XG4gICAgICBsZXQgeSA9IGNsYW1wMDEoc3RhcnQueSArIGR5KTtcbiAgICAgIHggPSBNYXRoLm1pbih4LCAxIC0gc3RhcnQud2lkdGgpO1xuICAgICAgeSA9IE1hdGgubWluKHksIDEgLSBzdGFydC5oZWlnaHQpO1xuICAgICAgcmV0dXJuIHsgeCwgeSwgd2lkdGg6IHN0YXJ0LndpZHRoLCBoZWlnaHQ6IHN0YXJ0LmhlaWdodCB9O1xuICAgIH1cblxuICAgIGxldCBsZWZ0ID0gc3RhcnQueDtcbiAgICBsZXQgdG9wID0gc3RhcnQueTtcbiAgICBsZXQgcmlnaHQgPSBzdGFydC54ICsgc3RhcnQud2lkdGg7XG4gICAgbGV0IGJvdHRvbSA9IHN0YXJ0LnkgKyBzdGFydC5oZWlnaHQ7XG5cbiAgICBpZiAoaGFuZGxlLmluY2x1ZGVzKCd3JykpIGxlZnQgPSBjbGFtcDAxKHN0YXJ0LnggKyBkeCk7XG4gICAgaWYgKGhhbmRsZS5pbmNsdWRlcygnZScpKSByaWdodCA9IGNsYW1wMDEoc3RhcnQueCArIHN0YXJ0LndpZHRoICsgZHgpO1xuICAgIGlmIChoYW5kbGUuaW5jbHVkZXMoJ24nKSkgdG9wID0gY2xhbXAwMShzdGFydC55ICsgZHkpO1xuICAgIGlmIChoYW5kbGUuaW5jbHVkZXMoJ3MnKSkgYm90dG9tID0gY2xhbXAwMShzdGFydC55ICsgc3RhcnQuaGVpZ2h0ICsgZHkpO1xuXG4gICAgaWYgKHJpZ2h0IC0gbGVmdCA8IE1JTl9TSVpFKSB7XG4gICAgICBpZiAoaGFuZGxlLmluY2x1ZGVzKCd3JykpIGxlZnQgPSByaWdodCAtIE1JTl9TSVpFO1xuICAgICAgZWxzZSByaWdodCA9IGxlZnQgKyBNSU5fU0laRTtcbiAgICB9XG4gICAgaWYgKGJvdHRvbSAtIHRvcCA8IE1JTl9TSVpFKSB7XG4gICAgICBpZiAoaGFuZGxlLmluY2x1ZGVzKCduJykpIHRvcCA9IGJvdHRvbSAtIE1JTl9TSVpFO1xuICAgICAgZWxzZSBib3R0b20gPSB0b3AgKyBNSU5fU0laRTtcbiAgICB9XG5cbiAgICBsZWZ0ID0gY2xhbXAwMShsZWZ0KTtcbiAgICByaWdodCA9IGNsYW1wMDEocmlnaHQpO1xuICAgIHRvcCA9IGNsYW1wMDEodG9wKTtcbiAgICBib3R0b20gPSBjbGFtcDAxKGJvdHRvbSk7XG5cbiAgICByZXR1cm4geyB4OiBsZWZ0LCB5OiB0b3AsIHdpZHRoOiByaWdodCAtIGxlZnQsIGhlaWdodDogYm90dG9tIC0gdG9wIH07XG4gIH1cblxuICBwcm90ZWN0ZWQgX2dldEV2ZW50UG9pbnQoZXZlbnQ6IE1vdXNlRXZlbnQgfCBUb3VjaEV2ZW50KTogeyB4OiBudW1iZXI7IHk6IG51bWJlciB9IHtcbiAgICBpZiAoJ3RvdWNoZXMnIGluIGV2ZW50ICYmIGV2ZW50LnRvdWNoZXMubGVuZ3RoKSB7XG4gICAgICByZXR1cm4geyB4OiBldmVudC50b3VjaGVzWzBdLmNsaWVudFgsIHk6IGV2ZW50LnRvdWNoZXNbMF0uY2xpZW50WSB9O1xuICAgIH1cbiAgICBpZiAoJ2NoYW5nZWRUb3VjaGVzJyBpbiBldmVudCAmJiBldmVudC5jaGFuZ2VkVG91Y2hlcy5sZW5ndGgpIHtcbiAgICAgIHJldHVybiB7IHg6IGV2ZW50LmNoYW5nZWRUb3VjaGVzWzBdLmNsaWVudFgsIHk6IGV2ZW50LmNoYW5nZWRUb3VjaGVzWzBdLmNsaWVudFkgfTtcbiAgICB9XG4gICAgY29uc3QgbW91c2VFdmVudCA9IGV2ZW50IGFzIE1vdXNlRXZlbnQ7XG4gICAgcmV0dXJuIHsgeDogbW91c2VFdmVudC5jbGllbnRYLCB5OiBtb3VzZUV2ZW50LmNsaWVudFkgfTtcbiAgfVxuXG4gIHByb3RlY3RlZCBfdXBkYXRlQ3JvcE92ZXJsYXlQb3NpdGlvbihzd2lwZXJFbGVtZW50OiBhbnksIHJldHJpZXNMZWZ0ID0gNjApOiB7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0gfCBudWxsIHtcbiAgICBjb25zdCBzd2lwZXIgPSB0aGlzLmdldFN3aXBlcihzd2lwZXJFbGVtZW50KTtcbiAgICBjb25zdCBhY3RpdmVTbGlkZSA9IHN3aXBlcj8uc2xpZGVzPy5bc3dpcGVyLmFjdGl2ZUluZGV4XSBhcyBIVE1MRWxlbWVudDtcbiAgICBpZiAoIWFjdGl2ZVNsaWRlKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCBjb250YWluZXJFbCA9IGFjdGl2ZVNsaWRlLnF1ZXJ5U2VsZWN0b3IoJy5zd2lwZXItem9vbS1jb250YWluZXInKSBhcyBIVE1MRWxlbWVudDtcbiAgICBjb25zdCBpbWdFbCA9IGFjdGl2ZVNsaWRlLnF1ZXJ5U2VsZWN0b3IoJy5zd2lwZXItem9vbS10YXJnZXQsIC5zd2lwZXItem9vbS1jb250YWluZXIgPiBpbWcnKSBhcyBIVE1MRWxlbWVudDtcbiAgICBjb25zdCBvdmVybGF5RWwgPSBhY3RpdmVTbGlkZS5xdWVyeVNlbGVjdG9yKCcuY3JvcC1vdmVybGF5JykgYXMgSFRNTEVsZW1lbnQ7XG4gICAgaWYgKCFjb250YWluZXJFbCB8fCAhaW1nRWwgfHwgIW92ZXJsYXlFbCkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgY29udGFpbmVyUmVjdCA9IGNvbnRhaW5lckVsLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIGNvbnN0IGltZ1JlY3QgPSBpbWdFbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAvLyBUaGUgaW1hZ2UgbWF5IG5vdCBiZSBsYWlkIG91dCB5ZXQ6IGtlZXAgYW55IHByZXZpb3VzIChub24temVybykgdmFsdWUgYW5kIHJldHJ5IG9uIHRoZSBuZXh0IGZyYW1lXG4gICAgaWYgKCFpbWdSZWN0LndpZHRoIHx8ICFpbWdSZWN0LmhlaWdodCkge1xuICAgICAgaWYgKHJldHJpZXNMZWZ0ID4gMCkge1xuICAgICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gdGhpcy5fdXBkYXRlQ3JvcE92ZXJsYXlQb3NpdGlvbihzd2lwZXJFbGVtZW50LCByZXRyaWVzTGVmdCAtIDEpKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLl9jcm9wT3ZlcmxheVJlY3Q7XG4gICAgfVxuICAgIG92ZXJsYXlFbC5zdHlsZS5sZWZ0ID0gYCR7aW1nUmVjdC5sZWZ0IC0gY29udGFpbmVyUmVjdC5sZWZ0fXB4YDtcbiAgICBvdmVybGF5RWwuc3R5bGUudG9wID0gYCR7aW1nUmVjdC50b3AgLSBjb250YWluZXJSZWN0LnRvcH1weGA7XG4gICAgb3ZlcmxheUVsLnN0eWxlLndpZHRoID0gYCR7aW1nUmVjdC53aWR0aH1weGA7XG4gICAgb3ZlcmxheUVsLnN0eWxlLmhlaWdodCA9IGAke2ltZ1JlY3QuaGVpZ2h0fXB4YDtcbiAgICB0aGlzLl9jcm9wT3ZlcmxheVJlY3QgPSB7IHdpZHRoOiBpbWdSZWN0LndpZHRoLCBoZWlnaHQ6IGltZ1JlY3QuaGVpZ2h0IH07XG4gICAgcmV0dXJuIHRoaXMuX2Nyb3BPdmVybGF5UmVjdDtcbiAgfVxuXG4gIC8vIC0tLSBDYW52YXMgcmVuZGVyaW5nLCB1c2VkIHRvIGV4cG9ydCB0aGUgY3JvcHBlZC9yb3RhdGVkIGltYWdlIC0tLVxuXG4gIHByb3RlY3RlZCBhc3luYyBfcmVuZGVyRWRpdGVkSW1hZ2UoaW1hZ2U6IEltYWdlLCByb3RhdGlvbjogbnVtYmVyLCBjcm9wUmVjdDogQ3JvcFJlY3QpOiBQcm9taXNlPEhUTUxDYW52YXNFbGVtZW50PiB7XG4gICAgY29uc3Qgc3JjID0gaW1hZ2UuZGF0YVVybCB8fCBpbWFnZS51cmw7XG4gICAgY29uc3QgaW1nID0gYXdhaXQgdGhpcy5fbG9hZEltYWdlRWxlbWVudChzcmMpO1xuICAgIGNvbnN0IHN3ID0gaW1nLm5hdHVyYWxXaWR0aDtcbiAgICBjb25zdCBzaCA9IGltZy5uYXR1cmFsSGVpZ2h0O1xuICAgIGNvbnN0IHN3YXBwZWQgPSByb3RhdGlvbiAlIDE4MCAhPT0gMDtcbiAgICBjb25zdCBydyA9IHN3YXBwZWQgPyBzaCA6IHN3O1xuICAgIGNvbnN0IHJoID0gc3dhcHBlZCA/IHN3IDogc2g7XG5cbiAgICAvLyAxKSBEcmF3IHRoZSBmdWxsICh1bmNyb3BwZWQpIGltYWdlLCByb3RhdGVkXG4gICAgY29uc3Qgcm90Q2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgcm90Q2FudmFzLndpZHRoID0gcnc7XG4gICAgcm90Q2FudmFzLmhlaWdodCA9IHJoO1xuICAgIGNvbnN0IHJjdHggPSByb3RDYW52YXMuZ2V0Q29udGV4dCgnMmQnKTtcbiAgICByY3R4LnRyYW5zbGF0ZShydyAvIDIsIHJoIC8gMik7XG4gICAgcmN0eC5yb3RhdGUoKHJvdGF0aW9uICogTWF0aC5QSSkgLyAxODApO1xuICAgIHJjdHguZHJhd0ltYWdlKGltZywgLXN3IC8gMiwgLXNoIC8gMik7XG5cbiAgICAvLyAyKSBDcm9wIHRoZSByb3RhdGVkIGltYWdlXG4gICAgY29uc3QgY3JvcFggPSBNYXRoLnJvdW5kKGNyb3BSZWN0LnggKiBydyk7XG4gICAgY29uc3QgY3JvcFkgPSBNYXRoLnJvdW5kKGNyb3BSZWN0LnkgKiByaCk7XG4gICAgY29uc3QgY3JvcFcgPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKGNyb3BSZWN0LndpZHRoICogcncpKTtcbiAgICBjb25zdCBjcm9wSCA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoY3JvcFJlY3QuaGVpZ2h0ICogcmgpKTtcblxuICAgIGNvbnN0IG91dENhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgIG91dENhbnZhcy53aWR0aCA9IGNyb3BXO1xuICAgIG91dENhbnZhcy5oZWlnaHQgPSBjcm9wSDtcbiAgICBvdXRDYW52YXMuZ2V0Q29udGV4dCgnMmQnKS5kcmF3SW1hZ2Uocm90Q2FudmFzLCBjcm9wWCwgY3JvcFksIGNyb3BXLCBjcm9wSCwgMCwgMCwgY3JvcFcsIGNyb3BIKTtcblxuICAgIHJldHVybiBvdXRDYW52YXM7XG4gIH1cblxuICBwcm90ZWN0ZWQgX2xvYWRJbWFnZUVsZW1lbnQoc3JjOiBzdHJpbmcpOiBQcm9taXNlPEhUTUxJbWFnZUVsZW1lbnQ+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgaW1nID0gbmV3IEltYWdlKCk7XG4gICAgICBpZiAoIXNyYy5zdGFydHNXaXRoKCdkYXRhOicpKSBpbWcuY3Jvc3NPcmlnaW4gPSAnYW5vbnltb3VzJztcbiAgICAgIGltZy5vbmxvYWQgPSAoKSA9PiByZXNvbHZlKGltZyk7XG4gICAgICBpbWcub25lcnJvciA9ICgpID0+IHJlamVjdChuZXcgRXJyb3IoYFtpbWFnZS1nYWxsZXJ5LXNsaWRlc2hvd10gQ2Fubm90IGxvYWQgaW1hZ2U6ICR7c3JjfWApKTtcbiAgICAgIGltZy5zcmMgPSBzcmM7XG4gICAgfSk7XG4gIH1cblxuICAvLyAtLS0gTW91c2Utd2hlZWwgem9vbSAmIG1vdXNlLWRyYWcgcGFuIC0tLVxuXG4gIHByb3RlY3RlZCBvblBvaW50ZXJEb3duKGV2ZW50OiBNb3VzZUV2ZW50LCBzd2lwZXJFbGVtZW50OiBhbnkpIHtcbiAgICBpZiAoIXRoaXMubW91c2Vab29tRW5hYmxlZCB8fCB0aGlzLl96b29tU3RhdGUuc2NhbGUgPD0gMSkgcmV0dXJuO1xuICAgIC8vIERvbid0IGludGVyY2VwdCBjbGlja3Mgb24gYnV0dG9ucy9jb250cm9sc1xuICAgIGlmICgoZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50KS5jbG9zZXN0KCdpb24tZmFiLWJ1dHRvbiwgaW9uLWJ1dHRvbiwgYnV0dG9uJykpIHJldHVybjtcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIHRoaXMuX2RyYWdTdGF0ZSA9IHtcbiAgICAgIHN0YXJ0WDogZXZlbnQuY2xpZW50WCxcbiAgICAgIHN0YXJ0WTogZXZlbnQuY2xpZW50WSxcbiAgICAgIHN0YXJ0VHg6IHRoaXMuX3pvb21TdGF0ZS50eCxcbiAgICAgIHN0YXJ0VHk6IHRoaXMuX3pvb21TdGF0ZS50eSxcbiAgICB9O1xuICAgIGNvbnN0IHdyYXBFbCA9IHRoaXMuX2dldEFjdGl2ZVpvb21XcmFwRWwoc3dpcGVyRWxlbWVudCk7XG4gICAgaWYgKHdyYXBFbCkgd3JhcEVsLnN0eWxlLmN1cnNvciA9ICdncmFiYmluZyc7XG5cbiAgICAvLyBEaXNhYmxlIFN3aXBlciBzbGlkZS1jaGFuZ2Ugd2hpbGUgZHJhZ2dpbmdcbiAgICBjb25zdCBzd2lwZXIgPSB0aGlzLmdldFN3aXBlcihzd2lwZXJFbGVtZW50KTtcbiAgICBpZiAoc3dpcGVyKSB7XG4gICAgICBzd2lwZXIuYWxsb3dTbGlkZU5leHQgPSBmYWxzZTtcbiAgICAgIHN3aXBlci5hbGxvd1NsaWRlUHJldiA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBvblBvaW50ZXJNb3ZlKGV2ZW50OiBNb3VzZUV2ZW50LCBzd2lwZXJFbGVtZW50OiBhbnkpIHtcbiAgICBpZiAodGhpcy5fY3JvcERyYWdTdGF0ZSkge1xuICAgICAgdGhpcy5vbkNyb3BQb2ludGVyTW92ZShldmVudCwgc3dpcGVyRWxlbWVudCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICghdGhpcy5fZHJhZ1N0YXRlKSByZXR1cm47XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICB0aGlzLl96b29tU3RhdGUudHggPSB0aGlzLl9kcmFnU3RhdGUuc3RhcnRUeCArIGV2ZW50LmNsaWVudFggLSB0aGlzLl9kcmFnU3RhdGUuc3RhcnRYO1xuICAgIHRoaXMuX3pvb21TdGF0ZS50eSA9IHRoaXMuX2RyYWdTdGF0ZS5zdGFydFR5ICsgZXZlbnQuY2xpZW50WSAtIHRoaXMuX2RyYWdTdGF0ZS5zdGFydFk7XG4gICAgdGhpcy5fY2xhbXBUcmFuc2xhdGlvbnMoc3dpcGVyRWxlbWVudCk7XG4gICAgdGhpcy5fYXBwbHlab29tKHN3aXBlckVsZW1lbnQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIG9uUG9pbnRlclVwKGV2ZW50OiBNb3VzZUV2ZW50LCBzd2lwZXJFbGVtZW50PzogYW55KSB7XG4gICAgaWYgKHRoaXMuX2Nyb3BEcmFnU3RhdGUpIHtcbiAgICAgIHRoaXMub25Dcm9wUG9pbnRlclVwKGV2ZW50LCBzd2lwZXJFbGVtZW50KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKCF0aGlzLl9kcmFnU3RhdGUpIHJldHVybjtcbiAgICB0aGlzLl9kcmFnU3RhdGUgPSBudWxsO1xuICAgIGNvbnN0IHdyYXBFbCA9IHRoaXMuX2dldEFjdGl2ZVpvb21XcmFwRWwoc3dpcGVyRWxlbWVudCk7XG4gICAgaWYgKHdyYXBFbCkgd3JhcEVsLnN0eWxlLmN1cnNvciA9IHRoaXMuX3pvb21TdGF0ZS5zY2FsZSA+IDEgPyAnZ3JhYicgOiAnJztcbiAgICAvLyBSZS1lbmFibGUgU3dpcGVyIG5hdmlnYXRpb25cbiAgICBjb25zdCBzd2lwZXIgPSBzd2lwZXJFbGVtZW50ID8gdGhpcy5nZXRTd2lwZXIoc3dpcGVyRWxlbWVudCkgOiBudWxsO1xuICAgIGlmIChzd2lwZXIpIHtcbiAgICAgIHN3aXBlci5hbGxvd1NsaWRlTmV4dCA9IHRydWU7XG4gICAgICBzd2lwZXIuYWxsb3dTbGlkZVByZXYgPSB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBfcmVzZXRab29tKHN3aXBlckVsZW1lbnQ/OiBhbnkpIHtcbiAgICB0aGlzLl96b29tU3RhdGUgPSB7IHNjYWxlOiAxLCB0eDogMCwgdHk6IDAgfTtcbiAgICB0aGlzLl9kcmFnU3RhdGUgPSBudWxsO1xuICAgIHRoaXMuX3JvdGF0aW9uID0gMDtcbiAgICB0aGlzLl9jcm9wUmVjdCA9IHsgeDogMCwgeTogMCwgd2lkdGg6IDEsIGhlaWdodDogMSB9O1xuICAgIHRoaXMuX2Nyb3BEcmFnU3RhdGUgPSBudWxsO1xuICAgIHRoaXMuX2Nyb3BUb29sQWN0aXZlID0gZmFsc2U7XG4gICAgaWYgKHN3aXBlckVsZW1lbnQpIHtcbiAgICAgIHRoaXMuX2FwcGx5Wm9vbShzd2lwZXJFbGVtZW50KTtcbiAgICAgIHRoaXMuX3VwZGF0ZUVkaXRJbWFnZVNpemUoc3dpcGVyRWxlbWVudCk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIF9zZXR1cFdoZWVsWm9vbShzd2lwZXJFbGVtZW50OiBhbnkpIHtcbiAgICB0aGlzLl9jbGVhbnVwV2hlZWxab29tKCk7XG4gICAgaWYgKCF0aGlzLm1vdXNlWm9vbUVuYWJsZWQpIHJldHVybjtcbiAgICBjb25zdCBlbDogSFRNTEVsZW1lbnQgPSBzd2lwZXJFbGVtZW50Py5uYXRpdmVFbGVtZW50ID8/IHN3aXBlckVsZW1lbnQ7XG4gICAgaWYgKCFlbCkgcmV0dXJuO1xuXG4gICAgdGhpcy5fd2hlZWxMaXN0ZW5lciA9IChldmVudDogV2hlZWxFdmVudCkgPT4ge1xuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgIGNvbnN0IHN0ZXAgPSB0aGlzLndoZWVsWm9vbVN0ZXA7XG4gICAgICBjb25zdCBvbGRTY2FsZSA9IHRoaXMuX3pvb21TdGF0ZS5zY2FsZTtcbiAgICAgIGNvbnN0IG5ld1NjYWxlID0gTWF0aC5tYXgoMSwgTWF0aC5taW4ob2xkU2NhbGUgKyAoZXZlbnQuZGVsdGFZIDwgMCA/IHN0ZXAgOiAtc3RlcCksIHRoaXMubWF4Wm9vbVJhdGlvKSk7XG4gICAgICBpZiAobmV3U2NhbGUgPT09IG9sZFNjYWxlKSByZXR1cm47XG5cbiAgICAgIGlmIChuZXdTY2FsZSA9PT0gMSkge1xuICAgICAgICB0aGlzLl96b29tU3RhdGUgPSB7IHNjYWxlOiAxLCB0eDogMCwgdHk6IDAgfTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFpvb20gdG93YXJkcyBjdXJzb3IgcG9zaXRpb25cbiAgICAgICAgY29uc3Qgc3dpcGVyID0gdGhpcy5nZXRTd2lwZXIoZWwpO1xuICAgICAgICBjb25zdCBhY3RpdmVTbGlkZSA9IHN3aXBlcj8uc2xpZGVzPy5bc3dpcGVyLmFjdGl2ZUluZGV4XSBhcyBIVE1MRWxlbWVudDtcbiAgICAgICAgaWYgKGFjdGl2ZVNsaWRlKSB7XG4gICAgICAgICAgY29uc3QgcmVjdCA9IGFjdGl2ZVNsaWRlLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICAgIGNvbnN0IGN4ID0gZXZlbnQuY2xpZW50WCAtIHJlY3QubGVmdCAtIHJlY3Qud2lkdGggLyAyO1xuICAgICAgICAgIGNvbnN0IGN5ID0gZXZlbnQuY2xpZW50WSAtIHJlY3QudG9wIC0gcmVjdC5oZWlnaHQgLyAyO1xuICAgICAgICAgIHRoaXMuX3pvb21TdGF0ZS50eCA9IGN4IC0gKChjeCAtIHRoaXMuX3pvb21TdGF0ZS50eCkgKiBuZXdTY2FsZSkgLyBvbGRTY2FsZTtcbiAgICAgICAgICB0aGlzLl96b29tU3RhdGUudHkgPSBjeSAtICgoY3kgLSB0aGlzLl96b29tU3RhdGUudHkpICogbmV3U2NhbGUpIC8gb2xkU2NhbGU7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fem9vbVN0YXRlLnNjYWxlID0gbmV3U2NhbGU7XG4gICAgICAgIHRoaXMuX2NsYW1wVHJhbnNsYXRpb25zKGVsKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuX2FwcGx5Wm9vbShlbCk7XG4gICAgfTtcblxuICAgIGVsLmFkZEV2ZW50TGlzdGVuZXIoJ3doZWVsJywgdGhpcy5fd2hlZWxMaXN0ZW5lciwgeyBwYXNzaXZlOiBmYWxzZSB9KTtcbiAgICB0aGlzLl93aGVlbEVsID0gZWw7XG4gIH1cblxuICBwcm90ZWN0ZWQgX2NsZWFudXBXaGVlbFpvb20oKSB7XG4gICAgaWYgKHRoaXMuX3doZWVsRWwgJiYgdGhpcy5fd2hlZWxMaXN0ZW5lcikge1xuICAgICAgdGhpcy5fd2hlZWxFbC5yZW1vdmVFdmVudExpc3RlbmVyKCd3aGVlbCcsIHRoaXMuX3doZWVsTGlzdGVuZXIpO1xuICAgIH1cbiAgICB0aGlzLl93aGVlbEVsID0gbnVsbDtcbiAgICB0aGlzLl93aGVlbExpc3RlbmVyID0gbnVsbDtcbiAgfVxuXG4gIHByb3RlY3RlZCBfYXBwbHlab29tKHN3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIGNvbnN0IHN3aXBlciA9IHRoaXMuZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQpO1xuICAgIGNvbnN0IGFjdGl2ZVNsaWRlID0gc3dpcGVyPy5zbGlkZXM/Lltzd2lwZXIuYWN0aXZlSW5kZXhdIGFzIEhUTUxFbGVtZW50O1xuICAgIGlmICghYWN0aXZlU2xpZGUpIHJldHVybjtcbiAgICBjb25zdCB3cmFwRWwgPSBhY3RpdmVTbGlkZS5xdWVyeVNlbGVjdG9yKCcuc3dpcGVyLXpvb20tY29udGFpbmVyJykgYXMgSFRNTEVsZW1lbnQ7XG4gICAgY29uc3QgaW1nRWwgPSBhY3RpdmVTbGlkZS5xdWVyeVNlbGVjdG9yKCcuc3dpcGVyLXpvb20tdGFyZ2V0LCAuc3dpcGVyLXpvb20tY29udGFpbmVyID4gaW1nJykgYXMgSFRNTEVsZW1lbnQ7XG4gICAgaWYgKCF3cmFwRWwgfHwgIWltZ0VsKSByZXR1cm47XG4gICAgY29uc3QgeyBzY2FsZSwgdHgsIHR5IH0gPSB0aGlzLl96b29tU3RhdGU7XG4gICAgd3JhcEVsLnN0eWxlLnRyYW5zaXRpb25EdXJhdGlvbiA9ICcwbXMnO1xuICAgIHdyYXBFbC5zdHlsZS50cmFuc2Zvcm0gPSBgdHJhbnNsYXRlM2QoJHt0eH1weCwgJHt0eX1weCwgMClgO1xuICAgIGltZ0VsLnN0eWxlLnRyYW5zaXRpb25EdXJhdGlvbiA9ICcwbXMnO1xuICAgIGltZ0VsLnN0eWxlLnRyYW5zZm9ybSA9IGB0cmFuc2xhdGUzZCgwLCAwLCAwKSBzY2FsZSgke3NjYWxlfSkgcm90YXRlKCR7dGhpcy5fcm90YXRpb259ZGVnKWA7XG4gICAgd3JhcEVsLnN0eWxlLmN1cnNvciA9IHNjYWxlID4gMSA/ICdncmFiJyA6ICcnO1xuICB9XG5cbiAgcHJvdGVjdGVkIF9jbGFtcFRyYW5zbGF0aW9ucyhzd2lwZXJFbGVtZW50OiBhbnkpIHtcbiAgICBjb25zdCBzd2lwZXIgPSB0aGlzLmdldFN3aXBlcihzd2lwZXJFbGVtZW50KTtcbiAgICBjb25zdCBhY3RpdmVTbGlkZSA9IHN3aXBlcj8uc2xpZGVzPy5bc3dpcGVyLmFjdGl2ZUluZGV4XSBhcyBIVE1MRWxlbWVudDtcbiAgICBpZiAoIWFjdGl2ZVNsaWRlKSByZXR1cm47XG4gICAgY29uc3QgaW1nRWwgPSBhY3RpdmVTbGlkZS5xdWVyeVNlbGVjdG9yKCcuc3dpcGVyLXpvb20tdGFyZ2V0LCAuc3dpcGVyLXpvb20tY29udGFpbmVyID4gaW1nJykgYXMgSFRNTEVsZW1lbnQ7XG4gICAgaWYgKCFpbWdFbCkgcmV0dXJuO1xuICAgIGNvbnN0IGltZ1cgPSBpbWdFbC5vZmZzZXRXaWR0aCB8fCBpbWdFbC5jbGllbnRXaWR0aDtcbiAgICBjb25zdCBpbWdIID0gaW1nRWwub2Zmc2V0SGVpZ2h0IHx8IGltZ0VsLmNsaWVudEhlaWdodDtcbiAgICBpZiAoIWltZ1cgfHwgIWltZ0gpIHJldHVybjtcbiAgICBjb25zdCB7IHNjYWxlIH0gPSB0aGlzLl96b29tU3RhdGU7XG4gICAgY29uc3QgbWF4VHggPSBNYXRoLm1heCgwLCAoaW1nVyAqIHNjYWxlIC0gYWN0aXZlU2xpZGUub2Zmc2V0V2lkdGgpIC8gMik7XG4gICAgY29uc3QgbWF4VHkgPSBNYXRoLm1heCgwLCAoaW1nSCAqIHNjYWxlIC0gYWN0aXZlU2xpZGUub2Zmc2V0SGVpZ2h0KSAvIDIpO1xuICAgIHRoaXMuX3pvb21TdGF0ZS50eCA9IE1hdGgubWF4KC1tYXhUeCwgTWF0aC5taW4obWF4VHgsIHRoaXMuX3pvb21TdGF0ZS50eCkpO1xuICAgIHRoaXMuX3pvb21TdGF0ZS50eSA9IE1hdGgubWF4KC1tYXhUeSwgTWF0aC5taW4obWF4VHksIHRoaXMuX3pvb21TdGF0ZS50eSkpO1xuICB9XG5cbiAgcHJvdGVjdGVkIF9nZXRBY3RpdmVab29tV3JhcEVsKHN3aXBlckVsZW1lbnQ6IGFueSk6IEhUTUxFbGVtZW50IHwgbnVsbCB7XG4gICAgY29uc3Qgc3dpcGVyID0gdGhpcy5nZXRTd2lwZXIoc3dpcGVyRWxlbWVudCk7XG4gICAgY29uc3QgYWN0aXZlU2xpZGUgPSBzd2lwZXI/LnNsaWRlcz8uW3N3aXBlci5hY3RpdmVJbmRleF0gYXMgSFRNTEVsZW1lbnQ7XG4gICAgcmV0dXJuIGFjdGl2ZVNsaWRlPy5xdWVyeVNlbGVjdG9yKCcuc3dpcGVyLXpvb20tY29udGFpbmVyJykgPz8gbnVsbDtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXJrRm9yQ2hlY2soKSB7XG4gICAgdGhpcy5jZC5tYXJrRm9yQ2hlY2soKTtcbiAgfVxufVxuIiwiPGlvbi1jb250ZW50XG4gIGNsYXNzPVwiaW9uLW5vLXBhZGRpbmcgaW9uLW5vLW1hcmdpblwiXG4gIChtb3VzZWRvd24pPVwib25Qb2ludGVyRG93bigkZXZlbnQsIHNsaWRlc2hvd1N3aXBlcilcIlxuICAobW91c2Vtb3ZlKT1cIm9uUG9pbnRlck1vdmUoJGV2ZW50LCBzbGlkZXNob3dTd2lwZXIpXCJcbiAgKG1vdXNldXApPVwib25Qb2ludGVyVXAoJGV2ZW50LCBzbGlkZXNob3dTd2lwZXIpXCJcbiAgKG1vdXNlbGVhdmUpPVwib25Qb2ludGVyVXAoJGV2ZW50LCBzbGlkZXNob3dTd2lwZXIpXCJcbiAgKHRvdWNobW92ZSk9XCJvbkNyb3BQb2ludGVyTW92ZSgkZXZlbnQsIHNsaWRlc2hvd1N3aXBlcilcIlxuICAodG91Y2hlbmQpPVwib25Dcm9wUG9pbnRlclVwKCRldmVudCwgc2xpZGVzaG93U3dpcGVyKVwiXG4gICh0b3VjaGNhbmNlbCk9XCJvbkNyb3BQb2ludGVyVXAoJGV2ZW50LCBzbGlkZXNob3dTd2lwZXIpXCJcbj5cbiAgQGxldCBjb3VudCA9IHJvd3M/Lmxlbmd0aCB8fCAwO1xuICA8IS0tIENvdW50ZXIgIC0tPlxuICA8ZGl2IGNsYXNzPVwidG9wLWxlZnRcIj5cbiAgICA8aW9uLWxhYmVsIGNvbG9yPVwibGlnaHRcIj57eyBhY3RpdmVTbGlkZUluZGV4ICsgMSB9fSAvIHt7IGNvdW50IH19PC9pb24tbGFiZWw+XG4gIDwvZGl2PlxuXG4gIDwhLS0gem9vbSAvIHJvdGF0aW9uIC8gY3JvcCBidXR0b25zOiBhbGwgc2hvd24gdG9nZXRoZXIsIG5vIFwiZW50ZXIgZWRpdCBtb2RlXCIgc3RlcCBuZWVkZWQgLS0+XG4gIDxkaXYgY2xhc3M9XCJ0b3AtcmlnaHRcIj5cbiAgICBAaWYgKCFtb2JpbGUpIHtcbiAgICAgIDxpb24tZmFiLWJ1dHRvbiBzaXplPVwic21hbGxcIiAoY2xpY2spPVwiem9vbShzbGlkZXNob3dTd2lwZXIsIHRydWUpXCIgY29sb3I9XCJsaWdodFwiPlxuICAgICAgICA8aW9uLWljb24gbmFtZT1cInNlYXJjaFwiPjwvaW9uLWljb24+XG4gICAgICAgIDxpb24taWNvbiBjbGFzcz1cImljb24tc2Vjb25kYXJ5XCIgbmFtZT1cImFkZFwiPjwvaW9uLWljb24+XG4gICAgICA8L2lvbi1mYWItYnV0dG9uPlxuICAgICAgPGlvbi1mYWItYnV0dG9uIHNpemU9XCJzbWFsbFwiIChjbGljayk9XCJ6b29tKHNsaWRlc2hvd1N3aXBlciwgZmFsc2UpXCIgY29sb3I9XCJsaWdodFwiPlxuICAgICAgICA8aW9uLWljb24gbmFtZT1cInNlYXJjaFwiPjwvaW9uLWljb24+XG4gICAgICAgIDxpb24taWNvbiBjbGFzcz1cImljb24tc2Vjb25kYXJ5XCIgbmFtZT1cInJlbW92ZVwiPjwvaW9uLWljb24+XG4gICAgICA8L2lvbi1mYWItYnV0dG9uPlxuICAgIH1cbiAgICBAaWYgKCFyZWFkT25seSAmJiByb3RhdGlvbkVuYWJsZWQpIHtcbiAgICAgIDxpb24tZmFiLWJ1dHRvblxuICAgICAgICBzaXplPVwic21hbGxcIlxuICAgICAgICAoY2xpY2spPVwicm90YXRlKHNsaWRlc2hvd1N3aXBlciwgJ2xlZnQnKVwiXG4gICAgICAgIGNvbG9yPVwibGlnaHRcIlxuICAgICAgICBbdGl0bGVdPVwiIXNob3dUb29sdGlwID8gKCdJTUFHRS5HQUxMRVJZLkJUTl9ST1RBVEVfTEVGVCcgfCB0cmFuc2xhdGUpIDogJydcIlxuICAgICAgICBbbWF0VG9vbHRpcF09XCJzaG93VG9vbHRpcCA/ICgnSU1BR0UuR0FMTEVSWS5CVE5fUk9UQVRFX0xFRlQnIHwgdHJhbnNsYXRlKSA6ICcnXCJcbiAgICAgID5cbiAgICAgICAgPG1hdC1pY29uPnJvdGF0ZV9sZWZ0PC9tYXQtaWNvbj5cbiAgICAgIDwvaW9uLWZhYi1idXR0b24+XG4gICAgICA8aW9uLWZhYi1idXR0b25cbiAgICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgKGNsaWNrKT1cInJvdGF0ZShzbGlkZXNob3dTd2lwZXIsICdyaWdodCcpXCJcbiAgICAgICAgY29sb3I9XCJsaWdodFwiXG4gICAgICAgIFt0aXRsZV09XCIhc2hvd1Rvb2x0aXAgPyAoJ0lNQUdFLkdBTExFUlkuQlROX1JPVEFURV9SSUdIVCcgfCB0cmFuc2xhdGUpIDogJydcIlxuICAgICAgICBbbWF0VG9vbHRpcF09XCJzaG93VG9vbHRpcCA/ICgnSU1BR0UuR0FMTEVSWS5CVE5fUk9UQVRFX1JJR0hUJyB8IHRyYW5zbGF0ZSkgOiAnJ1wiXG4gICAgICA+XG4gICAgICAgIDxtYXQtaWNvbj5yb3RhdGVfcmlnaHQ8L21hdC1pY29uPlxuICAgICAgPC9pb24tZmFiLWJ1dHRvbj5cbiAgICB9XG4gICAgQGlmICghcmVhZE9ubHkgJiYgY3JvcEVuYWJsZWQpIHtcbiAgICAgIDxpb24tZmFiLWJ1dHRvblxuICAgICAgICBzaXplPVwic21hbGxcIlxuICAgICAgICAoY2xpY2spPVwidG9nZ2xlQ3JvcFRvb2woc2xpZGVzaG93U3dpcGVyKVwiXG4gICAgICAgIFtjb2xvcl09XCJfY3JvcFRvb2xBY3RpdmUgPyAndGVydGlhcnknIDogJ2xpZ2h0J1wiXG4gICAgICAgIFt0aXRsZV09XCJcbiAgICAgICAgICAhc2hvd1Rvb2x0aXBcbiAgICAgICAgICAgID8gKChfY3JvcFRvb2xBY3RpdmUgPyAnSU1BR0UuR0FMTEVSWS5CVE5fQ1JPUF9ERUFDVElWQVRFJyA6ICdJTUFHRS5HQUxMRVJZLkJUTl9DUk9QX0FDVElWQVRFJykgfCB0cmFuc2xhdGUpXG4gICAgICAgICAgICA6ICcnXG4gICAgICAgIFwiXG4gICAgICAgIFttYXRUb29sdGlwXT1cIlxuICAgICAgICAgIHNob3dUb29sdGlwXG4gICAgICAgICAgICA/ICgoX2Nyb3BUb29sQWN0aXZlID8gJ0lNQUdFLkdBTExFUlkuQlROX0NST1BfREVBQ1RJVkFURScgOiAnSU1BR0UuR0FMTEVSWS5CVE5fQ1JPUF9BQ1RJVkFURScpIHwgdHJhbnNsYXRlKVxuICAgICAgICAgICAgOiAnJ1xuICAgICAgICBcIlxuICAgICAgPlxuICAgICAgICA8bWF0LWljb24+Y3JvcDwvbWF0LWljb24+XG4gICAgICA8L2lvbi1mYWItYnV0dG9uPlxuICAgICAgQGlmIChfY3JvcFRvb2xBY3RpdmUpIHtcbiAgICAgICAgPGlvbi1mYWItYnV0dG9uXG4gICAgICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAoY2xpY2spPVwicmVzZXRDcm9wKHNsaWRlc2hvd1N3aXBlcilcIlxuICAgICAgICAgIGNvbG9yPVwibGlnaHRcIlxuICAgICAgICAgIFt0aXRsZV09XCIhc2hvd1Rvb2x0aXAgPyAoJ0lNQUdFLkdBTExFUlkuQlROX0NST1BfUkVTRVQnIHwgdHJhbnNsYXRlKSA6ICcnXCJcbiAgICAgICAgICBbbWF0VG9vbHRpcF09XCJzaG93VG9vbHRpcCA/ICgnSU1BR0UuR0FMTEVSWS5CVE5fQ1JPUF9SRVNFVCcgfCB0cmFuc2xhdGUpIDogJydcIlxuICAgICAgICA+XG4gICAgICAgICAgPG1hdC1pY29uPmNyb3BfZnJlZTwvbWF0LWljb24+XG4gICAgICAgIDwvaW9uLWZhYi1idXR0b24+XG4gICAgICB9XG4gICAgfVxuICA8L2Rpdj5cblxuICA8IS0tIG5hdmlnYXRpb24gc2lkZSBidXR0b25zIG5leHQvYmFjayAtLT5cbiAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFtb2JpbGUgJiYgKHJvd3MgfCBpc0FycmF5TGVuZ3RoOiB7IGdyZWF0ZXJUaGFuOiAxIH0pXCI+XG4gICAgPGRpdiBjbGFzcz1cInNpZGUtYnV0dG9ucyBwcmV2XCIgKGNsaWNrKT1cInNsaWRlUHJldihzbGlkZXNob3dTd2lwZXIpXCI+XG4gICAgICA8aW9uLWljb24gbmFtZT1cImNoZXZyb24tYmFja1wiPjwvaW9uLWljb24+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInNpZGUtYnV0dG9ucyBuZXh0XCIgKGNsaWNrKT1cInNsaWRlTmV4dChzbGlkZXNob3dTd2lwZXIpXCI+XG4gICAgICA8aW9uLWljb24gbmFtZT1cImNoZXZyb24tZm9yd2FyZFwiPjwvaW9uLWljb24+XG4gICAgPC9kaXY+XG4gIDwvbmctY29udGFpbmVyPlxuXG4gIDwhLS0gaW1hZ2Ugc2xpZGVzIC0tPlxuICA8c3dpcGVyLWNvbnRhaW5lclxuICAgICNzbGlkZXNob3dTd2lwZXJcbiAgICBbbW9kdWxlc109XCJzd2lwZXJNb2R1bGVzXCJcbiAgICBbem9vbV09XCIhbW91c2Vab29tRW5hYmxlZFwiXG4gICAgKHN3aXBlcnNsaWRlY2hhbmdlKT1cIm9uU2xpZGVDaGFuZ2Uoc2xpZGVzaG93U3dpcGVyKVwiXG4gICAgKHN3aXBlcmluaXQpPVwib25Td2lwZXJJbml0KHNsaWRlc2hvd1N3aXBlcilcIlxuICA+XG4gICAgQGZvciAocm93IG9mIHJvd3M7IHRyYWNrIHJvdy5pZDsgbGV0IGkgPSAkaW5kZXgpIHtcbiAgICAgIDxzd2lwZXItc2xpZGU+XG4gICAgICAgIEBsZXQgaW1hZ2UgPSByb3cgfCBwcm9wZXJ0eUdldDogJ2N1cnJlbnREYXRhJztcbiAgICAgICAgPGRpdiBjbGFzcz1cInN3aXBlci16b29tLWNvbnRhaW5lclwiPlxuICAgICAgICAgIEBpZiAoaW1hZ2UudXJsKSB7XG4gICAgICAgICAgICA8aW9uLWltZyBjbGFzcz1cInN3aXBlci16b29tLXRhcmdldFwiIFtzcmNdPVwiaW1hZ2UudXJsIHwgYXBwZW5kUXVlcnlQYXJhbXM6IGdldEltYWdlU2l6ZVF1ZXJ5UGFyYW1zKClcIiAvPlxuICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgPGltZyBbc3JjXT1cImltYWdlLmRhdGFVcmxcIiBbYWx0XT1cImltYWdlLnRpdGxlXCIgLz5cbiAgICAgICAgICB9XG5cbiAgICAgICAgICA8IS0tIGNyb3Agb3ZlcmxheTogb25seSByZW5kZXJlZCBmb3IgdGhlIGFjdGl2ZSBzbGlkZSAtLT5cbiAgICAgICAgICBAaWYgKCFyZWFkT25seSAmJiBjcm9wRW5hYmxlZCAmJiBfY3JvcFRvb2xBY3RpdmUgJiYgaSA9PT0gYWN0aXZlU2xpZGVJbmRleCkge1xuICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICBjbGFzcz1cImNyb3Atb3ZlcmxheVwiXG4gICAgICAgICAgICAgIChtb3VzZWRvd24pPVwib25Dcm9wUG9pbnRlckRvd24oJGV2ZW50LCBzbGlkZXNob3dTd2lwZXIsICdtb3ZlJylcIlxuICAgICAgICAgICAgICAodG91Y2hzdGFydCk9XCJvbkNyb3BQb2ludGVyRG93bigkZXZlbnQsIHNsaWRlc2hvd1N3aXBlciwgJ21vdmUnKVwiXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgIDwhLS0gZGFya2VuZWQgbWFzayBhcm91bmQgdGhlIHNlbGVjdGlvbjogY2xpcHBlZCB0byB0aGUgaW1hZ2UgYm91bmRzIC0tPlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiY3JvcC1tYXNrXCI+XG4gICAgICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICAgICAgY2xhc3M9XCJjcm9wLW1hc2stcmVjdFwiXG4gICAgICAgICAgICAgICAgICBbc3R5bGUubGVmdC4lXT1cIl9jcm9wUmVjdC54ICogMTAwXCJcbiAgICAgICAgICAgICAgICAgIFtzdHlsZS50b3AuJV09XCJfY3JvcFJlY3QueSAqIDEwMFwiXG4gICAgICAgICAgICAgICAgICBbc3R5bGUud2lkdGguJV09XCJfY3JvcFJlY3Qud2lkdGggKiAxMDBcIlxuICAgICAgICAgICAgICAgICAgW3N0eWxlLmhlaWdodC4lXT1cIl9jcm9wUmVjdC5oZWlnaHQgKiAxMDBcIlxuICAgICAgICAgICAgICAgID48L2Rpdj5cbiAgICAgICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICAgICAgPCEtLSBpbnRlcmFjdGl2ZSBzZWxlY3Rpb24gYm9yZGVyLCBncmlkICYgZHJhZyBoYW5kbGVzOiBuZXZlciBjbGlwcGVkLCBzbyBjb3JuZXIvZWRnZSBoYW5kbGVzXG4gICAgICAgICAgICAgICAgICAgcmVtYWluIGZ1bGx5IGdyYWJiYWJsZSBldmVuIHdoZW4gdGhlIHNlbGVjdGlvbiB0b3VjaGVzIHRoZSBpbWFnZSBib3VuZHMgLS0+XG4gICAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgICBjbGFzcz1cImNyb3AtcmVjdFwiXG4gICAgICAgICAgICAgICAgW3N0eWxlLmxlZnQuJV09XCJfY3JvcFJlY3QueCAqIDEwMFwiXG4gICAgICAgICAgICAgICAgW3N0eWxlLnRvcC4lXT1cIl9jcm9wUmVjdC55ICogMTAwXCJcbiAgICAgICAgICAgICAgICBbc3R5bGUud2lkdGguJV09XCJfY3JvcFJlY3Qud2lkdGggKiAxMDBcIlxuICAgICAgICAgICAgICAgIFtzdHlsZS5oZWlnaHQuJV09XCJfY3JvcFJlY3QuaGVpZ2h0ICogMTAwXCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjcm9wLWdyaWRcIj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGluZSB2XCIgc3R5bGU9XCJsZWZ0OiAzMy4zMzMlXCI+PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsaW5lIHZcIiBzdHlsZT1cImxlZnQ6IDY2LjY2NiVcIj48L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpbmUgaFwiIHN0eWxlPVwidG9wOiAzMy4zMzMlXCI+PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsaW5lIGhcIiBzdHlsZT1cInRvcDogNjYuNjY2JVwiPjwvc3Bhbj5cbiAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICBAZm9yIChoYW5kbGUgb2YgX2Nyb3BIYW5kbGVzOyB0cmFjayBoYW5kbGUpIHtcbiAgICAgICAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJjcm9wLWhhbmRsZSB7eyBoYW5kbGUgfX1cIlxuICAgICAgICAgICAgICAgICAgICAobW91c2Vkb3duKT1cIm9uQ3JvcFBvaW50ZXJEb3duKCRldmVudCwgc2xpZGVzaG93U3dpcGVyLCBoYW5kbGUpXCJcbiAgICAgICAgICAgICAgICAgICAgKHRvdWNoc3RhcnQpPVwib25Dcm9wUG9pbnRlckRvd24oJGV2ZW50LCBzbGlkZXNob3dTd2lwZXIsIGhhbmRsZSlcIlxuICAgICAgICAgICAgICAgICAgPjwvZGl2PlxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICB9XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9zd2lwZXItc2xpZGU+XG4gICAgfVxuICA8L3N3aXBlci1jb250YWluZXI+XG48L2lvbi1jb250ZW50PlxuXG48aW9uLWZvb3Rlcj5cbiAgPGlvbi10b29sYmFyIGNvbG9yPVwibGlnaHRcIj5cbiAgICBAaWYgKCFoYXNQZW5kaW5nRWRpdCkge1xuICAgICAgPGlvbi1yb3c+XG4gICAgICAgIDxpb24tY29sIHNpemU9XCI0XCIgY2xhc3M9XCJpb24tdGV4dC1zdGFydFwiPlxuICAgICAgICAgIDxpb24tYnV0dG9uXG4gICAgICAgICAgICAqbmdJZj1cIiFyZWFkT25seVwiXG4gICAgICAgICAgICAoY2xpY2spPVwicmVxdWVzdERlbGV0ZShzbGlkZXNob3dTd2lwZXIpXCJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCJkaXNhYmxlZFwiXG4gICAgICAgICAgICBmaWxsPVwiY2xlYXJcIlxuICAgICAgICAgICAgY29sb3I9XCJkYW5nZXJcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxtYXQtaWNvbiBzbG90PVwic3RhcnRcIj5kZWxldGU8L21hdC1pY29uPlxuICAgICAgICAgICAge3sgJ0NPTU1PTi5CVE5fREVMRVRFJyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICAgIDwvaW9uLWJ1dHRvbj5cbiAgICAgICAgPC9pb24tY29sPlxuICAgICAgICA8aW9uLWNvbCBzaXplPVwiNFwiPjwvaW9uLWNvbD5cbiAgICAgICAgPGlvbi1jb2wgc2l6ZT1cIjRcIiBjbGFzcz1cImlvbi10ZXh0LWVuZFwiPlxuICAgICAgICAgIDxpb24tYnV0dG9uIChjbGljayk9XCJjbG9zZVJlcXVlc3RlZC5lbWl0KClcIiBjb2xvcj1cInRlcnRpYXJ5XCIgZmlsbD1cImNsZWFyXCIgKm5nSWY9XCJtb2JpbGU7IGVsc2UgZGVza3RvcFwiPlxuICAgICAgICAgICAgPGlvbi1pY29uIHNsb3Q9XCJzdGFydFwiIG5hbWU9XCJjbG9zZVwiPjwvaW9uLWljb24+XG4gICAgICAgICAgICB7eyAnQ09NTU9OLkJUTl9DTE9TRScgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgICAgPG5nLXRlbXBsYXRlICNkZXNrdG9wPlxuICAgICAgICAgICAgPGlvbi1idXR0b24gKGNsaWNrKT1cImNsb3NlUmVxdWVzdGVkLmVtaXQoKVwiIGNvbG9yPVwidGVydGlhcnlcIj5cbiAgICAgICAgICAgICAge3sgJ0NPTU1PTi5CVE5fQ0xPU0UnIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgPC9pb24tY29sPlxuICAgICAgPC9pb24tcm93PlxuICAgIH0gQGVsc2Uge1xuICAgICAgPCEtLSBBIGNyb3Agb3Igcm90YXRpb24gY2hhbmdlIGlzIHBlbmRpbmc6IGFzayBmb3IgYW4gZXhwbGljaXQgdmFsaWRhdGlvbiBiZWZvcmUgZXhwb3J0aW5nIHRoZSBpbWFnZSAtLT5cbiAgICAgIDxpb24tcm93PlxuICAgICAgICA8aW9uLWNvbCBzaXplPVwiNlwiIGNsYXNzPVwiaW9uLXRleHQtc3RhcnRcIj5cbiAgICAgICAgICA8aW9uLWJ1dHRvbiAoY2xpY2spPVwiZGlzY2FyZEVkaXQoc2xpZGVzaG93U3dpcGVyKVwiIGNvbG9yPVwibWVkaXVtXCIgZmlsbD1cImNsZWFyXCI+XG4gICAgICAgICAgICA8aW9uLWljb24gc2xvdD1cInN0YXJ0XCIgbmFtZT1cImNsb3NlXCI+PC9pb24taWNvbj5cbiAgICAgICAgICAgIHt7ICdDT01NT04uQlROX0NBTkNFTCcgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICAgIDwvaW9uLWNvbD5cbiAgICAgICAgPGlvbi1jb2wgc2l6ZT1cIjZcIiBjbGFzcz1cImlvbi10ZXh0LWVuZFwiPlxuICAgICAgICAgIDxpb24tYnV0dG9uIChjbGljayk9XCJ2YWxpZGF0ZUVkaXQoc2xpZGVzaG93U3dpcGVyKVwiIGNvbG9yPVwidGVydGlhcnlcIj5cbiAgICAgICAgICAgIDxpb24taWNvbiBzbG90PVwic3RhcnRcIiBuYW1lPVwiY2hlY2ttYXJrXCI+PC9pb24taWNvbj5cbiAgICAgICAgICAgIHt7ICdDT01NT04uQlROX1ZBTElEQVRFJyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICAgIDwvaW9uLWJ1dHRvbj5cbiAgICAgICAgPC9pb24tY29sPlxuICAgICAgPC9pb24tcm93PlxuICAgIH1cbiAgPC9pb24tdG9vbGJhcj5cbjwvaW9uLWZvb3Rlcj5cbiJdfQ==
|