gentics-ui-core
Version:
This is the common core framework for the Gentics CMS and Mesh UI, and other Angular applications.
470 lines • 68.6 kB
JavaScript
import { ChangeDetectorRef, Component, ContentChildren, ElementRef, EventEmitter, forwardRef, Input, Output, QueryList, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { coerceToBoolean } from '../../common/coerce-to-boolean';
import { KeyCode } from '../../common/keycodes';
import { DropdownContent } from '../dropdown-list/dropdown-content.component';
import { DropdownList } from '../dropdown-list/dropdown-list.component';
import { SelectOption, SelectOptionGroup } from './option.component';
import * as i0 from "@angular/core";
import * as i1 from "../dropdown-list/dropdown-list.component";
import * as i2 from "../dropdown-list/dropdown-content.component";
import * as i3 from "../checkbox/checkbox.component";
import * as i4 from "../button/button.component";
import * as i5 from "../dropdown-list/dropdown-trigger.directive";
import * as i6 from "@angular/common";
import * as i7 from "../icon/icon.directive";
const GTX_SELECT_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Select),
multi: true
};
/**
* A Select form control which works with any kind of value - as opposed to the native HTML `<select>` which only works
* with strings. The Select control depends on the [`<gtx-overlay-host>`](#/overlay-host) being present in the app.
*
* ```html
* <gtx-select label="Choose an option" [(ngModel)]="selectVal">
* <gtx-option *ngFor="let item of options"
* [value]="item"
* [disabled]="item.disabled">{{ item.label }}</gtx-option>
* </gtx-select>
* ```
*
*/
export class Select {
constructor(changeDetector, elementRef) {
this.changeDetector = changeDetector;
this.elementRef = elementRef;
/**
* Sets the select box to be auto-focused. Handled by `AutofocusDirective`.
*/
this.autofocus = false;
/**
* When set to true, allows multiple options to be selected. In this case, the input value should be
* an array of strings; events will emit an array of strings.
*/
this.multiple = false;
/**
* Sets the required state.
*/
this.required = false;
/**
* Placeholder which is shown if nothing is selected.
*/
this.placeholder = '';
/**
* Blur event.
*/
this.blur = new EventEmitter();
/**
* Focus event.
*/
this.focus = new EventEmitter();
/**
* Change event.
*/
this.change = new EventEmitter();
// An array of abstracted containers for options, which allows us to treat options and groups in a
// consistent way.
this.optionGroups = [];
this.subscriptions = [];
this.selectedOptions = [];
this.viewValue = '';
// Keeps track of the selected option. Two dimensional because options may be nested inside groups. The first
// value is the index of the group (-1 is the default "no group" group), and the second number is the index
// of the option within that group.
this.selectedIndex = [0, -1];
this._clearable = false;
this._disabled = false;
this.preventDeselect = false;
// ValueAccessor members
this.onChange = () => { };
this.onTouched = () => { };
}
/** If true the clear button is displayed, which allows the user to clear the selection. */
get clearable() {
return this._clearable;
}
set clearable(val) {
this._clearable = coerceToBoolean(val);
}
/**
* Sets the disabled state.
*/
get disabled() {
return this._disabled;
}
set disabled(value) {
this._disabled = coerceToBoolean(value);
}
ngAfterViewInit() {
// Update the value if there are any changes to the options
this.subscriptions.push(this._selectOptions.changes.subscribe(() => {
this.writeValue(this.value);
this.optionGroups = this.buildOptionGroups();
this.selectedOptions = this.getInitiallySelectedOptions();
}));
this.elementRef.nativeElement.querySelector('gtx-dropdown-list')
.addEventListener('keydown', this.handleKeydown.bind(this));
}
ngAfterContentInit() {
this.optionGroups = this.buildOptionGroups();
this.selectedOptions = this.getInitiallySelectedOptions();
this.updateViewValue();
}
ngOnDestroy() {
this.subscriptions.forEach(s => s.unsubscribe());
}
/**
* Event handler for when one of the Materialize-generated LI elements is clicked.
*/
selectItem(groupIndex, optionIndex) {
const option = this.optionGroups[groupIndex] && this.optionGroups[groupIndex].options[optionIndex];
if (!this.optionGroups[groupIndex].disabled && option && !option.disabled) {
this.toggleSelectedOption(option);
const selectedValues = this.selectedOptions.map(o => o.value);
this.value = this.multiple ? selectedValues : selectedValues[0];
this.onChange();
this.change.emit(this.value);
this.updateViewValue();
this.scrollToSelectedOption();
}
}
inputBlur(e) {
e.stopPropagation();
this.onTouched();
this.blur.emit(this.value);
}
/**
* Select the initial value when the dropdown is opened.
*/
dropdownOpened() {
if (0 < this.selectedOptions.length) {
this.preventDeselect = true;
const selected = this.selectedOptions[0];
this.selectedIndex = this.getIndexFromSelectOption(selected);
setTimeout(() => {
this.scrollToSelectedOption();
this.preventDeselect = false;
}, 100);
}
}
/**
* Handle keydown events to enable keyboard navigation and selection of options.
*/
handleKeydown(event) {
if (event.ctrlKey || event.altKey || event.metaKey) {
return;
}
const keyCode = event.keyCode;
switch (keyCode) {
case KeyCode.UpArrow:
this.updateSelectedIndex(this.getPreviousIndex(this.selectedIndex));
break;
case KeyCode.DownArrow:
this.updateSelectedIndex(this.getNextIndex(this.selectedIndex));
break;
case KeyCode.PageUp:
case KeyCode.Home:
this.updateSelectedIndex(this.getFirstIndex());
break;
case KeyCode.PageDown:
case KeyCode.End:
this.updateSelectedIndex(this.getLastIndex());
break;
case KeyCode.Enter:
case KeyCode.Space:
if (!this.dropdownList.isOpen) {
this.dropdownList.openDropdown();
}
else {
this.selectItem(this.selectedIndex[0], this.selectedIndex[1]);
if (!this.multiple) {
this.dropdownList.closeDropdown();
}
}
break;
default:
// Other keys are treated as if the user is trying to jump to an option by character
const indexOfMatch = this.searchByKey(event.key);
if (indexOfMatch) {
this.updateSelectedIndex(indexOfMatch);
}
}
}
isSelected(option) {
return -1 < this.selectedOptions.indexOf(option);
}
deselect() {
if (!this.preventDeselect) {
this.selectedIndex = [0, -1];
}
}
// ValueAccessor members
writeValue(value) {
this.value = value;
if (this._selectOptions) {
// select any options matching the initial value
this.selectedOptions = [];
const optionsArray = this._selectOptions.toArray();
if (this.multiple && this.value instanceof Array) {
optionsArray.forEach(o => {
if (-1 < this.value.indexOf(o.value)) {
this.selectedOptions.push(o);
}
});
}
else {
this.selectedOptions = optionsArray.filter(o => this.value === o.value);
}
this.updateViewValue();
}
}
registerOnChange(fn) {
this.onChange = () => {
fn(this.value);
};
}
registerOnTouched(fn) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
this._disabled = isDisabled;
this.changeDetector.markForCheck();
}
/** Clears the selected value and emits `null` with the `change` event. */
clearSelection() {
this.selectedOptions = [];
this.value = null;
this.change.emit(this.value);
this.onChange();
this.updateViewValue();
}
/**
* Given a SelectOption, returns the position in the 2D selectedIndex array.
*/
getIndexFromSelectOption(selected) {
if (selected) {
let selectedGroup = 0;
let selectedOption = 0;
for (let i = 0; i < this.optionGroups.length; i++) {
const group = this.optionGroups[i];
selectedGroup = i;
for (let j = 0; j < group.options.length; j++) {
const option = group.options[j];
selectedOption = j;
if (option === selected) {
return [selectedGroup, selectedOption];
}
}
}
}
else {
return [0, 0];
}
}
/**
* Once the contents have been compiled, we can build up the optionGroups array, grouping options into
* a "default" group, i.e. the group of options which are not children of a <gtx-optgroup>, and then any
* other groups as specified by optgroups.
*/
buildOptionGroups() {
const groups = this._selectOptionGroups.map(g => {
return {
get options() { return g.options; },
get label() { return g.label; },
get disabled() { return g.disabled; },
isDefaultGroup: false
};
});
if (this._selectOptions.length) {
groups.unshift({
options: this._selectOptions.toArray(),
label: '',
isDefaultGroup: true,
disabled: false
});
}
return groups;
}
/**
* Select any options which match the value passed in via the `value` attribute.
*/
getInitiallySelectedOptions() {
let selectedOptions = [];
const flatOptionsList = this.optionGroups.reduce((options, group) => options.concat(group.options), []);
if (this.value !== undefined) {
if (this.multiple) {
if (this.value instanceof Array) {
selectedOptions = flatOptionsList.filter(o => -1 < this.value.indexOf(o.value));
}
}
else {
selectedOptions = flatOptionsList.filter(o => this.value === o.value) || [];
return flatOptionsList.filter(o => this.value === o.value) || [];
}
}
return selectedOptions;
}
/**
* Toggle the selection of the given SelectOption, taking into account whether this is a multiple
* select.
*/
toggleSelectedOption(option) {
if (!this.multiple) {
this.selectedOptions = [];
}
let index = this.selectedOptions.indexOf(option);
if (-1 < index) {
// de-select the existing option
this.selectedOptions.splice(index, 1);
}
else {
this.selectedOptions.push(option);
}
}
updateViewValue() {
this.viewValue = this.selectedOptions.map(o => o.viewValue).join(', ');
this.changeDetector.markForCheck();
}
/**
* When a list of options is too long, there will be a scroll bar. This method ensures that the currently-selected
* options is scrolled into view in the options list.
*/
scrollToSelectedOption() {
setTimeout(() => {
const container = this.dropdownContent.elementRef.nativeElement;
const selectedItem = container.querySelector('li.selected');
if (selectedItem) {
const belowContainer = container.offsetHeight + container.scrollTop < selectedItem.offsetTop + selectedItem.offsetHeight;
const aboveContainer = selectedItem.offsetTop < container.scrollTop;
if (belowContainer) {
container.scrollTop = selectedItem.offsetTop + selectedItem.offsetHeight - container.offsetHeight;
}
if (aboveContainer) {
container.scrollTop = selectedItem.offsetTop;
}
}
});
}
/**
* Searches through the available options and locates the next option with a viewValue whose first character
* matches the character passed in. Useful for jumping to options quickly by typing the first letter of the
* option view value.
*/
searchByKey(key) {
const keyUpperCase = key.toLocaleUpperCase();
const totalOptionCount = this.optionGroups.reduce((total, group) => total + group.options.length, 0);
let currentIndex = this.selectedIndex.slice();
for (let counter = 0; counter < totalOptionCount; counter++) {
currentIndex = this.getNextIndex(currentIndex);
const option = this.optionGroups[currentIndex[0]].options[currentIndex[1]];
const firstLetterUppercase = option.viewValue.charAt(0).toLocaleUpperCase();
if (firstLetterUppercase === keyUpperCase) {
return currentIndex;
}
}
}
getFirstIndex() {
return [0, 0];
}
getLastIndex() {
const lastGroupIndex = this.optionGroups.length - 1;
return [lastGroupIndex, this.optionGroups[lastGroupIndex].options.length - 1];
}
getNextIndex(currentIndex) {
let nextIndex = currentIndex.slice();
const isLastGroup = currentIndex[0] === this.optionGroups.length - 1;
const isLastOptionInGroup = currentIndex[1] === this.optionGroups[currentIndex[0]].options.length - 1;
if (isLastOptionInGroup) {
if (isLastGroup) {
nextIndex = this.getFirstIndex();
}
else {
nextIndex[0]++;
nextIndex[1] = 0;
}
}
else {
nextIndex[1]++;
}
return nextIndex;
}
getPreviousIndex(currentIndex) {
let nextIndex = currentIndex.slice();
if (currentIndex[0] <= 0) {
if (0 < currentIndex[1]) {
nextIndex[1]--;
}
else {
nextIndex = this.getLastIndex();
}
}
else {
if (0 < currentIndex[1]) {
nextIndex[1]--;
}
else {
nextIndex[0]--;
nextIndex[1] = this.optionGroups[currentIndex[0]].options.length - 1;
}
}
return nextIndex;
}
/**
* Sets the `selectedOptions` array to contain the single option at the selectedIndex.
*/
updateSelectedIndex(index) {
this.selectedIndex = index;
const options = this.optionGroups[index[0]].options;
if (options && 0 <= index[1] && index[1] < options.length) {
if (!this.multiple) {
this.selectItem(index[0], index[1]);
}
else {
this.scrollToSelectedOption();
}
}
}
}
/** @nocollapse */ Select.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Select, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
/** @nocollapse */ Select.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.8", type: Select, selector: "gtx-select", inputs: { autofocus: "autofocus", clearable: "clearable", disabled: "disabled", multiple: "multiple", required: "required", value: "value", placeholder: "placeholder", label: "label" }, outputs: { blur: "blur", focus: "focus", change: "change" }, providers: [GTX_SELECT_VALUE_ACCESSOR], queries: [{ propertyName: "_selectOptions", predicate: SelectOption }, { propertyName: "_selectOptionGroups", predicate: SelectOptionGroup }], viewQueries: [{ propertyName: "dropdownList", first: true, predicate: DropdownList, descendants: true, static: true }, { propertyName: "dropdownContent", first: true, predicate: DropdownContent, descendants: true, static: true }], ngImport: i0, template: "<gtx-dropdown-list width=\"expand\"\n belowTrigger=\"false\"\n [sticky]=\"multiple\"\n [disabled]=\"disabled\"\n (open)=\"dropdownOpened()\"\n [class.clearable]=\"_clearable\">\n\n <gtx-dropdown-trigger [class.with-label]=\"label != null\">\n <div class=\"view-value select-input\"\n [attr.tabindex]=\"disabled ? null : 0\"\n [attr.disabled]=\"disabled ? true : null\"\n (blur)=\"inputBlur($event)\"\n #viewValueContainer>\n <div *ngIf=\"selectedOptions.length > 0\">{{ viewValue }}</div>\n <div class=\"placeholder\" *ngIf=\"placeholder && selectedOptions.length == 0\">{{ placeholder }}</div>\n <div *ngIf=\"!placeholder && selectedOptions.length == 0\"></div>\n <icon>arrow_drop_down</icon>\n </div><label *ngIf=\"label != null\" (click)=\"viewValueContainer.focus()\">{{ label }}</label>\n </gtx-dropdown-trigger>\n\n <gtx-dropdown-content (keydown)=\"handleKeydown($event)\">\n <ul class=\"select-options\"\n (click)=\"viewValueContainer.focus()\"\n (mouseover)=\"deselect()\">\n <ng-template ngFor [ngForOf]=\"optionGroups\" let-group let-groupIndex=\"index\">\n <li *ngIf=\"!group.isDefaultGroup\"\n class=\"group-label\">{{ group.label }}</li>\n <li *ngFor=\"let option of group.options; let optionIndex = index\"\n [class.disabled]=\"group.disabled || option.disabled\"\n [class.selected]=\"selectedIndex[0] === groupIndex && selectedIndex[1] === optionIndex\"\n class=\"select-option\"\n (click)=\"selectItem(groupIndex, optionIndex)\">\n <gtx-checkbox *ngIf=\"multiple\"\n [checked]=\"isSelected(option)\"\n (change)=\"selectItem(groupIndex, optionIndex)\"></gtx-checkbox>\n <icon *ngIf=\"option.icon\" class=\"material-icons\">{{option.icon}}</icon>\n {{ option.viewValue }}\n </li>\n </ng-template>\n </ul>\n </gtx-dropdown-content>\n\n</gtx-dropdown-list>\n\n<gtx-button icon\n class=\"clear-button\"\n *ngIf=\"_clearable\"\n type=\"secondary\"\n [disabled]=\"_disabled\"\n (click)=\"!_disabled && clearSelection()\">\n <icon>clear</icon>\n</gtx-button>\n", components: [{ type: i1.DropdownList, selector: "gtx-dropdown-list", inputs: ["align", "width", "belowTrigger", "sticky", "closeOnEscape", "disabled"], outputs: ["open", "close"] }, { type: i2.DropdownContent, selector: "gtx-dropdown-content" }, { type: i3.Checkbox, selector: "gtx-checkbox", inputs: ["autofocus", "checked", "indeterminate", "disabled", "id", "label", "name", "required", "value"], outputs: ["blur", "focus", "change"] }, { type: i4.Button, selector: "gtx-button", inputs: ["autofocus", "size", "type", "flat", "icon", "disabled", "submit"] }], directives: [{ type: i5.DropdownTriggerDirective, selector: "gtx-dropdown-trigger" }, { type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i7.Icon, selector: "icon" }, { type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Select, decorators: [{
type: Component,
args: [{ selector: 'gtx-select', providers: [GTX_SELECT_VALUE_ACCESSOR], template: "<gtx-dropdown-list width=\"expand\"\n belowTrigger=\"false\"\n [sticky]=\"multiple\"\n [disabled]=\"disabled\"\n (open)=\"dropdownOpened()\"\n [class.clearable]=\"_clearable\">\n\n <gtx-dropdown-trigger [class.with-label]=\"label != null\">\n <div class=\"view-value select-input\"\n [attr.tabindex]=\"disabled ? null : 0\"\n [attr.disabled]=\"disabled ? true : null\"\n (blur)=\"inputBlur($event)\"\n #viewValueContainer>\n <div *ngIf=\"selectedOptions.length > 0\">{{ viewValue }}</div>\n <div class=\"placeholder\" *ngIf=\"placeholder && selectedOptions.length == 0\">{{ placeholder }}</div>\n <div *ngIf=\"!placeholder && selectedOptions.length == 0\"></div>\n <icon>arrow_drop_down</icon>\n </div><label *ngIf=\"label != null\" (click)=\"viewValueContainer.focus()\">{{ label }}</label>\n </gtx-dropdown-trigger>\n\n <gtx-dropdown-content (keydown)=\"handleKeydown($event)\">\n <ul class=\"select-options\"\n (click)=\"viewValueContainer.focus()\"\n (mouseover)=\"deselect()\">\n <ng-template ngFor [ngForOf]=\"optionGroups\" let-group let-groupIndex=\"index\">\n <li *ngIf=\"!group.isDefaultGroup\"\n class=\"group-label\">{{ group.label }}</li>\n <li *ngFor=\"let option of group.options; let optionIndex = index\"\n [class.disabled]=\"group.disabled || option.disabled\"\n [class.selected]=\"selectedIndex[0] === groupIndex && selectedIndex[1] === optionIndex\"\n class=\"select-option\"\n (click)=\"selectItem(groupIndex, optionIndex)\">\n <gtx-checkbox *ngIf=\"multiple\"\n [checked]=\"isSelected(option)\"\n (change)=\"selectItem(groupIndex, optionIndex)\"></gtx-checkbox>\n <icon *ngIf=\"option.icon\" class=\"material-icons\">{{option.icon}}</icon>\n {{ option.viewValue }}\n </li>\n </ng-template>\n </ul>\n </gtx-dropdown-content>\n\n</gtx-dropdown-list>\n\n<gtx-button icon\n class=\"clear-button\"\n *ngIf=\"_clearable\"\n type=\"secondary\"\n [disabled]=\"_disabled\"\n (click)=\"!_disabled && clearSelection()\">\n <icon>clear</icon>\n</gtx-button>\n" }]
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, propDecorators: { autofocus: [{
type: Input
}], clearable: [{
type: Input
}], disabled: [{
type: Input
}], multiple: [{
type: Input
}], required: [{
type: Input
}], value: [{
type: Input
}], placeholder: [{
type: Input
}], label: [{
type: Input
}], blur: [{
type: Output
}], focus: [{
type: Output
}], change: [{
type: Output
}], dropdownList: [{
type: ViewChild,
args: [DropdownList, { static: true }]
}], dropdownContent: [{
type: ViewChild,
args: [DropdownContent, { static: true }]
}], _selectOptions: [{
type: ContentChildren,
args: [SelectOption, { descendants: false }]
}], _selectOptionGroups: [{
type: ContentChildren,
args: [SelectOptionGroup, { descendants: false }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VsZWN0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3NlbGVjdC9zZWxlY3QuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvc2VsZWN0L3NlbGVjdC50cGwuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0gsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxlQUFlLEVBQ2YsVUFBVSxFQUNWLFlBQVksRUFDWixVQUFVLEVBQ1YsS0FBSyxFQUNMLE1BQU0sRUFDTixTQUFTLEVBQ1QsU0FBUyxFQUNaLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBdUIsaUJBQWlCLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUd2RSxPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0sZ0NBQWdDLENBQUM7QUFDL0QsT0FBTyxFQUFDLE9BQU8sRUFBQyxNQUFNLHVCQUF1QixDQUFDO0FBQzlDLE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSw2Q0FBNkMsQ0FBQztBQUM1RSxPQUFPLEVBQUMsWUFBWSxFQUFDLE1BQU0sMENBQTBDLENBQUM7QUFDdEUsT0FBTyxFQUFDLFlBQVksRUFBRSxpQkFBaUIsRUFBQyxNQUFNLG9CQUFvQixDQUFDOzs7Ozs7Ozs7QUFFbkUsTUFBTSx5QkFBeUIsR0FBRztJQUM5QixPQUFPLEVBQUUsaUJBQWlCO0lBQzFCLFdBQVcsRUFBRSxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDO0lBQ3JDLEtBQUssRUFBRSxJQUFJO0NBQ2QsQ0FBQztBQVdGOzs7Ozs7Ozs7Ozs7R0FZRztBQU1ILE1BQU0sT0FBTyxNQUFNO0lBMEZmLFlBQW9CLGNBQWlDLEVBQ2pDLFVBQXNCO1FBRHRCLG1CQUFjLEdBQWQsY0FBYyxDQUFtQjtRQUNqQyxlQUFVLEdBQVYsVUFBVSxDQUFZO1FBMUYxQzs7V0FFRztRQUNNLGNBQVMsR0FB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ZixcbiAgICBDb21wb25lbnQsXG4gICAgQ29udGVudENoaWxkcmVuLFxuICAgIEVsZW1lbnRSZWYsXG4gICAgRXZlbnRFbWl0dGVyLFxuICAgIGZvcndhcmRSZWYsXG4gICAgSW5wdXQsXG4gICAgT3V0cHV0LFxuICAgIFF1ZXJ5TGlzdCxcbiAgICBWaWV3Q2hpbGRcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge0NvbnRyb2xWYWx1ZUFjY2Vzc29yLCBOR19WQUxVRV9BQ0NFU1NPUn0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHtTdWJzY3JpcHRpb259IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQge2NvZXJjZVRvQm9vbGVhbn0gZnJvbSAnLi4vLi4vY29tbW9uL2NvZXJjZS10by1ib29sZWFuJztcbmltcG9ydCB7S2V5Q29kZX0gZnJvbSAnLi4vLi4vY29tbW9uL2tleWNvZGVzJztcbmltcG9ydCB7RHJvcGRvd25Db250ZW50fSBmcm9tICcuLi9kcm9wZG93bi1saXN0L2Ryb3Bkb3duLWNvbnRlbnQuY29tcG9uZW50JztcbmltcG9ydCB7RHJvcGRvd25MaXN0fSBmcm9tICcuLi9kcm9wZG93bi1saXN0L2Ryb3Bkb3duLWxpc3QuY29tcG9uZW50JztcbmltcG9ydCB7U2VsZWN0T3B0aW9uLCBTZWxlY3RPcHRpb25Hcm91cH0gZnJvbSAnLi9vcHRpb24uY29tcG9uZW50JztcblxuY29uc3QgR1RYX1NFTEVDVF9WQUxVRV9BQ0NFU1NPUiA9IHtcbiAgICBwcm92aWRlOiBOR19WQUxVRV9BQ0NFU1NPUixcbiAgICB1c2VFeGlzdGluZzogZm9yd2FyZFJlZigoKSA9PiBTZWxlY3QpLFxuICAgIG11bHRpOiB0cnVlXG59O1xuXG5leHBvcnQgaW50ZXJmYWNlIE5vcm1hbGl6ZWRPcHRpb25Hcm91cCB7XG4gICAgb3B0aW9uczogU2VsZWN0T3B0aW9uW107XG4gICAgbGFiZWw6IHN0cmluZztcbiAgICBkaXNhYmxlZDogYm9vbGVhbjtcbiAgICBpc0RlZmF1bHRHcm91cDogYm9vbGVhbjtcbn1cblxuZXhwb3J0IHR5cGUgU2VsZWN0ZWRTZWxlY3RPcHRpb24gPSBbbnVtYmVyLCBudW1iZXJdO1xuXG4vKipcbiAqIEEgU2VsZWN0IGZvcm0gY29udHJvbCB3aGljaCB3b3JrcyB3aXRoIGFueSBraW5kIG9mIHZhbHVlIC0gYXMgb3Bwb3NlZCB0byB0aGUgbmF0aXZlIEhUTUwgYDxzZWxlY3Q+YCB3aGljaCBvbmx5IHdvcmtzXG4gKiB3aXRoIHN0cmluZ3MuIFRoZSBTZWxlY3QgY29udHJvbCBkZXBlbmRzIG9uIHRoZSBbYDxndHgtb3ZlcmxheS1ob3N0PmBdKCMvb3ZlcmxheS1ob3N0KSBiZWluZyBwcmVzZW50IGluIHRoZSBhcHAuXG4gKlxuICogYGBgaHRtbFxuICogPGd0eC1zZWxlY3QgbGFiZWw9XCJDaG9vc2UgYW4gb3B0aW9uXCIgWyhuZ01vZGVsKV09XCJzZWxlY3RWYWxcIj5cbiAqICAgICA8Z3R4LW9wdGlvbiAqbmdGb3I9XCJsZXQgaXRlbSBvZiBvcHRpb25zXCJcbiAqICAgICAgICAgICAgICAgICBbdmFsdWVdPVwiaXRlbVwiXG4gKiAgICAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cIml0ZW0uZGlzYWJsZWRcIj57eyBpdGVtLmxhYmVsIH19PC9ndHgtb3B0aW9uPlxuICogPC9ndHgtc2VsZWN0PlxuICogYGBgXG4gKlxuICovXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ2d0eC1zZWxlY3QnLFxuICAgIHRlbXBsYXRlVXJsOiAnLi9zZWxlY3QudHBsLmh0bWwnLFxuICAgIHByb3ZpZGVyczogW0dUWF9TRUxFQ1RfVkFMVUVfQUNDRVNTT1JdXG59KVxuZXhwb3J0IGNsYXNzIFNlbGVjdCBpbXBsZW1lbnRzIENvbnRyb2xWYWx1ZUFjY2Vzc29yIHtcbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBzZWxlY3QgYm94IHRvIGJlIGF1dG8tZm9jdXNlZC4gSGFuZGxlZCBieSBgQXV0b2ZvY3VzRGlyZWN0aXZlYC5cbiAgICAgKi9cbiAgICBASW5wdXQoKSBhdXRvZm9jdXM6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKiBJZiB0cnVlIHRoZSBjbGVhciBidXR0b24gaXMgZGlzcGxheWVkLCB3aGljaCBhbGxvd3MgdGhlIHVzZXIgdG8gY2xlYXIgdGhlIHNlbGVjdGlvbi4gKi9cbiAgICBASW5wdXQoKVxuICAgIGdldCBjbGVhcmFibGUoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jbGVhcmFibGU7XG4gICAgfVxuICAgIHNldCBjbGVhcmFibGUodmFsOiBib29sZWFuKSB7XG4gICAgICAgIHRoaXMuX2NsZWFyYWJsZSA9IGNvZXJjZVRvQm9vbGVhbih2YWwpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldHMgdGhlIGRpc2FibGVkIHN0YXRlLlxuICAgICAqL1xuICAgIEBJbnB1dCgpXG4gICAgZ2V0IGRpc2FibGVkKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fZGlzYWJsZWQ7XG4gICAgfVxuICAgIHNldCBkaXNhYmxlZCh2YWx1ZTogYm9vbGVhbikge1xuICAgICAgICB0aGlzLl9kaXNhYmxlZCA9IGNvZXJjZVRvQm9vbGVhbih2YWx1ZSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogV2hlbiBzZXQgdG8gdHJ1ZSwgYWxsb3dzIG11bHRpcGxlIG9wdGlvbnMgdG8gYmUgc2VsZWN0ZWQuIEluIHRoaXMgY2FzZSwgdGhlIGlucHV0IHZhbHVlIHNob3VsZCBiZVxuICAgICAqIGFuIGFycmF5IG9mIHN0cmluZ3M7IGV2ZW50cyB3aWxsIGVtaXQgYW4gYXJyYXkgb2Ygc3RyaW5ncy5cbiAgICAgKi9cbiAgICBASW5wdXQoKSBtdWx0aXBsZTogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgcmVxdWlyZWQgc3RhdGUuXG4gICAgICovXG4gICAgQElucHV0KCkgcmVxdWlyZWQ6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKlxuICAgICAqIFRoZSB2YWx1ZSBkZXRlcm1pbmVzIHdoaWNoIG9mIHRoZSBvcHRpb25zIGFyZSBzZWxlY3RlZC5cbiAgICAgKi9cbiAgICBASW5wdXQoKSB2YWx1ZTogYW55O1xuXG4gICAgLyoqXG4gICAgICogUGxhY2Vob2xkZXIgd2hpY2ggaXMgc2hvd24gaWYgbm90aGluZyBpcyBzZWxlY3RlZC5cbiAgICAgKi9cbiAgICBASW5wdXQoKSBwbGFjZWhvbGRlcjogc3RyaW5nID0gJyc7XG5cbiAgICAvKipcbiAgICAgKiBBIHRleHQgbGFiZWwgZm9yIHRoZSBpbnB1dC5cbiAgICAgKi9cbiAgICBASW5wdXQoKSBsYWJlbDogc3RyaW5nO1xuXG4gICAgLyoqXG4gICAgICogQmx1ciBldmVudC5cbiAgICAgKi9cbiAgICBAT3V0cHV0KCkgYmx1ciA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuICAgIC8qKlxuICAgICAqIEZvY3VzIGV2ZW50LlxuICAgICAqL1xuICAgIEBPdXRwdXQoKSBmb2N1cyA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuICAgIC8qKlxuICAgICAqIENoYW5nZSBldmVudC5cbiAgICAgKi9cbiAgICBAT3V0cHV0KCkgY2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XG5cbiAgICAvLyBBbiBhcnJheSBvZiBhYnN0cmFjdGVkIGNvbnRhaW5lcnMgZm9yIG9wdGlvbnMsIHdoaWNoIGFsbG93cyB1cyB0byB0cmVhdCBvcHRpb25zIGFuZCBncm91cHMgaW4gYVxuICAgIC8vIGNvbnNpc3RlbnQgd2F5LlxuICAgIG9wdGlvbkdyb3VwczogTm9ybWFsaXplZE9wdGlvbkdyb3VwW10gPSBbXTtcblxuICAgIHN1YnNjcmlwdGlvbnM6IFN1YnNjcmlwdGlvbltdID0gW107XG4gICAgc2VsZWN0ZWRPcHRpb25zOiBTZWxlY3RPcHRpb25bXSA9IFtdO1xuICAgIHZpZXdWYWx1ZTogc3RyaW5nID0gJyc7XG5cbiAgICAvLyBLZWVwcyB0cmFjayBvZiB0aGUgc2VsZWN0ZWQgb3B0aW9uLiBUd28gZGltZW5zaW9uYWwgYmVjYXVzZSBvcHRpb25zIG1heSBiZSBuZXN0ZWQgaW5zaWRlIGdyb3Vwcy4gVGhlIGZpcnN0XG4gICAgLy8gdmFsdWUgaXMgdGhlIGluZGV4IG9mIHRoZSBncm91cCAoLTEgaXMgdGhlIGRlZmF1bHQgXCJubyBncm91cFwiIGdyb3VwKSwgYW5kIHRoZSBzZWNvbmQgbnVtYmVyIGlzIHRoZSBpbmRleFxuICAgIC8vIG9mIHRoZSBvcHRpb24gd2l0aGluIHRoYXQgZ3JvdXAuXG4gICAgc2VsZWN0ZWRJbmRleDogU2VsZWN0ZWRTZWxlY3RPcHRpb24gPSBbMCwgLTFdO1xuXG4gICAgX2NsZWFyYWJsZTogYm9vbGVhbiA9IGZhbHNlO1xuICAgIF9kaXNhYmxlZDogYm9vbGVhbiA9IGZhbHNlO1xuICAgIHByaXZhdGUgcHJldmVudERlc2VsZWN0OiBib29sZWFuID0gZmFsc2U7XG4gICAgQFZpZXdDaGlsZChEcm9wZG93bkxpc3QsIHsgc3RhdGljOiB0cnVlIH0pIHByaXZhdGUgZHJvcGRvd25MaXN0OiBEcm9wZG93bkxpc3Q7XG4gICAgQFZpZXdDaGlsZChEcm9wZG93bkNvbnRlbnQsIHsgc3RhdGljOiB0cnVlIH0pIHByaXZhdGUgZHJvcGRvd25Db250ZW50OiBEcm9wZG93bkNvbnRlbnQ7XG4gICAgQENvbnRlbnRDaGlsZHJlbihTZWxlY3RPcHRpb24sIHsgZGVzY2VuZGFudHM6IGZhbHNlIH0pIHByaXZhdGUgX3NlbGVjdE9wdGlvbnM6IFF1ZXJ5TGlzdDxTZWxlY3RPcHRpb24+O1xuICAgIEBDb250ZW50Q2hpbGRyZW4oU2VsZWN0T3B0aW9uR3JvdXAsIHsgZGVzY2VuZGFudHM6IGZhbHNlIH0pIHByaXZhdGUgX3NlbGVjdE9wdGlvbkdyb3VwczogUXVlcnlMaXN0PFNlbGVjdE9wdGlvbkdyb3VwPjtcblxuICAgIC8vIFZhbHVlQWNjZXNzb3IgbWVtYmVyc1xuICAgIG9uQ2hhbmdlID0gKCk6IHZvaWQgPT4geyB9O1xuICAgIG9uVG91Y2hlZCA9ICgpOiB2b2lkID0+IHsgfTtcblxuICAgIGNvbnN0cnVjdG9yKHByaXZhdGUgY2hhbmdlRGV0ZWN0b3I6IENoYW5nZURldGVjdG9yUmVmLFxuICAgICAgICAgICAgICAgIHByaXZhdGUgZWxlbWVudFJlZjogRWxlbWVudFJlZikgeyB9XG5cbiAgICBuZ0FmdGVyVmlld0luaXQoKTogdm9pZCB7XG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgdmFsdWUgaWYgdGhlcmUgYXJlIGFueSBjaGFuZ2VzIHRvIHRoZSBvcHRpb25zXG4gICAgICAgIHRoaXMuc3Vic2NyaXB0aW9ucy5wdXNoKFxuICAgICAgICAgICAgdGhpcy5fc2VsZWN0T3B0aW9ucy5jaGFuZ2VzLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy53cml0ZVZhbHVlKHRoaXMudmFsdWUpO1xuICAgICAgICAgICAgICAgIHRoaXMub3B0aW9uR3JvdXBzID0gdGhpcy5idWlsZE9wdGlvbkdyb3VwcygpO1xuICAgICAgICAgICAgICAgIHRoaXMuc2VsZWN0ZWRPcHRpb25zID0gdGhpcy5nZXRJbml0aWFsbHlTZWxlY3RlZE9wdGlvbnMoKTtcbiAgICAgICAgICAgIH0pXG4gICAgICAgICk7XG5cbiAgICAgICAgdGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvcignZ3R4LWRyb3Bkb3duLWxpc3QnKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuYWRkRXZlbnRMaXN0ZW5lcigna2V5ZG93bicsIHRoaXMuaGFuZGxlS2V5ZG93bi5iaW5kKHRoaXMpKTtcbiAgICB9XG5cbiAgICBuZ0FmdGVyQ29udGVudEluaXQoKTogdm9pZCB7XG4gICAgICAgIHRoaXMub3B0aW9uR3JvdXBzID0gdGhpcy5idWlsZE9wdGlvbkdyb3VwcygpO1xuICAgICAgICB0aGlzLnNlbGVjdGVkT3B0aW9ucyA9IHRoaXMuZ2V0SW5pdGlhbGx5U2VsZWN0ZWRPcHRpb25zKCk7XG4gICAgICAgIHRoaXMudXBkYXRlVmlld1ZhbHVlKCk7XG4gICAgfVxuXG4gICAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuc3Vic2NyaXB0aW9ucy5mb3JFYWNoKHMgPT4gcy51bnN1YnNjcmliZSgpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBFdmVudCBoYW5kbGVyIGZvciB3aGVuIG9uZSBvZiB0aGUgTWF0ZXJpYWxpemUtZ2VuZXJhdGVkIExJIGVsZW1lbnRzIGlzIGNsaWNrZWQuXG4gICAgICovXG4gICAgc2VsZWN0SXRlbShncm91cEluZGV4OiBudW1iZXIsIG9wdGlvbkluZGV4OiBudW1iZXIpOiB2b2lkIHtcbiAgICAgICAgY29uc3Qgb3B0aW9uID0gdGhpcy5vcHRpb25Hcm91cHNbZ3JvdXBJbmRleF0gJiYgdGhpcy5vcHRpb25Hcm91cHNbZ3JvdXBJbmRleF0ub3B0aW9uc1tvcHRpb25JbmRleF07XG4gICAgICAgIGlmICghdGhpcy5vcHRpb25Hcm91cHNbZ3JvdXBJbmRleF0uZGlzYWJsZWQgJiYgb3B0aW9uICYmICFvcHRpb24uZGlzYWJsZWQpIHtcbiAgICAgICAgICAgIHRoaXMudG9nZ2xlU2VsZWN0ZWRPcHRpb24ob3B0aW9uKTtcbiAgICAgICAgICAgIGNvbnN0IHNlbGVjdGVkVmFsdWVzID0gdGhpcy5zZWxlY3RlZE9wdGlvbnMubWFwKG8gPT4gby52YWx1ZSk7XG4gICAgICAgICAgICB0aGlzLnZhbHVlID0gdGhpcy5tdWx0aXBsZSA/IHNlbGVjdGVkVmFsdWVzIDogc2VsZWN0ZWRWYWx1ZXNbMF07XG4gICAgICAgICAgICB0aGlzLm9uQ2hhbmdlKCk7XG4gICAgICAgICAgICB0aGlzLmNoYW5nZS5lbWl0KHRoaXMudmFsdWUpO1xuICAgICAgICAgICAgdGhpcy51cGRhdGVWaWV3VmFsdWUoKTtcbiAgICAgICAgICAgIHRoaXMuc2Nyb2xsVG9TZWxlY3RlZE9wdGlvbigpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgaW5wdXRCbHVyKGU6IEV2ZW50KTogdm9pZCB7XG4gICAgICAgIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgIHRoaXMub25Ub3VjaGVkKCk7XG4gICAgICAgIHRoaXMuYmx1ci5lbWl0KHRoaXMudmFsdWUpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNlbGVjdCB0aGUgaW5pdGlhbCB2YWx1ZSB3aGVuIHRoZSBkcm9wZG93biBpcyBvcGVuZWQuXG4gICAgICovXG4gICAgZ