aril 1.2.18 → 2.0.1-dev.1
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/boot/bridge/src/mfe-bridge.d.ts +42 -2
- package/boot/config/apps/index.d.ts +7 -1
- package/boot/config/apps/src/custom-reuse-outlet.component.d.ts +37 -0
- package/boot/config/apps/src/custom-route-reuse-strategy.class.d.ts +156 -0
- package/boot/config/apps/src/nav-link-context-menu.service.d.ts +33 -0
- package/boot/config/apps/src/nav-link.directive.d.ts +29 -0
- package/boot/config/apps/src/nav.service.d.ts +198 -0
- package/boot/config/apps/src/route-close.service.d.ts +9 -0
- package/boot/config/apps/src/safe-navigate.d.ts +17 -0
- package/boot/config/apps/src/tab-aware-url-serializer.d.ts +22 -0
- package/boot/config/plugins/src/getNgZone.d.ts +9 -1
- package/boot/mfe/src/app.component.d.ts +15 -4
- package/boot/mfe/src/isolated-location-strategy.d.ts +57 -0
- package/esm2022/boot/bridge/src/mfe-bridge.mjs +36 -5
- package/esm2022/boot/config/api/src/api.service.mjs +12 -3
- package/esm2022/boot/config/apps/index.mjs +8 -2
- package/esm2022/boot/config/apps/src/apps.service.mjs +14 -6
- package/esm2022/boot/config/apps/src/custom-reuse-outlet.component.mjs +207 -0
- package/esm2022/boot/config/apps/src/custom-route-reuse-strategy.class.mjs +540 -0
- package/esm2022/boot/config/apps/src/nav-link-context-menu.service.mjs +105 -0
- package/esm2022/boot/config/apps/src/nav-link.directive.mjs +45 -0
- package/esm2022/boot/config/apps/src/nav.service.mjs +675 -0
- package/esm2022/boot/config/apps/src/route-close.service.mjs +19 -0
- package/esm2022/boot/config/apps/src/safe-navigate.mjs +50 -0
- package/esm2022/boot/config/apps/src/tab-aware-url-serializer.mjs +50 -0
- package/esm2022/boot/config/plugins/src/getNgZone.mjs +13 -5
- package/esm2022/boot/host/src/app.component.mjs +1 -2
- package/esm2022/boot/host/src/bootstrap.mjs +22 -7
- package/esm2022/boot/mfe/src/app.component.mjs +143 -39
- package/esm2022/boot/mfe/src/bootstrap.mjs +197 -20
- package/esm2022/boot/mfe/src/isolated-location-strategy.mjs +142 -0
- package/esm2022/keycloak/src/auth.interceptor.mjs +17 -2
- package/esm2022/provider/src/prodiveHost.mjs +3 -5
- package/esm2022/provider/src/prodiveHostRouter.mjs +88 -9
- package/esm2022/theme/layout/app/expandableMenu/expandable-menu.component.mjs +81 -19
- package/esm2022/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.mjs +6 -4
- package/esm2022/theme/layout/app/general-search/general-search.component.mjs +4 -4
- package/esm2022/theme/layout/app/history/history-sidebar.component.mjs +6 -4
- package/esm2022/theme/layout/app/layout/app.layout.component.mjs +422 -20
- package/esm2022/theme/layout/app/layout/mfe.layout.component.mjs +22 -35
- package/esm2022/theme/layout/app/site-map/site-map-sidebar.component.mjs +6 -4
- package/esm2022/theme/layout/app/static-sidebar/static-sidebar.component.mjs +85 -27
- package/esm2022/theme/layout/app/topbar/app.topbar.component.mjs +3 -3
- package/esm2022/theme/layout/service/breadcrumb-publisher.service.mjs +86 -0
- package/esm2022/theme/layout/service/tab-session.service.mjs +126 -0
- package/esm2022/ui-business/ref-value/src/ref-value.component.mjs +15 -7
- package/esm2022/util/sync-active-tab-route/src/sync-active-tab-route.directive.mjs +29 -9
- package/fesm2022/aril-app.component-s14ruALV.mjs +183 -0
- package/fesm2022/aril-app.component-s14ruALV.mjs.map +1 -0
- package/fesm2022/aril-boot-bridge.mjs +35 -4
- package/fesm2022/aril-boot-bridge.mjs.map +1 -1
- package/fesm2022/aril-boot-config-api.mjs +11 -2
- package/fesm2022/aril-boot-config-api.mjs.map +1 -1
- package/fesm2022/aril-boot-config-apps.mjs +1678 -10
- package/fesm2022/aril-boot-config-apps.mjs.map +1 -1
- package/fesm2022/aril-boot-config-plugins.mjs +12 -4
- package/fesm2022/aril-boot-config-plugins.mjs.map +1 -1
- package/fesm2022/aril-boot-host.mjs +21 -7
- package/fesm2022/aril-boot-host.mjs.map +1 -1
- package/fesm2022/aril-boot-mfe-app.component-a34GeuUv.mjs +183 -0
- package/fesm2022/aril-boot-mfe-app.component-a34GeuUv.mjs.map +1 -0
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-KFO_X7yR.mjs +631 -0
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-KFO_X7yR.mjs.map +1 -0
- package/fesm2022/aril-boot-mfe.mjs +5 -3
- package/fesm2022/aril-boot-mfe.mjs.map +1 -1
- package/fesm2022/aril-keycloak.mjs +16 -1
- package/fesm2022/aril-keycloak.mjs.map +1 -1
- package/fesm2022/aril-provider.mjs +90 -12
- package/fesm2022/aril-provider.mjs.map +1 -1
- package/fesm2022/aril-theme-layout.mjs +2628 -2017
- package/fesm2022/aril-theme-layout.mjs.map +1 -1
- package/fesm2022/aril-ui-business-ref-value.mjs +14 -6
- package/fesm2022/aril-ui-business-ref-value.mjs.map +1 -1
- package/fesm2022/aril-util-sync-active-tab-route.mjs +28 -8
- package/fesm2022/aril-util-sync-active-tab-route.mjs.map +1 -1
- package/fesm2022/aril.mjs +354 -25
- package/fesm2022/aril.mjs.map +1 -1
- package/keycloak/src/auth.interceptor.d.ts +7 -0
- package/package.json +222 -222
- package/provider/src/prodiveHost.d.ts +1 -0
- package/theme/layout/app/expandableMenu/expandable-menu.component.d.ts +21 -4
- package/theme/layout/app/expandableMenu/expandable-menu.component.html +19 -5
- package/theme/layout/app/expandableMenu/expandable-menu.component.ts +69 -9
- package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.html +1 -0
- package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.ts +3 -1
- package/theme/layout/app/general-search/general-search.component.html +2 -1
- package/theme/layout/app/general-search/general-search.component.ts +2 -2
- package/theme/layout/app/history/history-sidebar.component.html +3 -1
- package/theme/layout/app/history/history-sidebar.component.ts +3 -1
- package/theme/layout/app/layout/app.layout.component.d.ts +105 -5
- package/theme/layout/app/layout/app.layout.component.html +102 -1
- package/theme/layout/app/layout/app.layout.component.scss +372 -0
- package/theme/layout/app/layout/app.layout.component.ts +452 -13
- package/theme/layout/app/layout/mfe.layout.component.d.ts +1 -5
- package/theme/layout/app/layout/mfe.layout.component.ts +12 -38
- package/theme/layout/app/site-map/site-map-sidebar.component.html +1 -0
- package/theme/layout/app/site-map/site-map-sidebar.component.ts +3 -1
- package/theme/layout/app/static-sidebar/static-sidebar.component.d.ts +26 -5
- package/theme/layout/app/static-sidebar/static-sidebar.component.html +11 -5
- package/theme/layout/app/static-sidebar/static-sidebar.component.ts +68 -13
- package/theme/layout/app/topbar/app.topbar.component.html +0 -1
- package/theme/layout/app/topbar/app.topbar.component.scss +1 -1
- package/theme/layout/service/breadcrumb-publisher.service.d.ts +24 -0
- package/theme/layout/service/breadcrumb-publisher.service.ts +95 -0
- package/theme/layout/service/tab-session.service.d.ts +52 -0
- package/theme/layout/service/tab-session.service.ts +138 -0
- package/theme/styles/layout/_breadcrumb.scss +95 -0
- package/theme/styles/layout/_content.scss +2 -2
- package/ui-business/ref-value/src/ref-value.component.d.ts +4 -2
- package/util/sync-active-tab-route/src/sync-active-tab-route.directive.d.ts +15 -2
- package/boot/config/apps/src/reuse-strategy.d.ts +0 -4
- package/esm2022/boot/config/apps/src/reuse-strategy.mjs +0 -9
- package/esm2022/theme/layout/app/breadcrumb/app.breadcrumb.component.mjs +0 -107
- package/fesm2022/aril-app.component-wxP3y8dg.mjs +0 -81
- package/fesm2022/aril-app.component-wxP3y8dg.mjs.map +0 -1
- package/fesm2022/aril-boot-mfe-app.component-7IjAmjz0.mjs +0 -80
- package/fesm2022/aril-boot-mfe-app.component-7IjAmjz0.mjs.map +0 -1
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-KXDpUyv7.mjs +0 -315
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-KXDpUyv7.mjs.map +0 -1
- package/theme/layout/app/breadcrumb/app.breadcrumb.component.d.ts +0 -25
- package/theme/layout/app/breadcrumb/app.breadcrumb.component.html +0 -8
- package/theme/layout/app/breadcrumb/app.breadcrumb.component.ts +0 -127
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import { DestroyRef, Injectable, effect, inject } from '@angular/core';
|
|
2
|
+
import { RouteCloseService } from './route-close.service';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Tab-bazlı navigation için custom `RouteReuseStrategy`.
|
|
6
|
+
*
|
|
7
|
+
* ## Tab izolasyonu (`_tab` query param)
|
|
8
|
+
* `NavService` her tab'a unique `tabId` üretir ve URL'e `?_tab=<tabId>` query param
|
|
9
|
+
* ekler. Strategy bu param'ı route key'in parçası yapar:
|
|
10
|
+
* - Matcher (web component) route'lar: `mfe:${remoteName}:${tabId}`
|
|
11
|
+
* - Normal route'lar: `pathFromRoot` + tüm queryParams (zaten `_tab` dahil)
|
|
12
|
+
*
|
|
13
|
+
* Sonuç: aynı sayfanın iki tab'i `storedRoutes` üzerinde farklı slot'lara yazılır
|
|
14
|
+
* → bağımsız componentRef'ler, bağımsız state. `shouldReuseRoute` matcher karşılaştırması
|
|
15
|
+
* `tabId` farkını da dikkate alır — tab değişiminde fresh component oluşur.
|
|
16
|
+
*
|
|
17
|
+
* ## Mimari: iki bağımsız instance
|
|
18
|
+
* Bu strategy hem `aril/boot/host/src/bootstrap.ts`'te hem her MFE remote'unun
|
|
19
|
+
* `aril/boot/mfe/src/bootstrap.ts`'inde provider olarak veriliyor. Class
|
|
20
|
+
* `providedIn: 'root'` olduğu için **her root injector'da AYRI instance** yaşar.
|
|
21
|
+
* İki instance bağımsız `storedRoutes` map'i tutar ve birbirinden habersizdir:
|
|
22
|
+
*
|
|
23
|
+
* - **Host instance**: matcher route'ları yönetir; key `mfe:${remoteName}:${tabId}`.
|
|
24
|
+
* Tab kapanıp `mfe:wdm:abc123` silindiğinde effect remote'a ait `#0/wdm/...`
|
|
25
|
+
* prefix'li VE `_tab=abc123` içeren child key'leri de temizler.
|
|
26
|
+
* - **MFE instance** (her remote'un kendi içinde): normal route'lar için çalışır.
|
|
27
|
+
* Query params zaten `getRouteKey` çıktısına dahil olduğu için tab izolasyonu
|
|
28
|
+
* doğal olarak gelir — ek özel davranış gerekmez.
|
|
29
|
+
*
|
|
30
|
+
* ## Plugin custom element preservation
|
|
31
|
+
* `preserveCustomElementsInTree` detached view'daki `<md-search>` gibi custom
|
|
32
|
+
* element'leri hidden container'a taşır — element document'tan ayrılmaz, Angular
|
|
33
|
+
* Elements `disconnectedCallback` 10ms destroy timer'ı tetiklenmez, plugin
|
|
34
|
+
* componentRef'i ve dahili state'i (form input, search sonuçları) korunur.
|
|
35
|
+
*
|
|
36
|
+
* ## Bilinen sınırlamalar
|
|
37
|
+
* - `shouldDetach` matcher route'lar için tabId yokken false döner (tabsız orphan
|
|
38
|
+
* handle oluşmasın), diğer durumlarda `true` — tüm detached route'lar cache'lenir.
|
|
39
|
+
* Memory leak'i sınırlamak için `NavService` tab sayısını `MAX_TABS` ile kısıtlar
|
|
40
|
+
* ve son tab kapatıldığında effect remote'un tüm child key'lerini temizler. Yine de
|
|
41
|
+
* tab içinde açılan detail/edit gibi parametreli alt sayfaların handle'ları, parent
|
|
42
|
+
* tab kapanana kadar storedRoutes'ta kalır.
|
|
43
|
+
*/
|
|
44
|
+
export class CustomRouteReuseStrategy {
|
|
45
|
+
constructor() {
|
|
46
|
+
this.routeCloseService = inject(RouteCloseService);
|
|
47
|
+
this.storedRoutes = {};
|
|
48
|
+
/**
|
|
49
|
+
* `(detachedTree as any).__arilPreservedCustomEls = [...]` runtime property
|
|
50
|
+
* eklemek yerine handle'ı key olarak kullanan bir WeakMap tutuyoruz —
|
|
51
|
+
* type-safe ve handle GC'ye uğradığında bellek otomatik temizlenir.
|
|
52
|
+
*/
|
|
53
|
+
this.preservedCustomEls = new WeakMap();
|
|
54
|
+
effect(() => {
|
|
55
|
+
const key = this.routeCloseService.deleteRoute().key;
|
|
56
|
+
if (!key)
|
|
57
|
+
return;
|
|
58
|
+
console.log(`[Strategy] cleanup tetiklendi key="${key}" storedKeys=[${Object.keys(this.storedRoutes).join(',')}]`);
|
|
59
|
+
this.processCleanupKey(key);
|
|
60
|
+
// Cross-injector yayın: `RouteCloseService` `providedIn: 'root'` olduğu için host ve
|
|
61
|
+
// her MFE remote'unda AYRI instance yaşar. Host'tan tetiklenen cleanup MFE strategy'sini
|
|
62
|
+
// otomatik uyandırmaz. Window event ile sinyal yayınlayıp MFE strategy'sinin kendi
|
|
63
|
+
// `storedRoutes`'unu temizlemesini sağlıyoruz (aksi takdirde MFE'de hidden container'a
|
|
64
|
+
// taşınmış plugin element'leri orphan kalır).
|
|
65
|
+
if (key.startsWith('mfe:')) {
|
|
66
|
+
const parsed = this.parseMatcherKey(key);
|
|
67
|
+
if (parsed) {
|
|
68
|
+
window.dispatchEvent(new CustomEvent('aril:remote-cleanup', {
|
|
69
|
+
detail: { remoteName: parsed.remoteName, tabId: parsed.tabId }
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// Karşı yönde (cross-injector): host strategy'nin yayınladığı `aril:remote-cleanup`
|
|
75
|
+
// event'ini her MFE strategy instance'ı yakalar, kendi `storedRoutes`'undaki o remote'a
|
|
76
|
+
// (ve varsa o tabId'ye) ait key'leri temizler. Host strategy de bu listener'a sahip
|
|
77
|
+
// ama kendi `#0/${remoteName}/...` key'leri olmadığı için no-op çalışır (güvenli).
|
|
78
|
+
const cleanupHandler = (e) => {
|
|
79
|
+
const detail = e.detail;
|
|
80
|
+
const remoteName = detail?.remoteName;
|
|
81
|
+
if (!remoteName)
|
|
82
|
+
return;
|
|
83
|
+
const tabId = detail?.tabId ?? null;
|
|
84
|
+
const prefix = `#0/${remoteName}`;
|
|
85
|
+
const keys = Object.keys(this.storedRoutes).filter((k) => {
|
|
86
|
+
if (!k.startsWith(prefix))
|
|
87
|
+
return false;
|
|
88
|
+
// tabId verildiyse SADECE o tab'a ait child handle'ları temizle. Aksi takdirde
|
|
89
|
+
// aynı remote'un başka tab'lerinin state'i de yanlışlıkla silinir.
|
|
90
|
+
if (tabId && !k.includes(`_tab=${tabId}`))
|
|
91
|
+
return false;
|
|
92
|
+
return true;
|
|
93
|
+
});
|
|
94
|
+
if (!keys.length)
|
|
95
|
+
return;
|
|
96
|
+
console.log(`[Strategy] cross-injector cleanup remoteName="${remoteName}" tabId="${tabId ?? '*'}" keys=[${keys.join(',')}]`);
|
|
97
|
+
for (const k of keys) {
|
|
98
|
+
this.cleanupStoredHandle(this.storedRoutes[k]);
|
|
99
|
+
delete this.storedRoutes[k];
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
window.addEventListener('aril:remote-cleanup', cleanupHandler);
|
|
103
|
+
// Strategy `providedIn: 'root'` → root injector destroy edildiğinde listener'ı kaldır.
|
|
104
|
+
// Pratikte uzun ömürlü ama test ortamı ve gelecekteki iframe vb. multi-window
|
|
105
|
+
// senaryolarda orphan listener bırakmamak için defensive.
|
|
106
|
+
inject(DestroyRef).onDestroy(() => window.removeEventListener('aril:remote-cleanup', cleanupHandler));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Injector destroy edildiğinde — hibrit modda her tab'ın child `EnvironmentInjector`'ı tab
|
|
110
|
+
* kapanınca `destroy()` edilir; bu strategy `providedIn:'root'` olduğundan child injector'da
|
|
111
|
+
* (root-scope) materialize olmuş instance da yıkılır — `storedRoutes`'ta kalan TÜM detached
|
|
112
|
+
* handle'ları temizler. Aksi halde `preserveCustomElementsInTree` ile hidden container'a
|
|
113
|
+
* taşınmış plugin custom element'leri (örn. `<md-search>`) orphan kalır (memory leak): MFE
|
|
114
|
+
* internal route key'leri `_tab` taşımadığı için `aril:remote-cleanup` cross-injector temizliği
|
|
115
|
+
* onlara ulaşamaz. Injector-destroy bazlı bu süpürme garantili ve idempotenttir.
|
|
116
|
+
*/
|
|
117
|
+
ngOnDestroy() {
|
|
118
|
+
const keys = Object.keys(this.storedRoutes);
|
|
119
|
+
if (keys.length)
|
|
120
|
+
console.log(`[Strategy] ngOnDestroy → ${keys.length} stored handle temizleniyor (tab kapandı)`);
|
|
121
|
+
for (const key of keys) {
|
|
122
|
+
this.cleanupStoredHandle(this.storedRoutes[key]);
|
|
123
|
+
delete this.storedRoutes[key];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Verilen key için direct + child cleanup uygular. Effect içinden ayrıştırılmış helper.
|
|
128
|
+
*/
|
|
129
|
+
processCleanupKey(key) {
|
|
130
|
+
// Doğrudan eşleşen key varsa onu sil.
|
|
131
|
+
if (this.storedRoutes[key]) {
|
|
132
|
+
console.log(`[Strategy] cleanup → direct match "${key}"`);
|
|
133
|
+
this.cleanupStoredHandle(this.storedRoutes[key]);
|
|
134
|
+
delete this.storedRoutes[key];
|
|
135
|
+
}
|
|
136
|
+
// Matcher key silindiğinde (`mfe:wdm:abc` gibi) o remote'a ait — varsa o tab'a ait —
|
|
137
|
+
// internal route handle'larını da temizle. Convention: matcher key
|
|
138
|
+
// `mfe:${remoteName}` veya `mfe:${remoteName}:${tabId}`, normal key'ler
|
|
139
|
+
// `#0/${remoteName}/...?...&_tab=${tabId}` formatında.
|
|
140
|
+
if (key.startsWith('mfe:')) {
|
|
141
|
+
const parsed = this.parseMatcherKey(key);
|
|
142
|
+
if (!parsed)
|
|
143
|
+
return;
|
|
144
|
+
const { remoteName, tabId } = parsed;
|
|
145
|
+
const prefix = `#0/${remoteName}`;
|
|
146
|
+
for (const k of Object.keys(this.storedRoutes)) {
|
|
147
|
+
if (!k.startsWith(prefix))
|
|
148
|
+
continue;
|
|
149
|
+
// tabId verildiyse sadece o tab'a ait child handle'ları temizle
|
|
150
|
+
if (tabId && !k.includes(`_tab=${tabId}`))
|
|
151
|
+
continue;
|
|
152
|
+
console.log(`[Strategy] cleanup → child match "${k}"`);
|
|
153
|
+
this.cleanupStoredHandle(this.storedRoutes[k]);
|
|
154
|
+
delete this.storedRoutes[k];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* `mfe:${remoteName}` veya `mfe:${remoteName}:${tabId}` formatındaki matcher key'i parse eder.
|
|
160
|
+
* tabId yoksa `null` döner (geriye dönük uyumluluk: legacy çağrılar tabId vermeyebilir).
|
|
161
|
+
*/
|
|
162
|
+
parseMatcherKey(key) {
|
|
163
|
+
if (!key.startsWith('mfe:'))
|
|
164
|
+
return null;
|
|
165
|
+
const tail = key.substring(4);
|
|
166
|
+
const colonIdx = tail.indexOf(':');
|
|
167
|
+
return {
|
|
168
|
+
remoteName: colonIdx === -1 ? tail : tail.substring(0, colonIdx),
|
|
169
|
+
tabId: colonIdx === -1 ? null : tail.substring(colonIdx + 1)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Stored handle'ı tamamen temizle: hidden container'daki preserved element'leri (plugin
|
|
174
|
+
* custom element'leri + ArilWebComponentWrapper'ın `<app-xxx>` element'i) DOM'dan kaldır,
|
|
175
|
+
* sonra Angular `componentRef.destroy()` ile view tree'sini destroy et.
|
|
176
|
+
*
|
|
177
|
+
* `componentRef.destroy()` tek başına yeterli değil: Angular sadece view tree'sini bilir,
|
|
178
|
+
* bizim `preserveCustomElementsInTree` ve `preserveWebComponentElement` ile hidden
|
|
179
|
+
* container'a manuel taşınan element'lerden haberi yoktur → onlar orphan kalır.
|
|
180
|
+
*/
|
|
181
|
+
cleanupStoredHandle(handle) {
|
|
182
|
+
if (!handle)
|
|
183
|
+
return; // Angular Router'ın `store(route, null)` ile bıraktığı orphan key'ler için defensive guard.
|
|
184
|
+
const hiddenContainer = this.getHiddenContainer();
|
|
185
|
+
// 1. preserveCustomElementsInTree ile taşınan plugin element'leri (örn. <md-search>)
|
|
186
|
+
const preserved = this.preservedCustomEls.get(handle);
|
|
187
|
+
if (preserved) {
|
|
188
|
+
for (const { el } of preserved) {
|
|
189
|
+
if (hiddenContainer.contains(el)) {
|
|
190
|
+
hiddenContainer.removeChild(el);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
this.preservedCustomEls.delete(handle);
|
|
194
|
+
}
|
|
195
|
+
// 2. preserveWebComponentElement ile taşınan wrapper element'i (örn. <app-wdm>)
|
|
196
|
+
const componentRef = handle.componentRef;
|
|
197
|
+
const instance = componentRef?.instance;
|
|
198
|
+
if (instance?.element) {
|
|
199
|
+
// Flag'i temizle ki removeChild sonrası tetiklenen disconnectedCallback Application'ı
|
|
200
|
+
// gerçekten destroy etsin — aksi halde MFEAppElement orphan kalır (memory leak).
|
|
201
|
+
delete instance.element.__arilPreserveDuringDetach;
|
|
202
|
+
if (hiddenContainer.contains(instance.element)) {
|
|
203
|
+
hiddenContainer.removeChild(instance.element);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// 3. Angular view tree'sini destroy et
|
|
207
|
+
componentRef?.destroy();
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Matcher (MFE) route'lar için tabId zorunlu. `getTabIdFromRoute` URL queryParams +
|
|
211
|
+
* history.state'i okur. Hiçbir kaynak tabId vermezse (ilk navigation interim'i) detach
|
|
212
|
+
* edilmez; `firstCheckForRoute` `_tab` ekleyip yeniden navigate edecek.
|
|
213
|
+
*/
|
|
214
|
+
shouldDetach(route) {
|
|
215
|
+
if (this.isMatcherRoute(route) && !this.getTabIdFromRoute(route)) {
|
|
216
|
+
console.log(`[Strategy] shouldDetach key="${this.getRouteKey(route)}" → false (matcher route, tabId yok)`);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
const routeKey = this.getRouteKey(route);
|
|
220
|
+
console.log(`[Strategy] shouldDetach key="${routeKey}" → true`);
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
store(route, detachedTree) {
|
|
224
|
+
const routeKey = this.getRouteKey(route);
|
|
225
|
+
console.log(`[Strategy] store key="${routeKey}" handle=`, !!detachedTree);
|
|
226
|
+
if (!detachedTree) {
|
|
227
|
+
delete this.storedRoutes[routeKey];
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (this.isMatcherRoute(route) && !this.getTabIdFromRoute(route)) {
|
|
231
|
+
console.log(`[Strategy] store SKIP key="${routeKey}" (matcher route, tabId yok)`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
this.storedRoutes[routeKey] = detachedTree;
|
|
235
|
+
this.preserveWebComponentElement(detachedTree);
|
|
236
|
+
this.preserveCustomElementsInTree(detachedTree);
|
|
237
|
+
}
|
|
238
|
+
shouldAttach(route) {
|
|
239
|
+
if (this.isMatcherRoute(route) && !this.getTabIdFromRoute(route)) {
|
|
240
|
+
console.log(`[Strategy] shouldAttach key="${this.getRouteKey(route)}" → false (matcher route, tabId yok)`);
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
const routeKey = this.getRouteKey(route);
|
|
244
|
+
const shouldAttach = !!this.storedRoutes[routeKey];
|
|
245
|
+
console.log(`[Strategy] shouldAttach key="${routeKey}" → ${shouldAttach} (storedKeys=[${Object.keys(this.storedRoutes).join(',')}])`);
|
|
246
|
+
return shouldAttach;
|
|
247
|
+
}
|
|
248
|
+
retrieve(route) {
|
|
249
|
+
if (this.isMatcherRoute(route) && !this.getTabIdFromRoute(route)) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const routeKey = this.getRouteKey(route);
|
|
253
|
+
const handle = this.storedRoutes[routeKey];
|
|
254
|
+
console.log(`[Strategy] retrieve key="${routeKey}" → ${handle ? 'HAS' : 'NULL'}`);
|
|
255
|
+
if (handle) {
|
|
256
|
+
this.restoreWebComponentElement(handle);
|
|
257
|
+
this.restoreCustomElementsInTree(handle);
|
|
258
|
+
}
|
|
259
|
+
return handle || null;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Web component element'ini hidden container'a taşır (state korunması için).
|
|
263
|
+
*
|
|
264
|
+
* Element'e `__arilPreserveDuringDetach` flag'i yazılır: `removeChild` + `appendChild`
|
|
265
|
+
* arasında MFE custom element'in `disconnectedCallback`'i tetikleniyor; flag oradaki
|
|
266
|
+
* destroy timer'ını **deterministic** şekilde bypass eder. Eskiden 50ms hardcoded timer
|
|
267
|
+
* yarış koşulu üretiyordu (yavaş cihazlarda Application istemsiz destroy oluyordu).
|
|
268
|
+
*/
|
|
269
|
+
preserveWebComponentElement(detachedTree) {
|
|
270
|
+
try {
|
|
271
|
+
const componentRef = detachedTree.componentRef;
|
|
272
|
+
if (!componentRef)
|
|
273
|
+
return;
|
|
274
|
+
const componentInstance = componentRef.instance;
|
|
275
|
+
if (!componentInstance?.element)
|
|
276
|
+
return;
|
|
277
|
+
const element = componentInstance.element;
|
|
278
|
+
const hiddenContainer = this.getHiddenContainer();
|
|
279
|
+
// Flag — disconnectedCallback bunu okuyup destroy'ı atlayacak. removeChild'tan
|
|
280
|
+
// ÖNCE set ediliyor ki callback ilk tetiklendiğinde flag yerinde olsun.
|
|
281
|
+
element.__arilPreserveDuringDetach = true;
|
|
282
|
+
// Eğer element zaten bir parent'a sahipse, ondan ayır
|
|
283
|
+
if (element.parentNode && element.parentNode !== hiddenContainer) {
|
|
284
|
+
element.parentNode.removeChild(element);
|
|
285
|
+
}
|
|
286
|
+
// Hidden container'a ekle (eğer zaten orada değilse)
|
|
287
|
+
if (!hiddenContainer.contains(element)) {
|
|
288
|
+
hiddenContainer.appendChild(element);
|
|
289
|
+
console.log('Web component element hidden container\'a taşındı (state korunuyor)');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
console.warn('Web component element preserve edilirken hata:', error);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Web component element'ini hidden container'dan çıkar ve component view'a eklemeyi dene.
|
|
298
|
+
*
|
|
299
|
+
* `__arilPreserveDuringDetach` flag'i temizlenir — eğer bu retrieve sonrası element
|
|
300
|
+
* gerçek anlamda destroy edilirse `disconnectedCallback` flag yok kabul edip Application
|
|
301
|
+
* cleanup'ını yürütecek.
|
|
302
|
+
*/
|
|
303
|
+
restoreWebComponentElement(detachedTree) {
|
|
304
|
+
try {
|
|
305
|
+
const componentRef = detachedTree.componentRef;
|
|
306
|
+
if (!componentRef)
|
|
307
|
+
return;
|
|
308
|
+
const componentInstance = componentRef.instance;
|
|
309
|
+
if (!componentInstance?.element)
|
|
310
|
+
return;
|
|
311
|
+
const element = componentInstance.element;
|
|
312
|
+
const hiddenContainer = this.getHiddenContainer();
|
|
313
|
+
// Eğer element hidden container'da ise, çıkar
|
|
314
|
+
if (hiddenContainer.contains(element)) {
|
|
315
|
+
hiddenContainer.removeChild(element);
|
|
316
|
+
delete element.__arilPreserveDuringDetach;
|
|
317
|
+
console.log('Web component element hidden container\'dan çıkarıldı');
|
|
318
|
+
// Component view'ın native element'ine eklemeyi dene
|
|
319
|
+
const tryRestore = () => {
|
|
320
|
+
const vc = componentInstance.vc;
|
|
321
|
+
if (vc?.nativeElement) {
|
|
322
|
+
vc.nativeElement.appendChild(element);
|
|
323
|
+
console.log('Web component element component view\'a eklendi (retrieve sırasında)');
|
|
324
|
+
// Props'ları güncellemek için flag set et (component kendi populateProps'unu çağıracak)
|
|
325
|
+
componentInstance.propsUpdated = false;
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
return false;
|
|
329
|
+
};
|
|
330
|
+
// Hemen dene
|
|
331
|
+
if (!tryRestore()) {
|
|
332
|
+
// View henüz hazır değilse, bir sonraki tick'te tekrar dene
|
|
333
|
+
setTimeout(() => {
|
|
334
|
+
if (!tryRestore()) {
|
|
335
|
+
// Hala başarısızsa, flag set et (ngAfterViewInit'te tekrar denenecek)
|
|
336
|
+
componentInstance.needsRestore = true;
|
|
337
|
+
console.log('Web component element restore edilecek (view hazır değil, ngAfterViewInit\'te tekrar denenecek)');
|
|
338
|
+
}
|
|
339
|
+
}, 0);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
console.warn('Web component element restore edilirken hata:', error);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Global hidden container'ı oluşturur veya döndürür
|
|
349
|
+
*/
|
|
350
|
+
getHiddenContainer() {
|
|
351
|
+
const slot = globalThis;
|
|
352
|
+
let container = slot.__arilWebComponentHiddenContainer;
|
|
353
|
+
if (!container) {
|
|
354
|
+
container = document.createElement('div');
|
|
355
|
+
container.style.display = 'none';
|
|
356
|
+
container.style.position = 'absolute';
|
|
357
|
+
container.style.left = '-9999px';
|
|
358
|
+
container.style.top = '-9999px';
|
|
359
|
+
container.style.visibility = 'hidden';
|
|
360
|
+
document.body.appendChild(container);
|
|
361
|
+
slot.__arilWebComponentHiddenContainer = container;
|
|
362
|
+
}
|
|
363
|
+
return container;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Detached route view'ı içindeki plugin/MFE custom element'lerini (tag adında `-`
|
|
367
|
+
* bulunan ve `customElements`'a kayıtlı olanları) hidden container'a taşır ve
|
|
368
|
+
* yerlerine birer Comment placeholder bırakır. Element document'tan ayrılmaz,
|
|
369
|
+
* Angular Elements `disconnectedCallback` 10ms destroy timer'ı kanat çırpmaz —
|
|
370
|
+
* dolayısıyla plugin'in `ComponentRef` ve dahili state'i (form değerleri, search
|
|
371
|
+
* sonuçları vs.) sonraki retrieve'e kadar korunur.
|
|
372
|
+
*/
|
|
373
|
+
preserveCustomElementsInTree(detachedTree) {
|
|
374
|
+
try {
|
|
375
|
+
const componentRef = detachedTree.componentRef;
|
|
376
|
+
const rootEl = componentRef?.location?.nativeElement;
|
|
377
|
+
if (!rootEl)
|
|
378
|
+
return;
|
|
379
|
+
const hiddenContainer = this.getHiddenContainer();
|
|
380
|
+
const all = rootEl.querySelectorAll('*');
|
|
381
|
+
const preservedList = [];
|
|
382
|
+
for (const el of Array.from(all)) {
|
|
383
|
+
const tag = el.tagName.toLowerCase();
|
|
384
|
+
if (!tag.includes('-'))
|
|
385
|
+
continue;
|
|
386
|
+
if (!window.customElements?.get(tag))
|
|
387
|
+
continue;
|
|
388
|
+
const node = el;
|
|
389
|
+
const placeholder = document.createComment(`aril-preserved:${tag}`);
|
|
390
|
+
node.parentNode?.insertBefore(placeholder, node);
|
|
391
|
+
hiddenContainer.appendChild(node);
|
|
392
|
+
preservedList.push({ el: node, placeholder });
|
|
393
|
+
}
|
|
394
|
+
if (preservedList.length) {
|
|
395
|
+
this.preservedCustomEls.set(detachedTree, preservedList);
|
|
396
|
+
console.log(`[Strategy] preserveCustomElementsInTree: ${preservedList.length} element taşındı`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
console.warn('Custom element preserve edilirken hata:', error);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* preserveCustomElementsInTree ile taşınan custom element'leri orijinal
|
|
405
|
+
* placeholder'larının yerine geri koyar.
|
|
406
|
+
*/
|
|
407
|
+
restoreCustomElementsInTree(detachedTree) {
|
|
408
|
+
try {
|
|
409
|
+
const preservedList = this.preservedCustomEls.get(detachedTree);
|
|
410
|
+
if (!preservedList?.length)
|
|
411
|
+
return;
|
|
412
|
+
for (const { el, placeholder } of preservedList) {
|
|
413
|
+
if (placeholder.parentNode) {
|
|
414
|
+
placeholder.parentNode.replaceChild(el, placeholder);
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
// Placeholder ağaçtan koparılmış — parent view tamamen destroy edilmiş olabilir
|
|
418
|
+
// (örn. remote tab kapanmış). Element'i hidden container'da bırakmamak için cleanup.
|
|
419
|
+
console.warn('[Strategy] restore: placeholder parent yok, custom element cleanup ediliyor', el.tagName);
|
|
420
|
+
if (el.parentNode === this.getHiddenContainer()) {
|
|
421
|
+
this.getHiddenContainer().removeChild(el);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
this.preservedCustomEls.delete(detachedTree);
|
|
426
|
+
console.log(`[Strategy] restoreCustomElementsInTree: ${preservedList.length} element geri yerleştirildi`);
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
console.warn('Custom element restore edilirken hata:', error);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
isMatcherRoute(route) {
|
|
433
|
+
return !!route?.routeConfig?.matcher;
|
|
434
|
+
}
|
|
435
|
+
getRemoteName(route) {
|
|
436
|
+
return route?.routeConfig?.data?.remoteName ?? null;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* URL `?_tab=<tabId>` query param'ını oku — `NavService` her tab için unique değer üretir.
|
|
440
|
+
* `IsolatedLocationStrategy` sayesinde host Router'a tabsız navigation gelmez, sadece
|
|
441
|
+
* `tabState({_tab})`'lı tab tıklamaları gelir → `route.queryParams['_tab']` her zaman dolu.
|
|
442
|
+
*/
|
|
443
|
+
getTabIdFromRoute(route) {
|
|
444
|
+
const v = route?.queryParams?.['_tab'];
|
|
445
|
+
return typeof v === 'string' && v.length > 0 ? v : null;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Route key'ini oluşturur. Matcher (web component) route'lar için remoteName + tabId
|
|
449
|
+
* bazlı key kullanılır — aynı remote'un her tab'i ayrı MFE custom element instance'ına
|
|
450
|
+
* sahiptir (bağımsız state).
|
|
451
|
+
*
|
|
452
|
+
* Normal route'lar için `pathFromRoot` üzerinden tam yol üretilir, ardından query params
|
|
453
|
+
* eklenir (zaten `_tab` dahil) — nested boş-path (`path: ''`) child route'larının aynı `""`
|
|
454
|
+
* key'iyle çakışmasını engeller ve aynı path'in iki tab'i farklı key alır.
|
|
455
|
+
*/
|
|
456
|
+
getRouteKey(route) {
|
|
457
|
+
if (this.isMatcherRoute(route)) {
|
|
458
|
+
const remoteName = this.getRemoteName(route);
|
|
459
|
+
if (remoteName) {
|
|
460
|
+
const tabId = this.getTabIdFromRoute(route);
|
|
461
|
+
return tabId ? `mfe:${remoteName}:${tabId}` : `mfe:${remoteName}`;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// Tam path: kök → intermediate → self. Boş segmentleri index ile koruyarak hiyerarşi
|
|
465
|
+
// pozisyonunu key'e dahil ederiz (kardeş empty-path route'lar farklı key alır).
|
|
466
|
+
const fullPath = route.pathFromRoot
|
|
467
|
+
.map((r, i) => {
|
|
468
|
+
const seg = r.url.map((s) => s.path).join('/');
|
|
469
|
+
return seg.length > 0 ? seg : `#${i}`;
|
|
470
|
+
})
|
|
471
|
+
.join('/');
|
|
472
|
+
const queryParams = this.serializeQueryParams(route.queryParams);
|
|
473
|
+
const fragment = route.fragment ? '#' + route.fragment : '';
|
|
474
|
+
return fullPath + queryParams + fragment;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Query param'ları URL-safe şekilde serileştirir. `URLSearchParams` constructor'una
|
|
478
|
+
* doğrudan object verirsek array değerler `?ids=1,2,3` gibi tek key olarak yazılır;
|
|
479
|
+
* Angular Router'ın `?ids=1&ids=2&ids=3` davranışıyla uyumsuz olabilir. `append` ile
|
|
480
|
+
* her array elemanı ayrı entry olur ve null/undefined değerler atlanır.
|
|
481
|
+
*/
|
|
482
|
+
serializeQueryParams(params) {
|
|
483
|
+
// `sort()` ile determinism — aynı sayfanın farklı insertion-order'lı queryParams'ı
|
|
484
|
+
// (deep link share, server-side render, JS-generated URL'ler) aynı route key üretir.
|
|
485
|
+
// Aksi takdirde `?b=1&a=2` ve `?a=2&b=1` iki ayrı handle olarak cache'lenir → tab
|
|
486
|
+
// izolasyonu aynı sayfanın iki ayrı instance'ını açar.
|
|
487
|
+
const keys = Object.keys(params).sort();
|
|
488
|
+
if (!keys.length)
|
|
489
|
+
return '';
|
|
490
|
+
const out = new URLSearchParams();
|
|
491
|
+
for (const k of keys) {
|
|
492
|
+
const v = params[k];
|
|
493
|
+
if (v == null)
|
|
494
|
+
continue;
|
|
495
|
+
if (Array.isArray(v)) {
|
|
496
|
+
v.forEach((vv) => vv != null && out.append(k, String(vv)));
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
out.append(k, String(v));
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
const str = out.toString();
|
|
503
|
+
return str ? '?' + str : '';
|
|
504
|
+
}
|
|
505
|
+
shouldReuseRoute(future, curr) {
|
|
506
|
+
const futureMatcher = this.isMatcherRoute(future);
|
|
507
|
+
const currMatcher = this.isMatcherRoute(curr);
|
|
508
|
+
if (futureMatcher && currMatcher) {
|
|
509
|
+
const fr = this.getRemoteName(future);
|
|
510
|
+
const cr = this.getRemoteName(curr);
|
|
511
|
+
const ft = this.getTabIdFromRoute(future);
|
|
512
|
+
const ct = this.getTabIdFromRoute(curr);
|
|
513
|
+
// Aynı remote olsa bile farklı tab'lerse fresh component instantiate edilmeli —
|
|
514
|
+
// aksi takdirde iki tab tek componentRef'i paylaşır ve state izolasyonu kaybolur.
|
|
515
|
+
const shouldReuse = !!fr && fr === cr && ft === ct;
|
|
516
|
+
console.log(`[Strategy] shouldReuseRoute matcher → ${shouldReuse}. future=${fr}/${ft} curr=${cr}/${ct}`);
|
|
517
|
+
return shouldReuse;
|
|
518
|
+
}
|
|
519
|
+
if (futureMatcher !== currMatcher) {
|
|
520
|
+
console.log('[Strategy] shouldReuseRoute mixed → false');
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
const shouldReuse = future.routeConfig === curr.routeConfig;
|
|
524
|
+
const futureKey = this.getRouteKey(future);
|
|
525
|
+
const currKey = this.getRouteKey(curr);
|
|
526
|
+
const futureComp = future.routeConfig?.component?.name ?? future.component?.name ?? 'none';
|
|
527
|
+
const currComp = curr.routeConfig?.component?.name ?? curr.component?.name ?? 'none';
|
|
528
|
+
console.log(`[Strategy] shouldReuseRoute normal → ${shouldReuse}. future="${futureKey}"(${futureComp}) curr="${currKey}"(${currComp})`);
|
|
529
|
+
return shouldReuse;
|
|
530
|
+
}
|
|
531
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CustomRouteReuseStrategy, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
532
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CustomRouteReuseStrategy, providedIn: 'root' }); }
|
|
533
|
+
}
|
|
534
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: CustomRouteReuseStrategy, decorators: [{
|
|
535
|
+
type: Injectable,
|
|
536
|
+
args: [{
|
|
537
|
+
providedIn: 'root'
|
|
538
|
+
}]
|
|
539
|
+
}], ctorParameters: () => [] });
|
|
540
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tLXJvdXRlLXJldXNlLXN0cmF0ZWd5LmNsYXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvYXJpbC9ib290L2NvbmZpZy9hcHBzL3NyYy9jdXN0b20tcm91dGUtcmV1c2Utc3RyYXRlZ3kuY2xhc3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFnQixVQUFVLEVBQWMsVUFBVSxFQUFhLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFJNUcsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7O0FBNkIxRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBdUNHO0FBSUgsTUFBTSxPQUFPLHdCQUF3QjtJQVdwQztRQVZBLHNCQUFpQixHQUFzQixNQUFNLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUNqRSxpQkFBWSxHQUF3QyxFQUFFLENBQUM7UUFFdkQ7Ozs7V0FJRztRQUNLLHVCQUFrQixHQUFHLElBQUksT0FBTyxFQUE0QyxDQUFDO1FBR3BGLE1BQU0sQ0FBQyxHQUFHLEVBQUU7WUFDWCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsV0FBVyxFQUFFLENBQUMsR0FBRyxDQUFDO1lBQ3JELElBQUksQ0FBQyxHQUFHO2dCQUFFLE9BQU87WUFDakIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzQ0FBc0MsR0FBRyxpQkFBaUIsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNuSCxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLENBQUM7WUFFNUIscUZBQXFGO1lBQ3JGLHlGQUF5RjtZQUN6RixtRkFBbUY7WUFDbkYsdUZBQXVGO1lBQ3ZGLDhDQUE4QztZQUM5QyxJQUFJLEdBQUcsQ0FB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TyxLQUFLLENBQUM7UUFDZCxDQUFDO1FBRUQsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLFdBQVcsS0FBSyxJQUFJLENBQUMsV0FBVyxDQUFDO1FBQzVELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDM0MsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2QyxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsV0FBVyxFQUFFLFNBQVMsRUFBRSxJQUFJLElBQUksTUFBTSxDQUFDLFNBQVMsRUFBRSxJQUFJLElBQUksTUFBTSxDQUFDO1FBQzNGLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUUsU0FBUyxFQUFFLElBQUksSUFBSSxJQUFJLENBQUMsU0FBUyxFQUFFLElBQUksSUFBSSxNQUFNLENBQUM7UUFDckYsT0FBTyxDQUFDLEdBQUcsQ0FDVix3Q0FBd0MsV0FBVyxhQUFhLFNBQVMsS0FBSyxVQUFVLFdBQVcsT0FBTyxLQUFLLFFBQVEsR0FBRyxDQUMxSCxDQUFDO1FBQ0YsT0FBTyxXQUFXLENBQUM7SUFDcEIsQ0FBQzs4R0E3Zlcsd0JBQXdCO2tIQUF4Qix3QkFBd0IsY0FGeEIsTUFBTTs7MkZBRU4sd0JBQXdCO2tCQUhwQyxVQUFVO21CQUFDO29CQUNYLFVBQVUsRUFBRSxNQUFNO2lCQUNsQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudFJlZiwgRGVzdHJveVJlZiwgRWxlbWVudFJlZiwgSW5qZWN0YWJsZSwgT25EZXN0cm95LCBlZmZlY3QsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90LCBEZXRhY2hlZFJvdXRlSGFuZGxlLCBSb3V0ZVJldXNlU3RyYXRlZ3kgfSBmcm9tICdAYW5ndWxhci9yb3V0ZXInO1xyXG5cclxuaW1wb3J0IHsgUGx1Z2luQ29uZmlnIH0gZnJvbSAnLi9pbnRlcmZhY2VzJztcclxuaW1wb3J0IHsgUm91dGVDbG9zZVNlcnZpY2UgfSBmcm9tICcuL3JvdXRlLWNsb3NlLnNlcnZpY2UnO1xyXG5cclxuLyoqXHJcbiAqIEFuZ3VsYXIgUm91dGVyIGBEZXRhY2hlZFJvdXRlSGFuZGxlYCfEsSByZXNtaSBvbGFyYWsgb3BhcXVlIChge31gKSBiaXIgdHlwZS5cclxuICogSW50ZXJuYWwgc2hhcGUnaSBwdWJsaWMgQVBJJ2RhIHlvaywgYW1hIGJpeiBwcmVzZXJ2ZS9yZXN0b3JlIGnDp2luIGNvbXBvbmVudFJlZidlXHJcbiAqIGVyacWfbWVtaXogZ2VyZWtpeW9yLiBCdSB5w7x6ZGVuIGludGVybmFsIHlhcMSxecSxIG1pbmltYWwgxZ9la2lsZGUgbW9kZWxsaXlvcnV6LlxyXG4gKi9cclxudHlwZSBJbnRlcm5hbERldGFjaGVkUm91dGVIYW5kbGUgPSBEZXRhY2hlZFJvdXRlSGFuZGxlICYge1xyXG5cdGNvbXBvbmVudFJlZj86IENvbXBvbmVudFJlZjx1bmtub3duPjtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBBcmlsV2ViQ29tcG9uZW50V3JhcHBlciBpbnN0YW5jZSfEsW5kYSBzdHJhdGVneSduaW4gZ8O2emxleWVjZcSfaSBhbGFubGFyLlxyXG4gKiBXcmFwcGVyJ2EgZG/En3J1ZGFuIGJhxJ/EsW1sxLEgb2xtYW1hayBpw6dpbiBkdWNrLXR5cGluZyBpbGUgbWluaW1hbCBpbnRlcmZhY2UuXHJcbiAqIGBuZWVkc1Jlc3RvcmVgIHZlIGBwcm9wc1VwZGF0ZWRgIGJheXJha2xhcsSxIHdyYXBwZXIgaWxlIHN0cmF0ZWd5IGFyYXPEsW5kYWtpIHPDtnpsZcWfbWU6XHJcbiAqIHN0cmF0ZWd5IGByZXRyaWV2ZWAgc8SxcmFzxLFuZGEgc2V0IGVkZXIsIHdyYXBwZXIgYG5nQWZ0ZXJWaWV3SW5pdGAndGUgdMO8a2V0aXIuXHJcbiAqL1xyXG50eXBlIFdyYXBwZXJJbnN0YW5jZSA9IHtcclxuXHRlbGVtZW50PzogSFRNTEVsZW1lbnQ7XHJcblx0dmM/OiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PjtcclxuXHRwcm9wc1VwZGF0ZWQ/OiBib29sZWFuO1xyXG5cdG5lZWRzUmVzdG9yZT86IGJvb2xlYW47XHJcbn07XHJcblxyXG5pbnRlcmZhY2UgUHJlc2VydmVkQ3VzdG9tRWwge1xyXG5cdGVsOiBIVE1MRWxlbWVudDtcclxuXHRwbGFjZWhvbGRlcjogQ29tbWVudDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFRhYi1iYXpsxLEgbmF2aWdhdGlvbiBpw6dpbiBjdXN0b20gYFJvdXRlUmV1c2VTdHJhdGVneWAuXHJcbiAqXHJcbiAqICMjIFRhYiBpem9sYXN5b251IChgX3RhYmAgcXVlcnkgcGFyYW0pXHJcbiAqIGBOYXZTZXJ2aWNlYCBoZXIgdGFiJ2EgdW5pcXVlIGB0YWJJZGAgw7xyZXRpciB2ZSBVUkwnZSBgP190YWI9PHRhYklkPmAgcXVlcnkgcGFyYW1cclxuICogZWtsZXIuIFN0cmF0ZWd5IGJ1IHBhcmFtJ8SxIHJvdXRlIGtleSdpbiBwYXLDp2FzxLEgeWFwYXI6XHJcbiAqICAtIE1hdGNoZXIgKHdlYiBjb21wb25lbnQpIHJvdXRlJ2xhcjogYG1mZToke3JlbW90ZU5hbWV9OiR7dGFiSWR9YFxyXG4gKiAgLSBOb3JtYWwgcm91dGUnbGFyOiBgcGF0aEZyb21Sb290YCArIHTDvG0gcXVlcnlQYXJhbXMgKHphdGVuIGBfdGFiYCBkYWhpbClcclxuICpcclxuICogU29udcOnOiBheW7EsSBzYXlmYW7EsW4gaWtpIHRhYidpIGBzdG9yZWRSb3V0ZXNgIMO8emVyaW5kZSBmYXJrbMSxIHNsb3QnbGFyYSB5YXrEsWzEsXJcclxuICog4oaSIGJhxJ/EsW1zxLF6IGNvbXBvbmVudFJlZidsZXIsIGJhxJ/EsW1zxLF6IHN0YXRlLiBgc2hvdWxkUmV1c2VSb3V0ZWAgbWF0Y2hlciBrYXLFn8SxbGHFn3TEsXJtYXPEsVxyXG4gKiBgdGFiSWRgIGZhcmvEsW7EsSBkYSBkaWtrYXRlIGFsxLFyIOKAlCB0YWIgZGXEn2nFn2ltaW5kZSBmcmVzaCBjb21wb25lbnQgb2x1xZ91ci5cclxuICpcclxuICogIyMgTWltYXJpOiBpa2kgYmHEn8SxbXPEsXogaW5zdGFuY2VcclxuICogQnUgc3RyYXRlZ3kgaGVtIGBhcmlsL2Jvb3QvaG9zdC9zcmMvYm9vdHN0cmFwLnRzYCd0ZSBoZW0gaGVyIE1GRSByZW1vdGUndW51blxyXG4gKiBgYXJpbC9ib290L21mZS9zcmMvYm9vdHN0cmFwLnRzYCdpbmRlIHByb3ZpZGVyIG9sYXJhayB2ZXJpbGl5b3IuIENsYXNzXHJcbiAqIGBwcm92aWRlZEluOiAncm9vdCdgIG9sZHXEn3UgacOnaW4gKipoZXIgcm9vdCBpbmplY3RvcidkYSBBWVJJIGluc3RhbmNlKiogeWHFn2FyLlxyXG4gKiDEsGtpIGluc3RhbmNlIGJhxJ/EsW1zxLF6IGBzdG9yZWRSb3V0ZXNgIG1hcCdpIHR1dGFyIHZlIGJpcmJpcmluZGVuIGhhYmVyc2l6ZGlyOlxyXG4gKlxyXG4gKiAtICoqSG9zdCBpbnN0YW5jZSoqOiBtYXRjaGVyIHJvdXRlJ2xhcsSxIHnDtm5ldGlyOyBrZXkgYG1mZToke3JlbW90ZU5hbWV9OiR7dGFiSWR9YC5cclxuICogICBUYWIga2FwYW7EsXAgYG1mZTp3ZG06YWJjMTIzYCBzaWxpbmRpxJ9pbmRlIGVmZmVjdCByZW1vdGUnYSBhaXQgYCMwL3dkbS8uLi5gXHJcbiAqICAgcHJlZml4J2xpIFZFIGBfdGFiPWFiYzEyM2AgacOnZXJlbiBjaGlsZCBrZXknbGVyaSBkZSB0ZW1pemxlci5cclxuICogLSAqKk1GRSBpbnN0YW5jZSoqIChoZXIgcmVtb3RlJ3VuIGtlbmRpIGnDp2luZGUpOiBub3JtYWwgcm91dGUnbGFyIGnDp2luIMOnYWzEscWfxLFyLlxyXG4gKiAgIFF1ZXJ5IHBhcmFtcyB6YXRlbiBgZ2V0Um91dGVLZXlgIMOnxLFrdMSxc8SxbmEgZGFoaWwgb2xkdcSfdSBpw6dpbiB0YWIgaXpvbGFzeW9udVxyXG4gKiAgIGRvxJ9hbCBvbGFyYWsgZ2VsaXIg4oCUIGVrIMO2emVsIGRhdnJhbsSxxZ8gZ2VyZWttZXouXHJcbiAqXHJcbiAqICMjIFBsdWdpbiBjdXN0b20gZWxlbWVudCBwcmVzZXJ2YXRpb25cclxuICogYHByZXNlcnZlQ3VzdG9tRWxlbWVudHNJblRyZWVgIGRldGFjaGVkIHZpZXcnZGFraSBgPG1kLXNlYXJjaD5gIGdpYmkgY3VzdG9tXHJcbiAqIGVsZW1lbnQnbGVyaSBoaWRkZW4gY29udGFpbmVyJ2EgdGHFn8SxciDigJQgZWxlbWVudCBkb2N1bWVudCd0YW4gYXlyxLFsbWF6LCBBbmd1bGFyXHJcbiAqIEVsZW1lbnRzIGBkaXNjb25uZWN0ZWRDYWxsYmFja2AgMTBtcyBkZXN0cm95IHRpbWVyJ8SxIHRldGlrbGVubWV6LCBwbHVnaW5cclxuICogY29tcG9uZW50UmVmJ2kgdmUgZGFoaWxpIHN0YXRlJ2kgKGZvcm0gaW5wdXQsIHNlYXJjaCBzb251w6dsYXLEsSkga29ydW51ci5cclxuICpcclxuICogIyMgQmlsaW5lbiBzxLFuxLFybGFtYWxhclxyXG4gKiAtIGBzaG91bGREZXRhY2hgIG1hdGNoZXIgcm91dGUnbGFyIGnDp2luIHRhYklkIHlva2tlbiBmYWxzZSBkw7ZuZXIgKHRhYnPEsXogb3JwaGFuXHJcbiAqICAgaGFuZGxlIG9sdcWfbWFzxLFuKSwgZGnEn2VyIGR1cnVtbGFyZGEgYHRydWVgIOKAlCB0w7xtIGRldGFjaGVkIHJvdXRlJ2xhciBjYWNoZSdsZW5pci5cclxuICogICBNZW1vcnkgbGVhaydpIHPEsW7EsXJsYW1hayBpw6dpbiBgTmF2U2VydmljZWAgdGFiIHNhecSxc8SxbsSxIGBNQVhfVEFCU2AgaWxlIGvEsXPEsXRsYXJcclxuICogICB2ZSBzb24gdGFiIGthcGF0xLFsZMSxxJ/EsW5kYSBlZmZlY3QgcmVtb3RlJ3VuIHTDvG0gY2hpbGQga2V5J2xlcmluaSB0ZW1pemxlci4gWWluZSBkZVxyXG4gKiAgIHRhYiBpw6dpbmRlIGHDp8SxbGFuIGRldGFpbC9lZGl0IGdpYmkgcGFyYW1ldHJlbGkgYWx0IHNheWZhbGFyxLFuIGhhbmRsZSdsYXLEsSwgcGFyZW50XHJcbiAqICAgdGFiIGthcGFuYW5hIGthZGFyIHN0b3JlZFJvdXRlcyd0YSBrYWzEsXIuXHJcbiAqL1xyXG5ASW5qZWN0YWJsZSh7XHJcblx0cHJvdmlkZWRJbjogJ3Jvb3QnXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBDdXN0b21Sb3V0ZVJldXNlU3RyYXRlZ3kgaW1wbGVtZW50cyBSb3V0ZVJldXNlU3RyYXRlZ3ksIE9uRGVzdHJveSB7XHJcblx0cm91dGVDbG9zZVNlcnZpY2U6IFJvdXRlQ2xvc2VTZXJ2aWNlID0gaW5qZWN0KFJvdXRlQ2xvc2VTZXJ2aWNlKTtcclxuXHRzdG9yZWRSb3V0ZXM6IFJlY29yZDxzdHJpbmcsIERldGFjaGVkUm91dGVIYW5kbGU+ID0ge307XHJcblxyXG5cdC8qKlxyXG5cdCAqIGAoZGV0YWNoZWRUcmVlIGFzIGFueSkuX19hcmlsUHJlc2VydmVkQ3VzdG9tRWxzID0gWy4uLl1gIHJ1bnRpbWUgcHJvcGVydHlcclxuXHQgKiBla2xlbWVrIHllcmluZSBoYW5kbGUnxLEga2V5IG9sYXJhayBrdWxsYW5hbiBiaXIgV2Vha01hcCB0dXR1eW9ydXog4oCUXHJcblx0ICogdHlwZS1zYWZlIHZlIGhhbmRsZSBHQyd5ZSB1xJ9yYWTEscSfxLFuZGEgYmVsbGVrIG90b21hdGlrIHRlbWl6bGVuaXIuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBwcmVzZXJ2ZWRDdXN0b21FbHMgPSBuZXcgV2Vha01hcDxEZXRhY2hlZFJvdXRlSGFuZGxlLCBQcmVzZXJ2ZWRDdXN0b21FbFtdPigpO1xyXG5cclxuXHRjb25zdHJ1Y3RvcigpIHtcclxuXHRcdGVmZmVjdCgoKSA9PiB7XHJcblx0XHRcdGNvbnN0IGtleSA9IHRoaXMucm91dGVDbG9zZVNlcnZpY2UuZGVsZXRlUm91dGUoKS5rZXk7XHJcblx0XHRcdGlmICgha2V5KSByZXR1cm47XHJcblx0XHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIGNsZWFudXAgdGV0aWtsZW5kaSBrZXk9XCIke2tleX1cIiBzdG9yZWRLZXlzPVske09iamVjdC5rZXlzKHRoaXMuc3RvcmVkUm91dGVzKS5qb2luKCcsJyl9XWApO1xyXG5cdFx0XHR0aGlzLnByb2Nlc3NDbGVhbnVwS2V5KGtleSk7XHJcblxyXG5cdFx0XHQvLyBDcm9zcy1pbmplY3RvciB5YXnEsW46IGBSb3V0ZUNsb3NlU2VydmljZWAgYHByb3ZpZGVkSW46ICdyb290J2Agb2xkdcSfdSBpw6dpbiBob3N0IHZlXHJcblx0XHRcdC8vIGhlciBNRkUgcmVtb3RlJ3VuZGEgQVlSSSBpbnN0YW5jZSB5YcWfYXIuIEhvc3QndGFuIHRldGlrbGVuZW4gY2xlYW51cCBNRkUgc3RyYXRlZ3knc2luaVxyXG5cdFx0XHQvLyBvdG9tYXRpayB1eWFuZMSxcm1hei4gV2luZG93IGV2ZW50IGlsZSBzaW55YWwgeWF5xLFubGF5xLFwIE1GRSBzdHJhdGVneSdzaW5pbiBrZW5kaVxyXG5cdFx0XHQvLyBgc3RvcmVkUm91dGVzYCd1bnUgdGVtaXpsZW1lc2luaSBzYcSfbMSxeW9ydXogKGFrc2kgdGFrZGlyZGUgTUZFJ2RlIGhpZGRlbiBjb250YWluZXInYVxyXG5cdFx0XHQvLyB0YcWfxLFubcSxxZ8gcGx1Z2luIGVsZW1lbnQnbGVyaSBvcnBoYW4ga2FsxLFyKS5cclxuXHRcdFx0aWYgKGtleS5zdGFydHNXaXRoKCdtZmU6JykpIHtcclxuXHRcdFx0XHRjb25zdCBwYXJzZWQgPSB0aGlzLnBhcnNlTWF0Y2hlcktleShrZXkpO1xyXG5cdFx0XHRcdGlmIChwYXJzZWQpIHtcclxuXHRcdFx0XHRcdHdpbmRvdy5kaXNwYXRjaEV2ZW50KFxyXG5cdFx0XHRcdFx0XHRuZXcgQ3VzdG9tRXZlbnQoJ2FyaWw6cmVtb3RlLWNsZWFudXAnLCB7XHJcblx0XHRcdFx0XHRcdFx0ZGV0YWlsOiB7IHJlbW90ZU5hbWU6IHBhcnNlZC5yZW1vdGVOYW1lLCB0YWJJZDogcGFyc2VkLnRhYklkIH1cclxuXHRcdFx0XHRcdFx0fSlcclxuXHRcdFx0XHRcdCk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9KTtcclxuXHJcblx0XHQvLyBLYXLFn8SxIHnDtm5kZSAoY3Jvc3MtaW5qZWN0b3IpOiBob3N0IHN0cmF0ZWd5J25pbiB5YXnEsW5sYWTEscSfxLEgYGFyaWw6cmVtb3RlLWNsZWFudXBgXHJcblx0XHQvLyBldmVudCdpbmkgaGVyIE1GRSBzdHJhdGVneSBpbnN0YW5jZSfEsSB5YWthbGFyLCBrZW5kaSBgc3RvcmVkUm91dGVzYCd1bmRha2kgbyByZW1vdGUnYVxyXG5cdFx0Ly8gKHZlIHZhcnNhIG8gdGFiSWQneWUpIGFpdCBrZXknbGVyaSB0ZW1pemxlci4gSG9zdCBzdHJhdGVneSBkZSBidSBsaXN0ZW5lcidhIHNhaGlwXHJcblx0XHQvLyBhbWEga2VuZGkgYCMwLyR7cmVtb3RlTmFtZX0vLi4uYCBrZXknbGVyaSBvbG1hZMSxxJ/EsSBpw6dpbiBuby1vcCDDp2FsxLHFn8SxciAoZ8O8dmVubGkpLlxyXG5cdFx0Y29uc3QgY2xlYW51cEhhbmRsZXIgPSAoZTogRXZlbnQpOiB2b2lkID0+IHtcclxuXHRcdFx0Y29uc3QgZGV0YWlsID0gKGUgYXMgQ3VzdG9tRXZlbnQ8eyByZW1vdGVOYW1lPzogc3RyaW5nOyB0YWJJZD86IHN0cmluZyB8IG51bGwgfT4pLmRldGFpbDtcclxuXHRcdFx0Y29uc3QgcmVtb3RlTmFtZSA9IGRldGFpbD8ucmVtb3RlTmFtZTtcclxuXHRcdFx0aWYgKCFyZW1vdGVOYW1lKSByZXR1cm47XHJcblx0XHRcdGNvbnN0IHRhYklkID0gZGV0YWlsPy50YWJJZCA/PyBudWxsO1xyXG5cdFx0XHRjb25zdCBwcmVmaXggPSBgIzAvJHtyZW1vdGVOYW1lfWA7XHJcblx0XHRcdGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyh0aGlzLnN0b3JlZFJvdXRlcykuZmlsdGVyKChrKSA9PiB7XHJcblx0XHRcdFx0aWYgKCFrLnN0YXJ0c1dpdGgocHJlZml4KSkgcmV0dXJuIGZhbHNlO1xyXG5cdFx0XHRcdC8vIHRhYklkIHZlcmlsZGl5c2UgU0FERUNFIG8gdGFiJ2EgYWl0IGNoaWxkIGhhbmRsZSdsYXLEsSB0ZW1pemxlLiBBa3NpIHRha2RpcmRlXHJcblx0XHRcdFx0Ly8gYXluxLEgcmVtb3RlJ3VuIGJhxZ9rYSB0YWInbGVyaW5pbiBzdGF0ZSdpIGRlIHlhbmzEscWfbMSxa2xhIHNpbGluaXIuXHJcblx0XHRcdFx0aWYgKHRhYklkICYmICFrLmluY2x1ZGVzKGBfdGFiPSR7dGFiSWR9YCkpIHJldHVybiBmYWxzZTtcclxuXHRcdFx0XHRyZXR1cm4gdHJ1ZTtcclxuXHRcdFx0fSk7XHJcblx0XHRcdGlmICgha2V5cy5sZW5ndGgpIHJldHVybjtcclxuXHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gY3Jvc3MtaW5qZWN0b3IgY2xlYW51cCByZW1vdGVOYW1lPVwiJHtyZW1vdGVOYW1lfVwiIHRhYklkPVwiJHt0YWJJZCA/PyAnKid9XCIga2V5cz1bJHtrZXlzLmpvaW4oJywnKX1dYCk7XHJcblx0XHRcdGZvciAoY29uc3QgayBvZiBrZXlzKSB7XHJcblx0XHRcdFx0dGhpcy5jbGVhbnVwU3RvcmVkSGFuZGxlKHRoaXMuc3RvcmVkUm91dGVzW2tdKTtcclxuXHRcdFx0XHRkZWxldGUgdGhpcy5zdG9yZWRSb3V0ZXNba107XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignYXJpbDpyZW1vdGUtY2xlYW51cCcsIGNsZWFudXBIYW5kbGVyKTtcclxuXHRcdC8vIFN0cmF0ZWd5IGBwcm92aWRlZEluOiAncm9vdCdgIOKGkiByb290IGluamVjdG9yIGRlc3Ryb3kgZWRpbGRpxJ9pbmRlIGxpc3RlbmVyJ8SxIGthbGTEsXIuXHJcblx0XHQvLyBQcmF0aWt0ZSB1enVuIMO2bcO8cmzDvCBhbWEgdGVzdCBvcnRhbcSxIHZlIGdlbGVjZWt0ZWtpIGlmcmFtZSB2Yi4gbXVsdGktd2luZG93XHJcblx0XHQvLyBzZW5hcnlvbGFyZGEgb3JwaGFuIGxpc3RlbmVyIGLEsXJha21hbWFrIGnDp2luIGRlZmVuc2l2ZS5cclxuXHRcdGluamVjdChEZXN0cm95UmVmKS5vbkRlc3Ryb3koKCkgPT4gd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2FyaWw6cmVtb3RlLWNsZWFudXAnLCBjbGVhbnVwSGFuZGxlcikpO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogSW5qZWN0b3IgZGVzdHJveSBlZGlsZGnEn2luZGUg4oCUIGhpYnJpdCBtb2RkYSBoZXIgdGFiJ8SxbiBjaGlsZCBgRW52aXJvbm1lbnRJbmplY3RvcmAnxLEgdGFiXHJcblx0ICoga2FwYW7EsW5jYSBgZGVzdHJveSgpYCBlZGlsaXI7IGJ1IHN0cmF0ZWd5IGBwcm92aWRlZEluOidyb290J2Agb2xkdcSfdW5kYW4gY2hpbGQgaW5qZWN0b3InZGFcclxuXHQgKiAocm9vdC1zY29wZSkgbWF0ZXJpYWxpemUgb2xtdcWfIGluc3RhbmNlIGRhIHnEsWvEsWzEsXIg4oCUIGBzdG9yZWRSb3V0ZXNgJ3RhIGthbGFuIFTDnE0gZGV0YWNoZWRcclxuXHQgKiBoYW5kbGUnbGFyxLEgdGVtaXpsZXIuIEFrc2kgaGFsZGUgYHByZXNlcnZlQ3VzdG9tRWxlbWVudHNJblRyZWVgIGlsZSBoaWRkZW4gY29udGFpbmVyJ2FcclxuXHQgKiB0YcWfxLFubcSxxZ8gcGx1Z2luIGN1c3RvbSBlbGVtZW50J2xlcmkgKMO2cm4uIGA8bWQtc2VhcmNoPmApIG9ycGhhbiBrYWzEsXIgKG1lbW9yeSBsZWFrKTogTUZFXHJcblx0ICogaW50ZXJuYWwgcm91dGUga2V5J2xlcmkgYF90YWJgIHRhxZ/EsW1hZMSxxJ/EsSBpw6dpbiBgYXJpbDpyZW1vdGUtY2xlYW51cGAgY3Jvc3MtaW5qZWN0b3IgdGVtaXpsacSfaVxyXG5cdCAqIG9ubGFyYSB1bGHFn2FtYXouIEluamVjdG9yLWRlc3Ryb3kgYmF6bMSxIGJ1IHPDvHDDvHJtZSBnYXJhbnRpbGkgdmUgaWRlbXBvdGVudHRpci5cclxuXHQgKi9cclxuXHRuZ09uRGVzdHJveSgpOiB2b2lkIHtcclxuXHRcdGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyh0aGlzLnN0b3JlZFJvdXRlcyk7XHJcblx0XHRpZiAoa2V5cy5sZW5ndGgpIGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIG5nT25EZXN0cm95IOKGkiAke2tleXMubGVuZ3RofSBzdG9yZWQgaGFuZGxlIHRlbWl6bGVuaXlvciAodGFiIGthcGFuZMSxKWApO1xyXG5cdFx0Zm9yIChjb25zdCBrZXkgb2Yga2V5cykge1xyXG5cdFx0XHR0aGlzLmNsZWFudXBTdG9yZWRIYW5kbGUodGhpcy5zdG9yZWRSb3V0ZXNba2V5XSk7XHJcblx0XHRcdGRlbGV0ZSB0aGlzLnN0b3JlZFJvdXRlc1trZXldO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogVmVyaWxlbiBrZXkgacOnaW4gZGlyZWN0ICsgY2hpbGQgY2xlYW51cCB1eWd1bGFyLiBFZmZlY3QgacOnaW5kZW4gYXlyxLHFn3TEsXLEsWxtxLHFnyBoZWxwZXIuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBwcm9jZXNzQ2xlYW51cEtleShrZXk6IHN0cmluZyk6IHZvaWQge1xyXG5cdFx0Ly8gRG/En3J1ZGFuIGXFn2xlxZ9lbiBrZXkgdmFyc2Egb251IHNpbC5cclxuXHRcdGlmICh0aGlzLnN0b3JlZFJvdXRlc1trZXldKSB7XHJcblx0XHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIGNsZWFudXAg4oaSIGRpcmVjdCBtYXRjaCBcIiR7a2V5fVwiYCk7XHJcblx0XHRcdHRoaXMuY2xlYW51cFN0b3JlZEhhbmRsZSh0aGlzLnN0b3JlZFJvdXRlc1trZXldKTtcclxuXHRcdFx0ZGVsZXRlIHRoaXMuc3RvcmVkUm91dGVzW2tleV07XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gTWF0Y2hlciBrZXkgc2lsaW5kacSfaW5kZSAoYG1mZTp3ZG06YWJjYCBnaWJpKSBvIHJlbW90ZSdhIGFpdCDigJQgdmFyc2EgbyB0YWInYSBhaXQg4oCUXHJcblx0XHQvLyBpbnRlcm5hbCByb3V0ZSBoYW5kbGUnbGFyxLFuxLEgZGEgdGVtaXpsZS4gQ29udmVudGlvbjogbWF0Y2hlciBrZXlcclxuXHRcdC8vIGBtZmU6JHtyZW1vdGVOYW1lfWAgdmV5YSBgbWZlOiR7cmVtb3RlTmFtZX06JHt0YWJJZH1gLCBub3JtYWwga2V5J2xlclxyXG5cdFx0Ly8gYCMwLyR7cmVtb3RlTmFtZX0vLi4uPy4uLiZfdGFiPSR7dGFiSWR9YCBmb3JtYXTEsW5kYS5cclxuXHRcdGlmIChrZXkuc3RhcnRzV2l0aCgnbWZlOicpKSB7XHJcblx0XHRcdGNvbnN0IHBhcnNlZCA9IHRoaXMucGFyc2VNYXRjaGVyS2V5KGtleSk7XHJcblx0XHRcdGlmICghcGFyc2VkKSByZXR1cm47XHJcblx0XHRcdGNvbnN0IHsgcmVtb3RlTmFtZSwgdGFiSWQgfSA9IHBhcnNlZDtcclxuXHRcdFx0Y29uc3QgcHJlZml4ID0gYCMwLyR7cmVtb3RlTmFtZX1gO1xyXG5cdFx0XHRmb3IgKGNvbnN0IGsgb2YgT2JqZWN0LmtleXModGhpcy5zdG9yZWRSb3V0ZXMpKSB7XHJcblx0XHRcdFx0aWYgKCFrLnN0YXJ0c1dpdGgocHJlZml4KSkgY29udGludWU7XHJcblx0XHRcdFx0Ly8gdGFiSWQgdmVyaWxkaXlzZSBzYWRlY2UgbyB0YWInYSBhaXQgY2hpbGQgaGFuZGxlJ2xhcsSxIHRlbWl6bGVcclxuXHRcdFx0XHRpZiAodGFiSWQgJiYgIWsuaW5jbHVkZXMoYF90YWI9JHt0YWJJZH1gKSkgY29udGludWU7XHJcblx0XHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gY2xlYW51cCDihpIgY2hpbGQgbWF0Y2ggXCIke2t9XCJgKTtcclxuXHRcdFx0XHR0aGlzLmNsZWFudXBTdG9yZWRIYW5kbGUodGhpcy5zdG9yZWRSb3V0ZXNba10pO1xyXG5cdFx0XHRcdGRlbGV0ZSB0aGlzLnN0b3JlZFJvdXRlc1trXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogYG1mZToke3JlbW90ZU5hbWV9YCB2ZXlhIGBtZmU6JHtyZW1vdGVOYW1lfToke3RhYklkfWAgZm9ybWF0xLFuZGFraSBtYXRjaGVyIGtleSdpIHBhcnNlIGVkZXIuXHJcblx0ICogdGFiSWQgeW9rc2EgYG51bGxgIGTDtm5lciAoZ2VyaXllIGTDtm7DvGsgdXl1bWx1bHVrOiBsZWdhY3kgw6dhxJ9yxLFsYXIgdGFiSWQgdmVybWV5ZWJpbGlyKS5cclxuXHQgKi9cclxuXHRwcml2YXRlIHBhcnNlTWF0Y2hlcktleShrZXk6IHN0cmluZyk6IHsgcmVtb3RlTmFtZTogc3RyaW5nOyB0YWJJZDogc3RyaW5nIHwgbnVsbCB9IHwgbnVsbCB7XHJcblx0XHRpZiAoIWtleS5zdGFydHNXaXRoKCdtZmU6JykpIHJldHVybiBudWxsO1xyXG5cdFx0Y29uc3QgdGFpbCA9IGtleS5zdWJzdHJpbmcoNCk7XHJcblx0XHRjb25zdCBjb2xvbklkeCA9IHRhaWwuaW5kZXhPZignOicpO1xyXG5cdFx0cmV0dXJuIHtcclxuXHRcdFx0cmVtb3RlTmFtZTogY29sb25JZHggPT09IC0xID8gdGFpbCA6IHRhaWwuc3Vic3RyaW5nKDAsIGNvbG9uSWR4KSxcclxuXHRcdFx0dGFiSWQ6IGNvbG9uSWR4ID09PSAtMSA/IG51bGwgOiB0YWlsLnN1YnN0cmluZyhjb2xvbklkeCArIDEpXHJcblx0XHR9O1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogU3RvcmVkIGhhbmRsZSfEsSB0YW1hbWVuIHRlbWl6bGU6IGhpZGRlbiBjb250YWluZXInZGFraSBwcmVzZXJ2ZWQgZWxlbWVudCdsZXJpIChwbHVnaW5cclxuXHQgKiBjdXN0b20gZWxlbWVudCdsZXJpICsgQXJpbFdlYkNvbXBvbmVudFdyYXBwZXInxLFuIGA8YXBwLXh4eD5gIGVsZW1lbnQnaSkgRE9NJ2RhbiBrYWxkxLFyLFxyXG5cdCAqIHNvbnJhIEFuZ3VsYXIgYGNvbXBvbmVudFJlZi5kZXN0cm95KClgIGlsZSB2aWV3IHRyZWUnc2luaSBkZXN0cm95IGV0LlxyXG5cdCAqXHJcblx0ICogYGNvbXBvbmVudFJlZi5kZXN0cm95KClgIHRlayBiYcWfxLFuYSB5ZXRlcmxpIGRlxJ9pbDogQW5ndWxhciBzYWRlY2UgdmlldyB0cmVlJ3NpbmkgYmlsaXIsXHJcblx0ICogYml6aW0gYHByZXNlcnZlQ3VzdG9tRWxlbWVudHNJblRyZWVgIHZlIGBwcmVzZXJ2ZVdlYkNvbXBvbmVudEVsZW1lbnRgIGlsZSBoaWRkZW5cclxuXHQgKiBjb250YWluZXInYSBtYW51ZWwgdGHFn8SxbmFuIGVsZW1lbnQnbGVyZGVuIGhhYmVyaSB5b2t0dXIg4oaSIG9ubGFyIG9ycGhhbiBrYWzEsXIuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBjbGVhbnVwU3RvcmVkSGFuZGxlKGhhbmRsZTogRGV0YWNoZWRSb3V0ZUhhbmRsZSB8IG51bGwgfCB1bmRlZmluZWQpOiB2b2lkIHtcclxuXHRcdGlmICghaGFuZGxlKSByZXR1cm47IC8vIEFuZ3VsYXIgUm91dGVyJ8SxbiBgc3RvcmUocm91dGUsIG51bGwpYCBpbGUgYsSxcmFrdMSxxJ/EsSBvcnBoYW4ga2V5J2xlciBpw6dpbiBkZWZlbnNpdmUgZ3VhcmQuXHJcblx0XHRjb25zdCBoaWRkZW5Db250YWluZXIgPSB0aGlzLmdldEhpZGRlbkNvbnRhaW5lcigpO1xyXG5cclxuXHRcdC8vIDEuIHByZXNlcnZlQ3VzdG9tRWxlbWVudHNJblRyZWUgaWxlIHRhxZ/EsW5hbiBwbHVnaW4gZWxlbWVudCdsZXJpICjDtnJuLiA8bWQtc2VhcmNoPilcclxuXHRcdGNvbnN0IHByZXNlcnZlZCA9IHRoaXMucHJlc2VydmVkQ3VzdG9tRWxzLmdldChoYW5kbGUpO1xyXG5cdFx0aWYgKHByZXNlcnZlZCkge1xyXG5cdFx0XHRmb3IgKGNvbnN0IHsgZWwgfSBvZiBwcmVzZXJ2ZWQpIHtcclxuXHRcdFx0XHRpZiAoaGlkZGVuQ29udGFpbmVyLmNvbnRhaW5zKGVsKSkge1xyXG5cdFx0XHRcdFx0aGlkZGVuQ29udGFpbmVyLnJlbW92ZUNoaWxkKGVsKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5wcmVzZXJ2ZWRDdXN0b21FbHMuZGVsZXRlKGhhbmRsZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gMi4gcHJlc2VydmVXZWJDb21wb25lbnRFbGVtZW50IGlsZSB0YcWfxLFuYW4gd3JhcHBlciBlbGVtZW50J2kgKMO2cm4uIDxhcHAtd2RtPilcclxuXHRcdGNvbnN0IGNvbXBvbmVudFJlZiA9IChoYW5kbGUgYXMgSW50ZXJuYWxEZXRhY2hlZFJvdXRlSGFuZGxlKS5jb21wb25lbnRSZWY7XHJcblx0XHRjb25zdCBpbnN0YW5jZSA9IGNvbXBvbmVudFJlZj8uaW5zdGFuY2UgYXMgV3JhcHBlckluc3RhbmNlIHwgdW5kZWZpbmVkO1xyXG5cdFx0aWYgKGluc3RhbmNlPy5lbGVtZW50KSB7XHJcblx0XHRcdC8vIEZsYWcnaSB0ZW1pemxlIGtpIHJlbW92ZUNoaWxkIHNvbnJhc8SxIHRldGlrbGVuZW4gZGlzY29ubmVjdGVkQ2FsbGJhY2sgQXBwbGljYXRpb24nxLFcclxuXHRcdFx0Ly8gZ2Vyw6dla3RlbiBkZXN0cm95IGV0c2luIOKAlCBha3NpIGhhbGRlIE1GRUFwcEVsZW1lbnQgb3JwaGFuIGthbMSxciAobWVtb3J5IGxlYWspLlxyXG5cdFx0XHRkZWxldGUgKGluc3RhbmNlLmVsZW1lbnQgYXMgSFRNTEVsZW1lbnQgJiB7IF9fYXJpbFByZXNlcnZlRHVyaW5nRGV0YWNoPzogYm9vbGVhbiB9KS5fX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaDtcclxuXHRcdFx0aWYgKGhpZGRlbkNvbnRhaW5lci5jb250YWlucyhpbnN0YW5jZS5lbGVtZW50KSkge1xyXG5cdFx0XHRcdGhpZGRlbkNvbnRhaW5lci5yZW1vdmVDaGlsZChpbnN0YW5jZS5lbGVtZW50KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIDMuIEFuZ3VsYXIgdmlldyB0cmVlJ3NpbmkgZGVzdHJveSBldFxyXG5cdFx0Y29tcG9uZW50UmVmPy5kZXN0cm95KCk7XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBNYXRjaGVyIChNRkUpIHJvdXRlJ2xhciBpw6dpbiB0YWJJZCB6b3J1bmx1LiBgZ2V0VGFiSWRGcm9tUm91dGVgIFVSTCBxdWVyeVBhcmFtcyArXHJcblx0ICogaGlzdG9yeS5zdGF0ZSdpIG9rdXIuIEhpw6diaXIga2F5bmFrIHRhYklkIHZlcm1lenNlIChpbGsgbmF2aWdhdGlvbiBpbnRlcmltJ2kpIGRldGFjaFxyXG5cdCAqIGVkaWxtZXo7IGBmaXJzdENoZWNrRm9yUm91dGVgIGBfdGFiYCBla2xleWlwIHllbmlkZW4gbmF2aWdhdGUgZWRlY2VrLlxyXG5cdCAqL1xyXG5cdHNob3VsZERldGFjaChyb3V0ZTogQWN0aXZhdGVkUm91dGVTbmFwc2hvdCk6IGJvb2xlYW4ge1xyXG5cdFx0aWYgKHRoaXMuaXNNYXRjaGVyUm91dGUocm91dGUpICYmICF0aGlzLmdldFRhYklkRnJvbVJvdXRlKHJvdXRlKSkge1xyXG5cdFx0XHRjb25zb2xlLmxvZyhgW1N0cmF0ZWd5XSBzaG91bGREZXRhY2gga2V5PVwiJHt0aGlzLmdldFJvdXRlS2V5KHJvdXRlKX1cIiDihpIgZmFsc2UgKG1hdGNoZXIgcm91dGUsIHRhYklkIHlvaylgKTtcclxuXHRcdFx0cmV0dXJuIGZhbHNlO1xyXG5cdFx0fVxyXG5cdFx0Y29uc3Qgcm91dGVLZXkgPSB0aGlzLmdldFJvdXRlS2V5KHJvdXRlKTtcclxuXHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIHNob3VsZERldGFjaCBrZXk9XCIke3JvdXRlS2V5fVwiIOKGkiB0cnVlYCk7XHJcblx0XHRyZXR1cm4gdHJ1ZTtcclxuXHR9XHJcblxyXG5cdHN0b3JlKHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90LCBkZXRhY2hlZFRyZWU6IERldGFjaGVkUm91dGVIYW5kbGUpOiB2b2lkIHtcclxuXHRcdGNvbnN0IHJvdXRlS2V5ID0gdGhpcy5nZXRSb3V0ZUtleShyb3V0ZSk7XHJcblx0XHRjb25zb2xlLmxvZyhgW1N0cmF0ZWd5XSBzdG9yZSBrZXk9XCIke3JvdXRlS2V5fVwiIGhhbmRsZT1gLCAhIWRldGFjaGVkVHJlZSk7XHJcblx0XHRpZiAoIWRldGFjaGVkVHJlZSkge1xyXG5cdFx0XHRkZWxldGUgdGhpcy5zdG9yZWRSb3V0ZXNbcm91dGVLZXldO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblx0XHRpZiAodGhpcy5pc01hdGNoZXJSb3V0ZShyb3V0ZSkgJiYgIXRoaXMuZ2V0VGFiSWRGcm9tUm91dGUocm91dGUpKSB7XHJcblx0XHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIHN0b3JlIFNLSVAga2V5PVwiJHtyb3V0ZUtleX1cIiAobWF0Y2hlciByb3V0ZSwgdGFiSWQgeW9rKWApO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblx0XHR0aGlzLnN0b3JlZFJvdXRlc1tyb3V0ZUtleV0gPSBkZXRhY2hlZFRyZWU7XHJcblx0XHR0aGlzLnByZXNlcnZlV2ViQ29tcG9uZW50RWxlbWVudChkZXRhY2hlZFRyZWUpO1xyXG5cdFx0dGhpcy5wcmVzZXJ2ZUN1c3RvbUVsZW1lbnRzSW5UcmVlKGRldGFjaGVkVHJlZSk7XHJcblx0fVxyXG5cclxuXHRzaG91bGRBdHRhY2gocm91dGU6IEFjdGl2YXRlZFJvdXRlU25hcHNob3QpOiBib29sZWFuIHtcclxuXHRcdGlmICh0aGlzLmlzTWF0Y2hlclJvdXRlKHJvdXRlKSAmJiAhdGhpcy5nZXRUYWJJZEZyb21Sb3V0ZShyb3V0ZSkpIHtcclxuXHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gc2hvdWxkQXR0YWNoIGtleT1cIiR7dGhpcy5nZXRSb3V0ZUtleShyb3V0ZSl9XCIg4oaSIGZhbHNlIChtYXRjaGVyIHJvdXRlLCB0YWJJZCB5b2spYCk7XHJcblx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdH1cclxuXHRcdGNvbnN0IHJvdXRlS2V5ID0gdGhpcy5nZXRSb3V0ZUtleShyb3V0ZSk7XHJcblx0XHRjb25zdCBzaG91bGRBdHRhY2ggPSAhIXRoaXMuc3RvcmVkUm91dGVzW3JvdXRlS2V5XTtcclxuXHRcdGNvbnNvbGUubG9nKGBbU3RyYXRlZ3ldIHNob3VsZEF0dGFjaCBrZXk9XCIke3JvdXRlS2V5fVwiIOKGkiAke3Nob3VsZEF0dGFjaH0gKHN0b3JlZEtleXM9WyR7T2JqZWN0LmtleXModGhpcy5zdG9yZWRSb3V0ZXMpLmpvaW4oJywnKX1dKWApO1xyXG5cdFx0cmV0dXJuIHNob3VsZEF0dGFjaDtcclxuXHR9XHJcblxyXG5cdHJldHJpZXZlKHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90KTogRGV0YWNoZWRSb3V0ZUhhbmRsZSB8IG51bGwge1xyXG5cdFx0aWYgKHRoaXMuaXNNYXRjaGVyUm91dGUocm91dGUpICYmICF0aGlzLmdldFRhYklkRnJvbVJvdXRlKHJvdXRlKSkge1xyXG5cdFx0XHRyZXR1cm4gbnVsbDtcclxuXHRcdH1cclxuXHRcdGNvbnN0IHJvdXRlS2V5ID0gdGhpcy5nZXRSb3V0ZUtleShyb3V0ZSk7XHJcblx0XHRjb25zdCBoYW5kbGUgPSB0aGlzLnN0b3JlZFJvdXRlc1tyb3V0ZUtleV07XHJcblx0XHRjb25zb2xlLmxvZyhgW1N0cmF0ZWd5XSByZXRyaWV2ZSBrZXk9XCIke3JvdXRlS2V5fVwiIOKGkiAke2hhbmRsZSA/ICdIQVMnIDogJ05VTEwnfWApO1xyXG5cdFx0aWYgKGhhbmRsZSkge1xyXG5cdFx0XHR0aGlzLnJlc3RvcmVXZWJDb21wb25lbnRFbGVtZW50KGhhbmRsZSk7XHJcblx0XHRcdHRoaXMucmVzdG9yZUN1c3RvbUVsZW1lbnRzSW5UcmVlKGhhbmRsZSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gaGFuZGxlIHx8IG51bGw7XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBXZWIgY29tcG9uZW50IGVsZW1lbnQnaW5pIGhpZGRlbiBjb250YWluZXInYSB0YcWfxLFyIChzdGF0ZSBrb3J1bm1hc8SxIGnDp2luKS5cclxuXHQgKlxyXG5cdCAqIEVsZW1lbnQnZSBgX19hcmlsUHJlc2VydmVEdXJpbmdEZXRhY2hgIGZsYWcnaSB5YXrEsWzEsXI6IGByZW1vdmVDaGlsZGAgKyBgYXBwZW5kQ2hpbGRgXHJcblx0ICogYXJhc8SxbmRhIE1GRSBjdXN0b20gZWxlbWVudCdpbiBgZGlzY29ubmVjdGVkQ2FsbGJhY2tgJ2kgdGV0aWtsZW5peW9yOyBmbGFnIG9yYWRha2lcclxuXHQgKiBkZXN0cm95IHRpbWVyJ8SxbsSxICoqZGV0ZXJtaW5pc3RpYyoqIMWfZWtpbGRlIGJ5cGFzcyBlZGVyLiBFc2tpZGVuIDUwbXMgaGFyZGNvZGVkIHRpbWVyXHJcblx0ICogeWFyxLHFnyBrb8WfdWx1IMO8cmV0aXlvcmR1ICh5YXZhxZ8gY2loYXpsYXJkYSBBcHBsaWNhdGlvbiBpc3RlbXNpeiBkZXN0cm95IG9sdXlvcmR1KS5cclxuXHQgKi9cclxuXHRwcml2YXRlIHByZXNlcnZlV2ViQ29tcG9uZW50RWxlbWVudChkZXRhY2hlZFRyZWU6IERldGFjaGVkUm91dGVIYW5kbGUpOiB2b2lkIHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdGNvbnN0IGNvbXBvbmVudFJlZiA9IChkZXRhY2hlZFRyZWUgYXMgSW50ZXJuYWxEZXRhY2hlZFJvdXRlSGFuZGxlKS5jb21wb25lbnRSZWY7XHJcblx0XHRcdGlmICghY29tcG9uZW50UmVmKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBjb21wb25lbnRJbnN0YW5jZSA9IGNvbXBvbmVudFJlZi5pbnN0YW5jZSBhcyBXcmFwcGVySW5zdGFuY2UgfCB1bmRlZmluZWQ7XHJcblx0XHRcdGlmICghY29tcG9uZW50SW5zdGFuY2U/LmVsZW1lbnQpIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IGVsZW1lbnQgPSBjb21wb25lbnRJbnN0YW5jZS5lbGVtZW50IGFzIEhUTUxFbGVtZW50ICYgeyBfX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaD86IGJvb2xlYW4gfTtcclxuXHRcdFx0Y29uc3QgaGlkZGVuQ29udGFpbmVyID0gdGhpcy5nZXRIaWRkZW5Db250YWluZXIoKTtcclxuXHJcblx0XHRcdC8vIEZsYWcg4oCUIGRpc2Nvbm5lY3RlZENhbGxiYWNrIGJ1bnUgb2t1eXVwIGRlc3Ryb3knxLEgYXRsYXlhY2FrLiByZW1vdmVDaGlsZCd0YW5cclxuXHRcdFx0Ly8gw5ZOQ0Ugc2V0IGVkaWxpeW9yIGtpIGNhbGxiYWNrIGlsayB0ZXRpa2xlbmRpxJ9pbmRlIGZsYWcgeWVyaW5kZSBvbHN1bi5cclxuXHRcdFx0ZWxlbWVudC5fX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaCA9IHRydWU7XHJcblxyXG5cdFx0XHQvLyBFxJ9lciBlbGVtZW50IHphdGVuIGJpciBwYXJlbnQnYSBzYWhpcHNlLCBvbmRhbiBhecSxclxyXG5cdFx0XHRpZiAoZWxlbWVudC5wYXJlbnROb2RlICYmIGVsZW1lbnQucGFyZW50Tm9kZSAhPT0gaGlkZGVuQ29udGFpbmVyKSB7XHJcblx0XHRcdFx0ZWxlbWVudC5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGVsZW1lbnQpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBIaWRkZW4gY29udGFpbmVyJ2EgZWtsZSAoZcSfZXIgemF0ZW4gb3JhZGEgZGXEn2lsc2UpXHJcblx0XHRcdGlmICghaGlkZGVuQ29udGFpbmVyLmNvbnRhaW5zKGVsZW1lbnQpKSB7XHJcblx0XHRcdFx0aGlkZGVuQ29udGFpbmVyLmFwcGVuZENoaWxkKGVsZW1lbnQpO1xyXG5cdFx0XHRcdGNvbnNvbGUubG9nKCdXZWIgY29tcG9uZW50IGVsZW1lbnQgaGlkZGVuIGNvbnRhaW5lclxcJ2EgdGHFn8SxbmTEsSAoc3RhdGUga29ydW51eW9yKScpO1xyXG5cdFx0XHR9XHJcblx0XHR9IGNhdGNoIChlcnJvcikge1xyXG5cdFx0XHRjb25zb2xlLndhcm4oJ1dlYiBjb21wb25lbnQgZWxlbWVudCBwcmVzZXJ2ZSBlZGlsaXJrZW4gaGF0YTonLCBlcnJvcik7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBXZWIgY29tcG9uZW50IGVsZW1lbnQnaW5pIGhpZGRlbiBjb250YWluZXInZGFuIMOnxLFrYXIgdmUgY29tcG9uZW50IHZpZXcnYSBla2xlbWV5aSBkZW5lLlxyXG5cdCAqXHJcblx0ICogYF9fYXJpbFByZXNlcnZlRHVyaW5nRGV0YWNoYCBmbGFnJ2kgdGVtaXpsZW5pciDigJQgZcSfZXIgYnUgcmV0cmlldmUgc29ucmFzxLEgZWxlbWVudFxyXG5cdCAqIGdlcsOnZWsgYW5sYW1kYSBkZXN0cm95IGVkaWxpcnNlIGBkaXNjb25uZWN0ZWRDYWxsYmFja2AgZmxhZyB5b2sga2FidWwgZWRpcCBBcHBsaWNhdGlvblxyXG5cdCAqIGNsZWFudXAnxLFuxLEgecO8csO8dGVjZWsuXHJcblx0ICovXHJcblx0cHJpdmF0ZSByZXN0b3JlV2ViQ29tcG9uZW50RWxlbWVudChkZXRhY2hlZFRyZWU6IERldGFjaGVkUm91dGVIYW5kbGUpOiB2b2lkIHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdGNvbnN0IGNvbXBvbmVudFJlZiA9IChkZXRhY2hlZFRyZWUgYXMgSW50ZXJuYWxEZXRhY2hlZFJvdXRlSGFuZGxlKS5jb21wb25lbnRSZWY7XHJcblx0XHRcdGlmICghY29tcG9uZW50UmVmKSByZXR1cm47XHJcblxyXG5cdFx0XHRjb25zdCBjb21wb25lbnRJbnN0YW5jZSA9IGNvbXBvbmVudFJlZi5pbnN0YW5jZSBhcyBXcmFwcGVySW5zdGFuY2UgfCB1bmRlZmluZWQ7XHJcblx0XHRcdGlmICghY29tcG9uZW50SW5zdGFuY2U/LmVsZW1lbnQpIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IGVsZW1lbnQgPSBjb21wb25lbnRJbnN0YW5jZS5lbGVtZW50IGFzIEhUTUxFbGVtZW50ICYgeyBfX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaD86IGJvb2xlYW4gfTtcclxuXHRcdFx0Y29uc3QgaGlkZGVuQ29udGFpbmVyID0gdGhpcy5nZXRIaWRkZW5Db250YWluZXIoKTtcclxuXHJcblx0XHRcdC8vIEXEn2VyIGVsZW1lbnQgaGlkZGVuIGNvbnRhaW5lcidkYSBpc2UsIMOnxLFrYXJcclxuXHRcdFx0aWYgKGhpZGRlbkNvbnRhaW5lci5jb250YWlucyhlbGVtZW50KSkge1xyXG5cdFx0XHRcdGhpZGRlbkNvbnRhaW5lci5yZW1vdmVDaGlsZChlbGVtZW50KTtcclxuXHRcdFx0XHRkZWxldGUgZWxlbWVudC5fX2FyaWxQcmVzZXJ2ZUR1cmluZ0RldGFjaDtcclxuXHRcdFx0XHRjb25zb2xlLmxvZygnV2ViIGNvbXBvbmVudCBlbGVtZW50IGhpZGRlbiBjb250YWluZXJcXCdkYW4gw6fEsWthcsSxbGTEsScpO1xyXG5cclxuXHRcdFx0XHQvLyBDb21wb25lbnQgdmlldyfEsW4gbmF0aXZlIGVsZW1lbnQnaW5lIGVrbGVtZXlpIGRlbmVcclxuXHRcdFx0XHRjb25zdCB0cnlSZXN0b3JlID0gKCk6IGJvb2xlYW4gPT4ge1xyXG5cdFx0XHRcdFx0Y29uc3QgdmMgPSBjb21wb25lbnRJbnN0YW5jZS52YztcclxuXHRcdFx0XHRcdGlmICh2Yz8ubmF0aXZlRWxlbWVudCkge1xyXG5cdFx0XHRcdFx0XHR2Yy5uYXRpdmVFbGVtZW50LmFwcGVuZENoaWxkKGVsZW1lbnQpO1xyXG5cdFx0XHRcdFx0XHRjb25zb2xlLmxvZygnV2ViIGNvbXBvbmVudCBlbGVtZW50IGNvbXBvbmVudCB2aWV3XFwnYSBla2xlbmRpIChyZXRyaWV2ZSBzxLFyYXPEsW5kYSknKTtcclxuXHRcdFx0XHRcdFx0Ly8gUHJvcHMnbGFyxLEgZ8O8bmNlbGxlbWVrIGnDp2luIGZsYWcgc2V0IGV0IChjb21wb25lbnQga2VuZGkgcG9wdWxhdGVQcm9wcyd1bnUgw6dhxJ/EsXJhY2FrKVxyXG5cdFx0XHRcdFx0XHRjb21wb25lbnRJbnN0YW5jZS5wcm9wc1VwZGF0ZWQgPSBmYWxzZTtcclxuXHRcdFx0XHRcdFx0cmV0dXJuIHRydWU7XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdFx0fTtcclxuXHJcblx0XHRcdFx0Ly8gSGVtZW4gZGVuZVxyXG5cdFx0XHRcdGlmICghdHJ5UmVzdG9yZSgpKSB7XHJcblx0XHRcdFx0XHQvLyBWaWV3IGhlbsO8eiBoYXrEsXIgZGXEn2lsc2UsIGJpciBzb25yYWtpIHRpY2sndGUgdGVrcmFyIGRlbmVcclxuXHRcdFx0XHRcdHNldFRpbWVvdXQoKCkgPT4ge1xyXG5cdFx0XHRcdFx0XHRpZiAoIXRyeVJlc3RvcmUoKSkge1xyXG5cdFx0XHRcdFx0XHRcdC8vIEhhbGEgYmHFn2FyxLFzxLF6c2EsIGZsYWcgc2V0IGV0IChuZ0FmdGVyVmlld0luaXQndGUgdGVrcmFyIGRlbmVuZWNlaylcclxuXHRcdFx0XHRcdFx0XHRjb21wb25lbnRJbnN0YW5jZS5uZWVkc1Jlc3RvcmUgPSB0cnVlO1xyXG5cdFx0XHRcdFx0XHRcdGNvbnNvbGUubG9nKCdXZWIgY29tcG9uZW50IGVsZW1lbnQgcmVzdG9yZSBlZGlsZWNlayAodmlldyBoYXrEsXIgZGXEn2lsLCBuZ0FmdGVyVmlld0luaXRcXCd0ZSB0ZWtyYXIgZGVuZW5lY2VrKScpO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHR9LCAwKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdH1cclxuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XHJcblx0XHRcdGNvbnNvbGUud2FybignV2ViIGNvbXBvbmVudCBlbGVtZW50IHJlc3RvcmUgZWRpbGlya2VuIGhhdGE6JywgZXJyb3IpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogR2xvYmFsIGhpZGRlbiBjb250YWluZXInxLEgb2x1xZ90dXJ1ciB2ZXlhIGTDtm5kw7xyw7xyXHJcblx0ICovXHJcblx0cHJpdmF0ZSBnZXRIaWRkZW5Db250YWluZXIoKTogSFRNTEVsZW1lbnQge1xyXG5cdFx0Y29uc3Qgc2xvdCA9IGdsb2JhbFRoaXMgYXMgdHlwZW9mIGdsb2JhbFRoaXMgJiB7IF9fYXJpbFdlYkNvbXBvbmVudEhpZGRlbkNvbnRhaW5lcj86IEhUTUxFbGVtZW50IH07XHJcblx0XHRsZXQgY29udGFpbmVyID0gc2xvdC5fX2FyaWxXZWJDb21wb25lbnRIaWRkZW5Db250YWluZXI7XHJcblx0XHRpZiAoIWNvbnRhaW5lcikge1xyXG5cdFx0XHRjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHRcdFx0Y29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XHJcblx0XHRcdGNvbnRhaW5lci5zdHlsZS5wb3NpdGlvbiA9ICdhYnNvbHV0ZSc7XHJcblx0XHRcdGNvbnRhaW5lci5zdHlsZS5sZWZ0ID0gJy05OTk5cHgnO1xyXG5cdFx0XHRjb250YWluZXIuc3R5bGUudG9wID0gJy05OTk5cHgnO1xyXG5cdFx0XHRjb250YWluZXIuc3R5bGUudmlzaWJpbGl0eSA9ICdoaWRkZW4nO1xyXG5cdFx0XHRkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XHJcblx0XHRcdHNsb3QuX19hcmlsV2ViQ29tcG9uZW50SGlkZGVuQ29udGFpbmVyID0gY29udGFpbmVyO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGNvbnRhaW5lcjtcclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIERldGFjaGVkIHJvdXRlIHZpZXcnxLEgacOnaW5kZWtpIHBsdWdpbi9NRkUgY3VzdG9tIGVsZW1lbnQnbGVyaW5pICh0YWcgYWTEsW5kYSBgLWBcclxuXHQgKiBidWx1bmFuIHZlIGBjdXN0b21FbGVtZW50c2AnYSBrYXnEsXRsxLEgb2xhbmxhcsSxKSBoaWRkZW4gY29udGFpbmVyJ2EgdGHFn8SxciB2ZVxyXG5cdCAqIHllcmxlcmluZSBiaXJlciBDb21tZW50IHBsYWNlaG9sZGVyIGLEsXJha8Sxci4gRWxlbWVudCBkb2N1bWVudCd0YW4gYXlyxLFsbWF6LFxyXG5cdCAqIEFuZ3VsYXIgRWxlbWVudHMgYGRpc2Nvbm5lY3RlZENhbGxiYWNrYCAxMG1zIGRlc3Ryb3kgdGltZXInxLEga2FuYXQgw6fEsXJwbWF6IOKAlFxyXG5cdCAqIGRvbGF5xLFzxLF5bGEgcGx1Z2luJ2luIGBDb21wb25lbnRSZWZgIHZlIGRhaGlsaSBzdGF0ZSdpIChmb3JtIGRlxJ9lcmxlcmksIHNlYXJjaFxyXG5cdCAqIHNvbnXDp2xhcsSxIHZzLikgc29ucmFraSByZXRyaWV2ZSdlIGthZGFyIGtvcnVudXIuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBwcmVzZXJ2ZUN1c3RvbUVsZW1lbnRzSW5UcmVlKGRldGFjaGVkVHJlZTogRGV0YWNoZWRSb3V0ZUhhbmRsZSk6IHZvaWQge1xyXG5cdFx0dHJ5IHtcclxuXHRcdFx0Y29uc3QgY29tcG9uZW50UmVmID0gKGRldGFjaGVkVHJlZSBhcyBJbnRlcm5hbERldGFjaGVkUm91dGVIYW5kbGUpLmNvbXBvbmVudFJlZjtcclxuXHRcdFx0Y29uc3Qgcm9vdEVsID0gY29tcG9uZW50UmVmPy5sb2NhdGlvbj8ubmF0aXZlRWxlbWVudCBhcyBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZDtcclxuXHRcdFx0aWYgKCFyb290RWwpIHJldHVybjtcclxuXHJcblx0XHRcdGNvbnN0IGhpZGRlbkNvbnRhaW5lciA9IHRoaXMuZ2V0SGlkZGVuQ29udGFpbmVyKCk7XHJcblx0XHRcdGNvbnN0IGFsbCA9IHJvb3RFbC5xdWVyeVNlbGVjdG9yQWxsKCcqJyk7XHJcblx0XHRcdGNvbnN0IHByZXNlcnZlZExpc3Q6IFByZXNlcnZlZEN1c3RvbUVsW10gPSBbXTtcclxuXHJcblx0XHRcdGZvciAoY29uc3QgZWwgb2YgQXJyYXkuZnJvbShhbGwpKSB7XHJcblx0XHRcdFx0Y29uc3QgdGFnID0gZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpO1xyXG5cdFx0XHRcdGlmICghdGFnLmluY2x1ZGVzKCctJykpIGNvbnRpbnVlO1xyXG5cdFx0XHRcdGlmICghd2luZG93LmN1c3RvbUVsZW1lbnRzPy5nZXQodGFnKSkgY29udGludWU7XHJcblxyXG5cdFx0XHRcdGNvbnN0IG5vZGUgPSBlbCBhcyBIVE1MRWxlbWVudDtcclxuXHRcdFx0XHRjb25zdCBwbGFjZWhvbGRlciA9IGRvY3VtZW50LmNyZWF0ZUNvbW1lbnQoYGFyaWwtcHJlc2VydmVkOiR7dGFnfWApO1xyXG5cdFx0XHRcdG5vZGUucGFyZW50Tm9kZT8uaW5zZXJ0QmVmb3JlKHBsYWNlaG9sZGVyLCBub2RlKTtcclxuXHRcdFx0XHRoaWRkZW5Db250YWluZXIuYXBwZW5kQ2hpbGQobm9kZSk7XHJcblx0XHRcdFx0cHJlc2VydmVkTGlzdC5wdXNoKHsgZWw6IG5vZGUsIHBsYWNlaG9sZGVyIH0pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRpZiAocHJlc2VydmVkTGlzdC5sZW5ndGgpIHtcclxuXHRcdFx0XHR0aGlzLnByZXNlcnZlZEN1c3RvbUVscy5zZXQoZGV0YWNoZWRUcmVlLCBwcmVzZXJ2ZWRMaXN0KTtcclxuXHRcdFx0XHRjb25zb2xlLmxvZyhgW1N0cmF0ZWd5XSBwcmVzZXJ2ZUN1c3RvbUVsZW1lbnRzSW5UcmVlOiAke3ByZXNlcnZlZExpc3QubGVuZ3RofSBlbGVtZW50IHRhxZ/EsW5kxLFgKTtcclxuXHRcdFx0fVxyXG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcclxuXHRcdFx0Y29uc29sZS53YXJuKCdDdXN0b20gZWxlbWVudCBwcmVzZXJ2ZSBlZGlsaXJrZW4gaGF0YTonLCBlcnJvcik7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBwcmVzZXJ2ZUN1c3RvbUVsZW1lbnRzSW5UcmVlIGlsZSB0YcWfxLFuYW4gY3VzdG9tIGVsZW1lbnQnbGVyaSBvcmlqaW5hbFxyXG5cdCAqIHBsYWNlaG9sZGVyJ2xhcsSxbsSxbiB5ZXJpbmUgZ2VyaSBrb3lhci5cclxuXHQgKi9cclxuXHRwcml2YXRlIHJlc3RvcmVDdXN0b21FbGVtZW50c0luVHJlZShkZXRhY2hlZFRyZWU6IERldGFjaGVkUm91dGVIYW5kbGUpOiB2b2lkIHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdGNvbnN0IHByZXNlcnZlZExpc3QgPSB0aGlzLnByZXNlcnZlZEN1c3RvbUVscy5nZXQoZGV0YWNoZWRUcmVlKTtcclxuXHRcdFx0aWYgKCFwcmVzZXJ2ZWRMaXN0Py5sZW5ndGgpIHJldHVybjtcclxuXHJcblx0XHRcdGZvciAoY29uc3QgeyBlbCwgcGxhY2Vob2xkZXIgfSBvZiBwcmVzZXJ2ZWRMaXN0KSB7XHJcblx0XHRcdFx0aWYgKHBsYWNlaG9sZGVyLnBhcmVudE5vZGUpIHtcclxuXHRcdFx0XHRcdHBsYWNlaG9sZGVyLnBhcmVudE5vZGUucmVwbGFjZUNoaWxkKGVsLCBwbGFjZWhvbGRlcik7XHJcblx0XHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRcdC8vIFBsYWNlaG9sZGVyIGHEn2HDp3RhbiBrb3BhcsSxbG3EscWfIOKAlCBwYXJlbnQgdmlldyB0YW1hbWVuIGRlc3Ryb3kgZWRpbG1pxZ8gb2xhYmlsaXJcclxuXHRcdFx0XHRcdC8vICjDtnJuLiByZW1vdGUgdGFiIGthcGFubcSxxZ8pLiBFbGVtZW50J2kgaGlkZGVuIGNvbnRhaW5lcidkYSBixLFyYWttYW1hayBpw6dpbiBjbGVhbnVwLlxyXG5cdFx0XHRcdFx0Y29uc29sZS53YXJuKCdbU3RyYXRlZ3ldIHJlc3RvcmU6IHBsYWNlaG9sZGVyIHBhcmVudCB5b2ssIGN1c3RvbSBlbGVtZW50IGNsZWFudXAgZWRpbGl5b3InLCBlbC50YWdOYW1lKTtcclxuXHRcdFx0XHRcdGlmIChlbC5wYXJlbnROb2RlID09PSB0aGlzLmdldEhpZGRlbkNvbnRhaW5lcigpKSB7XHJcblx0XHRcdFx0XHRcdHRoaXMuZ2V0SGlkZGVuQ29udGFpbmVyKCkucmVtb3ZlQ2hpbGQoZWwpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHR0aGlzLnByZXNlcnZlZEN1c3RvbUVscy5kZWxldGUoZGV0YWNoZWRUcmVlKTtcclxuXHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gcmVzdG9yZUN1c3RvbUVsZW1lbnRzSW5UcmVlOiAke3ByZXNlcnZlZExpc3QubGVuZ3RofSBlbGVtZW50IGdlcmkgeWVybGXFn3RpcmlsZGlgKTtcclxuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XHJcblx0XHRcdGNvbnNvbGUud2FybignQ3VzdG9tIGVsZW1lbnQgcmVzdG9yZSBlZGlsaXJrZW4gaGF0YTonLCBlcnJvcik7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHRwcml2YXRlIGlzTWF0Y2hlclJvdXRlKHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90IHwgbnVsbCk6IGJvb2xlYW4ge1xyXG5cdFx0cmV0dXJuICEhcm91dGU/LnJvdXRlQ29uZmlnPy5tYXRjaGVyO1xyXG5cdH1cclxuXHJcblx0cHJpdmF0ZSBnZXRSZW1vdGVOYW1lKHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90IHwgbnVsbCk6IHN0cmluZyB8IG51bGwge1xyXG5cdFx0cmV0dXJuIChyb3V0ZT8ucm91dGVDb25maWc/LmRhdGEgYXMgUGx1Z2luQ29uZmlnIHwgdW5kZWZpbmVkKT8ucmVtb3RlTmFtZSA/PyBudWxsO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogVVJMIGA/X3RhYj08dGFiSWQ+YCBxdWVyeSBwYXJhbSfEsW7EsSBva3Ug4oCUIGBOYXZTZXJ2aWNlYCBoZXIgdGFiIGnDp2luIHVuaXF1ZSBkZcSfZXIgw7xyZXRpci5cclxuXHQgKiBgSXNvbGF0ZWRMb2NhdGlvblN0cmF0ZWd5YCBzYXllc2luZGUgaG9zdCBSb3V0ZXInYSB0YWJzxLF6IG5hdmlnYXRpb24gZ2VsbWV6LCBzYWRlY2VcclxuXHQgKiBgdGFiU3RhdGUoe190YWJ9KWAnbMSxIHRhYiB0xLFrbGFtYWxhcsSxIGdlbGlyIOKGkiBgcm91dGUucXVlcnlQYXJhbXNbJ190YWInXWAgaGVyIHphbWFuIGRvbHUuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBnZXRUYWJJZEZyb21Sb3V0ZShyb3V0ZTogQWN0aXZhdGVkUm91dGVTbmFwc2hvdCB8IG51bGwpOiBzdHJpbmcgfCBudWxsIHtcclxuXHRcdGNvbnN0IHYgPSByb3V0ZT8ucXVlcnlQYXJhbXM/LlsnX3RhYiddO1xyXG5cdFx0cmV0dXJuIHR5cGVvZiB2ID09PSAnc3RyaW5nJyAmJiB2Lmxlbmd0aCA+IDAgPyB2IDogbnVsbDtcclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIFJvdXRlIGtleSdpbmkgb2x1xZ90dXJ1ci4gTWF0Y2hlciAod2ViIGNvbXBvbmVudCkgcm91dGUnbGFyIGnDp2luIHJlbW90ZU5hbWUgKyB0YWJJZFxyXG5cdCAqIGJhemzEsSBrZXkga3VsbGFuxLFsxLFyIOKAlCBheW7EsSByZW1vdGUndW4gaGVyIHRhYidpIGF5csSxIE1GRSBjdXN0b20gZWxlbWVudCBpbnN0YW5jZSfEsW5hXHJcblx0ICogc2FoaXB0aXIgKGJhxJ/EsW1zxLF6IHN0YXRlKS5cclxuXHQgKlxyXG5cdCAqIE5vcm1hbCByb3V0ZSdsYXIgacOnaW4gYHBhdGhGcm9tUm9vdGAgw7x6ZXJpbmRlbiB0YW0geW9sIMO8cmV0aWxpciwgYXJkxLFuZGFuIHF1ZXJ5IHBhcmFtc1xyXG5cdCAqIGVrbGVuaXIgKHphdGVuIGBfdGFiYCBkYWhpbCkg4oCUIG5lc3RlZCBib8WfLXBhdGggKGBwYXRoOiAnJ2ApIGNoaWxkIHJvdXRlJ2xhcsSxbsSxbiBheW7EsSBgXCJcImBcclxuXHQgKiBrZXknaXlsZSDDp2FrxLHFn21hc8SxbsSxIGVuZ2VsbGVyIHZlIGF5bsSxIHBhdGgnaW4gaWtpIHRhYidpIGZhcmtsxLEga2V5IGFsxLFyLlxyXG5cdCAqL1xyXG5cdHByaXZhdGUgZ2V0Um91dGVLZXkocm91dGU6IEFjdGl2YXRlZFJvdXRlU25hcHNob3QpOiBzdHJpbmcge1xyXG5cdFx0aWYgKHRoaXMuaXNNYXRjaGVyUm91dGUocm91dGUpKSB7XHJcblx0XHRcdGNvbnN0IHJlbW90ZU5hbWUgPSB0aGlzLmdldFJlbW90ZU5hbWUocm91dGUpO1xyXG5cdFx0XHRpZiAocmVtb3RlTmFtZSkge1xyXG5cdFx0XHRcdGNvbnN0IHRhYklkID0gdGhpcy5nZXRUYWJJZEZyb21Sb3V0ZShyb3V0ZSk7XHJcblx0XHRcdFx0cmV0dXJuIHRhYklkID8gYG1mZToke3JlbW90ZU5hbWV9OiR7dGFiSWR9YCA6IGBtZmU6JHtyZW1vdGVOYW1lfWA7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdC8vIFRhbSBwYXRoOiBrw7ZrIOKGkiBpbnRlcm1lZGlhdGUg4oaSIHNlbGYuIEJvxZ8gc2VnbWVudGxlcmkgaW5kZXggaWxlIGtvcnV5YXJhayBoaXllcmFyxZ9pXHJcblx0XHQvLyBwb3ppc3lvbnVudSBrZXknZSBkYWhpbCBlZGVyaXogKGthcmRlxZ8gZW1wdHktcGF0aCByb3V0ZSdsYXIgZmFya2zEsSBrZXkgYWzEsXIpLlxyXG5cdFx0Y29uc3QgZnVsbFBhdGggPSByb3V0ZS5wYXRoRnJvbVJvb3RcclxuXHRcdFx0Lm1hcCgociwgaSkgPT4ge1xyXG5cdFx0XHRcdGNvbnN0IHNlZyA9IHIudXJsLm1hcCgocykgPT4gcy5wYXRoKS5qb2luKCcvJyk7XHJcblx0XHRcdFx0cmV0dXJuIHNlZy5sZW5ndGggPiAwID8gc2VnIDogYCMke2l9YDtcclxuXHRcdFx0fSlcclxuXHRcdFx0LmpvaW4oJy8nKTtcclxuXHRcdGNvbnN0IHF1ZXJ5UGFyYW1zID0gdGhpcy5zZXJpYWxpemVRdWVyeVBhcmFtcyhyb3V0ZS5xdWVyeVBhcmFtcyk7XHJcblx0XHRjb25zdCBmcmFnbWVudCA9IHJvdXRlLmZyYWdtZW50ID8gJyMnICsgcm91dGUuZnJhZ21lbnQgOiAnJztcclxuXHJcblx0XHRyZXR1cm4gZnVsbFBhdGggKyBxdWVyeVBhcmFtcyArIGZyYWdtZW50O1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogUXVlcnkgcGFyYW0nbGFyxLEgVVJMLXNhZmUgxZ9la2lsZGUgc2VyaWxlxZ90aXJpci4gYFVSTFNlYXJjaFBhcmFtc2AgY29uc3RydWN0b3IndW5hXHJcblx0ICogZG/En3J1ZGFuIG9iamVjdCB2ZXJpcnNlayBhcnJheSBkZcSfZXJsZXIgYD9pZHM9MSwyLDNgIGdpYmkgdGVrIGtleSBvbGFyYWsgeWF6xLFsxLFyO1xyXG5cdCAqIEFuZ3VsYXIgUm91dGVyJ8SxbiBgP2lkcz0xJmlkcz0yJmlkcz0zYCBkYXZyYW7EscWfxLF5bGEgdXl1bXN1eiBvbGFiaWxpci4gYGFwcGVuZGAgaWxlXHJcblx0ICogaGVyIGFycmF5IGVsZW1hbsSxIGF5csSxIGVudHJ5IG9sdXIgdmUgbnVsbC91bmRlZmluZWQgZGXEn2VybGVyIGF0bGFuxLFyLlxyXG5cdCAqL1xyXG5cdHByaXZhdGUgc2VyaWFsaXplUXVlcnlQYXJhbXMocGFyYW1zOiBBY3RpdmF0ZWRSb3V0ZVNuYXBzaG90WydxdWVyeVBhcmFtcyddKTogc3RyaW5nIHtcclxuXHRcdC8vIGBzb3J0KClgIGlsZSBkZXRlcm1pbmlzbSDigJQgYXluxLEgc2F5ZmFuxLFuIGZhcmtsxLEgaW5zZXJ0aW9uLW9yZGVyJ2zEsSBxdWVyeVBhcmFtcyfEsVxyXG5cdFx0Ly8gKGRlZXAgbGluayBzaGFyZSwgc2VydmVyLXNpZGUgcmVuZGVyLCBKUy1nZW5lcmF0ZWQgVVJMJ2xlcikgYXluxLEgcm91dGUga2V5IMO8cmV0aXIuXHJcblx0XHQvLyBBa3NpIHRha2RpcmRlIGA/Yj0xJmE9MmAgdmUgYD9hPTImYj0xYCBpa2kgYXlyxLEgaGFuZGxlIG9sYXJhayBjYWNoZSdsZW5pciDihpIgdGFiXHJcblx0XHQvLyBpem9sYXN5b251IGF5bsSxIHNheWZhbsSxbiBpa2kgYXlyxLEgaW5zdGFuY2UnxLFuxLEgYcOnYXIuXHJcblx0XHRjb25zdCBrZXlzID0gT2JqZWN0LmtleXMocGFyYW1zKS5zb3J0KCk7XHJcblx0XHRpZiAoIWtleXMubGVuZ3RoKSByZXR1cm4gJyc7XHJcblx0XHRjb25zdCBvdXQgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKCk7XHJcblx0XHRmb3IgKGNvbnN0IGsgb2Yga2V5cykge1xyXG5cdFx0XHRjb25zdCB2ID0gcGFyYW1zW2tdO1xyXG5cdFx0XHRpZiAodiA9PSBudWxsKSBjb250aW51ZTtcclxuXHRcdFx0aWYgKEFycmF5LmlzQXJyYXkodikpIHtcclxuXHRcdFx0XHR2LmZvckVhY2goKHZ2KSA9PiB2diAhPSBudWxsICYmIG91dC5hcHBlbmQoaywgU3RyaW5nKHZ2KSkpO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdG91dC5hcHBlbmQoaywgU3RyaW5nKHYpKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0Y29uc3Qgc3RyID0gb3V0LnRvU3RyaW5nKCk7XHJcblx0XHRyZXR1cm4gc3RyID8gJz8nICsgc3RyIDogJyc7XHJcblx0fVxyXG5cclxuXHRzaG91bGRSZXVzZVJvdXRlKGZ1dHVyZTogQWN0aXZhdGVkUm91dGVTbmFwc2hvdCwgY3VycjogQWN0aXZhdGVkUm91dGVTbmFwc2hvdCk6IGJvb2xlYW4ge1xyXG5cdFx0Y29uc3QgZnV0dXJlTWF0Y2hlciA9IHRoaXMuaXNNYXRjaGVyUm91dGUoZnV0dXJlKTtcclxuXHRcdGNvbnN0IGN1cnJNYXRjaGVyID0gdGhpcy5pc01hdGNoZXJSb3V0ZShjdXJyKTtcclxuXHJcblx0XHRpZiAoZnV0dXJlTWF0Y2hlciAmJiBjdXJyTWF0Y2hlcikge1xyXG5cdFx0XHRjb25zdCBmciA9IHRoaXMuZ2V0UmVtb3RlTmFtZShmdXR1cmUpO1xyXG5cdFx0XHRjb25zdCBjciA9IHRoaXMuZ2V0UmVtb3RlTmFtZShjdXJyKTtcclxuXHRcdFx0Y29uc3QgZnQgPSB0aGlzLmdldFRhYklkRnJvbVJvdXRlKGZ1dHVyZSk7XHJcblx0XHRcdGNvbnN0IGN0ID0gdGhpcy5nZXRUYWJJZEZyb21Sb3V0ZShjdXJyKTtcclxuXHRcdFx0Ly8gQXluxLEgcmVtb3RlIG9sc2EgYmlsZSBmYXJrbMSxIHRhYidsZXJzZSBmcmVzaCBjb21wb25lbnQgaW5zdGFudGlhdGUgZWRpbG1lbGkg4oCUXHJcblx0XHRcdC8vIGFrc2kgdGFrZGlyZGUgaWtpIHRhYiB0ZWsgY29tcG9uZW50UmVmJ2kgcGF5bGHFn8SxciB2ZSBzdGF0ZSBpem9sYXN5b251IGtheWJvbHVyLlxyXG5cdFx0XHRjb25zdCBzaG91bGRSZXVzZSA9ICEhZnIgJiYgZnIgPT09IGNyICYmIGZ0ID09PSBjdDtcclxuXHRcdFx0Y29uc29sZS5sb2coYFtTdHJhdGVneV0gc2hvdWxkUmV1c2VSb3V0ZSBtYXRjaGVyIOKGkiAke3Nob3VsZFJldXNlfS4gZnV0dXJlPSR7ZnJ9LyR7ZnR9IGN1cnI9JHtjcn0vJHtjdH1gKTtcclxuXHRcdFx0cmV0dXJuIHNob3VsZFJldXNlO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChmdXR1cmVNYXRjaGVyICE9PSBjdXJyTWF0Y2hlcikge1xyXG5cdFx0XHRjb25zb2xlLmxvZygnW1N0cmF0ZWd5XSBzaG91bGRSZXVzZVJvdXRlIG1peGVkIOKGkiBmYWxzZScpO1xyXG5cdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHR9XHJcblxyXG5cdFx0Y29uc3Qgc2hvdWxkUmV1c2UgPSBmdXR1cmUucm91dGVDb25maWcgPT09IGN1cnIucm91dGVDb25maWc7XHJcblx0XHRjb25zdCBmdXR1cmVLZXkgPSB0aGlzLmdldFJvdXRlS2V5KGZ1dHVyZSk7XHJcblx0XHRjb25zdCBjdXJyS2V5ID0gdGhpcy5nZXRSb3V0ZUtleShjdXJyKTtcclxuXHRcdGNvbnN0IGZ1dHVyZUNvbXAgPSBmdXR1cmUucm91dGVDb25maWc/LmNvbXBvbmVudD8ubmFtZSA/PyBmdXR1cmUuY29tcG9uZW50Py5uYW1lID8/ICdub25lJztcclxuXHRcdGNvbnN0IGN1cnJDb21wID0gY3Vyci5yb3V0ZUNvbmZpZz8uY29tcG9uZW50Py5uYW1lID8/IGN1cnIuY29tcG9uZW50Py5uYW1lID8/ICdub25lJztcclxuXHRcdGNvbnNvbGUubG9nKFxyXG5cdFx0XHRgW1N0cmF0ZWd5XSBzaG91bGRSZXVzZVJvdXRlIG5vcm1hbCDihpIgJHtzaG91bGRSZXVzZX0uIGZ1dHVyZT1cIiR7ZnV0dXJlS2V5fVwiKCR7ZnV0dXJlQ29tcH0pIGN1cnI9XCIke2N1cnJLZXl9XCIoJHtjdXJyQ29tcH0pYFxyXG5cdFx0KTtcclxuXHRcdHJldHVybiBzaG91bGRSZXVzZTtcclxuXHR9XHJcbn1cclxuIl19
|