@product7/feedback-sdk 1.4.3 → 1.4.5
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 +4 -7
- package/dist/README.md +4 -7
- package/dist/feedback-sdk.js +149 -94
- package/dist/feedback-sdk.js.map +1 -1
- package/dist/feedback-sdk.min.js +1 -1
- package/dist/feedback-sdk.min.js.map +1 -1
- package/package.json +1 -1
- package/src/docs/framework-integrations.md +707 -0
- package/src/widgets/ChangelogWidget.js +52 -51
- package/src/widgets/MessengerWidget.js +29 -3
- package/src/widgets/messenger/MessengerState.js +7 -0
- package/src/widgets/messenger/views/ChangelogView.js +8 -8
- package/src/widgets/messenger/views/HelpView.js +21 -22
- package/src/widgets/messenger/views/HomeView.js +34 -12
|
@@ -109,18 +109,14 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
109
109
|
|
|
110
110
|
this.modalElement
|
|
111
111
|
.querySelector('.changelog-modal-container')
|
|
112
|
-
.addEventListener('click', (e) =>
|
|
113
|
-
e.stopPropagation();
|
|
114
|
-
});
|
|
112
|
+
.addEventListener('click', (e) => e.stopPropagation());
|
|
115
113
|
|
|
116
114
|
this.modalElement
|
|
117
115
|
.querySelector('.changelog-modal-close')
|
|
118
116
|
.addEventListener('click', () => this.closeModal());
|
|
119
117
|
|
|
120
118
|
this._escapeHandler = (e) => {
|
|
121
|
-
if (e.key === 'Escape')
|
|
122
|
-
this.closeModal();
|
|
123
|
-
}
|
|
119
|
+
if (e.key === 'Escape') this.closeModal();
|
|
124
120
|
};
|
|
125
121
|
document.addEventListener('keydown', this._escapeHandler);
|
|
126
122
|
}
|
|
@@ -198,9 +194,7 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
198
194
|
}
|
|
199
195
|
|
|
200
196
|
setTimeout(() => {
|
|
201
|
-
if (container.parentNode)
|
|
202
|
-
container.parentNode.removeChild(container);
|
|
203
|
-
}
|
|
197
|
+
if (container.parentNode) container.parentNode.removeChild(container);
|
|
204
198
|
}, 2500);
|
|
205
199
|
}
|
|
206
200
|
|
|
@@ -263,18 +257,14 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
263
257
|
|
|
264
258
|
this.listModalElement
|
|
265
259
|
.querySelector('.changelog-list-modal-container')
|
|
266
|
-
.addEventListener('click', (e) =>
|
|
267
|
-
e.stopPropagation();
|
|
268
|
-
});
|
|
260
|
+
.addEventListener('click', (e) => e.stopPropagation());
|
|
269
261
|
|
|
270
262
|
this.listModalElement
|
|
271
263
|
.querySelector('.changelog-list-modal-close')
|
|
272
264
|
.addEventListener('click', () => this.closeSidebar());
|
|
273
265
|
|
|
274
266
|
this._listModalEscapeHandler = (e) => {
|
|
275
|
-
if (e.key === 'Escape')
|
|
276
|
-
this.closeSidebar();
|
|
277
|
-
}
|
|
267
|
+
if (e.key === 'Escape') this.closeSidebar();
|
|
278
268
|
};
|
|
279
269
|
document.addEventListener('keydown', this._listModalEscapeHandler);
|
|
280
270
|
}
|
|
@@ -284,9 +274,7 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
284
274
|
'.changelog-list-modal-body'
|
|
285
275
|
);
|
|
286
276
|
|
|
287
|
-
if (this.isLoading)
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
277
|
+
if (this.isLoading) return;
|
|
290
278
|
|
|
291
279
|
if (this.changelogs.length === 0) {
|
|
292
280
|
body.innerHTML = `
|
|
@@ -309,8 +297,7 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
309
297
|
|
|
310
298
|
body.querySelectorAll('.changelog-list-item').forEach((item, index) => {
|
|
311
299
|
item.addEventListener('click', () => {
|
|
312
|
-
|
|
313
|
-
this._handleViewUpdate(changelog);
|
|
300
|
+
this._handleViewUpdate(this.changelogs[index]);
|
|
314
301
|
});
|
|
315
302
|
});
|
|
316
303
|
}
|
|
@@ -321,6 +308,10 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
321
308
|
const date = changelog.published_at
|
|
322
309
|
? this._formatDate(changelog.published_at)
|
|
323
310
|
: '';
|
|
311
|
+
const description = this._truncateDescription(
|
|
312
|
+
changelog.excerpt || changelog.description,
|
|
313
|
+
120
|
|
314
|
+
);
|
|
324
315
|
|
|
325
316
|
return `
|
|
326
317
|
<div class="changelog-list-item" data-index="${index}">
|
|
@@ -352,13 +343,7 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
352
343
|
: ''
|
|
353
344
|
}
|
|
354
345
|
<h3 class="changelog-list-item-title">${changelog.title}</h3>
|
|
355
|
-
${
|
|
356
|
-
changelog.excerpt || changelog.description
|
|
357
|
-
? `
|
|
358
|
-
<p class="changelog-list-item-description">${changelog.excerpt || changelog.description}</p>
|
|
359
|
-
`
|
|
360
|
-
: ''
|
|
361
|
-
}
|
|
346
|
+
${description ? `<p class="changelog-list-item-description">${description}</p>` : ''}
|
|
362
347
|
</div>
|
|
363
348
|
</div>
|
|
364
349
|
</div>
|
|
@@ -385,9 +370,7 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
385
370
|
_renderCurrentChangelog() {
|
|
386
371
|
const content = this.modalElement.querySelector('.changelog-modal-content');
|
|
387
372
|
|
|
388
|
-
if (this.isLoading)
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
373
|
+
if (this.isLoading) return;
|
|
391
374
|
|
|
392
375
|
if (this.changelogs.length === 0) {
|
|
393
376
|
content.innerHTML = `
|
|
@@ -406,6 +389,10 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
406
389
|
const hasImage = changelog.cover_image || changelog.image;
|
|
407
390
|
const imageUrl = changelog.cover_image || changelog.image;
|
|
408
391
|
const hasMultiple = this.changelogs.length > 1;
|
|
392
|
+
const description = this._truncateDescription(
|
|
393
|
+
changelog.excerpt || changelog.description,
|
|
394
|
+
160
|
|
395
|
+
);
|
|
409
396
|
|
|
410
397
|
content.innerHTML = `
|
|
411
398
|
<div class="changelog-popup-item">
|
|
@@ -420,13 +407,7 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
420
407
|
}
|
|
421
408
|
<div class="changelog-popup-body">
|
|
422
409
|
<h2 class="changelog-popup-title">${changelog.title}</h2>
|
|
423
|
-
${
|
|
424
|
-
changelog.excerpt || changelog.description
|
|
425
|
-
? `
|
|
426
|
-
<p class="changelog-popup-description">${changelog.excerpt || changelog.description}</p>
|
|
427
|
-
`
|
|
428
|
-
: ''
|
|
429
|
-
}
|
|
410
|
+
${description ? `<p class="changelog-popup-description">${description}</p>` : ''}
|
|
430
411
|
<button class="changelog-popup-btn" type="button">
|
|
431
412
|
${this.options.viewButtonText || 'View Update'}
|
|
432
413
|
</button>
|
|
@@ -473,8 +454,7 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
473
454
|
if (hasMultiple) {
|
|
474
455
|
content.querySelectorAll('.changelog-dot').forEach((dot) => {
|
|
475
456
|
dot.addEventListener('click', (e) => {
|
|
476
|
-
|
|
477
|
-
this.currentIndex = index;
|
|
457
|
+
this.currentIndex = parseInt(e.target.dataset.index, 10);
|
|
478
458
|
this._renderCurrentChangelog();
|
|
479
459
|
});
|
|
480
460
|
});
|
|
@@ -484,10 +464,19 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
484
464
|
_handleViewUpdate(changelog) {
|
|
485
465
|
this.sdk.eventBus.emit('changelog:view', { changelog });
|
|
486
466
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
467
|
+
const changelogBase = (this.options.changelogBaseUrl || '').replace(
|
|
468
|
+
/\/$/,
|
|
469
|
+
''
|
|
470
|
+
);
|
|
471
|
+
const url =
|
|
472
|
+
changelog.url ||
|
|
473
|
+
(changelog.slug && changelogBase
|
|
474
|
+
? `${changelogBase}/${changelog.slug}`
|
|
475
|
+
: null) ||
|
|
476
|
+
changelogBase ||
|
|
477
|
+
null;
|
|
478
|
+
|
|
479
|
+
if (url) {
|
|
491
480
|
if (this.options.openInNewTab !== false) {
|
|
492
481
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
493
482
|
} else {
|
|
@@ -500,10 +489,26 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
500
489
|
}
|
|
501
490
|
}
|
|
502
491
|
|
|
492
|
+
_stripHtml(html) {
|
|
493
|
+
const tmp = document.createElement('div');
|
|
494
|
+
tmp.innerHTML = html;
|
|
495
|
+
return (tmp.textContent || tmp.innerText || '').trim();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
_truncateDescription(html, maxLength = 120) {
|
|
499
|
+
if (!html) return '';
|
|
500
|
+
const plain = this._stripHtml(html);
|
|
501
|
+
if (plain.length <= maxLength) return plain;
|
|
502
|
+
return plain.substring(0, maxLength).trimEnd() + '...';
|
|
503
|
+
}
|
|
504
|
+
|
|
503
505
|
_formatDate(dateString) {
|
|
504
506
|
const date = new Date(dateString);
|
|
505
|
-
|
|
506
|
-
|
|
507
|
+
return date.toLocaleDateString('en-US', {
|
|
508
|
+
year: 'numeric',
|
|
509
|
+
month: 'short',
|
|
510
|
+
day: 'numeric',
|
|
511
|
+
});
|
|
507
512
|
}
|
|
508
513
|
|
|
509
514
|
_getContrastColor(hexColor) {
|
|
@@ -517,16 +522,12 @@ export class ChangelogWidget extends BaseWidget {
|
|
|
517
522
|
|
|
518
523
|
hideBadge() {
|
|
519
524
|
const badge = this.element?.querySelector('.changelog-badge');
|
|
520
|
-
if (badge)
|
|
521
|
-
badge.style.display = 'none';
|
|
522
|
-
}
|
|
525
|
+
if (badge) badge.style.display = 'none';
|
|
523
526
|
}
|
|
524
527
|
|
|
525
528
|
showBadge() {
|
|
526
529
|
const badge = this.element?.querySelector('.changelog-badge');
|
|
527
|
-
if (badge)
|
|
528
|
-
badge.style.display = 'block';
|
|
529
|
-
}
|
|
530
|
+
if (badge) badge.style.display = 'block';
|
|
530
531
|
}
|
|
531
532
|
|
|
532
533
|
nextChangelog() {
|
|
@@ -31,7 +31,12 @@ export class MessengerWidget extends BaseWidget {
|
|
|
31
31
|
enableHelp: options.enableHelp !== false,
|
|
32
32
|
enableChangelog: options.enableChangelog !== false,
|
|
33
33
|
featuredContent: options.featuredContent || null,
|
|
34
|
+
feedbackUrl: options.feedbackUrl || null,
|
|
35
|
+
changelogUrl: options.changelogUrl || null,
|
|
36
|
+
helpUrl: options.helpUrl || null,
|
|
37
|
+
roadmapUrl: options.roadmapUrl || null,
|
|
34
38
|
onSendMessage: options.onSendMessage || null,
|
|
39
|
+
onFeedbackClick: options.onFeedbackClick || null,
|
|
35
40
|
onArticleClick: options.onArticleClick || null,
|
|
36
41
|
onChangelogClick: options.onChangelogClick || null,
|
|
37
42
|
};
|
|
@@ -43,6 +48,12 @@ export class MessengerWidget extends BaseWidget {
|
|
|
43
48
|
enableHelp: this.messengerOptions.enableHelp,
|
|
44
49
|
enableChangelog: this.messengerOptions.enableChangelog,
|
|
45
50
|
userContext: this.sdk?.apiService?.getUserContext() || null,
|
|
51
|
+
urls: {
|
|
52
|
+
feedback: this.messengerOptions.feedbackUrl,
|
|
53
|
+
changelog: this.messengerOptions.changelogUrl,
|
|
54
|
+
help: this.messengerOptions.helpUrl,
|
|
55
|
+
roadmap: this.messengerOptions.roadmapUrl,
|
|
56
|
+
},
|
|
46
57
|
});
|
|
47
58
|
|
|
48
59
|
this.launcher = null;
|
|
@@ -89,6 +100,7 @@ export class MessengerWidget extends BaseWidget {
|
|
|
89
100
|
onSelectConversation: this._handleSelectConversation.bind(this),
|
|
90
101
|
onStartNewConversation: this._handleNewConversationClick.bind(this),
|
|
91
102
|
onIdentifyContact: this._handleIdentifyContact.bind(this),
|
|
103
|
+
onFeedbackClick: this.messengerOptions.onFeedbackClick,
|
|
92
104
|
onArticleClick: this.messengerOptions.onArticleClick,
|
|
93
105
|
onChangelogClick: this.messengerOptions.onChangelogClick,
|
|
94
106
|
});
|
|
@@ -544,12 +556,20 @@ export class MessengerWidget extends BaseWidget {
|
|
|
544
556
|
const response = await this.apiService.getHelpCollections();
|
|
545
557
|
if (response.success && response.data) {
|
|
546
558
|
const collections = response.data.collections || response.data;
|
|
559
|
+
const helpBase = (this.messengerOptions.helpUrl || '').replace(
|
|
560
|
+
/\/$/,
|
|
561
|
+
''
|
|
562
|
+
);
|
|
563
|
+
|
|
547
564
|
return collections.map((collection) => ({
|
|
548
565
|
id: collection.id,
|
|
549
566
|
title: collection.title,
|
|
550
567
|
description: collection.description || '',
|
|
551
568
|
articleCount: collection.article_count || 0,
|
|
552
|
-
url:
|
|
569
|
+
url:
|
|
570
|
+
collection.url_slug && helpBase
|
|
571
|
+
? `${helpBase}/collections/${collection.url_slug}`
|
|
572
|
+
: helpBase || null,
|
|
553
573
|
}));
|
|
554
574
|
}
|
|
555
575
|
return [];
|
|
@@ -654,7 +674,7 @@ export class MessengerWidget extends BaseWidget {
|
|
|
654
674
|
try {
|
|
655
675
|
await this.apiService.sendTypingIndicator(conversationId, isTyping);
|
|
656
676
|
} catch (error) {
|
|
657
|
-
//
|
|
677
|
+
// silent fail
|
|
658
678
|
}
|
|
659
679
|
}
|
|
660
680
|
|
|
@@ -762,6 +782,9 @@ export class MessengerWidget extends BaseWidget {
|
|
|
762
782
|
|
|
763
783
|
if (response.success && response.data) {
|
|
764
784
|
const changelogs = Array.isArray(response.data) ? response.data : [];
|
|
785
|
+
const changelogBase = (
|
|
786
|
+
this.messengerOptions.changelogUrl || ''
|
|
787
|
+
).replace(/\/$/, '');
|
|
765
788
|
|
|
766
789
|
const mappedItems = changelogs.map((item) => ({
|
|
767
790
|
id: item.id,
|
|
@@ -770,7 +793,10 @@ export class MessengerWidget extends BaseWidget {
|
|
|
770
793
|
tags: item.labels ? item.labels.map((label) => label.name) : [],
|
|
771
794
|
coverImage: item.cover_image || null,
|
|
772
795
|
publishedAt: item.published_at,
|
|
773
|
-
url:
|
|
796
|
+
url:
|
|
797
|
+
item.slug && changelogBase
|
|
798
|
+
? `${changelogBase}/${item.slug}`
|
|
799
|
+
: changelogBase || null,
|
|
774
800
|
}));
|
|
775
801
|
|
|
776
802
|
return {
|
|
@@ -23,14 +23,14 @@ export class ChangelogView {
|
|
|
23
23
|
|
|
24
24
|
_updateContent() {
|
|
25
25
|
this.element.innerHTML = `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
<div class="messenger-changelog-header">
|
|
27
|
+
<h2>Latest Updates</h2>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="messenger-changelog-body">
|
|
31
|
+
<div class="messenger-changelog-list"></div>
|
|
32
|
+
</div>
|
|
33
|
+
`;
|
|
34
34
|
|
|
35
35
|
this._updateChangelogList();
|
|
36
36
|
this._attachEvents();
|
|
@@ -23,30 +23,29 @@ export class HelpView {
|
|
|
23
23
|
|
|
24
24
|
_updateContent() {
|
|
25
25
|
const searchQuery = this.state.helpSearchQuery || '';
|
|
26
|
-
const collections = this.state.helpArticles || [];
|
|
27
26
|
|
|
28
27
|
this.element.innerHTML = `
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
28
|
+
<div class="messenger-help-header">
|
|
29
|
+
<div class="messenger-help-header-top">
|
|
30
|
+
<h2>Help</h2>
|
|
31
|
+
<button class="sdk-close-btn" aria-label="Close">
|
|
32
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 256 256">
|
|
33
|
+
<path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path>
|
|
34
|
+
</svg>
|
|
35
|
+
</button>
|
|
36
|
+
</div>
|
|
37
|
+
<input
|
|
38
|
+
type="text"
|
|
39
|
+
class="messenger-help-search-input"
|
|
40
|
+
placeholder="Search for help..."
|
|
41
|
+
value="${searchQuery}"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="messenger-help-body">
|
|
46
|
+
<div class="messenger-help-collections"></div>
|
|
47
|
+
</div>
|
|
48
|
+
`;
|
|
50
49
|
|
|
51
50
|
this._updateCollectionsList();
|
|
52
51
|
this._attachEvents();
|
|
@@ -129,11 +129,6 @@ export class HomeView {
|
|
|
129
129
|
`;
|
|
130
130
|
|
|
131
131
|
if (openConversation) {
|
|
132
|
-
const preview = openConversation.lastMessage
|
|
133
|
-
? openConversation.lastMessage.length > 40
|
|
134
|
-
? openConversation.lastMessage.substring(0, 40) + '...'
|
|
135
|
-
: openConversation.lastMessage
|
|
136
|
-
: 'Continue your conversation';
|
|
137
132
|
return `
|
|
138
133
|
<button class="messenger-home-message-btn messenger-home-continue-btn" data-conversation-id="${openConversation.id}">
|
|
139
134
|
<div class="messenger-home-continue-info">
|
|
@@ -141,18 +136,19 @@ export class HomeView {
|
|
|
141
136
|
</div>
|
|
142
137
|
${sendIcon}
|
|
143
138
|
</button>
|
|
144
|
-
<button class="messenger-home-message-btn" data-action="feedback">
|
|
139
|
+
<button class="messenger-home-message-btn messenger-feedback-btn" data-action="feedback">
|
|
145
140
|
<span class="messenger-home-continue-label">Leave us feedback</span>
|
|
146
141
|
${caretIcon}
|
|
147
142
|
</button>
|
|
148
143
|
`;
|
|
149
144
|
}
|
|
145
|
+
|
|
150
146
|
return `
|
|
151
147
|
<button class="messenger-home-message-btn">
|
|
152
148
|
<span>Start a conversation</span>
|
|
153
149
|
${sendIcon}
|
|
154
150
|
</button>
|
|
155
|
-
<button class="messenger-home-message-btn" data-action="feedback">
|
|
151
|
+
<button class="messenger-home-message-btn messenger-feedback-btn" data-action="feedback">
|
|
156
152
|
<span>Leave us feedback</span>
|
|
157
153
|
${caretIcon}
|
|
158
154
|
</button>
|
|
@@ -234,7 +230,9 @@ export class HomeView {
|
|
|
234
230
|
this.state.setOpen(false);
|
|
235
231
|
});
|
|
236
232
|
|
|
237
|
-
const msgBtn = this.element.querySelector(
|
|
233
|
+
const msgBtn = this.element.querySelector(
|
|
234
|
+
'.messenger-home-message-btn:not(.messenger-feedback-btn)'
|
|
235
|
+
);
|
|
238
236
|
if (msgBtn) {
|
|
239
237
|
msgBtn.addEventListener('click', () => {
|
|
240
238
|
const convId = msgBtn.dataset.conversationId;
|
|
@@ -251,11 +249,35 @@ export class HomeView {
|
|
|
251
249
|
});
|
|
252
250
|
}
|
|
253
251
|
|
|
252
|
+
const feedbackBtn = this.element.querySelector('.messenger-feedback-btn');
|
|
253
|
+
if (feedbackBtn) {
|
|
254
|
+
feedbackBtn.addEventListener('click', () => {
|
|
255
|
+
const url = this.state.urls?.feedback;
|
|
256
|
+
|
|
257
|
+
if (this.options.onFeedbackClick) {
|
|
258
|
+
this.options.onFeedbackClick();
|
|
259
|
+
} else if (url) {
|
|
260
|
+
window.open(url, '_blank');
|
|
261
|
+
} else {
|
|
262
|
+
console.warn(
|
|
263
|
+
'[Messenger] No feedback destination configured. Set `feedbackUrl` in MessengerWidget options.'
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
254
269
|
this.element
|
|
255
|
-
.querySelectorAll('.messenger-home-changelog-
|
|
256
|
-
.forEach((
|
|
257
|
-
|
|
258
|
-
this.state.
|
|
270
|
+
.querySelectorAll('.messenger-home-changelog-card')
|
|
271
|
+
.forEach((card) => {
|
|
272
|
+
card.addEventListener('click', () => {
|
|
273
|
+
const item = this.state.homeChangelogItems.find(
|
|
274
|
+
(i) => i.id === card.dataset.changelogId
|
|
275
|
+
);
|
|
276
|
+
if (item?.url) {
|
|
277
|
+
window.open(item.url, '_blank');
|
|
278
|
+
} else {
|
|
279
|
+
this.state.setView('changelog');
|
|
280
|
+
}
|
|
259
281
|
});
|
|
260
282
|
});
|
|
261
283
|
|