videojs-mobile-ui 0.7.0 → 1.0.0

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/index.html CHANGED
@@ -1,127 +1,200 @@
1
- <!doctype html>
1
+ <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>videojs-mobile-ui Demo</title>
7
- <link href="node_modules/video.js/dist/video-js.css" rel="stylesheet">
8
- <link href="dist/videojs-mobile-ui.css" rel="stylesheet">
7
+ <link href="node_modules/video.js/dist/video-js.css" rel="stylesheet" />
8
+ <link href="dist/videojs-mobile-ui.css" rel="stylesheet" />
9
9
  <style>
10
- .testEl {
11
- width: 10%;
12
- height: 10%;
13
- position: absolute;
14
- top: 0;
15
- pointer-events: none;
16
- display: none;
17
- }
10
+ .testEl {
11
+ width: 10%;
12
+ height: 10%;
13
+ position: absolute;
14
+ top: 0;
15
+ pointer-events: none;
16
+ display: none;
17
+ }
18
18
  </style>
19
19
  </head>
20
20
  <body>
21
- <video-js id="videojs-mobile-ui-player" class="video-js vjs-default-skin vjs-fluid" controls playsinline>
22
- <source src="//vjs.zencdn.net/v/oceans.mp4" type='video/mp4'>
23
- <source src="//vjs.zencdn.net/v/oceans.webm" type='video/webm'>
24
- </video-js>
21
+ <video-js
22
+ id="videojs-mobile-ui-player"
23
+ class="video-js vjs-default-skin vjs-fluid"
24
+ controls
25
+ playsinline
26
+ >
27
+ <source src="https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8" type="application/x-mpegurl" />
28
+ </video-js>
29
+ <ul>
30
+ <li><a href="test/">Run unit tests in browser.</a></li>
31
+ <li><a href="docs/api/">Read generated docs.</a></li>
32
+ </ul>
33
+ <h2>Options</h2>
34
+ <ul id="options">
35
+ <li>fullscreen:</li>
25
36
  <ul>
26
- <li><a href="test/">Run unit tests in browser.</a></li>
27
- <li><a href="docs/api/">Read generated docs.</a></li>
37
+ <li>
38
+ <input
39
+ type="checkbox"
40
+ data-section="fullscreen"
41
+ id="enterOnRotate"
42
+ />enterOnRotate
43
+ </li>
44
+ <li>
45
+ <input
46
+ type="checkbox"
47
+ data-section="fullscreen"
48
+ id="exitOnRotate"
49
+ />exitOnRotate
50
+ </li>
51
+ <li>
52
+ <input
53
+ type="checkbox"
54
+ data-section="fullscreen"
55
+ id="lockOnRotate"
56
+ />lockOnRotate
57
+ </li>
58
+ <li>
59
+ <input
60
+ type="checkbox"
61
+ data-section="fullscreen"
62
+ id="alwaysLockToLandscape"
63
+ />alwaysLockToLandscape
64
+ </li>
65
+ <li>
66
+ <input
67
+ type="checkbox"
68
+ data-section="fullscreen"
69
+ id="fullscreenDisabled"
70
+ />disabled
71
+ </li>
28
72
  </ul>
29
- <h2>Options</h2>
30
- <ul id="options">
31
- <li>fullscreen:</li>
32
- <ul>
33
- <li><input type="checkbox" data-section="fullscreen" id="enterOnRotate">enterOnRotate</li>
34
- <li><input type="checkbox" data-section="fullscreen" id="exitOnRotate">exitOnRotate</li>
35
- <li><input type="checkbox" data-section="fullscreen" id="lockOnRotate">lockOnRotate</li>
36
- <li><input type="checkbox" data-section="fullscreen" id="iOS">iOS <b>Deprecated</b></li>
37
- <li><input type="checkbox" data-section="fullscreen" id="fullscreenDisabled">disabled</li>
38
- </ul>
39
- <li>touchControls:</li>
40
- <ul>
41
- <li><input type="number" data-section="touchControls" id="seekSeconds">seekSeconds</li>
42
- <li><input type="number" data-section="touchControls" id="tapTimeout">tapTimeout</li>
43
- <li><input type="checkbox" data-section="touchControls" id="disableOnEnd">disableOnEnd</li>
44
- <li><input type="checkbox" data-section="touchControls" id="touchControlsDisabled">disabled</li>
45
- </ul>
73
+ <li>touchControls:</li>
74
+ <ul>
75
+ <li>
76
+ <input
77
+ type="number"
78
+ data-section="touchControls"
79
+ id="seekSeconds"
80
+ />seekSeconds
81
+ </li>
82
+ <li>
83
+ <input
84
+ type="number"
85
+ data-section="touchControls"
86
+ id="tapTimeout"
87
+ />tapTimeout
88
+ </li>
89
+ <li>
90
+ <input
91
+ type="checkbox"
92
+ data-section="touchControls"
93
+ id="disableOnEnd"
94
+ />disableOnEnd
95
+ </li>
96
+ <li>
97
+ <input
98
+ type="checkbox"
99
+ data-section="touchControls"
100
+ id="touchControlsDisabled"
101
+ />disabled
102
+ </li>
46
103
  </ul>
47
- <button id="reload">Reload with options</button>
48
- <ul id="log"></ul>
49
- <script src="node_modules/video.js/dist/video.js"></script>
50
- <script src="dist/videojs-mobile-ui.js"></script>
51
- <script>
52
- (function(window, videojs) {
53
- var options = {
54
- fullscreen: {
55
- enterOnRotate: true,
56
- exitOnRotate: true,
57
- lockOnRotate: true,
58
- iOS: false,
59
- disabled: false
60
- },
61
- touchControls: {
62
- seekSeconds: 10,
63
- tapTimeout: 300,
64
- disableOnEnd: false,
65
- disabled: false
66
- }
67
- };
104
+ </ul>
105
+ <button id="reload">Reload with options</button>
106
+ <ul id="log"></ul>
107
+ <script src="node_modules/video.js/dist/video.js"></script>
108
+ <script src="dist/videojs-mobile-ui.js"></script>
109
+ <script>
110
+ (function (window, videojs) {
111
+ var options = {
112
+ fullscreen: {
113
+ enterOnRotate: true,
114
+ exitOnRotate: true,
115
+ lockOnRotate: true,
116
+ alwaysLockToLandscape: false,
117
+ disabled: false,
118
+ },
119
+ touchControls: {
120
+ seekSeconds: 10,
121
+ tapTimeout: 300,
122
+ disableOnEnd: false,
123
+ disabled: false,
124
+ },
125
+ };
68
126
 
69
- var url = new URL(window.location);
70
- if (url.searchParams.has('options')) {
71
- options = JSON.parse(url.searchParams.get('options'));
72
- }
127
+ var url = new URL(window.location);
128
+ if (url.searchParams.has('options')) {
129
+ options = JSON.parse(url.searchParams.get('options'));
130
+ }
73
131
 
74
- console.log(JSON.stringify(options, null, 2));
132
+ console.log(JSON.stringify(options, null, 2));
75
133
 
76
- Object.keys(options).forEach(function(section) {
77
- Object.keys(options[section]).forEach(function(prop) {
78
- const val = options[section][prop];
134
+ Object.keys(options).forEach(function (section) {
135
+ Object.keys(options[section]).forEach(function (prop) {
136
+ const val = options[section][prop];
79
137
 
80
- if (prop === 'disabled') {
81
- prop = `${section}Disabled`;
82
- }
138
+ if (prop === 'disabled') {
139
+ prop = `${section}Disabled`;
140
+ }
83
141
 
84
- if (typeof val === 'boolean') {
85
- document.getElementById(prop).checked = val;
86
- }
87
- if (typeof val === 'number') {
88
- document.getElementById(prop).value = val;
89
- }
90
- });
142
+ if (typeof val === 'boolean') {
143
+ document.getElementById(prop).checked = val;
144
+ }
145
+ if (typeof val === 'number') {
146
+ document.getElementById(prop).value = val;
147
+ }
91
148
  });
149
+ });
92
150
 
93
- document.getElementById('options').querySelectorAll('input').forEach(function(opt) {
94
- opt.addEventListener('change', function() {
95
- if (this.type === 'checkbox') {
96
- const param = this.id.endsWith('Disabled') ? 'disabled' : this.id;
151
+ document
152
+ .getElementById('options')
153
+ .querySelectorAll('input')
154
+ .forEach(function (opt) {
155
+ opt.addEventListener('change', function () {
156
+ if (this.type === 'checkbox') {
157
+ const param = this.id.endsWith('Disabled')
158
+ ? 'disabled'
159
+ : this.id;
97
160
 
98
- options[this.getAttribute('data-section')][param] = this.checked;
99
- } else {
100
- options[this.getAttribute('data-section')][this.id] = parseFloat(this.value);
101
- }
102
- console.log(options);
103
- });
161
+ options[this.getAttribute('data-section')][param] =
162
+ this.checked;
163
+ } else {
164
+ options[this.getAttribute('data-section')][this.id] =
165
+ parseFloat(this.value);
166
+ }
167
+ console.log(options);
104
168
  });
169
+ });
105
170
 
106
- document.getElementById('reload').addEventListener('click', function() {
107
- url.searchParams.set('options', JSON.stringify(options));
171
+ document
172
+ .getElementById('reload')
173
+ .addEventListener('click', function () {
174
+ url.searchParams.set('options', JSON.stringify(options));
108
175
 
109
- window.location = url.href;
110
- })
176
+ window.location = url.href;
177
+ });
111
178
 
112
- window.addEventListener('orientationchange', function() {
113
- var el = document.createElement('li');
114
- var message = (new Date).toTimeString().split(' ')[0] + ' ' + window.orientation;
115
- message += (screen && screen.orientation ? ' ' + screen.orientation.type + ' ' + screen.orientation.angle : '');
116
- el.textContent = message;
117
- console.log(message);
118
- document.getElementById('log').appendChild(el);
119
- });
179
+ window.addEventListener('orientationchange', function () {
180
+ var el = document.createElement('li');
181
+ var message =
182
+ new Date().toTimeString().split(' ')[0] + ' ' + window.orientation;
183
+ message +=
184
+ screen && screen.orientation
185
+ ? ' ' + screen.orientation.type + ' ' + screen.orientation.angle
186
+ : '';
187
+ el.textContent = message;
188
+ console.log(message);
189
+ document.getElementById('log').appendChild(el);
190
+ });
120
191
 
121
- var testPlayer = window.testPlayer = videojs('videojs-mobile-ui-player');
122
- testPlayer.endscreen = function() {};
123
- testPlayer.mobileUi(options);
124
- }(window, window.videojs));
125
- </script>
192
+ var testPlayer = (window.testPlayer = videojs(
193
+ 'videojs-mobile-ui-player'
194
+ ));
195
+ testPlayer.endscreen = function () {};
196
+ testPlayer.mobileUi(options);
197
+ })(window, window.videojs);
198
+ </script>
126
199
  </body>
127
200
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "videojs-mobile-ui",
3
- "version": "0.7.0",
3
+ "version": "1.0.0",
4
4
  "description": "Mobile tap controls and fullscreen on rotate for Video.js",
5
5
  "main": "dist/videojs-mobile-ui.cjs.js",
6
6
  "module": "dist/videojs-mobile-ui.es.js",
@@ -31,7 +31,7 @@
31
31
  "watch": "npm-run-all -p watch:*",
32
32
  "watch:css": "npm run build:css -- -w",
33
33
  "watch:js": "npm run build:js -- -w",
34
- "prepublishOnly": "npm-run-all build-prod && vjsverify --verbose"
34
+ "prepublishOnly": "npm-run-all build-prod && vjsverify --verbose --skip-es-check"
35
35
  },
36
36
  "engines": {
37
37
  "node": ">=14",
@@ -68,24 +68,27 @@
68
68
  "README.md": "doctoc --notitle"
69
69
  },
70
70
  "dependencies": {
71
- "global": "^4.4.0",
72
- "video.js": "^7"
71
+ "global": "^4.4.0"
72
+ },
73
+ "peerDependencies": {
74
+ "video.js": "^8"
73
75
  },
74
76
  "devDependencies": {
75
77
  "@babel/runtime": "^7.14.0",
76
- "@videojs/generator-helpers": "~2.0.2",
77
- "jsdoc": "^3.6.7",
78
+ "@videojs/generator-helpers": "~3.2.0",
79
+ "husky": "^8.0.1",
80
+ "jsdoc": "^4.0.0",
78
81
  "karma": "^6.3.2",
79
82
  "postcss": "^8.2.13",
80
83
  "postcss-cli": "^8.3.1",
81
84
  "rollup": "^2.46.0",
82
- "sinon": "^9.1.0",
85
+ "sinon": "^14.0.1",
86
+ "video.js": "^8.0.0",
83
87
  "videojs-generate-karma-config": "~8.0.0",
84
88
  "videojs-generate-postcss-config": "~3.0.0",
85
- "videojs-generate-rollup-config": "~6.2.0",
86
- "videojs-generator-verify": "~3.0.3",
89
+ "videojs-generate-rollup-config": "^7.0.0",
90
+ "videojs-generator-verify": "^4.1.0",
87
91
  "videojs-languages": "^2.0.0",
88
- "videojs-standard": "^8.0.4",
89
- "husky": "^6.0.0"
92
+ "videojs-standard": "^9.0.1"
90
93
  }
91
94
  }
package/src/plugin.css CHANGED
@@ -30,16 +30,28 @@
30
30
 
31
31
  &.skip {
32
32
  opacity: 0;
33
- animation: fadeAndScale 0.6s linear;
33
+ animation: fadeAndScale 0.8s linear;
34
34
  background-repeat: no-repeat;
35
35
  background-position: 80% center;
36
36
  background-size: 10%;
37
37
  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>');
38
+
39
+ &:after {
40
+ content: attr(data-skip-text);
41
+ position: absolute;
42
+ top: 60%;
43
+ left: 70%;
44
+ }
38
45
  }
39
46
 
40
47
  &.skip.reverse {
41
48
  background-position: 20% center;
42
49
  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>');
50
+
51
+ &:after {
52
+ right: 70%;
53
+ left: unset;
54
+ }
43
55
  }
44
56
 
45
57
  .vjs-play-control {
package/src/plugin.js CHANGED
@@ -9,7 +9,7 @@ const defaults = {
9
9
  enterOnRotate: true,
10
10
  exitOnRotate: true,
11
11
  lockOnRotate: true,
12
- iOS: false,
12
+ lockToLandscapeOnEnter: false,
13
13
  disabled: false
14
14
  },
15
15
  touchControls: {
@@ -28,9 +28,9 @@ const screen = window.screen;
28
28
  * @return {string} orientation
29
29
  */
30
30
  const getOrientation = () => {
31
- if (screen) {
31
+ if (window.screen) {
32
32
  // Prefer the string over angle, as 0° can be landscape on some tablets
33
- const orientationString = ((screen.orientation || {}).type || screen.mozOrientation || screen.msOrientation || '').split('-')[0];
33
+ const orientationString = ((window.screen.orientation || {}).type || window.screen.mozOrientation || window.screen.msOrientation || '').split('-')[0];
34
34
 
35
35
  if (orientationString === 'landscape' || orientationString === 'portrait') {
36
36
  return orientationString;
@@ -48,9 +48,6 @@ const getOrientation = () => {
48
48
  return 'portrait';
49
49
  };
50
50
 
51
- // Cross-compatibility for Video.js 5 and 6.
52
- const registerPlugin = videojs.registerPlugin || videojs.plugin;
53
-
54
51
  /**
55
52
  * Add UI and event listeners
56
53
  *
@@ -64,17 +61,6 @@ const registerPlugin = videojs.registerPlugin || videojs.plugin;
64
61
  const onPlayerReady = (player, options) => {
65
62
  player.addClass('vjs-mobile-ui');
66
63
 
67
- if (options.fullscreen.iOS) {
68
- videojs.log.warn('videojs-mobile-ui: `fullscreen.iOS` is deprecated. Use Video.js option `preferFullWindow` instead.');
69
- if (videojs.browser.IS_IOS && videojs.browser.IOS_VERSION > 9 &&
70
- !player.el_.ownerDocument.querySelector('.bc-iframe')) {
71
- player.tech_.el_.setAttribute('playsinline', 'playsinline');
72
- player.tech_.supportsFullScreen = function() {
73
- return false;
74
- };
75
- }
76
- }
77
-
78
64
  if (!options.touchControls.disabled) {
79
65
 
80
66
  if (options.touchControls.disableOnEnd || typeof player.endscreen === 'function') {
@@ -82,20 +68,7 @@ const onPlayerReady = (player, options) => {
82
68
  }
83
69
 
84
70
  // Insert before the control bar
85
- let controlBarIdx;
86
- const versionParts = videojs.VERSION.split('.');
87
- const major = parseInt(versionParts[0], 10);
88
- const minor = parseInt(versionParts[1], 10);
89
-
90
- // Video.js < 7.7.0 doesn't account for precedding components that don't have elements
91
- if (major < 7 || (major === 7 && minor < 7)) {
92
- controlBarIdx = Array.prototype.indexOf.call(
93
- player.el_.children,
94
- player.getChild('ControlBar').el_
95
- );
96
- } else {
97
- controlBarIdx = player.children_.indexOf(player.getChild('ControlBar'));
98
- }
71
+ const controlBarIdx = player.children_.indexOf(player.getChild('ControlBar'));
99
72
 
100
73
  player.touchOverlay = player.addChild('TouchOverlay', options.touchControls, controlBarIdx);
101
74
  }
@@ -112,7 +85,7 @@ const onPlayerReady = (player, options) => {
112
85
  if (currentOrientation === 'landscape' && options.fullscreen.enterOnRotate) {
113
86
  if (player.paused() === false) {
114
87
  player.requestFullscreen();
115
- if (options.fullscreen.lockOnRotate &&
88
+ if ((options.fullscreen.lockOnRotate || options.fullscreen.lockToLandscapeOnEnter) &&
116
89
  screen.orientation && screen.orientation.lock) {
117
90
  screen.orientation.lock('landscape').then(() => {
118
91
  locked = true;
@@ -143,15 +116,21 @@ const onPlayerReady = (player, options) => {
143
116
  screen.orientation.onchange = null;
144
117
  });
145
118
  }
146
-
147
- player.on('fullscreenchange', _ => {
148
- if (!player.isFullscreen() && locked) {
149
- screen.orientation.unlock();
150
- locked = false;
151
- }
152
- });
153
119
  }
154
120
 
121
+ player.on('fullscreenchange', _ => {
122
+ if (player.isFullscreen() && options.fullscreen.lockToLandscapeOnEnter && getOrientation() === 'portrait') {
123
+ screen.orientation.lock('landscape').then(()=>{
124
+ locked = true;
125
+ }).catch((e) => {
126
+ videojs.log('Browser refused orientation lock:', e);
127
+ });
128
+ } else if (!player.isFullscreen() && locked) {
129
+ screen.orientation.unlock();
130
+ locked = false;
131
+ }
132
+ });
133
+
155
134
  player.on('ended', _ => {
156
135
  if (locked === true) {
157
136
  screen.orientation.unlock();
@@ -180,6 +159,9 @@ const onPlayerReady = (player, options) => {
180
159
  * Whether to leave fullscreen when rotating to portrait (if not locked)
181
160
  * @param {boolean} [options.fullscreen.lockOnRotate=true]
182
161
  * Whether to lock orientation when rotating to landscape
162
+ * Unlocked when exiting fullscreen or on 'ended
163
+ * @param {boolean} [options.fullscreen.lockToLandscapeOnEnter=false]
164
+ * Whether to always lock orientation to landscape on fullscreen mode
183
165
  * Unlocked when exiting fullscreen or on 'ended'
184
166
  * @param {boolean} [options.fullscreen.iOS=false]
185
167
  * Deprecated: Whether to disable iOS's native fullscreen so controls can work
@@ -198,13 +180,13 @@ const onPlayerReady = (player, options) => {
198
180
  const mobileUi = function(options = {}) {
199
181
  if (options.forceForTesting || videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) {
200
182
  this.ready(() => {
201
- onPlayerReady(this, videojs.mergeOptions(defaults, options));
183
+ onPlayerReady(this, videojs.obj.merge(defaults, options));
202
184
  });
203
185
  }
204
186
  };
205
187
 
206
188
  // Register the plugin with video.js.
207
- registerPlugin('mobileUi', mobileUi);
189
+ videojs.registerPlugin('mobileUi', mobileUi);
208
190
 
209
191
  // Include the version number.
210
192
  mobileUi.VERSION = VERSION;
@@ -30,6 +30,7 @@ class TouchOverlay extends Component {
30
30
 
31
31
  this.seekSeconds = options.seekSeconds;
32
32
  this.tapTimeout = options.tapTimeout;
33
+ this.taps = 0;
33
34
 
34
35
  // Add play toggle overlay
35
36
  this.addChild('playToggle', {});
@@ -44,6 +45,46 @@ class TouchOverlay extends Component {
44
45
  this.player_.options_.inactivityTimeout = 5000;
45
46
  }
46
47
 
48
+ /**
49
+ * Debounced tap handler.
50
+ * Seeks number of (taps - 1) * configured seconds to skip.
51
+ * One tap is a non-op
52
+ *
53
+ * @param {Event} event
54
+ */
55
+ this.handleTaps_ = videojs.fn.debounce(event => {
56
+ const increment = (this.taps - 1) * this.seekSeconds;
57
+
58
+ this.taps = 0;
59
+ if (increment < 1) {
60
+ return;
61
+ }
62
+
63
+ const rect = this.el_.getBoundingClientRect();
64
+ const x = event.changedTouches[0].clientX - rect.left;
65
+
66
+ // Check if double tap is in left or right area
67
+ if (x < rect.width * 0.4) {
68
+ this.player_.currentTime(Math.max(0, this.player_.currentTime() - increment));
69
+ this.addClass('reverse');
70
+ } else if (x > rect.width - (rect.width * 0.4)) {
71
+ this.player_.currentTime(Math.min(this.player_.duration(), this.player_.currentTime() + increment));
72
+ this.removeClass('reverse');
73
+ } else {
74
+ return;
75
+ }
76
+
77
+ // Remove play toggle if showing
78
+ this.removeClass('show-play-toggle');
79
+
80
+ // Remove and readd class to trigger animation
81
+ this.setAttribute('data-skip-text', `${increment} ${this.localize('seconds')}`);
82
+ this.removeClass('skip');
83
+ window.requestAnimationFrame(() => {
84
+ this.addClass('skip');
85
+ });
86
+ }, this.tapTimeout);
87
+
47
88
  this.enable();
48
89
  }
49
90
 
@@ -78,63 +119,12 @@ class TouchOverlay extends Component {
78
119
 
79
120
  event.preventDefault();
80
121
 
81
- if (this.firstTapCaptured) {
82
- this.firstTapCaptured = false;
83
- if (this.timeout) {
84
- window.clearTimeout(this.timeout);
85
- }
86
- this.handleDoubleTap(event);
87
- } else {
88
- this.firstTapCaptured = true;
89
- this.timeout = window.setTimeout(() => {
90
- this.firstTapCaptured = false;
91
- this.handleSingleTap(event);
92
- }, this.tapTimeout);
122
+ this.taps += 1;
123
+ if (this.taps === 1) {
124
+ this.removeClass('skip');
125
+ this.toggleClass('show-play-toggle');
93
126
  }
94
- }
95
-
96
- /**
97
- * Toggles display of play toggle
98
- *
99
- * @param {Event} event
100
- * The touch event
101
- *
102
- */
103
- handleSingleTap(event) {
104
- this.removeClass('skip');
105
- this.toggleClass('show-play-toggle');
106
- }
107
-
108
- /**
109
- * Seeks by configured number of seconds if left or right part of video double tapped
110
- *
111
- * @param {Event} event
112
- * The touch event
113
- *
114
- */
115
- handleDoubleTap(event) {
116
- const rect = this.el_.getBoundingClientRect();
117
- const x = event.changedTouches[0].clientX - rect.left;
118
-
119
- // Check if double tap is in left or right area
120
- if (x < rect.width * 0.4) {
121
- this.player_.currentTime(Math.max(0, this.player_.currentTime() - this.seekSeconds));
122
- this.addClass('reverse');
123
- } else if (x > rect.width - (rect.width * 0.4)) {
124
- this.player_.currentTime(Math.min(this.player_.duration(), this.player_.currentTime() + this.seekSeconds));
125
- this.removeClass('reverse');
126
- } else {
127
- return;
128
- }
129
-
130
- // Remove play toggle if showing
131
- this.removeClass('show-play-toggle');
132
-
133
- // Remove and readd class to trigger animation
134
- this.removeClass('skip');
135
- window.requestAnimationFrame(() => {
136
- this.addClass('skip');
137
- });
127
+ this.handleTaps_(event);
138
128
  }
139
129
 
140
130
  /**