diff options
author | Chocobozzz <me@florianbigard.com> | 2019-01-23 15:36:45 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2019-02-11 09:13:02 +0100 |
commit | 2adfc7ea9a1f858db874df9fe322e7ae833db77c (patch) | |
tree | e27c6ebe01b7c96ea0e053839a38fc1f824d1284 /client/src/assets/player/settings-menu-item.ts | |
parent | 7eeb6a0ba4028d0e20847b846332dd0b7747c7f8 (diff) | |
download | PeerTube-2adfc7ea9a1f858db874df9fe322e7ae833db77c.tar.gz PeerTube-2adfc7ea9a1f858db874df9fe322e7ae833db77c.tar.zst PeerTube-2adfc7ea9a1f858db874df9fe322e7ae833db77c.zip |
Refractor videojs player
Add fake p2p-media-loader plugin
Diffstat (limited to 'client/src/assets/player/settings-menu-item.ts')
-rw-r--r-- | client/src/assets/player/settings-menu-item.ts | 332 |
1 files changed, 0 insertions, 332 deletions
diff --git a/client/src/assets/player/settings-menu-item.ts b/client/src/assets/player/settings-menu-item.ts deleted file mode 100644 index 2a3460ae5..000000000 --- a/client/src/assets/player/settings-menu-item.ts +++ /dev/null | |||
@@ -1,332 +0,0 @@ | |||
1 | // Author: Yanko Shterev | ||
2 | // Thanks https://github.com/yshterev/videojs-settings-menu | ||
3 | |||
4 | // FIXME: something weird with our path definition in tsconfig and typings | ||
5 | // @ts-ignore | ||
6 | import * as videojs from 'video.js' | ||
7 | |||
8 | import { toTitleCase } from './utils' | ||
9 | import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' | ||
10 | |||
11 | const MenuItem: VideoJSComponentInterface = videojsUntyped.getComponent('MenuItem') | ||
12 | const component: VideoJSComponentInterface = videojsUntyped.getComponent('Component') | ||
13 | |||
14 | class SettingsMenuItem extends MenuItem { | ||
15 | |||
16 | constructor (player: videojs.Player, options: any, entry: string, menuButton: VideoJSComponentInterface) { | ||
17 | super(player, options) | ||
18 | |||
19 | this.settingsButton = menuButton | ||
20 | this.dialog = this.settingsButton.dialog | ||
21 | this.mainMenu = this.settingsButton.menu | ||
22 | this.panel = this.dialog.getChild('settingsPanel') | ||
23 | this.panelChild = this.panel.getChild('settingsPanelChild') | ||
24 | this.panelChildEl = this.panelChild.el_ | ||
25 | |||
26 | this.size = null | ||
27 | |||
28 | // keep state of what menu type is loading next | ||
29 | this.menuToLoad = 'mainmenu' | ||
30 | |||
31 | const subMenuName = toTitleCase(entry) | ||
32 | const SubMenuComponent = videojsUntyped.getComponent(subMenuName) | ||
33 | |||
34 | if (!SubMenuComponent) { | ||
35 | throw new Error(`Component ${subMenuName} does not exist`) | ||
36 | } | ||
37 | this.subMenu = new SubMenuComponent(this.player(), options, menuButton, this) | ||
38 | const subMenuClass = this.subMenu.buildCSSClass().split(' ')[0] | ||
39 | this.settingsSubMenuEl_.className += ' ' + subMenuClass | ||
40 | |||
41 | this.eventHandlers() | ||
42 | |||
43 | player.ready(() => { | ||
44 | // Voodoo magic for IOS | ||
45 | setTimeout(() => { | ||
46 | this.build() | ||
47 | |||
48 | // Update on rate change | ||
49 | player.on('ratechange', this.submenuClickHandler) | ||
50 | |||
51 | if (subMenuName === 'CaptionsButton') { | ||
52 | // Hack to regenerate captions on HTTP fallback | ||
53 | player.on('captionsChanged', () => { | ||
54 | setTimeout(() => { | ||
55 | this.settingsSubMenuEl_.innerHTML = '' | ||
56 | this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el_) | ||
57 | this.update() | ||
58 | this.bindClickEvents() | ||
59 | |||
60 | }, 0) | ||
61 | }) | ||
62 | } | ||
63 | |||
64 | this.reset() | ||
65 | }, 0) | ||
66 | }) | ||
67 | } | ||
68 | |||
69 | eventHandlers () { | ||
70 | this.submenuClickHandler = this.onSubmenuClick.bind(this) | ||
71 | this.transitionEndHandler = this.onTransitionEnd.bind(this) | ||
72 | } | ||
73 | |||
74 | onSubmenuClick (event: any) { | ||
75 | let target = null | ||
76 | |||
77 | if (event.type === 'tap') { | ||
78 | target = event.target | ||
79 | } else { | ||
80 | target = event.currentTarget | ||
81 | } | ||
82 | |||
83 | if (target && target.classList.contains('vjs-back-button')) { | ||
84 | this.loadMainMenu() | ||
85 | return | ||
86 | } | ||
87 | |||
88 | // To update the sub menu value on click, setTimeout is needed because | ||
89 | // updating the value is not instant | ||
90 | setTimeout(() => this.update(event), 0) | ||
91 | } | ||
92 | |||
93 | /** | ||
94 | * Create the component's DOM element | ||
95 | * | ||
96 | * @return {Element} | ||
97 | * @method createEl | ||
98 | */ | ||
99 | createEl () { | ||
100 | const el = videojsUntyped.dom.createEl('li', { | ||
101 | className: 'vjs-menu-item' | ||
102 | }) | ||
103 | |||
104 | this.settingsSubMenuTitleEl_ = videojsUntyped.dom.createEl('div', { | ||
105 | className: 'vjs-settings-sub-menu-title' | ||
106 | }) | ||
107 | |||
108 | el.appendChild(this.settingsSubMenuTitleEl_) | ||
109 | |||
110 | this.settingsSubMenuValueEl_ = videojsUntyped.dom.createEl('div', { | ||
111 | className: 'vjs-settings-sub-menu-value' | ||
112 | }) | ||
113 | |||
114 | el.appendChild(this.settingsSubMenuValueEl_) | ||
115 | |||
116 | this.settingsSubMenuEl_ = videojsUntyped.dom.createEl('div', { | ||
117 | className: 'vjs-settings-sub-menu' | ||
118 | }) | ||
119 | |||
120 | return el | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Handle click on menu item | ||
125 | * | ||
126 | * @method handleClick | ||
127 | */ | ||
128 | handleClick () { | ||
129 | this.menuToLoad = 'submenu' | ||
130 | // Remove open class to ensure only the open submenu gets this class | ||
131 | videojsUntyped.dom.removeClass(this.el_, 'open') | ||
132 | |||
133 | super.handleClick() | ||
134 | |||
135 | this.mainMenu.el_.style.opacity = '0' | ||
136 | // Whether to add or remove vjs-hidden class on the settingsSubMenuEl element | ||
137 | if (videojsUntyped.dom.hasClass(this.settingsSubMenuEl_, 'vjs-hidden')) { | ||
138 | videojsUntyped.dom.removeClass(this.settingsSubMenuEl_, 'vjs-hidden') | ||
139 | |||
140 | // animation not played without timeout | ||
141 | setTimeout(() => { | ||
142 | this.settingsSubMenuEl_.style.opacity = '1' | ||
143 | this.settingsSubMenuEl_.style.marginRight = '0px' | ||
144 | }, 0) | ||
145 | |||
146 | this.settingsButton.setDialogSize(this.size) | ||
147 | } else { | ||
148 | videojsUntyped.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden') | ||
149 | } | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Create back button | ||
154 | * | ||
155 | * @method createBackButton | ||
156 | */ | ||
157 | createBackButton () { | ||
158 | const button = this.subMenu.menu.addChild('MenuItem', {}, 0) | ||
159 | button.name_ = 'BackButton' | ||
160 | button.addClass('vjs-back-button') | ||
161 | button.el_.innerHTML = this.player_.localize(this.subMenu.controlText_) | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * Add/remove prefixed event listener for CSS Transition | ||
166 | * | ||
167 | * @method PrefixedEvent | ||
168 | */ | ||
169 | PrefixedEvent (element: any, type: any, callback: any, action = 'addEvent') { | ||
170 | let prefix = ['webkit', 'moz', 'MS', 'o', ''] | ||
171 | |||
172 | for (let p = 0; p < prefix.length; p++) { | ||
173 | if (!prefix[p]) { | ||
174 | type = type.toLowerCase() | ||
175 | } | ||
176 | |||
177 | if (action === 'addEvent') { | ||
178 | element.addEventListener(prefix[p] + type, callback, false) | ||
179 | } else if (action === 'removeEvent') { | ||
180 | element.removeEventListener(prefix[p] + type, callback, false) | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | onTransitionEnd (event: any) { | ||
186 | if (event.propertyName !== 'margin-right') { | ||
187 | return | ||
188 | } | ||
189 | |||
190 | if (this.menuToLoad === 'mainmenu') { | ||
191 | // hide submenu | ||
192 | videojsUntyped.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden') | ||
193 | |||
194 | // reset opacity to 0 | ||
195 | this.settingsSubMenuEl_.style.opacity = '0' | ||
196 | } | ||
197 | } | ||
198 | |||
199 | reset () { | ||
200 | videojsUntyped.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden') | ||
201 | this.settingsSubMenuEl_.style.opacity = '0' | ||
202 | this.setMargin() | ||
203 | } | ||
204 | |||
205 | loadMainMenu () { | ||
206 | this.menuToLoad = 'mainmenu' | ||
207 | this.mainMenu.show() | ||
208 | this.mainMenu.el_.style.opacity = '0' | ||
209 | |||
210 | // back button will always take you to main menu, so set dialog sizes | ||
211 | this.settingsButton.setDialogSize([this.mainMenu.width, this.mainMenu.height]) | ||
212 | |||
213 | // animation not triggered without timeout (some async stuff ?!?) | ||
214 | setTimeout(() => { | ||
215 | // animate margin and opacity before hiding the submenu | ||
216 | // this triggers CSS Transition event | ||
217 | this.setMargin() | ||
218 | this.mainMenu.el_.style.opacity = '1' | ||
219 | }, 0) | ||
220 | } | ||
221 | |||
222 | build () { | ||
223 | const saveUpdateLabel = this.subMenu.updateLabel | ||
224 | this.subMenu.updateLabel = () => { | ||
225 | this.update() | ||
226 | |||
227 | saveUpdateLabel.call(this.subMenu) | ||
228 | } | ||
229 | |||
230 | this.settingsSubMenuTitleEl_.innerHTML = this.player_.localize(this.subMenu.controlText_) | ||
231 | this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el_) | ||
232 | this.panelChildEl.appendChild(this.settingsSubMenuEl_) | ||
233 | this.update() | ||
234 | |||
235 | this.createBackButton() | ||
236 | this.getSize() | ||
237 | this.bindClickEvents() | ||
238 | |||
239 | // prefixed event listeners for CSS TransitionEnd | ||
240 | this.PrefixedEvent( | ||
241 | this.settingsSubMenuEl_, | ||
242 | 'TransitionEnd', | ||
243 | this.transitionEndHandler, | ||
244 | 'addEvent' | ||
245 | ) | ||
246 | } | ||
247 | |||
248 | update (event?: any) { | ||
249 | let target: HTMLElement = null | ||
250 | let subMenu = this.subMenu.name() | ||
251 | |||
252 | if (event && event.type === 'tap') { | ||
253 | target = event.target | ||
254 | } else if (event) { | ||
255 | target = event.currentTarget | ||
256 | } | ||
257 | |||
258 | // Playback rate menu button doesn't get a vjs-selected class | ||
259 | // or sets options_['selected'] on the selected playback rate. | ||
260 | // Thus we get the submenu value based on the labelEl of playbackRateMenuButton | ||
261 | if (subMenu === 'PlaybackRateMenuButton') { | ||
262 | setTimeout(() => this.settingsSubMenuValueEl_.innerHTML = this.subMenu.labelEl_.innerHTML, 250) | ||
263 | } else { | ||
264 | // Loop trough the submenu items to find the selected child | ||
265 | for (let subMenuItem of this.subMenu.menu.children_) { | ||
266 | if (!(subMenuItem instanceof component)) { | ||
267 | continue | ||
268 | } | ||
269 | |||
270 | if (subMenuItem.hasClass('vjs-selected')) { | ||
271 | // Prefer to use the function | ||
272 | if (typeof subMenuItem.getLabel === 'function') { | ||
273 | this.settingsSubMenuValueEl_.innerHTML = subMenuItem.getLabel() | ||
274 | break | ||
275 | } | ||
276 | |||
277 | this.settingsSubMenuValueEl_.innerHTML = subMenuItem.options_.label | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | |||
282 | if (target && !target.classList.contains('vjs-back-button')) { | ||
283 | this.settingsButton.hideDialog() | ||
284 | } | ||
285 | } | ||
286 | |||
287 | bindClickEvents () { | ||
288 | for (let item of this.subMenu.menu.children()) { | ||
289 | if (!(item instanceof component)) { | ||
290 | continue | ||
291 | } | ||
292 | item.on(['tap', 'click'], this.submenuClickHandler) | ||
293 | } | ||
294 | } | ||
295 | |||
296 | // save size of submenus on first init | ||
297 | // if number of submenu items change dynamically more logic will be needed | ||
298 | getSize () { | ||
299 | this.dialog.removeClass('vjs-hidden') | ||
300 | this.size = this.settingsButton.getComponentSize(this.settingsSubMenuEl_) | ||
301 | this.setMargin() | ||
302 | this.dialog.addClass('vjs-hidden') | ||
303 | videojsUntyped.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden') | ||
304 | } | ||
305 | |||
306 | setMargin () { | ||
307 | let [width] = this.size | ||
308 | |||
309 | this.settingsSubMenuEl_.style.marginRight = `-${width}px` | ||
310 | } | ||
311 | |||
312 | /** | ||
313 | * Hide the sub menu | ||
314 | */ | ||
315 | hideSubMenu () { | ||
316 | // after removing settings item this.el_ === null | ||
317 | if (!this.el_) { | ||
318 | return | ||
319 | } | ||
320 | |||
321 | if (videojsUntyped.dom.hasClass(this.el_, 'open')) { | ||
322 | videojsUntyped.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden') | ||
323 | videojsUntyped.dom.removeClass(this.el_, 'open') | ||
324 | } | ||
325 | } | ||
326 | |||
327 | } | ||
328 | |||
329 | SettingsMenuItem.prototype.contentElType = 'button' | ||
330 | videojsUntyped.registerComponent('SettingsMenuItem', SettingsMenuItem) | ||
331 | |||
332 | export { SettingsMenuItem } | ||