@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.
@@ -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: `&lt;libs_ui-components-audio
217
+ [fileAudio]="'path/to/audio.mp3'"
218
+ [checkPermissionDownloadAudio]="checkPermission"&gt;
219
+ &lt;/libs_ui-components-audio&gt;`
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
+ &lt;libs_ui-components-audio
232
+ #audioPlayer
233
+ [fileAudio]="audioSource()"
234
+ [checkPermissionDownloadAudio]="checkPermission"
235
+ (outFunctionsControl)="registerFunctions($event)"&gt;
236
+ &lt;/libs_ui-components-audio&gt;
237
+
238
+ &lt;button (click)="playAudio()"&gt;Phát/Tạm dừng&lt;/button&gt;
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
+ &lt;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()"&gt;
274
+ &lt;/libs_ui-components-audio&gt;
275
+
276
+ &lt;div class="audio-info"&gt;
277
+ &lt;p&gt;Trạng thái: {{ isPlaying() ? 'Đang phát' : 'Tạm dừng' }}&lt;/p&gt;
278
+ &lt;p&gt;Thời gian hiện tại: {{ currentTime() }}&lt;/p&gt;
279
+ &lt;p&gt;Tổng thời gian: {{ duration() }}&lt;/p&gt;
280
+ &lt;p&gt;Âm lượng: {{ volumeLevel() }}%&lt;/p&gt;
281
+ &lt;/div&gt;
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\">&#64;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 &#64;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 &#64;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\">&#64;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 &#64;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 &#64;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