UNPKG

carbon-components-angular

Version:
226 lines (217 loc) 27.7 kB
import { BehaviorSubject, combineLatest } from "rxjs"; import { map } from "rxjs/operators"; import { tabbableSelectorIgnoreTabIndex, getFocusElementList } from "carbon-components-angular/common"; /** * `DataGridInteractionModel` provides centralized control over arbitrary 2d grids, following the w3 specs. * * Refs: * - https://www.w3.org/TR/wai-aria-practices/examples/grid/dataGrids.html * - https://www.w3.org/TR/wai-aria-practices/#grid * * Example usage (taken from `table.component`): ```typescript // a standard HTML table const table = this.elementRef.nativeElement.querySelector("table") as HTMLTableElement; // `TableDomAdapter` implements `TableAdapter` and provides a consistent interface to query rows and columns in a table const tableAdapter = new TableDomAdapter(table); // the keydown events that we'll use for keyboard navigation of the table const keydownEventStream = fromEvent<KeyboardEvent>(table, "keydown"); // the click events we'll use to ensure focus is updated correctly on click const clickEventStream = fromEvent<MouseEvent>(table, "click"); // the `DataGridInteractionModel` instance! this.interactionModel = new DataGridInteractionModel(keydownEventStream, clickEventStream, tableAdapter); // subscribe to the combined position updates this.interactionModel.position.subscribe(event => { const [currentRow, currentColumn] = event.current; const [previousRow, previousColumn] = event.previous; // query the TableAdapter for the cell at the current row and column ... const currentElement = tableAdapter.getCell(currentRow, currentColumn); // ... and make it focusable it Table.setTabIndex(currentElement, 0); // if the model has just initialized don't focus or reset anything if (previousRow === -1 || previousColumn === -1) { return; } // query the TableAdapter for the cell at the previous row and column ... const previousElement = tableAdapter.getCell(previousRow, previousColumn); // ... and make it unfocusable (now there is only a single focusable cell) Table.setTabIndex(previousElement, -1); // finally, focus the current cell (skipped during initilzation) Table.focus(currentElement); }); ``` */ export class DataGridInteractionModel { /** * `DataGridInteractionModel` requires knowledge of events, and a representation of your table/grid to be useful. * * @param keyboardEventStream an Observable of KeyboardEvents. Should be scoped to the table container. * @param clickEventStream an Observable of ClickEvents. should only include clicks that take action on items known by the TableAdapter * @param tableAdapter an instance of a concrete class that implements TableAdapter. The standard carbon table uses TableDomAdapter */ constructor(keyboardEventStream, clickEventStream, tableAdapter) { this.keyboardEventStream = keyboardEventStream; this.clickEventStream = clickEventStream; this.tableAdapter = tableAdapter; /** * Internal subject to handle changes in row */ this.rowSubject = new BehaviorSubject({ current: 0, previous: -1 }); /** * Internal subject to handle changes in column */ this.columnSubject = new BehaviorSubject({ current: 0, previous: -1 }); this.rowIndex = this.rowSubject.asObservable(); this.columnIndex = this.columnSubject.asObservable(); this.position = combineLatest(this.rowIndex, this.columnIndex).pipe(map(positions => { const [row, column] = positions; return { current: [row.current, column.current], previous: [row.previous, column.previous] }; })); this.keyboardEventStream.subscribe(this.handleKeyboardEvent.bind(this)); this.clickEventStream.subscribe(this.handleClickEvent.bind(this)); } /** * The latest value emitted by the rowSubject */ get currentRow() { return this.rowSubject.getValue().current; } /** * The latest value emitted by the columnSubject */ get currentColumn() { return this.columnSubject.getValue().current; } /** * The last column as reported by the adapter */ get lastColumn() { return this.tableAdapter.lastColumnIndex; } /** * The last row as reported by the adapter */ get lastRow() { return this.tableAdapter.lastRowIndex; } /** * Handles moving the position according to the w3 datagrid navigation specs * * Refs: * - https://www.w3.org/TR/wai-aria-practices/examples/grid/dataGrids.html * - https://www.w3.org/TR/wai-aria-practices/#grid * * @param event the KeyboardEvent to handle */ handleKeyboardEvent(event) { const currentCell = this.tableAdapter.getCell(this.currentRow, this.currentColumn); let currentColumn = this.tableAdapter.findColumnIndex(currentCell); let currentRow = this.tableAdapter.findRowIndex(currentCell); switch (event.key) { case "ArrowRight": event.preventDefault(); // add the colspan since findColumnIndex will return the // first column containing the cell (of N columns it may span) // and we want to navigate to the next "real" column this.goToColumn(currentColumn + currentCell.colSpan); break; case "ArrowLeft": event.preventDefault(); // we only ever need to subtract 1 from the column, since findColumnIndex returns the // first of N columns containing the cell this.goToColumn(currentColumn - 1); break; case "ArrowDown": event.preventDefault(); this.goToRow(currentRow + currentCell.rowSpan); break; case "ArrowUp": event.preventDefault(); this.goToRow(currentRow - 1); break; case "Home": event.preventDefault(); if (event.ctrlKey) { this.goTo({ row: 0, column: 0 }); } else { this.goToColumn(0); } break; case "End": event.preventDefault(); if (event.ctrlKey) { this.goTo({ row: this.lastRow, column: this.lastColumn }); } else { this.goToColumn(this.lastColumn); } break; } } /** * Handles moving the position to the clicked cell * * @param event the MouseEvent to handle */ handleClickEvent(event) { const cell = event.target.closest("td, th"); const [rowIndex, cellIndex] = this.tableAdapter.findIndex(cell); this.goTo({ row: rowIndex, column: cellIndex }); } /** * Jump to a specific column without changing the row * * @param index column to jump to */ goToColumn(index) { if (index > this.lastColumn || index < 0) { return; } this.goTo({ row: this.currentRow, column: index }); } /** * Jump to a specific row without changing the column * * @param index row to jump to */ goToRow(index) { if (index > this.lastRow || index < 0) { return; } this.goTo({ row: index, column: this.currentColumn }); } /** * Jump to the specified row and column * * @param param0 an object that contains `row` and `column` properties */ goTo({ row, column }) { this.rowSubject.next({ current: row, previous: this.currentRow }); this.columnSubject.next({ current: column, previous: this.currentColumn }); } /** * Convenience method to reset the tab indexes on a standard carbon table. * For custom tables you may want to reset the indexes manually and simply call `.reset()` */ resetTabIndexes(newTabIndex = -1) { for (let i = 0; i < this.tableAdapter.lastRowIndex; i++) { const row = this.tableAdapter.getRow(i); for (const cell of Array.from(row.cells)) { const tabbableElements = getFocusElementList(cell, tabbableSelectorIgnoreTabIndex); tabbableElements.forEach((node) => node.tabIndex = newTabIndex); cell.tabIndex = newTabIndex; } } this.reset(); } /** * Resets the models focus position */ reset() { this.rowSubject.next({ current: 0, previous: -1 }); this.columnSubject.next({ current: 0, previous: -1 }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS1ncmlkLWludGVyYWN0aW9uLW1vZGVsLmNsYXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3RhYmxlL2RhdGEtZ3JpZC1pbnRlcmFjdGlvbi1tb2RlbC5jbGFzcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ04sZUFBZSxFQUVmLGFBQWEsRUFDYixNQUFNLE1BQU0sQ0FBQztBQUNkLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVyQyxPQUFPLEVBQUUsOEJBQThCLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQVl2Rzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQThDRztBQUNILE1BQU0sT0FBTyx3QkFBd0I7SUFtRHBDOzs7Ozs7T0FNRztJQUNILFlBQ1csbUJBQThDLEVBQzlDLGdCQUF3QyxFQUN4QyxZQUEwQjtRQUYxQix3QkFBbUIsR0FBbkIsbUJBQW1CLENBQTJCO1FBQzlDLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBd0I7UUFDeEMsaUJBQVksR0FBWixZQUFZLENBQWM7UUEvQ3JDOztXQUVHO1FBQ08sZUFBVSxHQUFHLElBQUksZUFBZSxDQUFDLEVBQUUsT0FBTyxFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3pFOztXQUVHO1FBQ08sa0JBQWEsR0FBRyxJQUFJLGVBQWUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQTBDM0UsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQy9DLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUNyRCxJQUFJLENBQUMsUUFBUSxHQUFH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QUFDLFFBQVEsR0FBRyxXQUFXLENBQUM7YUFDNUI7U0FDRDtRQUVELElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNkLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUs7UUFDSixJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNuRCxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUN2RCxDQUFDO0NBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuXHRCZWhhdmlvclN1YmplY3QsXG5cdE9ic2VydmFibGUsXG5cdGNvbWJpbmVMYXRlc3Rcbn0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7IG1hcCB9IGZyb20gXCJyeGpzL29wZXJhdG9yc1wiO1xuaW1wb3J0IHsgVGFibGVBZGFwdGVyIH0gZnJvbSBcIi4vdGFibGUtYWRhcHRlci5jbGFzc1wiO1xuaW1wb3J0IHsgdGFiYmFibGVTZWxlY3Rvcklnbm9yZVRhYkluZGV4LCBnZXRGb2N1c0VsZW1lbnRMaXN0IH0gZnJvbSBcImNhcmJvbi1jb21wb25lbnRzLWFuZ3VsYXIvY29tbW9uXCI7XG5cbi8qKlxuICogVGhlIGN1cnJlbnQgYW5kIHByZXZpb3VzIHBvc2l0aW9uIGluIHRoZSBncmlkLlxuICpcbiAqIGBjdXJyZW50YCBhbmQgYHByZXZpb3VzYCBhcmUgdHVwbGVzIHRoYXQgZm9sbG93IHRoZSBgW3JvdywgY29sdW1uXWAgY29udmVudGlvbi5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBEYXRhR3JpZFBvc2l0aW9uIHtcblx0Y3VycmVudDogW251bWJlciwgbnVtYmVyXTtcblx0cHJldmlvdXM6IFtudW1iZXIsIG51bWJlcl07XG59XG5cbi8qKlxuICogYERhdGFHcmlkSW50ZXJhY3Rpb25Nb2RlbGAgcHJvdmlkZXMgY2VudHJhbGl6ZWQgY29udHJvbCBvdmVyIGFyYml0cmFyeSAyZCBncmlkcywgZm9sbG93aW5nIHRoZSB3MyBzcGVjcy5cbiAqXG4gKiBSZWZzOlxuICogIC0gaHR0cHM6Ly93d3cudzMub3JnL1RSL3dhaS1hcmlhLXByYWN0aWNlcy9leGFtcGxlcy9ncmlkL2RhdGFHcmlkcy5odG1sXG4gKiAgLSBodHRwczovL3d3dy53My5vcmcvVFIvd2FpLWFyaWEtcHJhY3RpY2VzLyNncmlkXG4gKlxuICogRXhhbXBsZSB1c2FnZSAodGFrZW4gZnJvbSBgdGFibGUuY29tcG9uZW50YCk6XG5gYGB0eXBlc2NyaXB0XG4vLyBhIHN0YW5kYXJkIEhUTUwgdGFibGVcbmNvbnN0IHRhYmxlID0gdGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvcihcInRhYmxlXCIpIGFzIEhUTUxUYWJsZUVsZW1lbnQ7XG5cbi8vIGBUYWJsZURvbUFkYXB0ZXJgIGltcGxlbWVudHMgYFRhYmxlQWRhcHRlcmAgYW5kIHByb3ZpZGVzIGEgY29uc2lzdGVudCBpbnRlcmZhY2UgdG8gcXVlcnkgcm93cyBhbmQgY29sdW1ucyBpbiBhIHRhYmxlXG5jb25zdCB0YWJsZUFkYXB0ZXIgPSBuZXcgVGFibGVEb21BZGFwdGVyKHRhYmxlKTtcblxuLy8gdGhlIGtleWRvd24gZXZlbnRzIHRoYXQgd2UnbGwgdXNlIGZvciBrZXlib2FyZCBuYXZpZ2F0aW9uIG9mIHRoZSB0YWJsZVxuY29uc3Qga2V5ZG93bkV2ZW50U3RyZWFtID0gZnJvbUV2ZW50PEtleWJvYXJkRXZlbnQ+KHRhYmxlLCBcImtleWRvd25cIik7XG5cbi8vIHRoZSBjbGljayBldmVudHMgd2UnbGwgdXNlIHRvIGVuc3VyZSBmb2N1cyBpcyB1cGRhdGVkIGNvcnJlY3RseSBvbiBjbGlja1xuY29uc3QgY2xpY2tFdmVudFN0cmVhbSA9IGZyb21FdmVudDxNb3VzZUV2ZW50Pih0YWJsZSwgXCJjbGlja1wiKTtcblxuLy8gdGhlIGBEYXRhR3JpZEludGVyYWN0aW9uTW9kZWxgIGluc3RhbmNlIVxudGhpcy5pbnRlcmFjdGlvbk1vZGVsID0gbmV3IERhdGFHcmlkSW50ZXJhY3Rpb25Nb2RlbChrZXlkb3duRXZlbnRTdHJlYW0sIGNsaWNrRXZlbnRTdHJlYW0sIHRhYmxlQWRhcHRlcik7XG5cbi8vIHN1YnNjcmliZSB0byB0aGUgY29tYmluZWQgcG9zaXRpb24gdXBkYXRlc1xudGhpcy5pbnRlcmFjdGlvbk1vZGVsLnBvc2l0aW9uLnN1YnNjcmliZShldmVudCA9PiB7XG5cdGNvbnN0IFtjdXJyZW50Um93LCBjdXJyZW50Q29sdW1uXSA9IGV2ZW50LmN1cnJlbnQ7XG5cdGNvbnN0IFtwcmV2aW91c1JvdywgcHJldmlvdXNDb2x1bW5dID0gZXZlbnQucHJldmlvdXM7XG5cblx0Ly8gcXVlcnkgdGhlIFRhYmxlQWRhcHRlciBmb3IgdGhlIGNlbGwgYXQgdGhlIGN1cnJlbnQgcm93IGFuZCBjb2x1bW4gLi4uXG5cdGNvbnN0IGN1cnJlbnRFbGVtZW50ID0gdGFibGVBZGFwdGVyLmdldENlbGwoY3VycmVudFJvdywgY3VycmVudENvbHVtbik7XG5cdC8vIC4uLiBhbmQgbWFrZSBpdCBmb2N1c2FibGUgaXRcblx0VGFibGUuc2V0VGFiSW5kZXgoY3VycmVudEVsZW1lbnQsIDApO1xuXG5cdC8vIGlmIHRoZSBtb2RlbCBoYXMganVzdCBpbml0aWFsaXplZCBkb24ndCBmb2N1cyBvciByZXNldCBhbnl0aGluZ1xuXHRpZiAocHJldmlvdXNSb3cgPT09IC0xIHx8IHByZXZpb3VzQ29sdW1uID09PSAtMSkgeyByZXR1cm47IH1cblxuXHQvLyBxdWVyeSB0aGUgVGFibGVBZGFwdGVyIGZvciB0aGUgY2VsbCBhdCB0aGUgcHJldmlvdXMgcm93IGFuZCBjb2x1bW4gLi4uXG5cdGNvbnN0IHByZXZpb3VzRWxlbWVudCA9IHRhYmxlQWRhcHRlci5nZXRDZWxsKHByZXZpb3VzUm93LCBwcmV2aW91c0NvbHVtbik7XG5cdC8vIC4uLiBhbmQgbWFrZSBpdCB1bmZvY3VzYWJsZSAobm93IHRoZXJlIGlzIG9ubHkgYSBzaW5nbGUgZm9jdXNhYmxlIGNlbGwpXG5cdFRhYmxlLnNldFRhYkluZGV4KHByZXZpb3VzRWxlbWVudCwgLTEpO1xuXG5cdC8vIGZpbmFsbHksIGZvY3VzIHRoZSBjdXJyZW50IGNlbGwgKHNraXBwZWQgZHVyaW5nIGluaXRpbHphdGlvbilcblx0VGFibGUuZm9jdXMoY3VycmVudEVsZW1lbnQpO1xufSk7XG5gYGBcbiAqL1xuZXhwb3J0IGNsYXNzIERhdGFHcmlkSW50ZXJhY3Rpb25Nb2RlbCB7XG5cdC8qKlxuXHQgKiBBbiBPYnNlcnZhYmxlIHRoYXQgcHJvdmlkZXMgYW4gYWdncmVnYXRlZCB2aWV3IG9mIHRoZSBgcm93SW5kZXhgIGFuZCBgY29sdW1uSW5kZXhgIE9ic2VydmFibGVzXG5cdCAqL1xuXHRyZWFkb25seSBwb3NpdGlvbjogT2JzZXJ2YWJsZTxEYXRhR3JpZFBvc2l0aW9uPjtcblx0LyoqXG5cdCAqIEFuIE9ic2VydmFibGUgdGhhdCBwcm92aWRlcyB0aGUgY3VycmVudCBhbmQgcHJldmlvdXMgcm93IGluZGV4ZXMuXG5cdCAqL1xuXHRyZWFkb25seSByb3dJbmRleDogT2JzZXJ2YWJsZTx7IGN1cnJlbnQ6IG51bWJlciwgcHJldmlvdXM6IG51bWJlciB9Pjtcblx0LyoqXG5cdCAqIEFuIE9ic2VydmFibGUgdGhhdCBwcm92aWRlcyB0aGUgY3VycmVudCBhbmQgcHJldmlvdXMgY29sdW1uIGluZGV4ZXMuXG5cdCAqL1xuXHRyZWFkb25seSBjb2x1bW5JbmRleDogT2JzZXJ2YWJsZTx7IGN1cnJlbnQ6IG51bWJlciwgcHJldmlvdXM6IG51bWJlciB9PjtcblxuXHQvKipcblx0ICogSW50ZXJuYWwgc3ViamVjdCB0byBoYW5kbGUgY2hhbmdlcyBpbiByb3dcblx0ICovXG5cdHByb3RlY3RlZCByb3dTdWJqZWN0ID0gbmV3IEJlaGF2aW9yU3ViamVjdCh7IGN1cnJlbnQ6IDAsIHByZXZpb3VzOiAtMSB9KTtcblx0LyoqXG5cdCAqIEludGVybmFsIHN1YmplY3QgdG8gaGFuZGxlIGNoYW5nZXMgaW4gY29sdW1uXG5cdCAqL1xuXHRwcm90ZWN0ZWQgY29sdW1uU3ViamVjdCA9IG5ldyBCZWhhdmlvclN1YmplY3QoeyBjdXJyZW50OiAwLCBwcmV2aW91czogLTEgfSk7XG5cblx0LyoqXG5cdCAqIFRoZSBsYXRlc3QgdmFsdWUgZW1pdHRlZCBieSB0aGUgcm93U3ViamVjdFxuXHQgKi9cblx0cHJvdGVjdGVkIGdldCBjdXJyZW50Um93KCkge1xuXHRcdHJldHVybiB0aGlzLnJvd1N1YmplY3QuZ2V0VmFsdWUoKS5jdXJyZW50O1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBsYXRlc3QgdmFsdWUgZW1pdHRlZCBieSB0aGUgY29sdW1uU3ViamVjdFxuXHQgKi9cblx0cHJvdGVjdGVkIGdldCBjdXJyZW50Q29sdW1uKCkge1xuXHRcdHJldHVybiB0aGlzLmNvbHVtblN1YmplY3QuZ2V0VmFsdWUoKS5jdXJyZW50O1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBsYXN0IGNvbHVtbiBhcyByZXBvcnRlZCBieSB0aGUgYWRhcHRlclxuXHQgKi9cblx0cHJvdGVjdGVkIGdldCBsYXN0Q29sdW1uKCkge1xuXHRcdHJldHVybiB0aGlzLnRhYmxlQWRhcHRlci5sYXN0Q29sdW1uSW5kZXg7XG5cdH1cblxuXHQvKipcblx0ICogVGhlIGxhc3Qgcm93IGFzIHJlcG9ydGVkIGJ5IHRoZSBhZGFwdGVyXG5cdCAqL1xuXHRwcm90ZWN0ZWQgZ2V0IGxhc3RSb3coKSB7XG5cdFx0cmV0dXJuIHRoaXMudGFibGVBZGFwdGVyLmxhc3RSb3dJbmRleDtcblx0fVxuXG5cdC8qKlxuXHQgKiBgRGF0YUdyaWRJbnRlcmFjdGlvbk1vZGVsYCByZXF1aXJlcyBrbm93bGVkZ2Ugb2YgZXZlbnRzLCBhbmQgYSByZXByZXNlbnRhdGlvbiBvZiB5b3VyIHRhYmxlL2dyaWQgdG8gYmUgdXNlZnVsLlxuXHQgKlxuXHQgKiBAcGFyYW0ga2V5Ym9hcmRFdmVudFN0cmVhbSBhbiBPYnNlcnZhYmxlIG9mIEtleWJvYXJkRXZlbnRzLiBTaG91bGQgYmUgc2NvcGVkIHRvIHRoZSB0YWJsZSBjb250YWluZXIuXG5cdCAqIEBwYXJhbSBjbGlja0V2ZW50U3RyZWFtIGFuIE9ic2VydmFibGUgb2YgQ2xpY2tFdmVudHMuIHNob3VsZCBvbmx5IGluY2x1ZGUgY2xpY2tzIHRoYXQgdGFrZSBhY3Rpb24gb24gaXRlbXMga25vd24gYnkgdGhlIFRhYmxlQWRhcHRlclxuXHQgKiBAcGFyYW0gdGFibGVBZGFwdGVyIGFuIGluc3RhbmNlIG9mIGEgY29uY3JldGUgY2xhc3MgdGhhdCBpbXBsZW1lbnRzIFRhYmxlQWRhcHRlci4gVGhlIHN0YW5kYXJkIGNhcmJvbiB0YWJsZSB1c2VzIFRhYmxlRG9tQWRhcHRlclxuXHQgKi9cblx0Y29uc3RydWN0b3IoXG5cdFx0cHJvdGVjdGVkIGtleWJvYXJkRXZlbnRTdHJlYW06IE9ic2VydmFibGU8S2V5Ym9hcmRFdmVudD4sXG5cdFx0cHJvdGVjdGVkIGNsaWNrRXZlbnRTdHJlYW06IE9ic2VydmFibGU8TW91c2VFdmVudD4sXG5cdFx0cHJvdGVjdGVkIHRhYmxlQWRhcHRlcjogVGFibGVBZGFwdGVyXG5cdCkge1xuXHRcdHRoaXMucm93SW5kZXggPSB0aGlzLnJvd1N1YmplY3QuYXNPYnNlcnZhYmxlKCk7XG5cdFx0dGhpcy5jb2x1bW5JbmRleCA9IHRoaXMuY29sdW1uU3ViamVjdC5hc09ic2VydmFibGUoKTtcblx0XHR0aGlzLnBvc2l0aW9uID0gY29tYmluZUxhdGVzdCh0aGlzLnJvd0luZGV4LCB0aGlzLmNvbHVtbkluZGV4KS5waXBlKG1hcChwb3NpdGlvbnMgPT4ge1xuXHRcdFx0Y29uc3QgW3JvdywgY29sdW1uXSA9IHBvc2l0aW9ucztcblx0XHRcdHJldHVybiB7XG5cdFx0XHRcdGN1cnJlbnQ6IFtyb3cuY3VycmVudCwgY29sdW1uLmN1cnJlbnRdLFxuXHRcdFx0XHRwcmV2aW91czogW3Jvdy5wcmV2aW91cywgY29sdW1uLnByZXZpb3VzXVxuXHRcdFx0fTtcblx0XHR9KSkgYXMgT2JzZXJ2YWJsZTxEYXRhR3JpZFBvc2l0aW9uPjtcblx0XHR0aGlzLmtleWJvYXJkRXZlbnRTdHJlYW0uc3Vic2NyaWJlKHRoaXMuaGFuZGxlS2V5Ym9hcmRFdmVudC5iaW5kKHRoaXMpKTtcblx0XHR0aGlzLmNsaWNrRXZlbnRTdHJlYW0uc3Vic2NyaWJlKHRoaXMuaGFuZGxlQ2xpY2tFdmVudC5iaW5kKHRoaXMpKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBIYW5kbGVzIG1vdmluZyB0aGUgcG9zaXRpb24gYWNjb3JkaW5nIHRvIHRoZSB3MyBkYXRhZ3JpZCBuYXZpZ2F0aW9uIHNwZWNzXG5cdCAqXG5cdCAqIFJlZnM6XG5cdCAqICAtIGh0dHBzOi8vd3d3LnczLm9yZy9UUi93YWktYXJpYS1wcmFjdGljZXMvZXhhbXBsZXMvZ3JpZC9kYXRhR3JpZHMuaHRtbFxuXHQgKiAgLSBodHRwczovL3d3dy53My5vcmcvVFIvd2FpLWFyaWEtcHJhY3RpY2VzLyNncmlkXG5cdCAqXG5cdCAqIEBwYXJhbSBldmVudCB0aGUgS2V5Ym9hcmRFdmVudCB0byBoYW5kbGVcblx0ICovXG5cdGhhbmRsZUtleWJvYXJkRXZlbnQoZXZlbnQ6IEtleWJvYXJkRXZlbnQpIHtcblx0XHRjb25zdCBjdXJyZW50Q2VsbCA9IHRoaXMudGFibGVBZGFwdGVyLmdldENlbGwodGhpcy5jdXJyZW50Um93LCB0aGlzLmN1cnJlbnRDb2x1bW4pO1xuXHRcdGxldCBjdXJyZW50Q29sdW1uID0gdGhpcy50YWJsZUFkYXB0ZXIuZmluZENvbHVtbkluZGV4KGN1cnJlbnRDZWxsKTtcblx0XHRsZXQgY3VycmVudFJvdyA9IHRoaXMudGFibGVBZGFwdGVyLmZpbmRSb3dJbmRleChjdXJyZW50Q2VsbCk7XG5cblx0XHRzd2l0Y2ggKGV2ZW50LmtleSkge1xuXHRcdFx0Y2FzZSBcIkFycm93UmlnaHRcIjpcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0Ly8gYWRkIHRoZSBjb2xzcGFuIHNpbmNlIGZpbmRDb2x1bW5JbmRleCB3aWxsIHJldHVybiB0aGVcblx0XHRcdFx0Ly8gZmlyc3QgY29sdW1uIGNvbnRhaW5pbmcgdGhlIGNlbGwgKG9mIE4gY29sdW1ucyBpdCBtYXkgc3Bhbilcblx0XHRcdFx0Ly8gYW5kIHdlIHdhbnQgdG8gbmF2aWdhdGUgdG8gdGhlIG5leHQgXCJyZWFsXCIgY29sdW1uXG5cdFx0XHRcdHRoaXMuZ29Ub0NvbHVtbihjdXJyZW50Q29sdW1uICsgY3VycmVudENlbGwuY29sU3Bhbik7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0Y2FzZSBcIkFycm93TGVmdFwiOlxuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0XHQvLyB3ZSBvbmx5IGV2ZXIgbmVlZCB0byBzdWJ0cmFjdCAxIGZyb20gdGhlIGNvbHVtbiwgc2luY2UgZmluZENvbHVtbkluZGV4IHJldHVybnMgdGhlXG5cdFx0XHRcdC8vIGZpcnN0IG9mIE4gY29sdW1ucyBjb250YWluaW5nIHRoZSBjZWxsXG5cdFx0XHRcdHRoaXMuZ29Ub0NvbHVtbihjdXJyZW50Q29sdW1uIC0gMSk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0Y2FzZSBcIkFycm93RG93blwiOlxuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0XHR0aGlzLmdvVG9Sb3coY3VycmVudFJvdyArIGN1cnJlbnRDZWxsLnJvd1NwYW4pO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdGNhc2UgXCJBcnJvd1VwXCI6XG5cdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdHRoaXMuZ29Ub1JvdyhjdXJyZW50Um93IC0gMSk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0Y2FzZSBcIkhvbWVcIjpcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0aWYgKGV2ZW50LmN0cmxLZXkpIHtcblx0XHRcdFx0XHR0aGlzLmdvVG8oe3JvdzogMCwgY29sdW1uOiAwfSk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0dGhpcy5nb1RvQ29sdW1uKDApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0Y2FzZSBcIkVuZFwiOlxuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0XHRpZiAoZXZlbnQuY3RybEtleSkge1xuXHRcdFx0XHRcdHRoaXMuZ29Ubyh7IHJvdzogdGhpcy5sYXN0Um93LCBjb2x1bW46IHRoaXMubGFzdENvbHVtbiB9KTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHR0aGlzLmdvVG9Db2x1bW4odGhpcy5sYXN0Q29sdW1uKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRicmVhaztcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogSGFuZGxlcyBtb3ZpbmcgdGhlIHBvc2l0aW9uIHRvIHRoZSBjbGlja2VkIGNlbGxcblx0ICpcblx0ICogQHBhcmFtIGV2ZW50IHRoZSBNb3VzZUV2ZW50IHRvIGhhbmRsZVxuXHQgKi9cblx0aGFuZGxlQ2xpY2tFdmVudChldmVudDogTW91c2VFdmVudCkge1xuXHRcdGNvbnN0IGNlbGwgPSAoZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50KS5jbG9zZXN0KFwidGQsIHRoXCIpIGFzIEhUTUxUYWJsZUNlbGxFbGVtZW50O1xuXHRcdGNvbnN0IFtyb3dJbmRleCwgY2VsbEluZGV4XSA9IHRoaXMudGFibGVBZGFwdGVyLmZpbmRJbmRleChjZWxsKTtcblx0XHR0aGlzLmdvVG8oeyByb3c6IHJvd0luZGV4LCBjb2x1bW46IGNlbGxJbmRleCB9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBKdW1wIHRvIGEgc3BlY2lmaWMgY29sdW1uIHdpdGhvdXQgY2hhbmdpbmcgdGhlIHJvd1xuXHQgKlxuXHQgKiBAcGFyYW0gaW5kZXggY29sdW1uIHRvIGp1bXAgdG9cblx0ICovXG5cdGdvVG9Db2x1bW4oaW5kZXg6IG51bWJlcikge1xuXHRcdGlmIChpbmRleCA+IHRoaXMubGFzdENvbHVtbiB8fCBpbmRleCA8IDApIHsgcmV0dXJuOyB9XG5cdFx0dGhpcy5nb1RvKHsgcm93OiB0aGlzLmN1cnJlbnRSb3csIGNvbHVtbjogaW5kZXh9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBKdW1wIHRvIGEgc3BlY2lmaWMgcm93IHdpdGhvdXQgY2hhbmdpbmcgdGhlIGNvbHVtblxuXHQgKlxuXHQgKiBAcGFyYW0gaW5kZXggcm93IHRvIGp1bXAgdG9cblx0ICovXG5cdGdvVG9Sb3coaW5kZXg6IG51bWJlcikge1xuXHRcdGlmIChpbmRleCA+IHRoaXMubGFzdFJvdyB8fCBpbmRleCA8IDApIHsgcmV0dXJuOyB9XG5cdFx0dGhpcy5nb1RvKHtyb3c6IGluZGV4LCBjb2x1bW46IHRoaXMuY3VycmVudENvbHVtbn0pO1xuXHR9XG5cblx0LyoqXG5cdCAqIEp1bXAgdG8gdGhlIHNwZWNpZmllZCByb3cgYW5kIGNvbHVtblxuXHQgKlxuXHQgKiBAcGFyYW0gcGFyYW0wIGFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIGByb3dgIGFuZCBgY29sdW1uYCBwcm9wZXJ0aWVzXG5cdCAqL1xuXHRnb1RvKHtyb3csIGNvbHVtbn0pIHtcblx0XHR0aGlzLnJvd1N1YmplY3QubmV4dCh7IGN1cnJlbnQ6IHJvdywgcHJldmlvdXM6IHRoaXMuY3VycmVudFJvdyB9KTtcblx0XHR0aGlzLmNvbHVtblN1YmplY3QubmV4dCh7IGN1cnJlbnQ6IGNvbHVtbiwgcHJldmlvdXM6IHRoaXMuY3VycmVudENvbHVtbiB9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDb252ZW5pZW5jZSBtZXRob2QgdG8gcmVzZXQgdGhlIHRhYiBpbmRleGVzIG9uIGEgc3RhbmRhcmQgY2FyYm9uIHRhYmxlLlxuXHQgKiBGb3IgY3VzdG9tIHRhYmxlcyB5b3UgbWF5IHdhbnQgdG8gcmVzZXQgdGhlIGluZGV4ZXMgbWFudWFsbHkgYW5kIHNpbXBseSBjYWxsIGAucmVzZXQoKWBcblx0ICovXG5cdHJlc2V0VGFiSW5kZXhlcyhuZXdUYWJJbmRleCA9IC0xKSB7XG5cdFx0Zm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLnRhYmxlQWRhcHRlci5sYXN0Um93SW5kZXg7IGkrKykge1xuXHRcdFx0Y29uc3Qgcm93ID0gdGhpcy50YWJsZUFkYXB0ZXIuZ2V0Um93KGkpIGFzIEhUTUxUYWJsZVJvd0VsZW1lbnQ7XG5cdFx0XHRmb3IgKGNvbnN0IGNlbGwgb2YgQXJyYXkuZnJvbShyb3cuY2VsbHMpKSB7XG5cdFx0XHRcdGNvbnN0IHRhYmJhYmxlRWxlbWVudHMgPSBnZXRGb2N1c0VsZW1lbnRMaXN0KGNlbGwsIHRhYmJhYmxlU2VsZWN0b3JJZ25vcmVUYWJJbmRleCk7XG5cdFx0XHRcdHRhYmJhYmxlRWxlbWVudHMuZm9yRWFjaCgobm9kZTogSFRNTEVsZW1lbnQpID0+IG5vZGUudGFiSW5kZXggPSBuZXdUYWJJbmRleCk7XG5cdFx0XHRcdGNlbGwudGFiSW5kZXggPSBuZXdUYWJJbmRleDtcblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLnJlc2V0KCk7XG5cdH1cblxuXHQvKipcblx0ICogUmVzZXRzIHRoZSBtb2RlbHMgZm9jdXMgcG9zaXRpb25cblx0ICovXG5cdHJlc2V0KCkge1xuXHRcdHRoaXMucm93U3ViamVjdC5uZXh0KHsgY3VycmVudDogMCwgcHJldmlvdXM6IC0xIH0pO1xuXHRcdHRoaXMuY29sdW1uU3ViamVjdC5uZXh0KHsgY3VycmVudDogMCwgcHJldmlvdXM6IC0xIH0pO1xuXHR9XG59XG4iXX0=