@myrmidon/gve-core 0.0.5 → 1.0.0

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.
Files changed (35) hide show
  1. package/README.md +2 -0
  2. package/fesm2022/myrmidon-gve-core.mjs +370 -259
  3. package/fesm2022/myrmidon-gve-core.mjs.map +1 -1
  4. package/lib/components/batch-operation-editor/batch-operation-editor.component.d.ts +46 -0
  5. package/lib/components/chain-result-view/chain-result-view.component.d.ts +4 -1
  6. package/lib/components/feature-editor/feature-editor.component.d.ts +1 -1
  7. package/lib/components/feature-set-editor/feature-set-editor.component.d.ts +1 -1
  8. package/lib/components/snapshot-editor/snapshot-editor.component.d.ts +11 -9
  9. package/lib/components/steps-map/steps-map.component.d.ts +2 -0
  10. package/lib/models.d.ts +8 -0
  11. package/lib/services/gve-api.service.d.ts +33 -0
  12. package/package.json +10 -12
  13. package/public-api.d.ts +1 -0
  14. package/esm2022/lib/components/animation-timeline/animation-timeline.component.mjs +0 -207
  15. package/esm2022/lib/components/animation-timeline-set/animation-timeline-set.component.mjs +0 -154
  16. package/esm2022/lib/components/animation-tween/animation-tween.component.mjs +0 -184
  17. package/esm2022/lib/components/base-text-char/base-text-char.component.mjs +0 -46
  18. package/esm2022/lib/components/base-text-editor/base-text-editor.component.mjs +0 -115
  19. package/esm2022/lib/components/base-text-view/base-text-view.component.mjs +0 -159
  20. package/esm2022/lib/components/chain-operation-editor/chain-operation-editor.component.mjs +0 -570
  21. package/esm2022/lib/components/chain-result-view/chain-result-view.component.mjs +0 -222
  22. package/esm2022/lib/components/feature-editor/feature-editor.component.mjs +0 -200
  23. package/esm2022/lib/components/feature-set-editor/feature-set-editor.component.mjs +0 -175
  24. package/esm2022/lib/components/feature-set-view/feature-set-view.component.mjs +0 -100
  25. package/esm2022/lib/components/ln-heights-editor/ln-heights-editor.component.mjs +0 -126
  26. package/esm2022/lib/components/operation-source-editor/operation-source-editor.component.mjs +0 -131
  27. package/esm2022/lib/components/simple-tree/simple-tree.component.mjs +0 -72
  28. package/esm2022/lib/components/snapshot-editor/snapshot-editor.component.mjs +0 -863
  29. package/esm2022/lib/components/steps-map/steps-map.component.mjs +0 -77
  30. package/esm2022/lib/models.mjs +0 -2
  31. package/esm2022/lib/services/gve-api.service.mjs +0 -65
  32. package/esm2022/lib/services/settings.service.mjs +0 -87
  33. package/esm2022/lib/validators/svg-validators.mjs +0 -34
  34. package/esm2022/myrmidon-gve-core.mjs +0 -5
  35. package/esm2022/public-api.mjs +0 -22
@@ -0,0 +1,46 @@
1
+ import { EventEmitter, OnInit } from '@angular/core';
2
+ import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
3
+ import { MatDialogRef } from '@angular/material/dialog';
4
+ import { CharChainOperation } from '@myrmidon/gve-snapshot-view';
5
+ import { PayloadMatDialogConfig } from '../../models';
6
+ import { GveApiService } from '../../services/gve-api.service';
7
+ import * as i0 from "@angular/core";
8
+ /**
9
+ * 🔑 `gve-batch-operation-editor`
10
+ *
11
+ * A component to edit a batch of operations. This component can be used both in a dialog
12
+ * and as a standalone component.
13
+ *
14
+ * - ▶️ `preset` (`string`): the optional preset text to parse.
15
+ * - 🔥 `operationsChange` (`CharChainOperation[]`): event emitted when operations change.
16
+ */
17
+ export declare class BatchOperationEditorComponent implements OnInit {
18
+ private _api;
19
+ dialogRef?: MatDialogRef<BatchOperationEditorComponent> | undefined;
20
+ data?: PayloadMatDialogConfig<{
21
+ preset?: string;
22
+ }> | undefined;
23
+ private _preset?;
24
+ busy?: boolean;
25
+ parseError?: string;
26
+ text: FormControl<string | null>;
27
+ form: FormGroup;
28
+ /**
29
+ * The preset text to parse if any. Usually you start with a blank
30
+ * text, but sometimes you might want to pre-set it.
31
+ */
32
+ get preset(): string | undefined | null;
33
+ set preset(value: string | undefined | null);
34
+ /**
35
+ * Emitted when operations change.
36
+ */
37
+ readonly operationsChange: EventEmitter<CharChainOperation[]>;
38
+ constructor(formBuilder: FormBuilder, _api: GveApiService, dialogRef?: MatDialogRef<BatchOperationEditorComponent> | undefined, data?: PayloadMatDialogConfig<{
39
+ preset?: string;
40
+ }> | undefined);
41
+ ngOnInit(): void;
42
+ parseOperations(): void;
43
+ close(): void;
44
+ static ɵfac: i0.ɵɵFactoryDeclaration<BatchOperationEditorComponent, [null, null, { optional: true; }, { optional: true; }]>;
45
+ static ɵcmp: i0.ɵɵComponentDeclaration<BatchOperationEditorComponent, "gve-batch-operation-editor", never, { "preset": { "alias": "preset"; "required": false; }; }, { "operationsChange": "operationsChange"; }, never, never, true, never>;
46
+ }
@@ -8,7 +8,10 @@ import * as i0 from "@angular/core";
8
8
  /**
9
9
  * 🔑 `gve-chain-result-view`
10
10
  *
11
- * Component to display a chain result.
11
+ * Component to display a chain result. This provides a version picker
12
+ * by staged version or simple step, and shows the selected step's text
13
+ * and features, plus a steps map. User can pick a step from both the
14
+ * pickers or the map.
12
15
  * Used by the `gve-snapshot-editor` component.
13
16
  *
14
17
  * - ▶️ `result` (`CharChainResult`): the result to display.
@@ -65,7 +65,7 @@ export declare class FeatureEditorComponent implements OnInit, OnDestroy {
65
65
  /**
66
66
  * Event emitted when the user saves the edited feature.
67
67
  */
68
- featureChange: EventEmitter<OperationFeature | Feature>;
68
+ featureChange: EventEmitter<Feature | OperationFeature>;
69
69
  name: FormControl<string>;
70
70
  value: FormControl<string>;
71
71
  setPolicy: FormControl<FeatureSetPolicy>;
@@ -60,7 +60,7 @@ export declare class FeatureSetEditorComponent implements OnInit, OnDestroy {
60
60
  /**
61
61
  * Emitted when the features change.
62
62
  */
63
- featuresChange: EventEmitter<OperationFeature[] | Feature[]>;
63
+ featuresChange: EventEmitter<Feature[] | OperationFeature[]>;
64
64
  editedFeature?: Feature | OperationFeature;
65
65
  filter: FormControl<string | null>;
66
66
  filteredFeatures: Feature[] | OperationFeature[];
@@ -1,6 +1,7 @@
1
- import { ElementRef, EventEmitter, OnInit } from '@angular/core';
1
+ import { ElementRef, EventEmitter } from '@angular/core';
2
2
  import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
3
3
  import { MatSnackBar } from '@angular/material/snack-bar';
4
+ import { MatDialog } from '@angular/material/dialog';
4
5
  import { DialogService } from '@myrmidon/ng-mat-tools';
5
6
  import { CharChainOperation, CharNode, GveAnimationTimeline, GveVisualEvent, Snapshot, SnapshotViewComponent, SnapshotViewData, SnapshotViewRenderEvent } from '@myrmidon/gve-snapshot-view';
6
7
  import { VarBaseTextRange } from '../base-text-view/base-text-view.component';
@@ -18,8 +19,9 @@ import * as i0 from "@angular/core";
18
19
  * - 🔥 `snapshotChange` (`Snapshot`): emitted when the user saves the edited snapshot.
19
20
  * - 🔥 `snapshotCancel` (`void`): emitted when the user cancels the snapshot editing.
20
21
  */
21
- export declare class SnapshotEditorComponent implements OnInit {
22
+ export declare class SnapshotEditorComponent {
22
23
  private _api;
24
+ private _dialog;
23
25
  private _dialogService;
24
26
  private _snackbar;
25
27
  private readonly _nanoid;
@@ -66,7 +68,6 @@ export declare class SnapshotEditorComponent implements OnInit {
66
68
  spcWidthOffset: FormControl<number>;
67
69
  textStyle: FormControl<string | null>;
68
70
  operations: FormControl<CharChainOperation[]>;
69
- inputOps: FormControl<string | null>;
70
71
  opStyle: FormControl<string | null>;
71
72
  form: FormGroup;
72
73
  imageUrl: FormControl<string | null>;
@@ -102,8 +103,7 @@ export declare class SnapshotEditorComponent implements OnInit {
102
103
  result?: CharChainResult;
103
104
  resultOperationId?: string;
104
105
  initialStepIndex: number;
105
- constructor(formBuilder: FormBuilder, _api: GveApiService, _dialogService: DialogService, _snackbar: MatSnackBar);
106
- ngOnInit(): void;
106
+ constructor(formBuilder: FormBuilder, _api: GveApiService, _dialog: MatDialog, _dialogService: DialogService, _snackbar: MatSnackBar);
107
107
  /**
108
108
  * Set the view data for the snapshot view.
109
109
  *
@@ -182,6 +182,7 @@ export declare class SnapshotEditorComponent implements OnInit {
182
182
  * @returns Array of IDs found in the SVG content or undefined.
183
183
  */
184
184
  private parseSvgIds;
185
+ private getTransparentIds;
185
186
  /**
186
187
  * Run the operations up to the specified index (included).
187
188
  * This is called when:
@@ -198,12 +199,13 @@ export declare class SnapshotEditorComponent implements OnInit {
198
199
  * operation in the snapshot at index. This is used when previewing
199
200
  * the edited operation from within the operation editor.
200
201
  */
201
- runTo(index: number, lastOperation?: CharChainOperation): Promise<void>;
202
+ runTo(index: number, lastOperation?: CharChainOperation): Promise<CharChainResult>;
202
203
  /**
203
- * Run the operations up to the last operation if any.
204
- * Otherwise, just update the snapshot view.
204
+ * Run the operations up to the last operation if any, updating the
205
+ * execution result. The execution result is always the result from
206
+ * all the operations, as users must be able to browse across its steps.
205
207
  */
206
- runToLast(): void;
208
+ runToLast(): Promise<void>;
207
209
  /**
208
210
  * Update the snapshot view by running the operations up to the
209
211
  * currently edited operation if any.
@@ -22,6 +22,7 @@ import * as i0 from "@angular/core";
22
22
  */
23
23
  export declare class StepsMapComponent {
24
24
  private _steps?;
25
+ lines: string[];
25
26
  /**
26
27
  * The steps to display.
27
28
  */
@@ -40,6 +41,7 @@ export declare class StepsMapComponent {
40
41
  * the steps are set for the first time.
41
42
  */
42
43
  selectedStepChange: EventEmitter<ChainOperationContextStep>;
44
+ private updateLines;
43
45
  onStepClick(step: ChainOperationContextStep): void;
44
46
  static ɵfac: i0.ɵɵFactoryDeclaration<StepsMapComponent, never>;
45
47
  static ɵcmp: i0.ɵɵComponentDeclaration<StepsMapComponent, "gve-steps-map", never, { "steps": { "alias": "steps"; "required": false; }; "selectedStep": { "alias": "selectedStep"; "required": false; }; "textFontSize": { "alias": "textFontSize"; "required": false; }; }, { "selectedStepChange": "selectedStepChange"; }, never, never, true, never>;
package/lib/models.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { MatDialogConfig } from "@angular/material/dialog";
1
2
  /**
2
3
  * API call result wrapper.
3
4
  */
@@ -5,3 +6,10 @@ export interface ResultWrapper<T> {
5
6
  result?: T;
6
7
  error?: string;
7
8
  }
9
+ /**
10
+ * PayloadMatDialogConfig is a MatDialogConfig with a payload, used
11
+ * to pass data of type T to a dialog.
12
+ */
13
+ export interface PayloadMatDialogConfig<T> extends MatDialogConfig {
14
+ payload?: T;
15
+ }
@@ -8,23 +8,56 @@ import * as i0 from "@angular/core";
8
8
  * Execution step of a char-based chain operation.
9
9
  */
10
10
  export interface ChainOperationContextStep {
11
+ /**
12
+ * The operation that was executed.
13
+ */
11
14
  operation: CharChainOperation;
15
+ /**
16
+ * The input version tag.
17
+ */
12
18
  inputTag: string;
19
+ /**
20
+ * The output version tag.
21
+ */
13
22
  outputTag: string;
23
+ /**
24
+ * The string synthetically representing the result.
25
+ */
14
26
  result?: string;
27
+ /**
28
+ * The features emitted up to the executed operation.
29
+ * These are the features of the text as a whole, and
30
+ * those belonging to specific nodes.
31
+ */
15
32
  featureSet: {
16
33
  features: OperationFeature[];
17
34
  nodeFeatures: Record<string, OperationFeature[]>;
18
35
  };
36
+ /**
37
+ * The reference node IDs of the operation which was
38
+ * executed.
39
+ */
19
40
  refNodeIds: number[];
20
41
  }
21
42
  /**
22
43
  * Result of execution of a char-based chain.
23
44
  */
24
45
  export interface CharChainResult {
46
+ /**
47
+ * The execution steps of the chain.
48
+ */
25
49
  steps: ChainOperationContextStep[];
50
+ /**
51
+ * The chain dump, which is the text representation of the chain.
52
+ */
26
53
  chainDump: string;
54
+ /**
55
+ * The list of unique tags in the chain.
56
+ */
27
57
  chainTags: string[];
58
+ /**
59
+ * The sets of nodes in their order, for each version tag.
60
+ */
28
61
  taggedNodes: {
29
62
  [key: string]: CharNode[];
30
63
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myrmidon/gve-core",
3
- "version": "0.0.5",
3
+ "version": "1.0.0",
4
4
  "description": "Cadmus - GVE Angular frontend core components.",
5
5
  "keywords": [
6
6
  "GVE"
@@ -14,19 +14,19 @@
14
14
  "name": "Daniele Fusi"
15
15
  },
16
16
  "peerDependencies": {
17
- "@angular/common": "^18.0.0",
18
- "@angular/core": "^18.0.0",
19
- "@cisstech/nge": "^17.7.1",
20
- "@myrmidon/ng-mat-tools": "^3.1.0",
21
- "@myrmidon/ng-tools": "^3.1.1",
22
- "@myrmidon/gve-snapshot-view": "^0.0.19",
17
+ "@angular/common": "^19.0.0",
18
+ "@angular/core": "^19.0.0",
19
+ "@cisstech/nge": "^18.0.4",
20
+ "@myrmidon/ng-mat-tools": "^4.0.0",
21
+ "@myrmidon/ng-tools": "^4.0.0",
22
+ "@myrmidon/gve-snapshot-view": "^1.1.2",
23
23
  "@svgdotjs/svg.js": "^3.2.4",
24
24
  "gsap": "^3.12.5",
25
- "monaco-editor": "^0.50.0",
26
- "nanoid": "^5.0.7"
25
+ "monaco-editor": "^0.52.0",
26
+ "nanoid": "^5.0.8"
27
27
  },
28
28
  "dependencies": {
29
- "tslib": "^2.6.3"
29
+ "tslib": "^2.3.0"
30
30
  },
31
31
  "sideEffects": false,
32
32
  "module": "fesm2022/myrmidon-gve-core.mjs",
@@ -37,8 +37,6 @@
37
37
  },
38
38
  ".": {
39
39
  "types": "./index.d.ts",
40
- "esm2022": "./esm2022/myrmidon-gve-core.mjs",
41
- "esm": "./esm2022/myrmidon-gve-core.mjs",
42
40
  "default": "./fesm2022/myrmidon-gve-core.mjs"
43
41
  }
44
42
  }
package/public-api.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from './lib/components/animation-tween/animation-tween.component';
4
4
  export * from './lib/components/base-text-char/base-text-char.component';
5
5
  export * from './lib/components/base-text-editor/base-text-editor.component';
6
6
  export * from './lib/components/base-text-view/base-text-view.component';
7
+ export * from './lib/components/batch-operation-editor/batch-operation-editor.component';
7
8
  export * from './lib/components/chain-operation-editor/chain-operation-editor.component';
8
9
  export * from './lib/components/chain-result-view/chain-result-view.component';
9
10
  export * from './lib/components/feature-editor/feature-editor.component';
@@ -1,207 +0,0 @@
1
- import { Component, EventEmitter, Input, Output } from '@angular/core';
2
- import { ReactiveFormsModule, Validators, } from '@angular/forms';
3
- import { CommonModule } from '@angular/common';
4
- import { MatButtonModule } from '@angular/material/button';
5
- import { MatCheckboxModule } from '@angular/material/checkbox';
6
- import { MatExpansionModule } from '@angular/material/expansion';
7
- import { MatFormFieldModule } from '@angular/material/form-field';
8
- import { MatIconModule } from '@angular/material/icon';
9
- import { MatInputModule } from '@angular/material/input';
10
- import { MatSelectModule } from '@angular/material/select';
11
- import { MatTabsModule } from '@angular/material/tabs';
12
- import { MatTooltipModule } from '@angular/material/tooltip';
13
- import { NgToolsModule, NgToolsValidators } from '@myrmidon/ng-tools';
14
- import { AnimationTweenComponent } from '../animation-tween/animation-tween.component';
15
- import * as i0 from "@angular/core";
16
- import * as i1 from "@angular/forms";
17
- import * as i2 from "@angular/common";
18
- import * as i3 from "@angular/material/button";
19
- import * as i4 from "@angular/material/expansion";
20
- import * as i5 from "@angular/material/form-field";
21
- import * as i6 from "@angular/material/icon";
22
- import * as i7 from "@angular/material/input";
23
- import * as i8 from "@angular/material/select";
24
- import * as i9 from "@angular/material/core";
25
- import * as i10 from "@angular/material/tooltip";
26
- /**
27
- * 🔑 `gve-animation-timeline`
28
- *
29
- * A component to edit an animation timeline.
30
- * Used by the `gve-animation-timeline-set` component.
31
- *
32
- * - ▶️ `timeline` (`GveAnimationTimeline`): the animation timeline to edit.
33
- * - ▶️ `elementIds` (`string[]`): the IDs of the elements that can be selected
34
- * by the tween.
35
- * - ▶️ `tags` (`string[]`): the tags that can be used by the timeline.
36
- * - 🔥 `timelineChange` (`GveAnimationTimeline`): emitted when the timeline
37
- * is changed.
38
- * - 🔥 `timelineCancel` (`void`): emitted when the timeline editing is canceled.
39
- */
40
- export class AnimationTimelineComponent {
41
- /**
42
- * The animation timeline to edit.
43
- */
44
- get timeline() {
45
- return this._timeline;
46
- }
47
- set timeline(value) {
48
- this._timeline = value || undefined;
49
- this.updateForm(this._timeline);
50
- }
51
- constructor(formBuilder) {
52
- /**
53
- * The tags that can be used by the timeline.
54
- */
55
- this.tags = [];
56
- /**
57
- * Emitted when the timeline is changed.
58
- */
59
- this.timelineChange = new EventEmitter();
60
- /**
61
- * Emitted when the timeline editing is canceled.
62
- */
63
- this.timelineCancel = new EventEmitter();
64
- this.editedTweenIndex = -1;
65
- this.tag = formBuilder.control('', {
66
- nonNullable: true,
67
- validators: [Validators.required, Validators.maxLength(100)],
68
- });
69
- this.tweens = formBuilder.control([], {
70
- nonNullable: true,
71
- validators: NgToolsValidators.strictMinLengthValidator(1),
72
- });
73
- this.vars = formBuilder.control(null, this.jsonValidator);
74
- this.form = formBuilder.group({
75
- tag: this.tag,
76
- tweens: this.tweens,
77
- vars: this.vars,
78
- });
79
- }
80
- jsonValidator(control) {
81
- if (!control.value) {
82
- return null;
83
- }
84
- try {
85
- JSON.parse(control.value);
86
- return null;
87
- }
88
- catch (e) {
89
- return { json: true };
90
- }
91
- }
92
- updateForm(timeline) {
93
- if (!timeline) {
94
- this.form.reset();
95
- return;
96
- }
97
- this.tag.setValue(timeline.tag);
98
- this.tweens.setValue(timeline.tweens);
99
- this.vars.setValue(timeline.vars || null);
100
- this.form.markAsPristine();
101
- }
102
- addTween() {
103
- this.editedTweenIndex = -1;
104
- this.editedTween = {
105
- label: 'tween #' + (this.tweens.value.length + 1),
106
- type: 'to',
107
- selector: '',
108
- };
109
- }
110
- editTween(index) {
111
- this.editedTweenIndex = index;
112
- this.editedTween = this.tweens.value[index];
113
- }
114
- deleteTween(index) {
115
- this.tweens.setValue(this.tweens.value.filter((_, i) => i !== index));
116
- this.tweens.markAsDirty();
117
- this.tweens.updateValueAndValidity();
118
- }
119
- closeTween() {
120
- this.editedTween = undefined;
121
- this.editedTweenIndex = -1;
122
- }
123
- saveTween(tween) {
124
- if (this.editedTweenIndex === -1) {
125
- this.tweens.setValue([...this.tweens.value, tween]);
126
- }
127
- else {
128
- this.tweens.setValue(this.tweens.value.map((t, index) => index === this.editedTweenIndex ? tween : t));
129
- }
130
- this.tweens.markAsDirty();
131
- this.tweens.updateValueAndValidity();
132
- this.closeTween();
133
- }
134
- moveTweenUp(index) {
135
- if (index < 1) {
136
- return;
137
- }
138
- const tweens = [...this.tweens.value];
139
- const tmp = tweens[index];
140
- tweens[index] = tweens[index - 1];
141
- tweens[index - 1] = tmp;
142
- this.tweens.setValue(tweens);
143
- this.tweens.markAsDirty();
144
- this.tweens.updateValueAndValidity();
145
- }
146
- moveTweenDown(index) {
147
- if (index >= this.tweens.value.length - 1) {
148
- return;
149
- }
150
- const tweens = [...this.tweens.value];
151
- const tmp = tweens[index];
152
- tweens[index] = tweens[index + 1];
153
- tweens[index + 1] = tmp;
154
- this.tweens.setValue(tweens);
155
- this.tweens.markAsDirty();
156
- this.tweens.updateValueAndValidity();
157
- }
158
- getTimeline() {
159
- return {
160
- tag: this.tag.value || '',
161
- tweens: this.tweens.value,
162
- vars: this.vars.value || undefined,
163
- };
164
- }
165
- close() {
166
- this.timelineCancel.emit();
167
- }
168
- save() {
169
- if (this.form.invalid) {
170
- return;
171
- }
172
- this._timeline = this.getTimeline();
173
- this.timelineChange.emit(this._timeline);
174
- this.form.markAsPristine();
175
- }
176
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AnimationTimelineComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
177
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.8", type: AnimationTimelineComponent, isStandalone: true, selector: "gve-animation-timeline", inputs: { timeline: "timeline", elementIds: "elementIds", tags: "tags" }, outputs: { timelineChange: "timelineChange", timelineCancel: "timelineCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- tag (bound) -->\r\n @if (tags.length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags; track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n </mat-form-field>\r\n } @else {\r\n <!-- tag (free) -->\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <input matInput [formControl]=\"tag\" />\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.maxLength && (tag.dirty || tag.touched)\"\r\n >tag too long</mat-error\r\n >\r\n </mat-form-field>\r\n }\r\n\r\n <button\r\n mat-flat-button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addTween()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> tween\r\n </button>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n <mat-error *ngIf=\"$any(vars).errors?.json && (vars.dirty || vars.touched)\"\r\n >invalid JSON</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- tweens -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>label</th>\r\n <th>type</th>\r\n <th>selector</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of tweens.value; track t; let index = $index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"primary\"\r\n (click)=\"editTween(index)\"\r\n matTooltip=\"Edit tween\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"warn\"\r\n (click)=\"deleteTween(index)\"\r\n matTooltip=\"Delete tween\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n <!-- up -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenUp(index)\"\r\n matTooltip=\"Move tween up\"\r\n [disabled]=\"index === 0\"\r\n >\r\n <mat-icon>arrow_circle_up</mat-icon>\r\n </button>\r\n <!-- down -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenDown(index)\"\r\n matTooltip=\"Move tween down\"\r\n [disabled]=\"index === tweens.value.length - 1\"\r\n >\r\n <mat-icon>arrow_circle_down</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n {{ t.label }}\r\n </td>\r\n <td>\r\n {{ t.type }}\r\n </td>\r\n <td>\r\n {{ t.selector }}\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- tween editor -->\r\n <mat-expansion-panel [expanded]=\"editedTween\" [disabled]=\"!editedTween\">\r\n @if (editedTween) {\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>tween {{ editedTween.label }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n }\r\n <fieldset>\r\n <gve-animation-tween\r\n [elementIds]=\"elementIds\"\r\n [tween]=\"editedTween\"\r\n (tweenChange)=\"saveTween($event)\"\r\n (tweenCancel)=\"closeTween()\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n\r\n <!-- buttons -->\r\n <div class=\"button-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close timeline\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save timeline\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i5.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: NgToolsModule }, { kind: "component", type: AnimationTweenComponent, selector: "gve-animation-tween", inputs: ["tween", "elementIds"], outputs: ["tweenChange", "tweenCancel"] }] }); }
178
- }
179
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AnimationTimelineComponent, decorators: [{
180
- type: Component,
181
- args: [{ selector: 'gve-animation-timeline', standalone: true, imports: [
182
- CommonModule,
183
- ReactiveFormsModule,
184
- MatButtonModule,
185
- MatCheckboxModule,
186
- MatExpansionModule,
187
- MatFormFieldModule,
188
- MatIconModule,
189
- MatInputModule,
190
- MatSelectModule,
191
- MatTabsModule,
192
- MatTooltipModule,
193
- NgToolsModule,
194
- AnimationTweenComponent,
195
- ], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- tag (bound) -->\r\n @if (tags.length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags; track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n </mat-form-field>\r\n } @else {\r\n <!-- tag (free) -->\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <input matInput [formControl]=\"tag\" />\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.required && (tag.dirty || tag.touched)\"\r\n >tag required</mat-error\r\n >\r\n <mat-error\r\n *ngIf=\"$any(tag).errors?.maxLength && (tag.dirty || tag.touched)\"\r\n >tag too long</mat-error\r\n >\r\n </mat-form-field>\r\n }\r\n\r\n <button\r\n mat-flat-button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addTween()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> tween\r\n </button>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n <mat-error *ngIf=\"$any(vars).errors?.json && (vars.dirty || vars.touched)\"\r\n >invalid JSON</mat-error\r\n >\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- tweens -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>label</th>\r\n <th>type</th>\r\n <th>selector</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of tweens.value; track t; let index = $index) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"primary\"\r\n (click)=\"editTween(index)\"\r\n matTooltip=\"Edit tween\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"warn\"\r\n (click)=\"deleteTween(index)\"\r\n matTooltip=\"Delete tween\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n <!-- up -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenUp(index)\"\r\n matTooltip=\"Move tween up\"\r\n [disabled]=\"index === 0\"\r\n >\r\n <mat-icon>arrow_circle_up</mat-icon>\r\n </button>\r\n <!-- down -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenDown(index)\"\r\n matTooltip=\"Move tween down\"\r\n [disabled]=\"index === tweens.value.length - 1\"\r\n >\r\n <mat-icon>arrow_circle_down</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n {{ t.label }}\r\n </td>\r\n <td>\r\n {{ t.type }}\r\n </td>\r\n <td>\r\n {{ t.selector }}\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- tween editor -->\r\n <mat-expansion-panel [expanded]=\"editedTween\" [disabled]=\"!editedTween\">\r\n @if (editedTween) {\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>tween {{ editedTween.label }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n }\r\n <fieldset>\r\n <gve-animation-tween\r\n [elementIds]=\"elementIds\"\r\n [tween]=\"editedTween\"\r\n (tweenChange)=\"saveTween($event)\"\r\n (tweenCancel)=\"closeTween()\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n\r\n <!-- buttons -->\r\n <div class=\"button-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close timeline\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save timeline\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}\n"] }]
196
- }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { timeline: [{
197
- type: Input
198
- }], elementIds: [{
199
- type: Input
200
- }], tags: [{
201
- type: Input
202
- }], timelineChange: [{
203
- type: Output
204
- }], timelineCancel: [{
205
- type: Output
206
- }] } });
207
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5pbWF0aW9uLXRpbWVsaW5lLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL215cm1pZG9uL2d2ZS1jb3JlL3NyYy9saWIvY29tcG9uZW50cy9hbmltYXRpb24tdGltZWxpbmUvYW5pbWF0aW9uLXRpbWVsaW5lLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL215cm1pZG9uL2d2ZS1jb3JlL3NyYy9saWIvY29tcG9uZW50cy9hbmltYXRpb24tdGltZWxpbmUvYW5pbWF0aW9uLXRpbWVsaW5lLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdkUsT0FBTyxFQUtMLG1CQUFtQixFQUNuQixVQUFVLEdBQ1gsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzNELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQy9ELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ2xFLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN2RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDekQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzNELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN2RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwy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LDhCQUNqQixrQkFBa0IseWRBQ2xCLGtCQUFrQiwrZEFDbEIsYUFBYSxtTEFDYixjQUFjLDBXQUNkLGVBQWUsbXJCQUNmLGFBQWEsOEJBQ2IsZ0JBQWdCLDZUQUNoQixhQUFhLCtCQUNiLHVCQUF1Qjs7MkZBS2QsMEJBQTBCO2tCQXJCdEMsU0FBUzsrQkFDRSx3QkFBd0IsY0FDdEIsSUFBSSxXQUNQO3dCQUNQLFlBQVk7d0JBQ1osbUJBQW1CO3dCQUNuQixlQUFlO3dCQUNmLGlCQUFpQjt3QkFDakIsa0JBQWtCO3dCQUNsQixrQkFBa0I7d0JBQ2xCLGFBQWE7d0JBQ2IsY0FBYzt3QkFDZCxlQUFlO3dCQUNmLGFBQWE7d0JBQ2IsZ0JBQWdCO3dCQUNoQixhQUFhO3dCQUNiLHVCQUF1QjtxQkFDeEI7Z0ZBV1UsUUFBUTtzQkFEbEIsS0FBSztnQkFjQyxVQUFVO3NCQURoQixLQUFLO2dCQU9DLElBQUk7c0JBRFYsS0FBSztnQkFPVSxjQUFjO3NCQUQ3QixNQUFNO2dCQU9TLGNBQWM7c0JBRDdCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIEV2ZW50RW1pdHRlciwgSW5wdXQsIE91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQge1xyXG4gIEFic3RyYWN0Q29udHJvbCxcclxuICBGb3JtQnVpbGRlcixcclxuICBGb3JtQ29udHJvbCxcclxuICBGb3JtR3JvdXAsXHJcbiAgUmVhY3RpdmVGb3Jtc01vZHVsZSxcclxuICBWYWxpZGF0b3JzLFxyXG59IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcclxuXHJcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XHJcbmltcG9ydCB7IE1hdEJ1dHRvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2J1dHRvbic7XHJcbmltcG9ydCB7IE1hdENoZWNrYm94TW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvY2hlY2tib3gnO1xyXG5pbXBvcnQgeyBNYXRFeHBhbnNpb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9leHBhbnNpb24nO1xyXG5pbXBvcnQgeyBNYXRGb3JtRmllbGRNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9mb3JtLWZpZWxkJztcclxuaW1wb3J0IHsgTWF0SWNvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2ljb24nO1xyXG5pbXBvcnQgeyBNYXRJbnB1dE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2lucHV0JztcclxuaW1wb3J0IHsgTWF0U2VsZWN0TW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvc2VsZWN0JztcclxuaW1wb3J0IHsgTWF0VGFic01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3RhYnMnO1xyXG5pbXBvcnQgeyBNYXRUb29sdGlwTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvdG9vbHRpcCc7XHJcblxyXG5pbXBvcnQgeyBOZ1Rvb2xzTW9kdWxlLCBOZ1Rvb2xzVmFsaWRhdG9ycyB9IGZyb20gJ0BteXJtaWRvbi9uZy10b29scyc7XHJcbmltcG9ydCB7XHJcbiAgR3ZlQW5pbWF0aW9uVGltZWxpbmUsXHJcbiAgR3ZlQW5pbWF0aW9uVHdlZW4sXHJcbn0gZnJvbSAnQG15cm1pZG9uL2d2ZS1zbmFwc2hvdC12aWV3JztcclxuXHJcbmltcG9ydCB7IEFuaW1hdGlvblR3ZWVuQ29tcG9uZW50IH0gZnJvbSAnLi4vYW5pbWF0aW9uLXR3ZWVuL2FuaW1hdGlvbi10d2Vlbi5jb21wb25lbnQnO1xyXG5cclxuLyoqXHJcbiAqIPCflJEgYGd2ZS1hbmltYXRpb24tdGltZWxpbmVgXHJcbiAqXHJcbiAqIEEgY29tcG9uZW50IHRvIGVkaXQgYW4gYW5pbWF0aW9uIHRpbWVsaW5lLlxyXG4gKiBVc2VkIGJ5IHRoZSBgZ3ZlLWFuaW1hdGlvbi10aW1lbGluZS1zZXRgIGNvbXBvbmVudC5cclxuICpcclxuICogLSDilrbvuI8gYHRpbWVsaW5lYCAoYEd2ZUFuaW1hdGlvblRpbWVsaW5lYCk6IHRoZSBhbmltYXRpb24gdGltZWxpbmUgdG8gZWRpdC5cclxuICogLSDilrbvuI8gYGVsZW1lbnRJZHNgIChgc3RyaW5nW11gKTogdGhlIElEcyBvZiB0aGUgZWxlbWVudHMgdGhhdCBjYW4gYmUgc2VsZWN0ZWRcclxuICogYnkgdGhlIHR3ZWVuLlxyXG4gKiAtIOKWtu+4jyBgdGFnc2AgKGBzdHJpbmdbXWApOiB0aGUgdGFncyB0aGF0IGNhbiBiZSB1c2VkIGJ5IHRoZSB0aW1lbGluZS5cclxuICogLSDwn5SlIGB0aW1lbGluZUNoYW5nZWAgKGBHdmVBbmltYXRpb25UaW1lbGluZWApOiBlbWl0dGVkIHdoZW4gdGhlIHRpbWVsaW5lXHJcbiAqIGlzIGNoYW5nZWQuXHJcbiAqIC0g8J+UpSBgdGltZWxpbmVDYW5jZWxgIChgdm9pZGApOiBlbWl0dGVkIHdoZW4gdGhlIHRpbWVsaW5lIGVkaXRpbmcgaXMgY2FuY2VsZWQuXHJcbiAqL1xyXG5AQ29tcG9uZW50KHtcclxuICBzZWxlY3RvcjogJ2d2ZS1hbmltYXRpb24tdGltZWxpbmUnLFxyXG4gIHN0YW5kYWxvbmU6IHRydWUsXHJcbiAgaW1wb3J0czogW1xyXG4gICAgQ29tbW9uTW9kdWxlLFxyXG4gICAgUmVhY3RpdmVGb3Jtc01vZHVsZSxcclxuICAgIE1hdEJ1dHRvbk1vZHVsZSxcclxuICAgIE1hdENoZWNrYm94TW9kdWxlLFxyXG4gICAgTWF0RXhwYW5zaW9uTW9kdWxlLFxyXG4gICAgTWF0Rm9ybUZpZWxkTW9kdWxlLFxyXG4gICAgTWF0SWNvbk1vZHVsZSxcclxuICAgIE1hdElucHV0TW9kdWxlLFxyXG4gICAgTWF0U2VsZWN0TW9kdWxlLFxyXG4gICAgTWF0VGFic01vZHVsZSxcclxuICAgIE1hdFRvb2x0aXBNb2R1bGUsXHJcbiAgICBOZ1Rvb2xzTW9kdWxlLFxyXG4gICAgQW5pbWF0aW9uVHdlZW5Db21wb25lbnQsXHJcbiAgXSxcclxuICB0ZW1wbGF0ZVVybDogJy4vYW5pbWF0aW9uLXRpbWVsaW5lLmNvbXBvbmVudC5odG1sJyxcclxuICBzdHlsZVVybDogJy4vYW5pbWF0aW9uLXRpbWVsaW5lLmNvbXBvbmVudC5jc3MnLFxyXG59KVxyXG5leHBvcnQgY2xhc3MgQW5pbWF0aW9uVGltZWxpbmVDb21wb25lbnQge1xyXG4gIHByaXZhdGUgX3RpbWVsaW5lPzogR3ZlQW5pbWF0aW9uVGltZWxpbmU7XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoZSBhbmltYXRpb24gdGltZWxpbmUgdG8gZWRpdC5cclxuICAgKi9cclxuICBASW5wdXQoKVxyXG4gIHB1YmxpYyBnZXQgdGltZWxpbmUoKTogR3ZlQW5pbWF0aW9uVGltZWxpbmUgfCB1bmRlZmluZWQge1xyXG4gICAgcmV0dXJuIHRoaXMuX3RpbWVsaW5lO1xyXG4gIH1cclxuICBwdWJsaWMgc2V0IHRpbWVsaW5lKHZhbHVlOiBHdmVBbmltYXRpb25UaW1lbGluZSB8IHVuZGVmaW5lZCB8IG51bGwpIHtcclxuICAgIHRoaXMuX3RpbWVsaW5lID0gdmFsdWUgfHwgdW5kZWZpbmVkO1xyXG4gICAgdGhpcy51cGRhdGVGb3JtKHRoaXMuX3RpbWVsaW5lKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoZSBJRHMgb2YgdGhlIGVsZW1lbnRzIHRoYXQgY2FuIGJlIHNlbGVjdGVkIGJ5IHRoZSB0d2Vlbi5cclxuICAgKiBUaGlzIGxpc3QgaXMgdXNlZCB0byBhbGxvdyB0aGUgdXNlciB0byBzZWxlY3QgYW4gZWxlbWVudCBmcm9tIGEgZHJvcGRvd24uXHJcbiAgICovXHJcbiAgQElucHV0KClcclxuICBwdWJsaWMgZWxlbWVudElkcz86IHN0cmluZ1tdO1xyXG5cclxuICAvKipcclxuICAgKiBUaGUgdGFncyB0aGF0IGNhbiBiZSB1c2VkIGJ5IHRoZSB0aW1lbGluZS5cclxuICAgKi9cclxuICBASW5wdXQoKVxyXG4gIHB1YmxpYyB0YWdzOiBzdHJpbmdbXSA9IFtdO1xyXG5cclxuICAvKipcclxuICAgKiBFbWl0dGVkIHdoZW4gdGhlIHRpbWVsaW5lIGlzIGNoYW5nZWQuXHJcbiAgICovXHJcbiAgQE91dHB1dCgpXHJcbiAgcHVibGljIHJlYWRvbmx5IHRpbWVsaW5lQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxHdmVBbmltYXRpb25UaW1lbGluZT4oKTtcclxuXHJcbiAgLyoqXHJcbiAgICogRW1pdHRlZCB3aGVuIHRoZSB0aW1lbGluZSBlZGl0aW5nIGlzIGNhbmNlbGVkLlxyXG4gICAqL1xyXG4gIEBPdXRwdXQoKVxyXG4gIHB1YmxpYyByZWFkb25seSB0aW1lbGluZUNhbmNlbCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcclxuXHJcbiAgcHVibGljIHRhZzogRm9ybUNvbnRyb2w8c3RyaW5nPjtcclxuICBwdWJsaWMgdHdlZW5zOiBGb3JtQ29udHJvbDxHdmVBbmltYXRpb25Ud2VlbltdPjtcclxuICBwdWJsaWMgdmFyczogRm9ybUNvbnRyb2w8c3RyaW5nIHwgbnVsbD47XHJcbiAgcHVibGljIGZvcm06IEZvcm1Hcm91cDtcclxuXHJcbiAgcHVibGljIGVkaXRlZFR3ZWVuSW5kZXggPSAtMTtcclxuICBwdWJsaWMgZWRpdGVkVHdlZW4/OiBHdmVBbmltYXRpb25Ud2VlbjtcclxuXHJcbiAgY29uc3RydWN0b3IoZm9ybUJ1aWxkZXI6IEZvcm1CdWlsZGVyKSB7XHJcbiAgICB0aGlzLnRhZyA9IGZvcm1CdWlsZGVyLmNvbnRyb2woJycsIHtcclxuICAgICAgbm9uTnVsbGFibGU6IHRydWUsXHJcbiAgICAgIHZhbGlkYXRvcnM6IFtWYWxpZGF0b3JzLnJlcXVpcmVkLCBWYWxpZGF0b3JzLm1heExlbmd0aCgxMDApXSxcclxuICAgIH0pO1xyXG4gICAgdGhpcy50d2VlbnMgPSBmb3JtQnVpbGRlci5jb250cm9sKFtdLCB7XHJcbiAgICAgIG5vbk51bGxhYmxlOiB0cnVlLFxyXG4gICAgICB2YWxpZGF0b3JzOiBOZ1Rvb2xzVmFsaWRhdG9ycy5zdHJpY3RNaW5MZW5ndGhWYWxpZGF0b3IoMSksXHJcbiAgICB9KTtcclxuICAgIHRoaXMudmFycyA9IGZvcm1CdWlsZGVyLmNvbnRyb2wobnVsbCwgdGhpcy5qc29uVmFsaWRhdG9yKTtcclxuXHJcbiAgICB0aGlzLmZvcm0gPSBmb3JtQnVpbGRlci5ncm91cCh7XHJcbiAgICAgIHRhZzogdGhpcy50YWcsXHJcbiAgICAgIHR3ZWVuczogdGhpcy50d2VlbnMsXHJcbiAgICAgIHZhcnM6IHRoaXMudmFycyxcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBqc29uVmFsaWRhdG9yKFxyXG4gICAgY29udHJvbDogQWJzdHJhY3RDb250cm9sXHJcbiAgKTogeyBba2V5OiBzdHJpbmddOiBhbnkgfSB8IG51bGwge1xyXG4gICAgaWYgKCFjb250cm9sLnZhbHVlKSB7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG4gICAgdHJ5IHtcclxuICAgICAgSlNPTi5wYXJzZShjb250cm9sLnZhbHVlKTtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgIHJldHVybiB7IGpzb246IHRydWUgfTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHByaXZhdGUgdXBkYXRlRm9ybSh0aW1lbGluZTogR3ZlQW5pbWF0aW9uVGltZWxpbmUgfCB1bmRlZmluZWQpIHtcclxuICAgIGlmICghdGltZWxpbmUpIHtcclxuICAgICAgdGhpcy5mb3JtLnJlc2V0KCk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIHRoaXMudGFnLnNldFZhbHVlKHRpbWVsaW5lLnRhZyk7XHJcbiAgICB0aGlzLnR3ZWVucy5zZXRWYWx1ZSh0aW1lbGluZS50d2VlbnMpO1xyXG4gICAgdGhpcy52YXJzLnNldFZhbHVlKHRpbWVsaW5lLnZhcnMgfHwgbnVsbCk7XHJcbiAgICB0aGlzLmZvcm0ubWFya0FzUHJpc3RpbmUoKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBhZGRUd2VlbigpOiB2b2lkIHtcclxuICAgIHRoaXMuZWRpdGVkVHdlZW5JbmRleCA9IC0xO1xyXG4gICAgdGhpcy5lZGl0ZWRUd2VlbiA9IHtcclxuICAgICAgbGFiZWw6ICd0d2VlbiAjJyArICh0aGlzLnR3ZWVucy52YWx1ZS5sZW5ndGggKyAxKSxcclxuICAgICAgdHlwZTogJ3RvJyxcclxuICAgICAgc2VsZWN0b3I6ICcnLFxyXG4gICAgfTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBlZGl0VHdlZW4oaW5kZXg6IG51bWJlcik6IHZvaWQge1xyXG4gICAgdGhpcy5lZGl0ZWRUd2VlbkluZGV4ID0gaW5kZXg7XHJcbiAgICB0aGlzLmVkaXRlZFR3ZWVuID0gdGhpcy50d2VlbnMudmFsdWVbaW5kZXhdO1xyXG4gIH1cclxuXHJcbiAgcHVibGljIGRlbGV0ZVR3ZWVuKGluZGV4OiBudW1iZXIpOiB2b2lkIHtcclxuICAgIHRoaXMudHdlZW5zLnNldFZhbHVlKHRoaXMudHdlZW5zLnZhbHVlLmZpbHRlcigoXywgaSkgPT4gaSAhPT0gaW5kZXgpKTtcclxuICAgIHRoaXMudHdlZW5zLm1hcmtBc0RpcnR5KCk7XHJcbiAgICB0aGlzLnR3ZWVucy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCk7XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgY2xvc2VUd2VlbigpOiB2b2lkIHtcclxuICAgIHRoaXMuZWRpdGVkVHdlZW4gPSB1bmRlZmluZWQ7XHJcbiAgICB0aGlzLmVkaXRlZFR3ZWVuSW5kZXggPSAtMTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBzYXZlVHdlZW4odHdlZW46IEd2ZUFuaW1hdGlvblR3ZWVuKTogdm9pZCB7XHJcbiAgICBpZiAodGhpcy5lZGl0ZWRUd2VlbkluZGV4ID09PSAtMSkge1xyXG4gICAgICB0aGlzLnR3ZWVucy5zZXRWYWx1ZShbLi4udGhpcy50d2VlbnMudmFsdWUsIHR3ZWVuXSk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aGlzLnR3ZWVucy5zZXRWYWx1ZShcclxuICAgICAgICB0aGlzLnR3ZWVucy52YWx1ZS5tYXAoKHQsIGluZGV4KSA9PlxyXG4gICAgICAgICAgaW5kZXggPT09IHRoaXMuZWRpdGVkVHdlZW5JbmRleCA/IHR3ZWVuIDogdFxyXG4gICAgICAgIClcclxuICAgICAgKTtcclxuICAgIH1cclxuICAgIHRoaXMudHdlZW5zLm1hcmtBc0RpcnR5KCk7XHJcbiAgICB0aGlzLnR3ZWVucy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCk7XHJcbiAgICB0aGlzLmNsb3NlVHdlZW4oKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBtb3ZlVHdlZW5VcChpbmRleDogbnVtYmVyKTogdm9pZCB7XHJcbiAgICBpZiAoaW5kZXggPCAxKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIGNvbnN0IHR3ZWVucyA9IFsuLi50aGlzLnR3ZWVucy52YWx1ZV07XHJcbiAgICBjb25zdCB0bXAgPSB0d2VlbnNbaW5kZXhdO1xyXG4gICAgdHdlZW5zW2luZGV4XSA9IHR3ZWVuc1tpbmRleCAtIDFdO1xyXG4gICAgdHdlZW5zW2luZGV4IC0gMV0gPSB0bXA7XHJcblxyXG4gICAgdGhpcy50d2VlbnMuc2V0VmFsdWUodHdlZW5zKTtcclxuICAgIHRoaXMudHdlZW5zLm1hcmtBc0RpcnR5KCk7XHJcbiAgICB0aGlzLnR3ZWVucy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCk7XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgbW92ZVR3ZWVuRG93bihpbmRleDogbnVtYmVyKTogdm9pZCB7XHJcbiAgICBpZiAoaW5kZXggPj0gdGhpcy50d2VlbnMudmFsdWUubGVuZ3RoIC0gMSkge1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgICBjb25zdCB0d2VlbnMgPSBbLi4udGhpcy50d2VlbnMudmFsdWVdO1xyXG4gICAgY29uc3QgdG1wID0gdHdlZW5zW2luZGV4XTtcclxuICAgIHR3ZWVuc1tpbmRleF0gPSB0d2VlbnNbaW5kZXggKyAxXTtcclxuICAgIHR3ZWVuc1tpbmRleCArIDFdID0gdG1wO1xyXG5cclxuICAgIHRoaXMudHdlZW5zLnNldFZhbHVlKHR3ZWVucyk7XHJcbiAgICB0aGlzLnR3ZWVucy5tYXJrQXNEaXJ0eSgpO1xyXG4gICAgdGhpcy50d2VlbnMudXBkYXRlVmFsdWVBbmRWYWxpZGl0eSgpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBnZXRUaW1lbGluZSgpOiBHdmVBbmltYXRpb25UaW1lbGluZSB7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB0YWc6IHRoaXMudGFnLnZhbHVlIHx8ICcnLFxyXG4gICAgICB0d2VlbnM6IHRoaXMudHdlZW5zLnZhbHVlLFxyXG4gICAgICB2YXJzOiB0aGlzLnZhcnMudmFsdWUgfHwgdW5kZWZpbmVkLFxyXG4gICAgfTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBjbG9zZSgpOiB2b2lkIHtcclxuICAgIHRoaXMudGltZWxpbmVDYW5jZWwuZW1pdCgpO1xyXG4gIH1cclxuXHJcbiAgcHVibGljIHNhdmUoKTogdm9pZCB7XHJcbiAgICBpZiAodGhpcy5mb3JtLmludmFsaWQpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gICAgdGhpcy5fdGltZWxpbmUgPSB0aGlzLmdldFRpbWVsaW5lKCk7XHJcbiAgICB0aGlzLnRpbWVsaW5lQ2hhbmdlLmVtaXQodGhpcy5fdGltZWxpbmUpO1xyXG4gICAgdGhpcy5mb3JtLm1hcmtBc1ByaXN0aW5lKCk7XHJcbiAgfVxyXG59XHJcbiIsIjxmb3JtIFtmb3JtR3JvdXBdPVwiZm9ybVwiIChzdWJtaXQpPVwic2F2ZSgpXCI+XHJcbiAgPGRpdiBjbGFzcz1cImZvcm0tcm93XCI+XHJcbiAgICA8IS0tIHRhZyAoYm91bmQpIC0tPlxyXG4gICAgQGlmICh0YWdzLmxlbmd0aCkge1xyXG4gICAgPG1hdC1mb3JtLWZpZWxkPlxyXG4gICAgICA8bWF0LWxhYmVsPnRhZzwvbWF0LWxhYmVsPlxyXG4gICAgICA8bWF0LXNlbGVjdCBbZm9ybUNvbnRyb2xdPVwidGFnXCI+XHJcbiAgICAgICAgQGZvciAodCBvZiB0YWdzOyB0cmFjayB0KSB7XHJcbiAgICAgICAgPG1hdC1vcHRpb24gW3ZhbHVlXT1cInRcIj57eyB0IH19PC9tYXQtb3B0aW9uPlxyXG4gICAgICAgIH1cclxuICAgICAgPC9tYXQtc2VsZWN0PlxyXG4gICAgICA8bWF0LWVycm9yXHJcbiAgICAgICAgKm5nSWY9XCIkYW55KHRhZykuZXJyb3JzPy5yZXF1aXJlZCAmJiAodGFnLmRpcnR5IHx8IHRhZy50b3VjaGVkKVwiXHJcbiAgICAgICAgPnRhZyByZXF1aXJlZDwvbWF0LWVycm9yXHJcbiAgICAgID5cclxuICAgIDwvbWF0LWZvcm0tZmllbGQ+XHJcbiAgICB9IEBlbHNlIHtcclxuICAgIDwhLS0gdGFnIChmcmVlKSAtLT5cclxuICAgIDxtYXQtZm9ybS1maWVsZD5cclxuICAgICAgPG1hdC1sYWJlbD50YWc8L21hdC1sYWJlbD5cclxuICAgICAgPGlucHV0IG1hdElucHV0IFtmb3JtQ29udHJvbF09XCJ0YWdcIiAvPlxyXG4gICAgICA8bWF0LWVycm9yXHJcbiAgICAgICAgKm5nSWY9XCIkYW55KHRhZykuZXJyb3JzPy5yZXF1aXJlZCAmJiAodGFnLmRpcnR5IHx8IHRhZy50b3VjaGVkKVwiXHJcbiAgICAgICAgPnRhZyByZXF1aXJlZDwvbWF0LWVycm9yXHJcbiAgICAgID5cclxuICAgICAgPG1hdC1lcnJvclxyXG4gICAgICAgICpuZ0lmPVwiJGFueSh0YWcpLmVycm9ycz8ubWF4TGVuZ3RoICYmICh0YWcuZGlydHkgfHwgdGFnLnRvdWNoZWQpXCJcclxuICAgICAgICA+dGFnIHRvbyBsb25nPC9tYXQtZXJyb3JcclxuICAgICAgPlxyXG4gICAgPC9tYXQtZm9ybS1maWVsZD5cclxuICAgIH1cclxuXHJcbiAgICA8YnV0dG9uXHJcbiAgICAgIG1hdC1mbGF0LWJ1dHRvblxyXG4gICAgICB0eXBlPVwiYnV0dG9uXCJcclxuICAgICAgY29sb3I9XCJwcmltYXJ5XCJcclxuICAgICAgY2xhc3M9XCJtYXQtcHJpbWFyeVwiXHJcbiAgICAgIChjbGljayk9XCJhZGRUd2VlbigpXCJcclxuICAgID5cclxuICAgICAgPG1hdC1pY29uPmFkZF9jaXJjbGU8L21hdC1pY29uPiB0d2VlblxyXG4gICAgPC9idXR0b24+XHJcbiAgPC9kaXY+XHJcblxyXG4gIDwhLS0gdmFycyAtLT5cclxuICA8ZGl2PlxyXG4gICAgPG1hdC1mb3JtLWZpZWxkIGNsYXNzPVwibG9uZy10ZXh0XCI+XHJcbiAgICAgIDxtYXQtbGFiZWw+dmFyczwvbWF0LWxhYmVsPlxyXG4gICAgICA8dGV4dGFyZWEgbWF0SW5wdXQgW2Zvcm1Db250cm9sXT1cInZhcnNcIj48L3RleHRhcmVhPlxyXG4gICAgICA8bWF0LWhpbnQ+SlNPTiBvYmplY3Q8L21hdC1oaW50PlxyXG4gICAgICA8bWF0LWVycm9yICpuZ0lmPVwiJGFueSh2YXJzKS5lcnJvcnM/Lmpzb24gJiYgKHZhcnMuZGlydHkgfHwgdmFycy50b3VjaGVkKVwiXHJcbiAgICAgICAgPmludmFsaWQgSlNPTjwvbWF0LWVycm9yXHJcbiAgICAgID5cclxuICAgIDwvbWF0LWZvcm0tZmllbGQ+XHJcbiAgPC9kaXY+XHJcblxyXG4gIDwhLS0gdHdlZW5zIC0tPlxyXG4gIDx0YWJsZT5cclxuICAgIDx0aGVhZD5cclxuICAgICAgPHRyPlxyXG4gICAgICAgIDx0aD48L3RoPlxyXG4gICAgICAgIDx0aD5sYWJlbDwvdGg+XHJcbiAgICAgICAgPHRoPnR5cGU8L3RoPlxyXG4gICAgICAgIDx0aD5zZWxlY3RvcjwvdGg+XHJcbiAgICAgIDwvdHI+XHJcbiAgICA8L3RoZWFkPlxyXG4gICAgPHRib2R5PlxyXG4gICAgICBAZm9yICh0IG9mIHR3ZWVucy52YWx1ZTsgdHJhY2sgdDsgbGV0IGluZGV4ID0gJGluZGV4KSB7XHJcbiAgICAgIDx0cj5cclxuICAgICAgICA8dGQgY2xhc3M9XCJmaXQtd2lkdGhcIj5cclxuICAgICAgICAgIDwhLS0gZWRpdCAtLT5cclxuICAgICAgICAgIDxidXR0b25cclxuICAgICAgICAgICAgbWF0LWljb24tYnV0dG9uXHJcbiAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxyXG4gICAgICAgICAgICBjb2xvcj1cInByaW1hcnlcIlxyXG4gICAgICAgICAgICAoY2xpY2spPVwiZWRpdFR3ZWVuKGluZGV4KVwiXHJcbiAgICAgICAgICAgIG1hdFRvb2x0aXA9XCJFZGl0IHR3ZWVuXCJcclxuICAgICAgICAgID5cclxuICAgICAgICAgICAgPG1hdC1pY29uIGNsYXNzPVwibWF0LXByaW1hcnlcIj5lZGl0PC9tYXQtaWNvbj5cclxuICAgICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICAgICAgPCEtLSBkZWxldGUgLS0+XHJcbiAgICAgICAgICA8YnV0dG9uXHJcbiAgICAgICAgICAgIG1hdC1pY29uLWJ1dHRvblxyXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcclxuICAgICAgICAgICAgY29sb3I9XCJ3YXJuXCJcclxuICAgICAgICAgICAgKGNsaWNrKT1cImRlbGV0ZVR3ZWVuKGluZGV4KVwiXHJcbiAgICAgICAgICAgIG1hdFRvb2x0aXA9XCJEZWxldGUgdHdlZW5cIlxyXG4gICAgICAgICAgPlxyXG4gICAgICAgICAgICA8bWF0LWljb24gY2xhc3M9XCJtYXQtd2FyblwiPnJlbW92ZV9jaXJjbGU8L21hdC1pY29uPlxyXG4gICAgICAgICAgPC9idXR0b24+XHJcbiAgICAgICAgICA8IS0tIHVwIC0tPlxyXG4gICAgICAgICAgPGJ1dHRvblxyXG4gICAgICAgICAgICBtYXQtaWNvbi1idXR0b25cclxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXHJcbiAgICAgICAgICAgIChjbGljayk9XCJtb3ZlVHdlZW5VcChpbmRleClcIlxyXG4gICAgICAgICAgICBtYXRUb29sdGlwPVwiTW92ZSB0d2VlbiB1cFwiXHJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCJpbmRleCA9PT0gMFwiXHJcbiAgICAgICAgICA+XHJcbiAgICAgICAgICAgIDxtYXQtaWNvbj5hcnJvd19jaXJjbGVfdXA8L21hdC1pY29uPlxyXG4gICAgICAgICAgPC9idXR0b24+XHJcbiAgICAgICAgICA8IS0tIGRvd24gLS0+XHJcbiAgICAgICAgICA8YnV0dG9uXHJcbiAgICAgICAgICAgIG1hdC1pY29uLWJ1dHRvblxyXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcclxuICAgICAgICAgICAgKGNsaWNrKT1cIm1vdmVUd2VlbkRvd24oaW5kZXgpXCJcclxuICAgICAgICAgICAgbWF0VG9vbHRpcD1cIk1vdmUgdHdlZW4gZG93blwiXHJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCJpbmRleCA9PT0gdHdlZW5zLnZhbHVlLmxlbmd0aCAtIDFcIlxyXG4gICAgICAgICAgPlxyXG4gICAgICAgICAgICA8bWF0LWljb24+YXJyb3dfY2lyY2xlX2Rvd248L21hdC1pY29uPlxyXG4gICAgICAgICAgPC9idXR0b24+XHJcbiAgICAgICAgPC90ZD5cclxuICAgICAgICA8dGQ+XHJcbiAgICAgICAgICB7eyB0LmxhYmVsIH19XHJcbiAgICAgICAgPC90ZD5cclxuICAgICAgICA8dGQ+XHJcbiAgICAgICAgICB7eyB0LnR5cGUgfX1cclxuICAgICAgICA8L3RkPlxyXG4gICAgICAgIDx0ZD5cclxuICAgICAgICAgIHt7IHQuc2VsZWN0b3IgfX1cclxuICAgICAgICA8L3RkPlxyXG4gICAgICA8L3RyPlxyXG4gICAgICB9XHJcbiAgICA8L3Rib2R5PlxyXG4gIDwvdGFibGU+XHJcblxyXG4gIDwhLS0gdHdlZW4gZWRpdG9yIC0tPlxyXG4gIDxtYXQtZXhwYW5zaW9uLXBhbmVsIFtleHBhbmRlZF09XCJlZGl0ZWRUd2VlblwiIFtkaXNhYmxlZF09XCIhZWRpdGVkVHdlZW5cIj5cclxuICAgIEBpZiAoZWRpdGVkVHdlZW4pIHtcclxuICAgIDxtYXQtZXhwYW5zaW9uLXBhbmVsLWhlYWRlcj5cclxuICAgICAgPG1hdC1wYW5lbC10aXRsZT50d2VlbiB7eyBlZGl0ZWRUd2Vlbi5sYWJlbCB9fTwvbWF0LXBhbmVsLXRpdGxlPlxyXG4gICAgPC9tYXQtZXhwYW5zaW9uLXBhbmVsLWhlYWRlcj5cclxuICAgIH1cclxuICAgIDxmaWVsZHNldD5cclxuICAgICAgPGd2ZS1hbmltYXRpb24tdHdlZW5cclxuICAgICAgICBbZWxlbWVudElkc109XCJlbGVtZW50SWRzXCJcclxuICAgICAgICBbdHdlZW5dPVwiZWRpdGVkVHdlZW5cIlxyXG4gICAgICAgICh0d2VlbkNoYW5nZSk9XCJzYXZlVHdlZW4oJGV2ZW50KVwiXHJcbiAgICAgICAgKHR3ZWVuQ2FuY2VsKT1cImNsb3NlVHdlZW4oKVwiXHJcbiAgICAgIC8+XHJcbiAgICA8L2ZpZWxkc2V0PlxyXG4gIDwvbWF0LWV4cGFuc2lvbi1wYW5lbD5cclxuXHJcbiAgPCEtLSBidXR0b25zIC0tPlxyXG4gIDxkaXYgY2xhc3M9XCJidXR0b24tcm93XCI+XHJcbiAgICA8YnV0dG9uXHJcbiAgICAgIHR5cGU9XCJidXR0b25cIlxyXG4gICAgICBjbGFzcz1cIm1hdC13YXJuXCJcclxuICAgICAgbWF0LWljb24tYnV0dG9uXHJcbiAgICAgIG1hdFRvb2x0aXA9XCJDbG9zZSB0aW1lbGluZVwiXHJcbiAgICAgIChjbGljayk9XCJjbG9zZSgpXCJcclxuICAgID5cclxuICAgICAgPG1hdC1pY29uIGNsYXNzPVwibWF0LXdhcm5cIj5jbGVhcjwvbWF0LWljb24+XHJcbiAgICA8L2J1dHRvbj5cclxuICAgIDxidXR0b25cclxuICAgICAgdHlwZT1cInN1Ym1pdFwiXHJcbiAgICAgIGNsYXNzPVwibWF0LXByaW1hcnlcIlxyXG4gICAgICBtYXQtZmxhdC1idXR0b25cclxuICAgICAgbWF0VG9vbHRpcD1cIlNhdmUgdGltZWxpbmVcIlxyXG4gICAgICBbZGlzYWJsZWRdPVwiZm9ybS5pbnZhbGlkXCJcclxuICAgID5cclxuICAgICAgPG1hdC1pY29uIGNsYXNzPVwibWF0LXByaW1hcnlcIj5jaGVja19jaXJjbGU8L21hdC1pY29uPlxyXG4gICAgICB0aW1lbGluZVxyXG4gICAgPC9idXR0b24+XHJcbiAgPC9kaXY+XHJcbjwvZm9ybT5cclxuIl19