<!DOCTYPE html><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>SeekBar</title><link href="https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700" rel="stylesheet" type="text/css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/styles/atelier-forest.light.min.css"/><link rel="stylesheet" type="text/css" href="css/api-docs.css"/><link rel="canonical" href="https://docs.videojs.com/SeekBar.html">

This documentation is for an outdated version of Video.js. See documentation for the current release.

API Index

SeekBar Methods

Class Methods

createEl

getPercent

handleMouseDown

handleMouseMove

handleMouseUp

stepBack

stepForward

updateARIAAttributes

Inherited Methods from Slider

calculateDistance

handleBlur

handleClick

handleFocus

handleKeyPress

update

vertical

Inherited Methods from Component

$

$$

addChild

addClass

buildCSSClass

children

clearInterval

clearTimeout

contentEl

dimensions

dispose

el

enableTouchActivity

extend

getChild

getChildById

getComponent

hasClass

height

hide

id

initChildren

name

off

on

one

options

player

ready

registerComponent

removeChild

removeClass

setInterval

setTimeout

show

toggleClass

trigger

triggerReady

width

SeekBar

Seek Bar and holder for the progress bars

DEFINED IN: seek-bar.js line number: 12

EXTENDS: slider.js

Constructor

SeekBar( player,[options] )

Parameters

name	Type	Required	Description

player	Player|Object

options	Object

Methods

createEl()

Create the component's DOM element

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/progress-control/seek-bar.js line number: 28

[back to top]

getPercent()

Get percentage of video played

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/progress-control/seek-bar.js line number: 54

[back to top]

handleMouseDown()

Handle mouse down on seek bar

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/progress-control/seek-bar.js line number: 65

[back to top]

handleMouseMove()

Handle mouse move on seek bar

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/progress-control/seek-bar.js line number: 79

[back to top]

handleMouseUp()

Handle mouse up on seek bar

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/progress-control/seek-bar.js line number: 94

[back to top]

stepBack()

Move more quickly rewind for keyboard-only users

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/progress-control/seek-bar.js line number: 117

[back to top]

stepForward()

Move more quickly fast forward for keyboard-only users

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/progress-control/seek-bar.js line number: 108

[back to top]

updateARIAAttributes()

Update ARIA accessibility attributes

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/progress-control/seek-bar.js line number: 42

[back to top]

calculateDistance( event )

Calculate distance for slider

Parameters

name	Type	Required	Description

event	Object	yes	Event object

Defined in https://github.com/videojs/video.js/blob/master/src/js/slider/slider.js line number: 149

[back to top]

handleBlur()

Handle on blur for slider

Defined in https://github.com/videojs/video.js/blob/master/src/js/slider/slider.js line number: 188

[back to top]

handleClick( event )

Listener for click events on slider, used to prevent clicks
  from bubbling up to parent elements like button menus.

Parameters

name	Type	Required	Description

event	Object	yes	Event object

Defined in https://github.com/videojs/video.js/blob/master/src/js/slider/slider.js line number: 197

[back to top]

handleFocus()

Handle on focus for slider

Defined in https://github.com/videojs/video.js/blob/master/src/js/slider/slider.js line number: 163

[back to top]

handleKeyPress( event )

Handle key press for slider

Parameters

name	Type	Required	Description

event	Object	yes	Event object

Defined in https://github.com/videojs/video.js/blob/master/src/js/slider/slider.js line number: 172

[back to top]

update()

Update slider

Defined in https://github.com/videojs/video.js/blob/master/src/js/slider/slider.js line number: 111

[back to top]

vertical( bool )

Get/set if slider is horizontal for vertical

Parameters

name	Type	Required	Description

bool	Boolean	yes	True if slider is vertical, false is horizontal

Defined in https://github.com/videojs/video.js/blob/master/src/js/slider/slider.js line number: 209

[back to top]

$( selector, [context] )

Finds a single DOM element matching selector within the component's
contentEl or another custom context.

Parameters

name	Type	Required	Description

selector	String	yes	A valid CSS selector, which will be passed to querySelector.

context	Element|String	no	A DOM element within which to query. Can also be a selector
        string in which case the first matching element will be used
        as context. If missing (or no element matches selector), falls
        back to document.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 841

[back to top]

$$( selector, [context] )

Finds a all DOM elements matching selector within the component's
contentEl or another custom context.

Parameters

name	Type	Required	Description

selector	String	yes	A valid CSS selector, which will be passed to querySelectorAll.

context	Element|String	no	A DOM element within which to query. Can also be a selector
        string in which case the first matching element will be used
        as context. If missing (or no element matches selector), falls
        back to document.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 861

[back to top]

addChild( child, [options] )

Adds a child component inside this component

    myComponent.el();
    // ->

    myComponent.children();
    // [empty array]

    var myButton = myComponent.addChild('MyButton');
    // ->

myButton

    // -> myButton === myComponent.children()[0];

Pass in options for child constructors and options for children of the child

    var myButton = myComponent.addChild('MyButton', {
      text: 'Press Me',
      buttonChildExample: {
        buttonChildOption: true
      }
    });

Parameters

name	Type	Required	Description

child	String|Component	yes	The class name or instance of a child to add

options	Object	no	Options, including options to be passed to children of the child.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 315

[back to top]

addClass( classToAdd )

Add a CSS class name to the component's element

Parameters

name	Type	Required	Description

classToAdd	String	yes	Classname to add

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 892

[back to top]

buildCSSClass()

Allows sub components to stack CSS class names

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 593

[back to top]

children()

Get an array of all child components

    var kids = myComponent.children();

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 282

[back to top]

clearInterval( intervalId )

Clears an interval and removes the associated dispose listener

Parameters

name	Type	Required	Description

intervalId	Number	yes	The id of the interval to clear

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1295

[back to top]

clearTimeout( timeoutId )

Clears a timeout and removes the associated dispose listener

Parameters

name	Type	Required	Description

timeoutId	Number	yes	The id of the timeout to clear

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1252

[back to top]

contentEl()

Return the component's DOM element where children are inserted.
Will either be the same as el() or a new element defined in createEl().

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 245

[back to top]

dimensions( width, height )

Set both width and height at the same time

Parameters

name	Type	Required	Description

width	Number|String	yes	Width of player

height	Number|String	yes	Height of player

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1015

[back to top]

dispose()

Dispose of the component and all child components

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 101

[back to top]

el()

Get the component's DOM element

    var domEl = myComponent.el();

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 195

[back to top]

enableTouchActivity()

Report user touch activity when touch events occur
User activity is used to determine when controls should show/hide. It's
relatively simple when it comes to mouse events, because any mouse event
should show the controls. So we capture mouse events that bubble up to the
player and report activity when that happens.
With touch events it isn't as easy. We can't rely on touch events at the
player level, because a tap (touchstart + touchend) on the video itself on
mobile devices is meant to turn controls off (and on). User activity is
checked asynchronously, so what could happen is a tap event on the video
turns the controls off, then the touchend event bubbles up to the player,
which if it reported user activity, would turn the controls right back on.
(We also don't want to completely block touch events from bubbling up)
Also a touchmove, touch+hold, and anything other than a tap is not supposed
to turn the controls back on on a mobile device.
Here we're setting the default component behavior to report user activity
whenever touch events happen, and this can be turned off by components that
want touch events to act differently.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1174

[back to top]

static extend( props ) (deprecated)

Sets up the constructor using the supplied init method
or uses the init of the parent object

Parameters

name	Type	Required	Description

props	Object	yes	An object of properties

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1350

[back to top]

getChild()

Returns a child component with the provided name

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 305

[back to top]

getChildById()

Returns a child component with the provided ID

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 295

[back to top]

static getComponent( name )

Gets a component by name

Parameters

name	Type	Required	Description

name	String	yes	Name of the component to get

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1331

[back to top]

hasClass( classToCheck )

Check if a component's element has a CSS class name

Parameters

name	Type	Required	Description

classToCheck	String	yes	Classname to check

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 881

[back to top]

height( num, [skipListeners] )

Get or set the height of the component (CSS values)
Setting the video tag dimension values only works with values in pixels.
Percent values will not work.
Some percents can be used, but width()/height() will return the number + %,
not the actual computed width/height.

Parameters

name	Type	Required	Description

num	Number|String	yes	New component height

skipListeners	Boolean	no	Skip the resize event trigger

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 998

[back to top]

hide()

Hide the component element if currently showing

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 944

[back to top]

id()

Get the component's ID

    var id = myComponent.id();

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 256

[back to top]

initChildren()

Add and initialize default child components from options

    // when an instance of MyComponent is created, all children in options
    // will be added to the instance by their name strings and options
    MyComponent.prototype.options_ = {
      children: [
        'myChildComponent'
      ],
      myChildComponent: {
        myChildOption: true
      }
    };

    // Or when creating the component
    var myComp = new MyComponent(player, {
      children: [
        'myChildComponent'
      ],
      myChildComponent: {
        myChildOption: true
      }
    });

The children option can also be an array of
child options objects (that also include a 'name' key).
This can be used if you have two child components of the
same type that need different options.

    var myComp = new MyComponent(player, {
      children: [
        'button',
        {
          name: 'button',
          someOtherOption: true
        },
        {
          name: 'button',
          someOtherOption: false
        }
      ]
    });

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 455

[back to top]

name()

Get the component's name. The name is often used to reference the component.

    var name = myComponent.name();

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 269

[back to top]

off( first, second, [third] )

Remove an event listener from this component's element

    myComponent.off('eventType', myFunc);

If myFunc is excluded, ALL listeners for the event type will be removed.
If eventType is excluded, ALL listeners will be removed from the component.
Alternatively you can use off to remove listeners that were added to other
elements or components using myComponent.on(otherComponent....
In this case both the event type and listener function are REQUIRED.

    myComponent.off(otherElement, 'eventType', myFunc);
    myComponent.off(otherComponent, 'eventType', myFunc);

Parameters

name	Type	Required	Description

first	String|Component	yes	The event type or other component

second	function|String	yes	The listener function or event type

third	function	no	The listener for other component

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 680

[back to top]

on( first, second, third )

Add an event listener to this component's element

    var myFunc = function(){
      var myComponent = this;
      // Do something when the event is fired
    };

    myComponent.on('eventType', myFunc);

The context of myFunc will be myComponent unless previously bound.
Alternatively, you can add a listener to another element or component.

    myComponent.on(otherElement, 'eventName', myFunc);
    myComponent.on(otherComponent, 'eventName', myFunc);

The benefit of using this over VjsEvents.on(otherElement, 'eventName', myFunc)
and otherComponent.on('eventName', myFunc) is that this way the listeners
will be automatically cleaned up when either component is disposed.
It will also bind myComponent as the context of myFunc.
NOTE: When using this on elements in the page other than window
and document (both permanent), if you remove the element from the DOM
you need to call myComponent.trigger(el, 'dispose') on it to clean up
references to it and allow the browser to garbage collect it.

Parameters

name	Type	Required	Description

first	String|Component	yes	The event type or other component

second	function|String	yes	The event handler or event type

third	function	yes	The event handler

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 605

[back to top]

one( first, second, [third] )

Add an event listener to be triggered only once and then removed

    myComponent.one('eventName', myFunc);

Alternatively you can add a listener to another element or component
that will be triggered only once.

    myComponent.one(otherElement, 'eventName', myFunc);
    myComponent.one(otherComponent, 'eventName', myFunc);

Parameters

name	Type	Required	Description

first	String|Component	yes	The event type or other component

second	function|String	yes	The listener function or event type

third	function	no	The listener function for other component

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 728

[back to top]

options( obj )

Deep merge of options objects
Whenever a property is an object on both options objects
the two properties will be merged using mergeOptions.

    Parent.prototype.options_ = {
      optionSet: {
        'childOne': { 'foo': 'bar', 'asdf': 'fdsa' },
        'childTwo': {},
        'childThree': {}
      }
    }
    newOptions = {
      optionSet: {
        'childOne': { 'foo': 'baz', 'abc': '123' }
        'childTwo': null,
        'childFour': {}
      }
    }

    this.options(newOptions);

RESULT

    {
      optionSet: {
        'childOne': { 'foo': 'baz', 'asdf': 'fdsa', 'abc': '123' },
        'childTwo': null, // Disabled. Won't be initialized.
        'childThree': {},
        'childFour': {}
      }
    }

Parameters

name	Type	Required	Description

obj	Object	yes	Object of new option values

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 145

[back to top]

player()

Return the component's player

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 135

[back to top]

ready( fn, sync )

Bind a listener to the component's ready state.
Different from event listeners in that if the ready event has already happened
it will trigger the function immediately.

Parameters

name	Type	Required	Description

fn	function	yes	Ready listener

sync	Boolean	yes	Exec the listener synchronously if component is ready

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 787

[back to top]

static registerComponent( name, comp )

Registers a component

Parameters

name	Type	Required	Description

name	String	yes	Name of the component to register

comp	Object	yes	The component to register

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1314

[back to top]

removeChild( component )

Remove a child component from this component's list of children, and the
child component's element from this component's element

Parameters

name	Type	Required	Description

component	Component	yes	Component to remove

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 415

[back to top]

removeClass( classToRemove )

Remove a CSS class name from the component's element

Parameters

name	Type	Required	Description

classToRemove	String	yes	Classname to remove

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 904

[back to top]

setInterval( fn, interval )

Creates an interval and sets up disposal automatically.

Parameters

name	Type	Required	Description

fn	function	yes	The function to run every N seconds.

interval	Number	yes	Number of ms to delay before executing specified function.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1271

[back to top]

setTimeout( fn, timeout )

Creates timeout and sets up disposal automatically.

Parameters

name	Type	Required	Description

fn	function	yes	The function to run after the timeout.

timeout	Number	yes	Number of ms to delay before executing specified function.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1227

[back to top]

show()

Show the component element if hidden

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 933

[back to top]

toggleClass( classToToggle, [predicate] )

Add or remove a CSS class name from the component's element

Parameters

name	Type	Required	Description

classToToggle	String

predicate	Boolean|function	no	Can be a function that returns a Boolean. If true, the class
        will be added; if false, the class will be removed. If not
        given, the class will be added if not present and vice versa.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 916

[back to top]

trigger( event, [hash] )

Trigger an event on an element

    myComponent.trigger('eventName');
    myComponent.trigger({'type':'eventName'});
    myComponent.trigger('eventName', {data: 'some data'});
    myComponent.trigger({'type':'eventName'}, {data: 'some data'});

Parameters

name	Type	Required	Description

event	Event|Object|String	yes	A string (the type) or an event object with a type attribute

hash	Object	no	data hash to pass along with the event

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 768

[back to top]

triggerReady()

Trigger the ready listeners

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 814

[back to top]

width( num, skipListeners )

Set or get the width of the component (CSS values)
Setting the video tag dimension values only works with values in pixels.
Percent values will not work.
Some percents can be used, but width()/height() will return the number + %,
not the actual computed width/height.

Parameters

name	Type	Required	Description

num	Number|String	yes	Optional width number

skipListeners	Boolean	yes	Skip the 'resize' event trigger

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 981

[back to top]


* @param {boolean} mouseDown this is a flag that should be set to true if `handleMouseMove` is called directly. It allows us to skip things that should not happen if coming from mouse down but should happen on regular mouse move handler. Defaults to false

handleMouseMove(event, mouseDown = false) {

if (!mouseDown && !this.player_.scrubbing()) {

keep keep keep keep keep replace keep keep keep keep keep

if (!Dom.isSingleLeftClick(event) || isNaN(this.player_.duration())) {

<!DOCTYPE html>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link rel="stylesheet" type="text/css" href="../css/guides.css">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700,700italic,600,600italic' rel='stylesheet' type='text/css'><link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.8.0/styles/solarized_light.min.css" />

<link rel="canonical" href="https://docs.videojs.com/tutorial-options.html">

This documentation is for an outdated version of Video.js. See documentation for the current release.

Options

Setting Options

The Video.js embed code is simply an HTML5 video tag, so for many of the options you can use the standard tag attributes to set the options.

<video controls autoplay preload="auto" ...>

Alternatively, you can use the data-setup attribute to provide options in the JSON format. This is also how you would set options that aren't standard to the video tag.

<video data-setup='{ "controls": true, "autoplay": false, "preload": "auto" }'...>

Finally, if you're not using the data-setup attribute to trigger the player setup, you can pass in an object with the player options as the second argument in the javascript setup function.

videojs("example_video_1", { "controls": true, "autoplay": false, "preload": "auto" });

Individual Options

Note on Video Tag Attributes

With HTML5 video tag attributes that can only be true or false (boolean), you simply include the attribute (no equals sign) to turn it on, or exclude it to turn it off. For example, to turn controls on:

WRONG

<video controls="true" ...>

RIGHT

<video controls ...>

The biggest issue people run into is trying to set these values to false using false as the value (e.g. controls="false") which actually does the opposite and sets the value to true because the attribute is still included. If you need the attribute to include an equals sign for XHTML validation, you can set the attribute's value to the same as its name (e.g. controls="controls").

controls

The controls option sets whether or not the player has controls that the user can interact with. Without controls the only way to start the video playing is with the autoplay attribute or through the API.

<video controls ...>
or
{ "controls": true }

autoplay

If autoplay is true, the video will start playing as soon as page is loaded (without any interaction from the user).
NOT SUPPORTED BY APPLE iOS DEVICES. Apple blocks the autoplay functionality in an effort to protect it's customers from unwillingly using a lot of their (often expensive) monthly data plans. A user touch/click is required to start the video in this case.

<video autoplay ...>
or
{ "autoplay": true }

preload

The preload attribute informs the browser whether or not the video data should begin downloading as soon as the video tag is loaded. The options are auto, metadata, and none.

'auto': Start loading the video immediately (if the browser agrees). Some mobile devices like iPhones and iPads will not preload the video in order to protect their users' bandwidth. This is why the value is called 'auto' and not something more final like 'true'.

'metadata': Load only the meta data of the video, which includes information like the duration and dimensions of the video.

'none': Don't preload any of the video data. This will wait until the user clicks play to begin downloading.

<video preload ...>
or
{ "preload": "auto" }

poster

The poster attribute sets the image that displays before the video begins playing. This is often a frame of the video or a custom title screen. As soon as the user clicks play the image will go away.

<video poster="myPoster.jpg" ...>
or
{ "poster": "myPoster.jpg" }

loop

The loop attribute causes the video to start over as soon as it ends. This could be used for a visual effect like clouds in the background.

<video loop ...>
or
{ "loop": true }

width

The width attribute sets the display width of the video.

<video width="640" ...>
or
{ "width": 640 }

height

The height attribute sets the display height of the video.

<video height="480" ...>
or
{ "height": 480 }

Component Options

You can set the options for any single player component. For instance, if you wanted to remove the muteToggle button, which
is a child of controlBar, you can just set that component to false:

var player = videojs('video-id', {
  controlBar: {
    muteToggle: false
  }
});

This also works using the data-setup attribute on the video element, just remember the options need to use proper JSON
notation.

<video ... data-setup='{ "controlBar": { "muteToggle": false } }'></video>

The components guide has an excellent breakdown of the structure of a player, you
just need to remember to nest child components in a children array for each level.


const scrubbingSpy = sinon.spy(player, 'scrubbing');

// mousemove is listened to on the document.

// Specifically, we check the ownerDocument of the seekBar's bar.

// Therefore, we want to mock it out to be able to trigger mousemove

keep keep keep add keep keep keep keep keep

/**
 * @file seek-bar.js
 */
import Slider from '../../slider/slider.js';
import Component from '../../component.js';
import {IS_IOS, IS_ANDROID} from '../../utils/browser.js';
import * as Dom from '../../utils/dom.js';
import * as Fn from '../../utils/fn.js';
import {formatTime} from '../../utils/time.js';
import {silencePromise} from '../../utils/promise';
import keycode from 'keycode';
import document from 'global/document';

import './load-progress-bar.js';
import './play-progress-bar.js';
import './mouse-time-display.js';

// The number of seconds the `step*` functions move the timeline.
const STEP_SECONDS = 5;

// The multiplier of STEP_SECONDS that PgUp/PgDown move the timeline.
const PAGE_KEY_MULTIPLIER = 12;

/**
 * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
 * as its `bar`.
 *
 * @extends Slider
 */
class SeekBar extends Slider {

  /**
   * Creates an instance of this class.
   *
   * @param { import('../../player').default } player
   *        The `Player` that this class should be attached to.
   *
   * @param {Object} [options]
   *        The key/value store of player options.
   */
  constructor(player, options) {
    super(player, options);
    this.setEventHandlers_();
  }

  /**
   * Sets the event handlers
   *
   * @private
   */
  setEventHandlers_() {
    this.update_ = Fn.bind_(this, this.update);
    this.update = Fn.throttle(this.update_, Fn.UPDATE_REFRESH_INTERVAL);

    this.on(this.player_, ['ended', 'durationchange', 'timeupdate'], this.update);
    if (this.player_.liveTracker) {
      this.on(this.player_.liveTracker, 'liveedgechange', this.update);
    }

    // when playing, let's ensure we smoothly update the play progress bar
    // via an interval
    this.updateInterval = null;

    this.enableIntervalHandler_ = (e) => this.enableInterval_(e);
    this.disableIntervalHandler_ = (e) => this.disableInterval_(e);

    this.on(this.player_, ['playing'], this.enableIntervalHandler_);

    this.on(this.player_, ['ended', 'pause', 'waiting'], this.disableIntervalHandler_);

    // we don't need to update the play progress if the document is hidden,
    // also, this causes the CPU to spike and eventually crash the page on IE11.
    if ('hidden' in document && 'visibilityState' in document) {
      this.on(document, 'visibilitychange', this.toggleVisibility_);
    }
  }

  toggleVisibility_(e) {
    if (document.visibilityState === 'hidden') {
      this.cancelNamedAnimationFrame('SeekBar#update');
      this.cancelNamedAnimationFrame('Slider#update');
      this.disableInterval_(e);
    } else {
      if (!this.player_.ended() && !this.player_.paused()) {
        this.enableInterval_();
      }

      // we just switched back to the page and someone may be looking, so, update ASAP
      this.update();
    }
  }

  enableInterval_() {
    if (this.updateInterval) {
      return;

    }
    this.updateInterval = this.setInterval(this.update, Fn.UPDATE_REFRESH_INTERVAL);
  }

  disableInterval_(e) {
    if (this.player_.liveTracker && this.player_.liveTracker.isLive() && e && e.type !== 'ended') {
      return;
    }

    if (!this.updateInterval) {
      return;
    }

    this.clearInterval(this.updateInterval);
    this.updateInterval = null;
  }

  /**
   * Create the `Component`'s DOM element
   *
   * @return {Element}
   *         The element that was created.
   */
  createEl() {
    return super.createEl('div', {
      className: 'vjs-progress-holder'
    }, {
      'aria-label': this.localize('Progress Bar')
    });
  }

  /**
   * This function updates the play progress bar and accessibility
   * attributes to whatever is passed in.
   *
   * @param {Event} [event]
   *        The `timeupdate` or `ended` event that caused this to run.
   *
   * @listens Player#timeupdate
   *
   * @return {number}
   *          The current percent at a number from 0-1
   */
  update(event) {
    // ignore updates while the tab is hidden
    if (document.visibilityState === 'hidden') {
      return;
    }

    const percent = super.update();

    this.requestNamedAnimationFrame('SeekBar#update', () => {
      const currentTime = this.player_.ended() ?
        this.player_.duration() : this.getCurrentTime_();
      const liveTracker = this.player_.liveTracker;
      let duration = this.player_.duration();

      if (liveTracker && liveTracker.isLive()) {
        duration = this.player_.liveTracker.liveCurrentTime();
      }

      if (this.percent_ !== percent) {
        // machine readable value of progress bar (percentage complete)
        this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
        this.percent_ = percent;
      }

      if (this.currentTime_ !== currentTime || this.duration_ !== duration) {
        // human readable value of progress bar (time complete)
        this.el_.setAttribute(
          'aria-valuetext',
          this.localize(
            'progress bar timing: currentTime={1} duration={2}',
            [formatTime(currentTime, duration),
              formatTime(duration, duration)],
            '{1} of {2}'
          )
        );

        this.currentTime_ = currentTime;
        this.duration_ = duration;
      }

      // update the progress bar time tooltip with the current time
      if (this.bar) {
        this.bar.update(Dom.getBoundingClientRect(this.el()), this.getProgress());
      }
    });

    return percent;
  }

  /**
   * Prevent liveThreshold from causing seeks to seem like they
   * are not happening from a user perspective.
   *
   * @param {number} ct
   *        current time to seek to
   */
  userSeek_(ct) {
    if (this.player_.liveTracker && this.player_.liveTracker.isLive()) {
      this.player_.liveTracker.nextSeekedFromUser();
    }

    this.player_.currentTime(ct);
  }

  /**
   * Get the value of current time but allows for smooth scrubbing,
   * when player can't keep up.
   *
   * @return {number}
   *         The current time value to display
   *
   * @private
   */
  getCurrentTime_() {
    return (this.player_.scrubbing()) ?
      this.player_.getCache().currentTime :
      this.player_.currentTime();
  }

  /**
   * Get the percentage of media played so far.
   *
   * @return {number}
   *         The percentage of media played so far (0 to 1).
   */
  getPercent() {
    const currentTime = this.getCurrentTime_();
    let percent;
    const liveTracker = this.player_.liveTracker;

    if (liveTracker && liveTracker.isLive()) {
      percent = (currentTime - liveTracker.seekableStart()) / liveTracker.liveWindow();

      // prevent the percent from changing at the live edge
      if (liveTracker.atLiveEdge()) {
        percent = 1;
      }
    } else {
      percent = currentTime / this.player_.duration();
    }

    return percent;
  }

  /**
   * Handle mouse down on seek bar
   *
   * @param {MouseEvent} event
   *        The `mousedown` event that caused this to run.
   *
   * @listens mousedown
   */
  handleMouseDown(event) {
    if (!Dom.isSingleLeftClick(event)) {
      return;
    }

    // Stop event propagation to prevent double fire in progress-control.js
    event.stopPropagation();

    this.videoWasPlaying = !this.player_.paused();
    this.player_.pause();

    super.handleMouseDown(event);
  }

  /**
   * Handle mouse move on seek bar
   *
   * @param {MouseEvent} event
   *        The `mousemove` event that caused this to run.
   * @param {boolean} mouseDown this is a flag that should be set to true if `handleMouseMove` is called directly. It allows us to skip things that should not happen if coming from mouse down but should happen on regular mouse move handler. Defaults to false
   *
   * @listens mousemove
   */
  handleMouseMove(event, mouseDown = false) {
    if (!Dom.isSingleLeftClick(event)) {
      return;
    }

    if (!mouseDown && !this.player_.scrubbing()) {
      this.player_.scrubbing(true);
    }

    let newTime;
    const distance = this.calculateDistance(event);
    const liveTracker = this.player_.liveTracker;

    if (!liveTracker || !liveTracker.isLive()) {
      newTime = distance * this.player_.duration();

      // Don't let video end while scrubbing.
      if (newTime === this.player_.duration()) {
        newTime = newTime - 0.1;
      }
    } else {

      if (distance >= 0.99) {
        liveTracker.seekToLiveEdge();
        return;
      }
      const seekableStart = liveTracker.seekableStart();
      const seekableEnd = liveTracker.liveCurrentTime();

      newTime = seekableStart + (distance * liveTracker.liveWindow());

      // Don't let video end while scrubbing.
      if (newTime >= seekableEnd) {
        newTime = seekableEnd;
      }

      // Compensate for precision differences so that currentTime is not less
      // than seekable start
      if (newTime <= seekableStart) {
        newTime = seekableStart + 0.1;
      }

      // On android seekableEnd can be Infinity sometimes,
      // this will cause newTime to be Infinity, which is
      // not a valid currentTime.
      if (newTime === Infinity) {
        return;
      }
    }

    // Set new time (tell player to seek to new time)
    this.userSeek_(newTime);
  }

  enable() {
    super.enable();
    const mouseTimeDisplay = this.getChild('mouseTimeDisplay');

    if (!mouseTimeDisplay) {
      return;
    }

    mouseTimeDisplay.show();
  }

  disable() {
    super.disable();
    const mouseTimeDisplay = this.getChild('mouseTimeDisplay');

    if (!mouseTimeDisplay) {
      return;
    }

    mouseTimeDisplay.hide();
  }

  /**
   * Handle mouse up on seek bar
   *
   * @param {MouseEvent} event
   *        The `mouseup` event that caused this to run.
   *
   * @listens mouseup
   */
  handleMouseUp(event) {
    super.handleMouseUp(event);

    // Stop event propagation to prevent double fire in progress-control.js
    if (event) {
      event.stopPropagation();
    }
    this.player_.scrubbing(false);

    /**
     * Trigger timeupdate because we're done seeking and the time has changed.
     * This is particularly useful for if the player is paused to time the time displays.
     *
     * @event Tech#timeupdate
     * @type {Event}
     */
    this.player_.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
    if (this.videoWasPlaying) {
      silencePromise(this.player_.play());
    } else {
      // We're done seeking and the time has changed.
      // If the player is paused, make sure we display the correct time on the seek bar.
      this.update_();
    }
  }

  /**
   * Move more quickly fast forward for keyboard-only users
   */
  stepForward() {
    this.userSeek_(this.player_.currentTime() + STEP_SECONDS);
  }

  /**
   * Move more quickly rewind for keyboard-only users
   */
  stepBack() {
    this.userSeek_(this.player_.currentTime() - STEP_SECONDS);
  }

  /**
   * Toggles the playback state of the player
   * This gets called when enter or space is used on the seekbar
   *
   * @param {KeyboardEvent} event
   *        The `keydown` event that caused this function to be called
   *
   */
  handleAction(event) {
    if (this.player_.paused()) {
      this.player_.play();
    } else {
      this.player_.pause();
    }
  }

  /**
   * Called when this SeekBar has focus and a key gets pressed down.
   * Supports the following keys:
   *
   *   Space or Enter key fire a click event
   *   Home key moves to start of the timeline
   *   End key moves to end of the timeline
   *   Digit "0" through "9" keys move to 0%, 10% ... 80%, 90% of the timeline
   *   PageDown key moves back a larger step than ArrowDown
   *   PageUp key moves forward a large step
   *
   * @param {KeyboardEvent} event
   *        The `keydown` event that caused this function to be called.
   *
   * @listens keydown
   */
  handleKeyDown(event) {
    const liveTracker = this.player_.liveTracker;

    if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
      event.preventDefault();
      event.stopPropagation();
      this.handleAction(event);
    } else if (keycode.isEventKey(event, 'Home')) {
      event.preventDefault();
      event.stopPropagation();
      this.userSeek_(0);
    } else if (keycode.isEventKey(event, 'End')) {
      event.preventDefault();
      event.stopPropagation();
      if (liveTracker && liveTracker.isLive()) {
        this.userSeek_(liveTracker.liveCurrentTime());
      } else {
        this.userSeek_(this.player_.duration());
      }
    } else if (/^[0-9]$/.test(keycode(event))) {
      event.preventDefault();
      event.stopPropagation();
      const gotoFraction = (keycode.codes[keycode(event)] - keycode.codes['0']) * 10.0 / 100.0;

      if (liveTracker && liveTracker.isLive()) {
        this.userSeek_(liveTracker.seekableStart() + (liveTracker.liveWindow() * gotoFraction));
      } else {
        this.userSeek_(this.player_.duration() * gotoFraction);
      }
    } else if (keycode.isEventKey(event, 'PgDn')) {
      event.preventDefault();
      event.stopPropagation();
      this.userSeek_(this.player_.currentTime() - (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
    } else if (keycode.isEventKey(event, 'PgUp')) {
      event.preventDefault();
      event.stopPropagation();
      this.userSeek_(this.player_.currentTime() + (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
    } else {
      // Pass keydown handling up for unsupported keys
      super.handleKeyDown(event);
    }
  }

  dispose() {
    this.disableInterval_();

    this.off(this.player_, ['ended', 'durationchange', 'timeupdate'], this.update);
    if (this.player_.liveTracker) {
      this.off(this.player_.liveTracker, 'liveedgechange', this.update);
    }

    this.off(this.player_, ['playing'], this.enableIntervalHandler_);
    this.off(this.player_, ['ended', 'pause', 'waiting'], this.disableIntervalHandler_);

    // we don't need to update the play progress if the document is hidden,
    // also, this causes the CPU to spike and eventually crash the page on IE11.
    if ('hidden' in document && 'visibilityState' in document) {
      this.off(document, 'visibilitychange', this.toggleVisibility_);
    }

    super.dispose();
  }
}

/**
 * Default options for the `SeekBar`
 *
 * @type {Object}
 * @private
 */
SeekBar.prototype.options_ = {
  children: [
    'loadProgressBar',
    'playProgressBar'
  ],
  barName: 'playProgressBar'
};

// MouseTimeDisplay tooltips should not be added to a player on mobile devices
if (!IS_IOS && !IS_ANDROID) {
  SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
}

Component.registerComponent('SeekBar', SeekBar);
export default SeekBar;


/**
 * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
 * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
 * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
 * robust as jquery's, so there's probably some differences.
 *
 * @file events.js
 * @module events
 */
import DomData from './dom-data';
import * as Guid from './guid.js';
import log from './log.js';
import window from 'global/window';
import document from 'global/document';

/**
 * Clean up the listener cache and dispatchers
 *
 * @param {Element|Object} elem
 *        Element to clean up
 *
 * @param {string} type
 *        Type of event to clean up
 */
function _cleanUpEvents(elem, type) {
  if (!DomData.has(elem)) {
    return;
  }
  const data = DomData.get(elem);

  // Remove the events of a particular type if there are none left
  if (data.handlers[type].length === 0) {
    delete data.handlers[type];
    // data.handlers[type] = null;
    // Setting to null was causing an error with data.handlers

    // Remove the meta-handler from the element
    if (elem.removeEventListener) {
      elem.removeEventListener(type, data.dispatcher, false);
    } else if (elem.detachEvent) {
      elem.detachEvent('on' + type, data.dispatcher);
    }
  }

  // Remove the events object if there are no types left
  if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
    delete data.handlers;
    delete data.dispatcher;
    delete data.disabled;
  }

  // Finally remove the element data if there is no data left
  if (Object.getOwnPropertyNames(data).length === 0) {
    DomData.delete(elem);
  }
}

/**
 * Loops through an array of event types and calls the requested method for each type.
 *
 * @param {Function} fn
 *        The event method we want to use.
 *
 * @param {Element|Object} elem
 *        Element or object to bind listeners to
 *
 * @param {string} type
 *        Type of event to bind to.
 *
 * @param {Function} callback
 *        Event listener.
 */
function _handleMultipleEvents(fn, elem, types, callback) {
  types.forEach(function(type) {
    // Call the event method for each one of the types
    fn(elem, type, callback);
  });
}

/**
 * Fix a native event to have standard property values
 *
 * @param {Object} event
 *        Event object to fix.
 *
 * @return {Object}
 *         Fixed event object.
 */
export function fixEvent(event) {
  if (event.fixed_) {
    return event;
  }

  function returnTrue() {
    return true;
  }

  function returnFalse() {
    return false;
  }

  // Test if fixing up is needed
  // Used to check if !event.stopPropagation instead of isPropagationStopped
  // But native events return true for stopPropagation, but don't have
  // other expected methods like isPropagationStopped. Seems to be a problem
  // with the Javascript Ninja code. So we're just overriding all events now.
  if (!event || !event.isPropagationStopped || !event.isImmediatePropagationStopped) {
    const old = event || window.event;

    event = {};
    // Clone the old object so that we can modify the values event = {};
    // IE8 Doesn't like when you mess with native event properties
    // Firefox returns false for event.hasOwnProperty('type') and other props
    //  which makes copying more difficult.
    // TODO: Probably best to create a whitelist of event props
    for (const key in old) {
      // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
      // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
      // and webkitMovementX/Y
      // Lighthouse complains if Event.path is copied
      if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' &&
          key !== 'webkitMovementX' && key !== 'webkitMovementY' &&
          key !== 'path') {
        // Chrome 32+ warns if you try to copy deprecated returnValue, but
        // we still want to if preventDefault isn't supported (IE8).
        if (!(key === 'returnValue' && old.preventDefault)) {
          event[key] = old[key];
        }
      }
    }

    // The event occurred on this element
    if (!event.target) {
      event.target = event.srcElement || document;
    }

    // Handle which other element the event is related to
    if (!event.relatedTarget) {
      event.relatedTarget = event.fromElement === event.target ?
        event.toElement :
        event.fromElement;
    }

    // Stop the default browser action
    event.preventDefault = function() {
      if (old.preventDefault) {
        old.preventDefault();
      }
      event.returnValue = false;
      old.returnValue = false;
      event.defaultPrevented = true;
    };

    event.defaultPrevented = false;

    // Stop the event from bubbling
    event.stopPropagation = function() {
      if (old.stopPropagation) {
        old.stopPropagation();
      }
      event.cancelBubble = true;
      old.cancelBubble = true;
      event.isPropagationStopped = returnTrue;
    };

    event.isPropagationStopped = returnFalse;

    // Stop the event from bubbling and executing other handlers
    event.stopImmediatePropagation = function() {
      if (old.stopImmediatePropagation) {
        old.stopImmediatePropagation();
      }
      event.isImmediatePropagationStopped = returnTrue;
      event.stopPropagation();
    };

    event.isImmediatePropagationStopped = returnFalse;

    // Handle mouse position
    if (event.clientX !== null && event.clientX !== undefined) {
      const doc = document.documentElement;
      const body = document.body;

      event.pageX = event.clientX +
        (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
        (doc && doc.clientLeft || body && body.clientLeft || 0);
      event.pageY = event.clientY +
        (doc && doc.scrollTop || body && body.scrollTop || 0) -
        (doc && doc.clientTop || body && body.clientTop || 0);
    }

    // Handle key presses
    event.which = event.charCode || event.keyCode;

    // Fix button for mouse clicks:
    // 0 == left; 1 == middle; 2 == right
    if (event.button !== null && event.button !== undefined) {

      // The following is disabled because it does not pass videojs-standard
      // and... yikes.
      /* eslint-disable */
      event.button = (event.button & 1 ? 0 :
        (event.button & 4 ? 1 :
          (event.button & 2 ? 2 : 0)));
      /* eslint-enable */
    }
  }

  event.fixed_ = true;
  // Returns fixed-up instance
  return event;
}

/**
 * Whether passive event listeners are supported
 */
let _supportsPassive;

const supportsPassive = function() {
  if (typeof _supportsPassive !== 'boolean') {
    _supportsPassive = false;
    try {
      const opts = Object.defineProperty({}, 'passive', {
        get() {
          _supportsPassive = true;
        }
      });

      window.addEventListener('test', null, opts);
      window.removeEventListener('test', null, opts);
    } catch (e) {
      // disregard
    }
  }

  return _supportsPassive;
};

/**
 * Touch events Chrome expects to be passive
 */
const passiveEvents = [
  'touchstart',
  'touchmove'
];

/**
 * Add an event listener to element
 * It stores the handler function in a separate cache object
 * and adds a generic handler to the element's event,
 * along with a unique id (guid) to the element.
 *
 * @param {Element|Object} elem
 *        Element or object to bind listeners to
 *
 * @param {string|string[]} type
 *        Type of event to bind to.
 *
 * @param {Function} fn
 *        Event listener.
 */
export function on(elem, type, fn) {
  if (Array.isArray(type)) {
    return _handleMultipleEvents(on, elem, type, fn);
  }

  if (!DomData.has(elem)) {
    DomData.set(elem, {});
  }

  const data = DomData.get(elem);

  // We need a place to store all our handler data
  if (!data.handlers) {
    data.handlers = {};
  }

  if (!data.handlers[type]) {
    data.handlers[type] = [];
  }

  if (!fn.guid) {
    fn.guid = Guid.newGUID();
  }

  data.handlers[type].push(fn);

  if (!data.dispatcher) {
    data.disabled = false;

    data.dispatcher = function(event, hash) {

      if (data.disabled) {
        return;
      }

      event = fixEvent(event);

      const handlers = data.handlers[event.type];

      if (handlers) {
        // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
        const handlersCopy = handlers.slice(0);

        for (let m = 0, n = handlersCopy.length; m < n; m++) {
          if (event.isImmediatePropagationStopped()) {
            break;
          } else {
            try {
              handlersCopy[m].call(elem, event, hash);
            } catch (e) {
              log.error(e);
            }
          }
        }
      }
    };
  }

  if (data.handlers[type].length === 1) {
    if (elem.addEventListener) {
      let options = false;

      if (supportsPassive() &&
        passiveEvents.indexOf(type) > -1) {
        options = {passive: true};
      }
      elem.addEventListener(type, data.dispatcher, options);
    } else if (elem.attachEvent) {
      elem.attachEvent('on' + type, data.dispatcher);
    }
  }
}

/**
 * Removes event listeners from an element
 *
 * @param {Element|Object} elem
 *        Object to remove listeners from.
 *
 * @param {string|string[]} [type]
 *        Type of listener to remove. Don't include to remove all events from element.
 *
 * @param {Function} [fn]
 *        Specific listener to remove. Don't include to remove listeners for an event
 *        type.
 */
export function off(elem, type, fn) {
  // Don't want to add a cache object through getElData if not needed
  if (!DomData.has(elem)) {
    return;
  }

  const data = DomData.get(elem);

  // If no events exist, nothing to unbind
  if (!data.handlers) {
    return;
  }

  if (Array.isArray(type)) {
    return _handleMultipleEvents(off, elem, type, fn);
  }

  // Utility function
  const removeType = function(el, t) {
    data.handlers[t] = [];
    _cleanUpEvents(el, t);
  };

  // Are we removing all bound events?
  if (type === undefined) {
    for (const t in data.handlers) {
      if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
        removeType(elem, t);
      }
    }
    return;
  }

  const handlers = data.handlers[type];

  // If no handlers exist, nothing to unbind
  if (!handlers) {
    return;
  }

  // If no listener was provided, remove all listeners for type
  if (!fn) {
    removeType(elem, type);
    return;
  }

  // We're only removing a single handler
  if (fn.guid) {
    for (let n = 0; n < handlers.length; n++) {
      if (handlers[n].guid === fn.guid) {
        handlers.splice(n--, 1);
      }
    }
  }

  _cleanUpEvents(elem, type);
}

/**
 * Trigger an event for an element
 *
 * @param {Element|Object} elem
 *        Element to trigger an event on
 *
 * @param {EventTarget~Event|string} event
 *        A string (the type) or an event object with a type attribute
 *
 * @param {Object} [hash]
 *        data hash to pass along with the event
 *
 * @return {boolean|undefined}
 *         Returns the opposite of `defaultPrevented` if default was
 *         prevented. Otherwise, returns `undefined`
 */
export function trigger(elem, event, hash) {
  // Fetches element data and a reference to the parent (for bubbling).
  // Don't want to add a data object to cache for every parent,
  // so checking hasElData first.
  const elemData = DomData.has(elem) ? DomData.get(elem) : {};
  const parent = elem.parentNode || elem.ownerDocument;
  // type = event.type || event,
  // handler;

  // If an event name was passed as a string, creates an event out of it
  if (typeof event === 'string') {
    event = {type: event, target: elem};
  } else if (!event.target) {
    event.target = elem;
  }

  // Normalizes the event properties.
  event = fixEvent(event);

  // If the passed element has a dispatcher, executes the established handlers.
  if (elemData.dispatcher) {
    elemData.dispatcher.call(elem, event, hash);
  }

  // Unless explicitly stopped or the event does not bubble (e.g. media events)
  // recursively calls this function to bubble the event up the DOM.
  if (parent && !event.isPropagationStopped() && event.bubbles === true) {
    trigger.call(null, parent, event, hash);

  // If at the top of the DOM, triggers the default action unless disabled.
  } else if (!parent && !event.defaultPrevented && event.target && event.target[event.type]) {
    if (!DomData.has(event.target)) {
      DomData.set(event.target, {});
    }
    const targetData = DomData.get(event.target);

    // Checks if the target has a default action for this event.
    if (event.target[event.type]) {
      // Temporarily disables event dispatching on the target as we have already executed the handler.
      targetData.disabled = true;
      // Executes the default action.
      if (typeof event.target[event.type] === 'function') {
        event.target[event.type]();
      }
      // Re-enables event dispatching.
      targetData.disabled = false;
    }
  }

  // Inform the triggerer if the default was prevented by returning false
  return !event.defaultPrevented;
}

/**
 * Trigger a listener only once for an event.
 *
 * @param {Element|Object} elem
 *        Element or object to bind to.
 *
 * @param {string|string[]} type
 *        Name/type of event
 *
 * @param {Event~EventListener} fn
 *        Event listener function
 */
export function one(elem, type, fn) {
  if (Array.isArray(type)) {
    return _handleMultipleEvents(one, elem, type, fn);
  }
  const func = function() {
    off(elem, type, func);
    fn.apply(this, arguments);
  };

  // copy the guid to the new function so it can removed using the original function's ID
  func.guid = fn.guid = fn.guid || Guid.newGUID();
  on(elem, type, func);
}

/**
 * Trigger a listener only once and then turn if off for all
 * configured events
 *
 * @param {Element|Object} elem
 *        Element or object to bind to.
 *
 * @param {string|string[]} type
 *        Name/type of event
 *
 * @param {Event~EventListener} fn
 *        Event listener function
 */
export function any(elem, type, fn) {
  const func = function() {
    off(elem, type, func);
    fn.apply(this, arguments);
  };

  // copy the guid to the new function so it can removed using the original function's ID
  func.guid = fn.guid = fn.guid || Guid.newGUID();

  // multiple ons, but one off for everything
  on(elem, type, func);
}


.vjs-menu-button-popup .vjs-menu {
  display: none;
  position: absolute;
  bottom: 0;
  width: 10em;
  left: -3em; // (Width of vjs-menu - width of button) / 2
  height: 0em;
  margin-bottom: 1.5em;
  border-top-color: rgba($primary-background-color, $primary-background-transparency); // Same as ul background
}

.vjs-pip-window .vjs-menu-button-popup .vjs-menu {
  left: unset;
  right: 1em; // Extra offset for last menu button in pip window, as fullscreen button not present
}

// Button Pop-up Menu
.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
  @include background-color-with-alpha($primary-background-color, $primary-background-transparency);

  position: absolute;
  width: 100%;
  bottom: 1.5em; // Same bottom as vjs-menu border-top
  max-height: 15em;
}

.vjs-layout-tiny .vjs-menu-button-popup .vjs-menu .vjs-menu-content,
.vjs-layout-x-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
  max-height: 5em;
}

.vjs-layout-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
  max-height: 10em;
}

.vjs-layout-medium .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
  max-height: 14em;
}

.vjs-layout-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content,
.vjs-layout-x-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content,
.vjs-layout-huge .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
  max-height: 25em;
}

.vjs-workinghover .vjs-menu-button-popup.vjs-hover .vjs-menu,
.vjs-menu-button-popup .vjs-menu.vjs-lock-showing {
  display: block;
}


keep keep keep keep keep replace keep keep

// Allows to have a duration state similar to player.reset() by combining the durationchange

/* eslint-env qunit */
import TestHelpers from './test-helpers.js';
import {createTimeRanges} from '../../src/js/utils/time.js';
import sinon from 'sinon';
QUnit.module('player reset-ui');

QUnit.test('Calling resetProgressBar should reset the components displaying time related values', function(assert) {
  const clock = sinon.useFakeTimers();
  const player = TestHelpers.makePlayer({controls: true});

  clock.tick(1000);
  player.trigger('ready');
  player.buffered = function() {
    return createTimeRanges(0, 5);
  };
  player.duration(40);
  player.currentTime(20);

  const {
    currentTimeDisplay,
    durationDisplay,
    remainingTimeDisplay,
    progressControl: {
      seekBar
    }
  } = player.controlBar;

  clock.tick(30);

  player.trigger('timeupdate');
  player.trigger('progress');

  clock.tick(30);

  // Current time display
  assert.equal(currentTimeDisplay.textNode_.textContent, '0:20', 'current time display is 0:20');
  // Duration display
  assert.equal(durationDisplay.textNode_.textContent, '0:40', 'duration display is 0:40');
  // Remaining time display
  assert.equal(remainingTimeDisplay.textNode_.textContent, '0:20', 'remaining time display is 0:20');
  // Seek bar
  assert.equal(seekBar.getProgress(), '0.5', 'seek bar progress is 0.5');
  assert.equal(seekBar.getAttribute('aria-valuetext'), '0:20 of 0:40', 'seek bar progress holder aria value text is 0:20 of 0:40');
  assert.equal(seekBar.getAttribute('aria-valuenow'), '50.00', 'seek bar progress holder aria value now is 50.00');
  // Load progress
  assert.equal(seekBar.loadProgressBar.el().textContent, 'Loaded: 12.50%', 'load progress bar textContent is Loaded: 12.50%');
  assert.equal(seekBar.loadProgressBar.el().style.width, '12.5%', 'load progress bar width is 12.5%');
  // Play progress
  assert.equal(seekBar.playProgressBar.el().textContent, '0:20', 'player progress bar textContent is 0:20');
  assert.equal(seekBar.playProgressBar.el().style.width, '50%', 'player progress bar width is 50%');
  assert.equal(seekBar.playProgressBar.timeTooltip.el().textContent, '0:20', 'player progress bar time tooltip is 0:20');

  // Do reset
  player.resetProgressBar_();
  player.duration(0);
  clock.tick(30);

  assert.equal(player.currentTime(), 0, 'player current time is 0');

  // Current time display
  assert.equal(currentTimeDisplay.textNode_.textContent, '0:00', 'current time display is 0:00');
  // Duration display
  assert.equal(durationDisplay.textNode_.textContent, '0:00', 'duration display is 0:00');
  // Remaining time display
  assert.equal(remainingTimeDisplay.textNode_.textContent, '0:00', 'remaining time display is 0:00');
  // Seek bar
  assert.equal(seekBar.getProgress(), '0', 'seek bar progress is 0');
  assert.equal(seekBar.getAttribute('aria-valuetext'), '0:00 of 0:00', 'seek bar progress holder aria value text is 0:00 of 0:00');
  assert.equal(seekBar.getAttribute('aria-valuenow'), '0.00', 'seek bar progress holder aria value now is 0.00');
  // Load progress
  assert.equal(seekBar.loadProgressBar.el().textContent, 'Loaded: 0.00%', 'load progress bar textContent is Loaded: 0.00%');
  assert.equal(seekBar.loadProgressBar.el().style.width, '0%', 'load progress bar width is 0%');
  // Play progress
  assert.equal(seekBar.playProgressBar.el().textContent, '0:00', 'player progress bar textContent is 0:00');
  assert.equal(seekBar.playProgressBar.el().style.width, '0%', 'player progress bar width is 0%');
  assert.equal(seekBar.playProgressBar.timeTooltip.el().textContent, '0:00', 'player progress bar time tooltip is 0:00');

  clock.restore();
  player.dispose();
});

QUnit.test('Calling resetProgressBar_ player method when no durationDisplay or remainingTimeDisplay controls exist', function(assert) {
  const player = TestHelpers.makePlayer({
    children: {
      controlBar: {}
    },
    controlBar: {
      children: {
        playToggle: {}
      }
    }
  });

  player.currentTime(20);
  player.resetProgressBar_();

  assert.equal(
    player.currentTime(), 0,
    'player current time is 0'
  );
  player.dispose();
});

QUnit.test('Calling resetPlaybackRate player method should place play rate at 1x', function(assert) {
  const player = TestHelpers.makePlayer({techOrder: ['html5']});

  player.playbackRate(2);
  player.handleTechRateChange_();
  player.resetPlaybackRate_();
  const defaultRate = player.defaultPlaybackRate();

  assert.equal(
    player.controlBar.playbackRateMenuButton.labelEl_.textContent, defaultRate + 'x',
    'Playback rate is the default one on the UI'
  );
  assert.equal(
    player.playbackRate(), defaultRate,
    'Playback rate is the default one on the player object'
  );
  player.dispose();
});

QUnit.test('Calling resetVolumeBar player method should reset volume bar', function(assert) {
  const player = TestHelpers.makePlayer({ techOrder: ['html5'] });

  player.volume(0.5);

  player.trigger('volumechange');

  assert.equal(player.controlBar.volumePanel.volumeControl.volumeBar.el_.getAttribute('aria-valuenow'), 50, 'UI value of VolumeBar is 50');

  player.resetVolumeBar_();

  assert.equal(player.controlBar.volumePanel.volumeControl.volumeBar.el_.getAttribute('aria-valuenow'), 100, 'UI value of VolumeBar is 100');

  player.dispose();
});


Sie haben die Videowiedergabe abgebrochen.

Der Videodownload ist aufgrund eines Netzwerkfehlers fehlgeschlagen.

Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.

Die Videowiedergabe wurde entweder wegen eines Problems mit einem beschädigten Video oder wegen verwendeten Funktionen, die vom Browser nicht unterstützt werden, abgebrochen.

Für dieses Video wurde keine kompatible Quelle gefunden.

Durch Drücken der Esc-Taste bzw. Betätigung der Schaltfläche "Schließen" wird dieses modale Fenster geschlossen.

Die Entschlüsselungsschlüssel für den verschlüsselten Medieninhalt sind nicht verfügbar.

, öffnet Einstellungen für Beschreibungen

Schrift

Context: Hintergrund

Alle Einstellungen auf die Standardwerte zurücksetzen

Anfang des Dialogfensters. Esc bricht ab und schließt das Fenster.

Zur Live-Übertragung wechseln. Aktuell wird es nicht live abgespielt.

Zur Live-Übertragung wechseln. Es wird aktuell live abgespielt.

Wird im Bild-im-Bild-Modus wiedergegeben


/* eslint-env qunit */
import { formatTime, setFormatTime, resetFormatTime, createTimeRanges, createTimeRange } from '../../../src/js/utils/time.js';
import window from 'global/window';

QUnit.module('utils/time', function() {

  QUnit.module('TimeRanges');

  QUnit.test('should export the deprecated createTimeRange function', function(assert) {
    assert.equal(
      createTimeRange,
      createTimeRanges,
      'createTimeRange is an alias to createTimeRanges'
    );
  });

  QUnit.test('should create a fake single TimeRanges object', function(assert) {
    const tr = createTimeRanges(0, 10);

    assert.equal(tr.length, 1, 'length should be 1');
    assert.equal(
      tr.start(0),
      0,
      'works if start is called with valid index'
    );
    assert.equal(
      tr.end(0),
      10,
      'works if end is called with with valid index'
    );
    assert.throws(
      ()=>tr.start(1),
      /Failed to execute 'start'/,
      'fails if start is called with an invalid index'
    );
    assert.throws(
      ()=>tr.end(1),
      /Failed to execute 'end'/,
      'fails if end is called with with an invalid index'
    );
  });

  QUnit.test('should create a fake multiple TimeRanges object', function(assert) {
    const tr = createTimeRanges([
      [0, 10],
      [11, 20]
    ]);

    assert.equal(tr.length, 2, 'length should equal 2');
    assert.equal(tr.start(1), 11, 'works if start is called with valid index');
    assert.equal(tr.end(1), 20, 'works if end is called with with valid index');
    assert.throws(
      ()=>tr.start(-1),
      /Failed to execute 'start'/,
      'fails if start is called with an invalid index'
    );
    assert.throws(
      ()=>tr.end(-1),
      /Failed to execute 'end'/,
      'fails if end is called with with an invalid index'
    );
  });

  QUnit.test('should throw without being given an index', function(assert) {
    const tr = createTimeRanges([
      [0, 10],
      [11, 20]
    ]);

    assert.throws(
      () => tr.start(),
      /Failed to execute 'start'/,
      'start throws if no index is given'
    );

    assert.throws(
      () => tr.end(),
      /Failed to execute 'end'/,
      'end throws if no index is given'
    );
  });

  let testOrSkip = 'skip';

  if (window.Symbol && window.Symbol.iterator) {
    testOrSkip = 'test';
  }

  QUnit[testOrSkip]('Array.from works on our TimeRanges object', function(assert) {
    const trRepresentation = [
      [0, 10],
      [20, 30]
    ];
    let tr = createTimeRanges(trRepresentation);

    assert.deepEqual(Array.from(tr), trRepresentation, 'we got back what we put in');

    tr = createTimeRanges(0, 10);
    assert.deepEqual(Array.from(tr), [[0, 10]], 'we got back a ranges representation');
  });

  QUnit.module('formatTime', {
    afterEach: resetFormatTime()
  });

  QUnit.test('should format time as a string', function(assert) {
    assert.ok(formatTime(1) === '0:01');
    assert.ok(formatTime(10) === '0:10');
    assert.ok(formatTime(60) === '1:00');
    assert.ok(formatTime(600) === '10:00');
    assert.ok(formatTime(3600) === '1:00:00');
    assert.ok(formatTime(36000) === '10:00:00');
    assert.ok(formatTime(360000) === '100:00:00');

    // Using guide should provide extra leading zeros
    assert.ok(formatTime(1, 1) === '0:01');
    assert.ok(formatTime(1, 10) === '0:01');
    assert.ok(formatTime(1, 60) === '0:01');
    assert.ok(formatTime(1, 600) === '00:01');
    assert.ok(formatTime(1, 3600) === '0:00:01');
    // Don't do extra leading zeros for hours
    assert.ok(formatTime(1, 36000) === '0:00:01');
    assert.ok(formatTime(1, 360000) === '0:00:01');

    // Do not display negative time
    assert.ok(formatTime(-1) === '0:00');
    assert.ok(formatTime(-1, 3600) === '0:00:00');
  });

  QUnit.test('should format invalid times as dashes', function(assert) {
    assert.equal(formatTime(Infinity, 90), '-:-');
    assert.equal(formatTime(NaN), '-:-');
    assert.equal(formatTime(10, Infinity), '0:00:10');
    assert.equal(formatTime(90, NaN), '1:30');
  });

  QUnit.test('setFormatTime', function(assert) {
    setFormatTime((seconds, guide) => `custom:${seconds}:${guide}`);
    assert.equal(formatTime(1, 2), 'custom:1:2', 'it should replace the default formatTime implementation');
  });

  QUnit.test('resetFormatTime', function(assert) {
    setFormatTime((seconds, guide) => `custom:${seconds}:${guide}`);
    assert.equal(formatTime(1, 2), 'custom:1:2');
    resetFormatTime();
    assert.equal(formatTime(1), '0:01', 'it should reset formatTime to the default implementation');
  });
});


/**
 * @file html-track-element.js
 */

import EventTarget from '../event-target';
import TextTrack from '../tracks/text-track';

/**
 * A single track represented in the DOM.
 *
 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
 * @extends EventTarget
 */
class HTMLTrackElement extends EventTarget {

  /**
   * Create an instance of this class.
   *
   * @param {Object} options={}
   *        Object of option names and values
   *
   * @param { import('../tech/tech').default } options.tech
   *        A reference to the tech that owns this HTMLTrackElement.
   *
   * @param {TextTrack~Kind} [options.kind='subtitles']
   *        A valid text track kind.
   *
   * @param {TextTrack~Mode} [options.mode='disabled']
   *        A valid text track mode.
   *
   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
   *        A unique id for this TextTrack.
   *
   * @param {string} [options.label='']
   *        The menu label for this track.
   *
   * @param {string} [options.language='']
   *        A valid two character language code.
   *
   * @param {string} [options.srclang='']
   *        A valid two character language code. An alternative, but deprioritized
   *        version of `options.language`
   *
   * @param {string} [options.src]
   *        A url to TextTrack cues.
   *
   * @param {boolean} [options.default]
   *        If this track should default to on or off.
   */
  constructor(options = {}) {
    super();

    let readyState;

    const track = new TextTrack(options);

    this.kind = track.kind;
    this.src = track.src;
    this.srclang = track.language;
    this.label = track.label;
    this.default = track.default;

    Object.defineProperties(this, {

      /**
       * @memberof HTMLTrackElement
       * @member {HTMLTrackElement~ReadyState} readyState
       *         The current ready state of the track element.
       * @instance
       */
      readyState: {
        get() {
          return readyState;
        }
      },

      /**
       * @memberof HTMLTrackElement
       * @member {TextTrack} track
       *         The underlying TextTrack object.
       * @instance
       *
       */
      track: {
        get() {
          return track;
        }
      }
    });

    readyState = HTMLTrackElement.NONE;

    /**
     * @listens TextTrack#loadeddata
     * @fires HTMLTrackElement#load
     */
    track.addEventListener('loadeddata', () => {
      readyState = HTMLTrackElement.LOADED;

      this.trigger({
        type: 'load',
        target: this
      });
    });
  }
}

HTMLTrackElement.prototype.allowedEvents_ = {
  load: 'load'
};

/**
 * The text track not loaded state.
 *
 * @type {number}
 * @static
 */
HTMLTrackElement.NONE = 0;

/**
 * The text track loading state.
 *
 * @type {number}
 * @static
 */
HTMLTrackElement.LOADING = 1;

/**
 * The text track loaded state.
 *
 * @type {number}
 * @static
 */
HTMLTrackElement.LOADED = 2;

/**
 * The text track failed to load state.
 *
 * @type {number}
 * @static
 */
HTMLTrackElement.ERROR = 3;

export default HTMLTrackElement;


assert.equal(player.currentTime(), 0, 'player current time is 0');

assert.equal(currentTimeDisplay.textNode_.textContent, '0:00', 'current time display is 0:00');

const calculateDistance = sinon.spy(seekBar, 'calculateDistance');

seekBar.handleMouseMove({ offsetX: 1 });


<!DOCTYPE html>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link rel="stylesheet" type="text/css" href="./css/guides.css">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700,700italic,600,600italic' rel='stylesheet' type='text/css'><link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/styles/atelier-forest.light.min.css" />

<link rel="canonical" href="https://docs.videojs.com/">

This documentation is for an outdated version of Video.js. See documentation for the current release.

Videojs Documentation

Guides

Setup

Options

Tracks

Components

API

Skins

Languages

Tech

Plugins

Glossary

Samples

Simple Embed

Shared

API Reference

Videojs API


<!DOCTYPE html><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>Component</title><link href="https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700" rel="stylesheet" type="text/css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/styles/atelier-forest.light.min.css"/><link rel="stylesheet" type="text/css" href="css/api-docs.css"/><link rel="canonical" href="https://docs.videojs.com/Component.html">

This documentation is for an outdated version of Video.js. See documentation for the current release.

API Index

Component Methods

Class Methods

$

$$

addChild

addClass

buildCSSClass

children

clearInterval

clearTimeout

contentEl

createEl

dimensions

dispose

el

enableTouchActivity

extend

getChild

getChildById

getComponent

hasClass

height

hide

id

initChildren

name

off

on

one

options

player

ready

registerComponent

removeChild

removeClass

setInterval

setTimeout

show

toggleClass

trigger

triggerReady

width

Inherited Methods from Button

handleKeyPress

Inherited Methods from ClickableComponent

controlText

handleBlur

handleClick

handleFocus

Inherited Methods from Component

Component

Base UI Component class
Components are embeddable UI objects that are represented by both a
javascript object and an element in the DOM. They can be children of other
components, and can have many children themselves.

    // adding a button to the player
    var button = player.addChild('button');
    button.el(); // -> button element

Button

Components are also event targets.

    button.on('click', function(){
      console.log('Button Clicked!');
    });
    button.trigger('customevent');

DEFINED IN: component.js line number: 18

EXTENDS: button.js

Constructor

Component( player,[options],[ready] )

Parameters

name	Type	Required	Description

player	Object	yes	Main Player

options	Object	no	Object of option names and values

ready	function	no	Ready callback function

Methods

$( selector, [context] )

Finds a single DOM element matching selector within the component's
contentEl or another custom context.

Parameters

name	Type	Required	Description

selector	String	yes	A valid CSS selector, which will be passed to querySelector.

context	Element|String	no	A DOM element within which to query. Can also be a selector
        string in which case the first matching element will be used
        as context. If missing (or no element matches selector), falls
        back to document.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 841

[back to top]

$$( selector, [context] )

Finds a all DOM elements matching selector within the component's
contentEl or another custom context.

Parameters

name	Type	Required	Description

selector	String	yes	A valid CSS selector, which will be passed to querySelectorAll.

context	Element|String	no	A DOM element within which to query. Can also be a selector
        string in which case the first matching element will be used
        as context. If missing (or no element matches selector), falls
        back to document.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 861

[back to top]

addChild( child, [options] )

Adds a child component inside this component

    myComponent.el();
    // ->

    myComponent.children();
    // [empty array]

    var myButton = myComponent.addChild('MyButton');
    // ->

myButton

    // -> myButton === myComponent.children()[0];

Pass in options for child constructors and options for children of the child

    var myButton = myComponent.addChild('MyButton', {
      text: 'Press Me',
      buttonChildExample: {
        buttonChildOption: true
      }
    });

Parameters

name	Type	Required	Description

child	String|Component	yes	The class name or instance of a child to add

options	Object	no	Options, including options to be passed to children of the child.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 315

[back to top]

addClass( classToAdd )

Add a CSS class name to the component's element

Parameters

name	Type	Required	Description

classToAdd	String	yes	Classname to add

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 892

[back to top]

buildCSSClass()

Allows sub components to stack CSS class names

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 593

[back to top]

children()

Get an array of all child components

    var kids = myComponent.children();

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 282

[back to top]

clearInterval( intervalId )

Clears an interval and removes the associated dispose listener

Parameters

name	Type	Required	Description

intervalId	Number	yes	The id of the interval to clear

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1295

[back to top]

clearTimeout( timeoutId )

Clears a timeout and removes the associated dispose listener

Parameters

name	Type	Required	Description

timeoutId	Number	yes	The id of the timeout to clear

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1252

[back to top]

contentEl()

Return the component's DOM element where children are inserted.
Will either be the same as el() or a new element defined in createEl().

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 245

[back to top]

createEl( [tagName], [properties], [attributes] )

Create the component's DOM element

Parameters

name	Type	Required	Description

tagName	String	no	Element's node type. e.g. 'div'

properties	Object	no	An object of properties that should be set

attributes	Object	no	An object of attributes that should be set

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 208

[back to top]

dimensions( width, height )

Set both width and height at the same time

Parameters

name	Type	Required	Description

width	Number|String	yes	Width of player

height	Number|String	yes	Height of player

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1015

[back to top]

dispose()

Dispose of the component and all child components

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 101

[back to top]

el()

Get the component's DOM element

    var domEl = myComponent.el();

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 195

[back to top]

enableTouchActivity()

Report user touch activity when touch events occur
User activity is used to determine when controls should show/hide. It's
relatively simple when it comes to mouse events, because any mouse event
should show the controls. So we capture mouse events that bubble up to the
player and report activity when that happens.
With touch events it isn't as easy. We can't rely on touch events at the
player level, because a tap (touchstart + touchend) on the video itself on
mobile devices is meant to turn controls off (and on). User activity is
checked asynchronously, so what could happen is a tap event on the video
turns the controls off, then the touchend event bubbles up to the player,
which if it reported user activity, would turn the controls right back on.
(We also don't want to completely block touch events from bubbling up)
Also a touchmove, touch+hold, and anything other than a tap is not supposed
to turn the controls back on on a mobile device.
Here we're setting the default component behavior to report user activity
whenever touch events happen, and this can be turned off by components that
want touch events to act differently.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1174

[back to top]

static extend( props ) (deprecated)

Sets up the constructor using the supplied init method
or uses the init of the parent object

Parameters

name	Type	Required	Description

props	Object	yes	An object of properties

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1350

[back to top]

getChild()

Returns a child component with the provided name

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 305

[back to top]

getChildById()

Returns a child component with the provided ID

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 295

[back to top]

static getComponent( name )

Gets a component by name

Parameters

name	Type	Required	Description

name	String	yes	Name of the component to get

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1331

[back to top]

hasClass( classToCheck )

Check if a component's element has a CSS class name

Parameters

name	Type	Required	Description

classToCheck	String	yes	Classname to check

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 881

[back to top]

height( num, [skipListeners] )

Get or set the height of the component (CSS values)
Setting the video tag dimension values only works with values in pixels.
Percent values will not work.
Some percents can be used, but width()/height() will return the number + %,
not the actual computed width/height.

Parameters

name	Type	Required	Description

num	Number|String	yes	New component height

skipListeners	Boolean	no	Skip the resize event trigger

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 998

[back to top]

hide()

Hide the component element if currently showing

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 944

[back to top]

id()

Get the component's ID

    var id = myComponent.id();

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 256

[back to top]

initChildren()

Add and initialize default child components from options

    // when an instance of MyComponent is created, all children in options
    // will be added to the instance by their name strings and options
    MyComponent.prototype.options_ = {
      children: [
        'myChildComponent'
      ],
      myChildComponent: {
        myChildOption: true
      }
    };

    // Or when creating the component
    var myComp = new MyComponent(player, {
      children: [
        'myChildComponent'
      ],
      myChildComponent: {
        myChildOption: true
      }
    });

The children option can also be an array of
child options objects (that also include a 'name' key).
This can be used if you have two child components of the
same type that need different options.

    var myComp = new MyComponent(player, {
      children: [
        'button',
        {
          name: 'button',
          someOtherOption: true
        },
        {
          name: 'button',
          someOtherOption: false
        }
      ]
    });

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 455

[back to top]

name()

Get the component's name. The name is often used to reference the component.

    var name = myComponent.name();

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 269

[back to top]

off( first, second, [third] )

Remove an event listener from this component's element

    myComponent.off('eventType', myFunc);

If myFunc is excluded, ALL listeners for the event type will be removed.
If eventType is excluded, ALL listeners will be removed from the component.
Alternatively you can use off to remove listeners that were added to other
elements or components using myComponent.on(otherComponent....
In this case both the event type and listener function are REQUIRED.

    myComponent.off(otherElement, 'eventType', myFunc);
    myComponent.off(otherComponent, 'eventType', myFunc);

Parameters

name	Type	Required	Description

first	String|Component	yes	The event type or other component

second	function|String	yes	The listener function or event type

third	function	no	The listener for other component

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 680

[back to top]

on( first, second, third )

Add an event listener to this component's element

    var myFunc = function(){
      var myComponent = this;
      // Do something when the event is fired
    };

    myComponent.on('eventType', myFunc);

The context of myFunc will be myComponent unless previously bound.
Alternatively, you can add a listener to another element or component.

    myComponent.on(otherElement, 'eventName', myFunc);
    myComponent.on(otherComponent, 'eventName', myFunc);

The benefit of using this over VjsEvents.on(otherElement, 'eventName', myFunc)
and otherComponent.on('eventName', myFunc) is that this way the listeners
will be automatically cleaned up when either component is disposed.
It will also bind myComponent as the context of myFunc.
NOTE: When using this on elements in the page other than window
and document (both permanent), if you remove the element from the DOM
you need to call myComponent.trigger(el, 'dispose') on it to clean up
references to it and allow the browser to garbage collect it.

Parameters

name	Type	Required	Description

first	String|Component	yes	The event type or other component

second	function|String	yes	The event handler or event type

third	function	yes	The event handler

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 605

[back to top]

one( first, second, [third] )

Add an event listener to be triggered only once and then removed

    myComponent.one('eventName', myFunc);

Alternatively you can add a listener to another element or component
that will be triggered only once.

    myComponent.one(otherElement, 'eventName', myFunc);
    myComponent.one(otherComponent, 'eventName', myFunc);

Parameters

name	Type	Required	Description

first	String|Component	yes	The event type or other component

second	function|String	yes	The listener function or event type

third	function	no	The listener function for other component

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 728

[back to top]

options( obj )

Deep merge of options objects
Whenever a property is an object on both options objects
the two properties will be merged using mergeOptions.

    Parent.prototype.options_ = {
      optionSet: {
        'childOne': { 'foo': 'bar', 'asdf': 'fdsa' },
        'childTwo': {},
        'childThree': {}
      }
    }
    newOptions = {
      optionSet: {
        'childOne': { 'foo': 'baz', 'abc': '123' }
        'childTwo': null,
        'childFour': {}
      }
    }

    this.options(newOptions);

RESULT

    {
      optionSet: {
        'childOne': { 'foo': 'baz', 'asdf': 'fdsa', 'abc': '123' },
        'childTwo': null, // Disabled. Won't be initialized.
        'childThree': {},
        'childFour': {}
      }
    }

Parameters

name	Type	Required	Description

obj	Object	yes	Object of new option values

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 145

[back to top]

player()

Return the component's player

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 135

[back to top]

ready( fn, sync )

Bind a listener to the component's ready state.
Different from event listeners in that if the ready event has already happened
it will trigger the function immediately.

Parameters

name	Type	Required	Description

fn	function	yes	Ready listener

sync	Boolean	yes	Exec the listener synchronously if component is ready

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 787

[back to top]

static registerComponent( name, comp )

Registers a component

Parameters

name	Type	Required	Description

name	String	yes	Name of the component to register

comp	Object	yes	The component to register

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1314

[back to top]

removeChild( component )

Remove a child component from this component's list of children, and the
child component's element from this component's element

Parameters

name	Type	Required	Description

component	Component	yes	Component to remove

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 415

[back to top]

removeClass( classToRemove )

Remove a CSS class name from the component's element

Parameters

name	Type	Required	Description

classToRemove	String	yes	Classname to remove

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 904

[back to top]

setInterval( fn, interval )

Creates an interval and sets up disposal automatically.

Parameters

name	Type	Required	Description

fn	function	yes	The function to run every N seconds.

interval	Number	yes	Number of ms to delay before executing specified function.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1271

[back to top]

setTimeout( fn, timeout )

Creates timeout and sets up disposal automatically.

Parameters

name	Type	Required	Description

fn	function	yes	The function to run after the timeout.

timeout	Number	yes	Number of ms to delay before executing specified function.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 1227

[back to top]

show()

Show the component element if hidden

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 933

[back to top]

toggleClass( classToToggle, [predicate] )

Add or remove a CSS class name from the component's element

Parameters

name	Type	Required	Description

classToToggle	String

predicate	Boolean|function	no	Can be a function that returns a Boolean. If true, the class
        will be added; if false, the class will be removed. If not
        given, the class will be added if not present and vice versa.

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 916

[back to top]

trigger( event, [hash] )

Trigger an event on an element

    myComponent.trigger('eventName');
    myComponent.trigger({'type':'eventName'});
    myComponent.trigger('eventName', {data: 'some data'});
    myComponent.trigger({'type':'eventName'}, {data: 'some data'});

Parameters

name	Type	Required	Description

event	Event|Object|String	yes	A string (the type) or an event object with a type attribute

hash	Object	no	data hash to pass along with the event

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 768

[back to top]

triggerReady()

Trigger the ready listeners

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 814

[back to top]

width( num, skipListeners )

Set or get the width of the component (CSS values)
Setting the video tag dimension values only works with values in pixels.
Percent values will not work.
Some percents can be used, but width()/height() will return the number + %,
not the actual computed width/height.

Parameters

name	Type	Required	Description

num	Number|String	yes	Optional width number

skipListeners	Boolean	yes	Skip the 'resize' event trigger

Defined in https://github.com/videojs/video.js/blob/master/src/js/component.js line number: 981

[back to top]

handleKeyPress()

Handle KeyPress (document level) - Extend with specific functionality for button

Defined in https://github.com/videojs/video.js/blob/master/src/js/button.js line number: 74

[back to top]

controlText( el )

create control text

Parameters

name	Type	Required	Description

el	Element	yes	Parent element for the control text

Defined in https://github.com/videojs/video.js/blob/master/src/js/clickable-component.js line number: 65

[back to top]

handleBlur()

Handle Blur - Remove keyboard triggers

Defined in https://github.com/videojs/video.js/blob/master/src/js/clickable-component.js line number: 163

[back to top]

handleClick()

Handle Click - Override with specific functionality for component

Defined in https://github.com/videojs/video.js/blob/master/src/js/clickable-component.js line number: 132

[back to top]

handleFocus()

Handle Focus - Add keyboard functionality to element

Defined in https://github.com/videojs/video.js/blob/master/src/js/clickable-component.js line number: 139

[back to top]


/**
 * @module filter-source
 */
import {isObject} from './obj';
import {getMimetype} from './mimetypes';

/**
 * Filter out single bad source objects or multiple source objects in an
 * array. Also flattens nested source object arrays into a 1 dimensional
 * array of source objects.
 *
 * @param {Tech~SourceObject|Tech~SourceObject[]} src
 *        The src object to filter
 *
 * @return {Tech~SourceObject[]}
 *         An array of sourceobjects containing only valid sources
 *
 * @private
 */
const filterSource = function(src) {
  // traverse array
  if (Array.isArray(src)) {
    let newsrc = [];

    src.forEach(function(srcobj) {
      srcobj = filterSource(srcobj);

      if (Array.isArray(srcobj)) {
        newsrc = newsrc.concat(srcobj);
      } else if (isObject(srcobj)) {
        newsrc.push(srcobj);
      }
    });

    src = newsrc;
  } else if (typeof src === 'string' && src.trim()) {
    // convert string into object
    src = [fixSource({src})];
  } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
    // src is already valid
    src = [fixSource(src)];
  } else {
    // invalid source, turn it into an empty array
    src = [];
  }

  return src;
};

/**
 * Checks src mimetype, adding it when possible
 *
 * @param {Tech~SourceObject} src
 *        The src object to check
 * @return {Tech~SourceObject}
 *        src Object with known type
 */
function fixSource(src) {
  if (!src.type) {
    const mimetype = getMimetype(src.src);

    if (mimetype) {
      src.type = mimetype;
    }
  }

  return src;
}

export default filterSource;


assert.equal(durationDisplay.textNode_.textContent, '0:00', 'duration display is 0:00');

assert.equal(durationDisplay.textNode_.textContent, '-:-', 'duration display is -:-');