Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement lifecycle hooks and trigger pre/post setup hooks #3639

Merged
merged 10 commits into from
Nov 4, 2016
Merged

implement lifecycle hooks and trigger pre/post setup hooks #3639

merged 10 commits into from
Nov 4, 2016

Conversation

brandonocasey
Copy link
Contributor

Description

There is no current way to hook into the setup that video.js does, and this functionality would be useful for many plugins to use. (they can even initialize themselves with it)

Specific Changes proposed

  • Add several "hook" functions to the videojs object
  • Add pre/post setup lifecycle event triggers

Requirements Checklist

  • Feature implemented / Bug fixed
  • Reviewed by Two Core Contributors

Copy link
Member

@gkatsev gkatsev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that the hook function needs ready.
Also, would be good to add a guide documenting this feature.

}

videojs.hooks('presetup').forEach(function(hookFunction) {
options = videojs.mergeOptions(options, hookFunction(tag, options, ready));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why pass ready to the hook?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably don't need to

* @param {String} type the lifecyle event to get hooks from
* @return {Array} an array of hooks, or an empty array if there are none
*/
videojs.hooks = function(type) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is hooks a too-generic term? would we want any more hooks on the main videojs object beyond presetup and postsetup?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe pre/post shutdown? (there is no notion of this yet though

return tag.player || Player.players[tag.playerId];
}

videojs.hooks('presetup').forEach(function(hookFunction) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ModalDialog component has similar lifecycle events, but the naming scheme is:

  • beforefoo: Happens before the thing.
  • foo: Happens after the thing.

Maybe we ought to follow that universally and use beforesetup and setup?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could, and previously I had it implemented like that, it just seemed like a lot of duplicate code

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't changing it just just involve changing postsetup on line 111 into setup?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And presetup to beforesetup? I don't see where the duplicate code concern comes from...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the possible implementations with that naming scheme & the pro's/con's. do you see any other ways to implement it that sound better? (maybe I am just not seeing a good implementation?)

Also I wanted to note that using setup seem a bit weird to me since it will actually be called postSetup and not during setup

Add lifecycle setters, getters, removers, and lists in a loop to the prototype

  • can make things confusing
  • will not duplicate code
  • will pollute the video.js object with any lifecycle event functions that we need to add
  • may be more intuitive for a new user to call beforeSetup and setup to access things

use the hooks terminology internally and add lifecycle setters, getters, removers, and lists to the prototype in a loop

  • Makes me think we should just use hooks directly
  • will only pollute the video.js object with wrapper functions
  • will not duplicate code
  • may be more intuitive for the user

implement lifecycle functions without any looping or hooks terminology

  • Every time we need to expose a new event we have to duplicate functionality (adding a setter, getter, remover, and a list to video.js)
  • will pollute video.js with wrapper functions
  • lots of duplicate code
  • may be intuitive for the user

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is resolved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, thanks!

@gkatsev gkatsev added this to the 5.13 milestone Sep 27, 2016
@@ -0,0 +1,85 @@
# Hooks
Hooks exist so that users can latch on to certain Video.js lifecycle events
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we ought to explain that this is a separate concept from per-player events. In fact, we should probably not use the term "events" at all, lest we cause confusion with events-events.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe something like:
Hooks exist so that users can hook into the video.js player lifecycle

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conceptually similar to events, hooks provide users a way to intercept universal behaviors of video.js itself rather than individual players.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are they similar to events though? You don't generally have the ability to do an action like what we're doing with these hooks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, but they are conceptually similar in the sense that you're providing a function to be called when a certain condition is met. But, of course, I'm not married to any particular phrasing. 😄

// automatically
return options;
};
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this example (and the one below) is unclear without the videojs.hook() call.



## Current Hooks
Currently the following lifecycle events are accept and use hooks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the heading can go away. And the phrasing/grammar here is weird. Maybe:

Currently, the following hooks are available:

Currently the following lifecycle events are accept and use hooks

### beforesetup
Called just before the player is created, so that the user can modify the options passed to the player or modify the video element that will be used for the player
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a period here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should mention here (and not just in the example) what happens if multiple hooks are.
Also, state that the method takes in the video element and an options object and is expected to return a new options object.

@@ -0,0 +1,85 @@
# Hooks
Hooks exist so that users can latch on to certain Video.js lifecycle events
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe something like:
Hooks exist so that users can hook into the video.js player lifecycle

Currently the following lifecycle events are accept and use hooks

### beforesetup
Called just before the player is created, so that the user can modify the options passed to the player or modify the video element that will be used for the player
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should mention here (and not just in the example) what happens if multiple hooks are.
Also, state that the method takes in the video element and an options object and is expected to return a new options object.

}

videojs.hooks('beforesetup').forEach(function(hookFunction) {
options = videojs.mergeOptions(options, hookFunction(tag, videojs.mergeOptions({}, options)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if the hookFunction returns null or a string?
We probably should write some tests for this.

It may be easier to write tests if these are moved into a separate file and then copied over onto the videojs object but either way should work.

*/
videojs.hook = function(type, fn) {
videojs.hooks_[type] = videojs.hooks_[type] || [];
videojs.hooks_[type].push(fn);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of using push, we should do videojs.hooks_[type] = videojs.hooks_[type].concat(fn);.
See videojs/mux.js#121

* @return {Function|undefined} the function that was removed or undef
*/
videojs.removeHook = function(type, fn) {
const hooks = videojs.hooks_[type] || [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should also slice here as well. See videojs/mux.js#121 again.

@brandonocasey
Copy link
Contributor Author

I still have to write some unit tests for this functionality

* @param {String} type the lifecyle event to get hooks from
* @return {Array} an array of hooks, or an empty array if there are none
*/
videojs.hooks = function(type) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a check to hooks that if it's called with two arguments either logs an error or calls hook for the user? Just ran into this problem and took a second to figure it out.

Copy link
Member

@gkatsev gkatsev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With proposed changes, LGTM.

* @param {Function|Array} fn the function to attach
*/
videojs.hook = function(type, fn) {
const hooks = videojs.hooks[type];
Copy link
Member

@gkatsev gkatsev Oct 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be videojs.hooks_[type] || [] or videojs.hooks(type);

videojs.hook = function(type, fn) {
const hooks = videojs.hooks[type];

hooks[type].concat(fn);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be videojs.hooks_[type] = hooks.concat(fn);

@gkatsev gkatsev added minor This PR can be added to a minor release. It should not be added to a patch release. needs: updates labels Oct 3, 2016
}

videojs.hooks('beforesetup').forEach(function(hookFunction) {
const opts = hookFunction(tag, videojs.mergeOptions({}, options));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically, each handler that doesn't return an object is ignored?

Copy link
Contributor Author

@brandonocasey brandonocasey Oct 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well they are still run, we just assume they don't want to change options

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, makes sense. Meant the response is ignored.

videojs.hooks_[type] = videojs.hooks_[type].slice();
videojs.hooks_[type].splice(index, 1);

return index > -1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe also do this check above so we don't run the extra slice and splice unless necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

assert.equal(videojs.hooks_.bar.length, 0, 'should have 0 bar hook');
});

QUnit.test('should be get all hooks for a type', function(assert) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be able to get all hooks for a type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, good catch

let setupCalled = false;
let beforeSetupCalled = false;
const beforeSetup = function(video, options) {
beforeSetupCalled = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about asserting that setupCalled is still false in here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, also added a check for beforeSetupCalled in the setup function

assert.equal(setupCalled, true, 'setup was called');
});

QUnit.test('beforesetup returns dont break videojs options', function(assert) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want a positive test case for this as well?
Something like: beforesetup hooks modify options properly and merge each item in the list

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point


const vid = document.getElementById('test_vid_id');

videojs.hook('beforesetup', function(options) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add another hook that say adds fluid: true option?

@gkatsev gkatsev merged commit 77357b1 into videojs:master Nov 4, 2016
@brandonocasey brandonocasey deleted the feature/setup-hook branch December 6, 2016 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed minor This PR can be added to a minor release. It should not be added to a patch release.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants