+14
-14
embed_component.js
+14
-14
embed_component.js
···
10
10
this.embed = embed;
11
11
}
12
12
13
-
/** @returns {AnyElement} */
13
+
/** @returns {HTMLElement} */
14
14
15
15
buildElement() {
16
16
if (this.embed instanceof RawRecordEmbed) {
···
54
54
}
55
55
}
56
56
57
-
/** @returns {AnyElement} */
57
+
/** @returns {HTMLElement} */
58
58
59
59
quotedPostPlaceholder() {
60
60
return $tag('div.quote-embed', {
···
62
62
});
63
63
}
64
64
65
-
/** @param {InlineRecordEmbed | InlineRecordWithMediaEmbed} embed, @returns {AnyElement} */
65
+
/** @param {InlineRecordEmbed | InlineRecordWithMediaEmbed} embed, @returns {HTMLElement} */
66
66
67
67
buildQuotedPostElement(embed) {
68
68
let div = $tag('div.quote-embed');
···
88
88
return div;
89
89
}
90
90
91
-
/** @params {RawLinkEmbed | InlineLinkEmbed} embed, @returns {AnyElement} */
91
+
/** @params {RawLinkEmbed | InlineLinkEmbed} embed, @returns {HTMLElement} */
92
92
93
93
buildLinkComponent(embed) {
94
94
let hostname;
···
128
128
return a;
129
129
}
130
130
131
-
/** @param {FeedGeneratorRecord} feedgen, @returns {AnyElement} */
131
+
/** @param {FeedGeneratorRecord} feedgen, @returns {HTMLElement} */
132
132
133
133
buildFeedGeneratorView(feedgen) {
134
134
let link = this.linkToFeedGenerator(feedgen);
···
137
137
let box = $tag('div');
138
138
139
139
if (feedgen.avatar) {
140
-
let avatar = $tag('img.avatar');
140
+
let avatar = $tag('img.avatar', HTMLImageElement);
141
141
avatar.src = feedgen.avatar;
142
142
box.append(avatar);
143
143
}
···
167
167
return `https://bsky.app/profile/${repo}/feed/${rkey}`;
168
168
}
169
169
170
-
/** @param {UserListRecord} list, @returns {AnyElement} */
170
+
/** @param {UserListRecord} list, @returns {HTMLElement} */
171
171
172
172
buildUserListView(list) {
173
173
let link = this.linkToUserList(list);
···
176
176
let box = $tag('div');
177
177
178
178
if (list.avatar) {
179
-
let avatar = $tag('img.avatar');
179
+
let avatar = $tag('img.avatar', HTMLImageElement);
180
180
avatar.src = list.avatar;
181
181
box.append(avatar);
182
182
}
···
207
207
return a;
208
208
}
209
209
210
-
/** @param {StarterPackRecord} pack, @returns {AnyElement} */
210
+
/** @param {StarterPackRecord} pack, @returns {HTMLElement} */
211
211
212
212
buildStarterPackView(pack) {
213
213
let { repo, rkey } = atURI(pack.uri);
···
236
236
return `https://bsky.app/profile/${repo}/lists/${rkey}`;
237
237
}
238
238
239
-
/** @params {RawImageEmbed | InlineImageEmbed} embed, @returns {AnyElement} */
239
+
/** @params {RawImageEmbed | InlineImageEmbed} embed, @returns {HTMLElement} */
240
240
241
241
buildImagesComponent(embed) {
242
242
let wrapper = $tag('div');
···
246
246
p.append('[');
247
247
248
248
// TODO: load image
249
-
let a = $tag('a', { text: "Image" });
249
+
let a = $tag('a', { text: "Image" }, HTMLLinkElement);
250
250
251
251
if (image.fullsize) {
252
252
a.href = image.fullsize;
···
272
272
return wrapper;
273
273
}
274
274
275
-
/** @params {RawVideoEmbed | InlineVideoEmbed} embed, @returns {AnyElement} */
275
+
/** @params {RawVideoEmbed | InlineVideoEmbed} embed, @returns {HTMLElement} */
276
276
277
277
buildVideoComponent(embed) {
278
278
let wrapper = $tag('div');
279
279
280
280
// TODO: load thumbnail
281
-
let a = $tag('a', { text: "Video" });
281
+
let a = $tag('a', { text: "Video" }, HTMLLinkElement);
282
282
283
283
if (embed.playlistURL) {
284
284
a.href = embed.playlistURL;
···
303
303
return wrapper;
304
304
}
305
305
306
-
/** @param {string} uri, @param {AnyElement} div, @returns Promise<void> */
306
+
/** @param {string} uri, @param {HTMLElement} div, @returns Promise<void> */
307
307
308
308
async loadQuotedPost(uri, div) {
309
309
let record = await api.loadPostIfExists(uri);
+31
-27
post_component.js
+31
-27
post_component.js
···
5
5
class PostComponent {
6
6
/**
7
7
* Post component's root HTML element, if built.
8
-
* @type {AnyElement | undefined}
8
+
* @type {HTMLElement | undefined}
9
9
*/
10
10
_rootElement;
11
11
···
26
26
}
27
27
28
28
/**
29
-
* @returns {AnyElement}
29
+
* @returns {HTMLElement}
30
30
*/
31
31
get rootElement() {
32
32
if (!this._rootElement) {
···
91
91
}
92
92
}
93
93
94
-
/** @param {AnyElement} nodeToUpdate */
94
+
/** @param {HTMLElement} nodeToUpdate */
95
95
installIntoElement(nodeToUpdate) {
96
96
let view = this.buildElement();
97
97
98
-
nodeToUpdate.querySelector('.content').replaceWith(view.querySelector('.content'));
98
+
let oldContent = $(nodeToUpdate.querySelector('.content'));
99
+
let newContent = $(view.querySelector('.content'));
100
+
oldContent.replaceWith(newContent);
101
+
99
102
this._rootElement = nodeToUpdate;
100
103
}
101
104
102
-
/** @returns {AnyElement} */
105
+
/** @returns {HTMLElement} */
103
106
buildElement() {
104
107
if (this._rootElement) {
105
108
return this._rootElement;
···
196
199
return div;
197
200
}
198
201
199
-
/** @returns {AnyElement} */
202
+
/** @returns {HTMLElement} */
200
203
201
204
buildPostHeader() {
202
205
let timeFormat = this.timeFormatForTimestamp;
···
260
263
/** @param {string} url, @returns {HTMLImageElement} */
261
264
262
265
buildUserAvatar(url) {
263
-
let avatar = $tag('img.avatar', { loading: 'lazy' }); // needs to be set before src!
266
+
let avatar = $tag('img.avatar', { loading: 'lazy' }, HTMLImageElement); // needs to be set before src!
264
267
avatar.src = url;
265
268
window.avatarPreloader.observe(avatar);
266
269
return avatar;
267
270
}
268
271
269
-
/** @returns {AnyElement} */
272
+
/** @returns {HTMLElement} */
270
273
271
274
buildPostBody() {
272
275
if (this.post.originalFediContent) {
···
297
300
return p;
298
301
}
299
302
300
-
/** @param {string[]} tags, @returns {AnyElement} */
303
+
/** @param {string[]} tags, @returns {HTMLElement} */
301
304
302
305
buildTagsRow(tags) {
303
306
let p = $tag('p.tags');
···
313
316
return p;
314
317
}
315
318
316
-
/** @returns {AnyElement} */
319
+
/** @returns {HTMLElement} */
317
320
318
321
buildStatsFooter() {
319
322
let stats = $tag('p.stats');
···
347
350
return stats;
348
351
}
349
352
350
-
/** @param {number} count, @param {boolean} expanded, @returns {AnyElement} */
353
+
/** @param {number} count, @param {boolean} expanded, @returns {HTMLElement} */
351
354
352
355
buildQuotesIconLink(count, expanded) {
353
356
let q = new URL(getLocation());
···
369
372
/** @param {number} quoteCount, @param {boolean} expanded */
370
373
371
374
appendQuotesIconLink(quoteCount, expanded) {
372
-
let stats = this.rootElement.querySelector(':scope > .content > p.stats');
375
+
let stats = $(this.rootElement.querySelector(':scope > .content > p.stats'));
373
376
let quotesLink = this.buildQuotesIconLink(quoteCount, expanded);
374
377
stats.append(quotesLink);
375
378
}
376
379
377
-
/** @returns {AnyElement} */
380
+
/** @returns {HTMLElement} */
378
381
379
382
buildLoadMoreLink() {
380
383
let loadMore = $tag('p');
···
394
397
return loadMore;
395
398
}
396
399
397
-
/** @returns {AnyElement} */
400
+
/** @returns {HTMLElement} */
398
401
399
402
buildHiddenRepliesLink() {
400
403
let loadMore = $tag('p.hidden-replies');
···
419
422
return loadMore;
420
423
}
421
424
422
-
/** @param {HTMLLinkElement} loadMoreButton */
425
+
/** @param {HTMLElement} loadMoreButton */
423
426
424
427
loadHiddenReplies(loadMoreButton) {
425
428
loadMoreButton.innerHTML = `<img class="loader" src="icons/sunny.png">`;
···
443
446
});
444
447
}
445
448
446
-
/** @param {AnyElement} div, @returns {AnyElement} */
449
+
/** @param {HTMLElement} div, @returns {HTMLElement} */
447
450
448
451
buildBlockedPostElement(div) {
449
452
let p = $tag('p.blocked-header');
···
458
461
let blockStatus = this.post.blockedByUser ? 'has blocked you' : this.post.blocksUser ? "you've blocked them" : '';
459
462
blockStatus = blockStatus ? `, ${blockStatus}` : '';
460
463
461
-
let authorLink = $tag('a', { href: this.didLinkToAuthor, target: '_blank', text: 'see author' });
464
+
let authorLink = $tag('a', { href: this.didLinkToAuthor, target: '_blank', text: 'see author' }, HTMLLinkElement);
462
465
p.append(' (', authorLink, blockStatus, ') ');
463
466
div.appendChild(p);
464
467
···
479
482
return div;
480
483
}
481
484
482
-
/** @param {AnyElement} div, @returns {AnyElement} */
485
+
/** @param {HTMLElement} div, @returns {HTMLElement} */
483
486
484
487
buildDetachedQuoteElement(div) {
485
488
let p = $tag('p.blocked-header');
···
491
494
return p;
492
495
}
493
496
494
-
let authorLink = $tag('a', { href: this.didLinkToAuthor, target: '_blank', text: 'see author' });
497
+
let authorLink = $tag('a', { href: this.didLinkToAuthor, target: '_blank', text: 'see author' }, HTMLLinkElement);
495
498
p.append(' (', authorLink, ') ');
496
499
div.appendChild(p);
497
500
···
512
515
return div;
513
516
}
514
517
515
-
/** @param {AnyElement} div, @returns {AnyElement} */
518
+
/** @param {HTMLElement} div, @returns {HTMLElement} */
516
519
517
520
buildMissingPostElement(div) {
518
521
let p = $tag('p.blocked-header');
519
522
p.innerHTML = `<i class="fa-solid fa-ban"></i> <span>Deleted post</span>`;
520
523
521
-
let authorLink = $tag('a', { href: this.didLinkToAuthor, target: '_blank', text: 'see author' });
524
+
let authorLink = $tag('a', { href: this.didLinkToAuthor, target: '_blank', text: 'see author' }, HTMLLinkElement);
522
525
p.append(' (', authorLink, ') ');
523
526
524
527
this.loadReferencedPostAuthor(authorLink);
···
528
531
return div;
529
532
}
530
533
531
-
/** @param {string} uri, @param {AnyElement} div, @returns Promise<void> */
534
+
/** @param {string} uri, @param {HTMLElement} div, @returns Promise<void> */
532
535
533
536
async loadBlockedPost(uri, div) {
534
537
let record = await appView.loadPostIfExists(this.post.uri);
···
554
557
html: `<i class="fa-solid fa-arrows-split-up-and-left fa-rotate-180"></i>`
555
558
});
556
559
557
-
let header = div.querySelector('p.blocked-header');
560
+
let header = $(div.querySelector('p.blocked-header'));
558
561
let separator = $tag('span.separator', { html: '•' });
559
562
header.append(separator, ' ', a);
560
563
}
561
564
562
-
div.querySelector('p.load-post').remove();
565
+
let loadPost = $(div.querySelector('p.load-post'));
566
+
loadPost.remove();
563
567
564
568
if (this.isRoot && this.post.parentReference) {
565
569
let { repo, rkey } = atURI(this.post.parentReference.uri);
···
590
594
}
591
595
592
596
toggleSectionFold() {
593
-
let plus = this.rootElement.querySelector(':scope > .margin .plus');
597
+
let plus = $(this.rootElement.querySelector(':scope > .margin .plus'), HTMLImageElement);
594
598
595
599
if (this.isCollapsed()) {
596
600
this.rootElement.classList.remove('collapsed');
···
601
605
}
602
606
}
603
607
604
-
/** @param {AnyElement} heart */
608
+
/** @param {HTMLElement} heart */
605
609
606
610
onHeartClick(heart) {
607
611
if (!this.post.hasViewerInfo) {
···
627
631
return;
628
632
}
629
633
630
-
let count = heart.nextElementSibling;
634
+
let count = $(heart.nextElementSibling);
631
635
632
636
if (!heart.classList.contains('liked')) {
633
637
accountAPI.likePost(this.post).then((like) => {
+45
-37
skythread.js
+45
-37
skythread.js
···
1
1
function init() {
2
-
let document = /** @type {AnyElement} */ (/** @type {unknown} */ (window.document));
3
-
let html = /** @type {AnyElement} */ (/** @type {unknown} */ (window.document.body.parentNode));
2
+
let html = $(document.body.parentNode);
4
3
5
4
window.dateLocale = localStorage.getItem('locale') || undefined;
6
5
window.isIncognito = !!localStorage.getItem('incognito');
7
6
window.biohazardEnabled = JSON.parse(localStorage.getItem('biohazard') ?? 'null');
8
7
9
-
window.loginDialog = document.querySelector('#login');
10
-
window.accountMenu = document.querySelector('#account_menu');
8
+
window.loginDialog = $(document.querySelector('#login'));
9
+
window.accountMenu = $(document.querySelector('#account_menu'));
11
10
12
11
window.avatarPreloader = buildAvatarPreloader();
13
12
···
15
14
$id('account_menu').style.visibility = 'hidden';
16
15
});
17
16
18
-
document.querySelector('#search form').addEventListener('submit', (e) => {
17
+
$(document.querySelector('#search form')).addEventListener('submit', (e) => {
19
18
e.preventDefault();
20
19
submitSearch();
21
20
});
···
34
33
});
35
34
}
36
35
37
-
document.querySelector('#login .info a').addEventListener('click', (e) => {
36
+
$(document.querySelector('#login .info a')).addEventListener('click', (e) => {
38
37
e.preventDefault();
39
38
toggleLoginInfo();
40
39
});
41
40
42
-
document.querySelector('#login form').addEventListener('submit', (e) => {
41
+
$(document.querySelector('#login form')).addEventListener('submit', (e) => {
43
42
e.preventDefault();
44
43
submitLogin();
45
44
});
46
45
47
-
document.querySelector('#biohazard_show').addEventListener('click', (e) => {
46
+
$(document.querySelector('#biohazard_show')).addEventListener('click', (e) => {
48
47
e.preventDefault();
49
48
50
49
window.biohazardEnabled = true;
···
55
54
window.loadInfohazard = undefined;
56
55
}
57
56
58
-
let target = /** @type {AnyElement} */ (/** @type {unknown} */ (e.target));
57
+
let target = $(e.target);
59
58
60
59
hideDialog(target.closest('.dialog'));
61
60
});
62
61
63
-
document.querySelector('#biohazard_hide').addEventListener('click', (e) => {
62
+
$(document.querySelector('#biohazard_hide')).addEventListener('click', (e) => {
64
63
e.preventDefault();
65
64
66
65
window.biohazardEnabled = false;
···
68
67
toggleMenuButton('biohazard', false);
69
68
70
69
for (let p of document.querySelectorAll('p.hidden-replies, .content > .post.blocked, .blocked > .load-post')) {
71
-
p.style.display = 'none';
70
+
$(p).style.display = 'none';
72
71
}
73
72
74
-
let target = /** @type {AnyElement} */ (/** @type {unknown} */ (e.target));
73
+
let target = $(e.target);
75
74
76
75
hideDialog(target.closest('.dialog'));
77
76
});
78
77
79
-
document.querySelector('#account').addEventListener('click', (e) => {
78
+
$(document.querySelector('#account')).addEventListener('click', (e) => {
80
79
toggleAccountMenu();
81
80
e.stopPropagation();
82
81
});
···
85
84
e.stopPropagation();
86
85
});
87
86
88
-
accountMenu.querySelector('a[data-action=biohazard]').addEventListener('click', (e) => {
87
+
$(accountMenu.querySelector('a[data-action=biohazard]')).addEventListener('click', (e) => {
89
88
e.preventDefault();
90
89
91
90
let hazards = document.querySelectorAll('p.hidden-replies, .content > .post.blocked, .blocked > .load-post');
···
94
93
window.biohazardEnabled = true;
95
94
localStorage.setItem('biohazard', 'true');
96
95
toggleMenuButton('biohazard', true);
97
-
Array.from(hazards).forEach(p => { p.style.display = 'block' });
96
+
Array.from(hazards).forEach(p => { $(p).style.display = 'block' });
98
97
} else {
99
98
window.biohazardEnabled = false;
100
99
localStorage.setItem('biohazard', 'false');
101
100
toggleMenuButton('biohazard', false);
102
-
Array.from(hazards).forEach(p => { p.style.display = 'none' });
101
+
Array.from(hazards).forEach(p => { $(p).style.display = 'none' });
103
102
}
104
103
});
105
104
106
-
accountMenu.querySelector('a[data-action=incognito]').addEventListener('click', (e) => {
105
+
$(accountMenu.querySelector('a[data-action=incognito]')).addEventListener('click', (e) => {
107
106
e.preventDefault();
108
107
109
108
if (isIncognito) {
···
115
114
location.reload();
116
115
});
117
116
118
-
accountMenu.querySelector('a[data-action=login]').addEventListener('click', (e) => {
117
+
$(accountMenu.querySelector('a[data-action=login]')).addEventListener('click', (e) => {
119
118
e.preventDefault();
120
119
toggleDialog(loginDialog);
121
120
$id('account_menu').style.visibility = 'hidden';
122
121
});
123
122
124
-
accountMenu.querySelector('a[data-action=logout]').addEventListener('click', (e) => {
123
+
$(accountMenu.querySelector('a[data-action=logout]')).addEventListener('click', (e) => {
125
124
e.preventDefault();
126
125
logOut();
127
126
});
···
181
180
}
182
181
}
183
182
184
-
/** @param {AnyPost} post, @returns {AnyElement} */
183
+
/** @param {AnyPost} post, @returns {HTMLElement} */
185
184
186
185
function buildParentLink(post) {
187
186
let p = $tag('p.back');
···
189
188
if (post instanceof BlockedPost) {
190
189
let element = new PostComponent(post, 'parent').buildElement();
191
190
element.className = 'back';
192
-
element.querySelector('p.blocked-header span').innerText = 'Parent post blocked';
191
+
let span = $(element.querySelector('p.blocked-header span'));
192
+
span.innerText = 'Parent post blocked';
193
193
return element;
194
194
} else if (post instanceof MissingPost) {
195
195
p.innerHTML = `<i class="fa-solid fa-ban"></i> parent post has been deleted`;
···
226
226
}
227
227
228
228
function showSearch() {
229
-
$id('search').style.visibility = 'visible';
230
-
$id('search').querySelector('input[type=text]').focus();
229
+
let search = $id('search');
230
+
let searchField = $(search.querySelector('input[type=text]'));
231
+
232
+
search.style.visibility = 'visible';
233
+
searchField.focus();
231
234
}
232
235
233
236
function hideSearch() {
···
271
274
/** @param {string} buttonName */
272
275
273
276
function showMenuButton(buttonName) {
274
-
let button = accountMenu.querySelector(`a[data-action=${buttonName}]`);
275
-
button.parentNode.style.display = 'list-item';
277
+
let button = $(accountMenu.querySelector(`a[data-action=${buttonName}]`));
278
+
let item = $(button.parentNode);
279
+
item.style.display = 'list-item';
276
280
}
277
281
278
282
/** @param {string} buttonName */
279
283
280
284
function hideMenuButton(buttonName) {
281
-
let button = accountMenu.querySelector(`a[data-action=${buttonName}]`);
282
-
button.parentNode.style.display = 'none';
285
+
let button = $(accountMenu.querySelector(`a[data-action=${buttonName}]`));
286
+
let item = $(button.parentNode);
287
+
item.style.display = 'none';
283
288
}
284
289
285
290
/** @param {string} buttonName, @param {boolean} state */
286
291
287
292
function toggleMenuButton(buttonName, state) {
288
-
let button = accountMenu.querySelector(`a[data-action=${buttonName}]`);
289
-
button.querySelector('.check').style.display = (state) ? 'inline' : 'none';
293
+
let button = $(accountMenu.querySelector(`a[data-action=${buttonName}]`));
294
+
let check = $(button.querySelector('.check'));
295
+
check.style.display = (state) ? 'inline' : 'none';
290
296
}
291
297
292
298
/** @param {boolean | 'incognito'} loggedIn, @param {string | undefined | null} [avatar] */
···
295
301
let account = $id('account');
296
302
297
303
if (loggedIn === true && avatar) {
298
-
let button = account.querySelector('i');
304
+
let button = $(account.querySelector('i'));
299
305
300
306
let img = $tag('img.avatar', { src: avatar });
301
307
img.style.display = 'none';
···
318
324
}
319
325
320
326
function submitLogin() {
321
-
let handle = $id('login_handle');
322
-
let password = $id('login_password');
327
+
let handle = $id('login_handle', HTMLInputElement);
328
+
let password = $id('login_password', HTMLInputElement);
323
329
let submit = $id('login_submit');
324
330
let cloudy = $id('cloudy');
325
331
···
401
407
}
402
408
403
409
function submitSearch() {
404
-
let url = $id('search').querySelector('input[name=q]').value.trim();
410
+
let search = $id('search');
411
+
let searchField = $(search.querySelector('input[name=q]'), HTMLInputElement);
412
+
let url = searchField.value.trim();
405
413
406
414
if (!url) { return }
407
415
···
702
710
});
703
711
}
704
712
705
-
/** @param {Post} post, @param {AnyElement} nodeToUpdate */
713
+
/** @param {Post} post, @param {HTMLElement} nodeToUpdate */
706
714
707
715
function loadSubtree(post, nodeToUpdate) {
708
716
api.loadThreadByAtURI(post.uri).then(json => {
···
715
723
}).catch(showError);
716
724
}
717
725
718
-
/** @param {Post} post, @param {AnyElement} nodeToUpdate */
726
+
/** @param {Post} post, @param {HTMLElement} nodeToUpdate */
719
727
720
728
function loadHiddenSubtree(post, nodeToUpdate) {
721
-
let content = nodeToUpdate.querySelector('.content');
722
-
let hiddenRepliesDiv = content.querySelector(':scope > .hidden-replies');
729
+
let content = $(nodeToUpdate.querySelector('.content'));
730
+
let hiddenRepliesDiv = $(content.querySelector(':scope > .hidden-replies'));
723
731
724
732
blueAPI.getReplies(post.uri).then(replies => {
725
733
let missingReplies = replies.filter(r => !post.replies.some(x => x.uri === r));
+10
-25
types.d.ts
+10
-25
types.d.ts
···
11
11
declare var api: BlueskyAPI;
12
12
declare var isIncognito: boolean;
13
13
declare var biohazardEnabled: boolean;
14
-
declare var loginDialog: AnyElement;
15
-
declare var accountMenu: AnyElement;
14
+
declare var loginDialog: HTMLElement;
15
+
declare var accountMenu: HTMLElement;
16
16
declare var avatarPreloader: IntersectionObserver;
17
17
18
-
type SomeElement = Element | HTMLElement | AnyElement;
19
18
type json = Record<string, any>;
20
19
21
-
interface AnyElement {
22
-
classList: CSSClassList;
23
-
className: string;
24
-
innerText: string;
25
-
innerHTML: string;
26
-
nextElementSibling: AnyElement;
27
-
parentNode: AnyElement;
28
-
src: string;
29
-
style: CSSStyleDeclaration;
20
+
function $tag(tag: string): HTMLElement;
21
+
function $tag<T>(tag: string, type: new (...args: any[]) => T): T;
22
+
function $tag(tag: string, params: string | object): HTMLElement;
23
+
function $tag<T>(tag: string, params: string | object, type: new (...args: any[]) => T): T;
30
24
31
-
addEventListener<K extends keyof DocumentEventMap>(
32
-
type: K, listener: EventListenerOrEventListenerObject
33
-
): void;
25
+
function $id(id: string): HTMLElement;
26
+
function $id<T>(id: string, type: new (...args: any[]) => T): T;
34
27
35
-
append(...e: Array<string | SomeElement>): void;
36
-
appendChild(e: SomeElement): void;
37
-
closest(q: string): AnyElement;
38
-
querySelector(q: string): AnyElement;
39
-
querySelectorAll(q: string): AnyElement[];
40
-
prepend(...e: Array<string | SomeElement>): void;
41
-
remove(): void;
42
-
replaceChildren(e: SomeElement): void;
43
-
replaceWith(e: SomeElement): void;
44
-
}
28
+
function $(element: Node | EventTarget | null): HTMLElement;
29
+
function $<T>(element: Node | EventTarget | null, type: new (...args: any[]) => T): T;
+21
-6
utils.js
+21
-6
utils.js
···
17
17
}
18
18
}
19
19
20
-
/** @param {string} tag, @param {string | object} [params], @returns {any} */
20
+
/**
21
+
* @template T
22
+
* @param {string} tag
23
+
* @param {string | object} params
24
+
* @param {new (...args: any[]) => T} type
25
+
* @returns {T}
26
+
*/
21
27
22
-
function $tag(tag, params) {
28
+
function $tag(tag, params, type) {
23
29
let element;
24
30
let parts = tag.split('.');
25
31
···
45
51
}
46
52
}
47
53
48
-
return element;
54
+
return /** @type {T} */ (element);
55
+
}
56
+
57
+
function $id(name, type) {
58
+
return (document.getElementById(name));
49
59
}
50
60
51
-
/** @param {string} name, @returns {any} */
61
+
/**
62
+
* @template T
63
+
* @param {Node | EventTarget | null} element
64
+
* @param {new (...args: any[]) => T} type
65
+
* @returns {T}
66
+
*/
52
67
53
-
function $id(name) {
54
-
return document.getElementById(name);
68
+
function $(element, type) {
69
+
return /** @type {T} */ (element);
55
70
}
56
71
57
72
/** @param {string} uri, @returns {AtURI} */