@smartimpact-it/modern-video-embed 2.0.4 → 2.0.6
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 +322 -71
- package/dist/components/BaseVideoEmbed.d.ts +86 -0
- package/dist/components/BaseVideoEmbed.d.ts.map +1 -0
- package/dist/components/BaseVideoEmbed.js +256 -0
- package/dist/components/BaseVideoEmbed.js.map +1 -0
- package/dist/components/VideoEmbed.d.ts +68 -0
- package/dist/components/VideoEmbed.d.ts.map +1 -0
- package/dist/components/VideoEmbed.js +770 -0
- package/dist/components/VideoEmbed.js.map +1 -0
- package/dist/components/VimeoEmbed.d.ts +26 -36
- package/dist/components/VimeoEmbed.d.ts.map +1 -1
- package/dist/components/VimeoEmbed.js +205 -328
- package/dist/components/VimeoEmbed.js.map +1 -1
- package/dist/components/VimeoEmbed.min.js +1 -1
- package/dist/components/YouTubeEmbed.d.ts +108 -42
- package/dist/components/YouTubeEmbed.d.ts.map +1 -1
- package/dist/components/YouTubeEmbed.js +341 -373
- package/dist/components/YouTubeEmbed.js.map +1 -1
- package/dist/components/YouTubeEmbed.min.js +1 -1
- package/dist/css/components.css +235 -44
- package/dist/css/components.css.map +1 -1
- package/dist/css/components.min.css +1 -1
- package/dist/css/main.css +235 -44
- package/dist/css/main.css.map +1 -1
- package/dist/css/main.min.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/video-only.d.ts +7 -0
- package/dist/video-only.d.ts.map +1 -0
- package/dist/video-only.js +8 -0
- package/dist/video-only.js.map +1 -0
- package/dist/vimeo-only.d.ts +2 -2
- package/dist/vimeo-only.d.ts.map +1 -1
- package/dist/vimeo-only.js +2 -2
- package/dist/vimeo-only.js.map +1 -1
- package/dist/vimeo-only.min.js +1 -1
- package/dist/youtube-only.d.ts +2 -2
- package/dist/youtube-only.d.ts.map +1 -1
- package/dist/youtube-only.js +2 -2
- package/dist/youtube-only.js.map +1 -1
- package/dist/youtube-only.min.js +1 -1
- package/package.json +6 -5
- package/src/components/BaseVideoEmbed.ts +303 -0
- package/src/components/VideoEmbed.ts +852 -0
- package/src/components/VideoEmbed.ts.backup +1051 -0
- package/src/components/VimeoEmbed.ts +233 -397
- package/src/components/YouTubeEmbed.ts +359 -430
- package/src/index.ts +1 -0
- package/src/styles/_embed-base.scss +255 -0
- package/src/styles/_shared-functions.scss +56 -0
- package/src/styles/components.scss +4 -3
- package/src/styles/main.scss +7 -5
- package/src/styles/video-embed.scss +37 -0
- package/src/styles/vimeo-embed.scss +8 -248
- package/src/styles/youtube-embed.scss +8 -254
- package/src/types/index.ts +1 -0
- package/src/types/video-embed.d.ts +90 -0
- package/src/video-only.ts +9 -0
- package/src/vimeo-only.ts +2 -2
- package/src/youtube-only.ts +2 -2
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,8BAA8B,CAAC;AACtC,OAAO,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,8BAA8B,CAAC;AACtC,OAAO,4BAA4B,CAAC;AACpC,OAAO,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,OAAO,8BAA8B,CAAC;AACtC,OAAO,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,OAAO,8BAA8B,CAAC;AACtC,OAAO,4BAA4B,CAAC;AACpC,OAAO,4BAA4B,CAAC"}
|
package/dist/index.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./components/YouTubeEmbed.js";import"./components/VimeoEmbed.js";
|
|
1
|
+
import"./components/YouTubeEmbed.js";import"./components/VimeoEmbed.js";import"./components/VideoEmbed.js";
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,UAAU,CAAC;AACzB,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,UAAU,CAAC;AACzB,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-only.d.ts","sourceRoot":"","sources":["../src/video-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAGxD,OAAO,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video-only bundle entry point
|
|
3
|
+
* Use this for smaller bundle size when only MP4 video embeds are needed
|
|
4
|
+
*/
|
|
5
|
+
export { VideoEmbed } from "./components/VideoEmbed.js";
|
|
6
|
+
// Auto-register the custom element
|
|
7
|
+
import "./components/VideoEmbed.js";
|
|
8
|
+
//# sourceMappingURL=video-only.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-only.js","sourceRoot":"","sources":["../src/video-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAExD,mCAAmC;AACnC,OAAO,4BAA4B,CAAC"}
|
package/dist/vimeo-only.d.ts
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Vimeo-only bundle entry point
|
|
3
3
|
* Use this for smaller bundle size when only Vimeo embeds are needed
|
|
4
4
|
*/
|
|
5
|
-
export { VimeoEmbed } from "./components/VimeoEmbed";
|
|
6
|
-
import "./components/VimeoEmbed";
|
|
5
|
+
export { VimeoEmbed } from "./components/VimeoEmbed.js";
|
|
6
|
+
import "./components/VimeoEmbed.js";
|
|
7
7
|
//# sourceMappingURL=vimeo-only.d.ts.map
|
package/dist/vimeo-only.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vimeo-only.d.ts","sourceRoot":"","sources":["../src/vimeo-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"vimeo-only.d.ts","sourceRoot":"","sources":["../src/vimeo-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAGxD,OAAO,4BAA4B,CAAC"}
|
package/dist/vimeo-only.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Vimeo-only bundle entry point
|
|
3
3
|
* Use this for smaller bundle size when only Vimeo embeds are needed
|
|
4
4
|
*/
|
|
5
|
-
export { VimeoEmbed } from "./components/VimeoEmbed";
|
|
5
|
+
export { VimeoEmbed } from "./components/VimeoEmbed.js";
|
|
6
6
|
// Auto-register the custom element
|
|
7
|
-
import "./components/VimeoEmbed";
|
|
7
|
+
import "./components/VimeoEmbed.js";
|
|
8
8
|
//# sourceMappingURL=vimeo-only.js.map
|
package/dist/vimeo-only.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vimeo-only.js","sourceRoot":"","sources":["../src/vimeo-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"vimeo-only.js","sourceRoot":"","sources":["../src/vimeo-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAExD,mCAAmC;AACnC,OAAO,4BAA4B,CAAC"}
|
package/dist/vimeo-only.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{VimeoEmbed}from"./components/VimeoEmbed";import"./components/VimeoEmbed";
|
|
1
|
+
export{VimeoEmbed}from"./components/VimeoEmbed.js";import"./components/VimeoEmbed.js";
|
package/dist/youtube-only.d.ts
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* YouTube-only bundle entry point
|
|
3
3
|
* Use this for smaller bundle size when only YouTube embeds are needed
|
|
4
4
|
*/
|
|
5
|
-
export { YouTubeEmbed } from "./components/YouTubeEmbed";
|
|
6
|
-
import "./components/YouTubeEmbed";
|
|
5
|
+
export { YouTubeEmbed } from "./components/YouTubeEmbed.js";
|
|
6
|
+
import "./components/YouTubeEmbed.js";
|
|
7
7
|
//# sourceMappingURL=youtube-only.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"youtube-only.d.ts","sourceRoot":"","sources":["../src/youtube-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"youtube-only.d.ts","sourceRoot":"","sources":["../src/youtube-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAG5D,OAAO,8BAA8B,CAAC"}
|
package/dist/youtube-only.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* YouTube-only bundle entry point
|
|
3
3
|
* Use this for smaller bundle size when only YouTube embeds are needed
|
|
4
4
|
*/
|
|
5
|
-
export { YouTubeEmbed } from "./components/YouTubeEmbed";
|
|
5
|
+
export { YouTubeEmbed } from "./components/YouTubeEmbed.js";
|
|
6
6
|
// Auto-register the custom element
|
|
7
|
-
import "./components/YouTubeEmbed";
|
|
7
|
+
import "./components/YouTubeEmbed.js";
|
|
8
8
|
//# sourceMappingURL=youtube-only.js.map
|
package/dist/youtube-only.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"youtube-only.js","sourceRoot":"","sources":["../src/youtube-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"youtube-only.js","sourceRoot":"","sources":["../src/youtube-only.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE5D,mCAAmC;AACnC,OAAO,8BAA8B,CAAC"}
|
package/dist/youtube-only.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{YouTubeEmbed}from"./components/YouTubeEmbed";import"./components/YouTubeEmbed";
|
|
1
|
+
export{YouTubeEmbed}from"./components/YouTubeEmbed.js";import"./components/YouTubeEmbed.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartimpact-it/modern-video-embed",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"description": "Modern YouTube and Vimeo embed web components with lazy loading, accessibility, quality control, and comprehensive API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -40,18 +40,19 @@
|
|
|
40
40
|
"npm": ">=6.0.0"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
|
-
"build": "npm run build:clean && npm run build:ts && npm run build:css && npm run build:validate",
|
|
43
|
+
"build": "npm run build:clean && npm run build:ts && npm run build:css && npm run build:validate && npm run build:cache-bust",
|
|
44
44
|
"build:clean": "rimraf dist",
|
|
45
45
|
"build:ts": "tsc",
|
|
46
46
|
"build:css": "npm run build:css:main && npm run build:css:components",
|
|
47
|
-
"build:css:main": "sass src/styles/main.scss:dist/css/main.css",
|
|
48
|
-
"build:css:components": "sass src/styles/components.scss:dist/css/components.css",
|
|
47
|
+
"build:css:main": "sass --silence-deprecation=import --silence-deprecation=global-builtin --silence-deprecation=color-functions src/styles/main.scss:dist/css/main.css",
|
|
48
|
+
"build:css:components": "sass --silence-deprecation=import --silence-deprecation=global-builtin --silence-deprecation=color-functions src/styles/components.scss:dist/css/components.css",
|
|
49
49
|
"build:validate": "node scripts/verify-build.js",
|
|
50
|
+
"build:cache-bust": "node scripts/update-cache-busters.js",
|
|
50
51
|
"build:prod": "npm run build && npm run build:minify",
|
|
51
52
|
"build:minify": "npm run minify:js && npm run minify:css",
|
|
52
53
|
"minify:js": "terser dist/components/YouTubeEmbed.js -o dist/components/YouTubeEmbed.min.js -c -m && terser dist/components/VimeoEmbed.js -o dist/components/VimeoEmbed.min.js -c -m && terser dist/index.js -o dist/index.min.js -c -m && terser dist/youtube-only.js -o dist/youtube-only.min.js -c -m && terser dist/vimeo-only.js -o dist/vimeo-only.min.js -c -m",
|
|
53
54
|
"minify:css": "csso dist/css/main.css -o dist/css/main.min.css && csso dist/css/components.css -o dist/css/components.min.css",
|
|
54
|
-
"watch": "concurrently --names \"TS,CSS\" --prefix-colors \"blue,green\" \"tsc --watch\" \"sass --watch src/styles:dist/css\"",
|
|
55
|
+
"watch": "concurrently --names \"TS,CSS\" --prefix-colors \"blue,green\" \"tsc --watch\" \"sass --watch --silence-deprecation=import --silence-deprecation=global-builtin --silence-deprecation=color-functions src/styles:dist/css\"",
|
|
55
56
|
"start": "npm run watch",
|
|
56
57
|
"examples": "npm run build && echo \"Examples built! Open examples/index.html in your browser to view demos.\"",
|
|
57
58
|
"dev": "npm run build && npm run watch",
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for all video embed components (VideoEmbed, YouTubeEmbed, VimeoEmbed).
|
|
3
|
+
* Contains shared functionality to reduce code duplication and ensure consistency.
|
|
4
|
+
*/
|
|
5
|
+
export abstract class BaseVideoEmbed extends HTMLElement {
|
|
6
|
+
// Static properties shared across all components
|
|
7
|
+
protected static instanceCount = 0;
|
|
8
|
+
protected static DEBUG = false;
|
|
9
|
+
|
|
10
|
+
// Common instance properties
|
|
11
|
+
protected playerReady = false;
|
|
12
|
+
protected initialized = false;
|
|
13
|
+
protected updatingAttribute = false;
|
|
14
|
+
protected playPauseButton: HTMLDivElement | null = null;
|
|
15
|
+
protected setCustomControlState: ((playing: boolean) => void) | null = null;
|
|
16
|
+
protected ariaLiveRegion: HTMLElement | null = null;
|
|
17
|
+
protected intersectionObserver: IntersectionObserver | null = null;
|
|
18
|
+
protected hasLoadedVideo = false;
|
|
19
|
+
protected posterClickHandler: EventListener | null = null;
|
|
20
|
+
protected keyboardHandler: ((e: KeyboardEvent) => void) | null = null;
|
|
21
|
+
|
|
22
|
+
// Common protected properties that subclasses can access
|
|
23
|
+
protected _playing: boolean = false;
|
|
24
|
+
protected _autoplay: boolean = false;
|
|
25
|
+
protected _muted: boolean = false;
|
|
26
|
+
protected _controls: boolean = false;
|
|
27
|
+
protected _lazy: boolean = false;
|
|
28
|
+
protected _background: boolean = false;
|
|
29
|
+
protected _poster: string = "";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Abstract methods that subclasses must implement
|
|
33
|
+
*/
|
|
34
|
+
protected abstract initializePlayer(...args: any[]): Promise<void> | void;
|
|
35
|
+
protected abstract reinitializePlayer(): void;
|
|
36
|
+
protected abstract destroyPlayer(): void;
|
|
37
|
+
protected abstract getComponentName(): string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Logging utilities
|
|
41
|
+
*/
|
|
42
|
+
protected log(...args: any[]): void {
|
|
43
|
+
if ((this.constructor as typeof BaseVideoEmbed).DEBUG) {
|
|
44
|
+
console.log(`${this.getComponentName()}:`, ...args);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected warn(...args: any[]): void {
|
|
49
|
+
console.warn(...args);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected error(...args: any[]): void {
|
|
53
|
+
console.error(...args);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Event dispatching
|
|
58
|
+
*/
|
|
59
|
+
protected dispatchCustomEvent(eventName: string, detail?: any): void {
|
|
60
|
+
this.dispatchEvent(
|
|
61
|
+
new CustomEvent(eventName, {
|
|
62
|
+
detail,
|
|
63
|
+
bubbles: true,
|
|
64
|
+
composed: true,
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Attribute reflection helpers
|
|
71
|
+
*/
|
|
72
|
+
protected reflectBooleanAttribute(name: string, value: boolean): void {
|
|
73
|
+
this.updatingAttribute = true;
|
|
74
|
+
if (value) {
|
|
75
|
+
this.setAttribute(name, "");
|
|
76
|
+
} else {
|
|
77
|
+
this.removeAttribute(name);
|
|
78
|
+
}
|
|
79
|
+
this.updatingAttribute = false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
protected reflectAttribute(name: string, value: string): void {
|
|
83
|
+
this.updatingAttribute = true;
|
|
84
|
+
if (value) {
|
|
85
|
+
this.setAttribute(name, value);
|
|
86
|
+
} else {
|
|
87
|
+
this.removeAttribute(name);
|
|
88
|
+
}
|
|
89
|
+
this.updatingAttribute = false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Error message display (common across all components)
|
|
94
|
+
*/
|
|
95
|
+
protected showErrorMessage(message: string, errorClass: string): void {
|
|
96
|
+
const errorDiv = document.createElement("div");
|
|
97
|
+
errorDiv.className = errorClass;
|
|
98
|
+
errorDiv.setAttribute("role", "alert");
|
|
99
|
+
errorDiv.innerHTML = `
|
|
100
|
+
<div class="error-content">
|
|
101
|
+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
102
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
103
|
+
<line x1="12" y1="8" x2="12" y2="12"></line>
|
|
104
|
+
<line x1="12" y1="16" x2="12.01" y2="16"></line>
|
|
105
|
+
</svg>
|
|
106
|
+
<p class="error-message">${message}</p>
|
|
107
|
+
<button class="retry-button">Retry</button>
|
|
108
|
+
</div>
|
|
109
|
+
`;
|
|
110
|
+
|
|
111
|
+
const retryButton = errorDiv.querySelector(".retry-button");
|
|
112
|
+
if (retryButton) {
|
|
113
|
+
retryButton.addEventListener("click", () => {
|
|
114
|
+
errorDiv.remove();
|
|
115
|
+
this.handleRetry();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.innerHTML = "";
|
|
120
|
+
this.appendChild(errorDiv);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Retry handler - subclasses can override
|
|
125
|
+
*/
|
|
126
|
+
protected handleRetry(): void {
|
|
127
|
+
this.reinitializePlayer();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Background mode management (common across all components)
|
|
132
|
+
*/
|
|
133
|
+
protected updateBackgroundMode(): void {
|
|
134
|
+
if (this._background) {
|
|
135
|
+
this.classList.add("is-background");
|
|
136
|
+
// Enforce background mode requirements
|
|
137
|
+
this._autoplay = true;
|
|
138
|
+
this._muted = true;
|
|
139
|
+
this._controls = false;
|
|
140
|
+
this._lazy = false;
|
|
141
|
+
// Reflect the forced property changes back to attributes
|
|
142
|
+
this.reflectBooleanAttribute("autoplay", true);
|
|
143
|
+
this.reflectBooleanAttribute("muted", true);
|
|
144
|
+
this.reflectBooleanAttribute("controls", false);
|
|
145
|
+
this.reflectBooleanAttribute("lazy", false);
|
|
146
|
+
} else {
|
|
147
|
+
this.classList.remove("is-background");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Custom controls creation (similar pattern across components)
|
|
153
|
+
*/
|
|
154
|
+
protected addCustomControls(containerClass: string): void {
|
|
155
|
+
this.classList.add(containerClass);
|
|
156
|
+
|
|
157
|
+
const buttonOverlay = document.createElement("div");
|
|
158
|
+
buttonOverlay.classList.add("button-overlay");
|
|
159
|
+
|
|
160
|
+
this.playPauseButton = document.createElement("div");
|
|
161
|
+
this.playPauseButton.classList.add("button");
|
|
162
|
+
let isPlaying = false;
|
|
163
|
+
|
|
164
|
+
const setPlayingState = (playing: boolean) => {
|
|
165
|
+
isPlaying = playing;
|
|
166
|
+
if (playing) {
|
|
167
|
+
this.classList.add("is-playing");
|
|
168
|
+
} else {
|
|
169
|
+
this.classList.remove("is-playing");
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const togglePlayPause = () => {
|
|
174
|
+
if (isPlaying) {
|
|
175
|
+
this.handlePause();
|
|
176
|
+
setPlayingState(false);
|
|
177
|
+
} else {
|
|
178
|
+
this.handlePlay();
|
|
179
|
+
setPlayingState(true);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
this.setCustomControlState = setPlayingState;
|
|
184
|
+
|
|
185
|
+
buttonOverlay.addEventListener("click", togglePlayPause);
|
|
186
|
+
if (this.playPauseButton) {
|
|
187
|
+
buttonOverlay.appendChild(this.playPauseButton);
|
|
188
|
+
}
|
|
189
|
+
this.appendChild(buttonOverlay);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Play/Pause handlers - subclasses override with platform-specific logic
|
|
194
|
+
*/
|
|
195
|
+
protected abstract handlePlay(): void;
|
|
196
|
+
protected abstract handlePause(): void;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Lazy loading setup (common pattern)
|
|
200
|
+
*/
|
|
201
|
+
protected setupLazyLoading(callback: () => void): void {
|
|
202
|
+
this.intersectionObserver = new IntersectionObserver(
|
|
203
|
+
(entries) => {
|
|
204
|
+
entries.forEach((entry) => {
|
|
205
|
+
if (entry.isIntersecting && !this.hasLoadedVideo) {
|
|
206
|
+
this.log("Video entering viewport, loading...");
|
|
207
|
+
this.hasLoadedVideo = true;
|
|
208
|
+
callback();
|
|
209
|
+
if (this.intersectionObserver) {
|
|
210
|
+
this.intersectionObserver.disconnect();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
rootMargin: "100px",
|
|
217
|
+
},
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
this.intersectionObserver.observe(this);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* ARIA announcements for accessibility
|
|
225
|
+
*/
|
|
226
|
+
protected announceToScreenReader(message: string): void {
|
|
227
|
+
if (!this.ariaLiveRegion) {
|
|
228
|
+
this.ariaLiveRegion = document.createElement("div");
|
|
229
|
+
this.ariaLiveRegion.setAttribute("role", "status");
|
|
230
|
+
this.ariaLiveRegion.setAttribute("aria-live", "polite");
|
|
231
|
+
this.ariaLiveRegion.setAttribute("aria-atomic", "true");
|
|
232
|
+
this.ariaLiveRegion.style.position = "absolute";
|
|
233
|
+
this.ariaLiveRegion.style.left = "-10000px";
|
|
234
|
+
this.ariaLiveRegion.style.width = "1px";
|
|
235
|
+
this.ariaLiveRegion.style.height = "1px";
|
|
236
|
+
this.ariaLiveRegion.style.overflow = "hidden";
|
|
237
|
+
this.appendChild(this.ariaLiveRegion);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.ariaLiveRegion.textContent = message;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Lifecycle management
|
|
245
|
+
*/
|
|
246
|
+
connectedCallback(): void {
|
|
247
|
+
(this.constructor as typeof BaseVideoEmbed).instanceCount++;
|
|
248
|
+
this.log(
|
|
249
|
+
"connectedCallback called, instance count:",
|
|
250
|
+
(this.constructor as typeof BaseVideoEmbed).instanceCount,
|
|
251
|
+
);
|
|
252
|
+
this.dispatchCustomEvent("connected");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
disconnectedCallback(): void {
|
|
256
|
+
this.log("disconnectedCallback called. Cleaning up...");
|
|
257
|
+
|
|
258
|
+
// Cleanup player
|
|
259
|
+
try {
|
|
260
|
+
this.destroyPlayer();
|
|
261
|
+
} catch (error) {
|
|
262
|
+
this.warn("Error destroying player:", error);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Cleanup observers
|
|
266
|
+
if (this.intersectionObserver) {
|
|
267
|
+
this.intersectionObserver.disconnect();
|
|
268
|
+
this.intersectionObserver = null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Cleanup event listeners
|
|
272
|
+
if (this.posterClickHandler) {
|
|
273
|
+
this.removeEventListener("click", this.posterClickHandler);
|
|
274
|
+
this.posterClickHandler = null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (this.keyboardHandler) {
|
|
278
|
+
this.removeEventListener("keydown", this.keyboardHandler);
|
|
279
|
+
this.keyboardHandler = null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Decrement instance count
|
|
283
|
+
(this.constructor as typeof BaseVideoEmbed).instanceCount--;
|
|
284
|
+
this.log(
|
|
285
|
+
"Cleanup complete. Remaining instances:",
|
|
286
|
+
(this.constructor as typeof BaseVideoEmbed).instanceCount,
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
this.dispatchCustomEvent("disconnected");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Static debug toggle
|
|
294
|
+
*/
|
|
295
|
+
public static toggleDebug(forceState?: boolean): void {
|
|
296
|
+
if (forceState !== undefined) {
|
|
297
|
+
this.DEBUG = forceState;
|
|
298
|
+
} else {
|
|
299
|
+
this.DEBUG = !this.DEBUG;
|
|
300
|
+
}
|
|
301
|
+
console.log(`Debug mode ${this.DEBUG ? "enabled" : "disabled"}`);
|
|
302
|
+
}
|
|
303
|
+
}
|