vidply 1.0.28 → 1.0.30

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.
Files changed (75) hide show
  1. package/dist/dev/vidply.HLSRenderer-UMPUDSYL.js +266 -0
  2. package/dist/dev/vidply.HLSRenderer-UMPUDSYL.js.map +7 -0
  3. package/dist/dev/vidply.HTML5Renderer-FXBZQL6Y.js +12 -0
  4. package/dist/dev/vidply.HTML5Renderer-FXBZQL6Y.js.map +7 -0
  5. package/dist/dev/{vidply.TranscriptManager-QSF2PWUN.js → vidply.TranscriptManager-T677KF4N.js} +4 -5
  6. package/dist/dev/{vidply.TranscriptManager-QSF2PWUN.js.map → vidply.TranscriptManager-T677KF4N.js.map} +2 -2
  7. package/dist/dev/{vidply.chunk-SRM7VNHG.js → vidply.chunk-GS2JX5RQ.js} +136 -95
  8. package/dist/dev/vidply.chunk-GS2JX5RQ.js.map +7 -0
  9. package/dist/dev/vidply.chunk-W2LSBD6Y.js +251 -0
  10. package/dist/dev/vidply.chunk-W2LSBD6Y.js.map +7 -0
  11. package/dist/dev/vidply.esm.js +1880 -258
  12. package/dist/dev/vidply.esm.js.map +4 -4
  13. package/dist/legacy/vidply.js +2056 -365
  14. package/dist/legacy/vidply.js.map +4 -4
  15. package/dist/legacy/vidply.min.js +1 -1
  16. package/dist/legacy/vidply.min.meta.json +111 -25
  17. package/dist/prod/vidply.HLSRenderer-3CG7BZKA.min.js +6 -0
  18. package/dist/prod/vidply.HTML5Renderer-KKW3OLHM.min.js +6 -0
  19. package/dist/prod/vidply.TranscriptManager-WFZSW6NR.min.js +6 -0
  20. package/dist/prod/vidply.chunk-34RH2THY.min.js +6 -0
  21. package/dist/prod/vidply.chunk-LGTJRPUL.min.js +6 -0
  22. package/dist/prod/vidply.esm.min.js +8 -8
  23. package/dist/vidply.css +20 -1
  24. package/dist/vidply.esm.min.meta.json +120 -34
  25. package/dist/vidply.min.css +1 -1
  26. package/package.json +2 -2
  27. package/src/controls/ControlBar.js +182 -10
  28. package/src/controls/TranscriptManager.js +7 -7
  29. package/src/core/AudioDescriptionManager.js +701 -0
  30. package/src/core/Player.js +203 -256
  31. package/src/core/SignLanguageManager.js +1134 -0
  32. package/src/renderers/HTML5Renderer.js +7 -0
  33. package/src/styles/vidply.css +20 -1
  34. package/src/utils/DOMUtils.js +153 -114
  35. package/src/utils/MenuFactory.js +374 -0
  36. package/src/utils/VideoFrameCapture.js +110 -0
  37. package/dist/dev/vidply.TranscriptManager-GZKY44ON.js +0 -1744
  38. package/dist/dev/vidply.TranscriptManager-GZKY44ON.js.map +0 -7
  39. package/dist/dev/vidply.TranscriptManager-UTJBQC5B.js +0 -1744
  40. package/dist/dev/vidply.TranscriptManager-UTJBQC5B.js.map +0 -7
  41. package/dist/dev/vidply.chunk-5663PYKK.js +0 -1631
  42. package/dist/dev/vidply.chunk-5663PYKK.js.map +0 -7
  43. package/dist/dev/vidply.chunk-SRM7VNHG.js.map +0 -7
  44. package/dist/dev/vidply.chunk-UH5MTGKF.js +0 -1630
  45. package/dist/dev/vidply.chunk-UH5MTGKF.js.map +0 -7
  46. package/dist/dev/vidply.de-RXAJM5QE.js +0 -181
  47. package/dist/dev/vidply.de-RXAJM5QE.js.map +0 -7
  48. package/dist/dev/vidply.de-THBIMP4S.js +0 -180
  49. package/dist/dev/vidply.de-THBIMP4S.js.map +0 -7
  50. package/dist/dev/vidply.es-6VWDNNNL.js +0 -180
  51. package/dist/dev/vidply.es-6VWDNNNL.js.map +0 -7
  52. package/dist/dev/vidply.es-SADVLJTQ.js +0 -181
  53. package/dist/dev/vidply.es-SADVLJTQ.js.map +0 -7
  54. package/dist/dev/vidply.fr-V3VAYBBT.js +0 -181
  55. package/dist/dev/vidply.fr-V3VAYBBT.js.map +0 -7
  56. package/dist/dev/vidply.fr-WHTWCHWT.js +0 -180
  57. package/dist/dev/vidply.fr-WHTWCHWT.js.map +0 -7
  58. package/dist/dev/vidply.ja-BFQNPOFI.js +0 -180
  59. package/dist/dev/vidply.ja-BFQNPOFI.js.map +0 -7
  60. package/dist/dev/vidply.ja-KL2TLZGJ.js +0 -181
  61. package/dist/dev/vidply.ja-KL2TLZGJ.js.map +0 -7
  62. package/dist/prod/vidply.TranscriptManager-DZ2WZU3K.min.js +0 -6
  63. package/dist/prod/vidply.TranscriptManager-E5QHGFIR.min.js +0 -6
  64. package/dist/prod/vidply.TranscriptManager-UZ6DUFB6.min.js +0 -6
  65. package/dist/prod/vidply.chunk-5DWTMWEO.min.js +0 -6
  66. package/dist/prod/vidply.chunk-IBNYTGGM.min.js +0 -6
  67. package/dist/prod/vidply.chunk-MBUR3U5L.min.js +0 -6
  68. package/dist/prod/vidply.de-HGJBCLLE.min.js +0 -6
  69. package/dist/prod/vidply.de-SWFW4HYT.min.js +0 -6
  70. package/dist/prod/vidply.es-7BJ2DJAY.min.js +0 -6
  71. package/dist/prod/vidply.es-CZEBXCZN.min.js +0 -6
  72. package/dist/prod/vidply.fr-DPVR5DFY.min.js +0 -6
  73. package/dist/prod/vidply.fr-HFOL7MWA.min.js +0 -6
  74. package/dist/prod/vidply.ja-PEBVWKVH.min.js +0 -6
  75. package/dist/prod/vidply.ja-QTVU5C25.min.js +0 -6
@@ -32,6 +32,13 @@ export class HTML5Renderer {
32
32
  this.media.addEventListener('loadedmetadata', () => {
33
33
  this.player.state.duration = this.media.duration;
34
34
  this.player.emit('loadedmetadata');
35
+
36
+ // Auto-generate poster if none exists (for HTML5 video only)
37
+ if (this.media.tagName === 'VIDEO') {
38
+ this.player.autoGeneratePoster().catch(error => {
39
+ this.player.log('Failed to auto-generate poster:', error, 'warn');
40
+ });
41
+ }
35
42
  });
36
43
 
37
44
  this.media.addEventListener('play', () => {
@@ -793,11 +793,30 @@
793
793
  color: var(--vidply-white);
794
794
  display: none;
795
795
  font-size: 0.75rem;
796
- padding: 0.25rem 0.5rem;
796
+ padding: 0;
797
797
  pointer-events: none;
798
798
  position: absolute;
799
799
  transform: translateX(-50%);
800
800
  white-space: nowrap;
801
+ overflow: hidden;
802
+ }
803
+
804
+ .vidply-progress-preview {
805
+ background-size: cover;
806
+ background-position: center;
807
+ border-radius: 0.25rem 0.25rem 0 0;
808
+ display: none;
809
+ height: 5rem;
810
+ width: 8.888888889rem; /* 160px at 18px base */
811
+ min-width: 8.888888889rem;
812
+ }
813
+
814
+ .vidply-progress-tooltip-time {
815
+ background: var(--vidply-black-90);
816
+ border-radius: 0 0 0.25rem 0.25rem;
817
+ padding: 0.25rem 0.5rem;
818
+ text-align: center;
819
+ width: 100%;
801
820
  }
802
821
 
803
822
  /* Button Container */
@@ -1,8 +1,15 @@
1
1
  /**
2
2
  * DOM manipulation utilities
3
+ * Optimized for performance with CSS transitions
3
4
  */
4
5
 
5
6
  export const DOMUtils = {
7
+ /**
8
+ * Create an element with options
9
+ * @param {string} tag - HTML tag name
10
+ * @param {Object} options - Element options
11
+ * @returns {HTMLElement}
12
+ */
6
13
  createElement(tag, options = {}) {
7
14
  const element = document.createElement(tag);
8
15
 
@@ -11,9 +18,9 @@ export const DOMUtils = {
11
18
  }
12
19
 
13
20
  if (options.attributes) {
14
- Object.entries(options.attributes).forEach(([key, value]) => {
21
+ for (const [key, value] of Object.entries(options.attributes)) {
15
22
  element.setAttribute(key, value);
16
- });
23
+ }
17
24
  }
18
25
 
19
26
  if (options.innerHTML) {
@@ -29,193 +36,225 @@ export const DOMUtils = {
29
36
  }
30
37
 
31
38
  if (options.children) {
32
- options.children.forEach(child => {
39
+ for (const child of options.children) {
33
40
  if (child) element.appendChild(child);
34
- });
41
+ }
35
42
  }
36
43
 
37
44
  return element;
38
45
  },
39
46
 
40
- addClass(element, className) {
41
- if (element && className) {
42
- element.classList.add(className);
43
- }
44
- },
45
-
46
- removeClass(element, className) {
47
- if (element && className) {
48
- element.classList.remove(className);
49
- }
50
- },
51
-
52
- toggleClass(element, className) {
53
- if (element && className) {
54
- element.classList.toggle(className);
55
- }
56
- },
57
-
58
- hasClass(element, className) {
59
- return element && element.classList.contains(className);
60
- },
61
-
47
+ /**
48
+ * Show element (remove display:none)
49
+ * @param {HTMLElement} element
50
+ */
62
51
  show(element) {
63
- if (element) {
64
- element.style.display = '';
65
- }
52
+ element?.style && (element.style.display = '');
66
53
  },
67
54
 
55
+ /**
56
+ * Hide element
57
+ * @param {HTMLElement} element
58
+ */
68
59
  hide(element) {
69
- if (element) {
70
- element.style.display = 'none';
71
- }
60
+ element?.style && (element.style.display = 'none');
72
61
  },
73
62
 
74
- fadeIn(element, duration = 300) {
63
+ /**
64
+ * Fade in element using CSS transitions (GPU accelerated)
65
+ * @param {HTMLElement} element
66
+ * @param {number} duration - Duration in ms
67
+ * @param {Function} [onComplete] - Callback when complete
68
+ */
69
+ fadeIn(element, duration = 300, onComplete) {
75
70
  if (!element) return;
76
71
 
72
+ // Set up initial state
77
73
  element.style.opacity = '0';
78
74
  element.style.display = '';
75
+ element.style.transition = `opacity ${duration}ms ease`;
79
76
 
80
- let start = null;
81
- const animate = (timestamp) => {
82
- if (!start) start = timestamp;
83
- const progress = timestamp - start;
84
- const opacity = Math.min(progress / duration, 1);
85
-
86
- element.style.opacity = opacity;
87
-
88
- if (progress < duration) {
89
- requestAnimationFrame(animate);
90
- }
91
- };
77
+ // Force reflow to ensure transition works
78
+ element.offsetHeight;
79
+
80
+ // Trigger transition
81
+ element.style.opacity = '1';
92
82
 
93
- requestAnimationFrame(animate);
83
+ // Cleanup after transition
84
+ if (onComplete) {
85
+ const cleanup = () => {
86
+ element.removeEventListener('transitionend', cleanup);
87
+ onComplete();
88
+ };
89
+ element.addEventListener('transitionend', cleanup, { once: true });
90
+ // Fallback timeout in case transitionend doesn't fire
91
+ setTimeout(cleanup, duration + 50);
92
+ }
94
93
  },
95
94
 
96
- fadeOut(element, duration = 300) {
95
+ /**
96
+ * Fade out element using CSS transitions (GPU accelerated)
97
+ * @param {HTMLElement} element
98
+ * @param {number} duration - Duration in ms
99
+ * @param {Function} [onComplete] - Callback when complete
100
+ */
101
+ fadeOut(element, duration = 300, onComplete) {
97
102
  if (!element) return;
98
103
 
99
- const startOpacity = parseFloat(getComputedStyle(element).opacity) || 1;
100
- let start = null;
101
-
102
- const animate = (timestamp) => {
103
- if (!start) start = timestamp;
104
- const progress = timestamp - start;
105
- const opacity = Math.max(startOpacity - (progress / duration), 0);
106
-
107
- element.style.opacity = opacity;
108
-
109
- if (progress < duration) {
110
- requestAnimationFrame(animate);
111
- } else {
112
- element.style.display = 'none';
113
- }
104
+ element.style.transition = `opacity ${duration}ms ease`;
105
+ element.style.opacity = '0';
106
+
107
+ const cleanup = () => {
108
+ element.removeEventListener('transitionend', cleanup);
109
+ element.style.display = 'none';
110
+ if (onComplete) onComplete();
114
111
  };
115
112
 
116
- requestAnimationFrame(animate);
113
+ element.addEventListener('transitionend', cleanup, { once: true });
114
+ // Fallback timeout in case transitionend doesn't fire
115
+ setTimeout(cleanup, duration + 50);
117
116
  },
118
117
 
118
+ /**
119
+ * Get element's offset position and dimensions
120
+ * @param {HTMLElement} element
121
+ * @returns {Object} { top, left, width, height }
122
+ */
119
123
  offset(element) {
120
- if (!element) return { top: 0, left: 0 };
124
+ if (!element) return { top: 0, left: 0, width: 0, height: 0 };
121
125
 
122
126
  const rect = element.getBoundingClientRect();
123
127
  return {
124
- top: rect.top + window.pageYOffset,
125
- left: rect.left + window.pageXOffset,
128
+ top: rect.top + window.scrollY,
129
+ left: rect.left + window.scrollX,
126
130
  width: rect.width,
127
131
  height: rect.height
128
132
  };
129
133
  },
130
134
 
135
+ /**
136
+ * Escape HTML special characters
137
+ * @param {string} str - String to escape
138
+ * @returns {string} Escaped string
139
+ */
131
140
  escapeHTML(str) {
132
- const div = document.createElement('div');
133
- div.textContent = str;
134
- return div.innerHTML;
141
+ const escapeMap = {
142
+ '&': '&amp;',
143
+ '<': '&lt;',
144
+ '>': '&gt;',
145
+ '"': '&quot;',
146
+ "'": '&#x27;'
147
+ };
148
+ return str.replace(/[&<>"']/g, char => escapeMap[char]);
135
149
  },
136
150
 
151
+ /**
152
+ * Basic HTML sanitization for VTT captions
153
+ * Allows safe formatting tags, removes dangerous content
154
+ * @param {string} html - HTML string to sanitize
155
+ * @returns {string} Sanitized HTML
156
+ */
137
157
  sanitizeHTML(html) {
138
- // Basic HTML sanitization - allow safe tags for VTT captions
139
- // Since we control the HTML (from VTT parsing), we can safely allow these tags
140
- const temp = document.createElement('div');
141
-
142
- // Strip out any potentially dangerous tags/attributes
143
- // Allow: strong, em, u, span, b, i with class and data-voice attributes
158
+ // Remove dangerous content
144
159
  const safeHtml = html
145
160
  .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
146
161
  .replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '')
147
- .replace(/on\w+\s*=/gi, '') // Remove event handlers
148
- .replace(/javascript:/gi, ''); // Remove javascript: protocol
162
+ .replace(/on\w+\s*=/gi, '') // Event handlers
163
+ .replace(/javascript:/gi, ''); // javascript: protocol
149
164
 
165
+ // Use DOM parser for final sanitization
166
+ const temp = document.createElement('div');
150
167
  temp.innerHTML = safeHtml;
151
168
  return temp.innerHTML;
152
169
  },
153
170
 
154
171
  /**
155
- * Create a tooltip element that is aria-hidden (not read by screen readers)
172
+ * Create a tooltip element (aria-hidden)
156
173
  * @param {string} text - Tooltip text
157
- * @param {string} classPrefix - Class prefix for styling
158
- * @returns {HTMLElement} Tooltip element
174
+ * @param {string} classPrefix - Class prefix
175
+ * @returns {HTMLElement}
159
176
  */
160
177
  createTooltip(text, classPrefix = 'vidply') {
161
- const tooltip = this.createElement('span', {
178
+ return this.createElement('span', {
162
179
  className: `${classPrefix}-tooltip`,
163
180
  textContent: text,
164
- attributes: {
165
- 'aria-hidden': 'true'
166
- }
181
+ attributes: { 'aria-hidden': 'true' }
167
182
  });
168
- return tooltip;
169
183
  },
170
184
 
171
185
  /**
172
- * Attach a tooltip to an element
173
- * @param {HTMLElement} element - Element to attach tooltip to
186
+ * Attach a tooltip to an element with hover/focus behavior
187
+ * @param {HTMLElement} element - Target element
174
188
  * @param {string} text - Tooltip text
175
- * @param {string} classPrefix - Class prefix for styling
189
+ * @param {string} classPrefix - Class prefix
176
190
  */
177
191
  attachTooltip(element, text, classPrefix = 'vidply') {
178
192
  if (!element || !text) return;
179
193
 
180
- // Remove existing tooltip if any
181
- const existingTooltip = element.querySelector(`.${classPrefix}-tooltip`);
182
- if (existingTooltip) {
183
- existingTooltip.remove();
184
- }
194
+ // Remove existing tooltip
195
+ element.querySelector(`.${classPrefix}-tooltip`)?.remove();
185
196
 
186
197
  const tooltip = this.createTooltip(text, classPrefix);
187
198
  element.appendChild(tooltip);
188
199
 
189
- // Show tooltip on hover/focus
190
- const showTooltip = () => {
191
- tooltip.classList.add(`${classPrefix}-tooltip-visible`);
192
- };
193
-
194
- const hideTooltip = () => {
195
- tooltip.classList.remove(`${classPrefix}-tooltip-visible`);
196
- };
200
+ const visibleClass = `${classPrefix}-tooltip-visible`;
201
+ const show = () => tooltip.classList.add(visibleClass);
202
+ const hide = () => tooltip.classList.remove(visibleClass);
197
203
 
198
- element.addEventListener('mouseenter', showTooltip);
199
- element.addEventListener('mouseleave', hideTooltip);
200
- element.addEventListener('focus', showTooltip);
201
- element.addEventListener('blur', hideTooltip);
204
+ element.addEventListener('mouseenter', show);
205
+ element.addEventListener('mouseleave', hide);
206
+ element.addEventListener('focus', show);
207
+ element.addEventListener('blur', hide);
202
208
  },
203
209
 
204
210
  /**
205
- * Create visible button text that is hidden by CSS but visible when CSS is disabled
211
+ * Create button text element (visible when CSS disabled)
206
212
  * @param {string} text - Button text
207
- * @param {string} classPrefix - Class prefix for styling
208
- * @returns {HTMLElement} Button text element
213
+ * @param {string} classPrefix - Class prefix
214
+ * @returns {HTMLElement}
209
215
  */
210
216
  createButtonText(text, classPrefix = 'vidply') {
211
- const buttonText = this.createElement('span', {
217
+ return this.createElement('span', {
212
218
  className: `${classPrefix}-button-text`,
213
219
  textContent: text,
214
- attributes: {
215
- 'aria-hidden': 'true'
216
- }
220
+ attributes: { 'aria-hidden': 'true' }
217
221
  });
218
- return buttonText;
222
+ },
223
+
224
+ /**
225
+ * Add class to element (null-safe)
226
+ * @param {HTMLElement} element
227
+ * @param {string} className
228
+ */
229
+ addClass(element, className) {
230
+ element?.classList?.add(className);
231
+ },
232
+
233
+ /**
234
+ * Remove class from element (null-safe)
235
+ * @param {HTMLElement} element
236
+ * @param {string} className
237
+ */
238
+ removeClass(element, className) {
239
+ element?.classList?.remove(className);
240
+ },
241
+
242
+ /**
243
+ * Toggle class on element (null-safe)
244
+ * @param {HTMLElement} element
245
+ * @param {string} className
246
+ */
247
+ toggleClass(element, className) {
248
+ element?.classList?.toggle(className);
249
+ },
250
+
251
+ /**
252
+ * Check if element has class (null-safe)
253
+ * @param {HTMLElement} element
254
+ * @param {string} className
255
+ * @returns {boolean}
256
+ */
257
+ hasClass(element, className) {
258
+ return element?.classList?.contains(className) ?? false;
219
259
  }
220
260
  };
221
-