mirror of
https://github.com/videojs/video.js.git
synced 2025-03-31 22:22:09 +02:00
445 lines
16 KiB
JavaScript
445 lines
16 KiB
JavaScript
/* 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>';
|
|
};
|