14
14
* https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube
15
15
* https://github.com/Daugilas/lazyYT https://github.com/vb/lazyframe
16
16
*/
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
+
18
28
constructor ( ) {
19
29
super ( ) ;
20
- this . __iframeLoaded = false ;
21
- this . __setupDom ( ) ;
30
+ this . setupDom ( ) ;
22
31
}
23
32
24
- static get observedAttributes ( ) {
33
+ static get observedAttributes ( ) : string [ ] {
25
34
return [ 'videoid' ] ;
26
35
}
27
36
28
- connectedCallback ( ) {
37
+ connectedCallback ( ) : void {
29
38
this . addEventListener ( 'pointerover' , LiteYTEmbed . warmConnections , {
30
39
once : true ,
31
40
} ) ;
32
41
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
+ }
34
87
}
35
88
36
89
/**
37
90
* Define our shadowDOM for the component
38
- * @private
39
91
*/
40
- __setupDom ( ) {
92
+ private setupDom ( ) : void {
41
93
const shadowDom = this . attachShadow ( { mode : 'open' } ) ;
42
94
shadowDom . innerHTML = `
43
95
<style>
@@ -126,36 +178,37 @@ class LiteYTEmbed extends HTMLElement {
126
178
<button class="lty-playbtn"></button>
127
179
</div>
128
180
` ;
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
+ ) ! ,
134
192
} ;
135
- this . __domRefPlayButton = this . shadowRoot . querySelector ( '.lty-playbtn' ) ;
193
+ this . domRefPlayButton = this . shadowRoot . querySelector < HTMLButtonElement > (
194
+ '.lty-playbtn' ,
195
+ ) ! ;
136
196
}
137
197
138
198
/**
139
199
* Parse our attributes and fire up some placeholders
140
- * @private
141
200
*/
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 ( ) ;
150
203
151
- this . __domRefPlayButton . setAttribute (
204
+ this . domRefPlayButton . setAttribute (
152
205
'aria-label' ,
153
206
`${ this . videoPlay } : ${ this . videoTitle } ` ,
154
207
) ;
155
208
this . setAttribute ( 'title' , `${ this . videoPlay } : ${ this . videoTitle } ` ) ;
156
209
157
210
if ( this . autoLoad ) {
158
- this . __initIntersectionObserver ( ) ;
211
+ this . initIntersectionObserver ( ) ;
159
212
}
160
213
}
161
214
@@ -165,16 +218,20 @@ class LiteYTEmbed extends HTMLElement {
165
218
* @param {* } oldVal
166
219
* @param {* } newVal
167
220
*/
168
- attributeChangedCallback ( name , oldVal , newVal ) {
221
+ attributeChangedCallback (
222
+ name : string ,
223
+ oldVal : unknown ,
224
+ newVal : unknown ,
225
+ ) : void {
169
226
switch ( name ) {
170
227
case 'videoid' : {
171
228
if ( oldVal !== newVal ) {
172
- this . __setupComponent ( ) ;
229
+ this . setupComponent ( ) ;
173
230
174
231
// 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 ( ) ;
178
235
}
179
236
}
180
237
break ;
@@ -186,49 +243,46 @@ class LiteYTEmbed extends HTMLElement {
186
243
187
244
/**
188
245
* Inject the iframe into the component body
189
- * @private
190
246
*/
191
- __addIframe ( ) {
192
- if ( ! this . __iframeLoaded ) {
247
+ private addIframe ( ) : void {
248
+ if ( ! this . iframeLoaded ) {
193
249
const iframeHTML = `
194
250
<iframe frameborder="0"
195
251
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
196
252
src="https://www.youtube.com/embed/${ this . videoId } ?autoplay=1&start=${ this . videoStartAt } "
197
253
></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 ;
201
257
}
202
258
}
203
259
204
260
/**
205
261
* Setup the placeholder image for the component
206
- * @private
207
262
*/
208
- __initImagePlaceholder ( ) {
263
+ private initImagePlaceholder ( ) : void {
209
264
// we don't know which image type to preload, so warm the connection
210
265
LiteYTEmbed . addPrefetch ( 'preconnect' , 'https://i.ytimg.com/' ) ;
211
266
212
267
const posterUrlWebp = `https://i.ytimg.com/vi_webp/${ this . videoId } /hqdefault.webp` ;
213
268
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 (
218
273
'aria-label' ,
219
274
`${ this . videoPlay } : ${ this . videoTitle } ` ,
220
275
) ;
221
- this . __domRefImg . fallback . setAttribute (
276
+ this . domRefImg . fallback . setAttribute (
222
277
'alt' ,
223
278
`${ this . videoPlay } : ${ this . videoTitle } ` ,
224
279
) ;
225
280
}
226
281
227
282
/**
228
283
* Setup the Intersection Observer to load the iframe when scrolled into view
229
- * @private
230
284
*/
231
- __initIntersectionObserver ( ) {
285
+ private initIntersectionObserver ( ) : void {
232
286
if (
233
287
'IntersectionObserver' in window &&
234
288
'IntersectionObserverEntry' in window
@@ -241,9 +295,9 @@ class LiteYTEmbed extends HTMLElement {
241
295
242
296
const observer = new IntersectionObserver ( ( entries , observer ) => {
243
297
entries . forEach ( entry => {
244
- if ( entry . isIntersecting && ! this . __iframeLoaded ) {
298
+ if ( entry . isIntersecting && ! this . iframeLoaded ) {
245
299
LiteYTEmbed . warmConnections ( ) ;
246
- this . __addIframe ( ) ;
300
+ this . addIframe ( ) ;
247
301
observer . unobserve ( this ) ;
248
302
}
249
303
} ) ;
@@ -253,17 +307,22 @@ class LiteYTEmbed extends HTMLElement {
253
307
}
254
308
}
255
309
310
+ private static preconnected = false ;
311
+
256
312
/**
257
313
* Add a <link rel={preload | preconnect} ...> to the head
314
+ * @param {* } kind
315
+ * @param {* } url
316
+ * @param {* } as
258
317
*/
259
- static addPrefetch ( kind , url , as ) {
318
+ private static addPrefetch ( kind : string , url : string , as ?: string ) : void {
260
319
const linkElem = document . createElement ( 'link' ) ;
261
320
linkElem . rel = kind ;
262
321
linkElem . href = url ;
263
322
if ( as ) {
264
323
linkElem . as = as ;
265
324
}
266
- linkElem . crossorigin = true ;
325
+ linkElem . crossOrigin = ' true' ;
267
326
document . head . append ( linkElem ) ;
268
327
}
269
328
@@ -277,7 +336,7 @@ class LiteYTEmbed extends HTMLElement {
277
336
* http://crbug.com/593267 But TBH, I don't think it'll happen soon with Site
278
337
* Isolation and split caches adding serious complexity.
279
338
*/
280
- static warmConnections ( ) {
339
+ private static warmConnections ( ) : void {
281
340
if ( LiteYTEmbed . preconnected ) return ;
282
341
// Host that YT uses to serve JS needed by player, per amp-youtube
283
342
LiteYTEmbed . addPrefetch ( 'preconnect' , 'https://s.ytimg.com' ) ;
0 commit comments