UNPKG

moh-common-lib

Version:

A library of Angular components, services, and styles for B.C. Government Ministry of Health (MoH).

474 lines 50.8 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ import { forwardRef, Component, EventEmitter, Input, Output, ViewChild, HostListener, ElementRef } from '@angular/core'; import { ModalDirective } from 'ngx-bootstrap/modal'; import { of } from 'rxjs'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { CommonLogger } from '../../services/logger.service'; import { AbstractHttpService } from '../../services/abstract-api-service'; import { ControlContainer, NgForm, NG_VALUE_ACCESSOR } from '@angular/forms'; /** * Consent Modal is a Modal with the Information or Notice. It can be used to get the User's consent an * then proceed with the application. It also makes an API call to the SPA-ENV server to see if the app is under * maintenance. * * * \@example * <common-consent-modal #mspConsentModal body='Body Of Consent' * title='Notice' [application]="mspAccountApp" * processName='MSP' * agreeLabel='I have read and understand this info' * (onClose)="addressChangeChkBx.focus()"> * </common-consent-modal> * @export * @record */ export function ISpaEnvResponse() { } if (false) { /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_MSP_MAINTENANCE_FLAG; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_MSP_MAINTENANCE_MESSAGE; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_ACL_MAINTENANCE_FLAG; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_ACL_MAINTENANCE_MESSAGE; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_SUPPBEN_MAINTENANCE_FLAG; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_SUPPBEN_MAINTENANCE_MESSAGE; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_SUPPBEN_MAINTENANCE_START; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_SUPPBEN_MAINTENANCE_END; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_PACUTOFF_MAINTENANCE_FLAG; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_PACUTOFF_MAINTENANCE_MESSAGE; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_PACUTOFF_MAINTENANCE_START; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_NOW; /** @type {?} */ ISpaEnvResponse.prototype.SPA_ENV_PACUTOFF_MAINTENANCE_END; } // Disabling tslint for @example below. // tslint:disable:max-line-length /** * ConsentModalComponent, aka the "Information Collection Notice", is a modal * designed to be shown at the beginning of an application. It is a boilerplate * checkbox to consent to information collection. * * Currently this component requires the body to be manually set as a child * element (via transclusion) * * TODO - Set default body if none is passed in. * * \@example * <common-consent-modal #consentModal [isUnderMaintenance]="false" * title="Information collection notice" * agreeLabel="I have read and understand this information" * processName="processName" * (accept)="accountLetterApplication.infoCollectionAgreement = $event; saveApplication($event)"> * <p><strong>Keep your personal information secure – especially when using a shared device like a computer at a library, school or café.</strong> To delete any information that was entered, either complete the application and submit it or, if you don’t finish, close the web browser.</p><p><strong>Need to take a break and come back later?</strong> The data you enter on this form is saved locally to the computer or device you are using until you close the web browser or submit your application.</p><p><strong>Information in this application is collected by the Ministry of Health</strong> under section 26(a), (c) and (e) of the Freedom of Information and Protection of Privacy Act and will be used to determine eligibility for provincial health care benefits in BC and administer Premium Assistance. Should you have any questions about the collection of this personal information please <a href="http://www2.gov.bc.ca/gov/content/health/health-drug-coverage/msp/bc-residents-contact-us" target="_blank">contact Health Insurance BC <i class="fa fa-external-link" aria-hidden="true"></i></a>.</p> * </common-consent-modal> * * * // Component code - Remove backticks when copying (necessary to render docs) * `\@ViewChild('consentModal') consentModal: ConsentModalComponent` * ... * openModal(){ * this.consentModal.showFullSizeView(); * } */ export class ConsentModalComponent extends AbstractHttpService { /** * @param {?} http * @param {?} logService */ constructor(http, logService) { super(http); this.http = http; this.logService = logService; this._headers = new HttpHeaders(); /** * If `isUnderMaintenance` is true, then this will automatically try and * make a request to the SPA ENV server to determine if it's in a * maintenance window. If your application determines this manually, leave * this alone. */ this.isUnderMaintenance = false; // = '<p><strong>Keep your personal information secure – especially when using a shared device like a computer at a library, school or café.</strong> To delete any information that was entered, either complete the application and submit it or, if you don’t finish, close the web browser.</p><p><strong>Need to take a break and come back later?</strong> The data you enter on this form is saved locally to the computer or device you are using until you close the web browser or submit your application.</p><p><strong>Information in this application is collected by the Ministry of Health</strong> under section 26(a), (c) and (e) of the Freedom of Information and Protection of Privacy Act and will be used to determine eligibility for provincial health care benefits in BC and administer Premium Assistance. Should you have any questions about the collection of this personal information please <a href="http://www2.gov.bc.ca/gov/content/health/health-drug-coverage/msp/bc-residents-contact-us" target="_blank">contact Health Insurance BC <i class="fa fa-external-link" aria-hidden="true"></i></a>.</p>'; this.agreeLabel = 'I have read and understand this info'; this.continueButton = 'Continue'; this.maintenanceFlag = 'false'; this.url = '/msp/api/env'; this.close = new EventEmitter(); this.cutOffDate = new EventEmitter(); this.accept = new EventEmitter(); /** * Used in cases where we have custom form controls inside NgContent that we * wish to be satisifed before user can continue through modal. */ this.disableContinue = false; this.spaEnvRes = (/** @type {?} */ ({})); this.closed = false; // TODO: This should eventually be pulled out of the common library as it pertains to MSP-specific code. // tslint:disable-next-line:max-line-length this._applicationHeaderMap = new Map([ ['ACL', '{"SPA_ENV_ACL_MAINTENANCE_FLAG":"","SPA_ENV_ACL_MAINTENANCE_MESSAGE":""}'], ['MSP', '{"SPA_ENV_MSP_MAINTENANCE_FLAG":"","SPA_ENV_MSP_MAINTENANCE_MESSAGE":""}'], ['PA', '{"SPA_ENV_PACUTOFF_MAINTENANCE_START":"","SPA_ENV_PACUTOFF_MAINTENANCE_END":"","SPA_ENV_NOW":"","SPA_ENV_PACUTOFF_MAINTENANCE_FLAG":"","SPA_ENV_PACUTOFF_MAINTENANCE_MESSAGE":""}'], ['SUPPBEN', '{"SPA_ENV_SUPPBEN_MAINTENANCE_START":"","SPA_ENV_SUPPBEN_MAINTENANCE_END":"","SPA_ENV_NOW":"","SPA_ENV_SUPPBEN_MAINTENANCE_FLAG":"","SPA_ENV_SUPPBEN_MAINTENANCE_MESSAGE":"","SPA_ENV_PACUTOFF_MAINTENANCE_START":"","SPA_ENV_PACUTOFF_MAINTENANCE_END":""}'], ]); this.agreeCheck = false; this._onChange = (/** * @param {?} _ * @return {?} */ (_) => { }); this._onTouched = (/** * @return {?} */ () => { }); } /** * @return {?} */ ngOnInit() { // Called after ngOnInit when the component's or directive's content has been initialized. // Add 'implements AfterContentInit' to the class. if (this.isUnderMaintenance) { this.inMaintenance(); } } /** * @return {?} */ ngAfterViewInit() { // Create an array of focusable elements from the contents of the modal this.focusableEls = Array.from(this.modalContents.nativeElement.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]')); // Initialize to the first focusable element this.focusedEl = this.focusableEls[0]; this.focusedEl.focus(); this.fullSizeViewModal.config.backdrop = true; } /** * @param {?} event * @return {?} */ handleKeyDown(event) { // Check that the modal is open if (!this.closed) { // Handle tabbing if (event.key === 'Tab') { // Prevent usual tabbing, manually set focus event.preventDefault(); if (event.shiftKey) { this.handleTabBackwards(); } else { this.handleTab(); } // Stop users from being able to escape the modal } else if (event.key === 'Escape') { event.preventDefault(); } } } // Api callout to get the message from the Rapid code /** * @param {?} rapidResponseCode * @return {?} */ sendSpaEnvServer(rapidResponseCode) { this._headers = new HttpHeaders({ 'SPA_ENV_NAME': rapidResponseCode }); return this.post(this.url, null); } // Move to next focusable element, if at last element, move to first /** * @return {?} */ handleTab() { /** @type {?} */ const position = this.focusableEls.indexOf(this.focusedEl); if (position === this.focusableEls.length - 1) { this.focusedEl = this.focusableEls[0]; } else { this.focusedEl = this.focusableEls[position + 1]; } this.focusedEl.focus(); } ; // Move to next focusable element, if at last element, move to first /** * @return {?} */ handleTabBackwards() { /** @type {?} */ const position = this.focusableEls.indexOf(this.focusedEl); if (position === 0) { this.focusedEl = this.focusableEls[this.focusableEls.length - 1]; } else { this.focusedEl = this.focusableEls[position - 1]; } this.focusedEl.focus(); } ; /** * Call this method to display the modal. * @return {?} */ showFullSizeView() { this.fullSizeViewModal.config.keyboard = false; this.fullSizeViewModal.show(); } /** * @return {?} */ continue() { this.accept.emit(true); this.fullSizeViewModal.hide(); this.close.emit(); this.closed = true; this._onChange(true); this._onTouched(); } /** * @protected * @param {?} error * @return {?} */ handleError(error) { if (error.error instanceof ErrorEvent) { // Client-side / network error occured console.error('MspMaintenanceService error: ', error.error.message); } else { // The backend returned an unsuccessful response code console.error(`MspMaintenanceService Backend returned error code: ${error.status}. Error body: ${error.error}`); } // A user facing erorr message /could/ go here; we shouldn't log dev info through the throwError observable return of(error); } /** * @return {?} */ inMaintenance() { /** @type {?} */ const headerName = this._applicationHeaderMap.get(this.processName); this.sendSpaEnvServer(headerName) .subscribe((/** * @param {?} response * @return {?} */ response => { this.spaEnvRes = (/** @type {?} */ (response)); // TODO: This should eventually be pulled out of the common library as it pertains to MSP-specific code. if (this.spaEnvRes.SPA_ENV_ACL_MAINTENANCE_FLAG === 'true') { this.maintenanceFlag = 'true'; this.maintenanceMessage = this.spaEnvRes.SPA_ENV_ACL_MAINTENANCE_MESSAGE; } else if (this.spaEnvRes.SPA_ENV_MSP_MAINTENANCE_FLAG === 'true') { this.maintenanceFlag = 'true'; this.maintenanceMessage = this.spaEnvRes.SPA_ENV_MSP_MAINTENANCE_MESSAGE; } else if (this.spaEnvRes.SPA_ENV_PACUTOFF_MAINTENANCE_FLAG === 'true') { this.maintenanceFlag = 'true'; this.maintenanceMessage = this.spaEnvRes.SPA_ENV_PACUTOFF_MAINTENANCE_MESSAGE; } else if (this.spaEnvRes.SPA_ENV_SUPPBEN_MAINTENANCE_FLAG === 'true') { this.maintenanceFlag = 'true'; this.maintenanceMessage = this.spaEnvRes.SPA_ENV_SUPPBEN_MAINTENANCE_MESSAGE; } if (this.spaEnvRes.SPA_ENV_PACUTOFF_MAINTENANCE_START) { this.cutOffDate.emit(this.spaEnvRes); } }), (/** * @param {?} error * @return {?} */ (error) => { this.logService.log({ event: 'ACL - SPA Env System Error', success: false, errMsg: 'ACL - SPA Env Rapid Response Error' + error }); })); } /** * @param {?} fn * @return {?} */ registerOnChange(fn) { this.accept.emit(fn); this._onChange = fn; } /** * @param {?} fn * @return {?} */ registerOnTouched(fn) { this._onTouched = fn; } /** * @param {?} value * @return {?} */ writeValue(value) { } /** * @return {?} */ isContinueDisabled() { /** @type {?} */ const disabled = !this.agreeCheck || this.disableContinue; /** @type {?} */ const hasTabbableContinue = this.focusableEls ? this.focusableEls.indexOf(this.continueButtonRef.nativeElement) !== -1 : false; // If it is tabbable but no longer should be, remove it if (hasTabbableContinue && disabled) this.focusableEls.pop(); // If it's not tabbable but it now should be, add it if (!hasTabbableContinue && !disabled) this.focusableEls.push(this.continueButtonRef.nativeElement); return disabled; } } ConsentModalComponent.decorators = [ { type: Component, args: [{ selector: 'common-consent-modal', template: "<div bsModal #fullSizeViewModal=\"bs-modal\" class=\"modal fade\" tabindex=\"-1\" role=\"dialog\"\r\n data-keyboard=\"false\"\r\n aria-labelledby=\"myLargeModalLabel\" aria-hidden=\"true\"\r\n [config]=\"{backdrop: 'static', ignoreBackdropClick: true}\">\r\n\r\n <div class=\"modal-dialog\" style=\"margin-top: 90px;\">\r\n <div #modalContents class=\"modal-content\">\r\n <!-- Header -->\r\n <div class=\"modal-header modal-header-primary\">\r\n <h2 id=\"myLargeModalLabel\">{{maintenanceFlag == 'true' ? 'Maintenance notice' : title}}</h2>\r\n </div>\r\n\r\n <!-- Modal Body -->\r\n <div class=\"modal-body\">\r\n\r\n <div *ngIf=\"maintenanceFlag == 'true'\">\r\n <h4>{{maintenanceMessage}}</h4>\r\n </div>\r\n <div *ngIf=\"maintenanceFlag != 'true'\">\r\n <ng-content></ng-content>\r\n <br>\r\n <div *ngIf=\"maintenanceFlag != 'true'\">\r\n <div class=\"form-check form-check-inline\">\r\n <input [ngModelOptions]=\"{standalone: true}\" class=\"input-lg form-check-input\" id=\"agree\" type=\"checkbox\"\r\n [(ngModel)]=\"agreeCheck\" (ngModelChange)='accept.emit($event);' >\r\n <label class=\"form-check-label\" for=\"agree\"><strong>{{agreeLabel}}</strong></label>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div *ngIf=\"maintenanceFlag != 'true'\" class=\"modal-footer\">\r\n <button #continueButtonRef [disabled]=\"isContinueDisabled()\" class=\"btn btn-block btn-primary pull-left\"\r\n type=\"submit\" (click)=\"continue()\">{{continueButton}}</button>\r\n\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", viewProviders: [ { provide: ControlContainer, useExisting: forwardRef((/** * @return {?} */ () => NgForm)) } ], providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: forwardRef((/** * @return {?} */ () => ConsentModalComponent)) } ], styles: [""] }] } ]; /** @nocollapse */ ConsentModalComponent.ctorParameters = () => [ { type: HttpClient }, { type: CommonLogger } ]; ConsentModalComponent.propDecorators = { processName: [{ type: Input }], isUnderMaintenance: [{ type: Input }], title: [{ type: Input }], body: [{ type: Input }], agreeLabel: [{ type: Input }], continueButton: [{ type: Input }], maintenanceFlag: [{ type: Input }], url: [{ type: Input }], fullSizeViewModal: [{ type: ViewChild, args: ['fullSizeViewModal',] }], modalContents: [{ type: ViewChild, args: ['modalContents',] }], continueButtonRef: [{ type: ViewChild, args: ['continueButtonRef',] }], close: [{ type: Output }], cutOffDate: [{ type: Output }], accept: [{ type: Output }], disableContinue: [{ type: Input }], handleKeyDown: [{ type: HostListener, args: ['window:keydown', ['$event'],] }] }; if (false) { /** * @type {?} * @protected */ ConsentModalComponent.prototype._headers; /** @type {?} */ ConsentModalComponent.prototype.processName; /** * If `isUnderMaintenance` is true, then this will automatically try and * make a request to the SPA ENV server to determine if it's in a * maintenance window. If your application determines this manually, leave * this alone. * @type {?} */ ConsentModalComponent.prototype.isUnderMaintenance; /** @type {?} */ ConsentModalComponent.prototype.title; /** @type {?} */ ConsentModalComponent.prototype.body; /** @type {?} */ ConsentModalComponent.prototype.agreeLabel; /** @type {?} */ ConsentModalComponent.prototype.continueButton; /** @type {?} */ ConsentModalComponent.prototype.maintenanceFlag; /** @type {?} */ ConsentModalComponent.prototype.url; /** @type {?} */ ConsentModalComponent.prototype.fullSizeViewModal; /** @type {?} */ ConsentModalComponent.prototype.modalContents; /** @type {?} */ ConsentModalComponent.prototype.continueButtonRef; /** @type {?} */ ConsentModalComponent.prototype.close; /** @type {?} */ ConsentModalComponent.prototype.cutOffDate; /** @type {?} */ ConsentModalComponent.prototype.accept; /** * Used in cases where we have custom form controls inside NgContent that we * wish to be satisifed before user can continue through modal. * @type {?} */ ConsentModalComponent.prototype.disableContinue; /** @type {?} */ ConsentModalComponent.prototype.spaEnvRes; /** @type {?} */ ConsentModalComponent.prototype.maintenanceMessage; /** * @type {?} * @protected */ ConsentModalComponent.prototype.focusableEls; /** * @type {?} * @protected */ ConsentModalComponent.prototype.focusedEl; /** * @type {?} * @protected */ ConsentModalComponent.prototype.closed; /** * @type {?} * @private */ ConsentModalComponent.prototype._applicationHeaderMap; /** @type {?} */ ConsentModalComponent.prototype.agreeCheck; /** @type {?} */ ConsentModalComponent.prototype._onChange; /** @type {?} */ ConsentModalComponent.prototype._onTouched; /** * @type {?} * @protected */ ConsentModalComponent.prototype.http; /** * @type {?} * @private */ ConsentModalComponent.prototype.logService; /* Skipping unhandled member: ;*/ /* Skipping unhandled member: ;*/ } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc2VudC1tb2RhbC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9tb2gtY29tbW9uLWxpYi8iLCJzb3VyY2VzIjpbImxpYi9jb21wb25lbnRzL2NvbnNlbnQtbW9kYWwvY29uc2VudC1tb2RhbC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBVSxZQUFZLEVBQWlCLFVBQVUsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUM5SSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDckQsT0FBTyxFQUFjLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN0QyxPQUFPLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBcUIsTUFBTSxzQkFBc0IsQ0FBQztBQUVsRixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDN0QsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDMUUsT0FBTyxFQUFFLGdCQUFnQixFQUF3QixNQUFNLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFtQm5HLHFDQWNDOzs7SUFiQyx1REFBcUM7O0lBQ3JDLDBEQUF3Qzs7SUFDeEMsdURBQXFDOztJQUNyQywwREFBd0M7O0lBQ3hDLDJEQUF5Qzs7SUFDekMsOERBQTRDOztJQUM1Qyw0REFBMEM7O0lBQzFDLDBEQUF3Qzs7SUFDeEMsNERBQTBDOztJQUMxQywrREFBNkM7O0lBQzdDLDZEQUEyQzs7SUFDM0Msc0NBQW9COztJQUNwQiwyREFBeUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUE2QzNDLE1BQU0sT0FBTyxxQkFBc0IsU0FBUSxtQkFBbUI7Ozs7O0lBa0Q1RCxZQUFzQixJQUFnQixFQUFXLFVBQXdCO1FB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bEIsS0FBSztpQ0FRTCxLQUFLO29CQUNMLEtBQUs7bUJBQ0wsS0FBSzt5QkFDTCxLQUFLOzZCQUNMLEtBQUs7OEJBQ0wsS0FBSztrQkFDTCxLQUFLO2dDQUNMLFNBQVMsU0FBQyxtQkFBbUI7NEJBQzdCLFNBQVMsU0FBQyxlQUFlO2dDQUN6QixTQUFTLFNBQUMsbUJBQW1CO29CQUM3QixNQUFNO3lCQUNOLE1BQU07cUJBQ04sTUFBTTs4QkFNTixLQUFLOzRCQTJDTCxZQUFZLFNBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxRQUFRLENBQUM7Ozs7Ozs7SUF0RTFDLHlDQUFvRDs7SUFDcEQsNENBQTZCOzs7Ozs7OztJQVE3QixtREFBNkM7O0lBQzdDLHNDQUF1Qjs7SUFDdkIscUNBQXNCOztJQUN0QiwyQ0FBcUU7O0lBQ3JFLCtDQUE2Qzs7SUFDN0MsZ0RBQTJDOztJQUMzQyxvQ0FBc0M7O0lBQ3RDLGtEQUF5RTs7SUFDekUsOENBQTZEOztJQUM3RCxrREFBcUU7O0lBQ3JFLHNDQUEyQzs7SUFDM0MsMkNBQTBGOztJQUMxRix1Q0FBK0M7Ozs7OztJQU0vQyxnREFBMEM7O0lBRTFDLDBDQUE4Qzs7SUFDOUMsbURBQWtDOzs7OztJQUNsQyw2Q0FBc0M7Ozs7O0lBQ3RDLDBDQUFpQzs7Ozs7SUFDakMsdUNBQWtDOzs7OztJQUlsQyxzREFLRzs7SUFDSCwyQ0FBNEI7O0lBRTVCLDBDQUFrQzs7SUFDbEMsMkNBQTZCOzs7OztJQUVqQixxQ0FBMEI7Ozs7O0lBQUcsMkNBQWdDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZm9yd2FyZFJlZiwgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIElucHV0LCBPdXRwdXQsIFZpZXdDaGlsZCwgT25Jbml0LCBIb3N0TGlzdGVuZXIsIEFmdGVyVmlld0luaXQsIEVsZW1lbnRSZWZ9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBNb2RhbERpcmVjdGl2ZSB9IGZyb20gJ25neC1ib290c3RyYXAvbW9kYWwnO1xyXG5pbXBvcnQgeyBPYnNlcnZhYmxlLCBvZiB9IGZyb20gJ3J4anMnO1xyXG5pbXBvcnQgeyBIdHRwQ2xpZW50LCBIdHRwSGVhZGVycywgSHR0cEVycm9yUmVzcG9uc2UgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XHJcbmltcG9ydCB7IFJlc3BvbnNlIH0gZnJvbSAnQGFuZ3VsYXIvaHR0cCc7XHJcbmltcG9ydCB7IENvbW1vbkxvZ2dlciB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2xvZ2dlci5zZXJ2aWNlJztcclxuaW1wb3J0IHsgQWJzdHJhY3RIdHRwU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2Fic3RyYWN0LWFwaS1zZXJ2aWNlJztcclxuaW1wb3J0IHsgQ29udHJvbENvbnRhaW5lciwgQ29udHJvbFZhbHVlQWNjZXNzb3IsIE5nRm9ybSwgTkdfVkFMVUVfQUNDRVNTT1IgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XHJcblxyXG5cclxuLyoqXHJcbiAqIENvbnNlbnQgTW9kYWwgaXMgYSBNb2RhbCB3aXRoIHRoZSBJbmZvcm1hdGlvbiBvciBOb3RpY2UuIEl0IGNhbiBiZSB1c2VkIHRvIGdldCB0aGUgVXNlcidzIGNvbnNlbnQgYW5cclxuICogdGhlbiBwcm9jZWVkIHdpdGggdGhlIGFwcGxpY2F0aW9uLiBJdCBhbHNvIG1ha2VzIGFuIEFQSSBjYWxsIHRvIHRoZSBTUEEtRU5WIHNlcnZlciB0byBzZWUgaWYgdGhlIGFwcCBpcyB1bmRlclxyXG4gKiBtYWludGVuYW5jZS5cclxuICpcclxuICpcclxuICogQGV4YW1wbGVcclxuKiAgICAgICBcdDxjb21tb24tY29uc2VudC1tb2RhbCAjbXNwQ29uc2VudE1vZGFsIGJvZHk9J0JvZHkgT2YgQ29uc2VudCdcclxuKiAgICAgICAgICAgICAgIHRpdGxlPSdOb3RpY2UnIFthcHBsaWNhdGlvbl09XCJtc3BBY2NvdW50QXBwXCJcclxuKiAgICAgICAgICAgICAgIHByb2Nlc3NOYW1lPSdNU1AnXHJcbiogICAgICAgICAgICAgICBhZ3JlZUxhYmVsPSdJIGhhdmUgcmVhZCBhbmQgdW5kZXJzdGFuZCB0aGlzIGluZm8nXHJcbiogICAgICAgICAgICAgICAob25DbG9zZSk9XCJhZGRyZXNzQ2hhbmdlQ2hrQnguZm9jdXMoKVwiPlxyXG4qICAgICAgICAgICA8L2NvbW1vbi1jb25zZW50LW1vZGFsPlxyXG4gKiBAZXhwb3J0XHJcbiAqL1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBJU3BhRW52UmVzcG9uc2Uge1xyXG4gIFNQQV9FTlZfTVNQX01BSU5URU5BTkNFX0ZMQUc6IHN0cmluZztcclxuICBTUEFfRU5WX01TUF9NQUlOVEVOQU5DRV9NRVNTQUdFOiBzdHJpbmc7XHJcbiAgU1BBX0VOVl9BQ0xfTUFJTlRFTkFOQ0VfRkxBRzogc3RyaW5nO1xyXG4gIFNQQV9FTlZfQUNMX01BSU5URU5BTkNFX01FU1NBR0U6IHN0cmluZztcclxuICBTUEFfRU5WX1NVUFBCRU5fTUFJTlRFTkFOQ0VfRkxBRzogc3RyaW5nO1xyXG4gIFNQQV9FTlZfU1VQUEJFTl9NQUlOVEVOQU5DRV9NRVNTQUdFOiBzdHJpbmc7XHJcbiAgU1BBX0VOVl9TVVBQQkVOX01BSU5URU5BTkNFX1NUQVJUOiBzdHJpbmc7XHJcbiAgU1BBX0VOVl9TVVBQQkVOX01BSU5URU5BTkNFX0VORDogc3RyaW5nO1xyXG4gIFNQQV9FTlZfUEFDVVRPRkZfTUFJTlRFTkFOQ0VfRkxBRzogc3RyaW5nO1xyXG4gIFNQQV9FTlZfUEFDVVRPRkZfTUFJTlRFTkFOQ0VfTUVTU0FHRTogc3RyaW5nO1xyXG4gIFNQQV9FTlZfUEFDVVRPRkZfTUFJTlRFTkFOQ0VfU1RBUlQ6IHN0cmluZztcclxuICBTUEFfRU5WX05PVzogc3RyaW5nO1xyXG4gIFNQQV9FTlZfUEFDVVRPRkZfTUFJTlRFTkFOQ0VfRU5EOiBzdHJpbmc7XHJcbn1cclxuXHJcbi8vIERpc2FibGluZyB0c2xpbnQgZm9yIEBleGFtcGxlIGJlbG93LlxyXG4vLyB0c2xpbnQ6ZGlzYWJsZTptYXgtbGluZS1sZW5ndGhcclxuXHJcbi8qKlxyXG4gKiBDb25zZW50TW9kYWxDb21wb25lbnQsIGFrYSB0aGUgXCJJbmZvcm1hdGlvbiBDb2xsZWN0aW9uIE5vdGljZVwiLCBpcyBhIG1vZGFsXHJcbiAqIGRlc2lnbmVkIHRvIGJlIHNob3duIGF0IHRoZSBiZWdpbm5pbmcgb2YgYW4gYXBwbGljYXRpb24uIEl0IGlzIGEgYm9pbGVycGxhdGVcclxuICogY2hlY2tib3ggdG8gY29uc2VudCB0byBpbmZvcm1hdGlvbiBjb2xsZWN0aW9uLlxyXG4gKlxyXG4gKiBDdXJyZW50bHkgdGhpcyBjb21wb25lbnQgcmVxdWlyZXMgdGhlIGJvZHkgdG8gYmUgbWFudWFsbHkgc2V0IGFzIGEgY2hpbGRcclxuICogZWxlbWVudCAodmlhIHRyYW5zY2x1c2lvbilcclxuICpcclxuICogVE9ETyAtIFNldCBkZWZhdWx0IGJvZHkgaWYgbm9uZSBpcyBwYXNzZWQgaW4uXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqICAgICAgIDxjb21tb24tY29uc2VudC1tb2RhbCAjY29uc2VudE1vZGFsIFtpc1VuZGVyTWFpbnRlbmFuY2VdPVwiZmFsc2VcIlxyXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9XCJJbmZvcm1hdGlvbiBjb2xsZWN0aW9uIG5vdGljZVwiXHJcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICBhZ3JlZUxhYmVsPVwiSSBoYXZlIHJlYWQgYW5kIHVuZGVyc3RhbmQgdGhpcyBpbmZvcm1hdGlvblwiXHJcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9jZXNzTmFtZT1cInByb2Nlc3NOYW1lXCJcclxuICogICAgICAgICAgICAgICAgICAgICAgICAgIChhY2NlcHQpPVwiYWNjb3VudExldHRlckFwcGxpY2F0aW9uLmluZm9Db2xsZWN0aW9uQWdyZWVtZW50ID0gJGV2ZW50OyAgc2F2ZUFwcGxpY2F0aW9uKCRldmVudClcIj5cclxuICogICAgICAgICAgICAgICAgICAgICAgPHA+PHN0cm9uZz5LZWVwIHlvdXIgcGVyc29uYWwgaW5mb3JtYXRpb24gc2VjdXJlIOKAkyBlc3BlY2lhbGx5IHdoZW4gdXNpbmcgYSBzaGFyZWQgZGV2aWNlIGxpa2UgYSBjb21wdXRlciBhdCBhIGxpYnJhcnksIHNjaG9vbCBvciBjYWbDqS48L3N0cm9uZz4gVG8gZGVsZXRlIGFueSBpbmZvcm1hdGlvbiB0aGF0IHdhcyBlbnRlcmVkLCBlaXRoZXIgY29tcGxldGUgdGhlIGFwcGxpY2F0aW9uIGFuZCBzdWJtaXQgaXQgb3IsIGlmIHlvdSBkb27igJl0IGZpbmlzaCwgY2xvc2UgdGhlIHdlYiBicm93c2VyLjwvcD48cD48c3Ryb25nPk5lZWQgdG8gdGFrZSBhIGJyZWFrIGFuZCBjb21lIGJhY2sgbGF0ZXI/PC9zdHJvbmc+IFRoZSBkYXRhIHlvdSBlbnRlciBvbiB0aGlzIGZvcm0gaXMgc2F2ZWQgbG9jYWxseSB0byB0aGUgY29tcHV0ZXIgb3IgZGV2aWNlIHlvdSBhcmUgdXNpbmcgdW50aWwgeW91IGNsb3NlIHRoZSB3ZWIgYnJvd3NlciBvciBzdWJtaXQgeW91ciBhcHBsaWNhdGlvbi48L3A+PHA+PHN0cm9uZz5JbmZvcm1hdGlvbiBpbiB0aGlzIGFwcGxpY2F0aW9uIGlzIGNvbGxlY3RlZCBieSB0aGUgTWluaXN0cnkgb2YgSGVhbHRoPC9zdHJvbmc+IHVuZGVyIHNlY3Rpb24gMjYoYSksIChjKSBhbmQgKGUpIG9mIHRoZSBGcmVlZG9tIG9mIEluZm9ybWF0aW9uIGFuZCBQcm90ZWN0aW9uIG9mIFByaXZhY3kgQWN0IGFuZCB3aWxsIGJlIHVzZWQgdG8gZGV0ZXJtaW5lIGVsaWdpYmlsaXR5IGZvciBwcm92aW5jaWFsIGhlYWx0aCBjYXJlIGJlbmVmaXRzIGluIEJDIGFuZCBhZG1pbmlzdGVyIFByZW1pdW0gQXNzaXN0YW5jZS4gU2hvdWxkIHlvdSBoYXZlIGFueSBxdWVzdGlvbnMgYWJvdXQgdGhlIGNvbGxlY3Rpb24gb2YgdGhpcyBwZXJzb25hbCBpbmZvcm1hdGlvbiBwbGVhc2UgPGEgaHJlZj1cImh0dHA6Ly93d3cyLmdvdi5iYy5jYS9nb3YvY29udGVudC9oZWFsdGgvaGVhbHRoLWRydWctY292ZXJhZ2UvbXNwL2JjLXJlc2lkZW50cy1jb250YWN0LXVzXCIgdGFyZ2V0PVwiX2JsYW5rXCI+Y29udGFjdCBIZWFsdGggSW5zdXJhbmNlIEJDIDxpIGNsYXNzPVwiZmEgZmEtZXh0ZXJuYWwtbGlua1wiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPjwvaT48L2E+LjwvcD5cclxuICogICAgICAgPC9jb21tb24tY29uc2VudC1tb2RhbD5cclxuICpcclxuICpcclxuICogICAgICAgIC8vIENvbXBvbmVudCBjb2RlIC0gUmVtb3ZlIGJhY2t0aWNrcyB3aGVuIGNvcHlpbmcgKG5lY2Vzc2FyeSB0byByZW5kZXIgZG9jcylcclxuICogICAgICAgIGBAVmlld0NoaWxkKCdjb25zZW50TW9kYWwnKSBjb25zZW50TW9kYWw6IENvbnNlbnRNb2RhbENvbXBvbmVudGBcclxuICogICAgICAgIC4uLlxyXG4gKiAgICAgICAgb3Blbk1vZGFsKCl7XHJcbiAqICAgICAgICAgIHRoaXMuY29uc2VudE1vZGFsLnNob3dGdWxsU2l6ZVZpZXcoKTtcclxuICogICAgICAgIH1cclxuICovXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnY29tbW9uLWNvbnNlbnQtbW9kYWwnLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9jb25zZW50LW1vZGFsLmNvbXBvbmVudC5odG1sJyxcclxuICBzdHlsZVVybHM6IFsnLi9jb25zZW50LW1vZGFsLmNvbXBvbmVudC5zY3NzJ10sXHJcbiAgdmlld1Byb3ZpZGVyczogW1xyXG4gICAgeyBwcm92aWRlOiBDb250cm9sQ29udGFpbmVyLCB1c2VFeGlzdGluZzogZm9yd2FyZFJlZigoKSA9PiBOZ0Zvcm0gKSB9XHJcbiAgXSxcclxuICBwcm92aWRlcnM6IFtcclxuICAgIHsgcHJvdmlkZTogTkdfVkFMVUVfQUNDRVNTT1IsIG11bHRpOiB0cnVlLCB1c2VFeGlzdGluZzogZm9yd2FyZFJlZigoKSA9PiBDb25zZW50TW9kYWxDb21wb25lbnQgKX1cclxuICBdXHJcbn0pXHJcblxyXG5leHBvcnQgY2xhc3MgQ29uc2VudE1vZGFsQ29tcG9uZW50IGV4dGVuZHMgQWJzdHJhY3RIdHRwU2VydmljZSBpbXBsZW1lbnRzIENvbnRyb2xWYWx1ZUFjY2Vzc29yLCBPbkluaXQsIEFmdGVyVmlld0luaXQgIHtcclxuXHJcbiAgcHJvdGVjdGVkIF9oZWFkZXJzOiBIdHRwSGVhZGVycyA9IG5ldyBIdHRwSGVhZGVycygpO1xyXG4gIEBJbnB1dCgpIHByb2Nlc3NOYW1lOiBzdHJpbmc7XHJcblxyXG4gIC8qKlxyXG4gICAqIElmIGBpc1VuZGVyTWFpbnRlbmFuY2VgIGlzIHRydWUsIHRoZW4gdGhpcyB3aWxsIGF1dG9tYXRpY2FsbHkgdHJ5IGFuZFxyXG4gICAqIG1ha2UgYSByZXF1ZXN0IHRvIHRoZSBTUEEgRU5WIHNlcnZlciB0byBkZXRlcm1pbmUgaWYgaXQncyBpbiBhXHJcbiAgICogbWFpbnRlbmFuY2Ugd2luZG93LiAgSWYgeW91ciBhcHBsaWNhdGlvbiBkZXRlcm1pbmVzIHRoaXMgbWFudWFsbHksIGxlYXZlXHJcbiAgICogdGhpcyBhbG9uZS5cclxuICAgKi9cclxuICBASW5wdXQoKSBpc1VuZGVyTWFpbnRlbmFuY2U6IGJvb2xlYW4gPSBmYWxzZTtcclxuICBASW5wdXQoKSB0aXRsZTogc3RyaW5nO1xyXG4gIEBJbnB1dCgpIGJvZHk6IHN0cmluZzsgLy8gPSAnPHA+PHN0cm9uZz5LZWVwIHlvdXIgcGVyc29uYWwgaW5mb3JtYXRpb24gc2VjdXJlIOKAkyBlc3BlY2lhbGx5IHdoZW4gdXNpbmcgYSBzaGFyZWQgZGV2aWNlIGxpa2UgYSBjb21wdXRlciBhdCBhIGxpYnJhcnksIHNjaG9vbCBvciBjYWbDqS48L3N0cm9uZz4gVG8gZGVsZXRlIGFueSBpbmZvcm1hdGlvbiB0aGF0IHdhcyBlbnRlcmVkLCBlaXRoZXIgY29tcGxldGUgdGhlIGFwcGxpY2F0aW9uIGFuZCBzdWJtaXQgaXQgb3IsIGlmIHlvdSBkb27igJl0IGZpbmlzaCwgY2xvc2UgdGhlIHdlYiBicm93c2VyLjwvcD48cD48c3Ryb25nPk5lZWQgdG8gdGFrZSBhIGJyZWFrIGFuZCBjb21lIGJhY2sgbGF0ZXI/PC9zdHJvbmc+IFRoZSBkYXRhIHlvdSBlbnRlciBvbiB0aGlzIGZvcm0gaXMgc2F2ZWQgbG9jYWxseSB0byB0aGUgY29tcHV0ZXIgb3IgZGV2aWNlIHlvdSBhcmUgdXNpbmcgdW50aWwgeW91IGNsb3NlIHRoZSB3ZWIgYnJvd3NlciBvciBzdWJtaXQgeW91ciBhcHBsaWNhdGlvbi48L3A+PHA+PHN0cm9uZz5JbmZvcm1hdGlvbiBpbiB0aGlzIGFwcGxpY2F0aW9uIGlzIGNvbGxlY3RlZCBieSB0aGUgTWluaXN0cnkgb2YgSGVhbHRoPC9zdHJvbmc+IHVuZGVyIHNlY3Rpb24gMjYoYSksIChjKSBhbmQgKGUpIG9mIHRoZSBGcmVlZG9tIG9mIEluZm9ybWF0aW9uIGFuZCBQcm90ZWN0aW9uIG9mIFByaXZhY3kgQWN0IGFuZCB3aWxsIGJlIHVzZWQgdG8gZGV0ZXJtaW5lIGVsaWdpYmlsaXR5IGZvciBwcm92aW5jaWFsIGhlYWx0aCBjYXJlIGJlbmVmaXRzIGluIEJDIGFuZCBhZG1pbmlzdGVyIFByZW1pdW0gQXNzaXN0YW5jZS4gU2hvdWxkIHlvdSBoYXZlIGFueSBxdWVzdGlvbnMgYWJvdXQgdGhlIGNvbGxlY3Rpb24gb2YgdGhpcyBwZXJzb25hbCBpbmZvcm1hdGlvbiBwbGVhc2UgPGEgaHJlZj1cImh0dHA6Ly93d3cyLmdvdi5iYy5jYS9nb3YvY29udGVudC9oZWFsdGgvaGVhbHRoLWRydWctY292ZXJhZ2UvbXNwL2JjLXJlc2lkZW50cy1jb250YWN0LXVzXCIgdGFyZ2V0PVwiX2JsYW5rXCI+Y29udGFjdCBIZWFsdGggSW5zdXJhbmNlIEJDIDxpIGNsYXNzPVwiZmEgZmEtZXh0ZXJuYWwtbGlua1wiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPjwvaT48L2E+LjwvcD4nO1xyXG4gIEBJbnB1dCgpIGFncmVlTGFiZWw6IHN0cmluZyA9ICdJIGhhdmUgcmVhZCBhbmQgdW5kZXJzdGFuZCB0aGlzIGluZm8nO1xyXG4gIEBJbnB1dCgpIGNvbnRpbnVlQnV0dG9uOiBzdHJpbmcgPSAnQ29udGludWUnO1xyXG4gIEBJbnB1dCgpIG1haW50ZW5hbmNlRmxhZzogc3RyaW5nID0gJ2ZhbHNlJztcclxuICBASW5wdXQoKSB1cmw6IHN0cmluZyA9ICcvbXNwL2FwaS9lbnYnO1xyXG4gIEBWaWV3Q2hpbGQoJ2Z1bGxTaXplVmlld01vZGFsJykgcHVibGljIGZ1bGxTaXplVmlld01vZGFsOiBNb2RhbERpcmVjdGl2ZTtcclxuICBAVmlld0NoaWxkKCdtb2RhbENvbnRlbnRzJykgcHVibGljIG1vZGFsQ29udGVudHM6IEVsZW1lbnRSZWY7XHJcbiAgQFZpZXdDaGlsZCgnY29udGludWVCdXR0b25SZWYnKSBwdWJsaWMgY29udGludWVCdXR0b25SZWY6IEVsZW1lbnRSZWY7XHJcbiAgQE91dHB1dCgpIGNsb3NlID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xyXG4gIEBPdXRwdXQoKSBjdXRPZmZEYXRlOiBFdmVudEVtaXR0ZXI8SVNwYUVudlJlc3BvbnNlPiA9IG5ldyBFdmVudEVtaXR0ZXI8SVNwYUVudlJlc3BvbnNlPigpO1xyXG4gIEBPdXRwdXQoKSBhY2NlcHQgPSBuZXcgRXZlbnRFbWl0dGVyPGJvb2xlYW4+KCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIFVzZWQgaW4gY2FzZXMgd2hlcmUgd2UgaGF2ZSBjdXN0b20gZm9ybSBjb250cm9scyBpbnNpZGUgTmdDb250ZW50IHRoYXQgd2VcclxuICAgKiB3aXNoIHRvIGJlIHNhdGlzaWZlZCBiZWZvcmUgdXNlciBjYW4gY29udGludWUgdGhyb3VnaCBtb2RhbC5cclxuICAgKi9cclxuICBASW5wdXQoKSBkaXNhYmxlQ29udGludWU6IGJvb2xlYW4gPSBmYWxzZTtcclxuXHJcbiAgcHVibGljIHNwYUVudlJlczogSVNwYUVudlJlc3BvbnNlID0ge30gYXMgYW55O1xyXG4gIHB1YmxpYyBtYWludGVuYW5jZU1lc3NhZ2U6IHN0cmluZztcclxuICBwcm90ZWN0ZWQgZm9jdXNhYmxlRWxzOiBIVE1MRWxlbWVudFtdO1xyXG4gIHByb3RlY3RlZCBmb2N1c2VkRWw6IEhUTUxFbGVtZW50O1xyXG4gIHByb3RlY3RlZCBjbG9zZWQ6IGJvb2xlYW4gPSBmYWxzZTtcclxuXHJcbiAgLy8gVE9ETzogVGhpcyBzaG91bGQgZXZlbnR1YWxseSBiZSBwdWxsZWQgb3V0IG9mIHRoZSBjb21tb24gbGlicmFyeSBhcyBpdCBwZXJ0YWlucyB0byBNU1Atc3BlY2lmaWMgY29kZS5cclxuICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bWF4LWxpbmUtbGVuZ3RoXHJcbiAgcHJpdmF0ZSBfYXBwbGljYXRpb25IZWFkZXJNYXA6IE1hcDxzdHJpbmcsIHN0cmluZz4gPSBuZXcgTWFwKFtcclxuICAgIFsnQUNMJywgJ3tcIlNQQV9FTlZfQUNMX01BSU5URU5BTkNFX0ZMQUdcIjpcIlwiLFwiU1BBX0VOVl9BQ0xfTUFJTlRFTkFOQ0VfTUVTU0FHRVwiOlwiXCJ9J10sXHJcbiAgICBbJ01TUCcsICd7XCJTUEFfRU5WX01TUF9NQUlOVEVOQU5DRV9GTEFHXCI6XCJcIixcIlNQQV9FTlZfTVNQX01BSU5URU5BTkNFX01FU1NBR0VcIjpcIlwifSddLFxyXG4gICAgWydQQScsICd7XCJTUEFfRU5WX1BBQ1VUT0ZGX01BSU5URU5BTkNFX1NUQVJUXCI6XCJcIixcIlNQQV9FTlZfUEFDVVRPRkZfTUFJTlRFTkFOQ0VfRU5EXCI6XCJcIixcIlNQQV9FTlZfTk9XXCI6XCJcIixcIlNQQV9FTlZfUEFDVVRPRkZfTUFJTlRFTkFOQ0VfRkxBR1wiOlwiXCIsXCJTUEFfRU5WX1BBQ1VUT0ZGX01BSU5URU5BTkNFX01FU1NBR0VcIjpcIlwifSddLFxyXG4gICAgWydTVVBQQkVOJywgJ3tcIlNQQV9FTlZfU1VQUEJFTl9NQUlOVEVOQU5DRV9TVEFSVFwiOlwiXCIsXCJTUEFfRU5WX1NVUFBCRU5fTUFJTlRFTkFOQ0VfRU5EXCI6XCJcIixcIlNQQV9FTlZfTk9XXCI6XCJcIixcIlNQQV9FTlZfU1VQUEJFTl9NQUlOVEVOQU5DRV9GTEFHXCI6XCJcIixcIlNQQV9FTlZfU1VQUEJFTl9NQUlOVEVOQU5DRV9NRVNTQUdFXCI6XCJcIixcIlNQQV9FTlZfUEFDVVRPRkZfTUFJTlRFTkFOQ0VfU1RBUlRcIjpcIlwiLFwiU1BBX0VOVl9QQUNVVE9GRl9NQUlOVEVOQU5DRV9FTkRcIjpcIlwifSddLFxyXG4gIF0pO1xyXG4gIGFncmVlQ2hlY2s6IGJvb2xlYW4gPSBmYWxzZTtcclxuXHJcbiAgcHVibGljIF9vbkNoYW5nZSA9IChfOiBhbnkpID0+IHt9O1xyXG4gIHB1YmxpYyBfb25Ub3VjaGVkID0gKCkgPT4ge307XHJcblxyXG4gIGNvbnN0cnVjdG9yKHByb3RlY3RlZCBodHRwOiBIdHRwQ2xpZW50LCAgcHJpdmF0ZSBsb2dTZXJ2aWNlOiBDb21tb25Mb2dnZXIpIHtcclxuICAgICAgc3VwZXIoaHR0cCk7XHJcbiAgfVxyXG5cclxuICBuZ09uSW5pdCgpOiB2b2lkIHtcclxuICAgIC8vIENhbGxlZCBhZnRlciBuZ09uSW5pdCB3aGVuIHRoZSBjb21wb25lbnQncyBvciBkaXJlY3RpdmUncyBjb250ZW50IGhhcyBiZWVuIGluaXRpYWxpemVkLlxyXG4gICAgLy8gQWRkICdpbXBsZW1lbnRzIEFmdGVyQ29udGVudEluaXQnIHRvIHRoZSBjbGFzcy5cclxuICAgIGlmICh0aGlzLmlzVW5kZXJNYWludGVuYW5jZSkge1xyXG4gICAgICB0aGlzLmluTWFpbnRlbmFuY2UoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcclxuICAgIC8vIENyZWF0ZSBhbiBhcnJheSBvZiBmb2N1c2FibGUgZWxlbWVudHMgZnJvbSB0aGUgY29udGVudHMgb2YgdGhlIG1vZGFsXHJcbiAgICB0aGlzLmZvY3VzYWJsZUVscyA9IEFycmF5LmZyb20odGhpcy5tb2RhbENvbnRlbnRzLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvckFsbCgnYVtocmVmXSwgYXJlYVtocmVmXSwgaW5wdXQ6bm90KFtkaXNhYmxlZF0pLCBzZWxlY3Q6bm90KFtkaXNhYmxlZF0pLCB0ZXh0YXJlYTpub3QoW2Rpc2FibGVkXSksIGJ1dHRvbjpub3QoW2Rpc2FibGVkXSksIFt0YWJpbmRleD1cIjBcIl0nKSk7XHJcbiAgICAvLyBJbml0aWFsaXplIHRvIHRoZSBmaXJzdCBmb2N1c2FibGUgZWxlbWVudFxyXG4gICAgdGhpcy5mb2N1c2VkRWwgPSB0aGlzLmZvY3VzYWJsZUVsc1swXTtcclxuICAgIHRoaXMuZm9jdXNlZEVsLmZvY3VzKCk7XHJcbiAgICB0aGlzLmZ1bGxTaXplVmlld01vZGFsLmNvbmZpZy5iYWNrZHJvcCA9IHRydWU7XHJcblxyXG4gIH1cclxuXHJcbiAgQEhvc3RMaXN0ZW5lcignd2luZG93OmtleWRvd24nLCBbJyRldmVudCddKVxyXG4gIGhhbmRsZUtleURvd24oZXZlbnQ6IEtleWJvYXJkRXZlbnQpIHtcclxuICAgIC8vIENoZWNrIHRoYXQgdGhlIG1vZGFsIGlzIG9wZW5cclxuICAgIGlmICghdGhpcy5jbG9zZWQpIHtcclxuICAgICAgLy8gSGFuZGxlIHRhYmJpbmdcclxuICAgICAgaWYgKGV2ZW50LmtleSA9PT0gJ1RhYicpIHtcclxuICAgICAgICAvLyBQcmV2ZW50IHVzdWFsIHRhYmJpbmcsIG1hbnVhbGx5IHNldCBmb2N1c1xyXG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgICAgaWYgKGV2ZW50LnNoaWZ0S2V5KSB7XHJcbiAgICAgICAgICB0aGlzLmhhbmRsZVRhYkJhY2t3YXJkcygpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aGlzLmhhbmRsZVRhYigpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgIC8vIFN0b3AgdXNlcnMgZnJvbSBiZWluZyBhYmxlIHRvIGVzY2FwZSB0aGUgbW9kYWxcclxuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICdFc2NhcGUnKSB7XHJcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQXBpIGNhbGxvdXQgdG8gZ2V0IHRoZSBtZXNzYWdlIGZyb20gdGhlIFJhcGlkIGNvZGVcclxuICBzZW5kU3BhRW52U2VydmVyKHJhcGlkUmVzcG9uc2VDb2RlOiBzdHJpbmcpOiBPYnNlcnZhYmxlPGFueT4ge1xyXG4gICAgdGhpcy5faGVhZGVycyA9IG5ldyBIdHRwSGVhZGVycyh7XHJcbiAgICAgICdTUEFfRU5WX05BTUUnOiByYXBpZFJlc3BvbnNlQ29kZVxyXG4gICAgfSk7XHJcbiAgICByZXR1cm4gdGhpcy5wb3N0PGFueT4odGhpcy51cmwsIG51bGwpO1xyXG4gIH1cclxuXHJcbiAgLy8gTW92ZSB0byBuZXh0IGZvY3VzYWJsZSBlbGVtZW50LCBpZiBhdCBsYXN0IGVsZW1lbnQsIG1vdmUgdG8gZmlyc3RcclxuICBoYW5kbGVUYWIoKSB7XHJcbiAgICBjb25zdCBwb3NpdGlvbiA9IHRoaXMuZm9jdXNhYmxlRWxzLmluZGV4T2YodGhpcy5mb2N1c2VkRWwpO1xyXG4gICAgaWYgKHBvc2l0aW9uID09PSB0aGlzLmZvY3VzYWJsZUVscy5sZW5ndGggLSAxKSB7XHJcbiAgICAgIHRoaXMuZm9jdXNlZEVsID0gdGhpcy5mb2N1c2FibGVFbHNbMF07XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aGlzLmZvY3VzZWRFbCA9IHRoaXMuZm9jdXNhYmxlRWxzW3Bvc2l0aW9uICsgMV07XHJcbiAgICB9XHJcbiAgICB0aGlzLmZvY3VzZWRFbC5mb2N1cygpO1xyXG4gIH07XHJcblxyXG4gIC8vIE1vdmUgdG8gbmV4dCBmb2N1c2FibGUgZWxlbWVudCwgaWYgYXQgbGFzdCBlbGVtZW50LCBtb3ZlIHRvIGZpcnN0XHJcbiAgaGFuZGxlVGFiQmFja3dhcmRzKCkge1xyXG4gICAgY29uc3QgcG9zaXRpb24gPSB0aGlzLmZvY3VzYWJsZUVscy5pbmRleE9mKHRoaXMuZm9jdXNlZEVsKTtcclxuICAgIGlmIChwb3NpdGlvbiA9PT0gMCkge1xyXG4gICAgICB0aGlzLmZvY3VzZWRFbCA9IHRoaXMuZm9jdXNhYmxlRWxzW3RoaXMuZm9jdXNhYmxlRWxzLmxlbmd0aCAtIDFdO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdGhpcy5mb2N1c2VkRWwgPSB0aGlzLmZvY3VzYWJsZUVsc1twb3NpdGlvbiAtIDFdO1xyXG4gICAgfVxyXG4gICAgdGhpcy5mb2N1c2VkRWwuZm9jdXMoKTtcclxuICB9O1xyXG5cclxuICAvKipcclxuICAgKiBDYWxsIHRoaXMgbWV0aG9kIHRvIGRpc3BsYXkgdGhlIG1vZGFsLlxyXG4gICAqL1xyXG4gIHNob3dGdWxsU2l6ZVZpZXcoKSB7XHJcbiAgICAgIHRoaXMuZnVsbFNpemVWaWV3TW9kYWwuY29uZmlnLmtleWJvYXJkID0gZmFsc2U7XHJcbiAgICAgIHRoaXMuZnVsbFNpemVWaWV3TW9kYWwuc2hvdygpO1xyXG4gIH1cclxuXHJcbiAgY29udGludWUoKSB7XHJcbiAgICAgIHRoaXMuYWNjZXB0LmVtaXQodHJ1ZSk7XHJcbiAgICAgIHRoaXMuZnVsbFNpemVWaWV3TW9kYWwuaGlkZSgpO1xyXG4gICAgICB0aGlzLmNsb3NlLmVtaXQoKTtcclxuICAgICAgdGhpcy5jbG9zZWQgPSB0cnVlO1xyXG4gICAgICB0aGlzLl9vbkNoYW5nZSh0cnVlKTtcclxuICAgICAgdGhpcy5fb25Ub3VjaGVkKCk7XHJcbiAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBoYW5kbGVFcnJvcihlcnJvcjogSHR0cEVycm9yUmVzcG9uc2UpIHtcclxuICAgICAgaWYgKGVycm9yLmVycm9yIGluc3RhbmNlb2YgRXJyb3JFdmVudCkge1xyXG4gICAgICAgICAgLy8gQ2xpZW50LXNpZGUgLyBuZXR3b3JrIGVycm9yIG9jY3VyZWRcclxuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ01zcE1haW50ZW5hbmNlU2VydmljZSBlcnJvcjogJywgZXJyb3IuZXJyb3IubWVzc2FnZSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAvLyBUaGUgYmFja2VuZCByZXR1cm5lZCBhbiB1bnN1Y2Nlc3NmdWwgcmVzcG9uc2UgY29kZVxyXG4gICAgICAgICAgY29uc29sZS5lcnJvcihgTXNwTWFpbnRlbmFuY2VTZXJ2aWNlIEJhY2tlbmQgcmV0dXJuZWQgZXJyb3IgY29kZTogJHtlcnJvci5zdGF0dXN9LiAgRXJyb3IgYm9keTogJHtlcnJvci5lcnJvcn1gKTtcclxuICAgICAgfVxyXG5cclxuICAgIC8vIEEgdXNlciBmYWNpbmcgZXJvcnIgbWVzc2FnZSAvY291bGQvIGdvIGhlcmU7IHdlIHNob3VsZG4ndCBsb2cgZGV2IGluZm8gdGhyb3VnaCB0aGUgdGhyb3dFcnJvciBvYnNlcnZhYmxlXHJcbiAgICByZXR1cm4gb2YoZXJyb3IpO1xyXG4gIH1cclxuXHJcblxyXG4gIGluTWFpbnRlbmFuY2UoKSB7XHJcbiAgICAgICAgY29uc3QgaGVhZGVyTmFtZSA9IHRoaXMuX2FwcGxpY2F0aW9uSGVhZGVyTWFwLmdldCh0aGlzLnByb2Nlc3NOYW1lKTtcclxuXHJcbiAgICAgICAgdGhpcy5zZW5kU3BhRW52U2VydmVyKGhlYWRlck5hbWUpXHJcbiAgICAgICAgICAgICAgICAuc3Vic2NyaWJlKHJlc3BvbnNlID0+IHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLnNwYUVudlJlcyA9IDxJU3BhRW52UmVzcG9uc2U+IHJlc3BvbnNlO1xyXG4gICAgICAgICAgICAgICAgICAgIC8vIFRPRE86IFRoaXMgc2hvdWxkIGV2ZW50dWFsbHkgYmUgcHVsbGVkIG91dCBvZiB0aGUgY29tbW9uIGxpYnJhcnkgYXMgaXQgcGVydGFpbnMgdG8gTVNQLXNwZWNpZmljIGNvZGUuXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuc3BhRW52UmVzLlNQQV9FTlZfQUNMX01BSU5URU5BTkNFX0ZMQUcgPT09ICd0cnVlJykge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1haW50ZW5hbmNlRmxhZyA9ICd0cnVlJztcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5tYWludGVuYW5jZU1lc3NhZ2UgPSB0aGlzLnNwYUVudlJlcy5TUEFfRU5WX0FDTF9NQUlOVEVOQU5DRV9NRVNTQUdFO1xyXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5zcGFFbnZSZXMuU1BBX0VOVl9NU1BfTUFJTlRFTkFOQ0VfRkxBRyA9PT0gJ3RydWUnKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubWFpbnRlbmFuY2VGbGFnID0gJ3RydWUnO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1haW50ZW5hbmNlTWVzc2FnZSA9ICB0aGlzLnNwYUVudlJlcy5TUEFfRU5WX01TUF9NQUlOVEVOQU5DRV9NRVNTQUdFO1xyXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5zcGFFbnZSZXMuU1BBX0VOVl9QQUNVVE9GRl9NQUlOVEVOQU5DRV9GTEFHID09PSAndHJ1ZScpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5tYWludGVuYW5jZUZsYWcgPSAndHJ1ZSc7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubWFpbnRlbmFuY2VNZXNzYWdlID0gdGhpcy5zcGFFbnZSZXMuU1BBX0VOVl9QQUNVVE9GRl9NQUlOVEVOQU5DRV9NRVNTQUdFO1xyXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy5zcGFFbnZSZXMuU1BBX0VOVl9TVVBQQkVOX01BSU5URU5BTkNFX0ZMQUcgPT09ICd0cnVlJykge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1haW50ZW5hbmNlRmxhZyA9ICd0cnVlJztcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5tYWludGVuYW5jZU1lc3NhZ2UgPSB0aGlzLnNwYUVudlJlcy5TUEFfRU5WX1NVUFBCRU5fTUFJTlRFTkFOQ0VfTUVTU0FHRTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuc3BhRW52UmVzLlNQQV9FTlZfUEFDVVRPRkZfTUFJTlRFTkFOQ0VfU1RBUlQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5jdXRPZmZEYXRlLmVtaXQodGhpcy5zcGFFbnZSZXMpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIH0sIChlcnJvcjogUmVzcG9uc2UgfCBhbnkpID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMubG9nU2VydmljZS5sb2coe1xyXG4gICAgICAgICAgICAgICAgICBldmVudDogJ0FDTCAtIFNQQSBFbnYgU3lzdGVtIEVycm9yJyxcclxuICAgICAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXHJcbiAgICAgICAgICAgICAgICAgIGVyck1zZzogJ0FDTCAtIFNQQSBFbnYgUmFwaWQgUmVzcG9uc2UgRXJyb3InICsgZXJyb3JcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICk7XHJcbiAgfVxyXG5cclxuICByZWdpc3Rlck9uQ2hhbmdlKGZuOiBhbnkpOiB2b2lkIHtcclxuICAgIHRoaXMuYWNjZXB0LmVtaXQoZm4pIDtcclxuICAgIHRoaXMuX29uQ2hhbmdlID0gZm47XHJcbiAgfVxyXG5cclxuICByZWdpc3Rlck9uVG91Y2hlZChmbjogYW55KTogdm9pZCB7XHJcbiAgICB0aGlzLl9vblRvdWNoZWQgPSBmbjtcclxuICB9XHJcblxyXG4gIHdyaXRlVmFsdWUodmFsdWU6IGFueSk6IHZvaWQge31cclxuXHJcbiAgaXNDb250aW51ZURpc2FibG