UNPKG

carbon-components-angular

Version:
629 lines 64.3 kB
import { Component, Input, Output, EventEmitter, ViewChild, ViewChildren } from "@angular/core"; import { Observable, isObservable, of } from "rxjs"; import { first } from "rxjs/operators"; import { AbstractDropdownView } from "../abstract-dropdown-view.class"; import { watchFocusJump } from "../dropdowntools"; import * as i0 from "@angular/core"; import * as i1 from "carbon-components-angular/i18n"; import * as i2 from "@angular/common"; import * as i3 from "carbon-components-angular/icon"; /** * ```html * <cds-dropdown-list [items]="listItems"></cds-dropdown-list> * ``` * ```typescript * listItems = [ * { * content: "item one", * selected: false * }, * { * content: "item two", * selected: false, * }, * { * content: "item three", * selected: false * }, * { * content: "item four", * selected: false * } * ]; * ``` */ export class DropdownList { /** * Creates an instance of `DropdownList`. */ constructor(elementRef, i18n, appRef) { this.elementRef = elementRef; this.i18n = i18n; this.appRef = appRef; this.ariaLabel = this.i18n.get().DROPDOWN_LIST.LABEL; /** * Template to bind to items in the `DropdownList` (optional). */ this.listTpl = null; /** * Event to emit selection of a list item within the `DropdownList`. */ this.select = new EventEmitter(); /** * Event to emit scroll event of a list within the `DropdownList`. */ this.scroll = new EventEmitter(); /** * Event to suggest a blur on the view. * Emits _after_ the first/last item has been focused. * ex. * ArrowUp -> focus first item * ArrowUp -> emit event * * When this event fires focus should be placed on some element outside of the list - blurring the list as a result */ this.blurIntent = new EventEmitter(); /** * Defines whether or not the `DropdownList` supports selecting multiple items as opposed to single * item selection. */ this.type = "single"; /** * Defines whether to show title attribute or not */ this.showTitles = true; /** * Defines the rendering size of the `DropdownList` input component. */ this.size = "md"; this.listId = `listbox-${DropdownList.listCount++}`; this.highlightedItem = null; /** * Holds the list of items that will be displayed in the `DropdownList`. * It differs from the the complete set of items when filtering is used (but * it is always a subset of the total items in `DropdownList`). */ this.displayItems = []; /** * Maintains the index for the selected item within the `DropdownList`. */ this.index = -1; /** * Useful representation of the items, should be accessed via `getListItems`. */ this._items = []; } /** * The list items belonging to the `DropdownList`. */ set items(value) { if (isObservable(value)) { if (this._itemsSubscription) { this._itemsSubscription.unsubscribe(); } this._itemsReady = new Observable((observer) => { this._itemsSubscription = value.subscribe(v => { this.updateList(v); observer.next(true); observer.complete(); }); }); this.onItemsReady(null); } else { this.updateList(value); } this._originalItems = value; } get items() { return this._originalItems; } /** * Retrieves array of list items and index of the selected item after view has rendered. * Additionally, any Observables for the `DropdownList` are initialized. */ ngAfterViewInit() { this.index = this.getListItems().findIndex(item => item.selected); this.setupFocusObservable(); setTimeout(() => { this.doEmitSelect(true); }); } /** * Removes any Observables on destruction of the component. */ ngOnDestroy() { if (this.focusJump) { this.focusJump.unsubscribe(); } if (this._itemsSubscription) { this._itemsSubscription.unsubscribe(); } } doEmitSelect(isUpdate = true) { if (this.type === "single") { this.select.emit({ item: this._items.find(item => item.selected), isUpdate: isUpdate }); } else { // abuse javascripts object mutability until we can break the API and switch to // { items: [], isUpdate: true } const selected = this.getSelected() || []; selected["isUpdate"] = isUpdate; this.select.emit(selected); } } getItemId(index) { return `${this.listId}-${index}`; } /** * Updates the displayed list of items and then retrieves the most current properties for the `DropdownList` from the DOM. */ updateList(items) { this._items = items.map(item => Object.assign({}, item)); this.displayItems = this._items; this.updateIndex(); this.setupFocusObservable(); this.doEmitSelect(); } /** * Filters the items being displayed in the DOM list. */ filterBy(query = "") { if (query) { this.displayItems = this.getListItems().filter(item => item.content.toLowerCase().includes(query.toLowerCase())); // Reset index if items were found // Prevent selecting index in list that are undefined. if (this.displayItems) { this.index = 0; } } else { this.displayItems = this.getListItems(); } this.updateIndex(); } /** * Initializes (or re-initializes) the Observable that handles switching focus to an element based on * key input matching the first letter of the item in the list. */ setupFocusObservable() { if (!this.list) { return; } if (this.focusJump) { this.focusJump.unsubscribe(); } let elList = Array.from(this.list.nativeElement.querySelectorAll("li")); this.focusJump = watchFocusJump(this.list.nativeElement, elList) .subscribe(el => { el.focus(); }); } /** * Returns the `ListItem` that is subsequent to the selected item in the `DropdownList`. */ getNextItem() { if (this.index < this.displayItems.length - 1) { this.index++; } return this.displayItems[this.index]; } /** * Returns `true` if the selected item is not the last item in the `DropdownList`. */ hasNextElement() { return this.index < this.displayItems.length - 1 && (!(this.index === this.displayItems.length - 2) || !this.displayItems[this.index + 1].disabled); } /** * Returns the `HTMLElement` for the item that is subsequent to the selected item. */ getNextElement() { // Only return native elements if they are rendered const elemList = this.listElementList ? this.listElementList.toArray() : []; if (!elemList.length) { return null; } /** * Start checking from next index * Continue looping through the list until a non disabeled element is found or * end of list is reached */ for (let i = this.index + 1; i < elemList.length; i++) { // If the values in the list are not disabled if (!this.displayItems[i].disabled) { this.index = i; return elemList[i].nativeElement; } } return elemList[this.index]?.nativeElement; } /** * Returns the `ListItem` that precedes the selected item within `DropdownList`. */ getPrevItem() { if (this.index > 0) { this.index--; } return this.displayItems[this.index]; } /** * Returns `true` if the selected item is not the first in the list. */ hasPrevElement() { return this.index > 0 && (!(this.index === 1) || !this.displayItems[0].disabled); } /** * Returns the `HTMLElement` for the item that precedes the selected item. */ getPrevElement() { // Only return native elements if they are rendered const elemList = this.listElementList ? this.listElementList.toArray() : []; if (!elemList.length) { return null; } /** * Start checking from next index * Continue looping through the list until a non disabeled element is found or * end of list is reached */ for (let i = this.index - 1; i < this.index && i >= 0; i--) { // If the values in the list are not disabled if (!this.displayItems[i].disabled) { this.index = i; return elemList[i].nativeElement; } } return elemList[this.index].nativeElement; } /** * Returns the `ListItem` that is selected within `DropdownList`. */ getCurrentItem() { if (this.index < 0) { return this.displayItems[0]; } return this.displayItems[this.index]; } /** * Returns the `HTMLElement` for the item that is selected within the `DropdownList`. */ getCurrentElement() { if (this.index < 0) { return this.listElementList.first.nativeElement; } return this.listElementList.toArray()[this.index].nativeElement; } /** * Returns the items as an Array */ getListItems() { return this._items; } /** * Returns a list containing the selected item(s) in the `DropdownList`. */ getSelected() { let selected = this.getListItems().filter(item => item.selected); if (selected.length === 0) { return []; } return selected; } /** * Transforms array input list of items to the correct state by updating the selected item(s). */ propagateSelected(value) { // if we get a non-array, log out an error (since it is one) if (!Array.isArray(value)) { console.error(`${this.constructor.name}.propagateSelected expects an Array<ListItem>, got ${JSON.stringify(value)}`); } this.onItemsReady(() => { // loop through the list items and update the `selected` state for matching items in `value` for (let oldItem of this.getListItems()) { // copy the item let tempOldItem = Object.assign({}, oldItem); // deleted selected because it's what we _want_ to change delete tempOldItem.selected; // stringify for compare tempOldItem = JSON.stringify(tempOldItem); for (let newItem of value) { // copy the item let tempNewItem = Object.assign({}, newItem); // deleted selected because it's what we _want_ to change delete tempNewItem.selected; // stringify for compare tempNewItem = JSON.stringify(tempNewItem); // do the compare if (tempOldItem.includes(tempNewItem)) { oldItem.selected = newItem.selected; // if we've found a matching item, we can stop looping break; } else { oldItem.selected = false; } } } }); } /** * Initializes focus in the list, effectively a wrapper for `getCurrentElement().focus()` */ initFocus() { if (this.index < 0) { this.updateIndex(); } this.list.nativeElement.focus(); setTimeout(() => { this.highlightedItem = this.getItemId(this.index); }); } updateIndex() { // initialize index on the first selected item or // on the next non disabled item if no items are selected // in case, if all items are disabled, the index value will remain same const selected = this.getSelected(); if (selected.length) { this.index = this.displayItems.indexOf(selected[0]); } else if (this.hasNextElement()) { this.getNextElement(); } } /** * Manages the keyboard accessibility for navigation and selection within a `DropdownList`. */ navigateList(event) { if (event.key === "Enter" || event.key === " ") { if (this.listElementList.some(option => option.nativeElement === event.target)) { event.preventDefault(); } if (event.key === "Enter") { this.doClick(event, this.getCurrentItem()); } } else if (event.key === "ArrowDown" || event.key === "ArrowUp") { event.preventDefault(); if (event.key === "ArrowDown") { if (this.hasNextElement()) { this.getNextElement()?.scrollIntoView({ block: "end" }); } else { this.blurIntent.emit("bottom"); } } else if (event.key === "ArrowUp") { if (this.hasPrevElement()) { this.getPrevElement().scrollIntoView({ block: "nearest" }); } else { this.blurIntent.emit("top"); } } setTimeout(() => { this.highlightedItem = this.getItemId(this.index); }); } } /** * Emits the selected item or items after a mouse click event has occurred. */ doClick(event, item) { event.preventDefault(); if (item && !item.disabled) { this.list.nativeElement.focus(); if (this.type === "single") { item.selected = true; // reset the selection for (let otherItem of this.getListItems()) { if (item !== otherItem) { otherItem.selected = false; } } } else { item.selected = !item.selected; } this.index = this.displayItems.indexOf(item); this.highlightedItem = this.getItemId(this.index); this.doEmitSelect(false); this.appRef.tick(); } } onItemFocus(index) { const element = this.listElementList.toArray()[index].nativeElement; element.classList.add("cds--list-box__menu-item--highlighted"); element.tabIndex = 0; } onItemBlur(index) { const element = this.listElementList.toArray()[index].nativeElement; element.classList.remove("cds--list-box__menu-item--highlighted"); element.tabIndex = -1; } /** * Emits the scroll event of the options list */ emitScroll(event) { const atTop = event.srcElement.scrollTop === 0; const atBottom = event.srcElement.scrollHeight - event.srcElement.scrollTop === event.srcElement.clientHeight; const customScrollEvent = { atTop, atBottom, event }; this.scroll.emit(customScrollEvent); } /** * Subscribe the function passed to an internal observable that will resolve once the items are ready */ onItemsReady(subcription) { // this subscription will auto unsubscribe because of the `first()` pipe (this._itemsReady || of(true)).pipe(first()).subscribe(subcription); } reorderSelected(moveFocus = true) { this.displayItems = [...this.getSelected(), ...this.getListItems().filter(item => !item.selected)]; if (moveFocus) { setTimeout(() => { this.updateIndex(); this.highlightedItem = this.getItemId(this.index); }); } } } DropdownList.listCount = 0; DropdownList.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DropdownList, deps: [{ token: i0.ElementRef }, { token: i1.I18n }, { token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Component }); DropdownList.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: DropdownList, selector: "cds-dropdown-list, ibm-dropdown-list", inputs: { ariaLabel: "ariaLabel", items: "items", listTpl: "listTpl", type: "type", showTitles: "showTitles" }, outputs: { select: "select", scroll: "scroll", blurIntent: "blurIntent" }, providers: [ { provide: AbstractDropdownView, useExisting: DropdownList } ], viewQueries: [{ propertyName: "list", first: true, predicate: ["list"], descendants: true, static: true }, { propertyName: "listElementList", predicate: ["listItem"], descendants: true }], ngImport: i0, template: ` <ul #list [id]="listId" role="listbox" class="cds--list-box__menu cds--multi-select" (scroll)="emitScroll($event)" (keydown)="navigateList($event)" tabindex="-1" [attr.aria-label]="ariaLabel" [attr.aria-activedescendant]="highlightedItem"> <li role="option" *ngFor="let item of displayItems; let i = index" (click)="doClick($event, item)" class="cds--list-box__menu-item" [attr.aria-selected]="item.selected" [id]="getItemId(i)" [attr.title]=" showTitles ? item.content : null" [attr.disabled]="item.disabled ? true : null" [ngClass]="{ 'cds--list-box__menu-item--active': item.selected, 'cds--list-box__menu-item--highlighted': highlightedItem === getItemId(i) }"> <div #listItem tabindex="-1" class="cds--list-box__menu-item__option"> <div *ngIf="!listTpl && type === 'multi'" class="cds--form-item cds--checkbox-wrapper"> <label [attr.data-contained-checkbox-state]="item.selected" class="cds--checkbox-label"> <input class="cds--checkbox" type="checkbox" [checked]="item.selected" [disabled]="item.disabled" tabindex="-1"> <span class="cds--checkbox-appearance"></span> <span class="cds--checkbox-label-text">{{item.content}}</span> </label> </div> <ng-container *ngIf="!listTpl && type === 'single'">{{item.content}}</ng-container> <svg *ngIf="!listTpl && type === 'single'" cdsIcon="checkmark" size="16" class="cds--list-box__menu-item__selected-icon"> </svg> <ng-template *ngIf="listTpl" [ngTemplateOutletContext]="{item: item}" [ngTemplateOutlet]="listTpl"> </ng-template> </div> </li> </ul>`, isInline: true, dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DropdownList, decorators: [{ type: Component, args: [{ selector: "cds-dropdown-list, ibm-dropdown-list", template: ` <ul #list [id]="listId" role="listbox" class="cds--list-box__menu cds--multi-select" (scroll)="emitScroll($event)" (keydown)="navigateList($event)" tabindex="-1" [attr.aria-label]="ariaLabel" [attr.aria-activedescendant]="highlightedItem"> <li role="option" *ngFor="let item of displayItems; let i = index" (click)="doClick($event, item)" class="cds--list-box__menu-item" [attr.aria-selected]="item.selected" [id]="getItemId(i)" [attr.title]=" showTitles ? item.content : null" [attr.disabled]="item.disabled ? true : null" [ngClass]="{ 'cds--list-box__menu-item--active': item.selected, 'cds--list-box__menu-item--highlighted': highlightedItem === getItemId(i) }"> <div #listItem tabindex="-1" class="cds--list-box__menu-item__option"> <div *ngIf="!listTpl && type === 'multi'" class="cds--form-item cds--checkbox-wrapper"> <label [attr.data-contained-checkbox-state]="item.selected" class="cds--checkbox-label"> <input class="cds--checkbox" type="checkbox" [checked]="item.selected" [disabled]="item.disabled" tabindex="-1"> <span class="cds--checkbox-appearance"></span> <span class="cds--checkbox-label-text">{{item.content}}</span> </label> </div> <ng-container *ngIf="!listTpl && type === 'single'">{{item.content}}</ng-container> <svg *ngIf="!listTpl && type === 'single'" cdsIcon="checkmark" size="16" class="cds--list-box__menu-item__selected-icon"> </svg> <ng-template *ngIf="listTpl" [ngTemplateOutletContext]="{item: item}" [ngTemplateOutlet]="listTpl"> </ng-template> </div> </li> </ul>`, providers: [ { provide: AbstractDropdownView, useExisting: DropdownList } ] }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.I18n }, { type: i0.ApplicationRef }]; }, propDecorators: { ariaLabel: [{ type: Input }], items: [{ type: Input }], listTpl: [{ type: Input }], select: [{ type: Output }], scroll: [{ type: Output }], blurIntent: [{ type: Output }], list: [{ type: ViewChild, args: ["list", { static: true }] }], type: [{ type: Input }], showTitles: [{ type: Input }], listElementList: [{ type: ViewChildren, args: ["listItem"] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcGRvd24tbGlzdC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvZHJvcGRvd24vbGlzdC9kcm9wZG93bi1saXN0LmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ04sU0FBUyxFQUNULEtBQUssRUFDTCxNQUFNLEVBRU4sWUFBWSxFQUdaLFNBQVMsRUFFVCxZQUFZLEVBR1osTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQWdCLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNsRSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFHdkMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFdkUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGtCQUFrQixDQUFDOzs7OztBQUlsRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JHO0FBcUVILE1BQU0sT0FBTyxZQUFZO0lBMEd4Qjs7T0FFRztJQUNILFlBQW1CLFVBQXNCLEVBQVksSUFBVSxFQUFZLE1BQXNCO1FBQTlFLGVBQVUsR0FBVixVQUFVLENBQVk7UUFBWSxTQUFJLEdBQUosSUFBSSxDQUFNO1FBQVksV0FBTSxHQUFOLE1BQU0sQ0FBZ0I7UUEzR3hGLGNBQVMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUM7UUEwQnpEOztXQUVHO1FBQ00sWUFBTyxHQUE4QixJQUFJLENBQUM7UUFDbkQ7O1dBRUc7UUFDTyxXQUFNLEdBQXNFLElBQUksWUFBWSxFQUFFLENBQUM7UUFDekc7O1dBRUc7UUFDTyxXQUFNLEdBQW9DLElBQUksWUFBWSxFQUFFLENBQUM7UUFDdkU7Ozs7Ozs7O1dBUUc7UUFDTyxlQUFVLEdBQUcsSUFBSSxZQUFZLEVBQW9CLENBQUM7UUFLNUQ7OztX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TSxFQUFFLElBQUksRUFBRTtnQkFLMUIsSUFBSTtzQkFBWixLQUFLO2dCQUtHLFVBQVU7c0JBQWxCLEtBQUs7Z0JBcUI4QixlQUFlO3NCQUFsRCxZQUFZO3VCQUFDLFVBQVUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuXHRDb21wb25lbnQsXG5cdElucHV0LFxuXHRPdXRwdXQsXG5cdE9uRGVzdHJveSxcblx0RXZlbnRFbWl0dGVyLFxuXHRUZW1wbGF0ZVJlZixcblx0QWZ0ZXJWaWV3SW5pdCxcblx0Vmlld0NoaWxkLFxuXHRFbGVtZW50UmVmLFxuXHRWaWV3Q2hpbGRyZW4sXG5cdFF1ZXJ5TGlzdCxcblx0QXBwbGljYXRpb25SZWZcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7IE9ic2VydmFibGUsIGlzT2JzZXJ2YWJsZSwgU3Vic2NyaXB0aW9uLCBvZiB9IGZyb20gXCJyeGpzXCI7XG5pbXBvcnQgeyBmaXJzdCB9IGZyb20gXCJyeGpzL29wZXJhdG9yc1wiO1xuXG5pbXBvcnQgeyBJMThuIH0gZnJvbSBcImNhcmJvbi1jb21wb25lbnRzLWFuZ3VsYXIvaTE4blwiO1xuaW1wb3J0IHsgQWJzdHJhY3REcm9wZG93blZpZXcgfSBmcm9tIFwiLi4vYWJzdHJhY3QtZHJvcGRvd24tdmlldy5jbGFzc1wiO1xuaW1wb3J0IHsgTGlzdEl0ZW0gfSBmcm9tIFwiLi4vbGlzdC1pdGVtLmludGVyZmFjZVwiO1xuaW1wb3J0IHsgd2F0Y2hGb2N1c0p1bXAgfSBmcm9tIFwiLi4vZHJvcGRvd250b29sc1wiO1xuaW1wb3J0IHsgU2Nyb2xsQ3VzdG9tRXZlbnQgfSBmcm9tIFwiLi9zY3JvbGwtY3VzdG9tLWV2ZW50LmludGVyZmFjZVwiO1xuXG5cbi8qKlxuICogYGBgaHRtbFxuICogPGNkcy1kcm9wZG93bi1saXN0IFtpdGVtc109XCJsaXN0SXRlbXNcIj48L2Nkcy1kcm9wZG93bi1saXN0PlxuICogYGBgXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBsaXN0SXRlbXMgPSBbXG4gKiBcdHtcbiAqIFx0XHRjb250ZW50OiBcIml0ZW0gb25lXCIsXG4gKiBcdFx0c2VsZWN0ZWQ6IGZhbHNlXG4gKiBcdH0sXG4gKiBcdHtcbiAqIFx0XHRjb250ZW50OiBcIml0ZW0gdHdvXCIsXG4gKiBcdFx0c2VsZWN0ZWQ6IGZhbHNlLFxuICogXHR9LFxuICogXHR7XG4gKiBcdFx0Y29udGVudDogXCJpdGVtIHRocmVlXCIsXG4gKiBcdFx0c2VsZWN0ZWQ6IGZhbHNlXG4gKiBcdH0sXG4gKiBcdHtcbiAqIFx0XHRjb250ZW50OiBcIml0ZW0gZm91clwiLFxuICogXHRcdHNlbGVjdGVkOiBmYWxzZVxuICogXHR9XG4gKiBdO1xuICogYGBgXG4gKi9cbkBDb21wb25lbnQoe1xuXHRzZWxlY3RvcjogXCJjZHMtZHJvcGRvd24tbGlzdCwgaWJtLWRyb3Bkb3duLWxpc3RcIixcblx0dGVtcGxhdGU6IGBcblx0XHQ8dWxcblx0XHRcdCNsaXN0XG5cdFx0XHRbaWRdPVwibGlzdElkXCJcblx0XHRcdHJvbGU9XCJsaXN0Ym94XCJcblx0XHRcdGNsYXNzPVwiY2RzLS1saXN0LWJveF9fbWVudSBjZHMtLW11bHRpLXNlbGVjdFwiXG5cdFx0XHQoc2Nyb2xsKT1cImVtaXRTY3JvbGwoJGV2ZW50KVwiXG5cdFx0XHQoa2V5ZG93bik9XCJuYXZpZ2F0ZUxpc3QoJGV2ZW50KVwiXG5cdFx0XHR0YWJpbmRleD1cIi0xXCJcblx0XHRcdFthdHRyLmFyaWEtbGFiZWxdPVwiYXJpYUxhYmVsXCJcblx0XHRcdFthdHRyLmFyaWEtYWN0aXZlZGVzY2VuZGFudF09XCJoaWdobGlnaHRlZEl0ZW1cIj5cblx0XHRcdDxsaVxuXHRcdFx0XHRyb2xlPVwib3B0aW9uXCJcblx0XHRcdFx0Km5nRm9yPVwibGV0IGl0ZW0gb2YgZGlzcGxheUl0ZW1zOyBsZXQgaSA9IGluZGV4XCJcblx0XHRcdFx0KGNsaWNrKT1cImRvQ2xpY2soJGV2ZW50LCBpdGVtKVwiXG5cdFx0XHRcdGNsYXNzPVwiY2RzLS1saXN0LWJveF9fbWVudS1pdGVtXCJcblx0XHRcdFx0W2F0dHIuYXJpYS1zZWxlY3RlZF09XCJpdGVtLnNlbGVjdGVkXCJcblx0XHRcdFx0W2lkXT1cImdldEl0ZW1JZChpKVwiXG5cdFx0XHRcdFthdHRyLnRpdGxlXT1cIiBzaG93VGl0bGVzID8gaXRlbS5jb250ZW50IDogbnVsbFwiXG5cdFx0XHRcdFthdHRyLmRpc2FibGVkXT1cIml0ZW0uZGlzYWJsZWQgPyB0cnVlIDogbnVsbFwiXG5cdFx0XHRcdFtuZ0NsYXNzXT1cIntcblx0XHRcdFx0XHQnY2RzLS1saXN0LWJveF9fbWVudS1pdGVtLS1hY3RpdmUnOiBpdGVtLnNlbGVjdGVkLFxuXHRcdFx0XHRcdCdjZHMtLWxpc3QtYm94X19tZW51LWl0ZW0tLWhpZ2hsaWdodGVkJzogaGlnaGxpZ2h0ZWRJdGVtID09PSBnZXRJdGVtSWQoaSlcblx0XHRcdFx0fVwiPlxuXHRcdFx0XHQ8ZGl2XG5cdFx0XHRcdFx0I2xpc3RJdGVtXG5cdFx0XHRcdFx0dGFiaW5kZXg9XCItMVwiXG5cdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLWxpc3QtYm94X19tZW51LWl0ZW1fX29wdGlvblwiPlxuXHRcdFx0XHRcdDxkaXZcblx0XHRcdFx0XHRcdCpuZ0lmPVwiIWxpc3RUcGwgJiYgdHlwZSA9PT0gJ211bHRpJ1wiXG5cdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tZm9ybS1pdGVtIGNkcy0tY2hlY2tib3gtd3JhcHBlclwiPlxuXHRcdFx0XHRcdFx0PGxhYmVsXG5cdFx0XHRcdFx0XHRcdFthdHRyLmRhdGEtY29udGFpbmVkLWNoZWNrYm94LXN0YXRlXT1cIml0ZW0uc2VsZWN0ZWRcIlxuXHRcdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tY2hlY2tib3gtbGFiZWxcIj5cblx0XHRcdFx0XHRcdFx0PGlucHV0XG5cdFx0XHRcdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLWNoZWNrYm94XCJcblx0XHRcdFx0XHRcdFx0XHR0eXBlPVwiY2hlY2tib3hcIlxuXHRcdFx0XHRcdFx0XHRcdFtjaGVja2VkXT1cIml0ZW0uc2VsZWN0ZWRcIlxuXHRcdFx0XHRcdFx0XHRcdFtkaXNhYmxlZF09XCJpdGVtLmRpc2FibGVkXCJcblx0XHRcdFx0XHRcdFx0XHR0YWJpbmRleD1cIi0xXCI+XG5cdFx0XHRcdFx0XHRcdDxzcGFuIGNsYXNzPVwiY2RzLS1jaGVja2JveC1hcHBlYXJhbmNlXCI+PC9zcGFuPlxuXHRcdFx0XHRcdFx0XHQ8c3BhbiBjbGFzcz1cImNkcy0tY2hlY2tib3gtbGFiZWwtdGV4dFwiPnt7aXRlbS5jb250ZW50fX08L3NwYW4+XG5cdFx0XHRcdFx0XHQ8L2xhYmVsPlxuXHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhbGlzdFRwbCAmJiB0eXBlID09PSAnc2luZ2xlJ1wiPnt7aXRlbS5jb250ZW50fX08L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0XHQ8c3ZnXG5cdFx0XHRcdFx0XHQqbmdJZj1cIiFsaXN0VHBsICYmIHR5cGUgPT09ICdzaW5nbGUnXCJcblx0XHRcdFx0XHRcdGNkc0ljb249XCJjaGVja21hcmtcIlxuXHRcdFx0XHRcdFx0c2l6ZT1cIjE2XCJcblx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1saXN0LWJveF9fbWVudS1pdGVtX19zZWxlY3RlZC1pY29uXCI+XG5cdFx0XHRcdFx0PC9zdmc+XG5cdFx0XHRcdFx0PG5nLXRlbXBsYXRlXG5cdFx0XHRcdFx0XHQqbmdJZj1cImxpc3RUcGxcIlxuXHRcdFx0XHRcdFx0W25nVGVtcGxhdGVPdXRsZXRDb250ZXh0XT1cIntpdGVtOiBpdGVtfVwiXG5cdFx0XHRcdFx0XHRbbmdUZW1wbGF0ZU91dGxldF09XCJsaXN0VHBsXCI+XG5cdFx0XHRcdFx0PC9uZy10ZW1wbGF0ZT5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHQ8L2xpPlxuXHRcdDwvdWw+YCxcblx0cHJvdmlkZXJzOiBbXG5cdFx0e1xuXHRcdFx0cHJvdmlkZTogQWJzdHJhY3REcm9wZG93blZpZXcsXG5cdFx0XHR1c2VFeGlzdGluZzogRHJvcGRvd25MaXN0XG5cdFx0fVxuXHRdXG59KVxuZXhwb3J0IGNsYXNzIERyb3Bkb3duTGlzdCBpbXBsZW1lbnRzIEFic3RyYWN0RHJvcGRvd25WaWV3LCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xuXHRzdGF0aWMgbGlzdENvdW50ID0gMDtcblx0QElucHV0KCkgYXJpYUxhYmVsID0gdGhpcy5pMThuLmdldCgpLkRST1BET1dOX0xJU1QuTEFCRUw7XG5cdC8qKlxuXHQgKiBUaGUgbGlzdCBpdGVtcyBiZWxvbmdpbmcgdG8gdGhlIGBEcm9wZG93bkxpc3RgLlxuXHQgKi9cblx0QElucHV0KCkgc2V0IGl0ZW1zKHZhbHVlOiBBcnJheTxMaXN0SXRlbT4gfCBPYnNlcnZhYmxlPEFycmF5PExpc3RJdGVtPj4pIHtcblx0XHRpZiAoaXNPYnNlcnZhYmxlKHZhbHVlKSkge1xuXHRcdFx0aWYgKHRoaXMuX2l0ZW1zU3Vic2NyaXB0aW9uKSB7XG5cdFx0XHRcdHRoaXMuX2l0ZW1zU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLl9pdGVtc1JlYWR5ID0gbmV3IE9ic2VydmFibGU8Ym9vbGVhbj4oKG9ic2VydmVyKSA9PiB7XG5cdFx0XHRcdHRoaXMuX2l0ZW1zU3Vic2NyaXB0aW9uID0gdmFsdWUuc3Vic2NyaWJlKHYgPT4ge1xuXHRcdFx0XHRcdHRoaXMudXBkYXRlTGlzdCh2KTtcblx0XHRcdFx0XHRvYnNlcnZlci5uZXh0KHRydWUpO1xuXHRcdFx0XHRcdG9ic2VydmVyLmNvbXBsZXRlKCk7XG5cdFx0XHRcdH0pO1xuXHRcdFx0fSk7XG5cdFx0XHR0aGlzLm9uSXRlbXNSZWFkeShudWxsKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy51cGRhdGVMaXN0KHZhbHVlKTtcblx0XHR9XG5cdFx0dGhpcy5fb3JpZ2luYWxJdGVtcyA9IHZhbHVlO1xuXHR9XG5cblx0Z2V0IGl0ZW1zKCk6IEFycmF5PExpc3RJdGVtPiB8IE9ic2VydmFibGU8QXJyYXk8TGlzdEl0ZW0+PiB7XG5cdFx0cmV0dXJuIHRoaXMuX29yaWdpbmFsSXRlbXM7XG5cdH1cblx0LyoqXG5cdCAqIFRlbXBsYXRlIHRvIGJpbmQgdG8gaXRlbXMgaW4gdGhlIGBEcm9wZG93bkxpc3RgIChvcHRpb25hbCkuXG5cdCAqL1xuXHRASW5wdXQoKSBsaXN0VHBsOiBzdHJpbmcgfCBUZW1wbGF0ZVJlZjxhbnk+ID0gbnVsbDtcblx0LyoqXG5cdCAqIEV2ZW50IHRvIGVtaXQgc2VsZWN0aW9uIG9mIGEgbGlzdCBpdGVtIHdpdGhpbiB0aGUgYERyb3Bkb3duTGlzdGAuXG5cdCAqL1xuXHRAT3V0cHV0KCkgc2VsZWN0OiBFdmVudEVtaXR0ZXI8eyBpdGVtOiBMaXN0SXRlbSwgaXNVcGRhdGU/OiBib29sZWFuIH0gfCBMaXN0SXRlbVtdPiA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblx0LyoqXG5cdCAqIEV2ZW50IHRvIGVtaXQgc2Nyb2xsIGV2ZW50IG9mIGEgbGlzdCB3aXRoaW4gdGhlIGBEcm9wZG93bkxpc3RgLlxuXHQgKi9cblx0QE91dHB1dCgpIHNjcm9sbDogRXZlbnRFbWl0dGVyPFNjcm9sbEN1c3RvbUV2ZW50PiA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblx0LyoqXG5cdCAqIEV2ZW50IHRvIHN1Z2dlc3QgYSBibHVyIG9uIHRoZSB2aWV3LlxuXHQgKiBFbWl0cyBfYWZ0ZXJfIHRoZSBmaXJzdC9sYXN0IGl0ZW0gaGFzIGJlZW4gZm9jdXNlZC5cblx0ICogZXguXG5cdCAqIEFycm93VXAgLT4gZm9jdXMgZmlyc3QgaXRlbVxuXHQgKiBBcnJvd1VwIC0+IGVtaXQgZXZlbnRcblx0ICpcblx0ICogV2hlbiB0aGlzIGV2ZW50IGZpcmVzIGZvY3VzIHNob3VsZCBiZSBwbGFjZWQgb24gc29tZSBlbGVtZW50IG91dHNpZGUgb2YgdGhlIGxpc3QgLSBibHVycmluZyB0aGUgbGlzdCBhcyBhIHJlc3VsdFxuXHQgKi9cblx0QE91dHB1dCgpIGJsdXJJbnRlbnQgPSBuZXcgRXZlbnRFbWl0dGVyPFwidG9wXCIgfCBcImJvdHRvbVwiPigpO1xuXHQvKipcblx0ICogTWFpbnRhaW5zIGEgcmVmZXJlbmNlIHRvIHRoZSB2aWV3IERPTSBlbGVtZW50IGZvciB0aGUgdW5vcmRlcmVkIGxpc3Qgb2YgaXRlbXMgd2l0aGluIHRoZSBgRHJvcGRvd25MaXN0YC5cblx0ICovXG5cdEBWaWV3Q2hpbGQoXCJsaXN0XCIsIHsgc3RhdGljOiB0cnVlIH0pIGxpc3Q6IEVsZW1lbnRSZWY7XG5cdC8qKlxuXHQgKiBEZWZpbmVzIHdoZXRoZXIgb3Igbm90IHRoZSBgRHJvcGRvd25MaXN0YCBzdXBwb3J0cyBzZWxlY3RpbmcgbXVsdGlwbGUgaXRlbXMgYXMgb3Bwb3NlZCB0byBzaW5nbGVcblx0ICogaXRlbSBzZWxlY3Rpb24uXG5cdCAqL1xuXHRASW5wdXQoKSB0eXBlOiBcInNpbmdsZVwiIHwgXCJtdWx0aVwiID0gXCJzaW5nbGVcIjtcblxuXHQvKipcblx0ICogRGVmaW5lcyB3aGV0aGVyIHRvIHNob3cgdGl0bGUgYXR0cmlidXRlIG9yIG5vdFxuXHQgKi9cblx0QElucHV0KCkgc2hvd1RpdGxlcyA9IHRydWU7XG5cblx0LyoqXG5cdCAqIERlZmluZXMgdGhlIHJlbmRlcmluZyBzaXplIG9mIHRoZSBgRHJvcGRvd25MaXN0YCBpbnB1dCBjb21wb25lbnQuXG5cdCAqL1xuXHRwdWJsaWMgc2l6ZTogXCJzbVwiIHwgXCJtZFwiIHwgXCJsZ1wiID0gXCJtZFwiO1xuXHRwdWJsaWMgbGlzdElkID0gYGxpc3Rib3gtJHtEcm9wZG93bkxpc3QubGlzdENvdW50Kyt9YDtcblx0cHVibGljIGhpZ2hsaWdodGVkSXRlbSA9IG51bGw7XG5cdC8qKlxuXHQgKiBIb2xkcyB0aGUgbGlzdCBvZiBpdGVtcyB0aGF0IHdpbGwgYmUgZGlzcGxheWVkIGluIHRoZSBgRHJvcGRvd25MaXN0YC5cblx0ICogSXQgZGlmZmVycyBmcm9tIHRoZSB0aGUgY29tcGxldGUgc2V0IG9mIGl0ZW1zIHdoZW4gZmlsdGVyaW5nIGlzIHVzZWQgKGJ1dFxuXHQgKiBpdCBpcyBhbHdheXMgYSBzdWJzZXQgb2YgdGhlIHRvdGFsIGl0ZW1zIGluIGBEcm9wZG93bkxpc3RgKS5cblx0ICovXG5cdHB1YmxpYyBkaXNwbGF5SXRlbXM6IEFycmF5PExpc3RJdGVtPiA9IFtdO1xuXHQvKipcblx0ICogTWFpbnRhaW5zIHRoZSBpbmRleCBmb3IgdGhlIHNlbGVjdGVkIGl0ZW0gd2l0aGluIHRoZSBgRHJvcGRvd25MaXN0YC5cblx0ICovXG5cdHByb3RlY3RlZCBpbmRleCA9IC0xO1xuXHQvKipcblx0ICogQW4gYXJyYXkgaG9sZGluZyB0aGUgSFRNTCBsaXN0IGVsZW1lbnRzIGluIHRoZSB2aWV3LlxuXHQgKi9cblx0QFZpZXdDaGlsZHJlbihcImxpc3RJdGVtXCIpIHByb3RlY3RlZCBsaXN0RWxlbWVudExpc3Q6IFF1ZXJ5TGlzdDxFbGVtZW50UmVmPjtcblx0LyoqXG5cdCAqIE9ic2VydmFibGUgYm91bmQgdG8ga2V5ZG93biBldmVudHMgdG8gY29udHJvbCBmaWx0ZXJpbmcuXG5cdCAqL1xuXHRwcm90ZWN0ZWQgZm9jdXNKdW1wO1xuXHQvKipcblx0ICogVHJhY2tzIHRoZSBjdXJyZW50IChpZiBhbnkpIHN1YnNjcmlwdGlvbiB0byB0aGUgaXRlbXMgb2JzZXJ2YWJsZSBzbyB3ZSBjYW4gY2xlYW4gdXAgd2hlbiB0aGUgaW5wdXQgaXMgdXBkYXRlZC5cblx0ICovXG5cdHByb3RlY3RlZCBfaXRlbXNTdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbjtcblx0LyoqXG5cdCAqIFVzZWQgdG8gcmV0YWluIHRoZSBvcmlnaW5hbCBpdGVtcyBwYXNzZWQgdG8gdGhlIHNldHRlci5cblx0ICovXG5cdHByb3RlY3RlZCBfb3JpZ2luYWxJdGVtczogQXJyYXk8TGlzdEl0ZW0+IHwgT2JzZXJ2YWJsZTxBcnJheTxMaXN0SXRlbT4+O1xuXHQvKipcblx0ICogVXNlZnVsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpdGVtcywgc2hvdWxkIGJlIGFjY2Vzc2VkIHZpYSBgZ2V0TGlzdEl0ZW1zYC5cblx0ICovXG5cdHByb3RlY3RlZCBfaXRlbXM6IEFycmF5PExpc3RJdGVtPiA9IFtdO1xuXHQvKipcblx0ICogVXNlZCB0byB3YWl0IGZvciBpdGVtcyBpbiBjYXNlIHRoZXkgYXJlIHBhc3NlZCB0aHJvdWdoIGFuIG9ic2VydmFibGUuXG5cdCAqL1xuXHRwcm90ZWN0ZWQgX2l0ZW1zUmVhZHk6IE9ic2VydmFibGU8Ym9vbGVhbj47XG5cblx0LyoqXG5cdCAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYERyb3Bkb3duTGlzdGAuXG5cdCAqL1xuXHRjb25zdHJ1Y3RvcihwdWJsaWMgZWxlbWVudFJlZjogRWxlbWVudFJlZiwgcHJvdGVjdGVkIGkxOG46IEkxOG4sIHByb3RlY3RlZCBhcHBSZWY6IEFwcGxpY2F0aW9uUmVmKSB7fVxuXG5cdC8qKlxuXHQgKiBSZXRyaWV2ZXMgYXJyYXkgb2YgbGlzdCBpdGVtcyBhbmQgaW5kZXggb2YgdGhlIHNlbGVjdGVkIGl0ZW0gYWZ0ZXIgdmlldyBoYXMgcmVuZGVyZWQuXG5cdCAqIEFkZGl0aW9uYWxseSwgYW55IE9ic2VydmFibGVzIGZvciB0aGUgYERyb3Bkb3duTGlzdGAgYXJlIGluaXRpYWxpemVkLlxuXHQgKi9cblx0bmdBZnRlclZpZXdJbml0KCkge1xuXHRcdHRoaXMuaW5kZXggPSB0aGlzLmdldExpc3RJdGVtcygpLmZpbmRJbmRleChpdGVtID0+IGl0ZW0uc2VsZWN0ZWQpO1xuXHRcdHRoaXMuc2V0dXBGb2N1c09ic2VydmFibGUoKTtcblx0XHRzZXRUaW1lb3V0KCgpID0+IHtcblx0XHRcdHRoaXMuZG9FbWl0U2VsZWN0KHRydWUpO1xuXHRcdH0pO1xuXHR9XG5cblx0LyoqXG5cdCAqIFJlbW92ZXMgYW55IE9ic2VydmFibGVzIG9uIGRlc3RydWN0aW9uIG9mIHRoZSBjb21wb25lbnQuXG5cdCAqL1xuXHRuZ09uRGVzdHJveSgpIHtcblx0XHRpZiAodGhpcy5mb2N1c0p1bXApIHtcblx0XHRcdHRoaXMuZm9jdXNKdW1wLnVuc3Vic2NyaWJlKCk7XG5cdFx0fVxuXHRcdGlmICh0aGlzLl9pdGVtc1N1YnNjcmlwdGlvbikge1xuXHRcdFx0dGhpcy5faXRlbXNTdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcblx0XHR9XG5cdH1cblxuXHRkb0VtaXRTZWxlY3QoaXNVcGRhdGUgPSB0cnVlKSB7XG5cdFx0aWYgKHRoaXMudHlwZSA9PT0gXCJzaW5nbGVcIikge1xuXHRcdFx0dGhpcy5zZWxlY3QuZW1pdCh7IGl0ZW06IHRoaXMuX2l0ZW1zLmZpbmQoaXRlbSA9PiBpdGVtLnNlbGVjdGVkKSwgaXNVcGRhdGU6IGlzVXBkYXRlIH0pO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBhYnVzZSBqYXZhc2NyaXB0cyBvYmplY3QgbXV0YWJpbGl0eSB1bnRpbCB3ZSBjYW4gYnJlYWsgdGhlIEFQSSBhbmQgc3dpdGNoIHRvXG5cdFx0XHQvLyB7IGl0ZW1zOiBbXSwgaXNVcGRhdGU6IHRydWUgfVxuXHRcdFx0Y29uc3Qgc2VsZWN0ZWQgPSB0aGlzLmdldF