ssr.js 5.43 KB

/*
* VueVideoPlayer ssr.js
* Author: surmon@foxmail.com
* Github: https://github.com/surmon-china/vue-video-player
*/

// Require sources
import videojs from 'video.js'
import objectAssign from 'object-assign'

// as of videojs 6.6.0
const DEFAULT_EVENTS = [
  'loadeddata',
  'canplay',
  'canplaythrough',
  'play',
  'pause',
  'waiting',
  'playing',
  'ended',
  'error'
]

const videoPlayerDirective = globalOptions => {

  // globalOptions
  globalOptions.events = globalOptions.events || []
  globalOptions.options = globalOptions.options || {}

  // Get videojs instace name in directive
  const getInstanceName = (el, binding, vnode) => {
    let instanceName = null
    if (binding.arg) {
      instanceName = binding.arg
    } else if (vnode.data.attrs && (vnode.data.attrs.instanceName || vnode.data.attrs['instance-name'])) {
      instanceName = (vnode.data.attrs.instanceName || vnode.data.attrs['instance-name'])
    } else if (el.id) {
      instanceName = el.id
    }
    return instanceName || 'player'
  }

  // dom
  const repairDom = el => {
    if (!el.children.length) {
      const video = document.createElement('video')
      video.className = 'video-js'
      el.appendChild(video)
    }
  }

  // init
  const initPlayer = (el, binding, vnode) => {

    const self = vnode.context
    const attrs = vnode.data.attrs || {}
    const options = binding.value || {}
    const instanceName = getInstanceName(el, binding, vnode)
    const customEventName = attrs.customEventName || 'statechanged'
    let player = self[instanceName]

    // options
    const componentEvents = attrs.events || []
    const playsinline = attrs.playsinline || false

    // ios fullscreen
    if (playsinline) {
      el.children[0].setAttribute('playsinline', playsinline)
      el.children[0].setAttribute('webkit-playsinline', playsinline)
      el.children[0].setAttribute('x5-playsinline', playsinline)
      el.children[0].setAttribute('x5-video-player-type', 'h5')
      el.children[0].setAttribute('x5-video-player-fullscreen', false)
    }

    // cross origin
    if (attrs.crossOrigin) {
      el.children[0].crossOrigin = attrs.crossOrigin
      el.children[0].setAttribute('crossOrigin', attrs.crossOrigin)
    }

    // initialize
    if (!player) {

      // videoOptions
      const videoOptions = objectAssign({}, {
        controls: true,
        controlBar: {
          remainingTimeDisplay: false,
          playToggle: {},
          progressControl: {},
          fullscreenToggle: {},
          volumeMenuButton: {
            inline: false,
            vertical: true
          }
        },
        techOrder: ['html5'],
        plugins: {}
      }, globalOptions.options, options)

      // plugins
      if (videoOptions.plugins) {
        delete videoOptions.plugins.__ob__
      }

      // console.log('videoOptions', videoOptions)

      // eventEmit
      const eventEmit = (vnode, name, data) => {
        const handlers = (vnode.data && vnode.data.on) || 
                         (vnode.componentOptions && vnode.componentOptions.listeners)
        if (handlers && handlers[name]) handlers[name].fns(data)
      }

      // emit event
      const emitPlayerState = (event, value) => {
        if (event) {
          eventEmit(vnode, event, player)
        }
        if (value) {
          eventEmit(vnode, customEventName, { [event]: value })
        }
      }
      
      // instance
      player = self[instanceName] = videojs(el.children[0], videoOptions, function() {

        // events
        const events = DEFAULT_EVENTS.concat(componentEvents).concat(globalOptions.events)

        // watch events
        const onEdEvents = {}
        for (let i = 0; i < events.length; i++) {
          if (typeof events[i] === 'string' && onEdEvents[events[i]] === undefined) {
            (event => {
              onEdEvents[event] = null
              this.on(event, () => {
                emitPlayerState(event, true)
              })
            })(events[i])
          }
        }

        // watch timeupdate
        this.on('timeupdate', function() {
          emitPlayerState('timeupdate', this.currentTime())
        })

        // player readied
        emitPlayerState('ready')
      })
    }
  }

  // dispose
  const disposePlayer = (el, binding, vnode) => {
    const self = vnode.context
    const instanceName = getInstanceName(el, binding, vnode)
    const player = self[instanceName]
    if (player && player.dispose) {
      if (player.techName_ !== 'Flash') {
        player.pause && player.pause()
      }
      player.dispose()
      repairDom(el)
      self[instanceName] = null
      delete self[instanceName]
    }
  }

  return {
    inserted: initPlayer,

    // Bind directive
    bind(el, binding, vnode) {
      repairDom(el)
    },
    
    // Parse text model change
    update(el, binding, vnode) {
      const options = binding.value || {}
      disposePlayer(el, binding, vnode)
      if (options && options.sources && options.sources.length) {
          initPlayer(el, binding, vnode)
        }
    },
    
    // Destroy this directive
    unbind: disposePlayer
  }
}

// videoPlayer
const videoPlayer = videoPlayerDirective({})

// Global quill default options
const install = function (Vue, globalOptions = {
  options: {},
  events: []
}) {

  // Mount quill directive for Vue global
  Vue.directive('video-player', videoPlayerDirective(globalOptions))
}

const VueVideoPlayer = { videojs, videoPlayer, install }

export default VueVideoPlayer
export { videojs, videoPlayer, install }