@smartimpact-it/modern-video-embed 2.0.1
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 +1205 -0
- package/dist/components/VimeoEmbed.d.ts +143 -0
- package/dist/components/VimeoEmbed.d.ts.map +1 -0
- package/dist/components/VimeoEmbed.js +1176 -0
- package/dist/components/VimeoEmbed.js.map +1 -0
- package/dist/components/VimeoEmbed.min.js +1 -0
- package/dist/components/YouTubeEmbed.d.ts +225 -0
- package/dist/components/YouTubeEmbed.d.ts.map +1 -0
- package/dist/components/YouTubeEmbed.js +1354 -0
- package/dist/components/YouTubeEmbed.js.map +1 -0
- package/dist/components/YouTubeEmbed.min.js +1 -0
- package/dist/css/components.css +349 -0
- package/dist/css/components.css.map +1 -0
- package/dist/css/components.min.css +1 -0
- package/dist/css/main.css +12210 -0
- package/dist/css/main.css.map +1 -0
- package/dist/css/main.min.css +7 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/index.min.js +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/vimeo-only.d.ts +7 -0
- package/dist/vimeo-only.d.ts.map +1 -0
- package/dist/vimeo-only.js +8 -0
- package/dist/vimeo-only.js.map +1 -0
- package/dist/vimeo-only.min.js +1 -0
- package/dist/youtube-only.d.ts +7 -0
- package/dist/youtube-only.d.ts.map +1 -0
- package/dist/youtube-only.js +8 -0
- package/dist/youtube-only.js.map +1 -0
- package/dist/youtube-only.min.js +1 -0
- package/package.json +75 -0
- package/src/components/VimeoEmbed.ts +1340 -0
- package/src/components/YouTubeEmbed.ts +1568 -0
- package/src/index.ts +3 -0
- package/src/styles/README.md +56 -0
- package/src/styles/components.scss +7 -0
- package/src/styles/main.scss +10 -0
- package/src/styles/vimeo-embed.scss +255 -0
- package/src/styles/youtube-embed.scss +261 -0
- package/src/types/common.d.ts +198 -0
- package/src/types/index.ts +7 -0
- package/src/types/vimeo-embed.d.ts +80 -0
- package/src/types/youtube-embed.d.ts +83 -0
- package/src/vimeo-only.ts +9 -0
- package/src/youtube-only.ts +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,1205 @@
|
|
|
1
|
+
# Video Embed Components
|
|
2
|
+
|
|
3
|
+
Modern, performant YouTube and Vimeo embed web components with lazy loading, event-driven architecture, and comprehensive API control.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- **🎮 Full Control API** - Play, pause, stop, mute, and load videos programmatically
|
|
8
|
+
- **📡 Event-Driven** - Listen to player events (ready, play, pause, error, etc.)
|
|
9
|
+
- **⚡ Lazy Loading** - Improve performance with poster images and on-demand loading
|
|
10
|
+
- **🔗 Flexible URLs** - Support for full URLs, short links, and video IDs
|
|
11
|
+
- **📱 Responsive** - Mobile-first design with touch-friendly controls
|
|
12
|
+
- **♿ Accessible** - Keyboard navigation and screen reader support
|
|
13
|
+
- **🎨 Customizable** - Custom overlay controls or native player controls
|
|
14
|
+
- **🎬 Multi-Platform** - Both YouTube and Vimeo support
|
|
15
|
+
|
|
16
|
+
## 🚀 Quick Start
|
|
17
|
+
|
|
18
|
+
### Installation
|
|
19
|
+
|
|
20
|
+
#### Via NPM (recommended for projects)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @siit-dev/video-embed
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
#### Via CDN (for quick prototypes)
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<script
|
|
30
|
+
type="module"
|
|
31
|
+
src="https://unpkg.com/@siit-dev/video-embed@latest/dist/index.js"
|
|
32
|
+
></script>
|
|
33
|
+
<link
|
|
34
|
+
rel="stylesheet"
|
|
35
|
+
href="https://unpkg.com/@siit-dev/video-embed@latest/dist/css/components.css"
|
|
36
|
+
/>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Manual Build
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/siit-dev/si_video_embed.git
|
|
43
|
+
cd si_video_embed
|
|
44
|
+
npm install
|
|
45
|
+
npm run build
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Bundle Options
|
|
49
|
+
|
|
50
|
+
Choose the right bundle for your needs:
|
|
51
|
+
|
|
52
|
+
| Bundle | File | Size (Dev) | Size (Prod) | Use Case |
|
|
53
|
+
| ----------------- | ------------------------- | ---------- | ----------- | ---------------------- |
|
|
54
|
+
| **Full** | `dist/index.js` | 58KB | **29KB** | Both YouTube and Vimeo |
|
|
55
|
+
| **YouTube Only** | `dist/youtube-only.js` | 29KB | **15KB** | Only YouTube embeds |
|
|
56
|
+
| **Vimeo Only** | `dist/vimeo-only.js` | 29KB | **14KB** | Only Vimeo embeds |
|
|
57
|
+
| **Styles (Core)** | `dist/css/components.css` | 9KB | **7KB** | Component styles only |
|
|
58
|
+
| **Styles (Full)** | `dist/css/main.css` | 276KB | **224KB** | With Bootstrap (demos) |
|
|
59
|
+
|
|
60
|
+
**Production Files** use minified versions (`.min.js` / `.min.css`) for ~50% size reduction.
|
|
61
|
+
|
|
62
|
+
**Recommended for Production:**
|
|
63
|
+
|
|
64
|
+
- Use **YouTube-only** or **Vimeo-only** bundles when possible (~14-15KB minified)
|
|
65
|
+
- Use **Core styles** (`components.css`) instead of full Bootstrap (~7KB minified)
|
|
66
|
+
- Enable gzip compression on your server (reduces size by additional 60-70%)
|
|
67
|
+
|
|
68
|
+
### Basic Usage
|
|
69
|
+
|
|
70
|
+
#### Include Required Files
|
|
71
|
+
|
|
72
|
+
**For Production** (recommended - use minified files):
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<!-- Full bundle: Both YouTube and Vimeo (29KB minified) -->
|
|
76
|
+
<script type="module" src="dist/index.min.js"></script>
|
|
77
|
+
<link rel="stylesheet" href="dist/css/components.min.css" />
|
|
78
|
+
|
|
79
|
+
<!-- OR YouTube only (15KB minified) -->
|
|
80
|
+
<script type="module" src="dist/youtube-only.min.js"></script>
|
|
81
|
+
<link rel="stylesheet" href="dist/css/components.min.css" />
|
|
82
|
+
|
|
83
|
+
<!-- OR Vimeo only (14KB minified) -->
|
|
84
|
+
<script type="module" src="dist/vimeo-only.min.js"></script>
|
|
85
|
+
<link rel="stylesheet" href="dist/css/components.min.css" />
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**For Development** (use unminified files for debugging):
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<!-- Full bundle: Both YouTube and Vimeo -->
|
|
92
|
+
<script type="module" src="dist/index.js"></script>
|
|
93
|
+
|
|
94
|
+
<!-- OR YouTube only (smaller) -->
|
|
95
|
+
<script type="module" src="dist/youtube-only.js"></script>
|
|
96
|
+
|
|
97
|
+
<!-- OR Vimeo only (smaller) -->
|
|
98
|
+
<script type="module" src="dist/vimeo-only.js"></script>
|
|
99
|
+
|
|
100
|
+
<!-- Component CSS only -->
|
|
101
|
+
<link rel="stylesheet" href="dist/css/components.css" />
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**For Demos/Examples** (includes Bootstrap):
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<!-- Component JavaScript -->
|
|
108
|
+
<script type="module" src="dist/index.js"></script>
|
|
109
|
+
|
|
110
|
+
<!-- Full CSS with Bootstrap (~276KB) -->
|
|
111
|
+
<link rel="stylesheet" href="dist/css/main.css" />
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### YouTube
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<!-- Use in HTML -->
|
|
118
|
+
<youtube-embed url="https://www.youtube.com/watch?v=dQw4w9WgXcQ" controls>
|
|
119
|
+
</youtube-embed>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Vimeo
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<!-- Use in HTML -->
|
|
126
|
+
<vimeo-embed url="https://vimeo.com/76979871" controls> </vimeo-embed>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Advanced Usage
|
|
130
|
+
|
|
131
|
+
#### YouTube
|
|
132
|
+
|
|
133
|
+
```html
|
|
134
|
+
<!-- Lazy loading with custom poster -->
|
|
135
|
+
<youtube-embed
|
|
136
|
+
video-id="dQw4w9WgXcQ"
|
|
137
|
+
lazy
|
|
138
|
+
autoplay
|
|
139
|
+
muted
|
|
140
|
+
poster="custom-poster.jpg"
|
|
141
|
+
>
|
|
142
|
+
</youtube-embed>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Vimeo
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<!-- Lazy loading with custom poster -->
|
|
149
|
+
<vimeo-embed
|
|
150
|
+
video-id="123456789"
|
|
151
|
+
lazy
|
|
152
|
+
autoplay
|
|
153
|
+
muted
|
|
154
|
+
poster="custom-poster.jpg"
|
|
155
|
+
>
|
|
156
|
+
</vimeo-embed>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 📖 API Reference
|
|
160
|
+
|
|
161
|
+
### Attributes (Both Components)
|
|
162
|
+
|
|
163
|
+
| Attribute | Type | Description |
|
|
164
|
+
| ------------- | ------- | -------------------------------------------- |
|
|
165
|
+
| `url` | String | Full video URL |
|
|
166
|
+
| `video-id` | String | Video ID (YouTube: 11 chars, Vimeo: numeric) |
|
|
167
|
+
| `autoplay` | Boolean | Auto-play video when loaded |
|
|
168
|
+
| `controls` | Boolean | Show native player controls |
|
|
169
|
+
| `lazy` | Boolean | Enable lazy loading with poster |
|
|
170
|
+
| `muted` | Boolean | Start video muted |
|
|
171
|
+
| `poster` | String | Custom poster image URL |
|
|
172
|
+
| `background` | Boolean | Enable background video mode |
|
|
173
|
+
| `player-vars` | JSON | JSON object of extra player parameters |
|
|
174
|
+
|
|
175
|
+
### Methods
|
|
176
|
+
|
|
177
|
+
#### YouTube Embed
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
const embed = document.querySelector("youtube-embed");
|
|
181
|
+
|
|
182
|
+
// Playback control
|
|
183
|
+
embed.play(); // ▶️ Play video
|
|
184
|
+
embed.pause(); // ⏸️ Pause video
|
|
185
|
+
embed.stopVideo(); // ⏹️ Stop video
|
|
186
|
+
embed.togglePlay(); // ⏯️ Toggle play/pause
|
|
187
|
+
|
|
188
|
+
// Audio control
|
|
189
|
+
embed.mute(); // 🔇 Mute audio
|
|
190
|
+
embed.unmute(); // 🔊 Unmute audio
|
|
191
|
+
embed.toggleMute(); // 🔇 Toggle mute
|
|
192
|
+
|
|
193
|
+
// Video management
|
|
194
|
+
embed.loadVideo("dQw4w9WgXcQ"); // Load by video ID
|
|
195
|
+
embed.loadVideo("https://youtu.be/dQw4w9WgXcQ"); // Load by URL
|
|
196
|
+
|
|
197
|
+
// State inspection
|
|
198
|
+
const state = embed.getPlayerState();
|
|
199
|
+
// Returns: { playing: boolean, muted: boolean, videoId: string, ready: boolean, initialized: boolean }
|
|
200
|
+
|
|
201
|
+
// Quality control
|
|
202
|
+
const qualities = embed.getAvailableQualities(); // Get available quality levels
|
|
203
|
+
const current = embed.getCurrentQuality(); // Get current quality
|
|
204
|
+
embed.setQuality("hd1080"); // Set quality to 1080p
|
|
205
|
+
|
|
206
|
+
// Fullscreen control
|
|
207
|
+
embed.enterFullscreen(); // Enter fullscreen mode
|
|
208
|
+
embed.exitFullscreen(); // Exit fullscreen mode
|
|
209
|
+
embed.toggleFullscreen(); // Toggle fullscreen
|
|
210
|
+
const isFs = embed.isFullscreen(); // Check fullscreen state
|
|
211
|
+
|
|
212
|
+
// Set extra player parameters
|
|
213
|
+
embed.playerVars = { loop: 1, playlist: "dQw4w9WgXcQ" };
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Passing Extra Parameters:**
|
|
217
|
+
|
|
218
|
+
You can pass additional parameters to the YouTube or Vimeo player using the `player-vars` attribute or `playerVars` property:
|
|
219
|
+
|
|
220
|
+
```html
|
|
221
|
+
<!-- YouTube: Loop a video -->
|
|
222
|
+
<youtube-embed
|
|
223
|
+
video-id="dQw4w9WgXcQ"
|
|
224
|
+
player-vars='{"loop": 1, "playlist": "dQw4w9WgXcQ"}'
|
|
225
|
+
></youtube-embed>
|
|
226
|
+
|
|
227
|
+
<!-- YouTube: Custom start time and quality -->
|
|
228
|
+
<youtube-embed
|
|
229
|
+
video-id="dQw4w9WgXcQ"
|
|
230
|
+
player-vars='{"start": 30, "end": 90, "quality": "hd720"}'
|
|
231
|
+
></youtube-embed>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
// Set via JavaScript
|
|
236
|
+
const embed = document.querySelector("youtube-embed");
|
|
237
|
+
embed.playerVars = {
|
|
238
|
+
loop: 1,
|
|
239
|
+
playlist: "dQw4w9WgXcQ", // Required for loop to work
|
|
240
|
+
start: 10, // Start at 10 seconds
|
|
241
|
+
modestbranding: 1, // Minimal YouTube branding
|
|
242
|
+
rel: 0, // Don't show related videos
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
See [YouTube Player Parameters](https://developers.google.com/youtube/player_parameters) for all available options.
|
|
247
|
+
|
|
248
|
+
#### Vimeo Embed
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
const embed = document.querySelector("vimeo-embed");
|
|
252
|
+
|
|
253
|
+
// Playback control
|
|
254
|
+
embed.play(); // ▶️ Play video
|
|
255
|
+
embed.pause(); // ⏸️ Pause video
|
|
256
|
+
embed.stopVideo(); // ⏹️ Stop video
|
|
257
|
+
embed.togglePlay(); // ⏯️ Toggle play/pause
|
|
258
|
+
|
|
259
|
+
// Audio control
|
|
260
|
+
embed.mute(); // 🔇 Mute audio
|
|
261
|
+
embed.unmute(); // 🔊 Unmute audio
|
|
262
|
+
embed.toggleMute(); // 🔇 Toggle mute
|
|
263
|
+
|
|
264
|
+
// Video management
|
|
265
|
+
embed.loadVideo("123456789"); // Load by video ID
|
|
266
|
+
embed.loadVideo("https://vimeo.com/123456789"); // Load by URL
|
|
267
|
+
|
|
268
|
+
// State inspection
|
|
269
|
+
const state = embed.getPlayerState();
|
|
270
|
+
// Returns: { playing: boolean, muted: boolean, videoId: string, ready: boolean, initialized: boolean }
|
|
271
|
+
|
|
272
|
+
// Async methods
|
|
273
|
+
const currentTime = await embed.getCurrentTime();
|
|
274
|
+
const isMuted = await embed.isMuted();
|
|
275
|
+
|
|
276
|
+
// Quality control (async)
|
|
277
|
+
const qualities = await embed.getAvailableQualities(); // Get available quality levels
|
|
278
|
+
const current = await embed.getCurrentQuality(); // Get current quality
|
|
279
|
+
await embed.setQuality("720p"); // Set quality to 720p
|
|
280
|
+
|
|
281
|
+
// Fullscreen control
|
|
282
|
+
await embed.enterFullscreen(); // Enter fullscreen mode
|
|
283
|
+
await embed.exitFullscreen(); // Exit fullscreen mode
|
|
284
|
+
await embed.toggleFullscreen(); // Toggle fullscreen
|
|
285
|
+
const isFs = embed.isFullscreen(); // Check fullscreen state
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Passing Extra Parameters:**
|
|
289
|
+
|
|
290
|
+
You can pass additional parameters to the Vimeo player using the `player-vars` attribute or `playerVars` property:
|
|
291
|
+
|
|
292
|
+
```html
|
|
293
|
+
<!-- Vimeo: Loop a video -->
|
|
294
|
+
<vimeo-embed video-id="76979871" player-vars='{"loop": true}'></vimeo-embed>
|
|
295
|
+
|
|
296
|
+
<!-- Vimeo: Custom color and quality -->
|
|
297
|
+
<vimeo-embed
|
|
298
|
+
video-id="76979871"
|
|
299
|
+
player-vars='{"color": "ff0000", "quality": "720p"}'
|
|
300
|
+
></vimeo-embed>
|
|
301
|
+
|
|
302
|
+
<!-- Vimeo: Privacy and customization -->
|
|
303
|
+
<vimeo-embed
|
|
304
|
+
video-id="76979871"
|
|
305
|
+
player-vars='{"dnt": true, "title": false, "byline": false, "portrait": false}'
|
|
306
|
+
></vimeo-embed>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
// Set via JavaScript
|
|
311
|
+
const embed = document.querySelector("vimeo-embed");
|
|
312
|
+
embed.playerVars = {
|
|
313
|
+
loop: true, // Enable looping
|
|
314
|
+
color: "00adef", // Custom player color (hex without #)
|
|
315
|
+
title: false, // Hide video title
|
|
316
|
+
byline: false, // Hide author
|
|
317
|
+
portrait: false, // Hide author portrait
|
|
318
|
+
dnt: true, // Do Not Track
|
|
319
|
+
};
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
See [Vimeo Embed Options](https://developer.vimeo.com/player/sdk/embed) for all available options.
|
|
323
|
+
|
|
324
|
+
### Events
|
|
325
|
+
|
|
326
|
+
Both components support the same event interface:
|
|
327
|
+
|
|
328
|
+
```javascript
|
|
329
|
+
// Listen to player events
|
|
330
|
+
embed.addEventListener("ready", () => console.log("Player ready"));
|
|
331
|
+
embed.addEventListener("play", () => console.log("Playing"));
|
|
332
|
+
embed.addEventListener("pause", () => console.log("Paused"));
|
|
333
|
+
embed.addEventListener("ended", () => console.log("Video ended"));
|
|
334
|
+
embed.addEventListener("error", (e) => console.error("Error:", e.detail));
|
|
335
|
+
embed.addEventListener("qualitychange", (e) => {
|
|
336
|
+
console.log(
|
|
337
|
+
"Quality changed:",
|
|
338
|
+
e.detail.oldQuality,
|
|
339
|
+
"→",
|
|
340
|
+
e.detail.newQuality
|
|
341
|
+
);
|
|
342
|
+
console.log("Available qualities:", e.detail.availableQualities);
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Video Quality
|
|
347
|
+
|
|
348
|
+
Both components support programmatic quality control and emit events when quality changes.
|
|
349
|
+
|
|
350
|
+
#### YouTube Quality Levels
|
|
351
|
+
|
|
352
|
+
| Quality Level | Resolution | Description |
|
|
353
|
+
| ------------- | ---------- | ------------------- |
|
|
354
|
+
| `tiny` | 144p | Lowest quality |
|
|
355
|
+
| `small` | 240p | Low quality |
|
|
356
|
+
| `medium` | 360p | Medium quality |
|
|
357
|
+
| `large` | 480p | Standard quality |
|
|
358
|
+
| `hd720` | 720p | HD quality |
|
|
359
|
+
| `hd1080` | 1080p | Full HD |
|
|
360
|
+
| `hd1440` | 1440p | 2K quality |
|
|
361
|
+
| `hd2160` | 2160p | 4K quality |
|
|
362
|
+
| `highres` | >1080p | Highest available |
|
|
363
|
+
| `auto` | Auto | Automatic selection |
|
|
364
|
+
|
|
365
|
+
#### Vimeo Quality Levels
|
|
366
|
+
|
|
367
|
+
| Quality Level | Resolution | Description |
|
|
368
|
+
| ------------- | ---------- | ------------------- |
|
|
369
|
+
| `240p` | 240p | Low quality |
|
|
370
|
+
| `360p` | 360p | Medium quality |
|
|
371
|
+
| `540p` | 540p | Standard quality |
|
|
372
|
+
| `720p` | 720p | HD quality |
|
|
373
|
+
| `1080p` | 1080p | Full HD |
|
|
374
|
+
| `2k` | 1440p | 2K quality |
|
|
375
|
+
| `4k` | 2160p | 4K quality |
|
|
376
|
+
| `auto` | Auto | Automatic selection |
|
|
377
|
+
|
|
378
|
+
#### Usage Examples
|
|
379
|
+
|
|
380
|
+
```html
|
|
381
|
+
<!-- Set initial quality via attribute -->
|
|
382
|
+
<youtube-embed video-id="dQw4w9WgXcQ" quality="hd1080"></youtube-embed>
|
|
383
|
+
<vimeo-embed video-id="76979871" quality="720p"></vimeo-embed>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
```javascript
|
|
387
|
+
// YouTube quality control
|
|
388
|
+
const ytEmbed = document.querySelector("youtube-embed");
|
|
389
|
+
const qualities = ytEmbed.getAvailableQualities(); // ["auto", "hd1080", "hd720", "large", "medium"]
|
|
390
|
+
const current = ytEmbed.getCurrentQuality(); // "hd720"
|
|
391
|
+
ytEmbed.setQuality("hd1080"); // Switch to 1080p
|
|
392
|
+
|
|
393
|
+
// Vimeo quality control (async)
|
|
394
|
+
const vimeoEmbed = document.querySelector("vimeo-embed");
|
|
395
|
+
const qualities = await vimeoEmbed.getAvailableQualities(); // ["auto", "1080p", "720p", "540p"]
|
|
396
|
+
const current = await vimeoEmbed.getCurrentQuality(); // "720p"
|
|
397
|
+
await vimeoEmbed.setQuality("1080p"); // Switch to 1080p
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
#### Quality Change Event
|
|
401
|
+
|
|
402
|
+
```javascript
|
|
403
|
+
embed.addEventListener("qualitychange", (event) => {
|
|
404
|
+
const { oldQuality, newQuality, availableQualities } = event.detail;
|
|
405
|
+
console.log(`Quality changed from ${oldQuality} to ${newQuality}`);
|
|
406
|
+
console.log(`Available: ${availableQualities.join(", ")}`);
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Error Handling
|
|
411
|
+
|
|
412
|
+
Components include comprehensive error handling with automatic retry and user-friendly feedback:
|
|
413
|
+
|
|
414
|
+
#### Features
|
|
415
|
+
|
|
416
|
+
- **Automatic Retry**: API loads retry up to 3 times with 2-second delays
|
|
417
|
+
- **Timeout Protection**: 10-second timeout prevents indefinite waiting
|
|
418
|
+
- **Visual Feedback**: Error UI with descriptive messages and retry button
|
|
419
|
+
- **Event Notifications**: Detailed error events with codes and retry status
|
|
420
|
+
|
|
421
|
+
#### Error Event Details
|
|
422
|
+
|
|
423
|
+
```javascript
|
|
424
|
+
embed.addEventListener("error", (e) => {
|
|
425
|
+
console.log(e.detail.message); // Human-readable error message
|
|
426
|
+
console.log(e.detail.code); // Error code (e.g., "API_LOAD_ERROR")
|
|
427
|
+
console.log(e.detail.retryable); // Whether error is retryable
|
|
428
|
+
});
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
#### Common Error Scenarios
|
|
432
|
+
|
|
433
|
+
| Error Type | Cause | Solution |
|
|
434
|
+
| -------------------- | -------------------------- | ------------------------------------ |
|
|
435
|
+
| API Load Timeout | Ad blocker or slow network | Disable ad blocker, check connection |
|
|
436
|
+
| Script Load Failed | Network issue or CORS | Verify internet connection |
|
|
437
|
+
| Video Not Found | Invalid video ID | Check video ID/URL |
|
|
438
|
+
| Embedding Restricted | Video owner restrictions | Use different video |
|
|
439
|
+
|
|
440
|
+
### Accessibility
|
|
441
|
+
|
|
442
|
+
Both components are fully accessible with keyboard navigation and screen reader support:
|
|
443
|
+
|
|
444
|
+
#### Keyboard Shortcuts
|
|
445
|
+
|
|
446
|
+
| Key | Action |
|
|
447
|
+
| ----------------- | -------------------------- |
|
|
448
|
+
| `Tab` | Focus player |
|
|
449
|
+
| `Enter` / `Space` | Activate lazy-loaded video |
|
|
450
|
+
| `k` / `Space` | Play/Pause video |
|
|
451
|
+
| `m` | Mute/Unmute |
|
|
452
|
+
| `f` | Toggle fullscreen |
|
|
453
|
+
|
|
454
|
+
#### ARIA Support
|
|
455
|
+
|
|
456
|
+
- **ARIA Roles**: Components use `role="region"` with `aria-label="Video player"`
|
|
457
|
+
- **Live Regions**: Screen reader announcements for state changes (playing, paused, muted, etc.)
|
|
458
|
+
- **Button Labels**: Play buttons have descriptive `aria-label` attributes
|
|
459
|
+
- **Focus Management**: Proper `tabindex` and focus indicators
|
|
460
|
+
- **Keyboard Accessible**: All interactive elements support keyboard navigation
|
|
461
|
+
|
|
462
|
+
```html
|
|
463
|
+
<!-- Example: Accessible video embed -->
|
|
464
|
+
<youtube-embed
|
|
465
|
+
url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
|
466
|
+
controls
|
|
467
|
+
aria-label="Product demo video"
|
|
468
|
+
tabindex="0"
|
|
469
|
+
>
|
|
470
|
+
</youtube-embed>
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Properties
|
|
474
|
+
|
|
475
|
+
All attributes are accessible as JavaScript properties:
|
|
476
|
+
|
|
477
|
+
```javascript
|
|
478
|
+
embed.autoplay = true; // Enable autoplay
|
|
479
|
+
embed.muted = false; // Unmute video
|
|
480
|
+
embed.url = "https://..."; // Change video URL
|
|
481
|
+
console.log(embed.playing); // Get current playing state
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
## 🎯 Examples
|
|
485
|
+
|
|
486
|
+
Comprehensive examples are available in the `examples/` directory:
|
|
487
|
+
|
|
488
|
+
- **`examples/index.html`** - Overview and navigation to all demos
|
|
489
|
+
- **YouTube Demos:**
|
|
490
|
+
- `youtube-basic-demo.html` - Basic usage patterns and modal integration
|
|
491
|
+
- `youtube-lazy-loading-demo.html` - Performance optimization techniques
|
|
492
|
+
- `youtube-background-demo.html` - Background video integration
|
|
493
|
+
- **Vimeo Demos:**
|
|
494
|
+
- `vimeo-basic-demo.html` - Comprehensive Vimeo features
|
|
495
|
+
- `vimeo-lazy-loading-demo.html` - Performance-focused lazy loading
|
|
496
|
+
- `vimeo-background-demo.html` - Full-screen background videos
|
|
497
|
+
- `vimeo-demo.html` - Original Vimeo integration demo
|
|
498
|
+
- **`platform-comparison-demo.html`** - Side-by-side platform comparison
|
|
499
|
+
|
|
500
|
+
### Running Examples
|
|
501
|
+
|
|
502
|
+
1. Build the component: `npm run build`
|
|
503
|
+
2. Open `examples/index.html` in your browser
|
|
504
|
+
3. Explore different usage patterns and features
|
|
505
|
+
|
|
506
|
+
## 🧪 Testing
|
|
507
|
+
|
|
508
|
+
This component includes a comprehensive automated test suite to ensure reliability, performance, and correct functionality.
|
|
509
|
+
|
|
510
|
+
### Running Tests
|
|
511
|
+
|
|
512
|
+
1. **Quick Access**: Visit `test/index.html` in your browser
|
|
513
|
+
2. **From Examples**: Navigate to the test suite from `examples/index.html`
|
|
514
|
+
3. **Direct URL**: `http://localhost:8000/test/` (when serving locally)
|
|
515
|
+
|
|
516
|
+
### Test Categories
|
|
517
|
+
|
|
518
|
+
#### 🔧 **Unit Tests** (20+ tests)
|
|
519
|
+
|
|
520
|
+
- Component creation and initialization
|
|
521
|
+
- Attribute parsing and validation
|
|
522
|
+
- Video ID extraction from various URL formats
|
|
523
|
+
- Method availability and error handling
|
|
524
|
+
- DOM structure verification
|
|
525
|
+
|
|
526
|
+
#### ⏳ **Lazy Loading Tests** (15+ tests)
|
|
527
|
+
|
|
528
|
+
- Poster image display and interaction
|
|
529
|
+
- Deferred iframe creation
|
|
530
|
+
- Click-to-load functionality
|
|
531
|
+
- Performance benefit verification
|
|
532
|
+
- Resource optimization testing
|
|
533
|
+
|
|
534
|
+
#### ▶️ **Player State Tests** (12+ tests)
|
|
535
|
+
|
|
536
|
+
- Play/pause/stop operations
|
|
537
|
+
- State transitions and reporting
|
|
538
|
+
- Mute/unmute functionality
|
|
539
|
+
- Custom vs native controls
|
|
540
|
+
- Error recovery mechanisms
|
|
541
|
+
|
|
542
|
+
#### 🔗 **Integration Tests** (10+ tests)
|
|
543
|
+
|
|
544
|
+
- YouTube/Vimeo iframe API loading
|
|
545
|
+
- Real player initialization and events
|
|
546
|
+
- Cross-origin and security features
|
|
547
|
+
- Browser compatibility verification
|
|
548
|
+
- Multiple instance handling
|
|
549
|
+
|
|
550
|
+
#### ⚡ **Performance Tests** (10+ tests)
|
|
551
|
+
|
|
552
|
+
- Initialization speed benchmarks (< 200ms target)
|
|
553
|
+
- Memory leak detection
|
|
554
|
+
- Resource optimization verification
|
|
555
|
+
- Scalability testing (20+ instances)
|
|
556
|
+
- Bundle size impact assessment
|
|
557
|
+
|
|
558
|
+
### Test Framework Features
|
|
559
|
+
|
|
560
|
+
- **Visual Progress Tracking** - Real-time progress bars and statistics
|
|
561
|
+
- **Console Capture** - All logs displayed in beautiful interface
|
|
562
|
+
- **Test Filtering** - Run specific suites or filter by pass/fail
|
|
563
|
+
- **Results Export** - Download detailed JSON reports
|
|
564
|
+
- **Performance Monitoring** - Automatic timing and memory tracking
|
|
565
|
+
|
|
566
|
+
### Example Test Usage
|
|
567
|
+
|
|
568
|
+
```javascript
|
|
569
|
+
// Run all tests
|
|
570
|
+
runAllTests();
|
|
571
|
+
|
|
572
|
+
// Run specific test suites
|
|
573
|
+
runTestSuite("unit"); // Unit tests only
|
|
574
|
+
runTestSuite("lazy"); // Lazy loading tests
|
|
575
|
+
runTestSuite("integration"); // Integration tests
|
|
576
|
+
runTestSuite("performance"); // Performance tests
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### Test Results Interpretation
|
|
580
|
+
|
|
581
|
+
- **90%+ Success Rate** - Excellent, production ready
|
|
582
|
+
- **70-89% Success Rate** - Good, minor issues may exist
|
|
583
|
+
- **< 70% Success Rate** - Issues need investigation
|
|
584
|
+
|
|
585
|
+
Common test failures in browser environments:
|
|
586
|
+
|
|
587
|
+
- YouTube/Vimeo API loading delays
|
|
588
|
+
- Network connectivity issues
|
|
589
|
+
- Browser security restrictions
|
|
590
|
+
- Timing-sensitive operations
|
|
591
|
+
|
|
592
|
+
## ⚡ Performance Benefits
|
|
593
|
+
|
|
594
|
+
### Lazy Loading
|
|
595
|
+
|
|
596
|
+
- **Faster initial page loads** - Videos load on-demand
|
|
597
|
+
- **Reduced bandwidth** - Only loads when user wants to watch
|
|
598
|
+
- **Better Core Web Vitals** - Improved LCP scores
|
|
599
|
+
- **Mobile optimization** - Data usage conscious
|
|
600
|
+
|
|
601
|
+
### Event-Driven Architecture
|
|
602
|
+
|
|
603
|
+
- **Efficient updates** - React to state changes without polling
|
|
604
|
+
- **Better UX** - Responsive to user interactions
|
|
605
|
+
- **Easy integration** - Standard event listeners
|
|
606
|
+
|
|
607
|
+
## 🛠️ Development
|
|
608
|
+
|
|
609
|
+
### Build Process
|
|
610
|
+
|
|
611
|
+
#### Available Build Scripts
|
|
612
|
+
|
|
613
|
+
| Script | Purpose | Usage |
|
|
614
|
+
| ------------------------ | -------------------------------- | ------------------------------- |
|
|
615
|
+
| `npm run build` | Full clean build with validation | Always run before testing |
|
|
616
|
+
| `npm run build:clean` | Clean dist directory | Removes old build artifacts |
|
|
617
|
+
| `npm run build:ts` | Compile TypeScript only | Quick TypeScript updates |
|
|
618
|
+
| `npm run build:css` | Compile SCSS only | Quick style updates |
|
|
619
|
+
| `npm run build:validate` | Verify build integrity | Checks all required files |
|
|
620
|
+
| `npm run test:build` | Build + ready message | One-command test preparation |
|
|
621
|
+
| `npm run test:validate` | Build + comprehensive check | Full environment validation |
|
|
622
|
+
| `npm run watch` | Watch mode (TypeScript + SCSS) | Auto-rebuild during development |
|
|
623
|
+
| `npm run examples` | Build + examples ready | Prepare examples for viewing |
|
|
624
|
+
|
|
625
|
+
#### Quick Start
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
# Initial setup (one time)
|
|
629
|
+
npm install
|
|
630
|
+
|
|
631
|
+
# Development workflow (recommended)
|
|
632
|
+
npm run watch # Auto-rebuild on changes
|
|
633
|
+
|
|
634
|
+
# OR manual builds
|
|
635
|
+
npm run build # Full clean build
|
|
636
|
+
npm run test:validate # Build + validate everything
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
#### Build Validation
|
|
640
|
+
|
|
641
|
+
The build process includes automatic validation that checks:
|
|
642
|
+
|
|
643
|
+
- ✅ **TypeScript compilation** - `dist/index.js` and component files
|
|
644
|
+
- ✅ **SCSS compilation** - `dist/css/main.css` with proper styling
|
|
645
|
+
- ✅ **Test file integrity** - All test files present and accessible
|
|
646
|
+
- ✅ **Documentation** - README and guides are available
|
|
647
|
+
|
|
648
|
+
### Testing Workflow
|
|
649
|
+
|
|
650
|
+
#### Quick Testing Guide
|
|
651
|
+
|
|
652
|
+
1. **Build the component** (required):
|
|
653
|
+
|
|
654
|
+
```bash
|
|
655
|
+
npm run build
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
2. **Start watch mode** (recommended for development):
|
|
659
|
+
|
|
660
|
+
```bash
|
|
661
|
+
npm run watch
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
3. **Open the test suite**:
|
|
665
|
+
- Navigate to `test/index.html` in your browser
|
|
666
|
+
- Or serve via local server: `python -m http.server 8000`
|
|
667
|
+
- Visit: `http://localhost:8000/test/`
|
|
668
|
+
|
|
669
|
+
#### Development Workflow with Tests
|
|
670
|
+
|
|
671
|
+
When using `npm run watch`, the system will:
|
|
672
|
+
|
|
673
|
+
- ✅ Automatically compile TypeScript changes to JavaScript
|
|
674
|
+
- ✅ Automatically compile SCSS changes to CSS
|
|
675
|
+
- ✅ Keep compiled files up to date
|
|
676
|
+
|
|
677
|
+
**Workflow:**
|
|
678
|
+
|
|
679
|
+
1. Start watch mode: `npm run watch`
|
|
680
|
+
2. Make changes to TypeScript or SCSS files
|
|
681
|
+
3. Refresh the test page to see updates
|
|
682
|
+
4. Tests automatically use latest compiled code
|
|
683
|
+
|
|
684
|
+
#### Test Features
|
|
685
|
+
|
|
686
|
+
- ✅ **67+ comprehensive tests** across 5 categories (YouTube) + 5 categories (Vimeo)
|
|
687
|
+
- ✅ **Automatic component detection** and loading
|
|
688
|
+
- ✅ **Real-time console output** with colored messages
|
|
689
|
+
- ✅ **Progress tracking** and detailed statistics
|
|
690
|
+
- ✅ **JSON export** for results documentation
|
|
691
|
+
- ✅ **Individual test suite** execution
|
|
692
|
+
- ✅ **Component demo** integration
|
|
693
|
+
|
|
694
|
+
#### Troubleshooting Tests
|
|
695
|
+
|
|
696
|
+
**Component Not Loading:**
|
|
697
|
+
|
|
698
|
+
- Error: "Component not registered"
|
|
699
|
+
- Solution: Run `npm run build` or `npm run watch` first
|
|
700
|
+
|
|
701
|
+
**CORS Issues:**
|
|
702
|
+
|
|
703
|
+
- Error: Module loading fails
|
|
704
|
+
- Solution: Serve files via HTTP server instead of file:// protocol
|
|
705
|
+
|
|
706
|
+
**Outdated Tests:**
|
|
707
|
+
|
|
708
|
+
- Error: Tests show old behavior
|
|
709
|
+
- Solution: Ensure watch mode is running or rebuild manually
|
|
710
|
+
|
|
711
|
+
For detailed testing documentation, see `test/README.md`.
|
|
712
|
+
|
|
713
|
+
## 🌐 Browser Support
|
|
714
|
+
|
|
715
|
+
- Modern browsers with Web Components support
|
|
716
|
+
- ES6+ JavaScript features
|
|
717
|
+
- YouTube iframe API integration
|
|
718
|
+
- Vimeo Player API integration
|
|
719
|
+
|
|
720
|
+
## 📝 Migration Guide
|
|
721
|
+
|
|
722
|
+
### From Standard iframes to Web Components
|
|
723
|
+
|
|
724
|
+
Migrating from traditional iframe embeds is straightforward:
|
|
725
|
+
|
|
726
|
+
#### Before (Standard iframe):
|
|
727
|
+
|
|
728
|
+
```html
|
|
729
|
+
<iframe
|
|
730
|
+
width="560"
|
|
731
|
+
height="315"
|
|
732
|
+
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
|
|
733
|
+
frameborder="0"
|
|
734
|
+
allow="autoplay; encrypted-media"
|
|
735
|
+
allowfullscreen
|
|
736
|
+
></iframe>
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
#### After (Web Component):
|
|
740
|
+
|
|
741
|
+
```html
|
|
742
|
+
<youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
**Benefits of Migration:**
|
|
746
|
+
|
|
747
|
+
- ✅ Lazy loading support (faster page loads)
|
|
748
|
+
- ✅ Programmatic control (play, pause, mute, etc.)
|
|
749
|
+
- ✅ Event handling (ready, play, pause, ended, error)
|
|
750
|
+
- ✅ Quality control (set preferred video quality)
|
|
751
|
+
- ✅ Fullscreen API (enter/exit fullscreen programmatically)
|
|
752
|
+
- ✅ Accessibility features (keyboard shortcuts, ARIA support)
|
|
753
|
+
- ✅ Error handling with retry mechanism
|
|
754
|
+
- ✅ Responsive by default
|
|
755
|
+
|
|
756
|
+
### From Previous Versions
|
|
757
|
+
|
|
758
|
+
- `pause()` now pauses instead of stopping (use `stopVideo()` to stop)
|
|
759
|
+
- Boolean attributes work correctly (presence = true, absence = false)
|
|
760
|
+
- Event-based architecture replaces callbacks
|
|
761
|
+
- `video-id` attribute supported alongside `url`
|
|
762
|
+
|
|
763
|
+
### Event Migration
|
|
764
|
+
|
|
765
|
+
```javascript
|
|
766
|
+
// Old approach
|
|
767
|
+
embed.onReady = () => {
|
|
768
|
+
/* ... */
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
// New approach
|
|
772
|
+
embed.addEventListener("ready", () => {
|
|
773
|
+
/* ... */
|
|
774
|
+
});
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
## 🔧 Troubleshooting
|
|
778
|
+
|
|
779
|
+
### Common Issues and Solutions
|
|
780
|
+
|
|
781
|
+
#### Video Not Loading
|
|
782
|
+
|
|
783
|
+
**Problem**: Video doesn't load or shows error message
|
|
784
|
+
|
|
785
|
+
**Solutions**:
|
|
786
|
+
|
|
787
|
+
1. **Check Video ID**: Ensure the video ID is correct and publicly accessible
|
|
788
|
+
2. **Disable Ad Blockers**: Some ad blockers prevent video APIs from loading
|
|
789
|
+
3. **Check Network**: Verify internet connection and firewall settings
|
|
790
|
+
4. **CORS Issues**: Ensure your domain is allowed (usually not an issue with official APIs)
|
|
791
|
+
|
|
792
|
+
```javascript
|
|
793
|
+
// Listen for error events
|
|
794
|
+
embed.addEventListener("error", (e) => {
|
|
795
|
+
console.log("Error details:", e.detail);
|
|
796
|
+
// e.detail.message - Human-readable error
|
|
797
|
+
// e.detail.code - Error code (e.g., "API_LOAD_ERROR")
|
|
798
|
+
// e.detail.retryable - Whether automatic retry will occur
|
|
799
|
+
});
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
#### Autoplay Not Working
|
|
803
|
+
|
|
804
|
+
**Problem**: Video doesn't autoplay even with `autoplay` attribute
|
|
805
|
+
|
|
806
|
+
**Solutions**:
|
|
807
|
+
|
|
808
|
+
1. **Add `muted` attribute**: Most browsers require videos to be muted for autoplay
|
|
809
|
+
2. **User Interaction**: Some browsers require user interaction before autoplay
|
|
810
|
+
|
|
811
|
+
```html
|
|
812
|
+
<!-- Correct autoplay setup -->
|
|
813
|
+
<youtube-embed video-id="VIDEO_ID" autoplay muted></youtube-embed>
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
#### Lazy Loading Not Triggering
|
|
817
|
+
|
|
818
|
+
**Problem**: Video doesn't load when scrolled into view
|
|
819
|
+
|
|
820
|
+
**Solutions**:
|
|
821
|
+
|
|
822
|
+
1. **Check `lazy` attribute**: Ensure the `lazy` attribute is present
|
|
823
|
+
2. **Viewport Visibility**: Element must be within viewport threshold (50px)
|
|
824
|
+
3. **Browser Support**: Ensure browser supports Intersection Observer API
|
|
825
|
+
|
|
826
|
+
```javascript
|
|
827
|
+
// Debug lazy loading
|
|
828
|
+
const embed = document.querySelector("youtube-embed");
|
|
829
|
+
console.log("Lazy enabled:", embed.hasAttribute("lazy"));
|
|
830
|
+
console.log(
|
|
831
|
+
"In viewport:",
|
|
832
|
+
embed.getBoundingClientRect().top < window.innerHeight
|
|
833
|
+
);
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
#### Keyboard Shortcuts Not Working
|
|
837
|
+
|
|
838
|
+
**Problem**: Pressing 'k', 'm', or 'f' doesn't control the video
|
|
839
|
+
|
|
840
|
+
**Solutions**:
|
|
841
|
+
|
|
842
|
+
1. **Focus Element**: Click on the video component to focus it first
|
|
843
|
+
2. **Check Conflicts**: Ensure no other scripts are intercepting keyboard events
|
|
844
|
+
3. **Browser Compatibility**: Some browsers may restrict keyboard events
|
|
845
|
+
|
|
846
|
+
**Available Shortcuts**:
|
|
847
|
+
|
|
848
|
+
- `k` - Play/Pause toggle
|
|
849
|
+
- `m` - Mute/Unmute toggle
|
|
850
|
+
- `f` - Fullscreen toggle
|
|
851
|
+
|
|
852
|
+
#### Quality Selection Issues
|
|
853
|
+
|
|
854
|
+
**Problem**: Cannot change video quality or quality selector shows no options
|
|
855
|
+
|
|
856
|
+
**Solutions**:
|
|
857
|
+
|
|
858
|
+
1. **Wait for Ready Event**: Quality info only available after player is ready
|
|
859
|
+
2. **Video Support**: Not all videos have multiple quality levels
|
|
860
|
+
3. **API Limitations**: Platform APIs may not expose all quality levels immediately
|
|
861
|
+
|
|
862
|
+
```javascript
|
|
863
|
+
embed.addEventListener("ready", async () => {
|
|
864
|
+
// For YouTube
|
|
865
|
+
const qualities = embed.getAvailableQualities();
|
|
866
|
+
console.log("Available:", qualities);
|
|
867
|
+
|
|
868
|
+
// For Vimeo (async)
|
|
869
|
+
const qualities = await embed.getAvailableQualities();
|
|
870
|
+
console.log("Available:", qualities);
|
|
871
|
+
});
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
## 🌐 Browser Compatibility
|
|
875
|
+
|
|
876
|
+
### Minimum Requirements
|
|
877
|
+
|
|
878
|
+
| Feature | Chrome | Firefox | Safari | Edge |
|
|
879
|
+
| ------------------------- | ------ | ------- | ------ | ---- |
|
|
880
|
+
| **Web Components** | 67+ | 63+ | 10.1+ | 79+ |
|
|
881
|
+
| **Custom Elements** | 67+ | 63+ | 10.1+ | 79+ |
|
|
882
|
+
| **Shadow DOM** | 53+ | 63+ | 10.1+ | 79+ |
|
|
883
|
+
| **ES6 Modules** | 61+ | 60+ | 10.1+ | 79+ |
|
|
884
|
+
| **Intersection Observer** | 58+ | 55+ | 12.1+ | 79+ |
|
|
885
|
+
| **Fullscreen API** | 71+ | 64+ | 5.1+ | 79+ |
|
|
886
|
+
|
|
887
|
+
### Feature Support
|
|
888
|
+
|
|
889
|
+
| Feature | Support | Polyfill Available | Notes |
|
|
890
|
+
| ------------------ | ------------------ | ------------------ | ------------------------------------- |
|
|
891
|
+
| Lazy Loading | Modern Browsers | ✅ | Falls back to immediate load |
|
|
892
|
+
| Keyboard Shortcuts | All Browsers | N/A | Browser-dependent focus behavior |
|
|
893
|
+
| Fullscreen API | Modern Browsers | ❌ | Vendor prefixes handled automatically |
|
|
894
|
+
| Quality Control | Platform-dependent | N/A | Depends on YouTube/Vimeo API support |
|
|
895
|
+
| ARIA Support | All Browsers | N/A | Screen reader compatibility |
|
|
896
|
+
|
|
897
|
+
### Polyfills
|
|
898
|
+
|
|
899
|
+
For older browser support, include these polyfills:
|
|
900
|
+
|
|
901
|
+
```html
|
|
902
|
+
<!-- Web Components Polyfills -->
|
|
903
|
+
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@2.8.0/webcomponents-loader.js"></script>
|
|
904
|
+
|
|
905
|
+
<!-- Intersection Observer Polyfill -->
|
|
906
|
+
<script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
## 💡 Advanced Usage Examples
|
|
910
|
+
|
|
911
|
+
### Playlist Implementation
|
|
912
|
+
|
|
913
|
+
```html
|
|
914
|
+
<div id="playlist-container"></div>
|
|
915
|
+
|
|
916
|
+
<script type="module">
|
|
917
|
+
const videos = ["dQw4w9WgXcQ", "ScMzIvxBSi4", "M7lc1UVf-VE"];
|
|
918
|
+
|
|
919
|
+
let currentIndex = 0;
|
|
920
|
+
const container = document.getElementById("playlist-container");
|
|
921
|
+
|
|
922
|
+
function loadVideo(index) {
|
|
923
|
+
container.innerHTML = "";
|
|
924
|
+
const embed = document.createElement("youtube-embed");
|
|
925
|
+
embed.setAttribute("video-id", videos[index]);
|
|
926
|
+
embed.setAttribute("autoplay", "");
|
|
927
|
+
|
|
928
|
+
embed.addEventListener("ended", () => {
|
|
929
|
+
currentIndex = (currentIndex + 1) % videos.length;
|
|
930
|
+
loadVideo(currentIndex);
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
container.appendChild(embed);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
loadVideo(0);
|
|
937
|
+
</script>
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
### Custom Styling
|
|
941
|
+
|
|
942
|
+
```html
|
|
943
|
+
<style>
|
|
944
|
+
youtube-embed {
|
|
945
|
+
--embed-border-radius: 16px;
|
|
946
|
+
--embed-max-width: 800px;
|
|
947
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
youtube-embed::part(container) {
|
|
951
|
+
border-radius: var(--embed-border-radius);
|
|
952
|
+
}
|
|
953
|
+
</style>
|
|
954
|
+
|
|
955
|
+
<youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
### Dynamic Quality Selection
|
|
959
|
+
|
|
960
|
+
```html
|
|
961
|
+
<youtube-embed id="player" video-id="dQw4w9WgXcQ"></youtube-embed>
|
|
962
|
+
|
|
963
|
+
<select id="quality-selector">
|
|
964
|
+
<option value="auto">Auto</option>
|
|
965
|
+
</select>
|
|
966
|
+
|
|
967
|
+
<script type="module">
|
|
968
|
+
const player = document.getElementById("player");
|
|
969
|
+
const selector = document.getElementById("quality-selector");
|
|
970
|
+
|
|
971
|
+
player.addEventListener("ready", () => {
|
|
972
|
+
const qualities = player.getAvailableQualities();
|
|
973
|
+
|
|
974
|
+
// Populate selector
|
|
975
|
+
qualities.forEach((quality) => {
|
|
976
|
+
if (quality !== "auto") {
|
|
977
|
+
const option = document.createElement("option");
|
|
978
|
+
option.value = quality;
|
|
979
|
+
option.textContent = quality.toUpperCase();
|
|
980
|
+
selector.appendChild(option);
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
selector.addEventListener("change", (e) => {
|
|
986
|
+
player.setQuality(e.target.value);
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
player.addEventListener("qualitychange", (e) => {
|
|
990
|
+
console.log(
|
|
991
|
+
`Quality changed: ${e.detail.oldQuality} → ${e.detail.newQuality}`
|
|
992
|
+
);
|
|
993
|
+
});
|
|
994
|
+
</script>
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
### Event Logger
|
|
998
|
+
|
|
999
|
+
```html
|
|
1000
|
+
<youtube-embed id="player" video-id="dQw4w9WgXcQ"></youtube-embed>
|
|
1001
|
+
<div id="event-log"></div>
|
|
1002
|
+
|
|
1003
|
+
<script type="module">
|
|
1004
|
+
const player = document.getElementById("player");
|
|
1005
|
+
const log = document.getElementById("event-log");
|
|
1006
|
+
|
|
1007
|
+
const events = [
|
|
1008
|
+
"ready",
|
|
1009
|
+
"play",
|
|
1010
|
+
"pause",
|
|
1011
|
+
"ended",
|
|
1012
|
+
"error",
|
|
1013
|
+
"timeupdate",
|
|
1014
|
+
"qualitychange",
|
|
1015
|
+
];
|
|
1016
|
+
|
|
1017
|
+
events.forEach((eventName) => {
|
|
1018
|
+
player.addEventListener(eventName, (e) => {
|
|
1019
|
+
const entry = document.createElement("div");
|
|
1020
|
+
entry.textContent = `${new Date().toLocaleTimeString()} - ${eventName}: ${JSON.stringify(
|
|
1021
|
+
e.detail || {}
|
|
1022
|
+
)}`;
|
|
1023
|
+
log.prepend(entry);
|
|
1024
|
+
});
|
|
1025
|
+
});
|
|
1026
|
+
</script>
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
### Responsive Aspect Ratio
|
|
1030
|
+
|
|
1031
|
+
```html
|
|
1032
|
+
<style>
|
|
1033
|
+
.video-wrapper {
|
|
1034
|
+
position: relative;
|
|
1035
|
+
width: 100%;
|
|
1036
|
+
padding-bottom: 56.25%; /* 16:9 aspect ratio */
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
.video-wrapper youtube-embed {
|
|
1040
|
+
position: absolute;
|
|
1041
|
+
top: 0;
|
|
1042
|
+
left: 0;
|
|
1043
|
+
width: 100%;
|
|
1044
|
+
height: 100%;
|
|
1045
|
+
}
|
|
1046
|
+
</style>
|
|
1047
|
+
|
|
1048
|
+
<div class="video-wrapper">
|
|
1049
|
+
<youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>
|
|
1050
|
+
</div>
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
## 📦 Build & Distribution
|
|
1054
|
+
|
|
1055
|
+
### Development Build
|
|
1056
|
+
|
|
1057
|
+
```bash
|
|
1058
|
+
# Standard build (unminified for debugging)
|
|
1059
|
+
npm run build
|
|
1060
|
+
|
|
1061
|
+
# Watch mode for development
|
|
1062
|
+
npm run dev
|
|
1063
|
+
|
|
1064
|
+
# Validate build integrity
|
|
1065
|
+
npm run build:validate
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
### Production Build
|
|
1069
|
+
|
|
1070
|
+
```bash
|
|
1071
|
+
# Production build with minification (~50% size reduction)
|
|
1072
|
+
npm run build:prod
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
This creates optimized `.min.js` and `.min.css` files with:
|
|
1076
|
+
|
|
1077
|
+
- **Terser** minification for JavaScript (removes whitespace, shortens variable names)
|
|
1078
|
+
- **CSSO** optimization for CSS (removes redundant rules, merges selectors)
|
|
1079
|
+
- **~50% smaller** file sizes compared to development builds
|
|
1080
|
+
|
|
1081
|
+
### Build Output Structure
|
|
1082
|
+
|
|
1083
|
+
```
|
|
1084
|
+
dist/
|
|
1085
|
+
├── index.js # Full bundle (58KB)
|
|
1086
|
+
├── index.min.js # Full bundle minified (29KB) ⭐ Production
|
|
1087
|
+
├── youtube-only.js # YouTube bundle (29KB)
|
|
1088
|
+
├── youtube-only.min.js # YouTube minified (15KB) ⭐ Production
|
|
1089
|
+
├── vimeo-only.js # Vimeo bundle (29KB)
|
|
1090
|
+
├── vimeo-only.min.js # Vimeo minified (14KB) ⭐ Production
|
|
1091
|
+
├── components/
|
|
1092
|
+
│ ├── YouTubeEmbed.js # YouTube component (58KB)
|
|
1093
|
+
│ ├── YouTubeEmbed.min.js # YouTube minified (29KB)
|
|
1094
|
+
│ ├── VimeoEmbed.js # Vimeo component (49KB)
|
|
1095
|
+
│ └── VimeoEmbed.min.js # Vimeo minified (27KB)
|
|
1096
|
+
├── css/
|
|
1097
|
+
│ ├── components.css # Core styles (9KB)
|
|
1098
|
+
│ ├── components.min.css # Core minified (7KB) ⭐ Production
|
|
1099
|
+
│ ├── main.css # With Bootstrap (276KB)
|
|
1100
|
+
│ └── main.min.css # Bootstrap minified (224KB)
|
|
1101
|
+
└── types/ # TypeScript declarations
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
### Performance Optimization
|
|
1105
|
+
|
|
1106
|
+
**Bundle Size Comparison:**
|
|
1107
|
+
|
|
1108
|
+
| Configuration | Size (Dev) | Size (Prod) | With gzip\* |
|
|
1109
|
+
| -------------------- | ---------- | ----------- | ----------- |
|
|
1110
|
+
| YouTube + Core CSS | 38KB | **22KB** | ~7KB |
|
|
1111
|
+
| Vimeo + Core CSS | 38KB | **21KB** | ~7KB |
|
|
1112
|
+
| Both + Core CSS | 67KB | **36KB** | ~12KB |
|
|
1113
|
+
| Both + Bootstrap CSS | 343KB | **253KB** | ~45KB |
|
|
1114
|
+
|
|
1115
|
+
\*Estimated with gzip compression enabled on server
|
|
1116
|
+
|
|
1117
|
+
**Optimization Tips:**
|
|
1118
|
+
|
|
1119
|
+
1. ✅ **Use minified files** (`.min.js` / `.min.css`) in production - saves ~50%
|
|
1120
|
+
2. ✅ **Choose platform-specific bundles** when possible - saves ~50%
|
|
1121
|
+
3. ✅ **Use core CSS** instead of Bootstrap - saves ~97%
|
|
1122
|
+
4. ✅ **Enable gzip/brotli** on your server - saves additional ~70%
|
|
1123
|
+
5. ✅ **Lazy loading is built-in** - videos only load when visible
|
|
1124
|
+
|
|
1125
|
+
**Best Configuration for Production:**
|
|
1126
|
+
|
|
1127
|
+
```html
|
|
1128
|
+
<!-- YouTube only: 22KB total (7KB gzipped) -->
|
|
1129
|
+
<script type="module" src="dist/youtube-only.min.js"></script>
|
|
1130
|
+
<link rel="stylesheet" href="dist/css/components.min.css" />
|
|
1131
|
+
```
|
|
1132
|
+
|
|
1133
|
+
### Bundle Sizes
|
|
1134
|
+
|
|
1135
|
+
| File | Uncompressed | Gzipped | Description |
|
|
1136
|
+
| ----------------- | ------------ | ------- | -------------------------------- |
|
|
1137
|
+
| `index.js` | 58KB | ~15KB | Full bundle with both components |
|
|
1138
|
+
| `youtube-only.js` | 29KB | ~8KB | YouTube component only |
|
|
1139
|
+
| `vimeo-only.js` | 29KB | ~8KB | Vimeo component only |
|
|
1140
|
+
| `components.css` | 15KB | ~3KB | Core component styles |
|
|
1141
|
+
| `main.css` | 276KB | ~35KB | Full styles with Bootstrap |
|
|
1142
|
+
|
|
1143
|
+
### Publishing to NPM
|
|
1144
|
+
|
|
1145
|
+
```bash
|
|
1146
|
+
# Update version in package.json
|
|
1147
|
+
npm version patch|minor|major
|
|
1148
|
+
|
|
1149
|
+
# Build and publish
|
|
1150
|
+
npm publish
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
### Using in Your Project
|
|
1154
|
+
|
|
1155
|
+
#### ES Modules
|
|
1156
|
+
|
|
1157
|
+
```javascript
|
|
1158
|
+
// Full bundle
|
|
1159
|
+
import "@siit-dev/video-embed";
|
|
1160
|
+
|
|
1161
|
+
// YouTube only
|
|
1162
|
+
import "@siit-dev/video-embed/dist/youtube-only.js";
|
|
1163
|
+
|
|
1164
|
+
// Vimeo only
|
|
1165
|
+
import "@siit-dev/video-embed/dist/vimeo-only.js";
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
#### With Bundlers (Webpack, Vite, etc.)
|
|
1169
|
+
|
|
1170
|
+
```javascript
|
|
1171
|
+
// Install
|
|
1172
|
+
npm install @siit-dev/video-embed
|
|
1173
|
+
|
|
1174
|
+
// Import in your app
|
|
1175
|
+
import '@siit-dev/video-embed';
|
|
1176
|
+
import '@siit-dev/video-embed/dist/css/components.css';
|
|
1177
|
+
|
|
1178
|
+
// Use in your HTML/JSX
|
|
1179
|
+
<youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
#### TypeScript Support
|
|
1183
|
+
|
|
1184
|
+
```typescript
|
|
1185
|
+
import { YouTubeEmbed } from "@siit-dev/video-embed";
|
|
1186
|
+
|
|
1187
|
+
const embed = document.querySelector("youtube-embed") as YouTubeEmbed;
|
|
1188
|
+
embed.play();
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
## 🤝 Contributing
|
|
1192
|
+
|
|
1193
|
+
1. Fork the repository
|
|
1194
|
+
2. Create a feature branch
|
|
1195
|
+
3. Make your changes
|
|
1196
|
+
4. Test with the examples
|
|
1197
|
+
5. Submit a pull request
|
|
1198
|
+
|
|
1199
|
+
## 📄 License
|
|
1200
|
+
|
|
1201
|
+
This project is open source. See the LICENSE file for details.
|
|
1202
|
+
|
|
1203
|
+
---
|
|
1204
|
+
|
|
1205
|
+
**Built with vanilla JavaScript • Web Components • TypeScript ready**
|