Reputation: 405
I need to put a hook into Youtube's iFrame API that assigns the resulting player to a global variable (I can't touch the code that actually calls the API).
The API gets called like so:
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
What I'd like to do is something like this:
YT.OldPlayer = YT.Player
YT.Player = function() {
window.YTPlayer = new YT.OldPlayer(arguments)
return window.YTPlayer
}
YT.Player.prototype = YT.OldPlayer.prototype
But for some reason calls to new YT.Player()
, after I've modified it, cause an error that doesn't get called with the original. I know the new constructor is being called, but something in the call to YT.OldPlayer()
causes the error, because window.YTPlayer
is undefined. What am I doing wrong?
Upvotes: 0
Views: 257
Reputation: 19347
This may not be the answer you are looking for, but as a PSA:
What am I doing wrong?
I think the main problem here is that you are attempting to monkey-patch functionality on an external library.
This is not a good idea not only because it may not work as you think it works, but also because it may break in the future without any warnings.
You are modifying functionality on a function that the library may use internally.
There may be other functionality in the library that makes assumptions and depends on specific behaviors of this function, and you don't have much visibility unless you read the entire library.
Even if it works, the library may change its assumptions at any time, without warning.
Have in mind that you will probably not test all the use cases for this function, and that the authors of the library make no commitment about how internal functionality works. They can change their code to use, or stop using, the function in question, change the internals of how the function works, etc, without really needing to advertise the changes. This will almost certainly break your code in unforeseeable ways, so you should stay away from this pattern as much as possible.
In conclusion: If you can, find a way of solving your problem without touching internals of libraries you don't control you will save yourself a lot of trouble.
If you still absolutely need to do this, your problem seems to be that you are passing arguments as an array-like object which is not what you get when your constructor is called.
If you can use ES6 rest parameters and spread syntax, you can do:
YT.OldPlayer = YT.Player
YT.Player = function(...args) {
window.YTPlayer = new YT.OldPlayer(...args)
return window.YTPlayer
}
YT.Player.prototype = YT.OldPlayer.prototype
If you can't use either, because you need to support IE and don't have any transpilation set-up, for example, you would need to find a workaround:
YT.OldPlayer = YT.Player
YT.Player = function() {
window.YTPlayer = new (YT.OldPlayer.bind.apply(YT,arguments))
return window.YTPlayer
}
YT.Player.prototype = YT.OldPlayer.prototype
Upvotes: 2
Reputation: 405
The solution I ended up with is
YT.OldPlayer = YT.Player
YT.Player = function() {
window.YTPlayer = new YT.OldPlayer(...arguments)
return window.YTPlayer
}
YT.Player.prototype = YT.OldPlayer.prototype
Upvotes: 0
Reputation: 18931
Try applying the arguments: window.YTPlayer = new YT.OldPlayer.apply(this, arguments)
Upvotes: 0