UNPKG

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
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=