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,68 +1,41 @@
|
|
|
1
|
-
/*! @name videojs-mobile-ui @version 0.
|
|
1
|
+
/*! @name videojs-mobile-ui @version 0.9.0-beta.3 @license MIT */
|
|
2
2
|
(function (global, factory) {
|
|
3
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')
|
|
4
|
-
typeof define === 'function' && define.amd ? define(['video.js'
|
|
5
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.videojsMobileUi = factory(global.videojs
|
|
6
|
-
}(this, (function (videojs
|
|
3
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) :
|
|
4
|
+
typeof define === 'function' && define.amd ? define(['video.js'], factory) :
|
|
5
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.videojsMobileUi = factory(global.videojs));
|
|
6
|
+
})(this, (function (videojs) { 'use strict';
|
|
7
7
|
|
|
8
8
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
9
|
|
|
10
10
|
var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs);
|
|
11
|
-
var window__default = /*#__PURE__*/_interopDefaultLegacy(window);
|
|
12
11
|
|
|
13
|
-
var version = "0.
|
|
12
|
+
var version = "0.9.0-beta.3";
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
require: function (path, base) {
|
|
20
|
-
return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
|
|
21
|
-
}
|
|
22
|
-
}, fn(module, module.exports), module.exports;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function commonjsRequire () {
|
|
26
|
-
throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
var setPrototypeOf = createCommonjsModule(function (module) {
|
|
30
|
-
function _setPrototypeOf(o, p) {
|
|
31
|
-
module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
|
|
32
|
-
o.__proto__ = p;
|
|
33
|
-
return o;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
|
37
|
-
return _setPrototypeOf(o, p);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = _setPrototypeOf;
|
|
41
|
-
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
var inheritsLoose = createCommonjsModule(function (module) {
|
|
45
|
-
function _inheritsLoose(subClass, superClass) {
|
|
46
|
-
subClass.prototype = Object.create(superClass.prototype);
|
|
47
|
-
subClass.prototype.constructor = subClass;
|
|
48
|
-
setPrototypeOf(subClass, superClass);
|
|
49
|
-
}
|
|
14
|
+
/**
|
|
15
|
+
* @file touchOverlay.js
|
|
16
|
+
* Touch UI component
|
|
17
|
+
*/
|
|
50
18
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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.clearTimeout(timeoutId);
|
|
27
|
+
timeoutId = window.setTimeout(() => {
|
|
28
|
+
callback.apply(null, args);
|
|
29
|
+
}, wait);
|
|
30
|
+
};
|
|
31
|
+
};
|
|
54
32
|
|
|
55
|
-
var Component = videojs__default['default'].getComponent('Component');
|
|
56
|
-
var dom = videojs__default['default'].dom || videojs__default['default'];
|
|
57
33
|
/**
|
|
58
34
|
* The `TouchOverlay` is an overlay to capture tap events.
|
|
59
35
|
*
|
|
60
36
|
* @extends Component
|
|
61
37
|
*/
|
|
62
|
-
|
|
63
|
-
var TouchOverlay = /*#__PURE__*/function (_Component) {
|
|
64
|
-
inheritsLoose(TouchOverlay, _Component);
|
|
65
|
-
|
|
38
|
+
class TouchOverlay extends Component {
|
|
66
39
|
/**
|
|
67
40
|
* Creates an instance of the this class.
|
|
68
41
|
*
|
|
@@ -72,46 +45,80 @@
|
|
|
72
45
|
* @param {Object} [options]
|
|
73
46
|
* The key/value store of player options.
|
|
74
47
|
*/
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
player.on(['playing', 'userinactive'],
|
|
86
|
-
|
|
87
|
-
});
|
|
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
|
+
});
|
|
88
61
|
|
|
89
|
-
|
|
90
|
-
|
|
62
|
+
// A 0 inactivity timeout won't work here
|
|
63
|
+
if (this.player_.options_.inactivityTimeout === 0) {
|
|
64
|
+
this.player_.options_.inactivityTimeout = 5000;
|
|
91
65
|
}
|
|
92
66
|
|
|
93
|
-
|
|
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
|
+
}
|
|
94
93
|
|
|
95
|
-
|
|
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.requestAnimationFrame(() => {
|
|
101
|
+
this.addClass('skip');
|
|
102
|
+
});
|
|
103
|
+
}, this.tapTimeout);
|
|
104
|
+
this.enable();
|
|
96
105
|
}
|
|
106
|
+
|
|
97
107
|
/**
|
|
98
108
|
* Builds the DOM element.
|
|
99
109
|
*
|
|
100
110
|
* @return {Element}
|
|
101
111
|
* The DOM element.
|
|
102
112
|
*/
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
var _proto = TouchOverlay.prototype;
|
|
106
|
-
|
|
107
|
-
_proto.createEl = function createEl() {
|
|
108
|
-
var el = dom.createEl('div', {
|
|
113
|
+
createEl() {
|
|
114
|
+
const el = dom.createEl('div', {
|
|
109
115
|
className: 'vjs-touch-overlay',
|
|
110
116
|
// Touch overlay is not tabbable.
|
|
111
117
|
tabIndex: -1
|
|
112
118
|
});
|
|
113
119
|
return el;
|
|
114
120
|
}
|
|
121
|
+
|
|
115
122
|
/**
|
|
116
123
|
* Debounces to either handle a delayed single tap, or a double tap
|
|
117
124
|
*
|
|
@@ -119,111 +126,166 @@
|
|
|
119
126
|
* The touch event
|
|
120
127
|
*
|
|
121
128
|
*/
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
_proto.handleTap = function handleTap(event) {
|
|
125
|
-
var _this2 = this;
|
|
126
|
-
|
|
129
|
+
handleTap(event) {
|
|
127
130
|
// Don't handle taps on the play button
|
|
128
131
|
if (event.target !== this.el_) {
|
|
129
132
|
return;
|
|
130
133
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (this.firstTapCaptured) {
|
|
135
|
-
this.firstTapCaptured = false;
|
|
136
|
-
|
|
137
|
-
if (this.timeout) {
|
|
138
|
-
window__default['default'].clearTimeout(this.timeout);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
this.handleDoubleTap(event);
|
|
142
|
-
} else {
|
|
143
|
-
this.firstTapCaptured = true;
|
|
144
|
-
this.timeout = window__default['default'].setTimeout(function () {
|
|
145
|
-
_this2.firstTapCaptured = false;
|
|
146
|
-
|
|
147
|
-
_this2.handleSingleTap(event);
|
|
148
|
-
}, this.tapTimeout);
|
|
134
|
+
if (event.cancelable) {
|
|
135
|
+
event.preventDefault();
|
|
149
136
|
}
|
|
137
|
+
this.taps += 1;
|
|
138
|
+
if (this.taps === 1) {
|
|
139
|
+
this.removeClass('skip');
|
|
140
|
+
this.toggleClass('show-play-toggle');
|
|
141
|
+
}
|
|
142
|
+
this.handleTaps_(event);
|
|
150
143
|
}
|
|
144
|
+
|
|
151
145
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* @param {Event} event
|
|
155
|
-
* The touch event
|
|
156
|
-
*
|
|
146
|
+
* Enables touch handler
|
|
157
147
|
*/
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
this.removeClass('skip');
|
|
162
|
-
this.toggleClass('show-play-toggle');
|
|
148
|
+
enable() {
|
|
149
|
+
this.firstTapCaptured = false;
|
|
150
|
+
this.on('touchend', this.handleTap);
|
|
163
151
|
}
|
|
152
|
+
|
|
164
153
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* @param {Event} event
|
|
168
|
-
* The touch event
|
|
169
|
-
*
|
|
154
|
+
* Disables touch handler
|
|
170
155
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
156
|
+
disable() {
|
|
157
|
+
this.off('touchend', this.handleTap);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
Component.registerComponent('TouchOverlay', TouchOverlay);
|
|
175
161
|
|
|
176
|
-
|
|
177
|
-
|
|
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;
|
|
178
184
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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;
|
|
186
194
|
return;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
195
|
+
}
|
|
196
|
+
touchStartY = e.changedTouches[0].clientY;
|
|
197
|
+
couldBeSwiping = true;
|
|
198
|
+
player.tech_.el().style.transition = '';
|
|
199
|
+
};
|
|
191
200
|
|
|
192
|
-
this.removeClass('skip');
|
|
193
|
-
window__default['default'].requestAnimationFrame(function () {
|
|
194
|
-
_this3.addClass('skip');
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
201
|
/**
|
|
198
|
-
*
|
|
202
|
+
* Monitor the movement of a swipe
|
|
203
|
+
*
|
|
204
|
+
* @param {TouchEvent} e Triggering touch event
|
|
199
205
|
*/
|
|
200
|
-
|
|
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
|
+
};
|
|
201
224
|
|
|
202
|
-
_proto.enable = function enable() {
|
|
203
|
-
this.firstTapCaptured = false;
|
|
204
|
-
this.on('touchend', this.handleTap);
|
|
205
|
-
}
|
|
206
225
|
/**
|
|
207
|
-
*
|
|
226
|
+
* Monitor the touch end to determine a valid swipe
|
|
227
|
+
*
|
|
228
|
+
* @param {TouchEvent} e Triggering touch event
|
|
208
229
|
*/
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
+
}
|
|
213
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
|
+
};
|
|
214
279
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Component.registerComponent('TouchOverlay', TouchOverlay);
|
|
219
|
-
|
|
220
|
-
var defaults = {
|
|
280
|
+
// Default options for the plugin.
|
|
281
|
+
const defaults = {
|
|
221
282
|
fullscreen: {
|
|
222
283
|
enterOnRotate: true,
|
|
223
284
|
exitOnRotate: true,
|
|
224
285
|
lockOnRotate: true,
|
|
225
286
|
lockToLandscapeOnEnter: false,
|
|
226
|
-
|
|
287
|
+
swipeToFullscreen: false,
|
|
288
|
+
swipeFromFullscreen: false,
|
|
227
289
|
disabled: false
|
|
228
290
|
},
|
|
229
291
|
touchControls: {
|
|
@@ -233,37 +295,33 @@
|
|
|
233
295
|
disabled: false
|
|
234
296
|
}
|
|
235
297
|
};
|
|
236
|
-
|
|
298
|
+
const screen = window.screen;
|
|
299
|
+
const registerPlugin = videojs__default["default"].registerPlugin || videojs__default["default"].plugin;
|
|
300
|
+
|
|
237
301
|
/**
|
|
238
302
|
* Gets 'portrait' or 'lanscape' from the two orientation APIs
|
|
239
303
|
*
|
|
240
304
|
* @return {string} orientation
|
|
241
305
|
*/
|
|
242
|
-
|
|
243
|
-
var getOrientation = function getOrientation() {
|
|
306
|
+
const getOrientation = () => {
|
|
244
307
|
if (screen) {
|
|
245
308
|
// Prefer the string over angle, as 0° can be landscape on some tablets
|
|
246
|
-
|
|
247
|
-
|
|
309
|
+
const orientationString = ((screen.orientation || {}).type || screen.mozOrientation || screen.msOrientation || '').split('-')[0];
|
|
248
310
|
if (orientationString === 'landscape' || orientationString === 'portrait') {
|
|
249
311
|
return orientationString;
|
|
250
312
|
}
|
|
251
|
-
}
|
|
252
|
-
|
|
313
|
+
}
|
|
253
314
|
|
|
254
|
-
|
|
255
|
-
|
|
315
|
+
// iOS only supports window.orientation
|
|
316
|
+
if (typeof window.orientation === 'number') {
|
|
317
|
+
if (window.orientation === 0 || window.orientation === 180) {
|
|
256
318
|
return 'portrait';
|
|
257
319
|
}
|
|
258
|
-
|
|
259
320
|
return 'landscape';
|
|
260
321
|
}
|
|
261
|
-
|
|
262
322
|
return 'portrait';
|
|
263
|
-
};
|
|
264
|
-
|
|
323
|
+
};
|
|
265
324
|
|
|
266
|
-
var registerPlugin = videojs__default['default'].registerPlugin || videojs__default['default'].plugin;
|
|
267
325
|
/**
|
|
268
326
|
* Add UI and event listeners
|
|
269
327
|
*
|
|
@@ -271,63 +329,49 @@
|
|
|
271
329
|
* @param {Player} player
|
|
272
330
|
* A Video.js player object.
|
|
273
331
|
*
|
|
274
|
-
* @param {
|
|
332
|
+
* @param {MobileUiOptions} [options={}]
|
|
275
333
|
* A plain object containing options for the plugin.
|
|
276
334
|
*/
|
|
277
|
-
|
|
278
|
-
var onPlayerReady = function onPlayerReady(player, options) {
|
|
335
|
+
const onPlayerReady = (player, options) => {
|
|
279
336
|
player.addClass('vjs-mobile-ui');
|
|
280
|
-
|
|
281
|
-
if (options.fullscreen.iOS) {
|
|
282
|
-
videojs__default['default'].log.warn('videojs-mobile-ui: `fullscreen.iOS` is deprecated. Use Video.js option `preferFullWindow` instead.');
|
|
283
|
-
|
|
284
|
-
if (videojs__default['default'].browser.IS_IOS && videojs__default['default'].browser.IOS_VERSION > 9 && !player.el_.ownerDocument.querySelector('.bc-iframe')) {
|
|
285
|
-
player.tech_.el_.setAttribute('playsinline', 'playsinline');
|
|
286
|
-
|
|
287
|
-
player.tech_.supportsFullScreen = function () {
|
|
288
|
-
return false;
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
337
|
if (!options.touchControls.disabled) {
|
|
294
338
|
if (options.touchControls.disableOnEnd || typeof player.endscreen === 'function') {
|
|
295
339
|
player.addClass('vjs-mobile-ui-disable-end');
|
|
296
|
-
}
|
|
297
|
-
|
|
340
|
+
}
|
|
298
341
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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);
|
|
303
347
|
|
|
348
|
+
// Video.js < 7.7.0 doesn't account for precedding components that don't have elements
|
|
304
349
|
if (major < 7 || major === 7 && minor < 7) {
|
|
305
350
|
controlBarIdx = Array.prototype.indexOf.call(player.el_.children, player.getChild('ControlBar').el_);
|
|
306
351
|
} else {
|
|
307
352
|
controlBarIdx = player.children_.indexOf(player.getChild('ControlBar'));
|
|
308
353
|
}
|
|
309
|
-
|
|
310
354
|
player.touchOverlay = player.addChild('TouchOverlay', options.touchControls, controlBarIdx);
|
|
311
355
|
}
|
|
312
|
-
|
|
313
356
|
if (options.fullscreen.disabled) {
|
|
314
357
|
return;
|
|
315
358
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
359
|
+
if (options.fullscreen.swipeToFullscreen || options.fullscreen.swipeFromFullscreen) {
|
|
360
|
+
initSwipe(player, options);
|
|
361
|
+
}
|
|
362
|
+
let locked = false;
|
|
363
|
+
const rotationHandler = () => {
|
|
364
|
+
const currentOrientation = getOrientation();
|
|
322
365
|
if (currentOrientation === 'landscape' && options.fullscreen.enterOnRotate) {
|
|
323
|
-
if (player.paused()
|
|
324
|
-
player.requestFullscreen()
|
|
325
|
-
|
|
366
|
+
if (!player.paused() && !player.isFullscreen()) {
|
|
367
|
+
player.requestFullscreen().catch(err => {
|
|
368
|
+
player.log.warn('Browser refused fullscreen request:', err);
|
|
369
|
+
});
|
|
326
370
|
if ((options.fullscreen.lockOnRotate || options.fullscreen.lockToLandscapeOnEnter) && screen.orientation && screen.orientation.lock) {
|
|
327
|
-
screen.orientation.lock('landscape').then(
|
|
371
|
+
screen.orientation.lock('landscape').then(() => {
|
|
328
372
|
locked = true;
|
|
329
|
-
}).catch(
|
|
330
|
-
videojs__default[
|
|
373
|
+
}).catch(err => {
|
|
374
|
+
videojs__default["default"].log.warn('Browser refused orientation lock:', err);
|
|
331
375
|
});
|
|
332
376
|
}
|
|
333
377
|
}
|
|
@@ -337,100 +381,60 @@
|
|
|
337
381
|
}
|
|
338
382
|
}
|
|
339
383
|
};
|
|
340
|
-
|
|
341
384
|
if (options.fullscreen.enterOnRotate || options.fullscreen.exitOnRotate) {
|
|
342
|
-
if (videojs__default[
|
|
343
|
-
|
|
344
|
-
player.on('dispose',
|
|
345
|
-
|
|
385
|
+
if (videojs__default["default"].browser.IS_IOS) {
|
|
386
|
+
window.addEventListener('orientationchange', rotationHandler);
|
|
387
|
+
player.on('dispose', () => {
|
|
388
|
+
window.removeEventListener('orientationchange', rotationHandler);
|
|
346
389
|
});
|
|
347
390
|
} else if (screen.orientation) {
|
|
348
391
|
// addEventListener('orientationchange') is not a user interaction on Android
|
|
349
392
|
screen.orientation.onchange = rotationHandler;
|
|
350
|
-
player.on('dispose',
|
|
393
|
+
player.on('dispose', () => {
|
|
351
394
|
screen.orientation.onchange = null;
|
|
352
395
|
});
|
|
353
396
|
}
|
|
354
397
|
}
|
|
355
|
-
|
|
356
|
-
player.on('fullscreenchange', function (_) {
|
|
398
|
+
player.on('fullscreenchange', _ => {
|
|
357
399
|
if (player.isFullscreen() && options.fullscreen.lockToLandscapeOnEnter && getOrientation() === 'portrait') {
|
|
358
|
-
screen.orientation.lock('landscape').then(
|
|
400
|
+
screen.orientation.lock('landscape').then(() => {
|
|
359
401
|
locked = true;
|
|
360
|
-
}).catch(
|
|
361
|
-
videojs__default[
|
|
402
|
+
}).catch(e => {
|
|
403
|
+
videojs__default["default"].log('Browser refused orientation lock:', e);
|
|
362
404
|
});
|
|
363
405
|
} else if (!player.isFullscreen() && locked) {
|
|
364
406
|
screen.orientation.unlock();
|
|
365
407
|
locked = false;
|
|
366
408
|
}
|
|
367
409
|
});
|
|
368
|
-
player.on('ended',
|
|
410
|
+
player.on('ended', _ => {
|
|
369
411
|
if (locked === true) {
|
|
370
412
|
screen.orientation.unlock();
|
|
371
413
|
locked = false;
|
|
372
414
|
}
|
|
373
415
|
});
|
|
374
416
|
};
|
|
417
|
+
|
|
375
418
|
/**
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
* Adds a monile UI for player control, and fullscreen orientation control
|
|
419
|
+
* Adds a mobile UI for player control, and fullscreen orientation control
|
|
379
420
|
*
|
|
380
421
|
* @function mobileUi
|
|
381
|
-
* @param {Object} [options={}]
|
|
382
|
-
* Plugin options.
|
|
383
|
-
* @param {boolean} [options.forceForTesting=false]
|
|
384
|
-
* Enables the display regardless of user agent, for testing purposes
|
|
385
|
-
* @param {Object} [options.fullscreen={}]
|
|
386
|
-
* Fullscreen options.
|
|
387
|
-
* @param {boolean} [options.fullscreen.disabled=false]
|
|
388
|
-
* If true no fullscreen handling except the *deprecated* iOS fullwindow hack
|
|
389
|
-
* @param {boolean} [options.fullscreen.enterOnRotate=true]
|
|
390
|
-
* Whether to go fullscreen when rotating to landscape
|
|
391
|
-
* @param {boolean} [options.fullscreen.exitOnRotate=true]
|
|
392
|
-
* Whether to leave fullscreen when rotating to portrait (if not locked)
|
|
393
|
-
* @param {boolean} [options.fullscreen.lockOnRotate=true]
|
|
394
|
-
* Whether to lock orientation when rotating to landscape
|
|
395
|
-
* Unlocked when exiting fullscreen or on 'ended
|
|
396
|
-
* @param {boolean} [options.fullscreen.lockToLandscapeOnEnter=false]
|
|
397
|
-
* Whether to always lock orientation to landscape on fullscreen mode
|
|
398
|
-
* Unlocked when exiting fullscreen or on 'ended'
|
|
399
|
-
* @param {boolean} [options.fullscreen.iOS=false]
|
|
400
|
-
* Deprecated: Whether to disable iOS's native fullscreen so controls can work
|
|
401
|
-
* @param {Object} [options.touchControls={}]
|
|
402
|
-
* Touch UI options.
|
|
403
|
-
* @param {boolean} [options.touchControls.disabled=false]
|
|
404
|
-
* If true no touch controls are added.
|
|
405
|
-
* @param {int} [options.touchControls.seekSeconds=10]
|
|
406
|
-
* Number of seconds to seek on double-tap
|
|
407
|
-
* @param {int} [options.touchControls.tapTimeout=300]
|
|
408
|
-
* Interval in ms to be considered a doubletap
|
|
409
|
-
* @param {boolean} [options.touchControls.disableOnEnd=false]
|
|
410
|
-
* Whether to disable when the video ends (e.g., if there is an endscreen)
|
|
411
|
-
* Never shows if the endscreen plugin is present
|
|
422
|
+
* @param {Object} [options={}] Plugin options
|
|
412
423
|
*/
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (options === void 0) {
|
|
419
|
-
options = {};
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (options.forceForTesting || videojs__default['default'].browser.IS_ANDROID || videojs__default['default'].browser.IS_IOS) {
|
|
423
|
-
this.ready(function () {
|
|
424
|
-
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));
|
|
425
428
|
});
|
|
426
429
|
}
|
|
427
|
-
};
|
|
428
|
-
|
|
430
|
+
};
|
|
429
431
|
|
|
430
|
-
|
|
432
|
+
// Register the plugin with video.js.
|
|
433
|
+
registerPlugin('mobileUi', mobileUi);
|
|
431
434
|
|
|
435
|
+
// Include the version number.
|
|
432
436
|
mobileUi.VERSION = version;
|
|
433
437
|
|
|
434
438
|
return mobileUi;
|
|
435
439
|
|
|
436
|
-
}))
|
|
440
|
+
}));
|