@libs-ui/components-audio 0.2.190 → 0.2.192
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 +300 -57
- package/audio.component.d.ts +16 -5
- package/demo/audio-demo.component.d.ts +112 -0
- package/esm2022/audio.component.mjs +74 -15
- package/esm2022/demo/audio-demo.component.mjs +473 -0
- package/esm2022/index.mjs +3 -2
- package/esm2022/interfaces/function-control-event.interface.mjs +2 -0
- package/fesm2022/libs-ui-components-audio.mjs +528 -124
- package/fesm2022/libs-ui-components-audio.mjs.map +1 -1
- package/index.d.ts +2 -1
- package/interfaces/function-control-event.interface.d.ts +46 -0
- package/package.json +2 -2
- package/demo.component.d.ts +0 -20
- package/esm2022/demo.component.mjs +0 -128
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { Component, ViewChild, computed, signal } from '@angular/core';
|
|
2
|
+
import { LibsUiComponentsAudioComponent } from '../audio.component';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export class LibsUiComponentsAudioDemoComponent {
|
|
5
|
+
audioPlayer;
|
|
6
|
+
// State using signals
|
|
7
|
+
isPlaying = signal(false);
|
|
8
|
+
isMuted = signal(false);
|
|
9
|
+
volume = signal(100);
|
|
10
|
+
currentTime = signal('00:00:00');
|
|
11
|
+
duration = signal('00:00:00');
|
|
12
|
+
progress = signal(0);
|
|
13
|
+
// Computed properties for template display
|
|
14
|
+
volumePercent = computed(() => Math.round(this.volume()));
|
|
15
|
+
// Selected audio sample
|
|
16
|
+
selectedAudio = signal(1);
|
|
17
|
+
currentAudioSource = signal('https://nhacchuong123.com/nhac-chuong/abcdefgh/nhac-chuong-nguoi-ay-dau-co-dang-akira-phan-nguyen-van-chung.mp3');
|
|
18
|
+
// Function Control variables
|
|
19
|
+
functionControls = null;
|
|
20
|
+
// Audio samples for demo
|
|
21
|
+
audioSamples = signal([
|
|
22
|
+
{
|
|
23
|
+
id: 1,
|
|
24
|
+
name: 'Bản nhạc mẫu 1',
|
|
25
|
+
src: 'https://nhacchuong123.com/nhac-chuong/abcdefgh/nhac-chuong-nguoi-ay-dau-co-dang-akira-phan-nguyen-van-chung.mp3',
|
|
26
|
+
duration: '01:30'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 2,
|
|
30
|
+
name: 'Bản nhạc mẫu 2',
|
|
31
|
+
src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
|
|
32
|
+
duration: '02:15'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 3,
|
|
36
|
+
name: 'Bản nhạc mẫu 3',
|
|
37
|
+
src: 'https://dl.dropboxusercontent.com/s/75jpngrgnavyu1f/The-Noisy-Freaks.mp3',
|
|
38
|
+
duration: '03:45'
|
|
39
|
+
}
|
|
40
|
+
]);
|
|
41
|
+
// API Documentation data
|
|
42
|
+
inputsDoc = signal([
|
|
43
|
+
{
|
|
44
|
+
name: 'fileAudio',
|
|
45
|
+
type: 'string',
|
|
46
|
+
default: 'Bắt buộc',
|
|
47
|
+
description: 'URL của file audio cần phát'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'checkPermissionDownloadAudio',
|
|
51
|
+
type: '() => Promise<boolean>',
|
|
52
|
+
default: 'Bắt buộc',
|
|
53
|
+
description: 'Function trả về promise với kết quả boolean cho biết nếu được phép download'
|
|
54
|
+
}
|
|
55
|
+
]);
|
|
56
|
+
// Output documentation
|
|
57
|
+
outputsDoc = signal([
|
|
58
|
+
{
|
|
59
|
+
name: 'outFunctionsControl',
|
|
60
|
+
type: 'IAudioFunctionControlEvent',
|
|
61
|
+
description: 'Emits các hàm điều khiển audio'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'outVolumeControl',
|
|
65
|
+
type: 'number',
|
|
66
|
+
description: 'Emits giá trị âm lượng hiện tại (0-100)'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'outTimeUpdate',
|
|
70
|
+
type: '{ currentTime: string, duration: string }',
|
|
71
|
+
description: 'Emits thông tin thời gian hiện tại và tổng thời gian'
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'outEnded',
|
|
75
|
+
type: 'void',
|
|
76
|
+
description: 'Emits khi audio kết thúc phát'
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'outMute',
|
|
80
|
+
type: 'boolean',
|
|
81
|
+
description: 'Emits trạng thái tắt/bật tiếng'
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'outPlay',
|
|
85
|
+
type: 'boolean',
|
|
86
|
+
description: 'Emits trạng thái phát/tạm dừng'
|
|
87
|
+
}
|
|
88
|
+
]);
|
|
89
|
+
// Interface documentation
|
|
90
|
+
interfacesDoc = signal([
|
|
91
|
+
{
|
|
92
|
+
name: 'IAudioFunctionControlEvent',
|
|
93
|
+
description: 'Interface cho các chức năng điều khiển audio được cung cấp qua output event',
|
|
94
|
+
properties: [
|
|
95
|
+
{
|
|
96
|
+
name: 'playPause',
|
|
97
|
+
type: '(event?: Event) => void',
|
|
98
|
+
description: 'Bắt đầu hoặc tạm dừng phát audio'
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'toggleMute',
|
|
102
|
+
type: '(event?: Event) => void',
|
|
103
|
+
description: 'Bật hoặc tắt âm thanh'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'setVolume',
|
|
107
|
+
type: '(value: number) => void',
|
|
108
|
+
description: 'Điều chỉnh âm lượng (giá trị từ 0 đến 100)'
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'seekTo',
|
|
112
|
+
type: '(value: number) => void',
|
|
113
|
+
description: 'Di chuyển đến vị trí cụ thể trong audio (giá trị từ 0 đến 100)'
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'download',
|
|
117
|
+
type: '(event?: Event) => void',
|
|
118
|
+
description: 'Tải xuống file audio'
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'isPlaying',
|
|
122
|
+
type: '() => boolean',
|
|
123
|
+
description: 'Kiểm tra trạng thái đang phát audio'
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'isMuted',
|
|
127
|
+
type: '() => boolean',
|
|
128
|
+
description: 'Kiểm tra trạng thái tắt tiếng'
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
]);
|
|
133
|
+
// Method documentation
|
|
134
|
+
methodsDoc = signal([
|
|
135
|
+
{
|
|
136
|
+
name: 'playPause',
|
|
137
|
+
params: 'event?: Event',
|
|
138
|
+
returnType: 'void',
|
|
139
|
+
description: 'Phát hoặc tạm dừng audio'
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'toggleMute',
|
|
143
|
+
params: 'event?: Event',
|
|
144
|
+
returnType: 'void',
|
|
145
|
+
description: 'Bật/tắt âm thanh'
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: 'seekTo',
|
|
149
|
+
params: 'value: number',
|
|
150
|
+
returnType: 'void',
|
|
151
|
+
description: 'Di chuyển đến vị trí cụ thể trong audio (giá trị từ 0-100)'
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'setVolume',
|
|
155
|
+
params: 'value: number',
|
|
156
|
+
returnType: 'void',
|
|
157
|
+
description: 'Điều chỉnh âm lượng (giá trị từ 0-100)'
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'download',
|
|
161
|
+
params: 'event?: Event',
|
|
162
|
+
returnType: 'void',
|
|
163
|
+
description: 'Tải xuống file audio'
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'isPlaying',
|
|
167
|
+
params: '',
|
|
168
|
+
returnType: 'boolean',
|
|
169
|
+
description: 'Kiểm tra nếu audio đang phát'
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'isMuted',
|
|
173
|
+
params: '',
|
|
174
|
+
returnType: 'boolean',
|
|
175
|
+
description: 'Kiểm tra nếu audio đang tắt tiếng'
|
|
176
|
+
}
|
|
177
|
+
]);
|
|
178
|
+
// Features data
|
|
179
|
+
features = signal([
|
|
180
|
+
{
|
|
181
|
+
id: 1,
|
|
182
|
+
icon: '▶️',
|
|
183
|
+
title: 'Điều khiển audio',
|
|
184
|
+
description: 'Điều khiển audio component qua các API'
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 2,
|
|
188
|
+
icon: '🔊',
|
|
189
|
+
title: 'Quản lý âm lượng',
|
|
190
|
+
description: 'Điều chỉnh âm lượng và tắt tiếng với thanh trượt trực quan'
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: 3,
|
|
194
|
+
icon: '⏱️',
|
|
195
|
+
title: 'Hiển thị thời gian',
|
|
196
|
+
description: 'Hiển thị thời gian hiện tại và tổng thời gian theo định dạng HH:MM:SS'
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 4,
|
|
200
|
+
icon: '📊',
|
|
201
|
+
title: 'Thanh tiến độ',
|
|
202
|
+
description: 'Thanh tiến độ có thể tương tác để tua nhanh hoặc tua lại'
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: 5,
|
|
206
|
+
icon: '📥',
|
|
207
|
+
title: 'Tải xuống',
|
|
208
|
+
description: 'Hỗ trợ tải xuống file âm thanh với kiểm soát quyền'
|
|
209
|
+
}
|
|
210
|
+
]);
|
|
211
|
+
// Code examples data
|
|
212
|
+
codeExamples = signal([
|
|
213
|
+
{
|
|
214
|
+
id: 1,
|
|
215
|
+
title: 'Cài đặt cơ bản',
|
|
216
|
+
code: `<libs_ui-components-audio
|
|
217
|
+
[fileAudio]="'path/to/audio.mp3'"
|
|
218
|
+
[checkPermissionDownloadAudio]="checkPermission">
|
|
219
|
+
</libs_ui-components-audio>`
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: 2,
|
|
223
|
+
title: 'Sử dụng Function Control',
|
|
224
|
+
code: `import { Component, ViewChild, signal } from '@angular/core';
|
|
225
|
+
import { LibsUiComponentsAudioComponent } from '@libs-ui/components-audio';
|
|
226
|
+
import { IAudioFunctionControlEvent } from '@libs-ui/components-audio';
|
|
227
|
+
|
|
228
|
+
@Component({
|
|
229
|
+
selector: 'app-my-component',
|
|
230
|
+
template: \`
|
|
231
|
+
<libs_ui-components-audio
|
|
232
|
+
#audioPlayer
|
|
233
|
+
[fileAudio]="audioSource()"
|
|
234
|
+
[checkPermissionDownloadAudio]="checkPermission"
|
|
235
|
+
(outFunctionsControl)="registerFunctions($event)">
|
|
236
|
+
</libs_ui-components-audio>
|
|
237
|
+
|
|
238
|
+
<button (click)="playAudio()">Phát/Tạm dừng</button>
|
|
239
|
+
\`
|
|
240
|
+
})
|
|
241
|
+
export class MyComponent {
|
|
242
|
+
@ViewChild('audioPlayer') audioPlayer!: LibsUiComponentsAudioComponent;
|
|
243
|
+
audioSource = signal<string>('path/to/audio.mp3');
|
|
244
|
+
functionControls: IAudioFunctionControlEvent | null = null;
|
|
245
|
+
|
|
246
|
+
registerFunctions(event: IAudioFunctionControlEvent) {
|
|
247
|
+
this.functionControls = event;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
playAudio() {
|
|
251
|
+
if (this.functionControls) {
|
|
252
|
+
this.functionControls.playPause();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}`
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: 3,
|
|
259
|
+
title: 'Sử dụng Events để cập nhật UI',
|
|
260
|
+
code: `import { Component, signal } from '@angular/core';
|
|
261
|
+
import { LibsUiComponentsAudioComponent } from '@libs-ui/components-audio';
|
|
262
|
+
|
|
263
|
+
@Component({
|
|
264
|
+
selector: 'app-my-component',
|
|
265
|
+
template: \`
|
|
266
|
+
<libs_ui-components-audio
|
|
267
|
+
[fileAudio]="audioSource()"
|
|
268
|
+
[checkPermissionDownloadAudio]="checkPermission"
|
|
269
|
+
(outTimeUpdate)="handleTimeUpdate($event)"
|
|
270
|
+
(outVolumeControl)="handleVolumeChange($event)"
|
|
271
|
+
(outPlay)="handlePlayChange($event)"
|
|
272
|
+
(outMute)="handleMuteChange($event)"
|
|
273
|
+
(outEnded)="handleEnded()">
|
|
274
|
+
</libs_ui-components-audio>
|
|
275
|
+
|
|
276
|
+
<div class="audio-info">
|
|
277
|
+
<p>Trạng thái: {{ isPlaying() ? 'Đang phát' : 'Tạm dừng' }}</p>
|
|
278
|
+
<p>Thời gian hiện tại: {{ currentTime() }}</p>
|
|
279
|
+
<p>Tổng thời gian: {{ duration() }}</p>
|
|
280
|
+
<p>Âm lượng: {{ volumeLevel() }}%</p>
|
|
281
|
+
</div>
|
|
282
|
+
\`
|
|
283
|
+
})
|
|
284
|
+
export class MyComponent {
|
|
285
|
+
audioSource = signal<string>('path/to/audio.mp3');
|
|
286
|
+
isPlaying = signal<boolean>(false);
|
|
287
|
+
isMuted = signal<boolean>(false);
|
|
288
|
+
currentTime = signal<string>('00:00:00');
|
|
289
|
+
duration = signal<string>('00:00:00');
|
|
290
|
+
volumeLevel = signal<number>(100);
|
|
291
|
+
|
|
292
|
+
checkPermission = (): Promise<boolean> => {
|
|
293
|
+
return Promise.resolve(true);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
handleTimeUpdate(timeInfo: { currentTime: string, duration: string }) {
|
|
297
|
+
this.currentTime.set(timeInfo.currentTime);
|
|
298
|
+
this.duration.set(timeInfo.duration);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
handleVolumeChange(volume: number) {
|
|
302
|
+
this.volumeLevel.set(volume);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
handlePlayChange(isPlaying: boolean) {
|
|
306
|
+
this.isPlaying.set(isPlaying);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
handleMuteChange(isMuted: boolean) {
|
|
310
|
+
this.isMuted.set(isMuted);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
handleEnded() {
|
|
314
|
+
this.isPlaying.set(false);
|
|
315
|
+
}
|
|
316
|
+
}`
|
|
317
|
+
}
|
|
318
|
+
]);
|
|
319
|
+
// Control methods
|
|
320
|
+
playAudio() {
|
|
321
|
+
if (this.functionControls) {
|
|
322
|
+
this.functionControls.playPause();
|
|
323
|
+
this.isPlaying.set(this.functionControls.isPlaying());
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
toggleMute() {
|
|
327
|
+
if (this.functionControls) {
|
|
328
|
+
this.functionControls.toggleMute();
|
|
329
|
+
this.isMuted.set(this.functionControls.isMuted());
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
changeVolume(event) {
|
|
333
|
+
if (this.functionControls && event.target) {
|
|
334
|
+
const value = parseInt(event.target.value);
|
|
335
|
+
this.volume.set(value);
|
|
336
|
+
this.functionControls.setVolume(value);
|
|
337
|
+
this.isMuted.set(this.functionControls.isMuted());
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
downloadAudio() {
|
|
341
|
+
if (this.functionControls) {
|
|
342
|
+
this.functionControls.download();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Format thời gian từ giây sang chuỗi HH:MM:SS
|
|
347
|
+
*/
|
|
348
|
+
formatTime(timeInSeconds) {
|
|
349
|
+
if (isNaN(timeInSeconds))
|
|
350
|
+
return '00:00:00';
|
|
351
|
+
const hours = Math.floor(timeInSeconds / 3600);
|
|
352
|
+
const minutes = Math.floor((timeInSeconds - (hours * 3600)) / 60);
|
|
353
|
+
const seconds = Math.floor(timeInSeconds - (hours * 3600) - (minutes * 60));
|
|
354
|
+
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Sao chép text vào clipboard
|
|
358
|
+
*/
|
|
359
|
+
copyToClipboard(text) {
|
|
360
|
+
navigator.clipboard.writeText(text)
|
|
361
|
+
.then(() => {
|
|
362
|
+
alert('Đã sao chép vào clipboard!');
|
|
363
|
+
})
|
|
364
|
+
.catch(err => {
|
|
365
|
+
console.error('Failed to copy: ', err);
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Đăng ký các function control từ component
|
|
370
|
+
*/
|
|
371
|
+
registerFunctions(event) {
|
|
372
|
+
this.functionControls = event;
|
|
373
|
+
// Initialize state
|
|
374
|
+
if (this.functionControls) {
|
|
375
|
+
this.isPlaying.set(this.functionControls.isPlaying());
|
|
376
|
+
this.isMuted.set(this.functionControls.isMuted());
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Xử lý sự kiện thay đổi thời gian
|
|
381
|
+
*/
|
|
382
|
+
handleTimeUpdate(timeInfo) {
|
|
383
|
+
this.currentTime.set(timeInfo.currentTime);
|
|
384
|
+
this.duration.set(timeInfo.duration);
|
|
385
|
+
// Calculate progress based on currentTime and duration
|
|
386
|
+
const regex = /(\d+):(\d+):(\d+)/;
|
|
387
|
+
const currentMatches = timeInfo.currentTime.match(regex);
|
|
388
|
+
const durationMatches = timeInfo.duration.match(regex);
|
|
389
|
+
if (currentMatches && durationMatches) {
|
|
390
|
+
const currentSeconds = parseInt(currentMatches[1]) * 3600 +
|
|
391
|
+
parseInt(currentMatches[2]) * 60 +
|
|
392
|
+
parseInt(currentMatches[3]);
|
|
393
|
+
const totalSeconds = parseInt(durationMatches[1]) * 3600 +
|
|
394
|
+
parseInt(durationMatches[2]) * 60 +
|
|
395
|
+
parseInt(durationMatches[3]);
|
|
396
|
+
if (totalSeconds > 0) {
|
|
397
|
+
this.progress.set(Math.floor((currentSeconds / totalSeconds) * 100));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Xử lý sự kiện thay đổi âm lượng
|
|
403
|
+
*/
|
|
404
|
+
handleVolumeChange(volume) {
|
|
405
|
+
this.volume.set(volume);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Xử lý sự kiện thay đổi trạng thái phát
|
|
409
|
+
*/
|
|
410
|
+
handlePlayChange(isPlaying) {
|
|
411
|
+
this.isPlaying.set(isPlaying);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Xử lý sự kiện thay đổi trạng thái tắt tiếng
|
|
415
|
+
*/
|
|
416
|
+
handleMuteChange(isMuted) {
|
|
417
|
+
this.isMuted.set(isMuted);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Xử lý sự kiện kết thúc phát
|
|
421
|
+
*/
|
|
422
|
+
handleEnded() {
|
|
423
|
+
this.isPlaying.set(false);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Chọn mẫu âm thanh
|
|
427
|
+
*/
|
|
428
|
+
selectAudio(id) {
|
|
429
|
+
this.selectedAudio.set(id);
|
|
430
|
+
const sample = this.audioSamples().find(audio => audio.id === id);
|
|
431
|
+
if (sample) {
|
|
432
|
+
// Reset audio state
|
|
433
|
+
this.isPlaying.set(false);
|
|
434
|
+
this.progress.set(0);
|
|
435
|
+
this.currentTime.set('00:00:00');
|
|
436
|
+
this.duration.set('00:00:00');
|
|
437
|
+
// Simply update the source - the component will handle the reload
|
|
438
|
+
this.currentAudioSource.set(sample.src);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Kiểm tra quyền tải xuống
|
|
443
|
+
*/
|
|
444
|
+
checkDownloadPermission = () => {
|
|
445
|
+
return Promise.resolve(true);
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* Thay đổi vị trí phát bằng cách click trực tiếp vào thanh tiến độ
|
|
449
|
+
*/
|
|
450
|
+
seekAudioByClick(event) {
|
|
451
|
+
if (this.functionControls) {
|
|
452
|
+
const progressBar = event.currentTarget;
|
|
453
|
+
const rect = progressBar.getBoundingClientRect();
|
|
454
|
+
const offsetX = event.clientX - rect.left;
|
|
455
|
+
const percentX = (offsetX / rect.width) * 100;
|
|
456
|
+
// Đảm bảo giá trị nằm trong khoảng 0-100
|
|
457
|
+
const clampedPercent = Math.max(0, Math.min(100, percentX));
|
|
458
|
+
this.progress.set(Math.round(clampedPercent));
|
|
459
|
+
// Gọi method seekTo để cập nhật vị trí phát
|
|
460
|
+
this.functionControls.seekTo(this.progress());
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsAudioDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
464
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: LibsUiComponentsAudioDemoComponent, isStandalone: true, selector: "lib-audio-demo", viewQueries: [{ propertyName: "audioPlayer", first: true, predicate: ["audioPlayer"], descendants: true }], ngImport: i0, template: "<div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 font-sans text-gray-800\">\n <header class=\"text-center py-10 bg-white rounded-lg shadow mb-8\">\n <h1 class=\"text-4xl font-bold text-gray-800 mb-2\">Demo Tr\u00ECnh Ph\u00E1t \u00C2m Thanh</h1>\n <p class=\"text-xl text-gray-600\">Th\u01B0 vi\u1EC7n component cho Angular \u0111\u1EC3 ph\u00E1t v\u00E0 \u0111i\u1EC1u khi\u1EC3n \u00E2m thanh</p>\n </header>\n\n <main>\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">Gi\u1EDBi thi\u1EC7u</h2>\n <p class=\"text-lg\">\n <code class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">@libs-ui/components-audio</code> l\u00E0 m\u1ED9t\n component Angular m\u1EA1nh m\u1EBD cho ph\u00E9p ng\u01B0\u1EDDi d\u00F9ng ph\u00E1t, t\u1EA1m d\u1EEBng v\u00E0 \u0111i\u1EC1u khi\u1EC3n c\u00E1c t\u1EC7p \u00E2m thanh v\u1EDBi nhi\u1EC1u t\u00EDnh n\u0103ng.\n </p>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">C\u00E0i \u0111\u1EB7t</h2>\n <p>\u0110\u1EC3 c\u00E0i \u0111\u1EB7t th\u01B0 vi\u1EC7n, s\u1EED d\u1EE5ng npm ho\u1EB7c yarn:</p>\n\n <div class=\"relative my-4\">\n <pre\n class=\"bg-gray-100 p-4 rounded overflow-x-auto mb-4\"><code class=\"font-mono\">npm install @libs-ui/components-audio</code></pre>\n <button\n class=\"absolute top-2 right-2 bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600 transition\"\n (click)=\"copyToClipboard('npm install @libs-ui/components-audio')\">\n Sao ch\u00E9p\n </button>\n </div>\n\n <p>Ho\u1EB7c v\u1EDBi yarn:</p>\n\n <div class=\"relative my-4\">\n <pre\n class=\"bg-gray-100 p-4 rounded overflow-x-auto mb-4\"><code class=\"font-mono\">yarn add @libs-ui/components-audio</code></pre>\n <button\n class=\"absolute top-2 right-2 bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600 transition\"\n (click)=\"copyToClipboard('yarn add @libs-ui/components-audio')\">\n Sao ch\u00E9p\n </button>\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">Demo tr\u1EF1c ti\u1EBFp</h2>\n <div class=\"bg-gray-50 p-6 rounded-lg\">\n <p class=\"mb-4\">Th\u1EED nghi\u1EC7m tr\u00ECnh ph\u00E1t \u00E2m thanh v\u1EDBi c\u00E1c file m\u1EABu:</p>\n\n <div class=\"mb-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Ch\u1ECDn file \u00E2m thanh:</h3>\n <div class=\"flex flex-col gap-2\">\n @for (audio of audioSamples(); track audio.id) {\n <div\n class=\"flex justify-between p-3 bg-white rounded border cursor-pointer transition hover:bg-blue-50 hover:border-blue-500\"\n [class.bg-blue-50]=\"selectedAudio() === audio.id\"\n [class.border-blue-500]=\"selectedAudio() === audio.id\"\n [class.font-medium]=\"selectedAudio() === audio.id\"\n (click)=\"selectAudio(audio.id)\">\n <span>{{ audio.name }}</span>\n <span>{{ audio.duration }}</span>\n </div>\n }\n </div>\n </div>\n\n <div class=\"mb-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Tr\u00ECnh ph\u00E1t:</h3>\n <libs_ui-components-audio #audioPlayer\n [fileAudio]=\"currentAudioSource()\"\n [checkPermissionDownloadAudio]=\"checkDownloadPermission\"\n (outFunctionsControl)=\"registerFunctions($event)\"\n (outTimeUpdate)=\"handleTimeUpdate($event)\"\n (outVolumeControl)=\"handleVolumeChange($event)\"\n (outPlay)=\"handlePlayChange($event)\"\n (outMute)=\"handleMuteChange($event)\"\n (outEnded)=\"handleEnded()\">\n </libs_ui-components-audio>\n </div>\n\n <div class=\"mb-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">\u0110i\u1EC1u khi\u1EC3n qua Function Control:</h3>\n <div class=\"flex flex-wrap gap-3 items-center bg-white p-4 rounded border\">\n <button class=\"px-4 py-2 bg-blue-500 text-white font-medium rounded hover:bg-blue-600 transition\"\n (click)=\"playAudio()\">\n Ph\u00E1t/T\u1EA1m d\u1EEBng\n </button>\n <button class=\"px-4 py-2 bg-blue-500 text-white font-medium rounded hover:bg-blue-600 transition\"\n (click)=\"toggleMute()\">\n {{ isMuted() ? 'B\u1EADt ti\u1EBFng' : 'T\u1EAFt ti\u1EBFng' }}\n </button>\n\n <div class=\"flex items-center gap-2 ml-2\">\n <span>\u00C2m l\u01B0\u1EE3ng:</span>\n <input type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"volumePercent()\"\n (input)=\"changeVolume($event)\"\n class=\"w-24 volume-slider\"\n [style.--volume-percent.%]=\"volumePercent()\">\n <span>{{ volumePercent() }}%</span>\n </div>\n\n <div class=\"w-full mt-2\">\n <div class=\"flex items-center gap-2\">\n <span>Ti\u1EBFn \u0111\u1ED9:</span>\n <div class=\"relative h-2 bg-gray-200 rounded-full flex-1 cursor-pointer\"\n (click)=\"seekAudioByClick($event)\">\n <div class=\"absolute top-0 left-0 h-full bg-blue-500 rounded-full\"\n [style.width.%]=\"progress()\"></div>\n </div>\n <span>{{ progress() }}%</span>\n </div>\n </div>\n\n <button class=\"px-4 py-2 bg-green-500 text-white font-medium rounded hover:bg-green-600 transition mt-4\"\n (click)=\"downloadAudio()\">\n T\u1EA3i xu\u1ED1ng\n </button>\n </div>\n\n <div class=\"mt-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Tr\u1EA1ng th\u00E1i:</h3>\n <div class=\"bg-white p-4 rounded border\">\n <div class=\"mb-2\">\n <strong>Tr\u1EA1ng th\u00E1i:</strong>\n <span [class.text-green-600]=\"isPlaying()\"\n [class.text-red-600]=\"!isPlaying()\">\n {{ isPlaying() ? '\u0110ang ph\u00E1t' : 'T\u1EA1m d\u1EEBng' }}\n </span>\n </div>\n <div class=\"mb-2\">\n <strong>Th\u1EDDi gian hi\u1EC7n t\u1EA1i:</strong>\n <span>{{ currentTime() }}</span>\n </div>\n <div class=\"mb-2\">\n <strong>T\u1ED5ng th\u1EDDi gian:</strong>\n <span>{{ duration() }}</span>\n </div>\n <div class=\"mb-2\">\n <strong>Ti\u1EBFn \u0111\u1ED9:</strong>\n <span>{{ progress() }}%</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">T\u00E0i li\u1EC7u API</h2>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Inputs</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white\">\n <thead class=\"bg-gray-100\">\n <tr>\n <th class=\"py-3 px-4 text-left\">T\u00EAn</th>\n <th class=\"py-3 px-4 text-left\">Ki\u1EC3u d\u1EEF li\u1EC7u</th>\n <th class=\"py-3 px-4 text-left\">M\u1EB7c \u0111\u1ECBnh</th>\n <th class=\"py-3 px-4 text-left\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n @for (input of inputsDoc(); track input.name) {\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ input.name }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ input.type }}</code></td>\n <td class=\"py-3 px-4\">{{ input.default }}</td>\n <td class=\"py-3 px-4\">{{ input.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Outputs</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white\">\n <thead class=\"bg-gray-100\">\n <tr>\n <th class=\"py-3 px-4 text-left\">T\u00EAn</th>\n <th class=\"py-3 px-4 text-left\">Ki\u1EC3u d\u1EEF li\u1EC7u</th>\n <th class=\"py-3 px-4 text-left\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n @for (output of outputsDoc(); track output.name) {\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ output.name }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ output.type }}</code></td>\n <td class=\"py-3 px-4\">{{ output.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Function Control Methods</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white\">\n <thead class=\"bg-gray-100\">\n <tr>\n <th class=\"py-3 px-4 text-left\">T\u00EAn</th>\n <th class=\"py-3 px-4 text-left\">Tham s\u1ED1</th>\n <th class=\"py-3 px-4 text-left\">Ki\u1EC3u tr\u1EA3 v\u1EC1</th>\n <th class=\"py-3 px-4 text-left\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n @for (method of methodsDoc(); track method.name) {\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ method.name }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ method.params }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ method.returnType }}</code></td>\n <td class=\"py-3 px-4\">{{ method.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Interfaces</h3>\n <div class=\"overflow-x-auto mb-8\">\n @for (interfaceDoc of interfacesDoc(); track interfaceDoc.name) {\n <div class=\"mb-6 bg-white p-4 rounded border\">\n <h4 class=\"text-lg font-semibold text-gray-700 mb-2\">{{ interfaceDoc.name }}</h4>\n <p class=\"mb-4\">{{ interfaceDoc.description }}</p>\n\n <table class=\"min-w-full bg-white\">\n <thead class=\"bg-gray-100\">\n <tr>\n <th class=\"py-3 px-4 text-left\">T\u00EAn</th>\n <th class=\"py-3 px-4 text-left\">Ki\u1EC3u d\u1EEF li\u1EC7u</th>\n <th class=\"py-3 px-4 text-left\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n @for (prop of interfaceDoc.properties; track prop.name) {\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ prop.name }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ prop.type }}</code></td>\n <td class=\"py-3 px-4\">{{ prop.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">T\u00EDnh n\u0103ng</h2>\n <ul class=\"space-y-6\">\n @for (feature of features(); track feature.id) {\n <li class=\"flex items-start\">\n <span class=\"text-3xl mr-4 min-w-10 text-center\">{{ feature.icon }}</span>\n <div>\n <h3 class=\"text-xl font-semibold text-gray-700\">{{ feature.title }}</h3>\n <p>{{ feature.description }}</p>\n </div>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">C\u00E1ch s\u1EED d\u1EE5ng</h2>\n <div class=\"flex flex-col gap-4\">\n @for (example of codeExamples(); track example.id) {\n <div class=\"border border-gray-200 rounded-lg p-4\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-2\">{{ example.title }}</h3>\n <pre\n class=\"bg-gray-100 p-4 rounded overflow-x-auto\"><code [innerHTML]=\"example.code\" class=\"font-mono\"></code></pre>\n </div>\n }\n </div>\n </section>\n </main>\n</div>\n", styles: ["input[type=range]{cursor:pointer;-webkit-appearance:none;appearance:none;height:6px;border-radius:999px;background-color:#e5e7eb;outline:none;width:100%}input[type=range].volume-slider{background:linear-gradient(to right,#3b82f6 var(--volume-percent, 50%),#e5e7eb var(--volume-percent, 50%))}input[type=range]::-webkit-slider-runnable-track{width:100%;height:6px;background-color:#e5e7eb;border-radius:999px}input[type=range]::-moz-range-track{width:100%;height:6px;background-color:#e5e7eb;border-radius:999px}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;border-radius:50%;background-color:#3b82f6;cursor:pointer;transition:background-color .2s ease;margin-top:-5px}input[type=range]::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background-color:#3b82f6;cursor:pointer;transition:background-color .2s ease;border:none}input[type=range]::-webkit-slider-thumb:hover{background-color:#2563eb}input[type=range]::-moz-range-thumb:hover{background-color:#2563eb}input[type=range].volume-slider::-webkit-slider-runnable-track{background:linear-gradient(to right,#3b82f6 var(--volume-percent, 50%),#e5e7eb var(--volume-percent, 50%))}input[type=range].volume-slider::-moz-range-track{background:linear-gradient(to right,#3b82f6 var(--volume-percent, 50%),#e5e7eb var(--volume-percent, 50%))}.control-button:active{transform:scale(.95)}libs_ui-components-audio{display:block;width:100%}.transition{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}pre::-webkit-scrollbar{height:8px}pre::-webkit-scrollbar-track{background:#f1f1f1;border-radius:4px}pre::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:4px}pre::-webkit-scrollbar-thumb:hover{background:#a1a1a1}\n"], dependencies: [{ kind: "component", type: LibsUiComponentsAudioComponent, selector: "libs_ui-components-audio", inputs: ["fileAudio", "checkPermissionDownloadAudio"], outputs: ["outFunctionsControl", "outVolumeControl", "outTimeUpdate", "outEnded", "outMute", "outPlay"] }] });
|
|
465
|
+
}
|
|
466
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsAudioDemoComponent, decorators: [{
|
|
467
|
+
type: Component,
|
|
468
|
+
args: [{ selector: 'lib-audio-demo', standalone: true, imports: [LibsUiComponentsAudioComponent], template: "<div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 font-sans text-gray-800\">\n <header class=\"text-center py-10 bg-white rounded-lg shadow mb-8\">\n <h1 class=\"text-4xl font-bold text-gray-800 mb-2\">Demo Tr\u00ECnh Ph\u00E1t \u00C2m Thanh</h1>\n <p class=\"text-xl text-gray-600\">Th\u01B0 vi\u1EC7n component cho Angular \u0111\u1EC3 ph\u00E1t v\u00E0 \u0111i\u1EC1u khi\u1EC3n \u00E2m thanh</p>\n </header>\n\n <main>\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">Gi\u1EDBi thi\u1EC7u</h2>\n <p class=\"text-lg\">\n <code class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">@libs-ui/components-audio</code> l\u00E0 m\u1ED9t\n component Angular m\u1EA1nh m\u1EBD cho ph\u00E9p ng\u01B0\u1EDDi d\u00F9ng ph\u00E1t, t\u1EA1m d\u1EEBng v\u00E0 \u0111i\u1EC1u khi\u1EC3n c\u00E1c t\u1EC7p \u00E2m thanh v\u1EDBi nhi\u1EC1u t\u00EDnh n\u0103ng.\n </p>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">C\u00E0i \u0111\u1EB7t</h2>\n <p>\u0110\u1EC3 c\u00E0i \u0111\u1EB7t th\u01B0 vi\u1EC7n, s\u1EED d\u1EE5ng npm ho\u1EB7c yarn:</p>\n\n <div class=\"relative my-4\">\n <pre\n class=\"bg-gray-100 p-4 rounded overflow-x-auto mb-4\"><code class=\"font-mono\">npm install @libs-ui/components-audio</code></pre>\n <button\n class=\"absolute top-2 right-2 bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600 transition\"\n (click)=\"copyToClipboard('npm install @libs-ui/components-audio')\">\n Sao ch\u00E9p\n </button>\n </div>\n\n <p>Ho\u1EB7c v\u1EDBi yarn:</p>\n\n <div class=\"relative my-4\">\n <pre\n class=\"bg-gray-100 p-4 rounded overflow-x-auto mb-4\"><code class=\"font-mono\">yarn add @libs-ui/components-audio</code></pre>\n <button\n class=\"absolute top-2 right-2 bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600 transition\"\n (click)=\"copyToClipboard('yarn add @libs-ui/components-audio')\">\n Sao ch\u00E9p\n </button>\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">Demo tr\u1EF1c ti\u1EBFp</h2>\n <div class=\"bg-gray-50 p-6 rounded-lg\">\n <p class=\"mb-4\">Th\u1EED nghi\u1EC7m tr\u00ECnh ph\u00E1t \u00E2m thanh v\u1EDBi c\u00E1c file m\u1EABu:</p>\n\n <div class=\"mb-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Ch\u1ECDn file \u00E2m thanh:</h3>\n <div class=\"flex flex-col gap-2\">\n @for (audio of audioSamples(); track audio.id) {\n <div\n class=\"flex justify-between p-3 bg-white rounded border cursor-pointer transition hover:bg-blue-50 hover:border-blue-500\"\n [class.bg-blue-50]=\"selectedAudio() === audio.id\"\n [class.border-blue-500]=\"selectedAudio() === audio.id\"\n [class.font-medium]=\"selectedAudio() === audio.id\"\n (click)=\"selectAudio(audio.id)\">\n <span>{{ audio.name }}</span>\n <span>{{ audio.duration }}</span>\n </div>\n }\n </div>\n </div>\n\n <div class=\"mb-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Tr\u00ECnh ph\u00E1t:</h3>\n <libs_ui-components-audio #audioPlayer\n [fileAudio]=\"currentAudioSource()\"\n [checkPermissionDownloadAudio]=\"checkDownloadPermission\"\n (outFunctionsControl)=\"registerFunctions($event)\"\n (outTimeUpdate)=\"handleTimeUpdate($event)\"\n (outVolumeControl)=\"handleVolumeChange($event)\"\n (outPlay)=\"handlePlayChange($event)\"\n (outMute)=\"handleMuteChange($event)\"\n (outEnded)=\"handleEnded()\">\n </libs_ui-components-audio>\n </div>\n\n <div class=\"mb-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">\u0110i\u1EC1u khi\u1EC3n qua Function Control:</h3>\n <div class=\"flex flex-wrap gap-3 items-center bg-white p-4 rounded border\">\n <button class=\"px-4 py-2 bg-blue-500 text-white font-medium rounded hover:bg-blue-600 transition\"\n (click)=\"playAudio()\">\n Ph\u00E1t/T\u1EA1m d\u1EEBng\n </button>\n <button class=\"px-4 py-2 bg-blue-500 text-white font-medium rounded hover:bg-blue-600 transition\"\n (click)=\"toggleMute()\">\n {{ isMuted() ? 'B\u1EADt ti\u1EBFng' : 'T\u1EAFt ti\u1EBFng' }}\n </button>\n\n <div class=\"flex items-center gap-2 ml-2\">\n <span>\u00C2m l\u01B0\u1EE3ng:</span>\n <input type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"volumePercent()\"\n (input)=\"changeVolume($event)\"\n class=\"w-24 volume-slider\"\n [style.--volume-percent.%]=\"volumePercent()\">\n <span>{{ volumePercent() }}%</span>\n </div>\n\n <div class=\"w-full mt-2\">\n <div class=\"flex items-center gap-2\">\n <span>Ti\u1EBFn \u0111\u1ED9:</span>\n <div class=\"relative h-2 bg-gray-200 rounded-full flex-1 cursor-pointer\"\n (click)=\"seekAudioByClick($event)\">\n <div class=\"absolute top-0 left-0 h-full bg-blue-500 rounded-full\"\n [style.width.%]=\"progress()\"></div>\n </div>\n <span>{{ progress() }}%</span>\n </div>\n </div>\n\n <button class=\"px-4 py-2 bg-green-500 text-white font-medium rounded hover:bg-green-600 transition mt-4\"\n (click)=\"downloadAudio()\">\n T\u1EA3i xu\u1ED1ng\n </button>\n </div>\n\n <div class=\"mt-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Tr\u1EA1ng th\u00E1i:</h3>\n <div class=\"bg-white p-4 rounded border\">\n <div class=\"mb-2\">\n <strong>Tr\u1EA1ng th\u00E1i:</strong>\n <span [class.text-green-600]=\"isPlaying()\"\n [class.text-red-600]=\"!isPlaying()\">\n {{ isPlaying() ? '\u0110ang ph\u00E1t' : 'T\u1EA1m d\u1EEBng' }}\n </span>\n </div>\n <div class=\"mb-2\">\n <strong>Th\u1EDDi gian hi\u1EC7n t\u1EA1i:</strong>\n <span>{{ currentTime() }}</span>\n </div>\n <div class=\"mb-2\">\n <strong>T\u1ED5ng th\u1EDDi gian:</strong>\n <span>{{ duration() }}</span>\n </div>\n <div class=\"mb-2\">\n <strong>Ti\u1EBFn \u0111\u1ED9:</strong>\n <span>{{ progress() }}%</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">T\u00E0i li\u1EC7u API</h2>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Inputs</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white\">\n <thead class=\"bg-gray-100\">\n <tr>\n <th class=\"py-3 px-4 text-left\">T\u00EAn</th>\n <th class=\"py-3 px-4 text-left\">Ki\u1EC3u d\u1EEF li\u1EC7u</th>\n <th class=\"py-3 px-4 text-left\">M\u1EB7c \u0111\u1ECBnh</th>\n <th class=\"py-3 px-4 text-left\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n @for (input of inputsDoc(); track input.name) {\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ input.name }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ input.type }}</code></td>\n <td class=\"py-3 px-4\">{{ input.default }}</td>\n <td class=\"py-3 px-4\">{{ input.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Outputs</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white\">\n <thead class=\"bg-gray-100\">\n <tr>\n <th class=\"py-3 px-4 text-left\">T\u00EAn</th>\n <th class=\"py-3 px-4 text-left\">Ki\u1EC3u d\u1EEF li\u1EC7u</th>\n <th class=\"py-3 px-4 text-left\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n @for (output of outputsDoc(); track output.name) {\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ output.name }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ output.type }}</code></td>\n <td class=\"py-3 px-4\">{{ output.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Function Control Methods</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white\">\n <thead class=\"bg-gray-100\">\n <tr>\n <th class=\"py-3 px-4 text-left\">T\u00EAn</th>\n <th class=\"py-3 px-4 text-left\">Tham s\u1ED1</th>\n <th class=\"py-3 px-4 text-left\">Ki\u1EC3u tr\u1EA3 v\u1EC1</th>\n <th class=\"py-3 px-4 text-left\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n @for (method of methodsDoc(); track method.name) {\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ method.name }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ method.params }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ method.returnType }}</code></td>\n <td class=\"py-3 px-4\">{{ method.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Interfaces</h3>\n <div class=\"overflow-x-auto mb-8\">\n @for (interfaceDoc of interfacesDoc(); track interfaceDoc.name) {\n <div class=\"mb-6 bg-white p-4 rounded border\">\n <h4 class=\"text-lg font-semibold text-gray-700 mb-2\">{{ interfaceDoc.name }}</h4>\n <p class=\"mb-4\">{{ interfaceDoc.description }}</p>\n\n <table class=\"min-w-full bg-white\">\n <thead class=\"bg-gray-100\">\n <tr>\n <th class=\"py-3 px-4 text-left\">T\u00EAn</th>\n <th class=\"py-3 px-4 text-left\">Ki\u1EC3u d\u1EEF li\u1EC7u</th>\n <th class=\"py-3 px-4 text-left\">M\u00F4 t\u1EA3</th>\n </tr>\n </thead>\n <tbody>\n @for (prop of interfaceDoc.properties; track prop.name) {\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ prop.name }}</code></td>\n <td class=\"py-3 px-4\"><code\n class=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">{{ prop.type }}</code></td>\n <td class=\"py-3 px-4\">{{ prop.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">T\u00EDnh n\u0103ng</h2>\n <ul class=\"space-y-6\">\n @for (feature of features(); track feature.id) {\n <li class=\"flex items-start\">\n <span class=\"text-3xl mr-4 min-w-10 text-center\">{{ feature.icon }}</span>\n <div>\n <h3 class=\"text-xl font-semibold text-gray-700\">{{ feature.title }}</h3>\n <p>{{ feature.description }}</p>\n </div>\n </li>\n }\n </ul>\n </section>\n\n <section class=\"bg-white rounded-lg shadow p-8 mb-8\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-4 pb-2 border-b border-gray-200\">C\u00E1ch s\u1EED d\u1EE5ng</h2>\n <div class=\"flex flex-col gap-4\">\n @for (example of codeExamples(); track example.id) {\n <div class=\"border border-gray-200 rounded-lg p-4\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-2\">{{ example.title }}</h3>\n <pre\n class=\"bg-gray-100 p-4 rounded overflow-x-auto\"><code [innerHTML]=\"example.code\" class=\"font-mono\"></code></pre>\n </div>\n }\n </div>\n </section>\n </main>\n</div>\n", styles: ["input[type=range]{cursor:pointer;-webkit-appearance:none;appearance:none;height:6px;border-radius:999px;background-color:#e5e7eb;outline:none;width:100%}input[type=range].volume-slider{background:linear-gradient(to right,#3b82f6 var(--volume-percent, 50%),#e5e7eb var(--volume-percent, 50%))}input[type=range]::-webkit-slider-runnable-track{width:100%;height:6px;background-color:#e5e7eb;border-radius:999px}input[type=range]::-moz-range-track{width:100%;height:6px;background-color:#e5e7eb;border-radius:999px}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;border-radius:50%;background-color:#3b82f6;cursor:pointer;transition:background-color .2s ease;margin-top:-5px}input[type=range]::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background-color:#3b82f6;cursor:pointer;transition:background-color .2s ease;border:none}input[type=range]::-webkit-slider-thumb:hover{background-color:#2563eb}input[type=range]::-moz-range-thumb:hover{background-color:#2563eb}input[type=range].volume-slider::-webkit-slider-runnable-track{background:linear-gradient(to right,#3b82f6 var(--volume-percent, 50%),#e5e7eb var(--volume-percent, 50%))}input[type=range].volume-slider::-moz-range-track{background:linear-gradient(to right,#3b82f6 var(--volume-percent, 50%),#e5e7eb var(--volume-percent, 50%))}.control-button:active{transform:scale(.95)}libs_ui-components-audio{display:block;width:100%}.transition{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}pre::-webkit-scrollbar{height:8px}pre::-webkit-scrollbar-track{background:#f1f1f1;border-radius:4px}pre::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:4px}pre::-webkit-scrollbar-thumb:hover{background:#a1a1a1}\n"] }]
|
|
469
|
+
}], propDecorators: { audioPlayer: [{
|
|
470
|
+
type: ViewChild,
|
|
471
|
+
args: ['audioPlayer']
|
|
472
|
+
}] } });
|
|
473
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXVkaW8tZGVtby5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvYXVkaW8vc3JjL2RlbW8vYXVkaW8tZGVtby5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvYXVkaW8vc3JjL2RlbW8vYXVkaW8tZGVtby5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxNQUFNLG9CQUFvQixDQUFDOztBQVVwRSxNQUFNLE9BQU8sa0NBQWtDO0lBQ25CLFdBQVcsQ0FBa0M7SUFFdkUsc0JBQXNCO0lBQ2YsU0FBUyxHQUFHLE1BQU0sQ0FBVSxLQUFLLENBQUMsQ0FBQztJQUNuQyxPQUFPLEdBQUcsTUFBTSxDQUFVLEtBQUssQ0FBQyxDQUFDO0lBQ2pDLE1BQU0sR0FBRyxNQUFNLENBQVMsR0FBRyxDQUFDLENBQUM7SUFDN0IsV0FBVyxHQUFHLE1BQU0sQ0FBUyxVQUFVLENBQUMsQ0FBQztJQUN6QyxRQUFRLEdBQUcsTUFBTSxDQUFTLFVBQVUsQ0FBQyxDQUFDO0lBQ3RDLFFBQVEsR0FBRyxNQUFNLENBQVMsQ0FBQyxDQUFDLENBQUM7SUFFcEMsMkNBQTJDO0lBQ3BDLGFBQWEsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRWpFLHdCQUF3QjtJQUNqQixhQUFhLEdBQUcsTUFBTSxDQUFTLENBQUMsQ0FBQyxDQUFDO0lBQ2xDLGtCQUFrQixHQUFHLE1BQU0sQ0FBUyxpSEFBaUgsQ0FBQyxDQUFDO0lBRTlKLDZCQUE2QjtJQUN0QixnQkFB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bmVudCwgVmlld0NoaWxkLCBjb21wdXRlZCwgc2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBMaWJzVWlDb21wb25lbnRzQXVkaW9Db21wb25lbnQgfSBmcm9tICcuLi9hdWRpby5jb21wb25lbnQnO1xuaW1wb3J0IHsgSUF1ZGlvRnVuY3Rpb25Db250cm9sRXZlbnQgfSBmcm9tICcuLi9pbnRlcmZhY2VzL2Z1bmN0aW9uLWNvbnRyb2wtZXZlbnQuaW50ZXJmYWNlJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbGliLWF1ZGlvLWRlbW8nLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbTGlic1VpQ29tcG9uZW50c0F1ZGlvQ29tcG9uZW50XSxcbiAgdGVtcGxhdGVVcmw6ICcuL2F1ZGlvLWRlbW8uY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9hdWRpby1kZW1vLmNvbXBvbmVudC5jc3MnXVxufSlcbmV4cG9ydCBjbGFzcyBMaWJzVWlDb21wb25lbnRzQXVkaW9EZW1vQ29tcG9uZW50IHtcbiAgQFZpZXdDaGlsZCgnYXVkaW9QbGF5ZXInKSBhdWRpb1BsYXllciE6IExpYnNVaUNvbXBvbmVudHNBdWRpb0NvbXBvbmVudDtcblxuICAvLyBTdGF0ZSB1c2luZyBzaWduYWxzXG4gIHB1YmxpYyBpc1BsYXlpbmcgPSBzaWduYWw8Ym9vbGVhbj4oZmFsc2UpO1xuICBwdWJsaWMgaXNNdXRlZCA9IHNpZ25hbDxib29sZWFuPihmYWxzZSk7XG4gIHB1YmxpYyB2b2x1bWUgPSBzaWduYWw8bnVtYmVyPigxMDApO1xuICBwdWJsaWMgY3VycmVudFRpbWUgPSBzaWduYWw8c3RyaW5nPignMDA6MDA6MDAnKTtcbiAgcHVibGljIGR1cmF0aW9uID0gc2lnbmFsPHN0cmluZz4oJzAwOjAwOjAwJyk7XG4gIHB1YmxpYyBwcm9ncmVzcyA9IHNpZ25hbDxudW1iZXI+KDApO1xuXG4gIC8vIENvbXB1dGVkIHByb3BlcnRpZXMgZm9yIHRlbXBsYXRlIGRpc3BsYXlcbiAgcHVibGljIHZvbHVtZVBlcmNlbnQgPSBjb21wdXRlZCgoKSA9PiBNYXRoLnJvdW5kKHRoaXMudm9sdW1lKCkpKTtcblxuICAvLyBTZWxlY3RlZCBhdWRpbyBzYW1wbGVcbiAgcHVibGljIHNlbGVjdGVkQXVkaW8gPSBzaWduYWw8bnVtYmVyPigxKTtcbiAgcHVibGljIGN1cnJlbnRBdWRpb1NvdXJjZSA9IHNpZ25hbDxzdHJpbmc+KCdodHRwczovL25oYWNjaHVvbmcxMjMuY29tL25oYWMtY2h1b25nL2FiY2RlZmdoL25oYWMtY2h1b25nLW5ndW9pLWF5LWRhdS1jby1kYW5nLWFraXJhLXBoYW4tbmd1eWVuLXZhbi1jaHVuZy5tcDMnKTtcblxuICAvLyBGdW5jdGlvbiBDb250cm9sIHZhcmlhYmxlc1xuICBwdWJsaWMgZnVuY3Rpb25Db250cm9sczogSUF1ZGlvRnVuY3Rpb25Db250cm9sRXZlbnQgfCBudWxsID0gbnVsbDtcblxuICAvLyBBdWRpbyBzYW1wbGVzIGZvciBkZW1vXG4gIHB1YmxpYyBhdWRpb1NhbXBsZXMgPSBzaWduYWw8QXJyYXk8eyBpZDogbnVtYmVyLCBuYW1lOiBzdHJpbmcsIHNyYzogc3RyaW5nLCBkdXJhdGlvbjogc3RyaW5nIH0+PihbXG4gICAge1xuICAgICAgaWQ6IDEsXG4gICAgICBuYW1lOiAnQuG6o24gbmjhuqFjIG3huqt1IDEnLFxuICAgICAgc3JjOiAnaHR0cHM6Ly9uaGFjY2h1b25nMTIzLmNvbS9uaGFjLWNodW9uZy9hYmNkZWZnaC9uaGFjLWNodW9uZy1uZ3VvaS1heS1kYXUtY28tZGFuZy1ha2lyYS1waGFuLW5ndXllbi12YW4tY2h1bmcubXAzJyxcbiAgICAgIGR1cmF0aW9uOiAnMDE6MzAnXG4gICAgfSxcbiAgICB7XG4gICAgICBpZDogMixcbiAgICAgIG5hbWU6ICdC4bqjbiBuaOG6oWMgbeG6q3UgMicsXG4gICAgICBzcmM6ICdodHRwczovL3d3dy5zb3VuZGhlbGl4LmNvbS9leGFtcGxlcy9tcDMvU291bmRIZWxpeC1Tb25nLTEubXAzJyxcbiAgICAgIGR1cmF0aW9uOiAnMDI6MTUnXG4gICAgfSxcbiAgICB7XG4gICAgICBpZDogMyxcbiAgICAgIG5hbWU6ICdC4bqjbiBuaOG6oWMgbeG6q3UgMycsXG4gICAgICBzcmM6ICdodHRwczovL2RsLmRyb3Bib3h1c2VyY29udGVudC5jb20vcy83NWpwbmdyZ25hdnl1MWYvVGhlLU5vaXN5LUZyZWFrcy5tcDMnLFxuICAgICAgZHVyYXRpb246ICcwMzo0NSdcbiAgICB9XG4gIF0pO1xuXG4gIC8vIEFQSSBEb2N1bWVudGF0aW9uIGRhdGFcbiAgcHVibGljIGlucHV0c0RvYyA9IHNpZ25hbDxBcnJheTx7IG5hbWU6IHN0cmluZywgdHlwZTogc3RyaW5nLCBkZWZhdWx0OiBzdHJpbmcsIGRlc2NyaXB0aW9uOiBzdHJpbmcgfT4+KFtcbiAgICB7XG4gICAgICBuYW1lOiAnZmlsZUF1ZGlvJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZGVmYXVsdDogJ0Lhuq90IGJ14buZYycsXG4gICAgICBkZXNjcmlwdGlvbjogJ1VSTCBj4bunYSBmaWxlIGF1ZGlvIGPhuqduIHBow6F0J1xuICAgIH0sXG4gICAge1xuICAgICAgbmFtZTogJ2NoZWNrUGVybWlzc2lvbkRvd25sb2FkQXVkaW8nLFxuICAgICAgdHlwZTogJygpID0+IFByb21pc2U8Ym9vbGVhbj4nLFxuICAgICAgZGVmYXVsdDogJ0Lhuq90IGJ14buZYycsXG4gICAgICBkZXNjcmlwdGlvbjogJ0Z1bmN0aW9uIHRy4bqjIHbhu4EgcHJvbWlzZSB24bubaSBr4bq/dCBxdeG6oyBib29sZWFuIGNobyBiaeG6v3QgbuG6v3UgxJHGsOG7o2MgcGjDqXAgZG93bmxvYWQnXG4gICAgfVxuICBdKTtcblxuICAvLyBPdXRwdXQgZG9jdW1lbnRhdGlvblxuICBwdWJsaWMgb3V0cHV0c0RvYyA9IHNpZ25hbDxBcnJheTx7IG5hbWU6IHN0cmluZywgdHlwZTogc3RyaW5nLCBkZXNjcmlwdGlvbjogc3RyaW5nIH0+PihbXG4gICAge1xuICAgICAgbmFtZTogJ291dEZ1bmN0aW9uc0NvbnRyb2wnLFxuICAgICAgdHlwZTogJ0lBdWRpb0Z1bmN0aW9uQ29udHJvbEV2ZW50JyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW1pdHMgY8OhYyBow6BtIMSRaeG7gXUga2hp4buDbiBhdWRpbydcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6ICdvdXRWb2x1bWVDb250cm9sJyxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZGVzY3JpcHRpb246ICdFbWl0cyBnacOhIHRy4buLIMOibSBsxrDhu6NuZyBoaeG7h24gdOG6oWkgKDAtMTAwKSdcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6ICdvdXRUaW1lVXBkYXRlJyxcbiAgICAgIHR5cGU6ICd7IGN1cnJlbnRUaW1lOiBzdHJpbmcsIGR1cmF0aW9uOiBzdHJpbmcgfScsXG4gICAgICBkZXNjcmlwdGlvbjogJ0VtaXRzIHRow7RuZyB0aW4gdGjhu51pIGdpYW4gaGnhu4duIHThuqFpIHbDoCB04buVbmcgdGjhu51pIGdpYW4nXG4gICAgfSxcbiAgICB7XG4gICAgICBuYW1lOiAnb3V0RW5kZWQnLFxuICAgICAgdHlwZTogJ3ZvaWQnLFxuICAgICAgZGVzY3JpcHRpb246ICdFbWl0cyBraGkgYXVkaW8ga+G6v3QgdGjDumMgcGjDoXQnXG4gICAgfSxcbiAgICB7XG4gICAgICBuYW1lOiAnb3V0TXV0ZScsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBkZXNjcmlwdGlvbjogJ0VtaXRzIHRy4bqhbmcgdGjDoWkgdOG6r3QvYuG6rXQgdGnhur9uZydcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6ICdvdXRQbGF5JyxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW1pdHMgdHLhuqFuZyB0aMOhaSBwaMOhdC904bqhbSBk4burbmcnXG4gICAgfVxuICBdKTtcblxuICAvLyBJbnRlcmZhY2UgZG9jdW1lbnRhdGlvblxuICBwdWJsaWMgaW50ZXJmYWNlc0RvYyA9IHNpZ25hbDxBcnJheTx7IG5hbWU6IHN0cmluZywgZGVzY3JpcHRpb246IHN0cmluZywgcHJvcGVydGllczogQXJyYXk8eyBuYW1lOiBzdHJpbmcsIHR5cGU6IHN0cmluZywgZGVzY3JpcHRpb246IHN0cmluZyB9PiB9Pj4oW1xuICAgIHtcbiAgICAgIG5hbWU6ICdJQXVkaW9GdW5jdGlvbkNvbnRyb2xFdmVudCcsXG4gICAgICBkZXNjcmlwdGlvbjogJ0ludGVyZmFjZSBjaG8gY8OhYyBjaOG7qWMgbsSDbmcgxJFp4buBdSBraGnhu4NuIGF1ZGlvIMSRxrDhu6NjIGN1bmcgY+G6pXAgcXVhIG91dHB1dCBldmVudCcsXG4gICAgICBwcm9wZXJ0aWVzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiAncGxheVBhdXNlJyxcbiAgICAgICAgICB0eXBlOiAnKGV2ZW50PzogRXZlbnQpID0+IHZvaWQnLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQuG6r3QgxJHhuqd1IGhv4bq3YyB04bqhbSBk4burbmcgcGjDoXQgYXVkaW8nXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiAndG9nZ2xlTXV0ZScsXG4gICAgICAgICAgdHlwZTogJyhldmVudD86IEV2ZW50KSA9PiB2b2lkJyxcbiAgICAgICAgICBkZXNjcmlwdGlvbjogJ0Lhuq10IGhv4bq3YyB04bqvdCDDom0gdGhhbmgnXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiAnc2V0Vm9sdW1lJyxcbiAgICAgICAgICB0eXBlOiAnKHZhbHVlOiBudW1iZXIpID0+IHZvaWQnLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiAnxJBp4buBdSBjaOG7iW5oIMOibSBsxrDhu6NuZyAoZ2nDoSB0cuG7iyB04burIDAgxJHhur9uIDEwMCknXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiAnc2Vla1RvJyxcbiAgICAgICAgICB0eXBlOiAnKHZhbHVlOiBudW1iZXIpID0+IHZvaWQnLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiAnRGkgY2h1eeG7g24gxJHhur9uIHbhu4sgdHLDrSBj4bulIHRo4buDIHRyb25nIGF1ZGlvIChnacOhIHRy4buLIHThu6sgMCDEkeG6v24gMTAwKSdcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIG5hbWU6ICdkb3dubG9hZCcsXG4gICAgICAgICAgdHlwZTogJyhldmVudD86IEV2ZW50KSA9PiB2b2lkJyxcbiAgICAgICAgICBkZXNjcmlwdGlvbjogJ1ThuqNpIHh14buRbmcgZmlsZSBhdWRpbydcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIG5hbWU6ICdpc1BsYXlpbmcnLFxuICAgICAgICAgIHR5cGU6ICcoKSA9PiBib29sZWFuJyxcbiAgICAgICAgICBkZXNjcmlwdGlvbjogJ0tp4buDbSB0cmEgdHLhuqFuZyB0aMOhaSDEkWFuZyBwaMOhdCBhdWRpbydcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIG5hbWU6ICdpc011dGVkJyxcbiAgICAgICAgICB0eXBlOiAnKCkgPT4gYm9vbGVhbicsXG4gICAgICAgICAgZGVzY3JpcHRpb246ICdLaeG7g20gdHJhIHRy4bqhbmcgdGjDoWkgdOG6r3QgdGnhur9uZydcbiAgICAgICAgfVxuICAgICAgXVxuICAgIH1cbiAgXSk7XG5cbiAgLy8gTWV0aG9kIGRvY3VtZW50YXRpb25cbiAgcHVibGljIG1ldGhvZHNEb2MgPSBzaWduYWw8QXJyYXk8eyBuYW1lOiBzdHJpbmcsIHBhcmFtczogc3RyaW5nLCByZXR1cm5UeXBlOiBzdHJpbmcsIGRlc2NyaXB0aW9uOiBzdHJpbmcgfT4+KFtcbiAgICB7XG4gICAgICBuYW1lOiAncGxheVBhdXNlJyxcbiAgICAgIHBhcmFtczogJ2V2ZW50PzogRXZlbnQnLFxuICAgICAgcmV0dXJuVHlwZTogJ3ZvaWQnLFxuICAgICAgZGVzY3JpcHRpb246ICdQaMOhdCBob+G6t2MgdOG6oW0gZOG7q25nIGF1ZGlvJ1xuICAgIH0sXG4gICAge1xuICAgICAgbmFtZTogJ3RvZ2dsZU11dGUnLFxuICAgICAgcGFyYW1zOiAnZXZlbnQ/OiBFdmVudCcsXG4gICAgICByZXR1cm5UeXBlOiAndm9pZCcsXG4gICAgICBkZXNjcmlwdGlvbjogJ0Lhuq10L3Thuq90IMOibSB0aGFuaCdcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6ICdzZWVrVG8nLFxuICAgICAgcGFyYW1zOiAndmFsdWU6IG51bWJlcicsXG4gICAgICByZXR1cm5UeXBlOiAndm9pZCcsXG4gICAgICBkZXNjcmlwdGlvbjogJ0RpIGNodXnhu4NuIMSR4bq/biB24buLIHRyw60gY+G7pSB0aOG7gyB0cm9uZyBhdWRpbyAoZ2nDoSB0cuG7iyB04burIDAtMTAwKSdcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6ICdzZXRWb2x1bWUnLFxuICAgICAgcGFyYW1zOiAndmFsdWU6IG51bWJlcicsXG4gICAgICByZXR1cm5UeXBlOiAndm9pZCcsXG4gICAgICBkZXNjcmlwdGlvbjogJ8SQaeG7gXUgY2jhu4luaCDDom0gbMaw4bujbmcgKGdpw6EgdHLhu4sgdOG7qyAwLTEwMCknXG4gICAgfSxcbiAgICB7XG4gICAgICBuYW1lOiAnZG93bmxvYWQnLFxuICAgICAgcGFyYW1zOiAnZXZlbnQ/OiBFdmVudCcsXG4gICAgICByZXR1cm5UeXBlOiAndm9pZCcsXG4gICAgICBkZXNjcmlwdGlvbjogJ1ThuqNpIHh14buRbmcgZmlsZSBhdWRpbydcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6ICdpc1BsYXlpbmcnLFxuICAgICAgcGFyYW1zOiAnJyxcbiAgICAgIHJldHVyblR5cGU6ICdib29sZWFuJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnS2nhu4NtIHRyYSBu4bq/dSBhdWRpbyDEkWFuZyBwaMOhdCdcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6ICdpc011dGVkJyxcbiAgICAgIHBhcmFtczogJycsXG4gICAgICByZXR1cm5UeXBlOiAnYm9vbGVhbicsXG4gICAgICBkZXNjcmlwdGlvbjogJ0tp4buDbSB0cmEgbuG6v3UgYXVkaW8gxJFhbmcgdOG6r3QgdGnhur9uZydcbiAgICB9XG4gIF0pO1xuXG4gIC8vIEZlYXR1cmVzIGRhdGFcbiAgcHVibGljIGZlYXR1cmVzID0gc2lnbmFsPEFycmF5PHsgaWQ6IG51bWJlciwgaWNvbjogc3RyaW5nLCB0aXRsZTogc3RyaW5nLCBkZXNjcmlwdGlvbjogc3RyaW5nIH0+PihbXG4gICAge1xuICAgICAgaWQ6IDEsXG4gICAgICBpY29uOiAn4pa277iPJyxcbiAgICAgIHRpdGxlOiAnxJBp4buBdSBraGnhu4NuIGF1ZGlvJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnxJBp4buBdSBraGnhu4NuIGF1ZGlvIGNvbXBvbmVudCBxdWEgY8OhYyBBUEknXG4gICAgfSxcbiAgICB7XG4gICAgICBpZDogMixcbiAgICAgIGljb246ICfwn5SKJyxcbiAgICAgIHRpdGxlOiAnUXXhuqNuIGzDvSDDom0gbMaw4bujbmcnLFxuICAgICAgZGVzY3JpcHRpb246ICfEkGnhu4F1IGNo4buJbmggw6JtIGzGsOG7o25nIHbDoCB04bqvdCB0aeG6v25nIHbhu5tpIHRoYW5oIHRyxrDhu6N0IHRy4buxYyBxdWFuJ1xuICAgIH0sXG4gICAge1xuICAgICAgaWQ6IDMsXG4gICAgICBpY29uOiAn4o+x77iPJyxcbiAgICAgIHRpdGxlOiAnSGnhu4NuIHRo4buLIHRo4budaSBnaWFuJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGnhu4NuIHRo4buLIHRo4budaSBnaWFuIGhp4buHbiB04bqhaSB2w6AgdOG7lW5nIHRo4budaSBnaWFuIHRoZW8gxJHhu4tuaCBk4bqhbmcgSEg6TU06U1MnXG4gICAgfSxcbiAgICB7XG4gICAgICBpZDogNCxcbiAgICAgIGljb246ICfwn5OKJyxcbiAgICAgIHRpdGxlOiAnVGhhbmggdGnhur9uIMSR4buZJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhhbmggdGnhur9uIMSR4buZIGPDsyB0aOG7gyB0xrDGoW5nIHTDoWMgxJHhu4MgdHVhIG5oYW5oIGhv4bq3YyB0dWEgbOG6oWknXG4gICAgfSxcbiAgICB7XG4gICAgICBpZDogNSxcbiAgICAgIGljb246ICfwn5OlJyxcbiAgICAgIHRpdGxlOiAnVOG6o2kgeHXhu5FuZycsXG4gICAgICBkZXNjcmlwdGlvbjogJ0jhu5cgdHLhu6MgdOG6o2kgeHXhu5FuZyBmaWxlIMOibSB0aGFuaCB24bubaSBraeG7g20gc2/DoXQgcXV54buBbidcbiAgICB9XG4gIF0pO1xuXG4gIC8vIENvZGUgZXhhbXBsZXMgZGF0YVxuICBwdWJsaWMgY29kZUV4YW1wbGVzID0gc2lnbmFsPEFycmF5PHsgaWQ6IG51bWJlciwgdGl0bGU6IHN0cmluZywgY29kZTogc3RyaW5nIH0+PihbXG4gICAge1xuICAgICAgaWQ6IDEsXG4gICAgICB0aXRsZTogJ0PDoGkgxJHhurd0IGPGoSBi4bqjbicsXG4gICAgICBjb2RlOiBgJmx0O2xpYnNfdWktY29tcG9uZW50cy1hdWRpb1xuICBbZmlsZUF1ZGlvXT1cIidwYXRoL3RvL2F1ZGlvLm1wMydcIlxuICBbY2hlY2tQZXJtaXNzaW9uRG93bmxvYWRBdWRpb109XCJjaGVja1Blcm1pc3Npb25cIiZndDtcbiZsdDsvbGlic191aS1jb21wb25lbnRzLWF1ZGlvJmd0O2BcbiAgICB9LFxuICAgIHtcbiAgICAgIGlkOiAyLFxuICAgICAgdGl0bGU6ICdT4butIGThu6VuZyBGdW5jdGlvbiBDb250cm9sJyxcbiAgICAgIGNvZGU6IGBpbXBvcnQgeyBDb21wb25lbnQsIFZpZXdDaGlsZCwgc2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBMaWJzVWlDb21wb25lbnRzQXVkaW9Db21wb25lbnQgfSBmcm9tICdAbGlicy11aS9jb21wb25lbnRzLWF1ZGlvJztcbmltcG9ydCB7IElBdWRpb0Z1bmN0aW9uQ29udHJvbEV2ZW50IH0gZnJvbSAnQGxpYnMtdWkvY29tcG9uZW50cy1hdWRpbyc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2FwcC1teS1jb21wb25lbnQnLFxuICB0ZW1wbGF0ZTogXFxgXG4gICAgJmx0O2xpYnNfdWktY29tcG9uZW50cy1hdWRpb1xuICAgICAgI2F1ZGlvUGxheWVyXG4gICAgICBbZmlsZUF1ZGlvXT1cImF1ZGlvU291cmNlKClcIlxuICAgICAgW2NoZWNrUGVybWlzc2lvbkRvd25sb2FkQXVkaW9dPVwiY2hlY2tQZXJtaXNzaW9uXCJcbiAgICAgIChvdXRGdW5jdGlvbnNDb250cm9sKT1cInJlZ2lzdGVyRnVuY3Rpb25zKCRldmVudClcIiZndDtcbiAgICAmbHQ7L2xpYnNfdWktY29tcG9uZW50cy1hdWRpbyZndDtcbiAgICBcbiAgICAmbHQ7YnV0dG9uIChjbGljayk9XCJwbGF5QXVkaW8oKVwiJmd0O1Bow6F0L1ThuqFtIGThu6tuZyZsdDsvYnV0dG9uJmd0O1xuICBcXGBcbn0pXG5leHBvcnQgY2xhc3MgTXlDb21wb25lbnQge1xuICBAVmlld0NoaWxkKCdhdWRpb1BsYXllcicpIGF1ZGlvUGxheWVyITogTGlic1VpQ29tcG9uZW50c0F1ZGlvQ29tcG9uZW50O1xuICBhdWRpb1NvdXJjZSA9IHNpZ25hbDxzdHJpbmc+KCdwYXRoL3RvL2F1ZGlvLm1wMycpO1xuICBmdW5jdGlvbkNvbnRyb2xzOiBJQXVkaW9GdW5jdGlvbkNvbnRyb2xFdmVudCB8IG51bGwgPSBudWxsO1xuICBcbiAgcmVnaXN0ZXJGdW5jdGlvbnMoZXZlbnQ6IElBdWRpb0Z1bmN0aW9uQ29udHJvbEV2ZW50KSB7XG4gICAgdGhpcy5mdW5jdGlvbkNvbnRyb2xzID0gZXZlbnQ7XG4gIH1cbiAgXG4gIHBsYXlBdWRpbygpIHtcbiAgICBpZiAodGhpcy5mdW5jdGlvbkNvbnRyb2xzKSB7XG4gICAgICB0aGlzLmZ1bmN0aW9uQ29udHJvbHMucGxheVBhdXNlKCk7XG4gICAgfVxuICB9XG59YFxuICAgIH0sXG4gICAge1xuICAgICAgaWQ6IDMsXG4gICAgICB0aXRsZTogJ1Phu60gZOG7pW5nIEV2ZW50cyDEkeG7gyBj4bqtcCBuaOG6rXQgVUknLFxuICAgICAgY29kZTogYGltcG9ydCB7IENvbXBvbmVudCwgc2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBMaWJzVWlDb21wb25lbnRzQXVkaW9Db21wb25lbnQgfSBmcm9tICdAbGlicy11aS9jb21wb25lbnRzLWF1ZGlvJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYXBwLW15LWNvbXBvbmVudCcsXG4gIHRlbXBsYXRlOiBcXGBcbiAgICAmbHQ7bGlic191aS1jb21wb25lbnRzLWF1ZGlvXG4gICAgICBbZmlsZUF1ZGlvXT1cImF1ZGlvU291cmNlKClcIlxuICAgICAgW2NoZWNrUGVybWlzc2lvbkRvd25sb2FkQXVkaW9dPVwiY2hlY2tQZXJtaXNzaW9uXCJcbiAgICAgIChvdXRUaW1lVXBkYXRlKT1cImhhbmRsZVRpbWVVcGRhdGUoJGV2ZW50KVwiXG4gICAgICAob3V0Vm9sdW1lQ29udHJvbCk9XCJoYW5kbGVWb2x1bWVDaGFuZ2UoJGV2ZW50KVwiXG4gICAgICAob3V0UGxheSk9XCJoYW5kbGVQbGF5Q2hhbmdlKCRldmVudClcIlxuICAgICAgKG91dE11dGUpPVwiaGFuZGxlTXV0ZUNoYW5nZSgkZXZlbnQpXCJcbiAgICAgIChvdXRFbmRlZCk9XCJoYW5kbGVFbmRlZCgpXCImZ3Q7XG4gICAgJmx0Oy9saWJzX3VpLWNvbXBvbmVudHMtYXVkaW8mZ3Q7XG4gICAgXG4gICAgJmx0O2RpdiBjbGFzcz1cImF1ZGlvLWluZm9cIiZndDtcbiAgICAgICZsdDtwJmd0O1Ry4bqhbmcgdGjDoWk6IHt7IGlzUGxheWluZygpID8gJ8SQYW5nIHBow6F0JyA6ICdU4bqhbSBk4burbmcnIH19Jmx0Oy9wJmd0O1xuICAgICAgJmx0O3AmZ3Q7VGjhu51pIGdpYW4gaGnhu4duIHThuqFpOiB7eyBjdXJyZW50VGltZSgpIH19Jmx0Oy9wJmd0O1xuICAgICAgJmx0O3AmZ3Q7VOG7lW5nIHRo4budaSBnaWFuOiB7eyBkdXJhdGlvbigpIH19Jmx0Oy9wJmd0O1xuICAgICAgJmx0O3AmZ3Q7w4JtIGzGsOG7o25nOiB7eyB2b2x1bWVMZXZlbCgpIH19JSZsdDsvcCZndDtcbiAgICAmbHQ7L2RpdiZndDtcbiAgXFxgXG59KVxuZXhwb3J0IGNsYXNzIE15Q29tcG9uZW50IHtcbiAgYXVkaW9Tb3VyY2UgPSBzaWduYWw8c3RyaW5nPigncGF0aC90by9hdWRpby5tcDMnKTtcbiAgaXNQbGF5aW5nID0gc2lnbmFsPGJvb2xlYW4+KGZhbHNlKTtcbiAgaXNNdXRlZCA9IHNpZ25hbDxib29sZWFuPihmYWxzZSk7XG4gIGN1cnJlbnRUaW1lID0gc2lnbmFsPHN0cmluZz4oJzAwOjAwOjAwJyk7XG4gIGR1cmF0aW9uID0gc2lnbmFsPHN0cmluZz4oJzAwOjAwOjAwJyk7XG4gIHZvbHVtZUxldmVsID0gc2lnbmFsPG51bWJlcj4oMTAwKTtcbiAgXG4gIGNoZWNrUGVybWlzc2lvbiA9ICgpOiBQcm9taXNlPGJvb2xlYW4+ID0+IHtcbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHRydWUpO1xuICB9XG4gIFxuICBoYW5kbGVUaW1lVXBkYXRlKHRpbWVJbmZvOiB7IGN1cnJlbnRUaW1lOiBzdHJpbmcsIGR1cmF0aW9uOiBzdHJpbmcgfSkge1xuICAgIHRoaXMuY3VycmVudFRpbWUuc2V0KHRpbWVJbmZvLmN1cnJlbnRUaW1lKTtcbiAgICB0aGlzLmR1cmF0aW9uLnNldCh0aW1lSW5mby5kdXJhdGlvbik7XG4gIH1cbiAgXG4gIGhhbmRsZVZvbHVtZUNoYW5nZSh2b2x1bWU6IG51bWJlcikge1xuICAgIHRoaXMudm9sdW1lTGV2ZWwuc2V0KHZvbHVtZSk7XG4gIH1cbiAgXG4gIGhhbmRsZVBsYXlDaGFuZ2UoaXNQbGF5aW5nOiBib29sZWFuKSB7XG4gICAgdGhpcy5pc1BsYXlpbmcuc2V0KGlzUGxheWluZyk7XG4gIH1cbiAgXG4gIGhhbmRsZU11dGVDaGFuZ2UoaXNNdXRlZDogYm9vbGVhbikge1xuICAgIHRoaXMuaXNNdXRlZC5zZXQoaXNNdXRlZCk7XG4gIH1cbiAgXG4gIGhhbmRsZUVuZGVkKCkge1xuICAgIHRoaXMuaXNQbGF5aW5nLnNldChmYWxzZSk7XG4gIH1cbn1gXG4gICAgfVxuICBdKTtcblxuICAvLyBDb250cm9sIG1ldGhvZHNcbiAgcHVibGljIHBsYXlBdWRpbygpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5mdW5jdGlvbkNvbnRyb2xzKSB7XG4gICAgICB0aGlzLmZ1bmN0aW9uQ29udHJvbHMucGxheVBhdXNlKCk7XG4gICAgICB0aGlzLmlzUGxheWluZy5zZXQodGhpcy5mdW5jdGlvbkNvbnRyb2xzLmlzUGxheWluZygpKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgdG9nZ2xlTXV0ZSgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5mdW5jdGlvbkNvbnRyb2xzKSB7XG4gICAgICB0aGlzLmZ1bmN0aW9uQ29udHJvbHMudG9nZ2xlTXV0ZSgpO1xuICAgICAgdGhpcy5pc011dGVkLnNldCh0aGlzLmZ1bmN0aW9uQ29udHJvbHMuaXNNdXRlZCgpKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgY2hhbmdlVm9sdW1lKGV2ZW50OiBFdmVudCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmZ1bmN0aW9uQ29udHJvbHMgJiYgZXZlbnQudGFyZ2V0KSB7XG4gICAgICBjb25zdCB2YWx1ZSA9IHBhcnNlSW50KChldmVudC50YXJnZXQgYXMgSFRNTElucHV0RWxlbWVudCkudmFsdWUpO1xuICAgICAgdGhpcy52b2x1bWUuc2V0KHZhbHVlKTtcbiAgICAgIHRoaXMuZnVuY3Rpb25Db250cm9scy5zZXRWb2x1bWUodmFsdWUpO1xuICAgICAgdGhpcy5pc011dGVkLnNldCh0aGlzLmZ1bmN0aW9uQ29udHJvbHMuaXNNdXRlZCgpKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgZG93bmxvYWRBdWRpbygpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5mdW5jdGlvbkNvbnRyb2xzKSB7XG4gICAgICB0aGlzLmZ1bmN0aW9uQ29udHJvbHMuZG93bmxvYWQoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRm9ybWF0IHRo4budaSBnaWFuIHThu6sgZ2nDonkgc2FuZyBjaHXhu5dpIEhIOk1NOlNTXG4gICAqL1xuICBwdWJsaWMgZm9ybWF0VGltZSh0aW1lSW5TZWNvbmRzOiBudW1iZXIpOiBzdHJpbmcge1xuICAgIGlmIChpc05hTih0aW1lSW5TZWNvbmRzKSkgcmV0dXJuICcwMDowMDowMCc7XG5cbiAgICBjb25zdCBob3VycyA9IE1hdGguZmxvb3IodGltZUluU2Vjb25kcyAvIDM2MDApO1xuICAgIGNvbnN0IG1pbnV0ZXMgPSBNYXRoLmZsb29yKCh0aW1lSW5TZWNvbmRzIC0gKGhvdXJzICogMzYwMCkpIC8gNjApO1xuICAgIGNvbnN0IHNlY29uZHMgPSBNYXRoLmZsb29yKHRpbWVJblNlY29uZHMgLSAoaG91cnMgKiAzNjAwKSAtIChtaW51dGVzICogNjApKTtcblxuICAgIHJldHVybiBgJHtob3Vycy50b1N0cmluZygpLnBhZFN0YXJ0KDIsICcwJyl9OiR7bWludXRlcy50b1N0cmluZygpLnBhZFN0YXJ0KDIsICcwJyl9OiR7c2Vjb25kcy50b1N0cmluZygpLnBhZFN0YXJ0KDIsICcwJyl9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYW8gY2jDqXAgdGV4dCB2w6BvIGNsaXBib2FyZFxuICAgKi9cbiAgcHVibGljIGNvcHlUb0NsaXBib2FyZCh0ZXh0OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBuYXZpZ2F0b3IuY2xpcGJvYXJkLndyaXRlVGV4dCh0ZXh0KVxuICAgICAgLnRoZW4oKCkgPT4ge1xuICAgICAgICBhbGVydCgnxJDDoyBzYW8gY2jDqXAgdsOgbyBjbGlwYm9hcmQhJyk7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKGVyciA9PiB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBjb3B5OiAnLCBlcnIpO1xuICAgICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogxJDEg25nIGvDvSBjw6FjIGZ1bmN0aW9uIGNvbnRyb2wgdOG7qyBjb21wb25lbnRcbiAgICovXG4gIHB1YmxpYyByZWdpc3RlckZ1bmN0aW9ucyhldmVudDogSUF1ZGlvRnVuY3Rpb25Db250cm9sRXZlbnQpOiB2b2lkIHtcbiAgICB0aGlzLmZ1bmN0aW9uQ29udHJvbHMgPSBldmVudDtcblxuICAgIC8vIEluaXRpYWxpemUgc3RhdGVcbiAgICBpZiAodGhpcy5mdW5jdGlvbkNvbnRyb2xzKSB7XG4gICAgICB0aGlzLmlzUGxheWluZy5zZXQodGhpcy5mdW5jdGlvbkNvbnRyb2xzLmlzUGxheWluZygpKTtcbiAgICAgIHRoaXMuaXNNdXRlZC5zZXQodGhpcy5mdW5jdGlvbkNvbnRyb2xzLmlzTXV0ZWQoKSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFjhu60gbMO9IHPhu7Ega2nhu4duIHRoYXkgxJHhu5VpIHRo4budaSBnaWFuXG4gICAqL1xuICBwdWJsaWMgaGFuZGxlVGltZVVwZGF0ZSh0aW1lSW5mbzogeyBjdXJyZW50VGltZTogc3RyaW5nLCBkdXJhdGlvbjogc3RyaW5nIH0pOiB2b2lkIHtcbiAgICB0aGlzLmN1cnJlbnRUaW1lLnNldCh0aW1lSW5mby5jdXJyZW50VGltZSk7XG4gICAgdGhpcy5kdXJhdGlvbi5zZXQodGltZUluZm8uZHVyYXRpb24pO1xuXG4gICAgLy8gQ2FsY3VsYXRlIHByb2dyZXNzIGJhc2VkIG9uIGN1cnJlbnRUaW1lIGFuZCBkdXJhdGlvblxuICAgIGNvbnN0IHJlZ2V4ID0gLyhcXGQrKTooXFxkKyk6KFxcZCspLztcbiAgICBjb25zdCBjdXJyZW50TWF0Y2hlcyA9IHRpbWVJbmZvLmN1cnJlbnRUaW1lLm1hdGNoKHJlZ2V4KTtcbiAgICBjb25zdCBkdXJhdGlvbk1hdGNoZXMgPSB0aW1lSW5mby5kdXJhdGlvbi5tYXRjaChyZWdleCk7XG5cbiAgICBpZiAoY3VycmVudE1hdGNoZXMgJiYgZHVyYXRpb25NYXRjaGVzKSB7XG4gICAgICBjb25zdCBjdXJyZW50U2Vjb25kcyA9XG4gICAgICAgIHBhcnNlSW50KGN1cnJlbnRNYXRjaGVzWzFdKSAqIDM2MDAgK1xuICAgICAgICBwYXJzZUludChjdXJyZW50TWF0Y2hlc1syXSkgKiA2MCArXG4gICAgICAgIHBhcnNlSW50KGN1cnJlbnRNYXRjaGVzWzNdKTtcblxuICAgICAgY29uc3QgdG90YWxTZWNvbmRzID1cbiAgICAgICAgcGFyc2VJbnQoZHVyYXRpb25NYXRjaGVzWzFdKSAqIDM2MDAgK1xuICAgICAgICBwYXJzZUludChkdXJhdGlvbk1hdGNoZXNbMl0pICogNjAgK1xuICAgICAgICBwYXJzZUludChkdXJhdGlvbk1hdGNoZXNbM10pO1xuXG4gICAgICBpZiAodG90YWxTZWNvbmRzID4gMCkge1xuICAgICAgICB0aGlzLnByb2dyZXNzLnNldChNYXRoLmZsb29yKChjdXJyZW50U2Vjb25kcyAvIHRvdGFsU2Vjb25kcykgKiAxMDApKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogWOG7rSBsw70gc+G7sSBraeG7h24gdGhheSDEkeG7lWkgw6JtIGzGsOG7o25nXG4gICAqL1xuICBwdWJsaWMgaGFuZGxlVm9sdW1lQ2hhbmdlKHZvbHVtZTogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy52b2x1bWUuc2V0KHZvbHVtZSk7XG4gIH1cblxuICAvKipcbiAgICogWOG7rSBsw70gc+G7sSBraeG7h24gdGhheSDEkeG7lWkgdHLhuqFuZyB0aMOhaSBwaMOhdFxuICAgKi9cbiAgcHVibGljIGhhbmRsZVBsYXlDaGFuZ2UoaXNQbGF5aW5nOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5pc1BsYXlpbmcuc2V0KGlzUGxheWluZyk7XG4gIH1cblxuICAvKipcbiAgICogWOG7rSBsw70gc+G7sSBraeG7h24gdGhheSDEkeG7lWkgdHLhuqFuZyB0aMOhaSB04bqvdCB0aeG6v25nXG4gICAqL1xuICBwdWJsaWMgaGFuZGxlTXV0ZUNoYW5nZShpc011dGVkOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5pc011dGVkLnNldChpc011dGVkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBY4butIGzDvSBz4buxIGtp4buHbiBr4bq/dCB0aMO6YyBwaMOhdFxuICAgKi9cbiAgcHVibGljIGhhbmRsZUVuZGVkKCk6IHZvaWQge1xuICAgIHRoaXMuaXNQbGF5aW5nLnNldChmYWxzZSk7XG4gIH1cblxuICAvKipcbiAgICogQ2jhu41uIG3huqt1IMOibSB0aGFuaFxuICAgKi9cbiAgcHVibGljIHNlbGVjdEF1ZGlvKGlkOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnNlbGVjdGVkQXVkaW8uc2V0KGlkKTtcbiAgICBjb25zdCBzYW1wbGUgPSB0aGlzLmF1ZGlvU2FtcGxlcygpLmZpbmQoYXVkaW8gPT4gYXVkaW8uaWQgPT09IGlkKTtcbiAgICBpZiAoc2FtcGxlKSB7XG4gICAgICAvLyBSZXNldCBhdWRpbyBzdGF0ZVxuICAgICAgdGhpcy5pc1BsYXlpbmcuc2V0KGZhbHNlKTtcbiAgICAgIHRoaXMucHJvZ3Jlc3Muc2V0KDApO1xuICAgICAgdGhpcy5jdXJyZW50VGltZS5zZXQoJzAwOjAwOjAwJyk7XG4gICAgICB0aGlzLmR1cmF0aW9uLnNldCgnMDA6MDA6MDAnKTtcblxuICAgICAgLy8gU2ltcGx5IHVwZGF0ZSB0aGUgc291cmNlIC0gdGhlIGNvbXBvbmVudCB3aWxsIGhhbmRsZSB0aGUgcmVsb2FkXG4gICAgICB0aGlzLmN1cnJlbnRBdWRpb1NvdXJjZS5zZXQoc2FtcGxlLnNyYyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEtp4buDbSB0cmEgcXV54buBbiB04bqjaSB4deG7kW5nXG4gICAqL1xuICBwdWJsaWMgY2hlY2tEb3dubG9hZFBlcm1pc3Npb24gPSAoKTogUHJvbWlzZTxib29sZWFuPiA9PiB7XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh0cnVlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGF5IMSR4buVaSB24buLIHRyw60gcGjDoXQgYuG6sW5nIGPDoWNoIGNsaWNrIHRy4buxYyB0aeG6v3AgdsOgbyB0aGFuaCB0aeG6v24gxJHhu5lcbiAgICovXG4gIHB1YmxpYyBzZWVrQXVkaW9CeUNsaWNrKGV2ZW50OiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgaWYgKHRoaXMuZnVuY3Rpb25Db250cm9scykge1xuICAgICAgY29uc3QgcHJvZ3Jlc3NCYXIgPSBldmVudC5jdXJyZW50VGFyZ2V0IGFzIEhUTUxFbGVtZW50O1xuICAgICAgY29uc3QgcmVjdCA9IHByb2dyZXNzQmFyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgY29uc3Qgb2Zmc2V0WCA9IGV2ZW50LmNsaWVudFggLSByZWN0LmxlZnQ7XG4gICAgICBjb25zdCBwZXJjZW50WCA9IChvZmZzZXRYIC8gcmVjdC53aWR0aCkgKiAxMDA7XG5cbiAgICAgIC8vIMSQ4bqjbSBi4bqjbyBnacOhIHRy4buLIG7hurFtIHRyb25nIGtob+G6o25nIDAtMTAwXG4gICAgICBjb25zdCBjbGFtcGVkUGVyY2VudCA9IE1hdGgubWF4KDAsIE1hdGgubWluKDEwMCwgcGVyY2VudFgpKTtcbiAgICAgIHRoaXMucHJvZ3Jlc3Muc2V0KE1hdGgucm91bmQoY2xhbXBlZFBlcmNlbnQpKTtcblxuICAgICAgLy8gR+G7jWkgbWV0aG9kIHNlZWtUbyDEkeG7gyBj4bqtcCBuaOG6rXQgduG7iyB0csOtIHBow6F0XG4gICAgICB0aGlzLmZ1bmN0aW9uQ29udHJvbHMuc2Vla1RvKHRoaXMucHJvZ3Jlc3MoKSk7XG4gICAgfVxuICB9XG59IFxuIiwiPGRpdiBjbGFzcz1cIm1heC13LTd4bCBteC1hdXRvIHB4LTQgc206cHgtNiBsZzpweC04IGZvbnQtc2FucyB0ZXh0LWdyYXktODAwXCI+XG4gIDxoZWFkZXIgY2xhc3M9XCJ0ZXh0LWNlbnRlciBweS0xMCBiZy13aGl0ZSByb3VuZGVkLWxnIHNoYWRvdyBtYi04XCI+XG4gICAgPGgxIGNsYXNzPVwidGV4dC00eGwgZm9udC1ib2xkIHRleHQtZ3JheS04MDAgbWItMlwiPkRlbW8gVHLDrG5oIFBow6F0IMOCbSBUaGFuaDwvaDE+XG4gICAgPHAgY2xhc3M9XCJ0ZXh0LXhsIHRleHQtZ3JheS02MDBcIj5UaMawIHZp4buHbiBjb21wb25lbnQgY2hvIEFuZ3VsYXIgxJHhu4MgcGjDoXQgdsOgIMSRaeG7gXUga2hp4buDbiDDom0gdGhhbmg8L3A+XG4gIDwvaGVhZGVyPlxuXG4gIDxtYWluPlxuICAgIDxzZWN0aW9uIGNsYXNzPVwiYmctd2hpdGUgcm91bmRlZC1sZyBzaGFkb3cgcC04IG1iLThcIj5cbiAgICAgIDxoMiBjbGFzcz1cInRleHQtMnhsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTQgcGItMiBib3JkZXItYiBib3JkZXItZ3JheS0yMDBcIj5HaeG7m2kgdGhp4buHdTwvaDI+XG4gICAgICA8cCBjbGFzcz1cInRleHQtbGdcIj5cbiAgICAgICAgPGNvZGUgY2xhc3M9XCJiZy1ncmF5LTEwMCBweC0xIHB5LTAuNSByb3VuZGVkIHRleHQtc20gZm9udC1tb25vXCI+JiM2NDtsaWJzLXVpL2NvbXBvbmVudHMtYXVkaW88L2NvZGU+IGzDoCBt4buZdFxuICAgICAgICBjb21wb25lbnQgQW5ndWxhciBt4bqhbmggbeG6vSBjaG8gcGjDqXAgbmfGsOG7nWkgZMO5bmcgcGjDoXQsIHThuqFtIGThu6tuZyB2w6AgxJFp4buBdSBraGnhu4NuIGPDoWMgdOG7h3Agw6JtIHRoYW5oIHbhu5tpIG5oaeG7gXUgdMOtbmggbsSDbmcuXG4gICAgICA8L3A+XG4gICAgPC9zZWN0aW9uPlxuXG4gICAgPHNlY3Rpb24gY2xhc3M9XCJiZy13aGl0ZSByb3VuZGVkLWxnIHNoYWRvdyBwLTggbWItOFwiPlxuICAgICAgPGgyIGNsYXNzPVwidGV4dC0yeGwgZm9udC1ib2xkIHRleHQtZ3JheS04MDAgbWItNCBwYi0yIGJvcmRlci1iIGJvcmRlci1ncmF5LTIwMFwiPkPDoGkgxJHhurd0PC9oMj5cbiAgICAgIDxwPsSQ4buDIGPDoGkgxJHhurd0IHRoxrAgdmnhu4duLCBz4butIGThu6VuZyBucG0gaG/hurdjIHlhcm46PC9wPlxuXG4gICAgICA8ZGl2IGNsYXNzPVwicmVsYXRpdmUgbXktNFwiPlxuICAgICAgICA8cHJlXG4gICAgICAgICAgY2xhc3M9XCJiZy1ncmF5LTEwMCBwLTQgcm91bmRlZCBvdmVyZmxvdy14LWF1dG8gbWItNFwiPjxjb2RlIGNsYXNzPVwiZm9udC1tb25vXCI+bnBtIGluc3RhbGwgJiM2NDtsaWJzLXVpL2NvbXBvbmVudHMtYXVkaW88L2NvZGU+PC9wcmU+XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICBjbGFzcz1cImFic29sdXRlIHRvcC0yIHJpZ2h0LTIgYmctYmx1ZS01MDAgdGV4dC13aGl0ZSBweC0zIHB5LTEgcm91bmRlZCB0ZXh0LXNtIGhvdmVyOmJnLWJsdWUtNjAwIHRyYW5zaXRpb25cIlxuICAgICAgICAgIChjbGljayk9XCJjb3B5VG9DbGlwYm9hcmQoJ25wbSBpbnN0YWxsIEBsaWJzLXVpL2NvbXBvbmVudHMtYXVkaW8nKVwiPlxuICAgICAgICAgIFNhbyBjaMOpcFxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8cD5Ib+G6t2MgduG7m2kgeWFybjo8L3A+XG5cbiAgICAgIDxkaXYgY2xhc3M9XCJyZWxhdGl2ZSBteS00XCI+XG4gICAgICAgIDxwcmVcbiAgICAgICAgICBjbGFzcz1cImJnLWdyYXktMTAwIHAtNCByb3VuZGVkIG92ZXJmbG93LXgtYXV0byBtYi00XCI+PGNvZGUgY2xhc3M9XCJmb250LW1vbm9cIj55YXJuIGFkZCAmIzY0O2xpYnMtdWkvY29tcG9uZW50cy1hdWRpbzwvY29kZT48L3ByZT5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgIGNsYXNzPVwiYWJzb2x1dGUgdG9wLTIgcmlnaHQtMiBiZy1ibHVlLTUwMCB0ZXh0LXdoaXRlIHB4LTMgcHktMSByb3VuZGVkIHRleHQtc20gaG92ZXI6YmctYmx1ZS02MDAgdHJhbnNpdGlvblwiXG4gICAgICAgICAgKGNsaWNrKT1cImNvcHlUb0NsaXBib2FyZCgneWFybiBhZGQgQGxpYnMtdWkvY29tcG9uZW50cy1hdWRpbycpXCI+XG4gICAgICAgICAgU2FvIGNow6lwXG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgPC9kaXY+XG4gICAgPC9zZWN0aW9uPlxuXG4gICAgPHNlY3Rpb24gY2xhc3M9XCJiZy13aGl0ZSByb3VuZGVkLWxnIHNoYWRvdyBwLTggbWItOFwiPlxuICAgICAgPGgyIGNsYXNzPVwidGV4dC0yeGwgZm9udC1ib2xkIHRleHQtZ3JheS04MDAgbWItNCBwYi0yIGJvcmRlci1iIGJvcmRlci1ncmF5LTIwMFwiPkRlbW8gdHLhu7FjIHRp4bq/cDwvaDI+XG4gICAgICA8ZGl2IGNsYXNzPVwiYmctZ3JheS01MCBwLTYgcm91bmRlZC1sZ1wiPlxuICAgICAgICA8cCBjbGFzcz1cIm1iLTRcIj5UaOG7rSBuZ2hp4buHbSB0csOsbmggcGjDoXQgw6JtIHRoYW5oIHbhu5tpIGPDoWMgZmlsZSBt4bqrdTo8L3A+XG5cbiAgICAgICAgPGRpdiBjbGFzcz1cIm1iLTZcIj5cbiAgICAgICAgICA8aDMgY2xhc3M9XCJ0ZXh0LXhsIGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMCBtYi0zXCI+Q2jhu41uIGZpbGUgw6JtIHRoYW5oOjwvaDM+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImZsZXggZmxleC1jb2wgZ2FwLTJcIj5cbiAgICAgICAgICAgIEBmb3IgKGF1ZGlvIG9mIGF1ZGlvU2FtcGxlcygpOyB0cmFjayBhdWRpby5pZCkge1xuICAgICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgY2xhc3M9XCJmbGV4IGp1c3RpZnktYmV0d2VlbiBwLTMgYmctd2hpdGUgcm91bmRlZCBib3JkZXIgY3Vyc29yLXBvaW50ZXIgdHJhbnNpdGlvbiBob3ZlcjpiZy1ibHVlLTUwIGhvdmVyOmJvcmRlci1ibHVlLTUwMFwiXG4gICAgICAgICAgICAgICAgW2NsYXNzLmJnLWJsdWUtNTBdPVwic2VsZWN0ZWRBdWRpbygpID09PSBhdWRpby5pZFwiXG4gICAgICAgICAgICAgICAgW2NsYXNzLmJvcmRlci1ibHVlLTUwMF09XCJzZWxlY3RlZEF1ZGlvKCkgPT09IGF1ZGlvLmlkXCJcbiAgICAgICAgICAgICAgICBbY2xhc3MuZm9udC1tZWRpdW1dPVwic2VsZWN0ZWRBdWRpbygpID09PSBhdWRpby5pZFwiXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cInNlbGVjdEF1ZGlvKGF1ZGlvLmlkKVwiPlxuICAgICAgICAgICAgICAgIDxzcGFuPnt7IGF1ZGlvLm5hbWUgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgPHNwYW4+e3sgYXVkaW8uZHVyYXRpb24gfX08L3NwYW4+XG4gICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgfVxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwibWItNlwiPlxuICAgICAgICAgIDxoMyBjbGFzcz1cInRleHQteGwgZm9udC1zZW1pYm9sZCB0ZXh0LWdyYXktNzAwIG1iLTNcIj5UcsOsbmggcGjDoXQ6PC9oMz5cbiAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWF1ZGlvICNhdWRpb1BsYXllclxuICAgICAgICAgICAgW2ZpbGVBdWRpb109XCJjdXJyZW50QXVkaW9Tb3VyY2UoKVwiXG4gICAgICAgICAgICBbY2hlY2tQZXJtaXNzaW9uRG93bmxvYWRBdWRpb109XCJjaGVja0Rvd25sb2FkUGVybWlzc2lvblwiXG4gICAgICAgICAgICAob3V0RnVuY3Rpb25zQ29udHJvbCk9XCJyZWdpc3RlckZ1bmN0aW9ucygkZXZlbnQpXCJcbiAgICAgICAgICAgIChvdXRUaW1lVXBkYXRlKT1cImhhbmRsZVRpbWVVcGRhdGUoJGV2ZW50KVwiXG4gICAgICAgICAgICAob3V0Vm9sdW1lQ29udHJvbCk9XCJoYW5kbGVWb2x1bWVDaGFuZ2UoJGV2ZW50KVwiXG4gICAgICAgICAgICAob3V0UGxheSk9XCJoYW5kbGVQbGF5Q2hhbmdlKCRldmVudClcIlxuICAgICAgICAgICAgKG91dE11dGUpPVwiaGFuZGxlTXV0ZUNoYW5nZSgkZXZlbnQpXCJcbiAgICAgICAgICAgIChvdXRFbmRlZCk9XCJoYW5kbGVFbmRlZCgpXCI+XG4gICAgICAgICAgPC9saWJzX3VpLWNvbXBvbmVudHMtYXVkaW8+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxkaXYgY2xhc3M9XCJtYi02XCI+XG4gICAgICAgICAgPGgzIGNsYXNzPVwidGV4dC14bCBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDAgbWItM1wiPsSQaeG7gXUga2hp4buDbiBxdWEgRnVuY3Rpb24gQ29udHJvbDo8L2gzPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJmbGV4IGZsZXgtd3JhcCBnYXAtMyBpdGVtcy1jZW50ZXIgYmctd2hpdGUgcC00IHJvdW5kZWQgYm9yZGVyXCI+XG4gICAgICAgICAgICA8YnV0dG9uIGNsYXNzPVwicHgtNCBweS0yIGJnLWJsdWUtNTAwIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gcm91bmRlZCBob3ZlcjpiZy1ibHVlLTYwMCB0cmFuc2l0aW9uXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cInBsYXlBdWRpbygpXCI+XG4gICAgICAgICAgICAgIFBow6F0L1ThuqFtIGThu6tuZ1xuICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICA8YnV0dG9uIGNsYXNzPVwicHgtNCBweS0yIGJnLWJsdWUtNTAwIHRleHQtd2hpdGUgZm9udC1tZWRpdW0gcm91bmRlZCBob3ZlcjpiZy1ibHVlLTYwMCB0cmFuc2l0aW9uXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cInRvZ2dsZU11dGUoKVwiPlxuICAgICAgICAgICAgICB7eyBpc011dGVkKCkgPyAnQuG6rXQgdGnhur9uZycgOiAnVOG6r3QgdGnhur9uZycgfX1cbiAgICAgICAgICAgIDwvYnV0dG9uPlxuXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTIgbWwtMlwiPlxuICAgICAgICAgICAgICA8c3Bhbj7Dgm0gbMaw4bujbmc6PC9zcGFuPlxuICAgICAgICAgICAgICA8aW5wdXQgdHlwZT1cInJhbmdlXCJcbiAgICAgICAgICAgICAgICBtaW49XCIwXCJcbiAgICAgICAgICAgICAgICBtYXg9XCIxMDBcIlxuICAgICAgICAgICAgICAgIFt2YWx1ZV09XCJ2b2x1bWVQZXJjZW50KClcIlxuICAgICAgICAgICAgICAgIChpbnB1dCk9XCJjaGFuZ2VWb2x1bWUoJGV2ZW50KVwiXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJ3LTI0IHZvbHVtZS1zbGlkZXJcIlxuICAgICAgICAgICAgICAgIFtzdHlsZS4tLXZvbHVtZS1wZXJjZW50LiVdPVwidm9sdW1lUGVyY2VudCgpXCI+XG4gICAgICAgICAgICAgIDxzcGFuPnt7IHZvbHVtZVBlcmNlbnQoKSB9fSU8L3NwYW4+XG4gICAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInctZnVsbCBtdC0yXCI+XG4gICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmbGV4IGl0ZW1zLWNlbnRlciBnYXAtMlwiPlxuICAgICAgICAgICAgICAgIDxzcGFuPlRp4bq/biDEkeG7mTo8L3NwYW4+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInJlbGF0aXZlIGgtMiBiZy1ncmF5LTIwMCByb3VuZGVkLWZ1bGwgZmxleC0xIGN1cnNvci1wb2ludGVyXCJcbiAgICAgICAgICAgICAgICAgIChjbGljayk9XCJzZWVrQXVkaW9CeUNsaWNrKCRldmVudClcIj5cbiAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJhYnNvbHV0ZSB0b3AtMCBsZWZ0LTAgaC1mdWxsIGJnLWJsdWUtNTAwIHJvdW5kZWQtZnVsbFwiXG4gICAgICAgICAgICAgICAgICAgIFtzdHlsZS53aWR0aC4lXT1cInByb2dyZXNzKClcIj48L2Rpdj5cbiAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICA8c3Bhbj57eyBwcm9ncmVzcygpIH19JTwvc3Bhbj5cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgICAgPGJ1dHRvbiBjbGFzcz1cInB4LTQgcHktMiBiZy1ncmVlbi01MDAgdGV4dC13aGl0ZSBmb250LW1lZGl1bSByb3VuZGVkIGhvdmVyOmJnLWdyZWVuLTYwMCB0cmFuc2l0aW9uIG10LTRcIlxuICAgICAgICAgICAgICAoY2xpY2spPVwiZG93bmxvYWRBdWRpbygpXCI+XG4gICAgICAgICAgICAgIFThuqNpIHh14buRbmdcbiAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm10LTZcIj5cbiAgICAgICAgICAgIDxoMyBjbGFzcz1cInRleHQteGwgZm9udC1zZW1pYm9sZCB0ZXh0LWdyYXktNzAwIG1iLTNcIj5UcuG6oW5nIHRow6FpOjwvaDM+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiYmctd2hpdGUgcC00IHJvdW5kZWQgYm9yZGVyXCI+XG4gICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJtYi0yXCI+XG4gICAgICAgICAgICAgICAgPHN0cm9uZz5UcuG6oW5nIHRow6FpOjwvc3Ryb25nPlxuICAgICAgICAgICAgICAgIDxzcGFuIFtjbGFzcy50ZXh0LWdyZWVuLTYwMF09XCJpc1BsYXlpbmcoKVwiXG4gICAgICAgICAgICAgICAgICBbY2xhc3MudGV4dC1yZWQtNjAwXT1cIiFpc1BsYXlpbmcoKVwiPlxuICAgICAgICAgICAgICAgICAge3sgaXNQbGF5aW5nKCkgPyAnxJBhbmcgcGjDoXQnIDogJ1ThuqFtIGThu6tuZycgfX1cbiAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWItMlwiPlxuICAgICAgICAgICAgICAgIDxzdHJvbmc+VGjhu51pIGdpYW4gaGnhu4duIHThuqFpOjwvc3Ryb25nPlxuICAgICAgICAgICAgICAgIDxzcGFuPnt7IGN1cnJlbnRUaW1lKCkgfX08L3NwYW4+XG4gICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWItMlwiPlxuICAgICAgICAgICAgICAgIDxzdHJvbmc+VOG7lW5nIHRo4budaSBnaWFuOjwvc3Ryb25nPlxuICAgICAgICAgICAgICAgIDxzcGFuPnt7IGR1cmF0aW9uKCkgfX08L3NwYW4+XG4gICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWItMlwiPlxuICAgICAgICAgICAgICAgIDxzdHJvbmc+VGnhur9uIMSR4buZOjwvc3Ryb25nPlxuICAgICAgICAgICAgICAgIDxzcGFuPnt7IHByb2dyZXNzKCkgfX0lPC9zcGFuPlxuICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvc2VjdGlvbj5cblxuICAgIDxzZWN0aW9uIGNsYXNzPVwiYmctd2hpdGUgcm91bmRlZC1sZyBzaGFkb3cgcC04IG1iLThcIj5cbiAgICAgIDxoMiBjbGFzcz1cInRleHQtMnhsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTQgcGItMiBib3JkZXItYiBib3JkZXItZ3JheS0yMDBcIj5Uw6BpIGxp4buHdSBBUEk8L2gyPlxuXG4gICAgICA8aDMgY2xhc3M9XCJ0ZXh0LXhsIGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMCBtYi0zXCI+SW5wdXRzPC9oMz5cbiAgICAgIDxkaXYgY2xhc3M9XCJvdmVyZmxvdy14LWF1dG8gbWItOFwiPlxuICAgICAgICA8dGFibGUgY2xhc3M9XCJtaW4tdy1mdWxsIGJnLXdoaXRlXCI+XG4gICAgICAgICAgPHRoZWFkIGNsYXNzPVwiYmctZ3JheS0xMDBcIj5cbiAgICAgICAgICAgIDx0cj5cbiAgICAgICAgICAgICAgPHRoIGNsYXNzPVwicHktMyBweC00IHRleHQtbGVmdFwiPlTDqm48L3RoPlxuICAgICAgICAgICAgICA8dGggY2xhc3M9XCJweS0zIHB4LTQgdGV4dC1sZWZ0XCI+S2nhu4N1IGThu68gbGnhu4d1PC90aD5cbiAgICAgICAgICAgICAgPHRoIGNsYXNzPVwicHktMyBweC00IHRleHQtbGVmdFwiPk3hurdjIMSR4buLbmg8L3RoPlxuICAgICAgICAgICAgICA8dGggY2xhc3M9XCJweS0zIHB4LTQgdGV4dC1sZWZ0XCI+TcO0IHThuqM8L3RoPlxuICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICA8L3RoZWFkPlxuICAgICAgICAgIDx0Ym9keT5cbiAgICAgICAgICAgIEBmb3IgKGlucHV0IG9mIGlucHV0c0RvYygpOyB0cmFjayBpbnB1dC5uYW1lKSB7XG4gICAgICAgICAgICAgIDx0ciBjbGFzcz1cImJvcmRlci1iIGhvdmVyOmJnLWdyYXktNTBcIj5cbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJweS0zIHB4LTRcIj48Y29kZVxuICAgICAgICAgICAgICAgICAgICBjbGFzcz1cImJnLWdyYXktMTAwIHB4LTEgcHktMC41IHJvdW5kZWQgdGV4dC1zbSBmb250LW1vbm9cIj57eyBpbnB1dC5uYW1lIH19PC9jb2RlPjwvdGQ+XG4gICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwicHktMyBweC00XCI+PGNvZGVcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJiZy1ncmF5LTEwMCBweC0xIHB5LTAuNSByb3VuZGVkIHRleHQtc20gZm9udC1tb25vXCI+e3sgaW5wdXQudHlwZSB9fTwvY29kZT48L3RkPlxuICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInB5LTMgcHgtNFwiPnt7IGlucHV0LmRlZmF1bHQgfX08L3RkPlxuICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInB5LTMgcHgtNFwiPnt7IGlucHV0LmRlc2NyaXB0aW9uIH19PC90ZD5cbiAgICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICA8L3Rib2R5PlxuICAgICAgICA8L3RhYmxlPlxuICAgICAgPC9kaXY+XG5cbiAgICAgIDxoMyBjbGFzcz1cInRleHQteGwgZm9udC1zZW1pYm9sZCB0ZXh0LWdyYXktNzAwIG1iLTNcIj5PdXRwdXRzPC9oMz5cbiAgICAgIDxkaXYgY2xhc3M9XCJvdmVyZmxvdy14LWF1dG8gbWItOFwiPlxuICAgICAgICA8dGFibGUgY2xhc3M9XCJtaW4tdy1mdWxsIGJnLXdoaXRlXCI+XG4gICAgICAgICAgPHRoZWFkIGNsYXNzPVwiYmctZ3JheS0xMDBcIj5cbiAgICAgICAgICAgIDx0cj5cbiAgICAgICAgICAgICAgPHRoIGNsYXNzPVwicHktMyBweC00IHRleHQtbGVmdFwiPlTDqm48L3RoPlxuICAgICAgICAgICAgICA8dGggY2xhc3M9XCJweS0zIHB4LTQgdGV4dC1sZWZ0XCI+S2nhu4N1IGThu68gbGnhu4d1PC90aD5cbiAgICAgICAgICAgICAgPHRoIGNsYXNzPVwicHktMyBweC00IHRleHQtbGVmdFwiPk3DtCB04bqjPC90aD5cbiAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgPC90aGVhZD5cbiAgICAgICAgICA8dGJvZHk+XG4gICAgICAgICAgICBAZm9yIChvdXRwdXQgb2Ygb3V0cHV0c0RvYygpOyB0cmFjayBvdXRwdXQubmFtZSkge1xuICAgICAgICAgICAgICA8dHIgY2xhc3M9XCJib3JkZXItYiBob3ZlcjpiZy1ncmF5LTUwXCI+XG4gICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwicHktMyBweC00XCI+PGNvZGVcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJiZy1ncmF5LTEwMCBweC0xIHB5LTAuNSByb3VuZGVkIHRleHQtc20gZm9udC1tb25vXCI+e3sgb3V0cHV0Lm5hbWUgfX08L2NvZGU+PC90ZD5cbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJweS0zIHB4LTRcIj48Y29kZVxuICAgICAgICAgICAgICAgICAgICBjbGFzcz1cImJnLWdyYXktMTAwIHB4LTEgcHktMC41IHJvdW5kZWQgdGV4dC1zbSBmb250LW1vbm9cIj57eyBvdXRwdXQudHlwZSB9fTwvY29kZT48L3RkPlxuICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInB5LTMgcHgtNFwiPnt7IG91dHB1dC5kZXNjcmlwdGlvbiB9fTwvdGQ+XG4gICAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgPC90Ym9keT5cbiAgICAgICAgPC90YWJsZT5cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8aDMgY2xhc3M9XCJ0ZXh0LXhsIGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMCBtYi0zXCI+RnVuY3Rpb24gQ29udHJvbCBNZXRob2RzPC9oMz5cbiAgICAgIDxkaXYgY2xhc3M9XCJvdmVyZmxvdy14LWF1dG8gbWItOFwiPlxuICAgICAgICA8dGFibGUgY2xhc3M9XCJtaW4tdy1mdWxsIGJnLXdoaXRlXCI+XG4gICAgICAgICAgPHRoZWFkIGNsYXNzPVwiYmctZ3JheS0xMDBcIj5cbiAgICAgICAgICAgIDx0cj5cbiAgICAgICAgICAgICAgPHRoIGNsYXNzPVwicHktMyBweC00IHRleHQtbGVmdFwiPlTDqm48L3RoPlxuICAgICAgICAgICAgICA8dGggY2xhc3M9XCJweS0zIHB4LTQgdGV4dC1sZWZ0XCI+VGhhbSBz4buRPC90aD5cbiAgICAgICAgICAgICAgPHRoIGNsYXNzPVwicHktMyBweC00IHRleHQtbGVmdFwiPktp4buDdSB0cuG6oyB24buBPC90aD5cbiAgICAgICAgICAgICAgPHRoIGNsYXNzPVwicHktMyBweC00IHRleHQtbGVmdFwiPk3DtCB04bqjPC90aD5cbiAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgPC90aGVhZD5cbiAgICAgICAgICA8dGJvZHk+XG4gICAgICAgICAgICBAZm9yIChtZXRob2Qgb2YgbWV0aG9kc0RvYygpOyB0cmFjayBtZXRob2QubmFtZSkge1xuICAgICAgICAgICAgICA8dHIgY2xhc3M9XCJib3JkZXItYiBob3ZlcjpiZy1ncmF5LTUwXCI+XG4gICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwicHktMyBweC00XCI+PGNvZGVcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJiZy1ncmF5LTEwMCBweC0xIHB5LTAuNSByb3VuZGVkIHRleHQtc20gZm9udC1tb25vXCI+e3sgbWV0aG9kLm5hbWUgfX08L2NvZGU+PC90ZD5cbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJweS0zIHB4LTRcIj48Y29kZVxuICAgICAgICAgICAgICAgICAgICBjbGFzcz1cImJnLWdyYXktMTAwIHB4LTEgcHktMC41IHJvdW5kZWQgdGV4dC1zbSBmb250LW1vbm9cIj57eyBtZXRob2QucGFyYW1zIH19PC9jb2RlPjwvdGQ+XG4gICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwicHktMyBweC00XCI+PGNvZGVcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJiZy1ncmF5LTEwMCBweC0xIHB5LTAuNSByb3VuZGVkIHRleHQtc20gZm9udC1tb25vXCI+e3sgbWV0aG9kLnJldHVyblR5cGUgfX08L2NvZGU+PC90ZD5cbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJweS0zIHB4LTRcIj57eyBtZXRob2QuZGVzY3JpcHRpb24gfX08L3RkPlxuICAgICAgICAgICAgICA8L3RyPlxuICAgICAgICAgICAgfVxuICAgICAgICAgIDwvdGJvZHk+XG4gICAgICAgIDwvdGFibGU+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPGgzIGNsYXNzPVwidGV4dC14bCBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDAgbWItM1wiPkludGVyZmFjZXM8L2gzPlxuICAgICAgPGRpdiBjbGFzcz1cIm92ZXJmbG93LXgtYXV0byBtYi04XCI+XG4gICAgICAgIEBmb3IgKGludGVyZmFjZURvYyBvZiBpbnRlcmZhY2VzRG9jKCk7IHRyYWNrIGludGVyZmFjZURvYy5uYW1lKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm1iLTYgYmctd2hpdGUgcC00IHJvdW5kZWQgYm9yZGVyXCI+XG4gICAgICAgICAgICA8aDQgY2xhc3M9XCJ0ZXh0LWxnIGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMCBtYi0yXCI+e3sgaW50ZXJmYWNlRG9jLm5hbWUgfX08L2g0PlxuICAgICAgICAgICAgPHAgY2xhc3M9XCJtYi00XCI+e3sgaW50ZXJmYWNlRG9jLmRlc2NyaXB0aW9uIH19PC9wPlxuXG4gICAgICAgICAgICA8dGFibGUgY2xhc3M9XCJtaW4tdy1mdWxsIGJnLXdoaXRlXCI+XG4gICAgICAgICAgICAgIDx0aGVhZCBjbGFzcz1cImJnLWdyYXktMTAwXCI+XG4gICAgICAgICAgICAgICAgPHRyPlxuICAgICAgICAgICAgICAgICAgPHRoIGNsYXNzPVwicHktMyBweC00IHRleHQtbGVmdFwiPlTDqm48L3RoPlxuICAgICAgICAgICAgICAgICAgPHRoIGNsYXNzPVwicHktMyBweC00IHRleHQtbGVmdFwiPktp4buDdSBk4buvIGxp4buHdTwvdGg+XG4gICAgICAgICAgICAgICAgICA8dGggY2xhc3M9XCJweS0zIHB4LTQgdGV4dC1sZWZ0XCI+TcO0IHThuqM8L3RoPlxuICAgICAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgICAgIDwvdGhlYWQ+XG4gICAgICAgICAgICAgIDx0Ym9keT5cbiAgICAgICAgICAgICAgICBAZm9yIChwcm9wIG9mIGludGVyZmFjZURvYy5wcm9wZXJ0aWVzOyB0cmFjayBwcm9wLm5hbWUpIHtcbiAgICAgICAgICAgICAgICAgIDx0ciBjbGFzcz1cImJvcmRlci1iIGhvdmVyOmJnLWdyYXktNTBcIj5cbiAgICAgICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwicHktMyBweC00XCI+PGNvZGVcbiAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwiYmctZ3JheS0xMDAgcHgtMSBweS0wLjUgcm91bmRlZCB0ZXh0LXNtIGZvbnQtbW9ub1wiPnt7IHByb3AubmFtZSB9fTwvY29kZT48L3RkPlxuICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJweS0zIHB4LTRcIj48Y29kZVxuICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJiZy1ncmF5LTEwMCBweC0xIHB5LTAuNSByb3VuZGVkIHRleHQtc20gZm9udC1tb25vXCI+e3sgcHJvcC50eXBlIH19PC9jb2RlPjwvdGQ+XG4gICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInB5LTMgcHgtNFwiPnt7IHByb3AuZGVzY3JpcHRpb24gfX08L3RkPlxuICAgICAgICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDwvdGJvZHk+XG4gICAgICAgICAgICA8L3RhYmxlPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cbiAgICA8L3NlY3Rpb24+XG5cbiAgICA8c2VjdGlvbiBjbGFzcz1cImJnLXdoaXRlIHJvdW5kZWQtbGcgc2hhZG93IHAtOCBtYi04XCI+XG4gICAgICA8aDIgY2xhc3M9XCJ0ZXh0LTJ4bCBmb250LWJvbGQgdGV4dC1ncmF5LTgwMCBtYi00IHBiLTIgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwXCI+VMOtbmggbsSDbmc8L2gyPlxuICAgICAgPHVsIGNsYXNzPVwic3BhY2UteS02XCI+XG4gICAgICAgIEBmb3IgKGZlYXR1cmUgb2YgZmVhdHVyZXMoKTsgdHJhY2sgZmVhdHVyZS5pZCkge1xuICAgICAgICAgIDxsaSBjbGFzcz1cImZsZXggaXRlbXMtc3RhcnRcIj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC0zeGwgbXItNCBtaW4tdy0xMCB0ZXh0LWNlbnRlclwiPnt7IGZlYXR1cmUuaWNvbiB9fTwvc3Bhbj5cbiAgICAgICAgICAgIDxkaXY+XG4gICAgICAgICAgICAgIDxoMyBjbGFzcz1cInRleHQteGwgZm9udC1zZW1pYm9sZCB0ZXh0LWdyYXktNzAwXCI+e3sgZmVhdHVyZS50aXRsZSB9fTwvaDM+XG4gICAgICAgICAgICAgIDxwPnt7IGZlYXR1cmUuZGVzY3JpcHRpb24gfX08L3A+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L2xpPlxuICAgICAgICB9XG4gICAgICA8L3VsPlxuICAgIDwvc2VjdGlvbj5cblxuICAgIDxzZWN0aW9uIGNsYXNzPVwiYmctd2hpdGUgcm91bmRlZC1sZyBzaGFkb3cgcC04IG1iLThcIj5cbiAgICAgIDxoMiBjbGFzcz1cInRleHQtMnhsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTQgcGItMiBib3JkZXItYiBib3JkZXItZ3JheS0yMDBcIj5Dw6FjaCBz4butIGThu6VuZzwvaDI+XG4gICAgICA8ZGl2IGNsYXNzPVwiZmxleCBmbGV4LWNvbCBnYXAtNFwiPlxuICAgICAgICBAZm9yIChleGFtcGxlIG9mIGNvZGVFeGFtcGxlcygpOyB0cmFjayBleGFtcGxlLmlkKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImJvcmRlciBib3JkZXItZ3JheS0yMDAgcm91bmRlZC1sZyBwLTRcIj5cbiAgICAgICAgICAgIDxoMyBjbGFzcz1cInRleHQteGwgZm9udC1zZW1pYm9sZCB0ZXh0LWdyYXktNzAwIG1iLTJcIj57eyBleGFtcGxlLnRpdGxlIH19PC9oMz5cbiAgICAgICAgICAgIDxwcmVcbiAgICAgICAgICAgICAgY2xhc3M9XCJiZy1ncmF5LTEwMCBwLTQgcm91bmRlZCBvdmVyZmxvdy14LWF1dG9cIj48Y29kZSBbaW5uZXJIVE1MXT1cImV4YW1wbGUuY29kZVwiIGNsYXNzPVwiZm9udC1tb25vXCI+PC9jb2RlPjwvcHJlPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cbiAgICA8L3NlY3Rpb24+XG4gIDwvbWFpbj5cbjwvZGl2PlxuIl19
|