tas-uell-sdk 0.0.5 → 0.1.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.
- package/README.md +161 -51
- package/esm2020/lib/components/tas-avatar/tas-avatar.component.mjs +75 -0
- package/esm2020/lib/components/tas-btn/tas-btn.component.mjs +146 -61
- package/esm2020/lib/components/tas-floating-call/tas-floating-call.component.mjs +48 -23
- package/esm2020/lib/components/tas-incoming-appointment/tas-incoming-appointment.component.mjs +109 -0
- package/esm2020/lib/components/tas-videocall/tas-videocall.component.mjs +217 -20
- package/esm2020/lib/components/tas-waiting-room/tas-waiting-room.component.mjs +226 -160
- package/esm2020/lib/config/tas.config.mjs +1 -1
- package/esm2020/lib/interfaces/tas.interfaces.mjs +45 -2
- package/esm2020/lib/services/geolocation.service.mjs +56 -0
- package/esm2020/lib/services/tas.service.mjs +400 -34
- package/esm2020/lib/tas-uell-sdk.module.mjs +25 -21
- package/esm2020/public-api.mjs +4 -1
- package/fesm2015/tas-uell-sdk.mjs +1323 -302
- package/fesm2015/tas-uell-sdk.mjs.map +1 -1
- package/fesm2020/tas-uell-sdk.mjs +1307 -300
- package/fesm2020/tas-uell-sdk.mjs.map +1 -1
- package/lib/components/tas-avatar/tas-avatar.component.d.ts +9 -0
- package/lib/components/tas-btn/tas-btn.component.d.ts +35 -15
- package/lib/components/tas-floating-call/tas-floating-call.component.d.ts +5 -1
- package/lib/components/tas-incoming-appointment/tas-incoming-appointment.component.d.ts +33 -0
- package/lib/components/tas-videocall/tas-videocall.component.d.ts +49 -3
- package/lib/components/tas-waiting-room/tas-waiting-room.component.d.ts +50 -35
- package/lib/config/tas.config.d.ts +7 -0
- package/lib/interfaces/tas.interfaces.d.ts +127 -35
- package/lib/services/geolocation.service.d.ts +24 -0
- package/lib/services/tas.service.d.ts +98 -9
- package/lib/tas-uell-sdk.module.d.ts +6 -3
- package/package.json +1 -1
- package/public-api.d.ts +3 -0
- package/src/lib/styles/tas-global.scss +27 -28
- package/INSTALL_AND_TEST.md +0 -427
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ npm install @ng-bootstrap/ng-bootstrap @opentok/client interactjs
|
|
|
29
29
|
|
|
30
30
|
### 1. Create an HTTP Adapter
|
|
31
31
|
|
|
32
|
-
The library requires an HTTP adapter that implements `TasHttpClient` interface:
|
|
32
|
+
The library requires an HTTP adapter that implements the `TasHttpClient` interface:
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
35
|
import { Injectable } from '@angular/core';
|
|
@@ -41,9 +41,21 @@ import { TasHttpClient } from 'tas-uell-sdk';
|
|
|
41
41
|
export class TasHttpAdapterService implements TasHttpClient {
|
|
42
42
|
constructor(private http: HttpClient) {}
|
|
43
43
|
|
|
44
|
+
get<T>(url: string, options: { headers?: Record<string, string> }): Observable<T> {
|
|
45
|
+
return this.http.get<T>(`https://your-api.com/${url}`, {
|
|
46
|
+
headers: options.headers,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
post<T>(url: string, options: { body: any; headers?: Record<string, string> }): Observable<T> {
|
|
45
|
-
return this.http.post<T>(`https://your-api.com/${url}`, options.body, {
|
|
46
|
-
headers: options.headers
|
|
51
|
+
return this.http.post<T>(`https://your-api.com/${url}`, options.body, {
|
|
52
|
+
headers: options.headers,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
patch<T>(url: string, options: { body: any; headers?: Record<string, string> }): Observable<T> {
|
|
57
|
+
return this.http.patch<T>(`https://your-api.com/${url}`, options.body, {
|
|
58
|
+
headers: options.headers,
|
|
47
59
|
});
|
|
48
60
|
}
|
|
49
61
|
}
|
|
@@ -60,22 +72,26 @@ import { TasHttpAdapterService } from './adapters/tas-http-adapter.service';
|
|
|
60
72
|
@NgModule({
|
|
61
73
|
imports: [
|
|
62
74
|
TasUellSdkModule.forRoot({
|
|
63
|
-
config: {
|
|
64
|
-
tokBoxApiKey: 'YOUR_TOKBOX_API_KEY'
|
|
75
|
+
config: {
|
|
76
|
+
tokBoxApiKey: 'YOUR_TOKBOX_API_KEY',
|
|
77
|
+
// apiBaseUrl: 'https://your-api.com/v2', // Optional
|
|
65
78
|
},
|
|
66
|
-
httpClient: TasHttpAdapterService
|
|
67
|
-
})
|
|
68
|
-
]
|
|
79
|
+
httpClient: TasHttpAdapterService,
|
|
80
|
+
}),
|
|
81
|
+
],
|
|
69
82
|
})
|
|
70
|
-
export class AppModule {
|
|
83
|
+
export class AppModule {}
|
|
71
84
|
```
|
|
72
85
|
|
|
73
86
|
### 3. Add Global Styles
|
|
74
87
|
|
|
75
|
-
Add the TAS modal styles to your global `styles.scss
|
|
88
|
+
Add the TAS modal styles to your global `styles.scss`. You can either import the library's global styles or copy the CSS below:
|
|
76
89
|
|
|
77
90
|
```scss
|
|
78
|
-
/*
|
|
91
|
+
/* Option 1: Import from library */
|
|
92
|
+
@import 'tas-uell-sdk/src/lib/styles/tas-global.scss';
|
|
93
|
+
|
|
94
|
+
/* Option 2: Manual styles */
|
|
79
95
|
.tas-video-modal {
|
|
80
96
|
.modal-dialog {
|
|
81
97
|
max-width: 100vw;
|
|
@@ -93,7 +109,6 @@ Add the TAS modal styles to your global `styles.scss`:
|
|
|
93
109
|
}
|
|
94
110
|
}
|
|
95
111
|
|
|
96
|
-
/* TAS Waiting Room Modal */
|
|
97
112
|
.tas-waiting-room-modal {
|
|
98
113
|
.modal-dialog {
|
|
99
114
|
max-width: 480px;
|
|
@@ -122,76 +137,171 @@ Add `<tas-floating-call>` to your root component template (e.g., `app.component.
|
|
|
122
137
|
|
|
123
138
|
## Usage
|
|
124
139
|
|
|
140
|
+
> **Note:** The appointment/room must be pre-created in your backend before using this library.
|
|
141
|
+
|
|
125
142
|
### TAS Button Component
|
|
126
143
|
|
|
127
144
|
Use the `<tas-btn>` component to initiate a video call:
|
|
128
145
|
|
|
129
146
|
```html
|
|
130
147
|
<tas-btn
|
|
131
|
-
[
|
|
132
|
-
[
|
|
133
|
-
[
|
|
148
|
+
[roomType]="'TAS'"
|
|
149
|
+
[entityId]="appointment.entityId"
|
|
150
|
+
[tenant]="tenantId"
|
|
151
|
+
[businessRole]="'BACKOFFICE'"
|
|
134
152
|
[currentUser]="currentUser"
|
|
135
|
-
[ownerUserIds]="[ownerId]"
|
|
136
|
-
[regularUserIds]="regularUserIds"
|
|
137
|
-
[moderatorUserIds]="moderatorUserIds"
|
|
138
153
|
></tas-btn>
|
|
139
154
|
```
|
|
140
155
|
|
|
141
156
|
### Input Properties
|
|
142
157
|
|
|
143
|
-
| Property | Type | Required | Description |
|
|
144
|
-
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
150
|
-
|
|
151
|
-
|
|
158
|
+
| Property | Type | Required | Default | Description |
|
|
159
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
160
|
+
| `entityId` | `number` | Yes | - | The entity/ausencia ID |
|
|
161
|
+
| `tenant` | `string` | Yes | - | Tenant identifier |
|
|
162
|
+
| `roomType` | `TasRoomType` | No | `'TAS'` | Room type (TAS, JM, WEBINAR, WELLNESS_MANAGER) |
|
|
163
|
+
| `businessRole` | `TasBusinessRole` | No | `'USER'` | Role (ADMIN_MANAGER, MANAGER, BACKOFFICE, USER) |
|
|
164
|
+
| `currentUser` | `TasCurrentUser` | Yes | - | Current user info (name, lastname, role) |
|
|
165
|
+
|
|
166
|
+
### Waiting Room Behavior
|
|
167
|
+
|
|
168
|
+
When clicking the TAS button, a waiting room modal opens:
|
|
169
|
+
|
|
170
|
+
1. **Status Check**: The library calls `/v2/proxy/video/status` to get session info
|
|
171
|
+
2. **Token Acquisition**: Calls `/v2/proxy/video/start` to get the video call token
|
|
152
172
|
|
|
153
|
-
|
|
173
|
+
**Owner/Backoffice Users** (`businessRole: 'BACKOFFICE'` or user with `role: 'OWNER'`):
|
|
174
|
+
- Token is obtained immediately after status check
|
|
175
|
+
- Once token is received, the "Join" button appears
|
|
176
|
+
- If `/start` fails, an error screen is shown with a "Retry" button
|
|
154
177
|
|
|
178
|
+
**Non-Owner Users** (regular attendees):
|
|
179
|
+
- See "Medicina laboral te va a admitir en unos instantes..."
|
|
180
|
+
- Wait until the backend marks the session as `joinable: true`
|
|
181
|
+
- Once joinable, the token is obtained and the "Join" button appears
|
|
182
|
+
|
|
183
|
+
### Error Handling
|
|
184
|
+
|
|
185
|
+
The library handles errors gracefully:
|
|
186
|
+
- If `/status` fails: Shows error with retry option
|
|
187
|
+
- If `/start` fails: Shows error message from backend, stops polling, shows retry option
|
|
188
|
+
- Network errors: Displays user-friendly error message
|
|
189
|
+
|
|
190
|
+
## Interfaces & Enums
|
|
191
|
+
|
|
192
|
+
### TasCurrentUser
|
|
155
193
|
```typescript
|
|
156
194
|
interface TasCurrentUser {
|
|
195
|
+
id: number;
|
|
157
196
|
name: string;
|
|
158
197
|
lastname: string;
|
|
159
|
-
role: TasUserRole;
|
|
198
|
+
role: TasUserRole;
|
|
160
199
|
}
|
|
161
200
|
```
|
|
162
201
|
|
|
163
|
-
|
|
202
|
+
### TasCallConfig
|
|
203
|
+
```typescript
|
|
204
|
+
interface TasCallConfig {
|
|
205
|
+
roomType: TasRoomType;
|
|
206
|
+
entityId: number;
|
|
207
|
+
tenant: string;
|
|
208
|
+
businessRole: TasBusinessRole;
|
|
209
|
+
currentUser: TasCurrentUser;
|
|
210
|
+
}
|
|
211
|
+
```
|
|
164
212
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
213
|
+
### TasAppointment
|
|
214
|
+
```typescript
|
|
215
|
+
interface TasAppointment {
|
|
216
|
+
id: number;
|
|
217
|
+
agendaId: number;
|
|
218
|
+
date: string; // "YYYY-MM-DD"
|
|
219
|
+
startTime: string; // "HH:mm"
|
|
220
|
+
endTime: string; // "HH:mm"
|
|
221
|
+
bookingType: string;
|
|
222
|
+
status: AppointmentStatus;
|
|
223
|
+
title: string;
|
|
224
|
+
notes: string;
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Enums
|
|
229
|
+
- **TasUserRole**: `OWNER`, `USER`, `MODERATOR`
|
|
230
|
+
- **TasBusinessRole**: `ADMIN_MANAGER`, `MANAGER`, `BACKOFFICE`, `USER`
|
|
231
|
+
- **TasRoomType**: `TAS`, `JM`, `WEBINAR`, `WELLNESS_MANAGER`
|
|
232
|
+
- **CallState**: `IDLE`, `CONNECTING`, `CONNECTED`, `DISCONNECTED`, `ERROR`
|
|
233
|
+
- **ViewMode**: `FULLSCREEN`, `PIP`
|
|
234
|
+
- **AppointmentStatus**: `CONFIRMED`, `CANCELLED`
|
|
169
235
|
|
|
170
236
|
## Exported Services
|
|
171
237
|
|
|
172
|
-
|
|
238
|
+
### TasService
|
|
239
|
+
Core service for managing video sessions and state.
|
|
173
240
|
|
|
174
|
-
|
|
241
|
+
**Public API:**
|
|
242
|
+
- `callState$: Observable<CallState>` - Current connection status
|
|
243
|
+
- `viewMode$: Observable<ViewMode>` - Current view mode
|
|
244
|
+
- `isMuted$: Observable<boolean>` - Local audio mute state
|
|
245
|
+
- `joinable$: Observable<boolean>` - Whether the call is joinable
|
|
246
|
+
- `ownerHasJoined$: Observable<boolean>` - Whether an owner has joined
|
|
247
|
+
- `toggleMute()` - Toggles local audio
|
|
248
|
+
- `disconnectSession()` - Ends the current session
|
|
249
|
+
- `getProxyVideoStatus(params)` - Gets session status
|
|
250
|
+
- `getAppointments(params)` - Gets user appointments within date range
|
|
175
251
|
|
|
176
|
-
|
|
177
|
-
- `TasHttpClient`
|
|
178
|
-
- `TasCurrentUser`
|
|
179
|
-
- `CreateRoomRequest`
|
|
180
|
-
- `CreateRoomResponse`
|
|
181
|
-
- `GenerateTokenRequest`
|
|
182
|
-
- `GenerateTokenResponse`
|
|
183
|
-
- `TasRoomType`
|
|
184
|
-
- `TasSessionType`
|
|
185
|
-
- `TasUserRole`
|
|
186
|
-
- `CallState`
|
|
187
|
-
- `ViewMode`
|
|
252
|
+
## Exported Components
|
|
188
253
|
|
|
189
|
-
|
|
254
|
+
### TasButtonComponent - `<tas-btn>`
|
|
190
255
|
|
|
191
|
-
|
|
192
|
-
|
|
256
|
+
Initiates video calls. Supports style variants:
|
|
257
|
+
|
|
258
|
+
```html
|
|
259
|
+
<!-- Default (pink) -->
|
|
260
|
+
<tas-btn [entityId]="123" [tenant]="'tenant'" [currentUser]="user"></tas-btn>
|
|
261
|
+
|
|
262
|
+
<!-- Teal variant with custom label -->
|
|
263
|
+
<tas-btn variant="teal" buttonLabel="Ingresar" [entityId]="123" [tenant]="'tenant'" [currentUser]="user"></tas-btn>
|
|
193
264
|
```
|
|
194
265
|
|
|
266
|
+
| Property | Type | Default | Description |
|
|
267
|
+
| :--- | :--- | :--- | :--- |
|
|
268
|
+
| `variant` | `'default' \| 'teal'` | `'default'` | Button style variant |
|
|
269
|
+
| `buttonLabel` | `string` | `'Iniciar TAS'` | Button text |
|
|
270
|
+
|
|
271
|
+
### TasIncomingAppointmentComponent - `<tas-incoming-appointment>`
|
|
272
|
+
|
|
273
|
+
Displays the user's next scheduled appointment or an empty state. Uses `<tas-btn>` internally.
|
|
274
|
+
|
|
275
|
+
```html
|
|
276
|
+
<tas-incoming-appointment
|
|
277
|
+
[entityId]="123"
|
|
278
|
+
[tenant]="'tenant'"
|
|
279
|
+
[currentUser]="user"
|
|
280
|
+
[businessRole]="'USER'"
|
|
281
|
+
(enterCall)="onEnterCall($event)"
|
|
282
|
+
></tas-incoming-appointment>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
| Property | Type | Required | Default | Description |
|
|
286
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
287
|
+
| `entityId` | `number` | Yes | - | Entity/ausencia ID |
|
|
288
|
+
| `tenant` | `string` | Yes | - | Tenant identifier |
|
|
289
|
+
| `currentUser` | `TasCurrentUser` | Yes | - | Current user info |
|
|
290
|
+
| `roomType` | `TasRoomType` | No | `'TAS'` | Room type |
|
|
291
|
+
| `businessRole` | `TasBusinessRole` | No | `'USER'` | User's business role |
|
|
292
|
+
|
|
293
|
+
| Output | Type | Description |
|
|
294
|
+
| :--- | :--- | :--- |
|
|
295
|
+
| `enterCall` | `EventEmitter<TasAppointment>` | Emits appointment when clicking "Ingresar" |
|
|
296
|
+
|
|
297
|
+
**Note:** Requires the `TasHttpClient` adapter to implement the `get()` method.
|
|
298
|
+
|
|
299
|
+
### Other Components
|
|
300
|
+
- `TasVideocallComponent` - Full-screen video call interface
|
|
301
|
+
- `TasWaitingRoomComponent` - Pre-call waiting room
|
|
302
|
+
- `TasFloatingCallComponent` - `<tas-floating-call>`
|
|
303
|
+
- `TasAvatarComponent` - `<tas-avatar>`
|
|
304
|
+
|
|
195
305
|
## License
|
|
196
306
|
|
|
197
307
|
MIT
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export class TasAvatarComponent {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.name = '';
|
|
6
|
+
this.size = 80;
|
|
7
|
+
}
|
|
8
|
+
get initials() {
|
|
9
|
+
if (!this.name)
|
|
10
|
+
return '';
|
|
11
|
+
return this.name
|
|
12
|
+
.split(' ')
|
|
13
|
+
.filter((n) => n.length > 0)
|
|
14
|
+
.map((n) => n[0])
|
|
15
|
+
.join('')
|
|
16
|
+
.toUpperCase()
|
|
17
|
+
.substring(0, 2);
|
|
18
|
+
}
|
|
19
|
+
get fontSize() {
|
|
20
|
+
return Math.round(this.size * 0.4);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
TasAvatarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
24
|
+
TasAvatarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasAvatarComponent, selector: "tas-avatar", inputs: { name: "name", size: "size" }, ngImport: i0, template: `
|
|
25
|
+
<div
|
|
26
|
+
class="avatar"
|
|
27
|
+
[style.width.px]="size"
|
|
28
|
+
[style.height.px]="size"
|
|
29
|
+
[style.fontSize.px]="fontSize"
|
|
30
|
+
>
|
|
31
|
+
<span class="initials">{{ initials }}</span>
|
|
32
|
+
</div>
|
|
33
|
+
`, isInline: true, styles: [".avatar{display:flex;align-items:center;justify-content:center;border-radius:50%;background-color:#fff;color:#0072ac;font-weight:600;font-family:inherit;box-shadow:0 4px 12px #00000026}.initials{text-transform:uppercase;-webkit-user-select:none;user-select:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasAvatarComponent, decorators: [{
|
|
35
|
+
type: Component,
|
|
36
|
+
args: [{
|
|
37
|
+
selector: 'tas-avatar',
|
|
38
|
+
template: `
|
|
39
|
+
<div
|
|
40
|
+
class="avatar"
|
|
41
|
+
[style.width.px]="size"
|
|
42
|
+
[style.height.px]="size"
|
|
43
|
+
[style.fontSize.px]="fontSize"
|
|
44
|
+
>
|
|
45
|
+
<span class="initials">{{ initials }}</span>
|
|
46
|
+
</div>
|
|
47
|
+
`,
|
|
48
|
+
styles: [
|
|
49
|
+
`
|
|
50
|
+
.avatar {
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
border-radius: 50%;
|
|
55
|
+
background-color: #ffffff;
|
|
56
|
+
color: #0072ac;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
font-family: inherit;
|
|
59
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.initials {
|
|
63
|
+
text-transform: uppercase;
|
|
64
|
+
user-select: none;
|
|
65
|
+
}
|
|
66
|
+
`,
|
|
67
|
+
],
|
|
68
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
69
|
+
}]
|
|
70
|
+
}], propDecorators: { name: [{
|
|
71
|
+
type: Input
|
|
72
|
+
}], size: [{
|
|
73
|
+
type: Input
|
|
74
|
+
}] } });
|
|
75
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFzLWF2YXRhci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90YXMtdWVsbC1zZGsvc3JjL2xpYi9jb21wb25lbnRzL3Rhcy1hdmF0YXIvdGFzLWF2YXRhci5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxlQUFlLENBQUM7O0FBb0MxRSxNQUFNLE9BQU8sa0JBQWtCO0lBbEMvQjtRQW1DVyxTQUFJLEdBQVcsRUFBRSxDQUFDO1FBQ2xCLFNBQUksR0FBVyxFQUFFLENBQUM7S0FnQjVCO0lBZEMsSUFBSSxRQUFRO1FBQ1YsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDMUIsT0FBTyxJQUFJLENBQUMsSUFBSTthQUNiLEtBQUssQ0FBQyxHQUFHLENBQUM7YUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO2FBQzNCLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ2hCLElBQUksQ0FBQyxFQUFFLENBQUM7YUFDUixXQUFXLEVBQUU7YUFDYixTQUFTLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3JCLENBQUM7SUFFRCxJQUFJLFFBQVE7UUFDVixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztJQUNyQyxDQUFDOztnSEFqQlUsa0JBQWtCO29HQUFsQixrQkFBa0IsMEZBaENuQjs7Ozs7Ozs7O0dBU1Q7NEZBdUJVLGtCQUFrQjtrQkFsQzlCLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLFlBQVk7b0JBQ3RCLFFBQVEsRUFBRTs7Ozs7Ozs7O0dBU1Q7b0JBQ0QsTUFBTSxFQUFFO3dCQUNOOzs7Ozs7Ozs7Ozs7Ozs7OztLQWlCQztxQkFDRjtvQkFDRCxlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtpQkFDaEQ7OEJBRVUsSUFBSTtzQkFBWixLQUFLO2dCQUNHLElBQUk7c0JBQVosS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgSW5wdXQsIENoYW5nZURldGVjdGlvblN0cmF0ZWd5IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3Rhcy1hdmF0YXInLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXZcbiAgICAgIGNsYXNzPVwiYXZhdGFyXCJcbiAgICAgIFtzdHlsZS53aWR0aC5weF09XCJzaXplXCJcbiAgICAgIFtzdHlsZS5oZWlnaHQucHhdPVwic2l6ZVwiXG4gICAgICBbc3R5bGUuZm9udFNpemUucHhdPVwiZm9udFNpemVcIlxuICAgID5cbiAgICAgIDxzcGFuIGNsYXNzPVwiaW5pdGlhbHNcIj57eyBpbml0aWFscyB9fTwvc3Bhbj5cbiAgICA8L2Rpdj5cbiAgYCxcbiAgc3R5bGVzOiBbXG4gICAgYFxuICAgICAgLmF2YXRhciB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgIGJhY2tncm91bmQtY29sb3I6ICNmZmZmZmY7XG4gICAgICAgIGNvbG9yOiAjMDA3MmFjO1xuICAgICAgICBmb250LXdlaWdodDogNjAwO1xuICAgICAgICBmb250LWZhbWlseTogaW5oZXJpdDtcbiAgICAgICAgYm94LXNoYWRvdzogMCA0cHggMTJweCByZ2JhKDAsIDAsIDAsIDAuMTUpO1xuICAgICAgfVxuXG4gICAgICAuaW5pdGlhbHMge1xuICAgICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgICAgICB1c2VyLXNlbGVjdDogbm9uZTtcbiAgICAgIH1cbiAgICBgLFxuICBdLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgVGFzQXZhdGFyQ29tcG9uZW50IHtcbiAgQElucHV0KCkgbmFtZTogc3RyaW5nID0gJyc7XG4gIEBJbnB1dCgpIHNpemU6IG51bWJlciA9IDgwO1xuXG4gIGdldCBpbml0aWFscygpOiBzdHJpbmcge1xuICAgIGlmICghdGhpcy5uYW1lKSByZXR1cm4gJyc7XG4gICAgcmV0dXJuIHRoaXMubmFtZVxuICAgICAgLnNwbGl0KCcgJylcbiAgICAgIC5maWx0ZXIoKG4pID0+IG4ubGVuZ3RoID4gMClcbiAgICAgIC5tYXAoKG4pID0+IG5bMF0pXG4gICAgICAuam9pbignJylcbiAgICAgIC50b1VwcGVyQ2FzZSgpXG4gICAgICAuc3Vic3RyaW5nKDAsIDIpO1xuICB9XG5cbiAgZ2V0IGZvbnRTaXplKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIE1hdGgucm91bmQodGhpcy5zaXplICogMC40KTtcbiAgfVxufVxuIl19
|