valtech-components 2.0.996 → 2.0.998
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/esm2022/lib/components/organisms/notification-preferences-view/notification-preferences-view.component.mjs +667 -0
- package/esm2022/lib/components/organisms/notification-preferences-view/notification-preferences-view.i18n.mjs +71 -0
- package/esm2022/lib/components/organisms/notification-preferences-view/notification-preferences.routes.mjs +31 -0
- package/esm2022/lib/components/organisms/notification-preferences-view/types.mjs +2 -0
- package/esm2022/lib/components/organisms/permissions-view/permissions-view.component.mjs +511 -0
- package/esm2022/lib/components/organisms/permissions-view/permissions-view.i18n.mjs +116 -0
- package/esm2022/lib/components/organisms/permissions-view/permissions.routes.mjs +29 -0
- package/esm2022/lib/components/organisms/permissions-view/types.mjs +2 -0
- package/esm2022/lib/components/organisms/settings-hub/settings.routes.mjs +12 -1
- package/esm2022/lib/services/org/permission-catalog.service.mjs +33 -0
- package/esm2022/lib/version.mjs +2 -2
- package/esm2022/public-api.mjs +8 -1
- package/fesm2022/valtech-components.mjs +1532 -111
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/organisms/notification-preferences-view/notification-preferences-view.component.d.ts +146 -0
- package/lib/components/organisms/notification-preferences-view/notification-preferences-view.i18n.d.ts +11 -0
- package/lib/components/organisms/notification-preferences-view/notification-preferences.routes.d.ts +26 -0
- package/lib/components/organisms/notification-preferences-view/types.d.ts +36 -0
- package/lib/components/organisms/permissions-view/permissions-view.component.d.ts +71 -0
- package/lib/components/organisms/permissions-view/permissions-view.i18n.d.ts +9 -0
- package/lib/components/organisms/permissions-view/permissions.routes.d.ts +24 -0
- package/lib/components/organisms/permissions-view/types.d.ts +26 -0
- package/lib/components/organisms/settings-hub/settings.routes.d.ts +15 -0
- package/lib/services/org/permission-catalog.service.d.ts +34 -0
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
- package/public-api.d.ts +7 -0
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import { Component, computed, effect, inject, Input, signal } from '@angular/core';
|
|
2
|
+
import { FormControl } from '@angular/forms';
|
|
3
|
+
import { ActivatedRoute } from '@angular/router';
|
|
4
|
+
import { IonIcon } from '@ionic/angular/standalone';
|
|
5
|
+
import { addIcons } from 'ionicons';
|
|
6
|
+
import { alertCircleOutline, chevronDownOutline, chevronUpOutline, closeCircleOutline, notificationsOffOutline, notificationsOutline, refreshOutline, } from 'ionicons/icons';
|
|
7
|
+
import { ButtonComponent } from '../../atoms/button/button.component';
|
|
8
|
+
import { DisplayComponent } from '../../atoms/display/display.component';
|
|
9
|
+
import { TextComponent } from '../../atoms/text/text.component';
|
|
10
|
+
import { TitleComponent } from '../../atoms/title/title.component';
|
|
11
|
+
import { ToggleInputComponent } from '../../molecules/toggle-input/toggle-input.component';
|
|
12
|
+
import { ComponentStates } from '../../types';
|
|
13
|
+
import { AuthService } from '../../../services/auth/auth.service';
|
|
14
|
+
import { MessagingService } from '../../../services/firebase/messaging.service';
|
|
15
|
+
import { I18nService } from '../../../services/i18n';
|
|
16
|
+
import { NavigationService } from '../../../services/navigation/navigation.service';
|
|
17
|
+
import { PageRefreshService } from '../../../services/page-refresh/page-refresh.service';
|
|
18
|
+
import { PreferencesService } from '../../../services/preferences/preferences.service';
|
|
19
|
+
import { ToastService } from '../../../services/toast.service';
|
|
20
|
+
import { NOTIFICATION_PREFERENCES_VIEW_I18N } from './notification-preferences-view.i18n';
|
|
21
|
+
import * as i0 from "@angular/core";
|
|
22
|
+
addIcons({
|
|
23
|
+
alertCircleOutline,
|
|
24
|
+
chevronDownOutline,
|
|
25
|
+
chevronUpOutline,
|
|
26
|
+
closeCircleOutline,
|
|
27
|
+
notificationsOffOutline,
|
|
28
|
+
notificationsOutline,
|
|
29
|
+
refreshOutline,
|
|
30
|
+
});
|
|
31
|
+
const DEFAULT_NAMESPACE = 'Settings.Notifications';
|
|
32
|
+
/**
|
|
33
|
+
* `val-notification-preferences-view` — **preferencias de notificaciones push**
|
|
34
|
+
* (FCM) full-feature autocontenida (organism). Promovida desde `showcase` bajo el
|
|
35
|
+
* proceso de ADR-021.
|
|
36
|
+
*
|
|
37
|
+
* Separada de `val-notifications-view` (el inbox / feed). Aquí el user **activa o
|
|
38
|
+
* desactiva** las notificaciones push en este dispositivo.
|
|
39
|
+
*
|
|
40
|
+
* UX simplificado: UN solo toggle que ejecuta toda la secuencia (pedir permiso →
|
|
41
|
+
* SW ready → getToken → registrar en backend → set master pref) vía
|
|
42
|
+
* `MessagingService.enable()`. El registro del device en el backend va como
|
|
43
|
+
* callback (la lib no depende de `AuthService` para evitar ciclos). Mensajes
|
|
44
|
+
* contextuales y botón "Reintentar" aparecen solo cuando aplican.
|
|
45
|
+
*
|
|
46
|
+
* NO renderiza ion-content — vive dentro de val-page-wrapper. `ActivatedRoute` se
|
|
47
|
+
* inyecta `{ optional: true }` → funciona sin router (embebida / Storybook).
|
|
48
|
+
*/
|
|
49
|
+
export class NotificationPreferencesViewComponent {
|
|
50
|
+
/** Namespace i18n resuelto (capturado para llamadas no-reactivas). */
|
|
51
|
+
get ns() {
|
|
52
|
+
return this.resolvedConfig().i18nNamespace;
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Lifecycle
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
constructor() {
|
|
58
|
+
this.nav = inject(NavigationService);
|
|
59
|
+
this.i18n = inject(I18nService);
|
|
60
|
+
this.toast = inject(ToastService);
|
|
61
|
+
this.auth = inject(AuthService);
|
|
62
|
+
this.messaging = inject(MessagingService);
|
|
63
|
+
this.pageRefresh = inject(PageRefreshService);
|
|
64
|
+
this.prefs = inject(PreferencesService);
|
|
65
|
+
// `ActivatedRoute` opcional → la vista funciona embebida / en Storybook sin
|
|
66
|
+
// router (solo lo usa para leer el route data de config como fallback).
|
|
67
|
+
this.route = inject(ActivatedRoute, { optional: true });
|
|
68
|
+
this.resolvedConfig = computed(() => {
|
|
69
|
+
const fromRoute = (this.route?.snapshot.data['notificationPreferencesConfig'] ??
|
|
70
|
+
{});
|
|
71
|
+
const merged = { ...fromRoute, ...(this.config ?? {}) };
|
|
72
|
+
return {
|
|
73
|
+
showDetails: merged.showDetails ?? true,
|
|
74
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
|
|
75
|
+
onPushEnabled: merged.onPushEnabled,
|
|
76
|
+
onPushDisabled: merged.onPushDisabled,
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
/** Operación en curso (gobierna spinner del toggle + label "Activando…"). */
|
|
80
|
+
this._busy = signal(null);
|
|
81
|
+
/** True si el último intento falló — gobierna estado `error` + retry button. */
|
|
82
|
+
this._lastFailed = signal(false);
|
|
83
|
+
/** True mientras se re-obtiene el token FCM en background (no afecta la UI). */
|
|
84
|
+
this._repairingToken = false;
|
|
85
|
+
/** Toggle del expand de detalles técnicos. */
|
|
86
|
+
this.showDetails = signal(false);
|
|
87
|
+
this.permission = signal('default');
|
|
88
|
+
this.isSupported = signal(false);
|
|
89
|
+
this.currentToken = signal(null);
|
|
90
|
+
this.needsIOSInstall = signal(this.detectNeedsIOSInstall());
|
|
91
|
+
/** FormControl del toggle (sync-eado con `isEnabled` via effect). */
|
|
92
|
+
this.toggleControl = new FormControl(false, {
|
|
93
|
+
nonNullable: true,
|
|
94
|
+
});
|
|
95
|
+
/** Handler del listener `focus` — referencia guardada para poder removerlo. */
|
|
96
|
+
this.onWindowFocus = () => this.scheduleBrowserRefresh();
|
|
97
|
+
/** Timer del debounce del refresh disparado por `focus`. */
|
|
98
|
+
this.focusDebounceTimer = null;
|
|
99
|
+
/** Ventana de debounce (ms) para el refresh disparado por `focus`. */
|
|
100
|
+
this.FOCUS_DEBOUNCE_MS = 500;
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Estado derivado
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
this.pageTitle = computed(() => this.tt('pageTitle'));
|
|
105
|
+
this.pageDescription = computed(() => this.tt('pageDescription'));
|
|
106
|
+
/**
|
|
107
|
+
* Estado efectivo del toggle: "el usuario tiene push activo".
|
|
108
|
+
*
|
|
109
|
+
* Se deriva SOLO de la decisión del usuario (`master`) y del permiso del SO
|
|
110
|
+
* (`permission`). El token FCM NO forma parte del estado de UI: vive en
|
|
111
|
+
* memoria, se pierde en cada recarga/suspensión de la PWA y produciría flicker.
|
|
112
|
+
* Si falta token con `master && granted`, es un detalle reparable en
|
|
113
|
+
* background (ver `repairTokenIfNeeded`), no un cambio del toggle.
|
|
114
|
+
*/
|
|
115
|
+
this.isEnabled = computed(() => this.prefs.notificationsMaster() && this.permission() === 'granted');
|
|
116
|
+
this.state = computed(() => {
|
|
117
|
+
if (this.needsIOSInstall())
|
|
118
|
+
return 'needs-install';
|
|
119
|
+
if (this.permission() === 'unsupported' || !this.isSupported()) {
|
|
120
|
+
// iOS standalone aún puede dar isSupported false temprano antes de init —
|
|
121
|
+
// solo bloqueamos si el browser explícitamente no soporta APIs.
|
|
122
|
+
if (this.permission() === 'unsupported')
|
|
123
|
+
return 'unsupported';
|
|
124
|
+
}
|
|
125
|
+
const busy = this._busy();
|
|
126
|
+
if (busy === 'enabling')
|
|
127
|
+
return 'enabling';
|
|
128
|
+
if (busy === 'disabling')
|
|
129
|
+
return 'disabling';
|
|
130
|
+
if (this.isEnabled())
|
|
131
|
+
return 'enabled';
|
|
132
|
+
if (this.permission() === 'denied')
|
|
133
|
+
return 'blocked';
|
|
134
|
+
if (this._lastFailed())
|
|
135
|
+
return 'error';
|
|
136
|
+
return 'idle';
|
|
137
|
+
});
|
|
138
|
+
this.busyLabel = computed(() => {
|
|
139
|
+
const s = this.state();
|
|
140
|
+
if (s === 'enabling')
|
|
141
|
+
return this.tt('enabling');
|
|
142
|
+
if (s === 'disabling')
|
|
143
|
+
return this.tt('disabling');
|
|
144
|
+
return null;
|
|
145
|
+
});
|
|
146
|
+
this.contextMessage = computed(() => {
|
|
147
|
+
switch (this.state()) {
|
|
148
|
+
case 'unsupported':
|
|
149
|
+
return this.tt('permissionUnsupported');
|
|
150
|
+
case 'blocked':
|
|
151
|
+
return this.tt('permissionDeniedInfo');
|
|
152
|
+
case 'error':
|
|
153
|
+
return this.tt('errorGeneric');
|
|
154
|
+
default:
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
this.alertVariant = computed(() => {
|
|
159
|
+
const s = this.state();
|
|
160
|
+
if (s === 'error' || s === 'blocked')
|
|
161
|
+
return 'warning';
|
|
162
|
+
if (s === 'unsupported')
|
|
163
|
+
return 'danger';
|
|
164
|
+
return 'info';
|
|
165
|
+
});
|
|
166
|
+
this.alertIcon = computed(() => {
|
|
167
|
+
const s = this.state();
|
|
168
|
+
if (s === 'unsupported')
|
|
169
|
+
return 'close-circle-outline';
|
|
170
|
+
return 'alert-circle-outline';
|
|
171
|
+
});
|
|
172
|
+
this.toggleProps = computed(() => ({
|
|
173
|
+
control: this.toggleControl,
|
|
174
|
+
color: 'primary',
|
|
175
|
+
disabled: this._busy() !== null ||
|
|
176
|
+
this.state() === 'unsupported' ||
|
|
177
|
+
this.state() === 'needs-install' ||
|
|
178
|
+
this.state() === 'blocked',
|
|
179
|
+
}));
|
|
180
|
+
this.retryButtonProps = computed(() => ({
|
|
181
|
+
text: this.tt('retryCta'),
|
|
182
|
+
color: 'dark',
|
|
183
|
+
fill: 'outline',
|
|
184
|
+
size: 'default',
|
|
185
|
+
shape: 'round',
|
|
186
|
+
type: 'button',
|
|
187
|
+
icon: { name: 'refresh-outline', slot: 'start' },
|
|
188
|
+
state: this._busy() !== null ? ComponentStates.WORKING : ComponentStates.ENABLED,
|
|
189
|
+
}));
|
|
190
|
+
this.tokenPreview = computed(() => {
|
|
191
|
+
const t = this.currentToken();
|
|
192
|
+
if (!t)
|
|
193
|
+
return null;
|
|
194
|
+
return `${t.slice(0, 22)}…${t.slice(-6)}`;
|
|
195
|
+
});
|
|
196
|
+
const ns = this.ns;
|
|
197
|
+
if (!this.i18n.hasNamespace(ns)) {
|
|
198
|
+
this.i18n.registerContent(ns, NOTIFICATION_PREFERENCES_VIEW_I18N);
|
|
199
|
+
}
|
|
200
|
+
this.nav.setBackHeader('pageTitle', ns, { withMenu: true });
|
|
201
|
+
void this.refreshBrowserState();
|
|
202
|
+
// Re-aplicar header en cambio de idioma.
|
|
203
|
+
effect(() => {
|
|
204
|
+
this.i18n.lang();
|
|
205
|
+
this.nav.setBackHeader('pageTitle', this.ns, { withMenu: true });
|
|
206
|
+
}, { allowSignalWrites: true });
|
|
207
|
+
// Sync FormControl ←→ estado efectivo. Cuando el estado cambia (cambio de
|
|
208
|
+
// permission por el browser, o snapshot Firestore de `master`), el toggle se
|
|
209
|
+
// re-alinea con la realidad.
|
|
210
|
+
//
|
|
211
|
+
// IMPORTANTE: se ignoran los cambios mientras hay una operación en curso
|
|
212
|
+
// (`_busy() !== null`). Durante `onEnable`/`onDisable` las señales fuente se
|
|
213
|
+
// actualizan en momentos distintos y producirían estados intermedios →
|
|
214
|
+
// flicker visible. El toggle se re-alinea UNA sola vez en el `finally`.
|
|
215
|
+
effect(() => {
|
|
216
|
+
const eff = this.isEnabled();
|
|
217
|
+
if (this._busy() !== null)
|
|
218
|
+
return;
|
|
219
|
+
if (this.toggleControl.value !== eff) {
|
|
220
|
+
this.toggleControl.setValue(eff, { emitEvent: false });
|
|
221
|
+
}
|
|
222
|
+
}, { allowSignalWrites: true });
|
|
223
|
+
// Interceptar click del user: el toggle ya cambió visualmente, ejecutar
|
|
224
|
+
// la acción correspondiente.
|
|
225
|
+
this.toggleControl.valueChanges.subscribe(v => {
|
|
226
|
+
if (this._busy() !== null)
|
|
227
|
+
return;
|
|
228
|
+
if (v && !this.isEnabled()) {
|
|
229
|
+
void this.onEnable();
|
|
230
|
+
}
|
|
231
|
+
else if (!v && this.isEnabled()) {
|
|
232
|
+
void this.onDisable();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
// Detectar regreso a la pestaña — el user pudo haber cambiado permisos
|
|
236
|
+
// desde la config del browser. El refresh va con debounce para no
|
|
237
|
+
// dispararse en ráfagas (en PWA mobile `focus` salta con cada cambio de
|
|
238
|
+
// app / aparición del teclado). El listener se remueve en ngOnDestroy.
|
|
239
|
+
if (typeof window !== 'undefined') {
|
|
240
|
+
window.addEventListener('focus', this.onWindowFocus);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Registra el pull-to-refresh estándar. Aunque esta vista no muestra una
|
|
245
|
+
* lista remota, sí refleja estado mutable del browser (permiso, token,
|
|
246
|
+
* soporte FCM) que puede cambiar fuera de la app — el refresh re-evalúa todo.
|
|
247
|
+
*
|
|
248
|
+
* Se usa `ngOnInit`/`ngOnDestroy` (NO `ionViewWillEnter`) porque
|
|
249
|
+
* `val-page-wrapper` usa `<router-outlet>` plano — los hooks `ionView*` solo
|
|
250
|
+
* disparan dentro de `<ion-router-outlet>`.
|
|
251
|
+
*/
|
|
252
|
+
ngOnInit() {
|
|
253
|
+
this.pageRefresh.register(() => this.refreshBrowserState());
|
|
254
|
+
}
|
|
255
|
+
ngOnDestroy() {
|
|
256
|
+
this.pageRefresh.unregister();
|
|
257
|
+
if (typeof window !== 'undefined') {
|
|
258
|
+
window.removeEventListener('focus', this.onWindowFocus);
|
|
259
|
+
}
|
|
260
|
+
if (this.focusDebounceTimer !== null) {
|
|
261
|
+
clearTimeout(this.focusDebounceTimer);
|
|
262
|
+
this.focusDebounceTimer = null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/** Refresca el estado del browser con debounce — evita ráfagas de `focus`. */
|
|
266
|
+
scheduleBrowserRefresh() {
|
|
267
|
+
if (this.focusDebounceTimer !== null) {
|
|
268
|
+
clearTimeout(this.focusDebounceTimer);
|
|
269
|
+
}
|
|
270
|
+
this.focusDebounceTimer = setTimeout(() => {
|
|
271
|
+
this.focusDebounceTimer = null;
|
|
272
|
+
void this.refreshBrowserState();
|
|
273
|
+
}, this.FOCUS_DEBOUNCE_MS);
|
|
274
|
+
}
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
// Actions
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
/**
|
|
279
|
+
* Secuencia completa de activación.
|
|
280
|
+
*
|
|
281
|
+
* Toda la mecánica (permission → SW ready → getToken con retry → watchdog de
|
|
282
|
+
* auto-reload) vive en `MessagingService.enable()` de la lib. El paso de
|
|
283
|
+
* registro del device en el backend se pasa como callback (`registerDevice`)
|
|
284
|
+
* porque la lib no puede depender de `AuthService` sin crear un ciclo.
|
|
285
|
+
*
|
|
286
|
+
* `enable()` no hace throw: resuelve con un `EnablePushResult` descriptivo.
|
|
287
|
+
*/
|
|
288
|
+
async onEnable() {
|
|
289
|
+
this._busy.set('enabling');
|
|
290
|
+
this._lastFailed.set(false);
|
|
291
|
+
try {
|
|
292
|
+
const result = await this.messaging.enable({
|
|
293
|
+
registerDevice: token => this.auth.registerDevice(token).then(r => r.registered),
|
|
294
|
+
});
|
|
295
|
+
if (result.status === 'enabled') {
|
|
296
|
+
// Persistir flag master si aún no estaba activo.
|
|
297
|
+
if (!this.prefs.notificationsMaster()) {
|
|
298
|
+
await this.prefs.setNotificationsMaster(true);
|
|
299
|
+
}
|
|
300
|
+
this.toast.show({
|
|
301
|
+
message: this.tt('enableSuccess'),
|
|
302
|
+
duration: 2000,
|
|
303
|
+
color: 'dark',
|
|
304
|
+
position: 'top',
|
|
305
|
+
});
|
|
306
|
+
this.resolvedConfig().onPushEnabled?.();
|
|
307
|
+
}
|
|
308
|
+
else if (result.status === 'timeout' && result.reloaded) {
|
|
309
|
+
// La lib disparó el auto-reload — la página está por recargarse.
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
// denied / unsupported / error / timeout sin reload → fallo recuperable.
|
|
314
|
+
this._lastFailed.set(true);
|
|
315
|
+
this.toast.show({
|
|
316
|
+
message: this.tt('errorGeneric'),
|
|
317
|
+
duration: 3000,
|
|
318
|
+
color: 'dark',
|
|
319
|
+
position: 'top',
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// `enable()` no hace throw, pero el callback de registro sí puede.
|
|
325
|
+
this._lastFailed.set(true);
|
|
326
|
+
this.toast.show({
|
|
327
|
+
message: this.tt('errorGeneric'),
|
|
328
|
+
duration: 3000,
|
|
329
|
+
color: 'dark',
|
|
330
|
+
position: 'top',
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
finally {
|
|
334
|
+
// Único refresh final: re-alinea el toggle UNA sola vez con el estado real.
|
|
335
|
+
await this.refreshBrowserState();
|
|
336
|
+
this._busy.set(null);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async onDisable() {
|
|
340
|
+
this._busy.set('disabling');
|
|
341
|
+
this._lastFailed.set(false);
|
|
342
|
+
try {
|
|
343
|
+
await this.auth.disableNotifications();
|
|
344
|
+
if (this.prefs.notificationsMaster()) {
|
|
345
|
+
await this.prefs.setNotificationsMaster(false);
|
|
346
|
+
}
|
|
347
|
+
this.toast.show({
|
|
348
|
+
message: this.tt('disableSuccess'),
|
|
349
|
+
duration: 2000,
|
|
350
|
+
color: 'dark',
|
|
351
|
+
position: 'top',
|
|
352
|
+
});
|
|
353
|
+
this.resolvedConfig().onPushDisabled?.();
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
this._lastFailed.set(true);
|
|
357
|
+
}
|
|
358
|
+
finally {
|
|
359
|
+
await this.refreshBrowserState();
|
|
360
|
+
this._busy.set(null);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/** Reintentar la secuencia completa de activación, sin importar el estado actual. */
|
|
364
|
+
async onRetry() {
|
|
365
|
+
this._lastFailed.set(false);
|
|
366
|
+
await this.onEnable();
|
|
367
|
+
}
|
|
368
|
+
toggleDetails() {
|
|
369
|
+
this.showDetails.update(v => !v);
|
|
370
|
+
}
|
|
371
|
+
// ---------------------------------------------------------------------------
|
|
372
|
+
// Privados
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
async refreshBrowserState() {
|
|
375
|
+
this.isSupported.set(await this.messaging.isSupported());
|
|
376
|
+
this.permission.set(this.auth.getNotificationPermissionState());
|
|
377
|
+
this.currentToken.set(this.messaging.currentToken ?? null);
|
|
378
|
+
void this.repairTokenIfNeeded();
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Repara el token FCM en background.
|
|
382
|
+
*
|
|
383
|
+
* Si el usuario tiene push activo (`master && granted`) pero no hay token en
|
|
384
|
+
* memoria — caso típico tras un cold start de la PWA — re-obtenemos el token
|
|
385
|
+
* y re-registramos el device sin tocar el toggle ni mostrar un error
|
|
386
|
+
* alarmista. El token es un detalle de implementación reparable, no parte del
|
|
387
|
+
* estado de UI (ver `isEnabled`).
|
|
388
|
+
*/
|
|
389
|
+
async repairTokenIfNeeded() {
|
|
390
|
+
if (this._busy() !== null)
|
|
391
|
+
return;
|
|
392
|
+
if (this._repairingToken)
|
|
393
|
+
return;
|
|
394
|
+
if (!this.isEnabled())
|
|
395
|
+
return;
|
|
396
|
+
if (this.currentToken())
|
|
397
|
+
return;
|
|
398
|
+
this._repairingToken = true;
|
|
399
|
+
try {
|
|
400
|
+
const token = await this.messaging.getToken();
|
|
401
|
+
if (token) {
|
|
402
|
+
this.currentToken.set(token);
|
|
403
|
+
await this.auth.registerDevice(token);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
// Reparación best-effort: no degradamos el toggle ni mostramos error.
|
|
408
|
+
}
|
|
409
|
+
finally {
|
|
410
|
+
this._repairingToken = false;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* iOS browser sin estar instalado como PWA — push web no disponible.
|
|
415
|
+
* Aplica a Safari y a Edge/Chrome/Firefox en iOS (todos WebKit por mandato Apple).
|
|
416
|
+
*/
|
|
417
|
+
detectNeedsIOSInstall() {
|
|
418
|
+
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
const ua = navigator.userAgent || '';
|
|
422
|
+
const isIOS = /iPad|iPhone|iPod/.test(ua);
|
|
423
|
+
if (!isIOS)
|
|
424
|
+
return false;
|
|
425
|
+
const isStandalone = window.matchMedia?.('(display-mode: standalone)').matches ||
|
|
426
|
+
navigator.standalone === true;
|
|
427
|
+
return !isStandalone;
|
|
428
|
+
}
|
|
429
|
+
tt(key) {
|
|
430
|
+
return this.i18n.t(key, this.ns);
|
|
431
|
+
}
|
|
432
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NotificationPreferencesViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
433
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: NotificationPreferencesViewComponent, isStandalone: true, selector: "val-notification-preferences-view", inputs: { config: "config" }, ngImport: i0, template: `
|
|
434
|
+
<div class="page">
|
|
435
|
+
<header class="page-header">
|
|
436
|
+
<val-display [props]="{ size: 'small', color: 'dark', content: pageTitle() }" />
|
|
437
|
+
<val-title
|
|
438
|
+
[props]="{
|
|
439
|
+
size: 'large',
|
|
440
|
+
color: 'dark',
|
|
441
|
+
bold: false,
|
|
442
|
+
content: pageDescription(),
|
|
443
|
+
}"
|
|
444
|
+
/>
|
|
445
|
+
</header>
|
|
446
|
+
|
|
447
|
+
@if (state() === 'needs-install') {
|
|
448
|
+
<section class="settings-section">
|
|
449
|
+
<val-title
|
|
450
|
+
[props]="{
|
|
451
|
+
size: 'medium',
|
|
452
|
+
color: 'dark',
|
|
453
|
+
bold: true,
|
|
454
|
+
content: tt('iosInstallTitle'),
|
|
455
|
+
}"
|
|
456
|
+
/>
|
|
457
|
+
<div class="section-body">
|
|
458
|
+
<val-text
|
|
459
|
+
[props]="{
|
|
460
|
+
size: 'small',
|
|
461
|
+
color: 'medium',
|
|
462
|
+
bold: false,
|
|
463
|
+
content: tt('iosInstallHint'),
|
|
464
|
+
}"
|
|
465
|
+
/>
|
|
466
|
+
<ol class="install-steps">
|
|
467
|
+
<li>{{ tt('iosInstallStep1') }}</li>
|
|
468
|
+
<li>{{ tt('iosInstallStep2') }}</li>
|
|
469
|
+
<li>{{ tt('iosInstallStep3') }}</li>
|
|
470
|
+
<li>{{ tt('iosInstallStep4') }}</li>
|
|
471
|
+
</ol>
|
|
472
|
+
</div>
|
|
473
|
+
</section>
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
<section class="settings-section">
|
|
477
|
+
<div class="notif-row">
|
|
478
|
+
<div class="notif-row__icon">
|
|
479
|
+
<ion-icon name="notifications-outline" aria-hidden="true" />
|
|
480
|
+
</div>
|
|
481
|
+
<div class="notif-row__body">
|
|
482
|
+
<val-title
|
|
483
|
+
[props]="{
|
|
484
|
+
size: 'medium',
|
|
485
|
+
color: 'dark',
|
|
486
|
+
bold: true,
|
|
487
|
+
content: tt('simpleTitle'),
|
|
488
|
+
}"
|
|
489
|
+
/>
|
|
490
|
+
<val-text
|
|
491
|
+
[props]="{
|
|
492
|
+
size: 'small',
|
|
493
|
+
color: 'medium',
|
|
494
|
+
bold: false,
|
|
495
|
+
content: tt('simpleDescription'),
|
|
496
|
+
}"
|
|
497
|
+
/>
|
|
498
|
+
@if (busyLabel(); as label) {
|
|
499
|
+
<span class="notif-row__busy">{{ label }}</span>
|
|
500
|
+
}
|
|
501
|
+
</div>
|
|
502
|
+
<div class="notif-row__control">
|
|
503
|
+
<val-toggle-input [props]="toggleProps()" />
|
|
504
|
+
</div>
|
|
505
|
+
</div>
|
|
506
|
+
|
|
507
|
+
@if (contextMessage(); as msg) {
|
|
508
|
+
<div class="alert" [class]="'alert--' + alertVariant()">
|
|
509
|
+
<ion-icon [name]="alertIcon()" aria-hidden="true" />
|
|
510
|
+
<span>{{ msg }}</span>
|
|
511
|
+
</div>
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
@if (state() === 'error') {
|
|
515
|
+
<div class="row-actions">
|
|
516
|
+
<val-button [props]="retryButtonProps()" (click)="onRetry()" />
|
|
517
|
+
</div>
|
|
518
|
+
}
|
|
519
|
+
</section>
|
|
520
|
+
|
|
521
|
+
@if (resolvedConfig().showDetails && state() !== 'needs-install' && state() !== 'unsupported') {
|
|
522
|
+
<section class="settings-section">
|
|
523
|
+
<button class="details-toggle" type="button" (click)="toggleDetails()">
|
|
524
|
+
<span>{{ showDetails() ? tt('detailsHide') : tt('detailsShow') }}</span>
|
|
525
|
+
<ion-icon [name]="showDetails() ? 'chevron-up-outline' : 'chevron-down-outline'" aria-hidden="true" />
|
|
526
|
+
</button>
|
|
527
|
+
@if (showDetails()) {
|
|
528
|
+
<dl class="details">
|
|
529
|
+
<div class="details__row">
|
|
530
|
+
<dt>{{ tt('detailsToken') }}</dt>
|
|
531
|
+
<dd>{{ tokenPreview() ?? tt('detailsNone') }}</dd>
|
|
532
|
+
</div>
|
|
533
|
+
<div class="details__row">
|
|
534
|
+
<dt>{{ tt('diagnosticsPermission') }}</dt>
|
|
535
|
+
<dd>{{ permission() }}</dd>
|
|
536
|
+
</div>
|
|
537
|
+
<div class="details__row">
|
|
538
|
+
<dt>{{ tt('diagnosticsSupported') }}</dt>
|
|
539
|
+
<dd>{{ isSupported() ? '✓' : '✗' }}</dd>
|
|
540
|
+
</div>
|
|
541
|
+
</dl>
|
|
542
|
+
}
|
|
543
|
+
</section>
|
|
544
|
+
}
|
|
545
|
+
</div>
|
|
546
|
+
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:8px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-body{margin-top:16px;display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.notif-row{display:grid;grid-template-columns:44px 1fr auto;gap:16px;align-items:center}.notif-row__icon{width:44px;height:44px;border-radius:50%;background:var(--ion-color-light, rgba(0, 0, 0, .06));display:flex;align-items:center;justify-content:center}.notif-row__icon ion-icon{font-size:22px;color:var(--ion-color-dark, #1a1a1a)}.notif-row__body{display:flex;flex-direction:column;gap:4px;min-width:0}.notif-row__busy{font-size:12px;color:var(--ion-color-medium, #92949c);font-style:italic;margin-top:4px}.notif-row__control{flex-shrink:0}.alert{display:flex;gap:10px;align-items:flex-start;margin-top:12px;padding:12px 14px;border-radius:10px;font-size:13px;line-height:1.45;background:var(--ion-color-light, rgba(0, 0, 0, .04));color:var(--ion-color-dark, #1a1a1a)}.alert ion-icon{font-size:18px;flex-shrink:0;margin-top:1px}.alert--warning ion-icon{color:var(--ion-color-warning-shade, #c79e08)}.alert--danger ion-icon{color:var(--ion-color-danger-shade, #cf3c4f)}.alert--info ion-icon{color:var(--ion-color-medium, #6b6b6b)}.install-steps{margin:0;padding-left:20px;font-size:13px;line-height:1.6;color:var(--ion-color-dark, #1a1a1a)}.install-steps li+li{margin-top:4px}.details-toggle{display:flex;align-items:center;gap:6px;background:none;border:0;padding:0;font-size:13px;color:var(--ion-color-medium, #6b6b6b);cursor:pointer}.details-toggle ion-icon{font-size:14px}.details{margin:12px 0 0;display:flex;flex-direction:column;gap:6px}.details__row{display:grid;grid-template-columns:140px 1fr;gap:12px;font-size:12px;align-items:baseline}.details__row dt{margin:0;color:var(--ion-color-medium, #6b6b6b);text-transform:uppercase;letter-spacing:.4px;font-weight:500}.details__row dd{margin:0;color:var(--ion-color-dark, #1a1a1a);font-family:ui-monospace,SFMono-Regular,monospace;font-size:11px;word-break:break-all}\n"], dependencies: [{ kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: ToggleInputComponent, selector: "val-toggle-input", inputs: ["preset", "props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }] }); }
|
|
547
|
+
}
|
|
548
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NotificationPreferencesViewComponent, decorators: [{
|
|
549
|
+
type: Component,
|
|
550
|
+
args: [{ selector: 'val-notification-preferences-view', standalone: true, imports: [IonIcon, ToggleInputComponent, ButtonComponent, DisplayComponent, TitleComponent, TextComponent], template: `
|
|
551
|
+
<div class="page">
|
|
552
|
+
<header class="page-header">
|
|
553
|
+
<val-display [props]="{ size: 'small', color: 'dark', content: pageTitle() }" />
|
|
554
|
+
<val-title
|
|
555
|
+
[props]="{
|
|
556
|
+
size: 'large',
|
|
557
|
+
color: 'dark',
|
|
558
|
+
bold: false,
|
|
559
|
+
content: pageDescription(),
|
|
560
|
+
}"
|
|
561
|
+
/>
|
|
562
|
+
</header>
|
|
563
|
+
|
|
564
|
+
@if (state() === 'needs-install') {
|
|
565
|
+
<section class="settings-section">
|
|
566
|
+
<val-title
|
|
567
|
+
[props]="{
|
|
568
|
+
size: 'medium',
|
|
569
|
+
color: 'dark',
|
|
570
|
+
bold: true,
|
|
571
|
+
content: tt('iosInstallTitle'),
|
|
572
|
+
}"
|
|
573
|
+
/>
|
|
574
|
+
<div class="section-body">
|
|
575
|
+
<val-text
|
|
576
|
+
[props]="{
|
|
577
|
+
size: 'small',
|
|
578
|
+
color: 'medium',
|
|
579
|
+
bold: false,
|
|
580
|
+
content: tt('iosInstallHint'),
|
|
581
|
+
}"
|
|
582
|
+
/>
|
|
583
|
+
<ol class="install-steps">
|
|
584
|
+
<li>{{ tt('iosInstallStep1') }}</li>
|
|
585
|
+
<li>{{ tt('iosInstallStep2') }}</li>
|
|
586
|
+
<li>{{ tt('iosInstallStep3') }}</li>
|
|
587
|
+
<li>{{ tt('iosInstallStep4') }}</li>
|
|
588
|
+
</ol>
|
|
589
|
+
</div>
|
|
590
|
+
</section>
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
<section class="settings-section">
|
|
594
|
+
<div class="notif-row">
|
|
595
|
+
<div class="notif-row__icon">
|
|
596
|
+
<ion-icon name="notifications-outline" aria-hidden="true" />
|
|
597
|
+
</div>
|
|
598
|
+
<div class="notif-row__body">
|
|
599
|
+
<val-title
|
|
600
|
+
[props]="{
|
|
601
|
+
size: 'medium',
|
|
602
|
+
color: 'dark',
|
|
603
|
+
bold: true,
|
|
604
|
+
content: tt('simpleTitle'),
|
|
605
|
+
}"
|
|
606
|
+
/>
|
|
607
|
+
<val-text
|
|
608
|
+
[props]="{
|
|
609
|
+
size: 'small',
|
|
610
|
+
color: 'medium',
|
|
611
|
+
bold: false,
|
|
612
|
+
content: tt('simpleDescription'),
|
|
613
|
+
}"
|
|
614
|
+
/>
|
|
615
|
+
@if (busyLabel(); as label) {
|
|
616
|
+
<span class="notif-row__busy">{{ label }}</span>
|
|
617
|
+
}
|
|
618
|
+
</div>
|
|
619
|
+
<div class="notif-row__control">
|
|
620
|
+
<val-toggle-input [props]="toggleProps()" />
|
|
621
|
+
</div>
|
|
622
|
+
</div>
|
|
623
|
+
|
|
624
|
+
@if (contextMessage(); as msg) {
|
|
625
|
+
<div class="alert" [class]="'alert--' + alertVariant()">
|
|
626
|
+
<ion-icon [name]="alertIcon()" aria-hidden="true" />
|
|
627
|
+
<span>{{ msg }}</span>
|
|
628
|
+
</div>
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
@if (state() === 'error') {
|
|
632
|
+
<div class="row-actions">
|
|
633
|
+
<val-button [props]="retryButtonProps()" (click)="onRetry()" />
|
|
634
|
+
</div>
|
|
635
|
+
}
|
|
636
|
+
</section>
|
|
637
|
+
|
|
638
|
+
@if (resolvedConfig().showDetails && state() !== 'needs-install' && state() !== 'unsupported') {
|
|
639
|
+
<section class="settings-section">
|
|
640
|
+
<button class="details-toggle" type="button" (click)="toggleDetails()">
|
|
641
|
+
<span>{{ showDetails() ? tt('detailsHide') : tt('detailsShow') }}</span>
|
|
642
|
+
<ion-icon [name]="showDetails() ? 'chevron-up-outline' : 'chevron-down-outline'" aria-hidden="true" />
|
|
643
|
+
</button>
|
|
644
|
+
@if (showDetails()) {
|
|
645
|
+
<dl class="details">
|
|
646
|
+
<div class="details__row">
|
|
647
|
+
<dt>{{ tt('detailsToken') }}</dt>
|
|
648
|
+
<dd>{{ tokenPreview() ?? tt('detailsNone') }}</dd>
|
|
649
|
+
</div>
|
|
650
|
+
<div class="details__row">
|
|
651
|
+
<dt>{{ tt('diagnosticsPermission') }}</dt>
|
|
652
|
+
<dd>{{ permission() }}</dd>
|
|
653
|
+
</div>
|
|
654
|
+
<div class="details__row">
|
|
655
|
+
<dt>{{ tt('diagnosticsSupported') }}</dt>
|
|
656
|
+
<dd>{{ isSupported() ? '✓' : '✗' }}</dd>
|
|
657
|
+
</div>
|
|
658
|
+
</dl>
|
|
659
|
+
}
|
|
660
|
+
</section>
|
|
661
|
+
}
|
|
662
|
+
</div>
|
|
663
|
+
`, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:8px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-body{margin-top:16px;display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.notif-row{display:grid;grid-template-columns:44px 1fr auto;gap:16px;align-items:center}.notif-row__icon{width:44px;height:44px;border-radius:50%;background:var(--ion-color-light, rgba(0, 0, 0, .06));display:flex;align-items:center;justify-content:center}.notif-row__icon ion-icon{font-size:22px;color:var(--ion-color-dark, #1a1a1a)}.notif-row__body{display:flex;flex-direction:column;gap:4px;min-width:0}.notif-row__busy{font-size:12px;color:var(--ion-color-medium, #92949c);font-style:italic;margin-top:4px}.notif-row__control{flex-shrink:0}.alert{display:flex;gap:10px;align-items:flex-start;margin-top:12px;padding:12px 14px;border-radius:10px;font-size:13px;line-height:1.45;background:var(--ion-color-light, rgba(0, 0, 0, .04));color:var(--ion-color-dark, #1a1a1a)}.alert ion-icon{font-size:18px;flex-shrink:0;margin-top:1px}.alert--warning ion-icon{color:var(--ion-color-warning-shade, #c79e08)}.alert--danger ion-icon{color:var(--ion-color-danger-shade, #cf3c4f)}.alert--info ion-icon{color:var(--ion-color-medium, #6b6b6b)}.install-steps{margin:0;padding-left:20px;font-size:13px;line-height:1.6;color:var(--ion-color-dark, #1a1a1a)}.install-steps li+li{margin-top:4px}.details-toggle{display:flex;align-items:center;gap:6px;background:none;border:0;padding:0;font-size:13px;color:var(--ion-color-medium, #6b6b6b);cursor:pointer}.details-toggle ion-icon{font-size:14px}.details{margin:12px 0 0;display:flex;flex-direction:column;gap:6px}.details__row{display:grid;grid-template-columns:140px 1fr;gap:12px;font-size:12px;align-items:baseline}.details__row dt{margin:0;color:var(--ion-color-medium, #6b6b6b);text-transform:uppercase;letter-spacing:.4px;font-weight:500}.details__row dd{margin:0;color:var(--ion-color-dark, #1a1a1a);font-family:ui-monospace,SFMono-Regular,monospace;font-size:11px;word-break:break-all}\n"] }]
|
|
664
|
+
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
665
|
+
type: Input
|
|
666
|
+
}] } });
|
|
667
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm90aWZpY2F0aW9uLXByZWZlcmVuY2VzLXZpZXcuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL29yZ2FuaXNtcy9ub3RpZmljYXRpb24tcHJlZmVyZW5jZXMtdmlldy9ub3RpZmljYXRpb24tcHJlZmVyZW5jZXMtdmlldy5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQXFCLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN0RyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0MsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ2pELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQ3BDLE9BQU8sRUFDTCxrQkFBa0IsRUFDbEIsa0JBQWtCLEVBQ2xCLGdCQUFnQixFQUNoQixrQkFBa0IsRUFDbEIsdUJBQXVCLEVBQ3ZCLG9CQUFvQixFQUNwQixjQUFjLEdBQ2YsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDdEUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFDekUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ2hFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNuRSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxxREFBcUQsQ0FBQztBQUUzRixPQUFPLEVBQWtCLGVBQWUsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUU5RCxPQUFPLEVBQUUs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QUsiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIGNvbXB1dGVkLCBlZmZlY3QsIGluamVjdCwgSW5wdXQsIE9uRGVzdHJveSwgT25Jbml0LCBzaWduYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEZvcm1Db250cm9sIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgQWN0aXZhdGVkUm91dGUgfSBmcm9tICdAYW5ndWxhci9yb3V0ZXInO1xuaW1wb3J0IHsgSW9uSWNvbiB9IGZyb20gJ0Bpb25pYy9hbmd1bGFyL3N0YW5kYWxvbmUnO1xuaW1wb3J0IHsgYWRkSWNvbnMgfSBmcm9tICdpb25pY29ucyc7XG5pbXBvcnQge1xuICBhbGVydENpcmNsZU91dGxpbmUsXG4gIGNoZXZyb25Eb3duT3V0bGluZSxcbiAgY2hldnJvblVwT3V0bGluZSxcbiAgY2xvc2VDaXJjbGVPdXRsaW5lLFxuICBub3RpZmljYXRpb25zT2ZmT3V0bGluZSxcbiAgbm90aWZpY2F0aW9uc091dGxpbmUsXG4gIHJlZnJlc2hPdXRsaW5lLFxufSBmcm9tICdpb25pY29ucy9pY29ucyc7XG5cbmltcG9ydCB7IEJ1dHRvbkNvbXBvbmVudCB9IGZyb20gJy4uLy4uL2F0b21zL2J1dHRvbi9idXR0b24uY29tcG9uZW50JztcbmltcG9ydCB7IERpc3BsYXlDb21wb25lbnQgfSBmcm9tICcuLi8uLi9hdG9tcy9kaXNwbGF5L2Rpc3BsYXkuY29tcG9uZW50JztcbmltcG9ydCB7IFRleHRDb21wb25lbnQgfSBmcm9tICcuLi8uLi9hdG9tcy90ZXh0L3RleHQuY29tcG9uZW50JztcbmltcG9ydCB7IFRpdGxlQ29tcG9uZW50IH0gZnJvbSAnLi4vLi4vYXRvbXMvdGl0bGUvdGl0bGUuY29tcG9uZW50JztcbmltcG9ydCB7IFRvZ2dsZUlucHV0Q29tcG9uZW50IH0gZnJvbSAnLi4vLi4vbW9sZWN1bGVzL3RvZ2dsZS1pbnB1dC90b2dnbGUtaW5wdXQuY29tcG9uZW50JztcbmltcG9ydCB7IFRvZ2dsZUlucHV0TWV0YWRhdGEgfSBmcm9tICcuLi8uLi9tb2xlY3VsZXMvdG9nZ2xlLWlucHV0L3R5cGVzJztcbmltcG9ydCB7IEJ1dHRvbk1ldGFkYXRhLCBDb21wb25lbnRTdGF0ZXMgfSBmcm9tICcuLi8uLi90eXBlcyc7XG5cbmltcG9ydCB7IEF1dGhTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vLi4vc2VydmljZXMvYXV0aC9hdXRoLnNlcnZpY2UnO1xuaW1wb3J0IHsgTm90aWZpY2F0aW9uUGVybWlzc2lvblN0YXRlIH0gZnJvbSAnLi4vLi4vLi4vc2VydmljZXMvYXV0aC90eXBlcyc7XG5pbXBvcnQgeyBFbmFibGVQdXNoUmVzdWx0IH0gZnJvbSAnLi4vLi4vLi4vc2VydmljZXMvZmlyZWJhc2UvdHlwZXMnO1xuaW1wb3J0IHsgTWVzc2FnaW5nU2VydmljZSB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL2ZpcmViYXNlL21lc3NhZ2luZy5zZXJ2aWNlJztcbmltcG9ydCB7IEkxOG5TZXJ2aWNlIH0gZnJvbSAnLi4vLi4vLi4vc2VydmljZXMvaTE4bic7XG5pbXBvcnQgeyBOYXZpZ2F0aW9uU2VydmljZSB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL25hdmlnYXRpb24vbmF2aWdhdGlvbi5zZXJ2aWNlJztcbmltcG9ydCB7IFBhZ2VSZWZyZXNoU2VydmljZSB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL3BhZ2UtcmVmcmVzaC9wYWdlLXJlZnJlc2guc2VydmljZSc7XG5pbXBvcnQgeyBQcmVmZXJlbmNlc1NlcnZpY2UgfSBmcm9tICcuLi8uLi8uLi9zZXJ2aWNlcy9wcmVmZXJlbmNlcy9wcmVmZXJlbmNlcy5zZXJ2aWNlJztcbmltcG9ydCB7IFRvYXN0U2VydmljZSB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL3RvYXN0LnNlcnZpY2UnO1xuXG5pbXBvcnQgeyBOb3RpZmljYXRpb25QcmVmZXJlbmNlc1ZpZXdDb25maWcgfSBmcm9tICcuL3R5cGVzJztcbmltcG9ydCB7IE5PVElGSUNBVElPTl9QUkVGRVJFTkNFU19WSUVXX0kxOE4gfSBmcm9tICcuL25vdGlmaWNhdGlvbi1wcmVmZXJlbmNlcy12aWV3LmkxOG4nO1xuXG5hZGRJY29ucyh7XG4gIGFsZXJ0Q2lyY2xlT3V0bGluZSxcbiAgY2hldnJvbkRvd25PdXRsaW5lLFxuICBjaGV2cm9uVXBPdXRsaW5lLFxuICBjbG9zZUNpcmNsZU91dGxpbmUsXG4gIG5vdGlmaWNhdGlvbnNPZmZPdXRsaW5lLFxuICBub3RpZmljYXRpb25zT3V0bGluZSxcbiAgcmVmcmVzaE91dGxpbmUsXG59KTtcblxuY29uc3QgREVGQVVMVF9OQU1FU1BBQ0UgPSAnU2V0dGluZ3MuTm90aWZpY2F0aW9ucyc7XG5cbi8qKlxuICogRXN0YWRvcyBkZSBsYSB2aXN0YSBkZSBwcmVmZXJlbmNpYXMgcHVzaDpcbiAqICAtIGBpZGxlYDogICAgICAgICAgdG9nZ2xlIG9mZiwgc2luIG9wZXJhY2nDs24gZW4gY3Vyc28sIHNpbiBlcnJvcmVzXG4gKiAgLSBgZW5hYmxpbmdgOiAgICAgIHNlY3VlbmNpYSBkZSBhY3RpdmFjacOzbiBlbiBjdXJzbyAodG9nZ2xlIHNwaW5uZXIpXG4gKiAgLSBgZW5hYmxlZGA6ICAgICAgIHRvZG8gT0sgKG1hc3Rlcj10cnVlICsgcGVybWlzc2lvbj1ncmFudGVkICsgdG9rZW4gcmVnaXN0cmFkbylcbiAqICAtIGBkaXNhYmxpbmdgOiAgICAgZGVzYWN0aXZhY2nDs24gZW4gY3Vyc29cbiAqICAtIGBlcnJvcmA6ICAgICAgICAgw7psdGltbyBpbnRlbnRvIGZhbGzDsyDihpIgbWVuc2FqZSArIGJvdMOzbiBcIlJlaW50ZW50YXJcIlxuICogIC0gYGJsb2NrZWRgOiAgICAgICBuYXZlZ2Fkb3IgZGVuZWfDsyBwZXJtaXNvcyDihpIgbWVuc2FqZSArIGPDs21vIGFycmVnbGFyXG4gKiAgLSBgdW5zdXBwb3J0ZWRgOiAgIG5hdmVnYWRvciBubyBzb3BvcnRhIEZDTSDihpIgbWVuc2FqZVxuICogIC0gYG5lZWRzLWluc3RhbGxgOiBpT1Mgc2luIHN0YW5kYWxvbmUg4oaSIGluc3RydWNjaW9uZXMgZGUgaW5zdGFsYWNpw7NuIFBXQVxuICovXG50eXBlIFVpU3RhdGUgPSAnaWRsZScgfCAnZW5hYmxpbmcnIHwgJ2VuYWJsZWQnIHwgJ2Rpc2FibGluZycgfCAnZXJyb3InIHwgJ2Jsb2NrZWQnIHwgJ3Vuc3VwcG9ydGVkJyB8ICduZWVkcy1pbnN0YWxsJztcblxuLyoqXG4gKiBgdmFsLW5vdGlmaWNhdGlvbi1wcmVmZXJlbmNlcy12aWV3YCDigJQgKipwcmVmZXJlbmNpYXMgZGUgbm90aWZpY2FjaW9uZXMgcHVzaCoqXG4gKiAoRkNNKSBmdWxsLWZlYXR1cmUgYXV0b2NvbnRlbmlkYSAob3JnYW5pc20pLiBQcm9tb3ZpZGEgZGVzZGUgYHNob3djYXNlYCBiYWpvIGVsXG4gKiBwcm9jZXNvIGRlIEFEUi0wMjEuXG4gKlxuICogU2VwYXJhZGEgZGUgYHZhbC1ub3RpZmljYXRpb25zLXZpZXdgIChlbCBpbmJveCAvIGZlZWQpLiBBcXXDrSBlbCB1c2VyICoqYWN0aXZhIG9cbiAqIGRlc2FjdGl2YSoqIGxhcyBub3RpZmljYWNpb25lcyBwdXNoIGVuIGVzdGUgZGlzcG9zaXRpdm8uXG4gKlxuICogVVggc2ltcGxpZmljYWRvOiBVTiBzb2xvIHRvZ2dsZSBxdWUgZWplY3V0YSB0b2RhIGxhIHNlY3VlbmNpYSAocGVkaXIgcGVybWlzbyDihpJcbiAqIFNXIHJlYWR5IOKGkiBnZXRUb2tlbiDihpIgcmVnaXN0cmFyIGVuIGJhY2tlbmQg4oaSIHNldCBtYXN0ZXIgcHJlZikgdsOtYVxuICogYE1lc3NhZ2luZ1NlcnZpY2UuZW5hYmxlKClgLiBFbCByZWdpc3RybyBkZWwgZGV2aWNlIGVuIGVsIGJhY2tlbmQgdmEgY29tb1xuICogY2FsbGJhY2sgKGxhIGxpYiBubyBkZXBlbmRlIGRlIGBBdXRoU2VydmljZWAgcGFyYSBldml0YXIgY2ljbG9zKS4gTWVuc2FqZXNcbiAqIGNvbnRleHR1YWxlcyB5IGJvdMOzbiBcIlJlaW50ZW50YXJcIiBhcGFyZWNlbiBzb2xvIGN1YW5kbyBhcGxpY2FuLlxuICpcbiAqIE5PIHJlbmRlcml6YSBpb24tY29udGVudCDigJQgdml2ZSBkZW50cm8gZGUgdmFsLXBhZ2Utd3JhcHBlci4gYEFjdGl2YXRlZFJvdXRlYCBzZVxuICogaW55ZWN0YSBgeyBvcHRpb25hbDogdHJ1ZSB9YCDihpIgZnVuY2lvbmEgc2luIHJvdXRlciAoZW1iZWJpZGEgLyBTdG9yeWJvb2spLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICd2YWwtbm90aWZpY2F0aW9uLXByZWZlcmVuY2VzLXZpZXcnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbSW9uSWNvbiwgVG9nZ2xlSW5wdXRDb21wb25lbnQsIEJ1dHRvbkNvbXBvbmVudCwgRGlzcGxheUNvbXBvbmVudCwgVGl0bGVDb21wb25lbnQsIFRleHRDb21wb25lbnRdLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXYgY2xhc3M9XCJwYWdlXCI+XG4gICAgICA8aGVhZGVyIGNsYXNzPVwicGFnZS1oZWFkZXJcIj5cbiAgICAgICAgPHZhbC1kaXNwbGF5IFtwcm9wc109XCJ7IHNpemU6ICdzbWFsbCcsIGNvbG9yOiAnZGFyaycsIGNvbnRlbnQ6IHBhZ2VUaXRsZSgpIH1cIiAvPlxuICAgICAgICA8dmFsLXRpdGxlXG4gICAgICAgICAgW3Byb3BzXT1cIntcbiAgICAgICAgICAgIHNpemU6ICdsYXJnZScsXG4gICAgICAgICAgICBjb2xvcjogJ2RhcmsnLFxuICAgICAgICAgICAgYm9sZDogZmFsc2UsXG4gICAgICAgICAgICBjb250ZW50OiBwYWdlRGVzY3JpcHRpb24oKSxcbiAgICAgICAgICB9XCJcbiAgICAgICAgLz5cbiAgICAgIDwvaGVhZGVyPlxuXG4gICAgICBAaWYgKHN0YXRlKCkgPT09ICduZWVkcy1pbnN0YWxsJykge1xuICAgICAgICA8c2VjdGlvbiBjbGFzcz1cInNldHRpbmdzLXNlY3Rpb25cIj5cbiAgICAgICAgICA8dmFsLXRpdGxlXG4gICAgICAgICAgICBbcHJvcHNdPVwie1xuICAgICAgICAgICAgICBzaXplOiAnbWVkaXVtJyxcbiAgICAgICAgICAgICAgY29sb3I6ICdkYXJrJyxcbiAgICAgICAgICAgICAgYm9sZDogdHJ1ZSxcbiAgICAgICAgICAgICAgY29udGVudDogdHQoJ2lvc0luc3RhbGxUaXRsZScpLFxuICAgICAgICAgICAgfVwiXG4gICAgICAgICAgLz5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwic2VjdGlvbi1ib2R5XCI+XG4gICAgICAgICAgICA8dmFsLXRleHRcbiAgICAgICAgICAgICAgW3Byb3BzXT1cIntcbiAgICAgICAgICAgICAgICBzaXplOiAnc21hbGwnLFxuICAgICAgICAgICAgICAgIGNvbG9yOiAnbWVkaXVtJyxcbiAgICAgICAgICAgICAgICBib2xkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBjb250ZW50OiB0dCgnaW9zSW5zdGFsbEhpbnQnKSxcbiAgICAgICAgICAgICAgfVwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgICAgPG9sIGNsYXNzPVwiaW5zdGFsbC1zdGVwc1wiPlxuICAgICAgICAgICAgICA8bGk+e3sgdHQoJ2lvc0luc3RhbGxTdGVwMScpIH19PC9saT5cbiAgICAgICAgICAgICAgPGxpPnt7IHR0KCdpb3NJbnN0YWxsU3RlcDInKSB9fTwvbGk+XG4gICAgICAgICAgICAgIDxsaT57eyB0dCgnaW9zSW5zdGFsbFN0ZXAzJykgfX08L2xpPlxuICAgICAgICAgICAgICA8bGk+e3sgdHQoJ2lvc0luc3RhbGxTdGVwNCcpIH19PC9saT5cbiAgICAgICAgICAgIDwvb2w+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvc2VjdGlvbj5cbiAgICAgIH1cblxuICAgICAgPHNlY3Rpb24gY2xhc3M9XCJzZXR0aW5ncy1zZWN0aW9uXCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJub3RpZi1yb3dcIj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwibm90aWYtcm93X19pY29uXCI+XG4gICAgICAgICAgICA8aW9uLWljb24gbmFtZT1cIm5vdGlmaWNhdGlvbnMtb3V0bGluZVwiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiIC8+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm5vdGlmLXJvd19fYm9keVwiPlxuICAgICAgICAgICAgPHZhbC10aXRsZVxuICAgICAgICAgICAgICBbcHJvcHNdPVwie1xuICAgICAgICAgICAgICAgIHNpemU6ICdtZWRpdW0nLFxuICAgICAgICAgICAgICAgIGNvbG9yOiAnZGFyaycsXG4gICAgICAgICAgICAgICAgYm9sZDogdHJ1ZSxcbiAgICAgICAgICAgICAgICBjb250ZW50OiB0dCgnc2ltcGxlVGl0bGUnKSxcbiAgICAgICAgICAgICAgfVwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgICAgPHZhbC10ZXh0XG4gICAgICAgICAgICAgIFtwcm9wc109XCJ7XG4gICAgICAgICAgICAgICAgc2l6ZTogJ3NtYWxsJyxcbiAgICAgICAgICAgICAgICBjb2xvcjogJ21lZGl1bScsXG4gICAgICAgICAgICAgICAgYm9sZDogZmFsc2UsXG4gICAgICAgICAgICAgICAgY29udGVudDogdHQoJ3NpbXBsZURlc2NyaXB0aW9uJyksXG4gICAgICAgICAgICAgIH1cIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICAgIEBpZiAoYnVzeUxhYmVsKCk7IGFzIGxhYmVsKSB7XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibm90aWYtcm93X19idXN5XCI+e3sgbGFiZWwgfX08L3NwYW4+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm5vdGlmLXJvd19fY29udHJvbFwiPlxuICAgICAgICAgICAgPHZhbC10b2dnbGUtaW5wdXQgW3Byb3BzXT1cInRvZ2dsZVByb3BzKClcIiAvPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cblxuICAgICAgICBAaWYgKGNvbnRleHRNZXNzYWdlKCk7IGFzIG1zZykge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJhbGVydFwiIFtjbGFzc109XCInYWxlcnQtLScgKyBhbGVydFZhcmlhbnQoKVwiPlxuICAgICAgICAgICAgPGlvbi1pY29uIFtuYW1lXT1cImFsZXJ0SWNvbigpXCIgYXJpYS1oaWRkZW49XCJ0cnVlXCIgLz5cbiAgICAgICAgICAgIDxzcGFuPnt7IG1zZyB9fTwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuXG4gICAgICAgIEBpZiAoc3RhdGUoKSA9PT0gJ2Vycm9yJykge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJyb3ctYWN0aW9uc1wiPlxuICAgICAgICAgICAgPHZhbC1idXR0b24gW3Byb3BzXT1cInJldHJ5QnV0dG9uUHJvcHMoKVwiIChjbGljayk9XCJvblJldHJ5KClcIiAvPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICB9XG4gICAgICA8L3NlY3Rpb24+XG5cbiAgICAgIEBpZiAocmVzb2x2ZWRDb25maWcoKS5zaG93RGV0YWlscyAmJiBzdGF0ZSgpICE9PSAnbmVlZHMtaW5zdGFsbCcgJiYgc3RhdGUoKSAhPT0gJ3Vuc3VwcG9ydGVkJykge1xuICAgICAgICA8c2VjdGlvbiBjbGFzcz1cInNldHRpbmdzLXNlY3Rpb25cIj5cbiAgICAgICAgICA8YnV0dG9uIGNsYXNzPVwiZGV0YWlscy10b2dnbGVcIiB0eXBlPVwiYnV0dG9uXCIgKGNsaWNrKT1cInRvZ2dsZURldGFpbHMoKVwiPlxuICAgICAgICAgICAgPHNwYW4+e3sgc2hvd0RldGFpbHMoKSA/IHR0KCdkZXRhaWxzSGlkZScpIDogdHQoJ2RldGFpbHNTaG93JykgfX08L3NwYW4+XG4gICAgICAgICAgICA8aW9uLWljb24gW25hbWVdPVwic2hvd0RldGFpbHMoKSA/ICdjaGV2cm9uLXVwLW91dGxpbmUnIDogJ2NoZXZyb24tZG93bi1vdXRsaW5lJ1wiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiIC8+XG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgQGlmIChzaG93RGV0YWlscygpKSB7XG4gICAgICAgICAgICA8ZGwgY2xhc3M9XCJkZXRhaWxzXCI+XG4gICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkZXRhaWxzX19yb3dcIj5cbiAgICAgICAgICAgICAgICA8ZHQ+e3sgdHQoJ2RldGFpbHNUb2tlbicpIH19PC9kdD5cbiAgICAgICAgICAgICAgICA8ZGQ+e3sgdG9rZW5QcmV2aWV3KCkgPz8gdHQoJ2RldGFpbHNOb25lJykgfX08L2RkPlxuICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImRldGFpbHNfX3Jvd1wiPlxuICAgICAgICAgICAgICAgIDxkdD57eyB0dCgnZGlhZ25vc3RpY3NQZXJtaXNzaW9uJykgfX08L2R0PlxuICAgICAgICAgICAgICAgIDxkZD57eyBwZXJtaXNzaW9uKCkgfX08L2RkPlxuICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImRldGFpbHNfX3Jvd1wiPlxuICAgICAgICAgICAgICAgIDxkdD57eyB0dCgnZGlhZ25vc3RpY3NTdXBwb3J0ZWQnKSB9fTwvZHQ+XG4gICAgICAgICAgICAgICAgPGRkPnt7IGlzU3VwcG9ydGVkKCkgPyAn4pyTJyA6ICfinJcnIH19PC9kZD5cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L2RsPlxuICAgICAgICAgIH1cbiAgICAgICAgPC9zZWN0aW9uPlxuICAgICAgfVxuICAgIDwvZGl2PlxuICBgLFxuICBzdHlsZXM6IFtcbiAgICBgXG4gICAgICAucGFnZSB7XG4gICAgICAgIHBhZGRpbmc6IDE2cHggMDtcbiAgICAgICAgbWF4LXdpZHRoOiA3MjBweDtcbiAgICAgICAgbWFyZ2luOiAwIGF1dG87XG4gICAgICB9XG4gICAgICAucGFnZS1oZWFkZXIge1xuICAgICAgICBtYXJnaW4tYm90dG9tOiA4cHg7XG4gICAgICB9XG4gICAgICAuc2V0dGluZ3Mtc2VjdGlvbiB7XG4gICAgICAgIHBhZGRpbmc6IDE2cHggMDtcbiAgICAgIH1cbiAgICAgIC5zZXR0aW5ncy1zZWN0aW9uICsgLnNldHRpbmdzLXNlY3Rpb24ge1xuICAgICAgICBib3JkZXItdG9wOiAxcHggc29saWQgdmFyKC0tdmFsLWJvcmRlci1jb2xvciwgcmdiYSgwLCAwLCAwLCAwLjA4KSk7XG4gICAgICB9XG4gICAgICAuc2VjdGlvbi1ib2R5IHtcbiAgICAgICAgbWFyZ2luLXRvcDogMTZweDtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgICAgICAgZ2FwOiAxMHB4O1xuICAgICAgfVxuICAgICAgLnJvdy1hY3Rpb25zIHtcbiAgICAgICAgbWFyZ2luLXRvcDogMTJweDtcbiAgICAgIH1cblxuICAgICAgLm5vdGlmLXJvdyB7XG4gICAgICAgIGRpc3BsYXk6IGdyaWQ7XG4gICAgICAgIGdyaWQtdGVtcGxhdGUtY29sdW1uczogNDRweCAxZnIgYXV0bztcbiAgICAgICAgZ2FwOiAxNnB4O1xuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgfVxuICAgICAgLm5vdGlmLXJvd19faWNvbiB7XG4gICAgICAgIHdpZHRoOiA0NHB4O1xuICAgICAgICBoZWlnaHQ6IDQ0cHg7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDUwJTtcbiAgICAgICAgYmFja2dyb3VuZDogdmFyKC0taW9uLWNvbG9yLWxpZ2h0LCByZ2JhKDAsIDAsIDAsIDAuMDYpKTtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gICAgICB9XG4gICAgICAubm90aWYtcm93X19pY29uIGlvbi1pY29uIHtcbiAgICAgICAgZm9udC1zaXplOiAyMnB4O1xuICAgICAgICBjb2xvcjogdmFyKC0taW9uLWNvbG9yLWRhcmssICMxYTFhMWEpO1xuICAgICAgfVxuICAgICAgLm5vdGlmLXJvd19fYm9keSB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gICAgICAgIGdhcDogNHB4O1xuICAgICAgICBtaW4td2lkdGg6IDA7XG4gICAgICB9XG4gICAgICAubm90aWYtcm93X19idXN5IHtcbiAgICAgICAgZm9udC1zaXplOiAxMnB4O1xuICAgICAgICBjb2xvcjogdmFyKC0taW9uLWNvbG9yLW1lZGl1bSwgIzkyOTQ5Yyk7XG4gICAgICAgIGZvbnQtc3R5bGU6IGl0YWxpYztcbiAgICAgICAgbWFyZ2luLXRvcDogNHB4O1xuICAgICAgfVxuICAgICAgLm5vdGlmLXJvd19fY29udHJvbCB7XG4gICAgICAgIGZsZXgtc2hyaW5rOiAwO1xuICAgICAgfVxuXG4gICAgICAuYWxlcnQge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBnYXA6IDEwcHg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBmbGV4LXN0YXJ0O1xuICAgICAgICBtYXJnaW4tdG9wOiAxMnB4O1xuICAgICAgICBwYWRkaW5nOiAxMnB4IDE0cHg7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDEwcHg7XG4gICAgICAgIGZvbnQtc2l6ZTogMTNweDtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDEuNDU7XG4gICAgICAgIGJhY2tncm91bmQ6IHZhcigtLWlvbi1jb2xvci1saWdodCwgcmdiYSgwLCAwLCAwLCAwLjA0KSk7XG4gICAgICAgIGNvbG9yOiB2YXIoLS1pb24tY29sb3ItZGFyaywgIzFhMWExYSk7XG4gICAgICB9XG4gICAgICAuYWxlcnQgaW9uLWljb24ge1xuICAgICAgICBmb250LXNpemU6IDE4cHg7XG4gICAgICAgIGZsZXgtc2hyaW5rOiAwO1xuICAgICAgICBtYXJnaW4tdG9wOiAxcHg7XG4gICAgICB9XG4gICAgICAuYWxlcnQtLXdhcm5pbmcgaW9uLWljb24ge1xuICAgICAgICBjb2xvcjogdmFyKC0taW9uLWNvbG9yLXdhcm5pbmctc2hhZGUsICNjNzllMDgpO1xuICAgICAgfVxuICAgICAgLmFsZXJ0LS1kYW5nZXIgaW9uLWljb24ge1xuICAgICAgICBjb2xvcjogdmFyKC0taW9uLWNvbG9yLWRhbmdlci1zaGFkZSwgI2NmM2M0Zik7XG4gICAgICB9XG4gICAgICAuYWxlcnQtLWluZm8gaW9uLWljb24ge1xuICAgICAgICBjb2xvcjogdmFyKC0taW9uLWNvbG9yLW1lZGl1bSwgIzZiNmI2Yik7XG4gICAgICB9XG5cbiAgICAgIC5pbnN0YWxsLXN0ZXBzIHtcbiAgICAgICAgbWFyZ2luOiAwO1xuICAgICAgICBwYWRkaW5nLWxlZnQ6IDIwcHg7XG4gICAgICAgIGZvbnQtc2l6ZTogMTNweDtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDEuNjtcbiAgICAgICAgY29sb3I6IHZhcigtLWlvbi1jb2xvci1kYXJrLCAjMWExYTFhKTtcbiAgICAgIH1cbiAgICAgIC5pbnN0YWxsLXN0ZXBzIGxpICsgbGkge1xuICAgICAgICBtYXJnaW4tdG9wOiA0cHg7XG4gICAgICB9XG5cbiAgICAgIC5kZXRhaWxzLXRvZ2dsZSB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGdhcDogNnB4O1xuICAgICAgICBiYWNrZ3JvdW5kOiBub25lO1xuICAgICAgICBib3JkZXI6IDA7XG4gICAgICAgIHBhZGRpbmc6IDA7XG4gICAgICAgIGZvbnQtc2l6ZTogMTNweDtcbiAgICAgICAgY29sb3I6IHZhcigtLWlvbi1jb2xvci1tZWRpdW0sICM2YjZiNmIpO1xuICAgICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICB9XG4gICAgICAuZGV0YWlscy10b2dnbGUgaW9uLWljb24ge1xuICAgICAgICBmb250LXNpemU6IDE0cHg7XG4gICAgICB9XG4gICAgICAuZGV0YWlscyB7XG4gICAgICAgIG1hcmdpbjogMTJweCAwIDA7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gICAgICAgIGdhcDogNnB4O1xuICAgICAgfVxuICAgICAgLmRldGFpbHNfX3JvdyB7XG4gICAgICAgIGRpc3BsYXk6IGdyaWQ7XG4gICAgICAgIGdyaWQtdGVtcGxhdGUtY29sdW1uczogMTQwcHggMWZyO1xuICAgICAgICBnYXA6IDEycHg7XG4gICAgICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGJhc2VsaW5lO1xuICAgICAgfVxuICAgICAgLmRldGFpbHNfX3JvdyBkdCB7XG4gICAgICAgIG1hcmdpbjogMDtcbiAgICAgICAgY29sb3I6IHZhcigtLWlvbi1jb2xvci1tZWRpdW0sICM2YjZiNmIpO1xuICAgICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgICAgICBsZXR0ZXItc3BhY2luZzogMC40cHg7XG4gICAgICAgIGZvbnQtd2VpZ2h0OiA1MDA7XG4gICAgICB9XG4gICAgICAuZGV0YWlsc19fcm93IGRkIHtcbiAgICAgICAgbWFyZ2luOiAwO1xuICAgICAgICBjb2xvcjogdmFyKC0taW9uLWNvbG9yLWRhcmssICMxYTFhMWEpO1xuICAgICAgICBmb250LWZhbWlseTogdWktbW9ub3NwYWNlLCBTRk1vbm8tUmVndWxhciwgbW9ub3NwYWNlO1xuICAgICAgICBmb250LXNpemU6IDExcHg7XG4gICAgICAgIHdvcmQtYnJlYWs6IGJyZWFrLWFsbDtcbiAgICAgIH1cbiAgICBgLFxuICBdLFxufSlcbmV4cG9ydCBjbGFzcyBOb3RpZmljYXRpb25QcmVmZXJlbmNlc1ZpZXdDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gIHByaXZhdGUgbmF2ID0gaW5qZWN0KE5hdmlnYXRpb25TZXJ2aWNlKTtcbiAgcHJpdmF0ZSBpMThuID0gaW5qZWN0KEkxOG5TZXJ2aWNlKTtcbiAgcHJpdmF0ZSB0b2FzdCA9IGluamVjdChUb2FzdFNlcnZpY2UpO1xuICBwcml2YXRlIGF1dGggPSBpbmplY3QoQXV0aFNlcnZpY2UpO1xuICBwcml2YXRlIG1lc3NhZ2luZyA9IGluamVjdChNZXNzYWdpbmdTZXJ2aWNlKTtcbiAgcHJpdmF0ZSBwYWdlUmVmcmVzaCA9IGluamVjdChQYWdlUmVmcmVzaFNlcnZpY2UpO1xuICByZWFkb25seSBwcmVmcyA9IGluamVjdChQcmVmZXJlbmNlc1NlcnZpY2UpO1xuICAvLyBgQWN0aXZhdGVkUm91dGVgIG9wY2lvbmFsIOKGkiBsYSB2aXN0YSBmdW5jaW9uYSBlbWJlYmlkYSAvIGVuIFN0b3J5Ym9vayBzaW5cbiAgLy8gcm91dGVyIChzb2xvIGxvIHVzYSBwYXJhIGxlZXIgZWwgcm91dGUgZGF0YSBkZSBjb25maWcgY29tbyBmYWxsYmFjaykuXG4gIHByaXZhdGUgcm91dGUgPSBpbmplY3QoQWN0aXZhdGVkUm91dGUsIHsgb3B0aW9uYWw6IHRydWUgfSk7XG5cbiAgLyoqXG4gICAqIENvbmZpZyB2w61hIEBJbnB1dCAob2JqZWN0LWZpcnN0KS4gU2kgbm8gc2UgcGFzYSwgc2UgY2FlIGFsIHJvdXRlIGRhdGFcbiAgICogYG5vdGlmaWNhdGlvblByZWZlcmVuY2VzQ29uZmlnYCAocG9ibGFkbyBwb3JcbiAgICogYHByb3ZpZGVWYWx0ZWNoTm90aWZpY2F0aW9uUHJlZmVyZW5jZXNSb3V0ZXNgKS4gYHJlc29sdmVkQ29uZmlnYCBtZXJnZWEgY29uXG4gICAqIGxvcyBkZWZhdWx0cyDigJQgYEBJbnB1dGAgZ2FuYSBzb2JyZSByb3V0ZSBkYXRhLlxuICAgKi9cbiAgQElucHV0KCkgY29uZmlnPzogTm90aWZpY2F0aW9uUHJlZmVyZW5jZXNWaWV3Q29uZmlnO1xuXG4gIHJlYWRvbmx5IHJlc29sdmVkQ29uZmlnID0gY29tcHV0ZWQ8XG4gICAgUmVxdWlyZWQ8T21pdDxOb3RpZmljYXRpb25QcmVmZXJlbmNlc1ZpZXdDb25maWcsICdvblB1c2hFbmFibGVkJyB8ICdvblB1c2hEaXNhYmxlZCc+PiAmXG4gICAgICBQaWNrPE5vdGlmaWNhdGlvblByZWZlcmVuY2VzVmlld0NvbmZpZywgJ29uUHVzaEVuYWJsZWQnIHwgJ29uUHVzaERpc2FibGVkJz5cbiAgPigoKSA9PiB7XG4gICAgY29uc3QgZnJvbVJvdXRlID0gKHRoaXMucm91dGU/LnNuYXBzaG90LmRhdGFbJ25vdGlmaWNhdGlvblByZWZlcmVuY2VzQ29uZmlnJ10gPz9cbiAgICAgIHt9KSBhcyBOb3RpZmljYXRpb25QcmVmZXJlbmNlc1ZpZXdDb25maWc7XG4gICAgY29uc3QgbWVyZ2VkOiBOb3RpZmljYXRpb25QcmVmZXJlbmNlc1ZpZXdDb25maWcgPSB7IC4uLmZyb21Sb3V0ZSwgLi4uKHRoaXMuY29uZmlnID8/IHt9KSB9O1xuICAgIHJldHVybiB7XG4gICAgICBzaG93RGV0YWlsczogbWVyZ2VkLnNob3dEZXRhaWxzID8/IHRydWUsXG4gICAgICBpMThuTmFtZXNwYWNlOiBtZXJnZWQuaTE4bk5hbWVzcGFjZSA/PyBERUZBVUxUX05BTUVTUEFDRSxcbiAgICAgIG9uUHVzaEVuYWJsZWQ6IG1lcmdlZC5vblB1c2hFbmFibGVkLFxuICAgICAgb25QdXNoRGlzYWJsZWQ6IG1lcmdlZC5vblB1c2hEaXNhYmxlZCxcbiAgICB9O1xuICB9KTtcblxuICAvKiogTmFtZXNwYWNlIGkxOG4gcmVzdWVsdG8gKGNhcHR1cmFkbyBwYXJhIGxsYW1hZGFzIG5vLXJlYWN0aXZhcykuICovXG4gIHByaXZhdGUgZ2V0IG5zKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMucmVzb2x2ZWRDb25maWcoKS5pMThuTmFtZXNwYWNlO1xuICB9XG5cbiAgLyoqIE9wZXJhY2nDs24gZW4gY3Vyc28gKGdvYmllcm5hIHNwaW5uZXIgZGVsIHRvZ2dsZSArIGxhYmVsIFwiQWN0aXZhbmRv4oCmXCIpLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IF9idXN5ID0gc2lnbmFsPG51bGwgfCAnZW5hYmxpbmcnIHwgJ2Rpc2FibGluZyc+KG51bGwpO1xuICAvKiogVHJ1ZSBzaSBlbCDDumx0aW1vIGludGVudG8gZmFsbMOzIOKAlCBnb2JpZXJuYSBlc3RhZG8gYGVycm9yYCArIHJldHJ5IGJ1dHRvbi4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBfbGFzdEZhaWxlZCA9IHNpZ25hbChmYWxzZSk7XG4gIC8qKiBUcnVlIG1pZW50cmFzIHNlIHJlLW9idGllbmUgZWwgdG9rZW4gRkNNIGVuIGJhY2tncm91bmQgKG5vIGFmZWN0YSBsYSBVSSkuICovXG4gIHByaXZhdGUgX3JlcGFpcmluZ1Rva2VuID0gZmFsc2U7XG4gIC8qKiBUb2dnbGUgZGVsIGV4cGFuZCBkZSBkZXRhbGxlcyB0w6ljbmljb3MuICovXG4gIHJlYWRvbmx5IHNob3dEZXRhaWxzID0gc2lnbmFsKGZhbHNlKTtcblxuICByZWFkb25seSBwZXJtaXNzaW9uID0gc2lnbmFsPE5vdGlmaWNhdGlvblBlcm1pc3Npb25TdGF0ZT4oJ2RlZmF1bHQnKTtcbiAgcmVhZG9ubHkgaXNTdXBwb3J0ZWQgPSBzaWduYWwoZmFsc2UpO1xuICByZWFkb25seSBjdXJyZW50VG9rZW4gPSBzaWduYWw8c3RyaW5nIHwgbnVsbD4obnVsbCk7XG4gIHJlYWRvbmx5IG5lZWRzSU9TSW5zdGFsbCA9IHNpZ25hbCh0aGlzLmRldGVjdE5lZWRzSU9TSW5zdGFsbCgpKTtcblxuICAvKiogRm9ybUNvbnRyb2wgZGVsIHRvZ2dsZSAoc3luYy1lYWRvIGNvbiBgaXNFbmFibGVkYCB2aWEgZWZmZWN0KS4gKi9cbiAgcmVhZG9ubHkgdG9nZ2xlQ29udHJvbCA9IG5ldyBGb3JtQ29udHJvbDxib29sZWFuPihmYWxzZSwge1xuICAgIG5vbk51bGxhYmxlOiB0cnVlLFxuICB9KTtcblxuICAvKiogSGFuZGxlciBkZWwgbGlzdGVuZXIgYGZvY3VzYCDigJQgcmVmZXJlbmNpYSBndWFyZGFkYSBwYXJhIHBvZGVyIHJlbW92ZXJsby4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBvbldpbmRvd0ZvY3VzID0gKCkgPT4gdGhpcy5zY2hlZHVsZUJyb3dzZXJSZWZyZXNoKCk7XG4gIC8qKiBUaW1lciBkZWwgZGVib3VuY2UgZGVsIHJlZnJlc2ggZGlzcGFyYWRvIHBvciBgZm9jdXNgLiAqL1xuICBwcml2YXRlIGZvY3VzRGVib3VuY2VUaW1lcjogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4gfCBudWxsID0gbnVsbDtcbiAgLyoqIFZlbnRhbmEgZGUgZGVib3VuY2UgKG1zKSBwYXJhIGVsIHJlZnJlc2ggZGlzcGFyYWRvIHBvciBgZm9jdXNgLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IEZPQ1VTX0RFQk9VTkNFX01TID0gNTAwO1xuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBFc3RhZG8gZGVyaXZhZG9cbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgcmVhZG9ubHkgcGFnZVRpdGxlID0gY29tcHV0ZWQoKCkgPT4gdGhpcy50dCgncGFnZVRpdGxlJykpO1xuICByZWFkb25seSBwYWdlRGVzY3JpcHRpb24gPSBjb21wdXRlZCgoKSA9PiB0aGlzLnR0KCdwYWdlRGVzY3JpcHRpb24nKSk7XG5cbiAgLyoqXG4gICAqIEVzdGFkbyBlZmVjdGl2byBkZWwgdG9nZ2xlOiBcImVsIHVzdWFyaW8gdGllbmUgcHVzaCBhY3Rpdm9cIi5cbiAgICpcbiAgICogU2UgZGVyaXZhIFNPTE8gZGUgbGEgZGVjaXNpw7NuIGRlbCB1c3VhcmlvIChgbWFzdGVyYCkgeSBkZWwgcGVybWlzbyBkZWwgU09cbiAgICogKGBwZXJtaXNzaW9uYCkuIEVsIHRva2VuIEZDTSBOTyBmb3JtYSBwYXJ0ZSBkZWwgZXN0YWRvIGRlIFVJOiB2aXZlIGVuXG4gICAqIG1lbW9yaWEsIHNlIHBpZXJkZSBlbiBjYWRhIHJlY2FyZ2Evc3VzcGVuc2nDs24gZGUgbGEgUFdBIHkgcHJvZHVjaXLDrWEgZmxpY2tlci5cbiAgICogU2kgZmFsdGEgdG9rZW4gY29uIGBtYXN0ZXIgJiYgZ3JhbnRlZGAsIGVzIHVuIGRldGFsbGUgcmVwYXJhYmxlIGVuXG4gICAqIGJhY2tncm91bmQgKHZlciBgcmVwYWlyVG9rZW5JZk5lZWRlZGApLCBubyB1biBjYW1iaW8gZGVsIHRvZ2dsZS5cbiAgICovXG4gIHJlYWRvbmx5IGlzRW5hYmxlZCA9IGNvbXB1dGVkKCgpID0+IHRoaXMucHJlZnMubm90aWZpY2F0aW9uc01hc3RlcigpICYmIHRoaXMucGVybWlzc2lvbigpID09PSAnZ3JhbnRlZCcpO1xuXG4gIHJlYWRvbmx5IHN0YXRlID0gY29tcHV0ZWQ8VWlTdGF0ZT4oKCkgPT4ge1xuICAgIGlmICh0aGlzLm5lZWRzSU9TSW5zdGFsbCgpKSByZXR1cm4gJ25lZWRzLWluc3RhbGwnO1xuICAgIGlmICh0aGlzLnBlcm1pc3Npb24oKSA9PT0gJ3Vuc3VwcG9ydGVkJyB8fCAhdGhpcy5pc1N1cHBvcnRlZCgpKSB7XG4gICAgICAvLyBpT1Mgc3RhbmRhbG9uZSBhw7puIHB1ZWRlIGRhciBpc1N1cHBvcnRlZCBmYWxzZSB0ZW1wcmFubyBhbnRlcyBkZSBpbml0IOKAlFxuICAgICAgLy8gc29sbyBibG9xdWVhbW9zIHNpIGVsIGJyb3dzZXIgZXhwbMOtY2l0YW1lbnRlIG5vIHNvcG9ydGEgQVBJcy5cbiAgICAgIGlmICh0aGlzLnBlcm1pc3Npb24oKSA9PT0gJ3Vuc3VwcG9ydGVkJykgcmV0dXJuICd1bnN1cHBvcnRlZCc7XG4gICAgfVxuICAgIGNvbnN0IGJ1c3kgPSB0aGlzLl9idXN5KCk7XG4gICAgaWYgKGJ1c3kgPT09ICdlbmFibGluZycpIHJldHVybiAnZW5hYmxpbmcnO1xuICAgIGlmIChidXN5ID09PSAnZGlzYWJsaW5nJykgcmV0dXJuICdkaXNhYmxpbmcnO1xuICAgIGlmICh0aGlzLmlzRW5hYmxlZCgpKSByZXR1cm4gJ2VuYWJsZWQnO1xuICAgIGlmICh0aGlzLnBlcm1pc3Npb24oKSA9PT0gJ2RlbmllZCcpIHJldHVybiAnYmxvY2tlZCc7XG4gICAgaWYgKHRoaXMuX2xhc3RGYWlsZWQoKSkgcmV0dXJuICdlcnJvcic7XG4gICAgcmV0dXJuICdpZGxlJztcbiAgfSk7XG5cbiAgcmVhZG9ubHkgYnVzeUxhYmVsID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIGNvbnN0IHMgPSB0aGlzLnN0YXRlKCk7XG4gICAgaWYgKHMgPT09ICdlbmFibGluZycpIHJldHVybiB0aGlzLnR0KCdlbmFibGluZycpO1xuICAgIGlmIChzID09PSAnZGlzYWJsaW5nJykgcmV0dXJuIHRoaXMudHQoJ2Rpc2FibGluZycpO1xuICAgIHJldHVybiBudWxsO1xuICB9KTtcblxuICByZWFkb25seSBjb250ZXh0TWVzc2FnZSA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBzd2l0Y2ggKHRoaXMuc3RhdGUoKSkge1xuICAgICAgY2FzZSAndW5zdXBwb3J0ZWQnOlxuICAgICAgICByZXR1cm4gdGhpcy50dCgncGVybWlzc2lvblVuc3VwcG9ydGVkJyk7XG4gICAgICBjYXNlICdibG9ja2VkJzpcbiAgICAgICAgcmV0dXJuIHRoaXMudHQoJ3Blcm1pc3Npb25EZW5pZWRJbmZvJyk7XG4gICAgICBjYXNlICdlcnJvcic6XG4gICAgICAgIHJldHVybiB0aGlzLnR0KCdlcnJvckdlbmVyaWMnKTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfSk7XG5cbiAgcmVhZG9ubHkgYWxlcnRWYXJpYW50ID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIGNvbnN0IHMgPSB0aGlzLnN0YXRlKCk7XG4gICAgaWYgKHMgPT09ICdlcnJvcicgfHwgcyA9PT0gJ2Jsb2NrZWQnKSByZXR1cm4gJ3dhcm5pbmcnO1xuICAgIGlmIChzID09PSAndW5zdXBwb3J0ZWQnKSByZXR1cm4gJ2Rhbmdlcic7XG4gICAgcmV0dXJuICdpbmZvJztcbiAgfSk7XG5cbiAgcmVhZG9ubHkgYWxlcnRJY29uID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIGNvbnN0IHMgPSB0aGlzLnN0YXRlKCk7XG4gICAgaWYgKHMgPT09ICd1bnN1cHBvcnRlZCcpIHJldHVybiAnY2xvc2UtY2lyY2xlLW91dGxpbmUnO1xuICAgIHJldHVybiAnYWxlcnQtY2lyY2xlLW91dGxpbmUnO1xuICB9KTtcblxuICByZWFkb25seSB0b2dnbGVQcm9wcyA9IGNvbXB1dGVkPFRvZ2dsZUlucHV0TWV0YWRhdGE+KCgpID0+ICh7XG4gICAgY29udHJvbDogdGhpcy50b2dnbGVDb250cm9sLFxuICAgIGNvbG9yOiAncHJpbWFyeScsXG4gICAgZGlzYWJsZWQ6XG4gICAgICB0aGlzLl9idXN5KCkgIT09IG51bGwgfHxcbiAgICAgIHRoaXMuc3RhdGUoKSA9PT0gJ3Vuc3VwcG9ydGVkJyB8fFxuICAgICAgdGhpcy5zdGF0ZSgpID09PSAnbmVlZHMtaW5zdGFsbCcgfHxcbiAgICAgIHRoaXMuc3RhdGUoKSA9PT0gJ2Jsb2NrZWQnLFxuICB9KSk7XG5cbiAgcmVhZG9ubHkgcmV0cnlCdXR0b25Qcm9wcyA9IGNvbXB1dGVkPFBhcnRpYWw8QnV0dG9uTWV0YWRhdGE+PigoKSA9PiAoe1xuICAgIHRleHQ6IHRoaXMudHQoJ3JldHJ5Q3RhJyksXG4gICAgY29sb3I6ICdkYXJrJyxcbiAgICBmaWxsOiAnb3V0bGluZScsXG4gICAgc2l6ZTogJ2RlZmF1bHQnLFxuICAgIHNoYXBlOiAncm91bmQnLFxuICAgIHR5cGU6ICdidXR0b24nLFxuICAgIGljb246IHsgbmFtZTogJ3JlZnJlc2gtb3V0bGluZScsIHNsb3Q6ICdzdGFydCcgfSxcbiAgICBzdGF0ZTogdGhpcy5fYnVzeSgpICE9PSBudWxsID8gQ29tcG9uZW50U3RhdGVzLldPUktJTkcgOiBDb21wb25lbnRTdGF0ZXMuRU5BQkxFRCxcbiAgfSkpO1xuXG4gIHJlYWRvbmx5IHRva2VuUHJldmlldyA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBjb25zdCB0ID0gdGhpcy5jdXJyZW50VG9rZW4oKTtcbiAgICBpZiAoIXQpIHJldHVybiBudWxsO1xuICAgIHJldHVybiBgJHt0LnNsaWNlKDAsIDIyKX3igKYke3Quc2xpY2UoLTYpfWA7XG4gIH0pO1xuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBMaWZlY3ljbGVcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgY29uc3QgbnMgPSB0aGlzLm5zO1xuICAgIGlmICghdGhpcy5pMThuLmhhc05hbWVzcGFjZShucykpIHtcbiAgICAgIHRoaXMuaTE4bi5yZWdpc3RlckNvbnRlbnQobnMsIE5PVElGSUNBVElPTl9QUkVGRVJFTkNFU19WSUVXX0kxOE4pO1xuICAgIH1cbiAgICB0aGlzLm5hdi5zZXRCYWNrSGVhZGVyKCdwYWdlVGl0bGUnLCBucywgeyB3aXRoTWVudTogdHJ1ZSB9KTtcblxuICAgIHZvaWQgdGhpcy5yZWZyZXNoQnJvd3NlclN0YXRlKCk7XG5cbiAgICAvLyBSZS1hcGxpY2FyIGhlYWRlciBlbiBjYW1iaW8gZGUgaWRpb21hLlxuICAgIGVmZmVjdChcbiAgICAgICgpID0+IHtcbiAgICAgICAgdGhpcy5pMThuLmxhbmcoKTtcbiAgICAgICAgdGhpcy5uYXYuc2V0QmFja0hlYWRlcigncGFnZVRpdGxlJywgdGhpcy5ucywgeyB3aXRoTWVudTogdHJ1ZSB9KTtcbiAgICAgIH0sXG4gICAgICB7IGFsbG93U2lnbmFsV3JpdGVzOiB0cnVlIH1cbiAgICApO1xuXG4gICAgLy8gU3luYyBGb3JtQ29udHJvbCDihpDihpIgZXN0YWRvIGVmZWN0aXZvLiBDdWFuZG8gZWwgZXN0YWRvIGNhbWJpYSAoY2FtYmlvIGRlXG4gICAgLy8gcGVybWlzc2lvbiBwb3IgZWwgYnJvd3NlciwgbyBzbmFwc2hvdCBGaXJlc3RvcmUgZGUgYG1hc3RlcmApLCBlbCB0b2dnbGUgc2VcbiAgICAvLyByZS1hbGluZWEgY29uIGxhIHJlYWxpZGFkLlxuICAgIC8vXG4gICAgLy8gSU1QT1JUQU5URTogc2UgaWdub3JhbiBsb3MgY2FtYmlvcyBtaWVudHJhcyBoYXkgdW5hIG9wZXJhY2nDs24gZW4gY3Vyc29cbiAgICAvLyAoYF9idXN5KCkgIT09IG51bGxgKS4gRHVyYW50ZSBgb25FbmFibGVgL2BvbkRpc2FibGVgIGxhcyBzZcOxYWxlcyBmdWVudGUgc2VcbiAgICAvLyBhY3R1YWxpemFuIGVuIG1vbWVudG9zIGRpc3RpbnRvcyB5IHByb2R1Y2lyw61hbiBlc3RhZG9zIGludGVybWVkaW9zIOKGklxuICAgIC8vIGZsaWNrZXIgdmlzaWJsZS4gRWwgdG9nZ2xlIHNlIHJlLWFsaW5lYSBVTkEgc29sYSB2ZXogZW4gZWwgYGZpbmFsbHlgLlxuICAgIGVmZmVjdChcbiAgICAgICgpID0+IHtcbiAgICAgICAgY29uc3QgZWZmID0gdGhpcy5pc0VuYWJsZWQoKTtcbiAgICAgICAgaWYgKHRoaXMuX2J1c3koKSAhPT0gbnVsbCkgcmV0dXJuO1xuICAgICAgICBpZiAodGhpcy50b2dnbGVDb250cm9sLnZhbHVlICE9PSBlZmYpIHtcbiAgICAgICAgICB0aGlzLnRvZ2dsZUNvbnRyb2wuc2V0VmFsdWUoZWZmLCB7IGVtaXRFdmVudDogZmFsc2UgfSk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICB7IGFsbG93U2lnbmFsV3JpdGVzOiB0cnVlIH1cbiAgICApO1xuXG4gICAgLy8gSW50ZXJjZXB0YXIgY2xpY2sgZGVsIHVzZXI6IGVsIHRvZ2dsZSB5YSBjYW1iacOzIHZpc3VhbG1lbnRlLCBlamVjdXRhclxuICAgIC8vIGxhIGFjY2nDs24gY29ycmVzcG9uZGllbnRlLlxuICAgIHRoaXMudG9nZ2xlQ29udHJvbC52YWx1ZUNoYW5nZXMuc3Vic2NyaWJlKHYgPT4ge1xuICAgICAgaWYgKHRoaXMuX2J1c3koKSAhPT0gbnVsbCkgcmV0dXJuO1xuICAgICAgaWYgKHYgJiYgIXRoaXMuaXNFbmFibGVkKCkpIHtcbiAgICAgICAgdm9pZCB0aGlzLm9uRW5hYmxlKCk7XG4gICAgICB9IGVsc2UgaWYgKCF2ICYmIHRoaXMuaXNFbmFibGVkKCkpIHtcbiAgICAgICAgdm9pZCB0aGlzLm9uRGlzYWJsZSgpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gRGV0ZWN0YXIgcmVncmVzbyBhIGxhIHBlc3Rhw7FhIOKAlCBlbCB1c2VyIHB1ZG8gaGFiZXIgY2FtYmlhZG8gcGVybWlzb3NcbiAgICAvLyBkZXNkZSBsYSBjb25maWcgZGVsIGJyb3dzZXIuIEVsIHJlZnJlc2ggdmEgY29uIGRlYm91bmNlIHBhcmEgbm9cbiAgICAvLyBkaXNwYXJhcnNlIGVuIHLDoWZhZ2FzIChlbiBQV0EgbW9iaWxlIGBmb2N1c2Agc2FsdGEgY29uIGNhZGEgY2FtYmlvIGRlXG4gICAgLy8gYXBwIC8gYXBhcmljacOzbiBkZWwgdGVjbGFkbykuIEVsIGxpc3RlbmVyIHNlIHJlbXVldmUgZW4gbmdPbkRlc3Ryb3kuXG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignZm9jdXMnLCB0aGlzLm9uV2luZG93Rm9jdXMpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpc3RyYSBlbCBwdWxsLXRvLXJlZnJlc2ggZXN0w6FuZGFyLiBBdW5xdWUgZXN0YSB2aXN0YSBubyBtdWVzdHJhIHVuYVxuICAgKiBsaXN0YSByZW1vdGEsIHPDrSByZWZsZWphIGVzdGFkbyBtdXRhYmxlIGRlbCBicm93c2VyIChwZXJtaXNvLCB0b2tlbixcbiAgICogc29wb3J0ZSBGQ00pIHF1ZSBwdWVkZSBjYW1iaWFyIGZ1ZXJhIGRlIGxhIGFwcCDigJQgZWwgcmVmcmVzaCByZS1ldmFsw7phIHRvZG8uXG4gICAqXG4gICAqIFNlIHVzYSBgbmdPbkluaXRgL2BuZ09uRGVzdHJveWAgKE5PIGBpb25WaWV3V2lsbEVudGVyYCkgcG9ycXVlXG4gICAqIGB2YWwtcGFnZS13cmFwcGVyYCB1c2EgYDxyb3V0ZXItb3V0bGV0PmAgcGxhbm8g4oCUIGxvcyBob29rcyBgaW9uVmlldypgIHNvbG9cbiAgICogZGlzcGFyYW4gZGVudHJvIGRlIGA8aW9uLXJvdXRlci1vdXRsZXQ+YC5cbiAgICovXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIHRoaXMucGFnZVJlZnJlc2gucmVnaXN0ZXIoKCkgPT4gdGhpcy5yZWZyZXNoQnJvd3NlclN0YXRlKCkpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5wYWdlUmVmcmVzaC51bnJlZ2lzdGVyKCk7XG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcignZm9jdXMnLCB0aGlzLm9uV2luZG93Rm9jdXMpO1xuICAgIH1cbiAgICBpZiAodGhpcy5mb2N1c0RlYm91bmNlVGltZXIgIT09IG51bGwpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aGlzLmZvY3VzRGVib3VuY2VUaW1lcik7XG4gICAgICB0aGlzLmZvY3VzRGVib3VuY2VUaW1lciA9IG51bGw7XG4gICAgfVxuICB9XG5cbiAgLyoqIFJlZnJlc2NhIGVsIGVzdGFkbyBkZWwgYnJvd3NlciBjb24gZGVib3VuY2Ug4oCUIGV2aXRhIHLDoWZhZ2FzIGRlIGBmb2N1c2AuICovXG4gIHByaXZhdGUgc2NoZWR1bGVCcm93c2VyUmVmcmVzaCgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5mb2N1c0RlYm91bmNlVGltZXIgIT09IG51bGwpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aGlzLmZvY3VzRGVib3VuY2VUaW1lcik7XG4gICAgfVxuICAgIHRoaXMuZm9jdXNEZWJvdW5jZVRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLmZvY3VzRGVib3VuY2VUaW1lciA9IG51bGw7XG4gICAgICB2b2lkIHRoaXMucmVmcmVzaEJyb3dzZXJTdGF0ZSgpO1xuICAgIH0sIHRoaXMuRk9DVVNfREVCT1VOQ0VfTVMpO1xuICB9XG5cbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8vIEFjdGlvbnNcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgLyoqXG4gICAqIFNlY3VlbmNpYSBjb21wbGV0YSBkZSBhY3RpdmFjacOzbi5cbiAgICpcbiAgICogVG9kYSBsYSBtZWPDoW5pY2EgKHBlcm1pc3Npb24g4oaSIFNXIHJlYWR5IOKGkiBnZXRUb2tlbiBjb24gcmV0cnkg4oaSIHdhdGNoZG9nIGRlXG4gICAqIGF1dG8tcmVsb2FkKSB2aXZlIGVuIGBNZXNzYWdpbmdTZXJ2aWNlLmVuYWJsZSgpYCBkZSBsYSBsaWIuIEVsIHBhc28gZGVcbiAgICogcmVnaXN0cm8gZGVsIGRldmljZSBlbiBlbCBiYWNrZW5kIHNlIHBhc2EgY29tbyBjYWxsYmFjayAoYHJlZ2lzdGVyRGV2aWNlYClcbiAgICogcG9ycXVlIGxhIGxpYiBubyBwdWVkZSBkZXBlbmRlciBkZSBgQXV0aFNlcnZpY2VgIHNpbiBjcmVhciB1biBjaWNsby5cbiAgICpcbiAgICogYGVuYWJsZSgpYCBubyBoYWNlIHRocm93OiByZXN1ZWx2ZSBjb24gdW4gYEVuYWJsZVB1c2hSZXN1bHRgIGRlc2NyaXB0aXZvLlxuICAgKi9cbiAgYXN5bmMgb25FbmFibGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5fYnVzeS5zZXQoJ2VuYWJsaW5nJyk7XG4gICAgdGhpcy5fbGFzdEZhaWxlZC5zZXQoZmFsc2UpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdDogRW5hYmxlUHVzaFJlc3VsdCA9IGF3YWl0IHRoaXMubWVzc2FnaW5nLmVuYWJsZSh7XG4gICAgICAgIHJlZ2lzdGVyRGV2aWNlOiB0b2tlbiA9PiB0aGlzLmF1dGgucmVnaXN0ZXJEZXZpY2UodG9rZW4pLnRoZW4ociA9PiByLnJlZ2lzdGVyZWQpLFxuICAgICAgfSk7XG5cbiAgICAgIGlmIChyZXN1bHQuc3RhdHVzID09PSAnZW5hYmxlZCcpIHtcbiAgICAgICAgLy8gUGVyc2lzdGlyIGZsYWcgbWFzdGVyIHNpIGHDum4gbm8gZXN0YWJhIGFjdGl2by5cbiAgICAgICAgaWYgKCF0aGlzLnByZWZzLm5vdGlmaWNhdGlvbnNNYXN0ZXIoKSkge1xuICAgICAgICAgIGF3YWl0IHRoaXMucHJlZnMuc2V0Tm90aWZpY2F0aW9uc01hc3Rlcih0cnVlKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnRvYXN0LnNob3coe1xuICAgICAgICAgIG1lc3NhZ2U6IHRoaXMudHQoJ2VuYWJsZVN1Y2Nlc3MnKSxcbiAgICAgICAgICBkdXJhdGlvbjogMjAwMCxcbiAgICAgICAgICBjb2xvcjogJ2RhcmsnLFxuICAgICAgICAgIHBvc2l0aW9uOiAndG9wJyxcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMucmVzb2x2ZWRDb25maWcoKS5vblB1c2hFbmFibGVkPy4oKTtcbiAgICAgIH0gZWxzZSBpZiAocmVzdWx0LnN0YXR1cyA9PT0gJ3RpbWVvdXQnICYmIHJlc3VsdC5yZWxvYWRlZCkge1xuICAgICAgICAvLyBMYSBsaWIgZGlzcGFyw7MgZWwgYXV0by1yZWxvYWQg4oCUIGxhIHDDoWdpbmEgZXN0w6EgcG9yIHJlY2FyZ2Fyc2UuXG4gICAgICAgIHJldHVybjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIGRlbmllZCAvIHVuc3VwcG9ydGVkIC8gZXJyb3IgLyB0aW1lb3V0IHNpbiByZWxvYWQg4oaSIGZhbGxvIHJlY3VwZXJhYmxlLlxuICAgICAgICB0aGlzLl9sYXN0RmFpbGVkLnNldCh0cnVlKTtcbiAgICAgICAgdGhpcy50b2FzdC5zaG93KHtcbiAgICAgICAgICBtZXNzYWdlOiB0aGlzLnR0KCdlcnJvckdlbmVyaWMnKSxcbiAgICAgICAgICBkdXJhdGlvbjogMzAwMCxcbiAgICAgICAgICBjb2xvcjogJ2RhcmsnLFxuICAgICAgICAgIHBvc2l0aW9uOiAndG9wJyxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBgZW5hYmxlKClgIG5vIGhhY2UgdGhyb3csIHBlcm8gZWwgY2FsbGJhY2sgZGUgcmVnaXN0cm8gc8OtIHB1ZWRlLlxuICAgICAgdGhpcy5fbGFzdEZhaWxlZC5zZXQodHJ1ZSk7XG4gICAgICB0aGlzLnRvYXN0LnNob3coe1xuICAgICAgICBtZXNzYWdlOiB0aGlzLnR0KCdlcnJvckdlbmVyaWMnKSxcbiAgICAgICAgZHVyYXRpb246IDMwMDAsXG4gICAgICAgIGNvbG9yOiAnZGFyaycsXG4gICAgICAgIHBvc2l0aW9uOiAndG9wJyxcbiAgICAgIH0pO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAvLyDDmm5pY28gcmVmcmVzaCBmaW5hbDogcmUtYWxpbmVhIGVsIHRvZ2dsZSBVTkEgc29sYSB2ZXogY29uIGVsIGVzdGFkbyByZWFsLlxuICAgICAgYXdhaXQgdGhpcy5yZWZyZXNoQnJvd3NlclN0YXRlKCk7XG4gICAgICB0aGlzLl9idXN5LnNldChudWxsKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBvbkRpc2FibGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5fYnVzeS5zZXQoJ2Rpc2FibGluZycpO1xuICAgIHRoaXMuX2xhc3RGYWlsZWQuc2V0KGZhbHNlKTtcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLmF1dGguZGlzYWJsZU5vdGlmaWNhdGlvbnMoKTtcbiAgICAgIGlmICh0aGlzLnByZWZzLm5vdGlmaWNhdGlvbnNNYXN0ZXIoKSkge1xuICAgICAgICBhd2FpdCB0aGlzLnByZWZzLnNldE5vdGlmaWNhdGlvbnNNYXN0ZXIoZmFsc2UpO1xuICAgICAgfVxuICAgICAgdGhpcy50b2FzdC5zaG93KHtcbiAgICAgICAgbWVzc2FnZTogdGhpcy50dCgnZGlzYWJsZVN1Y2Nlc3MnKSxcbiAgICAgICAgZHVyYXRpb246IDIwMDAsXG4gICAgICAgIGNvbG9yOiAnZGFyaycsXG4gICAgICAgIHBvc2l0aW9uOiAndG9wJyxcbiAgICAgIH0pO1xuICAgICAgdGhpcy5yZXNvbHZlZENvbmZpZygpLm9uUHVzaERpc2FibGVkPy4oKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHRoaXMuX2xhc3RGYWlsZWQuc2V0KHRydWUpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBhd2FpdCB0aGlzLnJlZnJlc2hCcm93c2VyU3RhdGUoKTtcbiAgICAgIHRoaXMuX2J1c3kuc2V0KG51bGwpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBSZWludGVudGFyIGxhIHNlY3VlbmNpYSBjb21wbGV0YSBkZSBhY3RpdmFjacOzbiwgc2luIGltcG9ydGFyIGVsIGVzdGFkbyBhY3R1YWwuICovXG4gIGFzeW5jIG9uUmV0cnkoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5fbGFzdEZhaWxlZC5zZXQoZmFsc2UpO1xuICAgIGF3YWl0IHRoaXMub25FbmFibGUoKTtcbiAgfVxuXG4gIHRvZ2dsZURldGFpbHMoKTogdm9pZCB7XG4gICAgdGhpcy5zaG93RGV0YWlscy51cGRhdGUodiA9PiAhdik7XG4gIH1cblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgLy8gUHJpdmFkb3NcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgcHJpdmF0ZSBhc3luYyByZWZyZXNoQnJvd3NlclN0YXRlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuaXNTdXBwb3J0ZWQuc2V0KGF3YWl0IHRoaXMubWVzc2FnaW5nLmlzU3VwcG9ydGVkKCkpO1xuICAgIHRoaXMucGVybWlzc2lvbi5zZXQodGhpcy5hdXRoLmdldE5vdGlmaWNhdGlvblBlcm1pc3Npb25TdGF0ZSgpKTtcbiAgICB0aGlzLmN1cnJlbnRUb2tlbi5zZXQodGhpcy5tZXNzYWdpbmcuY3VycmVudFRva2VuID8/IG51bGwpO1xuICAgIHZvaWQgdGhpcy5yZXBhaXJUb2tlbklmTmVlZGVkKCk7XG4gIH1cblxuICAvKipcbiAgICogUmVwYXJhIGVsIHRva2VuIEZDTSBlbiBiYWNrZ3JvdW5kLlxuICAgKlxuICAgKiBTaSBlbCB1c3VhcmlvIHRpZW5lIHB1c2ggYWN0aXZvIChgbWFzdGVyICYmIGdyYW50ZWRgKSBwZXJvIG5vIGhheSB0b2tlbiBlblxuICAgKiBtZW1vcmlhIOKAlCBjYXNvIHTDrXBpY28gdHJhcyB1biBjb2xkIHN0YXJ0IGRlIGxhIFBXQSDigJQgcmUtb2J0ZW5lbW9zIGVsIHRva2VuXG4gICAqIHkgcmUtcmVnaXN0cmFtb3MgZWwgZGV2aWNlIHNpbiB0b2NhciBlbCB0b2dnbGUgbmkgbW9zdHJhciB1biBlcnJvclxuICAgKiBhbGFybWlzdGEuIEVsIHRva2VuIGVzIHVuIGRldGFsbGUgZGUgaW1wbGVtZW50YWNpw7NuIHJlcGFyYWJsZSwgbm8gcGFydGUgZGVsXG4gICAqIGVzdGFkbyBkZSBVSSAodmVyIGBpc0VuYWJsZWRgKS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVwYWlyVG9rZW5JZk5lZWRlZCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5fYnVzeSgpICE9PSBudWxsKSByZXR1cm47XG4gICAgaWYgKHRoaXMuX3JlcGFpcmluZ1Rva2VuKSByZXR1cm47XG4gICAgaWYgKCF0aGlzLmlzRW5hYmxlZCgpKSByZXR1cm47XG4gICAgaWYgKHRoaXMuY3VycmVudFRva2VuKCkpIHJldHVybjtcblxuICAgIHRoaXMuX3JlcGFpcmluZ1Rva2VuID0gdHJ1ZTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgdG9rZW4gPSBhd2FpdCB0aGlzLm1lc3NhZ2luZy5nZXRUb2tlbigpO1xuICAgICAgaWYgKHRva2VuKSB7XG4gICAgICAgIHRoaXMuY3VycmVudFRva2VuLnNldCh0b2tlbik7XG4gICAgICAgIGF3YWl0IHRoaXMuYXV0aC5yZWdpc3RlckRldmljZSh0b2tlbik7XG4gICAgICB9XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBSZXBhcmFjacOzbiBiZXN0LWVmZm9ydDogbm8gZGVncmFkYW1vcyBlbCB0b2dnbGUgbmkgbW9zdHJhbW9zIGVycm9yLlxuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLl9yZXBhaXJpbmdUb2tlbiA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBpT1MgYnJvd3NlciBzaW4gZXN0YXIgaW5zdGFsYWRvIGNvbW8gUFdBIOKAlCBwdXNoIHdlYiBubyBkaXNwb25pYmxlLlxuICAgKiBBcGxpY2EgYSBTYWZhcmkgeSBhIEVkZ2UvQ2hyb21lL0ZpcmVmb3ggZW4gaU9TICh0b2RvcyBXZWJLaXQgcG9yIG1hbmRhdG8gQXBwbGUpLlxuICAgKi9cbiAgcHJpdmF0ZSBkZXRlY3ROZWVkc0lPU0luc3RhbGwoKTogYm9vbGVhbiB7XG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgPT09ICd1bmRlZmluZWQnIHx8IHR5cGVvZiBuYXZpZ2F0b3IgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGNvbnN0IHVhID0gbmF2aWdhdG9yLnVzZXJBZ2VudCB8fCAnJztcbiAgICBjb25zdCBpc0lPUyA9IC9pUGFkfGlQaG9uZXxpUG9kLy50ZXN0KHVhKTtcbiAgICBpZiAoIWlzSU9TKSByZXR1cm4gZmFsc2U7XG4gICAgY29uc3QgaXNTdGFuZGFsb25lID1cbiAgICAgIHdpbmRvdy5tYXRjaE1lZGlhPy4oJyhkaXNwbGF5LW1vZGU6IHN0YW5kYWxvbmUpJykubWF0Y2hlcyB8fFxuICAgICAgKG5hdmlnYXRvciBhcyB1bmtub3duIGFzIHsgc3RhbmRhbG9uZT86IGJvb2xlYW4gfSkuc3RhbmRhbG9uZSA9PT0gdHJ1ZTtcbiAgICByZXR1cm4gIWlzU3RhbmRhbG9uZTtcbiAgfVxuXG4gIHByb3RlY3RlZCB0dChrZXk6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuaTE4bi50KGtleSwgdGhpcy5ucyk7XG4gIH1cbn1cbiJdfQ==
|