angleBetweenPoints

Find the angle of a segment from (point1.x, point1.y) -> (point2.x, point2.y).

angleBetweenY

Find the angle of a segment from (x1, y1) -> (x2, y2).\nNote that the difference between this method and Math.angleBetween is that this assumes the y coordinate travels\ndown the screen.

angleLimit

Keeps an angle value between the given min and max values.

angle

The angle value to check. Must be between -180 and +180.

min

The minimum angle that is allowed (must be -180 or greater).

max

The maximum angle that is allowed (must be 180 or less).

average

Averages all values passed to the function and returns the result. You can pass as many parameters as you like.

bezierInterpolation

A Bezier Interpolation Method, mostly used by Phaser.Tween.

catmullRomInterpolation

A Catmull Rom Interpolation Method, mostly used by Phaser.Tween.

ceil

Round up to the next whole number.  E.g. ceil(1.3) == 2, and ceil(-2.3) == -3.

ceilTo

base

The base to round in... default is 10 for decimal.

chanceRoll

True if the roll passed, or false otherwise.

Generate a random bool result based on the chance value.\n<p>\nReturns true or false based on the chance value (default 50%). For example if you wanted a player to have a 30% chance\nof getting a bonus, call chanceRoll(30) - true means the chance passed, false means it failed.\n</p>

chance

The chance of receiving the value. A number between 0 and 100 (effectively 0% to 100%).

clamp

Force a value within the boundaries of two values.\nClamp value to range <a, b>

distance

The distance between the two sets of coordinates.

Returns the distance between the two given set of coordinates.

distancePow

Returns the distance between the two given set of coordinates at the power given.

distanceRounded

The distance between this Point object and the destination Point object.

Returns the rounded distance between the two given set of coordinates.

floor

Round down to the next whole number. E.g. floor(1.7) == 1, and floor(-2.7) == -2.

fuzzyEqual

Two number are fuzzyEqual if their difference is less than ε.

fuzzyGreaterThan

a is fuzzyGreaterThan b if it is more than b - ε.

fuzzyLessThan

a is fuzzyLessThan b if it is less than b + ε.

getRandom

Fetch a random entry from the given array.\nWill return null if random selection is missing, or array has no entries.

startIndex

Optional offset off the front of the array. Default value is 0, or the beginning of the array.

length

Optional restriction on the number of values you want to randomly select from.

interpolateFloat

A one dimensional linear interpolation of a value.

isEven

True if the given number is even. False if the given number is odd.

Returns true if the number given is even.

isOdd

True if the given number is odd. False if the given number is even.

Returns true if the number given is odd.

limitValue

Ensures the given value is between min and max inclusive.

linearInterpolation

A Linear Interpolation Method, mostly used by Phaser.Tween.

mapLinear

Linear mapping from range <a1, a2> to range <b1, b2>

max

Updated version of Math.max that can be passed either an array of numbers or the numbers as parameters.

maxAdd

Adds the given amount to the value, but never lets the value go over the specified maximum.

maxProperty

Updated version of Math.max that can be passed a property and either an array of objects or the objects as parameters.\nIt will find the largest matching property value from the given objects.

min

Updated version of Math.min that can be passed either an array of numbers or the numbers as parameters.\nSee http://jsperf.com/math-s-min-max-vs-homemade/5

minProperty

Updated version of Math.min that can be passed a property and either an array of objects or the objects as parameters.\nIt will find the lowest matching property value from the given objects.

minSub

Subtracts the given amount from the value, but never lets the value go below the specified minimum.

amount

The amount to subtract from the base value.

normalizeAngle

Returns the angle, fit within the [0,2pi] range, in radians.

Normalizes an angle to the [0,2pi) range.

normalizeLatitude

Returns the latitude, fit within the [-90,90] range.

Normalizes a latitude to the [-90,90] range. Latitudes above 90 or below -90 are capped, not wrapped.

normalizeLongitude

Returns the longitude, fit within the [-180,180] range.

Normalizes a longitude to the [-180,180] range. Longitudes above 180 or below -180 are wrapped.

numberArray

Returns an Array containing the numbers from min to max and inclusive of both values.\nIf you need exclusive of max then see Phaser.Math.numberArrayEx.

min

The minimum value the array starts with.

numberArrayStep

Creates an array of numbers (positive and/or negative) progressing from\n`start` up to but not including `end`. If `start` is less than `stop` a\nzero-length range is created unless a negative `step` is specified.\n\n\nPhaser.Math.numberArrayStep(4);\n// => [0, 1, 2, 3]\n\nPhaser.Math.numberArrayStep(1, 5);\n// => [1, 2, 3, 4]\n\nPhaser.Math.numberArrayStep(0, 20, 5);\n// => [0, 5, 10, 15]\n\nPhaser.Math.numberArrayStep(0, -4, -1);\n// => [0, -1, -2, -3]\n\nPhaser.Math.numberArrayStep(1, 4, 0);\n// => [1, 1, 1]\n\nPhaser.Math.numberArrayStep(0);\n// => []

percent

The percentage a is of b, between 0 and 1.

Work out what percentage value a is of value b using the given base.

a

The value to work out the percentage for.

b

The value you wish to get the percentage of.

removeRandom

Removes a random object from the given array and returns it.\nWill return null if random selection is missing, or array has no entries.

roundTo

base

The base to round in... default is 10 for decimal.

shift

Removes the top element from the stack and re-inserts it onto the bottom, then returns it.\nThe original stack is modified in the process. This effectively moves the position of the data from the start to the end of the table.

shuffleArray

Shuffles the data in the given array into a new order

sign

A value representing the sign of the value.\n-1 for negative, +1 for positive, 0 if value is 0

sinCosGenerator

Generate a sine and cosine table simultaneously and extremely quickly. Based on research by Franky of scene.at\n<p>\nThe parameters allow you to specify the length, amplitude and frequency of the wave. Once you have called this function\nyou should get the results via getSinTable() and getCosTable(). This generator is fast enough to be used in real-time.\n</p>

sinAmplitude

The amplitude to apply to the sine table (default 1.0) if you need values between say -+ 125 then give 125 as the value

cosAmplitude

The amplitude to apply to the cosine table (default 1.0) if you need values between say -+ 125 then give 125 as the value

frequency

- The frequency of the sine and cosine table data

smootherstep

Smootherstep function as detailed at http://en.wikipedia.org/wiki/Smoothstep

smoothstep

Smoothstep function as detailed at http://en.wikipedia.org/wiki/Smoothstep

snapTo

Snap a value to nearest grid slice, using rounding.\n\nExample: if you have an interval gap of 5 and a position of 12... you will snap to 10 whereas 14 will snap to 15.

snapToCeil

Snap a value to nearest grid slice, using ceil.\n\nExample: if you have an interval gap of 5 and a position of 12... you will snap to 15. As will 14 will snap to 15... but 16 will snap to 20.

snapToFloor

Snap a value to nearest grid slice, using floor.\n\nExample: if you have an interval gap of 5 and a position of 12... you will snap to 10. As will 14 snap to 10... but 16 will snap to 15

snapToInArray

Snaps a value to the nearest value in an array.

within

Checks if two values are within the given tolerance of each other.

tolerance

The tolerance. Anything equal to or less than this is considered within the range.

wrap

Ensures that the value always stays between min and max, by wrapping the value around.\nmax should be larger than min, or the function will return 0.

wrapAngle

The new angle value, returns the same as the input angle if it was within bounds.

Keeps an angle value between -180 and +180.

radians

Set to `true` if the angle is given in radians, otherwise degrees is expected.

wrapValue

Adds value to amount and ensures that the result always stays between 0 and max, by wrapping the value around.\nValues must be positive integers, and are passed through Math.abs.


},
set: function (value) {
value = value || null;
if (value)
{
if (this._muted)

keep keep keep keep keep replace keep keep keep keep

<!DOCTYPE html>

	<meta charset="utf-8">
	<title>Phaser Class: Destroy</title>

	<link type="text/css" rel="stylesheet" href="styles/default.css">

	<link type="text/css" rel="stylesheet" href="styles/sunlight.default.css">

	<link type="text/css" rel="stylesheet" href="styles/site.cerulean.css">

			Phaser API

					Namespaces

							Phaser

							PIXI

					Classes

							Matrix

							Animation

							AnimationManager

							AnimationParser

							ArrayList

							ArraySet

							ArrayUtils

							AudioSprite

							BitmapData

							BitmapText

							Button

							Cache

							Camera

							Canvas

							Circle

							Color

							Angle

							Animation

							AutoCull

							Bounds

							BringToTop

							Core

							Crop

							Delta

							Destroy

							FixedToCamera

							Health

							InCamera

							InputEnabled

							InWorld

							LifeSpan

							LoadTexture

							Overlap

							PhysicsBody

							Reset

							ScaleMinMax

							Smoothed

							Device

							DOM

							Easing

							Back

							Bounce

							Circular

							Cubic

							Elastic

							Exponential

							Linear

							Quadratic

							Quartic

							Quintic

							Sinusoidal

							Ellipse

							Events

							Filter

							FlexGrid

							FlexLayer

							Frame

							FrameData

							Game

							GameObjectCreator

							GameObjectFactory

							Gamepad

							GamepadButton

							Graphics

							Group

							Image

							Input

							InputHandler

							Key

							Keyboard

							Line

							LinkedList

							Loader

							LoaderParser

							Math

							Mouse

							MSPointer

							Net

							Particle

							Particles

							Arcade

							Emitter

							Physics

							Arcade

							Body

							TilemapCollision

							Ninja

							AABB

							Body

							Circle

							Tile

							P2

							Body

							BodyDebug

							CollisionGroup

							ContactMaterial

							DistanceConstraint

							FixtureList

							GearConstraint

							InversePointProxy

							LockConstraint

							Material

							PointProxy

							PrismaticConstraint

							RevoluteConstraint

							RotationalSpring

							Spring

							Plugin

							PluginManager

							Point

							Pointer

							Polygon

							QuadTree

							RandomDataGenerator

							Rectangle

							RenderTexture

							RequestAnimationFrame

							RetroFont

							Rope

							ScaleManager

							Signal

							SignalBinding

							SinglePad

							Sound

							SoundManager

							Sprite

							SpriteBatch

							Stage

							State

							StateManager

							Text

							Tile

							Tilemap

							TilemapLayer

							TilemapParser

							Tileset

							TileSprite

							Time

							Timer

							TimerEvent

							Touch

							Tween

							TweenData

							TweenManager

							Utils

							Debug

							World

							AbstractFilter

							BaseTexture

							BitmapText

							CanvasBuffer

							CanvasGraphics

							CanvasMaskManager

							CanvasRenderer

							CanvasTinter

							ComplexPrimitiveShader

							DisplayObject

							DisplayObjectContainer

							Event

							EventTarget

							FilterTexture

							Graphics

							GraphicsData

							PIXI

							PixiFastShader

							PixiShader

							PolyK

							PrimitiveShader

							RenderTexture

							Rope

							Sprite

							SpriteBatch

							Stage

							Strip

							StripShader

							Text

							Texture

							TilingSprite

							VideoTexture

							WebGLBlendModeManager

							WebGLFastSpriteBatch

							WebGLFilterManager

							WebGLRenderer

							RoundedRectangle

					Global

							append

							apply

							applyInverse

							blendModes

							clone

							contains

							fromArray

							identity

							rotate

							scale

							scaleModes

							sort

							toArray

							translate

					Core

Game

Group

World

Loader

Time

Camera

State Manager

Tween Manager

Sound Manager

Input Manager

Scale Manager

					Game Objects

Factory (game.add)

Creator (game.make)

Sprite

Image

Sound

Particle Emitter

Particle

Text

Tween

BitmapText

Tilemap

BitmapData

RetroFont

Button

Animation

Graphics

RenderTexture

TileSprite

					Geometry

Circle

Rectangle

Point

Line

Ellipse

Polygon

					Physics

Arcade Physics

Body

P2 Physics

Body

Spring

CollisionGroup

ContactMaterial

Ninja Physics

Body

					Input

Input Handler

Pointer

Mouse

Keyboard

Key

Gamepad

					Community

Phaser Web Site

Phaser Github

Phaser Examples

Phaser Plugins

Forum

Stack Overflow

Weekly Coding Tips

Newsletter

Twitter

IRC

Donate to the project

Texture Packer

        Phaser.Component.

    Destroy

new Destroy()

The Destroy component is responsible for destroying a Game Object.

Source -
    gameobjects/components/Destroy.js, line 12

Members

<readonly> destroyPhase :boolean

As a Game Object runs through its destroy method this flag is set to true,
and can be checked in any sub-systems or plugins it is being destroyed from.

Source -
    gameobjects/components/Destroy.js, line 22

Methods

destroy(destroyChildren)

Destroys the Game Object. This removes it from its parent group, destroys the input, event and animation handlers if present
and nulls its reference to game, freeing it up for garbage collection.

If this Game Object has the Events component it will also dispatch the onDestroy event.

Parameters:

		Name

		Type

		Argument

		Default

		Description

                destroyChildren

boolean

                    <optional>

                    true

Should every child of this object have its destroy method called as well?

Source -
    gameobjects/components/Destroy.js, line 33

		Phaser Copyright © 2012-2015 Photon Storm Ltd.

		Documentation generated by JSDoc 3.3.0-alpha10
		on Thu Mar 26 2015 02:53:50 GMT+0000 (GMT Standard Time) using the DocStrap template.


.video-js .vjs-audio-button + .vjs-menu .vjs-description-menu-item .vjs-menu-item-text .vjs-icon-placeholder,

.video-js .vjs-audio-button .vjs-icon-placeholder {
  @extend .vjs-icon-audio;
}

.video-js .vjs-audio-button + .vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder {
  vertical-align: middle;
  display: inline-block;
  margin-bottom: -0.1em;
}

// Mark a main-desc-menu-item (main + description) item with a trailing Audio Description icon
.video-js .vjs-audio-button + .vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before {
  font-family: VideoJS;
  content: " \f12e";
  font-size: 1.5em;
  line-height: inherit;
}


<!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-plugins.html">

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

Plugins

If you've built something cool with Video.js, you can easily share it with the rest of the world by creating a plugin. Although, you can roll your own, you can also use generator-videojs-plugin, a Yeoman generator that provides scaffolding for video.js plugins including:

Grunt for build management

npm for dependency management

QUnit for testing

Step 1: Write Some Javascript

You may have already done this step. Code up something interesting and then wrap it in a function. At the most basic level, that's all a video.js plugin is. By convention, plugins take a hash of options as their first argument:

    function examplePlugin(options) {
      this.on('play', function(e) {
        console.log('playback has started!');
      });
    };

When it's activated, this will be the Video.js player your plugin is attached to. You can use anything you'd like in the Video.js API when you're writing a plugin: change the src, mess up the DOM, or listen for and emit your own events.

Step 2: Registering A Plugin

It's time to give the rest of the world the opportunity to be awed by your genius. When your plugin is loaded, it needs to let Video.js know this amazing new functionality is now available:

    videojs.plugin('examplePlugin', examplePlugin);

From this point on, your plugin will be added to the Video.js prototype and will show up as a property on every instance created. Make sure you choose a unique name that doesn't clash with any of the properties already in Video.js. Which leads us to...

Step 3: Using A Plugin

There are two ways to initialize a plugin. If you're creating your video tag dynamically, you can specify the plugins you'd like to initialize with it and any options you want to pass to them:

    videojs('vidId', {
      plugins: {
        examplePlugin: {
          exampleOption: true
        }
      }
    });

If you've already initialized your video tag, you can activate a plugin at any time by calling its setup function directly:

    var video = videojs('cool-vid');
    video.examplePlugin({ exampleOption: true });

That's it. Head on over to the Video.js wiki and add your plugin to the list so everyone else can check it out.

How should I use the Video.js icons in my plugin?

If you'd like to use any of the icons available in the Video.js icon set, please target them via the CSS class names instead of codepoints. The codepoints may change between versions of the font, so using the class names ensures that your plugin will stay up to date with any font changes.


import Button from '../../button';
import Component from '../../component';

/**
 * Button to skip forward a configurable amount of time
 * through a video. Renders in the control bar.
 *
 * e.g. options: {controlBar: {skipButtons: forward: 5}}
 *
 * @extends Button
 */
class SkipForward extends Button {
  constructor(player, options) {
    super(player, options);

    this.validOptions = [5, 10, 30];
    this.skipTime = this.getSkipForwardTime();

    if (this.skipTime && this.validOptions.includes(this.skipTime)) {
      this.controlText(this.localize('Skip forward {1} seconds', [this.skipTime]));
      this.show();
    } else {
      this.hide();
    }
  }

  getSkipForwardTime() {
    const playerOptions = this.options_.playerOptions;

    return playerOptions.controlBar && playerOptions.controlBar.skipButtons && playerOptions.controlBar.skipButtons.forward;
  }

  buildCSSClass() {
    return `vjs-skip-forward-${this.getSkipForwardTime()} ${super.buildCSSClass()}`;
  }

  /**
   * On click, skips forward in the duration/seekable range by a configurable amount of seconds.
   * If the time left in the duration/seekable range is less than the configured 'skip forward' time,
   * skips to end of duration/seekable range.
   *
   * Handle a click on a `SkipForward` button
   *
   * @param {EventTarget~Event} event
   *        The `click` event that caused this function
   *        to be called
   */
  handleClick(event) {
    const currentVideoTime = this.player_.currentTime();
    const liveTracker = this.player_.liveTracker;
    const duration = (liveTracker && liveTracker.isLive()) ? liveTracker.seekableEnd() : this.player_.duration();
    let newTime;

    if (currentVideoTime + this.skipTime <= duration) {
      newTime = currentVideoTime + this.skipTime;
    } else {
      newTime = duration;
    }

    this.player_.currentTime(newTime);
  }

  /**
   * Update control text on languagechange
   */
  handleLanguagechange() {
    this.controlText(this.localize('Skip forward {1} seconds', [this.skipTime]));
  }
}

Component.registerComponent('SkipForward', SkipForward);

export default SkipForward;


/* eslint-env qunit */
import CloseButton from '../../src/js/close-button';
import sinon from 'sinon';
import ModalDialog from '../../src/js/modal-dialog';
import * as Dom from '../../src/js/utils/dom';
import TestHelpers from './test-helpers';

const getMockEscapeEvent = () => ({
  which: 27,
  preventDefault() {},
  stopPropagation() {}
});

QUnit.module('ModalDialog', {

  beforeEach() {
    this.player = TestHelpers.makePlayer();
    this.modal = new ModalDialog(this.player, {temporary: false});
    this.el = this.modal.el();
  },

  afterEach() {
    this.player.dispose();
    this.modal.dispose();
    this.el = null;
  }
});

const tabTestHelper = function(assert, player) {
  return function(fromIndex, toIndex, shift = false) {
    const oldFocusableEls = ModalDialog.prototype.focusableEls_;
    const focus = function() {
      assert.equal(this.i, toIndex, `we should focus back on the ${toIndex} element, we got ${this.i}.`);
    };
    const els = [
      {i: 0, focus},
      {i: 1, focus},
      {i: 2, focus},
      {i: 3, focus}
    ];

    ModalDialog.prototype.focusableEls_ = () => els;

    const modal = new ModalDialog(player, {});
    const oldEl = modal.el_;

    modal.el_ = {
      querySelector() {
        const focusableEls = modal.focusableEls_();

        return focusableEls[fromIndex];
      }
    };

    let prevented = false;

    modal.handleKeyDown({
      which: 9,
      shiftKey: shift,
      preventDefault() {
        prevented = true;
      },
      stopPropagation() {
      }
    });

    if (!prevented) {
      const newIndex = shift ? fromIndex - 1 : fromIndex + 1;
      const newEl = modal.focusableEls_()[newIndex];

      if (newEl) {
        newEl.focus(newEl.i);
      }
    }

    modal.el_ = oldEl;
    modal.dispose();
    ModalDialog.prototype.focusableEls_ = oldFocusableEls;
  };
};

QUnit.test('should create the expected element', function(assert) {
  const elAssertions = TestHelpers.assertEl(assert, this.el, {
    tagName: 'div',
    classes: [
      'vjs-modal-dialog',
      'vjs-hidden'
    ],
    attrs: {
      'aria-describedby': this.modal.descEl_.id,
      'aria-hidden': 'true',
      'aria-label': this.modal.label(),
      'role': 'dialog'
    },
    props: {
      tabIndex: -1
    }
  });

  assert.expect(elAssertions.count);
  elAssertions();
});

QUnit.test('should create the expected description element', function(assert) {
  const elAssertions = TestHelpers.assertEl(assert, this.modal.descEl_, {
    tagName: 'p',
    innerHTML: this.modal.description(),
    classes: [
      'vjs-modal-dialog-description',
      'vjs-control-text'
    ],
    attrs: {
      id: this.el.getAttribute('aria-describedby')
    }
  });

  assert.expect(elAssertions.count);
  elAssertions();
});

QUnit.test('should create the expected contentEl', function(assert) {
  const elAssertions = TestHelpers.assertEl(assert, this.modal.contentEl(), {
    tagName: 'div',
    classes: [
      'vjs-modal-dialog-content'
    ],
    props: {
      parentNode: this.el
    }
  });

  assert.expect(elAssertions.count);
  elAssertions();
});

QUnit.test('should create a close button by default', function(assert) {
  const btn = this.modal.getChild('closeButton');

  // We only check the aspects of the button that relate to the modal. Other
  // aspects of the button (classes, etc) are tested in their appropriate test
  // module.
  assert.expect(2);
  assert.ok(btn instanceof CloseButton, 'close button is a CloseButton');
  assert.strictEqual(btn.el().parentNode, this.el, 'close button is a child of el');
});

QUnit.test('open() triggers events', function(assert) {
  const modal = this.modal;
  const beforeModalOpenSpy = sinon.spy(function() {
    assert.notOk(modal.opened(), 'modal is not opened before opening event');
  });

  const modalOpenSpy = sinon.spy(function() {
    assert.ok(modal.opened(), 'modal is opened on opening event');
  });

  assert.expect(4);

  modal.on('beforemodalopen', beforeModalOpenSpy);
  modal.on('modalopen', modalOpenSpy);
  modal.open();

  assert.strictEqual(beforeModalOpenSpy.callCount, 1, 'beforemodalopen spy was called');
  assert.strictEqual(modalOpenSpy.callCount, 1, 'modalopen spy was called');
});

QUnit.test('open() removes "vjs-hidden" class', function(assert) {
  assert.expect(2);
  assert.ok(this.modal.hasClass('vjs-hidden'), 'modal starts hidden');
  this.modal.open();
  assert.notOk(this.modal.hasClass('vjs-hidden'), 'modal is not hidden after opening');
});

QUnit.test('open() cannot be called on an opened modal', function(assert) {
  const spy = sinon.spy();

  this.modal.on('modalopen', spy);
  this.modal.open();
  this.modal.open();

  assert.expect(1);
  assert.strictEqual(spy.callCount, 1, 'modal was only opened once');
});

QUnit.test('close() triggers events', function(assert) {
  const modal = this.modal;
  const beforeModalCloseSpy = sinon.spy(function() {
    assert.ok(modal.opened(), 'modal is not closed before closing event');
  });

  const modalCloseSpy = sinon.spy(function() {
    assert.notOk(modal.opened(), 'modal is closed on closing event');
  });

  assert.expect(4);

  modal.on('beforemodalclose', beforeModalCloseSpy);
  modal.on('modalclose', modalCloseSpy);
  modal.open();
  modal.close();

  assert.strictEqual(beforeModalCloseSpy.callCount, 1, 'beforemodalclose spy was called');
  assert.strictEqual(modalCloseSpy.callCount, 1, 'modalclose spy was called');
});

QUnit.test('close() adds the "vjs-hidden" class', function(assert) {
  assert.expect(1);
  this.modal.open();
  this.modal.close();
  assert.ok(this.modal.hasClass('vjs-hidden'), 'modal is hidden upon close');
});

QUnit.test('pressing ESC triggers close(), but only when the modal is opened', function(assert) {
  const spy = sinon.spy();

  this.modal.on('modalclose', spy);
  this.modal.handleKeyDown(getMockEscapeEvent());
  assert.expect(2);
  assert.strictEqual(spy.callCount, 0, 'ESC did not close the closed modal');

  this.modal.open();
  this.modal.handleKeyDown(getMockEscapeEvent());
  assert.strictEqual(spy.callCount, 1, 'ESC closed the now-opened modal');
});

QUnit.test('close() cannot be called on a closed modal', function(assert) {
  const spy = sinon.spy();

  this.modal.on('modalclose', spy);
  this.modal.open();
  this.modal.close();
  this.modal.close();

  assert.expect(1);
  assert.strictEqual(spy.callCount, 1, 'modal was only closed once');
});

QUnit.test('open() pauses playback, close() resumes', function(assert) {
  const playSpy = sinon.spy();
  const pauseSpy = sinon.spy();

  // Quick and dirty; make it looks like the player is playing.
  this.player.paused = function() {
    return false;
  };

  this.player.play = function() {
    playSpy();
  };

  this.player.pause = function() {
    pauseSpy();
  };

  this.modal.open();

  assert.expect(2);
  assert.strictEqual(pauseSpy.callCount, 1, 'player is paused when the modal opens');

  this.modal.close();
  assert.strictEqual(playSpy.callCount, 1, 'player is resumed when the modal closes');
});

QUnit.test('open() does not pause, close() does not play() with pauseOnOpen set to false', function(assert) {
  const playSpy = sinon.spy();
  const pauseSpy = sinon.spy();

  // don't pause the video on modal open
  this.modal.options_.pauseOnOpen = false;

  // Quick and dirty; make it looks like the player is playing.
  this.player.paused = function() {
    return false;
  };

  this.player.play = function() {
    playSpy();
  };

  this.player.pause = function() {
    pauseSpy();
  };

  this.modal.open();

  assert.expect(2);
  assert.strictEqual(pauseSpy.callCount, 0, 'player remains playing when the modal opens');

  this.modal.close();
  assert.strictEqual(playSpy.callCount, 0, 'player is resumed when the modal closes');
});

QUnit.test('open() hides controls, close() shows controls', function(assert) {
  this.player.controls(true);
  this.modal.open();

  assert.expect(2);
  assert.notOk(this.player.controls_, 'controls are hidden');

  this.modal.close();
  assert.ok(this.player.controls_, 'controls are no longer hidden');
});

QUnit.test('open() hides controls, close() does not show controls if previously hidden', function(assert) {
  this.player.controls(false);
  this.modal.open();

  assert.expect(2);
  assert.notOk(this.player.controls_, 'controls are hidden');

  this.modal.close();
  assert.notOk(this.player.controls_, 'controls are still hidden');
});

QUnit.test('opened()', function(assert) {
  const openSpy = sinon.spy();
  const closeSpy = sinon.spy();

  assert.expect(4);
  assert.strictEqual(this.modal.opened(), false, 'the modal is closed');
  this.modal.open();
  assert.strictEqual(this.modal.opened(), true, 'the modal is open');

  this.modal.close();
  this.modal.on('modalopen', openSpy);
  this.modal.on('modalclose', closeSpy);
  this.modal.opened(true);

  this.modal.opened(true);
  this.modal.opened(false);
  assert.strictEqual(openSpy.callCount, 1, 'modal was opened only once');
  assert.strictEqual(closeSpy.callCount, 1, 'modal was closed only once');
});

QUnit.test('content()', function(assert) {
  assert.expect(3);
  assert.strictEqual(typeof this.modal.content(), 'undefined', 'no content by default');

  const content = this.modal.content(Dom.createEl());

  assert.ok(Dom.isEl(content), 'content was set from a single DOM element');

  assert.strictEqual(this.modal.content(null), null, 'content was nullified');
});

QUnit.test('fillWith()', function(assert) {
  const contentEl = this.modal.contentEl();
  const children = [Dom.createEl(), Dom.createEl(), Dom.createEl()];
  const beforeFillSpy = sinon.spy();
  const fillSpy = sinon.spy();

  children.forEach(function(el) {
    contentEl.appendChild(el);
  });

  this.modal.on('beforemodalfill', beforeFillSpy);
  this.modal.on('modalfill', fillSpy);
  this.modal.fillWith(children);

  assert.expect(5 + children.length);
  assert.strictEqual(contentEl.children.length, children.length, 'has the right number of children');

  children.forEach(function(el) {
    assert.strictEqual(el.parentNode, contentEl, 'new child appended');
  });

  assert.strictEqual(beforeFillSpy.callCount, 1, 'the "beforemodalfill" callback was called');
  assert.strictEqual(beforeFillSpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal');
  assert.strictEqual(fillSpy.callCount, 1, 'the "modalfill" callback was called');
  assert.strictEqual(fillSpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal');
});

QUnit.test('empty()', function(assert) {
  const beforeEmptySpy = sinon.spy();
  const emptySpy = sinon.spy();

  this.modal.fillWith([Dom.createEl(), Dom.createEl()]);
  this.modal.on('beforemodalempty', beforeEmptySpy);
  this.modal.on('modalempty', emptySpy);
  this.modal.empty();

  assert.expect(5);
  assert.strictEqual(this.modal.contentEl().children.length, 0, 'removed all `contentEl()` children');
  assert.strictEqual(beforeEmptySpy.callCount, 1, 'the "beforemodalempty" callback was called');
  assert.strictEqual(beforeEmptySpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal');
  assert.strictEqual(emptySpy.callCount, 1, 'the "modalempty" callback was called');
  assert.strictEqual(emptySpy.getCall(0).thisValue, this.modal, 'the value of "this" is the modal');
});

QUnit.test('closeable()', function(assert) {
  const initialCloseButton = this.modal.getChild('closeButton');

  assert.expect(8);
  assert.strictEqual(this.modal.closeable(), true, 'the modal is closed');

  this.modal.open();
  this.modal.closeable(false);
  assert.notOk(this.modal.getChild('closeButton'), 'the close button is no longer a child of the modal');
  assert.notOk(initialCloseButton.el(), 'the initial close button was disposed');

  this.modal.handleKeyDown(getMockEscapeEvent());
  assert.ok(this.modal.opened(), 'the modal was not closed by the ESC key');

  this.modal.close();
  assert.notOk(this.modal.opened(), 'the modal was closed programmatically');

  this.modal.open();
  this.modal.closeable(true);
  assert.ok(this.modal.getChild('closeButton'), 'a new close button was created');

  this.modal.getChild('closeButton').trigger('click');
  assert.notOk(this.modal.opened(), 'the modal was closed by the new close button');

  this.modal.open();
  this.modal.handleKeyDown(getMockEscapeEvent());
  assert.notOk(this.modal.opened(), 'the modal was closed by the ESC key');
});

QUnit.test('"content" option (fills on first open() invocation)', function(assert) {
  const modal = new ModalDialog(this.player, {
    content: Dom.createEl(),
    temporary: false
  });

  const spy = sinon.spy();

  modal.on('modalfill', spy);
  modal.open();
  modal.close();
  modal.open();

  assert.expect(3);
  assert.strictEqual(modal.content(), modal.options_.content, 'has the expected content');
  assert.strictEqual(spy.callCount, 1, 'auto-fills only once');
  assert.strictEqual(modal.contentEl().firstChild, modal.options_.content, 'has the expected content in the DOM');
  modal.dispose();
});

QUnit.test('"temporary" option', function(assert) {
  const temp = new ModalDialog(this.player, {temporary: true});
  const tempSpy = sinon.spy();
  const perm = new ModalDialog(this.player, {temporary: false});
  const permSpy = sinon.spy();

  temp.on('dispose', tempSpy);
  perm.on('dispose', permSpy);
  temp.open();
  temp.close();
  perm.open();
  perm.close();

  assert.expect(2);
  assert.strictEqual(tempSpy.callCount, 1, 'temporary modals are disposed');
  assert.strictEqual(permSpy.callCount, 0, 'permanent modals are not disposed');

  temp.dispose();
  perm.dispose();
});

QUnit.test('"fillAlways" option', function(assert) {
  const modal = new ModalDialog(this.player, {
    content: 'foo',
    fillAlways: true,
    temporary: false
  });

  const spy = sinon.spy();

  modal.on('modalfill', spy);
  modal.open();
  modal.close();
  modal.open();

  assert.expect(1);
  assert.strictEqual(spy.callCount, 2, 'the modal was filled on each open call');
  modal.dispose();
});

QUnit.test('"label" option', function(assert) {
  const label = 'foo';
  const modal = new ModalDialog(this.player, {label});

  assert.expect(1);
  assert.strictEqual(modal.el().getAttribute('aria-label'), label, 'uses the label as the aria-label');
  modal.dispose();
});

QUnit.test('"uncloseable" option', function(assert) {
  const modal = new ModalDialog(this.player, {
    temporary: false,
    uncloseable: true
  });

  const spy = sinon.spy();

  modal.on('modalclose', spy);

  assert.expect(3);
  assert.strictEqual(modal.closeable(), false, 'the modal is uncloseable');
  assert.notOk(modal.getChild('closeButton'), 'the close button is not present');

  modal.open();
  modal.handleKeyDown(getMockEscapeEvent());
  assert.strictEqual(spy.callCount, 0, 'ESC did not close the modal');
  modal.dispose();
});

QUnit.test('handleKeyDown traps tab focus', function(assert) {
  const tabTester = tabTestHelper(assert, this.player);

  // tabbing forward from first element to last and cycling back to first
  tabTester(0, 1, false);
  tabTester(1, 2, false);
  tabTester(2, 3, false);
  tabTester(3, 0, false);

  // tabbing backwards from last element to first and cycling back to last
  tabTester(3, 2, true);
  tabTester(2, 1, true);
  tabTester(1, 0, true);
  tabTester(0, 3, true);
});


// Mark a main-desc-menu-item (main + description) item with a trailing Audio Description icon

.video-js .vjs-audio-button + .vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before {

keep keep keep keep replace keep keep keep keep keep

// Mark a main-desc-menu-item (main + description) or description item with a trailing Audio Description icon

.video-js .vjs-audio-button + .vjs-menu .vjs-description-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before,

/**
 * @file audio-track-menu-item.js
 */
import MenuItem from '../../menu/menu-item.js';
import Component from '../../component.js';
import * as Dom from '../../utils/dom.js';

/**
 * An {@link AudioTrack} {@link MenuItem}
 *
 * @extends MenuItem
 */
class AudioTrackMenuItem extends MenuItem {

  /**
   * 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) {
    const track = options.track;
    const tracks = player.audioTracks();

    // Modify options for parent MenuItem class's init.
    options.label = track.label || track.language || 'Unknown';
    options.selected = track.enabled;

    super(player, options);

    this.track = track;

    this.addClass(`vjs-${track.kind}-menu-item`);

    const changeHandler = (...args) => {
      this.handleTracksChange.apply(this, args);
    };

    tracks.addEventListener('change', changeHandler);
    this.on('dispose', () => {
      tracks.removeEventListener('change', changeHandler);
    });
  }

  createEl(type, props, attrs) {
    const el = super.createEl(type, props, attrs);
    const parentSpan = el.querySelector('.vjs-menu-item-text');

    if (this.options_.track.kind === 'main-desc') {
      parentSpan.appendChild(Dom.createEl('span', {
        className: 'vjs-icon-placeholder'
      }, {
        'aria-hidden': true
      }));
      parentSpan.appendChild(Dom.createEl('span', {
        className: 'vjs-control-text',
        textContent: ' ' + this.localize('Descriptions')
      }));
    }

    return el;
  }

  /**
   * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
   * for more detailed information on what a click can be.
   *
   * @param {Event} [event]
   *        The `keydown`, `tap`, or `click` event that caused this function to be
   *        called.
   *
   * @listens tap
   * @listens click
   */
  handleClick(event) {
    super.handleClick(event);

    // the audio track list will automatically toggle other tracks
    // off for us.
    this.track.enabled = true;

    // when native audio tracks are used, we want to make sure that other tracks are turned off
    if (this.player_.tech_.featuresNativeAudioTracks) {
      const tracks = this.player_.audioTracks();

      for (let i = 0; i < tracks.length; i++) {
        const track = tracks[i];

        // skip the current track since we enabled it above
        if (track === this.track) {
          continue;
        }

        track.enabled = track === this.track;
      }
    }
  }

  /**
   * Handle any {@link AudioTrack} change.
   *
   * @param {Event} [event]
   *        The {@link AudioTrackList#change} event that caused this to run.
   *
   * @listens AudioTrackList#change
   */
  handleTracksChange(event) {
    this.selected(this.track.enabled);
  }
}

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


/**
 * @file plugin.js
 */
import evented from './mixins/evented';
import stateful from './mixins/stateful';
import * as Events from './utils/events';
import log from './utils/log';
import Player from './player';

/**
 * The base plugin name.
 *
 * @private
 * @constant
 * @type {string}
 */
const BASE_PLUGIN_NAME = 'plugin';

/**
 * The key on which a player's active plugins cache is stored.
 *
 * @private
 * @constant
 * @type     {string}
 */
const PLUGIN_CACHE_KEY = 'activePlugins_';

/**
 * Stores registered plugins in a private space.
 *
 * @private
 * @type    {Object}
 */
const pluginStorage = {};

/**
 * Reports whether or not a plugin has been registered.
 *
 * @private
 * @param   {string} name
 *          The name of a plugin.
 *
 * @return {boolean}
 *          Whether or not the plugin has been registered.
 */
const pluginExists = (name) => pluginStorage.hasOwnProperty(name);

/**
 * Get a single registered plugin by name.
 *
 * @private
 * @param   {string} name
 *          The name of a plugin.
 *
 * @return {typeof Plugin|Function|undefined}
 *          The plugin (or undefined).
 */
const getPlugin = (name) => pluginExists(name) ? pluginStorage[name] : undefined;

/**
 * Marks a plugin as "active" on a player.
 *
 * Also, ensures that the player has an object for tracking active plugins.
 *
 * @private
 * @param   {Player} player
 *          A Video.js player instance.
 *
 * @param   {string} name
 *          The name of a plugin.
 */
const markPluginAsActive = (player, name) => {
  player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
  player[PLUGIN_CACHE_KEY][name] = true;
};

/**
 * Triggers a pair of plugin setup events.
 *
 * @private
 * @param  {Player} player
 *         A Video.js player instance.
 *
 * @param  {Plugin~PluginEventHash} hash
 *         A plugin event hash.
 *
 * @param  {boolean} [before]
 *         If true, prefixes the event name with "before". In other words,
 *         use this to trigger "beforepluginsetup" instead of "pluginsetup".
 */
const triggerSetupEvent = (player, hash, before) => {
  const eventName = (before ? 'before' : '') + 'pluginsetup';

  player.trigger(eventName, hash);
  player.trigger(eventName + ':' + hash.name, hash);
};

/**
 * Takes a basic plugin function and returns a wrapper function which marks
 * on the player that the plugin has been activated.
 *
 * @private
 * @param   {string} name
 *          The name of the plugin.
 *
 * @param   {Function} plugin
 *          The basic plugin.
 *
 * @return {Function}
 *          A wrapper function for the given plugin.
 */
const createBasicPlugin = function(name, plugin) {
  const basicPluginWrapper = function() {

    // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
    // regardless, but we want the hash to be consistent with the hash provided
    // for advanced plugins.
    //
    // The only potentially counter-intuitive thing here is the `instance` in
    // the "pluginsetup" event is the value returned by the `plugin` function.
    triggerSetupEvent(this, {name, plugin, instance: null}, true);

    const instance = plugin.apply(this, arguments);

    markPluginAsActive(this, name);
    triggerSetupEvent(this, {name, plugin, instance});

    return instance;
  };

  Object.keys(plugin).forEach(function(prop) {
    basicPluginWrapper[prop] = plugin[prop];
  });

  return basicPluginWrapper;
};

/**
 * Takes a plugin sub-class and returns a factory function for generating
 * instances of it.
 *
 * This factory function will replace itself with an instance of the requested
 * sub-class of Plugin.
 *
 * @private
 * @param   {string} name
 *          The name of the plugin.
 *
 * @param   {Plugin} PluginSubClass
 *          The advanced plugin.
 *
 * @return {Function}
 */
const createPluginFactory = (name, PluginSubClass) => {

  // Add a `name` property to the plugin prototype so that each plugin can
  // refer to itself by name.
  PluginSubClass.prototype.name = name;

  return function(...args) {
    triggerSetupEvent(this, {name, plugin: PluginSubClass, instance: null}, true);

    const instance = new PluginSubClass(...[this, ...args]);

    // The plugin is replaced by a function that returns the current instance.
    this[name] = () => instance;

    triggerSetupEvent(this, instance.getEventHash());

    return instance;
  };
};

/**
 * Parent class for all advanced plugins.
 *
 * @mixes   module:evented~EventedMixin
 * @mixes   module:stateful~StatefulMixin
 * @fires   Player#beforepluginsetup
 * @fires   Player#beforepluginsetup:$name
 * @fires   Player#pluginsetup
 * @fires   Player#pluginsetup:$name
 * @listens Player#dispose
 * @throws  {Error}
 *          If attempting to instantiate the base {@link Plugin} class
 *          directly instead of via a sub-class.
 */
class Plugin {

  /**
   * Creates an instance of this class.
   *
   * Sub-classes should call `super` to ensure plugins are properly initialized.
   *
   * @param {Player} player
   *        A Video.js player instance.
   */
  constructor(player) {
    if (this.constructor === Plugin) {
      throw new Error('Plugin must be sub-classed; not directly instantiated.');
    }

    this.player = player;

    if (!this.log) {
      this.log = this.player.log.createLogger(this.name);
    }

    // Make this object evented, but remove the added `trigger` method so we
    // use the prototype version instead.
    evented(this);
    delete this.trigger;

    stateful(this, this.constructor.defaultState);
    markPluginAsActive(player, this.name);

    // Auto-bind the dispose method so we can use it as a listener and unbind
    // it later easily.
    this.dispose = this.dispose.bind(this);

    // If the player is disposed, dispose the plugin.
    player.on('dispose', this.dispose);
  }

  /**
   * Get the version of the plugin that was set on <pluginName>.VERSION
   */
  version() {
    return this.constructor.VERSION;
  }

  /**
   * Each event triggered by plugins includes a hash of additional data with
   * conventional properties.
   *
   * This returns that object or mutates an existing hash.
   *
   * @param   {Object} [hash={}]
   *          An object to be used as event an event hash.
   *
   * @return {Plugin~PluginEventHash}
   *          An event hash object with provided properties mixed-in.
   */
  getEventHash(hash = {}) {
    hash.name = this.name;
    hash.plugin = this.constructor;
    hash.instance = this;
    return hash;
  }

  /**
   * Triggers an event on the plugin object and overrides
   * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
   *
   * @param   {string|Object} event
   *          An event type or an object with a type property.
   *
   * @param   {Object} [hash={}]
   *          Additional data hash to merge with a
   *          {@link Plugin~PluginEventHash|PluginEventHash}.
   *
   * @return {boolean}
   *          Whether or not default was prevented.
   */
  trigger(event, hash = {}) {
    return Events.trigger(this.eventBusEl_, event, this.getEventHash(hash));
  }

  /**
   * Handles "statechanged" events on the plugin. No-op by default, override by
   * subclassing.
   *
   * @abstract
   * @param    {Event} e
   *           An event object provided by a "statechanged" event.
   *
   * @param    {Object} e.changes
   *           An object describing changes that occurred with the "statechanged"
   *           event.
   */
  handleStateChanged(e) {}

  /**
   * Disposes a plugin.
   *
   * Subclasses can override this if they want, but for the sake of safety,
   * it's probably best to subscribe the "dispose" event.
   *
   * @fires Plugin#dispose
   */
  dispose() {
    const {name, player} = this;

    /**
     * Signals that a advanced plugin is about to be disposed.
     *
     * @event Plugin#dispose
     * @type  {Event}
     */
    this.trigger('dispose');
    this.off();
    player.off('dispose', this.dispose);

    // Eliminate any possible sources of leaking memory by clearing up
    // references between the player and the plugin instance and nulling out
    // the plugin's state and replacing methods with a function that throws.
    player[PLUGIN_CACHE_KEY][name] = false;
    this.player = this.state = null;

    // Finally, replace the plugin name on the player with a new factory
    // function, so that the plugin is ready to be set up again.
    player[name] = createPluginFactory(name, pluginStorage[name]);
  }

  /**
   * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
   *
   * @param   {string|Function} plugin
   *          If a string, matches the name of a plugin. If a function, will be
   *          tested directly.
   *
   * @return {boolean}
   *          Whether or not a plugin is a basic plugin.
   */
  static isBasic(plugin) {
    const p = (typeof plugin === 'string') ? getPlugin(plugin) : plugin;

    return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
  }

  /**
   * Register a Video.js plugin.
   *
   * @param   {string} name
   *          The name of the plugin to be registered. Must be a string and
   *          must not match an existing plugin or a method on the `Player`
   *          prototype.
   *
   * @param   {typeof Plugin|Function} plugin
   *          A sub-class of `Plugin` or a function for basic plugins.
   *
   * @return {typeof Plugin|Function}
   *          For advanced plugins, a factory function for that plugin. For
   *          basic plugins, a wrapper function that initializes the plugin.
   */
  static registerPlugin(name, plugin) {
    if (typeof name !== 'string') {
      throw new Error(`Illegal plugin name, "${name}", must be a string, was ${typeof name}.`);
    }

    if (pluginExists(name)) {
      log.warn(`A plugin named "${name}" already exists. You may want to avoid re-registering plugins!`);
    } else if (Player.prototype.hasOwnProperty(name)) {
      throw new Error(`Illegal plugin name, "${name}", cannot share a name with an existing player method!`);
    }

    if (typeof plugin !== 'function') {
      throw new Error(`Illegal plugin for "${name}", must be a function, was ${typeof plugin}.`);
    }

    pluginStorage[name] = plugin;

    // Add a player prototype method for all sub-classed plugins (but not for
    // the base Plugin class).
    if (name !== BASE_PLUGIN_NAME) {
      if (Plugin.isBasic(plugin)) {
        Player.prototype[name] = createBasicPlugin(name, plugin);
      } else {
        Player.prototype[name] = createPluginFactory(name, plugin);
      }
    }

    return plugin;
  }

  /**
   * De-register a Video.js plugin.
   *
   * @param  {string} name
   *         The name of the plugin to be de-registered. Must be a string that
   *         matches an existing plugin.
   *
   * @throws {Error}
   *         If an attempt is made to de-register the base plugin.
   */
  static deregisterPlugin(name) {
    if (name === BASE_PLUGIN_NAME) {
      throw new Error('Cannot de-register base plugin.');
    }
    if (pluginExists(name)) {
      delete pluginStorage[name];
      delete Player.prototype[name];
    }
  }

  /**
   * Gets an object containing multiple Video.js plugins.
   *
   * @param   {Array} [names]
   *          If provided, should be an array of plugin names. Defaults to _all_
   *          plugin names.
   *
   * @return {Object|undefined}
   *          An object containing plugin(s) associated with their name(s) or
   *          `undefined` if no matching plugins exist).
   */
  static getPlugins(names = Object.keys(pluginStorage)) {
    let result;

    names.forEach(name => {
      const plugin = getPlugin(name);

      if (plugin) {
        result = result || {};
        result[name] = plugin;
      }
    });

    return result;
  }

  /**
   * Gets a plugin's version, if available
   *
   * @param   {string} name
   *          The name of a plugin.
   *
   * @return {string}
   *          The plugin's version or an empty string.
   */
  static getPluginVersion(name) {
    const plugin = getPlugin(name);

    return plugin && plugin.VERSION || '';
  }
}

/**
 * Gets a plugin by name if it exists.
 *
 * @static
 * @method   getPlugin
 * @memberOf Plugin
 * @param    {string} name
 *           The name of a plugin.
 *
 * @returns  {typeof Plugin|Function|undefined}
 *           The plugin (or `undefined`).
 */
Plugin.getPlugin = getPlugin;

/**
 * The name of the base plugin class as it is registered.
 *
 * @type {string}
 */
Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;

Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);

/**
 * Documented in player.js
 *
 * @ignore
 */
Player.prototype.usingPlugin = function(name) {
  return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
};

/**
 * Documented in player.js
 *
 * @ignore
 */
Player.prototype.hasPlugin = function(name) {
  return !!pluginExists(name);
};

export default Plugin;

/**
 * Signals that a plugin is about to be set up on a player.
 *
 * @event    Player#beforepluginsetup
 * @type     {Plugin~PluginEventHash}
 */

/**
 * Signals that a plugin is about to be set up on a player - by name. The name
 * is the name of the plugin.
 *
 * @event    Player#beforepluginsetup:$name
 * @type     {Plugin~PluginEventHash}
 */

/**
 * Signals that a plugin has just been set up on a player.
 *
 * @event    Player#pluginsetup
 * @type     {Plugin~PluginEventHash}
 */

/**
 * Signals that a plugin has just been set up on a player - by name. The name
 * is the name of the plugin.
 *
 * @event    Player#pluginsetup:$name
 * @type     {Plugin~PluginEventHash}
 */

/**
 * @typedef  {Object} Plugin~PluginEventHash
 *
 * @property {string} instance
 *           For basic plugins, the return value of the plugin function. For
 *           advanced plugins, the plugin instance on which the event is fired.
 *
 * @property {string} name
 *           The name of the plugin.
 *
 * @property {string} plugin
 *           For basic plugins, the plugin function. For advanced plugins, the
 *           plugin class/constructor.
 */


![Video.js logo][logo]

# [Video.js - HTML5 Video Player][vjs]

[![Build Status][travis-icon]][travis-link]
[![Coverage Status][coveralls-icon]][coveralls-link]
[![Greenkeeper badge](https://badges.greenkeeper.io/videojs/video.js.svg)](https://greenkeeper.io/)
[![Slack Status][slack-icon]][slack-link]

[![NPM][npm-icon]][npm-link]

> Video.js is a web video player built from the ground up for an HTML5 world. It supports HTML5 video and Media Source Extensions, as well as other playback techs like YouTube and Vimeo (through [plugins][plugins]). It supports video playback on desktops and mobile devices. This project was started mid 2010, and the player is now used on over ~~50,000~~ ~~100,000~~ ~~200,000~~ ~~400,000~~ [700,000 websites][builtwith].

## Table of Contents

* [Quick Start](#quick-start)
* [Contributing](#contributing)
* [Code of Conduct](#code-of-conduct)
* [License](#license)

## Quick Start

Thanks to the awesome folks over at [Fastly][fastly], there's a free, CDN hosted version of Video.js that anyone can use. Add these tags to your document's `<head>`:

```html
<link href="//vjs.zencdn.net/7.10.2/video-js.min.css" rel="stylesheet">
<script src="//vjs.zencdn.net/7.10.2/video.min.js"></script>
```

> For the latest version of video.js and URLs to use, check out the [Getting Started][getting-started] page on our website.

Video.js version 7 (and newer) CDN builds do not send any data to Google Analytics.

In older versions of Video.js (6 and earlier), in the `vjs.zencdn.net` CDN-hosted versions we include a [stripped down Google Analytics pixel](https://github.com/videojs/cdn/blob/master/src/analytics.js) that tracks a random sampling (currently 1%) of players loaded from the CDN. This allows us to see (roughly) what browsers are in use in the wild, along with other useful metrics such as OS and device. If you'd like to disable analytics, you can simply include the following global before including Video.js via the free CDN:

```html
<script>window.HELP_IMPROVE_VIDEOJS = false;</script>
```

Alternatively, you can include Video.js by getting it from [npm](https://videojs.com/getting-started/#install-via-npm), downloading from [GitHub releases](https://github.com/videojs/video.js/releases) or by including it via [unpkg](https://unpkg.com) or another JavaScript CDN like CDNjs. These releases _do not_ include Google Analytics tracking at all.

```html
<!-- unpkg : use the latest version of Video.js -->
<link href="https://unpkg.com/video.js/dist/video-js.min.css" rel="stylesheet">
<script src="https://unpkg.com/video.js/dist/video.min.js"></script>

<!-- unpkg : use a specific version of Video.js (change the version numbers as necessary) -->
<link href="https://unpkg.com/video.js@7.10.2/dist/video-js.min.css" rel="stylesheet">
<script src="https://unpkg.com/video.js@7.10.2/dist/video.min.js"></script>

<!-- cdnjs : use a specific version of Video.js (change the version numbers as necessary) -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.10.2/video-js.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.10.2/video.min.js"></script>
```

Next, using Video.js is as simple as creating a `<video>` element, but with an additional `data-setup` attribute. At a minimum, this attribute must have a value of `'{}'`, but it can include any Video.js [options][options] - just make sure it contains valid JSON!

```html
<video
    id="my-player"
    class="video-js"
    controls
    preload="auto"
    poster="//vjs.zencdn.net/v/oceans.png"
    data-setup='{}'>
  <source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4"></source>
  <source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm"></source>
  <source src="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg"></source>
  <p class="vjs-no-js">
    To view this video please enable JavaScript, and consider upgrading to a
    web browser that
    <a href="https://videojs.com/html5-video-support/" target="_blank">
      supports HTML5 video
    </a>
  </p>
</video>
```

When the page loads, Video.js will find this element and automatically setup a player in its place.

If you don't want to use automatic setup, you can leave off the `data-setup` attribute and initialize a `<video>` element manually using the `videojs` function:

```js
var player = videojs('my-player');
```

The `videojs` function also accepts an `options` object and a callback to be invoked
 when the player is ready:

```js
var options = {};

var player = videojs('my-player', options, function onPlayerReady() {
  videojs.log('Your player is ready!');

  // In this context, `this` is the player that was created by Video.js.
  this.play();

  // How about an event listener?
  this.on('ended', function() {
    videojs.log('Awww...over so soon?!');
  });
});
```

If you're ready to dive in, the [Getting Started][getting-started] page and [documentation][docs] are the best places to go for more information. If you get stuck, head over to our [Slack channel][slack-link]!

## Contributing

Video.js is a free and open source library, and we appreciate any help you're willing to give - whether it's fixing bugs, improving documentation, or suggesting new features. Check out the [contributing guide][contributing] for more!

_Video.js uses [BrowserStack][browserstack] for compatibility testing._

## [Code of Conduct][coc]

Please note that this project is released with a [Contributor Code of Conduct][coc]. By participating in this project you agree to abide by its terms.

## [License][license]

Video.js is [licensed][license] under the Apache License, Version 2.0.

[browserstack]: https://browserstack.com

[builtwith]: https://trends.builtwith.com/media/VideoJS

[contributing]: CONTRIBUTING.md

[coveralls-icon]: https://coveralls.io/repos/github/videojs/video.js/badge.svg?branch=main

[coveralls-link]: https://coveralls.io/github/videojs/video.js?branch=main

[docs]: https://docs.videojs.com

[fastly]: https://www.fastly.com/

[getting-started]: https://videojs.com/getting-started/

[license]: LICENSE

[logo]: https://videojs.com/logo-white.png

[npm-icon]: https://nodei.co/npm/video.js.png?downloads=true&downloadRank=true

[npm-link]: https://nodei.co/npm/video.js/

[options]: docs/guides/options.md

[plugins]: https://videojs.com/plugins/

[slack-icon]: http://slack.videojs.com/badge.svg

[slack-link]: http://slack.videojs.com

[travis-icon]: https://travis-ci.org/videojs/video.js.svg?branch=main

[travis-link]: https://travis-ci.org/videojs/video.js

[vjs]: https://videojs.com

[coc]: CODE_OF_CONDUCT.md


/* eslint-disable no-console */

const fs = require('fs');
const path = require('path');
const sh = require('shelljs');

const source = require('../lang/en.json');
const table = require('markdown-table');
const tableRegex = /(<!-- START langtable -->)(.|\n)*(<!-- END langtable -->)/;

let doc = fs.readFileSync(path.join(__dirname, '..', 'docs', 'translations-needed.md'), 'utf8');
const tableData = [['Language file', 'Missing translations']];

const filepaths = sh.find(path.join(__dirname, '..', 'lang', '**', '!(zh-Hans|zh-Hant)*.json'));

filepaths.forEach((filepath) => {
  const filename = path.basename(filepath);

  if (filename === 'en.json') {
    return;
  }

  const target = require(filepath);

  // Special case for English, the assumption being that since the keys are English only
  // a  few strings need to be altered for regional differences, e.g. en-GB.
  if (filename.startsWith('en-')) {
    console.log(`${filename} English - should be manually checked.`);
    tableData.push([`${filename} (has ${Object.keys(target).length})`, 'Needs manual checking. Can safely use most default strings.']);
    return;
  }

  const missing = [];

  for (const string in source) {
    if (!target[string]) {
      console.log(`${filename} missing "${string}"`);
      missing.push(string);
    }
  }
  if (missing.length > 0) {
    console.error(`${filename} is missing ${missing.length} translations.`);
    tableData.push([`${filename} (missing ${missing.length})`, missing[0]]);
    for (let i = 1; i < missing.length; i++) {
      tableData.push(['', missing[i]]);
    }
  } else {
    console.log(`${filename} is up to date.`);
    tableData.push([`${filename} (Complete)`, '']);
  }
});

doc = doc.replace(tableRegex, '$1\n' + table(tableData) + '\n$3');
fs.writeFileSync(path.join(__dirname, '..', 'docs', 'translations-needed.md'), doc, 'utf8');


const el = super.createEl(type, props, attrs);

const parentSpan = el.querySelector('.vjs-menu-item-text');

if (this.options_.track.kind === 'main-desc') {

keep keep keep keep keep replace keep keep keep keep keep

if (['main-desc', 'description'].indexOf(this.options_.track.kind) >= 0) {


/* eslint-env qunit */
import window from 'global/window';
import MediaError from '../../src/js/media-error';

const isModernBrowser = window.MediaError && Object.create && Object.defineProperty;

/**
 * Creates a real native MediaError object.
 *
 * @param  {number} code
 * @param  {string} [message]
 * @return {MediaError}
 */
const createNativeMediaError = (code, message) => {
  const err = Object.create(window.MediaError);

  Object.defineProperty(err, 'code', {value: code});

  if (message) {
    err.message = message;
  }

  return err;
};

QUnit.module('MediaError');

QUnit.test('can be constructed from a number', function(assert) {
  const mediaError = new MediaError(1);

  assert.strictEqual(mediaError.code, 1);
  assert.strictEqual(mediaError.message, MediaError.defaultMessages['1']);
});

QUnit.test('can be constructed from a string', function(assert) {
  const mediaError = new MediaError('hello, world');

  assert.strictEqual(mediaError.code, 0);
  assert.strictEqual(mediaError.message, 'hello, world');
});

QUnit.test('can be constructed from an object', function(assert) {
  const mediaError = new MediaError({code: 2});
  const mediaErrorMsg = new MediaError({code: 2, message: 'hello, world'});

  assert.strictEqual(mediaError.code, 2);
  assert.strictEqual(mediaError.message, MediaError.defaultMessages['2']);
  assert.strictEqual(mediaErrorMsg.code, 2);
  assert.strictEqual(mediaErrorMsg.message, 'hello, world');
});

if (isModernBrowser) {
  QUnit.test('can be constructed from a native MediaError object', function(assert) {
    const mediaError = new MediaError(createNativeMediaError(3));
    const mediaErrorMsg = new MediaError(createNativeMediaError(4, 'hello, world'));

    assert.strictEqual(mediaError.code, 3);
    assert.strictEqual(mediaError.message, MediaError.defaultMessages['3']);
    assert.strictEqual(mediaErrorMsg.code, 4);
    assert.strictEqual(mediaErrorMsg.message, 'hello, world');
  });
}

QUnit.test('can be constructed redundantly', function(assert) {
  const mediaError = new MediaError(2);
  const redundantMediaError = new MediaError(mediaError);

  assert.strictEqual(redundantMediaError, mediaError);
});


<!DOCTYPE html><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>ChaptersTrackMenuItem</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/ChaptersTrackMenuItem.html">

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

API Index

ChaptersTrackMenuItem Methods

Class Methods

handleClick

update

Inherited Methods from MenuItem

createEl

selected

Inherited Methods from Button

addChild

handleKeyPress

Inherited Methods from ClickableComponent

buildCSSClass

controlText

handleBlur

handleFocus

Inherited Methods from Component

$

$$

addClass

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

ChaptersTrackMenuItem

The chapter track menu item

DEFINED IN: chapters-track-menu-item.js line number: 8

EXTENDS: menu-item.js

Constructor

ChaptersTrackMenuItem( player,[options] )

Parameters

name	Type	Required	Description

player	Player|Object

options	Object

Methods

handleClick()

Handle click on menu item

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/text-track-controls/chapters-track-menu-item.js line number: 33

[back to top]

update()

Update chapter menu item

Defined in https://github.com/videojs/video.js/blob/master/src/js/control-bar/text-track-controls/chapters-track-menu-item.js line number: 44

[back to top]

createEl( [type], [props] )

Create the component's DOM element

Parameters

name	Type	Required	Description

type	String	no	Desc

props	Object	no	Desc

Defined in https://github.com/videojs/video.js/blob/master/src/js/menu/menu-item.js line number: 34

[back to top]

selected( selected )

Set this menu item as selected or not

Parameters

name	Type	Required	Description

selected	Boolean

Defined in https://github.com/videojs/video.js/blob/master/src/js/menu/menu-item.js line number: 59

[back to top]

addChild( child, [options] ) (deprecated)

Adds a child component inside this button

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/button.js line number: 57

[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]

buildCSSClass()

Allows sub components to stack CSS class names

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

[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]

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]

$( 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]

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]

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]