1
0
mirror of https://github.com/videojs/video.js.git synced 2025-04-11 11:42:08 +02:00

Merge pull request #1 from heff/Akkuma-master

Updates for videojs/video.js#1093
This commit is contained in:
Gregory Waxman 2014-05-06 11:16:15 -04:00
commit e7617bfdd4
35 changed files with 501 additions and 184 deletions

View File

@ -2,13 +2,31 @@ CHANGELOG
=========
## HEAD (Unreleased)
* Updated the UI to support live video ([view](https://github.com/videojs/video.js/pull/1121))
* The UI now resets after a source change ([view](https://github.com/videojs/video.js/pull/1124))
* Now assuming smart CSS defaults for sliders to prevent reflow on player init ([view](https://github.com/videojs/video.js/pull/1122))
* Fixed the title element placement in menus [[view](https://github.com/videojs/video.js/pull/1114)]
* Fixed title support for menu buttons ([view](https://github.com/videojs/video.js/pull/1128))
* Fixed extra mousemove events on Windows caused by certain apps, not users [[view](https://github.com/videojs/video.js/pull/1068)]
* Fixed error due to undefined tech when no source is supported [[view](https://github.com/videojs/video.js/pull/1172)]
* Fixed the progress bar not finishing when manual timeupdate events are used [[view](https://github.com/videojs/video.js/pull/1173)]
* Added a more informative and styled fallback message for non-html5 browsers [[view](https://github.com/videojs/video.js/pull/1181)]
--------------------
## 4.5.2 (2014-04-12)
* Updated release versioning to include bower.json and component.json
## 4.5.1 (2014-03-27)
* Fixed a bug from the last release where canPlaySource was no longer exported
## 4.5.0 (2014-03-27)
* Added component(1) support ([view](https://github.com/videojs/video.js/pull/1032))
* Captions now move down when controls are hidden ([view](https://github.com/videojs/video.js/pull/1053))
* Added the .less source file to the distribution files ([view](https://github.com/videojs/video.js/pull/1056))
* Changed src() to return the current selected source ([view](https://github.com/videojs/video.js/pull/968))
* Added a grunt task for opening the next issue that needs addressing ([view](https://github.com/videojs/video.js/pull/1059))
--------------------
* Fixed Android 4.0+ devices' check for HLS support ([view](https://github.com/videojs/video.js/pull/1084))
## 4.4.3 (2014-03-06)
* Fixed bugs in IE9 Windows 7N with no Media Player ([view](https://github.com/videojs/video.js/pull/1060))

View File

@ -183,15 +183,24 @@ cp sandbox/index.html.example sandbox/index.html
open sandbox/index.html
```
> #### NOTE: Testing Flash Locally in Chrome
> Chrome 21+ (as of 2013/01/01) doens't run Flash files that are local and loaded into a locally accessed page (file:///).
### Testing Locally
A simple Connect server is available via the Grunt plugin. The commands below will allow you to setup a test sandbox and begin development.
```bash
cp sandbox/index.html.example sandbox/index.html
grunt connect
open http://localhost:9999/sandbox/index.html
```
> NOTES regarding local testing in Chrome 21+ (as of 2013/01/01)
> Flash files that are local and loaded into a locally accessed page (file:///) will NOT run.
> To get around this you can do either of the following:
>
> 1. Do your development and testing using a local HTTP server.
> 1. Do your development and testing using a local HTTP server. See Grunt commands above.
>
> 2. [Disable the version of Flash included with Chrome](http://helpx.adobe.com/flash-player/kb/flash-player-google-chrome.html#How_can_I_run_debugger_or_alternate_versions_of_Flash_Player_in_Google_Chrome) and enable a system-wide version of Flash instead.
Commit and push changes as you go (using git directly). Write thorough descriptions of your changes in your commit messages.
```bash

View File

@ -71,6 +71,14 @@ module.exports = function(grunt) {
files: [ 'src/**/*', 'test/unit/*.js', 'Gruntfile.js' ],
tasks: 'dev'
},
connect: {
dev: {
options: {
port: 9999,
keepalive: true
}
}
},
copy: {
minor: {
files: [
@ -171,9 +179,28 @@ module.exports = function(grunt) {
}
}
},
bump: {
files: ['package.json'],
updateConfigs: ['pkg']
version: {
options: {
pkg: 'package.json'
},
major: {
options: {
release: 'major'
},
src: ['package.json', 'bower.json', 'component.json']
},
minor: {
options: {
release: 'minor'
},
src: ['package.json', 'bower.json', 'component.json']
},
patch: {
options: {
release: 'patch'
},
src: ['package.json', 'bower.json', 'component.json']
}
},
tagrelease: {
file: 'package.json',
@ -183,6 +210,7 @@ module.exports = function(grunt) {
}
});
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
@ -196,7 +224,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('videojs-doc-generator');
grunt.loadNpmTasks('grunt-zip');
grunt.loadNpmTasks('grunt-banner');
grunt.loadNpmTasks('grunt-bump');
grunt.loadNpmTasks('grunt-version');
grunt.loadNpmTasks('grunt-tagrelease');
grunt.loadNpmTasks('chg');

View File

@ -1,9 +1,9 @@
{
"name": "video.js",
"description": "An HTML5 and Flash video player with a common API and skin for both.",
"version": "4.4.3",
"version": "4.5.2",
"main": [
"dist/video-js/video.js",
"dist/video-js/video.js",
"dist/video-js/video-js.css"
],
"keywords": [
@ -13,4 +13,4 @@
"video",
"player"
]
}
}

View File

@ -25,6 +25,7 @@
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
<track kind="captions" src="demo.captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
<track kind="subtitles" src="demo.captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video>
</body>

View File

@ -26,6 +26,7 @@ var sourceFiles = [
"src/js/menu.js",
"src/js/player.js",
"src/js/control-bar/control-bar.js",
"src/js/control-bar/live-display.js",
"src/js/control-bar/play-toggle.js",
"src/js/control-bar/time-display.js",
"src/js/control-bar/fullscreen-toggle.js",

View File

@ -1,7 +1,7 @@
{
"name": "video.js",
"description": "An HTML5 and Flash video player with a common API and skin for both.",
"version": "4.4.2",
"version": "4.5.2",
"keywords": [
"videojs",
"html5",

View File

@ -257,9 +257,9 @@
"prompt": "confirm"
},
{
"id": "type",
"desc": "Provide the release type",
"prompt": {
"id": "type",
"message": "release type",
"default": "patch",
"type": "text"
@ -278,8 +278,8 @@
"exec": "grunt chg-release:<%= type %>"
},
{
"desc": "Bump the package version",
"exec": "grunt bump-only:<%= type %>"
"desc": "Bump package versions",
"exec": "grunt version:<%= type %>"
},
{
"desc": "Build the release",

View File

@ -234,7 +234,7 @@ _inherited from_: [src/js/component.js#L224](https://github.com/videojs/video.js
##### RETURNS:
* `Boolean` Controls are showing
_defined in_: [src/js/player.js#L1167](https://github.com/videojs/video.js/blob/master/src/js/player.js#L1167)
_defined in_: [src/js/player.js#L1174](https://github.com/videojs/video.js/blob/master/src/js/player.js#L1174)
---
@ -673,7 +673,7 @@ _inherited from_: [src/js/component.js#L120](https://github.com/videojs/video.js
* `String` poster URL when getting
* `vjs.Player` self when setting
_defined in_: [src/js/player.js#L1140](https://github.com/videojs/video.js/blob/master/src/js/player.js#L1140)
_defined in_: [src/js/player.js#L1147](https://github.com/videojs/video.js/blob/master/src/js/player.js#L1147)
---
@ -778,9 +778,10 @@ _inherited from_: [src/js/component.js#L653](https://github.com/videojs/video.js
* __source__ `String|Object|Array` _(OPTIONAL)_ The source URL, object, or array of sources
##### RETURNS:
* `vjs.Player` self
* `String` The current video source when getting
* `String` The player when setting
_defined in_: [src/js/player.js#L1024](https://github.com/videojs/video.js/blob/master/src/js/player.js#L1024)
_defined in_: [src/js/player.js#L1025](https://github.com/videojs/video.js/blob/master/src/js/player.js#L1025)
---

View File

@ -20,8 +20,8 @@ You can download the Video.js source and host it on your own servers, or use the
### CDN Version ###
```html
<link href="//vjs.zencdn.net/4.4/video-js.css" rel="stylesheet">
<script src="//vjs.zencdn.net/4.4/video.js"></script>
<link href="//vjs.zencdn.net/4.5/video-js.css" rel="stylesheet">
<script src="//vjs.zencdn.net/4.5/video.js"></script>
```
### Self Hosted. ###
@ -57,6 +57,7 @@ Otherwise include/exclude attributes, settings, sources, and tracks exactly as y
<source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' />
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' />
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' />
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video>
```
@ -89,13 +90,13 @@ The third argument is a 'ready' callback. Once Video.js has initialized it will
Instead of using an element ID, you can also pass a reference to the element itself.
```js
videojs(document.getElementsById('example_video_1')), {}, function()) {
videojs(document.getElementById('example_video_1'), {}, function() {
// This is functionally the same as the previous example.
});
```
```js
videojs(document.getElementsByClassName('awesome_video_class')[0], {}, function()) {
videojs(document.getElementsByClassName('awesome_video_class')[0], {}, function() {
// You can grab an element by class if you'd like, just make sure
// if it's an array that you pick one (here we chose the first).
});

View File

@ -1,7 +1,7 @@
{
"name": "video.js",
"description": "An HTML5 and Flash video player with a common API and skin for both.",
"version": "4.4.3",
"version": "4.5.2",
"copyright": "Copyright 2014 Brightcove, Inc. https://github.com/videojs/video.js/blob/master/LICENSE",
"keywords": [
"videojs",
@ -26,6 +26,7 @@
"devDependencies": {
"grunt-cli": "~0.1.0",
"grunt": "~0.4",
"grunt-contrib-connect": "~0.7.1",
"grunt-contrib-jshint": "~0.4.3",
"grunt-contrib-watch": "~0.1.4",
"grunt-contrib-qunit": "~0.2.1",
@ -53,9 +54,9 @@
"grunt-zip": "0.10.2",
"grunt-banner": "~0.2.0",
"chg": "~0.1.8",
"grunt-bump": "0.0.13",
"grunt-tagrelease": "~0.3.3",
"github": "~0.1.14",
"open": "0.0.4"
"open": "0.0.4",
"grunt-version": "~0.3.0"
}
}

View File

@ -25,7 +25,7 @@
<source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm'>
<source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg'>
<track kind="captions" src="../build/demo-files/demo.captions.vtt" srclang="en" label="English"></track><!-- Tracks need an ending tag thanks to IE9 -->
<p>Video Playback Not Supported</p>
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
</video>
<script>

View File

@ -303,6 +303,8 @@ fonts to show/hide properly.
top: 0;
left: 0;
height: 0.5em;
/* assuming volume starts at 1.0 */
width: 100%;
background: @slider-bar-color
url(@slider-bar-pattern)
@ -311,6 +313,10 @@ fonts to show/hide properly.
.vjs-default-skin .vjs-volume-bar .vjs-volume-handle {
width: 0.5em;
height: 0.5em;
/* Assumes volume starts at 1.0. If you change the size of the
handle relative to the volume bar, you'll need to update this value
too. */
left: 4.5em;
}
.vjs-default-skin .vjs-volume-handle:before {
@ -368,6 +374,8 @@ fonts to show/hide properly.
height: 100%;
margin: 0;
padding: 0;
/* updated by javascript during playback */
width: 0;
/* Needed for IE6 *///
left: 0;
top: 0;
@ -400,6 +408,27 @@ fonts to show/hide properly.
padding-top: 0.1em /* Minor adjustment */;
}
/* Live Mode
--------------------------------------------------------------------------------
*/
.vjs-default-skin.vjs-live .vjs-time-controls,
.vjs-default-skin.vjs-live .vjs-time-divider,
.vjs-default-skin.vjs-live .vjs-progress-control {
display: none;
}
.vjs-default-skin.vjs-live .vjs-live-display {
display: block;
}
/* Live Display
--------------------------------------------------------------------------------
*/
.vjs-default-skin .vjs-live-display {
display: none;
font-size: 1em;
line-height: 3em;
}
/* Time Display
--------------------------------------------------------------------------------
*/
@ -798,6 +827,26 @@ body.vjs-full-window {
visibility: visible;
}
/* In IE8 w/ no JavaScript (no HTML5 shim), the video tag doesn't register.
The .video-js classname on the video tag also isn't considered.
This optional paragraph inside the video tag can provide a message to users
about what's required to play video. */
.vjs-no-js {
padding: 20px;
color: #ccc;
background-color: #333;
font-size: 18px;
font-family: Arial, sans-serif;
text-align: center;
width: 300px;
height: 150px;
margin: 0px auto;
}
.vjs-no-js a, .vjs-no-js a:visited {
color: #F4A460;
}
// MIXINS
// =============================================================================
// Mixins are a LESS feature and are used to add vendor prefixes to CSS rules

View File

@ -439,37 +439,62 @@ vjs.Component.prototype.removeChild = function(component){
* myChildOption: true
* }
* }
*
* // Or when creating the component
* var myComp = new MyComponent(player, {
* children: {
* myChildComponent: {
* myChildOption: true
* }
* }
* });
*
* The children option can also be an Array of child names or
* child options objects (that also include a 'name' key).
*
* var myComp = new MyComponent(player, {
* children: [
* 'button',
* {
* name: 'button',
* someOtherOption: true
* }
* ]
* });
*
*/
vjs.Component.prototype.initChildren = function(){
var options = this.options_;
var parent, children, child, name, opts;
if (options && options['children']) {
var self = this;
parent = this;
children = this.options()['children'];
// Loop through components and add them to the player
vjs.each(options['children'], function(name, opts){
//Support for a simpler setup, children with no options
if (typeof name == 'number') {
name = opts;
opts = {};
if (children) {
// Allow for an array of children details to passed in the options
if (children instanceof Array) {
for (var i = 0; i < children.length; i++) {
child = children[i];
if (typeof child == 'string') {
name = child;
opts = {};
} else {
name = child.name;
opts = child;
}
parent[name] = parent.addChild(name, opts);
}
} else {
vjs.obj.each(children, function(name, opts){
// Allow for disabling default components
// e.g. vjs.options['children']['posterImage'] = false
if (opts === false) return;
// Allow for disabling default components
// e.g. vjs.options['children']['posterImage'] = false
if (opts === false) return;
// Allow waiting to add components until a specific event is called
var tempAdd = function(){
// Set property name on player. Could cause conflicts with other prop names, but it's worth making refs easy.
self[name] = self.addChild(name, opts);
};
if (opts['loadEvent']) {
// this.one(opts.loadEvent, tempAdd)
} else {
tempAdd();
}
});
parent[name] = parent.addChild(name, opts);
});
}
}
};

View File

@ -16,6 +16,7 @@ vjs.ControlBar.prototype.options_ = {
'timeDivider': {},
'durationDisplay': {},
'remainingTimeDisplay': {},
'liveDisplay': {},
'progressControl': {},
'fullscreenToggle': {},
'volumeControl': {},

View File

@ -0,0 +1,28 @@
/**
* Displays the live indicator
* TODO - Future make it click to snap to live
* @param {vjs.Player|Object} player
* @param {Object=} options
* @constructor
*/
vjs.LiveDisplay = vjs.Component.extend({
init: function(player, options){
vjs.Component.call(this, player, options);
}
});
vjs.LiveDisplay.prototype.createEl = function(){
var el = vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-live-controls vjs-control'
});
this.contentEl_ = vjs.createEl('div', {
className: 'vjs-live-display',
innerHTML: '<span class="vjs-control-text">Stream Type </span>LIVE',
'aria-live': 'off'
});
el.appendChild(this.contentEl_);
return el;
};

View File

@ -49,7 +49,6 @@ vjs.VolumeBar = vjs.Slider.extend({
vjs.Slider.call(this, player, options);
player.on('volumechange', vjs.bind(this, this.updateARIAAttributes));
player.ready(vjs.bind(this, this.updateARIAAttributes));
setTimeout(vjs.bind(this, this.update), 0); // update when elements is in DOM
}
});

View File

@ -206,9 +206,11 @@ vjs.fixEvent = function(event) {
}
event.returnValue = false;
event.isDefaultPrevented = returnTrue;
event.defaultPrevented = true;
};
event.isDefaultPrevented = returnFalse;
event.defaultPrevented = false;
// Stop the event from bubbling
event.stopPropagation = function () {
@ -293,7 +295,7 @@ vjs.trigger = function(elem, event) {
vjs.trigger(parent, event);
// If at the top of the DOM, triggers the default action unless disabled.
} else if (!parent && !event.isDefaultPrevented()) {
} else if (!parent && !event.defaultPrevented) {
var targetData = vjs.getData(event.target);
// Checks if the target has a default action for this event.
@ -310,7 +312,7 @@ vjs.trigger = function(elem, event) {
}
// Inform the triggerer if the default was prevented by returning false
return !event.isDefaultPrevented();
return !event.defaultPrevented;
/* Original version of js ninja events wasn't complete.
* We've since updated to the latest version, but keeping this around
* for now just in case.

View File

@ -84,6 +84,7 @@ goog.exportSymbol('videojs.CurrentTimeDisplay', vjs.CurrentTimeDisplay);
goog.exportSymbol('videojs.DurationDisplay', vjs.DurationDisplay);
goog.exportSymbol('videojs.TimeDivider', vjs.TimeDivider);
goog.exportSymbol('videojs.RemainingTimeDisplay', vjs.RemainingTimeDisplay);
goog.exportSymbol('videojs.LiveDisplay', vjs.LiveDisplay);
goog.exportSymbol('videojs.Slider', vjs.Slider);
goog.exportSymbol('videojs.ProgressControl', vjs.ProgressControl);
goog.exportSymbol('videojs.SeekBar', vjs.SeekBar);
@ -121,6 +122,8 @@ goog.exportSymbol('videojs.Html5', vjs.Html5);
goog.exportProperty(vjs.Html5, 'Events', vjs.Html5.Events);
goog.exportProperty(vjs.Html5, 'isSupported', vjs.Html5.isSupported);
goog.exportProperty(vjs.Html5, 'canPlaySource', vjs.Html5.canPlaySource);
goog.exportProperty(vjs.Html5, 'patchCanPlayType', vjs.Html5.patchCanPlayType);
goog.exportProperty(vjs.Html5, 'unpatchCanPlayType', vjs.Html5.unpatchCanPlayType);
// Export non-standard HTML5 video API methods.
// Standard method names already protected by default externs.

View File

@ -44,26 +44,6 @@ vjs.capitalize = function(string){
return string.charAt(0).toUpperCase() + string.slice(1);
};
/**
* Loop through an array, an array of objects, or each property
* in an object and call a function whose arguments are (key,value)
* @param {Object} arrLike Object of properties
* @param {Function} fn Function to be called on each property.
* @this {*}
* @private
*/
vjs.each = function (arrLike, fn, context) {
if (vjs.obj.isPlain(arrLike)) vjs.obj.each(arrLike, fn, context);
else {
for (var i = 0, len = arrLike.length; i < len; ++i) {
var val = arrLike[i];
if (vjs.obj.isPlain(val)) vjs.obj.each(val, fn, context);
else fn.call(context || this, i, val);
}
}
};
/**
* Object functions container
* @type {Object}
@ -713,8 +693,9 @@ vjs.findPosition = function(el) {
scrollTop = window.pageYOffset || body.scrollTop;
top = box.top + scrollTop - clientTop;
// Android sometimes returns slightly off decimal values, so need to round
return {
left: left,
top: top
left: vjs.round(left),
top: vjs.round(top)
};
};
};

View File

@ -284,8 +284,8 @@ vjs.Flash.prototype.currentSrc = function(){
var src = this.el_.vjs_getProperty('currentSrc');
// no src, check and see if RTMP
if (src == null) {
var connection = this.rtmpConnection(),
stream = this.rtmpStream();
var connection = this['rtmpConnection'](),
stream = this['rtmpStream']();
if (connection && stream) {
src = vjs.Flash.streamFromParts(connection, stream);

View File

@ -266,6 +266,53 @@ vjs.Html5.canControlVolume = function(){
return volume !== vjs.TEST_VID.volume;
};
// HTML5 Feature detection and Device Fixes --------------------------------- //
(function() {
var canPlayType,
mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i,
mp4RE = /^video\/mp4/i;
vjs.Html5.patchCanPlayType = function() {
// Android 4.0 and above can play HLS to some extent but it reports being unable to do so
if (vjs.ANDROID_VERSION >= 4.0) {
if (!canPlayType) {
canPlayType = vjs.TEST_VID.constructor.prototype.canPlayType;
}
vjs.TEST_VID.constructor.prototype.canPlayType = function(type) {
if (type && mpegurlRE.test(type)) {
return 'maybe';
}
return canPlayType.call(this, type);
};
}
// Override Android 2.2 and less canPlayType method which is broken
if (vjs.IS_OLD_ANDROID) {
if (!canPlayType) {
canPlayType = vjs.TEST_VID.constructor.prototype.canPlayType;
}
vjs.TEST_VID.constructor.prototype.canPlayType = function(type){
if (type && mp4RE.test(type)) {
return 'maybe';
}
return canPlayType.call(this, type);
};
}
};
vjs.Html5.unpatchCanPlayType = function() {
var r = vjs.TEST_VID.constructor.prototype.canPlayType;
vjs.TEST_VID.constructor.prototype.canPlayType = canPlayType;
canPlayType = null;
return r;
};
// by default, patch the video element
vjs.Html5.patchCanPlayType();
})();
// List of all HTML5 events (various uses).
vjs.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(',');
@ -300,12 +347,3 @@ vjs.Html5.disposeMediaElement = function(el){
})();
}
};
// HTML5 Feature detection and Device Fixes --------------------------------- //
// Override Android 2.2 and less canPlayType method which is broken
if (vjs.IS_OLD_ANDROID) {
document.createElement('video').constructor.prototype.canPlayType = function(type){
return (type && type.toLowerCase().indexOf('video/mp4') != -1) ? 'maybe' : '';
};
}

View File

@ -129,9 +129,9 @@ vjs.MenuButton.prototype.createMenu = function(){
// Add a title list item to the top
if (this.options().title) {
menu.el().appendChild(vjs.createEl('li', {
menu.contentEl().appendChild(vjs.createEl('li', {
className: 'vjs-menu-title',
innerHTML: vjs.capitalize(this.kind_),
innerHTML: vjs.capitalize(this.options().title),
tabindex: -1
}));
}

View File

@ -77,20 +77,7 @@ vjs.Player = vjs.Component.extend({
// this.addClass('vjs-touch-enabled');
// }
// Firstplay event implimentation. Not sold on the event yet.
// Could probably just check currentTime==0?
this.one('play', function(e){
var fpEvent = { type: 'firstplay', target: this.el_ };
// Using vjs.trigger so we can check if default was prevented
var keepGoing = vjs.trigger(this.el_, fpEvent);
if (!keepGoing) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
});
this.on('loadstart', this.onLoadStart);
this.on('ended', this.onEnded);
this.on('play', this.onPlay);
this.on('firstplay', this.onFirstPlay);
@ -335,14 +322,16 @@ vjs.Player.prototype.manualProgressOn = function(){
// 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.
// As opposed to doing user agent detection
this.tech.one('progress', function(){
if (this.tech) {
this.tech.one('progress', function(){
// Update known progress support for this playback technology
this.features['progressEvents'] = true;
// Update known progress support for this playback technology
this.features['progressEvents'] = true;
// Turn off manual progress tracking
this.player_.manualProgressOff();
});
// Turn off manual progress tracking
this.player_.manualProgressOff();
});
}
};
vjs.Player.prototype.manualProgressOff = function(){
@ -375,12 +364,14 @@ vjs.Player.prototype.manualTimeUpdatesOn = function(){
// timeupdate is also called by .currentTime whenever current time is set
// Watch for native timeupdate event
this.tech.one('timeupdate', function(){
// Update known progress support for this playback technology
this.features['timeupdateEvents'] = true;
// Turn off manual progress tracking
this.player_.manualTimeUpdatesOff();
});
if (this.tech) {
this.tech.one('timeupdate', function(){
// Update known progress support for this playback technology
this.features['timeupdateEvents'] = true;
// Turn off manual progress tracking
this.player_.manualTimeUpdatesOff();
});
}
};
vjs.Player.prototype.manualTimeUpdatesOff = function(){
@ -398,8 +389,13 @@ vjs.Player.prototype.trackCurrentTime = function(){
};
// Turn off play progress tracking (when paused or dragging)
vjs.Player.prototype.stopTrackingCurrentTime = function(){ clearInterval(this.currentTimeInterval); };
vjs.Player.prototype.stopTrackingCurrentTime = function(){
clearInterval(this.currentTimeInterval);
// #1002 - if the video ends right before the next timeupdate would happen,
// the progress bar won't make it all the way to the end
this.trigger('timeupdate');
};
// /* Player event handlers (how the player reacts to certain events)
// ================================================================================ */
@ -407,7 +403,27 @@ vjs.Player.prototype.stopTrackingCurrentTime = function(){ clearInterval(this.cu
* Fired when the user agent begins looking for media data
* @event loadstart
*/
vjs.Player.prototype.onLoadStart;
vjs.Player.prototype.onLoadStart = function() {
// remove any first play listeners that weren't triggered from a previous video.
this.off('play', initFirstPlay);
this.one('play', initFirstPlay);
vjs.removeClass(this.el_, 'vjs-has-started');
};
// Need to create this outside the scope of onLoadStart so it
// can be added and removed (to avoid piling first play listeners).
function initFirstPlay(e) {
var fpEvent = { type: 'firstplay', target: this.el_ };
// Using vjs.trigger so we can check if default was prevented
var keepGoing = vjs.trigger(this.el_, fpEvent);
if (!keepGoing) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
}
/**
* Fired when the player has initial duration and dimension information
@ -505,7 +521,16 @@ vjs.Player.prototype.onDurationChange = function(){
// accidentally cause the stack to blow up.
var duration = this.techGet('duration');
if (duration) {
if (duration < 0) {
duration = Infinity;
}
this.duration(duration);
// Determine if the stream is live and propagate styles down to UI.
if (duration === Infinity) {
this.addClass('vjs-live');
} else {
this.removeClass('vjs-live');
}
}
};
@ -1298,13 +1323,23 @@ vjs.Player.prototype.userActive = function(bool){
};
vjs.Player.prototype.listenForUserActivity = function(){
var onMouseActivity, onMouseDown, mouseInProgress, onMouseUp,
activityCheck, inactivityTimeout;
var onActivity, onMouseMove, onMouseDown, mouseInProgress, onMouseUp,
activityCheck, inactivityTimeout, lastMoveX, lastMoveY;
onMouseActivity = vjs.bind(this, this.reportUserActivity);
onActivity = vjs.bind(this, this.reportUserActivity);
onMouseMove = function(e) {
// #1068 - Prevent mousemove spamming
// Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
if(e.screenX != lastMoveX || e.screenY != lastMoveY) {
lastMoveX = e.screenX;
lastMoveY = e.screenY;
onActivity();
}
};
onMouseDown = function() {
onMouseActivity();
onActivity();
// For as long as the they are touching the device or have their mouse down,
// we consider them active even if they're not moving their finger or mouse.
// So we want to continue to update that they are active
@ -1312,24 +1347,24 @@ vjs.Player.prototype.listenForUserActivity = function(){
// Setting userActivity=true now and setting the interval to the same time
// as the activityCheck interval (250) should ensure we never miss the
// next activityCheck
mouseInProgress = setInterval(onMouseActivity, 250);
mouseInProgress = setInterval(onActivity, 250);
};
onMouseUp = function(event) {
onMouseActivity();
onActivity();
// Stop the interval that maintains activity if the mouse/touch is down
clearInterval(mouseInProgress);
};
// Any mouse movement will be considered user activity
this.on('mousedown', onMouseDown);
this.on('mousemove', onMouseActivity);
this.on('mousemove', onMouseMove);
this.on('mouseup', onMouseUp);
// Listen for keyboard navigation
// Shouldn't need to use inProgress interval because of key repeat
this.on('keydown', onMouseActivity);
this.on('keyup', onMouseActivity);
this.on('keydown', onActivity);
this.on('keyup', onActivity);
// Run an interval every 250 milliseconds instead of stuffing everything into
// the mousemove/touchmove function itself, to prevent performance degradation.

View File

@ -16,8 +16,6 @@ vjs.Slider = vjs.Component.extend({
this.bar = this.getChild(this.options_['barName']);
this.handle = this.getChild(this.options_['handleName']);
player.on(this.playerEvent, vjs.bind(this, this.update));
this.on('mousedown', this.onMouseDown);
this.on('touchstart', this.onMouseDown);
this.on('focus', this.onFocus);
@ -26,10 +24,7 @@ vjs.Slider = vjs.Component.extend({
this.player_.on('controlsvisible', vjs.bind(this, this.update));
// This is actually to fix the volume handle position. http://twitter.com/#!/gerritvanaaken/status/159046254519787520
// this.player_.one('timeupdate', vjs.bind(this, this.update));
player.ready(vjs.bind(this, this.update));
player.on(this.playerEvent, vjs.bind(this, this.update));
this.boundEvents = {};
}

View File

@ -948,7 +948,7 @@ vjs.ChaptersButton.prototype.createMenu = function(){
var menu = this.menu = new vjs.Menu(this.player_);
menu.el_.appendChild(vjs.createEl('li', {
menu.contentEl().appendChild(vjs.createEl('li', {
className: 'vjs-menu-title',
innerHTML: vjs.capitalize(this.kind_),
tabindex: -1

View File

@ -33,7 +33,9 @@
'test/unit/poster.js',
'test/unit/plugins.js',
'test/unit/flash.js',
'test/unit/api.js'
'test/unit/api.js',
'test/unit/menu.js',
'test/unit/tracks.js'
];
var projectRoot = '../';

View File

@ -91,6 +91,12 @@ test('should be able to access expected MediaTech API methods', function() {
ok(techProto.setPoster, 'setPoster should exist on the Media tech');
ok(html5Proto.setPoster, 'setPoster should exist on the HTML5 tech');
ok(flashProto.setPoster, 'setPoster should exist on the Flash tech');
ok(videojs.Html5.patchCanPlayType, 'patchCanPlayType should exist for HTML5');
ok(videojs.Html5.unpatchCanPlayType, 'unpatchCanPlayType should exist for HTML5');
ok(videojs.Html5.canPlaySource, 'canPlaySource should exist for HTML5');
ok(videojs.Flash.canPlaySource, 'canPlaySource should exist for Flash');
});
test('should export ready api call to public', function() {

View File

@ -53,9 +53,9 @@ test('should init child components from simple children array', function(){
test('should init child components from children array of objects', function(){
var comp = new vjs.Component(getFakePlayer(), {
children: [
{'component':{}},
{'component':{}},
{'component':{}}
{ 'name': 'component' },
{ 'name': 'component' },
{ 'name': 'component' }
]
});

View File

@ -114,3 +114,20 @@ test('should bubble up DOM unless bubbles == false', function(){
});
vjs.trigger(inner, { type:'nobub', target:inner, bubbles:false });
});
test('should have a defaultPrevented property on an event that was prevent from doing default action', function() {
expect(2);
var el = document.createElement('div');
vjs.on(el, 'test', function(e){
ok(true, 'First listener fired');
e.preventDefault();
});
vjs.on(el, 'test', function(e){
ok(e.defaultPrevented, 'Should have `defaultPrevented` to signify preventDefault being called');
});
vjs.trigger(el, 'test');
});

View File

@ -22,47 +22,13 @@ test('should loop through each property on an object', function(){
};
// Add 3 to each value
vjs.each(asdf, function(key, value){
vjs.obj.each(asdf, function(key, value){
asdf[key] = value + 3;
});
deepEqual(asdf,{a:4,b:5,'c':6});
});
test('should loop through simple array', function(){
var asdf = [
'a',
'b',
'c'
];
var newArr = [];
vjs.each(asdf, function(key, value){
newArr[key] = value;
ok(typeof key == 'number', 'Key is not a number, the array index');
ok(typeof value == 'string', 'Value is not a string');
});
deepEqual(asdf,newArr);
});
test('should loop through an array of objects', function(){
var asdf = [
{ a: {} },
{ b: {} },
{ 'c': {} }
];
var newObj = {};
vjs.each(asdf, function(key, value){
newObj[key] = value;
ok(typeof key == 'string', 'Key is not a string');
ok(vjs.obj.isPlain(value), 'Value is not an object');
});
deepEqual(newObj, { a: {}, b: {}, 'c': {}});
});
test('should copy an object', function(){
var asdf = {
a: 1,

View File

@ -38,3 +38,57 @@ test('should re-link the player if the tech is moved', function(){
strictEqual(player, tech.el()['player']);
});
test('patchCanPlayType patches canplaytype with our function, conditionally', function() {
// the patch runs automatically so we need to first unpatch
vjs.Html5.unpatchCanPlayType();
var oldAV = vjs.ANDROID_VERSION,
video = document.createElement('video'),
canPlayType = vjs.TEST_VID.constructor.prototype.canPlayType,
patchedCanPlayType,
unpatchedCanPlayType;
vjs.ANDROID_VERSION = 4.0;
vjs.Html5.patchCanPlayType();
notStrictEqual(video.canPlayType, canPlayType, 'original canPlayType and patched canPlayType should not be equal');
patchedCanPlayType = video.canPlayType;
unpatchedCanPlayType = vjs.Html5.unpatchCanPlayType();
strictEqual(canPlayType, vjs.TEST_VID.constructor.prototype.canPlayType, 'original canPlayType and unpatched canPlayType should be equal');
strictEqual(patchedCanPlayType, unpatchedCanPlayType, 'patched canPlayType and function returned from unpatch are equal');
vjs.ANDROID_VERSION = oldAV;
vjs.Html5.unpatchCanPlayType();
});
test('should return maybe for HLS urls on Android 4.0 or above', function() {
var oldAV = vjs.ANDROID_VERSION,
video = document.createElement('video');
vjs.ANDROID_VERSION = 4.0;
vjs.Html5.patchCanPlayType();
strictEqual(video.canPlayType('application/x-mpegurl'), 'maybe', 'android version 4.0 or above should be a maybe for x-mpegurl');
strictEqual(video.canPlayType('application/x-mpegURL'), 'maybe', 'android version 4.0 or above should be a maybe for x-mpegURL');
strictEqual(video.canPlayType('application/vnd.apple.mpegurl'), 'maybe', 'android version 4.0 or above should be a maybe for vnd.apple.mpegurl');
strictEqual(video.canPlayType('application/vnd.apple.mpegURL'), 'maybe', 'android version 4.0 or above should be a maybe for vnd.apple.mpegurl');
vjs.ANDROID_VERSION = oldAV;
vjs.Html5.unpatchCanPlayType();
});
test('should return a maybe for mp4 on OLD ANDROID', function() {
var isOldAndroid = vjs.IS_OLD_ANDROID,
video = document.createElement('video');
vjs.IS_OLD_ANDROID = true;
vjs.Html5.patchCanPlayType();
strictEqual(video.canPlayType('video/mp4'), 'maybe', 'old android should return a maybe for video/mp4');
vjs.IS_OLD_ANDROID = isOldAndroid;
vjs.Html5.unpatchCanPlayType();
});

18
test/unit/menu.js Normal file
View File

@ -0,0 +1,18 @@
module('MenuButton');
test('should place title list item into ul', function() {
var player, menuButton;
player = PlayerTest.makePlayer();
menuButton = new vjs.MenuButton(player, {
'title': 'testTitle'
});
var menuContentElement = menuButton.el().getElementsByTagName('UL')[0];
var titleElement = menuContentElement.children[0];
ok(titleElement.innerHTML === 'TestTitle', 'title element placed in ul');
player.dispose();
});

View File

@ -357,15 +357,6 @@ test('should use custom message when encountering an unsupported video type',
player.dispose();
});
test('should return the player when setting src', function() {
var player, ret;
player = PlayerTest.makePlayer({}),
ret = player.src('foo');
equal(player, ret, 'the player is returned');
});
test('should register players with generated ids', function(){
var fixture, video, player, id;
fixture = document.getElementById('qunit-fixture');
@ -380,3 +371,34 @@ test('should register players with generated ids', function(){
equal(player.el().id, player.id(), 'the player and element ids are equal');
ok(vjs.players[id], 'the generated id is registered');
});
test('should not add multiple first play events despite subsequent loads', function() {
expect(1);
var player = PlayerTest.makePlayer({});
player.on('firstplay', function(){
ok('First play should fire once.');
});
// Checking to make sure onLoadStart removes first play listener before adding a new one.
player.trigger('loadstart');
player.trigger('loadstart');
player.trigger('play');
});
test('should remove vjs-has-started class', function(){
expect(3);
var player = PlayerTest.makePlayer({});
player.trigger('loadstart');
player.trigger('play');
ok(player.el().className.indexOf('vjs-has-started') !== -1, 'vjs-has-started class added');
player.trigger('loadstart');
ok(player.el().className.indexOf('vjs-has-started') === -1, 'vjs-has-started class removed');
player.trigger('play');
ok(player.el().className.indexOf('vjs-has-started') !== -1, 'vjs-has-started class added again');
});

16
test/unit/tracks.js Normal file
View File

@ -0,0 +1,16 @@
module('Tracks');
test('should place title list item into ul', function() {
var player, chaptersButton;
player = PlayerTest.makePlayer();
chaptersButton = new vjs.ChaptersButton(player);
var menuContentElement = chaptersButton.el().getElementsByTagName('UL')[0];
var titleElement = menuContentElement.children[0];
ok(titleElement.innerHTML === 'Chapters', 'title element placed in ul');
player.dispose();
});