@atlasauth/angular 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/esm2022/lib/appearance.mjs +1 -1
- package/dist/esm2022/lib/atlas.service.mjs +39 -2
- package/dist/esm2022/lib/atlas.ui.mjs +3 -1
- package/dist/esm2022/lib/components/attempt-flow.component.mjs +384 -8
- package/dist/esm2022/lib/components/flow-controller.mjs +115 -2
- package/dist/esm2022/lib/components/impersonation-banner.component.mjs +75 -0
- package/dist/esm2022/lib/components/organization-audit-log.component.mjs +115 -0
- package/dist/esm2022/lib/components/organization-audit-log.mjs +40 -0
- package/dist/esm2022/lib/components/organization-profile.component.mjs +602 -27
- package/dist/esm2022/lib/components/recovery-codes.component.mjs +122 -0
- package/dist/esm2022/lib/components/sign-in.component.mjs +45 -4
- package/dist/esm2022/lib/components/sign-up-form.component.mjs +69 -6
- package/dist/esm2022/lib/components/sign-up.component.mjs +81 -7
- package/dist/esm2022/lib/i18n.mjs +58 -1
- package/dist/esm2022/lib/provide-atlas.mjs +3 -2
- package/dist/esm2022/lib/types.mjs +1 -1
- package/dist/esm2022/lib/version.mjs +2 -2
- package/dist/esm2022/public-api.mjs +4 -1
- package/dist/fesm2022/atlasauth-angular.mjs +1711 -46
- package/dist/fesm2022/atlasauth-angular.mjs.map +1 -1
- package/dist/lib/appearance.d.ts +1 -1
- package/dist/lib/atlas.service.d.ts +22 -1
- package/dist/lib/atlas.ui.d.ts +2 -1
- package/dist/lib/components/attempt-flow.component.d.ts +57 -2
- package/dist/lib/components/flow-controller.d.ts +64 -1
- package/dist/lib/components/impersonation-banner.component.d.ts +27 -0
- package/dist/lib/components/organization-audit-log.component.d.ts +34 -0
- package/dist/lib/components/organization-audit-log.d.ts +25 -0
- package/dist/lib/components/organization-profile.component.d.ts +55 -6
- package/dist/lib/components/recovery-codes.component.d.ts +26 -0
- package/dist/lib/components/sign-in.component.d.ts +2 -0
- package/dist/lib/components/sign-up-form.component.d.ts +11 -1
- package/dist/lib/components/sign-up.component.d.ts +13 -0
- package/dist/lib/provide-atlas.d.ts +2 -1
- package/dist/lib/types.d.ts +6 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/public-api.d.ts +4 -0
- package/package.json +3 -3
|
@@ -1,36 +1,396 @@
|
|
|
1
|
-
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
|
1
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, EventEmitter, Output, computed, effect, inject, signal, untracked, } from '@angular/core';
|
|
2
|
+
import { OrganizationProfileController, localizedErrorMessage, } from '@atlasauth/js';
|
|
2
3
|
import { AtlasService } from '../atlas.service';
|
|
3
|
-
import { AtlasProtectDirective } from '../directives/protect.directive';
|
|
4
4
|
import { classFor } from '../appearance';
|
|
5
5
|
import { translate } from '../i18n';
|
|
6
6
|
import * as i0 from "@angular/core";
|
|
7
7
|
/**
|
|
8
|
-
* `<atlas-organization-profile>` — the active organization's
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* `<atlas-organization-profile>` — the active organization's members (admins
|
|
9
|
+
* change roles and remove), invitations (admins invite, revoke and resend) and
|
|
10
|
+
* settings (leave; an admin deletes after confirming their password). The
|
|
11
|
+
* Angular peer of React's `<OrganizationProfile/>`, on the same headless
|
|
12
|
+
* controller. Every control is gated on the ACTIVE org role, matching the
|
|
13
|
+
* server's rule. Renders nothing with no active org. Emits `exit` with `left`
|
|
14
|
+
* or `deleted`.
|
|
12
15
|
*/
|
|
13
16
|
export class OrganizationProfileComponent {
|
|
14
17
|
atlas = inject(AtlasService);
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
exit = new EventEmitter();
|
|
19
|
+
client = this.atlas.createFapiClient();
|
|
20
|
+
controller = null;
|
|
21
|
+
unsubscribe = null;
|
|
22
|
+
state = signal(null);
|
|
23
|
+
tab = signal('members');
|
|
24
|
+
email = signal('');
|
|
25
|
+
inviteRole = signal('org:member');
|
|
26
|
+
confirm = signal(null);
|
|
27
|
+
needsPassword = signal(false);
|
|
28
|
+
password = signal('');
|
|
29
|
+
// The custom-role editor: null = creating, else the id of the role being edited.
|
|
30
|
+
editingRole = signal(null);
|
|
31
|
+
roleKey = signal('');
|
|
32
|
+
roleName = signal('');
|
|
33
|
+
rolePerms = signal([]);
|
|
34
|
+
customRoles = computed(() => (this.state()?.roles ?? []).filter((r) => Boolean(r.organization_id)));
|
|
35
|
+
userId = computed(() => this.atlas.claims()?.sub ?? null);
|
|
36
|
+
canManage = computed(() => {
|
|
37
|
+
this.atlas.claims();
|
|
38
|
+
return this.atlas.has({ permission: 'org:sys_memberships:manage' });
|
|
39
|
+
});
|
|
40
|
+
isAdmin = computed(() => this.atlas.organization()?.role === 'org:admin');
|
|
41
|
+
tabs = computed(() => ['members', ...(this.canManage() ? ['invitations', 'roles'] : []), 'settings']);
|
|
42
|
+
roles = computed(() => {
|
|
43
|
+
const roles = this.state()?.roles ?? [];
|
|
44
|
+
return roles.length > 0 ? roles : [{ key: 'org:member', name: 'org:member' }];
|
|
45
|
+
});
|
|
46
|
+
errorMessage = computed(() => {
|
|
47
|
+
const err = this.state()?.error;
|
|
48
|
+
if (!err || err.needsReverification)
|
|
49
|
+
return null;
|
|
50
|
+
return localizedErrorMessage(err, this.atlas.errorLocalization, this.atlas.localization);
|
|
51
|
+
});
|
|
52
|
+
constructor() {
|
|
53
|
+
effect(() => {
|
|
54
|
+
const id = this.atlas.organization()?.organization.id ?? null;
|
|
55
|
+
untracked(() => this.bind(id));
|
|
56
|
+
});
|
|
57
|
+
inject(DestroyRef).onDestroy(() => this.unsubscribe?.());
|
|
58
|
+
}
|
|
59
|
+
bind(id) {
|
|
60
|
+
if (this.controller?.organizationId === id)
|
|
61
|
+
return;
|
|
62
|
+
this.unsubscribe?.();
|
|
63
|
+
this.controller = null;
|
|
64
|
+
this.state.set(null);
|
|
65
|
+
if (!id)
|
|
66
|
+
return;
|
|
67
|
+
const controller = new OrganizationProfileController(this.client, id, {
|
|
68
|
+
canManage: this.canManage(),
|
|
69
|
+
onChange: async () => {
|
|
70
|
+
await this.atlas.reload();
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
this.controller = controller;
|
|
74
|
+
this.unsubscribe = controller.subscribe((next) => this.state.set(next));
|
|
75
|
+
this.state.set(controller.getState());
|
|
76
|
+
void controller.load();
|
|
77
|
+
}
|
|
78
|
+
t(key, vars) {
|
|
79
|
+
return translate(this.atlas.localization, key, vars);
|
|
17
80
|
}
|
|
18
81
|
cls(key, base) {
|
|
19
82
|
return classFor(key, this.atlas.appearance, base);
|
|
20
83
|
}
|
|
84
|
+
memberName(m) {
|
|
85
|
+
return [m.user?.first_name, m.user?.last_name].filter(Boolean).join(' ') || this.t('organization.unnamedMember');
|
|
86
|
+
}
|
|
87
|
+
changeRole(userId, role) {
|
|
88
|
+
void this.controller?.changeRole(userId, role);
|
|
89
|
+
}
|
|
90
|
+
removeMember(userId) {
|
|
91
|
+
void this.controller?.removeMember(userId);
|
|
92
|
+
}
|
|
93
|
+
async sendInvite() {
|
|
94
|
+
const address = this.email().trim();
|
|
95
|
+
if (this.controller && address && (await this.controller.invite(address, this.inviteRole())))
|
|
96
|
+
this.email.set('');
|
|
97
|
+
}
|
|
98
|
+
resend(id) {
|
|
99
|
+
void this.controller?.resendInvitation(id);
|
|
100
|
+
}
|
|
101
|
+
revoke(id) {
|
|
102
|
+
void this.controller?.revokeInvitation(id);
|
|
103
|
+
}
|
|
104
|
+
async leave() {
|
|
105
|
+
if (this.controller && (await this.controller.leave()))
|
|
106
|
+
this.exit.emit('left');
|
|
107
|
+
this.confirm.set(null);
|
|
108
|
+
}
|
|
109
|
+
async remove() {
|
|
110
|
+
const controller = this.controller;
|
|
111
|
+
if (!controller)
|
|
112
|
+
return;
|
|
113
|
+
if (this.needsPassword()) {
|
|
114
|
+
try {
|
|
115
|
+
await this.atlas.reauthenticate(this.password());
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.password.set('');
|
|
121
|
+
this.needsPassword.set(false);
|
|
122
|
+
}
|
|
123
|
+
if (await controller.deleteOrganization()) {
|
|
124
|
+
this.exit.emit('deleted');
|
|
125
|
+
this.confirm.set(null);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const err = controller.getState().error;
|
|
129
|
+
if (err?.needsReverification)
|
|
130
|
+
this.needsPassword.set(true);
|
|
131
|
+
}
|
|
132
|
+
editRole(role) {
|
|
133
|
+
this.editingRole.set(role.id ?? null);
|
|
134
|
+
this.roleKey.set(role.key);
|
|
135
|
+
this.roleName.set(role.name);
|
|
136
|
+
this.rolePerms.set([...(role.permissions ?? [])]);
|
|
137
|
+
}
|
|
138
|
+
resetRoleForm() {
|
|
139
|
+
this.editingRole.set(null);
|
|
140
|
+
this.roleKey.set('');
|
|
141
|
+
this.roleName.set('');
|
|
142
|
+
this.rolePerms.set([]);
|
|
143
|
+
}
|
|
144
|
+
togglePermission(key, on) {
|
|
145
|
+
this.rolePerms.update((current) => (on ? [...current, key] : current.filter((k) => k !== key)));
|
|
146
|
+
}
|
|
147
|
+
async saveRole() {
|
|
148
|
+
const controller = this.controller;
|
|
149
|
+
if (!controller)
|
|
150
|
+
return;
|
|
151
|
+
const editing = this.editingRole();
|
|
152
|
+
const out = editing
|
|
153
|
+
? await controller.updateRole(editing, { name: this.roleName().trim(), permissions: this.rolePerms() })
|
|
154
|
+
: await controller.createRole({ key: this.roleKey().trim(), name: this.roleName().trim(), permissions: this.rolePerms() });
|
|
155
|
+
if (out)
|
|
156
|
+
this.resetRoleForm();
|
|
157
|
+
}
|
|
158
|
+
deleteRole(role) {
|
|
159
|
+
if (role.id)
|
|
160
|
+
void this.controller?.deleteRole(role.id);
|
|
161
|
+
}
|
|
162
|
+
cancel() {
|
|
163
|
+
this.confirm.set(null);
|
|
164
|
+
this.needsPassword.set(false);
|
|
165
|
+
this.password.set('');
|
|
166
|
+
}
|
|
21
167
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OrganizationProfileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
22
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: OrganizationProfileComponent, isStandalone: true, selector: "atlas-organization-profile", ngImport: i0, template: `
|
|
168
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: OrganizationProfileComponent, isStandalone: true, selector: "atlas-organization-profile", outputs: { exit: "exit" }, ngImport: i0, template: `
|
|
23
169
|
@if (atlas.organization(); as membership) {
|
|
24
|
-
|
|
25
|
-
<
|
|
26
|
-
|
|
170
|
+
@if (state(); as st) {
|
|
171
|
+
<div [class]="cls('card', 'atlas-card')" data-atlas="organization-profile">
|
|
172
|
+
<h1>{{ membership.organization.name }}</h1>
|
|
173
|
+
<p>{{ t('organization.roleLabel') }}: {{ membership.role }}</p>
|
|
174
|
+
|
|
175
|
+
<div [class]="cls('menu', 'atlas-menu')" role="tablist">
|
|
176
|
+
@for (key of tabs(); track key) {
|
|
177
|
+
<button
|
|
178
|
+
type="button"
|
|
179
|
+
role="tab"
|
|
180
|
+
[attr.aria-selected]="tab() === key"
|
|
181
|
+
[class]="cls('menuItem', 'atlas-menu-item')"
|
|
182
|
+
[attr.data-atlas]="'organization-tab-' + key"
|
|
183
|
+
(click)="tab.set(key)"
|
|
184
|
+
>
|
|
185
|
+
{{ t('organization.' + key) }}
|
|
186
|
+
</button>
|
|
187
|
+
}
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
@if (errorMessage(); as message) {
|
|
191
|
+
<p [class]="cls('formFieldError', 'atlas-error')" role="alert">{{ message }}</p>
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@if (tab() === 'members') {
|
|
195
|
+
<ul class="atlas-list" data-atlas="organization-members">
|
|
196
|
+
@for (m of st.members; track m.id) {
|
|
197
|
+
<li data-atlas="organization-member">
|
|
198
|
+
<span>{{ memberName(m) }}{{ m.user?.id === userId() ? ' ' + t('organization.you') : '' }}</span>
|
|
199
|
+
@if (canManage() && m.user && m.user.id !== userId()) {
|
|
200
|
+
<select
|
|
201
|
+
[attr.aria-label]="t('organization.roleLabel')"
|
|
202
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
203
|
+
[disabled]="st.busy === 'role:' + m.user.id"
|
|
204
|
+
(change)="changeRole(m.user.id, $any($event.target).value)"
|
|
205
|
+
>
|
|
206
|
+
@for (r of roles(); track r.key) {
|
|
207
|
+
<option [value]="r.key" [selected]="r.key === m.role">{{ r.name }}</option>
|
|
208
|
+
}
|
|
209
|
+
</select>
|
|
210
|
+
<button
|
|
211
|
+
type="button"
|
|
212
|
+
[class]="cls('formButtonSecondary', 'atlas-button-secondary')"
|
|
213
|
+
[disabled]="st.busy === 'remove:' + m.user.id"
|
|
214
|
+
(click)="removeMember(m.user.id)"
|
|
215
|
+
>
|
|
216
|
+
{{ t('organization.remove') }}
|
|
217
|
+
</button>
|
|
218
|
+
} @else {
|
|
219
|
+
<span> · {{ m.role }}</span>
|
|
220
|
+
}
|
|
221
|
+
</li>
|
|
222
|
+
}
|
|
223
|
+
</ul>
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
@if (tab() === 'invitations' && canManage()) {
|
|
227
|
+
<div data-atlas="organization-invitations">
|
|
228
|
+
<form (submit)="$event.preventDefault(); sendInvite()">
|
|
229
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
230
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.emailLabel') }}</span>
|
|
231
|
+
<input
|
|
232
|
+
type="email"
|
|
233
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
234
|
+
[value]="email()"
|
|
235
|
+
(input)="email.set($any($event.target).value)"
|
|
236
|
+
/>
|
|
237
|
+
</label>
|
|
238
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
239
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleLabel') }}</span>
|
|
240
|
+
<select [class]="cls('formFieldInput', 'atlas-input')" (change)="inviteRole.set($any($event.target).value)">
|
|
241
|
+
@for (r of roles(); track r.key) {
|
|
242
|
+
<option [value]="r.key" [selected]="r.key === inviteRole()">{{ r.name }}</option>
|
|
243
|
+
}
|
|
244
|
+
</select>
|
|
245
|
+
</label>
|
|
246
|
+
<button
|
|
247
|
+
type="submit"
|
|
248
|
+
[class]="cls('formButtonPrimary', 'atlas-button')"
|
|
249
|
+
[disabled]="st.busy === 'invite' || !email().trim()"
|
|
250
|
+
>
|
|
251
|
+
{{ t('organization.sendInvite') }}
|
|
252
|
+
</button>
|
|
253
|
+
</form>
|
|
254
|
+
@if (st.invitations.length === 0) {
|
|
255
|
+
<p class="atlas-text">{{ t('organization.noInvitations') }}</p>
|
|
256
|
+
} @else {
|
|
257
|
+
<ul class="atlas-list">
|
|
258
|
+
@for (inv of st.invitations; track inv.id) {
|
|
259
|
+
<li data-atlas="organization-invitation">
|
|
260
|
+
<span>{{ inv.email_address }} · {{ inv.role }}</span>
|
|
261
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="resend(inv.id)">
|
|
262
|
+
{{ t('organization.resend') }}
|
|
263
|
+
</button>
|
|
264
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="revoke(inv.id)">
|
|
265
|
+
{{ t('organization.revoke') }}
|
|
266
|
+
</button>
|
|
267
|
+
</li>
|
|
268
|
+
}
|
|
269
|
+
</ul>
|
|
270
|
+
}
|
|
271
|
+
</div>
|
|
272
|
+
}
|
|
27
273
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
274
|
+
@if (tab() === 'roles' && canManage()) {
|
|
275
|
+
<div data-atlas="organization-roles">
|
|
276
|
+
@if (customRoles().length === 0) {
|
|
277
|
+
<p class="atlas-text">{{ t('organization.noCustomRoles') }}</p>
|
|
278
|
+
} @else {
|
|
279
|
+
<ul class="atlas-list">
|
|
280
|
+
@for (role of customRoles(); track role.key) {
|
|
281
|
+
<li data-atlas="organization-role">
|
|
282
|
+
<span>{{ role.name }} · {{ role.key }}</span>
|
|
283
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="editRole(role)">
|
|
284
|
+
{{ t('organization.editRole') }}
|
|
285
|
+
</button>
|
|
286
|
+
<button
|
|
287
|
+
type="button"
|
|
288
|
+
[class]="cls('formButtonSecondary', 'atlas-button-secondary')"
|
|
289
|
+
[disabled]="!role.id || st.busy === 'role:delete:' + role.id"
|
|
290
|
+
(click)="deleteRole(role)"
|
|
291
|
+
>
|
|
292
|
+
{{ t('organization.deleteRole') }}
|
|
293
|
+
</button>
|
|
294
|
+
</li>
|
|
295
|
+
}
|
|
296
|
+
</ul>
|
|
297
|
+
}
|
|
298
|
+
<form data-atlas="organization-role-form" (submit)="$event.preventDefault(); saveRole()">
|
|
299
|
+
@if (!editingRole()) {
|
|
300
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
301
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleKey') }}</span>
|
|
302
|
+
<input
|
|
303
|
+
name="role_key"
|
|
304
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
305
|
+
[value]="roleKey()"
|
|
306
|
+
(input)="roleKey.set($any($event.target).value)"
|
|
307
|
+
/>
|
|
308
|
+
</label>
|
|
309
|
+
}
|
|
310
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
311
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleName') }}</span>
|
|
312
|
+
<input
|
|
313
|
+
name="role_name"
|
|
314
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
315
|
+
[value]="roleName()"
|
|
316
|
+
(input)="roleName.set($any($event.target).value)"
|
|
317
|
+
/>
|
|
318
|
+
</label>
|
|
319
|
+
<fieldset [class]="cls('formField', 'atlas-field')">
|
|
320
|
+
<legend [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.rolePermissions') }}</legend>
|
|
321
|
+
@for (p of st.permissions; track p.key) {
|
|
322
|
+
<label data-atlas="organization-role-permission">
|
|
323
|
+
<input
|
|
324
|
+
type="checkbox"
|
|
325
|
+
[value]="p.key"
|
|
326
|
+
[checked]="rolePerms().includes(p.key)"
|
|
327
|
+
(change)="togglePermission(p.key, $any($event.target).checked)"
|
|
328
|
+
/>
|
|
329
|
+
{{ p.name }}
|
|
330
|
+
</label>
|
|
331
|
+
}
|
|
332
|
+
</fieldset>
|
|
333
|
+
<button
|
|
334
|
+
type="submit"
|
|
335
|
+
[class]="cls('formButtonPrimary', 'atlas-button')"
|
|
336
|
+
[disabled]="!roleName().trim() || (!editingRole() && !roleKey().trim()) || (st.busy ?? '').startsWith('role:')"
|
|
337
|
+
>
|
|
338
|
+
{{ editingRole() ? t('organization.saveRole') : t('organization.createRole') }}
|
|
339
|
+
</button>
|
|
340
|
+
@if (editingRole()) {
|
|
341
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="resetRoleForm()">
|
|
342
|
+
{{ t('organization.cancel') }}
|
|
343
|
+
</button>
|
|
344
|
+
}
|
|
345
|
+
</form>
|
|
346
|
+
</div>
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@if (tab() === 'settings') {
|
|
350
|
+
<div data-atlas="organization-settings">
|
|
351
|
+
@if (confirm(); as action) {
|
|
352
|
+
<p class="atlas-text">
|
|
353
|
+
{{ t(action === 'leave' ? 'organization.leaveConfirm' : 'organization.deleteConfirm', { name: membership.organization.name }) }}
|
|
354
|
+
</p>
|
|
355
|
+
@if (needsPassword()) {
|
|
356
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
357
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.passwordPrompt') }}</span>
|
|
358
|
+
<input
|
|
359
|
+
type="password"
|
|
360
|
+
autocomplete="current-password"
|
|
361
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
362
|
+
[value]="password()"
|
|
363
|
+
(input)="password.set($any($event.target).value)"
|
|
364
|
+
/>
|
|
365
|
+
</label>
|
|
366
|
+
}
|
|
367
|
+
<button
|
|
368
|
+
type="button"
|
|
369
|
+
[class]="cls('formButtonPrimary', 'atlas-button')"
|
|
370
|
+
[disabled]="st.busy === 'leave' || st.busy === 'delete'"
|
|
371
|
+
(click)="action === 'leave' ? leave() : remove()"
|
|
372
|
+
>
|
|
373
|
+
{{ t('organization.confirm') }}
|
|
374
|
+
</button>
|
|
375
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="cancel()">
|
|
376
|
+
{{ t('organization.cancel') }}
|
|
377
|
+
</button>
|
|
378
|
+
} @else {
|
|
379
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="confirm.set('leave')">
|
|
380
|
+
{{ t('organization.leave') }}
|
|
381
|
+
</button>
|
|
382
|
+
@if (isAdmin()) {
|
|
383
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="confirm.set('delete')">
|
|
384
|
+
{{ t('organization.delete') }}
|
|
385
|
+
</button>
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
</div>
|
|
389
|
+
}
|
|
390
|
+
</div>
|
|
391
|
+
}
|
|
32
392
|
}
|
|
33
|
-
`, isInline: true,
|
|
393
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34
394
|
}
|
|
35
395
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OrganizationProfileComponent, decorators: [{
|
|
36
396
|
type: Component,
|
|
@@ -38,19 +398,234 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
38
398
|
selector: 'atlas-organization-profile',
|
|
39
399
|
standalone: true,
|
|
40
400
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
41
|
-
imports: [AtlasProtectDirective],
|
|
42
401
|
template: `
|
|
43
402
|
@if (atlas.organization(); as membership) {
|
|
44
|
-
|
|
45
|
-
<
|
|
46
|
-
|
|
403
|
+
@if (state(); as st) {
|
|
404
|
+
<div [class]="cls('card', 'atlas-card')" data-atlas="organization-profile">
|
|
405
|
+
<h1>{{ membership.organization.name }}</h1>
|
|
406
|
+
<p>{{ t('organization.roleLabel') }}: {{ membership.role }}</p>
|
|
407
|
+
|
|
408
|
+
<div [class]="cls('menu', 'atlas-menu')" role="tablist">
|
|
409
|
+
@for (key of tabs(); track key) {
|
|
410
|
+
<button
|
|
411
|
+
type="button"
|
|
412
|
+
role="tab"
|
|
413
|
+
[attr.aria-selected]="tab() === key"
|
|
414
|
+
[class]="cls('menuItem', 'atlas-menu-item')"
|
|
415
|
+
[attr.data-atlas]="'organization-tab-' + key"
|
|
416
|
+
(click)="tab.set(key)"
|
|
417
|
+
>
|
|
418
|
+
{{ t('organization.' + key) }}
|
|
419
|
+
</button>
|
|
420
|
+
}
|
|
421
|
+
</div>
|
|
422
|
+
|
|
423
|
+
@if (errorMessage(); as message) {
|
|
424
|
+
<p [class]="cls('formFieldError', 'atlas-error')" role="alert">{{ message }}</p>
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
@if (tab() === 'members') {
|
|
428
|
+
<ul class="atlas-list" data-atlas="organization-members">
|
|
429
|
+
@for (m of st.members; track m.id) {
|
|
430
|
+
<li data-atlas="organization-member">
|
|
431
|
+
<span>{{ memberName(m) }}{{ m.user?.id === userId() ? ' ' + t('organization.you') : '' }}</span>
|
|
432
|
+
@if (canManage() && m.user && m.user.id !== userId()) {
|
|
433
|
+
<select
|
|
434
|
+
[attr.aria-label]="t('organization.roleLabel')"
|
|
435
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
436
|
+
[disabled]="st.busy === 'role:' + m.user.id"
|
|
437
|
+
(change)="changeRole(m.user.id, $any($event.target).value)"
|
|
438
|
+
>
|
|
439
|
+
@for (r of roles(); track r.key) {
|
|
440
|
+
<option [value]="r.key" [selected]="r.key === m.role">{{ r.name }}</option>
|
|
441
|
+
}
|
|
442
|
+
</select>
|
|
443
|
+
<button
|
|
444
|
+
type="button"
|
|
445
|
+
[class]="cls('formButtonSecondary', 'atlas-button-secondary')"
|
|
446
|
+
[disabled]="st.busy === 'remove:' + m.user.id"
|
|
447
|
+
(click)="removeMember(m.user.id)"
|
|
448
|
+
>
|
|
449
|
+
{{ t('organization.remove') }}
|
|
450
|
+
</button>
|
|
451
|
+
} @else {
|
|
452
|
+
<span> · {{ m.role }}</span>
|
|
453
|
+
}
|
|
454
|
+
</li>
|
|
455
|
+
}
|
|
456
|
+
</ul>
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
@if (tab() === 'invitations' && canManage()) {
|
|
460
|
+
<div data-atlas="organization-invitations">
|
|
461
|
+
<form (submit)="$event.preventDefault(); sendInvite()">
|
|
462
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
463
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.emailLabel') }}</span>
|
|
464
|
+
<input
|
|
465
|
+
type="email"
|
|
466
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
467
|
+
[value]="email()"
|
|
468
|
+
(input)="email.set($any($event.target).value)"
|
|
469
|
+
/>
|
|
470
|
+
</label>
|
|
471
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
472
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleLabel') }}</span>
|
|
473
|
+
<select [class]="cls('formFieldInput', 'atlas-input')" (change)="inviteRole.set($any($event.target).value)">
|
|
474
|
+
@for (r of roles(); track r.key) {
|
|
475
|
+
<option [value]="r.key" [selected]="r.key === inviteRole()">{{ r.name }}</option>
|
|
476
|
+
}
|
|
477
|
+
</select>
|
|
478
|
+
</label>
|
|
479
|
+
<button
|
|
480
|
+
type="submit"
|
|
481
|
+
[class]="cls('formButtonPrimary', 'atlas-button')"
|
|
482
|
+
[disabled]="st.busy === 'invite' || !email().trim()"
|
|
483
|
+
>
|
|
484
|
+
{{ t('organization.sendInvite') }}
|
|
485
|
+
</button>
|
|
486
|
+
</form>
|
|
487
|
+
@if (st.invitations.length === 0) {
|
|
488
|
+
<p class="atlas-text">{{ t('organization.noInvitations') }}</p>
|
|
489
|
+
} @else {
|
|
490
|
+
<ul class="atlas-list">
|
|
491
|
+
@for (inv of st.invitations; track inv.id) {
|
|
492
|
+
<li data-atlas="organization-invitation">
|
|
493
|
+
<span>{{ inv.email_address }} · {{ inv.role }}</span>
|
|
494
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="resend(inv.id)">
|
|
495
|
+
{{ t('organization.resend') }}
|
|
496
|
+
</button>
|
|
497
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="revoke(inv.id)">
|
|
498
|
+
{{ t('organization.revoke') }}
|
|
499
|
+
</button>
|
|
500
|
+
</li>
|
|
501
|
+
}
|
|
502
|
+
</ul>
|
|
503
|
+
}
|
|
504
|
+
</div>
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
@if (tab() === 'roles' && canManage()) {
|
|
508
|
+
<div data-atlas="organization-roles">
|
|
509
|
+
@if (customRoles().length === 0) {
|
|
510
|
+
<p class="atlas-text">{{ t('organization.noCustomRoles') }}</p>
|
|
511
|
+
} @else {
|
|
512
|
+
<ul class="atlas-list">
|
|
513
|
+
@for (role of customRoles(); track role.key) {
|
|
514
|
+
<li data-atlas="organization-role">
|
|
515
|
+
<span>{{ role.name }} · {{ role.key }}</span>
|
|
516
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="editRole(role)">
|
|
517
|
+
{{ t('organization.editRole') }}
|
|
518
|
+
</button>
|
|
519
|
+
<button
|
|
520
|
+
type="button"
|
|
521
|
+
[class]="cls('formButtonSecondary', 'atlas-button-secondary')"
|
|
522
|
+
[disabled]="!role.id || st.busy === 'role:delete:' + role.id"
|
|
523
|
+
(click)="deleteRole(role)"
|
|
524
|
+
>
|
|
525
|
+
{{ t('organization.deleteRole') }}
|
|
526
|
+
</button>
|
|
527
|
+
</li>
|
|
528
|
+
}
|
|
529
|
+
</ul>
|
|
530
|
+
}
|
|
531
|
+
<form data-atlas="organization-role-form" (submit)="$event.preventDefault(); saveRole()">
|
|
532
|
+
@if (!editingRole()) {
|
|
533
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
534
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleKey') }}</span>
|
|
535
|
+
<input
|
|
536
|
+
name="role_key"
|
|
537
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
538
|
+
[value]="roleKey()"
|
|
539
|
+
(input)="roleKey.set($any($event.target).value)"
|
|
540
|
+
/>
|
|
541
|
+
</label>
|
|
542
|
+
}
|
|
543
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
544
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.roleName') }}</span>
|
|
545
|
+
<input
|
|
546
|
+
name="role_name"
|
|
547
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
548
|
+
[value]="roleName()"
|
|
549
|
+
(input)="roleName.set($any($event.target).value)"
|
|
550
|
+
/>
|
|
551
|
+
</label>
|
|
552
|
+
<fieldset [class]="cls('formField', 'atlas-field')">
|
|
553
|
+
<legend [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.rolePermissions') }}</legend>
|
|
554
|
+
@for (p of st.permissions; track p.key) {
|
|
555
|
+
<label data-atlas="organization-role-permission">
|
|
556
|
+
<input
|
|
557
|
+
type="checkbox"
|
|
558
|
+
[value]="p.key"
|
|
559
|
+
[checked]="rolePerms().includes(p.key)"
|
|
560
|
+
(change)="togglePermission(p.key, $any($event.target).checked)"
|
|
561
|
+
/>
|
|
562
|
+
{{ p.name }}
|
|
563
|
+
</label>
|
|
564
|
+
}
|
|
565
|
+
</fieldset>
|
|
566
|
+
<button
|
|
567
|
+
type="submit"
|
|
568
|
+
[class]="cls('formButtonPrimary', 'atlas-button')"
|
|
569
|
+
[disabled]="!roleName().trim() || (!editingRole() && !roleKey().trim()) || (st.busy ?? '').startsWith('role:')"
|
|
570
|
+
>
|
|
571
|
+
{{ editingRole() ? t('organization.saveRole') : t('organization.createRole') }}
|
|
572
|
+
</button>
|
|
573
|
+
@if (editingRole()) {
|
|
574
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="resetRoleForm()">
|
|
575
|
+
{{ t('organization.cancel') }}
|
|
576
|
+
</button>
|
|
577
|
+
}
|
|
578
|
+
</form>
|
|
579
|
+
</div>
|
|
580
|
+
}
|
|
47
581
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
582
|
+
@if (tab() === 'settings') {
|
|
583
|
+
<div data-atlas="organization-settings">
|
|
584
|
+
@if (confirm(); as action) {
|
|
585
|
+
<p class="atlas-text">
|
|
586
|
+
{{ t(action === 'leave' ? 'organization.leaveConfirm' : 'organization.deleteConfirm', { name: membership.organization.name }) }}
|
|
587
|
+
</p>
|
|
588
|
+
@if (needsPassword()) {
|
|
589
|
+
<label [class]="cls('formField', 'atlas-field')">
|
|
590
|
+
<span [class]="cls('formFieldLabel', 'atlas-field-label')">{{ t('organization.passwordPrompt') }}</span>
|
|
591
|
+
<input
|
|
592
|
+
type="password"
|
|
593
|
+
autocomplete="current-password"
|
|
594
|
+
[class]="cls('formFieldInput', 'atlas-input')"
|
|
595
|
+
[value]="password()"
|
|
596
|
+
(input)="password.set($any($event.target).value)"
|
|
597
|
+
/>
|
|
598
|
+
</label>
|
|
599
|
+
}
|
|
600
|
+
<button
|
|
601
|
+
type="button"
|
|
602
|
+
[class]="cls('formButtonPrimary', 'atlas-button')"
|
|
603
|
+
[disabled]="st.busy === 'leave' || st.busy === 'delete'"
|
|
604
|
+
(click)="action === 'leave' ? leave() : remove()"
|
|
605
|
+
>
|
|
606
|
+
{{ t('organization.confirm') }}
|
|
607
|
+
</button>
|
|
608
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="cancel()">
|
|
609
|
+
{{ t('organization.cancel') }}
|
|
610
|
+
</button>
|
|
611
|
+
} @else {
|
|
612
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="confirm.set('leave')">
|
|
613
|
+
{{ t('organization.leave') }}
|
|
614
|
+
</button>
|
|
615
|
+
@if (isAdmin()) {
|
|
616
|
+
<button type="button" [class]="cls('formButtonSecondary', 'atlas-button-secondary')" (click)="confirm.set('delete')">
|
|
617
|
+
{{ t('organization.delete') }}
|
|
618
|
+
</button>
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
</div>
|
|
622
|
+
}
|
|
623
|
+
</div>
|
|
624
|
+
}
|
|
52
625
|
}
|
|
53
626
|
`,
|
|
54
627
|
}]
|
|
55
|
-
}]
|
|
56
|
-
|
|
628
|
+
}], ctorParameters: () => [], propDecorators: { exit: [{
|
|
629
|
+
type: Output
|
|
630
|
+
}] } });
|
|
631
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JnYW5pemF0aW9uLXByb2ZpbGUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL29yZ2FuaXphdGlvbi1wcm9maWxlLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFDVCxVQUFVLEVBQ1YsWUFBWSxFQUNaLE1BQU0sRUFDTixRQUFRLEVBQ1IsTUFBTSxFQUNOLE1BQU0sRUFDTixNQUFNLEVBQ04sU0FBUyxHQUNWLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFDTCw2QkFBNkIsRUFDN0IscUJBQXFCLEdBR3RCLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUsUUFBUSxFQUFtQixNQUFNLGVBQWUsQ0FBQztBQUMxRCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sU0FBUyxDQUFDOztBQUtwQzs7Ozs7Ozs7R0FRRztBQXdPSCxNQUFNLE9BQU8sNEJBQTRCO0lBQ3BCLEtBQUssR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDN0IsSUFBSSxHQUFHLElBQUksWUFBWSxFQUFzQixDQUFDO0lBRWhELE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGdCQUFnQixFQUFFLENBQUM7SUFDaEQsVUFBVSxHQUF5QyxJQUFJLENBQUM7SUFDeEQsV0FBVyxHQUF3QixJQUFJLENBQUM7SUFFN0IsS0FBSyxHQUFHLE1BQU0sQ0FBa0MsSUFBSSxDQUFDLENBQUM7SUFDdEQsR0FBRyxHQUFHLE1BQU0sQ0FBTSxTQUFTLENBQUMsQ0FBQztJQUM3QixLQUFLLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ25CLFVBQVUsR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDbEMsT0FBTyxHQUFHLE1BQU0sQ0FBNEIsSUFBSSxDQUFDLENBQUM7SUFDbEQsYUFBYSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM5QixRQUFRLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3pDLGlGQUFpRjtJQUM5RCxXQUFXLEdBQUcsTUFBTSxDQUFnQixJQUFJLENBQUMsQ0FBQztJQUMxQyxPQUFPLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3JCLFFBQVEsR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDdEIsU0FBUyxHQUFHLE1BQU0sQ0FBVyxFQUFFLENBQUMsQ0FBQztJQUNqQyxXQUFXLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRXBHLE1BQU0sR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsRUFBRSxHQUEwQixJQUFJLElBQUksQ0FBQyxDQUFDO0lBQ2xGLFNBQVMsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQzNDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDcEIsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLFVBQVUsRUFBRSw0QkFBNEIsRUFBRSxDQUFDLENBQUM7SUFDdEUsQ0FBQyxDQUFDLENBQUM7SUFDZ0IsT0FBTyxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFlBQVksRUFBRSxFQUFFLElBQUksS0FBSyxXQUFXLENBQUMsQ0FBQztJQUMxRSxJQUFJLEdBQUcsUUFBUSxDQUFRLEdBQUcsRUFBRSxDQUFDLENBQUMsU0FBUyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFFLENBQUMsYUFBYSxFQUFFLE9BQU8sQ0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxVQUFVLENBQUMsQ0FBQyxDQUFDO0lBQ3hILEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ3ZDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsRUFBRSxLQUFLLElBQUksRUFBRSxDQUFDO1FBQ3hDLE9BQU8sS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxDQUFDLENBQUM7SUFDaEYsQ0FBQyxDQUFDLENBQUM7SUFDZ0IsWUFBWSxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUU7UUFDOUMsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssRUFBRSxFQUFFLEtBQUssQ0FBQztRQUNoQyxJQUFJLENBQUMsR0FBRyxJQUFLLEdBQWdDLENBQUMsbUJBQW1CO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFDL0UsT0FBTyxxQkFBcUIsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxpQkFBaUIsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQzNGLENBQUMsQ0FBQyxDQUFDO0lBRUg7UUFDRSxNQUFNLENBQUMsR0FBRyxFQUFFO1lBQ1YsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQUUsRUFBRSxZQUFZLENBQUMsRUFBRSxJQUFJLElBQUksQ0FBQztZQUM5RCxTQUFTLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ2pDLENBQUMsQ0FBQyxDQUFDO1FBQ0gsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFFTyxJQUFJLENBQUMsRUFBaUI7UUFDNUIsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLGNBQWMsS0FBSyxFQUFFO1lBQUUsT0FBTztRQUNuRCxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQztRQUNyQixJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQztRQUN2QixJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsRUFBRTtZQUFFLE9BQU87UUFDaEIsTUFBTSxVQUFVLEdBQUcsSUFBSSw2QkFBNkIsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEVBQUUsRUFBRTtZQUNwRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRTtZQUMzQixRQUFRLEVBQUUsS0FBSyxJQUFJLEVBQUU7Z0JBQ25CLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUM1QixDQUFDO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7UUFDN0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3hFLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQ3RDLEtBQUssVUFBVSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFUyxDQUFDLENBQUMsR0FBVyxFQUFFLElBQTZCO1FBQ3BELE9BQU8sU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRVMsR0FBRyxDQUFDLEdBQWUsRUFBRSxJQUFZO1FBQ3pDLE9BQU8sUUFBUSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBRVMsVUFBVSxDQUFDLENBQThDO1FBQ2pFLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsSUFBSSxFQUFFLFNBQVMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO0lBQ25ILENBQUM7SUFFUyxVQUFVLENBQUMsTUFBYyxFQUFFLElBQVk7UUFDL0MsS0FBSyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUVTLFlBQVksQ0FBQyxNQUFjO1FBQ25DLEtBQUssSUFBSSxDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUVTLEtBQUssQ0FBQyxVQUFVO1FBQ3hCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNwQyxJQUFJLElBQUksQ0FBQyxVQUFVLElBQUksT0FBTyxJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUM7WUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNuSCxDQUFDO0lBRVMsTUFBTSxDQUFDLEVBQVU7UUFDekIsS0FBSyxJQUFJLENBQUMsVUFBVSxFQUFFLGdCQUFnQixDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzdDLENBQUM7SUFFUyxNQUFNLENBQUMsRUFBVTtRQUN6QixLQUFLLElBQUksQ0FBQyxVQUFVLEVBQUUsZ0JBQWdCLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUVTLEtBQUssQ0FBQyxLQUFLO1FBQ25CLElBQUksSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQy9FLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3pCLENBQUM7SUFFUyxLQUFLLENBQUMsTUFBTTtRQUNwQixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDO1FBQ25DLElBQUksQ0FBQyxVQUFVO1lBQUUsT0FBTztRQUN4QixJQUFJLElBQUksQ0FBQyxhQUFhLEVBQUUsRUFBRSxDQUFDO1lBQ3pCLElBQUksQ0FBQztnQkFDSCxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1lBQ25ELENBQUM7WUFBQyxNQUFNLENBQUM7Z0JBQ1AsT0FBTztZQUNULENBQUM7WUFDRCxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUN0QixJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNoQyxDQUFDO1FBQ0QsSUFBSSxNQUFNLFVBQVUsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLENBQUM7WUFDMUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDMUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdkIsT0FBTztRQUNULENBQUM7UUFDRCxNQUFNLEdBQUcsR0FBRyxVQUFVLENBQUMsUUFBUSxFQUFFLENBQUMsS0FBd0MsQ0FBQztRQUMzRSxJQUFJLEdBQUcsRUFBRSxtQkFBbUI7WUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM3RCxDQUFDO0lBRVMsUUFBUSxDQUFDLElBQWlCO1FBQ2xDLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksSUFBSSxDQUFDLENBQUM7UUFDdEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzNCLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QixJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBRVMsYUFBYTtRQUNyQixJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMzQixJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN0QixJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUN6QixDQUFDO0lBRVMsZ0JBQWdCLENBQUMsR0FBVyxFQUFFLEVBQVc7UUFDakQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsT0FBTyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2xHLENBQUM7SUFFUyxLQUFLLENBQUMsUUFBUTtRQUN0QixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDO1FBQ25DLElBQUksQ0FBQyxVQUFVO1lBQUUsT0FBTztRQUN4QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDbkMsTUFBTSxHQUFHLEdBQUcsT0FBTztZQUNqQixDQUFDLENBQUMsTUFBTSxVQUFVLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDO1lBQ3ZHLENBQUMsQ0FBQyxNQUFNLFVBQVUsQ0FBQyxVQUFVLENBQUMsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDN0gsSUFBSSxHQUFHO1lBQUUsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO0lBQ2hDLENBQUM7SUFFUyxVQUFVLENBQUMsSUFBaUI7UUFDcEMsSUFBSSxJQUFJLENBQUMsRUFBRTtZQUFFLEtBQUssSUFBSSxDQUFDLFVBQVUsRUFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFFUyxNQUFNO1FBQ2QsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDOUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDeEIsQ0FBQzt3R0FoS1UsNEJBQTRCOzRGQUE1Qiw0QkFBNEIsaUhBbk83Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaU9UOzs0RkFFVSw0QkFBNEI7a0JBdk94QyxTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSw0QkFBNEI7b0JBQ3RDLFVBQVUsRUFBRSxJQUFJO29CQUNoQixlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtvQkFDL0MsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpT1Q7aUJBQ0Y7d0RBR29CLElBQUk7c0JBQXRCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgQ29tcG9uZW50LFxuICBEZXN0cm95UmVmLFxuICBFdmVudEVtaXR0ZXIsXG4gIE91dHB1dCxcbiAgY29tcHV0ZWQsXG4gIGVmZmVjdCxcbiAgaW5qZWN0LFxuICBzaWduYWwsXG4gIHVudHJhY2tlZCxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBPcmdhbml6YXRpb25Qcm9maWxlQ29udHJvbGxlcixcbiAgbG9jYWxpemVkRXJyb3JNZXNzYWdlLFxuICB0eXBlIE9yZ2FuaXphdGlvblByb2ZpbGVTdGF0ZSxcbiAgdHlwZSBPcmdhbml6YXRpb25SZXF1ZXN0RXJyb3IsXG59IGZyb20gJ0BhdGxhc2F1dGgvanMnO1xuaW1wb3J0IHsgQXRsYXNTZXJ2aWNlIH0gZnJvbSAnLi4vYXRsYXMuc2VydmljZSc7XG5pbXBvcnQgeyBjbGFzc0ZvciwgdHlwZSBFbGVtZW50S2V5IH0gZnJvbSAnLi4vYXBwZWFyYW5jZSc7XG5pbXBvcnQgeyB0cmFuc2xhdGUgfSBmcm9tICcuLi9pMThuJztcblxudHlwZSBUYWIgPSAnbWVtYmVycycgfCAnaW52aXRhdGlvbnMnIHwgJ3JvbGVzJyB8ICdzZXR0aW5ncyc7XG50eXBlIFByb2ZpbGVSb2xlID0gT3JnYW5pemF0aW9uUHJvZmlsZVN0YXRlWydyb2xlcyddW251bWJlcl07XG5cbi8qKlxuICogYDxhdGxhcy1vcmdhbml6YXRpb24tcHJvZmlsZT5gIOKAlCB0aGUgYWN0aXZlIG9yZ2FuaXphdGlvbidzIG1lbWJlcnMgKGFkbWluc1xuICogY2hhbmdlIHJvbGVzIGFuZCByZW1vdmUpLCBpbnZpdGF0aW9ucyAoYWRtaW5zIGludml0ZSwgcmV2b2tlIGFuZCByZXNlbmQpIGFuZFxuICogc2V0dGluZ3MgKGxlYXZlOyBhbiBhZG1pbiBkZWxldGVzIGFmdGVyIGNvbmZpcm1pbmcgdGhlaXIgcGFzc3dvcmQpLiBUaGVcbiAqIEFuZ3VsYXIgcGVlciBvZiBSZWFjdCdzIGA8T3JnYW5pemF0aW9uUHJvZmlsZS8+YCwgb24gdGhlIHNhbWUgaGVhZGxlc3NcbiAqIGNvbnRyb2xsZXIuIEV2ZXJ5IGNvbnRyb2wgaXMgZ2F0ZWQgb24gdGhlIEFDVElWRSBvcmcgcm9sZSwgbWF0Y2hpbmcgdGhlXG4gKiBzZXJ2ZXIncyBydWxlLiBSZW5kZXJzIG5vdGhpbmcgd2l0aCBubyBhY3RpdmUgb3JnLiBFbWl0cyBgZXhpdGAgd2l0aCBgbGVmdGBcbiAqIG9yIGBkZWxldGVkYC5cbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYXRsYXMtb3JnYW5pemF0aW9uLXByb2ZpbGUnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgdGVtcGxhdGU6IGBcbiAgICBAaWYgKGF0bGFzLm9yZ2FuaXphdGlvbigpOyBhcyBtZW1iZXJzaGlwKSB7XG4gICAgICBAaWYgKHN0YXRlKCk7IGFzIHN0KSB7XG4gICAgICAgIDxkaXYgW2NsYXNzXT1cImNscygnY2FyZCcsICdhdGxhcy1jYXJkJylcIiBkYXRhLWF0bGFzPVwib3JnYW5pemF0aW9uLXByb2ZpbGVcIj5cbiAgICAgICAgICA8aDE+e3sgbWVtYmVyc2hpcC5vcmdhbml6YXRpb24ubmFtZSB9fTwvaDE+XG4gICAgICAgICAgPHA+e3sgdCgnb3JnYW5pemF0aW9uLnJvbGVMYWJlbCcpIH19OiB7eyBtZW1iZXJzaGlwLnJvbGUgfX08L3A+XG5cbiAgICAgICAgICA8ZGl2IFtjbGFzc109XCJjbHMoJ21lbnUnLCAnYXRsYXMtbWVudScpXCIgcm9sZT1cInRhYmxpc3RcIj5cbiAgICAgICAgICAgIEBmb3IgKGtleSBvZiB0YWJzKCk7IHRyYWNrIGtleSkge1xuICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgcm9sZT1cInRhYlwiXG4gICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1zZWxlY3RlZF09XCJ0YWIoKSA9PT0ga2V5XCJcbiAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdtZW51SXRlbScsICdhdGxhcy1tZW51LWl0ZW0nKVwiXG4gICAgICAgICAgICAgICAgW2F0dHIuZGF0YS1hdGxhc109XCInb3JnYW5pemF0aW9uLXRhYi0nICsga2V5XCJcbiAgICAgICAgICAgICAgICAoY2xpY2spPVwidGFiLnNldChrZXkpXCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIHt7IHQoJ29yZ2FuaXphdGlvbi4nICsga2V5KSB9fVxuICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgIEBpZiAoZXJyb3JNZXNzYWdlKCk7IGFzIG1lc3NhZ2UpIHtcbiAgICAgICAgICAgIDxwIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZEVycm9yJywgJ2F0bGFzLWVycm9yJylcIiByb2xlPVwiYWxlcnRcIj57eyBtZXNzYWdlIH19PC9wPlxuICAgICAgICAgIH1cblxuICAgICAgICAgIEBpZiAodGFiKCkgPT09ICdtZW1iZXJzJykge1xuICAgICAgICAgICAgPHVsIGNsYXNzPVwiYXRsYXMtbGlzdFwiIGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24tbWVtYmVyc1wiPlxuICAgICAgICAgICAgICBAZm9yIChtIG9mIHN0Lm1lbWJlcnM7IHRyYWNrIG0uaWQpIHtcbiAgICAgICAgICAgICAgICA8bGkgZGF0YS1hdGxhcz1cIm9yZ2FuaXphdGlvbi1tZW1iZXJcIj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuPnt7IG1lbWJlck5hbWUobSkgfX17eyBtLnVzZXI/LmlkID09PSB1c2VySWQoKSA/ICcgJyArIHQoJ29yZ2FuaXphdGlvbi55b3UnKSA6ICcnIH19PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgQGlmIChjYW5NYW5hZ2UoKSAmJiBtLnVzZXIgJiYgbS51c2VyLmlkICE9PSB1c2VySWQoKSkge1xuICAgICAgICAgICAgICAgICAgICA8c2VsZWN0XG4gICAgICAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCJ0KCdvcmdhbml6YXRpb24ucm9sZUxhYmVsJylcIlxuICAgICAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZElucHV0JywgJ2F0bGFzLWlucHV0JylcIlxuICAgICAgICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCJzdC5idXN5ID09PSAncm9sZTonICsgbS51c2VyLmlkXCJcbiAgICAgICAgICAgICAgICAgICAgICAoY2hhbmdlKT1cImNoYW5nZVJvbGUobS51c2VyLmlkLCAkYW55KCRldmVudC50YXJnZXQpLnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICBAZm9yIChyIG9mIHJvbGVzKCk7IHRyYWNrIHIua2V5KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICA8b3B0aW9uIFt2YWx1ZV09XCJyLmtleVwiIFtzZWxlY3RlZF09XCJyLmtleSA9PT0gbS5yb2xlXCI+e3sgci5uYW1lIH19PC9vcHRpb24+XG4gICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICA8L3NlbGVjdD5cbiAgICAgICAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25TZWNvbmRhcnknLCAnYXRsYXMtYnV0dG9uLXNlY29uZGFyeScpXCJcbiAgICAgICAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwic3QuYnVzeSA9PT0gJ3JlbW92ZTonICsgbS51c2VyLmlkXCJcbiAgICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwicmVtb3ZlTWVtYmVyKG0udXNlci5pZClcIlxuICAgICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLnJlbW92ZScpIH19XG4gICAgICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuPiDCtyB7eyBtLnJvbGUgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgPC91bD5cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBAaWYgKHRhYigpID09PSAnaW52aXRhdGlvbnMnICYmIGNhbk1hbmFnZSgpKSB7XG4gICAgICAgICAgICA8ZGl2IGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24taW52aXRhdGlvbnNcIj5cbiAgICAgICAgICAgICAgPGZvcm0gKHN1Ym1pdCk9XCIkZXZlbnQucHJldmVudERlZmF1bHQoKTsgc2VuZEludml0ZSgpXCI+XG4gICAgICAgICAgICAgICAgPGxhYmVsIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZCcsICdhdGxhcy1maWVsZCcpXCI+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRMYWJlbCcsICdhdGxhcy1maWVsZC1sYWJlbCcpXCI+e3sgdCgnb3JnYW5pemF0aW9uLmVtYWlsTGFiZWwnKSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICAgICAgICB0eXBlPVwiZW1haWxcIlxuICAgICAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRJbnB1dCcsICdhdGxhcy1pbnB1dCcpXCJcbiAgICAgICAgICAgICAgICAgICAgW3ZhbHVlXT1cImVtYWlsKClcIlxuICAgICAgICAgICAgICAgICAgICAoaW5wdXQpPVwiZW1haWwuc2V0KCRhbnkoJGV2ZW50LnRhcmdldCkudmFsdWUpXCJcbiAgICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgICAgICAgICA8bGFiZWwgW2NsYXNzXT1cImNscygnZm9ybUZpZWxkJywgJ2F0bGFzLWZpZWxkJylcIj5cbiAgICAgICAgICAgICAgICAgIDxzcGFuIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZExhYmVsJywgJ2F0bGFzLWZpZWxkLWxhYmVsJylcIj57eyB0KCdvcmdhbml6YXRpb24ucm9sZUxhYmVsJykgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8c2VsZWN0IFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZElucHV0JywgJ2F0bGFzLWlucHV0JylcIiAoY2hhbmdlKT1cImludml0ZVJvbGUuc2V0KCRhbnkoJGV2ZW50LnRhcmdldCkudmFsdWUpXCI+XG4gICAgICAgICAgICAgICAgICAgIEBmb3IgKHIgb2Ygcm9sZXMoKTsgdHJhY2sgci5rZXkpIHtcbiAgICAgICAgICAgICAgICAgICAgICA8b3B0aW9uIFt2YWx1ZV09XCJyLmtleVwiIFtzZWxlY3RlZF09XCJyLmtleSA9PT0gaW52aXRlUm9sZSgpXCI+e3sgci5uYW1lIH19PC9vcHRpb24+XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIDwvc2VsZWN0PlxuICAgICAgICAgICAgICAgIDwvbGFiZWw+XG4gICAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgICAgdHlwZT1cInN1Ym1pdFwiXG4gICAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uUHJpbWFyeScsICdhdGxhcy1idXR0b24nKVwiXG4gICAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwic3QuYnVzeSA9PT0gJ2ludml0ZScgfHwgIWVtYWlsKCkudHJpbSgpXCJcbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICB7eyB0KCdvcmdhbml6YXRpb24uc2VuZEludml0ZScpIH19XG4gICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgIDwvZm9ybT5cbiAgICAgICAgICAgICAgQGlmIChzdC5pbnZpdGF0aW9ucy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICA8cCBjbGFzcz1cImF0bGFzLXRleHRcIj57eyB0KCdvcmdhbml6YXRpb24ubm9JbnZpdGF0aW9ucycpIH19PC9wPlxuICAgICAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgICAgICA8dWwgY2xhc3M9XCJhdGxhcy1saXN0XCI+XG4gICAgICAgICAgICAgICAgICBAZm9yIChpbnYgb2Ygc3QuaW52aXRhdGlvbnM7IHRyYWNrIGludi5pZCkge1xuICAgICAgICAgICAgICAgICAgICA8bGkgZGF0YS1hdGxhcz1cIm9yZ2FuaXphdGlvbi1pbnZpdGF0aW9uXCI+XG4gICAgICAgICAgICAgICAgICAgICAgPHNwYW4+e3sgaW52LmVtYWlsX2FkZHJlc3MgfX0gwrcge3sgaW52LnJvbGUgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgW2NsYXNzXT1cImNscygnZm9ybUJ1dHRvblNlY29uZGFyeScsICdhdGxhcy1idXR0b24tc2Vjb25kYXJ5JylcIiAoY2xpY2spPVwicmVzZW5kKGludi5pZClcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IHQoJ29yZ2FuaXphdGlvbi5yZXNlbmQnKSB9fVxuICAgICAgICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25TZWNvbmRhcnknLCAnYXRsYXMtYnV0dG9uLXNlY29uZGFyeScpXCIgKGNsaWNrKT1cInJldm9rZShpbnYuaWQpXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICB7eyB0KCdvcmdhbml6YXRpb24ucmV2b2tlJykgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA8L3VsPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBAaWYgKHRhYigpID09PSAncm9sZXMnICYmIGNhbk1hbmFnZSgpKSB7XG4gICAgICAgICAgICA8ZGl2IGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24tcm9sZXNcIj5cbiAgICAgICAgICAgICAgQGlmIChjdXN0b21Sb2xlcygpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgIDxwIGNsYXNzPVwiYXRsYXMtdGV4dFwiPnt7IHQoJ29yZ2FuaXphdGlvbi5ub0N1c3RvbVJvbGVzJykgfX08L3A+XG4gICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgIDx1bCBjbGFzcz1cImF0bGFzLWxpc3RcIj5cbiAgICAgICAgICAgICAgICAgIEBmb3IgKHJvbGUgb2YgY3VzdG9tUm9sZXMoKTsgdHJhY2sgcm9sZS5rZXkpIHtcbiAgICAgICAgICAgICAgICAgICAgPGxpIGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24tcm9sZVwiPlxuICAgICAgICAgICAgICAgICAgICAgIDxzcGFuPnt7IHJvbGUubmFtZSB9fSDCtyB7eyByb2xlLmtleSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uU2Vjb25kYXJ5JywgJ2F0bGFzLWJ1dHRvbi1zZWNvbmRhcnknKVwiIChjbGljayk9XCJlZGl0Um9sZShyb2xlKVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLmVkaXRSb2xlJykgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25TZWNvbmRhcnknLCAnYXRsYXMtYnV0dG9uLXNlY29uZGFyeScpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhcm9sZS5pZCB8fCBzdC5idXN5ID09PSAncm9sZTpkZWxldGU6JyArIHJvbGUuaWRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cImRlbGV0ZVJvbGUocm9sZSlcIlxuICAgICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IHQoJ29yZ2FuaXphdGlvbi5kZWxldGVSb2xlJykgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA8L3VsPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDxmb3JtIGRhdGEtYXRsYXM9XCJvcmdhbml6YXRpb24tcm9sZS1mb3JtXCIgKHN1Ym1pdCk9XCIkZXZlbnQucHJldmVudERlZmF1bHQoKTsgc2F2ZVJvbGUoKVwiPlxuICAgICAgICAgICAgICAgIEBpZiAoIWVkaXRpbmdSb2xlKCkpIHtcbiAgICAgICAgICAgICAgICAgIDxsYWJlbCBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGQnLCAnYXRsYXMtZmllbGQnKVwiPlxuICAgICAgICAgICAgICAgICAgICA8c3BhbiBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRMYWJlbCcsICdhdGxhcy1maWVsZC1sYWJlbCcpXCI+e3sgdCgnb3JnYW5pemF0aW9uLnJvbGVLZXknKSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICAgICAgICAgICAgbmFtZT1cInJvbGVfa2V5XCJcbiAgICAgICAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRJbnB1dCcsICdhdGxhcy1pbnB1dCcpXCJcbiAgICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwicm9sZUtleSgpXCJcbiAgICAgICAgICAgICAgICAgICAgICAoaW5wdXQpPVwicm9sZUtleS5zZXQoJGFueSgkZXZlbnQudGFyZ2V0KS52YWx1ZSlcIlxuICAgICAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgPGxhYmVsIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZCcsICdhdGxhcy1maWVsZCcpXCI+XG4gICAgICAgICAgICAgICAgICA8c3BhbiBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRMYWJlbCcsICdhdGxhcy1maWVsZC1sYWJlbCcpXCI+e3sgdCgnb3JnYW5pemF0aW9uLnJvbGVOYW1lJykgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICAgICAgICAgbmFtZT1cInJvbGVfbmFtZVwiXG4gICAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZElucHV0JywgJ2F0bGFzLWlucHV0JylcIlxuICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwicm9sZU5hbWUoKVwiXG4gICAgICAgICAgICAgICAgICAgIChpbnB1dCk9XCJyb2xlTmFtZS5zZXQoJGFueSgkZXZlbnQudGFyZ2V0KS52YWx1ZSlcIlxuICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgICAgICAgIDxmaWVsZHNldCBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGQnLCAnYXRsYXMtZmllbGQnKVwiPlxuICAgICAgICAgICAgICAgICAgPGxlZ2VuZCBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRMYWJlbCcsICdhdGxhcy1maWVsZC1sYWJlbCcpXCI+e3sgdCgnb3JnYW5pemF0aW9uLnJvbGVQZXJtaXNzaW9ucycpIH19PC9sZWdlbmQ+XG4gICAgICAgICAgICAgICAgICBAZm9yIChwIG9mIHN0LnBlcm1pc3Npb25zOyB0cmFjayBwLmtleSkge1xuICAgICAgICAgICAgICAgICAgICA8bGFiZWwgZGF0YS1hdGxhcz1cIm9yZ2FuaXphdGlvbi1yb2xlLXBlcm1pc3Npb25cIj5cbiAgICAgICAgICAgICAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwicC5rZXlcIlxuICAgICAgICAgICAgICAgICAgICAgICAgW2NoZWNrZWRdPVwicm9sZVBlcm1zKCkuaW5jbHVkZXMocC5rZXkpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIChjaGFuZ2UpPVwidG9nZ2xlUGVybWlzc2lvbihwLmtleSwgJGFueSgkZXZlbnQudGFyZ2V0KS5jaGVja2VkKVwiXG4gICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgICB7eyBwLm5hbWUgfX1cbiAgICAgICAgICAgICAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA8L2ZpZWxkc2V0PlxuICAgICAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgICAgIHR5cGU9XCJzdWJtaXRcIlxuICAgICAgICAgICAgICAgICAgW2NsYXNzXT1cImNscygnZm9ybUJ1dHRvblByaW1hcnknLCAnYXRsYXMtYnV0dG9uJylcIlxuICAgICAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cIiFyb2xlTmFtZSgpLnRyaW0oKSB8fCAoIWVkaXRpbmdSb2xlKCkgJiYgIXJvbGVLZXkoKS50cmltKCkpIHx8IChzdC5idXN5ID8/ICcnKS5zdGFydHNXaXRoKCdyb2xlOicpXCJcbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICB7eyBlZGl0aW5nUm9sZSgpID8gdCgnb3JnYW5pemF0aW9uLnNhdmVSb2xlJykgOiB0KCdvcmdhbml6YXRpb24uY3JlYXRlUm9sZScpIH19XG4gICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgICAgQGlmIChlZGl0aW5nUm9sZSgpKSB7XG4gICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uU2Vjb25kYXJ5JywgJ2F0bGFzLWJ1dHRvbi1zZWNvbmRhcnknKVwiIChjbGljayk9XCJyZXNldFJvbGVGb3JtKClcIj5cbiAgICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLmNhbmNlbCcpIH19XG4gICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDwvZm9ybT5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cblxuICAgICAgICAgIEBpZiAodGFiKCkgPT09ICdzZXR0aW5ncycpIHtcbiAgICAgICAgICAgIDxkaXYgZGF0YS1hdGxhcz1cIm9yZ2FuaXphdGlvbi1zZXR0aW5nc1wiPlxuICAgICAgICAgICAgICBAaWYgKGNvbmZpcm0oKTsgYXMgYWN0aW9uKSB7XG4gICAgICAgICAgICAgICAgPHAgY2xhc3M9XCJhdGxhcy10ZXh0XCI+XG4gICAgICAgICAgICAgICAgICB7eyB0KGFjdGlvbiA9PT0gJ2xlYXZlJyA/ICdvcmdhbml6YXRpb24ubGVhdmVDb25maXJtJyA6ICdvcmdhbml6YXRpb24uZGVsZXRlQ29uZmlybScsIHsgbmFtZTogbWVtYmVyc2hpcC5vcmdhbml6YXRpb24ubmFtZSB9KSB9fVxuICAgICAgICAgICAgICAgIDwvcD5cbiAgICAgICAgICAgICAgICBAaWYgKG5lZWRzUGFzc3dvcmQoKSkge1xuICAgICAgICAgICAgICAgICAgPGxhYmVsIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZCcsICdhdGxhcy1maWVsZCcpXCI+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIFtjbGFzc109XCJjbHMoJ2Zvcm1GaWVsZExhYmVsJywgJ2F0bGFzLWZpZWxkLWxhYmVsJylcIj57eyB0KCdvcmdhbml6YXRpb24ucGFzc3dvcmRQcm9tcHQnKSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICAgICAgICAgICAgdHlwZT1cInBhc3N3b3JkXCJcbiAgICAgICAgICAgICAgICAgICAgICBhdXRvY29tcGxldGU9XCJjdXJyZW50LXBhc3N3b3JkXCJcbiAgICAgICAgICAgICAgICAgICAgICBbY2xhc3NdPVwiY2xzKCdmb3JtRmllbGRJbnB1dCcsICdhdGxhcy1pbnB1dCcpXCJcbiAgICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwicGFzc3dvcmQoKVwiXG4gICAgICAgICAgICAgICAgICAgICAgKGlucHV0KT1cInBhc3N3b3JkLnNldCgkYW55KCRldmVudC50YXJnZXQpLnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25QcmltYXJ5JywgJ2F0bGFzLWJ1dHRvbicpXCJcbiAgICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCJzdC5idXN5ID09PSAnbGVhdmUnIHx8IHN0LmJ1c3kgPT09ICdkZWxldGUnXCJcbiAgICAgICAgICAgICAgICAgIChjbGljayk9XCJhY3Rpb24gPT09ICdsZWF2ZScgPyBsZWF2ZSgpIDogcmVtb3ZlKClcIlxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgIHt7IHQoJ29yZ2FuaXphdGlvbi5jb25maXJtJykgfX1cbiAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uU2Vjb25kYXJ5JywgJ2F0bGFzLWJ1dHRvbi1zZWNvbmRhcnknKVwiIChjbGljayk9XCJjYW5jZWwoKVwiPlxuICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLmNhbmNlbCcpIH19XG4gICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIFtjbGFzc109XCJjbHMoJ2Zvcm1CdXR0b25TZWNvbmRhcnknLCAnYXRsYXMtYnV0dG9uLXNlY29uZGFyeScpXCIgKGNsaWNrKT1cImNvbmZpcm0uc2V0KCdsZWF2ZScpXCI+XG4gICAgICAgICAgICAgICAgICB7eyB0KCdvcmdhbml6YXRpb24ubGVhdmUnKSB9fVxuICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgICAgIEBpZiAoaXNBZG1pbigpKSB7XG4gICAgICAgICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBbY2xhc3NdPVwiY2xzKCdmb3JtQnV0dG9uU2Vjb25kYXJ5JywgJ2F0bGFzLWJ1dHRvbi1zZWNvbmRhcnknKVwiIChjbGljayk9XCJjb25maXJtLnNldCgnZGVsZXRlJylcIj5cbiAgICAgICAgICAgICAgICAgICAge3sgdCgnb3JnYW5pemF0aW9uLmRlbGV0ZScpIH19XG4gICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgfVxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBPcmdhbml6YXRpb25Qcm9maWxlQ29tcG9uZW50IHtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGF0bGFzID0gaW5qZWN0KEF0bGFzU2VydmljZSk7XG4gIEBPdXRwdXQoKSByZWFkb25seSBleGl0ID0gbmV3IEV2ZW50RW1pdHRlcjwnbGVmdCcgfCAnZGVsZXRlZCc+KCk7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBjbGllbnQgPSB0aGlzLmF0bGFzLmNyZWF0ZUZhcGlDbGllbnQoKTtcbiAgcHJpdmF0ZSBjb250cm9sbGVyOiBPcmdhbml6YXRpb25Qcm9maWxlQ29udHJvbGxlciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHVuc3Vic2NyaWJlOiAoKCkgPT4gdm9pZCkgfCBudWxsID0gbnVsbDtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc3RhdGUgPSBzaWduYWw8T3JnYW5pemF0aW9uUHJvZmlsZVN0YXRlIHwgbnVsbD4obnVsbCk7XG4gIHByb3RlY3RlZCByZWFkb25seSB0YWIgPSBzaWduYWw8VGFiPignbWVtYmVycycpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZW1haWwgPSBzaWduYWwoJycpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaW52aXRlUm9sZSA9IHNpZ25hbCgnb3JnOm1lbWJlcicpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY29uZmlybSA9IHNpZ25hbDwnbGVhdmUnIHwgJ2RlbGV0ZScgfCBudWxsPihudWxsKTtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IG5lZWRzUGFzc3dvcmQgPSBzaWduYWwoZmFsc2UpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcGFzc3dvcmQgPSBzaWduYWwoJycpO1xuICAvLyBUaGUgY3VzdG9tLXJvbGUgZWRpdG9yOiBudWxsID0gY3JlYXRpbmcsIGVsc2UgdGhlIGlkIG9mIHRoZSByb2xlIGJlaW5nIGVkaXRlZC5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGVkaXRpbmdSb2xlID0gc2lnbmFsPHN0cmluZyB8IG51bGw+KG51bGwpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcm9sZUtleSA9IHNpZ25hbCgnJyk7XG4gIHByb3RlY3RlZCByZWFkb25seSByb2xlTmFtZSA9IHNpZ25hbCgnJyk7XG4gIHByb3RlY3RlZCByZWFkb25seSByb2xlUGVybXMgPSBzaWduYWw8c3RyaW5nW10+KFtdKTtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGN1c3RvbVJvbGVzID0gY29tcHV0ZWQoKCkgPT4gKHRoaXMuc3RhdGUoKT8ucm9sZXMgPz8gW10pLmZpbHRlcigocikgPT4gQm9vbGVhbihyLm9yZ2FuaXphdGlvbl9pZCkpKTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgdXNlcklkID0gY29tcHV0ZWQoKCkgPT4gKHRoaXMuYXRsYXMuY2xhaW1zKCk/LnN1YiBhcyBzdHJpbmcgfCB1bmRlZmluZWQpID8/IG51bGwpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY2FuTWFuYWdlID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIHRoaXMuYXRsYXMuY2xhaW1zKCk7XG4gICAgcmV0dXJuIHRoaXMuYXRsYXMuaGFzKHsgcGVybWlzc2lvbjogJ29yZzpzeXNfbWVtYmVyc2hpcHM6bWFuYWdlJyB9KTtcbiAgfSk7XG4gIHByb3RlY3RlZCByZWFkb25seSBpc0FkbWluID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5hdGxhcy5vcmdhbml6YXRpb24oKT8ucm9sZSA9PT0gJ29yZzphZG1pbicpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgdGFicyA9IGNvbXB1dGVkPFRhYltdPigoKSA9PiBbJ21lbWJlcnMnLCAuLi4odGhpcy5jYW5NYW5hZ2UoKSA/IChbJ2ludml0YXRpb25zJywgJ3JvbGVzJ10gYXMgVGFiW10pIDogW10pLCAnc2V0dGluZ3MnXSk7XG4gIHByb3RlY3RlZCByZWFkb25seSByb2xlcyA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBjb25zdCByb2xlcyA9IHRoaXMuc3RhdGUoKT8ucm9sZXMgPz8gW107XG4gICAgcmV0dXJuIHJvbGVzLmxlbmd0aCA+IDAgPyByb2xlcyA6IFt7IGtleTogJ29yZzptZW1iZXInLCBuYW1lOiAnb3JnOm1lbWJlcicgfV07XG4gIH0pO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZXJyb3JNZXNzYWdlID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIGNvbnN0IGVyciA9IHRoaXMuc3RhdGUoKT8uZXJyb3I7XG4gICAgaWYgKCFlcnIgfHwgKGVyciBhcyBPcmdhbml6YXRpb25SZXF1ZXN0RXJyb3IpLm5lZWRzUmV2ZXJpZmljYXRpb24pIHJldHVybiBudWxsO1xuICAgIHJldHVybiBsb2NhbGl6ZWRFcnJvck1lc3NhZ2UoZXJyLCB0aGlzLmF0bGFzLmVycm9yTG9jYWxpemF0aW9uLCB0aGlzLmF0bGFzLmxvY2FsaXphdGlvbik7XG4gIH0pO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIGVmZmVjdCgoKSA9PiB7XG4gICAgICBjb25zdCBpZCA9IHRoaXMuYXRsYXMub3JnYW5pemF0aW9uKCk/Lm9yZ2FuaXphdGlvbi5pZCA/PyBudWxsO1xuICAgICAgdW50cmFja2VkKCgpID0+IHRoaXMuYmluZChpZCkpO1xuICAgIH0pO1xuICAgIGluamVjdChEZXN0cm95UmVmKS5vbkRlc3Ryb3koKCkgPT4gdGhpcy51bnN1YnNjcmliZT8uKCkpO1xuICB9XG5cbiAgcHJpdmF0ZSBiaW5kKGlkOiBzdHJpbmcgfCBudWxsKSB7XG4gICAgaWYgKHRoaXMuY29udHJvbGxlcj8ub3JnYW5pemF0aW9uSWQgPT09IGlkKSByZXR1cm47XG4gICAgdGhpcy51bnN1YnNjcmliZT8uKCk7XG4gICAgdGhpcy5jb250cm9sbGVyID0gbnVsbDtcbiAgICB0aGlzLnN0YXRlLnNldChudWxsKTtcbiAgICBpZiAoIWlkKSByZXR1cm47XG4gICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBPcmdhbml6YXRpb25Qcm9maWxlQ29udHJvbGxlcih0aGlzLmNsaWVudCwgaWQsIHtcbiAgICAgIGNhbk1hbmFnZTogdGhpcy5jYW5NYW5hZ2UoKSxcbiAgICAgIG9uQ2hhbmdlOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IHRoaXMuYXRsYXMucmVsb2FkKCk7XG4gICAgICB9LFxuICAgIH0pO1xuICAgIHRoaXMuY29udHJvbGxlciA9IGNvbnRyb2xsZXI7XG4gICAgdGhpcy51bnN1YnNjcmliZSA9IGNvbnRyb2xsZXIuc3Vic2NyaWJlKChuZXh0KSA9PiB0aGlzLnN0YXRlLnNldChuZXh0KSk7XG4gICAgdGhpcy5zdGF0ZS5zZXQoY29udHJvbGxlci5nZXRTdGF0ZSgpKTtcbiAgICB2b2lkIGNvbnRyb2xsZXIubG9hZCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHQoa2V5OiBzdHJpbmcsIHZhcnM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTogc3RyaW5nIHtcbiAgICByZXR1cm4gdHJhbnNsYXRlKHRoaXMuYXRsYXMubG9jYWxpemF0aW9uLCBrZXksIHZhcnMpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNscyhrZXk6IEVsZW1lbnRLZXksIGJhc2U6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGNsYXNzRm9yKGtleSwgdGhpcy5hdGxhcy5hcHBlYXJhbmNlLCBiYXNlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtZW1iZXJOYW1lKG06IE9yZ2FuaXphdGlvblByb2ZpbGVTdGF0ZVsnbWVtYmVycyddW251bWJlcl0pOiBzdHJpbmcge1xuICAgIHJldHVybiBbbS51c2VyPy5maXJzdF9uYW1lLCBtLnVzZXI/Lmxhc3RfbmFtZV0uZmlsdGVyKEJvb2xlYW4pLmpvaW4oJyAnKSB8fCB0aGlzLnQoJ29yZ2FuaXphdGlvbi51bm5hbWVkTWVtYmVyJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY2hhbmdlUm9sZSh1c2VySWQ6IHN0cmluZywgcm9sZTogc3RyaW5nKSB7XG4gICAgdm9pZCB0aGlzLmNvbnRyb2xsZXI/LmNoYW5nZVJvbGUodXNlcklkLCByb2xlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZW1vdmVNZW1iZXIodXNlcklkOiBzdHJpbmcpIHtcbiAgICB2b2lkIHRoaXMuY29udHJvbGxlcj8ucmVtb3ZlTWVtYmVyKHVzZXJJZCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgc2VuZEludml0ZSgpIHtcbiAgICBjb25zdCBhZGRyZXNzID0gdGhpcy5lbWFpbCgpLnRyaW0oKTtcbiAgICBpZiAodGhpcy5jb250cm9sbGVyICYmIGFkZHJlc3MgJiYgKGF3YWl0IHRoaXMuY29udHJvbGxlci5pbnZpdGUoYWRkcmVzcywgdGhpcy5pbnZpdGVSb2xlKCkpKSkgdGhpcy5lbWFpbC5zZXQoJycpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlc2VuZChpZDogc3RyaW5nKSB7XG4gICAgdm9pZCB0aGlzLmNvbnRyb2xsZXI/LnJlc2VuZEludml0YXRpb24oaWQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJldm9rZShpZDogc3RyaW5nKSB7XG4gICAgdm9pZCB0aGlzLmNvbnRyb2xsZXI/LnJldm9rZUludml0YXRpb24oaWQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFzeW5jIGxlYXZlKCkge1xuICAgIGlmICh0aGlzLmNvbnRyb2xsZXIgJiYgKGF3YWl0IHRoaXMuY29udHJvbGxlci5sZWF2ZSgpKSkgdGhpcy5leGl0LmVtaXQoJ2xlZnQnKTtcbiAgICB0aGlzLmNvbmZpcm0uc2V0KG51bGwpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFzeW5jIHJlbW92ZSgpIHtcbiAgICBjb25zdCBjb250cm9sbGVyID0gdGhpcy5jb250cm9sbGVyO1xuICAgIGlmICghY29udHJvbGxlcikgcmV0dXJuO1xuICAgIGlmICh0aGlzLm5lZWRzUGFzc3dvcmQoKSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgdGhpcy5hdGxhcy5yZWF1dGhlbnRpY2F0ZSh0aGlzLnBhc3N3b3JkKCkpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMucGFzc3dvcmQuc2V0KCcnKTtcbiAgICAgIHRoaXMubmVlZHNQYXNzd29yZC5zZXQoZmFsc2UpO1xuICAgIH1cbiAgICBpZiAoYXdhaXQgY29udHJvbGxlci5kZWxldGVPcmdhbml6YXRpb24oKSkge1xuICAgICAgdGhpcy5leGl0LmVtaXQoJ2RlbGV0ZWQnKTtcbiAgICAgIHRoaXMuY29uZmlybS5zZXQobnVsbCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGVyciA9IGNvbnRyb2xsZXIuZ2V0U3RhdGUoKS5lcnJvciBhcyBPcmdhbml6YXRpb25SZXF1ZXN0RXJyb3IgfCBudWxsO1xuICAgIGlmIChlcnI/Lm5lZWRzUmV2ZXJpZmljYXRpb24pIHRoaXMubmVlZHNQYXNzd29yZC5zZXQodHJ1ZSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZWRpdFJvbGUocm9sZTogUHJvZmlsZVJvbGUpIHtcbiAgICB0aGlzLmVkaXRpbmdSb2xlLnNldChyb2xlLmlkID8/IG51bGwpO1xuICAgIHRoaXMucm9sZUtleS5zZXQocm9sZS5rZXkpO1xuICAgIHRoaXMucm9sZU5hbWUuc2V0KHJvbGUubmFtZSk7XG4gICAgdGhpcy5yb2xlUGVybXMuc2V0KFsuLi4ocm9sZS5wZXJtaXNzaW9ucyA/PyBbXSldKTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNldFJvbGVGb3JtKCkge1xuICAgIHRoaXMuZWRpdGluZ1JvbGUuc2V0KG51bGwpO1xuICAgIHRoaXMucm9sZUtleS5zZXQoJycpO1xuICAgIHRoaXMucm9sZU5hbWUuc2V0KCcnKTtcbiAgICB0aGlzLnJvbGVQZXJtcy5zZXQoW10pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRvZ2dsZVBlcm1pc3Npb24oa2V5OiBzdHJpbmcsIG9uOiBib29sZWFuKSB7XG4gICAgdGhpcy5yb2xlUGVybXMudXBkYXRlKChjdXJyZW50KSA9PiAob24gPyBbLi4uY3VycmVudCwga2V5XSA6IGN1cnJlbnQuZmlsdGVyKChrKSA9PiBrICE9PSBrZXkpKSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgc2F2ZVJvbGUoKSB7XG4gICAgY29uc3QgY29udHJvbGxlciA9IHRoaXMuY29udHJvbGxlcjtcbiAgICBpZiAoIWNvbnRyb2xsZXIpIHJldHVybjtcbiAgICBjb25zdCBlZGl0aW5nID0gdGhpcy5lZGl0aW5nUm9sZSgpO1xuICAgIGNvbnN0IG91dCA9IGVkaXRpbmdcbiAgICAgID8gYXdhaXQgY29udHJvbGxlci51cGRhdGVSb2xlKGVkaXRpbmcsIHsgbmFtZTogdGhpcy5yb2xlTmFtZSgpLnRyaW0oKSwgcGVybWlzc2lvbnM6IHRoaXMucm9sZVBlcm1zKCkgfSlcbiAgICAgIDogYXdhaXQgY29udHJvbGxlci5jcmVhdGVSb2xlKHsga2V5OiB0aGlzLnJvbGVLZXkoKS50cmltKCksIG5hbWU6IHRoaXMucm9sZU5hbWUoKS50cmltKCksIHBlcm1pc3Npb25zOiB0aGlzLnJvbGVQZXJtcygpIH0pO1xuICAgIGlmIChvdXQpIHRoaXMucmVzZXRSb2xlRm9ybSgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRlbGV0ZVJvbGUocm9sZTogUHJvZmlsZVJvbGUpIHtcbiAgICBpZiAocm9sZS5pZCkgdm9pZCB0aGlzLmNvbnRyb2xsZXI/LmRlbGV0ZVJvbGUocm9sZS5pZCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY2FuY2VsKCkge1xuICAgIHRoaXMuY29uZmlybS5zZXQobnVsbCk7XG4gICAgdGhpcy5uZWVkc1Bhc3N3b3JkLnNldChmYWxzZSk7XG4gICAgdGhpcy5wYXNzd29yZC5zZXQoJycpO1xuICB9XG59XG4iXX0=
|