]> 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 14cb8ba43367c52c3dc0b4236c10a95d9bc2c7fe..339420a899bc52cc7366bcf883da9765a5b3b9e2 100644 (file)
@@ -1,36 +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 {
-  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()
@@ -40,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
     }
 
@@ -53,7 +75,7 @@ class SettingsButton extends Button {
 
   onDisposeSettingsItem (event: any, name: string) {
     if (name === undefined) {
-      let children = this.menu.children()
+      const children = this.menu.children()
 
       while (children.length > 0) {
         children[0].dispose()
@@ -62,7 +84,7 @@ class SettingsButton extends Button {
 
       this.addClass('vjs-hidden')
     } else {
-      let item = this.menu.getChild(name)
+      const item = this.menu.getChild(name)
 
       if (item) {
         item.dispose()
@@ -72,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
 
@@ -91,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 () {
@@ -110,31 +144,36 @@ class SettingsButton extends Button {
   }
 
   showDialog () {
-    this.menu.el_.style.opacity = '1'
+    this.player().peertube().onMenuOpen();
+
+    (this.menu.el() as HTMLElement).style.opacity = '1'
     this.dialog.show()
 
     this.setDialogSize(this.getComponentSize(this.menu))
   }
 
   hideDialog () {
+    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
+
+      width = el.offsetWidth
+      height = el.offsetHeight;
 
-      // keep width/height as properties for direct use
-      element.width = width
-      element.height = height
+      (element as any).width = width;
+      (element as any).height = height
     } else {
       width = element.offsetWidth
       height = element.offsetHeight
@@ -148,15 +187,17 @@ class SettingsButton extends Button {
       return
     }
 
-    let offset = this.options_.setup.maxHeightOffset
-    let 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`
@@ -166,7 +207,7 @@ class SettingsButton extends Button {
   buildMenu () {
     this.menu = new Menu(this.player())
     this.menu.addClass('vjs-main-menu')
-    let entries = this.options_.entries
+    const entries = this.settingsButtonOptions.entries
 
     if (entries.length === 0) {
       this.addClass('vjs-hidden')
@@ -174,8 +215,8 @@ class SettingsButton extends Button {
       return
     }
 
-    for (let entry of entries) {
-      this.addMenuItem(entry, this.options_)
+    for (const entry of entries) {
+      this.addMenuItem(entry, this.settingsButtonOptions)
     }
 
     this.panelChild.addChild(this.menu)
@@ -183,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)
-    let 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)
 
@@ -204,8 +247,8 @@ class SettingsButton extends Button {
   }
 
   resetChildren () {
-    for (let menuChild of this.menu.children()) {
-      menuChild.reset()
+    for (const menuChild of this.menu.children()) {
+      (menuChild as SettingsMenuItem).reset()
     }
   }
 
@@ -213,76 +256,17 @@ class SettingsButton extends Button {
    * Hide all the sub menus
    */
   hideChildren () {
-    for (let menuChild of this.menu.children()) {
-      menuChild.hideSubMenu()
+    for (const menuChild of this.menu.children()) {
+      (menuChild as SettingsMenuItem).hideSubMenu()
     }
   }
 
-}
-
-class SettingsPanel extends Component {
-  constructor (player: videojs.Player, options: any) {
-    super(player, options)
-  }
-
-  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
-    })
+  isInIframe () {
+    return window.self !== window.top
   }
 
 }
 
-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 }