mirror of
https://github.com/videojs/video.js.git
synced 2024-11-28 08:58:46 +02:00
Added jhurliman's YouTube updates.
This commit is contained in:
parent
9343610596
commit
d71b0858d4
20
build/release-files/youtube.html
Normal file
20
build/release-files/youtube.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Video.js | HTML5 Video Player | YouTube Demo</title>
|
||||
|
||||
<!-- Change URLs to wherever Video.js files will be hosted -->
|
||||
<link href="video-js.css" rel="stylesheet" type="text/css">
|
||||
<!-- video.js must be in the <head> for older IEs to work. -->
|
||||
<script src="video.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" width="640" height="360"
|
||||
data-setup='{"techOrder":["youtube","html5"],"ytcontrols":false}'>
|
||||
<source src="http://www.youtube.com/watch?v=qWjzVHG9T1I" type='video/youtube' />
|
||||
</video>
|
||||
|
||||
</body>
|
||||
</html>
|
44
docs/tech.md
44
docs/tech.md
@ -44,3 +44,47 @@ timeupdate
|
||||
progress
|
||||
enterFullScreen
|
||||
exitFullScreen
|
||||
|
||||
Adding Playback Technology
|
||||
==================
|
||||
When adding additional Tech to a video player, make sure to add the supported tech to the video object.
|
||||
|
||||
### Tag Method: ###
|
||||
<video data-setup='{"techOrder", "html5", "flash", [other supported tech]}'
|
||||
|
||||
### Object Method: ###
|
||||
_V_("videoID", {
|
||||
techOrder: {"html5", "flash", [other supported tech]}
|
||||
});
|
||||
|
||||
Youtube Technology
|
||||
==================
|
||||
To add a youtube source to your video tag, use the following source:
|
||||
|
||||
<source src="http://www.youtube.com/watch?v=[ytVideoId]" type="video/youtube"
|
||||
|
||||
Important Note:
|
||||
------------------
|
||||
> You can simply copy and paste the url of the youtube page from the browser and
|
||||
> the Youtube Tech will be able to find the video id by itself. This is just the
|
||||
> minimum needed to get the video working. (Useful for data storage)
|
||||
|
||||
|
||||
Youtube Technology - Extra Options
|
||||
----------------------------------
|
||||
|
||||
In Addition to the natively supported options, the Youtube API supports the following
|
||||
added options:
|
||||
|
||||
### ytcontrols ###
|
||||
Type: Boolean (T/F)
|
||||
Default: False
|
||||
|
||||
Determines whether to show Youtube's basic Red/Black default play bar skin or to hide
|
||||
it and use the native video-js play bar.
|
||||
|
||||
### hd ###
|
||||
Type: Boolean (T/F)
|
||||
Default: False
|
||||
|
||||
Determines whether or not to play back the video in HD.
|
||||
|
679
src/tech.js
679
src/tech.js
@ -25,680 +25,5 @@ _V_.apiMethods = "play,pause,paused,currentTime,setCurrentTime,duration,buffered
|
||||
_V_.each(_V_.apiMethods, function(methodName){
|
||||
_V_.PlaybackTech.prototype[methodName] = function(){
|
||||
throw new Error("The '"+methodName+"' method is not available on the playback technology's API");
|
||||
}
|
||||
});
|
||||
|
||||
/* HTML5 Playback Technology - Wrapper for HTML5 Media API
|
||||
================================================================================ */
|
||||
_V_.html5 = _V_.PlaybackTech.extend({
|
||||
|
||||
init: function(player, options, ready){
|
||||
this.player = player;
|
||||
this.el = this.createElement();
|
||||
this.ready(ready);
|
||||
|
||||
this.addEvent("click", this.proxy(this.onClick));
|
||||
|
||||
var source = options.source;
|
||||
|
||||
// If the element source is already set, we may have missed the loadstart event, and want to trigger it.
|
||||
// We don't want to set the source again and interrupt playback.
|
||||
if (source && this.el.currentSrc == source.src) {
|
||||
player.triggerEvent("loadstart");
|
||||
|
||||
// Otherwise set the source if one was provided.
|
||||
} else if (source) {
|
||||
this.el.src = source.src;
|
||||
}
|
||||
|
||||
// Chrome and Safari both have issues with autoplay.
|
||||
// In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
|
||||
// In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
|
||||
// This fixes both issues. Need to wait for API, so it updates displays correctly
|
||||
player.ready(function(){
|
||||
if (this.options.autoplay && this.paused()) {
|
||||
this.tag.poster = null; // Chrome Fix. Fixed in Chrome v16.
|
||||
this.play();
|
||||
}
|
||||
});
|
||||
|
||||
this.setupTriggers();
|
||||
|
||||
this.triggerReady();
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.player.tag = false;
|
||||
this.removeTriggers();
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
},
|
||||
|
||||
createElement: function(){
|
||||
var html5 = _V_.html5,
|
||||
player = this.player,
|
||||
|
||||
// If possible, reuse original tag for HTML5 playback technology element
|
||||
el = player.tag,
|
||||
newEl;
|
||||
|
||||
// Check if this browser supports moving the element into the box.
|
||||
// On the iPhone video will break if you move the element,
|
||||
// So we have to create a brand new element.
|
||||
if (!el || this.support.movingElementInDOM === false) {
|
||||
|
||||
// If the original tag is still there, remove it.
|
||||
if (el) {
|
||||
player.el.removeChild(el);
|
||||
}
|
||||
|
||||
newEl = _V_.createElement("video", {
|
||||
id: el.id || player.el.id + "_html5_api",
|
||||
className: el.className || "vjs-tech"
|
||||
});
|
||||
|
||||
el = newEl;
|
||||
_V_.insertFirst(el, player.el);
|
||||
}
|
||||
|
||||
// Update tag settings, in case they were overridden
|
||||
_V_.each(["autoplay","preload","loop","muted"], function(attr){ // ,"poster"
|
||||
if (player.options[attr] !== null) {
|
||||
el[attr] = player.options[attr];
|
||||
}
|
||||
}, this);
|
||||
|
||||
return el;
|
||||
},
|
||||
|
||||
// Make video events trigger player events
|
||||
// May seem verbose here, but makes other APIs possible.
|
||||
setupTriggers: function(){
|
||||
_V_.each.call(this, _V_.html5.events, function(type){
|
||||
_V_.addEvent(this.el, type, _V_.proxy(this.player, this.eventHandler));
|
||||
});
|
||||
},
|
||||
removeTriggers: function(){
|
||||
_V_.each.call(this, _V_.html5.events, function(type){
|
||||
_V_.removeEvent(this.el, type, _V_.proxy(this.player, this.eventHandler));
|
||||
});
|
||||
},
|
||||
eventHandler: function(e){
|
||||
e.stopPropagation();
|
||||
this.triggerEvent(e);
|
||||
},
|
||||
|
||||
play: function(){ this.el.play(); },
|
||||
pause: function(){ this.el.pause(); },
|
||||
paused: function(){ return this.el.paused; },
|
||||
|
||||
currentTime: function(){ return this.el.currentTime; },
|
||||
setCurrentTime: function(seconds){
|
||||
try {
|
||||
this.el.currentTime = seconds;
|
||||
} catch(e) {
|
||||
_V_.log(e, "Video isn't ready. (VideoJS)");
|
||||
// this.warning(VideoJS.warnings.videoNotReady);
|
||||
}
|
||||
},
|
||||
|
||||
duration: function(){ return this.el.duration || 0; },
|
||||
buffered: function(){ return this.el.buffered; },
|
||||
|
||||
volume: function(){ return this.el.volume; },
|
||||
setVolume: function(percentAsDecimal){ this.el.volume = percentAsDecimal; },
|
||||
muted: function(){ return this.el.muted; },
|
||||
setMuted: function(muted){ this.el.muted = muted },
|
||||
|
||||
width: function(){ return this.el.offsetWidth; },
|
||||
height: function(){ return this.el.offsetHeight; },
|
||||
|
||||
supportsFullScreen: function(){
|
||||
if (typeof this.el.webkitEnterFullScreen == 'function') {
|
||||
|
||||
// Seems to be broken in Chromium/Chrome && Safari in Leopard
|
||||
if (!navigator.userAgent.match("Chrome") && !navigator.userAgent.match("Mac OS X 10.5")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
enterFullScreen: function(){
|
||||
try {
|
||||
this.el.webkitEnterFullScreen();
|
||||
} catch (e) {
|
||||
if (e.code == 11) {
|
||||
// this.warning(VideoJS.warnings.videoNotReady);
|
||||
_V_.log("VideoJS: Video not ready.")
|
||||
}
|
||||
}
|
||||
},
|
||||
exitFullScreen: function(){
|
||||
try {
|
||||
this.el.webkitExitFullScreen();
|
||||
} catch (e) {
|
||||
if (e.code == 11) {
|
||||
// this.warning(VideoJS.warnings.videoNotReady);
|
||||
_V_.log("VideoJS: Video not ready.")
|
||||
}
|
||||
}
|
||||
},
|
||||
src: function(src){ this.el.src = src; },
|
||||
load: function(){ this.el.load(); },
|
||||
currentSrc: function(){ return this.el.currentSrc; },
|
||||
|
||||
preload: function(){ return this.el.preload; },
|
||||
setPreload: function(val){ this.el.preload = val; },
|
||||
autoplay: function(){ return this.el.autoplay; },
|
||||
setAutoplay: function(val){ this.el.autoplay = val; },
|
||||
loop: function(){ return this.el.loop; },
|
||||
setLoop: function(val){ this.el.loop = val; },
|
||||
|
||||
error: function(){ return this.el.error; },
|
||||
// networkState: function(){ return this.el.networkState; },
|
||||
// readyState: function(){ return this.el.readyState; },
|
||||
seeking: function(){ return this.el.seeking; },
|
||||
// initialTime: function(){ return this.el.initialTime; },
|
||||
// startOffsetTime: function(){ return this.el.startOffsetTime; },
|
||||
// played: function(){ return this.el.played; },
|
||||
// seekable: function(){ return this.el.seekable; },
|
||||
ended: function(){ return this.el.ended; },
|
||||
// videoTracks: function(){ return this.el.videoTracks; },
|
||||
// audioTracks: function(){ return this.el.audioTracks; },
|
||||
// videoWidth: function(){ return this.el.videoWidth; },
|
||||
// videoHeight: function(){ return this.el.videoHeight; },
|
||||
// textTracks: function(){ return this.el.textTracks; },
|
||||
// defaultPlaybackRate: function(){ return this.el.defaultPlaybackRate; },
|
||||
// playbackRate: function(){ return this.el.playbackRate; },
|
||||
// mediaGroup: function(){ return this.el.mediaGroup; },
|
||||
// controller: function(){ return this.el.controller; },
|
||||
controls: function(){ return this.player.options.controls; },
|
||||
defaultMuted: function(){ return this.el.defaultMuted; }
|
||||
});
|
||||
|
||||
/* HTML5 Support Testing -------------------------------------------------------- */
|
||||
|
||||
_V_.html5.isSupported = function(){
|
||||
return !!document.createElement("video").canPlayType;
|
||||
};
|
||||
|
||||
_V_.html5.canPlaySource = function(srcObj){
|
||||
return !!document.createElement("video").canPlayType(srcObj.type);
|
||||
// TODO: Check Type
|
||||
// If no Type, check ext
|
||||
// Check Media Type
|
||||
};
|
||||
|
||||
// List of all HTML5 events (various uses).
|
||||
_V_.html5.events = "loadstart,suspend,abort,error,emptied,stalled,loadedmetadata,loadeddata,canplay,canplaythrough,playing,waiting,seeking,seeked,ended,durationchange,timeupdate,progress,play,pause,ratechange,volumechange".split(",");
|
||||
|
||||
/* HTML5 Device Fixes ---------------------------------------------------------- */
|
||||
|
||||
_V_.html5.prototype.support = {
|
||||
|
||||
// Support for tech specific full screen. (webkitEnterFullScreen, not requestFullscreen)
|
||||
// http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html
|
||||
// Seems to be broken in Chromium/Chrome && Safari in Leopard
|
||||
fullscreen: (typeof _V_.testVid.webkitEnterFullScreen !== undefined) ? (!_V_.ua.match("Chrome") && !_V_.ua.match("Mac OS X 10.5") ? true : false) : false,
|
||||
|
||||
// In iOS, if you move a video element in the DOM, it breaks video playback.
|
||||
movingElementInDOM: !_V_.isIOS()
|
||||
|
||||
};
|
||||
|
||||
// Android
|
||||
if (_V_.isAndroid()) {
|
||||
|
||||
// Override Android 2.2 and less canPlayType method which is broken
|
||||
if (_V_.androidVersion() < 3) {
|
||||
document.createElement("video").constructor.prototype.canPlayType = function(type){
|
||||
return (type && type.toLowerCase().indexOf("video/mp4") != -1) ? "maybe" : "";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* VideoJS-SWF - Custom Flash Player with HTML5-ish API - https://github.com/zencoder/video-js-swf
|
||||
================================================================================ */
|
||||
_V_.flash = _V_.PlaybackTech.extend({
|
||||
|
||||
init: function(player, options){
|
||||
this.player = player;
|
||||
|
||||
var source = options.source,
|
||||
|
||||
// Which element to embed in
|
||||
parentEl = options.parentEl,
|
||||
|
||||
// Create a temporary element to be replaced by swf object
|
||||
placeHolder = this.el = _V_.createElement("div", { id: parentEl.id + "_temp_flash" }),
|
||||
|
||||
// Generate ID for swf object
|
||||
objId = player.el.id+"_flash_api",
|
||||
|
||||
// Store player options in local var for optimization
|
||||
playerOptions = player.options,
|
||||
|
||||
// Merge default flashvars with ones passed in to init
|
||||
flashVars = _V_.merge({
|
||||
|
||||
// SWF Callback Functions
|
||||
readyFunction: "_V_.flash.onReady",
|
||||
eventProxyFunction: "_V_.flash.onEvent",
|
||||
errorEventProxyFunction: "_V_.flash.onError",
|
||||
|
||||
// Player Settings
|
||||
autoplay: playerOptions.autoplay,
|
||||
preload: playerOptions.preload,
|
||||
loop: playerOptions.loop,
|
||||
muted: playerOptions.muted
|
||||
|
||||
}, options.flashVars),
|
||||
|
||||
// Merge default parames with ones passed in
|
||||
params = _V_.merge({
|
||||
wmode: "opaque", // Opaque is needed to overlay controls, but can affect playback performance
|
||||
bgcolor: "#000000" // Using bgcolor prevents a white flash when the object is loading
|
||||
}, options.params),
|
||||
|
||||
// Merge default attributes with ones passed in
|
||||
attributes = _V_.merge({
|
||||
id: objId,
|
||||
name: objId, // Both ID and Name needed or swf to identifty itself
|
||||
'class': 'vjs-tech'
|
||||
}, options.attributes)
|
||||
;
|
||||
|
||||
// If source was supplied pass as a flash var.
|
||||
if (source) {
|
||||
flashVars.src = encodeURIComponent(_V_.getAbsoluteURL(source.src));
|
||||
}
|
||||
|
||||
// Add placeholder to player div
|
||||
_V_.insertFirst(placeHolder, parentEl);
|
||||
|
||||
// Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers
|
||||
// This allows resetting the playhead when we catch the reload
|
||||
if (options.startTime) {
|
||||
this.ready(function(){
|
||||
this.load();
|
||||
this.play();
|
||||
this.currentTime(options.startTime);
|
||||
});
|
||||
}
|
||||
|
||||
// Flash iFrame Mode
|
||||
// In web browsers there are multiple instances where changing the parent element or visibility of a plugin causes the plugin to reload.
|
||||
// - Firefox just about always. https://bugzilla.mozilla.org/show_bug.cgi?id=90268 (might be fixed by version 13)
|
||||
// - Webkit when hiding the plugin
|
||||
// - Webkit and Firefox when using requestFullScreen on a parent element
|
||||
// Loading the flash plugin into a dynamically generated iFrame gets around most of these issues.
|
||||
// Issues that remain include hiding the element and requestFullScreen in Firefox specifically
|
||||
|
||||
// There's on particularly annoying issue with this method which is that Firefox throws a security error on an offsite Flash object loaded into a dynamically created iFrame.
|
||||
// Even though the iframe was inserted into a page on the web, Firefox + Flash considers it a local app trying to access an internet file.
|
||||
// I tried mulitple ways of setting the iframe src attribute but couldn't find a src that worked well. Tried a real/fake source, in/out of domain.
|
||||
// Also tried a method from stackoverflow that caused a security error in all browsers. http://stackoverflow.com/questions/2486901/how-to-set-document-domain-for-a-dynamically-generated-iframe
|
||||
// In the end the solution I found to work was setting the iframe window.location.href right before doing a document.write of the Flash object.
|
||||
// The only downside of this it seems to trigger another http request to the original page (no matter what's put in the href). Not sure why that is.
|
||||
|
||||
// NOTE (2012-01-29): Cannot get Firefox to load the remote hosted SWF into a dynamically created iFrame
|
||||
// Firefox 9 throws a security error, unleess you call location.href right before doc.write.
|
||||
// Not sure why that even works, but it causes the browser to look like it's continuously trying to load the page.
|
||||
// Firefox 3.6 keeps calling the iframe onload function anytime I write to it, causing an endless loop.
|
||||
|
||||
if (options.iFrameMode == true && !_V_.isFF) {
|
||||
|
||||
// Create iFrame with vjs-tech class so it's 100% width/height
|
||||
var iFrm = _V_.createElement("iframe", {
|
||||
id: objId + "_iframe",
|
||||
name: objId + "_iframe",
|
||||
className: "vjs-tech",
|
||||
scrolling: "no",
|
||||
marginWidth: 0,
|
||||
marginHeight: 0,
|
||||
frameBorder: 0
|
||||
});
|
||||
|
||||
// Update ready function names in flash vars for iframe window
|
||||
flashVars.readyFunction = "ready";
|
||||
flashVars.eventProxyFunction = "events";
|
||||
flashVars.errorEventProxyFunction = "errors";
|
||||
|
||||
// Tried multiple methods to get this to work in all browsers
|
||||
|
||||
// Tried embedding the flash object in the page first, and then adding a place holder to the iframe, then replacing the placeholder with the page object.
|
||||
// The goal here was to try to load the swf URL in the parent page first and hope that got around the firefox security error
|
||||
// var newObj = _V_.flash.embed(options.swf, placeHolder, flashVars, params, attributes);
|
||||
// (in onload)
|
||||
// var temp = _V_.createElement("a", { id:"asdf", innerHTML: "asdf" } );
|
||||
// iDoc.body.appendChild(temp);
|
||||
|
||||
// Tried embedding the flash object through javascript in the iframe source.
|
||||
// This works in webkit but still triggers the firefox security error
|
||||
// iFrm.src = "javascript: document.write('"+_V_.flash.getEmbedCode(options.swf, flashVars, params, attributes)+"');";
|
||||
|
||||
// Tried an actual local iframe just to make sure that works, but it kills the easiness of the CDN version if you require the user to host an iframe
|
||||
// We should add an option to host the iframe locally though, because it could help a lot of issues.
|
||||
// iFrm.src = "iframe.html";
|
||||
|
||||
// Wait until iFrame has loaded to write into it.
|
||||
_V_.addEvent(iFrm, "load", _V_.proxy(this, function(){
|
||||
|
||||
var iDoc, objTag, swfLoc,
|
||||
iWin = iFrm.contentWindow,
|
||||
varString = "";
|
||||
|
||||
|
||||
// The one working method I found was to use the iframe's document.write() to create the swf object
|
||||
// This got around the security issue in all browsers except firefox.
|
||||
// I did find a hack where if I call the iframe's window.location.href="", it would get around the security error
|
||||
// However, the main page would look like it was loading indefinitely (URL bar loading spinner would never stop)
|
||||
// Plus Firefox 3.6 didn't work no matter what I tried.
|
||||
// if (_V_.ua.match("Firefox")) {
|
||||
// iWin.location.href = "";
|
||||
// }
|
||||
|
||||
// Get the iFrame's document depending on what the browser supports
|
||||
iDoc = iFrm.contentDocument ? iFrm.contentDocument : iFrm.contentWindow.document;
|
||||
|
||||
// Tried ensuring both document domains were the same, but they already were, so that wasn't the issue.
|
||||
// Even tried adding /. that was mentioned in a browser security writeup
|
||||
// document.domain = document.domain+"/.";
|
||||
// iDoc.domain = document.domain+"/.";
|
||||
|
||||
// Tried adding the object to the iframe doc's innerHTML. Security error in all browsers.
|
||||
// iDoc.body.innerHTML = swfObjectHTML;
|
||||
|
||||
// Tried appending the object to the iframe doc's body. Security error in all browsers.
|
||||
// iDoc.body.appendChild(swfObject);
|
||||
|
||||
// Using document.write actually got around the security error that browsers were throwing.
|
||||
// Again, it's a dynamically generated (same domain) iframe, loading an external Flash swf.
|
||||
// Not sure why that's a security issue, but apparently it is.
|
||||
iDoc.write(_V_.flash.getEmbedCode(options.swf, flashVars, params, attributes));
|
||||
|
||||
// Setting variables on the window needs to come after the doc write because otherwise they can get reset in some browsers
|
||||
// So far no issues with swf ready event being called before it's set on the window.
|
||||
iWin.player = this.player;
|
||||
|
||||
// Create swf ready function for iFrame window
|
||||
iWin.ready = _V_.proxy(this.player, function(currSwf){
|
||||
var el = iDoc.getElementById(currSwf),
|
||||
player = this,
|
||||
tech = player.tech;
|
||||
|
||||
// Update reference to playback technology element
|
||||
tech.el = el;
|
||||
|
||||
// Now that the element is ready, make a click on the swf play the video
|
||||
_V_.addEvent(el, "click", tech.proxy(tech.onClick));
|
||||
|
||||
// Make sure swf is actually ready. Sometimes the API isn't actually yet.
|
||||
_V_.flash.checkReady(tech);
|
||||
});
|
||||
|
||||
// Create event listener for all swf events
|
||||
iWin.events = _V_.proxy(this.player, function(swfID, eventName, other){
|
||||
var player = this;
|
||||
if (player && player.techName == "flash") {
|
||||
player.triggerEvent(eventName);
|
||||
}
|
||||
});
|
||||
|
||||
// Create error listener for all swf errors
|
||||
iWin.errors = _V_.proxy(this.player, function(swfID, eventName){
|
||||
_V_.log("Flash Error", eventName);
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
// Replace placeholder with iFrame (it will load now)
|
||||
placeHolder.parentNode.replaceChild(iFrm, placeHolder);
|
||||
|
||||
// If not using iFrame mode, embed as normal object
|
||||
} else {
|
||||
_V_.flash.embed(options.swf, placeHolder, flashVars, params, attributes);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
},
|
||||
|
||||
// setupTriggers: function(){}, // Using global onEvent func to distribute events
|
||||
|
||||
play: function(){ this.el.vjs_play(); },
|
||||
pause: function(){ this.el.vjs_pause(); },
|
||||
src: function(src){
|
||||
// Make sure source URL is abosolute.
|
||||
src = _V_.getAbsoluteURL(src);
|
||||
|
||||
this.el.vjs_src(src);
|
||||
|
||||
// Currently the SWF doesn't autoplay if you load a source later.
|
||||
// e.g. Load player w/ no source, wait 2s, set src.
|
||||
if (this.player.autoplay()) {
|
||||
var tech = this;
|
||||
setTimeout(function(){ tech.play(); }, 0);
|
||||
}
|
||||
},
|
||||
load: function(){ this.el.vjs_load(); },
|
||||
poster: function(){ this.el.vjs_getProperty("poster"); },
|
||||
|
||||
buffered: function(){
|
||||
return _V_.createTimeRange(0, this.el.vjs_getProperty("buffered"));
|
||||
},
|
||||
|
||||
supportsFullScreen: function(){
|
||||
return false; // Flash does not allow fullscreen through javascript
|
||||
},
|
||||
enterFullScreen: function(){
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Create setters and getters for attributes
|
||||
(function(){
|
||||
|
||||
var api = _V_.flash.prototype,
|
||||
readWrite = "preload,currentTime,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted".split(","),
|
||||
readOnly = "error,currentSrc,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks".split(","),
|
||||
callOnly = "load,play,pause".split(",");
|
||||
// Overridden: buffered
|
||||
|
||||
createSetter = function(attr){
|
||||
var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
|
||||
api["set"+attrUpper] = function(val){ return this.el.vjs_setProperty(attr, val); };
|
||||
},
|
||||
|
||||
createGetter = function(attr){
|
||||
api[attr] = function(){ return this.el.vjs_getProperty(attr); };
|
||||
}
|
||||
;
|
||||
|
||||
// Create getter and setters for all read/write attributes
|
||||
_V_.each(readWrite, function(attr){
|
||||
createGetter(attr);
|
||||
createSetter(attr);
|
||||
});
|
||||
|
||||
// Create getters for read-only attributes
|
||||
_V_.each(readOnly, function(attr){
|
||||
createGetter(attr);
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
/* Flash Support Testing -------------------------------------------------------- */
|
||||
|
||||
_V_.flash.isSupported = function(){
|
||||
return _V_.flash.version()[0] >= 10;
|
||||
// return swfobject.hasFlashPlayerVersion("10");
|
||||
};
|
||||
|
||||
_V_.flash.canPlaySource = function(srcObj){
|
||||
if (srcObj.type in _V_.flash.prototype.support.formats) { return "maybe"; }
|
||||
};
|
||||
|
||||
_V_.flash.prototype.support = {
|
||||
formats: {
|
||||
"video/flv": "FLV",
|
||||
"video/x-flv": "FLV",
|
||||
"video/mp4": "MP4",
|
||||
"video/m4v": "MP4"
|
||||
},
|
||||
|
||||
// Optional events that we can manually mimic with timers
|
||||
progressEvent: false,
|
||||
timeupdateEvent: false,
|
||||
|
||||
// Resizing plugins using request fullscreen reloads the plugin
|
||||
fullscreenResize: false,
|
||||
|
||||
// Resizing plugins in Firefox always reloads the plugin (e.g. full window mode)
|
||||
parentResize: !(_V_.ua.match("Firefox"))
|
||||
};
|
||||
|
||||
_V_.flash.onReady = function(currSwf){
|
||||
|
||||
var el = _V_.el(currSwf);
|
||||
|
||||
// Get player from box
|
||||
// On firefox reloads, el might already have a player
|
||||
var player = el.player || el.parentNode.player,
|
||||
tech = player.tech;
|
||||
|
||||
// Reference player on tech element
|
||||
el.player = player;
|
||||
|
||||
// Update reference to playback technology element
|
||||
tech.el = el;
|
||||
|
||||
// Now that the element is ready, make a click on the swf play the video
|
||||
tech.addEvent("click", tech.onClick);
|
||||
|
||||
_V_.flash.checkReady(tech);
|
||||
};
|
||||
|
||||
// The SWF isn't alwasy ready when it says it is. Sometimes the API functions still need to be added to the object.
|
||||
// If it's not ready, we set a timeout to check again shortly.
|
||||
_V_.flash.checkReady = function(tech){
|
||||
|
||||
// Check if API property exists
|
||||
if (tech.el.vjs_getProperty) {
|
||||
|
||||
// If so, tell tech it's ready
|
||||
tech.triggerReady();
|
||||
|
||||
// Otherwise wait longer.
|
||||
} else {
|
||||
|
||||
setTimeout(function(){
|
||||
_V_.flash.checkReady(tech);
|
||||
}, 50);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Trigger events from the swf on the player
|
||||
_V_.flash.onEvent = function(swfID, eventName){
|
||||
var player = _V_.el(swfID).player;
|
||||
player.triggerEvent(eventName);
|
||||
};
|
||||
|
||||
// Log errors from the swf
|
||||
_V_.flash.onError = function(swfID, err){
|
||||
var player = _V_.el(swfID).player;
|
||||
player.triggerEvent("error");
|
||||
_V_.log("Flash Error", err, swfID);
|
||||
};
|
||||
|
||||
// Flash Version Check
|
||||
_V_.flash.version = function(){
|
||||
var version = '0,0,0'
|
||||
|
||||
// IE
|
||||
try {
|
||||
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
|
||||
|
||||
// other browsers
|
||||
} catch(e) {
|
||||
try {
|
||||
if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
|
||||
version = (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
return version.split(",");
|
||||
}
|
||||
|
||||
// Flash embedding method. Only used in non-iframe mode
|
||||
_V_.flash.embed = function(swf, placeHolder, flashVars, params, attributes){
|
||||
var code = _V_.flash.getEmbedCode(swf, flashVars, params, attributes),
|
||||
|
||||
// Get element by embedding code and retrieving created element
|
||||
obj = _V_.createElement("div", { innerHTML: code }).childNodes[0],
|
||||
|
||||
par = placeHolder.parentNode
|
||||
;
|
||||
|
||||
placeHolder.parentNode.replaceChild(obj, placeHolder);
|
||||
|
||||
// IE6 seems to have an issue where it won't initialize the swf object after injecting it.
|
||||
// This is a dumb temporary fix
|
||||
if (_V_.isIE()) {
|
||||
var newObj = par.childNodes[0];
|
||||
setTimeout(function(){
|
||||
newObj.style.display = "block";
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
};
|
||||
|
||||
_V_.flash.getEmbedCode = function(swf, flashVars, params, attributes){
|
||||
|
||||
var objTag = '<object type="application/x-shockwave-flash"',
|
||||
flashVarsString = '',
|
||||
paramsString = ''
|
||||
attrsString = '';
|
||||
|
||||
// Convert flash vars to string
|
||||
if (flashVars) {
|
||||
_V_.eachProp(flashVars, function(key, val){
|
||||
flashVarsString += (key + "=" + val + "&");
|
||||
});
|
||||
}
|
||||
|
||||
// Add swf, flashVars, and other default params
|
||||
params = _V_.merge({
|
||||
movie: swf,
|
||||
flashvars: flashVarsString,
|
||||
allowScriptAccess: "always", // Required to talk to swf
|
||||
allowNetworking: "all" // All should be default, but having security issues.
|
||||
}, params);
|
||||
|
||||
// Create param tags string
|
||||
_V_.eachProp(params, function(key, val){
|
||||
paramsString += '<param name="'+key+'" value="'+val+'" />';
|
||||
});
|
||||
|
||||
attributes = _V_.merge({
|
||||
// Add swf to attributes (need both for IE and Others to work)
|
||||
data: swf,
|
||||
|
||||
// Default to 100% width/height
|
||||
width: "100%",
|
||||
height: "100%"
|
||||
|
||||
}, attributes);
|
||||
|
||||
// Create Attributes string
|
||||
_V_.eachProp(attributes, function(key, val){
|
||||
attrsString += (key + '="' + val + '" ');
|
||||
});
|
||||
|
||||
return objTag + attrsString + '>' + paramsString + '</object>';
|
||||
}
|
||||
};
|
||||
});
|
444
src/tech/flash.js
Normal file
444
src/tech/flash.js
Normal file
@ -0,0 +1,444 @@
|
||||
/* VideoJS-SWF - Custom Flash Player with HTML5-ish API - https://github.com/zencoder/video-js-swf
|
||||
================================================================================ */
|
||||
_V_.flash = _V_.PlaybackTech.extend({
|
||||
|
||||
init: function(player, options){
|
||||
this.player = player;
|
||||
|
||||
var source = options.source,
|
||||
|
||||
// Which element to embed in
|
||||
parentEl = options.parentEl,
|
||||
|
||||
// Create a temporary element to be replaced by swf object
|
||||
placeHolder = this.el = _V_.createElement("div", { id: parentEl.id + "_temp_flash" }),
|
||||
|
||||
// Generate ID for swf object
|
||||
objId = player.el.id+"_flash_api",
|
||||
|
||||
// Store player options in local var for optimization
|
||||
playerOptions = player.options,
|
||||
|
||||
// Merge default flashvars with ones passed in to init
|
||||
flashVars = _V_.merge({
|
||||
|
||||
// SWF Callback Functions
|
||||
readyFunction: "_V_.flash.onReady",
|
||||
eventProxyFunction: "_V_.flash.onEvent",
|
||||
errorEventProxyFunction: "_V_.flash.onError",
|
||||
|
||||
// Player Settings
|
||||
autoplay: playerOptions.autoplay,
|
||||
preload: playerOptions.preload,
|
||||
loop: playerOptions.loop,
|
||||
muted: playerOptions.muted
|
||||
|
||||
}, options.flashVars),
|
||||
|
||||
// Merge default parames with ones passed in
|
||||
params = _V_.merge({
|
||||
wmode: "opaque", // Opaque is needed to overlay controls, but can affect playback performance
|
||||
bgcolor: "#000000" // Using bgcolor prevents a white flash when the object is loading
|
||||
}, options.params),
|
||||
|
||||
// Merge default attributes with ones passed in
|
||||
attributes = _V_.merge({
|
||||
id: objId,
|
||||
name: objId, // Both ID and Name needed or swf to identifty itself
|
||||
'class': 'vjs-tech'
|
||||
}, options.attributes)
|
||||
;
|
||||
|
||||
// If source was supplied pass as a flash var.
|
||||
if (source) {
|
||||
flashVars.src = encodeURIComponent(_V_.getAbsoluteURL(source.src));
|
||||
}
|
||||
|
||||
// Add placeholder to player div
|
||||
_V_.insertFirst(placeHolder, parentEl);
|
||||
|
||||
// Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers
|
||||
// This allows resetting the playhead when we catch the reload
|
||||
if (options.startTime) {
|
||||
this.ready(function(){
|
||||
this.load();
|
||||
this.play();
|
||||
this.currentTime(options.startTime);
|
||||
});
|
||||
}
|
||||
|
||||
// Flash iFrame Mode
|
||||
// In web browsers there are multiple instances where changing the parent element or visibility of a plugin causes the plugin to reload.
|
||||
// - Firefox just about always. https://bugzilla.mozilla.org/show_bug.cgi?id=90268 (might be fixed by version 13)
|
||||
// - Webkit when hiding the plugin
|
||||
// - Webkit and Firefox when using requestFullScreen on a parent element
|
||||
// Loading the flash plugin into a dynamically generated iFrame gets around most of these issues.
|
||||
// Issues that remain include hiding the element and requestFullScreen in Firefox specifically
|
||||
|
||||
// There's on particularly annoying issue with this method which is that Firefox throws a security error on an offsite Flash object loaded into a dynamically created iFrame.
|
||||
// Even though the iframe was inserted into a page on the web, Firefox + Flash considers it a local app trying to access an internet file.
|
||||
// I tried mulitple ways of setting the iframe src attribute but couldn't find a src that worked well. Tried a real/fake source, in/out of domain.
|
||||
// Also tried a method from stackoverflow that caused a security error in all browsers. http://stackoverflow.com/questions/2486901/how-to-set-document-domain-for-a-dynamically-generated-iframe
|
||||
// In the end the solution I found to work was setting the iframe window.location.href right before doing a document.write of the Flash object.
|
||||
// The only downside of this it seems to trigger another http request to the original page (no matter what's put in the href). Not sure why that is.
|
||||
|
||||
// NOTE (2012-01-29): Cannot get Firefox to load the remote hosted SWF into a dynamically created iFrame
|
||||
// Firefox 9 throws a security error, unleess you call location.href right before doc.write.
|
||||
// Not sure why that even works, but it causes the browser to look like it's continuously trying to load the page.
|
||||
// Firefox 3.6 keeps calling the iframe onload function anytime I write to it, causing an endless loop.
|
||||
|
||||
if (options.iFrameMode === true && !_V_.isFF) {
|
||||
|
||||
// Create iFrame with vjs-tech class so it's 100% width/height
|
||||
var iFrm = _V_.createElement("iframe", {
|
||||
id: objId + "_iframe",
|
||||
name: objId + "_iframe",
|
||||
className: "vjs-tech",
|
||||
scrolling: "no",
|
||||
marginWidth: 0,
|
||||
marginHeight: 0,
|
||||
frameBorder: 0
|
||||
});
|
||||
|
||||
// Update ready function names in flash vars for iframe window
|
||||
flashVars.readyFunction = "ready";
|
||||
flashVars.eventProxyFunction = "events";
|
||||
flashVars.errorEventProxyFunction = "errors";
|
||||
|
||||
// Tried multiple methods to get this to work in all browsers
|
||||
|
||||
// Tried embedding the flash object in the page first, and then adding a place holder to the iframe, then replacing the placeholder with the page object.
|
||||
// The goal here was to try to load the swf URL in the parent page first and hope that got around the firefox security error
|
||||
// var newObj = _V_.flash.embed(options.swf, placeHolder, flashVars, params, attributes);
|
||||
// (in onload)
|
||||
// var temp = _V_.createElement("a", { id:"asdf", innerHTML: "asdf" } );
|
||||
// iDoc.body.appendChild(temp);
|
||||
|
||||
// Tried embedding the flash object through javascript in the iframe source.
|
||||
// This works in webkit but still triggers the firefox security error
|
||||
// iFrm.src = "javascript: document.write('"+_V_.flash.getEmbedCode(options.swf, flashVars, params, attributes)+"');";
|
||||
|
||||
// Tried an actual local iframe just to make sure that works, but it kills the easiness of the CDN version if you require the user to host an iframe
|
||||
// We should add an option to host the iframe locally though, because it could help a lot of issues.
|
||||
// iFrm.src = "iframe.html";
|
||||
|
||||
// Wait until iFrame has loaded to write into it.
|
||||
_V_.addEvent(iFrm, "load", _V_.proxy(this, function(){
|
||||
|
||||
var iDoc, objTag, swfLoc,
|
||||
iWin = iFrm.contentWindow,
|
||||
varString = "";
|
||||
|
||||
|
||||
// The one working method I found was to use the iframe's document.write() to create the swf object
|
||||
// This got around the security issue in all browsers except firefox.
|
||||
// I did find a hack where if I call the iframe's window.location.href="", it would get around the security error
|
||||
// However, the main page would look like it was loading indefinitely (URL bar loading spinner would never stop)
|
||||
// Plus Firefox 3.6 didn't work no matter what I tried.
|
||||
// if (_V_.ua.match("Firefox")) {
|
||||
// iWin.location.href = "";
|
||||
// }
|
||||
|
||||
// Get the iFrame's document depending on what the browser supports
|
||||
iDoc = iFrm.contentDocument ? iFrm.contentDocument : iFrm.contentWindow.document;
|
||||
|
||||
// Tried ensuring both document domains were the same, but they already were, so that wasn't the issue.
|
||||
// Even tried adding /. that was mentioned in a browser security writeup
|
||||
// document.domain = document.domain+"/.";
|
||||
// iDoc.domain = document.domain+"/.";
|
||||
|
||||
// Tried adding the object to the iframe doc's innerHTML. Security error in all browsers.
|
||||
// iDoc.body.innerHTML = swfObjectHTML;
|
||||
|
||||
// Tried appending the object to the iframe doc's body. Security error in all browsers.
|
||||
// iDoc.body.appendChild(swfObject);
|
||||
|
||||
// Using document.write actually got around the security error that browsers were throwing.
|
||||
// Again, it's a dynamically generated (same domain) iframe, loading an external Flash swf.
|
||||
// Not sure why that's a security issue, but apparently it is.
|
||||
iDoc.write(_V_.flash.getEmbedCode(options.swf, flashVars, params, attributes));
|
||||
|
||||
// Setting variables on the window needs to come after the doc write because otherwise they can get reset in some browsers
|
||||
// So far no issues with swf ready event being called before it's set on the window.
|
||||
iWin.player = this.player;
|
||||
|
||||
// Create swf ready function for iFrame window
|
||||
iWin.ready = _V_.proxy(this.player, function(currSwf){
|
||||
var el = iDoc.getElementById(currSwf),
|
||||
player = this,
|
||||
tech = player.tech;
|
||||
|
||||
// Update reference to playback technology element
|
||||
tech.el = el;
|
||||
|
||||
// Now that the element is ready, make a click on the swf play the video
|
||||
_V_.addEvent(el, "click", tech.proxy(tech.onClick));
|
||||
|
||||
// Make sure swf is actually ready. Sometimes the API isn't actually yet.
|
||||
_V_.flash.checkReady(tech);
|
||||
});
|
||||
|
||||
// Create event listener for all swf events
|
||||
iWin.events = _V_.proxy(this.player, function(swfID, eventName, other){
|
||||
var player = this;
|
||||
if (player && player.techName == "flash") {
|
||||
player.triggerEvent(eventName);
|
||||
}
|
||||
});
|
||||
|
||||
// Create error listener for all swf errors
|
||||
iWin.errors = _V_.proxy(this.player, function(swfID, eventName){
|
||||
_V_.log("Flash Error", eventName);
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
// Replace placeholder with iFrame (it will load now)
|
||||
placeHolder.parentNode.replaceChild(iFrm, placeHolder);
|
||||
|
||||
// If not using iFrame mode, embed as normal object
|
||||
} else {
|
||||
_V_.flash.embed(options.swf, placeHolder, flashVars, params, attributes);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
},
|
||||
|
||||
// setupTriggers: function(){}, // Using global onEvent func to distribute events
|
||||
|
||||
play: function(){ this.el.vjs_play(); },
|
||||
pause: function(){ this.el.vjs_pause(); },
|
||||
src: function(src){
|
||||
// Make sure source URL is abosolute.
|
||||
src = _V_.getAbsoluteURL(src);
|
||||
|
||||
this.el.vjs_src(src);
|
||||
|
||||
// Currently the SWF doesn't autoplay if you load a source later.
|
||||
// e.g. Load player w/ no source, wait 2s, set src.
|
||||
if (this.player.autoplay()) {
|
||||
var tech = this;
|
||||
setTimeout(function(){ tech.play(); }, 0);
|
||||
}
|
||||
},
|
||||
load: function(){ this.el.vjs_load(); },
|
||||
poster: function(){ this.el.vjs_getProperty("poster"); },
|
||||
|
||||
buffered: function(){
|
||||
return _V_.createTimeRange(0, this.el.vjs_getProperty("buffered"));
|
||||
},
|
||||
|
||||
supportsFullScreen: function(){
|
||||
return false; // Flash does not allow fullscreen through javascript
|
||||
},
|
||||
enterFullScreen: function(){
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Create setters and getters for attributes
|
||||
(function(){
|
||||
|
||||
var api = _V_.flash.prototype,
|
||||
readWrite = "preload,currentTime,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted".split(","),
|
||||
readOnly = "error,currentSrc,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks".split(","),
|
||||
callOnly = "load,play,pause".split(",");
|
||||
// Overridden: buffered
|
||||
|
||||
createSetter = function(attr){
|
||||
var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
|
||||
api["set"+attrUpper] = function(val){ return this.el.vjs_setProperty(attr, val); };
|
||||
},
|
||||
|
||||
createGetter = function(attr){
|
||||
api[attr] = function(){ return this.el.vjs_getProperty(attr); };
|
||||
}
|
||||
;
|
||||
|
||||
// Create getter and setters for all read/write attributes
|
||||
_V_.each(readWrite, function(attr){
|
||||
createGetter(attr);
|
||||
createSetter(attr);
|
||||
});
|
||||
|
||||
// Create getters for read-only attributes
|
||||
_V_.each(readOnly, function(attr){
|
||||
createGetter(attr);
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
/* Flash Support Testing -------------------------------------------------------- */
|
||||
|
||||
_V_.flash.isSupported = function(){
|
||||
return _V_.flash.version()[0] >= 10;
|
||||
// return swfobject.hasFlashPlayerVersion("10");
|
||||
};
|
||||
|
||||
_V_.flash.canPlaySource = function(srcObj){
|
||||
if (srcObj.type in _V_.flash.prototype.support.formats) { return "maybe"; }
|
||||
};
|
||||
|
||||
_V_.flash.prototype.support = {
|
||||
formats: {
|
||||
"video/flv": "FLV",
|
||||
"video/x-flv": "FLV",
|
||||
"video/mp4": "MP4",
|
||||
"video/m4v": "MP4"
|
||||
},
|
||||
|
||||
// Optional events that we can manually mimic with timers
|
||||
progressEvent: false,
|
||||
timeupdateEvent: false,
|
||||
|
||||
// Resizing plugins using request fullscreen reloads the plugin
|
||||
fullscreenResize: false,
|
||||
|
||||
// Resizing plugins in Firefox always reloads the plugin (e.g. full window mode)
|
||||
parentResize: !(_V_.ua.match("Firefox"))
|
||||
};
|
||||
|
||||
_V_.flash.onReady = function(currSwf){
|
||||
|
||||
var el = _V_.el(currSwf);
|
||||
|
||||
// Get player from box
|
||||
// On firefox reloads, el might already have a player
|
||||
var player = el.player || el.parentNode.player,
|
||||
tech = player.tech;
|
||||
|
||||
// Reference player on tech element
|
||||
el.player = player;
|
||||
|
||||
// Update reference to playback technology element
|
||||
tech.el = el;
|
||||
|
||||
// Now that the element is ready, make a click on the swf play the video
|
||||
tech.addEvent("click", tech.onClick);
|
||||
|
||||
_V_.flash.checkReady(tech);
|
||||
};
|
||||
|
||||
// The SWF isn't alwasy ready when it says it is. Sometimes the API functions still need to be added to the object.
|
||||
// If it's not ready, we set a timeout to check again shortly.
|
||||
_V_.flash.checkReady = function(tech){
|
||||
|
||||
// Check if API property exists
|
||||
if (tech.el.vjs_getProperty) {
|
||||
|
||||
// If so, tell tech it's ready
|
||||
tech.triggerReady();
|
||||
|
||||
// Otherwise wait longer.
|
||||
} else {
|
||||
|
||||
setTimeout(function(){
|
||||
_V_.flash.checkReady(tech);
|
||||
}, 50);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Trigger events from the swf on the player
|
||||
_V_.flash.onEvent = function(swfID, eventName){
|
||||
var player = _V_.el(swfID).player;
|
||||
player.triggerEvent(eventName);
|
||||
};
|
||||
|
||||
// Log errors from the swf
|
||||
_V_.flash.onError = function(swfID, err){
|
||||
var player = _V_.el(swfID).player;
|
||||
player.triggerEvent("error");
|
||||
_V_.log("Flash Error", err, swfID);
|
||||
};
|
||||
|
||||
// Flash Version Check
|
||||
_V_.flash.version = function(){
|
||||
var version = '0,0,0';
|
||||
|
||||
// IE
|
||||
try {
|
||||
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
|
||||
|
||||
// other browsers
|
||||
} catch(e) {
|
||||
try {
|
||||
if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
|
||||
version = (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
return version.split(",");
|
||||
};
|
||||
|
||||
// Flash embedding method. Only used in non-iframe mode
|
||||
_V_.flash.embed = function(swf, placeHolder, flashVars, params, attributes){
|
||||
var code = _V_.flash.getEmbedCode(swf, flashVars, params, attributes),
|
||||
|
||||
// Get element by embedding code and retrieving created element
|
||||
obj = _V_.createElement("div", { innerHTML: code }).childNodes[0],
|
||||
|
||||
par = placeHolder.parentNode
|
||||
;
|
||||
|
||||
placeHolder.parentNode.replaceChild(obj, placeHolder);
|
||||
|
||||
// IE6 seems to have an issue where it won't initialize the swf object after injecting it.
|
||||
// This is a dumb temporary fix
|
||||
if (_V_.isIE()) {
|
||||
var newObj = par.childNodes[0];
|
||||
setTimeout(function(){
|
||||
newObj.style.display = "block";
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
};
|
||||
|
||||
_V_.flash.getEmbedCode = function(swf, flashVars, params, attributes){
|
||||
|
||||
var objTag = '<object type="application/x-shockwave-flash"',
|
||||
flashVarsString = '',
|
||||
paramsString = '',
|
||||
attrsString = '';
|
||||
|
||||
// Convert flash vars to string
|
||||
if (flashVars) {
|
||||
_V_.eachProp(flashVars, function(key, val){
|
||||
flashVarsString += (key + "=" + val + "&");
|
||||
});
|
||||
}
|
||||
|
||||
// Add swf, flashVars, and other default params
|
||||
params = _V_.merge({
|
||||
movie: swf,
|
||||
flashvars: flashVarsString,
|
||||
allowScriptAccess: "always", // Required to talk to swf
|
||||
allowNetworking: "all" // All should be default, but having security issues.
|
||||
}, params);
|
||||
|
||||
// Create param tags string
|
||||
_V_.eachProp(params, function(key, val){
|
||||
paramsString += '<param name="'+key+'" value="'+val+'" />';
|
||||
});
|
||||
|
||||
attributes = _V_.merge({
|
||||
// Add swf to attributes (need both for IE and Others to work)
|
||||
data: swf,
|
||||
|
||||
// Default to 100% width/height
|
||||
width: "100%",
|
||||
height: "100%"
|
||||
|
||||
}, attributes);
|
||||
|
||||
// Create Attributes string
|
||||
_V_.eachProp(attributes, function(key, val){
|
||||
attrsString += (key + '="' + val + '" ');
|
||||
});
|
||||
|
||||
return objTag + attrsString + '>' + paramsString + '</object>';
|
||||
};
|
228
src/tech/html5.js
Normal file
228
src/tech/html5.js
Normal file
@ -0,0 +1,228 @@
|
||||
/* HTML5 Playback Technology - Wrapper for HTML5 Media API
|
||||
================================================================================ */
|
||||
_V_.html5 = _V_.PlaybackTech.extend({
|
||||
|
||||
init: function(player, options, ready){
|
||||
this.player = player;
|
||||
this.el = this.createElement();
|
||||
this.ready(ready);
|
||||
|
||||
this.addEvent("click", this.proxy(this.onClick));
|
||||
|
||||
var source = options.source;
|
||||
|
||||
// If the element source is already set, we may have missed the loadstart event, and want to trigger it.
|
||||
// We don't want to set the source again and interrupt playback.
|
||||
if (source && this.el.currentSrc == source.src) {
|
||||
player.triggerEvent("loadstart");
|
||||
|
||||
// Otherwise set the source if one was provided.
|
||||
} else if (source) {
|
||||
this.el.src = source.src;
|
||||
}
|
||||
|
||||
// Chrome and Safari both have issues with autoplay.
|
||||
// In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
|
||||
// In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
|
||||
// This fixes both issues. Need to wait for API, so it updates displays correctly
|
||||
player.ready(function(){
|
||||
if (this.options.autoplay && this.paused()) {
|
||||
this.tag.poster = null; // Chrome Fix. Fixed in Chrome v16.
|
||||
this.play();
|
||||
}
|
||||
});
|
||||
|
||||
this.setupTriggers();
|
||||
|
||||
this.triggerReady();
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.player.tag = false;
|
||||
this.removeTriggers();
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
},
|
||||
|
||||
createElement: function(){
|
||||
var html5 = _V_.html5,
|
||||
player = this.player,
|
||||
|
||||
// If possible, reuse original tag for HTML5 playback technology element
|
||||
el = player.tag,
|
||||
newEl;
|
||||
|
||||
// Check if this browser supports moving the element into the box.
|
||||
// On the iPhone video will break if you move the element,
|
||||
// So we have to create a brand new element.
|
||||
if (!el || this.support.movingElementInDOM === false) {
|
||||
|
||||
// If the original tag is still there, remove it.
|
||||
if (el) {
|
||||
player.el.removeChild(el);
|
||||
}
|
||||
|
||||
newEl = _V_.createElement("video", {
|
||||
id: el.id || player.el.id + "_html5_api",
|
||||
className: el.className || "vjs-tech"
|
||||
});
|
||||
|
||||
el = newEl;
|
||||
_V_.insertFirst(el, player.el);
|
||||
}
|
||||
|
||||
// Update tag settings, in case they were overridden
|
||||
_V_.each(["autoplay","preload","loop","muted"], function(attr){ // ,"poster"
|
||||
if (player.options[attr] !== null) {
|
||||
el[attr] = player.options[attr];
|
||||
}
|
||||
}, this);
|
||||
|
||||
return el;
|
||||
},
|
||||
|
||||
// Make video events trigger player events
|
||||
// May seem verbose here, but makes other APIs possible.
|
||||
setupTriggers: function(){
|
||||
_V_.each.call(this, _V_.html5.events, function(type){
|
||||
_V_.addEvent(this.el, type, _V_.proxy(this.player, this.eventHandler));
|
||||
});
|
||||
},
|
||||
removeTriggers: function(){
|
||||
_V_.each.call(this, _V_.html5.events, function(type){
|
||||
_V_.removeEvent(this.el, type, _V_.proxy(this.player, this.eventHandler));
|
||||
});
|
||||
},
|
||||
eventHandler: function(e){
|
||||
e.stopPropagation();
|
||||
this.triggerEvent(e);
|
||||
},
|
||||
|
||||
play: function(){ this.el.play(); },
|
||||
pause: function(){ this.el.pause(); },
|
||||
paused: function(){ return this.el.paused; },
|
||||
|
||||
currentTime: function(){ return this.el.currentTime; },
|
||||
setCurrentTime: function(seconds){
|
||||
try {
|
||||
this.el.currentTime = seconds;
|
||||
} catch(e) {
|
||||
_V_.log(e, "Video isn't ready. (VideoJS)");
|
||||
// this.warning(VideoJS.warnings.videoNotReady);
|
||||
}
|
||||
},
|
||||
|
||||
duration: function(){ return this.el.duration || 0; },
|
||||
buffered: function(){ return this.el.buffered; },
|
||||
|
||||
volume: function(){ return this.el.volume; },
|
||||
setVolume: function(percentAsDecimal){ this.el.volume = percentAsDecimal; },
|
||||
muted: function(){ return this.el.muted; },
|
||||
setMuted: function(muted){ this.el.muted = muted; },
|
||||
|
||||
width: function(){ return this.el.offsetWidth; },
|
||||
height: function(){ return this.el.offsetHeight; },
|
||||
|
||||
supportsFullScreen: function(){
|
||||
if (typeof this.el.webkitEnterFullScreen == 'function') {
|
||||
|
||||
// Seems to be broken in Chromium/Chrome && Safari in Leopard
|
||||
if (!navigator.userAgent.match("Chrome") && !navigator.userAgent.match("Mac OS X 10.5")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
enterFullScreen: function(){
|
||||
try {
|
||||
this.el.webkitEnterFullScreen();
|
||||
} catch (e) {
|
||||
if (e.code == 11) {
|
||||
// this.warning(VideoJS.warnings.videoNotReady);
|
||||
_V_.log("VideoJS: Video not ready.");
|
||||
}
|
||||
}
|
||||
},
|
||||
exitFullScreen: function(){
|
||||
try {
|
||||
this.el.webkitExitFullScreen();
|
||||
} catch (e) {
|
||||
if (e.code == 11) {
|
||||
// this.warning(VideoJS.warnings.videoNotReady);
|
||||
_V_.log("VideoJS: Video not ready.");
|
||||
}
|
||||
}
|
||||
},
|
||||
src: function(src){ this.el.src = src; },
|
||||
load: function(){ this.el.load(); },
|
||||
currentSrc: function(){ return this.el.currentSrc; },
|
||||
|
||||
preload: function(){ return this.el.preload; },
|
||||
setPreload: function(val){ this.el.preload = val; },
|
||||
autoplay: function(){ return this.el.autoplay; },
|
||||
setAutoplay: function(val){ this.el.autoplay = val; },
|
||||
loop: function(){ return this.el.loop; },
|
||||
setLoop: function(val){ this.el.loop = val; },
|
||||
|
||||
error: function(){ return this.el.error; },
|
||||
// networkState: function(){ return this.el.networkState; },
|
||||
// readyState: function(){ return this.el.readyState; },
|
||||
seeking: function(){ return this.el.seeking; },
|
||||
// initialTime: function(){ return this.el.initialTime; },
|
||||
// startOffsetTime: function(){ return this.el.startOffsetTime; },
|
||||
// played: function(){ return this.el.played; },
|
||||
// seekable: function(){ return this.el.seekable; },
|
||||
ended: function(){ return this.el.ended; },
|
||||
// videoTracks: function(){ return this.el.videoTracks; },
|
||||
// audioTracks: function(){ return this.el.audioTracks; },
|
||||
// videoWidth: function(){ return this.el.videoWidth; },
|
||||
// videoHeight: function(){ return this.el.videoHeight; },
|
||||
// textTracks: function(){ return this.el.textTracks; },
|
||||
// defaultPlaybackRate: function(){ return this.el.defaultPlaybackRate; },
|
||||
// playbackRate: function(){ return this.el.playbackRate; },
|
||||
// mediaGroup: function(){ return this.el.mediaGroup; },
|
||||
// controller: function(){ return this.el.controller; },
|
||||
controls: function(){ return this.player.options.controls; },
|
||||
defaultMuted: function(){ return this.el.defaultMuted; }
|
||||
});
|
||||
|
||||
/* HTML5 Support Testing -------------------------------------------------------- */
|
||||
|
||||
_V_.html5.isSupported = function(){
|
||||
return !!document.createElement("video").canPlayType;
|
||||
};
|
||||
|
||||
_V_.html5.canPlaySource = function(srcObj){
|
||||
return !!document.createElement("video").canPlayType(srcObj.type);
|
||||
// TODO: Check Type
|
||||
// If no Type, check ext
|
||||
// Check Media Type
|
||||
};
|
||||
|
||||
// List of all HTML5 events (various uses).
|
||||
_V_.html5.events = "loadstart,suspend,abort,error,emptied,stalled,loadedmetadata,loadeddata,canplay,canplaythrough,playing,waiting,seeking,seeked,ended,durationchange,timeupdate,progress,play,pause,ratechange,volumechange".split(",");
|
||||
|
||||
/* HTML5 Device Fixes ---------------------------------------------------------- */
|
||||
|
||||
_V_.html5.prototype.support = {
|
||||
|
||||
// Support for tech specific full screen. (webkitEnterFullScreen, not requestFullscreen)
|
||||
// http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html
|
||||
// Seems to be broken in Chromium/Chrome && Safari in Leopard
|
||||
fullscreen: (typeof _V_.testVid.webkitEnterFullScreen !== undefined) ? (!_V_.ua.match("Chrome") && !_V_.ua.match("Mac OS X 10.5") ? true : false) : false,
|
||||
|
||||
// In iOS, if you move a video element in the DOM, it breaks video playback.
|
||||
movingElementInDOM: !_V_.isIOS()
|
||||
|
||||
};
|
||||
|
||||
// Android
|
||||
if (_V_.isAndroid()) {
|
||||
|
||||
// Override Android 2.2 and less canPlayType method which is broken
|
||||
if (_V_.androidVersion() < 3) {
|
||||
document.createElement("video").constructor.prototype.canPlayType = function(type){
|
||||
return (type && type.toLowerCase().indexOf("video/mp4") != -1) ? "maybe" : "";
|
||||
};
|
||||
}
|
||||
}
|
603
src/tech/vimeo.js
Normal file
603
src/tech/vimeo.js
Normal file
@ -0,0 +1,603 @@
|
||||
|
||||
var VimeoState = {
|
||||
UNSTARTED: -1,
|
||||
ENDED: 0,
|
||||
PLAYING: 1,
|
||||
PAUSED: 2,
|
||||
BUFFERING: 3
|
||||
};
|
||||
|
||||
/* VideoJS-Vimeo - Vimeo iFrame Wrapper
|
||||
================================================================================ */
|
||||
_V_.vimeo = _V_.PlaybackTech.extend({
|
||||
|
||||
init: function(player, options){
|
||||
this.player = player;
|
||||
|
||||
var source = options.source;
|
||||
|
||||
// Extract the Vimeo videoID from the source
|
||||
var videoId = this.getVideoId(source.src);
|
||||
|
||||
// Which element to embed in
|
||||
var parentEl = options.parentEl;
|
||||
|
||||
// Generate ID for iFrame
|
||||
var objId = player.el.id+"_vimeo_api";
|
||||
|
||||
// Create an iFrame for the Vimeo player
|
||||
var iFrm = this.el = _V_.createElement("iframe", {
|
||||
id: objId + "_iframe",
|
||||
name: objId + "_iframe",
|
||||
className: "vjs-tech",
|
||||
scrolling: "no",
|
||||
marginWidth: 0,
|
||||
marginHeight: 0,
|
||||
frameBorder: 0,
|
||||
webkitAllowFullScreen: "",
|
||||
mozallowfullscreen: "",
|
||||
allowFullScreen: ""
|
||||
});
|
||||
|
||||
var playerOptions = player.options;
|
||||
var optionsParams = options.params || {};
|
||||
|
||||
// Setup player parameters
|
||||
this.player.apiArgs = {
|
||||
api: 1,
|
||||
byline: 0,
|
||||
portrait: 0,
|
||||
show_title: 0,
|
||||
show_byline: 0,
|
||||
show_portrait: 0,
|
||||
fullscreen: 1,
|
||||
player_id: objId + "_iframe",
|
||||
color: optionsParams.color || playerOptions.color || 'ffffff',
|
||||
autoplay: this.toBoolInt(optionsParams.autoplay || playerOptions.autoplay),
|
||||
loop: this.toBoolInt(optionsParams.loop || playerOptions.loop)
|
||||
};
|
||||
|
||||
// Create a dummy object to hold Vimeo state in case any info is requested
|
||||
// before the iFrame loads
|
||||
this.player.vimeoInfo = {};
|
||||
|
||||
// Add iFrame to player div
|
||||
_V_.insertFirst(iFrm, parentEl);
|
||||
|
||||
this.loadPlayer(videoId);
|
||||
},
|
||||
|
||||
loadPlayer: function(videoId){
|
||||
var baseUrl = (document.location.protocol == 'https:') ?
|
||||
'https://secure.vimeo.com/video/' :
|
||||
'http://player.vimeo.com/video/';
|
||||
|
||||
// Wait until iFrame has loaded to initialize the API
|
||||
_V_.addEvent(this.el, "load", _V_.proxy(this, function(){
|
||||
// Initialize the Vimeo Player object
|
||||
this.vimeo = $f(this.el);
|
||||
|
||||
// Create an object to track the state of the Vimeo player, since we can
|
||||
// only fetch information from the iFrame asynchronously
|
||||
this.player.vimeoInfo = {
|
||||
state: VimeoState.UNSTARTED,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
muteVolume: 1,
|
||||
time: 0,
|
||||
duration: 0,
|
||||
buffered: 0,
|
||||
url: baseUrl + videoId,
|
||||
error: null
|
||||
};
|
||||
|
||||
// Register Vimeo event handlers
|
||||
this.vimeo.addEvent('ready', _V_.vimeo.onReady);
|
||||
this.vimeo.addEvent('loadProgress', _V_.vimeo.onLoadProgress);
|
||||
this.vimeo.addEvent('playProgress', _V_.vimeo.onPlayProgress);
|
||||
this.vimeo.addEvent('play', _V_.vimeo.onPlay);
|
||||
this.vimeo.addEvent('pause', _V_.vimeo.onPause);
|
||||
this.vimeo.addEvent('finish', _V_.vimeo.onFinish);
|
||||
this.vimeo.addEvent('seek', _V_.vimeo.onSeek);
|
||||
}));
|
||||
|
||||
// Set the iFrame URL to start loading the video
|
||||
this.el.src = baseUrl + videoId + "?" + this.makeQueryString(this.player.apiArgs);
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.vimeo.api("unload");
|
||||
delete this.vimeo;
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
},
|
||||
|
||||
play: function(){ this.vimeo.api("play"); },
|
||||
pause: function(){ this.vimeo.api("pause"); },
|
||||
paused: function(){
|
||||
var state = this.player.vimeoInfo.state;
|
||||
return state !== VimeoState.PLAYING &&
|
||||
state !== VimeoState.BUFFERING;
|
||||
},
|
||||
|
||||
src: function(src){
|
||||
var videoId = this.getVideoId(src);
|
||||
this.loadPlayer(videoId);
|
||||
},
|
||||
load: function(){ },
|
||||
poster: function(){
|
||||
// We could fetch the poster image using JSONP, but it would need to be
|
||||
// done ahead of time (and probably enabled only through a player param)
|
||||
return null;
|
||||
},
|
||||
|
||||
currentTime: function(){ return this.player.vimeoInfo.time || 0; },
|
||||
setCurrentTime: function(seconds){ this.vimeo.api("seekTo", seconds); },
|
||||
|
||||
duration: function(){ return this.player.vimeoInfo.duration || 0; },
|
||||
buffered: function(){
|
||||
return _V_.createTimeRange(0, this.player.vimeoInfo.buffered || 0);
|
||||
},
|
||||
|
||||
volume: function(){
|
||||
return (this.player.vimeoInfo.muted) ?
|
||||
this.player.vimeoInfo.muteVolume :
|
||||
this.player.vimeoInfo.volume;
|
||||
},
|
||||
setVolume: function(percentAsDecimal){
|
||||
this.vimeo.api("setVolume", percentAsDecimal);
|
||||
this.player.vimeoInfo.volume = percentAsDecimal;
|
||||
this.player.triggerEvent("volumechange");
|
||||
},
|
||||
muted: function(){ return this.player.vimeoInfo.muted || false; },
|
||||
setMuted: function(muted){
|
||||
if (muted) {
|
||||
this.player.vimeoInfo.muteVolume = this.player.vimeoInfo.volume;
|
||||
this.setVolume(0);
|
||||
} else {
|
||||
this.setVolume(this.player.vimeoInfo.muteVolume);
|
||||
}
|
||||
this.player.vimeoInfo.muted = muted;
|
||||
this.player.triggerEvent("volumechange");
|
||||
},
|
||||
|
||||
width: function(){ return this.el.offsetWidth; },
|
||||
height: function(){ return this.el.offsetHeight; },
|
||||
|
||||
currentSrc: function(){ return this.player.vimeoInfo.url; },
|
||||
|
||||
preload: function(){ return false; },
|
||||
setPreload: function(val){ },
|
||||
autoplay: function(){ return !!this.player.apiArgs.autoplay; },
|
||||
setAutoplay: function(val){ },
|
||||
loop: function(){ return !!this.player.apiArgs.loop; },
|
||||
setLoop: function(val){
|
||||
this.player.apiArgs.loop = (val ? 1 : 0);
|
||||
// We handle looping manually
|
||||
//this.vimeo.api("setLoop", val);
|
||||
},
|
||||
|
||||
supportsFullScreen: function(){ return false; },
|
||||
enterFullScreen: function(){ return false; },
|
||||
|
||||
error: function(){ return this.player.vimeoInfo.error; },
|
||||
seeking: function(){ return false; },
|
||||
ended: function(){ return this.player.vimeoInfo.state === VimeoState.ENDED; },
|
||||
videoWidth: function(){ return this.width(); },
|
||||
videoHeight: function(){ return this.height(); },
|
||||
controls: function(){ return this.player.options.controls; },
|
||||
defaultMuted: function(){ return false; },
|
||||
|
||||
// Helpers ------------------------------------------------------------------
|
||||
|
||||
makeQueryString: function(args) {
|
||||
var array = [];
|
||||
for (var key in args) {
|
||||
if (args.hasOwnProperty(key))
|
||||
array.push(encodeURIComponent(key) + "=" + encodeURIComponent(args[key]));
|
||||
}
|
||||
return array.join("&");
|
||||
},
|
||||
|
||||
getVideoId: function(url) {
|
||||
return url.match(/vimeo\.com\/(?:.*#|.*\/videos\/)?([0-9]+)/)[1];
|
||||
},
|
||||
|
||||
toBoolInt: function(val) {
|
||||
return val ? 1 : 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Event callbacks ------------------------------------------------------------
|
||||
|
||||
_V_.vimeo.onReady = function(id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.tech.triggerReady();
|
||||
player.triggerReady();
|
||||
player.triggerEvent("canplay");
|
||||
_V_.vimeo.hideOverlay(player);
|
||||
|
||||
// Hide our playback controls since we have no good way of hiding the Vimeo
|
||||
// controls
|
||||
player.controlBar.hide();
|
||||
};
|
||||
|
||||
_V_.vimeo.onLoadProgress = function(data, id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
var durationUpdate = !player.vimeoInfo.duration;
|
||||
player.vimeoInfo.duration = data.duration;
|
||||
player.vimeoInfo.buffered = data.percent;
|
||||
player.triggerEvent("progress");
|
||||
if (durationUpdate) player.triggerEvent("durationchange");
|
||||
};
|
||||
|
||||
_V_.vimeo.onPlayProgress = function(data, id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.time = data.seconds;
|
||||
player.triggerEvent("timeupdate");
|
||||
};
|
||||
|
||||
_V_.vimeo.onPlay = function(id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.state = VimeoState.PLAYING;
|
||||
player.triggerEvent("play");
|
||||
};
|
||||
|
||||
_V_.vimeo.onPause = function(id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.state = VimeoState.PAUSED;
|
||||
player.triggerEvent("pause");
|
||||
};
|
||||
|
||||
_V_.vimeo.onFinish = function(id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.state = VimeoState.ENDED;
|
||||
player.triggerEvent("ended");
|
||||
_V_.vimeo.hideOverlay(player);
|
||||
|
||||
// Vimeo looping doesn't seem to play well with VideoJS, so we need to
|
||||
// implement it manually here
|
||||
if (player.apiArgs.loop) {
|
||||
//player.tech.vimeo.api("seekTo", 0);
|
||||
player.tech.vimeo.api("play");
|
||||
} else {
|
||||
// Reset the video
|
||||
player.tech.vimeo.api("seekTo", 0);
|
||||
player.tech.vimeo.api("play");
|
||||
player.tech.vimeo.api("pause");
|
||||
}
|
||||
};
|
||||
|
||||
_V_.vimeo.onSeek = function(data, id) {
|
||||
var player = _V_.el(id).parentNode.player;
|
||||
player.vimeoInfo.time = data.seconds;
|
||||
player.triggerEvent("timeupdate");
|
||||
player.triggerEvent("seeked");
|
||||
};
|
||||
|
||||
// Helpers --------------------------------------------------------------------
|
||||
|
||||
_V_.vimeo.hideOverlay = function(player) {
|
||||
// Hide the big play button and poster since Vimeo provides these. Using
|
||||
// our own prevents the video from playing on the first click in mobile
|
||||
// devices
|
||||
player.bigPlayButton.hide();
|
||||
player.posterImage.hide();
|
||||
};
|
||||
|
||||
// Support testing ------------------------------------------------------------
|
||||
|
||||
_V_.vimeo.isSupported = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
_V_.vimeo.canPlaySource = function(srcObj){
|
||||
return srcObj.type == "video/vimeo";
|
||||
};
|
||||
|
||||
_V_.vimeo.prototype.support = {
|
||||
formats: {
|
||||
"video/vimeo": "VIM"
|
||||
},
|
||||
|
||||
// Optional events that we can manually mimic with timers
|
||||
progressEvent: true,
|
||||
timeupdateEvent: true,
|
||||
|
||||
//fullscreen: true,
|
||||
// In iOS, if you move a video element in the DOM, it breaks video playback.
|
||||
movingElementInDOM: !_V_.isIOS(),
|
||||
|
||||
fullscreenResize: true,
|
||||
parentResize: true
|
||||
};
|
||||
|
||||
// Froogaloop API -------------------------------------------------------------
|
||||
|
||||
// From https://github.com/vimeo/player-api/blob/master/javascript/froogaloop.js
|
||||
var Froogaloop = (function(){
|
||||
// Define a local copy of Froogaloop
|
||||
function Froogaloop(iframe) {
|
||||
// The Froogaloop object is actually just the init constructor
|
||||
return new Froogaloop.fn.init(iframe);
|
||||
}
|
||||
|
||||
var eventCallbacks = {},
|
||||
hasWindowEvent = false,
|
||||
isReady = false,
|
||||
slice = Array.prototype.slice,
|
||||
playerDomain = '';
|
||||
|
||||
Froogaloop.fn = Froogaloop.prototype = {
|
||||
element: null,
|
||||
|
||||
init: function(iframe) {
|
||||
if (typeof iframe === "string") {
|
||||
iframe = document.getElementById(iframe);
|
||||
}
|
||||
|
||||
this.element = iframe;
|
||||
|
||||
// Register message event listeners
|
||||
playerDomain = getDomainFromUrl(this.element.getAttribute('src'));
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/*
|
||||
* Calls a function to act upon the player.
|
||||
*
|
||||
* @param {string} method The name of the Javascript API method to call. Eg: 'play'.
|
||||
* @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method
|
||||
* or callback function when the method returns a value.
|
||||
*/
|
||||
api: function(method, valueOrCallback) {
|
||||
if (!this.element || !method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
element = self.element,
|
||||
target_id = element.id !== '' ? element.id : null,
|
||||
params = !isFunction(valueOrCallback) ? valueOrCallback : null,
|
||||
callback = isFunction(valueOrCallback) ? valueOrCallback : null;
|
||||
|
||||
// Store the callback for get functions
|
||||
if (callback) {
|
||||
storeCallback(method, callback, target_id);
|
||||
}
|
||||
|
||||
postMessage(method, params, element);
|
||||
return self;
|
||||
},
|
||||
|
||||
/*
|
||||
* Registers an event listener and a callback function that gets called when the event fires.
|
||||
*
|
||||
* @param eventName (String): Name of the event to listen for.
|
||||
* @param callback (Function): Function that should be called when the event fires.
|
||||
*/
|
||||
addEvent: function(eventName, callback) {
|
||||
if (!this.element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
element = self.element,
|
||||
target_id = element.id !== '' ? element.id : null;
|
||||
|
||||
|
||||
storeCallback(eventName, callback, target_id);
|
||||
|
||||
// The ready event is not registered via postMessage. It fires regardless.
|
||||
if (eventName != 'ready') {
|
||||
postMessage('addEventListener', eventName, element);
|
||||
}
|
||||
else if (eventName == 'ready' && isReady) {
|
||||
callback.call(null, target_id);
|
||||
}
|
||||
|
||||
return self;
|
||||
},
|
||||
|
||||
/*
|
||||
* Unregisters an event listener that gets called when the event fires.
|
||||
*
|
||||
* @param eventName (String): Name of the event to stop listening for.
|
||||
*/
|
||||
removeEvent: function(eventName) {
|
||||
if (!this.element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
element = self.element,
|
||||
target_id = element.id !== '' ? element.id : null,
|
||||
removed = removeCallback(eventName, target_id);
|
||||
|
||||
// The ready event is not registered
|
||||
if (eventName != 'ready' && removed) {
|
||||
postMessage('removeEventListener', eventName, element);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles posting a message to the parent window.
|
||||
*
|
||||
* @param method (String): name of the method to call inside the player. For api calls
|
||||
* this is the name of the api method (api_play or api_pause) while for events this method
|
||||
* is api_addEventListener.
|
||||
* @param params (Object or Array): List of parameters to submit to the method. Can be either
|
||||
* a single param or an array list of parameters.
|
||||
* @param target (HTMLElement): Target iframe to post the message to.
|
||||
*/
|
||||
function postMessage(method, params, target) {
|
||||
if (!target.contentWindow.postMessage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var url = target.getAttribute('src').split('?')[0],
|
||||
data = JSON.stringify({
|
||||
method: method,
|
||||
value: params
|
||||
});
|
||||
|
||||
if (url.substr(0, 2) === '//') {
|
||||
url = window.location.protocol + url;
|
||||
}
|
||||
|
||||
target.contentWindow.postMessage(data, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event that fires whenever the window receives a message from its parent
|
||||
* via window.postMessage.
|
||||
*/
|
||||
function onMessageReceived(event) {
|
||||
var data, method;
|
||||
|
||||
try {
|
||||
data = JSON.parse(event.data);
|
||||
method = data.event || data.method;
|
||||
}
|
||||
catch(e) {
|
||||
//fail silently... like a ninja!
|
||||
}
|
||||
|
||||
if (method == 'ready' && !isReady) {
|
||||
isReady = true;
|
||||
}
|
||||
|
||||
// Handles messages from moogaloop only
|
||||
if (event.origin != playerDomain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var value = data.value,
|
||||
eventData = data.data,
|
||||
target_id = target_id === '' ? null : data.player_id,
|
||||
|
||||
callback = getCallback(method, target_id),
|
||||
params = [];
|
||||
|
||||
if (!callback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value !== undefined) {
|
||||
params.push(value);
|
||||
}
|
||||
|
||||
if (eventData) {
|
||||
params.push(eventData);
|
||||
}
|
||||
|
||||
if (target_id) {
|
||||
params.push(target_id);
|
||||
}
|
||||
|
||||
return params.length > 0 ? callback.apply(null, params) : callback.call();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stores submitted callbacks for each iframe being tracked and each
|
||||
* event for that iframe.
|
||||
*
|
||||
* @param eventName (String): Name of the event. Eg. api_onPlay
|
||||
* @param callback (Function): Function that should get executed when the
|
||||
* event is fired.
|
||||
* @param target_id (String) [Optional]: If handling more than one iframe then
|
||||
* it stores the different callbacks for different iframes based on the iframe's
|
||||
* id.
|
||||
*/
|
||||
function storeCallback(eventName, callback, target_id) {
|
||||
if (target_id) {
|
||||
if (!eventCallbacks[target_id]) {
|
||||
eventCallbacks[target_id] = {};
|
||||
}
|
||||
eventCallbacks[target_id][eventName] = callback;
|
||||
}
|
||||
else {
|
||||
eventCallbacks[eventName] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves stored callbacks.
|
||||
*/
|
||||
function getCallback(eventName, target_id) {
|
||||
if (target_id) {
|
||||
return eventCallbacks[target_id][eventName];
|
||||
}
|
||||
else {
|
||||
return eventCallbacks[eventName];
|
||||
}
|
||||
}
|
||||
|
||||
function removeCallback(eventName, target_id) {
|
||||
if (target_id && eventCallbacks[target_id]) {
|
||||
if (!eventCallbacks[target_id][eventName]) {
|
||||
return false;
|
||||
}
|
||||
eventCallbacks[target_id][eventName] = null;
|
||||
}
|
||||
else {
|
||||
if (!eventCallbacks[eventName]) {
|
||||
return false;
|
||||
}
|
||||
eventCallbacks[eventName] = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a domain's root domain.
|
||||
* Eg. returns http://vimeo.com when http://vimeo.com/channels is sbumitted
|
||||
*
|
||||
* @param url (String): Url to test against.
|
||||
* @return url (String): Root domain of submitted url
|
||||
*/
|
||||
function getDomainFromUrl(url) {
|
||||
if (url.substr(0, 2) === '//') {
|
||||
url = window.location.protocol + url;
|
||||
}
|
||||
|
||||
var url_pieces = url.split('/'),
|
||||
domain_str = '';
|
||||
|
||||
for(var i = 0, length = url_pieces.length; i < length; i++) {
|
||||
if(i<3) {domain_str += url_pieces[i];}
|
||||
else {break;}
|
||||
if(i<2) {domain_str += '/';}
|
||||
}
|
||||
|
||||
return domain_str;
|
||||
}
|
||||
|
||||
function isFunction(obj) {
|
||||
return !!(obj && obj.constructor && obj.call && obj.apply);
|
||||
}
|
||||
|
||||
function isArray(obj) {
|
||||
return toString.call(obj) === '[object Array]';
|
||||
}
|
||||
|
||||
// Give the init function the Froogaloop prototype for later instantiation
|
||||
Froogaloop.fn.init.prototype = Froogaloop.fn;
|
||||
|
||||
// Listens for the message event.
|
||||
// W3C
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('message', onMessageReceived, false);
|
||||
}
|
||||
// IE
|
||||
else {
|
||||
window.attachEvent('onmessage', onMessageReceived);
|
||||
}
|
||||
|
||||
// Expose froogaloop to the global object
|
||||
return (window.Froogaloop = window.$f = Froogaloop);
|
||||
|
||||
})();
|
369
src/tech/youtube.js
Normal file
369
src/tech/youtube.js
Normal file
@ -0,0 +1,369 @@
|
||||
|
||||
/* VideoJS-YouTube - YouTube iFrame Wrapper
|
||||
================================================================================ */
|
||||
_V_.youtube = _V_.PlaybackTech.extend({
|
||||
|
||||
init: function(player, options){
|
||||
this.player = player;
|
||||
|
||||
var source = options.source;
|
||||
|
||||
// Extract the YouTube videoID from the source
|
||||
var videoId = this.getVideoId(source.src);
|
||||
|
||||
// Which element to embed in
|
||||
var parentEl = options.parentEl;
|
||||
|
||||
// Generate ID for iFrame
|
||||
var objId = player.el.id+"_youtube_api";
|
||||
|
||||
// Create an iFrame for the YouTube player
|
||||
var iFrm = this.el = _V_.createElement("iframe", {
|
||||
id: objId + "_iframe",
|
||||
name: objId + "_iframe",
|
||||
className: "vjs-tech",
|
||||
scrolling: "no",
|
||||
marginWidth: 0,
|
||||
marginHeight: 0,
|
||||
frameBorder: 0,
|
||||
webkitAllowFullScreen: "",
|
||||
mozallowfullscreen: "",
|
||||
allowFullScreen: ""
|
||||
});
|
||||
|
||||
// Store a global list of currently loading players
|
||||
_V_.youtube.loadingEls = _V_.youtube.loadingEls || [];
|
||||
_V_.youtube.loadingEls.push(parentEl);
|
||||
|
||||
var playerOptions = player.options;
|
||||
var optionsParams = options.params || {};
|
||||
|
||||
// Setup player parameters
|
||||
var params = {
|
||||
disablekb: 1,
|
||||
enablejsapi: 1,
|
||||
iv_load_policy: 3,
|
||||
modestbranding: 1,
|
||||
playerapiid: objId,
|
||||
wmode: "opaque", // Opaque is needed to overlay controls, but can affect playback performance (Flash only)
|
||||
rel: 0,
|
||||
showinfo: 0,
|
||||
showsearch: 0,
|
||||
controls: this.toBoolInt(optionsParams.ytcontrols || playerOptions.ytcontrols),
|
||||
autoplay: this.toBoolInt(optionsParams.autoplay || playerOptions.autoplay),
|
||||
loop: this.toBoolInt(optionsParams.loop || playerOptions.loop),
|
||||
hd: this.toBoolInt(optionsParams.hd || playerOptions.hd)
|
||||
};
|
||||
|
||||
var p = (document.location.protocol == 'https:') ? 'https:' : 'http:';
|
||||
|
||||
if (document.domain != 'localhost' && document.location.protocol != 'file:')
|
||||
params.origin = p + "//" + document.domain;
|
||||
|
||||
this.player.apiArgs = {
|
||||
videoId: videoId,
|
||||
playerVars: params,
|
||||
events: {
|
||||
"onReady": _V_.youtube.onReady,
|
||||
"onStateChange": _V_.youtube.onStateChange,
|
||||
"onPlaybackQualityChange": _V_.youtube.onPlaybackQualityChange,
|
||||
"onError": _V_.youtube.onError
|
||||
}
|
||||
};
|
||||
|
||||
_V_.addEvent(parentEl, "techready", _V_.proxy(this, function(){
|
||||
// YouTube JS API is ready, load the player
|
||||
iFrm.src = p + "//www.youtube.com/embed/" + videoId + "?" +
|
||||
this.makeQueryString(params);
|
||||
// Initialize the YouTube Player object. Only pass events as arguments,
|
||||
// since all of our other parameters went into the iFrame URL
|
||||
this.youtube = new YT.Player(iFrm, { events: this.player.apiArgs.events });
|
||||
}));
|
||||
|
||||
// Add iFrame to player div
|
||||
_V_.insertFirst(iFrm, parentEl);
|
||||
|
||||
_V_.youtube.updateVideoQuality(this.player, null);
|
||||
|
||||
this.loadApi();
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
this.youtube.destroy();
|
||||
delete this.youtube;
|
||||
},
|
||||
|
||||
play: function(){ this.youtube.playVideo(); },
|
||||
pause: function(){ this.youtube.pauseVideo(); },
|
||||
paused: function(){
|
||||
var state = this.youtube.getPlayerState();
|
||||
return state !== YT.PlayerState.PLAYING &&
|
||||
state !== YT.PlayerState.BUFFERING;
|
||||
},
|
||||
|
||||
src: function(src){
|
||||
delete this.player.error;
|
||||
|
||||
// FIXME: Does this work or do we have to set the iFrame src again?
|
||||
var videoId = this.getVideoId(src);
|
||||
this.youtube.loadVideoById(videoId);
|
||||
},
|
||||
load: function(){ },
|
||||
poster: function(){
|
||||
var videoId = this.getVideoId(this.youtube.getVideoUrl());
|
||||
return "http://img.youtube.com/vi/" + videoId + "/0.jpg";
|
||||
},
|
||||
|
||||
currentTime: function(){ return this.youtube.getCurrentTime() || 0; },
|
||||
setCurrentTime: function(seconds){
|
||||
this.youtube.seekTo(seconds, true);
|
||||
this.player.triggerEvent("timeupdate");
|
||||
},
|
||||
|
||||
duration: function(){ return this.youtube.getDuration() || 0; },
|
||||
buffered: function(){
|
||||
var loadedBytes = this.youtube.getVideoBytesLoaded();
|
||||
var totalBytes = this.youtube.getVideoBytesTotal();
|
||||
if (!loadedBytes || !totalBytes) return 0;
|
||||
|
||||
var duration = this.youtube.getDuration();
|
||||
var secondsBuffered = (loadedBytes / totalBytes) * duration;
|
||||
var secondsOffset = (this.youtube.getVideoStartBytes() / totalBytes) * duration;
|
||||
return _V_.createTimeRange(secondsOffset, secondsOffset + secondsBuffered);
|
||||
},
|
||||
|
||||
volume: function(){
|
||||
if (isNaN(this.youtube.volumeVal))
|
||||
this.youtube.volumeVal = this.youtube.getVolume() / 100.0;
|
||||
return this.youtube.volumeVal;
|
||||
},
|
||||
setVolume: function(percentAsDecimal){
|
||||
if (percentAsDecimal != this.youtube.volumeVal) {
|
||||
this.youtube.volumeVal = percentAsDecimal;
|
||||
this.youtube.setVolume(percentAsDecimal * 100.0);
|
||||
this.player.triggerEvent("volumechange");
|
||||
}
|
||||
},
|
||||
muted: function(){ return this.youtube.isMuted(); },
|
||||
setMuted: function(muted){
|
||||
if (muted)
|
||||
this.youtube.mute();
|
||||
else
|
||||
this.youtube.unMute();
|
||||
|
||||
// Volume changes do not show up in the API immediately, so we need
|
||||
// to wait for a moment
|
||||
var self = this;
|
||||
setTimeout(function() { self.player.triggerEvent("volumechange"); }, 50);
|
||||
},
|
||||
|
||||
width: function(){ return this.el.offsetWidth; },
|
||||
height: function(){ return this.el.offsetHeight; },
|
||||
|
||||
currentSrc: function(){ return this.youtube.getVideoUrl(); },
|
||||
|
||||
preload: function(){ return false; },
|
||||
setPreload: function(val){ },
|
||||
autoplay: function(){ return !!this.player.apiArgs.playerVars.autoplay; },
|
||||
setAutoplay: function(val){ },
|
||||
loop: function(){ return !!this.player.apiArgs.playerVars.loop; },
|
||||
setLoop: function(val){
|
||||
this.player.apiArgs.playerVars.loop = (val ? 1 : 0);
|
||||
// We handle looping manually
|
||||
//this.youtube.setLoop(val);
|
||||
},
|
||||
|
||||
supportsFullScreen: function(){ return false; },
|
||||
enterFullScreen: function(){ return false; },
|
||||
|
||||
error: function(){ return this.player.error; },
|
||||
seeking: function(){ return false; },
|
||||
ended: function(){ return this.youtube.getPlayerState() === YT.PlayerState.ENDED; },
|
||||
videoWidth: function(){ return this.player.videoWidth; },
|
||||
videoHeight: function(){ return this.player.videoHeight; },
|
||||
controls: function(){ return this.player.options.controls; },
|
||||
defaultMuted: function(){ return false; },
|
||||
|
||||
// Helpers ------------------------------------------------------------------
|
||||
|
||||
makeQueryString: function(args) {
|
||||
var array = [];
|
||||
for (var key in args) {
|
||||
if (args.hasOwnProperty(key))
|
||||
array.push(encodeURIComponent(key) + "=" + encodeURIComponent(args[key]));
|
||||
}
|
||||
return array.join("&");
|
||||
},
|
||||
|
||||
getVideoId: function(url) {
|
||||
return url.match(/v=([^&]+)/)[1];
|
||||
},
|
||||
|
||||
toBoolInt: function(val) {
|
||||
return val ? 1 : 0;
|
||||
},
|
||||
|
||||
loadApi: function() {
|
||||
// Check if the YouTube JS API has already been loaded
|
||||
var js, id = "youtube-jssdk", ref = document.getElementsByTagName("script")[0];
|
||||
if (_V_.el(id)) {
|
||||
window.onYouTubePlayerAPIReady();
|
||||
return;
|
||||
}
|
||||
|
||||
// Asynchronously load the YouTube JS API
|
||||
var p = (document.location.protocol == "https:") ? "https:" : "http:";
|
||||
js = _V_.createElement("script", { id: id, async: true, src: p + "//www.youtube.com/player_api" });
|
||||
ref.parentNode.insertBefore(js, ref);
|
||||
}
|
||||
});
|
||||
|
||||
// Event callbacks ------------------------------------------------------------
|
||||
|
||||
_V_.youtube.onReady = function(e){
|
||||
var player = e.target.getIframe().parentNode.player;
|
||||
|
||||
player.tech.triggerReady();
|
||||
player.triggerReady();
|
||||
player.triggerEvent("durationchange");
|
||||
|
||||
_V_.youtube.hideOverlay(player);
|
||||
};
|
||||
|
||||
_V_.youtube.onStateChange = function(e){
|
||||
var player = e.target.getIframe().parentNode.player;
|
||||
|
||||
// Suppress any duplicate events from YouTube
|
||||
if (player.lastState === e.data)
|
||||
return;
|
||||
|
||||
switch (e.data) {
|
||||
case -1: // Unstarted
|
||||
player.triggerEvent("durationchange");
|
||||
break;
|
||||
case YT.PlayerState.CUED:
|
||||
break;
|
||||
case YT.PlayerState.ENDED:
|
||||
player.triggerEvent("ended");
|
||||
_V_.youtube.hideOverlay(player);
|
||||
|
||||
// YouTube looping doesn't seem to play well with VideoJS, so we need to
|
||||
// implement it manually here
|
||||
if (player.apiArgs.playerVars.loop) {
|
||||
player.tech.youtube.seekTo(0, true);
|
||||
player.tech.youtube.playVideo();
|
||||
} else {
|
||||
player.tech.youtube.stopVideo();
|
||||
}
|
||||
break;
|
||||
case YT.PlayerState.PLAYING:
|
||||
player.triggerEvent("timeupdate");
|
||||
player.triggerEvent("playing");
|
||||
player.triggerEvent("play");
|
||||
break;
|
||||
case YT.PlayerState.PAUSED:
|
||||
player.triggerEvent("pause");
|
||||
break;
|
||||
case YT.PlayerState.BUFFERING:
|
||||
player.triggerEvent("timeupdate");
|
||||
player.triggerEvent("waiting");
|
||||
// Hide the waiting spinner since YouTube has its own
|
||||
player.loadingSpinner.hide();
|
||||
break;
|
||||
}
|
||||
|
||||
player.lastState = e.data;
|
||||
};
|
||||
|
||||
_V_.youtube.onPlaybackQualityChange = function(e){
|
||||
var player = e.target.getIframe().parentNode.player;
|
||||
_V_.youtube.updateVideoQuality(player, e.data);
|
||||
player.triggerEvent("ratechange");
|
||||
};
|
||||
|
||||
_V_.youtube.onError = function(e){
|
||||
var player = e.target.getIframe().parentNode.player;
|
||||
player.error = e.data;
|
||||
player.triggerEvent("error");
|
||||
};
|
||||
|
||||
// Helpers --------------------------------------------------------------------
|
||||
|
||||
_V_.youtube.hideOverlay = function(player) {
|
||||
// Hide the big play button and poster since YouTube provides these. Using
|
||||
// our own prevents the video from playing on the first click in mobile
|
||||
// devices
|
||||
player.bigPlayButton.hide();
|
||||
player.posterImage.hide();
|
||||
};
|
||||
|
||||
_V_.youtube.updateVideoQuality = function(player, quality) {
|
||||
switch (quality) {
|
||||
case 'medium':
|
||||
player.videoWidth = 480;
|
||||
player.videoHeight = 360;
|
||||
break;
|
||||
case 'large':
|
||||
player.videoWidth = 640;
|
||||
player.videoHeight = 480;
|
||||
break;
|
||||
case 'hd720':
|
||||
player.videoWidth = 960;
|
||||
player.videoHeight = 720;
|
||||
break;
|
||||
case 'hd1080':
|
||||
player.videoWidth = 1440;
|
||||
player.videoHeight = 1080;
|
||||
break;
|
||||
case 'highres':
|
||||
player.videoWidth = 1920;
|
||||
player.videoHeight = 1080;
|
||||
break;
|
||||
case 'small':
|
||||
player.videoWidth = 320;
|
||||
player.videoHeight = 240;
|
||||
break;
|
||||
default:
|
||||
player.videoWidth = 0;
|
||||
player.videoHeight = 0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Support testing ------------------------------------------------------------
|
||||
|
||||
_V_.youtube.isSupported = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
_V_.youtube.canPlaySource = function(srcObj){
|
||||
return srcObj.type == "video/youtube";
|
||||
};
|
||||
|
||||
_V_.youtube.prototype.support = {
|
||||
formats: {
|
||||
"video/youtube": "YT"
|
||||
},
|
||||
|
||||
// Optional events that we can manually mimic with timers
|
||||
progressEvent: false,
|
||||
timeupdateEvent: false,
|
||||
|
||||
//fullscreen: true,
|
||||
// In iOS, if you move a video element in the DOM, it breaks video playback.
|
||||
movingElementInDOM: !_V_.isIOS(),
|
||||
|
||||
fullscreenResize: true,
|
||||
parentResize: true
|
||||
};
|
||||
|
||||
// YouTube JS API load callback -----------------------------------------------
|
||||
|
||||
window.onYouTubePlayerAPIReady = function() {
|
||||
// Fire a techready event for each loading player
|
||||
var loadingEl;
|
||||
while ((loadingEl = _V_.youtube.loadingEls.shift())) {
|
||||
loadingEl.player.triggerEvent("techready");
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user