1
0
mirror of https://github.com/videojs/video.js.git synced 2024-12-25 02:42:10 +02:00

Optimized slider controls.

Fixed issue with fullscreen progress bar tracking (offset left wasn't working in new fullscreen)
This commit is contained in:
Steve Heffernan 2011-12-05 11:28:18 -08:00
parent afc9369849
commit e4f6bbb0b2
9 changed files with 235 additions and 1313 deletions

View File

@ -79,7 +79,7 @@ so you can upgrade to newer versions easier. You can remove all these styles by
}
.vjs-default-skin .vjs-control:focus {
outline: 0;
/* outline: 0;*/
}
/* Hide control text visually, but have it available for screenreaders: h5bp.com/v */

View File

@ -61,11 +61,8 @@ _V_.Component = _V_.Class.extend({
destroy: function(){},
createElement: function(type, options){
options = _V_.merge({
/* Standar Options */
}, options || {});
return _V_.createElement(type || "div", options);
createElement: function(type, attrs){
return _V_.createElement(type || "div", attrs);
},
buildCSSClass: function(){

374
src/controls.js vendored
View File

@ -1,9 +1,6 @@
/* Control - Base class for all control elements
================================================================================ */
_V_.Control = _V_.Component.extend({
init: function(player, options){
this._super(player, options);
},
buildCSSClass: function(){
return "vjs-control " + this._super();
@ -18,27 +15,21 @@ _V_.Button = _V_.Control.extend({
init: function(player, options){
this._super(player, options);
_V_.addEvent(this.el, "click", _V_.proxy(this, this.onClick));
_V_.addEvent(this.el, "focus", _V_.proxy(this, this.onFocus));
_V_.addEvent(this.el, "blur", _V_.proxy(this, this.onBlur));
return "fdsa";
this.addEvent("click", this.onClick);
this.addEvent("focus", this.onFocus);
this.addEvent("blur", this.onBlur);
},
createElement: function(type, attrs){
// Default to Div element
type = type || "div";
// Add standard Aria and Tabindex info
attrs = _V_.merge({
className: this.buildCSSClass(),
innerHTML: '<div><span class="vjs-control-text">' + (this.buttonText || "Need Text") + '</span></div>',
role: "button",
tabIndex: 0
}, attrs || {});
}, attrs);
return this._super(type, {
className: attrs.className || this.buildCSSClass(),
innerHTML: attrs.innerHTML || '<div><span class="vjs-control-text">' + (this.buttonText || "Need Text") + '</span></div>'
});
return this._super(type, attrs);
},
// Click - Override with specific functionality for button
@ -186,8 +177,8 @@ _V_.ControlBar = _V_.Component.extend({
init: function(player, options){
this._super(player, options);
// player.addEvent("mouseover", _V_.proxy(this, this.show));
// player.addEvent("mouseout", _V_.proxy(this, this.hide));
player.addEvent("mouseover", _V_.proxy(this, this.show));
player.addEvent("mouseout", _V_.proxy(this, this.hide));
},
createElement: function(){
@ -312,6 +303,138 @@ _V_.RemainingTimeDisplay = _V_.Component.extend({
});
/* Slider - Parent for seek bar and volume slider
================================================================================ */
_V_.Slider = _V_.Component.extend({
init: function(player, options){
this._super(player, options);
_V_.each.call(this, this.components, function(comp){
if (comp instanceof _V_[this.barClass]) {
this.bar = comp;
} else if (comp instanceof _V_[this.handleClass]) {
this.handle = comp;
}
});
player.addEvent(this.playerEvent, _V_.proxy(this, this.update));
this.addEvent("mousedown", this.onMouseDown);
this.addEvent("focus", this.onFocus);
this.addEvent("blur", this.onBlur);
},
createElement: function(type, attrs) {
attrs = _V_.merge({
role: "slider",
"aria-valuenow": 0,
"aria-valuemin": 0,
"aria-valuemax": 100,
tabIndex: 0
}, attrs);
return this._super(type, attrs);
},
onMouseDown: function(event){
event.preventDefault();
_V_.blockTextSelection();
_V_.addEvent(document, "mousemove", _V_.proxy(this, this.onMouseMove));
_V_.addEvent(document, "mouseup", _V_.proxy(this, this.onMouseUp));
this.onMouseMove(event);
},
onMouseUp: function(event) {
_V_.unblockTextSelection();
_V_.removeEvent(document, "mousemove", this.onMouseMove, false);
_V_.removeEvent(document, "mouseup", this.onMouseUp, false);
},
update: function(){
// If scrubbing, we could use a cached value to make the handle keep up with the user's mouse.
// On HTML5 browsers scrubbing is really smooth, but some flash players are slow, so we might want to utilize this later.
// var progress = (this.player.scrubbing) ? this.player.values.currentTime / this.player.duration() : this.player.currentTime() / this.player.duration();
var progress = this.getPercent();
handle = this.handle,
bar = this.bar,
barProgress = progress;
// Protect against no duration and other division issues
if (isNaN(progress)) { progress = 0; }
// If there is a handle, we need to account for the handle in our calculation for progress bar
// so that it doesn't fall short of or extend past the handle.
if (handle) {
var box = this.el,
boxWidth = box.offsetWidth,
// The width of the handle in percent of the containing box
handlePercent = handle.el.offsetWidth / boxWidth,
// Get the adjusted size of the box, considering that the handle's center never touches the left or right side.
// There is a margin of half the handle's width on both sides.
boxAdjustedPercent = 1 - handlePercent;
// Adjust the progress that we'll use to set widths to the new adjusted box width
adjustedProgress = progress * boxAdjustedPercent,
// The bar does reach the left side, so we need to account for this in the bar's width
barProgress = adjustedProgress + (handlePercent / 2);
// Move the handle from the left based on the adjected progress
handle.el.style.left = _V_.round(adjustedProgress * 100, 2) + "%";
}
// Set the new bar width
bar.el.style.width = _V_.round(barProgress * 100, 2) + "%";
},
calculateDistance: function(event){
var box = this.el,
boxX = _V_.findPosX(box),
boxW = box.offsetWidth,
handle = this.handle;
// _V_.log(box.offsetLeft, box.offsetLeft)
if (handle) {
var handleW = handle.el.offsetWidth;
// Adjusted X and Width, so handle doesn't go outside the bar
boxX = boxX + (handleW / 2);
boxW = boxW - handleW;
}
// _V_.log(event.pageX, boxX, boxW);
// Percent that the click is through the adjusted area
return Math.max(0, Math.min(1, (event.pageX - boxX) / boxW));
},
onFocus: function(event){
_V_.addEvent(document, "keyup", _V_.proxy(this, this.onKeyPress));
},
onKeyPress: function(event){
if (event.which == 37) { // Left Arrow
event.preventDefault();
this.stepBack();
} else if (event.which == 39) { // Right Arrow
event.preventDefault();
this.stepForward();
}
},
onBlur: function(event){
_V_.removeEvent(document, "keyup", _V_.proxy(this, this.onKeyPress));
}
});
/* Progress
================================================================================ */
@ -327,28 +450,14 @@ _V_.ProgressControl = _V_.Component.extend({
});
// Seek Bar and holder for the progress bars
_V_.SeekBar = _V_.Component.extend({
_V_.SeekBar = _V_.Slider.extend({
barClass: "PlayProgressBar",
handleClass: "SeekHandle",
playerEvent: "timeupdate",
init: function(player, options){
this._super(player, options);
_V_.each.call(this, this.components, function(comp){
if (comp instanceof _V_.PlayProgressBar) {
this.playProgressBar = comp;
} else if (comp instanceof _V_.SeekHandle) {
this.seekHandle = comp;
}
});
player.addEvent("timeupdate", _V_.proxy(this, this.update));
_V_.addEvent(this.el, "mousedown", _V_.proxy(this, this.onMouseDown));
_V_.addEvent(this.el, "focus", _V_.proxy(this, this.onFocus));
_V_.addEvent(this.el, "blur", _V_.proxy(this, this.onBlur));
// _V_.addEvent(element, "mousedown", _V_.proxy(element, function(event){
// player.onSeekBarMouseDown(event, this);
// }));
},
createElement: function(){
@ -357,66 +466,21 @@ _V_.SeekBar = _V_.Component.extend({
});
},
update: function(){
// If scrubbing, use the cached currentTime value for speed
var progress = /* (this.player.scrubbing) ? this.player.values.currentTime / this.player.duration() : */ this.player.currentTime() / this.player.duration();
// Protect against no duration and other division issues
if (isNaN(progress)) { progress = 0; }
var // barData = _V_.getData(bar),
barX = _V_.findPosX(this.el),
barW = this.el.offsetWidth,
handle = this.seekHandle,
progBar = this.playProgressBar,
handleW = (handle) ? handle.el.offsetWidth : 0;
// Adjusted X and Width, so handle doesn't go outside the bar
barAX = barX + (handleW / 2),
barAW = barW - handleW,
progBarProgress = _V_.round(progress * barAW + handleW / 2) + "px";
if (progBar && progBar.el.style) {
progBar.el.style.width = progBarProgress;
}
if (handle) {
handle.el.style.left = _V_.round(progress * barAW)+"px";
}
getPercent: function(){
return this.player.currentTime() / this.player.duration();
},
onMouseDown: function(event){
event.preventDefault();
_V_.blockTextSelection();
this.player.currSeekBar = this;
this.player.currHandle = this.seekHandle || false;
this._super(event);
this.player.scrubbing = true;
this.player.videoWasPlaying = !this.player.paused();
this.videoWasPlaying = !this.player.paused();
this.player.pause();
this.setCurrentTimeWithScrubber(event);
_V_.addEvent(document, "mousemove", _V_.proxy(this, this.onMouseMove));
_V_.addEvent(document, "mouseup", _V_.proxy(this, this.onMouseUp));
},
setCurrentTimeWithScrubber: function(event){
var bar = this.el,
barX = _V_.findPosX(bar),
barW = bar.offsetWidth,
handle = this.player.currHandle.el,
handleW = (handle) ? handle.offsetWidth : 0;
// Adjusted X and Width, so handle doesn't go outside the bar
barAX = barX + (handleW / 2),
barAW = barW - handleW,
// Percent that the click is through the adjusted area
percent = Math.max(0, Math.min(1, (event.pageX - barAX) / barAW)),
// Percent translated to pixels
percentPix = percent * barAW,
// Percent translated to seconds
newTime = percent * this.player.duration();
onMouseMove: function(event){
var newTime = this.calculateDistance(event) * this.player.duration();
// Don't let video end while scrubbing.
if (newTime == this.player.duration()) { newTime = newTime - 0.1; }
@ -425,33 +489,21 @@ _V_.SeekBar = _V_.Component.extend({
this.player.currentTime(newTime);
},
onMouseMove: function(event){ // Removeable
this.setCurrentTimeWithScrubber(event);
},
onMouseUp: function(event){ // Removeable
_V_.unblockTextSelection();
_V_.removeEvent(document, "mousemove", this.onMouseMove, false);
_V_.removeEvent(document, "mouseup", this.onMouseUp, false);
onMouseUp: function(event){
this._super(event);
this.player.scrubbing = false;
if (this.player.videoWasPlaying) {
if (this.videoWasPlaying) {
this.player.play();
}
},
onFocus: function(event){
_V_.addEvent(document, "keyup", _V_.proxy(this, this.onKeyPress));
stepForward: function(){
this.player.currentTime(this.player.currentTime() + 1);
},
onKeyPress: function(event){
if (event.which == 37) {
event.preventDefault();
this.player.currentTime(this.player.currentTime() - 1);
} else if (event.which == 39) {
event.preventDefault();
this.player.currentTime(this.player.currentTime() + 1);
}
},
onBlur: function(event){
_V_.removeEvent(document, "keyup", _V_.proxy(this, this.onKeyPress));
stepBack: function(){
this.player.currentTime(this.player.currentTime() - 1);
}
});
@ -497,9 +549,7 @@ _V_.SeekHandle = _V_.Component.extend({
createElement: function(){
return this._super("div", {
className: "vjs-seek-handle",
innerHTML: '<span class="vjs-control-text">00:00</span>',
tabIndex: 0,
role: "slider", "aria-valuenow": 0, "aria-valuemin": 0, "aria-valuemax": 100
innerHTML: '<span class="vjs-control-text">00:00</span>'
});
}
@ -518,25 +568,11 @@ _V_.VolumeControl = _V_.Component.extend({
});
_V_.VolumeBar = _V_.Component.extend({
_V_.VolumeBar = _V_.Slider.extend({
init: function(player, options){
this._super(player, options);
_V_.each.call(this, this.components, function(comp){
if (comp instanceof _V_.VolumeLevel) {
this.volumeLevel = comp;
} else if (comp instanceof _V_.VolumeHandle) {
this.volumeHandle = comp;
}
});
player.addEvent("volumechange", _V_.proxy(this, this.update));
_V_.addEvent(this.el, "mousedown", _V_.proxy(this, this.onMouseDown));
// _V_.addEvent(this.el, "focus", _V_.proxy(this, this.onFocus));
// _V_.addEvent(this.el, "blur", _V_.proxy(this, this.onBlur));
},
barClass: "VolumeLevel",
handleClass: "VolumeHandle",
playerEvent: "volumechange",
createElement: function(){
return this._super("div", {
@ -544,70 +580,21 @@ _V_.VolumeBar = _V_.Component.extend({
});
},
onMouseDown: function(event){
event.preventDefault();
_V_.blockTextSelection();
this.player.currVolumeBar = this;
this.player.currHandle = this.volumeHandle || false;
this.setVolumeWithSlider(event);
_V_.addEvent(document, "mousemove", _V_.proxy(this, this.onMouseMove));
_V_.addEvent(document, "mouseup", _V_.proxy(this, this.onMouseUp));
},
onMouseMove: function(event){ // Removeable
this.setVolumeWithSlider(event);
},
onMouseUp: function(event){ // Removeable
_V_.unblockTextSelection();
_V_.removeEvent(document, "mousemove", this.onMouseMove, false);
_V_.removeEvent(document, "mouseup", this.onMouseUp, false);
onMouseMove: function(event) {
this.player.volume(this.calculateDistance(event));
},
setVolumeWithSlider: function(event){
var bar = this.el,
barX = _V_.findPosX(bar),
barW = bar.offsetWidth,
handle = (this.player.currHandle) ? this.player.currHandle.el : false,
handleW = (handle) ? handle.offsetWidth : 0;
// Adjusted X and Width, so handle doesn't go outside the bar
barAX = barX + (handleW / 2),
barAW = barW - handleW,
// Percent that the click is through the adjusted area
percent = Math.max(0, Math.min(1, (event.pageX - barAX) / barAW)),
// Percent translated to pixels
percentPix = percent * barAW,
// Percent translated to seconds
newTime = percent * this.player.duration();
this.player.volume(percent);
getPercent: function(){
return this.player.volume();
},
update: function(){
var vol = this.player.volume();
var bar = this.el;
barX = _V_.findPosX(bar),
barW = bar.offsetWidth,
handle = (this.volumeHandle) ? this.volumeHandle.el : false,
level = (this.volumeLevel) ? this.volumeLevel.el : false,
handleW = (handle) ? handle.offsetWidth : 0;
// Adjusted X and Width, so handle doesn't go outside the bar
barAX = barX + (handleW / 2),
barAW = barW - handleW,
progBarProgress = _V_.round(vol * barAW + handleW / 2) + "px";
if (level) {
level.style.width = progBarProgress;
}
if (handle) {
handle.style.left = _V_.round(vol * barAW)+"px";
}
}
stepForward: function(){
this.player.volume(this.player.volume() + 0.1);
},
stepBack: function(){
this.player.volume(this.player.volume() - 0.1);
}
});
_V_.VolumeLevel = _V_.Component.extend({
@ -626,9 +613,9 @@ _V_.VolumeHandle = _V_.Component.extend({
createElement: function(){
return this._super("div", {
className: "vjs-volume-handle",
innerHTML: '<span class="vjs-control-text"></span>',
tabindex: 0,
role: "slider", "aria-valuenow": 0, "aria-valuemin": 0, "aria-valuemax": 100
innerHTML: '<span class="vjs-control-text"></span>'
// tabindex: 0,
// role: "slider", "aria-valuenow": 0, "aria-valuemin": 0, "aria-valuemax": 100
});
}
@ -708,16 +695,19 @@ _V_.SubtitlesDisplay = _V_.TextTrackDisplay.extend({
trackType: "subtitles"
});
_V_.CaptionsDisplay = _V_.TextTrackDisplay.extend({
trackType: "captions"
});
_V_.ChaptersDisplay = _V_.TextTrackDisplay.extend({
trackType: "chapters"
});
_V_.DescriptionsDisplay = _V_.TextTrackDisplay.extend({
trackType: "descriptions"

View File

@ -1,4 +1,7 @@
_V_.merge = function(obj1, obj2, safe){
// Make sure second object exists
if (!obj2) { obj2 = {}; };
for (var attrname in obj2){
if (obj2.hasOwnProperty(attrname) && (!safe || !obj1.hasOwnProperty(attrname))) { obj1[attrname]=obj2[attrname]; }
}
@ -128,14 +131,7 @@ _V_.extend({
getRelativePosition: function(x, relativeElement){
return Math.max(0, Math.min(1, (x - _V_.findPosX(relativeElement)) / relativeElement.offsetWidth));
},
// Get an objects position on the page
findPosX: function(obj) {
var curleft = obj.offsetLeft;
while(obj = obj.offsetParent) {
curleft += obj.offsetLeft;
}
return curleft;
},
getComputedStyleValue: function(element, style){
return window.getComputedStyle(element, null).getPropertyValue(style);
},
@ -300,4 +296,38 @@ _V_.log = function(){
(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,timeStamp,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();){b[a]=b[a]||c}})((function(){try
{console.log();return window.console;}catch(err){return window.console={};}})());
// Offset Left
if ("getBoundingClientRect" in document.documentElement) {
_V_.findPosX = function(el) {
var box;
try {
box = el.getBoundingClientRect();
} catch(e) {}
if (!box) { return 0; }
var docEl = document.documentElement,
body = document.body,
clientLeft = docEl.clientLeft || body.clientLeft || 0,
scrollLeft = window.pageXOffset || body.scrollLeft,
left = box.left + scrollLeft - clientLeft;
return left;
};
} else {
_V_.findPosX = function(el) {
var curleft = el.offsetLeft;
// _V_.log(obj.className, obj.offsetLeft)
while(el = obj.offsetParent) {
if (el.className.indexOf("video-js") == -1) {
// _V_.log(el.offsetParent, "OFFSETLEFT", el.offsetLeft)
// _V_.log("-webkit-full-screen", el.webkitMatchesSelector("-webkit-full-screen"));
// _V_.log("-webkit-full-screen", el.querySelectorAll(".video-js:-webkit-full-screen"));
} else {
}
curleft += el.offsetLeft;
}
return curleft;
};
}

View File

@ -362,9 +362,15 @@ _V_.Player.prototype.extend({
currentTime: function(seconds){
if (seconds !== undefined) {
this.values.currentTime = seconds; // Cache the last set value for smoother scrubbing.
// Cache the last set value for smoother scrubbing.
this.values.currentTime = seconds;
this.apiCall("setCurrentTime", seconds);
if (this.manualTimeUpdates) { this.triggerEvent("timeupdate"); }
if (this.manualTimeUpdates) {
this.triggerEvent("timeupdate");
}
return this;
}
return this.apiCall("currentTime");

View File

@ -1,671 +0,0 @@
/* UI Component- Base class for all UI objects
================================================================================ */
_V_.Player = _V_.Component.extend({
init: function(tag, addOptions, ready){
this.tag = tag; // Store the original tag used to set options
var el = this.el = _V_.createElement("div"), // Div to contain video and controls
options = this.options = {},
width = options.width = tag.width,
height = options.height = tag.height,
// Browsers default to 300x150 if there's no width/height or video size data.
initWidth = width || 300,
initHeight = height || 150;
// Make player findable on elements
tag.player = el.player = this;
// Add callback to ready queue
this.ready(ready);
// Wrap video tag in div (el/box) container
tag.parentNode.insertBefore(el, tag);
el.appendChild(tag); // Breaks iPhone, fixed in HTML5 setup.
// Give video tag properties to box
el.id = this.id = tag.id; // ID will now reference box, not the video tag
el.className = tag.className;
// Update tag id/class for use as HTML5 playback tech
tag.id += "_html5_api";
tag.className = "vjs-tech";
// Make player easily findable by ID
_V_.players[el.id] = this;
// Make box use width/height of tag, or default 300x150
el.setAttribute("width", initWidth);
el.setAttribute("height", initHeight);
// Enforce with CSS since width/height attrs don't work on divs
el.style.width = initWidth+"px";
el.style.height = initHeight+"px";
// Remove width/height attrs from tag so CSS can make it 100% width/height
tag.removeAttribute("width");
tag.removeAttribute("height");
// Set Options
_V_.merge(options, _V_.options); // Copy Global Defaults
_V_.merge(options, this.getVideoTagSettings()); // Override with Video Tag Options
_V_.merge(options, addOptions); // Override/extend with options from setup call
// Store controls setting, and then remove immediately so native controls don't flash.
tag.removeAttribute("controls");
// Empty video tag sources and tracks so the built in player doesn't use them also.
if (tag.hasChildNodes()) {
for (var i=0,j=tag.childNodes;i<j.length;i++) {
if (j[i].nodeName == "SOURCE" || j[i].nodeName == "TRACK") {
tag.removeChild(j[i]);
}
}
}
// Holder for playback tech components
this.techs = {};
// Cache for video property values.
this.values = {};
this.addClass("vjs-paused");
this.addEvent("ended", this.onEnded);
this.addEvent("play", this.onPlay);
this.addEvent("pause", this.onPause);
this.addEvent("error", this.onError);
// When the API is ready, loop through the components and add to the player.
if (this.options.controls) {
this.ready(function(){
this.each(this.options.components, function(set){
this.addComponent(set);
});
});
}
// If there are no sources when the player is initialized,
// load the first supported playback technology.
if (!this.options.sources || this.options.sources.length == 0) {
for (var i=0,j=this.options.techOrder;i<j.length;i++) {
var techName = j[i],
tech = _V_[techName];
// Check if the browser supports this technology
if (tech.isSupported()) {
this.loadTech(techName);
break;
}
}
} else {
// Loop through playback technologies (HTML5, Flash) and check for support
// Then load the best source.
this.src(this.options.sources);
}
},
// Cache for video property values.
values: {},
destroy: function(){},
createElement: function(type, options){
},
getVideoTagSettings: function(){
var options = {
sources: [],
tracks: []
};
options.src = this.tag.src;
options.controls = this.tag.getAttribute("controls") !== null;
options.poster = this.tag.poster;
options.preload = this.tag.preload;
options.autoplay = this.tag.getAttribute("autoplay") !== null; // hasAttribute not IE <8 compatible
options.loop = this.tag.getAttribute("loop") !== null;
options.muted = this.tag.getAttribute("muted") !== null;
for (var c,i=0,j=this.tag.children;i<j.length;i++) {
c = j[i];
if (c.nodeName == "SOURCE") {
options.sources.push({
src: c.src,
type: c.type,
media: c.media,
title: c.title
});
}
if (c.nodeName == "TRACK") {
options.tracks.push(new _V_.Track({
src: c.getAttribute("src"),
kind: c.getAttribute("kind"),
srclang: c.getAttribute("srclang"),
label: c.getAttribute("label"),
'default': c.getAttribute("default") !== null,
title: c.getAttribute("title")
}, this));
}
}
return options;
},
/* PLayback Technology (tech)
================================================================================ */
// Load/Create an instance of playback technlogy including element and API methods
// And append playback element in player div.
loadTech: function(techName, source){
this.triggerEvent("loadingtech");
// Pause and remove current playback technology
if (this.tech) {
this.removeTech(this.tech);
// Turn off any manual progress or timeupdate tracking
if (this.manualProgress) {
this.manualProgressOff()
}
if (this.manualTimeUpdates) {
this.manualTimeUpdatesOff()
}
// If the first time loading, HTML5 tag will exist but won't be initialized
// So we need to remove it if we're not loading HTML5
} else if (!this.tech && techName != "HTML5") {
this.removeTechElement(this.tag);
}
this.techName = techName;
// Turn off API access because we're loading a new tech that might load asynchronously
this.isReady = false;
var techReady = function(){
// Set up playback technology's event triggers
this.setupTriggers();
this.player.triggerReady();
// Manually track progress in cases where the browser/flash player doesn't report it.
if (!_V_.techSupports(this.name, "event", "progress")) {
this.player.manualProgressOn();
}
// Manually track timeudpates in cases where the browser/flash player doesn't report it.
if (!_V_.techSupports(this.name, "event", "timeupdate")) {
this.player.manualTimeUpdatesOn();
}
}
// Initialize new tech if it hasn't been yet and load source
// Add tech element to player div
if (this.techs[techName] === undefined) {
this.techs[techName] = this.tech = new _V_[techName](this, { source: source });
this.tech.ready(techReady)
} else {
this.tech = this.techs[techName];
_V_.log("here3")
_V_.insertFirst(this.techs[techName].el, this.el);
this.src(source);
}
},
removeTech: function(tech){
this.removeTechElement(tech.el);
// TODO: Remove API listeners as well
},
removeTechElement: function(el){
this.el.removeChild(el);
},
/* Fallbacks for unsupported event types
================================================================================ */
// Manually trigger progress events based on changes to the buffered amount
// Many flash players and older HTML5 browsers don't send progress or progress-like events
manualProgressOn: function(){
this.manualProgress = true;
// Trigger progress watching when a source begins loading
this.trackProgress();
// Watch for a native progress event call on the tech element
// In HTML5, some older versions don't support the progress event
// So we're assuming they don't, and turning off manual progress if they do.
this.tech.addEvent("progress", function(){
// Remove this listener from the element
this.removeEvent("progress", arguments.callee);
// Update known progress support for this playback technology
_V_.updateTechSupport(this.name, "event", "progress", true);
// Turn off manual progress tracking
this.player.manualProgressOff();
});
},
manualProgressOff: function(){
this.manualProgress = false;
this.stopTrackingProgress();
},
trackProgress: function(){
this.progressInterval = setInterval(_V_.proxy(this, function(){
// Don't trigger unless buffered amount is greater than last time
// log(this.values.bufferEnd, this.buffered().end(0), this.duration())
/* TODO: update for multiple buffered regions */
if (this.values.bufferEnd < this.buffered().end(0)) {
this.triggerEvent("progress");
} else if (this.bufferedPercent() == 1) {
this.stopTrackingProgress();
this.triggerEvent("progress"); // Last update
}
}), 500);
},
stopTrackingProgress: function(){ clearInterval(this.progressInterval); },
/* Time Tracking -------------------------------------------------------------- */
manualTimeUpdatesOn: function(){
this.manualTimeUpdates = true;
this.addEvent("play", this.trackCurrentTime);
this.addEvent("pause", this.stopTrackingCurrentTime);
// timeupdate is also called by .currentTime whenever current time is set
// Watch for native timeupdate event
this.tech.addEvent("timeupdate", function(){
// Remove this listener from the element
this.removeEvent("timeupdate", arguments.callee);
// Update known progress support for this playback technology
_V_.updateTechSupport(this.name, "event", "timeupdate", true);
// Turn off manual progress tracking
this.player.manualTimeUpdatesOff();
});
},
manualTimeUpdatesOff: function(){
this.manualTimeUpdates = false;
this.stopTrackingCurrentTime();
this.removeEvent("play", this.trackCurrentTime);
this.removeEvent("pause", this.stopTrackingCurrentTime);
},
trackCurrentTime: function(){
if (this.currentTimeInterval) { this.stopTrackingCurrentTime(); }
this.currentTimeInterval = setInterval(_V_.proxy(this, function(){
this.triggerEvent("timeupdate");
}), 250); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
},
// Turn off play progress tracking (when paused or dragging)
stopTrackingCurrentTime: function(){ clearInterval(this.currentTimeInterval); },
/* Player event handlers (how the player reacts to certain events)
================================================================================ */
onEnded: function(){
if (this.options.loop) {
this.currentTime(0);
this.play();
} else {
// this.pause();
// this.currentTime(0);
// this.pause();
}
},
onPlay: function(){
_V_.removeClass(this.el, "vjs-paused");
_V_.addClass(this.el, "vjs-playing");
},
onPause: function(){
_V_.removeClass(this.el, "vjs-playing");
_V_.addClass(this.el, "vjs-paused");
},
onError: function(e) {
_V_.log("Video Error", e);
}
});
/* Player API
================================================================================ */
_V_.Player.prototype.extend({
apiCall: function(method, arg){
if (this.isReady) {
return this.tech[method](arg);
} else {
_V_.log("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]")
return false;
// throw new Error("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]");
}
},
play: function(){
this.apiCall("play"); return this;
},
pause: function(){
this.apiCall("pause"); return this;
},
paused: function(){
return this.apiCall("paused");
},
currentTime: function(seconds){
if (seconds !== undefined) {
this.values.currentTime = seconds; // Cache the last set value for smoother scrubbing.
this.apiCall("setCurrentTime", seconds);
if (this.manualTimeUpdates) { this.triggerEvent("timeupdate"); }
return this;
}
return this.apiCall("currentTime");
},
duration: function(){
return this.apiCall("duration");
},
remainingTime: function(){
return this.duration() - this.currentTime();
},
buffered: function(){
var buffered = this.apiCall("buffered"),
start = 0, end = this.values.bufferEnd = this.values.bufferEnd || 0,
timeRange;
if (buffered && buffered.length > 0 && buffered.end(0) > end) {
end = buffered.end(0);
// Storing values allows them be overridden by setBufferedFromProgress
this.values.bufferEnd = end;
}
return _V_.createTimeRange(start, end);
},
// Calculates amount of buffer is full
bufferedPercent: function(){
return (this.duration()) ? this.buffered().end(0) / this.duration() : 0;
},
volume: function(percentAsDecimal){
if (percentAsDecimal !== undefined) {
var vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); // Force value to between 0 and 1
this.values.volume = vol;
this.apiCall("setVolume", vol);
_V_.setLocalStorage("volume", vol);
return this;
}
// if (this.values.volume) { return this.values.volume; }
return this.apiCall("volume");
},
muted: function(muted){
if (muted !== undefined) {
this.apiCall("setMuted", muted);
return this;
}
return this.apiCall("muted");
},
width: function(width, skipListeners){
if (width !== undefined) {
this.el.width = width;
this.el.style.width = width+"px";
if (!skipListeners) { this.triggerEvent("resize"); }
return this;
}
return parseInt(this.el.getAttribute("width"));
},
height: function(height){
if (height !== undefined) {
this.el.height = height;
this.el.style.height = height+"px";
this.triggerEvent("resize");
return this;
}
return parseInt(this.el.getAttribute("height"));
},
size: function(width, height){
// Skip resize listeners on width for optimization
return this.width(width, true).height(height);
},
supportsFullScreen: function(){ return this.apiCall("supportsFullScreen"); },
// Turn on fullscreen (or window) mode
enterFullScreen: function(){
<<<<<<< HEAD
if (this.supportsFullScreen()) {
this.apiCall("enterFullScreen");
} else {
this.enterFullWindow();
}
this.triggerEvent("enterFullScreen");
return this;
},
exitFullScreen: function(){
if (true || !this.supportsFullScreen()) {
this.exitFullWindow();
}
this.triggerEvent("exitFullScreen");
// Otherwise Shouldn't be called since native fullscreen uses own controls.
return this;
},
=======
this.videoIsFullScreen = true;
if (typeof this.el.webkitRequestFullScreen == 'function') {
this.el.webkitRequestFullScreen();
} else if (this.supportsFullScreen()) {
this.apiCall("enterFullScreen");
} else {
this.enterFullWindow();
}
this.triggerEvent("enterFullScreen");
return this;
},
exitFullScreen: function(){
this.videoIsFullScreen = false;
if (typeof this.el.webkitRequestFullScreen == 'function') {
document.webkitCancelFullScreen();
} else if (this.supportsFullScreen()) {
document.webkitExitFullScreen();
} else {
this.exitFullWindow();
}
this.triggerEvent("exitFullScreen");
// Otherwise Shouldn't be called since native fullscreen uses own controls.
return this;
},
>>>>>>> 2f99215f8dae0cac216f8c494f9058b087b39725
enterFullWindow: function(){
this.videoIsFullScreen = true;
// Storing original doc overflow value to return to when fullscreen is off
this.docOrigOverflow = document.documentElement.style.overflow;
// Add listener for esc key to exit fullscreen
_V_.addEvent(document, "keydown", _V_.proxy(this, this.fullWindowOnEscKey));
// Hide any scroll bars
document.documentElement.style.overflow = 'hidden';
// Apply fullscreen styles
_V_.addClass(document.body, "vjs-full-window");
_V_.addClass(this.el, "vjs-fullscreen");
this.triggerEvent("enterFullWindow");
},
fullWindowOnEscKey: function(event){
if (event.keyCode == 27) {
this.exitFullScreen();
}
},
exitFullWindow: function(){
this.videoIsFullScreen = false;
_V_.removeEvent(document, "keydown", this.fullWindowOnEscKey);
// Unhide scroll bars.
document.documentElement.style.overflow = this.docOrigOverflow;
// Remove fullscreen styles
_V_.removeClass(document.body, "vjs-full-window");
_V_.removeClass(this.el, "vjs-fullscreen");
// Resize the box, controller, and poster to original sizes
// this.positionAll();
this.triggerEvent("exitFullWindow");
},
// src is a pretty powerful function
// If you pass it an array of source objects, it will find the best source to play and use that object.src
// If the new source requires a new playback technology, it will switch to that.
// If you pass it an object, it will set the source to object.src
// If you pass it anything else (url string) it will set the video source to that
src: function(source){
// Case: Array of source objects to choose from and pick the best to play
if (source instanceof Array) {
var sources = source;
techLoop: // Named loop for breaking both loops
// Loop through each playback technology in the options order
for (var i=0,j=this.options.techOrder;i<j.length;i++) {
var techName = j[i],
tech = _V_[techName];
// tech = _V_.tech[techName];
// Check if the browser supports this technology
if (tech.isSupported()) {
// Loop through each source object
for (var a=0,b=sources;a<b.length;a++) {
var source = b[a];
// Check if source can be played with this technology
if (tech.canPlaySource.call(this, source)) {
// If this technology is already loaded, set source
if (techName == this.currentTechName) {
this.src(source); // Passing the source object
// Otherwise load this technology with chosen source
} else {
this.loadTech(techName, source);
}
break techLoop; // Break both loops
}
}
}
}
// Case: Source object { src: "", type: "" ... }
} else if (source instanceof Object) {
if (this.tech.canPlaySource(source)) {
this.src(source.src);
} else {
// Send through tech loop to check for a compatible technology.
this.src([source]);
}
// Case: URL String (http://myvideo...)
} else {
if (!this.isReady) {
this.ready(function(){
this.src(source);
});
} else {
this.apiCall("src", source);
if (this.options.preload == "auto") {
this.load();
}
if (this.options.autoplay) {
this.play();
}
}
}
return this;
},
// Begin loading the src data
load: function(){
this.apiCall("load");
return this;
},
currentSrc: function(){
return this.apiCall("currentSrc");
},
textTrackValue: function(kind, value){
if (value !== undefined) {
this.values[kind] = value;
this.triggerEvent(kind+"update");
return this;
}
return this.values[kind];
},
// Attributes/Options
preload: function(value){
if (value !== undefined) {
this.apiCall("setPreload", value);
this.options.preload = value;
return this;
}
return this.apiCall("preload", value);
},
autoplay: function(value){
if (value !== undefined) {
this.apiCall("setAutoplay", value);
this.options.autoplay = value;
return this;
}
return this.apiCall("autoplay", value);
},
loop: function(value){
if (value !== undefined) {
this.apiCall("setLoop", value);
this.options.loop = value;
return this;
}
return this.apiCall("loop", value);
},
controls: function(){ return this.options.controls; },
textTracks: function(){ return this.options.tracks; },
poster: function(){ return this.apiCall("poster"); },
error: function(){ return this.apiCall("error"); },
networkState: function(){ return this.apiCall("networkState"); },
readyState: function(){ return this.apiCall("readyState"); },
seeking: function(){ return this.apiCall("seeking"); },
initialTime: function(){ return this.apiCall("initialTime"); },
startOffsetTime: function(){ return this.apiCall("startOffsetTime"); },
played: function(){ return this.apiCall("played"); },
seekable: function(){ return this.apiCall("seekable"); },
ended: function(){ return this.apiCall("ended"); },
videoTracks: function(){ return this.apiCall("videoTracks"); },
audioTracks: function(){ return this.apiCall("audioTracks"); },
videoWidth: function(){ return this.apiCall("videoWidth"); },
videoHeight: function(){ return this.apiCall("videoHeight"); },
defaultPlaybackRate: function(){ return this.apiCall("defaultPlaybackRate"); },
playbackRate: function(){ return this.apiCall("playbackRate"); },
// mediaGroup: function(){ return this.apiCall("mediaGroup"); },
// controller: function(){ return this.apiCall("controller"); },
controls: function(){ return this.apiCall("controls"); },
defaultMuted: function(){ return this.apiCall("defaultMuted"); }
});

View File

@ -116,9 +116,10 @@ _V_.HTML5 = _V_.PlaybackTech.extend({
currentTime: function(){ return this.el.currentTime; },
setCurrentTime: function(seconds){
try { this.el.currentTime = seconds; }
catch(e) {
_V_.log(e);
try {
this.el.currentTime = seconds;
} catch(e) {
_V_.log(e, "Video isn't ready. (VideoJS)");
// this.warning(VideoJS.warnings.videoNotReady);
}
},

View File

@ -1,431 +0,0 @@
/* Playback Technology - Base class for playback technologies
================================================================================ */
_V_.PlaybackTech = _V_.Component.extend({
init: function(player, options){
// this._super(player, options);
// Make playback element clickable
// _V_.addEvent(this.el, "click", _V_.proxy(this, _V_.PlayToggle.prototype.onClick));
// player.triggerEvent("techready");
},
createElement: function(){},
setupTriggers: function(){},
removeTriggers: function(){},
canPlaySource: function(source){
return _V_[this.name].canPlaySource(source);
}
});
// Create placeholder methods for each that warn when a method
// isn't supported by the current playback technology
_V_.apiMethods = "play,pause,paused,currentTime,setCurrentTime,duration,buffered,volume,setVolume,muted,setMuted,width,height,supportsFullScreen,enterFullScreen,src,load,currentSrc,preload,setPreload,autoplay,setAutoplay,loop,setLoop,error,networkState,readyState,seeking,initialTime,startOffsetTime,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks,defaultPlaybackRate,playbackRate,mediaGroup,controller,controls,defaultMuted".split(",");
_V_.each(_V_.apiMethods, function(methodName){
_V_.PlaybackTech.prototype[methodName] = function(){
throw new Error("The '"+method+"' method is not available on the playback technology's API");
}
});
/* HTML5 Playback Technology - Wrapper for HTML5 Media API
================================================================================ */
_V_.HTML5 = _V_.PlaybackTech.extend({
name: "HTML5",
init: function(player, options, ready){
this.player = player;
this.el = this.createElement();
this.ready(ready);
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.triggerReady();
},
createElement: function(){
var html5 = _V_.HTML5,
player = this.player,
// 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 (html5.supports.movingElementInDOM === false) {
newEl = _V_.createElement("video", {
id: el.id,
className: el.className
});
player.el.removeChild(el);
el = newEl;
_V_.log("here")
_V_.insertFirst(el, player.el);
}
// Update tag settings, in case they were overridden
_V_.each(["autoplay","preload","loop","muted","poster"], function(attr){
el[attr] = player.options[attr];
}, this);
return el;
},
setupTriggers: function(){
// Make video events trigger player events
// May seem verbose here, but makes other APIs possible.
// ["play", "playing", "pause", "ended", "volumechange", "error", "progress", "seeking", "timeupdate"]
var types = _V_.HTML5.events,
i;
for (i = 0;i<types.length; i++) {
_V_.addEvent(this.el, types[i], _V_.proxy(this.player, function(e){
e.stopPropagation();
this.triggerEvent(e);
}));
}
},
removeTriggers: function(){},
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);
// 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(){
<<<<<<< HEAD
if(typeof this.el.webkitEnterFullScreen == 'function') {
=======
if (typeof this.el.webkitEnterFullScreen == 'function') {
>>>>>>> 2f99215f8dae0cac216f8c494f9058b087b39725
// 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;
}
<<<<<<< HEAD
}
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.")
}
}
=======
}
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.")
}
}
>>>>>>> 2f99215f8dae0cac216f8c494f9058b087b39725
},
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
};
_V_.HTML5.supports = {};
// 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 ---------------------------------------------------------- */
// iOS
if (_V_.isIOS()) {
// If you move a video element in the DOM, it breaks video playback.
_V_.HTML5.supports.movingElementInDOM = false;
}
// 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" : "";
};
}
}
/* H5SWF - Custom Flash Player with HTML5-ish API
================================================================================ */
_V_.H5swf = _V_.PlaybackTech.extend({
name: "H5swf",
swf: "flash/video-js.swf",
// swf: "https://s3.amazonaws.com/video-js/3.0b/video-js.swf",
// swf: "http://video-js.zencoder.com/3.0b/video-js.swf",
// swf: "http://video-js.com/test/video-js.swf",
// swf: "http://video-js.com/source/flash/video-js.swf",
// swf: "http://video-js.com/source/flash/video-js.swf",
// swf: "video-js.swf",
init: function(player, options){
this.player = player;
var placeHolder = this.el = _V_.createElement("div", { id: player.el.id + "_temp_h5swf" });
var source = options.source,
objId = player.el.id+"_h5swf_api",
playerOptions = player.options;
flashvars = {
readyFunction: "_V_.H5swf.onSWFReady",
eventProxyFunction: "_V_.H5swf.onSWFEvent",
errorEventProxyFunction: "_V_.H5swf.onSWFErrorEvent",
autoplay: playerOptions.autoplay,
preload: playerOptions.preload,
loop: playerOptions.loop,
muted: playerOptions.muted
},
params = {
allowScriptAccess: "always",
wmode: "opaque",
bgcolor: "#000000"
},
attributes = {
id: objId,
name: objId,
'class': 'vjs-tech'
};
if (playerOptions.poster) {
flashvars.poster = playerOptions.poster;
}
// If source was supplied pass as a flash var.
if (source) {
flashvars.src = source.src;
}
_V_.insertFirst(placeHolder, player.el);
swfobject.embedSWF(options.swf || this.swf, placeHolder.id, "480", "270", "9.0.124", "", flashvars, params, attributes);
},
setupTriggers: function(){
// Using global onSWFEvent func to distribute events
},
play: function(){ this.el.vjs_play(); },
pause: function(){ this.el.vjs_pause(); },
src: function(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_.H5swf.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_.H5swf.isSupported = function(){
return swfobject.hasFlashPlayerVersion("9");
};
_V_.H5swf.canPlaySource = function(srcObj){
if (srcObj.type in _V_.H5swf.supports.format) { return "maybe"; }
};
_V_.H5swf.supports = {
format: {
"video/flv": "FLV",
"video/x-flv": "FLV",
"video/mp4": "MP4",
"video/m4v": "MP4"
},
// Optional events that we can manually mimic with timers
event: {
progress: false,
timeupdate: false
}
};
_V_.H5swf.onSWFReady = function(currSwf){
_V_.log(currSwf, "currSwf")
// Flash seems to be catching errors, so raising them manally
try {
// Delay for real swf ready.
setTimeout(function(){
var el = _V_.el(currSwf);
// Get player from box
var player = el.parentNode.player,
tech = player.techs["H5swf"];
// Reference player on tech element
el.player = player;
// Update reference to playback technology element
tech.el = el;
tech.triggerReady();
},0);
} catch(err) {
_V_.log(err);
}
};
_V_.H5swf.onSWFEvent = function(swfID, eventName, other){
try {
var player = _V_.el(swfID).player;
if (player) {
player.triggerEvent(eventName);
}
} catch(err) {
_V_.log(err);
}
};
_V_.H5swf.onSWFErrorEvent = function(swfID, eventName){
_V_.log("Flash (H5SWF) Error", eventName);
};