UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

142 lines 44.5 kB
import { Component, ViewChild } from '@angular/core'; import { MapComponent } from '@c8y/ngx-components/map'; import { AlertService, ContextRouteService, OptionsService, Permissions, gettext } from '@c8y/ngx-components'; import { ActivatedRoute } from '@angular/router'; import { InventoryService } from '@c8y/client'; import { isUndefined, cloneDeep } from 'lodash-es'; import { takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; import * as i0 from "@angular/core"; import * as i1 from "@c8y/ngx-components"; import * as i2 from "@angular/router"; import * as i3 from "@c8y/client"; import * as i4 from "@c8y/ngx-components/map"; import * as i5 from "@angular/common"; import * as i6 from "@angular/forms"; export class LocationComponent { constructor(contextRouteService, activatedRoute, inventoryService, permissions, optionsService, alertService) { this.contextRouteService = contextRouteService; this.activatedRoute = activatedRoute; this.inventoryService = inventoryService; this.permissions = permissions; this.optionsService = optionsService; this.alertService = alertService; this.isEdit = false; this.canEdit = true; this.config = { realtime: false, follow: true, zoomLevel: 12 }; this.addressSearchTerm = ''; this.hideAddressSearchFeature = false; this.destroy$ = new Subject(); this.mapNominatimUrl = 'https://nominatim.openstreetmap.org/search?format=json&q={searchTerm}'; } ngOnInit() { this.activatedRoute.url.pipe(takeUntil(this.destroy$)).subscribe(() => { this.updateMap(); }); } async updateMap() { const { contextData } = this.contextRouteService.getContextData(this.activatedRoute); this.position = contextData; this.canEdit = await this.permissions.canEdit([Permissions.ROLE_INVENTORY_ADMIN, Permissions.ROLE_INVENTORY_CREATE], contextData); const hasCustomSetting = !isUndefined(this.optionsService.mapNominatimUrl); this.hideAddressSearchFeature = hasCustomSetting && !this.optionsService.mapNominatimUrl; if (hasCustomSetting) { this.mapNominatimUrl = this.optionsService.mapNominatimUrl; } this.map.refresh(); } toggleRealtime() { this.config = { ...this.config, realtime: !this.config.realtime }; } disableRealtime() { this.config = { ...this.config, realtime: false }; } centerMap() { this.config = { ...this.config, center: [this.position.c8y_Position.lat, this.position.c8y_Position.lng] }; } edit() { this.originalPosition = cloneDeep(this.position); this.enableMarkerDragging(); this.disableRealtime(); this.isEdit = true; } cancel() { this.position = this.originalPosition; this.isEdit = false; this.map.markers[0]?.dragging.disable(); this.centerMap(); } async save() { try { const { data } = await this.inventoryService.update({ id: this.position.id, c8y_Position: this.position.c8y_Position }); this.position = data; } catch (ex) { this.alertService.addServerFailure(ex); } this.map.markers[0]?.dragging.disable(); this.centerMap(); this.isEdit = false; } positionChanged(position) { this.position = position; } async searchAddress(searchTerm) { const fetchResponse = await fetch(this.mapNominatimUrl.replace('{searchTerm}', searchTerm)); if (fetchResponse.status !== 200) { this.alertService.danger(gettext('Failed to search for address.')); return; } const data = await fetchResponse.json(); const latLng = data[0]; if (!latLng) { this.alertService.warning(gettext('Address could not be found.')); return; } this.position.c8y_Position.lat = Number(latLng.lat); this.position.c8y_Position.lng = Number(latLng.lon); this.updateMarkerPosition(); if (latLng.licence) { this.map.map.attributionControl.addAttribution(latLng.licence); } } updateMarkerPosition() { this.map.refreshMarkers(); this.enableMarkerDragging(); this.centerMap(); } ngOnDestroy() { this.destroy$.next(); } enableMarkerDragging() { const marker = this.map.markers[0]; if (marker) { marker.dragging.enable(); marker.on('dragend', () => { const latlng = marker.getLatLng(); this.position.c8y_Position = { ...this.position.c8y_Position, lat: latlng.lat, lng: latlng.lng }; this.centerMap(); }); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocationComponent, deps: [{ token: i1.ContextRouteService }, { token: i2.ActivatedRoute }, { token: i3.InventoryService }, { token: i1.Permissions }, { token: i1.OptionsService }, { token: i1.AlertService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: LocationComponent, selector: "c8y-location", viewQueries: [{ propertyName: "map", first: true, predicate: MapComponent, descendants: true }], ngImport: i0, template: "<c8y-action-bar-item placement=\"right\">\n <button\n class=\"btn btn-link c8y-realtime\"\n title=\"{{ 'Toggle realtime' | translate }}\"\n type=\"button\"\n (click)=\"toggleRealtime()\"\n >\n <span\n class=\"c8y-pulse\"\n [ngClass]=\"{ active: config.realtime, inactive: !config.realtime }\"\n ></span>\n <span translate>Realtime</span>\n </button>\n</c8y-action-bar-item>\n<c8y-action-bar-item placement=\"right\">\n <button\n class=\"btn btn-clean\"\n title=\"{{ 'Center`verb`' | translate }}\"\n (click)=\"centerMap()\"\n >\n <i [c8yIcon]=\"'target1'\"></i>\n <span translate>Center`verb`</span>\n </button>\n</c8y-action-bar-item>\n\n<div class=\"card card--grid content-fullpage d-grid grid__col--8-4--md\">\n <div\n class=\"bg-white p-relative\"\n style=\"min-height: 30vh\"\n >\n <c8y-map\n [assets]=\"[position]\"\n [config]=\"config\"\n (onRealtimeUpdate)=\"positionChanged($event)\"\n ></c8y-map>\n </div>\n\n <form\n class=\"d-flex d-col bg-inherit content-fullpage bg-gray-white\"\n #positionForm=\"ngForm\"\n (ngSubmit)=\"save()\"\n >\n <div class=\"card-header large-padding separator sticky-top\">\n <span\n class=\"card-title\"\n translate\n >\n Device location\n </span>\n </div>\n <div class=\"inner-scroll\">\n <div\n class=\"m-16\"\n *ngIf=\"!isEdit\"\n >\n <div class=\"p-b-40\">\n <div\n class=\"text-bold\"\n translate\n >\n Latitude (\u00B0)\n </div>\n {{ position.c8y_Position.lat }}\n </div>\n <div class=\"p-b-40\">\n <div\n class=\"text-bold\"\n translate\n >\n Longitude (\u00B0)\n </div>\n {{ position.c8y_Position.lng }}\n </div>\n <div class=\"p-b-40\">\n <div\n class=\"text-bold\"\n translate\n >\n Altitude (m)\n </div>\n {{ position.c8y_Position.alt }}\n </div>\n </div>\n <div\n class=\"m-16\"\n *ngIf=\"isEdit\"\n >\n <c8y-form-group>\n <label\n for=\"lat\"\n translate\n >\n Latitude (\u00B0)\n </label>\n\n <input\n class=\"form-control\"\n [title]=\"'Latitude (\u00B0)' | translate\"\n id=\"lat\"\n name=\"lat\"\n type=\"number\"\n required\n min=\"-90\"\n max=\"90\"\n (blur)=\"positionForm.valid && updateMarkerPosition()\"\n [(ngModel)]=\"position.c8y_Position.lat\"\n />\n <c8y-messages>\n <c8y-message\n name=\"min\"\n [text]=\"'The minimum latitude value is -90\u00B0.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"max\"\n [text]=\"'The maximum latitude value is 90\u00B0.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <c8y-form-group>\n <label\n for=\"lat\"\n translate\n >\n Longitude (\u00B0)\n </label>\n\n <input\n class=\"form-control\"\n [title]=\"'Longitude (\u00B0)' | translate\"\n id=\"lng\"\n name=\"lng\"\n type=\"number\"\n required\n min=\"-180\"\n max=\"180\"\n (blur)=\"positionForm.valid && updateMarkerPosition()\"\n [(ngModel)]=\"position.c8y_Position.lng\"\n />\n <c8y-messages>\n <c8y-message\n name=\"min\"\n [text]=\"'The minimum longitude value is -180\u00B0.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"max\"\n [text]=\"'The maximum longitude value is 180\u00B0.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <c8y-form-group>\n <label\n for=\"alt\"\n translate\n >\n Altitude (m)\n </label>\n\n <input\n class=\"form-control\"\n [title]=\"'Altitude (m)' | translate\"\n id=\"alt\"\n name=\"alt\"\n type=\"number\"\n [(ngModel)]=\"position.c8y_Position.alt\"\n />\n </c8y-form-group>\n </div>\n\n <form\n class=\"card-block bg-level-1\"\n #addressSearch=\"ngForm\"\n novalidate\n (submit)=\"searchAddress(addressSearchTerm)\"\n *ngIf=\"isEdit && !hideAddressSearchFeature\"\n >\n <div class=\"form-group\">\n <label\n for=\"searchAddress\"\n translate\n >\n Find lat/long by address\n </label>\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n id=\"searchAddress\"\n name=\"addressSearchTerm\"\n type=\"text\"\n [placeholder]=\"'e.g. Speditionstra\u00DFe 13, D\u00FCsseldorf`LOCALIZE`' | translate\"\n [(ngModel)]=\"addressSearchTerm\"\n />\n <span class=\"input-group-btn\">\n <button\n class=\"btn btn-dot\"\n [title]=\"'Search' | translate\"\n type=\"submit\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n c8y-icon=\"search\"\n ></i>\n </button>\n </span>\n </div>\n </div>\n </form>\n </div>\n <div class=\"card-footer separator\">\n <button\n class=\"btn btn-default\"\n [title]=\"'Edit location' | translate\"\n type=\"button\"\n *ngIf=\"canEdit && !isEdit\"\n (click)=\"edit()\"\n translate\n >\n Edit location\n </button>\n <button\n class=\"btn btn-default\"\n [title]=\"'Cancel' | translate\"\n type=\"button\"\n (click)=\"cancel()\"\n *ngIf=\"isEdit\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n [title]=\"'Save' | translate\"\n type=\"submit\"\n *ngIf=\"isEdit\"\n translate\n [disabled]=\"!positionForm.valid\"\n >\n Save\n </button>\n </div>\n </form>\n</div>\n", dependencies: [{ kind: "component", type: i4.MapComponent, selector: "c8y-map", inputs: ["config", "assets", "polyline$", "polylineOptions"], outputs: ["onRealtimeUpdate", "onMove", "onMoveEnd", "onZoomStart", "onZoomEnd", "onMap", "onInit"] }, { kind: "component", type: i1.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.MinValidationDirective, selector: "[min]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidationDirective, selector: "[max]", inputs: ["max"] }, { kind: "directive", type: i6.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i6.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i6.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i6.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i6.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i1.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LocationComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-location', template: "<c8y-action-bar-item placement=\"right\">\n <button\n class=\"btn btn-link c8y-realtime\"\n title=\"{{ 'Toggle realtime' | translate }}\"\n type=\"button\"\n (click)=\"toggleRealtime()\"\n >\n <span\n class=\"c8y-pulse\"\n [ngClass]=\"{ active: config.realtime, inactive: !config.realtime }\"\n ></span>\n <span translate>Realtime</span>\n </button>\n</c8y-action-bar-item>\n<c8y-action-bar-item placement=\"right\">\n <button\n class=\"btn btn-clean\"\n title=\"{{ 'Center`verb`' | translate }}\"\n (click)=\"centerMap()\"\n >\n <i [c8yIcon]=\"'target1'\"></i>\n <span translate>Center`verb`</span>\n </button>\n</c8y-action-bar-item>\n\n<div class=\"card card--grid content-fullpage d-grid grid__col--8-4--md\">\n <div\n class=\"bg-white p-relative\"\n style=\"min-height: 30vh\"\n >\n <c8y-map\n [assets]=\"[position]\"\n [config]=\"config\"\n (onRealtimeUpdate)=\"positionChanged($event)\"\n ></c8y-map>\n </div>\n\n <form\n class=\"d-flex d-col bg-inherit content-fullpage bg-gray-white\"\n #positionForm=\"ngForm\"\n (ngSubmit)=\"save()\"\n >\n <div class=\"card-header large-padding separator sticky-top\">\n <span\n class=\"card-title\"\n translate\n >\n Device location\n </span>\n </div>\n <div class=\"inner-scroll\">\n <div\n class=\"m-16\"\n *ngIf=\"!isEdit\"\n >\n <div class=\"p-b-40\">\n <div\n class=\"text-bold\"\n translate\n >\n Latitude (\u00B0)\n </div>\n {{ position.c8y_Position.lat }}\n </div>\n <div class=\"p-b-40\">\n <div\n class=\"text-bold\"\n translate\n >\n Longitude (\u00B0)\n </div>\n {{ position.c8y_Position.lng }}\n </div>\n <div class=\"p-b-40\">\n <div\n class=\"text-bold\"\n translate\n >\n Altitude (m)\n </div>\n {{ position.c8y_Position.alt }}\n </div>\n </div>\n <div\n class=\"m-16\"\n *ngIf=\"isEdit\"\n >\n <c8y-form-group>\n <label\n for=\"lat\"\n translate\n >\n Latitude (\u00B0)\n </label>\n\n <input\n class=\"form-control\"\n [title]=\"'Latitude (\u00B0)' | translate\"\n id=\"lat\"\n name=\"lat\"\n type=\"number\"\n required\n min=\"-90\"\n max=\"90\"\n (blur)=\"positionForm.valid && updateMarkerPosition()\"\n [(ngModel)]=\"position.c8y_Position.lat\"\n />\n <c8y-messages>\n <c8y-message\n name=\"min\"\n [text]=\"'The minimum latitude value is -90\u00B0.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"max\"\n [text]=\"'The maximum latitude value is 90\u00B0.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <c8y-form-group>\n <label\n for=\"lat\"\n translate\n >\n Longitude (\u00B0)\n </label>\n\n <input\n class=\"form-control\"\n [title]=\"'Longitude (\u00B0)' | translate\"\n id=\"lng\"\n name=\"lng\"\n type=\"number\"\n required\n min=\"-180\"\n max=\"180\"\n (blur)=\"positionForm.valid && updateMarkerPosition()\"\n [(ngModel)]=\"position.c8y_Position.lng\"\n />\n <c8y-messages>\n <c8y-message\n name=\"min\"\n [text]=\"'The minimum longitude value is -180\u00B0.' | translate\"\n ></c8y-message>\n <c8y-message\n name=\"max\"\n [text]=\"'The maximum longitude value is 180\u00B0.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <c8y-form-group>\n <label\n for=\"alt\"\n translate\n >\n Altitude (m)\n </label>\n\n <input\n class=\"form-control\"\n [title]=\"'Altitude (m)' | translate\"\n id=\"alt\"\n name=\"alt\"\n type=\"number\"\n [(ngModel)]=\"position.c8y_Position.alt\"\n />\n </c8y-form-group>\n </div>\n\n <form\n class=\"card-block bg-level-1\"\n #addressSearch=\"ngForm\"\n novalidate\n (submit)=\"searchAddress(addressSearchTerm)\"\n *ngIf=\"isEdit && !hideAddressSearchFeature\"\n >\n <div class=\"form-group\">\n <label\n for=\"searchAddress\"\n translate\n >\n Find lat/long by address\n </label>\n <div class=\"input-group input-group-search\">\n <input\n class=\"form-control\"\n id=\"searchAddress\"\n name=\"addressSearchTerm\"\n type=\"text\"\n [placeholder]=\"'e.g. Speditionstra\u00DFe 13, D\u00FCsseldorf`LOCALIZE`' | translate\"\n [(ngModel)]=\"addressSearchTerm\"\n />\n <span class=\"input-group-btn\">\n <button\n class=\"btn btn-dot\"\n [title]=\"'Search' | translate\"\n type=\"submit\"\n >\n <i\n class=\"dlt-c8y-icon-search\"\n c8y-icon=\"search\"\n ></i>\n </button>\n </span>\n </div>\n </div>\n </form>\n </div>\n <div class=\"card-footer separator\">\n <button\n class=\"btn btn-default\"\n [title]=\"'Edit location' | translate\"\n type=\"button\"\n *ngIf=\"canEdit && !isEdit\"\n (click)=\"edit()\"\n translate\n >\n Edit location\n </button>\n <button\n class=\"btn btn-default\"\n [title]=\"'Cancel' | translate\"\n type=\"button\"\n (click)=\"cancel()\"\n *ngIf=\"isEdit\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n [title]=\"'Save' | translate\"\n type=\"submit\"\n *ngIf=\"isEdit\"\n translate\n [disabled]=\"!positionForm.valid\"\n >\n Save\n </button>\n </div>\n </form>\n</div>\n" }] }], ctorParameters: () => [{ type: i1.ContextRouteService }, { type: i2.ActivatedRoute }, { type: i3.InventoryService }, { type: i1.Permissions }, { type: i1.OptionsService }, { type: i1.AlertService }], propDecorators: { map: [{ type: ViewChild, args: [MapComponent] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYXRpb24uY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vbG9jYXRpb24vbG9jYXRpb24uY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vbG9jYXRpb24vbG9jYXRpb24uY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBcUIsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQW9DLE1BQU0seUJBQXlCLENBQUM7QUFDekYsT0FBTyxFQUNMLFlBQVksRUFDWixtQkFBbUIsRUFDbkIsY0FBYyxFQUNkLFdBQVcsRUFDWCxPQUFPLEVBQ1IsTUFBTSxxQkFBcUIsQ0FBQztBQUM3QixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDakQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQy9DLE9BQU8sRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ25ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDOzs7Ozs7OztBQU0vQixNQUFNLE9BQU8saUJBQWlCO0lBZTVCLFlBQ1UsbUJBQXdDLEVBQ3hDLGNBQThCLEVBQzlCLGdCQUFrQyxFQUNsQyxXQUF3QixFQUN4QixjQUE4QixFQUM5QixZQUEwQjtRQUwxQix3QkFBbUIsR0FBbkIsbUJBQW1CLENBQXFCO1FBQ3hDLG1CQUFjLEdBQWQsY0FBYyxDQUFnQjtRQUM5QixxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQWtCO1FBQ2xDLGdCQUFXLEdBQVgsV0FBVyxDQUFhO1FBQ3hCLG1CQUFjLEdBQWQsY0FBYyxDQUFnQjtRQUM5QixpQkFBWSxHQUFaLFlBQVksQ0FBYztRQWxCcEMsV0FBTSxHQUFHLEtBQUssQ0FBQztRQUNmLFlB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Q0FBQyxZQUFZLEdBQUc7b0JBQzNCLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxZQUFZO29CQUM3QixHQUFHLEVBQUUsTUFBTSxDQUFDLEdBQUc7b0JBQ2YsR0FBRyxFQUFFLE1BQU0sQ0FBQyxHQUFHO2lCQUNoQixDQUFDO2dCQUNGLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNuQixDQUFDLENBQUMsQ0FBQztRQUNMLENBQUM7SUFDSCxDQUFDOytHQTdJVSxpQkFBaUI7bUdBQWpCLGlCQUFpQix5RkFZakIsWUFBWSxnREMvQnpCLGsvTUFpUEE7OzRGRDlOYSxpQkFBaUI7a0JBSjdCLFNBQVM7K0JBQ0UsY0FBYztzT0FnQnhCLEdBQUc7c0JBREYsU0FBUzt1QkFBQyxZQUFZIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkRlc3Ryb3ksIE9uSW5pdCwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBNYXBDb21wb25lbnQsIE1hcENvbmZpZywgUG9zaXRpb25NYW5hZ2VkT2JqZWN0IH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cy9tYXAnO1xuaW1wb3J0IHtcbiAgQWxlcnRTZXJ2aWNlLFxuICBDb250ZXh0Um91dGVTZXJ2aWNlLFxuICBPcHRpb25zU2VydmljZSxcbiAgUGVybWlzc2lvbnMsXG4gIGdldHRleHRcbn0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQgeyBBY3RpdmF0ZWRSb3V0ZSB9IGZyb20gJ0Bhbmd1bGFyL3JvdXRlcic7XG5pbXBvcnQgeyBJbnZlbnRvcnlTZXJ2aWNlIH0gZnJvbSAnQGM4eS9jbGllbnQnO1xuaW1wb3J0IHsgaXNVbmRlZmluZWQsIGNsb25lRGVlcCB9IGZyb20gJ2xvZGFzaC1lcyc7XG5pbXBvcnQgeyB0YWtlVW50aWwgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2M4eS1sb2NhdGlvbicsXG4gIHRlbXBsYXRlVXJsOiAnLi9sb2NhdGlvbi5jb21wb25lbnQuaHRtbCdcbn0pXG5leHBvcnQgY2xhc3MgTG9jYXRpb25Db21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gIHBvc2l0aW9uOiBQb3NpdGlvbk1hbmFnZWRPYmplY3Q7XG4gIG9yaWdpbmFsUG9zaXRpb246IFBvc2l0aW9uTWFuYWdlZE9iamVjdDtcbiAgaXNFZGl0ID0gZmFsc2U7XG4gIGNhbkVkaXQgPSB0cnVlO1xuICBjb25maWc6IE1hcENvbmZpZyA9IHsgcmVhbHRpbWU6IGZhbHNlLCBmb2xsb3c6IHRydWUsIHpvb21MZXZlbDogMTIgfTtcbiAgYWRkcmVzc1NlYXJjaFRlcm0gPSAnJztcbiAgaGlkZUFkZHJlc3NTZWFyY2hGZWF0dXJlID0gZmFsc2U7XG5cbiAgcHJpdmF0ZSBkZXN0cm95JCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG4gIHByaXZhdGUgbWFwTm9taW5hdGltVXJsID0gJ2h0dHBzOi8vbm9taW5hdGltLm9wZW5zdHJlZXRtYXAub3JnL3NlYXJjaD9mb3JtYXQ9anNvbiZxPXtzZWFyY2hUZXJtfSc7XG5cbiAgQFZpZXdDaGlsZChNYXBDb21wb25lbnQpXG4gIG1hcDogTWFwQ29tcG9uZW50O1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgY29udGV4dFJvdXRlU2VydmljZTogQ29udGV4dFJvdXRlU2VydmljZSxcbiAgICBwcml2YXRlIGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSxcbiAgICBwcml2YXRlIGludmVudG9yeVNlcnZpY2U6IEludmVudG9yeVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBwZXJtaXNzaW9uczogUGVybWlzc2lvbnMsXG4gICAgcHJpdmF0ZSBvcHRpb25zU2VydmljZTogT3B0aW9uc1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBhbGVydFNlcnZpY2U6IEFsZXJ0U2VydmljZVxuICApIHt9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgdGhpcy5hY3RpdmF0ZWRSb3V0ZS51cmwucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICB0aGlzLnVwZGF0ZU1hcCgpO1xuICAgIH0pO1xuICB9XG5cbiAgYXN5bmMgdXBkYXRlTWFwKCkge1xuICAgIGNvbnN0IHsgY29udGV4dERhdGEgfSA9IHRoaXMuY29udGV4dFJvdXRlU2VydmljZS5nZXRDb250ZXh0RGF0YSh0aGlzLmFjdGl2YXRlZFJvdXRlKTtcbiAgICB0aGlzLnBvc2l0aW9uID0gY29udGV4dERhdGEgYXMgUG9zaXRpb25NYW5hZ2VkT2JqZWN0O1xuICAgIHRoaXMuY2FuRWRpdCA9IGF3YWl0IHRoaXMucGVybWlzc2lvbnMuY2FuRWRpdChcbiAgICAgIFtQZXJtaXNzaW9ucy5ST0xFX0lOVkVOVE9SWV9BRE1JTiwgUGVybWlzc2lvbnMuUk9MRV9JTlZFTlRPUllfQ1JFQVRFXSxcbiAgICAgIGNvbnRleHREYXRhXG4gICAgKTtcblxuICAgIGNvbnN0IGhhc0N1c3RvbVNldHRpbmcgPSAhaXNVbmRlZmluZWQodGhpcy5vcHRpb25zU2VydmljZS5tYXBOb21pbmF0aW1VcmwpO1xuICAgIHRoaXMuaGlkZUFkZHJlc3NTZWFyY2hGZWF0dXJlID0gaGFzQ3VzdG9tU2V0dGluZyAmJiAhdGhpcy5vcHRpb25zU2VydmljZS5tYXBOb21pbmF0aW1Vcmw7XG5cbiAgICBpZiAoaGFzQ3VzdG9tU2V0dGluZykge1xuICAgICAgdGhpcy5tYXBOb21pbmF0aW1VcmwgPSB0aGlzLm9wdGlvbnNTZXJ2aWNlLm1hcE5vbWluYXRpbVVybDtcbiAgICB9XG5cbiAgICB0aGlzLm1hcC5yZWZyZXNoKCk7XG4gIH1cblxuICB0b2dnbGVSZWFsdGltZSgpIHtcbiAgICB0aGlzLmNvbmZpZyA9IHsgLi4udGhpcy5jb25maWcsIHJlYWx0aW1lOiAhdGhpcy5jb25maWcucmVhbHRpbWUgfTtcbiAgfVxuXG4gIGRpc2FibGVSZWFsdGltZSgpIHtcbiAgICB0aGlzLmNvbmZpZyA9IHsgLi4udGhpcy5jb25maWcsIHJlYWx0aW1lOiBmYWxzZSB9O1xuICB9XG5cbiAgY2VudGVyTWFwKCkge1xuICAgIHRoaXMuY29uZmlnID0ge1xuICAgICAgLi4udGhpcy5jb25maWcsXG4gICAgICBjZW50ZXI6IFt0aGlzLnBvc2l0aW9uLmM4eV9Qb3NpdGlvbi5sYXQsIHRoaXMucG9zaXRpb24uYzh5X1Bvc2l0aW9uLmxuZ11cbiAgICB9O1xuICB9XG5cbiAgZWRpdCgpIHtcbiAgICB0aGlzLm9yaWdpbmFsUG9zaXRpb24gPSBjbG9uZURlZXAodGhpcy5wb3NpdGlvbik7XG4gICAgdGhpcy5lbmFibGVNYXJrZXJEcmFnZ2luZygpO1xuICAgIHRoaXMuZGlzYWJsZVJlYWx0aW1lKCk7XG4gICAgdGhpcy5pc0VkaXQgPSB0cnVlO1xuICB9XG5cbiAgY2FuY2VsKCkge1xuICAgIHRoaXMucG9zaXRpb24gPSB0aGlzLm9yaWdpbmFsUG9zaXRpb247XG4gICAgdGhpcy5pc0VkaXQgPSBmYWxzZTtcbiAgICB0aGlzLm1hcC5tYXJrZXJzWzBdPy5kcmFnZ2luZy5kaXNhYmxlKCk7XG4gICAgdGhpcy5jZW50ZXJNYXAoKTtcbiAgfVxuXG4gIGFzeW5jIHNhdmUoKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHsgZGF0YSB9ID0gYXdhaXQgdGhpcy5pbnZlbnRvcnlTZXJ2aWNlLnVwZGF0ZSh7XG4gICAgICAgIGlkOiB0aGlzLnBvc2l0aW9uLmlkLFxuICAgICAgICBjOHlfUG9zaXRpb246IHRoaXMucG9zaXRpb24uYzh5X1Bvc2l0aW9uXG4gICAgICB9KTtcbiAgICAgIHRoaXMucG9zaXRpb24gPSBkYXRhIGFzIFBvc2l0aW9uTWFuYWdlZE9iamVjdDtcbiAgICB9IGNhdGNoIChleCkge1xuICAgICAgdGhpcy5hbGVydFNlcnZpY2UuYWRkU2VydmVyRmFpbHVyZShleCk7XG4gICAgfVxuICAgIHRoaXMubWFwLm1hcmtlcnNbMF0/LmRyYWdnaW5nLmRpc2FibGUoKTtcbiAgICB0aGlzLmNlbnRlck1hcCgpO1xuICAgIHRoaXMuaXNFZGl0ID0gZmFsc2U7XG4gIH1cblxuICBwb3NpdGlvbkNoYW5nZWQocG9zaXRpb246IFBvc2l0aW9uTWFuYWdlZE9iamVjdCkge1xuICAgIHRoaXMucG9zaXRpb24gPSBwb3NpdGlvbjtcbiAgfVxuXG4gIGFzeW5jIHNlYXJjaEFkZHJlc3Moc2VhcmNoVGVybTogc3RyaW5nKSB7XG4gICAgY29uc3QgZmV0Y2hSZXNwb25zZSA9IGF3YWl0IGZldGNoKHRoaXMubWFwTm9taW5hdGltVXJsLnJlcGxhY2UoJ3tzZWFyY2hUZXJtfScsIHNlYXJjaFRlcm0pKTtcbiAgICBpZiAoZmV0Y2hSZXNwb25zZS5zdGF0dXMgIT09IDIwMCkge1xuICAgICAgdGhpcy5hbGVydFNlcnZpY2UuZGFuZ2VyKGdldHRleHQoJ0ZhaWxlZCB0byBzZWFyY2ggZm9yIGFkZHJlc3MuJykpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBkYXRhOiBBcnJheTx7IGxhdDogc3RyaW5nIHwgbnVtYmVyOyBsb246IHN0cmluZyB8IG51bWJlcjsgbGljZW5jZT86IHN0cmluZyB9PiA9XG4gICAgICBhd2FpdCBmZXRjaFJlc3BvbnNlLmpzb24oKTtcbiAgICBjb25zdCBsYXRMbmcgPSBkYXRhWzBdO1xuICAgIGlmICghbGF0TG5nKSB7XG4gICAgICB0aGlzLmFsZXJ0U2VydmljZS53YXJuaW5nKGdldHRleHQoJ0FkZHJlc3MgY291bGQgbm90IGJlIGZvdW5kLicpKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5wb3NpdGlvbi5jOHlfUG9zaXRpb24ubGF0ID0gTnVtYmVyKGxhdExuZy5sYXQpO1xuICAgIHRoaXMucG9zaXRpb24uYzh5X1Bvc2l0aW9uLmxuZyA9IE51bWJlcihsYXRMbmcubG9uKTtcbiAgICB0aGlzLnVwZGF0ZU1hcmtlclBvc2l0aW9uKCk7XG4gICAgaWYgKGxhdExuZy5saWNlbmNlKSB7XG4gICAgICB0aGlzLm1hcC5tYXAuYXR0cmlidXRpb25Db250cm9sLmFkZEF0dHJpYnV0aW9uKGxhdExuZy5saWNlbmNlKTtcbiAgICB9XG4gIH1cblxuICB1cGRhdGVNYXJrZXJQb3NpdGlvbigpIHtcbiAgICB0aGlzLm1hcC5yZWZyZXNoTWFya2VycygpO1xuICAgIHRoaXMuZW5hYmxlTWFya2VyRHJhZ2dpbmcoKTtcbiAgICB0aGlzLmNlbnRlck1hcCgpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XG4gIH1cblxuICBwcml2YXRlIGVuYWJsZU1hcmtlckRyYWdnaW5nKCkge1xuICAgIGNvbnN0IG1hcmtlciA9IHRoaXMubWFwLm1hcmtlcnNbMF07XG4gICAgaWYgKG1hcmtlcikge1xuICAgICAgbWFya2VyLmRyYWdnaW5nLmVuYWJsZSgpO1xuICAgICAgbWFya2VyLm9uKCdkcmFnZW5kJywgKCkgPT4ge1xuICAgICAgICBjb25zdCBsYXRsbmcgPSBtYXJrZXIuZ2V0TGF0TG5nKCk7XG4gICAgICAgIHRoaXMucG9zaXRpb24uYzh5X1Bvc2l0aW9uID0ge1xuICAgICAgICAgIC4uLnRoaXMucG9zaXRpb24uYzh5X1Bvc2l0aW9uLFxuICAgICAgICAgIGxhdDogbGF0bG5nLmxhdCxcbiAgICAgICAgICBsbmc6IGxhdGxuZy5sbmdcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5jZW50ZXJNYXAoKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxufVxuIiwiPGM4eS1hY3Rpb24tYmFyLWl0ZW0gcGxhY2VtZW50PVwicmlnaHRcIj5cbiAgPGJ1dHRvblxuICAgIGNsYXNzPVwiYnRuIGJ0bi1saW5rIGM4eS1yZWFsdGltZVwiXG4gICAgdGl0bGU9XCJ7eyAnVG9nZ2xlIHJlYWx0aW1lJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgdHlwZT1cImJ1dHRvblwiXG4gICAgKGNsaWNrKT1cInRvZ2dsZVJlYWx0aW1lKClcIlxuICA+XG4gICAgPHNwYW5cbiAgICAgIGNsYXNzPVwiYzh5LXB1bHNlXCJcbiAgICAgIFtuZ0NsYXNzXT1cInsgYWN0aXZlOiBjb25maWcucmVhbHRpbWUsIGluYWN0aXZlOiAhY29uZmlnLnJlYWx0aW1lIH1cIlxuICAgID48L3NwYW4+XG4gICAgPHNwYW4gdHJhbnNsYXRlPlJlYWx0aW1lPC9zcGFuPlxuICA8L2J1dHRvbj5cbjwvYzh5LWFjdGlvbi1iYXItaXRlbT5cbjxjOHktYWN0aW9uLWJhci1pdGVtIHBsYWNlbWVudD1cInJpZ2h0XCI+XG4gIDxidXR0b25cbiAgICBjbGFzcz1cImJ0biBidG4tY2xlYW5cIlxuICAgIHRpdGxlPVwie3sgJ0NlbnRlcmB2ZXJiYCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgIChjbGljayk9XCJjZW50ZXJNYXAoKVwiXG4gID5cbiAgICA8aSBbYzh5SWNvbl09XCIndGFyZ2V0MSdcIj48L2k+XG4gICAgPHNwYW4gdHJhbnNsYXRlPkNlbnRlcmB2ZXJiYDwvc3Bhbj5cbiAgPC9idXR0b24+XG48L2M4eS1hY3Rpb24tYmFyLWl0ZW0+XG5cbjxkaXYgY2xhc3M9XCJjYXJkIGNhcmQtLWdyaWQgY29udGVudC1mdWxscGFnZSBkLWdyaWQgZ3JpZF9fY29sLS04LTQtLW1kXCI+XG4gIDxkaXZcbiAgICBjbGFzcz1cImJnLXdoaXRlIHAtcmVsYXRpdmVcIlxuICAgIHN0eWxlPVwibWluLWhlaWdodDogMzB2aFwiXG4gID5cbiAgICA8Yzh5LW1hcFxuICAgICAgW2Fzc2V0c109XCJbcG9zaXRpb25dXCJcbiAgICAgIFtjb25maWddPVwiY29uZmlnXCJcbiAgICAgIChvblJlYWx0aW1lVXBkYXRlKT1cInBvc2l0aW9uQ2hhbmdlZCgkZXZlbnQpXCJcbiAgICA+PC9jOHktbWFwPlxuICA8L2Rpdj5cblxuICA8Zm9ybVxuICAgIGNsYXNzPVwiZC1mbGV4IGQtY29sIGJnLWluaGVyaXQgY29udGVudC1mdWxscGFnZSBiZy1ncmF5LXdoaXRlXCJcbiAgICAjcG9zaXRpb25Gb3JtPVwibmdGb3JtXCJcbiAgICAobmdTdWJtaXQpPVwic2F2ZSgpXCJcbiAgPlxuICAgIDxkaXYgY2xhc3M9XCJjYXJkLWhlYWRlciBsYXJnZS1wYWRkaW5nIHNlcGFyYXRvciBzdGlja3ktdG9wXCI+XG4gICAgICA8c3BhblxuICAgICAgICBjbGFzcz1cImNhcmQtdGl0bGVcIlxuICAgICAgICB0cmFuc2xhdGVcbiAgICAgID5cbiAgICAgICAgRGV2aWNlIGxvY2F0aW9uXG4gICAgICA8L3NwYW4+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cImlubmVyLXNjcm9sbFwiPlxuICAgICAgPGRpdlxuICAgICAgICBjbGFzcz1cIm0tMTZcIlxuICAgICAgICAqbmdJZj1cIiFpc0VkaXRcIlxuICAgICAgPlxuICAgICAgICA8ZGl2IGNsYXNzPVwicC1iLTQwXCI+XG4gICAgICAgICAgPGRpdlxuICAgICAgICAgICAgY2xhc3M9XCJ0ZXh0LWJvbGRcIlxuICAgICAgICAgICAgdHJhbnNsYXRlXG4gICAgICAgICAgPlxuICAgICAgICAgICAgTGF0aXR1ZGUgKMKwKVxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIHt7IHBvc2l0aW9uLmM4eV9Qb3NpdGlvbi5sYXQgfX1cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJwLWItNDBcIj5cbiAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICBjbGFzcz1cInRleHQtYm9sZFwiXG4gICAgICAgICAgICB0cmFuc2xhdGVcbiAgICAgICAgICA+XG4gICAgICAgICAgICBMb25naXR1ZGUgKMKwKVxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIHt7IHBvc2l0aW9uLmM4eV9Qb3NpdGlvbi5sbmcgfX1cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJwLWItNDBcIj5cbiAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICBjbGFzcz1cInRleHQtYm9sZFwiXG4gICAgICAgICAgICB0cmFuc2xhdGVcbiAgICAgICAgICA+XG4gICAgICAgICAgICBBbHRpdHVkZSAobSlcbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICB7eyBwb3NpdGlvbi5jOHlfUG9zaXRpb24uYWx0IH19XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwibS0xNlwiXG4gICAgICAgICpuZ0lmPVwiaXNFZGl0XCJcbiAgICAgID5cbiAgICAgICAgPGM4eS1mb3JtLWdyb3VwPlxuICAgICAgICAgIDxsYWJlbFxuICAgICAgICAgICAgZm9yPVwibGF0XCJcbiAgICAgICAgICAgIHRyYW5zbGF0ZVxuICAgICAgICAgID5cbiAgICAgICAgICAgIExhdGl0dWRlICjCsClcbiAgICAgICAgICA8L2xhYmVsPlxuXG4gICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbFwiXG4gICAgICAgICAgICBbdGl0bGVdPVwiJ0xhdGl0dWRlICjCsCknIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgICAgIGlkPVwibGF0XCJcbiAgICAgICAgICAgIG5hbWU9XCJsYXRcIlxuICAgICAgICAgICAgdHlwZT1cIm51bWJlclwiXG4gICAgICAgICAgICByZXF1aXJlZFxuICAgICAgICAgICAgbWluPVwiLTkwXCJcbiAgICAgICAgICAgIG1heD1cIjkwXCJcbiAgICAgICAgICAgIChibHVyKT1cInBvc2l0aW9uRm9ybS52YWxpZCAmJiB1cGRhdGVNYXJrZXJQb3NpdGlvbigpXCJcbiAgICAgICAgICAgIFsobmdNb2RlbCldPVwicG9zaXRpb24uYzh5X1Bvc2l0aW9uLmxhdFwiXG4gICAgICAgICAgLz5cbiAgICAgICAgICA8Yzh5LW1lc3NhZ2VzPlxuICAgICAgICAgICAgPGM4eS1tZXNzYWdlXG4gICAgICAgICAgICAgIG5hbWU9XCJtaW5cIlxuICAgICAgICAgICAgICBbdGV4dF09XCInVGhlIG1pbmltdW0gbGF0aXR1ZGUgdmFsdWUgaXMgLTkwwrAuJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgICAgICA+PC9jOHktbWVzc2FnZT5cbiAgICAgICAgICAgIDxjOHktbWVzc2FnZVxuICAgICAgICAgICAgICBuYW1lPVwibWF4XCJcbiAgICAgICAgICAgICAgW3RleHRdPVwiJ1RoZSBtYXhpbXVtIGxhdGl0dWRlIHZhbHVlIGlzIDkwwrAuJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgICAgICA+PC9jOHktbWVzc2FnZT5cbiAgICAgICAgICA8L2M4eS1tZXNzYWdlcz5cbiAgICAgICAgPC9jOHktZm9ybS1ncm91cD5cbiAgICAgICAgPGM4eS1mb3JtLWdyb3VwPlxuICAgICAgICAgIDxsYWJlbFxuICAgICAgICAgICAgZm9yPVwibGF0XCJcbiAgICAgICAgICAgIHRyYW5zbGF0ZVxuICAgICAgICAgID5cbiAgICAgICAgICAgIExvbmdpdHVkZSAowrApXG4gICAgICAgICAgPC9sYWJlbD5cblxuICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIlxuICAgICAgICAgICAgW3RpdGxlXT1cIidMb25naXR1ZGUgKMKwKScgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgICAgaWQ9XCJsbmdcIlxuICAgICAgICAgICAgbmFtZT1cImxuZ1wiXG4gICAgICAgICAgICB0eXBlPVwibnVtYmVyXCJcbiAgICAgICAgICAgIHJlcXVpcmVkXG4gICAgICAgICAgICBtaW49XCItMTgwXCJcbiAgICAgICAgICAgIG1heD1cIjE4MFwiXG4gICAgICAgICAgICAoYmx1cik9XCJwb3NpdGlvbkZvcm0udmFsaWQgJiYgdXBkYXRlTWFya2VyUG9zaXRpb24oKVwiXG4gICAgICAgICAgICBbKG5nTW9kZWwpXT1cInBvc2l0aW9uLmM4eV9Qb3NpdGlvbi5sbmdcIlxuICAgICAgICAgIC8+XG4gICAgICAgICAgPGM4eS1tZXNzYWdlcz5cbiAgICAgICAgICAgIDxjOHktbWVzc2FnZVxuICAgICAgICAgICAgICBuYW1lPVwibWluXCJcbiAgICAgICAgICAgICAgW3RleHRdPVwiJ1RoZSBtaW5pbXVtIGxvbmdpdHVkZSB2YWx1ZSBpcyAtMTgwwrAuJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgICAgICA+PC9jOHktbWVzc2FnZT5cbiAgICAgICAgICAgIDxjOHktbWVzc2FnZVxuICAgICAgICAgICAgICBuYW1lPVwibWF4XCJcbiAgICAgICAgICAgICAgW3RleHRdPVwiJ1RoZSBtYXhpbXVtIGxvbmdpdHVkZSB2YWx1ZSBpcyAxODDCsC4nIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgICAgID48L2M4eS1tZXNzYWdlPlxuICAgICAgICAgIDwvYzh5LW1lc3NhZ2VzPlxuICAgICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgICAgICA8Yzh5LWZvcm0tZ3JvdXA+XG4gICAgICAgICAgPGxhYmVsXG4gICAgICAgICAgICBmb3I9XCJhbHRcIlxuICAgICAgICAgICAgdHJhbnNsYXRlXG4gICAgICAgICAgPlxuICAgICAgICAgICAgQWx0aXR1ZGUgKG0pXG4gICAgICAgICAgPC9sYWJlbD5cblxuICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIlxuICAgICAgICAgICAgW3RpdGxlXT1cIidBbHRpdHVkZSAobSknIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgICAgIGlkPVwiYWx0XCJcbiAgICAgICAgICAgIG5hbWU9XCJhbHRcIlxuICAgICAgICAgICAgdHlwZT1cIm51bWJlclwiXG4gICAgICAgICAgICBbKG5nTW9kZWwpXT1cInBvc2l0aW9uLmM4eV9Qb3NpdGlvbi5hbHRcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPGZvcm1cbiAgICAgICAgY2xhc3M9XCJjYXJkLWJsb2NrIGJnLWxldmVsLTFcIlxuICAgICAgICAjYWRkcmVzc1NlYXJjaD1cIm5nRm9ybVwiXG4gICAgICAgIG5vdmFsaWRhdGVcbiAgICAgICAgKHN1Ym1pdCk9XCJzZWFyY2hBZGRyZXNzKGFkZHJlc3NTZWFyY2hUZXJtKVwiXG4gICAgICAgICpuZ0lmPVwiaXNFZGl0ICYmICFoaWRlQWRkcmVzc1NlYXJjaEZlYXR1cmVcIlxuICAgICAgPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiZm9ybS1ncm91cFwiPlxuICAgICAgICAgIDxsYWJlbFxuICAgICAgICAgICAgZm9yPVwic2VhcmNoQWRkcmVzc1wiXG4gICAgICAgICAgICB0cmFuc2xhdGVcbiAgICAgICAgICA+XG4gICAgICAgICAgICBGaW5kIGxhdC9sb25nIGJ5IGFkZHJlc3NcbiAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJpbnB1dC1ncm91cCBpbnB1dC1ncm91cC1zZWFyY2hcIj5cbiAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbFwiXG4gICAgICAgICAgICAgIGlkPVwic2VhcmNoQWRkcmVzc1wiXG4gICAgICAgICAgICAgIG5hbWU9XCJhZGRyZXNzU2VhcmNoVGVybVwiXG4gICAgICAgICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cIidlLmcuIFNwZWRpdGlvbnN0cmHDn2UgMTMsIETDvHNzZWxkb3JmYExPQ0FMSVpFYCcgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgICAgICBbKG5nTW9kZWwpXT1cImFkZHJlc3NTZWFyY2hUZXJtXCJcbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImlucHV0LWdyb3VwLWJ0blwiPlxuICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJidG4gYnRuLWRvdFwiXG4gICAgICAgICAgICAgICAgW3RpdGxlXT1cIidTZWFyY2gnIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgICAgICAgICB0eXBlPVwic3VibWl0XCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxpXG4gICAgICAgICAgICAgICAgICBjbGFzcz1cImRsdC1jOHktaWNvbi1zZWFyY2hcIlxuICAgICAgICAgICAgICAgICAgYzh5LWljb249XCJzZWFyY2hcIlxuICAgICAgICAgICAgICAgID48L2k+XG4gICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZm9ybT5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwiY2FyZC1mb290ZXIgc2VwYXJhdG9yXCI+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1kZWZhdWx0XCJcbiAgICAgICAgW3RpdGxlXT1cIidFZGl0IGxvY2F0aW9uJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAqbmdJZj1cImNhbkVkaXQgJiYgIWlzRWRpdFwiXG4gICAgICAgIChjbGljayk9XCJlZGl0KClcIlxuICAgICAgICB0cmFuc2xhdGVcbiAgICAgID5cbiAgICAgICAgRWRpdCBsb2NhdGlvblxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1kZWZhdWx0XCJcbiAgICAgICAgW3RpdGxlXT1cIidDYW5jZWwnIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIChjbGljayk9XCJjYW5jZWwoKVwiXG4gICAgICAgICpuZ0lmPVwiaXNFZGl0XCJcbiAgICAgICAgdHJhbnNsYXRlXG4gICAgICA+XG4gICAgICAgIENhbmNlbFxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1wcmltYXJ5XCJcbiAgICAgICAgW3RpdGxlXT1cIidTYXZlJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgIHR5cGU9XCJzdWJtaXRcIlxuICAgICAgICAqbmdJZj1cImlzRWRpdFwiXG4gICAgICAgIHRyYW5zbGF0ZVxuICAgICAgICBbZGlzYWJsZWRdPVwiIXBvc2l0aW9uRm9ybS52YWxpZFwiXG4gICAgICA+XG4gICAgICAgIFNhdmVcbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZGl2PlxuICA8L2Zvcm0+XG48L2Rpdj5cbiJdfQ==