@smarterplan/ngx-smarterplan-locations 0.2.21 → 0.2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/lib/components/carousel/carousel.component.mjs +27 -27
- package/esm2020/lib/components/chevron/chevron.component.mjs +17 -17
- package/esm2020/lib/components/detail-location/detail-location.component.mjs +154 -154
- package/esm2020/lib/components/form-location/form-location.component.mjs +237 -237
- package/esm2020/lib/components/images/images.component.mjs +90 -90
- package/esm2020/lib/components/locations/locations.component.mjs +141 -141
- package/esm2020/lib/components/locations/map/map-popup/map-popup.component.mjs +65 -65
- package/esm2020/lib/components/locations/map/map.component.mjs +90 -90
- package/esm2020/lib/components/plan-legend/plan-legend.component.mjs +47 -0
- package/esm2020/lib/components/plans/calibration/calibration.component.mjs +468 -468
- package/esm2020/lib/components/plans/edit-plan/edit-plan.component.mjs +324 -324
- package/esm2020/lib/components/plans/plans.component.mjs +210 -210
- package/esm2020/lib/components/tab-navigation/tab-navigation.component.mjs +41 -41
- package/esm2020/lib/components/visits/visits.component.mjs +235 -235
- package/esm2020/lib/components/zones/add-audio-zone/add-audio-zone.component.mjs +230 -230
- package/esm2020/lib/components/zones/add-zone/add-zone.component.mjs +294 -294
- package/esm2020/lib/components/zones/add-zone/selection/selection.component.mjs +76 -76
- package/esm2020/lib/components/zones/add-zone/sweep-plan-selection/sweep-plan-selection.component.mjs +414 -413
- package/esm2020/lib/components/zones/zones.component.mjs +236 -232
- package/esm2020/lib/helper.service.mjs +134 -134
- package/esm2020/lib/ngx-smarterplan-location-routing.module.mjs +49 -49
- package/esm2020/lib/ngx-smarterplan-locations.module.mjs +125 -122
- package/esm2020/lib/ngx-smarterplan-locations.service.mjs +13 -13
- package/esm2020/lib/pipes/count-audio-sweeps.pipe.mjs +26 -26
- package/esm2020/lib/radio-button/radio-button.component.mjs +26 -26
- package/esm2020/public-api.mjs +8 -8
- package/esm2020/smarterplan-ngx-smarterplan-locations.mjs +4 -4
- package/fesm2015/smarterplan-ngx-smarterplan-locations.mjs +3676 -3624
- package/fesm2015/smarterplan-ngx-smarterplan-locations.mjs.map +1 -1
- package/fesm2020/smarterplan-ngx-smarterplan-locations.mjs +3520 -3472
- package/fesm2020/smarterplan-ngx-smarterplan-locations.mjs.map +1 -1
- package/lib/components/carousel/carousel.component.d.ts +12 -12
- package/lib/components/chevron/chevron.component.d.ts +9 -9
- package/lib/components/detail-location/detail-location.component.d.ts +51 -51
- package/lib/components/form-location/form-location.component.d.ts +45 -45
- package/lib/components/images/images.component.d.ts +28 -28
- package/lib/components/locations/locations.component.d.ts +50 -50
- package/lib/components/locations/map/map-popup/map-popup.component.d.ts +22 -22
- package/lib/components/locations/map/map.component.d.ts +22 -22
- package/lib/components/plan-legend/plan-legend.component.d.ts +14 -0
- package/lib/components/plans/calibration/calibration.component.d.ts +140 -140
- package/lib/components/plans/edit-plan/edit-plan.component.d.ts +55 -55
- package/lib/components/plans/plans.component.d.ts +59 -59
- package/lib/components/tab-navigation/tab-navigation.component.d.ts +13 -13
- package/lib/components/visits/visits.component.d.ts +51 -51
- package/lib/components/zones/add-audio-zone/add-audio-zone.component.d.ts +63 -63
- package/lib/components/zones/add-zone/add-zone.component.d.ts +67 -67
- package/lib/components/zones/add-zone/selection/selection.component.d.ts +44 -44
- package/lib/components/zones/add-zone/sweep-plan-selection/sweep-plan-selection.component.d.ts +96 -96
- package/lib/components/zones/zones.component.d.ts +67 -67
- package/lib/helper.service.d.ts +53 -53
- package/lib/ngx-smarterplan-location-routing.module.d.ts +7 -7
- package/lib/ngx-smarterplan-locations.module.d.ts +36 -35
- package/lib/ngx-smarterplan-locations.service.d.ts +6 -6
- package/lib/pipes/count-audio-sweeps.pipe.d.ts +10 -10
- package/lib/radio-button/radio-button.component.d.ts +12 -12
- package/package.json +1 -1
- package/public-api.d.ts +4 -4
- package/smarterplan-ngx-smarterplan-locations.d.ts +5 -5
|
@@ -1,211 +1,211 @@
|
|
|
1
|
-
import { Component } from '@angular/core';
|
|
2
|
-
import { Validators } from '@angular/forms';
|
|
3
|
-
import { getMetaForImage, getSignedFile, downloadFileAsObject, downloadBlob } from '@smarterplan/ngx-smarterplan-core';
|
|
4
|
-
import panzoom from 'panzoom';
|
|
5
|
-
import * as i0 from "@angular/core";
|
|
6
|
-
import * as i1 from "@angular/router";
|
|
7
|
-
import * as i2 from "@smarterplan/ngx-smarterplan-core";
|
|
8
|
-
import * as i3 from "@angular/forms";
|
|
9
|
-
import * as i4 from "@ngx-translate/core";
|
|
10
|
-
import * as i5 from "../tab-navigation/tab-navigation.component";
|
|
11
|
-
import * as i6 from "@angular/common";
|
|
12
|
-
import * as i7 from "@ng-bootstrap/ng-bootstrap";
|
|
13
|
-
export class PlansComponent {
|
|
14
|
-
constructor(route, router, spaceService, planService, fb, translate) {
|
|
15
|
-
this.route = route;
|
|
16
|
-
this.router = router;
|
|
17
|
-
this.spaceService = spaceService;
|
|
18
|
-
this.planService = planService;
|
|
19
|
-
this.fb = fb;
|
|
20
|
-
this.translate = translate;
|
|
21
|
-
// chosenPlan: Plan;
|
|
22
|
-
this.indexDetails = -1;
|
|
23
|
-
this.loading = false;
|
|
24
|
-
this.uploadingPlan = false;
|
|
25
|
-
this.isCurrentPlanForZone = false;
|
|
26
|
-
this.chosenPlanIsPdf = false;
|
|
27
|
-
this.menuItems = [];
|
|
28
|
-
}
|
|
29
|
-
ngOnInit() {
|
|
30
|
-
this.spaceID = this.route.snapshot.paramMap.get("id");
|
|
31
|
-
this.getPlans();
|
|
32
|
-
if (this.planService.getPlanFileCache() &&
|
|
33
|
-
this.planService.getChosenPlan()) {
|
|
34
|
-
this.addPlanFromCache(this.planService.getPlanFileCache(), this.planService.getChosenPlan());
|
|
35
|
-
this.planService.setPlanFileCache(null);
|
|
36
|
-
}
|
|
37
|
-
this.planService.setChosenPlan(null);
|
|
38
|
-
}
|
|
39
|
-
setupMenuItems() {
|
|
40
|
-
this.menuItems = [
|
|
41
|
-
{ label: "Locations", url: "/localisation" },
|
|
42
|
-
{
|
|
43
|
-
label: this.currentSpace.name,
|
|
44
|
-
url: `/localisation/${this.currentSpace.id}`,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
label: "Plans",
|
|
48
|
-
url: `/localisation/${this.currentSpace.id}/plans`,
|
|
49
|
-
},
|
|
50
|
-
];
|
|
51
|
-
}
|
|
52
|
-
onGoBack() { }
|
|
53
|
-
async onPlanClick(plan) {
|
|
54
|
-
if (plan)
|
|
55
|
-
this.planService.setChosenPlan(plan);
|
|
56
|
-
this.chosenPlanIsPdf = plan.annexe.includes("pdf");
|
|
57
|
-
if (!this.chosenPlanIsPdf) {
|
|
58
|
-
setTimeout(() => {
|
|
59
|
-
this.configureCanvas().then(() => {
|
|
60
|
-
this.panzoom = panzoom(this.canvas, {
|
|
61
|
-
bounds: true,
|
|
62
|
-
boundsPadding: 0,
|
|
63
|
-
maxZoom: 3.5,
|
|
64
|
-
minZoom: 0.1,
|
|
65
|
-
initialZoom: 0.5,
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
}, 500);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
async configureCanvas() {
|
|
72
|
-
this.uploadingPlan = true;
|
|
73
|
-
const canvas = document.querySelector("#canvas");
|
|
74
|
-
const image = await getMetaForImage(this.getChoosenPlan().filepath);
|
|
75
|
-
canvas.width = image.width;
|
|
76
|
-
canvas.height = image.height;
|
|
77
|
-
this.canvas = canvas;
|
|
78
|
-
this.drawImage(this.getChoosenPlan().filepath);
|
|
79
|
-
this.uploadingPlan = false;
|
|
80
|
-
}
|
|
81
|
-
drawImage(url, x = 0, y = 0) {
|
|
82
|
-
const image = new Image();
|
|
83
|
-
const context = this.canvas.getContext("2d");
|
|
84
|
-
const imageWidth = this.canvas.width;
|
|
85
|
-
const imageHeight = this.canvas.height;
|
|
86
|
-
image.addEventListener("load", () => {
|
|
87
|
-
context.drawImage(image, 0, 0, image.width, image.height, x, y, imageWidth, imageHeight);
|
|
88
|
-
});
|
|
89
|
-
image.src = url;
|
|
90
|
-
// image.crossOrigin = "*"; //need to download as png,
|
|
91
|
-
// BUT: https://stackoverflow.com/questions/49503171/the-image-tag-with-crossorigin-anonymous-cant-load-success-from-s3
|
|
92
|
-
}
|
|
93
|
-
async getPlans() {
|
|
94
|
-
this.loading = true;
|
|
95
|
-
this.currentSpace = await this.spaceService.getSpace(this.spaceID);
|
|
96
|
-
this.setupMenuItems();
|
|
97
|
-
this.zones = this.currentSpace.zones.items;
|
|
98
|
-
this.plans = await this.planService.getSingedPlansForSpace(this.spaceID);
|
|
99
|
-
this.loading = false;
|
|
100
|
-
if (this.plans.length === 1) {
|
|
101
|
-
this.onPlanClick(this.plans[0]);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
getChoosenPlan() {
|
|
105
|
-
return this.planService.getChosenPlan();
|
|
106
|
-
}
|
|
107
|
-
removeImage() {
|
|
108
|
-
const context = this.canvas.getContext("2d");
|
|
109
|
-
context.clearRect(0, 0, 4096, 4096);
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
*
|
|
113
|
-
*/
|
|
114
|
-
onCalibrateClick() {
|
|
115
|
-
if (this.getChoosenPlan()) {
|
|
116
|
-
this.router.navigate(["/dashboard/localisation", this.spaceID, "plan-calibration"], { queryParams: { spaceID: this.spaceID } });
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
*
|
|
121
|
-
*/
|
|
122
|
-
onEditClick() {
|
|
123
|
-
if (this.getChoosenPlan()) {
|
|
124
|
-
this.router.navigate(["/dashboard/localisation", this.spaceID, "plan-edit"], { queryParams: { spaceID: this.spaceID } });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
onUploadClick() {
|
|
128
|
-
const inputFile = document.querySelector("#upload-file");
|
|
129
|
-
inputFile.click();
|
|
130
|
-
}
|
|
131
|
-
addPlan(target) {
|
|
132
|
-
this.fileToUpload = target.files[0];
|
|
133
|
-
this.planForm = this.fb.group({
|
|
134
|
-
name: [this.fileToUpload.name, [Validators.required]],
|
|
135
|
-
zoneID: [null, [Validators.required]],
|
|
136
|
-
spaceID: this.spaceID,
|
|
137
|
-
isModified: false,
|
|
138
|
-
calibration: "",
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
addPlanFromCache(file, plan) {
|
|
142
|
-
this.fileToUpload = file;
|
|
143
|
-
this.planForm = this.fb.group({
|
|
144
|
-
name: [this.fileToUpload.name, [Validators.required]],
|
|
145
|
-
zoneID: [plan ? plan.zoneID : null, [Validators.required]],
|
|
146
|
-
spaceID: this.spaceID,
|
|
147
|
-
isModified: !!plan,
|
|
148
|
-
calibration: plan && plan.isModified && plan.calibration
|
|
149
|
-
? plan.calibration
|
|
150
|
-
: "",
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
async onSavePlan() {
|
|
154
|
-
const formValue = this.planForm.value;
|
|
155
|
-
formValue.isCurrentForZone = this.isCurrentPlanForZone;
|
|
156
|
-
const createdPlan = await this.planService.createPlanWithAnnexe(formValue, this.fileToUpload);
|
|
157
|
-
const filepath = await getSignedFile(createdPlan.annexe);
|
|
158
|
-
if (filepath) {
|
|
159
|
-
createdPlan.filepath = filepath;
|
|
160
|
-
}
|
|
161
|
-
if (formValue.isCurrentForZone) {
|
|
162
|
-
// set all other plans to not current
|
|
163
|
-
this.planService.setAllPlansForZoneNotCurrent(createdPlan.zoneID, createdPlan.id);
|
|
164
|
-
}
|
|
165
|
-
this.plans.push(createdPlan);
|
|
166
|
-
this.planForm = null;
|
|
167
|
-
this.fileToUpload = null;
|
|
168
|
-
}
|
|
169
|
-
onCancelUpload() {
|
|
170
|
-
this.fileToUpload = null;
|
|
171
|
-
this.planForm = null;
|
|
172
|
-
}
|
|
173
|
-
async onCurrentPlanClick() {
|
|
174
|
-
await this.planService.updatePlan({
|
|
175
|
-
id: this.getChoosenPlan().id,
|
|
176
|
-
isCurrentForZone: this.getChoosenPlan().isCurrentForZone,
|
|
177
|
-
});
|
|
178
|
-
if (this.getChoosenPlan().isCurrentForZone) {
|
|
179
|
-
this.planService.setAllPlansForZoneNotCurrent(this.getChoosenPlan().zoneID, this.getChoosenPlan().id);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
async onDownloadClick() {
|
|
183
|
-
const signedFile = await downloadFileAsObject(this.getChoosenPlan().annexe);
|
|
184
|
-
if (signedFile) {
|
|
185
|
-
// @ts-ignore
|
|
186
|
-
downloadBlob(signedFile.Body, this.getChoosenPlan().name);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
async onDownloadAsPng() {
|
|
190
|
-
const canvas = document.querySelector("#canvas");
|
|
191
|
-
canvas.toBlob(function (blob) {
|
|
192
|
-
downloadBlob(blob, "plan-as-png.png");
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
async onRemoveClick() {
|
|
196
|
-
const message = this.translate.instant("confirm.deletePlan");
|
|
197
|
-
if (window.confirm(message)) {
|
|
198
|
-
await this.planService.deletePlan(this.getChoosenPlan());
|
|
199
|
-
this.removeImage();
|
|
200
|
-
this.planService.setChosenPlan(null);
|
|
201
|
-
this.getPlans();
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
PlansComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: PlansComponent, deps: [{ token: i1.ActivatedRoute }, { token: i1.Router }, { token: i2.SpaceService }, { token: i2.PlanService }, { token: i3.FormBuilder }, { token: i4.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
|
|
206
|
-
PlansComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: PlansComponent, selector: "lib-plans", ngImport: i0, template: "<div class=\"container-fluid\" *ngIf=\"currentSpace\" class=\"dashboard-tab\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\n </div>\n <div class=\"d-flex justify-content-center\" *ngIf=\"loading\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n <div class=\"mt-3\" *ngIf=\"!plans && !loading\">\n <h4 style=\"font-weight: bold;\">{{'No imported plans' | translate }}</h4>\n <p>{{'For import from 3D visit: go Virtual Visits => Import Images' | translate}}</p>\n </div>\n <div class=\"row ms-1\">\n <div ngbDropdown class=\"d-inline-block me-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>{{getChoosenPlan() ?\n getChoosenPlan().name: ('Choose Plan' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let plan of plans\" (click)=\"onPlanClick(plan)\">{{plan.name }}\n </button>\n </div>\n </div>\n <div class=\"d-inline-block\" ngbDropdown #myDrop=\"ngbDropdown\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownManual\" ngbDropdownToggle>{{'Choose action' |\n translate}}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownManual\">\n <button (click)=\"onCalibrateClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Calibrate' |\n translate}}</button>\n <button (click)=\"onEditClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Edit plan' |\n translate}}\n </button>\n <button (click)=\"onUploadClick()\" ngbDropdownItem>{{'Upload new plan' | translate}}</button>\n <button (click)=\"onDownloadClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Download plan' |\n translate}}</button>\n <button (click)=\"onRemoveClick()\" ngbDropdownItem\n [disabled]=\"!getChoosenPlan() || getChoosenPlan().isImportedMatterport\">{{'Delete plan' |\n translate}}</button>\n <!-- <button (click)=\"onDownloadAsPng()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">\n {{'Download plan as PNG' | translate}}</button> -->\n </div>\n </div>\n </div>\n <div class=\"row mt-3 ms-0\">\n <h4 *ngIf=\"fileToUpload\">{{'New plan' | translate }}</h4>\n <input class=\"hidden\" type=\"file\" id=\"upload-file\" name=\"upload-file\" accept=\"image/png, image/jpeg, .pdf, .svg\"\n ngf-max-size=\"6MB\" (change)=\"addPlan($event.target)\">\n </div>\n <div class=\"col-md-6 mt-3\" *ngIf=\"planForm\">\n <form (ngSubmit)=\"onSavePlan()\" [formGroup]=\"planForm\">\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Name' | translate}} </label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"name\">\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Zone' | translate}} </label>\n <div class=\"col-sm-9\">\n <select class=\"form-control\" formControlName=\"zoneID\">\n <option *ngFor=\"let zone of zones\" [ngValue]=\"zone.id\">\n {{ zone.name }}\n </option>\n </select>\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Set as current plan for zone' | translate}}</label>\n <div class=\"col-sm-3\">\n <input type=\"checkbox\" [(ngModel)]=\"isCurrentPlanForZone\" [ngModelOptions]=\"{standalone: true}\">\n </div>\n </div>\n <button [disabled]=\"planForm.invalid\" type='submit' class=\"btn btn-label-file rounded-pill\">{{'Save' |\n translate}}</button>\n <button class=\"btn btn-label-file rounded-pill ms-3\" type=\"button\" (click)=\"onCancelUpload()\">{{'Cancel' |\n translate}}</button>\n </form>\n </div>\n <ul class=\"col-md-6 list-group list-group-flush\" *ngIf=\"getChoosenPlan()\">\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate }} : {{getChoosenPlan().name}} </li>\n <li class=\"list-group-item bg-transparent\"> {{'Plan is ' | translate }} :\n {{getChoosenPlan().calibration ? ('Calibrated' | translate) : ('Not calibrated' | translate)}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Attributed to zone ' | translate }} :\n {{getChoosenPlan().zone ? getChoosenPlan().zone.name : 'None'}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Is current plan for zone ' | translate }} :\n <input type=\"checkbox\" [(ngModel)]=\"getChoosenPlan().isCurrentForZone\" (change)=\"onCurrentPlanClick()\">\n </li>\n\n </ul>\n\n <div class=\"row mt-4\">\n <div class=\"col-md-10\">\n <embed *ngIf=\"getChoosenPlan() && chosenPlanIsPdf\" [src]=\"getChoosenPlan().filepath | safeUrl\"\n type=\"application/pdf\" frameBorder=\"0\" scrolling=\"auto\" height=\"650px;\" width=\"100%\" />\n <div *ngIf=\"!chosenPlanIsPdf\" class=\"row\" style=\"height: 500px; overflow: hidden;\" id=\"canvasDiv\">\n <canvas id=\"canvas\" width=\"4096px\" height=\"4096px\">\n <!-- <img *ngIf=\"chosenPlan\" id=\"plan-image\" [src]=\"chosenPlan.filepath\" style=\"width: 100%;\"> -->\n </canvas>\n\n </div>\n </div>\n </div>\n</div>\n", styles: [".button-visit{display:none;height:30px;width:30px;position:absolute;background:url(https://api.iconify.design/mdi:map-marker-check.svg?color=red&height=30) no-repeat scroll 0 0 transparent;border:none}#button-visit-left{left:350px;top:550px}#button-visit-right{left:500px;top:600px}.hidden{visibility:hidden;width:1px;height:1px}\n"], components: [{ type: i5.TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }], directives: [{ type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i7.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { type: i7.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { type: i7.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i7.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["disabled"] }, { type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], pipes: { "translate": i4.TranslatePipe, "safeUrl": i2.SafeUrlPipe } });
|
|
207
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: PlansComponent, decorators: [{
|
|
208
|
-
type: Component,
|
|
209
|
-
args: [{ selector: 'lib-plans', template: "<div class=\"container-fluid\" *ngIf=\"currentSpace\" class=\"dashboard-tab\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\n </div>\n <div class=\"d-flex justify-content-center\" *ngIf=\"loading\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n <div class=\"mt-3\" *ngIf=\"!plans && !loading\">\n <h4 style=\"font-weight: bold;\">{{'No imported plans' | translate }}</h4>\n <p>{{'For import from 3D visit: go Virtual Visits => Import Images' | translate}}</p>\n </div>\n <div class=\"row ms-1\">\n <div ngbDropdown class=\"d-inline-block me-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>{{getChoosenPlan() ?\n getChoosenPlan().name: ('Choose Plan' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let plan of plans\" (click)=\"onPlanClick(plan)\">{{plan.name }}\n </button>\n </div>\n </div>\n <div class=\"d-inline-block\" ngbDropdown #myDrop=\"ngbDropdown\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownManual\" ngbDropdownToggle>{{'Choose action' |\n translate}}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownManual\">\n <button (click)=\"onCalibrateClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Calibrate' |\n translate}}</button>\n <button (click)=\"onEditClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Edit plan' |\n translate}}\n </button>\n <button (click)=\"onUploadClick()\" ngbDropdownItem>{{'Upload new plan' | translate}}</button>\n <button (click)=\"onDownloadClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Download plan' |\n translate}}</button>\n <button (click)=\"onRemoveClick()\" ngbDropdownItem\n [disabled]=\"!getChoosenPlan() || getChoosenPlan().isImportedMatterport\">{{'Delete plan' |\n translate}}</button>\n <!-- <button (click)=\"onDownloadAsPng()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">\n {{'Download plan as PNG' | translate}}</button> -->\n </div>\n </div>\n </div>\n <div class=\"row mt-3 ms-0\">\n <h4 *ngIf=\"fileToUpload\">{{'New plan' | translate }}</h4>\n <input class=\"hidden\" type=\"file\" id=\"upload-file\" name=\"upload-file\" accept=\"image/png, image/jpeg, .pdf, .svg\"\n ngf-max-size=\"6MB\" (change)=\"addPlan($event.target)\">\n </div>\n <div class=\"col-md-6 mt-3\" *ngIf=\"planForm\">\n <form (ngSubmit)=\"onSavePlan()\" [formGroup]=\"planForm\">\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Name' | translate}} </label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"name\">\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Zone' | translate}} </label>\n <div class=\"col-sm-9\">\n <select class=\"form-control\" formControlName=\"zoneID\">\n <option *ngFor=\"let zone of zones\" [ngValue]=\"zone.id\">\n {{ zone.name }}\n </option>\n </select>\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Set as current plan for zone' | translate}}</label>\n <div class=\"col-sm-3\">\n <input type=\"checkbox\" [(ngModel)]=\"isCurrentPlanForZone\" [ngModelOptions]=\"{standalone: true}\">\n </div>\n </div>\n <button [disabled]=\"planForm.invalid\" type='submit' class=\"btn btn-label-file rounded-pill\">{{'Save' |\n translate}}</button>\n <button class=\"btn btn-label-file rounded-pill ms-3\" type=\"button\" (click)=\"onCancelUpload()\">{{'Cancel' |\n translate}}</button>\n </form>\n </div>\n <ul class=\"col-md-6 list-group list-group-flush\" *ngIf=\"getChoosenPlan()\">\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate }} : {{getChoosenPlan().name}} </li>\n <li class=\"list-group-item bg-transparent\"> {{'Plan is ' | translate }} :\n {{getChoosenPlan().calibration ? ('Calibrated' | translate) : ('Not calibrated' | translate)}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Attributed to zone ' | translate }} :\n {{getChoosenPlan().zone ? getChoosenPlan().zone.name : 'None'}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Is current plan for zone ' | translate }} :\n <input type=\"checkbox\" [(ngModel)]=\"getChoosenPlan().isCurrentForZone\" (change)=\"onCurrentPlanClick()\">\n </li>\n\n </ul>\n\n <div class=\"row mt-4\">\n <div class=\"col-md-10\">\n <embed *ngIf=\"getChoosenPlan() && chosenPlanIsPdf\" [src]=\"getChoosenPlan().filepath | safeUrl\"\n type=\"application/pdf\" frameBorder=\"0\" scrolling=\"auto\" height=\"650px;\" width=\"100%\" />\n <div *ngIf=\"!chosenPlanIsPdf\" class=\"row\" style=\"height: 500px; overflow: hidden;\" id=\"canvasDiv\">\n <canvas id=\"canvas\" width=\"4096px\" height=\"4096px\">\n <!-- <img *ngIf=\"chosenPlan\" id=\"plan-image\" [src]=\"chosenPlan.filepath\" style=\"width: 100%;\"> -->\n </canvas>\n\n </div>\n </div>\n </div>\n</div>\n", styles: [".button-visit{display:none;height:30px;width:30px;position:absolute;background:url(https://api.iconify.design/mdi:map-marker-check.svg?color=red&height=30) no-repeat scroll 0 0 transparent;border:none}#button-visit-left{left:350px;top:550px}#button-visit-right{left:500px;top:600px}.hidden{visibility:hidden;width:1px;height:1px}\n"] }]
|
|
210
|
-
}], ctorParameters: function () { return [{ type: i1.ActivatedRoute }, { type: i1.Router }, { type: i2.SpaceService }, { type: i2.PlanService }, { type: i3.FormBuilder }, { type: i4.TranslateService }]; } });
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
import { Validators } from '@angular/forms';
|
|
3
|
+
import { getMetaForImage, getSignedFile, downloadFileAsObject, downloadBlob } from '@smarterplan/ngx-smarterplan-core';
|
|
4
|
+
import panzoom from 'panzoom';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
import * as i1 from "@angular/router";
|
|
7
|
+
import * as i2 from "@smarterplan/ngx-smarterplan-core";
|
|
8
|
+
import * as i3 from "@angular/forms";
|
|
9
|
+
import * as i4 from "@ngx-translate/core";
|
|
10
|
+
import * as i5 from "../tab-navigation/tab-navigation.component";
|
|
11
|
+
import * as i6 from "@angular/common";
|
|
12
|
+
import * as i7 from "@ng-bootstrap/ng-bootstrap";
|
|
13
|
+
export class PlansComponent {
|
|
14
|
+
constructor(route, router, spaceService, planService, fb, translate) {
|
|
15
|
+
this.route = route;
|
|
16
|
+
this.router = router;
|
|
17
|
+
this.spaceService = spaceService;
|
|
18
|
+
this.planService = planService;
|
|
19
|
+
this.fb = fb;
|
|
20
|
+
this.translate = translate;
|
|
21
|
+
// chosenPlan: Plan;
|
|
22
|
+
this.indexDetails = -1;
|
|
23
|
+
this.loading = false;
|
|
24
|
+
this.uploadingPlan = false;
|
|
25
|
+
this.isCurrentPlanForZone = false;
|
|
26
|
+
this.chosenPlanIsPdf = false;
|
|
27
|
+
this.menuItems = [];
|
|
28
|
+
}
|
|
29
|
+
ngOnInit() {
|
|
30
|
+
this.spaceID = this.route.snapshot.paramMap.get("id");
|
|
31
|
+
this.getPlans();
|
|
32
|
+
if (this.planService.getPlanFileCache() &&
|
|
33
|
+
this.planService.getChosenPlan()) {
|
|
34
|
+
this.addPlanFromCache(this.planService.getPlanFileCache(), this.planService.getChosenPlan());
|
|
35
|
+
this.planService.setPlanFileCache(null);
|
|
36
|
+
}
|
|
37
|
+
this.planService.setChosenPlan(null);
|
|
38
|
+
}
|
|
39
|
+
setupMenuItems() {
|
|
40
|
+
this.menuItems = [
|
|
41
|
+
{ label: "Locations", url: "/localisation" },
|
|
42
|
+
{
|
|
43
|
+
label: this.currentSpace.name,
|
|
44
|
+
url: `/localisation/${this.currentSpace.id}`,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: "Plans",
|
|
48
|
+
url: `/localisation/${this.currentSpace.id}/plans`,
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
onGoBack() { }
|
|
53
|
+
async onPlanClick(plan) {
|
|
54
|
+
if (plan)
|
|
55
|
+
this.planService.setChosenPlan(plan);
|
|
56
|
+
this.chosenPlanIsPdf = plan.annexe.includes("pdf");
|
|
57
|
+
if (!this.chosenPlanIsPdf) {
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
this.configureCanvas().then(() => {
|
|
60
|
+
this.panzoom = panzoom(this.canvas, {
|
|
61
|
+
bounds: true,
|
|
62
|
+
boundsPadding: 0,
|
|
63
|
+
maxZoom: 3.5,
|
|
64
|
+
minZoom: 0.1,
|
|
65
|
+
initialZoom: 0.5,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}, 500);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async configureCanvas() {
|
|
72
|
+
this.uploadingPlan = true;
|
|
73
|
+
const canvas = document.querySelector("#canvas");
|
|
74
|
+
const image = await getMetaForImage(this.getChoosenPlan().filepath);
|
|
75
|
+
canvas.width = image.width;
|
|
76
|
+
canvas.height = image.height;
|
|
77
|
+
this.canvas = canvas;
|
|
78
|
+
this.drawImage(this.getChoosenPlan().filepath);
|
|
79
|
+
this.uploadingPlan = false;
|
|
80
|
+
}
|
|
81
|
+
drawImage(url, x = 0, y = 0) {
|
|
82
|
+
const image = new Image();
|
|
83
|
+
const context = this.canvas.getContext("2d");
|
|
84
|
+
const imageWidth = this.canvas.width;
|
|
85
|
+
const imageHeight = this.canvas.height;
|
|
86
|
+
image.addEventListener("load", () => {
|
|
87
|
+
context.drawImage(image, 0, 0, image.width, image.height, x, y, imageWidth, imageHeight);
|
|
88
|
+
});
|
|
89
|
+
image.src = url;
|
|
90
|
+
// image.crossOrigin = "*"; //need to download as png,
|
|
91
|
+
// BUT: https://stackoverflow.com/questions/49503171/the-image-tag-with-crossorigin-anonymous-cant-load-success-from-s3
|
|
92
|
+
}
|
|
93
|
+
async getPlans() {
|
|
94
|
+
this.loading = true;
|
|
95
|
+
this.currentSpace = await this.spaceService.getSpace(this.spaceID);
|
|
96
|
+
this.setupMenuItems();
|
|
97
|
+
this.zones = this.currentSpace.zones.items;
|
|
98
|
+
this.plans = await this.planService.getSingedPlansForSpace(this.spaceID);
|
|
99
|
+
this.loading = false;
|
|
100
|
+
if (this.plans.length === 1) {
|
|
101
|
+
this.onPlanClick(this.plans[0]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
getChoosenPlan() {
|
|
105
|
+
return this.planService.getChosenPlan();
|
|
106
|
+
}
|
|
107
|
+
removeImage() {
|
|
108
|
+
const context = this.canvas.getContext("2d");
|
|
109
|
+
context.clearRect(0, 0, 4096, 4096);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
*
|
|
113
|
+
*/
|
|
114
|
+
onCalibrateClick() {
|
|
115
|
+
if (this.getChoosenPlan()) {
|
|
116
|
+
this.router.navigate(["/dashboard/localisation", this.spaceID, "plan-calibration"], { queryParams: { spaceID: this.spaceID } });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
*/
|
|
122
|
+
onEditClick() {
|
|
123
|
+
if (this.getChoosenPlan()) {
|
|
124
|
+
this.router.navigate(["/dashboard/localisation", this.spaceID, "plan-edit"], { queryParams: { spaceID: this.spaceID } });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
onUploadClick() {
|
|
128
|
+
const inputFile = document.querySelector("#upload-file");
|
|
129
|
+
inputFile.click();
|
|
130
|
+
}
|
|
131
|
+
addPlan(target) {
|
|
132
|
+
this.fileToUpload = target.files[0];
|
|
133
|
+
this.planForm = this.fb.group({
|
|
134
|
+
name: [this.fileToUpload.name, [Validators.required]],
|
|
135
|
+
zoneID: [null, [Validators.required]],
|
|
136
|
+
spaceID: this.spaceID,
|
|
137
|
+
isModified: false,
|
|
138
|
+
calibration: "",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
addPlanFromCache(file, plan) {
|
|
142
|
+
this.fileToUpload = file;
|
|
143
|
+
this.planForm = this.fb.group({
|
|
144
|
+
name: [this.fileToUpload.name, [Validators.required]],
|
|
145
|
+
zoneID: [plan ? plan.zoneID : null, [Validators.required]],
|
|
146
|
+
spaceID: this.spaceID,
|
|
147
|
+
isModified: !!plan,
|
|
148
|
+
calibration: plan && plan.isModified && plan.calibration
|
|
149
|
+
? plan.calibration
|
|
150
|
+
: "",
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async onSavePlan() {
|
|
154
|
+
const formValue = this.planForm.value;
|
|
155
|
+
formValue.isCurrentForZone = this.isCurrentPlanForZone;
|
|
156
|
+
const createdPlan = await this.planService.createPlanWithAnnexe(formValue, this.fileToUpload);
|
|
157
|
+
const filepath = await getSignedFile(createdPlan.annexe);
|
|
158
|
+
if (filepath) {
|
|
159
|
+
createdPlan.filepath = filepath;
|
|
160
|
+
}
|
|
161
|
+
if (formValue.isCurrentForZone) {
|
|
162
|
+
// set all other plans to not current
|
|
163
|
+
this.planService.setAllPlansForZoneNotCurrent(createdPlan.zoneID, createdPlan.id);
|
|
164
|
+
}
|
|
165
|
+
this.plans.push(createdPlan);
|
|
166
|
+
this.planForm = null;
|
|
167
|
+
this.fileToUpload = null;
|
|
168
|
+
}
|
|
169
|
+
onCancelUpload() {
|
|
170
|
+
this.fileToUpload = null;
|
|
171
|
+
this.planForm = null;
|
|
172
|
+
}
|
|
173
|
+
async onCurrentPlanClick() {
|
|
174
|
+
await this.planService.updatePlan({
|
|
175
|
+
id: this.getChoosenPlan().id,
|
|
176
|
+
isCurrentForZone: this.getChoosenPlan().isCurrentForZone,
|
|
177
|
+
});
|
|
178
|
+
if (this.getChoosenPlan().isCurrentForZone) {
|
|
179
|
+
this.planService.setAllPlansForZoneNotCurrent(this.getChoosenPlan().zoneID, this.getChoosenPlan().id);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async onDownloadClick() {
|
|
183
|
+
const signedFile = await downloadFileAsObject(this.getChoosenPlan().annexe);
|
|
184
|
+
if (signedFile) {
|
|
185
|
+
// @ts-ignore
|
|
186
|
+
downloadBlob(signedFile.Body, this.getChoosenPlan().name);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async onDownloadAsPng() {
|
|
190
|
+
const canvas = document.querySelector("#canvas");
|
|
191
|
+
canvas.toBlob(function (blob) {
|
|
192
|
+
downloadBlob(blob, "plan-as-png.png");
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async onRemoveClick() {
|
|
196
|
+
const message = this.translate.instant("confirm.deletePlan");
|
|
197
|
+
if (window.confirm(message)) {
|
|
198
|
+
await this.planService.deletePlan(this.getChoosenPlan());
|
|
199
|
+
this.removeImage();
|
|
200
|
+
this.planService.setChosenPlan(null);
|
|
201
|
+
this.getPlans();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
PlansComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: PlansComponent, deps: [{ token: i1.ActivatedRoute }, { token: i1.Router }, { token: i2.SpaceService }, { token: i2.PlanService }, { token: i3.FormBuilder }, { token: i4.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
|
|
206
|
+
PlansComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: PlansComponent, selector: "lib-plans", ngImport: i0, template: "<div class=\"container-fluid\" *ngIf=\"currentSpace\" class=\"dashboard-tab\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\n </div>\n <div class=\"d-flex justify-content-center\" *ngIf=\"loading\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n <div class=\"mt-3\" *ngIf=\"!plans && !loading\">\n <h4 style=\"font-weight: bold;\">{{'No imported plans' | translate }}</h4>\n <p>{{'For import from 3D visit: go Virtual Visits => Import Images' | translate}}</p>\n </div>\n <div class=\"row ms-1\">\n <div ngbDropdown class=\"d-inline-block me-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>{{getChoosenPlan() ?\n getChoosenPlan().name: ('Choose Plan' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let plan of plans\" (click)=\"onPlanClick(plan)\">{{plan.name }}\n </button>\n </div>\n </div>\n <div class=\"d-inline-block\" ngbDropdown #myDrop=\"ngbDropdown\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownManual\" ngbDropdownToggle>{{'Choose action' |\n translate}}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownManual\">\n <button (click)=\"onCalibrateClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Calibrate' |\n translate}}</button>\n <button (click)=\"onEditClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Edit plan' |\n translate}}\n </button>\n <button (click)=\"onUploadClick()\" ngbDropdownItem>{{'Upload new plan' | translate}}</button>\n <button (click)=\"onDownloadClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Download plan' |\n translate}}</button>\n <button (click)=\"onRemoveClick()\" ngbDropdownItem\n [disabled]=\"!getChoosenPlan() || getChoosenPlan().isImportedMatterport\">{{'Delete plan' |\n translate}}</button>\n <!-- <button (click)=\"onDownloadAsPng()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">\n {{'Download plan as PNG' | translate}}</button> -->\n </div>\n </div>\n </div>\n <div class=\"row mt-3 ms-0\">\n <h4 *ngIf=\"fileToUpload\">{{'New plan' | translate }}</h4>\n <input class=\"hidden\" type=\"file\" id=\"upload-file\" name=\"upload-file\" accept=\"image/png, image/jpeg, .pdf, .svg\"\n ngf-max-size=\"6MB\" (change)=\"addPlan($event.target)\">\n </div>\n <div class=\"col-md-6 mt-3\" *ngIf=\"planForm\">\n <form (ngSubmit)=\"onSavePlan()\" [formGroup]=\"planForm\">\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Name' | translate}} </label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"name\">\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Zone' | translate}} </label>\n <div class=\"col-sm-9\">\n <select class=\"form-control\" formControlName=\"zoneID\">\n <option *ngFor=\"let zone of zones\" [ngValue]=\"zone.id\">\n {{ zone.name }}\n </option>\n </select>\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Set as current plan for zone' | translate}}</label>\n <div class=\"col-sm-3\">\n <input type=\"checkbox\" [(ngModel)]=\"isCurrentPlanForZone\" [ngModelOptions]=\"{standalone: true}\">\n </div>\n </div>\n <button [disabled]=\"planForm.invalid\" type='submit' class=\"btn btn-label-file rounded-pill\">{{'Save' |\n translate}}</button>\n <button class=\"btn btn-label-file rounded-pill ms-3\" type=\"button\" (click)=\"onCancelUpload()\">{{'Cancel' |\n translate}}</button>\n </form>\n </div>\n <ul class=\"col-md-6 list-group list-group-flush\" *ngIf=\"getChoosenPlan()\">\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate }} : {{getChoosenPlan().name}} </li>\n <li class=\"list-group-item bg-transparent\"> {{'Plan is ' | translate }} :\n {{getChoosenPlan().calibration ? ('Calibrated' | translate) : ('Not calibrated' | translate)}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Attributed to zone ' | translate }} :\n {{getChoosenPlan().zone ? getChoosenPlan().zone.name : 'None'}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Is current plan for zone ' | translate }} :\n <input type=\"checkbox\" [(ngModel)]=\"getChoosenPlan().isCurrentForZone\" (change)=\"onCurrentPlanClick()\">\n </li>\n\n </ul>\n\n <div class=\"row mt-4\">\n <div class=\"col-md-10\">\n <embed *ngIf=\"getChoosenPlan() && chosenPlanIsPdf\" [src]=\"getChoosenPlan().filepath | safeUrl\"\n type=\"application/pdf\" frameBorder=\"0\" scrolling=\"auto\" height=\"650px;\" width=\"100%\" />\n <div *ngIf=\"!chosenPlanIsPdf\" class=\"row\" style=\"height: 500px; overflow: hidden;\" id=\"canvasDiv\">\n <canvas id=\"canvas\" width=\"4096px\" height=\"4096px\">\n <!-- <img *ngIf=\"chosenPlan\" id=\"plan-image\" [src]=\"chosenPlan.filepath\" style=\"width: 100%;\"> -->\n </canvas>\n\n </div>\n </div>\n </div>\n</div>\n", styles: [".button-visit{display:none;height:30px;width:30px;position:absolute;background:url(https://api.iconify.design/mdi:map-marker-check.svg?color=red&height=30) no-repeat scroll 0 0 transparent;border:none}#button-visit-left{left:350px;top:550px}#button-visit-right{left:500px;top:600px}.hidden{visibility:hidden;width:1px;height:1px}\n"], components: [{ type: i5.TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }], directives: [{ type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i7.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { type: i7.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { type: i7.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i7.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["disabled"] }, { type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], pipes: { "translate": i4.TranslatePipe, "safeUrl": i2.SafeUrlPipe } });
|
|
207
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: PlansComponent, decorators: [{
|
|
208
|
+
type: Component,
|
|
209
|
+
args: [{ selector: 'lib-plans', template: "<div class=\"container-fluid\" *ngIf=\"currentSpace\" class=\"dashboard-tab\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\n </div>\n <div class=\"d-flex justify-content-center\" *ngIf=\"loading\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n <div class=\"mt-3\" *ngIf=\"!plans && !loading\">\n <h4 style=\"font-weight: bold;\">{{'No imported plans' | translate }}</h4>\n <p>{{'For import from 3D visit: go Virtual Visits => Import Images' | translate}}</p>\n </div>\n <div class=\"row ms-1\">\n <div ngbDropdown class=\"d-inline-block me-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>{{getChoosenPlan() ?\n getChoosenPlan().name: ('Choose Plan' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let plan of plans\" (click)=\"onPlanClick(plan)\">{{plan.name }}\n </button>\n </div>\n </div>\n <div class=\"d-inline-block\" ngbDropdown #myDrop=\"ngbDropdown\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownManual\" ngbDropdownToggle>{{'Choose action' |\n translate}}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownManual\">\n <button (click)=\"onCalibrateClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Calibrate' |\n translate}}</button>\n <button (click)=\"onEditClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Edit plan' |\n translate}}\n </button>\n <button (click)=\"onUploadClick()\" ngbDropdownItem>{{'Upload new plan' | translate}}</button>\n <button (click)=\"onDownloadClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Download plan' |\n translate}}</button>\n <button (click)=\"onRemoveClick()\" ngbDropdownItem\n [disabled]=\"!getChoosenPlan() || getChoosenPlan().isImportedMatterport\">{{'Delete plan' |\n translate}}</button>\n <!-- <button (click)=\"onDownloadAsPng()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">\n {{'Download plan as PNG' | translate}}</button> -->\n </div>\n </div>\n </div>\n <div class=\"row mt-3 ms-0\">\n <h4 *ngIf=\"fileToUpload\">{{'New plan' | translate }}</h4>\n <input class=\"hidden\" type=\"file\" id=\"upload-file\" name=\"upload-file\" accept=\"image/png, image/jpeg, .pdf, .svg\"\n ngf-max-size=\"6MB\" (change)=\"addPlan($event.target)\">\n </div>\n <div class=\"col-md-6 mt-3\" *ngIf=\"planForm\">\n <form (ngSubmit)=\"onSavePlan()\" [formGroup]=\"planForm\">\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Name' | translate}} </label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"name\">\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Zone' | translate}} </label>\n <div class=\"col-sm-9\">\n <select class=\"form-control\" formControlName=\"zoneID\">\n <option *ngFor=\"let zone of zones\" [ngValue]=\"zone.id\">\n {{ zone.name }}\n </option>\n </select>\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Set as current plan for zone' | translate}}</label>\n <div class=\"col-sm-3\">\n <input type=\"checkbox\" [(ngModel)]=\"isCurrentPlanForZone\" [ngModelOptions]=\"{standalone: true}\">\n </div>\n </div>\n <button [disabled]=\"planForm.invalid\" type='submit' class=\"btn btn-label-file rounded-pill\">{{'Save' |\n translate}}</button>\n <button class=\"btn btn-label-file rounded-pill ms-3\" type=\"button\" (click)=\"onCancelUpload()\">{{'Cancel' |\n translate}}</button>\n </form>\n </div>\n <ul class=\"col-md-6 list-group list-group-flush\" *ngIf=\"getChoosenPlan()\">\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate }} : {{getChoosenPlan().name}} </li>\n <li class=\"list-group-item bg-transparent\"> {{'Plan is ' | translate }} :\n {{getChoosenPlan().calibration ? ('Calibrated' | translate) : ('Not calibrated' | translate)}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Attributed to zone ' | translate }} :\n {{getChoosenPlan().zone ? getChoosenPlan().zone.name : 'None'}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Is current plan for zone ' | translate }} :\n <input type=\"checkbox\" [(ngModel)]=\"getChoosenPlan().isCurrentForZone\" (change)=\"onCurrentPlanClick()\">\n </li>\n\n </ul>\n\n <div class=\"row mt-4\">\n <div class=\"col-md-10\">\n <embed *ngIf=\"getChoosenPlan() && chosenPlanIsPdf\" [src]=\"getChoosenPlan().filepath | safeUrl\"\n type=\"application/pdf\" frameBorder=\"0\" scrolling=\"auto\" height=\"650px;\" width=\"100%\" />\n <div *ngIf=\"!chosenPlanIsPdf\" class=\"row\" style=\"height: 500px; overflow: hidden;\" id=\"canvasDiv\">\n <canvas id=\"canvas\" width=\"4096px\" height=\"4096px\">\n <!-- <img *ngIf=\"chosenPlan\" id=\"plan-image\" [src]=\"chosenPlan.filepath\" style=\"width: 100%;\"> -->\n </canvas>\n\n </div>\n </div>\n </div>\n</div>\n", styles: [".button-visit{display:none;height:30px;width:30px;position:absolute;background:url(https://api.iconify.design/mdi:map-marker-check.svg?color=red&height=30) no-repeat scroll 0 0 transparent;border:none}#button-visit-left{left:350px;top:550px}#button-visit-right{left:500px;top:600px}.hidden{visibility:hidden;width:1px;height:1px}\n"] }]
|
|
210
|
+
}], ctorParameters: function () { return [{ type: i1.ActivatedRoute }, { type: i1.Router }, { type: i2.SpaceService }, { type: i2.PlanService }, { type: i3.FormBuilder }, { type: i4.TranslateService }]; } });
|
|
211
211
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGxhbnMuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LXNtYXJ0ZXJwbGFuLWxvY2F0aW9ucy9zcmMvbGliL2NvbXBvbmVudHMvcGxhbnMvcGxhbnMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LXNtYXJ0ZXJwbGFuLWxvY2F0aW9ucy9zcmMvbGliL2NvbXBvbmVudHMvcGxhbnMvcGxhbnMuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBVSxNQUFNLGVBQWUsQ0FBQztBQUNsRCxPQUFPLEVBQTBCLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBR3BFLE9BQU8sRUFBMEQsZUFBZSxFQUFFLGFBQWEsRUFBRSxvQkFBb0IsRUFBRSxZQUFZLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUMvSyxPQUFPLE9BQU8sTUFBTSxTQUFTLENBQUM7Ozs7Ozs7OztBQU85QixNQUFNLE9BQU8sY0FBYztJQW9DdkIsWUFDWSxLQUFxQixFQUNyQixNQUFjLEVBQ2QsWUFBMEIsRUFDMUIsV0FBd0IsRUFDeEIsRUFBZSxFQUNmLFNBQTJCO1FBTDNCLFVBQUssR0FBTCxLQUFLLENBQWdCO1FBQ3JCLFdBQU0sR0FBTixNQUFNLENBQVE7UUFDZCxpQkFBWSxHQUFaLFlBQVksQ0FBYztRQUMxQixnQkFBVyxHQUFYLFdBQVcsQ0FBYTtRQUN4QixPQUFFLEdBQUYsRUFBRSxDQUFhO1FBQ2YsY0FBUyxHQUFULFNBQVMsQ0FBa0I7UUFsQ3ZDLG9CQUFvQjtRQUVwQixpQkFBWSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBRWxCLFlBQU8sR0FBRyxLQUFLLENBQUM7UUFFaEIsa0JBQWEsR0FBRyxLQUFLLENBQUM7UUFJdEIseUJBQW9CLEdBQUcsS0FBSyxDQUFDO1FBWTdCLG9CQUFlLEdBQUcsS0FBSyxDQUFDO1FB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RENaM0IsNjRMQXNHQTsyRkQxRmEsY0FBYztrQkFMMUIsU0FBUzsrQkFDRSxXQUFXIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkluaXQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEZvcm1Hcm91cCwgRm9ybUJ1aWxkZXIsIFZhbGlkYXRvcnMgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgeyBBY3RpdmF0ZWRSb3V0ZSwgUm91dGVyIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcbmltcG9ydCB7IFRyYW5zbGF0ZVNlcnZpY2UgfSBmcm9tICdAbmd4LXRyYW5zbGF0ZS9jb3JlJztcbmltcG9ydCB7IFpvbmUsIFNwYWNlLCBQbGFuLCBNZW51SXRlbSwgU3BhY2VTZXJ2aWNlLCBQbGFuU2VydmljZSwgZ2V0TWV0YUZvckltYWdlLCBnZXRTaWduZWRGaWxlLCBkb3dubG9hZEZpbGVBc09iamVjdCwgZG93bmxvYWRCbG9iIH0gZnJvbSAnQHNtYXJ0ZXJwbGFuL25neC1zbWFydGVycGxhbi1jb3JlJztcbmltcG9ydCBwYW56b29tIGZyb20gJ3Bhbnpvb20nO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdsaWItcGxhbnMnLFxuICB0ZW1wbGF0ZVVybDogJy4vcGxhbnMuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9wbGFucy5jb21wb25lbnQuc2NzcyddXG59KVxuZXhwb3J0IGNsYXNzIFBsYW5zQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0IHtcblxuICAgIHNwYWNlSUQ6IHN0cmluZztcblxuICAgIGN1cnJlbnRTcGFjZTogU3BhY2U7XG5cbiAgICBwbGFuczogUGxhbltdO1xuXG4gICAgLy8gY2hvc2VuUGxhbjogUGxhbjtcblxuICAgIGluZGV4RGV0YWlscyA9IC0xO1xuXG4gICAgbG9hZGluZyA9IGZhbHNlO1xuXG4gICAgdXBsb2FkaW5nUGxhbiA9IGZhbHNlO1xuXG4gICAgcGxhbkZvcm06IEZvcm1Hcm91cDtcblxuICAgIGlzQ3VycmVudFBsYW5Gb3Jab25lID0gZmFsc2U7XG5cbiAgICBjYW52YXM7XG5cbiAgICBzY2FsZTogbnVtYmVyO1xuXG4gICAgcGFuem9vbTogYW55O1xuXG4gICAgY3VycmVudFpvbmU6IFpvbmU7XG5cbiAgICBmaWxlVG9VcGxvYWQ6IEZpbGU7XG5cbiAgICBjaG9zZW5QbGFuSXNQZGYgPSBmYWxzZTtcblxuICAgIHpvbmVzOiBab25lW107XG5cbiAgICBtZW51SXRlbXM6IE1lbnVJdGVtW10gPSBbXTtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwcml2YXRlIHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSxcbiAgICAgICAgcHJpdmF0ZSByb3V0ZXI6IFJvdXRlcixcbiAgICAgICAgcHJpdmF0ZSBzcGFjZVNlcnZpY2U6IFNwYWNlU2VydmljZSxcbiAgICAgICAgcHJpdmF0ZSBwbGFuU2VydmljZTogUGxhblNlcnZpY2UsXG4gICAgICAgIHByaXZhdGUgZmI6IEZvcm1CdWlsZGVyLFxuICAgICAgICBwcml2YXRlIHRyYW5zbGF0ZTogVHJhbnNsYXRlU2VydmljZSxcbiAgICApIHt9XG5cbiAgICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5zcGFjZUlEID0gdGhpcy5yb3V0ZS5zbmFwc2hvdC5wYXJhbU1hcC5nZXQoXCJpZFwiKTtcbiAgICAgICAgdGhpcy5nZXRQbGFucygpO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgICB0aGlzLnBsYW5TZXJ2aWNlLmdldFBsYW5GaWxlQ2FjaGUoKSAmJlxuICAgICAgICAgICAgdGhpcy5wbGFuU2VydmljZS5nZXRDaG9zZW5QbGFuKClcbiAgICAgICAgKSB7XG4gICAgICAgICAgICB0aGlzLmFkZFBsYW5Gcm9tQ2FjaGUoXG4gICAgICAgICAgICAgICAgdGhpcy5wbGFuU2VydmljZS5nZXRQbGFuRmlsZUNhY2hlKCksXG4gICAgICAgICAgICAgICAgdGhpcy5wbGFuU2VydmljZS5nZXRDaG9zZW5QbGFuKCksXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgdGhpcy5wbGFuU2VydmljZS5zZXRQbGFuRmlsZUNhY2hlKG51bGwpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMucGxhblNlcnZpY2Uuc2V0Q2hvc2VuUGxhbihudWxsKTtcbiAgICB9XG5cbiAgICBzZXR1cE1lbnVJdGVtcygpIHtcbiAgICAgICAgdGhpcy5tZW51SXRlbXMgPSBbXG4gICAgICAgICAgICB7IGxhYmVsOiBcIkxvY2F0aW9uc1wiLCB1cmw6IFwiL2xvY2FsaXNhdGlvblwiIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbGFiZWw6IHRoaXMuY3VycmVudFNwYWNlLm5hbWUsXG4gICAgICAgICAgICAgICAgdXJsOiBgL2xvY2FsaXNhdGlvbi8ke3RoaXMuY3VycmVudFNwYWNlLmlkfWAsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGxhYmVsOiBcIlBsYW5zXCIsXG4gICAgICAgICAgICAgICAgdXJsOiBgL2xvY2FsaXNhdGlvbi8ke3RoaXMuY3VycmVudFNwYWNlLmlkfS9wbGFuc2AsXG4gICAgICAgICAgICB9LFxuICAgICAgICBdO1xuICAgIH1cblxuICAgIG9uR29CYWNrKCkge31cblxuICAgIGFzeW5jIG9uUGxhbkNsaWNrKHBsYW4/OiBQbGFuKSB7XG4gICAgICAgIGlmIChwbGFuKSB0aGlzLnBsYW5TZXJ2aWNlLnNldENob3NlblBsYW4ocGxhbik7XG4gICAgICAgIHRoaXMuY2hvc2VuUGxhbklzUGRmID0gcGxhbi5hbm5leGUuaW5jbHVkZXMoXCJwZGZcIik7XG4gICAgICAgIGlmICghdGhpcy5jaG9zZW5QbGFuSXNQZGYpIHtcbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuY29uZmlndXJlQ2FudmFzKCkudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucGFuem9vbSA9IHBhbnpvb20odGhpcy5jYW52YXMsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJvdW5kczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGJvdW5kc1BhZGRpbmc6IDAsXG4gICAgICAgICAgICAgICAgICAgICAgICBtYXhab29tOiAzLjUsXG4gICAgICAgICAgICAgICAgICAgICAgICBtaW5ab29tOiAwLjEsXG4gICAgICAgICAgICAgICAgICAgICAgICBpbml0aWFsWm9vbTogMC41LFxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0sIDUwMCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBhc3luYyBjb25maWd1cmVDYW52YXMoKSB7XG4gICAgICAgIHRoaXMudXBsb2FkaW5nUGxhbiA9IHRydWU7XG4gICAgICAgIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXCIjY2FudmFzXCIpIGFzIEhUTUxDYW52YXNFbGVtZW50O1xuICAgICAgICBjb25zdCBpbWFnZSA9IGF3YWl0IGdldE1ldGFGb3JJbWFnZSh0aGlzLmdldENob29zZW5QbGFuKCkuZmlsZXBhdGgpO1xuICAgICAgICBjYW52YXMud2lkdGggPSBpbWFnZS53aWR0aDtcbiAgICAgICAgY2FudmFzLmhlaWdodCA9IGltYWdlLmhlaWdodDtcbiAgICAgICAgdGhpcy5jYW52YXMgPSBjYW52YXM7XG4gICAgICAgIHRoaXMuZHJhd0ltYWdlKHRoaXMuZ2V0Q2hvb3NlblBsYW4oKS5maWxlcGF0aCk7XG4gICAgICAgIHRoaXMudXBsb2FkaW5nUGxhbiA9IGZhbHNlO1xuICAgIH1cblxuICAgIGRyYXdJbWFnZSh1cmw6IHN0cmluZywgeCA9IDAsIHkgPSAwKSB7XG4gICAgICAgIGNvbnN0IGltYWdlID0gbmV3IEltYWdlKCk7XG4gICAgICAgIGNvbnN0IGNvbnRleHQgPSB0aGlzLmNhbnZhcy5nZXRDb250ZXh0KFwiMmRcIik7XG4gICAgICAgIGNvbnN0IGltYWdlV2lkdGggPSB0aGlzLmNhbnZhcy53aWR0aDtcbiAgICAgICAgY29uc3QgaW1hZ2VIZWlnaHQgPSB0aGlzLmNhbnZhcy5oZWlnaHQ7XG4gICAgICAgIGltYWdlLmFkZEV2ZW50TGlzdGVuZXIoXCJsb2FkXCIsICgpID0+IHtcbiAgICAgICAgICAgIGNvbnRleHQuZHJhd0ltYWdlKFxuICAgICAgICAgICAgICAgIGltYWdlLFxuICAgICAgICAgICAgICAgIDAsXG4gICAgICAgICAgICAgICAgMCxcbiAgICAgICAgICAgICAgICBpbWFnZS53aWR0aCxcbiAgICAgICAgICAgICAgICBpbWFnZS5oZWlnaHQsXG4gICAgICAgICAgICAgICAgeCxcbiAgICAgICAgICAgICAgICB5LFxuICAgICAgICAgICAgICAgIGltYWdlV2lkdGgsXG4gICAgICAgICAgICAgICAgaW1hZ2VIZWlnaHQsXG4gICAgICAgICAgICApO1xuICAgICAgICB9KTtcbiAgICAgICAgaW1hZ2Uuc3JjID0gdXJsO1xuICAgICAgICAvLyBpbWFnZS5jcm9zc09yaWdpbiA9IFwiKlwiOyAvL25lZWQgdG8gZG93bmxvYWQgYXMgcG5nLFxuICAgICAgICAvLyBCVVQ6IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQ5NTAzMTcxL3RoZS1pbWFnZS10YWctd2l0aC1jcm9zc29yaWdpbi1hbm9ueW1vdXMtY2FudC1sb2FkLXN1Y2Nlc3MtZnJvbS1zM1xuICAgIH1cblxuICAgIGFzeW5jIGdldFBsYW5zKCkge1xuICAgICAgICB0aGlzLmxvYWRpbmcgPSB0cnVlO1xuICAgICAgICB0aGlzLmN1cnJlbnRTcGFjZSA9IGF3YWl0IHRoaXMuc3BhY2VTZXJ2aWNlLmdldFNwYWNlKHRoaXMuc3BhY2VJRCk7XG4gICAgICAgIHRoaXMuc2V0dXBNZW51SXRlbXMoKTtcblxuICAgICAgICB0aGlzLnpvbmVzID0gdGhpcy5jdXJyZW50U3BhY2Uuem9uZXMuaXRlbXM7XG4gICAgICAgIHRoaXMucGxhbnMgPSBhd2FpdCB0aGlzLnBsYW5TZXJ2aWNlLmdldFNpbmdlZFBsYW5zRm9yU3BhY2UodGhpcy5zcGFjZUlEKTtcbiAgICAgICAgdGhpcy5sb2FkaW5nID0gZmFsc2U7XG4gICAgICAgIGlmICh0aGlzLnBsYW5zLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICAgICAgdGhpcy5vblBsYW5DbGljayh0aGlzLnBsYW5zWzBdKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGdldENob29zZW5QbGFuKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5wbGFuU2VydmljZS5nZXRDaG9zZW5QbGFuKCk7XG4gICAgfVxuXG4gICAgcmVtb3ZlSW1hZ2UoKSB7XG4gICAgICAgIGNvbnN0IGNvbnRleHQgPSB0aGlzLmNhbnZhcy5nZXRDb250ZXh0KFwiMmRcIik7XG4gICAgICAgIGNvbnRleHQuY2xlYXJSZWN0KDAsIDAsIDQwOTYsIDQwOTYpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqXG4gICAgICovXG4gICAgb25DYWxpYnJhdGVDbGljaygpIHtcbiAgICAgICAgaWYgKHRoaXMuZ2V0Q2hvb3NlblBsYW4oKSkge1xuICAgICAgICAgICAgdGhpcy5yb3V0ZXIubmF2aWdhdGUoXG4gICAgICAgICAgICAgICAgW1wiL2Rhc2hib2FyZC9sb2NhbGlzYXRpb25cIiwgdGhpcy5zcGFjZUlELCBcInBsYW4tY2FsaWJyYXRpb25cIl0sXG4gICAgICAgICAgICAgICAgeyBxdWVyeVBhcmFtczogeyBzcGFjZUlEOiB0aGlzLnNwYWNlSUQgfSB9LFxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqXG4gICAgICovXG4gICAgb25FZGl0Q2xpY2soKSB7XG4gICAgICAgIGlmICh0aGlzLmdldENob29zZW5QbGFuKCkpIHtcbiAgICAgICAgICAgIHRoaXMucm91dGVyLm5hdmlnYXRlKFxuICAgICAgICAgICAgICAgIFtcIi9kYXNoYm9hcmQvbG9jYWxpc2F0aW9uXCIsIHRoaXMuc3BhY2VJRCwgXCJwbGFuLWVkaXRcIl0sXG4gICAgICAgICAgICAgICAgeyBxdWVyeVBhcmFtczogeyBzcGFjZUlEOiB0aGlzLnNwYWNlSUQgfSB9LFxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uVXBsb2FkQ2xpY2soKSB7XG4gICAgICAgIGNvbnN0IGlucHV0RmlsZSA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXG4gICAgICAgICAgICBcIiN1cGxvYWQtZmlsZVwiLFxuICAgICAgICApIGFzIEhUTUxJbnB1dEVsZW1lbnQ7XG4gICAgICAgIGlucHV0RmlsZS5jbGljaygpO1xuICAgIH1cblxuICAgIGFkZFBsYW4odGFyZ2V0OiBFdmVudFRhcmdldCkge1xuICAgICAgICB0aGlzLmZpbGVUb1VwbG9hZCA9ICh0YXJnZXQgYXMgSFRNTElucHV0RWxlbWVudCkuZmlsZXNbMF07XG4gICAgICAgIHRoaXMucGxhbkZvcm0gPSB0aGlzLmZiLmdyb3VwKHtcbiAgICAgICAgICAgIG5hbWU6IFt0aGlzLmZpbGVUb1VwbG9hZC5uYW1lLCBbVmFsaWRhdG9ycy5yZXF1aXJlZF1dLFxuICAgICAgICAgICAgem9uZUlEOiBbbnVsbCwgW1ZhbGlkYXRvcnMucmVxdWlyZWRdXSxcbiAgICAgICAgICAgIHNwYWNlSUQ6IHRoaXMuc3BhY2VJRCxcbiAgICAgICAgICAgIGlzTW9kaWZpZWQ6IGZhbHNlLFxuICAgICAgICAgICAgY2FsaWJyYXRpb246XCJcIixcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgYWRkUGxhbkZyb21DYWNoZShmaWxlOiBGaWxlLCBwbGFuOiBQbGFuKSB7XG4gICAgICAgIHRoaXMuZmlsZVRvVXBsb2FkID0gZmlsZTtcbiAgICAgICAgdGhpcy5wbGFuRm9ybSA9IHRoaXMuZmIuZ3JvdXAoe1xuICAgICAgICAgICAgbmFtZTogW3RoaXMuZmlsZVRvVXBsb2FkLm5hbWUsIFtWYWxpZGF0b3JzLnJlcXVpcmVkXV0sXG4gICAgICAgICAgICB6b25lSUQ6IFtwbGFuID8gcGxhbi56b25lSUQgOiBudWxsLCBbVmFsaWRhdG9ycy5yZXF1aXJlZF1dLFxuICAgICAgICAgICAgc3BhY2VJRDogdGhpcy5zcGFjZUlELFxuICAgICAgICAgICAgaXNNb2RpZmllZDogISFwbGFuLFxuICAgICAgICAgICAgY2FsaWJyYXRpb246XG4gICAgICAgICAgICAgICAgcGxhbiAmJiBwbGFuLmlzTW9kaWZpZWQgJiYgcGxhbi5jYWxpYnJhdGlvblxuICAgICAgICAgICAgICAgICAgICA/IHBsYW4uY2FsaWJyYXRpb25cbiAgICAgICAgICAgICAgICAgICAgOiBcIlwiLFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBhc3luYyBvblNhdmVQbGFuKCkge1xuICAgICAgICBjb25zdCBmb3JtVmFsdWUgPSB0aGlzLnBsYW5Gb3JtLnZhbHVlIGFzIFBsYW47XG4gICAgICAgIGZvcm1WYWx1ZS5pc0N1cnJlbnRGb3Jab25lID0gdGhpcy5pc0N1cnJlbnRQbGFuRm9yWm9uZTtcbiAgICAgICAgY29uc3QgY3JlYXRlZFBsYW4gPSBhd2FpdCB0aGlzLnBsYW5TZXJ2aWNlLmNyZWF0ZVBsYW5XaXRoQW5uZXhlKFxuICAgICAgICAgICAgZm9ybVZhbHVlLFxuICAgICAgICAgICAgdGhpcy5maWxlVG9VcGxvYWQsXG4gICAgICAgICk7XG4gICAgICAgIGNvbnN0IGZpbGVwYXRoID0gYXdhaXQgZ2V0U2lnbmVkRmlsZShjcmVhdGVkUGxhbi5hbm5leGUpO1xuICAgICAgICBpZiAoZmlsZXBhdGgpIHtcbiAgICAgICAgICAgIGNyZWF0ZWRQbGFuLmZpbGVwYXRoID0gZmlsZXBhdGg7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGZvcm1WYWx1ZS5pc0N1cnJlbnRGb3Jab25lKSB7XG4gICAgICAgICAgICAvLyBzZXQgYWxsIG90aGVyIHBsYW5zIHRvIG5vdCBjdXJyZW50XG4gICAgICAgICAgICB0aGlzLnBsYW5TZXJ2aWNlLnNldEFsbFBsYW5zRm9yWm9uZU5vdEN1cnJlbnQoXG4gICAgICAgICAgICAgICAgY3JlYXRlZFBsYW4uem9uZUlELFxuICAgICAgICAgICAgICAgIGNyZWF0ZWRQbGFuLmlkLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnBsYW5zLnB1c2goY3JlYXRlZFBsYW4pO1xuICAgICAgICB0aGlzLnBsYW5Gb3JtID0gbnVsbDtcbiAgICAgICAgdGhpcy5maWxlVG9VcGxvYWQgPSBudWxsO1xuICAgIH1cblxuICAgIG9uQ2FuY2VsVXBsb2FkKCkge1xuICAgICAgICB0aGlzLmZpbGVUb1VwbG9hZCA9IG51bGw7XG4gICAgICAgIHRoaXMucGxhbkZvcm0gPSBudWxsO1xuICAgIH1cblxuICAgIGFzeW5jIG9uQ3VycmVudFBsYW5DbGljaygpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5wbGFuU2VydmljZS51cGRhdGVQbGFuKHtcbiAgICAgICAgICAgIGlkOiB0aGlzLmdldENob29zZW5QbGFuKCkuaWQsXG4gICAgICAgICAgICBpc0N1cnJlbnRGb3Jab25lOiB0aGlzLmdldENob29zZW5QbGFuKCkuaXNDdXJyZW50Rm9yWm9uZSxcbiAgICAgICAgfSk7XG4gICAgICAgIGlmICh0aGlzLmdldENob29zZW5QbGFuKCkuaXNDdXJyZW50Rm9yWm9uZSkge1xuICAgICAgICAgICAgdGhpcy5wbGFuU2VydmljZS5zZXRBbGxQbGFuc0ZvclpvbmVOb3RDdXJyZW50KFxuICAgICAgICAgICAgICAgIHRoaXMuZ2V0Q2hvb3NlblBsYW4oKS56b25lSUQsXG4gICAgICAgICAgICAgICAgdGhpcy5nZXRDaG9vc2VuUGxhbigpLmlkLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGFzeW5jIG9uRG93bmxvYWRDbGljaygpIHtcbiAgICAgICAgY29uc3Qgc2lnbmVkRmlsZSA9IGF3YWl0IGRvd25sb2FkRmlsZUFzT2JqZWN0KFxuICAgICAgICAgICAgdGhpcy5nZXRDaG9vc2VuUGxhbigpLmFubmV4ZSxcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKHNpZ25lZEZpbGUpIHtcbiAgICAgICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgICAgIGRvd25sb2FkQmxvYihzaWduZWRGaWxlLkJvZHksIHRoaXMuZ2V0Q2hvb3NlblBsYW4oKS5uYW1lKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGFzeW5jIG9uRG93bmxvYWRBc1BuZygpIHtcbiAgICAgICAgY29uc3QgY2FudmFzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcIiNjYW52YXNcIikgYXMgSFRNTENhbnZhc0VsZW1lbnQ7XG4gICAgICAgIGNhbnZhcy50b0Jsb2IoZnVuY3Rpb24gKGJsb2IpIHtcbiAgICAgICAgICAgIGRvd25sb2FkQmxvYihibG9iLCBcInBsYW4tYXMtcG5nLnBuZ1wiKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgYXN5bmMgb25SZW1vdmVDbGljaygpIHtcbiAgICAgICAgY29uc3QgbWVzc2FnZSA9IHRoaXMudHJhbnNsYXRlLmluc3RhbnQoXCJjb25maXJtLmRlbGV0ZVBsYW5cIik7XG4gICAgICAgIGlmICh3aW5kb3cuY29uZmlybShtZXNzYWdlKSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5wbGFuU2VydmljZS5kZWxldGVQbGFuKHRoaXMuZ2V0Q2hvb3NlblBsYW4oKSk7XG4gICAgICAgICAgICB0aGlzLnJlbW92ZUltYWdlKCk7XG4gICAgICAgICAgICB0aGlzLnBsYW5TZXJ2aWNlLnNldENob3NlblBsYW4obnVsbCk7XG4gICAgICAgICAgICB0aGlzLmdldFBsYW5zKCk7XG4gICAgICAgIH1cbiAgICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwiY29udGFpbmVyLWZsdWlkXCIgKm5nSWY9XCJjdXJyZW50U3BhY2VcIiBjbGFzcz1cImRhc2hib2FyZC10YWJcIj5cbiAgICA8ZGl2IGNsYXNzPVwibS0zXCI+XG4gICAgICAgIDxsaWItdGFiLW5hdmlnYXRpb24gW21lbnVJdGVtc109J21lbnVJdGVtcycgKG9uR29CYWNrKT1cIm9uR29CYWNrKClcIj48L2xpYi10YWItbmF2aWdhdGlvbj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IGp1c3RpZnktY29udGVudC1jZW50ZXJcIiAqbmdJZj1cImxvYWRpbmdcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cInNwaW5uZXItYm9yZGVyXCIgcm9sZT1cInN0YXR1c1wiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ2aXN1YWxseS1oaWRkZW5cIj5Mb2FkaW5nLi4uPC9zcGFuPlxuICAgICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwibXQtM1wiICpuZ0lmPVwiIXBsYW5zICYmICFsb2FkaW5nXCI+XG4gICAgICAgIDxoNCBzdHlsZT1cImZvbnQtd2VpZ2h0OiBib2xkO1wiPnt7J05vIGltcG9ydGVkIHBsYW5zJyB8IHRyYW5zbGF0ZSB9fTwvaDQ+XG4gICAgICAgIDxwPnt7J0ZvciBpbXBvcnQgZnJvbSAzRCB2aXNpdDogZ28gVmlydHVhbCBWaXNpdHMgPT4gSW1wb3J0IEltYWdlcycgfCB0cmFuc2xhdGV9fTwvcD5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwicm93IG1zLTFcIj5cbiAgICAgICAgPGRpdiBuZ2JEcm9wZG93biBjbGFzcz1cImQtaW5saW5lLWJsb2NrIG1lLTNcIj5cbiAgICAgICAgICAgIDxidXR0b24gY2xhc3M9XCJidG4gYnRuLWxhYmVsLWZpbGUgcm91bmRlZC1waWxsXCIgaWQ9XCJkcm9wZG93bkJhc2ljMVwiIG5nYkRyb3Bkb3duVG9nZ2xlPnt7Z2V0Q2hvb3NlblBsYW4oKSA/XG4gICAgICAgICAgICAgICAgZ2V0Q2hvb3NlblBsYW4oKS5uYW1lOiAoJ0Nob29zZSBQbGFuJyB8IHRyYW5zbGF0ZSkgfX08L2J1dHRvbj5cbiAgICAgICAgICAgIDxkaXYgbmdiRHJvcGRvd25NZW51IGFyaWEtbGFiZWxsZWRieT1cImRyb3Bkb3duQmFzaWMxXCI+XG4gICAgICAgICAgICAgICAgPGJ1dHRvbiBuZ2JEcm9wZG93bkl0ZW0gKm5nRm9yPVwibGV0IHBsYW4gb2YgcGxhbnNcIiAoY2xpY2spPVwib25QbGFuQ2xpY2socGxhbilcIj57e3BsYW4ubmFtZSB9fVxuICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8ZGl2IGNsYXNzPVwiZC1pbmxpbmUtYmxvY2tcIiBuZ2JEcm9wZG93biAjbXlEcm9wPVwibmdiRHJvcGRvd25cIj5cbiAgICAgICAgICAgIDxidXR0b24gY2xhc3M9XCJidG4gYnRuLWxhYmVsLWZpbGUgcm91bmRlZC1waWxsXCIgaWQ9XCJkcm9wZG93bk1hbnVhbFwiIG5nYkRyb3Bkb3duVG9nZ2xlPnt7J0Nob29zZSBhY3Rpb24nIHxcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGV9fTwvYnV0dG9uPlxuICAgICAgICAgICAgPGRpdiBuZ2JEcm9wZG93bk1lbnUgYXJpYS1sYWJlbGxlZGJ5PVwiZHJvcGRvd25NYW51YWxcIj5cbiAgICAgICAgICAgICAgICA8YnV0dG9uIChjbGljayk9XCJvbkNhbGlicmF0ZUNsaWNrKClcIiBuZ2JEcm9wZG93bkl0ZW0gW2Rpc2FibGVkXT1cIiFnZXRDaG9vc2VuUGxhbigpXCI+e3snQ2FsaWJyYXRlJyB8XG4gICAgICAgICAgICAgICAgICAgIHRyYW5zbGF0ZX19PC9idXR0b24+XG4gICAgICAgICAgICAgICAgPGJ1dHRvbiAoY2xpY2spPVwib25FZGl0Q2xpY2soKVwiIG5nYkRyb3Bkb3duSXRlbSBbZGlzYWJsZWRdPVwiIWdldENob29zZW5QbGFuKClcIj57eydFZGl0IHBsYW4nIHxcbiAgICAgICAgICAgICAgICAgICAgdHJhbnNsYXRlfX1cbiAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICA8YnV0dG9uIChjbGljayk9XCJvblVwbG9hZENsaWNrKClcIiBuZ2JEcm9wZG93bkl0ZW0+e3snVXBsb2FkIG5ldyBwbGFuJyB8IHRyYW5zbGF0ZX19PC9idXR0b24+XG4gICAgICAgICAgICAgICAgPGJ1dHRvbiAoY2xpY2spPVwib25Eb3dubG9hZENsaWNrKClcIiBuZ2JEcm9wZG93bkl0ZW0gW2Rpc2FibGVkXT1cIiFnZXRDaG9vc2VuUGxhbigpXCI+e3snRG93bmxvYWQgcGxhbicgfFxuICAgICAgICAgICAgICAgICAgICB0cmFuc2xhdGV9fTwvYnV0dG9uPlxuICAgICAgICAgICAgICAgIDxidXR0b24gKGNsaWNrKT1cIm9uUmVtb3ZlQ2xpY2soKVwiIG5nYkRyb3Bkb3duSXRlbVxuICAgICAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiIWdldENob29zZW5QbGFuKCkgfHwgZ2V0Q2hvb3NlblBsYW4oKS5pc0ltcG9ydGVkTWF0dGVycG9ydFwiPnt7J0RlbGV0ZSBwbGFuJyB8XG4gICAgICAgICAgICAgICAgICAgIHRyYW5zbGF0ZX19PC9idXR0b24+XG4gICAgICAgICAgICAgICAgPCEtLSA8YnV0dG9uIChjbGljayk9XCJvbkRvd25sb2FkQXNQbmcoKVwiIG5nYkRyb3Bkb3duSXRlbSBbZGlzYWJsZWRdPVwiIWdldENob29zZW5QbGFuKClcIj5cbiAgICAgICAgICAgICAgICAgICAge3snRG93bmxvYWQgcGxhbiBhcyBQTkcnIHwgdHJhbnNsYXRlfX08L2J1dHRvbj4gLS0+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInJvdyBtdC0zIG1zLTBcIj5cbiAgICAgICAgPGg0ICpuZ0lmPVwiZmlsZVRvVXBsb2FkXCI+e3snTmV3IHBsYW4nIHwgdHJhbnNsYXRlIH19PC9oND5cbiAgICAgICAgPGlucHV0IGNsYXNzPVwiaGlkZGVuXCIgdHlwZT1cImZpbGVcIiBpZD1cInVwbG9hZC1maWxlXCIgbmFtZT1cInVwbG9hZC1maWxlXCIgYWNjZXB0PVwiaW1hZ2UvcG5nLCBpbWFnZS9qcGVnLCAucGRmLCAuc3ZnXCJcbiAgICAgICAgICAgIG5nZi1tYXgtc2l6ZT1cIjZNQlwiIChjaGFuZ2UpPVwiYWRkUGxhbigkZXZlbnQudGFyZ2V0KVwiPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJjb2wtbWQtNiBtdC0zXCIgKm5nSWY9XCJwbGFuRm9ybVwiPlxuICAgICAgICA8Zm9ybSAobmdTdWJtaXQpPVwib25TYXZlUGxhbigpXCIgW2Zvcm1Hcm91cF09XCJwbGFuRm9ybVwiPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm1iLTMgcm93XCI+XG4gICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzPVwiY29sLXNtLTNcIj57eydOYW1lJyB8IHRyYW5zbGF0ZX19IDwvbGFiZWw+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImNvbC1zbS05XCI+XG4gICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwidGV4dFwiIGNsYXNzPVwiZm9ybS1jb250cm9sXCIgZm9ybUNvbnRyb2xOYW1lPVwibmFtZVwiPlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWItMyByb3dcIj5cbiAgICAgICAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJjb2wtc20tM1wiPnt7J1pvbmUnIHwgdHJhbnNsYXRlfX0gPC9sYWJlbD5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiY29sLXNtLTlcIj5cbiAgICAgICAgICAgICAgICAgICAgPHNlbGVjdCBjbGFzcz1cImZvcm0tY29udHJvbFwiIGZvcm1Db250cm9sTmFtZT1cInpvbmVJRFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgPG9wdGlvbiAqbmdGb3I9XCJsZXQgem9uZSBvZiB6b25lc1wiIFtuZ1ZhbHVlXT1cInpvbmUuaWRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7eyB6b25lLm5hbWUgfX1cbiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3B0aW9uPlxuICAgICAgICAgICAgICAgICAgICA8L3NlbGVjdD5cbiAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm1iLTMgcm93XCI+XG4gICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzPVwiY29sLXNtLTNcIj57eydTZXQgYXMgY3VycmVudCBwbGFuIGZvciB6b25lJyB8IHRyYW5zbGF0ZX19PC9sYWJlbD5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiY29sLXNtLTNcIj5cbiAgICAgICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9XCJjaGVja2JveFwiIFsobmdNb2RlbCldPVwiaXNDdXJyZW50UGxhbkZvclpvbmVcIiBbbmdNb2RlbE9wdGlvbnNdPVwie3N0YW5kYWxvbmU6IHRydWV9XCI+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDxidXR0b24gW2Rpc2FibGVkXT1cInBsYW5Gb3JtLmludmFsaWRcIiB0eXBlPSdzdWJtaXQnIGNsYXNzPVwiYnRuIGJ0bi1sYWJlbC1maWxlIHJvdW5kZWQtcGlsbFwiPnt7J1NhdmUnIHxcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGV9fTwvYnV0dG9uPlxuICAgICAgICAgICAgPGJ1dHRvbiBjbGFzcz1cImJ0biBidG4tbGFiZWwtZmlsZSByb3VuZGVkLXBpbGwgbXMtM1wiIHR5cGU9XCJidXR0b25cIiAoY2xpY2spPVwib25DYW5jZWxVcGxvYWQoKVwiPnt7J0NhbmNlbCcgfFxuICAgICAgICAgICAgICAgIHRyYW5zbGF0ZX19PC9idXR0b24+XG4gICAgICAgIDwvZm9ybT5cbiAgICA8L2Rpdj5cbiAgICA8dWwgY2xhc3M9XCJjb2wtbWQtNiBsaXN0LWdyb3VwIGxpc3QtZ3JvdXAtZmx1c2hcIiAqbmdJZj1cImdldENob29zZW5QbGFuKClcIj5cbiAgICAgICAgPGxpIGNsYXNzPVwibGlzdC1ncm91cC1pdGVtIGJnLXRyYW5zcGFyZW50XCI+e3snTmFtZScgfCB0cmFuc2xhdGUgfX0gOiB7e2dldENob29zZW5QbGFuKCkubmFtZX19IDwvbGk+XG4gICAgICAgIDxsaSBjbGFzcz1cImxpc3QtZ3JvdXAtaXRlbSBiZy10cmFuc3BhcmVudFwiPiB7eydQbGFuIGlzICcgfCB0cmFuc2xhdGUgfX0gOlxuICAgICAgICAgICAge3tnZXRDaG9vc2VuUGxhbigpLmNhbGlicmF0aW9uID8gKCdDYWxpYnJhdGVkJyB8IHRyYW5zbGF0ZSkgOiAoJ05vdCBjYWxpYnJhdGVkJyB8IHRyYW5zbGF0ZSl9fTwvbGk+XG4gICAgICAgIDxsaSBjbGFzcz1cImxpc3QtZ3JvdXAtaXRlbSBiZy10cmFuc3BhcmVudFwiPnt7J0F0dHJpYnV0ZWQgdG8gem9uZSAnIHwgdHJhbnNsYXRlIH19IDpcbiAgICAgICAgICAgIHt7Z2V0Q2hvb3NlblBsYW4oKS56b25lID8gZ2V0Q2hvb3NlblBsYW4oKS56b25lLm5hbWUgOiAnTm9uZSd9fTwvbGk+XG4gICAgICAgIDxsaSBjbGFzcz1cImxpc3QtZ3JvdXAtaXRlbSBiZy10cmFuc3BhcmVudFwiPnt7J0lzIGN1cnJlbnQgcGxhbiBmb3Igem9uZSAnIHwgdHJhbnNsYXRlIH19IDpcbiAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwiY2hlY2tib3hcIiBbKG5nTW9kZWwpXT1cImdldENob29zZW5QbGFuKCkuaXNDdXJyZW50Rm9yWm9uZVwiIChjaGFuZ2UpPVwib25DdXJyZW50UGxhbkNsaWNrKClcIj5cbiAgICAgICAgPC9saT5cblxuICAgIDwvdWw+XG5cbiAgICA8ZGl2IGNsYXNzPVwicm93IG10LTRcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImNvbC1tZC0xMFwiPlxuICAgICAgICAgICAgPGVtYmVkICpuZ0lmPVwiZ2V0Q2hvb3NlblBsYW4oKSAmJiBjaG9zZW5QbGFuSXNQZGZcIiBbc3JjXT1cImdldENob29zZW5QbGFuKCkuZmlsZXBhdGggfCBzYWZlVXJsXCJcbiAgICAgICAgICAgICAgICB0eXBlPVwiYXBwbGljYXRpb24vcGRmXCIgZnJhbWVCb3JkZXI9XCIwXCIgc2Nyb2xsaW5nPVwiYXV0b1wiIGhlaWdodD1cIjY1MHB4O1wiIHdpZHRoPVwiMTAwJVwiIC8+XG4gICAgICAgICAgICA8ZGl2ICpuZ0lmPVwiIWNob3NlblBsYW5Jc1BkZlwiIGNsYXNzPVwicm93XCIgc3R5bGU9XCJoZWlnaHQ6IDUwMHB4OyBvdmVyZmxvdzogaGlkZGVuO1wiIGlkPVwiY2FudmFzRGl2XCI+XG4gICAgICAgICAgICAgICAgPGNhbnZhcyBpZD1cImNhbnZhc1wiIHdpZHRoPVwiNDA5NnB4XCIgaGVpZ2h0PVwiNDA5NnB4XCI+XG4gICAgICAgICAgICAgICAgICAgIDwhLS0gPGltZyAqbmdJZj1cImNob3NlblBsYW5cIiBpZD1cInBsYW4taW1hZ2VcIiBbc3JjXT1cImNob3NlblBsYW4uZmlsZXBhdGhcIiBzdHlsZT1cIndpZHRoOiAxMDAlO1wiPiAtLT5cbiAgICAgICAgICAgICAgICA8L2NhbnZhcz5cblxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuPC9kaXY+XG4iXX0=
|