1+ 'use strict' ;
2+
3+ /**
4+ * Speed button
5+ *
6+ * This feature creates a button to speed media in different levels.
7+ */
8+
9+ // Translations (English required)
10+ mejs . i18n . en [ 'mejs.speed-rate' ] = 'Speed Rate' ;
11+
12+ // Feature configuration
13+ Object . assign ( mejs . MepDefaults , {
14+ /**
15+ * The speeds media can be accelerated
16+ *
17+ * Supports an array of float values or objects with format
18+ * [{name: 'Slow', value: '0.75'}, {name: 'Normal', value: '1.00'}, ...]
19+ * @type {{String[]|Object[]} }
20+ */
21+ speeds : [ '2.00' , '1.50' , '1.25' , '1.00' , '0.75' ] ,
22+ /**
23+ * @type {String }
24+ */
25+ defaultSpeed : '1.00' ,
26+ /**
27+ * @type {String }
28+ */
29+ speedChar : 'x' ,
30+ /**
31+ * @type {?String }
32+ */
33+ speedText : null
34+ } ) ;
35+
36+ Object . assign ( MediaElementPlayer . prototype , {
37+
38+ /**
39+ * Feature constructor.
40+ *
41+ * Always has to be prefixed with `build` and the name that will be used in MepDefaults.features list
42+ * @param {MediaElementPlayer } player
43+ * @param {HTMLElement } controls
44+ * @param {HTMLElement } layers
45+ * @param {HTMLElement } media
46+ */
47+ buildspeed ( player , controls , layers , media ) {
48+ const
49+ t = this ,
50+ isNative = t . media . rendererName !== null && / ( n a t i v e | h t m l 5 ) / i. test ( t . media . rendererName )
51+ ;
52+
53+ if ( ! isNative ) {
54+ return ;
55+ }
56+
57+ const
58+ speeds = [ ] ,
59+ speedTitle = mejs . Utils . isString ( t . options . speedText ) ? t . options . speedText : mejs . i18n . t ( 'mejs.speed-rate' ) ,
60+ ;
61+
62+ // define the current speed.
63+ let currentPlaybackSpeed = t . options . defaultSpeed ,
64+ defaultSpeedInSpeedsArray = false
65+
66+ // Convert t.options.speeds into the following format:
67+ // [{name: '2x label', value: 2}, {name: '1.5 label', value: 1.5}]
68+ // And always include the defaultSpeed
69+ {
70+ const length = t . options . speeds . length ;
71+ for ( let i = 0 ; i < length ; i ++ ) {
72+ const speed = t . options . speeds [ i ] ;
73+
74+ if ( typeof speed === 'string' ) {
75+
76+ speeds . push ( {
77+ name : speed + t . options . speedChar ,
78+ value : speed
79+ } ) ;
80+
81+ if ( Number ( speed ) === Number ( t . options . defaultSpeed ) ) {
82+ defaultSpeedInSpeedsArray = true ;
83+ }
84+ }
85+ else {
86+ speeds . push ( speed ) ;
87+ if ( Number ( speed . value ) === Number ( t . options . defaultSpeed ) ) {
88+ defaultInArray = true ;
89+ }
90+ }
91+
92+ }
93+
94+ if ( ! defaultInArray ) {
95+ speeds . push ( {
96+ name : t . options . defaultSpeed + t . options . speedChar ,
97+ value : t . options . defaultSpeed
98+ } ) ;
99+ }
100+
101+ }
102+
103+
104+ // Sort the speeds based on their values
105+ speeds . sort ( ( a , b ) => {
106+ return Number ( b . value ) - Number ( a . value ) ;
107+ } ) ;
108+
109+
110+ // Before doing any DOM work, clean up.
111+ t . cleanspeed ( player ) ;
112+
113+
114+ // Build up the main button and the <div><ul></ul></div> next to it within the player bar.
115+ {
116+ player . speedButton = document . createElement ( 'div' ) ;
117+ player . speedButton . className = `${ t . options . classPrefix } button ${ t . options . classPrefix } speed-button` ;
118+
119+ player . speedButton . innerHTML =
120+ '<button ' +
121+ 'type="button" ' +
122+ `aria-controls="${ t . id } " ` +
123+ `title="${ speedTitle } " ` +
124+ `aria-label="${ speedTitle } " ` +
125+ 'tabindex="0"' +
126+ '>' +
127+ getSpeedNameFromValue ( t . options . defaultSpeed ) +
128+ '</button>' +
129+ `<div class="${ t . options . classPrefix } speed-selector ${ t . options . classPrefix } offscreen">` +
130+ `<ul class="${ t . options . classPrefix } speed-selector-list"></ul>` +
131+ '</div>' ;
132+
133+ // Add this button into the DOM
134+ t . addControlElement ( player . speedButton , 'speed' ) ;
135+
136+
137+ // Add speed li items.
138+ let ulHTML = ''
139+ const length = speeds . length ;
140+ for ( let i = 0 ; i < length ; i ++ ) {
141+ const speed = speeds [ i ]
142+
143+ const inputId = `${ t . id } -speed-${ speeds [ i ] . value } ` ;
144+
145+ const speedIsDefaultSpeed = Number ( speed . value ) === Number ( t . options . defaultSpeed )
146+
147+ const liHTML =
148+ `<li class="${ t . options . classPrefix } speed-selector-list-item">` +
149+ `<input ` +
150+ `class="${ t . options . classPrefix } speed-selector-input" ` +
151+ `type="radio" ` +
152+ `name="${ t . id } _speed"` +
153+ `disabled="disabled" ` +
154+ `value="${ speed . value } " ` +
155+ `id="${ inputId } "` +
156+ `${ ( speedIsDefaultSpeed ? ' checked="checked"' : '' ) } ` +
157+ '/>' +
158+ '<label ' +
159+ `for="${ inputId } " ` +
160+ `class="${ t . options . classPrefix } speed-selector-label` +
161+ `${ ( speedIsDefaultSpeed ? ` ${ t . options . classPrefix } speed-selected` : '' ) } "` +
162+ '>' +
163+ `${ speed . name } ` +
164+ '</label>' +
165+ '</li>' ;
166+
167+ }
168+
169+ player . speedButton . querySelector ( 'ul' ) . innerHTML = ulHTML
170+ }
171+
172+ player . speedSelector = player . speedButton . querySelector ( `.${ t . options . classPrefix } speed-selector` )
173+
174+ // Enable inputs after they have been appended to controls to avoid tab and up/down arrow focus issues
175+ const
176+ radios = player . speedButton . querySelectorAll ( 'input[type="radio"]' ) ,
177+ labels = player . speedButton . querySelectorAll ( `.${ t . options . classPrefix } speed-selector-label` )
178+ ;
179+
180+
181+ // Handle the events.
182+ let menuIsHidden = true ;
183+
184+ function showMenu ( ) {
185+ mejs . Utils . removeClass ( player . speedSelector , `${ t . options . classPrefix } offscreen` ) ;
186+ player . speedSelector . style . height = player . speedSelector . querySelector ( 'ul' ) . offsetHeight ;
187+ player . speedSelector . style . top = `${ ( - 1 * Number ( player . speedSelector . offsetHeight ) ) } px` ;
188+
189+ menuIsHidden = false ;
190+ }
191+
192+ function hideMenu ( ) {
193+ mejs . Utils . addClass ( this , `${ t . options . classPrefix } offscreen` ) ;
194+
195+ menuIsHidden = true ;
196+ }
197+
198+ function hideShowMenu ( ) {
199+ // Ideally, we check for the ${t.options.classPrefix}offscreen class, and if it's not there, it should be visible.
200+ if ( menuIsHidden === true ) {
201+ showMenu ( ) ;
202+ } else {
203+ hideMenu ( ) ;
204+ }
205+ }
206+
207+ player . speedButton . addEventListener ( 'mouseenter' , showMenu ) ;
208+ player . speedButton . addEventListener ( 'focusin' , showMenu ) ;
209+
210+ player . speedButton . addEventListener ( 'mouseleave' , hideMenu ) ;
211+ player . speedButton . addEventListener ( 'focusout' , hideMenu ) ;
212+
213+ player . speedButton . addEventListener ( 'click' , hideShowMenu ) ;
214+
215+
216+
217+
218+
219+ // Code stolen from quality.js
220+ for ( let i = 0 , total = radios . length ; i < total ; i ++ ) {
221+ const radio = radios [ i ] ;
222+ radio . disabled = false ;
223+ radio . addEventListener ( 'change' , function ( ) {
224+ debugger
225+ // t.changeSpeed(this, player, )
226+ } ) ;
227+ }
228+
229+
230+ for ( let i = 0 , total = labels . length ; i < total ; i ++ ) {
231+ labels [ i ] . addEventListener ( 'click' , function ( ) {
232+ const
233+ radio = mejs . Utils . siblings ( this , ( el ) => el . tagName === 'INPUT' ) [ 0 ] ,
234+ event = mejs . Utils . createEvent ( 'click' , radio )
235+ ;
236+ radio . dispatchEvent ( event ) ;
237+ } ) ;
238+ }
239+
240+
241+
242+
243+
244+
245+
246+ // gets the speed name from a speed value.
247+ function getSpeedNameFromValue ( speedValue ) {
248+ const numSpeedValue = Number ( speedValue )
249+ const length = speeds . length ;
250+ for ( let i = 0 ; i < length ; i ++ ) {
251+ const speed = speeds [ i ]
252+ if ( Number ( speed . value ) === numSpeedValue ) {
253+ return speed . name ;
254+ }
255+ }
256+
257+ return speedValue
258+ }
259+ } ,
260+
261+
262+ changeSpeed ( self , player ) {
263+
264+
265+
266+ } ,
267+
268+
269+ /**
270+ * Feature destructor.
271+ *
272+ * Always has to be prefixed with `clean` and the name that was used in MepDefaults.features list
273+ * @param {MediaElementPlayer } player
274+ */
275+ cleanspeed ( player ) {
276+ if ( player ) {
277+ if ( player . speedButton ) {
278+ player . speedButton . parentNode . removeChild ( player . speedButton ) ;
279+ }
280+ if ( player . speedSelector ) {
281+ player . speedSelector . parentNode . removeChild ( player . speedSelector ) ;
282+ }
283+ }
284+ }
285+
286+ } ) ;
0 commit comments