vidply 1.0.5 → 1.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.
@@ -1,233 +1,233 @@
1
- /**
2
- * Keyboard Accessibility Manager
3
- */
4
-
5
- export class KeyboardManager {
6
- constructor(player) {
7
- this.player = player;
8
- this.shortcuts = player.options.keyboardShortcuts;
9
-
10
- this.init();
11
- }
12
-
13
- init() {
14
- this.attachEvents();
15
- }
16
-
17
- attachEvents() {
18
- // Listen for keyboard events on the player container
19
- this.player.container.addEventListener('keydown', (e) => {
20
- this.handleKeydown(e);
21
- });
22
-
23
- // Make player container focusable
24
- if (!this.player.container.hasAttribute('tabindex')) {
25
- this.player.container.setAttribute('tabindex', '0');
26
- }
27
- }
28
-
29
- handleKeydown(e) {
30
- // Don't handle if target is an input element
31
- if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
32
- return;
33
- }
34
-
35
- const key = e.key;
36
- let handled = false;
37
-
38
- // Check each shortcut category
39
- for (const [action, keys] of Object.entries(this.shortcuts)) {
40
- if (keys.includes(key)) {
41
- handled = this.executeAction(action, e);
42
- if (handled) {
43
- e.preventDefault();
44
- e.stopPropagation();
45
- this.announceAction(action);
46
- break;
47
- }
48
- }
49
- }
50
-
51
- // Log unhandled keys for debugging (in development)
52
- if (!handled && this.player.options.debug) {
53
- console.log('[VidPly] Unhandled key:', e.key, 'code:', e.code, 'shiftKey:', e.shiftKey);
54
- }
55
- }
56
-
57
- executeAction(action, event) {
58
- switch (action) {
59
- case 'play-pause':
60
- this.player.toggle();
61
- return true;
62
-
63
- case 'volume-up':
64
- this.player.setVolume(Math.min(1, this.player.state.volume + 0.1));
65
- return true;
66
-
67
- case 'volume-down':
68
- this.player.setVolume(Math.max(0, this.player.state.volume - 0.1));
69
- return true;
70
-
71
- case 'seek-forward':
72
- this.player.seekForward();
73
- return true;
74
-
75
- case 'seek-backward':
76
- this.player.seekBackward();
77
- return true;
78
-
79
- case 'mute':
80
- this.player.toggleMute();
81
- return true;
82
-
83
- case 'fullscreen':
84
- this.player.toggleFullscreen();
85
- return true;
86
-
87
- case 'captions':
88
- // If only one caption track, toggle on/off
89
- // If multiple tracks, open caption menu
90
- if (this.player.captionManager && this.player.captionManager.tracks.length > 1) {
91
- // Get captions button from control bar
92
- const captionsButton = this.player.controlBar && this.player.controlBar.controls.captions;
93
- if (captionsButton) {
94
- this.player.controlBar.showCaptionsMenu(captionsButton);
95
- } else {
96
- // Fallback to toggle if button doesn't exist
97
- this.player.toggleCaptions();
98
- }
99
- } else {
100
- this.player.toggleCaptions();
101
- }
102
- return true;
103
-
104
- case 'caption-style-menu':
105
- // Open caption style menu
106
- if (this.player.controlBar && this.player.controlBar.controls.captionStyle) {
107
- this.player.controlBar.showCaptionStyleMenu(this.player.controlBar.controls.captionStyle);
108
- return true;
109
- }
110
- return false;
111
-
112
- case 'speed-up':
113
- this.player.setPlaybackSpeed(
114
- Math.min(2, this.player.state.playbackSpeed + 0.25)
115
- );
116
- return true;
117
-
118
- case 'speed-down':
119
- this.player.setPlaybackSpeed(
120
- Math.max(0.25, this.player.state.playbackSpeed - 0.25)
121
- );
122
- return true;
123
-
124
- case 'speed-menu':
125
- // Open speed menu
126
- if (this.player.controlBar && this.player.controlBar.controls.speed) {
127
- this.player.controlBar.showSpeedMenu(this.player.controlBar.controls.speed);
128
- return true;
129
- }
130
- return false;
131
-
132
- case 'quality-menu':
133
- // Open quality menu
134
- if (this.player.controlBar && this.player.controlBar.controls.quality) {
135
- this.player.controlBar.showQualityMenu(this.player.controlBar.controls.quality);
136
- return true;
137
- }
138
- return false;
139
-
140
- case 'chapters-menu':
141
- // Open chapters menu
142
- if (this.player.controlBar && this.player.controlBar.controls.chapters) {
143
- this.player.controlBar.showChaptersMenu(this.player.controlBar.controls.chapters);
144
- return true;
145
- }
146
- return false;
147
-
148
- case 'transcript-toggle':
149
- // Toggle transcript
150
- if (this.player.transcriptManager) {
151
- this.player.transcriptManager.toggleTranscript();
152
- return true;
153
- }
154
- return false;
155
-
156
- default:
157
- return false;
158
- }
159
- }
160
-
161
- announceAction(action) {
162
- if (!this.player.options.screenReaderAnnouncements) return;
163
-
164
- let message = '';
165
-
166
- switch (action) {
167
- case 'play-pause':
168
- message = this.player.state.playing ? 'Playing' : 'Paused';
169
- break;
170
- case 'volume-up':
171
- message = `Volume ${Math.round(this.player.state.volume * 100)}%`;
172
- break;
173
- case 'volume-down':
174
- message = `Volume ${Math.round(this.player.state.volume * 100)}%`;
175
- break;
176
- case 'mute':
177
- message = this.player.state.muted ? 'Muted' : 'Unmuted';
178
- break;
179
- case 'fullscreen':
180
- message = this.player.state.fullscreen ? 'Fullscreen' : 'Exit fullscreen';
181
- break;
182
- case 'captions':
183
- message = this.player.state.captionsEnabled ? 'Captions on' : 'Captions off';
184
- break;
185
- case 'speed-up':
186
- case 'speed-down':
187
- message = `Speed ${this.player.state.playbackSpeed}x`;
188
- break;
189
- }
190
-
191
- if (message) {
192
- this.announce(message);
193
- }
194
- }
195
-
196
- announce(message, priority = 'polite') {
197
- // Create or get announcement element
198
- let announcer = document.getElementById('vidply-announcer');
199
-
200
- if (!announcer) {
201
- announcer = document.createElement('div');
202
- announcer.id = 'vidply-announcer';
203
- announcer.className = 'vidply-sr-only';
204
- announcer.setAttribute('aria-live', priority);
205
- announcer.setAttribute('aria-atomic', 'true');
206
- announcer.style.cssText = `
207
- position: absolute;
208
- left: -10000px;
209
- width: 1px;
210
- height: 1px;
211
- overflow: hidden;
212
- `;
213
- document.body.appendChild(announcer);
214
- }
215
-
216
- // Clear and set new message
217
- announcer.textContent = '';
218
- setTimeout(() => {
219
- announcer.textContent = message;
220
- }, 100);
221
- }
222
-
223
- updateShortcut(action, keys) {
224
- if (Array.isArray(keys)) {
225
- this.shortcuts[action] = keys;
226
- }
227
- }
228
-
229
- destroy() {
230
- // Event listeners are automatically removed when the container is destroyed
231
- }
232
- }
233
-
1
+ /**
2
+ * Keyboard Accessibility Manager
3
+ */
4
+
5
+ export class KeyboardManager {
6
+ constructor(player) {
7
+ this.player = player;
8
+ this.shortcuts = player.options.keyboardShortcuts;
9
+
10
+ this.init();
11
+ }
12
+
13
+ init() {
14
+ this.attachEvents();
15
+ }
16
+
17
+ attachEvents() {
18
+ // Listen for keyboard events on the player container
19
+ this.player.container.addEventListener('keydown', (e) => {
20
+ this.handleKeydown(e);
21
+ });
22
+
23
+ // Make player container focusable
24
+ if (!this.player.container.hasAttribute('tabindex')) {
25
+ this.player.container.setAttribute('tabindex', '0');
26
+ }
27
+ }
28
+
29
+ handleKeydown(e) {
30
+ // Don't handle if target is an input element
31
+ if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
32
+ return;
33
+ }
34
+
35
+ const key = e.key;
36
+ let handled = false;
37
+
38
+ // Check each shortcut category
39
+ for (const [action, keys] of Object.entries(this.shortcuts)) {
40
+ if (keys.includes(key)) {
41
+ handled = this.executeAction(action, e);
42
+ if (handled) {
43
+ e.preventDefault();
44
+ e.stopPropagation();
45
+ this.announceAction(action);
46
+ break;
47
+ }
48
+ }
49
+ }
50
+
51
+ // Log unhandled keys for debugging (in development)
52
+ if (!handled && this.player.options.debug) {
53
+ console.log('[VidPly] Unhandled key:', e.key, 'code:', e.code, 'shiftKey:', e.shiftKey);
54
+ }
55
+ }
56
+
57
+ executeAction(action, event) {
58
+ switch (action) {
59
+ case 'play-pause':
60
+ this.player.toggle();
61
+ return true;
62
+
63
+ case 'volume-up':
64
+ this.player.setVolume(Math.min(1, this.player.state.volume + 0.1));
65
+ return true;
66
+
67
+ case 'volume-down':
68
+ this.player.setVolume(Math.max(0, this.player.state.volume - 0.1));
69
+ return true;
70
+
71
+ case 'seek-forward':
72
+ this.player.seekForward();
73
+ return true;
74
+
75
+ case 'seek-backward':
76
+ this.player.seekBackward();
77
+ return true;
78
+
79
+ case 'mute':
80
+ this.player.toggleMute();
81
+ return true;
82
+
83
+ case 'fullscreen':
84
+ this.player.toggleFullscreen();
85
+ return true;
86
+
87
+ case 'captions':
88
+ // If only one caption track, toggle on/off
89
+ // If multiple tracks, open caption menu
90
+ if (this.player.captionManager && this.player.captionManager.tracks.length > 1) {
91
+ // Get captions button from control bar
92
+ const captionsButton = this.player.controlBar && this.player.controlBar.controls.captions;
93
+ if (captionsButton) {
94
+ this.player.controlBar.showCaptionsMenu(captionsButton);
95
+ } else {
96
+ // Fallback to toggle if button doesn't exist
97
+ this.player.toggleCaptions();
98
+ }
99
+ } else {
100
+ this.player.toggleCaptions();
101
+ }
102
+ return true;
103
+
104
+ case 'caption-style-menu':
105
+ // Open caption style menu
106
+ if (this.player.controlBar && this.player.controlBar.controls.captionStyle) {
107
+ this.player.controlBar.showCaptionStyleMenu(this.player.controlBar.controls.captionStyle);
108
+ return true;
109
+ }
110
+ return false;
111
+
112
+ case 'speed-up':
113
+ this.player.setPlaybackSpeed(
114
+ Math.min(2, this.player.state.playbackSpeed + 0.25)
115
+ );
116
+ return true;
117
+
118
+ case 'speed-down':
119
+ this.player.setPlaybackSpeed(
120
+ Math.max(0.25, this.player.state.playbackSpeed - 0.25)
121
+ );
122
+ return true;
123
+
124
+ case 'speed-menu':
125
+ // Open speed menu
126
+ if (this.player.controlBar && this.player.controlBar.controls.speed) {
127
+ this.player.controlBar.showSpeedMenu(this.player.controlBar.controls.speed);
128
+ return true;
129
+ }
130
+ return false;
131
+
132
+ case 'quality-menu':
133
+ // Open quality menu
134
+ if (this.player.controlBar && this.player.controlBar.controls.quality) {
135
+ this.player.controlBar.showQualityMenu(this.player.controlBar.controls.quality);
136
+ return true;
137
+ }
138
+ return false;
139
+
140
+ case 'chapters-menu':
141
+ // Open chapters menu
142
+ if (this.player.controlBar && this.player.controlBar.controls.chapters) {
143
+ this.player.controlBar.showChaptersMenu(this.player.controlBar.controls.chapters);
144
+ return true;
145
+ }
146
+ return false;
147
+
148
+ case 'transcript-toggle':
149
+ // Toggle transcript
150
+ if (this.player.transcriptManager) {
151
+ this.player.transcriptManager.toggleTranscript();
152
+ return true;
153
+ }
154
+ return false;
155
+
156
+ default:
157
+ return false;
158
+ }
159
+ }
160
+
161
+ announceAction(action) {
162
+ if (!this.player.options.screenReaderAnnouncements) return;
163
+
164
+ let message = '';
165
+
166
+ switch (action) {
167
+ case 'play-pause':
168
+ message = this.player.state.playing ? 'Playing' : 'Paused';
169
+ break;
170
+ case 'volume-up':
171
+ message = `Volume ${Math.round(this.player.state.volume * 100)}%`;
172
+ break;
173
+ case 'volume-down':
174
+ message = `Volume ${Math.round(this.player.state.volume * 100)}%`;
175
+ break;
176
+ case 'mute':
177
+ message = this.player.state.muted ? 'Muted' : 'Unmuted';
178
+ break;
179
+ case 'fullscreen':
180
+ message = this.player.state.fullscreen ? 'Fullscreen' : 'Exit fullscreen';
181
+ break;
182
+ case 'captions':
183
+ message = this.player.state.captionsEnabled ? 'Captions on' : 'Captions off';
184
+ break;
185
+ case 'speed-up':
186
+ case 'speed-down':
187
+ message = `Speed ${this.player.state.playbackSpeed}x`;
188
+ break;
189
+ }
190
+
191
+ if (message) {
192
+ this.announce(message);
193
+ }
194
+ }
195
+
196
+ announce(message, priority = 'polite') {
197
+ // Create or get announcement element
198
+ let announcer = document.getElementById('vidply-announcer');
199
+
200
+ if (!announcer) {
201
+ announcer = document.createElement('div');
202
+ announcer.id = 'vidply-announcer';
203
+ announcer.className = 'vidply-sr-only';
204
+ announcer.setAttribute('aria-live', priority);
205
+ announcer.setAttribute('aria-atomic', 'true');
206
+ announcer.style.cssText = `
207
+ position: absolute;
208
+ left: -10000px;
209
+ width: 1px;
210
+ height: 1px;
211
+ overflow: hidden;
212
+ `;
213
+ document.body.appendChild(announcer);
214
+ }
215
+
216
+ // Clear and set new message
217
+ announcer.textContent = '';
218
+ setTimeout(() => {
219
+ announcer.textContent = message;
220
+ }, 100);
221
+ }
222
+
223
+ updateShortcut(action, keys) {
224
+ if (Array.isArray(keys)) {
225
+ this.shortcuts[action] = keys;
226
+ }
227
+ }
228
+
229
+ destroy() {
230
+ // Event listeners are automatically removed when the container is destroyed
231
+ }
232
+ }
233
+