videojs-mobile-ui 0.8.0 → 0.9.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -31
- package/README.md +110 -50
- package/dist/videojs-mobile-ui.cjs.js +265 -222
- package/dist/videojs-mobile-ui.css +2 -2
- package/dist/videojs-mobile-ui.es.js +259 -215
- package/dist/videojs-mobile-ui.js +268 -264
- package/dist/videojs-mobile-ui.min.js +2 -2
- package/docs/api/swipeFullscreen.js.html +173 -0
- package/index.html +238 -176
- package/package.json +15 -15
- package/src/plugin.css +21 -3
- package/src/plugin.js +17 -54
- package/src/swipeFullscreen.js +117 -0
- package/src/touchOverlay.js +62 -56
- package/test/plugin.test.js +128 -20
- package/test/swipeFullscreen.test.js +365 -0
|
@@ -1,29 +1,41 @@
|
|
|
1
|
-
/*! @name videojs-mobile-ui @version 0.
|
|
1
|
+
/*! @name videojs-mobile-ui @version 0.9.0-beta.3 @license MIT */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var videojs = require('video.js');
|
|
5
|
-
var _inheritsLoose = require('@babel/runtime/helpers/inheritsLoose');
|
|
6
5
|
var window = require('global/window');
|
|
7
6
|
|
|
8
7
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
8
|
|
|
10
9
|
var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs);
|
|
11
|
-
var _inheritsLoose__default = /*#__PURE__*/_interopDefaultLegacy(_inheritsLoose);
|
|
12
10
|
var window__default = /*#__PURE__*/_interopDefaultLegacy(window);
|
|
13
11
|
|
|
14
|
-
var version = "0.
|
|
12
|
+
var version = "0.9.0-beta.3";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @file touchOverlay.js
|
|
16
|
+
* Touch UI component
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** @import Player from 'video.js/dist/types/player' */
|
|
20
|
+
|
|
21
|
+
const Component = videojs__default["default"].getComponent('Component');
|
|
22
|
+
const dom = videojs__default["default"].dom || videojs__default["default"];
|
|
23
|
+
const debounce = (callback, wait) => {
|
|
24
|
+
let timeoutId = null;
|
|
25
|
+
return (...args) => {
|
|
26
|
+
window__default["default"].clearTimeout(timeoutId);
|
|
27
|
+
timeoutId = window__default["default"].setTimeout(() => {
|
|
28
|
+
callback.apply(null, args);
|
|
29
|
+
}, wait);
|
|
30
|
+
};
|
|
31
|
+
};
|
|
15
32
|
|
|
16
|
-
var Component = videojs__default['default'].getComponent('Component');
|
|
17
|
-
var dom = videojs__default['default'].dom || videojs__default['default'];
|
|
18
33
|
/**
|
|
19
34
|
* The `TouchOverlay` is an overlay to capture tap events.
|
|
20
35
|
*
|
|
21
36
|
* @extends Component
|
|
22
37
|
*/
|
|
23
|
-
|
|
24
|
-
var TouchOverlay = /*#__PURE__*/function (_Component) {
|
|
25
|
-
_inheritsLoose__default['default'](TouchOverlay, _Component);
|
|
26
|
-
|
|
38
|
+
class TouchOverlay extends Component {
|
|
27
39
|
/**
|
|
28
40
|
* Creates an instance of the this class.
|
|
29
41
|
*
|
|
@@ -33,46 +45,80 @@ var TouchOverlay = /*#__PURE__*/function (_Component) {
|
|
|
33
45
|
* @param {Object} [options]
|
|
34
46
|
* The key/value store of player options.
|
|
35
47
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
player.on(['playing', 'userinactive'],
|
|
47
|
-
|
|
48
|
-
});
|
|
48
|
+
constructor(player, options) {
|
|
49
|
+
super(player, options);
|
|
50
|
+
this.seekSeconds = options.seekSeconds;
|
|
51
|
+
this.tapTimeout = options.tapTimeout;
|
|
52
|
+
this.taps = 0;
|
|
53
|
+
|
|
54
|
+
// Add play toggle overlay
|
|
55
|
+
this.addChild('playToggle', {});
|
|
56
|
+
|
|
57
|
+
// Clear overlay when playback starts or with control fade
|
|
58
|
+
player.on(['playing', 'userinactive'], e => {
|
|
59
|
+
this.removeClass('show-play-toggle');
|
|
60
|
+
});
|
|
49
61
|
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
// A 0 inactivity timeout won't work here
|
|
63
|
+
if (this.player_.options_.inactivityTimeout === 0) {
|
|
64
|
+
this.player_.options_.inactivityTimeout = 5000;
|
|
52
65
|
}
|
|
53
66
|
|
|
54
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Debounced tap handler.
|
|
69
|
+
* Seeks number of (taps - 1) * configured seconds to skip.
|
|
70
|
+
* One tap is a non-op
|
|
71
|
+
*
|
|
72
|
+
* @param {Event} event
|
|
73
|
+
*/
|
|
74
|
+
this.handleTaps_ = debounce(event => {
|
|
75
|
+
const increment = (this.taps - 1) * this.seekSeconds;
|
|
76
|
+
this.taps = 0;
|
|
77
|
+
if (increment < 1) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const rect = this.el_.getBoundingClientRect();
|
|
81
|
+
const x = event.changedTouches[0].clientX - rect.left;
|
|
82
|
+
|
|
83
|
+
// Check if double tap is in left or right area
|
|
84
|
+
if (x < rect.width * 0.4) {
|
|
85
|
+
this.player_.currentTime(Math.max(0, this.player_.currentTime() - increment));
|
|
86
|
+
this.addClass('reverse');
|
|
87
|
+
} else if (x > rect.width - rect.width * 0.4) {
|
|
88
|
+
this.player_.currentTime(Math.min(this.player_.duration(), this.player_.currentTime() + increment));
|
|
89
|
+
this.removeClass('reverse');
|
|
90
|
+
} else {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
55
93
|
|
|
56
|
-
|
|
94
|
+
// Remove play toggle if showing
|
|
95
|
+
this.removeClass('show-play-toggle');
|
|
96
|
+
|
|
97
|
+
// Remove and readd class to trigger animation
|
|
98
|
+
this.setAttribute('data-skip-text', `${increment} ${this.localize('seconds')}`);
|
|
99
|
+
this.removeClass('skip');
|
|
100
|
+
window__default["default"].requestAnimationFrame(() => {
|
|
101
|
+
this.addClass('skip');
|
|
102
|
+
});
|
|
103
|
+
}, this.tapTimeout);
|
|
104
|
+
this.enable();
|
|
57
105
|
}
|
|
106
|
+
|
|
58
107
|
/**
|
|
59
108
|
* Builds the DOM element.
|
|
60
109
|
*
|
|
61
110
|
* @return {Element}
|
|
62
111
|
* The DOM element.
|
|
63
112
|
*/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
var _proto = TouchOverlay.prototype;
|
|
67
|
-
|
|
68
|
-
_proto.createEl = function createEl() {
|
|
69
|
-
var el = dom.createEl('div', {
|
|
113
|
+
createEl() {
|
|
114
|
+
const el = dom.createEl('div', {
|
|
70
115
|
className: 'vjs-touch-overlay',
|
|
71
116
|
// Touch overlay is not tabbable.
|
|
72
117
|
tabIndex: -1
|
|
73
118
|
});
|
|
74
119
|
return el;
|
|
75
120
|
}
|
|
121
|
+
|
|
76
122
|
/**
|
|
77
123
|
* Debounces to either handle a delayed single tap, or a double tap
|
|
78
124
|
*
|
|
@@ -80,111 +126,166 @@ var TouchOverlay = /*#__PURE__*/function (_Component) {
|
|
|
80
126
|
* The touch event
|
|
81
127
|
*
|
|
82
128
|
*/
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
_proto.handleTap = function handleTap(event) {
|
|
86
|
-
var _this2 = this;
|
|
87
|
-
|
|
129
|
+
handleTap(event) {
|
|
88
130
|
// Don't handle taps on the play button
|
|
89
131
|
if (event.target !== this.el_) {
|
|
90
132
|
return;
|
|
91
133
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
window__default['default'].clearTimeout(this.timeout);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
this.handleDoubleTap(event);
|
|
103
|
-
} else {
|
|
104
|
-
this.firstTapCaptured = true;
|
|
105
|
-
this.timeout = window__default['default'].setTimeout(function () {
|
|
106
|
-
_this2.firstTapCaptured = false;
|
|
107
|
-
|
|
108
|
-
_this2.handleSingleTap(event);
|
|
109
|
-
}, this.tapTimeout);
|
|
134
|
+
if (event.cancelable) {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
}
|
|
137
|
+
this.taps += 1;
|
|
138
|
+
if (this.taps === 1) {
|
|
139
|
+
this.removeClass('skip');
|
|
140
|
+
this.toggleClass('show-play-toggle');
|
|
110
141
|
}
|
|
142
|
+
this.handleTaps_(event);
|
|
111
143
|
}
|
|
144
|
+
|
|
112
145
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* @param {Event} event
|
|
116
|
-
* The touch event
|
|
117
|
-
*
|
|
146
|
+
* Enables touch handler
|
|
118
147
|
*/
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.removeClass('skip');
|
|
123
|
-
this.toggleClass('show-play-toggle');
|
|
148
|
+
enable() {
|
|
149
|
+
this.firstTapCaptured = false;
|
|
150
|
+
this.on('touchend', this.handleTap);
|
|
124
151
|
}
|
|
152
|
+
|
|
125
153
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* @param {Event} event
|
|
129
|
-
* The touch event
|
|
130
|
-
*
|
|
154
|
+
* Disables touch handler
|
|
131
155
|
*/
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
156
|
+
disable() {
|
|
157
|
+
this.off('touchend', this.handleTap);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
Component.registerComponent('TouchOverlay', TouchOverlay);
|
|
136
161
|
|
|
137
|
-
|
|
138
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Sets up swiping to enter and exit fullscreen.
|
|
164
|
+
*
|
|
165
|
+
* @param {Object} player
|
|
166
|
+
* The player to initialise on.
|
|
167
|
+
* @param {Object} pluginOptions
|
|
168
|
+
* The options used by the mobile ui plugin.
|
|
169
|
+
*/
|
|
170
|
+
const initSwipe = (player, pluginOptions) => {
|
|
171
|
+
const {
|
|
172
|
+
swipeToFullscreen,
|
|
173
|
+
swipeFromFullscreen
|
|
174
|
+
} = pluginOptions.fullscreen;
|
|
175
|
+
if (swipeToFullscreen) {
|
|
176
|
+
player.addClass('using-fs-swipe-up');
|
|
177
|
+
}
|
|
178
|
+
if (swipeFromFullscreen) {
|
|
179
|
+
player.addClass('using-fs-swipe-down');
|
|
180
|
+
}
|
|
181
|
+
let touchStartY = 0;
|
|
182
|
+
let couldBeSwiping = false;
|
|
183
|
+
const swipeThreshold = 30;
|
|
139
184
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Monitor the possible start of a swipe
|
|
187
|
+
*
|
|
188
|
+
* @param {TouchEvent} e Triggering touch event
|
|
189
|
+
*/
|
|
190
|
+
const onStart = e => {
|
|
191
|
+
const isFullscreen = player.isFullscreen();
|
|
192
|
+
if (!isFullscreen && !swipeToFullscreen || isFullscreen && !swipeFromFullscreen) {
|
|
193
|
+
couldBeSwiping = false;
|
|
147
194
|
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
195
|
+
}
|
|
196
|
+
touchStartY = e.changedTouches[0].clientY;
|
|
197
|
+
couldBeSwiping = true;
|
|
198
|
+
player.tech_.el().style.transition = '';
|
|
199
|
+
};
|
|
152
200
|
|
|
153
|
-
this.removeClass('skip');
|
|
154
|
-
window__default['default'].requestAnimationFrame(function () {
|
|
155
|
-
_this3.addClass('skip');
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
201
|
/**
|
|
159
|
-
*
|
|
202
|
+
* Monitor the movement of a swipe
|
|
203
|
+
*
|
|
204
|
+
* @param {TouchEvent} e Triggering touch event
|
|
160
205
|
*/
|
|
161
|
-
|
|
206
|
+
const onMove = e => {
|
|
207
|
+
if (!couldBeSwiping) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const currentY = e.touches[0].clientY;
|
|
211
|
+
const deltaY = touchStartY - currentY;
|
|
212
|
+
const isFullscreen = player.isFullscreen();
|
|
213
|
+
let scale = 1;
|
|
214
|
+
if (!isFullscreen && deltaY > 0) {
|
|
215
|
+
// Swiping up to enter fullscreen: Zoom in (Max 1.1)
|
|
216
|
+
scale = 1 + Math.min(0.1, deltaY / 500);
|
|
217
|
+
player.tech_.el().style.transform = `scale(${scale})`;
|
|
218
|
+
} else if (isFullscreen && deltaY < 0) {
|
|
219
|
+
// Swiping down to exit fullscreen: Zoom out (Min 0.9)
|
|
220
|
+
scale = 1 - Math.min(0.1, Math.abs(deltaY) / 500);
|
|
221
|
+
player.tech_.el().style.transform = `scale(${scale})`;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
162
224
|
|
|
163
|
-
_proto.enable = function enable() {
|
|
164
|
-
this.firstTapCaptured = false;
|
|
165
|
-
this.on('touchend', this.handleTap);
|
|
166
|
-
}
|
|
167
225
|
/**
|
|
168
|
-
*
|
|
226
|
+
* Monitor the touch end to determine a valid swipe
|
|
227
|
+
*
|
|
228
|
+
* @param {TouchEvent} e Triggering touch event
|
|
169
229
|
*/
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
230
|
+
const onEnd = e => {
|
|
231
|
+
if (!couldBeSwiping) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
couldBeSwiping = false;
|
|
235
|
+
player.tech_.el().style.transition = 'transform 0.3s ease-out';
|
|
236
|
+
player.tech_.el().style.transform = 'scale(1)';
|
|
237
|
+
if (e.type === 'touchcancel') {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const touchEndY = e.changedTouches[0].clientY;
|
|
241
|
+
const deltaY = touchStartY - touchEndY;
|
|
242
|
+
if (deltaY > swipeThreshold && !player.isFullscreen()) {
|
|
243
|
+
player.requestFullscreen().catch(err => {
|
|
244
|
+
player.log.warn('Browser refused fullscreen', err);
|
|
245
|
+
});
|
|
246
|
+
} else if (deltaY < -swipeThreshold && player.isFullscreen()) {
|
|
247
|
+
player.exitFullscreen();
|
|
248
|
+
}
|
|
174
249
|
};
|
|
250
|
+
player.el().addEventListener('touchstart', onStart, {
|
|
251
|
+
passive: true
|
|
252
|
+
});
|
|
253
|
+
player.el().addEventListener('touchmove', onMove, {
|
|
254
|
+
passive: true
|
|
255
|
+
});
|
|
256
|
+
player.el().addEventListener('touchend', onEnd, {
|
|
257
|
+
passive: true
|
|
258
|
+
});
|
|
259
|
+
player.el().addEventListener('touchcancel', onEnd, {
|
|
260
|
+
passive: true
|
|
261
|
+
});
|
|
262
|
+
player.on('dispose', () => {
|
|
263
|
+
player.el().removeEventListener('touchstart', onStart, {
|
|
264
|
+
passive: true
|
|
265
|
+
});
|
|
266
|
+
player.el().removeEventListener('touchmove', onMove, {
|
|
267
|
+
passive: true
|
|
268
|
+
});
|
|
269
|
+
player.el().removeEventListener('touchend', onEnd, {
|
|
270
|
+
passive: true
|
|
271
|
+
});
|
|
272
|
+
player.el().removeEventListener('touchcancel', onEnd, {
|
|
273
|
+
passive: true
|
|
274
|
+
});
|
|
275
|
+
player.tech_.el().style.transform = '';
|
|
276
|
+
player.tech_.el().style.transition = '';
|
|
277
|
+
});
|
|
278
|
+
};
|
|
175
279
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
Component.registerComponent('TouchOverlay', TouchOverlay);
|
|
180
|
-
|
|
181
|
-
var defaults = {
|
|
280
|
+
// Default options for the plugin.
|
|
281
|
+
const defaults = {
|
|
182
282
|
fullscreen: {
|
|
183
283
|
enterOnRotate: true,
|
|
184
284
|
exitOnRotate: true,
|
|
185
285
|
lockOnRotate: true,
|
|
186
286
|
lockToLandscapeOnEnter: false,
|
|
187
|
-
|
|
287
|
+
swipeToFullscreen: false,
|
|
288
|
+
swipeFromFullscreen: false,
|
|
188
289
|
disabled: false
|
|
189
290
|
},
|
|
190
291
|
touchControls: {
|
|
@@ -194,37 +295,33 @@ var defaults = {
|
|
|
194
295
|
disabled: false
|
|
195
296
|
}
|
|
196
297
|
};
|
|
197
|
-
|
|
298
|
+
const screen = window__default["default"].screen;
|
|
299
|
+
const registerPlugin = videojs__default["default"].registerPlugin || videojs__default["default"].plugin;
|
|
300
|
+
|
|
198
301
|
/**
|
|
199
302
|
* Gets 'portrait' or 'lanscape' from the two orientation APIs
|
|
200
303
|
*
|
|
201
304
|
* @return {string} orientation
|
|
202
305
|
*/
|
|
203
|
-
|
|
204
|
-
var getOrientation = function getOrientation() {
|
|
306
|
+
const getOrientation = () => {
|
|
205
307
|
if (screen) {
|
|
206
308
|
// Prefer the string over angle, as 0° can be landscape on some tablets
|
|
207
|
-
|
|
208
|
-
|
|
309
|
+
const orientationString = ((screen.orientation || {}).type || screen.mozOrientation || screen.msOrientation || '').split('-')[0];
|
|
209
310
|
if (orientationString === 'landscape' || orientationString === 'portrait') {
|
|
210
311
|
return orientationString;
|
|
211
312
|
}
|
|
212
|
-
}
|
|
213
|
-
|
|
313
|
+
}
|
|
214
314
|
|
|
215
|
-
|
|
216
|
-
|
|
315
|
+
// iOS only supports window.orientation
|
|
316
|
+
if (typeof window__default["default"].orientation === 'number') {
|
|
317
|
+
if (window__default["default"].orientation === 0 || window__default["default"].orientation === 180) {
|
|
217
318
|
return 'portrait';
|
|
218
319
|
}
|
|
219
|
-
|
|
220
320
|
return 'landscape';
|
|
221
321
|
}
|
|
222
|
-
|
|
223
322
|
return 'portrait';
|
|
224
|
-
};
|
|
225
|
-
|
|
323
|
+
};
|
|
226
324
|
|
|
227
|
-
var registerPlugin = videojs__default['default'].registerPlugin || videojs__default['default'].plugin;
|
|
228
325
|
/**
|
|
229
326
|
* Add UI and event listeners
|
|
230
327
|
*
|
|
@@ -232,63 +329,49 @@ var registerPlugin = videojs__default['default'].registerPlugin || videojs__defa
|
|
|
232
329
|
* @param {Player} player
|
|
233
330
|
* A Video.js player object.
|
|
234
331
|
*
|
|
235
|
-
* @param {
|
|
332
|
+
* @param {MobileUiOptions} [options={}]
|
|
236
333
|
* A plain object containing options for the plugin.
|
|
237
334
|
*/
|
|
238
|
-
|
|
239
|
-
var onPlayerReady = function onPlayerReady(player, options) {
|
|
335
|
+
const onPlayerReady = (player, options) => {
|
|
240
336
|
player.addClass('vjs-mobile-ui');
|
|
241
|
-
|
|
242
|
-
if (options.fullscreen.iOS) {
|
|
243
|
-
videojs__default['default'].log.warn('videojs-mobile-ui: `fullscreen.iOS` is deprecated. Use Video.js option `preferFullWindow` instead.');
|
|
244
|
-
|
|
245
|
-
if (videojs__default['default'].browser.IS_IOS && videojs__default['default'].browser.IOS_VERSION > 9 && !player.el_.ownerDocument.querySelector('.bc-iframe')) {
|
|
246
|
-
player.tech_.el_.setAttribute('playsinline', 'playsinline');
|
|
247
|
-
|
|
248
|
-
player.tech_.supportsFullScreen = function () {
|
|
249
|
-
return false;
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
337
|
if (!options.touchControls.disabled) {
|
|
255
338
|
if (options.touchControls.disableOnEnd || typeof player.endscreen === 'function') {
|
|
256
339
|
player.addClass('vjs-mobile-ui-disable-end');
|
|
257
|
-
}
|
|
258
|
-
|
|
340
|
+
}
|
|
259
341
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
342
|
+
// Insert before the control bar
|
|
343
|
+
let controlBarIdx;
|
|
344
|
+
const versionParts = videojs__default["default"].VERSION.split('.');
|
|
345
|
+
const major = parseInt(versionParts[0], 10);
|
|
346
|
+
const minor = parseInt(versionParts[1], 10);
|
|
264
347
|
|
|
348
|
+
// Video.js < 7.7.0 doesn't account for precedding components that don't have elements
|
|
265
349
|
if (major < 7 || major === 7 && minor < 7) {
|
|
266
350
|
controlBarIdx = Array.prototype.indexOf.call(player.el_.children, player.getChild('ControlBar').el_);
|
|
267
351
|
} else {
|
|
268
352
|
controlBarIdx = player.children_.indexOf(player.getChild('ControlBar'));
|
|
269
353
|
}
|
|
270
|
-
|
|
271
354
|
player.touchOverlay = player.addChild('TouchOverlay', options.touchControls, controlBarIdx);
|
|
272
355
|
}
|
|
273
|
-
|
|
274
356
|
if (options.fullscreen.disabled) {
|
|
275
357
|
return;
|
|
276
358
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
359
|
+
if (options.fullscreen.swipeToFullscreen || options.fullscreen.swipeFromFullscreen) {
|
|
360
|
+
initSwipe(player, options);
|
|
361
|
+
}
|
|
362
|
+
let locked = false;
|
|
363
|
+
const rotationHandler = () => {
|
|
364
|
+
const currentOrientation = getOrientation();
|
|
283
365
|
if (currentOrientation === 'landscape' && options.fullscreen.enterOnRotate) {
|
|
284
|
-
if (player.paused()
|
|
285
|
-
player.requestFullscreen()
|
|
286
|
-
|
|
366
|
+
if (!player.paused() && !player.isFullscreen()) {
|
|
367
|
+
player.requestFullscreen().catch(err => {
|
|
368
|
+
player.log.warn('Browser refused fullscreen request:', err);
|
|
369
|
+
});
|
|
287
370
|
if ((options.fullscreen.lockOnRotate || options.fullscreen.lockToLandscapeOnEnter) && screen.orientation && screen.orientation.lock) {
|
|
288
|
-
screen.orientation.lock('landscape').then(
|
|
371
|
+
screen.orientation.lock('landscape').then(() => {
|
|
289
372
|
locked = true;
|
|
290
|
-
}).catch(
|
|
291
|
-
videojs__default[
|
|
373
|
+
}).catch(err => {
|
|
374
|
+
videojs__default["default"].log.warn('Browser refused orientation lock:', err);
|
|
292
375
|
});
|
|
293
376
|
}
|
|
294
377
|
}
|
|
@@ -298,98 +381,58 @@ var onPlayerReady = function onPlayerReady(player, options) {
|
|
|
298
381
|
}
|
|
299
382
|
}
|
|
300
383
|
};
|
|
301
|
-
|
|
302
384
|
if (options.fullscreen.enterOnRotate || options.fullscreen.exitOnRotate) {
|
|
303
|
-
if (videojs__default[
|
|
304
|
-
window__default[
|
|
305
|
-
player.on('dispose',
|
|
306
|
-
window__default[
|
|
385
|
+
if (videojs__default["default"].browser.IS_IOS) {
|
|
386
|
+
window__default["default"].addEventListener('orientationchange', rotationHandler);
|
|
387
|
+
player.on('dispose', () => {
|
|
388
|
+
window__default["default"].removeEventListener('orientationchange', rotationHandler);
|
|
307
389
|
});
|
|
308
390
|
} else if (screen.orientation) {
|
|
309
391
|
// addEventListener('orientationchange') is not a user interaction on Android
|
|
310
392
|
screen.orientation.onchange = rotationHandler;
|
|
311
|
-
player.on('dispose',
|
|
393
|
+
player.on('dispose', () => {
|
|
312
394
|
screen.orientation.onchange = null;
|
|
313
395
|
});
|
|
314
396
|
}
|
|
315
397
|
}
|
|
316
|
-
|
|
317
|
-
player.on('fullscreenchange', function (_) {
|
|
398
|
+
player.on('fullscreenchange', _ => {
|
|
318
399
|
if (player.isFullscreen() && options.fullscreen.lockToLandscapeOnEnter && getOrientation() === 'portrait') {
|
|
319
|
-
screen.orientation.lock('landscape').then(
|
|
400
|
+
screen.orientation.lock('landscape').then(() => {
|
|
320
401
|
locked = true;
|
|
321
|
-
}).catch(
|
|
322
|
-
videojs__default[
|
|
402
|
+
}).catch(e => {
|
|
403
|
+
videojs__default["default"].log('Browser refused orientation lock:', e);
|
|
323
404
|
});
|
|
324
405
|
} else if (!player.isFullscreen() && locked) {
|
|
325
406
|
screen.orientation.unlock();
|
|
326
407
|
locked = false;
|
|
327
408
|
}
|
|
328
409
|
});
|
|
329
|
-
player.on('ended',
|
|
410
|
+
player.on('ended', _ => {
|
|
330
411
|
if (locked === true) {
|
|
331
412
|
screen.orientation.unlock();
|
|
332
413
|
locked = false;
|
|
333
414
|
}
|
|
334
415
|
});
|
|
335
416
|
};
|
|
417
|
+
|
|
336
418
|
/**
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* Adds a monile UI for player control, and fullscreen orientation control
|
|
419
|
+
* Adds a mobile UI for player control, and fullscreen orientation control
|
|
340
420
|
*
|
|
341
421
|
* @function mobileUi
|
|
342
|
-
* @param {Object} [options={}]
|
|
343
|
-
* Plugin options.
|
|
344
|
-
* @param {boolean} [options.forceForTesting=false]
|
|
345
|
-
* Enables the display regardless of user agent, for testing purposes
|
|
346
|
-
* @param {Object} [options.fullscreen={}]
|
|
347
|
-
* Fullscreen options.
|
|
348
|
-
* @param {boolean} [options.fullscreen.disabled=false]
|
|
349
|
-
* If true no fullscreen handling except the *deprecated* iOS fullwindow hack
|
|
350
|
-
* @param {boolean} [options.fullscreen.enterOnRotate=true]
|
|
351
|
-
* Whether to go fullscreen when rotating to landscape
|
|
352
|
-
* @param {boolean} [options.fullscreen.exitOnRotate=true]
|
|
353
|
-
* Whether to leave fullscreen when rotating to portrait (if not locked)
|
|
354
|
-
* @param {boolean} [options.fullscreen.lockOnRotate=true]
|
|
355
|
-
* Whether to lock orientation when rotating to landscape
|
|
356
|
-
* Unlocked when exiting fullscreen or on 'ended
|
|
357
|
-
* @param {boolean} [options.fullscreen.lockToLandscapeOnEnter=false]
|
|
358
|
-
* Whether to always lock orientation to landscape on fullscreen mode
|
|
359
|
-
* Unlocked when exiting fullscreen or on 'ended'
|
|
360
|
-
* @param {boolean} [options.fullscreen.iOS=false]
|
|
361
|
-
* Deprecated: Whether to disable iOS's native fullscreen so controls can work
|
|
362
|
-
* @param {Object} [options.touchControls={}]
|
|
363
|
-
* Touch UI options.
|
|
364
|
-
* @param {boolean} [options.touchControls.disabled=false]
|
|
365
|
-
* If true no touch controls are added.
|
|
366
|
-
* @param {int} [options.touchControls.seekSeconds=10]
|
|
367
|
-
* Number of seconds to seek on double-tap
|
|
368
|
-
* @param {int} [options.touchControls.tapTimeout=300]
|
|
369
|
-
* Interval in ms to be considered a doubletap
|
|
370
|
-
* @param {boolean} [options.touchControls.disableOnEnd=false]
|
|
371
|
-
* Whether to disable when the video ends (e.g., if there is an endscreen)
|
|
372
|
-
* Never shows if the endscreen plugin is present
|
|
422
|
+
* @param {Object} [options={}] Plugin options
|
|
373
423
|
*/
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (options === void 0) {
|
|
380
|
-
options = {};
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (options.forceForTesting || videojs__default['default'].browser.IS_ANDROID || videojs__default['default'].browser.IS_IOS) {
|
|
384
|
-
this.ready(function () {
|
|
385
|
-
onPlayerReady(_this, videojs__default['default'].mergeOptions(defaults, options));
|
|
424
|
+
const mobileUi = function (options = {}) {
|
|
425
|
+
if (options.forceForTesting || videojs__default["default"].browser.IS_ANDROID || videojs__default["default"].browser.IS_IOS) {
|
|
426
|
+
this.ready(() => {
|
|
427
|
+
onPlayerReady(this, videojs__default["default"].mergeOptions(defaults, options));
|
|
386
428
|
});
|
|
387
429
|
}
|
|
388
|
-
};
|
|
389
|
-
|
|
430
|
+
};
|
|
390
431
|
|
|
391
|
-
|
|
432
|
+
// Register the plugin with video.js.
|
|
433
|
+
registerPlugin('mobileUi', mobileUi);
|
|
392
434
|
|
|
435
|
+
// Include the version number.
|
|
393
436
|
mobileUi.VERSION = version;
|
|
394
437
|
|
|
395
438
|
module.exports = mobileUi;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! @name videojs-mobile-ui @version 0.
|
|
2
|
-
|
|
1
|
+
/*! @name videojs-mobile-ui @version 0.9.0-beta.3 @license MIT */
|
|
2
|
+
@keyframes fadeAndScale{0%,to{opacity:0}25%{opacity:1}}.video-js.vjs-mobile-ui.vjs-has-started:not(.vjs-ad-playing) .vjs-touch-overlay{position:absolute;pointer-events:auto;top:0}.video-js.vjs-mobile-ui .vjs-touch-overlay{display:block;width:100%;height:100%;pointer-events:none}.video-js.vjs-mobile-ui .vjs-touch-overlay.skip{opacity:0;animation:fadeAndScale .8s linear;background-repeat:no-repeat;background-position:80% center;background-size:10%;background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay.skip:after{content:attr(data-skip-text);position:absolute;top:60%;left:70%}.video-js.vjs-mobile-ui .vjs-touch-overlay.skip.reverse{background-position:20% center;background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay.skip.reverse:after{right:70%;left:unset}.video-js.vjs-mobile-ui .vjs-touch-overlay .vjs-play-control{top:50%;left:50%;transform:translate(-50%,-50%);position:absolute;width:30%;height:80%;pointer-events:none;opacity:0;transition:opacity .3s ease}.video-js.vjs-mobile-ui .vjs-touch-overlay .vjs-play-control .vjs-icon-placeholder::before{content:'';background-size:60%;background-position:center center;background-repeat:no-repeat;background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/><path d="M0 0h24v24H0z" fill="none"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay .vjs-play-control.vjs-paused .vjs-icon-placeholder::before{content:'';background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M8 5v14l11-7z"/><path d="M0 0h24v24H0z" fill="none"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay .vjs-play-control.vjs-ended .vjs-icon-placeholder::before{content:'';background-image:url('data:image/svg+xml;utf8,<svg fill="%23FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/></svg>')}.video-js.vjs-mobile-ui .vjs-touch-overlay.show-play-toggle .vjs-play-control{opacity:1;pointer-events:auto}.video-js.vjs-mobile-ui.vjs-mobile-ui-disable-end.vjs-ended .vjs-touch-overlay{display:none}.video-js.vjs-mobile-ui:-ms-fullscreen.using-fs-swipe-down,.video-js.vjs-mobile-ui:not(:-ms-fullscreen).using-fs-swipe-up{touch-action:none;overflow:hidden}.video-js.vjs-mobile-ui:fullscreen.using-fs-swipe-down,.video-js.vjs-mobile-ui:not(:fullscreen).using-fs-swipe-up{touch-action:none;overflow:hidden}
|