aril 1.2.18 → 2.0.1-dev.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/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 +24 -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 +2630 -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 +216 -216
- 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 +7 -5
- package/theme/layout/app/layout/mfe.layout.component.ts +13 -39
- 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,675 @@
|
|
|
1
|
+
import { Injectable, inject, signal } from '@angular/core';
|
|
2
|
+
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
3
|
+
import { NavigationEnd, Router } from '@angular/router';
|
|
4
|
+
import { MessageService } from 'primeng/api';
|
|
5
|
+
import { TranslocoService } from '@ngneat/transloco';
|
|
6
|
+
import { filter, map, tap } from 'rxjs/operators';
|
|
7
|
+
import { RouteCloseService } from './route-close.service';
|
|
8
|
+
import { safeNavigate } from './safe-navigate';
|
|
9
|
+
import * as i0 from "@angular/core";
|
|
10
|
+
/**
|
|
11
|
+
* Router'a verilecek URL'e `_tab` query param ekler. Tab navLink'leri CLEAN saklandığı
|
|
12
|
+
* için her navigation'da bu helper'la `_tab` eklenir. UrlSerializer serialize sırasında
|
|
13
|
+
* `_tab`'i strip eder → adres çubuğunda görünmez ama Router internal queryParams'ında
|
|
14
|
+
* kalır → strategy doğru çalışır.
|
|
15
|
+
*
|
|
16
|
+
* Path'in başında leading slash garanti edilir (Router URL formatına uygunluk için).
|
|
17
|
+
*/
|
|
18
|
+
function withTabId(rawNavLink, tabId) {
|
|
19
|
+
const { path, query, fragment } = splitUrl(rawNavLink ?? '');
|
|
20
|
+
const params = new URLSearchParams(query);
|
|
21
|
+
params.set('_tab', tabId);
|
|
22
|
+
return `${path}?${params.toString()}${fragment}`;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* NavLink'i clean formatta normalize eder: leading slash garantisi, query string'ten
|
|
26
|
+
* `_tab` strip, fragment korunur. `activeRoute()` UrlSerializer override'ı sayesinde
|
|
27
|
+
* clean URL döner; tab storage da clean olmalı ki karşılaştırma çalışsın.
|
|
28
|
+
*/
|
|
29
|
+
function cleanNavLink(rawNavLink) {
|
|
30
|
+
const { path, query, fragment } = splitUrl(rawNavLink ?? '');
|
|
31
|
+
if (!query)
|
|
32
|
+
return path + fragment;
|
|
33
|
+
const params = new URLSearchParams(query);
|
|
34
|
+
params.delete('_tab');
|
|
35
|
+
const remaining = params.toString();
|
|
36
|
+
return remaining ? `${path}?${remaining}${fragment}` : path + fragment;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* URL string'ini path/query/fragment olarak parçalar. Fragment (`#`) ilk önce ayrılır
|
|
40
|
+
* ki query (`?`) içindeki `#` ile karışmasın. Path'in başına leading slash garanti
|
|
41
|
+
* edilir (Router URL formatı için zorunlu).
|
|
42
|
+
*
|
|
43
|
+
* `URL` constructor'ı relative URL ile çalışmaz, manuel split `URLSearchParams`'a
|
|
44
|
+
* uygun temiz query string verir. Fragment olduğu gibi (öncesinde `#` ile) döner.
|
|
45
|
+
*/
|
|
46
|
+
function splitUrl(raw) {
|
|
47
|
+
const fragmentIdx = raw.indexOf('#');
|
|
48
|
+
const fragment = fragmentIdx >= 0 ? raw.slice(fragmentIdx) : '';
|
|
49
|
+
const beforeFragment = fragmentIdx >= 0 ? raw.slice(0, fragmentIdx) : raw;
|
|
50
|
+
const queryIdx = beforeFragment.indexOf('?');
|
|
51
|
+
let path = queryIdx >= 0 ? beforeFragment.slice(0, queryIdx) : beforeFragment;
|
|
52
|
+
const query = queryIdx >= 0 ? beforeFragment.slice(queryIdx + 1) : '';
|
|
53
|
+
if (!path.startsWith('/'))
|
|
54
|
+
path = '/' + path;
|
|
55
|
+
return { path, query, fragment };
|
|
56
|
+
}
|
|
57
|
+
/** Router'a verilen extras.state objesi — `_tab`'i history state'inde tutar. */
|
|
58
|
+
function tabState(tabId) {
|
|
59
|
+
return { state: { _tab: tabId } };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* NavLink'in remote (ilk path segment) adını döndürür. Query param'lardan ve leading
|
|
63
|
+
* slash'tan etkilenmez — `/mw/app-management?_tab=abc` → `mw`.
|
|
64
|
+
*/
|
|
65
|
+
function extractRemoteName(navLink) {
|
|
66
|
+
if (!navLink)
|
|
67
|
+
return null;
|
|
68
|
+
let path = navLink.split('?')[0];
|
|
69
|
+
if (path.startsWith('/'))
|
|
70
|
+
path = path.slice(1);
|
|
71
|
+
return path.split('/')[0] || null;
|
|
72
|
+
}
|
|
73
|
+
export class NavService {
|
|
74
|
+
constructor() {
|
|
75
|
+
this.router = inject(Router);
|
|
76
|
+
this.routeCloseService = inject(RouteCloseService);
|
|
77
|
+
this.messageService = inject(MessageService);
|
|
78
|
+
this.translocoService = inject(TranslocoService);
|
|
79
|
+
// i18n çevirileri lazy (async HTTP) yüklenir; `firstCheckForRoute`/`openHomeTab` çeviri
|
|
80
|
+
// yüklenmeden `translate('tab.home')` ederse raw key ("tab.home") kalır. Çeviri yüklenince
|
|
81
|
+
// fallback-isimli tab'ları (özellikle anasayfa) menüyle revize et → "Anasayfa" yerine oturur.
|
|
82
|
+
this.i18nReloadSub = this.translocoService.events$
|
|
83
|
+
.pipe(
|
|
84
|
+
// Yalnız root/global i18n (scope YOK) yüklenince reconcile et — 'tab.home' orada.
|
|
85
|
+
// Feature lazy scope'ları (her MFE sayfası kendi scope'unu yükler) gereksiz menü-DFS
|
|
86
|
+
// tetiklemesin; reconcile yalnız home/menü adlarının bağlı olduğu global çeviri içindir.
|
|
87
|
+
filter((e) => e.type === 'translationLoadSuccess' && !e.payload?.scope), takeUntilDestroyed())
|
|
88
|
+
.subscribe(() => this.reconcileTabsWithMenu());
|
|
89
|
+
this.firstCheckForRoute = false;
|
|
90
|
+
/**
|
|
91
|
+
* Aynı anda açık olabilecek tab sayısı tavanı. Tab bar'ın UX'i ve `CustomRouteReuseStrategy`
|
|
92
|
+
* `storedRoutes` birikimi için üst sınır. Aşılırsa yeni navigasyon engellenir.
|
|
93
|
+
*/
|
|
94
|
+
this.MAX_TABS = 20;
|
|
95
|
+
/**
|
|
96
|
+
* Menu items'i NavService'ten almak için provider. `aril/boot/bridge`'i doğrudan import
|
|
97
|
+
* etmek ng-packagr'da `aril/boot/bridge → aril/boot/config/apps` döngüsü tetikliyordu
|
|
98
|
+
* (bridge `PluginMenuItem` type'ını import ediyor). Bunun yerine host shell
|
|
99
|
+
* (`AppLayoutComponent`) `setMenuItemsProvider(() => bridge.hostMenuItems())` ile bağlar.
|
|
100
|
+
* Provider yoksa label/icon resolve'u atlanır, URL segment fallback'i devreye girer.
|
|
101
|
+
*/
|
|
102
|
+
this.menuItemsProvider = null;
|
|
103
|
+
/**
|
|
104
|
+
* Host shell anasayfa NavItem'ını sağlar — `AppLayoutComponent.homeNavItem` (menüdeki
|
|
105
|
+
* `root: true` öğesinden türetilir: navLink/navName/icon). Tüm tab'lar kapatıldığında
|
|
106
|
+
* liste boş kalmasın diye `openHomeTab` bunu kullanır; provider yoksa `/` + i18n fallback.
|
|
107
|
+
*/
|
|
108
|
+
this.homeNavItemProvider = null;
|
|
109
|
+
/**
|
|
110
|
+
* URL adres çubuğundan görülen aktif route — `TabAwareUrlSerializer` `_tab`'i strip
|
|
111
|
+
* ettiği için bu CLEAN URL'dir (ör. `/crm/address-management/search`). Tab navLink'leri
|
|
112
|
+
* de clean saklandığı için `r.navLink === activeRoute()` doğrudan karşılaştırılır.
|
|
113
|
+
*/
|
|
114
|
+
this.activeRoute = toSignal(this.router.events.pipe(filter((e) => e instanceof NavigationEnd), tap((e) => {
|
|
115
|
+
// Her NavigationEnd'de history state'ten aktif tabId'yi okuyup signal'a yaz.
|
|
116
|
+
// `extras.state` ile yapılan navigation'larda Angular history.state'e `_tab`'i koyar;
|
|
117
|
+
// browser back/forward navigasyonlarında da history.state korunur.
|
|
118
|
+
const tabIdFromState = window.history.state?.['_tab'];
|
|
119
|
+
if (typeof tabIdFromState === 'string' && tabIdFromState.length > 0) {
|
|
120
|
+
this.activeTabId.set(tabIdFromState);
|
|
121
|
+
}
|
|
122
|
+
const currentUrl = e.urlAfterRedirects || e.url;
|
|
123
|
+
// NOT: Aktif tab'ın navLink'ini güncelleme `syncActiveTabNavLinkForTab` ile
|
|
124
|
+
// `bridge.activeMFEUrl` üzerinden yapılır (kaynağa-bağlı, cross-tab safe).
|
|
125
|
+
// Tab `activeRoutes`'ta YOKSA türet: (a) ilk açılış/refresh deep-link, VEYA
|
|
126
|
+
// (b) browser back/forward ile KAPATILMIŞ tab'ın history entry'sine dönüş
|
|
127
|
+
// (kapatma host history'yi silmez → geri tuşu o URL'e gider, tab yeniden açılmalı).
|
|
128
|
+
// Bilinen tab (normal tıklama / yeni tab / açık tab'a geri) → yalnız activeTabId
|
|
129
|
+
// güncellenir (yukarıda), mevcut tab'a focus; türetme yapılmaz.
|
|
130
|
+
const knownTab = typeof tabIdFromState === 'string' &&
|
|
131
|
+
tabIdFromState.length > 0 &&
|
|
132
|
+
this.activeRoutes().some((r) => r.tabId === tabIdFromState);
|
|
133
|
+
if (!knownTab) {
|
|
134
|
+
// Refresh sonrası deep link → URL'den nav item türet ve activeRoutes'a ekle.
|
|
135
|
+
// URL'de `_tab` yoksa (UrlSerializer strip ettiği için adres çubuğundan paylaşılan
|
|
136
|
+
// link `_tab` taşımaz) yeni tabId üret + history state'e yaz; navigateByUrl
|
|
137
|
+
// çağrısı ile state injekte ederiz, adres çubuğu temiz kalır.
|
|
138
|
+
if (currentUrl) {
|
|
139
|
+
const stateTabId = window.history.state?.['_tab'];
|
|
140
|
+
const navLink = cleanNavLink(currentUrl);
|
|
141
|
+
// İlk navigation'da (F5/açılış) gelen URL zaten activeRoutes'taki bir tab ile
|
|
142
|
+
// eşleşiyorsa (özellikle restore edilmiş pinned tab) YENİ tab TÜRETME — o tab'ı
|
|
143
|
+
// kullan. F5 sonrası Angular initial navigation `history.state._tab`'ı koruMAZ →
|
|
144
|
+
// `knownTab` yakalayamaz; navLink eşleşmesi restore-duplicate'ini önler. Yalnız ilk
|
|
145
|
+
// check'te + state'te `_tab` yokken: sonraki navigationlarda kasıtlı duplicate tab ve
|
|
146
|
+
// browser-back ile kapatılan tab'ın re-derive davranışı korunur.
|
|
147
|
+
const existing = !this.firstCheckForRoute && !stateTabId
|
|
148
|
+
? this.activeRoutes().find((r) => r.navLink === navLink)
|
|
149
|
+
: undefined;
|
|
150
|
+
let tabId = typeof stateTabId === 'string' && stateTabId.length > 0 ? stateTabId : existing?.tabId ?? '';
|
|
151
|
+
let needsStateInject = false;
|
|
152
|
+
if (!tabId) {
|
|
153
|
+
tabId = this.generateTabId();
|
|
154
|
+
needsStateInject = true;
|
|
155
|
+
}
|
|
156
|
+
else if (existing) {
|
|
157
|
+
// Eşleşen tab'ın `tabId`'sini URL/state'e yansıt — adres çubuğu `_tab` taşımadığı
|
|
158
|
+
// için RouteReuse `mfe:<remote>:<tabId>` key tutarlılığı state inject ister.
|
|
159
|
+
needsStateInject = true;
|
|
160
|
+
}
|
|
161
|
+
this.activeTabId.set(tabId);
|
|
162
|
+
if (!existing) {
|
|
163
|
+
const menuInfo = this.resolveNavInfoFromMenu(navLink);
|
|
164
|
+
const fallbackName = () => {
|
|
165
|
+
let path = navLink;
|
|
166
|
+
if (path.startsWith('/'))
|
|
167
|
+
path = path.slice(1);
|
|
168
|
+
const parts = path.split('/').filter((s) => s.length > 0);
|
|
169
|
+
const last = parts[parts.length - 1] || 'Page';
|
|
170
|
+
return last.charAt(0).toUpperCase() + last.slice(1).replace(/-/g, ' ');
|
|
171
|
+
};
|
|
172
|
+
this.addToActiveRoutes({
|
|
173
|
+
tabId,
|
|
174
|
+
navLink,
|
|
175
|
+
navName: menuInfo?.navName || fallbackName(),
|
|
176
|
+
icon: menuInfo?.icon
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (needsStateInject) {
|
|
180
|
+
// Mevcut navigation cycle bitiminden SONRA yeni navigation tetikle —
|
|
181
|
+
// `queueMicrotask` Router'ın navigation kuyruğu finalize olmadan çalışıp
|
|
182
|
+
// `AbortError: Transition was skipped`'a yol açıyordu. `setTimeout(0)` macrotask
|
|
183
|
+
// ile current cycle biter, Router idle olur. `this.navigate` zaten AbortError'ı
|
|
184
|
+
// yutuyor — concurrent navigation race'leri sessizce iptal edilir.
|
|
185
|
+
setTimeout(() => {
|
|
186
|
+
safeNavigate(this.router, withTabId(navLink, tabId), {
|
|
187
|
+
replaceUrl: true,
|
|
188
|
+
state: { _tab: tabId }
|
|
189
|
+
}).catch(() => { });
|
|
190
|
+
}, 0);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
this.firstCheckForRoute = true;
|
|
194
|
+
}
|
|
195
|
+
}), map((w) => w.urlAfterRedirects || w.url)));
|
|
196
|
+
this.activeRoutes = signal([]);
|
|
197
|
+
/** History state'ten okunan aktif tab kimliği — `isActive(tab) = tab.tabId === activeTabId()`. */
|
|
198
|
+
this.activeTabId = signal('');
|
|
199
|
+
}
|
|
200
|
+
/** RFC4122 v4 UUID — collision-free, internal state taşımaz. */
|
|
201
|
+
generateTabId() {
|
|
202
|
+
return crypto.randomUUID();
|
|
203
|
+
}
|
|
204
|
+
setMenuItemsProvider(provider) {
|
|
205
|
+
this.menuItemsProvider = provider;
|
|
206
|
+
// Provider geç set edildiyse (örn. shell layout NavigationEnd'den sonra mount oldu),
|
|
207
|
+
// activeRoutes'ta default/fallback isimle eklenmiş ilk tab'ı menüyle revize et.
|
|
208
|
+
this.reconcileTabsWithMenu();
|
|
209
|
+
}
|
|
210
|
+
setHomeNavItemProvider(provider) {
|
|
211
|
+
this.homeNavItemProvider = provider;
|
|
212
|
+
// Refresh/ilk açılış path'inde fallback ("Page") isim/ikonla eklenmiş anasayfa tab'ı
|
|
213
|
+
// varsa, provider hazır olunca menüyle (home dahil) revize et.
|
|
214
|
+
this.reconcileTabsWithMenu();
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Chrome bookmark click davranışı: aktif tab'ın navLink/navName'i güncellenir,
|
|
218
|
+
* o tab'a navigate edilir. Yeni tab açılmaz. Mevcut tab'ın `tabId`'si KORUNUR —
|
|
219
|
+
* Router'a `?_tab=tabId` ile navigate edilir, history state'e yazılır; navLink
|
|
220
|
+
* storage'da CLEAN saklanır.
|
|
221
|
+
*/
|
|
222
|
+
navigateInCurrentTab(navItem) {
|
|
223
|
+
const idx = this.findActiveTabIndex();
|
|
224
|
+
if (idx === -1) {
|
|
225
|
+
// Aktif tab yok — fallback olarak yeni tab aç.
|
|
226
|
+
this.openInNewTabAndFocus(navItem);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const existingTab = this.activeRoutes()[idx];
|
|
230
|
+
const tabId = existingTab.tabId;
|
|
231
|
+
const newNavLink = cleanNavLink(navItem.navLink);
|
|
232
|
+
// Aynı navLink'e tıklama → yine de navigate et (`onSameUrlNavigation: 'reload'`).
|
|
233
|
+
if (existingTab.navLink === newNavLink) {
|
|
234
|
+
safeNavigate(this.router, withTabId(newNavLink, tabId), tabState(tabId));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
this.activeRoutes.update((routes) => {
|
|
238
|
+
const next = [...routes];
|
|
239
|
+
// Mevcut tab'ın `tabId` VE `pinned` durumu korunur — menüden gelen navItem `pinned`
|
|
240
|
+
// taşımaz; `{ ...navItem }` ile ezilirse sabitlenmiş tab unpin olurdu (tab içeriğini
|
|
241
|
+
// değiştirmek sabitlemeyi bozmamalı).
|
|
242
|
+
next[idx] = { ...navItem, tabId, navLink: newNavLink, pinned: existingTab.pinned };
|
|
243
|
+
return next;
|
|
244
|
+
});
|
|
245
|
+
safeNavigate(this.router, withTabId(newNavLink, tabId), tabState(tabId));
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Arka planda yeni tab aç — aktif tab değişmez, navigate edilmez. `tabId` her zaman
|
|
249
|
+
* yeni üretilir (ya da caller verdiyse o kullanılır), exists check YOKTUR; aynı path
|
|
250
|
+
* birden fazla tab'da bağımsız yaşayabilir.
|
|
251
|
+
*/
|
|
252
|
+
openInBackgroundTab(navItem) {
|
|
253
|
+
if (this.activeRoutes().length >= this.MAX_TABS) {
|
|
254
|
+
this.notifyMaxTabsReached();
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const tabId = navItem.tabId || this.generateTabId();
|
|
258
|
+
const navLink = cleanNavLink(navItem.navLink);
|
|
259
|
+
this.activeRoutes.update((val) => [...val, { ...navItem, tabId, navLink }]);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Yeni tab aç ve o tab'a geç. Exists check YOK — aynı path'in iki ayrı tab'i kabul edilir;
|
|
263
|
+
* her tab kendi `tabId`'siyle ayrı componentRef instantiate eder.
|
|
264
|
+
*/
|
|
265
|
+
openInNewTabAndFocus(navItem) {
|
|
266
|
+
if (this.activeRoutes().length >= this.MAX_TABS) {
|
|
267
|
+
this.notifyMaxTabsReached();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const tabId = navItem.tabId || this.generateTabId();
|
|
271
|
+
const navLink = cleanNavLink(navItem.navLink);
|
|
272
|
+
this.activeRoutes.update((val) => [...val, { ...navItem, tabId, navLink }]);
|
|
273
|
+
safeNavigate(this.router, withTabId(navLink, tabId), tabState(tabId));
|
|
274
|
+
}
|
|
275
|
+
/** Programatik tab ekleme; activeRoute observable refresh path'i bunu kullanır. */
|
|
276
|
+
addToActiveRoutes(navItem) {
|
|
277
|
+
if (this.activeRoutes().length >= this.MAX_TABS) {
|
|
278
|
+
// Programmatic çağrı — toast göstermek yerine sessizce return.
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const tabId = navItem.tabId || this.generateTabId();
|
|
282
|
+
const navLink = cleanNavLink(navItem.navLink);
|
|
283
|
+
// Signal change detection reference identity ile çalışır — yeni array dön.
|
|
284
|
+
this.activeRoutes.update((val) => [...val, { ...navItem, tabId, navLink }]);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Aktif tab'ı `activeTabId` üzerinden bulur. Aynı `navLink`'in birden fazla tab'i
|
|
288
|
+
* olabileceği için navLink karşılaştırması ambiguous; tabId tek doğru kimlik.
|
|
289
|
+
*/
|
|
290
|
+
findActiveTabIndex() {
|
|
291
|
+
const tabId = this.activeTabId();
|
|
292
|
+
if (!tabId)
|
|
293
|
+
return -1;
|
|
294
|
+
return this.activeRoutes().findIndex((r) => r.tabId === tabId);
|
|
295
|
+
}
|
|
296
|
+
/** Tab limiti aşıldığında kullanıcıyı toast ile bilgilendirir. */
|
|
297
|
+
notifyMaxTabsReached() {
|
|
298
|
+
this.messageService.add({
|
|
299
|
+
severity: 'warn',
|
|
300
|
+
summary: this.translocoService.translate('tab.maxReachedSummary', { max: this.MAX_TABS }),
|
|
301
|
+
detail: this.translocoService.translate('tab.maxReachedDetail'),
|
|
302
|
+
key: 'toast-root'
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/** Anasayfa navLink'i — host menü root item'ından (yoksa '/'), clean format. */
|
|
306
|
+
homeNavLink() {
|
|
307
|
+
return cleanNavLink(this.homeNavItemProvider?.().navLink || '/');
|
|
308
|
+
}
|
|
309
|
+
/** Verilen tab anasayfaya mı ait. */
|
|
310
|
+
isHomeTab(item) {
|
|
311
|
+
return item.navLink === this.homeNavLink();
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Anasayfa tab'ı açıp ona navigate eder — tab listesi HİÇ boş kalmamalı (en az 1 tab).
|
|
315
|
+
* NavItem host menü root item'ından gelir; yoksa `/` + i18n fallback ('tab.home', 'pi pi-home').
|
|
316
|
+
*/
|
|
317
|
+
openHomeTab() {
|
|
318
|
+
const base = this.homeNavItemProvider?.();
|
|
319
|
+
const navLink = cleanNavLink(base?.navLink || '/');
|
|
320
|
+
const tabId = this.generateTabId();
|
|
321
|
+
const home = {
|
|
322
|
+
tabId,
|
|
323
|
+
navLink,
|
|
324
|
+
navName: base?.navName || this.translocoService.translate('tab.home'),
|
|
325
|
+
icon: base?.icon || 'pi pi-home'
|
|
326
|
+
};
|
|
327
|
+
this.activeRoutes.update((val) => [...val, home]);
|
|
328
|
+
return safeNavigate(this.router, withTabId(navLink, tabId), tabState(tabId));
|
|
329
|
+
}
|
|
330
|
+
closeNavigation(navItem) {
|
|
331
|
+
// `tabId` ile bul — aynı path'in birden fazla tab'i olabileceği için `navLink`
|
|
332
|
+
// karşılaştırması ambiguous olur, kimlik bazlı arama tek doğru yöntem.
|
|
333
|
+
const idx = this.activeRoutes().findIndex((r) => r.tabId === navItem.tabId);
|
|
334
|
+
console.log(`[NavService] closeNavigation tabId="${navItem.tabId}" navLink="${navItem.navLink}" index=${idx}`);
|
|
335
|
+
if (idx === -1)
|
|
336
|
+
return;
|
|
337
|
+
const closedItem = this.activeRoutes()[idx];
|
|
338
|
+
// Son kalan tab anasayfaysa kapatma — her zaman en az 1 (anasayfa) tab açık kalmalı.
|
|
339
|
+
if (this.activeRoutes().length === 1 && this.isHomeTab(closedItem))
|
|
340
|
+
return;
|
|
341
|
+
// Signal change detection reference identity ile çalışır — yeni array dön.
|
|
342
|
+
this.activeRoutes.update((val) => val.filter((_, i) => i !== idx));
|
|
343
|
+
const cleanup = () => this.cleanupTabHandle(closedItem);
|
|
344
|
+
const closingActiveTab = this.activeTabId() === closedItem.tabId;
|
|
345
|
+
if (closingActiveTab) {
|
|
346
|
+
// Aktif tab kapatılıyor — başka tab'a (veya home'a) navigate edip cleanup'ı SONRA tetikle.
|
|
347
|
+
// Aksi takdirde strategy henüz `store('mfe:${remoteName}:${tabId}', handle)`'i yapmadan
|
|
348
|
+
// `deleteStoredRoute` çağrılır ve cleanup boş hit eder; ardından detach tamamlanınca
|
|
349
|
+
// element hidden container'da orphan kalır (race condition).
|
|
350
|
+
const remaining = this.activeRoutes();
|
|
351
|
+
const willNavigateItem = remaining.length > 0 ? remaining[idx] || remaining[idx - 1] : null;
|
|
352
|
+
if (willNavigateItem) {
|
|
353
|
+
safeNavigate(this.router, withTabId(willNavigateItem.navLink, willNavigateItem.tabId), tabState(willNavigateItem.tabId)).finally(cleanup);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
// Hiç tab kalmadı — boş bırakma, anasayfa tab'ı aç (en az 1 tab kuralı).
|
|
357
|
+
this.openHomeTab().finally(cleanup);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
// Aktif değildi — navigation gerekmez. Ancak arka planda açılıp (`openInBackgroundTab`)
|
|
362
|
+
// hiç görüntülenmemiş tab henüz Router tarafından `store()` edilmemiş olabilir; cleanup'ı
|
|
363
|
+
// bir microtask geciktirip olası store cycle'ına fırsat veriyoruz (orphan handle önlemi).
|
|
364
|
+
void Promise.resolve().then(cleanup);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Bir tab hariç diğer tüm tab'ları kapatır. Aktif tab kapatılanlardan ise `keepItem`'a geçer.
|
|
369
|
+
* **Sabit tab'lar korunur** (Chrome davranışı) — pinned olanlar her zaman açık kalır.
|
|
370
|
+
*/
|
|
371
|
+
closeOthers(keepItem) {
|
|
372
|
+
const all = this.activeRoutes();
|
|
373
|
+
const toClose = all.filter((r) => r.tabId !== keepItem.tabId && !r.pinned);
|
|
374
|
+
if (toClose.length === 0)
|
|
375
|
+
return;
|
|
376
|
+
const survivorIds = new Set([keepItem.tabId, ...all.filter((r) => r.pinned).map((r) => r.tabId)]);
|
|
377
|
+
this.activeRoutes.set(all.filter((r) => survivorIds.has(r.tabId)));
|
|
378
|
+
const cleanupAll = () => toClose.forEach((t) => this.cleanupTabHandle(t));
|
|
379
|
+
if (this.activeTabId() !== keepItem.tabId) {
|
|
380
|
+
safeNavigate(this.router, withTabId(keepItem.navLink, keepItem.tabId), tabState(keepItem.tabId)).finally(cleanupAll);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
cleanupAll();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Verilen tab'ın sağındaki tüm tab'ları kapatır. **Sabit tab'lar korunur** —
|
|
388
|
+
* pinned bir tab sağda olsa bile kapatılmaz (Chrome davranışı).
|
|
389
|
+
*/
|
|
390
|
+
closeToRight(item) {
|
|
391
|
+
const all = this.activeRoutes();
|
|
392
|
+
const idx = all.findIndex((r) => r.tabId === item.tabId);
|
|
393
|
+
if (idx === -1)
|
|
394
|
+
return;
|
|
395
|
+
const toClose = all.slice(idx + 1).filter((r) => !r.pinned);
|
|
396
|
+
if (toClose.length === 0)
|
|
397
|
+
return;
|
|
398
|
+
const toCloseIds = new Set(toClose.map((r) => r.tabId));
|
|
399
|
+
this.activeRoutes.set(all.filter((r) => !toCloseIds.has(r.tabId)));
|
|
400
|
+
const activeWasClosed = toClose.some((r) => r.tabId === this.activeTabId());
|
|
401
|
+
const cleanupAll = () => toClose.forEach((t) => this.cleanupTabHandle(t));
|
|
402
|
+
if (activeWasClosed) {
|
|
403
|
+
safeNavigate(this.router, withTabId(item.navLink, item.tabId), tabState(item.tabId)).finally(cleanupAll);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
cleanupAll();
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Sabit olmayan tüm tab'ları kapatır. **Sabit tab'lar korunur** — kullanıcı "Tümünü Kapat"
|
|
411
|
+
* isterse bile pinned tab'lar açık kalır (Chrome davranışı). Hiç pinned tab yoksa ana sayfaya
|
|
412
|
+
* navigate edilir.
|
|
413
|
+
*/
|
|
414
|
+
closeAll() {
|
|
415
|
+
const all = this.activeRoutes();
|
|
416
|
+
if (all.length === 0)
|
|
417
|
+
return;
|
|
418
|
+
const toClose = all.filter((r) => !r.pinned);
|
|
419
|
+
if (toClose.length === 0)
|
|
420
|
+
return;
|
|
421
|
+
const survivors = all.filter((r) => r.pinned);
|
|
422
|
+
this.activeRoutes.set(survivors);
|
|
423
|
+
const cleanupAll = () => toClose.forEach((t) => this.cleanupTabHandle(t));
|
|
424
|
+
// Aktif tab kapatılanlardan ise: ya bir pinned tab'a geç (yaşıyorsa), yoksa ana sayfaya.
|
|
425
|
+
const activeWasClosed = toClose.some((r) => r.tabId === this.activeTabId());
|
|
426
|
+
if (activeWasClosed) {
|
|
427
|
+
if (survivors.length > 0) {
|
|
428
|
+
const target = survivors[0];
|
|
429
|
+
safeNavigate(this.router, withTabId(target.navLink, target.tabId), tabState(target.tabId)).finally(cleanupAll);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
// Pinned survivor yok — boş bırakma, anasayfa tab'ı aç (en az 1 tab kuralı).
|
|
433
|
+
this.openHomeTab().finally(cleanupAll);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
cleanupAll();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Tab sabitleme toggle — Chrome-vari pinning. Pinned tab'lar tab bar'ın **soluna** taşınır
|
|
442
|
+
* (insertion sırasını koruyarak), kompakt görünür, `closeOthers`/`closeToRight`/`closeAll`
|
|
443
|
+
* çağrılarından korunur. Aynı pinned tıklanırsa sabitlemeyi kaldırır → normal alanın **sonuna**
|
|
444
|
+
* taşınır.
|
|
445
|
+
*/
|
|
446
|
+
togglePin(item) {
|
|
447
|
+
const all = this.activeRoutes();
|
|
448
|
+
const idx = all.findIndex((r) => r.tabId === item.tabId);
|
|
449
|
+
if (idx === -1)
|
|
450
|
+
return;
|
|
451
|
+
const target = all[idx];
|
|
452
|
+
const newPinned = !target.pinned;
|
|
453
|
+
// Önce target'ı array'den çıkar, sonra pinned/normal bloğunun **sonuna** ekle.
|
|
454
|
+
// Bu Chrome davranışı: pinlenince pinned bloğun sonuna (en sağa), unpinlenince normal
|
|
455
|
+
// bloğun başına (pinned'in hemen sağına).
|
|
456
|
+
const without = all.filter((_, i) => i !== idx);
|
|
457
|
+
const pinnedSegment = without.filter((r) => r.pinned);
|
|
458
|
+
const normalSegment = without.filter((r) => !r.pinned);
|
|
459
|
+
const updated = { ...target, pinned: newPinned };
|
|
460
|
+
// Pin → pinned bloğun sonuna; unpin → normal bloğun başına. Her iki durumda da `updated`,
|
|
461
|
+
// pinned ve normal segmentlerin SINIRINA yerleşir (pozisyon aynı) → tek ifade yeterli.
|
|
462
|
+
const next = [...pinnedSegment, updated, ...normalSegment];
|
|
463
|
+
this.activeRoutes.set(next);
|
|
464
|
+
}
|
|
465
|
+
/** Sıradaki tab'a (circular) geçer. */
|
|
466
|
+
goToNextTab() {
|
|
467
|
+
const all = this.activeRoutes();
|
|
468
|
+
if (all.length === 0)
|
|
469
|
+
return;
|
|
470
|
+
const idx = this.findActiveTabIndex();
|
|
471
|
+
const nextIdx = idx === -1 ? 0 : (idx + 1) % all.length;
|
|
472
|
+
const target = all[nextIdx];
|
|
473
|
+
safeNavigate(this.router, withTabId(target.navLink, target.tabId), tabState(target.tabId));
|
|
474
|
+
}
|
|
475
|
+
/** Önceki tab'a (circular) geçer. */
|
|
476
|
+
goToPrevTab() {
|
|
477
|
+
const all = this.activeRoutes();
|
|
478
|
+
if (all.length === 0)
|
|
479
|
+
return;
|
|
480
|
+
const idx = this.findActiveTabIndex();
|
|
481
|
+
const prevIdx = idx === -1 ? all.length - 1 : (idx - 1 + all.length) % all.length;
|
|
482
|
+
const target = all[prevIdx];
|
|
483
|
+
safeNavigate(this.router, withTabId(target.navLink, target.tabId), tabState(target.tabId));
|
|
484
|
+
}
|
|
485
|
+
/** Index'teki tab'a geçer (Alt+1..9 için 0-tabanlı index). */
|
|
486
|
+
goToTabByIndex(index) {
|
|
487
|
+
const all = this.activeRoutes();
|
|
488
|
+
if (index < 0 || index >= all.length)
|
|
489
|
+
return;
|
|
490
|
+
const target = all[index];
|
|
491
|
+
safeNavigate(this.router, withTabId(target.navLink, target.tabId), tabState(target.tabId));
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Verilen tab'a navigate — UI'dan (tab tıklama) çağrılır. Router'a `_tab` ile URL ve
|
|
495
|
+
* `extras.state` ile tabId verir; UrlSerializer adres çubuğunu temiz tutar.
|
|
496
|
+
*/
|
|
497
|
+
navigateToTab(item) {
|
|
498
|
+
if (!item?.navLink || !item?.tabId)
|
|
499
|
+
return;
|
|
500
|
+
const url = withTabId(item.navLink, item.tabId);
|
|
501
|
+
console.log(`[NavService] navigateToTab tabId="${item.tabId}" navLink="${item.navLink}" → url="${url}"`);
|
|
502
|
+
safeNavigate(this.router, url, tabState(item.tabId));
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Belirli bir tab'ın `navLink`'ini ve (varsa) `navName`'ini günceller. **Kaynağa-bağlı**
|
|
506
|
+
* (per-tab) sync — caller hangi tab'ın URL'i olduğunu söyler, NavService aktif tab
|
|
507
|
+
* varsayımında bulunmaz. Hidden container'daki async MFE NavigationEnd'leri aktif tab'ın
|
|
508
|
+
* state'ini KONTAMİNE EDEMEZ.
|
|
509
|
+
*
|
|
510
|
+
* `title` parametresi MFE Router deepest route'unun `Route.title` veya `data.tabName`
|
|
511
|
+
* değerinden gelir → kullanıcı detail/edit gibi sub-sayfalara navigate olduğunda tab
|
|
512
|
+
* adı dinamik güncellenir. `title` undefined ise tab.navName değişmez (statik kalır).
|
|
513
|
+
*
|
|
514
|
+
* AppLayoutComponent effect'ten `bridge.activeMFEUrl` (`{tabId, url, title}`) ile çağrılır.
|
|
515
|
+
*/
|
|
516
|
+
syncActiveTabNavLinkForTab(tabId, rawUrl, title) {
|
|
517
|
+
if (!tabId || !rawUrl)
|
|
518
|
+
return;
|
|
519
|
+
const cleanUrl = cleanNavLink(rawUrl);
|
|
520
|
+
const target = this.activeRoutes().find((t) => t.tabId === tabId);
|
|
521
|
+
if (!target)
|
|
522
|
+
return;
|
|
523
|
+
const linkChanged = target.navLink !== cleanUrl;
|
|
524
|
+
const nameChanged = !!title && target.navName !== title;
|
|
525
|
+
if (!linkChanged && !nameChanged)
|
|
526
|
+
return;
|
|
527
|
+
const newNavLink = linkChanged ? cleanUrl : target.navLink;
|
|
528
|
+
const newName = nameChanged ? title : target.navName;
|
|
529
|
+
console.log(`[NavService] syncActiveTabNavLinkForTab tab="${tabId}" ` +
|
|
530
|
+
`navLink "${target.navLink}" → "${newNavLink}" name "${target.navName}" → "${newName}"`);
|
|
531
|
+
this.activeRoutes.update((routes) => routes.map((t) => (t.tabId === tabId ? { ...t, navLink: newNavLink, navName: newName } : t)));
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Tab context-menu "Yinele" (Chrome "Duplicate"): mevcut tab'ın path'i kopyalanır, yeni bir
|
|
535
|
+
* `tabId` üretilir ve yeni tab oluşturulur. Strategy `_tab` query param'ını route key'in
|
|
536
|
+
* parçası yaptığı için yeni tab bağımsız `storedRoutes` slot'una yazılır → bağımsız
|
|
537
|
+
* componentRef + state. Mevcut tab korunur, kullanıcı her ikisinde de bağımsız çalışabilir.
|
|
538
|
+
*/
|
|
539
|
+
duplicateTab(item) {
|
|
540
|
+
if (!item?.navLink)
|
|
541
|
+
return;
|
|
542
|
+
if (this.activeRoutes().length >= this.MAX_TABS) {
|
|
543
|
+
this.notifyMaxTabsReached();
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const newTabId = this.generateTabId();
|
|
547
|
+
const newNavLink = cleanNavLink(item.navLink);
|
|
548
|
+
this.activeRoutes.update((val) => [
|
|
549
|
+
...val,
|
|
550
|
+
{ ...item, tabId: newTabId, navLink: newNavLink }
|
|
551
|
+
]);
|
|
552
|
+
safeNavigate(this.router, withTabId(newNavLink, newTabId), tabState(newTabId));
|
|
553
|
+
}
|
|
554
|
+
reorderActiveRoutes(newRoutes) {
|
|
555
|
+
// Pinned/normal sınırı koruması — sabit tab'lar tab bar'ın solunda (insertion sırasıyla),
|
|
556
|
+
// normal tab'lar sağında kalır. CDK drag drop bir tab'ı yanlış segmente düşürürse
|
|
557
|
+
// burada otomatik düzeltilir. Segment içi sıra korunur.
|
|
558
|
+
const pinned = newRoutes.filter((r) => r.pinned);
|
|
559
|
+
const normal = newRoutes.filter((r) => !r.pinned);
|
|
560
|
+
this.activeRoutes.set([...pinned, ...normal]);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Menu config'inden navLink'e karşılık gelen item'i longest-prefix match ile bulup
|
|
564
|
+
* aktif dilde label + icon döner. Adres çubuğuna URL yapıştırarak deep link açıldığında
|
|
565
|
+
* tab'ın menüden açılan tab'la aynı görünüme sahip olmasını sağlar.
|
|
566
|
+
*
|
|
567
|
+
* Lookup `menuItemsProvider` üzerinden — host shell `setMenuItemsProvider` ile
|
|
568
|
+
* `bridge.hostMenuItems()`'a bağlar. Provider yoksa veya eşleşme yoksa `null` döner ve
|
|
569
|
+
* caller URL segment fallback'ine düşer.
|
|
570
|
+
*/
|
|
571
|
+
resolveNavInfoFromMenu(navLink) {
|
|
572
|
+
// Anasayfa (menüdeki `root` öğesi) çoğu zaman `routerLink` ile eşleşmez (routerLink'i `/`
|
|
573
|
+
// veya boş olabilir, longest-prefix match'e takılmaz) → `/` için fallback "Page" + jenerik
|
|
574
|
+
// ikon dönerdi. Home provider varsa ad/ikonu doğrudan oradan (root item'dan) çöz.
|
|
575
|
+
if (this.homeNavItemProvider) {
|
|
576
|
+
const home = this.homeNavItemProvider();
|
|
577
|
+
// Anasayfa navLink eşleşiyorsa dön — menü boş/geç yüklendiyse ya da menüde `root` öğesi
|
|
578
|
+
// yoksa navName/icon boş gelebilir; i18n ('tab.home') + ev ikonu fallback ile asla
|
|
579
|
+
// menü-segment fallback'ine ("Page") düşme.
|
|
580
|
+
if (navLink === cleanNavLink(home.navLink || '/')) {
|
|
581
|
+
return {
|
|
582
|
+
navName: home.navName || this.translocoService.translate('tab.home'),
|
|
583
|
+
icon: home.icon || 'pi pi-home'
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
const items = this.menuItemsProvider?.();
|
|
588
|
+
if (!items?.length)
|
|
589
|
+
return null;
|
|
590
|
+
const path = navLink.split('?')[0];
|
|
591
|
+
const matched = this.findMenuItemForRoute(items, path);
|
|
592
|
+
if (!matched)
|
|
593
|
+
return null;
|
|
594
|
+
const lang = this.translocoService.getActiveLang();
|
|
595
|
+
const label = matched.label;
|
|
596
|
+
const navName = label?.[lang] || label?.['tr'] || label?.['en'] || '';
|
|
597
|
+
if (!navName)
|
|
598
|
+
return null;
|
|
599
|
+
return { navName, icon: matched.icon };
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Menü/i18n yüklenince tab'ları (özellikle anasayfa) menüyle revize eder — refresh/ilk açılış
|
|
603
|
+
* path'inde tab fallback isim/iconla eklenmiş olabilir (menü veya çeviri o anda hazır değildi).
|
|
604
|
+
* Tetikleyiciler: `setMenuItemsProvider`/`setHomeNavItemProvider`, `translationLoadSuccess`
|
|
605
|
+
* event'i ve host shell menü signal'i değişimi (`AppLayoutComponent` effect). Public — host
|
|
606
|
+
* layout menü async yüklenince çağırır ki anasayfa adı `tab.home` fallback'inde takılmasın.
|
|
607
|
+
*/
|
|
608
|
+
reconcileTabsWithMenu() {
|
|
609
|
+
const tabs = this.activeRoutes();
|
|
610
|
+
if (!tabs.length)
|
|
611
|
+
return;
|
|
612
|
+
let changed = false;
|
|
613
|
+
const next = tabs.map((t) => {
|
|
614
|
+
const info = this.resolveNavInfoFromMenu(t.navLink);
|
|
615
|
+
if (!info)
|
|
616
|
+
return t;
|
|
617
|
+
if (t.navName === info.navName && t.icon === info.icon)
|
|
618
|
+
return t;
|
|
619
|
+
changed = true;
|
|
620
|
+
return { ...t, navName: info.navName, icon: info.icon };
|
|
621
|
+
});
|
|
622
|
+
if (changed)
|
|
623
|
+
this.activeRoutes.set(next);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Menu ağacında verilen path için en spesifik (longest routerLink prefix) eşleşmeyi bulur.
|
|
627
|
+
* `mw/app-management/view-app` için hem `mw` hem `mw/app-management/view-app` eşleşse,
|
|
628
|
+
* daha uzun olan kazanır → leaf menu item'ın label/icon'u kullanılır.
|
|
629
|
+
*
|
|
630
|
+
* Path leading slash içerebilir (Router URL formatı), menu config routerLink'leri
|
|
631
|
+
* genelde leading slash'sız (`mw/meters`); ikisini normalize edip karşılaştırırız.
|
|
632
|
+
*/
|
|
633
|
+
findMenuItemForRoute(items, path) {
|
|
634
|
+
const normalize = (p) => (p.startsWith('/') ? p.slice(1) : p);
|
|
635
|
+
const normalizedPath = normalize(path);
|
|
636
|
+
let best;
|
|
637
|
+
const visit = (list) => {
|
|
638
|
+
for (const it of list) {
|
|
639
|
+
if (it.routerLink) {
|
|
640
|
+
const r = normalize(it.routerLink);
|
|
641
|
+
if (r && (normalizedPath === r || normalizedPath.startsWith(r + '/'))) {
|
|
642
|
+
const length = r.length;
|
|
643
|
+
if (!best || length > best.length)
|
|
644
|
+
best = { length, item: it };
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (it.items?.length)
|
|
648
|
+
visit(it.items);
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
visit(items);
|
|
652
|
+
return best?.item;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Bir tab'ın stored route handle'ını strategy'den temizler. Key formatı
|
|
656
|
+
* `mfe:${remoteName}:${tabId}` — strategy `processCleanupKey` matcher key'inden hem
|
|
657
|
+
* MFE custom element handle'ını hem o tab'a ait child route handle'larını düşürür.
|
|
658
|
+
*/
|
|
659
|
+
cleanupTabHandle(item) {
|
|
660
|
+
const remoteName = extractRemoteName(item.navLink);
|
|
661
|
+
if (!remoteName || !item.tabId)
|
|
662
|
+
return;
|
|
663
|
+
console.log(`[NavService] cleanupTabHandle remoteName="${remoteName}" tabId="${item.tabId}"`);
|
|
664
|
+
this.routeCloseService.deleteStoredRoute(`mfe:${remoteName}:${item.tabId}`);
|
|
665
|
+
}
|
|
666
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: NavService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
667
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: NavService, providedIn: 'root' }); }
|
|
668
|
+
}
|
|
669
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: NavService, decorators: [{
|
|
670
|
+
type: Injectable,
|
|
671
|
+
args: [{
|
|
672
|
+
providedIn: 'root'
|
|
673
|
+
}]
|
|
674
|
+
}] });
|
|
675
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmF2LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9hcmlsL2Jvb3QvY29uZmlnL2FwcHMvc3JjL25hdi5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsUUFBUSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDMUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUV4RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRTdDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBR2xELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzFELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQzs7QUE4Qi9DOzs7Ozs7O0dBT0c7QUFDSCxTQUFTLFNBQVMsQ0FBQyxVQUFrQixFQUFFLEtBQWE7SUFDbkQsTUFBTSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLEdBQUcsUUFBUSxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUM3RCxNQUFNLE1BQU0sR0FBRyxJQUFJLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMxQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztJQUMxQixPQUFPLEdBQUcsSUFBSSxJQUFJLE1BQU0sQ0FBQyxRQUFRLEVBQUUsR0FBRyxRQUFRLEVBQUUsQ0FBQztBQUNsRCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNI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bmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IHRha2VVbnRpbERlc3Ryb3llZCwgdG9TaWduYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlL3J4anMtaW50ZXJvcCc7XHJcbmltcG9ydCB7IE5hdmlnYXRpb25FbmQsIFJvdXRlciB9IGZyb20gJ0Bhbmd1bGFyL3JvdXRlcic7XHJcblxyXG5pbXBvcnQgeyBNZXNzYWdlU2VydmljZSB9IGZyb20gJ3ByaW1lbmcvYXBpJztcclxuXHJcbmltcG9ydCB7IFRyYW5zbG9jb1NlcnZpY2UgfSBmcm9tICdAbmduZWF0L3RyYW5zbG9jbyc7XHJcbmltcG9ydCB7IGZpbHRlciwgbWFwLCB0YXAgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XHJcblxyXG5pbXBvcnQgeyBQbHVnaW5NZW51SXRlbSB9IGZyb20gJy4vaW50ZXJmYWNlcyc7XHJcbmltcG9ydCB7IFJvdXRlQ2xvc2VTZXJ2aWNlIH0gZnJvbSAnLi9yb3V0ZS1jbG9zZS5zZXJ2aWNlJztcclxuaW1wb3J0IHsgc2FmZU5hdmlnYXRlIH0gZnJvbSAnLi9zYWZlLW5hdmlnYXRlJztcclxuXHJcbi8qKlxyXG4gKiBgdGFiSWRgIGhlciB0YWInxLEgdGVraWwga8SxbGFuIGtpbWxpa3RpcjsgYXluxLEgYG5hdkxpbmtgIChwYXRoKSBhbHTEsW5kYSBiaXJkZW4gZmF6bGFcclxuICogYmHEn8SxbXPEsXogdGFiIGHDp8SxbGFiaWxtZXNpbmkgc2HEn2xhci4gTmF2U2VydmljZSBSb3V0ZXInYSBuYXZpZ2F0aW9uIHlhcGFya2VuIFVSTCdlXHJcbiAqIGBfdGFiPTx0YWJJZD5gIHF1ZXJ5IHBhcmFtIGVrbGVyIHZlIGBleHRyYXMuc3RhdGUuX3RhYmAgaWxlIGhpc3Rvcnkgc3RhdGUnZSB5YXphci5cclxuICogYFRhYkF3YXJlVXJsU2VyaWFsaXplcmAgYWRyZXMgw6d1YnXEn3VuZGFuIGBfdGFiYCdpIHN0cmlwIGVkZXI7IFJvdXRlciBpbnRlcm5hbFxyXG4gKiBgcXVlcnlQYXJhbXNgIGtvcnVudXIg4oaSIGBDdXN0b21Sb3V0ZVJldXNlU3RyYXRlZ3kuZ2V0Um91dGVLZXlgIGRvxJ9ydSB0YWIga2V5IHTDvHJldGlyLlxyXG4gKlxyXG4gKiAqKsOWbmVtbGkqKjogYE5hdkl0ZW0ubmF2TGlua2AgSEVSIFpBTUFOIENMRUFOIFVSTCAobm8gYF90YWJgKS4gSGVtIGRpc3BsYXkgaGVtXHJcbiAqIGBhY3RpdmVSb3V0ZSgpYCBpbGUga2FyxZ/EsWxhxZ90xLFybWEgacOnaW4ga3VsbGFuxLFsxLFyLiBUYWIga2ltbGnEn2kgYHRhYklkYCDDvHplcmluZGVuXHJcbiAqIHnDtm5ldGlsaXIgKGBhY3RpdmVUYWJJZGAgc2lnbmFsKS5cclxuICovXHJcbmV4cG9ydCBpbnRlcmZhY2UgTmF2SXRlbSB7XHJcblx0dGFiSWQ6IHN0cmluZztcclxuXHRuYXZMaW5rOiBzdHJpbmc7XHJcblx0bmF2TmFtZTogc3RyaW5nO1xyXG5cdC8qKlxyXG5cdCAqIE1lbnUgaXRlbSdkYW4gZ2VsZW4gaWNvbiBjbGFzcyfEsSAow7Zybi4gJ3BpIHBpLWJvbHQnKS4gVmVyaWxtZXpzZSBsYXlvdXQgYGdldFRhYkljb25gXHJcblx0ICogbWVudSBjb25maWcnaW5kZW4gbG9uZ2VzdC1wcmVmaXggbWF0Y2ggaWxlIGxvb2t1cCB5YXBhcjsgbyBkYSBidWxhbWF6c2EgZ2VuZXJpYyBpa29uYSBkw7zFn2VyLlxyXG5cdCAqL1xyXG5cdGljb24/OiBzdHJpbmc7XHJcblx0LyoqXHJcblx0ICogVGFiIHNhYml0bGVtZSDigJQgQ2hyb21lLXZhcmkgcGlubmluZy4gU2FiaXQgdGFiJ2xhciB0YWIgYmFyJ8SxbiAqKnNvbHVuYSoqIHPEsXJhbGFuxLFyLFxyXG5cdCAqIGtvbXBha3QgZ8O2c3RlcmlsaXIgKHlhbG7EsXogaWNvbiwgYmHFn2zEsWsgKyBjbG9zZSBidXR0b24gZ2l6bGkpLCBgY2xvc2VPdGhlcnNgL2BjbG9zZVRvUmlnaHRgL1xyXG5cdCAqIGBjbG9zZUFsbGAgw6dhxJ9yxLFsYXLEsW5kYW4ga29ydW51ci4gRHJhZyAmIGRyb3AndGEgcGlubmVkIOKGlCBub3JtYWwgc8SxbsSxcsSxIGdlw6dpbGVtZXouXHJcblx0ICovXHJcblx0cGlubmVkPzogYm9vbGVhbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJvdXRlcidhIHZlcmlsZWNlayBVUkwnZSBgX3RhYmAgcXVlcnkgcGFyYW0gZWtsZXIuIFRhYiBuYXZMaW5rJ2xlcmkgQ0xFQU4gc2FrbGFuZMSxxJ/EsVxyXG4gKiBpw6dpbiBoZXIgbmF2aWdhdGlvbidkYSBidSBoZWxwZXInbGEgYF90YWJgIGVrbGVuaXIuIFVybFNlcmlhbGl6ZXIgc2VyaWFsaXplIHPEsXJhc8SxbmRhXHJcbiAqIGBfdGFiYCdpIHN0cmlwIGVkZXIg4oaSIGFkcmVzIMOndWJ1xJ91bmRhIGfDtnLDvG5tZXogYW1hIFJvdXRlciBpbnRlcm5hbCBxdWVyeVBhcmFtcyfEsW5kYVxyXG4gKiBrYWzEsXIg4oaSIHN0cmF0ZWd5IGRvxJ9ydSDDp2FsxLHFn8Sxci5cclxuICpcclxuICogUGF0aCdpbiBiYcWfxLFuZGEgbGVhZGluZyBzbGFzaCBnYXJhbnRpIGVkaWxpciAoUm91dGVyIFVSTCBmb3JtYXTEsW5hIHV5Z3VubHVrIGnDp2luKS5cclxuICovXHJcbmZ1bmN0aW9uIHdpdGhUYWJJZChyYXdOYXZMaW5rOiBzdHJpbmcsIHRhYklkOiBzdHJpbmcpOiBzdHJpbmcge1xyXG5cdGNvbnN0IHsgcGF0aCwgcXVlcnksIGZyYWdtZW50IH0gPSBzcGxpdFVybChyYXdOYXZMaW5rID8/ICcnKTtcclxuXHRjb25zdCBwYXJhbXMgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKHF1ZXJ5KTtcclxuXHRwYXJhbXMuc2V0KCdfdGFiJywgdGFiSWQpO1xyXG5cdHJldHVybiBgJHtwYXRofT8ke3BhcmFtcy50b1N0cmluZygpfSR7ZnJhZ21lbnR9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIE5hdkxpbmsnaSBjbGVhbiBmb3JtYXR0YSBub3JtYWxpemUgZWRlcjogbGVhZGluZyBzbGFzaCBnYXJhbnRpc2ksIHF1ZXJ5IHN0cmluZyd0ZW5cclxuICogYF90YWJgIHN0cmlwLCBmcmFnbWVudCBrb3J1bnVyLiBgYWN0aXZlUm91dGUoKWAgVXJsU2VyaWFsaXplciBvdmVycmlkZSfEsSBzYXllc2luZGVcclxuICogY2xlYW4gVVJMIGTDtm5lcjsgdGFiIHN0b3JhZ2UgZGEgY2xlYW4gb2xtYWzEsSBraSBrYXLFn8SxbGHFn3TEsXJtYSDDp2FsxLHFn3PEsW4uXHJcbiAqL1xyXG5mdW5jdGlvbiBjbGVhbk5hdkxpbmsocmF3TmF2TGluazogc3RyaW5nKTogc3RyaW5nIHtcclxuXHRjb25zdCB7IHBhdGgsIHF1ZXJ5LCBmcmFnbWVudCB9ID0gc3BsaXRVcmwocmF3TmF2TGluayA/PyAnJyk7XHJcblx0aWYgKCFxdWVyeSkgcmV0dXJuIHBhdGggKyBmcmFnbWVudDtcclxuXHRjb25zdCBwYXJhbXMgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKHF1ZXJ5KTtcclxuXHRwYXJhbXMuZGVsZXRlKCdfdGFiJyk7XHJcblx0Y29uc3QgcmVtYWluaW5nID0gcGFyYW1zLnRvU3RyaW5nKCk7XHJcblx0cmV0dXJuIHJlbWFpbmluZyA/IGAke3BhdGh9PyR7cmVtYWluaW5nfSR7ZnJhZ21lbnR9YCA6IHBhdGggKyBmcmFnbWVudDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVSTCBzdHJpbmcnaW5pIHBhdGgvcXVlcnkvZnJhZ21lbnQgb2xhcmFrIHBhcsOnYWxhci4gRnJhZ21lbnQgKGAjYCkgaWxrIMO2bmNlIGF5csSxbMSxclxyXG4gKiBraSBxdWVyeSAoYD9gKSBpw6dpbmRla2kgYCNgIGlsZSBrYXLEscWfbWFzxLFuLiBQYXRoJ2luIGJhxZ/EsW5hIGxlYWRpbmcgc2xhc2ggZ2FyYW50aVxyXG4gKiBlZGlsaXIgKFJvdXRlciBVUkwgZm9ybWF0xLEgacOnaW4gem9ydW5sdSkuXHJcbiAqXHJcbiAqIGBVUkxgIGNvbnN0cnVjdG9yJ8SxIHJlbGF0aXZlIFVSTCBpbGUgw6dhbMSxxZ9tYXosIG1hbnVlbCBzcGxpdCBgVVJMU2VhcmNoUGFyYW1zYCdhXHJcbiAqIHV5Z3VuIHRlbWl6IHF1ZXJ5IHN0cmluZyB2ZXJpci4gRnJhZ21lbnQgb2xkdcSfdSBnaWJpICjDtm5jZXNpbmRlIGAjYCBpbGUpIGTDtm5lci5cclxuICovXHJcbmZ1bmN0aW9uIHNwbGl0VXJsKHJhdzogc3RyaW5nKTogeyBwYXRoOiBzdHJpbmc7IHF1ZXJ5OiBzdHJpbmc7IGZyYWdtZW50OiBzdHJpbmcgfSB7XHJcblx0Y29uc3QgZnJhZ21lbnRJZHggPSByYXcuaW5kZXhPZignIycpO1xyXG5cdGNvbnN0IGZyYWdtZW50ID0gZnJhZ21lbnRJZHggPj0gMCA/IHJhdy5zbGljZShmcmFnbWVudElkeCkgOiAnJztcclxuXHRjb25zdCBiZWZvcmVGcmFnbWVudCA9IGZyYWdtZW50SWR4ID49IDAgPyByYXcuc2xpY2UoMCwgZnJhZ21lbnRJZHgpIDogcmF3O1xyXG5cdGNvbnN0IHF1ZXJ5SWR4ID0gYmVmb3JlRnJhZ21lbnQuaW5kZXhPZignPycpO1xyXG5cdGxldCBwYXRoID0gcXVlcnlJZHggPj0gMCA/IGJlZm9yZUZyYWdtZW50LnNsaWNlKDAsIHF1ZXJ5SWR4KSA6IGJlZm9yZUZyYWdtZW50O1xyXG5cdGNvbnN0IHF1ZXJ5ID0gcXVlcnlJZHggPj0gMCA/IGJlZm9yZUZyYWdtZW50LnNsaWNlKHF1ZXJ5SWR4ICsgMSkgOiAnJztcclxuXHRpZiAoIXBhdGguc3RhcnRzV2l0aCgnLycpKSBwYXRoID0gJy8nICsgcGF0aDtcclxuXHRyZXR1cm4geyBwYXRoLCBxdWVyeSwgZnJhZ21lbnQgfTtcclxufVxyXG5cclxuLyoqIFJvdXRlcidhIHZlcmlsZW4gZXh0cmFzLnN0YXRlIG9iamVzaSDigJQgYF90YWJgJ2kgaGlzdG9yeSBzdGF0ZSdpbmRlIHR1dGFyLiAqL1xyXG5mdW5jdGlvbiB0YWJTdGF0ZSh0YWJJZDogc3RyaW5nKTogeyBzdGF0ZTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfSB7XHJcblx0cmV0dXJuIHsgc3RhdGU6IHsgX3RhYjogdGFiSWQgfSB9O1xyXG59XHJcblxyXG4vKipcclxuICogTmF2TGluaydpbiByZW1vdGUgKGlsayBwYXRoIHNlZ21lbnQpIGFkxLFuxLEgZMO2bmTDvHLDvHIuIFF1ZXJ5IHBhcmFtJ2xhcmRhbiB2ZSBsZWFkaW5nXHJcbiAqIHNsYXNoJ3RhbiBldGtpbGVubWV6IOKAlCBgL213L2FwcC1tYW5hZ2VtZW50P190YWI9YWJjYCDihpIgYG13YC5cclxuICovXHJcbmZ1bmN0aW9uIGV4dHJhY3RSZW1vdGVOYW1lKG5hdkxpbms6IHN0cmluZyB8IHVuZGVmaW5lZCk6IHN0cmluZyB8IG51bGwge1xyXG5cdGlmICghbmF2TGluaykgcmV0dXJuIG51bGw7XHJcblx0bGV0IHBhdGggPSBuYXZMaW5rLnNwbGl0KCc/JylbMF07XHJcblx0aWYgKHBhdGguc3RhcnRzV2l0aCgnLycpKSBwYXRoID0gcGF0aC5zbGljZSgxKTtcclxuXHRyZXR1cm4gcGF0aC5zcGxpdCgnLycpWzBdIHx8IG51bGw7XHJcbn1cclxuXHJcbkBJbmplY3RhYmxlKHtcclxuXHRwcm92aWRlZEluOiAncm9vdCdcclxufSlcclxuZXhwb3J0IGNsYXNzIE5hdlNlcnZpY2Uge1xyXG5cdHJvdXRlcjogUm91dGVyID0gaW5qZWN0KFJvdXRlcik7XHJcblx0cm91dGVDbG9zZVNlcnZpY2U6IFJvdXRlQ2xvc2VTZXJ2aWNlID0gaW5qZWN0KFJvdXRlQ2xvc2VTZXJ2aWNlKTtcclxuXHRwcml2YXRlIHJlYWRvbmx5IG1lc3NhZ2VTZXJ2aWNlOiBNZXNzYWdlU2VydmljZSA9IGluamVjdChNZXNzYWdlU2VydmljZSk7XHJcblx0cHJpdmF0ZSByZWFkb25seSB0cmFuc2xvY29TZXJ2aWNlOiBUcmFuc2xvY29TZXJ2aWNlID0gaW5qZWN0KFRyYW5zbG9jb1NlcnZpY2UpO1xyXG5cclxuXHQvLyBpMThuIMOnZXZpcmlsZXJpIGxhenkgKGFzeW5jIEhUVFApIHnDvGtsZW5pcjsgYGZpcnN0Q2hlY2tGb3JSb3V0ZWAvYG9wZW5Ib21lVGFiYCDDp2V2aXJpXHJcblx0Ly8gecO8a2xlbm1lZGVuIGB0cmFuc2xhdGUoJ3RhYi5ob21lJylgIGVkZXJzZSByYXcga2V5IChcInRhYi5ob21lXCIpIGthbMSxci4gw4dldmlyaSB5w7xrbGVuaW5jZVxyXG5cdC8vIGZhbGxiYWNrLWlzaW1saSB0YWInbGFyxLEgKMO2emVsbGlrbGUgYW5hc2F5ZmEpIG1lbsO8eWxlIHJldml6ZSBldCDihpIgXCJBbmFzYXlmYVwiIHllcmluZSBvdHVydXIuXHJcblx0cHJpdmF0ZSByZWFkb25seSBpMThuUmVsb2FkU3ViID0gdGhpcy50cmFuc2xvY29TZXJ2aWNlLmV2ZW50cyRcclxuXHRcdC5waXBlKFxyXG5cdFx0XHQvLyBZYWxuxLF6IHJvb3QvZ2xvYmFsIGkxOG4gKHNjb3BlIFlPSykgecO8a2xlbmluY2UgcmVjb25jaWxlIGV0IOKAlCAndGFiLmhvbWUnIG9yYWRhLlxyXG5cdFx0XHQvLyBGZWF0dXJlIGxhenkgc2NvcGUnbGFyxLEgKGhlciBNRkUgc2F5ZmFzxLEga2VuZGkgc2NvcGUndW51IHnDvGtsZXIpIGdlcmVrc2l6IG1lbsO8LURGU1xyXG5cdFx0XHQvLyB0ZXRpa2xlbWVzaW47IHJlY29uY2lsZSB5YWxuxLF6IGhvbWUvbWVuw7wgYWRsYXLEsW7EsW4gYmHEn2zEsSBvbGR1xJ91IGdsb2JhbCDDp2V2aXJpIGnDp2luZGlyLlxyXG5cdFx0XHRmaWx0ZXIoKGUpID0+IGUudHlwZSA9PT0gJ3RyYW5zbGF0aW9uTG9hZFN1Y2Nlc3MnICYmICFlLnBheWxvYWQ/LnNjb3BlKSxcclxuXHRcdFx0dGFrZVVudGlsRGVzdHJveWVkKClcclxuXHRcdClcclxuXHRcdC5zdWJzY3JpYmUoKCkgPT4gdGhpcy5yZWNvbmNpbGVUYWJzV2l0aE1lbnUoKSk7XHJcblxyXG5cdGZpcnN0Q2hlY2tGb3JSb3V0ZSA9IGZhbHNlO1xyXG5cdC8qKlxyXG5cdCAqIEF5bsSxIGFuZGEgYcOnxLFrIG9sYWJpbGVjZWsgdGFiIHNhecSxc8SxIHRhdmFuxLEuIFRhYiBiYXInxLFuIFVYJ2kgdmUgYEN1c3RvbVJvdXRlUmV1c2VTdHJhdGVneWBcclxuXHQgKiBgc3RvcmVkUm91dGVzYCBiaXJpa2ltaSBpw6dpbiDDvHN0IHPEsW7EsXIuIEHFn8SxbMSxcnNhIHllbmkgbmF2aWdhc3lvbiBlbmdlbGxlbmlyLlxyXG5cdCAqL1xyXG5cdHByaXZhdGUgcmVhZG9ubHkgTUFYX1RBQlMgPSAyMDtcclxuXHJcblx0LyoqIFJGQzQxMjIgdjQgVVVJRCDigJQgY29sbGlzaW9uLWZyZWUsIGludGVybmFsIHN0YXRlIHRhxZ/EsW1hei4gKi9cclxuXHRwcml2YXRlIGdlbmVyYXRlVGFiSWQoKTogc3RyaW5nIHtcclxuXHRcdHJldHVybiBjcnlwdG8ucmFuZG9tVVVJRCgpO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogTWVudSBpdGVtcydpIE5hdlNlcnZpY2UndGVuIGFsbWFrIGnDp2luIHByb3ZpZGVyLiBgYXJpbC9ib290L2JyaWRnZWAnaSBkb8SfcnVkYW4gaW1wb3J0XHJcblx0ICogZXRtZWsgbmctcGFja2FncidkYSBgYXJpbC9ib290L2JyaWRnZSDihpIgYXJpbC9ib290L2NvbmZpZy9hcHBzYCBkw7ZuZ8O8c8O8IHRldGlrbGl5b3JkdVxyXG5cdCAqIChicmlkZ2UgYFBsdWdpbk1lbnVJdGVtYCB0eXBlJ8SxbsSxIGltcG9ydCBlZGl5b3IpLiBCdW51biB5ZXJpbmUgaG9zdCBzaGVsbFxyXG5cdCAqIChgQXBwTGF5b3V0Q29tcG9uZW50YCkgYHNldE1lbnVJdGVtc1Byb3ZpZGVyKCgpID0+IGJyaWRnZS5ob3N0TWVudUl0ZW1zKCkpYCBpbGUgYmHEn2xhci5cclxuXHQgKiBQcm92aWRlciB5b2tzYSBsYWJlbC9pY29uIHJlc29sdmUndSBhdGxhbsSxciwgVVJMIHNlZ21lbnQgZmFsbGJhY2snaSBkZXZyZXllIGdpcmVyLlxyXG5cdCAqL1xyXG5cdHByaXZhdGUgbWVudUl0ZW1zUHJvdmlkZXI6ICgoKSA9PiBQbHVnaW5NZW51SXRlbVtdKSB8IG51bGwgPSBudWxsO1xyXG5cdHNldE1lbnVJdGVtc1Byb3ZpZGVyKHByb3ZpZGVyOiAoKSA9PiBQbHVnaW5NZW51SXRlbVtdKTogdm9pZCB7XHJcblx0XHR0aGlzLm1lbnVJdGVtc1Byb3ZpZGVyID0gcHJvdmlkZXI7XHJcblx0XHQvLyBQcm92aWRlciBnZcOnIHNldCBlZGlsZGl5c2UgKMO2cm4uIHNoZWxsIGxheW91dCBOYXZpZ2F0aW9uRW5kJ2RlbiBzb25yYSBtb3VudCBvbGR1KSxcclxuXHRcdC8vIGFjdGl2ZVJvdXRlcyd0YSBkZWZhdWx0L2ZhbGxiYWNrIGlzaW1sZSBla2xlbm1pxZ8gaWxrIHRhYifEsSBtZW7DvHlsZSByZXZpemUgZXQuXHJcblx0XHR0aGlzLnJlY29uY2lsZVRhYnNXaXRoTWVudSgpO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogSG9zdCBzaGVsbCBhbmFzYXlmYSBOYXZJdGVtJ8SxbsSxIHNhxJ9sYXIg4oCUIGBBcHBMYXlvdXRDb21wb25lbnQuaG9tZU5hdkl0ZW1gIChtZW7DvGRla2lcclxuXHQgKiBgcm9vdDogdHJ1ZWAgw7bEn2VzaW5kZW4gdMO8cmV0aWxpcjogbmF2TGluay9uYXZOYW1lL2ljb24pLiBUw7xtIHRhYidsYXIga2FwYXTEsWxkxLHEn8SxbmRhXHJcblx0ICogbGlzdGUgYm/FnyBrYWxtYXPEsW4gZGl5ZSBgb3BlbkhvbWVUYWJgIGJ1bnUga3VsbGFuxLFyOyBwcm92aWRlciB5b2tzYSBgL2AgKyBpMThuIGZhbGxiYWNrLlxyXG5cdCAqL1xyXG5cdHByaXZhdGUgaG9tZU5hdkl0ZW1Qcm92aWRlcjogKCgpID0+IE5hdkl0ZW0pIHwgbnVsbCA9IG51bGw7XHJcblx0c2V0SG9tZU5hdkl0ZW1Qcm92aWRlcihwcm92aWRlcjogKCkgPT4gTmF2SXRlbSk6IHZvaWQge1xyXG5cdFx0dGhpcy5ob21lTmF2SXRlbVByb3ZpZGVyID0gcHJvdmlkZXI7XHJcblx0XHQvLyBSZWZyZXNoL2lsayBhw6fEsWzEscWfIHBhdGgnaW5kZSBmYWxsYmFjayAoXCJQYWdlXCIpIGlzaW0vaWtvbmxhIGVrbGVubWnFnyBhbmFzYXlmYSB0YWInxLFcclxuXHRcdC8vIHZhcnNhLCBwcm92aWRlciBoYXrEsXIgb2x1bmNhIG1lbsO8eWxlIChob21lIGRhaGlsKSByZXZpemUgZXQuXHJcblx0XHR0aGlzLnJlY29uY2lsZVRhYnNXaXRoTWVudSgpO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogVVJMIGFkcmVzIMOndWJ1xJ91bmRhbiBnw7Zyw7xsZW4gYWt0aWYgcm91dGUg4oCUIGBUYWJBd2FyZVVybFNlcmlhbGl6ZXJgIGBfdGFiYCdpIHN0cmlwXHJcblx0ICogZXR0acSfaSBpw6dpbiBidSBDTEVBTiBVUkwnZGlyICjDtnIuIGAvY3JtL2FkZHJlc3MtbWFuYWdlbWVudC9zZWFyY2hgKS4gVGFiIG5hdkxpbmsnbGVyaVxyXG5cdCAqIGRlIGNsZWFuIHNha2xhbmTEscSfxLEgacOnaW4gYHIubmF2TGluayA9PT0gYWN0aXZlUm91dGUoKWAgZG/En3J1ZGFuIGthcsWfxLFsYcWfdMSxcsSxbMSxci5cclxuXHQgKi9cclxuXHRhY3RpdmVSb3V0ZSA9IHRvU2lnbmFsPHN0cmluZz4oXHJcblx0XHR0aGlzLnJvdXRlci5ldmVudHMucGlwZShcclxuXHRcdFx0ZmlsdGVyKChlKSA9PiBlIGluc3RhbmNlb2YgTmF2aWdhdGlvbkVuZCksXHJcblx0XHRcdHRhcCgoZSkgPT4ge1xyXG5cdFx0XHRcdC8vIEhlciBOYXZpZ2F0aW9uRW5kJ2RlIGhpc3Rvcnkgc3RhdGUndGVuIGFrdGlmIHRhYklkJ3lpIG9rdXl1cCBzaWduYWwnYSB5YXouXHJcblx0XHRcdFx0Ly8gYGV4dHJhcy5zdGF0ZWAgaWxlIHlhcMSxbGFuIG5hdmlnYXRpb24nbGFyZGEgQW5ndWxhciBoaXN0b3J5LnN0YXRlJ2UgYF90YWJgJ2kga295YXI7XHJcblx0XHRcdFx0Ly8gYnJvd3NlciBiYWNrL2ZvcndhcmQgbmF2aWdhc3lvbmxhcsSxbmRhIGRhIGhpc3Rvcnkuc3RhdGUga29ydW51ci5cclxuXHRcdFx0XHRjb25zdCB0YWJJZEZyb21TdGF0ZSA9ICh3aW5kb3cuaGlzdG9yeS5zdGF0ZSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IG51bGwpPy5bJ190YWInXTtcclxuXHRcdFx0XHRpZiAodHlwZW9mIHRhYklkRnJvbVN0YXRlID09PSAnc3RyaW5nJyAmJiB0YWJJZEZyb21TdGF0ZS5sZW5ndGggPiAwKSB7XHJcblx0XHRcdFx0XHR0aGlzLmFjdGl2ZVRhYklkLnNldCh0YWJJZEZyb21TdGF0ZSk7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRjb25zdCBjdXJyZW50VXJsID0gKGUgYXMgTmF2aWdhdGlvbkVuZCkudXJsQWZ0ZXJSZWRpcmVjdHMgfHwgKGUgYXMgTmF2aWdhdGlvbkVuZCkudXJsO1xyXG5cclxuXHRcdFx0XHQvLyBOT1Q6IEFrdGlmIHRhYifEsW4gbmF2TGluaydpbmkgZ8O8bmNlbGxlbWUgYHN5bmNBY3RpdmVUYWJOYXZMaW5rRm9yVGFiYCBpbGVcclxuXHRcdFx0XHQvLyBgYnJpZGdlLmFjdGl2ZU1GRVVybGAgw7x6ZXJpbmRlbiB5YXDEsWzEsXIgKGtheW5hxJ9hLWJhxJ9sxLEsIGNyb3NzLXRhYiBzYWZlKS5cclxuXHJcblx0XHRcdFx0Ly8gVGFiIGBhY3RpdmVSb3V0ZXNgJ3RhIFlPS1NBIHTDvHJldDogKGEpIGlsayBhw6fEsWzEscWfL3JlZnJlc2ggZGVlcC1saW5rLCBWRVlBXHJcblx0XHRcdFx0Ly8gKGIpIGJyb3dzZXIgYmFjay9mb3J3YXJkIGlsZSBLQVBBVElMTUnFniB0YWInxLFuIGhpc3RvcnkgZW50cnknc2luZSBkw7Zuw7zFn1xyXG5cdFx0XHRcdC8vIChrYXBhdG1hIGhvc3QgaGlzdG9yeSd5aSBzaWxtZXog4oaSIGdlcmkgdHXFn3UgbyBVUkwnZSBnaWRlciwgdGFiIHllbmlkZW4gYcOnxLFsbWFsxLEpLlxyXG5cdFx0XHRcdC8vIEJpbGluZW4gdGFiIChub3JtYWwgdMSxa2xhbWEgLyB5ZW5pIHRhYiAvIGHDp8SxayB0YWInYSBnZXJpKSDihpIgeWFsbsSxeiBhY3RpdmVUYWJJZFxyXG5cdFx0XHRcdC8vIGfDvG5jZWxsZW5pciAoeXVrYXLEsWRhKSwgbWV2Y3V0IHRhYidhIGZvY3VzOyB0w7xyZXRtZSB5YXDEsWxtYXouXHJcblx0XHRcdFx0Y29uc3Qga25vd25UYWIgPVxyXG5cdFx0XHRcdFx0dHlwZW9mIHRhYklkRnJvbVN0YXRlID09PSAnc3RyaW5nJyAmJlxyXG5cdFx0XHRcdFx0dGFiSWRGcm9tU3RhdGUubGVuZ3RoID4gMCAmJlxyXG5cdFx0XHRcdFx0dGhpcy5hY3RpdmVSb3V0ZXMoKS5zb21lKChyKSA9PiByLnRhYklkID09PSB0YWJJZEZyb21TdGF0ZSk7XHJcblx0XHRcdFx0aWYgKCFrbm93blRhYikge1xyXG5cdFx0XHRcdFx0Ly8gUmVmcmVzaCBzb25yYXPEsSBkZWVwIGxpbmsg4oaSIFVSTCdkZW4gbmF2IGl0ZW0gdMO8cmV0IHZlIGFjdGl2ZVJvdXRlcydhIGVrbGUuXHJcblx0XHRcdFx0XHQvLyBVUkwnZGUgYF90YWJgIHlva3NhIChVcmxTZXJpYWxpemVyIHN0cmlwIGV0dGnEn2kgacOnaW4gYWRyZXMgw6d1YnXEn3VuZGFuIHBheWxhxZ/EsWxhblxyXG5cdFx0XHRcdFx0Ly8gbGluayBgX3RhYmAgdGHFn8SxbWF6KSB5ZW5pIHRhYklkIMO8cmV0ICsgaGlzdG9yeSBzdGF0ZSdlIHlhejsgbmF2aWdhdGVCeVVybFxyXG5cdFx0XHRcdFx0Ly8gw6dhxJ9yxLFzxLEgaWxlIHN0YXRlIGluamVrdGUgZWRlcml6LCBhZHJlcyDDp3VidcSfdSB0ZW1peiBrYWzEsXIuXHJcblx0XHRcdFx0XHRpZiAoY3VycmVudFVybCkge1xyXG5cdFx0XHRcdFx0XHRjb25zdCBzdGF0ZVRhYklkID0gKHdpbmRvdy5oaXN0b3J5LnN0YXRlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgbnVsbCk/LlsnX3RhYiddO1xyXG5cdFx0XHRcdFx0XHRjb25zdCBuYXZMaW5rID0gY2xlYW5OYXZMaW5rKGN1cnJlbnRVcmwpO1xyXG5cdFx0XHRcdFx0XHQvLyDEsGxrIG5hdmlnYXRpb24nZGEgKEY1L2HDp8SxbMSxxZ8pIGdlbGVuIFVSTCB6YXRlbiBhY3RpdmVSb3V0ZXMndGFraSBiaXIgdGFiIGlsZVxyXG5cdFx0XHRcdFx0XHQvLyBlxZ9sZcWfaXlvcnNhICjDtnplbGxpa2xlIHJlc3RvcmUgZWRpbG1pxZ8gcGlubmVkIHRhYikgWUVOxLAgdGFiIFTDnFJFVE1FIOKAlCBvIHRhYifEsVxyXG5cdFx0XHRcdFx0XHQvLyBrdWxsYW4uIEY1IHNvbnJhc8SxIEFuZ3VsYXIgaW5pdGlhbCBuYXZpZ2F0aW9uIGBoaXN0b3J5LnN0YXRlLl90YWJgJ8SxIGtvcnVNQVog4oaSXHJcblx0XHRcdFx0XHRcdC8vIGBrbm93blRhYmAgeWFrYWxheWFtYXo7IG5hdkxpbmsgZcWfbGXFn21lc2kgcmVzdG9yZS1kdXBsaWNhdGUnaW5pIMO2bmxlci4gWWFsbsSxeiBpbGtcclxuXHRcdFx0XHRcdFx0Ly8gY2hlY2sndGUgKyBzdGF0ZSd0ZSBgX3RhYmAgeW9ra2VuOiBzb25yYWtpIG5hdmlnYXRpb25sYXJkYSBrYXPEsXRsxLEgZHVwbGljYXRlIHRhYiB2ZVxyXG5cdFx0XHRcdFx0XHQvLyBicm93c2VyLWJhY2sgaWxlIGthcGF0xLFsYW4gdGFiJ8SxbiByZS1kZXJpdmUgZGF2cmFuxLHFn8SxIGtvcnVudXIuXHJcblx0XHRcdFx0XHRcdGNvbnN0IGV4aXN0aW5nID1cclxuXHRcdFx0XHRcdFx0XHQhdGhpcy5maXJzdENoZWNrRm9yUm91dGUgJiYgIXN0YXRlVGFiSWRcclxuXHRcdFx0XHRcdFx0XHRcdD8gdGhpcy5hY3RpdmVSb3V0ZXMoKS5maW5kKChyKSA9PiByLm5hdkxpbmsgPT09IG5hdkxpbmspXHJcblx0XHRcdFx0XHRcdFx0XHQ6IHVuZGVmaW5lZDtcclxuXHRcdFx0XHRcdFx0bGV0IHRhYklkID1cclxuXHRcdFx0XHRcdFx0XHR0eXBlb2Ygc3RhdGVUYWJJZCA9PT0gJ3N0cmluZycgJiYgc3RhdGVUYWJJZC5sZW5ndGggPiAwID8gc3RhdGVUYWJJZCA6IGV4aXN0aW5nPy50YWJJZCA/PyAnJztcclxuXHRcdFx0XHRcdFx0bGV0IG5lZWRzU3RhdGVJbmplY3QgPSBmYWxzZTtcclxuXHRcdFx0XHRcdFx0aWYgKCF0YWJJZCkge1xyXG5cdFx0XHRcdFx0XHRcdHRhYklkID0gdGhpcy5nZW5lcmF0ZVRhYklkKCk7XHJcblx0XHRcdFx0XHRcdFx0bmVlZHNTdGF0ZUluamVjdCA9IHRydWU7XHJcblx0XHRcdFx0XHRcdH0gZWxzZSBpZiAoZXhpc3RpbmcpIHtcclxuXHRcdFx0XHRcdFx0XHQvLyBFxZ9sZcWfZW4gdGFiJ8SxbiBgdGFiSWRgJ3NpbmkgVVJML3N0YXRlJ2UgeWFuc8SxdCDigJQgYWRyZXMgw6d1YnXEn3UgYF90YWJgIHRhxZ/EsW1hZMSxxJ/EsVxyXG5cdFx0XHRcdFx0XHRcdC8vIGnDp2luIFJvdXRlUmV1c2UgYG1mZTo8cmVtb3RlPjo8dGFiSWQ+YCBrZXkgdHV0YXJsxLFsxLHEn8SxIHN0YXRlIGluamVjdCBpc3Rlci5cclxuXHRcdFx0XHRcdFx0XHRuZWVkc1N0YXRlSW5qZWN0ID0gdHJ1ZTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0XHR0aGlzLmFjdGl2ZVRhYklkLnNldCh0YWJJZCk7XHJcblx0XHRcdFx0XHRcdGlmICghZXhpc3RpbmcpIHtcclxuXHRcdFx0XHRcdFx0XHRjb25zdCBtZW51SW5mbyA9IHRoaXMucmVzb2x2ZU5hdkluZm9Gcm9tTWVudShuYXZMaW5rKTtcclxuXHRcdFx0XHRcdFx0XHRjb25zdCBmYWxsYmFja05hbWUgPSAoKTogc3RyaW5nID0+IHtcclxuXHRcdFx0XHRcdFx0XHRcdGxldCBwYXRoID0gbmF2TGluaztcclxuXHRcdFx0XHRcdFx0XHRcdGlmIChwYXRoLnN0YXJ0c1dpdGgoJy8nKSkgcGF0aCA9IHBhdGguc2xpY2UoMSk7XHJcblx0XHRcdFx0XHRcdFx0XHRjb25zdCBwYXJ0cyA9IHBhdGguc3BsaXQoJy8nKS5maWx0ZXIoKHMpID0+IHMubGVuZ3RoID4gMCk7XHJcblx0XHRcdFx0XHRcdFx0XHRjb25zdCBsYXN0ID0gcGFydHNbcGFydHMubGVuZ3RoIC0gMV0gfHwgJ1BhZ2UnO1xyXG5cdFx0XHRcdFx0XHRcdFx0cmV0dXJuIGxhc3QuY2hhckF0KDApLnRvVXBwZXJDYXNlKCkgKyBsYXN0LnNsaWNlKDEpLnJlcGxhY2UoLy0vZywgJyAnKTtcclxuXHRcdFx0XHRcdFx0XHR9O1xyXG5cdFx0XHRcdFx0XHRcdHRoaXMuYWRkVG9BY3RpdmVSb3V0ZXMoe1xyXG5cdFx0XHRcdFx0XHRcdFx0dGFiSWQsXHJcblx0XHRcdFx0XHRcdFx0XHRuYXZMaW5rLFxyXG5cdFx0XHRcdFx0XHRcdFx0bmF2TmFtZTogbWVudUluZm8/Lm5hdk5hbWUgfHwgZmFsbGJhY2tOYW1lKCksXHJcblx0XHRcdFx0XHRcdFx0XHRpY29uOiBtZW51SW5mbz8uaWNvblxyXG5cdFx0XHRcdFx0XHRcdH0pO1xyXG5cdFx0XHRcdFx0XHR9XHJcblx0XHRcdFx0XHRcdGlmIChuZWVkc1N0YXRlSW5qZWN0KSB7XHJcblx0XHRcdFx0XHRcdFx0Ly8gTWV2Y3V0IG5hdmlnYXRpb24gY3ljbGUgYml0aW1pbmRlbiBTT05SQSB5ZW5pIG5hdmlnYXRpb24gdGV0aWtsZSDigJRcclxuXHRcdFx0XHRcdFx0XHQvLyBgcXVldWVNaWNyb3Rhc2tgIFJvdXRlcifEsW4gbmF2aWdhdGlvbiBrdXlydcSfdSBmaW5hbGl6ZSBvbG1hZGFuIMOnYWzEscWfxLFwXHJcblx0XHRcdFx0XHRcdFx0Ly8gYEFib3J0RXJyb3I6IFRyYW5zaXRpb24gd2FzIHNraXBwZWRgJ2EgeW9sIGHDp8SxeW9yZHUuIGBzZXRUaW1lb3V0KDApYCBtYWNyb3Rhc2tcclxuXHRcdFx0XHRcdFx0XHQvLyBpbGUgY3VycmVudCBjeWNsZSBiaXRlciwgUm91dGVyIGlkbGUgb2x1ci4gYHRoaXMubmF2aWdhdGVgIHphdGVuIEFib3J0RXJyb3InxLFcclxuXHRcdFx0XHRcdFx0XHQvLyB5dXR1eW9yIOKAlCBjb25jdXJyZW50IG5hdmlnYXRpb24gcmFjZSdsZXJpIHNlc3NpemNlIGlwdGFsIGVkaWxpci5cclxuXHRcdFx0XHRcdFx0XHRzZXRUaW1lb3V0KCgpID0+IHtcclxuXHRcdFx0XHRcdFx0XHRcdHNhZmVOYXZpZ2F0ZSh0aGlzLnJvdXRlciwgd2l0aFRhYklkKG5hdkxpbmssIHRhYklkKSwge1xyXG5cdFx0XHRcdFx0XHRcdFx0XHRyZXBsYWNlVXJsOiB0cnVlLFxyXG5cdFx0XHRcdFx0XHRcdFx0XHRzdGF0ZTogeyBfdGFiOiB0YWJJZCB9XHJcblx0XHRcdFx0XHRcdFx0XHR9KS5jYXRjaCgoKSA9PiB7LyogcmVmcmVzaC1wYXRoIG5vaXNlOiBjYWxsZXIgeW9rLCBzaWxlbnQgKi99KTtcclxuXHRcdFx0XHRcdFx0XHR9LCAwKTtcclxuXHRcdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdFx0dGhpcy5maXJzdENoZWNrRm9yUm91dGUgPSB0cnVlO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fSksXHJcblx0XHRcdG1hcCgodykgPT4gKHcgYXMgTmF2aWdhdGlvbkVuZCkudXJsQWZ0ZXJSZWRpcmVjdHMgfHwgKHcgYXMgTmF2aWdhdGlvbkVuZCkudXJsKVxyXG5cdFx0KVxyXG5cdCk7XHJcblxyXG5cdGFjdGl2ZVJvdXRlcyA9IHNpZ25hbDxOYXZJdGVtW10+KFtdKTtcclxuXHQvKiogSGlzdG9yeSBzdGF0ZSd0ZW4gb2t1bmFuIGFrdGlmIHRhYiBraW1sacSfaSDigJQgYGlzQWN0aXZlKHRhYikgPSB0YWIudGFiSWQgPT09IGFjdGl2ZVRhYklkKClgLiAqL1xyXG5cdGFjdGl2ZVRhYklkID0gc2lnbmFsPHN0cmluZz4oJycpO1xyXG5cclxuXHQvKipcclxuXHQgKiBDaHJvbWUgYm9va21hcmsgY2xpY2sgZGF2cmFuxLHFn8SxOiBha3RpZiB0YWInxLFuIG5hdkxpbmsvbmF2TmFtZSdpIGfDvG5jZWxsZW5pcixcclxuXHQgKiBvIHRhYidhIG5hdmlnYXRlIGVkaWxpci4gWWVuaSB0YWIgYcOnxLFsbWF6LiBNZXZjdXQgdGFiJ8SxbiBgdGFiSWRgJ3NpIEtPUlVOVVIg4oCUXHJcblx0ICogUm91dGVyJ2EgYD9fdGFiPXRhYklkYCBpbGUgbmF2aWdhdGUgZWRpbGlyLCBoaXN0b3J5IHN0YXRlJ2UgeWF6xLFsxLFyOyBuYXZMaW5rXHJcblx0ICogc3RvcmFnZSdkYSBDTEVBTiBzYWtsYW7EsXIuXHJcblx0ICovXHJcblx0bmF2aWdhdGVJbkN1cnJlbnRUYWIobmF2SXRlbTogTmF2SXRlbSk6IHZvaWQge1xyXG5cdFx0Y29uc3QgaWR4ID0gdGhpcy5maW5kQWN0aXZlVGFiSW5kZXgoKTtcclxuXHRcdGlmIChpZHggPT09IC0xKSB7XHJcblx0XHRcdC8vIEFrdGlmIHRhYiB5b2sg4oCUIGZhbGxiYWNrIG9sYXJhayB5ZW5pIHRhYiBhw6cuXHJcblx0XHRcdHRoaXMub3BlbkluTmV3VGFiQW5kRm9jdXMobmF2SXRlbSk7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHRcdGNvbnN0IGV4aXN0aW5nVGFiID0gdGhpcy5hY3RpdmVSb3V0ZXMoKVtpZHhdO1xyXG5cdFx0Y29uc3QgdGFiSWQgPSBleGlzdGluZ1RhYi50YWJJZDtcclxuXHRcdGNvbnN0IG5ld05hdkxpbmsgPSBjbGVhbk5hdkxpbmsobmF2SXRlbS5uYXZMaW5rKTtcclxuXHRcdC8vIEF5bsSxIG5hdkxpbmsnZSB0xLFrbGFtYSDihpIgeWluZSBkZSBuYXZpZ2F0ZSBldCAoYG9uU2FtZVVybE5hdmlnYXRpb246ICdyZWxvYWQnYCkuXHJcblx0XHRpZiAoZXhpc3RpbmdUYWIubmF2TGluayA9PT0gbmV3TmF2TGluaykge1xyXG5cdFx0XHRzYWZlTmF2aWdhdGUodGhpcy5yb3V0ZXIsIHdpdGhUYWJJZChuZXdOYXZMaW5rLCB0YWJJZCksIHRhYlN0YXRlKHRhYklkKSk7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHRcdHRoaXMuYWN0aXZlUm91dGVzLnVwZGF0ZSgocm91dGVzKSA9PiB7XHJcblx0XHRcdGNvbnN0IG5leHQgPSBbLi4ucm91dGVzXTtcclxuXHRcdFx0Ly8gTWV2Y3V0IHRhYifEsW4gYHRhYklkYCBWRSBgcGlubmVkYCBkdXJ1bXUga29ydW51ciDigJQgbWVuw7xkZW4gZ2VsZW4gbmF2SXRlbSBgcGlubmVkYFxyXG5cdFx0XHQvLyB0YcWfxLFtYXo7IGB7IC4uLm5hdkl0ZW0gfWAgaWxlIGV6aWxpcnNlIHNhYml0bGVubWnFnyB0YWIgdW5waW4gb2x1cmR1ICh0YWIgacOnZXJpxJ9pbmlcclxuXHRcdFx0Ly8gZGXEn2nFn3Rpcm1layBzYWJpdGxlbWV5aSBib3ptYW1hbMSxKS5cclxuXHRcdFx0bmV4dFtpZHhdID0geyAuLi5uYXZJdGVtLCB0YWJJZCwgbmF2TGluazogbmV3TmF2TGluaywgcGlubmVkOiBleGlzdGluZ1RhYi5waW5uZWQgfTtcclxuXHRcdFx0cmV0dXJuIG5leHQ7XHJcblx0XHR9KTtcclxuXHRcdHNhZmVOYXZpZ2F0ZSh0aGlzLnJvdXRlciwgd2l0aFRhYklkKG5ld05hdkxpbmssIHRhYklkKSwgdGFiU3RhdGUodGFiSWQpKTtcclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIEFya2EgcGxhbmRhIHllbmkgdGFiIGHDpyDigJQgYWt0aWYgdGFiIGRlxJ9pxZ9tZXosIG5hdmlnYXRlIGVkaWxtZXouIGB0YWJJZGAgaGVyIHphbWFuXHJcblx0ICogeWVuaSDDvHJldGlsaXIgKHlhIGRhIGNhbGxlciB2ZXJkaXlzZSBvIGt1bGxhbsSxbMSxciksIGV4aXN0cyBjaGVjayBZT0tUVVI7IGF5bsSxIHBhdGhcclxuXHQgKiBiaXJkZW4gZmF6bGEgdGFiJ2RhIGJhxJ/EsW1zxLF6IHlhxZ9heWFiaWxpci5cclxuXHQgKi9cclxuXHRvcGVuSW5CYWNrZ3JvdW5kVGFiKG5hdkl0ZW06IE5hdkl0ZW0pOiB2b2lkIHtcclxuXHRcdGlmICh0aGlzLmFjdGl2ZVJvdXRlcygpLmxlbmd0aCA+PSB0aGlzLk1BWF9UQUJTKSB7XHJcblx0XHRcdHRoaXMubm90aWZ5TWF4VGFic1JlYWNoZWQoKTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cdFx0Y29uc3QgdGFiSWQgPSBuYXZJdGVtLnRhYklkIHx8IHRoaXMuZ2VuZXJhdGVUYWJJZCgpO1xyXG5cdFx0Y29uc3QgbmF2TGluayA9IGNsZWFuTmF2TGluayhuYXZJdGVtLm5hdkxpbmspO1xyXG5cdFx0dGhpcy5hY3RpdmVSb3V0ZXMudXBkYXRlKCh2YWwpID0+IFsuLi52YWwsIHsgLi4ubmF2SXRlbSwgdGFiSWQsIG5hdkxpbmsgfV0pO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogWWVuaSB0YWIgYcOnIHZlIG8gdGFiJ2EgZ2XDpy4gRXhpc3RzIGNoZWNrIFlPSyDigJQgYXluxLEgcGF0aCdpbiBpa2kgYXlyxLEgdGFiJ2kga2FidWwgZWRpbGlyO1xyXG5cdCAqIGhlciB0YWIga2VuZGkgYHRhYklkYCdzaXlsZSBheXLEsSBjb21wb25lbnRSZWYgaW5zdGFudGlhdGUgZWRlci5cclxuXHQgKi9cclxuXHRvcGVuSW5OZXdUYWJBbmRGb2N1cyhuYXZJdGVtOiBOYXZJdGVtKTogdm9pZCB7XHJcblx0XHRpZiAodGhpcy5hY3RpdmVSb3V0ZXMoKS5sZW5ndGggPj0gdGhpcy5NQVhfVEFCUykge1xyXG5cdFx0XHR0aGlzLm5vdGlmeU1heFRhYnNSZWFjaGVkKCk7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHRcdGNvbnN0IHRhYklkID0gbmF2SXRlbS50YWJJZCB8fCB0aGlzLmdlbmVyYXRlVGFiSWQoKTtcclxuXHRcdGNvbnN0IG5hdkxpbmsgPSBjbGVhbk5hdkxpbmsobmF2SXRlbS5uYXZMaW5rKTtcclxuXHRcdHRoaXMuYWN0aXZlUm91dGVzLnVwZGF0ZSgodmFsKSA9PiBbLi4udmFsLCB7IC4uLm5hdkl0ZW0sIHRhYklkLCBuYXZMaW5rIH1dKTtcclxuXHRcdHNhZmVOYXZpZ2F0ZSh0aGlzLnJvdXRlciwgd2l0aFRhYklkKG5hdkxpbmssIHRhYklkKSwgdGFiU3RhdGUodGFiSWQpKTtcclxuXHR9XHJcblxyXG5cdC8qKiBQcm9ncmFtYXRpayB0YWIgZWtsZW1lOyBhY3RpdmVSb3V0ZSBvYnNlcnZhYmxlIHJlZnJlc2ggcGF0aCdpIGJ1bnUga3VsbGFuxLFyLiAqL1xyXG5cdGFkZFRvQWN0aXZlUm91dGVzKG5hdkl0ZW06IE5hdkl0ZW0pOiB2b2lkIHtcclxuXHRcdGlmICh0aGlzLmFjdGl2ZVJvdXRlcygpLmxlbmd0aCA+PSB0aGlzLk1BWF9UQUJTKSB7XHJcblx0XHRcdC8vIFByb2dyYW1tYXRpYyDDp2HEn3LEsSDigJQgdG9hc3QgZ8O2c3Rlcm1layB5ZXJpbmUgc2Vzc2l6Y2UgcmV0dXJuLlxyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblx0XHRjb25zdCB0YWJJZCA9IG5hdkl0ZW0udGFiSWQgfHwgdGhpcy5nZW5lcmF0ZVRhYklkKCk7XHJcblx0XHRjb25zdCBuYXZMaW5rID0gY2xlYW5OYXZMaW5rKG5hdkl0ZW0ubmF2TGluayk7XHJcblx0XHQvLyBTaWduYWwgY2hhbmdlIGRldGVjdGlvbiByZWZlcmVuY2UgaWRlbnRpdHkgaWxlIMOnYWzEscWfxLFyIOKAlCB5ZW5pIGFycmF5IGTDtm4uXHJcblx0XHR0aGlzLmFjdGl2ZVJvdXRlcy51cGRhdGUoKHZhbCkgPT4gWy4uLnZhbCwgeyAuLi5uYXZJdGVtLCB0YWJJZCwgbmF2TGluayB9XSk7XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBBa3RpZiB0YWInxLEgYGFjdGl2ZVRhYklkYCDDvHplcmluZGVuIGJ1bHVyLiBBeW7EsSBgbmF2TGlua2AnaW4gYmlyZGVuIGZhemxhIHRhYidpXHJcblx0ICogb2xhYmlsZWNlxJ9pIGnDp2luIG5hdkxpbmsga2FyxZ/EsWxhxZ90xLFybWFzxLEgYW1iaWd1b3VzOyB0YWJJZCB0ZWsgZG/En3J1IGtpbWxpay5cclxuXHQgKi9cclxuXHRwcml2YXRlIGZpbmRBY3RpdmVUYWJJbmRleCgpOiBudW1iZXIge1xyXG5cdFx0Y29uc3QgdGFiSWQgPSB0aGlzLmFjdGl2ZVRhYklkKCk7XHJcblx0XHRpZiAoIXRhYklkKSByZXR1cm4gLTE7XHJcblx0XHRyZXR1cm4gdGhpcy5hY3RpdmVSb3V0ZXMoKS5maW5kSW5kZXgoKHIpID0+IHIudGFiSWQgPT09IHRhYklkKTtcclxuXHR9XHJcblxyXG5cdC8qKiBUYWIgbGltaXRpIGHFn8SxbGTEscSfxLFuZGEga3VsbGFuxLFjxLF5xLEgdG9hc3QgaWxlIGJpbGdpbGVuZGlyaXIuICovXHJcblx0cHJpdmF0ZSBub3RpZnlNYXhUYWJzUmVhY2hlZCgpOiB2b2lkIHtcclxuXHRcdHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcclxuXHRcdFx0c2V2ZXJpdHk6ICd3YXJuJyxcclxuXHRcdFx0c3VtbWFyeTogdGhpcy50cmFuc2xvY29TZXJ2aWNlLnRyYW5zbGF0ZSgndGFiLm1heFJlYWNoZWRTdW1tYXJ5JywgeyBtYXg6IHRoaXMuTUFYX1RBQlMgfSksXHJcblx0XHRcdGRldGFpbDogdGhpcy50cmFuc2xvY29TZXJ2aWNlLnRyYW5zbGF0ZSgndGFiLm1heFJlYWNoZWREZXRhaWwnKSxcclxuXHRcdFx0a2V5OiAndG9hc3Qtcm9vdCdcclxuXHRcdH0pO1xyXG5cdH1cclxuXHJcblx0LyoqIEFuYXNheWZhIG5hdkxpbmsnaSDigJQgaG9zdCBtZW7DvCByb290IGl0ZW0nxLFuZGFuICh5b2tzYSAnLycpLCBjbGVhbiBmb3JtYXQuICovXHJcblx0cHJpdmF0ZSBob21lTmF2TGluaygpOiBzdHJpbmcge1xyXG5cdFx0cmV0dXJuIGNsZWFuTmF2TGluayh0aGlzLmhvbWVOYXZJdGVtUHJvdmlkZXI/LigpLm5hdkxpbmsgfHwgJy8nKTtcclxuXHR9XHJcblxyXG5cdC8qKiBWZXJpbGVuIHRhYiBhbmFzYXlmYXlhIG3EsSBhaXQuICovXHJcblx0cHJpdmF0ZSBpc0hvbWVUYWIoaXRlbTogTmF2SXRlbSk6IGJvb2xlYW4ge1xyXG5cdFx0cmV0dXJuIGl0ZW0ubmF2TGluayA9PT0gdGhpcy5ob21lTmF2TGluaygpO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogQW5hc2F5ZmEgdGFiJ8SxIGHDp8SxcCBvbmEgbmF2aWdhdGUgZWRlciDigJQgdGFiIGxpc3Rlc2kgSMSww4cgYm/FnyBrYWxtYW1hbMSxIChlbiBheiAxIHRhYikuXHJcblx0ICogTmF2SXRlbSBob3N0IG1lbsO8IHJvb3QgaXRlbSfEsW5kYW4gZ2VsaXI7IHlva3NhIGAvYCArIGkxOG4gZmFsbGJhY2sgKCd0YWIuaG9tZScsICdwaSBwaS1ob21lJykuXHJcblx0ICovXHJcblx0cHJpdmF0ZSBvcGVuSG9tZVRhYigpOiBQcm9taXNlPGJvb2xlYW4+IHtcclxuXHRcdGNvbnN0IGJhc2UgPSB0aGlzLmhvbWVOYXZJdGVtUHJvdmlkZXI/LigpO1xyXG5cdFx0Y29uc3QgbmF2TGluayA9IGNsZWFuTmF2TGluayhiYXNlPy5uYXZMaW5rIHx8ICcvJyk7XHJcblx0XHRjb25zdCB0YWJJZCA9IHRoaXMuZ2VuZXJhdGVUYWJJZCgpO1xyXG5cdFx0Y29uc3QgaG9tZTogTmF2SXRlbSA9IHtcclxuXHRcdFx0dGFiSWQsXHJcblx0XHRcdG5hdkxpbmssXHJcblx0XHRcdG5hdk5hbWU6IGJhc2U/Lm5hdk5hbWUgfHwgdGhpcy50cmFuc2xvY29TZXJ2aWNlLnRyYW5zbGF0ZSgndGFiLmhvbWUnKSxcclxuXHRcdFx0aWNvbjogYmFzZT8uaWNvbiB8fCAncGkgcGktaG9tZSdcclxuXHRcdH07XHJcblx0XHR0aGlzLmFjdGl2ZVJvdXRlcy51cGRhdGUoKHZhbCkgPT4gWy4uLnZhbCwgaG9tZV0pO1xyXG5cdFx0cmV0dXJuIHNhZmVOYXZpZ2F0ZSh0aGlzLnJvdXRlciwgd2l0aFRhYklkKG5hdkxpbmssIHRhYklkKSwgdGFiU3RhdGUodGFiSWQpKTtcclxuXHR9XHJcblxyXG5cdGNsb3NlTmF2aWdhdGlvbihuYXZJdGVtOiBOYXZJdGVtKTogdm9pZCB7XHJcblx0XHQvLyBgdGFiSWRgIGlsZSBidWwg4oCUIGF5bsSxIHBhdGgnaW4gYmlyZGVuIGZhemxhIHRhYidpIG9sYWJpbGVjZcSfaSBpw6dpbiBgbmF2TGlua2BcclxuXHRcdC8vIGthcsWfxLFsYcWfdMSxcm1hc8SxIGFtYmlndW91cyBvbHVyLCBraW1saWsgYmF6bMSxIGFyYW1hIHRlayBkb8SfcnUgecO2bnRlbS5cclxuXHRcdGNvbnN0IGlkeCA9IHRoaXMuYWN0aXZlUm91dGVzKCkuZmluZEluZGV4KChyKSA9PiByLnRhYklkID09PSBuYXZJdGVtLnRhYklkKTtcclxuXHRcdGNvbnNvbGUubG9nKGBbTmF2U2VydmljZV0gY2xvc2VOYXZpZ2F0aW9uIHRhYklkPVwiJHtuYXZJdGVtLnRhYklkfVwiIG5hdkxpbms9XCIke25hdkl0ZW0ubmF2TGlua31cIiBpbmRleD0ke2lkeH1gKTtcclxuXHRcdGlmIChpZHggPT09IC0xKSByZXR1cm47XHJcblxyXG5cdFx0Y29uc3QgY2xvc2VkSXRlbSA9IHRoaXMuYWN0aXZlUm91dGVzKClbaWR4XTtcclxuXHRcdC8vIFNvbiBrYWxhbiB0YWIgYW5hc2F5ZmF5c2Ega2FwYXRtYSDigJQgaGVyIHphbWFuIGVuIGF6IDEgKGFuYXNheWZhKSB0YWIgYcOnxLFrIGthbG1hbMSxLlxyXG5cdFx0aWYgKHRoaXMuYWN0aXZlUm91dGVzKCkubGVuZ3RoID09PSAxICYmIHRoaXMuaXNIb21lVGFiKGNsb3NlZEl0ZW0pKSByZXR1cm47XHJcblx0XHQvLyBTaWduYWwgY2hhbmdlIGRldGVjdGlvbiByZWZlcmVuY2UgaWRlbnRpdHkgaWxlIMOnYWzEscWfxLFyIOKAlCB5ZW5pIGFycmF5IGTDtm4uXHJcblx0XHR0aGlzLmFjdGl2ZVJvdXRlcy51cGRhdGUoKHZhbCkgPT4gdmFsLmZpbHRlcigoXywgaSkgPT4gaSAhPT0gaWR4KSk7XHJcblxyXG5cdFx0Y29uc3QgY2xlYW51cCA9ICgpOiB2b2lkID0+IHRoaXMuY2xlYW51cFRhYkhhbmRsZShjbG9zZWRJdGVtKTtcclxuXHRcdGNvbnN0IGNsb3NpbmdBY3RpdmVUYWIgPSB0aGlzLmFjdGl2ZVRhYklkKCkgPT09IGNsb3NlZEl0ZW0udGFiSWQ7XHJcblxyXG5cdFx0aWYgKGNsb3NpbmdBY3RpdmVUYWIpIHtcclxuXHRcdFx0Ly8gQWt0aWYgdGFiIGthcGF0xLFsxLF5b3Ig4oCUIGJhxZ9rYSB0YWInYSAodmV5YSBob21lJ2EpIG5hdmlnYXRlIGVkaXAgY2xlYW51cCfEsSBTT05SQSB0ZXRpa2xlLlxyXG5cdFx0XHQvLyBBa3NpIHRha2RpcmRlIHN0cmF0ZWd5IGhlbsO8eiBgc3RvcmUoJ21mZToke3JlbW90ZU5hbWV9OiR7dGFiSWR9JywgaGFuZGxlKWAnaSB5YXBtYWRhblxyXG5cdFx0XHQvLyBgZGVsZXRlU3RvcmVkUm91dGVgIMOnYcSfcsSxbMSxciB2ZSBjbGVhbnVwIGJvxZ8gaGl0IGVkZXI7IGFyZMSxbmRhbiBkZXRhY2ggdGFtYW1sYW7EsW5jYVxyXG5cdFx0XHQvLyBlbGVtZW50IGhpZGRlbiBjb250YWluZXInZGEgb3JwaGFuIGthbMSxciAocmFjZSBjb25kaXRpb24pLlxyXG5cdFx0XHRjb25zdCByZW1haW5pbmcgPSB0aGlzLmFjdGl2ZVJvdXRlcygpO1xyXG5cdFx0XHRjb25zdCB3aWxsTmF2aWdhdGVJdGVtID0gcmVtYWluaW5nLmxlbmd0aCA+IDAgPyByZW1haW5pbmdbaWR4XSB8fCByZW1haW5pbmdbaWR4IC0gMV0gOiBudWxsO1xyXG5cdFx0XHRpZiAod2lsbE5hdmlnYXRlSXRlbSkge1xyXG5cdFx0XHRcdHNhZmVOYXZpZ2F0ZShcclxuXHRcdFx0XHRcdHRoaXMucm91dGVyLFxyXG5cdFx0XHRcdFx0d2l0aFRhYklkKHdpbGxOYXZpZ2F0ZUl0ZW0ubmF2TGluaywgd2lsbE5hdmlnYXRlSXRlbS50YWJJZCksXHJcblx0XHRcdFx0XHR0YWJTdGF0ZSh3aWxsTmF2aWdhdGVJdGVtLnRhYklkKVxyXG5cdFx0XHRcdCkuZmluYWxseShjbGVhbnVwKTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHQvLyBIacOnIHRhYiBrYWxtYWTEsSDigJQgYm/FnyBixLFyYWttYSwgYW5hc2F5ZmEgdGFiJ8SxIGHDpyAoZW4gYXogMSB0YWIga3VyYWzEsSkuXHJcblx0XHRcdFx0dGhpcy5vcGVuSG9tZVRhYigpLmZpbmFsbHkoY2xlYW51cCk7XHJcblx0XHRcdH1cclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdC8vIEFrdGlmIGRlxJ9pbGRpIOKAlCBuYXZpZ2F0aW9uIGdlcmVrbWV6LiBBbmNhayBhcmthIHBsYW5kYSBhw6fEsWzEsXAgKGBvcGVuSW5CYWNrZ3JvdW5kVGFiYClcclxuXHRcdFx0Ly8gaGnDpyBnw7Zyw7xudMO8bGVubWVtacWfIHRhYiBoZW7DvHogUm91dGVyIHRhcmFmxLFuZGFuIGBzdG9yZSgpYCBlZGlsbWVtacWfIG9sYWJpbGlyOyBjbGVhbnVwJ8SxXHJcblx0XHRcdC8vIGJpciBtaWNyb3Rhc2sgZ2VjaWt0aXJpcCBvbGFzxLEgc3RvcmUgY3ljbGUnxLFuYSBmxLFyc2F0IHZlcml5b3J1eiAob3JwaGFuIGhhbmRsZSDDtm5sZW1pKS5cclxuXHRcdFx0dm9pZCBQcm9taXNlLnJlc29sdmUoKS50aGVuKGNsZWFudXApO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogQmlyIHRhYiBoYXJpw6cgZGnEn2VyIHTDvG0gdGFiJ2xhcsSxIGthcGF0xLFyLiBBa3RpZiB0YWIga2FwYXTEsWxhbmxhcmRhbiBpc2UgYGtlZXBJdGVtYCdhIGdlw6dlci5cclxuXHQgKiAqKlNhYml0IHRhYidsYXIga29ydW51cioqIChDaHJvbWUgZGF2cmFuxLHFn8SxKSDigJQgcGlubmVkIG9sYW5sYXIgaGVyIHphbWFuIGHDp8SxayBrYWzEsXIuXHJcblx0ICovXHJcblx0Y2xvc2VPdGhlcnMoa2VlcEl0ZW06IE5hdkl0ZW0pOiB2b2lkIHtcclxuXHRcdGNvbnN0IGFsbCA9IHRoaXMuYWN0aXZlUm91dGVzKCk7XHJcblx0XHRjb25zdCB0b0Nsb3NlID0gYWxsLmZpbHRlcigocikgPT4gci50YWJJZCAhPT0ga2VlcEl0ZW0udGFiSWQgJiYgIXIucGlubmVkKTtcclxuXHRcdGlmICh0b0Nsb3NlLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xyXG5cdFx0Y29uc3Qgc3Vydml2b3JJZHMgPSBuZXcgU2V0KFtrZWVwSXRlbS50YWJJZCwgLi4uYWxsLmZpbHRlcigocikgPT4gci5waW5uZWQpLm1hcCgocikgPT4gci50YWJJZCldKTtcclxuXHRcdHRoaXMuYWN0aXZlUm91dGVzLnNldChhbGwuZmlsdGVyKChyKSA9PiBzdXJ2aXZvcklkcy5oYXMoci50YWJJZCkpKTtcclxuXHRcdGNvbnN0IGNsZWFudXBBbGwgPSAoKTogdm9pZCA9PiB0b0Nsb3NlLmZvckVhY2goKHQpID0+IHRoaXMuY2xlYW51cFRhYkhhbmRsZSh0KSk7XHJcblx0XHRpZiAodGhpcy5hY3RpdmVUYWJJZCgpICE9PSBrZWVwSXRlbS50YWJJZCkge1xyXG5cdFx0XHRzYWZlTmF2aWdhdGUoXHJcblx0XHRcdFx0dGhpcy5yb3V0ZXIsXHJcblx0XHRcdFx0d2l0aFRhYklkKGtlZXBJdGVtLm5hdkxpbmssIGtlZXBJdGVtLnRhYklkKSxcclxuXHRcdFx0XHR0YWJTdGF0ZShrZWVwSXRlbS50YWJJZClcclxuXHRcdFx0KS5maW5hbGx5KGNsZWFudXBBbGwpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0Y2xlYW51cEFsbCgpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogVmVyaWxlbiB0YWInxLFuIHNhxJ/EsW5kYWtpIHTDvG0gdGFiJ2xhcsSxIGthcGF0xLFyLiAqKlNhYml0IHRhYidsYXIga29ydW51cioqIOKAlFxyXG5cdCAqIHBpbm5lZCBiaXIgdGFiIHNhxJ9kYSBvbHNhIGJpbGUga2FwYXTEsWxtYXogKENocm9tZSBkYXZyYW7EscWfxLEpLlxyXG5cdCAqL1xyXG5cdGNsb3NlVG9SaWdodChpdGVtOiBOYXZJdGVtKTogdm9pZCB7XHJcblx0XHRjb25zdCBhbGwgPSB0aGlzLmFjdGl2ZVJvdXRlcygpO1xyXG5cdFx0Y29uc3QgaWR4ID0gYWxsLmZpbmRJbmRleCgocikgPT4gci50YWJJZCA9PT0gaXRlbS50YWJJZCk7XHJcblx0XHRpZiAoaWR4ID09PSAtMSkgcmV0dXJuO1xyXG5cdFx0Y29uc3QgdG9DbG9zZSA9IGFsbC5zbGljZShpZHggKyAxKS5maWx0ZXIoKHIpID0+ICFyLnBpbm5lZCk7XHJcblx0XHRpZiAodG9DbG9zZS5sZW5ndGggPT09IDApIHJldHVybjtcclxuXHRcdGNvbnN0IHRvQ2xvc2VJZHMgPSBuZXcgU2V0KHRvQ2xvc2UubWFwKChyKSA9PiByLnRhYklkKSk7XHJcblx0XHR0aGlzLmFjdGl2ZVJvdXRlcy5zZXQoYWxsLmZpbHRlcigocikgPT4gIXRvQ2xvc2VJZHMuaGFzKHIudGFiSWQpKSk7XHJcblx0XHRjb25zdCBhY3RpdmVXYXNDbG9zZWQgPSB0b0Nsb3NlLnNvbWUoKHIpID0+IHIudGFiSWQgPT09IHRoaXMuYWN0aXZlVGFiSWQoKSk7XHJcblx0XHRjb25zdCBjbGVhbnVwQWxsID0gKCk6IHZvaWQgPT4gdG9DbG9zZS5mb3JFYWNoKCh0KSA9PiB0aGlzLmNsZWFudXBUYWJIYW5kbGUodCkpO1xyXG5cdFx0aWYgKGFjdGl2ZVdhc0Nsb3NlZCkge1xyXG5cdFx0XHRzYWZlTmF2aWdhdGUoXHJcblx0XHRcdFx0dGhpcy5yb3V0ZXIsXHJcblx0XHRcdFx0d2l0aFRhYklkKGl0ZW0ubmF2TGluaywgaXRlbS50YWJJZCksXHJcblx0XHRcdFx0dGFiU3RhdGUoaXRlbS50YWJJZClcclxuXHRcdFx0KS5maW5hbGx5KGNsZWFudXBBbGwpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0Y2xlYW51cEFsbCgpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogU2FiaXQgb2xtYXlhbiB0w7xtIHRhYidsYXLEsSBrYXBhdMSxci4gKipTYWJpdCB0YWInbGFyIGtvcnVudXIqKiDigJQga3VsbGFuxLFjxLEgXCJUw7xtw7xuw7wgS2FwYXRcIlxyXG5cdCAqIGlzdGVyc2UgYmlsZSBwaW5uZWQgdGFiJ2xhciBhw6fEsWsga2FsxLFyIChDaHJvbWUgZGF2cmFuxLHFn8SxKS4gSGnDpyBwaW5uZWQgdGFiIHlva3NhIGFuYSBzYXlmYXlhXHJcblx0ICogbmF2aWdhdGUgZWRpbGlyLlxyXG5cdCAqL1xyXG5cdGNsb3NlQWxsKCk6IHZvaWQge1xyXG5cdFx0Y29uc3QgYWxsID0gdGhpcy5hY3RpdmVSb3V0ZXMoKTtcclxuXHRcdGlmIChhbGwubGVuZ3RoID09PSAwKSByZXR1cm47XHJcblx0XHRjb25zdCB0b0Nsb3NlID0gYWxsLmZpbHRlcigocikgPT4gIXIucGlubmVkKTtcclxuXHRcdGlmICh0b0Nsb3NlLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xyXG5cdFx0Y29uc3Qgc3Vydml2b3JzID0gYWxsLmZpbHRlcigocikgPT4gci5waW5uZWQpO1xyXG5cdFx0dGhpcy5hY3RpdmVSb3V0ZXMuc2V0KHN1cnZpdm9ycyk7XHJcblx0XHRjb25zdCBjbGVhbnVwQWxsID0gKCk6IHZvaWQgPT4gdG9DbG9zZS5mb3JFYWNoKCh0KSA9PiB0aGlzLmNsZWFudXBUYWJIYW5kbGUodCkpO1xyXG5cdFx0Ly8gQWt0aWYgdGFiIGthcGF0xLFsYW5sYXJkYW4gaXNlOiB5YSBiaXIgcGlubmVkIHRhYidhIGdlw6cgKHlhxZ/EsXlvcnNhKSwgeW9rc2EgYW5hIHNheWZheWEuXHJcblx0XHRjb25zdCBhY3RpdmVXYXNDbG9zZWQgPSB0b0Nsb3NlLnNvbWUoKHIpID0+IHIudGFiSWQgPT09IHRoaXMuYWN0aXZlVGFiSWQoKSk7XHJcblx0XHRpZiAoYWN0aXZlV2FzQ2xvc2VkKSB7XHJcblx0XHRcdGlmIChzdXJ2aXZvcnMubGVuZ3RoID4gMCkge1xyXG5cdFx0XHRcdGNvbnN0IHRhcmdldCA9IHN1cnZpdm9yc1swXTtcclxuXHRcdFx0XHRzYWZlTmF2aWdhdGUodGhpcy5yb3V0ZXIsIHdpdGhUYWJJZCh0YXJnZXQubmF2TGluaywgdGFyZ2V0LnRhYklkKSwgdGFiU3RhdGUodGFyZ2V0LnRhYklkKSkuZmluYWxseShjbGVhbnVwQWxsKTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHQvLyBQaW5uZWQgc3Vydml2b3IgeW9rIOKAlCBib8WfIGLEsXJha21hLCBhbmFzYXlmYSB0YWInxLEgYcOnIChlbiBheiAxIHRhYiBrdXJhbMSxKS5cclxuXHRcdFx0XHR0aGlzLm9wZW5Ib21lVGFiKCkuZmluYWxseShjbGVhbnVwQWxsKTtcclxuXHRcdFx0fVxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0Y2xlYW51cEFsbCgpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogVGFiIHNhYml0bGVtZSB0b2dnbGUg4oCUIENocm9tZS12YXJpIHBpbm5pbmcuIFBpbm5lZCB0YWInbGFyIHRhYiBiYXInxLFuICoqc29sdW5hKiogdGHFn8SxbsSxclxyXG5cdCAqIChpbnNlcnRpb24gc8SxcmFzxLFuxLEga29ydXlhcmFrKSwga29tcGFrdCBnw7Zyw7xuw7xyLCBgY2xvc2VPdGhlcnNgL2BjbG9zZVRvUmlnaHRgL2BjbG9zZUFsbGBcclxuXHQgKiDDp2HEn3LEsWxhcsSxbmRhbiBrb3J1bnVyLiBBeW7EsSBwaW5uZWQgdMSxa2xhbsSxcnNhIHNhYml0bGVtZXlpIGthbGTEsXLEsXIg4oaSIG5vcm1hbCBhbGFuxLFuICoqc29udW5hKipcclxuXHQgKiB0YcWfxLFuxLFyLlxyXG5cdCAqL1xyXG5cdHRvZ2dsZVBpbihpdGVtOiBOYXZJdGVtKTogdm9pZCB7XHJcblx0XHRjb25zdCBhbGwgPSB0aGlzLmFjdGl2ZVJvdXRlcygpO1xyXG5cdFx0Y29uc3QgaWR4ID0gYWxsLmZpbmRJbmRleCgocikgPT4gci50YWJJZCA9PT0gaXRlbS50YWJJZCk7XHJcblx0XHRpZiAoaWR4ID09PSAtMSkgcmV0dXJuO1xyXG5cdFx0Y29uc3QgdGFyZ2V0ID0gYWxsW2lkeF07XHJcblx0XHRjb25zdCBuZXdQaW5uZWQgPSAhdGFyZ2V0LnBpbm5lZDtcclxuXHRcdC8vIMOWbmNlIHRhcmdldCfEsSBhcnJheSdkZW4gw6fEsWthciwgc29ucmEgcGlubmVkL25vcm1hbCBibG/En3VudW4gKipzb251bmEqKiBla2xlLlxyXG5cdFx0Ly8gQnUgQ2hyb21lIGRhdnJhbsSxxZ/EsTogcGlubGVuaW5jZSBwaW5uZWQgYmxvxJ91biBzb251bmEgKGVuIHNhxJ9hKSwgdW5waW5sZW5pbmNlIG5vcm1hbFxyXG5cdFx0Ly8gYmxvxJ91biBiYcWfxLFuYSAocGlubmVkJ2luIGhlbWVuIHNhxJ/EsW5hKS5cclxuXHRcdGNvbnN0IHdpdGhvdXQgPSBhbGwuZmlsdGVyKChfLCBpKSA9PiBpICE9PSBpZHgpO1xyXG5cdFx0Y29uc3QgcGlubmVkU2VnbWVudCA9IHdpdGhvdXQuZmlsdGVyKChyKSA9PiByLnBpbm5lZCk7XHJcblx0XHRjb25zdCBub3JtYWxTZWdtZW50ID0gd2l0aG91dC5maWx0ZXIoKHIpID0+ICFyLnBpbm5lZCk7XHJcblx0XHRjb25zdCB1cGRhdGVkOiBOYXZJdGVtID0geyAuLi50YXJnZXQsIHBpbm5lZDogbmV3UGlubmVkIH07XHJcblx0XHQvLyBQaW4g4oaSIHBpbm5lZCBibG/En3VuIHNvbnVuYTsgdW5waW4g4oaSIG5vcm1hbCBibG/En3VuIGJhxZ/EsW5hLiBIZXIgaWtpIGR1cnVtZGEgZGEgYHVwZGF0ZWRgLFxyXG5cdFx0Ly8gcGlubmVkIHZlIG5vcm1hbCBzZWdtZW50bGVyaW4gU0lOSVJJTkEgeWVybGXFn2lyIChwb3ppc3lvbiBheW7EsSkg4oaSIHRlayBpZmFkZSB5ZXRlcmxpLlxyXG5cdFx0Y29uc3QgbmV4dCA9IFsuLi5waW5uZWRTZWdtZW50LCB1cGRhdGVkLCAuLi5ub3JtYWxTZWdtZW50XTtcclxuXHRcdHRoaXMuYWN0aXZlUm91dGVzLnNldChuZXh0KTtcclxuXHR9XHJcblxyXG5cdC8qKiBTxLFyYWRha2kgdGFiJ2EgKGNpcmN1bGFyKSBnZcOnZXIuICovXHJcblx0Z29Ub05leHRUYWIoKTogdm9pZCB7XHJcblx0XHRjb25zdCBhbGwgPSB0aGlzLmFjdGl2ZVJvdXRlcygpO1xyXG5cdFx0aWYgKGFsbC5sZW5ndGggPT09IDApIHJldHVybjtcclxuXHRcdGNvbnN0IGlkeCA9IHRoaXMuZmluZEFjdGl2ZVRhYkluZGV4KCk7XHJcblx0XHRjb25zdCBuZXh0SWR4ID0gaWR4ID09PSAtMSA/IDAgOiAoaWR4ICsgMSkgJSBhbGwubGVuZ3RoO1xyXG5cdFx0Y29uc3QgdGFyZ2V0ID0gYWxsW25leHRJZHhdO1xyXG5cdFx0c2FmZU5hdmlnYXRlKHRoaXMucm91dGVyLCB3aXRoVGFiSWQodGFyZ2V0Lm5hdkxpbmssIHRhcmdldC50YWJJZCksIHRhYlN0YXRlKHRhcmdldC50YWJJZCkpO1xyXG5cdH1cclxuXHJcblx0LyoqIMOWbmNla2kgdGFiJ2EgKGNpcmN1bGFyKSBnZcOnZXIuICovXHJcblx0Z29Ub1ByZXZUYWIoKTogdm9pZCB7XHJcblx0XHRjb25zdCBhbGwgPSB0aGlzLmFjdGl2ZVJvdXRlcygpO1xyXG5cdFx0aWYgKGFsbC5sZW5ndGggPT09IDApIHJldHVybjtcclxuXHRcdGNvbnN0IGlkeCA9IHRoaXMuZmluZEFjdGl2ZVRhYkluZGV4KCk7XHJcblx0XHRjb25zdCBwcmV2SWR4ID0gaWR4ID09PSAtMSA/IGFsbC5sZW5ndGggLSAxIDogKGlkeCAtIDEgKyBhbGwubGVuZ3RoKSAlIGFsbC5sZW5ndGg7XHJcblx0XHRjb25zdCB0YXJnZXQgPSBhbGxbcHJldklkeF07XHJcblx0XHRzYWZlTmF2aWdhdGUodGhpcy5yb3V0ZXIsIHdpdGhUYWJJZCh0YXJnZXQubmF2TGluaywgdGFyZ2V0LnRhYklkKSwgdGFiU3RhdGUodGFyZ2V0LnRhYklkKSk7XHJcblx0fVxyXG5cclxuXHQvKiogSW5kZXgndGVraSB0YWInYSBnZcOnZXIgKEFsdCsxLi45IGnDp2luIDAtdGFiYW5sxLEgaW5kZXgpLiAqL1xyXG5cdGdvVG9UYWJCeUluZGV4KGluZGV4OiBudW1iZXIpOiB2b2lkIHtcclxuXHRcdGNvbnN0IGFsbCA9IHRoaXMuYWN0aXZlUm91dGVzKCk7XHJcblx0XHRpZiAoaW5kZXggPCAwIHx8IGluZGV4ID49IGFsbC5sZW5ndGgpIHJldHVybjtcclxuXHRcdGNvbnN0IHRhcmdldCA9IGFsbFtpbmRleF07XHJcblx0XHRzYWZlTmF2aWdhdGUodGhpcy5yb3V0ZXIsIHdpdGhUYWJJZCh0YXJnZXQubmF2TGluaywgdGFyZ2V0LnRhYklkKSwgdGFiU3RhdGUodGFyZ2V0LnRhYklkKSk7XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBWZXJpbGVuIHRhYidhIG5hdmlnYXRlIOKAlCBVSSdkYW4gKHRhYiB0xLFrbGFtYSkgw6dhxJ9yxLFsxLFyLiBSb3V0ZXInYSBgX3RhYmAgaWxlIFVSTCB2ZVxyXG5cdCAqIGBleHRyYXMuc3RhdGVgIGlsZSB0YWJJZCB2ZXJpcjsgVXJsU2VyaWFsaXplciBhZHJlcyDDp3VidcSfdW51IHRlbWl6IHR1dGFyLlxyXG5cdCAqL1xyXG5cdG5hdmlnYXRlVG9UYWIoaXRlbTogTmF2SXRlbSk6IHZvaWQge1xyXG5cdFx0aWYgKCFpdGVtPy5uYXZMaW5rIHx8ICFpdGVtPy50YWJJZCkgcmV0dXJuO1xyXG5cdFx0Y29uc3QgdXJsID0gd2l0aFRhYklkKGl0ZW0ubmF2TGluaywgaXRlbS50YWJJZCk7XHJcblx0XHRjb25zb2xlLmxvZyhgW05hdlNlcnZpY2VdIG5hdmlnYXRlVG9UYWIgdGFiSWQ9XCIke2l0ZW0udGFiSWR9XCIgbmF2TGluaz1cIiR7aXRlbS5uYXZMaW5rfVwiIOKGkiB1cmw9XCIke3VybH1cImApO1xyXG5cdFx0c2FmZU5hdmlnYXRlKHRoaXMucm91dGVyLCB1cmwsIHRhYlN0YXRlKGl0ZW0udGFiSWQpKTtcclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIEJlbGlybGkgYmlyIHRhYifEsW4gYG5hdkxpbmtgJ2luaSB2ZSAodmFyc2EpIGBuYXZOYW1lYCdpbmkgZ8O8bmNlbGxlci4gKipLYXluYcSfYS1iYcSfbMSxKipcclxuXHQgKiAocGVyLXRhYikgc3luYyDigJQgY2FsbGVyIGhhbmdpIHRhYifEsW4gVVJMJ2kgb2xkdcSfdW51IHPDtnlsZXIsIE5hdlNlcnZpY2UgYWt0aWYgdGFiXHJcblx0ICogdmFyc2F5xLFtxLFuZGEgYnVsdW5tYXouIEhpZGRlbiBjb250YWluZXInZGFraSBhc3luYyBNRkUgTmF2aWdhdGlvbkVuZCdsZXJpIGFrdGlmIHRhYifEsW5cclxuXHQgKiBzdGF0ZSdpbmkgS09OVEFNxLBORSBFREVNRVouXHJcblx0ICpcclxuXHQgKiBgdGl0bGVgIHBhcmFtZXRyZXNpIE1GRSBSb3V0ZXIgZGVlcGVzdCByb3V0ZSd1bnVuIGBSb3V0ZS50aXRsZWAgdmV5YSBgZGF0YS50YWJOYW1lYFxyXG5cdCAqIGRlxJ9lcmluZGVuIGdlbGlyIOKGkiBrdWxsYW7EsWPEsSBkZXRhaWwvZWRpdCBnaWJpIHN1Yi1zYXlmYWxhcmEgbmF2aWdhdGUgb2xkdcSfdW5kYSB0YWJcclxuXHQgKiBhZMSxIGRpbmFtaWsgZ8O8bmNlbGxlbmlyLiBgdGl0bGVgIHVuZGVmaW5lZCBpc2UgdGFiLm5hdk5hbWUgZGXEn2nFn21leiAoc3RhdGlrIGthbMSxcikuXHJcblx0ICpcclxuXHQgKiBBcHBMYXlvdXRDb21wb25lbnQgZWZmZWN0J3RlbiBgYnJpZGdlLmFjdGl2ZU1GRVVybGAgKGB7dGFiSWQsIHVybCwgdGl0bGV9YCkgaWxlIMOnYcSfcsSxbMSxci5cclxuXHQgKi9cclxuXHRzeW5jQWN0aXZlVGFiTmF2TGlua0ZvclRhYih0YWJJZDogc3RyaW5nLCByYXdVcmw6IHN0cmluZywgdGl0bGU/OiBzdHJpbmcpOiB2b2lkIHtcclxuXHRcdGlmICghdGFiSWQgfHwgIXJhd1VybCkgcmV0dXJuO1xyXG5cdFx0Y29uc3QgY2xlYW5VcmwgPSBjbGVhbk5hdkxpbmsocmF3VXJsKTtcclxuXHRcdGNvbnN0IHRhcmdldCA9IHRoaXMuYWN0aXZlUm91dGVzKCkuZmluZCgodCkgPT4gdC50YWJJZCA9PT0gdGFiSWQpO1xyXG5cdFx0aWYgKCF0YXJnZXQpIHJldHVybjtcclxuXHRcdGNvbnN0IGxpbmtDaGFuZ2VkID0gdGFyZ2V0Lm5hdkxpbmsgIT09IGNsZWFuVXJsO1xyXG5cdFx0Y29uc3QgbmFtZUNoYW5nZWQgPSAhIXRpdGxlICYmIHRhcmdldC5uYXZOYW1lICE9PSB0aXRsZTtcclxuXHRcdGlmICghbGlua0NoYW5nZWQgJiYgIW5hbWVDaGFuZ2VkKSByZXR1cm47XHJcblx0XHRjb25zdCBuZXdOYXZMaW5rID0gbGlua0NoYW5nZWQgPyBjbGVhblVybCA6IHRhcmdldC5uYXZMaW5rO1xyXG5cdFx0Y29uc3QgbmV3TmFtZSA9IG5hbWVDaGFuZ2VkID8gdGl0bGUhIDogdGFyZ2V0Lm5hdk5hbWU7XHJcblx0XHRjb25zb2xlLmxvZyhcclxuXHRcdFx0YFtOYXZTZXJ2aWNlXSBzeW5jQWN0aXZlVGFiTmF2TGlua0ZvclRhYiB0YWI9XCIke3RhYklkfVwiIGAgK1xyXG5cdFx0XHRgbmF2TGluayBcIiR7dGFyZ2V0Lm5hdkxpbmt9XCIg4oaSIFwiJHtuZXdOYXZMaW5rfVwiIG5hbWUgXCIke3RhcmdldC5uYXZOYW1lfVwiIOKGkiBcIiR7bmV3TmFtZX1cImBcclxuXHRcdCk7XHJcblx0XHR0aGlzLmFjdGl2ZVJvdXRlcy51cGRhdGUoKHJvdXRlcykgPT5cclxuXHRcdFx0cm91dGVzLm1hcCgodCkgPT4gKHQudGFiSWQgPT09IHRhYklkID8geyAuLi50LCBuYXZMaW5rOiBuZXdOYXZMaW5rLCBuYXZOYW1lOiBuZXdOYW1lIH0gOiB0KSlcclxuXHRcdCk7XHJcblx0fVxyXG5cclxuXHQvKipcclxuXHQgKiBUYWIgY29udGV4dC1tZW51IFwiWWluZWxlXCIgKENocm9tZSBcIkR1cGxpY2F0ZVwiKTogbWV2Y3V0IHRhYifEsW4gcGF0aCdpIGtvcHlhbGFuxLFyLCB5ZW5pIGJpclxyXG5cdCAqIGB0YWJJZGAgw7xyZXRpbGlyIHZlIHllbmkgdGFiIG9sdcWfdHVydWx1ci4gU3RyYXRlZ3kgYF90YWJgIHF1ZXJ5IHBhcmFtJ8SxbsSxIHJvdXRlIGtleSdpblxyXG5cdCAqIHBhcsOnYXPEsSB5YXB0xLHEn8SxIGnDp2luIHllbmkgdGFiIGJhxJ/EsW1zxLF6IGBzdG9yZWRSb3V0ZXNgIHNsb3QndW5hIHlhesSxbMSxciDihpIgYmHEn8SxbXPEsXpcclxuXHQgKiBjb21wb25lbnRSZWYgKyBzdGF0ZS4gTWV2Y3V0IHRhYiBrb3J1bnVyLCBrdWxsYW7EsWPEsSBoZXIgaWtpc2luZGUgZGUgYmHEn8SxbXPEsXogw6dhbMSxxZ9hYmlsaXIuXHJcblx0ICovXHJcblx0ZHVwbGljYXRlVGFiKGl0ZW06IE5hdkl0ZW0pOiB2b2lkIHtcclxuXHRcdGlmICghaXRlbT8ubmF2TGluaykgcmV0dXJuO1xyXG5cdFx0aWYgKHRoaXMuYWN0aXZlUm91dGVzKCkubGVuZ3RoID49IHRoaXMuTUFYX1RBQlMpIHtcclxuXHRcdFx0dGhpcy5ub3RpZnlNYXhUYWJzUmVhY2hlZCgpO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblx0XHRjb25zdCBuZXdUYWJJZCA9IHRoaXMuZ2VuZXJhdGVUYWJJZCgpO1xyXG5cdFx0Y29uc3QgbmV3TmF2TGluayA9IGNsZWFuTmF2TGluayhpdGVtLm5hdkxpbmspO1xyXG5cdFx0dGhpcy5hY3RpdmVSb3V0ZXMudXBkYXRlKCh2YWwpID0+IFtcclxuXHRcdFx0Li4udmFsLFxyXG5cdFx0XHR7IC4uLml0ZW0sIHRhYklkOiBuZXdUYWJJZCwgbmF2TGluazogbmV3TmF2TGluayB9XHJcblx0XHRdKTtcclxuXHRcdHNhZmVOYXZpZ2F0ZSh0aGlzLnJvdXRlciwgd2l0aFRhYklkKG5ld05hdkxpbmssIG5ld1RhYklkKSwgdGFiU3RhdGUobmV3VGFiSWQpKTtcclxuXHR9XHJcblxyXG5cdHJlb3JkZXJBY3RpdmVSb3V0ZXMobmV3Um91dGVzOiBOYXZJdGVtW10pOiB2b2lkIHtcclxuXHRcdC8vIFBpbm5lZC9ub3JtYWwgc8SxbsSxcsSxIGtvcnVtYXPEsSDigJQgc2FiaXQgdGFiJ2xhciB0YWIgYmFyJ8SxbiBzb2x1bmRhIChpbnNlcnRpb24gc8SxcmFzxLF5bGEpLFxyXG5cdFx0Ly8gbm9ybWFsIHRhYidsYXIgc2HEn8SxbmRhIGthbMSxci4gQ0RLIGRyYWcgZHJvcCBiaXIgdGFiJ8SxIHlhbmzEscWfIHNlZ21lbnRlIGTDvMWfw7xyw7xyc2VcclxuXHRcdC8vIGJ1cmFkYSBvdG9tYXRpayBkw7x6ZWx0aWxpci4gU2VnbWVudCBpw6dpIHPEsXJhIGtvcnVudXIuXHJcblx0XHRjb25zdCBwaW5uZWQgPSBuZXdSb3V0ZXMuZmlsdGVyKChyKSA9PiByLnBpbm5lZCk7XHJcblx0XHRjb25zdCBub3JtYWwgPSBuZXdSb3V0ZXMuZmlsdGVyKChyKSA9PiAhci5waW5uZWQpO1xyXG5cdFx0dGhpcy5hY3RpdmVSb3V0ZXMuc2V0KFsuLi5waW5uZWQsIC4uLm5vcm1hbF0pO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogTWVudSBjb25maWcnaW5kZW4gbmF2TGluaydlIGthcsWfxLFsxLFrIGdlbGVuIGl0ZW0naSBsb25nZXN0LXByZWZpeCBtYXRjaCBpbGUgYnVsdXBcclxuXHQgKiBha3RpZiBkaWxkZSBsYWJlbCArIGljb24gZMO2bmVyLiBBZHJlcyDDp3VidcSfdW5hIFVSTCB5YXDEscWfdMSxcmFyYWsgZGVlcCBsaW5rIGHDp8SxbGTEscSfxLFuZGFcclxuXHQgKiB0YWInxLFuIG1lbsO8ZGVuIGHDp8SxbGFuIHRhYidsYSBheW7EsSBnw7Zyw7xuw7xtZSBzYWhpcCBvbG1hc8SxbsSxIHNhxJ9sYXIuXHJcblx0ICpcclxuXHQgKiBMb29rdXAgYG1lbnVJdGVtc1Byb3ZpZGVyYCDDvHplcmluZGVuIOKAlCBob3N0IHNoZWxsIGBzZXRNZW51SXRlbXNQcm92aWRlcmAgaWxlXHJcblx0ICogYGJyaWRnZS5ob3N0TWVudUl0ZW1zKClgJ2EgYmHEn2xhci4gUHJvdmlkZXIgeW9rc2EgdmV5YSBlxZ9sZcWfbWUgeW9rc2EgYG51bGxgIGTDtm5lciB2ZVxyXG5cdCAqIGNhbGxlciBVUkwgc2VnbWVudCBmYWxsYmFjaydpbmUgZMO8xZ9lci5cclxuXHQgKi9cclxuXHRwcml2YXRlIHJlc29sdmVOYXZJbmZvRnJvbU1lbnUobmF2TGluazogc3RyaW5nKTogeyBuYXZOYW1lOiBzdHJpbmc7IGljb24/OiBzdHJpbmcgfSB8IG51bGwge1xyXG5cdFx0Ly8gQW5hc2F5ZmEgKG1lbsO8ZGVraSBgcm9vdGAgw7bEn2VzaSkgw6dvxJ91IHphbWFuIGByb3V0ZXJMaW5rYCBpbGUgZcWfbGXFn21leiAocm91dGVyTGluaydpIGAvYFxyXG5cdFx0Ly8gdmV5YSBib8WfIG9sYWJpbGlyLCBsb25nZXN0LXByZWZpeCBtYXRjaCdlIHRha8SxbG1heikg4oaSIGAvYCBpw6dpbiBmYWxsYmFjayBcIlBhZ2VcIiArIGplbmVyaWtcclxuXHRcdC8vIGlrb24gZMO2bmVyZGkuIEhvbWUgcHJvdmlkZXIgdmFyc2EgYWQvaWtvbnUgZG/En3J1ZGFuIG9yYWRhbiAocm9vdCBpdGVtJ2Rhbikgw6fDtnouXHJcblx0XHRpZiAodGhpcy5ob21lTmF2SXRlbVByb3ZpZGVyKSB7XHJcblx0XHRcdGNvbnN0IGhvbWUgPSB0aGlzLmhvbWVOYXZJdGVtUHJvdmlkZXIoKTtcclxuXHRcdFx0Ly8gQW5hc2F5ZmEgbmF2TGluayBlxZ9sZcWfaXlvcnNhIGTDtm4g4oCUIG1lbsO8IGJvxZ8vZ2XDpyB5w7xrbGVuZGl5c2UgeWEgZGEgbWVuw7xkZSBgcm9vdGAgw7bEn2VzaVxyXG5cdFx0XHQvLyB5b2tzYSBuYXZOYW1lL2ljb24gYm/FnyBnZWxlYmlsaXI7IGkxOG4gKCd0YWIuaG9tZScpICsgZXYgaWtvbnUgZmFsbGJhY2sgaWxlIGFzbGFcclxuXHRcdFx0Ly8gbWVuw7wtc2VnbWVudCBmYWxsYmFjaydpbmUgKFwiUGFnZVwiKSBkw7zFn21lLlxyXG5cdFx0XHRpZiAobmF2TGluayA9PT0gY2xlYW5OYXZMaW5rKGhvbWUubmF2TGluayB8fCAnLycpKSB7XHJcblx0XHRcdFx0cmV0dXJuIHtcclxuXHRcdFx0XHRcdG5hdk5hbWU6IGhvbWUubmF2TmFtZSB8fCB0aGlzLnRyYW5zbG9jb1NlcnZpY2UudHJhbnNsYXRlKCd0YWIuaG9tZScpLFxyXG5cdFx0XHRcdFx0aWNvbjogaG9tZS5pY29uIHx8ICdwaSBwaS1ob21lJ1xyXG5cdFx0XHRcdH07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdGNvbnN0IGl0ZW1zID0gdGhpcy5tZW51SXRlbXNQcm92aWRlcj8uKCk7XHJcblx0XHRpZiAoIWl0ZW1zPy5sZW5ndGgpIHJldHVybiBudWxsO1xyXG5cdFx0Y29uc3QgcGF0aCA9IG5hdkxpbmsuc3BsaXQoJz8nKVswXTtcclxuXHRcdGNvbnN0IG1hdGNoZWQgPSB0aGlzLmZpbmRNZW51SXRlbUZvclJvdXRlKGl0ZW1zLCBwYXRoKTtcclxuXHRcdGlmICghbWF0Y2hlZCkgcmV0dXJuIG51bGw7XHJcblx0XHRjb25zdCBsYW5nID0gdGhpcy50cmFuc2xvY29TZXJ2aWNlLmdldEFjdGl2ZUxhbmcoKTtcclxuXHRcdGNvbnN0IGxhYmVsID0gbWF0Y2hlZC5sYWJlbCBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHwgdW5kZWZpbmVkO1xyXG5cdFx0Y29uc3QgbmF2TmFtZSA9IGxhYmVsPy5bbGFuZ10gfHwgbGFiZWw/LlsndHInXSB8fCBsYWJlbD8uWydlbiddIHx8ICcnO1xyXG5cdFx0aWYgKCFuYXZOYW1lKSByZXR1cm4gbnVsbDtcclxuXHRcdHJldHVybiB7IG5hdk5hbWUsIGljb246IG1hdGNoZWQuaWNvbiB9O1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogTWVuw7wvaTE4biB5w7xrbGVuaW5jZSB0YWInbGFyxLEgKMO2emVsbGlrbGUgYW5hc2F5ZmEpIG1lbsO8eWxlIHJldml6ZSBlZGVyIOKAlCByZWZyZXNoL2lsayBhw6fEsWzEscWfXHJcblx0ICogcGF0aCdpbmRlIHRhYiBmYWxsYmFjayBpc2ltL2ljb25sYSBla2xlbm1pxZ8gb2xhYmlsaXIgKG1lbsO8IHZleWEgw6dldmlyaSBvIGFuZGEgaGF6xLFyIGRlxJ9pbGRpKS5cclxuXHQgKiBUZXRpa2xleWljaWxlcjogYHNldE1lbnVJdGVtc1Byb3ZpZGVyYC9gc2V0SG9tZU5hdkl0ZW1Qcm92aWRlcmAsIGB0cmFuc2xhdGlvbkxvYWRTdWNjZXNzYFxyXG5cdCAqIGV2ZW50J2kgdmUgaG9zdCBzaGVsbCBtZW7DvCBzaWduYWwnaSBkZcSfacWfaW1pIChgQXBwTGF5b3V0Q29tcG9uZW50YCBlZmZlY3QpLiBQdWJsaWMg4oCUIGhvc3RcclxuXHQgKiBsYXlvdXQgbWVuw7wgYXN5bmMgecO8a2xlbmluY2Ugw6dhxJ/EsXLEsXIga2kgYW5hc2F5ZmEgYWTEsSBgdGFiLmhvbWVgIGZhbGxiYWNrJ2luZGUgdGFrxLFsbWFzxLFuLlxyXG5cdCAqL1xyXG5cdHJlY29uY2lsZVRhYnNXaXRoTWVudSgpOiB2b2lkIHtcclxuXHRcdGNvbnN0IHRhYnMgPSB0aGlzLmFjdGl2ZVJvdXRlcygpO1xyXG5cdFx0aWYgKCF0YWJzLmxlbmd0aCkgcmV0dXJuO1xyXG5cdFx0bGV0IGNoYW5nZWQgPSBmYWxzZTtcclxuXHRcdGNvbnN0IG5leHQgPSB0YWJzLm1hcCgodCkgPT4ge1xyXG5cdFx0XHRjb25zdCBpbmZvID0gdGhpcy5yZXNvbHZlTmF2SW5mb0Zyb21NZW51KHQubmF2TGluayk7XHJcblx0XHRcdGlmICghaW5mbykgcmV0dXJuIHQ7XHJcblx0XHRcdGlmICh0Lm5hdk5hbWUgPT09IGluZm8ubmF2TmFtZSAmJiB0Lmljb24gPT09IGluZm8uaWNvbikgcmV0dXJuIHQ7XHJcblx0XHRcdGNoYW5nZWQgPSB0cnVlO1xyXG5cdFx0XHRyZXR1cm4geyAuLi50LCBuYXZOYW1lOiBpbmZvLm5hdk5hbWUsIGljb246IGluZm8uaWNvbiB9O1xyXG5cdFx0fSk7XHJcblx0XHRpZiAoY2hhbmdlZCkgdGhpcy5hY3RpdmVSb3V0ZXMuc2V0KG5leHQpO1xyXG5cdH1cclxuXHJcblx0LyoqXHJcblx0ICogTWVudSBhxJ9hY8SxbmRhIHZlcmlsZW4gcGF0aCBpw6dpbiBlbiBzcGVzaWZpayAobG9uZ2VzdCByb3V0ZXJMaW5rIHByZWZpeCkgZcWfbGXFn21leWkgYnVsdXIuXHJcblx0ICogYG13L2FwcC1tYW5hZ2VtZW50L3ZpZXctYXBwYCBpw6dpbiBoZW0gYG13YCBoZW0gYG13L2FwcC1tYW5hZ2VtZW50L3ZpZXctYXBwYCBlxZ9sZcWfc2UsXHJcblx0ICogZGFoYSB1enVuIG9sYW4ga2F6YW7EsXIg4oaSIGxlYWYgbWVudSBpdGVtJ8SxbiBsYWJlbC9pY29uJ3Uga3VsbGFuxLFsxLFyLlxyXG5cdCAqXHJcblx0ICogUGF0aCBsZWFkaW5nIHNsYXNoIGnDp2VyZWJpbGlyIChSb3V0ZXIgVVJMIGZvcm1hdMSxKSwgbWVudSBjb25maWcgcm91dGVyTGluaydsZXJpXHJcblx0ICogZ2VuZWxkZSBsZWFkaW5nIHNsYXNoJ3PEsXogKGBtdy9tZXRlcnNgKTsgaWtpc2luaSBub3JtYWxpemUgZWRpcCBrYXLFn8SxbGHFn3TEsXLEsXLEsXouXHJcblx0ICovXHJcblx0cHJpdmF0ZSBmaW5kTWVudUl0ZW1Gb3JSb3V0ZShpdGVtczogUGx1Z2luTWVudUl0ZW1bXSwgcGF0aDogc3RyaW5nKTogUGx1Z2luTWVudUl0ZW0gfCB1bmRlZmluZWQge1xyXG5cdFx0Y29uc3Qgbm9ybWFsaXplID0gKHA6IHN0cmluZyk6IHN0cmluZyA9PiAocC5zdGFydHNXaXRoKCcvJykgPyBwLnNsaWNlKDEpIDogcCk7XHJcblx0XHRjb25zdCBub3JtYWxpemVkUGF0aCA9IG5vcm1hbGl6ZShwYXRoKTtcclxuXHRcdGxldCBiZXN0OiB7IGxlbmd0aDogbnVtYmVyOyBpdGVtOiBQbHVnaW5NZW51SXRlbSB9IHwgdW5kZWZpbmVkO1xyXG5cdFx0Y29uc3QgdmlzaXQgPSAobGlzdDogUGx1Z2luTWVudUl0ZW1bXSk6IHZvaWQgPT4ge1xyXG5cdFx0XHRmb3IgKGNvbnN0IGl0IG9mIGxpc3QpIHtcclxuXHRcdFx0XHRpZiAoaXQucm91dGVyTGluaykge1xyXG5cdFx0XHRcdFx0Y29uc3QgciA9IG5vcm1hbGl6ZShpdC5yb3V0ZXJMaW5rKTtcclxuXHRcdFx0XHRcdGlmIChyICYmIChub3JtYWxpemVkUGF0aCA9PT0gciB8fCBub3JtYWxpemVkUGF0aC5zdGFydHNXaXRoKHIgKyAnLycpKSkge1xyXG5cdFx0XHRcdFx0XHRjb25zdCBsZW5ndGggPSByLmxlbmd0aDtcclxuXHRcdFx0XHRcdFx0aWYgKCFiZXN0IHx8IGxlbmd0aCA+IGJlc3QubGVuZ3RoKSBiZXN0ID0geyBsZW5ndGgsIGl0ZW06IGl0IH07XHJcblx0XHRcdFx0XHR9XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdGlmIChpdC5pdGVtcz8ubGVuZ3RoKSB2aXNpdChpdC5pdGVtcyk7XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblx0XHR2aXNpdChpdGVtcyk7XHJcblx0XHRyZXR1cm4gYmVzdD8uaXRlbTtcclxuXHR9XHJcblxyXG5cdC8qKlxyXG5cdCAqIEJpciB0YWInxLFuIHN0b3JlZCByb3V0ZSBoYW5kbGUnxLFuxLEgc3RyYXRlZ3knZGVuIHRlbWl6bGVyLiBLZXkgZm9ybWF0xLFcclxuXHQgKiBgbWZlOiR7cmVtb3RlTmFtZX06JHt0YWJJZH1gIOKAlCBzdHJhdGVneSBgcHJvY2Vzc0NsZWFudXBLZXlgIG1hdGNoZXIga2V5J2luZGVuIGhlbVxyXG5cdCAqIE1GRSBjdXN0b20gZWxlbWVudCBoYW5kbGUnxLFuxLEgaGVtIG8gdGFiJ2EgYWl0IGNoaWxkIHJvdXRlIGhhbmRsZSdsYXLEsW7EsSBkw7zFn8O8csO8ci5cclxuXHQgKi9cclxuXHRwcml2YXRlIGNsZWFudXBUYWJIYW5kbGUoaXRlbTogTmF2SXRlbSk6IHZvaWQge1xyXG5cdFx0Y29uc3QgcmVtb3RlTmFtZSA9IGV4dHJhY3RSZW1vdGVOYW1lKGl0ZW0ubmF2TGluayk7XHJcblx0XHRpZiAoIXJlbW90ZU5hbWUgfHwgIWl0ZW0udGFiSWQpIHJldHVybjtcclxuXHRcdGNvbnNvbGUubG9nKGBbTmF2U2VydmljZV0gY2xlYW51cFRhYkhhbmRsZSByZW1vdGVOYW1lPVwiJHtyZW1vdGVOYW1lfVwiIHRhYklkPVwiJHtpdGVtLnRhYklkfVwiYCk7XHJcblx0XHR0aGlzLnJvdXRlQ2xvc2VTZXJ2aWNlLmRlbGV0ZVN0b3JlZFJvdXRlKGBtZmU6JHtyZW1vdGVOYW1lfToke2l0ZW0udGFiSWR9YCk7XHJcblx0fVxyXG59XHJcbiJdfQ==
|