@@ -71,7 +71,7 @@ Object.assign(MediaElementPlayer.prototype, {
7171 // And always include the defaultSpeed
7272 {
7373 const length = t . options . speeds . length ;
74- for ( let i = 0 ; i < length ; i ++ ) {
74+ for ( let i = 0 ; i < length ; i ++ ) {
7575 const speed = t . options . speeds [ i ] ;
7676
7777 if ( typeof speed === 'string' ) {
@@ -114,13 +114,15 @@ Object.assign(MediaElementPlayer.prototype, {
114114
115115 // Build up the main button and the <div><ul></ul></div> next to it within the player bar.
116116 {
117- player . speedButton = document . createElement ( 'div' ) ;
118- player . speedButton . className = `${ t . options . classPrefix } button ${ t . options . classPrefix } speed-button` ;
117+ player . speedContainer = document . createElement ( 'div' ) ;
118+ player . speedContainer . className = `${ t . options . classPrefix } button ${ t . options . classPrefix } speed-button` ;
119+
120+ const generateId = Date . now ( ) + '.' + Math . floor ( Math . random ( ) * 100 ) ;
119121
120- player . speedButton . innerHTML =
122+ player . speedContainer . innerHTML =
121123 '<button ' +
122124 'type="button" ' +
123- `aria-controls="${ t . id } " ` +
125+ `aria-controls="${ generateId } " ` +
124126 'aria-expanded="false" ' +
125127 `title="${ speedTitle } " ` +
126128 `aria-label="${ speedTitle } " ` +
@@ -129,17 +131,17 @@ Object.assign(MediaElementPlayer.prototype, {
129131 getSpeedNameFromValue ( t . options . defaultSpeed ) +
130132 '</button>' +
131133 `<div class="${ t . options . classPrefix } speed-selector ${ t . options . classPrefix } offscreen">` +
132- `<ul class="${ t . options . classPrefix } speed-selector-list"></ul>` +
133- '</div>' +
134+ `<ul id=" ${ generateId } " class="${ t . options . classPrefix } speed-selector-list" tabindex="-1 "></ul>` +
135+ '</div>'
134136
135137 // Add this button into the DOM
136- t . addControlElement ( player . speedButton , 'speed' ) ;
138+ t . addControlElement ( player . speedContainer , 'speed' ) ;
137139
138140
139141 // Add speed li items.
140142 let ulHTML = ''
141143 const length = speeds . length ;
142- for ( let i = 0 ; i < length ; i ++ ) {
144+ for ( let i = 0 ; i < length ; i ++ ) {
143145 const speed = speeds [ i ]
144146
145147 const inputId = `${ t . id } -speed-${ speeds [ i ] . value } ` ;
@@ -149,7 +151,7 @@ Object.assign(MediaElementPlayer.prototype, {
149151 const liHTML =
150152 `<li class="${ t . options . classPrefix } speed-selector-list-item">` +
151153 `<input ` +
152- `class="${ t . options . classPrefix } speed-selector-input" ` +
154+ `class="${ t . options . classPrefix } speed-selector-input ${ ( speedIsDefaultSpeed ? ` ${ t . options . classPrefix } speed-selected-input` : '' ) } " ` +
153155 `type="radio" ` +
154156 `name="${ t . id } _speed"` +
155157 `disabled="disabled" ` +
@@ -159,8 +161,7 @@ Object.assign(MediaElementPlayer.prototype, {
159161 '/>' +
160162 '<label ' +
161163 `for="${ inputId } " ` +
162- `class="${ t . options . classPrefix } speed-selector-label` +
163- `${ ( speedIsDefaultSpeed ? ` ${ t . options . classPrefix } speed-selected` : '' ) } "` +
164+ `class="${ t . options . classPrefix } speed-selector-label` + `${ ( speedIsDefaultSpeed ? ` ${ t . options . classPrefix } speed-selected` : '' ) } "` +
164165 '>' +
165166 `${ speed . name } ` +
166167 '</label>' +
@@ -170,38 +171,55 @@ Object.assign(MediaElementPlayer.prototype, {
170171
171172 }
172173
173- player . speedButton . querySelector ( 'ul' ) . innerHTML = ulHTML
174+ player . speedContainer . querySelector ( 'ul' ) . innerHTML = ulHTML
174175 }
175176
176- player . speedSelector = player . speedButton . querySelector ( `.${ t . options . classPrefix } speed-selector` )
177+ player . speedSelector = player . speedContainer . querySelector ( `.${ t . options . classPrefix } speed-selector` )
177178
178179 // Enable inputs after they have been appended to controls to avoid tab and up/down arrow focus issues
179180 const
180- radios = player . speedButton . querySelectorAll ( 'input[type="radio"]' ) ,
181- labels = player . speedButton . querySelectorAll ( `.${ t . options . classPrefix } speed-selector-label` ) ,
182- speedList = player . speedButton . querySelector ( `.${ t . options . classPrefix } speed-selector-list` )
181+ speedButton = player . speedContainer . querySelector ( 'button' ) ,
182+ radios = player . speedContainer . querySelectorAll ( 'input[type="radio"]' ) ,
183+ labels = player . speedContainer . querySelectorAll ( `.${ t . options . classPrefix } speed-selector-label` ) ,
184+ speedList = player . speedContainer . querySelector ( `.${ t . options . classPrefix } speed-selector-list` )
183185 ;
184186
185187 // Handle the events.
186188 let menuIsHidden = true ;
187189
190+ let lastShowChange = Date . now ( ) ;
188191 function showMenu ( ) {
192+ const now = Date . now ( ) ;
193+ const diff = now - lastShowChange ;
194+ if ( diff < 16 ) {
195+ return ;
196+ }
197+ lastShowChange = now ;
198+
189199 // show the ul menu.
190200 mejs . Utils . removeClass ( player . speedSelector , `${ t . options . classPrefix } offscreen` ) ;
191201 player . speedSelector . style . height = `${ player . speedSelector . querySelector ( 'ul' ) . offsetHeight } px` ;
192202 player . speedSelector . style . top = `${ ( - 1 * Number ( player . speedSelector . offsetHeight ) ) } px` ;
193- player . speedButton . setAttribute ( 'aria-expanded' , 'true' ) ;
194-
195- // focus first radio input
203+ speedButton . setAttribute ( 'aria-expanded' , 'true' ) ;
196204
205+ // focus on selected radio input
206+ speedList . querySelector ( '.' + t . options . classPrefix + 'speed-selected-input' ) . focus ( ) ;
197207
198208 menuIsHidden = false ;
199209 }
200210
201211 function hideMenu ( ) {
212+ const now = Date . now ( ) ;
213+ const diff = now - lastShowChange ;
214+ if ( diff < 16 ) {
215+ return ;
216+ }
217+ lastShowChange = now ;
218+
202219 // hide ul menu
203220 mejs . Utils . addClass ( player . speedSelector , `${ t . options . classPrefix } offscreen` ) ;
204- player . speedButton . setAttribute ( 'aria-expanded' , 'true' ) ;
221+ speedButton . setAttribute ( 'aria-expanded' , 'true' ) ;
222+ speedButton . focus ( )
205223
206224 menuIsHidden = true ;
207225 }
@@ -215,65 +233,34 @@ Object.assign(MediaElementPlayer.prototype, {
215233 }
216234 }
217235
218- player . speedButton . addEventListener ( 'mouseenter' , showMenu ) ;
219- // player.speedButton.addEventListener('focusin', showMenu);
220236
221- player . speedButton . addEventListener ( 'mouseleave' , hideMenu ) ;
237+ speedButton . addEventListener ( 'mouseenter' , showMenu ) ;
238+
239+ player . speedContainer . addEventListener ( 'mouseleave' , hideMenu ) ;
222240 speedList . addEventListener ( 'focusout' , ( event ) => {
223- if ( ! player . speedButton . contains ( event . relatedTarget ) ) {
241+ if ( ! player . speedContainer . contains ( event . relatedTarget ) ) {
224242 hideMenu ( ) ;
225243 }
226244 } ) ;
227245
228- player . speedButton . addEventListener ( 'click' , hideShowMenu ) ;
246+ speedButton . addEventListener ( 'click' , hideShowMenu ) ;
229247
230248
249+ // Close with Escape key.
250+ // Allow up/down arrow to change the selected radio without changing the volume.
251+ player . speedContainer . addEventListener ( 'keydown' , ( event ) => {
252+ if ( event . key === "Escape" ) {
253+ hideMenu ( ) ;
254+ }
255+
256+ event . stopPropagation ( ) ;
257+ } ) ;
258+
231259 // handle speed change when the value of the radio changes.
232260 for ( let i = 0 , total = radios . length ; i < total ; i ++ ) {
233261 const radio = radios [ i ] ;
234262 radio . disabled = false ;
235- radio . addEventListener ( 'change' , function ( ) {
236-
237-
238- // loop through all of the options.
239- for ( let i = 0 ; i < total ; i ++ ) {
240- const radio = radios [ i ]
241-
242- // remove the speed-selected class from the label
243- const siblings = mejs . Utils . siblings ( radio , ( el ) => mejs . Utils . hasClass ( el , `${ t . options . classPrefix } speed-selector-label` ) ) ;
244- for ( let i = 0 , total = siblings . length ; i < total ; i ++ ) {
245- mejs . Utils . removeClass ( siblings [ i ] , `${ t . options . classPrefix } speed-selected` ) ;
246- }
247-
248- if ( radio . checked ) {
249-
250-
251- // add the speed-selected class to the label
252- const siblings = mejs . Utils . siblings ( radio , ( el ) => mejs . Utils . hasClass ( el , `${ t . options . classPrefix } speed-selector-label` ) ) ;
253- for ( let i = 0 , total = siblings . length ; i < total ; i ++ ) {
254- mejs . Utils . addClass ( siblings [ i ] , `${ t . options . classPrefix } speed-selected` ) ;
255- }
256-
257- // set the speed onto the media
258- const newSpeed = Number ( radio . value )
259- media . playbackRate = newSpeed
260- currentPlaybackSpeed = newSpeed
261- }
262-
263- }
264- // debugger
265- /*const
266- radioEl = this,
267- newSpeed = Number(radioEl.value)
268- ;
269-
270- // set the speed.
271- currentPlaybackSpeed = newSpeed;
272- media.playbackRate = newSpeed
273- */
274- // debugger
275- // t.changeSpeed(this, player, )
276- } ) ;
263+ radio . addEventListener ( 'change' , handleChangeSpeed ) ;
277264 }
278265
279266 // simulate clicks on radio elements.
@@ -283,14 +270,51 @@ Object.assign(MediaElementPlayer.prototype, {
283270 radio = mejs . Utils . siblings ( this , ( el ) => el . tagName === 'INPUT' ) [ 0 ] ,
284271 event = mejs . Utils . createEvent ( 'click' , radio )
285272 ;
273+
286274 radio . dispatchEvent ( event ) ;
287275 } ) ;
288276 }
289277
290278
291279
292- // @TODO : make events work.
280+ media . addEventListener ( 'loadedmetadata' , ( ) => {
281+ if ( currentPlaybackSpeed ) {
282+ media . playbackRate = Number ( currentPlaybackSpeed ) ;
283+ }
284+ } ) ;
285+
293286
287+ function handleChangeSpeed ( ) {
288+ const total = radios . length ;
289+ for ( let i = 0 ; i < total ; i ++ ) {
290+ const radio = radios [ i ]
291+
292+ // remove the speed-selected class from the previous selected speed label
293+ mejs . Utils . removeClass ( radio , `${ t . options . classPrefix } speed-selected-input` ) ;
294+ const siblings = mejs . Utils . siblings ( radio , ( el ) => mejs . Utils . hasClass ( el , `${ t . options . classPrefix } speed-selector-label` ) ) ;
295+ for ( let i = 0 , total = siblings . length ; i < total ; i ++ ) {
296+ mejs . Utils . removeClass ( siblings [ i ] , `${ t . options . classPrefix } speed-selected` ) ;
297+ }
298+
299+ // handle the new speed.
300+ if ( radio . checked ) {
301+
302+ mejs . Utils . addClass ( radio , `${ t . options . classPrefix } speed-selected-input` ) ;
303+
304+ // add the speed-selected class to the label
305+ const siblings = mejs . Utils . siblings ( radio , ( el ) => mejs . Utils . hasClass ( el , `${ t . options . classPrefix } speed-selector-label` ) ) ;
306+ for ( let i = 0 , total = siblings . length ; i < total ; i ++ ) {
307+ mejs . Utils . addClass ( siblings [ i ] , `${ t . options . classPrefix } speed-selected` ) ;
308+ }
309+
310+ // set the speed onto the media
311+ const newSpeed = Number ( radio . value )
312+ media . playbackRate = newSpeed
313+ currentPlaybackSpeed = newSpeed
314+ speedButton . innerHTML = getSpeedNameFromValue ( newSpeed )
315+ }
316+ }
317+ }
294318
295319 // gets the speed name from a speed value.
296320 function getSpeedNameFromValue ( speedValue ) {
@@ -316,8 +340,8 @@ Object.assign(MediaElementPlayer.prototype, {
316340 */
317341 cleanspeed ( player ) {
318342 if ( player ) {
319- if ( player . speedButton ) {
320- player . speedButton . parentNode . removeChild ( player . speedButton ) ;
343+ if ( player . speedContainer ) {
344+ player . speedContainer . parentNode . removeChild ( player . speedContainer ) ;
321345 }
322346 if ( player . speedSelector ) {
323347 player . speedSelector . parentNode . removeChild ( player . speedSelector ) ;
0 commit comments