Skip to content

Commit a2c4977

Browse files
authored
feat: convert to typescript (justinribeiro#10)
1 parent f382373 commit a2c4977

File tree

7 files changed

+332
-75
lines changed

7 files changed

+332
-75
lines changed

.eslintrc.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
module.exports = {
2-
extends: ['eslint:recommended', 'google', 'prettier'],
2+
parser: '@typescript-eslint/parser',
3+
extends: [
4+
'eslint:recommended',
5+
'google',
6+
'prettier',
7+
'plugin:@typescript-eslint/eslint-recommended',
8+
'plugin:@typescript-eslint/recommended',
9+
],
310
parserOptions: {
411
ecmaVersion: 2018,
512
sourceType: 'module',
613
},
714
env: {
815
browser: true,
916
},
10-
plugins: ['html', 'lit'],
17+
plugins: ['html', 'lit', '@typescript-eslint'],
1118
rules: {
1219
'max-len': [
1320
'error',

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
node_modules
2+
package-lock.json
3+
*.js
4+
*.map
5+
*.d.ts

.npmignore

Whitespace-only changes.

lite-youtube.js renamed to lite-youtube.ts

+109-50
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,82 @@
1414
* https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube
1515
* https://github.com/Daugilas/lazyYT https://github.com/vb/lazyframe
1616
*/
17-
class LiteYTEmbed extends HTMLElement {
17+
export class LiteYTEmbed extends HTMLElement {
18+
shadowRoot!: ShadowRoot;
19+
private iframeLoaded = false;
20+
private domRefFrame!: HTMLDivElement;
21+
private domRefImg!: {
22+
fallback: HTMLImageElement;
23+
webp: HTMLSourceElement;
24+
jpeg: HTMLSourceElement;
25+
};
26+
private domRefPlayButton!: HTMLButtonElement;
27+
1828
constructor() {
1929
super();
20-
this.__iframeLoaded = false;
21-
this.__setupDom();
30+
this.setupDom();
2231
}
2332

24-
static get observedAttributes() {
33+
static get observedAttributes(): string[] {
2534
return ['videoid'];
2635
}
2736

28-
connectedCallback() {
37+
connectedCallback(): void {
2938
this.addEventListener('pointerover', LiteYTEmbed.warmConnections, {
3039
once: true,
3140
});
3241

33-
this.addEventListener('click', e => this.__addIframe());
42+
this.addEventListener('click', () => this.addIframe());
43+
}
44+
45+
get videoId(): string {
46+
return encodeURIComponent(this.getAttribute('videoid') || '');
47+
}
48+
49+
set videoId(id: string) {
50+
this.setAttribute('videoid', id);
51+
}
52+
53+
get videoTitle(): string {
54+
return this.getAttribute('videotitle') || 'Video';
55+
}
56+
57+
set videoTitle(title: string) {
58+
this.setAttribute('videotitle', title);
59+
}
60+
61+
get videoPlay(): string {
62+
return this.getAttribute('videoPlay') || 'Play';
63+
}
64+
65+
set videoPlay(name: string) {
66+
this.setAttribute('videoPlay', name);
67+
}
68+
69+
get videoStartAt(): number {
70+
return Number(this.getAttribute('videoPlay') || '0');
71+
}
72+
73+
set videoStartAt(time: number) {
74+
this.setAttribute('videoPlay', String(time));
75+
}
76+
77+
get autoLoad(): boolean {
78+
return this.hasAttribute('autoload');
79+
}
80+
81+
set autoLoad(value: boolean) {
82+
if (value) {
83+
this.setAttribute('autoload', '');
84+
} else {
85+
this.removeAttribute('autoload');
86+
}
3487
}
3588

3689
/**
3790
* Define our shadowDOM for the component
38-
* @private
3991
*/
40-
__setupDom() {
92+
private setupDom(): void {
4193
const shadowDom = this.attachShadow({mode: 'open'});
4294
shadowDom.innerHTML = `
4395
<style>
@@ -126,36 +178,37 @@ class LiteYTEmbed extends HTMLElement {
126178
<button class="lty-playbtn"></button>
127179
</div>
128180
`;
129-
this.__domRefFrame = this.shadowRoot.querySelector('#frame');
130-
this.__domRefImg = {
131-
fallback: this.shadowRoot.querySelector('#fallbackPlaceholder'),
132-
webp: this.shadowRoot.querySelector('#webpPlaceholder'),
133-
jpeg: this.shadowRoot.querySelector('#jpegPlaceholder'),
181+
this.domRefFrame = this.shadowRoot.querySelector<HTMLDivElement>('#frame')!;
182+
this.domRefImg = {
183+
fallback: this.shadowRoot.querySelector<HTMLImageElement>(
184+
'#fallbackPlaceholder',
185+
)!,
186+
webp: this.shadowRoot.querySelector<HTMLSourceElement>(
187+
'#webpPlaceholder',
188+
)!,
189+
jpeg: this.shadowRoot.querySelector<HTMLSourceElement>(
190+
'#jpegPlaceholder',
191+
)!,
134192
};
135-
this.__domRefPlayButton = this.shadowRoot.querySelector('.lty-playbtn');
193+
this.domRefPlayButton = this.shadowRoot.querySelector<HTMLButtonElement>(
194+
'.lty-playbtn',
195+
)!;
136196
}
137197

138198
/**
139199
* Parse our attributes and fire up some placeholders
140-
* @private
141200
*/
142-
__setupComponent() {
143-
this.videoId = encodeURIComponent(this.getAttribute('videoid'));
144-
this.videoTitle = this.getAttribute('videotitle') || 'Video';
145-
this.videoPlay = this.getAttribute('videoplay') || 'Play';
146-
this.videoStartAt = this.getAttribute('start') || 0;
147-
this.autoLoad = this.getAttribute('autoload') === '' ? true : false;
148-
149-
this.__initImagePlaceholder();
201+
private setupComponent(): void {
202+
this.initImagePlaceholder();
150203

151-
this.__domRefPlayButton.setAttribute(
204+
this.domRefPlayButton.setAttribute(
152205
'aria-label',
153206
`${this.videoPlay}: ${this.videoTitle}`,
154207
);
155208
this.setAttribute('title', `${this.videoPlay}: ${this.videoTitle}`);
156209

157210
if (this.autoLoad) {
158-
this.__initIntersectionObserver();
211+
this.initIntersectionObserver();
159212
}
160213
}
161214

@@ -165,16 +218,20 @@ class LiteYTEmbed extends HTMLElement {
165218
* @param {*} oldVal
166219
* @param {*} newVal
167220
*/
168-
attributeChangedCallback(name, oldVal, newVal) {
221+
attributeChangedCallback(
222+
name: string,
223+
oldVal: unknown,
224+
newVal: unknown,
225+
): void {
169226
switch (name) {
170227
case 'videoid': {
171228
if (oldVal !== newVal) {
172-
this.__setupComponent();
229+
this.setupComponent();
173230

174231
// if we have a previous iframe, remove it and the activated class
175-
if (this.__domRefFrame.classList.contains('lyt-activated')) {
176-
this.__domRefFrame.classList.remove('lyt-activated');
177-
this.shadowRoot.querySelector('iframe').remove();
232+
if (this.domRefFrame.classList.contains('lyt-activated')) {
233+
this.domRefFrame.classList.remove('lyt-activated');
234+
this.shadowRoot.querySelector('iframe')!.remove();
178235
}
179236
}
180237
break;
@@ -186,49 +243,46 @@ class LiteYTEmbed extends HTMLElement {
186243

187244
/**
188245
* Inject the iframe into the component body
189-
* @private
190246
*/
191-
__addIframe() {
192-
if (!this.__iframeLoaded) {
247+
private addIframe(): void {
248+
if (!this.iframeLoaded) {
193249
const iframeHTML = `
194250
<iframe frameborder="0"
195251
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
196252
src="https://www.youtube.com/embed/${this.videoId}?autoplay=1&start=${this.videoStartAt}"
197253
></iframe>`;
198-
this.__domRefFrame.insertAdjacentHTML('beforeend', iframeHTML);
199-
this.__domRefFrame.classList.add('lyt-activated');
200-
this.__iframeLoaded = true;
254+
this.domRefFrame.insertAdjacentHTML('beforeend', iframeHTML);
255+
this.domRefFrame.classList.add('lyt-activated');
256+
this.iframeLoaded = true;
201257
}
202258
}
203259

204260
/**
205261
* Setup the placeholder image for the component
206-
* @private
207262
*/
208-
__initImagePlaceholder() {
263+
private initImagePlaceholder(): void {
209264
// we don't know which image type to preload, so warm the connection
210265
LiteYTEmbed.addPrefetch('preconnect', 'https://i.ytimg.com/');
211266

212267
const posterUrlWebp = `https://i.ytimg.com/vi_webp/${this.videoId}/hqdefault.webp`;
213268
const posterUrlJpeg = `https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg`;
214-
this.__domRefImg.webp.srcset = posterUrlWebp;
215-
this.__domRefImg.jpeg.srcset = posterUrlJpeg;
216-
this.__domRefImg.fallback.src = posterUrlJpeg;
217-
this.__domRefImg.fallback.setAttribute(
269+
this.domRefImg.webp.srcset = posterUrlWebp;
270+
this.domRefImg.jpeg.srcset = posterUrlJpeg;
271+
this.domRefImg.fallback.src = posterUrlJpeg;
272+
this.domRefImg.fallback.setAttribute(
218273
'aria-label',
219274
`${this.videoPlay}: ${this.videoTitle}`,
220275
);
221-
this.__domRefImg.fallback.setAttribute(
276+
this.domRefImg.fallback.setAttribute(
222277
'alt',
223278
`${this.videoPlay}: ${this.videoTitle}`,
224279
);
225280
}
226281

227282
/**
228283
* Setup the Intersection Observer to load the iframe when scrolled into view
229-
* @private
230284
*/
231-
__initIntersectionObserver() {
285+
private initIntersectionObserver(): void {
232286
if (
233287
'IntersectionObserver' in window &&
234288
'IntersectionObserverEntry' in window
@@ -241,9 +295,9 @@ class LiteYTEmbed extends HTMLElement {
241295

242296
const observer = new IntersectionObserver((entries, observer) => {
243297
entries.forEach(entry => {
244-
if (entry.isIntersecting && !this.__iframeLoaded) {
298+
if (entry.isIntersecting && !this.iframeLoaded) {
245299
LiteYTEmbed.warmConnections();
246-
this.__addIframe();
300+
this.addIframe();
247301
observer.unobserve(this);
248302
}
249303
});
@@ -253,17 +307,22 @@ class LiteYTEmbed extends HTMLElement {
253307
}
254308
}
255309

310+
private static preconnected = false;
311+
256312
/**
257313
* Add a <link rel={preload | preconnect} ...> to the head
314+
* @param {*} kind
315+
* @param {*} url
316+
* @param {*} as
258317
*/
259-
static addPrefetch(kind, url, as) {
318+
private static addPrefetch(kind: string, url: string, as?: string): void {
260319
const linkElem = document.createElement('link');
261320
linkElem.rel = kind;
262321
linkElem.href = url;
263322
if (as) {
264323
linkElem.as = as;
265324
}
266-
linkElem.crossorigin = true;
325+
linkElem.crossOrigin = 'true';
267326
document.head.append(linkElem);
268327
}
269328

@@ -277,7 +336,7 @@ class LiteYTEmbed extends HTMLElement {
277336
* http://crbug.com/593267 But TBH, I don't think it'll happen soon with Site
278337
* Isolation and split caches adding serious complexity.
279338
*/
280-
static warmConnections() {
339+
private static warmConnections(): void {
281340
if (LiteYTEmbed.preconnected) return;
282341
// Host that YT uses to serve JS needed by player, per amp-youtube
283342
LiteYTEmbed.addPrefetch('preconnect', 'https://s.ytimg.com');

package.json

+16-3
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,32 @@
66
"type": "git",
77
"url": "[email protected]:justinribeiro/lite-youtube.git"
88
},
9+
"scripts": {
10+
"build": "tsc --project tsconfig.json",
11+
"lint": "npm run lint:eslint && npm run lint:prettier",
12+
"lint:eslint": "eslint *.ts --ignore-path .gitignore",
13+
"lint:prettier": "prettier --check *.ts --ignore-path .gitignore",
14+
"postinstall": "npm run build",
15+
"prepublishOnly": "npm run build"
16+
},
917
"license": "MIT",
1018
"version": "0.6.2",
1119
"main": "lite-youtube.js",
1220
"module": "lite-youtube.js",
21+
"types": "lite-youtube.d.ts",
1322
"keywords": [
1423
"web components",
1524
"youtube"
1625
],
1726
"devDependencies": {
18-
"eslint": "^6.4.0",
27+
"@typescript-eslint/eslint-plugin": "^2.14.0",
28+
"@typescript-eslint/parser": "^2.14.0",
29+
"eslint": "^6.8.0",
1930
"eslint-config-google": "^0.14.0",
20-
"eslint-config-prettier": "^6.3.0",
31+
"eslint-config-prettier": "^6.9.0",
2132
"eslint-plugin-html": "^6.0.0",
22-
"prettier": "^1.18.2"
33+
"eslint-plugin-lit": "^1.2.0",
34+
"prettier": "^1.19.1",
35+
"typescript": "^3.7.4"
2336
}
2437
}

0 commit comments

Comments
 (0)