videojs-mobile-ui 0.7.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/README.md +114 -46
- package/dist/videojs-mobile-ui.cjs.js +275 -223
- package/dist/videojs-mobile-ui.css +2 -2
- package/dist/videojs-mobile-ui.es.js +270 -217
- package/dist/videojs-mobile-ui.js +278 -265
- package/dist/videojs-mobile-ui.min.js +2 -2
- package/docs/api/TouchOverlay.html +964 -0
- package/docs/api/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Bold-webfont.svg +1830 -0
- package/docs/api/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
- package/docs/api/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Italic-webfont.svg +1830 -0
- package/docs/api/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-Light-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Light-webfont.svg +1831 -0
- package/docs/api/fonts/OpenSans-Light-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
- package/docs/api/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/docs/api/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/docs/api/fonts/OpenSans-Regular-webfont.svg +1831 -0
- package/docs/api/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/docs/api/global.html +957 -0
- package/docs/api/index.html +159 -0
- package/docs/api/plugin.js.html +221 -0
- package/docs/api/scripts/linenumber.js +25 -0
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/api/scripts/prettify/lang-css.js +2 -0
- package/docs/api/scripts/prettify/prettify.js +28 -0
- package/docs/api/styles/jsdoc-default.css +358 -0
- package/docs/api/styles/prettify-jsdoc.css +111 -0
- package/docs/api/styles/prettify-tomorrow.css +132 -0
- package/docs/api/swipeFullscreen.js.html +173 -0
- package/docs/api/touchOverlay.js.html +211 -0
- package/index.html +206 -65
- package/package.json +19 -16
- package/src/plugin.css +21 -3
- package/src/plugin.js +32 -59
- 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
|
+
}
|
|
93
|
+
|
|
94
|
+
// Remove play toggle if showing
|
|
95
|
+
this.removeClass('show-play-toggle');
|
|
94
96
|
|
|
95
|
-
|
|
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,110 +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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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();
|
|
136
|
+
}
|
|
137
|
+
this.taps += 1;
|
|
138
|
+
if (this.taps === 1) {
|
|
139
|
+
this.removeClass('skip');
|
|
140
|
+
this.toggleClass('show-play-toggle');
|
|
149
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,
|
|
287
|
+
swipeToFullscreen: false,
|
|
288
|
+
swipeFromFullscreen: false,
|
|
226
289
|
disabled: false
|
|
227
290
|
},
|
|
228
291
|
touchControls: {
|
|
@@ -232,37 +295,33 @@
|
|
|
232
295
|
disabled: false
|
|
233
296
|
}
|
|
234
297
|
};
|
|
235
|
-
|
|
298
|
+
const screen = window.screen;
|
|
299
|
+
const registerPlugin = videojs__default["default"].registerPlugin || videojs__default["default"].plugin;
|
|
300
|
+
|
|
236
301
|
/**
|
|
237
302
|
* Gets 'portrait' or 'lanscape' from the two orientation APIs
|
|
238
303
|
*
|
|
239
304
|
* @return {string} orientation
|
|
240
305
|
*/
|
|
241
|
-
|
|
242
|
-
var getOrientation = function getOrientation() {
|
|
306
|
+
const getOrientation = () => {
|
|
243
307
|
if (screen) {
|
|
244
308
|
// Prefer the string over angle, as 0° can be landscape on some tablets
|
|
245
|
-
|
|
246
|
-
|
|
309
|
+
const orientationString = ((screen.orientation || {}).type || screen.mozOrientation || screen.msOrientation || '').split('-')[0];
|
|
247
310
|
if (orientationString === 'landscape' || orientationString === 'portrait') {
|
|
248
311
|
return orientationString;
|
|
249
312
|
}
|
|
250
|
-
}
|
|
251
|
-
|
|
313
|
+
}
|
|
252
314
|
|
|
253
|
-
|
|
254
|
-
|
|
315
|
+
// iOS only supports window.orientation
|
|
316
|
+
if (typeof window.orientation === 'number') {
|
|
317
|
+
if (window.orientation === 0 || window.orientation === 180) {
|
|
255
318
|
return 'portrait';
|
|
256
319
|
}
|
|
257
|
-
|
|
258
320
|
return 'landscape';
|
|
259
321
|
}
|
|
260
|
-
|
|
261
322
|
return 'portrait';
|
|
262
|
-
};
|
|
263
|
-
|
|
323
|
+
};
|
|
264
324
|
|
|
265
|
-
var registerPlugin = videojs__default['default'].registerPlugin || videojs__default['default'].plugin;
|
|
266
325
|
/**
|
|
267
326
|
* Add UI and event listeners
|
|
268
327
|
*
|
|
@@ -270,63 +329,49 @@
|
|
|
270
329
|
* @param {Player} player
|
|
271
330
|
* A Video.js player object.
|
|
272
331
|
*
|
|
273
|
-
* @param {
|
|
332
|
+
* @param {MobileUiOptions} [options={}]
|
|
274
333
|
* A plain object containing options for the plugin.
|
|
275
334
|
*/
|
|
276
|
-
|
|
277
|
-
var onPlayerReady = function onPlayerReady(player, options) {
|
|
335
|
+
const onPlayerReady = (player, options) => {
|
|
278
336
|
player.addClass('vjs-mobile-ui');
|
|
279
|
-
|
|
280
|
-
if (options.fullscreen.iOS) {
|
|
281
|
-
videojs__default['default'].log.warn('videojs-mobile-ui: `fullscreen.iOS` is deprecated. Use Video.js option `preferFullWindow` instead.');
|
|
282
|
-
|
|
283
|
-
if (videojs__default['default'].browser.IS_IOS && videojs__default['default'].browser.IOS_VERSION > 9 && !player.el_.ownerDocument.querySelector('.bc-iframe')) {
|
|
284
|
-
player.tech_.el_.setAttribute('playsinline', 'playsinline');
|
|
285
|
-
|
|
286
|
-
player.tech_.supportsFullScreen = function () {
|
|
287
|
-
return false;
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
337
|
if (!options.touchControls.disabled) {
|
|
293
338
|
if (options.touchControls.disableOnEnd || typeof player.endscreen === 'function') {
|
|
294
339
|
player.addClass('vjs-mobile-ui-disable-end');
|
|
295
|
-
}
|
|
296
|
-
|
|
340
|
+
}
|
|
297
341
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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);
|
|
302
347
|
|
|
348
|
+
// Video.js < 7.7.0 doesn't account for precedding components that don't have elements
|
|
303
349
|
if (major < 7 || major === 7 && minor < 7) {
|
|
304
350
|
controlBarIdx = Array.prototype.indexOf.call(player.el_.children, player.getChild('ControlBar').el_);
|
|
305
351
|
} else {
|
|
306
352
|
controlBarIdx = player.children_.indexOf(player.getChild('ControlBar'));
|
|
307
353
|
}
|
|
308
|
-
|
|
309
354
|
player.touchOverlay = player.addChild('TouchOverlay', options.touchControls, controlBarIdx);
|
|
310
355
|
}
|
|
311
|
-
|
|
312
356
|
if (options.fullscreen.disabled) {
|
|
313
357
|
return;
|
|
314
358
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
359
|
+
if (options.fullscreen.swipeToFullscreen || options.fullscreen.swipeFromFullscreen) {
|
|
360
|
+
initSwipe(player, options);
|
|
361
|
+
}
|
|
362
|
+
let locked = false;
|
|
363
|
+
const rotationHandler = () => {
|
|
364
|
+
const currentOrientation = getOrientation();
|
|
321
365
|
if (currentOrientation === 'landscape' && options.fullscreen.enterOnRotate) {
|
|
322
|
-
if (player.paused()
|
|
323
|
-
player.requestFullscreen()
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
366
|
+
if (!player.paused() && !player.isFullscreen()) {
|
|
367
|
+
player.requestFullscreen().catch(err => {
|
|
368
|
+
player.log.warn('Browser refused fullscreen request:', err);
|
|
369
|
+
});
|
|
370
|
+
if ((options.fullscreen.lockOnRotate || options.fullscreen.lockToLandscapeOnEnter) && screen.orientation && screen.orientation.lock) {
|
|
371
|
+
screen.orientation.lock('landscape').then(() => {
|
|
327
372
|
locked = true;
|
|
328
|
-
}).catch(
|
|
329
|
-
videojs__default[
|
|
373
|
+
}).catch(err => {
|
|
374
|
+
videojs__default["default"].log.warn('Browser refused orientation lock:', err);
|
|
330
375
|
});
|
|
331
376
|
}
|
|
332
377
|
}
|
|
@@ -336,92 +381,60 @@
|
|
|
336
381
|
}
|
|
337
382
|
}
|
|
338
383
|
};
|
|
339
|
-
|
|
340
384
|
if (options.fullscreen.enterOnRotate || options.fullscreen.exitOnRotate) {
|
|
341
|
-
if (videojs__default[
|
|
342
|
-
|
|
343
|
-
player.on('dispose',
|
|
344
|
-
|
|
385
|
+
if (videojs__default["default"].browser.IS_IOS) {
|
|
386
|
+
window.addEventListener('orientationchange', rotationHandler);
|
|
387
|
+
player.on('dispose', () => {
|
|
388
|
+
window.removeEventListener('orientationchange', rotationHandler);
|
|
345
389
|
});
|
|
346
390
|
} else if (screen.orientation) {
|
|
347
391
|
// addEventListener('orientationchange') is not a user interaction on Android
|
|
348
392
|
screen.orientation.onchange = rotationHandler;
|
|
349
|
-
player.on('dispose',
|
|
393
|
+
player.on('dispose', () => {
|
|
350
394
|
screen.orientation.onchange = null;
|
|
351
395
|
});
|
|
352
396
|
}
|
|
353
|
-
|
|
354
|
-
player.on('fullscreenchange', function (_) {
|
|
355
|
-
if (!player.isFullscreen() && locked) {
|
|
356
|
-
screen.orientation.unlock();
|
|
357
|
-
locked = false;
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
397
|
}
|
|
361
|
-
|
|
362
|
-
|
|
398
|
+
player.on('fullscreenchange', _ => {
|
|
399
|
+
if (player.isFullscreen() && options.fullscreen.lockToLandscapeOnEnter && getOrientation() === 'portrait') {
|
|
400
|
+
screen.orientation.lock('landscape').then(() => {
|
|
401
|
+
locked = true;
|
|
402
|
+
}).catch(e => {
|
|
403
|
+
videojs__default["default"].log('Browser refused orientation lock:', e);
|
|
404
|
+
});
|
|
405
|
+
} else if (!player.isFullscreen() && locked) {
|
|
406
|
+
screen.orientation.unlock();
|
|
407
|
+
locked = false;
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
player.on('ended', _ => {
|
|
363
411
|
if (locked === true) {
|
|
364
412
|
screen.orientation.unlock();
|
|
365
413
|
locked = false;
|
|
366
414
|
}
|
|
367
415
|
});
|
|
368
416
|
};
|
|
417
|
+
|
|
369
418
|
/**
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* Adds a monile UI for player control, and fullscreen orientation control
|
|
419
|
+
* Adds a mobile UI for player control, and fullscreen orientation control
|
|
373
420
|
*
|
|
374
421
|
* @function mobileUi
|
|
375
|
-
* @param {Object} [options={}]
|
|
376
|
-
* Plugin options.
|
|
377
|
-
* @param {boolean} [options.forceForTesting=false]
|
|
378
|
-
* Enables the display regardless of user agent, for testing purposes
|
|
379
|
-
* @param {Object} [options.fullscreen={}]
|
|
380
|
-
* Fullscreen options.
|
|
381
|
-
* @param {boolean} [options.fullscreen.disabled=false]
|
|
382
|
-
* If true no fullscreen handling except the *deprecated* iOS fullwindow hack
|
|
383
|
-
* @param {boolean} [options.fullscreen.enterOnRotate=true]
|
|
384
|
-
* Whether to go fullscreen when rotating to landscape
|
|
385
|
-
* @param {boolean} [options.fullscreen.exitOnRotate=true]
|
|
386
|
-
* Whether to leave fullscreen when rotating to portrait (if not locked)
|
|
387
|
-
* @param {boolean} [options.fullscreen.lockOnRotate=true]
|
|
388
|
-
* Whether to lock orientation when rotating to landscape
|
|
389
|
-
* Unlocked when exiting fullscreen or on 'ended'
|
|
390
|
-
* @param {boolean} [options.fullscreen.iOS=false]
|
|
391
|
-
* Deprecated: Whether to disable iOS's native fullscreen so controls can work
|
|
392
|
-
* @param {Object} [options.touchControls={}]
|
|
393
|
-
* Touch UI options.
|
|
394
|
-
* @param {boolean} [options.touchControls.disabled=false]
|
|
395
|
-
* If true no touch controls are added.
|
|
396
|
-
* @param {int} [options.touchControls.seekSeconds=10]
|
|
397
|
-
* Number of seconds to seek on double-tap
|
|
398
|
-
* @param {int} [options.touchControls.tapTimeout=300]
|
|
399
|
-
* Interval in ms to be considered a doubletap
|
|
400
|
-
* @param {boolean} [options.touchControls.disableOnEnd=false]
|
|
401
|
-
* Whether to disable when the video ends (e.g., if there is an endscreen)
|
|
402
|
-
* Never shows if the endscreen plugin is present
|
|
422
|
+
* @param {Object} [options={}] Plugin options
|
|
403
423
|
*/
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (options === void 0) {
|
|
410
|
-
options = {};
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
if (options.forceForTesting || videojs__default['default'].browser.IS_ANDROID || videojs__default['default'].browser.IS_IOS) {
|
|
414
|
-
this.ready(function () {
|
|
415
|
-
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));
|
|
416
428
|
});
|
|
417
429
|
}
|
|
418
|
-
};
|
|
419
|
-
|
|
430
|
+
};
|
|
420
431
|
|
|
421
|
-
|
|
432
|
+
// Register the plugin with video.js.
|
|
433
|
+
registerPlugin('mobileUi', mobileUi);
|
|
422
434
|
|
|
435
|
+
// Include the version number.
|
|
423
436
|
mobileUi.VERSION = version;
|
|
424
437
|
|
|
425
438
|
return mobileUi;
|
|
426
439
|
|
|
427
|
-
}))
|
|
440
|
+
}));
|