UNPKG

ng-smart-on-fhir

Version:

[![SRDC](https://www.srdc.com.tr/wp-content/uploads/2014/12/srdc-wp.png)](https://srdc.com.tr/en)

302 lines 28.6 kB
import { Component } from '@angular/core'; import * as jose from 'jose'; import * as i0 from "@angular/core"; import * as i1 from "@angular/router"; import * as i2 from "../../services/smart-auth.service"; import * as i3 from "../../services/shl.service"; import * as i4 from "../../services/smart-on-fhir.service"; import * as i5 from "@angular/common"; import * as i6 from "../../pipes/camel-case-spaced.pipe"; /** * Component for searching and validating Smart Health Cards or Smart Health Links */ export class ShcShlHandlerComponent { get patientName() { return this.patient?.name?.map(name => (name.given?.join(' ') || '') + ' ' + name.family).join(', '); } get patientAge() { if (this.patient?.birthDate) { return Math.ceil((Date.now() - new Date(this.patient.birthDate).getTime()) / (1000 * 3600 * 24 * 365)); } else { return ''; } } constructor(route, auth, shl, sof) { this.route = route; this.auth = auth; this.shl = shl; this.sof = sof; this.invalidSignature = false; this.referenceMap = {}; this.auth.getPatient().then(patient => this.patient = patient); route.fragment.subscribe(async (fragment) => { try { if (fragment?.startsWith('shlink:/')) { this.sof.importSHCData(await this.shl.handleShl(fragment)); this.checkIPS(); } else if (fragment?.startsWith('shc:/')) { this.sof.importSHCData(await this.shl.handleShc(fragment)); this.checkIPS(); } else { console.log("No SHC data in the fragment:", fragment); } } catch (error) { if (error instanceof jose.errors.JWSSignatureVerificationFailed) { this.invalidSignature = true; } } }); } ngOnInit() { } logout() { this.sof.logout(); } checkIPS() { if (this.sof.importedResources['Composition']) { const ips = this.sof.importedResources['Composition'] .find((resource) => resource.type?.coding?.some(code => code.code === '60591-5')); if (ips) { this.referenceMap = this.sof.getAllImportedResources().reduce((map, resource) => { map[resource.resourceType + '/' + resource.id] = resource; return map; }, {}); this.ips = ips; } } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ShcShlHandlerComponent, deps: [{ token: i1.ActivatedRoute }, { token: i2.SmartAuthService }, { token: i3.ShlService }, { token: i4.SmartOnFhirService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: ShcShlHandlerComponent, selector: "sof-shc-shl-handler", ngImport: i0, template: ` <div class="container"> @if (invalidSignature) { <div class="sof-alert sof-alert-danger"> Issuer verification failed for the Smart Health Card! </div> <button class="sof-button sof-button-danger" (click)="logout()"><i class="sof-icon sof-icon-caret-left"></i> Back</button> <button class="sof-button sof-button-warning sof-ml-1 sof-mt-1" (click)="invalidSignature = false"><i class="sof-icon sof-icon-exclamation"></i> Ignore and Continue</button> } @else { <table class="sof-table" id="shc-content"> <tbody> <tr> <td><b>Patient</b></td> <td>{{ patientName }}</td> </tr> <tr> <td><b>Age</b></td> <td>{{ patientAge }}</td> </tr> <tr> <td><b>Gender</b></td> <td>{{ patient?.gender }}</td> </tr> @if (ips) { <tr> <td colspan="2"><h4>International Patient Summary Imported</h4></td> </tr> @for (section of ips.section; track section.title) { <tr> <td colspan="2"> <h5>{{section.title}}</h5> </td> </tr> <ng-template #myTemplate let-resource> @if (resource) { @switch (resource.resourceType) { @case ('Condition') { <tr> <td>{{resource.code?.coding?.at(0)?.display || resource.code?.coding?.at(0)?.code || resource.code?.text}}</td> <td class="text-end"> {{resource.onsetDateTime | date}} </td> </tr> } @case ('MedicationStatement') { <tr> <td>{{resource.medicationCodeableConcept?.coding?.at(0)?.display || resource.medicationCodeableConcept?.coding?.at(0)?.code || resource.medicationCodeableConcept?.text}}</td> <td class="text-end"> {{resource.effectiveDateTime | date}} </td> </tr> } @case ('AllergyIntolerance') { <tr> <td>{{resource.code?.coding?.at(0)?.display || resource.code?.coding?.at(0)?.code || resource.code?.text}}</td> <td class="text-end"> {{resource.onsetDateTime | date}} </td> </tr> } @case ('Immunization') { <tr> <td>{{resource.vaccineCode?.coding?.at(0)?.display || resource.vaccineCode?.coding?.at(0)?.code || resource.vaccineCode?.text}}</td> <td class="text-end"> {{resource.occurrenceDateTime | date}} </td> </tr> } @case ('Observation') { <tr> <td>{{resource.code?.coding?.at(0)?.display || resource.code?.coding?.at(0)?.code || resource.code?.text}}</td> <td class="text-end"> {{resource.effectiveDateTime | date}} <br> @if (resource.valueQuantity) { {{resource.valueQuantity.value}} {{resource.valueQuantity.unit || resource.valueQuantity.code}} } </td> </tr> } } } </ng-template> @for (entry of section.entry; track entry.reference) { <ng-container *ngTemplateOutlet="myTemplate; context: { $implicit: referenceMap[entry.reference || ''] }"></ng-container> } } } @else { @for (resources of sof.importedResources | keyvalue; track resources.key) { <tr> <td><b>{{resources.key | camelCaseSpaced}}</b></td> <td><span class="sof-badge sof-primary-background sof-text-light">{{resources.value?.length}}</span></td> </tr> } } <tr> <td colspan="2" class="text-end"> <button class="sof-button sof-button-primary" routerLink="/">Continue <i class="sof-icon sof-icon-caret-right"></i></button> </td> </tr> </tbody> </table> } </div> <style> #shc-content td:first-child { width: 25em; max-width: 100%; } </style> `, isInline: true, styles: ["\n #shc-content td:first-child {\n width: 25em;\n max-width: 100%;\n }\n "], dependencies: [{ kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i5.KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: i5.DatePipe, name: "date" }, { kind: "pipe", type: i6.CamelCaseSpacedPipe, name: "camelCaseSpaced" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ShcShlHandlerComponent, decorators: [{ type: Component, args: [{ selector: 'sof-shc-shl-handler', template: ` <div class="container"> @if (invalidSignature) { <div class="sof-alert sof-alert-danger"> Issuer verification failed for the Smart Health Card! </div> <button class="sof-button sof-button-danger" (click)="logout()"><i class="sof-icon sof-icon-caret-left"></i> Back</button> <button class="sof-button sof-button-warning sof-ml-1 sof-mt-1" (click)="invalidSignature = false"><i class="sof-icon sof-icon-exclamation"></i> Ignore and Continue</button> } @else { <table class="sof-table" id="shc-content"> <tbody> <tr> <td><b>Patient</b></td> <td>{{ patientName }}</td> </tr> <tr> <td><b>Age</b></td> <td>{{ patientAge }}</td> </tr> <tr> <td><b>Gender</b></td> <td>{{ patient?.gender }}</td> </tr> @if (ips) { <tr> <td colspan="2"><h4>International Patient Summary Imported</h4></td> </tr> @for (section of ips.section; track section.title) { <tr> <td colspan="2"> <h5>{{section.title}}</h5> </td> </tr> <ng-template #myTemplate let-resource> @if (resource) { @switch (resource.resourceType) { @case ('Condition') { <tr> <td>{{resource.code?.coding?.at(0)?.display || resource.code?.coding?.at(0)?.code || resource.code?.text}}</td> <td class="text-end"> {{resource.onsetDateTime | date}} </td> </tr> } @case ('MedicationStatement') { <tr> <td>{{resource.medicationCodeableConcept?.coding?.at(0)?.display || resource.medicationCodeableConcept?.coding?.at(0)?.code || resource.medicationCodeableConcept?.text}}</td> <td class="text-end"> {{resource.effectiveDateTime | date}} </td> </tr> } @case ('AllergyIntolerance') { <tr> <td>{{resource.code?.coding?.at(0)?.display || resource.code?.coding?.at(0)?.code || resource.code?.text}}</td> <td class="text-end"> {{resource.onsetDateTime | date}} </td> </tr> } @case ('Immunization') { <tr> <td>{{resource.vaccineCode?.coding?.at(0)?.display || resource.vaccineCode?.coding?.at(0)?.code || resource.vaccineCode?.text}}</td> <td class="text-end"> {{resource.occurrenceDateTime | date}} </td> </tr> } @case ('Observation') { <tr> <td>{{resource.code?.coding?.at(0)?.display || resource.code?.coding?.at(0)?.code || resource.code?.text}}</td> <td class="text-end"> {{resource.effectiveDateTime | date}} <br> @if (resource.valueQuantity) { {{resource.valueQuantity.value}} {{resource.valueQuantity.unit || resource.valueQuantity.code}} } </td> </tr> } } } </ng-template> @for (entry of section.entry; track entry.reference) { <ng-container *ngTemplateOutlet="myTemplate; context: { $implicit: referenceMap[entry.reference || ''] }"></ng-container> } } } @else { @for (resources of sof.importedResources | keyvalue; track resources.key) { <tr> <td><b>{{resources.key | camelCaseSpaced}}</b></td> <td><span class="sof-badge sof-primary-background sof-text-light">{{resources.value?.length}}</span></td> </tr> } } <tr> <td colspan="2" class="text-end"> <button class="sof-button sof-button-primary" routerLink="/">Continue <i class="sof-icon sof-icon-caret-right"></i></button> </td> </tr> </tbody> </table> } </div> <style> #shc-content td:first-child { width: 25em; max-width: 100%; } </style> ` }] }], ctorParameters: () => [{ type: i1.ActivatedRoute }, { type: i2.SmartAuthService }, { type: i3.ShlService }, { type: i4.SmartOnFhirService }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hjLXNobC1oYW5kbGVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3NtYXJ0LW9uLWZoaXIvc3JjL2xpYi9jb21wb25lbnRzL3NoYy1zaGwtaGFuZGxlci9zaGMtc2hsLWhhbmRsZXIuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBQyxTQUFTLEVBQVMsTUFBTSxlQUFlLENBQUM7QUFHaEQsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7Ozs7Ozs7O0FBSTdCOztHQUVHO0FBb0hILE1BQU0sT0FBTyxzQkFBc0I7SUFRakMsSUFBSSxXQUFXO1FBQ2IsT0FBTyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFBO0lBQ3RHLENBQUM7SUFFRCxJQUFJLFVBQVU7UUFDWixJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsU0FBUyxFQUFFLENBQUM7WUFDNUIsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksR0FBRyxJQUFJLEdBQUcsRUFBRSxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUE7UUFDeEcsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLEVBQUUsQ0FBQztRQUNaLENBQUM7SUFDSCxDQUFDO0lBRUQsWUFBb0IsS0FBcUIsRUFBVSxJQUFzQixFQUFVLEdBQWUsRUFBUyxH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Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E4R1Q7OzRGQUdVLHNCQUFzQjtrQkFuSGxDLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLHFCQUFxQjtvQkFDL0IsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQThHVDtpQkFDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7Q29tcG9uZW50LCBPbkluaXR9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQge0FjdGl2YXRlZFJvdXRlfSBmcm9tIFwiQGFuZ3VsYXIvcm91dGVyXCI7XHJcbmltcG9ydCB7U2hsU2VydmljZX0gZnJvbSBcIi4uLy4uL3NlcnZpY2VzL3NobC5zZXJ2aWNlXCI7XHJcbmltcG9ydCAqIGFzIGpvc2UgZnJvbSAnam9zZSc7XHJcbmltcG9ydCB7U21hcnRPbkZoaXJTZXJ2aWNlfSBmcm9tIFwiLi4vLi4vc2VydmljZXMvc21hcnQtb24tZmhpci5zZXJ2aWNlXCI7XHJcbmltcG9ydCB7U21hcnRBdXRoU2VydmljZX0gZnJvbSBcIi4uLy4uL3NlcnZpY2VzL3NtYXJ0LWF1dGguc2VydmljZVwiO1xyXG5cclxuLyoqXHJcbiAqIENvbXBvbmVudCBmb3Igc2VhcmNoaW5nIGFuZCB2YWxpZGF0aW5nIFNtYXJ0IEhlYWx0aCBDYXJkcyBvciBTbWFydCBIZWFsdGggTGlua3NcclxuICovXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnc29mLXNoYy1zaGwtaGFuZGxlcicsXHJcbiAgdGVtcGxhdGU6IGBcclxuICAgIDxkaXYgY2xhc3M9XCJjb250YWluZXJcIj5cclxuICAgICAgQGlmIChpbnZhbGlkU2lnbmF0dXJlKSB7XHJcbiAgICAgICAgPGRpdiBjbGFzcz1cInNvZi1hbGVydCBzb2YtYWxlcnQtZGFuZ2VyXCI+XHJcbiAgICAgICAgICBJc3N1ZXIgdmVyaWZpY2F0aW9uIGZhaWxlZCBmb3IgdGhlIFNtYXJ0IEhlYWx0aCBDYXJkIVxyXG4gICAgICAgIDwvZGl2PlxyXG4gICAgICAgIDxidXR0b24gY2xhc3M9XCJzb2YtYnV0dG9uIHNvZi1idXR0b24tZGFuZ2VyXCIgKGNsaWNrKT1cImxvZ291dCgpXCI+PGkgY2xhc3M9XCJzb2YtaWNvbiBzb2YtaWNvbi1jYXJldC1sZWZ0XCI+PC9pPiBCYWNrPC9idXR0b24+XHJcbiAgICAgICAgPGJ1dHRvbiBjbGFzcz1cInNvZi1idXR0b24gc29mLWJ1dHRvbi13YXJuaW5nIHNvZi1tbC0xIHNvZi1tdC0xXCIgKGNsaWNrKT1cImludmFsaWRTaWduYXR1cmUgPSBmYWxzZVwiPjxpIGNsYXNzPVwic29mLWljb24gc29mLWljb24tZXhjbGFtYXRpb25cIj48L2k+IElnbm9yZSBhbmQgQ29udGludWU8L2J1dHRvbj5cclxuICAgICAgfSBAZWxzZSB7XHJcbiAgICAgICAgPHRhYmxlIGNsYXNzPVwic29mLXRhYmxlXCIgaWQ9XCJzaGMtY29udGVudFwiPlxyXG4gICAgICAgICAgPHRib2R5PlxyXG4gICAgICAgICAgPHRyPlxyXG4gICAgICAgICAgICA8dGQ+PGI+UGF0aWVudDwvYj48L3RkPlxyXG4gICAgICAgICAgICA8dGQ+e3sgcGF0aWVudE5hbWUgfX08L3RkPlxyXG4gICAgICAgICAgPC90cj5cclxuICAgICAgICAgIDx0cj5cclxuICAgICAgICAgICAgPHRkPjxiPkFnZTwvYj48L3RkPlxyXG4gICAgICAgICAgICA8dGQ+e3sgcGF0aWVudEFnZSB9fTwvdGQ+XHJcbiAgICAgICAgICA8L3RyPlxyXG4gICAgICAgICAgPHRyPlxyXG4gICAgICAgICAgICA8dGQ+PGI+R2VuZGVyPC9iPjwvdGQ+XHJcbiAgICAgICAgICAgIDx0ZD57eyBwYXRpZW50Py5nZW5kZXIgfX08L3RkPlxyXG4gICAgICAgICAgPC90cj5cclxuICAgICAgICAgIEBpZiAoaXBzKSB7XHJcbiAgICAgICAgICAgIDx0cj5cclxuICAgICAgICAgICAgICA8dGQgY29sc3Bhbj1cIjJcIj48aDQ+SW50ZXJuYXRpb25hbCBQYXRpZW50IFN1bW1hcnkgSW1wb3J0ZWQ8L2g0PjwvdGQ+XHJcbiAgICAgICAgICAgIDwvdHI+XHJcbiAgICAgICAgICAgIEBmb3IgKHNlY3Rpb24gb2YgaXBzLnNlY3Rpb247IHRyYWNrIHNlY3Rpb24udGl0bGUpIHtcclxuICAgICAgICAgICAgICA8dHI+XHJcbiAgICAgICAgICAgICAgICA8dGQgY29sc3Bhbj1cIjJcIj5cclxuICAgICAgICAgICAgICAgICAgPGg1Pnt7c2VjdGlvbi50aXRsZX19PC9oNT5cclxuICAgICAgICAgICAgICAgIDwvdGQ+XHJcbiAgICAgICAgICAgICAgPC90cj5cclxuICAgICAgICAgICAgICA8bmctdGVtcGxhdGUgI215VGVtcGxhdGUgbGV0LXJlc291cmNlPlxyXG4gICAgICAgICAgICAgICAgQGlmIChyZXNvdXJjZSkge1xyXG4gICAgICAgICAgICAgICAgICBAc3dpdGNoIChyZXNvdXJjZS5yZXNvdXJjZVR5cGUpIHtcclxuICAgICAgICAgICAgICAgICAgICBAY2FzZSAoJ0NvbmRpdGlvbicpIHtcclxuICAgICAgICAgICAgICAgICAgICAgIDx0cj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7cmVzb3VyY2UuY29kZT8uY29kaW5nPy5hdCgwKT8uZGlzcGxheSB8fCByZXNvdXJjZS5jb2RlPy5jb2Rpbmc/LmF0KDApPy5jb2RlIHx8IHJlc291cmNlLmNvZGU/LnRleHR9fTwvdGQ+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInRleHQtZW5kXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAge3tyZXNvdXJjZS5vbnNldERhdGVUaW1lIHwgZGF0ZX19XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGQ+XHJcbiAgICAgICAgICAgICAgICAgICAgICA8L3RyPlxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICBAY2FzZSAoJ01lZGljYXRpb25TdGF0ZW1lbnQnKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICA8dHI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD57e3Jlc291cmNlLm1lZGljYXRpb25Db2RlYWJsZUNvbmNlcHQ/LmNvZGluZz8uYXQoMCk/LmRpc3BsYXkgfHwgcmVzb3VyY2UubWVkaWNhdGlvbkNvZGVhYmxlQ29uY2VwdD8uY29kaW5nPy5hdCgwKT8uY29kZSB8fCByZXNvdXJjZS5tZWRpY2F0aW9uQ29kZWFibGVDb25jZXB0Py50ZXh0fX08L3RkPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJ0ZXh0LWVuZFwiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgIHt7cmVzb3VyY2UuZWZmZWN0aXZlRGF0ZVRpbWUgfCBkYXRlfX1cclxuICAgICAgICAgICAgICAgICAgICAgICAgPC90ZD5cclxuICAgICAgICAgICAgICAgICAgICAgIDwvdHI+XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIEBjYXNlICgnQWxsZXJneUludG9sZXJhbmNlJykge1xyXG4gICAgICAgICAgICAgICAgICAgICAgPHRyPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3tyZXNvdXJjZS5jb2RlPy5jb2Rpbmc/LmF0KDApPy5kaXNwbGF5IHx8IHJlc291cmNlLmNvZGU/LmNvZGluZz8uYXQoMCk/LmNvZGUgfHwgcmVzb3VyY2UuY29kZT8udGV4dH19PC90ZD5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwidGV4dC1lbmRcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICB7e3Jlc291cmNlLm9uc2V0RGF0ZVRpbWUgfCBkYXRlfX1cclxuICAgICAgICAgICAgICAgICAgICAgICAgPC90ZD5cclxuICAgICAgICAgICAgICAgICAgICAgIDwvdHI+XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIEBjYXNlICgnSW1tdW5pemF0aW9uJykge1xyXG4gICAgICAgICAgICAgICAgICAgICAgPHRyPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3tyZXNvdXJjZS52YWNjaW5lQ29kZT8uY29kaW5nPy5hdCgwKT8uZGlzcGxheSB8fCByZXNvdXJjZS52YWNjaW5lQ29kZT8uY29kaW5nPy5hdCgwKT8uY29kZSB8fCByZXNvdXJjZS52YWNjaW5lQ29kZT8udGV4dH19PC90ZD5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwidGV4dC1lbmRcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICB7e3Jlc291cmNlLm9jY3VycmVuY2VEYXRlVGltZSB8IGRhdGV9fVxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8L3RkPlxyXG4gICAgICAgICAgICAgICAgICAgICAgPC90cj5cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgQGNhc2UgKCdPYnNlcnZhdGlvbicpIHtcclxuICAgICAgICAgICAgICAgICAgICAgIDx0cj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7cmVzb3VyY2UuY29kZT8uY29kaW5nPy5hdCgwKT8uZGlzcGxheSB8fCByZXNvdXJjZS5jb2RlPy5jb2Rpbmc/LmF0KDApPy5jb2RlIHx8IHJlc291cmNlLmNvZGU/LnRleHR9fTwvdGQ+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInRleHQtZW5kXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAge3tyZXNvdXJjZS5lZmZlY3RpdmVEYXRlVGltZSB8IGRhdGV9fVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgIDxicj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICBAaWYgKHJlc291cmNlLnZhbHVlUXVhbnRpdHkpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHt7cmVzb3VyY2UudmFsdWVRdWFudGl0eS52YWx1ZX19IHt7cmVzb3VyY2UudmFsdWVRdWFudGl0eS51bml0IHx8IHJlc291cmNlLnZhbHVlUXVhbnRpdHkuY29kZX19XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8L3RkPlxyXG4gICAgICAgICAgICAgICAgICAgICAgPC90cj5cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICA8L25nLXRlbXBsYXRlPlxyXG4gICAgICAgICAgICAgIEBmb3IgKGVudHJ5IG9mIHNlY3Rpb24uZW50cnk7IHRyYWNrIGVudHJ5LnJlZmVyZW5jZSkge1xyXG4gICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cIm15VGVtcGxhdGU7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiByZWZlcmVuY2VNYXBbZW50cnkucmVmZXJlbmNlIHx8ICcnXSB9XCI+PC9uZy1jb250YWluZXI+XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9IEBlbHNlIHtcclxuICAgICAgICAgICAgQGZvciAocmVzb3VyY2VzIG9mIHNvZi5pbXBvcnRlZFJlc291cmNlcyB8IGtleXZhbHVlOyB0cmFjayByZXNvdXJjZXMua2V5KSB7XHJcbiAgICAgICAgICAgICAgPHRyPlxyXG4gICAgICAgICAgICAgICAgPHRkPjxiPnt7cmVzb3VyY2VzLmtleSB8IGNhbWVsQ2FzZVNwYWNlZH19PC9iPjwvdGQ+XHJcbiAgICAgICAgICAgICAgICA8dGQ+PHNwYW4gY2xhc3M9XCJzb2YtYmFkZ2Ugc29mLXByaW1hcnktYmFja2dyb3VuZCBzb2YtdGV4dC1saWdodFwiPnt7cmVzb3VyY2VzLnZhbHVlPy5sZW5ndGh9fTwvc3Bhbj48L3RkPlxyXG4gICAgICAgICAgICAgIDwvdHI+XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICAgIDx0cj5cclxuICAgICAgICAgICAgPHRkIGNvbHNwYW49XCIyXCIgY2xhc3M9XCJ0ZXh0LWVuZFwiPlxyXG4gICAgICAgICAgICAgIDxidXR0b24gY2xhc3M9XCJzb2YtYnV0dG9uIHNvZi1idXR0b24tcHJpbWFyeVwiIHJvdXRlckxpbms9XCIvXCI+Q29udGludWUgPGkgY2xhc3M9XCJzb2YtaWNvbiBzb2YtaWNvbi1jYXJldC1yaWdodFwiPjwvaT48L2J1dHRvbj5cclxuICAgICAgICAgICAgPC90ZD5cclxuICAgICAgICAgIDwvdHI+XHJcbiAgICAgICAgICA8L3Rib2R5PlxyXG4gICAgICAgIDwvdGFibGU+XHJcbiAgICAgIH1cclxuICAgIDwvZGl2PlxyXG4gICAgPHN0eWxlPlxyXG4gICAgICAjc2hjLWNvbnRlbnQgdGQ6Zmlyc3QtY2hpbGQge1xyXG4gICAgICAgIHdpZHRoOiAyNWVtO1xyXG4gICAgICAgIG1heC13aWR0aDogMTAwJTtcclxuICAgICAgfVxyXG4gICAgPC9zdHlsZT5cclxuICBgXHJcbn0pXHJcblxyXG5leHBvcnQgY2xhc3MgU2hjU2hsSGFuZGxlckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCB7XHJcblxyXG4gIGludmFsaWRTaWduYXR1cmU6IGJvb2xlYW4gPSBmYWxzZTtcclxuXHJcbiAgcGF0aWVudDogZmhpcjQuUGF0aWVudHx1bmRlZmluZWQ7XHJcbiAgcmVmZXJlbmNlTWFwOiB7IFtyZWY6IHN0cmluZ106IGZoaXI0LlJlc291cmNlIH0gPSB7fTtcclxuICBpcHM6IGZoaXI0LkNvbXBvc2l0aW9ufHVuZGVmaW5lZDtcclxuXHJcbiAgZ2V0IHBhdGllbnROYW1lKCkge1xyXG4gICAgcmV0dXJuIHRoaXMucGF0aWVudD8ubmFtZT8ubWFwKG5hbWUgPT4gKG5hbWUuZ2l2ZW4/LmpvaW4oJyAnKSB8fCAnJykgKyAnICcgKyBuYW1lLmZhbWlseSkuam9pbignLCAnKVxyXG4gIH1cclxuXHJcbiAgZ2V0IHBhdGllbnRBZ2UoKSB7XHJcbiAgICBpZiAodGhpcy5wYXRpZW50Py5iaXJ0aERhdGUpIHtcclxuICAgICAgcmV0dXJuIE1hdGguY2VpbCgoRGF0ZS5ub3coKSAtIG5ldyBEYXRlKHRoaXMucGF0aWVudC5iaXJ0aERhdGUpLmdldFRpbWUoKSkgLyAoMTAwMCAqIDM2MDAgKiAyNCAqIDM2NSkpXHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICByZXR1cm4gJyc7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSwgcHJpdmF0ZSBhdXRoOiBTbWFydEF1dGhTZXJ2aWNlLCBwcml2YXRlIHNobDogU2hsU2VydmljZSwgcHVibGljIHNvZjogU21hcnRPbkZoaXJTZXJ2aWNlKSB7XHJcbiAgICB0aGlzLmF1dGguZ2V0UGF0aWVudCgpLnRoZW4ocGF0aWVudCA9PiB0aGlzLnBhdGllbnQgPSBwYXRpZW50KTtcclxuICAgIHJvdXRlLmZyYWdtZW50LnN1YnNjcmliZShhc3luYyBmcmFnbWVudCA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgaWYgKGZyYWdtZW50Py5zdGFydHNXaXRoKCdzaGxpbms6LycpKSB7XHJcbiAgICAgICAgICB0aGlzLnNvZi5pbXBvcnRTSENEYXRhKGF3YWl0IHRoaXMuc2hsLmhhbmRsZVNobChmcmFnbWVudCkpXHJcbiAgICAgICAgICB0aGlzLmNoZWNrSVBTKCk7XHJcbiAgICAgICAgfSBlbHNlIGlmIChmcmFnbWVudD8uc3RhcnRzV2l0aCgnc2hjOi8nKSkge1xyXG4gICAgICAgICAgdGhpcy5zb2YuaW1wb3J0U0hDRGF0YShhd2FpdCB0aGlzLnNobC5oYW5kbGVTaGMoZnJhZ21lbnQpKVxyXG4gICAgICAgICAgdGhpcy5jaGVja0lQUygpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBjb25zb2xlLmxvZyhcIk5vIFNIQyBkYXRhIGluIHRoZSBmcmFnbWVudDpcIiwgZnJhZ21lbnQpXHJcbiAgICAgICAgfVxyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIGpvc2UuZXJyb3JzLkpXU1NpZ25hdHVyZVZlcmlmaWNhdGlvbkZhaWxlZCkge1xyXG4gICAgICAgICAgdGhpcy5pbnZhbGlkU2lnbmF0dXJlID0gdHJ1ZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0pXHJcbiAgfVxyXG5cclxuICBuZ09uSW5pdCgpIHtcclxuICB9XHJcblxyXG4gIGxvZ291dCgpIHtcclxuICAgIHRoaXMuc29mLmxvZ291dCgpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBjaGVja0lQUygpIHtcclxuICAgIGlmICh0aGlzLnNvZi5pbXBvcnRlZFJlc291cmNlc1snQ29tcG9zaXRpb24nXSkge1xyXG4gICAgICBjb25zdCBpcHMgPSAoPGZoaXI0LkNvbXBvc2l0aW9uW10+dGhpcy5zb2YuaW1wb3J0ZWRSZXNvdXJjZXNbJ0NvbXBvc2l0aW9uJ10pXHJcbiAgICAgICAgLmZpbmQoKHJlc291cmNlOiBmaGlyNC5Db21wb3NpdGlvbikgPT4gcmVzb3VyY2UudHlwZT8uY29kaW5nPy5zb21lKGNvZGUgPT4gY29kZS5jb2RlID09PSAnNjA1OTEtNScpKVxyXG4gICAgICBpZiAoaXBzKSB7XHJcbiAgICAgICAgdGhpcy5yZWZlcmVuY2VNYXAgPSB0aGlzLnNvZi5nZXRBbGxJbXBvcnRlZFJlc291cmNlcygpLnJlZHVjZSgobWFwOiB7W3JlZjogc3RyaW5nXTogZmhpcjQuUmVzb3VyY2V9LCByZXNvdXJjZSkgPT4ge1xyXG4gICAgICAgICAgbWFwW3Jlc291cmNlLnJlc291cmNlVHlwZSArICcvJyArIHJlc291cmNlLmlkXSA9IHJlc291cmNlO1xyXG4gICAgICAgICAgcmV0dXJuIG1hcDtcclxuICAgICAgICB9LCB7fSlcclxuICAgICAgICB0aGlzLmlwcyA9IGlwcztcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxufVxyXG4iXX0=