]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - client/src/assets/player/videojs-components/settings-menu-button.ts
Fix console error when watching a video
[github/Chocobozzz/PeerTube.git] / client / src / assets / player / videojs-components / settings-menu-button.ts
index b700f4be6a23a5e20a185784274cec32aedb4042..339420a899bc52cc7366bcf883da9765a5b3b9e2 100644 (file)
@@ -1,48 +1,57 @@
-// Author: Yanko Shterev
-// Thanks https://github.com/yshterev/videojs-settings-menu
-
-// FIXME: something weird with our path definition in tsconfig and typings
-// @ts-ignore
-import * as videojs from 'video.js'
-
+// Thanks to Yanko Shterev: https://github.com/yshterev/videojs-settings-menu
 import { SettingsMenuItem } from './settings-menu-item'
-import { VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings'
 import { toTitleCase } from '../utils'
+import videojs from 'video.js'
+
+import { SettingsDialog } from './settings-dialog'
+import { SettingsPanel } from './settings-panel'
+import { SettingsPanelChild } from './settings-panel-child'
 
-const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button')
-const Menu: VideoJSComponentInterface = videojsUntyped.getComponent('Menu')
-const Component: VideoJSComponentInterface = videojsUntyped.getComponent('Component')
+const Button = videojs.getComponent('Button')
+const Menu = videojs.getComponent('Menu')
+const Component = videojs.getComponent('Component')
+
+export interface SettingsButtonOptions extends videojs.ComponentOptions {
+  entries: any[]
+  setup?: {
+    maxHeightOffset: number
+  }
+}
 
 class SettingsButton extends Button {
-  playerComponent = videojs.Player
-  dialog: any
-  dialogEl: any
-  menu: any
-  panel: any
-  panelChild: any
-
-  addSettingsItemHandler: Function
-  disposeSettingsItemHandler: Function
-  playerClickHandler: Function
-  userInactiveHandler: Function
-
-  constructor (player: videojs.Player, options: any) {
+  dialog: SettingsDialog
+  dialogEl: HTMLElement
+  menu: videojs.Menu
+  panel: SettingsPanel
+  panelChild: SettingsPanelChild
+
+  addSettingsItemHandler: typeof SettingsButton.prototype.onAddSettingsItem
+  disposeSettingsItemHandler: typeof SettingsButton.prototype.onDisposeSettingsItem
+  documentClickHandler: typeof SettingsButton.prototype.onDocumentClick
+  userInactiveHandler: typeof SettingsButton.prototype.onUserInactive
+
+  private settingsButtonOptions: SettingsButtonOptions
+
+  constructor (player: videojs.Player, options?: SettingsButtonOptions) {
     super(player, options)
 
-    this.playerComponent = player
-    this.dialog = this.playerComponent.addChild('settingsDialog')
-    this.dialogEl = this.dialog.el_
+    this.settingsButtonOptions = options
+
+    this.controlText('Settings')
+
+    this.dialog = this.player().addChild('settingsDialog')
+    this.dialogEl = this.dialog.el() as HTMLElement
     this.menu = null
     this.panel = this.dialog.addChild('settingsPanel')
     this.panelChild = this.panel.addChild('settingsPanelChild')
 
     this.addClass('vjs-settings')
-    this.el_.setAttribute('aria-label', 'Settings Button')
+    this.el().setAttribute('aria-label', 'Settings Button')
 
     // Event handlers
     this.addSettingsItemHandler = this.onAddSettingsItem.bind(this)
     this.disposeSettingsItemHandler = this.onDisposeSettingsItem.bind(this)
-    this.playerClickHandler = this.onPlayerClick.bind(this)
+    this.documentClickHandler = this.onDocumentClick.bind(this)
     this.userInactiveHandler = this.onUserInactive.bind(this)
 
     this.buildMenu()
@@ -52,9 +61,10 @@ class SettingsButton extends Button {
     this.player().one('play', () => this.hideDialog())
   }
 
-  onPlayerClick (event: MouseEvent) {
+  onDocumentClick (event: MouseEvent) {
     const element = event.target as HTMLElement
-    if (element.classList.contains('vjs-settings') || element.parentElement.classList.contains('vjs-settings')) {
+
+    if (element?.classList?.contains('vjs-settings') || element?.parentElement?.classList?.contains('vjs-settings')) {
       return
     }
 
@@ -84,11 +94,19 @@ class SettingsButton extends Button {
 
     this.hideDialog()
 
-    if (this.options_.entries.length === 0) {
+    if (this.settingsButtonOptions.entries.length === 0) {
       this.addClass('vjs-hidden')
     }
   }
 
+  dispose () {
+    document.removeEventListener('click', this.documentClickHandler)
+
+    if (this.isInIframe()) {
+      window.removeEventListener('blur', this.documentClickHandler)
+    }
+  }
+
   onAddSettingsItem (event: any, data: any) {
     const [ entry, options ] = data
 
@@ -103,10 +121,14 @@ class SettingsButton extends Button {
   }
 
   bindEvents () {
-    this.playerComponent.on('click', this.playerClickHandler)
-    this.playerComponent.on('addsettingsitem', this.addSettingsItemHandler)
-    this.playerComponent.on('disposesettingsitem', this.disposeSettingsItemHandler)
-    this.playerComponent.on('userinactive', this.userInactiveHandler)
+    document.addEventListener('click', this.documentClickHandler)
+    if (this.isInIframe()) {
+      window.addEventListener('blur', this.documentClickHandler)
+    }
+
+    this.player().on('addsettingsitem', this.addSettingsItemHandler)
+    this.player().on('disposesettingsitem', this.disposeSettingsItemHandler)
+    this.player().on('userinactive', this.userInactiveHandler)
   }
 
   buildCSSClass () {
@@ -122,9 +144,9 @@ class SettingsButton extends Button {
   }
 
   showDialog () {
-    this.player_.peertube().onMenuOpen()
+    this.player().peertube().onMenuOpen();
 
-    this.menu.el_.style.opacity = '1'
+    (this.menu.el() as HTMLElement).style.opacity = '1'
     this.dialog.show()
 
     this.setDialogSize(this.getComponentSize(this.menu))
@@ -134,23 +156,24 @@ class SettingsButton extends Button {
     this.player_.peertube().onMenuClosed()
 
     this.dialog.hide()
-    this.setDialogSize(this.getComponentSize(this.menu))
-    this.menu.el_.style.opacity = '1'
+    this.setDialogSize(this.getComponentSize(this.menu));
+    (this.menu.el() as HTMLElement).style.opacity = '1'
     this.resetChildren()
   }
 
-  getComponentSize (element: any) {
+  getComponentSize (element: videojs.Component | HTMLElement) {
     let width: number = null
     let height: number = null
 
     // Could be component or just DOM element
     if (element instanceof Component) {
-      width = element.el_.offsetWidth
-      height = element.el_.offsetHeight
+      const el = element.el() as HTMLElement
 
-      // keep width/height as properties for direct use
-      element.width = width
-      element.height = height
+      width = el.offsetWidth
+      height = el.offsetHeight;
+
+      (element as any).width = width;
+      (element as any).height = height
     } else {
       width = element.offsetWidth
       height = element.offsetHeight
@@ -164,15 +187,17 @@ class SettingsButton extends Button {
       return
     }
 
-    const offset = this.options_.setup.maxHeightOffset
-    const maxHeight = this.playerComponent.el_.offsetHeight - offset
+    const offset = this.settingsButtonOptions.setup.maxHeightOffset
+    const maxHeight = (this.player().el() as HTMLElement).offsetHeight - offset // FIXME: typings
+
+    const panelEl = this.panel.el() as HTMLElement
 
     if (height > maxHeight) {
       height = maxHeight
       width += 17
-      this.panel.el_.style.maxHeight = `${height}px`
-    } else if (this.panel.el_.style.maxHeight !== '') {
-      this.panel.el_.style.maxHeight = ''
+      panelEl.style.maxHeight = `${height}px`
+    } else if (panelEl.style.maxHeight !== '') {
+      panelEl.style.maxHeight = ''
     }
 
     this.dialogEl.style.width = `${width}px`
@@ -182,7 +207,7 @@ class SettingsButton extends Button {
   buildMenu () {
     this.menu = new Menu(this.player())
     this.menu.addClass('vjs-main-menu')
-    const entries = this.options_.entries
+    const entries = this.settingsButtonOptions.entries
 
     if (entries.length === 0) {
       this.addClass('vjs-hidden')
@@ -191,7 +216,7 @@ class SettingsButton extends Button {
     }
 
     for (const entry of entries) {
-      this.addMenuItem(entry, this.options_)
+      this.addMenuItem(entry, this.settingsButtonOptions)
     }
 
     this.panelChild.addChild(this.menu)
@@ -199,15 +224,17 @@ class SettingsButton extends Button {
 
   addMenuItem (entry: any, options: any) {
     const openSubMenu = function (this: any) {
-      if (videojsUntyped.dom.hasClass(this.el_, 'open')) {
-        videojsUntyped.dom.removeClass(this.el_, 'open')
+      if (videojs.dom.hasClass(this.el_, 'open')) {
+        videojs.dom.removeClass(this.el_, 'open')
       } else {
-        videojsUntyped.dom.addClass(this.el_, 'open')
+        videojs.dom.addClass(this.el_, 'open')
       }
     }
 
     options.name = toTitleCase(entry)
-    const settingsMenuItem = new SettingsMenuItem(this.player(), options, entry, this as any)
+
+    const newOptions = Object.assign({}, options, { entry, menuButton: this })
+    const settingsMenuItem = new SettingsMenuItem(this.player(), newOptions)
 
     this.menu.addChild(settingsMenuItem)
 
@@ -221,7 +248,7 @@ class SettingsButton extends Button {
 
   resetChildren () {
     for (const menuChild of this.menu.children()) {
-      menuChild.reset()
+      (menuChild as SettingsMenuItem).reset()
     }
   }
 
@@ -230,75 +257,16 @@ class SettingsButton extends Button {
    */
   hideChildren () {
     for (const menuChild of this.menu.children()) {
-      menuChild.hideSubMenu()
+      (menuChild as SettingsMenuItem).hideSubMenu()
     }
   }
 
-}
-
-class SettingsPanel extends Component {
-  constructor (player: videojs.Player, options: any) {
-    super(player, options)
+  isInIframe () {
+    return window.self !== window.top
   }
 
-  createEl () {
-    return super.createEl('div', {
-      className: 'vjs-settings-panel',
-      innerHTML: '',
-      tabIndex: -1
-    })
-  }
-}
-
-class SettingsPanelChild extends Component {
-  constructor (player: videojs.Player, options: any) {
-    super(player, options)
-  }
-
-  createEl () {
-    return super.createEl('div', {
-      className: 'vjs-settings-panel-child',
-      innerHTML: '',
-      tabIndex: -1
-    })
-  }
 }
 
-class SettingsDialog extends Component {
-  constructor (player: videojs.Player, options: any) {
-    super(player, options)
-    this.hide()
-  }
-
-  /**
-   * Create the component's DOM element
-   *
-   * @return {Element}
-   * @method createEl
-   */
-  createEl () {
-    const uniqueId = this.id_
-    const dialogLabelId = 'TTsettingsDialogLabel-' + uniqueId
-    const dialogDescriptionId = 'TTsettingsDialogDescription-' + uniqueId
-
-    return super.createEl('div', {
-      className: 'vjs-settings-dialog vjs-modal-overlay',
-      innerHTML: '',
-      tabIndex: -1
-    }, {
-      'role': 'dialog',
-      'aria-labelledby': dialogLabelId,
-      'aria-describedby': dialogDescriptionId
-    })
-  }
-
-}
-
-SettingsButton.prototype.controlText_ = 'Settings'
-
 Component.registerComponent('SettingsButton', SettingsButton)
-Component.registerComponent('SettingsDialog', SettingsDialog)
-Component.registerComponent('SettingsPanel', SettingsPanel)
-Component.registerComponent('SettingsPanelChild', SettingsPanelChild)
 
-export { SettingsButton, SettingsDialog, SettingsPanel, SettingsPanelChild }
+export { SettingsButton }