mirror of
https://github.com/videojs/video.js.git
synced 2024-11-24 08:42:25 +02:00
feat: Add videojs.hookOnce method to allow single-run hooks. (#4672)
This commit is contained in:
parent
ee2a49c18c
commit
85fe685696
@ -1,6 +1,6 @@
|
|||||||
# Hooks
|
# Hooks
|
||||||
|
|
||||||
Hooks exist so that users can "hook" on to certain Video.js player lifecycle
|
Hooks exist so that users can globally hook into certain Video.js lifecycle moments.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
@ -18,129 +18,153 @@ Currently, the following hooks are available:
|
|||||||
|
|
||||||
### beforesetup
|
### beforesetup
|
||||||
|
|
||||||
`beforesetup` is called just before the player is created. This allows:
|
`beforesetup` occurs just before a player is created. This allows:
|
||||||
|
|
||||||
* modification of the options passed to the Video.js function (`videojs('some-id, options)`)
|
* Modification of the options passed to the Video.js function (e.g., `videojs('some-id, options)`).
|
||||||
* modification of the dom video element that will be used for the player
|
* Modification of the DOM video element that will be used for the player that will be created.
|
||||||
|
|
||||||
`beforesetup` hook functions should:
|
`beforesetup` hook functions should:
|
||||||
|
|
||||||
* take two arguments
|
* Take two arguments:
|
||||||
1. videoEl: dom video element that Video.js is going to use to create a player
|
1. `videoEl`: DOM `<video>` element that Video.js is going to use to create a player.
|
||||||
1. options: options that Video.js was intialized with and will later pass to the player during creation
|
1. `options`: The options object that Video.js was called with and will be passed to the player during creation.
|
||||||
* return options that will merge and override options that Video.js with intialized with
|
* Return an options object that will be merged with the originally provided options.
|
||||||
|
|
||||||
Example: adding beforesetup hook
|
#### Example
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var beforeSetup = function(videoEl, options) {
|
videojs.hook('beforesetup', function(videoEl, options) {
|
||||||
// videoEl.id will be some-id here, since that is what Video.js
|
|
||||||
// was created with
|
// videoEl will be the video element with id="some-id" since that
|
||||||
|
// gets passed to videojs() below. On subsequent calls, it will be
|
||||||
|
// different.
|
||||||
|
|
||||||
videoEl.className += ' some-super-class';
|
videoEl.className += ' some-super-class';
|
||||||
|
|
||||||
// autoplay will be true here, since we passed in as such
|
// autoplay will be true here, since we passed it as such.
|
||||||
(options.autoplay) {
|
if (options.autoplay) {
|
||||||
options.autoplay = false
|
options.autoplay = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// options that are returned here will be merged with old options
|
// Options that are returned here will be merged with old options.
|
||||||
// in this example options will now be
|
//
|
||||||
// {autoplay: false, controls: true}
|
// In this example options will now be:
|
||||||
|
// {autoplay: false, controls: true}
|
||||||
|
//
|
||||||
|
// This has the practical effect of always disabling autoplay no matter
|
||||||
|
// what options are passed to videojs().
|
||||||
return options;
|
return options;
|
||||||
};
|
});
|
||||||
|
|
||||||
videojs.hook('beforesetup', beforeSetup);
|
// Create a new player.
|
||||||
videojs('some-id', {autoplay: true, controls: true});
|
videojs('some-id', {autoplay: true, controls: true});
|
||||||
```
|
```
|
||||||
|
|
||||||
### setup
|
### setup
|
||||||
|
|
||||||
`setup` is called just after the player is created. This allows:
|
`setup` occurs just after a player is created. This allows:
|
||||||
|
|
||||||
* plugin or custom functionality to intialize on the player
|
* Plugins or other custom functionality to initialize on the player.
|
||||||
* changes to the player object itself
|
* Changes to the player object itself.
|
||||||
|
|
||||||
`setup` hook functions:
|
`setup` hook functions:
|
||||||
|
|
||||||
* Take one argument
|
* Take one argument:
|
||||||
* player: the player that Video.js created
|
1. `player`: the player that Video.js created
|
||||||
* Don't have to return anything
|
* Don't have to return anything
|
||||||
|
|
||||||
Example: adding a setup hook
|
#### Example
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var setup = function(player) {
|
videojs.registerPlugin('foo', function() {
|
||||||
// initialize the foo plugin
|
|
||||||
player.foo();
|
|
||||||
};
|
|
||||||
var foo = function() {};
|
|
||||||
|
|
||||||
videojs.registerPlugin('foo', foo);
|
// This basic plugin will add the "some-super-class" class to a player.
|
||||||
videojs.hook('setup', setup);
|
this.addClass('some-super-class');
|
||||||
var player = videojs('some-id', {autoplay: true, controls: true});
|
});
|
||||||
|
|
||||||
|
videojs.hook('setup', function(player) {
|
||||||
|
|
||||||
|
// Initialize the foo plugin after any player is created.
|
||||||
|
player.foo();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a new player.
|
||||||
|
videojs('some-id', {autoplay: true, controls: true});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Adding
|
### Adding
|
||||||
|
|
||||||
In order to use hooks you must first include Video.js in the page or script that you are using. Then you add hooks using `videojs.hook(<name>, function)` before running the `videojs()` function.
|
Hooks can be added using `videojs.hook(<name>, function)` before running the `videojs()` function.
|
||||||
|
|
||||||
Example: adding hooks
|
#### Example
|
||||||
|
|
||||||
```js
|
```js
|
||||||
videojs.hook('beforesetup', function(videoEl, options) {
|
videojs.hook('beforesetup', function(videoEl, options) {
|
||||||
// videoEl will be the element with id=vid1
|
// This hook will be called twice. Once for "vid1" and once for "vid2".
|
||||||
// options will contain {autoplay: false}
|
// The options will match what is passed to videojs() for each of them.
|
||||||
});
|
|
||||||
videojs.hook('setup', function(player) {
|
|
||||||
// player will be the same player that is defined below
|
|
||||||
// as `var player`
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var player = videojs('vid1', {autoplay: false});
|
videojs.hook('setup', function(player) {
|
||||||
|
// This hook will be called twice. Once for "vid1" and once for "vid2".
|
||||||
|
// The player value will be the player that is created for each element.
|
||||||
|
});
|
||||||
|
|
||||||
|
videojs('vid1', {autoplay: false});
|
||||||
|
videojs('vid2', {autoplay: true});
|
||||||
```
|
```
|
||||||
|
|
||||||
After adding your hooks they will automatically be run at the correct time in the Video.js lifecycle.
|
After adding your hooks, they will automatically be run at the correct time in the Video.js lifecycle.
|
||||||
|
|
||||||
|
### Adding Once
|
||||||
|
|
||||||
|
In some cases, you may only want your hook to run once. In these cases, use `videojs.hookOnce(<name>, function)` before running the `videojs()` function.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
videojs.hookOnce('beforesetup', function(videoEl, options) {
|
||||||
|
// This hook will be called once for "vid1", but not for "vid2".
|
||||||
|
// The options will match what is passed to videojs().
|
||||||
|
});
|
||||||
|
|
||||||
|
videojs.hookOnce('setup', function(player) {
|
||||||
|
// This hook will be called once for "vid1", but not for "vid2".
|
||||||
|
// The player value will be the player that is created for each element.
|
||||||
|
});
|
||||||
|
|
||||||
|
videojs('vid1', {autoplay: false});
|
||||||
|
videojs('vid2', {autoplay: true});
|
||||||
|
```
|
||||||
|
|
||||||
### Getting
|
### Getting
|
||||||
|
|
||||||
To access the array of hooks that currently exists and will be run on the Video.js object you can use the `videojs.hooks` function.
|
To access the array of functions that currently exists for any hook, use the `videojs.hooks` function.
|
||||||
|
|
||||||
Example: getting all hooks attached to Video.js
|
#### Example
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
|
// Get an array of all the 'beforesetup' hooks.
|
||||||
var beforeSetupHooks = videojs.hooks('beforesetup');
|
var beforeSetupHooks = videojs.hooks('beforesetup');
|
||||||
|
|
||||||
|
// Get an array of all the 'setup' hooks.
|
||||||
var setupHooks = videojs.hooks('setup');
|
var setupHooks = videojs.hooks('setup');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Removing
|
### Removing
|
||||||
|
|
||||||
To stop hooks from being executed during the Video.js lifecycle you will remove them using `videojs.removeHook`.
|
To stop hooks from being executed during any future Video.js lifecycles you can remove them using `videojs.removeHook`.
|
||||||
|
|
||||||
Example: remove a hook that was defined by you
|
#### Example
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var beforeSetup = function(videoEl, options) {};
|
var beforeSetup = function(videoEl, options) {};
|
||||||
|
|
||||||
// add the hook
|
// Add the hook.
|
||||||
videojs.hook('beforesetup', beforeSetup);
|
videojs.hook('beforesetup', beforeSetup);
|
||||||
|
|
||||||
// remove that same hook
|
// Remove the same hook.
|
||||||
videojs.removeHook('beforesetup', beforeSetup);
|
videojs.removeHook('beforesetup', beforeSetup);
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use `videojs.hooks` in conjunction with `videojs.removeHook` but it may have unexpected results if used during an asynchronous callbacks as other plugins/functionality may have added hooks.
|
|
||||||
|
|
||||||
Example: using `videojs.hooks` and `videojs.removeHook` to remove a hook
|
|
||||||
|
|
||||||
```js
|
|
||||||
// add the hook
|
|
||||||
videojs.hook('setup', function(videoEl, options) {});
|
|
||||||
|
|
||||||
var setupHooks = videojs.hooks('setup');
|
|
||||||
|
|
||||||
// remove the hook you just added
|
|
||||||
videojs.removeHook('setup', setupHooks[setupHooks.length - 1]);
|
|
||||||
```
|
|
||||||
|
@ -140,8 +140,8 @@ videojs.hooks_ = {};
|
|||||||
* @param {string} type
|
* @param {string} type
|
||||||
* the lifecyle to get hooks from
|
* the lifecyle to get hooks from
|
||||||
*
|
*
|
||||||
* @param {Function} [fn]
|
* @param {Function|Function[]} [fn]
|
||||||
* Optionally add a hook to the lifecycle that your are getting.
|
* Optionally add a hook (or hooks) to the lifecycle that your are getting.
|
||||||
*
|
*
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
* an array of hooks, or an empty array if there are none.
|
* an array of hooks, or an empty array if there are none.
|
||||||
@ -167,6 +167,26 @@ videojs.hook = function(type, fn) {
|
|||||||
videojs.hooks(type, fn);
|
videojs.hooks(type, fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a function hook that will only run once to a specific videojs lifecycle.
|
||||||
|
*
|
||||||
|
* @param {string} type
|
||||||
|
* the lifecycle to hook the function to.
|
||||||
|
*
|
||||||
|
* @param {Function|Function[]}
|
||||||
|
* The function or array of functions to attach.
|
||||||
|
*/
|
||||||
|
videojs.hookOnce = function(type, fn) {
|
||||||
|
videojs.hooks(type, [].concat(fn).map(original => {
|
||||||
|
const wrapper = (...args) => {
|
||||||
|
videojs.removeHook(type, wrapper);
|
||||||
|
original(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a hook from a specific videojs lifecycle.
|
* Remove a hook from a specific videojs lifecycle.
|
||||||
*
|
*
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* eslint-env qunit */
|
/* eslint-env qunit */
|
||||||
import videojs from '../../src/js/video.js';
|
import videojs from '../../src/js/video.js';
|
||||||
import document from 'global/document';
|
import document from 'global/document';
|
||||||
|
import sinon from 'sinon';
|
||||||
import log from '../../src/js/utils/log.js';
|
import log from '../../src/js/utils/log.js';
|
||||||
|
|
||||||
QUnit.module('video.js:hooks ', {
|
QUnit.module('video.js:hooks ', {
|
||||||
@ -23,9 +24,7 @@ QUnit.test('should be able to add a hook', function(assert) {
|
|||||||
assert.equal(videojs.hooks_.bar.length, 2, 'should have 2 bar hooks');
|
assert.equal(videojs.hooks_.bar.length, 2, 'should have 2 bar hooks');
|
||||||
assert.equal(videojs.hooks_.foo.length, 1, 'should have 1 foo hook');
|
assert.equal(videojs.hooks_.foo.length, 1, 'should have 1 foo hook');
|
||||||
|
|
||||||
videojs.hook('foo', function() {});
|
videojs.hook('foo', [function() {}, function() {}, function() {}]);
|
||||||
videojs.hook('foo', function() {});
|
|
||||||
videojs.hook('foo', function() {});
|
|
||||||
assert.equal(videojs.hooks_.foo.length, 4, 'should have 4 foo hooks');
|
assert.equal(videojs.hooks_.foo.length, 4, 'should have 4 foo hooks');
|
||||||
assert.equal(videojs.hooks_.bar.length, 2, 'should have 2 bar hooks');
|
assert.equal(videojs.hooks_.bar.length, 2, 'should have 2 bar hooks');
|
||||||
});
|
});
|
||||||
@ -104,6 +103,26 @@ QUnit.test('should be get all hooks for a type and add at the same time', functi
|
|||||||
assert.deepEqual(videojs.hooks_.bar, barHooks, 'should return the exact bar list from videojs.hooks_');
|
assert.deepEqual(videojs.hooks_.bar, barHooks, 'should return the exact bar list from videojs.hooks_');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test('should be able to add a hook that runs once', function(assert) {
|
||||||
|
const spies = [
|
||||||
|
sinon.spy(),
|
||||||
|
sinon.spy(),
|
||||||
|
sinon.spy()
|
||||||
|
];
|
||||||
|
|
||||||
|
videojs.hookOnce('foo', spies);
|
||||||
|
|
||||||
|
assert.equal(videojs.hooks_.foo.length, 3, 'should have 3 foo hooks');
|
||||||
|
|
||||||
|
videojs.hooks('foo').forEach(fn => fn());
|
||||||
|
|
||||||
|
spies.forEach((spy, i) => {
|
||||||
|
assert.ok(spy.calledOnce, `spy #${i + 1} was called`);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(videojs.hooks_.foo.length, 0, 'should have 0 foo hooks');
|
||||||
|
});
|
||||||
|
|
||||||
QUnit.test('should trigger beforesetup and setup during videojs setup', function(assert) {
|
QUnit.test('should trigger beforesetup and setup during videojs setup', function(assert) {
|
||||||
const vjsOptions = {techOrder: ['techFaker']};
|
const vjsOptions = {techOrder: ['techFaker']};
|
||||||
let setupCalled = false;
|
let setupCalled = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user