@fireflysemantics/slice
Version:

668 lines (665 loc) • 62.3 kB
JavaScript
import { AbstractStore } from './AbstractStore';
import { GUID } from './utilities';
import { ReplaySubject, of } from 'rxjs';
import { takeWhile, filter, switchMap } from 'rxjs/operators';
import { Slice } from './Slice';
/**
* This `todoFactory` code will be used to illustrate the API examples. The following
* utilities are used in the tests and the API Typedoc examples contained here.
* @example Utilities for API Examples
* ```
* export const enum TodoSliceEnum {
* COMPLETE = "Complete",
* INCOMPLETE = "Incomplete"
* }
* export class Todo {
* constructor(
* public complete: boolean,
* public title: string,
* public gid?:string,
* public id?:string) {}
* }
*
* export let todos = [new Todo(false, "You complete me!"), new Todo(true, "You completed me!")];
*
* export function todosFactory():Todo[] {
* return [new Todo(false, "You complete me!"), new Todo(true, "You completed me!")];
* }
* ```
*/
export class EStore extends AbstractStore {
/**
* Store constructor (Initialization with element is optional)
*
* perform initial notification to all observers,
* such that functions like {@link combineLatest}{}
* will execute at least once.
*
* @param entities The entities to initialize the store with.
* @param config The optional configuration instance.
*
* @example EStore<Todo> Creation
* ```
* // Initialize the Store
* let store: EStore<Todo> = new EStore<Todo>(todosFactory());
* ```
*/
constructor(entities = [], config) {
super(config);
/**
* Notifies observers when the store is empty.
*/
this.notifyActive = new ReplaySubject(1);
/**
* `Map` of active entties. The instance is public and can be used
* directly to add and remove active entities, however we recommend
* using the {@link addActive} and {@link deleteActive} methods.
*/
this.active = new Map();
/**
* Notifies observers when the store is loading.
*
* This is a common pattern found when implementing
* `Observable` data sources.
*/
this.notifyLoading = new ReplaySubject(1);
/**
* The current loading state. Use loading when fetching new
* data for the store. The default loading state is `true`.
*
* This is such that if data is fetched asynchronously
* in a service, components can wait on loading notification
* before attempting to retrieve data from the service.
*
* Loading could be based on a composite response. For example
* when the stock and mutual funds have loaded, set loading to `false`.
*/
this._loading = true;
/**
* Notifies observers that a search is in progress.
*
* This is a common pattern found when implementing
* `Observable` data sources.
*/
this.notifySearching = new ReplaySubject(1);
/**
* The current `searching` state. Use `searching`
* for example to display a spinnner
* when performing a search.
* The default `searching` state is `false`.
*/
this._searching = false;
/**
* Store slices
*/
this.slices = new Map();
const delta = { type: "Initialize" /* ActionTypes.INTIALIZE */, entries: entities };
this.post(entities);
this.notifyDelta.next(delta);
}
/**
* Calls complete on all EStore {@link ReplaySubject} instances.
*
* Call destroy when disposing of the store.
*/
destroy() {
super.destroy();
this.notifyLoading.complete();
this.notifyActive.complete();
this.slices.forEach((slice) => slice.destroy());
}
/**
* Toggles the entity:
*
* If the store contains the entity
* it will be deleted. If the store
* does not contains the entity,
* it is added.
* @param e The entity to toggle
* @example Toggle the Todo instance
* ```
* estore.post(todo);
* // Remove todo
* estore.toggle(todo);
* // Add it back
* estore.toggle(todo);
* ```
*/
toggle(e) {
if (this.contains(e)) {
this.delete(e);
}
else {
this.post(e);
}
}
/**
* Add multiple entity entities to active.
*
* If the entity is not contained in the store it is added
* to the store before it is added to `active`.
*
* Also we clone the map prior to broadcasting it with
* `notifyActive` to make sure we will trigger Angular
* change detection in the event that it maintains
* a reference to the `active` state `Map` instance.
*
* @example Add todo1 and todo2 as active
* ```
* addActive(todo1);
* addActive(todo2);
* ```
*/
addActive(e) {
if (this.contains(e)) {
this.active.set(e.gid, e);
this.notifyActive.next(new Map(this.active));
}
else {
this.post(e);
this.active.set(e.gid, e);
this.notifyActive.next(new Map(this.active));
}
}
/**
* Delete an active entity.
*
* Also we clone the map prior to broadcasting it with
* `notifyActive` to make sure we will trigger Angular
* change detection in the event that it maintains
* a reference to the `active` state `Map` instance.
*
* @example Remove todo1 and todo2 as active entities
* ```
* deleteActive(todo1);
* deleteActive(todo2);
* ```
*/
deleteActive(e) {
this.active.delete(e.gid);
this.notifyActive.next(new Map(this.active));
}
/**
* Clear / reset the active entity map.
*
* Also we clone the map prior to broadcasting it with
* `notifyActive` to make sure we will trigger Angular
* change detection in the event that it maintains
* a reference to the `active` state `Map` instance.
*
* @example Clear active todo instances
* ```
* store.clearActive();
* ```
*/
clearActive() {
this.active.clear();
this.notifyActive.next(new Map(this.active));
}
/**
* Observe the active entities.
*
* @example
* ```
* let active$ = store.observeActive();
* ```
*/
observeActive() {
return this.notifyActive.asObservable();
}
/**
* Observe the active entity.
* @example
<pre>
let active$ = source.activeSnapshot();
</pre>
*/
activeSnapshot() {
return Array.from(this.active.values());
}
/**
* Sets the current loading state and notifies observers.
*/
set loading(loading) {
this._loading = loading;
this.notifyLoading.next(this._loading);
}
/**
* @return A snapshot of the loading state.
* @example Create a reference to the loading state
* ```
* const loading:boolean = todoStore.loading;
* ```
*/
get loading() {
return this._loading;
}
/**
* Observe loading.
*
* Note that this obverable piped through
* `takeWhile(v->v, true), such that it will
* complete after each emission.
*
* See:
* https://fireflysemantics.medium.com/waiting-on-estore-to-load-8dcbe161613c
*
* For more details.
* Also note that v=>v is the same as v=>v!=false
*
* @example
* ```
* const observeLoadingHandler: Observer<boolean> = {
* complete: () => {
* console.log(`Data Loaded and Observable Marked as Complete`);
* }, // completeHandler
* error: () => {
* console.log(`Any Errors?`);
* }, // errorHandler
* next: (l) => {
* console.log(`Data loaded and loading is ${l}`);
* },
* };
*
* const observeLoadingResubscribeHandler: Observer<boolean> = {
* complete: () => {
* console.log(`Data Loaded and Resubscribe Observable Marked as Complete`);
* }, // completeHandler
* error: () => {
* console.log(`Any Resubscribe Errors?`);
* }, // errorHandler
* next: (l) => {
* console.log(`Data loaded and resusbscribe loading value is ${l}`);
* },
* };
*
* const todoStore: EStore<Todo> = new EStore();
* //============================================
* // Loading is true by default
* //============================================
* console.log(`The initial value of loading is ${todoStore.loading}`);
* //============================================
* // Observe Loading
* //============================================
* let loading$: Observable<boolean> = todoStore.observeLoading();
* loading$.subscribe((l) => console.log(`The value of loading is ${l}`));
*
* todoStore.loading = false;
* loading$.subscribe(observeLoadingHandler);
* //============================================
* // The subscription no longer fires
* //============================================
* todoStore.loading = true;
* todoStore.loading = false;
*
* //============================================
* // The subscription no longer fires,
* // so if we want to observe loading again
* // resusbscribe.
* //============================================
* todoStore.loading = true;
* loading$ = todoStore.observeLoading();
* loading$.subscribe(observeLoadingResubscribeHandler);
* todoStore.loading = false;
* ```
*/
observeLoading() {
return this.notifyLoading.asObservable().pipe(takeWhile((v) => v, true));
}
/**
* Notfiies when loading has completed.
*/
observeLoadingComplete() {
return this.observeLoading().pipe(filter((loading) => loading == false), switchMap(() => of(true)));
}
/**
* Sets the current searching state and notifies observers.
*/
set searching(searching) {
this._searching = searching;
this.notifySearching.next(this._searching);
}
/**
* @return A snapshot of the searching state.
*/
get searching() {
return this._searching;
}
/**
* Observe searching.
* @example
<pre>
let searching$ = source.observeSearching();
</pre>
Note that this obverable piped through
`takeWhile(v->v, true), such that it will
complete after each emission.
See:
https://medium.com/@ole.ersoy/waiting-on-estore-to-load-8dcbe161613c
For more details.
*/
observeSearching() {
return this.notifySearching.asObservable().pipe(takeWhile((v) => v, true));
}
/**
* Notfiies when searching has completed.
*/
observeSearchingComplete() {
return this.observeSearching().pipe(filter((searching) => searching == false), switchMap(() => of(true)));
}
/**
* Adds a slice to the store and keys it by the slices label.
*
* @param p
* @param label
*
* @example Setup a Todo Slice for COMPLETE Todos
```
source.addSlice(todo => todo.complete, TodoSlices.COMPLETE);
```
*/
addSlice(p, label) {
const slice = new Slice(label, p, this);
this.slices.set(slice.label, slice);
}
/**
* Remove a slice
* @param label The label identifying the slice
*
* @example Remove the TodoSlices.COMPLETE Slice
```
source.removeSlice(TodoSlices.COMPLETE);
```
*/
removeSlice(label) {
this.slices.delete(label);
}
/**
* Get a slice
* @param label The label identifying the slice
* @return The Slice instance or undefined
*
* @example Get the TodoSlices.COMPLETE slice
```
source.getSlice(TodoSlices.COMPLETE);
```
*/
getSlice(label) {
return this.slices.get(label);
}
/**
* Post (Add a new) element(s) to the store.
* @param e An indiidual entity or an array of entities
* @example Post a Todo instance.
*
*```
* store.post(todo);
*```
*/
post(e) {
if (!Array.isArray(e)) {
const guid = e[this.GUID_KEY]
? e[this.GUID_KEY]
: GUID();
e[this.GUID_KEY] = guid;
this.entries.set(guid, e);
this.updateIDEntry(e);
Array.from(this.slices.values()).forEach((s) => {
s.post(e);
});
//Create a new array reference to trigger Angular change detection.
let v = [...Array.from(this.entries.values())];
const delta = { type: "Post" /* ActionTypes.POST */, entries: [e] };
this.notifyAll(v, delta);
}
else {
this.postA(e);
}
}
/**
* Post N entities to the store.
* @param ...e
* @example Post two Todo instances.
* ```
* store.post(todo1, todo2);
* ```
*/
postN(...e) {
e.forEach((e) => {
const guid = e[this.GUID_KEY]
? e[this.GUID_KEY]
: GUID();
e[this.GUID_KEY] = guid;
this.entries.set(guid, e);
this.updateIDEntry(e);
});
Array.from(this.slices.values()).forEach((s) => {
s.postA(e);
});
//Create a new array reference to trigger Angular change detection.
let v = [...Array.from(this.entries.values())];
const delta = { type: "Post" /* ActionTypes.POST */, entries: e };
this.notifyAll(v, delta);
}
/**
* Post (Add) an array of elements to the store.
* @param e
* @example Post a Todo array.
*
* ```
* store.post([todo1, todo2]);
* ```
*/
postA(e) {
this.postN(...e);
}
/**
* Put (Update) an entity.
* @param e
* @example Put a Todo instance.
* ```
* store.put(todo1);
* ```
*/
put(e) {
if (!Array.isArray(e)) {
let id = e[this.GUID_KEY];
this.entries.set(id, e);
this.updateIDEntry(e);
let v = [...Array.from(this.entries.values())];
this.notify.next(v);
const delta = { type: "Put" /* ActionTypes.PUT */, entries: [e] };
this.notifyDelta.next(delta);
Array.from(this.slices.values()).forEach((s) => {
s.put(e);
});
}
else {
this.putA(e);
}
}
/**
* Put (Update) an element or add an element that was read from a persistence source
* and thus already has an assigned global id`.
* @param e The enetity instances to update.
* @example Put N Todo instances.
*
* ```
* store.put(todo1, todo2);
* ```
*/
putN(...e) {
this.putA(e);
}
/**
* Put (Update) the array of enntities.
* @param e The array of enntities to update
* @example Put an array of Todo instances.
* ```
* store.put([todo1, todo2]);
* ```
*/
putA(e) {
e.forEach((e) => {
let guid = e[this.GUID_KEY];
this.entries.set(guid, e);
this.updateIDEntry(e);
});
//Create a new array reference to trigger Angular change detection.
let v = [...Array.from(this.entries.values())];
this.notify.next(v);
const delta = { type: "Put" /* ActionTypes.PUT */, entries: e };
this.notifyDelta.next(delta);
Array.from(this.slices.values()).forEach((s) => {
s.putA(e);
});
}
/**
* Delete (Update) the array of elements.
* @param e
* @example Delete todo1.
* ```
* store.delete(todo1]);
* ```
*/
delete(e) {
if (!Array.isArray(e)) {
this.deleteActive(e);
const guid = e[this.GUID_KEY];
this.entries.delete(guid);
this.deleteIDEntry(e);
Array.from(this.slices.values()).forEach((s) => {
s.entries.delete(guid);
});
//Create a new array reference to trigger Angular change detection.
let v = [...Array.from(this.entries.values())];
const delta = { type: "Delete" /* ActionTypes.DELETE */, entries: [e] };
this.notifyAll(v, delta);
Array.from(this.slices.values()).forEach((s) => {
s.delete(e);
});
}
else {
this.deleteA(e);
}
}
/**
* Delete N elements.
* @param ...e
* @example Delete N Todo instance argument.
* ```
* store.deleteN(todo1, todo2);
* ```
*/
deleteN(...e) {
this.deleteA(e);
}
/**
* Delete an array of elements.
* @param e The array of instances to be deleted
* @example Delete the array of Todo instances.
* ```
* store.deleteA([todo1, todo2]);
* ```
*/
deleteA(e) {
e.forEach((e) => {
this.deleteActive(e);
const guid = e[this.GUID_KEY];
this.entries.delete(guid);
this.deleteIDEntry(e);
Array.from(this.slices.values()).forEach((s) => {
s.entries.delete(guid);
});
});
//Create a new array reference to trigger Angular change detection.
let v = [...Array.from(this.entries.values())];
const delta = { type: "Delete" /* ActionTypes.DELETE */, entries: e };
this.notifyAll(v, delta);
Array.from(this.slices.values()).forEach((s) => {
s.deleteA(e);
});
}
/**
* Delete elements by {@link Predicate}.
* @param p The predicate.
* @example Delete the Todo instances.
* ```
* store.delete(todo1, todo2);
* ```
*/
deleteP(p) {
const d = [];
Array.from(this.entries.values()).forEach((e) => {
if (p(e)) {
d.push(e);
const id = e[this.GUID_KEY];
this.entries.delete(id);
this.deleteActive(e);
this.deleteIDEntry(e);
}
});
//Create a new array reference to trigger Angular change detection.
let v = [...Array.from(this.entries.values())];
const delta = { type: "Delete" /* ActionTypes.DELETE */, entries: d };
this.notifyAll(v, delta);
Array.from(this.slices.values()).forEach((s) => {
s.deleteA(d);
});
}
/**
* If the entity has the `id` key initialized with a value,
* then also add the entity to the `idEntries`.
*
* @param e The element to be added to the `idEntries`.
*/
updateIDEntry(e) {
if (e[this.ID_KEY]) {
this.idEntries.set(e[this.ID_KEY], e);
}
}
/**
* If the entity has the `id` key initialized with a value,
* then also delete the entity to the `idEntries`.
*
* @param e The element to be added to the `idEntries`.
*/
deleteIDEntry(e) {
if (e[this.ID_KEY]) {
this.idEntries.delete(e[this.ID_KEY]);
}
}
/**
* Resets the store and all contained slice instances to empty.
* Also perform delta notification that sends all current store entries.
* The ActionType.RESET code is sent with the delta notification. Slices
* send their own delta notification.
*
* @example Reset the store.
* ```
* store.reset();
* ```
*/
reset() {
const delta = {
type: "Reset" /* ActionTypes.RESET */,
entries: Array.from(this.entries.values()),
};
this.notifyAll([], delta);
this.entries = new Map();
Array.from(this.slices.values()).forEach((s) => {
s.reset();
});
}
/**
* Call all the notifiers at once.
*
* @param v
* @param delta
*/
notifyAll(v, delta) {
super.notifyAll(v, delta);
this.notifyLoading.next(this.loading);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRVN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvc2xpY2Uvc3JjL2xpYi9FU3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRWhELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFHbkMsT0FBTyxFQUFFLGFBQWEsRUFBRSxFQUFFLEVBQTZCLE1BQU0sTUFBTSxDQUFDO0FBQ3BFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzlELE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFFaEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBdUJHO0FBQ0gsTUFBTSxPQUFPLE1BQVUsU0FBUSxhQUFnQjtJQUM3Qzs7Ozs7Ozs7Ozs7Ozs7O09BZUc7SUFDSCxZQUFZLFdBQWdCLEVBQUUsRUFBRSxNQUFvQjtRQUNsRCxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7UUEyQ2hCOztXQUVHO1FBQ0ssaUJBQVksR0FBRyxJQUFJLGFBQWEsQ0FBaUIsQ0FBQyxDQUFDLENBQUM7UUFFNUQ7Ozs7V0FJRztRQUNJLFdBQU0sR0FBbUIsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQXNHMUM7Ozs7O1dBS0c7UUFDSyxrQkFBYSxHQUFHLElBQUksYUFBYSxDQUFVLENBQUMsQ0FBQyxDQUFDO1FBRXREOzs7Ozs7Ozs7O1dBVUc7UUFDSyxhQUFRLEdBQVksSUFBSSxDQUFDO1FBbUhqQzs7Ozs7V0FLRztRQUNLLG9CQUFlLEdBQUcsSUFBSSxhQUFhLENBQVUsQ0FBQyxDQUFDLENBQUM7UUFFeEQ7Ozs7O1dBS0c7UUFDSyxlQUFVLEdBQVksS0FBSyxDQUFDO1FBK0NwQzs7V0FFRztRQUNLLFdBQU0sR0FBMEIsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQWhXaEQsTUFB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QUFLLEVBQUUsQ0FBQztRQUNaLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ2dCLFNBQVMsQ0FBQyxDQUFNLEVBQUUsS0FBZTtRQUNsRCxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMxQixJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDeEMsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQWJzdHJhY3RTdG9yZSB9IGZyb20gJy4vQWJzdHJhY3RTdG9yZSc7XG5pbXBvcnQgeyBTdG9yZUNvbmZpZyB9IGZyb20gJy4vbW9kZWxzL1N0b3JlQ29uZmlnJztcbmltcG9ydCB7IEdVSUQgfSBmcm9tICcuL3V0aWxpdGllcyc7XG5cbmltcG9ydCB7IEFjdGlvblR5cGVzLCBQcmVkaWNhdGUsIERlbHRhIH0gZnJvbSAnLi9tb2RlbHMvJztcbmltcG9ydCB7IFJlcGxheVN1YmplY3QsIG9mLCBPYnNlcnZhYmxlLCBjb21iaW5lTGF0ZXN0IH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyB0YWtlV2hpbGUsIGZpbHRlciwgc3dpdGNoTWFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgU2xpY2UgfSBmcm9tICcuL1NsaWNlJztcblxuLyoqXG4gKiBUaGlzIGB0b2RvRmFjdG9yeWAgY29kZSB3aWxsIGJlIHVzZWQgdG8gaWxsdXN0cmF0ZSB0aGUgQVBJIGV4YW1wbGVzLiAgVGhlIGZvbGxvd2luZ1xuICogdXRpbGl0aWVzIGFyZSB1c2VkIGluIHRoZSB0ZXN0cyBhbmQgdGhlIEFQSSBUeXBlZG9jIGV4YW1wbGVzIGNvbnRhaW5lZCBoZXJlLlxuICogQGV4YW1wbGUgVXRpbGl0aWVzIGZvciBBUEkgRXhhbXBsZXNcbiAqIGBgYFxuICogZXhwb3J0IGNvbnN0IGVudW0gVG9kb1NsaWNlRW51bSB7XG4gKiAgICBDT01QTEVURSA9IFwiQ29tcGxldGVcIixcbiAqICAgIElOQ09NUExFVEUgPSBcIkluY29tcGxldGVcIlxuICogfVxuICogZXhwb3J0IGNsYXNzIFRvZG8ge1xuICogICAgY29uc3RydWN0b3IoXG4gKiAgICAgICAgIHB1YmxpYyBjb21wbGV0ZTogYm9vbGVhbixcbiAqICAgICAgICAgcHVibGljIHRpdGxlOiBzdHJpbmcsXG4gKiAgICAgICAgIHB1YmxpYyBnaWQ/OnN0cmluZyxcbiAqICAgICAgICAgcHVibGljIGlkPzpzdHJpbmcpIHt9XG4gKiB9XG4gKlxuICogZXhwb3J0IGxldCB0b2RvcyA9IFtuZXcgVG9kbyhmYWxzZSwgXCJZb3UgY29tcGxldGUgbWUhXCIpLCBuZXcgVG9kbyh0cnVlLCBcIllvdSBjb21wbGV0ZWQgbWUhXCIpXTtcbiAqXG4gKiBleHBvcnQgZnVuY3Rpb24gdG9kb3NGYWN0b3J5KCk6VG9kb1tdIHtcbiAqICAgcmV0dXJuIFtuZXcgVG9kbyhmYWxzZSwgXCJZb3UgY29tcGxldGUgbWUhXCIpLCBuZXcgVG9kbyh0cnVlLCBcIllvdSBjb21wbGV0ZWQgbWUhXCIpXTtcbiAqIH1cbiAqIGBgYFxuICovXG5leHBvcnQgY2xhc3MgRVN0b3JlPEU+IGV4dGVuZHMgQWJzdHJhY3RTdG9yZTxFPiB7XG4gIC8qKlxuICAgKiBTdG9yZSBjb25zdHJ1Y3RvciAoSW5pdGlhbGl6YXRpb24gd2l0aCBlbGVtZW50IGlzIG9wdGlvbmFsKVxuICAgKlxuICAgKiBwZXJmb3JtIGluaXRpYWwgbm90aWZpY2F0aW9uIHRvIGFsbCBvYnNlcnZlcnMsXG4gICAqIHN1Y2ggdGhhdCBmdW5jdGlvbnMgbGlrZSB7QGxpbmsgY29tYmluZUxhdGVzdH17fVxuICAgKiB3aWxsIGV4ZWN1dGUgYXQgbGVhc3Qgb25jZS5cbiAgICogXG4gICAqIEBwYXJhbSBlbnRpdGllcyBUaGUgZW50aXRpZXMgdG8gaW5pdGlhbGl6ZSB0aGUgc3RvcmUgd2l0aC5cbiAgICogQHBhcmFtIGNvbmZpZyBUaGUgb3B0aW9uYWwgY29uZmlndXJhdGlvbiBpbnN0YW5jZS5cbiAgICogXG4gICAqIEBleGFtcGxlIEVTdG9yZTxUb2RvPiBDcmVhdGlvblxuICAgKiBgYGBcbiAgICogLy8gSW5pdGlhbGl6ZSB0aGUgU3RvcmVcbiAgICogbGV0IHN0b3JlOiBFU3RvcmU8VG9kbz4gPSBuZXcgRVN0b3JlPFRvZG8+KHRvZG9zRmFjdG9yeSgpKTtcbiAgICogYGBgXG4gICAqL1xuICBjb25zdHJ1Y3RvcihlbnRpdGllczogRVtdID0gW10sIGNvbmZpZz86IFN0b3JlQ29uZmlnKSB7XG4gICAgc3VwZXIoY29uZmlnKTtcbiAgICBjb25zdCBkZWx0YTogRGVsdGE8RT4gPSB7IHR5cGU6IEFjdGlvblR5cGVzLklOVElBTElaRSwgZW50cmllczogZW50aXRpZXMgfTtcbiAgICB0aGlzLnBvc3QoZW50aXRpZXMpO1xuICAgIHRoaXMubm90aWZ5RGVsdGEubmV4dChkZWx0YSk7XG4gIH1cblxuICAvKipcbiAgICogQ2FsbHMgY29tcGxldGUgb24gYWxsIEVTdG9yZSB7QGxpbmsgUmVwbGF5U3ViamVjdH0gaW5zdGFuY2VzLlxuICAgKlxuICAgKiBDYWxsIGRlc3Ryb3kgd2hlbiBkaXNwb3Npbmcgb2YgdGhlIHN0b3JlLlxuICAgKi9cbiAgb3ZlcnJpZGUgZGVzdHJveSgpIHtcbiAgICBzdXBlci5kZXN0cm95KCk7XG4gICAgdGhpcy5ub3RpZnlMb2FkaW5nLmNvbXBsZXRlKCk7XG4gICAgdGhpcy5ub3RpZnlBY3RpdmUuY29tcGxldGUoKTtcbiAgICB0aGlzLnNsaWNlcy5mb3JFYWNoKChzbGljZSkgPT4gc2xpY2UuZGVzdHJveSgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUb2dnbGVzIHRoZSBlbnRpdHk6XG4gICAqXG4gICAqIElmIHRoZSBzdG9yZSBjb250YWlucyB0aGUgZW50aXR5XG4gICAqIGl0IHdpbGwgYmUgZGVsZXRlZC4gIElmIHRoZSBzdG9yZVxuICAgKiBkb2VzIG5vdCBjb250YWlucyB0aGUgZW50aXR5LFxuICAgKiBpdCBpcyBhZGRlZC5cbiAgICogQHBhcmFtIGUgVGhlIGVudGl0eSB0byB0b2dnbGVcbiAgICogQGV4YW1wbGUgVG9nZ2xlIHRoZSBUb2RvIGluc3RhbmNlXG4gICAqIGBgYFxuICAgKiBlc3RvcmUucG9zdCh0b2RvKTtcbiAgICogLy8gUmVtb3ZlIHRvZG9cbiAgICogZXN0b3JlLnRvZ2dsZSh0b2RvKTtcbiAgICogLy8gQWRkIGl0IGJhY2tcbiAgICogZXN0b3JlLnRvZ2dsZSh0b2RvKTtcbiAgICogYGBgXG4gICAqL1xuICBwdWJsaWMgdG9nZ2xlKGU6IEUpIHtcbiAgICBpZiAodGhpcy5jb250YWlucyhlKSkge1xuICAgICAgdGhpcy5kZWxldGUoZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMucG9zdChlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTm90aWZpZXMgb2JzZXJ2ZXJzIHdoZW4gdGhlIHN0b3JlIGlzIGVtcHR5LlxuICAgKi9cbiAgcHJpdmF0ZSBub3RpZnlBY3RpdmUgPSBuZXcgUmVwbGF5U3ViamVjdDxNYXA8c3RyaW5nLCBFPj4oMSk7XG5cbiAgLyoqXG4gICAqIGBNYXBgIG9mIGFjdGl2ZSBlbnR0aWVzLiBUaGUgaW5zdGFuY2UgaXMgcHVibGljIGFuZCBjYW4gYmUgdXNlZFxuICAgKiBkaXJlY3RseSB0byBhZGQgYW5kIHJlbW92ZSBhY3RpdmUgZW50aXRpZXMsIGhvd2V2ZXIgd2UgcmVjb21tZW5kXG4gICAqIHVzaW5nIHRoZSB7QGxpbmsgYWRkQWN0aXZlfSBhbmQge0BsaW5rIGRlbGV0ZUFjdGl2ZX0gbWV0aG9kcy5cbiAgICovXG4gIHB1YmxpYyBhY3RpdmU6IE1hcDxzdHJpbmcsIEU+ID0gbmV3IE1hcCgpO1xuXG4gIC8qKlxuICAgKiBBZGQgbXVsdGlwbGUgZW50aXR5IGVudGl0aWVzIHRvIGFjdGl2ZS5cbiAgICpcbiAgICogSWYgdGhlIGVudGl0eSBpcyBub3QgY29udGFpbmVkIGluIHRoZSBzdG9yZSBpdCBpcyBhZGRlZFxuICAgKiB0byB0aGUgc3RvcmUgYmVmb3JlIGl0IGlzIGFkZGVkIHRvIGBhY3RpdmVgLlxuICAgKlxuICAgKiBBbHNvIHdlIGNsb25lIHRoZSBtYXAgcHJpb3IgdG8gYnJvYWRjYXN0aW5nIGl0IHdpdGhcbiAgICogYG5vdGlmeUFjdGl2ZWAgdG8gbWFrZSBzdXJlIHdlIHdpbGwgdHJpZ2dlciBBbmd1bGFyXG4gICAqIGNoYW5nZSBkZXRlY3Rpb24gaW4gdGhlIGV2ZW50IHRoYXQgaXQgbWFpbnRhaW5zXG4gICAqIGEgcmVmZXJlbmNlIHRvIHRoZSBgYWN0aXZlYCBzdGF0ZSBgTWFwYCBpbnN0YW5jZS5cbiAgICpcbiAgICogQGV4YW1wbGUgQWRkIHRvZG8xIGFuZCB0b2RvMiBhcyBhY3RpdmVcbiAgICogYGBgXG4gICAqIGFkZEFjdGl2ZSh0b2RvMSk7XG4gICAqIGFkZEFjdGl2ZSh0b2RvMik7XG4gICAqIGBgYFxuICAgKi9cbiAgcHVibGljIGFkZEFjdGl2ZShlOiBFKSB7XG4gICAgaWYgKHRoaXMuY29udGFpbnMoZSkpIHtcbiAgICAgIHRoaXMuYWN0aXZlLnNldCgoPGFueT5lKS5naWQsIGUpO1xuICAgICAgdGhpcy5ub3RpZnlBY3RpdmUubmV4dChuZXcgTWFwKHRoaXMuYWN0aXZlKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMucG9zdChlKTtcbiAgICAgIHRoaXMuYWN0aXZlLnNldCgoPGFueT5lKS5naWQsIGUpO1xuICAgICAgdGhpcy5ub3RpZnlBY3RpdmUubmV4dChuZXcgTWFwKHRoaXMuYWN0aXZlKSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIERlbGV0ZSBhbiBhY3RpdmUgZW50aXR5LlxuICAgKlxuICAgKiBBbHNvIHdlIGNsb25lIHRoZSBtYXAgcHJpb3IgdG8gYnJvYWRjYXN0aW5nIGl0IHdpdGhcbiAgICogYG5vdGlmeUFjdGl2ZWAgdG8gbWFrZSBzdXJlIHdlIHdpbGwgdHJpZ2dlciBBbmd1bGFyXG4gICAqIGNoYW5nZSBkZXRlY3Rpb24gaW4gdGhlIGV2ZW50IHRoYXQgaXQgbWFpbnRhaW5zXG4gICAqIGEgcmVmZXJlbmNlIHRvIHRoZSBgYWN0aXZlYCBzdGF0ZSBgTWFwYCBpbnN0YW5jZS5cbiAgICpcbiAgICogQGV4YW1wbGUgUmVtb3ZlIHRvZG8xIGFuZCB0b2RvMiBhcyBhY3RpdmUgZW50aXRpZXNcbiAgICogYGBgXG4gICAqIGRlbGV0ZUFjdGl2ZSh0b2RvMSk7XG4gICAqIGRlbGV0ZUFjdGl2ZSh0b2RvMik7XG4gICAqIGBgYFxuICAgKi9cbiAgcHVibGljIGRlbGV0ZUFjdGl2ZShlOiBFKSB7XG4gICAgdGhpcy5hY3RpdmUuZGVsZXRlKCg8YW55PmUpLmdpZCk7XG4gICAgdGhpcy5ub3RpZnlBY3RpdmUubmV4dChuZXcgTWFwKHRoaXMuYWN0aXZlKSk7XG4gIH1cblxuICAvKipcbiAgICogQ2xlYXIgLyByZXNldCB0aGUgYWN0aXZlIGVudGl0eSBtYXAuXG4gICAqXG4gICAqIEFsc28gd2UgY2xvbmUgdGhlIG1hcCBwcmlvciB0byBicm9hZGNhc3RpbmcgaXQgd2l0aFxuICAgKiBgbm90aWZ5QWN0aXZlYCB0byBtYWtlIHN1cmUgd2Ugd2lsbCB0cmlnZ2VyIEFuZ3VsYXJcbiAgICogY2hhbmdlIGRldGVjdGlvbiBpbiB0aGUgZXZlbnQgdGhhdCBpdCBtYWludGFpbnNcbiAgICogYSByZWZlcmVuY2UgdG8gdGhlIGBhY3RpdmVgIHN0YXRlIGBNYXBgIGluc3RhbmNlLlxuICAgKlxuICAgKiBAZXhhbXBsZSBDbGVhciBhY3RpdmUgdG9kbyBpbnN0YW5jZXNcbiAgICogYGBgXG4gICAqIHN0b3JlLmNsZWFyQWN0aXZlKCk7XG4gICAqIGBgYFxuICAgKi9cbiAgY2xlYXJBY3RpdmUoKSB7XG4gICAgdGhpcy5hY3RpdmUuY2xlYXIoKTtcbiAgICB0aGlzLm5vdGlmeUFjdGl2ZS5uZXh0KG5ldyBNYXAodGhpcy5hY3RpdmUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPYnNlcnZlIHRoZSBhY3RpdmUgZW50aXRpZXMuXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYFxuICAgKiBsZXQgYWN0aXZlJCA9IHN0b3JlLm9ic2VydmVBY3RpdmUoKTtcbiAgICogYGBgXG4gICAqL1xuICBwdWJsaWMgb2JzZXJ2ZUFjdGl2ZSgpIHtcbiAgICByZXR1cm4gdGhpcy5ub3RpZnlBY3RpdmUuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICAvKipcbiAgICogT2JzZXJ2ZSB0aGUgYWN0aXZlIGVudGl0eS5cbiAgICogQGV4YW1wbGVcbiAgICAgPHByZT5cbiAgICBsZXQgYWN0aXZlJCA9IHNvdXJjZS5hY3RpdmVTbmFwc2hvdCgpO1xuICAgIDwvcHJlPlxuICAqL1xuICBwdWJsaWMgYWN0aXZlU25hcHNob3QoKSB7XG4gICAgcmV0dXJuIEFycmF5LmZyb20odGhpcy5hY3RpdmUudmFsdWVzKCkpO1xuICB9XG5cbiAgLy89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gTE9BRElOR1xuICAvLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBPYnNlcnZhYmxlIG9mIGVycm9ycyBvY2N1cnJlZCBkdXJpbmcgYSBsb2FkIHJlcXVlc3QuXG4gICAqXG4gICAqIFRoZSBlcnJvciBPYnNlcnZhYmxlIHNob3VsZCBiZSBjcmVhdGVkIGJ5IHRoZVxuICAgKiBjbGllbnQuXG4gICAqL1xuICBwdWJsaWMgbG9hZGluZ0Vycm9yITogT2JzZXJ2YWJsZTxhbnk+O1xuXG4gIC8qKlxuICAgKiBOb3RpZmllcyBvYnNlcnZlcnMgd2hlbiB0aGUgc3RvcmUgaXMgbG9hZGluZy5cbiAgICpcbiAgICogVGhpcyBpcyBhIGNvbW1vbiBwYXR0ZXJuIGZvdW5kIHdoZW4gaW1wbGVtZW50aW5nXG4gICAqIGBPYnNlcnZhYmxlYCBkYXRhIHNvdXJjZXMuXG4gICAqL1xuICBwcml2YXRlIG5vdGlmeUxvYWRpbmcgPSBuZXcgUmVwbGF5U3ViamVjdDxib29sZWFuPigxKTtcblxuICAvKipcbiAgICogVGhlIGN1cnJlbnQgbG9hZGluZyBzdGF0ZS4gIFVzZSBsb2FkaW5nIHdoZW4gZmV0Y2hpbmcgbmV3XG4gICAqIGRhdGEgZm9yIHRoZSBzdG9yZS4gIFRoZSBkZWZhdWx0IGxvYWRpbmcgc3RhdGUgaXMgYHRydWVgLlxuICAgKlxuICAgKiBUaGlzIGlzIHN1Y2ggdGhhdCBpZiBkYXRhIGlzIGZldGNoZWQgYXN5bmNocm9ub3VzbHlcbiAgICogaW4gYSBzZXJ2aWNlLCBjb21wb25lbnRzIGNhbiB3YWl0IG9uIGxvYWRpbmcgbm90aWZpY2F0aW9uXG4gICAqIGJlZm9yZSBhdHRlbXB0aW5nIHRvIHJldHJpZXZlIGRhdGEgZnJvbSB0aGUgc2VydmljZS5cbiAgICpcbiAgICogTG9hZGluZyBjb3VsZCBiZSBiYXNlZCBvbiBhIGNvbXBvc2l0ZSByZXNwb25zZS4gIEZvciBleGFtcGxlXG4gICAqIHdoZW4gdGhlIHN0b2NrIGFuZCBtdXR1YWwgZnVuZHMgaGF2ZSBsb2FkZWQsIHNldCBsb2FkaW5nIHRvIGBmYWxzZWAuXG4gICAqL1xuICBwcml2YXRlIF9sb2FkaW5nOiBib29sZWFuID0gdHJ1ZTtcblxuICAvKipcbiAgICogU2V0cyB0aGUgY3VycmVudCBsb2FkaW5nIHN0YXRlIGFuZCBub3RpZmllcyBvYnNlcnZlcnMuXG4gICAqL1xuICBzZXQgbG9hZGluZyhsb2FkaW5nOiBib29sZWFuKSB7XG4gICAgdGhpcy5fbG9hZGluZyA9IGxvYWRpbmc7XG4gICAgdGhpcy5ub3RpZnlMb2FkaW5nLm5leHQodGhpcy5fbG9hZGluZyk7XG4gIH1cblxuICAvKipcbiAgICogQHJldHVybiBBIHNuYXBzaG90IG9mIHRoZSBsb2FkaW5nIHN0YXRlLlxuICAgKiBAZXhhbXBsZSBDcmVhdGUgYSByZWZlcmVuY2UgdG8gdGhlIGxvYWRpbmcgc3RhdGVcbiAgICogYGBgXG4gICAqIGNvbnN0IGxvYWRpbmc6Ym9vbGVhbiA9IHRvZG9TdG9yZS5sb2FkaW5nO1xuICAgKiBgYGBcbiAgICovXG4gIGdldCBsb2FkaW5nKCkge1xuICAgIHJldHVybiB0aGlzLl9sb2FkaW5nO1xuICB9XG5cbiAgLyoqXG4gICAqIE9ic2VydmUgbG9hZGluZy5cbiAgICpcbiAgICogTm90ZSB0aGF0IHRoaXMgb2J2ZXJhYmxlIHBpcGVkIHRocm91Z2hcbiAgICogYHRha2VXaGlsZSh2LT52LCB0cnVlKSwgc3VjaCB0aGF0IGl0IHdpbGxcbiAgICogY29tcGxldGUgYWZ0ZXIgZWFjaCBlbWlzc2lvbi5cbiAgICpcbiAgICogU2VlOlxuICAgKiBodHRwczovL2ZpcmVmbHlzZW1hbnRpY3MubWVkaXVtLmNvbS93YWl0aW5nLW9uLWVzdG9yZS10by1sb2FkLThkY2JlMTYxNjEzY1xuICAgKlxuICAgKiBGb3IgbW9yZSBkZXRhaWxzLlxuICAgKiBBbHNvIG5vdGUgdGhhdCB2PT52IGlzIHRoZSBzYW1lIGFzIHY9PnYhPWZhbHNlXG4gICAqIFxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGBcbiAgICogY29uc3Qgb2JzZXJ2ZUxvYWRpbmdIYW5kbGVyOiBPYnNlcnZlcjxib29sZWFuPiA9IHtcbiAgICogICBjb21wbGV0ZTogKCkgPT4ge1xuICAgKiAgICAgY29uc29sZS5sb2coYERhdGEgTG9hZGVkIGFuZCBPYnNlcnZhYmxlIE1hcmtlZCBhcyBDb21wbGV0ZWApO1xuICAgKiAgIH0sIC8vIGNvbXBsZXRlSGFuZGxlclxuICAgKiAgIGVycm9yOiAoKSA9PiB7XG4gICAqICAgICBjb25zb2xlLmxvZyhgQW55IEVycm9ycz9gKTtcbiAgICogICB9LCAvLyBlcnJvckhhbmRsZXJcbiAgICogICBuZXh0OiAobCkgPT4ge1xuICAgKiAgICAgY29uc29sZS5sb2coYERhdGEgbG9hZGVkIGFuZCBsb2FkaW5nIGlzICR7bH1gKTtcbiAgICogICB9LFxuICAgKiB9O1xuICAgKlxuICAgKiBjb25zdCBvYnNlcnZlTG9hZGluZ1Jlc3Vic2NyaWJlSGFuZGxlcjogT2JzZXJ2ZXI8Ym9vbGVhbj4gPSB7XG4gICAqICAgY29tcGxldGU6ICgpID0+IHtcbiAgICogICAgIGNvbnNvbGUubG9nKGBEYXRhIExvYWRlZCBhbmQgUmVzdWJzY3JpYmUgT2JzZXJ2YWJsZSBNYXJrZWQgYXMgQ29tcGxldGVgKTtcbiAgICogICB9LCAvLyBjb21wbGV0ZUhhbmRsZXJcbiAgICogICBlcnJvcjogKCkgPT4ge1xuICAgKiAgICAgY29uc29sZS5sb2coYEFueSBSZXN1YnNjcmliZSBFcnJvcnM/YCk7XG4gICAqICAgfSwgLy8gZXJyb3JIYW5kbGVyXG4gICAqICAgbmV4dDogKGwpID0+IHtcbiAgICogICAgIGNvbnNvbGUubG9nKGBEYXRhIGxvYWRlZCBhbmQgcmVzdXNic2NyaWJlIGxvYWRpbmcgIHZhbHVlIGlzICR7bH1gKTtcbiAgICogICB9LFxuICAgKiB9O1xuICAgKlxuICAgKiBjb25zdCB0b2RvU3RvcmU6IEVTdG9yZTxUb2RvPiA9IG5ldyBFU3RvcmUoKTtcbiAgICogLy89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgKiAvLyBMb2FkaW5nIGlzIHRydWUgYnkgZGVmYXVsdFxuICAgKiAvLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAqIGNvbnNvbGUubG9nKGBUaGUgaW5pdGlhbCB2YWx1ZSBvZiBsb2FkaW5nIGlzICR7dG9kb1N0b3JlLmxvYWRpbmd9YCk7XG4gICAqIC8vPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICogLy8gT2JzZXJ2ZSBMb2FkaW5nXG4gICAqIC8vPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICogbGV0IGxvYWRpbmckOiBPYnNlcnZhYmxlPGJvb2xlYW4+ID0gdG9kb1N0b3JlLm9ic2VydmVMb2FkaW5nKCk7XG4gICAqIGxvYWRpbmckLnN1YnNjcmliZSgobCkgPT4gY29uc29sZS5sb2coYFRoZSB2YWx1ZSBvZiBsb2FkaW5nIGlzICR7bH1gKSk7XG4gICAqXG4gICAqIHRvZG9TdG9yZS5sb2FkaW5nID0gZmFsc2U7XG4gICAqIGxvYWRpbmckLnN1YnNjcmliZShvYnNlcnZlTG9hZGluZ0hhbmRsZXIpO1xuICAgKiAvLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAqIC8vIFRoZSBzdWJzY3JpcHRpb24gbm8gbG9uZ2VyIGZpcmVzXG4gICAqIC8vPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICogdG9kb1N0b3JlLmxvYWRpbmcgPSB0cnVlO1xuICAgKiB0b2RvU3RvcmUubG9hZGluZyA9IGZhbHNlO1xuICAgKlxuICAgKiAvLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAqIC8vIFRoZSBzdWJzY3JpcHRpb24gbm8gbG9uZ2VyIGZpcmVzLFxuICAgKiAvLyBzbyBpZiB3ZSB3YW50IHRvIG9ic2VydmUgbG9hZGluZyBhZ2FpblxuICAgKiAvLyByZXN1c2JzY3JpYmUuXG4gICAqIC8vPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICogdG9kb1N0b3JlLmxvYWRpbmcgPSB0cnVlO1xuICAgKiBsb2FkaW5nJCA9IHRvZG9TdG9yZS5vYnNlcnZlTG9hZGluZygpO1xuICAgKiBsb2FkaW5nJC5zdWJzY3JpYmUob2JzZXJ2ZUxvYWRpbmdSZXN1YnNjcmliZUhhbmRsZXIpO1xuICAgKiB0b2RvU3RvcmUubG9hZGluZyA9IGZhbHNlO1xuICAgKiBgYGBcbiAgICovXG4gIHB1YmxpYyBvYnNlcnZlTG9hZGluZygpIHtcbiAgICByZXR1cm4gdGhpcy5ub3RpZnlMb2FkaW5nLmFzT2JzZXJ2YWJsZSgpLnBpcGUodGFrZVdoaWxlKCh2KSA9PiB2LCB0cnVlKSk7XG4gIH1cblxuICAvKipcbiAgICogTm90ZmlpZXMgd2hlbiBsb2FkaW5nIGhhcyBjb21wbGV0ZWQuXG4gICAqL1xuICBwdWJsaWMgb2JzZXJ2ZUxvYWRpbmdDb21wbGV0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy5vYnNlcnZlTG9hZGluZygpLnBpcGUoXG4gICAgICBmaWx0ZXIoKGxvYWRpbmcpID0+IGxvYWRpbmcgPT0gZmFsc2UpLFxuICAgICAgc3dpdGNoTWFwKCgpID0+IG9mKHRydWUpKVxuICAgICk7XG4gIH1cblxuICAvLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBTRUFSQ0hJTkdcbiAgLy89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLyoqXG4gICAqIE9ic2VydmFibGUgb2YgZXJyb3JzIG9jY3VycmVkIGR1cmluZyBhIHNlYXJjaCByZXF1ZXN0LlxuICAgKlxuICAgKiBUaGUgZXJyb3IgT2JzZXJ2YWJsZSBzaG91bGQgYmUgY3JlYXRlZCBieSB0aGVcbiAgICogY2xpZW50LlxuICAgKi9cbiAgcHVibGljIHNlYXJjaEVycm9yITogT2JzZXJ2YWJsZTxhbnk+O1xuXG4gIC8qKlxuICAgKiBOb3RpZmllcyBvYnNlcnZlcnMgdGhhdCBhIHNlYXJjaCBpcyBpbiBwcm9ncmVzcy5cbiAgICpcbiAgICogVGhpcyBpcyBhIGNvbW1vbiBwYXR0ZXJuIGZvdW5kIHdoZW4gaW1wbGVtZW50aW5nXG4gICAqIGBPYnNlcnZhYmxlYCBkYXRhIHNvdXJjZXMuXG4gICAqL1xuICBwcml2YXRlIG5vdGlmeVNlYXJjaGluZyA9IG5ldyBSZXBsYXlTdWJqZWN0PGJvb2xlYW4+KDEpO1xuXG4gIC8qKlxuICAgKiBUaGUgY3VycmVudCBgc2VhcmNoaW5nYCBzdGF0ZS4gIFVzZSBgc2VhcmNoaW5nYFxuICAgKiBmb3IgZXhhbXBsZSB0byBkaXNwbGF5IGEgc3Bpbm5uZXJcbiAgICogd2hlbiBwZXJmb3JtaW5nIGEgc2VhcmNoLlxuICAgKiBUaGUgZGVmYXVsdCBgc2VhcmNoaW5nYCBzdGF0ZSBpcyBgZmFsc2VgLlxuICAgKi9cbiAgcHJpdmF0ZSBfc2VhcmNoaW5nOiBib29sZWFuID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIFNldHMgdGhlIGN1cnJlbnQgc2VhcmNoaW5nIHN0YXRlIGFuZCBub3RpZmllcyBvYnNlcnZlcnMuXG4gICAqL1xuICBzZXQgc2VhcmNoaW5nKHNlYXJjaGluZzogYm9vbGVhbikge1xuICAgIHRoaXMuX3NlYXJjaGluZyA9IHNlYXJjaGluZztcbiAgICB0aGlzLm5vdGlmeVNlYXJjaGluZy5uZXh0KHRoaXMuX3NlYXJjaGluZyk7XG4gIH1cblxuICAvKipcbiAgICogQHJldHVybiBBIHNuYXBzaG90IG9mIHRoZSBzZWFyY2hpbmcgc3RhdGUuXG4gICAqL1xuICBnZXQgc2VhcmNoaW5nKCkge1xuICAgIHJldHVybiB0aGlzLl9zZWFyY2hpbmc7XG4gIH1cblxuICAvKipcbiAgICogT2JzZXJ2ZSBzZWFyY2hpbmcuXG4gICAqIEBleGFtcGxlXG4gICAgIDxwcmU+XG4gICAgbGV0IHNlYXJjaGluZyQgPSBzb3VyY2Uub2JzZXJ2ZVNlYXJjaGluZygpO1xuICAgIDwvcHJlPlxuICBcbiAgICBOb3RlIHRoYXQgdGhpcyBvYnZlcmFibGUgcGlwZWQgdGhyb3VnaFxuICAgIGB0YWtlV2hpbGUodi0+diwgdHJ1ZSksIHN1Y2ggdGhhdCBpdCB3aWxsIFxuICAgIGNvbXBsZXRlIGFmdGVyIGVhY2ggZW1pc3Npb24uXG4gIFxuICAgIFNlZTpcbiAgICBodHRwczovL21lZGl1bS5jb20vQG9sZS5lcnNveS93YWl0aW5nLW9uLWVzdG9yZS10by1sb2FkLThkY2JlMTYxNjEzY1xuICBcbiAgICBGb3IgbW9yZSBkZXRhaWxzLlxuICAqL1xuICBwdWJsaWMgb2JzZXJ2ZVNlYXJjaGlu