vidply 1.0.28 → 1.0.29
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/dist/dev/{vidply.TranscriptManager-QSF2PWUN.js → vidply.TranscriptManager-T677KF4N.js} +4 -5
- package/dist/dev/{vidply.TranscriptManager-QSF2PWUN.js.map → vidply.TranscriptManager-T677KF4N.js.map} +2 -2
- package/dist/dev/{vidply.chunk-SRM7VNHG.js → vidply.chunk-GS2JX5RQ.js} +136 -95
- package/dist/dev/vidply.chunk-GS2JX5RQ.js.map +7 -0
- package/dist/dev/vidply.esm.js +1674 -310
- package/dist/dev/vidply.esm.js.map +4 -4
- package/dist/legacy/vidply.js +1776 -348
- package/dist/legacy/vidply.js.map +4 -4
- package/dist/legacy/vidply.min.js +1 -1
- package/dist/legacy/vidply.min.meta.json +92 -24
- package/dist/prod/vidply.TranscriptManager-WFZSW6NR.min.js +6 -0
- package/dist/prod/vidply.chunk-LGTJRPUL.min.js +6 -0
- package/dist/prod/vidply.esm.min.js +8 -8
- package/dist/vidply.esm.min.meta.json +92 -24
- package/package.json +1 -1
- package/src/controls/ControlBar.js +3 -7
- package/src/controls/TranscriptManager.js +7 -7
- package/src/core/AudioDescriptionManager.js +701 -0
- package/src/core/Player.js +4776 -4921
- package/src/core/SignLanguageManager.js +1134 -0
- package/src/utils/DOMUtils.js +153 -114
- package/src/utils/MenuFactory.js +374 -0
- package/dist/dev/vidply.TranscriptManager-GZKY44ON.js +0 -1744
- package/dist/dev/vidply.TranscriptManager-GZKY44ON.js.map +0 -7
- package/dist/dev/vidply.TranscriptManager-UTJBQC5B.js +0 -1744
- package/dist/dev/vidply.TranscriptManager-UTJBQC5B.js.map +0 -7
- package/dist/dev/vidply.chunk-5663PYKK.js +0 -1631
- package/dist/dev/vidply.chunk-5663PYKK.js.map +0 -7
- package/dist/dev/vidply.chunk-SRM7VNHG.js.map +0 -7
- package/dist/dev/vidply.chunk-UH5MTGKF.js +0 -1630
- package/dist/dev/vidply.chunk-UH5MTGKF.js.map +0 -7
- package/dist/dev/vidply.de-RXAJM5QE.js +0 -181
- package/dist/dev/vidply.de-RXAJM5QE.js.map +0 -7
- package/dist/dev/vidply.de-THBIMP4S.js +0 -180
- package/dist/dev/vidply.de-THBIMP4S.js.map +0 -7
- package/dist/dev/vidply.es-6VWDNNNL.js +0 -180
- package/dist/dev/vidply.es-6VWDNNNL.js.map +0 -7
- package/dist/dev/vidply.es-SADVLJTQ.js +0 -181
- package/dist/dev/vidply.es-SADVLJTQ.js.map +0 -7
- package/dist/dev/vidply.fr-V3VAYBBT.js +0 -181
- package/dist/dev/vidply.fr-V3VAYBBT.js.map +0 -7
- package/dist/dev/vidply.fr-WHTWCHWT.js +0 -180
- package/dist/dev/vidply.fr-WHTWCHWT.js.map +0 -7
- package/dist/dev/vidply.ja-BFQNPOFI.js +0 -180
- package/dist/dev/vidply.ja-BFQNPOFI.js.map +0 -7
- package/dist/dev/vidply.ja-KL2TLZGJ.js +0 -181
- package/dist/dev/vidply.ja-KL2TLZGJ.js.map +0 -7
- package/dist/prod/vidply.TranscriptManager-DZ2WZU3K.min.js +0 -6
- package/dist/prod/vidply.TranscriptManager-E5QHGFIR.min.js +0 -6
- package/dist/prod/vidply.TranscriptManager-UZ6DUFB6.min.js +0 -6
- package/dist/prod/vidply.chunk-5DWTMWEO.min.js +0 -6
- package/dist/prod/vidply.chunk-IBNYTGGM.min.js +0 -6
- package/dist/prod/vidply.chunk-MBUR3U5L.min.js +0 -6
- package/dist/prod/vidply.de-HGJBCLLE.min.js +0 -6
- package/dist/prod/vidply.de-SWFW4HYT.min.js +0 -6
- package/dist/prod/vidply.es-7BJ2DJAY.min.js +0 -6
- package/dist/prod/vidply.es-CZEBXCZN.min.js +0 -6
- package/dist/prod/vidply.fr-DPVR5DFY.min.js +0 -6
- package/dist/prod/vidply.fr-HFOL7MWA.min.js +0 -6
- package/dist/prod/vidply.ja-PEBVWKVH.min.js +0 -6
- package/dist/prod/vidply.ja-QTVU5C25.min.js +0 -6
package/src/utils/DOMUtils.js
CHANGED
|
@@ -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)
|
|
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
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
element.style.display = 'none';
|
|
71
|
-
}
|
|
60
|
+
element?.style && (element.style.display = 'none');
|
|
72
61
|
},
|
|
73
62
|
|
|
74
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
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.
|
|
125
|
-
left: rect.left + window.
|
|
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
|
|
133
|
-
|
|
134
|
-
|
|
141
|
+
const escapeMap = {
|
|
142
|
+
'&': '&',
|
|
143
|
+
'<': '<',
|
|
144
|
+
'>': '>',
|
|
145
|
+
'"': '"',
|
|
146
|
+
"'": '''
|
|
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
|
-
//
|
|
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, '') //
|
|
148
|
-
.replace(/javascript:/gi, ''); //
|
|
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
|
|
172
|
+
* Create a tooltip element (aria-hidden)
|
|
156
173
|
* @param {string} text - Tooltip text
|
|
157
|
-
* @param {string} classPrefix - Class prefix
|
|
158
|
-
* @returns {HTMLElement}
|
|
174
|
+
* @param {string} classPrefix - Class prefix
|
|
175
|
+
* @returns {HTMLElement}
|
|
159
176
|
*/
|
|
160
177
|
createTooltip(text, classPrefix = 'vidply') {
|
|
161
|
-
|
|
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 -
|
|
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
|
|
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
|
|
181
|
-
|
|
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
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
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',
|
|
199
|
-
element.addEventListener('mouseleave',
|
|
200
|
-
element.addEventListener('focus',
|
|
201
|
-
element.addEventListener('blur',
|
|
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
|
|
211
|
+
* Create button text element (visible when CSS disabled)
|
|
206
212
|
* @param {string} text - Button text
|
|
207
|
-
* @param {string} classPrefix - Class prefix
|
|
208
|
-
* @returns {HTMLElement}
|
|
213
|
+
* @param {string} classPrefix - Class prefix
|
|
214
|
+
* @returns {HTMLElement}
|
|
209
215
|
*/
|
|
210
216
|
createButtonText(text, classPrefix = 'vidply') {
|
|
211
|
-
|
|
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
|
-
|
|
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
|
-
|