mirror of
https://github.com/videojs/video.js.git
synced 2024-12-25 02:42:10 +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 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
|
||||
|
||||
@ -18,129 +18,153 @@ Currently, the following hooks are available:
|
||||
|
||||
### 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 dom video element that will be used for the player
|
||||
* 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 that will be created.
|
||||
|
||||
`beforesetup` hook functions should:
|
||||
|
||||
* take two arguments
|
||||
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
|
||||
* return options that will merge and override options that Video.js with intialized with
|
||||
* Take two arguments:
|
||||
1. `videoEl`: DOM `<video>` element that Video.js is going to use to create a player.
|
||||
1. `options`: The options object that Video.js was called with and will be passed to the player during creation.
|
||||
* Return an options object that will be merged with the originally provided options.
|
||||
|
||||
Example: adding beforesetup hook
|
||||
#### Example
|
||||
|
||||
```js
|
||||
var beforeSetup = function(videoEl, options) {
|
||||
// videoEl.id will be some-id here, since that is what Video.js
|
||||
// was created with
|
||||
videojs.hook('beforesetup', function(videoEl, options) {
|
||||
|
||||
// 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';
|
||||
|
||||
// autoplay will be true here, since we passed in as such
|
||||
(options.autoplay) {
|
||||
// autoplay will be true here, since we passed it as such.
|
||||
if (options.autoplay) {
|
||||
options.autoplay = false
|
||||
}
|
||||
|
||||
// options that are returned here will be merged with old options
|
||||
// in this example options will now be
|
||||
// {autoplay: false, controls: true}
|
||||
// Options that are returned here will be merged with old options.
|
||||
//
|
||||
// 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;
|
||||
};
|
||||
});
|
||||
|
||||
videojs.hook('beforesetup', beforeSetup);
|
||||
// Create a new player.
|
||||
videojs('some-id', {autoplay: true, controls: true});
|
||||
```
|
||||
|
||||
### 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
|
||||
* changes to the player object itself
|
||||
* Plugins or other custom functionality to initialize on the player.
|
||||
* Changes to the player object itself.
|
||||
|
||||
`setup` hook functions:
|
||||
|
||||
* Take one argument
|
||||
* player: the player that Video.js created
|
||||
* Take one argument:
|
||||
1. `player`: the player that Video.js created
|
||||
* Don't have to return anything
|
||||
|
||||
Example: adding a setup hook
|
||||
#### Example
|
||||
|
||||
```js
|
||||
var setup = function(player) {
|
||||
// initialize the foo plugin
|
||||
player.foo();
|
||||
};
|
||||
var foo = function() {};
|
||||
videojs.registerPlugin('foo', function() {
|
||||
|
||||
videojs.registerPlugin('foo', foo);
|
||||
videojs.hook('setup', setup);
|
||||
var player = videojs('some-id', {autoplay: true, controls: true});
|
||||
// This basic plugin will add the "some-super-class" class to a player.
|
||||
this.addClass('some-super-class');
|
||||
});
|
||||
|
||||
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
|
||||
|
||||
### 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
|
||||
videojs.hook('beforesetup', function(videoEl, options) {
|
||||
// videoEl will be the element with id=vid1
|
||||
// options will contain {autoplay: false}
|
||||
});
|
||||
videojs.hook('setup', function(player) {
|
||||
// player will be the same player that is defined below
|
||||
// as `var player`
|
||||
// This hook will be called twice. Once for "vid1" and once for "vid2".
|
||||
// The options will match what is passed to videojs() for each of them.
|
||||
});
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
// Get an array of all the 'beforesetup' hooks.
|
||||
var beforeSetupHooks = videojs.hooks('beforesetup');
|
||||
|
||||
// Get an array of all the 'setup' hooks.
|
||||
var setupHooks = videojs.hooks('setup');
|
||||
```
|
||||
|
||||
### 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
|
||||
var beforeSetup = function(videoEl, options) {};
|
||||
|
||||
// add the hook
|
||||
// Add the hook.
|
||||
videojs.hook('beforesetup', beforeSetup);
|
||||
|
||||
// remove that same hook
|
||||
// Remove the same hook.
|
||||
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
|
||||
* the lifecyle to get hooks from
|
||||
*
|
||||
* @param {Function} [fn]
|
||||
* Optionally add a hook to the lifecycle that your are getting.
|
||||
* @param {Function|Function[]} [fn]
|
||||
* Optionally add a hook (or hooks) to the lifecycle that your are getting.
|
||||
*
|
||||
* @return {Array}
|
||||
* 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* eslint-env qunit */
|
||||
import videojs from '../../src/js/video.js';
|
||||
import document from 'global/document';
|
||||
import sinon from 'sinon';
|
||||
import log from '../../src/js/utils/log.js';
|
||||
|
||||
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_.foo.length, 1, 'should have 1 foo hook');
|
||||
|
||||
videojs.hook('foo', function() {});
|
||||
videojs.hook('foo', function() {});
|
||||
videojs.hook('foo', function() {});
|
||||
videojs.hook('foo', [function() {}, function() {}, function() {}]);
|
||||
assert.equal(videojs.hooks_.foo.length, 4, 'should have 4 foo 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_');
|
||||
});
|
||||
|
||||
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) {
|
||||
const vjsOptions = {techOrder: ['techFaker']};
|
||||
let setupCalled = false;
|
||||
|
Loading…
Reference in New Issue
Block a user