/**
 * Player Component - Base class for all UI objects
 *
 * @file component.js
 */
import window from 'global/window';
import evented from './mixins/evented';
import stateful from './mixins/stateful';
import * as Dom from './utils/dom.js';
import DomData from './utils/dom-data';
import * as Fn from './utils/fn.js';
import * as Guid from './utils/guid.js';
import {toTitleCase, toLowerCase} from './utils/string-cases.js';
import mergeOptions from './utils/merge-options.js';
import computedStyle from './utils/computed-style';
import Map from './utils/map.js';
import Set from './utils/set.js';

/**
 * Base class for all UI Components.
 * Components are UI objects which represent both a javascript object and an element
 * in the DOM. They can be children of other components, and can have
 * children themselves.
 *
 * Components can also use methods from {@link EventTarget}
 */
class Component {

  /**
   * A callback that is called when a component is ready. Does not have any
   * paramters and any callback value will be ignored.
   *
   * @callback Component~ReadyCallback
   * @this Component
   */

  /**
   * Creates an instance of this class.
   *
   * @param {Player} player
   *        The `Player` that this class should be attached to.
   *
   * @param {Object} [options]
   *        The key/value store of player options.
   *
   * @param {Object[]} [options.children]
   *        An array of children objects to intialize this component with. Children objects have
   *        a name property that will be used if more than one component of the same type needs to be
   *        added.
   *
   * @param {Component~ReadyCallback} [ready]
   *        Function that gets called when the `Component` is ready.
   */
  constructor(player, options, ready) {

    // The component might be the player itself and we can't pass `this` to super
    if (!player && this.play) {
      this.player_ = player = this; // eslint-disable-line
    } else {
      this.player_ = player;
    }

    this.isDisposed_ = false;

    // Hold the reference to the parent component via `addChild` method
    this.parentComponent_ = null;

    // Make a copy of prototype.options_ to protect against overriding defaults
    this.options_ = mergeOptions({}, this.options_);

    // Updated options with supplied options
    options = this.options_ = mergeOptions(this.options_, options);

    // Get ID from options or options element if one is supplied
    this.id_ = options.id || (options.el && options.el.id);

    // If there was no ID from the options, generate one
    if (!this.id_) {
      // Don't require the player ID function in the case of mock players
      const id = player && player.id && player.id() || 'no_player';

      this.id_ = `${id}_component_${Guid.newGUID()}`;
    }

    this.name_ = options.name || null;

    // Create element if one wasn't provided in options
    if (options.el) {
      this.el_ = options.el;
    } else if (options.createEl !== false) {
      this.el_ = this.createEl();
    }

    // if evented is anything except false, we want to mixin in evented
    if (options.evented !== false) {
      // Make this an evented object and use `el_`, if available, as its event bus
      evented(this, {eventBusKey: this.el_ ? 'el_' : null});

      this.handleLanguagechange = this.handleLanguagechange.bind(this);
      this.on(this.player_, 'languagechange', this.handleLanguagechange);
    }
    stateful(this, this.constructor.defaultState);

    this.children_ = [];
    this.childIndex_ = {};
    this.childNameIndex_ = {};

    this.setTimeoutIds_ = new Set();
    this.setIntervalIds_ = new Set();
    this.rafIds_ = new Set();
    this.namedRafs_ = new Map();
    this.clearingTimersOnDispose_ = false;

    // Add any child components in options
    if (options.initChildren !== false) {
      this.initChildren();
    }

    // Don't want to trigger ready here or it will go before init is actually
    // finished for all children that run this constructor
    this.ready(ready);

    if (options.reportTouchActivity !== false) {
      this.enableTouchActivity();
    }

  }

  /**
   * Dispose of the `Component` and all child components.
   *
   * @fires Component#dispose
   */
  dispose() {

    // Bail out if the component has already been disposed.
    if (this.isDisposed_) {
      return;
    }

    if (this.readyQueue_) {
      this.readyQueue_.length = 0;
    }

    /**
     * Triggered when a `Component` is disposed.
     *
     * @event Component#dispose
     * @type {EventTarget~Event}
     *
     * @property {boolean} [bubbles=false]
     *           set to false so that the dispose event does not
     *           bubble up
     */
    this.trigger({type: 'dispose', bubbles: false});

    this.isDisposed_ = true;

    // Dispose all children.
    if (this.children_) {
      for (let i = this.children_.length - 1; i >= 0; i--) {
        if (this.children_[i].dispose) {
          this.children_[i].dispose();
        }
      }
    }

    // Delete child references
    this.children_ = null;
    this.childIndex_ = null;
    this.childNameIndex_ = null;

    this.parentComponent_ = null;

    if (this.el_) {
      // Remove element from DOM
      if (this.el_.parentNode) {
        this.el_.parentNode.removeChild(this.el_);
      }

      if (DomData.has(this.el_)) {
        DomData.delete(this.el_);
      }
      this.el_ = null;
    }

    // remove reference to the player after disposing of the element
    this.player_ = null;
  }

  /**
   * Determine whether or not this component has been disposed.
   *
   * @return {boolean}
   *         If the component has been disposed, will be `true`. Otherwise, `false`.
   */
  isDisposed() {
    return Boolean(this.isDisposed_);
  }

  /**
   * Return the {@link Player} that the `Component` has attached to.
   *
   * @return {Player}
   *         The player that this `Component` has attached to.
   */
  player() {
    return this.player_;
  }

  /**
   * Deep merge of options objects with new options.
   * > Note: When both `obj` and `options` contain properties whose values are objects.
   *         The two properties get merged using {@link module:mergeOptions}
   *
   * @param {Object} obj
   *        The object that contains new options.
   *
   * @return {Object}
   *         A new object of `this.options_` and `obj` merged together.
   */
  options(obj) {
    if (!obj) {
      return this.options_;
    }

    this.options_ = mergeOptions(this.options_, obj);
    return this.options_;
  }

  /**
   * Get the `Component`s DOM element
   *
   * @return {Element}
   *         The DOM element for this `Component`.
   */
  el() {
    return this.el_;
  }

  /**
   * Create the `Component`s DOM element.
   *
   * @param {string} [tagName]
   *        Element's DOM node type. e.g. 'div'
   *
   * @param {Object} [properties]
   *        An object of properties that should be set.
   *
   * @param {Object} [attributes]
   *        An object of attributes that should be set.
   *
   * @return {Element}
   *         The element that gets created.
   */
  createEl(tagName, properties, attributes) {
    return Dom.createEl(tagName, properties, attributes);
  }

  /**
   * Localize a string given the string in english.
   *
   * If tokens are provided, it'll try and run a simple token replacement on the provided string.
   * The tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array.
   *
   * If a `defaultValue` is provided, it'll use that over `string`,
   * if a value isn't found in provided language files.
   * This is useful if you want to have a descriptive key for token replacement
   * but have a succinct localized string and not require `en.json` to be included.
   *
   * Currently, it is used for the progress bar timing.
   * ```js
   * {
   *   "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
   * }
   * ```
   * It is then used like so:
   * ```js
   * this.localize('progress bar timing: currentTime={1} duration{2}',
   *               [this.player_.currentTime(), this.player_.duration()],
   *               '{1} of {2}');
   * ```
   *
   * Which outputs something like: `01:23 of 24:56`.
   *
   *
   * @param {string} string
   *        The string to localize and the key to lookup in the language files.
   * @param {string[]} [tokens]
   *        If the current item has token replacements, provide the tokens here.
   * @param {string} [defaultValue]
   *        Defaults to `string`. Can be a default value to use for token replacement
   *        if the lookup key is needed to be separate.
   *
   * @return {string}
   *         The localized string or if no localization exists the english string.
   */
  localize(string, tokens, defaultValue = string) {

    const code = this.player_.language && this.player_.language();
    const languages = this.player_.languages && this.player_.languages();
    const language = languages && languages[code];
    const primaryCode = code && code.split('-')[0];
    const primaryLang = languages && languages[primaryCode];

    let localizedString = defaultValue;

    if (language && language[string]) {
      localizedString = language[string];
    } else if (primaryLang && primaryLang[string]) {
      localizedString = primaryLang[string];
    }

    if (tokens) {
      localizedString = localizedString.replace(/\{(\d+)\}/g, function(match, index) {
        const value = tokens[index - 1];
        let ret = value;

        if (typeof value === 'undefined') {
          ret = match;
        }

        return ret;
      });
    }

    return localizedString;
  }

  /**
   * Handles language change for the player in components. Should be overriden by sub-components.
   *
   * @abstract
   */
  handleLanguagechange() {}

  /**
   * Return the `Component`s DOM element. This is where children get inserted.
   * This will usually be the the same as the element returned in {@link Component#el}.
   *
   * @return {Element}
   *         The content element for this `Component`.
   */
  contentEl() {
    return this.contentEl_ || this.el_;
  }

  /**
   * Get this `Component`s ID
   *
   * @return {string}
   *         The id of this `Component`
   */
  id() {
    return this.id_;
  }

  /**
   * Get the `Component`s name. The name gets used to reference the `Component`
   * and is set during registration.
   *
   * @return {string}
   *         The name of this `Component`.
   */
  name() {
    return this.name_;
  }

  /**
   * Get an array of all child components
   *
   * @return {Array}
   *         The children
   */
  children() {
    return this.children_;
  }

  /**
   * Returns the child `Component` with the given `id`.
   *
   * @param {string} id
   *        The id of the child `Component` to get.
   *
   * @return {Component|undefined}
   *         The child `Component` with the given `id` or undefined.
   */
  getChildById(id) {
    return this.childIndex_[id];
  }

  /**
   * Returns the child `Component` with the given `name`.
   *
   * @param {string} name
   *        The name of the child `Component` to get.
   *
   * @return {Component|undefined}
   *         The child `Component` with the given `name` or undefined.
   */
  getChild(name) {
    if (!name) {
      return;
    }

    return this.childNameIndex_[name];
  }

  /**
   * Returns the descendant `Component` following the givent
   * descendant `names`. For instance ['foo', 'bar', 'baz'] would
   * try to get 'foo' on the current component, 'bar' on the 'foo'
   * component and 'baz' on the 'bar' component and return undefined
   * if any of those don't exist.
   *
   * @param {...string[]|...string} names
   *        The name of the child `Component` to get.
   *
   * @return {Component|undefined}
   *         The descendant `Component` following the given descendant
   *         `names` or undefined.
   */
  getDescendant(...names) {
    // flatten array argument into the main array
    names = names.reduce((acc, n) => acc.concat(n), []);

    let currentChild = this;

    for (let i = 0; i < names.length; i++) {
      currentChild = currentChild.getChild(names[i]);

      if (!currentChild || !currentChild.getChild) {
        return;
      }
    }

    return currentChild;
  }

  /**
   * Add a child `Component` inside the current `Component`.
   *
   *
   * @param {string|Component} child
   *        The name or instance of a child to add.
   *
   * @param {Object} [options={}]
   *        The key/value store of options that will get passed to children of
   *        the child.
   *
   * @param {number} [index=this.children_.length]
   *        The index to attempt to add a child into.
   *
   * @return {Component}
   *         The `Component` that gets added as a child. When using a string the
   *         `Component` will get created by this process.
   */
  addChild(child, options = {}, index = this.children_.length) {
    let component;
    let componentName;

    // If child is a string, create component with options
    if (typeof child === 'string') {
      componentName = toTitleCase(child);

      const componentClassName = options.componentClass || componentName;

      // Set name through options
      options.name = componentName;

      // Create a new object & element for this controls set
      // If there's no .player_, this is a player
      const ComponentClass = Component.getComponent(componentClassName);

      if (!ComponentClass) {
        throw new Error(`Component ${componentClassName} does not exist`);
      }

      // data stored directly on the videojs object may be
      // misidentified as a component to retain
      // backwards-compatibility with 4.x. check to make sure the
      // component class can be instantiated.
      if (typeof ComponentClass !== 'function') {
        return null;
      }

      component = new ComponentClass(this.player_ || this, options);

    // child is a component instance
    } else {
      component = child;
    }

    if (component.parentComponent_) {
      component.parentComponent_.removeChild(component);
    }
    this.children_.splice(index, 0, component);
    component.parentComponent_ = this;

    if (typeof component.id === 'function') {
      this.childIndex_[component.id()] = component;
    }

    // If a name wasn't used to create the component, check if we can use the
    // name function of the component
    componentName = componentName || (component.name && toTitleCase(component.name()));

    if (componentName) {
      this.childNameIndex_[componentName] = component;
      this.childNameIndex_[toLowerCase(componentName)] = component;
    }

    // Add the UI object's element to the container div (box)
    // Having an element is not required
    if (typeof component.el === 'function' && component.el()) {
      // If inserting before a component, insert before that component's element
      let refNode = null;

      if (this.children_[index + 1]) {
        // Most children are components, but the video tech is an HTML element
        if (this.children_[index + 1].el_) {
          refNode = this.children_[index + 1].el_;
        } else if (Dom.isEl(this.children_[index + 1])) {
          refNode = this.children_[index + 1];
        }
      }

      this.contentEl().insertBefore(component.el(), refNode);
    }

    // Return so it can stored on parent object if desired.
    return component;
  }

  /**
   * Remove a child `Component` from this `Component`s list of children. Also removes
   * the child `Component`s element from this `Component`s element.
   *
   * @param {Component} component
   *        The child `Component` to remove.
   */
  removeChild(component) {
    if (typeof component === 'string') {
      component = this.getChild(component);
    }

    if (!component || !this.children_) {
      return;
    }

    let childFound = false;

    for (let i = this.children_.length - 1; i >= 0; i--) {
      if (this.children_[i] === component) {
        childFound = true;
        this.children_.splice(i, 1);
        break;
      }
    }

    if (!childFound) {
      return;
    }

    component.parentComponent_ = null;

    this.childIndex_[component.id()] = null;
    this.childNameIndex_[toTitleCase(component.name())] = null;
    this.childNameIndex_[toLowerCase(component.name())] = null;

    const compEl = component.el();

    if (compEl && compEl.parentNode === this.contentEl()) {
      this.contentEl().removeChild(component.el());
    }
  }

  /**
   * Add and initialize default child `Component`s based upon options.
   */
  initChildren() {
    const children = this.options_.children;

    if (children) {
      // `this` is `parent`
      const parentOptions = this.options_;

      const handleAdd = (child) => {
        const name = child.name;
        let opts = child.opts;

        // Allow options for children to be set at the parent options
        // e.g. videojs(id, { controlBar: false });
        // instead of videojs(id, { children: { controlBar: false });
        if (parentOptions[name] !== undefined) {
          opts = parentOptions[name];
        }

        // Allow for disabling default components
        // e.g. options['children']['posterImage'] = false
        if (opts === false) {
          return;
        }

        // Allow options to be passed as a simple boolean if no configuration
        // is necessary.
        if (opts === true) {
          opts = {};
        }

        // We also want to pass the original player options
        // to each component as well so they don't need to
        // reach back into the player for options later.
        opts.playerOptions = this.options_.playerOptions;

        // Create and add the child component.
        // Add a direct reference to the child by name on the parent instance.
        // If two of the same component are used, different names should be supplied
        // for each
        const newChild = this.addChild(name, opts);

        if (newChild) {
          this[name] = newChild;
        }
      };

      // Allow for an array of children details to passed in the options
      let workingChildren;
      const Tech = Component.getComponent('Tech');

      if (Array.isArray(children)) {
        workingChildren = children;
      } else {
        workingChildren = Object.keys(children);
      }

      workingChildren
      // children that are in this.options_ but also in workingChildren  would
      // give us extra children we do not want. So, we want to filter them out.
        .concat(Object.keys(this.options_)
          .filter(function(child) {
            return !workingChildren.some(function(wchild) {
              if (typeof wchild === 'string') {
                return child === wchild;
              }
              return child === wchild.name;
            });
          }))
        .map((child) => {
          let name;
          let opts;

          if (typeof child === 'string') {
            name = child;
            opts = children[name] || this.options_[name] || {};
          } else {
            name = child.name;
            opts = child;
          }

          return {name, opts};
        })
        .filter((child) => {
        // we have to make sure that child.name isn't in the techOrder since
        // techs are registerd as Components but can't aren't compatible
        // See https://github.com/videojs/video.js/issues/2772
          const c = Component.getComponent(child.opts.componentClass ||
                                       toTitleCase(child.name));

          return c && !Tech.isTech(c);
        })
        .forEach(handleAdd);
    }
  }

  /**
   * Builds the default DOM class name. Should be overriden by sub-components.
   *
   * @return {string}
   *         The DOM class name for this object.
   *
   * @abstract
   */
  buildCSSClass() {
    // Child classes can include a function that does:
    // return 'CLASS NAME' + this._super();
    return '';
  }

  /**
   * 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.
   *
   * @return {Component}
   *         Returns itself; method can be chained.
   */
  ready(fn, sync = false) {
    if (!fn) {
      return;
    }

    if (!this.isReady_) {
      this.readyQueue_ = this.readyQueue_ || [];
      this.readyQueue_.push(fn);
      return;
    }

    if (sync) {
      fn.call(this);
    } else {
      // Call the function asynchronously by default for consistency
      this.setTimeout(fn, 1);
    }
  }

  /**
   * Trigger all the ready listeners for this `Component`.
   *
   * @fires Component#ready
   */
  triggerReady() {
    this.isReady_ = true;

    // Ensure ready is triggered asynchronously
    this.setTimeout(function() {
      const readyQueue = this.readyQueue_;

      // Reset Ready Queue
      this.readyQueue_ = [];

      if (readyQueue && readyQueue.length > 0) {
        readyQueue.forEach(function(fn) {
          fn.call(this);
        }, this);
      }

      // Allow for using event listeners also
      /**
       * Triggered when a `Component` is ready.
       *
       * @event Component#ready
       * @type {EventTarget~Event}
       */
      this.trigger('ready');
    }, 1);
  }

  /**
   * Find a single DOM element matching a `selector`. This can be within the `Component`s
   * `contentEl()` or another custom context.
   *
   * @param {string} selector
   *        A valid CSS selector, which will be passed to `querySelector`.
   *
   * @param {Element|string} [context=this.contentEl()]
   *        A DOM element within which to query. Can also be a selector string in
   *        which case the first matching element will get used as context. If
   *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
   *        nothing it falls back to `document`.
   *
   * @return {Element|null}
   *         the dom element that was found, or null
   *
   * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
   */
  $(selector, context) {
    return Dom.$(selector, context || this.contentEl());
  }

  /**
   * Finds all DOM element matching a `selector`. This can be within the `Component`s
   * `contentEl()` or another custom context.
   *
   * @param {string} selector
   *        A valid CSS selector, which will be passed to `querySelectorAll`.
   *
   * @param {Element|string} [context=this.contentEl()]
   *        A DOM element within which to query. Can also be a selector string in
   *        which case the first matching element will get used as context. If
   *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
   *        nothing it falls back to `document`.
   *
   * @return {NodeList}
   *         a list of dom elements that were found
   *
   * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
   */
  $$(selector, context) {
    return Dom.$$(selector, context || this.contentEl());
  }

  /**
   * Check if a component's element has a CSS class name.
   *
   * @param {string} classToCheck
   *        CSS class name to check.
   *
   * @return {boolean}
   *         - True if the `Component` has the class.
   *         - False if the `Component` does not have the class`
   */
  hasClass(classToCheck) {
    return Dom.hasClass(this.el_, classToCheck);
  }

  /**
   * Add a CSS class name to the `Component`s element.
   *
   * @param {string} classToAdd
   *        CSS class name to add
   */
  addClass(classToAdd) {
    Dom.addClass(this.el_, classToAdd);
  }

  /**
   * Remove a CSS class name from the `Component`s element.
   *
   * @param {string} classToRemove
   *        CSS class name to remove
   */
  removeClass(classToRemove) {
    Dom.removeClass(this.el_, classToRemove);
  }

  /**
   * Add or remove a CSS class name from the component's element.
   * - `classToToggle` gets added when {@link Component#hasClass} would return false.
   * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
   *
   * @param  {string} classToToggle
   *         The class to add or remove based on (@link Component#hasClass}
   *
   * @param  {boolean|Dom~predicate} [predicate]
   *         An {@link Dom~predicate} function or a boolean
   */
  toggleClass(classToToggle, predicate) {
    Dom.toggleClass(this.el_, classToToggle, predicate);
  }

  /**
   * Show the `Component`s element if it is hidden by removing the
   * 'vjs-hidden' class name from it.
   */
  show() {
    this.removeClass('vjs-hidden');
  }

  /**
   * Hide the `Component`s element if it is currently showing by adding the
   * 'vjs-hidden` class name to it.
   */
  hide() {
    this.addClass('vjs-hidden');
  }

  /**
   * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
   * class name to it. Used during fadeIn/fadeOut.
   *
   * @private
   */
  lockShowing() {
    this.addClass('vjs-lock-showing');
  }

  /**
   * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
   * class name from it. Used during fadeIn/fadeOut.
   *
   * @private
   */
  unlockShowing() {
    this.removeClass('vjs-lock-showing');
  }

  /**
   * Get the value of an attribute on the `Component`s element.
   *
   * @param {string} attribute
   *        Name of the attribute to get the value from.
   *
   * @return {string|null}
   *         - The value of the attribute that was asked for.
   *         - Can be an empty string on some browsers if the attribute does not exist
   *           or has no value
   *         - Most browsers will return null if the attibute does not exist or has
   *           no value.
   *
   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
   */
  getAttribute(attribute) {
    return Dom.getAttribute(this.el_, attribute);
  }

  /**
   * Set the value of an attribute on the `Component`'s element
   *
   * @param {string} attribute
   *        Name of the attribute to set.
   *
   * @param {string} value
   *        Value to set the attribute to.
   *
   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
   */
  setAttribute(attribute, value) {
    Dom.setAttribute(this.el_, attribute, value);
  }

  /**
   * Remove an attribute from the `Component`s element.
   *
   * @param {string} attribute
   *        Name of the attribute to remove.
   *
   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
   */
  removeAttribute(attribute) {
    Dom.removeAttribute(this.el_, attribute);
  }

  /**
   * Get or set the width of the component based upon the CSS styles.
   * See {@link Component#dimension} for more detailed information.
   *
   * @param {number|string} [num]
   *        The width that you want to set postfixed with '%', 'px' or nothing.
   *
   * @param {boolean} [skipListeners]
   *        Skip the componentresize event trigger
   *
   * @return {number|string}
   *         The width when getting, zero if there is no width. Can be a string
   *           postpixed with '%' or 'px'.
   */
  width(num, skipListeners) {
    return this.dimension('width', num, skipListeners);
  }

  /**
   * Get or set the height of the component based upon the CSS styles.
   * See {@link Component#dimension} for more detailed information.
   *
   * @param {number|string} [num]
   *        The height that you want to set postfixed with '%', 'px' or nothing.
   *
   * @param {boolean} [skipListeners]
   *        Skip the componentresize event trigger
   *
   * @return {number|string}
   *         The width when getting, zero if there is no width. Can be a string
   *         postpixed with '%' or 'px'.
   */
  height(num, skipListeners) {
    return this.dimension('height', num, skipListeners);
  }

  /**
   * Set both the width and height of the `Component` element at the same time.
   *
   * @param  {number|string} width
   *         Width to set the `Component`s element to.
   *
   * @param  {number|string} height
   *         Height to set the `Component`s element to.
   */
  dimensions(width, height) {
    // Skip componentresize listeners on width for optimization
    this.width(width, true);
    this.height(height);
  }

  /**
   * Get or set width or height of the `Component` element. This is the shared code
   * for the {@link Component#width} and {@link Component#height}.
   *
   * Things to know:
   * - If the width or height in an number this will return the number postfixed with 'px'.
   * - If the width/height is a percent this will return the percent postfixed with '%'
   * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
   *   defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
   *   See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
   *   for more information
   * - If you want the computed style of the component, use {@link Component#currentWidth}
   *   and {@link {Component#currentHeight}
   *
   * @fires Component#componentresize
   *
   * @param {string} widthOrHeight
   8        'width' or 'height'
   *
   * @param  {number|string} [num]
   8         New dimension
   *
   * @param  {boolean} [skipListeners]
   *         Skip componentresize event trigger
   *
   * @return {number}
   *         The dimension when getting or 0 if unset
   */
  dimension(widthOrHeight, num, skipListeners) {
    if (num !== undefined) {
      // Set to zero if null or literally NaN (NaN !== NaN)
      if (num === null || num !== num) {
        num = 0;
      }

      // Check if using css width/height (% or px) and adjust
      if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
        this.el_.style[widthOrHeight] = num;
      } else if (num === 'auto') {
        this.el_.style[widthOrHeight] = '';
      } else {
        this.el_.style[widthOrHeight] = num + 'px';
      }

      // skipListeners allows us to avoid triggering the resize event when setting both width and height
      if (!skipListeners) {
        /**
         * Triggered when a component is resized.
         *
         * @event Component#componentresize
         * @type {EventTarget~Event}
         */
        this.trigger('componentresize');
      }

      return;
    }

    // Not setting a value, so getting it
    // Make sure element exists
    if (!this.el_) {
      return 0;
    }

    // Get dimension value from style
    const val = this.el_.style[widthOrHeight];
    const pxIndex = val.indexOf('px');

    if (pxIndex !== -1) {
      // Return the pixel value with no 'px'
      return parseInt(val.slice(0, pxIndex), 10);
    }

    // No px so using % or no style was set, so falling back to offsetWidth/height
    // If component has display:none, offset will return 0
    // TODO: handle display:none and no dimension style using px
    return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
  }

  /**
   * Get the computed width or the height of the component's element.
   *
   * Uses `window.getComputedStyle`.
   *
   * @param {string} widthOrHeight
   *        A string containing 'width' or 'height'. Whichever one you want to get.
   *
   * @return {number}
   *         The dimension that gets asked for or 0 if nothing was set
   *         for that dimension.
   */
  currentDimension(widthOrHeight) {
    let computedWidthOrHeight = 0;

    if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
      throw new Error('currentDimension only accepts width or height value');
    }

    computedWidthOrHeight = computedStyle(this.el_, widthOrHeight);

    // remove 'px' from variable and parse as integer
    computedWidthOrHeight = parseFloat(computedWidthOrHeight);

    // if the computed value is still 0, it's possible that the browser is lying
    // and we want to check the offset values.
    // This code also runs wherever getComputedStyle doesn't exist.
    if (computedWidthOrHeight === 0 || isNaN(computedWidthOrHeight)) {
      const rule = `offset${toTitleCase(widthOrHeight)}`;

      computedWidthOrHeight = this.el_[rule];
    }

    return computedWidthOrHeight;
  }

  /**
   * An object that contains width and height values of the `Component`s
   * computed style. Uses `window.getComputedStyle`.
   *
   * @typedef {Object} Component~DimensionObject
   *
   * @property {number} width
   *           The width of the `Component`s computed style.
   *
   * @property {number} height
   *           The height of the `Component`s computed style.
   */

  /**
   * Get an object that contains computed width and height values of the
   * component's element.
   *
   * Uses `window.getComputedStyle`.
   *
   * @return {Component~DimensionObject}
   *         The computed dimensions of the component's element.
   */
  currentDimensions() {
    return {
      width: this.currentDimension('width'),
      height: this.currentDimension('height')
    };
  }

  /**
   * Get the computed width of the component's element.
   *
   * Uses `window.getComputedStyle`.
   *
   * @return {number}
   *         The computed width of the component's element.
   */
  currentWidth() {
    return this.currentDimension('width');
  }

  /**
   * Get the computed height of the component's element.
   *
   * Uses `window.getComputedStyle`.
   *
   * @return {number}
   *         The computed height of the component's element.
   */
  currentHeight() {
    return this.currentDimension('height');
  }

  /**
   * Set the focus to this component
   */
  focus() {
    this.el_.focus();
  }

  /**
   * Remove the focus from this component
   */
  blur() {
    this.el_.blur();
  }

  /**
   * When this Component receives a `keydown` event which it does not process,
   *  it passes the event to the Player for handling.
   *
   * @param {EventTarget~Event} event
   *        The `keydown` event that caused this function to be called.
   */
  handleKeyDown(event) {
    if (this.player_) {

      // We only stop propagation here because we want unhandled events to fall
      // back to the browser.
      event.stopPropagation();
      this.player_.handleKeyDown(event);
    }
  }

  /**
   * Many components used to have a `handleKeyPress` method, which was poorly
   * named because it listened to a `keydown` event. This method name now
   * delegates to `handleKeyDown`. This means anyone calling `handleKeyPress`
   * will not see their method calls stop working.
   *
   * @param {EventTarget~Event} event
   *        The event that caused this function to be called.
   */
  handleKeyPress(event) {
    this.handleKeyDown(event);
  }

  /**
   * Emit a 'tap' events when touch event support gets detected. This gets used to
   * support toggling the controls through a tap on the video. They get enabled
   * because every sub-component would have extra overhead otherwise.
   *
   * @private
   * @fires Component#tap
   * @listens Component#touchstart
   * @listens Component#touchmove
   * @listens Component#touchleave
   * @listens Component#touchcancel
   * @listens Component#touchend

   */
  emitTapEvents() {
    // Track the start time so we can determine how long the touch lasted
    let touchStart = 0;
    let firstTouch = null;

    // Maximum movement allowed during a touch event to still be considered a tap
    // Other popular libs use anywhere from 2 (hammer.js) to 15,
    // so 10 seems like a nice, round number.
    const tapMovementThreshold = 10;

    // The maximum length a touch can be while still being considered a tap
    const touchTimeThreshold = 200;

    let couldBeTap;

    this.on('touchstart', function(event) {
      // If more than one finger, don't consider treating this as a click
      if (event.touches.length === 1) {
        // Copy pageX/pageY from the object
        firstTouch = {
          pageX: event.touches[0].pageX,
          pageY: event.touches[0].pageY
        };
        // Record start time so we can detect a tap vs. "touch and hold"
        touchStart = window.performance.now();
        // Reset couldBeTap tracking
        couldBeTap = true;
      }
    });

    this.on('touchmove', function(event) {
      // If more than one finger, don't consider treating this as a click
      if (event.touches.length > 1) {
        couldBeTap = false;
      } else if (firstTouch) {
        // Some devices will throw touchmoves for all but the slightest of taps.
        // So, if we moved only a small distance, this could still be a tap
        const xdiff = event.touches[0].pageX - firstTouch.pageX;
        const ydiff = event.touches[0].pageY - firstTouch.pageY;
        const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);

        if (touchDistance > tapMovementThreshold) {
          couldBeTap = false;
        }
      }
    });

    const noTap = function() {
      couldBeTap = false;
    };

    // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
    this.on('touchleave', noTap);
    this.on('touchcancel', noTap);

    // When the touch ends, measure how long it took and trigger the appropriate
    // event
    this.on('touchend', function(event) {
      firstTouch = null;
      // Proceed only if the touchmove/leave/cancel event didn't happen
      if (couldBeTap === true) {
        // Measure how long the touch lasted
        const touchTime = window.performance.now() - touchStart;

        // Make sure the touch was less than the threshold to be considered a tap
        if (touchTime < touchTimeThreshold) {
          // Don't let browser turn this into a click
          event.preventDefault();
          /**
           * Triggered when a `Component` is tapped.
           *
           * @event Component#tap
           * @type {EventTarget~Event}
           */
          this.trigger('tap');
          // It may be good to copy the touchend event object and change the
          // type to tap, if the other event properties aren't exact after
          // Events.fixEvent runs (e.g. event.target)
        }
      }
    });
  }

  /**
   * This function reports user activity whenever touch events happen. This can get
   * turned off by any sub-components that wants touch events to act another way.
   *
   * Report user touch activity when touch events occur. User activity gets used to
   * determine when controls should show/hide. It is 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 as `touchstart` and `touchend` toggle player
   * controls. So touch events can't help us at the player level either.
   *
   * User activity gets 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.
   * Furthermore a `touchmove` event and anything other than a tap, should not turn
   * controls back on.
   *
   * @listens Component#touchstart
   * @listens Component#touchmove
   * @listens Component#touchend
   * @listens Component#touchcancel
   */
  enableTouchActivity() {
    // Don't continue if the root player doesn't support reporting user activity
    if (!this.player() || !this.player().reportUserActivity) {
      return;
    }

    // listener for reporting that the user is active
    const report = Fn.bind(this.player(), this.player().reportUserActivity);

    let touchHolding;

    this.on('touchstart', function() {
      report();
      // For as long as the they are touching the device or have their mouse down,
      // we consider them active even if they're not moving their finger or mouse.
      // So we want to continue to update that they are active
      this.clearInterval(touchHolding);
      // report at the same interval as activityCheck
      touchHolding = this.setInterval(report, 250);
    });

    const touchEnd = function(event) {
      report();
      // stop the interval that maintains activity if the touch is holding
      this.clearInterval(touchHolding);
    };

    this.on('touchmove', report);
    this.on('touchend', touchEnd);
    this.on('touchcancel', touchEnd);
  }

  /**
   * A callback that has no parameters and is bound into `Component`s context.
   *
   * @callback Component~GenericCallback
   * @this Component
   */

  /**
   * Creates a function that runs after an `x` millisecond timeout. This function is a
   * wrapper around `window.setTimeout`. There are a few reasons to use this one
   * instead though:
   * 1. It gets cleared via  {@link Component#clearTimeout} when
   *    {@link Component#dispose} gets called.
   * 2. The function callback will gets turned into a {@link Component~GenericCallback}
   *
   * > Note: You can't use `window.clearTimeout` on the id returned by this function. This
   *         will cause its dispose listener not to get cleaned up! Please use
   *         {@link Component#clearTimeout} or {@link Component#dispose} instead.
   *
   * @param {Component~GenericCallback} fn
   *        The function that will be run after `timeout`.
   *
   * @param {number} timeout
   *        Timeout in milliseconds to delay before executing the specified function.
   *
   * @return {number}
   *         Returns a timeout ID that gets used to identify the timeout. It can also
   *         get used in {@link Component#clearTimeout} to clear the timeout that
   *         was set.
   *
   * @listens Component#dispose
   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
   */
  setTimeout(fn, timeout) {
    // declare as variables so they are properly available in timeout function
    // eslint-disable-next-line
    var timeoutId, disposeFn;

    fn = Fn.bind(this, fn);

    this.clearTimersOnDispose_();

    timeoutId = window.setTimeout(() => {
      if (this.setTimeoutIds_.has(timeoutId)) {
        this.setTimeoutIds_.delete(timeoutId);
      }
      fn();
    }, timeout);

    this.setTimeoutIds_.add(timeoutId);

    return timeoutId;
  }

  /**
   * Clears a timeout that gets created via `window.setTimeout` or
   * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
   * use this function instead of `window.clearTimout`. If you don't your dispose
   * listener will not get cleaned up until {@link Component#dispose}!
   *
   * @param {number} timeoutId
   *        The id of the timeout to clear. The return value of
   *        {@link Component#setTimeout} or `window.setTimeout`.
   *
   * @return {number}
   *         Returns the timeout id that was cleared.
   *
   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
   */
  clearTimeout(timeoutId) {
    if (this.setTimeoutIds_.has(timeoutId)) {
      this.setTimeoutIds_.delete(timeoutId);
      window.clearTimeout(timeoutId);
    }

    return timeoutId;
  }

  /**
   * Creates a function that gets run every `x` milliseconds. This function is a wrapper
   * around `window.setInterval`. There are a few reasons to use this one instead though.
   * 1. It gets cleared via  {@link Component#clearInterval} when
   *    {@link Component#dispose} gets called.
   * 2. The function callback will be a {@link Component~GenericCallback}
   *
   * @param {Component~GenericCallback} fn
   *        The function to run every `x` seconds.
   *
   * @param {number} interval
   *        Execute the specified function every `x` milliseconds.
   *
   * @return {number}
   *         Returns an id that can be used to identify the interval. It can also be be used in
   *         {@link Component#clearInterval} to clear the interval.
   *
   * @listens Component#dispose
   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
   */
  setInterval(fn, interval) {
    fn = Fn.bind(this, fn);

    this.clearTimersOnDispose_();

    const intervalId = window.setInterval(fn, interval);

    this.setIntervalIds_.add(intervalId);

    return intervalId;
  }

  /**
   * Clears an interval that gets created via `window.setInterval` or
   * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
   * use this function instead of `window.clearInterval`. If you don't your dispose
   * listener will not get cleaned up until {@link Component#dispose}!
   *
   * @param {number} intervalId
   *        The id of the interval to clear. The return value of
   *        {@link Component#setInterval} or `window.setInterval`.
   *
   * @return {number}
   *         Returns the interval id that was cleared.
   *
   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
   */
  clearInterval(intervalId) {
    if (this.setIntervalIds_.has(intervalId)) {
      this.setIntervalIds_.delete(intervalId);
      window.clearInterval(intervalId);
    }

    return intervalId;
  }

  /**
   * Queues up a callback to be passed to requestAnimationFrame (rAF), but
   * with a few extra bonuses:
   *
   * - Supports browsers that do not support rAF by falling back to
   *   {@link Component#setTimeout}.
   *
   * - The callback is turned into a {@link Component~GenericCallback} (i.e.
   *   bound to the component).
   *
   * - Automatic cancellation of the rAF callback is handled if the component
   *   is disposed before it is called.
   *
   * @param  {Component~GenericCallback} fn
   *         A function that will be bound to this component and executed just
   *         before the browser's next repaint.
   *
   * @return {number}
   *         Returns an rAF ID that gets used to identify the timeout. It can
   *         also be used in {@link Component#cancelAnimationFrame} to cancel
   *         the animation frame callback.
   *
   * @listens Component#dispose
   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
   */
  requestAnimationFrame(fn) {
    // Fall back to using a timer.
    if (!this.supportsRaf_) {
      return this.setTimeout(fn, 1000 / 60);
    }

    this.clearTimersOnDispose_();

    // declare as variables so they are properly available in rAF function
    // eslint-disable-next-line
    var id;
    fn = Fn.bind(this, fn);

    id = window.requestAnimationFrame(() => {
      if (this.rafIds_.has(id)) {
        this.rafIds_.delete(id);
      }
      fn();
    });
    this.rafIds_.add(id);

    return id;
  }

  /**
   * Request an animation frame, but only one named animation
   * frame will be queued. Another will never be added until
   * the previous one finishes.
   *
   * @param {string} name
   *        The name to give this requestAnimationFrame
   *
   * @param  {Component~GenericCallback} fn
   *         A function that will be bound to this component and executed just
   *         before the browser's next repaint.
   */
  requestNamedAnimationFrame(name, fn) {
    if (this.namedRafs_.has(name)) {
      return;
    }
    this.clearTimersOnDispose_();

    fn = Fn.bind(this, fn);

    const id = this.requestAnimationFrame(() => {
      fn();
      if (this.namedRafs_.has(name)) {
        this.namedRafs_.delete(name);
      }
    });

    this.namedRafs_.set(name, id);

    return name;
  }

  /**
   * Cancels a current named animation frame if it exists.
   *
   * @param {string} name
   *        The name of the requestAnimationFrame to cancel.
   */
  cancelNamedAnimationFrame(name) {
    if (!this.namedRafs_.has(name)) {
      return;
    }

    this.cancelAnimationFrame(this.namedRafs_.get(name));
    this.namedRafs_.delete(name);
  }

  /**
   * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
   * (rAF).
   *
   * If you queue an rAF callback via {@link Component#requestAnimationFrame},
   * use this function instead of `window.cancelAnimationFrame`. If you don't,
   * your dispose listener will not get cleaned up until {@link Component#dispose}!
   *
   * @param {number} id
   *        The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
   *
   * @return {number}
   *         Returns the rAF ID that was cleared.
   *
   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
   */
  cancelAnimationFrame(id) {
    // Fall back to using a timer.
    if (!this.supportsRaf_) {
      return this.clearTimeout(id);
    }

    if (this.rafIds_.has(id)) {
      this.rafIds_.delete(id);
      window.cancelAnimationFrame(id);
    }

    return id;

  }

  /**
   * A function to setup `requestAnimationFrame`, `setTimeout`,
   * and `setInterval`, clearing on dispose.
   *
   * > Previously each timer added and removed dispose listeners on it's own.
   * For better performance it was decided to batch them all, and use `Set`s
   * to track outstanding timer ids.
   *
   * @private
   */
  clearTimersOnDispose_() {
    if (this.clearingTimersOnDispose_) {
      return;
    }

    this.clearingTimersOnDispose_ = true;
    this.one('dispose', () => {
      [
        ['namedRafs_', 'cancelNamedAnimationFrame'],
        ['rafIds_', 'cancelAnimationFrame'],
        ['setTimeoutIds_', 'clearTimeout'],
        ['setIntervalIds_', 'clearInterval']
      ].forEach(([idName, cancelName]) => {
        // for a `Set` key will actually be the value again
        // so forEach((val, val) =>` but for maps we want to use
        // the key.
        this[idName].forEach((val, key) => this[cancelName](key));
      });

      this.clearingTimersOnDispose_ = false;
    });
  }

  /**
   * Register a `Component` with `videojs` given the name and the component.
   *
   * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
   *         should be registered using {@link Tech.registerTech} or
   *         {@link videojs:videojs.registerTech}.
   *
   * > NOTE: This function can also be seen on videojs as
   *         {@link videojs:videojs.registerComponent}.
   *
   * @param {string} name
   *        The name of the `Component` to register.
   *
   * @param {Component} ComponentToRegister
   *        The `Component` class to register.
   *
   * @return {Component}
   *         The `Component` that was registered.
   */
  static registerComponent(name, ComponentToRegister) {
    if (typeof name !== 'string' || !name) {
      throw new Error(`Illegal component name, "${name}"; must be a non-empty string.`);
    }

    const Tech = Component.getComponent('Tech');

    // We need to make sure this check is only done if Tech has been registered.
    const isTech = Tech && Tech.isTech(ComponentToRegister);
    const isComp = Component === ComponentToRegister ||
      Component.prototype.isPrototypeOf(ComponentToRegister.prototype);

    if (isTech || !isComp) {
      let reason;

      if (isTech) {
        reason = 'techs must be registered using Tech.registerTech()';
      } else {
        reason = 'must be a Component subclass';
      }

      throw new Error(`Illegal component, "${name}"; ${reason}.`);
    }

    name = toTitleCase(name);

    if (!Component.components_) {
      Component.components_ = {};
    }

    const Player = Component.getComponent('Player');

    if (name === 'Player' && Player && Player.players) {
      const players = Player.players;
      const playerNames = Object.keys(players);

      // If we have players that were disposed, then their name will still be
      // in Players.players. So, we must loop through and verify that the value
      // for each item is not null. This allows registration of the Player component
      // after all players have been disposed or before any were created.
      if (players &&
          playerNames.length > 0 &&
          playerNames.map((pname) => players[pname]).every(Boolean)) {
        throw new Error('Can not register Player component after player has been created.');
      }
    }

    Component.components_[name] = ComponentToRegister;
    Component.components_[toLowerCase(name)] = ComponentToRegister;

    return ComponentToRegister;
  }

  /**
   * Get a `Component` based on the name it was registered with.
   *
   * @param {string} name
   *        The Name of the component to get.
   *
   * @return {Component}
   *         The `Component` that got registered under the given name.
   *
   * @deprecated In `videojs` 6 this will not return `Component`s that were not
   *             registered using {@link Component.registerComponent}. Currently we
   *             check the global `videojs` object for a `Component` name and
   *             return that if it exists.
   */
  static getComponent(name) {
    if (!name || !Component.components_) {
      return;
    }

    return Component.components_[name];
  }
}

/**
 * Whether or not this component supports `requestAnimationFrame`.
 *
 * This is exposed primarily for testing purposes.
 *
 * @private
 * @type {Boolean}
 */
Component.prototype.supportsRaf_ = typeof window.requestAnimationFrame === 'function' &&
  typeof window.cancelAnimationFrame === 'function';

Component.registerComponent('Component', Component);

export default Component;


// the user set AR injected into the header.

# Video.js Options Reference

> **Note:** This document is only a reference for available options. To learn about passing options to Video.js, see [the setup guide](/docs/guides/setup.md#options).

## Table of Contents

* [Standard <video> Element Options](#standard-video-element-options)
  * [autoplay](#autoplay)
    * [More info on autoplay support and changes:](#more-info-on-autoplay-support-and-changes)
  * [controls](#controls)
  * [height](#height)
  * [loop](#loop)
  * [muted](#muted)
  * [poster](#poster)
  * [preload](#preload)
    * ['auto'](#auto)
    * ['metadata'](#metadata)
    * ['none'](#none)
  * [src](#src)
  * [width](#width)
* [Video.js-specific Options](#videojs-specific-options)
  * [aspectRatio](#aspectratio)
  * [autoSetup](#autosetup)
  * [breakpoints](#breakpoints)
  * [children](#children)
  * [fluid](#fluid)
  * [inactivityTimeout](#inactivitytimeout)
  * [language](#language)
  * [languages](#languages)
  * [liveui](#liveui)
  * [liveTracker.trackingThreshold](#livetrackertrackingthreshold)
  * [liveTracker.liveTolerance](#livetrackerlivetolerance)
  * [nativeControlsForTouch](#nativecontrolsfortouch)
  * [notSupportedMessage](#notsupportedmessage)
  * [noUITitleAttributes](#nouititleattributes)
  * [fullscreen](#fullscreen)
    * [options](#options)
  * [playbackRates](#playbackrates)
  * [plugins](#plugins)
  * [responsive](#responsive)
  * [sources](#sources)
  * [suppressNotSupportedError](#suppressnotsupportederror)
  * [techCanOverridePoster](#techcanoverrideposter)
  * [techOrder](#techorder)
  * [userActions](#useractions)
  * [userActions.doubleClick](#useractionsdoubleclick)
  * [userActions.hotkeys](#useractionshotkeys)
  * [userActions.hotkeys.fullscreenKey](#useractionshotkeysfullscreenkey)
  * [userActions.hotkeys.muteKey](#useractionshotkeysmutekey)
  * [userActions.hotkeys.playPauseKey](#useractionshotkeysplaypausekey)
  * [vtt.js](#vttjs)
* [Component Options](#component-options)
  * [children](#children-1)
  * [${componentName}](#componentname)
* [Tech Options](#tech-options)
  * [${techName}](#techname)
  * [html5](#html5)
    * [nativeControlsForTouch](#nativecontrolsfortouch-1)
    * [nativeAudioTracks](#nativeaudiotracks)
    * [nativeTextTracks](#nativetexttracks)
    * [nativeVideoTracks](#nativevideotracks)
    * [preloadTextTracks](#preloadtexttracks)

## Standard `<video>` Element Options

Each of these options is also available as a [standard `<video>` element attribute][video-attrs]; so, they can be defined in all three manners [outlined in the setup guide](/docs/guides/setup.md#options). Typically, defaults are not listed as this is left to browser vendors.

### `autoplay`

> Type: `boolean|string`
> NOTE: At this point, the autoplay attribute and option are NOT a guarantee that your video will autoplay.
> NOTE2: If there is an attribute on the media element the option will be ignored.
> NOTE3: You cannot pass a string value in the attribute, you must pass it in the videojs options

Instead of using the `autoplay` attribute you should pass an `autoplay` option to the `videojs` function. The following values
are valid:

* a boolean value of `false`: the same as having no attribute on the video element, won't `autoplay`
* a boolean value of `true`: the same as having attribute on the video element, will use browsers `autoplay`
* a string value of `'muted'`: will mute the video element and then manually call `play()` on `loadstart`. This is likely to work.
* a string value of `'play'`: will call `play()` on `loadstart`, similar to browsers `autoplay`
* a string value of `'any'`: will call `play()` on `loadstart` and if the promise is rejected it will mute the video element then call `play()`.

To pass the option

```js
var player = videojs('my-video', {
  autoplay: 'muted'
});

// or

player.autoplay('muted');
```

#### More info on autoplay support and changes:

* See our blog post: [Autoplay Best Practices with Video.js](https://videojs.com/blog/autoplay-best-practices-with-video-js/)

### `controls`

> Type: `boolean`

Determines 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 Player API.

### `height`

> Type: `string|number`

Sets the display height of the video player in pixels.

### `loop`

> Type: `boolean`

Causes the video to start over as soon as it ends.

### `muted`

> Type: `boolean`

Will silence any audio by default.

### `poster`

> Type: `string`

A URL to an 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 hits "play" the image will go away.

### `preload`

> Type: `string`

Suggests to the browser whether or not the video data should begin downloading as soon as the `<video>` element is loaded. Supported values are:

#### `'auto'`

Start loading the video immediately (if the browser supports it). Some mobile devices will not preload the video in order to protect their users' bandwidth/data usage. This is why the value is called 'auto' and not something more conclusive like `'true'`.

_This tends to be the most common and recommended value as it allows the browser to choose the best behavior._

#### `'metadata'`

Load only the meta data of the video, which includes information like the duration and dimensions of the video. Sometimes, the meta data will be loaded by downloading a few frames of video.

#### `'none'`

Don't preload any data. The browser will wait until the user hits "play" to begin downloading.

### `src`

> Type: `string`

The source URL to a video source to embed.

### `width`

> Type: `string|number`

Sets the display width of the video player in pixels.

## Video.js-specific Options

Each option is `undefined` by default unless otherwise specified.

### `aspectRatio`

> Type: `string`

Puts the player in [fluid](#fluid) mode and the value is used when calculating the dynamic size of the player. The value should represent a ratio - two numbers separated by a colon (e.g. `"16:9"` or `"4:3"`).

### `autoSetup`

> Type: `boolean`

Prevents the player from running the autoSetup for media elements with `data-setup` attribute.

> **Note**: this must be set globally with `videojs.options.autoSetup = false` in the same tick as videojs source is loaded to take effect.

### `breakpoints`

> Type: `Object`

When used with the [`responsive` option](#responsive), sets breakpoints that will configure how class names are toggled on the player to adjust the UI based on the player's dimensions.

By default, the breakpoints are:

| Class Name           | Width Range |
| -------------------- | ----------- |
| `vjs-layout-tiny`    | 0-210       |
| `vjs-layout-x-small` | 211-320     |
| `vjs-layout-small`   | 321-425     |
| `vjs-layout-medium`  | 426-768     |
| `vjs-layout-large`   | 769-1440    |
| `vjs-layout-x-large` | 1441-2560   |
| `vjs-layout-huge`    | 2561+       |

While the class names cannot be changed, the width ranges can be configured via an object like this:

```js
breakpoints: {
  tiny: 300,
  xsmall: 400,
  small: 500,
  medium: 600,
  large: 700,
  xlarge: 800,
  huge: 900
}
```

* The _keys_ of the `breakpoints` object are derived from the associated class names by removing the `vjs-layout-` prefix and any `-` characters.
* The _values_ of the `breakpoints` object define the max width for a range.
* Not all keys need to be defined. You can easily override a single breakpoint by passing an object with one key/value pair! Customized breakpoints will be merged with default breakpoints when the player is created.

When the player's size changes, the merged breakpoints will be inspected in the size order until a matching breakpoint is found.

That breakpoint's associated class name will be added as a class to the player. The previous breakpoint's class will be removed.

See the file `sandbox/responsive.html.example` for an example of a responsive player using the default breakpoints.

### `children`

> Type: `Array|Object`

This option is inherited from the [`Component` base class](#component-options).

### `fluid`

> Type: `boolean`

When `true`, the Video.js player will have a fluid size. In other words, it will scale to fit its container.

Also, if the `<video>` element has the `"vjs-fluid"`, this option is automatically set to `true`.

### `inactivityTimeout`

> Type: `number`

Video.js indicates that the user is interacting with the player by way of the `"vjs-user-active"` and `"vjs-user-inactive"` classes and the `"useractive"` event.

The `inactivityTimeout` determines how many milliseconds of inactivity is required before declaring the user inactive. A value of `0` indicates that there is no `inactivityTimeout` and the user will never be considered inactive.

### `language`

> Type: `string`, Default: browser default or `'en'`

A [language code][lang-codes] matching one of the available languages in the player. This sets the initial language for a player, but it can always be changed.

Learn more about [languages in Video.js][languages].

### `languages`

> Type: `Object`

Customize which languages are available in a player. The keys of this object will be [language codes][lang-codes] and the values will be objects with English keys and translated values.

Learn more about [languages in Video.js][languages]

> **Note**: Generally, this option is not needed and it would be better to pass your custom languages to `videojs.addLanguage()`, so they are available in all players!

### `liveui`

> Type: `boolean`
> Default: `false`

Allows the player to use the new live ui that includes:

* A progress bar for seeking within the live window
* A button that can be clicked to seek to the live edge with a circle indicating if you are at the live edge or not.

Without this option the progress bar will be hidden and in its place will be text that indicates `LIVE` playback. There will be no progress control
and you will not be able click the text to seek to the live edge. `liveui` will default to `true` in a future version!

### `liveTracker.trackingThreshold`

> Type: `number`
> Default: `30`

An option for the liveTracker component of the player that controls when the liveui should be shown. By default if a stream has less than 30s on the seekBar then we do not show the new liveui even with the liveui option set.

### `liveTracker.liveTolerance`

> Type: `number`
> Default: `15`

An option for the liveTracker component of the player that controls how far from the seekable end should be considered live playback. By default anything further than 15s from the live seekable edge is considered behind live and everything else is considered live. Any user interaction to seek backwards will ignore this value as a user would expect.

### `nativeControlsForTouch`

> Type: `boolean`

Explicitly set a default value for [the associated tech option](#nativecontrolsfortouch).

### `notSupportedMessage`

> Type: `string`

Allows overriding the default message that is displayed when Video.js cannot play back a media source.

### `noUITitleAttributes`

> Type: `boolean`
> Default: `false`

Control whether UI elements have a `title` attribute. A `title` attribute is shown on mouse hover, which can be helpful for usability, but has drawbacks for accessibility. Setting `noUITitleAttributes` to `true` prevents the `title` attribute from being added to UI elements, allowing for more accessible tooltips to be added to controls by a plugin or external framework.

### `fullscreen`

> Type: `Object`
> Default: `{options: {navigationUI: 'hide'}`

`fullscreen.options` can be set to pass in specific fullscreen options. At some point, it will be augmented with `element` and `handler` for more functionality.

#### `options`

> Type: `Object`
> Default: `{navigationUI: 'hide'}`

See [The Fullscreen API Spec](https://fullscreen.spec.whatwg.org/#dictdef-fullscreenoptions) for more details.

### `playbackRates`

> Type: `Array`

An array of numbers strictly greater than 0, where 1 means regular speed
(100%), 0.5 means half-speed (50%), 2 means double-speed (200%), etc.
If specified, Video.js displays a control (of class `vjs-playback-rate`)
allowing the user to choose playback speed from among the array of choices.
The choices are presented in the specified order from bottom to top.

For example:

```js
videojs('my-player', {
  playbackRates: [0.5, 1, 1.5, 2]
});
```

### `plugins`

> Type: `Object`

This supports having plugins be initialized automatically with custom options when the player is initialized - rather than requiring you to initialize them manually.

```js
videojs('my-player', {
  plugins: {
    foo: {bar: true},
    boo: {baz: false}
  }
});
```

The above is roughly equivalent to:

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

player.foo({bar: true});
player.boo({baz: false});
```

Although, since the `plugins` option is an object, the order of initialization is not guaranteed!

See [the plugins guide][plugins] for more information on Video.js plugins.

### `responsive`

> Type: `boolean`, Default: `false`

Setting this option to `true` will cause the player to customize itself based on responsive breakpoints (see: [`breakpoints` option](#breakpoints)).

When this option is `false` (the default), responsive breakpoints will be ignored.

### `sources`

> Type: `Array`

An array of objects that mirror the native `<video>` element's capability to have a series of child `<source>` elements. This should be an array of objects with the `src` and `type` properties. For example:

```js
videojs('my-player', {
  sources: [{
    src: '//path/to/video.mp4',
    type: 'video/mp4'
  }, {
    src: '//path/to/video.webm',
    type: 'video/webm'
  }]
});
```

Using `<source>` elements will have the same effect:

```html
<video ...>
  <source src="//path/to/video.mp4" type="video/mp4">
  <source src="//path/to/video.webm" type="video/webm">
</video>
```

### `suppressNotSupportedError`

> Type: `boolean`

If set to true, then the no compatible source error will not be triggered immediately and instead will occur on the first user interaction. This is useful for Google's "mobile friendly" test tool, which can't play video but where you might not want to see an error displayed.

### `techCanOverridePoster`

> Type: `boolean`

Gives the possibility to techs to override the player's poster
and integrate into the player's poster life-cycle.
This can be useful when multiple techs are used and each has to set their own poster
 any time a new source is played.

### `techOrder`

> Type: `Array`, Default: `['html5']`

Defines the order in which Video.js techs are preferred. By default, this means that the `Html5` tech is preferred. Other registered techs will be added after this tech in the order in which they are registered.

### `userActions`

> Type: `Object`

### `userActions.doubleClick`

> Type: `boolean|function`

Controls how double-clicking on the player/tech operates. If set to `false`, double-clicking is disabled. If undefined or set to `true`, double-clicking is enabled and toggles fullscreen mode. To override the default double-click handling, set `userActions.doubleClick` to a function which accepts a `dblclick` event:

```js
function myDoubleClickHandler(event) = {
  // `this` is the player in this context

  this.pause();
};

videojs('my-player', {
  userActions: {
    doubleClick: myDoubleClickHandler
  }
});
```

### `userActions.hotkeys`

> Type: `boolean|function|object`

Controls how player-wide hotkeys operate. If set to `false`, or `undefined`, hotkeys are disabled. If set to `true` or an object (to allow definitions of `fullscreenKey` etc. below), hotkeys are enabled as described below. To override the default hotkey handling, set `userActions.hotkeys` to a function which accepts a `keydown` event:

```js
var player = videojs('my-player', {
  userActions: {
    hotkeys: function(event) {
      // `this` is the player in this context

      // `x` key = pause
      if (event.which === 88) {
        this.pause();
      }
      // `y` key = play
      if (event.which === 89) {
        this.play();
      }
    }
  }
});
```

Default hotkey handling is:

|   Key   | Action            | Enabled by                                                        |
| :-----: | ----------------- | ----------------------------------------------------------------- |
|   `f`   | toggle fullscreen | only enabled if a Fullscreen button is present in the Control Bar |
|   `m`   | toggle mute       | always enabled, even if no Control Bar is present                 |
|   `k`   | toggle play/pause | always enabled, even if no Control Bar is present                 |
| `Space` | toggle play/pause | always enabled, even if no Control Bar is present                 |

Hotkeys require player focus first. Note that the `Space` key activates controls such as buttons and menus if that control has keyboard focus. The other hotkeys work regardless of which control in the player has focus.

### `userActions.hotkeys.fullscreenKey`

> Type: `function`

Override the fullscreen key definition. If this is set, the function receives the `keydown` event; if the function returns `true`, then the fullscreen toggle action is performed.

```js
var player = videojs('my-player', {
  userActions: {
    hotkeys: {
      muteKey: function(event) {
        // disable mute key
      },
      fullscreenKey: function(event) {
        // override fullscreen to trigger when pressing the v key
        return (event.which === 86);
      }
    }
  }
});
```

### `userActions.hotkeys.muteKey`

> Type: `function`

Override the mute key definition. If this is set, the function receives the `keydown` event; if the function returns `true`, then the mute toggle action is performed.

### `userActions.hotkeys.playPauseKey`

> Type: `function`

Override the play/pause key definition. If this is set, the function receives the `keydown` event; if the function returns `true`, then the play/pause toggle action is performed.

### `vtt.js`

> Type: `string`

Allows overriding the default URL to vtt.js, which may be loaded asynchronously to polyfill support for `WebVTT`.

This option will be used in the "novtt" build of Video.js (i.e. `video.novtt.js`). Otherwise, vtt.js is bundled with Video.js.

## Component Options

The Video.js player is a component. Like all components, you can define what children it includes, what order they appear in, and what options are passed to them.

This is meant to be a quick reference; so, for more detailed information on components in Video.js, check out the [components guide](/docs/guides/components.md).

### `children`

> Type: `Array|Object`

If an `Array` - which is the default - this is used to determine which children (by component name) and in which order they are created on a player (or other component):

```js
// The following code creates a player with ONLY bigPlayButton and
// controlBar child components.
videojs('my-player', {
  children: [
    'bigPlayButton',
    'controlBar'
  ]
});
```

The `children` options can also be passed as an `Object`. In this case, it is used to provide `options` for any/all children, including disabling them with `false`:

```js
// This player's ONLY child will be the controlBar. Clearly, this is not the
// ideal method for disabling a grandchild!
videojs('my-player', {
  children: {
    controlBar: {
      fullscreenToggle: false
    }
  }
});
```

### `${componentName}`

> Type: `Object`

Components can be given custom options via the _lower-camel-case variant of the component name_ (e.g. `controlBar` for `ControlBar`). These can be nested in a representation of grandchild relationships. For example, to disable the fullscreen control:

```js
videojs('my-player', {
  controlBar: {
    fullscreenToggle: false
  }
});
```

## Tech Options

### `${techName}`

> Type: `Object`

Video.js playback technologies (i.e. "techs") can be given custom options as part of the options passed to the `videojs` function. They should be passed under the _lower-case variant of the tech name_ (e.g. `"html5"`).

### `html5`

#### `nativeControlsForTouch`

> Type: `boolean`

Only supported by the `Html5` tech, this option can be set to `true` to force native controls for touch devices.

#### `nativeAudioTracks`

> Type: `boolean`

Can be set to `false` to disable native audio track support. Most commonly used with [videojs-contrib-hls][videojs-contrib-hls].

#### `nativeTextTracks`

> Type: `boolean`

Can be set to `false` to force emulation of text tracks instead of native support. The `nativeCaptions` option also exists, but is simply an alias to `nativeTextTracks`.

#### `nativeVideoTracks`

> Type: `boolean`

Can be set to `false` to disable native video track support. Most commonly used with [videojs-contrib-hls][videojs-contrib-hls].

#### `preloadTextTracks`

> Type: `boolean`

Can be set to `false` to delay loading of non-active text tracks until use. This can cause a short delay when switching captions during which there may be missing captions.

The default behavior is to preload all text tracks.

[plugins]: /docs/guides/plugins.md

[languages]: /docs/guides/languages.md

[ios-10-updates]: https://webkit.org/blog/6784/new-video-policies-for-ios/

[lang-codes]: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry

[video-attrs]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#Attributes

[videojs-contrib-hls]: https://github.com/videojs/videojs-contrib-hls


<!DOCTYPE html>

  <meta charset="utf-8" />
  <title>Video.js Text Descriptions, Chapters & Captions Example</title>

  <link href="http://vjs.zencdn.net/7.0/video-js.min.css" rel="stylesheet">

  <video id="example_video_1" class="video-js" controls preload="none" width="640" height="360"
      data-setup='{ "html5" : { "nativeTextTracks" : false } }'
      poster="http://d2zihajmogu5jn.cloudfront.net/elephantsdream/poster.png">

    <source src="//d2zihajmogu5jn.cloudfront.net/elephantsdream/ed_hd.mp4" type="video/mp4">
    <source src="//d2zihajmogu5jn.cloudfront.net/elephantsdream/ed_hd.ogg" type="video/ogg">

    <track kind="captions" src="captions.en.vtt" srclang="en" label="English" default>
    <track kind="captions" src="captions.sv.vtt" srclang="sv" label="Swedish">
    <track kind="captions" src="captions.ru.vtt" srclang="ru" label="Russian">
    <track kind="captions" src="captions.ja.vtt" srclang="ja" label="Japanese">
    <track kind="captions" src="captions.ar.vtt" srclang="ar" label="Arabic">

    <track kind="descriptions" src="descriptions.en.vtt" srclang="en" label="English">

    <track kind="chapters" src="chapters.en.vtt" srclang="en" label="English">

To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video

  </video>


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

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

API Index

VolumeControl Methods

Class Methods

createEl

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

VolumeControl

The component for controlling the volume level

DEFINED IN: volume-control.js line number: 9

EXTENDS: component.js

Constructor

VolumeControl( 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/volume-control/volume-control.js line number: 35

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