forked from videojs/video.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmedia.js
186 lines (160 loc) · 6.74 KB
/
media.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/**
* @fileoverview Media Technology Controller - Base class for media playback
* technology controllers like Flash and HTML5
*/
/**
* Base class for media (HTML5 Video, Flash) controllers
* @param {vjs.Player|Object} player Central player instance
* @param {Object=} options Options object
* @constructor
*/
vjs.MediaTechController = vjs.Component.extend({
/** @constructor */
init: function(player, options, ready){
vjs.Component.call(this, player, options, ready);
this.initControlsListeners();
}
});
/**
* Set up click and touch listeners for the playback element
* On desktops, a click on the video itself will toggle playback,
* on a mobile device a click on the video toggles controls.
* (toggling controls is done by toggling the user state between active and
* inactive)
*
* A tap can signal that a user has become active, or has become inactive
* e.g. a quick tap on an iPhone movie should reveal the controls. Another
* quick tap should hide them again (signaling the user is in an inactive
* viewing state)
*
* In addition to this, we still want the user to be considered inactive after
* a few seconds of inactivity.
*
* Note: the only part of iOS interaction we can't mimic with this setup
* is a touch and hold on the video element counting as activity in order to
* keep the controls showing, but that shouldn't be an issue. A touch and hold on
* any controls will still keep the user active
*/
vjs.MediaTechController.prototype.initControlsListeners = function(){
var player, tech, activateControls, deactivateControls;
tech = this;
player = this.player();
var activateControls = function(){
if (player.controls() && !player.usingNativeControls()) {
tech.addControlsListeners();
}
};
deactivateControls = vjs.bind(tech, tech.removeControlsListeners);
// Set up event listeners once the tech is ready and has an element to apply
// listeners to
this.ready(activateControls);
player.on('controlsenabled', activateControls);
player.on('controlsdisabled', deactivateControls);
};
vjs.MediaTechController.prototype.addControlsListeners = function(){
var preventBubble, userWasActive;
// Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
// trigger mousedown/up.
// http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
// Any touch events are set to block the mousedown event from happening
this.on('mousedown', this.onClick);
// We need to block touch events on the video element from bubbling up,
// otherwise they'll signal activity prematurely. The specific use case is
// when the video is playing and the controls have faded out. In this case
// only a tap (fast touch) should toggle the user active state and turn the
// controls back on. A touch and move or touch and hold should not trigger
// the controls (per iOS as an example at least)
//
// We always want to stop propagation on touchstart because touchstart
// at the player level starts the touchInProgress interval. We can still
// report activity on the other events, but won't let them bubble for
// consistency. We don't want to bubble a touchend without a touchstart.
this.on('touchstart', function(event) {
// Stop the mouse events from also happening
event.preventDefault();
event.stopPropagation();
// Record if the user was active now so we don't have to keep polling it
userWasActive = this.player_.userActive();
});
preventBubble = function(event){
event.stopPropagation();
if (userWasActive) {
this.player_.reportUserActivity();
}
};
// Treat all touch events the same for consistency
this.on('touchmove', preventBubble);
this.on('touchleave', preventBubble);
this.on('touchcancel', preventBubble);
this.on('touchend', preventBubble);
// Turn on component tap events
this.emitTapEvents();
// The tap listener needs to come after the touchend listener because the tap
// listener cancels out any reportedUserActivity when setting userActive(false)
this.on('tap', this.onTap);
};
/**
* Remove the listeners used for click and tap controls. This is needed for
* toggling to controls disabled, where a tap/touch should do nothing.
*/
vjs.MediaTechController.prototype.removeControlsListeners = function(){
// We don't want to just use `this.off()` because there might be other needed
// listeners added by techs that extend this.
this.off('tap');
this.off('touchstart');
this.off('touchmove');
this.off('touchleave');
this.off('touchcancel');
this.off('touchend');
this.off('click');
this.off('mousedown');
};
/**
* Handle a click on the media element. By default will play/pause the media.
*/
vjs.MediaTechController.prototype.onClick = function(event){
// We're using mousedown to detect clicks thanks to Flash, but mousedown
// will also be triggered with right-clicks, so we need to prevent that
if (event.button !== 0) return;
// When controls are disabled a click should not toggle playback because
// the click is considered a control
if (this.player().controls()) {
if (this.player().paused()) {
this.player().play();
} else {
this.player().pause();
}
}
};
/**
* Handle a tap on the media element. By default it will toggle the user
* activity state, which hides and shows the controls.
*/
vjs.MediaTechController.prototype.onTap = function(){
this.player().userActive(!this.player().userActive());
};
vjs.MediaTechController.prototype.features = {
'volumeControl': true,
// Resizing plugins using request fullscreen reloads the plugin
'fullscreenResize': false,
// Optional events that we can manually mimic with timers
// currently not triggered by video-js-swf
'progressEvents': false,
'timeupdateEvents': false
};
vjs.media = {};
/**
* List of default API methods for any MediaTechController
* @type {String}
*/
vjs.media.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(',');
// Create placeholder methods for each that warn when a method isn't supported by the current playback technology
function createMethod(methodName){
return function(){
throw new Error('The "'+methodName+'" method is not available on the playback technology\'s API');
};
}
for (var i = vjs.media.ApiMethods.length - 1; i >= 0; i--) {
var methodName = vjs.media.ApiMethods[i];
vjs.MediaTechController.prototype[vjs.media.ApiMethods[i]] = createMethod(methodName);
}