@sumaris-net/ngx-components 18.25.5 → 18.25.7
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 +3 -1
- 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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QUM7UUFDcEQsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFlBQVksSUFBSSxLQUFLLENBQUMsWUFBWSxDQUFDO1FBQ3RELElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJO1lBQUUsT0FBTztRQUMzQixNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztRQUNsQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksR0FBRyxLQUFLLEdBQUcsV0FBVyxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3hFLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxHQUFHLEtBQUssR0FBRyxXQUFXLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDekUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDM0UsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDN0UsQ0FBQztJQUVTLG9CQUFvQixDQUFDLGFBQWtCO1FBQy9DLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDN0MsTUFBTSxXQUFXLEdBQUcsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQWdCLENBQUM7UUFDeEUsT0FBTyxXQUFXLEVBQUUsYUFBYSxDQUFDLHdCQUF3QixDQUFDLElBQUksSUFBSSxDQUFDO0lBQ3RFLENBQUM7SUFFUyxZQUFZO1FBQ3BCLElBQUksQ0FBQyxFQUFFLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDekIsQ0FBQzt3R0E1bUJVLGlDQUFpQzs0RkFBakMsaUNBQWlDLGdJQXNCeEIsZ0JBQWdCLHNDQUNoQixnQkFBZ0Isc0NBQ2hCLGdCQUFnQiw2QkFDaEIsZ0JBQWdCLDhEQUNoQixnQkFBZ0IseUhBR2hCLGdCQUFnQiwrQ0FDaEIsZ0JBQWdCLDRmQzNFdEMsMjBRQTZNQTs7NEZEaEthLGlDQUFpQztrQkFON0MsU0FBUzsrQkFDRSw2QkFBNkIsbUJBR3RCLHVCQUF1QixDQUFDLE1BQU07bUpBc0J0QyxJQUFJO3NCQUFaLEtBQUs7Z0JBQ0csYUFBYTtzQkFBckIsS0FBSztnQkFDa0MsTUFBTTtzQkFBN0MsS0FBSzt1QkFBQyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRTtnQkFDRSxRQUFRO3NCQUEvQyxLQUFLO3VCQUFDLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFO2dCQUNFLFFBQVE7c0JBQS9DLEtBQUs7dUJBQUMsRUFBRSxTQUFTLEVBQUUsZ0JBQWdCLEVBQUU7Z0JBQ0UsS0FBSztzQkFBNUMsS0FBSzt1QkFBQyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRTtnQkFDRSxnQkFBZ0I7c0JBQXZELEtBQUs7dUJBQUMsRUFBRSxTQUFTLEVBQUUsZ0JBQWdCLEVBQUU7Z0JBQzdCLGFBQWE7c0JBQXJCLEtBQUs7Z0JBQ0csWUFBWTtzQkFBcEIsS0FBSztnQkFDa0MsZUFBZTtzQkFBdEQsS0FBSzt1QkFBQyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRTtnQkFDRSxXQUFXO3NCQUFsRCxLQUFLO3VCQUFDLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFO2dCQUM3QixpQkFBaUI7c0JBQXpCLEtBQUs7Z0JBQ0csa0JBQWtCO3NCQUExQixLQUFLO2dCQUNHLG1CQUFtQjtzQkFBM0IsS0FBSztnQkFDRyxjQUFjO3NCQUF0QixLQUFLO2dCQUdJLGVBQWU7c0JBQXhCLE1BQU07Z0JBQ0csY0FBYztzQkFBdkIsTUFBTTtnQkFDRyxjQUFjO3NCQUF2QixNQUFNO2dCQUNHLFlBQVk7c0JBQXJCLE1BQU07Z0JBRXVCLFNBQVM7c0JBQXRDLFNBQVM7dUJBQUMsaUJBQWlCO2dCQStNbEIsY0FBYztzQkFEdkIsWUFBWTt1QkFBQyxlQUFlIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgYm9vbGVhbkF0dHJpYnV0ZSxcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENoYW5nZURldGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIEVsZW1lbnRSZWYsXG4gIEV2ZW50RW1pdHRlcixcbiAgSG9zdExpc3RlbmVyLFxuICBpbmplY3QsXG4gIElucHV0LFxuICBPbkRlc3Ryb3ksXG4gIE91dHB1dCxcbiAgVmlld0NoaWxkLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IFRhYmxlRWxlbWVudCB9IGZyb20gJ0BlLWlzL25neC1tYXRlcmlhbC10YWJsZSc7XG5pbXBvcnQgeyBBbGVydENvbnRyb2xsZXIsIElvbmljU2xpZGVzIH0gZnJvbSAnQGlvbmljL2FuZ3VsYXInO1xuaW1wb3J0IHsgVHJhbnNsYXRlU2VydmljZSB9IGZyb20gJ0BuZ3gtdHJhbnNsYXRlL2NvcmUnO1xuaW1wb3J0IHsgU3dpcGVyIH0gZnJvbSAnc3dpcGVyL3R5cGVzJztcbmltcG9ydCB7IEltYWdlLCBJbWFnZVNpemUgfSBmcm9tICcuLi8uLi9pbWFnZS5tb2RlbCc7XG5pbXBvcnQgeyBpc05pbE9yQmxhbmsgfSBmcm9tICcuLi8uLi8uLi9mdW5jdGlvbnMnO1xuaW1wb3J0IHsgY3JlYXRlUHJvbWlzZUV2ZW50RW1pdHRlciwgZW1pdFByb21pc2VFdmVudCB9IGZyb20gJy4uLy4uLy4uL2V2ZW50cyc7XG5pbXBvcnQgeyBBbGVydHMgfSBmcm9tICcuLi8uLi8uLi9hbGVydHMnO1xuaW1wb3J0IHsgQVBQX1NIT1dfVE9PTFRJUCB9IGZyb20gJy4uLy4uLy4uL2NvbnN0YW50cyc7XG5pbXBvcnQge0ltYWdlRWRpdEV2ZW50LCBJbWFnZUV4cG9ydEZvcm1hdH0gZnJvbSAnLi4vaW1hZ2UtZ2FsbGVyeS5tb2RlbCc7XG5cbmludGVyZmFjZSBDcm9wUmVjdCB7XG4gIHg6IG51bWJlcjtcbiAgeTogbnVtYmVyO1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbn1cblxudHlwZSBDcm9wSGFuZGxlID0gJ21vdmUnIHwgJ253JyB8ICduJyB8ICduZScgfCAnZScgfCAnc2UnIHwgJ3MnIHwgJ3N3JyB8ICd3JztcblxuLyoqXG4gKiBUaGUgem9vbS9wYW4vcm90YXRlL2Nyb3Agc2xpZGVzaG93IHNob3duIHdoZW4gY2xpY2tpbmcgYW4gaW1hZ2UgaW4gYEFwcEltYWdlR2FsbGVyeUNvbXBvbmVudGAuXG4gKiBFeHRyYWN0ZWQgaW50byBpdHMgb3duIGNvbXBvbmVudCBzbyB0aGUgZXhhY3Qgc2FtZSBzbGlkZXNob3cgY2FuIGJlIHByZXNlbnRlZCBlaXRoZXIgaW5zaWRlIGFuXG4gKiBgaW9uLW1vZGFsYCAoZGVmYXVsdCksIG9yIGlubGluZSBhcyBhbiBvdmVybGF5IGRpcmVjdGx5IHdpdGhpbiB0aGUgZ2FsbGVyeSwgd2l0aG91dCBhbnkgZHVwbGljYXRpb24uXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2FwcC1pbWFnZS1nYWxsZXJ5LXNsaWRlc2hvdycsXG4gIHRlbXBsYXRlVXJsOiAnLi9pbWFnZS1nYWxsZXJ5LXNsaWRlc2hvdy5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2ltYWdlLWdhbGxlcnktc2xpZGVzaG93LmNvbXBvbmVudC5zY3NzJ10sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBBcHBJbWFnZUdhbGxlcnlTbGlkZXNob3dDb21wb25lbnQ8VCBleHRlbmRzIEltYWdlID0gSW1hZ2U+IGltcGxlbWVudHMgT25EZXN0cm95IHtcbiAgcHJvdGVjdGVkIGFjdGl2ZVNsaWRlSW5kZXggPSAwO1xuICBwcm90ZWN0ZWQgc3dpcGVyTW9kdWxlcyA9IFtJb25pY1NsaWRlc107XG4gIHByb3RlY3RlZCBfc3dpcGVyOiBTd2lwZXI7XG5cbiAgLy8gTW91c2Ugd2hlZWwgem9vbSAmIHJvdGF0aW9uIHN0YXRlXG4gIHByb3RlY3RlZCBfem9vbVN0YXRlID0geyBzY2FsZTogMSwgdHg6IDAsIHR5OiAwIH07XG4gIHByb3RlY3RlZCBfcm90YXRpb24gPSAwOyAvLyBkZWdyZWVzOiAwLCA5MCwgMTgwLCAyNzBcbiAgcHJvdGVjdGVkIF9kcmFnU3RhdGU6IHsgc3RhcnRYOiBudW1iZXI7IHN0YXJ0WTogbnVtYmVyOyBzdGFydFR4OiBudW1iZXI7IHN0YXJ0VHk6IG51bWJlciB9IHwgbnVsbCA9IG51bGw7XG4gIHByb3RlY3RlZCBfd2hlZWxMaXN0ZW5lcjogKChlOiBXaGVlbEV2ZW50KSA9PiB2b2lkKSB8IG51bGwgPSBudWxsO1xuICBwcm90ZWN0ZWQgX3doZWVsRWw6IEhUTUxFbGVtZW50IHwgbnVsbCA9IG51bGw7XG5cbiAgLy8gQ3JvcCAmIHJvdGF0ZSBzdGF0ZS4gUm90YXRpb24gaXMgYWx3YXlzIGF2YWlsYWJsZTsgdGhlIGNyb3AgZ3JpZC9oYW5kbGVzIG9ubHkgc2hvdyBvbmNlIHRoZVxuICAvLyBjcm9wIHRvb2wgaGFzIGJlZW4gZXhwbGljaXRseSBhY3RpdmF0ZWQgKGZhYiBidXR0b24pLiBWYWxpZGF0ZS9jYW5jZWwgc2hvdyB1cCBvbmNlIHNvbWV0aGluZyBjaGFuZ2VkLlxuICBwcm90ZWN0ZWQgX2Nyb3BUb29sQWN0aXZlID0gZmFsc2U7XG4gIHByb3RlY3RlZCBfY3JvcFJlY3Q6IENyb3BSZWN0ID0geyB4OiAwLCB5OiAwLCB3aWR0aDogMSwgaGVpZ2h0OiAxIH07XG4gIHByb3RlY3RlZCBfY3JvcE92ZXJsYXlSZWN0OiB7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcbiAgcHJvdGVjdGVkIF9jcm9wRHJhZ1N0YXRlOiB7IGhhbmRsZTogQ3JvcEhhbmRsZTsgc3RhcnRYOiBudW1iZXI7IHN0YXJ0WTogbnVtYmVyOyBzdGFydFJlY3Q6IENyb3BSZWN0IH0gfCBudWxsID0gbnVsbDtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IF9jcm9wSGFuZGxlczogQ3JvcEhhbmRsZVtdID0gWydudycsICduJywgJ25lJywgJ2UnLCAnc2UnLCAncycsICdzdycsICd3J107XG5cbiAgQElucHV0KCkgcm93czogVGFibGVFbGVtZW50PFQ+W10gfCBSZWFkb25seUFycmF5PFRhYmxlRWxlbWVudDxUPj4gPSBbXTtcbiAgQElucHV0KCkgc2VsZWN0ZWRJbmRleCA9IDA7XG4gIEBJbnB1dCh7IHRyYW5zZm9ybTogYm9vbGVhbkF0dHJpYnV0ZSB9KSBtb2JpbGUgPSBmYWxzZTtcbiAgQElucHV0KHsgdHJhbnNmb3JtOiBib29sZWFuQXR0cmlidXRlIH0pIHJlYWRPbmx5ID0gZmFsc2U7XG4gIEBJbnB1dCh7IHRyYW5zZm9ybTogYm9vbGVhbkF0dHJpYnV0ZSB9KSBkaXNhYmxlZCA9IGZhbHNlO1xuICBASW5wdXQoeyB0cmFuc2Zvcm06IGJvb2xlYW5BdHRyaWJ1dGUgfSkgZGVidWcgPSBmYWxzZTtcbiAgQElucHV0KHsgdHJhbnNmb3JtOiBib29sZWFuQXR0cmlidXRlIH0pIG1vdXNlWm9vbUVuYWJsZWQgPSBmYWxzZTtcbiAgQElucHV0KCkgd2hlZWxab29tU3RlcCA9IDAuMztcbiAgQElucHV0KCkgbWF4Wm9vbVJhdGlvID0gMztcbiAgQElucHV0KHsgdHJhbnNmb3JtOiBib29sZWFuQXR0cmlidXRlIH0pIHJvdGF0aW9uRW5hYmxlZCA9IHRydWU7XG4gIEBJbnB1dCh7IHRyYW5zZm9ybTogYm9vbGVhbkF0dHJpYnV0ZSB9KSBjcm9wRW5hYmxlZCA9IHRydWU7XG4gIEBJbnB1dCgpIGltYWdlRXhwb3J0Rm9ybWF0OiBJbWFnZUV4cG9ydEZvcm1hdCA9ICdwbmcnO1xuICBASW5wdXQoKSBpbWFnZUV4cG9ydFF1YWxpdHkgPSAwLjkyO1xuICBASW5wdXQoKSBpbWFnZVNpemVRdWVyeVBhcmFtID0gJ3NpemUnO1xuICBASW5wdXQoKSBtb2RhbEltYWdlU2l6ZTogSW1hZ2VTaXplIHwgc3RyaW5nO1xuXG4gIC8qKiBSZXF1ZXN0cyBkZWxldGlvbiBvZiB0aGUgZ2l2ZW4gcm93LiBUaGUgY2FsbGVyIChnYWxsZXJ5KSBvd25zIHRoZSBjb25maXJtYXRpb24vZGVsZXRpb24gbG9naWMgYW5kIHJlc29sdmVzIHRoZSBwcm9taXNlIHdpdGggd2hldGhlciBpdCBhY3R1YWxseSBoYXBwZW5lZC4gKi9cbiAgQE91dHB1dCgpIGRlbGV0ZVJlcXVlc3RlZCA9IGNyZWF0ZVByb21pc2VFdmVudEVtaXR0ZXI8Ym9vbGVhbiwgeyByb3c6IFRhYmxlRWxlbWVudDxUPiB9PigpO1xuICBAT3V0cHV0KCkgY2xvc2VSZXF1ZXN0ZWQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSBhZnRlckVkaXRJbWFnZSA9IG5ldyBFdmVudEVtaXR0ZXI8SW1hZ2VFZGl0RXZlbnQ8VD4+KCk7XG4gIEBPdXRwdXQoKSBhZnRlckVkaXRSb3cgPSBuZXcgRXZlbnRFbWl0dGVyPFRhYmxlRWxlbWVudDxUPj4oKTtcblxuICBAVmlld0NoaWxkKCdzbGlkZXNob3dTd2lwZXInKSBzd2lwZXJSZWY6IEVsZW1lbnRSZWY7XG5cbiAgc2hvd1Rvb2x0aXAgPSBpbmplY3QoQVBQX1NIT1dfVE9PTFRJUCwgeyBvcHRpb25hbDogdHJ1ZSB9KTtcblxuICBwcm90ZWN0ZWQgZ2V0IGhhc1BlbmRpbmdFZGl0KCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IHIgPSB0aGlzLl9jcm9wUmVjdDtcbiAgICByZXR1cm4gdGhpcy5fcm90YXRpb24gIT09IDAgfHwgci54ICE9PSAwIHx8IHIueSAhPT0gMCB8fCByLndpZHRoICE9PSAxIHx8IHIuaGVpZ2h0ICE9PSAxO1xuICB9XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJvdGVjdGVkIGFsdGVyQ3RybDogQWxlcnRDb250cm9sbGVyLFxuICAgIHByb3RlY3RlZCB0cmFuc2xhdGU6IFRyYW5zbGF0ZVNlcnZpY2UsXG4gICAgcHJvdGVjdGVkIGNkOiBDaGFuZ2VEZXRlY3RvclJlZlxuICApIHt9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgdGhpcy5fY2xlYW51cFdoZWVsWm9vbSgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGdldFN3aXBlcihzd2lwZXJFbGVtZW50PzogYW55KTogU3dpcGVyIHtcbiAgICB0aGlzLl9zd2lwZXIgPSAoc3dpcGVyRWxlbWVudCB8fCB0aGlzLnN3aXBlclJlZj8ubmF0aXZlRWxlbWVudCk/LnN3aXBlciB8fCB0aGlzLl9zd2lwZXI7XG4gICAgcmV0dXJuIHRoaXMuX3N3aXBlcjtcbiAgfVxuXG4gIHByb3RlY3RlZCBvblNsaWRlQ2hhbmdlKHN3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIGNvbnN0IHN3aXBlciA9IHRoaXMuZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQpO1xuICAgIHRoaXMuYWN0aXZlU2xpZGVJbmRleCA9IHN3aXBlci5hY3RpdmVJbmRleDtcbiAgICB0aGlzLl9yZXNldFpvb20oc3dpcGVyRWxlbWVudCk7XG4gICAgdGhpcy5fdXBkYXRlU3dpcGVyVG91Y2hNb3ZlKHN3aXBlckVsZW1lbnQpO1xuICAgIHRoaXMubWFya0ZvckNoZWNrKCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgb25Td2lwZXJJbml0KHN3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIHRoaXMuX3Jlc2V0Wm9vbSgpO1xuICAgIGlmICh0aGlzLnNlbGVjdGVkSW5kZXggPiAwKSB7XG4gICAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5kZWJ1ZyhgW2ltYWdlLWdhbGxlcnktc2xpZGVzaG93XSBJbml0aWFsaXppbmcgc2xpZGVzaG93LCBzZWxlY3RlZEluZGV4PSR7dGhpcy5zZWxlY3RlZEluZGV4fWApO1xuICAgICAgLy8gVXNlIGEgc2V0VGltZW91dCBvdGhlcndpc2UgdGhlIHN3aXBlciBjYW5ub3Qgc2xpZGVcbiAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICBjb25zdCBzd2lwZXIgPSB0aGlzLmdldFN3aXBlcihzd2lwZXJFbGVtZW50KTtcbiAgICAgICAgc3dpcGVyLnNsaWRlVG8odGhpcy5zZWxlY3RlZEluZGV4LCAwKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLl9zZXR1cFdoZWVsWm9vbShzd2lwZXJFbGVtZW50KTtcbiAgICB0aGlzLl91cGRhdGVTd2lwZXJUb3VjaE1vdmUoc3dpcGVyRWxlbWVudCk7XG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLl91cGRhdGVFZGl0SW1hZ2VTaXplKHN3aXBlckVsZW1lbnQpO1xuICAgICAgdGhpcy5fdXBkYXRlQ3JvcE92ZXJsYXlQb3NpdGlvbihzd2lwZXJFbGVtZW50KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTd2lwZXIncyBvd24gdG91Y2gvbW91c2UtZHJhZy10by1zd2lwZSBnZXN0dXJlIGlzIGNhcHR1cmVkIHZlcnkgZWFybHkgKGJlZm9yZSBvdXIgY3JvcCBvdmVybGF5J3NcbiAgICogb3duIG1vdXNlZG93biBoYW5kbGVyIGNhbiBzdG9wIGl0KSwgc28gaXQgbXVzdCBiZSBkaXNhYmxlZCB1cGZyb250IHdoZW5ldmVyIHRoZSBjcm9wIHRvb2wgaXMgYWN0aXZlXG4gICAqIC0tIG90aGVyd2lzZSBkcmFnZ2luZyBpbnNpZGUgdGhlIGNyb3Agc2VsZWN0aW9uIGVuZHMgdXAgc2xpZGluZyB0byB0aGUgbmV4dC9wcmV2aW91cyBpbWFnZSBpbnN0ZWFkLlxuICAgKiBQcmV2L25leHQgYXJyb3cgYnV0dG9ucyBrZWVwIHdvcmtpbmcsIHNpbmNlIHRoZXkgY2FsbCB0aGUgU3dpcGVyIEFQSSBkaXJlY3RseS5cbiAgICovXG4gIHByb3RlY3RlZCBfdXBkYXRlU3dpcGVyVG91Y2hNb3ZlKHN3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIGNvbnN0IHN3aXBlciA9IHRoaXMuZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQpO1xuICAgIGlmICghc3dpcGVyKSByZXR1cm47XG4gICAgc3dpcGVyLmFsbG93VG91Y2hNb3ZlID0gIXRoaXMuX2Nyb3BUb29sQWN0aXZlO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFzeW5jIHpvb20oc3dpcGVyRWxlbWVudDogYW55LCB6b29tSW46IGJvb2xlYW4pIHtcbiAgICBpZiAodGhpcy5kZWJ1ZykgY29uc29sZS5kZWJ1ZygnW2ltYWdlLWdhbGxlcnktc2xpZGVzaG93XSB6b29tICcgKyAoem9vbUluID8gJ2luJyA6ICdvdXQnKSwgc3dpcGVyRWxlbWVudCk7XG4gICAgaWYgKHRoaXMubW91c2Vab29tRW5hYmxlZCkge1xuICAgICAgY29uc3QgZGVsdGEgPSB6b29tSW4gPyB0aGlzLndoZWVsWm9vbVN0ZXAgOiAtdGhpcy53aGVlbFpvb21TdGVwO1xuICAgICAgY29uc3QgbmV3U2NhbGUgPSBNYXRoLm1heCgxLCBNYXRoLm1pbih0aGlzLl96b29tU3RhdGUuc2NhbGUgKyBkZWx0YSwgdGhpcy5tYXhab29tUmF0aW8pKTtcbiAgICAgIGlmIChuZXdTY2FsZSA9PT0gdGhpcy5fem9vbVN0YXRlLnNjYWxlKSByZXR1cm47XG4gICAgICBpZiAobmV3U2NhbGUgPT09IDEpIHtcbiAgICAgICAgdGhpcy5fem9vbVN0YXRlID0geyBzY2FsZTogMSwgdHg6IDAsIHR5OiAwIH07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl96b29tU3RhdGUuc2NhbGUgPSBuZXdTY2FsZTtcbiAgICAgICAgdGhpcy5fY2xhbXBUcmFuc2xhdGlvbnMoc3dpcGVyRWxlbWVudCk7XG4gICAgICB9XG4gICAgICB0aGlzLl9hcHBseVpvb20oc3dpcGVyRWxlbWVudCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHN3aXBlciA9IHRoaXMuZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQpO1xuICAgICAgaWYgKHpvb21Jbikgc3dpcGVyLnpvb20uaW4oKTtcbiAgICAgIGVsc2Ugc3dpcGVyLnpvb20ub3V0KCk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGFzeW5jIHJlcXVlc3REZWxldGUoc3dpcGVyRWxlbWVudDogYW55KTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3Qgc3dpcGVyID0gdGhpcy5nZXRTd2lwZXIoc3dpcGVyRWxlbWVudCk7XG4gICAgY29uc3QgY3VycmVudFJvdyA9IHRoaXMucm93cy5maW5kKChyb3csIGluZGV4KSA9PiBpbmRleCA9PT0gc3dpcGVyLmFjdGl2ZUluZGV4KTtcbiAgICBpZiAoIWN1cnJlbnRSb3cpIHJldHVybiBmYWxzZTsgLy8gUm93IG5vdCBmb3VuZFxuXG4gICAgbGV0IGRlbGV0ZWQgPSBmYWxzZTtcbiAgICB0cnkge1xuICAgICAgZGVsZXRlZCA9IGF3YWl0IGVtaXRQcm9taXNlRXZlbnQodGhpcy5kZWxldGVSZXF1ZXN0ZWQsICdkZWxldGUnLCB7IGRldGFpbDogeyByb3c6IGN1cnJlbnRSb3cgfSB9KTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGlmIChlcnIgPT09ICdDQU5DRUxMRUQnKSByZXR1cm4gZmFsc2U7IC8vIFVzZXIgY2FuY2VsXG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuXG4gICAgaWYgKGRlbGV0ZWQpIHtcbiAgICAgIGlmICghdGhpcy5yb3dzPy5sZW5ndGgpIHtcbiAgICAgICAgdGhpcy5jbG9zZVJlcXVlc3RlZC5lbWl0KCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl9yZXNldFpvb20oc3dpcGVyRWxlbWVudCk7XG4gICAgICAgIHN3aXBlci51cGRhdGUoKTtcbiAgICAgICAgdGhpcy5tYXJrRm9yQ2hlY2soKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGRlbGV0ZWQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgc2xpZGVQcmV2KHN3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIGNvbnN0IHN3aXBlciA9IHRoaXMuZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQpO1xuICAgIGNvbnN0IGFjdGl2ZUluZGV4ID0gc3dpcGVyLmFjdGl2ZUluZGV4O1xuICAgIGlmIChhY3RpdmVJbmRleCA9PT0gMCkge1xuICAgICAgLy8gTG9vcCB0byBsYXN0XG4gICAgICBhd2FpdCBzd2lwZXIuc2xpZGVUbyh0aGlzLnJvd3MubGVuZ3RoIC0gMSwgMCAvKm5vIHRyYW5zaXRpb24sIHRvIGF2b2lkIGEgXCJyZXZlcnRcIiBlZmZlY3QqLyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3YWl0IHN3aXBlci5zbGlkZVByZXYoKTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgc2xpZGVOZXh0KHN3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIGNvbnN0IHN3aXBlciA9IHRoaXMuZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQpO1xuICAgIGNvbnN0IGFjdGl2ZUluZGV4ID0gc3dpcGVyLmFjdGl2ZUluZGV4O1xuICAgIGlmIChhY3RpdmVJbmRleCA9PT0gdGhpcy5yb3dzLmxlbmd0aCAtIDEpIHtcbiAgICAgIC8vIExvb3AgdG8gZmlyc3RcbiAgICAgIGF3YWl0IHN3aXBlci5zbGlkZVRvKDAsIDAgLypubyB0cmFuc2l0aW9uLCB0byBhdm9pZCBhIFwicmV2ZXJ0XCIgZWZmZWN0Ki8pO1xuICAgIH0gZWxzZSB7XG4gICAgICBhd2FpdCBzd2lwZXIuc2xpZGVOZXh0KCk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGdldEltYWdlU2l6ZVF1ZXJ5UGFyYW1zKCk6IHsgW2tleTogc3RyaW5nXTogYW55IH0ge1xuICAgIGlmICghdGhpcy5pbWFnZVNpemVRdWVyeVBhcmFtIHx8IGlzTmlsT3JCbGFuayh0aGlzLm1vZGFsSW1hZ2VTaXplKSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICByZXR1cm4ge1xuICAgICAgW3RoaXMuaW1hZ2VTaXplUXVlcnlQYXJhbV06IHRoaXMubW9kYWxJbWFnZVNpemUsXG4gICAgfTtcbiAgfVxuXG4gIC8vIC0tLSBDcm9wICYgcm90YXRlIHRvb2xzOiBhbHdheXMgYXZhaWxhYmxlIChubyBcImVudGVyIGVkaXQgbW9kZVwiIHN0ZXApIC0tLVxuICAvLyBWYWxpZGF0ZS9DYW5jZWwgb25seSBhcHBlYXIgb25jZSB0aGUgdXNlciBhY3R1YWxseSBjaGFuZ2VkIHNvbWV0aGluZyAoc2VlIGBoYXNQZW5kaW5nRWRpdGApLlxuXG4gIHByb3RlY3RlZCBkaXNjYXJkRWRpdChzd2lwZXJFbGVtZW50OiBhbnkpIHtcbiAgICB0aGlzLl9yZXNldEVkaXRTdGF0ZShzd2lwZXJFbGVtZW50KTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNldENyb3AoX3N3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIHRoaXMuX2Nyb3BSZWN0ID0geyB4OiAwLCB5OiAwLCB3aWR0aDogMSwgaGVpZ2h0OiAxIH07XG4gICAgdGhpcy5tYXJrRm9yQ2hlY2soKTtcbiAgfVxuXG4gIHByb3RlY3RlZCB0b2dnbGVDcm9wVG9vbChzd2lwZXJFbGVtZW50OiBhbnkpIHtcbiAgICBpZiAoIXRoaXMuY3JvcEVuYWJsZWQgfHwgdGhpcy5yZWFkT25seSkgcmV0dXJuO1xuICAgIHRoaXMuX2Nyb3BUb29sQWN0aXZlID0gIXRoaXMuX2Nyb3BUb29sQWN0aXZlO1xuICAgIC8vIERpc2NhcmQgYW55IHBlbmRpbmcgY3JvcCBzZWxlY3Rpb24gd2hlbiBoaWRpbmcgdGhlIHRvb2xcbiAgICBpZiAoIXRoaXMuX2Nyb3BUb29sQWN0aXZlKSB7XG4gICAgICB0aGlzLl9jcm9wUmVjdCA9IHsgeDogMCwgeTogMCwgd2lkdGg6IDEsIGhlaWdodDogMSB9O1xuICAgICAgdGhpcy5fY3JvcERyYWdTdGF0ZSA9IG51bGw7XG4gICAgfVxuICAgIHRoaXMuX3VwZGF0ZVN3aXBlclRvdWNoTW92ZShzd2lwZXJFbGVtZW50KTtcbiAgICB0aGlzLm1hcmtGb3JDaGVjaygpO1xuICAgIGlmICh0aGlzLl9jcm9wVG9vbEFjdGl2ZSkge1xuICAgICAgLy8gV2FpdCBmb3IgdGhlIGNyb3Agb3ZlcmxheSB0byBiZSByZW5kZXJlZCwgdGhlbiBjb21wdXRlIGl0cyBwb3NpdGlvblxuICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLl91cGRhdGVDcm9wT3ZlcmxheVBvc2l0aW9uKHN3aXBlckVsZW1lbnQpKTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgdmFsaWRhdGVFZGl0KHN3aXBlckVsZW1lbnQ6IGFueSkge1xuICAgIGNvbnN0IHN3aXBlciA9IHRoaXMuZ2V0U3dpcGVyKHN3aXBlckVsZW1lbnQpO1xuICAgIGNvbnN0IGN1cnJlbnRSb3cgPSB0aGlzLnJvd3MuZmluZCgocm93LCBpbmRleCkgPT4gaW5kZXggPT09IHN3aXBlci5hY3RpdmVJbmRleCk7XG4gICAgaWYgKCFjdXJyZW50Um93KSByZXR1cm47XG5cbiAgICBjb25zdCBpbWFnZSA9IGN1cnJlbnRSb3cuY3VycmVudERhdGEgYXMgVDtcbiAgICBjb25zdCBmb3JtYXQgPSB0aGlzLmltYWdlRXhwb3J0Rm9ybWF0O1xuICAgIGNvbnN0IG1pbWVUeXBlID0gZm9ybWF0ID09PSAnanBlZycgPyAnaW1hZ2UvanBlZycgOiAnaW1hZ2UvcG5nJztcbiAgICBjb25zdCBxdWFsaXR5ID0gZm9ybWF0ID09PSAnanBlZycgPyB0aGlzLmltYWdlRXhwb3J0UXVhbGl0eSA6IHVuZGVmaW5lZDtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBjYW52YXMgPSBhd2FpdCB0aGlzLl9yZW5kZXJFZGl0ZWRJbWFnZShpbWFnZSwgdGhpcy5fcm90YXRpb24sIHRoaXMuX2Nyb3BSZWN0KTtcbiAgICAgIGNvbnN0IGRhdGFVcmwgPSBjYW52YXMudG9EYXRhVVJMKG1pbWVUeXBlLCBxdWFsaXR5KTtcbiAgICAgIGNvbnN0IGJsb2IgPSBhd2FpdCBuZXcgUHJvbWlzZTxCbG9iPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIGNhbnZhcy50b0Jsb2IoKGIpID0+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==
|