ngx-extended-pdf-viewer
Version:
Embedding PDF files in your Angular application. Highly configurable viewer including the toolbar, sidebar, and all the features you're used to.
236 lines • 27.3 kB
JavaScript
import { Injectable } from '@angular/core';
import * as i0 from "@angular/core";
export class FocusManagementService {
previousActiveElement = null;
ariaLiveRegion = null;
activeDialogId = null;
keydownHandler = null;
constructor() {
this.initializeAriaLiveRegion();
}
/**
* Initializes a hidden aria-live region for screen reader announcements
*/
initializeAriaLiveRegion() {
if (typeof document === 'undefined') {
return; // SSR guard
}
this.ariaLiveRegion = document.createElement('div');
this.ariaLiveRegion.setAttribute('aria-live', 'polite');
this.ariaLiveRegion.setAttribute('aria-atomic', 'true');
this.ariaLiveRegion.setAttribute('class', 'sr-only');
this.ariaLiveRegion.style.position = 'absolute';
this.ariaLiveRegion.style.left = '-10000px';
this.ariaLiveRegion.style.width = '1px';
this.ariaLiveRegion.style.height = '1px';
this.ariaLiveRegion.style.overflow = 'hidden';
if (document.body) {
document.body.appendChild(this.ariaLiveRegion);
}
else {
// If body is not ready yet, wait for DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
if (this.ariaLiveRegion) {
document.body.appendChild(this.ariaLiveRegion);
}
});
}
}
/**
* Announces a message to screen readers via aria-live region
* @param message The message to announce
*/
announce(message) {
if (!this.ariaLiveRegion) {
return;
}
// Clear previous message
this.ariaLiveRegion.textContent = '';
// Announce new message after a brief delay to ensure screen readers pick it up
setTimeout(() => {
if (this.ariaLiveRegion) {
this.ariaLiveRegion.textContent = message;
}
}, 100);
}
/**
* Moves focus to the first focusable element within a dialog
* @param dialogId The ID of the dialog element
* @param announceMessage Optional message to announce when dialog opens
* @param buttonId Optional ID of the button that triggered the dialog (for reliable focus return)
*/
moveFocusToDialog(dialogId, announceMessage, buttonId) {
if (typeof document === 'undefined') {
return; // SSR guard
}
// Store the button element for reliable focus return
// Use buttonId if provided, otherwise fall back to activeElement
if (buttonId) {
const button = document.getElementById(buttonId);
if (button) {
this.previousActiveElement = button;
}
}
else {
const activeElement = document.activeElement;
if (activeElement && activeElement !== document.body) {
this.previousActiveElement = activeElement;
}
}
// Find dialog and first focusable element
const dialog = document.getElementById(dialogId);
if (!dialog) {
console.warn(`Dialog with ID "${dialogId}" not found`);
return;
}
// Check if dialog is visible
if (dialog.classList.contains('hidden') || dialog.style.display === 'none') {
console.warn(`Dialog "${dialogId}" is not visible`);
return;
}
// Track active dialog and set up focus cycling
this.activeDialogId = dialogId;
this.setupFocusCycling(dialog);
const firstFocusable = this.findFirstFocusableElement(dialog);
if (firstFocusable) {
// Small delay to ensure dialog is fully rendered
setTimeout(() => {
firstFocusable.focus();
}, 50);
}
// Announce dialog opening to screen readers
if (announceMessage) {
this.announce(announceMessage);
}
}
/**
* Sets up focus cycling so that tabbing past the last element returns to the toolbar
* @param dialog The dialog element
*/
setupFocusCycling(dialog) {
// Clean up any existing handler
this.cleanupFocusCycling();
this.keydownHandler = (event) => {
if (event.key !== 'Tab') {
return;
}
const focusableElements = this.getAllFocusableElements(dialog);
if (focusableElements.length === 0) {
return;
}
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
const activeElement = document.activeElement;
// Tab on last element -> go to toolbar (previous element that opened the dialog)
if (!event.shiftKey && activeElement === lastElement) {
event.preventDefault();
if (this.previousActiveElement) {
this.previousActiveElement.focus();
}
}
// Shift+Tab on first element -> go to last element in dialog
else if (event.shiftKey && activeElement === firstElement) {
event.preventDefault();
lastElement.focus();
}
};
document.addEventListener('keydown', this.keydownHandler);
}
/**
* Cleans up focus cycling event listeners
*/
cleanupFocusCycling() {
if (this.keydownHandler) {
document.removeEventListener('keydown', this.keydownHandler);
this.keydownHandler = null;
}
this.activeDialogId = null;
}
/**
* Gets all focusable elements within a container
* @param container The container element
* @returns Array of focusable elements
*/
getAllFocusableElements(container) {
const focusableSelectors = [
'a[href]',
'area[href]',
'input:not([disabled]):not([type="hidden"])',
'select:not([disabled])',
'textarea:not([disabled])',
'button:not([disabled])',
'iframe',
'object',
'embed',
'[contenteditable]',
'[tabindex]:not([tabindex="-1"])',
].join(',');
const elements = container.querySelectorAll(focusableSelectors);
return Array.from(elements).filter((el) => this.isVisible(el));
}
/**
* Returns focus to the previously focused element (typically the button that opened the dialog)
* @param announceMessage Optional message to announce when dialog closes
*/
returnFocusToPrevious(announceMessage) {
// Clean up focus cycling
this.cleanupFocusCycling();
if (this.previousActiveElement) {
this.previousActiveElement.focus();
this.previousActiveElement = null;
}
// Announce dialog closing to screen readers
if (announceMessage) {
this.announce(announceMessage);
}
}
/**
* Finds the first focusable element within a container
* @param container The container element to search within
* @returns The first focusable element or null
*/
findFirstFocusableElement(container) {
if (!container) {
return null;
}
const focusableSelectors = [
'a[href]',
'area[href]',
'input:not([disabled]):not([type="hidden"])',
'select:not([disabled])',
'textarea:not([disabled])',
'button:not([disabled])',
'iframe',
'object',
'embed',
'[contenteditable]',
'[tabindex]:not([tabindex="-1"])',
].join(',');
const focusableElements = container.querySelectorAll(focusableSelectors);
// Return first visible and focusable element
for (const element of Array.from(focusableElements)) {
if (this.isVisible(element)) {
return element;
}
}
return null;
}
/**
* Checks if an element is visible
* @param element The element to check
* @returns True if the element is visible
*/
isVisible(element) {
const style = window.getComputedStyle(element);
return style.display !== 'none' && style.visibility !== 'hidden' && element.offsetParent !== null;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FocusManagementService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FocusManagementService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FocusManagementService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9jdXMtbWFuYWdlbWVudC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWV4dGVuZGVkLXBkZi12aWV3ZXIvc3JjL2xpYi9mb2N1cy1tYW5hZ2VtZW50LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQzs7QUFLM0MsTUFBTSxPQUFPLHNCQUFzQjtJQUN6QixxQkFBcUIsR0FBdUIsSUFBSSxDQUFDO0lBQ2pELGNBQWMsR0FBMEIsSUFBSSxDQUFDO0lBQzdDLGNBQWMsR0FBa0IsSUFBSSxDQUFDO0lBQ3JDLGNBQWMsR0FBNEMsSUFBSSxDQUFDO0lBRXZFO1FBQ0UsSUFBSSxDQUFDLHdCQUF3QixFQUFFLENBQUM7SUFDbEMsQ0FBQztJQUVEOztPQUVHO0lBQ0ssd0JBQXdCO1FBQzlCLElBQUksT0FBTyxRQUFRLEtBQUssV0FBVyxFQUFFO1lBQ25DLE9BQU8sQ0FBQyxZQUFZO1NBQ3JCO1FBRUQsSUFBSSxDQUFDLGNBQWMsR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxjQUFjLENBQUMsWUFBWSxDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUN4RCxJQUFJLENBQUMsY0FBYyxDQUFDLFlBQVksQ0FBQyxhQUFhLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDeEQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ3JELElBQUksQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLFFBQVEsR0FBRyxVQUFVLENBQUM7UUFDaEQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsSUFBSSxHQUFHLFVBQVUsQ0FBQztRQUM1QyxJQUFJLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxLQUFLLEdBQUcsS0FBSyxD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IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290Jyxcbn0pXG5leHBvcnQgY2xhc3MgRm9jdXNNYW5hZ2VtZW50U2VydmljZSB7XG4gIHByaXZhdGUgcHJldmlvdXNBY3RpdmVFbGVtZW50OiBIVE1MRWxlbWVudCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGFyaWFMaXZlUmVnaW9uOiBIVE1MRGl2RWxlbWVudCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGFjdGl2ZURpYWxvZ0lkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBrZXlkb3duSGFuZGxlcjogKChldmVudDogS2V5Ym9hcmRFdmVudCkgPT4gdm9pZCkgfCBudWxsID0gbnVsbDtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLmluaXRpYWxpemVBcmlhTGl2ZVJlZ2lvbigpO1xuICB9XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemVzIGEgaGlkZGVuIGFyaWEtbGl2ZSByZWdpb24gZm9yIHNjcmVlbiByZWFkZXIgYW5ub3VuY2VtZW50c1xuICAgKi9cbiAgcHJpdmF0ZSBpbml0aWFsaXplQXJpYUxpdmVSZWdpb24oKTogdm9pZCB7XG4gICAgaWYgKHR5cGVvZiBkb2N1bWVudCA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHJldHVybjsgLy8gU1NSIGd1YXJkXG4gICAgfVxuXG4gICAgdGhpcy5hcmlhTGl2ZVJlZ2lvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgIHRoaXMuYXJpYUxpdmVSZWdpb24uc2V0QXR0cmlidXRlKCdhcmlhLWxpdmUnLCAncG9saXRlJyk7XG4gICAgdGhpcy5hcmlhTGl2ZVJlZ2lvbi5zZXRBdHRyaWJ1dGUoJ2FyaWEtYXRvbWljJywgJ3RydWUnKTtcbiAgICB0aGlzLmFyaWFMaXZlUmVnaW9uLnNldEF0dHJpYnV0ZSgnY2xhc3MnLCAnc3Itb25seScpO1xuICAgIHRoaXMuYXJpYUxpdmVSZWdpb24uc3R5bGUucG9zaXRpb24gPSAnYWJzb2x1dGUnO1xuICAgIHRoaXMuYXJpYUxpdmVSZWdpb24uc3R5bGUubGVmdCA9ICctMTAwMDBweCc7XG4gICAgdGhpcy5hcmlhTGl2ZVJlZ2lvbi5zdHlsZS53aWR0aCA9ICcxcHgnO1xuICAgIHRoaXMuYXJpYUxpdmVSZWdpb24uc3R5bGUuaGVpZ2h0ID0gJzFweCc7XG4gICAgdGhpcy5hcmlhTGl2ZVJlZ2lvbi5zdHlsZS5vdmVyZmxvdyA9ICdoaWRkZW4nO1xuXG4gICAgaWYgKGRvY3VtZW50LmJvZHkpIHtcbiAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQodGhpcy5hcmlhTGl2ZVJlZ2lvbik7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIElmIGJvZHkgaXMgbm90IHJlYWR5IHlldCwgd2FpdCBmb3IgRE9NQ29udGVudExvYWRlZFxuICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsICgpID0+IHtcbiAgICAgICAgaWYgKHRoaXMuYXJpYUxpdmVSZWdpb24pIHtcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHRoaXMuYXJpYUxpdmVSZWdpb24pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQW5ub3VuY2VzIGEgbWVzc2FnZSB0byBzY3JlZW4gcmVhZGVycyB2aWEgYXJpYS1saXZlIHJlZ2lvblxuICAgKiBAcGFyYW0gbWVzc2FnZSBUaGUgbWVzc2FnZSB0byBhbm5vdW5jZVxuICAgKi9cbiAgcHVibGljIGFubm91bmNlKG1lc3NhZ2U6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICghdGhpcy5hcmlhTGl2ZVJlZ2lvbikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIENsZWFyIHByZXZpb3VzIG1lc3NhZ2VcbiAgICB0aGlzLmFyaWFMaXZlUmVnaW9uLnRleHRDb250ZW50ID0gJyc7XG5cbiAgICAvLyBBbm5vdW5jZSBuZXcgbWVzc2FnZSBhZnRlciBhIGJyaWVmIGRlbGF5IHRvIGVuc3VyZSBzY3JlZW4gcmVhZGVycyBwaWNrIGl0IHVwXG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBpZiAodGhpcy5hcmlhTGl2ZVJlZ2lvbikge1xuICAgICAgICB0aGlzLmFyaWFMaXZlUmVnaW9uLnRleHRDb250ZW50ID0gbWVzc2FnZTtcbiAgICAgIH1cbiAgICB9LCAxMDApO1xuICB9XG5cbiAgLyoqXG4gICAqIE1vdmVzIGZvY3VzIHRvIHRoZSBmaXJzdCBmb2N1c2FibGUgZWxlbWVudCB3aXRoaW4gYSBkaWFsb2dcbiAgICogQHBhcmFtIGRpYWxvZ0lkIFRoZSBJRCBvZiB0aGUgZGlhbG9nIGVsZW1lbnRcbiAgICogQHBhcmFtIGFubm91bmNlTWVzc2FnZSBPcHRpb25hbCBtZXNzYWdlIHRvIGFubm91bmNlIHdoZW4gZGlhbG9nIG9wZW5zXG4gICAqIEBwYXJhbSBidXR0b25JZCBPcHRpb25hbCBJRCBvZiB0aGUgYnV0dG9uIHRoYXQgdHJpZ2dlcmVkIHRoZSBkaWFsb2cgKGZvciByZWxpYWJsZSBmb2N1cyByZXR1cm4pXG4gICAqL1xuICBwdWJsaWMgbW92ZUZvY3VzVG9EaWFsb2coZGlhbG9nSWQ6IHN0cmluZywgYW5ub3VuY2VNZXNzYWdlPzogc3RyaW5nLCBidXR0b25JZD86IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0eXBlb2YgZG9jdW1lbnQgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICByZXR1cm47IC8vIFNTUiBndWFyZFxuICAgIH1cblxuICAgIC8vIFN0b3JlIHRoZSBidXR0b24gZWxlbWVudCBmb3IgcmVsaWFibGUgZm9jdXMgcmV0dXJuXG4gICAgLy8gVXNlIGJ1dHRvbklkIGlmIHByb3ZpZGVkLCBvdGhlcndpc2UgZmFsbCBiYWNrIHRvIGFjdGl2ZUVsZW1lbnRcbiAgICBpZiAoYnV0dG9uSWQpIHtcbiAgICAgIGNvbnN0IGJ1dHRvbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGJ1dHRvbklkKTtcbiAgICAgIGlmIChidXR0b24pIHtcbiAgICAgICAgdGhpcy5wcmV2aW91c0FjdGl2ZUVsZW1lbnQgPSBidXR0b247XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGFjdGl2ZUVsZW1lbnQgPSBkb2N1bWVudC5hY3RpdmVFbGVtZW50IGFzIEhUTUxFbGVtZW50O1xuICAgICAgaWYgKGFjdGl2ZUVsZW1lbnQgJiYgYWN0aXZlRWxlbWVudCAhPT0gZG9jdW1lbnQuYm9keSkge1xuICAgICAgICB0aGlzLnByZXZpb3VzQWN0aXZlRWxlbWVudCA9IGFjdGl2ZUVsZW1lbnQ7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRmluZCBkaWFsb2cgYW5kIGZpcnN0IGZvY3VzYWJsZSBlbGVtZW50XG4gICAgY29uc3QgZGlhbG9nID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoZGlhbG9nSWQpO1xuICAgIGlmICghZGlhbG9nKSB7XG4gICAgICBjb25zb2xlLndhcm4oYERpYWxvZyB3aXRoIElEIFwiJHtkaWFsb2dJZH1cIiBub3QgZm91bmRgKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBpZiBkaWFsb2cgaXMgdmlzaWJsZVxuICAgIGlmIChkaWFsb2cuY2xhc3NMaXN0LmNvbnRhaW5zKCdoaWRkZW4nKSB8fCBkaWFsb2cuc3R5bGUuZGlzcGxheSA9PT0gJ25vbmUnKSB7XG4gICAgICBjb25zb2xlLndhcm4oYERpYWxvZyBcIiR7ZGlhbG9nSWR9XCIgaXMgbm90IHZpc2libGVgKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBUcmFjayBhY3RpdmUgZGlhbG9nIGFuZCBzZXQgdXAgZm9jdXMgY3ljbGluZ1xuICAgIHRoaXMuYWN0aXZlRGlhbG9nSWQgPSBkaWFsb2dJZDtcbiAgICB0aGlzLnNldHVwRm9jdXNDeWNsaW5nKGRpYWxvZyk7XG5cbiAgICBjb25zdCBmaXJzdEZvY3VzYWJsZSA9IHRoaXMuZmluZEZpcnN0Rm9jdXNhYmxlRWxlbWVudChkaWFsb2cpO1xuXG4gICAgaWYgKGZpcnN0Rm9jdXNhYmxlKSB7XG4gICAgICAvLyBTbWFsbCBkZWxheSB0byBlbnN1cmUgZGlhbG9nIGlzIGZ1bGx5IHJlbmRlcmVkXG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgZmlyc3RGb2N1c2FibGUuZm9jdXMoKTtcbiAgICAgIH0sIDUwKTtcbiAgICB9XG5cbiAgICAvLyBBbm5vdW5jZSBkaWFsb2cgb3BlbmluZyB0byBzY3JlZW4gcmVhZGVyc1xuICAgIGlmIChhbm5vdW5jZU1lc3NhZ2UpIHtcbiAgICAgIHRoaXMuYW5ub3VuY2UoYW5ub3VuY2VNZXNzYWdlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2V0cyB1cCBmb2N1cyBjeWNsaW5nIHNvIHRoYXQgdGFiYmluZyBwYXN0IHRoZSBsYXN0IGVsZW1lbnQgcmV0dXJucyB0byB0aGUgdG9vbGJhclxuICAgKiBAcGFyYW0gZGlhbG9nIFRoZSBkaWFsb2cgZWxlbWVudFxuICAgKi9cbiAgcHJpdmF0ZSBzZXR1cEZvY3VzQ3ljbGluZyhkaWFsb2c6IEhUTUxFbGVtZW50KTogdm9pZCB7XG4gICAgLy8gQ2xlYW4gdXAgYW55IGV4aXN0aW5nIGhhbmRsZXJcbiAgICB0aGlzLmNsZWFudXBGb2N1c0N5Y2xpbmcoKTtcblxuICAgIHRoaXMua2V5ZG93bkhhbmRsZXIgPSAoZXZlbnQ6IEtleWJvYXJkRXZlbnQpID0+IHtcbiAgICAgIGlmIChldmVudC5rZXkgIT09ICdUYWInKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgY29uc3QgZm9jdXNhYmxlRWxlbWVudHMgPSB0aGlzLmdldEFsbEZvY3VzYWJsZUVsZW1lbnRzKGRpYWxvZyk7XG4gICAgICBpZiAoZm9jdXNhYmxlRWxlbWVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgY29uc3QgZmlyc3RFbGVtZW50ID0gZm9jdXNhYmxlRWxlbWVudHNbMF07XG4gICAgICBjb25zdCBsYXN0RWxlbWVudCA9IGZvY3VzYWJsZUVsZW1lbnRzW2ZvY3VzYWJsZUVsZW1lbnRzLmxlbmd0aCAtIDFdO1xuICAgICAgY29uc3QgYWN0aXZlRWxlbWVudCA9IGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQ7XG5cbiAgICAgIC8vIFRhYiBvbiBsYXN0IGVsZW1lbnQgLT4gZ28gdG8gdG9vbGJhciAocHJldmlvdXMgZWxlbWVudCB0aGF0IG9wZW5lZCB0aGUgZGlhbG9nKVxuICAgICAgaWYgKCFldmVudC5zaGlmdEtleSAmJiBhY3RpdmVFbGVtZW50ID09PSBsYXN0RWxlbWVudCkge1xuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBpZiAodGhpcy5wcmV2aW91c0FjdGl2ZUVsZW1lbnQpIHtcbiAgICAgICAgICB0aGlzLnByZXZpb3VzQWN0aXZlRWxlbWVudC5mb2N1cygpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBTaGlmdCtUYWIgb24gZmlyc3QgZWxlbWVudCAtPiBnbyB0byBsYXN0IGVsZW1lbnQgaW4gZGlhbG9nXG4gICAgICBlbHNlIGlmIChldmVudC5zaGlmdEtleSAmJiBhY3RpdmVFbGVtZW50ID09PSBmaXJzdEVsZW1lbnQpIHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgbGFzdEVsZW1lbnQuZm9jdXMoKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigna2V5ZG93bicsIHRoaXMua2V5ZG93bkhhbmRsZXIpO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFucyB1cCBmb2N1cyBjeWNsaW5nIGV2ZW50IGxpc3RlbmVyc1xuICAgKi9cbiAgcHJpdmF0ZSBjbGVhbnVwRm9jdXNDeWNsaW5nKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmtleWRvd25IYW5kbGVyKSB7XG4gICAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdrZXlkb3duJywgdGhpcy5rZXlkb3duSGFuZGxlcik7XG4gICAgICB0aGlzLmtleWRvd25IYW5kbGVyID0gbnVsbDtcbiAgICB9XG4gICAgdGhpcy5hY3RpdmVEaWFsb2dJZCA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogR2V0cyBhbGwgZm9jdXNhYmxlIGVsZW1lbnRzIHdpdGhpbiBhIGNvbnRhaW5lclxuICAgKiBAcGFyYW0gY29udGFpbmVyIFRoZSBjb250YWluZXIgZWxlbWVudFxuICAgKiBAcmV0dXJucyBBcnJheSBvZiBmb2N1c2FibGUgZWxlbWVudHNcbiAgICovXG4gIHByaXZhdGUgZ2V0QWxsRm9jdXNhYmxlRWxlbWVudHMoY29udGFpbmVyOiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50W10ge1xuICAgIGNvbnN0IGZvY3VzYWJsZVNlbGVjdG9ycyA9IFtcbiAgICAgICdhW2hyZWZdJyxcbiAgICAgICdhcmVhW2hyZWZdJyxcbiAgICAgICdpbnB1dDpub3QoW2Rpc2FibGVkXSk6bm90KFt0eXBlPVwiaGlkZGVuXCJdKScsXG4gICAgICAnc2VsZWN0Om5vdChbZGlzYWJsZWRdKScsXG4gICAgICAndGV4dGFyZWE6bm90KFtkaXNhYmxlZF0pJyxcbiAgICAgICdidXR0b246bm90KFtkaXNhYmxlZF0pJyxcbiAgICAgICdpZnJhbWUnLFxuICAgICAgJ29iamVjdCcsXG4gICAgICAnZW1iZWQnLFxuICAgICAgJ1tjb250ZW50ZWRpdGFibGVdJyxcbiAgICAgICdbdGFiaW5kZXhdOm5vdChbdGFiaW5kZXg9XCItMVwiXSknLFxuICAgIF0uam9pbignLCcpO1xuXG4gICAgY29uc3QgZWxlbWVudHMgPSBjb250YWluZXIucXVlcnlTZWxlY3RvckFsbDxIVE1MRWxlbWVudD4oZm9jdXNhYmxlU2VsZWN0b3JzKTtcbiAgICByZXR1cm4gQXJyYXkuZnJvbShlbGVtZW50cykuZmlsdGVyKChlbCkgPT4gdGhpcy5pc1Zpc2libGUoZWwpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGZvY3VzIHRvIHRoZSBwcmV2aW91c2x5IGZvY3VzZWQgZWxlbWVudCAodHlwaWNhbGx5IHRoZSBidXR0b24gdGhhdCBvcGVuZWQgdGhlIGRpYWxvZylcbiAgICogQHBhcmFtIGFubm91bmNlTWVzc2FnZSBPcHRpb25hbCBtZXNzYWdlIHRvIGFubm91bmNlIHdoZW4gZGlhbG9nIGNsb3Nlc1xuICAgKi9cbiAgcHVibGljIHJldHVybkZvY3VzVG9QcmV2aW91cyhhbm5vdW5jZU1lc3NhZ2U/OiBzdHJpbmcpOiB2b2lkIHtcbiAgICAvLyBDbGVhbiB1cCBmb2N1cyBjeWNsaW5nXG4gICAgdGhpcy5jbGVhbnVwRm9jdXNDeWNsaW5nKCk7XG5cbiAgICBpZiAodGhpcy5wcmV2aW91c0FjdGl2ZUVsZW1lbnQpIHtcbiAgICAgIHRoaXMucHJldmlvdXNBY3RpdmVFbGVtZW50LmZvY3VzKCk7XG4gICAgICB0aGlzLnByZXZpb3VzQWN0aXZlRWxlbWVudCA9IG51bGw7XG4gICAgfVxuXG4gICAgLy8gQW5ub3VuY2UgZGlhbG9nIGNsb3NpbmcgdG8gc2NyZWVuIHJlYWRlcnNcbiAgICBpZiAoYW5ub3VuY2VNZXNzYWdlKSB7XG4gICAgICB0aGlzLmFubm91bmNlKGFubm91bmNlTWVzc2FnZSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZpbmRzIHRoZSBmaXJzdCBmb2N1c2FibGUgZWxlbWVudCB3aXRoaW4gYSBjb250YWluZXJcbiAgICogQHBhcmFtIGNvbnRhaW5lciBUaGUgY29udGFpbmVyIGVsZW1lbnQgdG8gc2VhcmNoIHdpdGhpblxuICAgKiBAcmV0dXJucyBUaGUgZmlyc3QgZm9jdXNhYmxlIGVsZW1lbnQgb3IgbnVsbFxuICAgKi9cbiAgcHJpdmF0ZSBmaW5kRmlyc3RGb2N1c2FibGVFbGVtZW50KGNvbnRhaW5lcjogSFRNTEVsZW1lbnQgfCBudWxsKTogSFRNTEVsZW1lbnQgfCBudWxsIHtcbiAgICBpZiAoIWNvbnRhaW5lcikge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgZm9jdXNhYmxlU2VsZWN0b3JzID0gW1xuICAgICAgJ2FbaHJlZl0nLFxuICAgICAgJ2FyZWFbaHJlZl0nLFxuICAgICAgJ2lucHV0Om5vdChbZGlzYWJsZWRdKTpub3QoW3R5cGU9XCJoaWRkZW5cIl0pJyxcbiAgICAgICdzZWxlY3Q6bm90KFtkaXNhYmxlZF0pJyxcbiAgICAgICd0ZXh0YXJlYTpub3QoW2Rpc2FibGVkXSknLFxuICAgICAgJ2J1dHRvbjpub3QoW2Rpc2FibGVkXSknLFxuICAgICAgJ2lmcmFtZScsXG4gICAgICAnb2JqZWN0JyxcbiAgICAgICdlbWJlZCcsXG4gICAgICAnW2NvbnRlbnRlZGl0YWJsZV0nLFxuICAgICAgJ1t0YWJpbmRleF06bm90KFt0YWJpbmRleD1cIi0xXCJdKScsXG4gICAgXS5qb2luKCcsJyk7XG5cbiAgICBjb25zdCBmb2N1c2FibGVFbGVtZW50cyA9IGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yQWxsPEhUTUxFbGVtZW50Pihmb2N1c2FibGVTZWxlY3RvcnMpO1xuXG4gICAgLy8gUmV0dXJuIGZpcnN0IHZpc2libGUgYW5kIGZvY3VzYWJsZSBlbGVtZW50XG4gICAgZm9yIChjb25zdCBlbGVtZW50IG9mIEFycmF5LmZyb20oZm9jdXNhYmxlRWxlbWVudHMpKSB7XG4gICAgICBpZiAodGhpcy5pc1Zpc2libGUoZWxlbWVudCkpIHtcbiAgICAgICAgcmV0dXJuIGVsZW1lbnQ7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIGlmIGFuIGVsZW1lbnQgaXMgdmlzaWJsZVxuICAgKiBAcGFyYW0gZWxlbWVudCBUaGUgZWxlbWVudCB0byBjaGVja1xuICAgKiBAcmV0dXJucyBUcnVlIGlmIHRoZSBlbGVtZW50IGlzIHZpc2libGVcbiAgICovXG4gIHByaXZhdGUgaXNWaXNpYmxlKGVsZW1lbnQ6IEhUTUxFbGVtZW50KTogYm9vbGVhbiB7XG4gICAgY29uc3Qgc3R5bGUgPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShlbGVtZW50KTtcbiAgICByZXR1cm4gc3R5bGUuZGlzcGxheSAhPT0gJ25vbmUnICYmIHN0eWxlLnZpc2liaWxpdHkgIT09ICdoaWRkZW4nICYmIGVsZW1lbnQub2Zmc2V0UGFyZW50ICE9PSBudWxsO1xuICB9XG59XG4iXX0=